diff --git a/.gitignore b/.gitignore index f7849967c..f5a18b7e3 100644 --- a/.gitignore +++ b/.gitignore @@ -56,6 +56,7 @@ Makefile.gcc-orig *.lastcodeanalysissucceeded # VS 2017 caches data into the hidden directory .vs .vs +win/win32/vs/obj/* # Win32-specific ignores end .DS_Store # ms-dos diff --git a/Cross-compiling b/Cross-compiling index a1d294555..38998a937 100644 --- a/Cross-compiling +++ b/Cross-compiling @@ -361,41 +361,44 @@ Using the cross-compiler, build the following targets: cross-compile and link with these compiler switches: -DCROSSCOMPILE and -DCROSSCOMPILE_TARGET - core sources (2019): src/allmain.c, src/alloc.c, src/apply.c, + core sources (2024): 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/cmd.c, src/dbridge.c, + src/bones.c, src/botl.c, src/calendar.c, + src/coloratt.c, src/cmd.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/extralev.c, src/files.c, - src/fountain.c, src/hack.c, src/hacklib.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/fountain.c, src/getpos.c, src/glyphs.c, + src/hack.c, src/hacklib.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/nhlsel.c, src/nhlua.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/nhlua.c, src/nhlsel.c, src/nhlobj.c, + src/nhmd4.c, src/objects.c, src/o_init.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/sfstruct.c, - src/shk.c, src/shknam.c, src/sit.c, src/sounds.c, - src/sp_lev.c, src/spell.c, src/steal.c, src/steed.c, + src/region.c, src/report.c, src/restore.c,src/rip.c, + src/rnd.c, src/role.c, src/rumors.c, src/save.c, + src/selvar.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/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, sys/share/cppregex.cpp + 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/wizcmds.c, + src/worm.c, src/worn.c, src/write.c, src/zap.c, + sys/share/cppregex.cpp tty sources: win/tty/getline.c, win/tty/termcap.c, win/tty/topl.c, win/tty/wintty.c @@ -469,10 +472,10 @@ Cross-compiler pre-built binary downloads: or pdcursesmod from: https://github.com/Bill-Gray/PDCursesMod.git - - A shell script to download that djgpp cross-compiler and associated + - A bash script to download that djgpp cross-compiler and associated pieces for either linux or macOS is available: - sh sys/msdos/fetch-cross-compiler.sh + bash sys/msdos/fetch-cross-compiler.sh That script won't install anything, it just does file fetches and stores them in subfolders of lib. The linux.370 and macOS.370 hints files are @@ -480,7 +483,7 @@ Cross-compiler pre-built binary downloads: CROSS_TO_MSDOS=1 on your make command line. - Note: Both the fetch-cross-compiler.sh script and the msdos + Note: Both the fetch-cross-compiler.sh bash script and the msdos cross-compile and package procedures require unzip and zip to be available on your host build system. @@ -695,7 +698,7 @@ Cross-compiler url: https://emscripten.org/docs/getting_started/downloads.html +--------------------------------+ - | B7. Case sample: mips | + | B7. Case sample: mips | +--------------------------------+ Cross-compiler used: gcc-mipsel-linux-gnu, g++-mipsel-linux-gnu diff --git a/DEVEL/Developer.txt b/DEVEL/Developer.txt index c7d7683c9..37fd69c59 100644 --- a/DEVEL/Developer.txt +++ b/DEVEL/Developer.txt @@ -58,6 +58,11 @@ NOTE: These instructions assume you are on the default branch; this _is_ NOTE: The following instructions require perl. If you do not have perl on your system, please install it before proceeding. +NOTE: More information on nhgitset.pl is available before installation via: + perldoc DEVEL/nhgitset.pl + After installation, the same information is available with: + git nhhelp nhgitset + A. If you have never set up git on this machine before: (This assumes you will only be using git for NetHack. If you are going to use it for other projects as well, think before you type.) @@ -92,10 +97,11 @@ B. Specify the prefix for variable substitution: tree you cloned from git. I use ~/nethack/GITADDDIR; for that base, create the needed directories and edit the file: ~/nethack/GITADDDIR/DOTGIT/PRE - Put this in it (if your OS is not Unix-like you may need to change - the first line): + Put this in it, adapting it to your variant (if your OS is not Unix-like + you may need to change the first line): #!/bin/sh git config nethack.substprefix MINE + git config nethack.projectname MineHack Now make it executable: chmod +x ~/nethack/GITADDDIR/DOTGIT/PRE C. Configure the repository: @@ -169,8 +175,8 @@ A. Introduction The PREFIX is the value in the git config variable nethack.substprefix. VARNAME is one of: Date - Branch (experimental) - Revision (experimental) + Branch + Revision other names will give a warning. B. Enabling variable expansion diff --git a/DEVEL/VERSION b/DEVEL/VERSION new file mode 100644 index 000000000..3788cb6d5 --- /dev/null +++ b/DEVEL/VERSION @@ -0,0 +1,4 @@ +5 +Please see "git log DEVEL" for previous changes. +Make documentation of nhgitset.pl easier to find and +find out about. diff --git a/DEVEL/hooksdir/NHadd b/DEVEL/hooksdir/NHadd index c4d6e547d..f4386c81d 100644 --- a/DEVEL/hooksdir/NHadd +++ b/DEVEL/hooksdir/NHadd @@ -12,11 +12,32 @@ die "Bad subcommand '$ARGV[0]'" unless $ok{$ARGV[0]}; # we won't fail on a failure, so just system() $rv = system('.git/hooks/nhsub',"--$ARGV[0]",@ARGV[1..$#ARGV]); if($rv){ - print "warning: nhsub failed: $rv $!\n"; + print "warning: nhsub failed: $rv $!\n"; } if(length $ENV{GIT_PREFIX}){ - chdir($ENV{GIT_PREFIX}) or die "Can't chdir $ENV{GIT_PREFIX}: $!"; + chdir($ENV{GIT_PREFIX}) or die "Can't chdir $ENV{GIT_PREFIX}: $!"; } exec "git", @ARGV or die "Can't exec git: $!"; + +__END__ +=for nhgitset nhadd Add file contents to the index with NetHack additions +=for nhgitset nhcommit Record changes to the repository with NetHack additions + +=head1 NAME + +C - NetHack internal common code for nhadd and nhcommit + +=head1 SYNOPSIS + +Cgit add optionsE> + +Cgit add optionsE> + +=head1 DESCRIPTION + +Run nhsub with the given arguments, then run C or C +with the given arguments. Note that only basic arguments for those commands +are understood; more complex situations may be handled by running C +manually before running C or C. diff --git a/DEVEL/hooksdir/NHgithook.pm b/DEVEL/hooksdir/NHgithook.pm index c7c3456f5..065fcd60a 100644 --- a/DEVEL/hooksdir/NHgithook.pm +++ b/DEVEL/hooksdir/NHgithook.pm @@ -16,9 +16,11 @@ my $tracefile = "/tmp/nhgitt.$$"; # OS hackery my $DS = quotemeta('/'); +my $PDS = '/'; if ($^O eq "MSWin32") { $DS = quotemeta('\\'); + $PDS = '\\'; } our %saved_env; @@ -26,25 +28,23 @@ our @saved_argv; our $saved_input; sub saveSTDIN { - @saved_input = ; + @saved_input = ; - if($trace){ - print TRACE "STDIN:\n"; - print TRACE $saved_input; - print TRACE "ENDSTDIN\n"; - } + if($trace){ + print TRACE "STDIN:\n"; + print TRACE $saved_input; + print TRACE "ENDSTDIN\n"; + } - tie *STDIN, 'NHIO::STDIN', @saved_input; + tie *STDIN, 'NHIO::STDIN', @saved_input; } -# XXX this needs a re-write (don't tie and untie, just set NEXT=0) -# (the sensitive thing is @foo = ) sub resetSTDIN{ - my $x = tied(*STDIN); - my %x = %$x; - my $data = @$x{DATA}; - untie *STDIN; - tie *STDIN, 'NHIO::STDIN', $data; + my $x = tied(*STDIN); + my %x = %$x; + my $data = @$x{DATA}; + untie *STDIN; + tie *STDIN, 'NHIO::STDIN', $data; } # don't need this now @@ -55,21 +55,142 @@ sub resetSTDIN{ #} sub PRE { - &do_hook("PRE"); + &do_hook("PRE"); } sub POST { - &do_hook("POST"); + &do_hook("POST"); +} + +### +### versioning for nhgitset and friends +### + +# values of nethack.setupversion and DEVEL/VERSION: +# 1 is reserved for repos checked out before versioning was added +# 2 used clean/smudge filter, poorly +# 3 was first production version +# 4 added the version file and version checking; nhhelp, NH_DATESUB support, etc. + +sub version_in_devel { + # (1) check for a non-null nethack.setuppath - this handles + # any repo that has already been set up (but NOT checking + # out =v4 since nethack.setuppath will exist but + # DEVEL/VERSION will not). + # XXX if the source repo has been removed, we'll fall back to + # the third case - hopefully that's ok. + # XXX there's no way to recover from a missing source repo + # without editing .git/config. + my $path = `git config --local nethack.setuppath`; + chomp $path; + $path =~ s/DEVEL$//; # NOP if config not set + + # (2) else check the local directory; that will be correct for NHsource. + if(0 == length $path){ + $path = `git rev-parse --show-toplevel`; + chomp $path; + $path = '' unless(-d "$path${PDS}DEVEL"); + } + # (3) If that doesn't exist, check using the invocation path; that will be + # correct for other repos during nhgitset (but will also fail for + # checking out 3 over 4). + if(0 == length $path){ + # strip out "DEVEL" + $path = ($0 =~ m!^(.*)${PDS}DEVEL${PDS}.*?(*nla:DEVEL)!)[0]; + } + # Uh oh? + if(0==length($path) or (! -d "$path${PDS}DEVEL")){ + die "Can't locate DEVEL directory in '$path'."; + } + + # Handle checking out version <4 over version >=4. If + # this seems to be the situation, don't revert the code. + return 0 if(! -f "$path${PDS}DEVEL${PDS}VERSION"); + + my $version; + my $verfile = "$path${PDS}DEVEL${PDS}VERSION"; + open VERFH,"<",$verfile or die "xCan't open $verfile: $!"; + $version = 0+; + my $message = join('',); + close VERFH; + die "Valid version not found in $verfile" unless($version >= 4); + return ($version,$message) if($version > 0); + return 0; +} + +sub version_in_git { + my $vtemp = `git config --local --get nethack.setupversion`; + chomp($vtemp); + return $vtemp if($vtemp > 0); + return 0; +} + +sub version_set_git { + my $version_new = $_[0]; + + system("git config nethack.setupversion $version_new"); + if($?){ + die "Can't set nethack.setupversion $version_new: $?,$!\n"; + } } ### ### store githash and gitbranch in dat/gitinfo.txt ### +# CAUTION! This is run not just from git hooks, but also from +# sys/unix/gitinfo.sh sub nhversioning { use strict; use warnings; + # See if we're (probably) in a "git pull", in which case we need to + # check for upgrades. + my $check_upgrade = 1 if($_[0]); + + # Check for pre-v4 source repo. + my $is_sourcerepo; + { + chomp($is_sourcerepo = `git config --int --get nethack.is-sourcerepo`); + if(0 == length $is_sourcerepo){ # not set - assume old repo + $is_sourcerepo = 1; + }elsif($is_sourcerepo==1){ + ; + }elsif($is_sourcerepo==0){ + ; + } + } + + # Skip the skipping tests if we're being called directly. + # NB: post-commit has no args, but that will be caught by + # the next test for non-source repos. + if($#ARGV != -1){ + # Skip this if we didn't change branches, but see if we need to warn. + if(defined($ARGV[2]) and ($ARGV[2] == 0)){ + # Because we can create an out of sync state, possibly warn. + my $ref = $ARGV[1]; + if($is_sourcerepo and (0 != 0+`git diff --name-only $ref $ref^ |grep ^DEVEL|wc -l`)){ + warn "WARNING: DEVEL directory changed. Versioning may be inconsistent\n"; + } + return + } + } + + if($check_upgrade){ + my $current_version = version_in_git(); + my($new_version,$message) = version_in_devel(); + if($new_version > $current_version){ + warn "nhgitset.pl and/or related programs have changed.\n"; + warn "Please re-run nhgitset.pl to update from version $current_version to $new_version.\n"; + if(length $message){ + warn "Additional information\n$message\n"; + } + } + } + + # Skip versioning if we aren't in a source repo. + return if(0==$is_sourcerepo); + my $git_sha = `git rev-parse HEAD`; $git_sha =~ s/\s+//g; my $git_branch = `git rev-parse --abbrev-ref HEAD`; @@ -77,7 +198,14 @@ sub nhversioning { die "git rev-parse failed" unless(length $git_sha and length $git_branch); my $exists = 0; - if (open my $fh, '<', 'dat/gitinfo.txt') { + no strict 'refs'; + no strict 'subs'; + my $file_gitinfo = "dat${PDS}gitinfo.txt"; + my $file_gittemp = "dat${PDS}TMPgitinfo.txt"; + use strict 'subs'; + use strict 'refs'; + + if (open my $fh, '<', $file_gitinfo) { $exists = 1; my $hashok = 0; my $branchok = 0; @@ -91,61 +219,71 @@ sub nhversioning { } close $fh; if ($hashok && $branchok) { - print "dat/gitinfo.txt unchanged, githash=".$git_sha."\n"; + print "$file_gitinfo unchanged, githash=".$git_sha."\n"; return; } } else { - print "WARNING: Can't find dat directory\n" unless(-d "dat"); + warn "WARNING: Can't find dat directory\n" unless(-d "dat"); + return; } - if (open my $fh, '>', 'dat/gitinfo.txt') { + if (open my $fh, '>', $file_gittemp) { my $how = ($exists ? "updated" : "created"); print $fh 'githash='.$git_sha."\n"; print $fh 'gitbranch='.$git_branch."\n"; - print "dat/gitinfo.txt ".$how.", githash=".$git_sha."\n"; + print "$file_gitinfo ".$how.", githash=".$git_sha."\n"; + if(close($fh)){ + if(rename($file_gittemp, $file_gitinfo)){ + ; # all ok + } else { + warn "WARNING: Can't rename $file_gittemp -> $file_gitinfo"; + } + } else { + warn "WARNING: Can't close temp file: $!"; + } } else { - print "WARNING: Unable to open dat/gitinfo.txt: $!\n"; + warn "WARNING: Unable to open $file_gitinfo: $!\n"; } } # PRIVATE sub do_hook { - my($p) = @_; - my $hname = $0; - $hname =~ s!^((.*$DS)|())(.*)!$1$p-$4!; - if(-x $hname){ - print TRACE "START $p: $hname\n" if($trace); + my($p) = @_; + my $hname = $0; + $hname =~ s!^((.*$DS)|())(.*)!$1$p-$4!; + if(-x $hname){ + print TRACE "START $p: $hname\n" if($trace); - open TOHOOK, "|-", $hname or die "open $hname: $!"; - print TOHOOK ; - close TOHOOK or die "close $hname: $! $?"; + open TOHOOK, "|-", $hname or die "open $hname: $!"; + print TOHOOK ; + close TOHOOK or die "close $hname: $! $?"; - print TRACE "END $p\n" if($trace); - } + print TRACE "END $p\n" if($trace); + } } sub trace_start { - return unless($trace); - my $self = shift; - open TRACE, ">>", $tracefile; - print TRACE "START CLIENT PID:$$ ARGV:\n"; - print TRACE "CWD: " . cwd() . "\n"; - print TRACE "[0] $0\n"; - my $x1; - for(my $x=0;$x $ENV{$k}\n"; - } + return unless($trace); + my $self = shift; + open TRACE, ">>", $tracefile; + print TRACE "START CLIENT PID:$$ ARGV:\n"; + print TRACE "CWD: " . cwd() . "\n"; + print TRACE "[0] $0\n"; + my $x1; + for(my $x=0;$x $ENV{$k}\n"; + } } BEGIN { - %saved_env = %ENV; - @saved_argv = @ARGV; - &trace_start; + %saved_env = %ENV; + @saved_argv = @ARGV; + &trace_start; } ### @@ -153,42 +291,41 @@ BEGIN { ### package NHIO::STDIN; sub TIEHANDLE { - my $class = shift; - my %fh; - # XXX yuck - if(ref @_[0]){ - $fh{DATA} = @_[0]; - } else { - $fh{DATA} = \@_; - } - $fh{NEXT} = 0; - return bless \%fh, $class; + my $class = shift; + my %fh; + if(ref @_[0]){ + $fh{DATA} = @_[0]; + } else { + $fh{DATA} = \@_; + } + $fh{NEXT} = 0; + return bless \%fh, $class; } sub READLINE { - my $self = shift; - return undef if($self->{EOF}); - if(wantarray){ - my $lim = $#{$self->{DATA}}; - my @ary = @{$self->{DATA}}[$self->{NEXT}..$lim]; - my @rv = @ary[$self->{NEXT}..$#ary]; - $self->{EOF} = 1; - return @rv; - } else{ - my $rv = $self->{DATA}[$self->{NEXT}]; - if(length $rv){ - $self->{NEXT}++; - return $rv; - } else { - $self->{EOF} = 1; - return undef; - } - } + my $self = shift; + return undef if($self->{EOF}); + if(wantarray){ + my $lim = $#{$self->{DATA}}; + my @ary = @{$self->{DATA}}[$self->{NEXT}..$lim]; + my @rv = @ary[$self->{NEXT}..$#ary]; + $self->{EOF} = 1; + return @rv; + } else{ + my $rv = $self->{DATA}[$self->{NEXT}]; + if(length $rv){ + $self->{NEXT}++; + return $rv; + } else { + $self->{EOF} = 1; + return undef; + } + } } sub EOF { - $self = shift; - return $self->{EOF}; + $self = shift; + return $self->{EOF}; } 1; @@ -223,11 +360,20 @@ NHgithook - common code for NetHack git hooks (and other git bits) (core hook code) &NHgithook::POST; +__END__ +=for nhgitset NHgithook Infrastructure for NetHack git hooks. + =head1 DESCRIPTION +Perl module for infrastructure of NetHack Git hooks. + Buffers call information so multiple independent actions may be coded for Git hooks and similar Git callouts. +Maintains C. + +Common routines for dealing with nethack.setupversion git config variable. + =head1 SETUP Changing the C<$trace> and C<$tracefile> variables requires editing the @@ -247,6 +393,9 @@ may be useful since multiple processes may be live at the same time. Some features not well tested, especially under Windows. +Not well documented, but almost no one needs to change (or even call) +this code. + =head1 AUTHOR Kenneth Lorber (keni@his.com) diff --git a/DEVEL/hooksdir/NHsubst b/DEVEL/hooksdir/NHsubst index 99621fc57..0c7aeb067 100755 --- a/DEVEL/hooksdir/NHsubst +++ b/DEVEL/hooksdir/NHsubst @@ -17,24 +17,24 @@ my $dbgfile = ($^O eq "MSWin32") ? "$ENV{TEMP}.$$" : "/tmp/trace.$$"; open TRACE, ">>", $rawin?"/dev/tty":(($debug==0)? $sink : $dbgfile); print TRACE "TEST TRACE\n"; if($debug){ - print TRACE "START CLIENT ARGV:\n"; - print TRACE "[0] $0\n"; - my $x1; - for(my $x=0;$x $ENV{$k}\n"; - } - print TRACE "CWD: " . `pwd`; - &dumpfile($ARGV[0], "[0O]"); - &dumpfile($ARGV[1], "[1A]"); - &dumpfile($ARGV[2], "[2B]"); - print TRACE "L=$ARGV[3]\n"; - print TRACE "END\n"; + print TRACE "START CLIENT ARGV:\n"; + print TRACE "[0] $0\n"; + my $x1; + for(my $x=0;$x $ENV{$k}\n"; + } + print TRACE "CWD: " . `pwd`; + &dumpfile($ARGV[0], "[0O]"); + &dumpfile($ARGV[1], "[1A]"); + &dumpfile($ARGV[2], "[2B]"); + print TRACE "L=$ARGV[3]\n"; + print TRACE "END\n"; } my $mark_len = $ARGV[3]; @@ -47,37 +47,37 @@ my $mark_end = '>' x $mark_len; my $PREFIX; # pick up the prefix for substitutions in this repo if($rawin){ - $PREFIX = "TEST"; + $PREFIX = "TEST"; } else { - $PREFIX = `git config --local --get nethack.substprefix`; - chomp($PREFIX); + $PREFIX = `git config --local --get nethack.substprefix`; + chomp($PREFIX); } my @out; my $cntout; if($rawin){ - @out = ; + @out = ; } else { - #system "git merge-file -p .... > temp - my $tags = "-L CURRENT -L ANCESTOR -L OTHER"; # XXX should "CURRENT" be "MINE"? - @out = `git merge-file -p $tags $ARGV[1] $ARGV[0] $ARGV[2]`; + #system "git merge-file -p .... > temp + my $tags = "-L CURRENT -L ANCESTOR -L OTHER"; # XXX should "CURRENT" be "MINE"? + @out = `git merge-file -p $tags $ARGV[1] $ARGV[0] $ARGV[2]`; #NB: we don't check the exit value because it's useless - print TRACE "MERGE-FILE START\n".join("",@out)."MERGE-FILE END\n"; + print TRACE "MERGE-FILE START\n".join("",@out)."MERGE-FILE END\n"; } ($cntout,@out) = &edit_merge(@out); if($rawin){ - print "COUNT: $cntout\n"; - print @out; + print "COUNT: $cntout\n"; + print @out; } else { - # spit @out to $ARGV[1] (careful: what about EOL character?) - open OUT, ">$ARGV[1]" or die "Can't open $ARGV[1]"; - print OUT @out; - close OUT; + # spit @out to $ARGV[1] (careful: what about EOL character?) + open OUT, ">$ARGV[1]" or die "Can't open $ARGV[1]"; + print OUT @out; + close OUT; - print TRACE "WRITING START ($ARGV[1])\n".join("",@out)."WRITING END\n"; - &dumpfile($ARGV[1], "READBACK"); + print TRACE "WRITING START ($ARGV[1])\n".join("",@out)."WRITING END\n"; + &dumpfile($ARGV[1], "READBACK"); } print TRACE "COUNT: $cntout\n"; @@ -95,37 +95,37 @@ exit( ($cntout>0) ? 1 : 0); # keep failing so we don't need to keep changing the setup while building this script sub dumpfile { - my($file, $tag) = @_; - print TRACE "FILE $tag START\n"; - print TRACE `hexdump -C $file`; - print TRACE "FILE END\n"; + my($file, $tag) = @_; + print TRACE "FILE $tag START\n"; + print TRACE `hexdump -C $file`; + print TRACE "FILE END\n"; } sub edit_merge { - my(@input) = @_; - # $::count is a bit ugly XXX - local $::count = 0; # we need the number of conflicts for exit() - my @out; + my(@input) = @_; + # $::count is a bit ugly XXX + local $::count = 0; # we need the number of conflicts for exit() + my @out; - local $_; - while($_ = shift @input){ - if(m/^$mark_start /){ - print TRACE "FOUND A CONFLICT\n"; - my @conflict; - push(@conflict, $_); - while($_ = shift @input){ - push(@conflict, $_); - if(m/^$mark_end /){ - last; - } - } - push(@out, &edit_conflict(@conflict)); - } else { - push(@out, $_); + local $_; + while($_ = shift @input){ + if(m/^$mark_start /){ + print TRACE "FOUND A CONFLICT\n"; + my @conflict; + push(@conflict, $_); + while($_ = shift @input){ + push(@conflict, $_); + if(m/^$mark_end /){ + last; } + } + push(@out, &edit_conflict(@conflict)); + } else { + push(@out, $_); } - print TRACE "RETURN count=$::count\n"; - return($::count, @out); + } + print TRACE "RETURN count=$::count\n"; + return($::count, @out); } sub edit_conflict { @@ -305,41 +305,41 @@ print TRACE "MVM: -$varname-$oursval-$theirval-\n"; package PREFIX; # Resolve the conflict of a single var's 2 values. Return undef to leave the conflict. sub Date { - my($PREFIX, $varname, $mine, $theirs) = @_; - my $m = ($mine =~ m/(\d+)/)[0]; - my $t = ($theirs =~ m/(\d+)/)[0]; - return undef unless ($m>0) && ($t>0); + my($PREFIX, $varname, $mine, $theirs) = @_; + my $m = ($mine =~ m/(\d+)/)[0]; + my $t = ($theirs =~ m/(\d+)/)[0]; + return undef unless ($m>0) && ($t>0); - return "\$$PREFIX-$varname: " . (($m>$t)?$mine:$theirs) .' $'; + return "\$$PREFIX-$varname: " . (($m>$t)?$mine:$theirs) .' $'; } #sub Header { #sub Author { sub Branch { - my($PREFIX, $varname, $mine, $theirs) = @_; - $mine =~ s/^\s+//; $mine =~ s/\s+$//; - $theirs =~ s/^\s+//; $theirs =~ s/\s+$//; - return "\$$PREFIX-$varname: $mine \$" if(length $mine); - return "\$$PREFIX-$varname: $theirs \$" if(length $theirs); - return "\$$PREFIX-$varname\$" if(length $theirs); + my($PREFIX, $varname, $mine, $theirs) = @_; + $mine =~ s/^\s+//; $mine =~ s/\s+$//; + $theirs =~ s/^\s+//; $theirs =~ s/\s+$//; + return "\$$PREFIX-$varname: $mine \$" if(length $mine); + return "\$$PREFIX-$varname: $theirs \$" if(length $theirs); + return "\$$PREFIX-$varname\$" if(length $theirs); } sub Revision { - my($PREFIX, $varname, $mine, $theirs) = @_; - my($m) = ($mine =~ m/1.(\d+)/); - my($t) = ($theirs =~ m/1.(\d+)/); - if($m > 0 && $t > 0){ - my $q = ($m > $t) ? $m : $t; - return "\$$PREFIX-$varname: 1.$q \$"; - } - if($m > 0){ - return "\$$PREFIX-$varname: 1.$m \$"; - } - if($t > 0){ - return "\$$PREFIX-$varname: 1.$t \$"; - } - return "\$$PREFIX-$varname\$"; + my($PREFIX, $varname, $mine, $theirs) = @_; + my($m) = ($mine =~ m/1.(\d+)/); + my($t) = ($theirs =~ m/1.(\d+)/); + if($m > 0 && $t > 0){ + my $q = ($m > $t) ? $m : $t; + return "\$$PREFIX-$varname: 1.$q \$"; + } + if($m > 0){ + return "\$$PREFIX-$varname: 1.$m \$"; + } + if($t > 0){ + return "\$$PREFIX-$varname: 1.$t \$"; + } + return "\$$PREFIX-$varname\$"; } __END__ @@ -396,3 +396,17 @@ TEST 8: === /* NetHack 3.7 objnam.c $TEST-Date: 1426977394 2015/03/21 22:36:34 $ $TEST-Branch: master $:$TEST-Revision: 1.108 $ */ >>> d3 + +=for nhgitset NHsubst NetHack merge driver + +=head1 NAME + +C - NetHack merge driver + +=head1 SYNOPSIS + +(called from C, do not invoke directly) + +=head1 DESCRIPTION + +This is invoked by git through .git/config. diff --git a/DEVEL/hooksdir/NHtext b/DEVEL/hooksdir/NHtext index 7128c0d31..01848675b 100755 --- a/DEVEL/hooksdir/NHtext +++ b/DEVEL/hooksdir/NHtext @@ -3,6 +3,8 @@ # Copyright (c) 2015 by Kenneth Lorber, Kensington, Maryland # NetHack may be freely redistributed. See license for details. +# Not in use as of v3, but could come back in the future. + # clean/smudge filter for handling substitutions use strict; @@ -54,9 +56,9 @@ if($ARGV[0] eq "--clean"){ exit 1; } -# XXX for now, there isn't any - if we get called, we subst. No options for now. +# XX for now, there isn't any - if we get called, we subst. No options for now. # get relevant config info -#XXX +#XX #git check-attr -a $ARGV[1] # Process stdin to stdout. @@ -109,7 +111,7 @@ sub Date { my($val, $mode, $submode) = @_; if($mode eq "c"){ if($submode==0){ - # we add this to make merge easier for now XXX + # we add this to make merge easier for now XX my $now = time; # not %s below - may not be portable # YYYY/MM/DD HH:MM:SS $val = "$now " . strftime("%Y/%m/%d %H:%M:%S", gmtime($now)); diff --git a/DEVEL/hooksdir/nhhelp b/DEVEL/hooksdir/nhhelp new file mode 100644 index 000000000..90fb7d68f --- /dev/null +++ b/DEVEL/hooksdir/nhhelp @@ -0,0 +1,79 @@ +#!/usr/bin/perl +# $NHDT-Date: 1730238507 2024/10/29 21:48:27 $ $NHDT-Brev: keni-gitset:1.0 $ +# Copyright (c) 2024 by Kenneth Lorber, Kensington, Maryland +# NetHack may be freely redistributed. See license for details. + +# This deals with a git problem: there is no way to do: +# git help alias-name +# (yes, that will show the definition of the alias, but not show an actual +# help document). +# +# So we implement this: +# nhhelp +# With no arguments, run perldoc on this file. +# nhhelp FOO +# Run perldoc on .git/hooks/FOO (if it exists). + +if($#ARGV == -1){ + system("perldoc $0")==0 or die "perldoc error: $!\n"; + exit 0; +} +if($#ARGV == 0){ + if($ARGV[0] eq "-a"){ + &listhelp; + exit 0; + } + + chomp(my $target = `git config nethack.aliashelp.$ARGV[0]`); + my $file = ".git/hooks/$target"; + if(-f $file){ + system("perldoc $file")==0 or die "perldoc error: $!\n"; + } else { + print "Unknown name '$ARGV[0]'\n"; + &usage; + } + exit 0; +} +&usage; +exit 0; + +sub usage { +print <|-a] +E_O_M +} + +sub listhelp { + print "nhhelp is available for:\n"; + my @namelist = `git config --name-only --get-regexp 'nethack.aliashelp.*'`; + print "NAMELIST $?\n" if($?); + @namelist = map { + if(m/^nethack.aliashelp.(.*)/){ + chomp(my $x = `git config 'nethack.aliasdesc.$1'`); + sprintf("%-12s %s",$1,$x); + } + } sort @namelist; + print " " . join("\n ", @namelist)."\n"; + exit 0; +} + +__END__ +=for nhgitset nhhelp Help on NetHack git commands + +=head1 NAME + +C - NetHack git command for help on NetHack git commands + +=head1 SYNOPSIS + +C + +=head1 DESCRIPTION + +With no arguments, print this message. + +With one argument matching a NetHack git command, print the +documentation for that command. + +With the argument C<-a>, show all available nhhelp topics with one line +summaries. diff --git a/DEVEL/hooksdir/nhsub b/DEVEL/hooksdir/nhsub index 7e5fdbd2f..7aa183b65 100644 --- a/DEVEL/hooksdir/nhsub +++ b/DEVEL/hooksdir/nhsub @@ -1,5 +1,5 @@ #!/usr/bin/perl -# $NHDT-Date: 1524689646 2018/04/25 20:54:06 $ Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.7 $ +# $NHDT-Date: 1524689646 2018/04/25 20:54:06 $ Branch: NetHack-3.7 $:$NHDT-Revision: 1.7 $ # Copyright (c) 2015 by Kenneth Lorber, Kensington, Maryland # NetHack may be freely redistributed. See license for details. @@ -8,9 +8,11 @@ use strict; our %opt; #cmd v n f F m (other single char, but we don't care) my $mode; # a c d f (add, commit, date, date -f) +our $count; +our $skip; if(length $ENV{GIT_PREFIX}){ - chdir($ENV{GIT_PREFIX}) or die "Can't chdir $ENV{GIT_PREFIX}: $!"; + chdir($ENV{GIT_PREFIX}) or die "Can't chdir $ENV{GIT_PREFIX}: $!"; } #SO how do we know if a file has changed? @@ -23,74 +25,78 @@ if(length $ENV{GIT_PREFIX}){ # (see "git help status" for table) # No default. Undef means something unexpected happened. my %codes = ( - 'f M'=>1, 'f D'=>1, # [MD] not updated - 'a M'=>0, 'a D'=>0, - 'd M'=>0, 'd D'=>0, - 'c M'=>0, 'c D'=>0, + # [MD] not updated + 'f M'=>1, 'f D'=>1, + 'a M'=>0, 'a D'=>0, + 'd M'=>0, 'd D'=>0, + 'c M'=>0, 'c D'=>0, - 'dM '=>0, 'dMM'=>1, 'dMD'=>0, - 'aM '=>0, 'aMM'=>1, 'aMD'=>0, - 'cM '=>0, 'cMM'=>1, 'cMD'=>0, - 'fM '=>0, 'fMM'=>1, 'fMD'=>0, - # M [ MD] updated in index + 'dM '=>0, 'dMM'=>1, 'dMD'=>0, + 'aM '=>0, 'aMM'=>1, 'aMD'=>0, + 'cM '=>0, 'cMM'=>1, 'cMD'=>0, + 'fM '=>0, 'fMM'=>1, 'fMD'=>0, + # M [ MD] updated in index - 'dA '=>1, 'dAM'=>1, 'dAD'=>1, - 'aA '=>1, 'aAM'=>1, 'aAD'=>1, - 'cA '=>1, 'cAM'=>1, 'cAD'=>1, - 'fA '=>1, 'fAM'=>1, 'fAD'=>1, - # A [ MD] added to index + 'dA '=>1, 'dAM'=>1, 'dAD'=>1, + 'aA '=>1, 'aAM'=>1, 'aAD'=>1, + 'cA '=>1, 'cAM'=>1, 'cAD'=>1, + 'fA '=>1, 'fAM'=>1, 'fAD'=>1, + # A [ MD] added to index - 'dD '=>0, 'dDM'=>0, - 'aD '=>1, 'aDM'=>1, - 'cD '=>0, 'cDM'=>0, - 'fD '=>1, 'fDM'=>1, - # D [ M] deleted from index + 'dD '=>0, 'dDM'=>0, + 'aD '=>1, 'aDM'=>1, + 'cD '=>0, 'cDM'=>0, + 'fD '=>1, 'fDM'=>1, + # D [ M] deleted from index - 'dR '=>0, 'dRM'=>1, 'dRD'=>0, - 'aR '=>0, 'aRM'=>1, 'aRD'=>0, - 'cR '=>0, 'cRM'=>1, 'cRD'=>0, - 'fR '=>0, 'fRM'=>1, 'fRD'=>0, - # R [ MD] renamed in index + 'dR '=>0, 'dRM'=>1, 'dRD'=>0, + 'aR '=>0, 'aRM'=>1, 'aRD'=>0, + 'cR '=>0, 'cRM'=>1, 'cRD'=>0, + 'fR '=>0, 'fRM'=>1, 'fRD'=>0, + # R [ MD] renamed in index - 'dC '=>0, 'dCM'=>1, 'dCD'=>0, - 'aC '=>0, 'aCM'=>1, 'aCD'=>0, - 'cC '=>0, 'cCM'=>1, 'cCD'=>0, - 'fC '=>0, 'fCM'=>1, 'fCD'=>0, - # C [ MD] copied in index + 'dC '=>0, 'dCM'=>1, 'dCD'=>0, + 'aC '=>0, 'aCM'=>1, 'aCD'=>0, + 'cC '=>0, 'cCM'=>1, 'cCD'=>0, + 'fC '=>0, 'fCM'=>1, 'fCD'=>0, + # C [ MD] copied in index - 'aM '=>1, 'aA '=>1, 'aR '=>1, 'aC '=>1, - 'fM '=>1, 'fA '=>1, 'fR '=>1, 'fC '=>1, - # [MARC] index and work tree matches + 'aM '=>1, 'aA '=>1, 'aR '=>1, 'aC '=>1, + 'fM '=>1, 'fA '=>1, 'fR '=>1, 'fC '=>1, + # [MARC] index and work tree matches - 'd M'=>1, 'dMM'=>1, 'dAM'=>1, 'dRM'=>1, 'dCM'=>1, - 'a M'=>1, 'aMM'=>1, 'aAM'=>1, 'aRM'=>1, 'aCM'=>1, - 'c M'=>1, 'cMM'=>1, 'cAM'=>1, 'cRM'=>1, 'cCM'=>1, - 'f M'=>1, 'fMM'=>1, 'fAM'=>1, 'fRM'=>1, 'fCM'=>1, - # [ MARC] M work tree changed since index + 'd M'=>1, 'dMM'=>1, 'dAM'=>1, 'dRM'=>1, 'dCM'=>1, + 'a M'=>1, 'aMM'=>1, 'aAM'=>1, 'aRM'=>1, 'aCM'=>1, + 'c M'=>1, 'cMM'=>1, 'cAM'=>1, 'cRM'=>1, 'cCM'=>1, + 'f M'=>1, 'fMM'=>1, 'fAM'=>1, 'fRM'=>1, 'fCM'=>1, + # [ MARC] M work tree changed since index - 'd D'=>0, 'dMD'=>0, 'dAD'=>0, 'dRD'=>0, 'dCD'=>0, - 'a D'=>0, 'aMD'=>0, 'aAD'=>0, 'aRD'=>0, 'aCD'=>0, - 'c D'=>0, 'cMD'=>0, 'cAD'=>0, 'cRD'=>0, 'cCD'=>0, - 'f D'=>0, 'fMD'=>0, 'fAD'=>0, 'fRD'=>0, 'fCD'=>0, - # [ MARC] D deleted in work tree + 'd D'=>0, 'dMD'=>0, 'dAD'=>0, 'dRD'=>0, 'dCD'=>0, + 'a D'=>0, 'aMD'=>0, 'aAD'=>0, 'aRD'=>0, 'aCD'=>0, + 'c D'=>0, 'cMD'=>0, 'cAD'=>0, 'cRD'=>0, 'cCD'=>0, + 'f D'=>0, 'fMD'=>0, 'fAD'=>0, 'fRD'=>0, 'fCD'=>0, + # [ MARC] D deleted in work tree - # ------------------------------------------------- - # DD unmerged, both deleted - # AU unmerged, added by us - # UD unmerged, deleted by them - # UA unmerged, added by them - # DU unmerged, deleted by us - # AA unmerged, both added - # UU unmerged, both modified - # ------------------------------------------------- - 'a??'=>1, 'f??'=>1, # ?? untracked - 'd??'=>0, 'c??'=>0, + # ------------------------------------------------- + # DD unmerged, both deleted + # AU unmerged, added by us + # UD unmerged, deleted by them + # UA unmerged, added by them + # DU unmerged, deleted by us + # AA unmerged, both added + # UU unmerged, both modified + # ------------------------------------------------- + # ?? untracked + 'a??'=>1, 'f??'=>1, + 'd??'=>0, 'c??'=>0, - 'f!!'=>1, # !! ignored - 'a!!'=>0, 'd!!'=>0, 'c!!'=>0, + # !! ignored + 'f!!'=>1, - 'f@@'=>1, # @@ internal ignored - 'a@@'=>0, 'd@@'=>0, 'c@@'=>0 + 'a!!'=>0, 'd!!'=>0, 'c!!'=>0, + + 'f@@'=>1, # @@ internal ignored + 'a@@'=>0, 'd@@'=>0, 'c@@'=>0 ); # OS hackery @@ -123,309 +129,430 @@ my @rawlist0 = &cmdparse(@ARGV); # Let's try this for all commands. my @rawlist; foreach my $e (@rawlist0){ - if($e =~ m/[?*[\\]/){ - my @rv = &lsfiles(undef, $e); - push(@rawlist, @rv) if(@rv); - if($opt{f}){ - my @rv = &lsfiles('-i', $e); - push(@rawlist, @rv) if(@rv); - } - } else { - push(@rawlist, $e); + if($e =~ m/[?*[\\]/){ + my @rv = &lsfiles(undef, $e); + push(@rawlist, @rv) if(@rv); + if($opt{f}){ + my @rv = &lsfiles('-i', $e); + push(@rawlist, @rv) if(@rv); } + } else { + push(@rawlist, $e); + } } push(@rawlist,'.') if($#rawlist == -1); # pick up the prefix for substitutions in this repo -#TEST my $PREFIX = &git_config('nethack','substprefix'); -my $PREFIX = "NHDT"; +my $PREFIX = &git_config('nethack','substprefix'); +die "nethack.substprefix not set in git config" unless(length($PREFIX) > 0); print "PREFIX: '$PREFIX'\n" if($opt{v}); while(@rawlist){ - my $raw = shift @rawlist; - if(-f $raw){ + my $raw = shift @rawlist; + if(-f $raw){ &schedule_work($raw); next; + } + if(-d $raw){ + if($raw =~ m!$PDS.git$!o){ + print "SKIP $raw\n" if($opt{v}>=2); + next; } - if(-d $raw){ - if($raw =~ m!$PDS.git$!o){ - print "SKIP $raw\n" if($opt{v}>=2); - next; - } - opendir RDIR,$raw or die "Can't opendir: $raw"; - local($_); # needed until perl 5.11.2 - while($_ = readdir RDIR){ - next if(m/^\.\.?$/); - if(m/^\./ && $opt{f}){ - print " IGNORE-f: $raw$PDS$_\n" if($opt{v}>=2); - next; - } - push(@rawlist, $raw.$PDS.$_); - } - closedir RDIR; + opendir RDIR,$raw or die "Can't opendir: $raw"; + local($_); # needed until perl 5.11.2 + while($_ = readdir RDIR){ + next if(m/^\.\.?$/); + if(m/^\./ && $opt{f}){ + print " IGNORE-f: $raw$PDS$_\n" if($opt{v}>=2); + next; + } + push(@rawlist, $raw.$PDS.$_); } + closedir RDIR; + } # ignore other file types - if(! -e $raw){ - print "warning: missing file $raw\n"; + if(! -e $raw){ + print "warning: missing file $raw\n"; + } +} + +sub checkattr { + my($kw, $file) = @_; + my $attr = `git check-attr $kw -- $file`; + if($attr =~ m/$kw:\s+(.*)/){ + # This is a bug in git. What if the value of an attribute is the + # string "unset"? Sigh. + if(! $opt{F}){ + if($1 eq "unset" || $1 eq "unspecified"){ + print " NOATTR: $attr" if($opt{v}>=2); + return 0; + } } + return 1; + } + return 0; } # XXX could batch things up - later - sub schedule_work { - my($file) = @_; - print "CHECK: '$file'\n" if($opt{v}>=2); - local($_) = `git status --porcelain --ignored -- $file`; - my $key = $mode . join('',(m/^(.)(.)/)); - if(length $key == 1){ + my($file) = @_; + print "CHECK: '$file'\n" if($opt{v}>=2); + local($_) = `git status --porcelain --ignored -- $file`; + my $key = $mode . join('',(m/^(.)(.)/)); + if(length $key == 1){ # Hack. An unmodified, tracked file produces no output from # git status. Treat as another version of 'ignored'. - $key .= '@@'; + $key .= '@@'; + } + $key =~ s/-/ /g; # for Keni's locally mod'ed git + if(!exists $codes{$key}){ + die "I'm lost.\nK='$key' F=$file\nST=$_"; + } + if($codes{$key}==0){ + if($opt{v}>=2){ + print " IGNORE: $_" if(length); + print " IGNORE: !! $file\n" if(!length); } - $key =~ s/-/ /g; # for Keni's locally mod'ed git - if(!exists $codes{$key}){ - die "I'm lost.\nK='$key' F=$file\nST=$_"; - } - if($codes{$key}==0){ - if($opt{v}>=2){ - print " IGNORE: $_" if(length); - print " IGNORE: !! $file\n" if(!length); - } + return; + } + if($opt{F}){ + my $ign = `git check-ignore $file`; + if($ign !~ m/^\s*$/){ + print " IGNORE-F: $ign" if($opt{v}>=2); return; - } - if($opt{F}){ - my $ign = `git check-ignore $file`; - if($ign !~ m/^\s*$/){ - print " IGNORE-F: $ign" if($opt{v}>=2); - return; - } - } -# FALLTHROUGH and continue -#print "ACCEPT TEST\n"; # XXXXXXXXXX TEST -#return; + } + } - my $attr = `git check-attr NHSUBST -- $file`; - if($attr =~ m/NHSUBST:\s+(.*)/){ -# XXX this is a bug in git. What if the value of an attribute is the -# string "unset"? Sigh. - if(! $opt{F}){ - if($1 eq "unset" || $1 eq "unspecified"){ - print " NOATTR: $attr" if($opt{v}>=2); - return; - } - } - &process_file($file); - return; - } - die "Can't parse check-attr return: $attr\n"; + my $do_nhsubst = &checkattr('NHSUBST', $file); + my $do_nhdatesub = &checkattr('NH_DATESUB', $file); + &process_file($file, $do_nhsubst, $do_nhdatesub); +# XXX no longer reachable - parse errors not caught properly? +# die "Can't parse check-attr return: $attr\n"; } sub process_file { - my($file) = @_; - print "DOFIL: $file\n" if($opt{v}>=1); + my($file, $do_nhsubst, $do_nhdatesub) = @_; + print "DOFIL: $file\n" if($opt{v}>=1); + $count=0; # if we don't change anything, don't re-write the file - # For speed we read in the entire file then do the substitutions. - local($_) = ''; - my $len; - open INFILE, "<", $file or die "Can't open $file: $!"; - while(1){ - # On at least some systems we only get 64K. - my $len = sysread(INFILE, $_, 999999, length($_)); - last if($len == 0); - die "read failed: $!" unless defined($len); - } - close INFILE; + # For speed we read in the entire file then do the substitutions. + local($_) = ''; + my $len; + open INFILE, "<", $file or die "Can't open $file: $!"; + while(1){ + # On at least some systems we only get 64K. + my $len = sysread(INFILE, $_, 999999, length($_)); + last if($len == 0); + die "read failed: $!" unless defined($len); + } + close INFILE; - local $::current_file = $file; # used under handlevar - # $1 - var and value (including trailing space but not $) - # $2 - var - # $4 - value or undef -#s/\$$PREFIX-(([A-Za-z][A-Za-z0-9_]*)(: ([^\N{DOLLAR SIGN}]+))?)\$/&handlevar($2,$4)/eg; -my $count = s/\$$PREFIX-(([A-Za-z][A-Za-z0-9_]*)(: ([^\x24]+))?)\$/&handlevar($2,$4)/eg; -# XXX had o modifier, why? - return unless($count>0); - return if($opt{n}); - my $mode = 0777 & (stat($file))[2]; + local $::current_file = $file; # used under handle* + if($do_nhsubst){ + # TODO: This doesn't handle PREFIX-Assert. To do that, + # we need to capture an entire line, call new sub handlevars() + # and let it iterate across all PREFIX-Foo while + # maintaining $::skip and reverting (like &handledatesub does). + # Also: capture optional arg: $NHDT-Foo(arg)$ $NHDT-Foo(arg): value $ - my $ofile = $file . ".nht"; - open(TOUT, ">", $ofile) or die "Can't open $ofile"; + # $1 - var and value (including trailing space but not $) + # $2 - var + # $4 - value or undef + # \x24 == $ + s/\$$PREFIX-(([A-Za-z][A-Za-z0-9_]*)(: ([^\x24]+) )?)\$/&handlevar($2,$4,1)/eg; + } -# die "write failed: $!" unless defined syswrite(TOUT, $_); - my $offset = 0; - my $sent; -#print STDERR "L=",length,"\n"; - while($offset < length){ - $sent = syswrite(TOUT, $_, (length($_) - $offset), $offset); - die "write failed: $!" unless defined($sent); -#print STDERR "rv=$sent\n"; - last if($sent == (length($_) - $offset)); - $offset += $sent; -#print STDERR "loop: O=$offset\n"; - } + if($do_nhdatesub){ + # e.g: + #.\"DO NOT REMOVE NH_DATESUB .TH MAKEDEFS 6 "DATE(%-d %B %Y)" NETHACK + #.TH MAKEDEFS 6 "8 February 2022" NETHACK - close TOUT or die "Can't close $ofile"; - # Do the right thing for *nix and hope for the best elsewhere: - chmod($mode, $ofile)==1 or warn "Can't set filemode on $ofile"; - rename $ofile, $file or die "Can't rename $ofile to $file"; + # locate the keyword and the rest of the line, capturing the rest of the line (the pattern) + # use zero width assertions so we can capture them but not replace them (DOES THIS WORK?) + #and grab the next line (the replaced text) + + # $1 - pattern + # $2 - the next line (the expanded pattern) + s/ + \sNH_DATESUB\s+ # our trigger + (.*)[\n\r]+ # save the pattern in $1 + \K # but don't replace it + (.*) # save the oldvalue in $2 + / + &handledatesub($2, $1) # replace oldvalue + /emxg; + + #/m so ^$ match at each line + #/x this will be a mess, so attempt to comment it + } + +#print STDERR "COUNT = $count\n"; + return unless($count>0); + return if($opt{n}); + + my $ofile = $file . ".nht"; + my $mode = 0777 & (stat($file))[2]; # save the original mode + open(TOUT, ">", $ofile) or die "Can't open $ofile"; + + my $offset = 0; + my $sent; + while($offset < length){ + $sent = syswrite(TOUT, $_, (length($_) - $offset), $offset); + die "write failed: $!" unless defined($sent); + last if($sent == (length($_) - $offset)); + $offset += $sent; + } + + close TOUT or die "Can't close $ofile"; + # Do the right thing for *nix and hope for the best elsewhere: + chmod($mode, $ofile)==1 or warn "Can't set filemode on $ofile"; + rename $ofile, $file or die "Can't rename $ofile to $file"; } -# XXX docs for --fixup and --squash are wrong in git's synopsis. --file missing -# --message --template -t sub cmdparse { - my(@in) = @_; + my(@in) = @_; - # What are we doing? - $opt{cmd} = 'date'; # really nhsub - if($in[0] eq '--add'){ - $opt{cmd} = 'add'; - shift @in; - } - if($in[0] eq '--commit'){ - $opt{cmd} = 'commit'; - shift @in; - } + # What are we doing? + $opt{cmd} = 'date'; # really nhsub + if($in[0] eq '--add'){ + $opt{cmd} = 'add'; + shift @in; + } + if($in[0] eq '--commit'){ + $opt{cmd} = 'commit'; + shift @in; + } # add: -n -v # commit: --dry-run -v # nhsub: -n -v - while($in[0] =~ m/^-/){ - local($_) = $in[0]; - if($_ eq '--'){ - shift @in; - last; - } - if(m/^--/){ - if($opt{cmd} eq 'add' && $_ eq '--dry-run'){ - exit 0; - } - if($opt{cmd} eq 'commit' && $_ eq '--dry-run'){ - exit 0; - } - if($opt{cmd} eq 'add' && $_ eq '--refresh'){ - exit 0; - } - shift @in; - next; - } -# XXX this is messy - time for a rewrite? - if(m/^-(.*)/){ - foreach my $single ( split(//,$1) ){ - # don't do -v here from add/commit - if($single ne 'v'){ - # don't use -m from add/commit - if($opt{cmd} eq 'date' || $single ne 'm'){ - $opt{$single}++; - } - } elsif($opt{cmd} eq 'date'){ - $opt{$single}++; - } + while($in[0] =~ m/^-/){ + local($_) = $in[0]; + if($_ eq '--'){ + shift @in; + last; + } + if(m/^--/){ + if($opt{cmd} eq 'add' && $_ eq '--dry-run'){ + exit 0; + } + if($opt{cmd} eq 'commit' && $_ eq '--dry-run'){ + exit 0; + } + if($opt{cmd} eq 'add' && $_ eq '--refresh'){ + exit 0; + } + shift @in; + next; + } - if($opt{cmd} eq 'add' && $single eq 'n'){ - exit 0; - } + if(m/^-(.*)/){ + foreach my $single ( split(//,$1) ){ + # don't do -v here from add/commit + if($single ne 'v'){ + # don't use -m from add/commit + if($opt{cmd} eq 'date' || $single ne 'm'){ + $opt{$single}++; + } + } elsif($opt{cmd} eq 'date'){ + $opt{$single}++; + } + + if($opt{cmd} eq 'add' && $single eq 'n'){ + exit 0; + } #need to deal with options that eat a following element (-m, -F etc etc) #add: nothing? #commit: -c -C -F -m # -u mode is optional # -S keyid is optional - if($opt{cmd} eq 'commit'){ - if($single =~ m/[uS]/){ - last; - } - if($single =~ m/[cCFm]/){ -#XXX this will be a mess if the argument is wrong, but can we tell? No. - shift @in; - last; - } - } - } + if($opt{cmd} eq 'commit'){ + if($single =~ m/[uS]/){ + last; + } + if($single =~ m/[cCFm]/){ + # This will be a mess if the argument is wrong, but can we tell? No. + shift @in; + last; + } } - shift @in; + } } + shift @in; + } - ($mode) = ($opt{cmd} =~ m/^(.)/); - $mode = 'f' if($opt{cmd} eq 'date' && ($opt{f}||$opt{F})); - $mode = 'f' if($opt{cmd} eq 'add' && $opt{f}); + ($mode) = ($opt{cmd} =~ m/^(.)/); + $mode = 'f' if($opt{cmd} eq 'date' && ($opt{f}||$opt{F})); + $mode = 'f' if($opt{cmd} eq 'add' && $opt{f}); - if($opt{cmd} eq 'add' && $#in == -1){ - exit 0; - } - if($opt{cmd} eq 'commit' && $#in == -1){ - exit 0; - } - if($opt{cmd} eq 'add' && $opt{a} && $#in != -1){ - exit 0; - } - if($opt{cmd} eq 'add' && $opt{a}){ - my $x = `git rev-parse --show-toplevel`; - $x =~ s/[\n\r]+$//; - push(@in, $x); - } - return @in; # this is our file list + if($opt{cmd} eq 'add' && $#in == -1){ + # "git nhadd" with no files has nothing to work on + exit 0; + } + if($opt{cmd} eq 'commit' && $#in == -1){ + # "git nhcommit" with no args handles files already + # added, we assume with "git nhadd" + exit 0; + } + if($opt{cmd} eq 'commit' && $opt{a} && $#in == -1){ + # "git commit -a" does multiple things; we only care + # about modigied files. +#XXX this assumes $RS is set properly for Windows - need to check that + my @x = split(/$::RS/,`git ls-files -m`); + chomp(@x); + push(@in, @x); + } + if($opt{cmd} eq 'commit' && $opt{a} && $#in != -1){ + # Let git complain about this for us. + exit 0; + } +# "git add" doesn't have a -a option +# if($opt{cmd} eq 'add' && $opt{a} && $#in != -1){ +# exit 0; +# } +# I don't know what this was trying to do, but it's wrong. +# if($opt{cmd} eq 'add' && $opt{a}){ +# my $x = `git rev-parse --show-toplevel`; +# $x =~ s/[\n\r]+$//; +# push(@in, $x); +# } + return @in; # this is our file list } sub git_config { - my($section, $var) = @_; - my $raw = `git config --local --get $section.$var`; - $raw =~ s/[\r\n]*$//g; - return $raw if(length $raw); - die "Missing config var: [$section] $var\n"; + my($section, $var) = @_; + my $raw = `git config --local --get $section.$var`; + $raw =~ s/[\r\n]*$//g; + return $raw if(length $raw); + die "Missing config var: [$section] $var\n"; } -sub handlevar { - my($var, $val) = @_; -# print "HIT '$var' '$val'\n" if($debug2); +# (oldvalue, pattern) +sub handledatesub { + my $oldval = $_[0]; # used if assert fails + $skip = 0; # one if assert fails + my $out = $_[1]; # the pattern, which we'll edit in place +#print "OLD: '$oldval;\n"; +#print "PAT: '$out'\n"; + my $newvalue = $_[1]; + $out =~ s/ + \b # don't substitute on a partial word + (Assert|Date|Branch|Revision|Brev|Project) # $1 - keyword + \( # ( + ([^)]*) # $2 - argument + \) # ) + /&onedatesub($1,$2) + /egx; - my $subname = "PREFIX::$var"; - if(defined &$subname){ - no strict; - print " SUBIN: $var '$val'\n" if($opt{v}>=3); - $val =~ s/\s+$//; - $val = &$subname($val); - print " SUBOT: $var '$val'\n" if($opt{v}>=3); - } else { - warn "No handler for \$$PREFIX-$var\n"; + { + local $::x = $_[0] ne $out; + if ($::x){ + $count += $::x; +# warn "COUNT++"; } + } +#print STDERR "OUT SKIP=$skip count=$count\n"; +#print STDERR "OUT old='$oldval'\n new='$out'\n"; + return ($skip>0 or $count==0) ? $oldval : $out; +} + +sub onedatesub { + my($kw, $arg) = @_; + my $sname = "PREFIX::$kw"; + die "internal error, '$sname' not defined" unless defined(&$sname); + + no strict; + my $rv = &$sname(undef, $arg); + return $rv; +} + +# Assert(P=prefix) +# Date(format) +# Branch() +# Revision() +# Brev() Branch:Rev (aBREViated) + +sub handlevar { + my($var, $val, $wrap) = @_; +#print "HV '$var' '$val'\n"; + my $oldval = $val; + my $subname = "PREFIX::$var"; + if(defined &$subname){ + no strict; + print " SUBIN: $var '$val'\n" if($opt{v}>=3); + $val =~ s/\s+$//; + $val = &$subname($val, undef); + print " SUBOT: $var '$val'\n" if($opt{v}>=3); + { + local $::x = ($oldval ne $val); + if ($::x){ + $count += $::x; +# warn "COUNT2++ o='$oldval' v='$val'"; + } + } + } else { + warn "No handler for \$$PREFIX-$var\n"; + } + + if($wrap){ if(length $val){ return "\$$PREFIX-$var: $val \$"; } else { return "\$$PREFIX-$var\$"; } + } else { + return $val; + } } sub lsfiles { - my ($flags, $ps) = @_; - open RV, "-|", "git ls-files $flags '$ps'" or die "Can't ls-files"; - my @rv = ; - map { s/[\r\n]+$// } @rv; - if(!close RV){ - return undef if($! == 0); - die "close ls-files failed: $!"; - } - return undef if($#rv == -1); - return @rv; + my ($flags, $ps) = @_; + open RV, "-|", "git ls-files $flags '$ps'" or die "Can't ls-files"; + my @rv = ; + map { s/[\r\n]+$// } @rv; + if(!close RV){ + return undef if($! == 0); + die "close ls-files failed: $!"; + } + return undef if($#rv == -1); + return @rv; } package PREFIX; use POSIX qw(strftime); -# On push, put in the current date because we changed the file. -# On pull, keep the current value so we can see the last change date. sub Date { - my($val) = @_; - my $now; - if($opt{m}){ - my $hash = `git log -1 '--format=format:%H' $::current_file`; - #author keni 1429884677 -0400 - chomp($now = `git cat-file -p $hash | awk '/author/{print \$4}'`); + my(undef, $val) = @_; + my $now; + +# DONE XXX after several bug fixes, this set of ifs needs some cleanup +my $hash = `git log -1 '--format=format:%H' $::current_file`; + $now = $^T; +# if($opt{m} or not defined $hash){ + + # cope with file not yet added to git + if(not defined $hash){ + if(-f $::current_file){ + $now = (stat($::current_file))[9]; } else { - $now = time; + die "Can't find file '$::current_file'\n"; } + } elsif($opt{m}) { + #author keni 1429884677 -0400 + chomp($now = `git cat-file -p $hash | awk '/author/{print \$4}'`); + } +# } else { +# } + +# DONE XXX simplify this with %s ? +# my $fmt = length $val ? $val : "%Y/%m/%d %H:%M:%S"; + my $fmt = length $val ? $val : "%s %Y/%m/%d %H:%M:%S"; # YYYY/MM/DD HH:MM:SS - $val = "$now " . strftime("%Y/%m/%d %H:%M:%S", gmtime($now)); - return $val; +# $val = ((length $val==0)?"$now ":"") . strftime($fmt, gmtime($now)); + $val = strftime($fmt, gmtime($now)); + return $val; } #sub Header { @@ -434,26 +561,84 @@ sub Date { #} # NB: the standard-ish Revision line isn't enough - you need Branch:Revision - -# but we split it into 2 so we can use the standard processing code on Revision -# and just slip Branch in. +# but we split it into 2 so we can use the standard processing code on +# Revision and just slip Branch in. +# But see new Brev below. sub Branch { - my($val) = @_; - $val = `git symbolic-ref -q --short HEAD`; - $val =~ s/[\n\r]*$//; - $val =~ s/^\*\s*//; - $val = "(unknown)" unless($val =~ m/^[[:print:]]+$/); - return $val; + my $val; + $val = `git symbolic-ref -q --short HEAD`; + $val =~ s/[\n\r]*$//; + $val =~ s/^\*\s*//; + $val = "(unknown)" unless($val =~ m/^[[:print:]]+$/); + return $val; } sub Revision { - my($val) = @_; - my @val = `git log --follow --oneline $::current_file`; - my $ver = 0+$#val; - $ver = 0 if($ver < 0); - $val = "1.$ver"; - return $val; + my $val; + my @val = `git log --follow --oneline $::current_file`; + my $ver = 0+$#val; + $ver = 0 if($ver < 0); + $val = "1.$ver"; + return $val; } + +sub Brev { + my $branch; + $branch = `git symbolic-ref -q --short HEAD`; + $branch =~ s/[\n\r]*$//; + $branch =~ s/^\*\s*//; + $branch = "(unknown)" unless($branch =~ m/^[[:print:]]+$/); + + my @val = `git log --follow --oneline $::current_file`; + my $ver = 0+$#val; + $ver = 0 if($ver < 0); + $ver = "1.$ver"; + + my $val = "$branch:$ver"; + + return $val; +} + +sub Project { + my(undef, $arg) = @_; + my $pn = &::git_config('nethack','projectname'); + if(length $arg == 0){ + ; + } elsif($arg eq 'uc'){ + $pn = uc($pn); + } else { + warn "unknown argument '$arg' to Project()\n"; + } + return $pn; +} + + +sub Assert { + my(undef, $val) = @_; + + my($key, $arg) = ($val =~ m/^(.)=(.*)/); + if(!defined $arg){ + warn "syntax error: Assert($val)\n"; + $::skip = 1; + } + my $prefix = $2; + + # P assert arg matches saved prefix + if('P' eq $key){ + my $repoid = &::git_config('nethack','substprefix'); + if($repoid ne $prefix){ + $::skip = 1 + } + return ''; + } + + warn "Unknown Assert type '$key'\n"; + + return ''; +} + __END__ +=for nhgitset nhsub Update substitution variables =head1 NAME @@ -473,9 +658,15 @@ commands. The program re-writes those files listed on the command line; if the file is actually a directory, the program recurses into that directory tree. -Not all files found are re-written; some are ignored and those with no -substitution variables are not re-written. Unless changed by the options, -files that have not changed are not affected. +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 +re-written. Unless changed by the options, files that have not +changed are not affected. + +As a special case, a file not yet added to Git may be the target of +C. If no files are listed on the command line, the current directory is checked as if specified as C<.>. @@ -519,3 +710,83 @@ updated when last changed. (Do not use C/C after C assertions compare the current Git config variable +C +with the C and succeed if they are identical. + +=back + +=item Date(format) / PREFIX-Date + +This variable usually substitutes the current time but see C<-m> above. + +The format of the resulting date varies. For C or C with +no format, "%s %Y/%m/%d %H:%M:%S" is used. Otherwise the given strftime +format is used. + +=item Branch() / PREFIX-Branch + +This variable is replaced with the name of the currently checked out Git +branch. + +=item Revision() / PREFIX-Revision + +This variable's value emulates a RCS or CVS style revision number. +It consists of "1." followed by the number of commits affecting the +current file. + +=item Brev() / PREFIX-Brev + +This is a convenience variable that concatenates C, a colon, and +C. Short for "aBREViated". + +=item Project() + +Returns git variable nethack.projectname. If given the argument C, returns +an upper-case version of nethack.projectname. + +=back + +=head1 SUBSTITUTION STYLES + +=head2 Prefix Substitution + +If a file has the Git attribute C, any +text that looks like a RCS/CVS variable substitution will perform +that substitution. That is, either of the following forms: + + $Var$ + $Var: Value $ + +=head2 Template Substitution + +If a file has the Git attribute C, any line that has +the token C will replace the I line with the +variable expansion of everything after that token. For example: + + .\"DO NOT REMOVE NH_DATESUB .ds f2 DATE(%B %-d, %Y) + .ds f2 September 13, 2024 + +=head1 SEE ALSO + +git help gitattributes + +perldoc nhgitset.pl diff --git a/DEVEL/hooksdir/post-applypatch b/DEVEL/hooksdir/post-applypatch index ec6af70be..37170a48e 100755 --- a/DEVEL/hooksdir/post-applypatch +++ b/DEVEL/hooksdir/post-applypatch @@ -1,5 +1,5 @@ #!/usr/bin/perl -# $NHDT-Date: 1524689631 2018/04/25 20:53:51 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.1 $ +# NetHack 3.7 post-applypatch $NHDT-Date: 1524689631 2018/04/25 20:53:51 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1 $ # Copyright (c) 2015 by Kenneth Lorber, Kensington, Maryland # NetHack may be freely redistributed. See license for details. @@ -24,7 +24,11 @@ BEGIN { chomp $gitdir; push(@INC, $gitdir.$PDS."hooks"); } -use NHgithook; +eval {use NHgithook;}; +if($@){ + warn "loading NHgithook failed: $@"; + $nogithook = 1; +} #STARTUP-END &NHgithook::PRE; diff --git a/DEVEL/hooksdir/post-checkout b/DEVEL/hooksdir/post-checkout index d7e6c5724..db8578f94 100755 --- a/DEVEL/hooksdir/post-checkout +++ b/DEVEL/hooksdir/post-checkout @@ -24,10 +24,15 @@ BEGIN { chomp $gitdir; push(@INC, $gitdir.$PDS."hooks"); } -use NHgithook; +eval {use NHgithook;}; +if($@){ + warn "loading NHgithook failed: $@"; + $nogithook = 1; +} #STARTUP-END &NHgithook::PRE; -&NHgithook::nhversioning; +eval { &NHgithook::nhversioning unless($nogithook) }; +warn "nhversioning failed: $@" if($@); &NHgithook::POST; exit 0; diff --git a/DEVEL/hooksdir/post-commit b/DEVEL/hooksdir/post-commit index cb37b3800..753cb4a04 100755 --- a/DEVEL/hooksdir/post-commit +++ b/DEVEL/hooksdir/post-commit @@ -24,10 +24,17 @@ BEGIN { chomp $gitdir; push(@INC, $gitdir.$PDS."hooks"); } -use NHgithook; +eval {use NHgithook;}; +if($@){ + warn "loading NHgithook failed: $@"; + $nogithook = 1; +} #STARTUP-END &NHgithook::PRE; -&NHgithook::nhversioning; +eval { &NHgithook::nhversioning unless($nogithook) }; +warn "nhversioning failed: $@" if($@); &NHgithook::POST; exit 0; + + diff --git a/DEVEL/hooksdir/post-merge b/DEVEL/hooksdir/post-merge index a634f51ef..ca9e45e5f 100755 --- a/DEVEL/hooksdir/post-merge +++ b/DEVEL/hooksdir/post-merge @@ -25,10 +25,15 @@ BEGIN { push(@INC, $gitdir.$PDS."hooks"); } -use NHgithook; +eval {use NHgithook;}; +if($@){ + warn "loading NHgithook failed: $@"; + $nogithook = 1; +} #STARTUP-END &NHgithook::PRE; -&NHgithook::nhversioning; +eval { &NHgithook::nhversioning(1) unless($nogithook) }; +warn "nhversioning failed: $@" if($@); &NHgithook::POST; exit 0; diff --git a/DEVEL/hooksdir/post-rewrite b/DEVEL/hooksdir/post-rewrite index 655c1a0cd..3dcc7c18c 100755 --- a/DEVEL/hooksdir/post-rewrite +++ b/DEVEL/hooksdir/post-rewrite @@ -24,7 +24,11 @@ BEGIN { chomp $gitdir; push(@INC, $gitdir.$PDS."hooks"); } -use NHgithook; +eval {use NHgithook;}; +if($@){ + warn "loading NHgithook failed: $@"; + $nogithook = 1; +} #STARTUP-END &NHgithook::saveSTDIN; diff --git a/DEVEL/hooksdir/pre-applypatch b/DEVEL/hooksdir/pre-applypatch index 3b57a6f53..2ec0588a9 100755 --- a/DEVEL/hooksdir/pre-applypatch +++ b/DEVEL/hooksdir/pre-applypatch @@ -24,7 +24,11 @@ BEGIN { chomp $gitdir; push(@INC, $gitdir.$PDS."hooks"); } -use NHgithook; +eval {use NHgithook;}; +if($@){ + warn "loading NHgithook failed: $@"; + $nogithook = 1; +} #STARTUP-END &NHgithook::PRE; diff --git a/DEVEL/hooksdir/pre-auto-gc b/DEVEL/hooksdir/pre-auto-gc index d1ab9b005..22e45b666 100755 --- a/DEVEL/hooksdir/pre-auto-gc +++ b/DEVEL/hooksdir/pre-auto-gc @@ -24,7 +24,11 @@ BEGIN { chomp $gitdir; push(@INC, $gitdir.$PDS."hooks"); } -use NHgithook; +eval {use NHgithook;}; +if($@){ + warn "loading NHgithook failed: $@"; + $nogithook = 1; +} #STARTUP-END &NHgithook::PRE; diff --git a/DEVEL/hooksdir/pre-commit b/DEVEL/hooksdir/pre-commit index bb5dbfe84..b19fc2fd4 100755 --- a/DEVEL/hooksdir/pre-commit +++ b/DEVEL/hooksdir/pre-commit @@ -24,7 +24,11 @@ BEGIN { chomp $gitdir; push(@INC, $gitdir.$PDS."hooks"); } -use NHgithook; +eval {use NHgithook;}; +if($@){ + warn "loading NHgithook failed: $@"; + $nogithook = 1; +} #STARTUP-END &NHgithook::PRE; diff --git a/DEVEL/hooksdir/pre-push b/DEVEL/hooksdir/pre-push index c2639fa2e..a578ce529 100755 --- a/DEVEL/hooksdir/pre-push +++ b/DEVEL/hooksdir/pre-push @@ -24,7 +24,11 @@ BEGIN { chomp $gitdir; push(@INC, $gitdir.$PDS."hooks"); } -use NHgithook; +eval {use NHgithook;}; +if($@){ + warn "loading NHgithook failed: $@"; + $nogithook = 1; +} #STARTUP-END &NHgithook::saveSTDIN; diff --git a/DEVEL/hooksdir/pre-rebase b/DEVEL/hooksdir/pre-rebase index 7f447a082..4c472ef63 100755 --- a/DEVEL/hooksdir/pre-rebase +++ b/DEVEL/hooksdir/pre-rebase @@ -24,7 +24,11 @@ BEGIN { chomp $gitdir; push(@INC, $gitdir.$PDS."hooks"); } -use NHgithook; +eval {use NHgithook;}; +if($@){ + warn "loading NHgithook failed: $@"; + $nogithook = 1; +} #STARTUP-END &NHgithook::PRE; diff --git a/DEVEL/hooksdir/prepare-commit-msg b/DEVEL/hooksdir/prepare-commit-msg index b6813851a..a1ff14292 100755 --- a/DEVEL/hooksdir/prepare-commit-msg +++ b/DEVEL/hooksdir/prepare-commit-msg @@ -1,5 +1,5 @@ #!/usr/bin/perl -# $NHDT-Date: 1524689633 2018/04/25 20:53:53 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.1 $ +# NetHack 3.7 prepare-commit-msg $NHDT-Date: 1524689633 2018/04/25 20:53:53 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1 $ # Copyright (c) 2015 by Kenneth Lorber, Kensington, Maryland # NetHack may be freely redistributed. See license for details. @@ -24,7 +24,11 @@ BEGIN { chomp $gitdir; push(@INC, $gitdir.$PDS."hooks"); } -use NHgithook; +eval {use NHgithook;}; +if($@){ + warn "loading NHgithook failed: $@"; + $nogithook = 1; +} #STARTUP-END &NHgithook::PRE; diff --git a/DEVEL/nhgitset.pl b/DEVEL/nhgitset.pl index e29cae2b4..b20b5c57c 100755 --- a/DEVEL/nhgitset.pl +++ b/DEVEL/nhgitset.pl @@ -3,32 +3,29 @@ # Copyright (c) 2015 by Kenneth Lorber, Kensington, Maryland # NetHack may be freely redistributed. See license for details. -# value of nethack.setupversion we will end up with when this is done -# version 1 is reserved for repos checked out before versioning was added -my $version_new = 3; -my $version_old = 0; # current version, if any (0 is no entry ergo new repo) use Cwd; use Getopt::Std; # Activestate Perl doesn't include File::Spec. Grr. BEGIN { - eval "require File::Spec::Functions"; - if($@){ - die <import; + } + File::Spec::Functions->import; } exit 1 unless(getopts('nvf')); # TODO: this can probably have better output -# OS hackery -my $DS = quotemeta('/'); # Directory Separator (for regex) -my $DSP = '/'; # ... for printing +BEGIN { + # OS hackery + $DS = quotemeta('/'); # Directory Separator (for regex) + $PDS = '/'; # ... for printing # Temporarily disabled; there's something weird about msys # msys: POSIXish over a Windows filesystem (so / not \ but \r\n not \n). #if($^O eq "msys"){ @@ -37,78 +34,130 @@ my $DSP = '/'; # ... for printing # # NB: We don't need to do anything about File::Spec. It doesn't know # # about msys but it defaults to Unix, so we'll be ok. #} -if($^O eq "MSWin32"){ + if($^O eq "MSWin32"){ $DS = quotemeta('\\'); - $DSP = '\\'; + $PDS = '\\'; + } + + # Fix @INC so we can 'use NHgithook' before it's installed. + # Set $is_sourcerepo while we're at it - same logic. + { + # Special case for running nhgitset against a different repo. + # Must preceed the normal case! + # NB: we use $DEVhooksdir later in the program + $DEVhooksdir = ($0 =~ m!^(.*)$DS!)[0]; + chomp($DEVhooksdir); + $DEVhooksdir .= $PDS."hooksdir"; + push(@INC, $DEVhooksdir) if(-d $DEVhooksdir); + # This one is for the normal case (in NHsource) + my $topdir = `git rev-parse --show-toplevel`; + chomp $topdir; + $topdir .= "${PDS}DEVEL${PDS}hooksdir"; + push(@INC, $topdir) if(-d $topdir); + $is_sourcerepo = 0; + $is_sourcerepo = 1 if(-d $topdir); + } } +use NHgithook; + # current (installed) version, if any (0 is no entry ergo new repo) +my $version_old = NHgithook::version_in_git; + # version this program will install +my($version_new,$message_new) = NHgithook::version_in_devel; + +if(0==$version_new and !opt_f){ + # Edge case: this repo has been set up using version >= 4, but now we're running + # nhgitset after checking out DEVEL supporting version <4. + # Use -f to recover from broken DEVEL code. + print STDERR "DEVEL has version <4 code but version >=4 code already installed. Stopping.\n"; + print STDERR "(If you need to reinstall the old code, rerun with -f\n"; + exit 0; +} + + +die "Valid version not found in DEVEL/VERSION" unless(0==$version_new or $version_new >= 4); + # make sure we're at the top level of a repo if(! -d ".git"){ - die "This is not the top level of a git repository.\n"; + die "This is not the top level of a git repository.\n"; } -my $vtemp = `git config --local --get nethack.setupversion`; -chomp($vtemp); -if($vtemp > 0){ - $version_old = 0+$vtemp; - if($version_old != $version_new){ - print STDERR "Migrating from setup version $version_old to $version_new\n" if($opt_v); - } +if($version_old >= $version_new and !opt_f){ + print STDERR "Nothing to do.\n"; + exit 0; } -# legacy check: -if(length $vtemp == 0){ - if(`git config --get merge.NHsubst.name` =~ m/^Net/){ - $version_old = 1; - print STDERR "Migrating to setup version 1\n" if($opt_v); + +if($version_old > 0){ + if($version_old != $version_new){ + print STDERR "Migrating from setup version $version_old to $version_new\n"; + if(length $message_new){ + print STDERR "Additional information:\n$message_new\n"; } + } +} + + +# legacy check: +if(length $version_old == 0){ + if(`git config --get merge.NHsubst.name` =~ m/^Net/){ + $version_old = 1; + print STDERR "Migrating to setup version 1\n" if($opt_v); + } } my $gitadddir = `git config --get nethack.gitadddir`; chomp($gitadddir); if(length $gitadddir){ - if(! -d $gitadddir){ - die "nethack.gitadddir has invalid value '$gitadddir'\n"; - } + if(! -d $gitadddir){ + die "nethack.gitadddir has invalid value '$gitadddir'\n"; + } } print STDERR "nethack.gitadddir=$gitadddir\n" if($opt_v); # This is (relatively) safe because we know we're at R in R/DEVEL/nhgitset.pl my $srcdir = ($0 =~ m!^(.*)$DS!)[0]; +#XXX do I really want a full path for srcdir? how badly? + if(! -f catfile($srcdir, 'nhgitset.pl')){ - die "I can't find myself in '$srcdir'\n"; + die "I can't find myself in '$srcdir'\n"; } print STDERR "Copying from: $srcdir\n" if($opt_v); if($opt_f || $version_old==0){ - print STDERR "Configuring line endings\n" if($opt_v); - unlink catfile('.git','index') unless($opt_n); - system("git reset") unless($opt_n); - system("git config --local core.safecrlf true") unless($opt_n); - system("git config --local core.autocrlf false") unless($opt_n); + print STDERR "Configuring line endings\n" if($opt_v); + system("git reset") unless($opt_n); + &add_config('core.safecrlf', 'true') unless($opt_n); + &add_config('core.autocrlf', 'false') unless($opt_n); } elsif($version_old <2){ - my $xx = `git config --get --local core.safecrlf`; - if($xx !~ m/true/){ - print STDERR "\nNeed to 'rm .git${DSP}index;git reset'.\n"; - print STDERR " When ready to proceed, re-run with -f flag.\n"; - exit 2; - } + my $xx = `git config --get --local core.safecrlf`; + if($xx !~ m/true/){ + print STDERR "\nNeed to 'rm .git${PDS}index;git reset'.\n"; + print STDERR " When ready to proceed, re-run with -f flag.\n"; + exit 2; + } } - - print STDERR "Installing aliases\n" if($opt_v); $addpath = catfile(curdir(),'.git','hooks','NHadd'); &add_alias('nhadd', "!$addpath add"); + &add_help('nhadd', 'NHadd'); &add_alias('nhcommit', "!$addpath commit"); + &add_help('nhcommit', 'NHadd'); my $nhsub = catfile(curdir(),'.git','hooks','nhsub'); &add_alias('nhsub', "!$nhsub"); + &add_help('nhsub', 'nhsub'); +&add_alias('nhhelp', '!'.catfile(curdir(),'.git','hooks','nhhelp')); + &add_help('nhhelp', 'nhhelp'); + +&add_help('NHsubst', 'NHsubst'); +&add_help('NHgithook', 'NHgithook.pm'); +&add_help('nhgitset', 'gitsetdocs', '../../DEVEL/nhgitset.pl'); -print STDERR "Installing filter/merge\n" if($opt_v); -# XXXX need it in NHadd to find nhsub??? # removed at version 3 +#print STDERR "Installing filter/merge\n" if($opt_v); #if($^O eq "MSWin32"){ # $cmd = '.git\\\\hooks\\\\NHtext'; #} else { @@ -117,179 +166,211 @@ print STDERR "Installing filter/merge\n" if($opt_v); #&add_config('filter.NHtext.clean', "$cmd --clean %f"); #&add_config('filter.NHtext.smudge', "$cmd --smudge %f"); if($version_old == 1 or $version_old == 2){ - print STDERR "Removing filter.NHtext\n" if($opt_v); - system('git','config','--unset','filter.NHtext.clean') unless($opt_n); - system('git','config','--unset','filter.NHtext.smudge') unless($opt_n); - system('git','config','--remove-section','filter.NHtext') unless($opt_n); + print STDERR "Removing filter.NHtext\n" if($opt_v); + system('git','config','--unset','filter.NHtext.clean') unless($opt_n); + system('git','config','--unset','filter.NHtext.smudge') unless($opt_n); + system('git','config','--remove-section','filter.NHtext') unless($opt_n); - print STDERR "Removing NHtext\n" if($opt_v); - unlink catfile(curdir(),'.git','hooks','NHtext') unless($opt_n); + print STDERR "Removing NHtext\n" if($opt_v); + unlink catfile(curdir(),'.git','hooks','NHtext') unless($opt_n); } +&add_config('nethack.setuppath',$srcdir); +&add_config('nethack.is-sourcerepo',0+$is_sourcerepo); + $cmd = catfile(curdir(),'.git','hooks','NHsubst'); &add_config('merge.NHsubst.name', 'NetHack Keyword Substitution'); &add_config('merge.NHsubst.driver', "$cmd %O %A %B %L"); print STDERR "Running directories\n" if($opt_v); -foreach my $dir ( glob("$srcdir$DS*") ){ - next unless(-d $dir); +# copy directories into .git (right now that's just hooks and nhgitset.pl) +my @gitadd = length($gitadddir)?glob("$gitadddir$DS*"):undef; +foreach my $dir ( (glob("$srcdir$DS*"), @gitadd) ){ + next unless(-d $dir); - my $target = catfile($dir, 'TARGET'); - next unless(-f $target); + my $target = catfile($dir, 'TARGET'); + next unless(-f $target); - open TARGET, '<', $target or die "$target: $!"; - my $targetpath = ; - # still have to eat all these line endings under msys, so instead of chomp use this: - $targetpath =~ s![\r\n]!!g; - close TARGET; - print STDERR "Directory $dir -> $targetpath\n" if($opt_v); + open TARGET, '<', $target or die "$target: $!"; + my $targetpath = ; + # still have to eat all these line endings under msys, so instead of chomp use this: + $targetpath =~ s![\r\n]!!g; + close TARGET; + print STDERR "Directory $dir -> $targetpath\n" if($opt_v); - my $enddir = $dir; - $enddir =~ s!.*$DS!!; - if(! &process_override($enddir, "INSTEAD")){ - &process_override($enddir, "PRE"); - my $fnname = "do_dir_$enddir"; - if(defined &$fnname){ - &$fnname($dir, $targetpath); - } - &process_override($enddir, "POST"); + my $enddir = $dir; + $enddir =~ s!.*$DS!!; + if(! &process_override($enddir, "INSTEAD")){ + &process_override($enddir, "PRE"); + my $fnname = "do_dir_$enddir"; + if(defined &$fnname){ + &$fnname($dir, $targetpath); } + &process_override($enddir, "POST"); + } } +&do_file_nhgitset(); -&check_prefix; # for variable substitution +&check_gitvars; # for variable substitution -if($version_old != $version_new){ - print STDERR "Setting version to $version_new\n" if($opt_v); - if(! $opt_n){ - system("git config nethack.setupversion $version_new"); - if($?){ - die "Can't set nethack.setupversion $version_new: $?,$!\n"; - } - } +if($version_old != $version_new or $opt_f){ + print STDERR "Setting version to $version_new\n" if($opt_v); + NHgithook::version_set_git($version_new) if(! $opt_n); } exit 0; -sub process_override { - my($srcdir, $plname) = @_; - return 0 unless(length $gitadddir); +# @files: [0] is the name under .git/hooks; others are places to +# check during configuration +sub add_help { + my($cmd, @files) = @_; - my $plpath = catfile($gitadddir, $srcdir, $plname); -#print STDERR " ",catfile($srcdir, $plname),"\n"; # save this for updating docs - list of overrides - return 0 unless(-x $plpath); - - print STDERR "Running $plpath\n" if($opt_v); - # current directory is top of target repo - - unless($opt_n){ - system("$plpath $opt_v") and die "Callout $plpath failed: $?\n"; + &add_config("nethack.aliashelp.$cmd", $files[0]); + # pull out =for nhgitset CMD description... + my $desc; + foreach my $file (@files){ + open my $fh, "<", "$DEVhooksdir/$file"; + if($fh){ + while(<$fh>){ + m/^=for\s+nhgitset\s+\Q$cmd\E\s+(.*)/ && do { + $desc = $1; + goto found; + } + } + close $fh; + } else { + warn "Can't open: '$DEVhooksdir/$file' ($!)\n"; } - return 1; + } +found: + + if($desc){ + &add_config("nethack.aliasdesc.$cmd", $desc); + } else { + &add_config("nethack.aliasdesc.$cmd", "(no description available)"); + } +} + +sub process_override { + my($srcdir, $plname) = @_; + return 0 unless(length $gitadddir); + + my $plpath = catfile($gitadddir, $srcdir, $plname); + return 0 unless(-x $plpath); + + print STDERR "RunningOverride $plpath\n" if($opt_v); + + # current directory is top of target repo + unless($opt_n){ + system("$plpath $opt_v") and die "Callout $plpath failed: $?\n"; + } + return 1; } sub add_alias { - my($name, $def) = @_; - &add_config("alias.$name",$def); + my($name, $def) = @_; + &add_config("alias.$name",$def); } sub add_config { - my($name, $val) = @_; - system('git', 'config', '--local', $name, $val) unless($opt_n); + my($name, $val) = @_; + system('git', 'config', '--local', $name, $val) unless($opt_n); +} + +sub check_gitvars { + &check_prefix("substprefix"); + &check_prefix("projectname"); } sub check_prefix { - my $lcl = `git config --local --get nethack.substprefix`; - chomp($lcl); - if(0==length $lcl){ - my $other = `git config --get nethack.substprefix`; - chomp($other); - if(0==length $other){ - print STDERR "ERROR: nethack.substprefix is not set anywhere. Set it and re-run.\n"; - exit 2; - } else { - &add_config('nethack.substprefix', $other); - print STDERR "Copying prefix '$other' to local repository.\n" if($opt_v); - } - $lcl = $other; # for display below + my $which = $_[0]; + my $lcl = `git config --local --get nethack.$which`; + chomp($lcl); + if(0==length $lcl){ + my $other = `git config --get nethack.$which`; + chomp($other); + if(0==length $other){ + print STDERR "ERROR: nethack.$which is not set anywhere. Set it and re-run.\n"; + exit 2; + } else { + &add_config('nethack.$which', $other); + print STDERR "Copying prefix '$other' to local repository.\n" if($opt_v); } - print "\n\nUsing prefix '$lcl' - PLEASE MAKE SURE THIS IS CORRECT\n\n"; + $lcl = $other; # for display below + } + print "Using $which '$lcl' - PLEASE MAKE SURE THIS IS CORRECT\n"; } sub do_dir_DOTGIT { -if(1){ - # We are NOT going to mess with config now. - return; -} else { - my($srcdir, $targetdir) = @_; -#warn "do_dir_DOTGIT($srcdir, $targetdir)\n"; - my $cname = "$srcdir/config"; - if(-e $cname){ - print STDERR "Appending to .git/config\n" if($opt_v); - open CONFIG, ">>.git/config" or die "open .git/config: $!"; - open IN, "<", $cname or die "open $cname: $!"; - my @data = ; - print CONFIG @data; - close IN; - close CONFIG; - } else { - print STDERR " Nothing to add to .git/config\n" if($opt_v); - } -# XXX are there other files in .git that we might want to handle? -# So just in case: - for my $file ( glob("$srcdir/*") ){ - next if( $file =~ m!.*/TARGET$! ); - next if( $file =~ m!.*/config$! ); - die "ERROR: no handler for $file\n"; - } -} + my($srcdir, $targetdir) = @_; + # not currently in use so just bail + return; + # are there other files in .git that we might want to handle? + # So just in case: + for my $file ( glob("$srcdir/*") ){ + next if( $file =~ m!.*/TARGET$! ); + next if( $file =~ m!.*/config$! ); + die "ERROR: no handler for $file\n"; + } } sub do_dir_hooksdir { - my($srcdir, $targetdir) = @_; + my($srcdir, $targetdir) = @_; - unless (-d $targetdir){ + unless (-d $targetdir){ # Older versions of git, when cloning a repo and # the expected source templates directory does not # exist, does not create .git/hooks. So do it here. - mkdir $targetdir; - print STDERR "WARNING: .git/hooks had to be created.\n"; - print STDERR " You may want to update git.\n"; + mkdir $targetdir; + print STDERR "WARNING: .git/hooks had to be created.\n"; + print STDERR " You may want to update git.\n"; + } + + for my $path ( glob("$srcdir$DS*") ){ + next if( $path =~ m!.*${DS}TARGET$! ); + + my $file = $path; + + $file =~ s!.*$DS!!; + $file = catfile($targetdir, $file); + + next if($opt_n); + + open IN, "<", $path or die "Can't open $path: $!"; + open OUT, ">", "$file" or die "Can't open $file: $!"; + while(){ + print OUT; } + close OUT; + close IN; - for my $path ( glob("$srcdir$DS*") ){ - - next if( $path =~ m!.*${DS}TARGET$! ); - - my $file = $path; - - $file =~ s!.*$DS!!; - - $file = catfile($targetdir, $file); - - next if($opt_n); - - open IN, "<", $path or die "Can't open $path: $!"; - open OUT, ">", "$file" or die "Can't open $file: $!"; - while(){ - print OUT; - } - close OUT; - close IN; - - if(! -x $file){ - chmod 0755 ,$file; - } + if(! -x $file){ + chmod 0755 ,$file; } + } } +sub do_file_nhgitset { + my $infile = "DEVEL/nhgitset.pl"; + $infile = "$DEVhooksdir/../nhgitset.pl" unless(-f $infile); + my $outfile = ".git/hooks/gitsetdocs"; + open IN, "<", $infile or die "Can't open $infile:$!"; + open OUT, ">", $outfile or die "Can't open $outfile:$!"; + my $started; + print IN "die \"DO NOT RUN THIS FILE\n\""; + while(){ + m/^__END__/ && do {$started =1; next}; + print OUT if($started); + } + close OUT; + close IN; +} + +#(can we change the .gitattributes syntax to include a comment character?) +#maybe [comment] attr.c:parse_attr_line +#grr - looks like # is the comment character __END__ -(can we change the .gitattributes syntax to include a comment character?) -maybe [comment] attr.c:parse_attr_line -grr - looks like # is the comment character - - - =head1 NAME nhgitset.pl - Setup program for NetHack git repositories @@ -302,17 +383,36 @@ nhgitset.pl - Setup program for NetHack git repositories =head1 DESCRIPTION -nhgitset.pl installs NetHack-specific setup after a C (or after -changes to the desired configuration, which are installed by re-running -nhgitset.pl). +nhgitset.pl installs NetHack-specific setup after a C or after +changes to the setup, which are installed by re-running nhgitset.pl. If +an upgrade is needed, you will be informed during a C or similar +operation. The following options are available: -B<-f> Force. Do not use this unless the program requests it. +=over -B<-n> Make no changes. +=item B<-f> -B<-v> Verbose output. +Force. Do not use this unless the program requests it or the hooks are broken. + +=back + +=over + +=item B<-n> + +Dry-run - make no changes. + +=back + +=over + +=item B<-v> + +Verbose output. + +=back =head1 CONFIG @@ -329,8 +429,35 @@ nethack.gitadddir nethack.setupversion + The version for nhgitset.pl and friends; has no relationship + to NetHack version numbers. + nethack.substprefix + The prefix this repo uses for variable substitution. + +nethack.projectname + + The name of the game being built - see C. + +nethack.is-sourcerepo + + Does this repo contain NetHack source code? (1 = yes, 0 = no) + +nethack.setuppath + + Path to (and including) the DEVEL directory used including the + copy of nhgitset.pl used to set up this repo. + +nethack.aliashelp.* + + The last element of the variable is the name used with C + and the value is the name of a file to display with C. + +nethack.aliasdesc.* + + The last element of the variable is the name used with C + and the value is short help displayed with C. =head1 EXIT STATUS @@ -339,3 +466,5 @@ nethack.substprefix 1 Fail. 2 Intervention required. + +=for nhgitset nhgitset NetHack git helper installer diff --git a/Files b/Files index 16bcaecf4..e4f4e096b 100644 --- a/Files +++ b/Files @@ -12,8 +12,8 @@ Porting README azure-pipelines.yml DEVEL: (files for people developing changes to NetHack) -Developer.txt code_features.txt code_style.txt git_recipes.txt -gitinfo.pl nhgitset.pl +Developer.txt VERSION code_features.txt code_style.txt +git_recipes.txt gitinfo.pl nhgitset.pl DEVEL/DOTGIT: (file for people developing changes to NetHack) @@ -23,11 +23,11 @@ DEVEL/hooksdir: (files for people developing changes to NetHack) NHadd NHgithook.pm NHsubst NHtext TARGET applypatch-msg -commit-msg nhsub post-applypatch -post-checkout post-commit post-merge -post-rewrite pre-applypatch pre-auto-gc -pre-commit pre-push pre-rebase -prepare-commit-msg +commit-msg nhhelp nhsub +post-applypatch post-checkout post-commit +post-merge post-rewrite pre-applypatch +pre-auto-gc pre-commit pre-push +pre-rebase prepare-commit-msg dat: (files for all versions) @@ -47,20 +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 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 -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 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) @@ -93,13 +94,14 @@ 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 obj.h -objclass.h objects.h optlist.h patchlevel.h pcconf.h -permonst.h prop.h quest.h rect.h region.h -rm.h seffects.h selvar.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 winami.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 @@ -291,26 +293,27 @@ windsound.c src: (files for all versions) allmain.c alloc.c apply.c artifact.c attrib.c ball.c -bones.c botl.c calendar.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 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 +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 submodules: (files in top directory) @@ -428,14 +431,14 @@ vmsunix.c sys/windows: (files for Windows 7/8.x/10/11 version) -Install.windows Makefile.mingw32 Makefile.mingw32.depend -Makefile.nmake build-msys2.txt build-nmake.txt -build-vs.txt console.rc consoletty.c -fetch.cmd guitty.c nethack.def -nethackrc.template nhico.uu nhsetup.bat -porthelp sysconf.template win10.c -win10.h win32api.h windmain.c -windsys.c winos.h +GNUmakefile GNUmakefile.depend Install.windows +Makefile.nmake build-msys2.txt build-nmake.txt +build-vs.txt console.rc consoletty.c +fetch.cmd fetch.sh guitty.c +nethack.def nethackrc.template nhico.uu +nhsetup.bat porthelp sysconf.template +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) @@ -447,7 +450,7 @@ config.props console.props cpp.hint default.props default_dll.props default_lib.props dirs.props dll.props -files.props +files.props sfctool.sln sys/windows/vs/FetchPrereq: (files for Visual Studio 2019 or 2022 Community Edition builds) @@ -528,10 +531,18 @@ sys/windows/vs/dlb: (files for Visual Studio 2019 or 2022 Community Edition builds) afterdlb.proj dlb.vcxproj +sys/windows/vs/fetchctags: +(files for Visual Studio 2019 or 2022 Community Edition builds) +fetchctags.nmake fetchctags.vcxproj + sys/windows/vs/hacklib: (file for Visual Studio 2019 or 2022 Community Edition builds) hacklib.vcxproj +sys/windows/vs/lualib: +(file for Visual Studio 2019 or 2022 Community Edition builds) +lualib.vcxproj + sys/windows/vs/makedefs: (files for Visual Studio 2019 or 2022 Community Edition builds) aftermakedefs.proj makedefs.vcxproj @@ -544,6 +555,14 @@ sys/windows/vs/recover: (files for Visual Studio 2019 or 2022 Community Edition builds) afterrecover.proj recover.vcxproj +sys/windows/vs/sfctool: +(file for Visual Studio 2019 or 2022 Community Edition builds) +sfctool.vcxproj + +sys/windows/vs/sftags: +(files for Visual Studio 2019 or 2022 Community Edition builds) +aftersftags.proj sftags.vcxproj + sys/windows/vs/tile2bmp: (files for Visual Studio 2019 or 2022 Community Edition builds) aftertile2bmp.proj tile2bmp.vcxproj @@ -562,12 +581,13 @@ afteruudecode.proj uudecode.vcxproj test: (files for testing) -test_cnf.lua test_des.lua test_lev.lua test_obj.lua test_sel.lua -test_shk.lua test_src.lua testmove.lua testwish.lua +README.md test_cnf.lua test_des.lua test_lev.lua test_obj.lua +test_sel.lua test_shk.lua test_src.lua testmove.lua testwish.lua util: (files for all versions) dlb_main.c makedefs.c mdgrep.h mdgrep.pl panic.c recover.c +sfctool.c sfexpasc.c sftags.c stripbs.c win/Qt: (files for the Qt 4 or 5 widget library - X11, Windows, Mac OS X) @@ -664,7 +684,7 @@ pet_mark.xbm rip.xpm x11tiles src: (files for win32 that are moved into src at compile time) -Makefile Makefile.mingw32 +GNUmakefile Makefile (files generated by 'moc' for Qt interface at compile time) qt_kde0.moc qt_main.moc qt_map.moc qt_menu.moc qt_msg.moc diff --git a/README b/README index c3d975981..9a4923d03 100644 --- a/README +++ b/README @@ -57,10 +57,11 @@ considered spoilers: level once that square's location becomes known (found or magic mapped); goes away once sanctum temple is found (entered or high altar mapped) - - savefile: add support to deconstruct internal data structures down into - their individual fields and save those fields instead of the entire - struct - - savefile: use little-endian format for fields where that makes a difference + - savefile: add support for a tool to deconstruct data structures, + that get stored in a savefile, down into their individual fields and + save those individual fields instead of the entire struct. The + intention is to provide a way to export and transport savefiles + between platforms, architectures and data models. - - - - - - - - - - - diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2a8a4ee87..9da64ea50 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,27 +1,27 @@ strategy: matrix: - linux_latest_gcc9_minimal: + linux_latest_gcc14_minimal: imageName: 'ubuntu-latest' - toolchainName: gcc9 + toolchainName: gcc14 buildTargetName: minimal - linux_jammy_clang_all: - imageName: 'ubuntu-22.04' + linux_noble_clang_all: + imageName: 'ubuntu-24.04' toolchainName: clang buildTargetName: all linux_jammy_gcc9_all: imageName: 'ubuntu-22.04' toolchainName: gcc9 buildTargetName: all - linux_jammy_gcc12_all: - imageName: 'ubuntu-22.04' - toolchainName: gcc12 + linux_noble_gcc13_all: + imageName: 'ubuntu-24.04' + toolchainName: gcc13 buildTargetName: all macOS_latest_clang_all: imageName: 'macOS-latest' toolchainName: clang buildTargetName: all - macOS_Ventura_clang13_all: - imageName: 'macOS-13' + macOS_Sequoia_clang15_all: + imageName: 'macOS-15' toolchainName: clang buildTargetName: all windows-visualstudio: @@ -29,15 +29,15 @@ strategy: toolchainName: vs buildTargetName: all windows-mingw: - imageName: 'windows-2019' + imageName: 'windows-2025' toolchainName: mingw buildTargetName: all - linux_focal_cross_msdos: - imageName: 'ubuntu-20.04' + linux_noble_cross_msdos: + imageName: 'ubuntu-24.04' toolchainName: cross buildTargetName: msdos - linux_jammy_docs: - imageName: 'ubuntu-22.04' + linux_noble_docs: + imageName: 'ubuntu-24.04' toolchainName: docs buildTargetName: all continueOnError: true @@ -60,30 +60,20 @@ variables: steps: - bash: | - if [ "$(toolchain)" == "gcc7" ] - then - echo "##vso[task.setvariable variable=CC]gcc-7" - echo "##vso[task.setvariable variable=CXX]g++-7" - fi if [ "$(toolchain)" == "gcc9" ] then echo "##vso[task.setvariable variable=CC]gcc-9" echo "##vso[task.setvariable variable=CXX]g++-9" fi - if [ "$(toolchain)" == "gcc10" ] + if [ "$(toolchain)" == "gcc13" ] then - echo "##vso[task.setvariable variable=CC]gcc-10" - echo "##vso[task.setvariable variable=CXX]g++-10" + echo "##vso[task.setvariable variable=CC]gcc-13" + echo "##vso[task.setvariable variable=CXX]g++-13" fi - if [ "$(toolchain)" == "gcc11" ] + if [ "$(toolchain)" == "gcc14" ] then - echo "##vso[task.setvariable variable=CC]gcc-11" - echo "##vso[task.setvariable variable=CXX]g++-11" - fi - if [ "$(toolchain)" == "gcc12" ] - then - echo "##vso[task.setvariable variable=CC]gcc-12" - echo "##vso[task.setvariable variable=CXX]g++-12" + echo "##vso[task.setvariable variable=CC]gcc-14" + echo "##vso[task.setvariable variable=CXX]g++-14" fi if [ "$(toolchain)" == "clang" ] then @@ -92,8 +82,8 @@ steps: fi if [ "$(toolchain)" == "cross" ] then - echo "##vso[task.setvariable variable=CC]gcc-9" - echo "##vso[task.setvariable variable=CXX]g++-9" + echo "##vso[task.setvariable variable=CC]gcc-13" + echo "##vso[task.setvariable variable=CXX]g++-13" fi displayName: 'Setting variables' @@ -124,7 +114,7 @@ steps: condition: eq( variables.toolchain, 'vs' ) displayName: 'Copying store key' -- task: MSBuild@1 +- task: VSBuild@1 inputs: solution: $(Agent.BuildDirectory)/$(netHackPath)/sys/windows/vs/NetHack.sln platform: Win32 @@ -142,13 +132,15 @@ steps: export LUA_VERSION=5.4.6 # # 64-bit - #export CURLSRC=https://github.com/brechtsanders/winlibs_mingw/releases/download/11.2.0-9.0.0-ucrt-r5/winlibs-x86_64-posix-seh-gcc-11.2.0-mingw-w64ucrt-9.0.0-r5.zip + #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 + #export CURLSRC=https://github.com/brechtsanders/winlibs_mingw/releases/download/15.1.0posix-12.0.0-ucrt-r1/winlibs-x86_64-posix-seh-gcc-15.1.0-mingw-w64ucrt-12.0.0-r1.zip #export CURLDST=mingw-x64.zip #export MINGWBIN=mingw64 #export MSYSTEM=MINGW64 # # 32-bit - export CURLSRC=https://github.com/brechtsanders/winlibs_mingw/releases/download/11.2.0-9.0.0-ucrt-r5/winlibs-i686-posix-dwarf-gcc-11.2.0-mingw-w64ucrt-9.0.0-r5.zip + #export CURLSRC=https://github.com/brechtsanders/winlibs_mingw/releases/download/14.2.0posix-19.1.1-12.0.0-ucrt-r2/winlibs-i686-posix-dwarf-gcc-14.2.0-llvm-19.1.1-mingw-w64ucrt-12.0.0-r2.zip + export CURLSRC=https://github.com/brechtsanders/winlibs_mingw/releases/download/15.1.0posix-12.0.0-ucrt-r1/winlibs-i686-posix-dwarf-gcc-15.1.0-mingw-w64ucrt-12.0.0-r1.zip export CURLDST=mingw-x86.zip export MINGWBIN=mingw32 export MSYSTEM=MINGW32 @@ -164,10 +156,10 @@ steps: export cd ../src pwd - cp ../sys/windows/Makefile.mingw32* . - mingw32-make -f Makefile.mingw32 CI_COMPILER=1 GIT=1 MSYSTEM=$MSYSTEM LUA_VERSION=$LUA_VERSION clean - mingw32-make -f Makefile.mingw32 CI_COMPILER=1 GIT=1 MSYSTEM=$MSYSTEM LUA_VERSION=$LUA_VERSION depend - mingw32-make -f Makefile.mingw32 CI_COMPILER=1 GIT=1 MSYSTEM=$MSYSTEM LUA_VERSION=$LUA_VERSION + cp ../sys/windows/GNUmakefile* . + mingw32-make -f GNUmakefile CI_COMPILER=1 GIT=1 MSYSTEM=$MSYSTEM LUA_VERSION=$LUA_VERSION clean + mingw32-make -f GNUmakefile CI_COMPILER=1 GIT=1 MSYSTEM=$MSYSTEM LUA_VERSION=$LUA_VERSION depend + mingw32-make -f GNUmakefile CI_COMPILER=1 GIT=1 MSYSTEM=$MSYSTEM LUA_VERSION=$LUA_VERSION condition: eq( variables.toolchain, 'mingw' ) workingDirectory: $(Agent.BuildDirectory)/$(netHackPath)/src displayName: 'MinGW Build' @@ -222,6 +214,7 @@ steps: workingDirectory: $(Agent.BuildDirectory)/$(netHackPath) - bash: | + brew install groff cd sys/unix sh setup.sh hints/macos.370 cd ../.. @@ -232,12 +225,13 @@ steps: displayName: 'Building mac full build' - bash: | + sudo apt -qq -y install libfl2 export GCCVER=gcc1220 cd sys/unix sh setup.sh hints/linux.370 cd ../.. make fetch-lua - sh sys/msdos/fetch-cross-compiler.sh + 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 diff --git a/dat/bigrm-12.lua b/dat/bigrm-12.lua new file mode 100644 index 000000000..0e41bd9e4 --- /dev/null +++ b/dat/bigrm-12.lua @@ -0,0 +1,85 @@ +-- NetHack bigroom bigrm-12.lua $NHDT-Date: $ $NHDT-Branch: NetHack-3.7 $ +-- Copyright (c) 2024 by Pasi Kallinen +-- NetHack may be freely redistributed. See license for details. +-- +-- Two hexagons + +des.level_flags("mazelevel", "noflipy"); +des.level_init({ style = "solidfill", fg = " " }); + +des.map([[ + + ....................... ....................... + ......................... ......................... + ........................... ........................... + ............................. ............................. + ........PPPPPPPPPPPPPPP........ ........LLLLLLLLLLLLLLL........ + ........PPPPPPPPPPPPPPPPP........ ........LLLLLLLLLLLLLLLLL........ + ........PPPWWWWWWWWWWWWWPPP...............LLLZZZZZZZZZZZZZLLL........ + ........PPPWWWWWWWWWWWWWWWPPP.............LLLZZZZZZZZZZZZZZZLLL........ + ........PPPWWWWWWWWWWWWWWWWWPPP...........LLLZZZZZZZZZZZZZZZZZLLL........ + ........PPPWWWWWWWWWWWWWWWPPP.............LLLZZZZZZZZZZZZZZZLLL........ + ........PPPWWWWWWWWWWWWWPPP...............LLLZZZZZZZZZZZZZLLL........ + ........PPPPPPPPPPPPPPPPP........ ........LLLLLLLLLLLLLLLLL........ + ........PPPPPPPPPPPPPPP........ ........LLLLLLLLLLLLLLL........ + ............................. ............................. + ........................... ........................... + ......................... ......................... + ....................... ....................... + +]]); + +-- maybe replace lavawalls/waterwalls with stone walls +if percent(20) then + if percent(50) then + des.replace_terrain({ fromterrain = "W", toterrain = "-" }); + end + if percent(50) then + des.replace_terrain({ fromterrain = "Z", toterrain = "-" }); + end +end + +-- maybe replace pools with floor and then possibly walls with pools +if percent(25) then + des.replace_terrain({ fromterrain = "P", toterrain = "." }); + if percent(75) then + des.replace_terrain({ fromterrain = "W", toterrain = "P" }); + end +end +if percent(25) then + des.replace_terrain({ fromterrain = "L", toterrain = "." }); + if percent(75) then + des.replace_terrain({ fromterrain = "Z", toterrain = "L" }); + end +end + +-- maybe make both sides have the same terrain +if percent(20) then + if percent(50) then + -- both are lava + des.replace_terrain({ fromterrain = "P", toterrain = "L" }); + des.replace_terrain({ fromterrain = "W", toterrain = "Z" }); + else + -- both are water + des.replace_terrain({ fromterrain = "L", toterrain = "P" }); + des.replace_terrain({ fromterrain = "Z", toterrain = "W" }); + end +end + +des.region(selection.area(00,00,75,19), "lit") +des.non_diggable(); + +des.wallify(); + +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/bigrm-4.lua b/dat/bigrm-4.lua index 777eeb30f..a6b9340d1 100644 --- a/dat/bigrm-4.lua +++ b/dat/bigrm-4.lua @@ -9,20 +9,20 @@ des.level_flags("mazelevel", "noflip"); des.map([[ ----------- ----------- |.........| |.........| -|.........|-----------| |-----------|.........| -|-|...................|----------| |----------|...................|-| - -|.............................|-------|.............................|- - -|.................................................................|- - -|...............................................................|- - -|......LLLLL.......................................LLLLL......|- - -|.....LLLLL.......................................LLLLL.....|- - -|.....LLLLL.......................................LLLLL.....|- - -|......LLLLL.......................................LLLLL......|- - -|...............................................................|- - -|.................................................................|- - -|.............................|-------|.............................|- -|-|...................|----------| |----------|...................|-| -|.........|-----------| |-----------|.........| +|.........------------- -------------.........| +---...................------------ ------------...................--- + --.............................---------.............................-- + --.................................................................-- + --...............................................................-- + --......LLLLL.......................................LLLLL......-- + --.....LLLLL.......................................LLLLL.....-- + --.....LLLLL.......................................LLLLL.....-- + --......LLLLL.......................................LLLLL......-- + --...............................................................-- + --.................................................................-- + --.............................---------.............................-- +---...................------------ ------------...................--- +|.........------------- -------------.........| |.........| |.........| ----------- ----------- ]]); diff --git a/dat/bigrm-6.lua b/dat/bigrm-6.lua index f67cc1638..c6063a818 100644 --- a/dat/bigrm-6.lua +++ b/dat/bigrm-6.lua @@ -12,7 +12,7 @@ des.map([[ --...........-- --...........-- --...........-- --...........-- --.............-- --.............-- --.............-- --.............-- -...............- -...............- -...............- -...............- -|-...............---...............---...............---...............-- +--...............---...............---...............---...............-- |.................-.................-.................-.................| |........T.................T.................T.................T........| |.......................................................................| diff --git a/dat/bigrm-7.lua b/dat/bigrm-7.lua index 6dc828c83..07038d8d6 100644 --- a/dat/bigrm-7.lua +++ b/dat/bigrm-7.lua @@ -14,11 +14,11 @@ des.map([[ ---------.................................--- ---------...........................................--- ---------.....................................................--- -|--------...............................................................--| +---------...............................................................--- |.........................................................................| |.L.....................................................................L.| |.........................................................................| -|--...............................................................--------| +---...............................................................--------- ---.....................................................--------- ---...........................................--------- ---.................................--------- diff --git a/dat/castle.lua b/dat/castle.lua index 83eaecae6..0a2b7f243 100644 --- a/dat/castle.lua +++ b/dat/castle.lua @@ -144,6 +144,7 @@ local loc = place:rndcoord(1); des.object({ id = "chest", trapped = 0, locked = 1, coord = loc , contents = function() des.object("wishing"); + des.object("potion of gain level"); end }); -- Prevent monsters from eating it. (@'s never eat objects) diff --git a/dat/data.base b/dat/data.base index 505c1b17d..ed42fca25 100644 --- a/dat/data.base +++ b/dat/data.base @@ -1,5 +1,5 @@ # NetHack 3.7 data.base -# $NHDT-Date: 1684001509 2023/05/13 18:11:49 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.106 $ +# $NHDT-Date: 1737609388 2025/01/22 21:16:28 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.141 $ # Copyright (c) 1994, 1995, 1996 by the NetHack Development Team # Copyright (c) 1994 by Boudewijn Wayers # NetHack may be freely redistributed. See license for details. @@ -2056,6 +2056,16 @@ genetic engineer immediate mutations in the subject. Far from needing an elaborate laboratory to accomplish such abominations, a mere touch seems to be all that is necessary. + [] + + "I thought the whole point of genetic engineering was to avoid + the random waste of natural evolution, and replace it with + the efficiency of reason," Miles piped up. All three haut- + women turned to stare at him in astonishment, as if a potted + plant had suddenly offered a critique of its fertilization + routine. "Or ... so it seems to me," Miles trailed off in + a much smaller voice. + [ Cetaganda, Lois McMaster Bujold ] geryon Forthwith that image vile of fraud appear'd, His head and upper part expos'd on land, @@ -2395,7 +2405,6 @@ hell hound* ~helm of * ~crystal helmet helm* -dented pot * helmet A piece of armor designed to protect the head. (What were you expecting?) @@ -5230,6 +5239,7 @@ squeaky board additionally gives the wielder the power of regeneration. When invoked it performs healing magic. stair* +branch stair* Up he went -- very quickly at first -- then more slowly -- then in a little while even more slowly than that -- and finally, after many minutes of climbing up the endless stairway, one diff --git a/dat/dungeon.lua b/dat/dungeon.lua index ff9e9130a..eb6223d66 100644 --- a/dat/dungeon.lua +++ b/dat/dungeon.lua @@ -70,7 +70,7 @@ dungeon = { base = 10, range = 3, chance = 40, - nlevels = 11 + nlevels = 12 }, { name = "medusa", diff --git a/dat/epitaph.txt b/dat/epitaph.txt index a8fbb3266..d559d1fb3 100644 --- a/dat/epitaph.txt +++ b/dat/epitaph.txt @@ -279,6 +279,8 @@ I died laughing I disbelieved in reincarnation in my last life, too. I hacked myself to death I have all the time in the world +#"reached Mine's End": not necessarily true; sounds like "reached my end" +I have reached Mine's End I knew I'd find a use for this gravestone! I know my mind. And it's around here someplace. I lied! I'll never be alright! diff --git a/dat/hellfill.lua b/dat/hellfill.lua index d7675e19b..00175ffd3 100644 --- a/dat/hellfill.lua +++ b/dat/hellfill.lua @@ -410,6 +410,20 @@ hells = { end des.terrain(outside_walls, " "); -- return the outside back to solid wall end, + + -- 7: open cavern, "mines" with more space + function () + 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 }); + local sel = selection.match("."):grow(); + des.terrain({ selection = sel, typ = "." }); + + local border = selection.rect(0,0, 78, 20); + des.terrain({ selection = border, typ = " " }); + des.wallify(); + end, + }; local hellno = math.random(1, #hells); diff --git a/dat/luahelper b/dat/luahelper new file mode 100644 index 000000000..7f8a653fa --- /dev/null +++ b/dat/luahelper @@ -0,0 +1,102 @@ +# NetHack 3.7 luahelper $NHDT-Date: $ $NHDT-Branch: NetHack-3.7 $ +# +# Makefile to assist developers in adding lines to various build +# Makefiles, or project files, as lua files are added or removed. +# NetHack may be freely redistributed. See license for details. +# +# Must be run while your current directory is the top of the +# NetHack source tree. +# +# make -f dat/luahelper [target] +# +# Target examples: +# +# Visual Studio nmake : make -f dat/luahelper devhelp-nmake >file.txt +# +# Visual Studio project: make -f dat/luahelper devhelp-vstudio >file.txt +# +# MSYS2 GNUmakefile : make -f dat/luahelper devhelp-msys2 >file.txt +# +# Xcode project.pbxproj: make -f dat/luahelper devhelp-xcode >file.txt +# +# + +MAKEFLAGS += --no-print-directory +AWK=awk + + +luanames = asmodeus baalz bigrm-* castle fakewiz? juiblex knox medusa-? \ + minend-? minefill minetn-? oracle orcus sanctum soko?-? tower? \ + valley wizard? nhcore nhlib themerms astral air earth fire water \ + hellfill tut-? ???-goal ???-fil? ???-loca ???-strt \ + dungeon quest + +alllua = $(wildcard $(addsuffix .lua,$(addprefix dat/, $(luanames)))) + +# +# generates lines to insert into +# sys/windows/Makefile.nmake +# +devhelp-nmake: + @echo "$(notdir $(alllua))" | \ + $(AWK) '{for (i=1; i<=NF; ++i)printf "%s%s%s%s", \ + i == 1? "LUA_FILES = " : "", \ + "$$(DAT)", \ + $$i, \ + i % 3? " ": ((i == NF)? "\n" : " \\\n\t")} \ + i % 3{print ""}' + @echo "" + +# +# generates lines to insert into +# sys/windows/GNUmakefile +# +devhelp-msys2: + @echo "$(basename $(notdir $(alllua)))" | \ + $(AWK) '{for (i=1; i<=NF; ++i)printf "%s%s%s", \ + i == 1? "LUALIST = " : "", \ + $$i, \ + i % 5? " ": ((i == NF)? "\n" : " \\\n\t")} \ + i % 5{print ""}' + @echo "" + +xcodeextra = bogusmon cmdhelp data engrave epitaph help hh \ + history keyhelp opthelp optmenu options oracles \ + rumors tribute wizhelp +# +# generates lines to insert into +# sys/unix/NetHack.xcodeproj/project.pbxproj +# +devhelp-xcode: + @echo "\t\t\tinputPaths = (" + @echo "$(xcodeextra) $(notdir $(alllua))" | \ + $(AWK) '{for (i=1; i<=NF; ++i)printf "%s%s%s%s%s", \ + i == 1? "\t" : "", \ + "\t\t\t\042$$(NH_DAT_DIR)/", \ + $$i, \ + "\042", \ + i % 1? " ": ((i == NF)? "\n" : ",\n\t")} \ + i % 1{print ""}' + @echo "\t\t\t);" +# +# generates lines to insert into +# sys/windows/vs/files.props +# +devhelp-vstudio: + @echo " " + @echo "$(notdir $(alllua))" | \ + $(AWK) '{for (i=1; i<=NF; ++i)printf "%s%s%s%s%s", \ + i == 1? " " : "", \ + "", \ + i % 1? " ": ((i == NF)? "\n" : "\n ")} \ + i % 1{print ""}' + @echo " " + +all: + make -f dat/luahelper devhelp-nmake >./luahelp-nmake.txt + make -f dat/luahelper devhelp-msys2 >./luahelp-msys2.txt + make -f dat/luahelper devhelp-xcode >./luahelp-xcode.txt + make -f dat/luahelper devhelp-vstudio >./luahelp-vstudio.txt +# end diff --git a/dat/medusa-3.lua b/dat/medusa-3.lua index 4419117f7..282d3668b 100644 --- a/dat/medusa-3.lua +++ b/dat/medusa-3.lua @@ -132,7 +132,7 @@ des.monster("water nymph") des.monster("water nymph") for i=1,30 do - des.monster({ id = "raven", hostile = 1 }) + des.monster({ id = "raven", peaceful = 0 }) end --#medusa-3.lua diff --git a/dat/opthelp b/dat/opthelp index 55c9b0028..d36e5228b 100644 --- a/dat/opthelp +++ b/dat/opthelp @@ -40,7 +40,6 @@ mail enable the mail daemon [True] mention_decor give feedback when walking across stairs, altars, [False] fountains, and such even when not obscured by objects mention_walls give feedback when walking against a wall [False] -menu_objsyms show object symbols in menus if it is selectable [False] menu_overlay overlay menus on the screen and align to right [True] menucolors enable MENUCOLOR pattern matching to highlight [False] lines in object menus like inventory; might highlight with @@ -190,6 +189,16 @@ menustyle user interface for selection of multiple objects: [Full] only the first letter ('T','C','F','P') matters (With Traditional, many actions allow pseudo-class 'm' to request a menu for choosing items: one-shot Combination.) +menu_objsyms whether to include object class symbols in menus: [4] + 0 - none -- don't add object symbols to menus; + 1 - headers -- append object class symbol to menu header lines; + 2 - entries -- show object glyphs (same as class symbol + for ASCII interfaces) on each menu entry line; + 3 - both -- 1 and 2 combined; + 4 - conditional -- as 2 but only if no headers are present; + 5 - one-or-other -- 1 and 4 combined; + choices 0 and 1 should work with any interface; 2 through 5 + are supported by tty and curses msg_window behavior of ^P message recall for tty interface: [s] single -- one message at a time full -- full window with all saved top line messages diff --git a/dat/orcus.lua b/dat/orcus.lua index d00aac53c..67406d8e2 100644 --- a/dat/orcus.lua +++ b/dat/orcus.lua @@ -102,6 +102,13 @@ local orcus1 = des.map({ halign = "right", valign = "center", map = [[ des.object() des.object() des.object() + -- An object that's worth most of a wish + -- (this is part of the compensation for the reduced wishes at the Castle) + if math.random(0, 1) == 1 then + des.object("magic marker") + else + des.object("magic lamp") + end -- The resident nasty des.monster("Orcus",33,15) -- And its preferred companions diff --git a/dat/rumors.fal b/dat/rumors.fal index 721e13d8c..43895f568 100644 --- a/dat/rumors.fal +++ b/dat/rumors.fal @@ -28,6 +28,7 @@ All monsters are created evil, but some are more evil than others. Always attack a floating eye from behind! An elven cloak is always the height of fashion. Any small object that is accidentally dropped will hide under a larger object. +Archeologists are very squishy, often found under boulders. Archeologists find more bones piles. Austin Powers says: My Mojo is back! Yeah, baby! Balrogs do not appear above level 20. diff --git a/dat/soko1-1.lua b/dat/soko1-1.lua index bd90ecdc4..ba5a9dfd7 100644 --- a/dat/soko1-1.lua +++ b/dat/soko1-1.lua @@ -59,6 +59,8 @@ des.object("boulder", 09, 12); -- des.object("boulder", 03, 14); +-- prevent monster generation over the (filled) holes +des.exclusion({ type = "monster-generation", region = { 08,01, 23,01 } }); -- Traps des.trap("hole", 08, 01); des.trap("hole", 09, 01); diff --git a/dat/soko1-2.lua b/dat/soko1-2.lua index aff446df5..2ff3a9feb 100644 --- a/dat/soko1-2.lua +++ b/dat/soko1-2.lua @@ -60,6 +60,8 @@ des.object("boulder",10,08); des.object("boulder",12,09); des.object("boulder",11,10); +-- prevent monster generation over the (filled) holes +des.exclusion({ type = "monster-generation", region = { 05,01, 22,01 } }); -- Traps des.trap("hole",05,01) des.trap("hole",06,01) diff --git a/dat/soko2-1.lua b/dat/soko2-1.lua index 42cdcc5dd..eb66e0fbf 100644 --- a/dat/soko2-1.lua +++ b/dat/soko2-1.lua @@ -46,6 +46,8 @@ des.object("boulder",03,09) des.object("boulder",05,07) des.object("boulder",06,06) +-- prevent monster generation over the (filled) holes +des.exclusion({ type = "monster-generation", region = { 07,09, 18,09 } }); -- Traps des.trap("hole",08,09) des.trap("hole",09,09) diff --git a/dat/soko2-2.lua b/dat/soko2-2.lua index 27af1df10..1302a1b5e 100644 --- a/dat/soko2-2.lua +++ b/dat/soko2-2.lua @@ -46,6 +46,8 @@ des.object("boulder",06,09) des.object("boulder",05,10) des.object("boulder",05,11) +-- prevent monster generation over the (filled) holes +des.exclusion({ type = "monster-generation", region = { 06,11, 18,11 } }); -- Traps des.trap("hole",07,11) des.trap("hole",08,11) diff --git a/dat/soko3-1.lua b/dat/soko3-1.lua index bf14fbfad..2515ffca1 100644 --- a/dat/soko3-1.lua +++ b/dat/soko3-1.lua @@ -52,6 +52,8 @@ des.object("boulder",09,09) des.object("boulder",10,07) des.object("boulder",10,10) +-- prevent monster generation over the (filled) holes +des.exclusion({ type = "monster-generation", region = { 11,10, 27,10 } }); -- Traps des.trap("hole",12,10) des.trap("hole",13,10) diff --git a/dat/soko3-2.lua b/dat/soko3-2.lua index 855575552..1015125d6 100644 --- a/dat/soko3-2.lua +++ b/dat/soko3-2.lua @@ -47,6 +47,8 @@ des.object("boulder",07,10) des.object("boulder",10,10) des.object("boulder",03,11) +-- prevent monster generation over the (filled) holes +des.exclusion({ type = "monster-generation", region = { 12,10, 24,10 } }); -- Traps des.trap("hole",12,10) des.trap("hole",13,10) diff --git a/dat/soko4-1.lua b/dat/soko4-1.lua index 0998220f5..4c8f6d162 100644 --- a/dat/soko4-1.lua +++ b/dat/soko4-1.lua @@ -72,6 +72,8 @@ des.object("boulder",09,09) des.object("boulder",08,10) des.object("boulder",10,10) +-- prevent monster generation over the (filled) pits +des.exclusion({ type = "monster-generation", region = { 01,06, 07,11 } }); -- Traps des.trap("pit",03,06) des.trap("pit",04,06) diff --git a/dat/soko4-2.lua b/dat/soko4-2.lua index 6b994a95a..de749ad37 100644 --- a/dat/soko4-2.lua +++ b/dat/soko4-2.lua @@ -41,6 +41,9 @@ des.object("boulder",08,08) des.object("boulder",09,08) des.object("boulder",10,08) +-- prevent monster generation over the (filled) pits +des.exclusion({ type = "monster-generation", region = { 01,01, 01,09 } }); +des.exclusion({ type = "monster-generation", region = { 01,08, 07,09 } }); -- Traps des.trap("pit",01,02) des.trap("pit",01,03) diff --git a/dat/themerms.lua b/dat/themerms.lua index 75145eefb..6aff95286 100644 --- a/dat/themerms.lua +++ b/dat/themerms.lua @@ -1,13 +1,28 @@ --- NetHack themerms.lua $NHDT-Date: 1652196294 2022/05/10 15:24:54 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.16 $ +-- NetHack themerms.lua $NHDT-Date: 1744445274 2025/04/12 00:07:54 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.40 $ -- Copyright (c) 2020 by Pasi Kallinen -- NetHack may be freely redistributed. See license for details. -- --- themerooms is an array of tables and/or functions. --- the tables define "frequency", "contents", "mindiff" and "maxdiff". --- frequency is optional; if omitted, 1 is assumed. --- mindiff and maxdiff are optional and independent; if omitted, the room is --- not constrained by level difficulty. --- a plain function has frequency of 1, and no difficulty constraints. +-- themerooms is an array of tables. +-- the tables define "name", "frequency", "contents", "mindiff" and "maxdiff". +-- * "name" is not shown in-game; it is so that developers can specify a +-- certain room to generate by using the THEMERM or THEMERMFILL environment +-- variable. While technically optional, it should be provided on all the +-- rooms; if it isn't, the room can't be directly specified. +-- * "frequency" is optional; if omitted, 1 is assumed. +-- * "contents" is a function describing what gets put into the room. +-- * "mindiff" and "maxdiff" are optional and independent; if omitted, the +-- room is not constrained by level difficulty. +-- * "eligible" is optional; if omitted, True is assumed. +-- +-- themeroom_fills is an array of tables with the exact same structure as +-- themerooms. It is used for contents of a room that are independent of its +-- shape, so that interestingly-shaped themerooms can be filled with a variety +-- of contents. +-- * The "contents" functions in themeroom_fills take the room they are +-- filling as an argument. +-- * Frequency of themeroom_fills is a separate pool from themerooms, and has +-- no effect on how likely that any given room will receive a themeroom_fill. +-- -- des.room({ type = "ordinary", filled = 1 }) -- - ordinary rooms can be converted to shops or any other special rooms. -- - filled = 1 means the room gets random room contents, even if it @@ -24,35 +39,38 @@ -- The lua state is persistent through the gameplay, but not across saves, -- so remember to reset any variables. - local postprocess = { }; themeroom_fills = { - -- Ice room - function(rm) - local ice = selection.room(); - des.terrain(ice, "I"); - if (percent(25)) then - local mintime = 1000 - (nh.level_difficulty() * 100); - local ice_melter = function(x,y) - nh.start_timer_at(x,y, "melt-ice", mintime + nh.rn2(1000)); - end; - ice:iterate(ice_melter); - end - end, - - -- Cloud room - function(rm) - local fog = selection.room(); - for i = 1, (fog:numpoints() / 4) do - des.monster({ id = "fog cloud", asleep = true }); - end - des.gas_cloud({ selection = fog }); - end, - - -- Boulder room { + name = "Ice room", + contents = function(rm) + local ice = selection.room(); + des.terrain(ice, "I"); + if (percent(25)) then + local mintime = 1000 - (nh.level_difficulty() * 100); + local ice_melter = function(x,y) + nh.start_timer_at(x,y, "melt-ice", mintime + nh.rn2(1000)); + end; + ice:iterate(ice_melter); + end + end, + }, + + { + name = "Cloud room", + contents = function(rm) + local fog = selection.room(); + for i = 1, (fog:numpoints() / 4) do + des.monster({ id = "fog cloud", asleep = true }); + end + des.gas_cloud({ selection = fog }); + end, + }, + + { + name = "Boulder room", mindiff = 4, contents = function(rm) local locs = selection.room():percentage(30); @@ -64,35 +82,39 @@ themeroom_fills = { end end; locs:iterate(func); - end + end, }, - -- Spider nest - function(rm) - local spooders = nh.level_difficulty() > 8; - local locs = selection.room():percentage(30); - local func = function(x,y) - des.trap({ type = "web", x = x, y = y, - spider_on_web = spooders and percent(80) }); - end - locs:iterate(func); - end, - - -- Trap room - function(rm) - local traps = { "arrow", "dart", "falling rock", "bear", - "land mine", "sleep gas", "rust", - "anti magic" }; - shuffle(traps); - local locs = selection.room():percentage(30); - local func = function(x,y) - des.trap(traps[1], x, y); - end - locs:iterate(func); - end, - - -- Garden { + name = "Spider nest", + contents = function(rm) + local spooders = nh.level_difficulty() > 8; + local locs = selection.room():percentage(30); + local func = function(x,y) + des.trap({ type = "web", x = x, y = y, + spider_on_web = spooders and percent(80) }); + end + locs:iterate(func); + end, + }, + + { + name = "Trap room", + contents = function(rm) + local traps = { "arrow", "dart", "falling rock", "bear", + "land mine", "sleep gas", "rust", + "anti magic" }; + shuffle(traps); + local locs = selection.room():percentage(30); + local func = function(x,y) + des.trap(traps[1], x, y); + end + locs:iterate(func); + end, + }, + + { + name = "Garden", eligible = function(rm) return rm.lit == true; end, contents = function(rm) local s = selection.room(); @@ -103,220 +125,255 @@ themeroom_fills = { des.feature("fountain"); end end - table.insert(postprocess, { handler = make_garden_walls, data = { sel = selection.room() } }); + table.insert(postprocess, { handler = make_garden_walls, + data = { sel = selection.room() } }); end }, - -- Buried treasure - function(rm) - des.object({ id = "chest", buried = true, contents = function(otmp) - local xobj = otmp:totable(); - -- keep track of the last buried treasure - if (xobj.NO_OBJ == nil) then - table.insert(postprocess, { handler = make_dig_engraving, data = { x = xobj.ox, y = xobj.oy }}); - end - for i = 1, d(3,4) do - des.object(); - end - end }); - end, - - -- Buried zombies - function(rm) - local diff = nh.level_difficulty() - -- start with [1..4] for low difficulty - local zombifiable = { "kobold", "gnome", "orc", "dwarf" }; - if diff > 3 then -- medium difficulty - zombifiable[5], zombifiable[6] = "elf", "human"; - if diff > 6 then -- high difficulty (relatively speaking) - zombifiable[7], zombifiable[8] = "ettin", "giant"; - end - end - for i = 1, (rm.width * rm.height) / 2 do - shuffle(zombifiable); - local o = des.object({ id = "corpse", montype = zombifiable[1], - buried = true }); - o:stop_timer("rot-corpse"); - o:start_timer("zombify-mon", math.random(990, 1010)); - end - end, - - -- Massacre - function(rm) - local mon = { "apprentice", "warrior", "ninja", "thug", - "hunter", "acolyte", "abbot", "page", - "attendant", "neanderthal", "chieftain", - "student", "wizard", "valkyrie", "tourist", - "samurai", "rogue", "ranger", "priestess", - "priest", "monk", "knight", "healer", - "cavewoman", "caveman", "barbarian", - "archeologist" }; - local idx = math.random(#mon); - for i = 1, d(5,5) do - if (percent(10)) then idx = math.random(#mon); end - des.object({ id = "corpse", montype = mon[idx] }); - end - end, - - -- Statuary - function(rm) - for i = 1, d(5,5) do - des.object({ id = "statue" }); - end - for i = 1, d(3) do - des.trap("statue"); - end - end, - - -- Light source { + name = "Buried treasure", + contents = function(rm) + des.object({ id = "chest", buried = true, contents = function(otmp) + local xobj = otmp:totable(); + -- keep track of the last buried treasure + if (xobj.NO_OBJ == nil) then + table.insert(postprocess, { handler = make_dig_engraving, + data = { x = xobj.ox, y = xobj.oy }}); + end + for i = 1, d(3,4) do + des.object(); + end + end }); + end, + }, + + { + name = "Buried zombies", + contents = function(rm) + local diff = nh.level_difficulty() + -- start with [1..4] for low difficulty + local zombifiable = { "kobold", "gnome", "orc", "dwarf" }; + if diff > 3 then -- medium difficulty + zombifiable[5], zombifiable[6] = "elf", "human"; + if diff > 6 then -- high difficulty (relatively speaking) + zombifiable[7], zombifiable[8] = "ettin", "giant"; + end + end + for i = 1, (rm.width * rm.height) / 2 do + shuffle(zombifiable); + local o = des.object({ id = "corpse", montype = zombifiable[1], + buried = true }); + o:stop_timer("rot-corpse"); + o:start_timer("zombify-mon", math.random(990, 1010)); + end + end, + }, + + { + name = "Massacre", + contents = function(rm) + local mon = { "apprentice", "warrior", "ninja", "thug", + "hunter", "acolyte", "abbot", "page", + "attendant", "neanderthal", "chieftain", + "student", "wizard", "valkyrie", "tourist", + "samurai", "rogue", "ranger", "priestess", + "priest", "monk", "knight", "healer", + "cavewoman", "caveman", "barbarian", + "archeologist" }; + local idx = math.random(#mon); + for i = 1, d(5,5) do + if (percent(10)) then idx = math.random(#mon); end + des.object({ id = "corpse", montype = mon[idx] }); + end + end, + }, + + { + name = "Statuary", + contents = function(rm) + for i = 1, d(5,5) do + des.object({ id = "statue" }); + end + for i = 1, d(3) do + des.trap("statue"); + end + end, + }, + + + { + name = "Light source", eligible = function(rm) return rm.lit == false; end, contents = function(rm) des.object({ id = "oil lamp", lit = true }); end }, - -- Temple of the gods - function(rm) - des.altar({ align = align[1] }); - des.altar({ align = align[2] }); - des.altar({ align = align[3] }); - end, + { + name = "Temple of the gods", + contents = function(rm) + des.altar({ align = align[1] }); + des.altar({ align = align[2] }); + des.altar({ align = align[3] }); + end, + }, - -- Ghost of an Adventurer - function(rm) - local loc = selection.room():rndcoord(0); - des.monster({ id = "ghost", asleep = true, waiting = true, coord = loc }); - if percent(65) then - des.object({ id = "dagger", coord = loc, buc = "not-blessed" }); - end - if percent(55) then - des.object({ class = ")", coord = loc, buc = "not-blessed" }); - end - if percent(45) then - des.object({ id = "bow", coord = loc, buc = "not-blessed" }); - des.object({ id = "arrow", coord = loc, buc = "not-blessed" }); - end - if percent(65) then - des.object({ class = "[", coord = loc, buc = "not-blessed" }); - end - if percent(20) then - des.object({ class = "=", coord = loc, buc = "not-blessed" }); - end - if percent(20) then - des.object({ class = "?", coord = loc, buc = "not-blessed" }); - end - end, - - -- Storeroom - function(rm) - local locs = selection.room():percentage(30); - local func = function(x,y) - if (percent(25)) then - des.object("chest"); - else - des.monster({ class = "m", appear_as = "obj:chest" }); + { + name = "Ghost of an Adventurer", + contents = function(rm) + local loc = selection.room():rndcoord(0); + des.monster({ id = "ghost", asleep = true, waiting = true, + coord = loc }); + if percent(65) then + des.object({ id = "dagger", coord = loc, buc = "not-blessed" }); end - end; - locs:iterate(func); - end, - - -- Teleportation hub - function(rm) - local locs = selection.room():filter_mapchar("."); - for i = 1, 2 + nh.rn2(3) do - local pos = locs:rndcoord(1); - if (pos.x > 0) then - pos.x = pos.x + rm.region.x1 - 1; - pos.y = pos.y + rm.region.y1; - table.insert(postprocess, { handler = make_a_trap, data = { type = "teleport", seen = true, coord = pos, teledest = 1 } }); + if percent(55) then + des.object({ class = ")", coord = loc, buc = "not-blessed" }); end - end - end, + if percent(45) then + des.object({ id = "bow", coord = loc, buc = "not-blessed" }); + des.object({ id = "arrow", coord = loc, buc = "not-blessed" }); + end + if percent(65) then + des.object({ class = "[", coord = loc, buc = "not-blessed" }); + end + if percent(20) then + des.object({ class = "=", coord = loc, buc = "not-blessed" }); + end + if percent(20) then + des.object({ class = "?", coord = loc, buc = "not-blessed" }); + end + end, + }, + + { + name = "Storeroom", + contents = function(rm) + local locs = selection.room():percentage(30); + local func = function(x,y) + if (percent(25)) then + des.object("chest"); + else + des.monster({ class = "m", appear_as = "obj:chest" }); + end + end; + locs:iterate(func); + end, + }, + + { + name = "Teleportation hub", + contents = function(rm) + local locs = selection.room():filter_mapchar("."); + for i = 1, 2 + nh.rn2(3) do + local pos = locs:rndcoord(1); + if (pos.x > 0) then + pos.x = pos.x + rm.region.x1 - 1; + pos.y = pos.y + rm.region.y1; + table.insert(postprocess, { handler = make_a_trap, + data = { type = "teleport", seen = true, + coord = pos, teledest = 1 } }); + end + end + end, + }, }; themerooms = { { - -- the "default" room + name = "default", frequency = 1000, contents = function() des.room({ type = "ordinary", filled = 1 }); - end + end }, - -- Fake Delphi - function() - des.room({ type = "ordinary", w = 11,h = 9, filled = 1, - contents = function() - des.room({ type = "ordinary", x = 4,y = 3, w = 3,h = 3, filled = 1, - contents = function() - des.door({ state="random", wall="all" }); - end - }); - end - }); - end, - - -- Room in a room - function() - des.room({ type = "ordinary", filled = 1, - contents = function() - des.room({ type = "ordinary", - contents = function() - des.door({ state="random", wall="all" }); - end - }); - end - }); - end, - - -- Huge room, with another room inside (90%) - function() - des.room({ type = "ordinary", w = nh.rn2(10)+11,h = nh.rn2(5)+8, filled = 1, - contents = function() - if (percent(90)) then - des.room({ type = "ordinary", filled = 1, - contents = function() - des.door({ state="random", wall="all" }); - if (percent(50)) then - des.door({ state="random", wall="all" }); - end - end - }); - end - end - }); - end, - - -- Nesting rooms - function() - des.room({ type = "ordinary", w = 9 + nh.rn2(4), h = 9 + nh.rn2(4), filled = 1, - contents = function(rm) - local wid = math.random(math.floor(rm.width / 2), rm.width - 2); - local hei = math.random(math.floor(rm.height / 2), rm.height - 2); - des.room({ type = "ordinary", w = wid,h = hei, filled = 1, - contents = function() - if (percent(90)) then - des.room({ type = "ordinary", filled = 1, - contents = function() - des.door({ state="random", wall="all" }); - if (percent(15)) then - des.door({ state="random", wall="all" }); - end - end - }); - end - des.door({ state="random", wall="all" }); - if (percent(15)) then - des.door({ state="random", wall="all" }); - end - end - }); - end - }); - end, + { + name = "Fake Delphi", + contents = function() + des.room({ type = "ordinary", w = 11,h = 9, filled = 1, + contents = function() + des.room({ type = "ordinary", x = 4,y = 3, w = 3,h = 3, + filled = 1, + contents = function() + des.door({ state="random", wall="all" }); + end + }); + end + }); + end, + }, { + name = "Room in a room", + contents = function() + des.room({ type = "ordinary", filled = 1, + contents = function() + des.room({ type = "ordinary", + contents = function() + des.door({ state="random", wall="all" }); + end + }); + end + }); + end, + }, + + { + name = "Huge room with another room inside", + contents = function() + des.room({ type = "ordinary", w = nh.rn2(10)+11,h = nh.rn2(5)+8, + filled = 1, + contents = function() + if (percent(90)) then + des.room({ type = "ordinary", filled = 1, + contents = function() + des.door({ state="random", wall="all" }); + if (percent(50)) then + des.door({ state="random", wall="all" }); + end + end + }); + end + end + }); + end, + }, + + { + name = "Nesting rooms", + contents = function() + des.room({ type = "ordinary", w = 9 + nh.rn2(4), h = 9 + nh.rn2(4), + filled = 1, + contents = function(rm) + local wid = math.random(math.floor(rm.width / 2), rm.width - 2); + local hei = math.random(math.floor(rm.height / 2), + rm.height - 2); + des.room({ type = "ordinary", w = wid,h = hei, filled = 1, + contents = function() + if (percent(90)) then + des.room({ type = "ordinary", filled = 1, + contents = function() + des.door({ state="random", wall="all" }); + if (percent(15)) then + des.door({ state="random", wall="all" }); + end + end + }); + end + des.door({ state="random", wall="all" }); + if (percent(15)) then + des.door({ state="random", wall="all" }); + end + end + }); + end + }); + end, + }, + + { + name = "Default room with themed fill", frequency = 6, contents = function() des.room({ type = "themed", contents = themeroom_fill }); @@ -324,6 +381,7 @@ themerooms = { }, { + name = "Unlit room with themed fill", frequency = 2, contents = function() des.room({ type = "themed", lit = 0, contents = themeroom_fill }); @@ -331,71 +389,79 @@ themerooms = { }, { + name = "Room with both normal contents and themed fill", frequency = 2, contents = function() des.room({ type = "themed", filled = 1, contents = themeroom_fill }); end }, - -- Pillars - function() - des.room({ type = "themed", w = 10, h = 10, - contents = function(rm) - local terr = { "-", "-", "-", "-", "L", "P", "T" }; - shuffle(terr); - for x = 0, (rm.width / 4) - 1 do - for y = 0, (rm.height / 4) - 1 do - des.terrain({ x = x * 4 + 2, y = y * 4 + 2, typ = terr[1], lit = -2 }); - des.terrain({ x = x * 4 + 3, y = y * 4 + 2, typ = terr[1], lit = -2 }); - des.terrain({ x = x * 4 + 2, y = y * 4 + 3, typ = terr[1], lit = -2 }); - des.terrain({ x = x * 4 + 3, y = y * 4 + 3, typ = terr[1], lit = -2 }); - end - end - end - }); - end, + { + name = 'Pillars', + contents = function() + des.room({ type = "themed", w = 10, h = 10, + contents = function(rm) + local terr = { "-", "-", "-", "-", "L", "P", "T" }; + shuffle(terr); + for x = 0, (rm.width / 4) - 1 do + for y = 0, (rm.height / 4) - 1 do + des.terrain({ x = x * 4 + 2, y = y * 4 + 2, typ = terr[1], lit = -2 }); + des.terrain({ x = x * 4 + 3, y = y * 4 + 2, typ = terr[1], lit = -2 }); + des.terrain({ x = x * 4 + 2, y = y * 4 + 3, typ = terr[1], lit = -2 }); + des.terrain({ x = x * 4 + 3, y = y * 4 + 3, typ = terr[1], lit = -2 }); + end + end + end + }); + end, + }, - -- Mausoleum - function() - des.room({ type = "themed", w = 5 + nh.rn2(3)*2, h = 5 + nh.rn2(3)*2, - contents = function(rm) - des.room({ type = "themed", - x = (rm.width - 1) / 2, y = (rm.height - 1) / 2, - w = 1, h = 1, joined = false, - contents = function() - if (percent(50)) then - local mons = { "M", "V", "L", "Z" }; - shuffle(mons); - des.monster({ class = mons[1], x=0,y=0, waiting = 1 }); - else - des.object({ id = "corpse", montype = "@", coord = {0,0} }); - end - if (percent(20)) then - des.door({ state="secret", wall="all" }); - end - end - }); - end - }); - end, + { + name = 'Mausoleum', + contents = function() + des.room({ type = "themed", w = 5 + nh.rn2(3)*2, h = 5 + nh.rn2(3)*2, + contents = function(rm) + des.room({ type = "themed", + x = (rm.width - 1) / 2, y = (rm.height - 1) / 2, + w = 1, h = 1, joined = false, + contents = function() + if (percent(50)) then + local mons = { "M", "V", "L", "Z" }; + shuffle(mons); + des.monster({ class = mons[1], x=0,y=0, waiting = 1 }); + else + des.object({ id = "corpse", montype = "@", coord = {0,0} }); + end + if (percent(20)) then + des.door({ state="secret", wall="all" }); + end + end + }); + end + }); + end, + }, - -- Random dungeon feature in the middle of an odd-sized room - function() - local wid = 3 + (nh.rn2(3) * 2); - local hei = 3 + (nh.rn2(3) * 2); - des.room({ type = "ordinary", filled = 1, w = wid, h = hei, - contents = function(rm) - local feature = { "C", "L", "I", "P", "T" }; - shuffle(feature); - des.terrain((rm.width - 1) / 2, (rm.height - 1) / 2, - feature[1]); - end - }); - end, + { + name = 'Random dungeon feature in the middle of an odd-sized room', + contents = function() + local wid = 3 + (nh.rn2(3) * 2); + local hei = 3 + (nh.rn2(3) * 2); + des.room({ type = "ordinary", filled = 1, w = wid, h = hei, + contents = function(rm) + local feature = { "C", "L", "I", "P", "T" }; + shuffle(feature); + des.terrain((rm.width - 1) / 2, (rm.height - 1) / 2, + feature[1]); + end + }); + end, + }, - -- L-shaped - function() - des.map({ map = [[ + { + name = 'L-shaped', + contents = function() + des.map({ map = [[ -----xxx |...|xxx |...|xxx @@ -404,11 +470,13 @@ themerooms = { |......| |......| --------]], contents = function(m) filler_region(1,1); end }); - end, + end, + }, - -- L-shaped, rot 1 - function() - des.map({ map = [[ + { + name = 'L-shaped, rot 1', + contents = function() + des.map({ map = [[ xxx----- xxx|...| xxx|...| @@ -417,11 +485,13 @@ xxx|...| |......| |......| --------]], contents = function(m) filler_region(5,1); end }); - end, + end, + }, - -- L-shaped, rot 2 - function() - des.map({ map = [[ + { + name = 'L-shaped, rot 2', + contents = function() + des.map({ map = [[ -------- |......| |......| @@ -430,11 +500,13 @@ xxx|...| xxx|...| xxx|...| xxx-----]], contents = function(m) filler_region(1,1); end }); - end, + end, + }, - -- L-shaped, rot 3 - function() - des.map({ map = [[ + { + name = 'L-shaped, rot 3', + contents = function() + des.map({ map = [[ -------- |......| |......| @@ -443,11 +515,13 @@ xxx-----]], contents = function(m) filler_region(1,1); end }); |...|xxx |...|xxx -----xxx]], contents = function(m) filler_region(1,1); end }); - end, + end, + }, - -- Blocked center - function() - des.map({ map = [[ + { + name = 'Blocked center', + contents = function() + des.map({ map = [[ ----------- |.........| |.........| @@ -459,18 +533,22 @@ xxx-----]], contents = function(m) filler_region(1,1); end }); |.........| |.........| -----------]], contents = function(m) -if (percent(30)) then - local terr = { "-", "P" }; - shuffle(terr); - des.replace_terrain({ region = {1,1, 9,9}, fromterrain = "L", toterrain = terr[1] }); -end -filler_region(1,1); -end }); - end, + if (percent(30)) then + local terr = { "-", "P" }; + shuffle(terr); + des.replace_terrain({ region = {1,1, 9,9}, + fromterrain = "L", + toterrain = terr[1] }); + end + filler_region(1,1); + end }); + end, + }, - -- Circular, small - function() - des.map({ map = [[ + { + name = 'Circular, small', + contents = function() + des.map({ map = [[ xx---xx x--.--x --...-- @@ -478,11 +556,13 @@ x--.--x --...-- x--.--x xx---xx]], contents = function(m) filler_region(3,3); end }); - end, + end, + }, - -- Circular, medium - function() - des.map({ map = [[ + { + name = 'Circular, medium', + contents = function() + des.map({ map = [[ xx-----xx x--...--x --.....-- @@ -492,11 +572,13 @@ x--...--x --.....-- x--...--x xx-----xx]], contents = function(m) filler_region(4,4); end }); - end, + end, + }, - -- Circular, big - function() - des.map({ map = [[ + { + name = 'Circular, big', + contents = function() + des.map({ map = [[ xxx-----xxx x---...---x x-.......-x @@ -508,11 +590,13 @@ x-.......-x x-.......-x x---...---x xxx-----xxx]], contents = function(m) filler_region(5,5); end }); - end, + end, + }, - -- T-shaped - function() - des.map({ map = [[ + { + name = 'T-shaped', + contents = function() + des.map({ map = [[ xxx-----xxx xxx|...|xxx xxx|...|xxx @@ -521,11 +605,13 @@ xxx|...|xxx |.........| |.........| -----------]], contents = function(m) filler_region(5,5); end }); - end, + end, + }, - -- T-shaped, rot 1 - function() - des.map({ map = [[ + { + name = 'T-shaped, rot 1', + contents = function() + des.map({ map = [[ -----xxx |...|xxx |...|xxx @@ -537,11 +623,13 @@ xxx|...|xxx |...|xxx |...|xxx -----xxx]], contents = function(m) filler_region(2,2); end }); - end, + end, + }, - -- T-shaped, rot 2 - function() - des.map({ map = [[ + { + name = 'T-shaped, rot 2', + contents = function() + des.map({ map = [[ ----------- |.........| |.........| @@ -550,11 +638,13 @@ xxx|...|xxx xxx|...|xxx xxx|...|xxx xxx-----xxx]], contents = function(m) filler_region(2,2); end }); - end, + end, + }, - -- T-shaped, rot 3 - function() - des.map({ map = [[ + { + name = 'T-shaped, rot 3', + contents = function() + des.map({ map = [[ xxx----- xxx|...| xxx|...| @@ -566,11 +656,13 @@ xxx|...| xxx|...| xxx|...| xxx-----]], contents = function(m) filler_region(5,5); end }); - end, + end, + }, - -- S-shaped - function() - des.map({ map = [[ + { + name = 'S-shaped', + contents = function() + des.map({ map = [[ -----xxx |...|xxx |...|xxx @@ -582,11 +674,13 @@ xxx-----]], contents = function(m) filler_region(5,5); end }); xxx|...| xxx|...| xxx-----]], contents = function(m) filler_region(2,2); end }); - end, + end, + }, - -- S-shaped, rot 1 - function() - des.map({ map = [[ + { + name = 'S-shaped, rot 1', + contents = function() + des.map({ map = [[ xxx-------- xxx|......| xxx|......| @@ -595,11 +689,13 @@ xxx|......| |......|xxx |......|xxx --------xxx]], contents = function(m) filler_region(5,5); end }); - end, + end, + }, - -- Z-shaped - function() - des.map({ map = [[ + { + name = 'Z-shaped', + contents = function() + des.map({ map = [[ xxx----- xxx|...| xxx|...| @@ -611,11 +707,13 @@ xxx|...| |...|xxx |...|xxx -----xxx]], contents = function(m) filler_region(5,5); end }); - end, + end, + }, - -- Z-shaped, rot 1 - function() - des.map({ map = [[ + { + name = 'Z-shaped, rot 1', + contents = function() + des.map({ map = [[ --------xxx |......|xxx |......|xxx @@ -624,11 +722,13 @@ xxx|...| xxx|......| xxx|......| xxx--------]], contents = function(m) filler_region(2,2); end }); - end, + end, + }, - -- Cross - function() - des.map({ map = [[ + { + name = 'Cross', + contents = function() + des.map({ map = [[ xxx-----xxx xxx|...|xxx xxx|...|xxx @@ -640,11 +740,13 @@ xxx|...|xxx xxx|...|xxx xxx|...|xxx xxx-----xxx]], contents = function(m) filler_region(6,6); end }); - end, + end, + }, - -- Four-leaf clover - function() - des.map({ map = [[ + { + name = 'Four-leaf clover', + contents = function() + des.map({ map = [[ -----x----- |...|x|...| |...---...| @@ -656,59 +758,65 @@ xx|.....|xx |...---...| |...|x|...| -----x-----]], contents = function(m) filler_region(6,6); end }); - end, + end, + }, - -- Water-surrounded vault - function() - des.map({ map = [[ + { + name = 'Water-surrounded vault', + contents = function() + des.map({ map = [[ }}}}}} }----} }|..|} }|..|} }----} -}}}}}}]], contents = function(m) des.region({ region={3,3,3,3}, type="themed", irregular=true, filled=0, joined=false }); - local nasty_undead = { "giant zombie", "ettin zombie", "vampire lord" }; - local chest_spots = { { 2, 2 }, { 3, 2 }, { 2, 3 }, { 3, 3 } }; +}}}}}}]], contents = function(m) + des.region({ region={3,3,3,3}, type="themed", irregular=true, + filled=0, joined=false }); + local nasty_undead = { "giant zombie", "ettin zombie", "vampire lord" }; + local chest_spots = { { 2, 2 }, { 3, 2 }, { 2, 3 }, { 3, 3 } }; - shuffle(chest_spots) - -- Guarantee an escape item inside one of the chests, to prevent the hero - -- falling in from above and becoming permanently stuck - -- [cf. generate_way_out_method(sp_lev.c)]. - -- If the escape item is made of glass or crystal, make sure that the - -- chest isn't locked so that kicking it to gain access to its contents - -- won't be necessary; otherwise retain lock state from random creation. - -- "pick-axe", "dwarvish mattock" could be included in the list of escape - -- items but don't normally generate in containers. - local escape_items = { - "scroll of teleportation", "ring of teleportation", - "wand of teleportation", "wand of digging" - }; - local itm = obj.new(escape_items[math.random(#escape_items)]); - local itmcls = itm:class() - local box - if itmcls[ "material" ] == 19 then -- GLASS==19 - -- item is made of glass so explicitly force chest to be unlocked - box = des.object({ id = "chest", coord = chest_spots[1], - olocked = "no" }); - else - -- item isn't made of glass; accept random locked/unlocked state - box = des.object({ id = "chest", coord = chest_spots[1] }); - end; - box:addcontent(itm); + shuffle(chest_spots) + -- Guarantee an escape item inside one of the chests, to prevent + -- the hero falling in from above and becoming permanently stuck + -- [cf. generate_way_out_method(sp_lev.c)]. + -- If the escape item is made of glass or crystal, make sure that + -- the chest isn't locked so that kicking it to gain access to its + -- contents won't be necessary; otherwise retain lock state from + -- random creation. + -- "pick-axe", "dwarvish mattock" could be included in the list of + -- escape items but don't normally generate in containers. + local escape_items = { + "scroll of teleportation", "ring of teleportation", + "wand of teleportation", "wand of digging" + }; + local itm = obj.new(escape_items[math.random(#escape_items)]); + local itmcls = itm:class() + local box + if itmcls[ "material" ] == "glass" then + -- explicitly force chest to be unlocked + box = des.object({ id = "chest", coord = chest_spots[1], + olocked = "no" }); + else + -- accept random locked/unlocked state + box = des.object({ id = "chest", coord = chest_spots[1] }); + end; + box:addcontent(itm); - for i = 2, #chest_spots do - des.object({ id = "chest", coord = chest_spots[i] }); - end + for i = 2, #chest_spots do + des.object({ id = "chest", coord = chest_spots[i] }); + end - shuffle(nasty_undead); - des.monster(nasty_undead[1], 2, 2); - des.exclusion({ type = "teleport", region = { 2,2, 3,3 } }); -end }); - end, + shuffle(nasty_undead); + des.monster(nasty_undead[1], 2, 2); + des.exclusion({ type = "teleport", region = { 2,2, 3,3 } }); + end }); + end, + }, - -- Twin businesses { - mindiff = 4; -- arbitrary + name = 'Twin businesses', + mindiff = 4, -- arbitrary contents = function() -- Due to the way room connections work in mklev.c, we must guarantee -- that the "aisle" between the shops touches all four walls of the @@ -761,7 +869,14 @@ end }); }; +-- store these at global scope, they will be reinitialized in +-- pre_themerooms_generate +debug_rm_idx = nil +debug_fill_idx = nil +-- Given a point in a themed room, ensure that themed room is stocked with +-- regular room contents. +-- With 30% chance, also give it a random themed fill. function filler_region(x, y) local rmtyp = "ordinary"; local func = nil; @@ -775,31 +890,76 @@ end function is_eligible(room, mkrm) local t = type(room); local diff = nh.level_difficulty(); - if (t == "table") then - if (room.mindiff ~= nil and diff < room.mindiff) then - return false - elseif (room.maxdiff ~= nil and diff > room.maxdiff) then - return false - end - if (mkrm ~= nil and room.eligible ~= nil) then - return room.eligible(mkrm); - end - elseif (t == "function") then - -- functions currently have no constraints + if (room.mindiff ~= nil and diff < room.mindiff) then + return false + elseif (room.maxdiff ~= nil and diff > room.maxdiff) then + return false + end + if (mkrm ~= nil and room.eligible ~= nil) then + return room.eligible(mkrm); end return true end +-- given the name of a themed room or fill, return its index in that array +function lookup_by_name(name, checkfills) + if name == nil then + return nil + end + if checkfills then + for i = 1, #themeroom_fills do + if themeroom_fills[i].name == name then + return i + end + end + else + for i = 1, #themerooms do + if themerooms[i].name == name then + return i + end + end + end + return nil +end + -- called repeatedly until the core decides there are enough rooms function themerooms_generate() - local pick = 1; + if debug_rm_idx ~= nil then + -- room may not be suitable for stairs/portals, so create the "default" + -- room half of the time + -- (if the user specified BOTH a room and a fill, presumably they are + -- interested in what happens when that room gets that fill, so don't + -- bother generating default-with-fill rooms as happens below) + local actualrm = lookup_by_name("default", false); + if percent(50) then + if is_eligible(themerooms[debug_rm_idx]) then + actualrm = debug_rm_idx + else + pline("Warning: themeroom '"..themerooms[debug_rm_idx].name + .."' is ineligible") + end + end + themerooms[actualrm].contents(); + return + elseif debug_fill_idx ~= nil then + -- when a fill is requested but not a room, still create the "default" + -- room half of the time, and "default with themed fill" half of the time + -- (themeroom_fill will take care of guaranteeing the fill in it) + local actualrm = lookup_by_name(percent(50) and "Default room with themed fill" + or "default") + themerooms[actualrm].contents(); + return + end + local pick = nil; local total_frequency = 0; for i = 1, #themerooms do - -- Reservoir sampling: select one room from the set of eligible rooms, - -- which may change on different levels because of level difficulty. - if is_eligible(themerooms[i], nil) then + if (type(themerooms[i]) ~= "table") then + nh.impossible('themed room '..i..' is not a table') + elseif is_eligible(themerooms[i], nil) then + -- Reservoir sampling: select one room from the set of eligible rooms, + -- which may change on different levels because of level difficulty. local this_frequency; - if (type(themerooms[i]) == "table" and themerooms[i].frequency ~= nil) then + if (themerooms[i].frequency ~= nil) then this_frequency = themerooms[i].frequency; else this_frequency = 1; @@ -811,17 +971,34 @@ function themerooms_generate() end end end - - local t = type(themerooms[pick]); - if (t == "table") then - themerooms[pick].contents(); - elseif (t == "function") then - themerooms[pick](); + if pick == nil then + nh.impossible('no eligible themed rooms?') + return end + themerooms[pick].contents(); end -- called before any rooms are generated function pre_themerooms_generate() + local debug_themerm = nh.debug_themerm(false) + local debug_fill = nh.debug_themerm(true) + local xtrainfo = "" + debug_rm_idx = lookup_by_name(debug_themerm, false) + debug_fill_idx = lookup_by_name(debug_fill, true) + if debug_themerm ~= nil and debug_rm_idx == nil then + if lookup_by_name(debug_themerm, true) ~= nil then + xtrainfo = "; it is a fill type" + end + pline("Warning: themeroom '"..debug_themerm + .."' not found in themerooms"..xtrainfo, true) + end + if debug_fill ~= nil and debug_fill_idx == nil then + if lookup_by_name(debug_fill, false) ~= nil then + xtrainfo = "; it is a room type" + end + pline("Warning: themeroom fill '"..debug_fill + .."' not found in themeroom_fills"..xtrainfo, true) + end end -- called after all rooms have been generated @@ -830,32 +1007,45 @@ function post_themerooms_generate() end function themeroom_fill(rm) - local pick = 1; + if debug_fill_idx ~= nil then + if is_eligible(themeroom_fills[debug_fill_idx], rm) then + themeroom_fills[debug_fill_idx].contents(rm); + else + -- ideally this would be a debugpline, not a full pline, and offer + -- some more context on whether it failed because of difficulty or + -- because of eligible function returning false; the warning doesn't + -- necessarily mean anything. + pline("Warning: fill '"..themeroom_fills[debug_fill_idx].name + .."' is not eligible in room that generated it") + end + return + end + local pick = nil; local total_frequency = 0; for i = 1, #themeroom_fills do - -- Reservoir sampling: select one room from the set of eligible rooms, - -- which may change on different levels because of level difficulty. - if is_eligible(themeroom_fills[i], rm) then + if (type(themeroom_fills[i]) ~= "table") then + nh.impossible('themeroom fill '..i..' must be a table') + elseif is_eligible(themeroom_fills[i], rm) then + -- Reservoir sampling: select one room from the set of eligible rooms, + -- which may change on different levels because of level difficulty. local this_frequency; - if (type(themeroom_fills[i]) == "table" and themeroom_fills[i].frequency ~= nil) then + if (themeroom_fills[i].frequency ~= nil) then this_frequency = themeroom_fills[i].frequency; else this_frequency = 1; end total_frequency = total_frequency + this_frequency; - -- avoid rn2(0) if a room has freq 0 + -- avoid rn2(0) if a fill has freq 0 if this_frequency > 0 and nh.rn2(total_frequency) < this_frequency then pick = i; end end end - - local t = type(themeroom_fills[pick]); - if (t == "table") then - themeroom_fills[pick].contents(rm); - elseif (t == "function") then - themeroom_fills[pick](rm); + if pick == nil then + nh.impossible('no eligible themed room fills?') + return end + themeroom_fills[pick].contents(rm); end -- postprocess callback: create an engraving pointing at a location @@ -881,7 +1071,10 @@ end -- postprocess callback: turn room walls into trees function make_garden_walls(data) local sel = data.sel:grow(); + -- change walls to trees des.replace_terrain({ selection = sel, fromterrain="w", toterrain = "T" }); + -- update secret doors; attempting to change to AIR will set arboreal flag + des.replace_terrain({ selection = sel, fromterrain="S", toterrain = "A" }); end -- postprocess callback: make a trap diff --git a/dat/tower1.lua b/dat/tower1.lua index c968b707c..eae3016b3 100644 --- a/dat/tower1.lua +++ b/dat/tower1.lua @@ -37,9 +37,14 @@ des.monster("V",niches[3]) -- gave them titles rather than (or perhaps in addition to) specific names -- and we use those titles here. Marking them as 'waiting' forces them to -- start in vampire form instead of vampshifted into bat/fog/wolf form. -des.monster({ id="Vampire Lady", niches[4], name="Madame", waiting=1 }) -des.monster({ id="Vampire Lady", niches[5], name="Marquise", waiting=1 }) -des.monster({ id="Vampire Lady", niches[6], name="Countess", waiting=1 }) +local Vgenod = nh.is_genocided("vampire"); +local Vnames = { nil, nil, nil }; +if (not Vgenod) then + Vnames = { "Madame", "Marquise", "Countess" }; +end +des.monster({ id="vampire lady", coord=niches[4], name=Vnames[1], waiting=1 }) +des.monster({ id="vampire lady", coord=niches[5], name=Vnames[2], waiting=1 }) +des.monster({ id="vampire lady", coord=niches[6], name=Vnames[3], waiting=1 }) -- The doors des.door("closed",08,03) des.door("closed",10,03) diff --git a/dat/tribute b/dat/tribute index e8b34a8ec..5438d48be 100644 --- a/dat/tribute +++ b/dat/tribute @@ -3665,7 +3665,7 @@ and needs to see in the dark. Strange but true. %e passage # p. 218 %passage 9 -"He's bound to have done /something/," Noddy repeated. +"He's bound to have done /something/," Nobby repeated. In this he was echoing the Patrician's view of crime and punishment. If there was a crime, there should be punishment. If the specific criminal @@ -6031,7 +6031,7 @@ was mostly unexplored, too.(1) %e passage # p. 219 (passage begins mid-sentence) %passage 12 -[...] Sam Vines had learned a lot from watching Lady Sybil. She didn't +[...] Sam Vimes had learned a lot from watching Lady Sybil. She didn't mean to act like that, but she'd been born to it, into a class which had always behaved this way: You went through the world as if there was /no possibility/ that anyone would stop you or question you, and most of diff --git a/dat/tut-1.lua b/dat/tut-1.lua index 13e6ed3f7..b30e4edb2 100644 --- a/dat/tut-1.lua +++ b/dat/tut-1.lua @@ -1,26 +1,53 @@ +local tut_ctrl_key = nil; +local tut_alt_key = nil; + +function tut_key(command) + local s = nh.eckey(command); + local m = s:match("^^([A-Z])$"); -- ^X is Ctrl-X + if (m ~= nil) then + tut_ctrl_key = m; + return "Ctrl-" .. m; + end + + m = s:match("^M%-([A-Z])$"); -- M-X is Alt-X + if (m ~= nil) then + tut_alt_key = m; + return "Alt-" .. m; + end + + return s; +end + +function tut_key_help(x, y) + if (tut_ctrl_key ~= nil) then + des.engraving({ coord = { x,y }, type = "engrave", text = "Note: Outside the tutorial, Ctrl-key combinations are shown prefixed with a caret, like '^" .. tut_ctrl_key .. "'", degrade = false }); + tut_ctrl_key = nil; + end +end + des.level_init({ style = "solidfill", fg = " " }); des.level_flags("mazelevel", "noflip", "nomongen", "nodeathdrops", "noautosearch"); des.map([[ --------------------------------------------------------------------------- -|-.--|.......|......|..S....|.F.......|.............|.....................| +|-.--|.......|......|..S....|.F.......|.............|.......|.............| |.-..........|......|--|....|.F.....|.|S-------.....|.....................| -||.--|.......|..T......|....|.F.....|.|.......|.....|.....................| -||.|.|.......|......|-.|....|.F.....|.|.......|.....|.....................| +||.--|.......|..T......|....|.F.....|.|.......|.....|.......|.............| +||.|.|.......|......|-.|....|.F.....|.|.......|.....|--------.............| ||.|.|.......|......||.|-.-----------.-.......|-S----.....................| |-+-S---------..---.||........................|...|.......................| |......| |.-------------------.......|...|....--S----............| |......| ###### |.........| |..S.......|...|....|.....|............| |----.-| -+- # |.....---.|######+..|.......S...|....|.....|............| -|----+----.----+---.|.--|.|.|# ------------...|....|.....|............| +|----+----.----+---.|.--|.|.|# ------------...|....|.....F............| |........|.|......|.|...F...|# ........|.....+...|....|.....|............| -|.P......-S|......|------.---# .........|.....|...|....-------............| -|..........|......+.|...|.|.S# ..--S-----.....|LLL|.......................| -|.W......---......|.|.|.|.|.|# ..|......|.....|LLL|.......................| -|....Z.L.S.F......|.|.|.|.---# |......+.....|...|.......................| -|........|--......|...|.....|####+......|.....|...+.......................| +|.P......-S|......|------.---# .........|.....|...|....-------........----| +|..........|......+.|...|.|.S# ..--S-----.....|LLL|..................|..| | +|.W......---......|.|.|.|.|.|# ..|......|.....|LLL|..................|..--| +|....Z.L.S.F......|.|.|.|.---# |......+.....|...|..................|..|.| +|........|--......|...|.....|####+......|.....|...+..................||...| --------------------------------------------------------------------------- ]]); @@ -40,26 +67,30 @@ nh.parse_config("OPTIONS=mention_walls"); nh.parse_config("OPTIONS=mention_decor"); nh.parse_config("OPTIONS=lit_corridor"); -local movekeys = nh.eckey("movewest") .. " " .. - nh.eckey("movesouth") .. " " .. - nh.eckey("movenorth") .. " " .. - nh.eckey("moveeast"); +local movekeys = tut_key("movewest") .. " " .. + tut_key("movesouth") .. " " .. + tut_key("movenorth") .. " " .. + tut_key("moveeast"); -local diagmovekeys = nh.eckey("movesouthwest") .. " " .. - nh.eckey("movenortheast") .. " " .. - nh.eckey("movesoutheast") .. " " .. - nh.eckey("movenorthwest"); +local diagmovekeys = tut_key("movesouthwest") .. " " .. + tut_key("movenortheast") .. " " .. + tut_key("movesoutheast") .. " " .. + tut_key("movenorthwest"); des.engraving({ coord = { 9,3 }, type = "engrave", text = "Move around with " .. movekeys, degrade = false }); des.engraving({ coord = { 5,2 }, type = "engrave", text = "Move diagonally with " .. diagmovekeys, degrade = false }); +if (u.role == "Knight") then + des.engraving({ coord = { 12,1 }, type = "engrave", text = "Knights can jump with '" .. tut_key("jump") .. "'", degrade = false }); +end + -- des.engraving({ coord = { 2,4 }, type = "engrave", text = "Some actions may require multiple tries before succeeding", degrade = false }); des.engraving({ coord = { 2,5 }, type = "engrave", text = "Open the door by moving into it", degrade = false }); des.door({ coord = { 2,6 }, state = "closed" }); -des.engraving({ coord = { 2,7 }, type = "engrave", text = "Close the door with '" .. nh.eckey("close") .. "'", degrade = false }); +des.engraving({ coord = { 2,7 }, type = "engrave", text = "Close the door with '" .. tut_key("close") .. "'", degrade = false }); -- @@ -69,14 +100,20 @@ des.trap({ type = "magic portal", coord = { 4,4 }, seen = true }); -- -des.engraving({ coord = { 5,9 }, type = "engrave", text = "This door is locked. Kick it with '" .. nh.eckey("kick") .. "'", degrade = false }); +des.engraving({ coord = { 5,9 }, type = "engrave", text = "This door is locked. Kick it with '" .. tut_key("kick") .. "'", degrade = false }); des.door({ coord = { 5,10 }, state = "locked" }); -des.engraving({ coord = { 5,12 }, type = "engrave", text = "Look around the map with '" .. nh.eckey("glance") .. "'", degrade = false }); +-- by default, kick is the first command that can be a ctrl-key combo +tut_key_help(6, 8); + + +des.engraving({ coord = { 5,12 }, type = "engrave", text = "Look around the map with '" .. tut_key("glance") .. "', press ESC when you're done", degrade = false }); -- -des.engraving({ coord = { 10,13 }, type = "engrave", text = "Use '" .. nh.eckey("search") .. "' to search for secret doors", degrade = false }); +des.engraving({ coord = { 10,13 }, type = "engrave", text = "Use '" .. tut_key("search") .. "' to search for secret doors", degrade = false }); + +des.engraving({ coord = { 10,15 }, type = "engrave", text = "Wrong secret", degrade = false }); -- @@ -96,21 +133,24 @@ for i = 1, 4 do coord = locs[i], victim = false }); end +des.engraving({ coord = { 15,15 }, type = "engrave", text = "Some traps can be disabled with '" .. tut_key("untrap") .. "'", degrade = false }); +des.trap({ coord = { 15,16 }, type = "web", spider_on_web = false }); + -- des.door({ coord = { 18,13 }, state = "closed" }); -des.engraving({ coord = { 19,13 }, type = "engrave", text = "Pick up items with '" .. nh.eckey("pickup") .. "'", degrade = false }); +des.engraving({ coord = { 19,13 }, type = "engrave", text = "Pick up items with '" .. tut_key("pickup") .. "'", degrade = false }); local armor = (u.role == "Monk") and "leather gloves" or "leather armor"; des.object({ id = armor, spe = 0, buc = "cursed", coord = { 19,14} }); -des.engraving({ coord = { 19,15 }, type = "engrave", text = "Wear armor with '" .. nh.eckey("wear") .. "'", degrade = false }); +des.engraving({ coord = { 19,15 }, type = "engrave", text = "Wear armor with '" .. tut_key("wear") .. "'", degrade = false }); des.object({ id = "dagger", spe = 0, buc = "not-cursed", coord = { 21,15} }); -des.engraving({ coord = { 21,14 }, type = "engrave", text = "Wield weapons with '" .. nh.eckey("wield") .. "'", degrade = false }); +des.engraving({ coord = { 21,14 }, type = "engrave", text = "Wield weapons with '" .. tut_key("wield") .. "'", degrade = false }); des.engraving({ coord = { 22,13 }, type = "engrave", text = "Hit monsters by walking into them.", degrade = false }); @@ -131,13 +171,13 @@ des.object({ id = "boulder", coord = {25,12} }); -- -des.engraving({ coord = { 27,9 }, type = "engrave", text = "Take off armor with '" .. nh.eckey("takeoff") .. "'", degrade = false }); +des.engraving({ coord = { 27,9 }, type = "engrave", text = "Take off armor with '" .. tut_key("takeoff") .. "'", degrade = false }); -- des.object({ class = "?", id = "remove curse", buc = "blessed", coord = {23,11} }) des.engraving({ coord = { 22,11 }, type = "engrave", text = "Some items have shuffled descriptions, different each game", degrade = false }); -des.engraving({ coord = { 23,11 }, type = "engrave", text = "Pick up this scroll, read it with '" .. nh.eckey("read") .. "', and try to remove the armor again", degrade = false }); +des.engraving({ coord = { 23,11 }, type = "engrave", text = "Pick up this scroll, read it with '" .. tut_key("read") .. "', and try to remove the armor again", degrade = false }); -- @@ -157,14 +197,14 @@ des.object({ coord = {14, 6}, id = "boulder" }); des.door({ coord = { 20,3 }, state = percent(50) and "open" or "closed" }); des.engraving({ coord = { 21,3 }, type = "engrave", text = "Avoid being burdened, it slows you down", degrade = false }); -des.engraving({ coord = { 22,3 }, type = "engrave", text = "Drop items with '" .. nh.eckey("drop") .. "'", degrade = false }); +des.engraving({ coord = { 22,3 }, type = "engrave", text = "Drop items with '" .. tut_key("drop") .. "'", degrade = false }); des.engraving({ coord = { 22,4 }, type = "engrave", text = "You can drop partial stacks by prefixing the item slot letter with a number", degrade = false }); -- des.monster({ id = "yellow mold", coord = { 26,2 }, waiting = true, countbirth = false }); -des.engraving({ coord = { 25,5 }, type = "engrave", text = "Throw items with '" .. nh.eckey("throw") .. "'", degrade = false }); +des.engraving({ coord = { 25,5 }, type = "engrave", text = "Throw items with '" .. tut_key("throw") .. "'", degrade = false }); des.trap({ type = "magic portal", coord = { 21,1 }, seen = true }); @@ -176,35 +216,36 @@ des.engraving({ coord = { 37,4 }, type = "engrave", text = "Missiles, such as ro des.object({ coord = { 37,3 }, id = "sling", buc = "not-cursed", spe = 9 }); des.engraving({ coord = { 37,3 }, type = "engrave", text = "Wield the sling", degrade = false }); -des.engraving({ coord = { 36,1 }, type = "engrave", text = "Use '" .. nh.eckey("fire") .. "' to fire missiles with the wielded launcher", degrade = false }); +des.engraving({ coord = { 36,1 }, type = "engrave", text = "Use '" .. tut_key("fire") .. "' to fire missiles with the wielded launcher", degrade = false }); -des.engraving({ coord = { 35,4 }, type = "engrave", text = "Firing launches items from your quiver; Use '" .. nh.eckey("quiver") .. "' to put items in it", degrade = false }); +des.engraving({ coord = { 35,4 }, type = "engrave", text = "Firing launches items from your quiver; Use '" .. tut_key("quiver") .. "' to put items in it", degrade = false }); -des.engraving({ coord = { 33,4 }, type = "engrave", text = "You can wait a turn with '" .. nh.eckey("wait") .. "'", degrade = false }); +des.engraving({ coord = { 33,4 }, type = "engrave", text = "You can wait a turn with '" .. tut_key("wait") .. "'", degrade = false }); -- des.door({ coord = { 38,6 }, state = "closed" }); -des.engraving({ coord = { 39,6 }, type = "engrave", text = "You loot containers with '" .. nh.eckey("loot") .. "'", degrade = false }); +des.engraving({ coord = { 39,6 }, type = "engrave", text = "You loot containers with '" .. tut_key("loot") .. "'", degrade = false }); -des.object({ coord = { 42,6 }, id = "large box", broken = true, +des.object({ coord = { 41,6 }, id = "large box", broken = true, trapped = false, contents = function(obj) des.object({ id = "secret door detection", class = "/", spe = 30 }); end }); +des.engraving({ coord = { 42,6 }, type = "engrave", text = "Containers can also be emptied with '" .. tut_key("tip") .. "'", degrade = false }); -des.engraving({ coord = { 45,6 }, type = "engrave", text = "Magic wands are used with '" .. nh.eckey("zap") .. "'", degrade = false }); +des.engraving({ coord = { 45,6 }, type = "engrave", text = "Magic wands are used with '" .. tut_key("zap") .. "'", degrade = false }); -- des.door({ coord = { 35,9 }, state = "nodoor" }); -des.engraving({ coord = { 34,9 }, type = "engrave", text = "You can run by prefixing a movement key with '" .. nh.eckey("run") .. "'", degrade = false }); +des.engraving({ coord = { 34,9 }, type = "engrave", text = "You can run by prefixing a movement key with '" .. tut_key("run") .. "'", degrade = false }); -- des.door({ coord = { 33,16 }, state = "nodoor" }); -des.engraving({ coord = { 35,15 }, type = "engrave", text = "Travel across the level with '" .. nh.eckey("travel") .. "'", degrade = false }); +des.engraving({ coord = { 35,15 }, type = "engrave", text = "Travel across the level with '" .. tut_key("travel") .. "'", degrade = false }); -- @@ -212,23 +253,22 @@ des.trap({ type = "magic portal", coord = { 27,14 }, seen = true }); -- -des.engraving({ coord = { 48,1 }, type = "burn", text = "Use '" .. nh.eckey("eat") .. "' to eat edible things", degrade = false }); +des.engraving({ coord = { 48,1 }, type = "burn", text = "Use '" .. tut_key("eat") .. "' to eat edible things", degrade = false }); des.object({ coord = { 50,3 }, id = "apple", buc = "not-cursed" }); des.object({ coord = { 50,3 }, id = "candy bar", buc = "not-cursed" }); -local otmp = des.object({ coord = { 50,3 }, id = "corpse", montype = "newt", buc = "not-cursed" }); -otmp:stop_timer("rot-corpse"); +des.object({ coord = { 50,3 }, id = "corpse", montype = "lichen", buc = "not-cursed" }); -- des.door({ coord = { 46,11 }, state = "closed" }); -des.engraving({ coord = { 43,11 }, type = "burn", text = "Use '" .. nh.eckey("twoweapon") .. "' to use two weapons at once", degrade = false }); +des.engraving({ coord = { 43,11 }, type = "burn", text = "Use '" .. tut_key("twoweapon") .. "' to use two weapons at once", degrade = false }); des.object({ coord = { 43,13 }, id = "knife", buc = "uncursed" }); des.object({ coord = { 43,14 }, id = "dagger", buc = "blessed" }); -des.engraving({ coord = { 43,16 }, type = "burn", text = "Swap weapons quickly with '" .. nh.eckey("swap") .. "'", degrade = false }); +des.engraving({ coord = { 43,16 }, type = "burn", text = "Swap weapons quickly with '" .. tut_key("swap") .. "'", degrade = false }); des.door({ coord = { 40,15 }, state = "random" }); @@ -236,24 +276,60 @@ des.door({ coord = { 40,15 }, state = "random" }); des.object({ coord = { 48,7 }, id = "ring of levitation", buc = "not-cursed" }); -des.engraving({ coord = { 48,10 }, type = "burn", text = "Put on accessories with '" .. nh.eckey("puton") .. "'", degrade = false }); +des.engraving({ coord = { 48,10 }, type = "burn", text = "Put on accessories with '" .. tut_key("puton") .. "'", degrade = false }); -des.engraving({ coord = { 48,16 }, type = "burn", text = "Remove accessories with '" .. nh.eckey("remove") .. "'", degrade = false }); +des.engraving({ coord = { 48,16 }, type = "burn", text = "Remove accessories with '" .. tut_key("remove") .. "'", degrade = false }); des.door({ coord = { 50,16 }, state = "closed" }); -- -des.engraving({ coord = { 58,9 }, type = "burn", text = "Use '" .. nh.eckey("down") .. "' to go down the stairs", degrade = false }); +des.engraving({ coord = { 58,9 }, type = "burn", text = "Use '" .. tut_key("down") .. "' to go down the stairs", degrade = false }); des.stair({ dir = "down", coord = { 58,10 } }); -- +-- one more ctrl-key help, if needed +tut_key_help(64, 4); + des.engraving({ coord = { 65,3 }, type = "burn", text = "UNDER CONSTRUCTION", degrade = false }); des.trap({ type = "magic portal", coord = { 66,2 }, seen = true }); +-- + +-- squeezing through small gaps + +des.engraving({ coord = { 69,12 }, type = "burn", text = "Can't get through? You're carrying too much.", degrade = false }); + +-- try to squeeze over boulders, find a trap door + +des.object({ id = "boulder", coord = {71,16} }); +des.object({ id = "boulder", coord = {72,16} }); +des.object({ id = "boulder", coord = {73,16} }); +des.trap({ type = "trap door", coord = { 73,15 } }); + +-- + +des.engraving({ coord = { 60,2 }, type = "engrave", text = "Spellcasting", degrade = false }); +if (u.uenmax < 5) then + -- TODO: make sure hero has enough Pw to cast the spell (5 pw) instead? + -- TODO: ensure the first cast of this spell succeeds? + des.engraving({ coord = { 59,2 }, type = "engrave", text = "Unfortunately you don't have enough energy to cast spells.", degrade = false }); +end +des.engraving({ coord = { 57,2 }, type = "engrave", text = "Pick up the spellbook with '" .. tut_key("pickup") .. "'", degrade = false }); +des.object({ coord = { 57,2 }, id = "spellbook of light", buc = "blessed" }); +des.engraving({ coord = { 55,2 }, type = "engrave", text = "Read the spellbook with '" .. tut_key("read") .. "'", degrade = false }); +des.engraving({ coord = { 53,2 }, type = "engrave", text = "Use '" .. tut_key("cast") .. "' to cast a spell", degrade = false }); +des.region(selection.area(53,01, 59, 3), "unlit"); + +-- + +des.engraving({ coord = { 72,2 }, type = "engrave", text = "You \"quaff\" potions with '" .. tut_key("quaff") .. "'", degrade = false }); +des.object({ coord = { 72,2 }, id = "potion of object detection", buc = "blessed" }); + + ---------------- -- entering and leaving tutorial _branch_ now handled by core diff --git a/doc/.gitattributes b/doc/.gitattributes index f4d35defc..1d1afaeab 100644 --- a/doc/.gitattributes +++ b/doc/.gitattributes @@ -3,7 +3,6 @@ *.6 NHSUBST *.7 NHSUBST fixes* NHSUBST -*.txt NHSUBST config.nh NHSUBST Guidebook.txt NH_header=no tmac.n NH_header=no @@ -12,5 +11,7 @@ fixes* NH_header=no *.txt NH_header=no * NH_filestag=(file%s_for_all_versions) Guidebook.tex NH_DATESUB -Guidebook.mn NH_DATESUB -*.6 NH_DATESUB +Guidebook.mn NH_DATESUB NHAUTODOCS +*.6 NH_DATESUB NHAUTODOCS +*.7 NH_DATESUB NHAUTODOCS +fixes* -NHAUTODOCS diff --git a/doc/.gitignore b/doc/.gitignore index dfd019421..2cb6f5896 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -14,4 +14,10 @@ Guidebook.pdf Guidebook.ps Guidebook.dated.* *.synctex.* - +# files that might appear from "make fetch-docs" +dlb.pdf +makedefs.pdf +mn.pdf +mnh.pdf +nethack.pdf +recover.pdf diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 549f9c0b7..dae3b6713 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -1,4 +1,4 @@ -.\" $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.574 $ $NHDT-Date: 1711040379 2024/03/21 16:59:39 $ +.\" $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.598 $ $NHDT-Date: 1745139202 2025/04/20 00:53:22 $ .\" .\" This is an excerpt from the 'roff' man page from the 'groff' package. .\"+-- @@ -46,8 +46,8 @@ .ds vr NetHack 3.7.0 .ds f0 \*(vr .ds f1 \" empty -.\"DO NOT REMOVE NH_DATESUB .ds f2 DATE(%B %-d, %Y) -.ds f2 September 13, 2024 +.\"DO NOT REMOVE NH_DATESUB .ds f2 Date(%B %-d, %Y) +.ds f2 May 1, 2025 . .\" A note on some special characters: .\" \(lq = left double quote @@ -2920,8 +2920,16 @@ magic spells on them). .pg One of the most useful of these is the \fIscroll of identify\fP, 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. +blessed, and how many uses it has left. +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 +it isn't shown. +.pg +Many scrolls produce a different effect from usual if they are blessed or +cursed, or read while the hero is confused. .pg A mail daemon may run up and deliver mail to you as a \fIscroll of mail\fP (on versions compiled with this feature). @@ -3780,7 +3788,8 @@ See .op role for a description of how to use negation to exclude choices. .lp "" -Default is random. +If \f(CRalign\fP 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 \(oq\f(CRO\fP\(cq command. Persistent. .lp autodescribe @@ -4006,7 +4015,8 @@ See .op role for a description of how to use negation to exclude choices. .lp "" -Default is random. +If \f(CRgender\fP 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 \(oq\f(CRO\fP\(cq command. Persistent. .lp "goldX " @@ -4161,8 +4171,38 @@ Default \(oq|\(cq. Key to go to the next menu page. Default \(oq>\(cq. .lp menu_objsyms -Show object symbols in menu headings in menus where -the object symbols act as menu accelerators (default off). +." [originally menu_objsyms was a boolean] +." Show object symbols in menu headings in menus where +." the object symbols act as menu accelerators (default off). +Inventory and other object menus are normally separated by object class +(weapons, armor, and so forth), with a menu header line at the beginning +of each group. +You can have menus add the display symbol for the class of objects for +each header line. +You can also add the display symbol for the individual item in each menu +entry. +For a tiles map, that would be a small rendition of an object's tile. +For a text map, it is the same character as is used for the object's +class, which would be most useful when there are no headers separating +objects among classes. +Possible values are +.PS "5\ -\ One-or-other" +.PL "0\ -\ None" +no symbols for either header lines or menu entries; +.PL "1\ -\ Headers" +show symbols on header lines but not entries; +.PL "2\ -\ Entries" +show symbols on menu entry lines but not headers; +.PL "3\ -\ Both" +show symbols on headers and entries; +.PL "4\ -\ Conditional" +only show symbols for entries if there are no headers; +.PL "5\ -\ One-or-other" +show symbols on headers, or on entries if no headers. +.PE +Supported by tty and curses. +When setting the value, it can be specified by digit or keyword. +The default value is \f(CRConditional\fP (4). .lp menu_overlay Do not clear the screen before drawing menus, and align menus to the right edge of the screen. Only for the tty port. @@ -4246,6 +4286,11 @@ the role (that is, by suffixing one of If .op "\-@" is used for the role, then a random one will be automatically chosen. +.lp "" +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 \(oq\f(CRO\fP\(cq command. .lp "news " Read the NetHack news file, if present (default on). @@ -4506,7 +4551,8 @@ See .op role for a description of how to use negation to exclude choices. .lp "" -Default is random. +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 rest_on_space @@ -4542,7 +4588,8 @@ option if they're all negations. .\" Only one positive value is allowed, and if present, it overrides any .\" negations. .lp "" -Default is \f(CRrandom\fP. +If \f(CRrole\fP is not specified, there is no default value; +player will be prompted. Cannot be set with the \(oq\f(CRO\fP\(cq command. Persistent. .lp roguesymset @@ -6064,23 +6111,13 @@ escape command (!). The syntax is the same as WIZARDS. EXPLORERS\ =\ A list of users who are allowed to use the explore mode. The syntax is the same as WIZARDS. .lp +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 MAXPLAYERS\ =\ Limit the maximum number of games that can be running at the same time. .lp -SAVEFORMAT\ =\ A list of up to two save file formats separated by space. -The first format in the list will written as well as read. The second format -will be read only if no save file in the first format exists. -Valid choices are \(lqhistorical\(rq for binary writing of entire structs, -\(lqlendian\(rq for binary writing of each field in little-endian order, -\(lqascii\(rq for writing the save file content in ascii text. -.lp -BONESFORMAT\ =\ A list of up to two bones file formats separated by space. -The first format in the list will written as well as read. The second format -will be read only if no bones files in the first format exist. -Valid choices are \(lqhistorical\(rq for binary writing of entire structs, -\(lqlendian\(rq for binary writing of each field in little-endian order, -\(lqascii\(rq for writing the bones file content in ascii text. -.lp SUPPORT\ =\ A string explaining how to get local support (no default value). .lp RECOVER\ =\ A string explaining how to recover a game on this system diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index d71cbd330..a54d02b68 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -1,5 +1,5 @@ \documentstyle[titlepage,longtable]{article} -% NetHack 3.7 Guidebook.tex $NHDT-Date: 1711040379 2024/03/21 16:59:39 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.562 $ */ +% 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 @@ -47,8 +47,8 @@ %.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{September 13, 2024} +%DO NOT REMOVE NH_DATESUB \date{Date(%B %-d, %Y)} +\date{May 1, 2025} \maketitle @@ -3158,8 +3158,18 @@ magic spells on them). One of the most useful of these is the % {\it 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. +blessed, and how many uses it has left. +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 +it isn't shown. + +%.pg +Many scrolls produce a different effect from usual if they are blessed or +cursed, or read while the hero is confused. %.pg A mail daemon may run up and deliver mail to you as a % @@ -4135,7 +4145,8 @@ See {\it role\/} for a description of how to use negation to exclude choices. %.lp "" \\ -Default is random. +If {\tt 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. %.lp \item[\ib{autodescribe}] @@ -4390,7 +4401,8 @@ See {\it role\/} for a description of how to use negation to exclude choices. %.lp "" \\ -Default is random. +If {\tt 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. %.lp \item[\ib{goldX}] @@ -4566,8 +4578,40 @@ Key to go to the next menu page. Implemented by the Amiga, Gem and tty ports. Default `\verb+>+'. \item[\ib{menu\verb+_+objsyms}] -Show object symbols in menu headings in menus where -the object symbols act as menu accelerators (default off). +% [originally menu_objsyms was a boolean] +% Show object symbols in menu headings in menus where +% the object symbols act as menu accelerators (default off). +Inventory and other object menus are normally separated by object class +(weapons, armor, and so forth), with a menu header line at the beginning +of each group. +You can have menus add the display symbol for the class of objects for +each header line. +You can also add the display symbol for the individual item in each menu +entry. +For a tiles map, that would be a small rendition of an object's tile. +For a text map, it is the same character as is used for the object's +class, which would be most useful when there are no headers separating +objects among classes. +% FIXME! -- three column table may be simpler than mashing first two together +% Possible values are +% .PS "5\ -\ One-or-other" +% .PL "0\ -\ None" +% no symbols for either header lines or menu entries; +% .PL "1\ -\ Headers" +% show symbols on header lines but not entries; +% .PL "2\ -\ Entries" +% show symbols on menu entry lines but not headers; +% .PL "3\ -\ Both" +% show symbols on headers and entries; +% .PL "4\ -\ Conditional" +% only show symbols for entries if there are no headers; +% .PL "5\ -\ One-or-other" +% show symbols on headers, or on entries if no headers. +% .PE + +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}] Do not clear the screen before drawing menus, and align menus to the right edge of the screen. Only for the tty port. @@ -4666,6 +4710,12 @@ 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 automatically chosen. +%.lp +\\ +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. %.lp \item[\ib{news}] @@ -4948,7 +4998,9 @@ See {\it role\/} for a description of how to use negation to exclude choices. %.lp "" \\ -Default is random. +If {\tt 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. %.lp \item[\ib{rest\verb+_+on\verb+_+space}] @@ -4983,7 +5035,8 @@ option if they're all negations. %.\" negations. %.lp "" \\ -Default is random. +If {\tt role} is not specified, there is no default value; +player will be prompted. Cannot be set with the `{\tt O}' command. Persistent. %.lp \item[\ib{roguesymset}] @@ -5503,6 +5556,10 @@ Use bold black instead of blue for black glyphs (TTY only). \item[\ib{use\verb+_+inverse}] If {\it 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 +inventory. +%.lp \item[\ib{vary\verb+_+msgcount}] If {\it NetHack\/} can, it should display this number of messages at a time in the message window. @@ -6703,25 +6760,14 @@ The syntax is the same as WIZARDS. A list of users who are allowed to use the explore mode. The syntax is the same as WIZARDS. %.lp +\item[\ib{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}] Limit the maximum number of games that can be running at the same time. %.lp -\item[\ib{SAVEFORMAT}] -A list of up to two save file formats separated by space. -The first format in the list will written as well as read. The second format -will be read only if no save file in the first format exists. -Valid choices are ``{\tt historical}'' for binary writing of entire structs, -``{\tt lendian}'' for binary writing of each field in little-endian order, -``{\tt ascii}'' for writing the save file content in ascii text. -%.lp -\item[\ib{BONESFORMAT}] -A list of up to two bones file formats separated by space. -The first format in the list will written as well as read. The second -format will be read only if no bones files in the first format exist. -Valid choices are ``{\tt historical}'' for binary writing of entire structs, -``{\tt lendian}'' for binary writing of each field in little-endian order, -``{\tt ascii}'' for writing the bones file content in ascii text. -%.lp \item[\ib{SUPPORT}] A string explaining how to get local support (no default value). %.lp diff --git a/doc/Guidebook.txt b/doc/Guidebook.txt index 22a706fac..e36569992 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) - September 13, 2024 + May 1, 2025 @@ -81,8 +81,8 @@ much treasure as you can, retrieve the Amulet of Yendor, and escape the Mazes of Menace alive. - Your abilities and strengths for dealing with the hazards of ad- - venture will vary with your background and training: + Your abilities and strengths for dealing with the hazards of + adventure will vary with your background and training: Archeologists understand dungeons pretty well; this enables them to move quickly and sneak up on the local nasties. They start @@ -97,12 +97,13 @@ 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 be- - ing's state of health or sickness. Their medical practice earns them - quite reasonable amounts of money, with which they enter the dungeon. + 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 de- - votion to the ideals of chivalry and by the surpassing excellence of + Knights are distinguished from the common skirmisher by their + devotion to the ideals of chivalry and by the surpassing excellence of their armor. Monks are ascetics, who by rigorous practice of physical and men- @@ -111,22 +112,21 @@ 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 em- - ploy to great advantage. + 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -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 in- - stills in them stealth and cunning. + 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 op- - portunity. The armor and weapons fashioned by the Orcs are typically - of inferior quality. + 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -202,38 +202,38 @@ - that accept commands in pseudo-English sentences and explain the re- - sults 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 + 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 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 op- - tions were enabled when your executable was created. The three possi- - ble 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 + 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 characters to be substituted, but the default assignments use standard - ASCII characters to represent everything. There is no difference be- - tween 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 characters - from the monochrome character display when referring to things you - might see on the screen during your game. + 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 + 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -324,7 +324,7 @@ - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -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 at- - tributes. 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- + 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- 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -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 un- - friendly creatures. The lower this number is, the more effective - the armor; it is quite possible to have negative armor class. - See the Armor subsection of Objects for more information. + 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 + 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -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 ex- - plored 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -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,8 +579,8 @@ ` A boulder or statue or an engraving on the floor of a room. - Note: statues are displayed as if they were the monsters they de- - pict so won't appear as a grave accent (aka back-tick). + 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -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 ex- - tended 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- + 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- 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 `*' in- - dicating that you may choose an object not on the list, if you wanted - to use something unexpected. Typing a `*' lists your entire inven- - tory, so you can see the inventory letters of every object you're car- - rying. 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 com- - mand. + 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 + command. - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -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 ex- - ample above would be typed "n10s" instead. Commands for which counts - make no sense ignore them. In addition, movement commands can be pre- - fixed for greater control (see below). To cancel a count or a prefix, - press the ESC key. + 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 an- - other location to examine; `:' will show additional info, if any, - without asking for confirmation. When picking a location, press- - ing the ESC key will terminate this command, or pressing `?' will - give a brief reminder about how it works. + 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 in- - cludes map coordinates. + 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 in- - stead. Only these one-step movement commands cause you to fight + 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -730,7 +730,7 @@ - monsters; the others (below) are "safe." + fight monsters; the others (below) are "safe." +----------------------------------------------------------------+ | y k u 7 8 9 | | \ | / \ | / | @@ -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 of- - fer 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -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 im- - plicitly 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -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 in- - ventory 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,14 +903,14 @@ 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 in- - stead of just y. + 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -928,29 +928,29 @@ - Engraving the word "Elbereth" will cause most monsters to not at- - tack you hand-to-hand (but if you attack, you will rub it out); + 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 in- - stead of filling the quiver. This will also automatically use a - polearm if wielded. If fireassist is true, firing will automati- - cally 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 + 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 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, de- - pending 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. Op- - tions 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -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 ac- - cessories as the inventory choices but will accept an item of ar- - mor and attempt to wear it.) + 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 ex- - change still takes place. + 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -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 in- - ventory 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 in- - ventory 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 + 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 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -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 De- - velopment 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 @@ -1373,14 +1373,14 @@ 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -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,24 +1410,24 @@ 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. De- - fault key is `M-e'. + 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- - noid_confirmation:quit option to require a response of yes in- - stead. + 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. #fight Prefix key to force fight a direction, even if you see nothing to @@ -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 ex- - tinct. + 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -1457,14 +1457,14 @@ 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 ig- - nored for #genocided and alphabetical will be used instead. The - menu omits those two choices when used for #genocide. + 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,16 +1473,16 @@ 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 @@ -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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -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 re- - moving 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -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,11 +1622,11 @@ 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. De- - fault key is `|'. + 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 Pick up things at the current location. Default key is `,'. The @@ -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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -1654,8 +1654,8 @@ - trouble, there is an option to make you confirm your intent be- - fore 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. De- - fault response is n (no); continue playing. To really quit, re- - spond with y. You can set the paranoid_confirmation:quit option - to require a response of yes instead. + 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. De- - fault key is `M-R'. + Ride (or stop riding) a saddled creature. Autocompletes. + Default key is `M-R'. - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -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, ac- - cess for specific users can be controlled by the system configu- - ration file. Use the shell command `exit' to return to the game. - Default key is `!'. + 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 + 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -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 + 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 - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -1924,17 +1924,17 @@ 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,21 +1943,21 @@ #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 op- - tion so is remembered for subsequent #vanquished requests. The + 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'. @@ -1965,16 +1965,16 @@ #version Print compile time options for this version of NetHack. - The second paragraph lists the user interface(s) that are in- - cluded. If there are more than one, you can use the windowtype - option in your run-time configuration file to select the one you + 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -1985,15 +1985,15 @@ #versionshort - Show the program's version number, plus the date and time that - the running copy was built from sources (not the version's re- - lease 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 +2003,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,19 +2013,19 @@ Wipe off your face. Autocompletes. Default key is `M-w'. #wizborn - Show monster birth, death, genocide, and extinct statistics. De- - bug mode only. + 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 + 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 @@ -2033,14 +2033,14 @@ one. Autocompletes. Debug mode only. Default key is `^G'. #wizidentify - Identify all items in inventory. Autocompletes. Debug mode + Identify all items in inventory. Autocompletes. Debug mode only. Default key is `^I'. #wizintrinsic Set one or more intrinsic attributes. Autocompletes. Debug mode - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -2053,41 +2053,41 @@ 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. De- - fault key is `^V'. + 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 + Wish for something. Autocompletes. Debug mode only. Default key is `^W'. #wmode @@ -2101,12 +2101,12 @@ - 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 + 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -2119,17 +2119,17 @@ first letter of the command. On Windows and 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 Escape followed by the other key, - you may set the altmeta option to have NetHack combine them into - meta+. (This combining action only takes place when NetHack is + On other systems, if typing "Alt" plus another key transmits a two + character sequence consisting of an Escape followed by the other key, + you may set the altmeta option to have NetHack combine them into + meta+. (This combining action only takes place when NetHack is expecting a command to execute, not when accepting input to name some- thing or to make a wish.) Unlike control characters, where ^x and ^X denote the same thing, - meta characters are case-sensitive: M-x and 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 + meta characters are case-sensitive: M-x and 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 meta+Shift+ is needed. @@ -2172,7 +2172,7 @@ - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -2238,7 +2238,7 @@ - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -2252,7 +2252,7 @@ - If the number_pad option is on, some additional letter commands + If the number_pad option is on, some additional letter commands are available: h #help @@ -2304,7 +2304,7 @@ - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -2316,61 +2316,61 @@ 5. Rooms and corridors - Rooms and corridors in the dungeon are either lit or dark. Any - lit areas within your line of sight will be displayed; dark areas are - only displayed if they are within one space of you. Walls and corri- + Rooms and corridors in the dungeon are either lit or dark. Any + lit areas within your line of sight will be displayed; dark areas are + only displayed if they are within one space of you. Walls and corri- dors remain on the map as you explore them. Secret corridors are hidden and appear to be solid rock. You can find them with the `s' (search) command when adjacent to them. Multi- - ple search attempts may be needed. When searching is successful, se- - cret corridors become ordinary open corridor locations. Mapping magic - reveals secret corridors, so converts them into ordinary corridors and - shows them as such. + ple search attempts may be needed. When searching is successful, + secret corridors become ordinary open corridor locations. Mapping + magic reveals secret corridors, so converts them into ordinary corri- + dors and shows them as such. 5.1. Doorways - 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 `o' - (open) command; to close it again, use the `c' (close) command. By - default the autoopen option is enabled, so simply attempting to walk - onto a closed door's location will attempt to open it without needing - `o'. Opening via autoopen will not work if you are confused or + 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 `o' + (open) command; to close it again, use the `c' (close) command. By + default the autoopen option is enabled, so simply attempting to walk + onto a closed door's location will attempt to open it without needing + `o'. Opening via autoopen will not work if you are confused or stunned or suffer from the fumbling attribute. - Open doors cannot be entered diagonally; you must approach them - straight on, horizontally or vertically. Doorways without doors are - not restricted in this fashion except on one particular level (de- - scribed by "#overview" as "a primitive area"). + Open doors cannot be entered diagonally; you must approach them + straight on, horizontally or vertically. Doorways without doors are + not restricted in this fashion except on one particular level + (described by "#overview" as "a primitive area"). - Unlocking magic exists but usually won't be available early on. - You can get through a locked door without magic by first using an un- - locking tool with the `a' (apply) command, and then opening it. By - default the autounlock option is also enabled, so if you attempt to - open (via `o' or autoopen) a locked door while carrying an unlocking - tool, you'll be asked whether to use it on the door's lock. Alterna- - tively, you can break a closed door (whether locked or not) down by - kicking it via the `^D' (kick) command. Kicking down a door destroys + 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 `a' (apply) command, and then opening it. By + default the autounlock option is also enabled, so if you attempt to + open (via `o' or autoopen) a locked door while carrying an unlocking + tool, you'll be asked whether to use it on the door's lock. Alterna- + tively, you can break a closed door (whether locked or not) down by + kicking it via the `^D' (kick) command. Kicking down a door destroys it and makes a lot of noise which might wake sleeping monsters. - Some closed doors are booby-trapped and will explode if an at- - tempt 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 "#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 failure will set off the trap's explosion. - (If you decline, you effectively forget that a trap was found there.) + 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 "#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 failure will set off the trap's + explosion. (If you decline, you effectively forget that a trap was + found there.) Closed doors can be useful for shutting out monsters. Most mon- - sters cannot open closed doors, although a few don't need to (for ex- - ample, ghosts can walk through doors and fog clouds can flow under + sters cannot open closed doors, although a few don't need to (for + example, ghosts can walk through doors and fog clouds can flow under them). Some monsters who can open doors can also use unlocking tools. - And some (giants) can smash doors. - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -2380,63 +2380,63 @@ - Secret doors are hidden and appear to be ordinary wall (from in- - side a room) or solid rock (from outside). You can find them with the - `s' (search) command but it might take multiple tries (possibly many - tries if your luck is poor). Once found they are in all ways equiva- - lent to normal doors. Mapping magic does not reveal secret doors. + And some (giants) can smash doors. + + 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 `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. 5.2. Traps (`^') - There are traps throughout the dungeon to snare the unwary in- - truder. For example, you may suddenly fall into a pit and be stuck + There are traps throughout the dungeon to snare the unwary + intruder. 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 + appear on your map until you trigger it by moving onto it, you see someone else trigger it, or you discover it with the `s' (search) com- - mand (multiple attempts are often needed; if your luck is poor, many - attempts might be needed). Wands of secret door detection and spell - of detect unseen also reveal traps within a modest radius but only if + mand (multiple attempts are often needed; if your luck is poor, many + attempts might be needed). Wands of secret door detection and spell + of 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. - Monsters can fall prey to traps, too, which can potentially be - used as a defensive strategy. Unfortunately traps can be harmful to - your pet(s) as well. Monsters, including pets, usually will avoid + Monsters can fall prey to traps, too, which can potentially be + used as a defensive strategy. Unfortunately traps can be harmful to + your pet(s) as well. Monsters, including pets, usually will avoid moving onto a trap which is shown on your map if they have encountered that type of trap before. - Some traps such as pits, bear traps, and webs hold you in one - place. You can escape by simply trying to move to an adjacent spot + Some traps such as pits, bear traps, and webs hold you in one + place. You can escape by simply trying to move to an adjacent spot and repeat as needed; eventually you will get free. - Other traps can send you to different locations. Teleporters - send you elsewhere on the same dungeon level. Level teleporters send - you to a random dungeon level, the destination chosen from a few lev- - els lower all the way to the top. These traps choose a new destina- - tion each time they're activated. Trap doors and holes also send you - to another level, but one which is always below the current level. - Usually that will be the next level down but it can be farther. Un- - like (level) teleporters, the destination level of a particular trap - door or hole is persistent, so falling into one will bring you to the - same level each time--though not necessarily the same spot on the + Other traps can send you to different locations. Teleporters + send you elsewhere on the same dungeon level. Level teleporters send + you to a random dungeon level, the destination chosen from a few lev- + els lower all the way to the top. These traps choose a new destina- + tion each time they're activated. Trap doors and holes also send you + to another level, but one which is always below the current level. + Usually that will be the next level down but it can be farther. + Unlike (level) teleporters, the destination level of a particular trap + door or hole is persistent, so falling into one will bring you to the + same level each time--though not necessarily the same spot on the level. Magic portals behave similarly, but with some additional vari- - ation. Some portals are two-way and their remote destination is al- - ways the same: another portal which can take you back. Others are + ation. Some portals are two-way and their remote destination is + always the same: another portal which can take you back. Others are one-way and send you to a specific destination level but not necessar- ily to a specific location there. - There is a special multi-level branch of the dungeon with pre- - mapped levels based on the classic computer game "Sokoban." In that - game, you operate as a warehouse worker who pushes crates around ob- - stacles to position them at designated locations. In NetHack, the + There is a special multi-level branch of the dungeon with pre- + mapped levels based on the classic computer game "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 traps have all - been nullified, giving access to whatever is beyond them. In the - Sokoban game, you can only move in the four cardinal compass direc- - tions, and a crate in its final destination blocks further access to - that spot. In the Sokoban levels of NetHack, you can move diagonally + been nullified, giving access to whatever is beyond them. In the - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -2446,63 +2446,63 @@ - (unless that would let you pass between two neighboring boulders) but - you can only push boulders in the four cardinal directions, and a - boulder which fills a pit or hole removes both the boulder and the - trap so opens up normal access to that spot. With careful foresight, - it is possible to complete all of the levels according to the tradi- - tional 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 - 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 scroll of earth. However, doing such things will lower your luck - without any specific message given about that. See the Conduct sec- - tion for information about getting feedback for your actions in + Sokoban game, you can only move in the four cardinal compass direc- + tions, and a crate in its final destination blocks further access to + that spot. In the Sokoban levels of NetHack, you can move diagonally + (unless that would let you pass between two neighboring boulders) but + you can only push boulders in the four cardinal directions, and a + boulder which fills a pit or hole removes both the boulder and the + trap so opens up normal access to that spot. With careful foresight, + it is possible to complete all of the levels according to the tradi- + tional 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 + 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 scroll of earth. However, doing such things will lower your luck + without any specific message given about that. See the Conduct sec- + tion for information about getting feedback for your actions in Sokoban. 5.3. Stairs and ladders (`<', `>') In general, each level in the dungeon will have a staircase going - up (`<') to the previous level and another going down (`>') 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 continuing into the dungeon and the other branching into an area + up (`<') to the previous level and another going down (`>') 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 continuing into the dungeon and the other branching into an area known as the Gnomish Mines. Those mines eventually hit a dead end, so - after exploring them (if you choose to do so), you'll need to climb + after exploring them (if you choose to do so), you'll need to climb back up to the main dungeon. - When you traverse a set of stairs, or trigger a trap which sends + When you traverse a set of stairs, or trigger a trap which sends you to another level, the level you're leaving will be deactivated and - stored in a file on disk. If you're moving to a previously visited - level, it will be loaded from its file on disk and reactivated. If - you're moving to a level which has not yet been visited, it will be + stored in a file on disk. If you're moving to a previously visited + level, it will be loaded from its file on disk and reactivated. If + you're moving to a level which has not yet been visited, it will be created (from scratch for most random levels, from a template for some - "special" levels, or loaded from the remains of an earlier game for a - "bones" level as briefly described below). Monsters are only active - on the current level; those on other levels are essentially placed + "special" levels, or loaded from the remains of an earlier game for a + "bones" level as briefly described below). Monsters are only active + on the current level; those on other levels are essentially placed into stasis. Ordinarily when you climb a set of stairs, you will arrive on the - corresponding staircase at your destination. However, pets (see be- - low) and some other monsters will follow along if they're close enough - when you travel up or down stairs, and occasionally one of these crea- - tures will displace you during the climb. When that occurs, the pet - or other monster will arrive on the staircase and you will end up - nearby. + corresponding staircase at your destination. However, pets (see + below) and some other monsters will follow along if they're close + enough when you travel up or down stairs, and occasionally one of + these creatures will displace you during the climb. When that occurs, + the pet or other monster will arrive on the staircase and you will end + up nearby. - Ladders serve the same purpose as staircases, and the two types - of inter-level connections are nearly indistinguishable during game + Ladders serve the same purpose as staircases, and the two types + of inter-level connections are nearly indistinguishable during game play. - - - - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -2514,36 +2514,36 @@ 5.4. Shops and shopping - 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 + 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 `p' command. You can inquire about - the price of an item prior to picking it up by using the "#chat" com- - mand 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 + the price of an item prior to picking it up by using the "#chat" com- + mand 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. - You can sell items to a shopkeeper by dropping them to the floor + You can sell items to a shopkeeper by dropping them to the floor while inside a shop. You will either be offered an amount of gold and asked whether you're willing to sell, or you'll be told that the shop- - keeper isn't interested (generally, your item needs to be compatible + keeper isn't interested (generally, your item needs to be compatible with the type of merchandise carried by the shop). - If you drop something in a shop by accident, the shopkeeper will - usually claim ownership without offering any compensation. You'll + If you drop something in a shop by accident, the shopkeeper will + usually claim ownership without offering any compensation. You'll have to buy it back if you want to reclaim it. Shopkeepers sometime run out of money. When that happens, you'll - be offered credit instead of gold when you try to sell something. - Credit can be used to pay for purchases, but it is only good in the + be offered credit instead of gold when you try to sell something. + Credit can be used to pay for purchases, but it is only good in the shop where it was obtained; other shopkeepers won't honor it. (If you - happen to find a "credit card" in the dungeon, don't bother trying to + happen to find a "credit card" in the dungeon, don't bother trying to use it in shops; shopkeepers will not accept it.) - The `$' command, which reports the amount of gold you are carry- - ing, will also show current shop debt or credit, if any. The "Iu" - command lists unpaid items (those which still belong to the shop) if - you are carrying any. The "Ix" command shows an inventory-like dis- - play of any unpaid items which have been used up, along with other + The `$' command, which reports the amount of gold you are carry- + ing, will also show current shop debt or credit, if any. The "Iu" + command lists unpaid items (those which still belong to the shop) if + you are carrying any. The "Ix" command shows an inventory-like dis- + play of any unpaid items which have been used up, along with other shop fees, if any. 5.4.1. Shop idiosyncrasies @@ -2552,23 +2552,23 @@ * The price of a given item can vary due to a variety of factors. - * A shopkeeper treats the spot immediately inside the door as if it + * A shopkeeper treats the spot immediately inside the door as if it were outside the shop. - * While the shopkeeper watches you like a hawk, he or she will gener- + * While the shopkeeper watches you like a hawk, he or she will gener- ally ignore any other customers. - * If a shop is "closed for inventory," it will not open of its own ac- - cord. + * If a shop is "closed for inventory," it will not open of its own + accord. - * Shops do not get restocked with new items, regardless of inventory + * Shops do not get restocked with new items, regardless of inventory depletion. - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -2580,61 +2580,61 @@ 5.5. Movement feedback - Moving around the map usually provides no feedback--other than - drawing the hero at the new location--unless you step on an object or - pile of objects, or on a trap, or attempt to move onto a spot where a - monster is located. There are several options which can be used to + Moving around the map usually provides no feedback--other than + drawing the hero at the new location--unless you step on an object or + pile of objects, or on a trap, or attempt to move onto a spot where a + monster is located. There are several options which can be used to augment the normal feedback. - The pile_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 5. Setting it - to 1 would always give that message instead of listing any objects. - Setting it to 0 is a special case which will always list all objects + The pile_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 5. Setting it + to 1 would always give that message instead of listing any objects. + Setting it to 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 quan- - tities of those stacks (so 7 arrows or 25 gold pieces will each count - as 1 rather than as 7 and 25, respectively, and total to 2 when both + tities of those stacks (so 7 arrows or 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). - The "nopickup" command prefix (default `m') can be used before a - movement direction to step on objects without attempting auto-pickup + The "nopickup" command prefix (default `m') can be used before a + movement direction to step on objects without attempting auto-pickup and without giving feedback about them. The mention_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, then the wall that is being bumped into will be drawn on the - map). This option also gives feedback when rushing or running stops + 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, then the wall that is being bumped into will be drawn on the + map). This option also gives feedback when rushing or running stops for some non-obvious reason. - The mention_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 so is obscured on the map. Setting - this option to true will describe such things even when they aren't - obscured. Doorless doorways and open doors aren't considered worthy + The mention_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 so is obscured on the map. Setting + this option to true will describe such things even when they aren't + obscured. Doorless doorways and open doors aren't considered worthy of mention; closed doors (if you can move onto their spots) and broken - doors are. Assuming that you're able to do so, moving onto water or - lava or ice will give feedback if not yet on that type of terrain but - not repeat it (unless there has been some intervening message) when - moving from water to another water spot, or lava to lava, or ice to - ice. Moving off of any of those back onto "normal" terrain will give - one message too, unless there is feedback about one or more objects, + doors are. Assuming that you're able to do so, moving onto water or + lava or ice will give feedback if not yet on that type of terrain but + not repeat it (unless there has been some intervening message) when + moving from water to another water spot, or lava to lava, or ice to + ice. Moving off of any of those back onto "normal" terrain will give + one message too, unless there is feedback about one or more objects, in which case the back on land circumstance is implied. - The confirm and safe_pet options control what happens when you + The confirm and safe_pet options control what happens when you try to move onto a peaceful monster's spot or a tame one's spot. - The "nopickup" command prefix (default `m') is also the move- + The "nopickup" command prefix (default `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 - Fighting subsection of Monsters below). The "fight" command prefix - (default `F'; also `-' if number_pad is on) can be used to force an - attack, when guessing where an unseen monster is or when deliberately + monster's spot without the move being considered an attack (see the + Fighting subsection of Monsters below). The "fight" command prefix + (default `F'; also `-' if number_pad is on) can be used to force an + attack, when guessing where an unseen monster is or when deliberately - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -2646,61 +2646,61 @@ attacking a peaceful or tame creature. - The run_mode option controls how frequently the map gets redrawn - when moving more than one step in a single command (so when rushing, + The run_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). 5.6. Rogue level - One dungeon level (occurring in mid to late teens of the main + One dungeon level (occurring in mid to late teens of the main dungeon) is a tribute to the ancestor game hack's inspiration rogue. - It is usually displayed differently from other levels: possibly - in characters instead of tiles, or without line-drawing symbols if al- - ready in characters; also, gold is shown as * rather than $ and stairs - are shown as % rather than < and >. 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 ra- - dius around your character. And monsters represented by lower-case - letters aren't randomly generated on the rogue level. + 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 * rather than $ and + stairs are shown as % rather than < and >. There are some minor dif- + ferences 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. And monsters represented by + lower-case letters aren't randomly generated on the rogue level. The slight strangeness of this level is a feature, not a bug.... 6. Monsters Monsters you cannot see are not displayed on the screen. Beware! - You may suddenly come upon one in a dark place. Some magic items can - help you locate them before they locate you (which some monsters can + You may suddenly come upon one in a dark place. Some magic items can + help you locate them before they locate you (which some monsters can do very well). - The commands `/' and `;' may be used to obtain information about - those monsters who are displayed on the screen. The command "#name" - (by default bound to `C'), allows you to assign a name to a monster, + The commands `/' and `;' may be used to obtain information about + those monsters who are displayed on the screen. The command "#name" + (by default bound to `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 re- - move any prior name. + monsters are present. Assigning a name which is just a space will + remove any prior name. - The extended command "#chat" can be used to interact with an ad- - jacent 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. + The extended command "#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. 6.1. Fighting If you see a monster and you wish to fight it, just attempt to - walk into it. Many monsters you find will mind their own business un- - less you attack them. Some of them are very dangerous when angered. + walk into it. Many monsters you find will mind their own business + unless you attack them. Some of them are very dangerous when angered. Remember: discretion is the better part of valor. In most circumstances, if you attempt to attack a peaceful mon- - ster by moving into its location, you'll be asked to confirm your in- - tent. By default an answer of `y' acknowledges that intent, which can - be error prone if you're using `y' to move. You can set the para- - noid_confirmation:attack option to require a response of "yes" in- - stead. + ster by moving into its location, you'll be asked to confirm your + intent. By default an answer of `y' acknowledges that intent, which + can be error prone if you're using `y' to move. You can set the para- + noid_confirmation:attack option to require a response of "yes" - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -2710,6 +2710,8 @@ + instead. + If you can't see a monster (if it is invisible, or if you are blinded), the symbol `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 @@ -2747,8 +2749,8 @@ Some types of creatures in the dungeon can actually be ridden if you 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 al- - liance. Once you do have the beast under your control however, you + 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 "#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 @@ -2761,12 +2763,10 @@ to attempt to put that saddle on an adjacent creature. If successful, it will be transferred to that creature's inventory. - Use the "#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. - NetHack 3.7.0 September 13, 2024 + + NetHack 3.7.0 May 1, 2025 @@ -2776,6 +2776,10 @@ + Use the "#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. + 6.4. Bones levels You may encounter the shades and corpses of other adventurers (or @@ -2799,8 +2803,8 @@ a special "remembered, unseen monster" marker will be displayed at the location where you think it is. That will persist until you have proven that there is no monster there, even if the unseen monster - moves to another location or you move to a spot where the marker's lo- - cation ordinarily wouldn't be seen any more. + moves to another location or you move to a spot where the marker's + location ordinarily wouldn't be seen any more. 7. Objects @@ -2826,13 +2830,9 @@ NetHack will tell you how badly you have loaded yourself. If you are encumbered, one of the conditions Burdened, Stressed, Strained, - Overtaxed, or Overloaded will be shown on the bottom line status dis- - play. - - - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -2842,63 +2842,63 @@ + Overtaxed, or Overloaded will be shown on the bottom line status dis- + play. + 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 NetHack asks you to choose a particular - object you are carrying, you are usually presented with a list of in- - ventory letters to choose from (see Commands, above). + object you are carrying, you are usually presented with a list of + inventory letters to choose from (see Commands, above). Some objects, such as weapons, are easily differentiated. Oth- - ers, like scrolls and potions, are given descriptions which vary ac- - cording to type. During a game, any two objects with the same de- - scription are the same type. However, the descriptions will vary from - game to game. + ers, like scrolls and potions, are given descriptions which vary + according to type. During a game, any two objects with the same + description are the same type. However, the descriptions will vary + from game to game. When you use one of these objects, if its effect is obvious, - NetHack will remember what it is for you. If its effect isn't ex- - tremely 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 "#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 "#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. + 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 + "#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 + "#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. 7.1. Curses and Blessings - Any object that you find may be cursed, even if the object is - otherwise helpful. The most common effect of a curse is being stuck - with (and to) the item. Cursed weapons weld themselves to your hand + Any object that you find may be cursed, even if the object is + otherwise helpful. The most common effect of a curse is being stuck + with (and to) the item. Cursed weapons weld themselves to your hand when wielded, so you cannot unwield them. Any cursed item you wear is - not removable by ordinary means. In addition, cursed arms and armor - usually, but not always, bear negative enchantments that make them - less effective in combat. Other cursed objects may act poorly or + not removable by ordinary means. In addition, cursed arms and armor + usually, but not always, bear negative enchantments that make them + less effective in combat. Other cursed objects may act poorly or detrimentally in other ways. - Objects can also be blessed instead. Blessed items usually work - better or more beneficially than normal uncursed items. For example, + Objects can also be blessed instead. Blessed items usually work + better or more beneficially than normal uncursed items. For example, a blessed weapon will do slightly more damage against demons. - Objects which are neither cursed nor blessed are referred to as + Objects which are neither cursed nor blessed are referred to as uncursed. They could just as easily have been described as unblessed, - but the uncursed designation is what you will see within the game. A + but the uncursed designation is what you will see within the game. A "glass half full versus glass half empty" situation; make of that what you will. - There are magical means of bestowing or removing curses upon ob- - jects, so even if you are stuck with one, you can still have the curse - lifted and the item removed. Priests and Priestesses have an innate - sensitivity to this property in any object, so they can more easily - avoid cursed objects than other character roles. Dropping objects - onto an altar will reveal their bless or curse state provided that you - can see them land. - - An item with unknown status will be reported in your inventory - with no prefix. An item which you know the state of will be distin- - guished in your inventory by the presence of the word cursed, un- - cursed, or blessed in the description of the item. In some cases + There are magical means of bestowing or removing curses upon + objects, so even if you are stuck with one, you can still have the + curse lifted and the item removed. Priests and Priestesses have an + innate sensitivity to this property in any object, so they can more + easily avoid cursed objects than other character roles. Dropping + objects onto an altar will reveal their bless or curse state provided + that you can see them land. - NetHack 3.7.0 September 13, 2024 + + NetHack 3.7.0 May 1, 2025 @@ -2908,63 +2908,63 @@ + An item with unknown status will be reported in your inventory + with no prefix. An item which you know the state of will be distin- + guished in your inventory by the presence of the word cursed, + uncursed, or blessed in the description of the item. In some cases uncursed will be omitted as being redundant when enough other informa- - tion is displayed. The implicit_uncursed option can be used to con- - trol this; toggle it off to have uncursed be displayed even when that + tion is displayed. The implicit_uncursed option can be used to con- + trol this; toggle it off to have uncursed be displayed even when that can be deduced from other attributes. - Sometimes the bless or curse state of objects is referred to as - their "BUC" attribute, for Blessed, Uncursed, or Cursed state, or + Sometimes the bless or curse state of objects is referred to as + their "BUC" attribute, for Blessed, Uncursed, or Cursed state, or "BUCX" for Blessed, Uncursed, Cursed, or unknown. (The term beatitude is occasionally used as well.) 7.2. 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 - them first). Without a weapon, you do only 1-2 hit points of damage - (plus bonuses, if any). Monk characters are an exception; they nor- - mally do more damage with bare (or gloved) hands than they do with + Given a chance, most monsters in the Mazes of Menace will gratu- + itously try to kill you. You need weapons for self-defense (killing + them first). Without a weapon, you do only 1-2 hit points of damage + (plus bonuses, if any). Monk characters are an exception; they nor- + mally do more damage with bare (or gloved) hands than they do with weapons. - There are wielded weapons, like maces and swords, and thrown - weapons, like arrows and spears. To hit monsters with a weapon, you - must wield it and attack them, or throw it at them. You can simply - elect to throw a spear. To shoot an arrow, you should first wield a - bow, then throw the arrow. Crossbows shoot crossbow bolts. Slings + There are wielded weapons, like maces and swords, and thrown + weapons, like arrows and spears. To hit monsters with a weapon, you + must wield it and attack them, or throw it at them. You can simply + elect to throw a spear. To shoot an arrow, you should first wield a + bow, then throw the arrow. Crossbows shoot crossbow bolts. Slings hurl rocks and (other) stones (like gems). - Enchanted weapons have a "plus" (or "to hit enhancement" which - can be either positive or negative) that adds to your chance to hit - and the damage you do to a monster. The only way to determine a + Enchanted weapons have a "plus" (or "to hit enhancement" which + can be either positive or negative) that adds to your chance to hit + and the damage you do to a monster. The only way to determine a weapon's enchantment is to have it magically identified somehow. Most - weapons are subject to some type of damage like rust. Such "erosion" + weapons are subject to some type of damage like rust. Such "erosion" damage can be repaired. - The chance that an attack will successfully hit a monster, and - the amount of damage such a hit will do, depends upon many factors. - Among them are: type of weapon, quality of weapon (enchantment and/or + The chance that an attack will successfully hit a monster, and + the amount of damage such a hit will do, depends upon many factors. + Among them are: type of weapon, quality of weapon (enchantment and/or erosion), experience level, strength, dexterity, encumbrance, and pro- - ficiency (see below). The monster's armor class--a general defense - rating, not necessarily due to wearing of armor--is a factor too; - also, some monsters are particularly vulnerable to certain types of + ficiency (see below). The monster's armor class--a general defense + rating, not necessarily due to wearing of armor--is a factor too; + also, some monsters are particularly 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 `x' command, which ex- - changes 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 - `X' command to engage or disengage that. Only some types of charac- - 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 - time. + ready to use by setting things up with the `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 + `X' command to engage or disengage that. Only some types of charac- - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -2974,63 +2974,63 @@ - There might be times when you'd rather not wield any weapon at + 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 + time. + + There might be times when you'd rather not wield any weapon at all. To accomplish that, wield `-', or else use the `A' command which - allows you to unwield the current weapon in addition to taking off + allows you to unwield the current weapon in addition to taking off other worn items. - Those of you in the audience who are AD&D players, be aware that + 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 mon- - sters in NetHack. Some of the more obscure weapons (such as the + 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 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" + 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 - You can throw just about anything via the `t' command. It will - prompt for the item to throw; picking `?' will list things in your in- - ventory which are considered likely to be thrown, or picking `*' 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 ob- - ject and your strength. Arrows can be thrown by hand, but can be - thrown much farther and will be more likely to hit when thrown while + You can throw just about anything via the `t' command. It will + prompt for the item to throw; picking `?' will list things in your + inventory which are considered likely to be thrown, or picking `*' + will list your entire inventory. After you've chosen what to throw, + you will be prompted for a direction rather than for a specific tar- + get. The distance something can be thrown depends mainly on the type + of object and your strength. Arrows can be thrown by hand, but can be + thrown much farther and will be more likely to hit when thrown while you are wielding a bow. - 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 Mjoll- - nir; when thrown by a Valkyrie it will return too. However, aklyses - and Mjollnir occasionally fail to return. Returning thrown objects - occasionally fail to be caught, sometimes even hitting the thrower, + 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 Mjoll- + nir; when thrown by a Valkyrie it will return too. However, aklyses + and 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. - You can simplify the throwing operation by using the `Q' command - to select your preferred "missile", then using the `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 `f'. There is - also an option, autoquiver, which has NetHack choose another item to - automatically fill your quiver (or quiver sack, or have at the ready) - when the inventory slot used for `Q' runs out. If your quiver is - empty, autoquiver is false, and you are wielding a weapon which re- - turns when thrown, you will throw that weapon instead of filling the - quiver. The fire command also has extra assistance, if fireassist is + You can simplify the throwing operation by using the `Q' command + to select your preferred "missile", then using the `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 `f'. There is + also an option, autoquiver, which has NetHack choose another item to + automatically fill your quiver (or quiver sack, or have at the ready) + when the inventory slot used for `Q' runs out. If your quiver is + empty, 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 fireassist is on it will try to wield a launcher matching the ammo in the quiver. - 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 - in your hand--and still hit a target is not an easy task. Rangers are - among those who are adept at this task, as are those with a high level - of proficiency in the relevant weapon skill (in bow skill if you're - NetHack 3.7.0 September 13, 2024 + + NetHack 3.7.0 May 1, 2025 @@ -3040,63 +3040,63 @@ + 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 + in your hand--and still hit a target is not an easy task. Rangers are + among those who are adept at this task, as are those with a high level + of proficiency in the relevant weapon skill (in bow skill if you're wielding one to 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 `t' or `f' command. For + 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 `t' or `f' command. For example, "2f" (or "n2f" if using number_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 ("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, + 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 + 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 - You will have varying degrees of skill in the weapons available. + You will have varying degrees of skill in the weapons available. Weapon proficiency, or weapon skills, affect how well you can use par- ticular types of weapons, and you'll be able to improve your skills as - you progress through a game, depending on your role, your experience + you progress through a game, depending on your role, your experience level, and use of the weapons. - For the purposes of proficiency, weapons have been divided up - into various groups such as daggers, broadswords, and polearms. Each - role has a limit on what level of proficiency a character can achieve - for each group. For instance, wizards can become highly skilled in + For the purposes of proficiency, weapons have been divided up + into various groups such as daggers, broadswords, and polearms. Each + role has a limit on what level of proficiency a character can achieve + for each group. For instance, wizards can become highly skilled in daggers or staves but not in swords or bows. The "#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 "#enhance". (Divine intervention might - unrestrict a particular skill, in which case it will start at un- - skilled and be limited to basic.) Some characters can enhance their - barehanded combat or martial arts skill beyond expert to "master" or + 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 "#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 beyond expert to "master" or "grand master". - Use of a weapon in which you're restricted or unskilled will in- - cur a modest penalty in the chance to hit a monster and also in the - amount of damage done when you do hit; at basic level, there is no - penalty or bonus; at skilled level, you receive a modest bonus in the + Use of a weapon in which you're restricted or unskilled will + incur a modest penalty in the chance to hit a monster and also in the + amount of damage done when you do hit; at basic level, there is no + penalty or bonus; at skilled level, you receive a modest bonus in the chance to hit and amount of damage done; at expert level, the bonus is - higher. A successful hit has a chance to boost your training towards + 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 "#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. + you'll be told that you feel more confident in your skills. At that - - - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -3106,63 +3106,63 @@ + 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 - 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 "#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 one is considered secondary. + 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 "#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 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 - wielded weapon and the secondary is just an item in your inventory + for that matter--wielding two weapons at once. The primary is your + wielded weapon and the secondary is just an item in your inventory that's been designated as alternate weapon.) - If your primary weapon is wielded but your off hand is empty or - has the wrong weapon, use the sequence `x', `w', `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 `w' to wield the primary. Lastly, if neither hand holds + If your primary weapon is wielded but your off hand is empty or + has the wrong weapon, use the sequence `x', `w', `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 `w' to wield the primary. Lastly, if neither hand holds the correct weapon, use `w', `x', `w' to first wield the intended sec- ondary, swap it to off hand, and then wield the primary. - The whole process can be simplified via use of the pushweapon op- - tion. When it is enabled, then using `w' to wield something causes - the currently wielded weapon to become your alternate weapon. So the - sequence `w', `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 + The whole process can be simplified via use of the pushweapon + option. When it is enabled, then using `w' to wield something causes + the currently wielded weapon to become your alternate weapon. So the + sequence `w', `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. - When in two-weapon combat mode, using the `X' command toggles - back to single-weapon mode. Throwing or dropping either of the weap- - ons or having one of them be stolen or destroyed will also make you + When in two-weapon combat mode, using the `X' command toggles + back to single-weapon mode. Throwing or dropping either of the weap- + ons or having one of them be stolen or destroyed will also make you revert to single-weapon combat. 7.3. Armor (`[') - Lots of unfriendly things lurk about; you need armor to protect - yourself from their blows. Some types of armor offer better protec- - tion 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 ar- - mor which exists in AD&D gives the same protection in NetHack. + Lots of unfriendly things lurk about; you need armor to protect + yourself from their blows. Some types of armor offer better protec- + tion 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 NetHack. - Here is a list of the armor class values provided by suits of ar- - mor: + Here is a list of the armor class values provided by suits of + armor: Dragon scale mail 1 Plate mail, Crystal plate mail 3 Bronze plate mail, Splint mail, - Banded mail, Dwarvish mithril-coat 4 - Chain mail, Elven mithril-coat 5 - Scale mail, Orcish chain mail 6 - Ring mail, Studded leather armor, - Dragon scales 7 - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -3172,63 +3172,63 @@ + Banded mail, Dwarvish mithril-coat 4 + Chain mail, Elven mithril-coat 5 + Scale mail, Orcish chain mail 6 + Ring mail, Studded leather armor, + Dragon scales 7 Leather armor, Orcish ring mail 8 Leather jacket 9 none 10 - You can also wear other pieces of armor (cloak over suit, shirt - under suit, helmet, gloves, boots, shield) to lower your armor class + 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. Most of these provide a one or two point improvement to - AC (making the overall value smaller and eventually negative) but can - 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- - rosion to suits worn under them and against some monster touch at- - tacks. + AC (making the overall value smaller and eventually negative) but can + 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- + rosion to suits worn under them and against some monster touch + attacks. - If a piece of armor is enchanted, its armor protection will be + If a piece of armor is enchanted, its armor protection will be better (or worse) than normal, and its "plus" (or minus) will subtract - from your armor class. For example, a +1 chain mail would give you - better protection than normal chain mail, lowering your armor class - one unit further to 4. When you put on a piece of armor, you immedi- - ately find out the armor class and any "plusses" it provides. Cursed - pieces of armor usually have negative enchantments (minuses) in addi- + from your armor class. For example, a +1 chain mail would give you + better protection than normal chain mail, lowering your armor class + one unit further to 4. When you put on a piece of armor, you immedi- + ately find out the armor class and any "plusses" it provides. Cursed + pieces of armor usually have negative enchantments (minuses) in addi- tion to being unremovable. 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 + Such damage can be repaired. Some types of armor may inhibit spell casting. - The nudist option can be set (prior to game start) to attempt to - play the entire game without wearing any armor (a self-imposed chal- + The nudist option can be set (prior to game start) to attempt to + play the entire game without wearing any armor (a self-imposed chal- lenge which is extremely difficult to accomplish). The commands to use armor are `W' (wear) and `T' (take off). The `A' command can be used to take off armor as well as other worn items. Also, `P' (put on) and `R' (remove) which are normally for accessories - can be used for armor, but pieces of armor won't be shown as likely + 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 (`%') - Food is necessary to survive. If you go too long without eating - you will faint, and eventually die of starvation. Some types of food - will spoil, and become unhealthy to eat, if not protected. Food - stored in ice boxes or tins ("cans") will usually stay fresh, but ice + Food is necessary to survive. If you go too long without eating + you will faint, and eventually die of starvation. Some types of food + will spoil, and become unhealthy to eat, if not protected. Food + stored in ice boxes or tins ("cans") will usually stay fresh, but ice boxes are heavy, and tins take a while to open. When you kill monsters, they usually leave corpses which are also - "food." Many, but not all, of these are edible; some also give you - special powers when you eat them. A good rule of thumb is "you are + "food." Many, but not all, of these are edible; some also give you + special powers when you eat them. A good rule of thumb is "you are what you eat." - 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. - - - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -3238,32 +3238,44 @@ - You can name one food item after something you like to eat with + 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. + + You can name one food item after something you like to eat with the fruit option. The command to eat food is `e'. 7.5. Scrolls (`?') - Scrolls are labeled with various titles, probably chosen by an- - cient wizards for their amusement value (for example "READ ME," or - "THANX MAUD" backwards). Scrolls disappear after you read them (ex- - cept for blank ones, without magic spells on them). + 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). - 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 en- - chantment are difficult to identify without these. + 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 + enchantment are difficult to identify without these. - A mail daemon may run up and deliver mail to you as a scroll of + 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. + + Many scrolls produce a different effect from usual if they are + blessed or cursed, or read while the hero is confused. + + A mail daemon may run up and deliver mail to you as a scroll of mail (on versions compiled with this feature). To use this feature on - versions where NetHack mail delivery is triggered by electronic mail - appearing in your system mailbox, you must let NetHack know where to - look for new mail by setting the "MAIL" environment variable to the - file name of your mailbox. You may also want to set the "MAILREADER" - environment variable to the file name of your favorite reader, so - NetHack can shell to it when you read the scroll. On versions of - NetHack where mail is randomly generated internal to the game, these + versions where NetHack mail delivery is triggered by electronic mail + appearing in your system mailbox, you must let NetHack know where to + look for new mail by setting the "MAIL" environment variable to the + file name of your mailbox. You may also want to set the "MAILREADER" + environment variable to the file name of your favorite reader, so + NetHack can shell to it when you read the scroll. On versions of + 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 mail option. @@ -3271,30 +3283,18 @@ 7.6. Potions (`!') - Potions are distinguished by the color of the liquid inside the + Potions are distinguished by the color of the liquid inside the flask. 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 + 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 (`t') - at them. It is also sometimes very useful to dip ("#dip") an object + at them. It is also sometimes very useful to dip ("#dip") an object into a potion. - The command to drink a potion is `q' (quaff). - - 7.7. 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 - yourself (just give a `.' or `s' for the direction). Be warned, how- - ever, for this is often unwise. Other types of wands don't require a - direction. The number of charges in a wand is random and decreases by - one whenever you use it. - - - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -3304,63 +3304,63 @@ - When the number of charges left in a wand becomes zero, attempts - to use the wand will usually result in nothing happening. Occasion- - 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 + The command to drink a potion is `q' (quaff). + + 7.7. 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 + yourself (just give a `.' or `s' for the direction). Be warned, how- + ever, for this is often unwise. Other types of wands don't require a + direction. The number of charges in a wand is random and decreases by + one whenever you use it. + + When the number of charges left in a wand becomes zero, attempts + to use the wand will usually result in nothing happening. Occasion- + 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 of causing it to explode. The chance for such an explosion starts out very small and increases each time the wand is recharged. - In a truly desperate situation, when your back is up against the - wall, you might decide to go for broke and break your wand. This is - not for the faint of heart. Doing so will almost certainly cause a + In a truly desperate situation, when your back is up against the + wall, you might decide to go for broke and break your wand. This is + not for the faint of heart. Doing so will almost certainly cause a catastrophic release of magical energies. - When you have fully identified a particular wand, inventory dis- + When you have fully identified a particular wand, inventory dis- play will include additional information in parentheses: the number of - times it has been recharged followed by a colon and then by its cur- - rent number of charges. A current charge count of -1 is a special + times it has been recharged followed by a colon and then by its cur- + rent number of charges. A current charge count of -1 is a special case indicating that the wand has been cancelled. - The command to use a wand is `z' (zap). To break one, use the + The command to use a wand is `z' (zap). To break one, use the `a' (apply) command. 7.8. Rings (`=') - Rings are very useful items, since they are relatively permanent - magic, unlike the usually fleeting effects of potions, scrolls, and + Rings are very useful items, since they are relatively permanent + magic, unlike the usually fleeting effects of potions, scrolls, and wands. - Putting on a ring activates its magic. You can wear at most two + Putting on a ring activates its magic. You can wear at most two rings at any time, one on the ring finger of each hand. - Most worn rings also cause you to grow hungry more rapidly, the + Most worn rings also cause you to grow hungry more rapidly, the rate varying with the type of ring. - When wearing gloves, rings are worn underneath. If the gloves - are cursed, rings cannot be put on and any already being worn cannot - be removed. When worn gloves aren't cursed, you don't have to manu- - ally take them off before putting on or removing a ring and then re- + When wearing gloves, rings are worn underneath. If the gloves + are cursed, rings cannot be put on and any already being worn cannot + be removed. When worn gloves aren't cursed, you don't have to manu- + ally take them off before putting on or removing a ring and then re- wear them after. That's done implicitly to avoid unnecessary tedium. - The commands to use rings are `P' (put on) and `R' (remove). + The commands to use rings are `P' (put on) and `R' (remove). `A', `W', and `T' can also be used; see Amulets. - 7.9. Spellbooks (`+') - - Spellbooks are tomes of mighty magic. When studied with the `r' - (read) command, they transfer to the reader the knowledge of a spell - (and therefore eventually become unreadable)--unless the attempt back- - fires. Reading a cursed spellbook or one with mystic runes beyond - your ken can be harmful to your health! - - A spell (even when learned) can also backfire when you cast it. - If you attempt to cast a spell well above your experience level, or if - you have little skill with the appropriate spell type, or cast it at a - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -3370,63 +3370,63 @@ - time when your luck is particularly bad, you can end up wasting both + 7.9. Spellbooks (`+') + + Spellbooks are tomes of mighty magic. When studied with the `r' + (read) command, they transfer to the reader the knowledge of a spell + (and therefore eventually become unreadable)--unless the attempt back- + fires. Reading a cursed spellbook or one with mystic runes beyond + your ken can be harmful to your health! + + A spell (even when learned) can also backfire when you cast it. + If you attempt to cast a spell well above your experience level, or if + you have little skill with the appropriate spell type, or cast it at a + time when your luck is particularly bad, you can end up wasting both the energy and the time required in casting. - Casting a spell calls forth magical energies and focuses them - with your naked mind. Some of the magical energy released comes from + Casting a spell calls forth magical energies and focuses them + with your naked mind. Some of the magical energy released comes from 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 + slowly be recovered, and causes you to need additional food. Casting + of spells also requires practice. With practice, your skill in each category of spell casting will improve. Over time, however, your mem- ory of each spell will dim, and you will need to relearn it. Some spells require a direction in which to cast them, similar to wands. To cast one at yourself, just give a `.' or `s' for the direc- - tion. A few spells require you to pick a target location rather than - just specify a particular direction. Other spells don't require any + tion. 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. - 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 - "#enhance" command to advance a sufficiently exercised skill will af- - fect all spells within the group. Advanced skill may increase the po- - tency of spells, reduce their risk of failure during casting attempts, - and improve the accuracy of the estimate for how much longer they will - be retained in your memory. Skill slots are shared with weapons - skills. (See also the section on "Weapon proficiency".) + 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 + "#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 be retained in your memory. Skill slots are shared with + weapons skills. (See also the section on "Weapon proficiency".) Casting a spell also requires flexible movement, and wearing var- ious types of armor may interfere with that. - The command to read a spellbook is the same as for scrolls, `r' - (read). The `+' command lists each spell you know along with its + The command to read a spellbook is the same as for scrolls, `r' + (read). The `+' 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 `Z' (cast) command casts a + of how strongly it is remembered. The `Z' (cast) command casts a spell. 7.10. 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 + 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. - 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 - to being applied for their usual purpose, and in some cases (again, - pick-axe) become wielded as a weapon even when applied. - - The blind option can be set (prior to game start) to attempt to - play the entire game without being able to see (a self-imposed chal- - lenge which is very difficult to accomplish). - - - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -3436,63 +3436,63 @@ + 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 + to being applied for their usual purpose, and in some cases (again, + pick-axe) become wielded as a weapon even when applied. + + The blind option can be set (prior to game start) to attempt to + play the entire game without being able to see (a self-imposed chal- + lenge which is very difficult to accomplish). + The command to use a tool is `a' (apply). 7.10.1. Containers - You may encounter bags, boxes, and chests in your travels. A + You may encounter bags, boxes, and chests in your travels. A tool of this sort can be opened with the "#loot" extended command when - 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 - must set one down before unlocking it by using a key or lock-picking - tool with the `a' (apply) command, by kicking it with the `^D' com- - mand, or by using a weapon to force the lock with the "#force" ex- - tended command. + 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 + must set one down before unlocking it by using a key or lock-picking + tool with the `a' (apply) command, by kicking it with the `^D' com- + mand, or by using a weapon to force the lock with the "#force" + extended command. - Some chests are trapped, causing nasty things to happen when you - unlock or open them. You can check for and try to deactivate traps + 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 "#untrap" extended command. - 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 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. And you would need to have 3 unused in- - ventory slots available in order to take everything out (for the case - where the items you remove don't combine into bigger stacks with + 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 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. And you would need to have 3 unused + inventory slots available in order to take everything out (for the + case where the items you remove don't combine into bigger stacks with things you're already carrying). 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 con- + it can't be locked rather than that it no longer functions as a con- tainer. - The apply and 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 "#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 con- - tents into another container. (As of this writing, the other con- + The apply and 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 "#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 con- + tents into another container. (As of this writing, the other con- tainer must be carried rather than on the floor.) - 7.11. Amulets (`"') - - Amulets are very similar to rings, and often more powerful. Like - rings, amulets have various magical properties, some beneficial, some - harmful, which are activated by putting them on. - - Only one amulet may be worn at a time, around your neck. Like - wearing rings, wearing an amulet affects your metabolism, causing you - to grow hungry more rapidly. - - The commands to use amulets are the same as for rings, `P' (put - on) and `R' (remove). `A' can be used to remove various worn items - including amulets. Also, `W' (wear) and `T' (take off) which are - NetHack 3.7.0 September 13, 2024 + + + + NetHack 3.7.0 May 1, 2025 @@ -3502,63 +3502,63 @@ - normally for armor can be used for amulets and other accessories - (rings and eyewear), but accessories won't be shown as likely candi- - dates in a prompt for choosing what to wear or take off. + 7.11. Amulets (`"') + + Amulets are very similar to rings, and often more powerful. Like + rings, amulets have various magical properties, some beneficial, some + harmful, which are activated by putting them on. + + Only one amulet may be worn at a time, around your neck. Like + wearing rings, wearing an amulet affects your metabolism, causing you + to grow hungry more rapidly. + + The commands to use amulets are the same as for rings, `P' (put + on) and `R' (remove). `A' can be used to remove various worn items + including amulets. Also, `W' (wear) and `T' (take off) which are nor- + mally 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. 7.12. 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 + 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 gems increase your score if you bring them with you when you exit. Other small rocks are also categorized as gems, but they are much - less valuable. All rocks, however, can be used as projectile weapons - (if you have a sling). In the most desperate of cases, you can still + less valuable. 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. 7.13. Large rocks (``') - Statues and boulders are not particularly useful, and are gener- + Statues and boulders are not particularly useful, and are gener- ally heavy. It is rumored that some statues are not what they seem. - Boulders occasionally block your path. You can push one forward + Boulders occasionally block your path. You can push one forward (by attempting to walk onto its spot) when nothing blocks its path, or - you can smash it into a pile of small rocks with breaking magic or a + 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 `m') prior to the direction of movement will attempt to - move to a boulder's location without pushing it in addition to the + it any further be blocked. Using the move-without-picking-up prefix + (default key `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. - Very large humanoids (giants and their ilk) have been known to + Very large humanoids (giants and their ilk) have been known to pick up boulders and use them as missile weapons. - Unlike boulders, statues can't be pushed, but don't need to be - because they don't block movement. They can be smashed into rocks + Unlike boulders, statues can't be pushed, but don't need to be + because they don't block movement. They can be smashed into rocks though. - For some configurations of the program, statues are no longer - shown as ``' but by the letter representing the monster they depict + For some configurations of the program, statues are no longer + shown as ``' but by the letter representing the monster they depict instead. - 7.14. 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 - by the amount of gold 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 un- - cursed even if you turn off the implicit_uncursed option. You can set - the goldX option if you prefer to have gold pieces be treated as - bless/curse state unknown rather than as known to be uncursed. Only - matters when you're using an object selection prompt that can filter - by "BUCX" state. - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -3568,63 +3568,63 @@ + 7.14. 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 + by the amount of gold 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 implicit_uncursed option. You can + set the goldX option if you prefer to have gold pieces be treated as + bless/curse state unknown rather than as known to be uncursed. Only + matters when you're using an object selection prompt that can filter + by "BUCX" state. + 7.15. 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 - any more, it will continue to be displayed on your map. That remains - the case even if it is not actually there any more--perhaps a monster + and move to another location where you can't directly see that object + any more, it will continue to be displayed on your map. That remains + the case even if it is not actually there any more--perhaps a monster has picked it up or it has rotted away--until you can see or feel that location again. One notable exception is that if the object gets cov- - ered by the "remembered, unseen monster" marker. When that marker is + ered by the "remembered, unseen monster" marker. When that marker is later removed after you've verified that no monster is there, you will - have forgotten that there was any object there regardless of whether - the unseen monster actually took the object. If the object is still - there, then once you see or feel that location again you will re-dis- + have forgotten that there was any object there regardless of whether + the unseen monster actually took the object. If the object is still + there, then once you see or feel that location again you will re-dis- cover 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 hilite_pile option can be + the top item of the pile is displayed. The hilite_pile option can be enabled in order to show an item differently when it is the top one of a pile. 8. Conduct - As if winning 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 chal- - lenges, which can be checked at any time with the #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 challenges. Note - that it is perfectly acceptable to win the game without resorting to - these restrictions and that it is unusual for players to adhere to + As if winning 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 chal- + lenges, which can be checked at any time with the #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 challenges. Note + that it is perfectly acceptable to win the game without resorting to + these restrictions and that it is unusual for players to adhere to challenges the first time they win the game. - Several of the challenges are related to eating behavior. The + Several of the challenges are related to eating behavior. The most difficult of these is the foodless challenge. Although creatures - can survive long periods of time without food, there is a physiologi- - cal need for water; thus there is no restriction on drinking bever- - ages, even if they provide some minor food benefits. Calling upon + can survive long periods of time without food, there is a physiologi- + cal need for water; thus there is no restriction on drinking bever- + ages, even if they provide some minor food benefits. Calling upon your god for help with starvation does not violate any food challenges either. - A strict vegan diet is one which avoids any food derived from an- - imals. The primary source of nutrition is fruits and vegetables. The - corpses and tins of blobs (`b'), jellies (`j'), and fungi (`F') are - also considered to be vegetable matter. Certain human food is pre- - pared 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 into a creature - that can digest it is also considered vegan food. 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 (`P') other than - - - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -3634,63 +3634,63 @@ - the black puddings, eggs and food made from eggs (fortune cookies and - pancakes), food made with milk (cream pies and candy bars), and lumps + 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') + 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 into a creature + that can digest it is also considered vegan food. 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 (`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. Eating any kind of meat violates the vegetarian, vegan, and food- - less conducts. This includes tripe rations, the corpses or tins of + less conducts. This includes tripe rations, the corpses or tins of any monsters not mentioned above, and the various other chunks of meat - found in the dungeon. Swallowing and digesting a monster while poly- - morphed is treated as if you ate the creature's corpse. Eating - leather, dragon hide, or bone items while polymorphed into a creature - that can digest it, or eating monster brains while polymorphed into a - mind flayer, is considered eating an animal, although wax is only an + found in the dungeon. Swallowing and digesting a monster while poly- + morphed is treated as if you ate the creature's corpse. Eating + leather, dragon hide, or bone items while polymorphed into a creature + that can digest it, or eating monster brains while polymorphed into a + mind flayer, is considered eating an animal, although wax is only an animal byproduct. - Regardless of conduct, there will be some items which are indi- + Regardless of conduct, there will be some items which are indi- gestible, and others which are hazardous to eat. Using a swallow-and- - digest attack against a monster is equivalent to eating the monster's - corpse. Please note that the term "vegan" is used here only in the - context of diet. You are still free to choose not to use or wear - items derived from animals (e.g. leather, dragon hide, bone, horns, - coral), but the game will not keep track of this for you. Also note - that "milky" potions may be a translucent white, but they do not con- - tain milk, so they are compatible with a vegan diet. Slime molds or - player-defined "fruits", although they could be anything from "cher- + digest attack against a monster is equivalent to eating the monster's + corpse. Please note that the term "vegan" is used here only in the + context of diet. You are still free to choose not to use or wear + items derived from animals (e.g. leather, dragon hide, bone, horns, + coral), but the game will not keep track of this for you. Also note + that "milky" potions may be a translucent white, but they do not con- + tain milk, so they are compatible with a vegan diet. Slime molds or + player-defined "fruits", although they could be anything from "cher- ries" to "pork chops", are also assumed to be vegan. An atheist is one who rejects religion. This means that you can- not #pray, #offer sacrifices to any god, #turn undead, or #chat with a priest. Particularly selective readers may argue that playing Monk or - Priest characters should violate this conduct; that is a choice left + 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 win the game and is not counted against this conduct. You are also - not penalized for being spoken to by an angry god, priest(ess), or + 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. - 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 + 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. - 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 - - - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -3700,63 +3700,63 @@ - assumed to be learned from your teachers prior to the start of the + 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, + 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 + 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- + 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 + 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 + 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; + 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, + 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- + 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 + 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 at- - tained. 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 - which you might accomplish them. - - Rank - Attained rank title Rank. - Shop - Entered a shop. - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -3766,6 +3766,18 @@ + 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 + which you might accomplish them. + + Rank - Attained rank title Rank. + Shop - Entered a shop. Temple - Entered a temple. Mines - Entered the Gnomish Mines. Town - Entered Mine Town. @@ -3773,12 +3785,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. @@ -3798,31 +3810,19 @@ 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). - 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. - - - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -3832,63 +3832,63 @@ - The Medusa achievement is recorded if she dies for any reason, + 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 + 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 + 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 + 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 + 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 + 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 be- - fore starting the game or a global configuration for system adminis- + 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 + 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 + 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 + 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -3898,29 +3898,40 @@ - comment beginning with `#'). The text between the square brackets is - the section name. Section markers are only valid after a CHOOSE di- - rective 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- + 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 + 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 + 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. + tiple options separated by commas in a single OPTIONS directive. (Comma separated options are processed from right to left.) Example: @@ -3930,17 +3941,29 @@ HACKDIR Default location of files NetHack needs. On Windows HACKDIR defaults - to the location of the NetHack.exe or NetHackw.exe file so setting + 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 + 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 + The location where saved games are kept. Defaults to HACKDIR, must be writable. + + + NetHack 3.7.0 May 1, 2025 + + + + + + NetHack Guidebook 61 + + + BONESDIR The location that bones files are kept. Defaults to HACKDIR, must be writable. @@ -3950,24 +3973,13 @@ 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. - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 61 - - - 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. @@ -3976,13 +3988,13 @@ AUTOCOMPLETE=zap,!annotate AUTOPICKUP_EXCEPTION - Set exceptions to the pickup_types option. See the "Configuring Au- - topickup Exceptions" section. + Set exceptions to the pickup_types option. See the "Configuring + Autopickup Exceptions" section. BINDINGS - Change the key bindings of some special keys, menu accelerators, ex- - tended commands, or mouse buttons. You can specify multiple bind- - ings. Format is key followed by the command, separated by a colon. + 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. See the "Changing Key Bindings" section for more information. Example: @@ -4005,22 +4017,10 @@ 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 - the last section extends to the end of the options file. - - MENUCOLOR - Highlight menu lines with different colors. See the "Configuring - Menu Colors" section. - - MSGTYPE - Change the way messages are shown in the top status line. See the - "Configuring Message Types" section. - - ROGUESYMBOLS - Custom symbols for the rogue level's symbol set. See SYMBOLS below. + begins; whatever follows will be common to all sections. Otherwise - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -4030,16 +4030,29 @@ + the last section extends to the end of the options file. + + MENUCOLOR + Highlight menu lines with different colors. See the "Configuring + Menu Colors" section. + + MSGTYPE + Change the way messages are shown in the top status line. See the + "Configuring Message Types" section. + + ROGUESYMBOLS + Custom symbols for the rogue level's symbol set. See SYMBOLS below. + SOUND 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: @@ -4049,8 +4062,8 @@ 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 + 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. Example: @@ -4061,6 +4074,28 @@ Here is an example of configuration file contents: + + + + + + + + + + + + + NetHack 3.7.0 May 1, 2025 + + + + + + NetHack Guidebook 63 + + + # Set your character's role, race, gender, and alignment. OPTIONS=role:Valkyrie, race:Human, gender:female, align:lawful # @@ -4081,34 +4116,22 @@ 9.3. Using the NETHACKOPTIONS environment variable - The NETHACKOPTIONS variable is a comma-separated list of initial - values for the various options. Some can only be turned on or off. + 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, - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 63 - - - - and turn it off by typing a `!' or "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. The value is terminated by the next comma or the end of + and turn it off by typing a `!' or "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. The value is terminated by the next comma or the end of string. - For example, to set up an environment variable so that color is - on, legacy is off, character name is set to "Blue Meanie", and named + For example, to set up an environment variable so that color is + on, legacy is off, character name is set to "Blue Meanie", and named fruit is set to "lime", you would enter the command % setenv NETHACKOPTIONS "color,\!leg,name:Blue Meanie,fruit:lime" - in csh (note the need to escape the `!' since it's special to that + in csh (note the need to escape the `!' since it's special to that shell), or the pair of commands $ NETHACKOPTIONS="color,!leg,name:Blue Meanie,fruit:lime" @@ -4116,43 +4139,20 @@ in sh, ksh, or bash. - The NETHACKOPTIONS value is effectively the same as a single OP- - TIONS directive in a configuration file. The "OPTIONS=" prefix is im- - plied and comma separated options are processed from right to left. - Other types of configuration directives such as BIND or MSGTYPE are + The NETHACKOPTIONS value is effectively the same as a single + OPTIONS directive in a configuration file. The "OPTIONS=" prefix is + implied and comma separated options are processed from right to left. + Other types of configuration directives such as BIND or MSGTYPE are 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 `@' (at- - sign) to let NetHack know that the rest is intended as a file name. + 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 `@' (at- + sign) to let NetHack know that the rest is intended as a file name. If it does start with `/', the at-sign is optional. - 9.4. Customization options - Here are explanations of what the various options do. Character - strings that are too long may be truncated. Some of the options - listed may be inactive in your dungeon. - - 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. - - accessiblemsg - Add location or direction information to messages (default is off). - - acoustics - Enable messages about what your character hears (default on). Note - that this has nothing to do with your computer's audio capabilities. - Persistent. - - alignment - Your starting alignment (align:lawful, align:neutral, or - align:chaotic). You may specify just the first letter. Many roles - and the non-human races restrict which alignments are allowed. See - - - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -4162,13 +4162,37 @@ + 9.4. Customization options + + Here are explanations of what the various options do. Character + strings that are too long may be truncated. Some of the options + listed may be inactive in your dungeon. + + 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. + + accessiblemsg + Add location or direction information to messages (default is off). + + acoustics + Enable messages about what your character hears (default on). Note + that this has nothing to do with your computer's audio capabilities. + Persistent. + + alignment + Your starting alignment (align:lawful, align:neutral, or + align:chaotic). You may specify just the first letter. Many roles + and the non-human races restrict which alignments are allowed. See role for a description of how to use negation to exclude choices. - Default is random. Cannot be set with the `O' command. Persistent. + If 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 `O' command. Persistent. autodescribe - Automatically describe the terrain under cursor when asked to get a - location on the map (default true). The whatis_coord option con- + Automatically describe the terrain under cursor when asked to get a + location on the map (default true). The whatis_coord option con- trols whether the description includes map coordinates. autodig @@ -4180,45 +4204,21 @@ sistent. autopickup - Automatically pick up things onto which you move (default off). + Automatically pick up things onto which you move (default off). Persistent. - See pickup_types and also autopickup_exception for ways to refine + See pickup_types and also autopickup_exception for ways to refine the behavior. Note: prior to version 3.7.0, the default for autopickup was on. autoquiver - This option controls what happens when you attempt the `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. 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 `Q' command instead. If no weapon is found or - the option is false, the `t' (throw) command is executed instead. - Persistent. - - autounlock - Controls what action to take when attempting to walk into a locked - door or to loot a locked container. Takes a plus-sign separated - list of values: - - 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; - Apply-Key - if carrying a key or other unlocking tool, prompt about - using it; - 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); - Force - try to force a container's lid with your currently - wielded weapon (if you omit untrap or decline to attempt + This option controls what happens when you attempt the `f' (fire) + 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -4228,18 +4228,42 @@ + 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 + make ready with the `Q' command instead. If no weapon is found or + the option is false, the `t' (throw) command is executed instead. + Persistent. + + autounlock + Controls what action to take when attempting to walk into a locked + door or to loot a locked container. Takes a plus-sign separated + list of values: + + 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; + Apply-Key - if carrying a key or other unlocking tool, prompt about + using it; + 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); + 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); - None - none of the above; can't be combined with the other + None - none of the above; can't be combined with the other choices. Omitting the value is treated as if autounlock:apply-key. Preceding autounlock with `!' or "no" is treated as autounlock:none. - Applying a key might set off a trap if the door or container is - trapped. Successfully kicking a door will break it and wake up - nearby monsters. Successfully forcing a container open will break - its lock and might also destroy some of its contents or damage your + Applying a key might set off a trap if the door or container is + trapped. Successfully kicking a door will break it and wake up + nearby monsters. Successfully forcing a container open will break + its lock and might also destroy some of its contents or damage your weapon or both. The default is Apply-Key. Persistent. @@ -4251,40 +4275,16 @@ Allow saving and loading bones files (default true). Persistent. boulder - Set the character used to display boulders (default is the "large + Set the character used to display boulders (default is the "large rock" class symbol, ``'). catname - Name your starting cat (for example "catname:Morris"). Cannot be + Name your starting cat (for example "catname:Morris"). Cannot be set with the `O' command. - character - Synonym for "role" to pick the type of your character (for example - "character:Monk"). See role for more details. - - checkpoint - Save game state after each level change, for possible recovery after - program crash (default on). Persistent. - - cmdassist - Have the game provide some additional command assistance for new - players if it detects some anticipated mistakes (default on). - - confirm - Have user confirm attacks on pets, shopkeepers, and other peaceable - creatures (default on). Persistent. - - dark_room - Show out-of-sight areas of lit rooms (default on). Persistent. - - deaf - Start the character permanently deaf (default false). Persistent. - - dropped_nopick - If this option is on, items you dropped will not be automatically - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -4294,12 +4294,36 @@ - picked up, even if autopickup is also on and they are in - pickup_types or match a positive autopickup exception (default on). + character + Synonym for "role" to pick the type of your character (for example + "character:Monk"). See role for more details. + + checkpoint + Save game state after each level change, for possible recovery after + program crash (default on). Persistent. + + cmdassist + Have the game provide some additional command assistance for new + players if it detects some anticipated mistakes (default on). + + confirm + Have user confirm attacks on pets, shopkeepers, and other peaceable + creatures (default on). Persistent. + + dark_room + Show out-of-sight areas of lit rooms (default on). Persistent. + + deaf + Start the character permanently deaf (default false). Persistent. + + dropped_nopick + If this option is on, items you dropped will not be automatically + picked up, even if autopickup is also on and they are in + pickup_types or match a positive autopickup exception (default on). Persistent. disclose - Controls what information the program reveals when the game ends. + Controls what information the program reveals when the game ends. Value is a space separated list of prompting/category pairs (default is "ni na nv ng nc no", prompt with default response of `n' for each candidate). Persistent. The possibilities are: @@ -4311,7 +4335,7 @@ c - display your conduct; also achievements, if any; o - display dungeon overview. - Each disclosure possibility can optionally be preceded by a prefix + Each disclosure possibility can optionally be preceded by a prefix which lets you refine how it behaves. Here are the valid prefixes: y - prompt you and default to yes on the prompt; @@ -4319,38 +4343,14 @@ + - disclose it without prompting; - - do not disclose it and do not prompt. - The listings of vanquished monsters and of genocided types can be + The listings of vanquished monsters and of genocided types can be sorted, so there are two additional choices for `v' and `g': ? - prompt you and default to ask on the prompt; # - disclose it without prompting, ask for sort order. - 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 - the disclosure categories does not matter, program display for end- - of-game disclosure follows a set sequence. - - (for example "disclose:yi na +v -g o") The example sets inventory to - prompt and default to yes, attributes to prompt and default to no, - vanquished to disclose without prompting, genocided to not disclose - and not prompt, conduct to implicitly prompt and default to no, and - overview to disclose without prompting. - - Note that the vanquished monsters list includes all monsters killed - by 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. - - dogname - Name your starting dog (for example "dogname:Fang"). Cannot be set - - - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -4360,17 +4360,40 @@ + 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 + the disclosure categories does not matter, program display for end- + of-game disclosure follows a set sequence. + + (for example "disclose:yi na +v -g o") The example sets inventory to + prompt and default to yes, attributes to prompt and default to no, + vanquished to disclose without prompting, genocided to not disclose + and not prompt, conduct to implicitly prompt and default to no, and + overview to disclose without prompting. + + Note that the vanquished monsters list includes all monsters killed + by 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. + + dogname + Name your starting dog (for example "dogname:Fang"). Cannot be set with the `O' command. extmenu - Changes the extended commands interface to pop-up a menu of avail- - able commands. It is keystroke compatible with the traditional in- - terface except that it does not require that you hit Enter. It is + Changes the extended commands interface to pop-up a menu of avail- + able commands. It is keystroke compatible with the traditional + interface except that it does not require that you hit Enter. It is implemented for the tty interface (default off). - For the X11 interface, which always uses a menu for choosing an ex- - tended command, it controls whether the menu shows all available - commands (on) or just the subset of commands which have tradition- + For the X11 interface, which always uses a menu for choosing an + extended command, it controls whether the menu shows all available + commands (on) or just the subset of commands which have tradition- ally been considered extended ones (off). female @@ -4378,45 +4401,22 @@ command. fireassist - This option controls what happens when you attempt the `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 + This option controls what happens when you attempt the `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. 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 + on). If this is off, dropping an object shifts all the remaining inventory letters. Persistent. force_invmenu - Commands asking for an inventory item show a menu instead of a text + Commands asking for an inventory item show a menu instead of a text query with possible menu letters. Default is off. - fruit - Name a fruit after something you enjoy eating (for example - "fruit:mango") (default "slime mold"). Basically a nostalgic whimsy - that NetHack uses from time to time. You should set this to some- - thing you find more appetizing than slime mold. Apples, oranges, - pears, bananas, and melons already exist in NetHack, so don't use - those. - gender - Your starting gender (gender:male or gender:female). You may spec- - ify just the first letter. Although you can still denote your gen- - der using either of the deprecated male and female options, if the - gender option is also present it will take precedence. See role for - a description of how to use negation to exclude choices. - - Default is random. Cannot be set with the `O' command. Persistent. - - goldX - When filtering objects based on bless/curse state (BUCX), whether to - treat gold pieces as X (unknown bless/curse state, when "on") or U - (known to be uncursed, when "off", the default). Gold is never - blessed or cursed, but it is not described as "uncursed" even when - - - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -4426,63 +4426,63 @@ + fruit + Name a fruit after something you enjoy eating (for example + "fruit:mango") (default "slime mold"). Basically a nostalgic whimsy + that NetHack uses from time to time. You should set this to some- + thing you find more appetizing than slime mold. Apples, oranges, + pears, bananas, and melons already exist in NetHack, so don't use + those. + + gender + Your starting gender (gender:male or gender:female). You may spec- + ify just the first letter. Although you can still denote your gen- + der using either of the deprecated male and female options, if the + gender option is also present it will take precedence. See role for + a description of how to use negation to exclude choices. + + If 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 `O' command. Persistent. + + goldX + When filtering objects based on bless/curse state (BUCX), whether to + treat gold pieces as X (unknown bless/curse state, when "on") or 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 implicit_uncursed option is "off". help - If more information is available for an object looked at with the - `/' command, ask if you want to see it (default on). Turning help - off makes just looking at things faster, since you aren't inter- - rupted with the "More info?" prompt, but it also means that you - might miss some interesting and/or important information. Persis- + If more information is available for an object looked at with the + `/' command, ask if you want to see it (default on). Turning help + off makes just looking at things faster, since you aren't inter- + rupted with the "More info?" prompt, but it also means that you + might miss some interesting and/or important information. Persis- tent. herecmd_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. + or next to you, show a menu of possible actions for the location. Same as "#herecmdmenu" and "#therecmdmenu" commands. hilite_pet - Visually distinguish pets from similar animals (default off). The - behavior of this option depends on the type of windowing you use. + 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. With the tty or curses interface, the petattr option controls how to - highlight pets and setting it will turn the hilite_pet option on or + highlight pets and setting it will turn the hilite_pet option on or off as warranted. hilite_pile - Visually distinguish piles of objects from individual objects (de- - fault off). The behavior of this option depends on the type of win- - dowing 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. - - hitpointbar - Show a hit point bar graph behind your name and title in the status - display (default off). - - The "curses" interface supports it even if the status highlighting - feature has been disabled when building the program. The "tty" and - "mswin" (aka "Windows GUI") interfaces support it only if status - highlighting is left enabled when building. You don't need to set - up any highlighting rules in order to display 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 inverse. For tty and curses, blink will - also be used if the current hitpoint value is at or below the criti- - cal HP threshold. - - 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 resiz- - ing the status panel. To resize that, use the #optionsfull command - to toggle the hitpointbar option off, perform the resize while it's - off, then use the same command to toggle it back on.) + 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -4492,12 +4492,36 @@ + symbol beside the object on the top of the pile. + + hitpointbar + Show a hit point bar graph behind your name and title in the status + display (default off). + + The "curses" interface supports it even if the status highlighting + feature has been disabled when building the program. The "tty" and + "mswin" (aka "Windows GUI") interfaces support it only if status + highlighting is left enabled when building. You don't need to set + up any highlighting rules in order to display 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 inverse. For tty and curses, blink will + also be used if the current hitpoint value is at or below the criti- + cal HP threshold. + + 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 resiz- + ing the status panel. To resize that, use the #optionsfull command + to toggle the hitpointbar option off, perform the resize while it's + off, then use the same command to toggle it back on.) + horsename - Name your starting horse (for example "horsename:Trigger"). Cannot + Name your starting horse (for example "horsename:Trigger"). Cannot be set with the `O' command. ignintr - Ignore interrupt signals, including breaks (default off). Persis- + Ignore interrupt signals, including breaks (default off). Persis- tent. implicit_uncursed @@ -4515,40 +4539,16 @@ your character as lit (default off). Persistent. 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', + When using a menu to interact with a container, use the old `a', + `b', and `c' keyboard shortcuts rather than the mnemonics `o', `i', and `b' (default off). Persistent. mail Enable mail delivery during the game (default on). Persistent. - male - An obsolete synonym for "gender:male". Cannot be set with the `O' - command. - - mention_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). Cannot be set - with the `O' command. Persistent. - - mention_map - Give feedback when interesting map locations change (default off). - - mention_walls - Give feedback when walking against a wall (default off). Persis- - tent. - - menucolors - Enable coloring menu lines (default off). See "Configuring Menu - Colors" on how to configure the colors. - - menustyle - Controls the method used when you need to choose various objects (in - response to the Drop (aka droptype) command, for instance). The - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -4558,21 +4558,45 @@ - value specified should be the first letter of one of the following: - traditional, combination, full, or partial. Default is full. Per- + male + An obsolete synonym for "gender:male". Cannot be set with the `O' + command. + + mention_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). Cannot be set + with the `O' command. Persistent. + + mention_map + Give feedback when interesting map locations change (default off). + + mention_walls + Give feedback when walking against a wall (default off). Persis- + tent. + + menucolors + Enable coloring menu lines (default off). See "Configuring Menu + Colors" on how to configure the colors. + + menustyle + Controls the method used when you need to choose various objects (in + response to the Drop (aka droptype) command, for instance). The + value specified should be the first letter of one of the following: + traditional, combination, full, or partial. Default is full. Per- sistent. - Traditional was the only method available for very early versions; - it consists of a prompt for object class characters, followed by an - object-by-object prompt for all items matching the selected object + Traditional was the only method available for very early versions; + it consists of a prompt for object class characters, followed by an + object-by-object prompt for all items matching the selected object class(es). Combination starts with a prompt for object class(es) of - interest, but then displays a menu of matching objects rather than + interest, but then displays a menu of matching objects rather than prompting one-by-one. Full displays a menu of object classes rather - than a character prompt, and then a menu of matching objects for se- - lection. (Choosing its `A' (Autoselect-All) choice skips the second - menu. To avoid choosing that by accident, set paranoid_confirm:Au- - toAll to require confirmation.) Partial skips the object class fil- - tering and immediately displays a menu of all objects. + than a character prompt, and then a menu of matching objects for + selection. (Choosing its `A' (Autoselect-All) choice skips the sec- + ond menu. To avoid choosing that by accident, set paranoid_con- + firm:AutoAll to require confirmation.) Partial skips the object + class filtering and immediately displays a menu of all objects. menu_deselect_all Key to deselect all items in a menu. Default `-'. @@ -4584,11 +4608,22 @@ Key to jump to the first page in a menu. Default `^'. 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 + 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 `@'. @@ -4602,8 +4637,29 @@ Key to go to the next menu page. Default `>'. menu_objsyms - Show object symbols in menu headings in menus where the object sym- - bols act as menu accelerators (default off). + Inventory and other object menus are normally separated by object + class (weapons, armor, and so forth), with a menu header line at the + beginning of each group. You can have menus add the display symbol + for the class of objects for each header line. You can also add the + display symbol for the individual item in each menu entry. For a + tiles map, that would be a small rendition of an object's tile. For + a text map, it is the same character as is used for the object's + class, which would be most useful when there are no headers separat- + ing objects among classes. Possible values are + + 0 - None - no symbols for either header lines or menu + entries; + 1 - Headers - show symbols on header lines but not entries; + 2 - Entries - show symbols on menu entry lines but not headers; + 3 - Both - show symbols on headers and entries; + 4 - Conditional - only show symbols for entries if there are no + headers; + 5 - One-or-other - show symbols on headers, or on entries if no + headers. + + Supported by tty and curses. When setting the value, it can be + specified by digit or keyword. The default value is Conditional + (4). menu_overlay Do not clear the screen before drawing menus, and align menus to the @@ -4612,20 +4668,8 @@ menu_previous_page Key to go to the previous menu page. Default `<'. - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 71 - - - menu_search - Key to search for some text and toggle selection state of matching + Key to search for some text and toggle selection state of matching menu items. Default `:'. menu_select_all @@ -4634,22 +4678,34 @@ 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 + left. Implemented for perm_invent only by curses and X11. Default `{'. menu_shift_right - Key to scroll a menu which has text beyond the right edge to the + Key to scroll a menu which has text beyond the right edge to the right. Implemented for perm_invent only by curses and X11. Default `}'. mon_movement - Show a message when hero notices a monster movement (default is + Show a message when hero notices a monster movement (default is off). monpolycontrol - Prompt for new form whenever any monster changes shape (default + Prompt for new form whenever any monster changes shape (default off). Debug mode only. montelecontrol @@ -4667,49 +4723,50 @@ port is the same as specifying 0. msghistory - The number of top line messages to keep (and be able to recall with + The number of top line messages to keep (and be able to recall with `^P') (default 20). Cannot be set with the `O' command. msg_window - Allows you to change the way recalled messages are displayed. Cur- - rently it is only supported for tty (all four choices) and for + Allows you to change the way recalled messages are displayed. Cur- + rently it is only supported for tty (all four choices) and for curses (`f' and `r' choices, default `r'). The possible values are: - - - - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 72 - - - s - single message (default; only choice prior to 3.4.0); c - combination, two messages as "single", then as "full"; f - full window, oldest message first; r - full window reversed, newest message first. - For backward compatibility, no value needs to be specified (which - defaults to "full"), or it can be negated (which defaults to "sin- + For backward compatibility, no value needs to be specified (which + defaults to "full"), or it can be negated (which defaults to "sin- 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 - -P -Ra -Ro -S -T -V -W). If -@ is used for the role, then a random - one will be automatically chosen. Cannot be set with the `O' com- - mand. + 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. + + 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 `O' command. news Read the NetHack news file, if present (default on). Since the news - is shown at the beginning of the game, there's no point in setting + is shown at the beginning of the game, there's no point in setting this with the `O' command. nudist @@ -4730,89 +4787,32 @@ -1 - by letters but use `z' to go northwest, `y' to zap wands For backward compatibility, omitting a value is the same as specify- - ing 1 and negating number_pad is the same as specifying 0. (Set- - tings 2 and 4 are for compatibility with MS-DOS or old PC Hack; in - addition to the different behavior for `5', `Alt-5' acts as `G' and - `Alt-0' acts as `I'. Setting -1 is to accommodate some QWERTZ key- - boards which have the location of the `y' and `z' keys swapped.) - When moving by numbers, to enter a count prefix for those commands - which accept one (such as "12s" to search twelve times), precede it + ing 1 and negating number_pad is the same as specifying 0. (Set- + tings 2 and 4 are for compatibility with MS-DOS or old PC Hack; in + addition to the different behavior for `5', `Alt-5' acts as `G' and + `Alt-0' acts as `I'. Setting -1 is to accommodate some QWERTZ key- + boards which have the location of the `y' and `z' keys swapped.) + When moving by numbers, to enter a count prefix for those commands + which accept one (such as "12s" to search twelve times), precede it with the letter `n' ("n12s"). packorder - Specify the order to list object types in (default + Specify the order to list object types in (default "")[%?+!=/(*`0_"). The value of this option should be a string con- taining the symbols for the various object types. Any omitted types are filled in at the end from the previous order. - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 73 - - - paranoid_confirmation - A space separated list of specific situations where alternate - prompting is desired. The default is "paranoid_confirmation:pray + A space separated list of specific situations where alternate + 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 ac- - cepting any non-yes response as no; changes pray and - 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; - die - require "yes" rather than `y' to confirm dying (not - useful in normal play; applies to explore mode); - bones - require "yes" rather than `y' to confirm saving bones - data when dying in debug mode; - attack - require "yes" rather than `y' to confirm attacking a - peaceful monster; - wand-break - require "yes" rather than `y' to confirm breaking a - wand with the apply command; - eating - require "yes" rather than `y' to confirm whether to - continue eating; - Were-change - require "yes" rather than `y' to confirm changing form - due to lycanthropy when hero has polymorph control; - pray - require `y' to confirm an attempt to pray rather than - immediately praying; on by default; (to require "yes" - rather than just `y', set Confirm too); - trap - require `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); confir- - mation can be skipped by using the `m' movement pre- - fix; - swim - prevent walking into water or lava; on by default; (to - deliberately step onto/into such terrain when this is - set, use the `m' movement prefix when adjacent); - AutoAll - require confirmation when the `A' (Autoselect-All) - choice is selected in object class filtering menus for - menustyle:Full; (to require "yes" rather than just - `y', set Confirm too); - Remove - require selection from inventory for `R' and `T' com- - mands even when wearing just one applicable item; - all - turn on all of the above. - - By default, the pray, swim, and trap choices are enabled, the others - disabled. To disable them without setting any of the other choices, - use paranoid_confirmation:none. To keep them enabled while setting - any of the others, you can include them in the new list, such as - paranoid_confirmation:attack pray swim Remove or you can precede the - first entry in the list with a plus sign, paranoid_confirmation:+at- - tack Remove. To remove an entry that has been previously set with- - out removing others, precede the first entry in the list with a mi- - nus sign, paranoid_confirmation:-swim. To both add some new entries - and remove some old ones, you can use multiple paranoid_confirmation + 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -4822,14 +4822,72 @@ - option settings, or you can use the `+' form and list entries to be - added by their name and entries to be removed by `!' and name. The - positive (no `!') and negative (with `!') entries can be intermixed. + 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; + die - require "yes" rather than `y' to confirm dying (not + useful in normal play; applies to explore mode); + bones - require "yes" rather than `y' to confirm saving bones + data when dying in debug mode; + attack - require "yes" rather than `y' to confirm attacking a + peaceful monster; + wand-break - require "yes" rather than `y' to confirm breaking a + wand with the apply command; + eating - require "yes" rather than `y' to confirm whether to + continue eating; + Were-change - require "yes" rather than `y' to confirm changing form + due to lycanthropy when hero has polymorph control; + pray - require `y' to confirm an attempt to pray rather than + immediately praying; on by default; (to require "yes" + rather than just `y', set Confirm too); + trap - require `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); confir- + mation can be skipped by using the `m' movement pre- + fix; + swim - prevent walking into water or lava; on by default; (to + deliberately step onto/into such terrain when this is + set, use the `m' movement prefix when adjacent); + AutoAll - require confirmation when the `A' (Autoselect-All) + choice is selected in object class filtering menus for + menustyle:Full; (to require "yes" rather than just + `y', set Confirm too); + Remove - require selection from inventory for `R' and `T' com- + mands even when wearing just one applicable item; + all - turn on all of the above. + + By default, the pray, swim, and trap choices are enabled, the others + disabled. To disable them without setting any of the other choices, + use paranoid_confirmation:none. To keep them enabled while setting + any of the others, you can include them in the new list, such as + paranoid_confirmation:attack pray swim Remove or you can precede the + first entry in the list with a plus sign, paranoid_confirma- + tion:+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, paranoid_confirmation:-swim. To both add some + 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 + `!' and name. The positive (no `!') and negative (with `!') entries + can be intermixed. pauper Start the character with no possessions (default false). Persis- tent. + + NetHack 3.7.0 May 1, 2025 + + + + + + NetHack Guidebook 75 + + + perm_invent If true, always display your current inventory in a window (default false). @@ -4876,18 +4934,6 @@ Loaded), you will be asked if you want to continue. (Default `S'). Persistent. - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 75 - - - pickup_stolen If this option is on and autopickup is also on, try to pick up things that a monster stole from you, even if they aren't in @@ -4896,12 +4942,24 @@ 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. pickup_types - Specify the object types to be picked up when autopickup is on. De- - fault is all types. Persistent. + Specify the object types to be picked up when autopickup is on. + Default is all types. Persistent. The value is a list of object symbols, such as pickup_types:$?! to pick up gold, scrolls, and potions. You can use autopickup_excep- @@ -4924,10 +4982,10 @@ least that big; default value is 5. Persistent. playmode - Values are "normal", "explore", or "debug". Allows selection of ex- - plore 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 user name (on + Values are "normal", "explore", or "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 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. @@ -4942,18 +5000,6 @@ query_menu Use a menu when asked specific yes/no queries, instead of a prompt. - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 76 - - - quick_farsight When set, usually prevents the "you sense your surroundings" message where play pauses to allow you to browse the map whenever clairvoy- @@ -4962,13 +5008,27 @@ 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- human races are allowed. See role for a description of how to use negation to exclude choices. - Default is random. Cannot be set with the `O' command. Persistent. + If race is not specified, there is no default value; player will be + prompted unless role forces a choice for race. Cannot be set with + the `O' command. Persistent. rest_on_space Make the space bar a synonym for the `.' (#wait) command (default @@ -4992,7 +5052,8 @@ There can be multiple instances of the role option if they're all negations. - Default is random. Cannot be set with the `O' command. Persistent. + If role is not specified, there is no default value; player will be + prompted. Cannot be set with the `O' command. Persistent. roguesymset This option may be used to select one of the named symbol sets found @@ -5000,83 +5061,22 @@ rogue level. rlecomp - When writing out a save file, perform run length compression of the + 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. runmode - Controls the amount of screen updating for the map window when en- - gaged in multi-turn movement (running via shift+direction or con- - trol+direction and so forth, or via the travel command or mouse - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 77 - - - + Controls the amount of screen updating for the map window when + engaged in multi-turn movement (running via shift+direction or con- + trol+direction and so forth, or via the travel command or mouse click). The possible values are: 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; - crawl - like walk, but pause briefly after each step. - - This option only affects the game's screen display, not the actual - results of moving. The default is "run"; versions prior to 3.4.1 - used "teleport" only. Whether or not the effect is noticeable will - depend upon the window port used or on the type of terminal. Per- - sistent. - - safe_pet - Prevent you from (knowingly) attacking your pets (default on). Per- - sistent. - - safe_wait - Prevents you from waiting or searching when next to a hostile mon- - ster (default on). Persistent. - - sanity_check - Evaluate monsters, objects, and map prior to each turn (default - off). Debug mode only. - - 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 - the first letter of each category (`t', `a', or `o') is necessary. - Persistent. - - showdamage - Whenever your character takes damage, show a message of the damage - taken, and the amount of hit points left. - - showexp - Show your accumulated experience points on bottom line (default - off). Persistent. - - 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. - - showscore - Show your approximate accumulated score on bottom line (default - off). By default, this feature is suppressed when building the pro- - gram. Persistent. - - showvers - Include the game's version number on the status lines (default off). - Potentially useful if you switch between different versions or vari- - ants, or you are making screenshots or streaming video. Using the - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -5086,11 +5086,72 @@ - statuslines:3 option is recommended so that there will be more room - available for status information, unless you're using nethack's Qt - interface or your terminal emulator window displays fewer than 25 + crawl - like walk, but pause briefly after each step. + + This option only affects the game's screen display, not the actual + results of moving. The default is "run"; versions prior to 3.4.1 + used "teleport" only. Whether or not the effect is noticeable will + depend upon the window port used or on the type of terminal. Per- + sistent. + + safe_pet + Prevent you from (knowingly) attacking your pets (default on). Per- + sistent. + + safe_wait + Prevents you from waiting or searching when next to a hostile mon- + ster (default on). Persistent. + + sanity_check + Evaluate monsters, objects, and map prior to each turn (default + off). Debug mode only. + + 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 + the first letter of each category (`t', `a', or `o') is necessary. + Persistent. + + showdamage + Whenever your character takes damage, show a message of the damage + taken, and the amount of hit points left. + + showexp + Show your accumulated experience points on bottom line (default + off). Persistent. + + 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. + + showscore + Show your approximate accumulated score on bottom line (default + off). By default, this feature is suppressed when building the pro- + gram. Persistent. + + showvers + Include the game's version number on the status lines (default off). + 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 + 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. @@ -5100,32 +5161,32 @@ The possible values are: - o - list object types by class, in discovery order within each + o - list object types by class, in discovery order within each class; default; - s - list object types by 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 ob- - ject types partly-discovered via assigned name coming before + s - list object types by 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; c - list by class, alphabetically within each class; a - list alphabetically across all classes. - Can be interactively set via the `O' command or via using the `m' + Can be interactively set via the `O' command or via using the `m' prefix before the `\' or ``' command. sortloot - Controls the sorting behavior of the pickup lists for inventory and + Controls the sorting behavior of the pickup lists for inventory and #loot commands and some others. Persistent. The possible values are: full - always sort the lists; - loot - only sort the lists that don't use inventory letters, like + loot - only sort the lists that don't use inventory letters, like with the #loot and pickup commands; none - show lists the traditional way without sorting; default. sortpack - Sort the pack contents by type when displaying inventory (default + Sort the pack contents by type when displaying inventory (default on). Persistent. sortvanquished @@ -5136,29 +5197,29 @@ t - traditional--order by monster level; ties are broken by internal monster index; default; - d - order by monster difficulty rating; ties broken by internal in- - dex; - a - order alphabetically, first any unique monsters then all the + d - order by monster difficulty rating; ties broken by internal + index; + a - order alphabetically, first any unique monsters then all the others; - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 79 - - - c - order by monster class, by low to high level within each class; n - order by count, high to low; ties are broken by internal monster index; z - order by count, low to high; ties broken by internal index. - Can be interactively set via the `m O' command or via using the `m' - prefix before either the #vanquished command or the #genocided com- + + + 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. sounds @@ -5166,7 +5227,7 @@ on). sparkle - Display a sparkly effect when a monster (including yourself) is hit + Display a sparkly effect when a monster (including yourself) is hit by an attack to which it is resistant (default on). Persistent. spot_monsters @@ -5181,61 +5242,60 @@ ing Status Hilites" for further information. status_updates - Allow updates to the status lines at the bottom of the screen (de- - fault true). + Allow updates to the status lines at the bottom of the screen + (default true). suppress_alert - This option may be set to a NetHack version level to suppress alert - notification messages about feature changes for that and prior ver- + This option may be set to a NetHack version level to suppress alert + notification messages about feature changes for that and prior ver- sions (for example "suppress_alert:3.3.1"). symset This option may be used to select one of the named symbol sets found - within "symbols" to alter the symbols displayed on the screen. Use + within "symbols" to alter the symbols displayed on the screen. Use "symset:default" to explicitly select the default symbols. time - Show the elapsed game time in turns on bottom line (default off). + Show the elapsed game time in turns on bottom line (default off). Persistent. timed_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 + 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. - - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 80 - - - tips Show some helpful tips during gameplay (default on). Persistent. 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 (de- - fault off). Setting this option makes the score list visible when a - windowing version of NetHack is started without a parent window, but - it no longer leaves the score list around after game end on a termi- - nal or emulating window. + Put the ending display in a NetHack window instead of on stdout + (default off). Setting this option makes the score list visible + when a windowing version of NetHack is started without a parent win- + dow, but it no longer leaves the score list around after game end on + a terminal or emulating window. 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 af- - fect traveling via the `_' ("#travel") command. Persistent. + you make inadvertent mouse clicks on the map window. Does not + affect traveling via the `_' ("#travel") command. Persistent. tutorial Play a tutorial level at the start of the game. Setting this option @@ -5245,8 +5305,8 @@ Provide more commentary during the game (default on). Persistent. whatis_coord - When using the `/' or `;' commands to look around on the map with - autodescribe on, display coordinates after the description. Also + When using the `/' or `;' commands to look around on the map with + autodescribe on, display coordinates after the description. Also works in other situations where you are asked to pick a location. The possible settings are: @@ -5257,90 +5317,30 @@ s - screen [row,column] (row is offset to match tty usage); n - none (no coordinates shown) [default]. - The whatis_coord option is also used with the "/m", "/M", "/o", and - "/O" sub-commands of `/', where the "none" setting is overridden + The whatis_coord option is also used with the "/m", "/M", "/o", and + "/O" sub-commands of `/', where the "none" setting is overridden with "map". whatis_filter - When getting a location on the map, and using the keys to cycle - through next and previous targets, allows filtering the possible + When getting a location on the map, and using the keys to cycle + through next and previous targets, allows filtering the possible targets. n - no filtering [default] v - in view only a - in same area only - The area-filter tries to be slightly predictive--if you're standing - on a doorway, it will consider the area on the side of the door you - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 81 - - - + The area-filter tries to be slightly predictive--if you're standing + on a doorway, it will consider the area on the side of the door you were last moving towards. Filtering can also be changed when getting a location with the "get- pos.filter" key. - 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. - (default off) - - whatis_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) - - windowtype - When the program has been built to support multiple interfaces, se- - lect which one to use, such as "tty" or "X11" (default depends on - build-time settings; use "#version" to check). Cannot be set with - the `O' command. - - When used, it should be the first option set since its value might - 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 rightmost - option in the list. - - wizweight - Augment object descriptions with their objects' weight (default - off). Debug mode only. - - zerocomp - When writing out a save file, perform zero-comp compression of the - contents. Not all ports support zero-comp compression. It has no ef- - fect on reading an existing save file. - - 9.5. Window Port Customization options - - Here are explanations of the various options that are used to - customize and change the characteristics of the windowtype that you - have chosen. Character strings that are too long may be truncated. - Not all window ports will adjust for all settings listed here. You - can safely add any of these options to your configuration file, and if - the window port is capable of adjusting to suit your preferences, it - will attempt to do so. If it 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 - `O' command. - - align_message - Where to align or place the message window (top, bottom, left, or - right) - NetHack 3.7.0 September 13, 2024 + + NetHack 3.7.0 May 1, 2025 @@ -5350,16 +5350,78 @@ + 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. + (default off) + + whatis_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) + + windowtype + When the program has been built to support multiple interfaces, + select which one to use, such as "tty" or "X11" (default depends on + build-time settings; use "#version" to check). Cannot be set with + the `O' command. + + When used, it should be the first option set since its value might + 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 rightmost + option in the list. + + wizweight + Augment object descriptions with their objects' weight (default + off). Debug mode only. + + 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. + + 9.5. Window Port Customization options + + Here are explanations of the various options that are used to + customize and change the characteristics of the windowtype that you + have chosen. Character strings that are too long may be truncated. + Not all window ports will adjust for all settings listed here. You + can safely add any of these options to your configuration file, and if + the window port is capable of adjusting to suit your preferences, it + will attempt to do so. If it 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 + `O' command. + + align_message + Where to align or place the message window (top, bottom, left, or + right) + align_status - Where to align or place the status window (top, bottom, left, or + 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 - cases, characters can be augmented with line-drawing symbols; use - the symset option to select a symbol set such as DECgraphics or - IBMgraphics if your display supports them. Setting ascii_map to + If NetHack can, it should display the map using simple characters + (letters and punctuation) rather than tiles graphics. In some + cases, characters can be augmented with line-drawing symbols; use + the symset option to select a symbol set such as DECgraphics or + IBMgraphics if your display supports them. Setting ascii_map to True forces tiled_map to be False. color @@ -5368,15 +5430,15 @@ eight_bit_tty If NetHack can, it should pass eight-bit character values (for exam- - ple, specified with the traps option) straight through to your ter- + ple, specified with the traps option) straight through to your ter- minal (default off). font_map - if NetHack can, it should use a font by the chosen name for the map + if NetHack can, it should use a font by the chosen name for the map window. font_menu - If NetHack can, it should use a font by the chosen name for menu + If NetHack can, it should use a font by the chosen name for menu windows. font_message @@ -5388,7 +5450,7 @@ tus window. font_text - If NetHack can, it should use a font by the chosen name for text + If NetHack can, it should use a font by the chosen name for text windows. font_size_map @@ -5403,29 +5465,28 @@ font_size_status If NetHack can, it should use this size font for the status window. - - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 83 - - - font_size_text If NetHack can, it should use this size font for text windows. 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 - Use color text and/or highlighting attributes when displaying some - non-map data (such as menu selector letters). Curses interface + Use color text and/or highlighting attributes when displaying some + non-map data (such as menu selector letters). Curses interface only; default is on. large_font @@ -5435,17 +5496,17 @@ If NetHack can, it should display the map in the manner specified. player_selection - If NetHack can, it should pop up dialog boxes, or use prompts for + If NetHack can, it should pop up dialog boxes, or use prompts for character selection. popup_dialog If NetHack can, it should pop up dialog boxes for input. preload_tiles - If NetHack can, it should preload tiles into memory. For example, + If 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 perfor- - mance of the tile graphics, but uses more memory. (default on). + mance of the tile graphics, but uses more memory. (default on). Cannot be set with the `O' command. scroll_amount @@ -5462,83 +5523,22 @@ support this option. softkeyboard - Display an onscreen keyboard. Handhelds are most likely to support + Display an onscreen keyboard. Handhelds are most likely to support this option. splash_screen - If NetHack can, it should display an opening splash screen when it + If NetHack can, it should display an opening splash screen when it starts up (default yes). - - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 84 - - - statuslines - Number of lines for traditional below-the-map status display. Ac- - ceptable values are 2 and 3 (default is 2). + Number of lines for traditional below-the-map status display. + Acceptable values are 2 and 3 (default is 2). When set to 3, the 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 `O' command. - - The curses interface does likewise if the align_status option is set - to top or bottom but ignores statuslines when set to left or right. - - The Qt interface already displays more than 3 lines for status so - uses the statuslines value differently. A value of 3 renders status - in the Qt interface's original format, with the status window spread - out vertically. A value of 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 ver- - sion of Qt older than qt-5.9, statuslines can only be set in the - run-time configuration file or via NETHACKOPTIONS, not during play - with the `O' command.) - - term_cols and - - term_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. - - tile_file - Specify the name of an alternative tile file to override the de- - fault. - - Note: the X11 interface uses X resources rather than NetHack's op- - tions to select an alternate tile file. See NetHack.ad, the sample - X "application defaults" file. - - tile_height - Specify the preferred height of each tile in a tile capable port. - - tile_width - Specify the preferred width of each tile in a tile capable port - - tiled_map - If NetHack can, it should display the map using tiles graphics - rather than simple characters (letters and punctuation, possibly - augmented by line-drawing symbols). Setting tiled_map to True - forces ascii_map to be False. - - use_darkgray - Use bold black instead of blue for black glyphs (TTY only). + shows status conditions on their own line. A display capable of - - - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -5548,63 +5548,63 @@ + showing at least 25 lines is recommended. The value can be toggled + back and forth during the game with the `O' command. + + The curses interface does likewise if the align_status option is set + to top or bottom but ignores statuslines when set to left or right. + + The Qt interface already displays more than 3 lines for status so + uses the statuslines value differently. A value of 3 renders status + in the Qt interface's original format, with the status window spread + out vertically. A value of 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 ver- + sion of Qt older than qt-5.9, statuslines can only be set in the + run-time configuration file or via NETHACKOPTIONS, not during play + with the `O' command.) + + term_cols and + + term_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. + + tile_file + Specify the name of an alternative tile file to override the + default. + + Note: the X11 interface uses X resources rather than NetHack's + options to select an alternate tile file. See NetHack.ad, the sam- + ple X "application defaults" file. + + tile_height + Specify the preferred height of each tile in a tile capable port. + + tile_width + Specify the preferred width of each tile in a tile capable port + + tiled_map + If NetHack can, it should display the map using tiles graphics + rather than simple characters (letters and punctuation, possibly + augmented by line-drawing symbols). Setting tiled_map to True + forces ascii_map to be False. + + use_darkgray + Use bold black instead of blue for black glyphs (TTY only). + use_inverse - If NetHack can, it should display inverse when the game specifies + If NetHack can, it should display inverse when the game specifies it. vary_msgcount - If NetHack can, it should display this number of messages at a time + If NetHack can, it should display this number of messages at a time in the message window. - windowborders - Whether to draw boxes around the map, status area, message area, and - persistent inventory window if enabled. Curses interface only. Ac- - ceptable values are - 0 - off, never show borders - 1 - on, always show borders - 2 - auto, on if display is at least (24+2)x(80+2) [default] - 3 - on, except forced off for perm_invent - 4 - auto, except forced off for perm_invent - - (The 26x82 size threshold for `2' refers to number of rows and col- - umns of the display. A width of at least 110 columns (80+2+26+2) is - needed to show borders if align_status is set to left or right.) - - The persistent inventory window, when enabled, can grow until it is - too big to fit on most displays, resulting in truncation of its con- - tents. If borders are forced on (1) or the display is big enough to - show them (2), setting the value to 3 or 4 instead will keep borders - for the map, message, and status windows but have room for two addi- - tional lines of inventory plus widen each inventory line by two col- - umns. - - windowcolors - If 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 - - OPTION=windowcolors:style foreground/background - - where style is one of "menu", "message", "status", or "text", and - foreground and background are colors, either numeric (hash sign fol- - lowed by three pairs of hexadecimal digits, #rrggbb), one of the - named colors (black, red, green, brown, blue, magenta, cyan, orange, - bright-green, yellow, bright-blue, bright-magenta, bright-cyan, - white, gray, purple, silver, maroon, fuchsia, lime, olive, navy, - teal, aqua), or (for Windows only) one of Windows UI colors (true- - black, activeborder, activecaption, appworkspace, background, btn- - face, btnshadow, btntext, captiontext, graytext, greytext, high- - light, highlighttext, inactiveborder, inactivecaption, menu, menu- - text, scrollbar, window, windowframe, windowtext). - - wraptext - If NetHack can, it should wrap long lines of text if they don't fit - in the visible area of the window. - - - - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -5614,63 +5614,63 @@ + windowborders + Whether to draw boxes around the map, status area, message area, and + persistent inventory window if enabled. Curses interface only. + Acceptable values are + + 0 - off, never show borders + 1 - on, always show borders + 2 - auto, on if display is at least (24+2)x(80+2) [default] + 3 - on, except forced off for perm_invent + 4 - auto, except forced off for perm_invent + + (The 26x82 size threshold for `2' refers to number of rows and col- + umns of the display. A width of at least 110 columns (80+2+26+2) is + needed to show borders if align_status is set to left or right.) + + The persistent inventory window, when enabled, can grow until it is + too big to fit on most displays, resulting in truncation of its con- + tents. If borders are forced on (1) or the display is big enough to + show them (2), setting the value to 3 or 4 instead will keep borders + for the map, message, and status windows but have room for two addi- + tional lines of inventory plus widen each inventory line by two col- + umns. + + windowcolors + If 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 + + OPTION=windowcolors:style foreground/background + + where style is one of "menu", "message", "status", or "text", and + foreground and background are colors, either numeric (hash sign fol- + lowed by three pairs of hexadecimal digits, #rrggbb), one of the + named colors (black, red, green, brown, blue, magenta, cyan, orange, + bright-green, yellow, bright-blue, bright-magenta, bright-cyan, + white, gray, purple, silver, maroon, fuchsia, lime, olive, navy, + teal, aqua), or (for Windows only) one of Windows UI colors (true- + black, activeborder, activecaption, appworkspace, background, btn- + face, btnshadow, btntext, captiontext, graytext, greytext, high- + light, highlighttext, inactiveborder, inactivecaption, menu, menu- + text, scrollbar, window, windowframe, windowtext). + + wraptext + If NetHack can, it should wrap long lines of text if they don't fit + in the visible area of the window. + 9.6. Crash Report Options - Please note that NetHack does not send any information off your + Please note that NetHack does not send any information off your computer unless you manually click submit on a form. OPTION=crash_email:email_address - OPTION=crash_name:your_name - These options are used only to save you some typing on the - crash report and #bugreport forms. - - OPTION=crash_urlmax:bytes - This option is used to limit the length of the URLs generated - and is only needed if your browser cannot handle arbitrarily - long URLs. - - 9.7. Platform-specific Customization options - - Here are explanations of options that are used by specific plat- - forms or ports to customize and change the port behavior. - - altkeyhandling - Select an alternate way to handle keystrokes (Win32 tty NetHack - only). The name of the handling type is one of "default", "ray", - "340". - - altmeta - On systems where this option is available, it can be set to tell - NetHack to convert a two character sequence beginning with ESC into - a meta-shifted version of the second character (default off). - - 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-- - is also subject to this conversion, so attempting to abort the count - by typing ESC will leave 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. - - 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, OS/2, PC, and ST NetHack - only). - - rawio - Force raw (non-cbreak) mode for faster output and more bulletproof - input (MS-DOS sometimes treats `^P' as a printer toggle without it) - (default off, OS/2, PC, and ST NetHack only). Note: DEC Rainbows - hang if this is turned on. Cannot be set with the `O' command. - - subkeyvalue - (Win32 tty NetHack only). May be used to alter the value of key- - strokes that the operating system returns to NetHack to help - NetHack 3.7.0 September 13, 2024 + + NetHack 3.7.0 May 1, 2025 @@ -5680,63 +5680,63 @@ - compensate for international keyboard issues. OPTIONS=subkey- - value:171/92 will return 92 to 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 `O' command. + OPTION=crash_name:your_name + These options are used only to save you some typing on the + crash report and #bugreport forms. + + OPTION=crash_urlmax:bytes + This option is used to limit the length of the URLs generated + and is only needed if your browser cannot handle arbitrarily + long URLs. + + 9.7. Platform-specific Customization options + + Here are explanations of options that are used by specific plat- + forms or ports to customize and change the port behavior. + + altkeyhandling + Select an alternate way to handle keystrokes (Win32 tty NetHack + only). The name of the handling type is one of "default", "ray", + "340". + + altmeta + On systems where this option is available, it can be set to tell + NetHack to convert a two character sequence beginning with ESC into + a meta-shifted version of the second character (default off). + + 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-- + is also subject to this conversion, so attempting to abort the count + by typing ESC will leave 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. + + 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, OS/2, PC, and ST NetHack + only). + + rawio + Force raw (non-cbreak) mode for faster output and more bulletproof + input (MS-DOS sometimes treats `^P' as a printer toggle without it) + (default off, OS/2, PC, and ST NetHack only). Note: DEC Rainbows + hang if this is turned on. Cannot be set with the `O' command. + + subkeyvalue + (Win32 tty NetHack only). May be used to alter the value of key- + strokes that the operating system returns to NetHack to help compen- + sate for international keyboard issues. OPTIONS=subkeyvalue:171/92 + will return 92 to NetHack, if 171 was originally going to be + returned. You can use multiple subkeyvalue assignments in the con- + figuration file if needed. Cannot be set with the `O' command. video Set the video mode used (PC NetHack only). Values are "autodetect", - "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 - 16 colors, a mode that is compatible with all VGA hardware. Third - party tilesets will probably not work. Setting "autodetect" at- - tempts "vesa", then "vga", and finally sets "default" if neither of - those modes works. Cannot be set with the `O' command. - - video_height - Set the VGA mode resolution height (MS-DOS only, with video:vesa) - - video_width - Set the VGA mode resolution width (MS-DOS only, with video:vesa) - - videocolors - Set the color palette for PC systems using NO_TERMS (default - 4-2-6-1-5-3-15-12-10-14-9-13-11, (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 `O' command. - - videoshades - Set the intensity level of the three gray scales available (default - dark normal light, PC NetHack only). If the game display is diffi- - cult to read, try adjusting these scales; if this does not correct - the problem, try !color. Cannot be set with the `O' command. - - 9.8. Regular Expressions - - Regular expressions are normally POSIX extended regular expres- - sions. It is possible to compile 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 NetHack was - built this way, patterns are instead glob patterns; regardless, this - document refers to both as `regular expressions.' This applies to Au- - topickup exceptions, Message types, Menu colors, and User sounds. - - 9.9. Configuring Autopickup Exceptions - - You can further refine the behavior of the autopickup option be- - yond what is available through the pickup_types option. - - By placing autopickup_exception lines in your configuration file, - you can define patterns to be checked when the game is about to au- - topickup something. - - - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -5746,63 +5746,63 @@ + "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 + 16 colors, a mode that is compatible with all VGA hardware. Third + party tilesets will probably not work. Setting "autodetect" + attempts "vesa", then "vga", and finally sets "default" if neither + of those modes works. Cannot be set with the `O' command. + + video_height + Set the VGA mode resolution height (MS-DOS only, with video:vesa) + + video_width + Set the VGA mode resolution width (MS-DOS only, with video:vesa) + + videocolors + Set the color palette for PC systems using NO_TERMS (default + 4-2-6-1-5-3-15-12-10-14-9-13-11, (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 `O' command. + + videoshades + Set the intensity level of the three gray scales available (default + dark normal light, PC NetHack only). If the game display is diffi- + cult to read, try adjusting these scales; if this does not correct + the problem, try !color. Cannot be set with the `O' command. + + 9.8. Regular Expressions + + Regular expressions are normally POSIX extended regular expres- + sions. It is possible to compile 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 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. + + 9.9. Configuring Autopickup Exceptions + + You can further refine the behavior of the autopickup option + beyond what is available through the pickup_types option. + + By placing autopickup_exception lines in your configuration file, + you can define patterns to be checked when the game is about to + autopickup something. + autopickup_exception Sets an exception to the pickup_types option. The autopickup_excep- tion option should be followed by a regular expression to be used as - a pattern to match against the singular form of the description of + a pattern to match against the singular form of the description of an object at your location. - In addition, some characters are treated specially if they occur as + In addition, some characters are treated specially if they occur as the first character in the pattern, specifically: - < - always pickup an object that matches rest of pattern; - > - never pickup an object that matches rest of pattern. - The autopickup_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. - - Exceptions can be set with the `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. autopickup_exception rules and not - saved with the game. - - Here are some examples: - - autopickup_exception="<*arrow" - autopickup_exception=">*corpse" - autopickup_exception=">* cursed*" - - The first example above will result in autopickup of any type of - arrow. The second example results in the exclusion of any corpse from - autopickup. The last example results in the exclusion of items known - to be cursed from autopickup. - - 9.10. Changing Key Bindings - - It is possible to change the default key bindings of some special - commands, menu accelerator keys, and 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 ("x"), a control key ("^X", "C-x"), a meta key ("M-x"), a - mouse button, or a three-digit decimal ASCII code. - - For example: - - BIND=^X:getpos.autodescribe - BIND=\:menu_first_page - BIND=v:loot - - Extended command keys - You can bind multiple keys to the same extended command. Unbind a - key by using "nothing" as the extended command to bind to. You can - also bind the "", "", and "" keys. - - Menu accelerator keys - The menu control or accelerator keys can also be rebound via OPTIONS - - - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -5812,63 +5812,63 @@ - lines in the configuration file. You cannot bind object symbols or + < - always pickup an object that matches rest of pattern; + > - never pickup an object that matches rest of pattern. + + The autopickup_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. + + Exceptions can be set with the `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. autopickup_exception rules and not + saved with the game. + + Here are some examples: + + autopickup_exception="<*arrow" + autopickup_exception=">*corpse" + autopickup_exception=">* cursed*" + + The first example above will result in autopickup of any type of + arrow. The second example results in the exclusion of any corpse from + autopickup. The last example results in the exclusion of items known + to be cursed from autopickup. + + 9.10. Changing Key Bindings + + It is possible to change the default key bindings of some special + commands, menu accelerator keys, and 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 ("x"), a control key ("^X", "C-x"), a meta key ("M-x"), a + mouse button, or a three-digit decimal ASCII code. + + For example: + + BIND=^X:getpos.autodescribe + BIND=\:menu_first_page + BIND=v:loot + + Extended command keys + You can bind multiple keys to the same extended command. Unbind a + key by using "nothing" as the extended command to bind to. You can + also bind the "", "", and "" keys. + + 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 sup- port some of the menu accelerators. Mouse buttons - You can bind "mouse1" or "mouse2" to "nothing", "therecmdmenu", + You can bind "mouse1" or "mouse2" to "nothing", "therecmdmenu", "clicklook", or "mouseaction". - 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- - text", and if bound to same keys, only one of those commands will be - available. Special command can only be bound to a single key. - - count - Prefix key to start a count, to repeat a command this many times. - With number_pad only. Default is `n'. - - getdir.help - When asked for a direction, the key to show the help. Default is - `?'. - - 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 key- - strokes to move the cursor around the map, then type the get- - pos.pick.once key (default `,') or the getpos.pick key (default `.') - to finish as if performing a left or right click. Only useful when - using the #therecmdmenu command. Default is `_'. - - getdir.self - When asked for a direction, the key to target yourself. Default is - `.'. - - getdir.self2 - When asked for a direction, an alternate key to target yourself. - Default is `s'. - - getpos.autodescribe - When asked for a location, the key to toggle autodescribe. Default - is `#'. - - getpos.all.next - When asked for a location, the key to go to next closest interesting - thing. Default is `a'. - - getpos.all.prev - When asked for a location, the key to go to previous closest inter- - esting thing. Default is `A'. - - getpos.door.next - When asked for a location, the key to go to next closest door or - doorway. Default is `d'. - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -5878,6 +5878,52 @@ + 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- + text", and if bound to same keys, only one of those commands will be + available. Special command can only be bound to a single key. + + count + Prefix key to start a count, to repeat a command this many times. + With number_pad only. Default is `n'. + + getdir.help + When asked for a direction, the key to show the help. Default is + `?'. + + 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 key- + strokes to move the cursor around the map, then type the get- + pos.pick.once key (default `,') or the getpos.pick key (default `.') + to finish as if performing a left or right click. Only useful when + using the #therecmdmenu command. Default is `_'. + + getdir.self + When asked for a direction, the key to target yourself. Default is + `.'. + + getdir.self2 + When asked for a direction, an alternate key to target yourself. + Default is `s'. + + getpos.autodescribe + When asked for a location, the key to toggle autodescribe. Default + is `#'. + + getpos.all.next + When asked for a location, the key to go to next closest interesting + thing. Default is `a'. + + getpos.all.prev + When asked for a location, the key to go to previous closest inter- + esting thing. Default is `A'. + + getpos.door.next + When asked for a location, the key to go to next closest door or + doorway. Default is `d'. + getpos.door.prev When asked for a location, the key to go to previous closest door or doorway. Default is `D'. @@ -5885,56 +5931,10 @@ getpos.help When asked for a location, the key to show help. Default is `?'. - getpos.mon.next - When asked for a location, the key to go to next closest monster. - Default is `m'. - - getpos.mon.prev - When asked for a location, the key to go to previous closest mon- - ster. Default is `M'. - - getpos.obj.next - When asked for a location, the key to go to next closest object. - Default is `o'. - - getpos.obj.prev - When asked for a location, the key to go to previous closest object. - Default is `O'. - - 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. De- - fault is `!'. - - 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 `*'. - - 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 be- - tween no filtering, in view only, and in the same area only. De- - fault is `"'. - - getpos.pick - When asked for a location, the key to choose the location, and pos- - sibly 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 `.'. - - 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 - `,'. - - getpos.pick.quick - When asked for a location, the key to choose the location, skip ask- - ing for more info, and exit the location asking loop. Default is - NetHack 3.7.0 September 13, 2024 + + NetHack 3.7.0 May 1, 2025 @@ -5944,26 +5944,86 @@ + getpos.mon.next + When asked for a location, the key to go to next closest monster. + Default is `m'. + + getpos.mon.prev + When asked for a location, the key to go to previous closest mon- + ster. Default is `M'. + + getpos.obj.next + When asked for a location, the key to go to next closest object. + Default is `o'. + + getpos.obj.prev + When asked for a location, the key to go to previous closest object. + Default is `O'. + + 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 `!'. + + 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 `*'. + + 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 `"'. + + getpos.pick + When asked for a location, the key to choose the location, and pos- + sibly 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 `.'. + + 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 + `,'. + + getpos.pick.quick + When asked for a location, the key to choose the location, skip ask- + ing for more info, and exit the location asking loop. Default is `;'. getpos.pick.verbose - When asked for a location, the key to choose the location, and show + 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 + When asked for a location, the key to go to your location. Default is `@'. getpos.unexplored.next - When asked for a location, the key to go to next closest unexplored + When asked for a location, the key to go to next closest unexplored location. Default is `x'. getpos.unexplored.prev - When asked for a location, the key to go to previous closest unex- + When asked for a location, the key to go to previous closest unex- plored location. Default is `X'. getpos.valid - When asked for a location, the key to go to show valid target loca- + When asked for a location, the key to go to show valid target loca- tions. Default is `$'. getpos.valid.next @@ -5971,15 +6031,15 @@ tion. Default is `z'. getpos.valid.prev - When asked for a location, the key to go to previous closest valid + When asked for a location, the key to go to previous closest valid location. Default is `Z'. 9.11. Configuring Message Types - You can change the way the messages are shown in the message + You can change the way the messages are shown in the message area, when the message matches a user-defined pattern. - In general, the configuration file entries to describe the mes- + In general, the configuration file entries to describe the mes- sage types look like this: MSGTYPE=type "pattern" type - how the message should be shown; @@ -5992,81 +6052,21 @@ show - show message normally; hide - never show the message; stop - wait for user with more-prompt; - norep - show the message once, but not again if no other message is + norep - show the message once, but not again if no other message is shown in between. - Here's an example of message types using NetHack's internal pattern + Here's an example of message types using NetHack's internal pattern matching facility: - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 92 - - - MSGTYPE=stop "You feel hungry." MSGTYPE=hide "You displaced *." - specifies that whenever a message "You feel hungry" is shown, the - user is prompted with more-prompt, and a message matching "You dis- + specifies that whenever a message "You feel hungry" is shown, the + user is prompted with more-prompt, and a message matching "You dis- placed ." is not shown at all. - The order of the defined MSGTYPE lines is important; the last match- - ing rule is used. Put the general case first, exceptions below them. - 9.12. Configuring Menu Colors - - Some platforms allow you to define colors used in menu lines when - the line matches a user-defined pattern. At this time the tty, - curses, win32tty and win32gui interfaces support this. - - In general, the configuration file entries to describe the menu - color mappings look like this: - - MENUCOLOR="pattern"=color&attribute - - pattern - the pattern to match; - color - the color to use for lines matching the pattern; - attribute - the attribute to use for lines matching the pat- - tern. 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. - - The pattern should be a regular expression. - - 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 - isn't necessarily the same as any of the other colors. - - Allowed attributes are none, bold, dim, italic, underline, blink, - and inverse. "Normal" is a synonym for "none". Note that the plat- - form used may interpret the attributes any way it wants. - - Here's an example of menu colors using NetHack's internal pattern - matching facility: - - MENUCOLOR="* blessed *"=green - MENUCOLOR="* cursed *"=red - MENUCOLOR="* cursed *(being worn)"=red&underline - - specifies that any menu line with " blessed " contained in it will - be shown in green color, lines with " cursed " will be shown in red, - and lines with " cursed " followed by "(being worn)" on the same - line will be shown in red color and underlined. You can have multi- - ple MENUCOLOR entries in your configuration file, and the last MENU- - COLOR line that matches a menu line will be used for the line. - - - - - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -6076,19 +6076,80 @@ - Note that if you intend to have one or more color specifications - match " uncursed ", you will probably want to turn the implicit_un- - cursed option off so that all items known to be uncursed are actually - displayed with the "uncursed" description. + The order of the defined MSGTYPE lines is important; the last match- + ing rule is used. Put the general case first, exceptions below them. + + 9.12. Configuring Menu Colors + + Some platforms allow you to define colors used in menu lines when + the line matches a user-defined pattern. At this time the tty, + curses, win32tty and win32gui interfaces support this. + + In general, the configuration file entries to describe the menu + color mappings look like this: + + MENUCOLOR="pattern"=color&attribute + + pattern - the pattern to match; + color - the color to use for lines matching the pattern; + attribute - the attribute to use for lines matching the pat- + tern. 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. + + The pattern should be a regular expression. + + 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 + isn't necessarily the same as any of the other colors. + + Allowed attributes are none, bold, dim, italic, underline, blink, + and inverse. "Normal" is a synonym for "none". Note that the plat- + form used may interpret the attributes any way it wants. + + Here's an example of menu colors using NetHack's internal pattern + matching facility: + + MENUCOLOR="* blessed *"=green + MENUCOLOR="* cursed *"=red + MENUCOLOR="* cursed *(being worn)"=red&underline + + specifies that any menu line with " blessed " contained in it will + be shown in green color, lines with " cursed " will be shown in red, + and lines with " cursed " followed by "(being worn)" on the same + line will be shown in red color and underlined. You can have multi- + ple MENUCOLOR entries in your configuration file, and the last MENU- + COLOR line that matches a menu line will be used for the line. + + Note that if you intend to have one or more color specifications + match " uncursed ", you will probably want to turn the + 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 + Some platforms allow you to define sound files to be played when a message that matches a user-defined pattern is delivered to the mes- - sage window. At this time the Qt port and the win32tty and win32gui + sage window. At this time the Qt port and the win32tty and win32gui ports support the use of user sounds. - The following configuration file entries are relevant to mapping + The following configuration file entries are relevant to mapping user sounds to messages: SOUNDDIR @@ -6098,9 +6159,9 @@ An entry that maps a sound file to a user-specified message pattern. Each SOUND entry is broken down into the following parts: - MESG - message window mapping (the only one supported in + MESG - message window mapping (the only one supported in 3.7.0); - msgtype - optional; message type to use, see "Configuring Mes- + msgtype - optional; message type to use, see "Configuring Mes- sage Types" pattern - the pattern to match; sound file - the sound file to play; @@ -6119,65 +6180,65 @@ 9.14. Configuring Status Hilites - Your copy of NetHack may have been compiled with support for - "Status Hilites". If so, you can customize your game display by set- - ting thresholds to change the color or appearance of fields in the + Your copy of NetHack may have been compiled with support for + "Status Hilites". If so, you can customize your game display by set- + ting thresholds to change the color or appearance of fields in the status display. The format for defining status colors is: OPTION=hilite_status:field-name/behavior/color&attributes - For example, the following line in your configuration file will - cause the hitpoints field to display in the color red if your - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 94 - - - - hitpoints drop to or below a threshold of 30%: + For example, the following line in your configuration file will + cause the hitpoints field to display in the color red if your hit- + points drop to or below a threshold of 30%: OPTION=hilite_status:hitpoints/<=30%/red/normal - (That example is actually specifying red&normal for <=30% and no- + (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 + 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: 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 "no-color", the default foreground color on the + gray, orange, light-green, yellow, light-blue, light-magenta, light- + cyan, and white. And "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. "Normal" is a synonym for "none"; they should not be + and inverse. "Normal" is a synonym for "none"; they should not be used in combination with any of the other attributes. - To specify both a color and an attribute, use `&' to combine - them. To specify multiple attributes, use `+' to combine those. For + To specify both a color and an attribute, use `&' to combine + them. To specify multiple attributes, use `+' to combine those. For example: "magenta&inverse+dim". - Note that the display may substitute or ignore particular at- - tributes depending upon its capabilities, and in general may interpret - the attributes any way it wants. For example, on some display systems - a request for bold might yield blink or vice versa. On others, issu- - ing an attribute request while another is already set up will replace - the earlier attribute rather than combine with it. Since NetHack is- - sues attribute requests sequentially (at least with the tty interface) - rather than all at once, the only way a situation like that can be - controlled is to specify just one attribute. + Note that the display may substitute or ignore particular + 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- + 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 + 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 appearance of the following status fields: title dungeon-level experience-level @@ -6189,82 +6250,21 @@ charisma armor-class condition alignment score - The pseudo-field "characteristics" can be used to set all six of - Str, Dex, Con, Int, Wis, and Cha at once. "HD" is "hit dice", an - approximation of experience level displayed when polymorphed. "ex- - perience", "time", and "score" are conditionally displayed depending - upon your other option settings. + The pseudo-field "characteristics" can be used to set all six of + Str, Dex, Con, Int, Wis, and Cha at once. "HD" is "hit dice", an + approximation of experience level displayed when polymorphed. + "experience", "time", and "score" are conditionally displayed + depending upon your other option settings. - - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 95 - - - - Instead of a behavior, "condition" takes the following condition - flags: stone, slime, strngl, foodpois, termill, blind, deaf, stun, + Instead of a behavior, "condition" takes the following condition + flags: stone, slime, strngl, foodpois, termill, blind, deaf, stun, conf, hallu, lev, fly, and ride. You can use "major_troubles" as an - alias for stone through termill, "minor_troubles" for blind through + alias for stone through termill, "minor_troubles" for blind through hallu, "movement" for lev, fly, and ride, and "all" for every condi- tion. - 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. - It overrides other behavior rules if hit points are at or below the - major problem threshold (which varies depending upon maximum hit - points and experience level). - * "always" will set the default attributes for that field. - - * "up", "down" set the field attributes for when the field value - changes upwards or downwards. This attribute times out after - statushilites turns. - - * "changed" sets the field attribute for when the field value - changes. This attribute times out after 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.) - - * percentage sets the field attribute when the field value - matches the percentage. It is specified as a number between 0 - and 100, followed by `%' (percent sign). If the percentage is - 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 - 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- - sponding maximum field. Percentage highlight rules are also - allowed for "experience level" and "experience points" (valid - when the showexp option is enabled). For those, the percentage - is based on the progress from the start of the current experi- - ence level to the start of the next level. 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 =100% is al- - lowed and matches the special case of being exactly 1 experi- - ence point short of the next level. - - * absolute value sets the attribute when the field value matches - that number. The number must be 0 or higher, except for "ar- - mor-class' which allows negative values, and may optionally be - preceded by `='. If the number is preceded by `<=' or `>=' in- - stead, it also matches when value is below or above. If the - prefix is `<' or `>', only match when strictly above or below. - - - - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -6274,19 +6274,79 @@ - * criticalhp only applies to the hitpoints field and only when + 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. + It overrides other behavior rules if hit points are at or below the + major problem threshold (which varies depending upon maximum hit + points and experience level). + + * "always" will set the default attributes for that field. + + * "up", "down" set the field attributes for when the field value + changes upwards or downwards. This attribute times out after + statushilites turns. + + * "changed" sets the field attribute for when the field value + changes. This attribute times out after 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.) + + * percentage sets the field attribute when the field value + matches the percentage. It is specified as a number between 0 + and 100, followed by `%' (percent sign). If the percentage is + 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 + 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- + sponding maximum field. Percentage highlight rules are also + allowed for "experience level" and "experience points" (valid + when the showexp option is enabled). For those, the percentage + is based on the progress from the start of the current experi- + ence level to the start of the next level. 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 =100% is + allowed and matches the special case of being exactly 1 experi- + ence point short of the next level. + + * absolute value sets the attribute when the field value matches + that number. The number must be 0 or higher, except for + "armor-class' which allows negative values, and may optionally + be preceded by `='. If the number is preceded by `<=' or `>=' + instead, it also matches when value is below or above. If the + prefix is `<' or `>', only match when strictly above or below. + + * criticalhp only applies to the hitpoints field and only when current hit points are below a threshold (which varies by maxi- - mum hit points and experience level). When the threshold is - met, a criticalhp rule takes precedence over all other hit- + mum hit points and experience level). When the threshold is + met, a criticalhp rule takes precedence over all other hit- points rules. - * 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 ti- - tle, only the role's rank title is tested; the character's name - is ignored. - The in-game options menu can help you determine the correct syn- + + + 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 + title, only the role's rank title is tested; the character's + name is ignored. + + The in-game options menu can help you determine the correct syn- tax for a configuration file. The whole feature can be disabled by setting option statushilites @@ -6311,40 +6371,41 @@ NetHack can load entire symbol sets from the symbol file. - The options that are used to select a particular symbol set from + The options that are used to select a particular symbol set from the symbol file are: symset Set the name of the symbol set that you want to load. roguesymset - Set the name of the symbol set that you want to load for display on + Set the name of the symbol set that you want to load for display on the rogue level. - You can also override one or more symbols using the SYMBOLS and - ROGUESYMBOLS configuration file options. Symbols are specified as + You can also override one or more symbols using the SYMBOLS and + ROGUESYMBOLS configuration file options. Symbols are specified as name:value pairs. Note that NetHack escape-processes the value string - in conventional C fashion. This means that \ is a prefix to take the - following character literally. Thus \ needs to be represented as \\. - The special prefix form \m switches on the meta bit in the symbol - value, and the ^ prefix causes the following character to be treated - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 97 - - - + in conventional C fashion. This means that \ is a prefix to take the + following character literally. Thus \ needs to be represented as \\. + The special prefix form \m switches on the meta bit in the symbol + value, and the ^ prefix causes the following character to be treated as a control character. NetHack Symbols Symbol Name Description ----------------------------------------------------------------- + + + + NetHack 3.7.0 May 1, 2025 + + + + + + NetHack Guidebook 98 + + + S_air (air) _ S_altar (altar) " S_amulet (amulet) @@ -6393,24 +6454,24 @@ \ S_expl_tr (explosion top right) | S_expl_ml (explosion middle left) S_expl_mc (explosion middle center) - - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 98 - - - | S_expl_mr (explosion middle right) \ S_expl_bl (explosion bottom left) - 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) @@ -6459,24 +6520,24 @@ N S_naga (naga) . S_ndoor (doorway without door) n S_nymph (nymph) - - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 99 - - - O S_ogre (ogre) o S_orc (orc) 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) @@ -6525,24 +6586,24 @@ # S_tree (tree) T S_troll (troll) | S_trwall (wall) - - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 100 - - - - S_tuwall (wall) U S_umber (umber hulk) 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]) @@ -6571,96 +6632,35 @@ Notes: - * Several symbols in this table appear to be blank. They are the + * 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 + don't have any default value and can only be used if enabled in the "sysconf" file. - * S_rock is misleadingly named; rocks and stones use S_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_rock - is only used for boulders and not used at all if overridden by the + * S_rock is misleadingly named; rocks and stones use S_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_rock + is only used for boulders and not used at all if overridden by the more specific S_boulder. 9.16. Customizing Map Glyph Representations Using Unicode - If your platform or terminal supports the display of UTF-8 char- + If your platform or terminal supports the display of UTF-8 char- acter sequences, you can customize your game display by assigning Uni- - code codepoint values and red-green-blue colors to glyph representa- - tions. The customizations can be specified for use with a symset that - has a UTF8 handler within the symbols file such as the enhanced1 set, + code codepoint values and red-green-blue colors to glyph representa- + tions. The customizations can be specified for use with a symset that + has a UTF8 handler within the symbols file such as the enhanced1 set, or individually within your nethack.rc file. - - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 101 - - - The format for defining a glyph representation is: OPTIONS=glyph:glyphid/U+nnnn/R-G-B - The window port that is active needs to provide support for dis- - playing UTF-8 character sequences and explicit red-green-blue colors - 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 - color represented by R-G-B value 0-0-160: - - OPTIONS=glyph:G_pool/U+224B/0-0-160 - - The list of acceptable glyphid's can be produced by nethack --dumpg- - lyphids. Individual NetHack glyphs can be specified using the G_ pre- - fix, or you can use an S_ symbol for a glyphid and store the custom - representation for all NetHack glyphs that would map to that particu- - lar symbol. - - You will need to select a symset with a UTF8 handler to enable - the display of the customizations, such as the Enhanced symset. - - 9.17. Configuring NetHack for Play by the Blind - - 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 - NetHack (which use special line-drawing characters by default) com- - pletely accessible to the blind who use speech and/or Braille access - technologies. Players will require a good working knowledge of their - screen-reader's review features, and will have to know how to navigate - horizontally and vertically character by character. They will also - 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 - 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 - screen-reader should also have a function which 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 loca- - tion of items on the screen. - - 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 "#version" extended command shows "external program as a mes- - sage handler", your NetHack has been compiled with the capability. - When compiling NetHack from source on Linux and other POSIX systems, - define MSGHANDLER to enable it. To use the capability, set the envi- - ronment variable NETHACK_MSGHANDLER to an executable, which will be - executed with the game message as the program's only parameter. - - The most crucial settings to make the game more accessible are: + 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -6670,63 +6670,63 @@ + 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 + color represented by R-G-B value 0-0-160: + + OPTIONS=glyph:G_pool/U+224B/0-0-160 + + The list of acceptable glyphid's can be produced by nethack --dumpg- + lyphids. Individual NetHack glyphs can be specified using the G_ pre- + fix, or you can use an S_ symbol for a glyphid and store the custom + representation for all NetHack glyphs that would map to that particu- + lar symbol. + + You will need to select a symset with a UTF8 handler to enable + the display of the customizations, such as the Enhanced symset. + + 9.17. Configuring NetHack for Play by the Blind + + 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 + NetHack (which use special line-drawing characters by default) com- + pletely accessible to the blind who use speech and/or Braille access + technologies. Players will require a good working knowledge of their + screen-reader's review features, and will have to know how to navigate + horizontally and vertically character by character. They will also + 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 + 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 + screen-reader should also have a function which 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 loca- + tion of items on the screen. + + 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 "#version" extended command shows "external program as a mes- + sage handler", your NetHack has been compiled with the capability. + When compiling NetHack from source on Linux and other POSIX systems, + define MSGHANDLER to enable it. To use the capability, set the envi- + ronment variable NETHACK_MSGHANDLER to an executable, which will be + executed with the game message as the program's only parameter. + + The most crucial settings to make the game more accessible are: + symset:plain Load a symbol set appropriate for use by blind players. menustyle:traditional This will assist in the interface to speech synthesizers. - nomenu_overlay - Show menus on a cleared screen and aligned to the left edge. - - number_pad - A lot of speech access programs use the number-pad to review the - screen. If this is the case, disable the number_pad option and use - the traditional Rogue-like commands. - - paranoid_confirmation:swim - Prevent walking into water or lava. - - accessiblemsg - Adds direction or location information to messages. - - spot_monsters - Shows a message when hero notices a monster; combine with accessi- - blemsg. - - mon_movement - Shows a message when hero notices a monster movement; combine with - spot_monsters and accessiblemsg. - - autodescribe - Automatically describe the terrain under the cursor when targeting. - - mention_map - Give feedback messages when interesting map locations change. - - mention_walls - Give feedback messages when walking towards a wall or when travel - command was interrupted. - - whatis_coord:compass - When targeting with cursor, describe the cursor position with coor- - dinates relative to your character. - - whatis_filter:area - When targeting with cursor, filter possible locations so only those - in the same area (eg. same room, or same corridor) are considered. - - whatis_moveskip - When targeting with cursor and using fast-move, skip the same glyphs - instead of moving 8 units at a time. - - nostatus_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 "#attributes" command. - NetHack 3.7.0 September 13, 2024 + + NetHack 3.7.0 May 1, 2025 @@ -6736,63 +6736,63 @@ + nomenu_overlay + Show menus on a cleared screen and aligned to the left edge. + + number_pad + A lot of speech access programs use the number-pad to review the + screen. If this is the case, disable the number_pad option and use + the traditional Rogue-like commands. + + paranoid_confirmation:swim + Prevent walking into water or lava. + + accessiblemsg + Adds direction or location information to messages. + + spot_monsters + Shows a message when hero notices a monster; combine with accessi- + blemsg. + + mon_movement + Shows a message when hero notices a monster movement; combine with + spot_monsters and accessiblemsg. + + autodescribe + Automatically describe the terrain under the cursor when targeting. + + mention_map + Give feedback messages when interesting map locations change. + + mention_walls + Give feedback messages when walking towards a wall or when travel + command was interrupted. + + whatis_coord:compass + When targeting with cursor, describe the cursor position with coor- + dinates relative to your character. + + whatis_filter:area + When targeting with cursor, filter possible locations so only those + in the same area (eg. same room, or same corridor) are considered. + + whatis_moveskip + When targeting with cursor and using fast-move, skip the same glyphs + instead of moving 8 units at a time. + + nostatus_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 "#attributes" command. + showdamage Give a message of damage taken and how many hit points are left. - 9.18. Global Configuration for System Administrators - - If NetHack is compiled with the SYSCF option, a system adminis- - trator 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 di- - rectory as the other NetHack support files. The options recognized in - this file are listed below. Any option not set uses a compiled-in de- - fault (which may not be appropriate for your system). - - WIZARDS = A space-separated list of user names 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. - - SHELLERS = A list of users who are allowed to use the shell escape - command (!). The syntax is the same as WIZARDS. - - EXPLORERS = A list of users who are allowed to use the explore mode. - The syntax is the same as WIZARDS. - - MAXPLAYERS = Limit the maximum number of games that can be running - at the same time. - - SAVEFORMAT = A list of up to two save file formats separated by - space. The first format in the list will written as well as read. - The second format will be read only if no save file in the first - format exists. Valid choices are "historical" for binary writing of - entire structs, "lendian" for binary writing of each field in lit- - tle-endian order, "ascii" for writing the save file content in ascii - text. - - BONESFORMAT = A list of up to two bones file formats separated by - space. The first format in the list will written as well as read. - The second format will be read only if no bones files in the first - format exist. Valid choices are "historical" for binary writing of - entire structs, "lendian" for binary writing of each field in lit- - tle-endian order, "ascii" for writing the bones file content in - ascii text. - - SUPPORT = A string explaining how to get local support (no default - value). - - RECOVER = A string explaining how to recover a game on this system - (no default value). - - SEDUCE = 0 or 1 to disable or enable, respectively, the SEDUCE op- - tion. When disabled, incubi and succubi behave like nymphs. - - CHECK_PLNAME = Setting this to 1 will make the EXPLORERS, WIZARDS, - and SHELLERS check for the player name instead of the user's login - NetHack 3.7.0 September 13, 2024 + + + NetHack 3.7.0 May 1, 2025 @@ -6802,9 +6802,48 @@ + 9.18. Global Configuration for System Administrators + + If NetHack is compiled with the SYSCF option, a system adminis- + trator 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 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). + + WIZARDS = A space-separated list of user names 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. + + SHELLERS = A list of users who are allowed to use the shell escape + command (!). The syntax is the same as WIZARDS. + + EXPLORERS = A list of users who are allowed to use the explore mode. + The syntax is the same as WIZARDS. + + 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. + + MAXPLAYERS = Limit the maximum number of games that can be running + at the same time. + + SUPPORT = A string explaining how to get local support (no default + value). + + RECOVER = A string explaining how to recover a game on this system + (no default value). + + SEDUCE = 0 or 1 to disable or enable, respectively, the SEDUCE + option. When disabled, incubi and succubi behave like nymphs. + + CHECK_PLNAME = Setting this to 1 will make the EXPLORERS, WIZARDS, + and SHELLERS check for the player name instead of the user's login name. - CHECK_SAVE_UID = 0 or 1 to disable or enable, respectively, the UID + CHECK_SAVE_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). @@ -6814,28 +6853,40 @@ ENTRYMAX = Maximum number of entries in the score file. - POINTSMIN = Minimum number of points to get an entry in the score + POINTSMIN = Minimum number of points to get an entry in the score file. - PERS_IS_UID = 0 or 1 to use user names or numeric userids, respec- + + + 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. - HIDEUSAGE = 0 or 1 to control whether the help menu entry for com- + HIDEUSAGE = 0 or 1 to control whether the help menu entry for com- mand line usage is shown or suppressed. - MAX_STATUENAME_RANK = Maximum number of score file entries to use + MAX_STATUENAME_RANK = Maximum number of score file entries to use for random statue names (default is 10). ACCESSIBILITY = 0 or 1 to disable or enable, respectively, the abil- ity for players to set S_pet_override and S_hero_override symbols in their configuration file. - PORTABLE_DEVICE_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 + PORTABLE_DEVICE_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. - DUMPLOGFILE = A filename where the end-of-game dumplog is saved. - Not defining this will prevent dumplog from being created. Only + 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 follow- ing placeholders: @@ -6849,82 +6900,31 @@ %n - player name %N - first character of player name - 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 - the various bits used. Intended for server systems supporting si- - multaneous play by multiple players (to be clear, each one running a - separate single player game), for displaying their game progress to - observers. Only relevant if the program was built with LIVELOG - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 105 - - - - enabled. When available, it should be left commented out on single - player installations because over time the file could grow to be ex- - tremely large unless it is actively maintained. + 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 + 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), for displaying their game progress + to observers. Only relevant if the program was built with LIVELOG + enabled. 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. CRASHREPORTURL = If set to https://www.nethack.org/links/cr-37BETA.html and support is compiled - in, brings up a browser window pre-populated with the information - needed to report a problem if the game panics or ends up in an in- - ternally inconsistent state, or if the #bugreport command is in- - voked. + in, brings up a browser window pre-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. 10. Scoring - NetHack maintains a list of the top scores or scorers on your ma- - chine, depending on how it is set up. In the latter case, each ac- - count 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 - NetHack is compiled. - - Your score is chiefly based upon how much experience you gained, - how much loot you accumulated, how deep you explored, and how the game - ended. If you quit the game, you escape with all of your gold intact. - If, however, you get killed in the Mazes of Menace, the guild will - only hear about 90% of your gold when your corpse is discovered (ad- - venturers have been known to collect finder's fees). So, consider - whether you want to take one last hit at that monster and possibly - live, or quit and stop with whatever you have. If you quit, you keep - all your gold, but if you swing and live, you might find more. - - If you just want to see what the current top players/games list - is, you can type nethack -s all on most versions. - - 11. Explore mode - - 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 - paltry cost of not getting on the high score list. - - There are two ways of enabling explore mode. One is to start the - game with the -X command-line switch or with the playmode:explore op- - tion. The other is to issue the "#exploremode" extended command while - already playing the game. Starting a new game in explore mode pro- - vides your character with a wand of wishing in initial inventory; - switching during play does not. The other benefits of explore mode - are left for the trepid reader to discover. - - 11.1. Debug mode - - Debug mode, also known as wizard mode, is undocumented aside from - this brief description and the various "debug mode only" commands + 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 September 13, 2024 + NetHack 3.7.0 May 1, 2025 @@ -6934,42 +6934,95 @@ - listed among the command descriptions. It is intended for tracking - down problems within the program rather than to provide god-like pow- - ers 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 -D command-line switch or with the playmode:debug + 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 + NetHack is compiled. + + Your score is chiefly based upon how much experience you gained, + how much loot you accumulated, how deep you explored, and how the game + ended. If you quit the game, you escape with all of your gold intact. + If, however, you get killed in the Mazes of Menace, the guild will + only hear about 90% of your gold when your corpse is discovered + (adventurers have been known to collect finder's fees). So, consider + whether you want to take one last hit at that monster and possibly + live, or quit and stop with whatever you have. If you quit, you keep + all your gold, but if you swing and live, you might find more. + + If you just want to see what the current top players/games list + is, you can type nethack -s all on most versions. + + 11. Explore mode + + 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 + paltry cost of not getting on the high score list. + + There are two ways of enabling explore mode. One is to start the + game with the -X command-line switch or with the playmode:explore + 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; + switching during play does not. The other benefits of explore mode + are left for the trepid reader to discover. + + 11.1. Debug mode + + Debug mode, also known as wizard mode, is undocumented aside from + this brief description and the various "debug mode only" commands + listed among the command descriptions. It is intended for tracking + down problems within the program rather than to provide god-like pow- + ers 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 -D command-line switch or with the playmode:debug option. 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 Wizard role). Attempting to - start a game in debug mode when not allowed or not available will re- - sult in falling back to explore mode instead. + 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 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. + + + + + NetHack 3.7.0 May 1, 2025 + + + + + + NetHack Guidebook 107 + + 12. Credits - The original hack game was modeled on the Berkeley UNIX rogue + The original hack game was modeled on the Berkeley UNIX rogue game. Large portions of this document were shamelessly cribbed from A - Guide to the Dungeons of Doom, by Michael C. Toy and Kenneth C. R. C. - Arnold. Small portions were adapted from Further Exploration of the + Guide to the Dungeons of Doom, by Michael C. Toy and Kenneth C. R. C. + Arnold. Small portions were adapted from Further Exploration of the Dungeons of Doom, by Ken Arromdee. - NetHack is the product of literally scores of people's work. + NetHack is the product of literally scores of people's work. Main events in the course of the game development are described below: - Jay Fenlason wrote the original Hack, with help from Kenny Wood- + Jay Fenlason wrote the original Hack, with help from Kenny Wood- land, Mike Thome, and Jon Payne. - Andries Brouwer did a major re-write while at Stichting Mathema- - tisch Centrum (now Centrum Wiskunde & Informatica), transforming Hack + Andries Brouwer did a major re-write while at Stichting Mathema- + tisch 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 net.sources (later - renamed 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 net.games.hack (later renamed rec.games.hack, eventually re- - placed by rec.games.roguelike.nethack) was created for discussing it. + renamed 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 net.games.hack (later renamed rec.games.hack, eventually + replaced by rec.games.roguelike.nethack) was created for discussing + it. Don G. Kneller ported Hack 1.0.3 to Microsoft C and MS-DOS, pro- ducing PC HACK 1.01e, added support for DEC Rainbow graphics in ver- @@ -6980,26 +7033,14 @@ R. Black ported PC HACK 3.51 to Lattice C and the Atari 520/1040ST, producing ST Hack 1.03. - Mike Stephenson merged these various versions back together, in- - corporating many of the added features, and produced NetHack version + 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 debugging NetHack 1.4 and released 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 ftp and uucp after expiring from the newsgroup. - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 107 - - - Later, Mike coordinated a major re-write of the game, heading a team which included Ken Arromdee, Jean-Christophe Collet, Steve Creps, Eric Hendrickson, Izchak Miller, Eric S. Raymond, John Rupley, Mike @@ -7013,6 +7054,18 @@ 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. @@ -7024,8 +7077,8 @@ began to be used with 3.1.0. Headed by Mike Stephenson and coordinated by Izchak Miller and - Janet Walz, the NetHack Development Team which now included Ken Ar- - romdee, David Cohrs, Jean-Christophe Collet, Kevin Darcy, Matt Day, + Janet Walz, the NetHack Development Team which now included Ken + Arromdee, David Cohrs, Jean-Christophe Collet, Kevin Darcy, Matt Day, Timo Hakulinen, Steve Linhart, Dean Luick, Pat Rankin, Eric Raymond, and Eric Smith undertook a radical revision of 3.0. They re-struc- tured the game's design, and re-wrote major parts of the code. They @@ -7048,30 +7101,18 @@ House added a Think C port. Timo Hakulinen ported NetHack 3.1 to OS/2. Eric Smith ported - NetHack 3.1 to the Atari. Pat Rankin, with help from Joshua De- - lahunty, was responsible for the VMS version of NetHack 3.1. Michael - Allison ported NetHack 3.1 to Windows NT. + NetHack 3.1 to the Atari. Pat Rankin, with help from Joshua + Delahunty, was responsible for the VMS version of NetHack 3.1. + Michael Allison ported NetHack 3.1 to Windows NT. Dean Luick, with help from David Cohrs, developed NetHack 3.1 for X11. It drew the map as text rather than graphically but included - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 108 - - - 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 "a" in- - sects and another for all "[" armor and so forth, not separate images - for beetles and ants or for cloaks and boots). + for monster and object classes (so one custom image for all "a" + insects and another for all "[" armor and so forth, not separate + images for beetles and ants or for cloaks and boots). Warwick Allison wrote a graphically displayed version of NetHack for the Atari where the tiny pictures were described as "icons" and @@ -7079,6 +7120,18 @@ 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). @@ -7120,18 +7173,6 @@ The final update to 3.2 was the bug fix release 3.2.3, which was released simultaneously with 3.3.0 in December 1999 just in time for - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 109 - - - the Year 2000. Because of the newer version, 3.2.3 was released as a source code patch only, without any ready-to-play distribution for systems that usually had such. @@ -7142,15 +7183,26 @@ 1999's year 99 was followed by 2000's year 100. That got written out successfully but it unintentionally introduced an extra column in the file layout which prevented score entries from being read back in cor- - rectly, interfering with insertion of new high scores and with re- - trieval of old character names to use for random ghost and statue + rectly, interfering with insertion of new high scores and with + 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 - Rankin, Eric Smith, Mike Stephenson, Janet Walz, and Paul Winner, re- - leased 3.3.0 in December 1999 and 3.3.1 in August of 2000. + Rankin, Eric Smith, Mike Stephenson, Janet Walz, and Paul Winner, + released 3.3.0 in December 1999 and 3.3.1 in August of 2000. Version 3.3 offered many firsts. It was the first version to sep- arate race and profession. The Elf class was removed in preference to @@ -7186,18 +7238,6 @@ Yitzhak Sapir maintained and enhanced 3.4 for the Microsoft Windows platform. Alex Kompel contributed a new graphical interface for the Windows port. Alex Kompel also contributed a Windows CE port for - - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 110 - - - 3.4.1. Ron Van Iwaarden was the sole maintainer of NetHack for OS/2 the @@ -7211,17 +7251,30 @@ 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 than a decade. The NetHack Development Team slowly and quietly contin- ued 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 NetHack community. Notably sporkhack by Derek S. Ray, un- - nethack by Patric Mueller, nitrohack and its successors originally by - Daniel Thaler and then by Alex Smith, and Dynahack by Tung Nguyen. - Some of those variants continue to be developed, maintained, and en- - joyed by the community to this day. + within the NetHack community. Notably sporkhack by Derek S. Ray, + unnethack by Patric Mueller, nitrohack and its successors originally + by Daniel Thaler and then by Alex Smith, and Dynahack by Tung Nguyen. + Some of those variants continue to be developed, maintained, and + enjoyed by the community to this day. In September 2014, an interim snapshot of the code under develop- ment was released publicly by other parties. Since that code was a @@ -7235,13 +7288,13 @@ In January 2015, preparation began for the release of NetHack 3.6. - At the beginning of development for what would eventually get re- - leased as 3.6.0, the NetHack Development Team consisted of Warwick Al- - lison, Michael Allison, Ken Arromdee, David Cohrs, Jessie Collet, Ken - Lorber, Dean Luick, Pat Rankin, Mike Stephenson, Janet Walz, and Paul - Winner. In early 2015, ahead of the release of 3.6.0, new members - Sean Hunt, Pasi Kallinen, and Derek S. Ray joined the NetHack Develop- - ment Team. + At the beginning of development for what would eventually get + released as 3.6.0, the NetHack Development Team consisted of Warwick + Allison, Michael Allison, Ken Arromdee, David Cohrs, Jessie Collet, + Ken Lorber, Dean Luick, Pat Rankin, Mike Stephenson, Janet Walz, and + Paul Winner. In early 2015, ahead of the release of 3.6.0, new mem- + bers Sean Hunt, Pasi Kallinen, and Derek S. Ray joined the NetHack + Development Team. 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 @@ -7253,17 +7306,6 @@ community patches. Many bugs were fixed and some code was restruc- tured. - - NetHack 3.7.0 September 13, 2024 - - - - - - NetHack Guidebook 111 - - - The NetHack Development Team, as well as Steve VanDevender and Kevin Smolkowski, ensured that NetHack 3.6 continued to operate on various UNIX flavors and maintained the X11 interface. @@ -7275,6 +7317,19 @@ 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 @@ -7301,8 +7356,8 @@ NetHack 3.6.3 was released on December 5, 2019 containing over 190 bug fixes to NetHack 3.6.2. - NetHack 3.6.4 was released on December 18, 2019 containing a se- - curity fix and a few bug fixes. + NetHack 3.6.4 was released on December 18, 2019 containing a + security fix and a few bug fixes. NetHack 3.6.5 was released on January 27, 2020 containing some security fixes and a small number of bug fixes. @@ -7310,8 +7365,8 @@ NetHack 3.6.6 was released on March 8, 2020 containing a security fix and some bug fixes. - NetHack 3.6.7 was released on February 16, 2023 containing a se- - curity fix and some bug fixes. + NetHack 3.6.7 was released on February 16, 2023 containing a + security fix and some bug fixes. The official NetHack web site is maintained by Ken Lorber at https://www.nethack.org/. @@ -7320,13 +7375,24 @@ - NetHack 3.7.0 September 13, 2024 - NetHack Guidebook 112 + + + + + + + NetHack 3.7.0 May 1, 2025 + + + + + + NetHack Guidebook 113 @@ -7337,8 +7403,8 @@ NetHack server at nethack.alt.org. Thanks to Keith Simpson and Andy 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, de- - vnull.net (gone for now, but not forgotten). + as Junethack, The November NetHack Tournament, and in days past, + devnull.net (gone for now, but not forgotten). @@ -7386,13 +7452,13 @@ sends a particularly intriguing modification to help out with the - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 - NetHack Guidebook 113 + NetHack Guidebook 114 @@ -7452,13 +7518,13 @@ - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 - NetHack Guidebook 114 + NetHack Guidebook 115 @@ -7518,13 +7584,13 @@ - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 - NetHack Guidebook 115 + NetHack Guidebook 116 @@ -7584,7 +7650,7 @@ - NetHack 3.7.0 September 13, 2024 + NetHack 3.7.0 May 1, 2025 diff --git a/doc/config.nh b/doc/config.nh index e1903861b..920074b3e 100644 --- a/doc/config.nh +++ b/doc/config.nh @@ -1,7 +1,10 @@ -# NetHack 3.7 config.nh $NHDT-Date: 1596498144 2020/08/03 23:42:24 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.4 $ +# NetHack 3.7 config.nh $NHDT-Date: 1745978702 2025/04/29 18:05:02 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.10 $ # Copyright (c) 2016 by Pasi Kallinen # NetHack may be freely redistributed. See license for details. -# Sample config file for NetHack 3.7.0 +# +# Sample run-time configuration file for NetHack 3.7.0 +# usually named ".nethackrc" (note leading dot; location +# depends upon the type of system nethack is running on). # # A '#' at the beginning of a line means the rest of the line is a comment. # @@ -177,9 +180,17 @@ # Default is 'inverse' #OPTIONS=menu_headings:inverse -# Show object symbols in menus in the item class lines where that object symbol -# acts as an accelerator key to select items of that class. -#OPTIONS=menu_objsyms +# Control of displaying object symbols in menus for picking objects: +# show symbols in individual menu entries only if there are no class separators +#OPTIONS=menu_objsyms:4 +# show symbols on object class separator lines and also in individual entries +#OPTIONS=menu_objsyms:3 +# show symbols in individual entries but not on class separators +#OPTIONS=menu_objsyms:2 +# show object symbols on class separators but not in individual entries +#OPTIONS=menu_objsyms:1 +# don't show symbols on class separators or in individual menu entries +#OPTIONS=menu_objsyms:0 # Do not clear the screen before drawing menus, and align menus to right #OPTIONS=menu_overlay @@ -487,7 +498,7 @@ # Show all unholy water in red #MENUCOLOR=" unholy " = red # Show all cursed worn items in orange and underlined -#MENUCOLOR=" cursed .* (being worn)" = orange&underline +#MENUCOLOR=" cursed .* \(being worn\)" = orange&underline # Use the IBM character set rather than just plain ascii characters diff --git a/doc/dlb.6 b/doc/dlb.6 index 785120aa2..b58b12164 100644 --- a/doc/dlb.6 +++ b/doc/dlb.6 @@ -1,40 +1,73 @@ -.\" $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.12 $ $NHDT-Date: 1693253316 2023/08/28 20:08:36 $ -.\"DO NOT REMOVE NH_DATESUB .TH DLB 6 "DATE(%-d %B %Y)" NETHACK -.TH DLB 6 "8 February 2022" NETHACK -.\"DO NOT REMOVE NH_DATESUB .ds Nd DATE(%Y) -.ds Nd 2022 +.\" $NHDT-Branch: master $:$NHDT-Revision: 1.14 $ $NHDT-Date: 1735103831 2024/12/25 00:17:11 $ +.\"DO NOT REMOVE NH_DATESUB .TH DLB 6 "Date(%-d %B %Y)" Project(uc) +.TH DLB 6 "25 December 2024" NETHACK +.\"DO NOT REMOVE NH_DATESUB .ds Nd Date(%Y) +.ds Nd 2024 .de NB .ds Nb \\$2 .. .de NR .ds Nr \\$2 .. -.NB $NHDT-Branch: NetHack-3.7 $ -.NR $NHDT-Revision: 1.12 $ +.NB $NHDT-Branch: keni-gitset $ +.NR $NHDT-Revision: 1.13 $ +.\" groff and AT&T-descended troffs use different hyphenation patterns. +.\" Don't hyphenate the last word on a page or column, or +.\" before/after last/first 2 characters of a word. +.ie \n(.g .hy 12 +.el .hy 14 .ds Na Kenneth Lorber .SH NAME dlb \- NetHack data librarian .SH SYNOPSIS .B dlb -{ -.B xct -} -[ -.B vfIC -] -arguments... -[ -.B files... -] -.SH DESCRIPTION +.\" We'd use `RB` with 7 arguments, but Unix troff man(7) has a limit of +.\" 6 arguments to its macros. +{\c +.BR c | t | x\c +}\c +.RB [ v ]\c +.RB [ C +.IR directory ] +.RI [ file ] +\&.\|.\|. .PP +.B dlb +{\c +.BR c | t | x\c +}\c +.RB [ v ]\c +.B I +.IR list-file +.PP +.B dlb +{\c +.BR c | t | x\c +}\c +.RB [ v ]\c +.RB [ f +.IR archive-file-name ] +.RI [ file ] +\&.\|.\|. +.SH DESCRIPTION .I 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. +is a file archiving tool in the spirit (and tradition) of +.IR tar (1) +for +.IR 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 +.IR tar , +the letters specifying the operation and options +are expressed as a continuous string. +Unlike +.IR tar , +.I dlb +is configured with a default set of file names to process. +.ig .PP ^?ALLDOCS This facility is optional and may be excluded during NetHack @@ -48,61 +81,76 @@ This facility is optional and was excluded from this NetHack configuration. ^. ^. -.SH COMMANDS -The -.B x -command causes -.I dlb -to extract the contents of the archive into the current directory. -.PP -The +.. +.SS Operations .B c -command causes +causes .I dlb to create a new archive from files in the current directory. .PP -The .B t -command lists the files in the archive. -.SH OPTIONS AND ARGUMENTS -.DT -.ta \w'f archive\ \ \ 'u -v verbose output -.br -.sp 1 -f archive specify the archive. Default if f not specified is -LIBFILE (usually the nhdat file in the playground). -.br -.sp 1 -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. -.br -.sp 1 -C dir change directory. Changes directory before trying to -read any files (including the archive and the lfile). -.br +lists the files in the archive. +.PP +.B x +causes +.I dlb +to extract the contents of the archive into the current directory. +.SH OPTIONS +.TP 13n \" "I list-file" + 2n +.BI "C " dir +Change directory to +.I dir +before trying to +read any files. +.TP +.BI "f " archive +Read from or write to +.I archive +instead of LIBFILE +(usually the +.I nhdat +file in the playground). +.TP +.BI "I " list-file +Read from +.I list-file +the names of files to emplace within or extract from the archive. +The default for archive creation is LIBLISTFILE. +.TP +.B v +Operate verbosely. .SH EXAMPLES Create the default archive from the default file list: -.br - dlb c -.sp 1 -List the contents of the archive 'foo': -.br - dlb tf foo -.SH AUTHOR +.RS +.EX +dlb c +.EE +.RE .PP +List the contents of the archive +.IR foo : +.RS +.EX +dlb tf foo +.EE +.RE +.SH AUTHOR Kenneth Lorber .SH "SEE ALSO" -.PP -nethack(6), tar(1) +.IR nethack (6), +.IR tar (1) .SH BUGS -.PP -Not a good tar emulation; - does not mean stdin or stdout. +.IP \(bu 2n +Not a good +.I tar +emulation; +.B - +does not mean stdin or stdout. +.IP \(bu Should include an optional compression facility. +.IP \(bu 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. +examining the source is the only way to know which files can be. .SH COPYRIGHT This file is Copyright (C) \*(Na, \*(Nd for version \*(Nb:\*(Nr. NetHack may be freely redistributed. See license for details. diff --git a/doc/dlb.txt b/doc/dlb.txt index f185c3b2a..412c19820 100644 --- a/doc/dlb.txt +++ b/doc/dlb.txt @@ -1,4 +1,4 @@ -DLB(6) DLB(6) +DLB(6) Games Manual DLB(6) @@ -61,10 +61,10 @@ BUGS way to know which files can be. COPYRIGHT - This file is Copyright (C) Kenneth Lorber, 2022 for version - NetHack-3.7:1.12. NetHack may be freely redistributed. See license - for details. + This file is Copyright (C) Kenneth Lorber, 2024 for version keni-git- + set:1.13. NetHack may be freely redistributed. See license for + details. -NETHACK 8 February 2022 DLB(6) +NETHACK 25 December 2024 DLB(6) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index b30fbb712..fb87d1121 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.1484 $ $NHDT-Date: 1726862062 2024/09/20 19:54:22 $ +NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1535 $ $NHDT-Date: 1740629713 2025/02/26 20:15:13 $ General Fixes and Modified Features ----------------------------------- @@ -121,7 +121,8 @@ tribute (Discworld snippets) typos, in book order rather than fix order: quote, passage #6 first footnote, insert omitted "be", passage #7 last paragraph, "to" -> "be" Men at Arms passage #1, italicize /for/, passage #2, insert omitted - word "had": 'it was /fate/ that _had_ let Edward' + word "had": 'it was /fate/ that _had_ let Edward', #9, fix name typo + "Noddy" -> "Nobby" Interesting Times passage #1, italicize several words Feet of Clay passage #1, second "does not need" -> "doesn't even need" Hogfather passage #7 missing initial double quote for "Oh, just ...", @@ -131,7 +132,8 @@ tribute (Discworld snippets) typos, in book order rather than fix order: missing opening single quote on second sentence of Lord Downey's line, passage #11 both in footnote: "genious" -> "genius", "was, oddly enough, was one [...]" -> "was, oddly enough, one [...]" - The Fifth Elephant #1, italicize /always/, #9, "dublet" -> "doublet" + The Fifth Elephant #1, italicize /always/, #9, "dublet" -> "doublet", + #12, fix name typo, "Vines" -> "Vimes" The Truth #1, italicize several words Thief of Time #2, "gold starts" -> "gold stars" A Hat Full of Sky passage #9 "though" -> "thought" @@ -959,9 +961,11 @@ a hero on the quest home level who runs or travels past the quest leader and gets tossed out of the quest for some reason would keep running on the far side of the quest portal allow rush/run over water if wearing discovered water walking boots +putting on water walking boots while underwater (maybe via magical breathing) + and rising to surface wasn't causing the boots to become discovered flying pets wouldn't target underwater food but if they happened to fly over such food they could and would eat it -praying on an altar with pet corpse on it can revive the pet +praying on an altar with pet corpse or statue on it can revive the pet applying a cursed oil lamp can make your hands slippery valkyries start with a spear instead of a long sword grid bugs don't have hands @@ -987,7 +991,7 @@ bigroom variant 3 may have some walls replaced with other terrain bigroom variant 4 may have two large squares of terrain in the middle bigroom variant 5 may have patches of ice or clouds some large monsters can knock back smaller monsters with a hit -change Demonbane to a mace, make it the first sac gift for priests, +change Demonbane to a silver mace, make it the first sac gift for priests, and give it an invoke ability to banish demons wielding Giantslayer prevents knockback from larger monsters; likewise for carried loadstone(s) @@ -1237,7 +1241,7 @@ when a werecreature in human form attacked hero, it could transform to critter 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 -allow defining random-teleport exclusion zones in lua +allow defining random-teleport or monster-generation exclusion zones in lua if Magicbane cancelled a shapeshifter, forcing it to 'unshift', subsequent messages continued to refer to the shifted form a pet that was poison resistant but not stoning resistant would eat Medusa's @@ -1473,6 +1477,57 @@ interactively setting a status highlight for hunger with 'O' and choosing use C++ regex processing but not for tty+posixregex a pet with the hides-under attribute could "move reluctantly over" a cursed object and then hide under it +prevent monster generation in the sokoban trap hallway +change MSGHANDLER from compile-time to sysconf option +allow changing extended command autocompletions via #optionsfull +if eating a tin's contents caused the hero to choke to death or turn to stone, + resulting bones would contain the tin still intact +when hero who is poly'd into metallivore form eats a tin, bypass "smells like + " feedback and the "Eat it?" prompt; just eat the contents + along with the tin without asking +digging in ice was handled inconsistently, particularly if done at the span + spot in front of closed drawbridge +angry god may remove an intrinsic +gelatinous cubes eat organic objects inside them +pets with reflection were unwilling to attack floating eyes +artifact gifts are rebalanced (easier to obtain; higher-value sacrifices are + needed for higher-value artifacts; lower-value artifacts are usually + gifted enchanted; unaligned artifacts are possible but rare even on + the first gift; artifacts you can't use well are less likely) +luck gains from sacrificing are limited by the value of the sacrifice +failed #untrap could move hero diagonally into or out of an open doorway +remember box is trapped after finding the trap +when you hear a monster incant a scroll, ensure that the 'I' invisible + monster indicator doesn't trump telepathy briefly +proceed with showpaths option even if the sysconf file is missing +angry shopkeeper was not charging for thrown items +avoid "You fall down a deep shaft!" if deliberately flying down +since introduction in 3.1.0, the definition for Mitre of Holiness has specified + that carrying it provides fire resistance, but that had never been + implemented; wearing it didn't confer fire resistance either--there is + no 'defends' capability for it since carrying should encompass that +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 +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 +hitting with Ogresmasher gives a higher chance of knockback +Snickersnee can hit at a distance once per turn for free +the engraving pristine text field was not being appropriately populated during + level creation +worn alchemy smock reduces chances of dipped potions exploding +dwarves can sense buried items under their feet +monsters trapped in pits cannot kick +create familiar spell can create harder creatures +a vampire lord could choose to take on wolf form while flying over water or + lava, then revert to vampire lord form and teleport unnecessarily +a gas spore that was killed when an engulfer swallowed it produced an explosion + on the map rather than inside the engulfer +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 Fixes to 3.7.0-x General Problems Exposed Via git Repository @@ -2051,6 +2106,28 @@ non-fireproof water walking boots wouldn't be burnt up if fire resistant hero at wall locations if a pile of objects had plain arrow(s) on top, map location classification got confused and reported 'unexplored area' +when the 'fireassist' option was on, 'f' would choose uquiver over wielded + throw-and-return uwep +knockback could move hero diagonally into or out of an open doorway +farlook of /e or /E stopped reporting engraving and headstone text after a fix + for the situation where // or ; reported such text when there was an + object or monster in the way +message was garbled when lightning or acid breath hit iron bars +shop bug: buying a container with unpaid items in it could produce impossible + "unpaid_cost: object not on any bill" warnings +when walking into/against a locked closed door, 'autounlock'==kick didn't + execute kick when player answered yes to "Door is locked. Kick it?" +having a 1% chance of creating rideable monsters with worn saddle gave knights + a 1% chance of creating an extra saddle for starting pony; it wasn't + tracked with other objects so produced a trivial memory leak +when attacking a displacer beast, using the 'F' forcefight prefix prevented it + from swapping places with the hero +successfully disarming a chest trap was clearing the chest's 'tknown' bit + instead of setting it; likewise when failing to disarm triggered and + used up the trap +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 Fixes to 3.7.0-x Platform and/or Interface Problems Exposed Via git Repository @@ -2108,6 +2185,7 @@ Qt: with a pick-one menu containing a pre-selected entry, clicking on that instead of typing the selector letter toggled off preselected entry but failed to toggle on the explicitly selected one; clicking again after nothing was pre-selected anymore refused to toggle anything on +Qt: don't show main window and tombstone window maximized by default tty: redraw unexplored locations as S_unexplored rather than after map has been partially overwritten by popup menu or text display tty: previous change resulted in remnants of previous level being shown on @@ -2161,6 +2239,9 @@ X11: when trying to lookup scrollbars in order to handle scrolling via keys, X11: enable horizontal scrollbar for persistent inventory window X11: if a group accelerator matched a menu command ('^' in menu for '/') it wouldn't work to select, just to manipulate the menu +X11: ascii_map could try to use a color value, with some added flag bits set, + as an index into the color_gcs[] and inv_color_gcs[] arrays leading + to a segfault; clear the flag bits before using the color as an index X11+macOS: after the "bad Atom" fix (below), the persistent inventory window crept downward every time it got updated @@ -2204,7 +2285,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: allow changing default colors with the 'palette' config option +curses+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 @@ -2375,7 +2456,12 @@ tty: add support for petattr tty: if a ^C interrupt occurred while DECgraphics characters were being drawn on the map, the "Really quit?" prompt would be illegible due to being rendered with VT line drawing characters -tty: hide cursor unless waiting for user input +tty: hide cursor unless waiting for user input; now extended to include tty + platforms that define NO_TERMS, rather than just on those using + termcap/terminfo, namely Windows console and msdos (text-mode + implemented; vga and vesa just have stubs currently) +tty+curses: if terminal goes away while in the wishing prompt, return to the + wish prompt when game is restored Unix: when user name is used as default character name, keep hyphenated value intact instead stripping off dash and whatever follows as if that specified role/race/&c (worked once upon a time; broken since 3.3.0) @@ -2387,6 +2473,8 @@ Unix: re-do command line parsing Unix: add ../include/nhlua.h to the alloc.o dependencies in Makefile.utl to match Makefile.src 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 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 @@ -2490,6 +2578,8 @@ item-using monsters will zap wand of undead turning at corpse-wielding hero when the corpse is harmful boiling a pool or fountain now creates a temporary cloud of steam random themed rooms in the dungeons of doom +environment variables THEMERM and THEMERMFILL can be used in wizard mode to + provide some control over theme room generation extended achievement and conduct fields for xlogfile record amount of gold in hero's possession in xlogfile new objects: amulets of flying and guarding, helm of caution, @@ -2566,7 +2656,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 variant, a boulder maze +new bigroom variants, a boulder maze and hexagons 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 @@ -2710,6 +2800,20 @@ livelog/#chronicle for saving-grace: if saving-grace prevents hero's death, 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 +'selectsaved' lists "name-role-race-gend-algn" instead of just "name" in the + menu of save files available to be restored +'selectsaved' prefixes "name-role-race-gend-algn" with "- " for normal play, + "X " for explore mode, or "D " for debug mode if any of the games + shown in its menu weren't saved during normal play; if they're all + normal play, the prefix is suppressed +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 +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 Platform- and/or Interface-Specific New Features @@ -2806,7 +2910,7 @@ always print a message when the hero teleports (pr #265 by copperwater) always print a message when the hero level teleports (pr #265 by copperwater) remove Sokoban luck penalties for actions you can't cheat with (pr #260 by copperwater) -sounds for minotaurs (pr #298 by NullCGT) +sounds for minotaurs (pr #298 by Kestrel Gregorich-Trevor) correct the Guidebook descriptions for msdos video_width and video_height to state that they work with video:vesa; the video:vga setting that was described there forces the 640x480x16 mode where video_width and @@ -2820,7 +2924,7 @@ allow themed rooms constrained by level difficulty (pr #344 by copperwater) add a varied form of LIBNH nethack library contribution (pr #385, #403 by apowers313) add cross-compile to WASM (pr #385, #403, #412 by apowers313) -differentiating gendered monster tiles (pr #430 by NullCGT) +differentiating gendered monster tiles (pr #430 by Kestrel Gregorich-Trevor) check bones data directly for deja vu messages (pr #374 by entrez) unify code for extracting an object from a monster's inventory (pr #455 by copperwater) @@ -2847,9 +2951,8 @@ use %lu, not %d, in format string in timer_sanity_check() (pr #617 by argrath) bad cast making sp_lev chameleon light source (pr #625 by entrez) add Ray Chason's adaptation of nethack's Qt5 interface to work with Qt6 (issue #525 followup comment by chasonr) -mingw32 build updates and replacement of sys/windows/Makefile.gcc with new - sys/windows/Makefile.mingw32 and sys/windows/Makefile.mingw32.depend - (pr #661 by feiyunw) +mingw32 build updates to replace contents of sys/windows/GNUmakefile and new + sys/windows/GNUmakefile.depend (pr #661 by feiyunw) mark various pointers to const char as const pointers (pr #624 by argrath) function fill_special_room() in sp_lev.c was dereferencing a pointer argument prior to a subsequent check for a NULL pointer that @@ -2906,6 +3009,9 @@ split "cannot_push" on moverock_done() into a separate function (pr #1282 by argrath) split making pits on do_earthquake() into a separate function and eliminate some gotos on do_earthquake() (pr #1287 by argrath) +update the WASM cross-compile to work with the current + code (pr #1331 by guillaumebrunerie) +add silver maces (pr #1405 by disperse) Code Cleanup and Reorganization diff --git a/doc/fixesX-X-X.txt b/doc/fixesX-X-X.txt index 4ce05819f..462dd5f60 100644 --- a/doc/fixesX-X-X.txt +++ b/doc/fixesX-X-X.txt @@ -14,7 +14,6 @@ Platform- and/or Interface-Specific Fixes General New Features -------------------- -Conway's Life bigroom variant Platform- and/or Interface-Specific New Features diff --git a/doc/lua.adoc b/doc/lua.adoc index 84758dc3f..3b457b670 100644 --- a/doc/lua.adoc +++ b/doc/lua.adoc @@ -201,6 +201,7 @@ Returns a table with the following elements: | ttyp_name | text | name of trap type | tseen | boolean | trap seen by you? | madeby_u | boolean | trap made by you? +| once | boolean | trap is deleted once triggered | tnote | integer | note of a squeaky board trap | launchx, launchy, launch2x, launch2y | integer | coordinates of a boulder for a rolling boulder trap | conjoined | integer | encoded directions for a [spiked] pit. @@ -249,6 +250,15 @@ Example: local str = nh.ing_suffix("foo"); +=== is_genocided + +Is specific monster type genocided? Returns a boolean value. + +Example: + + local x = nh.is_genocided("vampire"); + + === level_difficulty Returns an integer value describing the level difficulty. @@ -531,9 +541,10 @@ Example: === exclusion Exclude an area of the map from being randomly chosen target when -falling or teleporting into the level. Multiple exclusions per level are allowed. +falling or teleporting into the level, or creating a monster. +Multiple exclusions per level are allowed. -* type is one of "teleport", "teleport-up", or "teleport-down". +* type is one of "teleport", "teleport-up", "teleport-down", or "monster-generation". Example: @@ -783,8 +794,9 @@ The hash parameter accepts the following keys: | adjacentok | boolean | is adjacent location ok, if given one is not suitable? | ignorewater | boolean | ignore water when choosing location for the monster | countbirth | boolean | do we count this monster as generated -| appear_as | string | monster can appear as object, monster, or terrain. Add "obj:", "mon:", or "ter:" prefix to the value. | -| inventory | function | objects generated in the function are given to the monster +| appear_as | string | monster can appear as object, monster, or terrain. Add "obj:", "mon:", or "ter:" prefix to the value. +| inventory | function | objects generated in the function are given to the monster (any random inventory it gets is discarded unless keep_default_invent is true) +| keep_default_invent | boolean | if inventory is specified and this is true, those items are in addition to random inventory for this species; if inventory is not specified and this is false, monster gets no starting inventory |=== Example: @@ -839,6 +851,7 @@ The table parameter accepts the following: | eroded | int | Object erosion | locked | boolean | Is the object locked? | trapped | boolean | Is the object trapped? +| trap_known | boolean | If container is trapped, is it obvious? | recharged | boolean | Is the object recharged? | greased | boolean | Is the object greased? | broken | boolean | Is the object broken? @@ -1307,6 +1320,8 @@ Example: === room Create a selection of locations inside the (current) room. +Does not include the edges of the room, such as its walls. +Does not do any check on terrain type, so if there are non-ROOM locations inside the room, they remain part of the selection. Example: diff --git a/doc/makedefs.6 b/doc/makedefs.6 index 1c2129f7d..136084473 100644 --- a/doc/makedefs.6 +++ b/doc/makedefs.6 @@ -1,7 +1,7 @@ -.\"DO NOT REMOVE NH_DATESUB .TH MAKEDEFS 6 "DATE(%-d %B %Y)" NETHACK -.TH MAKEDEFS 6 "8 February 2022" NETHACK -.\"DO NOT REMOVE NH_DATESUB .ds Nd DATE(%Y) -.ds Nd 2022 +.\"DO NOT REMOVE NH_DATESUB .TH MAKEDEFS 6 "Date(%-d %B %Y)" Project(uc) +.TH MAKEDEFS 6 "25 December 2024" NETHACK +.\"DO NOT REMOVE NH_DATESUB .ds Nd Date(%Y) +.ds Nd 2024 .de NB .ds Nb \\$2 .. @@ -10,6 +10,11 @@ .. .NB $NHDT-Branch: NetHack-3.7 $ .NR $NHDT-Revision: 1.22 $ +.\" groff and AT&T-descended troffs use different hyphenation patterns. +.\" Don't hyphenate the last word on a page or column, or +.\" before/after last/first 2 characters of a word. +.ie \n(.g .hy 12 +.el .hy 14 .ds Na Kenneth Lorber .SH NAME makedefs \- NetHack miscellaneous build-time functions diff --git a/doc/makedefs.txt b/doc/makedefs.txt index ad7ab4175..e784946c3 100644 --- a/doc/makedefs.txt +++ b/doc/makedefs.txt @@ -1,4 +1,4 @@ -MAKEDEFS(6) MAKEDEFS(6) +MAKEDEFS(6) Games Manual MAKEDEFS(6) @@ -32,13 +32,13 @@ SHORT COMMANDS -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. + info and include related preprocessor #defines in date.h file. -p Generate pm.h -q Generate the rumors file. - -s Generate the bogusmon , engrave and epitaph files. + -s Generate the bogusmon, engrave, and epitaph files. -1 Generate the epitaph file. @@ -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,10 +143,10 @@ AUTHOR The NetHack Development Team COPYRIGHT - This file is Copyright (C) Kenneth Lorber, 2022 for version keni-crash- - web2:1.21. NetHack may be freely redistributed. See license for - details. + This file is Copyright (C) Kenneth Lorber, 2024 for version + NetHack-3.7:1.22. NetHack may be freely redistributed. See license + for details. -NETHACK 8 February 2022 MAKEDEFS(6) +NETHACK 25 December 2024 MAKEDEFS(6) diff --git a/doc/mn.txt b/doc/mn.txt index 502a0f3bb..1c42c2a04 100644 --- a/doc/mn.txt +++ b/doc/mn.txt @@ -17,8 +17,8 @@ DESCRIPTION All -mn macros, diversions, string registers, and number registers are defined below. Many nroff and troff requests are unsafe in conjunction - with this package. However, the requests below may be used with - impunity: + with this package. However, the requests below may be used with im- + punity: .bp begin new page .br break output line @@ -29,8 +29,8 @@ DESCRIPTION Font and point size changes with \f and \s are also allowed; for exam- ple, ``\f2word\fR'' will italicize word. Output of the tbl(1), eqn(1), - and refer(1) preprocessors for equations, tables, and references is - acceptable as input. + and refer(1) preprocessors for equations, tables, and references is ac- + ceptable as input. FILES /usr/lib/tmac/tmac.n @@ -47,12 +47,12 @@ WARNINGS This package is not now intended for uses other than with the news doc- umentation. - Bug reports are always welcome; please send them to the author. - (Include a sample of the input; this helps track down the bug.) + Bug reports are always welcome; please send them to the author. (In- + clude a sample of the input; this helps track down the bug.) AUTHOR - Matt Bishop (mab@riacs.arpa, ihnp4!ames!riacs!mab, dec- - vax!decwrl!riacs!mab) + Matt Bishop (mab@riacs.arpa, ihnp4!ames!riacs!mab, decvax!decwrl!ri- + acs!mab) Updated for versions 1.4-1.6 by The NetHack Development Team REQUESTS @@ -82,7 +82,7 @@ bt num .5i+1v - bottom of footer to bottom of page cm num 0 - 0 if no cut marks, nonzero if cut marks .cn x y z mac - - print computer/site name; same as .i .dd div - i text of display -dg str *,- - footnote mark +dg str *,<*> - footnote mark dw str current - name of current day of week dy str current - full date .ed mac - b end display diff --git a/doc/mnh.7 b/doc/mnh.7 index daed16283..df7fe5856 100644 --- a/doc/mnh.7 +++ b/doc/mnh.7 @@ -76,6 +76,7 @@ and the second for Macro What Initial Note \0 Explanation Name It Is Value .sp .3 +nH num 0 \- avoid processing multiple times _BR mac \- \- hard line break with vertical padding inserted bR num \- i _CC \*Y mac \- \- aligned one char key \*x with \fIshort\fP definition \*y @@ -92,4 +93,5 @@ 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/nethack.6 b/doc/nethack.6 index 478f99f5d..3605423ca 100644 --- a/doc/nethack.6 +++ b/doc/nethack.6 @@ -1,7 +1,7 @@ -.\"DO NOT REMOVE NH_DATESUB .TH NETHACK 6 "DATE(%-d %B %Y)" NETHACK -.TH NETHACK 6 "21 February 2022" NETHACK -.\"DO NOT REMOVE NH_DATESUB .ds Nd DATE(%Y) -.ds Nd 2022 +.\"DO NOT REMOVE NH_DATESUB .TH NETHACK 6 "Date(%-d %B %Y)" Project(uc) +.TH NETHACK 6 "25 December 2024" NETHACK +.\"DO NOT REMOVE NH_DATESUB .ds Nd Date(%Y) +.ds Nd 2024 .de NB .ds Nb \\$2 .. @@ -10,6 +10,11 @@ .. .NB $NHDT-Branch: NetHack-3.7 $ .NR $NHDT-Revision: 1.31 $ +.\" groff and AT&T-descended troffs use different hyphenation patterns. +.\" Don't hyphenate the last word on a page or column, or +.\" before/after last/first 2 characters of a word. +.ie \n(.g .hy 12 +.el .hy 14 .ds Na Robert Patrick Rankin .SH NAME nethack \- Exploring The Mazes of Menace diff --git a/doc/nethack.txt b/doc/nethack.txt index 9afe9924d..ca120c3e0 100644 --- a/doc/nethack.txt +++ b/doc/nethack.txt @@ -286,4 +286,4 @@ COPYRIGHT -NETHACK 28 February 2024 NETHACK(6) +NETHACK 25 December 2024 NETHACK(6) diff --git a/doc/recover.6 b/doc/recover.6 index b94efda65..3829760e9 100644 --- a/doc/recover.6 +++ b/doc/recover.6 @@ -1,7 +1,7 @@ -.\"DO NOT REMOVE NH_DATESUB .TH RECOVER 6 "DATE(%-d %B %Y)" NETHACK -.TH RECOVER 6 "8 February 2022" NETHACK -.\"DO NOT REMOVE NH_DATESUB .ds Nd DATE(%Y) -.ds Nd 2022 +.\"DO NOT REMOVE NH_DATESUB .TH RECOVER 6 "Date(%-d %B %Y)" Project(uc) +.TH RECOVER 6 "25 December 2024" NETHACK +.\"DO NOT REMOVE NH_DATESUB .ds Nd Date(%Y) +.ds Nd 2024 .de NB .ds Nb \\$2 .. @@ -10,6 +10,11 @@ .. .NB $NHDT-Branch: NetHack-3.7 $ .NR $NHDT-Revision: 1.12 $ +.\" groff and AT&T-descended troffs use different hyphenation patterns. +.\" Don't hyphenate the last word on a page or column, or +.\" before/after last/first 2 characters of a word. +.ie \n(.g .hy 12 +.el .hy 14 .ds Na Kenneth Lorber .SH NAME recover \- recover a NetHack game interrupted by disaster @@ -43,6 +48,7 @@ supplies a directory which is the NetHack playground. It overrides the value from NETHACKDIR, HACKDIR, or the directory specified by the game administrator during compilation (usually /usr/games/lib/nethackdir). +.ig .PP ^?ALLDOCS For recovery to be possible, @@ -64,6 +70,7 @@ This configuration of was created without support for recovery. ^. ^. +.. NetHack normally writes out files for levels as the player leaves them, so they will be ready for return visits. When checkpointing, NetHack also writes out the level entered and diff --git a/doc/recover.txt b/doc/recover.txt index 1eacac0d7..e13d2995f 100644 --- a/doc/recover.txt +++ b/doc/recover.txt @@ -1,4 +1,4 @@ -RECOVER(6) RECOVER(6) +RECOVER(6) Games Manual RECOVER(6) @@ -22,15 +22,11 @@ DESCRIPTION The -d option, which must be the first argument if it appears, supplies a directory which is the NetHack playground. It overrides the value from NETHACKDIR, HACKDIR, or the directory specified by the game admin- - istrator during compilation (usually /usr/games/lib/nethackdir). - - For recovery to be possible, nethack must have been compiled with the - INSURANCE option, and the run-time option checkpoint must also have - been on. NetHack normally writes out files for levels as the player - leaves them, so they will be ready for return visits. When checkpoint- - ing, NetHack also writes out the level entered and the current game - state on every level change. This naturally slows level changes down - somewhat. + istrator during compilation (usually /usr/games/lib/nethackdir). + NetHack normally writes out files for levels as the player leaves them, + so they will be ready for return visits. When checkpointing, NetHack + also writes out the level entered and the current game state on every + level change. This naturally slows level changes down somewhat. The level file names are of the form base.nn, where nn is an internal bookkeeping number for the level. The file base.0 is used for game @@ -77,10 +73,10 @@ BUGS nism to avoid conflicts. COPYRIGHT - This file is Copyright (C) Kenneth Lorber, 2022 for version + This file is Copyright (C) Kenneth Lorber, 2024 for version NetHack-3.7:1.12. NetHack may be freely redistributed. See license for details. -NETHACK 8 February 2022 RECOVER(6) +NETHACK 25 December 2024 RECOVER(6) diff --git a/doc/sound.txt b/doc/sound.txt index 6af2a9707..2f90d3334 100644 --- a/doc/sound.txt +++ b/doc/sound.txt @@ -508,8 +508,8 @@ use the following guidelines: Windows with Visual studio nmake at the command line. - sys/windows/Makefile.mingw32 - sys/windows/Makefile.mingw32.depend + sys/windows/GNUmakefile + sys/windows/GNUmakefile.depend Will require updates in order to build on Windows with mingw32 or MSYS2 using GNU make at diff --git a/doc/tmac.nh b/doc/tmac.nh index fce6f786d..86bc643b4 100644 --- a/doc/tmac.nh +++ b/doc/tmac.nh @@ -134,8 +134,9 @@ \\$1\\$2 .. . -.\" Don't hyphenate the last word on a page or column. groff and AT&T- -.\" descended troffs use different hyphenation patterns. -.ie \n(.g .hy 6 -.el .hy 2 +.\" groff and AT&T-descended troffs use different hyphenation patterns. +.\" Don't hyphenate the last word on a page or column, or +.\" before/after last/first 2 characters of a word. +.ie \n(.g .hy 12 +.el .hy 14 .\"tmac.nh/" diff --git a/doc/window.txt b/doc/window.txt index 0bad7062c..45593eb88 100644 --- a/doc/window.txt +++ b/doc/window.txt @@ -649,14 +649,6 @@ can_suspend() -- Tell the core if the window system will allow the game to be suspended now. If unconditionally yes or no, use genl_can_suspend_yes() or genl_can_suspend_no(). -start_screen() -- Only used on Unix tty ports, but must be declared for - completeness. Sets up the tty to work in full-screen - graphics mode. Look at win/tty/termcap.c for an - example. If your window-port does not need this function - just declare an empty function. -end_screen() -- Only used on Unix tty ports, but must be declared for - completeness. The complement of start_screen(). - outrip(winid, int, time_t) -- The tombstone code. If you want the traditional code use genl_outrip for the value and check the #if in rip.c. diff --git a/include/.gitignore b/include/.gitignore index 1930f4302..6f4a4f802 100644 --- a/include/.gitignore +++ b/include/.gitignore @@ -15,3 +15,5 @@ tile.h win32api.h # really obsolete... .cvsignore +sfproto.h + diff --git a/include/artifact.h b/include/artifact.h index c564eba66..2db7270fb 100644 --- a/include/artifact.h +++ b/include/artifact.h @@ -5,6 +5,10 @@ #ifndef ARTIFACT_H #define ARTIFACT_H + +#include "permonst.h" +#include "prop.h" + /* clang-format off */ #define SPFX_NONE 0x00000000L /* no special effects, just a bonus */ @@ -49,6 +53,8 @@ struct artifact { aligntyp alignment; /* alignment of bequeathing gods */ short role; /* character role associated with */ short race; /* character race associated with */ + schar gen_spe; /* bias to spe when gifted or randomly generated */ + uchar gift_value; /* minimum sacrifice value to be gifted this */ long cost; /* price when sold to hero (default 100 x base cost) */ char acolor; /* color to use if artifact 'glows' */ }; @@ -65,8 +71,25 @@ enum invoke_prop_types { ENLIGHTENING, CREATE_AMMO, BANISH, + FLING_POISON, + FIRESTORM, + SNOWSTORM, BLINDING_RAY }; +/* artifact tracking; gift and wish imply found; it also gets set for items + seen on the floor, in containers, and wielded or dropped by monsters */ +struct arti_info { + Bitfield(exists, 1); /* 1 if corresponding artifact has been created */ + Bitfield(found, 1); /* 1 if artifact is known by hero to exist */ + Bitfield(gift, 1); /* 1 iff artifact was created as a prayer reward */ + Bitfield(wish, 1); /* 1 iff artifact was created via wish */ + Bitfield(named, 1); /* 1 iff artifact was made by naming an item */ + Bitfield(viadip, 1); /* 1 iff dipped long sword became Excalibur */ + Bitfield(lvldef, 1); /* 1 iff created by special level definition */ + Bitfield(bones, 1); /* 1 iff came from bones file */ + Bitfield(rndm, 1); /* 1 iff randomly generated */ +}; + /* clang-format on */ #endif /* ARTIFACT_H */ diff --git a/include/artilist.h b/include/artilist.h index 735ee95a2..b5dce04f8 100644 --- a/include/artilist.h +++ b/include/artilist.h @@ -7,28 +7,28 @@ /* in makedefs.c, all we care about is the list of names */ #define A(nam, typ, s1, s2, mt, atk, dfn, cry, inv, al, cl, rac, \ - cost, clr, bn) nam + gs, gv, cost, clr, bn) nam static const char *const artifact_names[] = { #elif defined(ARTI_ENUM) #define A(nam, typ, s1, s2, mt, atk, dfn, cry, inv, al, cl, rac, \ - cost, clr, bn) \ + gs, gv, cost, clr, bn) \ ART_##bn #elif defined(DUMP_ARTI_ENUM) #define A(nam, typ, s1, s2, mt, atk, dfn, cry, inv, al, cl, rac, \ - cost, clr, bn) \ + gs, gv, cost, clr, bn) \ { ART_##bn, "ART_" #bn } #else /* in artifact.c, set up the actual artifact list structure; color field is for an artifact when it glows, not for the item itself */ #define A(nam, typ, s1, s2, mt, atk, dfn, cry, inv, al, cl, rac, \ - cost, clr, bn) \ + gs, gv, cost, clr, bn) \ { \ typ, nam, s1, s2, mt, atk, dfn, cry, inv, al, cl, rac, \ - cost, clr \ + gs, gv, cost, clr \ } /* clang-format off */ @@ -43,6 +43,7 @@ static const char *const artifact_names[] = { #define FIRE(a,b) {0,AD_FIRE,a,b} #define ELEC(a,b) {0,AD_ELEC,a,b} /* electrical shock */ #define STUN(a,b) {0,AD_STUN,a,b} /* magical attack */ +#define POIS(a,b) {0,AD_DRST,a,b} /* poison */ /* clang-format on */ static NEARDATA struct artifact artilist[] = { @@ -52,24 +53,47 @@ static NEARDATA struct artifact artilist[] = { * 1. The more useful the artifact, the better its cost. * 2. Quest artifacts are highly valued. * 3. Chaotic artifacts are inflated due to scarcity (and balance). + * + * Artifact gen_spe rationale: + * 1. If the artifact is useful against most enemies, +0. + * 2. If the artifact is useful against only a few enemies, usually +2. + * This gives the artifact use to early-game characters who receive + * it as a gift or find it on the ground. + * 3. Role gift gen_spe is chosen to balance against the role's + * default starting weapon (it should be better, but need not be + * much better). + * 4. This can be modified as required for special cases. + * (In some cases, like Excalibur, the value is irrelevant.) + * 5. Nonweapon spe may have a special meaning, so gen_spe for + * nonweapons must always be 0. + * + * Artifact gift_value is chosen so that "endgame-quality" artifacts are + * not gifted in the early game (so that characters don't grind on an + * altar early-game until they have their endgame weapon, then use it to + * carry them through the game). Those artifacts have values ranging from + * around 8 to 10, based on how good the artifact is. Less powerful + * artifacts have values in the 1 to 5 range. Values in between are used + * for conditionally good artifacts. (Note that the value of a gift is + * normally 1 higher than the difficulty of the monster.) */ /* dummy element #0, so that all interesting indices are non-zero */ A("", STRANGE_OBJECT, 0, 0, 0, NO_ATTK, NO_DFNS, NO_CARY, 0, A_NONE, - NON_PM, NON_PM, 0L, NO_COLOR, NONARTIFACT), + NON_PM, NON_PM, + 0, 0, 0L, NO_COLOR, NONARTIFACT), A("Excalibur", LONG_SWORD, (SPFX_NOGEN | SPFX_RESTR | SPFX_SEEK | SPFX_DEFN | SPFX_INTEL | SPFX_SEARCH), 0, 0, PHYS(5, 10), DRLI(0, 0), NO_CARY, 0, A_LAWFUL, PM_KNIGHT, NON_PM, - 4000L, NO_COLOR, EXCALIBUR), + 0, 10, 4000L, NO_COLOR, EXCALIBUR), /* * Stormbringer only has a 2 because it can drain a level, * providing 8 more. */ A("Stormbringer", RUNESWORD, (SPFX_RESTR | SPFX_ATTK | SPFX_DEFN | SPFX_INTEL | SPFX_DRLI), 0, 0, - DRLI(5, 2), DRLI(0, 0), NO_CARY, 0, A_CHAOTIC, NON_PM, NON_PM, 8000L, - NO_COLOR, STORMBRINGER), + DRLI(5, 2), DRLI(0, 0), NO_CARY, 0, A_CHAOTIC, NON_PM, NON_PM, + 0, 9, 8000L, NO_COLOR, STORMBRINGER), /* * Mjollnir can be thrown when wielded if hero has 25 Strength * (usually via gauntlets of power but possible with rings of @@ -84,10 +108,12 @@ static NEARDATA struct artifact artilist[] = { */ A("Mjollnir", WAR_HAMMER, /* Mjo:llnir */ (SPFX_RESTR | SPFX_ATTK), 0, 0, ELEC(5, 24), NO_DFNS, NO_CARY, 0, - A_NEUTRAL, PM_VALKYRIE, NON_PM, 4000L, NO_COLOR, MJOLLNIR), + A_NEUTRAL, PM_VALKYRIE, NON_PM, + 0, 8, 4000L, NO_COLOR, MJOLLNIR), A("Cleaver", BATTLE_AXE, SPFX_RESTR, 0, 0, PHYS(3, 6), NO_DFNS, NO_CARY, - 0, A_NEUTRAL, PM_BARBARIAN, NON_PM, 1500L, NO_COLOR, CLEAVER), + 0, A_NEUTRAL, PM_BARBARIAN, NON_PM, + 0, 8, 1500L, NO_COLOR, CLEAVER), /* * Grimtooth glows in warning when elves are present, but its @@ -95,8 +121,9 @@ static NEARDATA struct artifact artilist[] = { * (handled as special case in spec_dbon()). */ A("Grimtooth", ORCISH_DAGGER, (SPFX_RESTR | SPFX_WARN | SPFX_DFLAG2), - 0, M2_ELF, PHYS(2, 6), NO_DFNS, - NO_CARY, 0, A_CHAOTIC, NON_PM, PM_ORC, 300L, CLR_RED, GRIMTOOTH), + 0, M2_ELF, PHYS(2, 6), POIS(0,0), + NO_CARY, FLING_POISON, A_CHAOTIC, NON_PM, PM_ORC, + 0, 5, 1200L, CLR_RED, GRIMTOOTH), /* * Orcrist and Sting have same alignment as elves. * @@ -105,56 +132,56 @@ static NEARDATA struct artifact artilist[] = { * Sting and Orcrist will warn of M2_ORC monsters. */ A("Orcrist", ELVEN_BROADSWORD, (SPFX_WARN | SPFX_DFLAG2), 0, M2_ORC, - PHYS(5, 0), NO_DFNS, NO_CARY, 0, A_CHAOTIC, NON_PM, PM_ELF, 2000L, - CLR_BRIGHT_BLUE, ORCRIST), /* bright blue is actually light blue */ + PHYS(5, 0), NO_DFNS, NO_CARY, 0, A_CHAOTIC, NON_PM, PM_ELF, + 3, 4, 2000L, CLR_BRIGHT_BLUE, ORCRIST), /* actually light blue */ A("Sting", ELVEN_DAGGER, (SPFX_WARN | SPFX_DFLAG2), 0, M2_ORC, - PHYS(5, 0), NO_DFNS, NO_CARY, 0, A_CHAOTIC, NON_PM, PM_ELF, 800L, - CLR_BRIGHT_BLUE, STING), + PHYS(5, 0), NO_DFNS, NO_CARY, 0, A_CHAOTIC, NON_PM, PM_ELF, + 3, 1, 800L, CLR_BRIGHT_BLUE, STING), /* * Magicbane is a bit different! Its magic fanfare * unbalances victims in addition to doing some damage. */ A("Magicbane", ATHAME, (SPFX_RESTR | SPFX_ATTK | SPFX_DEFN), 0, 0, STUN(3, 4), DFNS(AD_MAGM), NO_CARY, 0, A_NEUTRAL, PM_WIZARD, NON_PM, - 3500L, NO_COLOR, MAGICBANE), + 0, 7, 3500L, NO_COLOR, MAGICBANE), A("Frost Brand", LONG_SWORD, (SPFX_RESTR | SPFX_ATTK | SPFX_DEFN), 0, 0, - COLD(5, 0), COLD(0, 0), NO_CARY, 0, A_NONE, NON_PM, NON_PM, 3000L, - NO_COLOR, FROST_BRAND), + COLD(5, 0), COLD(0, 0), NO_CARY, SNOWSTORM, A_NONE, NON_PM, NON_PM, + 0, 9, 3000L, NO_COLOR, FROST_BRAND), A("Fire Brand", LONG_SWORD, (SPFX_RESTR | SPFX_ATTK | SPFX_DEFN), 0, 0, - FIRE(5, 0), FIRE(0, 0), NO_CARY, 0, A_NONE, NON_PM, NON_PM, 3000L, - NO_COLOR, FIRE_BRAND), + FIRE(5, 0), FIRE(0, 0), NO_CARY, FIRESTORM, A_NONE, NON_PM, NON_PM, + 0, 5, 3000L, NO_COLOR, FIRE_BRAND), A("Dragonbane", BROADSWORD, (SPFX_RESTR | SPFX_DCLAS | SPFX_REFLECT), 0, S_DRAGON, - PHYS(5, 0), NO_DFNS, NO_CARY, 0, A_NONE, NON_PM, NON_PM, 500L, - NO_COLOR, DRAGONBANE), + PHYS(5, 0), NO_DFNS, NO_CARY, 0, A_NONE, NON_PM, NON_PM, + 2, 5, 500L, NO_COLOR, DRAGONBANE), - A("Demonbane", MACE, (SPFX_RESTR | SPFX_DFLAG2), 0, M2_DEMON, + A("Demonbane", SILVER_MACE, (SPFX_RESTR | SPFX_DFLAG2), 0, M2_DEMON, PHYS(5, 0), NO_DFNS, NO_CARY, BANISH, A_LAWFUL, PM_CLERIC, NON_PM, - 2500L, NO_COLOR, DEMONBANE), + 1, 3, 2500L, NO_COLOR, DEMONBANE), A("Werebane", SILVER_SABER, (SPFX_RESTR | SPFX_DFLAG2), 0, M2_WERE, - PHYS(5, 0), DFNS(AD_WERE), NO_CARY, 0, A_NONE, NON_PM, NON_PM, 1500L, - NO_COLOR, WEREBANE), + PHYS(5, 0), DFNS(AD_WERE), NO_CARY, 0, A_NONE, NON_PM, NON_PM, + 1, 4, 1500L, NO_COLOR, WEREBANE), A("Grayswandir", SILVER_SABER, (SPFX_RESTR | SPFX_HALRES), 0, 0, - PHYS(5, 0), NO_DFNS, NO_CARY, 0, A_LAWFUL, NON_PM, NON_PM, 8000L, - NO_COLOR, GRAYSWANDIR), + PHYS(5, 0), NO_DFNS, NO_CARY, 0, A_LAWFUL, NON_PM, NON_PM, + 0, 10, 8000L, NO_COLOR, GRAYSWANDIR), A("Giantslayer", LONG_SWORD, (SPFX_RESTR | SPFX_DFLAG2), 0, M2_GIANT, - PHYS(5, 0), NO_DFNS, NO_CARY, 0, A_NEUTRAL, NON_PM, NON_PM, 200L, - NO_COLOR, GIANTSLAYER), + PHYS(5, 0), NO_DFNS, NO_CARY, 0, A_NEUTRAL, NON_PM, NON_PM, + 2, 4, 200L, NO_COLOR, GIANTSLAYER), A("Ogresmasher", WAR_HAMMER, (SPFX_RESTR | SPFX_DCLAS), 0, S_OGRE, - PHYS(5, 0), NO_DFNS, NO_CARY, 0, A_NONE, NON_PM, NON_PM, 200L, - NO_COLOR, OGRESMASHER), + PHYS(5, 0), NO_DFNS, NO_CARY, 0, A_NONE, NON_PM, NON_PM, + 2, 1, 200L, NO_COLOR, OGRESMASHER), - A("Trollsbane", MORNING_STAR, (SPFX_RESTR | SPFX_DCLAS), 0, S_TROLL, - PHYS(5, 0), NO_DFNS, NO_CARY, 0, A_NONE, NON_PM, NON_PM, 200L, - NO_COLOR, TROLLSBANE), + A("Trollsbane", MORNING_STAR, (SPFX_RESTR | SPFX_DCLAS | SPFX_REGEN), 0, + S_TROLL, PHYS(5, 0), NO_DFNS, NO_CARY, 0, A_NONE, NON_PM, NON_PM, + 2, 1, 200L, NO_COLOR, TROLLSBANE), /* * Two problems: 1) doesn't let trolls regenerate heads, @@ -162,8 +189,8 @@ static NEARDATA struct artifact artilist[] = { * allowing those at all causes more problems than worth the effort). */ A("Vorpal Blade", LONG_SWORD, (SPFX_RESTR | SPFX_BEHEAD), 0, 0, - PHYS(5, 1), NO_DFNS, NO_CARY, 0, A_NEUTRAL, NON_PM, NON_PM, 4000L, - NO_COLOR, VORPAL_BLADE), + PHYS(5, 1), NO_DFNS, NO_CARY, 0, A_NEUTRAL, NON_PM, NON_PM, + 1, 5, 4000L, NO_COLOR, VORPAL_BLADE), /* * Ah, never shall I forget the cry, @@ -174,33 +201,38 @@ static NEARDATA struct artifact artilist[] = { * (From Sir W.S. Gilbert's "The Mikado") */ A("Snickersnee", KATANA, SPFX_RESTR, 0, 0, PHYS(0, 8), NO_DFNS, NO_CARY, - 0, A_LAWFUL, PM_SAMURAI, NON_PM, 1200L, NO_COLOR, SNICKERSNEE), + 0, A_LAWFUL, PM_SAMURAI, NON_PM, + 0, 8, 1200L, NO_COLOR, SNICKERSNEE), /* Sunsword emits light when wielded (handled in the core rather than via artifact fields), but that light has no particular color */ A("Sunsword", LONG_SWORD, (SPFX_RESTR | SPFX_DFLAG2), 0, M2_UNDEAD, PHYS(5, 0), DFNS(AD_BLND), NO_CARY, BLINDING_RAY, A_LAWFUL, NON_PM, - NON_PM, 1500L, NO_COLOR, SUNSWORD), + NON_PM, + 0, 6, 1500L, NO_COLOR, SUNSWORD), /* * The artifacts for the quest dungeon, all self-willed. + * gen_spe should be 0; gift_value irrelevant and set to 12. */ A("The Orb of Detection", CRYSTAL_BALL, (SPFX_NOGEN | SPFX_RESTR | SPFX_INTEL), (SPFX_ESP | SPFX_HSPDAM), 0, NO_ATTK, NO_DFNS, CARY(AD_MAGM), INVIS, A_LAWFUL, PM_ARCHEOLOGIST, - NON_PM, 2500L, NO_COLOR, ORB_OF_DETECTION), + NON_PM, + 0, 12, 2500L, NO_COLOR, ORB_OF_DETECTION), A("The Heart of Ahriman", LUCKSTONE, (SPFX_NOGEN | SPFX_RESTR | SPFX_INTEL), SPFX_STLTH, 0, /* this stone does double damage if used as a projectile weapon */ PHYS(5, 0), NO_DFNS, NO_CARY, LEVITATION, A_NEUTRAL, PM_BARBARIAN, - NON_PM, 2500L, NO_COLOR, HEART_OF_AHRIMAN), + NON_PM, + 0, 12, 2500L, NO_COLOR, HEART_OF_AHRIMAN), A("The Sceptre of Might", MACE, (SPFX_NOGEN | SPFX_RESTR | SPFX_INTEL | SPFX_DALIGN), 0, 0, PHYS(5, 0), DFNS(AD_MAGM), NO_CARY, CONFLICT, A_LAWFUL, PM_CAVE_DWELLER, NON_PM, - 2500L, NO_COLOR, SCEPTRE_OF_MIGHT), + 0, 12, 2500L, NO_COLOR, SCEPTRE_OF_MIGHT), #if 0 /* OBSOLETE -- from 3.1.0 to 3.2.x, this was quest artifact for the * Elf role; in 3.3.0 elf became a race available to several roles @@ -209,35 +241,37 @@ static NEARDATA struct artifact artilist[] = { A("The Palantir of Westernesse", CRYSTAL_BALL, (SPFX_NOGEN | SPFX_RESTR | SPFX_INTEL), (SPFX_ESP | SPFX_REGEN | SPFX_HSPDAM), 0, - NO_ATTK, NO_DFNS, NO_CARY, TAMING, A_CHAOTIC, NON_PM, PM_ELF, 8000L, - NO_COLOR, PALANTIR_OF_WESTERNESSE), + NO_ATTK, NO_DFNS, NO_CARY, TAMING, A_CHAOTIC, NON_PM, PM_ELF, + 0, 12, 8000L, NO_COLOR, PALANTIR_OF_WESTERNESSE), #endif A("The Staff of Aesculapius", QUARTERSTAFF, (SPFX_NOGEN | SPFX_RESTR | SPFX_ATTK | SPFX_INTEL | SPFX_DRLI | SPFX_REGEN), 0, 0, DRLI(0, 0), DRLI(0, 0), NO_CARY, HEALING, A_NEUTRAL, PM_HEALER, - NON_PM, 5000L, NO_COLOR, STAFF_OF_AESCULAPIUS), + NON_PM, + 0, 12, 5000L, NO_COLOR, STAFF_OF_AESCULAPIUS), A("The Magic Mirror of Merlin", MIRROR, (SPFX_NOGEN | SPFX_RESTR | SPFX_INTEL | SPFX_SPEAK), SPFX_ESP, 0, - NO_ATTK, NO_DFNS, CARY(AD_MAGM), 0, A_LAWFUL, PM_KNIGHT, NON_PM, 1500L, - NO_COLOR, MAGIC_MIRROR_OF_MERLIN), + NO_ATTK, NO_DFNS, CARY(AD_MAGM), 0, A_LAWFUL, PM_KNIGHT, NON_PM, + 0, 12, 1500L, NO_COLOR, MAGIC_MIRROR_OF_MERLIN), A("The Eyes of the Overworld", LENSES, (SPFX_NOGEN | SPFX_RESTR | SPFX_INTEL | SPFX_XRAY), 0, 0, NO_ATTK, DFNS(AD_MAGM), NO_CARY, ENLIGHTENING, A_NEUTRAL, PM_MONK, NON_PM, - 2500L, NO_COLOR, EYES_OF_THE_OVERWORLD), + 0, 12, 2500L, NO_COLOR, EYES_OF_THE_OVERWORLD), A("The Mitre of Holiness", HELM_OF_BRILLIANCE, (SPFX_NOGEN | SPFX_RESTR | SPFX_DFLAG2 | SPFX_INTEL | SPFX_PROTECT), 0, M2_UNDEAD, NO_ATTK, NO_DFNS, CARY(AD_FIRE), ENERGY_BOOST, A_LAWFUL, - PM_CLERIC, NON_PM, 2000L, NO_COLOR, MITRE_OF_HOLINESS), + PM_CLERIC, NON_PM, + 0, 12, 2000L, NO_COLOR, MITRE_OF_HOLINESS), A("The Longbow of Diana", BOW, (SPFX_NOGEN | SPFX_RESTR | SPFX_INTEL | SPFX_REFLECT), SPFX_ESP, 0, PHYS(5, 0), NO_DFNS, NO_CARY, CREATE_AMMO, A_CHAOTIC, PM_RANGER, NON_PM, - 4000L, NO_COLOR, LONGBOW_OF_DIANA), + 0, 12, 4000L, NO_COLOR, LONGBOW_OF_DIANA), /* MKoT has an additional carry property if the Key is not cursed (for rogues) or blessed (for non-rogues): #untrap of doors and chests @@ -245,38 +279,39 @@ static NEARDATA struct artifact artilist[] = { A("The Master Key of Thievery", SKELETON_KEY, (SPFX_NOGEN | SPFX_RESTR | SPFX_INTEL | SPFX_SPEAK), (SPFX_WARN | SPFX_TCTRL | SPFX_HPHDAM), 0, NO_ATTK, NO_DFNS, NO_CARY, - UNTRAP, A_CHAOTIC, PM_ROGUE, NON_PM, 3500L, NO_COLOR, - MASTER_KEY_OF_THIEVERY), + UNTRAP, A_CHAOTIC, PM_ROGUE, NON_PM, + 0, 12, 3500L, NO_COLOR, MASTER_KEY_OF_THIEVERY), A("The Tsurugi of Muramasa", TSURUGI, (SPFX_NOGEN | SPFX_RESTR | SPFX_INTEL | SPFX_BEHEAD | SPFX_LUCK | SPFX_PROTECT), 0, 0, PHYS(0, 8), NO_DFNS, NO_CARY, 0, A_LAWFUL, PM_SAMURAI, NON_PM, - 4500L, NO_COLOR, TSURUGI_OF_MURAMASA), + 0, 12, 4500L, NO_COLOR, TSURUGI_OF_MURAMASA), A("The Platinum Yendorian Express Card", CREDIT_CARD, (SPFX_NOGEN | SPFX_RESTR | SPFX_INTEL | SPFX_DEFN), (SPFX_ESP | SPFX_HSPDAM), 0, NO_ATTK, NO_DFNS, CARY(AD_MAGM), - CHARGE_OBJ, A_NEUTRAL, PM_TOURIST, NON_PM, 7000L, NO_COLOR, - YENDORIAN_EXPRESS_CARD), + CHARGE_OBJ, A_NEUTRAL, PM_TOURIST, NON_PM, + 0, 12, 7000L, NO_COLOR, YENDORIAN_EXPRESS_CARD), A("The Orb of Fate", CRYSTAL_BALL, (SPFX_NOGEN | SPFX_RESTR | SPFX_INTEL | SPFX_LUCK), (SPFX_WARN | SPFX_HSPDAM | SPFX_HPHDAM), 0, NO_ATTK, NO_DFNS, NO_CARY, - LEV_TELE, A_NEUTRAL, PM_VALKYRIE, NON_PM, 3500L, NO_COLOR, - ORB_OF_FATE), + LEV_TELE, A_NEUTRAL, PM_VALKYRIE, NON_PM, + 0, 12, 3500L, NO_COLOR, ORB_OF_FATE), A("The Eye of the Aethiopica", AMULET_OF_ESP, (SPFX_NOGEN | SPFX_RESTR | SPFX_INTEL), (SPFX_EREGEN | SPFX_HSPDAM), 0, NO_ATTK, DFNS(AD_MAGM), NO_CARY, CREATE_PORTAL, A_NEUTRAL, PM_WIZARD, - NON_PM, 4000L, NO_COLOR, EYE_OF_THE_AETHIOPICA), + NON_PM, + 0, 12, 4000L, NO_COLOR, EYE_OF_THE_AETHIOPICA), #if !defined(ARTI_ENUM) && !defined(DUMP_ARTI_ENUM) /* * terminator; otyp must be zero */ - A(0, 0, 0, 0, 0, NO_ATTK, NO_DFNS, NO_CARY, 0, A_NONE, NON_PM, NON_PM, 0L, - NO_COLOR, TERMINATOR) + A(0, 0, 0, 0, 0, NO_ATTK, NO_DFNS, NO_CARY, 0, A_NONE, NON_PM, NON_PM, + 0, 0, 0L, NO_COLOR, TERMINATOR) }; /* artilist[] (or artifact_names[]) */ #endif diff --git a/include/botl.h b/include/botl.h index 3ffd6be1a..87c82d25c 100644 --- a/include/botl.h +++ b/include/botl.h @@ -249,7 +249,7 @@ enum hlattribs { struct hilite_s { enum statusfields fld; boolean set; - unsigned anytype; + enum any_types anytype; anything value; int behavior; char textmatch[MAXVALWIDTH]; @@ -271,7 +271,7 @@ struct istat_s { boolean chg; /* need to recalc time? */ boolean percent_matters; short percent_value; - unsigned anytype; + enum any_types anytype; anything a, rawval; char *val; int valwidth; diff --git a/include/config.h b/include/config.h index 9b77a7d10..7069b268d 100644 --- a/include/config.h +++ b/include/config.h @@ -259,21 +259,23 @@ # ifdef CRASHREPORT # undef CRASHREPORT # endif -# ifdef MSDOS +# if defined(MSDOS) || defined(NOPANICTRACE) # undef PANICTRACE # endif #endif #ifdef CRASHREPORT # ifndef DUMPLOG_CORE -# define DUMPLOG_CORE // required to get ^P info +# define DUMPLOG_CORE // required to get ^P info # endif # ifdef MACOS # define PANICTRACE # endif # ifdef __linux__ # define PANICTRACE -# define NOSTATICFN +# ifndef NOSTATICFN // may be defined on command line +# define NOSTATICFN +# endif # endif // This test isn't quite right: CNG is only available from Windows 2000 on. // But we'll check that at runtime. @@ -395,38 +397,6 @@ /* # define ZLIB_COMP */ /* ZLIB for compression */ #endif -/* - * Internal Compression Options - * - * Internal compression options RLECOMP and ZEROCOMP alter the data - * that gets written to the save file by NetHack, in contrast - * to COMPRESS or ZLIB_COMP which compress the entire file after - * the NetHack data is written out. - * - * Defining RLECOMP builds in support for internal run-length - * compression of level structures. If RLECOMP support is included - * it can be toggled on/off at runtime via the config file option - * rlecomp. - * - * Defining ZEROCOMP builds in support for internal zero-comp - * compression of data. If ZEROCOMP support is included it can still - * be toggled on/off at runtime via the config file option zerocomp. - * - * RLECOMP and ZEROCOMP support can be included even if - * COMPRESS or ZLIB_COMP support is included. One reason for doing - * so would be to provide savefile read compatibility with a savefile - * where those options were in effect. With RLECOMP and/or ZEROCOMP - * defined, NetHack can read an rlecomp or zerocomp savefile in, yet - * re-save without them. - * - * Using any compression option will create smaller bones/level/save - * files at the cost of additional code and time. - */ - -/* # define INTERNAL_COMP */ /* defines both ZEROCOMP and RLECOMP */ -/* # define ZEROCOMP */ /* Support ZEROCOMP compression */ -/* # define RLECOMP */ /* Support RLECOMP compression */ - /* * Data librarian. Defining DLB places most of the support files into * a tar-like file, thus making a neater installation. See *conf.h @@ -463,7 +433,7 @@ */ #define INSURANCE /* allow crashed game recovery */ -#ifndef MAC +#if !defined(MAC) && !defined(SHIM_GRAPHICS) #define CHDIR /* delete if no chdir() available */ #endif @@ -629,11 +599,10 @@ typedef unsigned char uchar; * glyph" code, then the escape codes for color and the glyph character * itself, and then the "end glyph" code. * - * To compile NetHack with this, add tile.c to WINSRC and tile.o to WINOBJ - * in the hints file or Makefile. - * Set boolean option vt_tiledata and/or vt_sounddata in your config file - * to turn either of these on. - * Note that gnome-terminal at least doesn't work with this. */ + * To compile NetHack with this, add tile.c to WINSRC and tile.o to WINOBJ in + * the hints file or Makefile. Set boolean option vt_tiledata and/or + * vt_sounddata in your config file to turn either of these on. Note that some + * terminals (e.g. old versions of gnome-terminal) don't work with this. */ /* #define TTY_TILES_ESCCODES */ /* #define TTY_SOUND_ESCCODES */ @@ -641,12 +610,6 @@ typedef unsigned char uchar; * at least 28 additional rows beneath the status window on your terminal */ /* #define TTY_PERM_INVENT */ -/* NetHack will execute an external program whenever a new message-window - * message is shown. The program to execute is given in environment variable - * NETHACK_MSGHANDLER. It will get the message as the only parameter. - * Only available with POSIX_TYPES, GNU C, or WIN32 */ -/* #define MSGHANDLER */ - /* enable status highlighting via STATUS_HILITE directives in run-time config file and the 'statushilites' option */ #define STATUS_HILITES /* support hilites of status fields */ diff --git a/include/config1.h b/include/config1.h index c4e14610c..3a8632564 100644 --- a/include/config1.h +++ b/include/config1.h @@ -31,6 +31,10 @@ #ifndef CROSSCOMPILE #define SHORT_FILENAMES #endif +/* this is not fully-implemented yet for msdos */ +#ifdef ENHANCED_SYMBOLS +#undef ENHANCED_SYMBOLS +#endif #endif /* diff --git a/include/context.h b/include/context.h index ab9baf3bb..589a80691 100644 --- a/include/context.h +++ b/include/context.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 context.h $NHDT-Date: 1646428003 2022/03/04 21:06:43 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.45 $ */ +/* NetHack 3.7 context.h $NHDT-Date: 1753856387 2025/07/29 22:19:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.58 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -135,6 +135,11 @@ struct achievement_tracking { boolean minetn_reached; /* avoid redundant checking for town entry */ }; +struct lifelists { + long total_seen_upclose; /* count of critters seen up close */ + long total_photographed; /* count of critters photographed (tourists) */ +}; + struct context_info { unsigned ident; /* social security number for each monster */ unsigned no_of_wizards; /* 0, 1 or 2 (wizard and his shadow) */ @@ -145,9 +150,11 @@ struct context_info { int current_fruit; /* fruit->fid corresponding to svp.pl_fruit[] */ int mysteryforce; /* adjusts how often "mysterious force" kicks in */ int rndencode; /* randomized escape sequence introducer */ + int startingpet_typ; /* monster type for initial pet */ int warnlevel; /* threshold (digit) to warn about unseen mons */ long next_attrib_check; /* next attribute check */ long seer_turn; /* when random clairvoyance will next kick in */ + long snickersnee_turn; /* Snickersnee last used to distance attack */ long stethoscope_seq; /* when a stethoscope was last used; first use * during a move takes no time, second uses move */ boolean travel; /* find way automatically to u.tx,u.ty */ @@ -160,6 +167,7 @@ struct context_info { boolean mv; 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]; struct dig_info digging; struct victual_info victual; @@ -173,6 +181,7 @@ struct context_info { struct tribute_info tribute; struct novel_tracking novel; struct achievement_tracking achieveo; + struct lifelists lifelist; char jingle[5 + 1]; }; diff --git a/include/cstd.h b/include/cstd.h index 64028749f..3e4764b52 100644 --- a/include/cstd.h +++ b/include/cstd.h @@ -27,9 +27,9 @@ * Common macro definitions * (C99) Fixed-width integer types * Input/output program utilities - * - * General utilities: memory management, program utilities, string - * conversions, random numbers, algorithms + * General utilities: memory management, + * program utilities, string conversions, + * random numbers, algorithms * String handling * (C99) Type-generic math (macros wrapping math.h and * complex.h) @@ -51,13 +51,17 @@ * */ #if !defined(__cplusplus) - #include +#include #include #include #include #include #include +#include +#else /* !__cplusplus */ +/* for FILE */ +#include #endif /* !__cplusplus */ #endif /* CSTD_H */ diff --git a/include/decl.h b/include/decl.h index 415d710b1..72f85eb90 100644 --- a/include/decl.h +++ b/include/decl.h @@ -181,7 +181,6 @@ struct instance_globals_a { struct h2o_ctx acid_ctx; boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_b { @@ -219,7 +218,6 @@ struct instance_globals_b { boolean bot_disabled; boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_c { @@ -260,7 +258,7 @@ struct instance_globals_c { /* symbols.c */ int currentgraphics; - /* files.c */ + /* files.c, cfgfiles.c */ char *cmdline_rcfile; /* set in unixmain.c, used in options.c */ char *config_section_chosen; char *config_section_current; @@ -298,8 +296,10 @@ struct instance_globals_c { short corpsenm_digested; /* monster type being digested, set by gulpum */ /* zap.c */ + /* new */ + boolean converted_savefile_loaded; + boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_d { @@ -340,8 +340,11 @@ struct instance_globals_d { boolean decor_fumble_override; boolean decor_levitate_override; + /* new */ + boolean deferred_showpaths; + char *deferred_showpaths_dir; + boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_e { @@ -362,7 +365,6 @@ struct instance_globals_e { to gb.beyond_savefile_load */ boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_f { @@ -387,7 +389,6 @@ struct instance_globals_f { long int followmsg; /* last time of follow message */ boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_g { @@ -422,6 +423,7 @@ struct instance_globals_g { long gmst_moves; struct obj *gmst_invent; genericptr_t *gmst_ubak, *gmst_disco, *gmst_mvitals; + struct spell gmst_spl_book[MAXSPELL + 1]; /* pline.c */ struct gamelog_line *gamelog; @@ -435,7 +437,6 @@ struct instance_globals_g { long glyphmap_perlevel_flags; boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_h { @@ -456,7 +457,6 @@ struct instance_globals_h { struct attack *hitmsg_prev; boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_i { @@ -490,7 +490,6 @@ struct instance_globals_i { /* new */ boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_j { @@ -499,7 +498,6 @@ struct instance_globals_j { int jumping_is_magic; /* current jump result of magic */ boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_k { @@ -513,7 +511,6 @@ struct instance_globals_k { boolean known; boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_l { @@ -579,7 +576,6 @@ struct instance_globals_l { char lua_copyright[LUA_COPYRIGHT_BUFSIZ]; boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_m { @@ -608,6 +604,9 @@ struct instance_globals_m { /* dokick.c */ struct rm *maploc; + /* mhitm.c */ + struct monst *mswallower; /* for gas spore explosion when it's swallowed*/ + /* mhitu.c */ int mhitu_dieroll; @@ -615,10 +614,10 @@ struct instance_globals_m { boolean made_branch; /* used only during level creation */ /* mkmap.c */ - int min_rx; /* rectangle bounds for regions */ - int max_rx; - int min_ry; - int max_ry; + coordxy min_rx; /* rectangle bounds for regions */ + coordxy max_rx; + coordxy min_ry; + coordxy max_ry; /* mkobj.c */ boolean mkcorpstat_norevive; /* for trolls */ @@ -646,7 +645,6 @@ struct instance_globals_m { * POT_WATER should become discovered */ boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_n { @@ -696,7 +694,6 @@ struct instance_globals_n { short nocreate4; boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_o { @@ -742,11 +739,9 @@ struct instance_globals_o { /* restore.c */ struct fruit *oldfruit; - long omoves; /* rumors.c */ int oracle_flg; /* -1=>don't use, 0=>need init, 1=>init done */ - unsigned long *oracle_loc; /* uhitm.c */ boolean override_confirmation; /* Used to flag attacks caused by @@ -755,7 +750,6 @@ struct instance_globals_o { boolean obj_zapped; boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_p { @@ -806,13 +800,11 @@ struct instance_globals_p { /* new stuff */ boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_q { boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_r { @@ -841,7 +833,6 @@ struct instance_globals_r { struct repo repo; boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_s { @@ -923,7 +914,6 @@ struct instance_globals_s { int seethru; /* 'bubble' debugging: clouds and water don't block light */ boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_t { @@ -949,8 +939,8 @@ struct instance_globals_t { const char *this_title; /* title for inventory list of specific type */ /* muse.c */ - int trapx; - int trapy; + coordxy trapx; + coordxy trapy; /* rumors.c */ long true_rumor_size; /* rumor size variables are signed so that value -1 @@ -974,7 +964,6 @@ struct instance_globals_t { int twohits; /* 0: single hit; 1: first of 2; 2: second of 2 */ boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_u { @@ -994,7 +983,6 @@ struct instance_globals_u { /* new stuff */ boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_v { @@ -1031,7 +1019,6 @@ struct instance_globals_v { struct sound_voice voice; boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_w { @@ -1040,6 +1027,9 @@ struct instance_globals_w { int warn_obj_cnt; /* count of monsters meeting criteria */ long wailmsg; + /* do_wear.c */ + uint8 wasinwater; + /* symbols.c */ nhsym warnsyms[WARNCOUNT]; /* the current warning display symbols */ @@ -1054,9 +1044,9 @@ struct instance_globals_w { /* new */ struct win_settings wsettings; /* wintype.h */ + long were_changes; /* were.c, allmain.c */ boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_x { @@ -1077,7 +1067,6 @@ struct instance_globals_x { coordxy xstart, xsize; boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_y { @@ -1095,7 +1084,6 @@ struct instance_globals_y { coordxy ystart, ysize; boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_z { @@ -1112,7 +1100,6 @@ struct instance_globals_z { * remember who zapped the wand. */ boolean havestate; - unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_saved_b { @@ -1188,6 +1175,10 @@ struct instance_globals_saved_n { struct instance_globals_saved_o { /* rumors.c */ unsigned oracle_cnt; /* oracles are handled differently from rumors... */ + unsigned long *oracle_loc; + + /* other */ + long omoves; /* level timestamp */ }; struct instance_globals_saved_p { diff --git a/include/display.h b/include/display.h index 24db18e91..fc52eac41 100644 --- a/include/display.h +++ b/include/display.h @@ -728,7 +728,7 @@ enum glyph_offsets { #define glyph_to_explosion(glyph) \ (glyph_is_explosion(glyph) ? (((glyph) - GLYPH_EXPLODE_OFF) % (S_expl_br - S_expl_tl + 1)) : 0) #define glyph_to_warning(glyph) \ - (glyph_is_warning(glyph) ? ((glyph) - GLYPH_WARNING_OFF) : NO_GLYPH) + (glyph_is_warning(glyph) ? ((glyph) - GLYPH_WARNING_OFF) : 0) /* * Return true if the given glyph is what we want. Note that bodies are diff --git a/include/dungeon.h b/include/dungeon.h index 09ea8dd0e..d4a1dcf62 100644 --- a/include/dungeon.h +++ b/include/dungeon.h @@ -39,7 +39,8 @@ enum level_region_types { LR_BRANCH, LR_TELE, LR_UPTELE, - LR_DOWNTELE + LR_DOWNTELE, + LR_MONGEN, }; typedef struct dest_area { /* non-stairway level change identifier */ @@ -187,66 +188,71 @@ struct linfo { /* what the player knows about a single dungeon level */ /* initialized in mklev() */ +struct mapseen_feat { + /* feature knowledge that must be calculated from levl array */ + Bitfield(nfount, 2); + Bitfield(nsink, 2); + Bitfield(naltar, 2); + Bitfield(nthrone, 2); + + Bitfield(ngrave, 2); + Bitfield(ntree, 2); + Bitfield(water, 2); + Bitfield(lava, 2); + + Bitfield(ice, 2); + /* calculated from rooms array */ + Bitfield(nshop, 2); + Bitfield(ntemple, 2); + /* altar alignment; MSA_NONE if there is more than one and + they aren't all the same */ + Bitfield(msalign, 2); + + Bitfield(shoptype, 5); +}; +struct mapseen_flags { + Bitfield(notreachable, 1); /* can't get back to this level */ + Bitfield(forgot, 1); /* player has forgotten about this level */ + Bitfield(knownbones, 1); /* player aware of bones */ + Bitfield(oracle, 1); + Bitfield(sokosolved, 1); + Bitfield(bigroom, 1); + Bitfield(castle, 1); + Bitfield(castletune, 1); /* add tune hint to castle annotation */ + + Bitfield(valley, 1); + Bitfield(msanctum, 1); + Bitfield(ludios, 1); + Bitfield(roguelevel, 1); + /* quest annotations: quest_summons is for main dungeon level + with entry portal and is reset once quest has been finished; + questing is for quest home (level 1) */ + Bitfield(quest_summons, 1); /* heard summons from leader */ + Bitfield(questing, 1); /* quest leader has unlocked quest stairs */ + /* "gateway to sanctum" */ + Bitfield(vibrating_square, 1); /* found vibrating square 'trap'; + * flag cleared once the msanctum + * annotation has been added (on + * the next dungeon level; temple + * entered or high altar mapped) */ + Bitfield(spare1, 1); /* not used */ +}; + +struct mapseen_rooms { + Bitfield(seen, 1); + Bitfield(untended, 1); /* flag for shop without shk */ +}; + typedef struct mapseen { struct mapseen *next; /* next map in the chain */ branch *br; /* knows about branch via taking it in goto_level */ d_level lev; /* corresponding dungeon level */ - struct mapseen_feat { - /* feature knowledge that must be calculated from levl array */ - Bitfield(nfount, 2); - Bitfield(nsink, 2); - Bitfield(naltar, 2); - Bitfield(nthrone, 2); - - Bitfield(ngrave, 2); - Bitfield(ntree, 2); - Bitfield(water, 2); - Bitfield(lava, 2); - - Bitfield(ice, 2); - /* calculated from rooms array */ - Bitfield(nshop, 2); - Bitfield(ntemple, 2); - /* altar alignment; MSA_NONE if there is more than one and - they aren't all the same */ - Bitfield(msalign, 2); - - Bitfield(shoptype, 5); - } feat; - struct mapseen_flags { - Bitfield(notreachable, 1); /* can't get back to this level */ - Bitfield(forgot, 1); /* player has forgotten about this level */ - Bitfield(knownbones, 1); /* player aware of bones */ - Bitfield(oracle, 1); - Bitfield(sokosolved, 1); - Bitfield(bigroom, 1); - Bitfield(castle, 1); - Bitfield(castletune, 1); /* add tune hint to castle annotation */ - - Bitfield(valley, 1); - Bitfield(msanctum, 1); - Bitfield(ludios, 1); - Bitfield(roguelevel, 1); - /* quest annotations: quest_summons is for main dungeon level - with entry portal and is reset once quest has been finished; - questing is for quest home (level 1) */ - Bitfield(quest_summons, 1); /* heard summons from leader */ - Bitfield(questing, 1); /* quest leader has unlocked quest stairs */ - /* "gateway to sanctum" */ - Bitfield(vibrating_square, 1); /* found vibrating square 'trap'; - * flag cleared once the msanctum - * annotation has been added (on - * the next dungeon level; temple - * entered or high altar mapped) */ - Bitfield(spare1, 1); /* not used */ - } flags; + struct mapseen_feat feat; + struct mapseen_flags flags; /* custom naming */ char *custom; unsigned custom_lth; - struct mapseen_rooms { - Bitfield(seen, 1); - Bitfield(untended, 1); /* flag for shop without shk */ - } msrooms[(MAXNROFROOMS + 1) * 2]; /* same size as svr.rooms[] */ + struct mapseen_rooms msrooms[(MAXNROFROOMS + 1) * 2]; /* same size as svr.rooms[] */ /* dead heroes; might not have graves or ghosts */ struct cemetery *final_resting_place; /* same as level.bonesinfo */ } mapseen; diff --git a/include/engrave.h b/include/engrave.h index c0a53a6df..47fd84783 100644 --- a/include/engrave.h +++ b/include/engrave.h @@ -33,8 +33,9 @@ struct engr { * even when hero isn't (so behaves similarly * to how Elbereth did in 3.4.3) */ Bitfield(nowipeout, 1); /* this engraving will not degrade */ - Bitfield(eread, 1); /* the engraving text has been read or felt */ - /* 5 free bits */ + Bitfield(eread, 1); /* refers to the engaving text: read or felt */ + Bitfield(erevealed, 1); /* refers to engraving map symbol: revealed */ + /* 4 free bits */ }; #define newengr(lth) \ @@ -46,8 +47,7 @@ struct engr { #define spot_shows_engravings(x,y) \ (levl[(x)][(y)].typ == CORR \ - || levl[(x)][(y)].typ == SCORR \ || levl[(x)][(y)].typ == ICE \ - || levl[(x)][(y)].typ == ROOM ) + || levl[(x)][(y)].typ == ROOM) #endif /* ENGRAVE_H */ diff --git a/include/extern.h b/include/extern.h index 59080ad54..17923a5d2 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 extern.h $NHDT-Date: 1723580890 2024/08/13 20:28:10 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1435 $ */ +/* NetHack 3.7 extern.h $NHDT-Date: 1738638877 2025/02/03 19:14:37 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1476 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -67,6 +67,10 @@ * */ +#ifndef ARTIFACT_H +#include "artifact.h" +#endif + /* ### alloc.c ### */ #if 0 @@ -126,7 +130,9 @@ extern void use_unicorn_horn(struct obj **); extern boolean tinnable(struct obj *) NONNULLPTRS; extern void reset_trapset(void); extern int use_whip(struct obj *) NONNULLPTRS; +extern boolean could_pole_mon(void); extern int use_pole(struct obj *, boolean) NONNULLPTRS; +extern void maybe_dunk_boulders(coordxy, coordxy); extern void fig_transform(union any *, long) NONNULLARG1; extern int unfixable_trouble_count(boolean); @@ -136,7 +142,7 @@ extern void init_artifacts(void); extern void save_artifacts(NHFILE *); extern void restore_artifacts(NHFILE *); extern const char *artiname(int); -extern struct obj *mk_artifact(struct obj *, aligntyp); +extern struct obj *mk_artifact(struct obj *, aligntyp, uchar, boolean); extern const char *artifact_name(const char *, short *, boolean) NONNULLARG1; extern boolean exist_artifact(int, const char *) NONNULLPTRS; extern void artifact_exists(struct obj *, const char *, boolean, unsigned) ; @@ -181,6 +187,7 @@ extern void mkot_trap_warn(void); extern boolean is_magic_key(struct monst *, struct obj *); extern struct obj *has_magic_key(struct monst *); extern boolean is_art(struct obj *, int); +extern boolean permapoisoned(struct obj *); /* ### attrib.c ### */ @@ -246,6 +253,8 @@ extern void savebones(int, time_t, struct obj *); extern int getbones(void); extern boolean bones_include_name(const char *) NONNULLARG1; extern void fix_ghostly_obj(struct obj *) NONNULLARG1; +extern void newebones(struct monst *) NONNULLARG1; +extern void free_ebones(struct monst *) NONNULLARG1; /* ### botl.c ### */ @@ -301,6 +310,28 @@ extern boolean friday_13th(void); extern int night(void); extern int midnight(void); +/* ### cfgfiles.c ### */ + +#if !defined(CROSSCOMPILE) || defined(CROSSCOMPILE_TARGET) +extern int l_get_config_errors(lua_State *) NONNULLARG1; +#endif +extern int do_write_config_file(void); +extern boolean parse_config_line(char *) NONNULLARG1; +#ifdef USER_SOUNDS +extern boolean can_read_file(const char *) NONNULLARG1; +#endif +extern void config_error_init(boolean, const char *, boolean); +extern void config_erradd(const char *); +extern int config_error_done(void); +/* arg1 of read_config_file can be NULL to pass through + * to fopen_config_file() to mean 'use the default config file name' */ +extern boolean read_config_file(const char *, int); +extern boolean parse_conf_str(const char *str, boolean (*proc)(char *)); +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); + /* ### coloratt.c ### */ extern char *color_attr_to_str(color_attr *); @@ -394,8 +425,10 @@ extern char extcmd_initiator(void); extern int doextcmd(void); extern struct ext_func_tab *extcmds_getentry(int); extern int count_bind_keys(void); +extern int count_autocompletions(void); extern void get_changed_key_binds(strbuf_t *); extern void handler_rebind_keys(void); +extern void handler_change_autocompletions(void); extern int extcmds_match(const char *, int, int **); extern const char *key2extcmddesc(uchar); extern boolean bind_specialkey(uchar, const char *); @@ -411,7 +444,7 @@ extern int enter_explore_mode(void); extern boolean bind_mousebtn(int, const char *); extern boolean bind_key(uchar, const char *); extern void dokeylist(void); -extern coordxy xytod(coordxy, coordxy); +extern int xytod(coordxy, coordxy); extern void dtoxy(coord *, int); extern int movecmd(char, int); extern int dxdy_moveok(void); @@ -730,12 +763,14 @@ extern int remarm_swapwep(void); extern int destroy_arm(struct obj *); 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 *); +extern int count_worn_armor(void); /* ### dog.c ### */ extern void newedog(struct monst *) NONNULLARG1; extern void free_edog(struct monst *) NONNULLARG1; -extern void initedog(struct monst *) NONNULLARG1; +extern void initedog(struct monst *, boolean) NONNULLARG1; extern struct monst *make_familiar(struct obj *, coordxy, coordxy, boolean); extern struct monst *makedog(void); extern void update_mlstmv(void); @@ -757,7 +792,7 @@ extern struct obj *droppables(struct monst *) NONNULLARG1; extern int dog_nutrition(struct monst *, struct obj *) NONNULLPTRS; extern int dog_eat(struct monst *, struct obj *, coordxy, coordxy, boolean) NONNULLPTRS; -extern int pet_ranged_attk(struct monst *) NONNULLARG1; +extern int pet_ranged_attk(struct monst *, boolean) NONNULLARG1; extern int dog_move(struct monst *, int) NONNULLARG1; extern boolean could_reach_item(struct monst *, coordxy, coordxy) NONNULLARG1; extern void finish_meating(struct monst *) NONNULLARG1; @@ -940,20 +975,21 @@ extern char *build_english_list(char *) NONNULLARG1; /* ### engrave.c ### */ -extern char *random_engraving(char *) NONNULLARG1; +extern char *random_engraving(char *, char *) NONNULLARG12; extern void wipeout_text(char *, int, unsigned) NONNULLARG1; extern boolean can_reach_floor(boolean); -extern void cant_reach_floor(coordxy, coordxy, boolean, boolean); +extern void cant_reach_floor(coordxy, coordxy, boolean, boolean, boolean); extern struct engr *engr_at(coordxy, coordxy); extern struct engr *sengr_at(const char *, coordxy, coordxy, boolean) NONNULLARG1; extern void u_wipe_engr(int); extern void wipe_engr_at(coordxy, coordxy, xint16, boolean); extern void read_engr_at(coordxy, coordxy); -extern void make_engr_at(coordxy, coordxy, const char *, long, int) NONNULLARG3; +extern void make_engr_at(coordxy, coordxy, const char *, const char *, long, int) NONNULLARG3; extern void del_engr_at(coordxy, coordxy); extern int freehand(void); extern int doengrave(void); extern void sanitize_engravings(void); +extern void forget_engravings(void); extern void engraving_sanity_check(void); extern void save_engravings(NHFILE *) NONNULLARG1; extern void rest_engravings(NHFILE *) NONNULLARG1; @@ -964,6 +1000,7 @@ extern void make_grave(coordxy, coordxy, const char *); extern void disturb_grave(coordxy, coordxy); extern void see_engraving(struct engr *) NONNULLARG1; extern void feel_engraving(struct engr *) NONNULLARG1; +extern boolean engr_can_be_felt(struct engr *) NONNULLARG1; /* ### exper.c ### */ @@ -994,15 +1031,12 @@ extern void makerogueghost(void); /* ### files.c ### */ extern const char *nh_basename(const char *, boolean) NONNULLARG1; -#if !defined(CROSSCOMPILE) || defined(CROSSCOMPILE_TARGET) -extern int l_get_config_errors(lua_State *) NONNULLARG1; -#endif extern char *fname_encode(const char *, char, char *, char *, int) NONNULLPTRS; extern char *fname_decode(char, char *, char *, int) NONNULLPTRS; extern const char *fqname(const char *, int, int); extern FILE *fopen_datafile(const char *, const char *, int) NONNULLPTRS; -extern void zero_nhfile(NHFILE *) NONNULLARG1; +extern void init_nhfile(NHFILE *) NONNULLARG1; extern void close_nhfile(NHFILE *) NONNULLARG1; extern void rewind_nhfile(NHFILE *) NONNULLARG1; extern void set_levelfile_name(char *, int) NONNULLARG1; @@ -1025,6 +1059,7 @@ extern void set_error_savefile(void); extern NHFILE *create_savefile(void); extern NHFILE *open_savefile(void); extern int delete_savefile(void); +extern NHFILE *get_freeing_nhfile(void); extern NHFILE *restore_saved_game(void); extern int check_panic_save(void); #ifdef SELECTSAVED @@ -1034,22 +1069,14 @@ extern char **get_saved_games(void); extern void free_saved_games(char **); extern void nh_compress(const char *); extern void nh_uncompress(const char *); +extern void nh_sfconvert(const char *); +extern void nh_sfunconvert(const char *); +extern int delete_convertedfile(const char *); +extern void free_convert_filenames(void); extern boolean lock_file(const char *, int, int) NONNULLARG1; extern void unlock_file(const char *) NONNULLARG1; -extern int do_write_config_file(void); -extern boolean parse_config_line(char *) NONNULLARG1; -#ifdef USER_SOUNDS -extern boolean can_read_file(const char *) NONNULLARG1; -#endif -extern void config_error_init(boolean, const char *, boolean); -extern void config_erradd(const char *); -extern int config_error_done(void); -/* arg1 of read_config_file can be NULL to pass through - * to fopen_config_file() to mean 'use the default config file name' */ -extern boolean read_config_file(const char *, int); extern void check_recordfile(const char *); extern void read_wizkit(void); -extern boolean parse_conf_str(const char *str, boolean (*proc)(char *)); extern int read_sym_file(int); extern void paniclog(const char *, const char *) NONNULLPTRS; extern void testinglog(const char *, const char *, const char *); @@ -1065,11 +1092,13 @@ extern int nhclose(int); #ifdef DEBUG extern boolean debugcore(const char *, boolean); #endif -extern void reveal_paths(void); +extern void reveal_paths(int); extern boolean read_tribute(const char *, const char *, int, char *, int, unsigned); extern boolean Death_quote(char *, int) NONNULLARG1; extern void livelog_add(long ll_type, const char *) NONNULLARG2; +ATTRNORETURN extern void do_deferred_showpaths(int) NORETURN; +extern boolean contains_directory(const char *); /* ### fountain.c ### */ @@ -1168,6 +1197,7 @@ extern boolean in_town(coordxy, coordxy); extern void check_special_room(boolean); extern int dopickup(void); extern void lookaround(void); +extern boolean doorless_door(coordxy, coordxy); extern boolean crawl_destination(coordxy, coordxy); extern int monster_nearby(void); extern void end_running(boolean); @@ -1182,6 +1212,7 @@ extern int near_capacity(void); extern int calc_capacity(int); extern int max_capacity(void); extern boolean check_capacity(const char *); +extern void dump_weights(void); extern int inv_cnt(boolean); /* sometimes money_cnt(gi.invent) which can be null */ extern long money_cnt(struct obj *) NO_NNARGS; @@ -1324,6 +1355,7 @@ extern void silly_thing(const char *, struct obj *) NONNULLARG1; 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); /* ### ioctl.c ### */ @@ -1431,6 +1463,7 @@ extern void mkmonmoney(struct monst *, long) NONNULLARG1; extern int bagotricks(struct obj *, boolean, int *); extern boolean propagate(int, boolean, boolean); extern void summon_furies(int); +extern void dump_mongen(void); /* ### mcastu.c ### */ @@ -1474,6 +1507,7 @@ extern struct monst *cloneu(void); extern void expels(struct monst *, struct permonst *, boolean) NONNULLARG12; extern struct attack *getmattk(struct monst *, struct monst *, int, int *, struct attack *) NONNULLARG12; +extern boolean mtrapped_in_pit(struct monst *) NONNULLARG1; extern int mattacku(struct monst *) NONNULLARG1; boolean diseasemu(struct permonst *) NONNULLARG1; boolean u_slip_free(struct monst *, struct attack *) NONNULLARG12; @@ -1483,6 +1517,11 @@ extern int gazemu(struct monst *, struct attack *) NONNULLARG12; extern void mdamageu(struct monst *, int) NONNULLARG1; extern int could_seduce(struct monst *, struct monst *, struct attack *) NONNULLARG12; extern int doseduce(struct monst *) NONNULLARG1; +extern boolean mon_avoiding_this_attack(struct monst *, int) NONNULLARG1; +/* extern boolean ranged_attk_assessed(struct monst *mtmp, + boolean (*assessfunct)(struct monst *, int)) NONNULLARG1; +*/ +extern boolean ranged_attk_available(struct monst *mtmp) NONNULLARG1; /* ### minion.c ### */ @@ -1504,9 +1543,11 @@ extern void gain_guardian_angel(void); /* ### mklev.c ### */ extern void sort_rooms(void); -extern void add_room(int, int, int, int, boolean, schar, boolean); -extern void add_subroom(struct mkroom *, int, int, int, int, boolean, schar, - boolean) NONNULLARG1; +extern void add_room(coordxy, coordxy, coordxy, coordxy, + boolean, schar, boolean); +extern void add_subroom(struct mkroom *, + coordxy, coordxy, coordxy, coordxy, + boolean, schar, boolean) NONNULLARG1; extern void free_luathemes(enum lua_theme_group); extern void makecorridors(void); extern void add_door(coordxy, coordxy, struct mkroom *) NONNULLARG3; @@ -1532,8 +1573,8 @@ extern void mineralize(int, int, int, int, boolean); /* ### mkmap.c ### */ -extern void flood_fill_rm(int, int, int, boolean, boolean); -extern void remove_rooms(int, int, int, int); +extern void flood_fill_rm(coordxy, coordxy, int, boolean, boolean); +extern void remove_rooms(coordxy, coordxy, coordxy, coordxy); extern boolean litstate_rnd(int); /* ### mkmaze.c ### */ @@ -1552,6 +1593,7 @@ extern void bound_digging(void); extern void mkportal(coordxy, coordxy, xint16, xint16); extern boolean bad_location(coordxy, coordxy, coordxy, coordxy, coordxy, coordxy); +extern boolean is_exclusion_zone(xint16, coordxy, coordxy); /* dungeon.c u_on_rndspot() passes NULL final arg to place_lregion() */ extern void place_lregion(coordxy, coordxy, coordxy, coordxy, coordxy, coordxy, coordxy, coordxy, xint16, d_level *) NO_NNARGS; @@ -1596,6 +1638,8 @@ extern void costly_alteration(struct obj *, int) NONNULLARG1; extern void clear_dknown(struct obj *); extern void unknow_object(struct obj *); extern struct obj *mksobj(int, boolean, boolean) NONNULL; +extern boolean stone_object_type(unsigned); +extern boolean stone_furniture_type(unsigned); extern int bcsign(struct obj *) NONNULLARG1; extern int weight(struct obj *) NONNULLARG1; extern struct obj *mkgold(long, coordxy, coordxy); @@ -1740,6 +1784,7 @@ extern struct monst *get_iter_mons(boolean (*)(struct monst *)); extern struct monst *get_iter_mons_xy(boolean (*)(struct monst *, coordxy, coordxy), coordxy, coordxy); +extern int healmon(struct monst *, int, int) NONNULLARG1; extern void rescham(void); extern void restartcham(void); extern void restore_cham(struct monst *) NONNULLARG1; @@ -1767,6 +1812,10 @@ extern void dealloc_mextra(struct monst *); extern boolean usmellmon(struct permonst *); extern void mimic_hit_msg(struct monst *, short); extern void adj_erinys(unsigned); +extern void see_monster_closeup(struct monst *, boolean) NONNULLARG1; +extern void see_nearby_monsters(void); +extern void shieldeff_mon(struct monst *) NONNULLARG1; +extern void flash_mon(struct monst *) NONNULLARG1; /* ### mondata.c ### */ @@ -1776,6 +1825,7 @@ extern boolean attacktype(struct permonst *, int) NONNULLARG1; extern boolean noattacks(struct permonst *) NONNULLARG1; extern boolean poly_when_stoned(struct permonst *) NONNULLARG1; extern boolean defended(struct monst *, int) NONNULLARG1; +extern boolean Resists_Elem(struct monst *, int) NONNULLARG1; extern boolean resists_drli(struct monst *) NONNULLARG1; extern boolean resists_magm(struct monst *) NONNULLARG1; extern boolean resists_blnd(struct monst *) NONNULLARG1; @@ -1926,6 +1976,7 @@ extern int dosuspend(void); extern void nt_regularize(char *); extern int(*nt_kbhit)(void); extern void Delay(int); +boolean get_user_home_folder(char *, size_t); # ifdef CRASHREPORT struct CRctxt; extern struct CRctxt *ctxp; @@ -2065,12 +2116,7 @@ extern void tutorial(boolean); #endif /* MAKEDEFS_C MDLIB_C CPPREGEX_C */ /* ### {cpp,pmatch,posix}regex.c ### */ - -extern struct nhregex *regex_init(void); -extern boolean regex_compile(const char *, struct nhregex *) NONNULLARG1; -extern char *regex_error_desc(struct nhregex *, char *) NONNULLARG2; -extern boolean regex_match(const char *, struct nhregex *) NO_NNARGS; -extern void regex_free(struct nhregex *) NONNULLARG1; +#include "nhregex.h" #if !defined(MAKEDEFS_C) && !defined(MDLIB_C) && !defined(CPPREGEX_C) @@ -2083,6 +2129,7 @@ extern void consoletty_open(int); extern void consoletty_rubout(void); extern int tgetch(void); extern int console_poskey(coordxy *, coordxy *, int *); +void console_g_putch(int in_ch); extern void set_output_mode(int); extern void synch_cursor(void); extern void nethack_enter_consoletty(void); @@ -2246,6 +2293,10 @@ extern boolean msgtype_parse_add(char *) NONNULLARG1; 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); +#ifdef TTY_PERM_INVENT +extern void check_perm_invent_again(void); +#endif /* ### pager.c ### */ @@ -2283,7 +2334,14 @@ extern int getlock(void); extern const char *get_portable_device(void); #endif -/* ### pcsys.c ### */ +/* ### pcsys.c, windsys.c ### */ +#if defined(MICRO) || defined(WIN32) +ATTRNORETURN extern void nethack_exit(int) NORETURN; +#else +#define nethack_exit exit +#endif + +/* ### pcsys.c ### */ #if defined(MICRO) || defined(WIN32) extern void flushout(void); @@ -2355,6 +2413,7 @@ extern int loot_mon(struct monst *, int *, boolean *) NO_NNARGS; extern int dotip(void); extern struct autopickup_exception *check_autopickup_exceptions(struct obj *) NONNULLARG1; extern boolean autopick_testobj(struct obj *, boolean) NONNULLARG1; +extern boolean u_safe_from_fatal_corpse(struct obj *obj, int) NONNULLARG1; /* ### pline.c ### */ @@ -2621,16 +2680,20 @@ extern int dorecover(NHFILE *) NONNULLARG1; extern void restcemetery(NHFILE *, struct cemetery **) NONNULLARG12; extern void trickery(char *) NO_NNARGS; extern void getlev(NHFILE *, int, xint8) NONNULLARG1; -extern void get_plname_from_file(NHFILE *, char *) NONNULLARG12; +extern void get_plname_from_file(NHFILE *, char *, boolean) NONNULLARG12; #ifdef SELECTSAVED extern int restore_menu(winid); #endif -extern void minit(void); extern boolean lookup_id_mapping(unsigned, unsigned *) NONNULLARG2; -extern int validate(NHFILE *, const char *, boolean) NONNULLARG1; /* extern void reset_restpref(void); */ /* extern void set_restpref(const char *); */ /* extern void set_savepref(const char *); */ +#ifdef SFCTOOL +void rest_bubbles(NHFILE *); +void restore_gamelog(NHFILE *); +boolean restgamestate(NHFILE *); +void restore_msghistory(NHFILE *); +#endif /* ### rip.c ### */ @@ -2776,11 +2839,7 @@ extern void bufoff(int); extern void bflush(int); extern void bwrite(int, const genericptr_t, unsigned) NONNULLARG2; extern void mread(int, genericptr_t, unsigned) NONNULLARG2; -extern void minit(void); extern void bclose(int); -#if defined(ZEROCOMP) -extern void zerocomp_bclose(int); -#endif /* ### shk.c ### */ @@ -2894,6 +2953,7 @@ extern void whimper(struct monst *) NONNULLARG1; extern void beg(struct monst *) NONNULLARG1; extern const char *maybe_gasp(struct monst *) NONNULLARG1; extern const char *cry_sound(struct monst *) NONNULLARG1; +extern int domonnoise(struct monst *) NONNULLARG1; extern int dotalk(void); extern int tiphat(void); #ifdef USER_SOUNDS @@ -2918,6 +2978,7 @@ extern char *get_sound_effect_filename(int32_t seidint, extern char *base_soundname_to_filename(char *, char *, size_t, int32_t) NONNULLARG1; extern void set_voice(struct monst *, int32_t, int32_t, int32_t) NO_NNARGS; extern void sound_speak(const char *) NO_NNARGS; +extern enum soundlib_ids soundlib_id_from_opt(char *); /* ### sp_lev.c ### */ @@ -2936,7 +2997,7 @@ extern void flip_level_rnd(int, boolean); extern boolean check_room(coordxy *, coordxy *, coordxy *, coordxy *, boolean) NONNULLPTRS; extern boolean create_room(coordxy, coordxy, coordxy, coordxy, coordxy, coordxy, xint16, xint16); -extern boolean dig_corridor(coord *, coord *, boolean, schar, schar) NONNULLARG12; +extern boolean dig_corridor(coord *, coord *, int *, boolean, schar, schar) NONNULLARG12; extern void fill_special_room(struct mkroom *) NO_NNARGS; extern void wallify_map(coordxy, coordxy, coordxy, coordxy); extern boolean load_special(const char *) NONNULLARG1; @@ -3025,7 +3086,7 @@ extern struct obj *findgold(struct obj *) NO_NNARGS; extern void rider_cant_reach(void); extern boolean can_saddle(struct monst *) NONNULLARG1; extern int use_saddle(struct obj *) NONNULLARG1; -extern void put_saddle_on_mon(struct obj *, struct monst *) NONNULLARG12; +extern void put_saddle_on_mon(struct obj *, struct monst *) NONNULLARG2; extern boolean can_ride(struct monst *) NONNULLARG1; extern int doride(void); extern boolean mount_steed(struct monst *, boolean) NO_NNARGS; @@ -3078,6 +3139,8 @@ extern boolean goodpos(coordxy, coordxy, struct monst *, mmflags_nht) NO_NNARGS; extern boolean enexto(coord *, coordxy, coordxy, struct permonst *) NONNULLARG1; +extern boolean enexto_gpflags(coord *, coordxy, coordxy, struct permonst *, + mmflags_nht) NONNULLARG1; extern boolean enexto_core(coord *, coordxy, coordxy, struct permonst *, mmflags_nht) NONNULLARG1; extern void teleds(coordxy, coordxy, int); @@ -3085,6 +3148,7 @@ extern int collect_coords(coord *, coordxy, coordxy, int, unsigned, boolean (*)(coordxy, coordxy)) NONNULLARG1; extern boolean safe_teleds(int); extern boolean teleport_pet(struct monst *, boolean) NONNULLARG1; +extern void tele_to_rnd_pet(void); extern void tele(void); extern void scrolltele(struct obj *) NO_NNARGS; extern int dotelecmd(void); @@ -3208,6 +3272,7 @@ extern void drain_en(int, boolean); extern int dountrap(void); extern int could_untrap(boolean, boolean); extern void cnv_trap_obj(int, int, struct trap *, boolean) NONNULLARG3; +extern boolean into_vs_onto(int); extern int untrap(boolean, coordxy, coordxy, struct obj *) NO_NNARGS; extern boolean openholdingtrap(struct monst *, boolean *) NO_NNARGS; extern boolean closeholdingtrap(struct monst *, boolean *) NO_NNARGS; @@ -3350,6 +3415,7 @@ extern boolean mhitm_knockback(struct monst *, struct monst *,struct attack *, extern int passive(struct monst *, struct obj *, boolean, boolean, uchar, boolean) NONNULLARG1; 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 int flash_hits_mon(struct monst *, struct obj *) NONNULLARG12; extern void light_hits_gremlin(struct monst *, int) NONNULLARG1; @@ -3367,6 +3433,7 @@ 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 */ /* ### unixtty.c ### */ @@ -3448,15 +3515,18 @@ extern int doextversion(void); extern boolean comp_times(long); #endif extern boolean check_version(struct version_info *, const char *, boolean, - unsigned long) NONNULLARG12; -extern boolean uptodate(NHFILE *, const char *, unsigned long) NONNULLARG1; -extern void store_formatindicator(NHFILE *) NONNULLARG1; + unsigned long) NONNULLARG1; +extern int uptodate(NHFILE *, const char *, unsigned long) NONNULLARG1; extern void store_version(NHFILE *) NONNULLARG1; extern unsigned long get_feature_notice_ver(char *) NO_NNARGS; extern unsigned long get_current_feature_ver(void); extern const char *copyright_banner_line(int) NONNULL; extern void early_version_info(boolean); extern void dump_version_info(void); +extern void store_critical_bytes(NHFILE *) NONNULLARG1; +extern int compare_critical_bytes(NHFILE *, int *, unsigned long) NONNULLARG1; +extern int get_critical_size_count(void); +extern int validate(NHFILE *, const char *, boolean) NONNULLARG1; /* ### video.c ### */ @@ -3475,12 +3545,14 @@ extern int assign_videocolors(char *) NONNULLARG1; /* ### vision.c ### */ +extern boolean get_viz_clear(int, int); extern void vision_init(void); extern int does_block(int, int, struct rm *) NONNULLARG3; extern void vision_reset(void); extern void vision_recalc(int); extern void block_point(int, int); extern void unblock_point(int, int); +extern void recalc_block_point(coordxy, coordxy); extern boolean clear_path(int, int, int, int); extern void do_clear_area(coordxy, coordxy, int, void(*)(coordxy, coordxy, void *), genericptr_t); @@ -3610,6 +3682,8 @@ extern int weapon_hit_bonus(struct obj *) NO_NNARGS; extern int weapon_dam_bonus(struct obj *) NO_NNARGS; extern void skill_init(const struct def_skill *) NONNULLARG1; extern void setmnotwielded(struct monst *, struct obj *) NONNULLARG1; +extern const struct throw_and_return_weapon *autoreturn_weapon(struct obj *) + NONNULLARG1; /* ### were.c ### */ @@ -3786,6 +3860,7 @@ extern boolean worm_cross(int, int, int, int); extern int wseg_at(struct monst *, int, int) NO_NNARGS; extern void flip_worm_segs_vertical(struct monst *, int, int) NONNULLARG1; extern void flip_worm_segs_horizontal(struct monst *, int, int) NONNULLARG1; +extern void redraw_worm(struct monst *); /* ### worn.c ### */ diff --git a/include/flag.h b/include/flag.h index e774a1c31..bd6a5dc58 100644 --- a/include/flag.h +++ b/include/flag.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 flag.h $NHDT-Date: 1715979826 2024/05/17 21:03:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.246 $ */ +/* NetHack 3.7 flag.h $NHDT-Date: 1744860497 2025/04/16 19:28:17 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.251 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -224,6 +224,12 @@ struct accessibility_data { a11y.mon_notices_blocked = 0; \ } } while(0) +enum debug_fuzzer_states { + fuzzer_off, + fuzzer_impossible_panic, + fuzzer_impossible_continue +}; + /* * Stuff that really isn't option or platform related and does not * get saved and restored. They are set and cleared during the game @@ -231,18 +237,23 @@ struct accessibility_data { * and probably warrant a structure of their own elsewhere some day. */ struct instance_flags { - boolean query_menu; /* use a menu for yes/no queries */ - boolean showdamage; boolean defer_plname; /* X11 hack: askname() might not set svp.plname */ + boolean fuzzerpending; /* fuzzing requested on command line but not active + * yet (to allow interactive initialization prior + * to input becoming taken over); + * True => enable fuzzer when entering moveloop */ boolean herecmd_menu; /* use menu when mouseclick on yourself */ boolean invis_goldsym; /* gold symbol is ' '? */ boolean in_lua; /* executing a lua script */ boolean lua_testing; /* doing lua tests */ + boolean term_gone; /* terminal is gone, abort abort abort */ boolean nofollowers; /* level change ignores pets (for tutorial) */ boolean partly_eaten_hack; /* extra flag for xname() used when it's called * indirectly so we can't use xname_flags() */ + boolean query_menu; /* use a menu for yes/no queries */ 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 */ 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 */ @@ -254,6 +265,8 @@ struct instance_flags { int getloc_filter; /* GFILTER_foo */ int in_lava_effects; /* hack for Boots_off() */ int last_msg; /* indicator of last message player saw */ + int menuobjsyms; /* value of 'menu_objsyms' option; + * ought to be in flags rather than iflags */ int override_ID; /* true to force full identification of objects */ int parse_config_file_src; /* hack for parse_config_line() */ int purge_monsters; /* # of dead monsters still on fmon list */ @@ -290,13 +303,13 @@ struct instance_flags { */ unsigned msg_history; /* hint: # of top lines to save */ int getpos_coords; /* show coordinates when getting cursor position */ - int menuinvertmode; /* 0 = invert toggles every item; - 1 = invert skips 'all items' item */ + int menuinvertmode; /* 0 = invert toggles every item; + * 1 = invert skips 'all items' item */ color_attr menu_headings; /* CLR_ and ATR_ for menu headings */ uint32_t colorcount; /* store how many colors terminal is capable of */ boolean use_truecolor; /* force use of truecolor */ #ifdef ALTMETA - boolean altmeta; /* Alt-c sends ESC c rather than M-c */ + boolean altmeta; /* Alt+c sends ESC c rather than M-c */ #endif boolean autodescribe; /* autodescribe mode in getpos() */ boolean cbreak; /* in cbreak mode, rogue format */ @@ -305,7 +318,8 @@ struct instance_flags { boolean echo; /* 1 to echo characters */ boolean force_invmenu; /* always menu when handling inventory */ boolean hilite_pile; /* mark piles of objects with a hilite */ - boolean menu_head_objsym; /* Show obj symbol in menu headings */ + boolean menu_head_objsym; /* Show obj symbol in menu headings; controlled + * by 'menuobjsyms' */ boolean menu_overlay; /* Draw menus over the map */ boolean menu_requested; /* Flag for overloaded use of 'm' prefix * on some non-move commands */ @@ -313,6 +327,7 @@ struct instance_flags { boolean news; /* print news */ boolean num_pad; /* use numbers for movement commands */ boolean perm_invent; /* display persistent inventory window */ + boolean perm_invent_pending; /* need to try again */ 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 */ @@ -323,6 +338,8 @@ struct instance_flags { boolean tux_penalty; /* True iff hero is a monk and wearing a suit */ boolean use_background_glyph; /* use background glyph when appropriate */ boolean use_menu_color; /* use color in menus; only if wc_color */ + boolean use_menu_glyphs; /* use object glyphs in menus, if the port + * supports it; controlled by 'menuobjsyms' */ #ifdef STATUS_HILITES long hilite_delta; /* number of moves to leave a temp hilite lit */ long unhilite_deadline; /* time when oldest temp hilite should be unlit */ diff --git a/include/global.h b/include/global.h index 0643ca13d..6d785ac9a 100644 --- a/include/global.h +++ b/include/global.h @@ -6,8 +6,6 @@ #ifndef GLOBAL_H #define GLOBAL_H -#include - /* * Files expected to exist in the playground directory (possibly inside * a dlb container file). @@ -117,16 +115,6 @@ typedef uchar nhsym; #endif #endif -#if 0 -/* comment out to test effects of each #define -- these will probably - * disappear eventually - */ -#ifdef INTERNAL_COMP -#define RLECOMP /* run-length compression of levl array - JLee */ -#define ZEROCOMP /* zero-run compression of everything - Olaf Seibert */ -#endif -#endif - /* #define SPECIALIZATION */ /* do "specialized" version of new topology */ #ifdef BITFIELDS @@ -359,25 +347,8 @@ struct version_info { unsigned long incarnation; /* actual version number */ unsigned long feature_set; /* bitmask of config settings */ unsigned long entity_count; /* # of monsters and objects */ - unsigned long struct_sizes1; /* size of key structs */ - unsigned long struct_sizes2; /* size of more key structs */ }; -struct savefile_info { - unsigned long sfi1; /* compression etc. */ - unsigned long sfi2; /* miscellaneous */ - unsigned long sfi3; /* thirdparty */ -}; -#ifdef NHSTDC -#define SFI1_EXTERNALCOMP (1UL) -#define SFI1_RLECOMP (1UL << 1) -#define SFI1_ZEROCOMP (1UL << 2) -#else -#define SFI1_EXTERNALCOMP (1L) -#define SFI1_RLECOMP (1L << 1) -#define SFI1_ZEROCOMP (1L << 2) -#endif - /* This is used to store some build-info data that used to be present in makedefs-generated header file date.h */ @@ -393,8 +364,6 @@ struct nomakedefs_s { unsigned long version_features; unsigned long ignored_features; unsigned long version_sanity1; - unsigned long version_sanity2; - unsigned long version_sanity3; unsigned long build_time; }; extern struct nomakedefs_s nomakedefs; @@ -431,6 +400,8 @@ extern struct nomakedefs_s nomakedefs; #define PL_CSIZ 32 /* sizeof pl_character */ #define PL_FSIZ 32 /* fruit name */ #define PL_PSIZ 63 /* player-given names for pets, other monsters, objects */ +/* room for "name-role-race-gend-algn" plus 1 character playmode code */ +#define PL_NSIZ_PLUS (PL_NSIZ + 4 * (1 + 3) + 1) /* 49 */ #define MAXDUNGEON 16 /* current maximum number of dungeons */ #define MAXLEVEL 32 /* max number of levels in one dungeon */ @@ -458,6 +429,7 @@ extern struct nomakedefs_s nomakedefs; /* PANICTRACE: Always defined for NH_DEVEL_STATUS != NH_STATUS_RELEASED but only for supported platforms. */ +#ifndef NOPANICTRACE #ifdef UNIX #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) /* see end.c */ @@ -468,6 +440,7 @@ extern struct nomakedefs_s nomakedefs; #endif /* CROSS_TO_WASM | CROSS_TO_MSDOS */ #endif /* NH_DEVEL_STATUS != NH_STATUS_RELEASED */ #endif /* UNIX */ +#endif /* !NOPANICTRACE */ /* The following are meaningless if PANICTRACE is not defined: */ #if defined(__linux__) && defined(__GLIBC__) && (__GLIBC__ >= 2) @@ -480,6 +453,9 @@ extern struct nomakedefs_s nomakedefs; #if !defined(CROSS_TO_WASM) /* no popen in WASM */ #define PANICTRACE_GDB #endif +#ifdef CROSS_TO_WASM +#undef COMPRESS +#endif #endif /* Supply nethack_enter macro if not supplied by port */ @@ -509,7 +485,6 @@ extern struct nomakedefs_s nomakedefs; #define C(c) (0x1f & (c)) #endif -#define unctrl(c) ((c) <= C('z') ? (0x60 | (c)) : (c)) #define unmeta(c) (0x7f & (c)) /* Game log message type flags */ @@ -596,4 +571,6 @@ typedef enum NHL_pcall_action { NHLpa_impossible } NHL_pcall_action; +#define SFCTOOL_BIT (1UL << 30) + #endif /* GLOBAL_H */ diff --git a/include/hack.h b/include/hack.h index 088007784..361b95cd3 100644 --- a/include/hack.h +++ b/include/hack.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 hack.h $NHDT-Date: 1725653009 2024/09/06 20:03:29 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.262 $ */ +/* NetHack 3.7 hack.h $NHDT-Date: 1736530208 2025/01/10 09:30:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.266 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Pasi Kallinen, 2017. */ /* NetHack may be freely redistributed. See license for details. */ @@ -12,6 +12,7 @@ #include "lint.h" #include "align.h" +#include "weight.h" #include "dungeon.h" #include "stairs.h" #include "objclass.h" @@ -29,13 +30,11 @@ #include "mkroom.h" #include "obj.h" #include "quest.h" -#include "rect.h" #include "region.h" #include "rm.h" #include "selvar.h" #include "sndprocs.h" #include "spell.h" -#include "sym.h" #include "sys.h" #include "timeout.h" #include "winprocs.h" @@ -48,7 +47,6 @@ #define ON 1 #define OFF 0 #define BOLT_LIM 8 /* from this distance ranged attacks will be made */ -#define MAX_CARR_CAP 1000 /* so that boulders can be heavier */ #define DUMMY { 0 } /* array initializer, letting [1..N-1] default */ #define DEF_NOTHING ' ' /* default symbol for NOTHING and UNEXPLORED */ @@ -67,9 +65,6 @@ #define CXN_ARTICLE 8 /* include a/an/the prefix */ #define CXN_NOCORPSE 16 /* suppress " corpse" suffix */ -/* weight increment of heavy iron ball */ -#define IRON_BALL_W_INCR 160 - /* number of turns it takes for vault guard to show up */ #define VAULT_GUARD_TIME 30 @@ -347,7 +342,6 @@ enum digcheck_result { DIGCHECK_FAIL_OBJ_POOL_OR_TRAP }; - /* Dismount: causes for why you are no longer riding */ enum dismount_types { @@ -442,6 +436,8 @@ enum earlyarg { , ARG_DUMPENUMS #endif , ARG_DUMPGLYPHIDS + , ARG_DUMPMONGEN + , ARG_DUMPWEIGHTS #ifdef WIN32 , ARG_WINDOWS #endif @@ -673,6 +669,8 @@ struct mvitals { uchar born; uchar died; uchar mvflags; + Bitfield(seen_close, 1); + Bitfield(photographed, 1); }; @@ -767,13 +765,7 @@ struct role_filter { boolean roles[NUM_ROLES + 1]; short mask; }; - -enum saveformats { - invalid = 0, - historical = 1, /* entire struct, binary, as-is */ - lendian = 2, /* each field, binary, little-endian */ - ascii = 3 /* each field, ascii text (just proof of concept) */ -}; +#define NUM_RACES (5) struct selectionvar { int wid, hei; @@ -796,6 +788,7 @@ struct sinfo { int exiting; /* an exit handler is executing */ int saving; /* creating a save file */ int restoring; /* reloading a save file */ + int freeingdata; /* in saveobjchn(), mode FREEING */ int in_getlev; /* in getlev() */ int in_moveloop; /* normal gameplay in progress */ int in_impossible; /* reporting a warning */ @@ -808,6 +801,7 @@ struct sinfo { int in_sanity_check; /* for impossible() during sanity checking */ int config_error_ready; /* config_error_add is ready, available */ int beyond_savefile_load; /* set when past savefile loading */ + int savefile_completed; /* savefile has completed writing */ #ifdef PANICLOG int in_paniclog; /* writing a panicloc entry */ #endif @@ -859,6 +853,20 @@ typedef struct strbuf { char buf[256]; } strbuf_t; +enum stoning_checks { + st_gloves = 0x1, /* wearing gloves? */ + st_corpse = 0x2, /* is it a corpse obj? */ + st_petrifies = 0x4, /* does the corpse petrify on touch? */ + st_resists = 0x8, /* do you have stoning resistance? */ + st_all = (st_gloves | st_corpse | st_petrifies | st_resists) +}; + +struct throw_and_return_weapon { + short otyp; + int range; + Bitfield(tethered, 1); +}; + struct trapinfo { struct obj *tobj; coordxy tx, ty; @@ -886,6 +894,19 @@ typedef struct { #define UTD_SKIP_SANITY1 0x04 #define UTD_SKIP_SAVEFILEINFO 0x08 #define UTD_WITHOUT_WAITSYNCH_PERFILE 0x10 +#define UTD_QUIETLY 0x20 + +/* Values for savefile status */ +#define SF_UPTODATE 0 +#define SF_OUTDATED 1 +#define SF_CRITICAL_BYTE_COUNT_MISMATCH 2 +#define SF_DM_IL32LLP64_ON_ILP32LL64 3 /* Wind x64 savefile on x86 */ +#define SF_DM_I32LP64_ON_ILP32LL64 4 /* Unix 64 savefile on x86 */ +#define SF_DM_ILP32LL64_ON_I32LP64 5 /* x86 savefile on Unix 64 */ +#define SF_DM_ILP32LL64_ON_IL32LLP64 6 /* x86 savefile on Wind x64 */ +#define SF_DM_I32LP64_ON_IL32LLP64 7 /* Unix 64 savefile on Wind x64 */ +#define SF_DM_IL32LLP64_ON_I32LP64 8 /* Wind x64 savefile on Unix 64 */ +#define SF_DM_MISMATCH 9 /* generic savefile byte mismatch */ #define ENTITIES 2 struct valuable_data { @@ -927,34 +948,49 @@ struct xlock_s { boolean magic_key; }; +#define MAX_BMASK 4 + /* NetHack ftypes */ #define NHF_LEVELFILE 1 #define NHF_SAVEFILE 2 #define NHF_BONESFILE 3 /* modes */ -#define READING 0x0 -#define COUNTING 0x1 -#define WRITING 0x2 -#define FREEING 0x4 -#define MAX_BMASK 4 +#define READING 0x0 +#define COUNTING 0x01 +#define WRITING 0x02 +#define FREEING 0x04 +#define CONVERTING 0x08 +#define UNCONVERTING 0x10 +#if 0 /* operations of the various saveXXXchn & co. routines */ #define perform_bwrite(nhfp) ((nhfp)->mode & (COUNTING | WRITING)) #define release_data(nhfp) ((nhfp)->mode & FREEING) +#endif + +/* operations of the various saveXXXchn & co. routines */ +#define update_file(nhfp) ((nhfp)->mode & (COUNTING | WRITING)) +#define release_data(nhfp) ((nhfp)->mode & FREEING) + +enum saveformats { + invalid = 0, + historical = 1, /* entire struct, binary, as-is */ + exportascii = 2, /* each field written out as ascii text */ + NUM_SAVEFORMATS +}; /* Content types for fieldlevel files */ struct fieldlevel_content { boolean deflt; /* individual fields */ boolean binary; /* binary rather than text */ - boolean json; /* JSON */ }; -typedef struct { +struct nh_file { int fd; /* for traditional structlevel binary writes */ - int mode; /* holds READING, WRITING, or FREEING modes */ + int mode; /* holds READING, WRITING, FREEING, CONVERTING modes */ int ftype; /* NHF_LEVELFILE, NHF_SAVEFILE, or NHF_BONESFILE */ int fnidx; /* index of procs for fieldlevel saves */ - long count; /* holds current line count for default style file, - field count for binary style */ + long rcount, /* read count since opening */ + wcount; /* write count since opening */ boolean structlevel; /* traditional structure binary saves */ boolean fieldlevel; /* fieldlevel saves each field individually */ boolean addinfo; /* if set, some additional context info from core */ @@ -965,7 +1001,10 @@ typedef struct { FILE *fplog; /* file pointer logfile */ FILE *fpdebug; /* file pointer debug info */ struct fieldlevel_content style; -} NHFILE; + struct nh_file *nhfpconvert; +}; + +typedef struct nh_file NHFILE; /* Monster name articles */ #define ARTICLE_NONE 0 @@ -1142,6 +1181,10 @@ typedef uint32_t mmflags_nht; /* makemon MM_ flags */ #define MHID_ALTMON 4 /* if mimicking a monster, include that */ #define MHID_REGION 8 /* include region when mon is in one */ +/* flags for that_is_a_mimic() */ +#define MIM_REVEAL 1 /* seemimic() */ +#define MIM_OMIT_WAIT 2 /* strip beginning from "Wait! That is a " */ + /* flags for make_corpse() and mkcorpstat(); 0..7 are recorded in obj->spe */ #define CORPSTAT_NONE 0x00 #define CORPSTAT_GENDER 0x03 /* 0x01 | 0x02 */ @@ -1247,11 +1290,12 @@ typedef uint32_t mmflags_nht; /* makemon MM_ flags */ /* flag for suppressing perm_invent update when name gets assigned */ #define ONAME_SKIP_INVUPD 0x0200U /* don't call update_inventory() */ -/* Flags to control find_mid() */ +/* Flags to control find_mid() and whereis_mon() */ #define FM_FMON 0x01 /* search the fmon chain */ #define FM_MIGRATE 0x02 /* search the migrating monster chain */ #define FM_MYDOGS 0x04 /* search gm.mydogs */ -#define FM_EVERYWHERE (FM_FMON | FM_MIGRATE | FM_MYDOGS) +#define FM_YOU 0x08 /* check for gy.youmonst */ +#define FM_EVERYWHERE (FM_YOU | FM_FMON | FM_MIGRATE | FM_MYDOGS) /* Flags to control pick_[race,role,gend,align] routines in role.c */ #define PICK_RANDOM 0 @@ -1498,17 +1542,16 @@ typedef uint32_t mmflags_nht; /* makemon MM_ flags */ #define getlogin() ((char *) 0) #endif /* MICRO */ -/* The function argument to qsort() requires a particular - * calling convention under WINCE which is not the default - * in that environment. - */ -#if defined(WIN_CE) -#define QSORTCALLBACK __cdecl -#else +/* These may have been defined to platform-specific values in *conf.h + * or on the compiler command line from a hints file or Makefile */ + +#ifndef QSORTCALLBACK #define QSORTCALLBACK #endif +#ifndef SIG_RET_TYPE #define SIG_RET_TYPE void (*)(int) +#endif #define DEVTEAM_EMAIL "devteam@nethack.org" #define DEVTEAM_URL "https://www.nethack.org/" @@ -1517,8 +1560,13 @@ typedef uint32_t mmflags_nht; /* makemon MM_ flags */ #include "nhlua.h" #endif +#if !defined(RECOVER_C) + #include "extern.h" +#include "savefile.h" #include "decl.h" +#endif /* RECOVER_C */ + #endif /* HACK_H */ diff --git a/include/hacklib.h b/include/hacklib.h index 5bea79023..18630e1b1 100644 --- a/include/hacklib.h +++ b/include/hacklib.h @@ -78,7 +78,9 @@ extern void nh_snprintf_w_impossible(const char *func, int line, char *str, extern unsigned Strlen_(const char *, const char *, int) NONNULLPTRS; #endif extern int unicodeval_to_utf8str(int, uint8 *, size_t); - +extern boolean copy_bytes(int, int); +extern const char *datamodel(int); +extern const char *what_datamodel_is_this(int, int, int, int, int, int); #endif /* HACKLIB_H */ diff --git a/include/integer.h b/include/integer.h index 32a3b3266..f12cf21bf 100644 --- a/include/integer.h +++ b/include/integer.h @@ -39,26 +39,29 @@ #define HAS_INTTYPES_H #else /*!__DECC*/ -#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) \ - && !defined(HAS_STDINT_H) +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) +#if !defined(HAS_STDINT_H) /* The compiler claims to conform to C99. Use stdint.h */ #define HAS_STDINT_H -#endif +#endif /* !HAS_STDINT_H */ +#if !defined(HAS_INTTYPES_H) +/* The compiler claims to conform to C99. Use inttypes.h */ +#define HAS_INTTYPES_H +#endif /* !HAS_INTTYPES_H */ #if defined(__GNUC__) && defined(__INT64_MAX__) && !defined(HAS_STDINT_H) #define HAS_STDINT_H -#endif +#endif +#endif /* claims to be C99 */ #endif /*?__DECC*/ #ifdef HAS_STDINT_H #include #define SKIP_STDINT_WORKAROUND -#else /*!stdint*/ +#endif #ifdef HAS_INTTYPES_H #include -#define SKIP_STDINT_WORKAROUND -#endif -#endif /*?stdint*/ +#endif /* HAS_INTTYPES_H */ #ifndef SKIP_STDINT_WORKAROUND /* !C99 */ /* @@ -86,8 +89,13 @@ typedef unsigned int uint32_t; 64-bit integers, you should comment out USE_ISAAC64 in config.h so that the previous RNG gets used instead. Then this file will be inhibited and it won't matter what the int64_t and uint64_t lines are. */ + +#if defined(__cplusplus) +#include +#else typedef long long int int64_t; typedef unsigned long long int uint64_t; +#endif #endif /* !C99 */ @@ -101,6 +109,11 @@ typedef uint32_t uint32; typedef int64_t int64; typedef uint64_t uint64; +/* Also provide ushort, uint, ulong */ +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + /* for non-negative L, calculate L * 10 + D, avoiding signed overflow; yields -1 if overflow would have happened; assumes compiler will optimize the constants */ diff --git a/include/mextra.h b/include/mextra.h index 1942f9dc7..6cafb4890 100644 --- a/include/mextra.h +++ b/include/mextra.h @@ -50,7 +50,7 @@ * src/makemon.c: if (mmflags & MM_XX) newXX(mtmp); * your new code: mon = makemon(&mons[mnum], x, y, MM_XX); * - * 7. Adjust size_monst() in src/cmd.c appropriately. + * 7. Adjust size_monst() in src/wizcmds.c appropriately. * 8. Adjust dealloc_mextra() in src/mon.c to clean up * properly during monst deallocation. * 9. Adjust copy_mextra() in src/mon.c to make duplicate @@ -75,6 +75,7 @@ struct fakecorridor { }; struct egd { + unsigned parentmid; /* make clobber-detection possible */ int fcbeg, fcend; /* fcend: first unused pos */ int vroom; /* room number of the vault */ coordxy gdx, gdy; /* goal of guard's walk */ @@ -92,6 +93,7 @@ struct egd { ** formerly epri.h -- temple priest extension */ struct epri { + unsigned parentmid; /* make clobber-detection possible */ aligntyp shralign; /* alignment of priest's shrine */ schar shroom; /* index in rooms */ coord shrpos; /* position of shrine */ @@ -118,6 +120,7 @@ struct bill_x { }; struct eshk { + unsigned parentmid; /* make clobber-detection possible */ long robbed; /* amount stolen by most recent customer */ long credit; /* amount credited to customer */ long debit; /* amount of debt for using unpaid items */ @@ -145,6 +148,7 @@ struct eshk { ** formerly emin.h -- minion extension */ struct emin { + unsigned parentmid; /* make clobber-detection possible */ aligntyp min_align; /* alignment of minion */ boolean renegade; /* hostile co-aligned priest or Angel */ }; @@ -165,6 +169,7 @@ enum dogfood_types { }; struct edog { + unsigned parentmid; /* make clobber-detection possible */ long droptime; /* moment dog dropped object */ unsigned dropdist; /* dist of dropped obj from @ */ int apport; /* amount of training */ @@ -177,6 +182,22 @@ struct edog { Bitfield(killed_by_u, 1); /* you attempted to kill him */ }; +/*** + ** extension tracking a player's remnant monster (ghost, mummy etc.) + */ +struct ebones { + unsigned parentmid; /* make clobber-detection possible */ + uchar role; /* index into roles[] */ + uchar race; /* index into races[] */ + align oldalign; /* character alignment */ + uchar deathlevel; /* level when dying (m_lev may differ) */ + schar luck; /* luck when dying */ + short mnum; /* monster type */ + Bitfield(female, 1); /* was female */ + Bitfield(demigod, 1); /* had killed wiz or invoked */ + Bitfield(crowned, 1); /* had been crowned */ +}; + /*** ** mextra.h -- collection of all monster extensions */ @@ -187,6 +208,7 @@ struct mextra { struct eshk *eshk; struct emin *emin; struct edog *edog; + struct ebones *ebones; int mcorpsenm; /* obj->corpsenm for mimic posing as statue or corpse, * obj->spe (fruit index) for one posing as a slime mold, * or an alignment mask for one posing as an altar */ @@ -198,6 +220,7 @@ struct mextra { #define ESHK(mon) ((mon)->mextra->eshk) #define EMIN(mon) ((mon)->mextra->emin) #define EDOG(mon) ((mon)->mextra->edog) +#define EBONES(mon) ((mon)->mextra->ebones) #define MCORPSENM(mon) ((mon)->mextra->mcorpsenm) #define has_mgivenname(mon) ((mon)->mextra && MGIVENNAME(mon)) @@ -206,6 +229,7 @@ struct mextra { #define has_eshk(mon) ((mon)->mextra && ESHK(mon)) #define has_emin(mon) ((mon)->mextra && EMIN(mon)) #define has_edog(mon) ((mon)->mextra && EDOG(mon)) +#define has_ebones(mon) ((mon)->mextra && EBONES(mon)) #define has_mcorpsenm(mon) ((mon)->mextra && MCORPSENM(mon) != NON_PM) #endif /* MEXTRA_H */ diff --git a/include/mfndpos.h b/include/mfndpos.h index a49f13262..762f5cdb9 100644 --- a/include/mfndpos.h +++ b/include/mfndpos.h @@ -6,26 +6,28 @@ #ifndef MFNDPOS_H #define MFNDPOS_H -#define ALLOW_MDISP 0x00001000L /* can displace a monster out of its way */ -#define ALLOW_TRAPS 0x00020000L /* can enter traps */ -#define ALLOW_U 0x00040000L /* can attack you */ -#define ALLOW_M 0x00080000L /* can attack other monsters */ -#define ALLOW_TM 0x00100000L /* can attack tame monsters */ +/* clang-format off */ +#define ALLOW_MDISP 0x00001000L /* can displace a monster out of its way */ +#define ALLOW_TRAPS 0x00020000L /* can enter traps */ +#define ALLOW_U 0x00040000L /* can attack you */ +#define ALLOW_M 0x00080000L /* can attack other monsters */ +#define ALLOW_TM 0x00100000L /* can attack tame monsters */ #define ALLOW_ALL (ALLOW_U | ALLOW_M | ALLOW_TM | ALLOW_TRAPS) -#define NOTONL 0x00200000L /* avoids direct line to player */ -#define OPENDOOR 0x00400000L /* opens closed doors */ -#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_DIG 0x08000000L /* digs */ -#define ALLOW_BARS 0x10000000L /* may pass thru iron bars */ -#define ALLOW_SANCT 0x20000000L /* enters temples */ -#define ALLOW_SSM 0x40000000L /* ignores scare monster */ +#define NOTONL 0x00200000L /* avoids direct line to player */ +#define OPENDOOR 0x00400000L /* opens closed doors */ +#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_DIG 0x08000000L /* digs */ +#define ALLOW_BARS 0x10000000L /* may pass thru iron bars */ +#define ALLOW_SANCT 0x20000000L /* enters temples */ +#define ALLOW_SSM 0x40000000L /* ignores scare monster */ #ifdef NHSTDC -#define NOGARLIC 0x80000000UL /* hates garlic */ +#define NOGARLIC 0x80000000UL /* hates garlic */ #else -#define NOGARLIC 0x80000000L /* hates garlic */ +#define NOGARLIC 0x80000000L /* hates garlic */ #endif +/* clang-format on */ #endif /* MFNDPOS_H */ diff --git a/include/monst.h b/include/monst.h index a7f39c18e..15114c600 100644 --- a/include/monst.h +++ b/include/monst.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 monst.h $NHDT-Date: 1678560511 2023/03/11 18:48:31 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.54 $ */ +/* NetHack 3.7 monst.h $NHDT-Date: 1738640524 2025/02/03 19:42:04 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.67 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2016. */ /* NetHack may be freely redistributed. See license for details. */ @@ -99,12 +99,12 @@ struct monst { short mnum; /* permanent monster index number */ short cham; /* if shapeshifter, orig mons[] idx goes here */ short movement; /* movement points (derived from permonst definition - and added effects */ + * and added effects */ uchar m_lev; /* adjusted difficulty level of monster */ aligntyp malign; /* alignment of this monster, relative to the - player (positive = good to kill) */ + * player (positive = good to kill) */ coordxy mx, my; - coordxy mux, muy; /* where the monster thinks you are */ + coordxy mux, muy; /* where the monster thinks you are */ #define MTSZ 4 /* mtrack[0..2] is used to keep extra data when migrating the monster */ coord mtrack[MTSZ]; /* monster track */ @@ -112,11 +112,11 @@ struct monst { unsigned mappearance; /* for undetected mimics and the wiz */ uchar m_ap_type; /* what mappearance is describing, m_ap_types */ - schar mtame; /* level of tameness, implies peaceful */ + schar mtame; /* level of tameness, implies peaceful */ unsigned short mintrinsics; /* low 8 correspond to mresists */ unsigned short mextrinsics; /* low 8 correspond to mresists */ unsigned long seen_resistance; /* M_SEEN_x; saw you resist an effect */ - int mspec_used; /* monster's special ability attack timeout */ + int mspec_used; /* monster's special ability attack timeout */ Bitfield(female, 1); /* is female */ Bitfield(minvis, 1); /* currently invisible */ @@ -156,14 +156,13 @@ struct monst { Bitfield(ispriest, 1); /* is an aligned priest or high priest */ Bitfield(iswiz, 1); /* is the Wizard of Yendor */ +#define MAX_NUM_WORMS 32 /* should be 2^(wormno bitfield size) */ Bitfield(wormno, 5); /* at most 31 worms on any level */ Bitfield(mtemplit, 1); /* temporarily seen; only valid during bhit() */ Bitfield(meverseen, 1); /* mon has been seen at some point */ Bitfield(mspotted, 1); /* mon is currently seen by hero */ -#define MAX_NUM_WORMS 32 /* should be 2^(wormno bitfield size) */ - unsigned long mstrategy; /* for monsters with mflag3: current strategy */ #ifdef NHSTDC #define STRAT_APPEARMSG 0x80000000UL @@ -180,12 +179,10 @@ struct monst { #define STRAT_PLAYER 0x01000000L #define STRAT_NONE 0x00000000L #define STRAT_STRATMASK 0x0f000000L -#define STRAT_XMASK 0x00ff0000L -#define STRAT_YMASK 0x0000ff00L + /* mstrategy unused 0x00ffff00L */ #define STRAT_GOAL 0x000000ffL -#define STRAT_GOALX(s) ((coordxy) ((s & STRAT_XMASK) >> 16)) -#define STRAT_GOALY(s) ((coordxy) ((s & STRAT_YMASK) >> 8)) + coord mgoal; /* monster strategy, target location */ long mtrapseen; /* bitmap of traps we've been trapped in */ long mlstmv; /* for catching up with lost time */ long mstate; /* debugging info on monsters stored here */ @@ -268,22 +265,15 @@ struct monst { #endif #define mon_resistancebits(mon) \ ((mon)->data->mresists | (mon)->mextrinsics | (mon)->mintrinsics) -#define resists_fire(mon) \ - ((mon_resistancebits(mon) & MR_FIRE) != 0) -#define resists_cold(mon) \ - ((mon_resistancebits(mon) & MR_COLD) != 0) -#define resists_sleep(mon) \ - ((mon_resistancebits(mon) & MR_SLEEP) != 0) -#define resists_disint(mon) \ - ((mon_resistancebits(mon) & MR_DISINT) != 0) -#define resists_elec(mon) \ - ((mon_resistancebits(mon) & MR_ELEC) != 0) -#define resists_poison(mon) \ - ((mon_resistancebits(mon) & MR_POISON) != 0) -#define resists_acid(mon) \ - ((mon_resistancebits(mon) & MR_ACID) != 0) -#define resists_ston(mon) \ - ((mon_resistancebits(mon) & MR_STONE) != 0) +#define resists_fire(mon) Resists_Elem(mon, FIRE_RES) +#define resists_cold(mon) Resists_Elem(mon, COLD_RES) +#define resists_sleep(mon) Resists_Elem(mon, SLEEP_RES) +#define resists_disint(mon) Resists_Elem(mon, DISINT_RES) +#define resists_elec(mon) Resists_Elem(mon, SHOCK_RES) +#define resists_poison(mon) Resists_Elem(mon, POISON_RES) +#define resists_acid(mon) Resists_Elem(mon, ACID_RES) +#define resists_ston(mon) Resists_Elem(mon, STONE_RES) + #define is_lminion(mon) \ (is_minion((mon)->data) && mon_aligntyp(mon) == A_LAWFUL) diff --git a/include/monsters.h b/include/monsters.h index e1987f8db..d13cc9178 100644 --- a/include/monsters.h +++ b/include/monsters.h @@ -47,8 +47,10 @@ * Rule #1: monsters of a given class are contiguous in the * mons[] array. * - * Rule #2: monsters of a given class are presented in ascending - * order of strength. + * Rule #2: monsters of a given class are generally kept in + * the same order as in previous versions of NetHack + * (they used to be presented in ascending order of + * strength, but this rule no longer applies). * * Rule #3: monster frequency is included in the geno mask; * the frequency can be from 0 to 7. 0's will also @@ -97,7 +99,7 @@ NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1, 5, MS_BUZZ, MZ_TINY), MR_POISON, MR_POISON, M1_ANIMAL | M1_FLY | M1_NOHANDS | M1_POIS, M2_HOSTILE | M2_FEMALE, 0, - 5, CLR_YELLOW, KILLER_BEE), + 6, CLR_YELLOW, KILLER_BEE), MON(NAM("soldier ant"), S_ANT, LVL(3, 18, 3, 0, 0), (G_GENO | G_SGROUP | 2), A(ATTK(AT_BITE, AD_PHYS, 2, 4), ATTK(AT_STNG, AD_DRST, 3, 4), @@ -105,7 +107,7 @@ SIZ(20, 5, MS_SILENT, MZ_TINY), MR_POISON, MR_POISON, M1_ANIMAL | M1_NOHANDS | M1_OVIPAROUS | M1_POIS | M1_CARNIVORE, M2_HOSTILE, 0, - 6, CLR_BLUE, SOLDIER_ANT), + 7, CLR_BLUE, SOLDIER_ANT), MON(NAM("fire ant"), S_ANT, LVL(3, 18, 3, 10, 0), (G_GENO | G_SGROUP | 1), A(ATTK(AT_BITE, AD_PHYS, 2, 4), ATTK(AT_BITE, AD_FIRE, 2, 4), @@ -215,8 +217,8 @@ SIZ(300, 250, MS_BARK, MZ_SMALL), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, 2, CLR_BROWN, COYOTE), - MON(NAM("werejackal"), - S_DOG, LVL(2, 12, 7, 10, -7), (G_NOGEN | G_NOCORPSE), + MON(NAM("werejackal"), S_DOG, + LVL(2, 12, 7, 10, -7), (G_NOGEN | G_NOCORPSE), A(ATTK(AT_BITE, AD_WERE, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(300, 250, MS_BARK, MZ_SMALL), MR_POISON, 0, @@ -271,7 +273,7 @@ M2_NOPOLY | M2_WERE | M2_HOSTILE, M3_INFRAVISIBLE, 7, CLR_GRAY, WEREWOLF), MON(NAM("winter wolf cub"), S_DOG, - LVL(5, 12, 4, 0, -5), + LVL(5, 12, 4, 0, 0), (G_NOHELL | G_GENO | G_SGROUP | 2), A(ATTK(AT_BITE, AD_PHYS, 1, 8), ATTK(AT_BREA, AD_COLD, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), @@ -286,21 +288,21 @@ M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, 8, CLR_BLACK, WARG), MON(NAM("winter wolf"), S_DOG, - LVL(7, 12, 4, 20, 0), (G_NOHELL | G_GENO | 1), + LVL(7, 12, 4, 20, -5), (G_NOHELL | G_GENO | 1), A(ATTK(AT_BITE, AD_PHYS, 2, 6), ATTK(AT_BREA, AD_COLD, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(700, 300, MS_BARK, MZ_LARGE), MR_COLD, MR_COLD, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE | M2_STRONG, 0, 9, CLR_CYAN, WINTER_WOLF), MON(NAM("hell hound pup"), S_DOG, - LVL(7, 12, 4, 20, -5), (G_HELL | G_GENO | G_SGROUP | 1), + LVL(7, 12, 4, 20, 0), (G_HELL | G_GENO | G_SGROUP | 1), A(ATTK(AT_BITE, AD_PHYS, 2, 6), ATTK(AT_BREA, AD_FIRE, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(200, 200, MS_BARK, MZ_SMALL), MR_FIRE, MR_FIRE, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, 9, CLR_RED, HELL_HOUND_PUP), MON(NAM("hell hound"), S_DOG, - LVL(12, 14, 2, 20, 0), (G_HELL | G_GENO | 1), + LVL(12, 14, 2, 20, -5), (G_HELL | G_GENO | 1), A(ATTK(AT_BITE, AD_PHYS, 3, 6), ATTK(AT_BREA, AD_FIRE, 3, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(600, 300, MS_BARK, MZ_MEDIUM), MR_FIRE, MR_FIRE, @@ -590,7 +592,7 @@ LVL(4, 0, 8, 10, 0), (G_GENO | 2), A(ATTK(AT_NONE, AD_COLD, 0, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(50, 20, MS_SILENT, MZ_MEDIUM), MR_COLD | MR_POISON, + SIZ(WT_JELLY, 20, MS_SILENT, MZ_MEDIUM), MR_COLD | MR_POISON, MR_COLD | MR_POISON, M1_BREATHLESS | M1_AMORPHOUS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_NOTAKE, @@ -600,7 +602,7 @@ LVL(5, 0, 8, 10, 0), (G_GENO | 1), A(ATTK(AT_NONE, AD_ACID, 0, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(50, 20, MS_SILENT, MZ_MEDIUM), + SIZ(WT_JELLY, 20, MS_SILENT, MZ_MEDIUM), MR_ACID | MR_STONE, MR_ACID | MR_STONE, M1_BREATHLESS | M1_AMORPHOUS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_ACID | M1_NOTAKE, @@ -610,7 +612,7 @@ LVL(6, 3, 8, 20, 0), (G_GENO | 2), A(ATTK(AT_ENGL, AD_ACID, 3, 6), ATTK(AT_NONE, AD_ACID, 3, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(50, 20, MS_SILENT, MZ_MEDIUM), + SIZ(WT_JELLY, 20, MS_SILENT, MZ_MEDIUM), MR_ACID | MR_STONE, MR_ACID | MR_STONE, M1_BREATHLESS | M1_AMORPHOUS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_ACID | M1_NOTAKE, @@ -701,14 +703,14 @@ LVL(3, 12, 9, 20, 0), (G_GENO | 2), A(ATTK(AT_CLAW, AD_SITM, 0, 0), ATTK(AT_CLAW, AD_SEDU, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(600, 300, MS_SEDUCE, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_TPORT, + SIZ(WT_NYMPH, 300, MS_SEDUCE, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_TPORT, M2_HOSTILE | M2_FEMALE | M2_COLLECT, M3_INFRAVISIBLE, 5, CLR_GREEN, WOOD_NYMPH), MON(NAM("water nymph"), S_NYMPH, LVL(3, 12, 9, 20, 0), (G_GENO | 2), A(ATTK(AT_CLAW, AD_SITM, 0, 0), ATTK(AT_CLAW, AD_SEDU, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(600, 300, MS_SEDUCE, MZ_HUMAN), 0, 0, + SIZ(WT_NYMPH, 300, MS_SEDUCE, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_TPORT | M1_SWIM, M2_HOSTILE | M2_FEMALE | M2_COLLECT, M3_INFRAVISIBLE, 5, CLR_BLUE, WATER_NYMPH), @@ -716,7 +718,7 @@ LVL(3, 12, 9, 20, 0), (G_GENO | 2), A(ATTK(AT_CLAW, AD_SITM, 0, 0), ATTK(AT_CLAW, AD_SEDU, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(600, 300, MS_SEDUCE, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_TPORT, + SIZ(WT_NYMPH, 300, MS_SEDUCE, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_TPORT, M2_HOSTILE | M2_FEMALE | M2_COLLECT, M3_INFRAVISIBLE, 5, CLR_BROWN, MOUNTAIN_NYMPH), /* @@ -1052,7 +1054,7 @@ LVL(3, 1, 0, 0, 0), (G_GENO | G_NOCORPSE | 2), A(ATTK(AT_ENGL, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(0, 0, MS_SILENT, MZ_HUGE), MR_SLEEP | MR_POISON | MR_STONE, 0, + SIZ(WT_ETHEREAL, 0, MS_SILENT, MZ_HUGE), MR_SLEEP | MR_POISON | MR_STONE, 0, M1_FLY | M1_BREATHLESS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_AMORPHOUS | M1_UNSOLID, M2_HOSTILE | M2_NEUTER, 0, @@ -1061,7 +1063,7 @@ LVL(4, 20, 2, 30, 0), (G_GENO | G_NOCORPSE | 2), A(ATTK(AT_ENGL, AD_BLND, 2, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(0, 0, MS_SILENT, MZ_HUGE), MR_SLEEP | MR_POISON | MR_STONE, 0, + SIZ(WT_ETHEREAL, 0, MS_SILENT, MZ_HUGE), MR_SLEEP | MR_POISON | MR_STONE, 0, M1_FLY | M1_BREATHLESS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS, M2_HOSTILE | M2_NEUTER, 0, @@ -1070,7 +1072,7 @@ LVL(5, 20, 2, 30, 0), (G_NOHELL | G_GENO | G_NOCORPSE | 1), A(ATTK(AT_ENGL, AD_COLD, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(0, 0, MS_SILENT, MZ_HUGE), + SIZ(WT_ETHEREAL, 0, MS_SILENT, MZ_HUGE), MR_COLD | MR_SLEEP | MR_POISON | MR_STONE, 0, M1_FLY | M1_BREATHLESS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS, @@ -1080,7 +1082,7 @@ LVL(6, 20, 2, 30, 0), (G_GENO | G_NOCORPSE | 1), A(ATTK(AT_ENGL, AD_ELEC, 1, 6), ATTK(AT_ENGL, AD_DREN, 2, 6), ATTK(AT_NONE, AD_ELEC, 0, 4), NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(0, 0, MS_SILENT, MZ_HUGE), + SIZ(WT_ETHEREAL, 0, MS_SILENT, MZ_HUGE), MR_ELEC | MR_SLEEP | MR_DISINT | MR_POISON | MR_STONE, 0, M1_FLY | M1_BREATHLESS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_UNSOLID, @@ -1090,7 +1092,7 @@ LVL(7, 22, 2, 30, 0), (G_HELL | G_GENO | G_NOCORPSE | 2), A(ATTK(AT_ENGL, AD_FIRE, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(0, 0, MS_SILENT, MZ_HUGE), + SIZ(WT_ETHEREAL, 0, MS_SILENT, MZ_HUGE), MR_FIRE | MR_SLEEP | MR_POISON | MR_STONE, 0, M1_FLY | M1_BREATHLESS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_UNSOLID, @@ -1100,7 +1102,7 @@ LVL(8, 22, 2, 30, 0), (G_HELL | G_GENO | G_NOCORPSE | 1), A(ATTK(AT_ENGL, AD_FIRE, 1, 10), ATTK(AT_NONE, AD_FIRE, 0, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(0, 0, MS_SILENT, MZ_HUGE), + SIZ(WT_ETHEREAL, 0, MS_SILENT, MZ_HUGE), MR_FIRE | MR_SLEEP | MR_POISON | MR_STONE, 0, M1_FLY | M1_BREATHLESS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_UNSOLID, @@ -1168,7 +1170,7 @@ LVL(3, 15, 0, 0, 0), (G_NOCORPSE | G_GENO | 4), A(ATTK(AT_EXPL, AD_BLND, 10, 20), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(0, 0, MS_SILENT, MZ_SMALL), + SIZ(WT_ETHEREAL, 0, MS_SILENT, MZ_SMALL), MR_FIRE | MR_COLD | MR_ELEC | MR_DISINT | MR_SLEEP | MR_POISON | MR_ACID | MR_STONE, 0, M1_FLY | M1_BREATHLESS | M1_AMORPHOUS | M1_NOEYES | M1_NOLIMBS @@ -1180,7 +1182,7 @@ LVL(5, 15, 0, 0, 0), (G_NOCORPSE | G_GENO | 2), A(ATTK(AT_EXPL, AD_HALU, 10, 12), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(0, 0, MS_SILENT, MZ_SMALL), + SIZ(WT_ETHEREAL, 0, MS_SILENT, MZ_SMALL), MR_FIRE | MR_COLD | MR_ELEC | MR_DISINT | MR_SLEEP | MR_POISON | MR_ACID | MR_STONE, 0, M1_FLY | M1_BREATHLESS | M1_AMORPHOUS | M1_NOEYES | M1_NOLIMBS @@ -1340,7 +1342,7 @@ LVL(12, 9, 2, 10, 0), G_GENO, A(ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(1500, 500, MS_ROAR, MZ_HUGE), 0, 0, + SIZ(WT_BABY_DRAGON, 500, MS_ROAR, MZ_HUGE), 0, 0, M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE | M2_STRONG | M2_GREEDY | M2_JEWELS, 0, 13, CLR_GRAY, BABY_GRAY_DRAGON), @@ -1348,7 +1350,7 @@ LVL(12, 9, 2, 10, 0), G_GENO, A(ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(1500, 500, MS_ROAR, MZ_HUGE), 0, 0, + SIZ(WT_BABY_DRAGON, 500, MS_ROAR, MZ_HUGE), 0, 0, M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE | M2_STRONG | M2_GREEDY | M2_JEWELS, M3_INFRAVISIBLE, 13, HI_GOLD, BABY_GOLD_DRAGON), @@ -1356,7 +1358,7 @@ LVL(12, 9, 2, 10, 0), G_GENO, A(ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(1500, 500, MS_ROAR, MZ_HUGE), 0, 0, + SIZ(WT_BABY_DRAGON, 500, MS_ROAR, MZ_HUGE), 0, 0, M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE | M2_STRONG | M2_GREEDY | M2_JEWELS, 0, 13, DRAGON_SILVER, BABY_SILVER_DRAGON), @@ -1366,7 +1368,7 @@ LVL(12, 9, 2, 10, 0), G_GENO, A(ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(1500, 500, MS_ROAR, MZ_HUGE), 0, 0, + SIZ(WT_BABY_DRAGON, 500, MS_ROAR, MZ_HUGE), 0, 0, M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE | M2_STRONG | M2_GREEDY | M2_JEWELS, 0, 13, CLR_CYAN, BABY_SHIMMERING_DRAGON), @@ -1375,7 +1377,7 @@ LVL(12, 9, 2, 10, 0), G_GENO, A(ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(1500, 500, MS_ROAR, MZ_HUGE), MR_FIRE, 0, + SIZ(WT_BABY_DRAGON, 500, MS_ROAR, MZ_HUGE), MR_FIRE, 0, M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE | M2_STRONG | M2_GREEDY | M2_JEWELS, M3_INFRAVISIBLE, 13, CLR_RED, BABY_RED_DRAGON), @@ -1383,7 +1385,7 @@ LVL(12, 9, 2, 10, 0), G_GENO, A(ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(1500, 500, MS_ROAR, MZ_HUGE), MR_COLD, 0, + SIZ(WT_BABY_DRAGON, 500, MS_ROAR, MZ_HUGE), MR_COLD, 0, M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE | M2_STRONG | M2_GREEDY | M2_JEWELS, 0, 13, CLR_WHITE, BABY_WHITE_DRAGON), @@ -1391,7 +1393,7 @@ LVL(12, 9, 2, 10, 0), G_GENO, A(ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(1500, 500, MS_ROAR, MZ_HUGE), MR_SLEEP, 0, + SIZ(WT_BABY_DRAGON, 500, MS_ROAR, MZ_HUGE), MR_SLEEP, 0, M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE | M2_STRONG | M2_GREEDY | M2_JEWELS, 0, 13, CLR_ORANGE, BABY_ORANGE_DRAGON), @@ -1399,7 +1401,7 @@ LVL(12, 9, 2, 10, 0), G_GENO, A(ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(1500, 500, MS_ROAR, MZ_HUGE), MR_DISINT, 0, + SIZ(WT_BABY_DRAGON, 500, MS_ROAR, MZ_HUGE), MR_DISINT, 0, M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE | M2_STRONG | M2_GREEDY | M2_JEWELS, 0, 13, CLR_BLACK, BABY_BLACK_DRAGON), @@ -1407,7 +1409,7 @@ LVL(12, 9, 2, 10, 0), G_GENO, A(ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(1500, 500, MS_ROAR, MZ_HUGE), MR_ELEC, 0, + SIZ(WT_BABY_DRAGON, 500, MS_ROAR, MZ_HUGE), MR_ELEC, 0, M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE | M2_STRONG | M2_GREEDY | M2_JEWELS, 0, 13, CLR_BLUE, BABY_BLUE_DRAGON), @@ -1415,7 +1417,7 @@ LVL(12, 9, 2, 10, 0), G_GENO, A(ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(1500, 500, MS_ROAR, MZ_HUGE), MR_POISON, 0, + SIZ(WT_BABY_DRAGON, 500, MS_ROAR, MZ_HUGE), MR_POISON, 0, M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_CARNIVORE | M1_POIS, M2_HOSTILE | M2_STRONG | M2_GREEDY | M2_JEWELS, 0, 13, CLR_GREEN, BABY_GREEN_DRAGON), @@ -1423,7 +1425,7 @@ LVL(12, 9, 2, 10, 0), G_GENO, A(ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(1500, 500, MS_ROAR, MZ_HUGE), MR_ACID | MR_STONE, 0, + SIZ(WT_BABY_DRAGON, 500, MS_ROAR, MZ_HUGE), MR_ACID | MR_STONE, 0, M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_CARNIVORE | M1_ACID, M2_HOSTILE | M2_STRONG | M2_GREEDY | M2_JEWELS, 0, 13, CLR_YELLOW, BABY_YELLOW_DRAGON), @@ -1573,7 +1575,7 @@ LVL(8, 36, 2, 30, 0), (G_NOCORPSE | 1), A(ATTK(AT_ENGL, AD_PHYS, 1, 10), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(0, 0, MS_SILENT, MZ_HUGE), MR_POISON | MR_STONE, 0, + SIZ(WT_ETHEREAL, 0, MS_SILENT, MZ_HUGE), MR_POISON | MR_STONE, 0, M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_BREATHLESS | M1_UNSOLID | M1_FLY, M2_STRONG | M2_NEUTER, 0, @@ -1582,7 +1584,7 @@ LVL(8, 12, 2, 30, 0), (G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_FIRE, 3, 6), ATTK(AT_NONE, AD_FIRE, 0, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(0, 0, MS_SILENT, MZ_HUGE), MR_FIRE | MR_POISON | MR_STONE, 0, + SIZ(WT_ETHEREAL, 0, MS_SILENT, MZ_HUGE), MR_FIRE | MR_POISON | MR_STONE, 0, M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_BREATHLESS | M1_UNSOLID | M1_FLY | M1_NOTAKE, M2_STRONG | M2_NEUTER, M3_INFRAVISIBLE, @@ -2335,7 +2337,7 @@ LVL(6, 12, 4, 15, -6), (G_GENO | 2), A(ATTK(AT_TUCH, AD_DRLI, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(0, 0, MS_SILENT, MZ_HUMAN), + SIZ(WT_ETHEREAL, 0, MS_SILENT, MZ_HUMAN), MR_COLD | MR_SLEEP | MR_POISON | MR_STONE, 0, M1_BREATHLESS | M1_FLY | M1_HUMANOID | M1_UNSOLID, M2_UNDEAD | M2_STALK | M2_HOSTILE, 0, diff --git a/include/nhregex.h b/include/nhregex.h new file mode 100644 index 000000000..6dc989213 --- /dev/null +++ b/include/nhregex.h @@ -0,0 +1,17 @@ +/* NetHack 3.7 nhregex.h $NHDT-Date: $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: $ */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef NHREGEX_H +#define NHREGEX_H + +/* ### {cpp,pmatch,posix}regex.c ### */ + +extern struct nhregex *regex_init(void); +extern boolean regex_compile(const char *, struct nhregex *) NONNULLARG1; +extern char *regex_error_desc(struct nhregex *, char *) NONNULLARG2; +extern boolean regex_match(const char *, struct nhregex *) NO_NNARGS; +extern void regex_free(struct nhregex *) NONNULLARG1; + +#endif /* NHREGEX_H */ + +/*extern.h*/ diff --git a/include/obj.h b/include/obj.h index abdd3c735..e23c5f457 100644 --- a/include/obj.h +++ b/include/obj.h @@ -87,6 +87,7 @@ struct obj { #define NOBJ_STATES 10 xint16 timed; /* # of fuses (timers) attached to this obj */ + /* Bitfields currently require 5 bytes minimum */ Bitfield(cursed, 1); /* uncursed when neither cursed nor blessed */ Bitfield(blessed, 1); Bitfield(unpaid, 1); /* owned by shop; valid for objects in hero's @@ -98,6 +99,10 @@ struct obj { * items on shop floor or in containers there; * implicit for items at any other location * unless carried and explicitly flagged unpaid */ + Bitfield(recharged, 3); /* number of times it's been recharged */ +#define on_ice recharged /* corpse on ice */ + Bitfield(lamplit, 1); /* a light-source -- can be lit */ + Bitfield(known, 1); /* exact nature known (for instance, charge count * or enchantment); many items have this preset if * they lack anything interesting to discover */ @@ -105,6 +110,14 @@ struct obj { * some types of items always have dknown set */ Bitfield(bknown, 1); /* BUC (blessed/uncursed/cursed) known */ Bitfield(rknown, 1); /* rustproofing status known */ + Bitfield(cknown, 1); /* for containers (including statues): the contents + * are known; also applicable to tins; also applies + * to horn of plenty but only for empty/non-empty */ + Bitfield(lknown, 1); /* locked/unlocked status is known; assigned for bags + * and for horn of plenty (when tipping) even though + * they have no locks */ + Bitfield(tknown, 1); /* trap status known for chests */ + Bitfield(nomerge, 1); /* set temporarily to prevent merging */ Bitfield(oeroded, 2); /* rusted/burnt weapon/armor */ Bitfield(oeroded2, 2); /* corroded/rotted weapon/armor */ @@ -123,33 +136,23 @@ struct obj { /* or accidental tripped rolling boulder trap */ #define opoisoned otrapped /* object (weapon) is coated with poison */ - Bitfield(recharged, 3); /* number of times it's been recharged */ -#define on_ice recharged /* corpse on ice */ - Bitfield(lamplit, 1); /* a light-source -- can be lit */ Bitfield(globby, 1); /* combines with like types on adjacent squares */ Bitfield(greased, 1); /* covered with grease */ - Bitfield(nomerge, 1); /* set temporarily to prevent merging */ - Bitfield(how_lost, 2); /* stolen by mon or thrown, dropped by hero */ - Bitfield(in_use, 1); /* for magic items before useup items */ Bitfield(bypass, 1); /* mark this as an object to be skipped by bhito() */ - Bitfield(cknown, 1); /* for containers (including statues): the contents - * are known; also applicable to tins; also applies - * to horn of plenty but only for empty/non-empty */ - Bitfield(lknown, 1); /* locked/unlocked status is known; assigned for bags - * and for horn of plenty (when tipping) even though - * they have no locks */ Bitfield(pickup_prev, 1); /* was picked up previously */ Bitfield(ghostly, 1); /* it just got placed into a bones file */ + Bitfield(how_lost, 3); /* stolen by mon or thrown, dropped by hero, etc */ + + Bitfield(named_how, 1); /* source of name per TODO in resetobjs() */ #if 0 /* not implemented */ - Bitfield(tknown, 1); /* trap status known for chests */ Bitfield(eknown, 1); /* effect known for wands zapped or rings worn when * not seen yet after being picked up while blind * [maybe for remaining stack of used potion too] */ - /* 7 free bits */ + /* 5 free bits */ #else - /* 1 free bit */ + /* 6 free bits */ #endif int corpsenm; /* type of corpse is mons[corpsenm] */ @@ -162,6 +165,7 @@ struct obj { * 1 for others (format as "next boulder") */ int usecount; /* overloaded for various things that tally */ #define spestudied usecount /* # of times a spellbook has been studied */ +#define wishedfor usecount /* flag for hold_another_object() if from wish */ unsigned oeaten; /* nutrition left in food, if partly eaten */ long age; /* creation date */ long owornmask; /* bit mask indicating which equipment slot(s) an @@ -218,10 +222,12 @@ struct obj { (otmp->oclass == WEAPON_CLASS \ && objects[otmp->otyp].oc_skill >= P_SHORT_SWORD \ && objects[otmp->otyp].oc_skill <= P_SABER) +/* Snickersnee is not a polearm, but can hit from distance */ #define is_pole(otmp) \ ((otmp->oclass == WEAPON_CLASS || otmp->oclass == TOOL_CLASS) \ && (objects[otmp->otyp].oc_skill == P_POLEARMS \ - || objects[otmp->otyp].oc_skill == P_LANCE)) + || objects[otmp->otyp].oc_skill == P_LANCE \ + || is_art(otmp, ART_SNICKERSNEE))) #define is_spear(otmp) \ (otmp->oclass == WEAPON_CLASS && objects[otmp->otyp].oc_skill == P_SPEAR) #define is_launcher(otmp) \ @@ -242,6 +248,9 @@ struct obj { ((o)->oclass == TOOL_CLASS && objects[(o)->otyp].oc_skill != P_NONE) /* towel is not a weptool: spe isn't an enchantment, cursed towel doesn't weld to hand, and twoweapon won't work with one */ +#define is_blunt_weapon(o) \ + (((o)->oclass == WEAPON_CLASS || is_weptool(o)) \ + && ((objects[(o)->otyp].oc_dir & WHACK) != 0)) #define is_wet_towel(o) ((o)->otyp == TOWEL && (o)->spe > 0) #define bimanual(otmp) \ ((otmp->oclass == WEAPON_CLASS || otmp->oclass == TOOL_CLASS) \ @@ -250,10 +259,11 @@ struct obj { (otmp->oclass == WEAPON_CLASS \ && objects[otmp->otyp].oc_skill >= -P_SHURIKEN \ && objects[otmp->otyp].oc_skill <= -P_BOW) -#define is_poisonable(otmp) \ - (otmp->oclass == WEAPON_CLASS \ - && objects[otmp->otyp].oc_skill >= -P_SHURIKEN \ - && objects[otmp->otyp].oc_skill <= -P_BOW) +#define is_poisonable(otmp) \ + ((otmp->oclass == WEAPON_CLASS \ + && objects[otmp->otyp].oc_skill >= -P_SHURIKEN \ + && objects[otmp->otyp].oc_skill <= -P_BOW) \ + || permapoisoned(otmp)) #define uslinging() (uwep && objects[uwep->otyp].oc_skill == P_SLING) /* 'is_quest_artifact()' only applies to the current role's artifact */ #define any_quest_artifact(o) ((o)->oartifact >= ART_ORB_OF_DETECTION) @@ -464,11 +474,17 @@ struct obj { #define POTHIT_MONST_THROW 2 /* thrown by a monster */ #define POTHIT_OTHER_THROW 3 /* propelled by some other means [scatter()] */ -/* tracking how an item left your inventory */ -#define LOST_NONE 0 /* still in inventory, or method not covered below */ -#define LOST_THROWN 1 /* thrown or fired by the hero */ -#define LOST_DROPPED 2 /* dropped or tipped out of a container by the hero */ -#define LOST_STOLEN 3 /* stolen from hero's inventory by a monster */ +/* tracking how an item left your inventory via how_lost field */ +#define LOST_NONE 0 /* still in inventory, or method not covered below */ +#define LOST_THROWN 1 /* thrown or fired by the hero */ +#define LOST_DROPPED 2 /* dropped or tipped out of a container by the hero */ +#define LOST_STOLEN 3 /* stolen from hero's inventory by a monster */ +#define LOSTOVERRIDEMASK 0x3 +#define LOST_EXPLODING 4 /* the object is exploding (i.e. POT_OIL) */ + +/* tracking how a name got acquired by an object in named_how field */ +#define NAMED_PLAIN 0 /* nothing special, typical naming */ +#define NAMED_KEEP 1 /* historic statue, or stoned/killed monster */ /* * Notes for adding new oextra structures: diff --git a/include/objclass.h b/include/objclass.h index 7e1af7fab..23c3a86f2 100644 --- a/include/objclass.h +++ b/include/objclass.h @@ -10,6 +10,7 @@ (liquid potion inside glass bottle, metal arrowhead on wooden shaft) and object definitions only specify one type on a best-fit basis */ enum obj_material_types { + NO_MATERIAL = 0, LIQUID = 1, /* currently only for venom */ WAX = 2, VEGGY = 3, /* foodstuffs */ diff --git a/include/objects.h b/include/objects.h index 784b66c36..1108f14b0 100644 --- a/include/objects.h +++ b/include/objects.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 objects.h $NHDT-Date: 1725653011 2024/09/06 20:03:31 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.26 $ */ +/* NetHack 3.7 objects.h $NHDT-Date: 1749097644 2025/06/04 20:27:24 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.30 $ */ /* Copyright (c) Mike Threepoint, 1989. */ /* NetHack may be freely redistributed. See license for details. */ @@ -352,6 +352,10 @@ WEAPON("mace", NoDes, 1, 0, 0, 40, 30, 5, 6, 6, 0, B, P_MACE, IRON, HI_METAL, MACE), /* +1 small */ +WEAPON("silver mace", NoDes, + 1, 0, 0, 2, 36, 60, 6, 6, 0, B, P_MACE, SILVER, HI_SILVER, + SILVER_MACE), + /* +1 small */ WEAPON("morning star", NoDes, 1, 0, 0, 12, 120, 10, 4, 6, 0, B, P_MORNING_STAR, IRON, HI_METAL, MORNING_STAR), @@ -931,7 +935,7 @@ EYEWEAR("lenses", NoDes, 1, 0, 5, 3, 80, GLASS, HI_GLASS, LENSES), EYEWEAR("blindfold", NoDes, 1, BLINDED, 50, 2, 20, CLOTH, CLR_BLACK, BLINDFOLD), -EYEWEAR("towel", NoDes, 1, BLINDED, 50, 2, 50, CLOTH, CLR_MAGENTA, +EYEWEAR("towel", NoDes, 1, BLINDED, 50, 5, 50, CLOTH, CLR_MAGENTA, TOWEL), #undef EYEWEAR @@ -1501,7 +1505,7 @@ COIN("gold piece", 1000, GOLD, 1, GOLD_PIECE), OBJECT(OBJ(name, desc), \ BITS(0, 1, 0, 0, 0, 0, 0, 0, 0, \ HARDGEM(mohs), 0, -P_SLING, glass), \ - 0, GEM_CLASS, prob, 0, 1, gval, 3, 3, 0, 0, nutr, color, sn) + 0, GEM_CLASS, prob, 0, wt, gval, 3, 3, 0, 0, nutr, color, sn) #define ROCK(name,desc,kn,prob,wt,gval,sdam,ldam,mgc,nutr,mohs,glass,colr,sn) \ OBJECT(OBJ(name, desc), \ BITS(kn, 1, 0, 0, mgc, 0, 0, 0, 0, \ diff --git a/include/optlist.h b/include/optlist.h index 08288fd08..b0bc715f3 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -167,6 +167,8 @@ static int optfn_##a(int, int, boolean, char *, char *); NHOPTB(ascii_map, Advanced, 0, opt_in, set_in_game, ascii_map_Def, Yes, No, No, NoAlias, &iflags.wc_ascii_map, Term_False, "show map as text") + NHOPTO("autocompletions", Advanced, o_autocomplete, BUFSZ, opt_in, set_in_game, + No, Yes, No, NoAlias, "edit autocompletions") NHOPTB(autodescribe, Advanced, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias, &iflags.autodescribe, Term_False, "describe terrain under cursor") @@ -440,9 +442,9 @@ static int optfn_##a(int, int, boolean, char *, char *); No, Yes, No, No, NoAlias, "jump to the last page in a menu") NHOPTC(menu_next_page, Advanced, 4, opt_in, set_in_config, No, Yes, No, No, NoAlias, "go to the next menu page") - NHOPTB(menu_objsyms, Advanced, 0, opt_in, set_in_game, - Off, Yes, No, No, NoAlias, &iflags.menu_head_objsym, - Term_False, "show object symbols in menus") + NHOPTC(menu_objsyms, Advanced, 12, opt_in, set_in_game, + Yes, Yes, No, Yes, "use_menu_glyphs", + "show object symbols in menus") #ifdef TTY_GRAPHICS NHOPTB(menu_overlay, Advanced, 0, opt_in, set_in_game, On, Yes, No, No, NoAlias, &iflags.menu_overlay, Term_False, diff --git a/include/patchlevel.h b/include/patchlevel.h index fb3d53212..ff02f26bc 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 patchlevel.h $NHDT-Date: 1725653013 2024/09/06 20:03:33 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.264 $ */ +/* NetHack 3.7 patchlevel.h $NHDT-Date: 1753856387 2025/07/29 22:19:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.288 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -17,7 +17,7 @@ * Incrementing EDITLEVEL can be used to force invalidation of old bones * and save files. */ -#define EDITLEVEL 107 +#define EDITLEVEL 130 /* * Development status possibilities. @@ -36,7 +36,7 @@ #define DEBUG #endif -#define COPYRIGHT_BANNER_A "NetHack, Copyright 1985-2024" +#define COPYRIGHT_BANNER_A "NetHack, Copyright 1985-2025" #define COPYRIGHT_BANNER_B \ " By Stichting Mathematisch Centrum and M. Stephenson." /* nomakedefs.copyright_banner_c is generated at runtime */ diff --git a/include/pcconf.h b/include/pcconf.h index 5f8de8fb5..6736d8d9d 100644 --- a/include/pcconf.h +++ b/include/pcconf.h @@ -111,33 +111,21 @@ #if defined(_MSC_VER) && (_MSC_VER >= 7) #include -#include #ifdef strcmpi #undef strcmpi #endif -#include /* Provides prototypes of strncmpi(), etc. */ #include #include #include #define SIG_RET_TYPE void(__cdecl *)(int) -#define M(c) ((char) (0x80 | (c))) #define vprintf printf #define vfprintf fprintf #define vsprintf sprintf #endif -#if defined(__BORLANDC__) && defined(STRNCMPI) -#include /* Provides prototypes of strncmpi(), etc. */ -#endif - -#if defined(__DJGPP__) -#define _NAIVE_DOS_REGS -#include -#include /* Provides prototypes of strncmpi(), etc. */ #ifndef M #define M(c) ((char) (0x80 | (c))) #endif -#endif /* * On the VMS and unix, this option controls whether a delay is done by @@ -226,10 +214,6 @@ #define HLOCK "NHPERM" #endif -#ifndef AMIGA -#include -#endif - /* the high quality random number routines */ #ifndef USE_ISAAC64 # ifdef RANDOM diff --git a/include/permonst.h b/include/permonst.h index 9f11bb1fc..6df66b841 100644 --- a/include/permonst.h +++ b/include/permonst.h @@ -47,12 +47,6 @@ struct attack { #define NATTK 6 -/* Weight of human body, elf, dragon - */ -#define WT_HUMAN 1450U -#define WT_ELF 800U -#define WT_DRAGON 4500U - #ifndef ALIGN_H #include "align.h" #endif @@ -83,13 +77,9 @@ struct permonst { uchar mcolor; /* color to use */ }; +#define NORMAL_SPEED 12 + extern NEARDATA struct permonst mons[NUMMONS + 1]; /* the master list of * monster types */ -#define VERY_SLOW 3 -#define SLOW_SPEED 9 -#define NORMAL_SPEED 12 /* movement rates */ -#define FAST_SPEED 15 -#define VERY_FAST 24 - #endif /* PERMONST_H */ diff --git a/include/prop.h b/include/prop.h index 9824d892b..16b7efd50 100644 --- a/include/prop.h +++ b/include/prop.h @@ -55,41 +55,42 @@ enum prop_types { CLAIRVOYANT = 35, INFRAVISION = 36, DETECT_MONSTERS = 37, + BLND_RES = 38, /* Appearance and behavior */ - ADORNED = 38, - INVIS = 39, - DISPLACED = 40, - STEALTH = 41, - AGGRAVATE_MONSTER = 42, - CONFLICT = 43, + ADORNED = 39, + INVIS = 40, + DISPLACED = 41, + STEALTH = 42, + AGGRAVATE_MONSTER = 43, + CONFLICT = 44, /* Transportation */ - JUMPING = 44, - TELEPORT = 45, - TELEPORT_CONTROL = 46, - LEVITATION = 47, - FLYING = 48, - WWALKING = 49, - SWIMMING = 50, - MAGICAL_BREATHING = 51, - PASSES_WALLS = 52, + JUMPING = 45, + TELEPORT = 46, + TELEPORT_CONTROL = 47, + LEVITATION = 48, + FLYING = 49, + WWALKING = 50, + SWIMMING = 51, + MAGICAL_BREATHING = 52, + PASSES_WALLS = 53, /* Physical attributes */ - SLOW_DIGESTION = 53, - HALF_SPDAM = 54, - HALF_PHDAM = 55, - REGENERATION = 56, - ENERGY_REGENERATION = 57, - PROTECTION = 58, - PROT_FROM_SHAPE_CHANGERS = 59, - POLYMORPH = 60, - POLYMORPH_CONTROL = 61, - UNCHANGING = 62, - FAST = 63, - REFLECTING = 64, - FREE_ACTION = 65, - FIXED_ABIL = 66, - LIFESAVED = 67 + SLOW_DIGESTION = 54, + HALF_SPDAM = 55, + HALF_PHDAM = 56, + REGENERATION = 57, + ENERGY_REGENERATION = 58, + PROTECTION = 59, + PROT_FROM_SHAPE_CHANGERS = 60, + POLYMORPH = 61, + POLYMORPH_CONTROL = 62, + UNCHANGING = 63, + FAST = 64, + REFLECTING = 65, + FREE_ACTION = 66, + FIXED_ABIL = 67, + LIFESAVED = 68, + LAST_PROP = LIFESAVED }; -#define LAST_PROP (LIFESAVED) /*** Where the properties come from ***/ /* Definitions were moved here from obj.h and you.h */ diff --git a/include/rm.h b/include/rm.h index 61e6f013d..e22f48054 100644 --- a/include/rm.h +++ b/include/rm.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 rm.h $NHDT-Date: 1684058570 2023/05/14 10:02:50 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.107 $ */ +/* NetHack 3.7 rm.h $NHDT-Date: 1745114235 2025/04/19 17:57:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.120 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Pasi Kallinen, 2017. */ /* NetHack may be freely redistributed. See license for details. */ @@ -103,9 +103,10 @@ enum levl_typ_types { */ #define IS_WALL(typ) ((typ) && (typ) <= DBWALL) #define IS_STWALL(typ) ((typ) <= DBWALL) /* STONE <= (typ) <= DBWALL */ -#define IS_ROCK(typ) ((typ) < POOL) /* absolutely nonaccessible */ +#define IS_OBSTRUCTED(typ) ((typ) < POOL) /* absolutely nonaccessible */ +#define IS_SDOOR(typ) ((typ) == SDOOR) #define IS_DOOR(typ) ((typ) == DOOR) -#define IS_DOORJOIN(typ) (IS_ROCK(typ) || (typ) == IRONBARS) +#define IS_DOORJOIN(typ) (IS_OBSTRUCTED(typ) || (typ) == IRONBARS) #define IS_TREE(typ) \ ((typ) == TREE || (svl.level.flags.arboreal && (typ) == STONE)) #define ACCESSIBLE(typ) ((typ) >= DOOR) /* good position */ @@ -135,6 +136,54 @@ enum levl_typ_types { : levl[x][y].typ) /* + * The structure describing a coordinate position. + * Before adding fields, remember that this will significantly affect + * the size of temporary files and save files. + * + * Also remember that the run-length encoding for some ports in save.c + * must be updated to consider the field. + */ +struct rm { + int glyph; /* what the hero thinks is there */ + schar typ; /* what is really there [why is this signed?] */ + uchar seenv; /* seen vector */ + Bitfield(flags, 5); /* extra information for typ */ + Bitfield(horizontal, 1); /* wall/door/etc is horiz. (more typ info) */ + Bitfield(lit, 1); /* speed hack for lit rooms */ + Bitfield(waslit, 1); /* remember if a location was lit */ + + Bitfield(roomno, 6); /* room # for special rooms */ + Bitfield(edge, 1); /* marks boundaries for special rooms*/ + Bitfield(candig, 1); /* Exception to Can_dig_down; was a trapdoor */ +}; + +/* + * rm flags field overloads: + * + * +-------------+-------------+------------+------------+------------+ + * | bit5 | bit4 | bit3 | bit2 | bit1 | + * | 0x10 | 0x8 | 0x4 | 0x2 | 0x1 | + * +-------------+-------------+------------+------------+------------+ + * wall |W_NONPASSWALL|W_NONDIGGABLE| W_MASK | W_MASK | W_MASK | + * door |D_TRAPPED | D_LOCKED | D_CLOSED | D_ISOPEN | D_BROKEN | + * |D_WARNED | | | | | + * sdoor |D_TRAPPED | D_LOCKED | W_MASK | W_MASK | W_MASK | + * drawbr. |DB_FLOOR | DB_ICE | DB_LAVA | DB_DIR | DB_DIR | + * sink | | | S_LRING | S_LDWASHER | S_LPUDDING | + * tree | | | | TREE_SWARM | TREE_LOOTED| + * throne | | | | | T_LOOTED | + * fountain| | | | F_WARNED | F_LOOTED | + * ladder | | | | LA_DOWN | LA_UP | + * pool |ICED_MOAT | ICED_POOL | | | | + * grave | | | | | emptygrave | + * altar |AM_SANCTUM | AM_SHRINE | AM_MASK | AM_MASK | AM_MASK | + * | | | | | | + * +-------------+-------------+------------+------------+------------+ + * + * + * If these get changed or expanded, make sure wizard-mode wishing becomes + * aware of the new usage + * * Note: secret doors (SDOOR) want to use both rm.doormask and * rm.wall_info but those both overload rm.flags. SDOOR only * has 2 states (closed or locked). However, it can't specify @@ -143,11 +192,24 @@ enum levl_typ_types { * it isn't set to D_LOCKED (see cvt_sdoor_to_door() in detect.c). * * D_LOCKED conflicts with W_NONDIGGABLE but the latter is not - * expected to be used on door locations. D_TRAPPED conflicts - * with W_NONPASSWALL but secret doors aren't trapped. + * expected to be used on door locations. + * D_TRAPPED conflicts with W_NONPASSWALL. * D_SECRET would not fit within struct rm's 5-bit 'flags' field. */ +#define doormask flags /* door, sdoor (note conflict with wall_info) */ +#define altarmask flags /* alignment and maybe temple */ +#define wall_info flags /* wall, sdoor (note conflict with doormask) */ +#define ladder flags /* up or down */ +#define drawbridgemask flags /* what's underneath when the span is open */ +#define looted flags /* used for throne, tree, fountain, sink, door */ +#define icedpool flags /* used for ice (in case it melts) */ +#define emptygrave flags /* no corpse in grave */ + +/* candig is used for floor trap locations so is available for overload + on walls, doors, secret doors, and furniture */ +#define arboreal_sdoor candig + /* * The 5 possible states of doors. * For historical reasons they are numbered as mask bits rather than 0..4. @@ -238,28 +300,6 @@ enum levl_typ_types { #define ICED_POOL 8 #define ICED_MOAT 16 -/* - * The structure describing a coordinate position. - * Before adding fields, remember that this will significantly affect - * the size of temporary files and save files. - * - * Also remember that the run-length encoding for some ports in save.c - * must be updated to consider the field. - */ -struct rm { - int glyph; /* what the hero thinks is there */ - schar typ; /* what is really there [why is this signed?] */ - uchar seenv; /* seen vector */ - Bitfield(flags, 5); /* extra information for typ */ - Bitfield(horizontal, 1); /* wall/door/etc is horiz. (more typ info) */ - Bitfield(lit, 1); /* speed hack for lit rooms */ - Bitfield(waslit, 1); /* remember if a location was lit */ - - Bitfield(roomno, 6); /* room # for special rooms */ - Bitfield(edge, 1); /* marks boundaries for special rooms*/ - Bitfield(candig, 1); /* Exception to Can_dig_down; was a trapdoor */ -}; - /* light states for terrain replacements, for set_levltyp_lit */ #define SET_LIT_RANDOM -1 #define SET_LIT_NOCHANGE -2 @@ -346,16 +386,6 @@ struct rm { #define SV7 ((seenV) 0x80) #define SVALL ((seenV) 0xFF) -/* if these get changed or expanded, make sure wizard-mode wishing becomes - aware of the new usage */ -#define doormask flags /* door, sdoor (note conflict with wall_info) */ -#define altarmask flags /* alignment and maybe temple */ -#define wall_info flags /* wall, sdoor (note conflict with doormask) */ -#define ladder flags /* up or down */ -#define drawbridgemask flags /* what's underneath when the span is open */ -#define looted flags /* used for throne, tree, fountain, sink, door */ -#define icedpool flags /* used for ice (in case it melts) */ -#define emptygrave flags /* no corpse in grave */ /* horizontal applies to walls, doors (including sdoor); also to iron bars even though they don't have separate symbols for horizontal and vertical */ #define blessedftn horizontal /* a fountain that grants attribs */ diff --git a/include/savefile.h b/include/savefile.h new file mode 100644 index 000000000..dc569c771 --- /dev/null +++ b/include/savefile.h @@ -0,0 +1,594 @@ +/* NetHack 3.7 savefile.h $NHDT-Date: 1738638877 2025/02/03 19:14:37 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1476 $ */ +/* Copyright (c) Michael Allison, 2025. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef SAVEFILE_H +#define SAVEFILE_H + +/* #define SAVEFILE_DEBUGGING */ + +extern void sf_init(void); +/* sfbase.c output functions */ +extern void sfo_aligntyp(NHFILE *, aligntyp *, const char *); +extern void sfo_any(NHFILE *, anything *, const char *); +extern void sfo_boolean(NHFILE *, boolean *, const char *); +extern void sfo_char(NHFILE *, char *, const char *, int); +extern void sfo_genericptr(NHFILE *, genericptr_t *, const char *); +extern void sfo_int16(NHFILE *, int16 *, const char *); +extern void sfo_int32(NHFILE *, int32 *, const char *); +extern void sfo_int64(NHFILE *, int64 *, const char *); +extern void sfo_uchar(NHFILE *, uchar *, const char *); +extern void sfo_uint16(NHFILE *, uint16 *, const char *); +extern void sfo_uint32(NHFILE *, uint32 *, const char *); +extern void sfo_uint64(NHFILE *, uint64 *, const char *); +extern void sfo_size_t(NHFILE *, size_t *, const char *); +extern void sfo_time_t(NHFILE *, time_t *, const char *); +//extern void sfo_str(NHFILE *, char *, const char *, int); +extern void sfo_arti_info(NHFILE *nhfp, + struct arti_info *d_arti_info, + const char *myname); +extern void sfo_dgn_topology(NHFILE *nhfp, + struct dgn_topology *d_dgn_topology, + const char *myname); +extern void sfo_dungeon(NHFILE *nhfp, struct dungeon *d_dungeon, + const char *myname); +extern void sfo_branch(NHFILE *nhfp, struct branch *d_branch, + const char *myname); +extern void sfo_linfo(NHFILE *nhfp, struct linfo *d_linfo, + const char *myname); +extern void sfo_nhcoord(NHFILE *nhfp, struct nhcoord *d_nhcoord, + const char *myname); +extern void sfo_d_level(NHFILE *nhfp, struct d_level *d_d_level, + const char *myname); +extern void sfo_mapseen_feat(NHFILE *nhfp, + struct mapseen_feat *d_mapseen_feat, + const char *myname); +extern void sfo_mapseen_flags(NHFILE *nhfp, + struct mapseen_flags *d_mapseen_flags, + const char *myname); +extern void sfo_mapseen_rooms(NHFILE *nhfp, + struct mapseen_rooms *d_mapseen_rooms, + const char *myname); +extern void sfo_kinfo(NHFILE *nhfp, + struct kinfo *d_kinfo, + const char *myname); +extern void sfo_engr(NHFILE *, struct engr *, const char *); +extern void sfo_ls_t(NHFILE *, struct ls_t *, const char *); +extern void sfo_bubble(NHFILE *, struct bubble *, const char *); +extern void sfo_mkroom(NHFILE *, struct mkroom *, const char *); +extern void sfo_objclass(NHFILE *, struct objclass *, const char *); +extern void sfo_nhrect(NHFILE *, struct nhrect *, const char *); +extern void sfo_fe(NHFILE *, struct fe *, const char *); +extern void sfo_version_info(NHFILE *, struct version_info *, + const char *); +extern void sfo_context_info(NHFILE *, struct context_info *, + const char *); +extern void sfo_flag(NHFILE *, struct flag *, const char *); +extern void sfo_you(NHFILE *, struct you *, const char *); +extern void sfo_mvitals(NHFILE *, struct mvitals *, const char *); +extern void sfo_q_score(NHFILE *, struct q_score *, const char *); +extern void sfo_spell(NHFILE *, struct spell *, const char *); +extern void sfo_dest_area(NHFILE *, struct dest_area *, const char *); +extern void sfo_levelflags(NHFILE *, struct levelflags *, const char *); +extern void sfo_rm(NHFILE *, struct rm *, const char *); +extern void sfo_cemetery(NHFILE *, struct cemetery *, const char *); +extern void sfo_damage(NHFILE *, struct damage *, const char *); +extern void sfo_stairway(NHFILE *, struct stairway *, const char *); +extern void sfo_obj(NHFILE *, struct obj *, const char *); +extern void sfo_monst(NHFILE *, struct monst *, const char *); +extern void sfo_ebones(NHFILE *, struct ebones *, const char *); +extern void sfo_edog(NHFILE *, struct edog *, const char *); +extern void sfo_egd(NHFILE *, struct egd *, const char *); +extern void sfo_emin(NHFILE *, struct emin *, const char *); +extern void sfo_engr(NHFILE *, struct engr *, const char *); +extern void sfo_epri(NHFILE *, struct epri *, const char *); +extern void sfo_eshk(NHFILE *, struct eshk *, const char *); +extern void sfo_trap(NHFILE *, struct trap *, const char *); +extern void sfo_gamelog_line(NHFILE *, struct gamelog_line *, const char *); +extern void sfo_fruit(NHFILE *, struct fruit *, const char *); +extern void sfo_s_level(NHFILE *, struct s_level *, const char *); +extern void sfo_xint8(NHFILE *, xint8 *, const char *); +extern void sfo_xint16(NHFILE *, xint16 *, const char *); +extern void sfo_schar(NHFILE *, schar *, const char *); +extern void sfo_short(NHFILE *, short *, const char *); +extern void sfo_ushort(NHFILE *, ushort *, const char *); +extern void sfo_int(NHFILE *, int *, const char *); +extern void sfo_unsigned(NHFILE *, unsigned *, const char *); +extern void sfo_long(NHFILE *, long *, const char *); +extern void sfo_ulong(NHFILE *, ulong *, const char *); +/* sfbase.c input functions */ +extern void sfi_addinfo(NHFILE *, const char *, const char *); +extern void sfi_aligntyp(NHFILE *, aligntyp *, const char *); +extern void sfi_any(NHFILE *, anything *, const char *); +extern void sfi_boolean(NHFILE *, boolean *, const char *); +extern void sfi_genericptr(NHFILE *, genericptr_t *, const char *); +extern void sfi_char(NHFILE *, char *, const char *, int); +extern void sfi_int16(NHFILE *, int16 *, const char *); +extern void sfi_int32(NHFILE *, int32 *, const char *); +extern void sfi_int64(NHFILE *, int64 *, const char *); +extern void sfi_uchar(NHFILE *, uchar *, const char *); +extern void sfi_uint16(NHFILE *, uint16 *, const char *); +extern void sfi_uint32(NHFILE *, uint32 *, const char *); +extern void sfi_uint64(NHFILE *, uint64 *, const char *); +extern void sfi_size_t(NHFILE *, size_t *, const char *); +extern void sfi_time_t(NHFILE *, time_t *, const char *); +extern void sfi_arti_info(NHFILE *nhfp, + struct arti_info *d_arti_info, + const char *myname); +extern void sfi_dungeon(NHFILE *nhfp, struct dungeon *d_dungeon, + const char *myname); +extern void sfi_dgn_topology(NHFILE *nhfp, + struct dgn_topology *d_dgn_topology, + const char *myname); +extern void sfi_branch(NHFILE *nhfp, struct branch *d_branch, + const char *myname); +extern void sfi_linfo(NHFILE *nhfp, struct linfo *d_linfo, + const char *myname); +extern void sfi_nhcoord(NHFILE *nhfp, struct nhcoord *d_nhcoord, + const char *myname); +extern void sfi_d_level(NHFILE *nhfp, struct d_level *d_d_level, + const char *myname); +extern void sfi_mapseen_feat(NHFILE *nhfp, + struct mapseen_feat *d_mapseen_feat, + const char *myname); +extern void sfi_mapseen_flags(NHFILE *nhfp, + struct mapseen_flags *d_mapseen_flags, + const char *myname); +extern void sfi_mapseen_rooms(NHFILE *nhfp, + struct mapseen_rooms *d_mapseen_rooms, + const char *myname); +extern void sfi_kinfo(NHFILE *nhfp, + struct kinfo *d_kinfo, + const char *myname); +extern void sfi_engr(NHFILE *, struct engr *, const char *); +extern void sfi_ls_t(NHFILE *, struct ls_t *, const char *); +extern void sfi_bubble(NHFILE *, struct bubble *, const char *); +extern void sfi_mkroom(NHFILE *, struct mkroom *, const char *); +extern void sfi_objclass(NHFILE *, struct objclass *, const char *); +extern void sfi_nhrect(NHFILE *, struct nhrect *, const char *); +extern void sfi_fe(NHFILE *, struct fe *, const char *); +extern void sfi_version_info(NHFILE *, struct version_info *, + const char *); +extern void sfi_context_info(NHFILE *, struct context_info *, + const char *); +extern void sfi_flag(NHFILE *, struct flag *, const char *); +extern void sfi_you(NHFILE *, struct you *, const char *); +extern void sfi_mvitals(NHFILE *, struct mvitals *, const char *); +extern void sfi_q_score(NHFILE *, struct q_score *, const char *); +extern void sfi_spell(NHFILE *, struct spell *, const char *); +extern void sfi_dest_area(NHFILE *, struct dest_area *, const char *); +extern void sfi_levelflags(NHFILE *, struct levelflags *, const char *); +extern void sfi_rm(NHFILE *, struct rm *, const char *); +extern void sfi_cemetery(NHFILE *, struct cemetery *, const char *); +extern void sfi_damage(NHFILE *, struct damage *, const char *); +extern void sfi_stairway(NHFILE *, struct stairway *, const char *); +extern void sfi_obj(NHFILE *, struct obj *, const char *); +extern void sfi_monst(NHFILE *, struct monst *, const char *); +extern void sfi_ebones(NHFILE *, struct ebones *, const char *); +extern void sfi_edog(NHFILE *, struct edog *, const char *); +extern void sfi_egd(NHFILE *, struct egd *, const char *); +extern void sfi_emin(NHFILE *, struct emin *, const char *); +extern void sfi_engr(NHFILE *, struct engr *, const char *); +extern void sfi_epri(NHFILE *, struct epri *, const char *); +extern void sfi_eshk(NHFILE *, struct eshk *, const char *); +extern void sfi_trap(NHFILE *, struct trap *, const char *); +extern void sfi_fruit(NHFILE *, struct fruit *, const char *); +extern void sfi_gamelog_line(NHFILE *, struct gamelog_line *, const char *); +extern void sfi_s_level(NHFILE *, struct s_level *, const char *); +extern void sfi_xint8(NHFILE *, xint8 *, const char *); +extern void sfi_xint16(NHFILE *, xint16 *, const char *); +extern void sfi_schar(NHFILE *, schar *, const char *); +extern void sfi_short(NHFILE *, short *, const char *); +extern void sfi_ushort(NHFILE *, ushort *, const char *); +extern void sfi_int(NHFILE *, int *, const char *); +extern void sfi_unsigned(NHFILE *, unsigned *, const char *); +extern void sfi_long(NHFILE *, long *, const char *); +extern void sfi_ulong(NHFILE *, ulong *, const char *); +#if NH_C < 202300L +#define Sfo_aligntyp(a,b,c) sfo_aligntyp(a, b, c) +#define Sfo_any(a,b,c) sfo_any(a, b, c) +#define Sfo_genericptr(a,b,c) sfo_genericptr(a, b, c) +#define Sfo_coordxy(a,b,c) sfo_int16(a, b, c) +#define Sfo_char(a,b,c,d) sfo_char(a, b, c, d) +#define Sfo_int16(a,b,c) sfo_int16(a, b, c) +#define Sfo_int32(a,b,c) sfo_int32(a, b, c) +#define Sfo_int64(a,b,c) sfo_int64(a, b, c) +#define Sfo_uchar(a,b,c) sfo_uchar(a, b, c) +#define Sfo_uint16(a,b,c) sfo_uint16(a, b, c) +#define Sfo_uint32(a,b,c) sfo_uint32(a, b, c) +#define Sfo_uint64(a,b,c) sfo_uint64(a, b, c) +#define Sfo_size_t(a,b,c) sfo_size_t(a, b, c) +#define Sfo_time_t(a,b,c) sfo_time_t(a, b, c) +#define Sfo_str(a,b,c) sfo_str(a, b, c) +#define Sfo_arti_info(a,b,c) sfo_arti_info(a, b, c) +#define Sfo_dgn_topology(a,b,c) sfo_dgn_topology(a, b, c) +#define Sfo_dungeon(a,b,c) sfo_dungeon(a, b, c) +#define Sfo_branch(a,b,c) sfo_branch(a, b, c) +#define Sfo_linfo(a,b,c) sfo_linfo(a, b, c) +#define Sfo_nhcoord(a,b,c) sfo_nhcoord(a, b, c) +#define Sfo_d_level(a,b,c) sfo_d_level(a, b, c) +#define Sfo_mapseen_feat(a,b,c) sfo_mapseen_feat(a, b, c) +#define Sfo_mapseen_flags(a,b,c) sfo_mapseen_flags(a, b, c) +#define Sfo_mapseen_rooms(a,b,c) sfo_mapseen_rooms(a, b, c) +#define Sfo_kinfo(a,b,c) sfo_kinfo(a, b, c) +#define Sfo_engr(a,b,c) sfo_engr(a, b, c) +#define Sfo_ls_t(a,b,c) sfo_ls_t(a, b, c) +#define Sfo_bubble(a,b,c) sfo_bubble(a, b, c) +#define Sfo_mkroom(a,b,c) sfo_mkroom(a, b, c) +#define Sfo_objclass(a,b,c) sfo_objclass(a, b, c) +#define Sfo_nhrect(a,b,c) sfo_nhrect(a, b, c) +#define Sfo_fe(a,b,c) sfo_fe(a, b, c) +#define Sfo_version_info(a,b,c) sfo_version_info(a, b, c) +#define Sfo_context_info(a,b,c) sfo_context_info(a, b, c) +#define Sfo_flag(a,b,c) sfo_flag(a, b, c) +#define Sfo_you(a,b,c) sfo_you(a, b, c) +#define Sfo_mvitals(a,b,c) sfo_mvitals(a, b, c) +#define Sfo_q_score(a,b,c) sfo_q_score(a, b, c) +#define Sfo_spell(a,b,c) sfo_spell(a, b, c) +#define Sfo_dest_area(a,b,c) sfo_dest_area(a, b, c) +#define Sfo_levelflags(a,b,c) sfo_levelflags(a, b, c) +#define Sfo_rm(a,b,c) sfo_rm(a, b, c) +#define Sfo_cemetery(a,b,c) sfo_cemetery(a, b, c) +#define Sfo_damage(a,b,c) sfo_damage(a, b, c) +#define Sfo_stairway(a,b,c) sfo_stairway(a, b, c) +#define Sfo_obj(a,b,c) sfo_obj(a, b, c) +#define Sfo_monst(a,b,c) sfo_monst(a, b, c) +#define Sfo_ebones(a,b,c) sfo_ebones(a, b, c) +#define Sfo_edog(a,b,c) sfo_edog(a, b, c) +#define Sfo_egd(a,b,c) sfo_egd(a, b, c) +#define Sfo_emin(a,b,c) sfo_emin(a, b, c) +#define Sfo_engr(a,b,c) sfo_engr(a, b, c) +#define Sfo_epri(a,b,c) sfo_epri(a, b, c) +#define Sfo_eshk(a,b,c) sfo_eshk(a, b, c) +#define Sfo_trap(a,b,c) sfo_trap(a, b, c) +#define Sfo_gamelog_line(a,b,c) sfo_gamelog_line(a, b, c) +#define Sfo_fruit(a,b,c) sfo_fruit(a, b, c) +#define Sfo_s_level(a,b,c) sfo_s_level(a, b, c) +#define Sfo_short(a, b, c) sfo_short(a, b, c) +#define Sfo_ushort(a, b, c) sfo_ushort(a, b, c) +#define Sfo_int(a, b, c) sfo_int(a, b, c) +#define Sfo_unsigned(a, b, c) sfo_unsigned(a, b, c) +#define Sfo_xint8(a, b, c) sfo_xint8(a, b, c); +#define Sfo_xint16(a, b, c) sfo_xint16(a, b, c) +/* sfbase.c input functions */ +#define Sfi_addinfo(a,b,c) sfi_addinfo(a, b, c) +#define Sfi_aligntyp(a,b,c) sfi_aligntyp(a, b, c) +#define Sfi_any(a,b,c) sfi_any(a, b, c) +#define Sfi_genericptr(a,b,c) sfi_genericptr(a, b, c) +#define Sfi_coordxy(a,b,c) sfi_int16(a, b, c) +#define Sfi_int16(a,b,c) sfi_int16(a, b, c) +#define Sfi_int32(a,b,c) sfi_int32(a, b, c) +#define Sfi_int64(a,b,c) sfi_int64(a, b, c) +#define Sfi_uchar(a,b,c) sfi_uchar(a, b, c) +#define Sfi_uint16(a,b,c) sfi_uint16(a, b, c) +#define Sfi_uint32(a,b,c) sfi_uint32(a, b, c) +#define Sfi_uint64(a,b,c) sfi_uint64(a, b, c) +#define Sfi_size_t(a,b,c) sfi_size_t(a, b, c) +#define Sfi_time_t(a,b,c) sfi_time_t(a, b, c) +#define Sfi_arti_info(a,b,c) sfi_arti_info(a, b, c) +#define Sfi_dungeon(a,b,c) sfi_dungeon(a, b, c) +#define Sfi_dgn_topology(a,b,c) sfi_dgn_topology(a, b, c) +#define Sfi_branch(a,b,c) sfi_branch(a, b, c) +#define Sfi_linfo(a,b,c) sfi_linfo(a, b, c) +#define Sfi_nhcoord(a,b,c) sfi_nhcoord(a, b, c) +#define Sfi_d_level(a,b,c) sfi_d_level(a, b, c) +#define Sfi_mapseen_feat(a,b,c) sfi_mapseen_feat(a, b, c) +#define Sfi_mapseen_flags(a,b,c) sfi_mapseen_flags(a, b, c) +#define Sfi_mapseen_rooms(a,b,c) sfi_mapseen_rooms(a, b, c) +#define Sfi_kinfo(a,b,c) sfi_kinfo(a, b, c) +#define Sfi_engr(a,b,c) sfi_engr(a, b, c) +#define Sfi_ls_t(a,b,c) sfi_ls_t(a, b, c) +#define Sfi_bubble(a,b,c) sfi_bubble(a, b, c) +#define Sfi_mkroom(a,b,c) sfi_mkroom(a, b, c) +#define Sfi_objclass(a,b,c) sfi_objclass(a, b, c) +#define Sfi_nhrect(a,b,c) sfi_nhrect(a, b, c) +#define Sfi_fe(a,b,c) sfi_fe(a, b, c) +#define Sfi_version_info(a,b,c) sfi_version_info(a, b, c) +#define Sfi_context_info(a,b,c) sfi_context_info(a, b, c) +#define Sfi_flag(a,b,c) sfi_flag(a, b, c) +#define Sfi_you(a,b,c) sfi_you(a, b, c) +#define Sfi_mvitals(a,b,c) sfi_mvitals(a, b, c) +#define Sfi_q_score(a,b,c) sfi_q_score(a, b, c) +#define Sfi_spell(a,b,c) sfi_spell(a, b, c) +#define Sfi_dest_area(a,b,c) sfi_dest_area(a, b, c) +#define Sfi_levelflags(a,b,c) sfi_levelflags(a, b, c) +#define Sfi_rm(a,b,c) sfi_rm(a, b, c) +#define Sfi_cemetery(a,b,c) sfi_cemetery(a, b, c) +#define Sfi_damage(a,b,c) sfi_damage(a, b, c) +#define Sfi_stairway(a,b,c) sfi_stairway(a, b, c) +#define Sfi_obj(a,b,c) sfi_obj(a, b, c) +#define Sfi_monst(a,b,c) sfi_monst(a, b, c) +#define Sfi_ebones(a,b,c) sfi_ebones(a, b, c) +#define Sfi_edog(a,b,c) sfi_edog(a, b, c) +#define Sfi_egd(a,b,c) sfi_egd(a, b, c) +#define Sfi_emin(a,b,c) sfi_emin(a, b, c) +#define Sfi_engr(a,b,c) sfi_engr(a, b, c) +#define Sfi_epri(a,b,c) sfi_epri(a, b, c) +#define Sfi_eshk(a,b,c) sfi_eshk(a, b, c) +#define Sfi_trap(a,b,c) sfi_trap(a, b, c) +#define Sfi_fruit(a,b,c) sfi_fruit(a, b, c) +#define Sfi_gamelog_line(a,b,c) sfi_gamelog_line(a, b, c) +#define Sfi_s_level(a,b,c) sfi_s_level(a, b, c) +#define Sfi_short(a, b, c) sfi_short(a, b, c) +#define Sfi_ushort(a, b, c) sfi_ushort(a, b, c) +#define Sfi_int(a, b, c) sfi_int(a, b, c); +#define Sfi_unsigned(a, b, c) sfi_unsigned(a, b, c); +#define Sfi_xint8(a, b, c) sfi_xint8(a, b, c); +#define Sfi_xint16(a, b, c) sfi_xint16(a, b, c); +#else + +#define sfo(nhfp, dt, tag) \ + _Generic( (dt), \ + anything * : sfo_any, \ + int16_t * : sfo_int16, \ + int32_t * : sfo_int32, \ + int64_t * : sfo_int64, \ + uchar * : sfo_uchar, \ + uint16_t * : sfo_uint16, \ + uint32_t * : sfo_uint32, \ + uint64_t * : sfo_uint64, \ + xint8 * : sfo_xint8, \ + struct arti_info * : sfo_arti_info, \ + struct nhrect * : sfo_nhrect, \ + struct branch * : sfo_branch, \ + struct bubble * : sfo_bubble, \ + struct cemetery * : sfo_cemetery, \ + struct context_info * : sfo_context_info, \ + coord * : sfo_nhcoord, \ + struct damage * : sfo_damage, \ + struct dgn_topology * : sfo_dgn_topology, \ + dungeon * : sfo_dungeon, \ + d_level * : sfo_d_level, \ + struct levelflags * : sfo_levelflags, \ + light_source * : sfo_ls_t, \ + struct dest_area * : sfo_dest_area, \ + struct ebones * : sfo_ebones, \ + struct edog * : sfo_edog, \ + struct egd * : sfo_egd, \ + struct emin * : sfo_emin, \ + struct engr * : sfo_engr, \ + struct epri * : sfo_epri, \ + struct eshk * : sfo_eshk, \ + struct fe * : sfo_fe, \ + struct flag * : sfo_flag, \ + struct fruit * : sfo_fruit, \ + struct gamelog_line * : sfo_gamelog_line, \ + struct kinfo * : sfo_kinfo, \ + struct linfo * : sfo_linfo, \ + struct mapseen_feat * : sfo_mapseen_feat, \ + struct mapseen_flags *: sfo_mapseen_flags, \ + struct mapseen_rooms *: sfo_mapseen_rooms, \ + struct mkroom * : sfo_mkroom, \ + struct monst * : sfo_monst, \ + struct mvitals * : sfo_mvitals, \ + struct obj * : sfo_obj, \ + struct objclass * : sfo_objclass, \ + struct q_score * : sfo_q_score, \ + struct rm * : sfo_rm, \ + struct spell * : sfo_spell, \ + struct stairway * : sfo_stairway, \ + struct s_level * : sfo_s_level, \ + struct trap * : sfo_trap, \ + struct version_info * : sfo_version_info, \ + struct you * : sfo_you \ + ) (nhfp, dt, tag) + +/* struct container * : sfo_container, */ +/* struct mapseen * : sfo_mapseen, */ +/* struct mextra * : sfo_mextra, */ +/* struct oextra * : sfo_oextra, */ +/* struct permonst * : sfo_permonst, */ + +#define sfi(nhfp, dt, tag) \ + _Generic( (dt), \ + anything * : sfi_any, \ + int16_t * : sfi_int16, \ + int32_t * : sfi_int32, \ + int64_t * : sfi_int64, \ + uchar * : sfi_uchar, \ + uint16_t * : sfi_uint16, \ + uint32_t * : sfi_uint32, \ + uint64_t * : sfi_uint64, \ + xint8 * : sfi_xint8, \ + struct arti_info * : sfi_arti_info, \ + struct nhrect * : sfi_nhrect, \ + struct branch * : sfi_branch, \ + struct bubble * : sfi_bubble, \ + struct cemetery * : sfi_cemetery, \ + struct context_info * : sfi_context_info, \ + coord * : sfi_nhcoord, \ + struct damage * : sfi_damage, \ + struct dgn_topology * : sfi_dgn_topology, \ + dungeon * : sfi_dungeon, \ + d_level * : sfi_d_level, \ + struct levelflags * : sfi_levelflags, \ + light_source * : sfi_ls_t, \ + struct dest_area * : sfi_dest_area, \ + struct ebones * : sfi_ebones, \ + struct edog * : sfi_edog, \ + struct egd * : sfi_egd, \ + struct emin * : sfi_emin, \ + struct engr * : sfi_engr, \ + struct epri * : sfi_epri, \ + struct eshk * : sfi_eshk, \ + struct fe * : sfi_fe, \ + struct flag * : sfi_flag, \ + struct fruit * : sfi_fruit, \ + struct gamelog_line * : sfi_gamelog_line, \ + struct kinfo * : sfi_kinfo, \ + struct linfo * : sfi_linfo, \ + struct mapseen_feat * : sfi_mapseen_feat, \ + struct mapseen_flags *: sfi_mapseen_flags, \ + struct mapseen_rooms *: sfi_mapseen_rooms, \ + struct mkroom * : sfi_mkroom, \ + struct monst * : sfi_monst, \ + struct mvitals * : sfi_mvitals, \ + struct obj * : sfi_obj, \ + struct objclass * : sfi_objclass, \ + struct q_score * : sfi_q_score, \ + struct rm * : sfi_rm, \ + struct spell * : sfi_spell, \ + struct stairway * : sfi_stairway, \ + struct s_level * : sfi_s_level, \ + struct trap * : sfi_trap, \ + struct version_info * : sfi_version_info, \ + struct you * : sfi_you \ + ) (nhfp, dt, tag) + +/* char * : sfo_char, */ +/* char * : sfi_char, */ +/* struct container * : sfi_container, */ +/* struct mapseen * : sfi_mapseen, */ +/* struct mextra * : sfi_mextra, */ +/* struct oextra * : sfi_oextra, */ +/* struct permonst * : sfi_permonst, */ + +#define Sfo_any(a,b,c) sfo(a, b, c) +#define Sfo_aligntyp(a,b,c) sfo(a, b, c) +#define Sfo_genericptr(a,b,c) sfo(a, b, c) +#define Sfo_coordxy(a,b,c) sfo(a, b, c) +#define Sfo_int16(a,b,c) sfo(a, b, c) +#define Sfo_int32(a,b,c) sfo(a, b, c) +#define Sfo_int64(a,b,c) sfo(a, b, c) +#define Sfo_uchar(a,b,c) sfo(a, b, c) +#define Sfo_unsigned(a,b,c) sfo(a, b, c) +#define Sfo_uchar(a,b,c) sfo(a, b, c) +#define Sfo_uint16(a,b,c) sfo(a, b, c) +#define Sfo_uint32(a,b,c) sfo(a, b, c) +#define Sfo_uint64(a,b,c) sfo(a, b, c) +#define Sfo_size_t(a,b,c) sfo(a, b, c) +#define Sfo_time_t(a,b,c) sfo(a, b, c) +#define Sfo_str(a,b,c) sfo(a, b, c) +#define Sfo_arti_info(a,b,c) sfo(a, b, c) +#define Sfo_dgn_topology(a,b,c) sfo(a, b, c) +#define Sfo_dungeon(a,b,c) sfo(a, b, c) +#define Sfo_branch(a,b,c) sfo(a, b, c) +#define Sfo_linfo(a,b,c) sfo(a, b, c) +#define Sfo_nhcoord(a,b,c) sfo(a, b, c) +#define Sfo_d_level(a,b,c) sfo(a, b, c) +#define Sfo_mapseen_feat(a,b,c) sfo(a, b, c) +#define Sfo_mapseen_flags(a,b,c) sfo(a, b, c) +#define Sfo_mapseen_rooms(a,b,c) sfo(a, b, c) +#define Sfo_kinfo(a,b,c) sfo(a, b, c) +#define Sfo_engr(a,b,c) sfo(a, b, c) +#define Sfo_ls_t(a,b,c) sfo(a, b, c) +#define Sfo_bubble(a,b,c) sfo(a, b, c) +#define Sfo_mkroom(a,b,c) sfo(a, b, c) +#define Sfo_objclass(a,b,c) sfo(a, b, c) +#define Sfo_nhrect(a,b,c) sfo(a, b, c) +#define Sfo_fe(a,b,c) sfo(a, b, c) +#define Sfo_version_info(a,b,c) sfo(a, b, c) +#define Sfo_context_info(a,b,c) sfo(a, b, c) +#define Sfo_flag(a,b,c) sfo(a, b, c) +#define Sfo_you(a,b,c) sfo(a, b, c) +#define Sfo_mvitals(a,b,c) sfo(a, b, c) +#define Sfo_q_score(a,b,c) sfo(a, b, c) +#define Sfo_spell(a,b,c) sfo(a, b, c) +#define Sfo_dest_area(a,b,c) sfo(a, b, c) +#define Sfo_levelflags(a,b,c) sfo(a, b, c) +#define Sfo_rm(a,b,c) sfo(a, b, c) +#define Sfo_cemetery(a,b,c) sfo(a, b, c) +#define Sfo_damage(a,b,c) sfo(a, b, c) +#define Sfo_stairway(a,b,c) sfo(a, b, c) +#define Sfo_obj(a,b,c) sfo(a, b, c) +#define Sfo_monst(a,b,c) sfo(a, b, c) +#define Sfo_ebones(a,b,c) sfo(a, b, c) +#define Sfo_edog(a,b,c) sfo(a, b, c) +#define Sfo_egd(a,b,c) sfo(a, b, c) +#define Sfo_emin(a,b,c) sfo(a, b, c) +#define Sfo_engr(a,b,c) sfo(a, b, c) +#define Sfo_epri(a,b,c) sfo(a, b, c) +#define Sfo_eshk(a,b,c) sfo(a, b, c) +#define Sfo_trap(a,b,c) sfo(a, b, c) +#define Sfo_gamelog_line(a,b,c) sfo(a, b, c) +#define Sfo_fruit(a,b,c) sfo(a, b, c) +#define Sfo_s_level(a,b,c) sfo(a, b, c) +#define Sfo_short(a, b, c) sfo(a, b, c) +#define Sfo_ushort(a, b, c) sfo(a, b, c) +#define Sfo_int(a, b, c) sfo(a, b, c) +#define Sfo_unsigned(a, b, c) sfo(a, b, c) +#define Sfo_xint8(a, b, c) sfo(a, b, c) +#define Sfo_xint16(a, b, c) sfo(a, b, c) + +/* sfbase.c input functions */ +#define Sfi_addinfo(a,b,c) sfi(a, b, c) +#define Sfi_aligntyp(a,b,c) sfi(a, b, c) +#define Sfi_any(a,b,c) sfi(a, b, c) +#define Sfi_genericptr(a,b,c) sfi(a, b, c) +#define Sfi_coordxy(a,b,c) sfi(a, b, c) +#define Sfi_int16(a,b,c) sfi(a, b, c) +#define Sfi_int32(a,b,c) sfi(a, b, c) +#define Sfi_int64(a,b,c) sfi(a, b, c) +#define Sfi_uchar(a,b,c) sfi(a, b, c) +#define Sfi_uint16(a,b,c) sfi(a, b, c) +#define Sfi_uint32(a,b,c) sfi(a, b, c) +#define Sfi_uint64(a,b,c) sfi(a, b, c) +#define Sfi_size_t(a,b,c) sfi(a, b, c) +#define Sfi_time_t(a,b,c) sfi(a, b, c) +#define Sfi_arti_info(a,b,c) sfi(a, b, c) +#define Sfi_dungeon(a,b,c) sfi(a, b, c) +#define Sfi_dgn_topology(a,b,c) sfi(a, b, c) +#define Sfi_branch(a,b,c) sfi(a, b, c) +#define Sfi_linfo(a,b,c) sfi(a, b, c) +#define Sfi_nhcoord(a,b,c) sfi(a, b, c) +#define Sfi_d_level(a,b,c) sfi(a, b, c) +#define Sfi_mapseen_feat(a,b,c) sfi(a, b, c) +#define Sfi_mapseen_flags(a,b,c) sfi(a, b, c) +#define Sfi_mapseen_rooms(a,b,c) sfi(a, b, c) +#define Sfi_kinfo(a,b,c) sfi(a, b, c) +#define Sfi_engr(a,b,c) sfi(a, b, c) +#define Sfi_ls_t(a,b,c) sfi(a, b, c) +#define Sfi_bubble(a,b,c) sfi(a, b, c) +#define Sfi_mkroom(a,b,c) sfi(a, b, c) +#define Sfi_objclass(a,b,c) sfi(a, b, c) +#define Sfi_nhrect(a,b,c) sfi(a, b, c) +#define Sfi_fe(a,b,c) sfi(a, b, c) +#define Sfi_version_info(a,b,c) sfi(a, b, c) +#define Sfi_context_info(a,b,c) sfi(a, b, c) +#define Sfi_flag(a,b,c) sfi(a, b, c) +#define Sfi_you(a,b,c) sfi(a, b, c) +#define Sfi_mvitals(a,b,c) sfi(a, b, c) +#define Sfi_q_score(a,b,c) sfi(a, b, c) +#define Sfi_spell(a,b,c) sfi(a, b, c) +#define Sfi_dest_area(a,b,c) sfi(a, b, c) +#define Sfi_levelflags(a,b,c) sfi(a, b, c) +#define Sfi_rm(a,b,c) sfi(a, b, c) +#define Sfi_cemetery(a,b,c) sfi(a, b, c) +#define Sfi_damage(a,b,c) sfi(a, b, c) +#define Sfi_stairway(a,b,c) sfi(a, b, c) +#define Sfi_obj(a,b,c) sfi(a, b, c) +#define Sfi_monst(a,b,c) sfi(a, b, c) +#define Sfi_ebones(a,b,c) sfi(a, b, c) +#define Sfi_edog(a,b,c) sfi(a, b, c) +#define Sfi_egd(a,b,c) sfi(a, b, c) +#define Sfi_emin(a,b,c) sfi(a, b, c) +#define Sfi_engr(a,b,c) sfi(a, b, c) +#define Sfi_epri(a,b,c) sfi(a, b, c) +#define Sfi_eshk(a,b,c) sfi(a, b, c) +#define Sfi_trap(a,b,c) sfi(a, b, c) +#define Sfi_fruit(a,b,c) sfi(a, b, c) +#define Sfi_gamelog_line(a,b,c) sfi(a, b, c) +#define Sfi_s_level(a,b,c) sfi(a, b, c) +#define Sfi_short(a, b, c) sfi(a, b, c) +#define Sfi_ushort(a, b, c) sfi(a, b, c) +#define Sfi_int(a,b,c) sfi(a, b, c) +#define Sfi_unsigned(a, b, c) sfi(a, b, c) +#define Sfi_xint8(a, b, c) sfi(a, b, c) +#define Sfi_xint16(a, b, c) sfi(a, b, c) +#endif + +/* not in _Generic */ +#define Sfo_long(a,b,c) sfo_long(a, b, c); +#define Sfo_ulong(a,b,c) sfo_ulong(a, b, c); +#define Sfo_char(a,b,c,d) sfo_char(a, b, c, d) +#define Sfo_boolean(a,b,c) sfo_boolean(a, b, c) +#define Sfo_schar(a,b,c) sfo_schar(a, b, c) + +#define Sfi_long(a,b,c) sfi_long(a, b, c); +#define Sfi_ulong(a,b,c) sfi_ulong(a, b, c); +#define Sfi_char(a,b,c,d) sfi_char(a, b, c, d) +#define Sfi_boolean(a,b,c) sfi_boolean(a, b, c) +#define Sfi_schar(a,b,c) sfi_schar(a, b, c) + +#endif /* SAVEFILE_H */ + diff --git a/include/seffects.h b/include/seffects.h index 91a9153a9..0774209b0 100644 --- a/include/seffects.h +++ b/include/seffects.h @@ -197,6 +197,7 @@ seffect(stone_breaking), seffect(stone_crumbling), seffect(swoosh), + seffect(sword_blade_rings), seffect(thud), seffect(thump), seffect(thunderclap), diff --git a/include/sfmacros.h b/include/sfmacros.h new file mode 100644 index 000000000..752b74978 --- /dev/null +++ b/include/sfmacros.h @@ -0,0 +1,81 @@ +/* NetHack 3.7 sfmacros.h $NHDT-Date$ $NHDT-Branch$:$NHDT-Revision$ */ +/* Copyright (c) Michael Allison, 2025. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* This file is included by sfbase.c, sfstruct.c */ + +#if defined(SF_C) && defined(SF_A) + +SF_C(struct, arti_info) +SF_C(struct, nhrect) +SF_C(struct, branch) +SF_C(struct, bubble) +SF_C(struct, cemetery) +SF_C(struct, context_info) +SF_C(struct, nhcoord) +SF_C(struct, damage) +SF_C(struct, dest_area) +SF_C(struct, dgn_topology) +SF_C(struct, dungeon) +SF_C(struct, d_level) +SF_C(struct, ebones) +SF_C(struct, edog) +SF_C(struct, egd) +SF_C(struct, emin) +SF_C(struct, engr) +SF_C(struct, epri) +SF_C(struct, eshk) +SF_C(struct, fe) +SF_C(struct, flag) +SF_C(struct, fruit) +SF_C(struct, gamelog_line) +SF_C(struct, kinfo) +SF_C(struct, levelflags) +SF_C(struct, ls_t) +SF_C(struct, linfo) +SF_C(struct, mapseen_feat) +SF_C(struct, mapseen_flags) +SF_C(struct, mapseen_rooms) +SF_C(struct, mkroom) +SF_C(struct, monst) +SF_C(struct, mvitals) +SF_C(struct, obj) +SF_C(struct, objclass) +SF_C(struct, q_score) +SF_C(struct, rm) +SF_C(struct, spell) +SF_C(struct, stairway) +SF_C(struct, s_level) +SF_C(struct, trap) +SF_C(struct, you) +SF_C(union, any) + +SF_A(aligntyp) +SF_A(boolean) +SF_A(coordxy) +//SF_A(genericptr) +SF_A(int) +SF_A(int16) +SF_A(int32) +SF_A(int64) +SF_A(long) +SF_A(schar) +SF_A(short) +SF_A(size_t) +SF_A(time_t) +SF_A(uchar) +SF_A(uint16) +SF_A(uint32) +SF_A(uint64) +SF_A(ulong) +SF_A(unsigned) +SF_A(ushort) +SF_A(xint16) +SF_A(xint8) + +#else + +#error Non-productive inclusion of sfmacros.h + +#endif /* SF_C && SF_A */ + diff --git a/include/sfprocs.h b/include/sfprocs.h new file mode 100644 index 000000000..f544b04c9 --- /dev/null +++ b/include/sfprocs.h @@ -0,0 +1,202 @@ +/* NetHack 3.6 sfprocs.h Tue Nov 6 19:38:48 2018 */ +/* Copyright (c) NetHack Development Team 2025. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef SFPROCS_H +#define SFPROCS_H + +#define NHTYPE_SIMPLE 1 +#define NHTYPE_COMPLEX 2 + +#define SF_PROTO(dtyp) \ + extern void sfo_##dtyp(NHFILE *, dtyp *, const char *); \ + extern void sfi_##dtyp(NHFILE *, dtyp *, const char *); \ + extern void sfo_x_##dtyp(NHFILE *, dtyp *, const char *); \ + extern void sfi_x_##dtyp(NHFILE *, dtyp *, const char *) +#define SF_PROTO_C(keyw, dtyp) \ + extern void sfo_##dtyp(NHFILE *, keyw dtyp *, const char *); \ + extern void sfi_##dtyp(NHFILE *, keyw dtyp *, const char *); \ + extern void sfo_x_##dtyp(NHFILE *, keyw dtyp *, const char *); \ + extern void sfi_x_##dtyp(NHFILE *, keyw dtyp *, const char *) +#define SF_PROTO_X(xxx, dtyp) \ + extern void sfo_##dtyp(NHFILE *, xxx *, const char *, int bfsz); \ + extern void sfi_##dtyp(NHFILE *, xxx *, const char *, int bfsz); \ + extern void sfo_x_##dtyp(NHFILE *, xxx *, const char *, int bfsz); \ + extern void sfi_x_##dtyp(NHFILE *, xxx *, const char *, int bfsz) + +SF_PROTO_C(struct, arti_info); +SF_PROTO_C(struct, nhrect); +SF_PROTO_C(struct, branch); +SF_PROTO_C(struct, bubble); +SF_PROTO_C(struct, cemetery); +SF_PROTO_C(struct, context_info); +SF_PROTO_C(struct, nhcoord); +SF_PROTO_C(struct, damage); +SF_PROTO_C(struct, dest_area); +SF_PROTO_C(struct, dgn_topology); +SF_PROTO_C(struct, dungeon); +SF_PROTO_C(struct, d_level); +SF_PROTO_C(struct, ebones); +SF_PROTO_C(struct, edog); +SF_PROTO_C(struct, egd); +SF_PROTO_C(struct, emin); +SF_PROTO_C(struct, engr); +SF_PROTO_C(struct, epri); +SF_PROTO_C(struct, eshk); +SF_PROTO_C(struct, fe); +SF_PROTO_C(struct, flag); +SF_PROTO_C(struct, fruit); +SF_PROTO_C(struct, gamelog_line); +SF_PROTO_C(struct, kinfo); +SF_PROTO_C(struct, levelflags); +SF_PROTO_C(struct, ls_t); +SF_PROTO_C(struct, linfo); +SF_PROTO_C(struct, mapseen_feat); +SF_PROTO_C(struct, mapseen_flags); +SF_PROTO_C(struct, mapseen_rooms); +SF_PROTO_C(struct, mkroom); +SF_PROTO_C(struct, monst); +SF_PROTO_C(struct, mvitals); +SF_PROTO_C(struct, obj); +SF_PROTO_C(struct, objclass); +SF_PROTO_C(struct, q_score); +SF_PROTO_C(struct, rm); +SF_PROTO_C(struct, spell); +SF_PROTO_C(struct, stairway); +SF_PROTO_C(struct, s_level); +SF_PROTO_C(struct, trap); +SF_PROTO_C(struct, version_info); +SF_PROTO_C(struct, you); +SF_PROTO_C(union, any); +SF_PROTO(int16); +SF_PROTO(int32); +SF_PROTO(int64); +SF_PROTO(uchar); +SF_PROTO(uint16); +SF_PROTO(uint32); +SF_PROTO(uint64); +SF_PROTO(long); +SF_PROTO(ulong); +SF_PROTO(xint8); +SF_PROTO(boolean); +SF_PROTO(schar); +SF_PROTO(aligntyp); +SF_PROTO(genericptr); +SF_PROTO(size_t); +SF_PROTO(time_t); +SF_PROTO(int); +SF_PROTO(unsigned); +SF_PROTO(coordxy); +SF_PROTO(short); +SF_PROTO(xint16); +SF_PROTO(ushort); +SF_PROTO_X(uint8_t, bitfield); +SF_PROTO_X(char, char); + +#undef SF_PROTO +#undef SF_PROTO_C +#undef SF_PROTO_X + +#define SF_ENTRY(dtyp) \ + void (*sf_##dtyp)(NHFILE *, dtyp *, const char *) +#define SF_ENTRY_C(keyw, dtyp) \ + void (*sf_##dtyp)(NHFILE *, keyw dtyp *, const char *) +#define SF_ENTRY_X(xxx, dtyp) \ + void (*sf_##dtyp)(NHFILE *, xxx *, const char *, int bfsz) + +struct sf_procs { + SF_ENTRY_C(struct, arti_info); + SF_ENTRY_C(struct, nhrect); + SF_ENTRY_C(struct, branch); + SF_ENTRY_C(struct, bubble); + SF_ENTRY_C(struct, cemetery); + SF_ENTRY_C(struct, context_info); + SF_ENTRY_C(struct, nhcoord); + SF_ENTRY_C(struct, damage); + SF_ENTRY_C(struct, dest_area); + SF_ENTRY_C(struct, dgn_topology); + SF_ENTRY_C(struct, dungeon); + SF_ENTRY_C(struct, d_level); + SF_ENTRY_C(struct, ebones); + SF_ENTRY_C(struct, edog); + SF_ENTRY_C(struct, egd); + SF_ENTRY_C(struct, emin); + SF_ENTRY_C(struct, engr); + SF_ENTRY_C(struct, epri); + SF_ENTRY_C(struct, eshk); + SF_ENTRY_C(struct, fe); + SF_ENTRY_C(struct, flag); + SF_ENTRY_C(struct, fruit); + SF_ENTRY_C(struct, gamelog_line); + SF_ENTRY_C(struct, kinfo); + SF_ENTRY_C(struct, levelflags); + SF_ENTRY_C(struct, ls_t); + SF_ENTRY_C(struct, linfo); + SF_ENTRY_C(struct, mapseen_feat); + SF_ENTRY_C(struct, mapseen_flags); + SF_ENTRY_C(struct, mapseen_rooms); + SF_ENTRY_C(struct, mkroom); + SF_ENTRY_C(struct, monst); + SF_ENTRY_C(struct, mvitals); + SF_ENTRY_C(struct, obj); + SF_ENTRY_C(struct, objclass); + SF_ENTRY_C(struct, q_score); + SF_ENTRY_C(struct, rm); + SF_ENTRY_C(struct, spell); + SF_ENTRY_C(struct, stairway); + SF_ENTRY_C(struct, s_level); + SF_ENTRY_C(struct, trap); + SF_ENTRY_C(struct, version_info); + SF_ENTRY_C(struct, you); + SF_ENTRY_C(union, any); + + SF_ENTRY(aligntyp); + SF_ENTRY(boolean); + SF_ENTRY(coordxy); + SF_ENTRY(genericptr); + SF_ENTRY(int); + SF_ENTRY(int16); + SF_ENTRY(int32); + SF_ENTRY(int64); + SF_ENTRY(long); + SF_ENTRY(schar); + SF_ENTRY(short); + SF_ENTRY(size_t); + SF_ENTRY(time_t); + SF_ENTRY(uchar); + SF_ENTRY(uint16); + SF_ENTRY(uint32); + SF_ENTRY(uint64); + SF_ENTRY(ulong); + SF_ENTRY(unsigned); + SF_ENTRY(ushort); + SF_ENTRY(xint16); + SF_ENTRY(xint8); + SF_ENTRY_X(char, char); + SF_ENTRY_X(uint8_t, bitfield); +}; + +#undef SF_ENTRY +#undef SF_ENTRY_C +#undef SF_ENTRY_X + +struct sf_structlevel_procs { + const char *ext; + struct sf_procs fn; /* called for structlevel (historical) */ +}; +struct sf_fieldlevel_procs { + const char *ext; + struct sf_procs fn_x; /* called for fieldlevel */ +}; +extern struct sf_structlevel_procs sfoprocs[NUM_SAVEFORMATS], sfiprocs[NUM_SAVEFORMATS]; +extern void sf_setprocs(int, struct sf_structlevel_procs *, struct sf_structlevel_procs *); +extern void sf_setflprocs(int, struct sf_fieldlevel_procs *, struct sf_fieldlevel_procs *); +extern struct sf_structlevel_procs sfoprocs[NUM_SAVEFORMATS], sfiprocs[NUM_SAVEFORMATS]; +extern struct sf_fieldlevel_procs sfoflprocs[NUM_SAVEFORMATS], sfiflprocs[NUM_SAVEFORMATS]; +extern struct sf_structlevel_procs historical_sfo_procs; +extern struct sf_structlevel_procs historical_sfi_procs; +extern struct sf_fieldlevel_procs exportascii_sfo_procs; +extern struct sf_fieldlevel_procs exportascii_sfi_procs; + +#endif /* SFPROCS_H */ + diff --git a/include/sp_lev.h b/include/sp_lev.h index d4d4d8c4b..769c6a1c7 100644 --- a/include/sp_lev.h +++ b/include/sp_lev.h @@ -74,6 +74,11 @@ enum lvlinit_types { #define NO_LOC_WARN 0x20 /* no complaints and set x & y to -1, if no loc */ #define SPACELOC 0x40 /* like DRY, but accepts furniture too */ +/* has_invent flags */ +#define NO_INVENT 0 /* monster doesn't get any invent */ +#define CUSTOM_INVENT 0x01 /* monster gets items specified in lua */ +#define DEFAULT_INVENT 0x02 /* monster gets items from makemon() */ + #define SP_COORD_X(l) (l & 0xff) #define SP_COORD_Y(l) ((l >> 16) & 0xff) #define SP_COORD_PACK(x, y) (((x) & 0xff) + (((y) & 0xff) << 16)) @@ -156,7 +161,7 @@ typedef struct { int quan; short buried; short lit; - short eroded, locked, trapped, recharged, invis, greased, broken, + short eroded, locked, trapped, tknown, recharged, invis, greased, broken, achievement; } object; diff --git a/include/sys.h b/include/sys.h index 32d9221cc..63f7f5394 100644 --- a/include/sys.h +++ b/include/sys.h @@ -5,7 +5,7 @@ #ifndef SYS_H #define SYS_H -struct sysopt { +struct sysopt_s { char *support; /* local support contact */ char *recover; /* how to run recover - may be overridden by win port */ char *wizards; /* space-separated list of usernames */ @@ -15,6 +15,7 @@ struct sysopt { char *shellers; /* like wizards, for ! command (-DSHELL); also ^Z */ char *genericusers; /* usernames that prompt for user name */ char *debugfiles; /* files to show debugplines in. '*' is all. */ + char *msghandler; #ifdef DUMPLOG char *dumplogfile; /* where the dump file is saved */ #endif @@ -59,7 +60,7 @@ struct sysopt { * 1: suppress it */ }; -extern struct sysopt sysopt; +extern struct sysopt_s sysopt; #define SYSOPT_SEDUCE sysopt.seduce diff --git a/include/tradstdc.h b/include/tradstdc.h index c5a9368fb..4c304b625 100644 --- a/include/tradstdc.h +++ b/include/tradstdc.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 tradstdc.h $NHDT-Date: 1685522034 2023/05/31 08:33:54 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.54 $ */ +/* NetHack 3.7 tradstdc.h $NHDT-Date: 1744938651 2025/04/17 17:10:51 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.67 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -178,14 +178,10 @@ typedef const char *vA; typedef genericptr genericptr_t; /* (void *) or (char *) */ #endif -#if defined(MICRO) || defined(WIN32) +#ifndef NO_PTR_FMT /* We actually want to know which systems have an ANSI run-time library * to know which support the %p format for printing pointers. - * Due to the presence of things like gcc, NHSTDC is not a good test. - * So we assume microcomputers have all converted to ANSI and bigger - * computers which may have older libraries give reasonable results with - * casting pointers to unsigned long int (fmt_ptr() in alloc.c). - */ + * Since we require C99 or later, assume the library supports it. */ #define HAS_PTR_FMT #endif @@ -317,6 +313,96 @@ typedef genericptr genericptr_t; /* (void *) or (char *) */ #undef signed #endif +/* + * Language + * Standard + * + * NetHack 3.7 range + * / + * / + * C2y X NetHack 3.6 and earlier range + * C23 X / + * C17 X X + * C11 X X + * C99 X X + * C89 X + * + * + * The NetHack 3.7 source code currently makes use of the following + * C99 (and above) language features: + * + * commas at the end of enumerator lists + * variable declarations in for loop initializers + * mixing declarations and code + * variadic macros + * 'long long' + * + * The NetHack 3.7 source code adheres to the following greater-than C99 + * language restrictions: + * + * Removal of K&R function definitions + * Removal of implicit int + */ + +/* + * Provide a shorthand way of checking for a certain C standard + * in the NetHack header files by always setting NH_C to one + * of three possible values (as of January 2025): + * + * NH_C >= 202300L Being compiled under C23 or greater + * NH_C >= 199900L Being compiled under C99 or greater + * NH_C >= 198900L Being compiled under C89 or greater, + * or C std could not be determined. + */ +#if defined(__STDC_VERSION__) +#if (__STDC_VERSION__ >= 202000L) +#define NH_C 202300L +#else +#define NH_C 199900L +#endif /* C23 or C99 */ +#else /* __STDC_VERSION not defined */ +#define NH_C 198900L +#endif /* __STDC_VERSION not defined */ +#ifndef NH_C +#define NH_C 198900L +#endif + +/* NH_C is now defined to 198900L or 199900L or 202300L */ + +#if NH_C >= 202300L +/* Give first priority to standard */ +#ifndef __has_c_attribute +#define __has_c_attribute(x) 0 +#endif +/* + * noreturn + */ +#ifndef ATTRNORETURN +#define ATTRNORETURN [[noreturn]] +/* #warning [[noreturn]] from C23 */ +#endif /* ATTRNORETURN not defined */ +/* + * fallthrough + */ +#if __has_c_attribute(fallthrough) +/* Standard attribute is available, use it. */ +#define FALLTHROUGH [[fallthrough]] +/* #warning [[fallthrough]] from C23 */ +#endif /* __has_c_attribute(fallthrough) */ +/* + * maybe_unused + */ +#if __has_c_attribute(maybe_unused) +#ifndef ATTRUNUSED +#define ATTRUNUSED [[maybe_unused]] +#endif +#endif /* __has_c_attribute(maybe_unused) */ +#endif /* NH_C >= 202300L */ + +/* + * Compiler-specific + */ + #ifdef __clang__ /* clang's gcc emulation is sufficient for nethack's usage */ #ifndef __GNUC__ @@ -325,19 +411,10 @@ typedef genericptr genericptr_t; /* (void *) or (char *) */ #endif /* - * Give first priority to standard - */ -#ifndef ATTRNORETURN -#if defined(__STDC_VERSION__) || defined(__cplusplus) -#if (__STDC_VERSION__ > 202300L) || defined(__cplusplus) -#define ATTRNORETURN [[noreturn]] -#endif -#endif -#endif - -/* - * Allow gcc2 to check parameters of printf-like calls with -Wformat; - * append this to a prototype declaration (see pline() in extern.h). + * gcc (and also clang which masquerades as__GNUC__==5 due to #define above) + * + * Allow gcc2 and above to check parameters of printf-like calls with + * -Wformat; append this to a prototype declaration (see pline() in extern.h). */ #ifdef __GNUC__ #if (__GNUC__ >= 2) && !defined(USE_OLDARGS) @@ -347,32 +424,52 @@ typedef genericptr genericptr_t; /* (void *) or (char *) */ #define PRINTF_F_PTR(f, v) PRINTF_F(f, v) #endif #if __GNUC__ >= 3 +#ifndef ATTRUNUSED #define UNUSED __attribute__((unused)) +#endif #ifndef ATTRNORETURN #ifndef NORETURN #define NORETURN __attribute__((noreturn)) -#endif -#endif -#if (!defined(__linux__) && !defined(MACOS)) || defined(GCC_URWARN) -/* disable gcc's __attribute__((__warn_unused_result__)) since explicitly - discarding the result by casting to (void) is not accepted as a 'use' */ -#define __warn_unused_result__ /*empty*/ -#define warn_unused_result /*empty*/ -#endif -#endif +/* #warning NORETURN __attribute__((noreturn)) from __GNUC__ >= 3 */ +#endif /* NORETURN */ +#endif /* ATTRNORETURN */ +#endif /* __GNUC__ >= 3 */ #if __GNUC__ >= 5 #ifndef NONNULLS_DEFINED #define DO_DEFINE_NONNULLS #endif /* !NONNULLS_DEFINED */ /* #pragma message is available */ #define NH_PRAGMA_MESSAGE 1 -#endif -#endif +#endif /* __GNUC__ greater than or equal to 5 */ +#if (!defined(__linux__) && !defined(MACOS)) || defined(GCC_URWARN) +/* disable gcc's __attribute__((__warn_unused_result__)) since explicitly + discarding the result by casting to (void) is not accepted as a 'use' */ +#define __warn_unused_result__ /*empty*/ +#define warn_unused_result /*empty*/ +#endif /* GCC_URWARN || !__linux || !MACOS */ +#endif /* __GNUC__ || clang masquerading as __GNUC__==5 */ -#if defined(__clang__) && !defined(DO_DEFINE_NONNULLS) +/* + * clang-specific + * + */ +#if defined(__clang__) +#ifndef FALLTHROUGH +#if defined(__clang_major__) +#if __clang_major__ >= 9 +#define FALLTHROUGH __attribute__((fallthrough)) +/* #warning FALLTHROUGH __attribute__((fallthrough)) from clang */ +#endif /* __clang_major__ greater than or equal to 9 */ +#endif /* __clang_major__ is defined */ +#endif /* FALLTHROUGH */ +#if !defined(DO_DEFINE_NONNULLS) #define DO_DEFINE_NONNULLS #endif +#endif /* __clang__ */ +/* + * NONNULL args + */ #if defined(DO_DEFINE_NONNULLS) && !defined(NONNULLS_DEFINED) #define NONNULL __attribute__((returns_nonnull)) #define NONNULLPTRS __attribute__((nonnull)) @@ -395,16 +492,25 @@ typedef genericptr genericptr_t; /* (void *) or (char *) */ #define NONNULLARG45 __attribute__((nonnull (4, 5))) /* do_screen_descri... */ #define NONNULLS_DEFINED #undef DO_DEFINE_NONNULLS -#endif /* __clang__ && !NONNULLS_DEFINED */ +#endif /* DO_DEFINE_NONNULLS && !NONNULLS_DEFINED */ +/* + * Microsoft compiler + */ #ifdef _MSC_VER #ifndef ATTRNORETURN #define ATTRNORETURN __declspec(noreturn) +/* #warning ATTRNORETURN __declspec(noreturn) from _MSC_VER */ #endif /* #pragma message is available */ #define NH_PRAGMA_MESSAGE 1 +#endif /* _MSC_VER */ + +#if !defined(UNUSED) && defined(ATTRUNUSED) +#define UNUSED ATTRUNUSED #endif +/* Fallback implementations */ #ifndef PRINTF_F #define PRINTF_F(f, v) #endif @@ -414,6 +520,12 @@ typedef genericptr genericptr_t; /* (void *) or (char *) */ #ifndef UNUSED #define UNUSED #endif +#ifndef ATTRUNUSED +#define ATTRUNUSED +#endif +#ifndef FALLTHROUGH +#define FALLTHROUGH +#endif #ifndef ATTRNORETURN #define ATTRNORETURN #endif diff --git a/include/unixconf.h b/include/unixconf.h index e27f542d2..1e346d3d5 100644 --- a/include/unixconf.h +++ b/include/unixconf.h @@ -302,8 +302,6 @@ #if defined(BSD) || defined(ULTRIX) #include -#else -#include #endif /* these might be needed for include/system.h; @@ -324,10 +322,7 @@ #define SHELL /* do not delete the '!' command */ #endif -/* #include "system.h" */ - #if defined(POSIX_TYPES) || defined(__GNUC__) -#include #include #endif diff --git a/include/vmsconf.h b/include/vmsconf.h index 7eac14895..57b8a7b05 100644 --- a/include/vmsconf.h +++ b/include/vmsconf.h @@ -213,7 +213,6 @@ PANICTRACE_GDB=2 #at conclusion of panic, show a call traceback and then #include #include #include -#include #include #include #include @@ -246,8 +245,6 @@ typedef int32_t off_t; #endif #endif /* _DECC_V4_SOURCE */ -#include - #ifndef VMSVSI #if 0 /* is missing for old gcc versions; skip it to save time */ #include diff --git a/include/weight.h b/include/weight.h new file mode 100644 index 000000000..a404eb1af --- /dev/null +++ b/include/weight.h @@ -0,0 +1,34 @@ +/* NetHack 3.7 weight.h $NHDT-Date: 1742427965 2025/03/19 23:46:05 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1 $ */ +/* Copyright (c) Michael Allison, 2025. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef WEIGHT_H +#define WEIGHT_H + +/* weight-related constants and thresholds */ +enum weight_constants { + WT_ETHEREAL = 0, + WT_SPLASH_THRESHOLD = 9, /* weight needed to make splash in water */ + WT_WEIGHTCAP_STRCON = 25, /* str + con multiplied by this for conv to + * carrying capacity in weight_cap() */ + WT_WEIGHTCAP_SPARE = 50, /* used in weight_cap calc */ + WT_JELLY = 50, /* weight of jelly body */ + WT_WOUNDEDLEG_REDUCT = 100, /* wounded legs reduce carrcap by this */ + WT_TO_DMG = 100, /* divisor to convert weight to dmg amt */ + WT_IRON_BALL_INCR = 160, /* weight increment of heavy iron ball */ + WT_IRON_BALL_BASE = 480, /* base starting weight of iron ball */ + WT_NOISY_INV = 500, /* inv_weight() max for noisy fumbling */ + WT_NYMPH = 600, /* weight of nymph body */ + WT_TOOMUCH_DIAGONAL = 600, /* weight_cap threshold for diag squeeze */ + WT_ELF = 800, /* weight of elf body */ + WT_SQUEEZABLE_INV = 850, /* inv_weight() maximum for squeezing */ + MAX_CARR_CAP = 1000, /* max carrying capacity, so that + * boulders can be heavier */ + WT_HUMAN = 1450, /* weight of human body */ + WT_BABY_DRAGON = 1500, /* weight of baby dragon body */ + WT_DRAGON = 4500, /* weight of dragon body */ +}; + +#endif /* WEIGHT_H */ + + diff --git a/include/winX.h b/include/winX.h index b43e442ac..295abaa8d 100644 --- a/include/winX.h +++ b/include/winX.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 winX.h $NHDT-Date: 1643491525 2022/01/29 21:25:25 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.52 $ */ +/* NetHack 3.7 winX.h $NHDT-Date: 1740795096 2025/02/28 18:11:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.65 $ */ /* Copyright (c) Dean Luick, 1992 */ /* NetHack may be freely redistributed. See license for details. */ @@ -26,6 +26,19 @@ #endif #endif +/* winX.c uses XtOffset() and the way that that macro is defined in + triggers "performing pointer subtraction with + a null pointer has undefined behavior" warnings; this modified + edition doesn't guarantee defined behavior but does silence those + warnings without needing to know whether current compiler version + supports the '-Wno-null-pointer-subtraction' option */ +#ifdef XtOffset +#undef XtOffset +#define XtOffset(p_type,field) \ + ((Cardinal) (((ptrdiff_t) (char *) (&(((p_type) NULL)->field))) \ + - ((ptrdiff_t) (char *) NULL))) +#endif + /* * Generic text buffer. */ @@ -489,10 +502,6 @@ extern void X11_status_enablefield(int, const char *, const char *, boolean); extern void X11_status_update(int, genericptr_t, int, int, int, unsigned long *); -/* other defs that really should go away (they're tty specific) */ -extern void X11_start_screen(void); -extern void X11_end_screen(void); - #ifdef GRAPHIC_TOMBSTONE extern void X11_outrip(winid, int, time_t); #else diff --git a/include/wincurs.h b/include/wincurs.h index 98ac5ae9e..4c5b7f19c 100644 --- a/include/wincurs.h +++ b/include/wincurs.h @@ -108,8 +108,6 @@ extern void curses_getlin(const char *question, char *input); extern int curses_get_ext_cmd(void); extern void curses_number_pad(int state); extern void curses_delay_output(void); -extern void curses_start_screen(void); -extern void curses_end_screen(void); extern void curses_outrip(winid wid, int how, time_t when); extern void genl_outrip(winid tmpwin, int how, time_t when); extern void curses_preference_update(const char *pref); diff --git a/include/windconf.h b/include/windconf.h index ebfac1e8b..bd30f8a6b 100644 --- a/include/windconf.h +++ b/include/windconf.h @@ -98,7 +98,7 @@ extern char *windows_exepath(void); *=============================================== */ -#ifdef __MINGW32__ +#ifdef __GNUC__ #define MD_USE_TMPFILE_S # #ifdef strncasecmp @@ -107,13 +107,9 @@ extern char *windows_exepath(void); #ifdef strcasecmp #undef strcasecmp /* https://sourceforge.net/p/mingw-w64/wiki2/gnu%20printf/ */ -#ifdef __USE_MINGW_ANSI_STDIO -#undef __USE_MINGW_ANSI_STDIO -#endif -#define __USE_MINGW_ANSI_STDIO 1 #endif /* extern int getlock(void); */ -#endif /* __MINGW32__ */ +#endif /* __GNUC__ */ #ifdef _MSC_VER #define MD_USE_TMPFILE_S @@ -169,8 +165,6 @@ typedef SSIZE_T ssize_t; #include -#include -#include #ifdef __BORLANDC__ #undef randomize #undef random @@ -189,10 +183,6 @@ typedef SSIZE_T ssize_t; #endif #define NO_SIGNAL - -/* Time stuff */ -#include - #define USE_STDARG /* Use the high quality random number routines. */ @@ -278,13 +268,16 @@ extern int alternative_palette(char *); #endif #define nethack_enter(argc, argv) nethack_enter_windows() -ATTRNORETURN extern void nethack_exit(int) NORETURN; extern boolean file_exists(const char *); extern boolean file_newer(const char *, const char *); #ifndef SYSTEM_H /* #include "system.h" */ #endif +#if defined(WIN_CE) +#define QSORTCALLBACK __cdecl +#endif + /* Override the default version of nhassert. The default version is unable * to generate a string form of the expression due to the need to be * compatible with compilers which do not support macro stringization (i.e. diff --git a/include/winprocs.h b/include/winprocs.h index 8fc078e6c..c77288891 100644 --- a/include/winprocs.h +++ b/include/winprocs.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 winprocs.h $NHDT-Date: 1725653017 2024/09/06 20:03:37 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.81 $ */ +/* NetHack 3.7 winprocs.h $NHDT-Date: 1736530208 2025/01/10 09:30:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.83 $ */ /* Copyright (c) David Cohrs, 1992 */ /* NetHack may be freely redistributed. See license for details. */ @@ -10,7 +10,7 @@ #include "color.h" #endif -enum wp_ids { wp_tty = 1, wp_X11, wp_Qt, wp_mswin, wp_curses, +enum wp_ids { wp_tty = 1, wp_X11, wp_Qt, wp_mswin, wp_curses, wp_chainin, wp_chainout, wp_safestartup, wp_shim, wp_hup, wp_guistubs, wp_ttystubs, #ifdef OUTDATED_STUFF @@ -80,10 +80,6 @@ struct window_procs { char *(*win_get_color_string)(void); #endif - /* other defs that really should go away (they're tty specific) */ - void (*win_start_screen)(void); - void (*win_end_screen)(void); - void (*win_outrip)(winid, int, time_t); void (*win_preference_update)(const char *); char *(*win_getmsghistory)(boolean); @@ -128,10 +124,9 @@ extern #define display_file (*windowprocs.win_display_file) #define start_menu (*windowprocs.win_start_menu) #define end_menu (*windowprocs.win_end_menu) -/* 3.7: There are real add_menu() and select_menu - * in the core now. +/* 3.7: There are real add_menu() and select_menu in the core now. * add_menu does some common activities, such as menu_colors. - * select_menu does some before and after activities. + * select_menu does some before and after activities. * add_menu() and select_menu() are in windows.c */ /* #define add_menu (*windowprocs.win_add_menu) */ @@ -177,10 +172,6 @@ extern */ /* #define yn_function (*windowprocs.win_yn_function) */ -/* other defs that really should go away (they're tty specific) */ -#define start_screen (*windowprocs.win_start_screen) -#define end_screen (*windowprocs.win_end_screen) - #define outrip (*windowprocs.win_outrip) #define preference_update (*windowprocs.win_preference_update) #define getmsghistory (*windowprocs.win_getmsghistory) @@ -193,10 +184,10 @@ extern #define ctrl_nhwindow (*windowprocs.win_ctrl_nhwindow) /* - * + * */ #define WPID(name) #name, wp_##name -#define WPIDMINUS(name) "-" #name, wp_##name +#define WPIDMINUS(name) "-" #name, wp_##name /* * WINCAP @@ -404,10 +395,6 @@ struct chain_procs { char *(*win_get_color_string)(CARGS); #endif - /* other defs that really should go away (they're tty specific) */ - void (*win_start_screen)(CARGS); - void (*win_end_screen)(CARGS); - void (*win_outrip)(CARGS, winid, int, time_t); void (*win_preference_update)(CARGS, const char *); char *(*win_getmsghistory)(CARGS, boolean); @@ -481,8 +468,6 @@ extern short safe_set_font_name(winid, char *); #endif extern char *safe_get_color_string(void); #endif -extern void safe_start_screen(void); -extern void safe_end_screen(void); extern void safe_outrip(winid, int, time_t); extern void safe_preference_update(const char *); extern char *safe_getmsghistory(boolean); diff --git a/include/wintty.h b/include/wintty.h index 87183708e..c0c5d842e 100644 --- a/include/wintty.h +++ b/include/wintty.h @@ -34,6 +34,7 @@ typedef struct tty_mi { anything identifier; /* user identifier */ long count; /* user count */ char *str; /* description string (including accelerator) */ + glyph_info glyphinfo; /* glyph */ int attr; /* string attribute */ int color; /* string color */ boolean selected; /* TRUE if selected by user */ @@ -129,6 +130,8 @@ struct tty_status_fields { #endif #define NHW_BASE (NHW_LAST_TYPE + 1) +/* external declarations */ + extern struct window_procs tty_procs; /* port specific variable declarations */ @@ -144,24 +147,56 @@ extern char defmorestr[]; /* default --more-- prompt */ /* port specific external function references */ /* ### getline.c ### */ + extern void xwaitforspace(const char *); /* ### termcap.c, video.c ### */ -extern void tty_startup(int *, int *); -#ifndef NO_TERMS -extern void tty_shutdown(void); -#endif -extern int xputc(int); -extern void xputs(const char *); -#if defined(SCREEN_VGA) || defined(SCREEN_8514) || defined(SCREEN_VESA) -extern void xputg(const glyph_info *, const glyph_info *); -#endif +/* + * TERM or NO_TERMS + * + * The tty windowport interface relies on lower-level support routines + * to actually manipulate the terminal/display. Those are the right place + * for doing strange and arcane things such as outputting escape sequences + * to select a color or whatever. wintty.c should concern itself with WHERE + * to put stuff in a window. + + * The TERM routines are found in: + * + * !NO_TERMS: termcap.c + * + * NO_TERMS: + * WINCON sys/windows/consoletty.c + * MSDOS sys/msdos/video.c + * + */ +extern void backsp(void); extern void cl_end(void); -extern void term_clear_screen(void); +extern void cl_eos(void); +extern void graph_on(void); +extern void graph_off(void); extern void home(void); extern void standoutbeg(void); extern void standoutend(void); +extern int term_attr_fixup(int); +extern void term_clear_screen(void); +extern void term_curs_set(int); +extern void term_end_attr(int attr); +extern void term_end_color(void); +extern void term_end_extracolor(void); +extern void term_end_raw_bold(void); +extern void term_end_screen(void); +extern void term_start_attr(int attr); +extern void term_start_bgcolor(int color); +extern void term_start_color(int color); +extern void term_start_extracolor(uint32, uint16); +extern void term_start_raw_bold(void); +extern void term_start_screen(void); +extern void term_startup(int *, int *); +extern void term_shutdown(void); + +extern int xputc(int); +extern void xputs(const char *); #if 0 extern void revbeg(void); extern void boldbeg(void); @@ -169,29 +204,9 @@ extern void blinkbeg(void); extern void dimbeg(void); extern void m_end(void); #endif -extern void backsp(void); -extern void graph_on(void); -extern void graph_off(void); -extern void cl_eos(void); - -/* - * termcap.c (or facsimiles in other ports) is the right place for doing - * strange and arcane things such as outputting escape sequences to select - * a color or whatever. wintty.c should concern itself with WHERE to put - * stuff in a window. - */ -extern int term_attr_fixup(int); -extern void term_start_attr(int attr); -extern void term_end_attr(int attr); -extern void term_start_raw_bold(void); -extern void term_end_raw_bold(void); - -extern void term_end_color(void); -extern void term_start_color(int color); -extern void term_start_bgcolor(int color); -extern void term_start_extracolor(uint32, uint16); -extern void term_end_extracolor(void); /* termcap.c, consoletty.c */ -extern void term_curs_set(int); +#if defined(SCREEN_VGA) || defined(SCREEN_8514) || defined(SCREEN_VESA) +extern void xputg(const glyph_info *, const glyph_info *); +#endif /* ### topl.c ### */ @@ -203,6 +218,7 @@ extern void update_topl(const char *); extern void putsyms(const char *); /* ### wintty.c ### */ + #ifdef CLIPPING extern void setclipped(void); #endif @@ -210,14 +226,14 @@ extern void docorner(int, int, int); extern void end_glyphout(void); extern void g_putch(int); #ifdef ENHANCED_SYMBOLS -#if defined(WIN32) || defined(UNIX) +#if defined(WIN32) || defined(UNIX) || defined(MSDOS) extern void g_pututf8(uint8 *); #endif #endif /* ENHANCED_SYMBOLS */ extern void erase_tty_screen(void); extern void win_tty_init(int); -/* external declarations */ +/* tty interface */ extern void tty_init_nhwindows(int *, char **); extern void tty_preference_update(const char *); extern void tty_player_selection(void); @@ -275,10 +291,6 @@ extern void tty_status_init(void); extern void tty_status_update(int, genericptr_t, int, int, int, unsigned long *); -/* other defs that really should go away (they're tty specific) */ -extern void tty_start_screen(void); -extern void tty_end_screen(void); - extern void genl_outrip(winid, int, time_t); extern char *tty_getmsghistory(boolean); diff --git a/include/wintype.h b/include/wintype.h index e3b38a959..6b94bef64 100644 --- a/include/wintype.h +++ b/include/wintype.h @@ -32,6 +32,8 @@ typedef union any { const char *a_string; int (*a_nfunc)(void); unsigned long a_mask32; /* used by status highlighting */ + int64 a_int64; + uint64 a_uint64; /* add types as needed */ } anything; #define ANY_P union any /* avoid typedef in prototypes @@ -55,7 +57,9 @@ enum any_types { ANY_ULPTR, /* pointer to unsigned long */ ANY_STR, /* pointer to null-terminated char string */ ANY_NFUNC, /* pointer to function taking no args, returning int */ - ANY_MASK32 /* 32-bit mask (stored as unsigned long) */ + ANY_MASK32, /* 32-bit mask (stored as unsigned long) */ + + ANY_INVALID /* leave this last */ }; /* menu return list */ @@ -205,6 +209,7 @@ enum to_core_flags { too_small = 0x002, prohibited = 0x004, no_init_done = 0x008, + too_early = 0x010, }; enum from_core_requests { diff --git a/include/you.h b/include/you.h index cd734ca42..bbd679d8f 100644 --- a/include/you.h +++ b/include/you.h @@ -52,6 +52,9 @@ struct u_event { Bitfield(udemigod, 1); /* killed the wiz */ Bitfield(uvibrated, 1); /* stepped on "vibrating square" */ Bitfield(ascended, 1); /* has offered the Amulet */ + + Bitfield(amulet_wish, 1); /* has gained a wish from the Amulet */ + /* 7 free bits */ }; /* @@ -153,6 +156,7 @@ struct u_conduct { /* number of times... */ long polyselfs; /* transformed yourself */ long wishes; /* used a wish */ long wisharti; /* wished for an artifact */ + long hf_reserved1; /* hf uses for elbereth;for hf savefile compatiblity */ long sokocheat; /* violated special 'rules' in Sokoban */ long pets; /* obtained a pet */ /* genocides already listed at end of game */ diff --git a/include/youprop.h b/include/youprop.h index 331d6ede1..f2c166606 100644 --- a/include/youprop.h +++ b/include/youprop.h @@ -156,6 +156,10 @@ #define Blind_telepat (HTelepat || ETelepat) #define Unblind_telepat (ETelepat) +#define HBlnd_resist u.uprops[BLND_RES].intrinsic /* from form */ +#define EBlnd_resist u.uprops[BLND_RES].extrinsic /* wielding Sunsword */ +#define Blnd_resist (HBlnd_resist || EBlnd_resist) + #define HWarning u.uprops[WARNING].intrinsic #define EWarning u.uprops[WARNING].extrinsic #define Warning (HWarning || EWarning) diff --git a/outdated/include/trampoli.h b/outdated/include/trampoli.h index 344dacc93..f45d3bb98 100644 --- a/outdated/include/trampoli.h +++ b/outdated/include/trampoli.h @@ -241,8 +241,8 @@ #define tty_nhbell() tty_nhbell_() #define tty_number_pad(x) tty_number_pad_(x) #define tty_delay_output() tty_delay_output_() -#define tty_start_screen() tty_start_screen_() -#define tty_end_screen() tty_end_screen_() +#define term_start_screen() term_start_screen_() +#define term_end_screen() term_end_screen_() /* ### topl.c ### */ #define tty_doprev_message() tty_doprev_message_() diff --git a/outdated/include/wingem.h b/outdated/include/wingem.h index 86b3f6ba3..4c1378c0e 100644 --- a/outdated/include/wingem.h +++ b/outdated/include/wingem.h @@ -83,7 +83,7 @@ extern void Gem_cliparound(int, int); #ifdef POSITIONBAR extern void Gem_update_positionbar(char *); #endif -extern void Gem_print_glyph(winid, xchar, xchar, const glyph_info *, +extern void Gem_print_glyph(winid, coordxy, coordxy, const glyph_info *, const glyph_info *); extern void Gem_raw_print(const char *); extern void Gem_raw_print_bold(const char *); @@ -101,10 +101,6 @@ extern void Gem_change_color(int color, long rgb, int reverse); extern char *Gem_get_color_string(void); #endif -/* other defs that really should go away (they're tty specific) */ -extern void Gem_start_screen(void); -extern void Gem_end_screen(void); - extern void genl_outrip(winid, int, time_t); diff --git a/outdated/sys/amiga/winami.c b/outdated/sys/amiga/winami.c index fc860d603..4b7866346 100644 --- a/outdated/sys/amiga/winami.c +++ b/outdated/sys/amiga/winami.c @@ -62,8 +62,7 @@ struct window_procs amii_procs = { #ifdef CHANGE_COLOR /* only a Mac option currently */ amii_change_color, amii_get_color_string, #endif - /* other defs that really should go away (they're tty specific) */ - amii_delay_output, amii_delay_output, amii_outrip, genl_preference_update, + amii_outrip, genl_preference_update, genl_getmsghistory, genl_putmsghistory, genl_status_init, genl_status_finish, genl_status_enablefield, genl_status_update, @@ -97,8 +96,7 @@ struct window_procs amiv_procs = { #ifdef CHANGE_COLOR /* only a Mac option currently */ amii_change_color, amii_get_color_string, #endif - /* other defs that really should go away (they're tty specific) */ - amii_delay_output, amii_delay_output, amii_outrip, genl_preference_update, + amii_outrip, genl_preference_update, genl_getmsghistory, genl_putmsghistory, genl_status_init, genl_status_finish, genl_status_enablefield, genl_status_update, diff --git a/outdated/sys/amiga/winami.p b/outdated/sys/amiga/winami.p index 0fd69a38f..2e26cfb10 100644 --- a/outdated/sys/amiga/winami.p +++ b/outdated/sys/amiga/winami.p @@ -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 , xchar , xchar , int, int ); +void amii_print_glyph(winid , coordxy , coordxy , int, int ); void DoMenuScroll(int , int ); void DisplayData(int , int , int ); void SetPropInfo(struct Window * , struct Gadget * , long , long , long ); diff --git a/outdated/sys/amiga/winfuncs.c b/outdated/sys/amiga/winfuncs.c index 3624e3815..28a811ba7 100644 --- a/outdated/sys/amiga/winfuncs.c +++ b/outdated/sys/amiga/winfuncs.c @@ -1515,8 +1515,7 @@ boolean blocking; void amii_curs(window, x, y) winid window; -register int x, y; /* not xchar: perhaps xchar is unsigned and - curx-x would be unsigned as well */ +register int x, y; { register struct amii_WinDesc *cw; register struct Window *w; @@ -1954,7 +1953,7 @@ port_help() void amii_print_glyph(win, x, y, glyph, bkglyph) winid win; -xchar x, y; +coordxy x, y; int glyph, bkglyph; { struct amii_WinDesc *cw; diff --git a/outdated/sys/amiga/winproto.h b/outdated/sys/amiga/winproto.h index eb2b588d8..efa1e9b58 100644 --- a/outdated/sys/amiga/winproto.h +++ b/outdated/sys/amiga/winproto.h @@ -101,7 +101,7 @@ void amii_resume_nhwindows(void); void amii_bell(void); void removetopl(int cnt); void port_help(void); -void amii_print_glyph(winid win, xchar x, xchar y, int glyph, int bkglyph); +void amii_print_glyph(winid win, coordxy x, coordxy y, int glyph, int bkglyph); void amii_raw_print(const char *s); void amii_raw_print_bold(const char *s); void amii_update_inventory(void); diff --git a/outdated/sys/mac/macwin.c b/outdated/sys/mac/macwin.c index 1da056a89..517b7f5a6 100644 --- a/outdated/sys/mac/macwin.c +++ b/outdated/sys/mac/macwin.c @@ -3275,9 +3275,6 @@ struct window_procs mac_procs = { tty_change_color, tty_change_background, set_tty_font_name, tty_get_color_string, #endif - /* other defs that really should go away (they're tty specific) */ - 0, // mac_start_screen, - 0, // mac_end_screen, genl_outrip, genl_preference_update, genl_can_suspend_no, }; diff --git a/outdated/sys/mac/mrecover.c b/outdated/sys/mac/mrecover.c index 01ea7587b..9a42c4889 100644 --- a/outdated/sys/mac/mrecover.c +++ b/outdated/sys/mac/mrecover.c @@ -1192,7 +1192,7 @@ restore_savefile() { static int savelev; long saveTemp, lev; - xchar levc; + xint8 levc; struct version_info version_data; /* level 0 file contains: @@ -1264,7 +1264,7 @@ restore_savefile() levRefNum = open_levelfile(lev); if (levRefNum >= 0) { /* any or all of these may not exist */ - levc = (xchar) lev; + levc = (xint8) lev; (void) write_savefile(saveRefNum, (Ptr) &levc, sizeof(levc)); diff --git a/outdated/sys/mac/mttymain.c b/outdated/sys/mac/mttymain.c index 2cd59a9d2..0020dccd1 100644 --- a/outdated/sys/mac/mttymain.c +++ b/outdated/sys/mac/mttymain.c @@ -541,7 +541,7 @@ setftty(void) } void -tty_startup(int *width, int *height) +term_startup(int *width, int *height) { _mt_init_stuff(); *width = CO; @@ -581,13 +581,13 @@ tty_number_pad(int arg) } void -tty_start_screen(void) +term_start_screen(void) { iflags.cbreak = 1; } void -tty_end_screen(void) +term_end_screen(void) { } diff --git a/outdated/sys/msdos/SCHEMA35.MSC b/outdated/sys/msdos/SCHEMA35.MSC index 5d9392e69..400dc2f1a 100644 --- a/outdated/sys/msdos/SCHEMA35.MSC +++ b/outdated/sys/msdos/SCHEMA35.MSC @@ -254,10 +254,10 @@ functions:230 _thrwmu _tileview _timed_occupation _timer_is_local _timer_sanity_ functions:231 _topl_putsym _topologize _topten _topten_print _topten_print_bold _toss_up _toss_wsegs _touch_artifact functions:232 _touchfood _trap_detect _trickery _try_disarm _try_lift _trycall _tt_oname _tty_add_menu functions:233 _tty_askname _tty_clear_nhwindow _tty_cliparound -functions:234 _tty_display_file _tty_display_nhwindow _tty_doprev_message _tty_end_menu _tty_end_screen _tty_exit_nhwindows _tty_get_ext_cmd _tty_get_nh_event +functions:234 _tty_display_file _tty_display_nhwindow _tty_doprev_message _tty_end_menu _term_end_screen _tty_exit_nhwindows _tty_get_ext_cmd _tty_get_nh_event functions:235 _tty_getlin _tty_init_nhwindows _tty_mark_synch _tty_message_menu _tty_nh_poskey _tty_nhbell _tty_nhgetch _tty_number_pad functions:236 _tty_player_selection _tty_raw_print _tty_raw_print_bold _tty_resume_nhwindows _tty_select_menu -functions:237 _tty_start_menu _tty_start_screen _tty_startup _tty_suspend_nhwindows _tty_update_inventory _tty_yn_function +functions:237 _tty_start_menu _term_start_screen _tty_startup _tty_suspend_nhwindows _tty_update_inventory _tty_yn_function functions:238 _txt_backsp _txt_cl_end _txt_cl_eos _txt_clear_screen _txt_get_scr_size _txt_monoadapt_check functions:239 _txt_nhbell _txt_startup _u_entered_shop _u_gname _u_init functions:240 _u_left_shop _u_on_dnstairs _u_on_newpos _u_on_sstairs _u_on_upstairs _u_slip_free _u_slow_down _u_teleport_mon @@ -272,7 +272,7 @@ functions:248 _use_unicorn_horn _use_whip _use_whistle _useup _useupall _useupf functions:249 _uwepgone _vault_occupied _vault_tele _verbalize _vga_backsp _vga_cl_end _vga_cl_eos _vga_clear_screen functions:250 _vga_cliparound _vga_detect _vga_DisplayCell _vga_DisplayCell_O _vga_DrawCursor _vga_Finish _vga_FontPtrs _vga_get_scr_size functions:251 _vga_gotoloc _vga_HideCursor _vga_Init _vga_overview _vga_redrawmap _vga_refresh _vga_SetPalette _vga_SwitchMode -functions:252 _vga_traditional _vga_tty_end_screen _vga_tty_startup _vga_update_positionbar _vga_userpan _vga_WriteChar _vga_WriteStr _vga_xputc +functions:252 _vga_traditional _vga_term_end_screen _vga_term_startup _vga_update_positionbar _vga_userpan _vga_WriteChar _vga_WriteStr _vga_xputc functions:254 _vision_reset _dodiscovered _dodoor _dodown _dodrink _dodrop _doeat _doengrave functions:255 _wallification _wallify_map _wallify_vault _wantdoor _watch_on_duty _water_damage _water_friction functions:256 _water_prayer _weapon_dam_bonus _weapon_hit_bonus _weapon_type _wearing_armor _weffects _weight diff --git a/outdated/sys/msdos/schema3.MSC b/outdated/sys/msdos/schema3.MSC index ab9353f67..1e8670872 100644 --- a/outdated/sys/msdos/schema3.MSC +++ b/outdated/sys/msdos/schema3.MSC @@ -254,10 +254,10 @@ functions:230 _thrwmu _tileview _timed_occupation _timer_is_local _timer_sanity_ functions:231 _topl_putsym _topologize _topten _topten_print _topten_print_bold _toss_up _toss_wsegs _touch_artifact functions:232 _touchfood _trap_detect _trickery _try_disarm _try_lift _trycall _tt_oname _tty_add_menu functions:233 _tty_askname _tty_clear_nhwindow _tty_cliparound -functions:234 _tty_display_file _tty_display_nhwindow _tty_doprev_message _tty_end_menu _tty_end_screen _tty_exit_nhwindows _tty_get_ext_cmd _tty_get_nh_event +functions:234 _tty_display_file _tty_display_nhwindow _tty_doprev_message _tty_end_menu _term_end_screen _tty_exit_nhwindows _tty_get_ext_cmd _tty_get_nh_event functions:235 _tty_getlin _tty_init_nhwindows _tty_mark_synch _tty_message_menu _tty_nh_poskey _tty_nhbell _tty_nhgetch _tty_number_pad functions:236 _tty_player_selection _tty_raw_print _tty_raw_print_bold _tty_resume_nhwindows _tty_select_menu -functions:237 _tty_start_menu _tty_start_screen _tty_startup _tty_suspend_nhwindows _tty_update_inventory _tty_yn_function +functions:237 _tty_start_menu _term_start_screen _term_startup _tty_suspend_nhwindows _tty_update_inventory _tty_yn_function functions:238 _txt_backsp _txt_cl_end _txt_cl_eos _txt_clear_screen _txt_get_scr_size _txt_monoadapt_check functions:239 _txt_nhbell _txt_startup _u_entered_shop _u_gname _u_init functions:240 _u_left_shop _u_on_dnstairs _u_on_newpos _u_on_sstairs _u_on_upstairs _u_slip_free _u_slow_down _u_teleport_mon @@ -272,7 +272,7 @@ functions:248 _use_unicorn_horn _use_whip _use_whistle _useup _useupall _useupf functions:249 _uwepgone _vault_occupied _vault_tele _verbalize _vga_backsp _vga_cl_end _vga_cl_eos _vga_clear_screen functions:250 _vga_cliparound _vga_detect _vga_DisplayCell _vga_DisplayCell_O _vga_DrawCursor _vga_Finish _vga_FontPtrs _vga_get_scr_size functions:251 _vga_gotoloc _vga_HideCursor _vga_Init _vga_overview _vga_redrawmap _vga_refresh _vga_SetPalette _vga_SwitchMode -functions:252 _vga_traditional _vga_tty_end_screen _vga_tty_startup _vga_update_positionbar _vga_userpan _vga_WriteChar _vga_WriteStr _vga_xputc +functions:252 _vga_traditional _vga_term_end_screen _vga_term_startup _vga_update_positionbar _vga_userpan _vga_WriteChar _vga_WriteStr _vga_xputc functions:254 _vision_reset _dodiscovered _dodoor _dodown _dodrink _dodrop _doeat _doengrave functions:255 _wallification _wallify_map _wallify_vault _wantdoor _watch_on_duty _water_damage _water_friction functions:256 _water_prayer _weapon_dam_bonus _weapon_hit_bonus _weapon_type _wearing_armor _weffects _weight diff --git a/outdated/sys/wince/mswproc.c b/outdated/sys/wince/mswproc.c index e84dab8e1..8011991bc 100644 --- a/outdated/sys/wince/mswproc.c +++ b/outdated/sys/wince/mswproc.c @@ -71,8 +71,7 @@ struct window_procs mswin_procs = { #ifdef CHANGE_COLOR /* only a Mac option currently */ mswin, mswin_change_background, #endif - /* other defs that really should go away (they're tty specific) */ - mswin_start_screen, mswin_end_screen, mswin_outrip, + mswin_outrip, mswin_preference_update, genl_getmsghistory, genl_putmsghistory, genl_status_init, genl_status_finish, genl_status_enablefield, genl_status_update, @@ -1537,31 +1536,6 @@ mswin_get_color_string() return (""); } -/* -start_screen() -- Only used on Unix tty ports, but must be declared for - completeness. Sets up the tty to work in full-screen - graphics mode. Look at win/tty/termcap.c for an - example. If your window-port does not need this function - just declare an empty function. -*/ -void -mswin_start_screen() -{ - /* Do Nothing */ - logDebug("mswin_start_screen()\n"); -} - -/* -end_screen() -- Only used on Unix tty ports, but must be declared for - completeness. The complement of start_screen(). -*/ -void -mswin_end_screen() -{ - /* Do Nothing */ - logDebug("mswin_end_screen()\n"); -} - /* outrip(winid, int, when) -- The tombstone code. If you want the traditional code use diff --git a/outdated/sys/wince/winMS.h b/outdated/sys/wince/winMS.h index 3ee5622df..0ff0dd916 100644 --- a/outdated/sys/wince/winMS.h +++ b/outdated/sys/wince/winMS.h @@ -152,8 +152,6 @@ void mswin_number_pad(int state); void mswin_delay_output(void); void mswin_change_color(void); char *mswin_get_color_string(void); -void mswin_start_screen(void); -void mswin_end_screen(void); void mswin_outrip(winid wid, int how, time_t when); void mswin_preference_update(const char *pref); diff --git a/outdated/win/Qt3/qt3_win.cpp b/outdated/win/Qt3/qt3_win.cpp index 483761cfd..26e46c4bb 100644 --- a/outdated/win/Qt3/qt3_win.cpp +++ b/outdated/win/Qt3/qt3_win.cpp @@ -5152,16 +5152,6 @@ void NetHackQtBind::qt_delay_output() delay.wait(); } -void NetHackQtBind::qt_start_screen() -{ - // Ignore. -} - -void NetHackQtBind::qt_end_screen() -{ - // Ignore. -} - void NetHackQtBind::qt_outrip(winid wid, int how, time_t when) { NetHackQtWindow* window=id_to_window[wid]; @@ -5285,9 +5275,6 @@ struct window_procs Qt_procs = { donull, donull, #endif - /* other defs that really should go away (they're tty specific) */ - NetHackQtBind::qt_start_screen, - NetHackQtBind::qt_end_screen, #ifdef GRAPHIC_TOMBSTONE NetHackQtBind::qt_outrip, #else diff --git a/outdated/win/Qt3/qt3_win.h b/outdated/win/Qt3/qt3_win.h index 522f57d69..c5548e37c 100644 --- a/outdated/win/Qt3/qt3_win.h +++ b/outdated/win/Qt3/qt3_win.h @@ -877,8 +877,6 @@ class NetHackQtBind : NetHackQtBindBase static int qt_get_ext_cmd(); static void qt_number_pad(int); static void qt_delay_output(); - static void qt_start_screen(); - static void qt_end_screen(); static void qt_outrip(winid wid, int how, time_t when); static int qt_kbhit(); diff --git a/outdated/win/gem/wingem.c b/outdated/win/gem/wingem.c index df8d1d81c..078a2d2a7 100644 --- a/outdated/win/gem/wingem.c +++ b/outdated/win/gem/wingem.c @@ -69,8 +69,7 @@ struct window_procs Gem_procs = { Gem_get_color_string, #endif - /* other defs that really should go away (they're tty specific) */ - Gem_start_screen, Gem_end_screen, Gem_outrip, Gem_preference_update, + Gem_outrip, Gem_preference_update, genl_getmsghistory, genl_putmsghistory genl_status_init, genl_status_finish, genl_status_enablefield, genl_status_update, @@ -523,16 +522,6 @@ Gem_resume_nhwindows() { } -void -Gem_end_screen() -{ -} - -void -Gem_start_screen() -{ -} - extern void mar_exit_nhwindows(void); extern boolean run_from_desktop; @@ -891,7 +880,7 @@ int x, y; * position and glyph are always correct (checked there)! */ -void mar_print_gl_char(winid, xchar, xchar, int); +void mar_print_gl_char(winid, coordxy, coordxy, int); extern int mar_set_rogue(int); @@ -900,7 +889,7 @@ extern void mar_add_pet_sign(winid, int, int); void Gem_print_glyph(window, x, y, glyph, bkglyph) winid window; -xchar x, y; +coordxy x, y; int glyph, bkglyph; { /* Move the cursor. */ @@ -919,12 +908,12 @@ int glyph, bkglyph; mar_print_gl_char(window, x, y, glyph); } -void mar_print_char(winid, xchar, xchar, char, int); +void mar_print_char(winid, coordxy, coordxy, char, int); void mar_print_gl_char(window, x, y, glyph) winid window; -xchar x, y; +coordxy x, y; int glyph; { int ch; diff --git a/outdated/win/gem/wingem1.c b/outdated/win/gem/wingem1.c index 75c96918d..594c3ccbf 100644 --- a/outdated/win/gem/wingem1.c +++ b/outdated/win/gem/wingem1.c @@ -26,11 +26,10 @@ typedef signed char schar; #define CHAR_P char #define SCHAR_P schar #define UCHAR_P uchar -#define XCHAR_P xchar +#define COORDXY_P coordxy #define SHORT_P short #define BOOLEAN_P boolean #define ALIGNTYP_P aligntyp -typedef signed char xchar; #include "wingem.h" #undef CHAR_P #undef SCHAR_P diff --git a/outdated/win/gnome/gnbind.c b/outdated/win/gnome/gnbind.c index 6fb043e21..a298af5a0 100644 --- a/outdated/win/gnome/gnbind.c +++ b/outdated/win/gnome/gnbind.c @@ -44,8 +44,7 @@ struct window_procs Gnome_procs = { #ifdef CHANGE_COLOR /* only a Mac option currently */ donull, donull, #endif - /* other defs that really should go away (they're tty specific) */ - gnome_start_screen, gnome_end_screen, gnome_outrip, + gnome_outrip, genl_preference_update, genl_getmsghistory, genl_putmsghistory, genl_status_init, genl_status_finish, genl_status_enablefield, genl_status_update, @@ -1132,29 +1131,6 @@ gnome_delay_output() } } -/* -start_screen() -- Only used on Unix tty ports, but must be declared for - completeness. Sets up the tty to work in full-screen - graphics mode. Look at win/tty/termcap.c for an - example. If your window-port does not need this function - just declare an empty function. -*/ -void -gnome_start_screen() -{ - /* Do Nothing */ -} - -/* -end_screen() -- Only used on Unix tty ports, but must be declared for - completeness. The complement of start_screen(). -*/ -void -gnome_end_screen() -{ - /* Do Nothing */ -} - /* outrip(winid, int, when) -- The tombstone code. If you want the traditional code use diff --git a/outdated/win/gnome/gnbind.h b/outdated/win/gnome/gnbind.h index b0f56e035..e39a56380 100644 --- a/outdated/win/gnome/gnbind.h +++ b/outdated/win/gnome/gnbind.h @@ -80,8 +80,6 @@ void gnome_getlin(const char *question, char *input); int gnome_get_ext_cmd(void); void gnome_number_pad(int state); void gnome_delay_output(void); -void gnome_start_screen(void); -void gnome_end_screen(void); void gnome_outrip(winid wid, int how, time_t when); void gnome_delete_nhwindow_by_reference(GtkWidget *menuWin); diff --git a/sound/windsound/windsound.c b/sound/windsound/windsound.c index 8ccfe9f65..0dc486f18 100644 --- a/sound/windsound/windsound.c +++ b/sound/windsound/windsound.c @@ -55,14 +55,13 @@ windsound_init_nhsound(void) } static void -windsound_exit_nhsound(const char *reason) +windsound_exit_nhsound(const char *reason UNUSED) { } static void -windsound_achievement(schar ach1, schar ach2, int32_t repeat) +windsound_achievement(schar ach1, schar ach2, int32_t repeat UNUSED) { - int reslt = 0; const char *filename; char resourcename[120], buf[PATHLEN]; int findsound_approach = sff_base_only; @@ -108,27 +107,27 @@ windsound_achievement(schar ach1, schar ach2, int32_t repeat) filename = base_soundname_to_filename(resourcename, buf, sizeof buf, findsound_approach); if (filename) { - reslt = PlaySound(filename, NULL, fdwsound); + (void) PlaySound(filename, NULL, fdwsound); } } static void -windsound_ambience(int32_t ambienceid, int32_t ambience_action, - int32_t hero_proximity) +windsound_ambience(int32_t ambienceid UNUSED, int32_t ambience_action UNUSED, + int32_t hero_proximity UNUSED) { } static void -windsound_verbal(char *text, int32_t gender, int32_t tone, - int32_t vol, int32_t moreinfo) +windsound_verbal(char *text UNUSED, int32_t gender UNUSED, int32_t tone UNUSED, + int32_t vol UNUSED, int32_t moreinfo UNUSED) { } static void -windsound_soundeffect(char *desc, int32_t seid, int32_t volume) +windsound_soundeffect(char *desc UNUSED, int32_t seid, int32_t volume UNUSED) { #ifdef SND_SOUNDEFFECTS_AUTOMAP - int reslt = 0; +/* int reslt = 0; */ int32_t findsound_approach = sff_base_only; char buf[PATHLEN]; const char *filename; @@ -147,7 +146,7 @@ windsound_soundeffect(char *desc, int32_t seid, int32_t volume) } if (filename) { - reslt = PlaySound(filename, NULL, fdwsound); + (void) PlaySound(filename, NULL, fdwsound); } #endif } @@ -155,7 +154,7 @@ windsound_soundeffect(char *desc, int32_t seid, int32_t volume) #define WAVEMUSIC_SOUNDS static void -windsound_hero_playnotes(int32_t instrument, const char *str, int32_t volume) +windsound_hero_playnotes(int32_t instrument, const char *str, int32_t volume UNUSED) { #ifdef WAVEMUSIC_SOUNDS int reslt = 0; @@ -239,6 +238,8 @@ windsound_hero_playnotes(int32_t instrument, const char *str, int32_t volume) /* the final, or only, one is played ASYNC */ maybe_preinsert_directory(findsound_approach, exedir, buf, sizeof buf); reslt = PlaySound(buf, NULL, fdwsound); + nhUse(filename); + nhUse(reslt); #endif } diff --git a/src/.gitattributes b/src/.gitattributes index 1e3746cde..a618fdc82 100644 --- a/src/.gitattributes +++ b/src/.gitattributes @@ -1,8 +1,8 @@ * NH_filestag=(file%s_for_all_versions) -..files NH_filegenerated=Makefile,Makefile.mingw32,qt_kde0.moc,qt_main.moc,qt_map.moc,qt_menu.moc,qt_msg.moc,qt_plsel.moc,qt_set.moc,qt_stat.moc,qt_xcmd.moc,qt_yndlg.moc,tile.c,monstr.c +..files NH_filegenerated=Makefile,GNUmakefile,qt_kde0.moc,qt_main.moc,qt_map.moc,qt_menu.moc,qt_msg.moc,qt_plsel.moc,qt_set.moc,qt_stat.moc,qt_xcmd.moc,qt_yndlg.moc,tile.c,monstr.c -Makefile.mingw32 NH_filesgentag=(file%s_for_win32_that_are_moved_into_src_at_compile_time) -Makefile NH_filesgentag=>Makefile.mingw32 +GNUmakefile NH_filesgentag=(file%s_for_win32_that_are_moved_into_src_at_compile_time) +Makefile NH_filesgentag=>GNUmakefile qt_kde0.moc NH_filesgentag=(file%s_generated_by_'moc'_for_Qt_interface_at_compile_time) qt_main.moc NH_filesgentag=>qt_kde0.moc diff --git a/src/.gitignore b/src/.gitignore index 9343f331b..13097d000 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -28,6 +28,7 @@ objutil/* objlua/* objpdc/* bundle/* -Makefile.mingw32 -Makefile.mingw32.depend +GNUmakefile +GNUmakefile.depend .*.c +.depend diff --git a/src/allmain.c b/src/allmain.c index 3247b10ad..83a63b325 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 allmain.c $NHDT-Date: 1726894914 2024/09/21 05:01:54 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.261 $ */ +/* NetHack 3.7 allmain.c $NHDT-Date: 1744860497 2025/04/16 19:28:17 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.276 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -6,7 +6,6 @@ /* various code that was replicated in *main.c */ #include "hack.h" -#include #ifndef NO_SIGNAL #include @@ -100,6 +99,12 @@ moveloop_preamble(boolean resuming) u.uz0.dlevel = u.uz.dlevel; svc.context.move = 0; + /* finish processing "--debug:fuzzer" from the command line */ + if (iflags.fuzzerpending) { + iflags.debug_fuzzer = fuzzer_impossible_panic; + iflags.fuzzerpending = FALSE; + } + program_state.in_moveloop = 1; /* for perm_invent preset at startup, display persistent inventory after invent is fully populated and the in_moveloop flag has been set */ @@ -184,6 +189,9 @@ moveloop_core(void) if (iflags.sanity_check || iflags.debug_fuzzer) sanity_check(); + if (svc.context.resume_wish) + makewish(); /* clears resume_wish */ + if (svc.context.move) { /* actual time passed */ u.umovement -= NORMAL_SPEED; @@ -204,6 +212,7 @@ moveloop_core(void) struct monst *mtmp; /* set up for a new turn */ + gw.were_changes = 0L; mcalcdistress(); /* adjust monsters' trap, blind, etc */ /* reallocate movement rations to monsters; don't need @@ -327,6 +336,10 @@ moveloop_core(void) (void) dosearch0(1); if (Warning) warnreveal(); + if (gw.were_changes) { + /* update innate intrinsics (mainly Drain_resistance) */ + set_uasmon(); + } mkot_trap_warn(); dosounds(); do_storms(); @@ -412,6 +425,7 @@ moveloop_core(void) else if (u.uburied) under_ground(0); + see_nearby_monsters(); } /* actual time passed */ /****************************************/ @@ -419,6 +433,13 @@ moveloop_core(void) /****************************************/ clear_splitobjs(); + + /* the Amulet of Yendor gives a wish when initially picked up */ + if (u.uhave.amulet && !u.uevent.amulet_wish) { + u.uevent.amulet_wish = 1; + makewish(); + } + find_ac(); if (!svc.context.mv || Blind) { /* redo monsters if hallu or wearing a helm of telepathy */ @@ -685,7 +706,7 @@ init_sound_disp_gamewindows(void) WIN_MESSAGE = create_nhwindow(NHW_MESSAGE); if (VIA_WINDOWPORT()) { - status_initialize(0); + status_initialize(FALSE); } else { WIN_STATUS = create_nhwindow(NHW_STATUS); } @@ -723,6 +744,10 @@ init_sound_disp_gamewindows(void) display_nhwindow(WIN_MESSAGE, FALSE); clear_glyph_buffer(); display_nhwindow(WIN_MAP, FALSE); +#ifdef TTY_PERM_INVENT + if (iflags.perm_invent_pending) + check_perm_invent_again(); +#endif } void @@ -733,7 +758,7 @@ newgame(void) /* make sure welcome messages are given before noticing monsters */ notice_mon_off(); disp.botlx = TRUE; - svc.context.ident = 1; + svc.context.ident = 2; /* id 1 is reserved for gy.youmonst */ svc.context.warnlevel = 1; svc.context.next_attrib_check = 600L; /* arbitrary first setting */ svc.context.tribute.enabled = TRUE; /* turn on 3.6 tributes */ @@ -935,6 +960,8 @@ static const struct early_opt earlyopts[] = { { 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 @@ -1036,6 +1063,12 @@ argcheck(int argc, char *argv[], enum earlyarg e_arg) 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(); @@ -1047,6 +1080,7 @@ argcheck(int argc, char *argv[], enum earlyarg e_arg) extended_opt++; return windows_early_options(extended_opt); } + FALLTHROUGH; /*FALLTHRU*/ #endif default: @@ -1067,6 +1101,7 @@ argcheck(int argc, char *argv[], enum earlyarg e_arg) * 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) @@ -1111,6 +1146,8 @@ debug_fields(const char *opts) 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; } @@ -1136,6 +1173,7 @@ timet_delta(time_t etim, time_t stim) /* end and start times */ /* 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" }, @@ -1216,12 +1254,6 @@ dump_enums(void) arti_enum, NUM_ENUM_DUMPS }; - static const char *const titles[NUM_ENUM_DUMPS] = { - "monnums", "objects_nums" , "misc_object_nums", - "cmap_symbols", "mon_syms", "mon_defchars", - "objclass_defchars", "objclass_classes", - "objclass_syms", "artifacts_nums", - }; #define dump_om(om) { om, #om } static const struct enum_dump omdump[] = { @@ -1242,6 +1274,7 @@ dump_enums(void) 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, @@ -1251,34 +1284,37 @@ dump_enums(void) objclass_syms_dump, arti_enum_dump, }; - static const char *const pfx[NUM_ENUM_DUMPS] = { - "PM_", "", "", "", "", "", "", "", "", "" - }; - /* 0 = dump numerically only, 1 = add 'char' comment */ - static const int dumpflgs[NUM_ENUM_DUMPS] = { - 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 - }; - static int szd[NUM_ENUM_DUMPS] = { SIZE(monsdump), SIZE(objdump), - SIZE(omdump), SIZE(defsym_cmap_dump), - SIZE(defsym_mon_syms_dump), - SIZE(defsym_mon_defchars_dump), - SIZE(objclass_defchars_dump), - SIZE(objclass_classes_dump), - SIZE(objclass_syms_dump), - SIZE(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 = {", titles[i]); - for (j = 0; j < szd[i]; ++j) { - int unprefixed_count = (i == monsters_enum) ? 4 : 1; - nmprefix = (j >= szd[i] - unprefixed_count) - ? "" : pfx[i]; /* "" or "PM_" */ + 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 (dumpflgs[i] > 0) { + if (edmp[i].dumpflgs > 0) { Snprintf(comment, sizeof comment, " /* '%c' */", (ed[i][j].val >= 32 && ed[i][j].val <= 126) @@ -1296,6 +1332,7 @@ dump_enums(void) } raw_print(""); } +#undef UNPREFIXED_COUNT #endif /* NODUMPENUMS */ void diff --git a/src/alloc.c b/src/alloc.c index f8f89a952..05dfb502f 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 alloc.c $NHDT-Date: 1706213795 2024/01/25 20:16:35 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.34 $ */ +/* NetHack 3.7 alloc.c $NHDT-Date: 1737281026 2025/01/19 02:03:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.38 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -221,6 +221,10 @@ nhdupstr(const char *string, const char *file, int line) /* we've got some info about the caller, so use it instead of __func__ */ unsigned len = FITSuint_(strlen(string), file, line); + if (FITSuint_(len + 1, file, line) < len) + panic("nhdupstr: string length overflow, line %d of %s", + line, file); + return strcpy((char *) nhalloc(len + 1, file, line), string); } #undef dupstr @@ -233,11 +237,17 @@ nhdupstr(const char *string, const char *file, int line) char * dupstr(const char *string) { - unsigned len = FITSuint_(strlen(string), __func__, (int) __LINE__); + size_t len = strlen(string); + + /* make sure len+1 doesn't overflow plain unsigned (for alloc()) */ + if (len > (unsigned) (~0U - 1U)) + panic("dupstr: string length overflow"); return strcpy((char *) alloc(len + 1), string); } +#if 0 /* suppress this; if included, it will need a MONITOR_HEAP edition */ + /* similar for reasonable size strings, but return length of input as well */ char * dupstr_n(const char *string, unsigned int *lenout) @@ -245,11 +255,11 @@ dupstr_n(const char *string, unsigned int *lenout) size_t len = strlen(string); if (len >= LARGEST_INT) - panic("string too long"); + panic("dupstr_n: string too long"); *lenout = (unsigned int) len; return strcpy((char *) alloc(len + 1), string); } - +#endif /* cast to int or panic on overflow; use via macro */ int diff --git a/src/apply.c b/src/apply.c index 30d0ac36e..4e3788dd4 100644 --- a/src/apply.c +++ b/src/apply.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 apply.c $NHDT-Date: 1720128162 2024/07/04 21:22:42 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.449 $ */ +/* NetHack 3.7 apply.c $NHDT-Date: 1753856387 2025/07/29 22:19:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.472 $ */ /* 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 use_whistle(struct obj *); staticfn void use_magic_whistle(struct obj *); staticfn void magic_whistled(struct obj *); staticfn int use_leash(struct obj *); +staticfn void use_leash_core(struct obj *, struct monst *, coord *, int); staticfn boolean mleashed_next2u(struct monst *); staticfn int use_mirror(struct obj *); staticfn void use_bell(struct obj **); @@ -31,6 +32,8 @@ staticfn int touchstone_ok(struct obj *); staticfn int use_stone(struct obj *); staticfn int set_trap(void); /* occupation callback */ staticfn void display_polearm_positions(boolean); +staticfn void calc_pole_range(int *, int *); +staticfn boolean snickersnee_used_dist_attk(struct obj *); staticfn int use_cream_pie(struct obj *); staticfn int jelly_ok(struct obj *); staticfn int use_royal_jelly(struct obj **); @@ -58,12 +61,15 @@ void do_blinding_ray(struct obj *obj) { struct monst *mtmp = bhit(u.dx, u.dy, COLNO, FLASHED_LIGHT, - (int (*) (MONST_P, OBJ_P)) 0, - (int (*) (OBJ_P, OBJ_P)) 0, &obj); + (int (*) (MONST_P, OBJ_P)) 0, + (int (*) (OBJ_P, OBJ_P)) 0, &obj); obj->ox = u.ux, obj->oy = u.uy; /* flash_hits_mon() wants this */ - if (mtmp) + if (mtmp) { (void) flash_hits_mon(mtmp, obj); + if (obj->otyp == EXPENSIVE_CAMERA) + see_monster_closeup(mtmp, TRUE); /* TRUE for photo */ + } /* normally bhit() would do this but for FLASHED_LIGHT we want it to be deferred until after flash_hits_mon() */ transient_light_cleanup(); @@ -94,6 +100,7 @@ use_camera(struct obj *obj) You("take a picture of the %s.", (u.dz > 0) ? surface(u.ux, u.uy) : ceiling(u.ux, u.uy)); } else if (!u.dx && !u.dy) { + /* TODO: we ought to have a "selfie" joke here... */ (void) zapyourself(obj, TRUE); } else { do_blinding_ray(obj); @@ -227,8 +234,7 @@ its_dead(coordxy rx, coordxy ry, int *resp) /* (most corpses don't retain the monster's sex, so we're usually forced to use generic pronoun here) */ if (mtmp) { - mptr = mtmp->data = &mons[mtmp->mnum]; - /* TRUE: override visibility check--it's not on the map */ + mtmp->data = &mons[mtmp->mnum]; gndr = pronoun_gender(mtmp, PRONOUN_NO_IT); } else { mptr = &mons[corpse->corpsenm]; @@ -355,7 +361,7 @@ use_stethoscope(struct obj *obj) Soundeffect(se_faint_splashing, 35); You_hear("faint splashing."); } else if (u.dz < 0 || !can_reach_floor(TRUE)) { - cant_reach_floor(u.ux, u.uy, (u.dz < 0), TRUE); + cant_reach_floor(u.ux, u.uy, (u.dz < 0), TRUE, FALSE); } else if (its_dead(u.ux, u.uy, &res)) { ; /* message already given */ } else if (Is_stronghold(&u.uz)) { @@ -447,6 +453,7 @@ use_stethoscope(struct obj *obj) Soundeffect(se_hollow_sound, 100); You_hear(hollow_str, "door"); cvt_sdoor_to_door(lev); /* ->typ = DOOR */ + recalc_block_point(rx, ry); feel_newsym(rx, ry); return res; case SCORR: @@ -493,6 +500,8 @@ 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)) + tele_to_rnd_pet(); } else { /* it's magic! it works underwater too (at a higher pitch) */ You(Deaf ? alt_whistle_str : whistle_str, @@ -752,13 +761,11 @@ leashable(struct monst *mtmp) && (!nolimbs(mtmp->data) || has_head(mtmp->data))); } -/* ARGSUSED */ staticfn int use_leash(struct obj *obj) { coord cc; struct monst *mtmp; - int spotmon; if (u.uswallow) { /* if the leash isn't in use, assume we're trying to leash @@ -785,8 +792,8 @@ use_leash(struct obj *obj) if (u_at(cc.x, cc.y)) { if (u.usteed && u.dz > 0) { mtmp = u.usteed; - spotmon = 1; - goto got_target; + use_leash_core(obj, mtmp, &cc, 1); + return ECMD_TIME; } pline("Leash yourself? Very funny..."); return ECMD_OK; @@ -802,16 +809,20 @@ use_leash(struct obj *obj) return ECMD_TIME; } - spotmon = canspotmon(mtmp); - got_target: + use_leash_core(obj, mtmp, &cc, canspotmon(mtmp)); + return ECMD_TIME; +} - if (!spotmon && !glyph_is_invisible(levl[cc.x][cc.y].glyph)) { +staticfn void +use_leash_core(struct obj *obj, struct monst *mtmp, coord *cc, int spotmon) +{ + if (!spotmon && !glyph_is_invisible(levl[cc->x][cc->y].glyph)) { /* for the unleash case, we don't verify whether this unseen monster is the creature attached to the current leash */ You("fail to %sleash something.", obj->leashmon ? "un" : ""); /* trying again will work provided the monster is tame (and also that it doesn't change location by retry time) */ - map_invisible(cc.x, cc.y); + map_invisible(cc->x, cc->y); } else if (!mtmp->mtame) { pline("%s %s leashed!", Monnam(mtmp), (!obj->leashmon) ? "cannot be" : "is not"); @@ -829,7 +840,7 @@ use_leash(struct obj *obj) char lmonbuf[BUFSZ]; char *lmonnam = l_monnam(mtmp); - if (cc.x != mtmp->mx || cc.y != mtmp->my) { + if (cc->x != mtmp->mx || cc->y != mtmp->my) { Sprintf(lmonbuf, "%s tail", s_suffix(lmonnam)); lmonnam = lmonbuf; } @@ -858,7 +869,6 @@ use_leash(struct obj *obj) spotmon ? "your " : "", l_monnam(mtmp)); } } - return ECMD_TIME; } /* assuming mtmp->mleashed has been checked */ @@ -1959,8 +1969,8 @@ display_jump_positions(boolean on_off) tmp_at(DISP_BEAM, cmap_to_glyph(S_goodpos)); for (dx = -4; dx <= 4; dx++) for (dy = -4; dy <= 4; dy++) { - x = dx + (coordxy) u.ux; - y = dy + (coordxy) u.uy; + x = dx + u.ux; + y = dy + u.uy; if (get_valid_jump_position(x, y) && !u_at(x, y)) tmp_at(x, y); } @@ -2510,7 +2520,7 @@ figurine_location_checks(struct obj *obj, coord *cc, boolean quietly) You("cannot put the figurine there."); return FALSE; } - if (IS_ROCK(levl[x][y].typ) + if (IS_OBSTRUCTED(levl[x][y].typ) && !(passes_walls(&mons[obj->corpsenm]) && may_passwall(x, y))) { if (!quietly) You("cannot place a figurine in %s!", @@ -2827,7 +2837,7 @@ use_trap(struct obj *otmp) else if (On_stairs(u.ux, u.uy)) { stairway *stway = stairway_at(u.ux, u.uy); what = stway->isladder ? "on the ladder" : "on the stairs"; - } else if (IS_FURNITURE(levtyp) || IS_ROCK(levtyp) + } else if (IS_FURNITURE(levtyp) || IS_OBSTRUCTED(levtyp) || closed_door(u.ux, u.uy) || t_at(u.ux, u.uy)) what = "here"; else if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)) @@ -3336,15 +3346,85 @@ display_polearm_positions(boolean on_off) } } +/* + * Calculate allowable range (pole's reach is always 2 steps): + * unskilled and basic: orthogonal direction, 4..4; + * skilled: as basic, plus knight's jump position, 4..5; + * expert: as skilled, plus diagonal, 4..8. + * ...9... + * .85458. + * .52125. + * 9410149 + * .52125. + * .85458. + * ...9... + * (Note: no roles in NetHack can become expert or better + * for polearm skill; Yeoman in slash'em can become expert.) + */ +staticfn void +calc_pole_range(int *min_range, int *max_range) +{ + int typ = uwep_skill_type(); + + *min_range = 4; + if (typ == P_NONE || P_SKILL(typ) <= P_BASIC) + *max_range = 4; + else if (P_SKILL(typ) == P_SKILLED) + *max_range = 5; + else + *max_range = 8; /* (P_SKILL(typ) >= P_EXPERT) */ + + gp.polearm_range_min = *min_range; + gp.polearm_range_max = *max_range; + +} + +/* return TRUE if hero is wielding a polearm and there's + at least one monster they could hit with it */ +boolean +could_pole_mon(void) +{ + int min_range, max_range; + coord cc; + struct monst *hitm = svc.context.polearm.hitmon; + + if (!uwep || !is_pole(uwep)) + return FALSE; + + calc_pole_range(&min_range, &max_range); + + cc.x = u.ux; + cc.y = u.uy; + if (!find_poleable_mon(&cc, min_range, max_range)) { + if (hitm && !DEADMONSTER(hitm) && sensemon(hitm) + && mdistu(hitm) <= max_range && mdistu(hitm) >= min_range) + return TRUE; + } else { + return TRUE; + } + return FALSE; +} + +/* was Snickersnee used to attack at distance this turn already? */ +staticfn boolean +snickersnee_used_dist_attk(struct obj *obj) +{ + if (obj && obj == uwep && u_wield_art(ART_SNICKERSNEE) + && svc.context.snickersnee_turn == svm.moves) + return TRUE; + return FALSE; +} + /* Distance attacks by pole-weapons */ int use_pole(struct obj *obj, boolean autohit) { const char thump[] = "Thump! Your blow bounces harmlessly off the %s."; - int res = ECMD_OK, typ, max_range, min_range, glyph; + int res = ECMD_OK, max_range, min_range, glyph; coord cc; struct monst *mtmp; struct monst *hitm = svc.context.polearm.hitmon; + boolean freehit = FALSE; /* Are you allowed to use the pole? */ if (u.uswallow) { @@ -3361,32 +3441,7 @@ use_pole(struct obj *obj, boolean autohit) } /* assert(obj == uwep); */ - /* - * Calculate allowable range (pole's reach is always 2 steps): - * unskilled and basic: orthogonal direction, 4..4; - * skilled: as basic, plus knight's jump position, 4..5; - * expert: as skilled, plus diagonal, 4..8. - * ...9... - * .85458. - * .52125. - * 9410149 - * .52125. - * .85458. - * ...9... - * (Note: no roles in NetHack can become expert or better - * for polearm skill; Yeoman in slash'em can become expert.) - */ - min_range = 4; - typ = uwep_skill_type(); - if (typ == P_NONE || P_SKILL(typ) <= P_BASIC) - max_range = 4; - else if (P_SKILL(typ) == P_SKILLED) - max_range = 5; - else - max_range = 8; /* (P_SKILL(typ) >= P_EXPERT) */ - - gp.polearm_range_min = min_range; - gp.polearm_range_max = max_range; + calc_pole_range(&min_range, &max_range); /* Prompt for a location */ if (!autohit) @@ -3438,8 +3493,25 @@ use_pole(struct obj *obj, boolean autohit) if (overexertion()) return ECMD_TIME; /* burn nutrition; maybe pass out */ svc.context.polearm.hitmon = mtmp; + + if (snickersnee_used_dist_attk(obj)) { + pline_The("blade doesn't reach there!"); + return ECMD_FAIL; + } + check_caitiff(mtmp); gn.notonhead = (gb.bhitpos.x != mtmp->mx || gb.bhitpos.y != mtmp->my); + + /* Snickersnee allows one free hit from a distance per turn */ + if (obj == uwep && u_wield_art(ART_SNICKERSNEE)) { + freehit = (svm.moves != svc.context.snickersnee_turn); + svc.context.snickersnee_turn = svm.moves; + if (freehit && !Deaf) { + Soundeffect(se_sword_blade_rings, 100); + pline("Shkinng!"); /* /sha-kin!/ */ + } + } + (void) thitmonst(mtmp, uwep); } else if (glyph_is_statue(glyph) /* might be hallucinatory */ && sobj_at(STATUE, gb.bhitpos.x, gb.bhitpos.y)) { @@ -3481,7 +3553,7 @@ use_pole(struct obj *obj, boolean autohit) } } u_wipe_engr(2); /* same as for melee or throwing */ - return ECMD_TIME; + return freehit ? ECMD_OK : ECMD_TIME; } #undef glyph_is_poleable @@ -3770,6 +3842,7 @@ use_grapple(struct obj *obj) (void) thitmonst(mtmp, uwep); return ECMD_TIME; } + FALLTHROUGH; /*FALLTHRU*/ case 3: /* Surface */ if (IS_AIR(levl[cc.x][cc.y].typ) || is_pool(cc.x, cc.y)) @@ -3813,6 +3886,18 @@ broken_wand_explode(struct obj *obj, int dmg, int expltype) discard_broken_wand(); } +/* if x,y has lava or water, dunk any boulders at that location into it */ +void +maybe_dunk_boulders(coordxy x, coordxy y) +{ + struct obj *otmp; + + while (is_pool_or_lava(x, y) && (otmp = sobj_at(BOULDER, x, y)) != 0) { + obj_extract_self(otmp); + (void) boulder_hits_pool(otmp, x,y, FALSE); + } +} + /* return 1 if the wand is broken, hence some time elapsed */ staticfn int do_break_wand(struct obj *obj) @@ -3889,6 +3974,7 @@ do_break_wand(struct obj *obj) discard_broken_wand(); return ECMD_TIME; } + FALLTHROUGH; /*FALLTHRU*/ case WAN_WISHING: case WAN_NOTHING: @@ -3917,6 +4003,7 @@ do_break_wand(struct obj *obj) Soundeffect(se_wall_of_force, 65); pline("A wall of force smashes down around you!"); dmg = d(1 + obj->spe, 6); /* normally 2d12 */ + FALLTHROUGH; /*FALLTHRU*/ case WAN_CANCELLATION: case WAN_POLYMORPH: @@ -3948,8 +4035,9 @@ do_break_wand(struct obj *obj) if (obj->otyp == WAN_DIGGING) { schar typ; + enum digcheck_result dcres = dig_check(BY_OBJECT, x, y); - if (dig_check(BY_OBJECT, x, y) < DIGCHECK_FAILED) { + if (dcres < DIGCHECK_FAILED || dcres == DIGCHECK_FAIL_BOULDER) { if (IS_WALL(levl[x][y].typ) || IS_DOOR(levl[x][y].typ)) { /* normally, pits and holes don't anger guards, but they * do if it's a wall or door that's being dug */ @@ -3957,6 +4045,8 @@ do_break_wand(struct obj *obj) if (*in_rooms(x, y, SHOPBASE)) shop_damage = TRUE; } + if (levl[x][y].typ == ICE) + spot_stop_timers(x, y, MELT_ICE_AWAY); /* * Let liquid flow into the newly created pits. * Adjust corresponding code in music.c for @@ -3977,6 +4067,9 @@ do_break_wand(struct obj *obj) && !levl[x][y].candig)) ? PIT : HOLE); } } + fill_pit(x, y); + maybe_dunk_boulders(x, y); + recalc_block_point(x, y); continue; } else if (obj->otyp == WAN_CREATE_MONSTER) { /* u.ux,u.uy creates it near you--x,y might create it in rock */ @@ -4302,6 +4395,7 @@ doapply(void) pline("It rings! ... But no-one answers."); break; } + FALLTHROUGH; /*FALLTHRU*/ default: /* Pole-weapons can strike at a distance */ diff --git a/src/artifact.c b/src/artifact.c index 009dd3406..e4f4c89bf 100644 --- a/src/artifact.c +++ b/src/artifact.c @@ -7,6 +7,8 @@ #include "artifact.h" #include "artilist.h" +#ifndef SFCTOOL + /* * Note: both artilist[] and artiexist[] have a dummy element #0, * so loops over them should normally start at #1. The primary @@ -27,6 +29,17 @@ staticfn int spec_applies(const struct artifact *, struct monst *) NONNULLARG12; staticfn int invoke_ok(struct obj *); staticfn void nothing_special(struct obj *) NONNULLARG1; +staticfn int invoke_taming(struct obj *) NONNULLARG1; +staticfn int invoke_healing(struct obj *) NONNULLARG1; +staticfn int invoke_energy_boost(struct obj *) NONNULLARG1; +staticfn int invoke_untrap(struct obj *) NONNULLARG1; +staticfn int invoke_charge_obj(struct obj *) NONNULLARG1; +staticfn int invoke_create_portal(struct obj *) NONNULLARG1; +staticfn int invoke_create_ammo(struct obj *) NONNULLARG1; +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(struct obj *); staticfn boolean Mb_hit(struct monst * magr, struct monst *mdef, struct obj *, int *, int, boolean, char *); @@ -35,6 +48,7 @@ staticfn uchar abil_to_adtyp(long *) NONNULLARG1; staticfn int glow_strength(int); staticfn boolean untouchable(struct obj *, boolean); staticfn int count_surround_traps(coordxy, coordxy); +staticfn void dispose_of_orig_obj(struct obj *); /* The amount added to the victim's total hit points to insure that the victim will be killed even after damage bonus/penalty adjustments. @@ -45,31 +59,23 @@ staticfn int count_surround_traps(coordxy, coordxy); Note: this will still break if they have more than about half the number of hit points that will fit in a 15 bit integer. */ #define FATAL_DAMAGE_MODIFIER 200 +#endif /* SFCTOOL */ + +/* arti_info struct definition moved to artifact.h */ -/* artifact tracking; gift and wish imply found; it also gets set for items - seen on the floor, in containers, and wielded or dropped by monsters */ -struct arti_info { - Bitfield(exists, 1); /* 1 if corresponding artifact has been created */ - Bitfield(found, 1); /* 1 if artifact is known by hero to exist */ - Bitfield(gift, 1); /* 1 iff artifact was created as a prayer reward */ - Bitfield(wish, 1); /* 1 iff artifact was created via wish */ - Bitfield(named, 1); /* 1 iff artifact was made by naming an item */ - Bitfield(viadip, 1); /* 1 iff dipped long sword became Excalibur */ - Bitfield(lvldef, 1); /* 1 iff created by special level definition */ - Bitfield(bones, 1); /* 1 iff came from bones file */ - Bitfield(rndm, 1); /* 1 iff randomly generated */ -}; /* array of flags tracking which artifacts exist, indexed by ART_xx; ART_xx values are 1..N, element [0] isn't used; no terminator needed */ static struct arti_info artiexist[1 + NROFARTIFACTS]; /* discovery list; for N discovered artifacts, the first N entries are ART_xx values in discovery order, the remaining (NROFARTIFACTS-N) slots are 0 */ static xint16 artidisco[NROFARTIFACTS]; -/* note: artiexist[] and artidisco[] don't need to be in struct g; they +/* note: artiexist[] and artidisco[] don't need to be in struct ga; they * get explicitly initialized at game start so don't need to be part of * bulk re-init if game restart ever gets implemented. They are saved * and restored but that is done through this file so they can be local. */ + +#ifndef SFCTOOL static const struct arti_info zero_artiexist = {0}; /* all bits zero */ staticfn void hack_artifacts(void); @@ -110,22 +116,35 @@ init_artifacts(void) void save_artifacts(NHFILE *nhfp) { - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) artiexist, sizeof artiexist); - bwrite(nhfp->fd, (genericptr_t) artidisco, sizeof artidisco); - } + int i; + + for (i = 0; i < (NROFARTIFACTS + 1); ++i) + Sfo_arti_info(nhfp, &artiexist[i], "artiexist"); + + for (i = 0; i < NROFARTIFACTS; ++i) + Sfo_xint16(nhfp, &artidisco[i], "artidisco"); } +#endif /* SFCTOOL */ + void restore_artifacts(NHFILE *nhfp) { - if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) artiexist, sizeof artiexist); - mread(nhfp->fd, (genericptr_t) artidisco, sizeof artidisco); - } + int i; + + for (i = 0; i < (NROFARTIFACTS + 1); ++i) + Sfi_arti_info(nhfp, &artiexist[i], "artiexist"); + for (i = 0; i < NROFARTIFACTS; ++i) + Sfi_short(nhfp, &artidisco[i], "artidisco"); +#ifndef SFCTOOL hack_artifacts(); /* redo non-saved special cases */ +#else + nhUse(artilist); +#endif } +#ifndef SFCTOOL + const char * artiname(int artinum) { @@ -142,13 +161,18 @@ artiname(int artinum) If no alignment is given, then 'otmp' is converted into an artifact of matching type, or returned as-is if that's not possible. - For the 2nd case, caller should use ``obj = mk_artifact(obj, A_NONE);'' - for the 1st, ``obj = mk_artifact((struct obj *) 0, some_alignment);''. + For the 2nd case, caller should use ``obj = mk_artifact(obj, A_NONE, 99);'' + For the 1st, ``obj = mk_artifact((struct obj *) 0, some_alignment, ...);''. + The max_giftvalue is the value of the sacrifice, for an artifact obtained + by sacrificing, or 99 otherwise. */ struct obj * mk_artifact( - struct obj *otmp, /* existing object; ignored if alignment specified */ - aligntyp alignment) /* target alignment, or A_NONE */ + struct obj *otmp, /* existing object; ignored and disposed of + * if alignment specified */ + aligntyp alignment, /* target alignment, or A_NONE */ + uchar max_giftvalue, /* cap on generated giftvalue */ + boolean adjust_spe) /* whether to add spe to situational artifacts */ { const struct artifact *a; int m, n, altn; @@ -156,6 +180,7 @@ mk_artifact( short o_typ = (by_align || !otmp) ? 0 : otmp->otyp; boolean unique = !by_align && otmp && objects[o_typ].oc_unique; short eligible[NROFARTIFACTS]; + xint16 skill_compatibility; n = altn = 0; /* no candidates found yet */ eligible[0] = 0; /* lint suppression */ @@ -165,6 +190,8 @@ mk_artifact( continue; if ((a->spfx & SPFX_NOGEN) || unique) continue; + if (a->gift_value > max_giftvalue && !Role_if(a->role)) + continue; if (!by_align) { /* looking for a particular type of item; not producing a @@ -186,17 +213,33 @@ mk_artifact( n = 1; break; /* skip all other candidates */ } + + /* check if this is skill-compatible */ + skill_compatibility = P_SKILLED; + if (objects[a->otyp].oc_class == WEAPON_CLASS) { + schar skill = objects[a->otyp].oc_skill; + if (skill < 0) + skill_compatibility = P_MAX_SKILL(-skill); + else + skill_compatibility = P_MAX_SKILL(skill); + } + /* found something to consider for random selection */ - if (a->alignment != A_NONE || u.ugifts > 0) { + if ((a->alignment != A_NONE || u.ugifts > 0 || !rn2(3)) && + (!rn2(4) || skill_compatibility >= P_SKILLED || + (skill_compatibility >= P_BASIC && rn2(2)))) { /* right alignment, or non-aligned with at least 1 - previous gift bestowed, makes this one viable */ + previous gift bestowed, makes this one viable; + unaligned artifacts are possible even as the first + gift, but less likely; if it's a bad weapon type + for the role that also makes it less likely */ eligible[n++] = m; } else { - /* non-aligned with no previous gifts; - if no candidates have been found yet, record + /* if no candidates have been found yet, record this one as a[nother] fallback possibility in case all aligned candidates have been used up - (via wishing, naming, bones, random generation) */ + (via wishing, naming, bones, random generation) + or failed the randomized compatibility checks */ if (!n) eligible[altn++] = m; /* [once a regular candidate is found, the list @@ -215,25 +258,64 @@ mk_artifact( a = &artilist[m]; /* make an appropriate object if necessary, then christen it */ - if (by_align) - otmp = mksobj((int) a->otyp, TRUE, FALSE); + if (by_align) { + /* 'by_align' indicates that an alignment was passed as + * an argument, but also that the 'otmp' argument is not + * relevant */ + struct obj *artiobj = mksobj((int) a->otyp, TRUE, FALSE); - if (otmp) { - /* prevent erosion from generating */ - otmp->oeroded = otmp->oeroded2 = 0; - otmp = oname(otmp, a->name, ONAME_NO_FLAGS); - otmp->oartifact = m; - /* set existence and reason for creation bits */ - artifact_origin(otmp, ONAME_RANDOM); /* 'random' is default */ + /* nonnull value of 'otmp' is unexpected. Cope. */ + if (otmp) /* just in case; avoid orphaning */ + dispose_of_orig_obj(otmp); + otmp = artiobj; + } + /* + * otmp should be nonnull at this point: + * either the passed argument (if !by_align == A_NONE), or + * the result of mksobj() just above if by_align is an alignment. */ + assert(otmp != 0); + /* prevent erosion from generating */ + otmp->oeroded = otmp->oeroded2 = 0; + otmp = oname(otmp, a->name, ONAME_NO_FLAGS); + otmp->oartifact = m; /* probably already set by this point, but */ + /* set existence and reason for creation bits */ + artifact_origin(otmp, ONAME_RANDOM); /* 'random' is default */ + if (adjust_spe) { + int new_spe; + + /* Adjust artiobj->spe by a->gen_spe. (This is a no-op for + non-weapons, which always have a gen_spe of 0, and for many + weapons, too.) The result is clamped into the "normal" range to + prevent an outside chance of +12 artifacts generating. */ + new_spe = (int) otmp->spe + a->gen_spe; + if (new_spe >= -10 && new_spe < 10) + otmp->spe = new_spe; } } else { /* nothing appropriate could be found; return original object */ - if (by_align) - otmp = 0; /* (there was no original object) */ + if (by_align && otmp) { + /* (there shouldn't have been an original object). Deal with it. + * The callers that passed an alignment and a NULL otmp are + * prepared to get a potential NULL return value, so this is okay */ + dispose_of_orig_obj(otmp); + otmp = 0; + } /* otherwise, otmp has not changed; just fallthrough to return it */ } + if (otmp && permapoisoned(otmp)) + otmp->opoisoned = 1; return otmp; } +staticfn void +dispose_of_orig_obj(struct obj *obj) +{ + if (!obj) + return; + + obj_extract_self(obj); + obfree(obj, (struct obj *) 0); +} + /* * Returns the full name (with articles and correct capitalization) of an * artifact named "name" if one exists, or NULL, it not. @@ -629,7 +711,10 @@ protects(struct obj *otmp, boolean being_worn) * unworn/unwielded/dropped. Pickup/drop only set/reset the W_ART mask. */ void -set_artifact_intrinsic(struct obj *otmp, boolean on, long wp_mask) +set_artifact_intrinsic( + struct obj *otmp, + boolean on, + long wp_mask) { long *mask = 0; const struct artifact *art, *oart = get_artifact(otmp); @@ -796,6 +881,13 @@ set_artifact_intrinsic(struct obj *otmp, boolean on, long wp_mask) && (u.uprops[oart->inv_prop].extrinsic & W_ARTI)) (void) arti_invoke(otmp); } + + if (wp_mask == W_WEP && is_art(otmp, ART_SUNSWORD)) { + if (on) + EBlnd_resist |= wp_mask; + else + EBlnd_resist &= ~wp_mask; + } } /* touch_artifact()'s return value isn't sufficient to tell whether it @@ -1592,9 +1684,7 @@ artifact_hit( healup(drain, 0, FALSE, FALSE); } else { assert(magr != 0); - magr->mhp += drain; - if (magr->mhp > magr->mhpmax) - magr->mhp = magr->mhpmax; + healmon(magr, drain, 0); } } return vis; @@ -1620,9 +1710,7 @@ artifact_hit( } losexp("life drainage"); if (magr && magr->mhp < magr->mhpmax) { - magr->mhp += (abs(oldhpmax - u.uhpmax) + 1) / 2; - if (magr->mhp > magr->mhpmax) - magr->mhp = magr->mhpmax; + healmon(magr, (abs(oldhpmax - u.uhpmax) + 1) / 2, 0); } return TRUE; } @@ -1675,10 +1763,331 @@ nothing_special(struct obj *obj) You_feel("a surge of power, but nothing seems to happen."); } +staticfn int +invoke_taming(struct obj *obj UNUSED) +{ + struct obj pseudo; + + pseudo = cg.zeroobj; /* neither cursed nor blessed, zero oextra too */ + pseudo.otyp = SCR_TAMING; + (void) seffects(&pseudo); + return ECMD_TIME; +} + +staticfn int +invoke_healing(struct obj *obj) +{ + int healamt = (u.uhpmax + 1 - u.uhp) / 2; + long creamed = (long) u.ucreamed; + + if (Upolyd) + healamt = (u.mhmax + 1 - u.mh) / 2; + if (healamt || Sick || Slimed || Blinded > creamed) + You_feel("better."); + if (healamt || Sick || Slimed || BlindedTimeout > creamed) + You_feel("%sbetter.", + (!healamt && !Sick && !Slimed + /* when healing temporary blindness (aside from + goop covering face), might still be blind + due to PermaBlind or eyeless polymorph; + vary the message in that situation */ + && (HBlinded & ~TIMEOUT) != 0L) ? "slightly " : ""); + else { + nothing_special(obj); + return ECMD_TIME; + } + if (healamt > 0) { + if (Upolyd) + u.mh += healamt; + else + u.uhp += healamt; + } + if (Sick) + make_sick(0L, (char *) 0, FALSE, SICK_ALL); + if (Slimed) + make_slimed(0L, (char *) 0); + if (BlindedTimeout > creamed) + make_blinded(creamed, FALSE); + disp.botl = TRUE; + return ECMD_TIME; +} + +staticfn int +invoke_energy_boost(struct obj *obj) +{ + int epboost = (u.uenmax + 1 - u.uen) / 2; + + if (epboost > 120) + epboost = 120; /* arbitrary */ + else if (epboost < 12) + epboost = u.uenmax - u.uen; + if (epboost) { + u.uen += epboost; + disp.botl = TRUE; + You_feel("re-energized."); + } else { + nothing_special(obj); + return ECMD_TIME; + } + return ECMD_TIME; +} + +staticfn int +invoke_untrap(struct obj *obj) +{ + if (!untrap(TRUE, 0, 0, (struct obj *) 0)) { + obj->age = 0; /* don't charge for changing their mind */ + return ECMD_CANCEL; + } + return ECMD_TIME; +} + +staticfn int +invoke_charge_obj(struct obj *obj) +{ + const struct artifact *oart = get_artifact(obj); + struct obj *otmp = getobj("charge", charge_ok, + GETOBJ_PROMPT | GETOBJ_ALLOWCNT); + boolean b_effect; + + if (!otmp) { + obj->age = 0; + return ECMD_CANCEL; + } + b_effect = (obj->blessed && (oart->role == Role_switch + || oart->role == NON_PM)); + recharge(otmp, b_effect ? 1 : obj->cursed ? -1 : 0); + update_inventory(); + return ECMD_TIME; +} + +staticfn int +invoke_create_portal(struct obj *obj) +{ + int i, num_ok_dungeons, last_ok_dungeon = 0; + d_level newlev; + winid tmpwin = create_nhwindow(NHW_MENU); + anything any; + int clr = NO_COLOR; + + any = cg.zeroany; /* set all bits to zero */ + start_menu(tmpwin, MENU_BEHAVE_STANDARD); + /* use index+1 (can't use 0) as identifier */ + for (i = num_ok_dungeons = 0; i < svn.n_dgns; i++) { + if (!svd.dungeons[i].dunlev_ureached) + continue; + if (i == tutorial_dnum) /* can't portal into tutorial */ + continue; + any.a_int = i + 1; + add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, + ATR_NONE, clr, + svd.dungeons[i].dname, MENU_ITEMFLAGS_NONE); + num_ok_dungeons++; + last_ok_dungeon = i; + } + end_menu(tmpwin, "Open a portal to which dungeon?"); + if (num_ok_dungeons > 1) { + /* more than one entry; display menu for choices */ + menu_item *selected; + int n; + + n = select_menu(tmpwin, PICK_ONE, &selected); + if (n <= 0) { + destroy_nhwindow(tmpwin); + nothing_special(obj); + return ECMD_TIME; + } + i = selected[0].item.a_int - 1; + free((genericptr_t) selected); + } else + i = last_ok_dungeon; /* also first & only OK dungeon */ + destroy_nhwindow(tmpwin); + + /* + * i is now index into dungeon structure for the new dungeon. + * Find the closest level in the given dungeon, open + * a use-once portal to that dungeon and go there. + * The closest level is either the entry or dunlev_ureached. + */ + newlev.dnum = i; + if (svd.dungeons[i].depth_start >= depth(&u.uz)) + newlev.dlevel = svd.dungeons[i].entry_lev; + else + newlev.dlevel = svd.dungeons[i].dunlev_ureached; + + if (u.uhave.amulet || In_endgame(&u.uz) || In_endgame(&newlev) + || newlev.dnum == u.uz.dnum || !next_to_u()) { + You_feel("very disoriented for a moment."); + } else { + if (!Blind) + You("are surrounded by a shimmering sphere!"); + else + You_feel("weightless for a moment."); + goto_level(&newlev, FALSE, FALSE, FALSE); + } + return ECMD_TIME; +} + +staticfn int +invoke_create_ammo(struct obj *obj) +{ + struct obj *otmp = mksobj(ARROW, TRUE, FALSE); + + if (!otmp) { + nothing_special(obj); + return ECMD_TIME; + } + otmp->blessed = obj->blessed; + otmp->cursed = obj->cursed; + otmp->bknown = obj->bknown; + otmp->oeroded = otmp->oeroded2 = 0; + if (obj->blessed) { + if (otmp->spe < 0) + otmp->spe = 0; + otmp->quan += rnd(10); + } else if (obj->cursed) { + if (otmp->spe > 0) + otmp->spe = 0; + } else + otmp->quan += rnd(5); + otmp->owt = weight(otmp); + otmp = hold_another_object(otmp, "Suddenly %s out.", + aobjnam(otmp, "fall"), (char *) 0); + nhUse(otmp); + return ECMD_TIME; +} + +staticfn int +invoke_banish(struct obj *obj UNUSED) +{ + int nvanished = 0, nstayed = 0; + struct monst *mtmp, *mtmp2; + d_level dest; + + find_hell(&dest); + + for (mtmp = fmon; mtmp; mtmp = mtmp2) { + int chance = 1; + + mtmp2 = mtmp->nmon; + if (DEADMONSTER(mtmp) || !isok(mtmp->mx, mtmp->my)) + continue; + if (!is_demon(mtmp->data) && mtmp->data->mlet != S_IMP) + continue; + if (!couldsee(mtmp->mx, mtmp->my)) + continue; + if (mtmp->data->msound == MS_NEMESIS) + continue; + + if (In_quest(&u.uz) && !svq.quest_status.killed_nemesis) + chance += 10; + if (is_dprince(mtmp->data)) + chance += 2; + if (is_dlord(mtmp->data)) + chance++; + + mtmp->msleeping = mtmp->mtame = mtmp->mpeaceful = 0; + if (chance <= 1 || !rn2(chance)) { + if (!Inhell) { + nvanished++; + /* banish to a random level in Gehennom */ + dest.dlevel = rn2(dunlevs_in_dungeon(&dest)); + migrate_mon(mtmp, ledger_no(&dest), MIGR_RANDOM); + } else { + u_teleport_mon(mtmp, FALSE); + } + } else { + nstayed++; + } + } + + if (nvanished) { + char subject[] = "demons"; + + if (nvanished == 1) + *(eos(subject) - 1) = '\0'; /* remove 's' */ + pline("%s %s %s in a cloud of brimstone!", + nstayed ? ((nvanished > nstayed) + ? "Most of the" + : "Some of the") + : "The", + subject, vtense(subject, "disappear")); + } + return ECMD_TIME; +} + +staticfn int +invoke_fling_poison(struct obj *obj) +{ + if (getdir((char *) 0)) { + int venom = rn2(2) ? BLINDING_VENOM : ACID_VENOM; + struct obj *otmp = mksobj(venom, TRUE, FALSE); + + otmp->spe = 1; /* the poison is yours */ + throwit(otmp, 0L, FALSE, (struct obj *) 0); + } else { + /* no direction picked */ + pline("%s", Never_mind); + obj->age = svm.moves; + return ECMD_CANCEL; + } + return ECMD_TIME; +} + +staticfn int +invoke_storm_spell(struct obj *obj) +{ + const struct artifact *oart = get_artifact(obj); + int storm = oart->inv_prop == SNOWSTORM ? SPE_CONE_OF_COLD : SPE_FIREBALL; + int skill = spell_skilltype(storm); + int expertise = P_SKILL(skill); + + P_SKILL(skill) = P_EXPERT; + (void) spelleffects(storm, FALSE, TRUE); + P_SKILL(skill) = expertise; + return ECMD_TIME; +} + +staticfn int +invoke_blinding_ray(struct obj *obj) +{ + if (getdir((char *) 0)) { + if (u.dx || u.dy) { + do_blinding_ray(obj); + } else if (u.dz) { + /* up or down => light this map spot; litroom() uses + radius 0 for Sunsword, except on Rogue level where + whole room gets lit and corridor spots remain unlit */ + litroom(TRUE, obj); + pline("%s", ((!Blind && levl[u.ux][u.uy].lit + && !levl[u.ux][u.uy].waslit) + ? "It is lit here now." + : nothing_seems_to_happen)); + } else { /* zapyourself() */ + boolean vulnerable = (u.umonnum == PM_GREMLIN); + int damg = obj->blessed ? 15 : !obj->cursed ? 10 : 5; + + if (vulnerable) /* could be fatal if Unchanging */ + (void) lightdamage(obj, TRUE, 2 * damg); + + if (!flashburn((long) (damg + rnd(damg)), FALSE) + && !vulnerable) + pline("%s", nothing_seems_to_happen); + } + } else { + /* no direction picked */ + pline("%s", Never_mind); + obj->age = svm.moves; + return ECMD_CANCEL; + } + return ECMD_TIME; +} + staticfn int arti_invoke(struct obj *obj) { const struct artifact *oart; + int res = ECMD_OK; if (!obj) { impossible("arti_invoke without obj"); @@ -1706,275 +2115,29 @@ arti_invoke(struct obj *obj) obj->age = svm.moves + rnz(100); switch (oart->inv_prop) { - case TAMING: { - struct obj pseudo; - - pseudo = - cg.zeroobj; /* neither cursed nor blessed, zero oextra too */ - pseudo.otyp = SCR_TAMING; - (void) seffects(&pseudo); - break; - } - case HEALING: { - int healamt = (u.uhpmax + 1 - u.uhp) / 2; - long creamed = (long) u.ucreamed; - - if (Upolyd) - healamt = (u.mhmax + 1 - u.mh) / 2; - if (healamt || Sick || Slimed || Blinded > creamed) - You_feel("better."); - if (healamt || Sick || Slimed || BlindedTimeout > creamed) - You_feel("%sbetter.", - (!healamt && !Sick && !Slimed - /* when healing temporary blindness (aside from - goop covering face), might still be blind - due to PermaBlind or eyeless polymorph; - vary the message in that situation */ - && (HBlinded & ~TIMEOUT) != 0L) ? "slightly " : ""); - else { - nothing_special(obj); - return ECMD_TIME; - } - if (healamt > 0) { - if (Upolyd) - u.mh += healamt; - else - u.uhp += healamt; - } - if (Sick) - make_sick(0L, (char *) 0, FALSE, SICK_ALL); - if (Slimed) - make_slimed(0L, (char *) 0); - if (BlindedTimeout > creamed) - make_blinded(creamed, FALSE); - disp.botl = TRUE; - break; - } - case ENERGY_BOOST: { - int epboost = (u.uenmax + 1 - u.uen) / 2; - - if (epboost > 120) - epboost = 120; /* arbitrary */ - else if (epboost < 12) - epboost = u.uenmax - u.uen; - if (epboost) { - u.uen += epboost; - disp.botl = TRUE; - You_feel("re-energized."); - } else { - nothing_special(obj); - return ECMD_TIME; - } - break; - } - case UNTRAP: { - if (!untrap(TRUE, 0, 0, (struct obj *) 0)) { - obj->age = 0; /* don't charge for changing their mind */ - return ECMD_OK; - } - break; - } - case CHARGE_OBJ: { - struct obj *otmp = getobj("charge", charge_ok, - GETOBJ_PROMPT | GETOBJ_ALLOWCNT); - boolean b_effect; - - if (!otmp) { - obj->age = 0; - return ECMD_CANCEL; - } - b_effect = (obj->blessed && (oart->role == Role_switch - || oart->role == NON_PM)); - recharge(otmp, b_effect ? 1 : obj->cursed ? -1 : 0); - update_inventory(); - break; - } - case LEV_TELE: - level_tele(); - break; - case CREATE_PORTAL: { - int i, num_ok_dungeons, last_ok_dungeon = 0; - d_level newlev; - winid tmpwin = create_nhwindow(NHW_MENU); - anything any; - int clr = NO_COLOR; - - any = cg.zeroany; /* set all bits to zero */ - start_menu(tmpwin, MENU_BEHAVE_STANDARD); - /* use index+1 (can't use 0) as identifier */ - for (i = num_ok_dungeons = 0; i < svn.n_dgns; i++) { - if (!svd.dungeons[i].dunlev_ureached) - continue; - if (i == tutorial_dnum) /* can't portal into tutorial */ - continue; - any.a_int = i + 1; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, - svd.dungeons[i].dname, MENU_ITEMFLAGS_NONE); - num_ok_dungeons++; - last_ok_dungeon = i; - } - end_menu(tmpwin, "Open a portal to which dungeon?"); - if (num_ok_dungeons > 1) { - /* more than one entry; display menu for choices */ - menu_item *selected; - int n; - - n = select_menu(tmpwin, PICK_ONE, &selected); - if (n <= 0) { - destroy_nhwindow(tmpwin); - nothing_special(obj); - return ECMD_TIME; - } - i = selected[0].item.a_int - 1; - free((genericptr_t) selected); - } else - i = last_ok_dungeon; /* also first & only OK dungeon */ - destroy_nhwindow(tmpwin); - - /* - * i is now index into dungeon structure for the new dungeon. - * Find the closest level in the given dungeon, open - * a use-once portal to that dungeon and go there. - * The closest level is either the entry or dunlev_ureached. - */ - newlev.dnum = i; - if (svd.dungeons[i].depth_start >= depth(&u.uz)) - newlev.dlevel = svd.dungeons[i].entry_lev; - else - newlev.dlevel = svd.dungeons[i].dunlev_ureached; - - if (u.uhave.amulet || In_endgame(&u.uz) || In_endgame(&newlev) - || newlev.dnum == u.uz.dnum || !next_to_u()) { - You_feel("very disoriented for a moment."); - } else { - if (!Blind) - You("are surrounded by a shimmering sphere!"); - else - You_feel("weightless for a moment."); - goto_level(&newlev, FALSE, FALSE, FALSE); - } - break; - } + case TAMING: res = invoke_taming(obj); break; + case HEALING: res = invoke_healing(obj); break; + case ENERGY_BOOST: res = invoke_energy_boost(obj); break; + case UNTRAP: res = invoke_untrap(obj); break; + case CHARGE_OBJ: res = invoke_charge_obj(obj); break; + case LEV_TELE: level_tele(); res = ECMD_TIME; break; + case CREATE_PORTAL: res = invoke_create_portal(obj); break; case ENLIGHTENING: enlightenment(MAGICENLIGHTENMENT, ENL_GAMEINPROGRESS); + res = ECMD_TIME; break; - case CREATE_AMMO: { - struct obj *otmp = mksobj(ARROW, TRUE, FALSE); - - if (!otmp) { - nothing_special(obj); - return ECMD_TIME; - } - otmp->blessed = obj->blessed; - otmp->cursed = obj->cursed; - otmp->bknown = obj->bknown; - otmp->oeroded = otmp->oeroded2 = 0; - if (obj->blessed) { - if (otmp->spe < 0) - otmp->spe = 0; - otmp->quan += rnd(10); - } else if (obj->cursed) { - if (otmp->spe > 0) - otmp->spe = 0; - } else - otmp->quan += rnd(5); - otmp->owt = weight(otmp); - otmp = hold_another_object(otmp, "Suddenly %s out.", - aobjnam(otmp, "fall"), (char *) 0); - nhUse(otmp); - break; - } - case BANISH: { - int nvanished = 0, nstayed = 0; - struct monst *mtmp, *mtmp2; - d_level dest; - - find_hell(&dest); - - for (mtmp = fmon; mtmp; mtmp = mtmp2) { - int chance = 1; - - mtmp2 = mtmp->nmon; - if (DEADMONSTER(mtmp) || !isok(mtmp->mx, mtmp->my)) - continue; - if (!is_demon(mtmp->data) && mtmp->data->mlet != S_IMP) - continue; - if (!couldsee(mtmp->mx, mtmp->my)) - continue; - if (mtmp->data->msound == MS_NEMESIS) - continue; - - if (In_quest(&u.uz) && !svq.quest_status.killed_nemesis) - chance += 10; - if (is_dprince(mtmp->data)) - chance += 2; - if (is_dlord(mtmp->data)) - chance++; - - mtmp->msleeping = mtmp->mtame = mtmp->mpeaceful = 0; - if (chance <= 1 || !rn2(chance)) { - if (!Inhell) { - nvanished++; - /* banish to a random level in Gehennom */ - dest.dlevel = rn2(dunlevs_in_dungeon(&dest)); - migrate_mon(mtmp, ledger_no(&dest), MIGR_RANDOM); - } else { - u_teleport_mon(mtmp, FALSE); - } - } else { - nstayed++; - } - } - - if (nvanished) { - char subject[] = "demons"; - - if (nvanished == 1) - *(eos(subject) - 1) = '\0'; /* remove 's' */ - pline("%s %s %s in a cloud of brimstone!", - nstayed ? ((nvanished > nstayed) - ? "Most of the" - : "Some of the") - : "The", - subject, vtense(subject, "disappear")); - } - break; - } - case BLINDING_RAY: - if (getdir((char *) 0)) { - if (u.dx || u.dy) { - do_blinding_ray(obj); - } else if (u.dz) { - /* up or down => light this map spot; litroom() uses - radius 0 for Sunsword, except on Rogue level where - whole room gets lit and corridor spots remain unlit */ - litroom(TRUE, obj); - pline("%s", ((!Blind && levl[u.ux][u.uy].lit - && !levl[u.ux][u.uy].waslit) - ? "It is lit here now." - : nothing_seems_to_happen)); - } else { /* zapyourself() */ - boolean vulnerable = (u.umonnum == PM_GREMLIN); - int damg = obj->blessed ? 15 : !obj->cursed ? 10 : 5; - - if (vulnerable) /* could be fatal if Unchanging */ - (void) lightdamage(obj, TRUE, 2 * damg); - - if (!flashburn((long) (damg + rnd(damg)), FALSE) - && !vulnerable) - pline("%s", nothing_seems_to_happen); - } - } else { - /* no direction picked */ - pline("%s", Never_mind); - obj->age = svm.moves; - } - break; + case CREATE_AMMO: res = invoke_create_ammo(obj); break; + case BANISH: res = invoke_banish(obj); break; + case FLING_POISON: res = invoke_fling_poison(obj); break; + case SNOWSTORM: + /*FALLTHRU*/ + case FIRESTORM: res = invoke_storm_spell(obj); break; + case BLINDING_RAY: res = invoke_blinding_ray(obj); break; default: impossible("Unknown invoke power %d.", oart->inv_prop); break; } + return res; } else { long eprop = (u.uprops[oart->inv_prop].extrinsic ^= W_ARTI), iprop = u.uprops[oart->inv_prop].intrinsic; @@ -2210,6 +2373,10 @@ what_gives(long *abil) if ((art->spfx & spfx) == spfx && obj->owornmask) return obj; } + if (obj == uwep && abil == &EBlnd_resist + && (*abil & W_WEP) != 0L) { + return obj; /* Sunsword */ + } } } else { if (wornbits && wornbits == (wornmask & obj->owornmask)) @@ -2627,4 +2794,13 @@ get_artifact(struct obj *obj) } return &artilist[ART_NONARTIFACT]; } + +/* is object permanently poisoned? (currently only Grimtooth) */ +boolean +permapoisoned(struct obj *obj) +{ + return (obj && is_art(obj, ART_GRIMTOOTH)); +} +#endif /* SFCTOOL */ + /*artifact.c*/ diff --git a/src/attrib.c b/src/attrib.c index 859b5b44a..fc6a7aebf 100644 --- a/src/attrib.c +++ b/src/attrib.c @@ -1,11 +1,10 @@ -/* NetHack 3.7 attrib.c $NHDT-Date: 1726168587 2024/09/12 19:16:27 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.129 $ */ +/* NetHack 3.7 attrib.c $NHDT-Date: 1754979443 2025/08/11 22:17:23 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.134 $ */ /* Copyright 1988, 1989, 1990, 1992, M. Stephenson */ /* NetHack may be freely redistributed. See license for details. */ /* attribute modification routines. */ #include "hack.h" -#include /* part of the output on gain or loss of attribute */ static const char @@ -702,7 +701,9 @@ init_attr_role_redist(int np, boolean addition) while ((addition ? (np > 0) : (np < 0)) && tryct < 100) { int i = rnd_attr(); - if (i >= A_MAX || ABASE(i) >= ATTRMAX(i)) { + if (i >= A_MAX + || (addition ? (ABASE(i) >= ATTRMAX(i)) + : (ABASE(i) <= ATTRMIN(i)))) { tryct++; continue; } @@ -889,7 +890,8 @@ is_innate(int propidx) ignore innateness if equipment is going to claim responsibility */ && !u.uprops[propidx].extrinsic) return FROM_ROLE; - if (propidx == BLINDED && !haseyes(gy.youmonst.data)) + if ((propidx == BLINDED && !haseyes(gy.youmonst.data)) + || (propidx == BLND_RES && (HBlnd_resist & FROMFORM) != 0)) return FROM_FORM; return FROM_NONE; } @@ -897,7 +899,8 @@ is_innate(int propidx) DISABLE_WARNING_FORMAT_NONLITERAL char * -from_what(int propidx) /* special cases can have negative values */ +from_what( + int propidx) /* special cases can have negative values */ { static char buf[BUFSZ]; @@ -939,7 +942,7 @@ from_what(int propidx) /* special cases can have negative values */ else if (innateness == FROM_LYCN) Strcpy(buf, " due to your lycanthropy"); else if (innateness == FROM_FORM) - Strcpy(buf, " from current creature form"); + Strcpy(buf, " from your creature form"); else if (propidx == FAST && Very_fast) Sprintf(buf, because_of, ((HFast & TIMEOUT) != 0L) ? "a potion or spell" diff --git a/src/ball.c b/src/ball.c index 1ff2cc339..ebacb7e83 100644 --- a/src/ball.c +++ b/src/ball.c @@ -609,7 +609,7 @@ drag_ball(coordxy x, coordxy y, int *bc_control, (distmin(x, y, chx, chy) <= 1 \ && distmin(chx, chy, uball->ox, uball->oy) <= 1) #define IS_CHAIN_ROCK(x, y) \ - (IS_ROCK(levl[x][y].typ) \ + (IS_OBSTRUCTED(levl[x][y].typ) \ || (IS_DOOR(levl[x][y].typ) \ && (levl[x][y].doormask & (D_CLOSED | D_LOCKED)))) /* @@ -743,7 +743,8 @@ drag_ball(coordxy x, coordxy y, int *bc_control, SKIP_TO_DRAG; break; } - /* fall through */ + FALLTHROUGH; + /* FALLTHRU */ case 1: case 0: /* do nothing if possible */ diff --git a/src/bones.c b/src/bones.c index 963c6d8f9..5afb0cbcd 100644 --- a/src/bones.c +++ b/src/bones.c @@ -5,6 +5,7 @@ #include "hack.h" +#ifndef SFCTOOL staticfn boolean no_bones_level(d_level *); staticfn void goodfruit(int); staticfn void resetobjs(struct obj *, boolean); @@ -105,6 +106,7 @@ resetobjs(struct obj *ochain, boolean restore) otmp->rknown = 0; otmp->lknown = 0; otmp->cknown = 0; + otmp->tknown = 0; otmp->invlet = 0; otmp->no_charge = 0; otmp->how_lost = LOST_NONE; @@ -444,6 +446,7 @@ savebones(int how, time_t when, struct obj *corpse) iter_mons(remove_mon_from_bones); /* send various unique monsters away, */ dmonsfree(); /* then discard dead or gone monsters */ + forget_engravings(); /* next hero won't have read any engravings yet */ /* mark all named fruits as nonexistent; if/when we come to instances of any of them we'll mark those as existing (using goodfruit()) */ for (f = gf.ffruit; f; f = f->nextf) @@ -500,10 +503,40 @@ savebones(int how, time_t when, struct obj *corpse) (void) obj_attach_mid(corpse, mtmp->m_id); } if (mtmp) { + int i; + mtmp->m_lev = (u.ulevel ? u.ulevel : 1); mtmp->mhp = mtmp->mhpmax = u.uhpmax; mtmp->female = flags.female; mtmp->msleeping = 1; + + if (!has_ebones(mtmp)) + newebones(mtmp); + if (has_ebones(mtmp)) { + for (i = 0; i <= NUM_ROLES; ++i) { + if (!strcmp(gu.urole.name.m, roles[i].name.m)) { + EBONES(mtmp)->role = i; + break; + } + /* impossible("savebones: bad gu.urole.name.m \"%s\"", + gu.urole.name.m); */ + } + for (i = 0; i <= NUM_RACES; ++i) { + if (!strcmp(gu.urace.noun, races[i].noun)) { + EBONES(mtmp)->race = i; + break; + } + /* impossible("savebones: bad gu.urace.noun \"%s\"", + gu.urace.noun); */ + } + EBONES(mtmp)->oldalign = u.ualign; + EBONES(mtmp)->deathlevel = u.ulevel; + EBONES(mtmp)->luck = u.uluck; /* moreluck not included */ + EBONES(mtmp)->mnum = Role_switch; + EBONES(mtmp)->female = flags.female; + EBONES(mtmp)->demigod = u.uevent.udemigod; + EBONES(mtmp)->crowned = u.uevent.uhand_of_elbereth; + } } for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { set_ghostly_objlist(mtmp->minvent); @@ -578,14 +611,11 @@ savebones(int how, time_t when, struct obj *corpse) nhfp->mode = WRITING; store_version(nhfp); - store_savefileinfo(nhfp); - if (nhfp->structlevel) { - /* if a bones pool digit is in use, it precedes the bonesid - string and isn't recorded in the file */ - bwrite(nhfp->fd, (genericptr_t) &c, sizeof c); - bwrite(nhfp->fd, (genericptr_t) bonesid, (unsigned) c); /* DD.nn */ - savefruitchn(nhfp); - } + /* if a bones pool digit is in use, it precedes the bonesid + string and isn't recorded in the file */ + Sfo_char(nhfp, &c, "bones_count", 1); + Sfo_char(nhfp, bonesid, "bonesid", (int) c); /* DD.nnn */ + savefruitchn(nhfp); update_mlstmv(); /* update monsters for eventual restoration */ savelev(nhfp, ledger_no(&u.uz)); close_nhfile(nhfp); @@ -593,6 +623,8 @@ savebones(int how, time_t when, struct obj *corpse) compress_bonesfile(); } +#endif /* !SFCTOOL */ + int getbones(void) { @@ -601,6 +633,7 @@ getbones(void) char c = 0, *bonesid, oldbonesid[40] = { 0 }; /* was [10]; more should be safer */ +#ifndef SFCTOOL if (discover) /* save bones files for real games */ return 0; @@ -612,6 +645,7 @@ getbones(void) return 0; if (no_bones_level(&u.uz)) return 0; +#endif /* !SFCTOOL */ nhfp = open_bonesfile(&u.uz, &bonesid); if (!nhfp) @@ -623,7 +657,7 @@ getbones(void) return 0; } - if (validate(nhfp, gb.bones, FALSE) != 0) { + if (validate(nhfp, gb.bones, FALSE) != SF_UPTODATE) { if (!wizard) pline("Discarding unusable bones; no need to panic..."); ok = FALSE; @@ -636,14 +670,9 @@ getbones(void) return 0; } } - if (nhfp->structlevel) { - /* if a bones pool digit is in use, it precedes the bonesid - string and wasn't recorded in the file */ - mread(nhfp->fd, (genericptr_t) &c, - sizeof c); /* length including terminating '\0' */ + Sfi_char(nhfp, &c, "bones_count", 1); /* length incl. '\0' */ if ((unsigned) c <= sizeof oldbonesid) { - mread(nhfp->fd, (genericptr_t) oldbonesid, - (unsigned) c); /* DD.nn or Qrrr.n for role rrr */ + Sfi_char(nhfp, oldbonesid, "bonesid", (int) c); } else { if (wizard) debugpline2("Abandoning bones , %u > %u.", @@ -653,7 +682,6 @@ getbones(void) /* ToDo: maybe unlink these problematic bones? */ return 0; } - } if (strcmp(bonesid, oldbonesid) != 0) { char errbuf[BUFSZ]; @@ -717,6 +745,8 @@ getbones(void) return ok; } +#ifndef SFCTOOL + /* check whether current level contains bones from a particular player */ boolean bones_include_name(const char *name) @@ -774,4 +804,30 @@ fix_ghostly_obj(struct obj *obj) obj->ghostly = 0; } +void +newebones(struct monst *mtmp) +{ + if (!mtmp->mextra) + mtmp->mextra = newmextra(); + if (!EBONES(mtmp)) { + EBONES(mtmp) = (struct ebones *) alloc( + sizeof (struct ebones)); + (void) memset((genericptr_t) EBONES(mtmp), 0, + sizeof (struct ebones)); + EBONES(mtmp)->parentmid = mtmp->m_id; + } +} + +/* this is not currently used */ +void +free_ebones(struct monst *mtmp) +{ + if (mtmp->mextra && EBONES(mtmp)) { + free((genericptr_t) EBONES(mtmp)); + EBONES(mtmp) = (struct ebones *) 0; + } +} + +#endif /* SFCTOOL */ + /*bones.c*/ diff --git a/src/botl.c b/src/botl.c index 51f15f19d..f3ad98aa5 100644 --- a/src/botl.c +++ b/src/botl.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 botl.c $NHDT-Date: 1720397739 2024/07/08 00:15:39 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.264 $ */ +/* NetHack 3.7 botl.c $NHDT-Date: 1742207239 2025/03/17 02:27:19 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.274 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -422,7 +422,7 @@ max_rank_sz(void) long botl_score(void) { - long deepest = deepest_lev_reached(FALSE); + long deepest = (long) deepest_lev_reached(FALSE); long umoney, depthbonus; /* hidden_gold(False): only gold in containers whose contents are known */ @@ -430,10 +430,10 @@ botl_score(void) /* don't include initial gold; don't impose penalty if it's all gone */ if ((umoney -= u.umoney0) < 0L) umoney = 0L; - depthbonus = 50 * (deepest - 1) - + (deepest > 30) ? 10000 - : (deepest > 20) ? 1000 * (deepest - 20) - : 0; + depthbonus = (50L * (deepest - 1L)) + + ((deepest > 30L) ? 10000L + : (deepest > 20L) ? (1000L * (deepest - 20L)) + : 0L); /* neither umoney nor depthbonus can grow unusually big (gold due to weight); u.urexp might */ return nowrap_add(u.urexp, umoney + depthbonus); @@ -1325,7 +1325,10 @@ eval_notify_windowport_field( reset = FALSE; #ifdef STATUS_HILITES - if (!gu.update_all && !chg && curr->time) { + if (gu.update_all) { + chg = 0; + curr->time = prev->time = 0L; + } else if (!chg && curr->time) { reset = hilite_reset_needed(prev, gb.bl_hilite_moves); if (reset) curr->time = prev->time = 0L; @@ -1949,7 +1952,7 @@ static const struct fieldid_t { { "xp", BL_EXP }, { "exp", BL_EXP }, { "flags", BL_CONDITION }, - {0, BL_FLUSH } + { NULL, BL_FLUSH } }; /* format arguments */ @@ -2024,8 +2027,8 @@ status_eval_next_unhilite(void) long next_unhilite, this_unhilite; gb.bl_hilite_moves = svm.moves; /* simplified; at one point we used to - * try to encode fractional amounts for - * multiple moves within same turn */ + * try to encode fractional amounts for + * multiple moves within same turn */ /* figure out whether an unhilight needs to be performed now */ next_unhilite = 0L; for (i = 0; i < MAXBLSTATS; ++i) { @@ -2424,8 +2427,7 @@ has_ltgt_percentnumber(const char *str) } /* splitsubfields(): splits str in place into '+' or '&' separated strings. - * returns number of strings, or -1 if more than maxsf or MAX_SUBFIELDS - */ + returns number of strings, or -1 if more than maxsf or MAX_SUBFIELDS */ staticfn int splitsubfields(char *str, char ***sfarr, int maxsf) { @@ -2562,7 +2564,7 @@ parse_status_hl2(char (*s)[QBUFSZ], boolean from_configfile) "Satiated", "", "Hungry", "Weak", "Fainting", "Fainted", "Starved" }; char *tmp, *how; - int sidx = 0, i = -1, dt = -1; + int sidx = 0, i = -1, dt = ANY_INVALID; int coloridx = -1, successes = 0; int disp_attrib = 0; boolean percent, changed, numeric, down, up, @@ -2575,7 +2577,7 @@ parse_status_hl2(char (*s)[QBUFSZ], boolean from_configfile) /* Examples: 3.6.1: OPTION=hilite_status: hitpoints/<10%/red - OPTION=hilite_status: hitpoints/<10%/red/<5%/purple/1/red+blink+inverse + OPTION=hilite_status: hitpoints/<10%/red/<5%/purple/1/red&blink+inverse OPTION=hilite_status: experience/down/red/up/green OPTION=hilite_status: cap/strained/yellow/overtaxed/orange OPTION=hilite_status: title/always/blue diff --git a/src/cfgfiles.c b/src/cfgfiles.c new file mode 100644 index 000000000..3a3c0ff06 --- /dev/null +++ b/src/cfgfiles.c @@ -0,0 +1,1925 @@ +/* NetHack 3.7 cfgfiles.c $NHDT-Date: 1740532826 2025/02/25 17:20:26 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.417 $ */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/*-Copyright (c) Derek S. Ray, 2015. */ +/* NetHack may be freely redistributed. See license for details. */ + +#define NEED_VARARGS + +#include "hack.h" +#include "dlb.h" +#include + +#if (!defined(MAC) && !defined(O_WRONLY) && !defined(AZTEC_C)) \ + || defined(USE_FCNTL) +#include +#endif + +#define BIGBUFSZ (5 * BUFSZ) /* big enough to format a 4*BUFSZ string (from + * config file parsing) with modest decoration; + * result will then be truncated to BUFSZ-1 */ + +#ifdef USER_SOUNDS +extern char *sounddir; /* defined in sounds.c */ +#endif + +staticfn void vconfig_error_add(const char *, va_list); +staticfn FILE *fopen_config_file(const char *, int); +staticfn int get_uchars(char *, uchar *, boolean, int, const char *); +#ifdef NOCWD_ASSUMPTIONS +staticfn void adjust_prefix(char *, int); +#endif +staticfn char *choose_random_part(char *, char); +staticfn boolean config_error_nextline(const char *); +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 *); +#ifndef SFCTOOL +staticfn boolean cnf_line_OPTIONS(char *); +staticfn boolean cnf_line_AUTOPICKUP_EXCEPTION(char *); +staticfn boolean cnf_line_BINDINGS(char *); +staticfn boolean cnf_line_AUTOCOMPLETE(char *); +staticfn boolean cnf_line_MSGTYPE(char *); +staticfn boolean cnf_line_HACKDIR(char *); +staticfn boolean cnf_line_LEVELDIR(char *); +staticfn boolean cnf_line_SAVEDIR(char *); +staticfn boolean cnf_line_BONESDIR(char *); +staticfn boolean cnf_line_DATADIR(char *); +staticfn boolean cnf_line_SCOREDIR(char *); +staticfn boolean cnf_line_LOCKDIR(char *); +staticfn boolean cnf_line_CONFIGDIR(char *); +staticfn boolean cnf_line_TROUBLEDIR(char *); +staticfn boolean cnf_line_NAME(char *); +staticfn boolean cnf_line_ROLE(char *); +staticfn boolean cnf_line_dogname(char *); +staticfn boolean cnf_line_catname(char *); +#endif /* SFCTOOL */ +#ifdef SYSCF +staticfn boolean cnf_line_WIZARDS(char *); +staticfn boolean cnf_line_SHELLERS(char *); +staticfn boolean cnf_line_MSGHANDLER(char *); +staticfn boolean cnf_line_EXPLORERS(char *); +staticfn boolean cnf_line_DEBUGFILES(char *); +staticfn boolean cnf_line_DUMPLOGFILE(char *); +staticfn boolean cnf_line_GENERICUSERS(char *); +staticfn boolean cnf_line_BONES_POOLS(char *); +staticfn boolean cnf_line_SUPPORT(char *); +staticfn boolean cnf_line_RECOVER(char *); +staticfn boolean cnf_line_CHECK_SAVE_UID(char *); +staticfn boolean cnf_line_CHECK_PLNAME(char *); +staticfn boolean cnf_line_SEDUCE(char *); +staticfn boolean cnf_line_HIDEUSAGE(char *); +staticfn boolean cnf_line_MAXPLAYERS(char *); +staticfn boolean cnf_line_PERSMAX(char *); +staticfn boolean cnf_line_PERS_IS_UID(char *); +staticfn boolean cnf_line_ENTRYMAX(char *); +staticfn boolean cnf_line_POINTSMIN(char *); +staticfn boolean cnf_line_MAX_STATUENAME_RANK(char *); +staticfn boolean cnf_line_LIVELOG(char *); +staticfn boolean cnf_line_PANICTRACE_LIBC(char *); +staticfn boolean cnf_line_PANICTRACE_GDB(char *); +staticfn boolean cnf_line_GDBPATH(char *); +staticfn boolean cnf_line_GREPPATH(char *); +staticfn boolean cnf_line_CRASHREPORTURL(char *); +staticfn boolean cnf_line_ACCESSIBILITY(char *); + +staticfn boolean cnf_line_PORTABLE_DEVICE_PATHS(char *); +#endif /* SYSCF */ +#ifndef SFCTOOL +staticfn boolean cnf_line_BOULDER(char *); +staticfn boolean cnf_line_MENUCOLOR(char *); +staticfn boolean cnf_line_HILITE_STATUS(char *); +staticfn boolean cnf_line_WARNINGS(char *); +staticfn boolean cnf_line_ROGUESYMBOLS(char *); +staticfn boolean cnf_line_SYMBOLS(char *); +staticfn boolean cnf_line_WIZKIT(char *); +#ifdef USER_SOUNDS +staticfn boolean cnf_line_SOUNDDIR(char *); +staticfn boolean cnf_line_SOUND(char *); +#endif +staticfn boolean cnf_line_QT_TILEWIDTH(char *); +staticfn boolean cnf_line_QT_TILEHEIGHT(char *); +staticfn boolean cnf_line_QT_FONTSIZE(char *); +staticfn boolean cnf_line_QT_COMPACT(char *); +#endif /* SFCTOOL */ +struct _cnf_parser_state; /* defined below (far below...) */ +staticfn void cnf_parser_init(struct _cnf_parser_state *parser); +staticfn void cnf_parser_done(struct _cnf_parser_state *parser); +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)); + +#ifdef SFCTOOL +#ifdef wait_synch +#undef wait_synch +#endif +#define wait_synch() +#endif /* SFCTOOL */ + +/* ---------- BEGIN CONFIG FILE HANDLING ----------- */ + +/* used for messaging. Also used in options.c */ +static const char *default_configfile = +#ifdef UNIX + ".nethackrc"; +#else +#if defined(MAC) || defined(__BEOS__) + "NetHack Defaults"; +#else +#if defined(MSDOS) || defined(WIN32) + CONFIG_FILE; +#else + "NetHack.cnf"; +#endif +#endif +#endif +static char configfile[BUFSZ]; + +char * +get_configfile(void) +{ + return configfile; +} + +const char * +get_default_configfile(void) +{ + return default_configfile; +} + +#ifdef MSDOS +/* conflict with speed-dial under windows + * for XXX.cnf file so support of NetHack.cnf + * is for backward compatibility only. + * Preferred name (and first tried) is now defaults.nh but + * the game will try the old name if there + * is no defaults.nh. + */ +const char *backward_compat_configfile = "nethack.cnf"; +#endif + +#ifndef SFCTOOL + +/* #saveoptions - save config options into file */ +int +do_write_config_file(void) +{ + FILE *fp; + char tmp[BUFSZ]; + + if (!configfile[0]) { + pline("Strange, could not figure out config file name."); + return ECMD_OK; + } + if (flags.suppress_alert < FEATURE_NOTICE_VER(3,7,0)) { + pline("Warning: saveoptions is highly experimental!"); + wait_synch(); + pline("Some settings are not saved!"); + wait_synch(); + pline("All manual customization and comments are removed" + " from the file!"); + wait_synch(); + } +#define overwrite_prompt "Overwrite config file %.*s?" + Sprintf(tmp, overwrite_prompt, + (int) (BUFSZ - sizeof overwrite_prompt - 2), configfile); +#undef overwrite_prompt + if (!paranoid_query(TRUE, tmp)) + return ECMD_OK; + + fp = fopen(configfile, "w"); + if (fp) { + size_t len, wrote; + strbuf_t buf; + + strbuf_init(&buf); + all_options_strbuf(&buf); + len = strlen(buf.str); + wrote = fwrite(buf.str, 1, len, fp); + fclose(fp); + strbuf_empty(&buf); + if (wrote != len) + pline("An error occurred, wrote only partial data (%zu/%zu).", + wrote, len); + } + return ECMD_OK; +} +#endif /* SFCTOOL */ + +/* remember the name of the file we're accessing; + if may be used in option reject messages */ +void +set_configfile_name(const char *fname) +{ + (void) strncpy(configfile, fname, sizeof configfile - 1); + configfile[sizeof configfile - 1] = '\0'; +} + +staticfn FILE * +fopen_config_file(const char *filename, int src) +{ + FILE *fp; +#if defined(UNIX) || defined(VMS) + char tmp_config[BUFSZ]; + char *envp; +#endif + + if (src == set_in_sysconf) { + /* SYSCF_FILE; if we can't open it, caller will bail */ + if (filename && *filename) { + set_configfile_name(fqname(filename, SYSCONFPREFIX, 0)); + fp = fopen(configfile, "r"); + } else + fp = (FILE *) 0; + return fp; + } + /* If src != set_in_sysconf, "filename" is an environment variable, so it + * should hang around. If set, it is expected to be a full path name + * (if relevant) + */ + if (filename && *filename) { + set_configfile_name(filename); +#ifdef UNIX + 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 + * place a file name may be wholly under the player's + * control (but SYSCF_FILE is not under the player's + * control so it's OK). + */ + raw_printf("Access to %s denied (%d).", configfile, errno); + wait_synch(); + /* fall through to standard names */ + } else +#endif + if ((fp = fopen(configfile, "r")) != (FILE *) 0) { + return fp; +#if defined(UNIX) || defined(VMS) + } else { + /* access() above probably caught most problems for UNIX */ + raw_printf("Couldn't open requested config file %s (%d).", + configfile, errno); + wait_synch(); +#endif + } + } + /* fall through to standard names */ + +#if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32) + set_configfile_name(fqname(default_configfile, CONFIGPREFIX, 0)); + if ((fp = fopen(configfile, "r")) != (FILE *) 0) { + return fp; + } else if (strcmp(default_configfile, configfile)) { + set_configfile_name(default_configfile); + if ((fp = fopen(configfile, "r")) != (FILE *) 0) + return fp; + } +#ifdef MSDOS + set_configfile_name(fqname(backward_compat_configfile, CONFIGPREFIX, 0)); + if ((fp = fopen(configfile, "r")) != (FILE *) 0) { + return fp; + } else if (strcmp(backward_compat_configfile, configfile)) { + set_configfile_name(backward_compat_configfile); + if ((fp = fopen(configfile, "r")) != (FILE *) 0) + return fp; + } +#endif +#else +/* constructed full path names don't need fqname() */ +#ifdef VMS + /* no punctuation, so might be a logical name */ + set_configfile_name("nethackini"); + if ((fp = fopen(configfile, "r")) != (FILE *) 0) + return fp; + set_configfile_name("sys$login:nethack.ini"); + if ((fp = fopen(configfile, "r")) != (FILE *) 0) + return fp; + + envp = nh_getenv("HOME"); + if (!envp || !*envp) + Strcpy(tmp_config, "NetHack.cnf"); + else + Sprintf(tmp_config, "%s%s%s", envp, + !strchr(":]>/", envp[strlen(envp) - 1]) ? "/" : "", + "NetHack.cnf"); + set_configfile_name(tmp_config); + if ((fp = fopen(configfile, "r")) != (FILE *) 0) + return fp; +#else /* should be only UNIX left */ + envp = nh_getenv("HOME"); + if (!envp) + Strcpy(tmp_config, ".nethackrc"); + else + Sprintf(tmp_config, "%s/%s", envp, ".nethackrc"); + + set_configfile_name(tmp_config); + if ((fp = fopen(configfile, "r")) != (FILE *) 0) + return fp; +#if defined(__APPLE__) /* UNIX+__APPLE__ => OSX || MacOS */ + /* try an alternative */ + if (envp) { + /* keep 'tmp_config' intact here; if alternates fail, use it to + restore configfile[] to its preferred setting (".nethackrc") */ + char alt_config[sizeof tmp_config]; + + /* OSX-style configuration settings */ + Snprintf(alt_config, sizeof alt_config, "%s/%s", envp, + "Library/Preferences/NetHack Defaults"); + set_configfile_name(alt_config); + if ((fp = fopen(configfile, "r")) != (FILE *) 0) + return fp; + /* may be easier for user to edit if filename has '.txt' suffix */ + Snprintf(alt_config, sizeof alt_config, "%s/%s", envp, + "Library/Preferences/NetHack Defaults.txt"); + set_configfile_name(alt_config); + if ((fp = fopen(configfile, "r")) != (FILE *) 0) + return fp; + /* couldn't open either of the alternate names; for use in + messages, put 'configfile' back to the normal value rather than + leaving it set to last alternate; retry open() to reset 'errno' */ + set_configfile_name(tmp_config); + if ((fp = fopen(configfile, "r")) != (FILE *) 0) + return fp; + } +#endif /*__APPLE__*/ + if (errno != ENOENT) { + const char *details; + + /* e.g., problems when setuid NetHack can't search home + directory restricted to user */ +#if defined(NHSTDC) && !defined(NOTSTDC) + if ((details = strerror(errno)) == 0) +#endif + details = ""; + raw_printf("Couldn't open default config file %s %s(%d).", + configfile, details, errno); + wait_synch(); + } +#endif /* !VMS => Unix */ +#endif /* !(MICRO || MAC || __BEOS__ || WIN32) */ + return (FILE *) 0; +} + +/* + * Retrieve a list of integers from buf into a uchar array. + * + * NOTE: zeros are inserted unless modlist is TRUE, in which case the list + * location is unchanged. Callers must handle zeros if modlist is FALSE. + */ +staticfn int +get_uchars(char *bufp, /* current pointer */ + uchar *list, /* return list */ + boolean modlist, /* TRUE: list is being modified in place */ + int size, /* return list size */ + const char *name) /* name of option for error message */ +{ + unsigned int num = 0; + int count = 0; + boolean havenum = FALSE; + + while (1) { + switch (*bufp) { + case ' ': + case '\0': + case '\t': + case '\n': + if (havenum) { + /* if modifying in place, don't insert zeros */ + if (num || !modlist) + list[count] = num; + count++; + num = 0; + havenum = FALSE; + } + if (count == size || !*bufp) + return count; + bufp++; + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + havenum = TRUE; + num = num * 10 + (*bufp - '0'); + bufp++; + break; + + case '\\': + goto gi_error; + break; + + default: + gi_error: + raw_printf("Syntax error in %s", name); + wait_synch(); + return count; + } + } + /*NOTREACHED*/ +} + +#ifdef NOCWD_ASSUMPTIONS +staticfn void +adjust_prefix(char *bufp, int prefixid) +{ + char *ptr; + + if (!bufp) + return; +#ifdef WIN32 + if (fqn_prefix_locked[prefixid]) + return; +#endif + /* Backward compatibility, ignore trailing ;n */ + if ((ptr = strchr(bufp, ';')) != 0) + *ptr = '\0'; + if (strlen(bufp) > 0) { + gf.fqn_prefix[prefixid] = (char *) alloc(strlen(bufp) + 2); + Strcpy(gf.fqn_prefix[prefixid], bufp); + append_slash(gf.fqn_prefix[prefixid]); + } +} +#endif + +/* Choose at random one of the sep separated parts from str. Mangles str. */ +staticfn char * +choose_random_part(char *str, char sep) +{ + int nsep = 1; + int csep; + int len = 0; + char *begin = str; + + if (!str) + return (char *) 0; + + while (*str) { + if (*str == sep) + nsep++; + str++; + } +#ifndef SFCTOOL + csep = rn2(nsep); +#else + nhUse(nsep); + csep = 1; +#endif + str = begin; + while ((csep > 0) && *str) { + str++; + if (*str == sep) + csep--; + } + if (*str) { + if (*str == sep) + str++; + begin = str; + while (*str && *str != sep) { + str++; + len++; + } + *str = '\0'; + if (len) + return begin; + } + return (char *) 0; +} + +staticfn void +free_config_sections(void) +{ + if (gc.config_section_chosen) { + free(gc.config_section_chosen); + gc.config_section_chosen = NULL; + } + if (gc.config_section_current) { + free(gc.config_section_current); + gc.config_section_current = NULL; + } +} + +/* check for " [ anything-except-bracket-or-empty ] # arbitrary-comment" + with spaces optional; returns pointer to "anything-except..." (with + trailing " ] #..." stripped) if ok, otherwise Null */ +staticfn char * +is_config_section( + char *str) /* trailing spaces are stripped, ']' too iff result is good */ +{ + char *a, *c, *z; + + /* remove any spaces at start and end; won't significantly interfere + with echoing the string in a config error message, if warranted */ + a = trimspaces(str); + /* first character should be open square bracket; set pointer past it */ + if (*a++ != '[') + return (char *) 0; + /* last character should be close bracket, ignoring any comment */ + z = strchr(a, ']'); + if (!z) + return (char *) 0; + /* comment, if present, can be preceded by spaces */ + for (c = z + 1; *c == ' '; ++c) + continue; + if (*c && *c != '#') + return (char *) 0; + /* we now know that result is good; there won't be a config error + message so we can modify the input string */ + *z = '\0'; + /* 'a' points past '[' and the string ends where ']' was; remove any + spaces between '[' and choice-start and between choice-end and ']' */ + return trimspaces(a); +} + +staticfn boolean +handle_config_section(char *buf) +{ + char *sect = is_config_section(buf); + + if (sect) { + if (gc.config_section_current) + free(gc.config_section_current), gc.config_section_current = 0; + /* is_config_section() removed brackets from 'sect' */ + if (!gc.config_section_chosen) { + config_error_add("Section \"[%s]\" without CHOOSE", sect); + return TRUE; + } + if (*sect) { /* got a section name */ + gc.config_section_current = dupstr(sect); + debugpline1("set config section: '%s'", + gc.config_section_current); + } else { /* empty section name => end of sections */ + free_config_sections(); + debugpline0("unset config section"); + } + return TRUE; + } + + if (gc.config_section_current) { + if (!gc.config_section_chosen) + return TRUE; + if (strcmp(gc.config_section_current, gc.config_section_chosen)) + return TRUE; + } + return FALSE; +} + +#define match_varname(INP, NAM, LEN) match_optname(INP, NAM, LEN, TRUE) + +/* find the '=' or ':' */ +staticfn char * +find_optparam(const char *buf) +{ + char *bufp, *altp; + + bufp = strchr(buf, '='); + altp = strchr(buf, ':'); + if (!bufp || (altp && altp < bufp)) + bufp = altp; + + return bufp; +} + +#ifndef SFCTOOL + +staticfn boolean +cnf_line_OPTIONS(char *origbuf) +{ + char *bufp = find_optparam(origbuf); + + ++bufp; /* skip '='; parseoptions() handles spaces */ + return parseoptions(bufp, TRUE, TRUE); +} + +staticfn boolean +cnf_line_AUTOPICKUP_EXCEPTION(char *bufp) +{ + add_autopickup_exception(bufp); + return TRUE; +} + +staticfn boolean +cnf_line_BINDINGS(char *bufp) +{ + return parsebindings(bufp); +} + +staticfn boolean +cnf_line_AUTOCOMPLETE(char *bufp) +{ + parseautocomplete(bufp, TRUE); + return TRUE; +} + +staticfn boolean +cnf_line_MSGTYPE(char *bufp) +{ + return msgtype_parse_add(bufp); +} + +staticfn boolean +cnf_line_HACKDIR(char *bufp) +{ +#ifdef NOCWD_ASSUMPTIONS + adjust_prefix(bufp, HACKPREFIX); +#else /*NOCWD_ASSUMPTIONS*/ +#ifdef MICRO + (void) strncpy(gh.hackdir, bufp, PATHLEN - 1); +#else /* MICRO */ + nhUse(bufp); +#endif /* MICRO */ +#endif /*NOCWD_ASSUMPTIONS*/ + return TRUE; +} + +staticfn boolean +cnf_line_LEVELDIR(char *bufp) +{ +#ifdef NOCWD_ASSUMPTIONS + adjust_prefix(bufp, LEVELPREFIX); +#else /*NOCWD_ASSUMPTIONS*/ +#ifdef MICRO + if (strlen(bufp) >= PATHLEN) + bufp[PATHLEN - 1] = '\0'; + Strcpy(g.permbones, bufp); + if (!ramdisk_specified || !*levels) + Strcpy(levels, bufp); + gr.ramdisk = (strcmp(g.permbones, levels) != 0); +#else /* MICRO */ + nhUse(bufp); +#endif /* MICRO */ +#endif /*NOCWD_ASSUMPTIONS*/ + return TRUE; +} + +staticfn boolean +cnf_line_SAVEDIR(char *bufp) +{ +#ifdef NOCWD_ASSUMPTIONS + adjust_prefix(bufp, SAVEPREFIX); +#else /*NOCWD_ASSUMPTIONS*/ +#ifdef MICRO + char *ptr; + + if ((ptr = strchr(bufp, ';')) != 0) { + *ptr = '\0'; + } + + (void) strncpy(gs.SAVEP, bufp, SAVESIZE - 1); + append_slash(gs.SAVEP); +#else /* MICRO */ + nhUse(bufp); +#endif /* MICRO */ +#endif /*NOCWD_ASSUMPTIONS*/ + return TRUE; +} + +staticfn boolean +cnf_line_BONESDIR(char *bufp) +{ +#ifdef NOCWD_ASSUMPTIONS + adjust_prefix(bufp, BONESPREFIX); +#else + nhUse(bufp); +#endif + return TRUE; +} + +staticfn boolean +cnf_line_DATADIR(char *bufp) +{ +#ifdef NOCWD_ASSUMPTIONS + adjust_prefix(bufp, DATAPREFIX); +#else + nhUse(bufp); +#endif + return TRUE; +} + +staticfn boolean +cnf_line_SCOREDIR(char *bufp) +{ +#ifdef NOCWD_ASSUMPTIONS + adjust_prefix(bufp, SCOREPREFIX); +#else + nhUse(bufp); +#endif + return TRUE; +} + +staticfn boolean +cnf_line_LOCKDIR(char *bufp) +{ +#ifdef NOCWD_ASSUMPTIONS + adjust_prefix(bufp, LOCKPREFIX); +#else + nhUse(bufp); +#endif + return TRUE; +} + +staticfn boolean +cnf_line_CONFIGDIR(char *bufp) +{ +#ifdef NOCWD_ASSUMPTIONS + adjust_prefix(bufp, CONFIGPREFIX); +#else + nhUse(bufp); +#endif + return TRUE; +} + +staticfn boolean +cnf_line_TROUBLEDIR(char *bufp) +{ +#ifdef NOCWD_ASSUMPTIONS + adjust_prefix(bufp, TROUBLEPREFIX); +#else + nhUse(bufp); +#endif + return TRUE; +} + +staticfn boolean +cnf_line_NAME(char *bufp) +{ + (void) strncpy(svp.plname, bufp, PL_NSIZ - 1); + return TRUE; +} + +staticfn boolean +cnf_line_ROLE(char *bufp) +{ + int len; + + if ((len = str2role(bufp)) >= 0) + flags.initrole = len; + return TRUE; +} + +staticfn boolean +cnf_line_dogname(char *bufp) +{ + (void) strncpy(gd.dogname, bufp, PL_PSIZ - 1); + return TRUE; +} + +staticfn boolean +cnf_line_catname(char *bufp) +{ + (void) strncpy(gc.catname, bufp, PL_PSIZ - 1); + return TRUE; +} +#endif /* SFCTOOL */ + +#ifdef SYSCF + +staticfn boolean +cnf_line_WIZARDS(char *bufp) +{ + if (sysopt.wizards) + free((genericptr_t) sysopt.wizards); + sysopt.wizards = dupstr(bufp); + if (strlen(sysopt.wizards) && strcmp(sysopt.wizards, "*")) { + /* pre-format WIZARDS list now; it's displayed during a panic + and since that panic might be due to running out of memory, + we don't want to risk attempting to allocate any memory then */ + if (sysopt.fmtd_wizard_list) + free((genericptr_t) sysopt.fmtd_wizard_list); + sysopt.fmtd_wizard_list = build_english_list(sysopt.wizards); + } + return TRUE; +} + +staticfn boolean +cnf_line_SHELLERS(char *bufp) +{ + if (sysopt.shellers) + free((genericptr_t) sysopt.shellers); + sysopt.shellers = dupstr(bufp); + return TRUE; +} + +staticfn boolean +cnf_line_MSGHANDLER(char *bufp) +{ + if (sysopt.msghandler) + free((genericptr_t) sysopt.msghandler); + sysopt.msghandler = dupstr(bufp); + return TRUE; +} + +staticfn boolean +cnf_line_EXPLORERS(char *bufp) +{ + if (sysopt.explorers) + free((genericptr_t) sysopt.explorers); + sysopt.explorers = dupstr(bufp); + return TRUE; +} + +staticfn boolean +cnf_line_DEBUGFILES(char *bufp) +{ + /* might already have a vaule from getenv("DEBUGFILES"); + if so, ignore this value from SYSCF */ + if (!sysopt.env_dbgfl) { + if (sysopt.debugfiles) + free((genericptr_t) sysopt.debugfiles); + sysopt.debugfiles = dupstr(bufp); + } + return TRUE; +} + +staticfn boolean +cnf_line_DUMPLOGFILE(char *bufp) +{ +#ifdef DUMPLOG + if (sysopt.dumplogfile) + free((genericptr_t) sysopt.dumplogfile); + sysopt.dumplogfile = dupstr(bufp); +#else + nhUse(bufp); +#endif /*DUMPLOG*/ + return TRUE; +} + +staticfn boolean +cnf_line_GENERICUSERS(char *bufp) +{ + if (sysopt.genericusers) + free((genericptr_t) sysopt.genericusers); + sysopt.genericusers = dupstr(bufp); + return TRUE; +} + +staticfn boolean +cnf_line_BONES_POOLS(char *bufp) +{ + /* max value of 10 guarantees (N % bones.pools) will be one digit + so we don't lose control of the length of bones file names */ + int n = atoi(bufp); + + sysopt.bones_pools = (n <= 0) ? 0 : min(n, 10); + /* note: right now bones_pools==0 is the same as bones_pools==1, + but we could change that and make bones_pools==0 become an + indicator to suppress bones usage altogether */ + return TRUE; +} + +staticfn boolean +cnf_line_SUPPORT(char *bufp) +{ + if (sysopt.support) + free((genericptr_t) sysopt.support); + sysopt.support = dupstr(bufp); + return TRUE; +} + +staticfn boolean +cnf_line_RECOVER(char *bufp) +{ + if (sysopt.recover) + free((genericptr_t) sysopt.recover); + sysopt.recover = dupstr(bufp); + return TRUE; +} + +staticfn boolean +cnf_line_CHECK_SAVE_UID(char *bufp) +{ + int n = atoi(bufp); + + sysopt.check_save_uid = n; + return TRUE; +} + +staticfn boolean +cnf_line_CHECK_PLNAME(char *bufp) +{ + int n = atoi(bufp); + + sysopt.check_plname = n; + return TRUE; +} + +staticfn boolean +cnf_line_SEDUCE(char *bufp) +{ + int n = !!atoi(bufp); /* XXX this could be tighter */ +#ifdef SYSCF + int src = iflags.parse_config_file_src; + boolean in_sysconf = (src == set_in_sysconf); +#else + boolean in_sysconf = FALSE; +#endif + + /* allow anyone to disable it but can only enable it in sysconf + or as a no-op for the user when sysconf hasn't disabled it */ + if (!in_sysconf && !sysopt.seduce && n != 0) { + config_error_add("Illegal value in SEDUCE"); + n = 0; + } + sysopt.seduce = n; + sysopt_seduce_set(sysopt.seduce); + return TRUE; +} + +staticfn boolean +cnf_line_HIDEUSAGE(char *bufp) +{ + int n = !!atoi(bufp); + + sysopt.hideusage = n; + return TRUE; +} + +staticfn boolean +cnf_line_MAXPLAYERS(char *bufp) +{ + int n = atoi(bufp); + + /* XXX to get more than 25, need to rewrite all lock code */ + if (n < 0 || n > 25) { + config_error_add("Illegal value in MAXPLAYERS (maximum is 25)"); + n = 5; + } + sysopt.maxplayers = n; + return TRUE; +} + +staticfn boolean +cnf_line_PERSMAX(char *bufp) +{ + int n = atoi(bufp); + + if (n < 1) { + config_error_add("Illegal value in PERSMAX (minimum is 1)"); + n = 0; + } + sysopt.persmax = n; + return TRUE; +} + +staticfn boolean +cnf_line_PERS_IS_UID(char *bufp) +{ + int n = atoi(bufp); + + if (n != 0 && n != 1) { + config_error_add("Illegal value in PERS_IS_UID (must be 0 or 1)"); + n = 0; + } + sysopt.pers_is_uid = n; + return TRUE; +} + +staticfn boolean +cnf_line_ENTRYMAX(char *bufp) +{ + int n = atoi(bufp); + + if (n < 10) { + config_error_add("Illegal value in ENTRYMAX (minimum is 10)"); + n = 10; + } + sysopt.entrymax = n; + return TRUE; +} + +staticfn boolean +cnf_line_POINTSMIN(char *bufp) +{ + int n = atoi(bufp); + + if (n < 1) { + config_error_add("Illegal value in POINTSMIN (minimum is 1)"); + n = 100; + } + sysopt.pointsmin = n; + return TRUE; +} + +staticfn boolean +cnf_line_MAX_STATUENAME_RANK(char *bufp) +{ + int n = atoi(bufp); + + if (n < 1) { + config_error_add("Illegal value in MAX_STATUENAME_RANK" + " (minimum is 1)"); + n = 10; + } + sysopt.tt_oname_maxrank = n; + return TRUE; +} + +staticfn boolean +cnf_line_LIVELOG(char *bufp) +{ + /* using 0 for base accepts "dddd" as decimal provided that first 'd' + isn't '0', "0xhhhh" as hexadecimal, and "0oooo" as octal; ignores + any trailing junk, including '8' or '9' for leading '0' octal */ + long L = strtol(bufp, NULL, 0); + + if (L < 0L || L > 0xffffL) { + config_error_add("Illegal value for LIVELOG" + " (must be between 0 and 0xFFFF)."); + return 0; + } + sysopt.livelog = L; + return TRUE; +} + +staticfn boolean +cnf_line_PANICTRACE_LIBC(char *bufp) +{ + int n = atoi(bufp); + +#if defined(PANICTRACE) && defined(PANICTRACE_LIBC) + if (n < 0 || n > 2) { + config_error_add("Illegal value in PANICTRACE_LIBC (not 0,1,2)"); + n = 0; + } +#endif + sysopt.panictrace_libc = n; + return TRUE; +} + +staticfn boolean +cnf_line_PANICTRACE_GDB(char *bufp) +{ + int n = atoi(bufp); + +#if defined(PANICTRACE) + if (n < 0 || n > 2) { + config_error_add("Illegal value in PANICTRACE_GDB (not 0,1,2)"); + n = 0; + } +#endif + sysopt.panictrace_gdb = n; + return TRUE; +} + +staticfn boolean +cnf_line_GDBPATH(char *bufp) +{ +#if defined(PANICTRACE) && !defined(VMS) + if (!file_exists(bufp)) { + config_error_add("File specified in GDBPATH does not exist"); + return FALSE; + } +#endif + if (sysopt.gdbpath) + free((genericptr_t) sysopt.gdbpath); + sysopt.gdbpath = dupstr(bufp); + return TRUE; +} + +staticfn boolean +cnf_line_GREPPATH(char *bufp) +{ +#if defined(PANICTRACE) && !defined(VMS) + if (!file_exists(bufp)) { + config_error_add("File specified in GREPPATH does not exist"); + return FALSE; + } +#endif + if (sysopt.greppath) + free((genericptr_t) sysopt.greppath); + sysopt.greppath = dupstr(bufp); + return TRUE; +} + +staticfn boolean +cnf_line_CRASHREPORTURL(char *bufp) +{ + if (sysopt.crashreporturl) + free((genericptr_t) sysopt.crashreporturl); + sysopt.crashreporturl = dupstr(bufp); + return TRUE; +} + +staticfn boolean +cnf_line_ACCESSIBILITY(char *bufp) +{ + int n = atoi(bufp); + + if (n < 0 || n > 1) { + config_error_add("Illegal value in ACCESSIBILITY (not 0,1)"); + n = 0; + } + sysopt.accessibility = n; + return TRUE; +} + +staticfn boolean +cnf_line_PORTABLE_DEVICE_PATHS(char *bufp) +{ +#ifdef WIN32 + int n = atoi(bufp); + + if (n < 0 || n > 1) { + config_error_add("Illegal value in PORTABLE_DEVICE_PATHS" + " (not 0 or 1)"); + n = 0; + } + sysopt.portable_device_paths = n; +#else /* Windows-only directive encountered by non-Windows config */ + nhUse(bufp); + config_error_add("PORTABLE_DEVICE_PATHS is not supported"); +#endif + return TRUE; +} +#endif /* SYSCF */ + +#ifndef SFCTOOL + +staticfn boolean +cnf_line_BOULDER(char *bufp) +{ + (void) get_uchars(bufp, &go.ov_primary_syms[SYM_BOULDER + SYM_OFF_X], + TRUE, 1, "BOULDER"); + return TRUE; +} + +staticfn boolean +cnf_line_MENUCOLOR(char *bufp) +{ + return add_menu_coloring(bufp); +} + +staticfn boolean +cnf_line_HILITE_STATUS(char *bufp) +{ +#ifdef STATUS_HILITES + return parse_status_hl1(bufp, TRUE); +#else + nhUse(bufp); + return TRUE; +#endif +} + +staticfn boolean +cnf_line_WARNINGS(char *bufp) +{ + uchar translate[MAXPCHARS]; + + (void) get_uchars(bufp, translate, FALSE, WARNCOUNT, "WARNINGS"); + assign_warnings(translate); + return TRUE; +} + +staticfn boolean +cnf_line_ROGUESYMBOLS(char *bufp) +{ + if (parsesymbols(bufp, ROGUESET)) { + switch_symbols(TRUE); + return TRUE; + } + config_error_add("Error in ROGUESYMBOLS definition '%s'", bufp); + return FALSE; +} + +staticfn boolean +cnf_line_SYMBOLS(char *bufp) +{ + if (parsesymbols(bufp, PRIMARYSET)) { + switch_symbols(TRUE); + return TRUE; + } + config_error_add("Error in SYMBOLS definition '%s'", bufp); + return FALSE; +} + +staticfn boolean +cnf_line_WIZKIT(char *bufp) +{ + (void) strncpy(gw.wizkit, bufp, WIZKIT_MAX - 1); + return TRUE; +} + +#ifdef USER_SOUNDS +staticfn boolean +cnf_line_SOUNDDIR(char *bufp) +{ + if (sounddir) + free((genericptr_t) sounddir); + sounddir = dupstr(bufp); + return TRUE; +} + +staticfn boolean +cnf_line_SOUND(char *bufp) +{ + add_sound_mapping(bufp); + return TRUE; +} +#endif /*USER_SOUNDS*/ + +staticfn boolean +cnf_line_QT_TILEWIDTH(char *bufp) +{ +#ifdef QT_GRAPHICS + extern char *qt_tilewidth; + + if (qt_tilewidth == NULL) + qt_tilewidth = dupstr(bufp); +#else + nhUse(bufp); +#endif + return TRUE; +} + +staticfn boolean +cnf_line_QT_TILEHEIGHT(char *bufp) +{ +#ifdef QT_GRAPHICS + extern char *qt_tileheight; + + if (qt_tileheight == NULL) + qt_tileheight = dupstr(bufp); +#else + nhUse(bufp); +#endif + return TRUE; +} + +staticfn boolean +cnf_line_QT_FONTSIZE(char *bufp) +{ +#ifdef QT_GRAPHICS + extern char *qt_fontsize; + + if (qt_fontsize == NULL) + qt_fontsize = dupstr(bufp); +#else + nhUse(bufp); +#endif + return TRUE; +} + +staticfn boolean +cnf_line_QT_COMPACT(char *bufp) +{ +#ifdef QT_GRAPHICS + extern int qt_compact_mode; + + qt_compact_mode = atoi(bufp); +#else + nhUse(bufp); +#endif + return TRUE; +} +#endif /* SFCTOOL */ + +typedef boolean (*config_line_stmt_func)(char *); + +/* normal */ +#define CNFL_N(n, l) { #n, l, FALSE, FALSE, cnf_line_##n } +/* 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 } + +static const struct match_config_line_stmt { + const char *name; + int len; + boolean syscnf_only; + boolean origbuf; + config_line_stmt_func fn; +} config_line_stmt[] = { +#ifndef SFCTOOL + /* OPTIONS handled separately */ + { "OPTIONS", 4, FALSE, TRUE, cnf_line_OPTIONS }, + CNFL_N(AUTOPICKUP_EXCEPTION, 5), + CNFL_N(BINDINGS, 4), + CNFL_N(AUTOCOMPLETE, 5), + CNFL_N(MSGTYPE, 7), + CNFL_N(HACKDIR, 4), + CNFL_N(LEVELDIR, 4), + CNFL_NA(LEVELS, 4, LEVELDIR), + CNFL_N(SAVEDIR, 4), + CNFL_N(BONESDIR, 5), + CNFL_N(DATADIR, 4), + CNFL_N(SCOREDIR, 4), + CNFL_N(LOCKDIR, 4), + CNFL_N(CONFIGDIR, 4), + CNFL_N(TROUBLEDIR, 4), + CNFL_N(NAME, 4), + CNFL_N(ROLE, 4), + CNFL_NA(CHARACTER, 4, ROLE), + CNFL_N(dogname, 3), + CNFL_N(catname, 3), +#endif /* SFCTOOL */ +#ifdef SYSCF + CNFL_S(WIZARDS, 7), + CNFL_S(SHELLERS, 8), + CNFL_S(MSGHANDLER, 9), + CNFL_S(EXPLORERS, 7), + CNFL_S(DEBUGFILES, 5), + CNFL_S(DUMPLOGFILE, 7), + CNFL_S(GENERICUSERS, 12), + CNFL_S(BONES_POOLS, 10), + CNFL_S(SUPPORT, 7), + CNFL_S(RECOVER, 7), + CNFL_S(CHECK_SAVE_UID, 14), + CNFL_S(CHECK_PLNAME, 12), + CNFL_S(SEDUCE, 6), + CNFL_S(HIDEUSAGE, 9), + CNFL_S(MAXPLAYERS, 10), + CNFL_S(PERSMAX, 7), + CNFL_S(PERS_IS_UID, 11), + CNFL_S(ENTRYMAX, 8), + CNFL_S(POINTSMIN, 9), + CNFL_S(MAX_STATUENAME_RANK, 10), + CNFL_S(LIVELOG, 7), + CNFL_S(PANICTRACE_LIBC, 15), + CNFL_S(PANICTRACE_GDB, 14), + CNFL_S(CRASHREPORTURL, 13), + CNFL_S(GDBPATH, 7), + CNFL_S(GREPPATH, 7), + CNFL_S(ACCESSIBILITY, 13), + CNFL_S(PORTABLE_DEVICE_PATHS, 8), +#endif /*SYSCF*/ +#ifndef SFCTOOL + CNFL_N(BOULDER, 3), + CNFL_N(MENUCOLOR, 9), + CNFL_N(HILITE_STATUS, 6), + CNFL_N(WARNINGS, 5), + CNFL_N(ROGUESYMBOLS, 4), + CNFL_N(SYMBOLS, 4), + CNFL_N(WIZKIT, 6), +#ifdef USER_SOUNDS + CNFL_N(SOUNDDIR, 8), + CNFL_N(SOUND, 5), +#endif /*USER_SOUNDS*/ + CNFL_N(QT_TILEWIDTH, 12), + CNFL_N(QT_TILEHEIGHT, 13), + CNFL_N(QT_FONTSIZE, 11), + CNFL_N(QT_COMPACT, 10) +#endif /* SFCTOOL */ +}; + +#undef CNFL_N +#undef CNFL_NA +#undef CNFL_S + +boolean +parse_config_line(char *origbuf) +{ +#if defined(MICRO) && !defined(NOCWD_ASSUMPTIONS) + static boolean ramdisk_specified = FALSE; +#endif +#ifdef SYSCF + int src = iflags.parse_config_file_src; + boolean in_sysconf = (src == set_in_sysconf); +#endif + char *bufp, buf[4 * BUFSZ]; + int i; + + while (*origbuf == ' ' || *origbuf == '\t') /* skip leading whitespace */ + ++origbuf; /* (caller probably already did this) */ + (void) strncpy(buf, origbuf, sizeof buf - 1); + buf[sizeof buf - 1] = '\0'; /* strncpy not guaranteed to NUL terminate */ + /* convert any tab to space, condense consecutive spaces into one, + remove leading and trailing spaces (exception: if there is nothing + but spaces, one of them will be kept even though it leads/trails) */ + mungspaces(buf); + + /* find the '=' or ':' */ + bufp = find_optparam(buf); + if (!bufp) { + config_error_add("Not a config statement, missing '='"); + return FALSE; + } + /* skip past '=', then space between it and value, if any */ + ++bufp; + if (*bufp == ' ') + ++bufp; + + for (i = 0; i < SIZE(config_line_stmt); i++) { +#ifdef SYSCF + if (config_line_stmt[i].syscnf_only && !in_sysconf) + continue; +#endif + if (match_varname(buf, config_line_stmt[i].name, + config_line_stmt[i].len)) { + char *parm = config_line_stmt[i].origbuf ? origbuf : bufp; + + return config_line_stmt[i].fn(parm); + } + } + + config_error_add("Unknown config statement"); + return FALSE; +} + +#ifdef USER_SOUNDS +boolean +can_read_file(const char *filename) +{ + return (boolean) (access(filename, 4) == 0); +} +#endif /* USER_SOUNDS */ + +struct _config_error_errmsg { + int line_num; + char *errormsg; + struct _config_error_errmsg *next; +}; + +struct _config_error_frame { + int line_num; + int num_errors; + boolean origline_shown; + boolean fromfile; + boolean secure; + char origline[4 * BUFSZ]; + char source[BUFSZ]; + struct _config_error_frame *next; +}; + +static struct _config_error_frame *config_error_data = 0; +static struct _config_error_errmsg *config_error_msg = 0; + +void +config_error_init(boolean from_file, const char *sourcename, boolean secure) +{ + struct _config_error_frame *tmp = (struct _config_error_frame *) + alloc(sizeof *tmp); + + tmp->line_num = 0; + tmp->num_errors = 0; + tmp->origline_shown = FALSE; + tmp->fromfile = from_file; + tmp->secure = secure; + tmp->origline[0] = '\0'; + if (sourcename && sourcename[0]) { + (void) strncpy(tmp->source, sourcename, sizeof (tmp->source) - 1); + tmp->source[sizeof (tmp->source) - 1] = '\0'; + } else + tmp->source[0] = '\0'; + + tmp->next = config_error_data; + config_error_data = tmp; + program_state.config_error_ready = TRUE; +} + +staticfn boolean +config_error_nextline(const char *line) +{ + struct _config_error_frame *ced = config_error_data; + + if (!ced) + return FALSE; + + if (ced->num_errors && ced->secure) + return FALSE; + + ced->line_num++; + ced->origline_shown = FALSE; + if (line && line[0]) { + (void) strncpy(ced->origline, line, sizeof (ced->origline) - 1); + ced->origline[sizeof (ced->origline) - 1] = '\0'; + } else + ced->origline[0] = '\0'; + + return TRUE; +} + +#ifndef SFCTOOL +int +l_get_config_errors(lua_State *L) +{ + struct _config_error_errmsg *dat = config_error_msg; + struct _config_error_errmsg *tmp; + int idx = 1; + + lua_newtable(L); + + while (dat) { + lua_pushinteger(L, idx++); + lua_newtable(L); + nhl_add_table_entry_int(L, "line", dat->line_num); + nhl_add_table_entry_str(L, "error", dat->errormsg); + lua_settable(L, -3); + tmp = dat->next; + free(dat->errormsg); + dat->errormsg = (char *) 0; + free(dat); + dat = tmp; + } + config_error_msg = (struct _config_error_errmsg *) 0; + + return 1; +} +#endif /* SFCTOOL */ + +/* varargs 'config_error_add()' moved to pline.c */ +void +config_erradd(const char *buf) +{ + char lineno[QBUFSZ]; + const char *punct; + + if (!buf || !*buf) + buf = "Unknown error"; + + /* if buf[] doesn't end in a period, exclamation point, or question mark, + we'll include a period (in the message, not appended to buf[]) */ + punct = c_eos((char *) buf) - 1; /* eos(buf)-1 is valid */ + punct = strchr(".!?", *punct) ? "" : "."; + + if (!program_state.config_error_ready) { + /* either very early, where pline() will use raw_print(), or + player gave bad value when prompted by interactive 'O' command */ + pline("%s%s%s", !iflags.window_inited ? "config_error_add: " : "", + buf, punct); + wait_synch(); + return; + } + + if (iflags.in_lua) { + struct _config_error_errmsg *dat + = (struct _config_error_errmsg *) alloc(sizeof *dat); + + dat->next = config_error_msg; + dat->line_num = config_error_data->line_num; + dat->errormsg = dupstr(buf); + config_error_msg = dat; + return; + } + + config_error_data->num_errors++; + if (!config_error_data->origline_shown && !config_error_data->secure) { + pline("\n%s", config_error_data->origline); + config_error_data->origline_shown = TRUE; + } + if (config_error_data->line_num > 0 && !config_error_data->secure) { + Sprintf(lineno, "Line %d: ", config_error_data->line_num); + } else + lineno[0] = '\0'; + + pline("%s %s%s%s", config_error_data->secure ? "Error:" : " *", + lineno, buf, punct); +} + +int +config_error_done(void) +{ + int n; + struct _config_error_frame *tmp = config_error_data; + + if (!config_error_data) + return 0; + n = config_error_data->num_errors; +#ifndef USER_SOUNDS + if (gn.no_sound_notified > 0) { + /* no USER_SOUNDS; config_error_add() was called once for first + SOUND or SOUNDDIR entry seen, then skipped for any others; + include those skipped ones in the total error count */ + n += (gn.no_sound_notified - 1); + gn.no_sound_notified = 0; + } +#endif + if (n) { + boolean cmdline = !strcmp(config_error_data->source, "command line"); + + pline("\n%d error%s %s %s.\n", n, plur(n), cmdline ? "on" : "in", + *config_error_data->source ? config_error_data->source + : configfile); + wait_synch(); + } + config_error_data = tmp->next; + free(tmp); + program_state.config_error_ready = (config_error_data != 0); + return n; +} + +boolean +read_config_file(const char *filename, int src) +{ + FILE *fp; + boolean rv = TRUE; + + if (!(fp = fopen_config_file(filename, src))) + return FALSE; +#ifndef SFCTOOL + /* begin detection of duplicate configfile options */ + reset_duplicate_opt_detection(); +#endif /* SFCTOOL */ + free_config_sections(); + iflags.parse_config_file_src = src; + + rv = parse_conf_file(fp, parse_config_line); + (void) fclose(fp); + + free_config_sections(); +#ifndef SFCTOOL + /* turn off detection of duplicate configfile options */ + reset_duplicate_opt_detection(); +#endif /* SFCTOOL */ + return rv; +} + +struct _cnf_parser_state { + char *inbuf; + unsigned inbufsz; + int rv; + char *ep; + char *buf; + boolean skip, morelines; + boolean cont; + boolean pbreak; +}; + +/* Initialize config parser data */ +staticfn void +cnf_parser_init(struct _cnf_parser_state *parser) +{ + parser->rv = TRUE; /* assume successful parse */ + parser->ep = parser->buf = (char *) 0; + parser->skip = FALSE; + parser->morelines = FALSE; + parser->inbufsz = 4 * BUFSZ; + parser->inbuf = (char *) alloc(parser->inbufsz); + parser->cont = FALSE; + parser->pbreak = FALSE; + memset(parser->inbuf, 0, parser->inbufsz); +} + +/* caller has finished with 'parser' (except for 'rv' so leave that intact) */ +staticfn void +cnf_parser_done(struct _cnf_parser_state *parser) +{ + parser->ep = 0; /* points into parser->inbuf, so becoming stale */ + if (parser->inbuf) + free(parser->inbuf), parser->inbuf = 0; + if (parser->buf) + free(parser->buf), parser->buf = 0; +} + +/* + * Parse config buffer, handling comments, empty lines, config sections, + * CHOOSE, and line continuation, calling proc for every valid line. + * + * Continued lines are merged together with one space in between. + */ +staticfn void +parse_conf_buf(struct _cnf_parser_state *p, boolean (*proc)(char *arg)) +{ + p->cont = FALSE; + p->pbreak = FALSE; + p->ep = strchr(p->inbuf, '\n'); + if (p->skip) { /* in case previous line was too long */ + if (p->ep) + p->skip = FALSE; /* found newline; next line is normal */ + } else { + if (!p->ep) { /* newline missing */ + if (strlen(p->inbuf) < (p->inbufsz - 2)) { + /* likely the last line of file is just + missing a newline; process it anyway */ + p->ep = eos(p->inbuf); + } else { + config_error_add("Line too long, skipping"); + p->skip = TRUE; /* discard next fgets */ + } + } else { + *p->ep = '\0'; /* remove newline */ + } + if (p->ep) { + char *tmpbuf = (char *) 0; + int len; + boolean ignoreline = FALSE; + boolean oldline = FALSE; + + /* line continuation (trailing '\') */ + p->morelines = (--p->ep >= p->inbuf && *p->ep == '\\'); + if (p->morelines) + *p->ep = '\0'; + + /* trim off spaces at end of line */ + while (p->ep >= p->inbuf + && (*p->ep == ' ' || *p->ep == '\t' || *p->ep == '\r')) + *p->ep-- = '\0'; + + if (!config_error_nextline(p->inbuf)) { + p->rv = FALSE; + if (p->buf) + free(p->buf), p->buf = (char *) 0; + p->pbreak = TRUE; + return; + } + + p->ep = p->inbuf; + while (*p->ep == ' ' || *p->ep == '\t') + ++p->ep; + + /* ignore empty lines and full-line comment lines */ + if (!*p->ep || *p->ep == '#') + ignoreline = TRUE; + + if (p->buf) + oldline = TRUE; + + /* merge now read line with previous ones, if necessary */ + if (!ignoreline) { + len = (int) strlen(p->ep) + 1; /* +1: final '\0' */ + if (p->buf) + len += (int) strlen(p->buf) + 1; /* +1: space */ + tmpbuf = (char *) alloc(len); + *tmpbuf = '\0'; + if (p->buf) { + Strcat(strcpy(tmpbuf, p->buf), " "); + free(p->buf), p->buf = 0; + } + p->buf = strcat(tmpbuf, p->ep); + if (strlen(p->buf) >= p->inbufsz) + p->buf[p->inbufsz - 1] = '\0'; + } + + if (p->morelines || (ignoreline && !oldline)) + return; + + if (handle_config_section(p->buf)) { + free(p->buf), p->buf = (char *) 0; + return; + } + + /* from here onwards, we'll handle buf only */ + + if (match_varname(p->buf, "CHOOSE", 6)) { + char *section; + char *bufp = find_optparam(p->buf); + + if (!bufp) { + config_error_add("Format is CHOOSE=section1" + ",section2,..."); + p->rv = FALSE; + free(p->buf), p->buf = (char *) 0; + return; + } + bufp++; + if (gc.config_section_chosen) + free(gc.config_section_chosen), + gc.config_section_chosen = 0; + section = choose_random_part(bufp, ','); + if (section) { + gc.config_section_chosen = dupstr(section); + } else { + config_error_add("No config section to choose"); + p->rv = FALSE; + } + free(p->buf), p->buf = (char *) 0; + return; + } + + if (!(*proc)(p->buf)) + p->rv = FALSE; + + free(p->buf), p->buf = (char *) 0; + } + } +} + +boolean +parse_conf_str(const char *str, boolean (*proc)(char *arg)) +{ + size_t len; + struct _cnf_parser_state parser; + + cnf_parser_init(&parser); + free_config_sections(); + config_error_init(FALSE, "parse_conf_str", FALSE); + while (str && *str) { + len = 0; + while (*str && len < (parser.inbufsz-1)) { + parser.inbuf[len] = *str; + len++; + str++; + if (parser.inbuf[len-1] == '\n') + break; + } + parser.inbuf[len] = '\0'; + parse_conf_buf(&parser, proc); + if (parser.pbreak) + break; + } + cnf_parser_done(&parser); + + free_config_sections(); + config_error_done(); + return parser.rv; +} + +/* parse_conf_file + * + * Read from file fp, calling parse_conf_buf for each line. + */ +boolean +parse_conf_file(FILE *fp, boolean (*proc)(char *arg)) +{ + struct _cnf_parser_state parser; + + cnf_parser_init(&parser); + free_config_sections(); + + while (fgets(parser.inbuf, parser.inbufsz, fp)) { + parse_conf_buf(&parser, proc); + if (parser.pbreak) + break; + } + cnf_parser_done(&parser); + + free_config_sections(); + return parser.rv; +} + +DISABLE_WARNING_FORMAT_NONLITERAL + +void +config_error_add(const char *str, ...) +{ + va_list the_args; + + va_start(the_args, str); + vconfig_error_add(str, the_args); + va_end(the_args); +} + +staticfn void +vconfig_error_add(const char *str, va_list the_args) +{ /* start of vconf...() or of nested block in USE_OLDARG's conf...() */ + int vlen = 0; + char buf[BIGBUFSZ]; /* will be chopped down to BUFSZ-1 if longer */ + + vlen = vsnprintf(buf, sizeof buf, str, the_args); +#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) && defined(DEBUG) + if (vlen >= (int) sizeof buf) + panic("%s: truncation of buffer at %zu of %d bytes", + "config_error_add", sizeof buf, vlen); +#else + nhUse(vlen); +#endif + buf[BUFSZ - 1] = '\0'; + config_erradd(buf); +} + +#ifdef SYSCF +#ifdef SYSCF_FILE +void +assure_syscf_file(void) +{ + int fd; + +#ifdef WIN32 + /* We are checking that the sysconf exists ... lock the path */ + fqn_prefix_locked[SYSCONFPREFIX] = TRUE; +#endif + /* + * All we really care about is the end result - can we read the file? + * So just check that directly. + * + * Not tested on most of the old platforms (which don't attempt + * to implement SYSCF). + * Some ports don't like open()'s optional third argument; + * VMS overrides open() usage with a macro which requires it. + */ +#ifndef VMS +#if defined(NOCWD_ASSUMPTIONS) && defined(WIN32) + fd = open(fqname(SYSCF_FILE, SYSCONFPREFIX, 0), O_RDONLY); +#else + fd = open(SYSCF_FILE, O_RDONLY); +#endif +#else + fd = open(SYSCF_FILE, O_RDONLY, 0); +#endif + if (fd >= 0) { + /* readable */ + close(fd); + return; + } +#ifndef SFCTOOL + if (gd.deferred_showpaths) + do_deferred_showpaths(1); /* does not return */ +#endif + raw_printf("Unable to open SYSCF_FILE.\n"); + exit(EXIT_FAILURE); +} + +#endif /* SYSCF_FILE */ +#endif /* SYSCF */ + +/* ---------- END CONFIG FILE HANDLING ----------- */ + +/*cfgfiles.c*/ + diff --git a/src/cmd.c b/src/cmd.c index 725ce0d47..3eb8a634e 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 cmd.c $NHDT-Date: 1717967336 2024/06/09 21:08:56 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.729 $ */ +/* NetHack 3.7 cmd.c $NHDT-Date: 1736401574 2025/01/08 21:46:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.744 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -805,7 +805,7 @@ extcmd_via_menu(void) * ('w' in wizard mode) */ /* -3: two line menu header, 1 line menu footer (for prompt) */ one_per_line = (nchoices < ROWNO - 3); - accelerator = prevaccelerator = 0; + prevaccelerator = 0; acount = 0; for (i = 0; choices[i]; ++i) { accelerator = choices[i]->ef_txt[matchlevel]; @@ -934,7 +934,7 @@ domonability(void) } else if (is_vampire(uptr) || is_vampshifter(&gy.youmonst)) { return dopoly(); } else if (u.usteed && can_breathe(u.usteed->data)) { - (void) pet_ranged_attk(u.usteed); + (void) pet_ranged_attk(u.usteed, TRUE); return ECMD_TIME; } else if (Upolyd) { pline("Any special ability you may have is purely reflexive."); @@ -981,7 +981,7 @@ enter_explore_mode(void) void makemap_prepost(boolean pre, boolean wiztower) { - NHFILE tmpnhfp; + NHFILE *tmpnhfp; struct monst *mtmp; if (pre) { @@ -1029,9 +1029,9 @@ makemap_prepost(boolean pre, boolean wiztower) dobjsfree(); /* discard current level; "saving" is used to release dynamic data */ - zero_nhfile(&tmpnhfp); /* also sets fd to -1 as desired */ - tmpnhfp.mode = FREEING; - savelev(&tmpnhfp, ledger_no(&u.uz)); + tmpnhfp = get_freeing_nhfile(); + savelev(tmpnhfp, ledger_no(&u.uz)); + close_nhfile(tmpnhfp); } else { vision_reset(); gv.vision_full_recalc = 1; @@ -2140,7 +2140,7 @@ handler_rebind_keys_add(boolean keyfirst) pline("Bind which key? "); key = pgetchar(); - if (!key || key == '\027') + if (!key || key == '\033') return; } @@ -2205,7 +2205,7 @@ handler_rebind_keys_add(boolean keyfirst) pline("Bind which key? "); key = pgetchar(); - if (!key || key == '\027') + if (!key || key == '\033') return; } @@ -2266,6 +2266,75 @@ handler_rebind_keys(void) } } +void +handler_change_autocompletions(void) +{ + winid win; + anything any; + int i, n; + menu_item *picks = (menu_item *) 0; + int clr = NO_COLOR; + struct ext_func_tab *ec; + char buf[BUFSZ]; + + win = create_nhwindow(NHW_MENU); + start_menu(win, MENU_BEHAVE_STANDARD); + any = cg.zeroany; + + for (i = 0; i < extcmdlist_length; i++) { + ec = &extcmdlist[i]; + + if ((ec->flags & (INTERNALCMD|CMD_NOT_AVAILABLE)) != 0) + continue; + if (strlen(ec->ef_txt) < 2) + continue; + + any.a_int = (i + 1); + Sprintf(buf, "%c %s: %s", + (ec->flags & AUTOCOMP_ADJ) ? '*' : ' ', + ec->ef_txt, ec->ef_desc); + add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, clr, buf, + (ec->flags & AUTOCOMPLETE) + ? MENU_ITEMFLAGS_SELECTED : + MENU_ITEMFLAGS_NONE); + } + + end_menu(win, "Which commands autocomplete?"); + n = select_menu(win, PICK_ANY, &picks); + if (n >= 0) { + int j; + + for (i = 0; i < extcmdlist_length; i++) { + boolean setit = FALSE; + + ec = &extcmdlist[i]; + + if ((ec->flags & (INTERNALCMD|CMD_NOT_AVAILABLE)) != 0) + continue; + if (strlen(ec->ef_txt) < 2) + continue; + + Sprintf(buf, "%s", ec->ef_txt); + + for (j = 0; j < n; ++j) { + if (ec == &extcmdlist[(picks[j].item.a_int - 1)]) { + parseautocomplete(buf, TRUE); + setit = TRUE; + break; + } + } + + if (!setit) { + parseautocomplete(buf, FALSE); + } + } + if (n > 0) + free((genericptr_t) picks); + } + + destroy_nhwindow(win); +} + /* find extended command entries matching findstr. if findstr is NULL, returns all available entries. returns: number of matching extended commands, @@ -2972,8 +3041,12 @@ parseautocomplete(char *autocomplete, boolean condition) /* find and modify the extended command */ for (efp = extcmdlist; efp->ef_txt; efp++) { if (!strcmp(autocomplete, efp->ef_txt)) { - if (condition == ((efp->flags & AUTOCOMPLETE) ? FALSE : TRUE)) - efp->flags |= AUTOCOMP_ADJ; + if (condition == ((efp->flags & AUTOCOMPLETE) ? FALSE : TRUE)) { + if ((efp->flags & AUTOCOMP_ADJ)) + efp->flags &= ~AUTOCOMP_ADJ; + else + efp->flags |= AUTOCOMP_ADJ; + } if (condition) efp->flags |= AUTOCOMPLETE; else @@ -3004,6 +3077,20 @@ all_options_autocomplete(strbuf_t *sbuf) } } +/* return the number of changed autocompletions */ +int +count_autocompletions(void) +{ + struct ext_func_tab *efp; + int n = 0; + + for (efp = extcmdlist; efp->ef_txt; efp++) + if ((efp->flags & AUTOCOMP_ADJ) != 0) + n++; + + return n; +} + /* save&clear the mouse button actions, or restore the saved ones */ void lock_mouse_buttons(boolean savebtns) @@ -3197,12 +3284,20 @@ accept_menu_prefix(const struct ext_func_tab *ec) return (ec && ((ec->flags & CMD_M_PREFIX) != 0)); } +/* choose a random character, biased towards movement commands, primarily + for debug-fuzzer testing */ char randomkey(void) { static unsigned i = 0; + static char last_c = '\0'; char c; + /* give ^A and ^P a high probability of being repeated */ + if ((last_c == C('a') || last_c == C('p')) + && program_state.input_state == commandInp && rn2(5)) + return last_c; + switch (rn2(16)) { default: c = '\033'; @@ -3250,6 +3345,8 @@ randomkey(void) break; } + if (program_state.input_state == commandInp) + last_c = c; return c; } @@ -3394,7 +3491,6 @@ rhack(int key) } res = ECMD_FAIL; prefix_seen = 0; - was_m_prefix = FALSE; } else { /* we discard 'const' because some compilers seem to have trouble with the pointer passed to set_occupation() */ @@ -3441,7 +3537,6 @@ rhack(int key) return; } prefix_seen = tlist; - bad_command = FALSE; cmdq_ec = NULL; if (func == do_reqmenu) was_m_prefix = TRUE; @@ -3476,7 +3571,6 @@ rhack(int key) return; } prefix_seen = 0; - was_m_prefix = FALSE; } /* it is possible to have a result of (ECMD_TIME|ECMD_CANCEL) [for example, using 'f'ire, manually filling quiver with @@ -3507,9 +3601,10 @@ rhack(int key) } if (bad_command) { - Norep("Unknown command '%s'.", visctrl(key)); + custompline(SUPPRESS_HISTORY, "Unknown command '%s'.", visctrl(key)); cmdq_clear(CQ_CANNED); cmdq_clear(CQ_REPEAT); + iflags.sanity_no_check = iflags.sanity_check; /* skip sanity check */ } /* didn't move */ svc.context.move = FALSE; @@ -3518,7 +3613,7 @@ rhack(int key) } /* convert an x,y pair into a direction code */ -coordxy +int xytod(coordxy x, coordxy y) { int dd; @@ -3655,16 +3750,36 @@ getdir(const char *s) retry: program_state.input_state = getdirInp; - if (gi.in_doagain || *readchar_queue) + if (gi.in_doagain || *readchar_queue) { dirsym = readchar(); - else + } else { dirsym = yn_function((s && *s != '^') ? s : "In what direction?", (char *) 0, '\0', FALSE); + + /* for the fuzzer, usually force the result to be a valid direction, + but sometimes let it exercise the invalid direction code; we + don't try to enforce no-diagonal for hero in grid bug form since + things like '^' to look at adjacent trap shouldn't be bound by + that (caller is expected to handle situations where it matters) */ + if (iflags.debug_fuzzer && rn2(20)) { + switch (rn2(20)) { + case 0: + dirsym = gc.Cmd.spkeys[rn2(2) ? NHKF_GETDIR_SELF : NHKF_ESC]; + break; + case 1: + dirsym = gc.Cmd.dirchars[rn2(2) ? DIR_DOWN : DIR_UP]; + break; + default: + dirsym = gc.Cmd.dirchars[rn2(N_DIRS)]; + break; + } + } + } /* remove the prompt string so caller won't have to */ clear_nhwindow(WIN_MESSAGE); if (redraw_cmd(dirsym)) { /* ^R */ - docrt(); /* redraw */ + docrt_flags(docrtRefresh); /* redraw */ goto retry; } if (!gi.in_doagain) @@ -4841,6 +4956,9 @@ end_of_input(void) program_state.something_worth_saving = 0; /* don't save */ #endif + if (In_tutorial(&u.uz)) + program_state.something_worth_saving = 0; /* don't save in tutorial */ + #ifndef SAFERHANGUP if (!program_state.done_hup++) #endif @@ -5154,6 +5272,28 @@ yn_function( res = cq.key; else cmdq_clear(CQ_CANNED); /* 'res' is ESC */ + addcmdq = FALSE; + + /* for the fuzzer, usually force a valid response, but sometimes let + it exercise windowport yn_function and invalid response handling */ + } else if (iflags.debug_fuzzer && resp && *resp && rn2(20)) { + int ln = (int) strlen(resp), ridx = rn2(ln); + + res = resp[ridx]; + /* if valid-responses includes ESC followed by unshown candidates + and we randomly picked the ESC, try again with only whatever is + before it; be careful to avoid rn2(0) */ + if (res == '\033') { + if (ln > 1) { + /* if ESC is at start (ridx==0), pick something after it */ + ridx = (ridx == 0) ? (1 + rn2(ln - 1)) : rn2(ridx); + res = resp[ridx]; + } else { + /* ESC is the only thing (ln==1); something is strange... */ + res = def; /* might be '\0' */ + } + } + } else { #ifdef SND_SPEECH if ((gp.pline_flags & PLINE_SPEECH) != 0) { @@ -5164,9 +5304,9 @@ yn_function( if (!yn_function_menu(query, resp, def, &res)) { res = (*windowprocs.win_yn_function)(query, resp, def); } - if (addcmdq) - cmdq_add_key(CQ_REPEAT, res); } + if (addcmdq) + cmdq_add_key(CQ_REPEAT, res); #ifdef DUMPLOG_CORE if (idx == gs.saved_pline_index) { @@ -5179,15 +5319,28 @@ yn_function( } #endif /* should not happen but cq.key has been observed to not obey 'resp'; - do this after dumplog has recorded the potentially bad value */ + 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)) { /* 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) */ int altres = def ? def : '\033'; - impossible("yn_function() returned '%s'; using '%s' instead", - visctrl(res), visctrl(altres)); + if (!gi.in_doagain || wizard) { +/*TEMP*/ xint8 fuzzing = iflags.debug_fuzzer; + char dbg_buf[BUFSZ]; + + Snprintf(dbg_buf, sizeof dbg_buf, "%s [%s] (%s)", + query, resp ? resp : "", def ? visctrl(def) : ""); + paniclog("yn debug", dbg_buf); +/*TEMP*/ /* don't let this known problem kill the fuzzer */ +/*TEMP*/ iflags.debug_fuzzer = fuzzer_impossible_continue; + impossible("yn_function() returned '%s'; using '%s' instead", + visctrl(res), visctrl(altres)); +/*TEMP*/ iflags.debug_fuzzer = fuzzing; + } res = altres; } /* in case we're called via getdir() which sets input_state */ diff --git a/src/coloratt.c b/src/coloratt.c index 8e68a469d..436e9274e 100644 --- a/src/coloratt.c +++ b/src/coloratt.c @@ -1,9 +1,8 @@ -/* NetHack 3.7 coloratt.c $NHDT-Date: 1710792438 2024/03/18 20:07:18 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.2 $ */ +/* NetHack 3.7 coloratt.c $NHDT-Date: 1737286550 2025/01/19 03:35:50 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.14 $ */ /* Copyright (c) Pasi Kallinen, 2024 */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" -#include struct color_names { const char *name; @@ -813,7 +812,6 @@ rgbstr_to_int32(const char *rgbstr) rgbstr ? rgbstr : ""); if (*buf && onlyhexdigits(buf)) { - r = g = b = 0; c_g = c_b = (char *) 0; c_r = cp = buf; while (*cp) { diff --git a/src/date.c b/src/date.c index aef985e75..3ec4fbb3d 100644 --- a/src/date.c +++ b/src/date.c @@ -29,15 +29,13 @@ struct nomakedefs_s nomakedefs = { "Version 1.0, built Jul 28 13:18:57 1987.", (const char *) 0, /* git_sha */ (const char *) 0, /* git_branch */ - (const char *) 0, /* git_prefix */ + (const char *) 0, /* git_prefix */ "1.0.0-0", "NetHack Version 1.0.0-0 - last build Tue Jul 28 13:18:57 1987.", 0x01010000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, - 0x00000000UL, - 0x00000000UL, 554476737UL, }; @@ -113,8 +111,6 @@ populate_nomakedefs(struct version_info *version) nomakedefs.version_features = version->feature_set; nomakedefs.ignored_features = md_ignored_features(); nomakedefs.version_sanity1 = version->entity_count; - nomakedefs.version_sanity2 = version->struct_sizes1; - nomakedefs.version_sanity3 = version->struct_sizes2; nomakedefs.version_string = dupstr(mdlib_version_string(tmpbuf2, ".")); nomakedefs.version_id = dupstr( version_id_string(tmpbuf2, sizeof tmpbuf2, nomakedefs.build_date)); diff --git a/src/dbridge.c b/src/dbridge.c index 8e2f081bd..5e739d4fa 100644 --- a/src/dbridge.c +++ b/src/dbridge.c @@ -255,6 +255,7 @@ create_drawbridge(coordxy x, coordxy y, int dir, boolean flag) break; default: impossible("bad direction in create_drawbridge"); + FALLTHROUGH; /*FALLTHRU*/ case DB_WEST: horiz = FALSE; diff --git a/src/decl.c b/src/decl.c index 08b54e4f7..a7e52dd80 100644 --- a/src/decl.c +++ b/src/decl.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 decl.c $NHDT-Date: 1725138480 2024/08/31 21:08:00 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.337 $ */ +/* NetHack 3.7 decl.c $NHDT-Date: 1736530208 2025/01/10 09:30:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.341 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2009. */ /* NetHack may be freely redistributed. See license for details. */ @@ -51,29 +51,6 @@ const struct c_common_strings c_common_strings = { "mon", "you" } }; -static const struct savefile_info default_sfinfo = { -#ifdef NHSTDC - 0x00000000UL -#else - 0x00000000L -#endif -#if defined(COMPRESS) || defined(ZLIB_COMP) - | SFI1_EXTERNALCOMP -#endif -#if defined(ZEROCOMP) - | SFI1_ZEROCOMP -#endif -#if defined(RLECOMP) - | SFI1_RLECOMP -#endif - , -#ifdef NHSTDC - 0x00000000UL, 0x00000000UL -#else - 0x00000000L, 0x00000000L -#endif -}; - const char disclosure_options[] = "iavgco"; char emptystr[] = {0}; /* non-const */ @@ -117,7 +94,6 @@ const char *materialnm[] = { "mysterious", "liquid", "wax", "organic", "platinum", "mithril", "plastic", "glass", "gemstone", "stone" }; const char quitchars[] = " \r\n\033"; -NEARDATA struct savefile_info sfcap, sfrestinfo, sfsaveinfo; const int shield_static[SHIELD_COUNT] = { S_ss1, S_ss2, S_ss3, S_ss2, S_ss1, S_ss2, S_ss4, /* 7 per row */ S_ss1, S_ss2, S_ss3, S_ss2, S_ss1, S_ss2, S_ss4, @@ -141,13 +117,10 @@ const char ynNaqchars[] = "yn#aq"; const char rightleftchars[] = "rl"; const char hidespinchars[] = "hsq"; NEARDATA long yn_number = 0L; - #ifdef PANICTRACE const char *ARGV0; #endif -#define IVMAGIC 0xdeadbeef - static const struct Role urole_init_data = { { "Undefined", 0 }, { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, @@ -228,12 +201,11 @@ static const struct instance_globals_a g_init_a = { /* trap.c */ { 0, 0, FALSE }, /* acid_ctx */ TRUE, /* havestate*/ - IVMAGIC /* a_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_b g_init_b = { /* botl.c */ - { { { NULL, NULL, 0L, FALSE, FALSE, 0, 0U, { 0 }, { 0 }, NULL, 0, 0, 0 + { { { NULL, NULL, 0L, FALSE, FALSE, 0, ANY_INVALID, { 0 }, { 0 }, NULL, 0, 0, 0 #ifdef STATUS_HILITES , UNDEFINED_PTR, UNDEFINED_PTR #endif @@ -261,7 +233,6 @@ static const struct instance_globals_b g_init_b = { FALSE, /* bot_disabled */ TRUE, /* havestate*/ - IVMAGIC /* b_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_c g_init_c = { @@ -311,8 +282,8 @@ static const struct instance_globals_c g_init_c = { UNDEFINED_PTR, /* coder */ /* uhitm.c */ NON_PM, /* corpsenm_digested */ + FALSE, /* converted_savefile_loaded */ TRUE, /* havestate*/ - IVMAGIC /* c_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_d g_init_d = { @@ -339,8 +310,9 @@ static const struct instance_globals_d g_init_d = { /* pickup.c */ FALSE, /* decor_fumble_override */ FALSE, /* decor_levitate_override */ + FALSE, /* deferred_showpaths */ + NULL, /* deferred_showpaths_dir */ TRUE, /* havestate*/ - IVMAGIC /* d_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_e g_init_e = { @@ -355,7 +327,6 @@ static const struct instance_globals_e g_init_e = { /* new */ 0, /* early_raw_messages */ TRUE, /* havestate*/ - IVMAGIC /* e_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_f g_init_f = { @@ -374,7 +345,6 @@ static const struct instance_globals_f g_init_f = { /* shk.c */ 0L, /* followmsg */ TRUE, /* havestate*/ - IVMAGIC /* f_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_g g_init_g = { @@ -402,6 +372,7 @@ static const struct instance_globals_g g_init_g = { 0L, /* gmst_moves */ NULL, /* gmst_invent */ NULL, NULL, NULL, /* gmst_ubak, gmst_disco, gmst_mvitals */ + { DUMMY }, /* gmst_spl_book */ /* pline.c */ UNDEFINED_PTR, /* gamelog */ /* region.c */ @@ -411,7 +382,6 @@ static const struct instance_globals_g g_init_g = { /* per-level glyph mapping flags */ 0L, /* glyphmap_perlevel_flags */ TRUE, /* havestate*/ - IVMAGIC /* g_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_h g_init_h = { @@ -430,7 +400,6 @@ static const struct instance_globals_h g_init_h = { NULL, /* hitmsg_prev */ /* save.c */ TRUE, /* havestate*/ - IVMAGIC /* h_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_i g_init_i = { @@ -453,14 +422,12 @@ static const struct instance_globals_i g_init_i = { FALSE, /* in_mk_themerooms */ TRUE, /* havestate*/ - IVMAGIC /* i_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_j g_init_j = { /* apply.c */ 0, /* jumping_is_magic */ TRUE, /* havestate*/ - IVMAGIC /* j_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_k g_init_k = { @@ -470,7 +437,6 @@ static const struct instance_globals_k g_init_k = { /* read.c */ UNDEFINED_VALUE, /* known */ TRUE, /* havestate*/ - IVMAGIC /* k_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_l g_init_l = { @@ -520,7 +486,6 @@ static const struct instance_globals_l g_init_l = { DUMMY, /* lua_ver[LUA_VER_BUFSIZ] */ DUMMY, /* lua_copyright[LUA_COPYRIGHT_BUFSIZ] */ TRUE, /* havestate*/ - IVMAGIC /* l_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_m g_init_m = { @@ -543,6 +508,8 @@ static const struct instance_globals_m g_init_m = { UNDEFINED_PTR, /* migrating_mons */ /* dokick.c */ UNDEFINED_PTR, /* maploc */ + /* mhitm.c */ + UNDEFINED_PTR, /* mswallower */ /* mhitu.c */ UNDEFINED_VALUE, /* mhitu_dieroll */ /* mklev.c */ @@ -569,7 +536,6 @@ static const struct instance_globals_m g_init_m = { /* trap.c */ FALSE, /* mentioned_water */ TRUE, /* havestate*/ - IVMAGIC /* m_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_n g_init_n = { @@ -604,7 +570,6 @@ static const struct instance_globals_n g_init_n = { STRANGE_OBJECT, /* nocreate3 */ STRANGE_OBJECT, /* nocreate4 */ TRUE, /* havestate*/ - IVMAGIC /* n_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_o g_init_o = { @@ -639,16 +604,13 @@ static const struct instance_globals_o g_init_o = { 0, /* oldcap */ /* restore.c */ UNDEFINED_PTR, /* oldfruit */ - 0L, /* omoves */ /* rumors.c */ 0, /* oracle_flag */ - UNDEFINED_PTR, /* oracle_loc */ /* uhitm.c */ FALSE, /* override_confirmation */ /* zap.c */ FALSE, /* obj_zapped */ TRUE, /* havestate*/ - IVMAGIC /* o_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_p g_init_p = { @@ -684,12 +646,10 @@ static const struct instance_globals_p g_init_p = { /* zap.c */ UNDEFINED_VALUE, /* poly_zap */ TRUE, /* havestate*/ - IVMAGIC /* p_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_q g_init_q = { TRUE, /* havestate*/ - IVMAGIC /* q_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_r g_init_r = { @@ -710,7 +670,6 @@ static const struct instance_globals_r g_init_r = { /* shk.c */ UNDEFINED_VALUES, /* repo */ TRUE, /* havestate*/ - IVMAGIC /* r_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_s g_init_s = { @@ -767,7 +726,6 @@ static const struct instance_globals_s g_init_s = { /* vision.c */ 0, /* seethru */ TRUE, /* havestate*/ - IVMAGIC /* s_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_t g_init_t = { @@ -802,7 +760,6 @@ static const struct instance_globals_t g_init_t = { 0, /* twohits */ /**/ TRUE, /* havestate*/ - IVMAGIC /* t_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_u g_init_u = { @@ -816,7 +773,6 @@ static const struct instance_globals_u g_init_u = { /* save.c */ { 0, 0 }, /* uz_save */ TRUE, /* havestate*/ - IVMAGIC /* u_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_v g_init_v = { @@ -842,13 +798,14 @@ static const struct instance_globals_v g_init_v = { FALSE, /* vision_full_recalc */ UNDEFINED_VALUES, /* voice */ TRUE, /* havestate*/ - IVMAGIC /* v_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_w g_init_w = { /* decl.c */ 0, /* warn_obj_cnt */ 0L, /* wailmsg */ + /* do_wear.c */ + 0U, /* wasinwater */ /* symbols.c */ DUMMY, /* warnsyms */ /* files.c */ @@ -859,8 +816,8 @@ static const struct instance_globals_w g_init_w = { UNDEFINED_PTR, /* wportal */ /* new */ { wdmode_traditional, NO_COLOR }, /* wsettings */ + 0L, /* were.c, allmain.c */ TRUE, /* havestate*/ - IVMAGIC /* w_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_x g_init_x = { @@ -874,7 +831,6 @@ static const struct instance_globals_x g_init_x = { UNDEFINED_VALUE, /* xstart */ UNDEFINED_VALUE, /* xsize */ TRUE, /* havestate*/ - IVMAGIC /* x_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_y g_init_y = { @@ -888,7 +844,6 @@ static const struct instance_globals_y g_init_y = { UNDEFINED_VALUE, /* ystart */ UNDEFINED_VALUE, /* ysize */ TRUE, /* havestate*/ - IVMAGIC /* y_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_z g_init_z = { @@ -897,7 +852,6 @@ static const struct instance_globals_z g_init_z = { /* muse.c */ FALSE, /* zap_oseen */ TRUE, /* havestate*/ - IVMAGIC /* z_magic to validate that structure layout has been preserved */ }; static const struct instance_globals_saved_b init_svb = { @@ -978,7 +932,11 @@ static const struct instance_globals_saved_n init_svn = { static const struct instance_globals_saved_o init_svo = { /* rumors.c */ - 0U /* oracle_cnt */ + 0U, /* oracle_cnt */ + UNDEFINED_PTR, /* oracle_loc */ + + /* other */ + 0L /* omoves */ }; static const struct instance_globals_saved_p init_svp = { @@ -1092,12 +1050,6 @@ const struct const_globals cg = { #define MAGICCHECK(xx) \ do { \ - if ((xx).magic != IVMAGIC) { \ - raw_printf( \ - "decl_globals_init: %s.magic in unexpected state (%lx).", \ - #xx, (xx).magic); \ - exit(1); \ - } \ if ((xx).havestate != TRUE) { \ raw_printf( \ "decl_globals_init: %s.havestate not True.", #xx); \ @@ -1164,7 +1116,7 @@ decl_globals_init(void) gv.valuables[1].size = SIZE(ga.amulets); gv.valuables[2].list = NULL; gv.valuables[2].size = 0; - + #if 0 MAGICCHECK(g_init); #endif @@ -1195,10 +1147,6 @@ decl_globals_init(void) MAGICCHECK(g_init_y); MAGICCHECK(g_init_z); - sfcap = default_sfinfo; - sfrestinfo = default_sfinfo; - sfsaveinfo = default_sfinfo; - gs.subrooms = &svr.rooms[MAXNROFROOMS + 1]; ZERO(flags); diff --git a/src/detect.c b/src/detect.c index 7ccce8911..61e5dd3d6 100644 --- a/src/detect.c +++ b/src/detect.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 detect.c $NHDT-Date: 1721684299 2024/07/22 21:38:19 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.180 $ */ +/* NetHack 3.7 detect.c $NHDT-Date: 1745114235 2025/04/19 17:57:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.190 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -38,6 +38,7 @@ staticfn void foundone(coordxy, coordxy, int); staticfn void findone(coordxy, coordxy, genericptr_t); staticfn void openone(coordxy, coordxy, genericptr_t); staticfn int mfind0(struct monst *, boolean); +staticfn boolean skip_premap_detect(coordxy, coordxy); staticfn int reveal_terrain_getglyph(coordxy, coordxy, unsigned, int, unsigned); @@ -930,6 +931,8 @@ detect_obj_traps( continue; } if (Is_box(otmp) && otmp->otrapped) { + otmp->tknown = 1; + otmp->dknown = 1; result |= u_at(x, y) ? OTRAP_HERE : OTRAP_THERE; if (ft) { flash_glyph_at(x, y, trapglyph, FOUND_FLASH_COUNT); @@ -1402,7 +1405,7 @@ show_map_spot(coordxy x, coordxy y, boolean cnf) if (!IS_FURNITURE(lev->typ)) { if ((t = t_at(x, y)) != 0 && t->tseen) { map_trap(t, 1); - } else if ((ep = engr_at(x, y)) != 0) { + } else if ((ep = engr_at(x, y)) != 0 && !cnf) { map_engraving(ep, 1); } else if (glyph_is_trap(oldglyph) || glyph_is_object(oldglyph)) { show_glyph(x, y, oldglyph); @@ -1581,7 +1584,7 @@ do_vicinity_map( docrt(); } -/* convert a secret door into a normal door */ +/* convert a secret door into a normal door; it might be trapped */ void cvt_sdoor_to_door(struct rm *lev) { @@ -1597,6 +1600,7 @@ cvt_sdoor_to_door(struct rm *lev) } lev->typ = DOOR; lev->doormask = newmask; + lev->arboreal_sdoor = 0; /* clears 'candig' */ } /* update the map for something which has just been found by wand of secret @@ -1649,6 +1653,7 @@ findone(coordxy zx, coordxy zy, genericptr_t whatfound) flash_glyph_at(zx, zy, cmap_to_glyph(sym), FOUND_FLASH_COUNT); cvt_sdoor_to_door(lev); /* set lev->typ = DOOR */ + recalc_block_point(zx, zy); magic_map_background(zx, zy, 0); foundone(zx, zy, back_to_glyph(zx, zy)); found_p->num_sdoors++; @@ -2038,6 +2043,7 @@ dosearch0(int aflag) /* intrinsic autosearch vs explicit searching */ if (rnl(7 - fund)) continue; cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */ + recalc_block_point(x, y); exercise(A_WIS, TRUE); nomul(0); feel_location(x, y); /* make sure it shows up */ @@ -2113,6 +2119,16 @@ warnreveal(void) } } +/* skip premap detection of areas outside Sokoban map */ +staticfn boolean +skip_premap_detect(coordxy x, coordxy y) +{ + if ((levl[x][y].typ == STONE) + && (levl[x][y].wall_info & (W_NONDIGGABLE | W_NONPASSWALL)) != 0) + return TRUE; + return FALSE; +} + /* Pre-map (the sokoban) levels */ void premap_detect(void) @@ -2124,6 +2140,8 @@ premap_detect(void) /* Map the background and boulders */ for (x = 1; x < COLNO; x++) for (y = 0; y < ROWNO; y++) { + if (skip_premap_detect(x, y)) + continue; levl[x][y].seenv = SVALL; levl[x][y].waslit = TRUE; if (levl[x][y].typ == SDOOR) diff --git a/src/dig.c b/src/dig.c index ed0879683..579a97013 100644 --- a/src/dig.c +++ b/src/dig.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 dig.c $NHDT-Date: 1724613307 2024/08/25 19:15:07 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.219 $ */ +/* NetHack 3.7 dig.c $NHDT-Date: 1740629713 2025/02/26 20:15:13 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.227 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -56,7 +56,7 @@ mkcavepos(coordxy x, coordxy y, int dist, boolean waslit, boolean rockit) if (rockit) { struct monst *mtmp; - if (IS_ROCK(lev->typ)) + if (IS_OBSTRUCTED(lev->typ)) return; if (t_at(x, y)) return; /* don't cover the portal */ @@ -168,29 +168,27 @@ pick_can_reach(struct obj *pick, coordxy x, coordxy y) int dig_typ(struct obj *otmp, coordxy x, coordxy y) { - boolean ispick; + int ltyp; - if (!otmp) - return DIGTYP_UNDIGGABLE; - ispick = is_pick(otmp); - if (!ispick && !is_axe(otmp)) + if (!isok(x, y) || !otmp || (!is_pick(otmp) && !is_axe(otmp))) return DIGTYP_UNDIGGABLE; - return ((ispick && sobj_at(STATUE, x, y) - && pick_can_reach(otmp, x, y)) - ? DIGTYP_STATUE - : (ispick && sobj_at(BOULDER, x, y) - && pick_can_reach(otmp, x, y)) - ? DIGTYP_BOULDER - : closed_door(x, y) - ? DIGTYP_DOOR - : IS_TREE(levl[x][y].typ) - ? (ispick ? DIGTYP_UNDIGGABLE : DIGTYP_TREE) - : (ispick && IS_ROCK(levl[x][y].typ) - && (!svl.level.flags.arboreal - || IS_WALL(levl[x][y].typ))) - ? DIGTYP_ROCK - : DIGTYP_UNDIGGABLE); + ltyp = levl[x][y].typ; + if (is_axe(otmp)) + return closed_door(x, y) ? DIGTYP_DOOR + : IS_TREE(ltyp) ? DIGTYP_TREE /* axe vs tree */ + : DIGTYP_UNDIGGABLE; + /*assert(is_pick(otmp));*/ + return (sobj_at(STATUE, x, y) && pick_can_reach(otmp, x, y)) + ? DIGTYP_STATUE + : (sobj_at(BOULDER, x, y) && pick_can_reach(otmp, x, y)) + ? DIGTYP_BOULDER + : closed_door(x, y) ? DIGTYP_DOOR + : IS_TREE(ltyp) ? DIGTYP_UNDIGGABLE /* pick vs tree */ + : (IS_OBSTRUCTED(ltyp) + && (!svl.level.flags.arboreal || IS_WALL(ltyp))) + ? DIGTYP_ROCK + : DIGTYP_UNDIGGABLE; } boolean @@ -227,7 +225,7 @@ dig_check(struct monst *madeby, coordxy x, coordxy y) return DIGCHECK_FAIL_AIRLEVEL; } else if (Is_waterlevel(&u.uz)) { return DIGCHECK_FAIL_WATERLEVEL; - } else if ((IS_ROCK(levl[x][y].typ) && levl[x][y].typ != SDOOR + } else if ((IS_OBSTRUCTED(levl[x][y].typ) && levl[x][y].typ != SDOOR && (levl[x][y].wall_info & W_NONDIGGABLE) != 0)) { return DIGCHECK_FAIL_TOOHARD; } else if (ttmp && undestroyable_trap(ttmp->ttyp)) { @@ -328,7 +326,7 @@ dig(void) pline("This tree seems to be petrified."); return 0; } - if (IS_ROCK(lev->typ) && !may_dig(dpx, dpy) + if (IS_OBSTRUCTED(lev->typ) && !may_dig(dpx, dpy) && dig_typ(uwep, dpx, dpy) == DIGTYP_ROCK) { pline("This %s is too hard to %s.", is_db_wall(dpx, dpy) ? "drawbridge" : "wall", verb); @@ -440,22 +438,24 @@ dig(void) } if (svc.context.digging.effort > 100) { + char digbuf[BUFSZ]; const char *digtxt, *dmgtxt = (const char *) 0; - struct obj *obj; + struct obj *obj, *bobj; boolean shopedge = *in_rooms(dpx, dpy, SHOPBASE); + int digtyp = dig_typ(uwep, dpx, dpy); - if ((obj = sobj_at(STATUE, dpx, dpy)) != 0) { + if (digtyp == DIGTYP_STATUE + && (obj = sobj_at(STATUE, dpx, dpy)) != 0) { if (break_statue(obj)) digtxt = "The statue shatters."; else /* it was a statue trap; break_statue() - * printed a message and updated the screen - */ + printed a message and updated the screen */ digtxt = (char *) 0; - } else if ((obj = sobj_at(BOULDER, dpx, dpy)) != 0) { - struct obj *bobj; - + } else if (digtyp == DIGTYP_BOULDER + && (obj = sobj_at(BOULDER, dpx, dpy)) != 0) { fracture_rock(obj); + /*[3.7: this probably isn't necessary anymore]*/ if ((bobj = sobj_at(BOULDER, dpx, dpy)) != 0) { /* another boulder here, restack it to the top */ obj_extract_self(bobj); @@ -474,7 +474,7 @@ dig(void) goto cleanup; } } - if (IS_TREE(lev->typ)) { + if (digtyp == DIGTYP_TREE) { digtxt = "You cut down the tree."; lev->typ = ROOM, lev->flags = 0; if (!rn2(5)) @@ -505,7 +505,9 @@ dig(void) if (!(lev->doormask & D_TRAPPED)) lev->doormask = D_BROKEN; } else if (closed_door(dpx, dpy)) { - digtxt = "You break through the door."; + Sprintf(digbuf, "You break through the door with your %s.", + simpleonames(uwep)); + digtxt = digbuf; if (shopedge) { add_damage(dpx, dpy, SHOP_DOOR_COST); dmgtxt = "break"; @@ -524,23 +526,15 @@ dig(void) pay_for_damage(dmgtxt, FALSE); if (Is_earthlevel(&u.uz) && !rn2(3)) { - struct monst *mtmp; + int mndx = rn2(2) ? PM_EARTH_ELEMENTAL : PM_XORN; - switch (rn2(2)) { - case 0: - mtmp = makemon(&mons[PM_EARTH_ELEMENTAL], dpx, dpy, - MM_NOMSG); - break; - default: - mtmp = makemon(&mons[PM_XORN], dpx, dpy, MM_NOMSG); - break; - } - if (mtmp) + if (makemon(&mons[mndx], dpx, dpy, MM_NOMSG)) pline_The("debris from your digging comes to life!"); } if (IS_DOOR(lev->typ) && (lev->doormask & D_TRAPPED)) { lev->doormask = D_NODOOR; b_trapped("door", NO_PART); + recalc_block_point(dpx, dpy); newsym(dpx, dpy); } cleanup: @@ -561,7 +555,7 @@ dig(void) return 0; } } else if (dig_target == DIGTYP_UNDIGGABLE - || (dig_target == DIGTYP_ROCK && !IS_ROCK(lev->typ))) + || (dig_target == DIGTYP_ROCK && !IS_OBSTRUCTED(lev->typ))) return 0; /* statue or boulder got taken */ if (!gd.did_dig_msg) { @@ -715,7 +709,10 @@ digactualhole(coordxy x, coordxy y, struct monst *madeby, int ttyp) pline("%s digs %s %s the %s.", Monnam(madeby), an(tname), in_thru, surface_type); } else if (cansee(x, y) && flags.verbose) { - pline("%s appears in the %s.", An(tname), surface_type); + if (IS_STWALL(old_typ)) + pline_The("%s crumbles into %s.", surface_type, an(tname)); + else + pline("%s appears in the %s.", An(tname), surface_type); } if (IS_FURNITURE(old_typ) && cansee(x, y)) pline_The("%s falls into the %s!", furniture, tname); @@ -859,8 +856,9 @@ liquid_flow( } if (ttmp) - (void) delfloortrap(ttmp); /* will untrap monster is one is here */ + (void) delfloortrap(ttmp); /* will untrap monster if one is here */ /* if any objects were frozen here, they're released now */ + obj_ice_effects(x, y, TRUE); unearth_objs(x, y); if (fillmsg) @@ -893,7 +891,7 @@ dighole(boolean pit_only, boolean by_magic, coord *cc) coordxy dig_x, dig_y; boolean nohole, retval = FALSE; enum digcheck_result dig_check_result; - + if (!cc) { dig_x = u.ux; dig_y = u.uy; @@ -913,7 +911,7 @@ dighole(boolean pit_only, boolean by_magic, coord *cc) old_typ = lev->typ; if ((ttmp && (undestroyable_trap(ttmp->ttyp) || nohole)) - || (IS_ROCK(old_typ) && old_typ != SDOOR + || (IS_OBSTRUCTED(old_typ) && old_typ != SDOOR && (lev->wall_info & W_NONDIGGABLE) != 0)) { pline_The("%s %shere is too hard to dig in.", surface(dig_x, dig_y), (dig_x != u.ux || dig_y != u.uy) ? "t" : ""); @@ -1230,7 +1228,7 @@ use_pick_axe2(struct obj *obj) (void) fire_damage(uwep, FALSE, rx, ry); } else if (IS_TREE(lev->typ)) { You("need an axe to cut down a tree."); - } else if (IS_ROCK(lev->typ)) { + } else if (IS_OBSTRUCTED(lev->typ)) { You("need a pick to dig rock."); } else if ((boulder = sobj_at(BOULDER, rx, ry)) != 0 || sobj_at(STATUE, rx, ry)) { @@ -1316,7 +1314,7 @@ use_pick_axe2(struct obj *obj) /* it must be air -- water checked above */ You("swing %s through thin air.", yobjnam(obj, (char *) 0)); } else if (!can_reach_floor(FALSE)) { - cant_reach_floor(u.ux, u.uy, FALSE, FALSE); + cant_reach_floor(u.ux, u.uy, FALSE, FALSE, FALSE); } else if (is_pool_or_lava(u.ux, u.uy)) { /* Monsters which swim also happen not to be able to dig */ You("cannot stay under%s long enough.", @@ -1326,7 +1324,7 @@ use_pick_axe2(struct obj *obj) dotrap(trap, FORCEBUNGLE); /* might escape trap and still be teetering at brink */ if (!u.utrap) - cant_reach_floor(u.ux, u.uy, FALSE, TRUE); + cant_reach_floor(u.ux, u.uy, FALSE, TRUE, FALSE); } else if (!ispick /* can only dig down with an axe when doing so will trigger or disarm a trap here */ @@ -1398,7 +1396,7 @@ watch_dig(struct monst *mtmp, coordxy x, coordxy y, boolean zap) str = "door"; else if (IS_TREE(lev->typ)) str = "tree"; - else if (IS_ROCK(lev->typ)) + else if (IS_OBSTRUCTED(lev->typ)) str = "wall"; else str = "fountain"; @@ -1431,7 +1429,7 @@ mdig_tunnel(struct monst *mtmp) sawit = canseemon(mtmp); /* before door state change and unblock_pt */ trapped = (here->doormask & D_TRAPPED) ? TRUE : FALSE; here->doormask = trapped ? D_NODOOR : D_BROKEN; - unblock_point(mtmp->mx, mtmp->my); /* vision */ + recalc_block_point(mtmp->mx, mtmp->my); /* vision */ newsym(mtmp->mx, mtmp->my); if (trapped) { seeit = canseemon(mtmp); @@ -1452,7 +1450,7 @@ mdig_tunnel(struct monst *mtmp) newsym(mtmp->mx, mtmp->my); draft_message(FALSE); /* "You feel a draft." */ return FALSE; - } else if (!IS_ROCK(here->typ) && !IS_TREE(here->typ)) { /* no dig */ + } else if (!IS_OBSTRUCTED(here->typ) && !IS_TREE(here->typ)) { /* no dig */ return FALSE; } @@ -1637,6 +1635,7 @@ zap_dig(void) if (diridx != DIR_ERR && !conjoined_pits(adjpit, trap_with_u, FALSE)) { digdepth = 0; /* limited to the adjacent location only */ + nhUse(digdepth); if (!(adjpit && is_pit(adjpit->ttyp))) { char buf[BUFSZ]; @@ -1678,7 +1677,7 @@ zap_dig(void) pline_The("door is razed!"); watch_dig((struct monst *) 0, zx, zy, TRUE); room->doormask = D_NODOOR; - unblock_point(zx, zy); /* vision */ + recalc_block_point(zx, zy); /* vision */ digdepth -= 2; if (maze_dig) break; @@ -1709,7 +1708,7 @@ zap_dig(void) pline_The("rock glows then fades."); break; } - } else if (IS_ROCK(room->typ)) { + } else if (IS_OBSTRUCTED(room->typ)) { if (!may_dig(zx, zy)) break; if (IS_WALL(room->typ) || room->typ == SDOOR) { @@ -1727,7 +1726,7 @@ zap_dig(void) } else if (IS_TREE(room->typ)) { room->typ = ROOM, room->flags = 0; digdepth -= 2; - } else { /* IS_ROCK but not IS_WALL or SDOOR */ + } else { /* IS_OBSTRUCTED but not IS_WALL or SDOOR */ room->typ = CORR, room->flags = 0; digdepth--; } @@ -2081,7 +2080,8 @@ bury_objs(int x, int y) } } -/* move objects from buriedobjlist to fobj/nexthere lists */ +/* move objects from buriedobjlist to fobj/nexthere lists; if caller + converts terrain from ice to something, it should call obj_ice_effects() */ void unearth_objs(int x, int y) { diff --git a/src/display.c b/src/display.c index fb198e97d..ba1841cca 100644 --- a/src/display.c +++ b/src/display.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 display.c $NHDT-Date: 1723834773 2024/08/16 18:59:33 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.244 $ */ +/* NetHack 3.7 display.c $NHDT-Date: 1745114235 2025/04/19 17:57:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.260 $ */ /* Copyright (c) Dean Luick, with acknowledgements to Kevin Darcy */ /* and Dave Cohrs, 1990. */ /* NetHack may be freely redistributed. See license for details. */ @@ -420,10 +420,13 @@ unmap_object(coordxy x, coordxy y) struct rm *lev = &levl[x][y]; if (spot_shows_engravings(x, y) - && (ep = engr_at(x, y)) != 0 && !covers_traps(x, y)) + && (ep = engr_at(x, y)) != 0 && !covers_traps(x, y)) { + if (cansee(x, y)) + ep->erevealed = 1; map_engraving(ep, 0); - else + } else { map_background(x, y, 0); + } /* turn remembered dark room squares dark */ if (!lev->waslit && lev->glyph == cmap_to_glyph(S_room) @@ -443,26 +446,29 @@ unmap_object(coordxy x, coordxy y) * Internal to display.c, this is a #define for speed. */ #define _map_location(x, y, show) \ - do { \ - struct obj *obj; \ - struct trap *trap; \ - struct engr *ep; \ - NhRegion *_ml_reg; \ - \ - if ((obj = vobj_at(x, y)) && !covers_objects(x, y)) \ - map_object(obj, show); \ - else if ((trap = t_at(x, y)) && trap->tseen && !covers_traps(x, y)) \ - map_trap(trap, show); \ - else if (spot_shows_engravings(x, y) \ - && (ep = engr_at(x, y)) != 0 \ - && !covers_traps(x, y)) \ - map_engraving(ep, show); \ - else \ - map_background(x, y, show); \ - \ - update_lastseentyp(x, y); \ - if (show && !Blind && (_ml_reg = visible_region_at(x, y)) != 0) \ - show_region(_ml_reg, x, y); \ + do { \ + struct obj *obj; \ + struct trap *trap; \ + struct engr *ml_ep; \ + NhRegion *_ml_reg; \ + \ + if ((obj = vobj_at(x, y)) && !covers_objects(x, y)) { \ + map_object(obj, show); \ + } else if ((trap = t_at(x, y)) \ + && trap->tseen && !covers_traps(x, y)) { \ + map_trap(trap, show); \ + } else if (spot_shows_engravings(x, y) \ + && (ml_ep = engr_at(x, y)) != 0 \ + && ml_ep->erevealed \ + && !covers_traps(x, y)) { \ + map_engraving(ml_ep, show); \ + } else { \ + map_background(x, y, show); \ + } \ + \ + update_lastseentyp(x, y); \ + if (show && !Blind && (_ml_reg = visible_region_at(x, y)) != 0) \ + show_region(_ml_reg, x, y); \ } while (0) void @@ -528,6 +534,7 @@ display_monster( default: impossible("display_monster: bad m_ap_type value [ = %d ]", (int) mon->m_ap_type); + FALLTHROUGH; /*FALLTHRU*/ case M_AP_NOTHING: show_glyph(x, y, mon_to_glyph(mon, newsym_rn2)); @@ -741,6 +748,7 @@ feel_location(coordxy x, coordxy y) struct rm *lev; struct obj *boulder; struct monst *mon; + struct engr *ep; /* replicate safeguards used by newsym(); might not be required here */ if (_suppress_map_output()) @@ -783,7 +791,7 @@ feel_location(coordxy x, coordxy y) * + Room/water positions * + Everything else (hallways!) */ - if (IS_ROCK(lev->typ) + if (IS_OBSTRUCTED(lev->typ) || (IS_DOOR(lev->typ) && (lev->doormask & (D_LOCKED | D_CLOSED)))) { map_background(x, y, 1); @@ -850,6 +858,9 @@ feel_location(coordxy x, coordxy y) show_glyph(x, y, lev->glyph = cmap_to_glyph(S_darkroom)); } } else { + if ((ep = engr_at(x, y)) != 0 && engr_can_be_felt(ep)) + ep->erevealed = 1; + _map_location(x, y, 1); if (Punished) { @@ -910,6 +921,7 @@ newsym(coordxy x, coordxy y) int see_it; boolean worm_tail; struct rm *lev = &(levl[x][y]); + struct engr *ep; /* don't try to produce map output when level is in a state of flux */ if (_suppress_map_output()) @@ -948,6 +960,8 @@ newsym(coordxy x, coordxy y) mon = m_at(x, y); worm_tail = is_worm_tail(mon); + if ((ep = engr_at(x, y)) != 0) + ep->erevealed = 1; /* even when covered by objects or a monster */ /* * Normal region shown only on accessible positions, but * poison clouds and steam clouds also shown above lava, @@ -1008,8 +1022,9 @@ newsym(coordxy x, coordxy y) display_warning(mon); } else if (glyph_is_invisible(lev->glyph)) { map_invisible(x, y); - } else + } else { _map_location(x, y, 1); /* map the location */ + } } /* Can't see the location. */ @@ -1983,8 +1998,9 @@ show_glyph(coordxy x, coordxy y, int glyph) oldglyph = gg.gbuf[y][x].glyphinfo.glyph; if (a11y.glyph_updates && !a11y.mon_notices_blocked - && !program_state.in_docrt - && !program_state.in_getlev + && !program_state.in_docrt && !program_state.gameover + && !program_state.in_getlev && !program_state.stopprint + && !_suppress_map_output() && (oldglyph != glyph || gg.gbuf[y][x].gnew)) { int c = glyph_to_cmap(glyph); @@ -2274,6 +2290,13 @@ back_to_glyph(coordxy x, coordxy y) case CORR: idx = (ptr->waslit || flags.lit_corridor) ? S_litcorr : S_corr; break; + case SDOOR: + if (ptr->arboreal_sdoor) { + idx = S_tree; + break; + } + FALLTHROUGH; + /*FALLTHRU*/ case HWALL: case VWALL: case TLCORNER: @@ -2285,7 +2308,6 @@ back_to_glyph(coordxy x, coordxy y) case TDWALL: case TLWALL: case TRWALL: - case SDOOR: idx = ptr->seenv ? wall_angle(ptr) : S_stone; break; case DOOR: @@ -3101,7 +3123,8 @@ check_pos(coordxy x, coordxy y, int which) if (!isok(x, y)) return which; type = levl[x][y].typ; - if (IS_ROCK(type) || type == CORR || type == SCORR) + /* Everything below POOL, excluding TREE */ + if (IS_STWALL(type) || type == CORR || type == SCORR || IS_SDOOR(type)) return which; return 0; } @@ -3563,8 +3586,13 @@ wall_angle(struct rm *lev) break; case SDOOR: + if (lev->arboreal_sdoor) { + idx = S_tree; + break; + } if (lev->horizontal) goto horiz; + FALLTHROUGH; /*FALLTHRU*/ case VWALL: switch (lev->wall_info & WM_MASK) { diff --git a/src/do.c b/src/do.c index 591537f12..ae26e7158 100644 --- a/src/do.c +++ b/src/do.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 do.c $NHDT-Date: 1704225560 2024/01/02 19:59:20 $ $NHDT-Branch: keni-luabits2 $:$NHDT-Revision: 1.376 $ */ +/* NetHack 3.7 do.c $NHDT-Date: 1737287889 2025/01/19 03:58:09 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.399 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -20,8 +20,9 @@ staticfn NHFILE *currentlevel_rewrite(void); staticfn void familiar_level_msg(void); staticfn void final_level(void); staticfn void temperature_change_msg(schar); +staticfn boolean better_not_try_to_drop_that(struct obj *); -/* static boolean badspot(coordxy,coordxy); */ + /* static boolean badspot(coordxy,coordxy); */ /* the #drop command: drop one inventory item */ int @@ -47,9 +48,9 @@ dodrop(void) */ boolean boulder_hits_pool( - struct obj *otmp, - coordxy rx, coordxy ry, - boolean pushing) + struct obj *otmp, /* the object falling into a pool or water or lava */ + coordxy rx, coordxy ry, /* coordinates of the pool */ + boolean pushing) /* for a boulder, whether or not it is being pushed */ { if (!otmp || otmp->otyp != BOULDER) { impossible("Not a boulder?"); @@ -74,6 +75,7 @@ boulder_hits_pool( levl[rx][ry].drawbridgemask |= DB_FLOOR; } else { levl[rx][ry].typ = ROOM, levl[rx][ry].flags = 0; + recalc_block_point(rx, ry); } /* 3.7: normally DEADMONSTER() is used when traversing the fmon list--dead monsters usually aren't still at specific map @@ -137,8 +139,9 @@ boulder_hits_pool( losehp(Maybe_Half_Phys(dmg), /* lava damage */ "molten lava", KILLED_BY); } else if (!fills_up && flags.verbose - && (pushing ? !Blind : cansee(rx, ry))) + && (pushing ? !Blind : cansee(rx, ry))) { pline("It sinks without a trace!"); + } } /* boulder is now gone */ @@ -156,7 +159,10 @@ boulder_hits_pool( * away. */ boolean -flooreffects(struct obj *obj, coordxy x, coordxy y, const char *verb) +flooreffects( + struct obj *obj, /* the object landing on the floor */ + coordxy x, coordxy y, /* map coordinates for spot where it is landing */ + const char *verb) /* "fall", "drop", "land", &c */ { struct trap *t; struct monst *mtmp; @@ -218,6 +224,7 @@ flooreffects(struct obj *obj, coordxy x, coordxy y, const char *verb) } if (!DEADMONSTER(mtmp) && !is_whirly(mtmp->data)) res = FALSE; /* still alive, boulder still intact */ + nhUse(res); } mtmp->mtrapped = 0; } else { @@ -235,12 +242,10 @@ flooreffects(struct obj *obj, coordxy x, coordxy y, const char *verb) You_hear("a CRASH! beneath you."); } else if (!Blind && cansee(x, y)) { pline_The("boulder %s%s.", - (ttyp == TRAPDOOR && !tseen) - ? "triggers and " : "", - (ttyp == TRAPDOOR) - ? "plugs a trap door" - : (ttyp == HOLE) ? "plugs a hole" - : "fills a pit"); + (ttyp == TRAPDOOR && !tseen) ? "triggers and " : "", + (ttyp == TRAPDOOR) ? "plugs a trap door" + : (ttyp == HOLE) ? "plugs a hole" + : "fills a pit"); } else { Soundeffect(se_boulder_drop, 100); You_hear("a boulder %s.", verb); @@ -251,10 +256,13 @@ flooreffects(struct obj *obj, coordxy x, coordxy y, const char *verb) * || mondied) -> mondead -> m_detach -> fill_pit. */ deletedwithboulder: - if ((t = t_at(x, y)) != 0) - deltrap(t); - if (u.utrap && u_at(x, y)) - reset_utrap(FALSE); + /* creating a pit in ice results in that ice being turned into + floor so we shouldn't need any special ice handing here */ + if ((t = t_at(x, y)) != 0) { + (void) delfloortrap(t); + if (u.utrap && u_at(x, y)) + reset_utrap(FALSE); + } useupf(obj, 1L); bury_objs(x, y); newsym(x, y); @@ -267,7 +275,7 @@ flooreffects(struct obj *obj, coordxy x, coordxy y, const char *verb) * noise. Stuff dropped near fountains always misses */ if ((Blind || (Levitation || Flying)) && !Deaf && u_at(x, y)) { if (!Underwater) { - if (weight(obj) > 9) { + if (weight(obj) > WT_SPLASH_THRESHOLD) { pline("Splash!"); } else if (Levitation || Flying) { pline("Plop!"); @@ -709,6 +717,8 @@ drop(struct obj *obj) return ECMD_FAIL; if (!canletgo(obj, "drop")) return ECMD_FAIL; + if (obj->otyp == CORPSE && better_not_try_to_drop_that(obj)) + return ECMD_FAIL; if (obj == uwep) { if (welded(uwep)) { weldmsg(obj); @@ -867,7 +877,7 @@ engulfer_digests_food(struct obj *obj) } else if (could_grow) { (void) grow_up(u.ustuck, (struct monst *) 0); } else if (could_heal) { - u.ustuck->mhp = u.ustuck->mhpmax; + healmon(u.ustuck, u.ustuck->mhpmax, 0); /* False: don't realize that sight is cured from inside */ mcureblindness(u.ustuck, FALSE); } @@ -933,6 +943,23 @@ doddrop(void) return result; } +staticfn boolean +better_not_try_to_drop_that(struct obj *otmp) +{ + char buf[BUFSZ]; + + /* u_safe_from_fatal_corpse() with st_all checks for gloves and stoning + * resistance before bothering to prompt you. + */ + if (otmp->otyp == CORPSE && !u_safe_from_fatal_corpse(otmp, st_all)) { + Snprintf( + buf, sizeof buf, + "Drop the %s corpse without %s protection on?", + obj_pmname(otmp), body_part(HAND)); + return (paranoid_ynq(TRUE, buf, FALSE) != 'y'); + } + return FALSE; +} staticfn int /* check callers */ menudrop_split(struct obj *otmp, long cnt) { @@ -1681,7 +1708,6 @@ goto_level( } reseed_random(rn2); reseed_random(rn2_on_display_rng); - minit(); /* ZEROCOMP */ getlev(nhfp, svh.hackpid, new_ledger); close_nhfile(nhfp); oinit(); /* reassign level dependent obj probabilities */ @@ -1921,6 +1947,11 @@ goto_level( (void) describe_level(dloc, 2); livelog_printf(major ? LL_ACHIEVE : LL_DEBUG, "entered %s", dloc); + + if (Role_if(PM_TOURIST)) { + more_experienced(level_difficulty(), 0); + newexplevel(); + } } assign_level(&u.uz0, &u.uz); /* reset u.uz0 */ @@ -2192,6 +2223,7 @@ revive_corpse(struct obj *corpse) fill_pit(mtmp->mx, mtmp->my); break; } + FALLTHROUGH; /*FALLTHRU*/ default: /* we should be able to handle the other cases... */ diff --git a/src/do_name.c b/src/do_name.c index 82c486451..9a2d2f54c 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 do_name.c $NHDT-Date: 1720895738 2024/07/13 18:35:38 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.320 $ */ +/* NetHack 3.7 do_name.c $NHDT-Date: 1737013431 2025/01/15 23:43:51 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.326 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Pasi Kallinen, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -273,7 +273,7 @@ do_mgivenname(void) verbalize("I'm %s, not %s.", shkname(mtmp), buf); } } else if (mtmp->ispriest || mtmp->isminion || mtmp->isshk - || mtmp->data == &mons[PM_GHOST]) { + || mtmp->data == &mons[PM_GHOST] || has_ebones(mtmp)) { if (!alreadynamed(mtmp, monnambuf, buf)) pline("%s will not accept the name %s.", upstart(monnambuf), buf); } else { @@ -442,6 +442,7 @@ objtyp_is_callable(int i) determine which one was the real one */ if (i == AMULET_OF_YENDOR || i == FAKE_AMULET_OF_YENDOR) break; /* return FALSE */ + FALLTHROUGH; /*FALLTHRU*/ case SCROLL_CLASS: case POTION_CLASS: @@ -749,7 +750,7 @@ namefloorobj(void) } if (fakeobj) { obj->where = OBJ_FREE; /* object_from_map() sets it to OBJ_FLOOR */ - dealloc_obj(obj); + dealloc_obj(obj); /* has no contents */ } } @@ -761,7 +762,7 @@ static const char *const ghostnames[] = { "John", "Jon", "Karnov", "Kay", "Kenny", "Kevin", "Maud", "Michiel", "Mike", "Peter", "Robert", "Ron", "Tom", "Wilmar", "Nick Danger", "Phoenix", "Jiro", "Mizue", - "Stephan", "Lance Braccus", "Shadowhawk" + "Stephan", "Lance Braccus", "Shadowhawk", "Murphy" }; /* ghost names formerly set by x_monnam(), now by makemon() instead */ @@ -777,12 +778,12 @@ rndghostname(void) * x_monnam is the generic monster-naming function. * seen unseen detected named * mon_nam: the newt it the invisible orc Fido - * noit_mon_nam:the newt (as if detected) the invisible orc Fido + * noit_mon_nam:your newt (as if detected) your invisible orc Fido * some_mon_nam:the newt someone the invisible orc Fido * or something * l_monnam: newt it invisible orc dog called Fido * Monnam: The newt It The invisible orc Fido - * noit_Monnam: The newt (as if detected) The invisible orc Fido + * noit_Monnam: Your newt (as if detected) Your invisible orc Fido * Some_Monnam: The newt Someone The invisible orc Fido * or Something * Adjmonnam: The poor newt It The poor invisible orc The poor Fido @@ -953,6 +954,10 @@ x_monnam( } else if (do_name && has_mgivenname(mtmp)) { char *name = MGIVENNAME(mtmp); +#if 0 + /* hardfought */ + if (has_ebones(mtmp)) { +#endif if (mdat == &mons[PM_GHOST]) { Sprintf(eos(buf), "%s ghost", s_suffix(name)); name_at_start = TRUE; @@ -975,6 +980,9 @@ x_monnam( Strcat(buf, name); name_at_start = TRUE; } +#if 0 /* hardfought */ + } +#endif } else if (is_mplayer(mdat) && !In_endgame(&u.uz)) { char pbuf[BUFSZ]; @@ -1035,14 +1043,15 @@ mon_nam(struct monst *mtmp) (has_mgivenname(mtmp)) ? SUPPRESS_SADDLE : 0, FALSE); } -/* print the name as if mon_nam() was called, but assume that the player - * can always see the monster--used for probing and for monsters aggravating - * the player with a cursed potion of invisibility - */ +/* print the name as if mon_nam() (y_monnam() if tame) was called, but + assume that the player can always see the monster--used for probing and + for monsters aggravating the player with a cursed potion of invisibility; + also used for pet moving "reluctantly" onto cursed object when that pet + can be seen either before or after it moves */ char * noit_mon_nam(struct monst *mtmp) { - return x_monnam(mtmp, ARTICLE_THE, (char *) 0, + return x_monnam(mtmp, ARTICLE_YOUR, (char *) 0, (has_mgivenname(mtmp) ? (SUPPRESS_SADDLE | SUPPRESS_IT) : SUPPRESS_IT), FALSE); diff --git a/src/do_wear.c b/src/do_wear.c index d6c52c4ae..058d903d7 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 do_wear.c $NHDT-Date: 1727251255 2024/09/25 08:00:55 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.191 $ */ +/* NetHack 3.7 do_wear.c $NHDT-Date: 1737343372 2025/01/19 19:22:52 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.201 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -30,7 +30,7 @@ staticfn int Gloves_on(void); staticfn int Shield_on(void); staticfn int Shirt_on(void); staticfn void dragon_armor_handling(struct obj *, boolean, boolean); -staticfn void Amulet_on(void); +staticfn void Amulet_on(struct obj *) NONNULLARG1; staticfn void learnring(struct obj *, boolean); staticfn void adjust_attrib(struct obj *, int, int); staticfn void Ring_off_or_gone(struct obj *, boolean); @@ -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 boolean better_not_take_that_off(struct obj *) NONNULLARG1; /* plural "fingers" or optionally "gloves" */ const char * @@ -73,6 +74,15 @@ off_msg(struct obj *otmp) staticfn void on_msg(struct obj *otmp) { + /* on_msg() for rings and amulets just shows add-to-invent feedback + [after caller calls setworn(), for suffix: "(on {left|right} hand)" + or "(being worn)"]; eyewear too unless giving verbose message below */ + if ((otmp->owornmask & (W_RING | W_AMUL)) != 0L + || ((otmp->owornmask & W_TOOL) != 0L && !flags.verbose)) { + prinv((char *) NULL, otmp, 0L); + return; + } + if (flags.verbose) { char how[BUFSZ]; /* call xname() before obj_is_pname(); formatting obj's name @@ -186,8 +196,22 @@ Boots_on(void) case KICKING_BOOTS: break; case WATER_WALKING_BOOTS: + /* + * Sequencing issue? If underwater (perhaps via magical breathing), + * putting on water walking boots produces "you slowly rise above + * the surface" then "you finish your dressing maneuver". + */ + + /* spoteffects() doesn't get called here; pooleffects() is called + during movement and u.uinwater is already False after setworn() */ if (u.uinwater) spoteffects(TRUE); + /* init'd in accessory_or_armor_on() and only used here */ + if (gw.wasinwater) { + if (!u.uinwater) + makeknown(WATER_WALKING_BOOTS); + gw.wasinwater = 0U; + } /* (we don't need a lava check here since boots can't be put on while feet are stuck) */ break; @@ -410,6 +434,9 @@ Helmet_on(void) { switch (uarmh->otyp) { case FEDORA: + if (Role_if(PM_ARCHEOLOGIST)) + change_luck(1); + break; case HELMET: case DENTED_POT: case ELVEN_LEATHER_HELM: @@ -442,6 +469,7 @@ Helmet_on(void) : (uarmh->o_id % 2) ? A_CHAOTIC : A_LAWFUL, A_CG_HELM_ON); /* makeknown(HELM_OF_OPPOSITE_ALIGNMENT); -- below, after Tobjnam() */ + FALLTHROUGH; /*FALLTHRU*/ case DUNCE_CAP: if (uarmh && !uarmh->cursed) { @@ -492,6 +520,9 @@ Helmet_off(void) switch (uarmh->otyp) { case FEDORA: + if (Role_if(PM_ARCHEOLOGIST)) + change_luck(-1); + break; case HELMET: case DENTED_POT: case ELVEN_LEATHER_HELM: @@ -924,16 +955,13 @@ Armor_gone(void) } staticfn void -Amulet_on(void) +Amulet_on(struct obj *amul) { - /* make sure amulet isn't wielded; can't use remove_worn_item() - here because it has already been set worn in amulet slot */ - if (uamul == uwep) - setuwep((struct obj *) 0); - else if (uamul == uswapwep) - setuswapwep((struct obj *) 0); - else if (uamul == uquiver) - setuqwep((struct obj *) 0); + boolean on_msg_done = FALSE; + + /* make sure amulet isn't wielded/alt-wielded/quivered, before wearing */ + remove_worn_item(amul, FALSE); + setworn(amul, W_AMUL); switch (uamul->otyp) { case AMULET_OF_ESP: @@ -951,8 +979,10 @@ Amulet_on(void) was_in_poison_gas = region_danger(); EMagical_breathing |= W_AMUL; if (was_in_poison_gas) { - You("are no longer bothered by the poison gas."); makeknown(AMULET_OF_MAGICAL_BREATHING); + on_msg(uamul); + on_msg_done = TRUE; + You("are no longer bothered by the poison gas."); } /* no need to check for becoming able to breathe underwater; if we are underwater, we already can or we would have drowned */ @@ -963,46 +993,58 @@ Amulet_on(void) make_slimed(0L, (char *) 0); break; case AMULET_OF_CHANGE: { + boolean call_it = FALSE; int new_sex, orig_sex = poly_gender(); - if (Unchanging) - break; - change_sex(); + /* in normal play it's not possible to put on an amulet of change + while already wearing an amulet of unchanging, but in wizard + mode the Unchanging attribute can be set via #wizintrinsic */ + if (!Unchanging) + change_sex(); + new_sex = poly_gender(); + if (new_sex != orig_sex) + makeknown(AMULET_OF_CHANGE); + on_msg(uamul); /* show 'z - amulet of change (being worn)' */ + on_msg_done = TRUE; + /* Don't use same message as polymorph */ if (new_sex != orig_sex) { - makeknown(AMULET_OF_CHANGE); + newsym(u.ux, u.uy); /* glyphmon flag and tile have changed */ + disp.botl = TRUE; /* role name or rank title might have changed */ You("are suddenly very %s!", flags.female ? "feminine" : "masculine"); - disp.botl = TRUE; - newsym(u.ux, u.uy); /* glyphmon flag and tile may have gone - * from male to female or vice versa */ } else { /* already polymorphed into single-gender monster; only changed the character's base sex */ You("don't feel like yourself."); + /* checking dknown is redundant--amulets always have dknown set */ + call_it = (uamul->dknown != 0); } livelog_newform(FALSE, orig_sex, new_sex); pline_The("amulet disintegrates!"); - if (orig_sex == poly_gender() && uamul->dknown) + if (call_it) trycall(uamul); useup(uamul); break; } case AMULET_OF_STRANGULATION: - if (can_be_strangled(&gy.youmonst)) { + /* note: might already be Strangled (via #wizintrinsic) */ + if (can_be_strangled(&gy.youmonst) && !Strangled) { makeknown(AMULET_OF_STRANGULATION); Strangled = 6L; disp.botl = TRUE; + on_msg(uamul); + on_msg_done = TRUE; pline("It constricts your throat!"); } break; case AMULET_OF_RESTFUL_SLEEP: { - long newnap = (long) rnd(100), oldnap = (HSleepy & TIMEOUT); + long newnap = (long) rnd(98) + 2L, oldnap = (HSleepy & TIMEOUT); - /* avoid clobbering FROMOUTSIDE bit, which might have - gotten set by previously eating one of these amulets */ if (newnap < oldnap || oldnap == 0L) + /* avoid clobbering FROMOUTSIDE bit, which might have + gotten set by previously eating one of these amulets */ HSleepy = (HSleepy & ~TIMEOUT) | newnap; break; } @@ -1020,6 +1062,8 @@ Amulet_on(void) if (!already_flying) { makeknown(AMULET_OF_FLYING); + on_msg(uamul); + on_msg_done = TRUE; disp.botl = TRUE; /* status: 'Fly' On */ You("are now in flight."); } @@ -1032,19 +1076,28 @@ Amulet_on(void) case AMULET_OF_YENDOR: break; } + + if (!on_msg_done) + on_msg(uamul); } void Amulet_off(void) { + struct obj *amul = uamul; /* for off_msg() after setworn(NULL,W_AMUL) */ + boolean mkn = FALSE, early_off_msg = FALSE; + svc.context.takeoff.mask &= ~W_AMUL; switch (uamul->otyp) { case AMULET_OF_ESP: /* need to update ability before calling see_monsters() */ setworn((struct obj *) 0, W_AMUL); + off_msg(amul); + early_off_msg = TRUE; + see_monsters(); - return; + break; case AMULET_OF_LIFE_SAVING: case AMULET_VERSUS_POISON: case AMULET_OF_REFLECTION: @@ -1053,21 +1106,31 @@ Amulet_off(void) case FAKE_AMULET_OF_YENDOR: break; case AMULET_OF_MAGICAL_BREATHING: + /* amulet is currently still on; take it off before calling drown() + and region_danger(); call off_msg() before specific messages */ + setworn((struct obj *) 0, W_AMUL); + off_msg(amul); /* 'uamul' has been set to Null */ + early_off_msg = TRUE; + if (Underwater) { - /* HMagical_breathing must be set off before calling drown() */ - setworn((struct obj *) 0, W_AMUL); if (!cant_drown(gy.youmonst.data) && !Swimming) { You("suddenly inhale an unhealthy amount of %s!", hliquid("water")); + mkn = TRUE; /* in case of life-saving */ (void) drown(); } - return; } - /* - * FIXME: we need a poison gas region check here - */ + if (region_danger()) { + /* "breathing": wouldn't get here otherwise */ + You("are breathing poison gas!"); + mkn = TRUE; + } break; case AMULET_OF_STRANGULATION: + setworn((struct obj *) 0, W_AMUL); + off_msg(amul); + early_off_msg = TRUE; + if (Strangled) { Strangled = 0L; disp.botl = TRUE; @@ -1075,6 +1138,7 @@ Amulet_off(void) Your("%s is no longer constricted!", body_part(NECK)); else You("can breathe more easily!"); + mkn = TRUE; } break; case AMULET_OF_RESTFUL_SLEEP: @@ -1082,20 +1146,24 @@ Amulet_off(void) /* HSleepy = 0L; -- avoid clobbering FROMOUTSIDE bit */ if (!ESleepy && !(HSleepy & ~TIMEOUT)) HSleepy &= ~TIMEOUT; /* clear timeout bits */ - return; + break; case AMULET_OF_FLYING: { boolean was_flying = !!Flying; - /* remove amulet 'early' to determine whether Flying changes */ + /* remove amulet 'early' to determine whether Flying changes; + also in case spoteffects() does something with the amulet */ setworn((struct obj *) 0, W_AMUL); + off_msg(amul); + early_off_msg = TRUE; + float_vs_flight(); /* probably not needed here */ if (was_flying && !Flying) { - makeknown(AMULET_OF_FLYING); disp.botl = TRUE; /* status: 'Fly' Off */ You("%s.", (is_pool_or_lava(u.ux, u.uy) || Is_waterlevel(&u.uz) || Is_airlevel(&u.uz)) ? "stop flying" : "land"); + mkn = TRUE; /* makeknown(AMULET_OF_FLYING) */ spoteffects(TRUE); } break; @@ -1106,7 +1174,12 @@ Amulet_off(void) case AMULET_OF_YENDOR: break; } + setworn((struct obj *) 0, W_AMUL); + if (!early_off_msg) + off_msg(amul); /* (not 'uamul'; it's Null now) */ + if (mkn) + makeknown(amul->otyp); return; } @@ -1197,7 +1270,9 @@ Ring_on(struct obj *obj) case RIN_FREE_ACTION: case RIN_SLOW_DIGESTION: case RIN_SUSTAIN_ABILITY: + break; case MEAT_RING: + /* wearing a meat ring does not affect vegan conduct */ break; case RIN_STEALTH: toggle_stealth(obj, oldprop, TRUE); @@ -1356,10 +1431,11 @@ Ring_off_or_gone(struct obj *obj, boolean gone) find_ac(); /* updates botl */ break; case RIN_PROTECTION_FROM_SHAPE_CHAN: - /* If you're no longer protected, let the chameleons - * change shape again -dgk - */ - restartcham(); + /* if you're no longer protected, let the chameleons change + shape again; however, might still be protected if wearing + 2nd ring of this type (or via #wizintrinsic) */ + if (!Protection_from_shape_changers) + restartcham(); break; } } @@ -1382,8 +1458,7 @@ Blindf_on(struct obj *otmp) boolean already_blind = Blind, changed = FALSE; /* blindfold might be wielded; release it for wearing */ - if (otmp->owornmask & W_WEAPONS) - remove_worn_item(otmp, FALSE); + remove_worn_item(otmp, FALSE); setworn(otmp, W_TOOL); on_msg(otmp); @@ -1468,7 +1543,7 @@ set_wear( if (!obj ? uleft != 0 : (obj == uleft)) (void) Ring_on(uleft); if (!obj ? uamul != 0 : (obj == uamul)) - (void) Amulet_on(); + (void) Amulet_on(uamul); if (!obj ? uarmu != 0 : (obj == uarmu)) (void) Shirt_on(); @@ -1605,8 +1680,8 @@ cancel_don(void) /* called by steal() during theft from hero; interrupt donning/doffing */ int -stop_donning(struct obj *stolenobj) /* no message if stolenobj is already - being doffing */ +stop_donning( + struct obj *stolenobj) /* no mesg if stolenobj is already being doffed */ { char buf[BUFSZ]; struct obj *otmp; @@ -1650,8 +1725,9 @@ static NEARDATA int Narmorpieces, Naccessories; /* assign values to Narmorpieces and Naccessories */ staticfn void -count_worn_stuff(struct obj **which, /* caller wants this when count is 1 */ - boolean accessorizing) +count_worn_stuff( + struct obj **which, /* caller wants this when count is 1 */ + boolean accessorizing) { struct obj *otmp; @@ -1735,12 +1811,12 @@ armor_or_accessory_off(struct obj *obj) off_msg(obj); Ring_off(obj); } else if (obj == uamul) { - Amulet_off(); - off_msg(obj); + Amulet_off(); /* does its own off_msg */ } else if (obj == ublindf) { Blindf_off(obj); /* does its own off_msg */ } else { - impossible("removing strange accessory?"); + impossible("removing strange accessory: %s", + safe_typename(obj->otyp)); if (obj->owornmask) remove_worn_item(obj, FALSE); } @@ -2013,12 +2089,12 @@ canwearobj(struct obj *otmp, long *mask, boolean noisy) You("have no feet..."); /* not body_part(FOOT) */ err++; } else if (Upolyd && gy.youmonst.data->mlet == S_CENTAUR) { - /* break_armor() pushes boots off for centaurs, - so don't let dowear() put them back on... */ + /* break_armor() pushes boots off for centaurs, so don't let + dowear() put them back on; + makeplural(body_part(FOOT)) would yield "rear hooves" here, + which sounds odd, so use hard-coded "hooves" */ if (noisy) - You("have too many hooves to wear %s.", - c_boots); /* makeplural(body_part(FOOT)) yields - "rear hooves" which sounds odd */ + You("have too many hooves to wear %s.", c_boots); err++; } else if (u.utrap && (u.utraptype == TT_BEARTRAP || u.utraptype == TT_INFLOOR @@ -2113,7 +2189,7 @@ staticfn int accessory_or_armor_on(struct obj *obj) { long mask = 0L; - boolean armor, ring, eyewear; + boolean armor, ring, amulet, eyewear; if (obj->owornmask & (W_ACCESSORY | W_ARMOR)) { already_wearing(c_that_); @@ -2121,6 +2197,7 @@ accessory_or_armor_on(struct obj *obj) } armor = (obj->oclass == ARMOR_CLASS); ring = (obj->oclass == RING_CLASS || obj->otyp == MEAT_RING); + amulet = (obj->oclass == AMULET_CLASS); eyewear = (obj->otyp == BLINDFOLD || obj->otyp == TOWEL || obj->otyp == LENSES); /* checks which are performed prior to actually touching the item */ @@ -2218,7 +2295,7 @@ accessory_or_armor_on(struct obj *obj) return res ? ECMD_TIME : ECMD_OK; } } - } else if (obj->oclass == AMULET_CLASS) { + } else if (amulet) { if (uamul) { already_wearing("an amulet"); return ECMD_OK; @@ -2275,6 +2352,7 @@ accessory_or_armor_on(struct obj *obj) * obj->known = 1; */ + gw.wasinwater = u.uinwater; /* for WWALKING; Boots_on() is too late */ setworn(obj, mask); /* if there's no delay, we'll execute 'afternmv' immediately */ if (obj == uarm) @@ -2304,27 +2382,27 @@ accessory_or_armor_on(struct obj *obj) on_msg(obj); } svc.context.takeoff.mask = svc.context.takeoff.what = 0L; + /* gw.wasinwater = 0U; // can't clear this yet; Boots_on() needs it + * and gets called via afternmv() after this routine has returned */ } else { /* not armor */ - boolean give_feedback = FALSE; - - /* [releasing wielded accessory handled in Xxx_on()] */ if (ring) { + /* Ring_on() expects ring to already be worn as uleft or uright */ setworn(obj, mask); Ring_on(obj); - give_feedback = TRUE; - } else if (obj->oclass == AMULET_CLASS) { - setworn(obj, W_AMUL); - Amulet_on(); - /* no feedback here if amulet of change got used up */ - give_feedback = (uamul != 0); + /* is_worn(): 'obj' will always be worn here except when putting + on a ring of levitation while at a sink location */ + if (is_worn(obj)) + on_msg(obj); + } else if (amulet) { + /* setworn() and on_msg() handled by Amulet_on() */ + Amulet_on(obj); } else if (eyewear) { - /* setworn() handled by Blindf_on() */ + /* setworn() and on_msg() handled by Blindf_on() */ Blindf_on(obj); - /* message handled by Blindf_on(); leave give_feedback False */ + } else { + impossible("putting on unexpected type of accessory: %s", + safe_typename(obj->otyp)); } - /* feedback for ring or for amulet other than 'change' */ - if (give_feedback && is_worn(obj)) - prinv((char *) 0, obj, 0L); } return ECMD_TIME; } @@ -2641,6 +2719,8 @@ select_off(struct obj *otmp) gloves_simple_name(uarmg)); return 0; } + if (better_not_take_that_off(otmp)) + return 0; } /* special boot checks */ if (otmp == uarmf) { @@ -2886,6 +2966,29 @@ take_off(void) return 1; /* get busy */ } +staticfn boolean +better_not_take_that_off(struct obj *otmp) +{ + struct obj *corpse = carrying_stoning_corpse(); + char buf[BUFSZ]; + + /* u_safe_from_fatal_corpse() with + (st_corpse | st_petrifies | st_resists) instead of + (st_corpse | st_petrifies) + would also check for no stoning resistance before + bothering to prompt, but losing stoning resistance + later, without the gloves on could prove dangerous, + so we won't factor that in */ + if (corpse + && !u_safe_from_fatal_corpse(corpse, st_corpse | st_petrifies)) { + Snprintf(buf, sizeof buf, + "Take off your %s despite carrying a dead %s?", + gloves_simple_name(otmp), obj_pmname(corpse)); + return (paranoid_ynq(TRUE, buf, FALSE) != 'y'); + } + return FALSE; +} + /* clear saved context to avoid inappropriate resumption of interrupted 'A' */ void reset_remarm(void) @@ -2914,7 +3017,7 @@ doddoremarm(void) if (flags.menu_style != MENU_TRADITIONAL || (result = ggetobj("take off", select_off, 0, FALSE, (unsigned *) 0)) < -1) - result = menu_remarm(result); + (void) menu_remarm(result); if (svc.context.takeoff.mask) { (void) strncpy(svc.context.takeoff.disrobing, @@ -3020,7 +3123,7 @@ menu_remarm(int retry) staticfn void wornarm_destroyed(struct obj *wornarm) { - struct obj *invobj; + struct obj *invobj, *nextobj; unsigned wornoid = wornarm->o_id; /* cancel_don() resets 'afternmv' when appropriate but doesn't reset @@ -3049,11 +3152,13 @@ wornarm_destroyed(struct obj *wornarm) scan invent instead; if already freed it shouldn't be possible to have re-used the stale memory for a new item yet but verify o_id just in case */ - for (invobj = gi.invent; invobj; invobj = invobj->nobj) + for (invobj = gi.invent; invobj; invobj = nextobj) { + nextobj = invobj->nobj; if (invobj == wornarm && invobj->o_id == wornoid) { useup(wornarm); break; } + } } /* @@ -3285,4 +3390,31 @@ takeoff_ok(struct obj *obj) return equip_ok(obj, TRUE, FALSE); } +/* getobj callback for blessed destroy armor. + suggest any worn armor, even if covered by other armor */ +int +any_worn_armor_ok(struct obj *obj) +{ + if (obj && (obj->owornmask & W_ARMOR)) + return GETOBJ_SUGGEST; + return GETOBJ_EXCLUDE; +} + +/* number of armor pieces worn by hero */ +int +count_worn_armor(void) +{ + int ret = 0; + + if (uarm) ret++; + if (uarmc) ret++; + if (uarmh) ret++; + if (uarms) ret++; + if (uarmg) ret++; + if (uarmf) ret++; + if (uarmu) ret++; + + return ret; +} + /*do_wear.c*/ diff --git a/src/dog.c b/src/dog.c index 11d13dcbd..a583de705 100644 --- a/src/dog.c +++ b/src/dog.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 dog.c $NHDT-Date: 1725227804 2024/09/01 21:56:44 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.164 $ */ +/* NetHack 3.7 dog.c $NHDT-Date: 1753856387 2025/07/29 22:19:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.190 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -27,6 +27,7 @@ newedog(struct monst *mtmp) if (!EDOG(mtmp)) { EDOG(mtmp) = (struct edog *) alloc(sizeof(struct edog)); (void) memset((genericptr_t) EDOG(mtmp), 0, sizeof(struct edog)); + EDOG(mtmp)->parentmid = mtmp->m_id; } } @@ -41,25 +42,48 @@ free_edog(struct monst *mtmp) } void -initedog(struct monst *mtmp) +initedog(struct monst *mtmp, boolean everything) { - mtmp->mtame = is_domestic(mtmp->data) ? 10 : 5; + struct edog *edogp = EDOG(mtmp); + long minhungry = svm.moves + 1000L; + schar minimumtame = is_domestic(mtmp->data) ? 10 : 5; + + mtmp->mtame = max(minimumtame, mtmp->mtame); mtmp->mpeaceful = 1; mtmp->mavenge = 0; set_malign(mtmp); /* recalc alignment now that it's tamed */ - mtmp->mleashed = 0; - mtmp->meating = 0; - EDOG(mtmp)->droptime = 0; - EDOG(mtmp)->dropdist = 10000; - EDOG(mtmp)->apport = ACURR(A_CHA); - EDOG(mtmp)->whistletime = 0; - EDOG(mtmp)->hungrytime = 1000 + svm.moves; - EDOG(mtmp)->ogoal.x = -1; /* force error if used before set */ - EDOG(mtmp)->ogoal.y = -1; - EDOG(mtmp)->abuse = 0; - EDOG(mtmp)->revivals = 0; - EDOG(mtmp)->mhpmax_penalty = 0; - EDOG(mtmp)->killed_by_u = 0; + if (everything) { + mtmp->mleashed = 0; + mtmp->meating = 0; + edogp->droptime = 0; + edogp->dropdist = 10000; + edogp->apport = ACURR(A_CHA); + edogp->whistletime = 0; + /* edogp->hungrytime = 0L; // set below */ + edogp->ogoal.x = -1; /* force error if used before set */ + edogp->ogoal.y = -1; + edogp->abuse = 0; + edogp->revivals = 0; + edogp->mhpmax_penalty = 0; + edogp->killed_by_u = 0; + } else { + if (edogp->apport <= 0) + edogp->apport = 1; + } + /* always set for newly tamed pet or feral former pet; hungrytime might + already be higher when taming magic affects already tame monst */ + if (edogp->hungrytime < minhungry) + edogp->hungrytime = minhungry; + /* livelog first pet, but only if you didn't start with one (the starting + * pet will be initialized before in_moveloop is true) */ + if (!u.uconduct.pets && program_state.in_moveloop) { + /* "obtained" a pet rather than "tamed" it because it might have come + * from a figurine or some other method in which it was created tame + * using an() is safe unless it somehow becomes possible to tame a + * unique monster */ + livelog_printf(LL_CONDUCT, "obtained %s first pet (%s)", + uhis(), an(mon_pmname(mtmp))); + } u.uconduct.pets++; } @@ -100,7 +124,10 @@ pick_familiar_pm(struct obj *otmp, boolean quietly) } else if (!rn2(3)) { pm = &mons[pet_type()]; } else { - pm = rndmonst(); + int skill = spell_skilltype(SPE_CREATE_FAMILIAR); + int max = 3 * P_SKILL(skill); + + pm = rndmonst_adj(0, max); if (!pm && !quietly) There("seems to be nothing available for a familiar."); } @@ -113,6 +140,7 @@ make_familiar(struct obj *otmp, coordxy x, coordxy y, boolean quietly) struct permonst *pm; struct monst *mtmp = 0; int chance, trycnt = 100; + boolean reallytame = TRUE; do { mmflags_nht mmflags; @@ -154,17 +182,14 @@ make_familiar(struct obj *otmp, coordxy x, coordxy y, boolean quietly) if (is_pool(mtmp->mx, mtmp->my) && minliquid(mtmp)) return (struct monst *) 0; - initedog(mtmp); - mtmp->msleeping = 0; if (otmp) { /* figurine; resulting monster might not become a pet */ chance = rn2(10); /* 0==tame, 1==peaceful, 2==hostile */ if (chance > 2) chance = otmp->blessed ? 0 : !otmp->cursed ? 1 : 2; /* 0,1,2: b=80%,10,10; nc=10%,80,10; c=10%,10,80 */ if (chance > 0) { - mtmp->mtame = 0; /* not tame after all */ - u.uconduct.pets--; /* doesn't count as creating a pet */ - if (chance == 2) { /* hostile (cursed figurine) */ + reallytame = FALSE; /* not tame after all */ + if (chance == 2) { /* hostile (cursed figurine) */ if (!quietly) You("get a bad feeling about this."); mtmp->mpeaceful = 0; @@ -175,6 +200,9 @@ make_familiar(struct obj *otmp, coordxy x, coordxy y, boolean quietly) if (has_oname(otmp)) mtmp = christen_monst(mtmp, ONAME(otmp)); } + if (reallytame) + initedog(mtmp, TRUE); + mtmp->msleeping = 0; set_malign(mtmp); /* more alignment changes */ newsym(mtmp->mx, mtmp->my); @@ -186,19 +214,21 @@ make_familiar(struct obj *otmp, coordxy x, coordxy y, boolean quietly) return mtmp; } -/* used exclusively for hero's starting pet */ +/* despite rather general name, used exclusively for hero's starting pet */ struct monst * makedog(void) { struct monst *mtmp; - struct obj *otmp; const char *petname; int pettype; - if (gp.preferred_pet == 'n') + if (gp.preferred_pet == 'n') { + /* static init yields 0 (PM_GIANT_ANT); fix that up now */ + svc.context.startingpet_typ = NON_PM; return ((struct monst *) 0); + } - pettype = pet_type(); + pettype = svc.context.startingpet_typ = pet_type(); petname = (pettype == PM_LITTLE_DOG) ? gd.dogname : (pettype == PM_KITTEN) ? gc.catname : (pettype == PM_PONY) ? gh.horsename @@ -217,7 +247,12 @@ makedog(void) petname = "Sirius"; /* Orion's dog */ } - mtmp = makemon(&mons[pettype], u.ux, u.uy, MM_EDOG); + /* specifying NO_MINVENT prevents makemon() from having a 1% chance + of creating a pony with an already worn saddle; dogs and cats + aren't affected because they don't have any initial inventory + [if anybody adds stranger pets that are expected to have such, + they'll need to modify this] */ + mtmp = makemon(&mons[pettype], u.ux, u.uy, MM_EDOG | NO_MINVENT); if (!mtmp) return ((struct monst *) 0); /* pets were genocided [how?] */ @@ -225,15 +260,18 @@ makedog(void) if (!svc.context.startingpet_mid) { svc.context.startingpet_mid = mtmp->m_id; if (!u.uroleplay.pauper) { - /* initial horses already wear saddle (unless hero is a pauper) */ - if (pettype == PM_PONY - && (otmp = mksobj(SADDLE, TRUE, FALSE)) != 0) { - /* pseudo initial inventory; saddle is not actually in hero's - * invent so assume that update_inventory() isn't needed */ - fully_identify_obj(otmp); - put_saddle_on_mon(otmp, mtmp); + /* initial horses start wearing a saddle (pauper hero excluded) */ + if (pettype == PM_PONY) { + /* NULL obj arg means put_saddle_on_mon() + * will carry out the saddle creation */ + put_saddle_on_mon((struct obj *) 0, mtmp); } } + /* starting pet's type has been seen up close (unless PermaBlind) + and for tourist treat it as having already been photographed */ + gb.bhitpos.x = mtmp->mx, gb.bhitpos.y = mtmp->my; + gn.notonhead = FALSE; + see_monster_closeup(mtmp, carrying(EXPENSIVE_CAMERA) ? TRUE : FALSE); } else { impossible("makedog() when startingpet_mid is already non-zero?"); } @@ -241,7 +279,7 @@ makedog(void) if (!gp.petname_used++ && *petname) mtmp = christen_monst(mtmp, petname); - initedog(mtmp); + initedog(mtmp, TRUE); return mtmp; } @@ -417,6 +455,12 @@ mon_arrive(struct monst *mtmp, int when) fromdlev.dnum = mtmp->mtrack[2].x; fromdlev.dlevel = mtmp->mtrack[2].y; mon_track_clear(mtmp); + /* in case Protection_from_shape_changers is different now from when + 'mtmp' went onto the migrating monsters list; that's handled in + getlev() when returning to a previously visited level and by the + special level code for monsters specified in the level, but needed + here for monsters migrating to a newly created level */ + restore_cham(mtmp); if (mtmp == u.usteed) return; /* don't place steed on the map */ @@ -514,10 +558,13 @@ mon_arrive(struct monst *mtmp, int when) && (stway = stairway_find_dir(!builds_up(&u.uz))) != 0) { /* debugfuzzer returns from or enters another branch */ xlocale = stway->sx, ylocale = stway->sy; + break; } else if (!(u.uevent.qexpelled && (Is_qstart(&u.uz0) || Is_qstart(&u.uz)))) { impossible("mon_arrive: no corresponding portal?"); - } /*FALLTHRU*/ + } + FALLTHROUGH; + /*FALLTHRU*/ default: case MIGR_RANDOM: xlocale = ylocale = 0; @@ -667,10 +714,7 @@ mon_catchup_elapsed_time( /* recover lost hit points */ if (!regenerates(mtmp->data)) imv /= 20; - if (mtmp->mhp + imv >= mtmp->mhpmax) - mtmp->mhp = mtmp->mhpmax; - else - mtmp->mhp += imv; + healmon(mtmp, imv, 0); set_mon_lastmove(mtmp); } @@ -783,8 +827,8 @@ keepdogs( mdrop_special_objs(mtmp); /* drop Amulet */ } else if (mtmp->meating || mtmp->mtrapped) { if (canseemon(mtmp)) - pline("%s is still %s.", Monnam(mtmp), - mtmp->meating ? "eating" : "trapped"); + pline_mon(mtmp, "%s is still %s.", Monnam(mtmp), + mtmp->meating ? "eating" : "trapped"); stay_behind = TRUE; } else if (mon_has_amulet(mtmp)) { if (canseemon(mtmp)) @@ -906,6 +950,8 @@ discard_migrations(void) mtmp->nmon = 0; discard_minvent(mtmp, FALSE); /* bypass mongone() and its call to m_detach() plus dmonsfree() */ + if (emits_light(mtmp->data)) + del_light_source(LS_MONSTER, monst_to_any(mtmp)); dealloc_monst(mtmp); } } @@ -928,6 +974,12 @@ discard_migrations(void) otmp->where = OBJ_FREE; otmp->owornmask = 0L; /* overloaded for destination usage; * obfree() will complain if nonzero */ + /* + * obfree(otmp,) + * -> dealloc_obj(otmp) + * -> obj_stop_timers(otmp) + * -> del_light_source(LS_OBJECT, obj_to_any(otmp)) + */ obfree(otmp, (struct obj *) 0); /* releases any contents too */ } } @@ -943,6 +995,8 @@ dogfood(struct monst *mon, struct obj *obj) starving, mblind; int fx; + if (obj->opoisoned && !resists_poison(mon)) + return POISON; if (is_quest_artifact(obj) || obj_resists(obj, 0, 95)) return obj->cursed ? TABU : APPORT; @@ -1068,6 +1122,7 @@ dogfood(struct monst *mon, struct obj *obj) && obj->oclass != BALL_CLASS && obj->oclass != CHAIN_CLASS) return APPORT; + FALLTHROUGH; /*FALLTHRU*/ case ROCK_CLASS: return UNDEF; @@ -1081,8 +1136,18 @@ dogfood(struct monst *mon, struct obj *obj) * on the original mtmp. It now returns TRUE if the taming succeeded. */ boolean -tamedog(struct monst *mtmp, struct obj *obj, boolean givemsg) +tamedog( + struct monst *mtmp, + struct obj *obj, /* food or scroll/spell */ + boolean givemsg) { + boolean blessed_scroll = FALSE; + + if (obj && (obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS)) { + blessed_scroll = obj->blessed ? TRUE : FALSE; + /* the rest of this routine assumes 'obj' represents food */ + obj = (struct obj *) NULL; + } /* reduce timed sleep or paralysis, leaving mtmp->mcanmove as-is (note: if mtmp is donning armor, this will reduce its busy time) */ if (mtmp->mfrozen) @@ -1098,7 +1163,7 @@ tamedog(struct monst *mtmp, struct obj *obj, boolean givemsg) /* worst case, at least it'll be peaceful. */ if (givemsg && !mtmp->mpeaceful && canspotmon(mtmp)) { - pline("%s seems %s.", Monnam(mtmp), + pline_mon(mtmp, "%s seems %s.", Monnam(mtmp), Hallucination ? "really chill" : "more amiable"); givemsg = FALSE; /* don't give another message below */ } @@ -1133,8 +1198,9 @@ tamedog(struct monst *mtmp, struct obj *obj, boolean givemsg) boolean big_corpse = (obj->otyp == CORPSE && ismnum(obj->corpsenm) && mons[obj->corpsenm].msize > mtmp->data->msize); - pline("%s catches %s%s", Monnam(mtmp), the(xname(obj)), - !big_corpse ? "." : ", or vice versa!"); + pline_mon(mtmp, "%s catches %s%s", + Monnam(mtmp), the(xname(obj)), + !big_corpse ? "." : ", or vice versa!"); } else if (cansee(mtmp->mx, mtmp->my)) pline("%s.", Tobjnam(obj, "stop")); /* dog_eat expects a floor object */ @@ -1148,11 +1214,17 @@ tamedog(struct monst *mtmp, struct obj *obj, boolean givemsg) return FALSE; } - /* if already tame, taming magic might make it become tamer */ - if (mtmp->mtame) { - /* maximum tameness is 20, only reachable via eating */ - if (rnd(10) > mtmp->mtame) + /* maximum tameness is 20, only reachable via eating; if already tame but + less than 10, taming magic might make it become tamer; blessed scroll + or skilled spell raises low tameness by 2 or 3, uncursed by 0 or 1 */ + if (mtmp->mtame && mtmp->mtame < 10) { + if (mtmp->mtame < rnd(10)) mtmp->mtame++; + if (blessed_scroll) { + mtmp->mtame += 2; + if (mtmp->mtame > 10) + mtmp->mtame = 10; + } return FALSE; /* didn't just get tamed */ } /* pacify angry shopkeeper but don't tame him/her/it/them */ @@ -1175,8 +1247,12 @@ tamedog(struct monst *mtmp, struct obj *obj, boolean givemsg) return FALSE; /* add the pet extension */ - newedog(mtmp); - initedog(mtmp); + if (!has_edog(mtmp)) { + newedog(mtmp); + initedog(mtmp, TRUE); + } else { + initedog(mtmp, FALSE); + } if (obj) { /* thrown food */ /* defer eating until the edog extension has been set up */ @@ -1188,10 +1264,12 @@ tamedog(struct monst *mtmp, struct obj *obj, boolean givemsg) } if (givemsg && canspotmon(mtmp)) - pline("%s seems quite %s.", Monnam(mtmp), + pline_mon(mtmp, "%s seems quite %s.", Monnam(mtmp), Hallucination ? "approachable" : "friendly"); newsym(mtmp->mx, mtmp->my); + if (mtmp->wormno) + redraw_worm(mtmp); if (attacktype(mtmp->data, AT_WEAP)) { mtmp->weapon_check = NEED_HTH_WEAPON; (void) mon_wield_item(mtmp); @@ -1233,11 +1311,12 @@ wary_dog(struct monst *mtmp, boolean was_dead) if (!quietly && cansee(mtmp->mx, mtmp->my)) { if (haseyes(gy.youmonst.data)) { if (haseyes(mtmp->data)) - pline("%s %s to look you in the %s.", Monnam(mtmp), - mtmp->mpeaceful ? "seems unable" : "refuses", - body_part(EYE)); + pline_mon(mtmp, + "%s %s to look you in the %s.", Monnam(mtmp), + mtmp->mpeaceful ? "seems unable" : "refuses", + body_part(EYE)); else - pline("%s avoids your gaze.", Monnam(mtmp)); + pline_mon(mtmp, "%s avoids your gaze.", Monnam(mtmp)); } } } else { @@ -1249,7 +1328,7 @@ wary_dog(struct monst *mtmp, boolean was_dead) if (!mtmp->mtame) { if (!quietly && canspotmon(mtmp)) - pline("%s %s.", Monnam(mtmp), + pline_mon(mtmp, "%s %s.", Monnam(mtmp), mtmp->mpeaceful ? "is no longer tame" : "has become feral"); newsym(mtmp->mx, mtmp->my); /* a life-saved monster might be leashed; @@ -1300,8 +1379,12 @@ abuse_dog(struct monst *mtmp) else growl(mtmp); /* give them a moment's worry */ - if (!mtmp->mtame) + if (!mtmp->mtame) { newsym(mtmp->mx, mtmp->my); + if (mtmp->wormno) { + redraw_worm(mtmp); + } + } } } diff --git a/src/dogmove.c b/src/dogmove.c index 6e25c39c2..50f40f135 100644 --- a/src/dogmove.c +++ b/src/dogmove.c @@ -17,10 +17,11 @@ staticfn int dog_invent(struct monst *, struct edog *, int); staticfn int dog_goal(struct monst *, struct edog *, int, int, int); staticfn struct monst *find_targ(struct monst *, int, int, int); staticfn int find_friends(struct monst *, struct monst *, int); -staticfn struct monst *best_target(struct monst *); +staticfn struct monst *best_target(struct monst *, boolean); staticfn long score_targ(struct monst *, struct monst *); staticfn boolean can_reach_location(struct monst *, coordxy, coordxy, coordxy, coordxy) NONNULLARG1; +staticfn boolean mnum_leashable(int); /* pick a carried item for pet to drop */ struct obj * @@ -76,6 +77,7 @@ droppables(struct monst *mon) if (pickaxe && pickaxe->otyp == PICK_AXE && pickaxe != wep && (!pickaxe->oartifact || obj->oartifact)) return pickaxe; /* drop the one we earlier decided to keep */ + FALLTHROUGH; /*FALLTHRU*/ case PICK_AXE: if (!pickaxe || (obj->oartifact && !pickaxe->oartifact)) { @@ -104,12 +106,14 @@ droppables(struct monst *mon) if (key && key->otyp == LOCK_PICK && (!key->oartifact || obj->oartifact)) return key; /* drop the one we earlier decided to keep */ + FALLTHROUGH; /*FALLTHRU*/ case LOCK_PICK: /* keep lock-pick in preference to credit card */ if (key && key->otyp == CREDIT_CARD && (!key->oartifact || obj->oartifact)) return key; + FALLTHROUGH; /*FALLTHRU*/ case CREDIT_CARD: if (!key || (obj->oartifact && !key->oartifact)) { @@ -280,9 +284,9 @@ dog_eat(struct monst *mtmp, result won't be printed */ obj_name = distant_name(obj, doname); if (tunnels(mtmp->data)) - pline("%s digs in.", noit_Monnam(mtmp)); + pline_mon(mtmp, "%s digs in.", noit_Monnam(mtmp)); else - pline("%s %s %s.", noit_Monnam(mtmp), + pline_mon(mtmp, "%s %s %s.", noit_Monnam(mtmp), devour ? "devours" : "eats", obj_name); } else if (seeobj) { obj_name = distant_name(obj, doname); @@ -308,9 +312,16 @@ dog_eat(struct monst *mtmp, } else { /* 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) + if (dogfood(mtmp, obj) == DOGFOOD && obj->invlet) { 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)", + edog->apport, edog->dropdist, edog->droptime, + svm.moves); + edog->apport = 1; + } + } if (obj->unpaid) { /* edible item owned by shop has been thrown or kicked by hero and caught by tame or food-tameable monst */ @@ -332,7 +343,7 @@ dog_starve(struct monst *mtmp) if (mtmp->mleashed && mtmp != u.usteed) Your("leash goes slack."); else if (cansee(mtmp->mx, mtmp->my)) - pline("%s starves.", Monnam(mtmp)); + pline_mon(mtmp, "%s starves.", Monnam(mtmp)); else You_feel("%s for a moment.", Hallucination ? "bummed" : "sad"); @@ -360,7 +371,7 @@ dog_hunger(struct monst *mtmp, struct edog *edog) return TRUE; } if (cansee(mtmp->mx, mtmp->my)) - pline("%s is confused from hunger.", Monnam(mtmp)); + pline_mon(mtmp, "%s is confused from hunger.", Monnam(mtmp)); else if (couldsee(mtmp->mx, mtmp->my)) beg(mtmp); else @@ -396,6 +407,7 @@ dog_invent(struct monst *mtmp, struct edog *edog, int udist) * Use udist+1 so steed won't cause divide by zero. */ if (droppables(mtmp)) { + assert(edog->apport > 0); if (!rn2(udist + 1) || !rn2(edog->apport)) if (rn2(10) < edog->apport) { relobj(mtmp, (int) mtmp->minvis, TRUE); @@ -816,7 +828,7 @@ score_targ(struct monst *mtmp, struct monst *mtarg) } staticfn struct monst * -best_target(struct monst *mtmp) /* Pet */ +best_target(struct monst *mtmp, boolean forced) /* Pet */ { int dx, dy; long bestscore = -40000L, currscore; @@ -859,7 +871,7 @@ best_target(struct monst *mtmp) /* Pet */ } /* Filter out targets the pet doesn't like */ - if (bestscore < 0L) + if (!forced && bestscore < 0L) best_targ = 0; return best_targ; @@ -867,7 +879,7 @@ best_target(struct monst *mtmp) /* Pet */ /* Pet considers and maybe executes a ranged attack */ int -pet_ranged_attk(struct monst *mtmp) +pet_ranged_attk(struct monst *mtmp, boolean forced) { struct monst *mtarg; int hungry = 0; @@ -882,7 +894,7 @@ pet_ranged_attk(struct monst *mtmp) /* Identify the best target in a straight line from the pet; * if there is such a target, we'll let the pet attempt an attack. */ - mtarg = best_target(mtmp); + mtarg = best_target(mtmp, forced); /* Hungry pets are unlikely to use breath/spit attacks */ if (mtarg && (!hungry || !rn2(5))) { @@ -942,7 +954,8 @@ pet_ranged_attk(struct monst *mtmp) */ if (mstatus != M_ATTK_MISS) return MMOVE_DONE; - } + } else if (forced) + (void) domonnoise(mtmp); return MMOVE_NOTHING; } @@ -965,7 +978,7 @@ dog_move( struct obj *obj = (struct obj *) 0; xint16 otyp; boolean cursemsg[9], do_eat = FALSE; - boolean better_with_displacing = FALSE; + boolean better_with_displacing = FALSE, ranged_only; coordxy nix, niy; /* position mtmp is (considering) moving to */ coordxy nx, ny; /* temporary coordinates */ xint16 cnt, uncursedcnt, chcnt; @@ -1078,6 +1091,8 @@ dog_move( if (!edog && (j = distu(nx, ny)) > 16 && j >= udist) continue; + ranged_only = FALSE; + if ((info[i] & ALLOW_M) && MON_AT(nx, ny)) { int mstatus; struct monst *mtmp2 = m_at(nx, ny); @@ -1098,16 +1113,28 @@ dog_move( int balk = mtmp->m_lev + ((5 * mtmp->mhp) / mtmp->mhpmax) - 2; if ((int) mtmp2->m_lev >= balk - || (mtmp2->data == &mons[PM_FLOATING_EYE] && rn2(10) - && mtmp->mcansee && haseyes(mtmp->data) && mtmp2->mcansee - && (perceives(mtmp->data) || !mtmp2->minvis)) - || (mtmp2->data == &mons[PM_GELATINOUS_CUBE] && rn2(10)) + || (mtmp2->mtame && mtmp->mtame && !Conflict) || (max_passive_dmg(mtmp2, mtmp) >= mtmp->mhp) || ((mtmp->mhp * 4 < mtmp->mhpmax || mtmp2->data->msound == MS_GUARDIAN - || mtmp2->data->msound == MS_LEADER) && mtmp2->mpeaceful - && !Conflict) - || (touch_petrifies(mtmp2->data) && !resists_ston(mtmp))) + || mtmp2->data->msound == MS_LEADER) + && mtmp2->mpeaceful && !Conflict)) { + continue; + } + if ((mtmp2->data == &mons[PM_FLOATING_EYE] && rn2(10) + && mtmp->mcansee && haseyes(mtmp->data) && mtmp2->mcansee + && (!mtmp2->minvis || perceives(mtmp->data)) + && !mon_reflects(mtmp, (char *) NULL)) + || (mtmp2->data == &mons[PM_GELATINOUS_CUBE] && rn2(10)) + || (touch_petrifies(mtmp2->data) && !resists_ston(mtmp))) { + /* only skip this foe if a ranged attack isn't viable */ + if (dist2(mtmp->mx, mtmp->my, mtmp2->mx, mtmp2->my) <= 2 + || best_target(mtmp, FALSE) != mtmp2) + continue; + ranged_only = TRUE; + } + /** FIXME: 'ranged_only' isn't used as intended yet **/ + if (ranged_only) continue; if (after) @@ -1180,6 +1207,7 @@ dog_move( /* (minion isn't interested; `cursemsg' stays FALSE) */ if (edog) { boolean can_reach_food = could_reach_item(mtmp, nx, ny); + for (obj = svl.level.objects[nx][ny]; obj; obj = obj->nexthere) { if (obj->cursed) { cursemsg[i] = TRUE; @@ -1236,7 +1264,7 @@ dog_move( * now's the time for ranged attacks. Note that the pet can move * after it performs its ranged attack. Should this be changed? */ - if ((i = pet_ranged_attk(mtmp)) != MMOVE_NOTHING) + if ((i = pet_ranged_attk(mtmp, FALSE)) != MMOVE_NOTHING) return i; newdogpos: @@ -1245,8 +1273,8 @@ dog_move( if (info[chi] & ALLOW_U) { if (mtmp->mleashed) { /* play it safe */ - pline("%s breaks loose of %s leash!", Monnam(mtmp), - mhis(mtmp)); + pline_mon(mtmp, "%s breaks loose of %s leash!", + Monnam(mtmp), mhis(mtmp)); m_unleash(mtmp, FALSE); } (void) mattacku(mtmp); @@ -1270,7 +1298,7 @@ dog_move( ? vobj_at(nix, niy) : 0; const char *what = o ? distant_name(o, doname) : something; - pline("%s %s reluctantly %s %s.", noit_Monnam(mtmp), + pline_mon(mtmp, "%s %s reluctantly %s %s.", noit_Monnam(mtmp), vtense((char *) 0, locomotion(mtmp->data, "step")), (is_flyer(mtmp->data) || is_floater(mtmp->data)) ? "over" : "onto", @@ -1362,7 +1390,7 @@ can_reach_location( continue; if (dist2(i, j, fx, fy) >= dist) continue; - if (IS_ROCK(levl[i][j].typ) && !passes_walls(mon->data) + if (IS_OBSTRUCTED(levl[i][j].typ) && !passes_walls(mon->data) && (!may_dig(i, j) || !tunnels(mon->data) /* tunnelling monsters can't do that on the rogue level */ || Is_rogue_level(&u.uz))) @@ -1422,10 +1450,23 @@ finish_meating(struct monst *mtmp) } } +/* + * variation of leashable() that takes a PM_ index */ +staticfn boolean +mnum_leashable(int mnum) +{ + return ((mnum >= LOW_PM && mnum <= HIGH_PM) + && mnum != PM_LONG_WORM && !unsolid(&mons[mnum]) + && (!nolimbs(&mons[mnum]) || has_head(&mons[mnum]))) + ? TRUE + : FALSE; +} + void quickmimic(struct monst *mtmp) { int idx = 0, trycnt = 5, spotted, seeloc; + boolean was_leashed = mtmp->mleashed; char buf[BUFSZ]; if (Protection_from_shape_changers || !mtmp->meating) @@ -1475,6 +1516,12 @@ quickmimic(struct monst *mtmp) : something; newsym(mtmp->mx, mtmp->my); + if (was_leashed + && (M_AP_TYPE(mtmp) != M_AP_MONSTER + || !mnum_leashable(mtmp->mappearance))) { + Your("leash goes slack."); + m_unleash(mtmp, FALSE); + } if (glyph_at(mtmp->mx, mtmp->my) != prev_glyph) You("%s %s %s where %s was!", seeloc ? "see" : "sense that", diff --git a/src/dokick.c b/src/dokick.c index 6d3d82cd2..45d0b097c 100644 --- a/src/dokick.c +++ b/src/dokick.c @@ -612,9 +612,9 @@ really_kick_object(coordxy x, coordxy y) Norep("You kick %s.", !isgold ? singular(gk.kickedobj, doname) : doname(gk.kickedobj)); - if (IS_ROCK(levl[x][y].typ) || closed_door(x, y)) { + if (IS_OBSTRUCTED(levl[x][y].typ) || closed_door(x, y)) { if ((!martial() && rn2(20) > ACURR(A_DEX)) - || IS_ROCK(levl[u.ux][u.uy].typ) || closed_door(u.ux, u.uy)) { + || IS_OBSTRUCTED(levl[u.ux][u.uy].typ) || closed_door(u.ux, u.uy)) { if (Blind) pline("It doesn't come loose."); else @@ -805,7 +805,7 @@ kickstr(char *buf, const char *kickobjnam) what = "a tree"; else if (IS_STWALL(gm.maploc->typ)) what = "a wall"; - else if (IS_ROCK(gm.maploc->typ)) + else if (IS_OBSTRUCTED(gm.maploc->typ)) what = "a rock"; else if (IS_THRONE(gm.maploc->typ)) what = "a throne"; @@ -949,7 +949,7 @@ kick_door(coordxy x, coordxy y, int avrg_attrib) gm.maploc->doormask = D_BROKEN; } feel_newsym(x, y); /* we know we broke it */ - unblock_point(x, y); /* vision */ + recalc_block_point(x, y); /* vision */ if (shopdoor) { add_damage(x, y, SHOP_DOOR_COST); pay_for_damage("break", FALSE); @@ -1340,6 +1340,7 @@ dokick(void) pline("%s burps loudly.", Monnam(u.ustuck)); break; } + FALLTHROUGH; /*FALLTHRU*/ default: Your("feeble kick has no effect."); @@ -1360,7 +1361,7 @@ dokick(void) * reachable for bracing purposes * Possible extension: allow bracing against stuff on the side? */ - if (isok(xx, yy) && !IS_ROCK(levl[xx][yy].typ) + if (isok(xx, yy) && !IS_OBSTRUCTED(levl[xx][yy].typ) && !IS_DOOR(levl[xx][yy].typ) && (!Is_airlevel(&u.uz) || !OBJ_AT(xx, yy))) { You("have nothing to brace yourself against."); @@ -1484,6 +1485,7 @@ drop_to(coord *cc, schar loc, coordxy x, coordxy y) cc->y = cc->x = 0; break; } + FALLTHROUGH; /*FALLTHRU*/ case MIGR_STAIRS_UP: case MIGR_LADDER_UP: @@ -1800,6 +1802,7 @@ obj_delivery(boolean near_hero) switch (where) { case MIGR_LADDER_UP: isladder = TRUE; + FALLTHROUGH; /*FALLTHRU*/ case MIGR_STAIRS_UP: case MIGR_SSTAIRS: diff --git a/src/dothrow.c b/src/dothrow.c index 286e00f1c..df019f6e4 100644 --- a/src/dothrow.c +++ b/src/dothrow.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 dothrow.c $NHDT-Date: 1709969638 2024/03/09 07:33:58 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.285 $ */ +/* NetHack 3.7 dothrow.c $NHDT-Date: 1737343372 2025/01/19 19:22:52 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.300 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -16,6 +16,7 @@ staticfn int gem_accept(struct monst *, struct obj *); staticfn boolean toss_up(struct obj *, boolean) NONNULLARG1; staticfn void sho_obj_return_to_u(struct obj * obj); staticfn void throwit_return(boolean); +staticfn void swallowit(struct obj *); staticfn struct obj *return_throw_to_inv(struct obj *, long, boolean, struct obj *); staticfn void tmiss(struct obj *, struct monst *, boolean); @@ -67,6 +68,7 @@ multishot_class_bonus( case PM_NINJA: if (skill == -P_SHURIKEN || skill == -P_DART) multishot++; + FALLTHROUGH; /*FALLTHRU*/ case PM_SAMURAI: /* role-specific launcher and its ammo */ @@ -175,6 +177,7 @@ throw_obj(struct obj *obj, int shotlimit) switch (P_SKILL(weapon_type(obj))) { case P_EXPERT: multishot++; + FALLTHROUGH; /*FALLTHRU*/ case P_SKILLED: if (!weakmultishot) @@ -250,6 +253,7 @@ throw_obj(struct obj *obj, int shotlimit) gm.m_shot.n = multishot; for (gm.m_shot.i = 1; gm.m_shot.i <= gm.m_shot.n; gm.m_shot.i++) { twoweap = u.twoweap; + assert(obj != NULL); /* m_shot.i <= m_shot.n guarantees this */ /* split this object off from its slot if necessary */ if (obj->quan > 1L) { otmp = splitobj(obj, 1L); @@ -470,7 +474,8 @@ dofire(void) on its caller to make sure hero is strong enough to throw that */ boolean uwep_Throw_and_Return = (uwep && AutoReturn(uwep, uwep->owornmask) && (uwep->oartifact != ART_MJOLLNIR - || ACURR(A_STR) >= STR19(25))); + || ACURR(A_STR) >= STR19(25))), + skip_fireassist = FALSE; int altres, res = ECMD_OK; /* @@ -500,6 +505,7 @@ dofire(void) throwing Mjollnir if quiver contains daggers] */ if (uwep_Throw_and_Return && (!obj || is_ammo(obj))) { obj = uwep; + skip_fireassist = TRUE; } else if (!obj) { if (!flags.autoquiver) { @@ -548,9 +554,12 @@ dofire(void) obj = uquiver; } - if (uquiver && is_ammo(uquiver) && iflags.fireassist) { + if (uquiver && is_ammo(uquiver) && iflags.fireassist + && !skip_fireassist) { struct obj *olauncher; + if (uwep && is_pole(uwep) && could_pole_mon()) + return use_pole(uwep, TRUE); /* Try to find a launcher */ if (ammo_and_launcher(uquiver, uwep)) { obj = uquiver; @@ -605,6 +614,7 @@ hitfloor( if (IS_ALTAR(levl[u.ux][u.uy].typ)) { doaltarobj(obj); } else if (verbosely) { + const char *verb = (obj->otyp == WAN_STRIKING) ? "strike" : "hit"; const char *surf = surface(u.ux, u.uy); struct trap *t = t_at(u.ux, u.uy); @@ -626,7 +636,7 @@ hitfloor( break; } } - pline("%s %s the %s.", Doname2(obj), otense(obj, "hit"), surf); + pline("%s %s the %s.", Doname2(obj), otense(obj, verb), surf); } if (hero_breaks(obj, u.ux, u.uy, BRK_FROM_INV)) @@ -676,14 +686,15 @@ walk_path( if (dx < 0) { x_change = -1; dx = -dx; - } else + } else { x_change = 1; + } if (dy < 0) { y_change = -1; dy = -dy; - } else + } else { y_change = 1; - + } i = err = 0; if (dx < dy) { while (i++ < dy) { @@ -767,7 +778,8 @@ hurtle_step(genericptr_t arg, coordxy x, coordxy y) struct monst *mon; boolean may_pass = TRUE, via_jumping, stopping_short; struct trap *ttmp; - int dmg = 0; + struct rm *lev; + int ltyp, dmg = 0; if (!isok(x, y)) { You_feel("the spirits holding you back."); @@ -779,69 +791,52 @@ hurtle_step(genericptr_t arg, coordxy x, coordxy y) } via_jumping = (EWwalking & I_SPECIAL) != 0L; stopping_short = (via_jumping && *range < 2); + lev = &levl[x][y]; + ltyp = lev->typ; if (!Passes_walls || !(may_pass = may_passwall(x, y))) { - boolean odoor_diag = (IS_DOOR(levl[x][y].typ) - && (levl[x][y].doormask & D_ISOPEN) - && (u.ux - x) && (u.uy - y)); - - if (IS_ROCK(levl[x][y].typ) || closed_door(x, y) || odoor_diag) { - const char *s; + const char *why = NULL; + boolean diagonal = (u.ux - x) != 0 && (u.uy - y) != 0, + open_door = IS_DOOR(ltyp) && (lev->doormask & D_ISOPEN) != 0, + odoor_diag = open_door && diagonal; + if (IS_OBSTRUCTED(levl[x][y].typ) + || closed_door(x, y) || odoor_diag) { + why = IS_TREE(ltyp) ? "bumping into a tree" + : IS_OBSTRUCTED(ltyp) ? "bumping into a wall" + : odoor_diag ? "bumping into a door frame" + : "bumping into a closed door"; if (odoor_diag) - You("hit the door edge!"); + You("hit the door frame!"); pline("Ouch!"); - if (IS_TREE(levl[x][y].typ)) - s = "bumping into a tree"; - else if (IS_ROCK(levl[x][y].typ)) - s = "bumping into a wall"; - else - s = "bumping into a door"; - dmg = rnd(2 + *range); - losehp(Maybe_Half_Phys(dmg), s, KILLED_BY); - wake_nearto(x,y, 10); - return FALSE; - } - if (levl[x][y].typ == IRONBARS) { + } else if (ltyp == IRONBARS) { + why = "crashing into iron bars"; You("crash into some iron bars. Ouch!"); - dmg = rnd(2 + *range); - losehp(Maybe_Half_Phys(dmg), "crashing into iron bars", - KILLED_BY); - wake_nearto(x,y, 20); - return FALSE; - } - if ((obj = sobj_at(BOULDER, x, y)) != 0) { + } else if ((obj = sobj_at(BOULDER, x, y)) != 0) { + why = "bumping into a boulder"; You("bump into a %s. Ouch!", xname(obj)); - dmg = rnd(2 + *range); - losehp(Maybe_Half_Phys(dmg), "bumping into a boulder", KILLED_BY); - wake_nearto(x,y, 10); - return FALSE; - } - if (!may_pass) { + } else if (!may_pass) { /* did we hit a no-dig non-wall position? */ + why = "touching the edge of the universe"; You("smack into something!"); - dmg = rnd(2 + *range); - losehp(Maybe_Half_Phys(dmg), "touching the edge of the universe", - KILLED_BY); - wake_nearto(x,y, 10); - return FALSE; - } - if ((u.ux - x) && (u.uy - y) && bad_rock(gy.youmonst.data, u.ux, y) - && bad_rock(gy.youmonst.data, x, u.uy)) { + } else if (diagonal + && bad_rock(gy.youmonst.data, u.ux, y) + && bad_rock(gy.youmonst.data, x, u.uy)) { boolean too_much = (gi.invent - && (inv_weight() + weight_cap() > 600)); + && (inv_weight() + weight_cap() > WT_TOOMUCH_DIAGONAL)); - /* Move at a diagonal. */ if (bigmonst(gy.youmonst.data) || too_much) { + why = "wedging into a narrow crevice"; You("%sget forcefully wedged into a crevice.", too_much ? "and all your belongings " : ""); - dmg = rnd(2 + *range); - losehp(Maybe_Half_Phys(dmg), "wedging into a narrow crevice", - KILLED_BY); - wake_nearto(x,y, 10); - return FALSE; } } + if (why) { + dmg = rnd(2 + *range); + losehp(Maybe_Half_Phys(dmg), why, KILLED_BY); + wake_nearto(x, y, 10); + return FALSE; + } } if ((mon = m_at(x, y)) != 0 @@ -918,7 +913,7 @@ hurtle_step(genericptr_t arg, coordxy x, coordxy y) /* if terrain type changes, levitation or flying might become blocked or unblocked; might issue message, so do this after map+vision has been updated for new location instead of right after u_on_newpos() */ - if (levl[u.ux][u.uy].typ != levl[ox][oy].typ) + if (ltyp != levl[ox][oy].typ) switch_terrain(); /* might be entering a special room (treasure zoo, thrown room, &c) that @@ -957,8 +952,7 @@ hurtle_step(genericptr_t arg, coordxy x, coordxy y) dotrap(ttmp, NO_TRAP_FLAGS); /* doesn't print messages */ } else if (ttmp->ttyp == FIRE_TRAP) { dotrap(ttmp, NO_TRAP_FLAGS); - } else if ((is_pit(ttmp->ttyp) || is_hole(ttmp->ttyp)) - && Sokoban) { + } else if ((is_pit(ttmp->ttyp) || is_hole(ttmp->ttyp)) && Sokoban) { /* air currents overcome the recoil in Sokoban; when jumping, caller performs last step and enters trap */ if (!via_jumping) @@ -1311,6 +1305,7 @@ toss_up(struct obj *obj, boolean hitsroof) Your("%s fails to protect you.", helm_simple_name(uarmh)); goto petrify; } + FALLTHROUGH; /*FALLTHRU*/ case CREAM_PIE: case BLINDING_VENOM: @@ -1352,7 +1347,7 @@ toss_up(struct obj *obj, boolean hitsroof) &dmg, rn1(18, 2)); if (!dmg) { /* probably wasn't a weapon; base damage on weight */ - dmg = ((int) obj->owt + 99) / 100; + dmg = ((int) obj->owt + (WT_TO_DMG - 1)) / WT_TO_DMG; dmg = (dmg <= 1) ? 1 : rnd(dmg); if (dmg > 6) dmg = 6; @@ -1462,9 +1457,20 @@ throwit_return(boolean clear_thrownobj) gt.thrownobj = (struct obj *) 0; } +staticfn void +swallowit(struct obj *obj) +{ + if (obj != uball) { + (void) mpickobj(u.ustuck, obj); /* clears 'gt.thrownobj' */ + throwit_return(FALSE); + } else + throwit_return(TRUE); +} + /* throw an object, NB: obj may be consumed in the process */ void -throwit(struct obj *obj, +throwit( + struct obj *obj, long wep_mask, /* used to re-equip returning boomerang */ boolean twoweap, /* used to restore twoweapon mode if * wielded weapon returns */ @@ -1472,10 +1478,11 @@ throwit(struct obj *obj, { struct monst *mon; int range, urange; + const struct throw_and_return_weapon *arw = autoreturn_weapon(obj); boolean crossbowing, impaired = (Confusion || Stunned || Blind || Hallucination || Fumbling), - tethered_weapon = (obj->otyp == AKLYS && (wep_mask & W_WEP) != 0); + tethered_weapon = (arw && arw->tethered && (wep_mask & W_WEP) != 0); gn.notonhead = FALSE; /* reset potentially stale value */ if ((obj->cursed || obj->greased) && (u.dx || u.dy) && !rn2(7)) { @@ -1612,9 +1619,9 @@ throwit(struct obj *obj, else if (is_art(obj, ART_MJOLLNIR)) range = (range + 1) / 2; /* it's heavy */ else if (tethered_weapon) /* primary weapon is aklys */ - /* if an aklys is going to return, range is limited by the + /* range of a tethered_weapon is limited by the length of the attached cord [implicit aspect of item] */ - range = min(range, BOLT_LIM / 2); + range = min(range, isqrt(arw->range)); else if (obj == uball && u.utrap && u.utraptype == TT_INFLOOR) range = 1; @@ -1670,12 +1677,7 @@ throwit(struct obj *obj, if (tethered_weapon) tmp_at(DISP_END, 0); } else if (u.uswallow && !iflags.returning_missile) { - swallowit: - if (obj != uball) { - (void) mpickobj(u.ustuck, obj); /* clears 'gt.thrownobj' */ - throwit_return(FALSE); - } else - throwit_return(TRUE); + swallowit(obj); return; } else { /* Mjollnir must be wielded to be thrown--caller verifies this; @@ -1721,8 +1723,10 @@ throwit(struct obj *obj, KILLED_BY); } - if (u.uswallow) - goto swallowit; + if (u.uswallow) { + swallowit(obj); + return; + } if (!ship_object(obj, u.ux, u.uy, FALSE)) dropy(obj); } @@ -1740,8 +1744,10 @@ throwit(struct obj *obj, capability back anyway, quivered or not shouldn't matter */ pline("%s to return!", Tobjnam(obj, "fail")); - if (u.uswallow) - goto swallowit; + if (u.uswallow) { + swallowit(obj); + return; + } /* continue below with placing 'obj' at target location */ } } @@ -1766,7 +1772,8 @@ throwit(struct obj *obj, || (is_lava(gb.bhitpos.x, gb.bhitpos.y) && !is_flammable(obj))) { Soundeffect(se_splash, 50); - pline((weight(obj) > 9) ? "Splash!" : "Plop!"); + pline((weight(obj) > WT_SPLASH_THRESHOLD) + ? "Splash!" : "Plop!"); } } if (flooreffects(obj, gb.bhitpos.x, gb.bhitpos.y, "fall")) { @@ -2084,7 +2091,7 @@ thitmonst( /* ...or any special item, if you've made him angry */ || !mon->mpeaceful) { /* give an explanation for keeping the item only if leader is - not doing it out of anger */ + not doing it out of anger */ if (mon->mpeaceful && !Deaf) { /* just in case, identify the object so its name will appear in the message */ @@ -2100,7 +2107,7 @@ thitmonst( (void) mpickobj(mon, obj); } else { /* under normal circumstances, leader will say something and - then return the item to the hero */ + then return the item to the hero */ boolean next2u = monnear(mon, u.ux, u.uy); finish_quest(obj); /* acknowledge quest completion */ @@ -2109,6 +2116,7 @@ thitmonst( if (!next2u) sho_obj_return_to_u(obj); obj = addinv(obj); /* back into your inventory */ + nhUse(obj); (void) encumber_msg(); } return 1; /* caller doesn't need to place it */ @@ -2588,12 +2596,14 @@ breakmsg(struct obj *obj, boolean in_view) default: /* glass or crystal wand */ if (obj->oclass != WAND_CLASS) impossible("breaking odd object (%d)?", obj->otyp); + FALLTHROUGH; /*FALLTHRU*/ case LENSES: case MIRROR: case CRYSTAL_BALL: case EXPENSIVE_CAMERA: to_pieces = " into a thousand pieces"; + FALLTHROUGH; /*FALLTHRU*/ case POT_WATER: /* really, all potions */ if (!in_view) diff --git a/src/drawing.c b/src/drawing.c index 7881e1f8e..587489053 100644 --- a/src/drawing.c +++ b/src/drawing.c @@ -83,8 +83,9 @@ const uchar def_r_oc_syms[MAXOCLASSES] = { /* * Convert the given character to an object class. If the character is not - * recognized, then MAXOCLASSES is returned. Used in detect.c, invent.c, - * objnam.c, options.c, pickup.c, sp_lev.c, lev_main.c, and tilemap.c. + * recognized, then MAXOCLASSES is returned. Used in detect.c, drawing.c, + * invent.c, o_init.c, objnam.c, options.c, pickup.c, sp_lev.c, and + * windows.c. */ int def_char_to_objclass(char ch) @@ -100,7 +101,8 @@ def_char_to_objclass(char ch) /* * Convert a character into a monster class. This returns the _first_ * match made. If there are no matches, return MAXMCLASSES. - * Used in detect.c, options.c, read.c, sp_lev.c, and lev_main.c + * Used in detect.c, drawing.c, mondata.c, options.c, pickup.c, + * sp_lev.c, and windows.c. */ int def_char_to_monclass(char ch) diff --git a/src/dungeon.c b/src/dungeon.c index e75808a31..5b172b815 100644 --- a/src/dungeon.c +++ b/src/dungeon.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 dungeon.c $NHDT-Date: 1715203022 2024/05/08 21:17:02 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.222 $ */ +/* NetHack 3.7 dungeon.c $NHDT-Date: 1737343478 2025/01/19 19:24:38 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.228 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -32,6 +32,9 @@ struct lchoice { char menuletter; }; +static mapseen *load_mapseen(NHFILE *); + +#ifndef SFCTOOL #if 0 staticfn void Fread(genericptr_t, int, int, dlb *); #endif @@ -57,6 +60,7 @@ staticfn void init_dungeon_set_depth(struct proto_dungeon *, int); staticfn void init_castle_tune(void); staticfn void fixup_level_locations(void); staticfn void free_proto_dungeon(struct proto_dungeon *); +staticfn void earth_sense(void); staticfn boolean init_dungeon_dungeons(lua_State *, struct proto_dungeon *, int); staticfn boolean unplaced_floater(struct dungeon *); @@ -147,41 +151,36 @@ save_dungeon( boolean perform_write, boolean free_data) { - int count; + int i, count; branch *curr, *next; mapseen *curr_ms, *next_ms; if (perform_write) { - if(nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &svn.n_dgns, sizeof svn.n_dgns); - bwrite(nhfp->fd, (genericptr_t) svd.dungeons, - sizeof(dungeon) * (unsigned) svn.n_dgns); - bwrite(nhfp->fd, (genericptr_t) &svd.dungeon_topology, - sizeof svd.dungeon_topology); - bwrite(nhfp->fd, (genericptr_t) svt.tune, sizeof tune); + Sfo_int(nhfp, &svn.n_dgns, "dungeon_count"); + for (i = 0; i < svn.n_dgns; ++i) { + Sfo_dungeon(nhfp, &svd.dungeons[i], "dungeon"); } + Sfo_dgn_topology(nhfp, &svd.dungeon_topology, "svd.dungeon_topology"); + Sfo_char(nhfp, svt.tune, "tune", (int) sizeof tune); for (count = 0, curr = svb.branches; curr; curr = curr->next) count++; - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &count, sizeof count); + Sfo_int(nhfp, &count, "branch_count"); for (curr = svb.branches; curr; curr = curr->next) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) curr, sizeof *curr); + Sfo_branch(nhfp, curr, "branch"); } count = maxledgerno(); - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &count, sizeof count); - bwrite(nhfp->fd, (genericptr_t) svl.level_info, - (unsigned) count * sizeof (struct linfo)); - bwrite(nhfp->fd, (genericptr_t) &svi.inv_pos, sizeof svi.inv_pos); + Sfo_int(nhfp, &count, "level_info_count"); + for (i = 0; i < count; ++i) { + Sfo_linfo(nhfp, &svl.level_info[i], "svl.level_info"); } + Sfo_nhcoord(nhfp, &svi.inv_pos, "svi.inv_pos"); + for (count = 0, curr_ms = svm.mapseenchn; curr_ms; curr_ms = curr_ms->next) count++; - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &count, sizeof count); + Sfo_int(nhfp, &count, "mapseen_count"); for (curr_ms = svm.mapseenchn; curr_ms; curr_ms = curr_ms->next) { save_mapseen(nhfp, curr_ms); @@ -205,32 +204,31 @@ save_dungeon( svm.mapseenchn = 0; } } +#endif /* !SFCTOOL */ /* Restore the dungeon structures. */ void restore_dungeon(NHFILE *nhfp) { branch *curr, *last; - int count = 0, i; + int count = 0; + int i; mapseen *curr_ms, *last_ms; - if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) &svn.n_dgns, sizeof svn.n_dgns); - mread(nhfp->fd, (genericptr_t) svd.dungeons, - sizeof (dungeon) * (unsigned) svn.n_dgns); - mread(nhfp->fd, (genericptr_t) &svd.dungeon_topology, - sizeof svd.dungeon_topology); - mread(nhfp->fd, (genericptr_t) svt.tune, sizeof tune); + Sfi_int(nhfp, &svn.n_dgns, "dungeon_count"); + for (i = 0; i < svn.n_dgns; ++i) { + Sfi_dungeon(nhfp, &svd.dungeons[i], "dungeon"); } + Sfi_dgn_topology(nhfp, &svd.dungeon_topology, "svd.dungeon_topology"); + Sfi_char(nhfp, svt.tune, "tune", (int) sizeof tune); + last = svb.branches = (branch *) 0; - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) &count, sizeof count); + Sfi_int(nhfp, &count, "branch_count"); for (i = 0; i < count; i++) { curr = (branch *) alloc(sizeof *curr); - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) curr, sizeof *curr); + Sfi_branch(nhfp, curr, "branch"); curr->next = (branch *) 0; if (last) last->next = curr; @@ -239,21 +237,19 @@ restore_dungeon(NHFILE *nhfp) last = curr; } - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) &count, sizeof count); + Sfi_int(nhfp, &count, "level_info_count"); if (count >= MAXLINFO) panic("level information count larger (%d) than allocated size", count); - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) svl.level_info, - (unsigned) count * sizeof (struct linfo)); - - if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) &svi.inv_pos, sizeof svi.inv_pos); - mread(nhfp->fd, (genericptr_t) &count, sizeof count); + for (i = 0; i < count; ++i) { + Sfi_linfo(nhfp, &svl.level_info[i], "svl.level_info"); } + Sfi_nhcoord(nhfp, &svi.inv_pos, "svi.inv_pos"); + + Sfi_int(nhfp, &count, "mapseen_count"); + last_ms = (mapseen *) 0; for (i = 0; i < count; i++) { curr_ms = load_mapseen(nhfp); @@ -266,6 +262,7 @@ restore_dungeon(NHFILE *nhfp) } } +#ifndef SFCTOOL #if 0 staticfn void Fread(genericptr_t ptr, int size, int nitems, dlb *stream) @@ -1153,10 +1150,11 @@ fixup_level_locations(void) if (on_level(&br->end2, &knox_level)) break; - if (br) + if (br) { br->end1.dnum = svn.n_dgns; - /* adjust the branch's position on the list */ - insert_branch(br, TRUE); + /* adjust the branch's position on the list */ + insert_branch(br, TRUE); + } } } } @@ -1434,6 +1432,7 @@ depth(d_level *lev) { return (schar) (svd.dungeons[lev->dnum].depth_start + lev->dlevel - 1); } +#endif /* !SFCTOOL */ /* are "lev1" and "lev2" actually the same? */ boolean @@ -1443,6 +1442,7 @@ on_level(d_level *lev1, d_level *lev2) && lev1->dlevel == lev2->dlevel); } +#ifndef SFCTOOL /* is this level referenced in the special level chain? */ s_level * Is_special(d_level *lev) @@ -1542,6 +1542,28 @@ prev_level(boolean at_stairs) } } +/* Dwarves have "earth sense", + able to sense if something is buried under their feet */ +staticfn void +earth_sense(void) +{ + struct obj *otmp; + + if (!Race_if(PM_DWARF)) + return; + if (u.usteed || Flying || Levitation || Upolyd) + return; + if (levl[u.ux][u.uy].typ != CORR + && levl[u.ux][u.uy].typ != ROOM) + return; + + for (otmp = svl.level.buriedobjlist; otmp; otmp = otmp->nobj) + if (u_at(otmp->ox, otmp->oy)) { + You("sense something below your %s.", makeplural(body_part(FOOT))); + return; + } +} + void u_on_newpos(coordxy x, coordxy y) { @@ -1569,6 +1591,7 @@ u_on_newpos(coordxy x, coordxy y) /* still on same level; might have come close enough to generic object(s) to redisplay them as specific objects */ see_nearby_objects(); + earth_sense(); } /* place you on a random location when arriving on a level */ @@ -1608,6 +1631,8 @@ u_on_rndspot(int upflag) switch_terrain(); } +#endif /* !SFCTOOL */ +#ifndef SFCTOOL boolean Is_botlevel(d_level *lev) { @@ -2048,7 +2073,7 @@ level_difficulty(void) } /* ring of aggravate monster */ if (EAggravate_monster) - res += 15; + res = res > 25 ? 50 : res * 2; return res; } @@ -2529,7 +2554,9 @@ query_annotation(d_level *lev) /* add new annotation, unless it's all spaces (which will be an empty string after mungspaces() above) */ if (*nbuf && strcmp(nbuf, " ")) { - mptr->custom = dupstr_n(nbuf,&mptr->custom_lth); + mptr->custom = dupstr(nbuf); + /* _lth field does not include trailing '\0' in the count */ + mptr->custom_lth = (unsigned) strlen(mptr->custom); } } @@ -2540,6 +2567,7 @@ donamelevel(void) query_annotation((d_level *) 0); return ECMD_OK; } +#endif /* !SFCTOOL */ /* exclusion zones */ void @@ -2565,17 +2593,14 @@ save_exclusions(NHFILE *nhfp) for (nez = 0, ez = sve.exclusion_zones; ez; ez = ez->next, ++nez) ; - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &nez, sizeof nez); - - for (ez = sve.exclusion_zones; ez; ez = ez->next) { - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &ez->zonetype, - sizeof ez->zonetype); - bwrite(nhfp->fd, (genericptr_t) &ez->lx, sizeof ez->lx); - bwrite(nhfp->fd, (genericptr_t) &ez->ly, sizeof ez->ly); - bwrite(nhfp->fd, (genericptr_t) &ez->hx, sizeof ez->hx); - bwrite(nhfp->fd, (genericptr_t) &ez->hy, sizeof ez->hy); + if (update_file(nhfp)) { + Sfo_int(nhfp, &nez, "exclusion_count"); + for (ez = sve.exclusion_zones; ez; ez = ez->next) { + Sfo_xint16(nhfp, &ez->zonetype, "exclusion-zonetype"); + Sfo_coordxy(nhfp, &ez->lx, "exclusion-lx"); + Sfo_coordxy(nhfp, &ez->ly, "exclusion-ly"); + Sfo_coordxy(nhfp, &ez->hx, "exclusion-hx"); + Sfo_coordxy(nhfp, &ez->hy, "exclusion-hy"); } } } @@ -2586,24 +2611,22 @@ load_exclusions(NHFILE *nhfp) struct exclusion_zone *ez; int nez = 0; - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) &nez, sizeof nez); + Sfi_int(nhfp, &nez, "exclusion_count"); while (nez-- > 0) { ez = (struct exclusion_zone *) alloc(sizeof *ez); - if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) &ez->zonetype, - sizeof ez->zonetype); - mread(nhfp->fd, (genericptr_t) &ez->lx, sizeof ez->lx); - mread(nhfp->fd, (genericptr_t) &ez->ly, sizeof ez->ly); - mread(nhfp->fd, (genericptr_t) &ez->hx, sizeof ez->hx); - mread(nhfp->fd, (genericptr_t) &ez->hy, sizeof ez->hy); - } + Sfi_xint16(nhfp, &ez->zonetype, "exclusion-zonetype"); + Sfi_coordxy(nhfp, &ez->lx, "exclusion-lx"); + Sfi_coordxy(nhfp, &ez->ly, "exclusion-ly"); + Sfi_coordxy(nhfp, &ez->hx, "exclusion-hx"); + Sfi_coordxy(nhfp, &ez->hy, "exclusion-hy"); ez->next = sve.exclusion_zones; sve.exclusion_zones = ez; } } +#ifndef SFCTOOL + /* find the particular mapseen object in the chain; may return null */ staticfn mapseen * find_mapseen(d_level *lev) @@ -2640,100 +2663,88 @@ rm_mapseen(int ledger_num) if (svd.dungeons[mptr->lev.dnum].ledger_start + mptr->lev.dlevel == ledger_num) break; - if (!mptr) return; if (mptr->custom) - free((genericptr_t) mptr->custom); + free((genericptr_t) mptr->custom), mptr->custom = (char *) NULL; - bp = mptr->final_resting_place; - while (bp) { + bpnext = mptr->final_resting_place; + while ((bp = bpnext) != NULL) { bpnext = bp->next; - free(bp); - bp = bpnext; + free((genericptr_t) bp); } if (mprev) { mprev->next = mptr->next; - free(mptr); } else { svm.mapseenchn = mptr->next; - free(mptr); } + free(mptr); } staticfn void save_mapseen(NHFILE *nhfp, mapseen *mptr) { branch *curr; - int brindx; + int i, brindx; for (brindx = 0, curr = svb.branches; curr; curr = curr->next, ++brindx) if (curr == mptr->br) break; - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &brindx, sizeof brindx); - - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &mptr->lev, sizeof mptr->lev); - bwrite(nhfp->fd, (genericptr_t) &mptr->feat, sizeof mptr->feat); - bwrite(nhfp->fd, (genericptr_t) &mptr->flags, sizeof mptr->flags); - bwrite(nhfp->fd, (genericptr_t) &mptr->custom_lth, - sizeof mptr->custom_lth); - } + Sfo_int(nhfp, &brindx, "mapseen-branch_index"); + Sfo_d_level(nhfp, &mptr->lev, "mapseen-d_level"); + Sfo_mapseen_feat(nhfp, &mptr->feat, "mapseen-feat"); + Sfo_mapseen_flags(nhfp, &mptr->flags, "mapseen-flags"); + Sfo_unsigned(nhfp, &mptr->custom_lth, "mapseen-custom_lth"); if (mptr->custom_lth) { - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) mptr->custom, mptr->custom_lth); - } + Sfo_char(nhfp, mptr->custom, "mapseen-custom", + (int) mptr->custom_lth); } - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &mptr->msrooms, sizeof mptr->msrooms); + for (i = 0; i < ((MAXNROFROOMS + 1) * 2); ++i) { + Sfo_mapseen_rooms(nhfp, &mptr->msrooms[i], "mapseen-msrooms"); } savecemetery(nhfp, &mptr->final_resting_place); } +#endif /* !SFCTOOL */ staticfn mapseen * load_mapseen(NHFILE *nhfp) { - int branchnum = 0, brindx; + int i, branchnum = 0, brindx; mapseen *load; branch *curr; load = (mapseen *) alloc(sizeof *load); - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) &branchnum, sizeof branchnum); + Sfi_int(nhfp, &branchnum, "mapseen-branch_index"); for (brindx = 0, curr = svb.branches; curr; curr = curr->next, ++brindx) if (brindx == branchnum) break; load->br = curr; - if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) &load->lev, sizeof load->lev); - mread(nhfp->fd, (genericptr_t) &load->feat, sizeof load->feat); - mread(nhfp->fd, (genericptr_t) &load->flags, sizeof load->flags); - mread(nhfp->fd, (genericptr_t) &load->custom_lth, - sizeof load->custom_lth); - } + Sfi_d_level(nhfp, &load->lev, "mapseen-d_level"); + Sfi_mapseen_feat(nhfp, &load->feat, "mapseen-feat"); + Sfi_mapseen_flags(nhfp, &load->flags, "mapseen-flags"); + Sfi_unsigned(nhfp, &load->custom_lth, "mapseen-custom_lth"); if (load->custom_lth) { /* length doesn't include terminator (which isn't saved & restored) */ load->custom = (char *) alloc(load->custom_lth + 1); - if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) load->custom, load->custom_lth); - } + Sfi_char(nhfp, load->custom, "mapseen-custom", + (int) load->custom_lth); load->custom[load->custom_lth] = '\0'; } else { load->custom = 0; } - if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) &load->msrooms, sizeof load->msrooms); + for (i = 0; i < ((MAXNROFROOMS + 1) * 2); ++i) { + Sfi_mapseen_rooms(nhfp, &load->msrooms[i], "mapseen-msrooms"); } restcemetery(nhfp, &load->final_resting_place); return load; } +#ifndef SFCTOOL DISABLE_WARNING_FORMAT_NONLITERAL @@ -2747,7 +2758,7 @@ overview_stats( char buf[BUFSZ], hdrbuf[QBUFSZ]; long ocount, osize, bcount, bsize, acount, asize; struct cemetery *ce; - mapseen *mptr = find_mapseen(&u.uz); + mapseen *mptr; ocount = bcount = acount = osize = bsize = asize = 0L; for (mptr = svm.mapseenchn; mptr; mptr = mptr->next) { @@ -3039,6 +3050,7 @@ count_feat_lastseentyp( } if (is_drawbridge_wall(x, y) < 0) break; + FALLTHROUGH; /*FALLTHRU*/ case DBWALL: case DRAWBRIDGE_DOWN: @@ -3706,7 +3718,7 @@ print_mapseen( } } } - +#endif /* !SFCTOOL */ #undef OF_INTEREST #undef ADDNTOBUF #undef ADDTOBUF diff --git a/src/eat.c b/src/eat.c index 9b992e1c7..b15f36ade 100644 --- a/src/eat.c +++ b/src/eat.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 eat.c $NHDT-Date: 1715177703 2024/05/08 14:15:03 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.334 $ */ +/* NetHack 3.7 eat.c $NHDT-Date: 1740534854 2025/02/25 17:54:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.344 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -23,6 +23,7 @@ staticfn boolean temp_givit(int, struct permonst *); staticfn void givit(int, struct permonst *); staticfn void eye_of_newt_buzz(void); staticfn void cpostfx(int); +staticfn void use_up_tin(struct obj *) NONNULLARG1; staticfn void consume_tin(const char *); staticfn void start_tin(struct obj *); staticfn int eatcorpse(struct obj *); @@ -609,6 +610,12 @@ eat_brains( boolean give_nutrit = FALSE; int result = M_ATTK_HIT, xtra_dmg = rnd(10); + /* previous tentacle attack might have triggered fatal passive + counterattack [callers ought to be updated to avoid this situation] */ + if (magr != &gy.youmonst && DEADMONSTER(magr)) { + return M_ATTK_AGR_DIED; + } + if (noncorporeal(pd)) { if (visflag) pline("%s brain is unharmed.", @@ -788,6 +795,10 @@ cprefx(int pm) if (!Stone_resistance && !(poly_when_stoned(gy.youmonst.data) && polymon(PM_STONE_GOLEM))) { + /* if food was a tin, use it up early to keep it out of bones */ + if (svc.context.tin.tin) + use_up_tin(svc.context.tin.tin); + Sprintf(svk.killer.name, "tasting %s meat", mons[pm].pmnames[NEUTRAL]); svk.killer.format = KILLED_BY; @@ -843,6 +854,7 @@ cprefx(int pm) make_slimed(10L, (char *) 0); delayed_killer(SLIMED, KILLED_BY_AN, ""); } + FALLTHROUGH; /* Fall through */ default: if (acidic(&mons[pm]) && Stoned) @@ -1159,19 +1171,23 @@ cpostfx(int pm) HSee_invisible |= FROMOUTSIDE; } newsym(u.ux, u.uy); + FALLTHROUGH; /*FALLTHRU*/ case PM_YELLOW_LIGHT: case PM_GIANT_BAT: make_stunned((HStun & TIMEOUT) + 30L, FALSE); + FALLTHROUGH; /*FALLTHRU*/ case PM_BAT: make_stunned((HStun & TIMEOUT) + 30L, FALSE); break; case PM_GIANT_MIMIC: tmp += 10; + FALLTHROUGH; /*FALLTHRU*/ case PM_LARGE_MIMIC: tmp += 20; + FALLTHROUGH; /*FALLTHRU*/ case PM_SMALL_MIMIC: tmp += 20; @@ -1232,6 +1248,14 @@ cpostfx(int pm) if (Unchanging) { You_feel("momentarily different."); /* same as poly trap */ } else { + /* polyself() is potentially fatal; if food is a tin, use it up + early to keep it out of bones */ + if (svc.context.tin.tin) { + use_up_tin(svc.context.tin.tin); + /* most tin effects end up being skipped */ + lesshungry(200 + (metallivorous(gy.youmonst.data) ? 5 : 0)); + } + You("%s.", (pm == PM_GENETIC_ENGINEER) ? "undergo a freakish metamorphosis" : "feel a change coming over you"); @@ -1265,6 +1289,7 @@ cpostfx(int pm) } else { pline("For some reason, that tasted bland."); } + FALLTHROUGH; /*FALLTHRU*/ default: check_intrinsics = TRUE; @@ -1486,18 +1511,34 @@ tin_variety( return r; } +/* finish consume_tin(); also potentially used by cprefx() and cpostfx() */ +staticfn void +use_up_tin(struct obj *tin) +{ + if (carried(tin)) + useup(tin); + else + useupf(tin, 1L); + /* reset tin context */ + svc.context.tin.tin = (struct obj *) NULL; + svc.context.tin.o_id = 0; +} + staticfn void consume_tin(const char *mesg) { const char *what; - int which, mnum, r; + int which, mnum, r, nutamt; + /* if you've eaten tin itself, chance to not eat contents gets bypassed */ + boolean always_eat = metallivorous(gy.youmonst.data); struct obj *tin = svc.context.tin.tin; r = tin_variety(tin, FALSE); if (tin->otrapped || (tin->cursed && r != HOMEMADE_TIN && !rn2(8))) { b_trapped("tin", NO_PART); tin = costly_tin(COST_DSTROY); - goto use_up_tin; + use_up_tin(tin); + return; } pline1(mesg); /* "You succeed in opening the tin." */ @@ -1508,7 +1549,10 @@ consume_tin(const char *mesg) pline("It turns out to be empty."); tin->dknown = tin->known = 1; tin = costly_tin(COST_OPEN); - goto use_up_tin; + use_up_tin(tin); + if (always_eat) + lesshungry(5); /* metallivorous hero ate the tin itself */ + return; } which = 0; /* 0=>plural, 1=>as-is, 2=>"the" prefix */ @@ -1530,14 +1574,17 @@ consume_tin(const char *mesg) else if (which == 2) what = the(what); - pline("It smells like %s.", what); - if (y_n("Eat it?") == 'n') { - if (flags.verbose) - You("discard the open tin."); - if (!Hallucination) - tin->dknown = tin->known = 1; - tin = costly_tin(COST_OPEN); - goto use_up_tin; + if (!always_eat) { + pline("It smells like %s.", what); + if (y_n("Eat it?") == 'n') { + if (flags.verbose) + You("discard the open tin."); + if (!Hallucination) + tin->dknown = tin->known = 1; + tin = costly_tin(COST_OPEN); + use_up_tin(tin); + return; + } } /* in case stop_occupation() was called on previous meal */ @@ -1548,31 +1595,44 @@ consume_tin(const char *mesg) eating_conducts(&mons[mnum]); tin->dknown = tin->known = 1; - cprefx(mnum); - cpostfx(mnum); - /* charge for one at pre-eating cost */ - tin = costly_tin(COST_OPEN); + tin = svc.context.tin.tin = costly_tin(COST_OPEN); + + /* cprefx() or cpostfx() might use up tin to keep it out of bones */ + cprefx(mnum); + if (svc.context.tin.tin) + cpostfx(mnum); + if (!svc.context.tin.tin) + return; if (tintxts[r].nut < 0) { /* rotten */ make_vomiting((long) rn1(15, 10), FALSE); } else { - int nutamt = tintxts[r].nut; - + nutamt = tintxts[r].nut; /* nutrition from a homemade tin (made from a single corpse) shouldn't be more than nutrition from the corresponding corpse; other tinning modes might use more than one corpse or add extra ingredients so aren't similarly restricted */ if (r == HOMEMADE_TIN && nutamt > mons[mnum].cnutrit) nutamt = mons[mnum].cnutrit; + if (always_eat) + nutamt += 5; /* metallivorous hero ate the tin itself */ + /* use up tin now; lesshungry() could be fatal and produce bones */ + use_up_tin(tin), tin = NULL; lesshungry(nutamt); } if (tintxts[r].greasy) { - /* Assume !Glib, because you can't open tins when Glib. */ - make_glib(rn1(11, 5)); /* 5..15 */ - pline("Eating %s food made your %s very slippery.", - tintxts[r].txt, fingers_or_gloves(TRUE)); + /* normal hero is !Glib, because you can't open tins when Glib, + but one poly'd into metallivorous form might already be Glib; + it's debatable whether a rock mole should have its paws made + slippery when eating a greasy tin, but we'll go with that... */ + int alreadyglib = (int) (Glib & TIMEOUT); + + make_glib(alreadyglib + rn1(11, 5)); /* 5..15 */ + pline("Eating %s food made your %s %s slippery.", + tintxts[r].txt, fingers_or_gloves(TRUE), + alreadyglib ? "even more" : "very"); } } else { /* spinach... */ @@ -1584,11 +1644,12 @@ consume_tin(const char *mesg) tin->dknown = tin->known = 1; } - if (y_n("Eat it?") == 'n') { + if (!always_eat && y_n("Eat it?") == 'n') { if (flags.verbose) You("discard the open tin."); tin = costly_tin(COST_OPEN); - goto use_up_tin; + use_up_tin(tin); + return; } /* @@ -1612,19 +1673,19 @@ consume_tin(const char *mesg) : (flags.female ? "Olive Oyl" : "Bluto")); gainstr(tin, 0, FALSE); - tin = costly_tin(COST_OPEN); - lesshungry(tin->blessed ? 600 /* blessed */ - : !tin->cursed ? (400 + rnd(200)) /* uncursed */ - : (200 + rnd(400))); /* cursed */ + tin = svc.context.tin.tin = costly_tin(COST_OPEN); + nutamt = (tin->blessed ? 600 /* blessed */ + : !tin->cursed ? (400 + rnd(200)) /* uncursed */ + : (200 + rnd(400))); /* cursed */ + if (always_eat) + nutamt += 5; /* metallivorous hero also eats the tin itself */ + /* use up tin first; lesshungry() could be fatal and produce bones */ + use_up_tin(tin), tin = NULL; + lesshungry(nutamt); } - - use_up_tin: - if (carried(tin)) - useup(tin); - else - useupf(tin, 1L); - svc.context.tin.tin = (struct obj *) 0; - svc.context.tin.o_id = 0; + if (tin) + use_up_tin(tin); + return; } /* called during each move whilst opening a tin */ @@ -2094,6 +2155,7 @@ fprefx(struct obj *otmp) break; } iter_mons(garlic_breath); + FALLTHROUGH; /*FALLTHRU*/ default: if (otmp->otyp == SLIME_MOLD && !otmp->cursed diff --git a/src/end.c b/src/end.c index 519b599dc..ba2ea4f19 100644 --- a/src/end.c +++ b/src/end.c @@ -9,12 +9,12 @@ #ifndef NO_SIGNAL #include #endif -#include #ifndef LONG_MAX #include #endif #include "dlb.h" +#ifndef SFCTOOL #ifndef NO_SIGNAL staticfn void done_intr(int); # if defined(UNIX) || defined(VMS) || defined(__EMX__) @@ -34,15 +34,11 @@ staticfn void dump_plines(void); #endif staticfn void dump_everything(int, time_t); staticfn void fixup_death(int); +#endif /* SFCTOOL */ staticfn int wordcount(char *); staticfn void bel_copy1(char **, char *); -#if defined(__BEOS__) || defined(MICRO) || defined(OS2) || defined(WIN32) -ATTRNORETURN extern void nethack_exit(int) NORETURN; -#else -#define nethack_exit exit -#endif - +#ifndef SFCTOOL #define done_stopprint program_state.stopprint /* @@ -77,7 +73,7 @@ done1(int sig_unused UNUSED) #ifndef NO_SIGNAL (void) signal(SIGINT, SIG_IGN); #endif - iflags.debug_fuzzer = FALSE; + iflags.debug_fuzzer = fuzzer_off; if (flags.ignintr) { #ifndef NO_SIGNAL (void) signal(SIGINT, (SIG_RET_TYPE) done1); @@ -118,7 +114,7 @@ done2(void) if (abandon_tutorial) schedule_goto(&u.ucamefrom, UTOTYPE_ATSTAIRS, - "Resuming regular play", (char *) 0); + "Resuming regular play.", (char *) 0); return ECMD_OK; } @@ -211,7 +207,12 @@ done_in_by(struct monst *mtmp, int how) svk.killer.format = KILLED_BY; } /* _the_ ghost of Dudley */ +#if 0 + /* hardfought */ + if (has_ebones(mtmp)) { +#else if (mptr == &mons[PM_GHOST] && has_mgivenname(mtmp)) { +#endif Strcat(buf, "the "); svk.killer.format = KILLED_BY; } @@ -254,6 +255,10 @@ done_in_by(struct monst *mtmp, int how) : "%s imitating %s", realnm, shape); mptr = mtmp->data; /* reset for mimicker case */ +#if 0 /* hardfought */ + } else if (has_ebones(mtmp)) { + Strcpy(buf, m_monnam(mtmp)); +#endif } else if (mptr == &mons[PM_GHOST]) { Strcat(buf, "ghost"); if (has_mgivenname(mtmp)) @@ -271,8 +276,11 @@ done_in_by(struct monst *mtmp, int how) Strcat(buf, m_monnam(mtmp)); } else { Strcat(buf, pmname(mptr, Mgender(mtmp))); - if (has_mgivenname(mtmp)) - Sprintf(eos(buf), " called %s", MGIVENNAME(mtmp)); + if (has_mgivenname(mtmp)) { + Sprintf(eos(buf), " %s %s", + has_ebones(mtmp) ? "of" : "called", + MGIVENNAME(mtmp)); + } } Strcpy(svk.killer.name, buf); @@ -625,6 +633,7 @@ disclose(int how, boolean taken) if (c == 'y') { /* caller has already ID'd everything; we pass 'want_reply=True' to force display_pickinv() to avoid using WIN_INVENT */ + iflags.force_invmenu = FALSE; (void) display_inventory((char *) 0, TRUE); container_contents(gi.invent, TRUE, TRUE, FALSE); } @@ -1236,14 +1245,15 @@ really_done(int how) display_nhwindow(WIN_MESSAGE, FALSE); if (how != PANICKED) { - struct obj *obj; + struct obj *obj, *nextobj; /* * This is needed for both inventory disclosure and dumplog. * Both are optional, so do it once here instead of duplicating * it in both of those places. */ - for (obj = gi.invent; obj; obj = obj->nobj) { + for (obj = gi.invent; obj; obj = nextobj) { + nextobj = obj->nobj; discover_object(obj->otyp, TRUE, FALSE); obj->known = obj->bknown = obj->dknown = obj->rknown = 1; set_cknown_lknown(obj); /* set flags when applicable */ @@ -1747,10 +1757,9 @@ save_killers(NHFILE *nhfp) { struct kinfo *kptr; - if (perform_bwrite(nhfp)) { + if (update_file(nhfp)) { for (kptr = &svk.killer; kptr; kptr = kptr->next) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) kptr, sizeof (struct kinfo)); + Sfo_kinfo(nhfp, kptr, "kinfo"); } } if (release_data(nhfp)) { @@ -1761,6 +1770,7 @@ save_killers(NHFILE *nhfp) } } } +#endif /* !SFCTOOL */ void restore_killers(NHFILE *nhfp) @@ -1768,8 +1778,7 @@ restore_killers(NHFILE *nhfp) struct kinfo *kptr; for (kptr = &svk.killer; kptr != (struct kinfo *) 0; kptr = kptr->next) { - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) kptr, sizeof(struct kinfo)); + Sfi_kinfo(nhfp, kptr, "kinfo"); if (kptr->next) { kptr->next = (struct kinfo *) alloc(sizeof (struct kinfo)); } @@ -1924,12 +1933,12 @@ NH_abort(char *why USED_FOR_CRASHREPORT) panictrace_setsignals(FALSE); #endif #endif /* PANICTRACE */ -#ifdef WIN32 +#if defined(WIN32) win32_abort(); #else abort(); #endif } -#undef USED_FOR_CRASHREPORT +#undef USED_FOR_CRASHREPORT /*end.c*/ diff --git a/src/engrave.c b/src/engrave.c index 8105efeec..e029e87dc 100644 --- a/src/engrave.c +++ b/src/engrave.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 engrave.c $NHDT-Date: 1713657576 2024/04/20 23:59:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.157 $ */ +/* NetHack 3.7 engrave.c $NHDT-Date: 1737345573 2025/01/19 19:59:33 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.165 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -37,7 +37,7 @@ struct _doengrave_ctx { size_t len; /* # of nonspace chars of new engraving text */ }; - +#ifndef SFCTOOL staticfn int stylus_ok(struct obj *); staticfn boolean u_can_engrave(void); staticfn void doengrave_ctx_init(struct _doengrave_ctx *); @@ -48,15 +48,16 @@ staticfn int engrave(void); staticfn const char *blengr(void); char * -random_engraving(char *outbuf) +random_engraving(char *outbuf, char *pristine_copy) { const char *rumor; /* a random engraving may come from the "rumors" file, or from the "engrave" file (formerly in an array here) */ - if (!rn2(4) || !(rumor = getrumor(0, outbuf, TRUE)) || !*rumor) - (void) get_rnd_text(ENGRAVEFILE, outbuf, rn2, MD_PAD_RUMORS); + if (!rn2(4) || !(rumor = getrumor(0, pristine_copy, TRUE)) || !*rumor) + (void) get_rnd_text(ENGRAVEFILE, pristine_copy, rn2, MD_PAD_RUMORS); + Strcpy(outbuf, pristine_copy); wipeout_text(outbuf, (int) (strlen(outbuf) / 4), 0); return outbuf; } @@ -214,13 +215,16 @@ can_reach_floor(boolean check_pit) /* give a message after caller has determined that hero can't reach */ void -cant_reach_floor(coordxy x, coordxy y, boolean up, boolean check_pit) +cant_reach_floor(coordxy x, coordxy y, boolean up, + boolean check_pit, boolean wand_engraving) { - You("can't reach the %s.", - up ? ceiling(x, y) - : (check_pit && can_reach_floor(FALSE)) - ? "bottom of the pit" - : surface(x, y)); + pline("%s can't reach the %s.", + wand_engraving + ? "The wand does nothing more, and the tip of the wand" + : "You", + up ? ceiling(x, y) + : (check_pit && can_reach_floor(FALSE)) ? "bottom of the pit" + : surface(x, y)); } struct engr * @@ -285,6 +289,31 @@ wipe_engr_at(coordxy x, coordxy y, xint16 cnt, boolean magical) } } +/* + * Returns: + * non-zero if it can be felt + */ +boolean +engr_can_be_felt(struct engr *ep) +{ + boolean canfeel = FALSE; + + switch (ep->engr_type) { + case ENGRAVE: + case HEADSTONE: + case BURN: + canfeel = TRUE; + break; + case DUST: + case MARK: + case ENGR_BLOOD: + default: + canfeel = FALSE; + break; + } + return canfeel; +} + void read_engr_at(coordxy x, coordxy y) { @@ -292,7 +321,7 @@ read_engr_at(coordxy x, coordxy y) const char *eloc = surface(x, y); int sensed = 0; - /* Sensing an engraving does not require sight, + /* Sensing an engraving does not require sight for some engraving types, * nor does it necessarily imply comprehension (literacy). */ if (ep && ep->engr_txt[actual_text][0]) { @@ -355,6 +384,7 @@ read_engr_at(coordxy x, coordxy y) You("%s: \"%s\".", (Blind) ? "feel the words" : "read", et); Strcpy(ep->engr_txt[remembered_text], ep->engr_txt[actual_text]); ep->eread = 1; + ep->erevealed = 1; if (svc.context.run > 0) nomul(0); } @@ -365,13 +395,21 @@ void make_engr_at( coordxy x, coordxy y, const char *s, + const char *pristine_s, long e_time, int e_type) { int i; struct engr *ep; unsigned smem = Strlen(s) + 1; + boolean havepristine = FALSE; + if (pristine_s != NULL) { + unsigned prmem = Strlen(pristine_s) + 1; + if (prmem > smem) + smem = prmem; + havepristine = TRUE; + } if ((ep = engr_at(x, y)) != 0) del_engr(ep); @@ -386,6 +424,8 @@ make_engr_at( ep->engr_txt[pristine_text] = ep->engr_txt[remembered_text] + smem; for(i = 0; i < text_states; ++i) Strcpy(ep->engr_txt[i], s); + if (havepristine) + Strcpy(ep->engr_txt[pristine_text], pristine_s); if (!strcmp(s, "Elbereth")) { /* engraving "Elbereth": if done when making a level, it creates an old-style Elbereth that deters monsters when any objects are @@ -399,7 +439,8 @@ make_engr_at( ep->engr_type = (xint8) ((e_type > 0) ? e_type : rnd(N_ENGRAVE - 1)); ep->engr_szeach = smem; ep->engr_alloc = smem * 3; - /* we do not set ep->eread; the caller will need to if required */ + /* we do not set ep->eread or ep->erevealed; + * the caller will need to if required */ } /* delete any engraving at location */ @@ -455,7 +496,7 @@ u_can_engrave(void) pline("What would you write? \"Jonah was here\"?"); return FALSE; } else if (is_whirly(u.ustuck->data)) { - cant_reach_floor(u.ux, u.uy, FALSE, FALSE); + cant_reach_floor(u.ux, u.uy, FALSE, FALSE, FALSE); return FALSE; } /* Note: for amorphous engulfers, writing attempt is allowed here @@ -564,7 +605,7 @@ doengrave_sfx_item_WAN(struct _doengrave_ctx *de) if (de->oep) { if (!Blind) { de->type = (xint16) 0; /* random */ - (void) random_engraving(de->buf); + (void) random_engraving(de->buf, de->ebuf); } else { /* keep the same type so that feels don't change and only the text is altered, @@ -606,6 +647,7 @@ doengrave_sfx_item_WAN(struct _doengrave_ctx *de) "A few ice cubes drop from the wand."); if (!de->oep || (de->oep->engr_type != BURN)) break; + FALLTHROUGH; /*FALLTHRU*/ case WAN_CANCELLATION: case WAN_MAKE_INVISIBLE: @@ -706,6 +748,7 @@ doengrave_sfx_item(struct _doengrave_ctx *de) de->type = DUST; break; } + FALLTHROUGH; /*FALLTHRU*/ /* Objects too large to engrave with */ case BALL_CLASS: @@ -901,6 +944,7 @@ doengrave(void) char *sp; /* Place holder for space count of engr text */ struct _doengrave_ctx *de; int retval; + boolean initial_msg_given = FALSE; /* Can the adventurer engrave at all? */ if (!u_can_engrave()) @@ -942,12 +986,19 @@ doengrave(void) Your("message dissolves..."); goto doengr_exit; } - if (de->otmp->oclass != WAND_CLASS && !can_reach_floor(TRUE)) { - cant_reach_floor(u.ux, u.uy, FALSE, TRUE); - goto doengr_exit; + if (!can_reach_floor(TRUE)) { + if (de->otmp->oclass != WAND_CLASS) { + cant_reach_floor(u.ux, u.uy, FALSE, TRUE, FALSE); + goto doengr_exit; + } else { + You("gesture, with your wand, towards the %s below you.", + surface(u.ux, u.uy)); + initial_msg_given = TRUE; + } } if (IS_ALTAR(levl[u.ux][u.uy].typ)) { - You("make a motion towards the altar with %s.", de->writer); + if (!initial_msg_given) + You("make a motion towards the altar with %s.", de->writer); altar_wrath(u.ux, u.uy); goto doengr_exit; } @@ -994,6 +1045,7 @@ doengrave(void) if (de->teleengr) { rloc_engr(de->oep); de->oep->eread = 0; + de->oep->erevealed = 0; de->disprefresh = TRUE; de->oep = (struct engr *) 0; } @@ -1006,12 +1058,13 @@ doengrave(void) if (*de->buf) { struct engr *tmp_ep; - make_engr_at(u.ux, u.uy, de->buf, svm.moves, de->type); + make_engr_at(u.ux, u.uy, de->buf, de->ebuf, svm.moves, de->type); tmp_ep = engr_at(u.ux, u.uy); if (!Blind) { if (tmp_ep != 0) { pline_The("engraving now reads: \"%s\".", de->buf); tmp_ep->eread = 1; + tmp_ep->erevealed = 1; de->disprefresh = TRUE; } } @@ -1032,7 +1085,7 @@ doengrave(void) if (!de->ptext) { if (de->otmp && de->otmp->oclass == WAND_CLASS && !can_reach_floor(TRUE)) - cant_reach_floor(u.ux, u.uy, FALSE, TRUE); + cant_reach_floor(u.ux, u.uy, FALSE, TRUE, TRUE); de->ret = ECMD_TIME; goto doengr_exit; } @@ -1317,6 +1370,7 @@ engrave(void) obj_extract_self(stylus); stylus = hold_another_object(stylus, "You drop one %s!", doname(stylus), (char *) NULL); + nhUse(stylus); } else if (dulled && stylus->known) { /* reflect change in stylus->spe; not needed for splitstack since hold_another_object() does this */ @@ -1390,11 +1444,13 @@ engrave(void) (void) strncat(buf, svc.context.engraving.nextc, min(space_left, endc - svc.context.engraving.nextc)); - make_engr_at(u.ux, u.uy, buf, svm.moves - gm.multi, + make_engr_at(u.ux, u.uy, buf, NULL, svm.moves - gm.multi, svc.context.engraving.type); oep = engr_at(u.ux, u.uy); - if (oep) + if (oep) { oep->eread = 1; + oep->erevealed = 1; + } if (*endc) { svc.context.engraving.nextc = endc; @@ -1434,6 +1490,22 @@ sanitize_engravings(void) } } +/* mark all engravings as not-discovered/not-read when saving bones */ +void +forget_engravings(void) +{ + struct engr *ep; + + for (ep = head_engr; ep; ep = ep->nxt_engr) { + ep->erevealed = ep->eread = 0; + + /* Note: engr_txt[actual_text], engr_txt[rememberd_text], and + * engr_txt[pristine_text] retain their original text rather + * than get updated to reflect each engraving's current text. + * Does it matter? */ + } +} + void engraving_sanity_check(void) { @@ -1465,55 +1537,62 @@ void save_engravings(NHFILE *nhfp) { struct engr *ep, *ep2; - unsigned no_more_engr = 0; + unsigned no_more_engr = 0, engr_alloc; + unsigned szeach; for (ep = head_engr; ep; ep = ep2) { ep2 = ep->nxt_engr; if (ep->engr_alloc - && ep->engr_txt[actual_text][0] && perform_bwrite(nhfp)) { - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &(ep->engr_alloc), - sizeof ep->engr_alloc); - bwrite(nhfp->fd, (genericptr_t) ep, - sizeof (struct engr) + ep->engr_alloc); - } + && ep->engr_txt[actual_text][0] && update_file(nhfp)) { + engr_alloc = (unsigned) ep->engr_alloc; + 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[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); + Sfo_char(nhfp, ep->engr_txt[remembered_text], "engraving-remembered_text", szeach); + Sfo_char(nhfp, ep->engr_txt[pristine_text], "engraving-pristine_text", szeach); } if (release_data(nhfp)) dealloc_engr(ep); } - if (perform_bwrite(nhfp)) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &no_more_engr, - sizeof no_more_engr); + if (update_file(nhfp)) { + Sfo_unsigned(nhfp, &no_more_engr, "engraving-engr_alloc"); } if (release_data(nhfp)) head_engr = 0; } +#endif /* !SFCTOOL */ void rest_engravings(NHFILE *nhfp) { struct engr *ep; unsigned lth = 0; + unsigned szeach; head_engr = 0; while (1) { - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) <h, sizeof (unsigned)); - + Sfi_unsigned(nhfp, <h, "engraving-engr_alloc"); if (lth == 0) return; ep = newengr(lth); - if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) ep, sizeof (struct engr) + lth); - } + Sfi_engr(nhfp, ep, "engraving"); + 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[remembered_text] = ep->engr_txt[actual_text] - + ep->engr_szeach; - ep->engr_txt[pristine_text] = ep->engr_txt[remembered_text] - + ep->engr_szeach; + 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], + "engraving-actual_text", (int) szeach); + Sfi_char(nhfp, ep->engr_txt[remembered_text], + "engraving-remembered_text", (int) szeach); + Sfi_char(nhfp, ep->engr_txt[pristine_text], + "engraving-pristine_text", (int) szeach); + while (ep->engr_txt[actual_text][0] == ' ') ep->engr_txt[actual_text]++; while (ep->engr_txt[remembered_text][0] == ' ') @@ -1525,6 +1604,7 @@ rest_engravings(NHFILE *nhfp) } } +#ifndef SFCTOOL DISABLE_WARNING_FORMAT_NONLITERAL /* to support '#stats' wizard-mode command */ @@ -1604,7 +1684,7 @@ make_grave(coordxy x, coordxy y, const char *str) del_engr_at(x, y); if (!str) str = get_rnd_text(EPITAPHFILE, buf, rn2, MD_PAD_RUMORS); - make_engr_at(x, y, str, 0L, HEADSTONE); + make_engr_at(x, y, str, NULL, 0L, HEADSTONE); return; } @@ -1632,15 +1712,18 @@ see_engraving(struct engr *ep) newsym(ep->engr_x, ep->engr_y); } -/* like see_engravings() but overrides vision, but - only for some types of engravings that can be felt */ +/* like see_engravings() but overrides vision, but only for some types + of engravings that can be felt [this isn't actually used anywhere?] */ void feel_engraving(struct engr *ep) { - ep->eread = 1; - map_engraving(ep, 1); - /* in case it's beneath something, redisplay the something */ - newsym(ep->engr_x, ep->engr_y); + if (engr_can_be_felt(ep)) { + ep->eread = 1; + ep->erevealed = 1; + map_engraving(ep, 1); + /* in case it's beneath something, redisplay the something */ + newsym(ep->engr_x, ep->engr_y); + } } static const char blind_writing[][21] = { @@ -1669,5 +1752,5 @@ blengr(void) { return ROLL_FROM(blind_writing); } - +#endif /* !SFCTOOL */ /*engrave.c*/ diff --git a/src/explode.c b/src/explode.c index f87c6008d..36c3aca59 100644 --- a/src/explode.c +++ b/src/explode.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 explode.c $NHDT-Date: 1619553210 2021/04/27 19:53:30 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.77 $ */ +/* NetHack 3.7 explode.c $NHDT-Date: 1736530208 2025/01/10 09:30:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.122 $ */ /* Copyright (C) 1990 by Ken Arromdee */ /* NetHack may be freely redistributed. See license for details. */ @@ -794,7 +794,7 @@ scatter(coordxy sx, coordxy sy, /* location of objects to scatter */ if (cansee(sx, sy)) { pline("%s.", Tobjnam(otmp, "crumble")); } else { - Soundeffect(se_stone_crumbling, 100); + Soundeffect(se_stone_crumbling, 100); You_hear("stone crumbling."); } (void) break_statue(otmp); @@ -807,7 +807,7 @@ scatter(coordxy sx, coordxy sy, /* location of objects to scatter */ } else if ((scflags & MAY_DESTROY) != 0 && (!rn2(10) || (objects[otmp->otyp].oc_material == GLASS || otmp->otyp == EGG))) { - if (breaks(otmp, (coordxy) sx, (coordxy) sy)) + if (breaks(otmp, sx, sy)) used_up = TRUE; } @@ -976,6 +976,7 @@ explode_oil(struct obj *obj, coordxy x, coordxy y) if (!obj->lamplit) impossible("exploding unlit oil"); end_burn(obj, TRUE); + obj->how_lost = LOST_EXPLODING; splatter_burning_oil(x, y, diluted_oil); } diff --git a/src/extralev.c b/src/extralev.c index 971bf08c1..e0c2afc84 100644 --- a/src/extralev.c +++ b/src/extralev.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 extralev.c $NHDT-Date: 1596498169 2020/08/03 23:42:49 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.19 $ */ +/* NetHack 3.7 extralev.c $NHDT-Date: 1737345573 2025/01/19 19:59:33 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.28 $ */ /* Copyright 1988, 1989 by Ken Arromdee */ /* NetHack may be freely redistributed. See license for details. */ @@ -301,6 +301,7 @@ makerogueghost(void) return; ghost->msleeping = 1; ghost = christen_monst(ghost, roguename()); + nhUse(ghost); if (rn2(4)) { ghostobj = mksobj_at(FOOD_RATION, x, y, FALSE, FALSE); diff --git a/src/files.c b/src/files.c index e522bc4e0..c984c673e 100644 --- a/src/files.c +++ b/src/files.c @@ -1,13 +1,38 @@ -/* NetHack 3.7 files.c $NHDT-Date: 1717449127 2024/06/03 21:12:07 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.399 $ */ +/* NetHack 3.7 files.c $NHDT-Date: 1740532826 2025/02/25 17:20:26 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.417 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ #define NEED_VARARGS +#if defined(WIN32) +#include "win32api.h" +#endif + #include "hack.h" #include "dlb.h" -#include + +#ifdef SFCTOOL +#ifdef TTY_GRAPHICS +#undef TTY_GRAPHICS +#endif +#ifdef mark_synch +#undef mark_synch +#endif +#define mark_synch() +#ifdef raw_print +#undef raw_print +#endif +#define raw_print(a) +#ifdef WINDOWPORT +#undef WINDOWPORT +#endif +#define WINDOWPORT(x) FALSE +#ifdef clear_nhwindow +#undef clear_nhwindow +#endif +#define clear_nhwindow(x) +#endif /* SFCTOOL */ #ifdef TTY_GRAPHICS #include "wintty.h" /* more() */ @@ -45,7 +70,6 @@ const #if defined(UNIX) && defined(SELECTSAVED) #include #include -#include #endif #if defined(UNIX) || defined(VMS) || !defined(NO_SIGNAL) @@ -79,6 +103,9 @@ static char fqn_filename_buffer[FQN_NUMBUF][FQN_MAX_FILENAME]; #if defined(WIN32) #include +#include +#define F_OK 0 +#define access _access #endif #ifdef AMIGA @@ -98,11 +125,15 @@ extern void amii_set_text_font(char *, int); #endif #define Close close #ifndef WIN_CE +#ifdef DeleteFile +#undef DeleteFile +#endif #define DeleteFile unlink #endif #ifdef WIN32 -/*from windmain.c */ +/*from windsys.c */ extern char *translate_path_variables(const char *, char *); +extern boolean get_user_home_folder(char *, size_t); #endif #endif @@ -116,12 +147,14 @@ extern char *translate_path_variables(const char *, char *); #define PRAGMA_UNUSED #endif -#ifdef USER_SOUNDS -extern char *sounddir; /* defined in sounds.c */ -#endif - +#ifndef SFCTOOL staticfn NHFILE *new_nhfile(void); staticfn void free_nhfile(NHFILE *); +#else +NHFILE *new_nhfile(void); +void free_nhfile(NHFILE *); +#endif /* SFCTOOL */ + #ifdef SELECTSAVED staticfn int QSORTCALLBACK strcmp_wrap(const void *, const void *); #endif @@ -136,105 +169,30 @@ staticfn void docompress_file(const char *, boolean); #if defined(ZLIB_COMP) staticfn boolean make_compressed_name(const char *, char *); #endif + +staticfn NHFILE *problematic_savefile(int, const char *); +#ifndef SFCTOOL +staticfn int doconvert_file(const char *, int, boolean); +#endif /* SFCTOOL */ +staticfn boolean make_converted_name(const char *); + +#ifndef SFCTOOL +staticfn NHFILE *viable_nhfile(NHFILE *); +#ifdef SELECTSAVED +staticfn int QSORTCALLBACK strcmp_wrap(const void *, const void *); +#endif +staticfn char *set_bonesfile_name(char *, d_level *); +staticfn char *set_bonestemp_name(void); #ifndef USE_FCNTL staticfn char *make_lockname(const char *, char *); #endif -staticfn void set_configfile_name(const char *); -staticfn FILE *fopen_config_file(const char *, int); -staticfn int get_uchars(char *, uchar *, boolean, int, const char *); -#ifdef NOCWD_ASSUMPTIONS -staticfn void adjust_prefix(char *, int); -#endif -staticfn char *choose_random_part(char *, char); -staticfn boolean config_error_nextline(const char *); -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 boolean cnf_line_OPTIONS(char *); -staticfn boolean cnf_line_AUTOPICKUP_EXCEPTION(char *); -staticfn boolean cnf_line_BINDINGS(char *); -staticfn boolean cnf_line_AUTOCOMPLETE(char *); -staticfn boolean cnf_line_MSGTYPE(char *); -staticfn boolean cnf_line_HACKDIR(char *); -staticfn boolean cnf_line_LEVELDIR(char *); -staticfn boolean cnf_line_SAVEDIR(char *); -staticfn boolean cnf_line_BONESDIR(char *); -staticfn boolean cnf_line_DATADIR(char *); -staticfn boolean cnf_line_SCOREDIR(char *); -staticfn boolean cnf_line_LOCKDIR(char *); -staticfn boolean cnf_line_CONFIGDIR(char *); -staticfn boolean cnf_line_TROUBLEDIR(char *); -staticfn boolean cnf_line_NAME(char *); -staticfn boolean cnf_line_ROLE(char *); -staticfn boolean cnf_line_dogname(char *); -staticfn boolean cnf_line_catname(char *); -#ifdef SYSCF -staticfn boolean cnf_line_WIZARDS(char *); -staticfn boolean cnf_line_SHELLERS(char *); -staticfn boolean cnf_line_EXPLORERS(char *); -staticfn boolean cnf_line_DEBUGFILES(char *); -staticfn boolean cnf_line_DUMPLOGFILE(char *); -staticfn boolean cnf_line_GENERICUSERS(char *); -staticfn boolean cnf_line_BONES_POOLS(char *); -staticfn boolean cnf_line_SUPPORT(char *); -staticfn boolean cnf_line_RECOVER(char *); -staticfn boolean cnf_line_CHECK_SAVE_UID(char *); -staticfn boolean cnf_line_CHECK_PLNAME(char *); -staticfn boolean cnf_line_SEDUCE(char *); -staticfn boolean cnf_line_HIDEUSAGE(char *); -staticfn boolean cnf_line_MAXPLAYERS(char *); -staticfn boolean cnf_line_PERSMAX(char *); -staticfn boolean cnf_line_PERS_IS_UID(char *); -staticfn boolean cnf_line_ENTRYMAX(char *); -staticfn boolean cnf_line_POINTSMIN(char *); -staticfn boolean cnf_line_MAX_STATUENAME_RANK(char *); -staticfn boolean cnf_line_LIVELOG(char *); -staticfn boolean cnf_line_PANICTRACE_LIBC(char *); -staticfn boolean cnf_line_PANICTRACE_GDB(char *); -staticfn boolean cnf_line_GDBPATH(char *); -staticfn boolean cnf_line_GREPPATH(char *); -staticfn boolean cnf_line_CRASHREPORTURL(char *); -staticfn boolean cnf_line_SAVEFORMAT(char *); -staticfn boolean cnf_line_BONESFORMAT(char *); -staticfn boolean cnf_line_ACCESSIBILITY(char *); -staticfn boolean cnf_line_PORTABLE_DEVICE_PATHS(char *); -staticfn void parseformat(int *, char *); -#endif /* SYSCF */ -staticfn boolean cnf_line_BOULDER(char *); -staticfn boolean cnf_line_MENUCOLOR(char *); -staticfn boolean cnf_line_HILITE_STATUS(char *); -staticfn boolean cnf_line_WARNINGS(char *); -staticfn boolean cnf_line_ROGUESYMBOLS(char *); -staticfn boolean cnf_line_SYMBOLS(char *); -staticfn boolean cnf_line_WIZKIT(char *); -#ifdef USER_SOUNDS -staticfn boolean cnf_line_SOUNDDIR(char *); -staticfn boolean cnf_line_SOUND(char *); -#endif -staticfn boolean cnf_line_QT_TILEWIDTH(char *); -staticfn boolean cnf_line_QT_TILEHEIGHT(char *); -staticfn boolean cnf_line_QT_FONTSIZE(char *); -staticfn boolean cnf_line_QT_COMPACT(char *); -struct _cnf_parser_state; /* defined below (far below...) */ -staticfn void cnf_parser_init(struct _cnf_parser_state *parser); -staticfn void cnf_parser_done(struct _cnf_parser_state *parser); -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)); -staticfn boolean parse_conf_file(FILE *fp, boolean (*proc)(char *arg)); staticfn FILE *fopen_wizkit_file(void); staticfn void wizkit_addinv(struct obj *); boolean proc_wizkit_line(char *buf); void read_wizkit(void); /* in extern.h; why here too? */ staticfn FILE *fopen_sym_file(void); - -#ifdef SELF_RECOVER -staticfn boolean copy_bytes(int, int); -#endif staticfn NHFILE *viable_nhfile(NHFILE *); +#endif /* !SFCTOOL */ /* return a file's name without its path and optionally trailing 'type' */ const char * @@ -353,7 +311,6 @@ fname_decode(char quotechar, char *s, char *callerbuf, int bufsz) sp = s; op = callerbuf; *op = '\0'; - calc = 0; while (*sp) { /* Do we have room for one more character? */ @@ -431,6 +388,7 @@ fqname(const char *basenam, #endif /* !PREFIXES_IN_USE */ } +#ifndef SFCTOOL /* reasonbuf must be at least BUFSZ, supplied by caller */ int validate_prefix_locations(char *reasonbuf) @@ -491,6 +449,7 @@ fopen_datafile(const char *filename, const char *mode, int prefix) fp = fopen(filename, mode); return fp; } +#endif /* !SFCTOOL */ /* ---------- EXTERNAL FILE SUPPORT ----------- */ @@ -499,36 +458,58 @@ static const int bei = 1; #define IS_BIGENDIAN() ( (*(char*)&bei) == 0 ) void -zero_nhfile(NHFILE *nhfp) +init_nhfile(NHFILE *nhfp) { + if (nhfp->structlevel) { + if (nhfp->fd != -1) { + impossible("Warning - Unclosed structlevel file being reinitialized"); + (void) nhclose(nhfp->fd); + } + } else if (nhfp->fpdef) { + if (nhfp->fpdef) { + impossible("Warning - Unclosed fieldlevel file being reinitialized"); + (void) fclose(nhfp->fpdef); + } + } nhfp->fd = -1; + nhfp->fpdef = (FILE *) 0; + nhfp->mode = COUNTING; - nhfp->structlevel = FALSE; + nhfp->structlevel = TRUE; nhfp->fieldlevel = FALSE; nhfp->addinfo = FALSE; nhfp->bendian = IS_BIGENDIAN(); - nhfp->fpdef = (FILE *) 0; nhfp->fplog = (FILE *) 0; nhfp->fpdebug = (FILE *) 0; - nhfp->count = 0; + nhfp->rcount = nhfp->wcount = 0; nhfp->eof = FALSE; nhfp->fnidx = 0; + nhfp->style.deflt = FALSE; + nhfp->style.binary = TRUE; + nhfp->nhfpconvert = 0; } -staticfn NHFILE * +#ifndef SFCTOOL +staticfn +#endif +NHFILE * new_nhfile(void) { - NHFILE *nhfp = (NHFILE *) alloc(sizeof(NHFILE)); + NHFILE *nhfp = (NHFILE *) alloc(sizeof *nhfp); - zero_nhfile(nhfp); + memset((genericptr_t) nhfp, 0, sizeof *nhfp); + init_nhfile(nhfp); return nhfp; } -staticfn void +#ifndef SFCTOOL +staticfn +#endif +void free_nhfile(NHFILE *nhfp) { if (nhfp) { - zero_nhfile(nhfp); + init_nhfile(nhfp); free(nhfp); } } @@ -538,7 +519,14 @@ close_nhfile(NHFILE *nhfp) { if (nhfp->structlevel && nhfp->fd != -1) (void) nhclose(nhfp->fd), nhfp->fd = -1; - zero_nhfile(nhfp); + else if (nhfp->fpdef) + (void) fclose(nhfp->fpdef), nhfp->fpdef = (FILE *) 0; + if (nhfp->fplog) + (void) fprintf(nhfp->fplog, "# closing\n"); + if (nhfp->fplog) + (void) fclose(nhfp->fplog); + if (nhfp->fpdebug) + (void) fclose(nhfp->fpdebug); free_nhfile(nhfp); } @@ -551,9 +539,12 @@ rewind_nhfile(NHFILE *nhfp) #else (void) lseek(nhfp->fd, (off_t) 0, 0); #endif + } else { + rewind(nhfp->fpdef); } } +#ifndef SFCTOOL staticfn NHFILE * viable_nhfile(NHFILE *nhfp) { @@ -561,20 +552,50 @@ viable_nhfile(NHFILE *nhfp) the pointer to the nethack file descriptor */ if (nhfp) { /* check for no open file at all, - * not a structlevel legacy file + * not a structlevel legacy file, + * nor a fieldlevel file. */ - if (nhfp->structlevel && nhfp->fd < 0) { + if (((nhfp->fd == -1) && !nhfp->fpdef) + || (nhfp->structlevel && nhfp->fd < 0) + || (nhfp->fieldlevel && !nhfp->fpdef)) { /* not viable, start the cleanup */ - zero_nhfile(nhfp); + if (nhfp->fieldlevel) { + if (nhfp->fpdef) { + (void) fclose(nhfp->fpdef); + nhfp->fpdef = (FILE *) 0; + } + if (nhfp->fplog) { + (void) fprintf(nhfp->fplog, "# closing, not viable\n"); + (void) fclose(nhfp->fplog); + } + if (nhfp->fpdebug) + (void) fclose(nhfp->fpdebug); + } free_nhfile(nhfp); nhfp = (NHFILE *) 0; } } return nhfp; } +#endif /* !SFCTOOL */ + +int +nhclose(int fd) +{ + int retval = 0; + + if (fd >= 0) { + if (close_check(fd)) + bclose(fd); + else + retval = close(fd); + } + return retval; +} /* ---------- BEGIN LEVEL FILE HANDLING ----------- */ +#ifndef SFCTOOL /* Construct a file name for a level-type file, which is of the form * something.level (with any old level stripped off). * This assumes there is space on the end of 'file' to append @@ -616,6 +637,7 @@ create_levelfile(int lev, char errbuf[]) nhfp->addinfo = FALSE; nhfp->style.deflt = FALSE; nhfp->style.binary = TRUE; + nhfp->fnidx = historical; nhfp->fd = -1; nhfp->fpdef = (FILE *) 0; #if defined(MICRO) || defined(WIN32) @@ -638,6 +660,9 @@ create_levelfile(int lev, char errbuf[]) Sprintf(errbuf, "Cannot create file \"%s\" for level %d (errno %d).", gl.lock, lev, errno); +#if defined(MSDOS) + setmode(nhfp->fd, O_BINARY); +#endif } nhfp = viable_nhfile(nhfp); return nhfp; @@ -662,6 +687,7 @@ open_levelfile(int lev, char errbuf[]) nhfp->style.deflt = FALSE; nhfp->style.binary = TRUE; nhfp->ftype = NHF_LEVELFILE; + nhfp->fnidx = historical; nhfp->fd = -1; nhfp->fpdef = (FILE *) 0; } @@ -679,6 +705,9 @@ open_levelfile(int lev, char errbuf[]) Sprintf(errbuf, "Cannot open file \"%s\" for level %d (errno %d).", gl.lock, lev, errno); +#if defined(MSDOS) + setmode(nhfp->fd, O_BINARY); +#endif } nhfp = viable_nhfile(nhfp); return nhfp; @@ -723,28 +752,10 @@ clearlocks(void) staticfn int QSORTCALLBACK strcmp_wrap(const void *p, const void *q) { -#if defined(UNIX) && defined(QT_GRAPHICS) - return strncasecmp(*(char **) p, *(char **) q, 16); -#else - return strncmpi(*(char **) p, *(char **) q, 16); -#endif + return strcmp(*(char **) p, *(char **) q); } #endif -int -nhclose(int fd) -{ - int retval = 0; - - if (fd >= 0) { - if (close_check(fd)) - bclose(fd); - else - retval = close(fd); - } - return retval; -} - /* ---------- END LEVEL FILE HANDLING ----------- */ /* ---------- BEGIN BONES FILE HANDLING ----------- */ @@ -831,10 +842,23 @@ create_bonesfile(d_level *lev, char **bonesid, char errbuf[]) nhfp = new_nhfile(); if (nhfp) { - nhfp->structlevel = TRUE; - nhfp->fieldlevel = FALSE; nhfp->ftype = NHF_BONESFILE; nhfp->mode = WRITING; + nhfp->structlevel = TRUE; + nhfp->fieldlevel = FALSE; + nhfp->addinfo = TRUE; + nhfp->style.deflt = TRUE; + nhfp->style.binary = TRUE; + nhfp->fnidx = historical; + nhfp->fd = -1; + nhfp->fpdef = fopen(file, nhfp->style.binary ? WRBMODE : WRTMODE); + if (nhfp->fpdef) { +#ifdef SAVEFILE_DEBUGGING + nhfp->fpdebug = fopen("create_bonesfile-debug.log", "a"); +#endif + } else { + failed = errno; + } if (nhfp->structlevel) { #if defined(MICRO) || defined(WIN32) /* Use O_TRUNC to force the file to be shortened if it already @@ -842,15 +866,19 @@ create_bonesfile(d_level *lev, char **bonesid, char errbuf[]) */ nhfp->fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, FCMASK); -#else +#else /* ?MICRO || WIN32 */ +/* implies UNIX or MAC (MAC is for OS9 or earlier) */ #ifdef MAC nhfp->fd = maccreat(file, BONE_TYPE); #else nhfp->fd = creat(file, FCMASK); -#endif -#endif +#endif /* ?MAC */ +#endif /* ?MICRO || WIN32 */ if (nhfp->fd < 0) failed = errno; +#if defined(MSDOS) + setmode(nhfp->fd, O_BINARY); +#endif } if (failed && errbuf) /* failure explanation */ Sprintf(errbuf, "Cannot create bones \"%s\", id %s (errno %d).", @@ -914,11 +942,25 @@ open_bonesfile(d_level *lev, char **bonesid) nhfp->fieldlevel = FALSE; nhfp->ftype = NHF_BONESFILE; nhfp->mode = READING; + nhfp->addinfo = TRUE; + nhfp->style.deflt = TRUE; + nhfp->style.binary = (sysopt.bonesformat[0] != exportascii); + nhfp->fnidx = sysopt.bonesformat[0]; + nhfp->fd = -1; + nhfp->fpdef = fopen(fq_bones, nhfp->style.binary ? RDBMODE : RDTMODE); + if (nhfp->fpdef) { +#ifdef SAVEFILE_DEBUGGING + nhfp->fpdebug = fopen("open_bonesfile-debug.log", "a"); +#endif + } if (nhfp->structlevel) { #ifdef MAC nhfp->fd = macopen(fq_bones, O_RDONLY | O_BINARY, BONE_TYPE); #else nhfp->fd = open(fq_bones, O_RDONLY | O_BINARY, 0); +#endif +#if defined(MSDOS) + setmode(nhfp->fd, O_BINARY); #endif } } @@ -929,8 +971,12 @@ open_bonesfile(d_level *lev, char **bonesid) int delete_bonesfile(d_level *lev) { + int reslt; + (void) set_bonesfile_name(gb.bones, lev); - return !(unlink(fqname(gb.bones, BONESPREFIX, 0)) < 0); + reslt = unlink(fqname(gb.bones, BONESPREFIX, 0)); + delete_convertedfile(fqname(gb.bones, BONESPREFIX, 0)); + return !(reslt < 0); } /* assume we're compressing the recently read or created bonesfile, so the @@ -938,8 +984,10 @@ delete_bonesfile(d_level *lev) void compress_bonesfile(void) { + nh_sfconvert(fqname(gb.bones, BONESPREFIX, 0)); nh_compress(fqname(gb.bones, BONESPREFIX, 0)); } +#endif /* !SFCTOOL */ /* ---------- END BONES FILE HANDLING ----------- */ @@ -993,7 +1041,7 @@ set_savefile_name(boolean regularize_it) if (strlen(gs.SAVEF) < (SAVESIZE - 1)) (void) strncat(gs.SAVEF, svp.plname, (SAVESIZE - strlen(gs.SAVEF))); #endif -#if defined(MICRO) && !defined(VMS) && !defined(WIN32) && !defined(MSDOS) +#if defined(MICRO) && !defined(WIN32) && !defined(MSDOS) if (strlen(gs.SAVEP) < (SAVESIZE - 1)) Strcpy(gs.SAVEF, gs.SAVEP); else @@ -1029,9 +1077,6 @@ set_savefile_name(boolean regularize_it) if (strlen(SAVE_EXTENSION) > (0) && !overflow) { if (strlen(gs.SAVEF) + strlen(SAVE_EXTENSION) < (SAVESIZE - 1)) { Strcat(gs.SAVEF, SAVE_EXTENSION); -#ifdef MSDOS - sfindicator = ""; -#endif } else overflow = 3; } @@ -1056,12 +1101,12 @@ set_savefile_name(boolean regularize_it) #endif } +#ifndef SFCTOOL #ifdef INSURANCE void save_savefile_name(NHFILE *nhfp) { - if (nhfp->structlevel) - (void) write(nhfp->fd, (genericptr_t) gs.SAVEF, sizeof(gs.SAVEF)); + Sfo_char(nhfp, gs.SAVEF, "savefile_name", sizeof(gs.SAVEF)); } #endif @@ -1099,12 +1144,10 @@ create_savefile(void) fq_save = fqname(gs.SAVEF, SAVEPREFIX, 0); nhfp = new_nhfile(); if (nhfp) { - nhfp->structlevel = TRUE; - nhfp->fieldlevel = FALSE; nhfp->ftype = NHF_SAVEFILE; nhfp->mode = WRITING; if (program_state.in_self_recover || do_historical) { - do_historical = TRUE; /* force it */ + nhUse(do_historical); nhfp->structlevel = TRUE; nhfp->fieldlevel = FALSE; nhfp->addinfo = FALSE; @@ -1113,19 +1156,25 @@ create_savefile(void) nhfp->fnidx = historical; nhfp->fd = -1; nhfp->fpdef = (FILE *) 0; - } - if (nhfp->structlevel) { +#ifdef SAVEFILE_DEBUGGING + nhfp->fplog = fopen("create-savefile.log", "w"); +#endif #if defined(MICRO) || defined(WIN32) nhfp->fd = open(fq_save, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK); -#else +#else /* !MICRO && !WIN32 */ +/* UNIX || MAC implied (MAC is OS9 or earlier only) */ #ifdef MAC nhfp->fd = maccreat(fq_save, SAVE_TYPE); #else nhfp->fd = creat(fq_save, FCMASK); #endif #endif /* MICRO || WIN32 */ - } +#if defined(MSDOS) || defined(WIN32) + if (nhfp->fd >= 0) + (void) setmode(nhfp->fd, O_BINARY); +#endif + } } #if defined(VMS) && !defined(SECURE) /* @@ -1153,12 +1202,11 @@ open_savefile(void) fq_save = fqname(gs.SAVEF, SAVEPREFIX, 0); nhfp = new_nhfile(); if (nhfp) { - nhfp->structlevel = TRUE; - nhfp->fieldlevel = FALSE; nhfp->ftype = NHF_SAVEFILE; nhfp->mode = READING; if (program_state.in_self_recover || do_historical) { do_historical = TRUE; /* force it */ + nhUse(do_historical); nhfp->structlevel = TRUE; nhfp->fieldlevel = FALSE; nhfp->addinfo = FALSE; @@ -1167,14 +1215,19 @@ open_savefile(void) nhfp->fnidx = historical; nhfp->fd = -1; nhfp->fpdef = (FILE *) 0; - } - if (nhfp->structlevel) { -#ifdef MAC - nhfp->fd = macopen(fq_save, O_RDONLY | O_BINARY, SAVE_TYPE); -#else - nhfp->fd = open(fq_save, O_RDONLY | O_BINARY, 0); +#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 + nhfp->fd = open(fq_save, O_RDONLY | O_BINARY, 0); +#endif +#if defined(MSDOS) || defined(WIN32) + if (nhfp->fd >= 0) + (void) setmode(nhfp->fd, O_BINARY); #endif - } } nhfp = viable_nhfile(nhfp); return nhfp; @@ -1184,7 +1237,10 @@ open_savefile(void) int delete_savefile(void) { - (void) unlink(fqname(gs.SAVEF, SAVEPREFIX, 0)); + const char *sfname = fqname(gs.SAVEF, SAVEPREFIX, 0); + + (void) unlink(sfname); + (void) delete_convertedfile(sfname); return 0; /* for restore_saved_game() (ex-xxxmain.c) test */ } @@ -1194,21 +1250,42 @@ restore_saved_game(void) { const char *fq_save; NHFILE *nhfp = (NHFILE *) 0; + int sfstatus = 0; set_savefile_name(TRUE); fq_save = fqname(gs.SAVEF, SAVEPREFIX, 0); nh_uncompress(fq_save); if ((nhfp = open_savefile()) != 0) { - if (validate(nhfp, fq_save, FALSE) != 0) { + if ((sfstatus = validate(nhfp, fq_save, FALSE)) != SF_UPTODATE) { close_nhfile(nhfp); - nhfp = (NHFILE *) 0; - (void) delete_savefile(); + nhfp = problematic_savefile(sfstatus, fq_save); } } return nhfp; } +/* + * This doesn't open any files. It provides a valid (NHFILE *) + * to provide to functions that take one as a parameter, with + * only the FREEING bit set. + * + * close_nhfile() can, and should, be called on the returned + * (NHFILE *), and it will handle it correctly. + */ + +NHFILE * +get_freeing_nhfile(void) +{ + NHFILE *nhfp = (NHFILE *) 0; + + nhfp = new_nhfile(); /* also sets fd to -1 */ + if (nhfp) { + nhfp->mode = FREEING; + } + return nhfp; +} + /* called if there is no save file for current character */ int check_panic_save(void) @@ -1256,10 +1333,14 @@ check_panic_save(void) #if defined(SELECTSAVED) char * -plname_from_file(const char *filename, boolean without_wait_synch_per_file) +plname_from_file( + const char *filename, + boolean without_wait_synch_per_file) { - NHFILE *nhfp = (NHFILE *) 0; + NHFILE *nhfp; + unsigned ln; char *result = 0; + int sfstatus = 0; Strcpy(gs.SAVEF, filename); #ifdef COMPRESS_EXTENSION @@ -1274,68 +1355,43 @@ plname_from_file(const char *filename, boolean without_wait_synch_per_file) #endif nh_uncompress(gs.SAVEF); if ((nhfp = open_savefile()) != 0) { - if (validate(nhfp, filename, without_wait_synch_per_file) == 0) { - char tplname[PL_NSIZ]; - - get_plname_from_file(nhfp, tplname); - result = dupstr(tplname); + if ((sfstatus = validate(nhfp, filename, + without_wait_synch_per_file)) == SF_UPTODATE) { + /* room for "name+role+race+gend+algn X" where the space before + X is actually NUL and X is playmode: one of '-', 'X', or 'D' */ + ln = (unsigned) PL_NSIZ_PLUS; + result = memset((genericptr_t) alloc(ln), '\0', ln); + get_plname_from_file(nhfp, result, FALSE); } close_nhfile(nhfp); } nh_compress(gs.SAVEF); - - return result; -#if 0 -/* --------- obsolete - used to be ifndef STORE_PLNAME_IN_FILE ----*/ -#if defined(UNIX) && defined(QT_GRAPHICS) - /* Name not stored in save file, so we have to extract it from - the filename, which loses information - (eg. "/", "_", and "." characters are lost. */ - int k; - int uid; - char name[64]; /* more than PL_NSIZ */ -#ifdef COMPRESS_EXTENSION -#define EXTSTR COMPRESS_EXTENSION -#else -#define EXTSTR "" -#endif - - if (sscanf(filename, "%*[^/]/%d%63[^.]" EXTSTR, &uid, name) == 2) { -#undef EXTSTR - /* "_" most likely means " ", which certainly looks nicer */ - for (k = 0; name[k]; k++) - if (name[k] == '_') - name[k] = ' '; - return dupstr(name); - } else -#endif /* UNIX && QT_GRAPHICS */ - { - return 0; - } -/* --------- end of obsolete code ----*/ -#endif /* 0 - WAS STORE_PLNAME_IN_FILE*/ + return result; /* file's plname[]+playmode value */ } #endif /* defined(SELECTSAVED) */ #define SUPPRESS_WAITSYNCH_PERFILE TRUE #define ALLOW_WAITSYNCH_PERFILE FALSE +/* get list of saved games owned by current user */ char ** get_saved_games(void) { + char **result = NULL; #if defined(SELECTSAVED) #if defined(WIN32) || defined(UNIX) int n; #endif int j = 0; - char **result = 0; #ifdef WIN32 { char *foundfile; const char *fq_save; +#if 0 const char *fq_new_save; const char *fq_old_save; +#endif char **files = 0; int i, count_failures = 0; @@ -1355,8 +1411,8 @@ get_saved_games(void) } if (n > 0) { - files = (char **) alloc((n + 1) * sizeof(char *)); /* at most */ - (void) memset((genericptr_t) files, 0, (n + 1) * sizeof(char *)); + files = (char **) alloc((n + 1) * sizeof (char *)); /* at most */ + (void) memset((genericptr_t) files, 0, (n + 1) * sizeof (char *)); if (findfirst((char *) fq_save)) { i = 0; do { @@ -1366,13 +1422,19 @@ get_saved_games(void) } if (n > 0) { - result = (char **) alloc((n + 1) * sizeof(char *)); /* at most */ - (void) memset((genericptr_t) result, 0, (n + 1) * sizeof(char *)); + result = (char **) alloc((n + 1) * sizeof (char *)); /* at most */ + (void) memset((genericptr_t) result, 0, (n + 1) * sizeof (char *)); for(i = 0; i < n; i++) { char *r; r = plname_from_file(files[i], SUPPRESS_WAITSYNCH_PERFILE); if (r) { + /* this renaming of the savefile is not compatible + * with 1f36b98b, 'selectsaved' extension from + * Oct 10, 2024. Disable the renaming for the time + * being. + */ +#if 0 /* rename file if it is not named as expected */ Strcpy(svp.plname, r); set_savefile_name(TRUE); @@ -1382,7 +1444,7 @@ get_saved_games(void) if (strcmp(fq_old_save, fq_new_save) != 0 && !file_exists(fq_new_save)) (void) rename(fq_old_save, fq_new_save); - +#endif result[j++] = r; } else { count_failures++; @@ -1394,7 +1456,7 @@ get_saved_games(void) if (count_failures) wait_synch(); } -#endif +#endif /* WIN32 */ #ifdef UNIX /* posixly correct version */ int myuid = getuid(); @@ -1413,7 +1475,7 @@ get_saved_games(void) (void) memset((genericptr_t) result, 0, (n + 1) * sizeof(char *)); for (i = 0, j = 0; i < n; i++) { int uid; - char name[64]; /* more than PL_NSIZ */ + char name[64]; /* more than PL_NSIZ+1 */ struct dirent *entry = readdir(dir); if (!entry) @@ -1434,7 +1496,7 @@ get_saved_games(void) closedir(dir); } } -#endif +#endif /* UNIX */ #ifdef VMS Strcpy(svp.plname, "*"); set_savefile_name(FALSE); @@ -1444,14 +1506,14 @@ get_saved_games(void) if (j > 0) { if (j > 1) qsort(result, j, sizeof (char *), strcmp_wrap); - result[j] = 0; - return result; + result[j] = (char *) NULL; } else if (result) { /* could happen if save files are obsolete */ free_saved_games(result); + result = (char **) NULL; } #endif /* SELECTSAVED */ - return 0; + return result; } #undef SUPPRESS_WAITSYNCH_PERFILE #undef ALLOW_WAITSYNCH_PERFILE @@ -1460,13 +1522,14 @@ void free_saved_games(char **saved) { if (saved) { - int i = 0; + int i; - while (saved[i]) - free((genericptr_t) saved[i++]); + for (i = 0; saved[i]; ++i) + free((genericptr_t) saved[i]); free((genericptr_t) saved); } } +#endif /* !SFCTOOL */ /* ---------- END SAVE FILE HANDLING ----------- */ @@ -1524,8 +1587,14 @@ docompress_file(const char *filename, boolean uncomp) #else xtra = ""; #endif +#ifdef SFCTOOL + ln = strlen(filename) + sizeof COMPRESS_EXTENSION; + cfn = (char *) alloc(ln); +#else /* SFCTOOL */ ln = (unsigned) (strlen(filename) + strlen(xtra)); cfn = (char *) alloc(ln + 1); +#endif /* SFCTOOL */ + Strcpy(cfn, filename); Strcat(cfn, xtra); @@ -1569,8 +1638,9 @@ docompress_file(const char *filename, boolean uncomp) * there is an error message from the compression, the 'y' or 'n' can * end up being displayed after the error message. */ - if (istty) + if (istty) { mark_synch(); + } #endif f = fork(); if (f == 0) { /* child */ @@ -1580,8 +1650,9 @@ docompress_file(const char *filename, boolean uncomp) * them will have to clear the first line. This should be * invisible if there are no error messages. */ - if (istty) + if (istty) { raw_print(""); + } #endif /* run compressor without privileges, in case other programs * have surprises along the line of gzip once taking filenames @@ -1744,10 +1815,17 @@ docompress_file(const char *filename, boolean uncomp) { gzFile compressedfile; FILE *uncompressedfile; +#ifndef SFCTOOL char cfn[256]; +#else + char *cfn; +#endif char buf[1024]; unsigned len, len2; +#ifdef SFCTOOL + cfn = (char *) alloc(strlen(filename) + strlen(COMPRESS_EXTENSION) + 1); +#endif if (!make_compressed_name(filename, cfn)) return; @@ -1768,6 +1846,9 @@ docompress_file(const char *filename, boolean uncomp) } else { panic("Error in docompress_file %d", errno); } +#ifdef SFCTOOL + free(cfn); +#endif fclose(uncompressedfile); return; } @@ -1782,6 +1863,9 @@ docompress_file(const char *filename, boolean uncomp) fclose(uncompressedfile); gzclose(compressedfile); (void) unlink(cfn); +#ifdef SFCTOOL + free(cfn); +#endif return; } if (len == 0) @@ -1794,6 +1878,9 @@ docompress_file(const char *filename, boolean uncomp) fclose(uncompressedfile); gzclose(compressedfile); (void) unlink(cfn); +#ifdef SFCTOOL + free(cfn); +#endif return; } } @@ -1819,12 +1906,18 @@ docompress_file(const char *filename, boolean uncomp) panic("Error in zlib docompress_file %s, %d", filename, errno); } +#ifdef SFCTOOL + free(cfn); +#endif return; } uncompressedfile = fopen(filename, WRBMODE); if (!uncompressedfile) { pline("Error in zlib docompress file uncompress %s", filename); gzclose(compressedfile); +#ifdef SFCTOOL + free(cfn); +#endif return; } @@ -1838,6 +1931,9 @@ docompress_file(const char *filename, boolean uncomp) fclose(uncompressedfile); gzclose(compressedfile); (void) unlink(filename); +#ifdef SFCTOOL + free(cfn); +#endif return; } if (len == 0) @@ -1850,6 +1946,9 @@ docompress_file(const char *filename, boolean uncomp) fclose(uncompressedfile); gzclose(compressedfile); (void) unlink(filename); +#ifdef SFCTOOL + free(cfn); +#endif return; } } @@ -1860,6 +1959,9 @@ docompress_file(const char *filename, boolean uncomp) /* Delete the file left behind */ (void) unlink(cfn); } +#ifdef SFCTOOL + free(cfn); +#endif } #endif /* RLC 09 Mar 1999: End ZLIB patch */ @@ -1867,8 +1969,209 @@ docompress_file(const char *filename, boolean uncomp) /* ---------- END FILE COMPRESSION HANDLING ----------- */ + +/* ---------- BEGIN PROBLEMATIC SAVEFILE HANDLING ----------- */ +#ifndef SFCTOOL +static struct sfstatus_to_msg { + int sfstatus; + const char *msg; +} sf2msg[] = { + { SF_UPTODATE, "everything matches" }, + { SF_OUTDATED, "outdated savefile" }, + { SF_CRITICAL_BYTE_COUNT_MISMATCH, + "savefile critical byte-count mismatch" }, + { SF_DM_IL32LLP64_ON_ILP32LL64, "Windows x64 savefile on x86" }, + { SF_DM_I32LP64_ON_ILP32LL64, "Unix 64 savefile on x86" }, + { SF_DM_ILP32LL64_ON_I32LP64, "x86 savefile on Unix 64" }, + { SF_DM_ILP32LL64_ON_IL32LLP64, "x86 savefile on Windows x64" }, + { SF_DM_I32LP64_ON_IL32LLP64, "Unix 64 savefile on Windows x64" }, + { SF_DM_IL32LLP64_ON_I32LP64, "Windows x64 savefile on Unix 64" }, + { SF_DM_MISMATCH, "generic savefile mismatch" }, +}; + +staticfn NHFILE * +problematic_savefile(int sfstatus, const char *savefilenm) +{ + int i; + NHFILE *nhfp = (NHFILE *) 0; + + switch (sfstatus) { + case SF_UPTODATE: + break; + case SF_DM_IL32LLP64_ON_ILP32LL64: + case SF_DM_I32LP64_ON_ILP32LL64: + case SF_DM_ILP32LL64_ON_I32LP64: + case SF_DM_ILP32LL64_ON_IL32LLP64: + case SF_DM_I32LP64_ON_IL32LLP64: + case SF_DM_IL32LLP64_ON_I32LP64: + FALLTHROUGH; + /*FALLTHRU*/ + case SF_DM_MISMATCH: + case SF_OUTDATED: + case SF_CRITICAL_BYTE_COUNT_MISMATCH: + default: + for (i = 0; i < SIZE(sf2msg); ++i) { + if (sf2msg[i].sfstatus == sfstatus) { + raw_printf("\n%s is %s %s\n", + savefilenm, + (sfstatus == SF_OUTDATED) ? "an" : "a", + sf2msg[i].msg); + break; + } + } + } + return nhfp; +} +#endif /* !SFCTOOL */ + +/* ---------- END PROBLEMATIC SAVEFILE HANDLING ----------- */ + +/* ---------- BEGIN EXTERNAL CONVERSION HANDLING ----------- */ + +static boolean cvtinit = FALSE; + +#ifndef SFCTOOL +static char *unconverted_filename = 0, *converted_filename = 0; + +/* + * Returns non-zero if unconvert was successful + */ +staticfn int +doconvert_file(const char *filename, int sfstatus, boolean unconvert) +{ + nhUse(filename); + nhUse(sfstatus); + nhUse(unconvert); + return 1; +} + +/* convert file */ +void nh_sfconvert(const char *filename) +{ + (void) doconvert_file(filename, 0, FALSE); +} + +/* unconvert file if it exists */ +void nh_sfunconvert(const char *filename) +{ + (void) doconvert_file(filename, 0, TRUE); +} + +#else /* !SFCTOOL */ +/* in sfctool, these are in sfctool.c, not in here */ +extern char *unconverted_filename, *converted_filename; +#endif /* !SFCTOOL */ + +staticfn boolean +make_converted_name(const char *filename) +{ + unsigned ln; + const char *xtra, *finaldirchar; + const char *dir = NULL; + boolean needsep = FALSE; +#if defined(WIN32) + static char folderbuf[MAX_PATH]; +#endif + + if (!filename) + return FALSE; + + if (unconverted_filename) + free((genericptr_t) unconverted_filename), unconverted_filename = 0; + if (converted_filename) + free((genericptr_t) converted_filename), converted_filename = 0; + +#ifndef SHORT_FILENAMES + /* do we need to do some ms-dos processing here? */ +#endif /* SHORT_FILENAMES */ + + ln = (unsigned) strlen(filename); + if (!contains_directory(filename)) { +#if defined(UNIX) + dir = nh_getenv("NETHACKDIR"); + if (!dir) + dir = nh_getenv("HACKDIR"); +#ifdef HACKDIR + if (!dir) + dir = HACKDIR; +#endif +#elif defined(WIN32) + if (get_user_home_folder(folderbuf, sizeof folderbuf)) { + size_t sz = strlen(folderbuf); + + Snprintf(eos(folderbuf), sizeof folderbuf - sz, + "\\AppData\\Local\\NetHack\\3.7\\"); + dir = (const char *) folderbuf; + } +#endif /* UNIX || WIN32 */ + if (dir) { + finaldirchar = c_eos(dir); + finaldirchar--; + if (!(*finaldirchar == '/' || *finaldirchar == '\\' + || *finaldirchar == ':')) { + needsep = TRUE; + ln += 1; + } + ln += strlen(dir); + } + } + unconverted_filename = (char *) alloc(ln + 1); + Snprintf(unconverted_filename, ln + 1, "%s%s%s", + dir ? dir : "", + (dir && needsep) ? "/" : "", + filename); + xtra = ".exportascii"; + ln += (unsigned) strlen(xtra); + converted_filename = (char *) alloc(ln + 1); + Strcpy(converted_filename, unconverted_filename); + Strcat(converted_filename, xtra); + return TRUE; +} + +/* delete converted savefile as a normal course of action */ +int +delete_convertedfile(const char *basefilename) +{ + if (!converted_filename) + make_converted_name(basefilename); + if (converted_filename) { + (void) unlink(converted_filename); + } + return 0; +} + +void free_convert_filenames(void) +{ + if (converted_filename) + free((genericptr_t) converted_filename), converted_filename = 0; + if (unconverted_filename) + free((genericptr_t) unconverted_filename), unconverted_filename = 0; + cvtinit = FALSE; +} + +/* return TRUE if s contains a directory, not just a filespec */ +boolean +contains_directory(const char *s) +{ + int i, slen = strlen(s); + const char *cp = s; + + for (i = 0; i < slen; ++i) { + if (*cp == '\\' || *cp == '/' || *cp == ':') + return TRUE; + cp++; + } + return FALSE; +} + +/* =========================================================================*/ + +/* ---------- END EXTERNAL CONVERSION HANDLING ----------- */ + /* ---------- BEGIN FILE LOCKING HANDLING ----------- */ +#ifndef SFCTOOL + #if defined(NO_FILE_LINKS) || defined(USE_FCNTL) /* implies UNIX */ static int lockfd = -1; /* for lock_file() to pass to unlock_file() */ #endif @@ -2131,1732 +2434,6 @@ unlock_file(const char *filename) /* ---------- END FILE LOCKING HANDLING ----------- */ -/* ---------- BEGIN CONFIG FILE HANDLING ----------- */ - -static const char *default_configfile = -#ifdef UNIX - ".nethackrc"; -#else -#if defined(MAC) || defined(__BEOS__) - "NetHack Defaults"; -#else -#if defined(MSDOS) || defined(WIN32) - CONFIG_FILE; -#else - "NetHack.cnf"; -#endif -#endif -#endif - -/* used for messaging. Also used in options.c */ -char configfile[BUFSZ]; - -#ifdef MSDOS -/* conflict with speed-dial under windows - * for XXX.cnf file so support of NetHack.cnf - * is for backward compatibility only. - * Preferred name (and first tried) is now defaults.nh but - * the game will try the old name if there - * is no defaults.nh. - */ -const char *backward_compat_configfile = "nethack.cnf"; -#endif - -/* #saveoptions - save config options into file */ -int -do_write_config_file(void) -{ - FILE *fp; - char tmp[BUFSZ]; - - if (!configfile[0]) { - pline("Strange, could not figure out config file name."); - return ECMD_OK; - } - if (flags.suppress_alert < FEATURE_NOTICE_VER(3,7,0)) { - pline("Warning: saveoptions is highly experimental!"); - wait_synch(); - pline("Some settings are not saved!"); - wait_synch(); - pline("All manual customization and comments are removed" - " from the file!"); - wait_synch(); - } -#define overwrite_prompt "Overwrite config file %.*s?" - Sprintf(tmp, overwrite_prompt, - (int) (BUFSZ - sizeof overwrite_prompt - 2), configfile); -#undef overwrite_prompt - if (!paranoid_query(TRUE, tmp)) - return ECMD_OK; - - fp = fopen(configfile, "w"); - if (fp) { - size_t len, wrote; - strbuf_t buf; - - strbuf_init(&buf); - all_options_strbuf(&buf); - len = strlen(buf.str); - wrote = fwrite(buf.str, 1, len, fp); - fclose(fp); - strbuf_empty(&buf); - if (wrote != len) - pline("An error occurred, wrote only partial data (%zu/%zu).", - wrote, len); - } - return ECMD_OK; -} - -/* remember the name of the file we're accessing; - if may be used in option reject messages */ -staticfn void -set_configfile_name(const char *fname) -{ - (void) strncpy(configfile, fname, sizeof configfile - 1); - configfile[sizeof configfile - 1] = '\0'; -} - -staticfn FILE * -fopen_config_file(const char *filename, int src) -{ - FILE *fp; -#if defined(UNIX) || defined(VMS) - char tmp_config[BUFSZ]; - char *envp; -#endif - - if (src == set_in_sysconf) { - /* SYSCF_FILE; if we can't open it, caller will bail */ - if (filename && *filename) { - set_configfile_name(fqname(filename, SYSCONFPREFIX, 0)); - fp = fopen(configfile, "r"); - } else - fp = (FILE *) 0; - return fp; - } - /* If src != set_in_sysconf, "filename" is an environment variable, so it - * should hang around. If set, it is expected to be a full path name - * (if relevant) - */ - if (filename && *filename) { - set_configfile_name(filename); -#ifdef UNIX - 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 - * place a file name may be wholly under the player's - * control (but SYSCF_FILE is not under the player's - * control so it's OK). - */ - raw_printf("Access to %s denied (%d).", configfile, errno); - wait_synch(); - /* fall through to standard names */ - } else -#endif - if ((fp = fopen(configfile, "r")) != (FILE *) 0) { - return fp; -#if defined(UNIX) || defined(VMS) - } else { - /* access() above probably caught most problems for UNIX */ - raw_printf("Couldn't open requested config file %s (%d).", - configfile, errno); - wait_synch(); -#endif - } - } - /* fall through to standard names */ - -#if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32) - set_configfile_name(fqname(default_configfile, CONFIGPREFIX, 0)); - if ((fp = fopen(configfile, "r")) != (FILE *) 0) { - return fp; - } else if (strcmp(default_configfile, configfile)) { - set_configfile_name(default_configfile); - if ((fp = fopen(configfile, "r")) != (FILE *) 0) - return fp; - } -#ifdef MSDOS - set_configfile_name(fqname(backward_compat_configfile, CONFIGPREFIX, 0)); - if ((fp = fopen(configfile, "r")) != (FILE *) 0) { - return fp; - } else if (strcmp(backward_compat_configfile, configfile)) { - set_configfile_name(backward_compat_configfile); - if ((fp = fopen(configfile, "r")) != (FILE *) 0) - return fp; - } -#endif -#else -/* constructed full path names don't need fqname() */ -#ifdef VMS - /* no punctuation, so might be a logical name */ - set_configfile_name("nethackini"); - if ((fp = fopen(configfile, "r")) != (FILE *) 0) - return fp; - set_configfile_name("sys$login:nethack.ini"); - if ((fp = fopen(configfile, "r")) != (FILE *) 0) - return fp; - - envp = nh_getenv("HOME"); - if (!envp || !*envp) - Strcpy(tmp_config, "NetHack.cnf"); - else - Sprintf(tmp_config, "%s%s%s", envp, - !strchr(":]>/", envp[strlen(envp) - 1]) ? "/" : "", - "NetHack.cnf"); - set_configfile_name(tmp_config); - if ((fp = fopen(configfile, "r")) != (FILE *) 0) - return fp; -#else /* should be only UNIX left */ - envp = nh_getenv("HOME"); - if (!envp) - Strcpy(tmp_config, ".nethackrc"); - else - Sprintf(tmp_config, "%s/%s", envp, ".nethackrc"); - - set_configfile_name(tmp_config); - if ((fp = fopen(configfile, "r")) != (FILE *) 0) - return fp; -#if defined(__APPLE__) /* UNIX+__APPLE__ => MacOSX */ - /* try an alternative */ - if (envp) { - /* OSX-style configuration settings */ - Sprintf(tmp_config, "%s/%s", envp, - "Library/Preferences/NetHack Defaults"); - set_configfile_name(tmp_config); - if ((fp = fopen(configfile, "r")) != (FILE *) 0) - return fp; - /* may be easier for user to edit if filename has '.txt' suffix */ - Sprintf(tmp_config, "%s/%s", envp, - "Library/Preferences/NetHack Defaults.txt"); - set_configfile_name(tmp_config); - if ((fp = fopen(configfile, "r")) != (FILE *) 0) - return fp; - } -#endif /*__APPLE__*/ - if (errno != ENOENT) { - const char *details; - - /* e.g., problems when setuid NetHack can't search home - directory restricted to user */ -#if defined(NHSTDC) && !defined(NOTSTDC) - if ((details = strerror(errno)) == 0) -#endif - details = ""; - raw_printf("Couldn't open default config file %s %s(%d).", - configfile, details, errno); - wait_synch(); - } -#endif /* !VMS => Unix */ -#endif /* !(MICRO || MAC || __BEOS__ || WIN32) */ - return (FILE *) 0; -} - -/* - * Retrieve a list of integers from buf into a uchar array. - * - * NOTE: zeros are inserted unless modlist is TRUE, in which case the list - * location is unchanged. Callers must handle zeros if modlist is FALSE. - */ -staticfn int -get_uchars(char *bufp, /* current pointer */ - uchar *list, /* return list */ - boolean modlist, /* TRUE: list is being modified in place */ - int size, /* return list size */ - const char *name) /* name of option for error message */ -{ - unsigned int num = 0; - int count = 0; - boolean havenum = FALSE; - - while (1) { - switch (*bufp) { - case ' ': - case '\0': - case '\t': - case '\n': - if (havenum) { - /* if modifying in place, don't insert zeros */ - if (num || !modlist) - list[count] = num; - count++; - num = 0; - havenum = FALSE; - } - if (count == size || !*bufp) - return count; - bufp++; - break; - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - havenum = TRUE; - num = num * 10 + (*bufp - '0'); - bufp++; - break; - - case '\\': - goto gi_error; - break; - - default: - gi_error: - raw_printf("Syntax error in %s", name); - wait_synch(); - return count; - } - } - /*NOTREACHED*/ -} - -#ifdef NOCWD_ASSUMPTIONS -staticfn void -adjust_prefix(char *bufp, int prefixid) -{ - char *ptr; - - if (!bufp) - return; -#ifdef WIN32 - if (fqn_prefix_locked[prefixid]) - return; -#endif - /* Backward compatibility, ignore trailing ;n */ - if ((ptr = strchr(bufp, ';')) != 0) - *ptr = '\0'; - if (strlen(bufp) > 0) { - gf.fqn_prefix[prefixid] = (char *) alloc(strlen(bufp) + 2); - Strcpy(gf.fqn_prefix[prefixid], bufp); - append_slash(gf.fqn_prefix[prefixid]); - } -} -#endif - -/* Choose at random one of the sep separated parts from str. Mangles str. */ -staticfn char * -choose_random_part(char *str, char sep) -{ - int nsep = 1; - int csep; - int len = 0; - char *begin = str; - - if (!str) - return (char *) 0; - - while (*str) { - if (*str == sep) - nsep++; - str++; - } - csep = rn2(nsep); - str = begin; - while ((csep > 0) && *str) { - str++; - if (*str == sep) - csep--; - } - if (*str) { - if (*str == sep) - str++; - begin = str; - while (*str && *str != sep) { - str++; - len++; - } - *str = '\0'; - if (len) - return begin; - } - return (char *) 0; -} - -staticfn void -free_config_sections(void) -{ - if (gc.config_section_chosen) { - free(gc.config_section_chosen); - gc.config_section_chosen = NULL; - } - if (gc.config_section_current) { - free(gc.config_section_current); - gc.config_section_current = NULL; - } -} - -/* check for " [ anything-except-bracket-or-empty ] # arbitrary-comment" - with spaces optional; returns pointer to "anything-except..." (with - trailing " ] #..." stripped) if ok, otherwise Null */ -staticfn char * -is_config_section( - char *str) /* trailing spaces are stripped, ']' too iff result is good */ -{ - char *a, *c, *z; - - /* remove any spaces at start and end; won't significantly interfere - with echoing the string in a config error message, if warranted */ - a = trimspaces(str); - /* first character should be open square bracket; set pointer past it */ - if (*a++ != '[') - return (char *) 0; - /* last character should be close bracket, ignoring any comment */ - z = strchr(a, ']'); - if (!z) - return (char *) 0; - /* comment, if present, can be preceded by spaces */ - for (c = z + 1; *c == ' '; ++c) - continue; - if (*c && *c != '#') - return (char *) 0; - /* we now know that result is good; there won't be a config error - message so we can modify the input string */ - *z = '\0'; - /* 'a' points past '[' and the string ends where ']' was; remove any - spaces between '[' and choice-start and between choice-end and ']' */ - return trimspaces(a); -} - -staticfn boolean -handle_config_section(char *buf) -{ - char *sect = is_config_section(buf); - - if (sect) { - if (gc.config_section_current) - free(gc.config_section_current), gc.config_section_current = 0; - /* is_config_section() removed brackets from 'sect' */ - if (!gc.config_section_chosen) { - config_error_add("Section \"[%s]\" without CHOOSE", sect); - return TRUE; - } - if (*sect) { /* got a section name */ - gc.config_section_current = dupstr(sect); - debugpline1("set config section: '%s'", - gc.config_section_current); - } else { /* empty section name => end of sections */ - free_config_sections(); - debugpline0("unset config section"); - } - return TRUE; - } - - if (gc.config_section_current) { - if (!gc.config_section_chosen) - return TRUE; - if (strcmp(gc.config_section_current, gc.config_section_chosen)) - return TRUE; - } - return FALSE; -} - -#define match_varname(INP, NAM, LEN) match_optname(INP, NAM, LEN, TRUE) - -/* find the '=' or ':' */ -staticfn char * -find_optparam(const char *buf) -{ - char *bufp, *altp; - - bufp = strchr(buf, '='); - altp = strchr(buf, ':'); - if (!bufp || (altp && altp < bufp)) - bufp = altp; - - return bufp; -} - -staticfn boolean -cnf_line_OPTIONS(char *origbuf) -{ - char *bufp = find_optparam(origbuf); - - ++bufp; /* skip '='; parseoptions() handles spaces */ - return parseoptions(bufp, TRUE, TRUE); -} - -staticfn boolean -cnf_line_AUTOPICKUP_EXCEPTION(char *bufp) -{ - add_autopickup_exception(bufp); - return TRUE; -} - -staticfn boolean -cnf_line_BINDINGS(char *bufp) -{ - return parsebindings(bufp); -} - -staticfn boolean -cnf_line_AUTOCOMPLETE(char *bufp) -{ - parseautocomplete(bufp, TRUE); - return TRUE; -} - -staticfn boolean -cnf_line_MSGTYPE(char *bufp) -{ - return msgtype_parse_add(bufp); -} - -staticfn boolean -cnf_line_HACKDIR(char *bufp) -{ -#ifdef NOCWD_ASSUMPTIONS - adjust_prefix(bufp, HACKPREFIX); -#else /*NOCWD_ASSUMPTIONS*/ -#ifdef MICRO - (void) strncpy(gh.hackdir, bufp, PATHLEN - 1); -#else /* MICRO */ - nhUse(bufp); -#endif /* MICRO */ -#endif /*NOCWD_ASSUMPTIONS*/ - return TRUE; -} - -staticfn boolean -cnf_line_LEVELDIR(char *bufp) -{ -#ifdef NOCWD_ASSUMPTIONS - adjust_prefix(bufp, LEVELPREFIX); -#else /*NOCWD_ASSUMPTIONS*/ -#ifdef MICRO - if (strlen(bufp) >= PATHLEN) - bufp[PATHLEN - 1] = '\0'; - Strcpy(g.permbones, bufp); - if (!ramdisk_specified || !*levels) - Strcpy(levels, bufp); - gr.ramdisk = (strcmp(g.permbones, levels) != 0); -#else /* MICRO */ - nhUse(bufp); -#endif /* MICRO */ -#endif /*NOCWD_ASSUMPTIONS*/ - return TRUE; -} - -staticfn boolean -cnf_line_SAVEDIR(char *bufp) -{ -#ifdef NOCWD_ASSUMPTIONS - adjust_prefix(bufp, SAVEPREFIX); -#else /*NOCWD_ASSUMPTIONS*/ -#ifdef MICRO - char *ptr; - - if ((ptr = strchr(bufp, ';')) != 0) { - *ptr = '\0'; - } - - (void) strncpy(gs.SAVEP, bufp, SAVESIZE - 1); - append_slash(gs.SAVEP); -#else /* MICRO */ - nhUse(bufp); -#endif /* MICRO */ -#endif /*NOCWD_ASSUMPTIONS*/ - return TRUE; -} - -staticfn boolean -cnf_line_BONESDIR(char *bufp) -{ -#ifdef NOCWD_ASSUMPTIONS - adjust_prefix(bufp, BONESPREFIX); -#else - nhUse(bufp); -#endif - return TRUE; -} - -staticfn boolean -cnf_line_DATADIR(char *bufp) -{ -#ifdef NOCWD_ASSUMPTIONS - adjust_prefix(bufp, DATAPREFIX); -#else - nhUse(bufp); -#endif - return TRUE; -} - -staticfn boolean -cnf_line_SCOREDIR(char *bufp) -{ -#ifdef NOCWD_ASSUMPTIONS - adjust_prefix(bufp, SCOREPREFIX); -#else - nhUse(bufp); -#endif - return TRUE; -} - -staticfn boolean -cnf_line_LOCKDIR(char *bufp) -{ -#ifdef NOCWD_ASSUMPTIONS - adjust_prefix(bufp, LOCKPREFIX); -#else - nhUse(bufp); -#endif - return TRUE; -} - -staticfn boolean -cnf_line_CONFIGDIR(char *bufp) -{ -#ifdef NOCWD_ASSUMPTIONS - adjust_prefix(bufp, CONFIGPREFIX); -#else - nhUse(bufp); -#endif - return TRUE; -} - -staticfn boolean -cnf_line_TROUBLEDIR(char *bufp) -{ -#ifdef NOCWD_ASSUMPTIONS - adjust_prefix(bufp, TROUBLEPREFIX); -#else - nhUse(bufp); -#endif - return TRUE; -} - -staticfn boolean -cnf_line_NAME(char *bufp) -{ - (void) strncpy(svp.plname, bufp, PL_NSIZ - 1); - return TRUE; -} - -staticfn boolean -cnf_line_ROLE(char *bufp) -{ - int len; - - if ((len = str2role(bufp)) >= 0) - flags.initrole = len; - return TRUE; -} - -staticfn boolean -cnf_line_dogname(char *bufp) -{ - (void) strncpy(gd.dogname, bufp, PL_PSIZ - 1); - return TRUE; -} - -staticfn boolean -cnf_line_catname(char *bufp) -{ - (void) strncpy(gc.catname, bufp, PL_PSIZ - 1); - return TRUE; -} - -#ifdef SYSCF - -staticfn boolean -cnf_line_WIZARDS(char *bufp) -{ - if (sysopt.wizards) - free((genericptr_t) sysopt.wizards); - sysopt.wizards = dupstr(bufp); - if (strlen(sysopt.wizards) && strcmp(sysopt.wizards, "*")) { - /* pre-format WIZARDS list now; it's displayed during a panic - and since that panic might be due to running out of memory, - we don't want to risk attempting to allocate any memory then */ - if (sysopt.fmtd_wizard_list) - free((genericptr_t) sysopt.fmtd_wizard_list); - sysopt.fmtd_wizard_list = build_english_list(sysopt.wizards); - } - return TRUE; -} - -staticfn boolean -cnf_line_SHELLERS(char *bufp) -{ - if (sysopt.shellers) - free((genericptr_t) sysopt.shellers); - sysopt.shellers = dupstr(bufp); - return TRUE; -} - -staticfn boolean -cnf_line_EXPLORERS(char *bufp) -{ - if (sysopt.explorers) - free((genericptr_t) sysopt.explorers); - sysopt.explorers = dupstr(bufp); - return TRUE; -} - -staticfn boolean -cnf_line_DEBUGFILES(char *bufp) -{ - /* might already have a vaule from getenv("DEBUGFILES"); - if so, ignore this value from SYSCF */ - if (!sysopt.env_dbgfl) { - if (sysopt.debugfiles) - free((genericptr_t) sysopt.debugfiles); - sysopt.debugfiles = dupstr(bufp); - } - return TRUE; -} - -staticfn boolean -cnf_line_DUMPLOGFILE(char *bufp) -{ -#ifdef DUMPLOG - if (sysopt.dumplogfile) - free((genericptr_t) sysopt.dumplogfile); - sysopt.dumplogfile = dupstr(bufp); -#else - nhUse(bufp); -#endif /*DUMPLOG*/ - return TRUE; -} - -staticfn boolean -cnf_line_GENERICUSERS(char *bufp) -{ - if (sysopt.genericusers) - free((genericptr_t) sysopt.genericusers); - sysopt.genericusers = dupstr(bufp); - return TRUE; -} - -staticfn boolean -cnf_line_BONES_POOLS(char *bufp) -{ - /* max value of 10 guarantees (N % bones.pools) will be one digit - so we don't lose control of the length of bones file names */ - int n = atoi(bufp); - - sysopt.bones_pools = (n <= 0) ? 0 : min(n, 10); - /* note: right now bones_pools==0 is the same as bones_pools==1, - but we could change that and make bones_pools==0 become an - indicator to suppress bones usage altogether */ - return TRUE; -} - -staticfn boolean -cnf_line_SUPPORT(char *bufp) -{ - if (sysopt.support) - free((genericptr_t) sysopt.support); - sysopt.support = dupstr(bufp); - return TRUE; -} - -staticfn boolean -cnf_line_RECOVER(char *bufp) -{ - if (sysopt.recover) - free((genericptr_t) sysopt.recover); - sysopt.recover = dupstr(bufp); - return TRUE; -} - -staticfn boolean -cnf_line_CHECK_SAVE_UID(char *bufp) -{ - int n = atoi(bufp); - - sysopt.check_save_uid = n; - return TRUE; -} - -staticfn boolean -cnf_line_CHECK_PLNAME(char *bufp) -{ - int n = atoi(bufp); - - sysopt.check_plname = n; - return TRUE; -} - -staticfn boolean -cnf_line_SEDUCE(char *bufp) -{ - int n = !!atoi(bufp); /* XXX this could be tighter */ -#ifdef SYSCF - int src = iflags.parse_config_file_src; - boolean in_sysconf = (src == set_in_sysconf); -#else - boolean in_sysconf = FALSE; -#endif - - /* allow anyone to disable it but can only enable it in sysconf - or as a no-op for the user when sysconf hasn't disabled it */ - if (!in_sysconf && !sysopt.seduce && n != 0) { - config_error_add("Illegal value in SEDUCE"); - n = 0; - } - sysopt.seduce = n; - sysopt_seduce_set(sysopt.seduce); - return TRUE; -} - -staticfn boolean -cnf_line_HIDEUSAGE(char *bufp) -{ - int n = !!atoi(bufp); - - sysopt.hideusage = n; - return TRUE; -} - -staticfn boolean -cnf_line_MAXPLAYERS(char *bufp) -{ - int n = atoi(bufp); - - /* XXX to get more than 25, need to rewrite all lock code */ - if (n < 0 || n > 25) { - config_error_add("Illegal value in MAXPLAYERS (maximum is 25)"); - n = 5; - } - sysopt.maxplayers = n; - return TRUE; -} - -staticfn boolean -cnf_line_PERSMAX(char *bufp) -{ - int n = atoi(bufp); - - if (n < 1) { - config_error_add("Illegal value in PERSMAX (minimum is 1)"); - n = 0; - } - sysopt.persmax = n; - return TRUE; -} - -staticfn boolean -cnf_line_PERS_IS_UID(char *bufp) -{ - int n = atoi(bufp); - - if (n != 0 && n != 1) { - config_error_add("Illegal value in PERS_IS_UID (must be 0 or 1)"); - n = 0; - } - sysopt.pers_is_uid = n; - return TRUE; -} - -staticfn boolean -cnf_line_ENTRYMAX(char *bufp) -{ - int n = atoi(bufp); - - if (n < 10) { - config_error_add("Illegal value in ENTRYMAX (minimum is 10)"); - n = 10; - } - sysopt.entrymax = n; - return TRUE; -} - -staticfn boolean -cnf_line_POINTSMIN(char *bufp) -{ - int n = atoi(bufp); - - if (n < 1) { - config_error_add("Illegal value in POINTSMIN (minimum is 1)"); - n = 100; - } - sysopt.pointsmin = n; - return TRUE; -} - -staticfn boolean -cnf_line_MAX_STATUENAME_RANK(char *bufp) -{ - int n = atoi(bufp); - - if (n < 1) { - config_error_add("Illegal value in MAX_STATUENAME_RANK" - " (minimum is 1)"); - n = 10; - } - sysopt.tt_oname_maxrank = n; - return TRUE; -} - -staticfn boolean -cnf_line_LIVELOG(char *bufp) -{ - /* using 0 for base accepts "dddd" as decimal provided that first 'd' - isn't '0', "0xhhhh" as hexadecimal, and "0oooo" as octal; ignores - any trailing junk, including '8' or '9' for leading '0' octal */ - long L = strtol(bufp, NULL, 0); - - if (L < 0L || L > 0xffffL) { - config_error_add("Illegal value for LIVELOG" - " (must be between 0 and 0xFFFF)."); - return 0; - } - sysopt.livelog = L; - return TRUE; -} - -staticfn boolean -cnf_line_PANICTRACE_LIBC(char *bufp) -{ - int n = atoi(bufp); - -#if defined(PANICTRACE) && defined(PANICTRACE_LIBC) - if (n < 0 || n > 2) { - config_error_add("Illegal value in PANICTRACE_LIBC (not 0,1,2)"); - n = 0; - } -#endif - sysopt.panictrace_libc = n; - return TRUE; -} - -staticfn boolean -cnf_line_PANICTRACE_GDB(char *bufp) -{ - int n = atoi(bufp); - -#if defined(PANICTRACE) - if (n < 0 || n > 2) { - config_error_add("Illegal value in PANICTRACE_GDB (not 0,1,2)"); - n = 0; - } -#endif - sysopt.panictrace_gdb = n; - return TRUE; -} - -staticfn boolean -cnf_line_GDBPATH(char *bufp) -{ -#if defined(PANICTRACE) && !defined(VMS) - if (!file_exists(bufp)) { - config_error_add("File specified in GDBPATH does not exist"); - return FALSE; - } -#endif - if (sysopt.gdbpath) - free((genericptr_t) sysopt.gdbpath); - sysopt.gdbpath = dupstr(bufp); - return TRUE; -} - -staticfn boolean -cnf_line_GREPPATH(char *bufp) -{ -#if defined(PANICTRACE) && !defined(VMS) - if (!file_exists(bufp)) { - config_error_add("File specified in GREPPATH does not exist"); - return FALSE; - } -#endif - if (sysopt.greppath) - free((genericptr_t) sysopt.greppath); - sysopt.greppath = dupstr(bufp); - return TRUE; -} - -staticfn boolean -cnf_line_CRASHREPORTURL(char *bufp) -{ - if (sysopt.crashreporturl) - free((genericptr_t) sysopt.crashreporturl); - sysopt.crashreporturl = dupstr(bufp); - return TRUE; -} - -staticfn boolean -cnf_line_SAVEFORMAT(char *bufp) -{ - parseformat(sysopt.saveformat, bufp); - return TRUE; -} - -staticfn boolean -cnf_line_BONESFORMAT(char *bufp) -{ - parseformat(sysopt.bonesformat, bufp); - return TRUE; -} - -staticfn boolean -cnf_line_ACCESSIBILITY(char *bufp) -{ - int n = atoi(bufp); - - if (n < 0 || n > 1) { - config_error_add("Illegal value in ACCESSIBILITY (not 0,1)"); - n = 0; - } - sysopt.accessibility = n; - return TRUE; -} - -staticfn boolean -cnf_line_PORTABLE_DEVICE_PATHS(char *bufp) -{ -#ifdef WIN32 - int n = atoi(bufp); - - if (n < 0 || n > 1) { - config_error_add("Illegal value in PORTABLE_DEVICE_PATHS" - " (not 0 or 1)"); - n = 0; - } - sysopt.portable_device_paths = n; -#else /* Windows-only directive encountered by non-Windows config */ - nhUse(bufp); - config_error_add("PORTABLE_DEVICE_PATHS is not supported"); -#endif - return TRUE; -} - -#endif /* SYSCF */ - -staticfn boolean -cnf_line_BOULDER(char *bufp) -{ - (void) get_uchars(bufp, &go.ov_primary_syms[SYM_BOULDER + SYM_OFF_X], - TRUE, 1, "BOULDER"); - return TRUE; -} - -staticfn boolean -cnf_line_MENUCOLOR(char *bufp) -{ - return add_menu_coloring(bufp); -} - -staticfn boolean -cnf_line_HILITE_STATUS(char *bufp) -{ -#ifdef STATUS_HILITES - return parse_status_hl1(bufp, TRUE); -#else - nhUse(bufp); - return TRUE; -#endif -} - -staticfn boolean -cnf_line_WARNINGS(char *bufp) -{ - uchar translate[MAXPCHARS]; - - (void) get_uchars(bufp, translate, FALSE, WARNCOUNT, "WARNINGS"); - assign_warnings(translate); - return TRUE; -} - -staticfn boolean -cnf_line_ROGUESYMBOLS(char *bufp) -{ - if (parsesymbols(bufp, ROGUESET)) { - switch_symbols(TRUE); - return TRUE; - } - config_error_add("Error in ROGUESYMBOLS definition '%s'", bufp); - return FALSE; -} - -staticfn boolean -cnf_line_SYMBOLS(char *bufp) -{ - if (parsesymbols(bufp, PRIMARYSET)) { - switch_symbols(TRUE); - return TRUE; - } - config_error_add("Error in SYMBOLS definition '%s'", bufp); - return FALSE; -} - -staticfn boolean -cnf_line_WIZKIT(char *bufp) -{ - (void) strncpy(gw.wizkit, bufp, WIZKIT_MAX - 1); - return TRUE; -} - -#ifdef USER_SOUNDS -staticfn boolean -cnf_line_SOUNDDIR(char *bufp) -{ - if (sounddir) - free((genericptr_t) sounddir); - sounddir = dupstr(bufp); - return TRUE; -} - -staticfn boolean -cnf_line_SOUND(char *bufp) -{ - add_sound_mapping(bufp); - return TRUE; -} -#endif /*USER_SOUNDS*/ - -staticfn boolean -cnf_line_QT_TILEWIDTH(char *bufp) -{ -#ifdef QT_GRAPHICS - extern char *qt_tilewidth; - - if (qt_tilewidth == NULL) - qt_tilewidth = dupstr(bufp); -#else - nhUse(bufp); -#endif - return TRUE; -} - -staticfn boolean -cnf_line_QT_TILEHEIGHT(char *bufp) -{ -#ifdef QT_GRAPHICS - extern char *qt_tileheight; - - if (qt_tileheight == NULL) - qt_tileheight = dupstr(bufp); -#else - nhUse(bufp); -#endif - return TRUE; -} - -staticfn boolean -cnf_line_QT_FONTSIZE(char *bufp) -{ -#ifdef QT_GRAPHICS - extern char *qt_fontsize; - - if (qt_fontsize == NULL) - qt_fontsize = dupstr(bufp); -#else - nhUse(bufp); -#endif - return TRUE; -} - -staticfn boolean -cnf_line_QT_COMPACT(char *bufp) -{ -#ifdef QT_GRAPHICS - extern int qt_compact_mode; - - qt_compact_mode = atoi(bufp); -#else - nhUse(bufp); -#endif - return TRUE; -} - -typedef boolean (*config_line_stmt_func)(char *); - -/* normal */ -#define CNFL_N(n, l) { #n, l, FALSE, FALSE, cnf_line_##n } -/* 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 } - -static const struct match_config_line_stmt { - const char *name; - int len; - boolean syscnf_only; - boolean origbuf; - config_line_stmt_func fn; -} config_line_stmt[] = { - /* OPTIONS handled separately */ - { "OPTIONS", 4, FALSE, TRUE, cnf_line_OPTIONS }, - CNFL_N(AUTOPICKUP_EXCEPTION, 5), - CNFL_N(BINDINGS, 4), - CNFL_N(AUTOCOMPLETE, 5), - CNFL_N(MSGTYPE, 7), - CNFL_N(HACKDIR, 4), - CNFL_N(LEVELDIR, 4), - CNFL_NA(LEVELS, 4, LEVELDIR), - CNFL_N(SAVEDIR, 4), - CNFL_N(BONESDIR, 5), - CNFL_N(DATADIR, 4), - CNFL_N(SCOREDIR, 4), - CNFL_N(LOCKDIR, 4), - CNFL_N(CONFIGDIR, 4), - CNFL_N(TROUBLEDIR, 4), - CNFL_N(NAME, 4), - CNFL_N(ROLE, 4), - CNFL_NA(CHARACTER, 4, ROLE), - CNFL_N(dogname, 3), - CNFL_N(catname, 3), -#ifdef SYSCF - CNFL_S(WIZARDS, 7), - CNFL_S(SHELLERS, 8), - CNFL_S(EXPLORERS, 7), - CNFL_S(DEBUGFILES, 5), - CNFL_S(DUMPLOGFILE, 7), - CNFL_S(GENERICUSERS, 12), - CNFL_S(BONES_POOLS, 10), - CNFL_S(SUPPORT, 7), - CNFL_S(RECOVER, 7), - CNFL_S(CHECK_SAVE_UID, 14), - CNFL_S(CHECK_PLNAME, 12), - CNFL_S(SEDUCE, 6), - CNFL_S(HIDEUSAGE, 9), - CNFL_S(MAXPLAYERS, 10), - CNFL_S(PERSMAX, 7), - CNFL_S(PERS_IS_UID, 11), - CNFL_S(ENTRYMAX, 8), - CNFL_S(POINTSMIN, 9), - CNFL_S(MAX_STATUENAME_RANK, 10), - CNFL_S(LIVELOG, 7), - CNFL_S(PANICTRACE_LIBC, 15), - CNFL_S(PANICTRACE_GDB, 14), - CNFL_S(CRASHREPORTURL, 13), - CNFL_S(GDBPATH, 7), - CNFL_S(GREPPATH, 7), - CNFL_S(SAVEFORMAT, 10), - CNFL_S(BONESFORMAT, 11), - CNFL_S(ACCESSIBILITY, 13), - CNFL_S(PORTABLE_DEVICE_PATHS, 8), -#endif /*SYSCF*/ - CNFL_N(BOULDER, 3), - CNFL_N(MENUCOLOR, 9), - CNFL_N(HILITE_STATUS, 6), - CNFL_N(WARNINGS, 5), - CNFL_N(ROGUESYMBOLS, 4), - CNFL_N(SYMBOLS, 4), - CNFL_N(WIZKIT, 6), -#ifdef USER_SOUNDS - CNFL_N(SOUNDDIR, 8), - CNFL_N(SOUND, 5), -#endif /*USER_SOUNDS*/ - CNFL_N(QT_TILEWIDTH, 12), - CNFL_N(QT_TILEHEIGHT, 13), - CNFL_N(QT_FONTSIZE, 11), - CNFL_N(QT_COMPACT, 10) -}; - -#undef CNFL_N -#undef CNFL_NA -#undef CNFL_S - -boolean -parse_config_line(char *origbuf) -{ -#if defined(MICRO) && !defined(NOCWD_ASSUMPTIONS) - static boolean ramdisk_specified = FALSE; -#endif -#ifdef SYSCF - int src = iflags.parse_config_file_src; - boolean in_sysconf = (src == set_in_sysconf); -#endif - char *bufp, buf[4 * BUFSZ]; - int i; - - while (*origbuf == ' ' || *origbuf == '\t') /* skip leading whitespace */ - ++origbuf; /* (caller probably already did this) */ - (void) strncpy(buf, origbuf, sizeof buf - 1); - buf[sizeof buf - 1] = '\0'; /* strncpy not guaranteed to NUL terminate */ - /* convert any tab to space, condense consecutive spaces into one, - remove leading and trailing spaces (exception: if there is nothing - but spaces, one of them will be kept even though it leads/trails) */ - mungspaces(buf); - - /* find the '=' or ':' */ - bufp = find_optparam(buf); - if (!bufp) { - config_error_add("Not a config statement, missing '='"); - return FALSE; - } - /* skip past '=', then space between it and value, if any */ - ++bufp; - if (*bufp == ' ') - ++bufp; - - for (i = 0; i < SIZE(config_line_stmt); i++) { -#ifdef SYSCF - if (config_line_stmt[i].syscnf_only && !in_sysconf) - continue; -#endif - if (match_varname(buf, config_line_stmt[i].name, - config_line_stmt[i].len)) { - char *parm = config_line_stmt[i].origbuf ? origbuf : bufp; - - return config_line_stmt[i].fn(parm); - } - } - - config_error_add("Unknown config statement"); - return FALSE; -} - -#ifdef USER_SOUNDS -boolean -can_read_file(const char *filename) -{ - return (boolean) (access(filename, 4) == 0); -} -#endif /* USER_SOUNDS */ - -struct _config_error_errmsg { - int line_num; - char *errormsg; - struct _config_error_errmsg *next; -}; - -struct _config_error_frame { - int line_num; - int num_errors; - boolean origline_shown; - boolean fromfile; - boolean secure; - char origline[4 * BUFSZ]; - char source[BUFSZ]; - struct _config_error_frame *next; -}; - -static struct _config_error_frame *config_error_data = 0; -static struct _config_error_errmsg *config_error_msg = 0; - -void -config_error_init(boolean from_file, const char *sourcename, boolean secure) -{ - struct _config_error_frame *tmp = (struct _config_error_frame *) - alloc(sizeof *tmp); - - tmp->line_num = 0; - tmp->num_errors = 0; - tmp->origline_shown = FALSE; - tmp->fromfile = from_file; - tmp->secure = secure; - tmp->origline[0] = '\0'; - if (sourcename && sourcename[0]) { - (void) strncpy(tmp->source, sourcename, sizeof (tmp->source) - 1); - tmp->source[sizeof (tmp->source) - 1] = '\0'; - } else - tmp->source[0] = '\0'; - - tmp->next = config_error_data; - config_error_data = tmp; - program_state.config_error_ready = TRUE; -} - -staticfn boolean -config_error_nextline(const char *line) -{ - struct _config_error_frame *ced = config_error_data; - - if (!ced) - return FALSE; - - if (ced->num_errors && ced->secure) - return FALSE; - - ced->line_num++; - ced->origline_shown = FALSE; - if (line && line[0]) { - (void) strncpy(ced->origline, line, sizeof (ced->origline) - 1); - ced->origline[sizeof (ced->origline) - 1] = '\0'; - } else - ced->origline[0] = '\0'; - - return TRUE; -} - -int -l_get_config_errors(lua_State *L) -{ - struct _config_error_errmsg *dat = config_error_msg; - struct _config_error_errmsg *tmp; - int idx = 1; - - lua_newtable(L); - - while (dat) { - lua_pushinteger(L, idx++); - lua_newtable(L); - nhl_add_table_entry_int(L, "line", dat->line_num); - nhl_add_table_entry_str(L, "error", dat->errormsg); - lua_settable(L, -3); - tmp = dat->next; - free(dat->errormsg); - dat->errormsg = (char *) 0; - free(dat); - dat = tmp; - } - config_error_msg = (struct _config_error_errmsg *) 0; - - return 1; -} - -/* varargs 'config_error_add()' moved to pline.c */ -void -config_erradd(const char *buf) -{ - char lineno[QBUFSZ]; - const char *punct; - - if (!buf || !*buf) - buf = "Unknown error"; - - /* if buf[] doesn't end in a period, exclamation point, or question mark, - we'll include a period (in the message, not appended to buf[]) */ - punct = c_eos((char *) buf) - 1; /* eos(buf)-1 is valid */ - punct = strchr(".!?", *punct) ? "" : "."; - - if (!program_state.config_error_ready) { - /* either very early, where pline() will use raw_print(), or - player gave bad value when prompted by interactive 'O' command */ - pline("%s%s%s", !iflags.window_inited ? "config_error_add: " : "", - buf, punct); - wait_synch(); - return; - } - - if (iflags.in_lua) { - struct _config_error_errmsg *dat - = (struct _config_error_errmsg *) alloc(sizeof *dat); - - dat->next = config_error_msg; - dat->line_num = config_error_data->line_num; - dat->errormsg = dupstr(buf); - config_error_msg = dat; - return; - } - - config_error_data->num_errors++; - if (!config_error_data->origline_shown && !config_error_data->secure) { - pline("\n%s", config_error_data->origline); - config_error_data->origline_shown = TRUE; - } - if (config_error_data->line_num > 0 && !config_error_data->secure) { - Sprintf(lineno, "Line %d: ", config_error_data->line_num); - } else - lineno[0] = '\0'; - - pline("%s %s%s%s", config_error_data->secure ? "Error:" : " *", - lineno, buf, punct); -} - -int -config_error_done(void) -{ - int n; - struct _config_error_frame *tmp = config_error_data; - - if (!config_error_data) - return 0; - n = config_error_data->num_errors; -#ifndef USER_SOUNDS - if (gn.no_sound_notified > 0) { - /* no USER_SOUNDS; config_error_add() was called once for first - SOUND or SOUNDDIR entry seen, then skipped for any others; - include those skipped ones in the total error count */ - n += (gn.no_sound_notified - 1); - gn.no_sound_notified = 0; - } -#endif - if (n) { - boolean cmdline = !strcmp(config_error_data->source, "command line"); - - pline("\n%d error%s %s %s.\n", n, plur(n), cmdline ? "on" : "in", - *config_error_data->source ? config_error_data->source - : configfile); - wait_synch(); - } - config_error_data = tmp->next; - free(tmp); - program_state.config_error_ready = (config_error_data != 0); - return n; -} - -boolean -read_config_file(const char *filename, int src) -{ - FILE *fp; - boolean rv = TRUE; - - if (!(fp = fopen_config_file(filename, src))) - return FALSE; - - /* begin detection of duplicate configfile options */ - reset_duplicate_opt_detection(); - free_config_sections(); - iflags.parse_config_file_src = src; - - rv = parse_conf_file(fp, parse_config_line); - (void) fclose(fp); - - free_config_sections(); - /* turn off detection of duplicate configfile options */ - reset_duplicate_opt_detection(); - return rv; -} - -struct _cnf_parser_state { - char *inbuf; - unsigned inbufsz; - int rv; - char *ep; - char *buf; - boolean skip, morelines; - boolean cont; - boolean pbreak; -}; - -/* Initialize config parser data */ -staticfn void -cnf_parser_init(struct _cnf_parser_state *parser) -{ - parser->rv = TRUE; /* assume successful parse */ - parser->ep = parser->buf = (char *) 0; - parser->skip = FALSE; - parser->morelines = FALSE; - parser->inbufsz = 4 * BUFSZ; - parser->inbuf = (char *) alloc(parser->inbufsz); - parser->cont = FALSE; - parser->pbreak = FALSE; - memset(parser->inbuf, 0, parser->inbufsz); -} - -/* caller has finished with 'parser' (except for 'rv' so leave that intact) */ -staticfn void -cnf_parser_done(struct _cnf_parser_state *parser) -{ - parser->ep = 0; /* points into parser->inbuf, so becoming stale */ - if (parser->inbuf) - free(parser->inbuf), parser->inbuf = 0; - if (parser->buf) - free(parser->buf), parser->buf = 0; -} - -/* - * Parse config buffer, handling comments, empty lines, config sections, - * CHOOSE, and line continuation, calling proc for every valid line. - * - * Continued lines are merged together with one space in between. - */ -staticfn void -parse_conf_buf(struct _cnf_parser_state *p, boolean (*proc)(char *arg)) -{ - p->cont = FALSE; - p->pbreak = FALSE; - p->ep = strchr(p->inbuf, '\n'); - if (p->skip) { /* in case previous line was too long */ - if (p->ep) - p->skip = FALSE; /* found newline; next line is normal */ - } else { - if (!p->ep) { /* newline missing */ - if (strlen(p->inbuf) < (p->inbufsz - 2)) { - /* likely the last line of file is just - missing a newline; process it anyway */ - p->ep = eos(p->inbuf); - } else { - config_error_add("Line too long, skipping"); - p->skip = TRUE; /* discard next fgets */ - } - } else { - *p->ep = '\0'; /* remove newline */ - } - if (p->ep) { - char *tmpbuf = (char *) 0; - int len; - boolean ignoreline = FALSE; - boolean oldline = FALSE; - - /* line continuation (trailing '\') */ - p->morelines = (--p->ep >= p->inbuf && *p->ep == '\\'); - if (p->morelines) - *p->ep = '\0'; - - /* trim off spaces at end of line */ - while (p->ep >= p->inbuf - && (*p->ep == ' ' || *p->ep == '\t' || *p->ep == '\r')) - *p->ep-- = '\0'; - - if (!config_error_nextline(p->inbuf)) { - p->rv = FALSE; - if (p->buf) - free(p->buf), p->buf = (char *) 0; - p->pbreak = TRUE; - return; - } - - p->ep = p->inbuf; - while (*p->ep == ' ' || *p->ep == '\t') - ++p->ep; - - /* ignore empty lines and full-line comment lines */ - if (!*p->ep || *p->ep == '#') - ignoreline = TRUE; - - if (p->buf) - oldline = TRUE; - - /* merge now read line with previous ones, if necessary */ - if (!ignoreline) { - len = (int) strlen(p->ep) + 1; /* +1: final '\0' */ - if (p->buf) - len += (int) strlen(p->buf) + 1; /* +1: space */ - tmpbuf = (char *) alloc(len); - *tmpbuf = '\0'; - if (p->buf) { - Strcat(strcpy(tmpbuf, p->buf), " "); - free(p->buf), p->buf = 0; - } - p->buf = strcat(tmpbuf, p->ep); - if (strlen(p->buf) >= p->inbufsz) - p->buf[p->inbufsz - 1] = '\0'; - } - - if (p->morelines || (ignoreline && !oldline)) - return; - - if (handle_config_section(p->buf)) { - free(p->buf), p->buf = (char *) 0; - return; - } - - /* from here onwards, we'll handle buf only */ - - if (match_varname(p->buf, "CHOOSE", 6)) { - char *section; - char *bufp = find_optparam(p->buf); - - if (!bufp) { - config_error_add("Format is CHOOSE=section1" - ",section2,..."); - p->rv = FALSE; - free(p->buf), p->buf = (char *) 0; - return; - } - bufp++; - if (gc.config_section_chosen) - free(gc.config_section_chosen), - gc.config_section_chosen = 0; - section = choose_random_part(bufp, ','); - if (section) { - gc.config_section_chosen = dupstr(section); - } else { - config_error_add("No config section to choose"); - p->rv = FALSE; - } - free(p->buf), p->buf = (char *) 0; - return; - } - - if (!(*proc)(p->buf)) - p->rv = FALSE; - - free(p->buf), p->buf = (char *) 0; - } - } -} - -boolean -parse_conf_str(const char *str, boolean (*proc)(char *arg)) -{ - size_t len; - struct _cnf_parser_state parser; - - cnf_parser_init(&parser); - free_config_sections(); - config_error_init(FALSE, "parse_conf_str", FALSE); - while (str && *str) { - len = 0; - while (*str && len < (parser.inbufsz-1)) { - parser.inbuf[len] = *str; - len++; - str++; - if (parser.inbuf[len-1] == '\n') - break; - } - parser.inbuf[len] = '\0'; - parse_conf_buf(&parser, proc); - if (parser.pbreak) - break; - } - cnf_parser_done(&parser); - - free_config_sections(); - config_error_done(); - return parser.rv; -} - -/* parse_conf_file - * - * Read from file fp, calling parse_conf_buf for each line. - */ -staticfn boolean -parse_conf_file(FILE *fp, boolean (*proc)(char *arg)) -{ - struct _cnf_parser_state parser; - - cnf_parser_init(&parser); - free_config_sections(); - - while (fgets(parser.inbuf, parser.inbufsz, fp)) { - parse_conf_buf(&parser, proc); - if (parser.pbreak) - break; - } - cnf_parser_done(&parser); - - free_config_sections(); - return parser.rv; -} - -#ifdef SYSCF -staticfn void -parseformat(int *arr, char *str) -{ - const char *legal[] = { "historical", "lendian", "ascii" }; - int i, kwi = 0, words = 0; - char *p = str, *keywords[2]; - - while (*p) { - while (*p && isspace((uchar) *p)) { - *p = '\0'; - p++; - } - if (*p) { - words++; - if (kwi < 2) - keywords[kwi++] = p; - } - while (*p && !isspace((uchar) *p)) - p++; - } - if (!words) { - impossible("missing format list"); - return; - } - while (--kwi >= 0) - if (kwi < 2) { - for (i = 0; i < SIZE(legal); ++i) { - if (!strcmpi(keywords[kwi], legal[i])) - arr[kwi] = i + 1; - } - } -} -#endif /* SYSCF */ - -/* ---------- END CONFIG FILE HANDLING ----------- */ - /* ---------- BEGIN WIZKIT FILE HANDLING ----------- */ staticfn FILE * @@ -4254,18 +2831,22 @@ testinglog(const char *filenm, /* ad hoc file name */ #ifdef SELF_RECOVER /* ---------- BEGIN INTERNAL RECOVER ----------- */ + +extern uchar critical_sizes[], cscbuf[]; /* version.c */ + boolean recover_savefile(void) { NHFILE *gnhfp, *lnhfp, *snhfp; - int lev, savelev, hpid, pltmpsiz, filecmc; + int lev, savelev, hpid, + pltmpsiz, cscount = get_critical_size_count(); xint8 levc; struct version_info version_data; int processed[256]; - char savename[SAVESIZE], errbuf[BUFSZ], indicator; - struct savefile_info sfi; - char tmpplbuf[PL_NSIZ]; + char savename[SAVESIZE], errbuf[BUFSZ], indicator, file_cscount; + char tmpplbuf[PL_NSIZ_PLUS]; const char *savewrite_failure = (const char *) 0; + off_t filesz = 0; for (lev = 0; lev < 256; lev++) processed[lev] = 0; @@ -4274,7 +2855,6 @@ recover_savefile(void) * pid of creating process (ignored here) * level number for current level of save file * name of save file nethack would have created - * savefile info * player name * and game state */ @@ -4284,6 +2864,30 @@ recover_savefile(void) raw_printf("%s\n", errbuf); return FALSE; } + filesz = lseek(gnhfp->fd, 0L, SEEK_END); + (void) lseek(gnhfp->fd, 0L, SEEK_SET); + if ((size_t) filesz < (sizeof hpid + + sizeof lev + + sizeof savename + + sizeof indicator + + sizeof file_cscount + + sizeof version_data + sizeof pltmpsiz)) { + const char *fq_save; + + /* this indicates a .0 file that was created as part of + * recover that did not complete. There could be an intact + * savefile already there. Check for that and return TRUE + * if there is. */ + set_savefile_name(TRUE); + fq_save = fqname(gs.SAVEF, SAVEPREFIX, 0); + if (access(fq_save, F_OK) == 0) { + close_nhfile(gnhfp); + delete_levelfile(0); + return TRUE; + } else { + /* savefile doesn't exist, so fall through */ + } + } if (read(gnhfp->fd, (genericptr_t) &hpid, sizeof hpid) != sizeof hpid) { raw_printf("\n%s\n%s\n", "Checkpoint data incompletely written" @@ -4305,13 +2909,15 @@ recover_savefile(void) != sizeof savename) || (read(gnhfp->fd, (genericptr_t) &indicator, sizeof indicator) != sizeof indicator) - || (read(gnhfp->fd, (genericptr_t) &filecmc, sizeof filecmc) - != sizeof filecmc) + || (read(gnhfp->fd, (genericptr_t) &file_cscount, sizeof file_cscount) + != sizeof file_cscount) + || (file_cscount <= cscount + && read(gnhfp->fd, (genericptr_t) &cscbuf, file_cscount) + != file_cscount) || (read(gnhfp->fd, (genericptr_t) &version_data, sizeof version_data) != sizeof version_data) - || (read(gnhfp->fd, (genericptr_t) &sfi, sizeof sfi) != sizeof sfi) || (read(gnhfp->fd, (genericptr_t) &pltmpsiz, sizeof pltmpsiz) - != sizeof pltmpsiz) || (pltmpsiz > PL_NSIZ) + != sizeof pltmpsiz) || (pltmpsiz > PL_NSIZ_PLUS) || (read(gnhfp->fd, (genericptr_t) &tmpplbuf, pltmpsiz) != pltmpsiz)) { raw_printf("\nError reading %s -- can't recover.\n", gl.lock); @@ -4320,16 +2926,19 @@ recover_savefile(void) } /* save file should contain: - * format indicator and cmc + * format indicator (1 byte) + * n = count of critical size list (1 byte) + * n bytes of critical sizes (n bytes) * version info - * savefile info - * player name + * plnametmp = player name size (int, 2 bytes) + * player name (PL_NSIZ_PLUS) * current level (including pets) * (non-level-based) game state * other levels */ /* + * * Set a flag for the savefile routines to know the * circumstances and act accordingly: * program_state.in_self_recover @@ -4352,32 +2961,20 @@ recover_savefile(void) return FALSE; } - /* - * Our savefile output format might _not_ be structlevel. - * We have to check and use the correct output routine here. - */ - /*store_formatindicator(snhfp); */ store_version(snhfp); - if (snhfp->structlevel) { - if (write(snhfp->fd, (genericptr_t) &sfi, sizeof sfi) != sizeof sfi) - savewrite_failure = "savefileinfo"; - } if (savewrite_failure) goto cleanup; - if (snhfp->structlevel) { - if (write(snhfp->fd, (genericptr_t) &pltmpsiz, sizeof pltmpsiz) - != sizeof pltmpsiz) - savewrite_failure = "player name size"; - } + /* TODO: this is not a single byte, so a big-endian byte swap + * might be necessary here, if anyone is concerned about big-endian */ + Sfo_int(snhfp, &pltmpsiz, "plname-size"); + savewrite_failure = (const char *) 0; if (savewrite_failure) goto cleanup; - if (snhfp->structlevel) { - if (write(snhfp->fd, (genericptr_t) &tmpplbuf, pltmpsiz) != pltmpsiz) - savewrite_failure = "player name"; - } + Sfo_char(snhfp, tmpplbuf, "plname", pltmpsiz); + savewrite_failure = (const char *) 0; if (savewrite_failure) goto cleanup; @@ -4452,68 +3049,38 @@ recover_savefile(void) return TRUE; } -boolean -copy_bytes(int ifd, int ofd) -{ - char buf[BUFSIZ]; - int nfrom, nto = 0; - - do { - nfrom = read(ifd, buf, BUFSIZ); - /* read can return -1 */ - if (nfrom >= 0 && nfrom <= BUFSIZ) - nto = write(ofd, buf, nfrom); - if (nto != nfrom || nfrom < 0) - return FALSE; - } while (nfrom == BUFSIZ); - return TRUE; -} - /* ---------- END INTERNAL RECOVER ----------- */ #endif /*SELF_RECOVER*/ /* ---------- OTHER ----------- */ -#ifdef SYSCF -#ifdef SYSCF_FILE -void -assure_syscf_file(void) +ATTRNORETURN void +do_deferred_showpaths(int code) { - int fd; + gd.deferred_showpaths = FALSE; + reveal_paths(code); -#ifdef WIN32 - /* We are checking that the sysconf exists ... lock the path */ - fqn_prefix_locked[SYSCONFPREFIX] = TRUE; -#endif - /* - * All we really care about is the end result - can we read the file? - * So just check that directly. - * - * Not tested on most of the old platforms (which don't attempt - * to implement SYSCF). - * Some ports don't like open()'s optional third argument; - * VMS overrides open() usage with a macro which requires it. - */ -#ifndef VMS -# if defined(NOCWD_ASSUMPTIONS) && defined(WIN32) - fd = open(fqname(SYSCF_FILE, SYSCONFPREFIX, 0), O_RDONLY); -# else - fd = open(SYSCF_FILE, O_RDONLY); -# endif + /* cleanup before heading to an exit */ + freedynamicdata(); + dlb_cleanup(); + l_nhcore_done(); + +#ifdef UNIX + after_opt_showpaths(gd.deferred_showpaths_dir); #else - fd = open(SYSCF_FILE, O_RDONLY, 0); +#ifndef WIN32 +#ifdef CHDIR + chdirx(gd.deferred_showpaths_dir, 0); +#endif +#endif +#if defined(WIN32) || defined(MICRO) || defined(OS2) + nethack_exit(EXIT_SUCCESS); +#else + exit(EXIT_SUCCESS); +#endif #endif - if (fd >= 0) { - /* readable */ - close(fd); - return; - } - raw_printf("Unable to open SYSCF_FILE.\n"); - exit(EXIT_FAILURE); } - -#endif /* SYSCF_FILE */ -#endif /* SYSCF */ +#endif /* !SFCTOOL */ #ifdef DEBUG /* used by debugpline() to decide whether to issue a message @@ -4568,16 +3135,23 @@ debugcore(const char *filename, boolean wildcards) #endif /*DEBUG*/ +#ifndef SFCTOOL #ifdef UNIX #ifndef PATH_MAX #include #endif #endif +#define SYSCONFFILE "system configuration file" + void -reveal_paths(void) +reveal_paths(int code) { +#if defined(SYSCF) + boolean skip_sysopt = FALSE; +#endif const char *fqn, *nodumpreason; + char buf[BUFSZ]; #if defined(SYSCF) || !defined(UNIX) || defined(DLB) const char *filep; @@ -4611,7 +3185,8 @@ reveal_paths(void) #else buf[0] = '\0'; #endif - raw_printf("%s system configuration file%s:", s_suffix(gamename), buf); + raw_printf("%s %s%s:", s_suffix(gamename), + SYSCONFFILE, buf); #ifdef SYSCF_FILE filep = SYSCF_FILE; #else @@ -4620,9 +3195,14 @@ reveal_paths(void) fqn = fqname(filep, SYSCONFPREFIX, 0); if (fqn) { set_configfile_name(fqn); - filep = configfile; + filep = get_configfile(); } raw_printf(" \"%s\"", filep); + if (code == 1) { + raw_printf("NOTE: The %s above is missing or inaccessible!", + SYSCONFFILE); + skip_sysopt = TRUE; + } #else /* !SYSCF */ raw_printf("No system configuration file."); #endif /* ?SYSCF */ @@ -4695,17 +3275,22 @@ reveal_paths(void) /* dumplog */ + fqn = (char *) 0; #ifndef DUMPLOG nodumpreason = "not supported"; #else nodumpreason = "disabled"; #ifdef SYSCF - fqn = sysopt.dumplogfile; + if (!skip_sysopt) { + fqn = sysopt.dumplogfile; + if (!fqn) + nodumpreason = "DUMPLOGFILE is not set in " SYSCONFFILE; + } else { + nodumpreason = SYSCONFFILE " is missing; no DUMPLOGFILE setting"; + } #else /* !SYSCF */ #ifdef DUMPLOG_FILE fqn = DUMPLOG_FILE; -#else - fqn = (char *) 0; #endif #endif /* ?SYSCF */ if (fqn && *fqn) { @@ -4713,22 +3298,27 @@ reveal_paths(void) (void) dump_fmtstr(fqn, buf, FALSE); buf[sizeof buf - sizeof " \"\""] = '\0'; raw_printf(" \"%s\"", buf); - } else -#endif /* ?DUMPLOG */ + } else { raw_printf("No end-of-game disclosure file (%s).", nodumpreason); + } +#endif /* ?DUMPLOG */ +#ifdef SYSCF #ifdef WIN32 - if (sysopt.portable_device_paths) { - const char *pd = get_portable_device(); + if (!skip_sysopt) { + if (sysopt.portable_device_paths) { + const char *pd = get_portable_device(); - /* an empty value for pd indicates that portable_device_paths - got set TRUE in a sysconf file other than the one containing - the executable; disregard it */ - if (strlen(pd) > 0) { - raw_printf("portable_device_paths (set in sysconf):"); - raw_printf(" \"%s\"", pd); + /* an empty value for pd indicates that portable_device_paths + got set TRUE in a sysconf file other than the one containing + the executable; disregard it */ + if (strlen(pd) > 0) { + raw_printf("portable_device_paths (set in sysconf):"); + raw_printf(" \"%s\"", pd); + } } } +#endif #endif /* personal configuration file */ @@ -4749,7 +3339,7 @@ reveal_paths(void) Strcat(buf, "/"); } endp = eos(buf); - copynchars(endp, default_configfile, + copynchars(endp, get_default_configfile(), (int) (sizeof buf - 1 - strlen(buf))); #if defined(__APPLE__) /* UNIX+__APPLE__ => MacOSX aka OSX aka macOS */ if (envp) { @@ -4767,7 +3357,7 @@ reveal_paths(void) if (access(buf, 4) == -1) { /* second alternate failed too, so revert to the original default ("$HOME/.nethackrc") for message */ - copynchars(endp, default_configfile, + copynchars(endp, get_default_configfile(), (int) (sizeof buf - 1 - strlen(buf))); } } @@ -4778,15 +3368,21 @@ reveal_paths(void) #else /* !UNIX */ fqn = (const char *) 0; #ifdef PREFIXES_IN_USE - fqn = fqname(default_configfile, CONFIGPREFIX, 2); + fqn = fqname(get_default_configfile(), CONFIGPREFIX, 2); #endif - raw_printf(" \"%s\"", fqn ? fqn : default_configfile); + raw_printf(" \"%s\"", fqn ? fqn : get_default_configfile()); #endif /* ?UNIX */ raw_print(""); #if defined(WIN32) && !defined(WIN32CON) wait_synch(); #endif +#ifndef DUMPLOG +#ifdef SYSCF + nhUse(skip_sysopt); +#endif + nhUse(nodumpreason); +#endif } /* ---------- BEGIN TRIBUTE ----------- */ @@ -5030,6 +3626,7 @@ Death_quote(char *buf, int bufsz) } /* ---------- END TRIBUTE ----------- */ +#endif /* !SFCTOOL */ #ifdef LIVELOG #define LLOG_SEP "\t" /* livelog field separator, as a string literal */ diff --git a/src/fountain.c b/src/fountain.c index 848adfbfb..979bb0518 100644 --- a/src/fountain.c +++ b/src/fountain.c @@ -315,18 +315,20 @@ drinkfountain(void) dowaterdemon(); break; case 24: { /* Maybe curse some items */ - struct obj *obj; + struct obj *obj, *nextobj; int buc_changed = 0; pline("This water's no good!"); morehungry(rn1(20, 11)); exercise(A_CON, FALSE); /* this is more severe than rndcurse() */ - for (obj = gi.invent; obj; obj = obj->nobj) + for (obj = gi.invent; obj; obj = nextobj) { + nextobj = obj->nobj; if (obj->oclass != COIN_CLASS && !obj->cursed && !rn2(5)) { curse(obj); ++buc_changed; } + } if (buc_changed) update_inventory(); break; @@ -357,6 +359,7 @@ drinkfountain(void) dofindgem(); break; } + FALLTHROUGH; /*FALLTHRU*/ case 28: /* Water Nymph */ dowaternymph(); @@ -484,6 +487,7 @@ dipfountain(struct obj *obj) dofindgem(); break; } + FALLTHROUGH; /*FALLTHRU*/ case 25: /* Water gushes forth */ dogushforth(FALSE); @@ -498,13 +502,14 @@ dipfountain(struct obj *obj) pline("An urge to take a bath overwhelms you."); { long money = money_cnt(gi.invent); - struct obj *otmp; + struct obj *otmp, *nextobj; if (money > 10) { /* Amount to lose. Might get rounded up as fountains don't * pay change... */ money = somegold(money) / 10; - for (otmp = gi.invent; otmp && money > 0; otmp = otmp->nobj) + for (otmp = gi.invent; otmp && money > 0; otmp = nextobj) { + nextobj = otmp->nobj; if (otmp->oclass == COIN_CLASS) { int denomination = objects[otmp->otyp].oc_cost; long coin_loss = @@ -515,6 +520,7 @@ dipfountain(struct obj *obj) if (!otmp->quan) delobj(otmp); } + } You("lost some of your gold in the fountain!"); CLEAR_FOUNTAIN_LOOTED(u.ux, u.uy); exercise(A_WIS, FALSE); @@ -695,6 +701,7 @@ drinksink(void) pline("From the murky drain, a hand reaches up... --oops--"); break; } + FALLTHROUGH; /*FALLTHRU*/ default: You("take a sip of %s %s.", @@ -766,6 +773,7 @@ dipsink(struct obj *obj) try_call = TRUE; break; } + FALLTHROUGH; /* FALLTHRU */ case POT_GAIN_LEVEL: case POT_GAIN_ENERGY: diff --git a/src/getpos.c b/src/getpos.c index 0152fba53..36e9d95ea 100644 --- a/src/getpos.c +++ b/src/getpos.c @@ -465,6 +465,7 @@ gather_locs_interesting(coordxy x, coordxy y, int gloc) case GLOC_VALID: if (getpos_getvalid) return (*getpos_getvalid)(x, y); + FALLTHROUGH; /*FALLTHRU*/ case GLOC_INTERESTING: return (gather_locs_interesting(x, y, GLOC_DOOR) diff --git a/src/glyphs.c b/src/glyphs.c index c13df781a..8b6a77854 100644 --- a/src/glyphs.c +++ b/src/glyphs.c @@ -121,6 +121,7 @@ glyphrep_to_custom_map_entries( if (!glyphid_cache) reslt = 1; /* for debugger use only; no cache available */ + nhUse(reslt); Snprintf(buf, sizeof buf, "%s", op); c_unicode = c_colorval = (char *) 0; @@ -470,6 +471,7 @@ glyphrep(const char *op) if (!glyphid_cache) reslt = 1; /* for debugger use only; no cache available */ + nhUse(reslt); reslt = glyphrep_to_custom_map_entries(op, &glyph); if (reslt) return 1; @@ -675,12 +677,14 @@ shuffle_customizations(void) tmp_customcolor[i] = other_customcolor; tmp_color256idx[i] = other_color256idx; #ifdef ENHANCED_SYMBOLS - tmp_u[i] = (struct unicode_representation *) - alloc(sizeof *tmp_u[i]); - *tmp_u[i] = *other; - if (other->utf8str != NULL) { - tmp_u[i]->utf8str = (uint8 *) - dupstr((const char *) other->utf8str); + if (other) { + tmp_u[i] = (struct unicode_representation *) alloc( + sizeof *tmp_u[i]); + *tmp_u[i] = *other; + if (other->utf8str != NULL) { + tmp_u[i]->utf8str = + (uint8 *) dupstr((const char *) other->utf8str); + } } #endif } else { @@ -906,30 +910,26 @@ parse_id( } else if (glyph_is_body(glyph)) { /* buf2 will hold the distinguishing prefix */ /* buf3 will hold the base name */ - buf2 = ""; /* superfluous */ + buf2 = glyph_is_body_piletop(glyph) + ? "piletop_body_" + : "body_"; buf3 = monsdump[glyph_to_body_corpsenm(glyph)].nm; - if (glyph_is_body_piletop(glyph)) { - buf2 = "piletop_body_"; - } else { - buf2 = "body_"; - } Strcpy(buf[0], "G_"); Strcat(buf[0], buf2); Strcat(buf[0], buf3); } else if (glyph_is_statue(glyph)) { /* buf2 will hold the distinguishing prefix */ /* buf3 will hold the base name */ - buf2 = ""; + buf2 = glyph_is_fem_statue_piletop(glyph) + ? "piletop_statue_of_female_" + : glyph_is_fem_statue(glyph) + ? "statue_of_female_" + : glyph_is_male_statue_piletop(glyph) + ? "piletop_statue_of_male_" + : glyph_is_male_statue(glyph) + ? "statue_of_male_" + : ""; /* shouldn't happen */ buf3 = monsdump[glyph_to_statue_corpsenm(glyph)].nm; - if (glyph_is_fem_statue_piletop(glyph)) { - buf2 = "piletop_statue_of_female_"; - } else if (glyph_is_fem_statue(glyph)) { - buf2 = "statue_of_female_"; - } else if (glyph_is_male_statue_piletop(glyph)) { - buf2 = "piletop_statue_of_male_"; - } else if (glyph_is_male_statue(glyph)) { - buf2 = "statue_of_male_"; - } Strcpy(buf[0], "G_"); Strcat(buf[0], buf2); Strcat(buf[0], buf3); @@ -937,8 +937,6 @@ parse_id( i = glyph_to_obj(glyph); /* buf2 will hold the distinguishing prefix */ /* buf3 will hold the base name */ - buf2 = ""; - buf3 = ""; if (((i > SCR_STINKING_CLOUD) && (i < SCR_MAIL)) || ((i > WAN_LIGHTNING) && (i < GOLD_PIECE))) skip_this_one = TRUE; @@ -958,6 +956,8 @@ parse_id( buf2 = "ring of "; else if (i == LAND_MINE) buf2 = "unset "; + else + buf2 = ""; buf3 = (i == SCR_BLANK_PAPER) ? "blank scroll" : (i == SPE_BLANK_PAPER) ? "blank spellbook" : (i == SLIME_MOLD) ? "slime mold" @@ -1047,7 +1047,6 @@ parse_id( j = glyph - GLYPH_SWALLOW_OFF; cmap = glyph_to_swallow(glyph); mnum = j / ((S_sw_br - S_sw_tl) + 1); - i = cmap - S_sw_tl; Strcpy(buf[3], "swallow "); Strcat(buf[3], monsdump[mnum].nm); Strcat(buf[3], " "); diff --git a/src/hack.c b/src/hack.c index eb4bd1423..7c30870c7 100644 --- a/src/hack.c +++ b/src/hack.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 hack.c $NHDT-Date: 1723410639 2024/08/11 21:10:39 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.452 $ */ +/* NetHack 3.7 hack.c $NHDT-Date: 1736530208 2025/01/10 09:30:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.477 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -9,8 +9,11 @@ /* #define DEBUG */ /* uncomment for debugging */ staticfn boolean could_move_onto_boulder(coordxy, coordxy); +staticfn void dopush(coordxy, coordxy, coordxy, coordxy, struct obj *, + boolean); staticfn void cannot_push_msg(struct obj *, coordxy, coordxy); staticfn int cannot_push(struct obj *, coordxy, coordxy); +staticfn void rock_disappear_msg(struct obj *); staticfn void moverock_done(coordxy, coordxy); staticfn int moverock(void); staticfn int moverock_core(coordxy, coordxy); @@ -35,6 +38,7 @@ staticfn boolean impaired_movement(coordxy *, coordxy *) NONNULLPTRS; staticfn boolean avoid_moving_on_trap(coordxy, coordxy, boolean); staticfn boolean avoid_moving_on_liquid(coordxy, coordxy, boolean); staticfn boolean avoid_running_into_trap_or_liquid(coordxy, coordxy); +staticfn boolean avoid_trap_andor_region(coordxy, coordxy); staticfn boolean move_out_of_bounds(coordxy, coordxy); staticfn boolean carrying_too_much(void); staticfn boolean escape_from_sticky_mon(coordxy, coordxy); @@ -44,9 +48,9 @@ staticfn struct monst *monstinroom(struct permonst *, int) NONNULLARG1; staticfn boolean furniture_present(int, int); staticfn void move_update(boolean); staticfn int pickup_checks(void); -staticfn boolean doorless_door(coordxy, coordxy); staticfn void maybe_wail(void); staticfn boolean water_turbulence(coordxy *, coordxy *); +staticfn int QSORTCALLBACK cmp_weights(const void *, const void *); #define IS_SHOP(x) (svr.rooms[x].rtype >= SHOPBASE) @@ -132,7 +136,8 @@ revive_nasty(coordxy x, coordxy y, const char *msg) return revived; } -#define squeezeablylightinvent() (!gi.invent || inv_weight() <= -850) +#define squeezeablylightinvent() (!gi.invent \ + || inv_weight() <= (WT_SQUEEZABLE_INV * -1)) /* can hero move onto a spot containing one or more boulders? used for m and travel and during boulder push failure */ @@ -148,8 +153,8 @@ could_move_onto_boulder(coordxy sx, coordxy sy) /* can if a giant, unless doing so allows hero to pass into a diagonal squeeze at the same time */ if (throws_rocks(gy.youmonst.data)) - return (!u.dx || !u.dy || !(IS_ROCK(levl[u.ux][sy].typ) - && IS_ROCK(levl[sx][u.uy].typ))); + return (!u.dx || !u.dy || !(IS_OBSTRUCTED(levl[u.ux][sy].typ) + && IS_OBSTRUCTED(levl[sx][u.uy].typ))); /* can if tiny (implies carrying very little else couldn't move at all) */ if (verysmall(gy.youmonst.data)) return TRUE; @@ -157,6 +162,87 @@ could_move_onto_boulder(coordxy sx, coordxy sy) return squeezeablylightinvent(); } +staticfn void +dopush( + coordxy sx, + coordxy sy, + coordxy rx, + coordxy ry, + struct obj *otmp, + boolean costly) +{ + struct monst *shkp; + + { + const char *what; + boolean givemesg, easypush; + /* give boulder pushing feedback if this is a different + boulder than the last one pushed or if it's been at + least 2 turns since we last pushed this boulder; + unlike with Norep(), intervening messages don't cause + it to repeat, only doing something else in the meantime */ + if (otmp->o_id != gb.bldrpush_oid) { + gb.bldrpushtime = svm.moves + 1L; + gb.bldrpush_oid = otmp->o_id; + } + givemesg = (svm.moves > gb.bldrpushtime + 2L + || svm.moves < gb.bldrpushtime); + what = givemesg ? the(xname(otmp)) : 0; + if (!u.usteed) { + easypush = throws_rocks(gy.youmonst.data); + if (givemesg) + pline("With %s effort you move %s.", + easypush ? "little" : "great", what); + if (!easypush) + exercise(A_STR, TRUE); + } else { + if (givemesg) + pline("%s moves %s.", YMonnam(u.usteed), what); + } + gb.bldrpushtime = svm.moves; + } + + /* Move the boulder *after* the message. */ + if (glyph_is_invisible(levl[rx][ry].glyph)) + unmap_object(rx, ry); + otmp->next_boulder = 0; + movobj(otmp, rx, ry); /* does newsym(rx,ry) */ + if (Blind) { + feel_location(rx, ry); + feel_location(sx, sy); + } else { + newsym(sx, sy); + } + /* maybe adjust bill if boulder was pushed across shop boundary; + normally otmp->unpaid would not apply because otmp isn't in + hero's inventory, but addtobill() sets it and subfrombill() + clears it */ + if (costly && !costly_spot(rx, ry)) { + /* pushing from inside shop to its boundary (or free spot) */ + addtobill(otmp, FALSE, FALSE, FALSE); + } else if (!costly && costly_spot(rx, ry) && otmp->unpaid + && ((shkp = shop_keeper(*in_rooms(rx, ry, SHOPBASE))) + != 0) + && onshopbill(otmp, shkp, TRUE)) { + /* this can happen if hero pushes boulder from farther inside + shop into shop's free spot (which will add it to the bill), + then teleports or Passes_walls to doorway (without exiting + the shop), and then pushes the boulder from the free spot + back into the shop; it's contingent upon the shopkeeper not + "muttering an incantation" to fracture the boulder while it + is unpaid at the free spot */ + subfrombill(otmp, shkp); + } else if (otmp->unpaid + && (shkp = find_objowner(otmp, sx, sy)) != 0 + && !strchr(in_rooms(rx, ry, SHOPBASE), + ESHK(shkp)->shoproom)) { + /* once the boulder is fully out of the shop, so that it's + * impossible to change your mind and push it back in without + * leaving and triggering Kops, switch it to stolen_value */ + stolen_value(otmp, sx, sy, TRUE, FALSE); + } +} + staticfn void cannot_push_msg(struct obj *otmp, coordxy sx, coordxy sy) { @@ -225,6 +311,18 @@ cannot_push(struct obj *otmp, coordxy sx, coordxy sy) } } +staticfn void +rock_disappear_msg(struct obj *otmp) +{ + if (u.usteed) + pline("%s pushes %s and suddenly it disappears!", + YMonnam(u.usteed), the(xname(otmp))); + else + You("push %s and suddenly it disappears!", + the(xname(otmp))); + +} + staticfn void moverock_done(coordxy sx, coordxy sy) { @@ -252,8 +350,7 @@ moverock_core(coordxy sx, coordxy sy) coordxy rx, ry; struct obj *otmp; struct trap *ttmp; - struct monst *mtmp, *shkp; - const char *what; + struct monst *mtmp; boolean costly, firstboulder = TRUE; while ((otmp = sobj_at(BOULDER, sx, sy)) != 0) { @@ -332,7 +429,7 @@ moverock_core(coordxy sx, coordxy sy) pline("You're too small to push that %s.", xname(otmp)); return cannot_push(otmp, sx, sy); } - if (isok(rx, ry) && !IS_ROCK(levl[rx][ry].typ) + if (isok(rx, ry) && !IS_OBSTRUCTED(levl[rx][ry].typ) && levl[rx][ry].typ != IRONBARS && (!IS_DOOR(levl[rx][ry].typ) || !(u.dx && u.dy) || doorless_door(rx, ry)) && !sobj_at(BOULDER, rx, ry)) { @@ -472,16 +569,14 @@ moverock_core(coordxy sx, coordxy sy) that if in single-level branch (Knox) or in endgame */ newlev = random_teleport_level(); /* if trap doesn't work, skip "disappears" message */ - if (newlev == depth(&u.uz)) - goto dopush; + if (newlev == depth(&u.uz)) { + dopush(sx, sy, rx, ry, otmp, costly); + continue; + } + FALLTHROUGH; /*FALLTHRU*/ case TELEP_TRAP: - if (u.usteed) - pline("%s pushes %s and suddenly it disappears!", - YMonnam(u.usteed), the(xname(otmp))); - else - You("push %s and suddenly it disappears!", - the(xname(otmp))); + rock_disappear_msg(otmp); otmp->next_boulder = 0; /* reset before moving it */ if (ttmp->ttyp == TELEP_TRAP) { (void) rloco(otmp); @@ -513,75 +608,7 @@ moverock_core(coordxy sx, coordxy sy) remove_object(otmp); place_object(otmp, otmp->ox, otmp->oy); } - - { - boolean givemesg, easypush; - dopush: - /* give boulder pushing feedback if this is a different - boulder than the last one pushed or if it's been at - least 2 turns since we last pushed this boulder; - unlike with Norep(), intervening messages don't cause - it to repeat, only doing something else in the meantime */ - if (otmp->o_id != gb.bldrpush_oid) { - gb.bldrpushtime = svm.moves + 1L; - gb.bldrpush_oid = otmp->o_id; - } - givemesg = (svm.moves > gb.bldrpushtime + 2L - || svm.moves < gb.bldrpushtime); - what = givemesg ? the(xname(otmp)) : 0; - if (!u.usteed) { - easypush = throws_rocks(gy.youmonst.data); - if (givemesg) - pline("With %s effort you move %s.", - easypush ? "little" : "great", what); - if (!easypush) - exercise(A_STR, TRUE); - } else { - if (givemesg) - pline("%s moves %s.", YMonnam(u.usteed), what); - } - gb.bldrpushtime = svm.moves; - } - - /* Move the boulder *after* the message. */ - if (glyph_is_invisible(levl[rx][ry].glyph)) - unmap_object(rx, ry); - otmp->next_boulder = 0; - movobj(otmp, rx, ry); /* does newsym(rx,ry) */ - if (Blind) { - feel_location(rx, ry); - feel_location(sx, sy); - } else { - newsym(sx, sy); - } - /* maybe adjust bill if boulder was pushed across shop boundary; - normally otmp->unpaid would not apply because otmp isn't in - hero's inventory, but addtobill() sets it and subfrombill() - clears it */ - if (costly && !costly_spot(rx, ry)) { - /* pushing from inside shop to its boundary (or free spot) */ - addtobill(otmp, FALSE, FALSE, FALSE); - } else if (!costly && costly_spot(rx, ry) && otmp->unpaid - && ((shkp = shop_keeper(*in_rooms(rx, ry, SHOPBASE))) - != 0) - && onshopbill(otmp, shkp, TRUE)) { - /* this can happen if hero pushes boulder from farther inside - shop into shop's free spot (which will add it to the bill), - then teleports or Passes_walls to doorway (without exiting - the shop), and then pushes the boulder from the free spot - back into the shop; it's contingent upon the shopkeeper not - "muttering an incantation" to fracture the boulder while it - is unpaid at the free spot */ - subfrombill(otmp, shkp); - } else if (otmp->unpaid - && (shkp = find_objowner(otmp, sx, sy)) != 0 - && !strchr(in_rooms(rx, ry, SHOPBASE), - ESHK(shkp)->shoproom)) { - /* once the boulder is fully out of the shop, so that it's - * impossible to change your mind and push it back in without - * leaving and triggering Kops, switch it to stolen_value */ - stolen_value(otmp, sx, sy, TRUE, FALSE); - } + dopush(sx, sy, rx, ry, otmp, costly); } else { cannot_push_msg(otmp, sx, sy); return cannot_push(otmp, sx, sy); @@ -608,7 +635,7 @@ still_chewing(coordxy x, coordxy y) sizeof (struct dig_info)); if (!boulder - && ((IS_ROCK(lev->typ) && !may_dig(x, y)) + && ((IS_OBSTRUCTED(lev->typ) && !may_dig(x, y)) /* may_dig() checks W_NONDIGGABLE but doesn't handle iron bars */ || (lev->typ == IRONBARS && (lev->wall_info & W_NONDIGGABLE)))) { You("hurt your teeth on the %s.", @@ -637,7 +664,7 @@ still_chewing(coordxy x, coordxy y) assign_level(&svc.context.digging.level, &u.uz); /* solid rock takes more work & time to dig through */ svc.context.digging.effort = - (IS_ROCK(lev->typ) && !IS_TREE(lev->typ) ? 30 : 60) + u.udaminc; + (IS_OBSTRUCTED(lev->typ) && !IS_TREE(lev->typ) ? 30 : 60) + u.udaminc; You("start chewing %s %s.", (boulder || IS_TREE(lev->typ) || lev->typ == IRONBARS) ? "on a" @@ -646,7 +673,7 @@ still_chewing(coordxy x, coordxy y) ? "boulder" : IS_TREE(lev->typ) ? "tree" - : IS_ROCK(lev->typ) + : IS_OBSTRUCTED(lev->typ) ? "rock" : (lev->typ == IRONBARS) ? "bar" @@ -661,7 +688,7 @@ still_chewing(coordxy x, coordxy y) ? "boulder" : IS_TREE(lev->typ) ? "tree" - : IS_ROCK(lev->typ) + : IS_OBSTRUCTED(lev->typ) ? "rock" : (lev->typ == IRONBARS) ? "bars" @@ -677,7 +704,7 @@ still_chewing(coordxy x, coordxy y) "ate for the first time, by chewing through %s", boulder ? "a boulder" : IS_TREE(lev->typ) ? "a tree" - : IS_ROCK(lev->typ) ? "rock" + : IS_OBSTRUCTED(lev->typ) ? "rock" : (lev->typ == IRONBARS) ? "iron bars" : "a door"); u.uhunger += rnd(20); @@ -693,7 +720,7 @@ still_chewing(coordxy x, coordxy y) * * [perhaps use does_block() below (from vision.c)] */ - if (IS_ROCK(lev->typ) || closed_door(x, y) + if (IS_OBSTRUCTED(lev->typ) || closed_door(x, y) || sobj_at(BOULDER, x, y)) { block_point(x, y); /* delobj will unblock the point */ /* reset dig state */ @@ -763,7 +790,7 @@ still_chewing(coordxy x, coordxy y) lev->typ = CORR; } - unblock_point(x, y); /* vision */ + recalc_block_point(x, y); /* vision */ newsym(x, y); if (digtxt) You1(digtxt); /* after newsym */ @@ -892,7 +919,7 @@ boolean bad_rock(struct permonst *mdat, coordxy x, coordxy y) { return (boolean) ((Sokoban && sobj_at(BOULDER, x, y)) - || (IS_ROCK(levl[x][y].typ) + || (IS_OBSTRUCTED(levl[x][y].typ) && (!tunnels(mdat) || needspick(mdat) || !may_dig(x, y)) && !(passes_walls(mdat) && may_passwall(x, y)))); @@ -938,7 +965,7 @@ invocation_pos(coordxy x, coordxy y) && x == svi.inv_pos.x && y == svi.inv_pos.y); } -/* return TRUE if (dx,dy) is an OK place to move; +/* return TRUE if (ux+dx,uy+dy) is an OK place to move; mode is one of DO_MOVE, TEST_MOVE, TEST_TRAV, or TEST_TRAP */ boolean test_move( @@ -948,14 +975,20 @@ test_move( { coordxy x = ux + dx; coordxy y = uy + dy; - struct rm *tmpr = &levl[x][y]; + struct rm *tmpr; struct rm *ust; svc.context.door_opened = FALSE; + + if (!isok(x, y)) + return FALSE; + + tmpr = &levl[x][y]; + /* * Check for physical obstacles. First, the place we are going. */ - if (IS_ROCK(tmpr->typ) || tmpr->typ == IRONBARS) { + if (IS_OBSTRUCTED(tmpr->typ) || tmpr->typ == IRONBARS) { if (Blind && mode == DO_MOVE) feel_location(x, y); if (Passes_walls && may_passwall(x, y)) { @@ -1043,9 +1076,19 @@ test_move( " but can't squeeze your possessions through."); if (flags.autoopen && !svc.context.run && !Confusion && !Stunned && !Fumbling) { - svc.context.door_opened - = svc.context.move - = (doopen_indir(x, y) == ECMD_TIME ? 1 : 0); + int tmp = doopen_indir(x, y); + /* if 'autounlock' includes Kick, we might have a + kick at the door queued up after doopen_indir() */ + struct _cmd_queue *cq = cmdq_peek(CQ_CANNED); + + if (tmp == ECMD_OK && cq && cq->typ == CMDQ_EXTCMD + && cq->ec_entry == ext_func_tab_from_func(dokick)) + /* door hasn't been opened, but fake it so that + canned kick will be executed as next command */ + svc.context.door_opened = TRUE; + else + svc.context.door_opened = !closed_door(x, y); + svc.context.move = (ux != u.ux || uy != u.uy); } else if (x == ux || y == uy) { if (Blind || Stunned || ACURR(A_DEX) < 10 || Fumbling) { @@ -1685,22 +1728,25 @@ notice_all_mons(boolean reset) struct monst **arr = NULL; int j, i = 0, cnt = 0; - for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) + continue; if (canspotmon(mtmp)) cnt++; else if (reset) mtmp->mspotted = FALSE; - + } if (!cnt) return; - arr = (struct monst **) alloc(cnt * sizeof(struct monst *)); - + arr = (struct monst **) alloc(cnt * sizeof (struct monst *)); for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) + continue; if (!canspotmon(mtmp)) mtmp->mspotted = FALSE; - else if (!DEADMONSTER(mtmp) && i < cnt) + else if (i < cnt) arr[i++] = mtmp; } @@ -1752,9 +1798,14 @@ u_locomotion(const char *def) { boolean capitalize = (*def == highc(*def)); + /* regular locomotion() takes a monster type rather than a specific + monster, so can't tell whether it is operating on hero; + its is_flyer() and is_floater() tests wouldn't work on hero except + when hero is polymorphed and not wearing an amulet of flying + or boots/ring/spell of levitation */ return Levitation ? (capitalize ? "Float" : "float") - : Flying ? (capitalize ? "Fly" : "fly") - : locomotion(gy.youmonst.data, def); + : Flying ? (capitalize ? "Fly" : "fly") + : locomotion(gy.youmonst.data, def); } /* Return a simplified floor solid/liquid state based on hero's state */ @@ -1814,8 +1865,7 @@ staticfn boolean swim_move_danger(coordxy x, coordxy y) { schar newtyp = u_simple_floortyp(x, y); - boolean liquid_wall = IS_WATERWALL(newtyp) - || newtyp == LAVAWALL; + boolean liquid_wall = IS_WATERWALL(newtyp) || newtyp == LAVAWALL; if (Underwater && (is_pool(x,y) || IS_WATERWALL(newtyp))) return FALSE; @@ -1912,9 +1962,11 @@ domove_attackmon_at( && bad_rock(gy.youmonst.data, x, u.uy0)))) && goodpos(u.ux0, u.uy0, mtmp, GP_ALLOW_U)); /* if not displacing, try to attack; note that it might evade; - also, we don't attack tame when _safepet_ */ - if (!*displaceu && do_attack(mtmp)) - return TRUE; + also, we don't attack tame or peaceful when safemon() */ + if (!*displaceu) { + if (do_attack(mtmp)) + return TRUE; + } } return FALSE; } @@ -2023,7 +2075,9 @@ domove_fight_web(coordxy x, coordxy y) /* maybe swap places with a pet? returns TRUE if swapped places */ staticfn boolean -domove_swap_with_pet(struct monst *mtmp, coordxy x, coordxy y) +domove_swap_with_pet( + struct monst *mtmp, + coordxy x, coordxy y) { struct trap *trap; /* if it turns out we can't actually move */ @@ -2208,6 +2262,7 @@ domove_fight_empty(coordxy x, coordxy y) map_object(boulder, TRUE); newsym(x, y); glyph = glyph_at(x, y); /* might have just changed */ + nhUse(glyph); if (boulder) { Strcpy(buf, ansimpleoname(boulder)); @@ -2430,6 +2485,79 @@ avoid_running_into_trap_or_liquid(coordxy x, coordxy y) return FALSE; } +/* if paranoid_confirm:Trap is enabled, check whether the next step forward + needs player confirmation due to visible region or discovered trap; + result: True => stop moving, False => proceed */ +staticfn boolean +avoid_trap_andor_region(coordxy x, coordxy y) +{ + char qbuf[QBUFSZ]; + NhRegion *newreg, *oldreg; + struct trap *trap = NULL; + + /* treat entering a visible gas cloud region like entering a trap; + there could be a known trap as well as a region at the target spot; + if so, ask about entring the region first; even though this could + lead to two consecutive confirmation prompts, the situation seems + to be too uncommon to warrant a separate case with combined + trap+region confirmation */ + if (ParanoidTrap && !Blind && !Stunned && !Confusion && !Hallucination + /* skip if player used 'm' prefix or is moving recklessly */ + && (!svc.context.nopick || svc.context.run) + /* check for region(s) */ + && (newreg = visible_region_at(x, y)) != 0 + && ((oldreg = visible_region_at(u.ux, u.uy)) == 0 + /* if moving from one region into another, only ask for + confirmation if the one potentially being entered inflicts + damage (poison gas) and the one being exited doesn't (vapor) */ + || (reg_damg(newreg) > 0 && reg_damg(oldreg) == 0)) + /* check whether attempted move will be viable */ + && test_move(u.ux, u.uy, u.dx, u.dy, TEST_MOVE) + /* we don't override confirmation for poison resistance since the + region also hinders hero's vision even if/when no damage is done */ + ) { + Snprintf(qbuf, sizeof qbuf, "%s into that %s cloud?", + u_locomotion("step"), + (reg_damg(newreg) > 0) ? "poison gas" : "vapor"); + if (!paranoid_query(ParanoidConfirm, upstart(qbuf))) { + nomul(0); + svc.context.move = 0; + return TRUE; + } + } + + /* maybe ask player for confirmation before walking into known trap */ + if (ParanoidTrap && !Stunned && !Confusion + /* skip if player used 'm' prefix or is moving recklessly */ + && (!svc.context.nopick || svc.context.run) + /* check for discovered trap */ + && (trap = t_at(x, y)) != 0 && trap->tseen + /* check whether attempted move will be viable */ + && test_move(u.ux, u.uy, u.dx, u.dy, TEST_MOVE) + /* override confirmation if the trap is harmless to the hero */ + && (immune_to_trap(&gy.youmonst, trap->ttyp) != TRAP_CLEARLY_IMMUNE + /* Hallucination: all traps still show as ^, but the + hero can't tell what they are, so treat as dangerous */ + || Hallucination)) { + int traptype = (Hallucination ? rnd(TRAPNUM - 1) : (int) trap->ttyp); + boolean into = into_vs_onto(traptype); + + Snprintf(qbuf, sizeof qbuf, "Really %s %s that %s?", + u_locomotion("step"), into ? "into" : "onto", + defsyms[trap_to_defsym(traptype)].explanation); + /* handled like paranoid_confirm:pray; when paranoid_confirm:trap + isn't set, don't ask at all but if it is set (checked above), + ask via y/n if parnoid_confirm:confirm isn't also set or via + yes/no if it is */ + if (!paranoid_query(ParanoidConfirm, qbuf)) { + nomul(0); + svc.context.move = 0; + return TRUE; + } + } + return FALSE; +} + /* trying to move out-of-bounds? */ staticfn boolean move_out_of_bounds(coordxy x, coordxy y) @@ -2511,27 +2639,29 @@ escape_from_sticky_mon(coordxy x, coordxy y) * guaranteed escape. */ switch (rn2(!u.ustuck->mcanmove ? 8 : 40)) { - case 0: - case 1: - case 2: - pull_free: - mtmp = u.ustuck; - set_ustuck((struct monst *) 0); - You("pull free from %s.", y_monnam(mtmp)); - break; case 3: if (!u.ustuck->mcanmove) { /* it's free to move on next turn */ u.ustuck->mfrozen = 1; u.ustuck->msleeping = 0; } + FALLTHROUGH; /*FALLTHRU*/ default: - if (u.ustuck->mtame && !Conflict && !u.ustuck->mconf) - goto pull_free; - You("cannot escape from %s!", y_monnam(u.ustuck)); - nomul(0); - return TRUE; + if (Conflict || u.ustuck->mconf || !u.ustuck->mtame) { + You("cannot escape from %s!", y_monnam(u.ustuck)); + nomul(0); + return TRUE; + } + FALLTHROUGH; + /*FALLTHRU*/ + case 0: + case 1: + case 2: + mtmp = u.ustuck; + set_ustuck((struct monst *) 0); + You("pull free from %s.", y_monnam(mtmp)); + break; } } } @@ -2560,9 +2690,7 @@ domove_core(void) { struct monst *mtmp; struct rm *tmpr; - NhRegion *newreg, *oldreg; coordxy x, y; - struct trap *trap = NULL; int glyph; coordxy chainx = 0, chainy = 0, ballx = 0, bally = 0; /* ball&chain new positions */ @@ -2648,132 +2776,63 @@ domove_core(void) return; } - if (domove_fight_ironbars(x, y)) - return; + if (!displaceu) { - if (domove_fight_web(x, y)) - return; + if (domove_fight_ironbars(x, y)) + return; - if (domove_fight_empty(x, y)) - return; + if (domove_fight_web(x, y)) + return; - (void) unmap_invisible(x, y); - /* not attacking an animal, so we try to move */ - if ((u.dx || u.dy) && u.usteed && stucksteed(FALSE)) { - nomul(0); - return; - } + if (domove_fight_empty(x, y)) + return; - if (u_rooted()) - return; - - /* treat entering a visible gas cloud region like entering a trap; - there could be a known trap as well as a region at the target spot; - if so, ask about entring the region first; even though this could - lead to two consecutive confirmation prompts, the situation seems to - be too uncommon to warrant a separate case with combined trap+region - confirmation */ - if (ParanoidTrap && !Blind && !Stunned && !Confusion && !Hallucination - /* skip if player used 'm' prefix or is moving recklessly */ - && (!svc.context.nopick || svc.context.run) - /* check for region(s) */ - && (newreg = visible_region_at(x, y)) != 0 - && ((oldreg = visible_region_at(u.ux, u.uy)) == 0 - /* if moving from one region into another, only ask for - confirmation if the one potentially being entered inflicts - damage (poison gas) and the one being exited doesn't (vapor) */ - || (reg_damg(newreg) > 0 && reg_damg(oldreg) == 0)) - /* check whether attempted move will be viable */ - && test_move(u.ux, u.uy, u.dx, u.dy, TEST_MOVE) - /* we don't override confirmation for poison resistance since the - region also hinders hero's vision even if/when no damage is done */ - ) { - char qbuf[QBUFSZ]; - - Snprintf(qbuf, sizeof qbuf, "%s into that %s cloud?", - locomotion(gy.youmonst.data, "step"), - (reg_damg(newreg) > 0) ? "poison gas" : "vapor"); - if (!paranoid_query(ParanoidConfirm, upstart(qbuf))) { + (void) unmap_invisible(x, y); + /* not attacking an animal, so we try to move */ + if ((u.dx || u.dy) && u.usteed && stucksteed(FALSE)) { nomul(0); - svc.context.move = 0; return; } - } - /* maybe ask player for confirmation before walking into known traps */ - if (ParanoidTrap && !Stunned && !Confusion - /* skip if player used 'm' prefix or is moving recklessly */ - && (!svc.context.nopick || svc.context.run) - /* check for discovered trap */ - && (trap = t_at(x, y)) != 0 && trap->tseen - /* check whether attempted move will be viable */ - /* - * FIXME: - * this will result in "Really step into trap?" if there is a - * peaceful or tame monster already there. - */ - && test_move(u.ux, u.uy, u.dx, u.dy, TEST_MOVE) - /* override confirmation if the trap is harmless to the hero */ - && (immune_to_trap(&gy.youmonst, trap->ttyp) != TRAP_CLEARLY_IMMUNE - /* Hallucination: all traps still show as ^, but the - hero can't tell what they are, so treat as dangerous */ - || Hallucination)) { - char qbuf[QBUFSZ]; - int traptype = (Hallucination ? rnd(TRAPNUM - 1) : (int) trap->ttyp); - boolean into = FALSE; /* "onto" the trap vs "into" */ - switch (traptype) { - case BEAR_TRAP: - case PIT: - case SPIKED_PIT: - case HOLE: - case TELEP_TRAP: - case LEVEL_TELEP: - case MAGIC_PORTAL: - case WEB: - into = TRUE; - break; + if (u_rooted()) + return; + + /* handling for paranoid_confirm:Trap which doubles as + paranoid_confirm:Region */ + if (ParanoidTrap) { + if (avoid_trap_andor_region(x, y)) + return; } - Snprintf(qbuf, sizeof qbuf, "Really %s %s that %s?", - locomotion(gy.youmonst.data, "step"), - into ? "into" : "onto", - defsyms[trap_to_defsym(traptype)].explanation); - /* handled like paranoid_confirm:pray; when paranoid_confirm:trap - isn't set, don't ask at all but if it is set (checked above), - ask via y/n if parnoid_confirm:confirm isn't also set or via - yes/no if it is */ - if (!paranoid_query(ParanoidConfirm, qbuf)) { - nomul(0); - svc.context.move = 0; + + if (u.utrap) { /* when u.utrap is True, displaceu is False */ + boolean moved = trapmove(x, y, (struct trap *) NULL); + + if (!u.utrap) { + disp.botl = TRUE; + reset_utrap(TRUE); /* might resume levitation or flight */ + } + /* might not have escaped, or did escape but remain in the same + spot */ + if (!moved) + return; + } + + if (!test_move(u.ux, u.uy, x - u.ux, y - u.uy, DO_MOVE)) { + if (!svc.context.door_opened) { + svc.context.move = 0; + nomul(0); + } return; } - } - if (u.utrap) { - boolean moved = trapmove(x, y, trap); - - if (!u.utrap) { - disp.botl = TRUE; - reset_utrap(TRUE); /* might resume levitation or flight */ - } - /* might not have escaped, or did escape but remain in same spot */ - if (!moved) - return; - } - - if (!test_move(u.ux, u.uy, x - u.ux, y - u.uy, DO_MOVE)) { - if (!svc.context.door_opened) { + /* Is it dangerous to swim in water or lava? */ + if (swim_move_danger(x, y)) { svc.context.move = 0; nomul(0); + return; } - return; - } - /* Is it dangerous to swim in water or lava? */ - if (swim_move_danger(x, y)) { - svc.context.move = 0; - nomul(0); - return; - } + } /* !dislacedu */ /* Move ball and chain. */ if (Punished) @@ -2854,7 +2913,7 @@ domove_core(void) reset_occupations(); if (svc.context.run) { if (svc.context.run < 8) - if (IS_DOOR(tmpr->typ) || IS_ROCK(tmpr->typ) + if (IS_DOOR(tmpr->typ) || IS_OBSTRUCTED(tmpr->typ) || IS_FURNITURE(tmpr->typ)) nomul(0); } @@ -3009,7 +3068,7 @@ void switch_terrain(void) { struct rm *lev = &levl[u.ux][u.uy]; - boolean blocklev = (IS_ROCK(lev->typ) || closed_door(u.ux, u.uy) + boolean blocklev = (IS_OBSTRUCTED(lev->typ) || closed_door(u.ux, u.uy) || IS_WATERWALL(lev->typ) || lev->typ == LAVAWALL), was_levitating = !!Levitation, was_flying = !!Flying; @@ -3546,6 +3605,7 @@ check_special_room(boolean newlev) } case TEMPLE: intemple(roomno + ROOMOFFSET); + FALLTHROUGH; /*FALLTHRU*/ default: msg_given = (rt == TEMPLE || rt >= SHOPBASE); @@ -3777,7 +3837,7 @@ lookaround(void) } /* more uninteresting terrain */ - if (IS_ROCK(levl[x][y].typ) || levl[x][y].typ == ROOM + if (IS_OBSTRUCTED(levl[x][y].typ) || levl[x][y].typ == ROOM || IS_AIR(levl[x][y].typ) || levl[x][y].typ == ICE) { continue; } else if (closed_door(x, y) || (mtmp && is_door_mappear(mtmp))) { @@ -3880,7 +3940,7 @@ lookaround(void) } /* check for a doorway which lacks its door (NODOOR or BROKEN) */ -staticfn boolean +boolean doorless_door(coordxy x, coordxy y) { struct rm *lev_p = &levl[x][y]; @@ -4071,7 +4131,7 @@ saving_grace(int dmg) to phrase this though; classifying it as a spoiler will hide it 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-grave", + "survived one-shot death via saving-grace", dmg, u.uhp, u.uhpmax); /* note: this could reduce dmg to 0 if u.uhpmax==1 */ @@ -4153,7 +4213,8 @@ weight_cap(void) functions enough in that situation to enhance carrying capacity */ BLevitation &= ~I_SPECIAL; - carrcap = 25 * (ACURRSTR + ACURR(A_CON)) + 50; + carrcap = (WT_WEIGHTCAP_STRCON * (ACURRSTR + ACURR(A_CON))) + + WT_WEIGHTCAP_SPARE; if (Upolyd) { /* consistent with can_carry() in mon.c */ if (gy.youmonst.data->mlet == S_NYMPH) @@ -4174,9 +4235,9 @@ weight_cap(void) carrcap = MAX_CARR_CAP; if (!Flying) { if (EWounded_legs & LEFT_SIDE) - carrcap -= 100; + carrcap -= WT_WOUNDEDLEG_REDUCT; if (EWounded_legs & RIGHT_SIDE) - carrcap -= 100; + carrcap -= WT_WOUNDEDLEG_REDUCT; } } @@ -4252,6 +4313,89 @@ check_capacity(const char *str) return 0; } +struct weight_table_entry { + unsigned wtyp; /* 1 = monst, 2 = obj */ + const char *nm; /* 0-6 = weight in ascii; 7 - end = name */ + int wt, idx; + boolean unique; +}; + +static struct weight_table_entry *weightlist; + +void +dump_weights(void) +{ + int i, cnt = 0, nmwidth = 49, mcount = NUMMONS, ocount = NUM_OBJECTS; + char nmbuf[BUFSZ], nmbufbase[BUFSZ]; + + weightlist = (struct weight_table_entry *) + alloc(sizeof (struct weight_table_entry) * (mcount + ocount)); + decl_globals_init(); + init_objects(); + for (i = 0; i < mcount; ++i) { + if (i != PM_LONG_WORM_TAIL) { + boolean cm; + + weightlist[cnt].wt = (int) mons[i].cwt; + weightlist[cnt].idx = i; + weightlist[cnt].wtyp = 1; + weightlist[cnt].unique = ((mons[i].geno & G_UNIQ) != 0); + Snprintf(nmbuf, sizeof nmbuf, "%07u", weightlist[cnt].wt); + cm = CapitalMon(mons[i].pmnames[NEUTRAL]); + Snprintf(&nmbuf[7], sizeof nmbuf - 7, "%s%s", "the body of ", + (cm) ? the(mons[i].pmnames[NEUTRAL]) + : weightlist[cnt].unique ? mons[i].pmnames[NEUTRAL] + : an(mons[i].pmnames[NEUTRAL])); + weightlist[cnt].nm = dupstr(nmbuf); + cnt++; + } + } + for (i = 0; i < ocount; ++i) { + const char *oc_name = (i == SLIME_MOLD) ? "slime mold" + : obj_descr[i].oc_name; + int wt = (int) objects[i].oc_weight; + + if (wt && oc_name) { + weightlist[cnt].idx = i; + weightlist[cnt].wt = wt; + weightlist[cnt].wtyp = 2; + weightlist[cnt].unique = (objects[i].oc_unique != 0); + objects[i].oc_name_known = 1; + Strcpy(nmbufbase, simple_typename(i)); + Snprintf(nmbuf, sizeof nmbuf, "%07u%s", wt, + (weightlist[cnt].unique) ? the(nmbufbase) + : an(nmbufbase)); + weightlist[cnt].nm = dupstr(nmbuf); + cnt++; + } + } + qsort((genericptr_t) weightlist, cnt, + sizeof (struct weight_table_entry), cmp_weights); + raw_printf("int all_weights[] = {"); + for (i = 0; i < cnt; ++i) { + if (weightlist[i].nm) { + raw_printf(" %7u%s /* %*s */", weightlist[i].wt, + (i == cnt - 1) ? " " : ",", -nmwidth, + &weightlist[i].nm[7]); + free((genericptr_t) weightlist[i].nm), weightlist[i].nm = 0; + } + } + raw_print("};"); + raw_print(""); + free((genericptr_t) weightlist); + freedynamicdata(); +} + +staticfn int QSORTCALLBACK +cmp_weights(const void *p1, const void *p2) +{ + struct weight_table_entry *i1 = (struct weight_table_entry *) p1, + *i2 = (struct weight_table_entry *) p2; + + /* return (i1->wt - i2->wt); */ + return strcmp(i1->nm, i2->nm); +} + int inv_cnt(boolean incl_gold) { @@ -4290,6 +4434,7 @@ spot_checks(coordxy x, coordxy y, schar old_typ) switch (old_typ) { case DRAWBRIDGE_UP: db_ice_now = ((levl[x][y].drawbridgemask & DB_UNDER) == DB_ICE); + FALLTHROUGH; /*FALLTHRU*/ case ICE: if ((new_typ != old_typ) diff --git a/src/hacklib.c b/src/hacklib.c index 8b78daff7..209982751 100644 --- a/src/hacklib.c +++ b/src/hacklib.c @@ -45,9 +45,9 @@ const char * ordin (int) char * sitoa (int) int sgn (int) - int distmin (int, int, int, int) - int dist2 (int, int, int, int) - boolean online2 (int, int) + int distmin (coordxy, coordxy, coordxy, coordxy) + int dist2 (coordxy, coordxy, coordxy, coordxy) + boolean online2 (coordxy, coordxy) int strncmpi (const char *, const char *, int) char * strstri (const char *, const char *) boolean fuzzymatch (const char *, const char *, @@ -936,4 +936,72 @@ case_insensitive_comp(const char *s1, const char *s2) return u1 - u2; } +boolean +copy_bytes(int ifd, int ofd) +{ + char buf[BUFSIZ]; + int nfrom, nto; + + do { + nto = 0; + nfrom = read(ifd, buf, BUFSIZ); + /* read can return -1 */ + if (nfrom >= 0 && nfrom <= BUFSIZ) + nto = write(ofd, buf, nfrom); + if (nto != nfrom || nfrom < 0) + return FALSE; + } while (nfrom == BUFSIZ); + return TRUE; +} +#define MAX_D 5 +struct datamodel_information { + int sz[MAX_D]; + const char *datamodel; + const char *dmplatform; +}; + +static struct datamodel_information dm[] = { + { { (int) sizeof(short), (int) sizeof(int), (int) sizeof(long), + (int) sizeof(long long), (int) sizeof(genericptr_t) }, + "", "" }, + { { 2, 4, 4, 8, 4 }, "ILP32LL64", "x86 32-bit" }, /* Windows or Unix */ + { { 2, 4, 4, 8, 8 }, "IL32LLP64", "Windows x64 64-bit" }, + { { 2, 4, 8, 8, 8 }, "I32LP64", "Unix 64-bit"}, + { { 2, 8, 8, 8, 8 }, "ILP64", "Unix ILP64"}, /* HAL, SPARC64 */ +}; + +const char * +datamodel(int retidx) +{ + int i, j, matchcount; + static const char *unknown = "Unknown"; + + for (i = 1; i < SIZE(dm); ++i) { + matchcount = 0; + for (j = 0; j < MAX_D; ++j) { + if (dm[0].sz[j] == dm[i].sz[j]) + ++matchcount; + } + if (matchcount == MAX_D) + return (retidx == 0) ? dm[i].datamodel : dm[i].dmplatform; + } + return unknown; +} + +const char * +what_datamodel_is_this(int retidx, int szshort, int szint, int szlong, int szll, + int szptr) +{ + int i; + static const char *unknown = "Unknown"; + + for (i = 1; i < SIZE(dm); ++i) { + if (szshort == dm[i].sz[0] && szint == dm[i].sz[1] + && szlong == dm[i].sz[2] && szll == dm[i].sz[3] + && szptr == dm[i].sz[4]) + return (retidx == 0) ? dm[i].datamodel : dm[i].dmplatform; + } + return unknown; +} +#undef MAX_D /*hacklib.c*/ diff --git a/src/insight.c b/src/insight.c index 08fd80b6b..9f0f71059 100644 --- a/src/insight.c +++ b/src/insight.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 insight.c $NHDT-Date: 1724094296 2024/08/19 19:04:56 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.115 $ */ +/* NetHack 3.7 insight.c $NHDT-Date: 1737384766 2025/01/20 06:52:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.128 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -156,7 +156,11 @@ enlght_line( /* format increased chance to hit or damage or defense (Protection) */ staticfn char * -enlght_combatinc(const char *inctyp, int incamt, int final, char *outbuf) +enlght_combatinc( + const char *inctyp, /* "to hit" or "damage" or "defense" */ + int incamt, /* amount of increment (negative if decrement) */ + int final, /* ENL_{GAMEINPROGRESS,GAMEOVERALIVE,GAMEOVERDEAD} */ + char *outbuf) { const char *modif, *bonus; boolean invrt; @@ -405,9 +409,13 @@ enlightenment( } if (!flags.bones) { - you_have_X("disabled loading of bones levels"); + /* mention not saving bones iff hero just died */ + Sprintf(buf, "disabled loading%s of bones levels", + (final == ENL_GAMEOVERDEAD) ? " and storing" : ""); + you_have_X(buf); } else if (!u.uroleplay.numbones) { - you_have_never("encountered a bones level"); + enl_msg(You_, "haven't encountered", "didn't encounter", + " any bones levels", ""); } else { Sprintf(buf, "encountered %ld bones level%s", u.uroleplay.numbones, plur(u.uroleplay.numbones)); @@ -1073,7 +1081,8 @@ status_enlightenment(int mode, int final) || strcmp(MGIVENNAME(u.ustuck), "it") != 0)) Strcpy(heldmon, "an unseen creature"); } - if (u.uswallow) { /* implies u.ustuck is non-Null */ + if (u.uswallow) { + assert(u.ustuck != NULL); /* implied by u.uswallow */ Snprintf(buf, sizeof buf, "%s by %s", digests(u.ustuck->data) ? "swallowed" : "engulfed", heldmon); @@ -1533,6 +1542,9 @@ attributes_enlightenment( /*** Vision and senses ***/ if ((HBlinded || EBlinded) && BBlinded) /* blind w/ blindness blocked */ you_can("see", from_what(-BLINDED)); /* Eyes of the Overworld */ + if (Blnd_resist && !Blind) /* skip if no eyes or blindfolded */ + you_are("not subject to light-induced blindness", + from_what(BLND_RES)); if (See_invisible) { if (!Blind) enl_msg(You_, "see", "saw", " invisible", from_what(SEE_INVIS)); @@ -1634,7 +1646,7 @@ attributes_enlightenment( if (Stealth) { you_are("stealthy", from_what(STEALTH)); } else if (BStealth && (HStealth || EStealth)) { - Sprintf(buf, " steathy%s", + Sprintf(buf, " stealthy%s", (BStealth == FROMOUTSIDE) ? " if not mounted" : ""); enl_msg(You_, "would be", "would have been", buf, ""); } @@ -1778,6 +1790,23 @@ attributes_enlightenment( enlght_halfdmg(HALF_SPDAM, final); if (Half_gas_damage) enl_msg(You_, "take", "took", " reduced poison gas damage", ""); + if (spellid(0) > NO_SPELL) { /* skip if no spells are known yet */ + /* greatly simplified edition of percent_success(spell.c)--may need + to be suppressed if oversimplification leads to player confusion */ + char cast_adj[QBUFSZ]; + boolean suit = uarm && is_metallic(uarm), + robe = uarmc && uarmc->otyp == ROBE; + + *cast_adj = '\0'; + if (suit) /* omit "wearing" to shorten the text */ + Sprintf(cast_adj, " impaired by metallic armor%s", + robe ? ", mitigated by your robe" : ""); + else if (robe) + Strcpy(cast_adj, " enhanced by wearing a robe"); + + if (*cast_adj) + enl_msg("Your spell casting ", "is", "was", cast_adj, ""); + } /* polymorph and other shape change */ if (Protection_from_shape_changers) you_are("protected from shape changers", @@ -1965,6 +1994,8 @@ attributes_enlightenment( switch (u.umortality) { case 0: impossible("dead without dying?"); + FALLTHROUGH; + /* FALLTHRU */ case 1: break; /* just "are dead" */ default: @@ -2549,7 +2580,7 @@ show_gamelog(int final) continue; if (!eventcnt++) putstr(win, 0, " Turn"); - Sprintf(buf, "%5ld: %s", llmsg->turn, llmsg->text); + Snprintf(buf, sizeof buf, "%5ld: %s", llmsg->turn, llmsg->text); putstr(win, 0, buf); } /* since start of game is logged as a major event, 'eventcnt' should @@ -2621,6 +2652,7 @@ vanqsort_cmp( res = uniq2 - uniq1; break; } /* else both unique or neither unique */ + FALLTHROUGH; /*FALLTHRU*/ case VANQ_ALPHA_MIX: name1 = mons[indx1].pmnames[NEUTRAL]; @@ -3014,12 +3046,14 @@ list_genocided(char defquery, boolean ask) ngone = num_gone(mvflags, mindx); /* genocided or extinct species list */ - if (ngenocided != 0 || nextinct != 0) { + if (ngone > 0) { Sprintf(buf, "Do you want a list of %sspecies%s%s?", (nextinct && !ngenocided) ? "extinct " : "", (ngenocided) ? " genocided" : "", (nextinct && ngenocided) ? " and extinct" : ""); - c = ask ? yn_function(buf, ynaqchars, defquery, TRUE) : defquery; + c = ask ? yn_function(buf, (ngone > 1) ? "ynaq" : "ynq\033a", + defquery, TRUE) + : defquery; if (c == 'q') done_stopprint++; if (c == 'y' || c == 'a') { @@ -3369,7 +3403,7 @@ mstatusline(struct monst *mtmp) /* avoid "Status of the invisible newt ..., invisible" */ /* and unlike a normal mon_nam, use "saddled" even if it has a name */ - Strcpy(monnambuf, x_monnam(mtmp, ARTICLE_THE, (char *) 0, + Strcpy(monnambuf, x_monnam(mtmp, ARTICLE_YOUR, (char *) 0, (SUPPRESS_IT | SUPPRESS_INVISIBLE), FALSE)); pline("Status of %s (%s, %s): Level %d HP %d(%d) AC %d%s.", diff --git a/src/invent.c b/src/invent.c index 665e2bd1d..ef5a78dea 100644 --- a/src/invent.c +++ b/src/invent.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 invent.c $NHDT-Date: 1724094299 2024/08/19 19:04:59 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.516 $ */ +/* NetHack 3.7 invent.c $NHDT-Date: 1737384766 2025/01/20 06:52:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.531 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1230,6 +1230,12 @@ hold_another_object( dropped, avoid perminv update when temporarily adding it */ obj = addinv_core0(obj, (struct obj *) 0, FALSE); goto drop_it; + } else if (obj->otyp == CORPSE + && !u_safe_from_fatal_corpse(obj, st_all) + && obj->wishedfor) { + obj->wishedfor = 0; + obj = addinv_core0(obj, (struct obj *) 0, FALSE); + goto drop_it; } else { long oquan = obj->quan; int prev_encumbr = near_capacity(); /* before addinv() */ @@ -1367,6 +1373,11 @@ freeinv_core(struct obj *obj) } else if (obj->otyp == FIGURINE && obj->timed) { (void) stop_timer(FIG_TRANSFORM, obj_to_any(obj)); } + + if (obj == svc.context.tin.tin) { + svc.context.tin.tin = (struct obj *) 0; + svc.context.tin.o_id = 0; + } } /* remove an object from the hero's inventory */ @@ -1474,6 +1485,18 @@ carrying(int type) return otmp; } +/* return inventory object of type that will petrify on touch */ +struct obj * +carrying_stoning_corpse(void) +{ + struct obj *otmp; + + for (otmp = gi.invent; otmp; otmp = otmp->nobj) + if (otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm])) + break; + return otmp; +} + /* Fictional and not-so-fictional currencies. * http://concord.wikia.com/wiki/List_of_Fictional_Currencies */ @@ -2224,15 +2247,17 @@ ggetobj(const char *word, int (*fn)(OBJ_P), int mx, return 0; if (strchr(buf, 'i')) { char ailets[1+26+26+1+5+1]; /* $ + a-z + A-Z + # + slop + \0 */ - struct obj *otmp; + struct obj *otmp, *nextobj; /* applicable inventory letters; if empty, show entire invent */ ailets[0] = '\0'; if (ofilter) - for (otmp = gi.invent; otmp; otmp = otmp->nobj) + for (otmp = gi.invent; otmp; otmp = nextobj) { + nextobj = otmp->nobj; /* strchr() check: limit overflow items to one '#' */ if ((*ofilter)(otmp) && !strchr(ailets, otmp->invlet)) (void) strkitten(ailets, otmp->invlet); + } if (display_inventory(ailets, TRUE) == '\033') return 0; } else @@ -2443,6 +2468,7 @@ askchain( switch (sym) { case 'a': allflag = 1; + FALLTHROUGH; /*FALLTHRU*/ case 'y': tmp = (*fn)(otmp); @@ -2461,10 +2487,13 @@ askchain( cnt += tmp; if (--mx == 0) goto ret; + FALLTHROUGH; /*FALLTHRU*/ case 'n': if (nodot) dud++; + FALLTHROUGH; + /*FALLTHRU*/ default: break; case 'q': @@ -2513,7 +2542,7 @@ fully_identify_obj(struct obj *otmp) { makeknown(otmp->otyp); if (otmp->oartifact) - discover_artifact((coordxy) otmp->oartifact); + discover_artifact((xint16) otmp->oartifact); otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1; set_cknown_lknown(otmp); /* set otmp->{cknown,lknown} if applicable */ if (otmp->otyp == EGG && otmp->corpsenm != NON_PM) @@ -2775,7 +2804,7 @@ xprname( char suffix[80]; /* plenty of room for count and hallucinatory currency */ int sfxlen, txtlen; /* signed int for %*s formatting */ const char *fmt; - boolean use_invlet = (flags.invlet_constant + boolean use_invlet = (flags.invlet_constant && obj != NULL && let != CONTAINED_SYM && let != HANDS_SYM); long savequan = 0L; @@ -2790,8 +2819,10 @@ xprname( * > Then the object is contained and doesn't have an inventory letter. */ fmt = "%c - %.*s%s"; - if (!txt) + if (!txt) { + assert(obj != NULL); txt = doname(obj); + } txtlen = (int) strlen(txt); if (cost != 0L || let == '*') { @@ -2955,6 +2986,7 @@ itemactions_pushkeys(struct obj *otmp, int act) switch (act) { default: impossible("Unknown item action"); + break; case IA_NONE: break; case IA_UNWIELD: @@ -3491,7 +3523,7 @@ dispinv_with_action( boolean use_inuse_ordering, /* affects sortloot() and header labels */ const char *alt_label) /* alternate value for in-use "Accessories" */ { - struct obj *otmp; + struct obj *otmp, *nextobj; const char *save_accessories = 0; char c, save_sortloot = 0; unsigned len = lets ? (unsigned) strlen(lets) : 0U; @@ -3517,9 +3549,11 @@ dispinv_with_action( iflags.force_invmenu = save_force_invmenu; if (c && c != '\033') { - for (otmp = gi.invent; otmp; otmp = otmp->nobj) + for (otmp = gi.invent; otmp; otmp = nextobj) { + nextobj = otmp->nobj; if (otmp->invlet == c) return itemactions(otmp); + } } return ECMD_OK; } @@ -3600,11 +3634,10 @@ display_pickinv( Loot *sortedinvent, *srtinv; int8_t prevorderclass; boolean (*filter)(struct obj *) = (boolean (*)(OBJ_P)) 0; - boolean wizid = (wizard && iflags.override_ID), gotsomething = FALSE; int clr = NO_COLOR, menu_behavior = MENU_BEHAVE_STANDARD; boolean show_gold = TRUE, inuse_only = FALSE, skipped_gold = FALSE, - doing_perm_invent = FALSE, save_flags_sortpack = flags.sortpack, + doing_perm_invent = FALSE, save_flags_sortpack, usextra = (xtra_choice && allowxtra); if (lets && !*lets) @@ -3632,8 +3665,8 @@ display_pickinv( win = WIN_INVEN; menu_behavior = MENU_BEHAVE_PERMINV; prepare_perminvent(win); - show_gold = ((wri_info.fromcore.invmode & InvShowGold) != 0); - inuse_only = ((wri_info.fromcore.invmode & InvInUse) != 0); + show_gold = ((wri_info.fromcore.invmode & (enum inv_modes) InvShowGold) != 0); + inuse_only = ((wri_info.fromcore.invmode & (enum inv_modes) InvInUse) != 0); doing_perm_invent = TRUE; } /* @@ -4199,7 +4232,7 @@ dounpaid( } win = create_nhwindow(NHW_MENU); - cost = totcost = 0; + totcost = 0L; num_so_far = 0; /* count of # printed so far */ if (!flags.invlet_constant) reassign(); @@ -4911,6 +4944,8 @@ mergable( if (obj->cursed != otmp->cursed || obj->blessed != otmp->blessed) return FALSE; + if ((obj->how_lost & ~LOSTOVERRIDEMASK) != 0) + 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; * caller who wants that behavior should use 'nomerge' instead */ @@ -6122,6 +6157,12 @@ sync_perminvent(void) || in_perm_invent_toggled) { wri = ctrl_nhwindow(WIN_INVEN, request_settings, &wri_info); if (wri != 0) { + if ((wri->tocore.tocore_flags & (too_early)) != 0) { + /* don't be too noisy about this as it's really + * a startup timing issue. Just set a marker. */ + iflags.perm_invent_pending = TRUE; + return; + } if ((wri->tocore.tocore_flags & (too_small | prohibited)) != 0) { /* sizes aren't good enough */ diff --git a/src/light.c b/src/light.c index 5ab517177..574665fa9 100644 --- a/src/light.c +++ b/src/light.c @@ -13,8 +13,8 @@ * Light sources are "things" that have a physical position and range. * They have a type, which gives us information about them. Currently * they are only attached to objects and monsters. Note well: the - * polymorphed-player handling assumes that both gy.youmonst.m_id and - * gy.youmonst.mx will always remain 0. + * polymorphed-player handling assumes that gy.youmonst.m_id will + * always remain 1 and gy.youmonst.mx will always remain 0. * * Light sources, like timers, either follow game play (RANGE_GLOBAL) or * stay on a level (RANGE_LEVEL). Light sources are unique by their @@ -38,15 +38,18 @@ */ /* flags */ -#define LSF_SHOW 0x1 /* display the light source */ -#define LSF_NEEDS_FIXUP 0x2 /* need oid fixup */ +#define LSF_SHOW 0x1 /* display the light source */ +#define LSF_NEEDS_FIXUP 0x2 /* need oid fixup */ +#define LSF_IS_PROBLEMATIC 0x4 /* impossible situation encountered */ +#ifndef SFCTOOL staticfn light_source *new_light_core(coordxy, coordxy, int, int, anything *) NONNULLPTRS; staticfn void delete_ls(light_source *); staticfn void discard_flashes(void); staticfn void write_ls(NHFILE *, light_source *); staticfn int maybe_write_ls(NHFILE *, int, boolean); +staticfn unsigned whereis_mon(struct monst *, unsigned); /* imported from vision.c, for small circles */ extern const coordxy circle_data[]; @@ -374,7 +377,7 @@ find_mid(unsigned nid, unsigned fmflags) { struct monst *mtmp; - if (!nid) + if ((fmflags & FM_YOU) && nid == 1) return &gy.youmonst; if (fmflags & FM_FMON) for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) @@ -391,6 +394,28 @@ find_mid(unsigned nid, unsigned fmflags) return (struct monst *) 0; } +staticfn unsigned +whereis_mon(struct monst *mon, unsigned fmflags) +{ + struct monst *mtmp; + + if ((fmflags & FM_YOU) && mon == &gy.youmonst) + return FM_YOU; + if (fmflags & FM_FMON) + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (mtmp == mon) + return FM_FMON; + if (fmflags & FM_MIGRATE) + for (mtmp = gm.migrating_mons; mtmp; mtmp = mtmp->nmon) + if (mtmp == mon) + return FM_MIGRATE; + if (fmflags & FM_MYDOGS) + for (mtmp = gm.mydogs; mtmp; mtmp = mtmp->nmon) + if (mtmp == mon) + return FM_MYDOGS; + return 0; +} + /* Save all light sources of the given range. */ void save_light_sources(NHFILE *nhfp, int range) @@ -405,11 +430,9 @@ save_light_sources(NHFILE *nhfp, int range) discard_flashes(); gv.vision_full_recalc = 0; - if (perform_bwrite(nhfp)) { + if (update_file(nhfp)) { count = maybe_write_ls(nhfp, range, FALSE); - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &count, sizeof count); - } + Sfo_int(nhfp, &count, "lightsource-count"); actual = maybe_write_ls(nhfp, range, TRUE); if (actual != count) panic("counted %d light sources, wrote %d! [range=%d]", count, @@ -446,6 +469,7 @@ save_light_sources(NHFILE *nhfp, int range) } } } +#endif /* !SFCTOOL */ /* * Pull in the structures from disk, but don't recalculate the object @@ -458,18 +482,18 @@ restore_light_sources(NHFILE *nhfp) light_source *ls; /* restore elements */ - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) &count, sizeof count); + Sfi_int(nhfp, &count, "lightsource-count"); while (count-- > 0) { ls = (light_source *) alloc(sizeof(light_source)); - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) ls, sizeof(light_source)); + Sfi_ls_t(nhfp, ls, "lightsource"); ls->next = gl.light_base; gl.light_base = ls; } } +#ifndef SFCTOOL + DISABLE_WARNING_FORMAT_NONLITERAL /* to support '#stats' wizard-mode command */ @@ -615,8 +639,7 @@ write_ls(NHFILE *nhfp, light_source *ls) if (ls->type == LS_OBJECT || ls->type == LS_MONSTER) { if (ls->flags & LSF_NEEDS_FIXUP) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) ls, sizeof(light_source)); + Sfo_ls_t(nhfp, ls, "lightsource"); } else { /* replace object pointer with id for write, then put back */ arg_save = ls->id; @@ -624,22 +647,54 @@ write_ls(NHFILE *nhfp, light_source *ls) otmp = ls->id.a_obj; ls->id = cg.zeroany; ls->id.a_uint = otmp->o_id; - if (find_oid((unsigned) ls->id.a_uint) != otmp) + if (find_oid((unsigned) ls->id.a_uint) != otmp) { impossible("write_ls: can't find obj #%u!", ls->id.a_uint); + ls->flags |= LSF_IS_PROBLEMATIC; + } } else { /* ls->type == LS_MONSTER */ + unsigned monloc = 0; + mtmp = (struct monst *) ls->id.a_monst; - ls->id = cg.zeroany; - ls->id.a_uint = mtmp->m_id; - if (find_mid((unsigned) ls->id.a_uint, FM_EVERYWHERE) != mtmp) - impossible("write_ls: can't find mon #%u!", - ls->id.a_uint); + + /* The monster pointer has been stashed in the light source + * for a while and while there is code meant to clean-up the + * light source aspects if a monster goes away, there have + * been some reports of light source issues, such as when + * going to the planes. + * + * Verify that the stashed monst pointer is still present + * in one of the monster chains before pulling subfield + * values such as m_id from it, to avoid any attempt to + * pull random m_id value from (now) freed memory. + * + * find_mid() disregards a DEADMONSTER, but whereis_mon() + * does not. */ + + if ((monloc = whereis_mon(mtmp, FM_EVERYWHERE)) != 0) { + ls->id = cg.zeroany; + ls->id.a_uint = mtmp->m_id; + if (find_mid((unsigned) ls->id.a_uint, monloc) != mtmp) { + impossible("write_ls: can't find mon%s #%u!", + DEADMONSTER(mtmp) ? " because it's dead" + : "", + ls->id.a_uint); + ls->flags |= LSF_IS_PROBLEMATIC; + } + } else { + impossible( + "write_ls: stashed monst ptr not in any chain"); + ls->flags |= LSF_IS_PROBLEMATIC; + } + } + if (ls->flags & LSF_IS_PROBLEMATIC) { + /* TODO: cleanup this ls, or skip writing it */ } ls->flags |= LSF_NEEDS_FIXUP; - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) ls, sizeof(light_source)); + Sfo_ls_t(nhfp, ls, "lightsource"); ls->id = arg_save; ls->flags &= ~LSF_NEEDS_FIXUP; + ls->flags &= ~LSF_IS_PROBLEMATIC; } } else { impossible("write_ls: bad type (%d)", ls->type); @@ -918,11 +973,10 @@ 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 mon_is_local - /*light.c*/ diff --git a/src/lock.c b/src/lock.c index fa8fb22f8..2e8851754 100644 --- a/src/lock.c +++ b/src/lock.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 lock.c $NHDT-Date: 1718745135 2024/06/18 21:12:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.137 $ */ +/* NetHack 3.7 lock.c $NHDT-Date: 1741793439 2025/03/12 07:30:39 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.145 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -105,11 +105,12 @@ picklock(void) : (gx.xlock.door->doormask & D_TRAPPED) != 0) && gx.xlock.magic_key) { gx.xlock.chance += 20; /* less effort needed next time */ - /* unfortunately we don't have a 'tknown' flag to record - "known to be trapped" so declining to disarm and then - retrying lock manipulation will find it all over again */ - if (y_n("You find a trap! Do you want to try to disarm it?") - == 'y') { + if (!gx.xlock.door) { + if (!gx.xlock.box->tknown) + You("find a trap!"); + gx.xlock.box->tknown = 1; + } + if (y_n("Do you want to try to disarm it?") == 'y') { const char *what; boolean alreadyunlocked; @@ -120,6 +121,7 @@ picklock(void) alreadyunlocked = !(gx.xlock.door->doormask & D_LOCKED); } else { gx.xlock.box->otrapped = 0; + gx.xlock.box->tknown = 0; what = (gx.xlock.box->otyp == CHEST) ? "chest" : "box"; alreadyunlocked = !gx.xlock.box->olocked; } @@ -468,7 +470,8 @@ pick_lock( if (autounlock && (flags.autounlock & AUTOUNLOCK_UNTRAP) != 0 && could_untrap(FALSE, TRUE) - && (c = ynq(safe_qbuf(qbuf, "Check ", " for a trap?", + && (c = otmp->tknown ? (otmp->otrapped ? 'y' : 'n') + : ynq(safe_qbuf(qbuf, "Check ", " for a trap?", otmp, yname, ysimple_name, "this"))) != 'n') { if (c == 'q') @@ -697,7 +700,7 @@ doforce(void) return ECMD_OK; } if (!can_reach_floor(TRUE)) { - cant_reach_floor(u.ux, u.uy, FALSE, TRUE); + cant_reach_floor(u.ux, u.uy, FALSE, TRUE, FALSE); return ECMD_OK; } @@ -878,13 +881,15 @@ doopen_indir(coordxy x, coordxy y) && (unlocktool = autokey(TRUE)) != 0) { res = pick_lock(unlocktool, cc.x, cc.y, (struct obj *) 0) ? ECMD_TIME : ECMD_OK; - } else if (!u.usteed - && (flags.autounlock & AUTOUNLOCK_KICK) != 0 + } else if ((flags.autounlock & AUTOUNLOCK_KICK) != 0 + && !u.usteed /* kicking is different when mounted */ && ynq("Kick it?") == 'y') { cmdq_add_ec(CQ_CANNED, dokick); cmdq_add_dir(CQ_CANNED, sgn(cc.x - u.ux), sgn(cc.y - u.uy), 0); - res = ECMD_TIME; + /* this was 'ECMD_TIME', but time shouldn't elapse until + the canned kick takes place */ + res = ECMD_OK; } } return res; @@ -907,7 +912,7 @@ doopen_indir(coordxy x, coordxy y) } else door->doormask = D_ISOPEN; feel_newsym(cc.x, cc.y); /* the hero knows she opened it */ - unblock_point(cc.x, cc.y); /* vision: new see through there */ + recalc_block_point(cc.x, cc.y); /* vision: new see through there */ } else { exercise(A_STR, TRUE); set_msg_xy(cc.x, cc.y); @@ -1228,7 +1233,7 @@ doorlock(struct obj *otmp, coordxy x, coordxy y) } sawit = cansee(x, y); door->doormask = D_BROKEN; - unblock_point(x, y); + recalc_block_point(x, y); seeit = cansee(x, y); newsym(x, y); if (flags.verbose) { diff --git a/src/mail.c b/src/mail.c index c1c62dc78..e6a5c2935 100644 --- a/src/mail.c +++ b/src/mail.c @@ -201,7 +201,7 @@ md_start(coord *startp) startp->y = row; startp->x = gv.viz_rmin[row]; - } else if (enexto(&testcc, (coordxy) gv.viz_rmin[row], row, + } else if (enexto(&testcc, gv.viz_rmin[row], row, (struct permonst *) 0) && !cansee(testcc.x, testcc.y) && couldsee(testcc.x, testcc.y)) { @@ -216,7 +216,7 @@ md_start(coord *startp) startp->y = row; startp->x = gv.viz_rmax[row]; - } else if (enexto(&testcc, (coordxy) gv.viz_rmax[row], row, + } else if (enexto(&testcc, gv.viz_rmax[row], row, (struct permonst *) 0) && !cansee(testcc.x, testcc.y) && couldsee(testcc.x, testcc.y)) { diff --git a/src/makemon.c b/src/makemon.c index a712b6a31..c95fe8439 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -5,8 +5,6 @@ #include "hack.h" -#include - /* this assumes that a human quest leader or nemesis is an archetype of the corresponding role; that isn't so for some roles (tourist for instance) but is for the priests and monks we use it for... */ @@ -18,6 +16,9 @@ staticfn boolean uncommon(int); staticfn int align_shift(struct permonst *); staticfn int temperature_shift(struct permonst *); staticfn boolean mk_gen_ok(int, unsigned, unsigned); +staticfn int QSORTCALLBACK cmp_init_mongen_order(const void *, const void *); +staticfn void check_mongen_order(void); +staticfn void init_mongen_order(void); staticfn boolean wrong_elem_type(struct permonst *); staticfn void m_initgrp(struct monst *, coordxy, coordxy, int, mmflags_nht); staticfn void m_initthrow(struct monst *, int, int); @@ -129,7 +130,7 @@ m_initgrp( * are peaceful and some are not, the result will just be a * smaller group. */ - if (enexto(&mm, mm.x, mm.y, mtmp->data)) { + if (enexto_gpflags(&mm, mm.x, mm.y, mtmp->data, mmflags)) { mon = makemon(mtmp->data, mm.x, mm.y, (mmflags | MM_NOGRP)); if (mon) { mon->mpeaceful = FALSE; @@ -329,20 +330,24 @@ m_initweap(struct monst *mtmp) case S_ANGEL: if (humanoid(ptr)) { - /* create minion stuff; can't use mongets */ - otmp = mksobj(LONG_SWORD, FALSE, FALSE); + /* create minion stuff; bypass mongets */ + int typ = rn2(3) ? LONG_SWORD : SILVER_MACE; + const char *nam = (typ == LONG_SWORD) ? "Sunsword" : "Demonbane"; - /* maybe make it special */ + otmp = mksobj(typ, FALSE, FALSE); + /* maybe promote weapon to an artifact */ if ((!rn2(20) || is_lord(ptr)) && sgn(mtmp->isminion ? EMIN(mtmp)->min_align : ptr->maligntyp) == A_LAWFUL) - /* [note: this used to have a 50:50 chance for Sunsword or - Demonbane, but Demonbane has been changed into a mace] */ - otmp = oname(otmp, artiname(ART_SUNSWORD), - ONAME_RANDOM); /* randomly created */ + otmp = oname(otmp, nam, ONAME_RANDOM); /* randomly created */ + /* enhance the weapon */ bless(otmp); otmp->oerodeproof = TRUE; + /* make long sword be +0 to +3, mace be +3 to +6 to compensate + for being significantly weaker against large opponents */ otmp->spe = rn2(4); + if (typ == SILVER_MACE) + otmp->spe += 3; (void) mpickobj(mtmp, otmp); otmp = mksobj(!rn2(4) || is_lord(ptr) ? SHIELD_OF_REFLECTION @@ -517,6 +522,7 @@ m_initweap(struct monst *mtmp) */ if (!is_demon(ptr)) break; + FALLTHROUGH; /*FALLTHRU*/ default: /* @@ -700,12 +706,15 @@ m_initinv(struct monst *mtmp) /* MAJOR fall through ... */ case 0: (void) mongets(mtmp, WAN_MAGIC_MISSILE); + FALLTHROUGH; /*FALLTHRU*/ case 1: (void) mongets(mtmp, POT_EXTRA_HEALING); + FALLTHROUGH; /*FALLTHRU*/ case 2: (void) mongets(mtmp, POT_HEALING); + FALLTHROUGH; /*FALLTHRU*/ case 3: (void) mongets(mtmp, WAN_STRIKING); @@ -1433,9 +1442,9 @@ makemon( if (!rn2(100) && is_domestic(ptr) && can_saddle(mtmp) && !which_armor(mtmp, W_SADDLE)) { - struct obj *otmp = mksobj(SADDLE, TRUE, FALSE); - - put_saddle_on_mon(otmp, mtmp); + /* NULL obj arg means put_saddle_on_mon() + * will create the saddle itself */ + put_saddle_on_mon((struct obj *) 0, mtmp); } } else { @@ -1476,8 +1485,10 @@ makemon( } if (what) { set_msg_xy(mtmp->mx, mtmp->my); - Norep("%s%s appears%s%c", what, + Norep("%s%s %s%s%c", what, exclaim ? " suddenly" : "", + /* 'what' might be "gold pieces" so need plural verb */ + vtense(what, "appear"), next2u(x, y) ? " next to you" : (distu(x, y) <= (BOLT_LIM * BOLT_LIM)) ? " close by" : "", @@ -1736,6 +1747,117 @@ mk_gen_ok(int mndx, unsigned mvflagsmask, unsigned genomask) return TRUE; } +/* monsters in order by mlet & difficulty for mkclass() */ +static int mongen_order[NUMMONS]; +static xint8 mclass_maxf[MAXMCLASSES]; +static boolean mongen_order_init = FALSE; + +staticfn int QSORTCALLBACK +cmp_init_mongen_order(const void *p1, const void *p2) +{ + int i1 = *((int *) p1), i2 = *((int *) p2); + #if 0 + /* This will cause these to be moved last in the mlet sort order */ + int offset1 = ((mons[i1].geno & (G_NOGEN | G_UNIQ)) != 0) ? 99 : 0, + offset2 = ((mons[i2].geno & (G_NOGEN | G_UNIQ)) != 0) ? 99 : 0; +#else + int offset1 = 0, offset2 = 0; +#endif + + /* incorporate the mlet into the sort values for comparison */ + int difficulty1 = + ((mons[i1].difficulty + offset1) | ((int) mons[i1].mlet << 8)), + difficulty2 = + ((mons[i2].difficulty + offset2) | ((int) mons[i2].mlet << 8)); + return difficulty1 - difficulty2; +} + +/* check that monsters are in correct difficulty order for mkclass() */ +staticfn void +check_mongen_order(void) +{ + int i, diff = 0; + char mlet = '\0'; + for (i = LOW_PM; i < SPECIAL_PM; i++) { + if (i != mongen_order[i]) { + debugpline2("changed:%s=>%s", mons[i].pmnames[NEUTRAL], mons[mongen_order[i]].pmnames[NEUTRAL]); + } + + if (mlet == mons[mongen_order[i]].mlet) { + if (mons[mongen_order[i]].difficulty < diff) + debugpline1("%s", mons[mongen_order[i]].pmnames[NEUTRAL]); + diff = mons[mongen_order[i]].difficulty; + } + if (!mlet || mlet != mons[mongen_order[i]].mlet) { + mlet = mons[mongen_order[i]].mlet; + diff = 0; + } + } +} + +/* initialize monster order for mkclass */ +staticfn void +init_mongen_order(void) +{ + int i, mlet; + + if (mongen_order_init) + return; + + mongen_order_init = TRUE; + for (i = LOW_PM; i < NUMMONS; i++) { + mongen_order[i] = i; + mlet = mons[i].mlet; + if ((xint8) (mons[i].geno & G_FREQ) > mclass_maxf[mlet]) + mclass_maxf[mlet] = (xint8) (mons[i].geno & G_FREQ); + } +#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) + check_mongen_order(); +#endif + qsort((genericptr_t) mongen_order, SPECIAL_PM, sizeof(int), cmp_init_mongen_order); +#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) + check_mongen_order(); +#endif +} + +#define MONSi(i) (mongen_order[i]) + +extern struct enum_dump monsdump[]; /* allmain.c */ + +void +dump_mongen(void) +{ + char mlet, prev_mlet = 0; + int i, nmwidth = 27, special; + char nmbuf[80]; + + monst_globals_init(); + init_mongen_order(); + raw_printf("int mongen_order[] = {"); + for (i = LOW_PM; i < SPECIAL_PM; ++i) { + special = (mons[MONSi(i)].geno & (G_NOGEN | G_UNIQ)); + mlet = def_monsyms[(int) mons[MONSi(i)].mlet].sym; + if (prev_mlet && prev_mlet != mlet) + raw_print(""); + Snprintf(nmbuf, sizeof nmbuf, "PM_%s%s", + monsdump[MONSi(i)].nm, + (i == SPECIAL_PM - 1) ? "" : ","); + raw_printf(" %*s /* %c seq=%3d, idx=%3d, sym='%c', diff=%2d, freq=%2d[%d] %s */", + -nmwidth, nmbuf, (i == MONSi(i)) ? ' ' : '.', i, MONSi(i), + mlet, (int) mons[MONSi(i)].difficulty, + (int) (mons[MONSi(i)].geno & G_FREQ), + (int) mclass_maxf[(int) mons[MONSi(i)].mlet], + (special == (G_NOGEN | G_UNIQ)) ? "(G_NOGEN | G_UNIQ)" + : (special == G_NOGEN) ? "(G_NOGEN)" + : (special == G_UNIQ) ? "(G_UNIQ)" + : ""); + prev_mlet = mlet; + } + raw_print("};"); + raw_print(""); + freedynamicdata(); +} + /* Make one of the multiple types of a given monster class. The second parameter specifies a special casing bit mask to allow the normal genesis masks to be deactivated. @@ -1755,6 +1877,7 @@ mkclass_aligned(char class, int spc, /* special mons[].geno handling */ int k, nums[SPECIAL_PM + 1]; /* +1: insurance for final return value */ int maxmlev, gehennom = Inhell != 0; unsigned mv_mask, gn_mask; + boolean zero_freq_for_entire_class; (void) memset((genericptr_t) nums, 0, sizeof nums); maxmlev = level_difficulty() >> 1; @@ -1762,6 +1885,11 @@ mkclass_aligned(char class, int spc, /* special mons[].geno handling */ impossible("mkclass called with bad class!"); return (struct permonst *) 0; } + + init_mongen_order(); + /* the following must come after init_mongen_order() */ + zero_freq_for_entire_class = (mclass_maxf[(int) class] == 0); + /* Assumption #1: monsters of a given class are contiguous in the * mons[] array. Player monsters and quest denizens * are an exception; mkclass() won't pick them. @@ -1769,7 +1897,7 @@ mkclass_aligned(char class, int spc, /* special mons[].geno handling */ * regular monsters from the exceptions. */ for (first = LOW_PM; first < SPECIAL_PM; first++) - if (mons[first].mlet == class) + if (mons[MONSi(first)].mlet == class) break; if (first == SPECIAL_PM) { impossible("mkclass found no class %d monsters", class); @@ -1786,9 +1914,9 @@ mkclass_aligned(char class, int spc, /* special mons[].geno handling */ /* Assumption #2: monsters of a given class are presented in ascending * order of strength. */ - for (last = first; last < SPECIAL_PM && mons[last].mlet == class; + for (last = first; last < SPECIAL_PM && mons[MONSi(last)].mlet == class; last++) { - if (atyp != A_NONE && sgn(mons[last].maligntyp) != sgn(atyp)) + if (atyp != A_NONE && sgn(mons[MONSi(last)].maligntyp) != sgn(atyp)) continue; /* traditionally mkclass() ignored hell-only and never-in-hell; now we usually honor those but not all the time, mostly so that @@ -1800,17 +1928,18 @@ mkclass_aligned(char class, int spc, /* special mons[].geno handling */ gn_mask |= (gehennom ? G_NOHELL : G_HELL); gn_mask &= ~spc; - if (mk_gen_ok(last, mv_mask, gn_mask)) { + if (mk_gen_ok(MONSi(last), mv_mask, gn_mask)) { /* consider it; don't reject a toostrong() monster if we don't have anything yet (num==0) or if it is the same (or lower) difficulty as preceding candidate (non-zero 'num' implies last > first so mons[last-1] is safe); sometimes accept it even if high difficulty */ - if (num && montoostrong(last, maxmlev) - && mons[last].difficulty > mons[last - 1].difficulty + if (num && montoostrong(MONSi(last), maxmlev) + && mons[MONSi(last)].difficulty > mons[MONSi(last - 1)].difficulty && rn2(2)) break; - if ((k = (mons[last].geno & G_FREQ)) > 0) { + if ((k = (mons[MONSi(last)].geno & G_FREQ)) > 0 + || (k = (zero_freq_for_entire_class ? 1 : 0)) > 0) { /* skew towards lower value monsters at lower exp. levels (this used to be done in the next loop, but that didn't work well when multiple species had the same level and @@ -1820,8 +1949,8 @@ mkclass_aligned(char class, int spc, /* special mons[].geno handling */ being picked nearly twice as often as succubus); we need the '+1' in case the entire set is too high level (really low svl.level hero) */ - nums[last] = k + 1 - (adj_lev(&mons[last]) > (u.ulevel * 2)); - num += nums[last]; + nums[MONSi(last)] = k + 1 - (adj_lev(&mons[MONSi(last)]) > (u.ulevel * 2)); + num += nums[MONSi(last)]; } } } @@ -1831,11 +1960,13 @@ mkclass_aligned(char class, int spc, /* special mons[].geno handling */ /* the hard work has already been done; 'num' should hit 0 before first reaches last (which is actually one past our last candidate) */ for (num = rnd(num); first < last; first++) - if ((num -= nums[first]) <= 0) + if ((num -= nums[MONSi(first)]) <= 0) break; - return nums[first] ? &mons[first] : (struct permonst *) 0; + return nums[MONSi(first)] ? &mons[MONSi(first)] : (struct permonst *) 0; } +#undef MONSi + /* like mkclass(), but excludes difficulty considerations; used when player with polycontrol picks a class instead of a specific type; diff --git a/src/mcastu.c b/src/mcastu.c index 8c28b2507..95e9a1c65 100644 --- a/src/mcastu.c +++ b/src/mcastu.c @@ -84,6 +84,7 @@ choose_magic_spell(int spellval) case 23: if (Antimagic || Hallucination) return MGC_PSI_BOLT; + FALLTHROUGH; /*FALLTHRU*/ case 22: case 21: @@ -138,6 +139,7 @@ choose_clerical_spell(int spellnum) case 14: if (rn2(3)) return CLC_OPEN_WOUNDS; + FALLTHROUGH; /*FALLTHRU*/ case 13: return CLC_GEYSER; @@ -359,10 +361,9 @@ m_cure_self(struct monst *mtmp, int dmg) { if (mtmp->mhp < mtmp->mhpmax) { if (canseemon(mtmp)) - pline("%s looks better.", Monnam(mtmp)); + pline_mon(mtmp, "%s looks better.", Monnam(mtmp)); /* note: player healing does 6d4; this used to do 1d8 */ - if ((mtmp->mhp += d(3, 6)) > mtmp->mhpmax) - mtmp->mhp = mtmp->mhpmax; + healmon(mtmp, d(3, 6), 0); dmg = 0; } return dmg; @@ -563,7 +564,7 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) case MGC_DISAPPEAR: /* makes self invisible */ if (!mtmp->minvis && !mtmp->invis_blkd) { if (canseemon(mtmp)) - pline("%s suddenly %s!", Monnam(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)) @@ -788,7 +789,7 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) fmt = "%s summons %s!"; } if (fmt) - pline(fmt, Monnam(mtmp), what); + pline_mon(mtmp, fmt, Monnam(mtmp), what); dmg = 0; break; @@ -991,7 +992,7 @@ buzzmu(struct monst *mtmp, struct attack *mattk) if (lined_up(mtmp) && rn2(3)) { nomul(0); if (canseemon(mtmp)) - pline("%s zaps you with a %s!", Monnam(mtmp), + pline_mon(mtmp, "%s zaps you with a %s!", Monnam(mtmp), flash_str(BZ_OFS_AD(mattk->adtyp), FALSE)); gb.buzzer = mtmp; buzz(BZ_M_SPELL(BZ_OFS_AD(mattk->adtyp)), (int) mattk->damn, diff --git a/src/mdlib.c b/src/mdlib.c index b6da48e33..fb4155dab 100644 --- a/src/mdlib.c +++ b/src/mdlib.c @@ -56,11 +56,15 @@ static boolean date_via_env = FALSE; extern unsigned long md_ignored_features(void); +extern const char *datamodel(int); char *version_id_string(char *, size_t, const char *) NONNULL NONNULLPTRS; char *bannerc_string(char *, size_t, const char *) NONNULL NONNULLPTRS; int case_insensitive_comp(const char *, const char *) NONNULLPTRS; -staticfn void make_version(void); +#ifndef SFCTOOL +staticfn +#endif /* SFCTOOL */ +void make_version(void); #ifndef HAS_NO_MKSTEMP #ifdef _MSC_VER @@ -228,31 +232,19 @@ static struct soundlib_information soundlib_opts[] = { }; #endif /* !MAKEDEFS_C */ -/* - * Use this to explicitly mask out features during version checks. - * - * ZEROCOMP, RLECOMP, and ZLIB_COMP describe compression features - * that the port/platform which wrote the savefile was capable of - * dealing with. Don't reject a savefile just because the port - * reading the savefile doesn't match on all/some of them. - * The actual compression features used to produce the savefile are - * recorded in the savefile_info structure immediately following the - * version_info, and that is what needs to be checked against the - * feature set of the port that is reading the savefile back in. - * That check is done in src/restore.c now. - * - */ unsigned long md_ignored_features(void) { return (0UL | (1UL << 19) /* SCORE_ON_BOTL */ - | (1UL << 27) /* ZEROCOMP */ - | (1UL << 28) /* RLECOMP */ + | SFCTOOL_BIT /* stored by SFCTOOL, not NetHack itself */ ); } -staticfn void +#ifndef SFCTOOL +staticfn +#endif +void make_version(void) { int i; @@ -285,15 +277,6 @@ make_version(void) #endif #ifdef SCORE_ON_BOTL | (1L << 19) -#endif -/* data format (27..31) - * External compression methods such as COMPRESS and ZLIB_COMP - * do not affect the contents and are thus excluded from here */ -#ifdef ZEROCOMP - | (1L << 27) -#endif -#ifdef RLECOMP - | (1L << 28) #endif ); /* @@ -307,15 +290,6 @@ make_version(void) version.entity_count = (version.entity_count << 12) | (unsigned long) i; i = NUMMONS; version.entity_count = (version.entity_count << 12) | (unsigned long) i; - /* - * Value used for compiler (word size/field alignment/padding) check. - */ - version.struct_sizes1 = - (((unsigned long) sizeof(struct context_info) << 24) - | ((unsigned long) sizeof(struct obj) << 17) - | ((unsigned long) sizeof(struct monst) << 10) - | ((unsigned long) sizeof(struct you))); - version.struct_sizes2 = (((unsigned long) sizeof(struct flag) << 10)); /* free bits in here */ return; } @@ -478,9 +452,6 @@ static const char *const build_opts[] = { #ifdef HOLD_LOCKFILE_OPEN "exclusive lock on level 0 file", #endif -#ifdef MSGHANDLER - "external program as a message handler", -#endif #if defined(HANGUPHANDLING) && !defined(NO_SIGNAL) #ifdef SAFERHANGUP "deferred handling of hangup signal", @@ -614,12 +585,6 @@ static const char *const build_opts[] = { #ifdef VISION_TABLES "vision tables", #endif -#ifdef ZEROCOMP - "zero-compressed save files", -#endif -#ifdef RLECOMP - "run-length compression of map in save files", -#endif #ifdef SYSCF "system configuration at run-time", #endif @@ -731,6 +696,8 @@ build_options(void) STOREOPTTEXT(optbuf); optbuf[0] = '\0'; length = COLNO + 1; /* force 1st item onto new line */ + Strcat(strcpy(buf, datamodel(0)), " data model,"); + opt_out_words(buf, &length); for (i = 0; i < SIZE(build_opts); i++) { #if !defined(MAKEDEFS_C) && defined(FOR_RUNTIME) #ifdef WIN32 diff --git a/src/mhitm.c b/src/mhitm.c index a473fa06c..322b65b22 100644 --- a/src/mhitm.c +++ b/src/mhitm.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mhitm.c $NHDT-Date: 1698939796 2023/11/02 15:43:16 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.244 $ */ +/* NetHack 3.7 mhitm.c $NHDT-Date: 1732979463 2024/11/30 07:11:03 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.253 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -232,7 +232,7 @@ mdisplacem( pline("%s tries to move %s out of %s way.", Monnam(magr), mon_nam(mdef), is_rider(pa) ? "the" : mhis(magr)); } - pline("%s turns to stone!", Monnam(magr)); + pline_mon(magr, "%s turns to stone!", Monnam(magr)); } monstone(magr); if (!DEADMONSTER(magr)) @@ -284,6 +284,9 @@ mdisplacem( * Each successive attack has a lower probability of hitting. Some rely on * success of previous attacks. ** this doesn't seem to be implemented -dl ** * + * Attacker has targeted rather than + * mx,mdef->my>; matters for long worms. + * * In the case of exploding monsters, the monster dies as well. */ int @@ -338,7 +341,7 @@ mattackm( } else { if (iflags.last_msg == PLNMSG_HIDE_UNDER && mdef->m_id == gl.last_hider) - pline("%s emerges from hiding.", Monnam(mdef)); + pline_mon(mdef, "%s emerges from hiding.", Monnam(mdef)); else if (mdef->m_id == gl.last_hider) You("notice %s.", mon_nam(mdef)); else @@ -353,7 +356,7 @@ mattackm( /* Set up the visibility of action */ gv.vis = ((cansee(magr->mx, magr->my) && canspotmon(magr)) - || (cansee(mdef->mx, mdef->my) && canspotmon(mdef))); + || (cansee(mdef->mx, mdef->my) && canspotmon(mdef))); /* Set flag indicating monster has moved this turn. Necessary since a * monster might get an attack out of sequence (i.e. before its move) in @@ -371,6 +374,12 @@ mattackm( /* Now perform all attacks for the monster. */ for (i = 0; i < NATTK; i++) { res[i] = M_ATTK_MISS; + + /* target might no longer be there */ + if (i > 0 && (m_at(gb.bhitpos.x, gb.bhitpos.y) != mdef + || DEADMONSTER(magr) || DEADMONSTER(mdef))) + continue; + mattk = getmattk(magr, mdef, i, res, &alt_attk); /* reduce verbosity for mind flayer attacking creature without a head (or worm's tail); this is similar to monster with multiple @@ -405,6 +414,7 @@ mattackm( mswingsm(magr, mdef, mwep); tmp += hitval(mwep, mdef); } + FALLTHROUGH; /*FALLTHRU*/ case AT_CLAW: case AT_KICK: @@ -413,6 +423,8 @@ mattackm( case AT_TUCH: case AT_BUTT: case AT_TENT: + if (mattk->aatyp == AT_KICK && mtrapped_in_pit(magr)) + continue; /* Nymph that teleported away on first attack? */ if (distmin(magr->mx, magr->my, mdef->mx, mdef->my) > 1) /* Continue because the monster may have a ranged attack. */ @@ -484,7 +496,7 @@ mattackm( case AT_EXPL: /* D: Prevent explosions from a distance */ - if (distmin(magr->mx,magr->my,mdef->mx,mdef->my) > 1) + if (distmin(magr->mx, magr->my, mdef->mx, mdef->my) > 1) continue; res[i] = explmm(magr, mdef, mattk); @@ -524,25 +536,20 @@ mattackm( break; case AT_BREA: - if (!monnear(magr, mdef->mx, mdef->my)) { - strike = (breamm(magr, mattk, mdef) == M_ATTK_MISS) ? 0 : 1; - - /* We don't really know if we hit or not; pretend we did. */ - if (strike) - res[i] |= M_ATTK_HIT; - if (DEADMONSTER(mdef)) - res[i] = M_ATTK_DEF_DIED; - if (DEADMONSTER(magr)) - res[i] |= M_ATTK_AGR_DIED; - } - else - strike = 0; - break; - case AT_SPIT: + /* + * Ranged attacks aren't allowed at point blank range. + * + * That impacts pet use of ranged attacks. It's rather arbitrary + * but various parts of the code assume it to be the case, not to + * mention a part of player tactics when fighting dragons. + */ if (!monnear(magr, mdef->mx, mdef->my)) { - strike = (spitmm(magr, mattk, mdef) == M_ATTK_MISS) ? 0 : 1; + int mmtmp = ((mattk->aatyp == AT_BREA) + ? breamm(magr, mattk, mdef) + : spitmm(magr, mattk, mdef)); + strike = (mmtmp == M_ATTK_MISS) ? 0 : 1; /* We don't really know if we hit or not; pretend we did. */ if (strike) res[i] |= M_ATTK_HIT; @@ -550,6 +557,9 @@ mattackm( res[i] = M_ATTK_DEF_DIED; if (DEADMONSTER(magr)) res[i] |= M_ATTK_AGR_DIED; + } else { + strike = 0; + attk = 0; } break; @@ -683,6 +693,7 @@ hitmm( Snprintf(buf, sizeof buf, "%s squeezes", magr_name); break; } + FALLTHROUGH; /*FALLTHRU*/ default: if (!weaponhit || !mwep || !mwep->oartifact) @@ -771,7 +782,7 @@ gazemm(struct monst *magr, struct monst *mdef, struct attack *mattk) return M_ATTK_MISS; } if (canseemon(magr)) - pline("%s is turned to stone!", Monnam(magr)); + pline_mon(magr, "%s is turned to stone!", Monnam(magr)); monstone(magr); if (!DEADMONSTER(magr)) return M_ATTK_MISS; @@ -818,7 +829,7 @@ engulf_target(struct monst *magr, struct monst *mdef) dy = (mdef == &gy.youmonst) ? u.uy : mdef->my; lev = &levl[dx][dy]; if (!(udef ? Passes_walls : passes_walls(mdef->data)) - && (IS_ROCK(lev->typ) || closed_door(dx, dy) || IS_TREE(lev->typ) + && (IS_OBSTRUCTED(lev->typ) || closed_door(dx, dy) || IS_TREE(lev->typ) /* not passes_bars(); engulfer isn't squeezing through */ || (lev->typ == IRONBARS && !is_whirly(magr->data)))) return FALSE; @@ -826,7 +837,7 @@ engulf_target(struct monst *magr, struct monst *mdef) ay = (magr == &gy.youmonst) ? u.uy : magr->my; lev = &levl[ax][ay]; if (!(uatk ? Passes_walls : passes_walls(magr->data)) - && (IS_ROCK(lev->typ) || closed_door(ax, ay) || IS_TREE(lev->typ) + && (IS_OBSTRUCTED(lev->typ) || closed_door(ax, ay) || IS_TREE(lev->typ) || (lev->typ == IRONBARS && !is_whirly(mdef->data)))) return FALSE; @@ -895,7 +906,9 @@ gulpmm( newsym(ax, ay); /* erase old position */ newsym(dx, dy); /* update new position */ + gm.mswallower = magr; /* corpse_chance() wants this */ status = mdamagem(magr, mdef, mattk, (struct obj *) 0, 0); + gm.mswallower = (struct monst *) 0; /* reset */ if ((status & (M_ATTK_AGR_DIED | M_ATTK_DEF_DIED)) == (M_ATTK_AGR_DIED | M_ATTK_DEF_DIED)) { @@ -962,7 +975,7 @@ explmm(struct monst *magr, struct monst *mdef, struct attack *mattk) return M_ATTK_MISS; if (cansee(magr->mx, magr->my)) - pline("%s explodes!", Monnam(magr)); + pline_mon(magr, "%s explodes!", Monnam(magr)); else noises(magr, mattk); @@ -1033,7 +1046,7 @@ mdamagem( return M_ATTK_HIT; /* no damage during the polymorph */ } if (gv.vis && canspotmon(magr)) - pline("%s turns to stone!", Monnam(magr)); + pline_mon(magr, "%s turns to stone!", Monnam(magr)); monstone(magr); if (!DEADMONSTER(magr)) return M_ATTK_HIT; /* lifesaved */ @@ -1093,7 +1106,7 @@ mdamagem( return (M_ATTK_DEF_DIED | (!DEADMONSTER(magr) ? 0 : M_ATTK_AGR_DIED)); } else if (pd == &mons[PM_NURSE]) { - magr->mhp = magr->mhpmax; + healmon(magr, magr->mhpmax, 0); } mon_givit(magr, pd); } @@ -1137,7 +1150,7 @@ mon_poly(struct monst *magr, struct monst *mdef, int dmg) if (resists_magm(mdef)) { /* Magic resistance */ if (gv.vis) - shieldeff(mdef->mx, mdef->my); + shieldeff_mon(mdef); } else if (resist(mdef, WAND_CLASS, 0, TELL)) { /* general resistance to magic... */ ; @@ -1209,6 +1222,12 @@ paralyze_monst(struct monst *mon, int amt) int sleep_monst(struct monst *mon, int amt, int how) { + /* reveal mimic unless already asleep or paralyzed (won't be 'busy') */ + if (how >= 0 && !mon->msleeping && !mon->mfrozen + && mon->data->mlet == S_MIMIC && (M_AP_TYPE(mon) == M_AP_FURNITURE + || M_AP_TYPE(mon) == M_AP_OBJECT)) + seemimic(mon); + if (resists_sleep(mon) || defended(mon, AD_SLEE) || (how >= 0 && resist(mon, (char) how, 0, NOTELL))) { shieldeff(mon->mx, mon->my); @@ -1232,7 +1251,7 @@ slept_monst(struct monst *mon) { if (helpless(mon) && mon == u.ustuck && !sticks(gy.youmonst.data) && !u.uswallow) { - pline("%s grip relaxes.", s_suffix(Monnam(mon))); + pline_mon(mon, "%s grip relaxes.", s_suffix(Monnam(mon))); unstuck(mon); } } @@ -1267,7 +1286,7 @@ mswingsm( struct obj *otemp) /* attacker's weapon */ { if (flags.verbose && !Blind && mon_visible(magr)) { - boolean bash = (is_pole(otemp) + boolean bash = (is_pole(otemp) && !is_art(otemp, ART_SNICKERSNEE) && (dist2(magr->mx, magr->my, mdef->mx, mdef->my) <= 2)); @@ -1376,17 +1395,15 @@ passivemm( case AD_COLD: if (resists_cold(magr)) { if (canseemon(magr)) { - pline("%s is mildly chilly.", Monnam(magr)); + pline_mon(magr, "%s is mildly chilly.", Monnam(magr)); golemeffects(magr, AD_COLD, tmp); } tmp = 0; break; } if (canseemon(magr)) - pline("%s is suddenly very cold!", Monnam(magr)); - mdef->mhp += tmp / 2; - if (mdef->mhpmax < mdef->mhp) - mdef->mhpmax = mdef->mhp; + pline_mon(magr, "%s is suddenly very cold!", Monnam(magr)); + healmon(mdef, tmp/2, tmp/2); if (mdef->mhpmax > ((int) (mdef->m_lev + 1) * 8)) (void) split_mon(mdef, magr); break; @@ -1394,7 +1411,7 @@ passivemm( if (!magr->mstun) { magr->mstun = 1; if (canseemon(magr)) - pline("%s %s...", Monnam(magr), + pline_mon(magr, "%s %s...", Monnam(magr), makeplural(stagger(magr->data, "stagger"))); } tmp = 0; @@ -1402,26 +1419,27 @@ passivemm( case AD_FIRE: if (resists_fire(magr)) { if (canseemon(magr)) { - pline("%s is mildly warmed.", Monnam(magr)); + pline_mon(magr, "%s is mildly warmed.", Monnam(magr)); golemeffects(magr, AD_FIRE, tmp); } tmp = 0; break; } if (canseemon(magr)) - pline("%s is suddenly very hot!", Monnam(magr)); + pline_mon(magr, "%s is suddenly very hot!", Monnam(magr)); break; case AD_ELEC: if (resists_elec(magr)) { if (canseemon(magr)) { - pline("%s is mildly tingled.", Monnam(magr)); + pline_mon(magr, "%s is mildly tingled.", Monnam(magr)); golemeffects(magr, AD_ELEC, tmp); } tmp = 0; break; } if (canseemon(magr)) - pline("%s is jolted with electricity!", Monnam(magr)); + pline_mon(magr, "%s is jolted with electricity!", + Monnam(magr)); break; default: tmp = 0; @@ -1447,7 +1465,7 @@ xdrainenergym(struct monst *mon, boolean givemsg) || attacktype(mon->data, AT_BREA))) { mon->mspec_used += d(2, 2); if (givemsg) - pline("%s seems lethargic.", Monnam(mon)); + pline_mon(mon, "%s seems lethargic.", Monnam(mon)); } } diff --git a/src/mhitu.c b/src/mhitu.c index 38b765c9c..d6332c18d 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mhitu.c $NHDT-Date: 1721844072 2024/07/24 18:01:12 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.318 $ */ +/* NetHack 3.7 mhitu.c $NHDT-Date: 1740534854 2025/02/25 17:54:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.327 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -19,6 +19,7 @@ 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 *); +staticfn int assess_dmg(struct monst *, int); staticfn int passiveum(struct permonst *, struct monst *, struct attack *); #define ld() ((yyyymmdd((time_t) 0) - (getyear() * 10000L)) == 0xe5) @@ -268,7 +269,7 @@ expels( if (digests(mdat)) { You("get regurgitated!"); } else if (enfolds(mdat)) { - pline("%s unfolds and you are released!", Monnam(mtmp)); + pline_mon(mtmp, "%s unfolds and you are released!", Monnam(mtmp)); } else { char blast[40]; struct attack *attk = attacktype_fordmg(mdat, AT_ENGL, AD_ANY); @@ -452,6 +453,22 @@ calc_mattacku_vars( gn.notonhead = FALSE; } +/* return TRUE iff monster or hero is trapped in a (spiked) pit */ +boolean +mtrapped_in_pit(struct monst *mtmp) +{ + struct trap *ttmp = 0; + + if (mtmp == &gy.youmonst) + ttmp = (u.utrap && u.utraptype == TT_PIT) ? t_at(u.ux, u.uy) : 0; + else + ttmp = mtmp->mtrapped ? t_at(mtmp->mx, mtmp->my) : 0; + + if (ttmp && is_pit(ttmp->ttyp)) + return TRUE; + return FALSE; +} + /* * mattacku: monster attacks you * returns 1 if monster dies (e.g. "yellow light"), 0 otherwise @@ -469,7 +486,7 @@ mattacku(struct monst *mtmp) struct permonst *mdat = mtmp->data; /* * ranged: Is it near you? Affects your actions. - * ranged2: Does it think it's near you? Affects its actions. + * range2: Does it think it's near you? Affects its actions. * foundyou: Is it attacking you or your image? * youseeit: Can you observe the attack? It might be attacking your * image around the corner, or invisible, or you might be blind. @@ -485,7 +502,9 @@ mattacku(struct monst *mtmp) if (!ranged) nomul(0); - if (DEADMONSTER(mtmp) || (Underwater && !is_swimmer(mtmp->data))) + if (DEADMONSTER(mtmp)) + return 1; + if (Underwater && !is_swimmer(mtmp->data)) return 0; /* If swallowed, can only be affected by u.ustuck */ @@ -494,11 +513,10 @@ mattacku(struct monst *mtmp) return 0; u.ustuck->mux = u.ux; u.ustuck->muy = u.uy; - range2 = 0; - foundyou = 1; if (u.uinvulnerable) return 0; /* stomachs can't hurt you! */ - + range2 = 0; + foundyou = 1; } else if (u.usteed) { if (mtmp == u.usteed) /* Your steed won't attack you */ @@ -548,7 +566,7 @@ mattacku(struct monst *mtmp) so mtmp's next move will be a regular attack */ place_monster(mtmp, mtmp->mx, mtmp->my); /* put back */ newsym(u.ux, u.uy); /* u.uundetected was toggled */ - pline("%s draws back as you drop!", Monnam(mtmp)); + pline_mon(mtmp, "%s draws back as you drop!", Monnam(mtmp)); return 0; } @@ -716,7 +734,7 @@ mattacku(struct monst *mtmp) if (u.uinvulnerable) { /* in the midst of successful prayer */ /* monsters won't attack you */ if (mtmp == u.ustuck) { - pline("%s loosens its grip slightly.", Monnam(mtmp)); + pline_mon(mtmp, "%s loosens its grip slightly.", Monnam(mtmp)); } else if (!range2) { if (youseeit || sensemon(mtmp)) pline("%s starts to attack you, but pulls back.", @@ -740,6 +758,9 @@ mattacku(struct monst *mtmp) for (i = 0; i < NATTK; i++) { sum[i] = M_ATTK_MISS; + /* counterattack against attack [i-1] might have been fatal */ + if (DEADMONSTER(mtmp)) + return 1; if (i > 0) { /* recalc in case prior attack moved hero; mtmp doesn't make another attempt to guess your location but might have @@ -749,6 +770,8 @@ mattacku(struct monst *mtmp) /* if hero was found but isn't anymore, avoid wildmiss now */ if (firstfoundyou && !foundyou) continue; /* set sum[i] to 'miss' but skip other actions */ + if (!u_at(gb.bhitpos.x, gb.bhitpos.y)) + continue; } mon_currwep = (struct obj *) 0; mattk = getmattk(mtmp, &gy.youmonst, i, sum, &alt_attk); @@ -766,6 +789,8 @@ mattacku(struct monst *mtmp) case AT_TUCH: case AT_BUTT: case AT_TENT: + if (mattk->aatyp == AT_KICK && mtrapped_in_pit(mtmp)) + continue; if (!range2 && (!MON_WEP(mtmp) || mtmp->mconf || Conflict || !touch_petrifies(gy.youmonst.data))) { if (foundyou) { @@ -820,10 +845,11 @@ mattacku(struct monst *mtmp) missmu(mtmp, (tmp == j), mattk); } } else if (digests(mtmp->data)) { - pline("%s gulps some air!", Monnam(mtmp)); + pline_mon(mtmp, "%s gulps some air!", Monnam(mtmp)); } else { if (youseeit) { - pline("%s lunges forward and recoils!", Monnam(mtmp)); + pline_mon(mtmp, "%s lunges forward and recoils!", + Monnam(mtmp)); } else { if (is_whirly(mtmp->data)) { Soundeffect(se_rushing_wind_noise, 60); @@ -866,6 +892,7 @@ mattacku(struct monst *mtmp) mon_currwep = MON_WEP(mtmp); if (mon_currwep) { boolean bash = (is_pole(mon_currwep) + && !is_art(mon_currwep, ART_SNICKERSNEE) && m_next2u(mtmp)); hittmp = hitval(mon_currwep, &gy.youmonst); @@ -955,7 +982,7 @@ summonmu(struct monst *mtmp, boolean youseeit) Strcpy(genericwere, "creature"); if (youseeit) - pline("%s summons help!", Monnam(mtmp)); + pline_mon(mtmp, "%s summons help!", Monnam(mtmp)); numhelp = were_summon(mdat, FALSE, &numseen, genericwere); if (youseeit) { if (numhelp > 0) { @@ -1025,7 +1052,7 @@ u_slip_free(struct monst *mtmp, struct attack *mattk) protection might fail (33% chance) when the armor is cursed */ if (obj && (obj->greased || obj->otyp == OILSKIN_CLOAK) && (!obj->cursed || rn2(3))) { - pline("%s %s your %s %s!", Monnam(mtmp), + pline_mon(mtmp, "%s %s your %s %s!", Monnam(mtmp), (mattk->adtyp == AD_WRAP) ? "slips off of" : "grabs you, but cannot hold onto", obj->greased ? "greased" : "slippery", @@ -1244,14 +1271,14 @@ gulp_blnd_check(void) return FALSE; } -/* monster swallows you, or damage if u.uswallow */ +/* monster swallows you, or damage if already swallowed (u.uswallow != 0) */ staticfn int gulpmu(struct monst *mtmp, struct attack *mattk) { struct trap *t = t_at(u.ux, u.uy); int tmp = d((int) mattk->damn, (int) mattk->damd); int tim_tmp; - struct obj *otmp2; + struct obj *otmp2, *nextobj; int i; boolean physical_damage = FALSE; @@ -1356,8 +1383,10 @@ gulpmu(struct monst *mtmp, struct attack *mattk) u.uswldtim = (unsigned) ((tim_tmp < 2) ? 2 : tim_tmp); swallowed(1); /* update the map display, shows hero swallowed */ if (!flaming(mtmp->data)) { - for (otmp2 = gi.invent; otmp2; otmp2 = otmp2->nobj) + for (otmp2 = gi.invent; otmp2; otmp2 = nextobj) { + nextobj = otmp2->nobj; (void) snuff_lit(otmp2); + } } } @@ -1508,7 +1537,9 @@ gulpmu(struct monst *mtmp, struct attack *mattk) if (physical_damage) tmp = Maybe_Half_Phys(tmp); + gm.mswallower = mtmp; /* match gulpmm() */ mdamageu(mtmp, tmp); + gm.mswallower = 0; if (tmp) stop_occupation(); @@ -1659,7 +1690,7 @@ gazemu(struct monst *mtmp, struct attack *mattk) if (is_medusa && Hallucination && !rn2(3)) pline("Someone seems overdue for a serpent cut."); else - pline("%s %s.", Monnam(mtmp), + pline_mon(mtmp, "%s %s.", Monnam(mtmp), (is_medusa && mtmp->mcan && !react) ? "doesn't look all that ugly" : "gazes ineffectually"); @@ -1683,7 +1714,7 @@ gazemu(struct monst *mtmp, struct attack *mattk) break; } if (useeit) - pline("%s is turned to stone!", Monnam(mtmp)); + pline_mon(mtmp, "%s is turned to stone!", Monnam(mtmp)); gs.stoned = TRUE; killed(mtmp); @@ -1713,7 +1744,8 @@ gazemu(struct monst *mtmp, struct attack *mattk) mtmp->mspec_used = mtmp->mspec_used + (conf + rn2(6)); if (!Confusion) - pline("%s gaze confuses you!", s_suffix(Monnam(mtmp))); + pline_mon(mtmp, "%s gaze confuses you!", + s_suffix(Monnam(mtmp))); else You("are getting more and more confused."); make_confused(HConfusion + conf, FALSE); @@ -1730,7 +1762,7 @@ gazemu(struct monst *mtmp, struct attack *mattk) int stun = d(2, 6); mtmp->mspec_used = mtmp->mspec_used + (stun + rn2(6)); - pline("%s stares piercingly at you!", Monnam(mtmp)); + pline_mon(mtmp, "%s stares piercingly at you!", Monnam(mtmp)); make_stunned((HStun & TIMEOUT) + (long) stun, TRUE); stop_occupation(); } @@ -1774,7 +1806,8 @@ gazemu(struct monst *mtmp, struct attack *mattk) } else { int dmg = d(2, 6), orig_dmg = dmg, lev = (int) mtmp->m_lev; - pline("%s attacks you with a fiery gaze!", Monnam(mtmp)); + pline_mon(mtmp, "%s attacks you with a fiery gaze!", + Monnam(mtmp)); stop_occupation(); if (Fire_resistance) { shieldeff(u.ux, u.uy); @@ -1834,7 +1867,7 @@ gazemu(struct monst *mtmp, struct attack *mattk) react = rn2(SIZE(reactions)); /* cancelled/hallucinatory feedback; monster might look "confused", "stunned",&c but we don't actually set corresponding attribute */ - pline("%s looks %s%s.", Monnam(mtmp), + pline_mon(mtmp, "%s looks %s%s.", Monnam(mtmp), !rn2(3) ? "" : already ? "quite " : (!rn2(2) ? "a bit " : "somewhat "), reactions[react]); @@ -1938,12 +1971,13 @@ doseduce(struct monst *mon) char qbuf[QBUFSZ], Who[QBUFSZ]; if (mon->mcan || mon->mspec_used) { - pline("%s acts as though %s has got a %sheadache.", Monnam(mon), - mhe(mon), mon->mcan ? "severe " : ""); + pline_mon(mon, "%s acts as though %s has got a %sheadache.", + Monnam(mon), mhe(mon), mon->mcan ? "severe " : ""); return 0; } if (unresponsive()) { - pline("%s seems dismayed at your lack of response.", Monnam(mon)); + pline_mon(mon, "%s seems dismayed at your lack of response.", + Monnam(mon)); return 0; } seewho = canseemon(mon); @@ -2101,7 +2135,7 @@ doseduce(struct monst *mon) : ""); } } else if (seewho) - pline("%s appears to sigh.", Monnam(mon)); + pline_mon(mon, "%s appears to sigh.", Monnam(mon)); /* else no regret message if can't see or hear seducer */ if (!tele_restrict(mon)) @@ -2211,7 +2245,8 @@ doseduce(struct monst *mon) pline("%s demands that you pay %s, but you refuse...", noit_Monnam(mon), noit_mhim(mon)); } else if (u.umonnum == PM_LEPRECHAUN) { - pline("%s tries to take your gold, but fails...", noit_Monnam(mon)); + pline_mon(mon, "%s tries to take your gold, but fails...", + noit_Monnam(mon)); } else { long cost; long umoney = money_cnt(gi.invent); @@ -2231,8 +2266,8 @@ doseduce(struct monst *mon) SetVoice(mon, 0, 80, 0); verbalize("It's on the house!"); } else { - pline("%s takes %ld %s for services rendered!", noit_Monnam(mon), - cost, currency(cost)); + pline_mon(mon, "%s takes %ld %s for services rendered!", + noit_Monnam(mon), cost, currency(cost)); money2mon(mon, cost); disp.botl = TRUE; } @@ -2291,6 +2326,75 @@ mayberem(struct monst *mon, remove_worn_item(obj, TRUE); } +staticfn int +assess_dmg(struct monst *mtmp, int tmp) +{ + if ((mtmp->mhp -= tmp) <= 0) { + pline_mon(mtmp, "%s dies!", Monnam(mtmp)); + xkilled(mtmp, XKILL_NOMSG); + if (!DEADMONSTER(mtmp)) + return M_ATTK_HIT; + return M_ATTK_AGR_DIED; + } + return M_ATTK_HIT; +} + +#if 0 +/* returns True if monster has a range attack in its repertoire + that it will actually utilize. Caller provides the assessment + callback (optional). Callback returns 0 if the attack is + active */ + +boolean ranged_attk_assessed( +struct monst *mtmp, +boolean (*assessfunc)(struct monst *, int)) +{ + int i; + struct permonst *ptr = mtmp->data; + + for (i = 0; i < NATTK; i++) + if (DISTANCE_ATTK_TYPE(ptr->mattk[i].aatyp)) { + if (!assessfunc || (*assessfunc)(mtmp, i) == 0) + return TRUE; + } + return FALSE; +} +#endif + +/* 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) +{ + struct permonst *ptr = mtmp->data; + int typ = -1; + + if (attkidx >= 0 + && (typ = get_atkdam_type(ptr->mattk[attkidx].adtyp)) >= 0 + && m_seenres(mtmp, cvt_adtyp_to_mseenres(typ))) + return TRUE; + return FALSE; +} + +/* + * This would be equivalent to: + * ranged_attk_assessed(mtmp, mon_avoiding_this_attack) + * but without the added assessment function call overhead. + */ +boolean ranged_attk_available(struct monst *mtmp) +{ + int i, typ = -1; + struct permonst *ptr = mtmp->data; + + for (i = 0; i < NATTK; i++) { + if (DISTANCE_ATTK_TYPE(ptr->mattk[i].aatyp) + && (typ = get_atkdam_type(ptr->mattk[i].adtyp)) >= 0 + && m_seenres(mtmp, cvt_adtyp_to_mseenres(typ)) == 0) + return TRUE; + } + return FALSE; +} + /* FIXME: * sequencing issue: a monster's attack might cause poly'd hero * to revert to normal form. The messages for passive counterattack @@ -2330,13 +2434,13 @@ passiveum( switch (oldu_mattk->adtyp) { case AD_ACID: if (!rn2(2)) { - pline("%s is splashed by %s%s!", Monnam(mtmp), + pline_mon(mtmp, "%s is splashed by %s%s!", Monnam(mtmp), /* temporary? hack for sequencing issue: "your acid" looks strange coming immediately after player has been told that hero has reverted to normal form */ !Upolyd ? "" : "your ", hliquid("acid")); if (resists_acid(mtmp)) { - pline("%s is not affected.", Monnam(mtmp)); + pline_mon(mtmp, "%s is not affected.", Monnam(mtmp)); tmp = 0; } } else @@ -2345,7 +2449,7 @@ passiveum( erode_armor(mtmp, ERODE_CORRODE); if (!rn2(6)) acid_damage(MON_WEP(mtmp)); - goto assess_dmg; + return assess_dmg(mtmp, tmp); case AD_STON: /* cockatrice */ { long protector = attk_protection((int) mattk->aatyp), @@ -2363,7 +2467,7 @@ passiveum( mon_to_stone(mtmp); return 1; } - pline("%s turns to stone!", Monnam(mtmp)); + pline_mon(mtmp, "%s turns to stone!", Monnam(mtmp)); gs.stoned = 1; xkilled(mtmp, XKILL_NOMSG); if (!DEADMONSTER(mtmp)) @@ -2394,7 +2498,7 @@ passiveum( You("explode!"); /* KMH, balance patch -- this is okay with unchanging */ rehumanize(); - goto assess_dmg; + return assess_dmg(mtmp, tmp); } break; case AD_PLYS: /* Floating eye */ @@ -2413,13 +2517,14 @@ passiveum( if (mon_reflects(mtmp, "Your gaze is reflected by %s %s.")) return 1; - pline("%s is frozen by your gaze!", Monnam(mtmp)); + pline_mon(mtmp, "%s is frozen by your gaze!", + Monnam(mtmp)); paralyze_monst(mtmp, tmp); return M_ATTK_AGR_DONE; } } } else { /* gelatinous cube */ - pline("%s is frozen by you.", Monnam(mtmp)); + pline_mon(mtmp, "%s is frozen by you.", Monnam(mtmp)); paralyze_monst(mtmp, tmp); return M_ATTK_AGR_DONE; } @@ -2427,12 +2532,12 @@ passiveum( case AD_COLD: /* Brown mold or blue jelly */ if (resists_cold(mtmp)) { shieldeff(mtmp->mx, mtmp->my); - pline("%s is mildly chilly.", Monnam(mtmp)); + pline_mon(mtmp, "%s is mildly chilly.", Monnam(mtmp)); golemeffects(mtmp, AD_COLD, tmp); tmp = 0; break; } - pline("%s is suddenly very cold!", Monnam(mtmp)); + pline_mon(mtmp, "%s is suddenly very cold!", Monnam(mtmp)); u.mh += (tmp + rn2(2)) / 2; if (u.mhmax < u.mh) u.mhmax = u.mh; @@ -2442,7 +2547,7 @@ passiveum( case AD_STUN: /* Yellow mold */ if (!mtmp->mstun) { mtmp->mstun = 1; - pline("%s %s.", Monnam(mtmp), + pline_mon(mtmp, "%s %s.", Monnam(mtmp), makeplural(stagger(mtmp->data, "stagger"))); } tmp = 0; @@ -2450,22 +2555,23 @@ passiveum( case AD_FIRE: /* Red mold */ if (resists_fire(mtmp)) { shieldeff(mtmp->mx, mtmp->my); - pline("%s is mildly warm.", Monnam(mtmp)); + pline_mon(mtmp, "%s is mildly warm.", Monnam(mtmp)); golemeffects(mtmp, AD_FIRE, tmp); tmp = 0; break; } - pline("%s is suddenly very hot!", Monnam(mtmp)); + pline_mon(mtmp, "%s is suddenly very hot!", Monnam(mtmp)); break; case AD_ELEC: if (resists_elec(mtmp)) { shieldeff(mtmp->mx, mtmp->my); - pline("%s is slightly tingled.", Monnam(mtmp)); + pline_mon(mtmp, "%s is slightly tingled.", Monnam(mtmp)); golemeffects(mtmp, AD_ELEC, tmp); tmp = 0; break; } - pline("%s is jolted with your electricity!", Monnam(mtmp)); + pline_mon(mtmp, "%s is jolted with your electricity!", + Monnam(mtmp)); break; default: tmp = 0; @@ -2474,15 +2580,7 @@ passiveum( else tmp = 0; - assess_dmg: - if ((mtmp->mhp -= tmp) <= 0) { - pline("%s dies!", Monnam(mtmp)); - xkilled(mtmp, XKILL_NOMSG); - if (!DEADMONSTER(mtmp)) - return M_ATTK_HIT; - return M_ATTK_AGR_DIED; - } - return M_ATTK_HIT; + return assess_dmg(mtmp, tmp); } struct monst * @@ -2501,7 +2599,7 @@ cloneu(void) return NULL; mon->mcloned = 1; mon = christen_monst(mon, svp.plname); - initedog(mon); + initedog(mon, TRUE); mon->m_lev = gy.youmonst.data->mlevel; mon->mhpmax = u.mhmax; mon->mhp = u.mh / 2; diff --git a/src/minion.c b/src/minion.c index 77644e7a7..6acd508a7 100644 --- a/src/minion.c +++ b/src/minion.c @@ -21,6 +21,7 @@ newemin(struct monst *mtmp) if (!EMIN(mtmp)) { EMIN(mtmp) = (struct emin *) alloc(sizeof(struct emin)); (void) memset((genericptr_t) EMIN(mtmp), 0, sizeof(struct emin)); + EMIN(mtmp)->parentmid = mtmp->m_id; } } @@ -528,10 +529,6 @@ gain_guardian_angel(void) * the final level of the game. The angel will still appear, but * won't be tamed. */ if (u.uconduct.pets) { - /* guardian angel -- the one case mtame doesn't - * imply an edog structure, so we don't want to - * call tamedog(). - */ mtmp->mtame = 10; u.uconduct.pets++; } diff --git a/src/mklev.c b/src/mklev.c index d4205b12a..8c75cc1df 100644 --- a/src/mklev.c +++ b/src/mklev.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mklev.c $NHDT-Date: 1704830831 2024/01/09 20:07:11 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.175 $ */ +/* NetHack 3.7 mklev.c $NHDT-Date: 1737387068 2025/01/20 07:31:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.194 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Alex Smith, 2017. */ /* NetHack may be freely redistributed. See license for details. */ @@ -23,7 +23,10 @@ staticfn void mk_knox_portal(coordxy, coordxy); staticfn void makevtele(void); staticfn void fill_ordinary_room(struct mkroom *, boolean) NONNULLARG1; staticfn void themerooms_post_level_generate(void); +staticfn boolean chk_okdoor(coordxy, coordxy); +staticfn void mklev_sanity_check(void); staticfn void makelevel(void); +staticfn boolean water_has_kelp(coordxy, coordxy, int, int); staticfn boolean bydoor(coordxy, coordxy); staticfn void mktrap_victim(struct trap *); staticfn int traptype_rnd(unsigned); @@ -42,8 +45,10 @@ staticfn void do_room_or_subroom(struct mkroom *, coordxy, coordxy, coordxy, coordxy, boolean, schar, boolean, boolean); staticfn void makerooms(void); -staticfn boolean door_into_nonjoined(coordxy, coordxy); -staticfn boolean finddpos(coord *, coordxy, coordxy, coordxy, coordxy); +staticfn boolean good_rm_wall_doorpos(coordxy, coordxy, int, struct mkroom *); +staticfn boolean finddpos_shift(coordxy *, coordxy *, int, struct mkroom *); + +staticfn boolean finddpos(coord *, int, struct mkroom *); #define create_vault() create_room(-1, -1, 2, 2, -1, -1, VAULT, TRUE) #define init_vault() gv.vault_x = -1 @@ -63,54 +68,135 @@ mkroom_cmp(const genericptr vx, const genericptr vy) return (x->lx > y->lx); } -/* Return TRUE if a door placed at (x, y) which otherwise passes okdoor() - * checks would be connecting into an area that was declared as joined=false. - * Checking for this in finddpos() enables us to have rooms with sub-areas - * (such as shops) that will never randomly generate unwanted doors in order - * to connect them up to other areas. - */ +/* is x,y a good location for a door into room? */ staticfn boolean -door_into_nonjoined(coordxy x, coordxy y) +good_rm_wall_doorpos(coordxy x, coordxy y, int dir, struct mkroom *room) { - coordxy tx, ty, i; + coordxy tx, ty; + int rmno; - for (i = 0; i < 4; i++) { - tx = x + xdir[dirs_ord[i]]; - ty = y + ydir[dirs_ord[i]]; - if (!isok(tx, ty) || IS_ROCK(levl[tx][ty].typ)) - continue; + if (!isok(x, y) || !room->needjoining) + return FALSE; - /* Is this connecting to a room that doesn't want joining? */ - if (levl[tx][ty].roomno >= ROOMOFFSET - && !svr.rooms[levl[tx][ty].roomno - ROOMOFFSET].needjoining) { - return TRUE; + if (!(levl[x][y].typ == HWALL + || levl[x][y].typ == VWALL + || IS_DOOR(levl[x][y].typ) + || levl[x][y].typ == SDOOR)) + return FALSE; + + if (bydoor(x, y)) + return FALSE; + + tx = x + xdir[dir]; + ty = y + ydir[dir]; + + if (!isok(tx,ty) || IS_OBSTRUCTED(levl[tx][ty].typ)) + return FALSE; + + rmno = (room - svr.rooms) + ROOMOFFSET; + + if (rmno != (int) levl[tx][ty].roomno) + return FALSE; + + return TRUE; +} + +/* starting from x,y going towards dir, find a good location for a door */ +staticfn boolean +finddpos_shift(coordxy *x, coordxy *y, int dir, struct mkroom *aroom) +{ + coordxy dx, dy; + + dir = DIR_180(dir); + + dx = xdir[dir]; + dy = ydir[dir]; + + if (good_rm_wall_doorpos(*x, *y, dir, aroom)) + return TRUE; + + /* irregular rooms may have the room wall away from the room rectangular + area; go into the area until we encounter something */ + if (aroom->irregular) { + coordxy rx = *x, ry = *y; + boolean fail = FALSE; + + while (!fail && isok(rx, ry) + && (levl[rx][ry].typ == STONE || levl[rx][ry].typ == CORR)) { + rx += dx; + ry += dy; + if (good_rm_wall_doorpos(rx, ry, dir, aroom)) { + *x = rx; + *y = ry; + return TRUE; + } + if (!(levl[rx][ry].typ == STONE || levl[rx][ry].typ == CORR)) + fail = TRUE; + if (rx < aroom->lx || rx > aroom->hx + || ry < aroom->ly || ry > aroom->hy) + fail = TRUE; } } return FALSE; } +/* find a valid door position at room edge. + dir is the preferred edge of the room. + if found, returns TRUE and the coordinate in cc */ staticfn boolean -finddpos(coord *cc, coordxy xl, coordxy yl, coordxy xh, coordxy yh) +finddpos(coord *cc, int dir, struct mkroom *aroom) { coordxy x, y; + coordxy x1, y1, x2, y2; + int tryct = 0; - x = rn1(xh - xl + 1, xl); - y = rn1(yh - yl + 1, yl); - if (okdoor(x, y) && !door_into_nonjoined(x, y)) - goto gotit; + switch (dir) { + case DIR_N: + x1 = aroom->lx; + x2 = aroom->hx; + y1 = aroom->ly - 1; + y2 = aroom->ly - 1; + break; + case DIR_S: + x1 = aroom->lx; + x2 = aroom->hx; + y1 = aroom->hy + 1; + y2 = aroom->hy + 1; + break; + case DIR_W: + x1 = aroom->lx - 1; + x2 = aroom->lx - 1; + y1 = aroom->ly; + y2 = aroom->hy; + break; + case DIR_E: + x1 = aroom->hx + 1; + x2 = aroom->hx + 1; + y1 = aroom->ly; + y2 = aroom->hy; + break; + default: + impossible("finddpos: illegal dir"); + return FALSE; + } - for (x = xl; x <= xh; x++) - for (y = yl; y <= yh; y++) - if (okdoor(x, y) && !door_into_nonjoined(x, y)) + /* try random points */ + do { + x = (x2 - x1) ? rn1(x2 - x1 + 1, x1) : x1; + y = (y2 - y1) ? rn1(y2 - y1 + 1, y1) : y1; + if (finddpos_shift(&x, &y, dir, aroom)) + goto gotit; + } while (++tryct < 20); + + /* try all the points */ + for (x = x1; x <= x2; x++) + for (y = y1; y <= y2; y++) + if (finddpos_shift(&x, &y, dir, aroom)) goto gotit; - for (x = xl; x <= xh; x++) - for (y = yl; y <= yh; y++) - if (IS_DOOR(levl[x][y].typ) || levl[x][y].typ == SDOOR) - goto gotit; /* cannot find something reasonable -- strange */ - x = xl; - y = yh; + cc->x = x1; + cc->y = y1; return FALSE; gotit: cc->x = x; @@ -215,7 +301,7 @@ do_room_or_subroom(struct mkroom *croom, } void -add_room(int lowx, int lowy, int hix, int hiy, +add_room(coordxy lowx, coordxy lowy, coordxy hix, coordxy hiy, boolean lit, schar rtype, boolean special) { struct mkroom *croom; @@ -229,7 +315,9 @@ add_room(int lowx, int lowy, int hix, int hiy, } void -add_subroom(struct mkroom *proom, int lowx, int lowy, int hix, int hiy, +add_subroom(struct mkroom *proom, + coordxy lowx, coordxy lowy, + coordxy hix, coordxy hiy, boolean lit, schar rtype, boolean special) { struct mkroom *croom; @@ -344,6 +432,8 @@ join(int a, int b, boolean nxcor) coordxy tx, ty, xx, yy; struct mkroom *croom, *troom; int dx, dy; + int npoints; + boolean dig_result; croom = &svr.rooms[a]; troom = &svr.rooms[b]; @@ -361,36 +451,36 @@ join(int a, int b, boolean nxcor) dy = 0; xx = croom->hx + 1; tx = troom->lx - 1; - if (!finddpos(&cc, xx, croom->ly, xx, croom->hy)) + if (!finddpos(&cc, DIR_E, croom)) return; - if (!finddpos(&tt, tx, troom->ly, tx, troom->hy)) + if (!finddpos(&tt, DIR_W, troom)) return; } else if (troom->hy < croom->ly) { dy = -1; dx = 0; yy = croom->ly - 1; ty = troom->hy + 1; - if (!finddpos(&cc, croom->lx, yy, croom->hx, yy)) + if (!finddpos(&cc, DIR_N, croom)) return; - if (!finddpos(&tt, troom->lx, ty, troom->hx, ty)) + if (!finddpos(&tt, DIR_S, troom)) return; } else if (troom->hx < croom->lx) { dx = -1; dy = 0; xx = croom->lx - 1; tx = troom->hx + 1; - if (!finddpos(&cc, xx, croom->ly, xx, croom->hy)) + if (!finddpos(&cc, DIR_W, croom)) return; - if (!finddpos(&tt, tx, troom->ly, tx, troom->hy)) + if (!finddpos(&tt, DIR_E, troom)) return; } else { dy = 1; dx = 0; yy = croom->hy + 1; ty = troom->ly - 1; - if (!finddpos(&cc, croom->lx, yy, croom->hx, yy)) + if (!finddpos(&cc, DIR_S, croom)) return; - if (!finddpos(&tt, troom->lx, ty, troom->hx, ty)) + if (!finddpos(&tt, DIR_N, troom)) return; } xx = cc.x; @@ -399,16 +489,20 @@ join(int a, int b, boolean nxcor) ty = tt.y - dy; if (nxcor && levl[xx + dx][yy + dy].typ != STONE) return; - if (okdoor(xx, yy) || !nxcor) - dodoor(xx, yy, croom); org.x = xx + dx; org.y = yy + dy; dest.x = tx; dest.y = ty; - if (!dig_corridor(&org, &dest, nxcor, - svl.level.flags.arboreal ? ROOM : CORR, STONE)) + dig_result = dig_corridor(&org, &dest, &npoints, nxcor, + svl.level.flags.arboreal ? ROOM : CORR, STONE); + + /* we created at least 1 tile of corridor, even if it failed */ + if ((npoints > 0) && (okdoor(xx, yy) || !nxcor)) + dodoor(xx, yy, croom); + + if (!dig_result) return; /* we succeeded in digging the corridor */ @@ -611,13 +705,11 @@ place_niche( if (rn2(2)) { *dy = 1; - if (!finddpos(&dd, aroom->lx, aroom->hy + 1, - aroom->hx, aroom->hy + 1)) + if (!finddpos(&dd, DIR_S, aroom)) return FALSE; } else { *dy = -1; - if (!finddpos(&dd, aroom->lx, aroom->ly - 1, - aroom->hx, aroom->ly - 1)) + if (!finddpos(&dd, DIR_N, aroom)) return FALSE; } *xx = dd.x; @@ -672,7 +764,7 @@ makeniche(int trap_type) ttmp->once = 1; if (trap_engravings[trap_type]) { make_engr_at(xx, yy - dy, - trap_engravings[trap_type], 0L, + trap_engravings[trap_type], NULL, 0L, DUST); wipe_engr_at(xx, yy - dy, 5, FALSE); /* age it a little */ @@ -682,9 +774,9 @@ makeniche(int trap_type) dosdoor(xx, yy, aroom, SDOOR); } else { rm->typ = CORR; - if (rn2(7)) + if (rn2(7)) { dosdoor(xx, yy, aroom, rn2(5) ? SDOOR : DOOR); - else { + } else { /* inaccessible niches occasionally have iron bars */ if (!rn2(5) && IS_WALL(levl[xx][yy].typ)) { (void) set_levltyp(xx, yy, IRONBARS); @@ -1045,8 +1137,8 @@ fill_ordinary_room( /* maybe make some graffiti */ if (!rn2(27 + 3 * abs(depth(&u.uz)))) { - char buf[BUFSZ]; - const char *mesg = random_engraving(buf); + char buf[BUFSZ], pristinebuf[BUFSZ]; + const char *mesg = random_engraving(buf, pristinebuf); if (mesg) { do { @@ -1055,7 +1147,7 @@ fill_ordinary_room( y = pos.y; } while (levl[x][y].typ != ROOM && !rn2(40)); if (levl[x][y].typ == ROOM) - make_engr_at(x, y, mesg, 0L, MARK); + make_engr_at(x, y, mesg, pristinebuf, 0L, MARK); } } @@ -1093,11 +1185,65 @@ themerooms_post_level_generate(void) iflags.in_lua = gi.in_mk_themerooms = FALSE; wallification(1, 0, COLNO - 1, ROWNO - 1); - free(gc.coder); - gc.coder = NULL; + if (gc.coder) + free(gc.coder), gc.coder = NULL; lua_gc(themes, LUA_GCCOLLECT); } +/* if x,y is door, does it open into solid terrain */ +staticfn boolean +chk_okdoor(coordxy x, coordxy y) +{ + if (IS_DOOR(levl[x][y].typ)) { + if (levl[x][y].horizontal) { + if ((isok(x, y-1) && (levl[x][y-1].typ > TREE)) + && (isok(x, y+1) && (levl[x][y+1].typ <= TREE))) + return FALSE; + if ((isok(x, y-1) && (levl[x][y-1].typ <= TREE)) + && (isok(x, y+1) && (levl[x][y+1].typ > TREE))) + return FALSE; + } else { + if ((isok(x-1, y) && (levl[x-1][y].typ > TREE)) + && (isok(x+1, y) && (levl[x+1][y].typ <= TREE))) + return FALSE; + if ((isok(x-1, y) && (levl[x-1][y].typ <= TREE)) + && (isok(x+1, y) && (levl[x+1][y].typ > TREE))) + return FALSE; + } + return TRUE; + } + return TRUE; +} + +/* check mklev created level sanity */ +staticfn void +mklev_sanity_check(void) +{ + coordxy x, y; + int i; + int rmno = -1; + + if (!(iflags.sanity_check || iflags.debug_fuzzer)) + return; + + for (y = 0; y < ROWNO; y++) { + for (x = 1; x < COLNO; x++) { + if (!chk_okdoor(x,y)) + impossible("levl[%i][%i] door not ok", x, y); + } + } + + for (i = 0; i < svn.nroom; i++) { + if (!svr.rooms[i].needjoining) + continue; + if (rmno == -1) + rmno = gs.smeq[i]; + if (rmno != -1 && gs.smeq[i] != rmno) + impossible("room %i not connected?", i); + } +} + + staticfn void makelevel(void) { @@ -1105,14 +1251,17 @@ makelevel(void) branch *branchp; stairway *prevstairs; int room_threshold; - s_level *slev = Is_special(&u.uz); + s_level *slev; int i; - if (wiz1_level.dlevel == 0) + if (wiz1_level.dlevel == 0) { + impossible("makelevel() called when dungeon not yet initialized."); init_dungeons(); + } oinit(); /* assign level dependent obj probabilities */ clear_level_structures(); + slev = Is_special(&u.uz); /* check for special levels */ if (slev && !Is_rogue_level(&u.uz)) { makemaz(slev->proto); @@ -1158,6 +1307,8 @@ makelevel(void) makecorridors(); make_niches(); + mklev_sanity_check(); + /* make a secret treasure vault, not connected to the rest */ if (do_vault()) { coordxy w, h; @@ -1273,6 +1424,18 @@ makelevel(void) } } +/* return TRUE if water location at (x,y) should have kelp. */ +staticfn boolean +water_has_kelp(coordxy x, coordxy y, int kelp_pool, int kelp_moat) +{ + if ((kelp_pool && (levl[x][y].typ == POOL + || (levl[x][y].typ == WATER && !Is_waterlevel(&u.uz))) + && !rn2(kelp_pool)) + || (kelp_moat && levl[x][y].typ == MOAT && !rn2(kelp_moat))) + return TRUE; + return FALSE; +} + /* * Place deposits of minerals (gold and misc gems) in the stone * surrounding the rooms on the map. @@ -1298,8 +1461,7 @@ mineralize(int kelp_pool, int kelp_moat, int goldprob, int gemprob, return; for (x = 2; x < (COLNO - 2); x++) for (y = 1; y < (ROWNO - 1); y++) - if ((kelp_pool && levl[x][y].typ == POOL && !rn2(kelp_pool)) - || (kelp_moat && levl[x][y].typ == MOAT && !rn2(kelp_moat))) + if (water_has_kelp(x, y, kelp_pool, kelp_moat)) (void) mksobj_at(KELP_FROND, x, y, TRUE, FALSE); /* determine if it is even allowed; @@ -1530,7 +1692,6 @@ place_branch( coord m = {0}; d_level *dest; boolean make_stairs; - struct mkroom *br_room; /* * Return immediately if there is no branch to make or we have @@ -1541,14 +1702,13 @@ place_branch( if (!br || gm.made_branch) return; - nhUse(br_room); if (!x) { /* find random coordinates for branch */ /* br_room = find_branch_room(&m); */ (void) find_branch_room(&m); /* sets m via mazexy() or somexy() */ x = m.x; y = m.y; } else { - br_room = pos_to_room(x, y); + (void) pos_to_room(x, y); } if (on_level(&br->end1, &u.uz)) { @@ -1618,10 +1778,10 @@ okdoor(coordxy x, coordxy y) boolean near_door = bydoor(x, y); return ((levl[x][y].typ == HWALL || levl[x][y].typ == VWALL) - && ((isok(x - 1, y) && !IS_ROCK(levl[x - 1][y].typ)) - || (isok(x + 1, y) && !IS_ROCK(levl[x + 1][y].typ)) - || (isok(x, y - 1) && !IS_ROCK(levl[x][y - 1].typ)) - || (isok(x, y + 1) && !IS_ROCK(levl[x][y + 1].typ))) + && ((isok(x - 1, y) && !IS_OBSTRUCTED(levl[x - 1][y].typ)) + || (isok(x + 1, y) && !IS_OBSTRUCTED(levl[x + 1][y].typ)) + || (isok(x, y - 1) && !IS_OBSTRUCTED(levl[x][y - 1].typ)) + || (isok(x, y + 1) && !IS_OBSTRUCTED(levl[x][y + 1].typ))) && !near_door); } diff --git a/src/mkmap.c b/src/mkmap.c index 32c6aef68..fdf50649c 100644 --- a/src/mkmap.c +++ b/src/mkmap.c @@ -10,7 +10,7 @@ staticfn void init_map(schar); staticfn void init_fill(schar, schar); -staticfn schar get_map(int, int, schar); +staticfn schar get_map(coordxy, coordxy, schar); staticfn void pass_one(schar, schar); staticfn void pass_two(schar, schar); staticfn void pass_three(schar, schar); @@ -23,36 +23,36 @@ void mkmap(lev_init *); staticfn void init_map(schar bg_typ) { - int i, j; + coordxy x, y; - for (i = 1; i < COLNO; i++) - for (j = 0; j < ROWNO; j++) { - levl[i][j].roomno = NO_ROOM; - levl[i][j].typ = bg_typ; - levl[i][j].lit = FALSE; + for (x = 1; x < COLNO; x++) + for (y = 0; y < ROWNO; y++) { + levl[x][y].roomno = NO_ROOM; + levl[x][y].typ = bg_typ; + levl[x][y].lit = FALSE; } } staticfn void init_fill(schar bg_typ, schar fg_typ) { - int i, j; + coordxy x, y; long limit, count; limit = (WIDTH * HEIGHT * 2) / 5; count = 0; while (count < limit) { - i = rn1(WIDTH - 1, 2); - j = rnd(HEIGHT - 1); - if (levl[i][j].typ == bg_typ) { - levl[i][j].typ = fg_typ; + x = (coordxy) rn1(WIDTH - 1, 2); + y = (coordxy) rnd(HEIGHT - 1); + if (levl[x][y].typ == bg_typ) { + levl[x][y].typ = fg_typ; count++; } } } staticfn schar -get_map(int col, int row, schar bg_typ) +get_map(coordxy col, coordxy row, schar bg_typ) { if (col <= 0 || row < 0 || col > WIDTH || row >= HEIGHT) return bg_typ; @@ -67,13 +67,13 @@ staticfn const int dirs[16] = { staticfn void pass_one(schar bg_typ, schar fg_typ) { - int i, j; + coordxy x, y; short count, dr; - for (i = 2; i <= WIDTH; i++) - for (j = 1; j < HEIGHT; j++) { + for (x = 2; x <= WIDTH; x++) + for (y = 1; y < HEIGHT; y++) { for (count = 0, dr = 0; dr < 8; dr++) - if (get_map(i + dirs[dr * 2], j + dirs[(dr * 2) + 1], bg_typ) + if (get_map(x + dirs[dr * 2], y + dirs[(dr * 2) + 1], bg_typ) == fg_typ) count++; @@ -81,13 +81,13 @@ pass_one(schar bg_typ, schar fg_typ) case 0: /* death */ case 1: case 2: - levl[i][j].typ = bg_typ; + levl[x][y].typ = bg_typ; break; case 5: case 6: case 7: case 8: - levl[i][j].typ = fg_typ; + levl[x][y].typ = fg_typ; break; default: break; @@ -100,47 +100,47 @@ pass_one(schar bg_typ, schar fg_typ) staticfn void pass_two(schar bg_typ, schar fg_typ) { - int i, j; + coordxy x, y; short count, dr; - for (i = 2; i <= WIDTH; i++) - for (j = 1; j < HEIGHT; j++) { + for (x = 2; x <= WIDTH; x++) + for (y = 1; y < HEIGHT; y++) { for (count = 0, dr = 0; dr < 8; dr++) - if (get_map(i + dirs[dr * 2], j + dirs[(dr * 2) + 1], bg_typ) + if (get_map(x + dirs[dr * 2], y + dirs[(dr * 2) + 1], bg_typ) == fg_typ) count++; if (count == 5) - new_loc(i, j) = bg_typ; + new_loc(x, y) = bg_typ; else - new_loc(i, j) = get_map(i, j, bg_typ); + new_loc(x, y) = get_map(x, y, bg_typ); } - for (i = 2; i <= WIDTH; i++) - for (j = 1; j < HEIGHT; j++) - levl[i][j].typ = new_loc(i, j); + for (x = 2; x <= WIDTH; x++) + for (y = 1; y < HEIGHT; y++) + levl[x][y].typ = new_loc(x, y); } staticfn void pass_three(schar bg_typ, schar fg_typ) { - int i, j; + coordxy x, y; short count, dr; - for (i = 2; i <= WIDTH; i++) - for (j = 1; j < HEIGHT; j++) { + for (x = 2; x <= WIDTH; x++) + for (y = 1; y < HEIGHT; y++) { for (count = 0, dr = 0; dr < 8; dr++) - if (get_map(i + dirs[dr * 2], j + dirs[(dr * 2) + 1], bg_typ) + if (get_map(x + dirs[dr * 2], y + dirs[(dr * 2) + 1], bg_typ) == fg_typ) count++; if (count < 3) - new_loc(i, j) = bg_typ; + new_loc(x, y) = bg_typ; else - new_loc(i, j) = get_map(i, j, bg_typ); + new_loc(x, y) = get_map(x, y, bg_typ); } - for (i = 2; i <= WIDTH; i++) - for (j = 1; j < HEIGHT; j++) - levl[i][j].typ = new_loc(i, j); + for (x = 2; x <= WIDTH; x++) + for (y = 1; y < HEIGHT; y++) + levl[x][y].typ = new_loc(x, y); } /* @@ -151,14 +151,13 @@ pass_three(schar bg_typ, schar fg_typ) */ void flood_fill_rm( - int sx, - int sy, + coordxy sx, + coordxy sy, int rmno, boolean lit, boolean anyroom) { - int i; - int nx; + coordxy i, nx; schar fg_typ = levl[sx][sy].typ; /* back up to find leftmost uninitialized location */ @@ -179,7 +178,7 @@ flood_fill_rm( levl[i][sy].lit = lit; if (anyroom) { /* add walls to room as well */ - int ii, jj; + coordxy ii, jj; for (ii = (i == sx ? i - 1 : i); ii <= i + 1; ii++) for (jj = sy - 1; jj <= sy + 1; jj++) if (isok(ii, jj) && (IS_WALL(levl[ii][jj].typ) @@ -260,19 +259,18 @@ join_map(schar bg_typ, schar fg_typ) { struct mkroom *croom, *croom2; - int i, j; - int sx, sy; + coordxy x, y, sx, sy; coord sm, em; /* first, use flood filling to find all of the regions that need joining */ - for (i = 2; i <= WIDTH; i++) - for (j = 1; j < HEIGHT; j++) { - if (levl[i][j].typ == fg_typ && levl[i][j].roomno == NO_ROOM) { - gm.min_rx = gm.max_rx = i; - gm.min_ry = gm.max_ry = j; + for (x = 2; x <= WIDTH; x++) + for (y = 1; y < HEIGHT; y++) { + if (levl[x][y].typ == fg_typ && levl[x][y].roomno == NO_ROOM) { + gm.min_rx = gm.max_rx = x; + gm.min_ry = gm.max_ry = y; gn.n_loc_filled = 0; - flood_fill_rm(i, j, svn.nroom + ROOMOFFSET, FALSE, FALSE); + flood_fill_rm(x, y, svn.nroom + ROOMOFFSET, FALSE, FALSE); if (gn.n_loc_filled > 3) { add_room(gm.min_rx, gm.min_ry, gm.max_rx, gm.max_ry, FALSE, OROOM, TRUE); @@ -315,7 +313,7 @@ join_map(schar bg_typ, schar fg_typ) em.y = croom2->ly + ((croom2->hy - croom2->ly) / 2); } - (void) dig_corridor(&sm, &em, FALSE, fg_typ, bg_typ); + (void) dig_corridor(&sm, &em, NULL, FALSE, fg_typ, bg_typ); /* choose next region to join */ /* only increment croom if croom and croom2 are non-overlapping */ @@ -337,30 +335,30 @@ finish_map( boolean walled, boolean icedpools) { - int i, j; + coordxy x, y; if (walled) wallify_map(1, 0, COLNO-1, ROWNO-1); if (lit) { - for (i = 1; i < COLNO; i++) - for (j = 0; j < ROWNO; j++) - if ((!IS_ROCK(fg_typ) && levl[i][j].typ == fg_typ) - || (!IS_ROCK(bg_typ) && levl[i][j].typ == bg_typ) - || (bg_typ == TREE && levl[i][j].typ == bg_typ) - || (walled && IS_WALL(levl[i][j].typ))) - levl[i][j].lit = TRUE; - for (i = 0; i < svn.nroom; i++) - svr.rooms[i].rlit = 1; + for (x = 1; x < COLNO; x++) + for (y = 0; y < ROWNO; y++) + if ((!IS_OBSTRUCTED(fg_typ) && levl[x][y].typ == fg_typ) + || (!IS_OBSTRUCTED(bg_typ) && levl[x][y].typ == bg_typ) + || (bg_typ == TREE && levl[x][y].typ == bg_typ) + || (walled && IS_WALL(levl[x][y].typ))) + levl[x][y].lit = TRUE; + for (x = 0; x < svn.nroom; x++) + svr.rooms[x].rlit = 1; } /* light lava even if everything's otherwise unlit; ice might be frozen pool rather than frozen moat */ - for (i = 1; i < COLNO; i++) - for (j = 0; j < ROWNO; j++) { - if (levl[i][j].typ == LAVAPOOL) - levl[i][j].lit = TRUE; - else if (levl[i][j].typ == ICE) - levl[i][j].icedpool = icedpools ? ICED_POOL : ICED_MOAT; + for (x = 1; x < COLNO; x++) + for (y = 0; y < ROWNO; y++) { + if (levl[x][y].typ == LAVAPOOL) + levl[x][y].lit = TRUE; + else if (levl[x][y].typ == ICE) + levl[x][y].icedpool = icedpools ? ICED_POOL : ICED_MOAT; } } @@ -378,7 +376,7 @@ finish_map( * region are all set. */ void -remove_rooms(int lx, int ly, int hx, int hy) +remove_rooms(coordxy lx, coordxy ly, coordxy hx, coordxy hy) { int i; struct mkroom *croom; @@ -415,7 +413,7 @@ remove_room(unsigned int roomno) { struct mkroom *croom = &svr.rooms[roomno]; struct mkroom *maxroom = &svr.rooms[--svn.nroom]; - int i, j; + coordxy x, y; unsigned oroomno; if (croom != maxroom) { @@ -427,10 +425,10 @@ remove_room(unsigned int roomno) /* since maxroom moved, update affected level roomno values */ oroomno = svn.nroom + ROOMOFFSET; roomno += ROOMOFFSET; - for (i = croom->lx; i <= croom->hx; ++i) - for (j = croom->ly; j <= croom->hy; ++j) { - if (levl[i][j].roomno == oroomno) - levl[i][j].roomno = roomno; + for (x = croom->lx; x <= croom->hx; ++x) + for (y = croom->ly; y <= croom->hy; ++y) { + if (levl[x][y].roomno == oroomno) + levl[x][y].roomno = roomno; } } diff --git a/src/mkmaze.c b/src/mkmaze.c index d485dabdf..9c665a37e 100644 --- a/src/mkmaze.c +++ b/src/mkmaze.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mkmaze.c $NHDT-Date: 1712454188 2024/04/07 01:43:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.163 $ */ +/* NetHack 3.7 mkmaze.c $NHDT-Date: 1745114235 2025/04/19 17:57:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.179 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Pasi Kallinen, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -6,6 +6,7 @@ #include "hack.h" #include "sp_lev.h" +#ifndef SFCTOOL staticfn int iswall(coordxy, coordxy); staticfn int iswall_or_stone(coordxy, coordxy); staticfn boolean is_solid(coordxy, coordxy); @@ -26,7 +27,6 @@ staticfn void stolen_booty(void); staticfn boolean maze_inbounds(coordxy, coordxy); staticfn void maze_remove_deadends(xint16); staticfn void populate_maze(void); -staticfn boolean is_exclusion_zone(xint16, coordxy, coordxy); /* adjust a coordinate one step in the specified direction */ #define mz_move(X, Y, dir) \ @@ -77,21 +77,32 @@ boolean set_levltyp(coordxy x, coordxy y, schar newtyp) { if (isok(x, y) && newtyp >= STONE && newtyp < MAX_TYPE) { - if (CAN_OVERWRITE_TERRAIN(levl[x][y].typ)) { - schar oldtyp = levl[x][y].typ; - boolean was_ice = (levl[x][y].typ == ICE); + schar oldtyp = levl[x][y].typ; + + /* hack for secret doors in garden theme rooms */ + if (oldtyp == SDOOR && newtyp == AIR) { + /* levl[][].typ stays SDOOR rather than change to AIR */ + levl[x][y].arboreal_sdoor = 1; + return TRUE; + } + + if (CAN_OVERWRITE_TERRAIN(oldtyp)) { + /* typ==ICE || (typ==DRAWBRIDGE_UP && drawbridgemask==DB_ICE) */ + boolean was_ice = is_ice(x, y); levl[x][y].typ = newtyp; /* TODO? * if oldtyp used flags or horizontal differently from - * from the way newtyp will use them, clear them. + * the way newtyp will use them, clear them. */ - if (IS_LAVA(newtyp)) + if (IS_LAVA(newtyp)) /* [what about IS_LAVA(oldtyp)=>.lit = 0?] */ levl[x][y].lit = 1; - - if (was_ice && newtyp != ICE) + if (was_ice && newtyp != ICE) { + /* frozen corpses resume rotting, no more ice to melt away */ + obj_ice_effects(x, y, TRUE); spot_stop_timers(x, y, MELT_ICE_AWAY); + } if ((IS_FOUNTAIN(oldtyp) != IS_FOUNTAIN(newtyp)) || (IS_SINK(oldtyp) != IS_SINK(newtyp))) count_level_features(); /* level.flags.nfountains,nsinks */ @@ -302,7 +313,7 @@ maze0xy(coord *cc) return; } -staticfn boolean +boolean is_exclusion_zone(xint16 type, coordxy x, coordxy y) { struct exclusion_zone *ez = sve.exclusion_zones; @@ -586,6 +597,7 @@ fixup_special(void) sp = find_level(r->rname.str); lev = sp->dlevel; } + FALLTHROUGH; /*FALLTHRU*/ case LR_UPSTAIR: @@ -1006,7 +1018,6 @@ create_maze(int corrwid, int wallthick, boolean rmdeadends) mx = (x % 2) ? corrwid : (x == 2 || x == rdx * 2) ? 1 : wallthick; ry = y = 2; while (ry < gy.y_maze_max) { - dx = dy = 0; my = (y % 2) ? corrwid : (y == 2 || y == rdy * 2) ? 1 : wallthick; @@ -1319,8 +1330,8 @@ mazexy(coord *cc) x = rnd(gx.x_maze_max); y = rnd(gy.y_maze_max); if (levl[x][y].typ == allowedtyp) { - cc->x = (coordxy) x; - cc->y = (coordxy) y; + cc->x = x; + cc->y = y; return; } } while (++cpt < 100); @@ -1328,8 +1339,8 @@ mazexy(coord *cc) for (x = 1; x <= gx.x_maze_max; x++) for (y = 1; y <= gy.y_maze_max; y++) if (levl[x][y].typ == allowedtyp) { - cc->x = (coordxy) x; - cc->y = (coordxy) y; + cc->x = x; + cc->y = y; return; } /* every spot on the area of map allowed for mazes has been rejected */ @@ -1438,7 +1449,8 @@ bound_digging(void) for (x = 0; x < COLNO; x++) for (y = 0; y < ROWNO; y++) - if (y <= ymin || y >= ymax || x <= xmin || x >= xmax) + if (IS_STWALL(levl[x][y].typ) + && (y <= ymin || y >= ymax || x <= xmin || x >= xmax)) levl[x][y].wall_info |= W_NONDIGGABLE; } @@ -1633,7 +1645,7 @@ movebubbles(void) for (x = 1; x <= (COLNO - 1); x++) for (y = 0; y <= (ROWNO - 1); y++) { levl[x][y] = air_pos; - unblock_point(x, y); + recalc_block_point(x, y); /* all air or all cloud around the perimeter of the Air level tends to look strange; break up the pattern */ xedge = (boolean) (x < gbxmin || x > gbxmax); @@ -1709,25 +1721,23 @@ save_waterlevel(NHFILE *nhfp) if (!svb.bbubbles) return; - if (perform_bwrite(nhfp)) { + if (update_file(nhfp)) { int n = 0; for (b = svb.bbubbles; b; b = b->next) ++n; - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &n, sizeof(int)); - bwrite(nhfp->fd, (genericptr_t) &svx.xmin, sizeof(int)); - bwrite(nhfp->fd, (genericptr_t) &svy.ymin, sizeof(int)); - bwrite(nhfp->fd, (genericptr_t) &svx.xmax, sizeof(int)); - bwrite(nhfp->fd, (genericptr_t) &svy.ymax, sizeof(int)); - } + Sfo_int(nhfp, &n, "waterlevel-bubble_count"); + Sfo_int(nhfp, &svx.xmin, "waterlevel-xmin"); + Sfo_int(nhfp, &svy.ymin, "waterlevel-ymin"); + Sfo_int(nhfp, &svx.xmax, "waterlevel-xmax"); + Sfo_int(nhfp, &svy.ymax, "waterlevel-ymax"); for (b = svb.bbubbles; b; b = b->next) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) b, sizeof(struct bubble)); + Sfo_bubble(nhfp, b, "waterlevel-bubble"); } } if (release_data(nhfp)) unsetup_waterlevel(); } +#endif /* !SFCTOOL */ /* restoring air bubbles on Plane of Water or clouds on Plane of Air */ void @@ -1736,20 +1746,20 @@ restore_waterlevel(NHFILE *nhfp) struct bubble *b = (struct bubble *) 0, *btmp; int i, n = 0; +#ifdef SFCTOOL svb.bbubbles = (struct bubble *) 0; - set_wportal(); - if (nhfp->structlevel) { - mread(nhfp->fd,(genericptr_t) &n, sizeof (int)); - mread(nhfp->fd,(genericptr_t) &svx.xmin, sizeof (int)); - mread(nhfp->fd,(genericptr_t) &svy.ymin, sizeof (int)); - mread(nhfp->fd,(genericptr_t) &svx.xmax, sizeof (int)); - mread(nhfp->fd,(genericptr_t) &svy.ymax, sizeof (int)); - } + /* set_wportal(); */ +#endif + + Sfi_int(nhfp, &n, "waterlevel-bubble_count"); + Sfi_int(nhfp, &svx.xmin, "waterlevel-xmin"); + Sfi_int(nhfp, &svy.ymin, "waterlevel-ymin"); + Sfi_int(nhfp, &svx.xmax, "waterlevel-xmax"); + Sfi_int(nhfp, &svy.ymax, "waterlevel-ymax"); for (i = 0; i < n; i++) { btmp = b; b = (struct bubble *) alloc((unsigned) sizeof *b); - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) b, (unsigned) sizeof *b); + Sfi_bubble(nhfp, b, "waterlevel-bubble"); if (btmp) { btmp->next = b; b->prev = btmp; @@ -1757,8 +1767,11 @@ restore_waterlevel(NHFILE *nhfp) svb.bbubbles = b; b->prev = (struct bubble *) 0; } +#ifndef SFCTOOL mv_bubble(b, 0, 0, TRUE); +#endif } +#ifndef SFCTOOL ge.ebubbles = b; if (b) { b->next = (struct bubble *) 0; @@ -1775,8 +1788,10 @@ restore_waterlevel(NHFILE *nhfp) : "air bubbles or clouds"); program_state.something_worth_saving = 1; } +#endif } +#ifndef SFCTOOL staticfn void set_wportal(void) { @@ -2070,6 +2085,7 @@ mv_bubble(struct bubble *b, coordxy dx, coordxy dy, boolean ini) break; case 3: b->dy = -b->dy; + FALLTHROUGH; /*FALLTHRU*/ case 2: b->dx = -b->dx; @@ -2083,5 +2099,6 @@ mv_bubble(struct bubble *b, coordxy dx, coordxy dy, boolean ini) } } } +#endif /* !SFCTOOL */ /*mkmaze.c*/ diff --git a/src/mkobj.c b/src/mkobj.c index 73126264e..a7687ca8a 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mkobj.c $NHDT-Date: 1725138481 2024/08/31 21:08:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.304 $ */ +/* NetHack 3.7 mkobj.c $NHDT-Date: 1737528890 2025/01/21 22:54:50 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.315 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -9,7 +9,7 @@ staticfn boolean may_generate_eroded(struct obj *); staticfn void mkobj_erosions(struct obj *); staticfn void mkbox_cnts(struct obj *); staticfn unsigned nextoid(struct obj *, struct obj *); -staticfn void mksobj_init(struct obj *, boolean); +staticfn void mksobj_init(struct obj **, boolean); staticfn int item_on_ice(struct obj *); staticfn void shrinking_glob_gone(struct obj *); staticfn void obj_timer_checks(struct obj *, coordxy, coordxy, int); @@ -325,6 +325,7 @@ mkbox_cnts(struct obj *box) n = 0; break; } + FALLTHROUGH; /*FALLTHRU*/ case BAG_OF_HOLDING: n = 1; @@ -457,8 +458,11 @@ splitobj(struct obj *obj, long num) { struct obj *otmp; + /* can't split containers */ if (obj->cobj || num <= 0L || obj->quan <= num) - panic("splitobj"); /* can't split containers */ + panic("splitobj [cobj=%s num=%ld quan=%ld]", + obj->cobj ? "non-empty container" : "(null)", num, obj->quan); + otmp = newobj(); *otmp = *obj; /* copies whole structure */ otmp->oextra = (struct oextra *) 0; @@ -521,7 +525,7 @@ next_ident(void) uses 16-bit 'int'), just live with that and hope no o_id conflicts between objects or m_id conflicts between monsters arise */ if (!svc.context.ident) - svc.context.ident = rnd(2); + svc.context.ident = rnd(2) + 1; /* id 1 is reserved */ return res; } @@ -851,6 +855,7 @@ unknow_object(struct obj *obj) obj->bknown = obj->rknown = 0; obj->cknown = obj->lknown = 0; + obj->tknown = 0; /* for an existing object, awareness of charges or enchantment has gone poof... [object types which don't use the known flag have it set True for some reason] */ @@ -859,9 +864,10 @@ unknow_object(struct obj *obj) /* do some initialization to newly created object; otyp must already be set */ staticfn void -mksobj_init(struct obj *otmp, boolean artif) +mksobj_init(struct obj **obj, boolean artif) { int mndx, tryct; + struct obj *otmp = *obj; char let = objects[otmp->otyp].oc_class; switch (let) { @@ -878,8 +884,11 @@ mksobj_init(struct obj *otmp, boolean artif) if (is_poisonable(otmp) && !rn2(100)) otmp->opoisoned = 1; - if (artif && !rn2(20 + (10 * nartifact_exist()))) - otmp = mk_artifact(otmp, (aligntyp) A_NONE); + if (artif && !rn2(20 + (10 * nartifact_exist()))) { + /* mk_artifact() with otmp and A_NONE will never return NULL */ + otmp = mk_artifact(otmp, (aligntyp) A_NONE, 99, TRUE); + *obj = otmp; + } break; case FOOD_CLASS: otmp->oeaten = 0; @@ -999,6 +1008,8 @@ mksobj_init(struct obj *otmp, boolean artif) case LARGE_BOX: otmp->olocked = !!(rn2(5)); otmp->otrapped = !(rn2(10)); + otmp->tknown = otmp->otrapped && !rn2(100); /* obvious trap */ + FALLTHROUGH; /*FALLTHRU*/ case ICE_BOX: case SACK: @@ -1081,8 +1092,11 @@ mksobj_init(struct obj *otmp, boolean artif) otmp->spe = rne(3); } else blessorcurse(otmp, 10); - if (artif && !rn2(40 + (10 * nartifact_exist()))) - otmp = mk_artifact(otmp, (aligntyp) A_NONE); + if (artif && !rn2(40 + (10 * nartifact_exist()))) { + /* mk_artifact() with otmp and A_NONE will never return NULL */ + otmp = mk_artifact(otmp, (aligntyp) A_NONE, 99, TRUE); + *obj = otmp; + } /* simulate lacquered armor for samurai */ if (Role_if(PM_SAMURAI) && otmp->otyp == SPLINT_MAIL && (svm.moves <= 1 || In_quest(&u.uz))) { @@ -1097,7 +1111,7 @@ mksobj_init(struct obj *otmp, boolean artif) break; case WAND_CLASS: if (otmp->otyp == WAN_WISHING) - otmp->spe = rnd(3); + otmp->spe = 1; else otmp->spe = rn1(5, (objects[otmp->otyp].oc_dir == NODIR) ? 11 : 4); @@ -1149,6 +1163,8 @@ mksobj_init(struct obj *otmp, boolean artif) } mkobj_erosions(otmp); + if (permapoisoned(otmp)) + otmp->opoisoned = 1; } /* mksobj(): create a specific type of object; result is always non-Null */ @@ -1172,7 +1188,7 @@ mksobj(int otyp, boolean init, boolean artif) otmp->pickup_prev = 0; if (init) - mksobj_init(otmp, artif); + mksobj_init(&otmp, artif); /* some things must get done (corpsenm, timers) even if init = 0 */ switch ((otmp->oclass == POTION_CLASS && otmp->otyp != POT_OIL) @@ -1184,6 +1200,7 @@ mksobj(int otyp, boolean init, boolean artif) if (svm.mvitals[otmp->corpsenm].mvflags & (G_NOCORPSE | G_GONE)) otmp->corpsenm = gu.urole.mnum; } + FALLTHROUGH; /*FALLTHRU*/ case STATUE: case FIGURINE: @@ -1197,6 +1214,7 @@ mksobj(int otyp, boolean init, boolean artif) : is_male(ptr) ? CORPSTAT_MALE : rn2(2) ? CORPSTAT_FEMALE : CORPSTAT_MALE); } + FALLTHROUGH; /*FALLTHRU*/ case EGG: /* case TIN: */ @@ -1210,6 +1228,7 @@ mksobj(int otyp, boolean init, boolean artif) break; case POT_OIL: otmp->age = MAX_OIL_IN_FLASK; /* amount of oil */ + FALLTHROUGH; /*FALLTHRU*/ case POT_WATER: /* POTION_CLASS */ otmp->fromsink = 0; /* overloads corpsenm, which was set to NON_PM */ @@ -1224,12 +1243,50 @@ mksobj(int otyp, boolean init, boolean artif) } /* unique objects may have an associated artifact entry */ - if (objects[otyp].oc_unique && !otmp->oartifact) - otmp = mk_artifact(otmp, (aligntyp) A_NONE); + if (objects[otyp].oc_unique && !otmp->oartifact) { + /* mk_artifact() with otmp and A_NONE will never return NULL */ + otmp = mk_artifact(otmp, (aligntyp) A_NONE, 99, FALSE); + } otmp->owt = weight(otmp); return otmp; } +/* potential mimic shapes that should be undone by stone-to-flesh; + not used for objects that will be transformed when hit by stone-to-flesh */ +boolean +stone_object_type(unsigned mappearance) +{ + int otyp = (int) mappearance; + + /* we exclude wands, rings, and gems even though some qualify as stone; + there aren't any weapons or armor classified as made out of stone */ + return (otyp == BOULDER || otyp == STATUE || otyp == FIGURINE); +} + +/* possible mimic shapes that are affected by stone-to-flesh; + mappearance for furniture is a display symbol rather than a terrain type */ +boolean +stone_furniture_type(unsigned mappearance) +{ + int sym = (int) mappearance; + + switch (sym) { + case S_upstair: + case S_dnstair: + case S_brupstair: + case S_brdnstair: + case S_altar: + case S_throne: + case S_sink: /* stone sink is iffy; metal might be more appropriate */ + return TRUE; + default: + if (sym >= S_vwall && sym <= S_trwall) + return TRUE; + break; + } + return FALSE; +} + /* * Several areas of the code made direct reassignments * to obj->corpsenm. Because some special handling is @@ -2097,6 +2154,7 @@ save_mtraits(struct obj *obj, struct monst *mtmp) mtmp2->nmon = (struct monst *) 0; mtmp2->data = (struct permonst *) 0; mtmp2->minvent = (struct obj *) 0; + MON_NOWEP(mtmp2); /* mtmp2->mw = (struct obj *) 0; */ if (mtmp->mextra) copy_mextra(mtmp2, mtmp); /* if mtmp is a long worm with segments, its saved traits will @@ -2433,12 +2491,11 @@ remove_object(struct obj *otmp) coordxy y = otmp->oy; if (otmp->where != OBJ_FLOOR) - panic("remove_object: obj not on floor"); + panic("remove_object: obj where=%d, not on floor", otmp->where); extract_nexthere(otmp, &svl.level.objects[x][y]); extract_nobj(otmp, &fobj); - /* update vision iff this was the only boulder at its spot */ - if (otmp->otyp == BOULDER && !sobj_at(BOULDER, x, y)) - unblock_point(x, y); /* vision */ + if (otmp->otyp == BOULDER) + recalc_block_point(x, y); /* vision */ if (otmp->timed) obj_timer_checks(otmp, x, y, 0); } @@ -2509,7 +2566,7 @@ obj_extract_self(struct obj *obj) extract_nobj(obj, &gb.billobjs); break; default: - panic("obj_extract_self"); + panic("obj_extract_self, where=%d", obj->where); break; } } @@ -2573,7 +2630,7 @@ add_to_minv(struct monst *mon, struct obj *obj) struct obj *otmp; if (obj->where != OBJ_FREE) - panic("add_to_minv: obj not free"); + panic("add_to_minv: obj where=%d, not free", obj->where); /* merge if possible */ for (otmp = mon->minvent; otmp; otmp = otmp->nobj) @@ -2601,7 +2658,7 @@ add_to_container(struct obj *container, struct obj *obj) struct obj *otmp; if (obj->where != OBJ_FREE) - panic("add_to_container: obj not free"); + panic("add_to_container: obj where=%d, not free", obj->where); if (container->where != OBJ_INVENT && container->where != OBJ_MINVENT) obj_no_longer_held(obj); @@ -2621,7 +2678,7 @@ void add_to_migration(struct obj *obj) { if (obj->where != OBJ_FREE) - panic("add_to_migration: obj not free"); + panic("add_to_migration: obj where=%d, not free", obj->where); if (obj->unpaid) /* caller should have changed unpaid item to stolen */ impossible("unpaid object migrating to another level? [%s]", @@ -2643,7 +2700,7 @@ void add_to_buried(struct obj *obj) { if (obj->where != OBJ_FREE) - panic("add_to_buried: obj not free"); + panic("add_to_buried: obj where=%d, not free", obj->where); obj->where = OBJ_BURIED; obj->nobj = svl.level.buriedobjlist; @@ -2667,6 +2724,8 @@ container_weight(struct obj *object) void dealloc_obj(struct obj *obj) { + if (obj->otyp == BOULDER) + obj->next_boulder = 0; if (obj->where == OBJ_DELETED) { impossible("dealloc_obj: obj already deleted (type=%d)", obj->otyp); return; @@ -2704,6 +2763,10 @@ dealloc_obj(struct obj *obj) gt.thrownobj = 0; if (obj == gk.kickedobj) gk.kickedobj = 0; + if (obj == svc.context.tin.tin) { + svc.context.tin.tin = (struct obj *) 0; + svc.context.tin.o_id = 0; + } /* if obj came from the most recent splitobj(), it's no longer eligible for unsplitobj(); perform inline clear_splitobjs() */ @@ -2716,10 +2779,15 @@ dealloc_obj(struct obj *obj) obj->where = OBJ_LUAFREE; return; } - /* mark object as deleted, put it into queue to be freed */ - obj->where = OBJ_DELETED; - obj->nobj = go.objs_deleted; - go.objs_deleted = obj; + if (!program_state.freeingdata) { + /* mark object as deleted, put it into queue to be freed */ + obj->where = OBJ_DELETED; + obj->nobj = go.objs_deleted; + go.objs_deleted = obj; + } else { + /* when saving, there's no need to stage deletions on objs_deleted */ + dealloc_obj_real(obj); + } } /* actually deallocate the object */ @@ -2745,12 +2813,12 @@ dobjsfree(void) struct obj *otmp; while (go.objs_deleted) { - otmp = go.objs_deleted->nobj; - if (go.objs_deleted->where != OBJ_DELETED) - panic("dobjsfree: obj where is not OBJ_DELETED"); - obj_extract_self(go.objs_deleted); - dealloc_obj_real(go.objs_deleted); - go.objs_deleted = otmp; + otmp = go.objs_deleted; + go.objs_deleted = otmp->nobj; + if (otmp->where != OBJ_DELETED) + panic("dobjsfree: obj where=%d, not OBJ_DELETED", otmp->where); + obj_extract_self(otmp); + dealloc_obj_real(otmp); } } @@ -2982,6 +3050,7 @@ objlist_sanity(struct obj *objlist, int wheretype, const char *mesg) /* note: ball and chain can also be OBJ_FREE, but not across turns so this sanity check shouldn't encounter that */ bc_ok = TRUE; + FALLTHROUGH; /*FALLTHRU*/ default: if ((obj != uchain && obj != uball) || !bc_ok) { @@ -3120,7 +3189,7 @@ mon_obj_sanity(struct monst *monlist, const char *mesg) for (mon = monlist; mon; mon = mon->nmon) { if (DEADMONSTER(mon)) continue; - mwep = MON_WEP(mon); + mwep = MON_WEP(mon); /* mon->mw */ if (mwep) { if (!mcarried(mwep)) insane_object(mwep, mfmt1, mesg, mon); @@ -3140,6 +3209,18 @@ mon_obj_sanity(struct monst *monlist, const char *mesg) if (obj->in_use || obj->bypass || obj->nomerge || (obj->otyp == BOULDER && obj->next_boulder)) insane_obj_bits(obj, mon); + if (obj == mwep) + mwep = (struct obj *) 0; + } + if (mwep) { + /* this is a monster check rather than an object check, but doing + it here avoids making an extra pass through mon's minvent; + if the full pass through that list hasn't reset mwep to Null, + then mwep isn't in that list where it should be */ + impossible("monst (%s: %u) wielding %s (%u) not in %s inventory", + pmname(mon->data, Mgender(mon)), mon->m_id, + safe_typename(mwep->otyp), mwep->o_id, mhis(mon)); + } } } diff --git a/src/mkroom.c b/src/mkroom.c index 61a913e8f..c859b1c0e 100644 --- a/src/mkroom.c +++ b/src/mkroom.c @@ -17,6 +17,7 @@ #include "hack.h" +#ifndef SFCTOOL staticfn boolean isbig(struct mkroom *); staticfn struct mkroom *pick_room(boolean); staticfn void mkshop(void), mkzoo(int), mkswamp(void); @@ -25,8 +26,12 @@ staticfn void mktemple(void); staticfn coord *shrine_pos(int); staticfn struct permonst *morguemon(void); staticfn struct permonst *squadmon(void); +#endif /* SFCTOOL */ + staticfn void save_room(NHFILE *, struct mkroom *); staticfn void rest_room(NHFILE *, struct mkroom *); + +#ifndef SFCTOOL staticfn boolean invalid_shop_shape(struct mkroom *sroom); #define sq(x) ((x) * (x)) @@ -293,7 +298,7 @@ fill_zoo(struct mkroom *sroom) (void) somexyspace(sroom, &mm); tx = mm.x; ty = mm.y; - } while (occupied((coordxy) tx, (coordxy) ty) && --i > 0); + } while (occupied(tx, ty) && --i > 0); throne_placed: mk_zoo_thronemon(tx, ty); break; @@ -845,8 +850,7 @@ save_room(NHFILE *nhfp, struct mkroom *r) * of writing the whole structure. That is I should not write * the gs.subrooms pointers, but who cares ? */ - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) r, sizeof (struct mkroom)); + Sfo_mkroom(nhfp, r, "room-mkroom"); for (i = 0; i < r->nsubrooms; i++) { save_room(nhfp, r->sbrooms[i]); } @@ -861,19 +865,18 @@ save_rooms(NHFILE *nhfp) short i; /* First, write the number of rooms */ - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &svn.nroom, sizeof(svn.nroom)); + Sfo_int(nhfp, &svn.nroom, "room-nroom"); for (i = 0; i < svn.nroom; i++) save_room(nhfp, &svr.rooms[i]); } +#endif /* !SFCTOOL */ staticfn void rest_room(NHFILE *nhfp, struct mkroom *r) { short i; - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) r, sizeof(struct mkroom)); + Sfi_mkroom(nhfp, r, "room-mkroom"); for (i = 0; i < r->nsubrooms; i++) { r->sbrooms[i] = &gs.subrooms[gn.nsubroom]; @@ -891,8 +894,7 @@ rest_rooms(NHFILE *nhfp) { short i; - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) &svn.nroom, sizeof(svn.nroom)); + Sfi_int(nhfp, &svn.nroom, "room-nroom"); gn.nsubroom = 0; for (i = 0; i < svn.nroom; i++) { @@ -903,6 +905,7 @@ rest_rooms(NHFILE *nhfp) gs.subrooms[gn.nsubroom].hx = -1; } +#ifndef SFCTOOL /* convert a display symbol for terrain into topology type; used for remembered terrain when mimics pose as furniture */ int @@ -1091,5 +1094,6 @@ invalid_shop_shape(struct mkroom *sroom) } return FALSE; } +#endif /* !SFCTOOL */ /*mkroom.c*/ diff --git a/src/mon.c b/src/mon.c index 5d7ed1eea..dce75521c 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1,11 +1,10 @@ -/* NetHack 3.7 mon.c $NHDT-Date: 1722365546 2024/07/30 18:52:26 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.584 $ */ +/* NetHack 3.7 mon.c $NHDT-Date: 1753856387 2025/07/29 22:19:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.611 $ */ /* 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" #include "mfndpos.h" -#include staticfn void sanity_check_single_mon(struct monst *, boolean, const char *); staticfn struct obj *make_corpse(struct monst *, unsigned); @@ -21,7 +20,10 @@ staticfn void set_mon_min_mhpmax(struct monst *, int); staticfn void lifesaved_monster(struct monst *); staticfn boolean vamprises(struct monst *); staticfn void logdeadmon(struct monst *, int); +staticfn void anger_quest_guardians(struct monst *); staticfn boolean ok_to_obliterate(struct monst *); +staticfn void m_respond_shrieker(struct monst *); +staticfn void m_respond_medusa(struct monst *); staticfn void qst_guardians_respond(void); staticfn void peacefuls_respond(struct monst *); staticfn void wake_nearto_core(coordxy, coordxy, int, boolean); @@ -861,7 +863,6 @@ make_corpse(struct monst *mtmp, unsigned int corpseflags) case PM_PAGE: case PM_ABBOT: case PM_ACOLYTE: case PM_HUNTER: case PM_THUG: case PM_NINJA: case PM_ROSHI: case PM_GUIDE: case PM_WARRIOR: case PM_APPRENTICE: - /*FALLTHRU*/ #else default: #endif @@ -1021,14 +1022,22 @@ minliquid_core(struct monst *mtmp) pline_mon(mtmp, "%s surrenders to the fire.", Monnam(mtmp)); mondead(mtmp); /* no corpse */ - } else if (cansee(mtmp->mx, mtmp->my)) - pline_mon(mtmp, "%s burns slightly.", - Monnam(mtmp)); + } else if (cansee(mtmp->mx, mtmp->my)) { + pline_mon(mtmp, "%s burns slightly.", Monnam(mtmp)); + } } if (!DEADMONSTER(mtmp)) { - (void) fire_damage_chain(mtmp->minvent, FALSE, FALSE, - mtmp->mx, mtmp->my); - (void) rloc(mtmp, RLOC_MSG); + if (m_in_air(mtmp)) { + ; /* vampshifter in wolf form can revert to vampire lord + * and become a flyer so not need to teleport */ + } else if (likes_lava(mtmp->data)) { + ; /* likes_lava case is hypothetical */ + } else { + (void) fire_damage_chain(mtmp->minvent, FALSE, FALSE, + mtmp->mx, mtmp->my); + if (!rloc(mtmp, RLOC_MSG)) + deal_with_overcrowding(mtmp); + } return 0; } return 1; @@ -1064,9 +1073,14 @@ minliquid_core(struct monst *mtmp) else xkilled(mtmp, XKILL_NOMSG); if (!DEADMONSTER(mtmp)) { - water_damage_chain(mtmp->minvent, FALSE); - if (!rloc(mtmp, RLOC_NOMSG)) - deal_with_overcrowding(mtmp); + if (m_in_air(mtmp)) { + ; /* vampshifter in wolf form can revert to vampire lord + * and become a flyer so not need to teleport */ + } else { + water_damage_chain(mtmp->minvent, FALSE); + if (!rloc(mtmp, RLOC_NOMSG)) + deal_with_overcrowding(mtmp); + } return 0; } return 1; @@ -1353,9 +1367,7 @@ m_consume_obj(struct monst *mtmp, struct obj *otmp) /* non-pet: Heal up to the object's weight in hp */ if (!ispet && mtmp->mhp < mtmp->mhpmax) { - mtmp->mhp += objects[otmp->otyp].oc_weight; - if (mtmp->mhp > mtmp->mhpmax) - mtmp->mhp = mtmp->mhpmax; + healmon(mtmp, objects[otmp->otyp].oc_weight, 0); } if (Has_contents(otmp)) meatbox(mtmp, otmp); @@ -1400,7 +1412,7 @@ m_consume_obj(struct monst *mtmp, struct obj *otmp) } } if (heal) - mtmp->mhp = mtmp->mhpmax; + healmon(mtmp, mtmp->mhpmax, 0); if ((eyes || heal) && !mtmp->mcansee) mcureblindness(mtmp, canseemon(mtmp)); if (ispet && deadmimic) @@ -1435,8 +1447,9 @@ meatmetal(struct monst *mtmp) otmp = otmp->nexthere) { /* Don't eat indigestible/choking/inappropriate objects */ if ((mtmp->data == &mons[PM_RUST_MONSTER] && !is_rustprone(otmp)) - || (otmp->otyp == AMULET_OF_STRANGULATION) - || (otmp->otyp == RIN_SLOW_DIGESTION)) + || (otmp->otyp == AMULET_OF_STRANGULATION + || otmp->otyp == RIN_SLOW_DIGESTION) + || (otmp->opoisoned && !resists_poison(mtmp))) continue; if (is_metallic(otmp) && !obj_resists(otmp, 5, 95) && touch_artifact(otmp, mtmp)) { @@ -1489,7 +1502,7 @@ meatmetal(struct monst *mtmp) /* monster eats a pile of objects */ int -meatobj(struct monst* mtmp) /* for gelatinous cubes */ +meatobj(struct monst *mtmp) /* for gelatinous cubes */ { struct obj *otmp, *otmp2; struct permonst *ptr, *original_ptr = mtmp->data; @@ -1548,6 +1561,7 @@ meatobj(struct monst* mtmp) /* for gelatinous cubes */ included for emphasis */ || (otmp->otyp == AMULET_OF_STRANGULATION || otmp->otyp == RIN_SLOW_DIGESTION) + || (otmp->opoisoned && !resists_poison(mtmp)) /* cockatrice corpses handled above; this touch_petrifies() check catches eggs */ || (mstoning(otmp) && !resists_ston(mtmp)) @@ -1935,7 +1949,7 @@ can_touch_safely(struct monst *mtmp, struct obj *otmp) * * to support the new pet behavior, this now returns the max # of objects * that a given monster could pick up from a pile. frequently this will be - * otmp->quan, but special cases for 'only one' now exist so. + * otmp->quan, but special cases for 'only one' now exist. * * this will probably cause very amusing behavior with pets and gold coins. * @@ -2168,12 +2182,12 @@ mfndpos( if (nx == x && ny == y) continue; ntyp = levl[nx][ny].typ; - if (IS_ROCK(ntyp) + if (IS_OBSTRUCTED(ntyp) && !((flag & ALLOW_WALL) && may_passwall(nx, ny)) && !((IS_TREE(ntyp) ? treeok : rockok) && may_dig(nx, ny))) continue; /* intelligent peacefuls avoid digging shop/temple walls */ - if (IS_ROCK(ntyp) && rockok + if (IS_OBSTRUCTED(ntyp) && rockok && !mindless(mon->data) && (mon->mpeaceful || mon->mtame) && (*in_rooms(nx, ny, TEMPLE) || *in_rooms(nx, ny, SHOPBASE)) && !(*in_rooms(x, y, TEMPLE) || *in_rooms(x, y, SHOPBASE))) @@ -2190,8 +2204,7 @@ mfndpos( if (IS_DOOR(ntyp) /* an amorphous creature can only move under/through a closed door if it doesn't currently have hero engulfed */ - && !((amorphous(mdat) || can_fog(mon)) - && (mon != u.ustuck || !u.uswallow)) + && !((amorphous(mdat) || can_fog(mon)) && !engulfing_u(mon)) && (((levl[nx][ny].doormask & D_CLOSED) && !(flag & OPENDOOR)) || ((levl[nx][ny].doormask & D_LOCKED) && !(flag & UNLOCKDOOR))) && !thrudoor) @@ -2346,9 +2359,21 @@ mfndpos( staticfn long mm_2way_aggression(struct monst *magr, struct monst *mdef) { - /* zombies vs things that can be zombified */ + /* 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). + + 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) - return (ALLOW_M | ALLOW_TM); + if (!Is_stronghold(&u.uz) && + !unique_corpstat(magr->data) && !unique_corpstat(mdef->data)) + return (ALLOW_M | ALLOW_TM); return 0; } @@ -2569,6 +2594,12 @@ copy_mextra(struct monst *mtmp2, struct monst *mtmp1) assert(has_edog(mtmp2)); *EDOG(mtmp2) = *EDOG(mtmp1); } + if (EBONES(mtmp1)) { + if (!EBONES(mtmp2)) + newebones(mtmp2); + assert(has_ebones(mtmp2)); + *EBONES(mtmp2) = *EBONES(mtmp1); + } if (has_mcorpsenm(mtmp1)) MCORPSENM(mtmp2) = MCORPSENM(mtmp1); } @@ -2591,6 +2622,8 @@ dealloc_mextra(struct monst *m) free((genericptr_t) x->emin), x->emin = 0; if (x->edog) free((genericptr_t) x->edog), x->edog = 0; + if (x->ebones) + free((genericptr_t) x->ebones), x->ebones = 0; x->mcorpsenm = NON_PM; /* no allocation to release */ free((genericptr_t) x); @@ -2636,11 +2669,25 @@ mon_leaving_level(struct monst *mon) remove_monster(mx, my); #if 0 /* mustn't do this; too many places assume that the stale - monst->mx,my values are still valid */ + * monst->mx,my values are still valid */ mon->mx = mon->my = 0; /* off normal map */ #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 */ @@ -2810,15 +2857,20 @@ lifesaved_monster(struct monst *mtmp) } /* when a shape-shifted vampire is killed, it reverts to base form instead - of dying; moved into separate routine to unclutter mondead() */ + of dying; returns True if mtmp successfully revives, False otherwise; + "successfully revived" vampire might be killed by a booby trapped door */ staticfn boolean vamprises(struct monst *mtmp) { int mndx = mtmp->cham; + /* + * Protection from shape changers protects against this because + * the vampire will always be in normal form instead of shifted. + * So there's no need to check for that attribute being active. + */ if (ismnum(mndx) && mndx != monsndx(mtmp->data) && !(svm.mvitals[mndx].mvflags & G_GENOD)) { - coord new_xy; char action[BUFSZ]; /* alternate message phrasing for some monster types */ boolean spec_mon = (nonliving(mtmp->data) @@ -2831,30 +2883,28 @@ vamprises(struct monst *mtmp) /* construct a 'before' argument to pass to pline(); this used to construct a dynamic format string but that's overkill */ - Snprintf(action, sizeof action, "%s suddenly %s and rises as", + Snprintf(action, sizeof action, "%s%s %s%s and rises as", + Unaware ? "you dream that " : "", x_monnam(mtmp, ARTICLE_THE, spec_mon ? (char *) 0 : "seemingly dead", (SUPPRESS_INVISIBLE | AUGMENT_IT), FALSE), + Unaware ? "" : "suddenly ", spec_death ? "reconstitutes" : "transforms"); mtmp->mcanmove = 1; mtmp->mfrozen = 0; set_mon_min_mhpmax(mtmp, 10); /* mtmp->mhpmax=max(m_lev+1,10) */ mtmp->mhp = mtmp->mhpmax; - /* mtmp==u.ustuck can happen if previously a fog cloud - or poly'd hero is hugging a vampire bat */ + /* mtmp==u.ustuck can happen if previously a fog cloud or if + poly'd hero is hugging a vampire bat */ if (mtmp == u.ustuck) { if (u.uswallow) expels(mtmp, mtmp->data, FALSE); else uunstick(); } - /* if fog cloud is on a closed door space, move it to a more - appropriate spot for its intended new form */ - if (amorphous(mtmp->data) && closed_door(mtmp->mx, mtmp->my)) { - if (enexto(&new_xy, mtmp->mx, mtmp->my, &mons[mndx])) - rloc_to(mtmp, new_xy.x, new_xy.y); - } - (void) newcham(mtmp, &mons[mndx], NO_NC_FLAGS); + + if (!newcham(mtmp, &mons[mndx], NO_NC_FLAGS)) + return !DEADMONSTER(mtmp); mtmp->cham = (mtmp->data == &mons[mndx]) ? NON_PM : mndx; if (canspotmon(mtmp)) { @@ -2867,6 +2917,42 @@ vamprises(struct monst *mtmp) FALSE)); gv.vamp_rise_msg = TRUE; } + /* revived vampire is in normal shape, so can't be amorphous; if on + a closed door spot, destroy the door and if trapped, blow it up */ + if (closed_door(x, y)) { + static const char + door_smashed[] = "a door being smashed", + door_go_boom[] = "a door exploding"; + struct rm *door = &levl[x][y]; + boolean trapped = (door->doormask & D_TRAPPED) != 0, + seeit = cansee(x, y); + + set_msg_xy(x, y); /* You()/pline() will reset this */ + if (!seeit) + You_hear("%s.", trapped ? "an explosion" : door_smashed); + else if (!canspotmon(mtmp)) + You_see("%s.", trapped ? door_go_boom : door_smashed); + else if (!Unaware) + pline_The("door is smashed%s", + trapped ? " and it explodes!" : "."); + set_msg_xy(0, 0); /* in case none of the messages was delivered */ + + door->doormask = D_NODOOR; + unblock_point(x, y); + if (trapped) { + boolean trap_killed, save_verbose = flags.verbose; + + flags.verbose = FALSE; /* suppress mb_trapped() messages + * (that makes the 'seeit' arg moot) */ + trap_killed = mb_trapped(mtmp, seeit); + flags.verbose = save_verbose; + /* if the booby trap has killed the monster, mondied() will + have been called but no message about its death given yet; + mtmp was a vampire so use unconditional "destroyed" */ + if (trap_killed && canspotmon(mtmp) && !Unaware) + pline_mon(mtmp, "%s is destroyed!", Monnam(mtmp)); + } + } newsym(x, y); return TRUE; } @@ -2954,7 +3040,13 @@ logdeadmon(struct monst *mtmp, int mndx) } } -#undef livelog_mon_nam +/* anger all the quest guards on the level */ +staticfn void +anger_quest_guardians(struct monst *mtmp) +{ + if (mtmp->data == &mons[gu.urole.guardnum]) + setmangry(mtmp, TRUE); +} /* monster 'mtmp' has died; maybe life-save, otherwise unshapeshift and update vanquished stats and update map */ @@ -3035,7 +3127,8 @@ mondead(struct monst *mtmp) (void) makemon(mtmp->data, stway->sx, stway->sy, NO_MM_FLAGS); break; } - /* fall-through */ + FALLTHROUGH; + /* FALLTHRU */ case 2: /* randomly */ (void) makemon(mtmp->data, 0, 0, NO_MM_FLAGS); break; @@ -3066,6 +3159,9 @@ corpse_chance( struct permonst *mdat = mon->data; int i, tmp; + if (!magr && gm.mswallower && attacktype(gm.mswallower->data, AT_ENGL)) + magr = gm.mswallower, was_swallowed = TRUE; /* for gas spore boom */ + if (mdat == &mons[PM_VLAD_THE_IMPALER] || mdat->mlet == S_LICH) { if (cansee(mon->mx, mon->my) && !was_swallowed) pline_mon(mon, "%s body crumbles into dust.", @@ -3082,7 +3178,10 @@ corpse_chance( tmp = d((int) mdat->mlevel + 1, (int) mdat->mattk[i].damd); else tmp = 0; + if (was_swallowed && magr) { + /* mdef is a gas spore (AT_BOOM) that is exploding inside an + engulfer; suppress usual explosion since it's contained */ if (magr == &gy.youmonst) { There("is an explosion in your %s!", body_part(STOMACH)); Sprintf(svk.killer.name, "%s explosion", @@ -3096,14 +3195,11 @@ corpse_chance( mondied(magr); if (DEADMONSTER(magr)) { /* maybe lifesaved */ if (canspotmon(magr)) - pline_mon(magr, "%s rips open!", - Monnam(magr)); + pline_mon(magr, "%s rips open!", Monnam(magr)); } else if (canseemon(magr)) - pline_mon(magr, - "%s seems to have indigestion.", + pline_mon(magr, "%s seems to have indigestion.", Monnam(magr)); } - return FALSE; } @@ -3556,6 +3652,8 @@ xkilled( change_luck(-20); pline("That was %sa bad idea...", u.uevent.qcompleted ? "probably " : ""); + if (!svc.context.mon_moving) + iter_mons(anger_quest_guardians); } else if (mdat->msound == MS_NEMESIS) { /* Real good! */ if (!svq.quest_status.killed_leader) adjalign((int) (ALIGNLIM / 4)); @@ -3597,10 +3695,24 @@ xkilled( /* malign was already adjusted for u.ualign.type and randomization */ adjalign(mtmp->malign); + +#if 0 /* HARDFOUGHT-only at present */ +#ifdef LIVELOG + if (has_ebones(mtmp)) { + livelog_printf(LL_UMONST, "destroyed %s, %s former %s", + livelog_mon_nam(mtmp), + (mtmp->data == &mons[PM_GHOST]) ? "the" : "and", + rank_of(EBONES(mtmp)->deathlevel, + EBONES(mtmp)->mnum, + EBONES(mtmp)->female)); + } +#endif /* LIVELOG */ +#endif return; } #undef LEVEL_SPECIFIC_NOCORPSE +#undef livelog_mon_nam /* changes the monster into a stone monster of the same type this should only be called when poly_when_stoned() is true */ @@ -3944,36 +4056,50 @@ mnearto( return res; } -/* monster responds to player action; not the same as a passive attack; - assumes reason for response has been tested, and response _must_ be made */ +/* shrieker special action: shriek, maybe summon monster, aggravate */ +staticfn void +m_respond_shrieker(struct monst *mtmp) +{ + if (!Deaf) { + pline("%s shrieks.", Monnam(mtmp)); + stop_occupation(); + } + if (!rn2(10)) { /* 1/10 chance per shriek to create a monster */ + /* new monster has a 1/13 chance to be a purple worm, random + otherwise; baby purple worm if adult is too difficult */ + (void) makemon(rn2(13) ? (struct permonst *) 0 + : &mons[montoostrong(PM_PURPLE_WORM, + monmax_difficulty_lev()) + ? PM_BABY_PURPLE_WORM : PM_PURPLE_WORM], + 0, 0, NO_MM_FLAGS); + } + aggravate(); +} + +/* medusa special action: gaze at hero */ +staticfn void +m_respond_medusa(struct monst *mtmp) +{ + int i; + + for (i = 0; i < NATTK; i++) + if (mtmp->data->mattk[i].aatyp == AT_GAZE) { + (void) gazemu(mtmp, &mtmp->data->mattk[i]); + break; + } +} + +/* monster responds to player action; not the same as a passive attack */ void m_respond(struct monst *mtmp) { - if (mtmp->data->msound == MS_SHRIEK) { - if (!Deaf) { - pline("%s shrieks.", Monnam(mtmp)); - stop_occupation(); - } - if (!rn2(10)) { /* 1/10 chance per shriek to create a monster */ - /* new monster has a 1/13 chance to be a purple worm, random - otherwise; baby purple worm if adult is too difficult */ - (void) makemon(rn2(13) ? (struct permonst *) 0 - : &mons[montoostrong(PM_PURPLE_WORM, - monmax_difficulty_lev()) - ? PM_BABY_PURPLE_WORM : PM_PURPLE_WORM], - 0, 0, NO_MM_FLAGS); - } + if (mtmp->data->msound == MS_SHRIEK && !um_dist(mtmp->mx, mtmp->my, 1)) + m_respond_shrieker(mtmp); + if (mtmp->data == &mons[PM_MEDUSA] && couldsee(mtmp->mx, mtmp->my)) + m_respond_medusa(mtmp); + /* Erinyes will inform surrounding monsters of your crimes */ + if (mtmp->data == &mons[PM_ERINYS] && !mtmp->mpeaceful && m_canseeu(mtmp)) aggravate(); - } - if (mtmp->data == &mons[PM_MEDUSA]) { - int i; - - for (i = 0; i < NATTK; i++) - if (mtmp->data->mattk[i].aatyp == AT_GAZE) { - (void) gazemu(mtmp, &mtmp->data->mattk[i]); - break; - } - } } /* how quest guardians respond when you attack the quest leader */ @@ -4422,9 +4548,47 @@ get_iter_mons_xy( } +/* Heal the given monster by amt hitpoints, unless it is somehow prevented + from healing. "overheal" is the maximum amount by which the max HP will + increase to allow for the healing (the resulting HP caps at max HP + + overheal, and the max HP stays the some unless it needs to increase to + accommodate the new HP). Overhealing the player is not currently + implemented by this method. + + This function should only be used for situations which are conceptually + heals, rather than other situations where a monster's HP is set, so that + "prevent healing" effects work correctly. In particular, it should not + be used for cases where a monster's HP is restored upon revival, or when + a monster is created. It also shouldn't be used for lifesaving, which + overrides "cannot heal" effects. + + amt and overheal must not be negative (0 is allowed, and a very common + amount for overheal). Returns the number of hitpoints healed. */ +int +healmon(struct monst *mtmp, int amt, int overheal) +{ + if (mtmp == &gy.youmonst) { + int oldhp = Upolyd ? u.mh : u.uhp; + healup(amt, 0, 0, 0); + return (Upolyd ? u.mh : u.uhp) - oldhp; + } else { + int oldhp = mtmp->mhp; + if (mtmp->mhp + amt > mtmp->mhpmax + overheal) { + mtmp->mhpmax += overheal; + mtmp->mhp = mtmp->mhpmax; + } else { + mtmp->mhp += amt; + if (mtmp->mhp > mtmp->mhpmax) + mtmp->mhpmax = mtmp->mhp; + } + return mtmp->mhp - oldhp; + } +} + + /* force all chameleons and mimics to become themselves and werecreatures to revert to human form; called when Protection_from_shape_changers gets - activated via wearing or eating ring or wizintrinsics */ + activated via wearing or eating ring or via #wizintrinsic */ void rescham(void) { @@ -4591,6 +4755,7 @@ hideunder(struct monst *mtmp) if (undetected && seenmon && seenobj) { if (!locomo) locomo = locomotion(mtmp->data, "hide"); + set_msg_xy(mtmp->mx, mtmp->my); /* pline() will reset this */ You_see("%s %s under %s.", seenmon, locomo, seenobj); iflags.last_msg = PLNMSG_HIDE_UNDER; gl.last_hider = mtmp->m_id; @@ -4749,12 +4914,17 @@ pickvampshape(struct monst *mon) if (mon_has_special(mon)) break; /* leave mndx as is */ wolfchance = 3; + FALLTHROUGH; /*FALLTHRU*/ case PM_VAMPIRE_LEADER: /* vampire lord or Vlad can become wolf */ - if (!rn2(wolfchance) && !uppercase_only) { + if (!rn2(wolfchance) && !uppercase_only + /* don't pick a walking form if that would lead to immediate + drowning or immolation and reversion to vampire form */ + && !is_pool_or_lava(mon->mx, mon->my)) { mndx = PM_WOLF; break; } + FALLTHROUGH; /*FALLTHRU*/ case PM_VAMPIRE: /* any vampire can become fog or bat */ mndx = (!rn2(4) && !uppercase_only) ? PM_FOG_CLOUD : PM_VAMPIRE_BAT; @@ -4858,6 +5028,7 @@ validvamp(struct monst *mon, int *mndx_p, int monclass) *mndx_p = PM_WOLF; break; } + FALLTHROUGH; /*FALLTHRU*/ default: *mndx_p = NON_PM; @@ -5491,10 +5662,7 @@ golemeffects(struct monst *mon, int damtype, int dam) mon_adjust_speed(mon, -1, (struct obj *) 0); } if (heal) { - if (mon->mhp < mon->mhpmax) { - mon->mhp += heal; - if (mon->mhp > mon->mhpmax) - mon->mhp = mon->mhpmax; + if (healmon(mon, heal, 0)) { if (cansee(mon->mx, mon->my)) pline_mon(mon, "%s seems healthier.", Monnam(mon)); } @@ -5760,4 +5928,116 @@ adj_erinys(unsigned abuse) pm->difficulty = min(10 + (u.ualign.abuse / 3), 25); } +/* mark individual monster type as seen from close-up, + if we haven't seen it nearby before */ +void +see_monster_closeup(struct monst *mtmp, boolean photo) +{ + int mndx; + + if (Hallucination || (Blind && !Blind_telepat)) + return; + + mndx = monsndx(mtmp->data); + if (M_AP_TYPE(mtmp) == M_AP_MONSTER && !sensemon(mtmp)) + mndx = mtmp->mappearance; + if (mndx == PM_LONG_WORM && gn.notonhead) + mndx = PM_LONG_WORM_TAIL; + + if (!svm.mvitals[mndx].seen_close) { + svm.mvitals[mndx].seen_close = 1; + svc.context.lifelist.total_seen_upclose++; + } + + /* hallucinatory monsters don't reach here--they're not recorded; + being able to see invisible doesn't make invisible monsters show up + on photos; likewise, telepathy allows hero to see hidden monsters + but doesn't cause them to appear on photos */ + if (photo && !mtmp->minvis && !mtmp->mundetected + && (M_AP_TYPE(mtmp) == M_AP_NOTHING + || M_AP_TYPE(mtmp) == M_AP_MONSTER)) { + if (M_AP_TYPE(mtmp) == M_AP_MONSTER) /* cloned Wizard of Yendor */ + mndx = mtmp->mappearance; + + if (!svm.mvitals[mndx].photographed) { + svm.mvitals[mndx].photographed = 1; + svc.context.lifelist.total_photographed++; + + /* tourist earns points (toward EXP but not final score) for + the first instance of each type of monster photographed; + worm tail can be photographed but yields no EXP bonus */ + if (Role_if(PM_TOURIST) + /* suppress extra points for photographing the pet that hero + started with (unless it has changed shape due to growing + up or being polymorphed) */ + && (mtmp->m_id != svc.context.startingpet_mid + || mndx != svc.context.startingpet_typ) + /* monsndx() check covers worm tail and also disguised + Wizard of Yendor; experienced() won't yield a reasonable + value for those */ + && mndx == monsndx(mtmp->data)) { + more_experienced(experience(mtmp, 0), 0); + newexplevel(); + } + } + } +} + +/* mark a monster type as seen close-up when we see it next to us */ +void +see_nearby_monsters(void) +{ + struct monst *mtmp; + int mndx; + coordxy x, y; + + if (Hallucination || (Blind && !Blind_telepat)) + return; + + for (x = u.ux - 1; x <= u.ux + 1; x++) + for (y = u.uy - 1; y <= u.uy + 1; y++) { + if (!isok(x, y)) + continue; + if (!(mtmp = m_at(x, y))) + continue; + mndx = monsndx(mtmp->data); + if (M_AP_TYPE(mtmp) == M_AP_MONSTER) + mndx = mtmp->mappearance; + /* skip closeup handling if this mon type has already been done */ + if (svm.mvitals[mndx].seen_close) + continue; + /* disguised mimics pass canseemon(); undetected hiders don't */ + if (canseemon(mtmp) || (mtmp->mundetected && sensemon(mtmp))) { + gb.bhitpos.x = x, gb.bhitpos.y = y; + gn.notonhead = (x != mtmp->mx || y != mtmp->my); + see_monster_closeup(mtmp, FALSE); + } + } +} + +/* monster resists something. + make a shield effect at monster's location and give a message */ +void +shieldeff_mon(struct monst *mtmp) +{ + shieldeff(mtmp->mx, mtmp->my); + /* does not depend on seeing the monster; the shield effect is visible */ + if (cansee(mtmp->mx, mtmp->my)) + pline_mon(mtmp, "%s resists!", Monnam(mtmp)); +} + +void +flash_mon(struct monst *mtmp) +{ + coordxy mx = mtmp->mx, my = mtmp->my; + int count = couldsee(mx, my) ? 8 : 4; + char saveviz = gv.viz_array[my][mx]; + + if (!flags.sparkle) + count /= 2; + gv.viz_array[my][mx] |= (IN_SIGHT | COULD_SEE); + flash_glyph_at(mx, my, mon_to_glyph(mtmp, newsym_rn2), count); + gv.viz_array[my][mx] = saveviz; + newsym(mx, my); +} /*mon.c*/ diff --git a/src/mondata.c b/src/mondata.c index 28da3358a..d7ea80245 100644 --- a/src/mondata.c +++ b/src/mondata.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mondata.c $NHDT-Date: 1711620615 2024/03/28 10:10:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.132 $ */ +/* NetHack 3.7 mondata.c $NHDT-Date: 1738638877 2025/02/03 19:14:37 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.140 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -123,6 +123,79 @@ defended(struct monst *mon, int adtyp) return FALSE; } +/* returns True if monster resists particular elemental damage; + handles 'carry' effects of artifacts as well as worn/wielded items */ +boolean +Resists_Elem(struct monst *mon, int propindx) +{ + struct obj *o; + long slotmask; + boolean is_you = (mon == &gy.youmonst); + int u_resist = 0, damgtype = 0, rsstmask = 0; + + /* + * Main damage/resistance types, mostly matching dragon breath values. + * propindx = property index, fire (1), cold, (2) through stone (8); + * damgtype = damage type, 2 through 9 (0 and 1 aren't used here); + * rsstmask = resistance mask, 1, 2, 4, ..., 64, 128. + */ + + switch (propindx) { + case FIRE_RES: /* 1 */ + case COLD_RES: /* 2 */ + case SLEEP_RES: /* 3 */ + case DISINT_RES: /* 4 */ + case SHOCK_RES: /* 5 */ + case POISON_RES: /* 6 */ + case ACID_RES: /* 7 */ + case STONE_RES: /* 8 */ + damgtype = propindx + 1; /* valid for propindx 1..8, damgtype 2..9 */ + rsstmask = 1 << (propindx - 1); /* valid for propindx 1..8 */ + u_resist = u.uprops[propindx].intrinsic + || u.uprops[propindx].extrinsic; + break; + + /* accept these, but we expect callers to use their routines directly */ + case ANTIMAGIC: + return resists_magm(mon); + case DRAIN_RES: + return resists_drli(mon); + case BLND_RES: + return resists_blnd(mon); + + default: + impossible("Resists_Elem(%d), unexpected property type", propindx); + return FALSE; + } + + if (is_you ? u_resist : ((mon_resistancebits(mon) & rsstmask) != 0)) + return TRUE; + /* check for resistance granted by wielded weapon */ + o = is_you ? uwep : MON_WEP(mon); + if (o && o->oartifact && defends(damgtype, o)) + return TRUE; + /* check for resistance granted by worn or carried items */ + o = is_you ? gi.invent : mon->minvent; + slotmask = W_ARMOR | W_ACCESSORY; + if (!is_you /* assumes monsters don't wield non-weapons */ + || (uwep && (uwep->oclass == WEAPON_CLASS || is_weptool(uwep)))) + slotmask |= W_WEP; + if (is_you && u.twoweap) + slotmask |= W_SWAPWEP; + for (; o; o = o->nobj) + if (((o->owornmask & slotmask) != 0L + && objects[o->otyp].oc_oprop == propindx) + || ((o->owornmask & W_ARMC) == W_ARMC + /* worn apron confers a pair of resistances but + objects[ALCHEMY_SMOCK].oc_oprop can only represent one; + we check both so won't need to know which one that is */ + && o->otyp == ALCHEMY_SMOCK + && (propindx == POISON_RES || propindx == ACID_RES)) + || (o->oartifact && defends_when_carried(damgtype, o))) + return TRUE; + return FALSE; +} + /* returns True if monster is drain-life resistant */ boolean resists_drli(struct monst *mon) @@ -187,7 +260,15 @@ resists_blnd(struct monst *mon) if (dmgtype_fromattack(ptr, AD_BLND, AT_EXPL) || dmgtype_fromattack(ptr, AD_BLND, AT_GAZE)) return TRUE; - return resists_blnd_by_arti(mon); + /* Sunsword */ + if (resists_blnd_by_arti(mon)) + return TRUE; + /* catchall */ + if (is_you && Blnd_resist) { + impossible("'Blnd_resist' but not resists_blnd()?"); + return TRUE; + } + return FALSE; } /* True iff monster is resistant to light-induced blindness due to worn @@ -393,11 +474,17 @@ mstrength(struct permonst *ptr) n += ((int) (ptr->mattk[i].damd * ptr->mattk[i].damn) > 23); } - /* Leprechauns are special cases. They have many hit dice so they - can hit and are hard to kill, but they don't really do much damage. */ + /* Leprechauns are a special case. They have many hit dice so they can + hit and are hard to kill, but they don't really do much damage. */ if (!strcmp(ptr->pmnames[NEUTRAL], "leprechaun")) n -= 2; + /* despite group and poison increments, soldier ants and killer bees are + underestimated by the formula, so have an artificial +1 difficulty */ + if (!strcmp(ptr->pmnames[NEUTRAL], "killer bee") || + !strcmp(ptr->pmnames[NEUTRAL], "soldier ant")) + n += 2; /* +1 after 'tmp += n/2' below */ + /* finally, adjust the monster level 0 <= n <= 24 (approx.) */ if (n == 0) tmp -= 1; diff --git a/src/monmove.c b/src/monmove.c index a56b14fba..8bf665099 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 monmove.c $NHDT-Date: 1722116054 2024/07/27 21:34:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.255 $ */ +/* NetHack 3.7 monmove.c $NHDT-Date: 1737392015 2025/01/20 08:53:35 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.266 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -23,7 +23,7 @@ staticfn int postmov(struct monst *, struct permonst *, coordxy, coordxy, int, unsigned, boolean, boolean, boolean) NONNULLPTRS; staticfn boolean leppie_avoidance(struct monst *); staticfn void leppie_stash(struct monst *); -staticfn boolean m_balks_at_approaching(struct monst *); +staticfn int m_balks_at_approaching(int, struct monst *, int *, int *); staticfn boolean stuff_prevents_passage(struct monst *); staticfn int vamp_shift(struct monst *, struct permonst *, boolean); staticfn void maybe_spin_web(struct monst *); @@ -295,9 +295,8 @@ onscary(coordxy x, coordxy y, struct monst *mtmp) void mon_regen(struct monst *mon, boolean digest_meal) { - if (mon->mhp < mon->mhpmax - && (svm.moves % 20 == 0 || regenerates(mon->data))) - mon->mhp++; + if (svm.moves % 20 == 0 || regenerates(mon->data)) + healmon(mon, 1, 0); if (mon->mspec_used) mon->mspec_used--; if (digest_meal) { @@ -409,6 +408,32 @@ bee_eat_jelly(struct monst *mon, struct obj *obj) return -1; /* a queen is already present; ordinary bee hasn't moved yet */ } +/* gelatinous cube eats something from its inventory */ +static int +gelcube_digests(struct monst *mtmp) +{ + struct obj *otmp = mtmp->minvent; + + if (mtmp->meating || !mtmp->minvent) + return -1; + + while (otmp) { + if (is_organic(otmp) && !otmp->oartifact + && !is_mines_prize(otmp) && !is_soko_prize(otmp)) + break; + otmp = otmp->nobj; + } + + if (!otmp) + return -1; + + mtmp->meating = eaten_stat(mtmp->meating, otmp); + extract_from_minvent(mtmp, otmp, TRUE, TRUE); + m_consume_obj(mtmp, otmp); + return 0; /* used a move */ +} + + /* FIXME: gremlins don't flee from monsters wielding Sunsword or wearing gold dragon scales/mail, nor from gold dragons, only from the hero */ #define flees_light(mon) \ @@ -713,17 +738,8 @@ dochug(struct monst *mtmp) return 0; } - /* Erinyes will inform surrounding monsters of your crimes */ - if (mdat == &mons[PM_ERINYS] && !mtmp->mpeaceful && m_canseeu(mtmp)) - aggravate(); - - /* Shriekers and Medusa have irregular abilities which must be - checked every turn. These abilities do not cost a turn when - used. */ - if (mdat->msound == MS_SHRIEK && !um_dist(mtmp->mx, mtmp->my, 1)) - m_respond(mtmp); - if (mdat == &mons[PM_MEDUSA] && couldsee(mtmp->mx, mtmp->my)) - m_respond(mtmp); + /* some monsters have special abilities */ + m_respond(mtmp); if (DEADMONSTER(mtmp)) return 1; /* m_respond gaze can kill medusa */ @@ -846,6 +862,10 @@ dochug(struct monst *mtmp) && (res = bee_eat_jelly(mtmp, otmp)) >= 0) return res; + if (mdat == &mons[PM_GELATINOUS_CUBE] + && (res = gelcube_digests(mtmp)) >= 0) + return res; + /* A monster that passes the following checks has the opportunity to move. Movement itself is handled by the m_move() function. */ if (!nearby || mtmp->mflee || scared || mtmp->mconf || mtmp->mstun @@ -887,6 +907,7 @@ dochug(struct monst *mtmp) case MMOVE_NOMOVES: if (scared) panicattk = TRUE; + FALLTHROUGH; /*FALLTHRU*/ case MMOVE_NOTHING: /* no movement, but it can still attack you */ case MMOVE_DONE: /* absolutely no movement */ @@ -910,7 +931,8 @@ dochug(struct monst *mtmp) return 0; /* Monsters can move and then shoot on same turn; our hero can't. Is that fair? */ - if (!nearby && (ranged_attk(mdat) + if (!nearby + && (ranged_attk_available(mtmp) || attacktype(mdat, AT_WEAP) || find_offensive(mtmp))) break; @@ -1139,32 +1161,57 @@ leppie_stash(struct monst *mtmp) } } -/* does monster want to avoid you? */ -staticfn boolean -m_balks_at_approaching(struct monst *mtmp) +/* does monster want to avoid you? + * returns the original value of appr if not. + * returns -1 if so. + * returns -2 if monster wants to adhere to a particular range, + * which may actually be further away, + * and sets *pdistmin and *pdistmax to describe that range + */ +staticfn int +m_balks_at_approaching(int oldappr, struct monst *mtmp, int *pdistmin, + int *pdistmax) { + struct obj *mwep = MON_WEP(mtmp); + coordxy x = mtmp->mx, y = mtmp->my, ux = mtmp->mux, uy = mtmp->muy; + int edist = dist2(x, y, ux, uy); + const struct throw_and_return_weapon *arw; + + if (pdistmin) + *pdistmin = 0; + if (pdistmax) + *pdistmax = 0; + /* peaceful, far away, or can't see you */ - if (mtmp->mpeaceful - || (dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) >= 5*5) - || !m_canseeu(mtmp)) - return FALSE; + if (mtmp->mpeaceful || (edist >= 5 * 5) || !m_canseeu(mtmp)) + return oldappr; /* has ammo+launcher */ if (m_has_launcher_and_ammo(mtmp)) - return TRUE; + return -1; /* is using a polearm and in range */ if (MON_WEP(mtmp) && is_pole(MON_WEP(mtmp)) - && dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <= MON_POLE_DIST) - return TRUE; + && edist <= MON_POLE_DIST) + return -1; + + /* is using a throw-and-return weapon; provide min and max preferred range + */ + if (mwep && (arw = autoreturn_weapon(mwep)) != 0) { + if (pdistmin) + *pdistmin = 2 * 2; + if (pdistmax) + *pdistmax = arw->range; + return -2; + } /* can attack from distance, and hp loss or attack not used */ - if (ranged_attk(mtmp->data) + if (ranged_attk_available(mtmp) && ((mtmp->mhp < (mtmp->mhpmax+1) / 3) || !mtmp->mspec_used)) - return TRUE; + return -1; - return FALSE; + return oldappr; /* leaves appr unchanged */ } staticfn boolean @@ -1173,7 +1220,7 @@ holds_up_web(coordxy x, coordxy y) stairway *sway; if (!isok(x, y) - || IS_ROCK(levl[x][y].typ) + || IS_OBSTRUCTED(levl[x][y].typ) || ((levl[x][y].typ == STAIRS || levl[x][y].typ == LADDER) && (sway = stairway_at(x, y)) != 0 && sway->up) || levl[x][y].typ == IRONBARS) @@ -1440,6 +1487,7 @@ postmov( if (vamp_shift(mtmp, &mons[PM_FOG_CLOUD], ((seenflgs & 1) != 0) ? TRUE : FALSE)) { ptr = mtmp->data; /* update cached value */ + nhUse(ptr); } if (seenflgs) { remove_monster(omx, omy); @@ -1470,7 +1518,7 @@ postmov( do { \ (where)->doormask = (what); \ newsym((who)->mx, (who)->my); \ - unblock_point((who)->mx, (who)->my); \ + recalc_block_point((who)->mx, (who)->my); \ vision_recalc(0); \ /* update cached value since it might change */ \ canseeit = didseeit || cansee((who)->mx, (who)->my); \ @@ -1490,9 +1538,9 @@ postmov( && amorphous(ptr)) { if (flags.verbose && canseemon(mtmp)) pline_mon(mtmp, "%s %s under the door.", YMonnam(mtmp), - (ptr == &mons[PM_FOG_CLOUD] - || ptr->mlet == S_LIGHT) ? "flows" : "oozes"); - } else if (here->doormask & D_LOCKED && can_unlock) { + (ptr == &mons[PM_FOG_CLOUD] + || ptr->mlet == S_LIGHT) ? "flows" : "oozes"); + } else if ((here->doormask & D_LOCKED) != 0 && can_unlock) { /* like the vampshift hack, there are sequencing issues when the monster is moved to the door's spot first then door handling plus feedback comes after */ @@ -1531,7 +1579,7 @@ postmov( } } } - } else if (here->doormask & (D_LOCKED | D_CLOSED)) { + } else if ((here->doormask & (D_LOCKED | D_CLOSED)) != 0) { /* mfndpos guarantees this must be a doorbuster */ unsigned mask; @@ -1665,7 +1713,8 @@ m_move(struct monst *mtmp, int after) boolean better_with_displacing = FALSE; unsigned seenflgs; struct permonst *ptr; - int chi, mmoved = MMOVE_NOTHING; /* not strictly nec.: chi >= 0 will do */ + int chi, mmoved = MMOVE_NOTHING, /* not strictly nec.: chi >= 0 will do */ + preferredrange_min = 0, preferredrange_max = 0; long info[9]; long flag; coordxy omx = mtmp->mx, omy = mtmp->my; @@ -1718,8 +1767,8 @@ m_move(struct monst *mtmp, int after) if (is_covetous(ptr)) { /* [should this include * '&& mtmp->mstrategy != STRAT_NONE'?] */ int covetousattack; - coordxy tx = STRAT_GOALX(mtmp->mstrategy), - ty = STRAT_GOALY(mtmp->mstrategy); + coordxy tx = mtmp->mgoal.x, + ty = mtmp->mgoal.y; struct monst *intruder = isok(tx, ty) ? m_at(tx, ty) : NULL; /* * if there's a monster on the object or in possession of it, @@ -1757,6 +1806,7 @@ m_move(struct monst *mtmp, int after) break; default: impossible("unknown shk/gd/pri_move return value (%d)", xm); + FALLTHROUGH; /*FALLTHRU*/ case 0: case 1: @@ -1815,8 +1865,7 @@ m_move(struct monst *mtmp, int after) appr = -1; /* hostiles with ranged weapon or attack try to stay away */ - if (m_balks_at_approaching(mtmp)) - appr = -1; + appr = m_balks_at_approaching(appr, mtmp, &preferredrange_min, &preferredrange_max); if (!should_see && can_track(ptr)) { coord *cp; @@ -1865,7 +1914,7 @@ m_move(struct monst *mtmp, int after) coord poss[9]; cnt = mfndpos(mtmp, poss, info, flag); - if (cnt == 0) { + if (cnt == 0 && !is_unicorn(mtmp->data)) { if (find_defensive(mtmp, TRUE) && use_defensive(mtmp)) return MMOVE_DONE; return MMOVE_NOMOVES; @@ -1909,7 +1958,11 @@ m_move(struct monst *mtmp, int after) nearer = ((ndist = dist2(nx, ny, ggx, ggy)) < nidist); if ((appr == 1 && nearer) || (appr == -1 && !nearer) - || (!appr && !rn2(++chcnt)) || (mmoved == MMOVE_NOTHING)) { + || (!appr && !rn2(++chcnt)) + || (appr == -2 + && ((ndist <= preferredrange_min && !nearer) + || (ndist >= preferredrange_max && nearer))) + || (mmoved == MMOVE_NOTHING)) { nix = nx; niy = ny; nidist = ndist; diff --git a/src/monst.c b/src/monst.c index 4b7f57fb1..b5864ecdb 100644 --- a/src/monst.c +++ b/src/monst.c @@ -4,6 +4,7 @@ /* NetHack may be freely redistributed. See license for details. */ #include "config.h" +#include "weight.h" #include "permonst.h" #include "wintype.h" #include "sym.h" diff --git a/src/mplayer.c b/src/mplayer.c index a422de3ca..041a997ec 100644 --- a/src/mplayer.c +++ b/src/mplayer.c @@ -69,7 +69,7 @@ dev_name(void) } staticfn void -get_mplname(struct monst* mtmp, char *nam) +get_mplname(struct monst *mtmp, char *nam) { boolean fmlkind = is_female(mtmp->data); const char *devnam; @@ -261,8 +261,9 @@ mk_mplayer(struct permonst *ptr, coordxy x, coordxy y, boolean special) otmp->oerodeproof = 1; else if (!rn2(2)) otmp->greased = 1; + /* mk_artifact() with otmp and A_NONE will never return NULL */ if (special && rn2(2)) - otmp = mk_artifact(otmp, A_NONE); + otmp = mk_artifact(otmp, A_NONE, 99, FALSE); /* usually increase stack size if stackable weapon */ if (objects[otmp->otyp].oc_merge && !otmp->oartifact && monmightthrowwep(otmp)) diff --git a/src/mthrowu.c b/src/mthrowu.c index b91d44435..259484698 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mthrowu.c $NHDT-Date: 1629497158 2021/08/20 22:05:58 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.117 $ */ +/* NetHack 3.7 mthrowu.c $NHDT-Date: 1737392015 2025/01/20 08:53:35 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.173 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Pasi Kallinen, 2016. */ /* NetHack may be freely redistributed. See license for details. */ @@ -12,6 +12,7 @@ staticfn const char *breathwep_name(int); staticfn boolean drop_throw(struct obj *, boolean, coordxy, coordxy); staticfn boolean blocking_terrain(coordxy, coordxy); staticfn int m_lined_up(struct monst *, struct monst *) NONNULLARG12; +staticfn void return_from_mtoss(struct monst *, struct obj *, boolean); #define URETREATING(x, y) \ (distmin(u.ux, u.uy, x, y) > distmin(u.ux0, u.uy0, x, y)) @@ -196,7 +197,9 @@ drop_throw( /* calculate multishot volley count for mtmp throwing otmp (if not ammo) or shooting otmp with mwep (if otmp is ammo and mwep appropriate launcher) */ staticfn int -monmulti(struct monst *mtmp, struct obj *otmp, struct obj *mwep) +monmulti( + struct monst *mtmp, + struct obj *otmp, struct obj *mwep) { int multishot = 1; @@ -238,11 +241,11 @@ monmulti(struct monst *mtmp, struct obj *otmp, struct obj *mwep) /* racial bonus */ if ((is_elf(mtmp->data) && otmp->otyp == ELVEN_ARROW - && mwep->otyp == ELVEN_BOW) + && mwep && mwep->otyp == ELVEN_BOW) || (is_orc(mtmp->data) && otmp->otyp == ORCISH_ARROW - && mwep->otyp == ORCISH_BOW) + && mwep && mwep->otyp == ORCISH_BOW) || (is_gnome(mtmp->data) && otmp->otyp == CROSSBOW_BOLT - && mwep->otyp == CROSSBOW)) + && mwep && mwep->otyp == CROSSBOW)) multishot++; } @@ -310,7 +313,7 @@ monshoot(struct monst *mtmp, struct obj *otmp, struct obj *mwep) } /* an object launched by someone/thing other than player attacks a monster; - return 1 if the object has stopped moving (hit or its range used up) + return 1 if the object has stopped moving (hit or its range used up); can anger the monster, if this happened due to hero (eg. exploding bag of holding throwing the items) */ boolean @@ -320,7 +323,7 @@ ohitmon( int range, /* how much farther will object travel if it misses; * use -1 to signify to keep going even after hit, * unless it's gone (for rolling_boulder_traps) */ - boolean verbose)/* give messages even when you can't see what happened */ + boolean verbose) /* give messages even when you can't see what happened */ { int damage, tmp; boolean vis, ismimic, objgone; @@ -453,8 +456,8 @@ ohitmon( (nonliving(mtmp->data) || is_vampshifter(mtmp) || !canspotmon(mtmp)) ? "destroyed" : "killed"); /* don't blame hero for unknown rolling boulder trap */ - if (!svc.context.mon_moving && (otmp->otyp != BOULDER - || range >= 0 || otmp->otrapped)) + if (!svc.context.mon_moving + && (otmp->otyp != BOULDER || range >= 0 || otmp->otrapped)) xkilled(mtmp, XKILL_NOMSG); else mondied(mtmp); @@ -528,7 +531,7 @@ ucatchgem( (/* missile hits edge of screen */ \ !isok(gb.bhitpos.x + dx, gb.bhitpos.y + dy) \ /* missile hits the wall */ \ - || IS_ROCK(levl[gb.bhitpos.x + dx][gb.bhitpos.y + dy].typ) \ + || IS_OBSTRUCTED(levl[gb.bhitpos.x + dx][gb.bhitpos.y + dy].typ) \ /* missile hit closed door */ \ || closed_door(gb.bhitpos.x + dx, gb.bhitpos.y + dy) \ /* missile might hit iron bars */ \ @@ -546,8 +549,8 @@ ucatchgem( void m_throw( struct monst *mon, /* launching monster */ - coordxy x, coordxy y, /* launch point */ - coordxy dx, coordxy dy, /* direction */ + coordxy x, coordxy y, /* launch point */ + coordxy dx, coordxy dy, /* direction */ int range, /* maximum distance */ struct obj *obj) /* missile (or stack providing it) */ { @@ -556,6 +559,10 @@ m_throw( boolean forcehit; char sym = obj->oclass; int hitu = 0, oldumort, blindinc = 0; + const struct throw_and_return_weapon *arw = autoreturn_weapon(obj); + boolean tethered_weapon = + (obj == MON_WEP(mon) && arw && arw->tethered != 0), + return_flightpath = FALSE; gb.bhitpos.x = x; gb.bhitpos.y = y; @@ -568,7 +575,7 @@ m_throw( * with 0 daggers? (This caused the infamous 2^32-1 orcish * dagger bug). * - * VENOM is not in minvent - it should already be OBJ_FREE. + * VENOM is not in minvent--it should already be OBJ_FREE. * The extract below does nothing. */ @@ -586,7 +593,7 @@ m_throw( /* global pointer for missile object in OBJ_FREE state */ gt.thrownobj = singleobj; - singleobj->owornmask = 0; /* threw one of multiple weapons in hand? */ + singleobj->owornmask = 0L; /* threw one of multiple weapons in hand? */ if (!canseemon(mon)) clear_dknown(singleobj); /* singleobj->dknown = 0; */ @@ -617,8 +624,30 @@ m_throw( * early to avoid the dagger bug, anyone who modifies this code should * be careful not to use either one after it's been freed. */ - if (sym) - tmp_at(DISP_FLASH, obj_to_glyph(singleobj, rn2_on_display_rng)); + if (sym) { + if (!tethered_weapon) { + tmp_at(DISP_FLASH, obj_to_glyph(singleobj, rn2_on_display_rng)); + } else { + tmp_at(DISP_TETHER, obj_to_glyph(singleobj, rn2_on_display_rng)); + /* + * Considerations for a tethered object based on throwit()/bhit() : + * - wall of water/lava will stop items, and triggers return. + * - iron bars will stop items, and triggers return. + * - pass harmlessly through shades. + * X stops forward motion at hit monster/hero, triggers return. + * - closed door will stop item's forward motion, triggers return. + * - sinks stop forward motion, triggers fall, then return. + * - object can get tangled in a web, no return (tether snaps?). + * On return: + * X rn2(100) chance of returning to thrower's location. + * X if impaired and rn2(100) == 0, + * -50/50 chance of landing on the ground. + * -50/50 chance of hitting the thrower and causing + * rnd(3) damage. + * + */ + } + } while (range-- > 0) { /* Actually the loop is always exited by break */ singleobj->ox = gb.bhitpos.x += dx; singleobj->oy = gb.bhitpos.y += dy; @@ -655,6 +684,7 @@ m_throw( hitu = 0; break; } + FALLTHROUGH; /*FALLTHRU*/ case CREAM_PIE: case BLINDING_VENOM: @@ -727,7 +757,12 @@ m_throw( } stop_occupation(); if (hitu) { - (void) drop_throw(singleobj, hitu, u.ux, u.uy); + if (!tethered_weapon) { + (void) drop_throw(singleobj, hitu, u.ux, u.uy); + } else { + /* ready for return journey */ + return_flightpath = TRUE; + } break; } } @@ -748,8 +783,13 @@ m_throw( && (cansee(gb.bhitpos.x, gb.bhitpos.y) || (gm.marcher && canseemon(gm.marcher)))) pline("%s misses.", The(mshot_xname(singleobj))); - - (void) drop_throw(singleobj, 0, gb.bhitpos.x, gb.bhitpos.y); + if (!tethered_weapon) { + (void) drop_throw(singleobj, 0, + gb.bhitpos.x, gb.bhitpos.y); + } else { + /*ready for return journey */ + return_flightpath = TRUE; + } } break; } @@ -758,7 +798,11 @@ m_throw( } tmp_at(gb.bhitpos.x, gb.bhitpos.y); nh_delay_output(); - tmp_at(DISP_END, 0); + if (arw && return_flightpath) + return_from_mtoss(mon, singleobj, tethered_weapon); + /* mon could be DEADMONSTER now */ + else + tmp_at(DISP_END, 0); gm.mesg_given = 0; /* reset */ if (blindinc) { @@ -774,6 +818,124 @@ m_throw( #undef MT_FLIGHTCHECK +staticfn void +return_from_mtoss( + struct monst *magr, + struct obj *otmp, + boolean tethered_weapon) +{ + boolean impaired = (magr->mconf || magr->mstun || magr->mblinded), + notcaught = FALSE, hits_thrower = FALSE; + coordxy x = gb.bhitpos.x, y = gb.bhitpos.y; + int made_it_back = rn2(100), dmg = 0; + + if (otmp && made_it_back) { + /* it made it back to thrower's location */ + if (tethered_weapon) { + tmp_at(DISP_END, BACKTRACK); + } else { + int dx = sgn(x - magr->mx), + dy = sgn(y - magr->my); + + if (x != magr->mx || y != magr->my) { + tmp_at(DISP_FLASH, obj_to_glyph(otmp, rn2_on_display_rng)); + while (isok(x, y) && (x != magr->mx || y != magr->my)) { + tmp_at(x, y); + nh_delay_output(); + x -= dx; + y -= dy; + } + tmp_at(DISP_END, 0); + } + } + x = magr->mx; + y = magr->my; + if (!impaired && rn2(100)) { + /* FIXME: this should be moved to struct g (gd these days) */ + static long do_not_annoy = 0; + + if (!do_not_annoy || (svm.moves - do_not_annoy) > 500L) { + pline("%s to %s %s!", Tobjnam(otmp, "return"), + s_suffix(mon_nam(magr)), mbodypart(magr, HAND)); + do_not_annoy = svm.moves; + } + if (otmp) { + add_to_minv(magr, otmp); + if (tethered_weapon) { + magr->mw = otmp; + otmp->owornmask |= W_WEP; + } + } + if (cansee(x, y)) + newsym(x, y); + } else { + boolean mlevitating = FALSE; /* msg future-proofing only */ + + dmg = rn2(2); + if (!dmg) { + if (canseemon(magr)) { + pline("%s back to %s, landing %s %s %s.", + Tobjnam(otmp, "return"), mon_nam(magr), + mlevitating ? "beneath" : "at", mhis(magr), + makeplural(mbodypart(magr, FOOT))); + } else if (!Deaf) { + You_hear("%s land near %s.", Something, mon_nam(magr)); + } + } else { + dmg += rnd(3); + if (canseemon(magr)) { + pline("%s back toward %s, hitting %s %s!", + Tobjnam(otmp, "fly"), mon_nam(magr), + mhis(magr), body_part(ARM)); + } else if (!Deaf) { + You_hear("%s hit %s with a thud!", something, + mon_nam(magr)); + } + hits_thrower = TRUE; + } + notcaught = TRUE; + } + } else { + /* it didn't make it back to thrower's location */ + if (tethered_weapon) + tmp_at(DISP_END, 0); + You_hear("a loud snap!"); + notcaught = TRUE; + } + if (otmp) { + if (hits_thrower) { + if (otmp->oartifact) + (void) artifact_hit((struct monst *) 0, magr, otmp, &dmg, 0); + magr->mhp -= dmg; + if (DEADMONSTER(magr)) + monkilled(magr, canspotmon(magr) ? "" : (char *) 0, AD_PHYS); + } + if (notcaught) { + (void) snuff_candle(otmp); + if (!ship_object(otmp, x, y, FALSE)) { + if (flooreffects(otmp, x, y, "drop")) { + if (cansee(x, y)) + newsym(x, y); + return; + } + place_object(otmp, x, y); + stackobj(otmp); + } + if (!Deaf && !Underwater) { + /* Some sound effects when item lands in water or lava */ + if (is_pool(x, y) || (is_lava(x, y) && !is_flammable(otmp))) { + Soundeffect(se_splash, 50); + pline((weight(otmp) > 9) ? "Splash!" : "Plop!"); + } + } + if (obj_sheds_light(otmp)) + gv.vision_full_recalc = 1; + } + } + if (cansee(x, y)) + newsym(x, y); +} + /* Monster throws item at another monster */ int thrwmm(struct monst *mtmp, struct monst *mtarg) @@ -851,6 +1013,7 @@ spitmm(struct monst *mtmp, struct attack *mattk, struct monst *mtarg) break; default: impossible("bad attack type in spitmm"); + FALLTHROUGH; /*FALLTHRU*/ case AD_ACID: otmp = mksobj(ACID_VENOM, TRUE, FALSE); @@ -984,6 +1147,9 @@ thrwmu(struct monst *mtmp) struct obj *otmp, *mwep; coordxy x, y; const char *onm; + int rang; + const struct throw_and_return_weapon *arw; + boolean always_toss = FALSE; /* Rearranged beginning so monsters can use polearms not in a line */ if (mtmp->weapon_check == NEED_WEAPON || !MON_WEP(mtmp)) { @@ -999,10 +1165,10 @@ thrwmu(struct monst *mtmp) return; if (is_pole(otmp)) { - int dam, hitv, rang; + int dam, hitv; if (otmp != MON_WEP(mtmp)) - return; /* polearm must be wielded */ + return; /* polearm, aklys must be wielded */ /* * MON_POLE_DIST encompasses knight's move range (5): two spots @@ -1024,7 +1190,7 @@ thrwmu(struct monst *mtmp) if (canseemon(mtmp)) { onm = xname(otmp); - pline_xy(mtmp->mx, mtmp->my, "%s %s %s.", Monnam(mtmp), + pline_mon(mtmp, "%s %s %s.", Monnam(mtmp), /* "thrusts" or "swings", or "bashes with" if adjacent */ mswings_verb(otmp, (rang <= 2) ? TRUE : FALSE), obj_is_pname(otmp) ? the(onm) : an(onm)); @@ -1043,6 +1209,11 @@ thrwmu(struct monst *mtmp) (void) thitu(hitv, dam, &otmp, (char *) 0); stop_occupation(); return; + } else if ((arw = autoreturn_weapon(otmp)) != 0 && !mwelded(otmp)) { + rang = dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy); + if (rang > arw->range || !couldsee(mtmp->mx, mtmp->my)) + return; /* Out of range, or intervening wall */ + always_toss = TRUE; } x = mtmp->mx; @@ -1054,7 +1225,8 @@ thrwmu(struct monst *mtmp) */ if (!lined_up(mtmp) || (URETREATING(x, y) - && rn2(BOLT_LIM - distmin(x, y, mtmp->mux, mtmp->muy)))) + && (!always_toss + && rn2(BOLT_LIM - distmin(x, y, mtmp->mux, mtmp->muy))))) return; mwep = MON_WEP(mtmp); /* wielded weapon */ @@ -1080,7 +1252,7 @@ breamu(struct monst *mtmp, struct attack *mattk) staticfn boolean blocking_terrain(coordxy x, coordxy y) { - if (!isok(x, y) || IS_ROCK(levl[x][y].typ) || closed_door(x, y) + if (!isok(x, y) || IS_OBSTRUCTED(levl[x][y].typ) || closed_door(x, y) || is_waterwall(x, y) || levl[x][y].typ == LAVAWALL) return TRUE; return FALSE; @@ -1274,7 +1446,7 @@ hit_bars( weight is normally 480 but can be increased by increments of 160 (scrolls of punishment read while already punished) */ int spe = ((otmp->otyp == HEAVY_IRON_BALL) /* 3+ for iron ball */ - ? ((int) otmp->owt / IRON_BALL_W_INCR) + ? ((int) otmp->owt / WT_IRON_BALL_INCR) : otmp->spe); /* chance: used in saving throw for the bars; more likely to break those when 'chance' is _lower_; acurrstr(): 3..25 */ diff --git a/src/muse.c b/src/muse.c index dd61a9432..42243c832 100644 --- a/src/muse.c +++ b/src/muse.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 muse.c $NHDT-Date: 1715109270 2024/05/07 19:14:30 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.218 $ */ +/* NetHack 3.7 muse.c $NHDT-Date: 1737392015 2025/01/20 08:53:35 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.234 $ */ /* Copyright (C) 1990 by Ken Arromdee */ /* NetHack may be freely redistributed. See license for details. */ @@ -15,10 +15,10 @@ */ staticfn int precheck(struct monst *, struct obj *); -staticfn void mzapwand(struct monst *, struct obj *, boolean); -staticfn void mplayhorn(struct monst *, struct obj *, boolean); -staticfn void mreadmsg(struct monst *, struct obj *); -staticfn void mquaffmsg(struct monst *, struct obj *); +staticfn void mzapwand(struct monst *, struct obj *, boolean) NONNULLPTRS; +staticfn void mplayhorn(struct monst *, struct obj *, boolean) NONNULLPTRS; +staticfn void mreadmsg(struct monst *, struct obj *) NONNULLPTRS; +staticfn void mquaffmsg(struct monst *, struct obj *) NONNULLPTRS; staticfn boolean m_use_healing(struct monst *); staticfn boolean m_sees_sleepy_soldier(struct monst *); staticfn void m_tele(struct monst *, boolean, boolean, int); @@ -107,7 +107,7 @@ precheck(struct monst *mon, struct obj *obj) pline1(empty); } else { if (vis) - pline("In a cloud of smoke, %s emerges!", a_monnam(mtmp)); + pline_mon(mtmp, "In a cloud of smoke, %s emerges!", a_monnam(mtmp)); pline("%s speaks.", vis ? Monnam(mtmp) : Something); /* I suspect few players will be upset that monsters */ /* can't wish for wands of death here.... */ @@ -133,7 +133,7 @@ precheck(struct monst *mon, struct obj *obj) /* 3.6.1: no Deaf filter; 'if' message doesn't warrant it, 'else' message doesn't need it since You_hear() has one of its own */ if (vis) { - pline("%s zaps %s, which suddenly explodes!", Monnam(mon), + pline_mon(mon, "%s zaps %s, which suddenly explodes!", Monnam(mon), an(xname(obj))); } else { /* same near/far threshold as mzapwand() */ @@ -182,7 +182,7 @@ mzapwand( monverbself(mtmp, Monnam(mtmp), "zap", (char *) 0), doname(otmp)); } else { - pline("%s zaps %s!", Monnam(mtmp), an(xname(otmp))); + pline_mon(mtmp, "%s zaps %s!", Monnam(mtmp), an(xname(otmp))); stop_occupation(); } otmp->spe -= 1; @@ -236,7 +236,8 @@ staticfn void mreadmsg(struct monst *mtmp, struct obj *otmp) { char onambuf[BUFSZ]; - boolean vismon = canseemon(mtmp); + boolean vismon = canseemon(mtmp), + tpindicator = (!vismon && sensemon(mtmp)); if (!vismon && Deaf) return; /* no feedback */ @@ -246,7 +247,7 @@ mreadmsg(struct monst *mtmp, struct obj *otmp) if (vismon) { /* directly see the monster reading the scroll */ - pline("%s reads %s!", Monnam(mtmp), onambuf); + pline_mon(mtmp, "%s reads %s!", Monnam(mtmp), onambuf); } else { /* !Deaf, otherwise we wouldn't reach here */ char blindbuf[BUFSZ]; boolean similar = same_race(gy.youmonst.data, mtmp->data), @@ -262,11 +263,15 @@ mreadmsg(struct monst *mtmp, struct obj *otmp) int mflags = (SUPPRESS_INVISIBLE | SUPPRESS_SADDLE | (recognize ? SUPPRESS_IT : AUGMENT_IT)); - /* monster can't be seen; hero might be blind or monster might - be at a spot that isn't in view or might be invisible; remember - it if the spot is within line of sight and relatively close */ - if (couldsee(mtmp->mx, mtmp->my) && mdistu(mtmp) <= 10 * 10) + if (sensemon(mtmp)) { + tpindicator = TRUE; + } else if (couldsee(mtmp->mx, mtmp->my) && mdistu(mtmp) <= 10 * 10) { + /* monster can't be seen or sensed; hero might be blind or monster + might be at a spot that isn't in view or might be invisible; + remember it if the spot is within line of sight and relatively + close */ map_invisible(mtmp->mx, mtmp->my); + } Snprintf(blindbuf, sizeof blindbuf, "reading %s", onambuf); strsubst(blindbuf, "reading a scroll labeled", @@ -274,6 +279,8 @@ mreadmsg(struct monst *mtmp, struct obj *otmp) You_hear("%s %s.", x_monnam(mtmp, ARTICLE_A, (char *) 0, mflags, FALSE), blindbuf); + if (tpindicator) + flash_mon(mtmp); } if (mtmp->mconf) /* (note: won't get if not seen and hero can't hear) */ pline("Being confused, %s mispronounces the magic words...", @@ -285,7 +292,7 @@ mquaffmsg(struct monst *mtmp, struct obj *otmp) { if (canseemon(mtmp)) { otmp->dknown = 1; - pline("%s drinks %s!", Monnam(mtmp), singular(otmp, doname)); + pline_mon(mtmp, "%s drinks %s!", Monnam(mtmp), singular(otmp, doname)); } else if (!Deaf) { Soundeffect(se_mon_chugging_potion, 25); You_hear("a chugging sound."); @@ -386,7 +393,7 @@ m_tele( mon_learns_traps(mtmp, TELEP_TRAP); } else if ((mon_has_amulet(mtmp) || On_W_tower_level(&u.uz)) && !rn2(3)) { if (vismon) - pline("%s seems disoriented for a moment.", Monnam(mtmp)); + pline_mon(mtmp, "%s seems disoriented for a moment.", Monnam(mtmp)); } else { /* teleport monster 'mtmp' */ if (how) { @@ -751,8 +758,7 @@ reveal_trap(struct trap *t, boolean seeit) if (lev->typ == SCORR) { lev->typ = CORR, lev->flags = 0; /* set_levltyp(,,CORR) */ - if (seeit) - unblock_point(t->tx, t->ty); + unblock_point(t->tx, t->ty); } if (seeit) seetrap(t); @@ -775,7 +781,7 @@ mon_escape(struct monst *mtmp, boolean vismon) || (mtmp->iswiz && svc.context.no_of_wizards < 2)) return 0; if (vismon) - pline("%s escapes the dungeon!", Monnam(mtmp)); + pline_mon(mtmp, "%s escapes the dungeon!", Monnam(mtmp)); mongone(mtmp); return 2; } @@ -787,6 +793,7 @@ mon_escape(struct monst *mtmp, boolean vismon) int use_defensive(struct monst *mtmp) { + static const char MissingDefensiveItem[] = "use_defensive: no %s"; int i, fleetim; struct obj *otmp = gm.m.defensive; boolean vis, vismon, oseen; @@ -809,9 +816,10 @@ use_defensive(struct monst *mtmp) switch (gm.m.has_defense) { case MUSE_UNICORN_HORN: + /* unlike most defensive cases, unicorn horn object is optional */ if (vismon) { if (otmp) - pline("%s uses a unicorn horn!", Monnam(mtmp)); + pline_mon(mtmp, "%s uses a unicorn horn!", Monnam(mtmp)); else pline_The("tip of %s's horn glows!", mon_nam(mtmp)); } @@ -820,13 +828,16 @@ use_defensive(struct monst *mtmp) } else if (mtmp->mconf || mtmp->mstun) { mtmp->mconf = mtmp->mstun = 0; if (vismon) - pline("%s seems steadier now.", Monnam(mtmp)); - } else + pline_mon(mtmp, "%s seems steadier now.", Monnam(mtmp)); + } else { impossible("No need for unicorn horn?"); + } return 2; case MUSE_BUGLE: + if (!otmp) + panic(MissingDefensiveItem, "bugle"); if (vismon) { - pline("%s plays %s!", Monnam(mtmp), doname(otmp)); + pline_mon(mtmp, "%s plays %s!", Monnam(mtmp), doname(otmp)); } else if (!Deaf) { Soundeffect(se_bugle_playing_reveille, 100); You_hear("a bugle playing reveille!"); @@ -834,6 +845,8 @@ use_defensive(struct monst *mtmp) awaken_soldiers(mtmp); return 2; case MUSE_WAN_TELEPORTATION_SELF: + if (!otmp) + panic(MissingDefensiveItem, "wand of teleportation"); if ((mtmp->isshk && inhishop(mtmp)) || mtmp->isgd || mtmp->ispriest) return 2; m_flee(mtmp); @@ -841,6 +854,8 @@ use_defensive(struct monst *mtmp) m_tele(mtmp, vismon, oseen, WAN_TELEPORTATION); return 2; case MUSE_WAN_TELEPORTATION: + if (!otmp) + panic(MissingDefensiveItem, "wand of teleportation"); gz.zap_oseen = oseen; mzapwand(mtmp, otmp, FALSE); gm.m_using = TRUE; @@ -851,8 +866,11 @@ use_defensive(struct monst *mtmp) gm.m_using = FALSE; return 2; case MUSE_SCR_TELEPORTATION: { - int obj_is_cursed = otmp->cursed; + int obj_is_cursed; + if (!otmp) + panic(MissingDefensiveItem, "scroll of teleportation"); + obj_is_cursed = otmp->cursed; if (mtmp->isshk || mtmp->isgd || mtmp->ispriest) return 2; m_flee(mtmp); @@ -873,11 +891,11 @@ use_defensive(struct monst *mtmp) nlev = random_teleport_level(); if (mon_has_amulet(mtmp) || In_endgame(&u.uz)) { if (vismon) - pline("%s seems very disoriented for a moment.", + pline_mon(mtmp, "%s seems very disoriented for a moment.", Monnam(mtmp)); } else if (nlev == depth(&u.uz)) { if (vismon) - pline("%s shudders for a moment.", Monnam(mtmp)); + pline_mon(mtmp, "%s shudders for a moment.", Monnam(mtmp)); } else { get_level(&flev, nlev); migrate_to_level(mtmp, ledger_no(&flev), MIGR_RANDOM, @@ -895,6 +913,8 @@ use_defensive(struct monst *mtmp) return 2; } case MUSE_WAN_DIGGING: + if (!otmp) + panic(MissingDefensiveItem, "wand of digging"); m_flee(mtmp); mzapwand(mtmp, otmp, FALSE); if (oseen) @@ -920,20 +940,22 @@ use_defensive(struct monst *mtmp) /* pit creation succeeded */ if (vis) { seetrap(t); - pline("%s has made a pit in the %s.", Monnam(mtmp), + pline_mon(mtmp, "%s has made a pit in the %s.", Monnam(mtmp), surface(mtmp->mx, mtmp->my)); } fill_pit(mtmp->mx, mtmp->my); + recalc_block_point(mtmp->mx, mtmp->my); return (mintrap(mtmp, FORCEBUNGLE) == Trap_Killed_Mon) ? 1 : 2; } t = maketrap(mtmp->mx, mtmp->my, HOLE); if (!t) return 2; + recalc_block_point(mtmp->mx, mtmp->my); seetrap(t); if (vis) { - pline("%s has made a hole in the %s.", Monnam(mtmp), + pline_mon(mtmp, "%s has made a hole in the %s.", Monnam(mtmp), surface(mtmp->mx, mtmp->my)); - pline("%s %s through...", Monnam(mtmp), + pline_mon(mtmp, "%s %s through...", Monnam(mtmp), is_flyer(mtmp->data) ? "dives" : "falls"); } else if (!Deaf) { Soundeffect(se_crash_through_floor, 100); @@ -946,6 +968,8 @@ use_defensive(struct monst *mtmp) (coord *) 0); return 2; case MUSE_WAN_UNDEAD_TURNING: + if (!otmp) + panic(MissingDefensiveItem, "wand of undead turning"); gz.zap_oseen = oseen; mzapwand(mtmp, otmp, FALSE); gm.m_using = TRUE; @@ -959,6 +983,8 @@ use_defensive(struct monst *mtmp) struct permonst *pm = !is_pool(mtmp->mx, mtmp->my) ? 0 : &mons[u.uinwater ? PM_GIANT_EEL : PM_CROCODILE]; + if (!otmp) + panic(MissingDefensiveItem, "wand of create monster"); if (!enexto(&cc, mtmp->mx, mtmp->my, pm)) return 0; mzapwand(mtmp, otmp, FALSE); @@ -974,6 +1000,8 @@ use_defensive(struct monst *mtmp) struct monst *mon; boolean known = FALSE; + if (!otmp) + panic(MissingDefensiveItem, "scroll of create monster"); if (!rn2(73)) cnt += rnd(4); if (mtmp->mconf || otmp->cursed) @@ -1014,7 +1042,7 @@ use_defensive(struct monst *mtmp) m_flee(mtmp); t = t_at(gt.trapx, gt.trapy); if (vis) { - pline("%s %s into a %s!", Monnam(mtmp), + pline_mon(mtmp, "%s %s into a %s!", Monnam(mtmp), vtense(fakename[0], locomotion(mtmp->data, "jump")), trapname(t->ttyp, FALSE)); } @@ -1043,8 +1071,8 @@ use_defensive(struct monst *mtmp) if (Inhell && mon_has_amulet(mtmp) && !rn2(4) && (dunlev(&u.uz) < dunlevs_in_dungeon(&u.uz) - 3)) { if (vismon) - pline( - "As %s climbs the stairs, a mysterious force momentarily surrounds %s...", + pline("As %s climbs the stairs, a mysterious force" + " momentarily surrounds %s...", mon_nam(mtmp), mhim(mtmp)); /* simpler than for the player; this will usually be the Wizard and he'll immediately go right to the @@ -1054,7 +1082,7 @@ use_defensive(struct monst *mtmp) (coord *) 0); } else { if (vismon) - pline("%s escapes upstairs!", Monnam(mtmp)); + pline_mon(mtmp, "%s escapes upstairs!", Monnam(mtmp)); migrate_to_level(mtmp, ledger_no(&(stway->tolev)), MIGR_STAIRS_DOWN, (coord *) 0); } @@ -1065,7 +1093,7 @@ use_defensive(struct monst *mtmp) if (!stway) return 0; if (vismon) - pline("%s escapes downstairs!", Monnam(mtmp)); + pline_mon(mtmp, "%s escapes downstairs!", Monnam(mtmp)); migrate_to_level(mtmp, ledger_no(&(stway->tolev)), MIGR_STAIRS_UP, (coord *) 0); return 2; @@ -1075,7 +1103,7 @@ use_defensive(struct monst *mtmp) if (!stway) return 0; if (vismon) - pline("%s escapes up the ladder!", Monnam(mtmp)); + pline_mon(mtmp, "%s escapes up the ladder!", Monnam(mtmp)); migrate_to_level(mtmp, ledger_no(&(stway->tolev)), MIGR_LADDER_DOWN, (coord *) 0); return 2; @@ -1085,7 +1113,7 @@ use_defensive(struct monst *mtmp) if (!stway) return 0; if (vismon) - pline("%s escapes down the ladder!", Monnam(mtmp)); + pline_mon(mtmp, "%s escapes down the ladder!", Monnam(mtmp)); migrate_to_level(mtmp, ledger_no(&(stway->tolev)), MIGR_LADDER_UP, (coord *) 0); return 2; @@ -1098,7 +1126,7 @@ use_defensive(struct monst *mtmp) return mon_escape(mtmp, vismon); } if (vismon) - pline("%s escapes %sstairs!", Monnam(mtmp), + pline_mon(mtmp, "%s escapes %sstairs!", Monnam(mtmp), stway->up ? "up" : "down"); /* going from the Valley to Castle (Stronghold) has no sstairs to target, but having gs.sstairs. == <0,0> will work the @@ -1111,7 +1139,7 @@ use_defensive(struct monst *mtmp) m_flee(mtmp); t = t_at(gt.trapx, gt.trapy); if (vis) { - pline("%s %s onto a %s!", Monnam(mtmp), + pline_mon(mtmp, "%s %s onto a %s!", Monnam(mtmp), vtense(fakename[0], locomotion(mtmp->data, "jump")), trapname(t->ttyp, FALSE)); } @@ -1123,52 +1151,57 @@ use_defensive(struct monst *mtmp) place_monster(mtmp, gt.trapx, gt.trapy); if (mtmp->wormno) worm_move(mtmp); + maybe_unhide_at(mtmp->mx, mtmp->my); newsym(gt.trapx, gt.trapy); /* 0: 'no object' rather than STRANGE_OBJECT; FALSE: obj not seen */ m_tele(mtmp, vismon, FALSE, 0); return 2; case MUSE_POT_HEALING: + if (!otmp) + panic(MissingDefensiveItem, "potioh of healing"); mquaffmsg(mtmp, otmp); i = d(6 + 2 * bcsign(otmp), 4); - mtmp->mhp += i; - if (mtmp->mhp > mtmp->mhpmax) - mtmp->mhp = ++mtmp->mhpmax; + healmon(mtmp, i, 1); if (!otmp->cursed && !mtmp->mcansee) mcureblindness(mtmp, vismon); if (vismon) - pline("%s looks better.", Monnam(mtmp)); + pline_mon(mtmp, "%s looks better.", Monnam(mtmp)); if (oseen) makeknown(POT_HEALING); m_useup(mtmp, otmp); return 2; case MUSE_POT_EXTRA_HEALING: + if (!otmp) + panic(MissingDefensiveItem, "potioh of extra healing"); mquaffmsg(mtmp, otmp); i = d(6 + 2 * bcsign(otmp), 8); - mtmp->mhp += i; - if (mtmp->mhp > mtmp->mhpmax) - mtmp->mhp = (mtmp->mhpmax += (otmp->blessed ? 5 : 2)); + healmon(mtmp, i, otmp->blessed ? 5 : 2); if (!mtmp->mcansee) mcureblindness(mtmp, vismon); if (vismon) - pline("%s looks much better.", Monnam(mtmp)); + pline_mon(mtmp, "%s looks much better.", Monnam(mtmp)); if (oseen) makeknown(POT_EXTRA_HEALING); m_useup(mtmp, otmp); return 2; case MUSE_POT_FULL_HEALING: + if (!otmp) + panic(MissingDefensiveItem, "potioh of full healing"); mquaffmsg(mtmp, otmp); if (otmp->otyp == POT_SICKNESS) unbless(otmp); /* Pestilence */ - mtmp->mhp = (mtmp->mhpmax += (otmp->blessed ? 8 : 4)); + healmon(mtmp, mtmp->mhpmax, otmp->blessed ? 8 : 4); if (!mtmp->mcansee && otmp->otyp != POT_SICKNESS) mcureblindness(mtmp, vismon); if (vismon) - pline("%s looks completely healed.", Monnam(mtmp)); + pline_mon(mtmp, "%s looks completely healed.", Monnam(mtmp)); if (oseen) makeknown(otmp->otyp); m_useup(mtmp, otmp); return 2; case MUSE_LIZARD_CORPSE: + if (!otmp) + panic(MissingDefensiveItem, "lizard corpse"); /* not actually called for its unstoning effect */ mon_consume_unstone(mtmp, otmp, FALSE, FALSE); return 2; @@ -1201,6 +1234,7 @@ rnd_defensive_item(struct monst *mtmp) goto try_again; if (!rn2(3)) return WAN_TELEPORTATION; + FALLTHROUGH; /*FALLTHRU*/ case 0: case 1: @@ -1209,6 +1243,7 @@ rnd_defensive_item(struct monst *mtmp) case 10: if (!rn2(3)) return WAN_CREATE_MONSTER; + FALLTHROUGH; /*FALLTHRU*/ case 2: return SCR_CREATE_MONSTER; @@ -1594,7 +1629,7 @@ mbhitm(struct monst *mtmp, struct obj *otmp) /* for consistency with zap.c, don't identify */ if (mtmp->ispriest && *in_rooms(mtmp->mx, mtmp->my, TEMPLE)) { if (cansee(mtmp->mx, mtmp->my)) - pline("%s resists the magic!", Monnam(mtmp)); + pline_mon(mtmp, "%s resists the magic!", Monnam(mtmp)); } else if (!tele_restrict(mtmp)) (void) rloc(mtmp, RLOC_MSG); } @@ -1840,7 +1875,7 @@ use_offensive(struct monst *mtmp) for (y = mmy - 1; y <= mmy + 1; y++) { /* Is this a suitable spot? */ if (isok(x, y) && !closed_door(x, y) - && !IS_ROCK(levl[x][y].typ) && !IS_AIR(levl[x][y].typ) + && !IS_OBSTRUCTED(levl[x][y].typ) && !IS_AIR(levl[x][y].typ) && (((x == mmx) && (y == mmy)) ? !is_blessed : !is_cursed) && (x != u.ux || y != u.uy)) { (void) drop_boulder_on_monster(x, y, confused, FALSE); @@ -1887,7 +1922,7 @@ use_offensive(struct monst *mtmp) if (vis) pline_The("scroll erupts in a tower of flame!"); shieldeff(mtmp->mx, mtmp->my); - pline("%s is uninjured.", Monnam(mtmp)); + pline_mon(mtmp, "%s is uninjured.", Monnam(mtmp)); (void) destroy_mitem(mtmp, SCROLL_CLASS, AD_FIRE); (void) destroy_mitem(mtmp, SPBOOK_CLASS, AD_FIRE); (void) destroy_mitem(mtmp, POTION_CLASS, AD_FIRE); @@ -1968,7 +2003,9 @@ rnd_offensive_item(struct monst *mtmp) if (hard_helmet(mtmp_helmet) || amorphous(pm) || passes_walls(pm) || noncorporeal(pm) || unsolid(pm)) return SCR_EARTH; - } /* fall through */ + } + FALLTHROUGH; + /* FALLTHRU */ case 1: return WAN_STRIKING; case 2: @@ -2233,8 +2270,10 @@ mloot_container( if (!rn2(nitems + 1)) break; nitems = rn2(nitems); - for (xobj = container->cobj; nitems > 0; xobj = xobj->nobj) - --nitems; + for (xobj = container->cobj; xobj != 0; xobj = xobj->nobj) + if (--nitems < 0) + break; + assert(xobj != NULL); container->cknown = 0; /* hero no longer knows container's contents * even if [attempted] removal is observed */ @@ -2258,7 +2297,7 @@ mloot_container( if (howfar > 2) /* not adjacent */ Norep("%s rummages through %s.", Monnam(mon), contnr_nam); else if (takeout_indx == 0) /* adjacent, first item */ - pline("%s removes %s from %s.", Monnam(mon), + pline_mon(mon, "%s removes %s from %s.", Monnam(mon), doname(xobj), contnr_nam); else /* adjacent, additional items */ pline("%s removes %s.", upstart(mpronounbuf), @@ -2295,6 +2334,7 @@ DISABLE_WARNING_UNREACHABLE_CODE int use_misc(struct monst *mtmp) { + static const char MissingMiscellaneousItem[] = "use_misc: no %s"; char nambuf[BUFSZ]; boolean vis, vismon, vistrapspot, oseen; int i; @@ -2309,6 +2349,8 @@ use_misc(struct monst *mtmp) switch (gm.m.has_misc) { case MUSE_POT_GAIN_LEVEL: + if (!otmp) + panic(MissingMiscellaneousItem, "potion of gain level"); mquaffmsg(mtmp, otmp); if (otmp->cursed) { if (Can_rise_up(mtmp->mx, mtmp->my, &u.uz)) { @@ -2320,8 +2362,9 @@ use_misc(struct monst *mtmp) if (on_level(&tolevel, &u.uz)) goto skipmsg; if (vismon) { - pline("%s rises up, through the %s!", Monnam(mtmp), - ceiling(mtmp->mx, mtmp->my)); + pline_mon(mtmp, "%s rises up, through the %s!", + Monnam(mtmp), + ceiling(mtmp->mx, mtmp->my)); trycall(otmp); } m_useup(mtmp, otmp); @@ -2331,7 +2374,7 @@ use_misc(struct monst *mtmp) } else { skipmsg: if (vismon) { - pline("%s looks uneasy.", Monnam(mtmp)); + pline_mon(mtmp, "%s looks uneasy.", Monnam(mtmp)); trycall(otmp); } m_useup(mtmp, otmp); @@ -2339,7 +2382,7 @@ use_misc(struct monst *mtmp) } } if (vismon) - pline("%s seems more experienced.", Monnam(mtmp)); + pline_mon(mtmp, "%s seems more experienced.", Monnam(mtmp)); if (oseen) makeknown(POT_GAIN_LEVEL); m_useup(mtmp, otmp); @@ -2349,6 +2392,8 @@ use_misc(struct monst *mtmp) return 2; case MUSE_WAN_MAKE_INVISIBLE: case MUSE_POT_INVISIBILITY: + if (!otmp) + panic(MissingMiscellaneousItem, "potion of invisibility"); if (otmp->otyp == WAN_MAKE_INVISIBLE) { mzapwand(mtmp, otmp, TRUE); } else @@ -2376,10 +2421,14 @@ use_misc(struct monst *mtmp) } return 2; case MUSE_WAN_SPEED_MONSTER: + if (!otmp) + panic(MissingMiscellaneousItem, "wand of speed monster"); mzapwand(mtmp, otmp, TRUE); mon_adjust_speed(mtmp, 1, otmp); return 2; case MUSE_POT_SPEED: + if (!otmp) + panic(MissingMiscellaneousItem, "potion of speed"); mquaffmsg(mtmp, otmp); /* note difference in potion effect due to substantially different methods of maintaining speed ratings: @@ -2389,6 +2438,8 @@ use_misc(struct monst *mtmp) m_useup(mtmp, otmp); return 2; case MUSE_WAN_POLYMORPH: + if (!otmp) + panic(MissingMiscellaneousItem, "wand of polymorph"); mzapwand(mtmp, otmp, TRUE); (void) newcham(mtmp, muse_newcham_mon(mtmp), NC_VIA_WAND_OR_SPELL | NC_SHOW_MSG); @@ -2396,10 +2447,12 @@ use_misc(struct monst *mtmp) makeknown(WAN_POLYMORPH); return 2; case MUSE_POT_POLYMORPH: + if (!otmp) + panic(MissingMiscellaneousItem, "potion of polymorph"); mquaffmsg(mtmp, otmp); m_useup(mtmp, otmp); if (vismon) - pline("%s suddenly mutates!", Monnam(mtmp)); + pline_mon(mtmp, "%s suddenly mutates!", Monnam(mtmp)); (void) newcham(mtmp, muse_newcham_mon(mtmp), NC_SHOW_MSG); if (oseen) makeknown(POT_POLYMORPH); @@ -2410,7 +2463,7 @@ use_misc(struct monst *mtmp) if (vis || vistrapspot) seetrap(t); if (vismon || vistrapspot) { - pline("%s deliberately %s onto a %s!", Some_Monnam(mtmp), + pline_mon(mtmp, "%s deliberately %s onto a %s!", Some_Monnam(mtmp), vtense(fakename[0], locomotion(mtmp->data, "jump")), t->tseen ? trapname(t->ttyp, FALSE) : "hidden trap"); /* note: if mtmp is unseen because it is invisible, its new @@ -2431,6 +2484,8 @@ use_misc(struct monst *mtmp) (void) newcham(mtmp, (struct permonst *) 0, NC_SHOW_MSG); return 2; case MUSE_BAG: + if (!otmp) + panic(MissingMiscellaneousItem, "container"); return mloot_container(mtmp, otmp, vismon); case MUSE_BULLWHIP: /* attempt to disarm hero */ @@ -2453,8 +2508,8 @@ use_misc(struct monst *mtmp) hand = makeplural(hand); if (vismon) - pline("%s flicks a bullwhip towards your %s!", Monnam(mtmp), - hand); + pline_mon(mtmp, "%s flicks a bullwhip towards your %s!", + Monnam(mtmp), hand); if (obj->otyp == HEAVY_IRON_BALL) { pline("%s fails to wrap around %s.", The_whip, the_weapon); return 1; @@ -2481,17 +2536,17 @@ use_misc(struct monst *mtmp) freeinv(obj); switch (where_to) { case 1: /* onto floor beneath mon */ - pline("%s yanks %s from your %s!", Monnam(mtmp), the_weapon, - hand); + pline_mon(mtmp, "%s yanks %s from your %s!", Monnam(mtmp), + the_weapon, hand); place_object(obj, mtmp->mx, mtmp->my); break; case 2: /* onto floor beneath you */ - pline("%s yanks %s to the %s!", Monnam(mtmp), the_weapon, - surface(u.ux, u.uy)); + pline_mon(mtmp, "%s yanks %s to the %s!", Monnam(mtmp), + the_weapon, surface(u.ux, u.uy)); dropy(obj); break; case 3: /* into mon's inventory */ - pline("%s snatches %s!", Monnam(mtmp), the_weapon); + pline_mon(mtmp, "%s snatches %s!", Monnam(mtmp), the_weapon); (void) mpickobj(mtmp, obj); break; } @@ -2759,7 +2814,7 @@ mcureblindness(struct monst *mon, boolean verbos) mon->mcansee = 1; mon->mblinded = 0; if (verbos && haseyes(mon->data)) - pline("%s can see again.", Monnam(mon)); + pline_mon(mon, "%s can see again.", Monnam(mon)); } } @@ -2809,7 +2864,7 @@ mon_consume_unstone( long save_quan = obj->quan; obj->quan = 1L; - pline("%s %s %s.", Monnam(mon), + pline_mon(mon, "%s %s %s.", Monnam(mon), ((obj->oclass == POTION_CLASS) ? "quaffs" : (obj->otyp == TIN) ? "opens and eats the contents of" : "eats"), @@ -2825,9 +2880,9 @@ mon_consume_unstone( if (acid && !tinned && !resists_acid(mon)) { mon->mhp -= rnd(15); if (vis) - pline("%s has a very bad case of stomach acid.", Monnam(mon)); + pline_mon(mon, "%s has a very bad case of stomach acid.", Monnam(mon)); if (DEADMONSTER(mon)) { - pline("%s dies!", Monnam(mon)); + pline_mon(mon, "%s dies!", Monnam(mon)); if (by_you) /* hero gets credit (experience) and blame (possible loss of alignment and/or luck and/or telepathy depending on @@ -2843,13 +2898,13 @@ mon_consume_unstone( pline("What a pity - %s just ruined a future piece of art!", mon_nam(mon)); else - pline("%s seems limber!", Monnam(mon)); + pline_mon(mon, "%s seems limber!", Monnam(mon)); } if (lizard && (mon->mconf || mon->mstun)) { mon->mconf = 0; mon->mstun = 0; if (vis && !is_bat(mon->data) && mon->data != &mons[PM_STALKER]) - pline("%s seems steadier now.", Monnam(mon)); + pline_mon(mon, "%s seems steadier now.", Monnam(mon)); } if (mon->mtame && !mon->isminion && nutrit > 0) { struct edog *edog = EDOG(mon); @@ -2996,7 +3051,7 @@ muse_unslime( boolean vis = canseemon(mon), res = TRUE; if (vis) - pline("%s starts turning %s.", Monnam(mon), + pline_mon(mon, "%s starts turning %s.", Monnam(mon), green_mon(mon) ? "into ooze" : hcolor(NH_GREEN)); /* -4 => sliming, causes quiet loss of enhanced speed */ mon_adjust_speed(mon, -4, (struct obj *) 0); @@ -3025,7 +3080,8 @@ muse_unslime( } else if (otyp == STRANGE_OBJECT) { /* monster is using fire breath on self */ if (vis) - pline("%s.", monverbself(mon, Monnam(mon), "breath", "fire on")); + pline_mon(mon, "%s.", + monverbself(mon, Monnam(mon), "breath", "fire on")); if (!rn2(3)) mon->mspec_used = rn1(10, 5); /* -21 => monster's fire breath; 1 => # of damage dice */ @@ -3059,14 +3115,14 @@ muse_unslime( * Monsters don't start with oil and don't actively pick up oil * so this may never occur in a real game. (Possible though; * nymph can steal potions of oil; shapechanger could take on - * nymph form or vacuum up stuff as a g.cube and then eventually + * nymph form or vacuum up stuff as a gel.cube and then eventually * engage with a green slime.) */ if (obj->quan > 1L) obj = splitobj(obj, 1L); if (vis && !was_lit) { - pline("%s ignites %s.", Monnam(mon), ansimpleoname(obj)); + pline_mon(mon, "%s ignites %s.", Monnam(mon), ansimpleoname(obj)); saw_lit = TRUE; } begin_burn(obj, was_lit); @@ -3101,7 +3157,7 @@ muse_unslime( for pacifist conduct); xkilled()'s message would say "You killed/destroyed " so give our own message */ if (vis) - pline("%s is %s by the fire!", Monnam(mon), + pline_mon(mon, "%s is %s by the fire!", Monnam(mon), nonliving(mon->data) ? "destroyed" : "killed"); xkilled(mon, XKILL_NOMSG | XKILL_NOCONDUCT); } else @@ -3109,12 +3165,12 @@ muse_unslime( } else { /* non-fatal damage occurred */ if (vis) - pline("%s is burned%s", Monnam(mon), exclam(dmg)); + pline_mon(mon, "%s is burned%s", Monnam(mon), exclam(dmg)); } } if (vis) { if (res && !DEADMONSTER(mon)) - pline("%s slime is burned away!", s_suffix(Monnam(mon))); + pline_mon(mon, "%s slime is burned away!", s_suffix(Monnam(mon))); if (otyp != STRANGE_OBJECT) makeknown(otyp); } diff --git a/src/music.c b/src/music.c index 241f53aee..21614860d 100644 --- a/src/music.c +++ b/src/music.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 music.c $NHDT-Date: 1702349065 2023/12/12 02:44:25 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.102 $ */ +/* NetHack 3.7 music.c $NHDT-Date: 1736530208 2025/01/10 09:30:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.120 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -159,7 +159,7 @@ calm_nymphs(int distance) /* Awake soldiers anywhere the level (and any nearby monster). */ void -awaken_soldiers(struct monst* bugler /* monster that played instrument */) +awaken_soldiers(struct monst *bugler /* monster that played instrument */) { struct monst *mtmp; int distance, distm; @@ -443,6 +443,7 @@ do_earthquake(int force) unblock_point(x, y); if (cansee(x, y)) pline("A secret corridor is revealed."); + FALLTHROUGH; /*FALLTHRU*/ case CORR: case ROOM: @@ -452,6 +453,7 @@ do_earthquake(int force) cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */ if (cansee(x, y)) pline("A secret door is revealed."); + FALLTHROUGH; /*FALLTHRU*/ case DOOR: /* make the door collapse */ /* if already doorless, treat like room or corridor */ @@ -461,7 +463,7 @@ do_earthquake(int force) } /* wasn't doorless, now it will be */ levl[x][y].doormask = D_NODOOR; - unblock_point(x, y); + recalc_block_point(x, y); newsym(x, y); /* before pline */ if (cansee(x, y)) pline_The("door collapses."); @@ -731,7 +733,7 @@ staticfn char * improvised_notes(boolean *same_as_last_time) { static const char notes[7] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G' }; - /* target buffer has to be in svc.context, otherwise saving game + /* target buffer has to be in svc.context, otherwise saving game * between improvised recitals would not be able to maintain * the same_as_last_time context. */ diff --git a/src/nhlobj.c b/src/nhlobj.c index ca2f74e17..e06fc4b59 100644 --- a/src/nhlobj.c +++ b/src/nhlobj.c @@ -219,7 +219,7 @@ l_obj_objects_to_table(lua_State *L) /* TODO: oc_bimanual, oc_bulky */ nhl_add_table_entry_int(L, "tough", o->oc_tough); nhl_add_table_entry_int(L, "dir", o->oc_dir); /* TODO: convert to text */ - nhl_add_table_entry_int(L, "material", o->oc_material); /* TODO: convert to text */ + nhl_add_table_entry_str(L, "material", materialnm[o->oc_material]); /* TODO: oc_subtyp, oc_skill, oc_armcat */ nhl_add_table_entry_int(L, "oprop", o->oc_oprop); nhl_add_table_entry_char(L, "class", @@ -295,6 +295,7 @@ l_obj_to_table(lua_State *L) nhl_add_table_entry_int(L, "dknown", obj->dknown); nhl_add_table_entry_int(L, "bknown", obj->bknown); nhl_add_table_entry_int(L, "rknown", obj->rknown); + nhl_add_table_entry_int(L, "tknown", obj->tknown); if (obj->oclass == POTION_CLASS) nhl_add_table_entry_int(L, "odiluted", obj->odiluted); else diff --git a/src/nhlsel.c b/src/nhlsel.c index 2d0f92b4c..306820cf9 100644 --- a/src/nhlsel.c +++ b/src/nhlsel.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 nhlua.c $NHDT-Date: 1704225560 2024/01/02 19:59:20 $ $NHDT-Branch: keni-luabits2 $:$NHDT-Revision: 1.61 $ */ +/* NetHack 3.7 nhlua.c $NHDT-Date: 1737545957 2025/01/22 03:39:17 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.64 $ */ /* Copyright (c) 2018 by Pasi Kallinen */ /* NetHack may be freely redistributed. See license for details. */ @@ -266,7 +266,7 @@ l_selection_not(lua_State *L) sel = l_selection_check(L, 1); selection_clear(sel, 1); } else { - sel = l_selection_check(L, 1); + (void) l_selection_check(L, 1); (void) l_selection_clone(L); sel2 = l_selection_check(L, 2); selection_not(sel2); @@ -629,15 +629,17 @@ l_selection_randline(lua_State *L) staticfn int l_selection_grow(lua_State *L) { - const char *const growdirs[] = { + static const char *const growdirs[] = { "all", "random", "north", "west", "east", "south", NULL }; - const int growdirs2i[] = { + static const int growdirs2i[] = { W_ANY, W_RANDOM, W_NORTH, W_WEST, W_EAST, W_SOUTH, 0 }; - int argc = lua_gettop(L); - struct selectionvar *sel = l_selection_check(L, 1); - int dir = growdirs2i[luaL_checkoption(L, 2, "all", growdirs)]; + struct selectionvar *sel; + int dir, argc = lua_gettop(L); + + (void) l_selection_check(L, 1); + dir = growdirs2i[luaL_checkoption(L, 2, "all", growdirs)]; if (argc == 2) lua_pop(L, 1); /* get rid of growdir */ @@ -923,7 +925,7 @@ l_selection_iterate(lua_State *L) if (argc == 2 && lua_type(L, 2) == LUA_TFUNCTION) { sel = l_selection_check(L, 1); selection_getbounds(sel, &rect); - for (y = rect.ly; y <= rect.hy; y++) + for (y = rect.ly; y <= rect.hy; y++) { for (x = max(1,rect.lx); x <= rect.hx; x++) if (selection_getpoint(x, y, sel)) { coordxy tmpx = x, tmpy = y; @@ -937,6 +939,8 @@ l_selection_iterate(lua_State *L) goto out; } } + lua_gc(L, LUA_GCCOLLECT, 0); + } } else { nhl_error(L, "wrong parameters"); /*NOTREACHED*/ diff --git a/src/nhlua.c b/src/nhlua.c index 08de9cfe1..b43e4c089 100644 --- a/src/nhlua.c +++ b/src/nhlua.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 nhlua.c $NHDT-Date: 1711034373 2024/03/21 15:19:33 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.141 $ */ +/* NetHack 3.7 nhlua.c $NHDT-Date: 1744963460 2025/04/18 00:04:20 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.153 $ */ /* Copyright (c) 2018 by Pasi Kallinen */ /* NetHack may be freely redistributed. See license for details. */ @@ -25,6 +25,7 @@ struct e; +#ifndef SFCTOOL /* lua_CFunction prototypes */ #ifdef DUMPLOG staticfn int nhl_dump_fmtstr(lua_State *); @@ -64,6 +65,8 @@ staticfn int nhl_an(lua_State *); staticfn int nhl_rn2(lua_State *); staticfn int nhl_random(lua_State *); staticfn int nhl_level_difficulty(lua_State *); +staticfn int nhl_is_genocided(lua_State *); +staticfn int nhl_get_debug_themerm_name(lua_State *); staticfn void init_nhc_data(lua_State *); staticfn int nhl_push_anything(lua_State *, int, void *); staticfn int nhl_meta_u_index(lua_State *); @@ -89,7 +92,9 @@ staticfn void nhl_warn(void *, const char *, int); staticfn void nhl_clearfromtable(lua_State *, int, int, struct e *); staticfn int nhl_panic(lua_State *); staticfn void nhl_hookfn(lua_State *, lua_Debug *); +#endif /* !SFCTOOL */ +#ifndef SFCTOOL static const char *const nhcore_call_names[NUM_NHCORE_CALLS] = { "start_new_game", "restore_old_game", @@ -100,6 +105,7 @@ static const char *const nhcore_call_names[NUM_NHCORE_CALLS] = { "leave_tutorial", }; static boolean nhcore_call_available[NUM_NHCORE_CALLS]; +#endif /* internal structure that hangs off L->ud (but use lua_getallocf() ) * Note that we use it for both memory use tracking and instruction counting. @@ -124,6 +130,7 @@ typedef struct nhl_user_data { #endif } nhl_user_data; +#ifndef SFCTOOL static lua_State *luapat; /* instance for file pattern matching */ void @@ -430,6 +437,7 @@ nhl_gettrap(lua_State *L) get_trapname_bytype(ttmp->ttyp)); nhl_add_table_entry_bool(L, "tseen", ttmp->tseen); nhl_add_table_entry_bool(L, "madeby_u", ttmp->madeby_u); + nhl_add_table_entry_bool(L, "once", ttmp->once); switch (ttmp->ttyp) { case SQKY_BOARD: nhl_add_table_entry_int(L, "tnote", ttmp->tnote); @@ -954,6 +962,51 @@ nhl_level_difficulty(lua_State *L) return 1; } +/* local x = nh.is_genocided("vampire") */ +staticfn int +nhl_is_genocided(lua_State *L) +{ + int argc = lua_gettop(L); + + if (argc == 1) { + const char *paramstr = luaL_checkstring(L, 1); + int mgend; + int i = name_to_mon(paramstr, &mgend); + + lua_pushboolean(L, (i != NON_PM) + && (svm.mvitals[i].mvflags & G_GENOD) + ? TRUE : FALSE); + } else { + nhl_error(L, "Wrong args"); + } + return 1; +} + +/* local debug_themerm = nh.debug_themerm(isfill) + * if isfill is false, returns value of env variable THEMERM + * if isfill is true, returns value of env variable THEMERMFILL + * return nil if not in wizard mode or the variable isn't set */ +staticfn int +nhl_get_debug_themerm_name(lua_State *L) +{ + int argc = lua_gettop(L); + if (argc == 1) { + char *dbg_themerm = (char *) 0; + boolean is_fill = lua_toboolean(L, 1); + lua_pop(L, 1); + if (wizard) + dbg_themerm = getenv(is_fill ? "THEMERMFILL" : "THEMERM"); + if (!dbg_themerm || !*dbg_themerm) { + lua_pushnil(L); + } else { + lua_pushstring(L, dbg_themerm); + } + } else { + nhl_error(L, "debug_themerm should have 1 boolean arg"); + } + return 1; +} + RESTORE_WARNING_UNREACHABLE_CODE /* get mandatory integer value from table */ @@ -999,9 +1052,18 @@ char * get_table_str_opt(lua_State *L, const char *name, char *defval) { const char *ret; + int ltyp; lua_getfield(L, -1, name); - ret = luaL_optstring(L, -1, defval); + ltyp = lua_type(L, -1); + if (ltyp == LUA_TSTRING || ltyp == LUA_TNIL) { + ret = luaL_optstring(L, -1, defval); + } else if (ltyp == LUA_TFUNCTION) { + nhl_pcall_handle(L, 0, 1, "get_table_str_opt", NHLpa_panic); + ret = luaL_optstring(L, -1, defval); + } else { + nhl_error(L, "get_table_str_opt: no string"); + } if (ret) { lua_pop(L, 1); return dupstr(ret); @@ -1207,19 +1269,26 @@ get_nh_lua_variables(void) RESTORE_WARNING_UNREACHABLE_CODE +#endif /* !SFCTOOL */ + +#ifdef SFCTOOL +char *lua_data; +#endif + /* save nh_lua_variables table to file */ void save_luadata(NHFILE *nhfp) { unsigned lua_data_len; +#ifndef SFCTOOL char *lua_data = get_nh_lua_variables(); /* note: '\0' terminated */ +#endif if (!lua_data) lua_data = dupstr(emptystr); lua_data_len = Strlen(lua_data) + 1; /* +1: include the terminator */ - bwrite(nhfp->fd, (genericptr_t) &lua_data_len, - (unsigned) sizeof lua_data_len); - bwrite(nhfp->fd, (genericptr_t) lua_data, lua_data_len); + Sfo_unsigned(nhfp, &lua_data_len, "luadata-lua_data_len"); + Sfo_char(nhfp, lua_data, "luadata", lua_data_len); free(lua_data); } @@ -1227,20 +1296,26 @@ save_luadata(NHFILE *nhfp) void restore_luadata(NHFILE *nhfp) { - unsigned lua_data_len; + unsigned lua_data_len = 0; +#ifndef SFCTOOL char *lua_data; +#endif /* !SFCTOOL */ - mread(nhfp->fd, (genericptr_t) &lua_data_len, - (unsigned) sizeof lua_data_len); + Sfi_unsigned(nhfp, &lua_data_len, "luadata-lua_data_len"); lua_data = (char *) alloc(lua_data_len); - mread(nhfp->fd, (genericptr_t) lua_data, lua_data_len); + Sfi_char(nhfp, lua_data, "luadata", lua_data_len); + +#ifndef SFCTOOL if (!gl.luacore) l_nhcore_init(); luaL_loadstring(gl.luacore, lua_data); free(lua_data); nhl_pcall_handle(gl.luacore, 0, 0, "restore_luadata", NHLpa_panic); +#endif /* !SFCTOOL */ } +#ifndef SFCTOOL + /* local stairs = stairways(); */ staticfn int nhl_stairways(lua_State *L) @@ -1577,6 +1652,7 @@ nhl_gamestate(lua_State *L) struct obj *otmp; int argc = lua_gettop(L); boolean reststate = (argc > 0) ? lua_toboolean(L, -1) : FALSE; + int otyp; debugpline4("gamestate: %d:%d (%c vs %c)", u.uz.dnum, u.uz.dlevel, reststate ? 'T' : 'F', gg.gmst_stored ? 't' : 'f'); @@ -1608,11 +1684,18 @@ nhl_gamestate(lua_State *L) assert(gg.gmst_mvitals != NULL); (void) memcpy((genericptr_t) &svm.mvitals, gg.gmst_mvitals, sizeof svm.mvitals); + /* clear user-given object type names */ + for (otyp = 0; otyp < NUM_OBJECTS; otyp++) + if (objects[otyp].oc_uname) { + free(objects[otyp].oc_uname); + objects[otyp].oc_uname = (char *) 0; + } /* some restored state would confuse the level change in progress */ u.uz = cur_uz, u.uz0 = cur_uz0; init_uhunger(); free_tutorial(); /* release gg.gmst_XYZ */ gg.gmst_stored = FALSE; + (void) memcpy(svs.spl_book, gg.gmst_spl_book, sizeof svs.spl_book); } else if (!reststate && !gg.gmst_stored) { /* store game state */ gg.gmst_moves = svm.moves; @@ -1633,6 +1716,8 @@ nhl_gamestate(lua_State *L) gg.gmst_mvitals = (genericptr_t) alloc(sizeof svm.mvitals); (void) memcpy(gg.gmst_mvitals, (genericptr_t) &svm.mvitals, sizeof svm.mvitals); + (void) memcpy(gg.gmst_spl_book, svs.spl_book, sizeof svs.spl_book); + (void) memset(svs.spl_book, 0, sizeof(svs.spl_book)); gg.gmst_stored = TRUE; } else { impossible("nhl_gamestate: inconsistent state (%s vs %s)", @@ -1722,6 +1807,8 @@ static const struct luaL_Reg nhl_functions[] = { { "rn2", nhl_rn2 }, { "random", nhl_random }, { "level_difficulty", nhl_level_difficulty }, + { "is_genocided", nhl_is_genocided }, + { "debug_themerm", nhl_get_debug_themerm_name }, { "parse_config", nhl_parse_config }, { "get_config", nhl_get_config }, { "get_config_errors", l_get_config_errors }, @@ -1996,6 +2083,8 @@ nhl_pcall_handle(lua_State *L, int nargs, int nresults, const char *name, case NHLpa_panic: panic("Lua error %d:%s %s", nud->sid, nud->name ? nud->name : "(unknown)", lua_tostring(L, -1)); + /*NOTREACHED*/ + break; case NHLpa_impossible: impossible("Lua error: %d:%s %s", nud->sid, nud->name ? nud->name : "(unknown)", @@ -2809,7 +2898,7 @@ nhl_alloc(void *ud, void *ptr, size_t osize UNUSED, size_t nsize) return NULL; } - return re_alloc(ptr, nsize); + return re_alloc(ptr, (unsigned) nsize); } DISABLE_WARNING_UNREACHABLE_CODE @@ -2916,6 +3005,8 @@ nhlL_newstate(nhl_sandbox_info *sbi, const char *name) return L; } +#endif /* !SFCTOOL */ + /* (See end of comment for conclusion.) to make packages safe, we need something like: diff --git a/src/o_init.c b/src/o_init.c index aeff43030..1185c177f 100644 --- a/src/o_init.c +++ b/src/o_init.c @@ -1,10 +1,11 @@ -/* NetHack 3.7 o_init.c $NHDT-Date: 1720391455 2024/07/07 22:30:55 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.87 $ */ +/* NetHack 3.7 o_init.c $NHDT-Date: 1756520041 2025/08/29 18:14:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.96 $ */ /* 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" +#ifndef SFCTOOL staticfn void setgemprobs(d_level *); staticfn void randomize_gem_colors(void); staticfn void shuffle(int, int, boolean); @@ -347,7 +348,7 @@ shuffle_all(void) /* Return TRUE if the provided string matches the unidentified description of * the provided object. */ boolean -objdescr_is(struct obj* obj, const char * descr) +objdescr_is(struct obj *obj, const char *descr) { const char *objdescr; @@ -375,12 +376,15 @@ savenames(NHFILE *nhfp) int i; unsigned int len; - if (perform_bwrite(nhfp)) { - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) svb.bases, sizeof svb.bases); - bwrite(nhfp->fd, (genericptr_t) svd.disco, sizeof svd.disco); - bwrite(nhfp->fd, (genericptr_t) objects, - sizeof(struct objclass) * NUM_OBJECTS); + if (update_file(nhfp)) { + for (i = 0; i < (MAXOCLASSES + 2); ++i) { + Sfo_int(nhfp, &svb.bases[i], "names-bases"); + } + for (i = 0; i < NUM_OBJECTS; ++i) { + Sfo_short(nhfp, &svd.disco[i], "names-disco"); + } + for (i = 0; i < NUM_OBJECTS; ++i) { + Sfo_objclass(nhfp, &objects[i], "names-objclass"); } } /* as long as we use only one version of Hack we @@ -388,12 +392,11 @@ savenames(NHFILE *nhfp) oc_uname for all objects */ for (i = 0; i < NUM_OBJECTS; i++) if (objects[i].oc_uname) { - if (perform_bwrite(nhfp)) { + if (update_file(nhfp)) { len = Strlen(objects[i].oc_uname) + 1; - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &len, sizeof len); - bwrite(nhfp->fd, (genericptr_t) objects[i].oc_uname, len); - } + Sfo_unsigned(nhfp, &len, "names-len"); + Sfo_char(nhfp, objects[i].oc_uname, "names-oc_uname", + (int) len); } if (release_data(nhfp)) { free((genericptr_t) objects[i].oc_uname); @@ -401,6 +404,7 @@ savenames(NHFILE *nhfp) } } } +#endif /* !SFCTOOL */ void restnames(NHFILE *nhfp) @@ -408,28 +412,30 @@ restnames(NHFILE *nhfp) int i; unsigned int len = 0; - if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) svb.bases, sizeof svb.bases); - mread(nhfp->fd, (genericptr_t) svd.disco, sizeof svd.disco); - mread(nhfp->fd, (genericptr_t) objects, - NUM_OBJECTS * sizeof (struct objclass)); + for (i = 0; i < (MAXOCLASSES + 2); ++i) { + Sfi_int(nhfp, &svb.bases[i], "names-bases"); + } + for (i = 0; i < NUM_OBJECTS; ++i) { + Sfi_short(nhfp, &svd.disco[i], "names-disco"); + } + for (i = 0; i < NUM_OBJECTS; ++i) { + Sfi_objclass(nhfp, &objects[i], "names-objclass"); } for (i = 0; i < NUM_OBJECTS; i++) { if (objects[i].oc_uname) { - if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) &len, sizeof len); - } + Sfi_unsigned(nhfp, &len, "names-len"); objects[i].oc_uname = (char *) alloc(len); - if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) objects[i].oc_uname, len); - } + Sfi_char(nhfp, objects[i].oc_uname, "names-oc_uname", (int) len); } } +#ifndef SFCTOOL #ifdef TILES_IN_GLYPHMAP shuffle_tiles(); #endif +#endif } +#ifndef SFCTOOL void discover_object( int oindx, @@ -561,6 +567,7 @@ sortloot_descr(int otyp, char *outbuf) sl_cookie.orderclass, sl_cookie.subclass, sl_cookie.disco); return outbuf; } +#endif /* !SFCTOOL */ #define DISCO_BYCLASS 0 /* by discovery order within each class */ #define DISCO_SORTLOOT 1 /* by discovery order within each subclass */ @@ -576,6 +583,8 @@ static const char *const disco_orders_descr[] = { (char *) 0 }; +#ifndef SFCTOOL + int choose_disco_sort( int mode) /* 0 => 'O' cmd, 1 => full discoveries; 2 => class disco */ @@ -683,9 +692,10 @@ disco_append_typename(char *buf, int dis) /* sort and output sorted_lines to window and free the lines */ staticfn void -disco_output_sorted(winid tmpwin, - char **sorted_lines, int sorted_ct, - boolean lootsort) +disco_output_sorted( + winid tmpwin, + char **sorted_lines, int sorted_ct, + boolean lootsort) { char *p; int j; @@ -693,6 +703,7 @@ disco_output_sorted(winid tmpwin, qsort(sorted_lines, sorted_ct, sizeof (char *), discovered_cmp); for (j = 0; j < sorted_ct; ++j) { p = sorted_lines[j]; + assert(p != NULL); /* pacify static analyzer */ if (lootsort) { p[6] = p[0]; /* '*' or ' ' */ p += 6; @@ -1109,6 +1120,7 @@ rename_disco(void) destroy_nhwindow(tmpwin); return; } +#endif /* !SFCTOOL */ void get_sortdisco(char *opts, boolean cnf) diff --git a/src/objects.c b/src/objects.c index 45ceaeeab..9389e3863 100644 --- a/src/objects.c +++ b/src/objects.c @@ -3,6 +3,7 @@ /* NetHack may be freely redistributed. See license for details. */ #include "config.h" +#include "weight.h" #include "obj.h" #include "prop.h" diff --git a/src/objnam.c b/src/objnam.c index dfb6ab11d..e05ea1c12 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 objnam.c $NHDT-Date: 1711809641 2024/03/30 14:40:41 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.427 $ */ +/* NetHack 3.7 objnam.c $NHDT-Date: 1745114235 2025/04/19 17:57:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.453 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -32,9 +32,9 @@ struct _readobjnam_data { char fruitbuf[BUFSZ]; }; -staticfn char *strprepend(char *, const char *); -staticfn char *nextobuf(void); -staticfn void releaseobuf(char *); +staticfn char *strprepend(char *, const char *) NONNULL NONNULLARG1; +staticfn char *nextobuf(void) NONNULL; +staticfn void releaseobuf(char *) NONNULLARG1; staticfn void xcalled(char *, int, const char *, const char *); staticfn char *xname_flags(struct obj *, unsigned); staticfn char *minimal_xname(struct obj *); @@ -48,6 +48,7 @@ staticfn boolean badman(const char *, boolean); staticfn boolean wishymatch(const char *, const char *, boolean); staticfn short rnd_otyp_by_wpnskill(schar); staticfn short rnd_otyp_by_namedesc(const char *, char, int); +staticfn void set_wallprop_from_str(char *) NONNULLARG1; staticfn struct obj *wizterrainwish(struct _readobjnam_data *); staticfn void dbterrainmesg(const char *, coordxy, coordxy) NONNULLARG1; staticfn void readobjnam_init(char *, struct _readobjnam_data *); @@ -121,15 +122,16 @@ static const struct Jitem Japanese_items[] = { staticfn char * strprepend(char *s, const char *pref) { + char star_s = *s; int i = (int) strlen(pref); if (i > PREFIX) { impossible("PREFIX too short (for %d).", i); return s; } - s -= i; - (void) strncpy(s, pref, i); /* do not copy trailing 0 */ - return s; + copynchars(s - i, pref, i + 1); + *s = star_s; + return s - i; } /* manage a pool of BUFSZ buffers, so callers don't have to */ @@ -218,8 +220,7 @@ obj_typename(int otyp) buf[0] = '\0'; /* redundant */ switch (ocl->oc_class) { case COIN_CLASS: - Strcpy(buf, "coin"); - break; + return strcpy(buf, actualn); /* "gold piece" */ case POTION_CLASS: Strcpy(buf, "potion"); break; @@ -250,9 +251,17 @@ obj_typename(int otyp) if (dn) Sprintf(eos(buf), " (%s)", dn); return buf; + case ARMOR_CLASS: + if (objects[otyp].oc_armcat == ARM_GLOVES + || objects[otyp].oc_armcat == ARM_BOOTS) + Strcpy(buf, "pair of "); + else if (otyp >= GRAY_DRAGON_SCALES && otyp <= YELLOW_DRAGON_SCALES) + Strcpy(buf, "set of "); + FALLTHROUGH; + /*FALLTHRU*/ default: if (nn) { - Strcpy(buf, actualn); + Strcat(buf, actualn); if (GemStone(otyp)) Strcat(buf, " stone"); if (un) /* 3: length of " (" + ")" which will enclose 'dn' */ @@ -260,7 +269,7 @@ obj_typename(int otyp) if (dn) Sprintf(eos(buf), " (%s)", dn); } else { - Strcpy(buf, dn ? dn : actualn); + Strcat(buf, dn ? dn : actualn); if (ocl->oc_class == GEM_CLASS) Strcat(buf, (ocl->oc_material == MINERAL) ? " stone" : " gem"); @@ -676,6 +685,7 @@ xname_flags( case WEAPON_CLASS: if (is_poisonable(obj) && obj->opoisoned) Strcpy(buf, "poisoned "); + FALLTHROUGH; /*FALLTHRU*/ case VENOM_CLASS: case TOOL_CLASS: @@ -808,6 +818,9 @@ xname_flags( more robust because the default value for that overloaded field (obj->corpsenm) is NON_PM (-1) rather than 0 */ Strcat(strcpy(buf, "next "), actualn); /* "next boulder" */ + /* once "next boulder" occurs, subsequent messages should just + use ordinary "boulder" */ + obj->next_boulder = 0; } else { Strcpy(buf, actualn); /* "boulder" or "statue" */ } @@ -1332,6 +1345,13 @@ doname_base( Strcat(prefix, "uncursed "); } + /* "a large trapped box" would perhaps be more correct; [no!] + what about ``(obj->tknown && !obj->otrapped)''? shouldn't that + yield "a non-trapped large box"? (not "an untrapped large box"); + TODO: this should be ``(Is_box(obj) || obj->otyp == TIN) && ...'' + but at present there's no way to set obj->tknown for tins */ + if (Is_box(obj) && obj->otrapped && obj->tknown && obj->dknown) + Strcat(prefix,"trapped "); if (lknown && Is_box(obj)) { if (obj->obroken) /* 3.6.0 used "unlockable" here but that could be misunderstood @@ -1390,6 +1410,7 @@ doname_base( ConcatF1(bp, 1, ", %s lit)", arti_light_description(obj)); } } + FALLTHROUGH; /*FALLTHRU*/ case WEAPON_CLASS: if (ispoisoned) @@ -2372,6 +2393,22 @@ Ysimple_name2(struct obj *obj) return s; } + /* + * FIXME: + * simpleonames(), ansimpleoname(), and thesimpleoname() need to + * know the beginning of the obuf[] they use so that they can + * guard against buffer overflow when pluralizing (is that an + * actual word?) or inserting "an" or "the". + * + * minimal_xname() returns a call to xname() which writes into + * the middle of its obuf[] then backs up to accomodate a prefix, + * so BUFSZ is not a reliable limit for the length of the result. + * + * [Overflow likely moot. Since the formatted object name has + * user-supplied name suppressed, the length is sure to be short + * enough to added plural suffix or "an" or "the" prefix.] + */ + /* "scroll" or "scrolls" */ char * simpleonames(struct obj *obj) @@ -2405,12 +2442,14 @@ ansimpleoname(struct obj *obj) if (objects[otyp].oc_unique && OBJ_NAME(objects[otyp]) && !strcmp(simpleoname, OBJ_NAME(objects[otyp]))) { /* the() will allocate another obuf[]; we want to avoid using two */ - Strcpy(simpleoname, obufp = the(simpleoname)); + obufp = the(simpleoname); + Strcpy(simpleoname, obufp); releaseobuf(obufp); } else if (obj->quan == 1L) { /* simpleoname[] is singular if quan==1, plural otherwise; an() will allocate another obuf[]; we want to avoid using two */ - Strcpy(simpleoname, obufp = an(simpleoname)); + obufp = an(simpleoname); + Strcpy(simpleoname, obufp); releaseobuf(obufp); } return simpleoname; @@ -2423,7 +2462,8 @@ thesimpleoname(struct obj *obj) char *obufp, *simpleoname = simpleonames(obj); /* the() will allocate another obuf[]; we want to avoid using two */ - Strcpy(simpleoname, obufp = the(simpleoname)); + obufp = the(simpleoname); + Strcpy(simpleoname, obufp); releaseobuf(obufp); return simpleoname; } @@ -2474,7 +2514,7 @@ static const char wrpsym[] = { WAND_CLASS, RING_CLASS, POTION_CLASS, /* return form of the verb (input plural) if xname(otmp) were the subject */ char * -otense(struct obj* otmp,const char * verb) +otense(struct obj *otmp, const char *verb) { char *buf; @@ -3350,6 +3390,7 @@ static const struct alt_spellings { { "kelp", KELP_FROND }, { "eucalyptus", EUCALYPTUS_LEAF }, { "lembas", LEMBAS_WAFER }, + { "tripe", TRIPE_RATION }, { "cookie", FORTUNE_COOKIE }, { "pie", CREAM_PIE }, { "huge meatball", ENORMOUS_MEATBALL }, /* likely conflated name */ @@ -3479,16 +3520,31 @@ shiny_obj(char oclass) return (int) rnd_otyp_by_namedesc("shiny", oclass, 0); } +/* set wall under hero undiggable/unphaseable from string */ +staticfn void +set_wallprop_from_str(char *bp) +{ + int wall_prop = 0; + + if (strstr(bp, "undiggable ") || strstr(bp, "nondiggable ")) + wall_prop |= W_NONDIGGABLE; + if (strstr(bp, "unphaseable ") || strstr(bp, "nonpasswall ")) + wall_prop |= W_NONPASSWALL; + /* |= because wall_info (aka flags) is overloaded with other stuff */ + if (wall_prop) + levl[u.ux][u.uy].wall_info |= wall_prop; +} + /* in wizard mode, readobjnam() can accept wishes for traps and terrain */ staticfn struct obj * wizterrainwish(struct _readobjnam_data *d) { struct rm *lev; - boolean madeterrain = FALSE, badterrain = FALSE, didblock, is_dbridge; + boolean madeterrain = FALSE, badterrain = FALSE, is_dbridge; int trap; unsigned oldtyp, ltyp; coordxy x = u.ux, y = u.uy; - char *bp = d->bp, *p = d->p; + char *bp = d->bp, *p; for (trap = NO_TRAP + 1; trap < TRAPNUM; trap++) { struct trap *t; @@ -3516,7 +3572,6 @@ wizterrainwish(struct _readobjnam_data *d) lev = &levl[x][y]; oldtyp = lev->typ; is_dbridge = (oldtyp == DRAWBRIDGE_DOWN || oldtyp == DRAWBRIDGE_UP); - didblock = does_block(x, y, lev); p = eos(bp); if (!BSTRCMPI(bp, p - 8, "fountain")) { lev->typ = FOUNTAIN; @@ -3649,11 +3704,13 @@ wizterrainwish(struct _readobjnam_data *d) } else if (!BSTRCMPI(bp, p - 4, "tree")) { lev->typ = TREE; lev->looted = d->looted ? (TREE_LOOTED | TREE_SWARM) : 0; + set_wallprop_from_str(bp); pline("A tree."); madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 4, "bars")) { lev->typ = IRONBARS; lev->flags = 0; + set_wallprop_from_str(bp); /* [FIXME: if this isn't a wall or door location where 'horizontal' is already set up, that should be calculated for this spot. Unfortunately, it can be tricky; placing one in open space @@ -3713,10 +3770,9 @@ wizterrainwish(struct _readobjnam_data *d) lev->wall_info |= (old_wall_info & WM_MASK); /* set up trapped flag; open door states aren't eligible */ if (d->trapped == 2 /* 2: wish includes explicit "untrapped" */ - || secret /* secret doors can't be trapped due to their use - * of both doormask and wall_info; those both - * overlay rm->flags and partially conflict */ - || (lev->doormask & (D_LOCKED | D_CLOSED)) == 0) + || ((lev->doormask & (D_LOCKED | D_CLOSED)) == 0 + /* D_CLOSED is implicit for secret doors */ + && !secret)) d->trapped = 0; if (d->trapped) lev->doormask |= D_TRAPPED; @@ -3750,7 +3806,7 @@ wizterrainwish(struct _readobjnam_data *d) badterrain = TRUE; } } else if (!BSTRCMPI(bp, p - 4, "wall") - && (bp == p - 4 || p[-4] == ' ')) { + && (bp == p - 4 || p[-5] == ' ')) { schar wall = HWALL; if ((isok(u.ux, u.uy-1) && IS_WALL(levl[u.ux][u.uy-1].typ)) @@ -3758,6 +3814,8 @@ wizterrainwish(struct _readobjnam_data *d) wall = VWALL; madeterrain = TRUE; lev->typ = wall; + lev->flags = 0; + set_wallprop_from_str(bp); fix_wall_spines(max(0,u.ux-1), max(0,u.uy-1), min(COLNO,u.ux+1), min(ROWNO,u.uy+1)); pline("A wall."); @@ -3809,14 +3867,7 @@ wizterrainwish(struct _readobjnam_data *d) } else { if (u.utrap && u.utraptype == TT_LAVA && !is_lava(u.ux, u.uy)) reset_utrap(FALSE); - - if (does_block(x, y, lev)) { - if (!didblock) - block_point(x, y); - } else { - if (didblock) - unblock_point(x, y); - } + recalc_block_point(x, y); } /* fixups for replaced terrain that aren't handled above */ @@ -4487,8 +4538,8 @@ readobjnam_postparse1(struct _readobjnam_data *d) return 4; /*goto any;*/ } - /* Search for class names: XXXXX potion, scroll of XXXXX. Avoid */ - /* false hits on, e.g., rings for "ring mail". */ + /* Search for class names: XXXXX potion, scroll of XXXXX. + Avoid false hits on, e.g., rings for "ring mail". */ if (strncmpi(d->bp, "enchant ", 8) && strncmpi(d->bp, "destroy ", 8) && strncmpi(d->bp, "detect food", 11) @@ -4528,12 +4579,26 @@ readobjnam_postparse1(struct _readobjnam_data *d) if (d->p > d->bp && d->p[-1] == ' ') d->p[-1] = '\0'; } else { + int k, l; + char amubuf[BUFSZ]; + /* amulet without "of"; convoluted wording but better a special case that's handled than one that's missing */ if (!strncmpi(d->bp, "versus poison ", 14)) { d->typ = AMULET_VERSUS_POISON; return 2; /*goto typfnd;*/ } + /* check for " amulet"; strip off trailing + " amulet" for that w/o changing contents of d->bp */ + l = (int) strlen(d->bp) - j; + if (l > 0 && d->bp[l - 1] == ' ') + l -= 1; + copynchars(amubuf, d->bp, min(l, (int) sizeof amubuf - 1)); + k = rnd_otyp_by_namedesc(amubuf, AMULET_CLASS, 0); + if (k != STRANGE_OBJECT) { + d->typ = k; + return 2; /*goto typfnd;*/ + } } d->actualn = d->dn = d->bp; return 1; /*goto srch;*/ @@ -5057,7 +5122,8 @@ readobjnam(char *bp, struct obj *no_wish) break; case SLIME_MOLD: d.otmp->spe = d.ftype; - /* Fall through */ + FALLTHROUGH; + /* FALLTHRU */ case SKELETON_KEY: case CHEST: case LARGE_BOX: @@ -5089,7 +5155,8 @@ readobjnam(char *bp, struct obj *no_wish) /* scroll of mail: 0: delivered in-game via external event (or randomly for fake mail); 1: from bones or wishing; 2: written with marker */ case SCR_MAIL: - /*FALLTHRU*/ + d.otmp->spe = 1; + break; #endif /* splash of venom: 0: normal, and transitory; 1: wishing */ case ACID_VENOM: @@ -5101,6 +5168,7 @@ readobjnam(char *bp, struct obj *no_wish) d.otmp->spe = (rn2(10) ? -1 : 0); break; } + FALLTHROUGH; /*FALLTHRU*/ default: d.otmp->spe = d.spe; @@ -5283,6 +5351,9 @@ readobjnam(char *bp, struct obj *no_wish) } } + if (permapoisoned(d.otmp)) + d.otmp->opoisoned = 1; + /* more wishing abuse: don't allow wishing for certain artifacts */ /* and make them pay; charge them for the wish anyway! */ if ((is_quest_artifact(d.otmp) @@ -5309,7 +5380,7 @@ readobjnam(char *bp, struct obj *no_wish) } d.otmp->owt = weight(d.otmp); if (d.very && d.otmp->otyp == HEAVY_IRON_BALL) - d.otmp->owt += IRON_BALL_W_INCR; + d.otmp->owt += WT_IRON_BALL_INCR; return d.otmp; } @@ -5554,6 +5625,7 @@ safe_qbuf( lenlimit = QBUFSZ - 1; endp = qbuf + lenlimit; + assert(endp != NULL); /* workaround for static analyzer issue */ /* sanity check, aimed mainly at paniclog (it's conceivable for the result of short_oname() to be shorter than the length of the last resort string, but we ignore that possibility here) */ diff --git a/src/options.c b/src/options.c index 28eaa91c3..090300fe1 100644 --- a/src/options.c +++ b/src/options.c @@ -1,19 +1,19 @@ -/* NetHack 3.7 options.c $NHDT-Date: 1710792444 2024/03/18 20:07:24 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.723 $ */ +/* NetHack 3.7 options.c $NHDT-Date: 1737556914 2025/01/22 06:41:54 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.753 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2008. */ /* NetHack may be freely redistributed. See license for details. */ -#ifdef OPTION_LISTS_ONLY /* (AMIGA) external program for opt lists */ +#ifndef OPTION_LISTS_ONLY +#include "hack.h" +#include "tcap.h" +#else /* OPTION_LISTS_ONLY: (AMIGA) external program for opt lists */ #include "config.h" #include "objclass.h" #include "flag.h" NEARDATA struct flag flags; /* provide linkage */ NEARDATA struct instance_flags iflags; /* provide linkage */ +NEARDATA struct accessibility_data a11y; #define static -#else -#include "hack.h" -#include "tcap.h" -#include #endif #define BACKWARD_COMPAT @@ -111,7 +111,7 @@ static struct allopt_t allopt[SIZE(allopt_init)]; /* use rest of file */ -extern char configfile[]; /* for messages; files.c */ +/* extern char configfile[]; */ /* for messages; files.c */ extern const struct symparse loadsyms[]; #if defined(TOS) extern boolean colors_changed; /* in tos.c */ @@ -257,6 +257,46 @@ static NEARDATA const char *perminv_modes[][3] = { /*8*/ { "in-use", "inuse-only", "subset: items currently in use" }, }; +struct objsymopt { + int num; + const char *nam; + const char *descr; +}; + +/* + * menuobjsyms: + * Inventory display for the various values of menuobjsyms. + * 4' and 5' represent !sortpack which lacks headers; they + * produce the same result. + * + * 0: 1: + * Weapons Weapons (')') + * a - 15 darts a - 15 darts + * Armor Armor ('[') + * b - Hawaiian shirt b - Hawaiian shirt + * 2: 3: + * Weapons Weapons (')') + * a ) 15 darts a ) 15 darts + * Armor Armor ('[') + * b [ Hawaiian shirt b [ Hawaiian shirt + * 4: 5: + * Weapons Weapons (')') + * a - 15 darts a - 15 darts + * Armor Armor ('[') + * b - Hawaiian shirt b - Hawaiian shirt + * 4': 5': + * a ) 15 darts a ) 15 darts + * b [ Hawaiian shirt b [ Hawaiian shirt + */ +static const struct objsymopt objsymvals[] = { + { 0, "none", "don't show object symbols in menus" }, + { 1, "headers", "show object symbols in menu header lines" }, + { 2, "entries", "show object symbols in individual menu entries" }, + { 3, "both", "show object symbols in headers and menu entries" }, + { 4, "conditional", "show objsyms in entries if no headers are shown" }, + { 5, "one-or-other", "show objsyms in header, in entries if no header" }, +}; + /* * Default menu manipulation command accelerators. These may _not_ be: * @@ -323,6 +363,7 @@ staticfn void rejectoption(const char *); staticfn char *string_for_opt(char *, boolean); staticfn char *string_for_env_opt(const char *, char *, boolean); staticfn void bad_negation(const char *, boolean); +staticfn void set_menuobjsyms_flags(int); staticfn int change_inv_order(char *); staticfn boolean warning_opts(char *, const char *); staticfn int feature_alert_opts(char *, const char *); @@ -368,6 +409,7 @@ staticfn int handler_align_misc(int); staticfn int handler_autounlock(int); staticfn int handler_disclose(void); staticfn int handler_menu_headings(void); +staticfn int handler_menu_objsyms(void); staticfn int handler_menustyle(void); staticfn int handler_msg_window(void); staticfn int handler_number_pad(void); @@ -415,8 +457,8 @@ ask_do_tutorial(void) boolean norc; int n, pass = 0; - rc = nh_basename(configfile, TRUE); - norc = !strcmp(configfile, "/dev/null"); + rc = nh_basename(get_configfile(), TRUE); + norc = !strcmp(get_configfile(), "/dev/null"); Snprintf(buf, sizeof buf, "Put \"OPTIONS=!tutorial\" in %s to skip this query.", (rc && *rc && !norc) ? rc : "your configuration file"); @@ -772,7 +814,7 @@ freeroleoptvals(void) void saveoptvals(NHFILE *nhfp) { - if (perform_bwrite(nhfp)) { + if (update_file(nhfp)) { char *val; unsigned len; int i, j; @@ -781,11 +823,9 @@ saveoptvals(NHFILE *nhfp) for (j = 0; j < num_opt_phases; ++j) { val = roleoptvals[i][j]; len = val ? Strlen(val) + 1 : 0; - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &len, sizeof len); - if (val) - bwrite(nhfp->fd, (genericptr_t) val, len); - } + Sfo_unsigned(nhfp, &len, "optvals-len"); + if (val) + Sfo_char(nhfp, val, "optvals-val", len); } } if (release_data(nhfp)) @@ -804,10 +844,10 @@ restoptvals(NHFILE *nhfp) for (i = 0; i < 4; ++i) for (j = 0; j < num_opt_phases; ++j) { /* len includes terminating '\0' for non-Null values */ - mread(nhfp->fd, (genericptr_t) &len, sizeof len); + Sfi_unsigned(nhfp, &len, "optvals-len"); if (len) { val = roleoptvals[i][j] = (char *) alloc(len); - mread(nhfp->fd, (genericptr_t) val, len); + Sfi_char(nhfp, val, "opvals-val", (int) len); } else { roleoptvals[i][j] = NULL; } @@ -1238,11 +1278,11 @@ optfn_crash_email( return optn_ok; } if (req == do_set) { - if ((op = string_for_opt(opts, FALSE)) - != empty_optstr) { - gc.crash_email = dupstr(op); - } else + if ((op = string_for_opt(opts, FALSE)) == empty_optstr) return optn_err; + if (gc.crash_email) + free((genericptr_t) gc.crash_email); + gc.crash_email = dupstr(op); return optn_ok; } if (req == get_val || req == get_cnf_val) { @@ -1264,11 +1304,11 @@ optfn_crash_name( return optn_ok; } if (req == do_set) { - if ((op = string_for_opt(opts, FALSE)) - != empty_optstr) { - gc.crash_name = dupstr(op); - } else + if ((op = string_for_opt(opts, FALSE)) == empty_optstr) return optn_err; + if (gc.crash_name) + free((genericptr_t) gc.crash_name); + gc.crash_name = dupstr(op); return optn_ok; } if (req == get_val || req == get_cnf_val) { @@ -1311,6 +1351,7 @@ optfn_crash_urlmax( } return optn_ok; } + #endif /* CRASHREPORT */ #ifdef CURSES_GRAPHICS @@ -2186,6 +2227,71 @@ optfn_menu_headings( return optn_ok; } +staticfn int +optfn_menu_objsyms( + int optidx, int req, + boolean negated, + char *opts, char *op) +{ + if (req == do_init) { + /* set iflags.menu_objsyms to 4, "conditional"; also sets + iflags.menu_head_objsym to False and + iflags.use_menu_glyphs True */ + set_menuobjsyms_flags(4); + return optn_ok; + } + if (req == do_set) { + unsigned k, l; + int i, osyms; + + if (negated) { + /* allow '!menu_objsyms' (and '!use_menu_glyphs') as + 'menu_objsyms:none' (0) */ + osyms = 0; + } else if (op == empty_optstr) { + /* treat boolean 'menu_objsyms' as 'menu_objsyms:headers' (1) + accept obsolete boolean 'use_menu_glyphs' as a synonym + for 'menu_objsyms:entries' (2) */ + osyms = !strncmp(opts, "use_menu_glyphs", 15) ? 2 : 1; + } else if (digit(*op)) { + i = atoi(op); + if (i >= SIZE(objsymvals)) { + config_error_add("Illegal %s parameter '%s'", + allopt[optidx].name, op); + return optn_err; + } + osyms = i; + } else { + /* stilted "one-or-other" is used to compress the menu width */ + static const char alt5[] = "one-or-the-other"; + unsigned l5 = (unsigned) (sizeof alt5 - sizeof ""); + + osyms = 0; + k = (unsigned) strlen(op); + for (i = 0; i < SIZE(objsymvals); ++i) { + l = (unsigned) strlen(objsymvals[i].nam); + if (k >= 4) + l = k; + if (!strncmpi(objsymvals[i].nam, op, l) + || (i == 5 && !strncmpi(alt5, op, l5))) { + osyms = i; + break; + } + } + } + set_menuobjsyms_flags(osyms); + return optn_ok; + } + if (req == get_val || req == get_cnf_val) { + Sprintf(opts, "%s", objsymvals[iflags.menuobjsyms].nam); + return optn_ok; + } + if (req == do_handler) { + return handler_menu_objsyms(); + } + return optn_ok; +} + staticfn int optfn_menuinvertmode( int optidx, int req, @@ -3451,6 +3557,9 @@ optfn_roguesymset( } if (req == do_set) { if (op != empty_optstr) { + if (gs.symset[ROGUESET].name) + free((genericptr_t) gs.symset[ROGUESET].name), + gs.symset[ROGUESET].name = 0; gs.symset[ROGUESET].name = dupstr(op); if (!read_sym_file(ROGUESET)) { clear_symsetentry(ROGUESET, TRUE); @@ -3621,6 +3730,7 @@ optfn_scores( allopt[optidx].name); return optn_silenterr; } + FALLTHROUGH; /*FALLTHRU*/ default: config_error_add("Unknown %s parameter '%s'", @@ -3737,8 +3847,11 @@ optfn_soundlib( */ if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE)) != empty_optstr) { + enum soundlib_ids option_id; get_soundlib_name(soundlibbuf, WINTYPELEN); + option_id = soundlib_id_from_opt(op); + gc.chosen_soundlib = option_id; assign_soundlib(gc.chosen_soundlib); } else return optn_err; @@ -4066,6 +4179,9 @@ optfn_symset( } if (req == do_set) { if (op != empty_optstr) { + if (gs.symset[PRIMARYSET].name) + free((genericptr_t) gs.symset[PRIMARYSET].name), + gs.symset[PRIMARYSET].name = 0; gs.symset[PRIMARYSET].name = dupstr(op); if (!read_sym_file(PRIMARYSET)) { clear_symsetentry(PRIMARYSET, TRUE); @@ -5291,6 +5407,9 @@ optfn_boolean( case opt_rest_on_space: update_rest_on_space(); break; + case opt_accessiblemsg: + a11y.msg_loc.x = a11y.msg_loc.y = 0; + break; default: break; } @@ -5371,7 +5490,11 @@ can_set_perm_invent(void) iflags.perminv_mode = InvOptOn; #ifdef TTY_PERM_INVENT - if (WINDOWPORT(tty) && !go.opt_initial) { + if ((WINDOWPORT(tty) +#ifdef WIN32 + || WINDOWPORT(safestartup) +#endif + ) && !go.opt_initial) { perm_invent_toggled(FALSE); /* perm_invent_toggled() -> sync_perminvent() @@ -5388,6 +5511,20 @@ can_set_perm_invent(void) return TRUE; } + +#ifdef TTY_PERM_INVENT +void +check_perm_invent_again(void) +{ + if (iflags.perm_invent_pending) { + iflags.perm_invent = FALSE; + if (can_set_perm_invent()) + iflags.perm_invent = TRUE; + iflags.perm_invent_pending = FALSE; + } +} +#endif + staticfn int handler_menustyle(void) { @@ -5639,6 +5776,43 @@ handler_menu_headings(void) return optn_ok; } +staticfn int +handler_menu_objsyms(void) +{ + winid tmpwin; + anything any; + char buf[BUFSZ]; + menu_item *picklist = (menu_item *) 0; + const char sep = iflags.menu_tab_sep ? '\t' : ' '; + int i, j, n, clr = NO_COLOR; + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin, MENU_BEHAVE_STANDARD); + any = cg.zeroany; + for (i = 0; i < SIZE(objsymvals); ++i) { + Snprintf(buf, sizeof buf, "%-12.12s%c%.60s", + objsymvals[i].nam, sep, objsymvals[i].descr); + any.a_int = i + 1; + j = objsymvals[i].num; + add_menu(tmpwin, &nul_glyphinfo, &any, '0' + i, *buf, + ATR_NONE, clr, buf, + (j == iflags.menuobjsyms) ? MENU_ITEMFLAGS_SELECTED + : MENU_ITEMFLAGS_NONE); + } + end_menu(tmpwin, "Set object symbols in menus to what?"); + n = select_menu(tmpwin, PICK_ONE, &picklist); + if (n > 0) { + i = picklist[0].item.a_int - 1; + /* if there are two picks, use the one that wasn't pre-selected */ + if (n > 1 && i == iflags.menuobjsyms) + i = picklist[1].item.a_int - 1; + set_menuobjsyms_flags(i); + free((genericptr_t) picklist); + } + destroy_nhwindow(tmpwin); + return optn_ok; +} + staticfn int handler_msg_window(void) { @@ -6623,10 +6797,10 @@ staticfn void rejectoption(const char *optname) { #ifdef MICRO - pline("\"%s\" settable only from %s.", optname, configfile); + pline("\"%s\" settable only from %s.", optname, get_configfile()); #else pline("%s can be set only from NETHACKOPTIONS or %s.", optname, - configfile); + get_configfile()); #endif } @@ -6932,6 +7106,11 @@ initoptions(void) */ #endif #endif /* SYSCF */ + + /* Carry out options that got deferred from early_options */ + if (gd.deferred_showpaths) + do_deferred_showpaths(0); /* does not return */ + initoptions_finish(); } @@ -6945,7 +7124,9 @@ initoptions_init(void) int i; boolean have_branch = (nomakedefs.git_branch && *nomakedefs.git_branch); - go.opt_phase = builtin_opt; // Did I need to move this here? + 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(); @@ -6996,6 +7177,7 @@ initoptions_init(void) flags.pile_limit = PILE_LIMIT_DFLT; /* 5 */ flags.runmode = RUN_LEAP; iflags.msg_history = 20; + /* msg_window has conflicting defaults for multi-interface binary */ #ifdef TTY_GRAPHICS iflags.prevmsg_window = 's'; @@ -7273,6 +7455,16 @@ initoptions_finish(void) ******************************************* */ +/* iflags.menuobjsyms also controls iflags.menu_head_objsym, and + iflags.use_menu_glyphs; they affect execution but are no longer options */ +staticfn void +set_menuobjsyms_flags(int newobjsyms) +{ + iflags.menuobjsyms = newobjsyms; + iflags.menu_head_objsym = ((newobjsyms & 1) != 0) ? TRUE : FALSE; + iflags.use_menu_glyphs = ((newobjsyms & (2 | 4)) != 0) ? TRUE : FALSE; +} + /* * Change the inventory order, using the given string as the new order. * Missing characters in the new order are filled in at the end from @@ -7585,6 +7777,7 @@ msgtype_free(void) tmp2 = tmp->next; free((genericptr_t) tmp->pattern); regex_free(tmp->regex); + tmp->regex = 0; free((genericptr_t) tmp); } gp.plinemsg_types = (struct plinemsg_type *) 0; @@ -7732,7 +7925,7 @@ parse_role_opt( char **opp) { static char neg_opt[] = "!"; /* not 'const' but never modified */ - char *preval, *op = *opp; + char *preval, *op; int which = (optidx == opt_role) ? RS_ROLE : (optidx == opt_race) ? RS_RACE : (optidx == opt_gender) ? RS_GENDER @@ -7819,7 +8012,7 @@ parse_role_opt( if it's ok, replace it with canonical form */ saveoptstr(optidx, op); *opp = op; - ok = TRUE; + /*ok = TRUE; // redundant*/ /* don't return yet; value might be a list that follows this with something else which might make it invalid */ } @@ -8164,6 +8357,28 @@ optfn_o_bind_keys( return optn_ok; } +staticfn int +optfn_o_autocomplete( + int optidx UNUSED, int req, boolean negated UNUSED, + char *opts, char *op UNUSED) +{ + if (req == do_init) { + return optn_ok; + } + if (req == do_set) { + } + if (req == get_val || req == get_cnf_val) { + if (!opts) + return optn_err; + Sprintf(opts, n_currently_set, count_autocompletions()); + return optn_ok; + } + if (req == do_handler) { + handler_change_autocompletions(); + } + return optn_ok; +} + staticfn int optfn_o_menu_colors(int optidx UNUSED, int req, boolean negated UNUSED, char *opts, char *op UNUSED) @@ -9037,7 +9252,6 @@ handle_add_list_remove(const char *optname, int numtotal) }; int clr = NO_COLOR; - opt_idx = 0; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); any = cg.zeroany; @@ -9261,7 +9475,7 @@ option_help(void) datawin = create_nhwindow(NHW_TEXT); Snprintf(buf, sizeof buf, - "Set options as OPTIONS= in %s", configfile); + "Set options as OPTIONS= in %s", get_configfile()); opt_intro[CONFIG_SLOT] = (const char *) buf; for (i = 0; opt_intro[i]; i++) putstr(datawin, 0, opt_intro[i]); @@ -9557,7 +9771,7 @@ next_opt(winid datawin, const char *str) if (!*str) { s = eos(buf); if (s > &buf[1] && s[-2] == ',') - Strcpy(s - 2, "."); /* replace last ", " */ + s[-2] = '.', s[-1] = '\0'; /* replace ending ", " with "." */ i = COLNO; /* (greater than COLNO - 2) */ } else { i = Strlen(buf) + Strlen(str) + 2; @@ -9799,6 +10013,16 @@ wc_set_font_name(int opttype, char *fontname) return; } +static char **fgp[] = { &iflags.wcolors[wcolor_menu].fg, + &iflags.wcolors[wcolor_message].fg, + &iflags.wcolors[wcolor_status].fg, + &iflags.wcolors[wcolor_text].fg }; +static char **bgp[] = { &iflags.wcolors[wcolor_menu].bg, + &iflags.wcolors[wcolor_message].bg, + &iflags.wcolors[wcolor_status].bg, + &iflags.wcolors[wcolor_text].bg }; +int options_set_window_colors_flag = 0; + staticfn int wc_set_window_colors(char *op) { @@ -9806,14 +10030,7 @@ wc_set_window_colors(char *op) * menu white/black message green/yellow status white/blue text * white/black */ - static char **fgp[] = { &iflags.wcolors[wcolor_menu].fg, - &iflags.wcolors[wcolor_message].fg, - &iflags.wcolors[wcolor_status].fg, - &iflags.wcolors[wcolor_text].fg }; - static char **bgp[] = { &iflags.wcolors[wcolor_menu].bg, - &iflags.wcolors[wcolor_message].bg, - &iflags.wcolors[wcolor_status].bg, - &iflags.wcolors[wcolor_text].bg }; + int j; int32 clr; char buf[BUFSZ]; @@ -9895,9 +10112,24 @@ wc_set_window_colors(char *op) wn); } } + options_set_window_colors_flag = 1; return 1; } +void +options_free_window_colors(void) +{ + int j; + + for (j = 0; j < WC_COUNT; ++j) { + if (*fgp[j]) + free((genericptr_t) *fgp[j]), *fgp[j] = 0; + if (*bgp[j]) + free((genericptr_t) *bgp[j]), *bgp[j] = 0; + } + options_set_window_colors_flag = 0; +} + /* set up for wizard mode if player or save file has requested it; called from port-specific startup code to handle `nethack -D' or OPTIONS=playmode:debug, or from dorecover()'s restgamestate() if diff --git a/src/pager.c b/src/pager.c index 74ceeccbe..061937687 100644 --- a/src/pager.c +++ b/src/pager.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 pager.c $NHDT-Date: 1724094301 2024/08/19 19:05:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.279 $ */ +/* NetHack 3.7 pager.c $NHDT-Date: 1737013431 2025/01/15 23:43:51 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.287 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -50,7 +50,8 @@ staticfn void domenucontrols(void); extern void port_help(void); #endif staticfn char *setopt_cmd(char *) NONNULL NONNULLARG1; -staticfn boolean add_quoted_engraving(coordxy, coordxy, char *) NONNULLARG3; +staticfn boolean add_quoted_engraving(coordxy, coordxy, char *, boolean) + NONNULLARG3; enum checkfileflags { chkfilNone = 0, @@ -219,13 +220,13 @@ mhidden_description( what = (otmp && otmp->otyp != STRANGE_OBJECT) ? simpleonames(otmp) : obj_descr[STRANGE_OBJECT].oc_name; - if (incl_article) + if (incl_article && (!otmp || otmp->quan == 1L)) what = an(what); Strcat(outbuf, what); if (fakeobj && otmp) { otmp->where = OBJ_FREE; /* object_from_map set to OBJ_FLOOR */ - dealloc_obj(otmp); + dealloc_obj(otmp); /* has no contents */ } } else { Strcat(outbuf, something); @@ -280,12 +281,19 @@ mhidden_description( /* extracted from lookat(); also used by namefloorobj() */ boolean -object_from_map(int glyph, coordxy x, coordxy y, struct obj **obj_p) +object_from_map( + int glyph, + coordxy x, coordxy y, + struct obj **obj_p) { boolean fakeobj = FALSE, mimic_obj = FALSE; struct monst *mtmp; struct obj *otmp; - int glyphotyp = glyph_to_obj(glyph); + int glyphotyp = glyph_is_object(glyph) ? glyph_to_obj(glyph) + /* if not an object, probably a detected chest trap */ + : glyph_is_cmap(glyph) /* assume trapped chest|door */ + ? (sobj_at(CHEST, x, y) ? CHEST : LARGE_BOX) + : STRANGE_OBJECT; *obj_p = (struct obj *) 0; /* TODO: check inside containers in case glyph came from detection */ @@ -304,9 +312,24 @@ object_from_map(int glyph, coordxy x, coordxy y, struct obj **obj_p) if (!otmp || otmp->otyp != glyphotyp) { /* this used to exclude STRANGE_OBJECT; now caller deals with it */ - otmp = mksobj(glyphotyp, FALSE, FALSE); - if (!otmp) - return FALSE; + if (OBJ_NAME(objects[glyphotyp])) { + /* map shows a regular object, but one that's not actually here */ + otmp = mksobj(glyphotyp, FALSE, FALSE); + } else { + /* map shows a non-item that holds an extra object type (shown + on map due to hallucination) for a name which might have been + shuffled into play but wasn't (or was shuffled out of play); + pick another item that is a regular one in same object class */ + otmp = mkobj(objects[glyphotyp].oc_class, FALSE); + /* mkobj() doesn't provide any no-init option; however, there + aren't any extra tool items (or statues) so we won't get here + for tools and don't need to check for and delete container + contents or extinguish lights on the temporary object */ + } + /* even though we pass False for mksobj()'s 'init' arg, corpse-rot, + egg-hatch, and figurine-transform timers get initialized */ + if (otmp->timed) + obj_stop_timers(otmp); fakeobj = TRUE; if (otmp->oclass == COIN_CLASS) otmp->quan = 2L; /* to force pluralization */ @@ -369,7 +392,7 @@ look_at_object( : obj_descr[STRANGE_OBJECT].oc_name); if (fakeobj) { otmp->where = OBJ_FREE; /* object_from_map set it to OBJ_FLOOR */ - dealloc_obj(otmp), otmp = 0; + dealloc_obj(otmp), otmp = NULL; /* has no contents */ } } else Strcpy(buf, something); /* sanity precaution */ @@ -760,6 +783,7 @@ lookat(coordxy x, coordxy y, char *buf, char *monbuf) Strcpy(buf, "stone"); break; } + FALLTHROUGH; /*FALLTHRU*/ default: Strcpy(buf, defsyms[symidx].explanation); @@ -1482,11 +1506,11 @@ do_screen_description( x_str = def_warnsyms[i].explanation; if (sym == (looked ? gw.warnsyms[i] : def_warnsyms[i].sym)) { if (!found) { - Sprintf(out_str, "%s%s", prefix, def_warnsyms[i].explanation); - *firstmatch = def_warnsyms[i].explanation; + Sprintf(out_str, "%s%s", prefix, x_str); + *firstmatch = x_str;; found++; } else { - found += append_str(out_str, def_warnsyms[i].explanation); + found += append_str(out_str, x_str); } /* Kludge: warning trumps boulders on the display. Reveal the boulder too or player can get confused */ @@ -1575,7 +1599,7 @@ do_screen_description( *firstmatch = look_buf; if (*(*firstmatch)) { Sprintf(temp_buf, " (%s", *firstmatch); - (void) add_quoted_engraving(cc.x, cc.y, temp_buf); + (void) add_quoted_engraving(cc.x, cc.y, temp_buf, FALSE); Strcat(temp_buf, ")"); (void) strncat(out_str, temp_buf, BUFSZ - strlen(out_str) - 1); @@ -1594,7 +1618,10 @@ do_screen_description( /* when farlook is reporting on an engraving, include its text */ staticfn boolean -add_quoted_engraving(coordxy x, coordxy y, char *buf) +add_quoted_engraving( + coordxy x, coordxy y, + char *buf, + boolean force) /* True: '/e' or '/E', False: '//' or ';' */ { char temp_buf[BUFSZ]; struct engr *ep = engr_at(x, y); @@ -1611,7 +1638,10 @@ add_quoted_engraving(coordxy x, coordxy y, char *buf) * or object) that happens to be on top of an engraving, so we won't * append the engraving text. */ - if (!ep || (!floorengr && !headstone)) + if (!ep) + return FALSE; + + if (!floorengr && !headstone && !force) return FALSE; if (ep->eread) @@ -1661,7 +1691,6 @@ do_look(int mode, coord *click_cc) if (!clicklook) { if (quick) { - from_screen = TRUE; /* yes, we want to use the cursor */ i = 'y'; } else { menu_item *pick_list = (menu_item *) 0; @@ -1826,7 +1855,6 @@ do_look(int mode, coord *click_cc) do { /* Reset some variables. */ pm = (struct permonst *) 0; - found = 0; out_str[0] = '\0'; if (from_screen || clicklook) { @@ -2076,16 +2104,19 @@ look_engrs(boolean nearby) winid win; struct engr *e; char lookbuf[BUFSZ], outbuf[BUFSZ]; - nhsym sym; coordxy x, y, lo_x, lo_y, hi_x, hi_y; boolean is_headstone; + nhsym sym; int glyph, count = 0; win = create_nhwindow(NHW_TEXT); look_region_nearby(&lo_x, &lo_y, &hi_x, &hi_y, nearby); + /*assert(lo_x >= 1 && lo_y >= 0 && hi_x < MAXCO && hi_y < MAXLI);*/ for (y = lo_y; y <= hi_y; y++) { for (x = lo_x; x <= hi_x; x++) { lookbuf[0] = '\0'; + if (!levl[x][y].seenv) + continue; /* this won't find remembered engravings which aren't there anymore (in case the hero is unaware that they're gone; scuffed away by monster movement or deleted during shop @@ -2093,14 +2124,9 @@ look_engrs(boolean nearby) e = engr_at(x, y); if (!e) continue; - glyph = glyph_at(x, y); - sym = ((levl[x][y].typ == GRAVE || svl.lastseentyp[x][y] == GRAVE) - ? S_grave - : (levl[x][y].typ == CORR) ? S_engrcorr - : S_engroom); - is_headstone = (sym == S_grave); - Sprintf(lookbuf, "(%s", is_headstone ? "grave" : "engraving"); - (void) add_quoted_engraving(x, y, lookbuf); + is_headstone = IS_GRAVE(svl.lastseentyp[x][y]); + Sprintf(lookbuf, " (%s", is_headstone ? "grave" : "engraving"); + (void) add_quoted_engraving(x, y, lookbuf, TRUE); /* the paren is used by farlook and add_quoted_engraving() expected to see it; we don't want it here */ if (is_headstone) { @@ -2108,21 +2134,21 @@ look_engrs(boolean nearby) (void) strsubst(lookbuf, "(grave whose ", ""); } else { (void) strsubst(lookbuf, "(engraving with ", ""); - (void) strsubst(lookbuf, "(engraving that ", "one that "); + (void) strsubst(lookbuf, "(engraving ", "engraving "); } - if (glyph_is_cmap(glyph) && !glyph_is_trap(glyph)) { + glyph = glyph_at(x, y); + sym = glyph_is_cmap(glyph) ? glyph_to_cmap(glyph) : SYM_NOTHING; + if (is_cmap_engraving(sym) || sym == S_grave) { /* engraving or grave+headstone shown on the map */ ++count; - } else if (e->eread || is_headstone) { + } else { /* engraving or grave covered by object(s) */ Snprintf(eos(lookbuf), sizeof lookbuf - strlen(lookbuf), ", obscured by %s", encglyph(glyph)); glyph = is_headstone ? cmap_to_glyph(S_grave) : engraving_to_glyph(e); ++count; - } else { - continue; } if (*lookbuf) { /* (redundant) */ char coordbuf[20], cmode; @@ -2144,7 +2170,7 @@ look_engrs(boolean nearby) : (cmode == GPCOORDS_MAP) ? "%8s " : "%12s ", coord_desc(x, y, coordbuf, cmode)); - Sprintf(eos(outbuf), "%s ", encglyph(glyph)); + Sprintf(eos(outbuf), "%s ", encglyph(glyph)); /* guard against potential overflow */ lookbuf[sizeof lookbuf - 1 - strlen(outbuf)] = '\0'; Strcat(outbuf, lookbuf); diff --git a/src/pickup.c b/src/pickup.c index 75e685b97..b74119402 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -261,12 +261,30 @@ query_classes( return TRUE; } +/* + * tests: + * st_gloves wearing gloves? + * st_corpse is it a corpse obj? + * st_petrifies does the corpse petrify on touch? + * st_resists does hero have stoning resistance? + * st_all st_gloves | st_corpse | st_petrifies | st_resists + */ +boolean +u_safe_from_fatal_corpse(struct obj *obj, int tests) +{ + if (((tests & st_gloves) && uarmg) + || ((tests & st_corpse) && obj->otyp != CORPSE) + || ((tests & st_petrifies) && !touch_petrifies(&mons[obj->corpsenm])) + || ((tests & st_resists) && Stone_resistance)) + return TRUE; + return FALSE; +} + /* check whether hero is bare-handedly touching a cockatrice corpse */ staticfn boolean fatal_corpse_mistake(struct obj *obj, boolean remotely) { - if (uarmg || remotely || obj->otyp != CORPSE - || !touch_petrifies(&mons[obj->corpsenm]) || Stone_resistance) + if (u_safe_from_fatal_corpse(obj, st_all) || remotely) return FALSE; if (poly_when_stoned(gy.youmonst.data) && polymon(PM_STONE_GOLEM)) { @@ -852,6 +870,7 @@ pickup(int what) /* should be a long */ lcount = (long) yn_number; if (lcount > obj->quan) lcount = obj->quan; + FALLTHROUGH; /*FALLTHRU*/ default: /* 'y' */ break; @@ -931,6 +950,8 @@ autopick_testobj(struct obj *otmp, boolean calc_costly) return TRUE; if (flags.nopick_dropped && otmp->how_lost == LOST_DROPPED) return FALSE; + if (otmp->how_lost == LOST_EXPLODING) + return FALSE; /* check for pickup_types */ pickit = (!*otypes || strchr(otypes, otmp->oclass)); @@ -1460,6 +1481,7 @@ query_category( /* assert( n == 1 ); */ break; /* from switch */ } + FALLTHROUGH; /*FALLTHRU*/ case 'q': default: @@ -1846,7 +1868,7 @@ pickup_object( 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 = LOST_NONE; /* affects merge_choice() */ + obj->how_lost &= ~LOSTOVERRIDEMASK; /* affects merge_choice() */ 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 */ @@ -1859,7 +1881,7 @@ pickup_object( if (obj->quan != count && obj->otyp != LOADSTONE) obj = splitobj(obj, count); - obj->how_lost = LOST_NONE; + obj->how_lost &= ~LOSTOVERRIDEMASK; obj = pick_obj(obj); if (uwep && uwep == obj) @@ -2029,7 +2051,7 @@ able_to_loot( if (u.usteed && P_SKILL(P_RIDING) < P_BASIC) rider_cant_reach(); /* not skilled enough to reach */ else - cant_reach_floor(x, y, FALSE, TRUE); + cant_reach_floor(x, y, FALSE, TRUE, FALSE); return FALSE; } else if ((is_pool(x, y) && (looting || !Underwater)) || is_lava(x, y)) { /* at present, can't loot in water even when Underwater; diff --git a/src/pline.c b/src/pline.c index 1e23846ce..48f8fc89f 100644 --- a/src/pline.c +++ b/src/pline.c @@ -11,9 +11,7 @@ staticfn void putmesg(const char *); staticfn char *You_buf(int); -#if defined(MSGHANDLER) staticfn void execplinehandler(const char *); -#endif #ifdef USER_SOUNDS extern void maybe_play_sound(const char *); #endif @@ -78,6 +76,23 @@ putmesg(const char *line) SoundSpeak(line); } +/* set the direction where next message happens */ +void +set_msg_dir(int dir) +{ + dtoxy(&a11y.msg_loc, dir); + a11y.msg_loc.x += u.ux; + a11y.msg_loc.y += u.uy; +} + +/* set the coordinate where next message happens */ +void +set_msg_xy(coordxy x, coordxy y) +{ + a11y.msg_loc.x = x; + a11y.msg_loc.y = y; +} + staticfn void vpline(const char *, va_list); DISABLE_WARNING_FORMAT_NONLITERAL @@ -121,30 +136,16 @@ pline_mon(struct monst *mtmp, const char *line, ...) { va_list the_args; - set_msg_xy(mtmp->mx, mtmp->my); + if (mtmp == &gy.youmonst) + set_msg_xy(0, 0); + else + set_msg_xy(mtmp->mx, mtmp->my); va_start(the_args, line); vpline(line, the_args); va_end(the_args); } -/* set the direction where next message happens */ -void -set_msg_dir(int dir) -{ - dtoxy(&a11y.msg_loc, dir); - a11y.msg_loc.x += u.ux; - a11y.msg_loc.y += u.uy; -} - -/* set the coordinate where next message happens */ -void -set_msg_xy(coordxy x, coordxy y) -{ - a11y.msg_loc.x = x; - a11y.msg_loc.y = y; -} - staticfn void vpline(const char *line, va_list the_args) { @@ -153,6 +154,11 @@ vpline(const char *line, va_list the_args) int ln; int msgtyp; boolean no_repeat; + coord a11y_mesgxy; + + a11y_mesgxy = a11y.msg_loc; /* save a11y.msg_loc before reseting it */ + /* always reset a11y.msg_loc whether we end up using it or not */ + a11y.msg_loc.x = a11y.msg_loc.y = 0; if (!line || !*line) return; @@ -163,16 +169,15 @@ vpline(const char *line, va_list the_args) if (program_state.wizkit_wishing) return; - if (a11y.accessiblemsg && isok(a11y.msg_loc.x,a11y.msg_loc.y)) { - char *tmp; - char *dirstr; - static char dirstrbuf[BUFSZ]; - int g = (iflags.getpos_coords == GPCOORDS_NONE) - ? GPCOORDS_COMFULL : iflags.getpos_coords; + /* when accessiblemsg is set and a11y.msg_loc is nonzero, use the latter + to insert a location prefix in front of current message */ + if (a11y.accessiblemsg && isok(a11y_mesgxy.x, a11y_mesgxy.y)) { + char *tmp, *dirstr, dirstrbuf[QBUFSZ]; - dirstr = coord_desc(a11y.msg_loc.x, a11y.msg_loc.y, dirstrbuf, g); - a11y.msg_loc.x = a11y.msg_loc.y = 0; - tmp = (char *)alloc(strlen(line) + sizeof ": " + strlen(dirstr)); + dirstr = coord_desc(a11y_mesgxy.x, a11y_mesgxy.y, dirstrbuf, + ((iflags.getpos_coords == GPCOORDS_NONE) + ? GPCOORDS_COMFULL : iflags.getpos_coords)); + tmp = (char *) alloc(strlen(line) + sizeof ": " + strlen(dirstr)); Strcpy(tmp, dirstr); Strcat(tmp, ": "); Strcat(tmp, line); @@ -255,16 +260,19 @@ vpline(const char *line, va_list the_args) goto pline_done; } - if (gv.vision_full_recalc) + if (gv.vision_full_recalc) { + int tmp_in_pline = in_pline; + + in_pline = 0; vision_recalc(0); + in_pline = tmp_in_pline; + } if (u.ux) flush_screen((gp.pline_flags & NO_CURS_ON_U) ? 0 : 1); /* %% */ putmesg(line); -#if defined(MSGHANDLER) execplinehandler(line); -#endif /* this gets cleared after every pline message */ iflags.last_msg = PLNMSG_UNKNOWN; @@ -564,9 +572,7 @@ vraw_printf(const char *line, va_list the_args) pbuf[BUFSZ - 1] = '\0'; /* terminate strncpy or truncate vsprintf */ } raw_print(line); -#if defined(MSGHANDLER) execplinehandler(line); -#endif if (!program_state.beyond_savefile_load) ge.early_raw_messages++; } @@ -587,7 +593,7 @@ impossible(const char *s, ...) va_end(the_args); pbuf[BUFSZ - 1] = '\0'; /* sanity */ paniclog("impossible", pbuf); - if (iflags.debug_fuzzer == 1) + if (iflags.debug_fuzzer == fuzzer_impossible_panic) panic("%s", pbuf); gp.pline_flags = URGENT_MESSAGE; @@ -626,37 +632,30 @@ impossible(const char *s, ...) RESTORE_WARNING_FORMAT_NONLITERAL -#if defined(MSGHANDLER) static boolean use_pline_handler = TRUE; staticfn void execplinehandler(const char *line) { -#if defined(POSIX_TYPES) || defined(__GNUC__) +#if defined(UNIX) && (defined(POSIX_TYPES) || defined(__GNUC__)) int f; #endif const char *args[3]; - char *env; - if (!use_pline_handler) + if (!use_pline_handler || !sysopt.msghandler) return; - if (!(env = nh_getenv("NETHACK_MSGHANDLER"))) { - use_pline_handler = FALSE; - return; - } - -#if defined(POSIX_TYPES) || defined(__GNUC__) +#if defined(UNIX) && (defined(POSIX_TYPES) || defined(__GNUC__)) f = fork(); if (f == 0) { /* child */ - args[0] = env; + args[0] = sysopt.msghandler; args[1] = line; args[2] = NULL; (void) setgid(getgid()); (void) setuid(getuid()); (void) execv(args[0], (char *const *) args); perror((char *) 0); - (void) fprintf(stderr, "Exec to message handler %s failed.\n", env); + (void) fprintf(stderr, "Exec to message handler %s failed.\n", sysopt.msghandler); nh_terminate(EXIT_FAILURE); } else if (f > 0) { int status; @@ -670,53 +669,18 @@ execplinehandler(const char *line) #elif defined(WIN32) { intptr_t ret; - args[0] = env; + args[0] = sysopt.msghandler; args[1] = line; args[2] = NULL; - ret = _spawnv(_P_NOWAIT, env, args); + ret = _spawnv(_P_NOWAIT, sysopt.msghandler, args); + nhUse(ret); /* -Wunused-but-set-variable */ } #else -#error MSGHANDLER is not implemented on this system. + use_pline_handler = FALSE; + nhUse(args); + nhUse(line); #endif } -#endif /* MSGHANDLER */ - -/* - * varargs handling for files.c - */ -staticfn void vconfig_error_add(const char *, va_list); - -DISABLE_WARNING_FORMAT_NONLITERAL - -void -config_error_add(const char *str, ...) -{ - va_list the_args; - - va_start(the_args, str); - vconfig_error_add(str, the_args); - va_end(the_args); -} - -staticfn void -vconfig_error_add(const char *str, va_list the_args) -{ /* start of vconf...() or of nested block in USE_OLDARG's conf...() */ - int vlen = 0; - char buf[BIGBUFSZ]; /* will be chopped down to BUFSZ-1 if longer */ - - vlen = vsnprintf(buf, sizeof buf, str, the_args); -#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) && defined(DEBUG) - if (vlen >= (int) sizeof buf) - panic("%s: truncation of buffer at %zu of %d bytes", - "config_error_add", sizeof buf, vlen); -#else - nhUse(vlen); -#endif - buf[BUFSZ - 1] = '\0'; - config_erradd(buf); -} - -RESTORE_WARNING_FORMAT_NONLITERAL /* nhassert_failed is called when an nhassert's condition is false */ void diff --git a/src/polyself.c b/src/polyself.c index 736c62dff..4ab183113 100644 --- a/src/polyself.c +++ b/src/polyself.c @@ -1,13 +1,13 @@ -/* NetHack 3.7 polyself.c $NHDT-Date: 1703845752 2023/12/29 10:29:12 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.207 $ */ +/* NetHack 3.7 polyself.c $NHDT-Date: 1740534595 2025/02/25 17:49:55 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.223 $ */ /* Copyright (C) 1987, 1988, 1989 by Ken Arromdee */ /* NetHack may be freely redistributed. See license for details. */ /* * Polymorph self routine. * - * Note: the light source handling code assumes that both gy.youmonst.m_id - * and gy.youmonst.mx will always remain 0 when it handles the case of the - * player polymorphed into a light-emitting monster. + * Note: the light source handling code assumes that gy.youmonst.m_id + * always remains 1 and gy.youmonst.mx will always remain 0 when it handles + * the case of the player polymorphed into a light-emitting monster. * * Transformation sequences: * /-> polymon poly into monster form @@ -41,6 +41,7 @@ set_uasmon(void) boolean was_vampshifter = valid_vampshiftform(gy.youmonst.cham, u.umonnum); set_mon_data(&gy.youmonst, mdat); + gy.youmonst.m_id = 1; if (Protection_from_shape_changers) gy.youmonst.cham = NON_PM; @@ -58,15 +59,16 @@ set_uasmon(void) else \ u.uprops[PropIndx].intrinsic &= ~FROMFORM; \ } while (0) +#define resist_from_form(MRtyp) ((gy.youmonst.data->mresists & (MRtyp)) != 0) - PROPSET(FIRE_RES, resists_fire(&gy.youmonst)); - PROPSET(COLD_RES, resists_cold(&gy.youmonst)); - PROPSET(SLEEP_RES, resists_sleep(&gy.youmonst)); - PROPSET(DISINT_RES, resists_disint(&gy.youmonst)); - PROPSET(SHOCK_RES, resists_elec(&gy.youmonst)); - PROPSET(POISON_RES, resists_poison(&gy.youmonst)); - PROPSET(ACID_RES, resists_acid(&gy.youmonst)); - PROPSET(STONE_RES, resists_ston(&gy.youmonst)); + PROPSET(FIRE_RES, resist_from_form(MR_FIRE)); + PROPSET(COLD_RES, resist_from_form( MR_COLD)); + PROPSET(SLEEP_RES, resist_from_form(MR_SLEEP)); + PROPSET(DISINT_RES, resist_from_form(MR_DISINT)); + PROPSET(SHOCK_RES, resist_from_form(MR_ELEC)); + PROPSET(POISON_RES, resist_from_form(MR_POISON)); + PROPSET(ACID_RES, resist_from_form(MR_ACID)); + PROPSET(STONE_RES, resist_from_form(MR_STONE)); { /* resists_drli() takes wielded weapon into account; suppress it */ struct obj *save_uwep = uwep; @@ -103,7 +105,10 @@ set_uasmon(void) PROPSET(REGENERATION, regenerates(mdat)); PROPSET(REFLECTING, (mdat == &mons[PM_SILVER_DRAGON])); PROPSET(BLINDED, !haseyes(mdat)); + PROPSET(BLND_RES, (dmgtype_fromattack(mdat, AD_BLND, AT_EXPL) + || dmgtype_fromattack(mdat, AD_BLND, AT_GAZE))); #undef PROPSET +#undef resist_from_form /* whether the player is flying/floating depends on their steed, which won't be known during the restore process: but BFlying @@ -117,6 +122,8 @@ set_uasmon(void) if (VIA_WINDOWPORT()) status_initialize(REASSESS_ONLY); #endif + /* we can reset this now, having just done what it is meant to trigger */ + gw.were_changes = 0L; } /* Levitation overrides Flying; set or clear BFlying|I_SPECIAL */ @@ -1128,6 +1135,15 @@ dropp(struct obj *obj) for (otmp = gi.invent; otmp; otmp = otmp->nobj) { if (otmp == obj) { dropx(obj); + /* Note that otmp->nobj is pointing at fobj now, + * as a result of: + * dropx() -> dropy() -> dropz() -> place_object(), + * and no longer pointing at the next obj in inventory. + * That would be an issue if this loop were allowed + * to continue, but the break statement that + * follows prevents the loop from continuing on with + * objects on the floor. + */ break; } } @@ -1439,6 +1455,7 @@ dospit(void) break; default: impossible("bad attack type in dospit"); + FALLTHROUGH; /*FALLTHRU*/ case AD_ACID: otmp = mksobj(ACID_VENOM, TRUE, FALSE); diff --git a/src/potion.c b/src/potion.c index 874c5940c..a5cf0a1f4 100644 --- a/src/potion.c +++ b/src/potion.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 potion.c $NHDT-Date: 1726356849 2024/09/14 23:34:09 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.270 $ */ +/* NetHack 3.7 potion.c $NHDT-Date: 1737605675 2025/01/22 20:14:35 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.274 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -41,6 +41,7 @@ staticfn int dip_hands_ok(struct obj *); staticfn void hold_potion(struct obj *, const char *, const char *, const char *); staticfn void poof(struct obj *); +staticfn boolean dip_potion_explosion(struct obj *, int); staticfn int potion_dip(struct obj *obj, struct obj *potion); /* used to indicate whether quaff or dip has skipped an opportunity to @@ -1720,23 +1721,26 @@ potionhit(struct monst *mon, struct obj *obj, int how) switch (obj->otyp) { case POT_FULL_HEALING: cureblind = TRUE; + FALLTHROUGH; /*FALLTHRU*/ case POT_EXTRA_HEALING: if (!obj->cursed) cureblind = TRUE; + FALLTHROUGH; /*FALLTHRU*/ case POT_HEALING: if (obj->blessed) cureblind = TRUE; if (mon->data == &mons[PM_PESTILENCE]) goto do_illness; + FALLTHROUGH; /*FALLTHRU*/ case POT_RESTORE_ABILITY: case POT_GAIN_ABILITY: do_healing: angermon = FALSE; if (mon->mhp < mon->mhpmax) { - mon->mhp = mon->mhpmax; + healmon(mon, mon->mhpmax, 0); if (canseemon(mon)) pline("%s looks sound and hale again.", Monnam(mon)); } @@ -1823,9 +1827,7 @@ potionhit(struct monst *mon, struct obj *obj, int how) angermon = FALSE; if (canseemon(mon)) pline("%s looks healthier.", Monnam(mon)); - mon->mhp += d(2, 6); - if (mon->mhp > mon->mhpmax) - mon->mhp = mon->mhpmax; + healmon(mon, d(2, 6), 0); if (is_were(mon->data) && is_human(mon->data) && !Protection_from_shape_changers) new_were(mon); /* transform into beast */ @@ -1959,6 +1961,7 @@ potionbreathe(struct obj *obj) if (u.uhp < u.uhpmax) u.uhp++, disp.botl = TRUE; cureblind = TRUE; + FALLTHROUGH; /*FALLTHRU*/ case POT_EXTRA_HEALING: if (Upolyd && u.mh < u.mhmax) @@ -1967,6 +1970,7 @@ potionbreathe(struct obj *obj) u.uhp++, disp.botl = TRUE; if (!obj->cursed) cureblind = TRUE; + FALLTHROUGH; /*FALLTHRU*/ case POT_HEALING: if (Upolyd && u.mh < u.mhmax) @@ -2115,6 +2119,7 @@ mixtype(struct obj *o1, struct obj *o2) case POT_HEALING: if (o2typ == POT_SPEED) return POT_EXTRA_HEALING; + FALLTHROUGH; /*FALLTHRU*/ case POT_EXTRA_HEALING: case POT_FULL_HEALING: @@ -2122,6 +2127,7 @@ mixtype(struct obj *o1, struct obj *o2) return (o1typ == POT_HEALING) ? POT_EXTRA_HEALING : (o1typ == POT_EXTRA_HEALING) ? POT_FULL_HEALING : POT_GAIN_ABILITY; + FALLTHROUGH; /*FALLTHRU*/ case UNICORN_HORN: switch (o2typ) { @@ -2230,6 +2236,7 @@ hold_potion( obj_extract_self(potobj); /* re-insert into inventory, possibly merging with compatible stack */ potobj = hold_another_object(potobj, drop_fmt, drop_arg, hold_msg); + nhUse(potobj); flags.pickup_burden = save_pickup_burden; update_inventory(); return; @@ -2386,6 +2393,31 @@ poof(struct obj *potion) useup(potion); } +/* do dipped potion(s) explode? */ +staticfn boolean +dip_potion_explosion(struct obj *obj, int dmg) +{ + if (obj->cursed || obj->otyp == POT_ACID + || (obj->otyp == POT_OIL && obj->lamplit) + || !rn2((uarmc && uarmc->otyp == ALCHEMY_SMOCK) ? 30 : 10)) { + /* it would be better to use up the whole stack in advance + of the message, but we can't because we need to keep it + around for potionbreathe() [and we can't set obj->in_use + to 'amt' because that's not implemented] */ + obj->in_use = 1; + pline("%sThey explode!", !Deaf ? "BOOM! " : ""); + wake_nearto(u.ux, u.uy, (BOLT_LIM + 1) * (BOLT_LIM + 1)); + exercise(A_STR, FALSE); + if (!breathless(gy.youmonst.data) || haseyes(gy.youmonst.data)) + potionbreathe(obj); + useupall(obj); + losehp(dmg, /* not physical damage */ + "alchemic blast", KILLED_BY_AN); + return TRUE; + } + return FALSE; +} + /* called by dodip() or dip_into() after obj and potion have been chosen */ staticfn int potion_dip(struct obj *obj, struct obj *potion) @@ -2487,22 +2519,8 @@ potion_dip(struct obj *obj, struct obj *potion) useup(potion); /* now gone */ /* Mixing potions is dangerous... KMH, balance patch -- acid is particularly unstable */ - if (obj->cursed || obj->otyp == POT_ACID || !rn2(10)) { - /* it would be better to use up the whole stack in advance - of the message, but we can't because we need to keep it - around for potionbreathe() [and we can't set obj->in_use - to 'amt' because that's not implemented] */ - obj->in_use = 1; - pline("%sThey explode!", !Deaf ? "BOOM! " : ""); - wake_nearto(u.ux, u.uy, (BOLT_LIM + 1) * (BOLT_LIM + 1)); - exercise(A_STR, FALSE); - if (!breathless(gy.youmonst.data) || haseyes(gy.youmonst.data)) - potionbreathe(obj); - useupall(obj); - losehp(amt + rnd(9), /* not physical damage */ - "alchemic blast", KILLED_BY_AN); + if (dip_potion_explosion(obj, amt + rnd(9))) return ECMD_TIME; - } obj->blessed = obj->cursed = obj->bknown = 0; if (Blind || Hallucination) @@ -2587,9 +2605,10 @@ potion_dip(struct obj *obj, struct obj *potion) obj->opoisoned = TRUE; poof(potion); return ECMD_TIME; - } else if (obj->opoisoned && (potion->otyp == POT_HEALING - || potion->otyp == POT_EXTRA_HEALING - || potion->otyp == POT_FULL_HEALING)) { + } else if (obj->opoisoned && !permapoisoned(obj) + && (potion->otyp == POT_HEALING + || potion->otyp == POT_EXTRA_HEALING + || potion->otyp == POT_FULL_HEALING)) { pline("A coating wears off %s.", the(xname(obj))); obj->opoisoned = 0; poof(potion); diff --git a/src/pray.c b/src/pray.c index 774ac7847..d2b505b05 100644 --- a/src/pray.c +++ b/src/pray.c @@ -21,10 +21,14 @@ staticfn void gods_upset(aligntyp); staticfn void consume_offering(struct obj *); staticfn void offer_too_soon(aligntyp); staticfn void offer_real_amulet(struct obj *, aligntyp); /* NORETURN */ +staticfn void offer_negative_valued(boolean, aligntyp); staticfn void offer_fake_amulet(struct obj *, boolean, aligntyp); staticfn void offer_different_alignment_altar(struct obj *, aligntyp); staticfn void sacrifice_your_race(struct obj *, boolean, aligntyp); -staticfn int bestow_artifact(void); +staticfn int bestow_artifact(uchar); +staticfn int sacrifice_value(struct obj *); +staticfn int eval_offering(struct obj *, aligntyp); +staticfn void offer_corpse(struct obj *, boolean, aligntyp); staticfn boolean pray_revive(void); staticfn boolean water_prayer(boolean); staticfn boolean blocked_boulder(int, int); @@ -165,7 +169,7 @@ stuck_in_wall(void) continue; y = u.uy + j; if (!isok(x, y) - || (IS_ROCK(levl[x][y].typ) + || (IS_OBSTRUCTED(levl[x][y].typ) && (levl[x][y].typ != SDOOR && levl[x][y].typ != SCORR)) || (blocked_boulder(i, j) && !throws_rocks(gy.youmonst.data))) ++count; @@ -397,7 +401,8 @@ fix_worst_trouble(int trouble) break; case TROUBLE_STARVING: /* temporarily lost strength recovery now handled by init_uhunger() */ - /*FALLTHRU*/ + FALLTHROUGH; + /* FALLTHRU*/ case TROUBLE_HUNGRY: Your("%s feels content.", body_part(STOMACH)); init_uhunger(); @@ -739,13 +744,16 @@ angrygods(aligntyp resp_god) gods_angry(resp_god); punish((struct obj *) 0); break; - } /* else fall thru */ + } + FALLTHROUGH; + /* FALLTHRU */ case 4: case 5: gods_angry(resp_god); if (!Blind && !Antimagic) pline("%s glow surrounds you.", An(hcolor(NH_BLACK))); - rndcurse(); + if (rn2(2) || !attrcurse()) + rndcurse(); break; case 7: case 8: @@ -1121,6 +1129,7 @@ pleased(aligntyp g_align) switch (min(action, 5)) { case 5: pat_on_head = 1; + FALLTHROUGH; /*FALLTHRU*/ case 4: do @@ -1129,9 +1138,12 @@ pleased(aligntyp g_align) break; case 3: + /* up to 10 troubles */ fix_worst_trouble(trouble); + FALLTHROUGH; + /*FALLTHRU*/ case 2: - /* arbitrary number of tries */ + /* up to 9 troubles */ while ((trouble = in_trouble()) > 0 && (++tryct < 10)) fix_worst_trouble(trouble); break; @@ -1139,6 +1151,7 @@ pleased(aligntyp g_align) case 1: if (trouble > 0) fix_worst_trouble(trouble); + break; case 0: break; /* your god blows you off, too bad */ } @@ -1225,6 +1238,7 @@ pleased(aligntyp g_align) break; } } + FALLTHROUGH; /*FALLTHRU*/ case 2: if (!Blind) @@ -1264,14 +1278,15 @@ pleased(aligntyp g_align) disp.botl = TRUE; break; case 4: { - struct obj *otmp; + struct obj *otmp, *nextobj; int any = 0; if (Blind) You_feel("the power of %s.", u_gname()); else You("are surrounded by %s aura.", an(hcolor(NH_LIGHT_BLUE))); - for (otmp = gi.invent; otmp; otmp = otmp->nobj) { + for (otmp = gi.invent; otmp; otmp = nextobj) { + nextobj = otmp->nobj; if (otmp->cursed && (otmp != uarmh /* [see worst_cursed_item()] */ || uarmh->otyp != HELM_OF_OPPOSITE_ALIGNMENT)) { @@ -1325,6 +1340,7 @@ pleased(aligntyp g_align) gcrownu(); break; } + FALLTHROUGH; /*FALLTHRU*/ case 6: give_spell(); @@ -1569,6 +1585,16 @@ offer_real_amulet(struct obj *otmp, aligntyp altaralign) /*NOTREACHED*/ } +staticfn void +offer_negative_valued(boolean highaltar, aligntyp altaralign) +{ + if (altaralign != u.ualign.type && highaltar) { + desecrate_altar(highaltar, altaralign); + } else { + gods_upset(altaralign); + } +} + staticfn void offer_fake_amulet( struct obj *otmp, @@ -1593,12 +1619,7 @@ offer_fake_amulet( change_luck(-3); adjalign(-1); u.ugangr += 3; - /* value = -3; */ - if (altaralign != u.ualign.type && highaltar) { - desecrate_altar(highaltar, altaralign); - } else { /* value < 0 */ - gods_upset(altaralign); - } + offer_negative_valued(highaltar, altaralign); } } @@ -1754,17 +1775,25 @@ sacrifice_your_race( } staticfn int -bestow_artifact(void) +bestow_artifact(uchar max_giftvalue) { int nartifacts = nartifact_exist(); + boolean do_bestow = u.ulevel > 2 && u.uluck >= 0; + if (do_bestow) { + /* you were already in pretty good standing */ + /* The player can gain an artifact */ + /* The chance goes down as the number of artifacts goes up */ + if (wizard) + do_bestow = y_n("Gift an artifact?") == 'y'; + else + do_bestow = !rn2(6 + (2 * u.ugifts * nartifacts)); + } - /* you were already in pretty good standing */ - /* The player can gain an artifact */ - /* The chance goes down as the number of artifacts goes up */ - if (u.ulevel > 2 && u.uluck >= 0 - && !rn2(10 + (2 * u.ugifts * nartifacts))) { + if (do_bestow) { struct obj *otmp; - otmp = mk_artifact((struct obj *) 0, a_align(u.ux, u.uy)); + /* mk_artifact() with NULL obj and a_align() arg can return NULL */ + otmp = mk_artifact((struct obj *) 0, a_align(u.ux, u.uy), + max_giftvalue, TRUE); if (otmp) { char buf[BUFSZ]; @@ -1803,6 +1832,20 @@ bestow_artifact(void) return FALSE; } +staticfn int +sacrifice_value(struct obj *otmp) +{ + int value = 0; + + if (otmp->corpsenm == PM_ACID_BLOB + || (svm.moves <= peek_at_iced_corpse_age(otmp) + 50)) { + value = mons[otmp->corpsenm].difficulty + 1; + if (otmp->oeaten) + value = eaten_stat(value, otmp); + } + return value; +} + /* the #offer command - sacrifice something to the gods */ int dosacrifice(void) @@ -1810,7 +1853,6 @@ dosacrifice(void) struct obj *otmp; boolean highaltar; aligntyp altaralign = a_align(u.ux, u.uy); - int value = 0; if (!on_altar() || u.uswallow) { You("are not %s an altar.", @@ -1841,6 +1883,82 @@ dosacrifice(void) return ECMD_TIME; } /* fake Amulet */ + if (otmp->otyp == CORPSE) { + offer_corpse(otmp, highaltar, altaralign); + return ECMD_TIME; + } + + pline1(nothing_happens); + return ECMD_TIME; +} + +staticfn int +eval_offering(struct obj *otmp, aligntyp altaralign) +{ + struct permonst *ptr; + int value; + + value = sacrifice_value(otmp); + + if (!value) + return 0; + + ptr = &mons[otmp->corpsenm]; + + if (is_undead(ptr)) { /* Not demons--no demon corpses */ + /* most undead that leave a corpse yield 'human' (or other race) + corpse so won't get here; the exception is wraith; give the + bonus for wraith to chaotics too because they are sacrificing + something valuable (unless hero refuses to eat such things) */ + if (u.ualign.type != A_CHAOTIC + /* reaching this side of the 'or' means hero is chaotic */ + || (ptr == &mons[PM_WRAITH] && u.uconduct.unvegetarian)) + value += 1; + } else if (is_unicorn(ptr)) { + int unicalign = sgn(ptr->maligntyp); + + if (unicalign == altaralign) { + /* When same as altar, always a very bad action. + */ + pline("Such an action is an insult to %s!", + (unicalign == A_CHAOTIC) ? "chaos" + : unicalign ? "law" : "balance"); + (void) adjattrib(A_WIS, -1, TRUE); + return -1; + } else if (u.ualign.type == altaralign) { + /* When different from altar, and altar is same as yours, + * it's a very good action. + */ + if (u.ualign.record < ALIGNLIM) + You_feel("appropriately %s.", align_str(u.ualign.type)); + else + You_feel("you are thoroughly on the right path."); + adjalign(5); + value += 3; + } else if (unicalign == u.ualign.type) { + /* When sacrificing unicorn of your alignment to altar not of + * your alignment, your god gets angry and it's a conversion. + */ + u.ualign.record = -1; + value = 1; + } else { + /* Otherwise, unicorn's alignment is different from yours + * and different from the altar's. It's an ordinary (well, + * with a bonus) sacrifice on a cross-aligned altar. + */ + value += 3; + } + } + return value; +} + +staticfn void +offer_corpse(struct obj *otmp, boolean highaltar, aligntyp altaralign) +{ + int value; + struct permonst *ptr; + struct monst *mtmp; + /* * Was based on nutritional value and aging behavior (< 50 moves). * Sacrificing a food ration got you max luck instantly, making the @@ -1852,188 +1970,150 @@ dosacrifice(void) */ #define MAXVALUE 24 /* Highest corpse value (besides Wiz) */ - if (otmp->otyp == CORPSE) { - struct permonst *ptr = &mons[otmp->corpsenm]; - struct monst *mtmp; + /* KMH, conduct */ + if (!u.uconduct.gnostic++) + livelog_printf(LL_CONDUCT, "rejected atheism" + " by offering %s on an altar of %s", + corpse_xname(otmp, (const char *) 0, CXN_ARTICLE), + a_gname()); - /* KMH, conduct */ - if (!u.uconduct.gnostic++) - livelog_printf(LL_CONDUCT, "rejected atheism" - " by offering %s on an altar of %s", - corpse_xname(otmp, (const char *) 0, CXN_ARTICLE), - a_gname()); + /* you're handling this corpse, even if it was killed upon the altar + */ + feel_cockatrice(otmp, TRUE); + if (rider_corpse_revival(otmp, FALSE)) + return; - /* you're handling this corpse, even if it was killed upon the altar - */ - feel_cockatrice(otmp, TRUE); - if (rider_corpse_revival(otmp, FALSE)) - return ECMD_TIME; + ptr = &mons[otmp->corpsenm]; - if (otmp->corpsenm == PM_ACID_BLOB - || (svm.moves <= peek_at_iced_corpse_age(otmp) + 50)) { - value = mons[otmp->corpsenm].difficulty + 1; - if (otmp->oeaten) - value = eaten_stat(value, otmp); - } - - /* same race or former pet results apply even if the corpse is - too old (value==0) */ - if (your_race(ptr)) { - sacrifice_your_race(otmp, highaltar, altaralign); - return ECMD_TIME; - } else if (has_omonst(otmp) - && (mtmp = get_mtraits(otmp, FALSE)) != 0 - && mtmp->mtame) { - /* mtmp is a temporary pointer to a tame monster's attributes, - * not a real monster */ - pline("So this is how you repay loyalty?"); - adjalign(-3); - value = -1; - HAggravate_monster |= FROMOUTSIDE; - } else if (!value) { - ; /* too old; don't give undead or unicorn bonus or penalty */ - } else if (is_undead(ptr)) { /* Not demons--no demon corpses */ - /* most undead that leave a corpse yield 'human' (or other race) - corpse so won't get here; the exception is wraith; give the - bonus for wraith to chaotics too because they are sacrificing - something valuable (unless hero refuses to eat such things) */ - if (u.ualign.type != A_CHAOTIC - /* reaching this side of the 'or' means hero is chaotic */ - || (ptr == &mons[PM_WRAITH] && u.uconduct.unvegetarian)) - value += 1; - } else if (is_unicorn(ptr)) { - int unicalign = sgn(ptr->maligntyp); - - if (unicalign == altaralign) { - /* When same as altar, always a very bad action. - */ - pline("Such an action is an insult to %s!", - (unicalign == A_CHAOTIC) ? "chaos" - : unicalign ? "law" : "balance"); - (void) adjattrib(A_WIS, -1, TRUE); - value = -5; - } else if (u.ualign.type == altaralign) { - /* When different from altar, and altar is same as yours, - * it's a very good action. - */ - if (u.ualign.record < ALIGNLIM) - You_feel("appropriately %s.", align_str(u.ualign.type)); - else - You_feel("you are thoroughly on the right path."); - adjalign(5); - value += 3; - } else if (unicalign == u.ualign.type) { - /* When sacrificing unicorn of your alignment to altar not of - * your alignment, your god gets angry and it's a conversion. - */ - u.ualign.record = -1; - value = 1; - } else { - /* Otherwise, unicorn's alignment is different from yours - * and different from the altar's. It's an ordinary (well, - * with a bonus) sacrifice on a cross-aligned altar. - */ - value += 3; - } - } - } else { /* !corpse */ - ; /* value==0 which is what we want for non-corpse */ - } /* ?corpse */ + /* same race or former pet results apply even if the corpse is + too old (value==0) */ + if (your_race(ptr)) { + sacrifice_your_race(otmp, highaltar, altaralign); + return; + } + if (has_omonst(otmp) + && (mtmp = get_mtraits(otmp, FALSE)) != 0 + && mtmp->mtame) { + /* mtmp is a temporary pointer to a tame monster's attributes, + * not a real monster */ + pline("So this is how you repay loyalty?"); + adjalign(-3); + HAggravate_monster |= FROMOUTSIDE; + offer_negative_valued(highaltar, altaralign); + return; + } + value = eval_offering(otmp, altaralign); if (value == 0) { + /* too old; don't give undead or unicorn bonus or penalty */ pline1(nothing_happens); - return ECMD_TIME; + return; + } + if (value < 0) { + offer_negative_valued(highaltar, altaralign); + return; } if (altaralign != u.ualign.type && highaltar) { desecrate_altar(highaltar, altaralign); - } else if (value < 0) { /* don't think the gods are gonna like this... */ - gods_upset(altaralign); - } else if (u.ualign.type != altaralign) { + return; + } + if (u.ualign.type != altaralign) { /* Sacrificing at an altar of a different alignment */ offer_different_alignment_altar(otmp, altaralign); - return ECMD_TIME; - } else { + return; + } + consume_offering(otmp); + /* OK, you get brownie points. */ + if (u.ugangr) { int saved_anger = u.ugangr; + u.ugangr -= ((value * (u.ualign.type == A_CHAOTIC ? 2 : 3)) + / MAXVALUE); + if (u.ugangr < 0) + u.ugangr = 0; + if (u.ugangr != saved_anger) { + if (u.ugangr) { + pline("%s seems %s.", u_gname(), + Hallucination ? "groovy" : "slightly mollified"); + + if ((int) u.uluck < 0) + change_luck(1); + } else { + pline("%s seems %s.", u_gname(), + Hallucination ? "cosmic (not a new fact)" + : "mollified"); + + if ((int) u.uluck < 0) + u.uluck = 0; + } + } else { /* not satisfied yet */ + if (Hallucination) + pline_The("gods seem tall."); + else + You("have a feeling of inadequacy."); + } + } else if (ugod_is_angry()) { + if (value > MAXVALUE) + value = MAXVALUE; + if (value > -u.ualign.record) + value = -u.ualign.record; + adjalign(value); + You_feel("partially absolved."); + } else if (u.ublesscnt > 0) { int saved_cnt = u.ublesscnt; - int saved_luck = u.uluck; - - consume_offering(otmp); - /* OK, you get brownie points. */ - if (u.ugangr) { - u.ugangr -= ((value * (u.ualign.type == A_CHAOTIC ? 2 : 3)) - / MAXVALUE); - if (u.ugangr < 0) - u.ugangr = 0; - if (u.ugangr != saved_anger) { - if (u.ugangr) { - pline("%s seems %s.", u_gname(), - Hallucination ? "groovy" : "slightly mollified"); - - if ((int) u.uluck < 0) - change_luck(1); - } else { - pline("%s seems %s.", u_gname(), - Hallucination ? "cosmic (not a new fact)" - : "mollified"); - - if ((int) u.uluck < 0) - u.uluck = 0; - } - } else { /* not satisfied yet */ + u.ublesscnt -= ((value * (u.ualign.type == A_CHAOTIC ? 500 : 300)) + / MAXVALUE); + if (u.ublesscnt < 0) + u.ublesscnt = 0; + if (u.ublesscnt != saved_cnt) { + if (u.ublesscnt) { if (Hallucination) - pline_The("gods seem tall."); + You("realize that the gods are not like you and I."); else - You("have a feeling of inadequacy."); - } - } else if (ugod_is_angry()) { - if (value > MAXVALUE) - value = MAXVALUE; - if (value > -u.ualign.record) - value = -u.ualign.record; - adjalign(value); - You_feel("partially absolved."); - } else if (u.ublesscnt > 0) { - u.ublesscnt -= ((value * (u.ualign.type == A_CHAOTIC ? 500 : 300)) - / MAXVALUE); - if (u.ublesscnt < 0) - u.ublesscnt = 0; - if (u.ublesscnt != saved_cnt) { - if (u.ublesscnt) { - if (Hallucination) - You("realize that the gods are not like you and I."); - else - You("have a hopeful feeling."); - if ((int) u.uluck < 0) - change_luck(1); - } else { - if (Hallucination) - pline("Overall, there is a smell of fried onions."); - else - You("have a feeling of reconciliation."); - if ((int) u.uluck < 0) - u.uluck = 0; - } - } - } else { - if (bestow_artifact()) - return ECMD_TIME; - change_luck((value * LUCKMAX) / (MAXVALUE * 2)); - if ((int) u.uluck < 0) - u.uluck = 0; - if (u.uluck != saved_luck) { - if (Blind) - You("think %s brushed your %s.", something, - body_part(FOOT)); + You("have a hopeful feeling."); + if ((int) u.uluck < 0) + change_luck(1); + } else { + if (Hallucination) + pline("Overall, there is a smell of fried onions."); else - You(Hallucination - ? "see crabgrass at your %s. A funny thing in a dungeon." - : "glimpse a four-leaf clover at your %s.", - makeplural(body_part(FOOT))); + You("have a feeling of reconciliation."); + if ((int) u.uluck < 0) + u.uluck = 0; } } + } else { + int orig_luck, luck_increase; + + if (bestow_artifact(value)) + return; + + orig_luck = u.uluck; + luck_increase = (value * LUCKMAX) / (MAXVALUE * 2); + + /* sacrificing can't increase non-bonus Luck to above the value of the + sacrifice; this prevents players immediately maxing their Luck as + soon as they find an altar and a few rations via sacrificing lots + of low-valued corpses, which can unbalance the early game */ + if (orig_luck > value) + luck_increase = 0; + else if (orig_luck + luck_increase > value) + luck_increase = value - orig_luck; + + change_luck(luck_increase); + if ((int) u.uluck < 0) + u.uluck = 0; + if (u.uluck != orig_luck) { + if (Blind) + You("think %s brushed your %s.", something, + body_part(FOOT)); + else + You(Hallucination + ? "see crabgrass at your %s. A funny thing in a dungeon." + : "glimpse a four-leaf clover at your %s.", + makeplural(body_part(FOOT))); + } } - return ECMD_TIME; } /* determine prayer results in advance; also used for enlightenment */ @@ -2096,14 +2176,19 @@ pray_revive(void) struct obj *otmp; for (otmp = svl.level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere) - if (otmp->otyp == CORPSE && has_omonst(otmp) + if ((otmp->otyp == CORPSE || otmp->otyp == STATUE) + && has_omonst(otmp) && OMONST(otmp)->mtame && !OMONST(otmp)->isminion) break; if (!otmp) return FALSE; - return (revive(otmp, TRUE) != NULL); + if (otmp->otyp == CORPSE) + return (revive(otmp, TRUE) != NULL); + else { + return (animate_statue(otmp, u.ux, u.uy, ANIMATE_SPELL, NULL) != NULL); + } } /* #pray command */ @@ -2283,18 +2368,23 @@ maybe_turn_mon_iter(struct monst *mtmp) than zombies. */ case S_LICH: xlev += 2; + FALLTHROUGH; /*FALLTHRU*/ case S_GHOST: xlev += 2; + FALLTHROUGH; /*FALLTHRU*/ case S_VAMPIRE: xlev += 2; + FALLTHROUGH; /*FALLTHRU*/ case S_WRAITH: xlev += 2; + FALLTHROUGH; /*FALLTHRU*/ case S_MUMMY: xlev += 2; + FALLTHROUGH; /*FALLTHRU*/ case S_ZOMBIE: if (u.ulevel >= xlev && !resist(mtmp, '\0', 0, NOTELL)) { @@ -2306,6 +2396,7 @@ maybe_turn_mon_iter(struct monst *mtmp) } break; } /* else flee */ + FALLTHROUGH; /*FALLTHRU*/ default: monflee(mtmp, 0, FALSE, TRUE); @@ -2604,6 +2695,7 @@ blocked_boulder(int dx, int dy) /* this is only approximate since multiple boulders might sink */ if (is_pool_or_lava(nx, ny)) /* does its own isok() check */ break; /* still need Sokoban check below */ + FALLTHROUGH; /*FALLTHRU*/ default: /* more than one boulder--blocked after they push the top one; @@ -2615,7 +2707,7 @@ blocked_boulder(int dx, int dy) return TRUE; if (!isok(nx, ny)) return TRUE; - if (IS_ROCK(levl[nx][ny].typ)) + if (IS_OBSTRUCTED(levl[nx][ny].typ)) return TRUE; if (sobj_at(BOULDER, nx, ny)) return TRUE; diff --git a/src/priest.c b/src/priest.c index 8308a96e6..10e83accc 100644 --- a/src/priest.c +++ b/src/priest.c @@ -20,6 +20,7 @@ newepri(struct monst *mtmp) if (!EPRI(mtmp)) { EPRI(mtmp) = (struct epri *) alloc(sizeof(struct epri)); (void) memset((genericptr_t) EPRI(mtmp), 0, sizeof(struct epri)); + EPRI(mtmp)->parentmid = mtmp->m_id; } } diff --git a/src/questpgr.c b/src/questpgr.c index 29ef494c8..707846e90 100644 --- a/src/questpgr.c +++ b/src/questpgr.c @@ -374,6 +374,7 @@ convert_line(char *in_line, char *out_line) /* pluralize */ case 'P': gc.cvt_buf[0] = highc(gc.cvt_buf[0]); + FALLTHROUGH; /*FALLTHRU*/ case 'p': Strcpy(gc.cvt_buf, makeplural(gc.cvt_buf)); @@ -382,6 +383,7 @@ convert_line(char *in_line, char *out_line) /* append possessive suffix */ case 'S': gc.cvt_buf[0] = highc(gc.cvt_buf[0]); + FALLTHROUGH; /*FALLTHRU*/ case 's': Strcpy(gc.cvt_buf, s_suffix(gc.cvt_buf)); @@ -403,8 +405,9 @@ convert_line(char *in_line, char *out_line) Strcat(cc, gc.cvt_buf); cc += strlen(gc.cvt_buf); break; - } /* else fall through */ - + } + FALLTHROUGH; + /* FALLTHRU */ default: *cc++ = *c; break; diff --git a/src/read.c b/src/read.c index 5d1a3c92d..5ebe8ab64 100644 --- a/src/read.c +++ b/src/read.c @@ -16,6 +16,7 @@ staticfn int read_ok(struct obj *); staticfn void stripspe(struct obj *); staticfn void p_glow1(struct obj *); staticfn void p_glow2(struct obj *, const char *); +staticfn void p_glow3(struct obj *, const char *); staticfn void forget(int); staticfn int maybe_tame(struct monst *, struct obj *); staticfn boolean can_center_cloud(coordxy, coordxy); @@ -490,11 +491,13 @@ doread(void) pline("This %s has no label.", singular(scroll, xname)); return ECMD_OK; } else if (otyp == MAGIC_MARKER) { + static const int red_mons[] = { + PM_FIRE_ANT, PM_PYROLISK, PM_HELL_HOUND, PM_IMP, + PM_LARGE_MIMIC, PM_LEOCROTTA, PM_SCORPION, PM_XAN, + PM_GIANT_BAT, PM_WATER_MOCCASIN, PM_FLESH_GOLEM, + PM_BARBED_DEVIL, PM_MARILITH, PM_PIRANHA + }; char buf[BUFSZ]; - const int red_mons[] = { PM_FIRE_ANT, PM_PYROLISK, PM_HELL_HOUND, - PM_IMP, PM_LARGE_MIMIC, PM_LEOCROTTA, PM_SCORPION, PM_XAN, - PM_GIANT_BAT, PM_WATER_MOCCASIN, PM_FLESH_GOLEM, PM_BARBED_DEVIL, - PM_MARILITH, PM_PIRANHA }; struct permonst *pm = &mons[red_mons[scroll->o_id % SIZE(red_mons)]]; if (Blind) { @@ -672,6 +675,14 @@ p_glow2(struct obj *otmp, const char *color) Blind ? "" : " ", Blind ? "" : hcolor(color)); } +staticfn void +p_glow3(struct obj *otmp, const char *color) +{ + pline("%s feebly%s%s for a moment.", + Yobjnam2(otmp, Blind ? "vibrate" : "glow"), + Blind ? "" : " ", Blind ? "" : hcolor(color)); +} + /* getobj callback for object to charge */ int charge_ok(struct obj *obj) @@ -724,7 +735,7 @@ recharge(struct obj *obj, int curse_bless) if (obj->oclass == WAND_CLASS) { int lim = (obj->otyp == WAN_WISHING) - ? 3 + ? 1 : (objects[obj->otyp].oc_dir != NODIR) ? 8 : 15; /* undo any prior cancellation, even when is_cursed */ @@ -758,7 +769,7 @@ recharge(struct obj *obj, int curse_bless) if (is_cursed) { stripspe(obj); } else { - n = (lim == 3) ? 3 : rn1(5, lim + 1 - 5); + n = (lim == 1) ? 1 : rn1(5, lim + 1 - 5); if (!is_blessed) n = rnd(n); @@ -767,10 +778,15 @@ recharge(struct obj *obj, int curse_bless) else obj->spe++; if (obj->otyp == WAN_WISHING && obj->spe > 3) { + /* wands can't give more than three wishes; this code is + currently unreachable but left in case the rules for + wands of wishing change in future */ wand_explode(obj, 1); return; } - if (obj->spe >= lim) + if (lim == 1) + p_glow3(obj, NH_BLUE); + else if (obj->spe >= lim) p_glow2(obj, NH_BLUE); else p_glow1(obj); @@ -1037,8 +1053,9 @@ maybe_tame(struct monst *mtmp, struct obj *sobj) /* for a shopkeeper, tamedog() will call make_happy_shk() but not tame the target, so call it even if taming gets resisted */ if (!resist(mtmp, sobj->oclass, 0, NOTELL) || mtmp->isshk) - (void) tamedog(mtmp, (struct obj *) 0, FALSE); - if ((!was_peaceful && mtmp->mpeaceful) || (!was_tame && mtmp->mtame)) + (void) tamedog(mtmp, sobj, FALSE); + + if ((!was_peaceful && mtmp->mpeaceful) || was_tame != mtmp->mtame) return 1; } return 0; @@ -1273,6 +1290,21 @@ seffect_destroy_armor(struct obj **sobjp) return; } if (!scursed || !otmp || !otmp->cursed) { + boolean gets_choice = (otmp && sobj && sobj->blessed + && count_worn_armor() > 1); + + if (gets_choice) { + struct obj *atmp; + + if (!objects[sobj->otyp].oc_name_known) + pline("This is %s!", an(actualoname(sobj))); + gk.known = TRUE; + atmp = getobj("destroy", any_worn_armor_ok, GETOBJ_PROMPT); + /* check the return value, if user picked non-valid obj */ + if (any_worn_armor_ok(atmp) == GETOBJ_SUGGEST) + otmp = atmp; + } + if (!destroy_arm(otmp)) { strange_feeling(sobj, "Your skin itches."); *sobjp = 0; /* useup() in strange_feeling() */ @@ -1662,7 +1694,7 @@ seffect_light(struct obj **sobjp) mon = makemon(&mons[pm], u.ux, u.uy, MM_EDOG | NO_MINVENT | MM_NOMSG); if (mon) { - initedog(mon); + initedog(mon, TRUE); mon->msleeping = 0; mon->mcan = TRUE; if (canspotmon(mon)) @@ -1850,7 +1882,7 @@ seffect_earth(struct obj **sobjp) for (y = u.uy - 1; y <= u.uy + 1; y++) { /* Is this a suitable spot? */ if (isok(x, y) && !closed_door(x, y) - && !IS_ROCK(levl[x][y].typ) + && !IS_OBSTRUCTED(levl[x][y].typ) && !IS_AIR(levl[x][y].typ) && (x != u.ux || y != u.uy)) { nboulders += @@ -2017,8 +2049,11 @@ seffect_magic_mapping(struct obj **sobjp) for (x = 1; x < COLNO; x++) for (y = 0; y < ROWNO; y++) - if (levl[x][y].typ == SDOOR) + if (levl[x][y].typ == SDOOR) { cvt_sdoor_to_door(&levl[x][y]); + if (Is_rogue_level(&u.uz)) + unblock_point(x, y); + } /* do_mapping() already reveals secret passages */ } gk.known = TRUE; @@ -2082,7 +2117,8 @@ seffect_mail(struct obj **sobjp) /* scroll effects; return 1 if we use up the scroll and possibly make it become discovered, 0 if caller should take care of those side-effects */ int -seffects(struct obj *sobj) /* sobj - scroll or fake spellbook for spell */ +seffects( + struct obj *sobj) /* sobj - scroll or fake spellbook for spell */ { int otyp = sobj->otyp; @@ -2301,7 +2337,7 @@ drop_boulder_on_monster(coordxy x, coordxy y, boolean confused, boolean byu) /* overcharging any wand or zapping/engraving cursed wand */ void -wand_explode(struct obj* obj, int chg /* recharging */) +wand_explode(struct obj *obj, int chg /* recharging */) { const char *expl = !chg ? "suddenly" : "vibrates violently and"; int dmg, n, k; @@ -2382,7 +2418,7 @@ litroom( boolean on, /* True: make nearby area lit; False: cursed scroll */ struct obj *obj) /* scroll, spellbook (for spell), or wand of light */ { - struct obj *otmp; + struct obj *otmp, *nextobj; boolean blessed_effect = (obj && obj->oclass == SCROLL_CLASS && obj->blessed); boolean no_op = (u.uswallow || Underwater || Is_waterlevel(&u.uz)); @@ -2400,7 +2436,8 @@ litroom( * Shouldn't this affect all lit objects in the area of effect * rather than just those carried by the hero? */ - for (otmp = gi.invent; otmp; otmp = otmp->nobj) { + for (otmp = gi.invent; otmp; otmp = nextobj) { + nextobj = otmp->nobj; if (otmp->lamplit) { if (!artifact_light(otmp)) (void) snuff_lit(otmp); @@ -2431,7 +2468,8 @@ litroom( } else { /* on */ if (blessed_effect) { /* might bless artifact lights; no effect on ordinary lights */ - for (otmp = gi.invent; otmp; otmp = otmp->nobj) { + for (otmp = gi.invent; otmp; otmp = nextobj) { + nextobj = otmp->nobj; if (otmp->lamplit && artifact_light(otmp)) /* wielded Sunsword or worn gold dragon scales/mail; maybe raise its BUC state if not already blessed */ @@ -2914,7 +2952,7 @@ punish(struct obj *sobj) You("are being punished for your misbehavior!"); if (Punished) { Your("iron ball gets heavier."); - uball->owt += IRON_BALL_W_INCR * (1 + cursed_levy); + uball->owt += WT_IRON_BALL_INCR * (1 + cursed_levy); return; } if (amorphous(gy.youmonst.data) || is_whirly(gy.youmonst.data) @@ -3213,9 +3251,9 @@ create_particular_creation( set_malign(mtmp); } if (d->saddled && can_saddle(mtmp) && !which_armor(mtmp, W_SADDLE)) { - struct obj *otmp = mksobj(SADDLE, TRUE, FALSE); - - put_saddle_on_mon(otmp, mtmp); + /* NULL obj arg means put_saddle_on_mon() + * will create the saddle itself */ + put_saddle_on_mon((struct obj *) 0, mtmp); } if (d->hidden && ((is_hider(mtmp->data) && mtmp->data->mlet != S_MIMIC) @@ -3227,17 +3265,9 @@ create_particular_creation( /* if asking for 'hidden', show location of every created monster that can't be seen--whether that's due to successfully hiding or vision issues (line-of-sight, invisibility, blindness) */ - if ((d->hidden || d->invisible) && !canspotmon(mtmp)) { - int count = couldsee(mx, my) ? 8 : 4; - char saveviz = gv.viz_array[my][mx]; + if ((d->hidden || d->invisible) && !canspotmon(mtmp)) + flash_mon(mtmp); - if (!flags.sparkle) - count /= 2; - gv.viz_array[my][mx] |= (IN_SIGHT | COULD_SEE); - flash_glyph_at(mx, my, mon_to_glyph(mtmp, newsym_rn2), count); - gv.viz_array[my][mx] = saveviz; - newsym(mx, my); - } madeany = TRUE; /* in case we got a doppelganger instead of what was asked for, make it start out looking like what was asked for */ diff --git a/src/rect.c b/src/rect.c index 801af1343..1fb0f2b82 100644 --- a/src/rect.c +++ b/src/rect.c @@ -46,6 +46,7 @@ free_rect(void) { if (rect) free(rect); + rect = 0; n_rects = rect_cnt = 0; } diff --git a/src/region.c b/src/region.c index b38f0487f..796d48fc1 100644 --- a/src/region.c +++ b/src/region.c @@ -12,6 +12,8 @@ #define NO_CALLBACK (-1) +void free_region(NhRegion *); +#ifndef SFCTOOL boolean inside_gas_cloud(genericptr, genericptr); boolean expire_gas_cloud(genericptr, genericptr); boolean inside_rect(NhRect *, int, int); @@ -24,7 +26,6 @@ boolean mon_in_region(NhRegion *, struct monst *); #if 0 NhRegion *clone_region(NhRegion *); #endif -void free_region(NhRegion *); void add_region(NhRegion *); void remove_region(NhRegion *); @@ -253,6 +254,7 @@ clone_region(NhRegion *reg) } #endif /*0*/ +#endif /* !SFCTOOL */ /* * Free mem from region. @@ -273,6 +275,7 @@ free_region(NhRegion *reg) } } +#ifndef SFCTOOL /* * Add a region to the list. * This actually activates the region. @@ -357,11 +360,14 @@ remove_region(NhRegion *reg) reg->ttl = -2L; /* for visible_region_at */ if (reg->visible) { int pass; + boolean tmp_uinwater = u.uinwater; /* need to process the region's spots twice, first unblocking all locations which no longer block line-of-sight, then redrawing spots within revised line-of-sight; skip second pass if blind */ for (pass = 1; pass <= (Blind ? 1 : 2); ++pass) { + u.uinwater = (pass == 1) ? 0 : tmp_uinwater; + for (x = reg->bounding_box.lx; x <= reg->bounding_box.hx; x++) for (y = reg->bounding_box.ly; y <= reg->bounding_box.hy; y++) if (isok(x, y) && inside_region(reg, x, y)) { @@ -374,9 +380,11 @@ remove_region(NhRegion *reg) } } } + u.uinwater = tmp_uinwater; } free_region(reg); } +#endif /* !SFCTOOL */ /* * Remove all regions and clear all related data. This must be done @@ -396,6 +404,7 @@ clear_regions(void) gr.regions = (NhRegion **) 0; } +#ifndef SFCTOOL /* * This function is called every turn. * It makes the regions age, if necessary and calls the appropriate @@ -735,73 +744,56 @@ save_regions(NHFILE *nhfp) int i, j; unsigned n; - if (!perform_bwrite(nhfp)) + if (!update_file(nhfp)) goto skip_lots; - if (nhfp->structlevel) { - /* timestamp */ - bwrite(nhfp->fd, (genericptr_t) &svm.moves, sizeof svm.moves); - bwrite(nhfp->fd, (genericptr_t) &svn.n_regions, sizeof svn.n_regions); - } + /* timestamp */ + Sfo_long(nhfp, &svm.moves, "region-tmstamp"); + Sfo_int(nhfp, &svn.n_regions, "region-region_count"); + for (i = 0; i < svn.n_regions; i++) { r = gr.regions[i]; - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &r->bounding_box, - sizeof (NhRect)); - bwrite(nhfp->fd, (genericptr_t) &r->nrects, sizeof (short)); - } + Sfo_nhrect(nhfp, &r->bounding_box, "region-bounding_box"); + Sfo_short(nhfp, &r->nrects, "region-nrects"); for (j = 0; j < r->nrects; j++) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &r->rects[j], - sizeof (NhRect)); + Sfo_nhrect(nhfp, &r->rects[j], "region-rect"); } - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &r->attach_2_u, sizeof (boolean)); - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &r->attach_2_m, sizeof (unsigned)); - + Sfo_boolean(nhfp, &r->attach_2_u, "region-attach_2_u"); + Sfo_unsigned(nhfp, &r->attach_2_m, "region-attach_2_m"); + n = 0; n = !r->enter_msg ? 0U : (unsigned) strlen(r->enter_msg); - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &n, sizeof n); + Sfo_unsigned(nhfp, &n, "region-enter_msg_length"); if (n > 0) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) r->enter_msg, n); + Sfo_char(nhfp, (char *) r->enter_msg, + "region-enter_msg", (int) n); } n = !r->leave_msg ? 0U : (unsigned) strlen(r->leave_msg); - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &n, sizeof n); + Sfo_unsigned(nhfp, &n, "region-leave_msg_length"); if (n > 0) { - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) r->leave_msg, n); - } - } - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &r->ttl, sizeof (long)); - bwrite(nhfp->fd, (genericptr_t) &r->expire_f, sizeof (short)); - bwrite(nhfp->fd, (genericptr_t) &r->can_enter_f, sizeof (short)); - bwrite(nhfp->fd, (genericptr_t) &r->enter_f, sizeof (short)); - bwrite(nhfp->fd, (genericptr_t) &r->can_leave_f, sizeof (short)); - bwrite(nhfp->fd, (genericptr_t) &r->leave_f, sizeof (short)); - bwrite(nhfp->fd, (genericptr_t) &r->inside_f, sizeof (short)); - bwrite(nhfp->fd, (genericptr_t) &r->player_flags, - sizeof (unsigned)); - bwrite(nhfp->fd, (genericptr_t) &r->n_monst, sizeof (short)); + Sfo_char(nhfp, (char *) r->leave_msg, "region-leave_msg", (int) n); } + Sfo_long(nhfp, &r->ttl, "region-ttl"); + Sfo_short(nhfp, &r->expire_f, "region-expire_f"); + Sfo_short(nhfp, &r->can_enter_f, "region-can_enter_f"); + Sfo_short(nhfp, &r->enter_f, "region-enter_f"); + Sfo_short(nhfp, &r->can_leave_f, "region-can_leave_f"); + Sfo_short(nhfp, &r->leave_f, "region-leave_f"); + Sfo_short(nhfp, &r->inside_f, "region-inside_f"); + Sfo_unsigned(nhfp, &r->player_flags, "region-player_flags"); + Sfo_short(nhfp, &r->n_monst, "region-monster_count"); + for (j = 0; j < r->n_monst; j++) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &r->monsters[j], - sizeof (unsigned)); - } - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &r->visible, sizeof (boolean)); - bwrite(nhfp->fd, (genericptr_t) &r->glyph, sizeof (int)); - bwrite(nhfp->fd, (genericptr_t) &r->arg, sizeof (anything)); + Sfo_unsigned(nhfp, &r->monsters[j], "region-monster"); } + Sfo_boolean(nhfp, &r->visible, "region-visible"); + Sfo_int(nhfp, &r->glyph, "region-glyph"); + Sfo_any(nhfp, &r->arg, "region-arg"); } skip_lots: if (release_data(nhfp)) clear_regions(); } +#endif /* !SFCTOOL */ void rest_regions(NHFILE *nhfp) @@ -814,99 +806,79 @@ rest_regions(NHFILE *nhfp) boolean ghostly = (nhfp->ftype == NHF_BONESFILE); clear_regions(); /* Just for security */ - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) &tmstamp, sizeof (tmstamp)); + Sfi_long(nhfp, &tmstamp, "region-tmstamp"); if (ghostly) tmstamp = 0; else tmstamp = (svm.moves - tmstamp); - - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) &svn.n_regions, sizeof svn.n_regions); - + Sfi_int(nhfp, &svn.n_regions, "region-region_count"); gm.max_regions = svn.n_regions; if (svn.n_regions > 0) gr.regions = (NhRegion **) alloc(svn.n_regions * sizeof (NhRegion *)); for (i = 0; i < svn.n_regions; i++) { r = gr.regions[i] = (NhRegion *) alloc(sizeof (NhRegion)); - if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) &r->bounding_box, sizeof (NhRect)); - mread(nhfp->fd, (genericptr_t) &r->nrects, sizeof (short)); - } + Sfi_nhrect(nhfp, &r->bounding_box, "region-bounding box"); + Sfi_short(nhfp, &r->nrects, "region-nrects"); if (r->nrects > 0) r->rects = (NhRect *) alloc(r->nrects * sizeof (NhRect)); else r->rects = (NhRect *) 0; for (j = 0; j < r->nrects; j++) { - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) &r->rects[j], sizeof (NhRect)); - } - if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) &r->attach_2_u, sizeof (boolean)); - mread(nhfp->fd, (genericptr_t) &r->attach_2_m, sizeof (unsigned)); + Sfi_nhrect(nhfp, &r->rects[j], "region-rect"); } - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) &n, sizeof n); + Sfi_boolean(nhfp, &r->attach_2_u, "region-attach_2_u"); + Sfi_unsigned(nhfp, &r->attach_2_m, "region-attach_2_m"); + Sfi_unsigned(nhfp, &n, "region-enter_msg_length"); if (n > 0) { msg_buf = (char *) alloc(n + 1); - if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) msg_buf, n); - } + Sfi_char(nhfp, msg_buf, "region-enter_msg", n); msg_buf[n] = '\0'; - } else + } else { msg_buf = (char *) 0; + } r->enter_msg = (const char *) msg_buf; - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) &n, sizeof n); - if (n > 0) { + Sfi_unsigned(nhfp, &n, "region-leave_msg_length"); + if (n > 0) { msg_buf = (char *) alloc(n + 1); - if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) msg_buf, n); - } + Sfi_char(nhfp, msg_buf, "region-leave_msg", n); msg_buf[n] = '\0'; - } else + r->leave_msg = (const char *) msg_buf; + } else { msg_buf = (char *) 0; - r->leave_msg = (const char *) msg_buf; + } + r->leave_msg = (const char *) msg_buf; - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) &r->ttl, sizeof (long)); + Sfi_long(nhfp, &r->ttl, "region-ttl"); /* check for expired region */ if (r->ttl >= 0L) r->ttl = (r->ttl > tmstamp) ? r->ttl - tmstamp : 0L; - if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) &r->expire_f, sizeof (short)); - mread(nhfp->fd, (genericptr_t) &r->can_enter_f, sizeof (short)); - mread(nhfp->fd, (genericptr_t) &r->enter_f, sizeof (short)); - mread(nhfp->fd, (genericptr_t) &r->can_leave_f, sizeof (short)); - mread(nhfp->fd, (genericptr_t) &r->leave_f, sizeof (short)); - mread(nhfp->fd, (genericptr_t) &r->inside_f, sizeof (short)); - mread(nhfp->fd, (genericptr_t) &r->player_flags, - sizeof (unsigned)); - } + Sfi_short(nhfp, &r->expire_f, "region-expire_f"); + Sfi_short(nhfp, &r->can_enter_f, "region-can_enter_f"); + Sfi_short(nhfp, &r->enter_f, "region-enter_f"); + Sfi_short(nhfp, &r->can_leave_f, "region-can_leave_f"); + Sfi_short(nhfp, &r->leave_f, "region-leave_f"); + Sfi_short(nhfp, &r->inside_f, "region-inside_f"); + Sfi_unsigned(nhfp, &r->player_flags, "region-player_flags"); if (ghostly) { /* settings pertained to old player */ clear_hero_inside(r); clear_heros_fault(r); } - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) &r->n_monst, sizeof (short)); + Sfi_short(nhfp, &r->n_monst, "region-monster_count"); if (r->n_monst > 0) r->monsters = (unsigned *) alloc(r->n_monst * sizeof (unsigned)); else r->monsters = (unsigned *) 0; r->max_monst = r->n_monst; for (j = 0; j < r->n_monst; j++) { - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) &r->monsters[j], - sizeof (unsigned)); - } - if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) &r->visible, sizeof (boolean)); - mread(nhfp->fd, (genericptr_t) &r->glyph, sizeof (int)); - mread(nhfp->fd, (genericptr_t) &r->arg, sizeof (anything)); + Sfi_unsigned(nhfp, &r->monsters[j], "region-monster"); } + Sfi_boolean(nhfp, &r->visible, "region-visible"); + Sfi_int(nhfp, &r->glyph, "region-glyph"); + Sfi_any(nhfp, &r->arg, "region-arg"); } +#ifndef SFCTOOL /* remove expired regions, do not trigger the expire_f callback (yet!); also update monster lists if this data is coming from a bones file */ for (i = svn.n_regions - 1; i >= 0; i--) { @@ -916,8 +888,10 @@ rest_regions(NHFILE *nhfp) else if (ghostly && r->n_monst > 0) reset_region_mids(r); } +#endif /* !SFCTOOL */ } +#ifndef SFCTOOL DISABLE_WARNING_FORMAT_NONLITERAL /* to support '#stats' wizard-mode command */ @@ -1429,5 +1403,6 @@ region_safety(void) if (BlindedTimeout == 1L) make_blinded(0L, TRUE); } +#endif /* !SFCTOOL */ /*region.c*/ diff --git a/src/report.c b/src/report.c index 2d4eae979..ecfa0ad17 100644 --- a/src/report.c +++ b/src/report.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 report.c $NHDT-Date: 1710525914 2024/03/15 18:05:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.7 $ */ +/* NetHack 3.7 report.c $NHDT-Date: 1741406837 2025/03/07 20:07:17 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.16 $ */ /* Copyright (c) Kenneth Lorber, Kensington, Maryland, 2024 */ /* NetHack may be freely redistributed. See license for details. */ @@ -9,7 +9,6 @@ # ifndef NO_SIGNAL #include # endif -#include # ifndef LONG_MAX #include # endif @@ -46,7 +45,7 @@ # define HASH_OFLAGS O_RDONLY # define HASH_BINFILE_DECL char *binfile = argv[0]; # if (NH_DEVEL_STATUS == NH_STATUS_BETA) -# define HASH_BINFILE \ +# define HASH_BINFILE() \ if (!binfile || !*binfile) { \ /* If this triggers, investigate CFBundleGetMainBundle */ \ /* or CFBundleCopyExecutableURL. */ \ @@ -111,7 +110,7 @@ static char bid[40]; /* ARGSUSED */ void -crashreport_init(int argc UNUSED, char *argv[] UNUSED) +crashreport_init(int argc, char *argv[]) { static int once = 0; if (once++) /* NetHackW.exe calls us twice */ @@ -163,12 +162,15 @@ crashreport_init(int argc UNUSED, char *argv[] UNUSED) --cnt; } *p = '\0'; + HASH_CLEANUP(ctxp); return; skip: Strcpy(bid, "unknown"); HASH_CLEANUP(ctxp); HASH_PRAGMA_END + nhUse(argc); + nhUse(argv); } #undef HASH_CONTEXTPTR @@ -219,7 +221,7 @@ crashreport_bidshow(void) mark = uend; \ if (utmp >= urem) \ goto full; \ - memcpy(uend, str, utmp); \ + memcpy(uend, str, utmp); \ uend += utmp; urem -= utmp; \ *uend = '\0'; @@ -253,10 +255,18 @@ swr_add_uricoded( *out = markp, *remaining = 0; **out = '\0'; return TRUE; + } else { + char chr[40]; /* [4] should suffice */ + int x; + + Sprintf(chr, "%%%02X", *in); + x = (int) strlen(chr); + if (x <= *remaining) { + Strcpy(*out, chr); + *out += x; + *remaining -= x; + } } - int x = snprintf(*out, *remaining, "%%%02X", *in); - *out += x; - *remaining -= x; } in++; if (!*remaining) { @@ -289,15 +299,20 @@ submit_web_report(int cos, const char *msg, const char *why) if (!sysopt.crashreporturl) return FALSE; SWR_ADD(sysopt.crashreporturl); + /* + * Note: all snprintf() calls here changed to sprintf() to avoid + * complaints from static analyzer. All but one were unnecessary + * since they were formatting int or unsigned into a large buffer. + */ /* cos - operation, v - version */ - Snprintf(temp, sizeof temp, "?cos=%d&v=1", cos); + Sprintf(temp, "?cos=%d&v=1", cos); SWR_ADD(temp); /* msg==NULL for #bugreport */ if (msg) { SWR_ADD("&subject="); - Snprintf(temp, sizeof temp, "%s report for NetHack %s", - msg, version_string(temp2, sizeof temp2 )); + Sprintf(temp, "%.40s report for NetHack %.40s", + msg, version_string(temp2, sizeof temp2 )); SWR_ADD_URIcoded(temp); } @@ -352,7 +367,7 @@ submit_web_report(int cos, const char *msg, const char *why) # if 0 // __linux__ // not needed for MacOS // XXX is it actually needed for linux? TBD - Snprintf(temp2, sizeof temp2, "[%02lu]\n", (unsigned long) x); + Sprintf(temp2, "[%02lu]\n", (unsigned long) x); uend--; // remove the \n we added above SWR_ADD_URIcoded(temp2); # endif // linux @@ -384,7 +399,7 @@ submit_web_report(int cos, const char *msg, const char *why) // detailrows: Guess since we can't know the // width of the window. SWR_ADD("&detailrows="); - Snprintf(temp, sizeof temp, "%d", min(count + countpp, 30)); + Sprintf(temp, "%d", min(count + countpp, 30)); SWR_ADD_URIcoded(temp); full: @@ -403,7 +418,7 @@ printf("ShellExecute returned: %p\n",rv); // >32 is ok int pid = fork(); extern char **environ; if (pid == 0) { - char err[100]; + char err[400]; # ifdef CRASHREPORT_EXEC_NOSTDERR int devnull; /* Keep the output clean - firefox spews useless errors on @@ -413,7 +428,10 @@ printf("ShellExecute returned: %p\n",rv); // >32 is ok # endif (void) execve(CRASHREPORT, (char * const *) xargv, environ); - Sprintf(err, "Can't start " CRASHREPORT ": %s", strerror(errno)); + Sprintf(err, "Can't start " CRASHREPORT ": %.*s", + (int) (sizeof err + - sizeof "Can't start " CRASHREPORT ": "), + strerror(errno)); raw_print(err); # ifdef CRASHREPORT_EXEC_NOSTDERR (void) close(devnull); @@ -446,7 +464,7 @@ dobugreport(void) pline("Unable to send bug report. Please visit %s instead.", (sysopt.crashreporturl && *sysopt.crashreporturl) ? sysopt.crashreporturl - : "https://www.nethack.org" + : DEVTEAM_URL ); } return ECMD_OK; @@ -523,15 +541,15 @@ NH_panictrace_gdb(void) if (greppath == NULL || greppath[0] == 0) return FALSE; - sprintf(buf, "%s -n -q %s %d 2>&1 | %s '^#'", - gdbpath, ARGV0, getpid(), greppath); + Snprintf(buf, sizeof buf, "%s -n -q %s %d 2>&1 | %s '^#'", + gdbpath, ARGV0, getpid(), greppath); gdb = popen(buf, "w"); if (gdb) { raw_print(" Generating more information you may report:\n"); - fprintf(gdb, "bt\nquit\ny"); - fflush(gdb); + (void) fprintf(gdb, "bt\nquit\ny"); + (void) fflush(gdb); sleep(4); /* ugly */ - pclose(gdb); + (void) pclose(gdb); return TRUE; } else { return FALSE; @@ -555,6 +573,7 @@ get_saved_pline(int lineno USED_if_dumplog) #ifdef DUMPLOG_CORE int p; int limit = DUMPLOG_MSG_COUNT; + if (lineno >= DUMPLOG_MSG_COUNT) return NULL; p = (gs.saved_pline_index - 1) % DUMPLOG_MSG_COUNT; @@ -639,4 +658,8 @@ panictrace_setsignals(boolean set) # endif /* NO_SIGNAL */ #endif /* PANICTRACE */ +/* + * FIXME: this should have a lot of '#undef's for onefile support. + */ + /*report.c*/ diff --git a/src/restore.c b/src/restore.c index 63c038124..59bc231ad 100644 --- a/src/restore.c +++ b/src/restore.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 restore.c $NHDT-Date: 1717878585 2024/06/08 20:29:45 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.228 $ */ +/* NetHack 3.7 restore.c $NHDT-Date: 1736530208 2025/01/10 09:30:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.234 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2009. */ /* NetHack may be freely redistributed. See license for details. */ @@ -11,12 +11,6 @@ extern int dotcnt; /* shared with save */ extern int dotrow; /* shared with save */ #endif -#ifdef ZEROCOMP -staticfn void zerocomp_minit(void); -staticfn void zerocomp_mread(int, genericptr_t, unsigned int); -staticfn int zerocomp_mgetc(void); -#endif - staticfn void find_lev_obj(void); staticfn void restlevchn(NHFILE *); staticfn void restdamage(NHFILE *); @@ -26,16 +20,22 @@ staticfn void restmon(NHFILE *, struct monst *); staticfn struct monst *restmonchn(NHFILE *); staticfn struct fruit *loadfruitchn(NHFILE *); staticfn void freefruitchn(struct fruit *); +staticfn void rest_levl(NHFILE *); +staticfn void rest_stairs(NHFILE *); +#ifndef SFCTOOL staticfn void ghostfruit(struct obj *); staticfn boolean restgamestate(NHFILE *); staticfn void restlevelstate(void); staticfn int restlevelfile(xint8); staticfn void rest_bubbles(NHFILE *); staticfn void restore_gamelog(NHFILE *); -staticfn void restore_msghistory(NHFILE *); staticfn void reset_oattached_mids(boolean); -staticfn void rest_levl(NHFILE *, boolean); -staticfn void rest_stairs(NHFILE *); +/* these ones are declared non-static in extern.h if SFCTOOL is defined */ +staticfn boolean restgamestate(NHFILE *); +staticfn void rest_bubbles(NHFILE *); +staticfn void restore_gamelog(NHFILE *); +staticfn void restore_msghistory(NHFILE *); +#endif /* * Save a mapping of IDs from ghost levels to the current level. This @@ -50,8 +50,10 @@ struct bucket { } map[N_PER_BUCKET]; }; +#ifndef SFCTOOL staticfn void clear_id_mapping(void); staticfn void add_id_mapping(unsigned, unsigned); +#endif /* SFCTOOL */ #ifdef AMII_GRAPHICS void amii_setpens(int); /* use colors from save file */ @@ -62,12 +64,9 @@ extern int amii_numcolors; #define Is_IceBox(o) ((o)->otyp == ICE_BOX ? TRUE : FALSE) -/* third arg passed to mread() should be 'unsigned' but most calls use - sizeof so are attempting to pass 'size_t'; mread()'s prototype results - in an implicit conversion; this macro does it explicitly */ -#define Mread(fd,adr,siz) mread((fd), (genericptr_t) (adr), (unsigned) (siz)) - +#ifndef SFCTOOL /* Recalculate svl.level.objects[x][y], since this info was not saved. */ + staticfn void find_lev_obj(void) { @@ -125,6 +124,8 @@ inven_inuse(boolean quietly) } } +#endif /* SFCTOOL */ + staticfn void restlevchn(NHFILE *nhfp) { @@ -132,13 +133,10 @@ restlevchn(NHFILE *nhfp) s_level *tmplev, *x; svs.sp_levchn = (s_level *) 0; - if (nhfp->structlevel) - Mread(nhfp->fd, &cnt, sizeof cnt); - + Sfi_int(nhfp, &cnt, "levchn-lev_count"); for (; cnt > 0; cnt--) { tmplev = (s_level *) alloc(sizeof(s_level)); - if (nhfp->structlevel) - Mread(nhfp->fd, tmplev, sizeof *tmplev); + Sfi_s_level(nhfp, tmplev, "levchn-s_level"); if (!svs.sp_levchn) svs.sp_levchn = tmplev; @@ -157,24 +155,26 @@ restdamage(NHFILE *nhfp) unsigned int dmgcount = 0; int counter; struct damage *tmp_dam; +#ifndef SFCTOOL boolean ghostly = (nhfp->ftype == NHF_BONESFILE); +#endif - if (nhfp->structlevel) - Mread(nhfp->fd, &dmgcount, sizeof dmgcount); + Sfi_unsigned(nhfp, &dmgcount, "damage-damage_count"); counter = (int) dmgcount; if (!counter) return; do { tmp_dam = (struct damage *) alloc(sizeof *tmp_dam); - if (nhfp->structlevel) - Mread(nhfp->fd, tmp_dam, sizeof *tmp_dam); + Sfi_damage(nhfp, tmp_dam, "damage"); +#ifndef SFCTOOL if (ghostly) - tmp_dam->when += (svm.moves - go.omoves); + tmp_dam->when += (svm.moves - svo.omoves); tmp_dam->next = svl.level.damagelist; svl.level.damagelist = tmp_dam; +#endif /* !SFCTOOL */ } while (--counter > 0); } @@ -183,10 +183,9 @@ staticfn void restobj(NHFILE *nhfp, struct obj *otmp) { int buflen = 0; + unsigned omid = 0; - if (nhfp->structlevel) - Mread(nhfp->fd, otmp, sizeof *otmp); - + Sfi_obj(nhfp, otmp, "obj"); otmp->lua_ref_cnt = 0; /* next object pointers are invalid; otmp->cobj needs to be left as is--being non-null is key to restoring container contents */ @@ -196,18 +195,14 @@ restobj(NHFILE *nhfp, struct obj *otmp) otmp->oextra = newoextra(); /* oname - object's name */ - if (nhfp->structlevel) - Mread(nhfp->fd, &buflen, sizeof buflen); - + Sfi_int(nhfp, &buflen, "obj-oname_length"); if (buflen > 0) { /* includes terminating '\0' */ new_oname(otmp, buflen); - if (nhfp->structlevel) - Mread(nhfp->fd, ONAME(otmp), buflen); + Sfi_char(nhfp, ONAME(otmp), "obj-oname", buflen); } /* omonst - corpse or statue might retain full monster details */ - if (nhfp->structlevel) - Mread(nhfp->fd, &buflen, sizeof buflen); + Sfi_int(nhfp, &buflen, "obj-omonst_length"); if (buflen > 0) { newomonst(otmp); /* this is actually a monst struct, so we @@ -216,21 +211,19 @@ restobj(NHFILE *nhfp, struct obj *otmp) } /* omailcmd - feedback mechanism for scroll of mail */ - if (nhfp->structlevel) - Mread(nhfp->fd, &buflen, sizeof buflen); + Sfi_int(nhfp, &buflen, "obj-omailcmd_length"); if (buflen > 0) { char *omailcmd = (char *) alloc(buflen); - if (nhfp->structlevel) - Mread(nhfp->fd, omailcmd, buflen); + Sfi_char(nhfp, omailcmd, "obj-omailcmd", buflen); new_omailcmd(otmp, omailcmd); free((genericptr_t) omailcmd); } /* omid - monster id number, connecting corpse to ghost */ newomid(otmp); /* superfluous; we're already allocated otmp->oextra */ - if (nhfp->structlevel) - Mread(nhfp->fd, &OMID(otmp), sizeof OMID(otmp)); + Sfi_unsigned(nhfp, &omid, "obj-omid"); + OMID(otmp) = omid; } } @@ -240,12 +233,12 @@ restobjchn(NHFILE *nhfp, boolean frozen) struct obj *otmp, *otmp2 = 0; struct obj *first = (struct obj *) 0; int buflen = 0; +#ifndef SFCTOOL boolean ghostly = (nhfp->ftype == NHF_BONESFILE); +#endif while (1) { - if (nhfp->structlevel) - Mread(nhfp->fd, &buflen, sizeof buflen); - + Sfi_int(nhfp, &buflen, "obj-obj_length"); if (buflen == -1) break; @@ -257,6 +250,7 @@ restobjchn(NHFILE *nhfp, boolean frozen) else otmp2->nobj = otmp; +#ifndef SFCTOOL if (ghostly) { unsigned nid = next_ident(); @@ -270,7 +264,8 @@ restobjchn(NHFILE *nhfp, boolean frozen) * immediately after old player died. */ if (ghostly && !frozen && !age_is_relative(otmp)) - otmp->age = svm.moves - go.omoves + otmp->age; + otmp->age = svm.moves - svo.omoves + otmp->age; +#endif /* !SFCTOOL */ /* get contents of a container or statue */ if (Has_contents(otmp)) { @@ -281,6 +276,8 @@ restobjchn(NHFILE *nhfp, boolean frozen) for (otmp3 = otmp->cobj; otmp3; otmp3 = otmp3->nobj) otmp3->ocontainer = otmp; } + +#ifndef SFCTOOL if (otmp->bypass) otmp->bypass = 0; if (!ghostly) { @@ -292,13 +289,16 @@ restobjchn(NHFILE *nhfp, boolean frozen) if (otmp->o_id == svc.context.spbook.o_id) svc.context.spbook.book = otmp; } +#endif /* !SFADUSTER */ otmp2 = otmp; } if (first && otmp2->nobj) { impossible("Restobjchn: error reading objchn."); otmp2->nobj = 0; } - +#ifdef SFCTOOL + nhUse(frozen); +#endif return first; } @@ -306,10 +306,9 @@ restobjchn(NHFILE *nhfp, boolean frozen) staticfn void restmon(NHFILE *nhfp, struct monst *mtmp) { - int buflen = 0; + int buflen = 0, mc = 0; - if (nhfp->structlevel) - Mread(nhfp->fd, mtmp, sizeof *mtmp); + Sfi_monst(nhfp, mtmp, "monst"); /* next monster pointer is invalid */ mtmp->nmon = (struct monst *) 0; @@ -318,58 +317,55 @@ restmon(NHFILE *nhfp, struct monst *mtmp) mtmp->mextra = newmextra(); /* mgivenname - monster's name */ - if (nhfp->structlevel) - Mread(nhfp->fd, &buflen, sizeof buflen); + Sfi_int(nhfp, &buflen, "monst-mgivenname_length"); if (buflen > 0) { /* includes terminating '\0' */ new_mgivenname(mtmp, buflen); - if (nhfp->structlevel) - Mread(nhfp->fd, MGIVENNAME(mtmp), buflen); + Sfi_char(nhfp, MGIVENNAME(mtmp), "monst-mgivenname", (int) buflen); } /* egd - vault guard */ - if (nhfp->structlevel) - Mread(nhfp->fd, &buflen, sizeof buflen); - + Sfi_int(nhfp, &buflen, "monst-egd_length"); if (buflen > 0) { newegd(mtmp); - if (nhfp->structlevel) - Mread(nhfp->fd, EGD(mtmp), sizeof (struct egd)); + Sfi_egd(nhfp, EGD(mtmp), "monst-egd"); } /* epri - temple priest */ - if (nhfp->structlevel) - Mread(nhfp->fd, &buflen, sizeof buflen); + Sfi_int(nhfp, &buflen, "monst-epri_length"); if (buflen > 0) { newepri(mtmp); - if (nhfp->structlevel) - Mread(nhfp->fd, EPRI(mtmp), sizeof (struct epri)); + Sfi_epri(nhfp, EPRI(mtmp), "monst-epri"); } /* eshk - shopkeeper */ - if (nhfp->structlevel) - Mread(nhfp->fd, &buflen, sizeof buflen); + Sfi_int(nhfp, &buflen, "monst-eshk_length"); if (buflen > 0) { neweshk(mtmp); - if (nhfp->structlevel) - Mread(nhfp->fd, ESHK(mtmp), sizeof (struct eshk)); + Sfi_eshk(nhfp, ESHK(mtmp), "monst-eshk"); } /* emin - minion */ - if (nhfp->structlevel) - Mread(nhfp->fd, &buflen, sizeof buflen); + Sfi_int(nhfp, &buflen, "monst-emin_length"); if (buflen > 0) { newemin(mtmp); - if (nhfp->structlevel) - Mread(nhfp->fd, EMIN(mtmp), sizeof (struct emin)); + Sfi_emin(nhfp, EMIN(mtmp), "monst-emin"); } /* edog - pet */ - if (nhfp->structlevel) - Mread(nhfp->fd, &buflen, sizeof buflen); + Sfi_int(nhfp, &buflen, "monst-edog_length"); if (buflen > 0) { newedog(mtmp); - if (nhfp->structlevel) - Mread(nhfp->fd, EDOG(mtmp), sizeof (struct edog)); + Sfi_edog(nhfp, EDOG(mtmp), "monst-edog"); + /* sanity check to prevent rn2(0) */ + if (EDOG(mtmp)->apport <= 0) { + EDOG(mtmp)->apport = 1; + } + } + /* ebones */ + Sfi_int(nhfp, &buflen, "monst-ebones_length"); + if (buflen > 0) { + newebones(mtmp); + Sfi_ebones(nhfp, EBONES(mtmp), "monst-ebones"); } /* mcorpsenm - obj->corpsenm for mimic posing as corpse or statue (inline int rather than pointer to something) */ - if (nhfp->structlevel) - Mread(nhfp->fd, &MCORPSENM(mtmp), sizeof MCORPSENM(mtmp)); + Sfi_int(nhfp, &mc, "monst-mcorpsenm"); + MCORPSENM(mtmp) = mc; } /* mextra */ } @@ -378,12 +374,14 @@ restmonchn(NHFILE *nhfp) { struct monst *mtmp, *mtmp2 = 0; struct monst *first = (struct monst *) 0; - int offset, buflen = 0; + int buflen = 0; +#ifndef SFCTOOL + int offset; boolean ghostly = (nhfp->ftype == NHF_BONESFILE); +#endif while (1) { - if (nhfp->structlevel) - Mread(nhfp->fd, &buflen, sizeof buflen); + Sfi_int(nhfp, &buflen, "monst-monst_length"); if (buflen == -1) break; @@ -395,6 +393,7 @@ restmonchn(NHFILE *nhfp) else mtmp2->nmon = mtmp; +#ifndef SFCTOOL if (ghostly) { unsigned nid = next_ident(); @@ -412,13 +411,21 @@ restmonchn(NHFILE *nhfp) mtmp->mhpmax = DEFUNCT_MONSTER; } } +#endif /* !SFCTOOL */ + if (mtmp->minvent) { struct obj *obj; mtmp->minvent = restobjchn(nhfp, FALSE); +#ifndef SFCTOOL /* restore monster back pointer */ for (obj = mtmp->minvent; obj; obj = obj->nobj) obj->ocarry = mtmp; +#else + nhUse(obj); +#endif } + +#ifndef SFCTOOL if (mtmp->mw) { struct obj *obj; @@ -442,12 +449,15 @@ restmonchn(NHFILE *nhfp) if (mtmp->m_id == svc.context.polearm.m_id) svc.context.polearm.hitmon = mtmp; } +#endif /* !SFCTOOL */ mtmp2 = mtmp; } +#ifndef SFCTOOL if (first && mtmp2->nmon) { impossible("Restmonchn: error reading monchn."); mtmp2->nmon = 0; } +#endif return first; } @@ -459,8 +469,7 @@ loadfruitchn(NHFILE *nhfp) flist = 0; for (;;) { fnext = newfruit(); - if (nhfp->structlevel) - Mread(nhfp->fd, fnext, sizeof *fnext); + Sfi_fruit(nhfp, fnext, "fruit"); if (fnext->fid != 0) { fnext->nextf = flist; flist = fnext; @@ -483,6 +492,7 @@ freefruitchn(struct fruit *flist) } } +#ifndef SFCTOOL staticfn void ghostfruit(struct obj *otmp) { @@ -497,6 +507,7 @@ ghostfruit(struct obj *otmp) else otmp->spe = fruitadd(oldf->fname, (struct fruit *) 0); } +#endif /* !SFCTOOL */ #ifdef SYSCF #define SYSOPT_CHECK_SAVE_UID sysopt.check_save_uid @@ -504,32 +515,41 @@ ghostfruit(struct obj *otmp) #define SYSOPT_CHECK_SAVE_UID TRUE #endif -staticfn boolean +#ifndef SFCTOOL +staticfn +#endif +boolean restgamestate(NHFILE *nhfp) { + int i; struct flag newgameflags; struct context_info newgamecontext; /* all 0, but has some pointers */ - struct obj *otmp; struct obj *bc_obj; char timebuf[15]; unsigned long uid = 0; +#ifndef SFCTOOL boolean defer_perm_invent, restoring_special; + struct obj *otmp; +#endif - if (nhfp->structlevel) - Mread(nhfp->fd, &uid, sizeof uid); - + Sfi_ulong(nhfp, &uid, "gamestate-uid"); +#ifndef SFCTOOL if (SYSOPT_CHECK_SAVE_UID && uid != (unsigned long) getuid()) { /* strange ... */ - /* for wizard mode, issue a reminder; for others, treat it - as an attempt to cheat and refuse to restore this file */ - pline("Saved game was not yours."); - if (!wizard) + if (!gc.converted_savefile_loaded) + /* for wizard mode, issue a reminder; for others, treat it + * as an attempt to cheat and refuse to restore this file */ + pline("Saved game was not yours."); + if (wizard || gc.converted_savefile_loaded) { + if (gc.converted_savefile_loaded) + gc.converted_savefile_loaded = FALSE; + } else { return FALSE; + } } - +#endif /* SFCTOOL */ newgamecontext = svc.context; /* copy statically init'd context */ - if (nhfp->structlevel) - Mread(nhfp->fd, &svc.context, sizeof svc.context); + Sfi_context_info(nhfp, &svc.context, "gamestate-context"); svc.context.warntype.species = (ismnum(svc.context.warntype.speciesidx)) ? &mons[svc.context.warntype.speciesidx] : (struct permonst *) 0; @@ -541,9 +561,9 @@ restgamestate(NHFILE *nhfp) file option values instead of keeping old save file option values if partial restore fails and we resort to starting a new game */ newgameflags = flags; - if (nhfp->structlevel) - Mread(nhfp->fd, &flags, sizeof flags); + Sfi_flag(nhfp, &flags, "gamestate-flags"); +#ifndef SFCTOOL /* avoid keeping permanent inventory window up to date during restore (setworn() calls update_inventory); attempting to include the cost of unpaid items before shopkeeper's bill is available is a no-no; @@ -570,27 +590,26 @@ restgamestate(NHFILE *nhfp) #ifdef AMII_GRAPHICS amii_setpens(amii_numcolors); /* use colors from save file */ #endif - if (nhfp->structlevel) - Mread(nhfp->fd, &u, sizeof u); +#endif /* !SFCTOOL */ + Sfi_you(nhfp, &u, "gamestate-you"); gy.youmonst.cham = u.mcham; +#ifndef SFCTOOL if (restoring_special && iflags.explore_error_flag) { /* savefile has wizard or explore mode, but player is no longer authorized to access either; can't downgrade mode any further, so fail restoration. */ - u.uhp = 0; + u.uhp = 0; } +#endif - if (nhfp->structlevel) - Mread(nhfp->fd, timebuf, 14); + Sfi_char(nhfp, timebuf, "gamestate-ubirthday", 14); timebuf[14] = '\0'; ubirthday = time_from_yyyymmddhhmmss(timebuf); - if (nhfp->structlevel) - Mread(nhfp->fd, &urealtime.realtime, sizeof urealtime.realtime); - - if (nhfp->structlevel) - Mread(nhfp->fd, timebuf, 14); + Sfi_long(nhfp, &urealtime.realtime, "gamestate-realtime"); + Sfi_char(nhfp, timebuf, "gamestate-start_timing", 14); timebuf[14] = '\0'; +#ifndef SFCTOOL urealtime.start_timing = time_from_yyyymmddhhmmss(timebuf); /* current time is the time to use for next urealtime.realtime update */ @@ -619,6 +638,7 @@ restgamestate(NHFILE *nhfp) } /* in case hangup save occurred in midst of level change */ assign_level(&u.uz0, &u.uz); +#endif /* !SFCTOOL */ /* this stuff comes after potential aborted restore attempts */ restore_killers(nhfp); @@ -629,6 +649,7 @@ restgamestate(NHFILE *nhfp) /* restore dangling (not on floor or in inventory) ball and/or chain */ bc_obj = restobjchn(nhfp, FALSE); +#ifndef SFCTOOL while (bc_obj) { struct obj *nobj = bc_obj->nobj; @@ -637,13 +658,15 @@ restgamestate(NHFILE *nhfp) setworn(bc_obj, bc_obj->owornmask); bc_obj = nobj; } +#endif gm.migrating_objs = restobjchn(nhfp, FALSE); gm.migrating_mons = restmonchn(nhfp); - if (nhfp->structlevel) { - Mread(nhfp->fd, &svm.mvitals[0], sizeof svm.mvitals); + for (i = 0; i < NUMMONS; ++i) { + Sfi_mvitals(nhfp, &svm.mvitals[i], "gamestate-mvitals"); } +#ifndef SFCTOOL /* * There are some things after this that can have unintended display * side-effects too early in the game. @@ -665,22 +688,23 @@ restgamestate(NHFILE *nhfp) setuwep(otmp); /* (don't need any null check here) */ if (!uwep || uwep->otyp == PICK_AXE || uwep->otyp == GRAPPLING_HOOK) gu.unweapon = TRUE; +#endif /* !SFCTOOL */ restore_dungeon(nhfp); restlevchn(nhfp); - if (nhfp->structlevel) { - Mread(nhfp->fd, &svm.moves, sizeof svm.moves); - /* hero_seq isn't saved and restored because it can be recalculated */ - gh.hero_seq = svm.moves << 3; /* normally handled in moveloop() */ - Mread(nhfp->fd, &svq.quest_status, sizeof svq.quest_status); - Mread(nhfp->fd, svs.spl_book, (MAXSPELL + 1) * sizeof (struct spell)); + Sfi_long(nhfp, &svm.moves, "gamestate-moves"); + /* hero_seq isn't saved and restored because it can be recalculated */ + gh.hero_seq = svm.moves << 3; /* normally handled in moveloop() */ + Sfi_q_score(nhfp, &svq.quest_status, "gamestate-quest_status"); + + for (i = 0; i < (MAXSPELL + 1); ++i) { + Sfi_spell(nhfp, &svs.spl_book[i], "gamestate-spl_book"); } restore_artifacts(nhfp); restore_oracles(nhfp); - if (nhfp->structlevel) { - Mread(nhfp->fd, svp.pl_character, sizeof svp.pl_character); - Mread(nhfp->fd, svp.pl_fruit, sizeof svp.pl_fruit); - } + Sfi_char(nhfp, svp.pl_character, + "gamestate-pl_character", sizeof svp.pl_character); + Sfi_char(nhfp, svp.pl_fruit, "gamestate-pl_fruit", sizeof svp.pl_fruit); freefruitchn(gf.ffruit); /* clean up fruit(s) made by initoptions() */ gf.ffruit = loadfruitchn(nhfp); @@ -688,15 +712,22 @@ restgamestate(NHFILE *nhfp) restore_msghistory(nhfp); restore_gamelog(nhfp); restore_luadata(nhfp); +#ifndef SFCTOOL /* must come after all mons & objs are restored */ relink_timers(FALSE); relink_light_sources(FALSE); adj_erinys(u.ualign.abuse); /* inventory display is now viable */ iflags.perm_invent = defer_perm_invent; +#else + nhUse(bc_obj); + nhUse(newgamecontext); + nhUse(newgameflags); +#endif /* !SFCTOOL */ return TRUE; } +#ifndef SFCTOOL /* update game state pointers to those valid for the current level (so we don't dereference a wild u.ustuck when saving game state, for instance) */ staticfn void @@ -731,6 +762,21 @@ restlevelfile(xint8 ltmp) return 2; } +/* + * restore_saved_game() prior to this left us at this position in + * the savefile for dorecover(): + * + * format indicator (1 byte) + * n = count of critical size list (1 byte) + * n bytes of critical sizes (n bytes) + * version info + * --> plnametmp = player name size (int, 2 bytes) + * player name (PL_NSIZ_PLUS) + * current level (including pets) + * (non-level-based) game state + * other levels + */ + int dorecover(NHFILE *nhfp) { @@ -740,18 +786,27 @@ dorecover(NHFILE *nhfp) /* suppress map display if some part of the code tries to update that */ program_state.restoring = REST_GSTATE; - get_plname_from_file(nhfp, svp.plname); + get_plname_from_file(nhfp, svp.plname, TRUE); + /* + * The position in the save file is now here: + * + * format indicator (1 byte) + * n = count of critical size list (1 byte) + * n bytes of critical sizes (n bytes) + * version info + * plnametmp = player name size (int, 2 bytes) + * player name (PL_NSIZ_PLUS) + * --> current level (including pets) + * (non-level-based) game state + * other levels + */ getlev(nhfp, 0, (xint8) 0); if (!restgamestate(nhfp)) { - NHFILE tnhfp; + NHFILE *tnhfp = get_freeing_nhfile(); display_nhwindow(WIN_MESSAGE, TRUE); - zero_nhfile(&tnhfp); - tnhfp.mode = FREEING; - tnhfp.fd = -1; - savelev(&tnhfp, 0); /* discard current level */ - /* no need for close_nhfile(&tnhfp), which - is not really affiliated with an open file */ + savelev(tnhfp, 0); /* discard current level */ + close_nhfile(tnhfp); close_nhfile(nhfp); (void) delete_savefile(); u.usteed_mid = u.ustuck_mid = 0; @@ -804,11 +859,9 @@ dorecover(NHFILE *nhfp) #endif restoreinfo.mread_flags = 1; /* return despite error */ while (1) { - if (nhfp->structlevel) { - Mread(nhfp->fd, <mp, sizeof ltmp); - if (restoreinfo.mread_flags == -1) - break; - } + Sfi_xint8(nhfp, <mp, "gamestate-level_number"); + if (nhfp->eof) + break; getlev(nhfp, 0, ltmp); #ifdef MICRO curs(WIN_MAP, 1 + dotcnt++, dotrow); @@ -826,9 +879,10 @@ dorecover(NHFILE *nhfp) return rtmp; /* dorecover called recursively */ } restoreinfo.mread_flags = 0; + rewind_nhfile(nhfp); /* return to beginning of file */ (void) validate(nhfp, (char *) 0, FALSE); - get_plname_from_file(nhfp, svp.plname); + get_plname_from_file(nhfp, svp.plname, TRUE); /* not 0 nor REST_GSTATE nor REST_LEVELS */ program_state.restoring = REST_CURRENT_LEVEL; @@ -887,36 +941,38 @@ dorecover(NHFILE *nhfp) check_special_room(FALSE); return 1; } +#endif /* !SFCTOOL */ staticfn void rest_stairs(NHFILE *nhfp) { int buflen = 0; stairway stway = UNDEFINED_VALUES; +#ifndef SFCTOOL stairway *newst; +#endif +#ifndef SFCTOOL stairway_free_all(); +#endif while (1) { - if (nhfp->structlevel) { - Mread(nhfp->fd, &buflen, sizeof buflen); - } - + Sfi_int(nhfp, &buflen, "stairs-staircount"); if (buflen == -1) break; - if (nhfp->structlevel) { - Mread(nhfp->fd, &stway, sizeof stway); - } + Sfi_stairway(nhfp, &stway, "stairs-stairway"); if (program_state.restoring != REST_GSTATE && stway.tolev.dnum == u.uz.dnum) { /* stairway dlevel is relative, make it absolute */ stway.tolev.dlevel += u.uz.dlevel; } +#ifndef SFCTOOL stairway_add(stway.sx, stway.sy, stway.up, stway.isladder, &(stway.tolev)); newst = stairway_at(stway.sx, stway.sy); if (newst) newst->u_traversed = stway.u_traversed; +#endif } } @@ -926,67 +982,47 @@ restcemetery(NHFILE *nhfp, struct cemetery **cemeteryaddr) struct cemetery *bonesinfo, **bonesaddr; int cflag = 0; - if (nhfp->structlevel) - Mread(nhfp->fd, &cflag, sizeof cflag); + Sfi_int(nhfp, &cflag, "cemetery-cemetery_flag"); if (cflag == 0) { bonesaddr = cemeteryaddr; do { bonesinfo = (struct cemetery *) alloc(sizeof *bonesinfo); - if (nhfp->structlevel) - Mread(nhfp->fd, bonesinfo, sizeof *bonesinfo); + Sfi_cemetery(nhfp, bonesinfo, "cemetery-bonesinfo"); *bonesaddr = bonesinfo; bonesaddr = &(*bonesaddr)->next; } while (*bonesaddr); } else { *cemeteryaddr = 0; } + if (((nhfp->mode & CONVERTING) != 0) + || ((nhfp->mode & UNCONVERTING) != 0)) { + struct cemetery *thisbones, *nextbones; + + /* free the memory */ + nextbones = *cemeteryaddr; + while ((thisbones = nextbones) != 0) { + nextbones = thisbones->next; + free((genericptr_t)thisbones); + } + *cemeteryaddr = 0; + } } /*ARGSUSED*/ staticfn void -rest_levl( - NHFILE *nhfp, -#ifdef RLECOMP - boolean rlecomp -#else - boolean rlecomp UNUSED -#endif -) +rest_levl(NHFILE *nhfp) { -#ifdef RLECOMP - short i, j; - uchar len; - struct rm r; + int c, r; - if (rlecomp) { - (void) memset((genericptr_t) &r, 0, sizeof(r)); - i = 0; - j = 0; - len = 0; - while (i < ROWNO) { - while (j < COLNO) { - if (len > 0) { - levl[j][i] = r; - len -= 1; - j += 1; - } else { - if (nhfp->structlevel) { - Mread(nhfp->fd, &len, sizeof len); - Mread(nhfp->fd, &r, sizeof r); - } - } - } - j = 0; - i += 1; + for (c = 0; c < COLNO; ++c) { + for (r = 0; r < ROWNO; ++r) { + Sfi_rm(nhfp, &levl[c][r], "location-rm"); } - return; - } -#endif /* RLECOMP */ - if (nhfp->structlevel) { - Mread(nhfp->fd, levl, sizeof levl); } } +#ifndef SFCTOOL + void trickery(char *reason) { @@ -996,31 +1032,41 @@ trickery(char *reason) Strcpy(svk.killer.name, reason ? reason : ""); done(TRICKED); } +#endif /* !SFCTOOL */ void getlev(NHFILE *nhfp, int pid, xint8 lev) { struct trap *trap; +#ifndef SFCTOOL struct monst *mtmp; - long elapsed; branch *br; + int x, y; +#endif + long elapsed = 0L; int hpid = 0; xint8 dlvl = 0; - int x, y; + int i, c, r; boolean ghostly = (nhfp->ftype == NHF_BONESFILE); + coord *tmpc = 0; #ifdef TOS short tlev; #endif program_state.in_getlev = TRUE; +#ifndef SFCTOOL if (ghostly) clear_id_mapping(); +#endif /* !SFCTOOL */ +#if 0 #if defined(MSDOS) || defined(OS2) if (nhfp->structlevel) setmode(nhfp->fd, O_BINARY); #endif +#endif + /* Load the old fruit info. We have to do it first, so the * information is available when restoring the objects. */ @@ -1028,18 +1074,10 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) go.oldfruit = loadfruitchn(nhfp); /* First some sanity checks */ - if (nhfp->structlevel) - Mread(nhfp->fd, &hpid, sizeof hpid); - + Sfi_int(nhfp, &hpid, "gamestate-hackpid"); /* CHECK: This may prevent restoration */ -#ifdef TOS - if (nhfp->structlevel) - Mread(nhfp->fd, &tlev, sizeof tlev); - dlvl = tlev & 0x00ff; -#else - if (nhfp->structlevel) - Mread(nhfp->fd, &dlvl, sizeof dlvl); -#endif + Sfi_xint8(nhfp, &dlvl, "gamestate-dlvl"); +#ifndef SFCTOOL if ((pid && pid != hpid) || (lev && dlvl != lev)) { char trickbuf[BUFSZ]; @@ -1052,49 +1090,58 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) pline1(trickbuf); trickery(trickbuf); } +#endif restcemetery(nhfp, &svl.level.bonesinfo); - rest_levl(nhfp, - (boolean) ((sfrestinfo.sfi1 & SFI1_RLECOMP) == SFI1_RLECOMP)); - if (nhfp->structlevel) { - Mread(nhfp->fd, svl.lastseentyp, sizeof svl.lastseentyp); - Mread(nhfp->fd, &go.omoves, sizeof go.omoves); - } - elapsed = svm.moves - go.omoves; + rest_levl(nhfp); - if (nhfp->structlevel) { - rest_stairs(nhfp); - Mread(nhfp->fd, &svu.updest, sizeof svu.updest); - Mread(nhfp->fd, &svd.dndest, sizeof svd.dndest); - Mread(nhfp->fd, &svl.level.flags, sizeof svl.level.flags); - if (svd.doors) { - free(svd.doors); - svd.doors = 0; - } - Mread(nhfp->fd, &svd.doors_alloc, sizeof svd.doors_alloc); - if (svd.doors_alloc) { /* avoid pointless alloc(0) */ - svd.doors = (coord *) alloc(svd.doors_alloc * sizeof (coord)); - Mread(nhfp->fd, svd.doors, svd.doors_alloc * sizeof (coord)); + 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); + + rest_stairs(nhfp); + 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"); + + if (svd.doors) { + free(svd.doors); + svd.doors = 0; + } + + Sfi_int(nhfp, &svd.doors_alloc, "lev-doors_alloc"); + if (svd.doors_alloc) { /* avoid pointless alloc(0) */ + svd.doors = (coord *) alloc(svd.doors_alloc * sizeof (coord)); + tmpc = svd.doors; + for (i = 0; i < svd.doors_alloc; ++i) { + Sfi_nhcoord(nhfp, tmpc, "lev-doors"); + tmpc++; } } rest_rooms(nhfp); /* No joke :-) */ - if (svn.nroom) + if (svn.nroom) { +#ifndef SFCTOOL gd.doorindex = svr.rooms[svn.nroom - 1].fdoor - + svr.rooms[svn.nroom - 1].doorct; - else + + svr.rooms[svn.nroom - 1].doorct; + } else { gd.doorindex = 0; +#else + gd.doorindex = 0; +#endif /* !SFCTOOL */ + } restore_timers(nhfp, RANGE_LEVEL, elapsed); restore_light_sources(nhfp); fmon = restmonchn(nhfp); - - /* rest_worm(fd); */ /* restore worm information */ rest_worm(nhfp); /* restore worm information */ gf.ftrap = 0; for (;;) { trap = newtrap(); - if (nhfp->structlevel) - Mread(nhfp->fd, trap, sizeof *trap); + Sfi_trap(nhfp, trap, "trap"); if (trap->tx != 0) { if (program_state.restoring != REST_GSTATE && trap->dst.dnum == u.uz.dnum) { @@ -1109,13 +1156,16 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) dealloc_trap(trap); fobj = restobjchn(nhfp, FALSE); +#ifndef SFCTOOL find_lev_obj(); +#endif /* !SFCTOOL */ /* restobjchn()'s `frozen' argument probably ought to be a callback routine so that we can check for objects being buried under ice */ svl.level.buriedobjlist = restobjchn(nhfp, FALSE); gb.billobjs = restobjchn(nhfp, FALSE); rest_engravings(nhfp); +#ifndef SFCTOOL /* reset level.monsters for new level */ for (x = 0; x < COLNO; x++) for (y = 0; y < ROWNO; y++) @@ -1161,12 +1211,15 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) if (ghostly || (elapsed > 00 && elapsed > (long) rnd(10))) hide_monst(mtmp); } +#endif /* !SFCTOOL */ + restdamage(nhfp); rest_regions(nhfp); rest_bubbles(nhfp); /* for water and air; empty marker on other levels */ load_exclusions(nhfp); rest_track(nhfp); +#ifndef SFCTOOL if (ghostly) { stairway *stway = gs.stairs; while (stway) { @@ -1234,7 +1287,6 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) } } } - /* must come after all mons & objs are restored */ relink_timers(ghostly); relink_light_sources(ghostly); @@ -1243,22 +1295,47 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) if (ghostly) clear_id_mapping(); program_state.in_getlev = FALSE; +#else + nhUse(pid); + nhUse(lev); +#endif /* !SFCTOOL */ + program_state.in_getlev = FALSE; } +/* "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 -get_plname_from_file(NHFILE *nhfp, char *plbuf) +get_plname_from_file( + NHFILE *nhfp, + char *outbuf, /* size must be at least [PL_NSIZ_PLUS] even if name_only */ + boolean name_only) /* True: just name; False: name-role-race-gend-algn */ { + char plbuf[PL_NSIZ_PLUS]; int pltmpsiz = 0; - if (nhfp->structlevel) { - (void) read(nhfp->fd, (genericptr_t) &pltmpsiz, sizeof(pltmpsiz)); - (void) read(nhfp->fd, (genericptr_t) plbuf, pltmpsiz); - } + plbuf[0] = '\0'; + + Sfi_int(nhfp, &pltmpsiz, "plname-size"); + /* pltmpsiz should now be PL_NSIZ_PLUS */ + Sfi_char(nhfp, plbuf, "plname", pltmpsiz); + /* plbuf[PL_NSIZ_PLUS-2] should be '\0'; + plbuf[PL_NSIZ_PLUS-1] should be '-' or 'X' or 'D' */ + /* "-race-role-gend-algn" is already present except that it has been + hidden by replacing the initial dash with NUL; if we want that + information, replace the NUL with a dash */ + if (!name_only) + *eos(plbuf) = '-'; + /* not simple strcpy(); playmode is in the last slot and could (probably + will) be preceded by NULs */ + (void) memcpy((genericptr_t) outbuf, (genericptr_t) plbuf, PL_NSIZ_PLUS); return; } /* restore Plane of Water's air bubbles and Plane of Air's clouds */ -staticfn void +#ifndef SFCTOOL +staticfn +#endif +void rest_bubbles(NHFILE *nhfp) { xint8 bbubbly; @@ -1267,60 +1344,74 @@ rest_bubbles(NHFILE *nhfp) clouds are present is recorded during save so that we don't have to know what level is being restored */ bbubbly = 0; + Sfi_xint8(nhfp, &bbubbly, "bubbles-bbubbly"); +#if 0 if (nhfp->structlevel) - Mread(nhfp->fd, &bbubbly, sizeof bbubbly); - + xxread(nhfp->fd, &bbubbly, sizeof bbubbly); +#endif if (bbubbly) restore_waterlevel(nhfp); } -staticfn void +#ifndef SFCTOOL +staticfn +#endif +void restore_gamelog(NHFILE *nhfp) { int slen = 0; char msg[BUFSZ*2]; - struct gamelog_line tmp; + struct gamelog_line tmp = { 0 }; while (1) { - if (nhfp->structlevel) - Mread(nhfp->fd, &slen, sizeof slen); + Sfi_int(nhfp, &slen, "gamelog-length"); if (slen == -1) break; if (slen > ((BUFSZ*2) - 1)) panic("restore_gamelog: msg too big (%d)", slen); - if (nhfp->structlevel) { - Mread(nhfp->fd, msg, slen); - Mread(nhfp->fd, &tmp, sizeof tmp); - msg[slen] = '\0'; - gamelog_add(tmp.flags, tmp.turn, msg); - } + Sfi_char(nhfp, msg, "gamelog-gamelog_text", slen); + msg[slen] = '\0'; + Sfi_gamelog_line(nhfp, &tmp, "gamelog-gamelog_line"); +#ifndef SFCTOOL + gamelog_add(tmp.flags, tmp.turn, msg); +#endif /* !SFCTOOL */ } } -staticfn void +#ifndef SFCTOOL +staticfn +#endif +void restore_msghistory(NHFILE *nhfp) { - int msgsize = 0, msgcount = 0; + int msgsize = 0; +#ifndef SFCTOOL + int msgcount = 0; +#endif char msg[BUFSZ]; while (1) { - if (nhfp->structlevel) - Mread(nhfp->fd, &msgsize, sizeof msgsize); + Sfi_int(nhfp, &msgsize, "msghistory-length"); if (msgsize == -1) break; if (msgsize > BUFSZ - 1) panic("restore_msghistory: msg too big (%d)", msgsize); - if (nhfp->structlevel) - Mread(nhfp->fd, msg, msgsize); + Sfi_char(nhfp, msg, "msghistory-msg", msgsize); msg[msgsize] = '\0'; +#ifndef SFCTOOL putmsghistory(msg, TRUE); ++msgcount; +#endif /* !SFCTOOL */ } +#ifndef SFCTOOL if (msgcount) putmsghistory((char *) 0, TRUE); debugpline1("Read %d messages from savefile.", msgcount); +#endif /* !SFCTOOL */ } +#ifndef SFCTOOL + /* Clear all structures for object and monster ID mapping. */ staticfn void clear_id_mapping(void) @@ -1418,7 +1509,8 @@ restore_menu( { winid tmpwin; anything any; - char **saved; + char **saved, *next, mode, menutext[BUFSZ]; + boolean all_normal; menu_item *chosen_game = (menu_item *) 0; int k, clet, ch = 0; /* ch: 0 => new game */ int clr = NO_COLOR; @@ -1438,19 +1530,37 @@ restore_menu( add_menu_str(tmpwin, ""); } add_menu_str(tmpwin, "Select one of your saved games"); + /* if all the save files have a playmode of '-' then we'll just list + their character name-role-race-gend-algn values, but if any are + 'X' or 'D', we'll list playmode along with name-role-&c values + for every entry; first, figure out if they're all normal play */ + for (all_normal = TRUE, k = 0; all_normal && saved[k]; ++k) { + next = saved[k]; + mode = next[PL_NSIZ_PLUS - 1]; /* fixed last char, beyond '\0' */ + if (mode != '-') + all_normal = FALSE; + } for (k = 0; saved[k]; ++k) { any.a_int = k + 1; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, saved[k], MENU_ITEMFLAGS_NONE); + next = saved[k]; + mode = next[PL_NSIZ_PLUS - 1]; + if (all_normal) + Sprintf(menutext, "%.*s", PL_NSIZ_PLUS - 1, next); + else + Sprintf(menutext, "%c %.*s", mode, PL_NSIZ_PLUS - 1, next); + add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, + menutext, MENU_ITEMFLAGS_SKIPMENUCOLORS); } - clet = (k <= 'n' - 'a') ? 'n' : 0; /* new game */ + clet = (k <= 'n' - 'a') ? 'n' /* new game */ + : (k <= 26 + 'N' - 'A') ? 'N' : 0; any.a_int = -1; /* not >= 0 */ - add_menu(tmpwin, &nul_glyphinfo, &any, clet, 0, ATR_NONE, - clr, "Start a new character", MENU_ITEMFLAGS_NONE); - clet = (k + 1 <= 'q' - 'a') ? 'q' : 0; /* quit */ + add_menu(tmpwin, &nul_glyphinfo, &any, clet, 'N', ATR_NONE, clr, + "Start a new character", MENU_ITEMFLAGS_NONE); + clet = (k + 1 <= 'q' - 'a' && clet == 'n') ? 'q' /* quit */ + : (k + 1 <= 26 + 'Q' - 'A' && clet == 'N') ? 'Q' : 0; any.a_int = -2; - add_menu(tmpwin, &nul_glyphinfo, &any, clet, 0, ATR_NONE, - clr, "Never mind (quit)", MENU_ITEMFLAGS_SELECTED); + add_menu(tmpwin, &nul_glyphinfo, &any, clet, 'Q', ATR_NONE, clr, + "Never mind (quit)", MENU_ITEMFLAGS_SELECTED); /* no prompt on end_menu, as we've done our own at the top */ end_menu(tmpwin, (char *) 0); if (select_menu(tmpwin, PICK_ONE, &chosen_game) > 0) { @@ -1477,43 +1587,6 @@ restore_menu( return (ch > 0) ? 1 : ch; } #endif /* SELECTSAVED */ - -int -validate(NHFILE *nhfp, const char *name, boolean without_waitsynch_perfile) -{ - readLenType rlen = 0; - struct savefile_info sfi; - unsigned long utdflags = 0L; - boolean verbose = name ? TRUE : FALSE, reslt = FALSE; - - if (nhfp->structlevel) - utdflags |= UTD_CHECKSIZES; - if (without_waitsynch_perfile) - utdflags |= UTD_WITHOUT_WAITSYNCH_PERFILE; - if (!(reslt = uptodate(nhfp, name, utdflags))) - return 1; - - if ((nhfp->mode & WRITING) == 0) { - if (nhfp->structlevel) - rlen = (readLenType) read(nhfp->fd, (genericptr_t) &sfi, - sizeof sfi); - } else { - if (nhfp->structlevel) - rlen = (readLenType) read(nhfp->fd, (genericptr_t) &sfi, - sizeof sfi); - minit(); /* ZEROCOMP */ - if (rlen == 0) { - if (verbose) { - pline("File \"%s\" is empty during save file feature check?", - name); - wait_synch(); - } - return -1; - } - } - return 0; -} - -#undef Mread +#endif /* !SFCTOOL */ /*restore.c*/ diff --git a/src/role.c b/src/role.c index 7055ff5b7..e95d3f121 100644 --- a/src/role.c +++ b/src/role.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 role.c $NHDT-Date: 1711734229 2024/03/29 17:43:49 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.100 $ */ +/* NetHack 3.7 role.c $NHDT-Date: 1737607158 2025/01/22 20:39:18 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.107 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985-1999. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -24,6 +24,9 @@ * * God names use a leading underscore to flag goddesses. */ + +/* NUM_ROLES is defined in hack.h */ + const struct Role roles[NUM_ROLES+1] = { { { "Archeologist", 0 }, { { "Digger", 0 }, @@ -573,7 +576,9 @@ const struct Role roles[NUM_ROLES+1] = { }; /* Table of all races */ -const struct Race races[] = { + +/* NUM_RACES is defined in hack.h */ +const struct Race races[NUM_RACES + 1] = { { "human", "human", @@ -1357,6 +1362,7 @@ clearrolefilter(int which) switch (which) { case RS_filter: gr.rfilter.mask = 0; /* clear race, gender, and alignment filters */ + FALLTHROUGH; /*FALLTHRU*/ case RS_ROLE: for (i = 0; i < SIZE(roles) - 1; ++i) @@ -1561,7 +1567,7 @@ root_plselection_prompt( if (donefirst) Strcat(buf, " "); Strcat(buf, "character"); - donefirst = TRUE; + /*donefirst = TRUE;*/ } /* || * || @@ -2175,7 +2181,7 @@ genl_player_selection(void) /*NOTREACHED*/ } -#if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS) +#if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS) || defined(SHIM_GRAPHICS) /* ['#else' far below] */ staticfn boolean reset_role_filtering(void); diff --git a/src/rumors.c b/src/rumors.c index 11545fb6d..5aac3bd40 100644 --- a/src/rumors.c +++ b/src/rumors.c @@ -41,6 +41,7 @@ * and placed there by 'makedefs'. */ +#ifndef SFCTOOL staticfn void unpadline(char *); staticfn void init_rumors(dlb *); staticfn char *get_rnd_line(dlb *, char *, unsigned, int (*)(int), @@ -563,6 +564,7 @@ outrumor( return; case BY_COOKIE: pline(fortune_msg); + FALLTHROUGH; /* FALLTHRU */ case BY_PAPER: pline("It reads:"); @@ -583,10 +585,10 @@ init_oracles(dlb *fp) (void) dlb_fgets(line, sizeof line, fp); if (sscanf(line, "%5d\n", &cnt) == 1 && cnt > 0) { svo.oracle_cnt = (unsigned) cnt; - go.oracle_loc = (unsigned long *) alloc((unsigned) cnt * sizeof(long)); + svo.oracle_loc = (unsigned long *) alloc((unsigned) cnt * sizeof(long)); for (i = 0; i < cnt; i++) { (void) dlb_fgets(line, sizeof line, fp); - (void) sscanf(line, "%5lx\n", &go.oracle_loc[i]); + (void) sscanf(line, "%5lx\n", &svo.oracle_loc[i]); } } return; @@ -595,41 +597,45 @@ init_oracles(dlb *fp) void save_oracles(NHFILE *nhfp) { - if (perform_bwrite(nhfp)) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &svo.oracle_cnt, - sizeof svo.oracle_cnt); - if (svo.oracle_cnt) { - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) go.oracle_loc, - svo.oracle_cnt * sizeof (long)); - } + int i; + + if (update_file(nhfp)) { + Sfo_unsigned(nhfp, &svo.oracle_cnt, "oracle-oracle_cnt"); + if (svo.oracle_cnt) { + for (i = 0; (unsigned) i < svo.oracle_cnt; ++i) { + Sfo_ulong(nhfp, &svo.oracle_loc[i], "oracle-oracle_loc"); } + } } if (release_data(nhfp)) { if (svo.oracle_cnt) { - free((genericptr_t) go.oracle_loc); - go.oracle_loc = 0, svo.oracle_cnt = 0, go.oracle_flg = 0; + svo.oracle_cnt = 0, go.oracle_flg = 0; + } + if (svo.oracle_loc) { + free((genericptr_t) svo.oracle_loc); + svo.oracle_loc = 0; } } } +#endif /* !SFCTOOL */ void restore_oracles(NHFILE *nhfp) { - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) &svo.oracle_cnt, sizeof svo.oracle_cnt); + int i; + Sfi_unsigned(nhfp, &svo.oracle_cnt, "oracle-oracle_cnt"); if (svo.oracle_cnt) { - go.oracle_loc = (unsigned long *) alloc(svo.oracle_cnt * sizeof(long)); - if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) go.oracle_loc, - svo.oracle_cnt * sizeof (long)); + svo.oracle_loc = + (unsigned long *) alloc(svo.oracle_cnt * sizeof (unsigned long)); + for (i = 0; (unsigned) i < svo.oracle_cnt; ++i) { + Sfi_ulong(nhfp, &svo.oracle_loc[i], "oracle-oracle_loc"); } go.oracle_flg = 1; /* no need to call init_oracles() */ } } +#ifndef SFCTOOL void outoracle(boolean special, boolean delphi) { @@ -657,9 +663,9 @@ outoracle(boolean special, boolean delphi) if (svo.oracle_cnt <= 1 && !special) goto close_oracles; /*(shouldn't happen)*/ oracle_idx = special ? 0 : rnd((int) svo.oracle_cnt - 1); - (void) dlb_fseek(oracles, (long) go.oracle_loc[oracle_idx], SEEK_SET); + (void) dlb_fseek(oracles, (long) svo.oracle_loc[oracle_idx], SEEK_SET); if (!special) /* move offset of very last one into this slot */ - go.oracle_loc[oracle_idx] = go.oracle_loc[--svo.oracle_cnt]; + svo.oracle_loc[oracle_idx] = svo.oracle_loc[--svo.oracle_cnt]; tmpwin = create_nhwindow(NHW_TEXT); if (delphi) @@ -945,5 +951,6 @@ free_CapMons(void) } CapMonSiz = 0; } +#endif /* !SFCTOOL */ /*rumors.c*/ diff --git a/src/save.c b/src/save.c index 0f194ba34..57c8bfb41 100644 --- a/src/save.c +++ b/src/save.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 save.c $NHDT-Date: 1706079844 2024/01/24 07:04:04 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.214 $ */ +/* NetHack 3.7 save.c $NHDT-Date: 1737610109 2025/01/22 21:28:29 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.232 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2009. */ /* NetHack may be freely redistributed. See license for details. */ @@ -17,7 +17,7 @@ int dotcnt, dotrow; /* also used in restore */ #endif staticfn void savelevchn(NHFILE *); -staticfn void savelevl(NHFILE *,boolean); +staticfn void savelevl(NHFILE *); staticfn void savedamage(NHFILE *); staticfn void save_bubbles(NHFILE *, xint8); staticfn void save_stairs(NHFILE *); @@ -32,14 +32,6 @@ staticfn void savegamestate(NHFILE *); staticfn void savelev_core(NHFILE *, xint8); staticfn void save_msghistory(NHFILE *); -#ifdef ZEROCOMP -staticfn void zerocomp_bufon(int); -staticfn void zerocomp_bufoff(int); -staticfn void zerocomp_bflush(int); -staticfn void zerocomp_bwrite(int, genericptr_t, unsigned int); -staticfn void zerocomp_bputc(int); -#endif - #if defined(HANGUPHANDLING) #define HUP if (!program_state.done_hup) #else @@ -62,6 +54,7 @@ dosave(void) program_state.done_hup = 0; #endif if (dosave0()) { + program_state.savefile_completed++; u.uhp = -1; /* universal game's over indicator */ if (soundprocs.sound_exit_nhsound) (*soundprocs.sound_exit_nhsound)("dosave"); @@ -123,6 +116,7 @@ dosave0(void) clear_nhwindow(WIN_MESSAGE); There("seems to be an old save file."); if (y_n("Overwrite the old file?") == 'n') { + nh_sfconvert(fq_save); nh_compress(fq_save); goto done; } @@ -137,6 +131,9 @@ dosave0(void) (void) delete_savefile(); /* ab@unido */ goto done; } + if (nhfp && nhfp->fplog) { + nhfp->rcount = nhfp->wcount = 0L; + } vision_recalc(2); /* shut down vision to prevent problems in the event of an impossible() call */ @@ -158,9 +155,6 @@ dosave0(void) #endif nhfp->mode = WRITING | FREEING; store_version(nhfp); - store_savefileinfo(nhfp); - if (nhfp && nhfp->fplog) - (void) fprintf(nhfp->fplog, "# post-validation\n"); store_plname_in_file(nhfp); /* savelev() might save uball and uchain, releasing their memory if FREEING, so we need to check their status now; if hero is swallowed, @@ -213,11 +207,9 @@ dosave0(void) HUP done(TRICKED); goto done; } - minit(); /* ZEROCOMP */ getlev(onhfp, svh.hackpid, ltmp); close_nhfile(onhfp); - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) <mp, sizeof ltmp); /* lvl no. */ + Sfo_xint8(nhfp, <mp, "gamestate-level_number"); savelev(nhfp, ltmp); /* actual level*/ delete_levelfile(ltmp); } @@ -229,6 +221,7 @@ dosave0(void) /* get rid of current level --jgm */ delete_levelfile(ledger_no(&u.uz)); delete_levelfile(0); + nh_sfconvert(fq_save); nh_compress(fq_save); /* this should probably come sooner... */ program_state.something_worth_saving = 0; @@ -248,53 +241,44 @@ save_gamelog(NHFILE *nhfp) while (tmp) { tmp2 = tmp->next; - if (perform_bwrite(nhfp)) { - if (nhfp->structlevel) { - slen = Strlen(tmp->text); - bwrite(nhfp->fd, (genericptr_t) &slen, sizeof slen); - bwrite(nhfp->fd, (genericptr_t) tmp->text, slen); - bwrite(nhfp->fd, (genericptr_t) tmp, - sizeof (struct gamelog_line)); - } + if (nhfp->mode & (COUNTING | WRITING)) { + slen = Strlen(tmp->text); + Sfo_int(nhfp, &slen, "gamelog-length"); + Sfo_char(nhfp, tmp->text, "gamelog-gamelog_text", slen); + Sfo_gamelog_line(nhfp, tmp, "gamelog-gamelog_line"); } - if (release_data(nhfp)) { + if (nhfp->mode & FREEING) { free((genericptr_t) tmp->text); free((genericptr_t) tmp); } tmp = tmp2; } - if (perform_bwrite(nhfp)) { - if (nhfp->structlevel) { - slen = -1; - bwrite(nhfp->fd, (genericptr_t) &slen, sizeof slen); - } + if (nhfp->mode & (COUNTING | WRITING)) { + slen = -1; + Sfo_int(nhfp, &slen, "gamelog-length"); } - if (release_data(nhfp)) + if (nhfp->mode & FREEING) gg.gamelog = NULL; } staticfn void savegamestate(NHFILE *nhfp) { + int i; unsigned long uid; program_state.saving++; /* caller should/did already set this... */ uid = (unsigned long) getuid(); - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &uid, sizeof uid); - bwrite(nhfp->fd, (genericptr_t) &svc.context, sizeof svc.context); - bwrite(nhfp->fd, (genericptr_t) &flags, sizeof flags); - } + Sfo_ulong(nhfp, &uid, "gamestate-uid"); + Sfo_context_info(nhfp, &svc.context, "gamestate-context"); + Sfo_flag(nhfp, &flags, "gamestate-flags"); urealtime.finish_time = getnow(); urealtime.realtime += timet_delta(urealtime.finish_time, urealtime.start_timing); - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &u, sizeof u); - bwrite(nhfp->fd, yyyymmddhhmmss(ubirthday), 14); - bwrite(nhfp->fd, (genericptr_t) &urealtime.realtime, - sizeof urealtime.realtime); - bwrite(nhfp->fd, yyyymmddhhmmss(urealtime.start_timing), 14); - } + Sfo_you(nhfp, &u, "gamestate-you"); + Sfo_char(nhfp, yyyymmddhhmmss(ubirthday), "gamestate-ubirthday", 14); + Sfo_long(nhfp, &urealtime.realtime, "gamestate-realtime"); + Sfo_char(nhfp, yyyymmddhhmmss(urealtime.start_timing), "gamestate-start_timing", 14); /* this is the value to use for the next update of urealtime.realtime */ urealtime.start_timing = urealtime.finish_time; save_killers(nhfp); @@ -314,25 +298,21 @@ savegamestate(NHFILE *nhfp) savemonchn(nhfp, gm.migrating_mons); if (release_data(nhfp)) gm.migrating_mons = (struct monst *) 0; - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) svm.mvitals, sizeof svm.mvitals); - save_dungeon(nhfp, (boolean) !!perform_bwrite(nhfp), + for (i = 0; i < NUMMONS; ++i) { + Sfo_mvitals(nhfp, &svm.mvitals[i], "gamestate-mvitals"); + } + save_dungeon(nhfp, (boolean) !!update_file(nhfp), (boolean) !!release_data(nhfp)); savelevchn(nhfp); - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &svm.moves, sizeof svm.moves); - bwrite(nhfp->fd, (genericptr_t) &svq.quest_status, - sizeof svq.quest_status); - bwrite(nhfp->fd, (genericptr_t) svs.spl_book, - sizeof (struct spell) * (MAXSPELL + 1)); + Sfo_long(nhfp, &svm.moves, "gamestate-moves"); + Sfo_q_score(nhfp, &svq.quest_status, "gamestate-quest_status"); + for (i = 0; i < (MAXSPELL + 1); ++i) { + Sfo_spell(nhfp, &svs.spl_book[i], "gamestate-spl_book"); } save_artifacts(nhfp); save_oracles(nhfp); - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) svp.pl_character, - sizeof svp.pl_character); - bwrite(nhfp->fd, (genericptr_t) svp.pl_fruit, sizeof svp.pl_fruit); - } + Sfo_char(nhfp, svp.pl_character, "gamestate-pl_character", sizeof svp.pl_character); + Sfo_char(nhfp, svp.pl_fruit, "gamestate-pl_fruit", sizeof svp.pl_fruit); savefruitchn(nhfp); savenames(nhfp); save_msghistory(nhfp); @@ -391,8 +371,7 @@ savestateinlock(void) return; } - if (nhfp->structlevel) - (void) read(nhfp->fd, (genericptr_t) &hpid, sizeof hpid); + Sfi_int(nhfp, &hpid, "gamestate-hackpid"); if (svh.hackpid != hpid) { Sprintf(whynot, "Level #0 pid (%d) doesn't match ours (%d)!", hpid, svh.hackpid); @@ -415,18 +394,13 @@ savestateinlock(void) return; } nhfp->mode = WRITING; - if (nhfp->structlevel) - (void) write(nhfp->fd, (genericptr_t) &svh.hackpid, - sizeof svh.hackpid); + Sfo_int(nhfp, &svh.hackpid, "gamestate-hackpid"); if (flags.ins_chkpt) { int currlev = ledger_no(&u.uz); - if (nhfp->structlevel) - (void) write(nhfp->fd, (genericptr_t) &currlev, - sizeof currlev); + Sfo_int(nhfp, &currlev, "gamestate-savestateinlock"); save_savefile_name(nhfp); store_version(nhfp); - store_savefileinfo(nhfp); store_plname_in_file(nhfp); /* if ball and/or chain aren't on floor or in invent, keep a copy @@ -452,7 +426,7 @@ savelev(NHFILE *nhfp, xint8 lev) if not, we need to set it for save_bubbles(); caveat: if the player quits during character selection, u.uz won't be set yet but we'll be called during run-down */ - if (set_uz_save && perform_bwrite(nhfp)) { + if (set_uz_save && (nhfp->mode & (COUNTING | WRITING))) { if (u.uz.dnum == 0 && u.uz.dlevel == 0) { program_state.something_worth_saving = 0; panic("savelev: where are we?"); @@ -472,6 +446,8 @@ savelev_core(NHFILE *nhfp, xint8 lev) #ifdef TOS short tlev; #endif + int i, c, r; + coord *tmpc; program_state.saving++; /* even if current mode is FREEING */ @@ -502,20 +478,14 @@ savelev_core(NHFILE *nhfp, xint8 lev) create statue trap then immediately level teleport) */ if (iflags.purge_monsters) dmonsfree(); + /* clear objs_deleted list too */ + if (go.objs_deleted) + dobjsfree(); /* really free deleted objects */ if (lev >= 0 && lev <= maxledgerno()) svl.level_info[lev].flags |= VISITED; - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &svh.hackpid, sizeof svh.hackpid); -#ifdef TOS - tlev = lev; - tlev &= 0x00ff; - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &tlev, sizeof tlev); -#else - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &lev, sizeof lev); -#endif + Sfo_int(nhfp, &svh.hackpid, "gamestate-hackpid"); + Sfo_xint8(nhfp, &lev, "gamestate-dlvl"); } /* bones info comes before level data; the intent is for an external @@ -528,23 +498,28 @@ savelev_core(NHFILE *nhfp, xint8 lev) if (nhfp->mode == FREEING) /* see above */ goto skip_lots; - savelevl(nhfp, ((sfsaveinfo.sfi1 & SFI1_RLECOMP) == SFI1_RLECOMP)); - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) svl.lastseentyp, - sizeof svl.lastseentyp); - bwrite(nhfp->fd, (genericptr_t) &svm.moves, sizeof svm.moves); - save_stairs(nhfp); - bwrite(nhfp->fd, (genericptr_t) &svu.updest, sizeof (dest_area)); - bwrite(nhfp->fd, (genericptr_t) &svd.dndest, sizeof (dest_area)); - bwrite(nhfp->fd, (genericptr_t) &svl.level.flags, - sizeof svl.level.flags); - bwrite(nhfp->fd, (genericptr_t) &svd.doors_alloc, - sizeof svd.doors_alloc); - /* don't rely on underlying write() behavior to write - * nothing if count arg is 0, just skip it */ - if (svd.doors_alloc) - bwrite(nhfp->fd, (genericptr_t) svd.doors, - svd.doors_alloc * sizeof (coord)); + savelevl(nhfp); + for (c = 0; c < COLNO; ++c) { + for (r = 0; r < ROWNO; ++r) { + Sfo_schar(nhfp, &svl.lastseentyp[c][r], "lastseentyp"); + } + } + /* svm.moves will actually be read back into svo.omoves on restore */ + Sfo_long(nhfp, &svm.moves, "lev-timestmp"); + save_stairs(nhfp); + Sfo_dest_area(nhfp, &svu.updest, "lev-updest"); + Sfo_dest_area(nhfp, &svd.dndest, "lev-dndest"); + Sfo_levelflags(nhfp, &svl.level.flags, "lev-level_flags"); + + Sfo_int(nhfp, &svd.doors_alloc, "lev-doors_alloc"); + /* don't rely on underlying write() behavior to write + * nothing if count arg is 0, just skip it */ + if (svd.doors_alloc) { + tmpc = svd.doors; + for (i = 0; i < svd.doors_alloc; ++i) { + Sfo_nhcoord(nhfp, tmpc, "lev-doors"); + tmpc++; + } } save_rooms(nhfp); /* no dynamic memory to reclaim */ @@ -582,61 +557,14 @@ savelev_core(NHFILE *nhfp, xint8 lev) } staticfn void -savelevl(NHFILE *nhfp, boolean rlecomp) +savelevl(NHFILE *nhfp) { -#ifdef RLECOMP - struct rm *prm, *rgrm; int x, y; - uchar match; - - if (rlecomp) { - /* perform run-length encoding of rm structs */ - - rgrm = &levl[0][0]; /* start matching at first rm */ - match = 0; + for (x = 0; x < COLNO; x++) { for (y = 0; y < ROWNO; y++) { - for (x = 0; x < COLNO; x++) { - prm = &levl[x][y]; - if (prm->glyph == rgrm->glyph && prm->typ == rgrm->typ - && prm->seenv == rgrm->seenv - && prm->horizontal == rgrm->horizontal - && prm->flags == rgrm->flags && prm->lit == rgrm->lit - && prm->waslit == rgrm->waslit - && prm->roomno == rgrm->roomno && prm->edge == rgrm->edge - && prm->candig == rgrm->candig) { - match++; - if (match > 254) { - match = 254; /* undo this match */ - goto writeout; - } - } else { - /* run has been broken, write out run-length encoding */ - writeout: - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &match, sizeof match); - bwrite(nhfp->fd, (genericptr_t) rgrm, sizeof *rgrm); - } - /* start encoding again. we have at least 1 rm - in the next run, viz. this one. */ - match = 1; - rgrm = prm; - } - } + Sfo_rm(nhfp, &levl[x][y], "location-rm"); } - if (match > 0) { - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &match, sizeof (uchar)); - bwrite(nhfp->fd, (genericptr_t) rgrm, sizeof (struct rm)); - } - } - return; - } -#else /* !RLECOMP */ - nhUse(rlecomp); -#endif /* ?RLECOMP */ - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) levl, sizeof levl); } return; } @@ -656,8 +584,12 @@ save_bubbles(NHFILE *nhfp, xint8 lev) bbubbly = 0; if (lev == ledger_no(&water_level) || lev == ledger_no(&air_level)) bbubbly = lev; /* non-zero */ + if (update_file(nhfp)) + Sfo_xint8(nhfp, &bbubbly, "bubbles-bbubbly"); +#if 0 if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &bbubbly, sizeof bbubbly); + xxwrite(nhfp->fd, (genericptr_t) &bbubbly, sizeof bbubbly); +#endif if (bbubbly) save_waterlevel(nhfp); /* save air bubbles or clouds */ @@ -671,16 +603,14 @@ savecemetery(NHFILE *nhfp, struct cemetery **cemeteryaddr) int flag; flag = *cemeteryaddr ? 0 : -1; - if (perform_bwrite(nhfp)) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &flag, sizeof flag); + if (update_file(nhfp)) { + Sfo_int(nhfp, &flag, "cemetery-cemetery_flag"); } nextbones = *cemeteryaddr; while ((thisbones = nextbones) != 0) { nextbones = thisbones->next; - if (perform_bwrite(nhfp)) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) thisbones, sizeof *thisbones); + if (update_file(nhfp)) { + Sfo_cemetery(nhfp, thisbones, "cemetery-bonesinfo"); } if (release_data(nhfp)) free((genericptr_t) thisbones); @@ -698,14 +628,12 @@ savedamage(NHFILE *nhfp) damageptr = svl.level.damagelist; for (tmp_dam = damageptr; tmp_dam; tmp_dam = tmp_dam->next) xl++; - if (perform_bwrite(nhfp)) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &xl, sizeof xl); + if (update_file(nhfp)) { + Sfo_unsigned(nhfp, &xl, "damage-damage_count"); } - while (xl--) { - if (perform_bwrite(nhfp)) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) damageptr, sizeof *damageptr); + while (damageptr) { + if (update_file(nhfp)) { + Sfo_damage(nhfp, damageptr, "damage"); } tmp_dam = damageptr; damageptr = damageptr->next; @@ -723,17 +651,15 @@ save_stairs(NHFILE *nhfp) int buflen = (int) sizeof *stway; while (stway) { - if (perform_bwrite(nhfp)) { + if (update_file(nhfp)) { boolean use_relative = (program_state.restoring != REST_GSTATE && stway->tolev.dnum == u.uz.dnum); if (use_relative) { /* make dlevel relative to current level */ stway->tolev.dlevel -= u.uz.dlevel; } - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &buflen, sizeof buflen); - bwrite(nhfp->fd, (genericptr_t) stway, sizeof *stway); - } + Sfo_int(nhfp, &buflen, "stairs-staircount"); + Sfo_stairway(nhfp, stway, "stairs-stairway"); if (use_relative) { /* reset stairway dlevel back to absolute */ stway->tolev.dlevel += u.uz.dlevel; @@ -741,11 +667,9 @@ save_stairs(NHFILE *nhfp) } stway = stway->next; } - if (perform_bwrite(nhfp)) { + if (update_file(nhfp)) { buflen = -1; - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &buflen, sizeof buflen); - } + Sfo_int(nhfp, &buflen, "stairs-staircount"); } } @@ -780,46 +704,38 @@ save_bc(NHFILE *nhfp) } /* save one object; - caveat: this is only for perform_bwrite(); caller handles release_data() */ + caveat: this is only for update_file(); caller handles release_data() */ staticfn void saveobj(NHFILE *nhfp, struct obj *otmp) { int buflen, zerobuf = 0; buflen = (int) sizeof (struct obj); - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &buflen, sizeof buflen); - bwrite(nhfp->fd, (genericptr_t) otmp, buflen); - } + Sfo_int(nhfp, &buflen, "obj-obj_length"); + Sfo_obj(nhfp, otmp, "obj"); if (otmp->oextra) { buflen = ONAME(otmp) ? (int) strlen(ONAME(otmp)) + 1 : 0; - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &buflen, sizeof buflen); + Sfo_int(nhfp, &buflen, "obj-oname_length"); if (buflen > 0) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) ONAME(otmp), buflen); + Sfo_char(nhfp, ONAME(otmp), "obj-oname", buflen); } /* defer to savemon() for this one */ if (OMONST(otmp)) { savemon(nhfp, OMONST(otmp)); } else { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &zerobuf, sizeof zerobuf); + Sfo_int(nhfp, &zerobuf, "obj-omonst_length"); } /* extra info about scroll of mail */ buflen = OMAILCMD(otmp) ? (int) strlen(OMAILCMD(otmp)) + 1 : 0; - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &buflen, sizeof buflen); + Sfo_int(nhfp, &buflen, "obj-omailcmd_length"); if (buflen > 0) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) OMAILCMD(otmp), buflen); + Sfo_char(nhfp, OMAILCMD(otmp), "obj-omailcmd", buflen); } /* omid used to be indirect via a pointer in oextra but has become part of oextra itself; 0 means not applicable and gets saved/restored whenever any other oextra components do */ - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &OMID(otmp), sizeof OMID(otmp)); + Sfo_unsigned(nhfp, &OMID(otmp), "obj-omid"); } } @@ -835,7 +751,7 @@ saveobjchn(NHFILE *nhfp, struct obj **obj_p) while (otmp) { otmp2 = otmp->nobj; - if (perform_bwrite(nhfp)) { + if (update_file(nhfp)) { saveobj(nhfp, otmp); } if (Has_contents(otmp)) @@ -873,13 +789,14 @@ saveobjchn(NHFILE *nhfp, struct obj **obj_p) setworn((struct obj *) 0, otmp->owornmask & (W_BALL | W_CHAIN)); otmp->owornmask = 0L; /* no longer care */ + program_state.freeingdata++; dealloc_obj(otmp); + program_state.freeingdata--; } otmp = otmp2; } - if (perform_bwrite(nhfp)) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &minusone, sizeof (int)); + if (update_file(nhfp)) { + Sfo_int(nhfp, &minusone, "obj-obj_length"); } if (release_data(nhfp)) { if (is_invent) @@ -896,58 +813,48 @@ savemon(NHFILE *nhfp, struct monst *mtmp) mtmp->mtemplit = 0; /* normally clear; if set here then a panic save * is being written while bhit() was executing */ buflen = (int) sizeof (struct monst); - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &buflen, sizeof buflen); - bwrite(nhfp->fd, (genericptr_t) mtmp, buflen); - } + Sfo_int(nhfp, &buflen, "monst-monst_length"); + Sfo_monst(nhfp, mtmp, "monst"); if (mtmp->mextra) { buflen = MGIVENNAME(mtmp) ? (int) strlen(MGIVENNAME(mtmp)) + 1 : 0; - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &buflen, sizeof buflen); + Sfo_int(nhfp, &buflen, "monst-mgivenname_length"); if (buflen > 0) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) MGIVENNAME(mtmp), buflen); + Sfo_char(nhfp, MGIVENNAME(mtmp), "monst-mgivenname", buflen); } buflen = EGD(mtmp) ? (int) sizeof (struct egd) : 0; - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &buflen, sizeof buflen); + Sfo_int(nhfp, &buflen, "monst-egd_length"); if (buflen > 0) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) EGD(mtmp), buflen); + Sfo_egd(nhfp, EGD(mtmp), "monst-egd"); } buflen = EPRI(mtmp) ? (int) sizeof (struct epri) : 0; - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &buflen, sizeof buflen); + Sfo_int(nhfp, &buflen, "monst-epri_length"); if (buflen > 0) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) EPRI(mtmp), buflen); + Sfo_epri(nhfp, EPRI(mtmp), "monst-epri"); } buflen = ESHK(mtmp) ? (int) sizeof (struct eshk) : 0; - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &buflen, sizeof (int)); + Sfo_int(nhfp, &buflen, "monst-eshk_length"); if (buflen > 0) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) ESHK(mtmp), buflen); + Sfo_eshk(nhfp, ESHK(mtmp), "monst-eshk"); } buflen = EMIN(mtmp) ? (int) sizeof (struct emin) : 0; - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &buflen, sizeof (int)); + Sfo_int(nhfp, &buflen, "monst-emin_length"); if (buflen > 0) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) EMIN(mtmp), buflen); + Sfo_emin(nhfp, EMIN(mtmp), "monst-emin"); } buflen = EDOG(mtmp) ? (int) sizeof (struct edog) : 0; - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &buflen, sizeof (int)); + Sfo_int(nhfp, &buflen, "monst-edog_length"); if (buflen > 0) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) EDOG(mtmp), buflen); + Sfo_edog(nhfp, EDOG(mtmp), "monst-edog"); + } + buflen = EBONES(mtmp) ? (int) sizeof (struct ebones) : 0; + Sfo_int(nhfp, &buflen, "monst-ebones_length"); + if (buflen > 0) { + Sfo_ebones(nhfp, EBONES(mtmp), "monst-ebones"); } /* mcorpsenm is inline int rather than pointer to something, so doesn't need to be preceded by a length field */ - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &MCORPSENM(mtmp), - sizeof MCORPSENM(mtmp)); + buflen = (int) MCORPSENM(mtmp); + Sfo_int(nhfp, &buflen, "monst-mcorpsenm"); } } @@ -959,7 +866,7 @@ savemonchn(NHFILE *nhfp, struct monst *mtmp) while (mtmp) { mtmp2 = mtmp->nmon; - if (perform_bwrite(nhfp)) { + if (update_file(nhfp)) { mtmp->mnum = monsndx(mtmp->data); if (mtmp->ispriest) forget_temple_entry(mtmp); /* EPRI() */ @@ -981,9 +888,8 @@ savemonchn(NHFILE *nhfp, struct monst *mtmp) } mtmp = mtmp2; } - if (perform_bwrite(nhfp)) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &minusone, sizeof (int)); + if (update_file(nhfp)) { + Sfo_int(nhfp, &minusone, "monst-monst_length"); } } @@ -1000,9 +906,8 @@ savetrapchn(NHFILE *nhfp, struct trap *trap) trap2 = trap->ntrap; if (use_relative) trap->dst.dlevel -= u.uz.dlevel; /* make it relative */ - if (perform_bwrite(nhfp)) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) trap, sizeof *trap); + if (update_file(nhfp)) { + Sfo_trap(nhfp, trap, "trap"); } if (use_relative) trap->dst.dlevel += u.uz.dlevel; /* reset back to absolute */ @@ -1010,9 +915,8 @@ savetrapchn(NHFILE *nhfp, struct trap *trap) dealloc_trap(trap); trap = trap2; } - if (perform_bwrite(nhfp)) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &zerotrap, sizeof zerotrap); + if (update_file(nhfp)) { + Sfo_trap(nhfp, &zerotrap, "trap"); } } @@ -1030,17 +934,15 @@ savefruitchn(NHFILE *nhfp) f1 = gf.ffruit; while (f1) { f2 = f1->nextf; - if (f1->fid >= 0 && perform_bwrite(nhfp)) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) f1, sizeof *f1); + if (f1->fid >= 0 && update_file(nhfp)) { + Sfo_fruit(nhfp, f1, "fruit"); } if (release_data(nhfp)) dealloc_fruit(f1); f1 = f2; } - if (perform_bwrite(nhfp)) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &zerofruit, sizeof zerofruit); + if (update_file(nhfp)) { + Sfo_fruit(nhfp, &zerofruit, "fruit"); } if (release_data(nhfp)) gf.ffruit = 0; @@ -1054,15 +956,13 @@ savelevchn(NHFILE *nhfp) for (tmplev = svs.sp_levchn; tmplev; tmplev = tmplev->next) cnt++; - if (perform_bwrite(nhfp)) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &cnt, sizeof cnt); + if (update_file(nhfp)) { + Sfo_int(nhfp, &cnt, "levchn-lev_count"); } for (tmplev = svs.sp_levchn; tmplev; tmplev = tmplev2) { tmplev2 = tmplev->next; - if (perform_bwrite(nhfp)) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) tmplev, sizeof *tmplev); + if (update_file(nhfp)) { + Sfo_s_level(nhfp, tmplev, "levchn-s_level"); } if (release_data(nhfp)) free((genericptr_t) tmplev); @@ -1071,18 +971,36 @@ savelevchn(NHFILE *nhfp) svs.sp_levchn = 0; } +/* write "name-role-race-gend-algn" into save file for menu-based restore; + the first dash is actually stored as '\0' instead of '-' */ void store_plname_in_file(NHFILE *nhfp) { - int plsiztmp = PL_NSIZ; + char hero[PL_NSIZ_PLUS]; /* [PL_NSIZ + 4*(1+3) + 1] */ + int plsiztmp = (int) sizeof hero; - if (nhfp->structlevel) { - bufoff(nhfp->fd); - /* bwrite() before bufon() uses plain write() */ - bwrite(nhfp->fd, (genericptr_t) &plsiztmp, sizeof plsiztmp); - bwrite(nhfp->fd, (genericptr_t) svp.plname, plsiztmp); + (void) memset((genericptr_t) hero, '\0', sizeof hero); + /* augment svp.plname[]; the gender and alignment values reflect those + in effect at time of saving rather than at start of game */ + Snprintf(hero, sizeof hero, "%s-%.3s-%.3s-%.3s-%.3s", + svp.plname, gu.urole.filecode, + gu.urace.filecode, genders[flags.female].filecode, + aligns[1 - u.ualign.type].filecode); + /* replace "-role-race..." with "\0role-race..." so that we can include + or exclude the role-&c suffix easily, without worrying about whether + plname contains any dashes; but don't rely on snprintf() for this */ + hero[strlen(svp.plname)] = '\0'; + /* insert playmode into final slot of hero[]; + 'D','X','-' are the same characters as are used for paniclog entries */ + assert(hero[PL_NSIZ_PLUS - 1 - 1] == '\0'); + hero[PL_NSIZ_PLUS - 1] = wizard ? 'D' : discover ? 'X' : '-'; + + if (nhfp->structlevel) + bufoff(nhfp->fd); /* bwrite() before bufon() uses plain write() */ + Sfo_int(nhfp, &plsiztmp, "plname-size"); + Sfo_char(nhfp, hero, "plname", plsiztmp); + if (nhfp->structlevel) bufon(nhfp->fd); - } return; } @@ -1090,85 +1008,57 @@ staticfn void save_msghistory(NHFILE *nhfp) { char *msg; - int msgcount = 0, msglen; - int minusone = -1; + int msgcount = 0; + int minusone = -1, msglen; boolean init = TRUE; - if (perform_bwrite(nhfp)) { + if (update_file(nhfp)) { /* ask window port for each message in sequence */ while ((msg = getmsghistory(init)) != 0) { init = FALSE; - msglen = Strlen(msg); + msglen = (int) Strlen(msg); if (msglen < 1) continue; /* sanity: truncate if necessary (shouldn't happen); no need to modify msg[] since terminator isn't written */ if (msglen > BUFSZ - 1) msglen = BUFSZ - 1; - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &msglen, sizeof msglen); - bwrite(nhfp->fd, (genericptr_t) msg, msglen); - } + Sfo_int(nhfp, &msglen, "msghistory-length"); + Sfo_char(nhfp, msg, "msghistory-msg", msglen); ++msgcount; } - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &minusone, sizeof (int)); + Sfo_int(nhfp, &minusone, "msghistory-length"); } debugpline1("Stored %d messages into savefile.", msgcount); /* note: we don't attempt to handle release_data() here */ } -void -store_savefileinfo(NHFILE *nhfp) -{ - /* sfcap (decl.c) describes the savefile feature capabilities - * that are supported by this port/platform build. - * - * sfsaveinfo (decl.c) describes the savefile info that actually - * gets written into the savefile, and is used to determine the - * save file being written. - * - * sfrestinfo (decl.c) describes the savefile info that is - * being used to read the information from an existing savefile. - */ - - if (nhfp->structlevel) { - bufoff(nhfp->fd); - /* bwrite() before bufon() uses plain write() */ - bwrite(nhfp->fd, (genericptr_t) &sfsaveinfo, - (unsigned) sizeof sfsaveinfo); - bufon(nhfp->fd); - } - return; -} - /* also called by prscore(); this probably belongs in dungeon.c... */ void free_dungeons(void) { #ifdef FREE_ALL_MEMORY - NHFILE tnhfp; + NHFILE *tnhfp = get_freeing_nhfile(); - zero_nhfile(&tnhfp); /* also sets fd to -1 */ - tnhfp.mode = FREEING; - savelevchn(&tnhfp); - save_dungeon(&tnhfp, FALSE, TRUE); + savelevchn(tnhfp); + save_dungeon(tnhfp, FALSE, TRUE); free_luathemes(all_themes); + close_nhfile(tnhfp); #endif return; } +extern int options_set_window_colors_flag; /* options.c */ + /* free a lot of allocated memory which is ordinarily freed during save */ void freedynamicdata(void) { - NHFILE tnhfp; + NHFILE *tnhfp = get_freeing_nhfile(); #if defined(UNIX) && defined(MAIL) free_maildata(); #endif - zero_nhfile(&tnhfp); /* also sets fd to -1 */ - tnhfp.mode = FREEING; free_menu_coloring(); free_invbuf(); /* let_to_name (invent.c) */ free_youbuf(); /* You_buf,&c (pline.c) */ @@ -1177,22 +1067,22 @@ freedynamicdata(void) tmp_at(DISP_FREEMEM, 0); /* temporary display effects */ purge_all_custom_entries(); #ifdef FREE_ALL_MEMORY -#define free_current_level() savelev(&tnhfp, -1) -#define freeobjchn(X) (saveobjchn(&tnhfp, &X), X = 0) -#define freemonchn(X) (savemonchn(&tnhfp, X), X = 0) -#define freefruitchn() savefruitchn(&tnhfp) -#define freenames() savenames(&tnhfp) -#define free_killers() save_killers(&tnhfp) -#define free_oracles() save_oracles(&tnhfp) -#define free_waterlevel() save_waterlevel(&tnhfp) -#define free_timers(R) save_timers(&tnhfp, R) -#define free_light_sources(R) save_light_sources(&tnhfp, R) +#define free_current_level() savelev(tnhfp, -1) +#define freeobjchn(X) (saveobjchn(tnhfp, &X), X = 0) +#define freemonchn(X) (savemonchn(tnhfp, X), X = 0) +#define freefruitchn() savefruitchn(tnhfp) +#define freenames() savenames(tnhfp) +#define free_killers() save_killers(tnhfp) +#define free_oracles() save_oracles(tnhfp) +#define free_waterlevel() save_waterlevel(tnhfp) +#define free_timers(R) save_timers(tnhfp, R) +#define free_light_sources(R) save_light_sources(tnhfp, R) #define free_animals() mon_animal_list(FALSE) -#define discard_gamelog() save_gamelog(&tnhfp); +#define discard_gamelog() save_gamelog(tnhfp); /* move-specific data */ dmonsfree(); /* release dead monsters */ - dobjsfree(); + /* dobjsfree(); // handled below */ alloc_itermonarr(0U); /* a request of 0 releases existing allocation */ /* level-specific data */ @@ -1221,6 +1111,9 @@ freedynamicdata(void) cmdq_clear(CQ_REPEAT); free_tutorial(); /* (only needed if quitting while in tutorial) */ + /* per-turn data, but might get added to when freeing other stuff */ + dobjsfree(); /* really free deleted objects */ + /* some pointers in iflags */ if (iflags.wc_font_map) free((genericptr_t) iflags.wc_font_map), iflags.wc_font_map = 0; @@ -1248,15 +1141,26 @@ freedynamicdata(void) #endif discard_gamelog(); release_runtime_info(); /* build-time options and version stuff */ + free_convert_filenames(); #endif /* FREE_ALL_MEMORY */ if (VIA_WINDOWPORT()) status_finish(); + if (options_set_window_colors_flag) + options_free_window_colors(); + + if (glyphid_cache_status()) + free_glyphid_cache(); + + if (tnhfp) { + close_nhfile(tnhfp); + tnhfp = 0; + } + /* last, because it frees data that might be used by panic() to provide feedback to the user; conceivably other freeing might trigger panic */ sysopt_release(); /* SYSCF strings */ - return; } /*save.c*/ diff --git a/src/selvar.c b/src/selvar.c index 1951edfb8..5d08ca95e 100644 --- a/src/selvar.c +++ b/src/selvar.c @@ -582,6 +582,7 @@ selection_do_gradient( switch (gtyp) { default: impossible("Unrecognized gradient type! Defaulting to radial..."); + FALLTHROUGH; /* FALLTHRU */ case SEL_GRADIENT_RADIAL: { for (dx = 0; dx < COLNO; dx++) diff --git a/src/sfbase.c b/src/sfbase.c new file mode 100644 index 000000000..0922839d1 --- /dev/null +++ b/src/sfbase.c @@ -0,0 +1,1117 @@ +/* NetHack 3.7 sfbase.c.template $NHDT-Date$ $NHDT-Branch$:$NHDT-Revision$ */ +/* Copyright (c) Michael Allison, 2025. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "sfprocs.h" + +#ifdef SFCTOOL +//#include "sfproto.h" +#endif + +/* #define DO_DEBUG */ + +//#define TURN_OFF_LOGGING 0x20 +#define TURN_OFF_LOGGING (UNCONVERTING << 1) + +struct sf_structlevel_procs sfoprocs[NUM_SAVEFORMATS], sfiprocs[NUM_SAVEFORMATS], + zerosfoprocs = {0}, zerosfiprocs = {0}; +struct sf_fieldlevel_procs sfoflprocs[NUM_SAVEFORMATS], sfiflprocs[NUM_SAVEFORMATS], + zerosfoflprocs = {0}, zerosfiflprocs = {0}; + +char *sfvalue_aligntyp(aligntyp *a); +char *sfvalue_any(anything *a); +char *sfvalue_genericptr(genericptr a); +char *sfvalue_int16(int16 *a); +char *sfvalue_int32(int32 *a); +char *sfvalue_int64(int64 *a); +char *sfvalue_uchar(uchar *a); +char *sfvalue_uint16(uint16 *a); +char *sfvalue_uint32(uint32 *a); +char *sfvalue_uint64(uint64 *a); +char *sfvalue_size_t(size_t *a); +char *sfvalue_time_t(time_t *a); +char *sfvalue_short(short *a); +char *sfvalue_ushort(ushort *a); +char *sfvalue_int(int *a); +char *sfvalue_unsigned(unsigned *a); +char *sfvalue_long(long *a); +char *sfvalue_ulong(ulong *a); +char *sfvalue_xint8(xint8 *a); +char *sfvalue_xint16(xint16 *a); +char *sfvalue_char(char *a, int n); +char *sfvalue_boolean(boolean *a); +char *sfvalue_schar(schar *a); +char *sfvalue_bitfield(uint8 *a); +char *complex_dump(uchar *a); +char *bitfield_dump(uint8 *a); + +void sf_log(NHFILE *, const char *, size_t, int, char *); + +#if NH_C < 202300L +#define Sfvalue_aligntyp(a) sfvalue_aligntyp(a) +#define Sfvalue_any(a) sfvalue_any(a) +#define Sfvalue_genericptr(a) sfvalue_genericptr(a) +#define Sfvalue_coordxy(a) sfvalue_int16(a) +#define Sfvalue_int16(a) sfvalue_int16(a) +#define Sfvalue_int32(a) sfvalue_int32(a) +#define Sfvalue_int64(a) sfvalue_int64(a) +#define Sfvalue_uchar(a) sfvalue_uchar(a) +#define Sfvalue_uint16(a) sfvalue_uint16(a) +#define Sfvalue_uint32(a) sfvalue_uint32(a) +#define Sfvalue_uint64(a) sfvalue_uint64(a) +#define Sfvalue_size_t(a) sfvalue_size_t(a) +#define Sfvalue_time_t(a) sfvalue_time_t(a) +#define Sfvalue_short(a) sfvalue_short(a) +#define Sfvalue_ushort(a) sfvalue_ushort(a) +#define Sfvalue_int(a) sfvalue_int(a) +#define Sfvalue_unsigned(a) sfvalue_unsigned(a) +#define Sfvalue_xint8(a) sfvalue_xint8(a) +#define Sfvalue_xint16(a) sfvalue_xint16(a) + +#else + +#define sfvalue(x) \ + _Generic( (x), \ + anything *: sfvalue_any, \ + genericptr_t *: sfvalue_genericptr, \ + int16_t *: sfvalue_int16, \ + int32_t *: sfvalue_int32, \ + int64_t *: sfvalue_int64, \ + uchar *: sfvalue_uchar, \ + uint16_t *: sfvalue_uint16, \ + uint32_t *: sfvalue_uint32, \ + uint64_t *: sfvalue_uint64, \ + xint8 *: sfvalue_xint8 \ + )(x) + +#define Sfvalue_any(a) sfvalue(a) +#define Sfvalue_aligntyp(a) sfvalue(a) +#define Sfvalue_genericptr(a) sfvalue(a) +#define Sfvalue_coordxy(a) sfvalue(a) +#define Sfvalue_int16(a) sfvalue(a) +#define Sfvalue_int32(a) sfvalue(a) +#define Sfvalue_int64(a) sfvalue(a) +#define Sfvalue_uchar(a) sfvalue(a) +#define Sfvalue_unsigned(a) sfvalue(a) +#define Sfvalue_uchar(a) sfvalue(a) +#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) +#define Sfvalue_unsigned(a) sfvalue(a) +#define Sfvalue_xint8(a) sfvalue(a) +#define Sfvalue_xint16(a) sfvalue(a) +#endif + +/* not in _Generic */ +#define Sfvalue_long(a) sfvalue_long(a) +#define Sfvalue_ulong(a) sfvalue_ulong(a) +#define Sfvalue_char(a, d) sfvalue_char(a, d) +#define Sfvalue_boolean(a) sfvalue_boolean(a) +#define Sfvalue_schar(a) sfvalue_schar(a) +#define Sfvalue_bitfield(a) sfvalue_bitfield(a) + +#define SF_A(dtyp) \ +void sfo_##dtyp(NHFILE *nhfp, dtyp *d_##dtyp, const char *myname) \ +{ \ + if (nhfp->fplog) \ + sf_log(nhfp, myname, sizeof *d_##dtyp, 1, Sfvalue_##dtyp(d_##dtyp)); \ + if (nhfp->structlevel) { \ + (*sfoprocs[nhfp->fnidx].fn.sf_##dtyp)(nhfp, d_##dtyp, myname); \ + } else { \ + FILE *save_fplog = nhfp->fplog; \ + \ + nhfp->fplog = 0; \ + (*sfoflprocs[nhfp->fnidx].fn_x.sf_##dtyp)(nhfp, d_##dtyp, myname); \ + nhfp->fplog = save_fplog; \ + } \ +} \ + \ +void sfi_##dtyp(NHFILE *nhfp, dtyp *d_##dtyp, const char *myname) \ +{ \ + if (nhfp->structlevel) { \ + (*sfiprocs[nhfp->fnidx].fn.sf_##dtyp)(nhfp, d_##dtyp, myname); \ + } else { \ + int save_mode = nhfp->mode; \ + \ + nhfp->mode &= ~(CONVERTING | UNCONVERTING); \ + nhfp->mode |= TURN_OFF_LOGGING; \ + (*sfiflprocs[nhfp->fnidx].fn_x.sf_##dtyp)(nhfp, d_##dtyp, myname); \ + nhfp->mode = save_mode; \ + } \ + if (!nhfp->eof) { \ + if ((((nhfp->mode & CONVERTING) != 0) \ + || ((nhfp->mode & UNCONVERTING) != 0)) && nhfp->nhfpconvert) { \ + sfo_##dtyp(nhfp->nhfpconvert, d_##dtyp, myname); \ + } \ + if (nhfp->fplog) \ + sf_log(nhfp, myname, sizeof *d_##dtyp, 1, \ + Sfvalue_##dtyp(d_##dtyp)); \ + } \ +} + +#define SF_C(keyw, dtyp) \ +void sfo_##dtyp(NHFILE *nhfp, keyw dtyp *d_##dtyp, const char *myname) \ +{ \ + if (nhfp->fplog) \ + sf_log(nhfp, myname, sizeof *d_##dtyp, 1, \ + complex_dump((uchar *) d_##dtyp)); \ + if (nhfp->structlevel) { \ + (*sfoprocs[nhfp->fnidx].fn.sf_##dtyp)(nhfp, d_##dtyp, myname); \ + } else { \ + FILE *save_fplog = nhfp->fplog; \ + \ + nhfp->fplog = 0; \ + (*sfoflprocs[nhfp->fnidx].fn_x.sf_##dtyp)(nhfp, d_##dtyp, myname); \ + nhfp->fplog = save_fplog; \ + } \ +} \ + \ +void sfi_##dtyp(NHFILE *nhfp, keyw dtyp *d_##dtyp, const char *myname) \ +{ \ + if (nhfp->structlevel) { \ + (*sfiprocs[nhfp->fnidx].fn.sf_##dtyp)(nhfp, d_##dtyp, myname); \ + } else { \ + int save_mode = nhfp->mode; \ + \ + nhfp->mode &= ~(CONVERTING | UNCONVERTING); \ + nhfp->mode |= TURN_OFF_LOGGING; \ + (*sfiflprocs[nhfp->fnidx].fn_x.sf_##dtyp)(nhfp, d_##dtyp, myname); \ + nhfp->mode = save_mode; \ + } \ + if (!nhfp->eof) { \ + if ((((nhfp->mode & CONVERTING) != 0) \ + || ((nhfp->mode & UNCONVERTING) != 0)) \ + && nhfp->nhfpconvert) { \ + sfo_##dtyp(nhfp->nhfpconvert, d_##dtyp, myname); \ + } \ + if (nhfp->fplog) \ + sf_log(nhfp, myname, sizeof *d_##dtyp, 1, \ + complex_dump((uchar *) d_##dtyp)); \ + } \ +} + +#define SF_X(xxx, dtyp) \ +void sfo_##dtyp(NHFILE *nhfp, xxx *d_##dtyp, const char *myname, int bfsz) \ +{ \ + if (nhfp->fplog) \ + sf_log(nhfp, myname, sizeof *d_##dtyp, 1, \ + Sfvalue_##dtyp(d_##dtyp)); \ + if (nhfp->structlevel) { \ + (*sfoprocs[nhfp->fnidx].fn.sf_##dtyp)(nhfp, d_##dtyp, myname, bfsz); \ + } else { \ + FILE *save_fplog = nhfp->fplog; \ + \ + nhfp->fplog = 0; \ + (*sfoflprocs[nhfp->fnidx].fn_x.sf_##dtyp)(nhfp, d_##dtyp, \ + myname, bfsz); \ + nhfp->fplog = save_fplog; \ + } \ + if (nhfp->fplog && !nhfp->eof) \ + sf_log(nhfp, myname, sizeof *d_##dtyp, 1, Sfvalue_##dtyp(d_##dtyp)); \ +} \ + \ +void sfi_##dtyp(NHFILE *nhfp, xxx *d_##dtyp, const char *myname, int bfsz) \ +{ \ + if (nhfp->structlevel) { \ + (*sfiprocs[nhfp->fnidx].fn.sf_##dtyp)(nhfp, d_##dtyp, myname, bfsz); \ + } else { \ + int save_mode = nhfp->mode; \ + \ + nhfp->mode &= ~(CONVERTING | UNCONVERTING); \ + nhfp->mode |= TURN_OFF_LOGGING; \ + (*sfiflprocs[nhfp->fnidx].fn_x.sf_##dtyp)(nhfp, d_##dtyp, \ + myname, bfsz); \ + nhfp->mode = save_mode; \ + } \ + if (!nhfp->eof) { \ + if ((((nhfp->mode & CONVERTING) != 0) \ + || ((nhfp->mode & UNCONVERTING) != 0)) \ + && nhfp->nhfpconvert) { \ + sfo_##dtyp(nhfp->nhfpconvert, d_##dtyp, myname, bfsz); \ + } \ + if (nhfp->fplog) \ + sf_log(nhfp, myname, sizeof *d_##dtyp, 1, \ + dtyp##_dump(d_##dtyp)); \ + } \ +} + +#include "sfmacros.h" + +SF_X(uint8_t, bitfield) + +void +sfo_char(NHFILE *nhfp, char *d_char, const char *myname, int cnt) +{ + if (nhfp->fplog) + sf_log(nhfp, myname, sizeof(char), cnt, Sfvalue_char(d_char, cnt)); + if (nhfp->structlevel) { + (*sfoprocs[nhfp->fnidx].fn.sf_char)(nhfp, d_char, myname, cnt); + } else { + FILE *save_fplog = nhfp->fplog; + + nhfp->fplog = 0; + (*sfoflprocs[nhfp->fnidx].fn_x.sf_char)(nhfp, d_char, myname, cnt); + nhfp->fplog = save_fplog; + } +} + +void +sfi_char(NHFILE *nhfp, char *d_char, const char *myname, int cnt) +{ + if (nhfp->structlevel) { + (*sfiprocs[nhfp->fnidx].fn.sf_char)(nhfp, d_char, myname, cnt); + } else { + int save_mode = nhfp->mode; + + nhfp->mode &= ~(CONVERTING | UNCONVERTING); + nhfp->mode |= TURN_OFF_LOGGING; + (*sfiflprocs[nhfp->fnidx].fn_x.sf_char)(nhfp, d_char, myname, cnt); + nhfp->mode = save_mode; + } + if (!nhfp->eof) { + if ((((nhfp->mode & CONVERTING) != 0) + || ((nhfp->mode & UNCONVERTING) != 0)) + && nhfp->nhfpconvert) { + sfo_char(nhfp->nhfpconvert, d_char, myname, cnt); + } + if (nhfp->fplog) + sf_log(nhfp, myname, sizeof(char), cnt, + Sfvalue_char(d_char, cnt)); + } +} + +void +sfo_genericptr(NHFILE *nhfp, void **d_genericptr, const char *myname) +{ + if (nhfp->fplog) + sf_log(nhfp, myname, sizeof *d_genericptr, 1, + Sfvalue_genericptr(d_genericptr)); + if (nhfp->structlevel) { + (*sfoprocs[nhfp->fnidx].fn.sf_genericptr)(nhfp, d_genericptr, myname); + } else { + FILE *save_fplog = nhfp->fplog; + nhfp->fplog = 0; + (*sfoflprocs[nhfp->fnidx].fn_x.sf_genericptr)(nhfp, d_genericptr, + myname); + nhfp->fplog = save_fplog; + } +} +void +sfi_genericptr(NHFILE *nhfp, void **d_genericptr, const char *myname) +{ + if (nhfp->structlevel) { + (*sfiprocs[nhfp->fnidx].fn.sf_genericptr)(nhfp, d_genericptr, myname); + } else { + int save_mode = nhfp->mode; + nhfp->mode &= ~(CONVERTING | UNCONVERTING); + nhfp->mode |= TURN_OFF_LOGGING; + (*sfiflprocs[nhfp->fnidx].fn_x.sf_genericptr)(nhfp, d_genericptr, + myname); + nhfp->mode = save_mode; + } + if (!nhfp->eof) { + if ((((nhfp->mode & CONVERTING) != 0) || ((nhfp->mode & UNCONVERTING) != 0)) + && nhfp->nhfpconvert) { + sfo_genericptr(nhfp->nhfpconvert, d_genericptr, myname); + } + if (nhfp->fplog) + sf_log(nhfp, myname, sizeof *d_genericptr, 1, + Sfvalue_genericptr(d_genericptr)); + } +} + +void +sfo_version_info(NHFILE *nhfp, struct version_info *d_version_info, + const char *myname) +{ + if (nhfp->fplog) + sf_log(nhfp, myname, sizeof *d_version_info, 1, + complex_dump((uchar *) d_version_info)); + if (nhfp->structlevel) { + (*sfoprocs[nhfp->fnidx].fn.sf_version_info)(nhfp, d_version_info, + myname); + } else { + FILE *save_fplog = nhfp->fplog; + nhfp->fplog = 0; + (*sfoflprocs[nhfp->fnidx].fn_x.sf_version_info)(nhfp, d_version_info, + myname); + nhfp->fplog = save_fplog; + } +} +void +sfi_version_info(NHFILE *nhfp, struct version_info *d_version_info, + const char *myname) +{ + if (nhfp->structlevel) { + (*sfiprocs[nhfp->fnidx].fn.sf_version_info)(nhfp, d_version_info, + myname); + } else { + int save_mode = nhfp->mode; + nhfp->mode &= ~(CONVERTING | UNCONVERTING); + nhfp->mode |= TURN_OFF_LOGGING; + (*sfiflprocs[nhfp->fnidx].fn_x.sf_version_info)(nhfp, d_version_info, + myname); + nhfp->mode = save_mode; + } + if (!nhfp->eof) { + if ((((nhfp->mode & CONVERTING) != 0) || ((nhfp->mode & UNCONVERTING) != 0)) + && nhfp->nhfpconvert) { + d_version_info->feature_set |= SFCTOOL_BIT; + sfo_version_info(nhfp->nhfpconvert, d_version_info, myname); + } + if (nhfp->fplog) + sf_log(nhfp, myname, sizeof *d_version_info, 1, + complex_dump((uchar *) d_version_info)); + } +} + +/* ---------------------------------------------------------------*/ + +void +sf_log(NHFILE *nhfp, const char *t1, size_t sz, int cnt, char *txtvalue) +{ + FILE *fp = nhfp->fplog; + long *iocount; + boolean dolog = ((nhfp->mode & TURN_OFF_LOGGING) == 0); + + if (fp && dolog) { + iocount = ((nhfp->mode & WRITING) == 0) ? &nhfp->rcount : &nhfp->wcount; + (void) fprintf(fp, +#ifndef VMS + "%08ld %s sz=%zu cnt=%d |%s|\n", +#else + "%08ld %s sz=%lu cnt=%d |%s|\n", +#endif + *iocount, + t1, +#ifndef VMS + sz, +#else + (unsigned long) sz, +#endif + cnt, txtvalue); +// (*iocount)++; +// if (*iocount == 87) +// __debugbreak(); + fflush(fp); + } +} + +char *sfvalue_char(char *a, int n) +{ + int i; + static char buf[120]; + char *cp; + + cp = &buf[0]; + if (n < (int) (sizeof buf - 1)) + buf[n] = '\0'; + else + buf[(int) (sizeof buf - 1)] = '\0'; + for (i = 0; i < n; ++i, ++cp, ++a) + *cp = *a; + *cp = '\0'; + return buf; +} + +char *sfvalue_boolean(boolean *a) +{ + static char buf[20]; + + Snprintf(buf, sizeof buf, "%s", + (*a == 0) ? "false" : "true"); + return buf; +} + +char *sfvalue_schar(schar *a) +{ + static char buf[20]; + + Snprintf(buf, sizeof buf, "%d", (int) *a); + return buf; +} + +char * sfvalue_aligntyp(aligntyp *a) +{ + static char buf[20]; + + Snprintf(buf, sizeof buf, "%d", (int) *a); + return buf; +} + +char * +sfvalue_any(anything *a) +{ + static char buf[20]; + + Snprintf(buf, sizeof buf, + "%" PRId64, + a->a_int64); + return buf; +} + +char * +sfvalue_genericptr(genericptr a) +{ + static char buf[20]; + + Snprintf(buf, sizeof buf, "%s", + (a == 0) ? "0" : "glorkum"); + return buf; +} + +char * sfvalue_int16(int16 *a) +{ + static char buf[20]; + + Snprintf(buf, sizeof buf, "%d", *a); + return buf; +} + +char * sfvalue_int32(int32 *a) +{ + static char buf[20]; + + Snprintf(buf, sizeof buf, "%" PRId32, *a); + return buf; +} + +char * sfvalue_int64(int64 *a) +{ + static char buf[20]; + Snprintf(buf, sizeof buf, "%" PRId64, *a); + return buf; +} + +char * sfvalue_uchar(uchar *a) +{ + static char buf[20]; + unsigned x; + + x = *a; + Snprintf(buf, sizeof buf, "%03u", x); + return buf; +} + +char * sfvalue_uint16(uint16 *a) +{ + static char buf[20]; + + Snprintf(buf, sizeof buf, "%u", (uint) *a); + return buf; +} + +char * sfvalue_uint32(uint32 *a) +{ + static char buf[20]; + + Snprintf(buf, sizeof buf, "%" PRIu32, *a); + return buf; +} + +char * sfvalue_uint64(uint64 *a) +{ + static char buf[20]; + + Snprintf(buf, sizeof buf, "%" PRIu64, *a); + return buf; +} + +char * sfvalue_size_t(size_t *a UNUSED) +{ + static char buf[20]; + + Snprintf(buf, sizeof buf, "%s", (char *) ""); + return buf; +} + +char * sfvalue_time_t(time_t *a UNUSED) +{ + static char buf[20]; + + Snprintf(buf, sizeof buf, "%s", (char *) ""); + return buf; +} + +char * sfvalue_short(short *a) +{ + static char buf[20]; + + Snprintf(buf, sizeof buf, "%d", (int) *a); + return buf; +} + +char * sfvalue_ushort(ushort *a) +{ + static char buf[20]; + + Snprintf(buf, sizeof buf, "%u", (unsigned) *a); + return buf; +} + +char * sfvalue_int(int *a) +{ + static char buf[20]; + + Snprintf(buf, sizeof buf, "%d", *a); + return buf; +} + +char * sfvalue_unsigned(unsigned *a) +{ + static char buf[20]; + + Snprintf(buf, sizeof buf, "%u", *a); + return buf; +} + +char * sfvalue_long(long *a) +{ + static char buf[20]; + + Snprintf(buf, sizeof buf, "%ld", *a); + return buf; +} + +char * sfvalue_ulong(ulong *a) +{ + static char buf[20]; + + Snprintf(buf, sizeof buf, "%lu", *a); + return buf; +} + +char * sfvalue_xint8(xint8 *a) +{ + static char buf[20]; + + Snprintf(buf, sizeof buf, "%d", (int) *a); + return buf; +} + +char * sfvalue_xint16(xint16 *a) +{ + static char buf[20]; + + + Snprintf(buf, sizeof buf, "%d", (int) *a); + return buf; +} + +char * +sfvalue_bitfield(uint8 *a) +{ + static char buf[20]; + + Snprintf(buf, sizeof buf, "%u", (uint) *a); + return buf; +} + +char * +bitfield_dump(uint8 *a) +{ + static char buf[20]; + + Snprintf(buf, sizeof buf, "%u", (uint) *a); + return buf; +} +char * +complex_dump(uchar *a) +{ + int i; + uchar *uc = a; + static char buf[50]; + unsigned x[10]; + + for (i = 0; i < SIZE(x); ++i) { + x[i] = *uc++; + } + Snprintf(buf, sizeof buf, "%03x %03x %03x %03x %03x %03x %03x %03x %03x %03x", + x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], x[8], x[9]); + buf[40] = '\0'; + return buf; +} +/* + *---------------------------------------------------------------------------- + * initialize the function pointers. These are called from initoptions_init(). + *---------------------------------------------------------------------------- + */ + +void +sf_init(void) +{ + sfoprocs[invalid] = zerosfoprocs; + sfiprocs[invalid] = zerosfiprocs; + sfoprocs[historical] = historical_sfo_procs; + sfiprocs[historical] = historical_sfi_procs; + sfoflprocs[exportascii] = zerosfoflprocs; + sfiflprocs[exportascii] = zerosfiflprocs; +} + +void +sf_setprocs(int idx, struct sf_structlevel_procs *sfi, struct sf_structlevel_procs *sfo) +{ + sfoprocs[idx] = *sfo; + sfiprocs[idx] = *sfi; +} +void +sf_setflprocs(int idx, struct sf_fieldlevel_procs *flsfi, + struct sf_fieldlevel_procs *flsfo) +{ + sfoflprocs[idx] = *flsfo; + sfiflprocs[idx] = *flsfi; +} + +#ifndef SFCTOOL +void norm_ptrs_any(union any *d_any); +void norm_ptrs_align(struct align *d_align); +void norm_ptrs_arti_info(struct arti_info *d_arti_info); +void norm_ptrs_attribs(struct attribs *d_attribs); +void norm_ptrs_bill_x(struct bill_x *d_bill_x); +void norm_ptrs_branch(struct branch *d_branch); +void norm_ptrs_bubble(struct bubble *d_bubble); +void norm_ptrs_cemetery(struct cemetery *d_cemetery); +void norm_ptrs_context_info(struct context_info *d_context_info); +void norm_ptrs_achievement_tracking( + struct achievement_tracking *d_achievement_tracking); +void norm_ptrs_book_info(struct book_info *d_book_info); +void norm_ptrs_dig_info(struct dig_info *d_dig_info); +void norm_ptrs_engrave_info(struct engrave_info *d_engrave_info); +void norm_ptrs_obj_split(struct obj_split *d_obj_split); +void norm_ptrs_polearm_info(struct polearm_info *d_polearm_info); +void norm_ptrs_takeoff_info(struct takeoff_info *d_takeoff_info); +void norm_ptrs_tin_info(struct tin_info *d_tin_info); +void norm_ptrs_tribute_info(struct tribute_info *d_tribute_info); +void norm_ptrs_victual_info(struct victual_info *d_victual_info); +void norm_ptrs_warntype_info(struct warntype_info *d_warntype_info); +void norm_ptrs_d_flags(struct d_flags *d_d_flags); +void norm_ptrs_d_level(struct d_level *d_d_level); +void norm_ptrs_damage(struct damage *d_damage); +void norm_ptrs_dest_area(struct dest_area *d_dest_area); +void norm_ptrs_dgn_topology(struct dgn_topology *d_dgn_topology); +void norm_ptrs_dungeon(struct dungeon *d_dungeon); +void norm_ptrs_ebones(struct ebones *d_ebones); +void norm_ptrs_edog(struct edog *d_edog); +void norm_ptrs_egd(struct egd *d_egd); +void norm_ptrs_emin(struct emin *d_emin); +void norm_ptrs_engr(struct engr *d_engr); +void norm_ptrs_epri(struct epri *d_epri); +void norm_ptrs_eshk(struct eshk *d_eshk); +void norm_ptrs_fakecorridor(struct fakecorridor *d_fakecorridor); +void norm_ptrs_fe(struct fe *d_fe); +void norm_ptrs_flag(struct flag *d_flag); +void norm_ptrs_fruit(struct fruit *d_fruit); +void norm_ptrs_gamelog_line(struct gamelog_line *d_gamelog_line); +void norm_ptrs_kinfo(struct kinfo *d_kinfo); +void norm_ptrs_levelflags(struct levelflags *d_levelflags); +void norm_ptrs_linfo(struct linfo *d_linfo); +void norm_ptrs_ls_t(struct ls_t *d_ls_t); +void norm_ptrs_mapseen_feat(struct mapseen_feat *d_mapseen_feat); +void norm_ptrs_mapseen_flags(struct mapseen_flags *d_mapseen_flags); +void norm_ptrs_mapseen_rooms(struct mapseen_rooms *d_mapseen_rooms); +void norm_ptrs_mapseen(struct mapseen *d_mapseen); +void norm_ptrs_mextra(struct mextra *d_mextra); +void norm_ptrs_mkroom(struct mkroom *d_mkroom); +void norm_ptrs_monst(struct monst *d_monst); +void norm_ptrs_mvitals(struct mvitals *d_mvitals); +void norm_ptrs_nhcoord(struct nhcoord *d_nhcoord); +void norm_ptrs_nhrect(struct nhrect *d_nhrect); +void norm_ptrs_novel_tracking(struct novel_tracking *d_novel_tracking); +void norm_ptrs_obj(struct obj *d_obj); +void norm_ptrs_objclass(struct objclass *d_objclass); +void norm_ptrs_oextra(struct oextra *d_oextra); +void norm_ptrs_prop(struct prop *d_prop); +void norm_ptrs_q_score(struct q_score *d_q_score); +void norm_ptrs_rm(struct rm *d_rm); +void norm_ptrs_s_level(struct s_level *d_s_level); +void norm_ptrs_skills(struct skills *d_skills); +void norm_ptrs_spell(struct spell *d_spell); +void norm_ptrs_stairway(struct stairway *d_stairway); +void norm_ptrs_trap(struct trap *d_trap); +void norm_ptrs_u_conduct(struct u_conduct *d_u_conduct); +void norm_ptrs_u_event(struct u_event *d_u_event); +void norm_ptrs_u_have(struct u_have *d_u_have); +void norm_ptrs_u_realtime(struct u_realtime *d_u_realtime); +void norm_ptrs_u_roleplay(struct u_roleplay *d_u_roleplay); +void norm_ptrs_version_info(struct version_info *d_version_info); +void norm_ptrs_vlaunchinfo(union vlaunchinfo *d_vlaunchinfo); +void norm_ptrs_vptrs(union vptrs *d_vptrs); +void norm_ptrs_you(struct you *d_you); + +void +norm_ptrs_any(union any *d_any UNUSED) +{ +} +void +norm_ptrs_align(struct align *d_align UNUSED) +{ +} + +void +norm_ptrs_arti_info(struct arti_info *d_arti_info UNUSED) +{ +} + +void +norm_ptrs_attribs(struct attribs *d_attribs UNUSED) +{ +} + +void +norm_ptrs_bill_x(struct bill_x *d_bill_x UNUSED) +{ +} + +void +norm_ptrs_branch(struct branch *d_branch UNUSED) +{ +} + +void +norm_ptrs_bubble(struct bubble *d_bubble UNUSED) +{ +} + +void +norm_ptrs_cemetery(struct cemetery *d_cemetery UNUSED) +{ +} + +void +norm_ptrs_context_info(struct context_info *d_context_info UNUSED) +{ +} + +void +norm_ptrs_achievement_tracking(struct achievement_tracking *d_achievement_tracking UNUSED) +{ +} + +void +norm_ptrs_book_info(struct book_info *d_book_info UNUSED) +{ +} + +void +norm_ptrs_dig_info(struct dig_info *d_dig_info UNUSED) +{ +} + +void +norm_ptrs_engrave_info(struct engrave_info *d_engrave_info UNUSED) +{ +} + +void +norm_ptrs_obj_split(struct obj_split *d_obj_split UNUSED) +{ +} + +void +norm_ptrs_polearm_info(struct polearm_info *d_polearm_info UNUSED) +{ +} + +void +norm_ptrs_takeoff_info(struct takeoff_info *d_takeoff_info UNUSED) +{ +} + +void +norm_ptrs_tin_info(struct tin_info *d_tin_info UNUSED) +{ +} + +void +norm_ptrs_tribute_info(struct tribute_info *d_tribute_info UNUSED) +{ +} + +void +norm_ptrs_victual_info(struct victual_info *d_victual_info UNUSED) +{ +} + +void +norm_ptrs_warntype_info(struct warntype_info *d_warntype_info UNUSED) +{ +} + +void +norm_ptrs_d_flags(struct d_flags *d_d_flags UNUSED) +{ +} + +void +norm_ptrs_d_level(struct d_level *d_d_level UNUSED) +{ +} + +void +norm_ptrs_damage(struct damage *d_damage UNUSED) +{ +} + +void +norm_ptrs_dest_area(struct dest_area *d_dest_area UNUSED) +{ +} + +void +norm_ptrs_dgn_topology(struct dgn_topology *d_dgn_topology UNUSED) +{ +} + +void +norm_ptrs_dungeon(struct dungeon *d_dungeon UNUSED) +{ +} + +void +norm_ptrs_ebones(struct ebones *d_ebones UNUSED) +{ +} + +void +norm_ptrs_edog(struct edog *d_edog UNUSED) +{ +} + +void +norm_ptrs_egd(struct egd *d_egd UNUSED) +{ +} + +void +norm_ptrs_emin(struct emin *d_emin UNUSED) +{ +} + +void +norm_ptrs_engr(struct engr *d_engr UNUSED) +{ +} + +void +norm_ptrs_epri(struct epri *d_epri UNUSED) +{ +} + +void +norm_ptrs_eshk(struct eshk *d_eshk UNUSED) +{ +} + +void +norm_ptrs_fakecorridor(struct fakecorridor *d_fakecorridor UNUSED) +{ +} + +void +norm_ptrs_fe(struct fe *d_fe UNUSED) +{ +} + +void +norm_ptrs_flag(struct flag *d_flag UNUSED) +{ +} + +void +norm_ptrs_fruit(struct fruit *d_fruit UNUSED) +{ +} + +void +norm_ptrs_gamelog_line(struct gamelog_line *d_gamelog_line UNUSED) +{ +} + +void +norm_ptrs_kinfo(struct kinfo *d_kinfo UNUSED) +{ +} + +void +norm_ptrs_levelflags(struct levelflags *d_levelflags UNUSED) +{ +} + +void +norm_ptrs_linfo(struct linfo *d_linfo UNUSED) +{ +} + +void +norm_ptrs_ls_t(struct ls_t *d_ls_t UNUSED) +{ +} + +void +norm_ptrs_mapseen_feat(struct mapseen_feat *d_mapseen_feat UNUSED) +{ +} + +void +norm_ptrs_mapseen_flags(struct mapseen_flags *d_mapseen_flags UNUSED) +{ +} + +void +norm_ptrs_mapseen_rooms(struct mapseen_rooms *d_mapseen_rooms UNUSED) +{ +} + +void +norm_ptrs_mapseen(struct mapseen *d_mapseen UNUSED) +{ +} + +void +norm_ptrs_mextra(struct mextra *d_mextra UNUSED) +{ +} + +void +norm_ptrs_mkroom(struct mkroom *d_mkroom UNUSED) +{ +} + +void +norm_ptrs_monst(struct monst *d_monst UNUSED) +{ +} + +void +norm_ptrs_mvitals(struct mvitals *d_mvitals UNUSED) +{ +} + +void +norm_ptrs_nhcoord(struct nhcoord *d_nhcoord UNUSED) +{ +} + +void +norm_ptrs_nhrect(struct nhrect *d_nhrect UNUSED) +{ +} + +void +norm_ptrs_novel_tracking(struct novel_tracking *d_novel_tracking UNUSED) +{ +} + +void +norm_ptrs_obj(struct obj *d_obj UNUSED) +{ +} + +void +norm_ptrs_objclass(struct objclass *d_objclass UNUSED) +{ +} + +void +norm_ptrs_oextra(struct oextra *d_oextra UNUSED) +{ +} + +void +norm_ptrs_prop(struct prop *d_prop UNUSED) +{ +} + +void +norm_ptrs_q_score(struct q_score *d_q_score UNUSED) +{ +} + +void +norm_ptrs_rm(struct rm *d_rm UNUSED) +{ +} + +void +norm_ptrs_s_level(struct s_level *d_s_level UNUSED) +{ +} + +void +norm_ptrs_skills(struct skills *d_skills UNUSED) +{ +} + +void +norm_ptrs_spell(struct spell *d_spell UNUSED) +{ +} + +void +norm_ptrs_stairway(struct stairway *d_stairway UNUSED) +{ +} + +void +norm_ptrs_trap(struct trap *d_trap UNUSED) +{ +} + +void +norm_ptrs_u_conduct(struct u_conduct *d_u_conduct UNUSED) +{ +} + +void +norm_ptrs_u_event(struct u_event *d_u_event UNUSED) +{ +} + +void +norm_ptrs_u_have(struct u_have *d_u_have UNUSED) +{ +} + +void +norm_ptrs_u_realtime(struct u_realtime *d_u_realtime UNUSED) +{ +} + +void +norm_ptrs_u_roleplay(struct u_roleplay *d_u_roleplay UNUSED) +{ +} + +void +norm_ptrs_version_info(struct version_info *d_version_info UNUSED) +{ +} + +void +norm_ptrs_vlaunchinfo(union vlaunchinfo *d_vlaunchinfo UNUSED) +{ +} + +void +norm_ptrs_vptrs(union vptrs *d_vptrs UNUSED) +{ +} + +void +norm_ptrs_you(struct you *d_you UNUSED) +{ +} +#endif /* SFCTOOL */ + +#undef SF_X +#undef SF_C +#undef SF_A + +/* end of sfbase.c */ + diff --git a/src/sfstruct.c b/src/sfstruct.c index ae0b2c5fd..8a15a0c17 100644 --- a/src/sfstruct.c +++ b/src/sfstruct.c @@ -1,9 +1,310 @@ /* NetHack 3.7 sfstruct.c $NHDT-Date: 1606765215 2020/11/30 19:40:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.4 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ -/*-Copyright (c) Michael Allison, 2009. */ +/*-Copyright (c) Michael Allison, 2025. */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" +#include "sfprocs.h" + +/* #define SFLOGGING */ /* debugging */ + +staticfn void sfstruct_read_error(void); + +/* historical full struct savings */ + +#ifdef SAVEFILE_DEBUGGING +#if defined(__GNUC__) +#define DEBUGFORMATSTR64 "%s %s %ld %ld %d\n" +#elif defined(_MSC_VER) +#define DEBUGFORMATSTR64 "%s %s %lld %ld %d\n" +#endif +#endif + +#ifdef SFLOGGING +staticfn void logging_finish(void); +#endif + +#define SFO_BODY(dt) \ +{ \ + bwrite(nhfp->fd, (genericptr_t) d_##dt, sizeof *d_##dt); \ +} + +#define SFI_BODY(dt) \ +{ \ + if (nhfp->eof) { \ + sfstruct_read_error(); \ + } \ + mread(nhfp->fd, (genericptr_t) d_##dt, sizeof *d_##dt); \ + if (restoreinfo.mread_flags == -1) \ + nhfp->eof = TRUE; \ +} + +#define SF_A(dtyp) \ +void historical_sfo_##dtyp(NHFILE *, dtyp *d_##dtyp, const char *); \ +void historical_sfi_##dtyp(NHFILE *, dtyp *d_##dtyp, const char *); \ + \ +void historical_sfo_##dtyp(NHFILE *nhfp, dtyp *d_##dtyp, \ + const char *myname UNUSED) \ + SFO_BODY(dtyp) \ + \ +void historical_sfi_##dtyp(NHFILE *nhfp, dtyp *d_##dtyp, \ + const char *myname UNUSED) \ + SFI_BODY(dtyp) + +#define SFO_CBODY(dt) \ + { \ + norm_ptrs_##dt(d_##dt); \ + bwrite(nhfp->fd, (genericptr_t) d_##dt, sizeof *d_##dt); \ + } + +#define SFI_CBODY(dt) \ + { \ + if (nhfp->eof) { \ + sfstruct_read_error(); \ + } \ + mread(nhfp->fd, (genericptr_t) d_##dt, sizeof *d_##dt); \ + norm_ptrs_##dt(d_##dt); \ + if (restoreinfo.mread_flags == -1) \ + nhfp->eof = TRUE; \ + } + +#define SF_C(keyw, dtyp) \ +void historical_sfo_##dtyp(NHFILE *, keyw dtyp *d_##dtyp, \ + const char *); \ +void historical_sfi_##dtyp(NHFILE *, keyw dtyp *d_##dtyp, \ + const char *); \ +extern void norm_ptrs_##dtyp(keyw dtyp *d_##dtyp); \ + \ +void historical_sfo_##dtyp(NHFILE *nhfp, keyw dtyp *d_##dtyp, \ + const char *myname UNUSED) \ + SFO_CBODY(dtyp) \ + \ +void historical_sfi_##dtyp(NHFILE *nhfp, keyw dtyp *d_##dtyp, \ + const char *myname UNUSED) \ + SFI_CBODY(dtyp) + +#define SF_X(xxx, dtyp) \ +void historical_sfo_##dtyp(NHFILE *, xxx *d_##dtyp, const char *, int); \ +void historical_sfi_##dtyp(NHFILE *, xxx *d_##dtyp, const char *, int); \ + \ +void historical_sfo_##dtyp(NHFILE *nhfp, xxx *d_##dtyp, \ + const char *myname UNUSED, int bflen UNUSED) \ + SFO_BODY(dtyp) \ + \ +void historical_sfi_##dtyp(NHFILE *nhfp, xxx *d_##dtyp, \ + const char *myname UNUSED, int bflen UNUSED) \ + SFI_BODY(dtyp) + + +#include "sfmacros.h" +SF_C(struct, version_info) + +void historical_sfo_char(NHFILE *, char *d_char, const char *, int); +void historical_sfi_char(NHFILE *, char *d_char, const char *, int); + +void +historical_sfo_char(NHFILE *nhfp, char *d_char, + const char *myname UNUSED, int cnt) +{ + bwrite(nhfp->fd, (genericptr_t) d_char, cnt * sizeof (char)); +} + +void +historical_sfi_char(NHFILE *nhfp, char *d_char, + const char *myname UNUSED, int cnt) +{ + mread(nhfp->fd, (genericptr_t) d_char, cnt * sizeof (char)); + if (restoreinfo.mread_flags == -1) + nhfp->eof = TRUE; +} +//extern void sfo_genericptr(NHFILE *, void **, const char *); +//extern void sfi_genericptr(NHFILE *, void **, const char *); +//extern void sfo_x_genericptr(NHFILE *, void **, const char *); +//extern void sfi_x_genericptr(NHFILE *, void **, const char *); + +void historical_sfo_genericptr_t(NHFILE *, genericptr_t *d_genericptr_t, + const char *); +void historical_sfi_genericptr_t(NHFILE *, genericptr_t *d_genericptr_t, + const char *); +void +historical_sfo_genericptr_t(NHFILE *nhfp, genericptr_t *d_genericptr_t, + const char *myname UNUSED) +{ + bwrite(nhfp->fd, (genericptr_t) d_genericptr_t, sizeof *d_genericptr_t); +} +void +historical_sfi_genericptr_t(NHFILE *nhfp, genericptr_t *d_genericptr_t, + const char *myname UNUSED) +{ + if (nhfp->eof) { + sfstruct_read_error(); + } + mread(nhfp->fd, (genericptr_t) d_genericptr_t, sizeof *d_genericptr_t); + if (restoreinfo.mread_flags == -1) + nhfp->eof = TRUE; +} + +SF_X(uint8_t, bitfield) + +struct sf_structlevel_procs historical_sfo_procs = { + "", + /* sf */ + { + historical_sfo_arti_info, + historical_sfo_nhrect, + historical_sfo_branch, + historical_sfo_bubble, + historical_sfo_cemetery, + historical_sfo_context_info, + historical_sfo_nhcoord, + historical_sfo_damage, + historical_sfo_dest_area, + historical_sfo_dgn_topology, + historical_sfo_dungeon, + historical_sfo_d_level, + historical_sfo_ebones, + historical_sfo_edog, + historical_sfo_egd, + historical_sfo_emin, + historical_sfo_engr, + historical_sfo_epri, + historical_sfo_eshk, + historical_sfo_fe, + historical_sfo_flag, + historical_sfo_fruit, + historical_sfo_gamelog_line, + historical_sfo_kinfo, + historical_sfo_levelflags, + historical_sfo_ls_t, + historical_sfo_linfo, + historical_sfo_mapseen_feat, + historical_sfo_mapseen_flags, + historical_sfo_mapseen_rooms, + historical_sfo_mkroom, + historical_sfo_monst, + historical_sfo_mvitals, + historical_sfo_obj, + historical_sfo_objclass, + historical_sfo_q_score, + historical_sfo_rm, + historical_sfo_spell, + historical_sfo_stairway, + historical_sfo_s_level, + historical_sfo_trap, + historical_sfo_version_info, + historical_sfo_you, + historical_sfo_any, + + historical_sfo_aligntyp, + historical_sfo_boolean, + historical_sfo_coordxy, + historical_sfo_genericptr_t, + historical_sfo_int, + historical_sfo_int16, + historical_sfo_int32, + historical_sfo_int64, + historical_sfo_long, + historical_sfo_schar, + historical_sfo_short, + historical_sfo_size_t, + historical_sfo_time_t, + historical_sfo_uchar, + historical_sfo_uint16, + historical_sfo_uint32, + historical_sfo_uint64, + historical_sfo_ulong, + historical_sfo_unsigned, + historical_sfo_ushort, + historical_sfo_xint16, + historical_sfo_xint8, + historical_sfo_char, + historical_sfo_bitfield, + } +}; + +struct sf_structlevel_procs historical_sfi_procs = { + "", + /* sf */ + { + historical_sfi_arti_info, + historical_sfi_nhrect, + historical_sfi_branch, + historical_sfi_bubble, + historical_sfi_cemetery, + historical_sfi_context_info, + historical_sfi_nhcoord, + historical_sfi_damage, + historical_sfi_dest_area, + historical_sfi_dgn_topology, + historical_sfi_dungeon, + historical_sfi_d_level, + historical_sfi_ebones, + historical_sfi_edog, + historical_sfi_egd, + historical_sfi_emin, + historical_sfi_engr, + historical_sfi_epri, + historical_sfi_eshk, + historical_sfi_fe, + historical_sfi_flag, + historical_sfi_fruit, + historical_sfi_gamelog_line, + historical_sfi_kinfo, + historical_sfi_levelflags, + historical_sfi_ls_t, + historical_sfi_linfo, + historical_sfi_mapseen_feat, + historical_sfi_mapseen_flags, + historical_sfi_mapseen_rooms, + historical_sfi_mkroom, + historical_sfi_monst, + historical_sfi_mvitals, + historical_sfi_obj, + historical_sfi_objclass, + historical_sfi_q_score, + historical_sfi_rm, + historical_sfi_spell, + historical_sfi_stairway, + historical_sfi_s_level, + historical_sfi_trap, + historical_sfi_version_info, + historical_sfi_you, + historical_sfi_any, + + historical_sfi_aligntyp, + historical_sfi_boolean, + historical_sfi_coordxy, + historical_sfi_genericptr_t, + historical_sfi_int, + historical_sfi_int16, + historical_sfi_int32, + historical_sfi_int64, + historical_sfi_long, + historical_sfi_schar, + historical_sfi_short, + historical_sfi_size_t, + historical_sfi_time_t, + historical_sfi_uchar, + historical_sfi_uint16, + historical_sfi_uint32, + historical_sfi_uint64, + historical_sfi_ulong, + historical_sfi_unsigned, + historical_sfi_ushort, + historical_sfi_xint16, + historical_sfi_xint8, + historical_sfi_char, + historical_sfi_bitfield, + } +}; + +/* + * The historical bwrite() and mread() functions follow + */ + +#ifdef SAVEFILE_DEBUGGING +static long floc = 0L; +#endif /* * historical structlevel savefile writing and reading routines follow. @@ -28,6 +329,19 @@ static int bw_buffered[MAXFD] = {0,0,0,0,0}; static FILE *bw_FILE[MAXFD] = {0,0,0,0,0}; #endif +#ifdef SFLOGGING +static FILE *ofp[20] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + char ofnamebuf[80]; + static long ocnt = 0L; + + static FILE *ifp[20] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + char ifnamebuf[80]; + static long icnt = 0L; +#endif + + /* * Presumably, the fdopen() to allow use of stdio fwrite() * over write() was done for performance or functionality @@ -146,6 +460,17 @@ bclose(int fd) /* return the idx to the pool */ bw_sticky[idx] = -1; } +#ifdef SFLOGGING + if (fd >= 0 && fd <= SIZE(ofp)) { + if (ofp[fd]) { + fclose(ofp[fd]); + ofp[fd] = 0; + } else if (ifp[fd]) { + fclose(ifp[fd]); + ifp[fd] = 0; + } + } +#endif return; } @@ -171,6 +496,20 @@ bwrite(int fd, const genericptr_t loc, unsigned num) boolean failed; int idx = getidx(fd, NOFLG); +#ifdef SFLOGGING + if (fd >= 0 && fd < SIZE(ofp)) { + if (!ofp[fd]) { + Snprintf(ofnamebuf, sizeof ofnamebuf, "bwrite_%02d.log", fd); + ofp[fd] = fopen(ofnamebuf, "w"); + } + if (ofp[fd]) { + fprintf(ofp[fd], "%08ld, %08ld, %d\n", ocnt, + ftell(ofp[fd]), num); + ocnt++; + } + } +#endif + if (idx >= 0) { if (num == 0) { /* nothing to do; we need a special case to exit early @@ -206,16 +545,25 @@ bwrite(int fd, const genericptr_t loc, unsigned num) /* ===================================================== */ -void -minit(void) -{ - return; -} - void mread(int fd, genericptr_t buf, unsigned len) { -#if defined(BSD) || defined(ULTRIX) + +#ifdef SFLOGGING + if (fd >= 0 && fd < 9) { + if (!ifp[fd]) { + Snprintf(ifnamebuf, sizeof ifnamebuf, "mread_%02d.log", fd); + ifp[fd] = fopen(ifnamebuf, "w"); + } + if (ifp[fd]) { + fprintf(ifp[fd], "%08ld, %08ld, %d\n", icnt, + ftell(ifp[fd]), (int) len); + icnt++; + } + } +#endif + +#if defined(BSD) || defined(ULTRIX) || defined(WIN32) #define readLenType int #else /* e.g. SYSV, __TURBOC__ */ #define readLenType unsigned @@ -228,6 +576,7 @@ mread(int fd, genericptr_t buf, unsigned len) restoreinfo.mread_flags = -1; return; } else { +#ifndef SFCTOOL pline("Read %d instead of %u bytes.", (int) rlen, len); display_nhwindow(WIN_MESSAGE, TRUE); /* flush before error() */ if (program_state.restoring) { @@ -236,8 +585,46 @@ mread(int fd, genericptr_t buf, unsigned len) error("Error restoring old game."); } panic("Error reading level file."); +#else + printf("Read %d instead of %u bytes.\n", (int) rlen, len); +#endif } } } +staticfn void +sfstruct_read_error(void) +{ + /* problem */; +} + +#ifdef SFLOGGING +staticfn void +logging_finish(void) +{ + int i; + + for (i = 0; i < SIZE(ofp); ++i) { + if (ofp[i]) { + fclose(ofp[i]); + ofp[i] = 0; + } + } + ocnt = 0L; + + for (i = 0; i < SIZE(ifp); ++i) { + if (ifp[i]) { + fclose(ifp[i]); + ifp[i] = 0; + } + } + icnt = 0L; +} +#endif /* SFLOGGING */ + +#undef SF_X +#undef SF_C +#undef SF_A + +/* end of sfstruct.c */ diff --git a/src/shk.c b/src/shk.c index 0ec28f217..3463effbd 100644 --- a/src/shk.c +++ b/src/shk.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 shk.c $NHDT-Date: 1720717993 2024/07/11 17:13:13 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.298 $ */ +/* NetHack 3.7 shk.c $NHDT-Date: 1736516428 2025/01/10 05:40:28 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.306 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -27,17 +27,21 @@ enum billitem_status { KnownContainer = 5, /* container->cknown==1, holding unpaid item(s) */ UndisclosedContainer = 6, /* container->cknown==0 */ }; -/* this is similar to sortloot; the shop bill gets converted into a array of +/* this is similar to sortloot; the shop bill gets converted into an array of struct sortbill_item so that sorting and traversal don't need to access the original bill or even the shk; the array gets sorted by usedup vs unpaid and by cost within each of those two categories */ struct sortbill_item { struct obj *obj; - long cost; - long quan; - int bidx; - int8 usedup; /* small but signed */ - boolean queuedpay; + long cost; /* full amount for current quantity, not per-unit amount */ + long quan; /* count for this entry; subset if this is partly used or + * partly intact */ + int bidx; /* index into ESHK(shkp)->bill_p[]; hero-owned container, + * which isn't in bill_p[], uses bidx == -1 */ + int8 usedup; /* billitem_status, small but needs to be signed for qsort() + * [for an earlier edition; 'signed' no longer necessary] */ + boolean queuedpay; /* buy without asking when containers are involved + * or purchase targets have been picked via menu */ }; typedef struct sortbill_item Bill; @@ -70,6 +74,7 @@ staticfn void clear_unpaid_obj(struct monst *, struct obj *); staticfn void clear_unpaid(struct monst *, struct obj *); staticfn void clear_no_charge_obj(struct monst *, struct obj *); staticfn void clear_no_charge(struct monst *, struct obj *); +staticfn void clear_no_charge_pets(struct monst *); staticfn long check_credit(long, struct monst *); staticfn void pay(long, struct monst *); staticfn long get_cost(struct obj *, struct monst *); @@ -115,7 +120,7 @@ staticfn uint8 litter_getpos(uint8 *, coordxy, coordxy, struct monst *); staticfn void litter_scatter(uint8 *, coordxy, coordxy, struct monst *); staticfn void litter_newsyms(uint8 *, coordxy, coordxy); staticfn int repair_damage(struct monst *, struct damage *, boolean); -staticfn void sub_one_frombill(struct obj *, struct monst *); +staticfn void sub_one_frombill(struct obj *, struct monst *) NONNULLPTRS; staticfn void add_one_tobill(struct obj *, boolean, struct monst *); staticfn void dropped_container(struct obj *, struct monst *, boolean); staticfn void add_to_billobjs(struct obj *); @@ -379,6 +384,17 @@ clear_no_charge(struct monst *shkp, struct obj *list) } } +/* clear no_charge from objects in pets' inventories belonging to shkp */ +staticfn void +clear_no_charge_pets(struct monst *shkp) +{ + struct monst *mtmp; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (mtmp->mtame && mtmp->minvent) + clear_no_charge(shkp, mtmp->minvent); +} + /* either you paid or left the shop or the shopkeeper died */ void setpaid(struct monst *shkp) @@ -1115,6 +1131,8 @@ obfree(struct obj *obj, struct obj *merge) delete_contents(obj); if (Is_container(obj)) maybe_reset_pick(obj); + if (obj->otyp == BOULDER) + obj->next_boulder = 0; shkp = 0; if (obj->unpaid) { @@ -1157,19 +1175,12 @@ obfree(struct obj *obj, struct obj *merge) merge->unpaid ? 1 : 0); return; } else { + struct eshk *eshkp = ESHK(shkp); + /* this was a merger */ bpm->bquan += bp->bquan; - ESHK(shkp)->billct--; -#ifdef DUMB - { - /* DRS/NS 2.2.6 messes up -- Peter Kendell */ - int indx = ESHK(shkp)->billct; - - *bp = ESHK(shkp)->bill_p[indx]; - } -#else - *bp = ESHK(shkp)->bill_p[ESHK(shkp)->billct]; -#endif + eshkp->billct--; + *bp = eshkp->bill_p[eshkp->billct]; } } else { /* not on bill; if the item is being merged away rather than @@ -1379,6 +1390,7 @@ hot_pursuit(struct monst *shkp) floor of this level (including inside containers on floor), even those that are in other shopkeepers' shops */ clear_no_charge((struct monst *) NULL, fobj); + clear_no_charge_pets(shkp); } /* Used when the shkp is teleported or falls (ox == 0) out of his shop, or @@ -1457,6 +1469,7 @@ cheapest_item(int ibillct, Bill *ibill) return gmin; } + /* for itemized purchasing, create an alternate shop bill that hides container contents */ staticfn int /* returns number of entries */ @@ -1469,7 +1482,7 @@ make_itemized_bill( struct bill_x *bp; struct obj *otmp; struct eshk *eshkp = ESHK(shkp); - int i, n, ebillct = eshkp->billct; + int i, n, bidx, ebillct = eshkp->billct; int8 used; long quan, cost; @@ -1491,6 +1504,7 @@ make_itemized_bill( impossible("Can't find shop bill entry for #%d", bp->bo_id); continue; } + bidx = i; /* index into bill_p[], except for hero-owner container */ if (otmp->quan == 0L || otmp->where == OBJ_ONBILL) { /* item is completely used up; restore quantity from when it @@ -1506,7 +1520,7 @@ make_itemized_bill( ibill[n].obj = otmp; ibill[n].quan = bp->bquan - otmp->quan; ibill[n].cost = bp->price * ibill[n].quan; - ibill[n].bidx = i; /* duplicate index into eshkp->bill_p[] */ + ibill[n].bidx = bidx; /* duplicate index into eshkp->bill_p[] */ ibill[n].usedup = PartlyUsedUp; /* for sorting */ ++n; /* intact portion will be a separate entry, next */ } @@ -1547,6 +1561,8 @@ make_itemized_bill( /* include 1 container containing unpaid item(s) */ quan = 1L; cost = unpaid_cost(otmp, COST_CONTENTS); + if (!otmp->unpaid) + bidx = -1; /* an unpaid container without any unpaid contents is classified as 'FullyIntact'; a container with unpaid contents will be '*Container' regardless of whether it is unpaid itself */ @@ -1564,13 +1580,14 @@ make_itemized_bill( ibill[n].obj = otmp; ibill[n].quan = quan; ibill[n].cost = cost; - ibill[n].bidx = i; + ibill[n].bidx = bidx; ibill[n].usedup = used; ++n; } ibill[n].bidx = -1; /* end of list; not strictly needed */ - /* ibill[0..n-1] contains data, ibill[n] has Null obj and -1 bidx */ + /* ibill[0..n-1] contains data, ibill[n] has Null obj and -1 bidx and + is excluded from the sort */ if (n > 1) qsort((genericptr_t) ibill, n, sizeof *ibill, sortbill_cmp); return n; @@ -1716,6 +1733,7 @@ dopay(void) shkp = next_shkp(shkp->nmon, FALSE)) if (canspotmon(shkp)) break; + assert(shkp != NULL); /* seensk==1 => traversal will spot one shk */ if (shkp != resident && !m_next2u(shkp)) { pline("%s is not near enough to receive your payment.", Shknam(shkp)); @@ -2033,11 +2051,7 @@ pay_billed_items( if (queuedpay && !ibill[indx].queuedpay) continue; - bidx = ibill[indx].bidx; - bp = &eshkp->bill_p[bidx]; - otmp = ibill[indx].obj; - pass = (ibill[indx].usedup <= PartlyUsedUp) ? 0 : 1; - + otmp = ibill[indx].obj; /* ordinary object or outermost container */ if (ibill[indx].usedup >= KnownContainer) { /* when successfull, buy_container() will call both dopayobj() and update_bill(), possibly multiple times */ @@ -2053,6 +2067,10 @@ pay_billed_items( buy = PAY_CANT; } } else { + bidx = ibill[indx].bidx; + bp = &eshkp->bill_p[bidx]; + pass = (ibill[indx].usedup <= PartlyUsedUp) ? 0 : 1; + buy = dopayobj(shkp, bp, otmp, pass, itemize, FALSE); if (buy == PAY_BUY) @@ -2082,7 +2100,7 @@ pay_billed_items( /* update shk's bill and augmented bill after an item has been purchased */ staticfn void update_bill( - int indx, + int indx, /* index into ibill[]; -1 for unpaid contained item */ int ibillct, Bill *ibill, struct eshk *eshkp, @@ -2090,12 +2108,11 @@ update_bill( struct obj *paiditem) { int j, newebillct; - int bidx = ibill[indx].bidx; /* remove from eshkp->bill_p[] unless this was the used up portion of partly used item (since removal would take out both; note: can't buy PartlyIntact until PartlyUsedUp has been paid for) */ - if (ibill[indx].usedup == PartlyUsedUp) { + if (indx >= 0 && ibill[indx].usedup == PartlyUsedUp) { /* 'paiditem' points to the partly intact portion still in invent or inside a container (ibill[indx].obj points to the container) */ bp->bquan = paiditem->quan; @@ -2109,7 +2126,7 @@ update_bill( from shop bill; if it was used up, remove it from the billobjs list and delete it; update shop's bill by moving last bill_p[] entry into vacated slot; also update ibill[] indices for that */ - paiditem->unpaid = 0; /* set before maybe deallocating */ + paiditem->unpaid = 0; /* clear before maybe deallocating */ if (paiditem->where == OBJ_ONBILL) { obj_extract_self(paiditem); dealloc_obj(paiditem); @@ -2118,7 +2135,7 @@ update_bill( *bp = eshkp->bill_p[newebillct]; for (j = 0; j < ibillct; ++j) if (ibill[j].bidx == newebillct) - ibill[j].bidx = bidx; + ibill[j].bidx = (int) (bp - eshkp->bill_p); eshkp->billct = newebillct; /* eshkp->billct - 1 */ } return; @@ -2135,7 +2152,7 @@ dopayobj( struct monst *shkp, struct bill_x *bp, struct obj *obj, - int which /* 0 => used-up item, 1 => other (unpaid or lost) */, + int which, /* 0 => used-up item, 1 => other (unpaid or lost) */ boolean itemize, boolean unseen) { @@ -2229,9 +2246,8 @@ buy_container( unsigned boid, boids[BILLSZ]; int i, j, buy, buycount = 0, boidsct = 0; struct eshk *eshkp = ESHK(shkp); - int bidx = ibill[indx].bidx, - ebillct = eshkp->billct; - struct bill_x *bp = &eshkp->bill_p[bidx]; + int ebillct = eshkp->billct; + struct bill_x *bp; struct obj *otmp, *otop, *container = ibill[indx].obj; unsigned unpaidcontainer = container->unpaid; @@ -2294,10 +2310,15 @@ buy_container( otmp = bp_to_obj(bp); buy = dopayobj(shkp, bp, otmp, 1, FALSE, sightunseen); - if (buy != PAY_BUY) + if (buy != PAY_BUY) { impossible("Buying %s contents failed unexpectedly (#%u %d).", simpleonames(container), otmp->o_id, buy); - update_bill(indx, ibillct, ibill, eshkp, bp, otmp); + continue; + } + /* [updating cost here is not necessary but useful when debugging] */ + ibill[indx].cost -= (bp->price * bp->bquan); /* update container */ + update_bill((boid == container->o_id) ? indx : -1, + ibillct, ibill, eshkp, bp, otmp); ++buycount; } if (buycount && sightunseen) { @@ -3127,7 +3148,7 @@ gem_learned(int oindx) for (shkp = next_shkp(fmon, TRUE); shkp; shkp = next_shkp(shkp->nmon, TRUE)) { ct = ESHK(shkp)->billct; - bp = ESHK(shkp)->bill; + bp = ESHK(shkp)->bill_p; while (--ct >= 0) { obj = find_oid(bp->bo_id); if (!obj) /* shouldn't happen */ @@ -3567,6 +3588,7 @@ staticfn void sub_one_frombill(struct obj *obj, struct monst *shkp) { struct bill_x *bp; + struct eshk *eshkp; if ((bp = onbill(obj, shkp, FALSE)) != 0) { struct obj *otmp; @@ -3584,17 +3606,9 @@ sub_one_frombill(struct obj *obj, struct monst *shkp) add_to_billobjs(otmp); return; } - ESHK(shkp)->billct--; -#ifdef DUMB - { - /* DRS/NS 2.2.6 messes up -- Peter Kendell */ - int indx = ESHK(shkp)->billct; - - *bp = ESHK(shkp)->bill_p[indx]; - } -#else - *bp = ESHK(shkp)->bill_p[ESHK(shkp)->billct]; -#endif + eshkp = ESHK(shkp); + eshkp->billct--; + *bp = eshkp->bill_p[eshkp->billct]; return; } else if (obj->unpaid) { impossible("sub_one_frombill: unpaid object not on bill"); @@ -3642,6 +3656,7 @@ stolen_container( /* billable() returns false for objects already on bill */ if ((bp = onbill(otmp, shkp, FALSE)) == 0) continue; + assert(shkp != NULL); /* onbill() found shkp so it's not Null */ /* this assumes that we're being called by stolen_value() (or by a recursive call to self on behalf of it) where the cost of this object is about to be added to shop @@ -3695,6 +3710,7 @@ stolen_value( /* things already on the bill yield a not-billable result, so we need to check bill before deciding that shk doesn't care */ if ((bp = onbill(obj, shkp, FALSE)) != 0) { + assert(shkp != NULL); /* onbill() found shkp so it's not Null */ /* shk does care; take obj off bill to avoid double billing */ billamt = bp->bquan * bp->price; sub_one_frombill(obj, shkp); @@ -3872,6 +3888,21 @@ sellobj( offer = ltmp + cltmp; + /* you dropped something of your own - probably want to sell it */ + rouse_shk(shkp, TRUE); /* wake up sleeping or paralyzed shk */ + eshkp = ESHK(shkp); + + if (ANGRY(shkp)) { /* they become shop-objects, no pay */ + if (!Deaf && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); + verbalize("Thank you, scum!"); + } else { + pline("%s smirks with satisfaction.", Shknam(shkp)); + } + subfrombill(obj, shkp); + return; + } + /* get one case out of the way: nothing to sell, and no gold */ if (!(isgold || cgold) && ((offer + gltmp) == 0L || gs.sell_how == SELL_DONTSELL)) { @@ -3892,21 +3923,6 @@ sellobj( return; } - /* you dropped something of your own - probably want to sell it */ - rouse_shk(shkp, TRUE); /* wake up sleeping or paralyzed shk */ - eshkp = ESHK(shkp); - - if (ANGRY(shkp)) { /* they become shop-objects, no pay */ - if (!Deaf && !muteshk(shkp)) { - SetVoice(shkp, 0, 80, 0); - verbalize("Thank you, scum!"); - } else { - pline("%s smirks with satisfaction.", Shknam(shkp)); - } - subfrombill(obj, shkp); - return; - } - if (eshkp->robbed) { /* bones; shop robbed by previous customer */ if (isgold) offer = obj->quan; @@ -4068,6 +4084,7 @@ sellobj( switch (gs.sell_response ? gs.sell_response : nyaq(qbuf)) { case 'q': gs.sell_response = 'n'; + FALLTHROUGH; /*FALLTHRU*/ case 'n': if (container) @@ -4078,6 +4095,7 @@ sellobj( break; case 'a': gs.sell_response = 'y'; + FALLTHROUGH; /*FALLTHRU*/ case 'y': if (container) @@ -4447,9 +4465,8 @@ discard_damage_owned_by(struct monst *shkp) prevdam->next = dam2; if (dam == svl.level.damagelist) svl.level.damagelist = dam2; - (void) memset(dam, 0, sizeof(struct damage)); - free((genericptr_t) dam); - dam = dam2; + (void) memset(dam, 0, sizeof *dam); + free((genericptr_t) dam), dam = (struct damage *) NULL; } else { prevdam = dam; dam2 = dam->next; @@ -5926,16 +5943,7 @@ globby_bill_fixup(struct obj *obj_absorber, struct obj *obj_absorbed) /* the glob being absorbed has a billing record */ amount = bp->price; eshkp->billct--; -#ifdef DUMB - { - /* DRS/NS 2.2.6 messes up -- Peter Kendell */ - int indx = eshkp->billct; - - *bp = eshkp->bill_p[indx]; - } -#else *bp = eshkp->bill_p[eshkp->billct]; -#endif clear_unpaid_obj(shkp, obj_absorbed); if (bp_absorber) { diff --git a/src/shknam.c b/src/shknam.c index 7bd9f4e21..ecf12f938 100644 --- a/src/shknam.c +++ b/src/shknam.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 shknam.c $NHDT-Date: 1715203028 2024/05/08 21:17:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.78 $ */ +/* NetHack 3.7 shknam.c $NHDT-Date: 1736530208 2025/01/10 09:30:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.82 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -201,7 +201,7 @@ static const char *const shkhealthfoods[] = { * *_CLASS enum value) or a specific object enum value. * In the latter case, prepend it with a unary minus so the code can know * (by testing the sign) whether to use mkobj() or mksobj(). - * shtypes[] is externally referenced from mkroom.c, mon.c and shk.c. + * shtypes[] is externally referenced from mkroom.c, mon.c and shk.c. * * The second, usually shorter, store type name is used in automatically * generated annotations for #overview. If Null, the first name gets used. @@ -411,8 +411,8 @@ shkveg(void) char oclass = FOOD_CLASS; int ok[NUM_OBJECTS]; + (void) memset((genericptr_t) ok, 0, sizeof ok); /* lint suppression */ j = maxprob = 0; - ok[0] = 0; /* lint suppression */ for (i = svb.bases[(int) oclass]; i < NUM_OBJECTS; ++i) { if (objects[i].oc_class != oclass) break; @@ -511,7 +511,7 @@ nameshk(struct monst *shk, const char *const *nlp) for (names_avail = 0; nlp[names_avail]; names_avail++) continue; - + assert(names_avail > 0); name_wanted = name_wanted % names_avail; for (trycnt = 0; trycnt < 50; trycnt++) { @@ -561,6 +561,7 @@ neweshk(struct monst *mtmp) if (!ESHK(mtmp)) ESHK(mtmp) = (struct eshk *) alloc(sizeof(struct eshk)); (void) memset((genericptr_t) ESHK(mtmp), 0, sizeof(struct eshk)); + ESHK(mtmp)->parentmid = mtmp->m_id; ESHK(mtmp)->bill_p = (struct bill_x *) 0; } @@ -754,7 +755,7 @@ stock_room(int shp_indx, struct mkroom *sroom) else if (inside_shop(sx, sy - 1)) n++; Sprintf(buf, "Closed for inventory"); - make_engr_at(m, n, buf, 0L, DUST); + make_engr_at(m, n, buf, NULL, 0L, DUST); if (levl[m][n].typ != CORR && levl[m][n].typ != ROOM) levl[m][n].typ = (Is_special(&u.uz) || *in_rooms(m, n, 0)) ? ROOM : CORR; diff --git a/src/sit.c b/src/sit.c index 82fccb6e9..40628c1b0 100644 --- a/src/sit.c +++ b/src/sit.c @@ -7,6 +7,7 @@ #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 */ @@ -32,12 +33,16 @@ take_gold(void) } } +staticfn void special_throne_effect(int effect); + /* maybe do something when hero sits on a throne */ staticfn void throne_sit_effect(void) { coordxy tx = u.ux, ty = u.uy; + boolean special_throne = !!In_V_tower(&u.uz); + if (rnd(6) > 4) { /* [why so convoluted? it's the same as '!rn2(3)'] */ int effect = rnd(13); @@ -56,6 +61,11 @@ throne_sit_effect(void) effect = which; } + if (special_throne) { + special_throne_effect(effect); + return; + } + switch (effect) { case 1: (void) adjattrib(rn2(A_MAX), -rn1(4, 3), FALSE); @@ -158,6 +168,7 @@ throne_sit_effect(void) default: case 2: /* more than 1 eye */ eye = makeplural(eye); + FALLTHROUGH; /*FALLTHRU*/ case 1: /* one eye (Cyclops, floating eye) */ Your("%s %s...", eye, vtense(eye, "tingle")); @@ -211,7 +222,8 @@ throne_sit_effect(void) only happened when teleporting back to the same point where hero started from.] "Analyzing a throne" doesn't really make any sense but if the answer is yes than it will vanish in a puff of logic. */ - if (!rn2(3) && (!wizard || y_n("Analyze throne?") == 'y')) { + if (!special_throne && + !rn2(3) && (!wizard || y_n("Analyze throne?") == 'y')) { levl[tx][ty].typ = ROOM, levl[tx][ty].flags = 0; map_background(tx, ty, FALSE); newsym_force(tx, ty); @@ -222,6 +234,110 @@ throne_sit_effect(void) } } +/* special throne in Vlad's tower: effect is 1 to 13 inclusive */ +staticfn void +special_throne_effect(int effect) { + coordxy tx = u.ux, ty = u.uy; + + switch (effect) { + case 1: + case 2: + case 3: + case 4: + /* 4 chances of a wish, but then the throne disappears. + + This is the only way the throne can disappear from sitting + on it, so if you sit on it enough (enduring the negative + effects) you are guaranteed an eventual wish. */ + makewish(); + levl[tx][ty].typ = ROOM, levl[tx][ty].flags = 0; + map_background(tx, ty, FALSE); + newsym_force(tx, ty); + pline_The("throne disintegrates, having spent its power."); + break; + case 5: + /* permanent level drain */ + pline("Sitting on the throne was a terrible experience."); + if (!Drain_resistance) { + losexp("a bad experience sitting on a throne"); + if (u.ulevelmax > u.ulevel) + u.ulevelmax -= 1; + } + break; + case 6: + /* containers become cursed */ + rndcurse_inner(TRUE); + break; + case 7: + /* lose an intrinsic */ + attrcurse(); + pline_The("throne somehow seems to be amused."); + break; + case 8: + { + /* level teleport to Vibrating Square level */ + d_level vs_level; + find_hell(&vs_level); + vs_level.dlevel = svd.dungeons[vs_level.dnum].num_dunlevs - 1; + if (u.uhave.amulet) + You_feel("extremely disoriented for a moment."); + else + schedule_goto( + &vs_level, UTOTYPE_NONE, (char *) 0, + "You feel extremely out of place."); + break; + } + case 9: + { + /* summon demons; a NULL argument to msummon summons demons as + though they were summoned by the Wizard of Yendor */ + pline_The("throne seeems to be calling for help!"); + msummon(NULL); + msummon(NULL); + msummon(NULL); + break; + } + case 10: + { + /* confused blessed remove curse effect */ + struct obj fake_spellbook; + long save_confusion = HConfusion; + + fake_spellbook = cg.zeroobj; + fake_spellbook.otyp = SPE_REMOVE_CURSE; + fake_spellbook.oclass = SPBOOK_CLASS; + fake_spellbook.blessed = 1; + HConfusion = 1L; + (void) seffects(&fake_spellbook); + HConfusion = save_confusion; + break; + } + case 11: + /* polymorph effect (not blocked by magic resistance) */ + pline("This throne was not meant for those such as you!"); + You_feel("a change coming over you."); + polyself(POLY_NOFLAGS); + break; + case 12: + /* acid damage */ + pline("The throne is covered in acid!"); + losehp(Acid_resistance ? rnd(16) : rnd(80), "acidic chair", + KILLED_BY_AN); + exercise(A_CON, FALSE); + break; + case 13: + { + /* ability shuffle */ + int ability; + pline("As you sit on the throne, your body and mind start to warp."); + for (ability = 0; ability < A_MAX; ++ability) { + adjattrib(ability, rn2(5) - 2, -1); + } + break; + } + } +} + /* hero lays an egg */ staticfn int lay_an_egg(void) @@ -315,7 +431,10 @@ dosit(void) } else if (obj->otyp == TOWEL) { pline("It's probably not a good time for a picnic..."); } else { - You("sit on %s.", the(xname(obj))); + if (slithy(gy.youmonst.data)) + You("coil up around %s.", the(xname(obj))); + else + You("sit on %s.", the(xname(obj))); if (obj->otyp == CORPSE && amorphous(&mons[obj->corpsenm])) pline("It's squishy..."); else if (obj->otyp == CREAM_PIE) { @@ -432,6 +551,12 @@ 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; @@ -445,23 +570,30 @@ rndcurse(void) if (Antimagic) { shieldeff(u.ux, u.uy); - You(mal_aura, "you"); } + You(mal_aura, "you"); + for (otmp = gi.invent; otmp; otmp = otmp->nobj) { /* 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 = rnd(6 / ((!!Antimagic) + (!!Half_spell_damage) + 1)); - cnt > 0; cnt--) { + for (; cnt > 0; cnt--) { onum = rnd(nobj); for (otmp = gi.invent; otmp; otmp = otmp->nobj) { /* as above */ if (otmp->oclass == COIN_CLASS) continue; + if (prefer_containers && !otmp->cobj) + continue; if (--onum == 0) break; /* found the target */ } @@ -517,6 +649,7 @@ attrcurse(void) ret = FIRE_RES; break; } + FALLTHROUGH; /*FALLTHRU*/ case 2: if (HTeleportation & INTRINSIC) { @@ -525,6 +658,7 @@ attrcurse(void) ret = TELEPORT; break; } + FALLTHROUGH; /*FALLTHRU*/ case 3: if (HPoison_resistance & INTRINSIC) { @@ -533,6 +667,7 @@ attrcurse(void) ret = POISON_RES; break; } + FALLTHROUGH; /*FALLTHRU*/ case 4: if (HTelepat & INTRINSIC) { @@ -543,6 +678,7 @@ attrcurse(void) ret = TELEPAT; break; } + FALLTHROUGH; /*FALLTHRU*/ case 5: if (HCold_resistance & INTRINSIC) { @@ -551,6 +687,7 @@ attrcurse(void) ret = COLD_RES; break; } + FALLTHROUGH; /*FALLTHRU*/ case 6: if (HInvis & INTRINSIC) { @@ -559,6 +696,7 @@ attrcurse(void) ret = INVIS; break; } + FALLTHROUGH; /*FALLTHRU*/ case 7: if (HSee_invisible & INTRINSIC) { @@ -574,6 +712,7 @@ attrcurse(void) ret = SEE_INVIS; break; } + FALLTHROUGH; /*FALLTHRU*/ case 8: if (HFast & INTRINSIC) { @@ -582,6 +721,7 @@ attrcurse(void) ret = FAST; break; } + FALLTHROUGH; /*FALLTHRU*/ case 9: if (HStealth & INTRINSIC) { @@ -590,6 +730,7 @@ attrcurse(void) ret = STEALTH; break; } + FALLTHROUGH; /*FALLTHRU*/ case 10: /* intrinsic protection is just disabled, not set back to 0 */ @@ -599,6 +740,7 @@ attrcurse(void) ret = PROTECTION; break; } + FALLTHROUGH; /*FALLTHRU*/ case 11: if (HAggravate_monster & INTRINSIC) { @@ -607,6 +749,7 @@ attrcurse(void) ret = AGGRAVATE_MONSTER; break; } + FALLTHROUGH; /*FALLTHRU*/ default: break; diff --git a/src/sounds.c b/src/sounds.c index eac50772a..599469de8 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 sounds.c $NHDT-Date: 1674548234 2023/01/24 08:17:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.134 $ */ +/* NetHack 3.7 sounds.c $NHDT-Date: 1736530208 2025/01/10 09:30:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.165 $ */ /* Copyright (c) 1989 Janet Walz, Mike Threepoint */ /* NetHack may be freely redistributed. See license for details. */ @@ -10,7 +10,6 @@ staticfn boolean morgue_mon_sound(struct monst *); staticfn boolean zoo_mon_sound(struct monst *); staticfn boolean temple_priest_sound(struct monst *); staticfn boolean mon_is_gecko(struct monst *); -staticfn int domonnoise(struct monst *); staticfn int dochat(void); staticfn struct monst *responsive_mon_at(int, int); staticfn int mon_in_room(struct monst *, int); @@ -264,6 +263,7 @@ dosounds(void) break; } } + FALLTHROUGH; /*FALLTHRU*/ case 0: Soundeffect(se_guards_footsteps, 30); @@ -631,7 +631,6 @@ cry_sound(struct monst *mtmp) ret = "hiss"; break; case MS_ROAR: /* baby dragons; have them growl instead of roar */ - /*FALLTHRU*/ case MS_GROWL: /* (none) */ ret = "growl"; break; @@ -675,7 +674,7 @@ mon_is_gecko(struct monst *mon) DISABLE_WARNING_FORMAT_NONLITERAL -staticfn int /* check calls to this */ +int /* check calls to this */ domonnoise(struct monst *mtmp) { char verbuf[BUFSZ]; @@ -870,6 +869,7 @@ domonnoise(struct monst *mtmp) } break; } + FALLTHROUGH; /*FALLTHRU*/ case MS_GROWL: Soundeffect((mtmp->mpeaceful ? se_snarl : se_growl), 80); @@ -1019,6 +1019,7 @@ domonnoise(struct monst *mtmp) } break; } + FALLTHROUGH; /*FALLTHRU*/ case MS_HUMANOID: if (!mtmp->mpeaceful) { @@ -1141,7 +1142,8 @@ domonnoise(struct monst *mtmp) (void) demon_talk(mtmp); break; } - /* fall through */ + FALLTHROUGH; + /* FALLTHRU */ case MS_CUSS: if (!mtmp->mpeaceful) cuss(mtmp); @@ -1228,7 +1230,7 @@ domonnoise(struct monst *mtmp) and without quotation marks */ char tmpbuf[BUFSZ]; pline1(ucase(strcpy(tmpbuf, verbl_msg))); - SetVoice((struct monst *) 0, 0, 80, voice_death); + SetVoice((struct monst *) 0, 0, 80, voice_death); sound_speak(tmpbuf); } else { SetVoice(mtmp, 0, 80, 0); @@ -1876,6 +1878,21 @@ get_soundlib_name(char *dest, int maxlen) *dest = '\0'; } +enum soundlib_ids +soundlib_id_from_opt(char *op) +{ + int idx; + struct sound_procs *defproc = &nosound_procs, + *sp = 0; + + for (idx = 0; idx < SIZE(soundlib_choices); ++idx) { + sp = soundlib_choices[idx].sndprocs; + if (!strcmp(sp->soundname, op)) + return sp->soundlib_id; + } + return defproc->soundlib_id; +} + /* * The default sound interface * @@ -2152,14 +2169,14 @@ set_voice( if (gv.voice.nameid) free((genericptr_t) gv.voice.nameid); gv.voice.gender = gender; - gv.voice.serialno = mtmp ? mtmp->m_id + gv.voice.serialno = mtmp ? mtmp->m_id : ((moreinfo & voice_talking_artifact) != 0) ? 3 : ((moreinfo & voice_deity) != 0) ? 4 : 2; gv.voice.tone = tone; gv.voice.volume = volume; gv.voice.moreinfo = moreinfo; gv.voice.nameid = (const char *) 0; - gp.pline_flags |= PLINE_SPEECH; + gp.pline_flags |= PLINE_SPEECH; #endif } @@ -2196,7 +2213,7 @@ sound_speak(const char *text SPEECHONLY) *cpdst = '\0'; } (*soundprocs.sound_verbal)(buf, gv.voice.gender, gv.voice.tone, - gv.voice.volume, gv.voice.moreinfo); + gv.voice.volume, gv.voice.moreinfo); } #endif } diff --git a/src/sp_lev.c b/src/sp_lev.c index 316385c29..3bccc196c 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 sp_lev.c $NHDT-Date: 1709921020 2024/03/08 18:03:40 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.359 $ */ +/* NetHack 3.7 sp_lev.c $NHDT-Date: 1737610109 2025/01/22 21:28:29 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.373 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -548,6 +548,7 @@ flip_level( timer_element *timer; boolean ball_active = FALSE, ball_fliparea = FALSE; stairway *stway; + struct exclusion_zone *ez; /* nothing to do unless (flp & 1) or (flp & 2) or both */ if ((flp & 3) == 0) @@ -652,6 +653,8 @@ flip_level( if (flp & 2) mtmp->mx = FlipX(mtmp->mx); + Flip_coord(mtmp->mgoal); + if (mtmp->ispriest) { Flip_coord(EPRI(mtmp)->shrpos); } else if (mtmp->isshk) { @@ -872,6 +875,28 @@ flip_level( } } + /* exclusion zones */ + for (ez = sve.exclusion_zones; ez; ez = ez->next) { + if (flp & 1) { + ez->ly = FlipY(ez->ly); + ez->hy = FlipY(ez->hy); + if (ez->ly > ez->hy) { + itmp = ez->ly; + ez->ly = ez->hy; + ez->hy = itmp; + } + } + if (flp & 2) { + ez->lx = FlipX(ez->lx); + ez->hx = FlipX(ez->hx); + if (ez->lx > ez->hx) { + itmp = ez->lx; + ez->lx = ez->hx; + ez->hx = itmp; + } + } + } + if (extras) { /* for #wizfliplevel rather than during level creation */ /* flip hero location only if inside the flippable area */ if (inFlipArea(u.ux, u.uy)) { @@ -1268,7 +1293,7 @@ is_ok_location(coordxy x, coordxy y, getloc_flags_t humidity) if (humidity & ANY_LOC) return TRUE; - if ((humidity & SOLID) && IS_ROCK(typ)) + if ((humidity & SOLID) && IS_OBSTRUCTED(typ)) return TRUE; if ((humidity & (DRY|SPACELOC)) && SPACE_POS(typ)) { @@ -1735,7 +1760,7 @@ create_door(room_door *dd, struct mkroom *broom) y = broom->ly - 1; x = broom->lx + ((dpos == -1) ? rn2(1 + broom->hx - broom->lx) : dpos); - if (!isok(x, y - 1) || IS_ROCK(levl[x][y - 1].typ)) + if (!isok(x, y - 1) || IS_OBSTRUCTED(levl[x][y - 1].typ)) continue; break; case 1: @@ -1744,7 +1769,7 @@ create_door(room_door *dd, struct mkroom *broom) y = broom->hy + 1; x = broom->lx + ((dpos == -1) ? rn2(1 + broom->hx - broom->lx) : dpos); - if (!isok(x, y + 1) || IS_ROCK(levl[x][y + 1].typ)) + if (!isok(x, y + 1) || IS_OBSTRUCTED(levl[x][y + 1].typ)) continue; break; case 2: @@ -1753,7 +1778,7 @@ create_door(room_door *dd, struct mkroom *broom) x = broom->lx - 1; y = broom->ly + ((dpos == -1) ? rn2(1 + broom->hy - broom->ly) : dpos); - if (!isok(x - 1, y) || IS_ROCK(levl[x - 1][y].typ)) + if (!isok(x - 1, y) || IS_OBSTRUCTED(levl[x - 1][y].typ)) continue; break; case 3: @@ -1762,7 +1787,7 @@ create_door(room_door *dd, struct mkroom *broom) x = broom->hx + 1; y = broom->ly + ((dpos == -1) ? rn2(1 + broom->hy - broom->ly) : dpos); - if (!isok(x + 1, y) || IS_ROCK(levl[x + 1][y].typ)) + if (!isok(x + 1, y) || IS_OBSTRUCTED(levl[x + 1][y].typ)) continue; break; default: @@ -2142,8 +2167,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->has_invent) { + 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 + * forgetting to flag them as receiving their default inventory */ + mdrop_special_objs(mtmp); discard_minvent(mtmp, TRUE); + } + if (m->has_invent & CUSTOM_INVENT) { invent_carrying_monster = mtmp; } } @@ -2254,6 +2285,8 @@ create_object(object *o, struct mkroom *croom) } if (o->trapped == 0 || o->trapped == 1) otmp->otrapped = o->trapped; + if (o->trapped && (o->tknown == 0 || o->tknown == 1)) + otmp->tknown = o->tknown; otmp->greased = o->greased ? 1 : 0; if (o->quan > 0 && objects[otmp->otyp].oc_merge) { @@ -2276,7 +2309,7 @@ create_object(object *o, struct mkroom *croom) ; /* ['otmp' remains on floor] */ } else { remove_object(otmp); - if (otmp->otyp == SADDLE) + if (otmp->otyp == SADDLE && can_saddle(invent_carrying_monster)) put_saddle_on_mon(otmp, invent_carrying_monster); else (void) mpickobj(invent_carrying_monster, otmp); @@ -2484,9 +2517,8 @@ search_door( yy = croom->ly; break; default: - dx = dy = xx = yy = 0; panic("search_door: Bad wall!"); - break; + /*NOTREACHED*/ } while (xx <= croom->hx + 1 && yy <= croom->hy + 1) { if (IS_DOOR(levl[xx][yy].typ) || levl[xx][yy].typ == SDOOR) { @@ -2505,10 +2537,12 @@ search_door( * Dig a corridor between two points, using terrain ftyp. * if nxcor is TRUE, he corridor may be blocked by a boulder, * or just end without reaching the destination. + * if not null, npoints has the number of map locations used */ boolean dig_corridor( coord *org, coord *dest, + int *npoints, boolean nxcor, schar ftyp, schar btyp) { @@ -2516,6 +2550,8 @@ dig_corridor( struct rm *crm; int tx, ty, xx, yy; + if (npoints) + *npoints = 0; xx = org->x; yy = org->y; tx = dest->x; @@ -2552,8 +2588,12 @@ dig_corridor( crm = &levl[xx][yy]; if (crm->typ == btyp) { if (ftyp == CORR && maybe_sdoor(100)) { + if (npoints) + (*npoints)++; crm->typ = SCORR; } else { + if (npoints) + (*npoints)++; crm->typ = ftyp; if (nxcor && !rn2(50)) (void) mksobj_at(BOULDER, xx, yy, TRUE, FALSE); @@ -2674,7 +2714,7 @@ create_corridor(corridor *c) dest.x++; break; } - (void) dig_corridor(&org, &dest, FALSE, CORR, STONE); + (void) dig_corridor(&org, &dest, NULL, FALSE, CORR, STONE); } } @@ -2899,7 +2939,12 @@ fill_empty_maze(void) TRUE); } for (x = rnd((int) (12 * mapfact) / 100); x; x--) { + struct trap *ttmp; + maze1xy(&mm, DRY); + if ((ttmp = t_at(mm.x, mm.y)) != 0 + && (is_pit(ttmp->ttyp) || is_hole(ttmp->ttyp))) + continue; (void) mksobj_at(BOULDER, mm.x, mm.y, TRUE, FALSE); } for (x = rn2(2); x; x--) { @@ -3187,7 +3232,7 @@ lspo_monster(lua_State *L) tmpmons.stunned = 0; tmpmons.confused = 0; tmpmons.seentraps = 0; - tmpmons.has_invent = 0; + tmpmons.has_invent = DEFAULT_INVENT; tmpmons.waiting = 0; tmpmons.mm_flags = NO_MM_FLAGS; @@ -3235,6 +3280,7 @@ lspo_monster(lua_State *L) : (mgend == MALE) ? MALE : rn2(2); } } else { + int keep_default_invent = -1; /* -1 = unspecified */ lcheck_param_table(L); tmpmons.peaceful = get_table_boolean_opt(L, "peaceful", BOOL_RANDOM); @@ -3255,7 +3301,8 @@ lspo_monster(lua_State *L) tmpmons.confused = get_table_boolean_opt(L, "confused", FALSE); tmpmons.waiting = get_table_boolean_opt(L, "waiting", FALSE); tmpmons.seentraps = 0; /* TODO: list of trap names to bitfield */ - tmpmons.has_invent = 0; + keep_default_invent = + get_table_boolean_opt(L, "keep_default_invent", -1); if (!get_table_boolean_opt(L, "tail", TRUE)) tmpmons.mm_flags |= MM_NOTAIL; @@ -3304,7 +3351,19 @@ lspo_monster(lua_State *L) lua_getfield(L, 1, "inventory"); if (!lua_isnil(L, -1)) { - tmpmons.has_invent = 1; + /* overwrite DEFAULT_INVENT - most times inventory is specified, + * the monster should not get its species' default inventory. Only + * provide it if explicitly requested. */ + tmpmons.has_invent = CUSTOM_INVENT; + if (keep_default_invent == TRUE) + tmpmons.has_invent |= DEFAULT_INVENT; + } + else { + /* if keep_default_invent was not specified (-1), keep has_invent as + * DEFAULT_INVENT and provide the species' default inventory. + * But if it was explicitly set to false, provide *no* inventory. */ + if (keep_default_invent == FALSE) + tmpmons.has_invent = NO_INVENT; } } @@ -3318,7 +3377,8 @@ lspo_monster(lua_State *L) create_monster(&tmpmons, gc.coder->croom); - if (tmpmons.has_invent && lua_type(L, -1) == LUA_TFUNCTION) { + if ((tmpmons.has_invent & CUSTOM_INVENT) + && lua_type(L, -1) == LUA_TFUNCTION) { lua_remove(L, -2); nhl_pcall_handle(L, 0, 0, "lspo_monster", NHLpa_panic); spo_end_moninvent(); @@ -3494,7 +3554,7 @@ lspo_object(lua_State *L) 0, /* quan */ 0, /* buried */ 0, /* lit */ - 0, 0, 0, 0, /* eroded, locked, trapped, recharged */ + 0, 0, 0, 0, 0, /* eroded, locked, trapped, tknown, recharged */ 0, 0, 0, 0, /* invis, greased, broken, achievement */ }; #if 0 @@ -3514,6 +3574,7 @@ lspo_object(lua_State *L) tmpobj.spe = -127; tmpobj.quan = -1; tmpobj.trapped = -1; + tmpobj.tknown = -1; tmpobj.locked = -1; tmpobj.corpsenm = NON_PM; @@ -3567,6 +3628,7 @@ lspo_object(lua_State *L) tmpobj.eroded = get_table_int_opt(L, "eroded", 0); tmpobj.locked = get_table_boolean_opt(L, "locked", -1); tmpobj.trapped = get_table_boolean_opt(L, "trapped", -1); + tmpobj.tknown = get_table_boolean_opt(L, "trap_known", -1); tmpobj.recharged = get_table_int_opt(L, "recharged", 0); tmpobj.greased = get_table_boolean_opt(L, "greased", 0); tmpobj.broken = get_table_boolean_opt(L, "broken", 0); @@ -3850,7 +3912,7 @@ lspo_engraving(lua_State *L) ecoord = SP_COORD_PACK(x, y); get_location_coord(&x, &y, DRY, gc.coder->croom, ecoord); - make_engr_at(x, y, txt, 0L, etyp); + make_engr_at(x, y, txt, NULL, 0L, etyp); Free(txt); ep = engr_at(x, y); if (ep) { @@ -5423,25 +5485,34 @@ int lspo_exclusion(lua_State *L) { static const char *const ez_types[] = { - "teleport", "teleport-up", "teleport-down", NULL + "teleport", "teleport-up", "teleport-down", "monster-generation", NULL }; static const int ez_types2i[] = { - LR_TELE, LR_UPTELE, LR_DOWNTELE, 0 + LR_TELE, LR_UPTELE, LR_DOWNTELE, LR_MONGEN, 0 }; struct exclusion_zone *ez = (struct exclusion_zone *) alloc(sizeof *ez); lua_Integer x1,y1,x2,y2; + coordxy a1,b1,a2,b2; create_des_coder(); lcheck_param_table(L); ez->zonetype = ez_types2i[get_table_option(L, "type", "teleport", ez_types)]; get_table_region(L, "region", &x1, &y1, &x2, &y2, FALSE); - ez->lx = x1; - ez->ly = y1; - ez->hx = x2; - ez->hy = y2; - cvt_to_abscoord(&ez->lx, &ez->ly); - cvt_to_abscoord(&ez->hx, &ez->hy); + + a1 = x1, b1 = y1; + a2 = x2, b2 = y2; + + get_location_coord(&a1, &b1, ANY_LOC|NO_LOC_WARN, gc.coder->croom, + SP_COORD_PACK(a1, b1)); + get_location_coord(&a2, &b2, ANY_LOC|NO_LOC_WARN, gc.coder->croom, + SP_COORD_PACK(a2, b2)); + + ez->lx = a1; + ez->ly = b1; + ez->hx = a2; + ez->hy = b2; + ez->next = sve.exclusion_zones; sve.exclusion_zones = ez; return 0; @@ -6072,7 +6143,7 @@ TODO: gc.coder->croom needs to be updated if (y < 1) y = 1; } else { - y = rn2(ROWNO - mf->wid); + y = rn2(ROWNO - mf->hei); } } } diff --git a/src/spell.c b/src/spell.c index 65ef028b1..e3d7f2e12 100644 --- a/src/spell.c +++ b/src/spell.c @@ -1441,11 +1441,13 @@ spelleffects(int spell_otyp, boolean atme, boolean force) } break; } /* else */ + FALLTHROUGH; /*FALLTHRU*/ /* these spells are all duplicates of wand effects */ case SPE_FORCE_BOLT: physical_damage = TRUE; + FALLTHROUGH; /*FALLTHRU*/ case SPE_SLEEP: case SPE_MAGIC_MISSILE: @@ -1507,11 +1509,12 @@ spelleffects(int spell_otyp, boolean atme, boolean force) case SPE_DETECT_FOOD: case SPE_CAUSE_FEAR: case SPE_IDENTIFY: + case SPE_CHARM_MONSTER: /* high skill yields effect equivalent to blessed scroll */ if (role_skill >= P_SKILLED) pseudo->blessed = 1; + FALLTHROUGH; /*FALLTHRU*/ - case SPE_CHARM_MONSTER: case SPE_MAGIC_MAPPING: case SPE_CREATE_MONSTER: (void) seffects(pseudo); @@ -1526,6 +1529,7 @@ spelleffects(int spell_otyp, boolean atme, boolean force) /* high skill yields effect equivalent to blessed potion */ if (role_skill >= P_SKILLED) pseudo->blessed = 1; + FALLTHROUGH; /*FALLTHRU*/ case SPE_INVISIBILITY: (void) peffects(pseudo); @@ -1571,7 +1575,8 @@ spelleffects(int spell_otyp, boolean atme, boolean force) } /* gain skill for successful cast */ - use_skill(skill, spellev(spell)); + if (!force) + use_skill(skill, spellev(spell)); obfree(pseudo, (struct obj *) 0); /* now, get rid of it */ return ECMD_TIME; diff --git a/src/steal.c b/src/steal.c index 08b4db982..f90d66a55 100644 --- a/src/steal.c +++ b/src/steal.c @@ -165,12 +165,13 @@ staticfn int stealarm(void) { struct monst *mtmp; - struct obj *otmp; + struct obj *otmp, *nextobj; if (!gs.stealoid || !gs.stealmid) goto botm; - for (otmp = gi.invent; otmp; otmp = otmp->nobj) { + for (otmp = gi.invent; otmp; otmp = nextobj) { + nextobj = otmp->nobj; if (otmp->o_id == gs.stealoid) { for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (mtmp->m_id == gs.stealmid) { @@ -649,11 +650,11 @@ mpickobj(struct monst *mtmp, struct obj *otmp) pline("%s out.", Tobjnam(otmp, "go")); snuff_otmp = TRUE; } + /* for hero owned object on shop floor, mtmp is taking possession + and if it's eventually dropped in a shop, shk will claim it */ + otmp->no_charge = 0; /* some object handling is only done if mtmp isn't a pet */ if (!mtmp->mtame) { - /* for hero owned object on shop floor, mtmp is taking possession - and if it's eventually dropped in a shop, shk will claim it */ - otmp->no_charge = 0; /* if monst is unseen, some info hero knows about this object becomes lost; continual pickup and drop by pets makes this too annoying if it is applied to them; when engulfed (where monster can't be seen diff --git a/src/steed.c b/src/steed.c index 2c7be9c2e..48d537b99 100644 --- a/src/steed.c +++ b/src/steed.c @@ -131,6 +131,7 @@ use_saddle(struct obj *otmp) if (otmp->owornmask) remove_worn_item(otmp, FALSE); freeinv(otmp); + /* !can_saddle(mtmp) already eliminated above */ put_saddle_on_mon(otmp, mtmp); } else pline("%s resists!", Monnam(mtmp)); @@ -140,8 +141,19 @@ use_saddle(struct obj *otmp) void put_saddle_on_mon(struct obj *saddle, struct monst *mtmp) { - if (!can_saddle(mtmp) || which_armor(mtmp, W_SADDLE)) + if (!can_saddle(mtmp) || which_armor(mtmp, W_SADDLE)) { + if (saddle) + impossible("put_saddle_on_mon: saddle obj could get orphaned"); return; + } + if (!saddle) { + if ((saddle = mksobj(SADDLE, TRUE, FALSE)) != 0) { + fully_identify_obj(saddle); + /* mpickobj can later override identification if out-of-view */ + } else { + return; + } + } if (mpickobj(mtmp, saddle)) panic("merged saddle?"); mtmp->misc_worn_check |= W_SADDLE; @@ -589,6 +601,7 @@ dismount_steed( switch (reason) { case DISMOUNT_THROWN: verb = "are thrown"; + FALLTHROUGH; /*FALLTHRU*/ case DISMOUNT_KNOCKED: case DISMOUNT_FELL: diff --git a/src/symbols.c b/src/symbols.c index d9de7b8db..7c32af3e3 100644 --- a/src/symbols.c +++ b/src/symbols.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 symbols.c $NHDT-Date: 1711477037 2024/03/26 18:17:17 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.120 $ */ +/* NetHack 3.7 symbols.c $NHDT-Date: 1736530208 2025/01/10 09:30:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.123 $ */ /* Copyright (c) NetHack Development Team 2020. */ /* NetHack may be freely redistributed. See license for details. */ @@ -11,12 +11,12 @@ staticfn struct _savedsym *savedsym_find(const char *, int); extern const uchar def_r_oc_syms[MAXOCLASSES]; /* drawing.c */ #if defined(TERMLIB) || defined(CURSES_GRAPHICS) -void (*decgraphics_mode_callback)(void) = 0; /* set in tty_start_screen() */ +void (*decgraphics_mode_callback)(void) = 0; /* set in term_start_screen() */ #endif /* TERMLIB || CURSES */ #ifdef PC9800 -void (*ibmgraphics_mode_callback)(void) = 0; /* set in tty_start_screen() */ -void (*ascgraphics_mode_callback)(void) = 0; /* set in tty_start_screen() */ +void (*ibmgraphics_mode_callback)(void) = 0; /* set in term_start_screen() */ +void (*ascgraphics_mode_callback)(void) = 0; /* set in term_start_screen() */ #endif #ifdef CURSES_GRAPHICS void (*cursesgraphics_mode_callback)(void) = 0; @@ -25,7 +25,7 @@ void (*cursesgraphics_mode_callback)(void) = 0; void (*ibmgraphics_mode_callback)(void) = 0; #endif #ifdef ENHANCED_SYMBOLS -void (*utf8graphics_mode_callback)(void) = 0; /* set in tty_start_screen and +void (*utf8graphics_mode_callback)(void) = 0; /* set in term_start_screen and * found in unixtty,windtty,&c */ #endif @@ -371,7 +371,7 @@ symset_is_compatible( * particular types of symset "handling", define a * H_XXX macro in include/sym.h and add the name * to this array at the matching offset. - * Externally referenced from files.c, options.c, utf8map.c. + * Externally referenced from files.c, options.c, utf8map.c. */ const char *const known_handling[] = { "UNKNOWN", /* H_UNK */ diff --git a/src/sys.c b/src/sys.c index ad2274fbe..4d8ad2dcf 100644 --- a/src/sys.c +++ b/src/sys.c @@ -15,28 +15,37 @@ at runtime by setting up a value for "DEBUGFILES" in the environment */ #endif -struct sysopt sysopt; +struct sysopt_s sysopt; void sys_early_init(void) { const char *p; + /* Don't assume that these are not already set, and that it is + * safe to dupstr() without orphaning any pointers. Check them. */ + sysopt.support = (char *) 0; sysopt.recover = (char *) 0; #ifdef SYSCF sysopt.wizards = (char *) 0; #else + if (sysopt.wizards) + free((genericptr_t) sysopt.wizards); sysopt.wizards = dupstr(WIZARD_NAME); #endif if ((p = getenv("DEBUGFILES")) != 0) { + if (sysopt.debugfiles) + free((genericptr_t) sysopt.debugfiles); sysopt.debugfiles = dupstr(p); sysopt.env_dbgfl = 1; /* prevent sysconf processing from overriding */ } else { #if defined(SYSCF) || !defined(DEBUGFILES) sysopt.debugfiles = (char *) 0; #else + if (sysopt.debugfiles) + free((genericptr_t) sysopt.debugfiles); sysopt.debugfiles = dupstr(DEBUGFILES); #endif sysopt.env_dbgfl = 0; @@ -48,6 +57,7 @@ sys_early_init(void) sysopt.shellers = (char *) 0; sysopt.explorers = (char *) 0; sysopt.genericusers = (char *) 0; + sysopt.msghandler = (char *) 0; sysopt.maxplayers = 0; /* XXX eventually replace MAX_NR_OF_PLAYERS */ sysopt.bones_pools = 0; sysopt.livelog = LL_NONE; @@ -65,7 +75,11 @@ sys_early_init(void) #ifdef PANICTRACE /* panic options */ + if (sysopt.gdbpath) + free((genericptr_t) sysopt.gdbpath); sysopt.gdbpath = dupstr(GDBPATH); + if (sysopt.greppath) + free((genericptr_t) sysopt.greppath); sysopt.greppath = dupstr(GREPPATH); #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) sysopt.panictrace_gdb = 1; @@ -85,8 +99,7 @@ sys_early_init(void) sysopt.check_plname = 0; sysopt.seduce = 1; /* if it's compiled in, default to on */ sysopt_seduce_set(sysopt.seduce); - /* default to little-endian in 3.7 */ - sysopt.saveformat[0] = sysopt.bonesformat[0] = lendian; + sysopt.saveformat[0] = sysopt.bonesformat[0] = historical; sysopt.accessibility = 0; #ifdef WIN32 sysopt.portable_device_paths = 0; @@ -115,23 +128,32 @@ sysopt_release(void) free((genericptr_t) sysopt.debugfiles), sysopt.debugfiles = (char *) 0; sysopt.env_dbgfl = 0; + if (sysopt.msghandler) + free((genericptr_t) sysopt.msghandler), sysopt.msghandler = (char *) 0; #ifdef DUMPLOG if (sysopt.dumplogfile) free((genericptr_t) sysopt.dumplogfile), sysopt.dumplogfile=(char *) 0; #endif if (sysopt.genericusers) free((genericptr_t) sysopt.genericusers), - sysopt.genericusers = (char *) 0; + sysopt.genericusers = (char *) 0; if (sysopt.gdbpath) free((genericptr_t) sysopt.gdbpath), sysopt.gdbpath = (char *) 0; if (sysopt.greppath) free((genericptr_t) sysopt.greppath), sysopt.greppath = (char *) 0; +#ifdef CRASHREPORT + if (gc.crash_email) + free((genericptr_t) gc.crash_email), gc.crash_email = (char *) NULL; + if (gc.crash_name) + free((genericptr_t) gc.crash_name), gc.crash_name = (char *) NULL; +#endif + /* this one's last because it might be used in panic feedback, although none of the preceding ones are likely to trigger a controlled panic */ if (sysopt.fmtd_wizard_list) free((genericptr_t) sysopt.fmtd_wizard_list), - sysopt.fmtd_wizard_list = (char *) 0; + sysopt.fmtd_wizard_list = (char *) 0; return; } diff --git a/src/teleport.c b/src/teleport.c index e4aeedbe3..3c3d6df3e 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 teleport.c $NHDT-Date: 1685863331 2023/06/04 07:22:11 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.206 $ */ +/* NetHack 3.7 teleport.c $NHDT-Date: 1736129950 2025/01/05 18:19:10 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.235 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -172,6 +172,9 @@ goodpos( /* skip boulder locations for most creatures */ if (sobj_at(BOULDER, x, y) && (!mdat || !throws_rocks(mdat))) return FALSE; + /* pretend GP_AVOID_MONPOS == monster creation */ + if (avoid_monpos && is_exclusion_zone(LR_MONGEN, x, y)) + return FALSE; return TRUE; } @@ -194,6 +197,17 @@ enexto( || enexto_core(cc, xx, yy, mdat, NO_MM_FLAGS)); } +boolean +enexto_gpflags( + coord *cc, + coordxy xx, coordxy yy, + struct permonst *mdat, + mmflags_nht entflags) +{ + return (enexto_core(cc, xx, yy, mdat, GP_CHECKSCARY | entflags) + || enexto_core(cc, xx, yy, mdat, entflags)); +} + #ifdef NEW_ENEXTO boolean @@ -790,6 +804,28 @@ teleport_pet(struct monst *mtmp, boolean force_it) return TRUE; } +/* teleport to random pet, if valid location next to it */ +void +tele_to_rnd_pet(void) +{ + struct monst *mtmp, *pet = (struct monst *) 0; + int cnt = 0; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (!DEADMONSTER(mtmp) && mtmp->mtame && !mon_offmap(mtmp)) { + cnt++; + if (!rn2(cnt)) + pet = mtmp; + } + if (pet && !m_next2u(pet)) { + coordxy tx = pet->mx + rn2(3) - 1, + ty = pet->my + rn2(3) - 1; + + if (isok(tx, ty) && teleok(tx, ty, FALSE)) + teleds(tx, ty, TELEDS_TELEPORT); + } +} + /* teleport the hero via some method other than scroll of teleport */ void tele(void) @@ -1096,12 +1132,14 @@ dotele( } if (next_to_u()) { - if (trap && trap_once) + if (trap && trap_once) { vault_tele(); - else if (trap && isok(trap->teledest.x, trap->teledest.y)) + } else if (trap && isok(trap->teledest.x, trap->teledest.y)) { teleds(trap->teledest.x, trap->teledest.y, TELEDS_TELEPORT); - else + } else { + iflags.travelcc.x = iflags.travelcc.y = 0; tele(); + } (void) next_to_u(); } else { You("%s", shudder_for_moment); @@ -1442,6 +1480,14 @@ domagicportal(struct trap *ttmp) void tele_trap(struct trap *trap) { + /* a fixed-destination teleport trap could theoretically place hero onto a + * second teleport trap; prevent the recursive call from spoteffects() from + * triggering the trap at the destination */ + static boolean in_tele_trap = FALSE; + if (in_tele_trap) + return; + + in_tele_trap = TRUE; if (In_endgame(&u.uz) || Antimagic) { if (Antimagic) shieldeff(u.ux, u.uy); @@ -1462,13 +1508,19 @@ tele_trap(struct trap *trap) /* could not find some other place to put mtmp; the level must * be nearly or completely full */ You1(shudder_for_moment); - return; } - rloc_to(mtmp, cc.x, cc.y); + else { + rloc_to(mtmp, cc.x, cc.y); + mtmp = (struct monst *) 0; /* no longer a monster at dest */ + } + } + if (!mtmp) { + teleds(trap->teledest.x, trap->teledest.y, TELEDS_TELEPORT); } - teleds(trap->teledest.x, trap->teledest.y, TELEDS_TELEPORT); } else tele(); + + in_tele_trap = FALSE; } void @@ -1627,6 +1679,7 @@ rloc_to_core( if (u.ustuck == mtmp) { if (u.uswallow) { u_on_newpos(mtmp->mx, mtmp->my); + check_special_room(FALSE); docrt(); } else if (!m_next2u(mtmp)) { unstuck(mtmp); @@ -1636,14 +1689,16 @@ rloc_to_core( maybe_unhide_at(x, y); newsym(x, y); /* update new location */ set_apparxy(mtmp); /* orient monster */ - if (domsg && (canspotmon(mtmp) || appearmsg)) { + if (domsg && (canspotmon(mtmp) || appearmsg || mtmp == u.ustuck)) { int du = distu(x, y), olddu; const char *next = (du <= 2) ? " next to you" : 0, /* next2u() */ *nearu = (du <= BOLT_LIM * BOLT_LIM) ? " close by" : 0; set_msg_xy(x, y); mtmp->mstrategy &= ~STRAT_APPEARMSG; /* one chance only */ - if (telemsg && (couldsee(x, y) || sensemon(mtmp))) { + if (mtmp == u.ustuck && !u_at(u.ux0, u.uy0)) { + You("and %s teleport together.", mon_nam(mtmp)); + } else if (telemsg && (couldsee(x, y) || sensemon(mtmp))) { pline("%s vanishes and reappears%s.", Monnam(mtmp), next ? next @@ -1658,11 +1713,18 @@ rloc_to_core( !Blind ? "appears" : "arrives", next ? next : nearu ? nearu : ""); } + /* wand discovery only happens if a messaage is delivered (bug?); + if spell or q.mechanic attack or artifact #invoke for banish + then current_wand will be Null */ + if (gc.current_wand && gc.current_wand->otyp == WAN_TELEPORTATION) + makeknown(WAN_TELEPORTATION); } /* shopkeepers will only teleport if you zap them with a wand of teleportation or if they've been transformed into a jumpy monster; - the latter only happens if you've attacked them with polymorph */ + the latter only happens if you've attacked them with polymorph + [FIXME? or they've been hit by a genetic engineer, which won't + necessarily be due to Conflict by hero] */ if (resident_shk && !inhishop(mtmp)) make_angry_shk(mtmp, oldx, oldy); @@ -2183,7 +2245,9 @@ random_teleport_level(void) /* you teleport a monster (via wand, spell, or poly'd q.mechanic attack); return false iff the attempt fails */ boolean -u_teleport_mon(struct monst *mtmp, boolean give_feedback) +u_teleport_mon( + struct monst *mtmp, + boolean give_feedback) { coord cc; @@ -2198,10 +2262,12 @@ u_teleport_mon(struct monst *mtmp, boolean give_feedback) if (!rloc(mtmp, RLOC_MSG)) m_into_limbo(mtmp); } else if ((is_rider(mtmp->data) || control_teleport(mtmp->data)) - && rn2(13) && enexto(&cc, u.ux, u.uy, mtmp->data)) + && rn2(13) && enexto(&cc, u.ux, u.uy, mtmp->data)) { rloc_to(mtmp, cc.x, cc.y); - else - (void) rloc(mtmp, RLOC_MSG); + } else { + if (!rloc(mtmp, RLOC_MSG)) + return FALSE; + } return TRUE; } diff --git a/src/timeout.c b/src/timeout.c index 13ebb0d4e..1b85d5398 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -1,10 +1,11 @@ -/* NetHack 3.7 timeout.c $NHDT-Date: 1727251273 2024/09/25 08:01:13 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.193 $ */ +/* NetHack 3.7 timeout.c $NHDT-Date: 1756531249 2025/08/29 21:20:49 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.205 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2018. */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" +#ifndef SFCTOOL staticfn void stoned_dialogue(void); staticfn void vomiting_dialogue(void); staticfn void sleep_dialogue(void); @@ -79,6 +80,7 @@ static const struct propname { { SICK_RES, "sickness resistance" }, { ANTIMAGIC, "magic resistance" }, { HALLUC_RES, "hallucination resistance" }, + { BLND_RES, "light-induced blindness resistance" }, { FUMBLING, "fumbling" }, { HUNGER, "voracious hunger" }, { TELEPAT, "telepathic" }, @@ -213,6 +215,7 @@ vomiting_dialogue(void) make_stunned((HStun & TIMEOUT) + (long) d(2, 4), FALSE); if (!Popeye(VOMITING)) stop_occupation(); + FALLTHROUGH; /*FALLTHRU*/ case 9: make_confused((HConfusion & TIMEOUT) + (long) d(2, 4), FALSE); @@ -597,6 +600,9 @@ nh_timeout(void) if (svq.quest_status.killed_leader) baseluck -= 4; + if (Role_if(PM_ARCHEOLOGIST) && uarmh && uarmh->otyp == FEDORA) + baseluck += 1; + if (u.uluck != baseluck && svm.moves % ((u.uhave.amulet || u.ugangr) ? 300 : 600) == 0) { /* Cursed luckstones stop bad luck from timing out; blessed luckstones @@ -905,7 +911,7 @@ nh_timeout(void) * are to make noise when you fumble. Adjustments * to this number must be thoroughly play tested. */ - if ((inv_weight() > -500)) { + if ((inv_weight() > (WT_NOISY_INV * -1))) { if (!Deaf) You("make a lot of noise!"); wake_nearby(FALSE); @@ -929,6 +935,12 @@ nh_timeout(void) case GLIB: make_glib(0); /* might update persistent inventory */ break; + case PROT_FROM_SHAPE_CHANGERS: + /* timed Protection_from_shape_changers is via + #wizintrinsic only */ + if (!Protection_from_shape_changers) + restartcham(); + break; } } @@ -1434,6 +1446,7 @@ burn_object(anything *arg, long timeout) switch (obj->where) { case OBJ_INVENT: need_invupdate = TRUE; + FALLTHROUGH; /*FALLTHRU*/ case OBJ_MINVENT: pline("%spotion of oil has burnt away.", whose); @@ -1497,6 +1510,7 @@ burn_object(anything *arg, long timeout) switch (obj->where) { case OBJ_INVENT: need_invupdate = TRUE; + FALLTHROUGH; /*FALLTHRU*/ case OBJ_MINVENT: if (obj->otyp == BRASS_LANTERN) @@ -1576,6 +1590,7 @@ burn_object(anything *arg, long timeout) switch (obj->where) { case OBJ_INVENT: need_invupdate = TRUE; + FALLTHROUGH; /*FALLTHRU*/ case OBJ_MINVENT: pline("%scandelabrum's flame%s.", whose, @@ -1591,6 +1606,7 @@ burn_object(anything *arg, long timeout) case OBJ_INVENT: /* no need_invupdate for update_inventory() necessary; useupall() -> freeinv() handles it */ + FALLTHROUGH; /*FALLTHRU*/ case OBJ_MINVENT: pline("%s %s consumed!", Yname2(obj), @@ -2007,7 +2023,7 @@ print_queue(winid win, timer_element *base) for (curr = base; curr; curr = curr->next) { #ifdef VERBOSE_TIMER Sprintf(buf, " %4ld %4ld %-6s %s(%s)", curr->timeout, - curr->tid, kind_name(curr->kind), + (long) curr->tid, kind_name(curr->kind), timeout_funcs[curr->func_index].name, fmt_ptr((genericptr_t) curr->arg.a_void)); #else @@ -2163,7 +2179,13 @@ timer_sanity_check(void) the analyzer isn't aware that isok() filters such things */ assert(x > 0 && x < COLNO && y >= 0 && y < ROWNO); - if (curr->func_index == MELT_ICE_AWAY && !is_ice(x, y)) + if (curr->func_index == MELT_ICE_AWAY && !is_ice(x, y) + /* the terrain under the span of an open drawbridge might + be frozen moat; is_ice() only checks for that when + the drawbridge is closed (and terrain here would be + DRAWBRIGE_UP) */ + && !(levl[x][y].typ == DRAWBRIDGE_DOWN + && (levl[x][y].drawbridgemask & DB_UNDER) == DB_ICE)) impossible( "timer sanity: melt timer %lu on non-ice %d <%d,%d>", t_id, levl[x][y].typ, x, y); @@ -2481,22 +2503,19 @@ write_timer(NHFILE *nhfp, timer_element *timer) case TIMER_GLOBAL: case TIMER_LEVEL: /* assume no pointers in arg */ - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) timer, sizeof(timer_element)); + Sfo_fe(nhfp, timer, "timer"); break; case TIMER_OBJECT: if (timer->needs_fixup) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) timer, sizeof(timer_element)); + Sfo_fe(nhfp, timer, "timer"); } else { /* replace object pointer with id */ arg_save.a_obj = timer->arg.a_obj; timer->arg = cg.zeroany; timer->arg.a_uint = (arg_save.a_obj)->o_id; timer->needs_fixup = 1; - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) timer, sizeof(timer_element)); + Sfo_fe(nhfp, timer, "timer"); timer->arg.a_obj = arg_save.a_obj; timer->needs_fixup = 0; } @@ -2504,16 +2523,14 @@ write_timer(NHFILE *nhfp, timer_element *timer) case TIMER_MONSTER: if (timer->needs_fixup) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) timer, sizeof(timer_element)); + Sfo_fe(nhfp, timer, "timer"); } else { /* replace monster pointer with id */ arg_save.a_monst = timer->arg.a_monst; timer->arg = cg.zeroany; timer->arg.a_uint = (arg_save.a_monst)->m_id; timer->needs_fixup = 1; - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) timer, sizeof(timer_element)); + Sfo_fe(nhfp, timer, "timer"); timer->arg.a_monst = arg_save.a_monst; timer->needs_fixup = 0; } @@ -2645,15 +2662,12 @@ save_timers(NHFILE *nhfp, int range) timer_element *curr, *prev, *next_timer = 0; int count; - if (perform_bwrite(nhfp)) { + if (update_file(nhfp)) { if (range == RANGE_GLOBAL) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &svt.timer_id, - sizeof svt.timer_id); + Sfo_ulong(nhfp, &svt.timer_id, "timer-timer_id"); } count = maybe_write_timer(nhfp, range, FALSE); - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &count, sizeof count); + Sfo_int(nhfp, &count, "timer-timer_count"); (void) maybe_write_timer(nhfp, range, TRUE); } @@ -2675,6 +2689,7 @@ save_timers(NHFILE *nhfp, int range) } } } +#endif /* !SFCTOOL */ /* * Pull in the structures from disk, but don't recalculate the object and @@ -2688,30 +2703,28 @@ restore_timers(NHFILE *nhfp, int range, long adjust) boolean ghostly = (nhfp->ftype == NHF_BONESFILE); /* from a ghost level */ if (range == RANGE_GLOBAL) { - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) &svt.timer_id, - sizeof svt.timer_id); + Sfi_ulong(nhfp, &svt.timer_id, "timer-timer_id"); } /* restore elements */ - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) &count, sizeof count); - + Sfi_int(nhfp, &count, "timer-timer_count"); while (count-- > 0) { curr = (timer_element *) alloc(sizeof(timer_element)); - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) curr, sizeof(timer_element)); + Sfi_fe(nhfp, curr, "timer"); if (ghostly) curr->timeout += adjust; +#ifndef SFCTOOL insert_timer(curr); +#endif } } +#ifndef SFCTOOL DISABLE_WARNING_FORMAT_NONLITERAL /* to support '#stats' wizard-mode command */ void -timer_stats(const char* hdrfmt, char *hdrbuf, long *count, long *size) +timer_stats(const char *hdrfmt, char *hdrbuf, long *count, long *size) { timer_element *te; @@ -2751,5 +2764,6 @@ relink_timers(boolean ghostly) } } } +#endif /* !SFCTOOL */ /*timeout.c*/ diff --git a/src/topten.c b/src/topten.c index f38de9244..cb968a726 100644 --- a/src/topten.c +++ b/src/topten.c @@ -110,11 +110,13 @@ formatkiller( switch (svk.killer.format) { default: impossible("bad killer format? (%d)", svk.killer.format); + FALLTHROUGH; /*FALLTHRU*/ case NO_KILLER_PREFIX: break; case KILLED_BY_AN: kname = an(kname); + FALLTHROUGH; /*FALLTHRU*/ case KILLED_BY: (void) strncat(buf, killed_by_prefix[how], siz - 1); diff --git a/src/track.c b/src/track.c index e9c4923f8..f5b915f22 100644 --- a/src/track.c +++ b/src/track.c @@ -18,6 +18,7 @@ initrack(void) (void) memset((genericptr_t) &utrack, 0, sizeof(utrack)); } +#ifndef SFCTOOL /* add to track */ void settrack(void) @@ -51,6 +52,7 @@ gettrack(coordxy x, coordxy y) } return (coord *) 0; } +#endif /* !SFCTOOL */ /* return TRUE if x,y has hero tracks on it */ boolean @@ -69,18 +71,13 @@ hastrack(coordxy x, coordxy y) void save_track(NHFILE *nhfp) { - if (perform_bwrite(nhfp)) { - if (nhfp->structlevel) { - int i; + if (update_file(nhfp)) { + int i; - bwrite(nhfp->fd, (genericptr_t) &utcnt, sizeof utcnt); - bwrite(nhfp->fd, (genericptr_t) &utpnt, sizeof utpnt); - for (i = 0; i < utcnt; i++) { - bwrite(nhfp->fd, (genericptr_t) &utrack[i].x, - sizeof utrack[i].x); - bwrite(nhfp->fd, (genericptr_t) &utrack[i].y, - sizeof utrack[i].y); - } + Sfo_int(nhfp, &utcnt, "track-utcnt"); + Sfo_int(nhfp, &utpnt, "track-utpnt"); + for (i = 0; i < utcnt; i++) { + Sfo_nhcoord(nhfp, &utrack[i], "utrack"); } } if (release_data(nhfp)) @@ -91,17 +88,15 @@ save_track(NHFILE *nhfp) void rest_track(NHFILE *nhfp) { - if (nhfp->structlevel) { - int i; + int i; - mread(nhfp->fd, (genericptr_t) &utcnt, sizeof utcnt); - mread(nhfp->fd, (genericptr_t) &utpnt, sizeof utpnt); - if (utcnt > UTSZ || utpnt > UTSZ) - panic("rest_track: impossible pt counts"); - for (i = 0; i < utcnt; i++) { - mread(nhfp->fd, (genericptr_t) &utrack[i].x, sizeof utrack[i].x); - mread(nhfp->fd, (genericptr_t) &utrack[i].y, sizeof utrack[i].y); - } + Sfi_int(nhfp, &utcnt, "track-utcnt"); + Sfi_int(nhfp, &utpnt, "track-utpnt"); + + if (utcnt > UTSZ || utpnt > UTSZ) + panic("rest_track: impossible pt counts"); + for (i = 0; i < utcnt; i++) { + Sfi_nhcoord(nhfp, &utrack[i], "utrack"); } } diff --git a/src/trap.c b/src/trap.c index 9e791e1c5..21adb2314 100644 --- a/src/trap.c +++ b/src/trap.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 trap.c $NHDT-Date: 1720128169 2024/07/04 21:22:49 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.602 $ */ +/* NetHack 3.7 trap.c $NHDT-Date: 1741926700 2025/03/13 20:31:40 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.621 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -65,6 +65,7 @@ staticfn void clear_conjoined_pits(struct trap *); staticfn boolean adj_nonconjoined_pit(struct trap *); staticfn int try_lift(struct monst *, struct trap *, int, boolean); staticfn int help_monster_out(struct monst *, struct trap *); +staticfn void disarm_box(struct obj *, boolean, boolean); staticfn void untrap_box(struct obj *, boolean, boolean); #if 0 staticfn void join_adjacent_pits(struct trap *); @@ -455,7 +456,7 @@ struct trap * maketrap(coordxy x, coordxy y, int typ) { static union vlaunchinfo zero_vl; - boolean oldplace; + boolean oldplace, was_ice, clear_flags; struct trap *ttmp; struct rm *lev = &levl[x][y]; @@ -473,10 +474,10 @@ maketrap(coordxy x, coordxy y, int typ) || (u.utraptype == TT_LAVA && !is_lava(x, y)))) reset_utrap(FALSE); /* old remain valid */ - } else if (!CAN_OVERWRITE_TERRAIN(lev->typ) - || (IS_FURNITURE(lev->typ) - && (typ != PIT && typ != HOLE)) + } else if (!CAN_OVERWRITE_TERRAIN(lev->typ) /* stairs */ || is_pool_or_lava(x, y) + || (IS_FURNITURE(lev->typ) && (typ != PIT && typ != HOLE)) + || (lev->typ == DRAWBRIDGE_UP && typ == MAGIC_PORTAL) || (IS_AIR(lev->typ) && typ != MAGIC_PORTAL) || (typ == LEVEL_TELEP && single_level_branch(&u.uz))) { /* no trap on top of furniture (caller usually screens the @@ -513,6 +514,7 @@ maketrap(coordxy x, coordxy y, int typ) case PIT: case SPIKED_PIT: ttmp->conjoined = 0; + FALLTHROUGH; /*FALLTHRU*/ case HOLE: case TRAPDOOR: @@ -522,24 +524,44 @@ maketrap(coordxy x, coordxy y, int typ) && (is_hole(typ) || IS_DOOR(lev->typ) || IS_WALL(lev->typ))) add_damage(x, y, /* schedule repair */ ((IS_DOOR(lev->typ) || IS_WALL(lev->typ)) - && !svc.context.mon_moving) - ? SHOP_HOLE_COST - : 0L); - lev->doormask = 0; /* subsumes altarmask, icedpool... */ - if (IS_ROOM(lev->typ)) /* && !IS_AIR(lev->typ) */ + && !svc.context.mon_moving) ? SHOP_HOLE_COST : 0L); + + clear_flags = TRUE; /* assume lev->flags needs to be reset */ + /* DRAWBRIDGE_UP passes the IS_ROOM() test so check it first; + it also needs to retain lev->drawbridgemask */ + if (lev->typ == DRAWBRIDGE_UP) { + /* bridge is closed and we're putting a hole or pit at the span + spot; this trap will be deleted if/when the bridge is opened; + terrain becomes room floor even if it was moat, lava, or ice */ + clear_flags = FALSE; /* keep lev->drawbridgemask */ + was_ice = (lev->drawbridgemask & DB_UNDER) == DB_ICE; + lev->drawbridgemask &= ~DB_UNDER; + lev->drawbridgemask |= DB_FLOOR; + if (was_ice) { + /* subset of set_levltyp() after changing ice to floor; + frozen corpses resume rotting, no more ice to melt away */ + obj_ice_effects(x, y, TRUE); + spot_stop_timers(x, y, MELT_ICE_AWAY); + } + } else if (IS_ROOM(lev->typ)) { (void) set_levltyp(x, y, ROOM); + /* * some cases which can happen when digging * down while phazing thru solid areas */ - else if (lev->typ == STONE || lev->typ == SCORR) + } else if (lev->typ == STONE || lev->typ == SCORR) { (void) set_levltyp(x, y, CORR); - else if (IS_WALL(lev->typ) || lev->typ == SDOOR) + } else if (IS_WALL(lev->typ) || lev->typ == SDOOR) { (void) set_levltyp(x, y, svl.level.flags.is_maze_lev ? ROOM : svl.level.flags.is_cavernous_lev ? CORR : DOOR); + } + if (clear_flags) + lev->flags = 0; /* set_levltyp doesn't take care of this [yet?] */ unearth_objs(x, y); + recalc_block_point(x, y); break; case TELEP_TRAP: if (isok(gl.launchplace.x, gl.launchplace.y)) { @@ -586,6 +608,7 @@ fall_through( const char *dont_fall = 0; int newlevel; struct trap *t = (struct trap *) 0; + boolean controlled_flight = FALSE; /* we'll fall even while levitating in Sokoban; otherwise, if we won't fall and won't be told that we aren't falling, give up now */ @@ -631,12 +654,15 @@ fall_through( return; } if ((Flying || is_clinger(gy.youmonst.data)) - && (ftflags & TOOKPLUNGE) && td && t) + && (ftflags & TOOKPLUNGE) && td && t) { + if (Flying) + controlled_flight = TRUE; You("%s down %s!", Flying ? "swoop" : "deliberately drop", (t->ttyp == TRAPDOOR) ? "through the trap door" : "into the gaping hole"); + } if (*u.ushops) shopdig(1); @@ -656,7 +682,9 @@ fall_through( } dist = depth(&dtmp) - depth(&u.uz); if (dist > 1) - You("fall down a %s%sshaft!", dist > 3 ? "very " : "", + You("%s down a %s%sshaft!", + controlled_flight ? "fly" : "fall", + dist > 3 ? "very " : "", dist > 2 ? "deep " : ""); } if (!td) @@ -1107,10 +1135,13 @@ m_harmless_trap(struct monst *mtmp, struct trap *ttmp) return TRUE; break; case PIT: + FALLTHROUGH; /*FALLTHRU*/ case SPIKED_PIT: + FALLTHROUGH; /*FALLTHRU*/ case HOLE: + FALLTHROUGH; /*FALLTHRU*/ case TRAPDOOR: if (is_clinger(mdat) && !Sokoban) @@ -1551,7 +1582,7 @@ trapeffect_rust_trap( struct trap *trap, unsigned int trflags UNUSED) { - struct obj *otmp; + struct obj *otmp, *nextobj; if (mtmp == &gy.youmonst) { seetrap(trap); @@ -1583,10 +1614,12 @@ trapeffect_rust_trap( pline("%s you!", A_gush_of_water_hits); /* note: exclude primary and secondary weapons from splashing because cases 1 and 2 target them [via water_damage()] */ - for (otmp = gi.invent; otmp; otmp = otmp->nobj) + for (otmp = gi.invent; otmp; otmp = nextobj) { + nextobj = otmp->nobj; if (otmp->lamplit && otmp != uwep && (otmp != uswapwep || !u.twoweap)) (void) splash_lit(otmp); + } if (uarmc) (void) water_damage(uarmc, cloak_simple_name(uarmc), TRUE); else if (uarm) @@ -2001,8 +2034,6 @@ trapeffect_hole( if (in_sight) { pline_mon(mtmp, "%s seems to be yanked down!", Monnam(mtmp)); - /* suppress message in mlevel_tele_trap() */ - in_sight = FALSE; seetrap(trap); } } else @@ -2169,6 +2200,7 @@ trapeffect_web( mtmp->mtrapped = 1; break; } + FALLTHROUGH; /*FALLTHRU*/ default: if (mptr->mlet == S_GIANT @@ -2418,7 +2450,7 @@ trapeffect_poly_trap( boolean in_sight = canseemon(mtmp) || (mtmp == u.usteed); if (resists_magm(mtmp)) { - shieldeff(mtmp->mx, mtmp->my); + shieldeff_mon(mtmp); } else if (!resist(mtmp, WAND_CLASS, 0, NOTELL)) { (void) newcham(mtmp, (struct permonst *) 0, NC_SHOW_MSG); if (in_sight) @@ -2700,6 +2732,7 @@ immune_to_trap(struct monst *mon, unsigned ttype) if (pm->msize <= MZ_SMALL || amorphous(pm) || is_whirly(pm) || unsolid(pm)) return TRAP_CLEARLY_IMMUNE; + FALLTHROUGH; /*FALLTHRU*/ case SQKY_BOARD: case LANDMINE: @@ -2788,6 +2821,7 @@ immune_to_trap(struct monst *mon, unsigned ttype) for monsters, only replicates fire trap, so fall through */ if (is_you) return TRAP_NOT_IMMUNE; + FALLTHROUGH; /*FALLTHRU*/ case FIRE_TRAP: /* can always destroy items being carried */ /* harmful if not resistant or if carrying anything that could burn */ @@ -3104,6 +3138,9 @@ blow_up_landmine(struct trap *trap) } } } + fill_pit(x, y); + maybe_dunk_boulders(x, y); + recalc_block_point(x, y); spot_checks(x, y, old_typ); } @@ -3223,10 +3260,12 @@ launch_obj( /* use otrapped as a flag to ohitmon */ singleobj->otrapped = 1; style &= ~LAUNCH_KNOWN; + FALLTHROUGH; /*FALLTHRU*/ case ROLL: roll: delaycnt = 2; + FALLTHROUGH; /*FALLTHRU*/ default: if (!delaycnt) @@ -3331,6 +3370,7 @@ launch_obj( /* if trap doesn't work, skip "disappears" message */ if (newlev == depth(&u.uz)) break; + FALLTHROUGH; /*FALLTHRU*/ case TELEP_TRAP: if (cansee(x, y)) @@ -3381,7 +3421,7 @@ launch_obj( const char *bmsg = " as one boulder sets another in motion"; coordxy fx = x + dx, fy = y + dy; - if (!isok(fx, fy) || !dist || IS_ROCK(levl[fx][fy].typ)) + if (!isok(fx, fy) || !dist || IS_OBSTRUCTED(levl[fx][fy].typ)) bmsg = " as one boulder hits another"; Soundeffect(se_loud_crash, 80); @@ -3403,7 +3443,7 @@ launch_obj( } levl[x][y].doormask = D_BROKEN; if (dist) - unblock_point(x, y); + recalc_block_point(x, y); } /* if about to hit something, do so now */ @@ -4029,6 +4069,7 @@ float_down( case TRAPDOOR: if (!Can_fall_thru(&u.uz) || u.ustuck) break; + FALLTHROUGH; /*FALLTHRU*/ default: if (!u.utrap) /* not already in the trap */ @@ -4764,13 +4805,14 @@ emergency_disrobe(boolean *lostsome) int invc = inv_cnt(TRUE); while (near_capacity() > (Punished ? UNENCUMBERED : SLT_ENCUMBER)) { - struct obj *obj, *otmp = (struct obj *) 0; + struct obj *obj, *nextobj, *otmp = (struct obj *) 0; int i; /* Pick a random object */ if (invc > 0) { i = rn2(invc); - for (obj = gi.invent; obj; obj = obj->nobj) { + for (obj = gi.invent; obj; obj = nextobj) { + nextobj = obj->nobj; /* * Undroppables are: body armor, boots, gloves, * amulets, and rings because of the time and effort @@ -5234,6 +5276,24 @@ cnv_trap_obj( deltrap(ttmp); } +/* whether moving to a trap location is moving "into" the trap or "onto" it */ +boolean +into_vs_onto(int traptype) +{ + switch (traptype) { + case BEAR_TRAP: + case PIT: + case SPIKED_PIT: + case HOLE: + case TELEP_TRAP: + case LEVEL_TELEP: + case MAGIC_PORTAL: + case WEB: + return TRUE; + } + return FALSE; +} + /* while attempting to disarm an adjacent trap, we've fallen into it */ staticfn void move_into_trap(struct trap *ttmp) @@ -5243,9 +5303,13 @@ move_into_trap(struct trap *ttmp) boolean unused; bx = by = cx = cy = 0; /* lint suppression */ - /* we know there's no monster in the way, and we're not trapped */ - if (!Punished - || drag_ball(x, y, &bc, &bx, &by, &cx, &cy, &unused, TRUE)) { + /* we know there's no monster in the way and we're not trapped, but + need to make sure the move is not diagonally into or out of a + doorway; the sgn() calls are redundant since ttmp is adjacent */ + if (test_move(u.ux, u.uy, sgn(x - u.ux), sgn(y - u.uy), TEST_MOVE) + && (!Punished + || drag_ball(x, y, &bc, &bx, &by, &cx, &cy, &unused, TRUE))) { + /* move hero and update map */ u.ux0 = u.ux, u.uy0 = u.uy; /* set u.ux,u.uy and u.usteed->mx,my plus handle CLIPPING */ u_on_newpos(x, y); @@ -5270,6 +5334,11 @@ move_into_trap(struct trap *ttmp) if ((ttmp = t_at(u.ux, u.uy)) != 0) ttmp->tseen = 1; exercise(A_WIS, FALSE); + } else { + /* caller has just printed "Whoops..." so if hero is prevented from + moving, a followup message is needed */ + pline("Fortunately, you don't move %s it.", + into_vs_onto(ttmp->ttyp) ? "into" : "onto"); } } @@ -5297,7 +5366,7 @@ try_disarm( /* duplicate tight-space checks from test_move */ if (u.dx && u.dy && bad_rock(gy.youmonst.data, u.ux, ttmp->ty) && bad_rock(gy.youmonst.data, ttmp->tx, u.uy)) { - if ((gi.invent && (inv_weight() + weight_cap() > 600)) + if ((gi.invent && (inv_weight() + weight_cap() > WT_TOOMUCH_DIAGONAL)) || bigmonst(gy.youmonst.data)) { /* don't allow untrap if they can't get thru to it */ You("are unable to reach the %s!", trapname(ttype, FALSE)); @@ -5627,6 +5696,32 @@ help_monster_out( return 1; } +staticfn void +disarm_box(struct obj *box, boolean force, boolean confused) +{ + if (box->otrapped) { + int ch = ACURR(A_DEX) + u.ulevel; + + if (Role_if(PM_ROGUE)) + ch *= 2; + if (!force && (confused || Fumbling + || rnd(75 + level_difficulty() / 2) > ch)) { + (void) chest_trap(box, FINGER, TRUE); + /* 'box' might be gone now */ + } else { + You("disarm it!"); + box->otrapped = 0; + box->tknown = 1; + more_experienced(8, 0); + newexplevel(); + } + exercise(A_DEX, TRUE); + } else { + pline("That %s was not trapped.", xname(box)); + box->tknown = 0; + } +} + /* check a particular container for a trap and optionally disarm it */ staticfn void untrap_box( @@ -5636,32 +5731,19 @@ untrap_box( { if ((box->otrapped && (force || (!confused && rn2(MAXULEV + 1 - u.ulevel) < 10))) + || box->tknown || (!force && confused && !rn2(3))) { - You("find a trap on %s!", the(xname(box))); + if (!(box->tknown && box->dknown)) + You("find a trap on %s!", the(xname(box))); + else + pline("There's a trap on %s.", the(xname(box))); + box->tknown = 1; + box->dknown = 1; if (!confused) exercise(A_WIS, TRUE); - if (ynq("Disarm it?") == 'y') { - if (box->otrapped) { - int ch = ACURR(A_DEX) + u.ulevel; - - if (Role_if(PM_ROGUE)) - ch *= 2; - if (!force && (confused || Fumbling - || rnd(75 + level_difficulty() / 2) > ch)) { - (void) chest_trap(box, FINGER, TRUE); - /* 'box' might be gone now */ - } else { - You("disarm it!"); - box->otrapped = 0; - more_experienced(8, 0); - newexplevel(); - } - exercise(A_DEX, TRUE); - } else { - pline("That %s was not trapped.", xname(box)); - } - } + if (ynq("Disarm it?") == 'y') + disarm_box(box, force, confused); } else { You("find no traps on %s.", the(xname(box))); } @@ -5822,20 +5904,28 @@ untrap( whether any had been found but not attempted to untrap; now at most one per move may be checked and we only continue on to door handling if they are all declined */ - for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp->nexthere) - if (Is_box(otmp)) { + for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp->nexthere) { + if (!Is_box(otmp)) + continue; + if (otmp->tknown && otmp->dknown) + (void) safe_qbuf(qbuf, "Disarm this ", NULL, + otmp, xname, ansimpleoname, "a box"); + else (void) safe_qbuf(qbuf, "There is ", " here. Check it for traps?", otmp, doname, ansimpleoname, "a box"); - switch (ynq(qbuf)) { + switch (ynq(qbuf)) { case 'q': return 0; case 'y': - untrap_box(otmp, force, confused); + if (otmp->tknown && otmp->dknown) + disarm_box(otmp, force, confused); + else + untrap_box(otmp, force, confused); return 1; /* even for 'no' at "Disarm it?" prompt */ } /* 'n' => continue to next box */ - } + } There("are no other chests or boxes here."); } @@ -5919,7 +6009,7 @@ openholdingtrap( boolean *noticed) /* set to true iff hero notices the effect; * otherwise left with its previous value intact */ { - struct trap *t; + struct trap *t, tdummy; char buf[BUFSZ], whichbuf[20]; const char *trapdescr = 0, *which = 0; boolean ishero = (mon == &gy.youmonst); @@ -5932,7 +6022,15 @@ openholdingtrap( t = t_at(ishero ? u.ux : mon->mx, ishero ? u.uy : mon->my); if (ishero && u.utrap) { /* all u.utraptype values are holding traps */ + /* there might not be any trap at hero's spot for tt_buriedball; + conversely, there might be an unrelated trap at that spot */ + if (!t) { + t = &tdummy; + (void) memset(t, 0, sizeof *t), t->ntrap = NULL; + /* fallback 't' is now nonNull, t->tseen and t->madeby_u are 0 */ + } which = the_your[(!t || !t->tseen || !t->madeby_u) ? 0 : 1]; + switch (u.utraptype) { case TT_LAVA: trapdescr = "molten lava"; @@ -5948,7 +6046,9 @@ openholdingtrap( case TT_BEARTRAP: case TT_PIT: case TT_WEB: - trapdescr = 0; /* use defsyms[].explanation */ + trapdescr = defsyms[(u.utraptype == TT_WEB) ? S_web + : (u.utraptype == TT_PIT) ? S_pit + : S_bear_trap].explanation; break; default: /* lint suppression in case 't' is unexpectedly Null @@ -5960,10 +6060,9 @@ openholdingtrap( /* if no trap here or it's not a holding trap, we're done */ if (!t || (t->ttyp != BEAR_TRAP && t->ttyp != WEB)) return FALSE; - } - - if (!trapdescr) trapdescr = trapname(t->ttyp, FALSE); + } + assert(t != NULL); if (!which) which = t->tseen ? the_your[t->madeby_u] : strchr(vowels, *trapdescr) ? "an" : "a"; @@ -6095,7 +6194,8 @@ openfallingtrap( return result; } -/* only called when the player is doing something to the chest directly */ +/* only called when the player is doing something to the chest directly; + returns True if chest is destroyed, False if it remains in play */ boolean chest_trap( struct obj *obj, @@ -6110,8 +6210,9 @@ chest_trap( if (get_obj_location(obj, &cc.x, &cc.y, 0)) /* might be carried */ obj->ox = cc.x, obj->oy = cc.y; + otmp->tknown = 0; /* for xname(); will be set to 1 below */ otmp->otrapped = 0; /* trap is one-shot; clear flag first in case - chest kills you and ends up in bones file */ + * chest kills you and ends up in bones file */ You(disarm ? "set it off!" : "trigger a trap!"); display_nhwindow(WIN_MESSAGE, FALSE); if (Luck > -13 && rn2(13 + Luck) > 7) { /* saved by luck */ @@ -6156,7 +6257,7 @@ chest_trap( case 21: { struct monst *shkp = 0; long loss = 0L; - boolean costly, insider; + boolean costly, insider, chestgone; coordxy ox = obj->ox, oy = obj->oy; /* the obj location need not be that of player */ @@ -6186,28 +6287,35 @@ chest_trap( && uball->ox == ox && uball->oy == oy))) unpunish(); /* destroy everything at the spot (the Amulet, the - invocation tools, and Rider corpses will remain intact) */ + invocation tools, and Rider corpses will remain intact); + usually the chest will be destroyed along with the stuff at + this spot, but not if it is being carried */ + chestgone = FALSE; for (otmp = svl.level.objects[ox][oy]; otmp; otmp = otmp2) { otmp2 = otmp->nexthere; if (costly) loss += stolen_value(otmp, otmp->ox, otmp->oy, (boolean) shkp->mpeaceful, TRUE); + if (otmp == obj) + chestgone = TRUE; delobj(otmp); } wake_nearby(FALSE); losehp(Maybe_Half_Phys(d(6, 6)), buf, KILLED_BY_AN); exercise(A_STR, FALSE); if (costly && loss) { - if (insider) + if (insider) { You("owe %ld %s for objects destroyed.", loss, currency(loss)); - else { + } else { You("caused %ld %s worth of damage!", loss, currency(loss)); make_angry_shk(shkp, ox, oy); } } - return TRUE; + if (chestgone) + return TRUE; + break; /* set tknown and return False */ } /* case 21 */ case 20: case 19: @@ -6292,6 +6400,7 @@ chest_trap( bot(); /* to get immediate botl re-display */ } + otmp->tknown = 1; /* hero knows chest is no longer trapped */ return FALSE; } @@ -6351,7 +6460,8 @@ conjoined_pits( struct trap *trap1, boolean u_entering_trap2) { - coordxy dx, dy, diridx, adjidx; + coordxy dx, dy; + int diridx, adjidx; if (!trap1 || !trap2) return FALSE; @@ -6589,7 +6699,7 @@ static const char lava_killer[] = "molten lava"; boolean lava_effects(void) { - struct obj *obj, *obj2; + struct obj *obj, *obj2, *nextobj; boolean usurvive, boil_away; unsigned protect_oid = 0; int burncount = 0, burnmesgcount = 0; @@ -6616,7 +6726,8 @@ lava_effects(void) * emergency save file created before item destruction. */ if (!usurvive) { - for (obj = gi.invent; obj; obj = obj->nobj) { + for (obj = gi.invent; obj; obj = nextobj) { + nextobj = obj->nobj; if (obj->in_use) { /* remove_worn_item() sets in_use */ /* one item can be protected from burning up [accommodates steal(AMULET_OF_FLYING) -> remove_worn_item() -> fall @@ -6952,10 +7063,11 @@ trapname( void ignite_items(struct obj *objchn) { - struct obj *obj; + struct obj *obj, *nextobj; boolean bynexthere = (objchn && objchn->where == OBJ_FLOOR); - for (obj = objchn; obj; obj = bynexthere ? obj->nexthere : obj->nobj) { + for (obj = objchn; obj; obj = bynexthere ? obj->nexthere : nextobj) { + nextobj = obj->nobj; /* ignitable items like lamps and candles will catch fire */ if (!obj->lamplit && !obj->in_use) catch_lit(obj); @@ -6978,7 +7090,8 @@ trap_ice_effects(coordxy x, coordxy y, boolean ice_is_melting) int otyp = (ttmp->ttyp == LANDMINE) ? LAND_MINE : BEARTRAP; cnv_trap_obj(otyp, 1, ttmp, TRUE); } else { - deltrap(ttmp); + if (!undestroyable_trap(ttmp->ttyp)) + deltrap(ttmp); } } } diff --git a/src/u_init.c b/src/u_init.c index 58040518f..e4d60dd62 100644 --- a/src/u_init.c +++ b/src/u_init.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 u_init.c $NHDT-Date: 1725227809 2024/09/01 21:56:49 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.111 $ */ +/* NetHack 3.7 u_init.c $NHDT-Date: 1737620595 2025/01/23 00:23:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.113 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2017. */ /* NetHack may be freely redistributed. See license for details. */ @@ -963,6 +963,7 @@ u_init(void) u.uevent.uheard_tune = 0; u.uevent.uopened_dbridge = 0; u.uevent.udemigod = 0; /* not a demi-god yet... */ + u.uevent.amulet_wish = 0; u.udg_cnt = 0; u.mh = u.mhmax = u.mtimedone = 0; u.uz.dnum = u.uz0.dnum = 0; @@ -1329,6 +1330,7 @@ ini_inv(struct trobj *trop) * it was UNDEF_TYP or not after this. */ otyp = ini_inv_obj_substitution(trop, obj); + nhUse(otyp); /* nudist gets no armor */ if (u.uroleplay.nudist && obj->oclass == ARMOR_CLASS) { diff --git a/src/uhitm.c b/src/uhitm.c index bda3557f0..cbf797e8e 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 uhitm.c $NHDT-Date: 1713334817 2024/04/17 06:20:17 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.444 $ */ +/* NetHack 3.7 uhitm.c $NHDT-Date: 1752823766 2025/07/17 23:29:26 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.477 $ */ /* 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,8 @@ staticfn boolean mhitm_mgc_atk_negated(struct monst *, struct monst *, staticfn boolean known_hitum(struct monst *, struct obj *, int *, int, int, struct attack *, int) NONNULLARG13; staticfn boolean theft_petrifies(struct obj *) NONNULLARG1; +staticfn void mhitm_really_poison(struct monst *, struct attack *, + struct monst *, struct mhitm_data *); staticfn void steal_it(struct monst *, struct attack *) NONNULLARG1; /* hitum_cleave() has contradictory information. There's a comment * beside the 1st arg 'target' stating non-null, but later on there @@ -88,7 +90,7 @@ mhitm_mgc_atk_negated( if (mdef == &gy.youmonst) You("avoid harm."); else if (gv.vis && canseemon(mdef)) - pline("%s avoids harm.", Monnam(mdef)); + pline_mon(mdef, "%s avoids harm.", Monnam(mdef)); } return TRUE; } @@ -469,7 +471,7 @@ do_attack(struct monst *mtmp) */ boolean foo = (Punished || !rn2(7) || (is_longworm(mtmp->data) && mtmp->wormno) - || (IS_ROCK(levl[u.ux][u.uy].typ) + || (IS_OBSTRUCTED(levl[u.ux][u.uy].typ) && !passes_walls(mtmp->data))), inshop = FALSE; char *p; @@ -1036,6 +1038,9 @@ hmon_hitmon_weapon_melee( if (obj->opoisoned && is_poisonable(obj)) hmd->ispoisoned = TRUE; } + /* permapoisoned is non-ammo/missile, limit the poison */ + if (permapoisoned(obj) && hmd->dieroll <= 5) + hmd->ispoisoned = TRUE; } staticfn void @@ -1050,7 +1055,8 @@ hmon_hitmon_weapon( /* or strike with a missile in your hand... */ || (!hmd->thrown && (is_missile(obj) || is_ammo(obj))) /* or use a pole at short range and not mounted... */ - || (!hmd->thrown && !u.usteed && is_pole(obj)) + || (!hmd->thrown && !u.usteed && is_pole(obj) + && !is_art(obj,ART_SNICKERSNEE)) /* or throw a missile without the proper bow... */ || (is_ammo(obj) && (hmd->thrown != HMON_THROWN || !ammo_and_launcher(obj, uwep)))) { @@ -1484,7 +1490,7 @@ hmon_hitmon_poison( You_feel("like an evil coward for using a poisoned weapon."); adjalign(-1); } - if (!rn2(nopoison)) { + if (!permapoisoned(obj) && !rn2(nopoison)) { /* remove poison now in case obj ends up in a bones file */ obj->opoisoned = FALSE; /* defer "obj is no longer poisoned" until after hit message */ @@ -1719,6 +1725,7 @@ hmon_hitmon( int dieroll) { struct _hitmon_data hmd; + boolean maybe_knockback = FALSE; hmd.dmg = 0; hmd.thrown = thrown; @@ -1732,7 +1739,7 @@ hmon_hitmon( hmd.silverobj = FALSE; hmd.lightobj = FALSE; hmd.material = obj ? objects[obj->otyp].oc_material - : 0; /* 0 == NO_MATERIAL */ + : NO_MATERIAL; hmd.jousting = 0; hmd.hittxt = FALSE; hmd.get_dmg_bonus = TRUE; @@ -1785,6 +1792,9 @@ hmon_hitmon( hmon_hitmon_jousting(&hmd, mon, obj); } else if (hmd.unarmed && hmd.dmg > 1 && !thrown && !obj && !Upolyd) { hmon_hitmon_stagger(&hmd, mon, obj); + } else if (!hmd.unarmed && hmd.dmg > 1 && !thrown && !Upolyd + && !u.twoweap && uwep) { + maybe_knockback = TRUE; } if (!hmd.already_killed) { @@ -1813,8 +1823,10 @@ hmon_hitmon( hmon_hitmon_msg_hit(&hmd, mon, obj); - if (hmd.dryit) /* dryit implies wet towel, so 'obj' is still intact */ + if (hmd.dryit) { /* dryit implies wet towel, so 'obj' is still intact */ + assert(obj != NULL); dry_a_towel(obj, -1, TRUE); + } if (hmd.silvermsg) hmon_hitmon_msg_silver(&hmd, mon, obj); @@ -1826,8 +1838,10 @@ hmon_hitmon( obj->opoisoned was cleared above and any message referring to "poisoned " has now been given; we want just "" for last message, so reformat while obj is still accessible */ - if (hmd.unpoisonmsg) + if (hmd.unpoisonmsg) { + assert(obj != NULL); Strcpy(hmd.saved_oname, cxname(obj)); + } /* [note: thrown obj might go away during killed()/xkilled() call (via 'thrownobj'; if swallowed, it gets added to engulfer's @@ -1860,9 +1874,17 @@ hmon_hitmon( Your("%s %s no longer poisoned.", hmd.saved_oname, vtense(hmd.saved_oname, "are")); - if (!hmd.destroyed) - wakeup(mon, TRUE); + if (!hmd.destroyed) { + int hitflags = M_ATTK_HIT; + wakeup(mon, TRUE); + if (maybe_knockback + && mhitm_knockback(&gy.youmonst, mon, gy.youmonst.data->mattk, + &hitflags, TRUE)) { + if ((hitflags & M_ATTK_DEF_DIED) != 0) + hmd.destroyed = TRUE; + } + } return hmd.destroyed ? FALSE : TRUE; } @@ -2242,7 +2264,7 @@ mhitm_ad_rust( return; if (completelyrusts(pd)) { /* PM_IRON_GOLEM */ if (gv.vis && canseemon(mdef)) - pline("%s %s to pieces!", Monnam(mdef), + pline_mon(mdef, "%s %s to pieces!", Monnam(mdef), !mlifesaver(mdef) ? "falls" : "starts to fall"); monkilled(mdef, (char *) 0, AD_RUST); if (!DEADMONSTER(mdef)) { @@ -2322,7 +2344,7 @@ mhitm_ad_dcay( /* note: the life-saved case is hypothetical because life-saving doesn't work for golems */ if (gv.vis && canseemon(mdef)) - pline("%s %s to pieces!", Monnam(mdef), + pline_mon(mdef, "%s %s to pieces!", Monnam(mdef), !mlifesaver(mdef) ? "falls" : "starts to fall"); monkilled(mdef, (char *) 0, AD_DCAY); if (!DEADMONSTER(mdef)) { @@ -2424,7 +2446,7 @@ mhitm_ad_drli( if (!is_death) /* Stormbringer uses monhp_per_lvl (1d8) */ mhm->damage = d(2, 6); if (gv.vis && canspotmon(mdef)) - pline("%s becomes weaker!", Monnam(mdef)); + pline_mon(mdef, "%s becomes weaker!", Monnam(mdef)); if (mdef->mhpmax - mhm->damage > (int) mdef->m_lev) { mdef->mhpmax -= mhm->damage; } else { @@ -2518,12 +2540,12 @@ mhitm_ad_fire( return; } if (gv.vis && canseemon(mdef)) - pline("%s is %s!", Monnam(mdef), on_fire(pd, mattk)); + pline_mon(mdef, "%s is %s!", Monnam(mdef), on_fire(pd, mattk)); if (completelyburns(pd)) { /* paper golem or straw golem */ /* note: the life-saved case is hypothetical because life-saving doesn't work for golems */ if (gv.vis && canseemon(mdef)) - pline("%s %s!", Monnam(mdef), + pline_mon(mdef, "%s %s!", Monnam(mdef), !mlifesaver(mdef) ? "burns completely" : "is totally engulfed in flames"); monkilled(mdef, (char *) 0, AD_FIRE); @@ -2595,7 +2617,7 @@ mhitm_ad_cold( return; } if (gv.vis && canseemon(mdef)) - pline("%s is covered in frost!", Monnam(mdef)); + pline_mon(mdef, "%s is covered in frost!", Monnam(mdef)); if (resists_cold(mdef) || defended(mdef, AD_COLD)) { if (gv.vis && canseemon(mdef)) pline_The("frost doesn't seem to chill %s!", mon_nam(mdef)); @@ -2653,7 +2675,7 @@ mhitm_ad_elec( return; } if (gv.vis && canseemon(mdef)) - pline("%s gets zapped!", Monnam(mdef)); + pline_mon(mdef, "%s gets zapped!", Monnam(mdef)); if (resists_elec(mdef) || defended(mdef, AD_ELEC)) { if (gv.vis && canseemon(mdef)) pline_The("zap doesn't shock %s!", mon_nam(mdef)); @@ -2702,7 +2724,7 @@ mhitm_ad_acid( Monnam(mdef), hliquid("acid")); mhm->damage = 0; } else if (gv.vis && canseemon(mdef)) { - pline("%s is covered in %s!", Monnam(mdef), hliquid("acid")); + pline_mon(mdef, "%s is covered in %s!", Monnam(mdef), hliquid("acid")); pline("It burns %s!", mon_nam(mdef)); } if (!rn2(30)) @@ -2813,7 +2835,7 @@ mhitm_ad_tlpt( int tmphp; hitmsg(magr, mattk); - if (!mhitm_mgc_atk_negated(magr, mdef, FALSE)) { + if (mhitm_mgc_atk_negated(magr, mdef, FALSE)) { You("are not affected."); } else { if (flags.verbose) @@ -2858,7 +2880,7 @@ mhitm_ad_tlpt( ; /* no negation message */ } else if (mhitm_mgc_atk_negated(magr, mdef, TRUE)) { if (gv.vis) - pline("%s is not affected.", Monnam(mdef)); + pline_mon(mdef, "%s is not affected.", Monnam(mdef)); } else { char mdef_Monnam[BUFSZ]; boolean wasseen = canspotmon(mdef); @@ -2972,7 +2994,7 @@ mhitm_ad_curs( if (Blind) { You_hear("laughter."); } else { - pline("%s chuckles.", Monnam(magr)); + pline_mon(magr, "%s chuckles.", Monnam(magr)); } } if (u.umonnum == PM_CLAY_GOLEM) { @@ -2996,7 +3018,7 @@ mhitm_ad_curs( if (gv.vis && canseemon(mdef)) { pline("Some writing vanishes from %s head!", s_suffix(mon_nam(mdef))); - pline("%s is destroyed!", Monnam(mdef)); + pline_mon(mdef, "%s is destroyed!", Monnam(mdef)); } mondied(mdef); if (!DEADMONSTER(mdef)) { @@ -3016,12 +3038,35 @@ mhitm_ad_curs( if (!gv.vis) You_hear("laughter."); else if (canseemon(magr)) - pline("%s chuckles.", Monnam(magr)); + pline_mon(magr, "%s chuckles.", Monnam(magr)); } } } } +/* Helper for mhitm_ad_drst(), containing some code that is also called from + * mhitm_ad_phys (for poisoned weapons) and shouldn't be subject to magic + * cancellation or a 1/8 chance roll. + * In this specific case, the "mhitm" in the name ACTUALLY means just that - + * this should be called only for monster versus monster situations. */ +staticfn void +mhitm_really_poison(struct monst *magr, struct attack *mattk, + struct monst *mdef, struct mhitm_data *mhm) +{ + if (gv.vis && canspotmon(magr)) + pline("%s %s was poisoned!", s_suffix(Monnam(magr)), + mpoisons_subj(magr, mattk)); + if (resists_poison(mdef)) { + if (gv.vis && canspotmon(mdef) && canspotmon(magr)) + pline_The("poison doesn't seem to affect %s.", + mon_nam(mdef)); + } else { + mhm->damage += rn1(10, 6); + if (mhm->damage >= mdef->mhp && gv.vis && canspotmon(mdef)) + pline_The("poison was deadly..."); + } +} + void mhitm_ad_drst( struct monst *magr, struct attack *mattk, @@ -3063,22 +3108,7 @@ mhitm_ad_drst( } else { /* mhitm */ if (!negated && !rn2(8)) { - if (gv.vis && canspotmon(magr)) - pline("%s %s was poisoned!", s_suffix(Monnam(magr)), - mpoisons_subj(magr, mattk)); - if (resists_poison(mdef)) { - if (gv.vis && canspotmon(mdef) && canspotmon(magr)) - pline_The("poison doesn't seem to affect %s.", - mon_nam(mdef)); - } else { - if (rn2(10)) { - mhm->damage += rn1(10, 6); - } else { - if (gv.vis && canspotmon(mdef)) - pline_The("poison was deadly..."); - mhm->damage = mdef->mhp; - } - } + mhitm_really_poison(magr, mattk, mdef, mhm); } } } @@ -3180,14 +3210,21 @@ mhitm_ad_drin( } /* adjattrib gives dunce cap message when appropriate */ (void) adjattrib(A_INT, -rnd(2), FALSE); - + if (!rn2(5)) { + losespells(); + gs.skipdrin = TRUE; + } + if (!rn2(5)) { + drain_weapon_skill(rnd(2)); + gs.skipdrin = TRUE; + } } else { /* mhitm */ char buf[BUFSZ]; if (gn.notonhead || !has_head(pd)) { if (gv.vis && canspotmon(mdef)) - pline("%s doesn't seem harmed.", Monnam(mdef)); + pline_mon(mdef, "%s doesn't seem harmed.", Monnam(mdef)); /* Not clear what to do for green slimes */ mhm->damage = 0; /* don't bother with additional DRIN attacks since they wouldn't @@ -3318,10 +3355,11 @@ mhitm_ad_wrap( mhm->damage = 0; if (flags.verbose) { if (coil) - pline("%s brushes against you.", Monnam(magr)); + pline_mon(magr, "%s brushes against you.", + Monnam(magr)); else - pline("%s brushes against your %s.", Monnam(magr), - body_part(LEG)); + pline_mon(magr, "%s brushes against your %s.", + Monnam(magr), body_part(LEG)); } } } else @@ -3507,6 +3545,7 @@ mhitm_ad_slim( mhm->damage = 0; } } + nhUse(pd); } void @@ -3548,7 +3587,7 @@ mhitm_ad_ench( break; } } - if (drain_item(obj, FALSE)) { + if (obj && drain_item(obj, FALSE)) { pline("%s less effective.", Yobjnam2(obj, "seem")); } } @@ -3591,7 +3630,7 @@ mhitm_ad_slow( mon_adjust_speed(mdef, -1, (struct obj *) 0); mdef->mstrategy &= ~STRAT_WAITFORU; if (mdef->mspeed != oldspeed && gv.vis && canspotmon(mdef)) - pline("%s slows down.", Monnam(mdef)); + pline_mon(mdef, "%s slows down.", Monnam(mdef)); } } } @@ -3628,7 +3667,7 @@ mhitm_ad_conf( */ if (!magr->mcan && !mdef->mconf && !magr->mspec_used) { if (gv.vis && canseemon(mdef)) - pline("%s looks confused.", Monnam(mdef)); + pline_mon(mdef, "%s looks confused.", Monnam(mdef)); mdef->mconf = 1; mdef->mstrategy &= ~STRAT_WAITFORU; } @@ -3698,7 +3737,8 @@ mhitm_ad_famn( goto mhitm_famn; } else if (mdef == &gy.youmonst) { /* mhitu */ - pline("%s reaches out, and your body shrivels.", Monnam(magr)); + pline_mon(magr, "%s reaches out, and your body shrivels.", + Monnam(magr)); exercise(A_CON, FALSE); if (!is_fainted()) morehungry(rn1(40, 40)); @@ -3728,7 +3768,8 @@ mhitm_ad_pest( goto mhitm_pest; } else if (mdef == &gy.youmonst) { /* mhitu */ - pline("%s reaches out, and you feel fever and chills.", Monnam(magr)); + pline_mon(magr, "%s reaches out, and you feel fever and chills.", + Monnam(magr)); (void) diseasemu(pa); /* plus the normal damage */ } else { @@ -3756,7 +3797,7 @@ mhitm_ad_deth( goto mhitm_deth; } else if (mdef == &gy.youmonst) { /* mhitu */ - pline("%s reaches out with its deadly touch.", Monnam(magr)); + pline_mon(magr, "%s reaches out with its deadly touch.", Monnam(magr)); if (is_undead(pd)) { /* still does some damage */ mhm->damage = (mhm->damage + 1) / 2; @@ -3772,6 +3813,7 @@ mhitm_ad_deth( mhm->damage = 0; return; } + FALLTHROUGH; /*FALLTHRU*/ default: /* case 16: ... case 5: */ You_feel("your life force draining away..."); @@ -3818,7 +3860,7 @@ mhitm_ad_halu( /* mhitm */ if (!magr->mcan && haseyes(pd) && mdef->mcansee) { if (gv.vis && canseemon(mdef)) - pline("%s looks %sconfused.", Monnam(mdef), + pline_mon(mdef, "%s looks %sconfused.", Monnam(mdef), mdef->mconf ? "more " : ""); mdef->mconf = 1; mdef->mstrategy &= ~STRAT_WAITFORU; @@ -3866,7 +3908,7 @@ do_stone_mon( } if (!resists_ston(mdef)) { if (gv.vis && canseemon(mdef)) - pline("%s turns to stone!", Monnam(mdef)); + pline_mon(mdef, "%s turns to stone!", Monnam(mdef)); monstone(mdef); post_stone: if (!DEADMONSTER(mdef)) { @@ -3934,7 +3976,7 @@ mhitm_ad_phys( mhm->hitflags |= M_ATTK_MISS; } else { set_ustuck(magr); - pline("%s grabs you!", Monnam(magr)); + pline_mon(magr, "%s grabs you!", Monnam(magr)); mhm->hitflags |= M_ATTK_HIT; } } else if (u.ustuck == magr) { @@ -3948,12 +3990,14 @@ mhitm_ad_phys( if (mattk->aatyp == AT_WEAP && otmp) { struct obj *marmg; int tmp; + boolean was_poisoned = (otmp->opoisoned || permapoisoned(otmp)); if (otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm])) { mhm->damage = 1; - pline("%s hits you with the %s corpse.", Monnam(magr), - mons[otmp->corpsenm].pmnames[NEUTRAL]); + pline_mon(magr, "%s hits you with the %s corpse.", + Monnam(magr), + mons[otmp->corpsenm].pmnames[NEUTRAL]); if (!Stoned) { if (do_stone_u(magr)) { mhm->hitflags = M_ATTK_HIT; @@ -4005,6 +4049,21 @@ mhitm_ad_phys( You("divide as %s hits you!", mon_nam(magr)); } rustm(&gy.youmonst, otmp); + if (was_poisoned && gm.mhitu_dieroll <= 5) { + 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). */ + 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. + * (This is the same as poisoned ammo.) */ + poisoned(buf, A_STR, pmname(magr->data, Mgender(magr)), + 10, FALSE); + } } else if (mattk->aatyp != AT_TUCH || mhm->damage != 0 || magr != u.ustuck) { hitmsg(magr, mattk); @@ -4050,7 +4109,7 @@ mhitm_ad_phys( if (!artifact_hit(magr, mdef, mwep, &mhm->damage, mhm->dieroll)) { if (gv.vis) - pline("%s hits %s.", Monnam(magr), + pline_mon(magr, "%s hits %s.", Monnam(magr), mon_nam_too(mdef, magr)); mhm->hitflags |= M_ATTK_HIT; } @@ -4067,6 +4126,13 @@ mhitm_ad_phys( } if (mhm->damage) rustm(mdef, mwep); + if ((mwep->opoisoned || permapoisoned(mwep)) && !rn2(4)) { + /* 1/4 chance of weapon poison applying is the same as in + * uhitm and mhitu cases. But since we don't need to call + * any special functions or go through tangled hmon_hitmon + * code, we can just jump straight to the poisoning. */ + mhitm_really_poison(magr, mattk, mdef, mhm); + } } else if (pa == &mons[PM_PURPLE_WORM] && pd == &mons[PM_SHRIEKER]) { /* hack to enhance mm_aggression(); we don't want purple worm's bite attack to kill a shrieker because then it @@ -4197,7 +4263,8 @@ mhitm_ad_heal( && !uarms && !uarmg && !uarmf && !uarmh) { boolean goaway = FALSE; - pline("%s hits! (I hope you don't mind.)", Monnam(magr)); + pline_mon(magr, "%s hits! (I hope you don't mind.)", + Monnam(magr)); if (Upolyd) { u.mh += rnd(7); if (!rn2(7)) { @@ -4290,7 +4357,7 @@ mhitm_ad_stun( if (magr->mcan) return; if (canseemon(mdef)) - pline("%s %s for a moment.", Monnam(mdef), + pline_mon(mdef, "%s %s for a moment.", Monnam(mdef), makeplural(stagger(pd, "stagger"))); mdef->mstun = 1; mhitm_ad_phys(magr, mattk, mdef, mhm); @@ -4329,7 +4396,7 @@ mhitm_ad_legs( pline("%s tries to reach your %s %s!", Monst_name, sidestr, leg); mhm->damage = 0; } else if (magr->mcan) { - pline("%s nuzzles against your %s %s!", Monnam(magr), + pline_mon(magr, "%s nuzzles against your %s %s!", Monnam(magr), sidestr, leg); mhm->damage = 0; } else { @@ -4388,7 +4455,7 @@ mhitm_ad_dgst( /* eating a Rider or its corpse is fatal */ if (is_rider(pd)) { if (gv.vis && canseemon(magr)) - pline("%s %s!", Monnam(magr), + pline_mon(magr, "%s %s!", Monnam(magr), (pd == &mons[PM_FAMINE]) ? "belches feebly, shrivels up and dies" : (pd == &mons[PM_PESTILENCE]) @@ -4522,7 +4589,7 @@ mhitm_ad_sedu( is disabled, it would be changed to another damage type, but when defending, it remains as-is */ || dmgtype(gy.youmonst.data, AD_SSEX)) { - pline("%s %s.", Monnam(magr), + pline_mon(magr, "%s %s.", Monnam(magr), Deaf ? "says something but you can't hear it" : magr->minvent ? "brags about the goods some dungeon explorer provided" @@ -4560,8 +4627,9 @@ mhitm_ad_sedu( (void) rloc(magr, RLOC_MSG); if (is_animal(magr->data) && *buf) { if (canseemon(magr)) - pline("%s tries to %s away with %s.", Monnam(magr), - locomotion(magr->data, "run"), buf); + pline_mon(magr, "%s tries to %s away with %s.", + Monnam(magr), + locomotion(magr->data, "run"), buf); } monflee(magr, 0, FALSE, FALSE); mhm->hitflags = M_ATTK_AGR_DONE; /* return 3??? */ @@ -4937,6 +5005,7 @@ gulpum(struct monst *mdef, struct attack *mattk) "you totally digest " will be coming soon (after several turns) but the level-gain message seems out of order if the kill message is left implicit */ + gm.mswallower = &gy.youmonst; xkilled(mdef, XKILL_GIVEMSG | XKILL_NOCORPSE); if (!DEADMONSTER(mdef)) { /* monster lifesaved */ You("hurriedly regurgitate the sizzling in your %s.", @@ -4977,6 +5046,7 @@ gulpum(struct monst *mdef, struct attack *mattk) } else exercise(A_CON, TRUE); } + gm.mswallower = (struct monst *) 0; end_engulf(); return M_ATTK_DEF_DIED; case AD_PHYS: @@ -5131,14 +5201,55 @@ mhitm_knockback( const char *knockedhow; coordxy dx, dy, defx, defy; int knockdistance = rn2(3) ? 1 : 2; /* 67%: 1 step, 33%: 2 steps */ + int chance = 6; /* 1/6 chance of attack knocking back a monster */ boolean u_agr = (magr == &gy.youmonst); boolean u_def = (mdef == &gy.youmonst); boolean was_u = FALSE, dismount = FALSE; + struct obj *wep = weapon_used ? (u_agr ? uwep : MON_WEP(magr)) + : (struct obj *) 0; - /* 1/6 chance of attack knocking back a monster */ - if (rn2(6)) + if (wep && is_art(wep, ART_OGRESMASHER)) + chance = 2; + + if (rn2(chance)) return FALSE; + /* only certain attacks qualify for knockback */ + if (!((mattk->adtyp == AD_PHYS) + && (mattk->aatyp == AT_CLAW + || mattk->aatyp == AT_KICK + || mattk->aatyp == AT_BUTT + || mattk->aatyp == AT_WEAP))) + return FALSE; + + /* don't knockback if attacker also wants to grab or engulf */ + if (attacktype(magr->data, AT_ENGL) + || attacktype(magr->data, AT_HUGS) + || sticks(magr->data)) + return FALSE; + + /* decide where the first step will place the target; not accurate + for being knocked out of saddle but doesn't need to be; used for + test_move() and for message before actual hurtle */ + defx = u_def ? u.ux : mdef->mx; + defy = u_def ? u.uy : mdef->my; + dx = sgn(defx - (u_agr ? u.ux : magr->mx)); + dy = sgn(defy - (u_agr ? u.uy : magr->my)); + + /* can't move most targets into or out of a doorway diagonally */ + if (u_def) { + if (!test_move(defx, defy, dx, dy, TEST_MOVE)) + return FALSE; + } else { + /* subset of test_move() */ + if (!isok(defx + dx, defy + dy)) + return FALSE; + if (IS_DOOR(levl[defx][defy].typ) + && (defx - magr->mx && defy - magr->my) + && !doorless_door(defx, defy)) + return FALSE; + } + /* if hero is stuck to a cursed saddle, knock the steed back */ if (u_def && u.usteed) { if ((otmp = which_armor(u.usteed, W_SADDLE)) != 0 && otmp->cursed) { @@ -5159,12 +5270,8 @@ mhitm_knockback( if (!(magr->data->msize > (mdef->data->msize + 1))) return FALSE; - /* only certain attacks qualify for knockback */ - if (!((mattk->adtyp == AD_PHYS) - && (mattk->aatyp == AT_CLAW - || mattk->aatyp == AT_KICK - || mattk->aatyp == AT_BUTT - || (mattk->aatyp == AT_WEAP && !weapon_used)))) + /* no knockback with a flimsy or non-blunt weapon */ + if (wep && (is_flimsy(wep) || !is_blunt_weapon(wep))) return FALSE; /* needs a solid physical hit */ @@ -5190,13 +5297,6 @@ mhitm_knockback( return FALSE; } - /* decide where the first step will place the target; not accurate - for being knocked out of saddle but doesn't need to be; used for - message before actual hurtle */ - defx = u_def ? u.ux : mdef->mx; - defy = u_def ? u.uy : mdef->my; - dx = sgn(defx - (u_agr ? u.ux : magr->mx)); - dy = sgn(defy - (u_agr ? u.uy : magr->my)); /* subtly vary the message text if monster won't actually move */ knockedhow = dismount ? "out of your saddle" : will_hurtle(mdef, defx + dx, defy + dy) ? "backward" @@ -5297,6 +5397,14 @@ hmonas(struct monst *mon) for (i = 0; i < NATTK; i++) { /* sum[i] = M_ATTK_MISS; -- now done above */ + + /* target might have been knocked back so no longer in range, or an + engulfing vampshifted fog cloud killed and reverted to vampire + that's placed at another spot (hero occupies mon's first spot) */ + if (i > 0 && (m_at(gb.bhitpos.x, gb.bhitpos.y) != mon + || DEADMONSTER(mon))) + continue; + mattk = getmattk(&gy.youmonst, mon, i, sum, &alt_attk); if (gs.skipdrin && mattk->aatyp == AT_TENT && mattk->adtyp == AD_DRIN) continue; @@ -5385,12 +5493,18 @@ hmonas(struct monst *mon) case AT_CLAW: if (uwep && !cantwield(gy.youmonst.data) && !weapon_used) goto use_weapon; + FALLTHROUGH; /*FALLTHRU*/ case AT_TUCH: if (uwep && gy.youmonst.data->mlet == S_LICH && !weapon_used) goto use_weapon; + FALLTHROUGH; /*FALLTHRU*/ case AT_KICK: + if (mattk->aatyp == AT_KICK && mtrapped_in_pit(&gy.youmonst)) + continue; + FALLTHROUGH; + /*FALLTHRU*/ case AT_BITE: case AT_STNG: case AT_BUTT: @@ -5632,6 +5746,7 @@ hmonas(struct monst *mon) || gy.youmonst.data->mlet == S_ORC || gy.youmonst.data->mlet == S_GNOME) && !weapon_used) goto use_weapon; + FALLTHROUGH; /*FALLTHRU*/ case AT_NONE: @@ -5906,9 +6021,7 @@ passive( You("are suddenly very cold!"); mdamageu(mon, tmp); /* monster gets stronger with your heat! */ - mon->mhp += (tmp + rn2(2)) / 2; - if (mon->mhpmax < mon->mhp) - mon->mhpmax = mon->mhp; + healmon(mon, (tmp + rn2(2)) / 2, (tmp + 1) / 2); /* at a certain point, the monster will reproduce! */ if (mon->mhpmax > (((int) mon->m_lev) + 1) * 8) (void) split_mon(mon, &gy.youmonst); @@ -6016,6 +6129,8 @@ passive_obj( } break; } + FALLTHROUGH; + /* FALLTHRU */ default: break; } @@ -6026,40 +6141,97 @@ passive_obj( DISABLE_WARNING_FORMAT_NONLITERAL -/* Note: caller must ascertain mtmp is mimicking... */ +/* used by stumble_onto_mimic() and bhitm() cases WAN_LOCKING, WAN_OPENING */ void -stumble_onto_mimic(struct monst *mtmp) +that_is_a_mimic( + struct monst *mtmp, /* a hidden mimic (nonnull) */ + unsigned mimic_flags) /* 0, MIM_REVEAL, MIM_OMIT_WAIT, REVEAL+OMIT */ { - const char *fmt = "Wait! That's %s!", *generic = "a monster", *what = 0; - - if (!u.ustuck && !mtmp->mflee && dmgtype(mtmp->data, AD_STCK) - /* must be adjacent; attack via polearm could be from farther away */ - && m_next2u(mtmp)) - set_ustuck(mtmp); + static char generic[] = "a monster"; + char fmtbuf[BUFSZ]; + const char *what = NULL; + boolean reveal_it = (mimic_flags & MIM_REVEAL) != 0, + omit_wait = (mimic_flags & MIM_OMIT_WAIT) != 0; + Strcpy(fmtbuf, "Wait! That's %s!"); if (Blind) { if (!Blind_telepat) what = generic; /* with default fmt */ else if (M_AP_TYPE(mtmp) == M_AP_MONSTER) what = a_monnam(mtmp); /* differs from what was sensed */ } else { - int glyph = levl[u.ux + u.dx][u.uy + u.dy].glyph; + coordxy x = mtmp->mx, y = mtmp->my; + int glyph = glyph_at(x, y); - if (glyph_is_cmap(glyph) && (glyph_to_cmap(glyph) == S_hcdoor - || glyph_to_cmap(glyph) == S_vcdoor)) - fmt = "The door actually was %s!"; - else if (glyph_is_object(glyph) && glyph_to_obj(glyph) == GOLD_PIECE) - fmt = "That gold was %s!"; + if (glyph_is_cmap(glyph)) { + int sym = glyph_to_cmap(glyph); + + if (M_AP_TYPE(mtmp) == M_AP_FURNITURE + || (M_AP_TYPE(mtmp) == M_AP_OBJECT && sym == S_trapped_chest)) + Snprintf(fmtbuf, sizeof fmtbuf, "That %s actually is %%s!", + defsyms[sym].explanation); + } else if (glyph_is_object(glyph)) { + boolean fakeobj; + const char *otmp_name; + struct obj *otmp = NULL; + + fakeobj = object_from_map(glyph, x, y, &otmp); + otmp_name = (otmp && otmp->otyp != STRANGE_OBJECT) + ? simpleonames(otmp) : "strange object"; + Snprintf(fmtbuf, sizeof fmtbuf, "%s %s %s %%s!", + (otmp && is_plural(otmp)) ? "Those" : "That", + otmp_name, otmp ? otense(otmp, "are") : "is"); + if (fakeobj && otmp) { + otmp->where = OBJ_FREE; /* object_from_map set to OBJ_FLOOR */ + dealloc_obj(otmp); + } + } else if (glyph_is_monster(glyph)) { + const char *mtmp_name; + int mndx = glyph_to_mon(glyph); + + assert(mndx >= LOW_PM && mndx <= HIGH_PM); + mtmp_name = pmname(&mons[mndx], Mgender(mtmp)); + Snprintf(fmtbuf, sizeof fmtbuf, + "Wait! That %s is really %%s!", mtmp_name); + } /* cloned Wiz starts out mimicking some other monster and might make himself invisible before being revealed */ if (mtmp->minvis && !See_invisible) what = generic; + else if (M_AP_TYPE(mtmp) == M_AP_MONSTER) + what = x_monnam(mtmp, ARTICLE_A, (char *) NULL, EXACT_NAME, TRUE); + else if (mtmp->data->mlet == S_MIMIC + && (M_AP_TYPE(mtmp) == M_AP_OBJECT + || M_AP_TYPE(mtmp) == M_AP_FURNITURE) + && (mtmp->msleeping || mtmp->mfrozen)) + /* BUG: this will misclassify a paralyzed mimic as sleeping */ + what = x_monnam(mtmp, ARTICLE_A, "sleeping", 0, FALSE); else what = a_monnam(mtmp); } - if (what) - pline(fmt, what); + + if (what) { + int i = (omit_wait && !strncmp(fmtbuf, "Wait! ", 7)) ? 7 : 0; + + pline(&fmtbuf[i], what); + } + if (reveal_it) + seemimic(mtmp); +} + +RESTORE_WARNING_FORMAT_NONLITERAL + +/* Note: caller must ascertain mtmp is mimicking... */ +void +stumble_onto_mimic(struct monst *mtmp) +{ + that_is_a_mimic(mtmp, MIM_REVEAL); + + if (!u.ustuck && !mtmp->mflee && dmgtype(mtmp->data, AD_STCK) + /* must be adjacent; attack via polearm could be from farther away */ + && m_next2u(mtmp)) + set_ustuck(mtmp); wakeup(mtmp, FALSE); /* clears mimicking */ /* if hero is blind, wakeup() won't display the monster even though @@ -6069,8 +6241,6 @@ stumble_onto_mimic(struct monst *mtmp) map_invisible(mtmp->mx, mtmp->my); } -RESTORE_WARNING_FORMAT_NONLITERAL - staticfn void nohandglow(struct monst *mon) { @@ -6184,8 +6354,14 @@ flash_hits_mon( void light_hits_gremlin(struct monst *mon, int dmg) { - pline("%s %s!", Monnam(mon), - (dmg > mon->mhp / 2) ? "wails in agony" : "cries out in pain"); + if (!Deaf && mdistu(mon) <= 90) { + /* cry of pain can be heard somewhat farther than the waking radius */ + pline_mon(mon, "%s %s!", Monnam(mon), + (dmg > mon->mhp / 2) ? "wails in agony" + : "cries out in pain"); + } else if (canseemon(mon)) { + pline_mon(mon, "%s recoils from the light!", Monnam(mon)); + } mon->mhp -= dmg; wake_nearto(mon->mx, mon->my, 30); if (DEADMONSTER(mon)) { diff --git a/src/utf8map.c b/src/utf8map.c index f39ce26a1..87688b9ce 100644 --- a/src/utf8map.c +++ b/src/utf8map.c @@ -3,7 +3,6 @@ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" -#include #ifdef ENHANCED_SYMBOLS diff --git a/src/vault.c b/src/vault.c index eca749697..90157b7fa 100644 --- a/src/vault.c +++ b/src/vault.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 vault.c $NHDT-Date: 1657868307 2022/07/15 06:58:27 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.91 $ */ +/* NetHack 3.7 vault.c $NHDT-Date: 1737622664 2025/01/23 00:57:44 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.113 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -27,6 +27,7 @@ newegd(struct monst *mtmp) if (!EGD(mtmp)) { EGD(mtmp) = (struct egd *) alloc(sizeof (struct egd)); (void) memset((genericptr_t) EGD(mtmp), 0, sizeof (struct egd)); + EGD(mtmp)->parentmid = mtmp->m_id; } } @@ -99,8 +100,7 @@ clear_fcorr(struct monst *grd, boolean forceshow) } del_engr_at(fcx, fcy); map_location(fcx, fcy, 1); /* bypass vision */ - if (!ACCESSIBLE(lev->typ)) - block_point(fcx, fcy); + recalc_block_point(fcx, fcy); gv.vision_full_recalc = 1; egrd->fcbeg++; } @@ -109,7 +109,7 @@ clear_fcorr(struct monst *grd, boolean forceshow) /* only give encased message if hero is still alive (might get here via paygd() -> mongone() -> grddead() when game is over; died: no message, quit: message) */ - if (IS_ROCK(levl[u.ux][u.uy].typ) && (Upolyd ? u.mh : u.uhp) > 0 + if (IS_OBSTRUCTED(levl[u.ux][u.uy].typ) && (Upolyd ? u.mh : u.uhp) > 0 && !silently) You("are encased in rock."); return TRUE; @@ -320,12 +320,19 @@ invault(void) struct obj *otmp; boolean spotted; int trycount, vaultroom = (int) vault_occupied(u.urooms); + int vgdeathcount; if (!vaultroom) { u.uinvault = 0; return; } - ++u.uinvault; + /* after a couple of guards don't come back from their trips to + the vault, future guards become more reluctant to turn up (even + if summoned via whistle) */ + vgdeathcount = svm.mvitals[PM_GUARD].died; + if (vgdeathcount < 2 || + (vgdeathcount < 50 && !rn2(vgdeathcount * vgdeathcount))) + ++u.uinvault; if (u.uinvault < VAULT_GUARD_TIME || (u.uinvault % (VAULT_GUARD_TIME / 2)) != 0) return; @@ -409,6 +416,10 @@ invault(void) EGD(guard)->vroom = vaultroom; EGD(guard)->warncnt = 0; + /* ensure the guard doesn't respawn again next turn if killed + immediately */ + ++u.uinvault; + reset_faint(); /* if fainted - wake up */ /* if there are any boulders in the guard's way, destroy them; perhaps the guard knows a touch equivalent of force bolt; @@ -608,6 +619,7 @@ invault(void) EGD(guard)->fakecorr[0].ftyp = typ; EGD(guard)->fakecorr[0].flags = levl[x][y].flags; /* guard's entry point where confrontation with hero takes place */ + spot_stop_timers(x, y, MELT_ICE_AWAY); levl[x][y].typ = DOOR; levl[x][y].doormask = D_NODOOR; unblock_point(x, y); /* empty doorway doesn't block light */ @@ -730,6 +742,7 @@ gd_mv_monaway(struct monst *grd, int nx, int ny) } if (!rloc(mtmp, RLOC_ERR | RLOC_MSG) || MON_AT(nx, ny)) m_into_limbo(mtmp); + recalc_block_point(nx, ny); } } @@ -887,7 +900,7 @@ gd_move(struct monst *grd) if (!on_level(&(egrd->gdlevel), &u.uz)) return -1; - nx = ny = m = n = 0; + if (semi_dead || !grd->mx || egrd->gddone) { egrd->gddone = 1; return gd_move_cleanup(grd, semi_dead, FALSE); @@ -958,6 +971,7 @@ gd_move(struct monst *grd) mnexto(grd, RLOC_NOMSG); levl[m][n].typ = egrd->fakecorr[0].ftyp; levl[m][n].flags = egrd->fakecorr[0].flags; + recalc_block_point(m, n); del_engr_at(m, n); newsym(m, n); return -1; @@ -975,6 +989,7 @@ gd_move(struct monst *grd) (void) rloc(grd, RLOC_MSG); levl[m][n].typ = egrd->fakecorr[0].ftyp; levl[m][n].flags = egrd->fakecorr[0].flags; + recalc_block_point(m, n); /* guard corridor goes away */ del_engr_at(m, n); newsym(m, n); grd->mpeaceful = 0; @@ -1031,6 +1046,7 @@ gd_move(struct monst *grd) } } } + m = n = 0; for (fci = egrd->fcbeg; fci < egrd->fcend; fci++) if (g_at(egrd->fakecorr[fci].fx, egrd->fakecorr[fci].fy)) { m = egrd->fakecorr[fci].fx; diff --git a/src/version.c b/src/version.c index 6678b8d1e..99c36efdf 100644 --- a/src/version.c +++ b/src/version.c @@ -1,11 +1,15 @@ -/* NetHack 3.7 version.c $NHDT-Date: 1655402415 2022/06/16 18:00:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.92 $ */ +/* NetHack 3.7 version.c $NHDT-Date: 1737622664 2025/01/23 00:57:44 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.105 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2018. */ /* NetHack may be freely redistributed. See license for details. */ + #include "hack.h" #include "dlb.h" +#ifndef MINIMAL_FOR_RECOVER +#ifndef SFCTOOL + #ifndef OPTIONS_AT_RUNTIME #define OPTIONS_AT_RUNTIME #endif @@ -275,14 +279,14 @@ early_version_info(boolean pastebuf) char buf1[BUFSZ], buf2[BUFSZ]; char *buf, *tmp; - Snprintf(buf1, sizeof(buf1), "test"); + Snprintf(buf1, sizeof buf1, "test"); /* this is early enough that we have to do our own line-splitting */ getversionstring(buf1, sizeof buf1); tmp = strstri(buf1, " ("); /* split at start of version info */ if (tmp) { /* retain one buffer so that it all goes into the paste buffer */ *tmp++ = '\0'; - Snprintf(buf2, sizeof(buf2), "%s\n%s", buf1, tmp); + Snprintf(buf2, sizeof (buf2),"%s\n%s", buf1, tmp); buf = buf2; } else { buf = buf1; @@ -354,6 +358,14 @@ comp_times(long filetime) return ((unsigned long) filetime < (unsigned long) nomakedefs.build_time); } #endif +#endif /* !SFCTOOL */ + +#ifdef SFCTOOL +#ifdef wait_synch +#undef wait_synch +#endif +#define wait_synch() +#endif /* SFCTOOL */ boolean check_version( @@ -362,6 +374,18 @@ check_version( boolean complain, unsigned long utdflags) { + if (!filename) { +#ifdef EXTRA_SANITY_CHECKS + if (complain) + impossible("check_version() called with" + " 'complain'=True but 'filename'=Null"); +#endif + complain = FALSE; /* 'complain' requires 'filename' for pline("%s") */ + } + if ((version_data->feature_set & SFCTOOL_BIT) != 0) { + gc.converted_savefile_loaded = TRUE; + version_data->feature_set &= ~(SFCTOOL_BIT); + } if ( #ifdef VERSION_COMPATIBILITY /* patchlevel.h */ version_data->incarnation < VERSION_COMPATIBILITY @@ -370,116 +394,32 @@ check_version( version_data->incarnation != nomakedefs.version_number #endif ) { +#ifndef SFCTOOL if (complain) { pline("Version mismatch for file \"%s\".", filename); if (WIN_MESSAGE != WIN_ERR) display_nhwindow(WIN_MESSAGE, TRUE); } +#endif return FALSE; } else if ( (version_data->feature_set & ~nomakedefs.ignored_features) != (nomakedefs.version_features & ~nomakedefs.ignored_features) || ((utdflags & UTD_SKIP_SANITY1) == 0 && version_data->entity_count != nomakedefs.version_sanity1) - || ((utdflags & UTD_CHECKSIZES) != 0 - && version_data->struct_sizes1 != nomakedefs.version_sanity2) - || ((utdflags & UTD_CHECKSIZES) != 0 - && version_data->struct_sizes2 != nomakedefs.version_sanity3) ) { +#ifndef SFCTOOL if (complain) { pline("Configuration incompatibility for file \"%s\".", filename); display_nhwindow(WIN_MESSAGE, TRUE); } +#endif return FALSE; } return TRUE; } -/* this used to be based on file date and somewhat OS-dependent, - but now examines the initial part of the file's contents */ -boolean -uptodate(NHFILE *nhfp, const char *name, unsigned long utdflags) -{ - ssize_t rlen = 0; - int cmc = 0, filecmc = 0; - struct version_info vers_info; - boolean verbose = name ? TRUE : FALSE; - char indicator; - - if (nhfp->structlevel) { - rlen = read(nhfp->fd, (genericptr_t) &indicator, sizeof indicator); - rlen = read(nhfp->fd, (genericptr_t) &filecmc, sizeof filecmc); - if (rlen == 0) - return FALSE; - } - if (cmc != filecmc) - return FALSE; - - rlen = read(nhfp->fd, (genericptr_t) &vers_info, sizeof vers_info); - minit(); /* ZEROCOMP */ - if (rlen == 0) { - if (verbose) { - pline("File \"%s\" is empty?", name); - if ((utdflags & UTD_WITHOUT_WAITSYNCH_PERFILE) == 0) - wait_synch(); - } - return FALSE; - } - - if (!check_version(&vers_info, name, verbose, utdflags)) { - if (verbose) { - if ((utdflags & UTD_WITHOUT_WAITSYNCH_PERFILE) == 0) - wait_synch(); - } - return FALSE; - } - return TRUE; -} - -void -store_formatindicator(NHFILE *nhfp) -{ - char indicate = 'u'; - int cmc = 0; - - if (nhfp->mode & WRITING) { - if (nhfp->structlevel) { - indicate = 'h'; /* historical */ - bwrite(nhfp->fd, (genericptr_t) &indicate, sizeof indicate); - bwrite(nhfp->fd, (genericptr_t) &cmc, sizeof cmc); - } - } -} - -void -store_version(NHFILE *nhfp) -{ - struct version_info version_data = { - 0UL, 0UL, 0UL, 0UL, 0UL - }; - - /* actual version number */ - version_data.incarnation = nomakedefs.version_number; - /* bitmask of config settings */ - version_data.feature_set = nomakedefs.version_features; - /* # of monsters and objects */ - version_data.entity_count = nomakedefs.version_sanity1; - /* size of key structs */ - version_data.struct_sizes1 = nomakedefs.version_sanity2; - /* size of more key structs */ - version_data.struct_sizes2 = nomakedefs.version_sanity3; - - if (nhfp->structlevel) { - bufoff(nhfp->fd); - /* bwrite() before bufon() uses plain write() */ - store_formatindicator(nhfp); - bwrite(nhfp->fd, (genericptr_t) &version_data, - (unsigned) (sizeof version_data)); - bufon(nhfp->fd); - } - return; -} - +#ifndef SFCTOOL #ifdef AMIGA const char amiga_version_string[] = AMIGA_VERSION_STRING; #endif @@ -556,16 +496,361 @@ dump_version_info(void) if (strlen(hname) > 33) hname = eos(nhStr(hname)) - 33; /* discard const for eos() */ runtime_info_init(); - Snprintf(buf, sizeof buf, "%-12.33s %08lx %08lx %08lx %08lx %08lx", + Snprintf(buf, sizeof buf, "%-12.33s %08lx %08lx %08lx", hname, nomakedefs.version_number, (nomakedefs.version_features & ~nomakedefs.ignored_features), - nomakedefs.version_sanity1, - nomakedefs.version_sanity2, - nomakedefs.version_sanity3); + nomakedefs.version_sanity1); raw_print(buf); release_runtime_info(); return; } +void +store_version(NHFILE *nhfp) +{ + struct version_info version_data = { + 0UL, + 0UL, + 0UL, + }; + /* actual version number */ + version_data.incarnation = nomakedefs.version_number; + /* bitmask of config settings */ + version_data.feature_set = nomakedefs.version_features; + /* # of monsters and objects */ + version_data.entity_count = nomakedefs.version_sanity1; + + /* bwrite() before bufon() uses plain write() */ + if (nhfp->structlevel) + bufoff(nhfp->fd); + + store_critical_bytes(nhfp); + Sfo_version_info(nhfp, (struct version_info *) &version_data, + "version_info"); + + if (nhfp->structlevel) + bufon(nhfp->fd); + return; +} +#endif /* !SFCTOOL */ +#endif /* MINIMAL_FOR_RECOVER */ + +struct critical_sizes_with_names { + uchar ucsize; + const char *nm; +}; + +struct critical_sizes_with_names critical_sizes[] = { + { 0, "unused" }, + /* simple types, that don't have subfields */ + { (uchar) sizeof(short), "short" }, + { (uchar) sizeof(int), "int" }, + { (uchar) sizeof(long), "long" }, + { (uchar) sizeof(long long), "long long" }, + { (uchar) sizeof(genericptr_t), "genericptr_t" }, + { (uchar) sizeof(aligntyp), "aligntyp" }, + { (uchar) sizeof(boolean), "boolean" }, + { (uchar) sizeof(coordxy), "coordxy" }, + { (uchar) sizeof(int16), "int16" }, + { (uchar) sizeof(int32), "int32" }, + { (uchar) sizeof(int64), "int64" }, + { (uchar) sizeof(schar), "schar" }, + { (uchar) sizeof(size_t), "size_t" }, + { (uchar) sizeof(uchar), "uchar" }, + { (uchar) sizeof(uint16), "uint16" }, + { (uchar) sizeof(uint32), "uint32" }, + { (uchar) sizeof(uint64), "uint64" }, + { (uchar) sizeof(ulong), "ulong" }, + { (uchar) sizeof(unsigned), "unsigned" }, + { (uchar) sizeof(ushort), "ushort" }, + { (uchar) sizeof(xint16), "xint16" }, + { (uchar) sizeof(xint8), "xint8" }, + /* complex - they break down into one or more simple types */ + { (uchar) sizeof(struct arti_info), "struct arti_info" }, + { (uchar) sizeof(struct nhrect), "struct nhrect" }, + { (uchar) sizeof(struct branch), "struct branch" }, + { (uchar) sizeof(struct bubble), "struct bubble" }, + { (uchar) sizeof(struct cemetery), "struct cemetery" }, + { (uchar) sizeof(struct context_info), "struct context_info" }, + { (uchar) sizeof(struct nhcoord), "struct nhcoord" }, + { (uchar) sizeof(struct damage), "struct damage" }, + { (uchar) sizeof(struct dest_area), "struct dest_area" }, + { (uchar) sizeof(struct dgn_topology), "struct dgn_topology" }, + { (uchar) sizeof(struct dungeon), "struct dungeon" }, + { (uchar) sizeof(struct d_level), "struct d_level" }, + { (uchar) sizeof(struct ebones), "struct ebones" }, + { (uchar) sizeof(struct edog), "struct edog" }, + { (uchar) sizeof(struct egd), "struct egd" }, + { (uchar) sizeof(struct emin), "struct emin" }, + { (uchar) sizeof(struct engr), "struct engr" }, + { (uchar) sizeof(struct epri), "struct epri" }, + { (uchar) sizeof(struct eshk), "struct eshk" }, + { (uchar) sizeof(struct fe), "struct fe" }, + { (uchar) sizeof(struct flag), "struct flag" }, + { (uchar) sizeof(struct fruit), "struct fruit" }, + { (uchar) sizeof(struct gamelog_line), "struct gamelog_line" }, + { (uchar) sizeof(struct kinfo), "struct kinfo" }, + { (uchar) sizeof(struct levelflags), "struct levelflags" }, + { (uchar) sizeof(struct ls_t), "struct ls_t" }, + { (uchar) sizeof(struct linfo), "struct linfo" }, + { (uchar) sizeof(struct mapseen_feat), "struct mapseen_feat" }, + { (uchar) sizeof(struct mapseen_flags), "struct mapseen_flags" }, + { (uchar) sizeof(struct mapseen_rooms), "struct mapseen_rooms" }, + { (uchar) sizeof(struct mextra), "struct mextra" }, + { (uchar) sizeof(struct mkroom), "struct mkroom" }, + { (uchar) sizeof(struct monst), "struct monst" }, + { (uchar) sizeof(struct mvitals), "struct mvitals" }, + { (uchar) sizeof(struct obj), "struct obj" }, + { (uchar) sizeof(struct objclass), "struct objclass" }, + { (uchar) sizeof(struct oextra), "struct oextra" }, + { (uchar) sizeof(struct q_score), "struct q_score" }, + { (uchar) sizeof(struct rm), "struct rm" }, + { (uchar) sizeof(struct spell), "struct spell" }, + { (uchar) sizeof(struct stairway), "struct stairway" }, + { (uchar) sizeof(struct s_level), "struct s_level" }, + { (uchar) sizeof(struct trap), "struct trap" }, + { (uchar) sizeof(struct version_info), "struct version_info" }, + { (uchar) sizeof(anything), "anything" }, + /* struct you requires 2 bytes */ + { (uchar) ((sizeof(struct you) & 0x00FF)), "you_LO" }, + { (uchar) ((sizeof(struct you) & 0xFF00) >> 8), "you_HI" }, +#ifdef SF_INCLUDE_SUBSTRUCTS + /* + * the ones below are substructures of the ones + * above, so there is no need to check these directly. + */ + { (uchar) sizeof(struct attribs), "struct attribs" }, + { (uchar) sizeof(struct dig_info), "struct dig_info" }, + { (uchar) sizeof(struct tin_info), "struct tin_info" }, + { (uchar) sizeof(struct book_info), "struct book_info" }, + { (uchar) sizeof(struct takeoff_info), "struct takeoff_info" }, + { (uchar) sizeof(struct victual_info), "struct victual_info" }, + { (uchar) sizeof(struct engrave_info), "struct engrave_info" }, + { (uchar) sizeof(struct warntype_info), "struct warntype_info" }, + { (uchar) sizeof(struct polearm_info), "struct polearm_info" }, + { (uchar) sizeof(struct obj_split), "struct obj_split" }, + { (uchar) sizeof(struct tribute_info), "struct tribute_info" }, + { (uchar) sizeof(struct novel_tracking), "struct novel_tracking" }, + { (uchar) sizeof(struct achievement_tracking), + "struct achievement_tracking" }, + { (uchar) sizeof(struct d_flags), "struct d_flags" }, + { (uchar) sizeof(struct mapseen), "struct mapseen" }, + { (uchar) sizeof(struct fakecorridor), "struct fakecorridor" }, + { (uchar) sizeof(struct bill_x), "struct bill_x" }, + { (uchar) sizeof(union vptrs), "union vptrs" }, + { (uchar) sizeof(struct prop), "struct prop" }, + { (uchar) sizeof(struct skills), "struct skills" }, + { (uchar) sizeof(union vlaunchinfo), "union vlaunchinfo" }, + { (uchar) sizeof(struct u_have), "struct u_have" }, + { (uchar) sizeof(struct u_event), "struct u_event" }, + { (uchar) sizeof(struct u_realtime), "struct u_realtime" }, + { (uchar) sizeof(struct u_conduct), "struct u_conduct" }, + { (uchar) sizeof(struct u_roleplay), "struct u_roleplay" }, +#endif /* SF_INCLUDE_SUBSTRUCTS */ + /* 10 for future expansion without changing array size */ + { 0, "" }, + { 0, "" }, + { 0, "" }, + { 0, "" }, + { 0, "" }, + { 0, "" }, + { 0, "" }, + { 0, "" }, + { 0, "" }, + { 0, "" }, +}; + +uchar cscbuf[SIZE(critical_sizes)]; + +int +get_critical_size_count(void) +{ + return SIZE(critical_sizes); +} + +#ifndef MINIMAL_FOR_RECOVER +void +store_critical_bytes(NHFILE *nhfp) +{ + int i, cnt; + char indicate = 'u', csc_count = (char) SIZE(critical_sizes); + /* int cmc = 0; */ + + if (nhfp->mode & WRITING) { + indicate = (nhfp->structlevel) ? 'h' + : (nhfp->fnidx == exportascii) + ? 'a' + : '?'; + Sfo_char(nhfp, &indicate, "indicate-format", 1); + Sfo_char(nhfp, &csc_count, "count-critical_sizes", 1); + cnt = (int) csc_count; + for (i = 0; i < cnt; ++i) { + Sfo_uchar(nhfp, &critical_sizes[i].ucsize, "critical_sizes"); + } + } +} + +/* this used to be based on file date and somewhat OS-dependent, + * but now examines the initial part of the file's contents. + * + * returns: + * + * SF_UPTODATE (0) everything matched and looks good + * SF_OUTDATED (1) savefile is outdated + * SF_CRITICAL_BYTE_COUNT_MISMATCH (2) critical size count mismatch + * SF_DM_IL32LLP64_ON_ILP32LL64 (3) Windows x64 savefile on x86 + * SF_DM_I32LP64_ON_ILP32LL64 (4) Unix 64 savefile on x86 + * SF_DM_ILP32LL64_ON_I32LP64 (5) x86 savefile on Unix 64 + * SF_DM_ILP32LL64_ON_IL32LLP64 (6) x86 savefile on Windows x64 + * SF_DM_I32LP64_ON_IL32LLP64 (7) Unix 64 savefile on Windows x64 + * SF_DM_IL32LLP64_ON_I32LP64 (8) Windows x64 savefile on Unix 64 + * SF_DM_MISMATCH (9) some other mismatch + */ +int +uptodate(NHFILE *nhfp, const char *name, unsigned long utdflags) +{ +#ifdef SFCTOOL + extern struct version_info vers_info; +#else + struct version_info vers_info; +#endif + char indicator; + int sfstatus = 0, idx_1st_mismatch = 0; + boolean quietly = (utdflags & UTD_QUIETLY) != 0; + boolean verbose = name ? TRUE : FALSE; + + Sfi_char(nhfp, &indicator, "indicate-format", 1); + if ((sfstatus = compare_critical_bytes(nhfp, &idx_1st_mismatch, utdflags)) + != SF_UPTODATE) { + if (sfstatus > 0 && idx_1st_mismatch) { + if (!quietly) + raw_printf("comparison of critical bytes mismatched at %d (%s).", + critical_sizes[idx_1st_mismatch].ucsize, + critical_sizes[idx_1st_mismatch].nm); + } + } + + Sfi_version_info(nhfp, &vers_info, "version_info"); + if (!check_version(&vers_info, name, verbose, utdflags)) { + if (verbose) { + if ((utdflags & UTD_WITHOUT_WAITSYNCH_PERFILE) == 0) { + wait_synch(); + } + } + return SF_OUTDATED; + } + return sfstatus; +} + +/* + * returns: + * + * SF_UPTODATE (0) everything matched and looks good + * SF_OUTDATED (1) savefile is outdated + * SF_CRITICAL_BYTE_COUNT_MISMATCH (2) critical size count mismatch + * SF_DM_IL32LLP64_ON_ILP32LL64 (3) Windows x64 savefile on x86 + * SF_DM_I32LP64_ON_ILP32LL64 (4) Unix 64 savefile on x86 + * SF_DM_ILP32LL64_ON_I32LP64 (5) x86 savefile on Unix 64 + * SF_DM_ILP32LL64_ON_IL32LLP64 (6) x86 savefile on Windows x64 + * SF_DM_I32LP64_ON_IL32LLP64 (7) Unix 64 savefile on Windows x64 + * SF_DM_IL32LLP64_ON_I32LP64 (8) Windows x64 savefile on Unix 64 + * SF_DM_MISMATCH (9) some other mismatch + */ +int +compare_critical_bytes(NHFILE *nhfp, int *idx_1st_mismatch, unsigned long utdflags) +{ + char active_csc_count = (char) SIZE(critical_sizes), + file_csc_count; + int i, cnt = (int) active_csc_count, + dmmismatch = SF_DM_MISMATCH; + boolean quietly = (utdflags & UTD_QUIETLY) != 0; + + Sfi_char(nhfp, &file_csc_count, "count-critical_sizes", 1); + if (file_csc_count > cnt) { + if (!quietly) + raw_printf("critical byte counts do not match" + ", file:%d, critical_sizes:%d.", + file_csc_count, SIZE(critical_sizes)); + return SF_CRITICAL_BYTE_COUNT_MISMATCH; + } + for (i = 0; i < (int) file_csc_count; ++i) { + Sfi_uchar(nhfp, &cscbuf[i], "critical_sizes"); + } + for (i = 1; i < cnt; ++i) { + if (cscbuf[i] != critical_sizes[i].ucsize) { + const char *dm = datamodel(0), *dmfile; + + dmfile = what_datamodel_is_this(0, + cscbuf[1], /* short */ + cscbuf[2], /* int */ + cscbuf[3], /* long */ + cscbuf[4], /* long long */ + cscbuf[5]); /* ptr */ + + if (!strcmp(dmfile, "IL32LLP64") && !strcmp(dm, "ILP32LL64")) { + /* Windows x64 savefile on x86 */ + dmmismatch = SF_DM_IL32LLP64_ON_ILP32LL64; + } else if (!strcmp(dmfile, "I32LP64") + && !strcmp(dm, "ILP32LL64")) { + /* Unix 64 savefile on x86*/ + dmmismatch = SF_DM_I32LP64_ON_ILP32LL64; + } else if (!strcmp(dmfile, "ILP32LL64") + && !strcmp(dm, "I32LP64")) { + /* x86 savefile on Unix 64 */ + dmmismatch = SF_DM_ILP32LL64_ON_I32LP64; + } else if (!strcmp(dmfile, "ILP32LL64") + && !strcmp(dm, "IL32LLP64")) { + /* x86 savefile on Windows x64 */ + dmmismatch = SF_DM_ILP32LL64_ON_IL32LLP64; + } else if (!strcmp(dmfile, "I32LP64") + && !strcmp(dm, "IL32LLP64")) { + /* Unix 64 savefile on Windows x64 */ + dmmismatch = SF_DM_I32LP64_ON_IL32LLP64; + } else if (!strcmp(dmfile, "IL32LLP64") + && !strcmp(dm, "I32LP64")) { + /* Windows x64 savefile on Unix 64 */ + dmmismatch = SF_DM_IL32LLP64_ON_I32LP64; + } + if (idx_1st_mismatch) + *idx_1st_mismatch = i; + return dmmismatch; + } + } + return SF_UPTODATE; /* everything matched */ +} + +/* + * returns: + * + * SF_UPTODATE (0) everything matched and looks good + * SF_OUTDATED (1) savefile is outdated + * SF_CRITICAL_BYTE_COUNT_MISMATCH (2) critical size count mismatch + * SF_DM_IL32LLP64_ON_ILP32LL64 (3) Windows x64 savefile on x86 + * SF_DM_I32LP64_ON_ILP32LL64 (4) Unix 64 savefile on x86 + * SF_DM_ILP32LL64_ON_I32LP64 (5) x86 savefile on Unix 64 + * SF_DM_ILP32LL64_ON_IL32LLP64 (6) x86 savefile on Windows x64 + * SF_DM_I32LP64_ON_IL32LLP64 (7) Unix 64 savefile on Windows x64 + * SF_DM_IL32LLP64_ON_I32LP64 (8) Windows x64 savefile on Unix 64 + * SF_DM_MISMATCH (9) some other mismatch + */ +int +validate(NHFILE *nhfp, const char *name, boolean without_waitsynch_perfile) +{ + unsigned long utdflags = 0L; + int validsf = 0; + +#ifdef SFCTOOL + utdflags |= UTD_QUIETLY; +#endif + if (nhfp->structlevel) + utdflags |= UTD_CHECKSIZES; + if (without_waitsynch_perfile) + utdflags |= UTD_WITHOUT_WAITSYNCH_PERFILE; + if (nhfp->fieldlevel) + utdflags |= UTD_CHECKFIELDCOUNTS | UTD_SKIP_SANITY1; + validsf = uptodate(nhfp, name, utdflags); + return validsf; +} +#endif /* MINIMAL_FOR_RECOVER */ /*version.c*/ diff --git a/src/vision.c b/src/vision.c index d94c56bf4..3f33d726c 100644 --- a/src/vision.c +++ b/src/vision.c @@ -100,6 +100,15 @@ staticfn void rogue_vision(seenV **, coordxy *, coordxy *); #define sign(z) ((z) < 0 ? -1 : ((z) ? 1 : 0)) #define v_abs(z) ((z) < 0 ? -(z) : (z)) /* don't use abs -- it may exist */ +/* expose viz_clear[][] for sanity checking */ +boolean +get_viz_clear(int x, int y) +{ + if (isok(x,y) && !viz_clear[y][x]) + return TRUE; + return FALSE; +} + /* * vision_init() * @@ -154,7 +163,7 @@ does_block(int x, int y, struct rm *lev) #endif /* Features that block . . */ - if (IS_ROCK(lev->typ) || lev->typ == TREE + if (IS_OBSTRUCTED(lev->typ) || lev->typ == TREE || (IS_DOOR(lev->typ) && (lev->doormask & (D_CLOSED | D_LOCKED | D_TRAPPED)))) return 1; @@ -221,7 +230,7 @@ vision_reset(void) block = TRUE; /* location (0,y) is always stone; it's !isok() */ lev = &levl[1][y]; for (x = 1; x < COLNO; x++, lev += ROWNO) - if (block != (IS_ROCK(lev->typ) || does_block(x, y, lev))) { + if (block != (IS_OBSTRUCTED(lev->typ) || does_block(x, y, lev))) { if (block) { for (i = dig_left; i < x; i++) { left_ptrs[y][i] = dig_left; @@ -887,7 +896,17 @@ unblock_point(int x, int y) gv.vision_full_recalc = 1; } -/*==========================================================================*\ +/* recalc if point should be blocked or unblocked */ +void +recalc_block_point(coordxy x, coordxy y) +{ + if (does_block(x, y, &levl[x][y])) + block_point(x, y); + else + unblock_point(x, y); +} + +/*==========================================================================* \ : : : Everything below this line uses (y,x) instead of (x,y) --- the : : algorithms are faster if they are less recursive and can scan : diff --git a/src/weapon.c b/src/weapon.c index d65c64945..59fa7c30d 100644 --- a/src/weapon.c +++ b/src/weapon.c @@ -266,6 +266,7 @@ dmgval(struct obj *otmp, struct monst *mon) case IRON_CHAIN: case CROSSBOW_BOLT: case MACE: + case SILVER_MACE: case WAR_HAMMER: case FLAIL: case SPETUM: @@ -310,7 +311,7 @@ dmgval(struct obj *otmp, struct monst *mon) int wt = (int) objects[HEAVY_IRON_BALL].oc_weight; if ((int) otmp->owt > wt) { - wt = ((int) otmp->owt - wt) / IRON_BALL_W_INCR; + wt = ((int) otmp->owt - wt) / WT_IRON_BALL_INCR; tmp += rnd(4 * wt); if (tmp > 25) tmp = 25; /* objects[].oc_wldam */ @@ -493,21 +494,39 @@ oselect(struct monst *mtmp, int type) return (struct obj *) 0; } -/* TODO: have monsters use aklys' throw-and-return */ static NEARDATA const int rwep[] = { DWARVISH_SPEAR, SILVER_SPEAR, ELVEN_SPEAR, SPEAR, ORCISH_SPEAR, JAVELIN, SHURIKEN, YA, SILVER_ARROW, ELVEN_ARROW, ARROW, ORCISH_ARROW, CROSSBOW_BOLT, SILVER_DAGGER, ELVEN_DAGGER, DAGGER, ORCISH_DAGGER, KNIFE, - FLINT, ROCK, LOADSTONE, LUCKSTONE, DART, - /* BOOMERANG, */ CREAM_PIE + FLINT, ROCK, LOADSTONE, LUCKSTONE, DART, CREAM_PIE, }; +/* polearms */ static NEARDATA const int pwep[] = { HALBERD, BARDICHE, SPETUM, BILL_GUISARME, VOULGE, RANSEUR, GUISARME, GLAIVE, LUCERN_HAMMER, BEC_DE_CORBIN, FAUCHARD, PARTISAN, LANCE }; +#define AKLYS_LIM (BOLT_LIM / 2) +/* throw-and-return weapons */ +static NEARDATA const struct throw_and_return_weapon arwep[] = { + /* { BOOMERANG, 5, 0 }, */ + { AKLYS, AKLYS_LIM * AKLYS_LIM, 1 }, +}; + +const struct throw_and_return_weapon * +autoreturn_weapon(struct obj *otmp) +{ + int i; + + for (i = 0; i < SIZE(arwep); i++) { + if (otmp->otyp == arwep[i].otyp) + return &arwep[i]; + } + return (struct throw_and_return_weapon *) 0; +} + /* select a ranged weapon for the monster */ struct obj * select_rwep(struct monst *mtmp) @@ -539,6 +558,11 @@ select_rwep(struct monst *mtmp) mweponly = (mwelded(mwep) && mtmp->weapon_check == NO_WEAPON_WANTED); if (dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <= 13 && couldsee(mtmp->mx, mtmp->my)) { + if (is_art(mwep, ART_SNICKERSNEE)) { + gp.propellor = mwep; + return mwep; + } + for (i = 0; i < SIZE(pwep); i++) { /* Only strong monsters can wield big (esp. long) weapons. * Big weapon is basically the same as bimanual. @@ -557,9 +581,31 @@ select_rwep(struct monst *mtmp) } } } + /* Next, try to select a throw-and-return weapon, since they are + * also not as expendable. Again, don't pick one if monster's + * weapon is welded. + */ + for (i = 0; i < SIZE(arwep); i++) { + const struct throw_and_return_weapon *arw = &arwep[i]; + + if (!mindless(mtmp->data) && !is_animal(mtmp->data) && !mweponly + && dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <= arw->range + && couldsee(mtmp->mx, mtmp->my)) { + if ((((mtmp->misc_worn_check & W_ARMS) == 0) + || !objects[arw->otyp].oc_bimanual) + && (objects[arw->otyp].oc_material != SILVER + || !mon_hates_silver(mtmp))) { + if ((otmp = oselect(mtmp, arw->otyp)) != 0 + && (otmp == mwep || !mweponly)) { + gp.propellor = otmp; /* force the monster to wield it */ + return otmp; + } + } + } + } /* - * other than these two specific cases, always select the + * other than the specific cases above, always select the * most potent ranged weapon to hand. */ for (i = 0; i < SIZE(rwep); i++) { @@ -646,8 +692,8 @@ static const NEARDATA short hwep[] = { TSURUGI, RUNESWORD, DWARVISH_MATTOCK, TWO_HANDED_SWORD, BATTLE_AXE, KATANA, UNICORN_HORN, CRYSKNIFE, TRIDENT, LONG_SWORD, ELVEN_BROADSWORD, BROADSWORD, SCIMITAR, SILVER_SABER, MORNING_STAR, ELVEN_SHORT_SWORD, - DWARVISH_SHORT_SWORD, SHORT_SWORD, ORCISH_SHORT_SWORD, MACE, AXE, - DWARVISH_SPEAR, SILVER_SPEAR, ELVEN_SPEAR, SPEAR, ORCISH_SPEAR, FLAIL, + DWARVISH_SHORT_SWORD, SHORT_SWORD, ORCISH_SHORT_SWORD, SILVER_MACE, MACE, + AXE, DWARVISH_SPEAR, SILVER_SPEAR, ELVEN_SPEAR, SPEAR, ORCISH_SPEAR, FLAIL, BULLWHIP, QUARTERSTAFF, JAVELIN, AKLYS, CLUB, PICK_AXE, RUBBER_HOSE, WAR_HAMMER, SILVER_DAGGER, ELVEN_DAGGER, DAGGER, ORCISH_DAGGER, ATHAME, SCALPEL, KNIFE, WORM_TOOTH @@ -716,7 +762,7 @@ possibly_unwield(struct monst *mon, boolean polyspot) mon->weapon_check = NO_WEAPON_WANTED; /* if we're going to call distant_name(), do so before extract_self */ if (cansee(mon->mx, mon->my)) { - pline("%s drops %s.", Monnam(mon), distant_name(obj, doname)); + pline_mon(mon, "%s drops %s.", Monnam(mon), distant_name(obj, doname)); newsym(mon->mx, mon->my); } obj_extract_self(obj); @@ -827,7 +873,7 @@ mon_wield_item(struct monst *mon) pline("%s cannot wield that %s.", mon_nam(mon), xname(obj)); } else { - pline("%s tries to wield %s.", Monnam(mon), doname(obj)); + pline_mon(mon, "%s tries to wield %s.", Monnam(mon), doname(obj)); pline("%s %s!", Yname2(mw_tmp), welded_buf); } mw_tmp->bknown = 1; @@ -840,10 +886,15 @@ mon_wield_item(struct monst *mon) mon->weapon_check = NEED_WEAPON; if (canseemon(mon)) { boolean newly_welded; + const struct throw_and_return_weapon *arw; pline_mon(mon, "%s wields %s%c", Monnam(mon), doname(obj), exclaim ? '!' : '.'); + if ((arw = autoreturn_weapon(obj)) != 0 && arw->tethered != 0) + pline_mon(mon, "%s secures the tether on %s.", Monnam(mon), + the(xname(obj))); + /* 3.6.3: mwelded() predicate expects the object to have its W_WEP bit set in owormmask, but the pline here and for artifact_light don't want that because they'd have '(weapon @@ -1466,7 +1517,9 @@ weapon_hit_bonus(struct obj *weapon) } else if (type <= P_LAST_WEAPON) { switch (P_SKILL(type)) { default: - impossible(bad_skill, P_SKILL(type)); /* fall through */ + impossible(bad_skill, P_SKILL(type)); + FALLTHROUGH; + /* FALLTHRU */ case P_ISRESTRICTED: case P_UNSKILLED: bonus = -4; @@ -1487,7 +1540,9 @@ weapon_hit_bonus(struct obj *weapon) skill = P_SKILL(wep_type); switch (skill) { default: - impossible(bad_skill, skill); /* fall through */ + impossible(bad_skill, skill); + FALLTHROUGH; + /* FALLTHRU */ case P_ISRESTRICTED: case P_UNSKILLED: bonus = -9; @@ -1561,7 +1616,8 @@ weapon_dam_bonus(struct obj *weapon) switch (P_SKILL(type)) { default: impossible("weapon_dam_bonus: bad skill %d", P_SKILL(type)); - /* fall through */ + FALLTHROUGH; + /* FALLTHRU */ case P_ISRESTRICTED: case P_UNSKILLED: bonus = -2; @@ -1730,4 +1786,19 @@ setmnotwielded(struct monst *mon, struct obj *obj) obj->owornmask &= ~W_WEP; } +#undef PN_BARE_HANDED +#undef PN_RIDING +#undef PN_POLEARMS +#undef PN_SABER +#undef PN_HAMMER +#undef PN_WHIP +#undef PN_ATTACK_SPELL +#undef PN_HEALING_SPELL +#undef PN_DIVINATION_SPELL +#undef PN_ENCHANTMENT_SPELL +#undef PN_CLERIC_SPELL +#undef PN_ESCAPE_SPELL +#undef PN_MATTER_SPELL +#undef AKLYS_LIM + /*weapon.c*/ diff --git a/src/were.c b/src/were.c index 0832555f1..b38bf0ed0 100644 --- a/src/were.c +++ b/src/were.c @@ -16,6 +16,7 @@ were_change(struct monst *mon) && !rn2(night() ? (flags.moonphase == FULL_MOON ? 3 : 30) : (flags.moonphase == FULL_MOON ? 10 : 50))) { new_were(mon); /* change into animal form */ + gw.were_changes++; if (!Deaf && !canseemon(mon)) { const char *howler; @@ -39,9 +40,8 @@ were_change(struct monst *mon) } } else if (!rn2(30) || Protection_from_shape_changers) { new_were(mon); /* change back into human form */ + gw.were_changes++; } - /* update innate intrinsics (mainly Drain_resistance) */ - set_uasmon(); /* new_were() doesn't do this */ } int @@ -123,7 +123,7 @@ new_were(struct monst *mon) mon->mcanmove = 1; } /* regenerate by 1/4 of the lost hit points */ - mon->mhp += (mon->mhpmax - mon->mhp) / 4; + healmon(mon, (mon->mhpmax - mon->mhp) / 4, 0); newsym(mon->mx, mon->my); mon_break_armor(mon, FALSE); possibly_unwield(mon, FALSE); @@ -202,6 +202,7 @@ you_were(void) if (!paranoid_query(ParanoidWerechange, qbuf)) return; } + gw.were_changes++; (void) polymon(u.ulycn); } diff --git a/src/wield.c b/src/wield.c index 9aaea38fc..53b0aa94f 100644 --- a/src/wield.c +++ b/src/wield.c @@ -123,7 +123,7 @@ setuwep(struct obj *obj) if (obj) { gu.unweapon = (obj->oclass == WEAPON_CLASS) ? is_launcher(obj) || is_ammo(obj) || is_missile(obj) - || (is_pole(obj) && !u.usteed) + || (is_pole(obj) && !u.usteed && !is_art(obj, ART_SNICKERSNEE)) : !is_weptool(obj) && !is_wet_towel(obj); } else gu.unweapon = TRUE; /* for "bare hands" message */ diff --git a/src/windows.c b/src/windows.c index d763f7eaf..b929cf62f 100644 --- a/src/windows.c +++ b/src/windows.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 windows.c $NHDT-Date: 1700012891 2023/11/15 01:48:11 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.109 $ */ +/* NetHack 3.7 windows.c $NHDT-Date: 1737345149 2025/01/19 19:52:29 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.138 $ */ /* Copyright (c) D. Cohrs, 1993. */ /* NetHack may be freely redistributed. See license for details. */ @@ -598,8 +598,6 @@ static struct window_procs hup_procs = { #endif hup_get_color_string, #endif /* CHANGE_COLOR */ - hup_void_ndecl, /* start_screen */ - hup_void_ndecl, /* end_screen */ hup_outrip, genl_preference_update, genl_getmsghistory, genl_putmsghistory, hup_void_ndecl, /* status_init */ @@ -1655,12 +1653,11 @@ choose_classes_menu(const char *prompt, char buf[BUFSZ]; const char *text = 0; boolean selected; - int ret, i, n, next_accelerator, accelerator; + int ret, i, n, next_accelerator, accelerator = 0; int clr = NO_COLOR; if (!class_list || !class_select) return 0; - accelerator = 0; next_accelerator = 'a'; any = cg.zeroany; win = create_nhwindow(NHW_MENU); diff --git a/src/wizard.c b/src/wizard.c index aefba556d..2f4b0bdb9 100644 --- a/src/wizard.c +++ b/src/wizard.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 wizard.c $NHDT-Date: 1718303204 2024/06/13 18:26:44 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.110 $ */ +/* NetHack 3.7 wizard.c $NHDT-Date: 1741407262 2025/03/07 20:14:22 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.116 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2016. */ /* NetHack may be freely redistributed. See license for details. */ @@ -135,9 +135,6 @@ mon_has_special(struct monst *mtmp) * The strategy section decides *what* the monster is going * to attempt, the tactics section implements the decision. */ -#define STRAT(w, x, y, typ) \ - ((unsigned long) (w) | ((unsigned long) (x) << 16) \ - | ((unsigned long) (y) << 8) | (unsigned long) (typ)) #define M_Wants(mask) (mtmp->data->mflags3 & (mask)) @@ -247,17 +244,25 @@ target_on(int mask, struct monst *mtmp) otyp = which_arti(mask); if (!mon_has_arti(mtmp, otyp)) { - if (you_have(mask)) - return STRAT(STRAT_PLAYER, u.ux, u.uy, mask); - else if ((otmp = on_ground(otyp))) - return STRAT(STRAT_GROUND, otmp->ox, otmp->oy, mask); - else if ((mtmp2 = other_mon_has_arti(mtmp, otyp)) != 0 + if (you_have(mask)) { + mtmp->mgoal.x = u.ux; + mtmp->mgoal.y = u.uy; + return (STRAT_PLAYER | mask); + } else if ((otmp = on_ground(otyp))) { + mtmp->mgoal.x = otmp->ox; + mtmp->mgoal.y = otmp->oy; + return (STRAT_GROUND | mask); + } else if ((mtmp2 = other_mon_has_arti(mtmp, otyp)) != 0 /* when seeking the Amulet, avoid targeting the Wizard or temple priests (to protect Moloch's high priest) */ && (otyp != AMULET_OF_YENDOR - || (!mtmp2->iswiz && !inhistemple(mtmp2)))) - return STRAT(STRAT_MONSTR, mtmp2->mx, mtmp2->my, mask); + || (!mtmp2->iswiz && !inhistemple(mtmp2)))) { + mtmp->mgoal.x = mtmp2->mx; + mtmp->mgoal.y = mtmp2->my; + return (STRAT_MONSTR | mask); + } } + mtmp->mgoal.x = mtmp->mgoal.y = 0; return (unsigned long) STRAT_NONE; } @@ -284,8 +289,8 @@ strategy(struct monst *mtmp) case 1: /* the wiz is less cautious */ if (mtmp->data != &mons[PM_WIZARD_OF_YENDOR]) return (unsigned long) STRAT_HEAL; - /* else fall through */ - + FALLTHROUGH; + /* FALLTHRU */ case 2: dstrat = STRAT_HEAL; break; @@ -396,9 +401,10 @@ tactics(struct monst *mtmp) /* if you're not around, cast healing spells */ if (distu(mx, my) > (BOLT_LIM * BOLT_LIM)) if (mtmp->mhp <= mtmp->mhpmax - 8) { - mtmp->mhp += rnd(8); + healmon(mtmp, rnd(8), 0); return 1; } + FALLTHROUGH; /*FALLTHRU*/ case STRAT_NONE: /* harass */ @@ -409,7 +415,7 @@ tactics(struct monst *mtmp) default: /* kill, maim, pillage! */ { long where = (strat & STRAT_STRATMASK); - coordxy tx = STRAT_GOALX(strat), ty = STRAT_GOALY(strat); + coordxy tx = mtmp->mgoal.x, ty = mtmp->mgoal.y; int targ = (int) (strat & STRAT_GOAL); struct obj *otmp; @@ -656,6 +662,9 @@ nasty(struct monst *summoner) bypos.x, bypos.y, mmflags)) != 0) { m_cls = mtmp->data->mlet; if ((difcap > 0 && mtmp->data->difficulty >= difcap + /* always capping for substitutes made wanton + genocide become too strong in the endgame */ + && rn2(In_endgame(&u.uz) ? 3 : 7) /* usually */ && attacktype(mtmp->data, AT_MAGC)) || (s_cls == S_DEMON && m_cls == S_ANGEL) || (s_cls == S_ANGEL && m_cls == S_DEMON)) @@ -664,10 +673,11 @@ nasty(struct monst *summoner) } if (mtmp) { - /* create at most one arch-lich or Archon regardless - of who is doing the summoning (note: Archon is - not in nasties[] but could be chosen as random - replacement for a genocided selection) */ + /* if creating an arch-lich or Archon, further directly + selected nasties will have to be less difficult, and + substitues for geno victims will usually be less + (note: Archon is not in nasties[] but could be chosen + as random replacement for a genocided selection) */ if (mtmp->data == &mons[PM_ARCH_LICH] || mtmp->data == &mons[PM_ARCHON]) { tmp = min(mons[PM_ARCHON].difficulty, /* A:26 */ diff --git a/src/wizcmds.c b/src/wizcmds.c index 88dd27aac..6dd756880 100644 --- a/src/wizcmds.c +++ b/src/wizcmds.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 wizcmds.c $NHDT-Date: 1723580901 2024/08/13 20:28:21 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.12 $ */ +/* NetHack 3.7 wizcmds.c $NHDT-Date: 1736530208 2025/01/10 09:30:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.21 $ */ /*-Copyright (c) Robert Patrick Rankin, 2024. */ /* NetHack may be freely redistributed. See license for details. */ @@ -20,6 +20,7 @@ staticfn void mon_chain(winid, const char *, struct monst *, boolean, long *, staticfn void contained_stats(winid, const char *, long *, long *); staticfn void misc_stats(winid, long *, long *); staticfn void you_sanity_check(void); +staticfn void levl_sanity_check(void); staticfn void makemap_unmakemon(struct monst *, boolean); staticfn int QSORTCALLBACK migrsort_cmp(const genericptr, const genericptr); staticfn void list_migrating_mons(d_level *); @@ -68,7 +69,7 @@ RESTORE_WARNING_FORMAT_NONLITERAL /* used when wiz_makemap() gets rid of monsters for the old incarnation of a level before creating a new incarnation of it */ -void +staticfn void makemap_unmakemon(struct monst *mtmp, boolean migratory) { int ndx = monsndx(mtmp->data); @@ -551,8 +552,14 @@ wiz_fuzzer(void) pline("The fuzz tester will make NetHack execute random keypresses."); There("is no conventional way out of this mode."); } - if (paranoid_query(TRUE, "Do you want to start fuzz testing?")) - iflags.debug_fuzzer = TRUE; /* Thoth, take the reins */ + if (paranoid_query(TRUE, "Do you want to start fuzz testing?")) { + /* Thoth, take the reins */ + if (y_n("Do you want to call panic() after impossible()?") == 'n') { + iflags.debug_fuzzer = fuzzer_impossible_continue; + } else { + iflags.debug_fuzzer = fuzzer_impossible_panic; + } + } return ECMD_OK; } @@ -1059,6 +1066,7 @@ wiz_intrinsic(void) so needs more than simple incr_itimeout() but we want the pline() issued with that */ make_glib((int) newtimeout); + FALLTHROUGH; /*FALLTHRU*/ default: def_feedback: @@ -1074,6 +1082,10 @@ wiz_intrinsic(void) float_vs_flight(); else if (p == PROT_FROM_SHAPE_CHANGERS) rescham(); + if (p == WWALKING || p == LEVITATION || p == FLYING) { + if (u.uinwater) + (void) pooleffects(FALSE); + } } if (n >= 1) free((genericptr_t) pick_list); @@ -1234,6 +1246,8 @@ size_monst(struct monst *mtmp, boolean incl_wsegs) sz += (int) sizeof (struct emin); if (EDOG(mtmp)) sz += (int) sizeof (struct edog); + if (EBONES(mtmp)) + sz += (int) sizeof (struct ebones); /* mextra->mcorpsenm doesn't point to more memory */ } return sz; @@ -1426,6 +1440,22 @@ you_sanity_check(void) (void) check_invent_gold("invent"); } +staticfn void +levl_sanity_check(void) +{ + coordxy x, y; + + if (Underwater) + return; /* Underwater uses different vision */ + + for (y = 0; y < ROWNO; y++) { + for (x = 1; x < COLNO; x++) { + if ((does_block(x, y, &levl[x][y]) ? 1 : 0) != get_viz_clear(x, y)) + impossible("levl[%i][%i] vision blocking", x, y); + } + } +} + void sanity_check(void) { @@ -1446,6 +1476,7 @@ sanity_check(void) bc_sanity_check(); trap_sanity_check(); engraving_sanity_check(); + levl_sanity_check(); program_state.in_sanity_check--; } @@ -1800,6 +1831,7 @@ wiz_migrate_mons(void) char inbuf[BUFSZ]; struct permonst *ptr; struct monst *mtmp; + boolean use_random_mon = TRUE; #endif d_level tolevel; @@ -1823,17 +1855,25 @@ wiz_migrate_mons(void) return ECMD_OK; mcount = atoi(inbuf); + if (mcount < 0) { + use_random_mon = FALSE; + mcount *= -1; + } if (mcount < 1) mcount = 0; else if (mcount > ((COLNO - 1) * ROWNO)) mcount = (COLNO - 1) * ROWNO; while (mcount > 0) { - ptr = rndmonst(); - mtmp = makemon(ptr, 0, 0, MM_NOMSG); + if (use_random_mon) { + ptr = rndmonst(); + mtmp = makemon(ptr, 0, 0, MM_NOMSG); + } else { + mtmp = fmon; + } if (mtmp) migrate_to_level(mtmp, ledger_no(&tolevel), MIGR_RANDOM, - (coord *) 0); + (coord *) 0); mcount--; } #endif /* DEBUG_MIGRATING_MONS */ @@ -1849,7 +1889,7 @@ wiz_custom(void) if (wizard) { static const char wizcustom[] = "#wizcustom"; winid win; - char buf[BUFSZ], bufa[BUFSZ]; + char buf[BUFSZ], bufa[BUFSZ]; int n; #if 0 int j, glyph; diff --git a/src/worm.c b/src/worm.c index bb95dc4d5..e2bb61c3b 100644 --- a/src/worm.c +++ b/src/worm.c @@ -14,12 +14,15 @@ struct wseg { coordxy wx, wy; /* the segment's position */ }; +#ifndef SFCTOOL staticfn void toss_wsegs(struct wseg *, boolean) NO_NNARGS; staticfn void shrink_worm(int); + #if 0 staticfn void random_dir(int, int, int *, int *); #endif staticfn struct wseg *create_worm_tail(int); /* may return NULL */ +#endif /* !SFCTOOL */ /* Description of long worm implementation. * @@ -75,6 +78,7 @@ static struct wseg *wheads[MAX_NUM_WORMS] = DUMMY, *wtails[MAX_NUM_WORMS] = DUMMY; static long wgrowtime[MAX_NUM_WORMS] = DUMMY; +#ifndef SFCTOOL /* * get_wormno() * @@ -527,28 +531,22 @@ save_worm(NHFILE *nhfp) int count; struct wseg *curr, *temp; - if (perform_bwrite(nhfp)) { + if (update_file(nhfp)) { for (i = 1; i < MAX_NUM_WORMS; i++) { for (count = 0, curr = wtails[i]; curr; curr = curr->nseg) count++; /* Save number of segments */ - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &count, sizeof count); + Sfo_int(nhfp, &count, "worm-segment_count"); /* Save segment locations of the monster. */ if (count) { for (curr = wtails[i]; curr; curr = curr->nseg) { - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &(curr->wx), - sizeof curr->wx); - bwrite(nhfp->fd, (genericptr_t) &(curr->wy), - sizeof curr->wy); - } + Sfo_coordxy(nhfp, &(curr->wx), "worm-wx"); + Sfo_coordxy(nhfp, &(curr->wy), "worm-wy"); } } } - if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) wgrowtime, sizeof wgrowtime); - } + for (i = 0; i < MAX_NUM_WORMS; ++i) + Sfo_long(nhfp, &wgrowtime[i], "worm-wgrowtime"); } if (release_data(nhfp)) { @@ -568,6 +566,7 @@ save_worm(NHFILE *nhfp) } } } +#endif /* !SFCTOOL */ /* * rest_worm() @@ -577,21 +576,19 @@ save_worm(NHFILE *nhfp) void rest_worm(NHFILE *nhfp) { - int i, j, count = 0; + int i, j; + int count = 0; struct wseg *curr, *temp; for (i = 1; i < MAX_NUM_WORMS; i++) { - if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) &count, sizeof count); + Sfi_int(nhfp, &count, "worm-segment_count"); /* Get the segments. */ for (curr = (struct wseg *) 0, j = 0; j < count; j++) { temp = newseg(); temp->nseg = (struct wseg *) 0; - if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) &(temp->wx), sizeof temp->wx); - mread(nhfp->fd, (genericptr_t) &(temp->wy), sizeof temp->wy); - } + Sfi_coordxy(nhfp, &(temp->wx), "worm-wx"); + Sfi_coordxy(nhfp, &(temp->wy), "worm-wy"); if (curr) curr->nseg = temp; else @@ -600,11 +597,12 @@ rest_worm(NHFILE *nhfp) } wheads[i] = curr; } - if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) wgrowtime, sizeof wgrowtime); + for (i = 0; i < MAX_NUM_WORMS; ++i) { + Sfi_long(nhfp, &wgrowtime[i], "worm-wgrowtime"); } } +#ifndef SFCTOOL /* * place_wsegs() * @@ -988,4 +986,16 @@ flip_worm_segs_horizontal(struct monst *worm, int minx, int maxx) } } +void +redraw_worm(struct monst *worm) +{ + struct wseg *curr = wtails[worm->wormno]; + + while (curr) { + newsym(curr->wx, curr->wy); + curr = curr->nseg; + } +} +#endif /* !SFCTOOL */ + /*worm.c*/ diff --git a/src/worn.c b/src/worn.c index 1f35776d4..b7db2d94a 100644 --- a/src/worn.c +++ b/src/worn.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 worn.c $NHDT-Date: 1715109581 2024/05/07 19:19:41 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.109 $ */ +/* NetHack 3.7 worn.c $NHDT-Date: 1736530208 2025/01/10 09:30:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.116 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -162,7 +162,7 @@ setnotworn(struct obj *obj) *(wp->w_obj) = (struct obj *) 0; p = objects[obj->otyp].oc_oprop; u.uprops[p].extrinsic = u.uprops[p].extrinsic & ~wp->w_mask; - monstunseesu_prop(p); /* remove this extrinsic from seenres */ + monstunseesu_prop(p); /* remove this extrinsic from seenres */ obj->owornmask &= ~wp->w_mask; if (obj->oartifact) set_artifact_intrinsic(obj, 0, wp->w_mask); @@ -475,11 +475,13 @@ mon_adjust_speed( /* mimic the player's petrification countdown; "slowing down" even if fast movement rate retained via worn speed boots */ if (flags.verbose) - pline("%s is slowing down.", Monnam(mon)); + pline_mon(mon, "%s is slowing down.", Monnam(mon)); } else if (adjust > 0 || mon->mspeed == MFAST) - pline("%s is suddenly moving %sfaster.", Monnam(mon), howmuch); + pline_mon(mon, "%s is suddenly moving %sfaster.", + Monnam(mon), howmuch); else - pline("%s seems to be moving %sslower.", Monnam(mon), howmuch); + pline_mon(mon, "%s seems to be moving %sslower.", + Monnam(mon), howmuch); /* might discover an object if we see the speed change happen */ if (obj != 0) @@ -723,7 +725,7 @@ staticfn void m_dowear_type( struct monst *mon, long flag, /* wornmask value */ - boolean creation, + boolean creation, /* no wear messages when mon is being created */ boolean racialexception) /* small monsters that are allowed for player * races (gnomes) can wear suits */ { @@ -847,14 +849,31 @@ m_dowear_type( if (!creation) { if (sawmon) { - char buf[BUFSZ]; + char buf[BUFSZ], oldarm[BUFSZ], newarm[BUFSZ + sizeof "another "]; - if (old) - Sprintf(buf, " removes %s and", distant_name(old, doname)); - else - buf[0] = '\0'; - pline("%s%s puts on %s.", Monnam(mon), buf, - distant_name(best, doname)); + /* " [removes and ]puts on ." + uses accessory verbs for armor but we can live with that */ + if (old) { + Strcpy(oldarm, distant_name(old, doname)); + Snprintf(buf, sizeof buf, " removes %s and", oldarm); + } else { + buf[0] = oldarm[0] = '\0'; + } + Strcpy(newarm, distant_name(best, doname)); + /* a monster will swap an item of the same type as the one it + is replacing when the enchantment is better; + if newarm and oldarm have identical descriptions, substitute + "another " for "a|an " */ + if (!strcmpi(newarm, oldarm)) { + /* size of newarm[] has been overallocated to guarantee + enough room to insert "another " */ + if (!strncmpi(newarm, "a ", 2)) + (void) strsubst(newarm, "a ", "another "); + else if (!strncmpi(newarm, "an ", 3)) + (void) strsubst(newarm, "an ", "another "); + newarm[BUFSZ - 1] = '\0'; + } + pline_mon(mon, "%s%s puts on %s.", Monnam(mon), buf, newarm); if (autocurse) pline("%s %s %s %s for a moment.", s_suffix(Monnam(mon)), simpleonames(best), otense(best, "glow"), @@ -1100,7 +1119,8 @@ mon_break_armor(struct monst *mon, boolean polyspot) } else { Soundeffect(se_cracking_sound, 100); if (vis) - pline("%s breaks out of %s armor!", Monnam(mon), ppronoun); + pline_mon(mon, "%s breaks out of %s armor!", + Monnam(mon), ppronoun); else You_hear("a cracking sound."); } @@ -1111,13 +1131,13 @@ mon_break_armor(struct monst *mon, boolean polyspot) && (otmp->otyp != MUMMY_WRAPPING || !WrappingAllowed(mdat))) { if (otmp->oartifact) { if (vis) - pline("%s %s falls off!", s_suffix(Monnam(mon)), + pline_mon(mon, "%s %s falls off!", s_suffix(Monnam(mon)), cloak_simple_name(otmp)); m_lose_armor(mon, otmp, polyspot); } else { Soundeffect(se_ripping_sound, 100); if (vis) - pline("%s %s tears apart!", s_suffix(Monnam(mon)), + pline_mon(mon, "%s %s tears apart!", s_suffix(Monnam(mon)), cloak_simple_name(otmp)); else You_hear("a ripping sound."); @@ -1126,7 +1146,8 @@ mon_break_armor(struct monst *mon, boolean polyspot) } if ((otmp = which_armor(mon, W_ARMU)) != 0) { if (vis) - pline("%s shirt rips to shreds!", s_suffix(Monnam(mon))); + pline_mon(mon, "%s shirt rips to shreds!", + s_suffix(Monnam(mon))); else You_hear("a ripping sound."); m_useup(mon, otmp); @@ -1138,8 +1159,8 @@ mon_break_armor(struct monst *mon, boolean polyspot) if ((otmp = which_armor(mon, W_ARM)) != 0) { Soundeffect(se_thud, 50); if (vis) - pline("%s armor falls around %s!", s_suffix(Monnam(mon)), - pronoun); + pline_mon(mon, "%s armor falls around %s!", + s_suffix(Monnam(mon)), pronoun); else You_hear("a thud."); m_lose_armor(mon, otmp, polyspot); @@ -1149,21 +1170,22 @@ mon_break_armor(struct monst *mon, boolean polyspot) && (otmp->otyp != MUMMY_WRAPPING || !WrappingAllowed(mdat))) { if (vis) { if (is_whirly(mon->data)) - pline("%s %s falls, unsupported!", s_suffix(Monnam(mon)), - cloak_simple_name(otmp)); + pline_mon(mon, "%s %s falls, unsupported!", + s_suffix(Monnam(mon)), cloak_simple_name(otmp)); else - pline("%s shrinks out of %s %s!", Monnam(mon), ppronoun, - cloak_simple_name(otmp)); + pline_mon(mon, "%s shrinks out of %s %s!", + Monnam(mon), ppronoun, + cloak_simple_name(otmp)); } m_lose_armor(mon, otmp, polyspot); } if ((otmp = which_armor(mon, W_ARMU)) != 0) { if (vis) { if (passes_thru_clothes) - pline("%s seeps right through %s shirt!", Monnam(mon), - ppronoun); + pline_mon(mon, "%s seeps right through %s shirt!", + Monnam(mon), ppronoun); else - pline("%s becomes much too small for %s shirt!", + pline_mon(mon, "%s becomes much too small for %s shirt!", Monnam(mon), ppronoun); } m_lose_armor(mon, otmp, polyspot); @@ -1173,15 +1195,16 @@ mon_break_armor(struct monst *mon, boolean polyspot) /* [caller needs to handle weapon checks] */ if ((otmp = which_armor(mon, W_ARMG)) != 0) { if (vis) - pline("%s drops %s gloves%s!", Monnam(mon), ppronoun, - MON_WEP(mon) ? " and weapon" : ""); + pline_mon(mon, "%s drops %s gloves%s!", + Monnam(mon), ppronoun, + MON_WEP(mon) ? " and weapon" : ""); m_lose_armor(mon, otmp, polyspot); } if ((otmp = which_armor(mon, W_ARMS)) != 0) { Soundeffect(se_clank, 50); if (vis) - pline("%s can no longer hold %s shield!", Monnam(mon), - ppronoun); + pline_mon(mon, "%s can no longer hold %s shield!", + Monnam(mon), ppronoun); else You_hear("a clank."); m_lose_armor(mon, otmp, polyspot); @@ -1192,8 +1215,8 @@ mon_break_armor(struct monst *mon, boolean polyspot) /* flimsy test for horns matches polyself handling */ && (handless_or_tiny || !is_flimsy(otmp))) { if (vis) - pline("%s helmet falls to the %s!", s_suffix(Monnam(mon)), - surface(mon->mx, mon->my)); + pline_mon(mon, "%s helmet falls to the %s!", + s_suffix(Monnam(mon)), surface(mon->mx, mon->my)); else You_hear("a clank."); m_lose_armor(mon, otmp, polyspot); @@ -1203,9 +1226,11 @@ mon_break_armor(struct monst *mon, boolean polyspot) if ((otmp = which_armor(mon, W_ARMF)) != 0) { if (vis) { if (is_whirly(mon->data)) - pline("%s boots fall away!", s_suffix(Monnam(mon))); + pline_mon(mon, "%s boots fall away!", + s_suffix(Monnam(mon))); else - pline("%s boots %s off %s feet!", s_suffix(Monnam(mon)), + pline_mon(mon, "%s boots %s off %s feet!", + s_suffix(Monnam(mon)), verysmall(mdat) ? "slide" : "are pushed", ppronoun); } m_lose_armor(mon, otmp, polyspot); @@ -1215,7 +1240,7 @@ mon_break_armor(struct monst *mon, boolean polyspot) if ((otmp = which_armor(mon, W_SADDLE)) != 0) { m_lose_armor(mon, otmp, polyspot); if (vis) - pline("%s saddle falls off.", s_suffix(Monnam(mon))); + pline_mon(mon, "%s saddle falls off.", s_suffix(Monnam(mon))); } if (mon == u.usteed) noride = TRUE; diff --git a/src/zap.c b/src/zap.c index fa3795560..a1a5c3380 100644 --- a/src/zap.c +++ b/src/zap.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 zap.c $NHDT-Date: 1723946858 2024/08/18 02:07:38 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.542 $ */ +/* NetHack 3.7 zap.c $NHDT-Date: 1741793439 2025/03/12 07:30:39 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.564 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -148,7 +148,11 @@ learnwand(struct obj *obj) } } -/* Routines for IMMEDIATE wands and spells. */ +/* + * Routines for IMMEDIATE wands and spells. + * Also RAY or NODIR for wands that are being broken rather than zapped. + */ + /* bhitm: monster mtmp was hit by the effect of wand or spell otmp */ int bhitm(struct monst *mtmp, struct obj *otmp) @@ -163,6 +167,15 @@ bhitm(struct monst *mtmp, struct obj *otmp) struct obj *obj; boolean disguised_mimic = (mtmp->data->mlet == S_MIMIC && M_AP_TYPE(mtmp) != M_AP_NOTHING); + /* box_or_door(): mimic appearances that have locks */ +#define box_or_door(monst) \ + ((M_AP_TYPE(monst) == M_AP_OBJECT \ + && ((monst)->mappearance == CHEST \ + || (monst)->mappearance == LARGE_BOX)) \ + || (M_AP_TYPE(monst) == M_AP_FURNITURE \ + /* is_cmap_door() tests S_symbol values, and */ \ + /* mon->mappearance for furniture contains one of those */ \ + && is_cmap_door((monst)->mappearance))) if (engulfing_u(mtmp)) reveal_invis = FALSE; @@ -173,6 +186,7 @@ bhitm(struct monst *mtmp, struct obj *otmp) switch (otyp) { case WAN_STRIKING: zap_type_text = "wand"; + FALLTHROUGH; /*FALLTHRU*/ case SPE_FORCE_BOLT: reveal_invis = TRUE; @@ -251,7 +265,7 @@ bhitm(struct monst *mtmp, struct obj *otmp) } else if (resists_magm(mtmp)) { /* magic resistance protects from polymorph traps, so make it guard against involuntary polymorph attacks too... */ - shieldeff(mtmp->mx, mtmp->my); + shieldeff_mon(mtmp); } else if (!resist(mtmp, otmp->oclass, 0, NOTELL)) { boolean polyspot = (otyp != POT_POLYMORPH), give_msg = (!Hallucination @@ -350,6 +364,8 @@ bhitm(struct monst *mtmp, struct obj *otmp) } case WAN_LOCKING: case SPE_WIZARD_LOCK: + if (disguised_mimic && box_or_door(mtmp)) + that_is_a_mimic(mtmp, MIM_REVEAL); /*seemimic()*/ wake = closeholdingtrap(mtmp, &learn_it); break; case WAN_PROBING: @@ -360,6 +376,8 @@ bhitm(struct monst *mtmp, struct obj *otmp) break; case WAN_OPENING: case SPE_KNOCK: + if (disguised_mimic && box_or_door(mtmp)) + that_is_a_mimic(mtmp, MIM_REVEAL); /*seemimic()*/ wake = FALSE; /* don't want immediate counterattack */ if (mtmp == u.ustuck) { /* zapping either holder/holdee or self [zapyourself()] will @@ -408,13 +426,15 @@ bhitm(struct monst *mtmp, struct obj *otmp) } break; case SPE_HEALING: - case SPE_EXTRA_HEALING: + case SPE_EXTRA_HEALING: { + int healamt = d(6, otyp == SPE_EXTRA_HEALING ? 8 : 4); + reveal_invis = TRUE; if (mtmp->data != &mons[PM_PESTILENCE]) { + int delta = mtmp->mhpmax - mtmp->mhp; + wake = FALSE; /* wakeup() makes the target angry */ - mtmp->mhp += d(6, otyp == SPE_EXTRA_HEALING ? 8 : 4); - if (mtmp->mhp > mtmp->mhpmax) - mtmp->mhp = mtmp->mhpmax; + healmon(mtmp, healamt, 0); /* plain healing must be blessed to cure blindness; extra healing only needs to not be cursed, so spell always cures [potions quaffed by monsters behave slightly differently; @@ -433,15 +453,19 @@ bhitm(struct monst *mtmp, struct obj *otmp) pline("%s looks%s better.", Monnam(mtmp), otyp == SPE_EXTRA_HEALING ? " much" : ""); } + if (mtmp->mtame && Role_if(PM_HEALER) && (delta > 0)) { + more_experienced(min(delta, healamt), 0); + newexplevel(); + } if (mtmp->mtame || mtmp->mpeaceful) { adjalign(Role_if(PM_HEALER) ? 1 : sgn(u.ualign.type)); } } else { /* Pestilence */ - /* Pestilence will always resist; damage is half of 3d{4,8} */ - (void) resist(mtmp, otmp->oclass, - d(3, otyp == SPE_EXTRA_HEALING ? 8 : 4), TELL); + /* Pestilence will always resist; damage is half of (healamt/2) */ + (void) resist(mtmp, otmp->oclass, healamt / 2, TELL); } break; + } case WAN_LIGHT: /* (broken wand) */ if (flash_hits_mon(mtmp, otmp)) { learn_it = TRUE; @@ -450,7 +474,8 @@ bhitm(struct monst *mtmp, struct obj *otmp) break; case WAN_SLEEP: /* (broken wand) */ /* [wakeup() doesn't rouse victims of temporary sleep, - so it's okay to leave `wake' set to TRUE here] */ + so it's okay to leave `wake' set to TRUE here; + revealing concealed mimic is handled by sleep_monst()] */ reveal_invis = TRUE; if (sleep_monst(mtmp, d(1 + otmp->spe, 12), WAND_CLASS)) slept_monst(mtmp); @@ -458,19 +483,35 @@ bhitm(struct monst *mtmp, struct obj *otmp) learn_it = TRUE; break; case SPE_STONE_TO_FLESH: - if (monsndx(mtmp->data) == PM_STONE_GOLEM) { - char *name = Monnam(mtmp); + if (mtmp->data->mlet == S_GOLEM) { + const char *mesg; + char *name = Monnam(mtmp); /* before possible polymorph */ - /* turn into flesh golem */ - if (newcham(mtmp, &mons[PM_FLESH_GOLEM], NO_NC_FLAGS)) { - if (canseemon(mtmp)) - pline("%s turns to flesh!", name); - } else { - if (canseemon(mtmp)) - pline("%s looks rather fleshy for a moment.", name); + /* turn stone golem into flesh golem */ + if (monsndx(mtmp->data) == PM_STONE_GOLEM + && newcham(mtmp, &mons[PM_FLESH_GOLEM], NO_NC_FLAGS)) + mesg = "turns to flesh!"; + else if (monsndx(mtmp->data) == PM_FLESH_GOLEM) + mesg = "seems fleshier..."; + else + mesg = "looks rather fleshy for a moment."; + + if (canseemon(mtmp)) + pline("%s %s", name, mesg); + } else if (mtmp->data->mlet == S_MIMIC + && ((M_AP_TYPE(mtmp) == M_AP_FURNITURE + && stone_furniture_type(mtmp->mappearance)) + || (M_AP_TYPE(mtmp) == M_AP_OBJECT + && stone_object_type(mtmp->mappearance)))) { + /* note: if that_is_a_mimic() doesn't get called to reveal the + mimic, wakeup() below will call seemimic() */ + if (cansee(mtmp->mx, mtmp->my)) { + set_msg_xy(mtmp->mx, mtmp->my); + that_is_a_mimic(mtmp, MIM_REVEAL | MIM_OMIT_WAIT); } - } else + } else { wake = FALSE; + } break; case SPE_DRAIN_LIFE: if (disguised_mimic) @@ -481,7 +522,7 @@ bhitm(struct monst *mtmp, struct obj *otmp) if (otyp == SPE_DRAIN_LIFE) dmg = spell_damage_bonus(dmg); if (resists_drli(mtmp)) { - shieldeff(mtmp->mx, mtmp->my); + shieldeff_mon(mtmp); } else if (!resist(mtmp, otmp->oclass, dmg, NOTELL) && !DEADMONSTER(mtmp)) { mtmp->mhp -= dmg; @@ -503,22 +544,19 @@ bhitm(struct monst *mtmp, struct obj *otmp) impossible("What an interesting effect (%d)", otyp); break; } - if (wake) { - if (!DEADMONSTER(mtmp)) { - wakeup(mtmp, helpful_gesture ? FALSE : TRUE); - m_respond(mtmp); - if (mtmp->isshk && !*u.ushops) - hot_pursuit(mtmp); - } else if (M_AP_TYPE(mtmp)) - seemimic(mtmp); /* might unblock if mimicking a boulder/door */ + if (wake && !DEADMONSTER(mtmp)) { + /* seemimic() is done by wakeup() and might unblock vision */ + wakeup(mtmp, helpful_gesture ? FALSE : TRUE); + m_respond(mtmp); + if (mtmp->isshk && !*u.ushops) + hot_pursuit(mtmp); } /* note: gb.bhitpos won't be set if swallowed, but that's okay since * reveal_invis will be false. We can't use mtmp->mx, my since it * might be an invisible worm hit on the tail. */ - if (reveal_invis) { - if (!DEADMONSTER(mtmp) && cansee(gb.bhitpos.x, gb.bhitpos.y) - && !canspotmon(mtmp)) + if (reveal_invis && !DEADMONSTER(mtmp)) { + if (cansee(gb.bhitpos.x, gb.bhitpos.y) && !canspotmon(mtmp)) map_invisible(gb.bhitpos.x, gb.bhitpos.y); } /* if effect was observable then discover the wand type provided @@ -526,6 +564,7 @@ bhitm(struct monst *mtmp, struct obj *otmp) if (learn_it) learnwand(otmp); return ret; +#undef box_or_door } /* hero is held by a monster or engulfed or holding a monster and has zapped @@ -1082,6 +1121,7 @@ revive(struct obj *corpse, boolean by_hero) obfree(corpse, (struct obj *) 0); break; } + FALLTHROUGH; /*FALLTHRU*/ case OBJ_FREE: case OBJ_MIGRATING: @@ -1752,10 +1792,9 @@ poly_obj(struct obj *obj, int id) /* Keep chest/box traps and poisoned ammo if we may */ if (obj->otrapped && Is_box(otmp)) - otmp->otrapped = TRUE; - + otmp->otrapped = 1; if (obj->opoisoned && is_poisonable(otmp)) - otmp->opoisoned = TRUE; + otmp->opoisoned = 1; if (id == STRANGE_OBJECT && obj->otyp == CORPSE) { /* turn crocodile corpses into shoes */ @@ -2042,11 +2081,13 @@ stone_to_flesh_obj(struct obj *obj) /* nonnull */ smell = TRUE; break; case WEAPON_CLASS: /* crysknife */ + FALLTHROUGH; /*FALLTHRU*/ default: res = 0; break; } + nhUse(obj); /* avoid 'assigned value not used' for poly_obj() calls */ if (smell) { /* non-meat eaters smell meat, meat eaters smell its flavor; @@ -2179,10 +2220,14 @@ bhito(struct obj *obj, struct obj *otmp) obj->dknown = 1; if (Is_container(obj) || obj->otyp == STATUE) { obj->cknown = obj->lknown = 1; - /* plural handling here is superfluous because containers - and statues don't stack */ - if (obj->otrapped) - pline("%s trapped!", Tobjnam(obj, "are")); + if (Is_box(obj) && !obj->tknown) { + /* obj->tknown applies to boxes and chests, not bags or + statues; plural handling here and the "empty" case + below are superfluous because containers don't stack */ + if (obj->otrapped) + pline("%s trapped!", Tobjnam(obj, "are")); + obj->tknown = 1; + } if (!obj->cobj) { pline("%s empty.", Tobjnam(obj, "are")); @@ -2626,15 +2671,17 @@ dozap(void) staticfn void boxlock_invent(struct obj *obj) { - struct obj *otmp; + struct obj *otmp, *nextobj; boolean boxing = FALSE; /* (un)lock carried boxes */ - for (otmp = gi.invent; otmp; otmp = otmp->nobj) + for (otmp = gi.invent; otmp; otmp = nextobj) { + nextobj = otmp->nobj; if (Is_box(otmp)) { (void) boxlock(otmp, obj); boxing = TRUE; } + } if (boxing) update_inventory(); /* in case any box->lknown has changed */ } @@ -2853,6 +2900,7 @@ zapyourself(struct obj *obj, boolean ordinary) case WAN_LIGHT: /* (broken wand) */ /* assert( !ordinary ); */ damage = d(obj->spe, 25); + FALLTHROUGH; /*FALLTHRU*/ case EXPENSIVE_CAMERA: if (!damage) @@ -3183,7 +3231,7 @@ zap_updown(struct obj *obj) /* wand or spell, nonnull */ we need to call it before probing for buried objects */ ltyp = SURFACE_AT(x, y); zap_map(x, y, obj); - map_zapped = TRUE; + /*map_zapped = TRUE; // not needed due to early return*/ if (ltyp == ICE || IS_FURNITURE(ltyp)) { surf = "it"; if (svl.lastseentyp[x][y] != rememberedltyp) @@ -3227,6 +3275,7 @@ zap_updown(struct obj *obj) /* wand or spell, nonnull */ case WAN_STRIKING: case SPE_FORCE_BOLT: striking = TRUE; + FALLTHROUGH; /*FALLTHRU*/ case WAN_LOCKING: case SPE_WIZARD_LOCK: @@ -3489,7 +3538,8 @@ exclam(int force) } void -hit(const char *str, /* zap text or missile name */ +hit( + const char *str, /* zap text or missile name */ struct monst *mtmp, /* target; for missile, might be hero */ const char *force) /* usually either "." or "!" via exclam() */ { @@ -3498,11 +3548,8 @@ hit(const char *str, /* zap text or missile name */ && (cansee(gb.bhitpos.x, gb.bhitpos.y) || canspotmon(mtmp) || engulfing_u(mtmp)))); - if (!verbosely) - pline("%s %s it.", The(str), vtense(str, "hit")); - else - pline("%s %s %s%s", The(str), vtense(str, "hit"), - mon_nam(mtmp), force); + pline("%s %s %s%s", The(str), vtense(str, "hit"), + verbosely ? mon_nam(mtmp) : "it", force); } void @@ -3581,7 +3628,7 @@ zap_map( ttmp = t_at(x, y); /* refresh in case trap was altered or is gone */ if (u.dz > 0) { /* zapping down */ - char ebuf[BUFSZ]; + char ebuf[BUFSZ], pristinebuf[BUFSZ], *etxt; struct engr *e = engr_at(x, y); /* subset of engraving effects; none sets `disclose' */ @@ -3590,7 +3637,8 @@ zap_map( case WAN_POLYMORPH: case SPE_POLYMORPH: del_engr(e); - make_engr_at(x, y, random_engraving(ebuf), svm.moves, 0); + etxt = random_engraving(ebuf, pristinebuf); + make_engr_at(x, y, etxt, pristinebuf, svm.moves, 0); break; case WAN_CANCELLATION: case SPE_CANCELLATION: @@ -3674,6 +3722,7 @@ zap_map( /* secret door gets revealed, converted into regular door */ if (ltyp == SDOOR) { cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */ + recalc_block_point(x, y); newsym(x, y); if (cansee(x, y)) { pline("Probing reveals a secret door."); @@ -3805,6 +3854,7 @@ bhit( while (range-- > 0) { coordxy x, y; + int xyglyph; gb.bhitpos.x += ddx; gb.bhitpos.y += ddy; @@ -3840,7 +3890,8 @@ bhit( && hits_bars(pobj, x - ddx, y - ddy, x, y, point_blank ? 0 : !rn2(5), 1)) { /* caveat: obj might now be null... */ - obj = *pobj; + obj = *pobj; /* not currently needed due to 'break'; keep */ + nhUse(obj); /* in case usage gets added after the loop */ gb.bhitpos.x -= ddx; gb.bhitpos.y -= ddy; break; @@ -3907,9 +3958,20 @@ bhit( give message and skip it in order to keep going; if attack is light and mtmp is a mimic pretending to be an object, behave as if there is no monster here (if pretending - to be furniture, it will be revealed by flash_hits_mon()) */ + to be furniture, it will be revealed by flash_hits_mon()); + thrown objects don't hit mimics pretending to be objects (both + because the hero is likely aiming to throw over what seems to + be an object rather than at it, and for balance because + otherwise mimics are too easy to identify by throwing gold at + them); exception: if the hero knows there is a monster there, + they will be aiming at the monster */ + xyglyph = glyph_at(x, y); if (mtmp && (((weapon == THROWN_WEAPON || weapon == KICKED_WEAPON) - && shade_miss(&gy.youmonst, mtmp, obj, TRUE, TRUE)) + && (shade_miss(&gy.youmonst, mtmp, obj, TRUE, TRUE) + || (M_AP_TYPE(mtmp) == M_AP_OBJECT + && !glyph_is_monster(xyglyph) + && !glyph_is_warning(xyglyph) + && !glyph_is_invisible(xyglyph)))) || (weapon == FLASHED_LIGHT && M_AP_TYPE(mtmp) == M_AP_OBJECT))) mtmp = (struct monst *) 0; @@ -4092,7 +4154,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 = (int) xytod(dx, dy); + i = xytod(dx, dy); tmp_at(DISP_FLASH, cmap_to_glyph(boom)); for (ct = 0; ct < 10; ct++) { i = DIR_CLAMP(i); @@ -4207,7 +4269,8 @@ zhitm( tmp += destroy_items(mon, AD_COLD, orig_dmg); break; case ZT_SLEEP: - /* possibly resistance and shield effect handled by sleep_monst() */ + /* resistance and shield effect and revealing concealed mimic are + handled by sleep_monst() */ tmp = 0; (void) sleep_monst(mon, d(nd, 25), type == ZT_WAND(ZT_SLEEP) ? WAND_CLASS : '\0'); @@ -4215,10 +4278,9 @@ zhitm( case ZT_DEATH: /* death/disintegration */ if (abs(type) != ZT_BREATH(ZT_DEATH)) { /* death */ if (mon->data == &mons[PM_DEATH]) { - mon->mhpmax += mon->mhpmax / 2; + healmon(mon, mon->mhpmax * 3 / 2, mon->mhpmax / 2); if (mon->mhpmax >= MAGIC_COOKIE) mon->mhpmax = MAGIC_COOKIE - 1; - mon->mhp = mon->mhpmax; tmp = 0; break; } @@ -4574,8 +4636,9 @@ burn_floor_objects( /* will zap/spell/breath attack score a hit against armor class `ac'? */ staticfn int -zap_hit(int ac, - int type) /* either hero cast spell type or 0 */ +zap_hit( + int ac, + int type) /* either hero cast spell type or 0 */ { int chance = rn2(20); int spell_bonus = type ? spell_hit_bonus(type) : 0; @@ -4908,6 +4971,7 @@ dobuzz( switch (bounce) { case 0: dx = -dx; + FALLTHROUGH; /*FALLTHRU*/ case 1: dy = -dy; @@ -5167,7 +5231,7 @@ zap_over_floor( else lev->typ = HWALL; fix_wall_spines(max(0,x-1), max(0,y-1), - min(COLNO,x+1), min(ROWNO,y+1)); + min(COLNO-1,x+1), min(ROWNO-1,y+1)); } else { lev->typ = lava ? ROOM : ICE; } @@ -5240,6 +5304,7 @@ zap_over_floor( break; case ZT_LIGHTNING: + FALLTHROUGH; /*FALLTHRU*/ case ZT_ACID: if (lev->typ == IRONBARS) { @@ -5248,13 +5313,14 @@ zap_over_floor( if ((lev->wall_info & W_NONDIGGABLE) != 0) { if (see_it) Norep("The %s %s somewhat but remain intact.", - (damgtype == ZT_ACID) ? "corrode" : "melt", - defsyms[S_bars].explanation); + defsyms[S_bars].explanation, + (damgtype == ZT_ACID) ? "corrode" : "melt"); /* but nothing actually happens... */ } else { rangemod -= 3; if (see_it) - Norep("The %s melt.", defsyms[S_bars].explanation); + Norep("The %s %s.", defsyms[S_bars].explanation, + (damgtype == ZT_ACID) ? "corrode away" : "melt"); dissolve_bars(x, y); if (*in_rooms(x, y, SHOPBASE)) { add_damage(x, y, (type >= 0) ? SHOP_BARS_COST : 0L); @@ -5293,6 +5359,7 @@ zap_over_floor( /* secret door gets revealed, converted into regular door */ if (levl[x][y].typ == SDOOR) { cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */ + recalc_block_point(x, y); /* target spot will now pass closed_door() test below (except on rogue level) */ newsym(x, y); @@ -5366,7 +5433,7 @@ zap_over_floor( add_damage(x, y, 0L); } lev->doormask = new_doormask; - unblock_point(x, y); /* vision */ + recalc_block_point(x, y); /* vision */ if (see_it) { pline1(see_txt); newsym(x, y); @@ -5575,12 +5642,16 @@ u_adtyp_resistance_obj(int dmgtyp) if (!prop) return 0; - /* Items that give an extrinsic resistance give 99% protection to - your items */ - if ((u.uprops[prop].extrinsic & (W_ARMOR | W_ACCESSORY | W_WEP)) != 0) + /* FIXME? these percentages (99 and 90) seem too high... */ + + /* items that give an extrinsic resistance when worn or wielded or + carried give 99% protection to your items */ + if ((u.uprops[prop].extrinsic & (W_ARMOR | W_ACCESSORY | W_WEP | W_ART)) + != 0L) return 99; - /* Dwarvish cloaks give a 90% protection to items against heat and cold */ + /* worn dwarvish cloaks give 90% protection against heat and cold to + carried items */ if (uarmc && uarmc->otyp == DWARVISH_CLOAK && (dmgtyp == AD_COLD || dmgtyp == AD_FIRE)) return 90; @@ -6031,10 +6102,8 @@ resist(struct monst *mtmp, char oclass, int damage, int tell) resisted = rn2(100 + alev - dlev) < mtmp->data->mr; if (resisted) { - if (tell) { - shieldeff(mtmp->mx, mtmp->my); - pline("%s resists!", Monnam(mtmp)); - } + if (tell) + shieldeff_mon(mtmp); damage = (damage + 1) / 2; } @@ -6123,6 +6192,7 @@ makewish(void) int tries = 0; long oldwisharti = u.uconduct.wisharti; + svc.context.resume_wish = 0; promptbuf[0] = '\0'; nothing = cg.zeroobj; /* lint suppression; only its address matters */ if (flags.verbose) @@ -6133,6 +6203,12 @@ makewish(void) Strcat(promptbuf, " (enter 'help' for assistance)"); Strcat(promptbuf, "?"); getlin(promptbuf, buf); + + if (iflags.term_gone) { + svc.context.resume_wish = 1; + return; + } + (void) mungspaces(buf); if (buf[0] == '\033') { buf[0] = '\0'; @@ -6187,14 +6263,22 @@ makewish(void) /* TODO? maybe generate a second event describing what was received since these just echo player's request rather than show actual result */ - const char *verb = ((Is_airlevel(&u.uz) || u.uinwater) ? "slip" : "drop"), + if (otmp->otyp == CORPSE && !u_safe_from_fatal_corpse(otmp, st_all)) + otmp->wishedfor = 1; + + const char *verb = ((Is_airlevel(&u.uz) || u.uinwater) + ? "slip" + : (otmp->otyp == CORPSE && otmp->wishedfor) + ? "materialize" : "drop"), *oops_msg = (u.uswallow ? "Oops! %s out of your reach!" : (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz) || levl[u.ux][u.uy].typ < IRONBARS || levl[u.ux][u.uy].typ >= ICE) ? "Oops! %s away from you!" - : "Oops! %s to the floor!"); + : !(otmp->otyp == CORPSE && otmp->wishedfor) + ? "Oops! %s to the floor!" + : "Careful! %s on the floor!"); /* The(aobjnam()) is safe since otmp is unidentified -dlc */ (void) hold_another_object(otmp, oops_msg, The(aobjnam(otmp, verb)), diff --git a/sys/libnh/libnhmain.c b/sys/libnh/libnhmain.c index d43cdc362..562de1c78 100644 --- a/sys/libnh/libnhmain.c +++ b/sys/libnh/libnhmain.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 libnhmain.c $NHDT-Date: 1693359589 2023/08/30 01:39:49 $ $NHDT-Branch: keni-crashweb2 $:$NHDT-Revision: 1.106 $ */ +/* NetHack 3.7 libnhmain.c $NHDT-Date: 1693359589 2023/08/30 01:39:49 $ $NHDT-Branch: keni-crashweb2 $:$NHDT-Revision: 1.106 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -8,10 +8,10 @@ #include "hack.h" #include "dlb.h" -#include #include #include #include +#include #ifndef O_RDONLY #include #endif @@ -51,6 +51,7 @@ extern void init_linux_cons(void); static void wd_message(void); static struct passwd *get_unix_pw(void); +ATTRNORETURN static void opt_terminate(void) NORETURN; #ifdef __EMSCRIPTEN__ /* if WebAssembly, export this API and don't optimize it out */ @@ -103,19 +104,13 @@ nhmain(int argc, char *argv[]) exit(EXIT_SUCCESS); #ifdef CRASHREPORT - if (argcheck(argc, argv, ARG_BIDSHOW)) - exit(EXIT_SUCCESS); + if (argcheck(argc, argv, ARG_BIDSHOW)) + exit(EXIT_SUCCESS); #endif if (argcheck(argc, argv, ARG_SHOWPATHS) == 2) { -#ifdef CHDIR - chdirx((char *) 0, 0); -#endif - iflags.initoptions_noterminate = TRUE; - initoptions(); - iflags.initoptions_noterminate = FALSE; - reveal_paths(); - exit(EXIT_SUCCESS); + gd.deferred_showpaths = TRUE; + return; } if (argcheck(argc, argv, ARG_DEBUG) == 1) { argc--; @@ -174,6 +169,12 @@ nhmain(int argc, char *argv[]) chdirx(dir, 1); #endif +#ifdef __EMSCRIPTEN__ + js_helpers_init(); + js_constants_init(); + js_globals_init(); +#endif + #ifdef _M_UNIX check_sco_console(); #endif @@ -201,11 +202,6 @@ nhmain(int argc, char *argv[]) process_options(argc, argv); /* command line options */ #ifdef WINCHAIN commit_windowchain(); -#endif -#ifdef __EMSCRIPTEN__ - js_helpers_init(); - js_constants_init(); - js_globals_init(); #endif init_nhwindows(&argc, argv); /* now we can set up window system */ #ifdef _M_UNIX @@ -773,6 +769,32 @@ sys_random_seed(void) return seed; } +/* 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*/ +} + +/* 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*/ +} + #ifdef __EMSCRIPTEN__ /*** * Helpers @@ -781,9 +803,9 @@ EM_JS(void, js_helpers_init, (), { globalThis.nethackGlobal = globalThis.nethackGlobal || {}; globalThis.nethackGlobal.helpers = globalThis.nethackGlobal.helpers || {}; - installHelper(displayInventory); - installHelper(getPointerValue); - installHelper(setPointerValue); + installHelper(displayInventory, "displayInventory"); + installHelper(getPointerValue, "getPointerValue"); + installHelper(setPointerValue, "setPointerValue"); // used by update_inventory function displayInventory() { @@ -804,6 +826,8 @@ EM_JS(void, js_helpers_init, (), { return getValue(ptr, "*"); case "c": // char return String.fromCharCode(getValue(ptr, "i8")); + case "b": + return getValue(ptr, "i8") == 1; case "0": /* 2^0 = 1 byte */ return getValue(ptr, "i8"); case "1": /* 2^1 = 2 bytes */ @@ -816,8 +840,8 @@ EM_JS(void, js_helpers_init, (), { return getValue(ptr, "float"); case "d": // double return getValue(ptr, "double"); - case "o": // overloaded: multiple types - return ptr; + case "v": // void + return undefined; default: throw new TypeError ("unknown type:" + type); } @@ -828,7 +852,8 @@ EM_JS(void, js_helpers_init, (), { // console.log("setPointerValue", name, "0x" + ptr.toString(16), type, value); switch (type) { case "p": - throw new Error("not implemented"); + setValue(ptr, value, "*"); + break; case "s": if(typeof value !== "string") throw new TypeError(`expected ${name} return type to be string`); @@ -841,6 +866,11 @@ EM_JS(void, js_helpers_init, (), { throw new TypeError(`expected ${name} return type to be integer`); setValue(ptr, value, "i32"); break; + case "1": + if(typeof value !== "number" || !Number.isInteger(value)) + throw new TypeError(`expected ${name} return type to be integer`); + setValue(ptr, value, "i16"); + break; case "c": if(typeof value !== "number" || value < 0 || value > 128) throw new TypeError(`expected ${name} return type to be integer representing an ASCII character`); @@ -857,6 +887,10 @@ EM_JS(void, js_helpers_init, (), { throw new TypeError(`expected ${name} return type to be double`); setValue(ptr, value, "double"); break; + case "b": + if (typeof value !== "boolean") + throw new TypeError(`expected ${name} return type to be boolean`); + setValue(ptr, value ? 1 : 0, "i8"); case "v": break; default: @@ -896,11 +930,19 @@ EM_JS(void, set_const_str, (char *scope_str, char *name_str, char *input_str), { globalThis.nethackGlobal.constants[scope] = globalThis.nethackGlobal.constants[scope] || {}; globalThis.nethackGlobal.constants[scope][name] = str; }); +#define SET_POINTER(name) set_const_ptr(#name, (void *)&name); +EM_JS(void, set_const_ptr, (char *name_str, void* ptr), { + let name = UTF8ToString(name_str); + + globalThis.nethackGlobal.pointers = globalThis.nethackGlobal.pointers || {}; + globalThis.nethackGlobal.pointers[name] = ptr; +}); void js_constants_init() { EM_ASM({ globalThis.nethackGlobal = globalThis.nethackGlobal || {}; globalThis.nethackGlobal.constants = globalThis.nethackGlobal.constants || {}; + globalThis.nethackGlobal.pointers = globalThis.nethackGlobal.pointers || {}; }); // create_nhwindow @@ -989,8 +1031,7 @@ void js_constants_init() { // copyright SET_CONSTANT_STRING("COPYRIGHT", COPYRIGHT_BANNER_A); SET_CONSTANT_STRING("COPYRIGHT", COPYRIGHT_BANNER_B); - // XXX: not set for cross-compile - //SET_CONSTANT_STRING("COPYRIGHT", COPYRIGHT_BANNER_C); + set_const_str("COPYRIGHT", "COPYRIGHT_BANNER_C", (char*) COPYRIGHT_BANNER_C); SET_CONSTANT_STRING("COPYRIGHT", COPYRIGHT_BANNER_D); // glyphs @@ -1042,6 +1083,119 @@ void js_constants_init() { SET_CONSTANT("COLOR_ATTR", HL_ATTCLR_BLINK); SET_CONSTANT("COLOR_ATTR", HL_ATTCLR_INVERSE); SET_CONSTANT("COLOR_ATTR", BL_ATTCLR_MAX); + + SET_CONSTANT("BL_MASK", BL_MASK_BAREH); + SET_CONSTANT("BL_MASK", BL_MASK_BLIND); + SET_CONSTANT("BL_MASK", BL_MASK_BUSY); + SET_CONSTANT("BL_MASK", BL_MASK_CONF); + SET_CONSTANT("BL_MASK", BL_MASK_DEAF); + SET_CONSTANT("BL_MASK", BL_MASK_ELF_IRON); + SET_CONSTANT("BL_MASK", BL_MASK_FLY); + SET_CONSTANT("BL_MASK", BL_MASK_FOODPOIS); + SET_CONSTANT("BL_MASK", BL_MASK_GLOWHANDS); + SET_CONSTANT("BL_MASK", BL_MASK_GRAB); + SET_CONSTANT("BL_MASK", BL_MASK_HALLU); + SET_CONSTANT("BL_MASK", BL_MASK_HELD); + SET_CONSTANT("BL_MASK", BL_MASK_ICY); + SET_CONSTANT("BL_MASK", BL_MASK_INLAVA); + SET_CONSTANT("BL_MASK", BL_MASK_LEV); + SET_CONSTANT("BL_MASK", BL_MASK_PARLYZ); + SET_CONSTANT("BL_MASK", BL_MASK_RIDE); + SET_CONSTANT("BL_MASK", BL_MASK_SLEEPING); + SET_CONSTANT("BL_MASK", BL_MASK_SLIME); + SET_CONSTANT("BL_MASK", BL_MASK_SLIPPERY); + SET_CONSTANT("BL_MASK", BL_MASK_STONE); + SET_CONSTANT("BL_MASK", BL_MASK_STRNGL); + SET_CONSTANT("BL_MASK", BL_MASK_STUN); + SET_CONSTANT("BL_MASK", BL_MASK_SUBMERGED); + SET_CONSTANT("BL_MASK", BL_MASK_TERMILL); + SET_CONSTANT("BL_MASK", BL_MASK_TETHERED); + SET_CONSTANT("BL_MASK", BL_MASK_TRAPPED); + SET_CONSTANT("BL_MASK", BL_MASK_UNCONSC); + SET_CONSTANT("BL_MASK", BL_MASK_WOUNDEDL); + SET_CONSTANT("BL_MASK", BL_MASK_HOLDING); + + SET_CONSTANT("ROLE_RACEMASK", MH_HUMAN); + SET_CONSTANT("ROLE_RACEMASK", MH_ELF); + SET_CONSTANT("ROLE_RACEMASK", MH_DWARF); + SET_CONSTANT("ROLE_RACEMASK", MH_GNOME); + SET_CONSTANT("ROLE_RACEMASK", MH_ORC); + + SET_CONSTANT("ROLE_GENDMASK", ROLE_MALE); + SET_CONSTANT("ROLE_GENDMASK", ROLE_FEMALE); + SET_CONSTANT("ROLE_GENDMASK", ROLE_NEUTER); + + SET_CONSTANT("ROLE_ALIGNMASK", ROLE_LAWFUL); + SET_CONSTANT("ROLE_ALIGNMASK", ROLE_NEUTRAL); + SET_CONSTANT("ROLE_ALIGNMASK", ROLE_CHAOTIC); + + SET_CONSTANT("blconditions", bl_bareh); + SET_CONSTANT("blconditions", bl_blind); + SET_CONSTANT("blconditions", bl_busy); + SET_CONSTANT("blconditions", bl_conf); + SET_CONSTANT("blconditions", bl_deaf); + SET_CONSTANT("blconditions", bl_elf_iron); + SET_CONSTANT("blconditions", bl_fly); + SET_CONSTANT("blconditions", bl_foodpois); + SET_CONSTANT("blconditions", bl_glowhands); + SET_CONSTANT("blconditions", bl_grab); + SET_CONSTANT("blconditions", bl_hallu); + SET_CONSTANT("blconditions", bl_held); + SET_CONSTANT("blconditions", bl_icy); + SET_CONSTANT("blconditions", bl_inlava); + SET_CONSTANT("blconditions", bl_lev); + SET_CONSTANT("blconditions", bl_parlyz); + SET_CONSTANT("blconditions", bl_ride); + SET_CONSTANT("blconditions", bl_sleeping); + SET_CONSTANT("blconditions", bl_slime); + SET_CONSTANT("blconditions", bl_slippery); + SET_CONSTANT("blconditions", bl_stone); + SET_CONSTANT("blconditions", bl_strngl); + SET_CONSTANT("blconditions", bl_stun); + SET_CONSTANT("blconditions", bl_submerged); + SET_CONSTANT("blconditions", bl_termill); + SET_CONSTANT("blconditions", bl_tethered); + SET_CONSTANT("blconditions", bl_trapped); + SET_CONSTANT("blconditions", bl_unconsc); + SET_CONSTANT("blconditions", bl_woundedl); + SET_CONSTANT("blconditions", bl_holding); + SET_CONSTANT("blconditions", CONDITION_COUNT); + + SET_CONSTANT("HL", HL_UNDEF); + SET_CONSTANT("HL", HL_NONE); + SET_CONSTANT("HL", HL_BOLD); + SET_CONSTANT("HL", HL_DIM); + SET_CONSTANT("HL", HL_ITALIC); + SET_CONSTANT("HL", HL_ULINE); + SET_CONSTANT("HL", HL_BLINK); + SET_CONSTANT("HL", HL_INVERSE); + + SET_CONSTANT("MG", MG_HERO); + SET_CONSTANT("MG", MG_CORPSE); + SET_CONSTANT("MG", MG_INVIS); + SET_CONSTANT("MG", MG_DETECT); + SET_CONSTANT("MG", MG_PET); + SET_CONSTANT("MG", MG_RIDDEN); + SET_CONSTANT("MG", MG_STATUE); + SET_CONSTANT("MG", MG_OBJPILE); + SET_CONSTANT("MG", MG_BW_LAVA); + SET_CONSTANT("MG", MG_BW_ICE); + SET_CONSTANT("MG", MG_BW_SINK); + SET_CONSTANT("MG", MG_BW_ENGR); + SET_CONSTANT("MG", MG_NOTHING); + SET_CONSTANT("MG", MG_UNEXPL); + SET_CONSTANT("MG", MG_MALE); + SET_CONSTANT("MG", MG_FEMALE); + + SET_POINTER(extcmdlist); + SET_POINTER(conditions); + SET_POINTER(condtests); + + /* roles/races/genders/alignments */ + SET_POINTER(roles); + SET_POINTER(races); + SET_POINTER(genders); + SET_POINTER(aligns); } /*** @@ -1073,6 +1227,20 @@ void js_globals_init() { CREATE_GLOBAL(WIN_MESSAGE, "i"); CREATE_GLOBAL(WIN_INVEN, "i"); CREATE_GLOBAL(WIN_STATUS, "i"); + + /* instance flags */ + CREATE_GLOBAL(iflags.window_inited, "b"); + CREATE_GLOBAL(iflags.wc2_hitpointbar, "b"); + CREATE_GLOBAL(iflags.wc_hilite_pet, "b"); + CREATE_GLOBAL(iflags.hilite_pile, "b"); + + /* flags */ + CREATE_GLOBAL(flags.initrole, "i"); + CREATE_GLOBAL(flags.initrace, "i"); + CREATE_GLOBAL(flags.initgend, "i"); + CREATE_GLOBAL(flags.initalign, "i"); + CREATE_GLOBAL(flags.showexp, "b"); + CREATE_GLOBAL(flags.time, "b"); } EM_JS(void, create_global, (char *name_str, void *ptr, char *type_str), { diff --git a/sys/msdos/Install.dos b/sys/msdos/Install.dos index 108e117a4..10b94888d 100644 --- a/sys/msdos/Install.dos +++ b/sys/msdos/Install.dos @@ -56,9 +56,9 @@ II. There once was a time when people built NetHack right on their DOS machine. pdcurses from: https://github.com/wmcbrine/PDCurses.git - - A shell script to download the above-mentioned djgpp cross-compiler and + - A bash script to download the above-mentioned djgpp cross-compiler and associated support pieces for either linux or macOS is available: - sh sys/msdos/fetch-cross-compiler.sh + sys/msdos/fetch-cross-compiler.sh That script won't install anything, it just does file fetches and stores them in subfolders of lib. The linux.370 and macOS.370 hints files are @@ -66,7 +66,7 @@ II. There once was a time when people built NetHack right on their DOS machine. CROSS_TO_MSDOS=1 on your make command line. - Note: Both the fetch-cross-compiler.sh script and the msdos + Note: Both the fetch-cross-compiler.sh bash script and the msdos cross-compile and package procedures require unzip and zip to be available on your host build system. @@ -136,8 +136,8 @@ Appendix B - Common Difficulties Encountered make: *** [Makefile:1531: nethack] Error 2 That result usually indicates that you haven't successfully run - the script to fetch the cross-compiler. Try again: - sh sys/msdos/fetch-cross-compiler.sh + the bash script to fetch the cross-compiler. Try again: + sys/msdos/fetch-cross-compiler.sh ii) The cross-compile stops with an error "libfl.so.2: cannot open shared object file: No such file or directory. @@ -145,23 +145,16 @@ Appendix B - Common Difficulties Encountered This indicates that your host system does not have the flex library installed, and the cross-compiler needs it to be. On Ubuntu systems, you can easily rectify this issue by - just installing flex, as follows: + just installing the flex shared library as follows: - sudo apt-get install flex + sudo apt install libfl2 Reading package lists... Done Building dependency tree... Done Reading state information... Done - The following additional packages will be installed: - libfl-dev libfl2 m4 - Suggested packages: - bison flex-doc m4-doc The following NEW packages will be installed: - flex libfl-dev libfl2 m4 - 0 upgraded, 4 newly installed, 0 to remove and 0 not upgraded. - Need to get 558 kB of archives. - After this operation, 1699 kB of additional disk space will be - used. Do you want to continue? [Y/n] Y + libfl2 + 0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. Appendix D - Contacting the Development Team diff --git a/sys/msdos/Makefile.GCC b/sys/msdos/Makefile.GCC index 52272f5d8..069b2679a 100644 --- a/sys/msdos/Makefile.GCC +++ b/sys/msdos/Makefile.GCC @@ -272,31 +272,31 @@ DLBOBJ = $(O)dlb.o VOBJ01 = $(O)allmain.o $(O)alloc.o $(O)apply.o $(O)artifact.o $(O)attrib.o -VOBJ02 = $(O)ball.o $(O)bones.o $(O)botl.o $(O)calendar.o $(O)cmd.o -VOBJ03 = $(O)coloratt.o $(O)date.o $(O)dbridge.o $(O)decl.o $(O)detect.o -VOBJ04 = $(O)dig.o $(O)display.o $(O)do.o $(O)do_name.o $(O)do_wear.o -VOBJ05 = $(O)dog.o $(O)dogmove.o $(O)dokick.o $(O)dothrow.o $(O)drawing.o -VOBJ06 = $(O)dungeon.o $(O)eat.o $(O)end.o $(O)engrave.o $(O)exper.o -VOBJ07 = $(O)explode.o $(O)extralev.o $(O)files.o $(O)fountain.o $(O)getpos.o -VOBJ08 = $(O)glyphs.o $(O)getline.o $(O)hack.o $(O)hacklib.o $(O)insight.o -VOBJ09 = $(O)invent.o $(O)isaac64.o $(O)lock.o $(O)mail.o $(O)main.o -VOBJ10 = $(O)makemon.o $(O)mcastu.o $(O)mdlib.o $(O)mhitm.o $(O)mhitu.o -VOBJ11 = $(O)minion.o $(O)mkmap.o $(O)mklev.o $(O)mkmaze.o $(O)mkobj.o -VOBJ12 = $(O)mkroom.o $(O)mon.o $(O)mondata.o $(O)monmove.o $(O)monst.o -VOBJ13 = $(O)mplayer.o $(O)mthrowu.o $(O)muse.o $(O)music.o $(O)o_init.o -VOBJ14 = $(O)objects.o $(O)objnam.o $(O)options.o $(O)pickup.o $(O)pline.o -VOBJ15 = $(O)polyself.o $(O)potion.o $(O)quest.o $(O)questpgr.o $(O)pager.o -VOBJ16 = $(O)pray.o $(O)priest.o $(O)read.o $(O)rect.o $(O)region.o -VOBJ17 = $(O)report.o $(O)restore.o $(O)rip.o $(O)rnd.o $(O)role.o -VOBJ18 = $(O)rumors.o $(O)save.o $(O)selvar.o $(O)sfstruct.o $(O)shk.o -VOBJ19 = $(O)shknam.o $(O)sit.o $(O)sounds.o $(O)sp_lev.o $(O)spell.o -VOBJ20 = $(O)stairs.o $(O)steal.o $(O)steed.o $(O)symbols.o $(O)sys.o -VOBJ21 = $(O)teleport.o $(O)strutil.o $(O)termcap.o $(O)timeout.o $(O)topl.o -VOBJ22 = $(O)topten.o $(O)trap.o $(O)u_init.o $(O)uhitm.o $(O)utf8map.o -VOBJ23 = $(O)vault.o $(O)track.o $(O)vision.o $(O)weapon.o $(O)were.o -VOBJ24 = $(O)wield.o $(O)windows.o $(O)wintty.o $(O)wizard.o $(O)wizcmds.o -VOBJ25 = $(O)worm.o $(O)worn.o $(O)write.o $(O)zap.o $(O)light.o -VOBJ26 = $(O)dlb.o $(REGEX) +VOBJ02 = $(O)ball.o $(O)bones.o $(O)botl.o $(O)calendar.o $(O)cfgfiles.o +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) SOBJ = $(O)msdos.o $(O)pcsys.o $(O)tty.o $(O)unix.o \ $(O)video.o $(O)vidtxt.o $(O)pckeys.o @@ -419,7 +419,7 @@ HACK_H = $(CONFIG_H) $(INCL)/context.h $(DUNGEON_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) \ - $(INCL)/sys.h + $(INCL)/sys.h $(INCL)/weight.h DLB_H = $(INCL)/dlb.h ifeq ($(SUPPRESS_GRAPHICS),Y) @@ -458,18 +458,11 @@ endif INCLDIR=$(PDCINCL) -I../include -I../sys/msdos $(LUAINCL) # Debugging -#cflags = -pg -c $(INCLDIR) $(DLBFLG) $(CURSESDEF) -DSUPPRESS_GRAPHICS +#cflags = -pg -c -D_NAIVE_DOS_REGS $(INCLDIR) $(DLBFLG) $(CURSESDEF) -DSUPPRESS_GRAPHICS #LFLAGS = -pg -cflags = -c -O $(INCLDIR) $(DLBFLG) $(CURSESDEF) -DSUPPRESS_GRAPHICS -LFLAGS = - -# Debugging -#cflags = -g -c $(INCLDIR) $(DLBFLG) $(CURSESDEF) -DTILES_IN_GLYPHMAP -#LFLAGS = -g - # Normal -cflags = -c -O $(INCLDIR) $(DLBFLG) $(CURSESDEF) -DTILES_IN_GLYPHMAP +cflags = -c -O -D_NAIVE_DOS_REGS $(INCLDIR) $(DLBFLG) $(CURSESDEF) -DTILES_IN_GLYPHMAP LFLAGS = #========================================== @@ -702,8 +695,8 @@ $(O)hacklibu.o: $(CONFIG_H) $(SRC)/hacklib.c # Recover Utility #========================================== -$(U)recover.exe: $(RECOVOBJS) - $(LINK) $(LFLAGS) -o$@ $(O)recover.o +$(U)recover.exe: $(RECOVOBJS) $(HACKLIB) + $(LINK) $(LFLAGS) -o$@ $(O)recover.o $(HACKLIB) $(O)recover.o: $(CONFIG_H) $(U)recover.c $(CC) $(cflags) -o$@ $(U)recover.c diff --git a/sys/msdos/fetch-cross-compiler.sh b/sys/msdos/fetch-cross-compiler.sh index 3dedc8847..36558a00c 100755 --- a/sys/msdos/fetch-cross-compiler.sh +++ b/sys/msdos/fetch-cross-compiler.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash if [ ! -z "${TF_BUILD}" ]; then set -x @@ -60,6 +60,7 @@ fi cd lib if [ ! -f "$DJGPP_FILE" ]; then + echo "fetching djgpp cross-compiler int lib/djgpp" if [ "$(uname)" = "Darwin" ]; then #Mac curl -L $DJGPP_URL -o $DJGPP_FILE @@ -69,12 +70,14 @@ if [ ! -f "$DJGPP_FILE" ]; then fi if [ ! -d djgpp/i586-pc-msdosdjgpp ]; then + echo "extracting djgpp from $DJGPP_FILE" tar xjf "$DJGPP_FILE" #rm -f $DJGPP_FILE fi # DOS-extender for use with djgpp if [ ! -d djgpp/cwsdpmi ]; then + echo "fetching DOS extender for use with djgpp" if [ "$(uname)" = "Darwin" ]; then #Mac curl http://sandmann.dotster.com/cwsdpmi/csdpmi7b.zip -o csdpmi7b.zip @@ -94,18 +97,18 @@ fi # PDCurses (non-Unicode build uses this) if [ ! -d "pdcurses" ]; then - echo "Getting ../pdcurses from https://github.com/wmcbrine/PDCurses.git" ; \ + echo "fetching ../pdcurses from https://github.com/wmcbrine/PDCurses.git" ; \ git clone --depth 1 https://github.com/wmcbrine/PDCurses.git pdcurses fi # PDCursesMod (Unicode build uses this) if [ ! -d "pdcursesmod" ]; then - echo "Getting ../pdcursesmod from https://github.com/Bill-Gray/PDCursesMod.git" ; \ + echo "fetching ../pdcursesmod from https://github.com/Bill-Gray/PDCursesMod.git" ; \ git clone --depth 1 https://github.com/Bill-Gray/PDCursesMod.git pdcursesmod fi if [ ! -d djgpp/djgpp-patch ]; then - echo "Getting djlsr205.zip" ; + echo "fetching djlsr205.zip" ; cd djgpp mkdir -p djgpp-patch cd djgpp-patch @@ -132,31 +135,83 @@ if [ ! -d djgpp/djgpp-patch ]; then mkdir -p src/libc/go32 unzip -p djlsr205.zip src/libc/go32/exceptn.S >src/libc/go32/exceptn.S patch -p0 -l -i ../../../sys/msdos/exceptn.S.patch + echo "djgpp patch applied" cd ../../ fi -# get a copy of symify to insert in the final zip package -# to make bug reports more useful -# curl --output djdev205.zip http://www.mirrorservice.org/sites/ftp.delorie.com/pub/djgpp/current/v2/djdev205.zip -if [ ! -d djgpp/symify ]; then - echo "Getting djdev205.zip" ; +# create a directories to hold some native DOS pieces that might get deployed +if [ ! -d djgpp/target ]; then cd djgpp - mkdir -p symify - cd symify - if [ "$(uname)" = "Darwin" ]; then - #Mac - curl --output djdev205.zip http://www.mirrorservice.org/sites/ftp.delorie.com/pub/djgpp/current/v2/djdev205.zip - export cmdstatus=$? - else - wget --quiet --no-hsts http://www.mirrorservice.org/sites/ftp.delorie.com/pub/djgpp/current/v2/djdev205.zip - export cmdstatus=$? - fi - ls -l - if [ $cmdstatus -eq 0 ]; then - echo "fetch of symify was successful" - unzip -p djdev205.zip bin/symify.exe >./simify.exe - fi - cd ../../ + mkdir -p target + cd ../ +fi +if [ ! -d djgpp/target/bin ]; then + mkdir -p djgpp/target/bin +fi +if [ ! -d djgpp/target/lib ]; then + mkdir -p djgpp/target/lib +fi +if [ ! -d djgpp/target/include ]; then + mkdir -p djgpp/target/include +fi + + +# These 4 arrays must have their elements kept in synch. +# +# full URL for the remote zip file to fetch +zipurl=( + "http://www.mirrorservice.org/sites/ftp.delorie.com/pub/djgpp/current/v2/djdev205.zip" + "http://www.mirrorservice.org/sites/ftp.delorie.com/pub/djgpp/current/v2gnu/gdb801b.zip" +) +# name of zip file upon landing here +ziplocal=( + "djdev205.zip" + "gdb801b.zip" +) +# name of file after extraction from zip file +exesought=( + "bin/symify.exe" + "bin/gdb.exe" + +) +# stored full path inside zip file +exepathinzip=( + "bin/symify.exe" + "bin/gdb.exe" +) + +if [ -d djgpp/target ]; then + cd djgpp/target + count=0 + while [ "x${zipurl[count]}" != "x" ] + do + if [ ! -f ${exesought[count]} ]; then + if [ ! -f ${ziplocal[count]} ]; then + echo "Getting ${ziplocal[count]}" ; + if [ "$(uname)" = "Darwin" ]; then + #Mac + curl --output ${ziplocal[count]} ${zipurl[count]} + export cmdstatus=$? + else + wget --quiet --no-hsts ${zipurl[count]} + export cmdstatus=$? + fi + if [ $cmdstatus -eq 0 ]; then + echo "fetch of ${ziplocal[count]} was successful" + fi + fi + if [ -f ${ziplocal[count]} ]; then + echo "unzipping ${ziplocal[count]}" + unzip -p ${ziplocal[count]} ${exepathinzip[count]} >./${exesought[count]} + fi + fi + count=$(( $count + 1 )) + done + + echo "Native DOS executables in lib/djgpp/target:" + ls -l bin/*.exe + # + cd ../../ fi @@ -177,6 +232,8 @@ if [ ! -d "$FONT_LFILE" ]; then fi tar -xvf $FONT_RFILE rm $FONT_RFILE +else + echo "terminus fonts are already available in lib/$FONT_LFILE" fi cd ../ diff --git a/sys/msdos/msdos.c b/sys/msdos/msdos.c index 735e84dd9..8a059008d 100644 --- a/sys/msdos/msdos.c +++ b/sys/msdos/msdos.c @@ -15,7 +15,6 @@ #include "pcvideo.h" #include -#include /* * MS-DOS functions diff --git a/sys/msdos/pcvideo.h b/sys/msdos/pcvideo.h index dfd9fc1c2..1d889a8b2 100644 --- a/sys/msdos/pcvideo.h +++ b/sys/msdos/pcvideo.h @@ -55,6 +55,7 @@ #endif #define GETCURPOS 0x03 /* Get Cursor Position */ +#define SETCURTYP 0x01 /* Set Cursor Type */ #define GETMODE 0x0f /* Get Video Mode */ #define SETMODE 0x00 /* Set Video Mode */ #define SETPAGE 0x05 /* Set Video Page */ @@ -248,6 +249,8 @@ extern void txt_nhbell(void); extern void txt_startup(int *, int *); extern void txt_xputs(const char *, int, int); extern void txt_xputc(char, int); +extern void txt_hide_cursor(void); +extern void txt_show_cursor(void); /* ### vidvga.c ### */ @@ -258,6 +261,8 @@ extern void vga_clear_screen(int); extern void vga_cl_end(int, int); extern void vga_cl_eos(int); extern int vga_detect(void); +extern void vga_hide_cursor(void); +extern void vga_show_cursor(void); #ifdef SIMULATE_CURSOR extern void vga_DrawCursor(void); #endif @@ -272,8 +277,8 @@ extern void vga_update_positionbar(char *); extern void vga_HideCursor(void); #endif extern void vga_Init(void); -extern void vga_tty_end_screen(void); -extern void vga_tty_startup(int *, int *); +extern void vga_term_end_screen(void); +extern void vga_term_startup(int *, int *); extern void vga_xputs(const char *, int, int); extern void vga_xputc(char, int); extern void vga_xputg(const glyph_info *, const glyph_info *); @@ -288,6 +293,8 @@ extern void vesa_clear_screen(int); extern void vesa_cl_end(int, int); extern void vesa_cl_eos(int); extern int vesa_detect(void); +extern void vesa_hide_cursor(void); +extern void vesa_show_cursor(void); #ifdef SIMULATE_CURSOR extern void vesa_DrawCursor(void); #endif @@ -301,8 +308,8 @@ extern void vesa_update_positionbar(char *); extern void vesa_HideCursor(void); #endif extern void vesa_Init(void); -extern void vesa_tty_end_screen(void); -extern void vesa_tty_startup(int *, int *); +extern void vesa_term_end_screen(void); +extern void vesa_term_startup(int *, int *); extern void vesa_xputs(const char *, int, int); extern void vesa_xputc(char, int); extern void vesa_xputg(const glyph_info *, const glyph_info *); diff --git a/sys/msdos/tile2bin.c b/sys/msdos/tile2bin.c index 1fa622d11..0e90935fc 100644 --- a/sys/msdos/tile2bin.c +++ b/sys/msdos/tile2bin.c @@ -18,10 +18,6 @@ #include "pctiles.h" /* #include */ -#ifndef MONITOR_HEAP -#include -#endif -#include #ifdef __GO32__ #include diff --git a/sys/msdos/video.c b/sys/msdos/video.c index e172513ee..916a3518d 100644 --- a/sys/msdos/video.c +++ b/sys/msdos/video.c @@ -14,6 +14,7 @@ */ #include "hack.h" +#include "wintty.h" #ifndef STUBVIDEO #include "pcvideo.h" @@ -88,6 +89,14 @@ get_scr_size(void) txt_get_scr_size(); } +#ifdef ENHANCED_SYMBOLS +void g_pututf8(uint8 *utf8str) +{ + /* not implemented for msdos (yet) */ + nhUse(utf8str); +} +#endif + /* * -------------------------------------------------------------- * The rest of this file is only compiled if NO_TERMS is defined. @@ -96,7 +105,6 @@ get_scr_size(void) #ifdef NO_TERMS -#include #include "wintty.h" #ifdef __GO32__ @@ -138,6 +146,61 @@ boolean traditional = FALSE; /* traditional TTY character mode */ boolean inmap = FALSE; /* in the map window */ char ttycolors[CLR_MAX]; /* also used/set in options.c */ +/* for linkage from wintty.c */ + +void +term_shutdown(void) +{ +} + +#ifdef ASCIIGRAPH +void +graph_on(void) +{ +} + +void +graph_off(void) +{ +} +#endif + +void +term_curs_set(int visibility) +{ + static int vis = -1; + + if (vis == visibility) + return; + + if (!visibility) { + if (!iflags.grmode) { + txt_hide_cursor(); +#ifdef SCREEN_VGA + } else if (iflags.usevga) { + vga_hide_cursor(); +#endif +#ifdef SCREEN_VESA + } else if (iflags.usevesa) { + vesa_hide_cursor(); + } +#endif + } else if (visibility) { + if (!iflags.grmode) { + txt_show_cursor(); +#ifdef SCREEN_VGA + } else if (iflags.usevga) { + vga_show_cursor(); +#endif +#ifdef SCREEN_VESA + } else if (iflags.usevesa) { + vesa_show_cursor(); + } +#endif + } + vis = visibility; +} + void backsp(void) { @@ -293,6 +356,7 @@ term_end_attr(int attr) switch (attr) { case ATR_INVERSE: inversed = 0; + FALLTHROUGH; /*FALLTHRU*/ case ATR_ULINE: case ATR_BOLD: @@ -341,6 +405,7 @@ term_start_attr(int attr) break; case ATR_INVERSE: inversed = 1; + FALLTHROUGH; /*FALLTHRU*/ default: g_attribute = iflags.grmode ? attrib_gr_normal : attrib_text_normal; @@ -406,7 +471,7 @@ tty_delay_output(void) } void -tty_end_screen(void) +term_end_screen(void) { if (!iflags.grmode) { txt_clear_screen(); @@ -415,11 +480,11 @@ tty_end_screen(void) #endif #ifdef SCREEN_VGA } else if (iflags.usevga) { - vga_tty_end_screen(); + vga_term_end_screen(); #endif #ifdef SCREEN_VESA } else if (iflags.usevesa) { - vesa_tty_end_screen(); + vesa_term_end_screen(); #endif } } @@ -437,7 +502,7 @@ tty_number_pad(int state) } void -tty_startup(int *wid, int *hgt) +term_startup(int *wid, int *hgt) { /* code to sense display adapter is required here - MJA */ @@ -451,12 +516,12 @@ tty_startup(int *wid, int *hgt) #ifdef SCREEN_VGA if (iflags.usevga) { - vga_tty_startup(wid, hgt); + vga_term_startup(wid, hgt); } else #endif #ifdef SCREEN_VESA if (iflags.usevesa) { - vesa_tty_startup(wid, hgt); + vesa_term_startup(wid, hgt); } else #endif txt_startup(wid, hgt); @@ -481,7 +546,7 @@ tty_startup(int *wid, int *hgt) } void -tty_start_screen(void) +term_start_screen(void) { #ifdef PC9800 fputs("\033[>1h", stdout); diff --git a/sys/msdos/vidtxt.c b/sys/msdos/vidtxt.c index b89d3616c..990d6d064 100644 --- a/sys/msdos/vidtxt.c +++ b/sys/msdos/vidtxt.c @@ -18,7 +18,6 @@ #include "wintty.h" #include -#include #if defined(_MSC_VER) #if _MSC_VER >= 700 @@ -39,6 +38,14 @@ extern int attrib_gr_normal; /* graphics mode normal attribute */ extern int attrib_text_intense; /* text mode intense attribute */ extern int attrib_gr_intense; /* graphics mode intense attribute */ +#if defined(SCREEN_BIOS) && !defined(PC9800) +static unsigned char cursor_info = 0, + cursor_start_scanline = 6, cursor_end_scanline = 7; + +static void get_cursinfo(unsigned char *start, unsigned char *end, + unsigned char *flg); +#endif + void txt_get_scr_size(void) { @@ -81,6 +88,7 @@ txt_get_scr_size(void) LI = regs.h.dl + 1; CO = regs.h.ah; + #endif /* PC9800 */ } @@ -261,6 +269,9 @@ txt_startup(int *wid, int *hgt) attrib_gr_normal = attrib_text_normal; attrib_gr_intense = attrib_text_intense; g_attribute = attrib_text_normal; /* Give it a starting value */ + get_cursinfo(&cursor_start_scanline, + &cursor_end_scanline, + &cursor_info); } /* @@ -401,6 +412,61 @@ void txt_get_cursor(int *x, int *y) (void) int86(VIDEO_BIOS, ®s, ®s); /* Get Cursor Position */ *x = regs.h.dl; *y = regs.h.dh; + if (!cursor_info) { + cursor_start_scanline = regs.h.ch; + cursor_end_scanline = regs.h.cl; + cursor_info = 1; + } +} + +void txt_hide_cursor(void) +{ + union REGS regs; + + regs.x.dx = 0; + regs.h.ah = SETCURTYP; /* set cursor type */ + regs.h.ch = 0x3F; /* starting scanline */ + regs.h.cl = 0; /* ending scanline */ + regs.x.bx = 0; + (void) int86(VIDEO_BIOS, ®s, ®s); +} + +void txt_show_cursor(void) +{ + union REGS regs; + + regs.x.dx = 0; + regs.h.ah = SETCURTYP; /* set cursor type */ + if (cursor_info) { + regs.h.ch = cursor_start_scanline; /* starting scanline */ + regs.h.cl = cursor_end_scanline; /* ending scanline */ + } else { + regs.h.ch = 6; /* starting scanline */ + regs.h.cl = 7; /* ending scanline */ + } + regs.x.bx = 0; + (void) int86(VIDEO_BIOS, ®s, ®s); +} + +static void +get_cursinfo(uchar *start, uchar *end, uchar *flg) +{ + union REGS regs; + + regs.x.dx = 0; + regs.h.ah = GETCURPOS; /* get cursor position */ + regs.x.cx = 0; + regs.x.bx = 0; + (void) int86(VIDEO_BIOS, ®s, ®s); /* Get Cursor Position */ + + if (regs.h.ch != 0x3f) { + *start = regs.h.ch; + *end = regs.h.cl; + } else { + *start = 6; + *end = 7; + } + *flg = 1; } #endif /* SCREEN_BIOS && !PC9800 */ diff --git a/sys/msdos/vidvesa.c b/sys/msdos/vidvesa.c index 08d92b7a3..8a3423bc0 100644 --- a/sys/msdos/vidvesa.c +++ b/sys/msdos/vidvesa.c @@ -596,14 +596,14 @@ vesa_cl_eos(int cy) } void -vesa_tty_end_screen(void) +vesa_term_end_screen(void) { vesa_clear_screen(BACKGROUND_VESA_COLOR); vesa_SwitchMode(MODETEXT); } void -vesa_tty_startup(int *wid, int *hgt) +vesa_term_startup(int *wid, int *hgt) { /* code to sense display adapter is required here - MJA */ @@ -2042,6 +2042,16 @@ vesa_SetSoftPalette(const struct Pixel *palette) return TRUE; } +void +vesa_hide_cursor(void) +{ +} + +void +vesa_show_cursor(void) +{ +} + #ifdef POSITIONBAR #define PBAR_ROW (LI - 4) diff --git a/sys/msdos/vidvga.c b/sys/msdos/vidvga.c index f2473cfc5..140b816a5 100644 --- a/sys/msdos/vidvga.c +++ b/sys/msdos/vidvga.c @@ -14,7 +14,6 @@ #include "font.h" #include -#include #include "wintty.h" #ifdef __GO32__ @@ -291,14 +290,14 @@ void vga_cl_eos(int cy) } void -vga_tty_end_screen(void) +vga_term_end_screen(void) { vga_clear_screen(BACKGROUND_VGA_COLOR); vga_SwitchMode(MODETEXT); } void -vga_tty_startup(int *wid, int *hgt) +vga_term_startup(int *wid, int *hgt) { /* code to sense display adapter is required here - MJA */ @@ -1160,6 +1159,16 @@ vga_SetPalette(const struct Pixel *p) } } +void +vga_hide_cursor(void) +{ +} + +void +vga_show_cursor(void) +{ +} + /*static unsigned char colorbits[]={0x01,0x02,0x04,0x08}; */ /* wrong */ static unsigned char colorbits[] = { 0x08, 0x04, 0x02, 0x01 }; diff --git a/sys/share/cppregex.cpp b/sys/share/cppregex.cpp index 4d45928c7..9835a1f5c 100644 --- a/sys/share/cppregex.cpp +++ b/sys/share/cppregex.cpp @@ -10,7 +10,7 @@ extern "C" { #include "config.h" #define CPPREGEX_C -//#include "extern.h" +#include "nhregex.h" } // extern "C" diff --git a/sys/share/pcmain.c b/sys/share/pcmain.c index 3e0a4c355..8850e2d82 100644 --- a/sys/share/pcmain.c +++ b/sys/share/pcmain.c @@ -12,8 +12,6 @@ #include #endif -#include - #if !defined(AMIGA) && !defined(__DJGPP__) #include #else @@ -24,12 +22,6 @@ #include /* for getcwd() prototype */ #endif -#if defined(MICRO) || defined(OS2) -ATTRNORETURN void nethack_exit(int) NORETURN; -#else -#define nethack_exit exit -#endif - char *exepath(char *); char orgdir[PATHLEN]; /* also used in pcsys.c, amidos.c */ @@ -437,8 +429,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ raw_print("Cannot create lock file"); } else { svh.hackpid = 1; - if (nhfp->structlevel) - write(nhfp->fd, (genericptr_t) &svh.hackpid, sizeof(svh.hackpid)); + Sfo_int(nhfp, &svh.hackpid, "svh.hackpid"); close_nhfile(nhfp); } diff --git a/sys/share/pcsys.c b/sys/share/pcsys.c index 20b68c3e3..07cab8e42 100644 --- a/sys/share/pcsys.c +++ b/sys/share/pcsys.c @@ -10,7 +10,6 @@ #include "hack.h" #include "wintty.h" -#include #include #if !defined(MSDOS) && !defined(WIN_CE) && !defined(CROSS_TO_AMIGA) #include @@ -28,11 +27,6 @@ #define filesize filesize_nh #endif -#if defined(MICRO) || defined(OS2) -ATTRNORETURN void nethack_exit(int) NORETURN; -#else -#define nethack_exit exit -#endif static void msexit(void); #ifdef MOVERLAY @@ -277,6 +271,7 @@ msexit(void) restore_colors(); #endif wait_synch(); + term_curs_set(1); return; } #endif /* MICRO || OS2 */ diff --git a/sys/share/pctty.c b/sys/share/pctty.c index 6b718d66a..fee616980 100644 --- a/sys/share/pctty.c +++ b/sys/share/pctty.c @@ -38,7 +38,7 @@ settty(const char *s) #if defined(MSDOS) && defined(NO_TERMS) gr_finish(); #endif - end_screen(); + term_end_screen(); if (s) raw_print(s); #if !defined(TOS) @@ -50,7 +50,7 @@ settty(const char *s) void setftty(void) { - start_screen(); + term_start_screen(); } #if defined(TIMED_DELAY) && defined(_MSC_VER) @@ -77,7 +77,7 @@ VA_DECL(const char *, s) VA_INIT(s, const char *); /* error() may get called before tty is initialized */ if (iflags.window_inited) - end_screen(); + term_end_screen(); putchar('\n'); Vprintf(s, VA_ARGS); putchar('\n'); diff --git a/sys/share/pmatchregex.c b/sys/share/pmatchregex.c index 95dc4de11..53c5ef531 100644 --- a/sys/share/pmatchregex.c +++ b/sys/share/pmatchregex.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 pmatchregex.c $NHDT-Date: 1596498285 2020/08/03 23:44:45 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.4 $ */ +/* NetHack 3.7 pmatchregex.c $NHDT-Date: 1737691300 2025/01/23 20:01:40 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.10 $ */ /* Copyright (c) Sean Hunt 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -59,11 +59,11 @@ regex_match(const char *s, struct nhregex *re) void regex_free(struct nhregex *re) { - if (re) { - if (re->pat) - free((genericptr_t) re->pat); - free((genericptr_t) re); - } + assert(re != NULL); /* regex_free() is declared with NONNULLPTR1 */ + + if (re->pat) + free((genericptr_t) re->pat); + free((genericptr_t) re); } /*pmatchregex.c*/ diff --git a/sys/share/unixtty.c b/sys/share/unixtty.c index 44fbadc6a..3dce02269 100644 --- a/sys/share/unixtty.c +++ b/sys/share/unixtty.c @@ -11,6 +11,9 @@ #define NEED_VARARGS #include "hack.h" +#if defined(TTY_GRAPHICS) && !defined(NOTTYGRAPHICS) +#include "wintty.h" +#endif /* * The distinctions here are not BSD - rest but rather USG - rest, as @@ -229,7 +232,10 @@ gettty(void) void settty(const char *s) { - end_screen(); +#ifdef TTY_GRAPHICS + if (WINDOWPORT(tty)) + term_end_screen(); +#endif if (s) raw_print(s); if (STTY(&inittyb) < 0 || STTY2(&inittyb2) < 0) @@ -306,7 +312,11 @@ setftty(void) if (change) setctty(); - start_screen(); + +#ifdef TTY_GRAPHICS + if (WINDOWPORT(tty)) + term_start_screen(); +#endif } void intron(void) /* enable kbd interrupts if enabled when game started */ @@ -480,7 +490,7 @@ RESTORE_WARNING_FORMAT_NONLITERAL #ifdef ENHANCED_SYMBOLS /* - * set in tty_start_screen() and allows + * set in term_start_screen() and allows * OS-specific changes that may be * required for support of utf8. */ diff --git a/sys/unix/Makefile.doc b/sys/unix/Makefile.doc index 92473995b..56d6818c3 100644 --- a/sys/unix/Makefile.doc +++ b/sys/unix/Makefile.doc @@ -11,6 +11,8 @@ NHSROOT=.. MAKEDEFS = ../util/makedefs +#STRIPBS ?= ../util/stripbs + # Which version do we want to build? (XXX These are not used anywhere.) GUIDEBOOK = Guidebook # regular ASCII file #GUIDEBOOK = Guidebook.ps # PostScript file @@ -21,8 +23,9 @@ GUIDEBOOK = Guidebook # regular ASCII file # recognize the option. Sigh. # # col is unnecessary, but harmless, with groff. See grotty(1). -COLCMD = col -bx -#COLCMD = col -b +COLCMD ?= col -bx +#COLCMD ?= col -b +#COLCMD ?= $(STRIPBS) # The command to use to generate a PostScript file # PSCMD = ditroff | psdit @@ -59,8 +62,8 @@ ONEPAGE_PREFORMAT = cat Gbk-1pg-pfx.mn $(GUIDEBOOK_MN) Gbk-1pg-sfx.mn \ | $(NHGREP) | tbl tmac.n - # the basic guidebook -Guidebook : $(GUIDEBOOK_MN) tmac.n tmac.nh $(NEEDMAKEDEFS) - $(GUIDECMD) > Guidebook +Guidebook : $(GUIDEBOOK_MN) tmac.n tmac.nh $(NEEDMAKEDEFS) $(STRIPBS) + -$(GUIDECMD) > Guidebook # Fancier output for those with ditroff, psdit and a PostScript printer. # Could be converted to Guidebook.pdf if tool(s) for that are available. @@ -80,7 +83,10 @@ Guidebook.dvi : $(GUIDEBOOK_TEX) # (note: 'make makedefs', not 'make $(MAKEDEFS)') $(MAKEDEFS) : ../util/makedefs.c ../include/config.h ../src/mdlib.c \ ../util/mdgrep.h - ( cd ../util ; make makedefs ) + ( cd .. ; make makedefs ) + +../util/stripbs: ../util/stripbs.c + ( cd .. ; $(MAKE) stripbs ) GAME = nethack MANDIR ?= /usr/man/man6 diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index 0b3086aa9..695b43f28 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -188,6 +188,10 @@ SYSOBJ = $(TARGETPFX)ioctl.o $(TARGETPFX)unixmain.o $(TARGETPFX)unixtty.o \ #CFLAGS = -O -I../include #LFLAGS = +#fallback values, only if these are not already set +CFLAGS ?= -I../include +LINK ?= $(CC) + AR = ar ARFLAGS = rcs @@ -406,6 +410,10 @@ WINCURSESLIB = -lncurses # # LIBS = +#fallback values, only if these are not already set +WINOBJ ?= $(WINTTYOBJ) +WINLIB ?= $(WINTTYLIB) -lncurses + # make NetHack GAME = nethack # GAME = nethack.prg @@ -518,7 +526,7 @@ HACKCSRC = allmain.c alloc.c apply.c artifact.c attrib.c ball.c bones.c \ nhlua.c nhlsel.c nhlobj.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 sfstruct.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 twitch.c u_init.c utf8map.c \ @@ -560,15 +568,16 @@ CSOURCES = $(HACKCSRC) $(HACKLIBSRC) $(SYSCSRC) $(WINCSRC) $(CHAINSRC) $(GENCSRC # and dgn_file.h, special level & dungeon files. # HACKINCL = align.h artifact.h artilist.h attrib.h botl.h \ - color.h config.h config1.h context.h coord.h decl.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 mfndpos.h \ - micro.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 selvar.h sym.h defsym.h rm.h sp_lev.h \ - spell.h sndprocs.h seffects.h stairs.h sys.h tcap.h timeout.h \ - tradstdc.h trap.h unixconf.h vision.h vmsconf.h wintty.h wincurs.h \ - winX.h winprocs.h wintype.h you.h youprop.h cstd.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 \ + 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 \ + rm.h sp_lev.h spell.h sndprocs.h seffects.h stairs.h sys.h \ + tcap.h timeout.h tradstdc.h trap.h unixconf.h vision.h vmsconf.h \ + wintty.h wincurs.h winX.h winprocs.h wintype.h you.h youprop.h \ + weight.h HSOURCES = $(HACKINCL) dgn_file.h @@ -582,7 +591,7 @@ HOSTOBJ = $(FIRSTOBJ) alloc.o drawing.o HOBJ = $(TARGETPFX)allmain.o $(TARGETPFX)alloc.o \ $(TARGETPFX)apply.o $(TARGETPFX)artifact.o $(TARGETPFX)attrib.o \ $(TARGETPFX)ball.o $(TARGETPFX)bones.o $(TARGETPFX)botl.o \ - $(TARGETPFX)calendar.o $(TARGETPFX)cmd.o \ + $(TARGETPFX)calendar.o $(TARGETPFX)cfgfiles.o $(TARGETPFX)cmd.o \ $(TARGETPFX)coloratt.o $(TARGETPFX)dbridge.o $(TARGETPFX)decl.o \ $(TARGETPFX)detect.o $(TARGETPFX)dig.o $(TARGETPFX)display.o \ $(TARGETPFX)dlb.o $(TARGETPFX)do.o $(TARGETPFX)do_name.o \ @@ -610,7 +619,7 @@ HOBJ = $(TARGETPFX)allmain.o $(TARGETPFX)alloc.o \ $(TARGETPFX)rect.o $(TARGETPFX)region.o $(TARGETPFX)report.o \ $(TARGETPFX)restore.o $(TARGETPFX)rip.o $(TARGETPFX)rnd.o \ $(TARGETPFX)role.o $(TARGETPFX)rumors.o $(TARGETPFX)save.o \ - $(TARGETPFX)selvar.o $(TARGETPFX)sfstruct.o \ + $(TARGETPFX)selvar.o $(TARGETPFX)sfbase.o $(TARGETPFX)sfstruct.o \ $(TARGETPFX)shk.o $(TARGETPFX)shknam.o $(TARGETPFX)sit.o \ $(TARGETPFX)sounds.o $(TARGETPFX)sp_lev.o $(TARGETPFX)spell.o \ $(TARGETPFX)stairs.o $(TARGETPFX)symbols.o $(TARGETPFX)sys.o \ @@ -760,12 +769,13 @@ hacklib.a: hacklib.o ../win/gnome/gn_rip.h: ../win/X11/rip.xpm cp ../win/X11/rip.xpm ../win/gnome/gn_rip.h -$(TARGETPFX)sfstruct.o: sfstruct.c $(HACK_H) +$(TARGETPFX)sfbase.o: sfbase.c $(HACK_H) ../include/sfprocs.h ../include/sfmacros.h # date.c should be recompiled any time any of the source or include code # is modified. +DATECFLAGS = $(TARGET_CFLAGS) $(GITHASH) $(GITBRANCH) $(GITPREFIX) $(TARGETPFX)date.o: date.c $(HACK_H) $(HACKCSRC) $(HOBJ) $(TARGET_HACKLIB) - $(TARGET_CC) $(TARGET_CFLAGS) $(GITHASH) $(GITBRANCH) $(GITPREFIX) -c -o $@ date.c + $(TARGET_CC) $(DATECFLAGS) -c -o $@ date.c # date.h should be remade any time any of the source or include code # is modified. Unfortunately, this would make the contents of this @@ -885,12 +895,13 @@ $(HACK_H): $(CONFIG_H) ../include/align.h ../include/artilist.h \ ../include/nhlua.h ../include/obj.h ../include/objclass.h \ ../include/objects.h ../include/permonst.h ../include/prop.h \ ../include/quest.h ../include/rect.h ../include/region.h \ - ../include/rm.h ../include/seffects.h ../include/selvar.h \ + ../include/rm.h ../include/savefile.h ../include/sfprocs.h \ + ../include/seffects.h ../include/selvar.h \ ../include/skills.h ../include/sndprocs.h ../include/spell.h \ ../include/stairs.h ../include/sym.h ../include/sys.h \ ../include/timeout.h ../include/trap.h ../include/vision.h \ - ../include/winprocs.h ../include/wintype.h ../include/you.h \ - ../include/youprop.h + ../include/weight.h ../include/winprocs.h \ + ../include/wintype.h ../include/you.h ../include/youprop.h touch $(HACK_H) # $(TARGETPFX)cppregex.o: ../sys/share/cppregex.cpp $(CONFIG_H) @@ -1126,6 +1137,7 @@ $(TARGETPFX)ball.o: ball.c $(HACK_H) $(TARGETPFX)bones.o: bones.c $(HACK_H) $(TARGETPFX)botl.o: botl.c $(HACK_H) $(TARGETPFX)calendar.o: calendar.c $(HACK_H) +$(TARGETPFX)cfgfiles.o: cfgfiles.c $(HACK_H) $(TARGETPFX)cmd.o: cmd.c $(HACK_H) ../include/func_tab.h $(TARGETPFX)coloratt.o: coloratt.c $(HACK_H) $(TARGETPFX)dbridge.o: dbridge.c $(HACK_H) @@ -1176,7 +1188,7 @@ $(TARGETPFX)mdlib.o: mdlib.c $(CONFIG_H) ../include/align.h \ ../include/objclass.h ../include/objects.h \ ../include/permonst.h ../include/prop.h ../include/seffects.h \ ../include/skills.h ../include/sndprocs.h ../include/sym.h \ - ../include/wintype.h ../include/you.h + ../include/weight.h ../include/wintype.h ../include/you.h $(TARGETPFX)mhitm.o: mhitm.c $(HACK_H) ../include/artifact.h $(TARGETPFX)mhitu.o: mhitu.c $(HACK_H) ../include/artifact.h $(TARGETPFX)minion.o: minion.c $(HACK_H) @@ -1192,7 +1204,7 @@ $(TARGETPFX)monmove.o: monmove.c $(HACK_H) ../include/artifact.h \ $(TARGETPFX)monst.o: monst.c $(CONFIG_H) ../include/align.h \ ../include/defsym.h ../include/monattk.h ../include/monflag.h \ ../include/monsters.h ../include/permonst.h ../include/sym.h \ - ../include/wintype.h + ../include/weight.h ../include/wintype.h $(TARGETPFX)mplayer.o: mplayer.c $(HACK_H) $(TARGETPFX)mthrowu.o: mthrowu.c $(HACK_H) $(TARGETPFX)muse.o: muse.c $(HACK_H) @@ -1230,7 +1242,7 @@ $(TARGETPFX)role.o: role.c $(HACK_H) $(TARGETPFX)rumors.o: rumors.c $(HACK_H) ../include/dlb.h $(TARGETPFX)save.o: save.c $(HACK_H) $(TARGETPFX)selvar.o: selvar.c $(HACK_H) ../include/sp_lev.h -$(TARGETPFX)sfstruct.o: sfstruct.c $(HACK_H) +$(TARGETPFX)sfstruct.o: sfstruct.c $(HACK_H) ../include/sfprocs.h ../include/sfmacros.h $(TARGETPFX)shk.o: shk.c $(HACK_H) $(TARGETPFX)shknam.o: shknam.c $(HACK_H) $(TARGETPFX)sit.o: sit.c $(HACK_H) ../include/artifact.h diff --git a/sys/unix/Makefile.top b/sys/unix/Makefile.top index c7bb4b524..6e45eafa9 100644 --- a/sys/unix/Makefile.top +++ b/sys/unix/Makefile.top @@ -118,6 +118,8 @@ DAT = $(DATNODLB) $(DATDLB) # $(CHGRP) $(GAMEGRP) $(INSTDIR)/sysconf && \ # chmod $(VARFILEPERM) $(INSTDIR)/sysconf; fi ); +RECOVERBIN = recover + # Lua LUAHEADERS = lib/lua-$(LUA_VERSION)/src LUATESTTARGET = $(LUAHEADERS)/lua.h @@ -148,8 +150,9 @@ $(TOPLUALIB): $(LUATOP)/liblua.a include/nhlua.h: $(TOPLUALIB) echo '/* nhlua.h - generated by top Makefile */' > $@ @echo '#include "../$(LUAHEADERS)/lua.h"' >> $@ - @sed -e '/(lua_error)/!d' -e '/(lua_error)/s/;/ NORETURN;/1' \ - < $(LUAHEADERS)/lua.h >> $@ + @sed -e '/(lua_error)/!d' \ + -e '/(lua_error)/s/LUA_API/ATTRNORETURN LUA_API/1' \ + -e '/(lua_error)/s/;/ NORETURN;/1' < $(LUAHEADERS)/lua.h >> $@ @echo '#include "../$(LUAHEADERS)/lualib.h"' >> $@ @echo '#include "../$(LUAHEADERS)/lauxlib.h"' >> $@ @echo '/*nhlua.h*/' >> $@ @@ -283,7 +286,12 @@ package: $(GAME) recover $(VARDAT) spec_levs # recover can be used when INSURANCE is defined in include/config.h # and the checkpoint option is true recover: $(GAME) - ( cd util ; $(MAKE) recover ) + ( cd util ; $(MAKE) $(RECOVERBIN) ) + +# sfctool can be configured by the sysadmin to convert the savefile +# content format as necessary. +sfctool: $(GAME) + ( cd util ; $(MAKE) sfctool ) dofiles: target=`sed -n \ @@ -394,6 +402,31 @@ fetch-lua-http: tar zxf lua-$(LUA_VERSION).tar.gz && \ rm -f lua-$(LUA_VERSION).tar.gz ) +# +# This is not part of the dependency build hierarchy. +# It requires an explicit "make fetch-docs". +fetch-docs: + @FDroot=https://www.nethack.org; \ + FDvmajor=`awk '/VERSION_MAJOR/{print $$3}' < include/patchlevel.h`; \ + FDvminor=`awk '/VERSION_MINOR/{print $$3}' < include/patchlevel.h`; \ + RDIR="NetHack-$${FDvmajor}.$${FDvminor}"; \ + echo "Fetching directory information for $${RDIR}"; \ + RDOCFILES=`curl --no-progress-meter $${FDroot}/cgi-bin/lsautodocs?$$RDIR`; \ + FDbase="$${FDroot}/autodocs/$${RDIR}"; \ + set -- $${RDOCFILES}; \ + echo "Fetching $$# files"; \ + while [ $$# -gt 0 ]; do \ + echo "Downloading: '$$1' from '$${FDbase}/$$1'"; \ + curl --no-progress-meter -R $${FDbase}/$$1 -o doc/$$1; \ + shift; \ + done + +makedefs: + ( cd util ; $(MAKE) makedefs ) + +stripbs: + ( cd util ; $(MAKE) stripbs ) + # 'make update' can be used to install a revised version after making # customizations or such. Unlike 'make install', it doesn't delete everything # from the target directory to have a clean start. diff --git a/sys/unix/Makefile.utl b/sys/unix/Makefile.utl index 25b1f65e8..84f73b4f6 100644 --- a/sys/unix/Makefile.utl +++ b/sys/unix/Makefile.utl @@ -106,6 +106,9 @@ NHSROOT=.. #CFLAGS = -O -I../include #LFLAGS = +#fallback defaults if not set +CFLAGS ?= -I../include + # -lm required by lua LFLAGS += -lm @@ -118,6 +121,12 @@ LIBS = OBJDIR = ../src +# This is the universal ctags utility which produces the tags in the +# format that util/readtags requires. +# https://github.com/universal-ctags/ctags.git +#CTAGSCMD = universal-ctags +CTAGSCMD = ctags + # if you change this to 1, feedback while building will omit -Dthis -Wthat # -Isomewhere so that each file being compiled is listed on one short line; # it requires support for '$<' in rules with more than one prerequisite @@ -168,6 +177,7 @@ AT_V1 := @ AT = $(AT_V$(QUIETCC)) # Verbosity, end +GAME=nethack # timestamps for primary header files, matching src/Makefile CONFIG_H = ../src/config.h-t HACK_H = ../src/hack.h-t @@ -200,7 +210,7 @@ HACKLIBOBJ = $(OBJDIR)/hacklib.o panic.o MAKEOBJS = makedefs.o $(OMONOBJ) $(ODATE) $(OALLOC) # object files for recovery utility -RECOVOBJS = $(TARGETPFX)recover.o +RECOVOBJS = recover.o recover-version.o # object files for the data librarian DLBOBJS = dlb_main.o $(OBJDIR)/dlb.o $(OALLOC) @@ -220,6 +230,7 @@ TARGET_CXX = $(CXX) TARGET_CXXFLAGS = $(CXXFLAGS) TARGET_AR = $(AR) +# # dependencies for makedefs # makedefs: $(HACKLIB) $(MAKEOBJS) mdgrep.h @@ -277,12 +288,177 @@ lintdgn: # dependencies for recover # -$(TARGETPFX)recover: $(RECOVOBJS) - $(TARGET_CLINK) $(TARGET_LFLAGS) -o recover $(RECOVOBJS) $(LIBS) +recover: $(RECOVOBJS) $(HACKLIB) + $(CLINK) $(LFLAGS) -o recover \ + $(RECOVOBJS) $(HACKLIB) $(LIBS) -$(TARGETPFX)recover.o: recover.c $(CONFIG_H) - $(TARGET_CC) $(TARGET_CFLAGS) $(CSTD) -c recover.c -o $@ +recover.o: recover.c $(CONFIG_H) + $(CC) $(CFLAGS) $(CSTD) -c recover.c -o $@ +recover-version.o: ../src/version.c $(HACK_H) + $(CC) $(CFLAGS) $(CSTD) -DMINIMAL_FOR_RECOVER -c ../src/version.c -o $@ +# +# dependencies for optional sfctool +# +# object files for sfctool utility +SFCTOOLOBJS = $(TARGETPFX)sfctool.o \ + $(TARGETPFX)sf-alloc.o $(TARGETPFX)sfdata.o \ + $(TARGETPFX)sfexpasc.o $(TARGETPFX)sf-cfgfiles.o \ + $(TARGETPFX)sf-date.o $(TARGETPFX)sf-artifact.o \ + $(TARGETPFX)sf-calendar.o $(TARGETPFX)sf-decl.o \ + $(TARGETPFX)sf-dungeon.o $(TARGETPFX)sf-end.o \ + $(TARGETPFX)sf-engrave.o $(TARGETPFX)sf-files.o \ + $(TARGETPFX)sf-light.o $(TARGETPFX)sf-mdlib.o \ + $(TARGETPFX)sf-mkmaze.o $(TARGETPFX)sf-mkroom.o \ + $(TARGETPFX)sf-monst.o $(TARGETPFX)sf-nhlua.o \ + $(TARGETPFX)sf-objects.o $(TARGETPFX)sf-o_init.o \ + $(TARGETPFX)panic.o $(TARGETPFX)sf-region.o \ + $(TARGETPFX)sf-restore.o $(TARGETPFX)sf-rumors.o \ + $(TARGETPFX)sfbase.o $(TARGETPFX)sf-struct.o \ + $(TARGETPFX)strutil.o $(TARGETPFX)sf-sys.o \ + $(TARGETPFX)sf-timeout.o $(TARGETPFX)sf-track.o \ + $(TARGETPFX)sf-version.o $(TARGETPFX)sf-worm.o + +SFCTOOLBIN = sfctool + +SFFLAGS=-DSFCTOOL -DNOPANICTRACE -DNOCRASHREPORT -DNO_CHRONICLE +sfutil: $(SFCTOOLBIN) + @echo '$(SFCTOOLBIN) is up to date.' +$(SFCTOOLBIN): $(SFCTOOLOBJS) $(HACKLIB) + $(TARGET_CLINK) $(TARGET_LFLAGS) -o $@ $(SFCTOOLOBJS) $(HACKLIB) $(TARGET_LIBS) +$(TARGETPFX)sfctool.o: sfctool.c $(HACK_H) ../include/sfprocs.h + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c sfctool.c +$(TARGETPFX)sf-alloc.o: ../src/alloc.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/alloc.c +$(TARGETPFX)sf-calendar.o: ../src/calendar.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/calendar.c +$(TARGETPFX)sf-cfgfiles.o: ../src/cfgfiles.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/cfgfiles.c +$(TARGETPFX)sf-date.o: ../src/date.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/date.c +$(TARGETPFX)sf-decl.o: ../src/decl.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/decl.c +$(TARGETPFX)sf-monst.o: ../src/monst.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/monst.c +$(TARGETPFX)sf-objects.o: ../src/objects.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/objects.c +$(TARGETPFX)sf-panic.o: panic.c $(CONFIG_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c panic.c +$(TARGETPFX)sf-artifact.o: ../src/artifact.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/artifact.c +$(TARGETPFX)sf-dungeon.o: ../src/dungeon.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/dungeon.c +$(TARGETPFX)sf-end.o: ../src/end.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/end.c +$(TARGETPFX)sf-engrave.o: ../src/engrave.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/engrave.c +$(TARGETPFX)sf-files.o: ../src/files.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/files.c +$(TARGETPFX)sf-light.o: ../src/light.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/light.c +$(TARGETPFX)sf-mdlib.o: ../src/mdlib.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/mdlib.c +$(TARGETPFX)sf-mkmaze.o: ../src/mkmaze.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/mkmaze.c +$(TARGETPFX)sf-mkroom.o: ../src/mkroom.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/mkroom.c +$(TARGETPFX)sf-nhlua.o: ../src/nhlua.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/nhlua.c +$(TARGETPFX)sf-o_init.o: ../src/o_init.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/o_init.c +$(TARGETPFX)sf-region.o: ../src/region.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/region.c +$(TARGETPFX)sf-restore.o: ../src/restore.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/restore.c +$(TARGETPFX)sf-rumors.o: ../src/rumors.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/rumors.c +$(TARGETPFX)sfbase.o: ../src/sfbase.c $(HACK_H) ../include/sfmacros.h + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/sfbase.c +$(TARGETPFX)sfdata.o: sfdata.c $(HACK_H) ../include/sfprocs.h ../include/sfproto.h + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c sfdata.c +$(TARGETPFX)sfexpasc.o: sfexpasc.c $(HACK_H) ../include/sfprocs.h \ + ../include/sfproto.h ../include/sfmacros.h + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c sfexpasc.c +$(TARGETPFX)sf-struct.o: ../src/sfstruct.c $(HACK_H) ../include/sfmacros.h + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/sfstruct.c +$(TARGETPFX)strutil.o: ../src/strutil.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/strutil.c +$(TARGETPFX)sf-sys.o: ../src/sys.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/sys.c +$(TARGETPFX)sf-timeout.o: ../src/timeout.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/timeout.c +$(TARGETPFX)sf-track.o: ../src/track.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/track.c +$(TARGETPFX)sf-version.o: ../src/version.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(SFFLAGS) -o $@ -c ../src/version.c +$(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.o: sftags.c $(HACK_H) + $(CC) $(CFLAGS) -c sftags.c +../include/sfproto.h: sf.tags sftags + ./sftags +sfdata.c: sf.tags sftags + ./sftags +# dependencies for sftags +# # +CTAGDEP = ../include/align.h ../include/artifact.h ../include/artilist.h \ + ../include/attrib.h ../include/context.h ../include/coord.h \ + ../include/decl.h ../include/dungeon.h ../include/engrave.h \ + ../include/flag.h ../include/func_tab.h ../include/global.h \ + ../include/hack.h ../include/mextra.h \ + ../include/mkroom.h ../include/monst.h ../include/defsym.h \ + ../include/obj.h ../include/objclass.h ../include/prop.h \ + ../include/quest.h ../include/rect.h ../include/region.h \ + ../include/rm.h ../include/skills.h ../include/spell.h \ + ../include/sys.h ../include/timeout.h ../include/trap.h \ + ../include/you.h ../include/onames.h ../include/wintype.h +# ../include/permonst.h +CTAGSOPT = --language-force=c --sort=no -D"Bitfield(x,n)=unsigned x : n" --excmd=pattern +# +INCL=../include/ +SRC=../src/ +sf.tags: $(CTAGDEP) + $(CTAGSCMD) $(CTAGSOPT) -f $@ $(INCL)align.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)artifact.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(SRC)artifact.c + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)artilist.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)attrib.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(SRC)bones.c + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)context.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)coord.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)decl.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(SRC)decl.c + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)dungeon.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)engrave.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(SRC)engrave.c + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)flag.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)func_tab.h + $(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)mkroom.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)monst.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)defsym.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)obj.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)objclass.h +# $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)permonst.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)prop.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)quest.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)rect.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)region.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)rm.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)skills.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)spell.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)stairs.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)sys.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)timeout.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)trap.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)you.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)onames.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)wintype.h # dependencies for dlb # @@ -292,6 +468,11 @@ dlb: $(DLBOBJS) $(HACKLIB) dlb_main.o: dlb_main.c $(CONFIG_H) ../include/dlb.h $(CC) $(CFLAGS) $(CSTD) -c dlb_main.c -o $@ +stripbs: stripbs.o + $(CC) $(LFLAGS) -o stripbs stripbs.o + +stripbs.o: stripbs.c + $(CC) $(CFLAGS) -c stripbs.c # dependencies for tile utilities # @@ -440,7 +621,10 @@ clean-fixup: clean: clean-fixup -rm -f *.o +# obsolete dgn_comp and lev_comp could be left around from 3.6.x spotless: clean -rm -f makedefs recover dlb $(HACKLIB) -rm -f gif2txt txt2ppm tile2x11 tile2img.ttp xpm2img.ttp \ tilemap tileedit tile2bmp uudecode + -rm -f dgn_comp* dgn_lex.c dgn_yacc.c ../include/dgn_comp.h \ + lev_comp* lev_lex.c lev_yacc.c ../include/lev_comp.h diff --git a/sys/unix/NetHack.xcodeproj/project.pbxproj b/sys/unix/NetHack.xcodeproj/project.pbxproj index cc4463319..0ba3338fb 100644 --- a/sys/unix/NetHack.xcodeproj/project.pbxproj +++ b/sys/unix/NetHack.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 70; objects = { /* Begin PBXBuildFile section */ @@ -100,7 +100,6 @@ 31B8A3C721A238060055BD01 /* monmove.c in Sources */ = {isa = PBXBuildFile; fileRef = 31B8A36121A238040055BD01 /* monmove.c */; }; 31B8A3C821A238060055BD01 /* options.c in Sources */ = {isa = PBXBuildFile; fileRef = 31B8A36221A238040055BD01 /* options.c */; }; 31B8A3C921A238060055BD01 /* sounds.c in Sources */ = {isa = PBXBuildFile; fileRef = 31B8A36321A238040055BD01 /* sounds.c */; }; - 31B8A3CA21A238060055BD01 /* hacklib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31B8A36421A238040055BD01 /* hacklib.c */; }; 31B8A3CB21A238060055BD01 /* alloc.c in Sources */ = {isa = PBXBuildFile; fileRef = 31B8A36521A238040055BD01 /* alloc.c */; }; 31B8A3CC21A238060055BD01 /* pickup.c in Sources */ = {isa = PBXBuildFile; fileRef = 31B8A36621A238040055BD01 /* pickup.c */; }; 31B8A3CD21A238060055BD01 /* write.c in Sources */ = {isa = PBXBuildFile; fileRef = 31B8A36721A238040055BD01 /* write.c */; }; @@ -164,6 +163,7 @@ 5462D14823E7B19200969423 /* insight.c in Sources */ = {isa = PBXBuildFile; fileRef = 5462D14723E7B19200969423 /* insight.c */; }; 5493735A277AAE830031FE02 /* alloc.c in Sources */ = {isa = PBXBuildFile; fileRef = 31B8A36521A238040055BD01 /* alloc.c */; }; 54A3D3EC282C55A900143F8C /* utf8map.c in Sources */ = {isa = PBXBuildFile; fileRef = 54A3D3EB282C55A900143F8C /* utf8map.c */; }; + 54BD340D2D8B70350073C484 /* hacklib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31B8A36421A238040055BD01 /* hacklib.c */; }; 54FB2B4B246310A600397C0E /* symbols.c in Sources */ = {isa = PBXBuildFile; fileRef = 54FB2B4A246310A600397C0E /* symbols.c */; }; 54FCE8292223261F00F393C8 /* isaac64.c in Sources */ = {isa = PBXBuildFile; fileRef = 54FCE8282223261F00F393C8 /* isaac64.c */; }; BAB57DB527C1C3E200FCF150 /* libnhlua.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BAE8010A27B97760002B3786 /* libnhlua.a */; }; @@ -202,6 +202,12 @@ 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 */; }; + 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 */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -240,13 +246,6 @@ remoteGlobalIDString = 3189577E21A1FDA400FB2ABE; remoteInfo = makedefs; }; - BAE8011027B9865F002B3786 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 3189576921A1FCC100FB2ABE /* Project object */; - proxyType = 1; - remoteGlobalIDString = BAE8010927B97760002B3786; - remoteInfo = nhlua.a; - }; BAE8011227B9996A002B3786 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 3189576921A1FCC100FB2ABE /* Project object */; @@ -254,6 +253,20 @@ remoteGlobalIDString = BAE8010927B97760002B3786; remoteInfo = nhlua.a; }; + F5457B222DED148700039D83 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 3189576921A1FCC100FB2ABE /* Project object */; + proxyType = 1; + remoteGlobalIDString = F5457B1B2DED143E00039D83; + remoteInfo = hacklib; + }; + F5457B242DED149500039D83 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 3189576921A1FCC100FB2ABE /* Project object */; + proxyType = 1; + remoteGlobalIDString = F5457B1B2DED143E00039D83; + remoteInfo = hacklib; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -528,7 +541,6 @@ 544768B523995488004B9739 /* nhlua.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nhlua.h; path = ../../include/nhlua.h; sourceTree = ""; }; 544768B923995BB7004B9739 /* liblua.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = liblua.a; path = ../../lib/lua/liblua.a; sourceTree = ""; }; 5462D14723E7B19200969423 /* insight.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = insight.c; path = ../../src/insight.c; sourceTree = ""; }; - 546A4E5A29AAB2DA00E02495 /* seffects.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = seffects.h; sourceTree = ""; }; 548FB9F9297F2B03000D04CF /* sndprocs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = sndprocs.h; path = ../../include/sndprocs.h; sourceTree = ""; }; 548FB9FA297F2BBD000D04CF /* optlist.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = optlist.h; path = ../../include/optlist.h; sourceTree = ""; }; 548FB9FB297F2BBD000D04CF /* hacklib.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = hacklib.h; path = ../../include/hacklib.h; sourceTree = ""; }; @@ -540,6 +552,12 @@ 548FBA01297F2BBD000D04CF /* monsters.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = monsters.h; path = ../../include/monsters.h; sourceTree = ""; }; 54A3D3EB282C55A900143F8C /* utf8map.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = utf8map.c; path = ../../src/utf8map.c; sourceTree = ""; }; 54AEB885297EE7C4005F1B13 /* macsound.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = macsound.m; path = ../../sound/macsound/macsound.m; sourceTree = ""; }; + 54BD340E2D8B711E0073C484 /* nhregex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = nhregex.h; path = ../../include/nhregex.h; sourceTree = ""; }; + 54BD340F2D8B711E0073C484 /* seffects.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = seffects.h; path = ../../include/seffects.h; sourceTree = ""; }; + 54BD34102D8B711E0073C484 /* selvar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = selvar.h; path = ../../include/selvar.h; sourceTree = ""; }; + 54BD34112D8B711E0073C484 /* stairs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = stairs.h; path = ../../include/stairs.h; sourceTree = ""; }; + 54BD34122D8B711E0073C484 /* weight.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = weight.h; path = ../../include/weight.h; sourceTree = ""; }; + 54EBA6D22DDED3F100CD20EC /* cfgfiles.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = cfgfiles.c; path = ../../src/cfgfiles.c; sourceTree = ""; }; 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; }; @@ -577,13 +595,55 @@ 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 = ""; }; + 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 = ""; }; + F515A6F72DED0916006E1F63 /* sys.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = sys.c; path = ../../src/sys.c; sourceTree = ""; }; + F515A6F82DED0917006E1F63 /* calendar.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = calendar.c; path = ../../src/calendar.c; sourceTree = ""; }; + F515A6F92DED0917006E1F63 /* sfstruct.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = sfstruct.c; path = ../../src/sfstruct.c; sourceTree = ""; }; + F515A6FA2DED0917006E1F63 /* engrave.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = engrave.c; path = ../../src/engrave.c; sourceTree = ""; }; + F515A6FB2DED0917006E1F63 /* mdlib.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = mdlib.c; path = ../../src/mdlib.c; sourceTree = ""; }; + F515A6FC2DED0917006E1F63 /* o_init.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = o_init.c; path = ../../src/o_init.c; sourceTree = ""; }; + F515A6FD2DED0917006E1F63 /* mkmaze.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = mkmaze.c; path = ../../src/mkmaze.c; sourceTree = ""; }; + F515A6FE2DED0917006E1F63 /* version.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = version.c; path = ../../src/version.c; sourceTree = ""; }; + F515A6FF2DED0918006E1F63 /* alloc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = alloc.c; path = ../../src/alloc.c; sourceTree = ""; }; + F515A7002DED0918006E1F63 /* artifact.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = artifact.c; path = ../../src/artifact.c; sourceTree = ""; }; + F515A7012DED0918006E1F63 /* cfgfiles.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = cfgfiles.c; path = ../../src/cfgfiles.c; sourceTree = ""; }; + F515A7022DED0918006E1F63 /* nhlua.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = nhlua.c; path = ../../src/nhlua.c; sourceTree = ""; }; + F515A7032DED0918006E1F63 /* objects.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = objects.c; path = ../../src/objects.c; sourceTree = ""; }; + F515A7042DED0918006E1F63 /* strutil.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = strutil.c; path = ../../src/strutil.c; sourceTree = ""; }; + F515A7052DED0919006E1F63 /* decl.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = decl.c; path = ../../src/decl.c; sourceTree = ""; }; + F515A7062DED0919006E1F63 /* mkroom.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = mkroom.c; path = ../../src/mkroom.c; sourceTree = ""; }; + F515A7072DED0919006E1F63 /* rumors.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = rumors.c; path = ../../src/rumors.c; sourceTree = ""; }; + F515A7082DED0919006E1F63 /* monst.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = monst.c; path = ../../src/monst.c; sourceTree = ""; }; + F515A7092DED0919006E1F63 /* files.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = files.c; path = ../../src/files.c; sourceTree = ""; }; + F515A70A2DED091A006E1F63 /* restore.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = restore.c; path = ../../src/restore.c; sourceTree = ""; }; + F515A70B2DED091A006E1F63 /* end.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = end.c; path = ../../src/end.c; sourceTree = ""; }; + F515A70C2DED091A006E1F63 /* worm.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = worm.c; path = ../../src/worm.c; sourceTree = ""; }; + F515A7252DED0978006E1F63 /* sfexpasc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = sfexpasc.c; path = ../../util/sfexpasc.c; sourceTree = ""; }; + F515A7262DED0978006E1F63 /* panic.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = panic.c; path = ../../util/panic.c; sourceTree = ""; }; + F515A72A2DED0A7C006E1F63 /* sftags.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = sftags.c; path = ../../util/sftags.c; sourceTree = ""; }; + F515A73B2DED0B47006E1F63 /* hacklib.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = hacklib.c; path = ../../src/hacklib.c; sourceTree = ""; }; + F5457B1C2DED143E00039D83 /* libhacklib.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libhacklib.a; sourceTree = BUILT_PRODUCTS_DIR; }; + F5457B202DED146B00039D83 /* hacklib.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = hacklib.c; path = ../../src/hacklib.c; sourceTree = ""; }; + F54C1AE12E43D22A006CAA8E /* nhmd4.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = nhmd4.h; path = ../../include/include/nhmd4.h; sourceTree = ""; }; + F54C1AE22E43D22A006CAA8E /* savefile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = savefile.h; path = ../../include/include/savefile.h; sourceTree = ""; }; + F54C1AE32E43D22A006CAA8E /* sfmacros.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = sfmacros.h; path = ../../include/include/sfmacros.h; sourceTree = ""; }; + 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 = ""; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedRootGroup section */ + F515A7322DED0B08006E1F63 /* hacklib */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = hacklib; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ 3189576E21A1FCC100FB2ABE /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + F5457B2C2DED16F200039D83 /* libhacklib.a in Frameworks */, BAB57DB527C1C3E200FCF150 /* libnhlua.a in Frameworks */, 31B8A41721A243E80055BD01 /* libncurses.tbd in Frameworks */, ); @@ -593,6 +653,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + F5457B2D2DED171800039D83 /* libhacklib.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -617,12 +678,51 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F5457B1A2DED143E00039D83 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 3189576821A1FCC100FB2ABE = { isa = PBXGroup; children = ( + F515A73B2DED0B47006E1F63 /* hacklib.c */, + F5457B202DED146B00039D83 /* hacklib.c */, + F515A72A2DED0A7C006E1F63 /* sftags.c */, + F515A7262DED0978006E1F63 /* panic.c */, + F515A7252DED0978006E1F63 /* sfexpasc.c */, + F515A6FF2DED0918006E1F63 /* alloc.c */, + F515A7002DED0918006E1F63 /* artifact.c */, + F515A6F82DED0917006E1F63 /* calendar.c */, + F515A7012DED0918006E1F63 /* cfgfiles.c */, + F515A6F52DED0916006E1F63 /* date.c */, + F515A7052DED0919006E1F63 /* decl.c */, + F515A70B2DED091A006E1F63 /* end.c */, + F515A6FA2DED0917006E1F63 /* engrave.c */, + F515A7092DED0919006E1F63 /* files.c */, + F515A6FB2DED0917006E1F63 /* mdlib.c */, + F515A6FD2DED0917006E1F63 /* mkmaze.c */, + F515A7062DED0919006E1F63 /* mkroom.c */, + F515A7082DED0919006E1F63 /* monst.c */, + F515A7022DED0918006E1F63 /* nhlua.c */, + F515A6FC2DED0917006E1F63 /* o_init.c */, + F515A7032DED0918006E1F63 /* objects.c */, + F515A70A2DED091A006E1F63 /* restore.c */, + F515A7072DED0919006E1F63 /* rumors.c */, + F515A6F62DED0916006E1F63 /* sfbase.c */, + F515A6F92DED0917006E1F63 /* sfstruct.c */, + F515A7042DED0918006E1F63 /* strutil.c */, + F515A6F72DED0916006E1F63 /* sys.c */, + F515A6FE2DED0917006E1F63 /* version.c */, + F515A70C2DED091A006E1F63 /* worm.c */, + F515A6F32DED074D006E1F63 /* sfctool.c */, + F5857AA52DED03BB00A8CB4F /* sfbase.c */, + F5857AA32DED032C00A8CB4F /* cfgfiles.c */, 54AEB886297EE7E9005F1B13 /* sound */, 54AEB885297EE7C4005F1B13 /* macsound.m */, BAE8015827B99D44002B3786 /* nhlualib */, @@ -632,6 +732,7 @@ 3189579621A2046700FB2ABE /* include */, 3189579321A200EC00FB2ABE /* util */, 3189578C21A1FF8200FB2ABE /* src */, + F515A7322DED0B08006E1F63 /* hacklib */, 3189577221A1FCC100FB2ABE /* Products */, 31B8A41421A243CB0055BD01 /* Frameworks */, ); @@ -645,6 +746,7 @@ 31B8A44A21A26A4B0055BD01 /* recover */, 31B8A45721A26A970055BD01 /* dlb */, BAE8010A27B97760002B3786 /* libnhlua.a */, + F5457B1C2DED143E00039D83 /* libhacklib.a */, ); name = Products; sourceTree = ""; @@ -652,6 +754,7 @@ 3189578C21A1FF8200FB2ABE /* src */ = { isa = PBXGroup; children = ( + 54EBA6D22DDED3F100CD20EC /* cfgfiles.c */, 056E43BB2C810EE800FD1F52 /* calendar.c */, 056E43BC2C810EE800FD1F52 /* coloratt.c */, 056E43BD2C810EE800FD1F52 /* getpos.c */, @@ -797,18 +900,10 @@ 3189579621A2046700FB2ABE /* include */ = { isa = PBXGroup; children = ( - 546A4E5A29AAB2DA00E02495 /* seffects.h */, - 548FB9FD297F2BBD000D04CF /* defsym.h */, - 548FB9FE297F2BBD000D04CF /* fnamesiz.h */, - 548FB9FB297F2BBD000D04CF /* hacklib.h */, - 548FBA01297F2BBD000D04CF /* monsters.h */, - 548FB9FF297F2BBD000D04CF /* objects.h */, - 548FB9FA297F2BBD000D04CF /* optlist.h */, - 548FBA00297F2BBD000D04CF /* tile.h */, - 548FB9FC297F2BBD000D04CF /* warnings.h */, - 548FB9F9297F2B03000D04CF /* sndprocs.h */, - 544768B523995488004B9739 /* nhlua.h */, - 544768B423995447004B9739 /* isaac64.h */, + F54C1AE12E43D22A006CAA8E /* nhmd4.h */, + F54C1AE22E43D22A006CAA8E /* savefile.h */, + F54C1AE32E43D22A006CAA8E /* sfmacros.h */, + F54C1AE42E43D22A006CAA8E /* sfprocs.h */, 3186A3B721A4B0FD0052BF02 /* align.h */, 3186A38821A4B0FB0052BF02 /* artifact.h */, 3186A3AB21A4B0FD0052BF02 /* artilist.h */, @@ -819,8 +914,10 @@ 3186A3C621A4B0FE0052BF02 /* config1.h */, 3186A37121A4B0FA0052BF02 /* context.h */, 3186A3C521A4B0FE0052BF02 /* coord.h */, + 3186A38A21A4B0FB0052BF02 /* cstd.h */, 3186A3B021A4B0FD0052BF02 /* date.h */, 3186A3D021A4B0FE0052BF02 /* decl.h */, + 548FB9FD297F2BBD000D04CF /* defsym.h */, 3186A3A021A4B0FD0052BF02 /* dgn_file.h */, 3186A3A921A4B0FD0052BF02 /* display.h */, 3186A38E21A4B0FC0052BF02 /* dlb.h */, @@ -828,10 +925,13 @@ 3186A3A221A4B0FD0052BF02 /* engrave.h */, 3186A37021A4B0FA0052BF02 /* extern.h */, 3186A3B521A4B0FD0052BF02 /* flag.h */, + 548FB9FE297F2BBD000D04CF /* fnamesiz.h */, 3186A37321A4B0FA0052BF02 /* func_tab.h */, 3186A3C121A4B0FD0052BF02 /* global.h */, 3186A3A521A4B0FD0052BF02 /* hack.h */, + 548FB9FB297F2BBD000D04CF /* hacklib.h */, 3186A37A21A4B0FA0052BF02 /* integer.h */, + 544768B423995447004B9739 /* isaac64.h */, 3186A3BC21A4B0FD0052BF02 /* lint.h */, 3186A3B821A4B0FD0052BF02 /* mail.h */, 3186A38521A4B0FB0052BF02 /* mextra.h */, @@ -842,10 +942,13 @@ 3186A3C821A4B0FE0052BF02 /* mondata.h */, 3186A38F21A4B0FC0052BF02 /* monflag.h */, 3186A3BB21A4B0FD0052BF02 /* monst.h */, - 3186A38421A4B0FB0052BF02 /* sym.h */, - 3186A39B21A4B0FD0052BF02 /* windconf.h */, + 548FBA01297F2BBD000D04CF /* monsters.h */, + 544768B523995488004B9739 /* nhlua.h */, + 54BD340E2D8B711E0073C484 /* nhregex.h */, 3186A39521A4B0FC0052BF02 /* obj.h */, 3186A3A821A4B0FD0052BF02 /* objclass.h */, + 548FB9FF297F2BBD000D04CF /* objects.h */, + 548FB9FA297F2BBD000D04CF /* optlist.h */, 3186A37521A4B0FA0052BF02 /* patchlevel.h */, 3186A38121A4B0FB0052BF02 /* pcconf.h */, 3186A38321A4B0FB0052BF02 /* permonst.h */, @@ -854,12 +957,17 @@ 3186A37C21A4B0FA0052BF02 /* rect.h */, 3186A37B21A4B0FA0052BF02 /* region.h */, 3186A39721A4B0FC0052BF02 /* rm.h */, + 54BD340F2D8B711E0073C484 /* seffects.h */, + 54BD34102D8B711E0073C484 /* selvar.h */, 3186A38221A4B0FB0052BF02 /* skills.h */, + 548FB9F9297F2B03000D04CF /* sndprocs.h */, 3186A3B621A4B0FD0052BF02 /* sp_lev.h */, 3186A3A321A4B0FD0052BF02 /* spell.h */, + 54BD34112D8B711E0073C484 /* stairs.h */, + 3186A38421A4B0FB0052BF02 /* sym.h */, 3186A3CB21A4B0FE0052BF02 /* sys.h */, - 3186A38A21A4B0FB0052BF02 /* cstd.h */, 3186A3C321A4B0FE0052BF02 /* tcap.h */, + 548FBA00297F2BBD000D04CF /* tile.h */, 3186A3A121A4B0FD0052BF02 /* tile2x11.h */, 3186A39421A4B0FC0052BF02 /* tileset.h */, 3186A3CA21A4B0FE0052BF02 /* timeout.h */, @@ -868,8 +976,11 @@ 3186A3AD21A4B0FD0052BF02 /* unixconf.h */, 3186A37E21A4B0FA0052BF02 /* vision.h */, 3186A3BE21A4B0FD0052BF02 /* vmsconf.h */, + 548FB9FC297F2BBD000D04CF /* warnings.h */, + 54BD34122D8B711E0073C484 /* weight.h */, 3186A3AA21A4B0FD0052BF02 /* winami.h */, 3186A37621A4B0FA0052BF02 /* wincurs.h */, + 3186A39B21A4B0FD0052BF02 /* windconf.h */, 3186A36F21A4B0FA0052BF02 /* winprocs.h */, 3186A39A21A4B0FD0052BF02 /* wintty.h */, 3186A3C021A4B0FD0052BF02 /* wintype.h */, @@ -1021,6 +1132,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F5457B182DED143E00039D83 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ @@ -1039,6 +1157,7 @@ buildRules = ( ); dependencies = ( + F5457B232DED148700039D83 /* PBXTargetDependency */, BAE8011327B9996A002B3786 /* PBXTargetDependency */, 31B8A31421A2355C0055BD01 /* PBXTargetDependency */, 3192867621A3AAFE00325BEB /* PBXTargetDependency */, @@ -1067,7 +1186,7 @@ buildRules = ( ); dependencies = ( - BAE8011127B9865F002B3786 /* PBXTargetDependency */, + F5457B252DED149500039D83 /* PBXTargetDependency */, ); name = makedefs; productName = makedefs; @@ -1134,6 +1253,25 @@ productReference = BAE8010A27B97760002B3786 /* libnhlua.a */; productType = "com.apple.product-type.library.static"; }; + F5457B1B2DED143E00039D83 /* hacklib */ = { + isa = PBXNativeTarget; + buildConfigurationList = F5457B1D2DED143E00039D83 /* Build configuration list for PBXNativeTarget "hacklib" */; + buildPhases = ( + F5457B182DED143E00039D83 /* Headers */, + F5457B192DED143E00039D83 /* Sources */, + F5457B1A2DED143E00039D83 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = hacklib; + packageProductDependencies = ( + ); + productName = hacklib; + productReference = F5457B1C2DED143E00039D83 /* libhacklib.a */; + productType = "com.apple.product-type.library.static"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -1158,6 +1296,9 @@ BAE8010927B97760002B3786 = { CreatedOnToolsVersion = 13.2.1; }; + F5457B1B2DED143E00039D83 = { + CreatedOnToolsVersion = 16.4; + }; }; }; buildConfigurationList = 3189576C21A1FCC100FB2ABE /* Build configuration list for PBXProject "NetHack" */; @@ -1177,6 +1318,7 @@ 3189577E21A1FDA400FB2ABE /* makedefs */, 31B8A44921A26A4B0055BD01 /* recover */, 31B8A45621A26A970055BD01 /* dlb */, + F5457B1B2DED143E00039D83 /* hacklib */, BAE8010927B97760002B3786 /* nhlua */, ); }; @@ -1196,6 +1338,7 @@ outputFileListPaths = ( ); outputPaths = ( + "${NH_LIB_DIR}/libnhlua.a", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -1222,6 +1365,7 @@ }; 059660C22C80B0FD00398EDE /* Codesign dlb */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -1251,6 +1395,7 @@ outputFileListPaths = ( ); outputPaths = ( + "$(NH_UTIL_DIR}/recover", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -1362,7 +1507,7 @@ }; 317E7C5421A3804400F6E4E5 /* Build Guidebook */ = { isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; + buildActionMask = 8; files = ( ); inputFileListPaths = ( @@ -1379,7 +1524,7 @@ outputPaths = ( "$(NH_DOC_DIR)/Guidebook", ); - runOnlyForDeploymentPostprocessing = 0; + runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; shellScript = "cd \"${NH_DOC_DIR}\"\ncat Guidebook.mn | ${NH_UTIL_DIR}/makedefs --grep --input - --output - | tbl tmac.n - | nroff -c -Tascii | col -bx > Guidebook\n"; }; @@ -1423,6 +1568,8 @@ "$(NH_DAT_DIR)/Bar-strt.lua", "$(NH_DAT_DIR)/bigrm-1.lua", "$(NH_DAT_DIR)/bigrm-10.lua", + "$(NH_DAT_DIR)/bigrm-11.lua", + "$(NH_DAT_DIR)/bigrm-12.lua", "$(NH_DAT_DIR)/bigrm-2.lua", "$(NH_DAT_DIR)/bigrm-3.lua", "$(NH_DAT_DIR)/bigrm-4.lua", @@ -1705,6 +1852,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F5857AA62DED03BB00A8CB4F /* sfbase.c in Sources */, + F5857AA42DED032D00A8CB4F /* cfgfiles.c in Sources */, BAE8015A27B9C872002B3786 /* date.c in Sources */, 54435B52247999CB00804CB3 /* nhlobj.c in Sources */, 31B8A3BC21A238060055BD01 /* eat.c in Sources */, @@ -1809,7 +1958,6 @@ 31B8A41121A23EEC0055BD01 /* cursstat.c in Sources */, 31B8A3E021A238060055BD01 /* wield.c in Sources */, 31B8A41821A2448C0055BD01 /* monst.c in Sources */, - 31B8A3CA21A238060055BD01 /* hacklib.c in Sources */, 31B8A3A121A238060055BD01 /* rip.c in Sources */, 31B8A39321A238060055BD01 /* were.c in Sources */, 31B8A3EF21A23D420055BD01 /* ioctl.c in Sources */, @@ -1872,6 +2020,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F5857AA22DED026700A8CB4F /* version.c in Sources */, + 54BD340D2D8B70350073C484 /* hacklib.c in Sources */, 31B8A45221A26A750055BD01 /* recover.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1929,6 +2079,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F5457B192DED143E00039D83 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F5457B212DED146B00039D83 /* hacklib.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -1957,16 +2115,21 @@ target = 3189577E21A1FDA400FB2ABE /* makedefs */; targetProxy = 31B8A31321A2355C0055BD01 /* PBXContainerItemProxy */; }; - BAE8011127B9865F002B3786 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = BAE8010927B97760002B3786 /* nhlua */; - targetProxy = BAE8011027B9865F002B3786 /* PBXContainerItemProxy */; - }; BAE8011327B9996A002B3786 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = BAE8010927B97760002B3786 /* nhlua */; targetProxy = BAE8011227B9996A002B3786 /* PBXContainerItemProxy */; }; + F5457B232DED148700039D83 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F5457B1B2DED143E00039D83 /* hacklib */; + targetProxy = F5457B222DED148700039D83 /* PBXContainerItemProxy */; + }; + F5457B252DED149500039D83 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F5457B1B2DED143E00039D83 /* hacklib */; + targetProxy = F5457B242DED149500039D83 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -2144,10 +2307,12 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = 5J8MP36Y73; GCC_C_LANGUAGE_STANDARD = c99; INSTALL_PATH = "$(NH_INSTALL_DIR)"; MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( + "-Wno-ambiguous-macro", "-DNOMAIL", "-DNOTPARMDECL", "-DDEFAULT_WINDOW_SYS=\\\"tty\\\"", @@ -2175,10 +2340,12 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = 5J8MP36Y73; GCC_C_LANGUAGE_STANDARD = c99; INSTALL_PATH = "$(NH_INSTALL_DIR)"; MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( + "-Wno-ambiguous-macro", "-DNOMAIL", "-DNOTPARMDECL", "-DDEFAULT_WINDOW_SYS=\\\"tty\\\"", @@ -2226,6 +2393,22 @@ CODE_SIGN_STYLE = Automatic; DEAD_CODE_STRIPPING = YES; MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + OTHER_CFLAGS = ( + "-DMINIMAL_FOR_RECOVER", + "-DNOMAIL", + "-DNOTPARMDECL", + "-DDEFAULT_WINDOW_SYS=\\\"tty\\\"", + "-DDLB", + "-DGREPPATH=\\\"/usr/bin/grep\\\"", + "-DSYSCF", + "-DSYSCF_FILE=\\\"$(NH_INSTALL_DIR)/sysconf\\\"", + "-DHACKDIR=\\\"$(NH_INSTALL_DIR)\\\"", + "-DSECURE", + "-DCURSES_GRAPHICS", + "-DSND_LIB_MACSOUND", + "-DSND_SOUNDEFFECTS_AUTOMAP", + "-DUSER_SOUNDS", + ); PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -2237,6 +2420,19 @@ CODE_SIGN_STYLE = Automatic; DEAD_CODE_STRIPPING = YES; MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + OTHER_CFLAGS = ( + "-DMINIMAL_FOR_RECOVER", + "-DNOMAIL", + "-DNOTPARMDECL", + "-DDEFAULT_WINDOW_SYS=\\\"tty\\\"", + "-DDLB", + "-DGREPPATH=\\\"/usr/bin/grep\\\"", + "-DSYSCF", + "-DSYSCF_FILE=\\\"$(NH_INSTALL_DIR)/sysconf\\\"", + "-DHACKDIR=\\\"$(NH_INSTALL_DIR)\\\"", + "-DSECURE", + "-DCURSES_GRAPHICS", + ); PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -2274,7 +2470,8 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CODE_SIGN_STYLE = Automatic; DEAD_CODE_STRIPPING = YES; - DEVELOPMENT_TEAM = 6978C4Q2VB; + DEVELOPMENT_TEAM = 5J8MP36Y73; + ENABLE_USER_SCRIPT_SANDBOXING = NO; EXECUTABLE_PREFIX = lib; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; @@ -2310,7 +2507,8 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CODE_SIGN_STYLE = Automatic; DEAD_CODE_STRIPPING = YES; - DEVELOPMENT_TEAM = 6978C4Q2VB; + DEVELOPMENT_TEAM = 5J8MP36Y73; + ENABLE_USER_SCRIPT_SANDBOXING = NO; EXECUTABLE_PREFIX = lib; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; @@ -2335,6 +2533,52 @@ }; name = Release; }; + F5457B1E2DED143E00039D83 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_COMMA = NO; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 5J8MP36Y73; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + EXECUTABLE_PREFIX = lib; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 15.5; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + F5457B1F2DED143E00039D83 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_COMMA = NO; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 5J8MP36Y73; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + EXECUTABLE_PREFIX = lib; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 15.5; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -2392,6 +2636,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + F5457B1D2DED143E00039D83 /* Build configuration list for PBXNativeTarget "hacklib" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F5457B1E2DED143E00039D83 /* Debug */, + F5457B1F2DED143E00039D83 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 3189576921A1FCC100FB2ABE /* Project object */; diff --git a/sys/unix/README-hints b/sys/unix/README-hints index 5a531efbd..9c5f06934 100644 --- a/sys/unix/README-hints +++ b/sys/unix/README-hints @@ -26,7 +26,7 @@ cases to include support for multiple features. Some of the options require prerequisite packages to be available on your build machine. make WANT_WIN_TTY=1 Include support for the TTY interface (default). -make NANT_WIN_CURSES=1 Include support for the curses interface. +make WANT_WIN_CURSES=1 Include support for the curses interface. make WANT_WIN_X11=1 Include support for the X11 interface. make WANT_WIN_Qt=1 Include support for Qt interface, defaults to Qt5. make WANT_WIN_QT5=1 Include support for Qt5 interface. @@ -45,6 +45,11 @@ make WANT_DEFAULT=X11 Make X11 the default interface if multiple window make WANT_DEFAULT=Qt Make Qt the default interface if multiple window interfaces were specified. +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 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. @@ -72,3 +77,4 @@ make WANT_SOURCE_INSTALL=1 Place the results of the install/update portion tree, rather than in a system-wide shared area. make VIEWDEPRECATIONS=1 Turn off the suppression of -Wdeprecated and -Wdeprecated-declarations warnings + diff --git a/sys/unix/hints/include/compiler.370 b/sys/unix/hints/include/compiler.370 index 1a0c3b990..5ef6f801e 100755 --- a/sys/unix/hints/include/compiler.370 +++ b/sys/unix/hints/include/compiler.370 @@ -26,6 +26,7 @@ CCFLAGS = -g # If these are set on entry, preparation for C++ compiles is affected. # CPLUSPLUS_NEEDED = 1 C++ compile bits included # CPLUSPLUS_NEED17 = 1 C++ -std=c++17 (at least) +# CPLUSPLUS_NEED20 = 1 C++ -std=c++20 (at least) # CPLUSPLUS_NEED_DEPSUPPRESS = 1 C++ -Wno-deprecated-copy, # -Wno-deprecated-declarations @@ -78,20 +79,28 @@ CXX=g++ -std=gnu++11 GCCGTEQ9 := $(shell expr `$(CC) -dumpversion | cut -f1 -d.` \>= 9) GCCGTEQ11 := $(shell expr `$(CC) -dumpversion | cut -f1 -d.` \>= 11) GCCGTEQ12 := $(shell expr `$(CC) -dumpversion | cut -f1 -d.` \>= 12) +GCCGTEQ14 := $(shell expr `$(CC) -dumpversion | cut -f1 -d.` \>= 14) ifeq "$(GCCGTEQ9)" "1" # flags present in gcc version greater than or equal to 9 can go here CFLAGS+=-Wformat-overflow CFLAGS+=-Wmissing-parameter-type endif # GCC greater than or equal to 9 #ifeq "$(GCCGTEQ11)" "1" +CFLAGS+=-Wimplicit-fallthrough #endif #ifeq "$(GCCGTEQ12)" "1" #endif +#ifeq "$(GCCGTEQ14)" "1" +#endif # end of gcc-specific else # gcc or clang? 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) +ifeq "$(CLANGGTEQ12)" "1" +CFLAGS+=-Wimplicit-fallthrough +endif ifeq "$(CLANGGTEQ14)" "1" ifneq "$(VIEWDEPRECATIONS)" "1" CFLAGS+=-Wno-deprecated-declarations @@ -119,6 +128,7 @@ CCXX=g++ -std=gnu++11 GPPGTEQ9 := $(shell expr `$(CXX) -dumpversion | cut -f1 -d.` \>= 9) GPPGTEQ11 := $(shell expr `$(CXX) -dumpversion | cut -f1 -d.` \>= 11) GPPGTEQ12 := $(shell expr `$(CXX) -dumpversion | cut -f1 -d.` \>= 12) +GPPGTEQ14 := $(shell expr `$(CXX) -dumpversion | cut -f1 -d.` \>= 14) ifeq "$(GPPGTEQ9)" "1" CCXXFLAGS+=-Wformat-overflow ifdef CPLUSPLUS_NEED_DEPSUPPRESS @@ -135,11 +145,19 @@ CFLAGS+=-fPIC endif # g++ version greater than or equal to 11 ifdef CPLUSPLUS_NEED17 ifeq "$(GPPGTEQ12)" "1" -CCXX=g++ -std=c++20 +CCXX=g++ -std=c++17 else # g++ version greater than or equal to 12? (no follows) CCXX=g++ -std=c++17 endif # g++ version greater than or equal to 12 endif # CPLUSPLUS_NEED17 +ifdef CPLUSPLUS_NEED20 +ifeq "$(GPPGTEQ9)" "1" +CCXX=g++ -std=c++20 +else # g++ version greater than or equal to 9? (no follows) +CCXX=g++ -std=c++17 +endif # g++ version greater than or equal to 9 +endif # CPLUSPLUS_NEED20 + else # g++ or clang++ ? @@ -149,6 +167,7 @@ CCXX=clang++ -std=c++11 CLANGPPGTEQ9 := $(shell expr `$(CXX) -dumpversion | cut -f1 -d.` \>= 9) CLANGPPGTEQ11 := $(shell expr `$(CXX) -dumpversion | cut -f1 -d.` \>= 11) CLANGPPGTEQ14 := $(shell expr `$(CXX) -dumpversion | cut -f1 -d.` \>= 14) +CLANGPPGTEQ16 := $(shell expr `$(CXX) -dumpversion | cut -f1 -d.` \>= 16) CLANGPPGTEQ17 := $(shell expr `$(CXX) -dumpversion | cut -f1 -d.` \>= 17) ifeq "$(CLANGPPGTEQ9)" "1" #CCXXFLAGS+=-Wformat-overflow @@ -170,9 +189,14 @@ ifeq "$(CLANGPPGTEQ14)" "1" CCXX=clang++ -std=c++17 endif # clang++ greater than or equal to 14 ifeq "$(CLANGPPGTEQ17)" "1" -CCXX=clang++ -std=c++20 +CCXX=clang++ -std=c++17 endif # clang++ greater than or equal to 17 endif # CPLUSPLUS_NEED17 +ifdef CPLUSPLUS_NEED20 +ifeq "$(CLANGPPGTEQ17)" "1" +CCXX=clang++ -std=c++20 +endif # clang++ greater than or equal to 17 +endif # CPLUSPLUS_NEED20 endif # end of clang++-specific section CXX=$(CCXX) endif # CPLUSPLUS_NEEDED diff --git a/sys/unix/hints/include/cross-post.370 b/sys/unix/hints/include/cross-post.370 index 557b3504a..9d7c5b548 100644 --- a/sys/unix/hints/include/cross-post.370 +++ b/sys/unix/hints/include/cross-post.370 @@ -22,9 +22,9 @@ $(TARGETPFX)tile.o : tile.c $(TARGETPFX)exceptn.o : ../lib/djgpp/djgpp-patch/src/libc/go32/exceptn.S $(TARGET_CC) -c -o $@ ../lib/djgpp/djgpp-patch/src/libc/go32/exceptn.S $(TARGET_AR) ru ../lib/djgpp/i586-pc-msdosdjgpp/lib/libc.a $(TARGETPFX)exceptn.o -$(GAMEBIN) : $(HOBJ) $(HACKLIB) $(LUACROSSLIB) - $(TARGET_LINK) $(TARGET_LFLAGS) -o $(GAMEBIN) \ - $(HOBJ) $(HACKLIB) $(WINLIB) $(TARGET_LIBS) +$(GAMEBIN) : $(HOBJ) $(TARGETPFX)date.o $(TARGET_HACKLIB) $(LUACROSSLIB) + $(TARGET_LINK) $(TARGET_LFLAGS) -o $@ \ + $(HOBJ) $(TARGET_HACKLIB) $(WINLIB) $(TARGET_LIBS) $(DOSFONT)/ter-u16b.psf: $(FONTTOP)/ter-u16b.bdf $(DOSFONT)/nh-u16b.bdf $(DOSFONT)/makefont.lua $(LUABIN) $(LUABIN) $(DOSFONT)/makefont.lua $(FONTTOP)/ter-u16b.bdf $(DOSFONT)/nh-u16b.bdf $@ $(DOSFONT)/ter-u16v.psf: $(FONTTOP)/ter-u16v.bdf $(DOSFONT)/nh-u16v.bdf $(DOSFONT)/makefont.lua $(LUABIN) @@ -49,6 +49,7 @@ dospkg: dodata dosfonts $(GAMEBIN) $(TARGETPFX)recover.exe ../dat/nhtiles.bmp mkdir -p $(TARGETPFX)pkg cp $(GAMEBIN) $(TARGETPFX)pkg/NETHACK.EXE cp $(TARGETPFX)recover.exe $(TARGETPFX)pkg/RECOVER.EXE + -cp $(SFCTOOLBIN) $(TARGETPFX)pkg/SFCTOOL.EXE cp ../dat/nhdat $(TARGETPFX)pkg/NHDAT cp ../dat/license $(TARGETPFX)pkg/LICENSE cp ../dat/nhtiles.bmp $(TARGETPFX)pkg/NHTILES.BMP @@ -65,11 +66,29 @@ dospkg: dodata dosfonts $(GAMEBIN) $(TARGETPFX)recover.exe ../dat/nhtiles.bmp 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/symify/simify.exe ]; then \ - cp ../lib/djgpp/symify/simify.exe $(TARGETPFX)pkg/SYMIFY.EXE; \ + ( if [ -f ../lib/djgpp/target/bin/symify.exe ]; then \ + cp ../lib/djgpp/target/bin/symify.exe $(TARGETPFX)pkg/SYMIFY.EXE; \ else \ - pwd; echo "../lib/djgpp/symify/symify.exe not found"; \ + pwd; echo "../lib/djgpp/target/bin/symify.exe not found"; \ fi; ) +ifeq "$(WANT_DEBUG)" "1" + ( if [ -f $(GDBEXE) ]; \ + then \ + cp $(GDBEXE) $(TARGETPFX)pkg/GDB.EXE; \ + echo "gdb -ex '$(GDBCMDLINE)' NETHACK.EXE"> $(TARGETPFX)pkg/$(GDBBAT); \ + else \ + pwd; echo "$(GDBEXE) not found and WANT_DEBUG=1 specified"; \ + fi; ) +else + -( if [ -f $(TARGETPFX)pkg/GDB.EXE ]; \ + then \ + rm $(TARGETPFX)pkg/GDB.EXE; \ + fi; ) + -( if [ -f $(TARGETPFX)pkg/NHGDB.BAT ]; \ + then \ + rm $(TARGETPFX)pkg/NHGDB.BAT; \ + fi; ) +endif -touch $(TARGETPFX)pkg/RECORD cd $(TARGETPFX)pkg ; zip -9 ../NH370DOS.ZIP * ; cd ../../.. @echo msdos package zip file $(TARGETPFX)NH370DOS.ZIP @@ -78,7 +97,58 @@ $(LUABIN): ( cd .. && make luabin && cd src) dodata: ( cd .. && make dlb && cd src) +ifdef dosbox +# make CROSS_TO_MSDOS=1 dosbox=~/dosbox deploy-to-dosbox +ifdef MAKEFILE_TOP +deploy-to-dosbox: + ( cd src; make $(DEPLOY); cd .. ) +endif +.PHONY: deploytodosbox +deploytodosbox: $(TARGETPFX)NH370DOS.ZIP $(dosboxnhfolder) $(dosboxnhsrc) \ + $(dosboxnhsrc)/src $(dosboxnhsrc)/include \ + $(dosboxnhsrc)/sys/msdos $(dosboxnhsrc)/sys/share \ + $(dosboxnhsrc)/win/share $(dosboxnhsrc)/win/curses \ + $(dosboxnhsrc)/win/tty $(dosboxnhsrc)/util \ + $(dosboxnhfolder)/NETHACK.EXE + @echo DOS NetHack deployed to dosbox at $(dosboxnhfolder) +$(TARGETPFX)NH370DOS.ZIP: dospkg +$(dosboxnhfolder): + mkdir -p $@ +$(dosboxnhsrc): $(dosboxnhfolder) + mkdir -p $@ +$(dosboxnhsrc)/src: $(dosboxnhsrc) + mkdir -p $@ + cp --preserve=timestamps $(FLDR)src/*.c $(dosboxnhsrc)/src +$(dosboxnhsrc)/include: $(dosboxnhsrc) + mkdir -p $@ + cp --preserve=timestamps $(FLDR)include/*.h $(dosboxnhsrc)/include +$(dosboxnhsrc)/sys/msdos: $(dosboxnhsrc) + mkdir -p $@ + cp --preserve=timestamps $(FLDR)sys/msdos/*.c $(dosboxnhsrc)/sys/msdos + cp --preserve=timestamps $(FLDR)sys/msdos/*.h $(dosboxnhsrc)/sys/msdos +$(dosboxnhsrc)/sys/share: $(dosboxnhsrc) + mkdir -p $@ + cp --preserve=timestamps $(FLDR)sys/share/*.c $(dosboxnhsrc)/sys/share +$(dosboxnhsrc)/win/share: $(dosboxnhsrc) + mkdir -p $@ + cp --preserve=timestamps $(FLDR)win/share/*.c $(dosboxnhsrc)/win/share +$(dosboxnhsrc)/win/curses: $(dosboxnhsrc) + mkdir -p $@ + cp --preserve=timestamps $(FLDR)win/curses/*.c $(dosboxnhsrc)/win/curses + cp --preserve=timestamps $(FLDR)win/curses/*.h $(dosboxnhsrc)/win/curses +$(dosboxnhsrc)/win/tty: $(dosboxnhsrc) + mkdir -p $@ + cp --preserve=timestamps $(FLDR)win/tty/*.c $(dosboxnhsrc)/win/tty +$(dosboxnhsrc)/util: $(dosboxnhsrc) + mkdir -p $@ + cp --preserve=timestamps $(FLDR)util/*.c $(dosboxnhsrc)/util + cp --preserve=timestamps $(FLDR)util/*.h $(dosboxnhsrc)/util +$(dosboxnhfolder)/NETHACK.EXE: $(TARGETPFX)NH370DOS.ZIP + unzip -o $(TARGETPFX)NH370DOS.ZIP -d $(dosboxnhfolder) + find $(dosboxnhfolder) -type f -name "$(dosboxconfigfile)" \ + | xargs sed -i 's/#OPTIONS=video:autodetect/OPTIONS=video:autodetect/g' +endif # dosbox endif # CROSS_TO_MSDOS ifdef CROSS_TO_WASM @@ -107,12 +177,17 @@ $(TARGETPFX)unixtty.o : ../sys/share/unixtty.c $(HACK_H) $(TARGETPFX)winshim.o : ../win/shim/winshim.c $(HACK_H) $(TARGETPFX)libnhmain.o : ../sys/libnh/libnhmain.c $(HACK_H) endif # CROSS_TO_WASM + # ifdef CROSS_TO_MIPS -$(MIPS_TARGET): pregame $(TARGETPFX)date.o $(HOSTOBJ) $(HOBJ) $(LUACROSSLIB) $(MIPS_DATA_DIR) +$(MIPS_TARGET): pregame $(TARGETPFX)date.o $(HOSTOBJ) $(HOBJ) $(LUACROSSLIB) \ + $(TARGETPFX)ncurses/lib/libncurses.a \ + $(MIPS_DATA_DIR) -rm $@ - $(TARGET_CC) $(TARGET_LFLAGS) $(TARGET_CFLAGS) -o $@ \ - $(HOBJ) $(TARGETPFX)date.o $(TARGET_LIBS) + $(TARGET_LINK) $(TARGET_LFLAGS) -o $@ \ + $(HOBJ) $(TARGETPFX)date.o $(TARGETPFX)$(HACKLIB) \ + $(TARGETPFX)ncurses/lib/libncurses.a \ + $(TARGET_LIBS) $(MIPS_DATA_DIR): $(MIPS_DATA_DIR)/nhdat touch $(MIPS_DATA_DIR)/perm @@ -131,9 +206,28 @@ $(TARGETPFX)unixres.o : ../sys/unix/unixres.c $(HACK_H) $(TARGETPFX)unixunix.o : ../sys/unix/unixunix.c $(HACK_H) $(TARGETPFX)ioctl.o : ../sys/share/ioctl.c $(HACK_H) $(TARGETPFX)unixtty.o : ../sys/share/unixtty.c $(HACK_H) -endif # CROSS_TO_MIPS -# +$(LUABIN): + ( cd .. && make luabin && cd src) +dodata: + ( cd .. && make dlb && cd src) + +mipsrecover: $(TARGETPFX)recover +.PHONY: mipspkg +mipspkg: dodata $(GAMEBIN) $(TARGETPFX)recover + mkdir -p $(TARGETPFX)pkg + cp $(GAMEBIN) $(TARGETPFX)pkg/nethack + cp $(TARGETPFX)recover $(TARGETPFX)pkg/recover + cp ../dat/nhdat $(TARGETPFX)pkg/nhdat + cp ../dat/license $(TARGETPFX)pkg/license + cp ../dat/symbols $(TARGETPFX)pkg/symbols + cp ../sys/share/NetHack.cnf $(TARGETPFX)pkg/.nethackrc + cp ../sys/msdos/sysconf $(TARGETPFX)pkg/sysconf + cp ../doc/nethack.txt $(TARGETPFX)pkg/nethack.txt + -touch $(TARGETPFX)pkg/record + cd $(TARGETPFX)pkg ; zip -9 ../nh370mips.zip * ; cd ../../.. + @echo MIPS package zip file $(TARGETPFX)nh370mips.zip +endif # CROSS_TO_MIPS ifdef CROSS_SHARED # shared file dependencies @@ -144,15 +238,28 @@ $(TARGETPFX)pcunix.o : ../sys/share/pcunix.c $(HACK_H) $(TARGETPFX)tileset.o : ../win/share/tileset.c $(TARGETPFX)bmptiles.o : ../win/share/bmptiles.c $(TARGETPFX)giftiles.o : ../win/share/giftiles.c -$(TARGETPFX)recover.o : ../util/recover.c -$(TARGETPFX)recover.exe : $(TARGETPFX)recover.o - $(TARGET_LINK) $(TARGET_LFLAGS) $(TARGETPFX)recover.o -o $@ endif # CROSS_SHARED # ifdef CROSS $(TARGETPFX)hacklib.a: $(TARGETPFX)hacklib.o $(TARGET_AR) $(TARGET_ARFLAGS) $@ $(TARGETPFX)hacklib.o +ifdef MAKEFILE_UTL +$(TARGETPFX)recover.o: recover.c $(CONFIG_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(CSTD) -c recover.c -o $@ +ifdef CROSS_TO_MSDOS +$(TARGETPFX)recover.exe : $(TARGETPFX)recover.o $(TARGETPFX)rversion.o $(TARGETPFX)hacklib.a + $(TARGET_LINK) $(TARGET_LFLAGS) \ + $(TARGETPFX)recover.o $(TARGETPFX)rversion.o $(TARGETPFX)hacklib.a -o $@ +else +$(TARGETPFX)recover : $(TARGETPFX)recover.o $(TARGETPFX)rversion.o $(TARGETPFX)hacklib.a + $(TARGET_LINK) $(TARGET_LFLAGS) \ + $(TARGETPFX)recover.o $(TARGETPFX)rversion.o $(TARGETPFX)hacklib.a -o $@ endif +$(TARGETPFX)rversion.o: ../src/version.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) -DMINIMAL_FOR_RECOVER $(CSTD) -c ../src/version.c -o $@ +endif # MAKEFILE_UTL +endif # CROSS + ifdef BUILD_TARGET_LUA # Lua lib $(LUACROSSLIB): $(LUALIBOBJS) @@ -204,11 +311,11 @@ ifdef BUILD_TARGET_NCURSES .PHONY: build-ncurses ifdef MAKEFILE_SRC ../lib/ncurses.tar.gz: - @echo "You will need to successfully execute 'make CROSS_TO_MIPS=1 fetch-ncurses' first" + @echo "You will need to successfully execute 'make CROSS_TO_$(NCURSES_PLATFORM)=1 fetch-ncurses' first" @false $(TARGETPFX)ncurses/lib/libncurses.a: ../lib/ncurses.tar.gz (cd $(TARGETDIR) ; mkdir -p ncurses ; cd ncurses ; tar -xf ../../../lib/ncurses.tar.gz --strip-components=1 ; \ - ./configure --build i686-pc-linux-gnu --host mipsel-linux-gnu ; \ + ./configure --build $(NCURSES_CONFIGURE_BUILD) --host $(NCURSES_CONFIGURE_HOST) ; \ make ; \ cd ../../../src) endif #MAKEFILE_SRC diff --git a/sys/unix/hints/include/cross-pre2.370 b/sys/unix/hints/include/cross-pre2.370 index fa5f0911c..df4ef6105 100644 --- a/sys/unix/hints/include/cross-pre2.370 +++ b/sys/unix/hints/include/cross-pre2.370 @@ -35,7 +35,8 @@ LUACROSSLIB = $(TARGETPFX)lua$(subst .,,$(LUA_VERSION)).a LUAINCL = -I$(LUASRCDIR) override BUILDMORE += $(LUACROSSLIB) override CLEANMORE += rm -f $(LUACROSSLIB) ; -override TARGET_LIBS += $(LUACROSSLIB) -lm +override TARGET_LIBS += $(LUACROSSLIB) +LIBLM = -lm else LUAINCL= endif # BUILD_TARGET_LUA @@ -84,8 +85,6 @@ PDCOBJS = $(TARGETPFX)pdcclip.o $(TARGETPFX)pdcdisp.o \ $(TARGETPFX)pdcscrn.o $(TARGETPFX)pdcsetsc.o \ $(TARGETPFX)pdcutil.o override TARGET_LIBS += $(PDCLIB) -ifdef CROSS_TO_MSDOS -endif override BUILDMORE += $(PDCLIB) override CLEANMORE += rm -f $(PDCLIB) ; else #WANT_WIN_CURSES @@ -135,19 +134,57 @@ override TARGET_CC = $(TOOLTOP1)/i586-pc-msdosdjgpp-gcc override TARGET_CXX = $(TOOLTOP1)/i586-pc-msdosdjgpp-g++ override TARGET_AR = $(TOOLTOP1)/i586-pc-msdosdjgpp-gcc-ar override TARGET_STUBEDIT = ../lib/djgpp/i586-pc-msdosdjgpp/bin/stubedit -MSDOS_TARGET_CFLAGS = -c -O -I../include -I../sys/msdos -I../win/share \ - $(LUAINCL) -DDLB $(PDCURSESDEF) \ - -DTILES_IN_GLYPHMAP -DCROSSCOMPILE -DCROSSCOMPILE_TARGET \ - -DCROSS_TO_MSDOS \ - -Wall -Wextra -Wno-missing-field-initializers -Wreturn-type -Wunused \ - -Wformat -Wswitch -Wshadow -Wwrite-strings \ - -Wimplicit -Wimplicit-function-declaration -Wimplicit-int \ - -Wmissing-parameter-type -Wold-style-definition -Wstrict-prototypes -MSDOS_TARGET_CXXFLAGS = -c -O -I../include -I../sys/msdos -I../win/share \ +ifdef MAKEFILE_SRC +FLDR=../ +endif +ifdef MAKEFILE_TOP +FLDR= +endif +dostargetexes=$(FLDR)lib/djgpp/target/bin/ +# +ifdef DOSBOX +dosbox=$(DOSBOX) +endif +ifdef dosbox +dosboxgameid=NH370 +dosboxtop=$(dosbox) +dosboxnhfolder=$(dosboxtop)/$(dosboxgameid) +dosboxnhsrc=$(dosboxnhfolder)/NHSRC +dosboxconfigfile=NETHACK.CNF +dosgdburl=http://www.mirrorservice.org/sites/ftp.delorie.com/pub/djgpp/current/v2gnu/gdb801b.zip +WANT_DEBUG=1 +DEPLOY=deploytodosbox +endif # dosbox +# +ifeq "$(WANT_DEBUG)" "1" +DBGFLAGS = -g +else +DBGFLAGS = +endif +MSDOS_GCC_CFLAGS = -Wall -Wextra -Wreturn-type -Wunused -Wformat \ + -Wswitch -Wshadow -Wwrite-strings -Wmissing-declarations \ + -Wunreachable-code \ + -Wimplicit -Wimplicit-function-declaration \ + -Wimplicit-int -Wmissing-prototypes -Wold-style-definition \ + -Wstrict-prototypes -Wnonnull -Wformat-overflow \ + -Wmissing-parameter-type -Wimplicit-fallthrough +#-Wno-missing-field-initializers -Wno-cast-function-type +#-Wno-format +MSDOS_PEDANTIC = -pedantic +MSDOS_GPP_CFLAGS = -Wall -Wextra -Wno-missing-field-initializers -Wreturn-type \ + -Wunused -Wformat -Wswitch -Wshadow -Wwrite-strings -pedantic \ + -Wmissing-declarations -Wformat-nonliteral -Wunreachable-code \ + -Wno-maybe-uninitialized +MSDOS_TARGET_CFLAGS = -c -O $(DBGFLAGS) -I../include -I../sys/msdos -I../win/share \ + $(LUAINCL) -DDLB $(PDCURSESDEF) -DTILES_IN_GLYPHMAP \ + -DCROSSCOMPILE -DCROSSCOMPILE_TARGET -DCROSS_TO_MSDOS \ + -D_NAIVE_DOS_REGS \ + $(MSDOS_GCC_CFLAGS) $(SNDCFLAGS) +MSDOS_TARGET_CXXFLAGS = -c -O $(DBGFLAGS) -I../include -I../sys/msdos -I../win/share \ $(LUAINCL) -DDLB $(PDCURSESDEF) \ -DUSE_TILES -DCROSSCOMPILE -DCROSSCOMPILE_TARGET -DCROSS_TO_MSDOS \ - -Wall -Wextra -Wno-missing-field-initializers -Wreturn-type -Wunused \ - -Wformat -Wswitch -Wshadow -Wwrite-strings -Wno-maybe-uninitialized + -D_NAIVE_DOS_REGS \ + $(MSDOS_GPP_CFLAGS) $(SNDCFLAGS) PDCINCL += -I$(PDCPORT) PDC_TARGET_CFLAGS = $(MSDOS_TARGET_CFLAGS) -Wno-unused-parameter \ -Wno-missing-prototypes @@ -160,9 +197,7 @@ FONTTARGETS = $(DOSFONT)/ter-u16b.psf $(DOSFONT)/ter-u16v.psf \ $(DOSFONT)/ter-u28b.psf $(DOSFONT)/ter-u32b.psf LUABIN = ../lib/lua-$(LUA_VERSION)/src/lua LUA_TARGET_CFLAGS = $(MSDOS_TARGET_CFLAGS) -override TARGET_CFLAGS = $(MSDOS_TARGET_CFLAGS) -Wmissing-declarations \ - -Wmissing-prototypes -pedantic -Wmissing-declarations \ - -Wformat-nonliteral +override TARGET_CFLAGS = $(MSDOS_TARGET_CFLAGS) $(MSDOS_PEDANTIC) override TARGET_CXXFLAGS = $(MSDOS_TARGET_CXXFLAGS) ifdef CPLUSPLUS_NEEDED override TARGET_LINK = $(TOOLTOP1)/i586-pc-msdosdjgpp-g++ @@ -170,7 +205,7 @@ else override TARGET_LINK = $(TOOLTOP1)/i586-pc-msdosdjgpp-gcc endif override TARGET_LFLAGS= -override TARGET_LIBS += -lpc +override TARGET_LIBS += -lpc $(LIBLM) override SYSSRC = ../sys/share/pcmain.c ../sys/msdos/msdos.c \ ../sys/share/pcsys.c ../sys/share/pctty.c \ ../sys/share/pcunix.c ../sys/msdos/video.c \ @@ -192,9 +227,22 @@ override LUALIB= override LUALIBS= override TOPLUALIB= override GAMEBIN = $(TARGETPFX)nethack.exe +override RECOVERBIN = $(TARGETPFX)recover.exe +override SFCTOOLBIN = $(TARGETPFX)sfctool.exe override PACKAGE = dospkg override PREGAME += mkdir -p $(TARGETDIR) ; make $(TARGETPFX)exceptn.o ; override CLEANMORE += rm -f -r $(TARGETDIR) ; rm -f -r $(FONTTARGETS) ; +ifeq "$(WANT_DEBUG)" "1" +GDBEXE=$(dostargetexes)gdb.exe +GDBBAT=NHGDB.BAT +GDBCMDLINE=directory nhsrc/src nhsrc/include nhsrc/sys/msdos \ + nhsrc/sys/share nhsrc/win/curses \ + nhsrc/win/tty +else +GDBEXE= +GDBBAT= +GDBCMDLINE= +endif VARDATND += nhtiles.bmp # ifdef WANT_WIN_CURSES @@ -216,7 +264,7 @@ ifdef CROSS_TO_WASM # originally from https://github.com/NetHack/NetHack/pull/385 #===============-================================================= # -WASM_DEBUG = 1 +#WASM_DEBUG = 1 WASM_DATA_DIR = $(TARGETPFX)wasm-data WASM_TARGET = $(TARGETPFX)nethack.js EMCC_LFLAGS = @@ -227,10 +275,13 @@ 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"]' +EMCC_LFLAGS += -s EXPORTED_FUNCTIONS='["_main", "_shim_graphics_set_callback", "_display_inventory", "_malloc"]' EMCC_LFLAGS += -s EXPORTED_RUNTIME_METHODS='["cwrap", "ccall", "addFunction", \ - "removeFunction", "UTF8ToString", "getValue", "setValue"]' + "removeFunction", "UTF8ToString", "stringToUTF8", "getValue", \ + "setValue", "ENV", "FS", "IDBFS"]' EMCC_LFLAGS += -s ERROR_ON_UNDEFINED_SYMBOLS=0 +EMCC_LFLAGS += -s EXPORT_ES6=1 +EMCC_LFLAGS += -lidbfs.js # XXX: the "@/" at the end of "--embed-file" tells emscripten to embed the files # in the root directory, otherwise they will end up in the $(WASM_DATA_DIR) path EMCC_LFLAGS += --embed-file $(WASM_DATA_DIR)@/ @@ -355,6 +406,7 @@ CFLAGS += -DCROSSCOMPILE override TARGET_CC = mipsel-linux-gnu-gcc override TARGET_CXX = CXX=mipsel-linux-gnu-g++ override TARGET_AR = mipsel-linux-gnu-ar +MIPS_TARGET = $(TARGETPFX)nethack #override TARGET_LINK = mipsel-linux-gnu-ld MIPS_TARGET_CFLAGS = -c -O -I../include -I../sys/unix -I../win/share \ $(LUAINCL) -DDLB \ @@ -364,7 +416,8 @@ MIPS_TARGET_CFLAGS = -c -O -I../include -I../sys/unix -I../win/share \ -Wall -Wextra -Wno-missing-field-initializers -Wreturn-type -Wunused \ -Wformat -Wswitch -Wshadow -Wwrite-strings \ -Wimplicit -Wimplicit-function-declaration -Wimplicit-int \ - -Wmissing-parameter-type -Wold-style-definition -Wstrict-prototypes + -Wmissing-parameter-type -Wold-style-definition -Wstrict-prototypes \ + -Wno-unused-result MIPS_TARGET_CXXFLAGS = -c -O -I../include -I../sys/unix -I../win/share \ $(LUAINCL) -DDLB \ -DUSE_TILES -DCROSSCOMPILE -DCROSSCOMPILE_TARGET -DCROSS_TO_MIPS \ @@ -386,10 +439,14 @@ override WINLIB = $(NCURSESLIB) override LUALIB= override LUALIBS= override TOPLUALIB= -#override GAMEBIN= -#override PACKAGE= +override GAMEBIN=$(MIPS_TARGET) +override RECOVERBIN = $(TARGETPFX)recover +override PACKAGE = mipspkg override PREGAME += mkdir -p $(TARGETDIR) ; override CLEANMORE += rm -f -r $(TARGETDIR) ; +NCURSES_CONFIGURE_BUILD=i686-pc-linux-gnu +NCURSES_CONFIGURE_HOST=mipsel-linux-gnu +NCURSES_PLATFORM=MIPS # Rule for file in sys/unix #$(TARGETPFX)%.o : ../sys/unix/%.c # $(TARGET_CC) $(TARGET_CFLAGS) -c -o$@ $< diff --git a/sys/unix/hints/include/multiw-2.370 b/sys/unix/hints/include/multiw-2.370 index 938c958f3..c580063b6 100644 --- a/sys/unix/hints/include/multiw-2.370 +++ b/sys/unix/hints/include/multiw-2.370 @@ -163,7 +163,7 @@ ifndef CPLUSPLUS_NEEDED CPLUSPLUS_NEEDED = 1 endif # CPLUSPLUS_NEEDED ifdef WANT_WIN_QT6 -CPLUSPLUS_NEED17 = 1 +CPLUSPLUS_NEED20 = 1 CPLUSPLUS_NEED_DEPSUPPRESS = 1 endif # WANT_WIN_QT6 endif # WANT_WIN_QT @@ -194,6 +194,21 @@ CPLUSPLUS_NEEDED = 1 endif endif +ifeq "$(musl)" "1" +MUSL=1 +endif +ifeq "$(MUSL)" "1" +ifneq "$(NOCRASHREPORT)" "1" +NOCRASHREPORT=1 +endif +WINCFLAGS += -DMUSL_LIBC +# use this instead of col -bx +COLCMD=../util/stripbs +STRIPBS=../util/stripbs +else +WINCFLAGS += -DGNU_LIBC +endif + ifeq "$(NOCRASHREPORT)" "1" WINCFLAGS += -DNOCRASHREPORT endif diff --git a/sys/unix/hints/linux.370 b/sys/unix/hints/linux.370 index ce12472c7..3ce7d47d4 100755 --- a/sys/unix/hints/linux.370 +++ b/sys/unix/hints/linux.370 @@ -74,6 +74,9 @@ endif # WANT_WIN_QT5 ifdef WANT_WIN_QT6 #if your Qt6 is elsewhere, change this to match QTDIR=/usr/local/qt6 +ifeq "$(GPPGTEQ14)" "1" +CCXXFLAGS += -Wno-template-id-cdtor +endif # g++ greater than or equal to 14 endif # WANT_WIN_QT6 endif # WANT_WIN_QT @@ -101,6 +104,7 @@ CURSESLIB = -lncurses -ltinfo ifdef MAKEFILE_SRC comma:=, NCURSES_LFLAGS = $(shell pkg-config ncursesw --libs) +NCURSES_CFLAGS = $(shell pkg-config ncursesw --cflags) ifeq (,$(findstring ncursesw, $(NCURSES_LFLAGS))) ifeq (,$(findstring ncurses, $(NCURSES_LFLAGS))) #this indicates that pkg-config itself was unavailable @@ -122,8 +126,33 @@ CURSESLIB = $(subst -Wl$(comma)-Bsymbolic-functions,,$(NCURSES_LFLAGS)) else CURSESLIB = $(NCURSES_LFLAGS) endif -#$(info $(CURSESLIB)) endif #HAVE_NCURSESW +#$(info $(CURSESLIB)) +ifeq (,$(findstring ncursesw, $(NCURSES_LFLAGS))) +ifeq (,$(findstring ncurses, $(NCURSES_LFLAGS))) +#this indicates that pkg-config itself was unavailable +NOPKGCONFIG = 1 +endif #ncurses not in NCURSES_LFLAGS? +endif #ncursesw not in NCURSES_LFLAGS? +# +ifeq "$(NOPKGCONFIG)" "1" +NCURSES_CFLAGS = -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 +ifeq "$(HAVE_NCURSESW)" "1" +ifeq (,$(wildcard /usr/include/ncursesw/curses.h)) +NCURSES_CFLAGS += -I/usr/include +else +NCURSES_CFLAGS += -I/usr/include/ncursesw +endif +NCURSES_LFLAGS = -lncursesw -ltinfo +else #HAVE_NCURSESW +ifeq (,$(wildcard /usr/include/ncurses/curses.h)) +NCURSES_CFLAGS += -I/usr/include +else +NCURSES_CFLAGS += -I/usr/include/ncurses +endif +NCURSES_LFLAGS = -lncurses -ltinfo +endif #HAVE_NCURSESW +endif #NOPKGCONFIG endif #MAKEFILE_SRC endif #USE_CURSESLIB @@ -149,6 +178,7 @@ NHCFLAGS+=-DCOMPRESS=\"/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\" #NHCFLAGS+=-DCHANGE_COLOR NHCFLAGS+=-DSELF_RECOVER ifdef WANT_WIN_CURSES +NHCFLAGS+=$(NCURSES_CFLAGS) ifeq "$(HAVE_NCURSESW)" "1" NHCFLAGS+=-DCURSES_UNICODE else @@ -162,17 +192,28 @@ endif #WANT_WIN_CURSES #-INCLUDE multisnd1-pre.370 # +ifeq "$(CCISCLANG)" "1" +# clang-specific starts +# clang-specific ends +else +# gcc-specific starts +LIBCFLAGS+=-DSIG_RET_TYPE=__sighandler_t +# gcc-specific ends +endif + # WINCFLAGS set from multiw-2.370 # SNDCFLAGS set from multisnd-pre.370 CFLAGS+= $(WINCFLAGS) CFLAGS+= $(SNDCFLAGS) CFLAGS+= $(NHCFLAGS) +CFLAGS+= $(LIBCFLAGS) # WINCFLAGS set from multiw-2.370 # SNDCFLAGS set from multisnd-pre.370 CCXXFLAGS+= $(WINCFLAGS) CCXXFLAGS+= $(SNDCFLAGS) CCXXFLAGS+= $(NHCFLAGS) +CCXXFLAGS+= $(LIBCFLAGS) VARDATND = VARDATND0 = @@ -357,10 +398,10 @@ POSTINSTALL+= sed -i -e 's;^GDBPATH=/usr/bin/gdb;\#GDBPATH=/usr/bin/gdb;' \ -e 's;PANICTRACE_GDB=1;PANICTRACE_GDB=0;' $(INSTDIR)/sysconf; endif -ifeq 'USENONOSTATICFN' '1' +ifeq '$(USE_NONOSTATICFN)' '1' CFLAGS += -DNONOSTATICFN else -ifeq 'USE_NOSTATICFN' '1' +ifeq '$(USE_NOSTATICFN)' '1' CFLAGS += -DNOSTATICFN endif endif @@ -426,13 +467,14 @@ endif endif ifdef WANT_LIBNH -$(TARGETPFX)libnh.a: $(HOBJ) $(LIBNHSYSOBJ) ../lib/lua/liblua.a - $(AR) rcs $@ $(HOBJ) $(LIBNHSYSOBJ) ../lib/lua/liblua.a +$(TARGETPFX)libnh.a: $(HOBJ) $(LIBNHSYSOBJ) ../lib/lua/liblua-$(LUA_VERSION).a + $(AR) rcs $@ $(HOBJ) $(LIBNHSYSOBJ) ../lib/lua/liblua-$(LUA_VERSION).a @echo "$@ built." $(TARGETPFX)libnhmain.o : ../sys/libnh/libnhmain.c $(HACK_H) $(CC) $(CFLAGS) -c -o$@ $< -$(TARGETPFX)winshim.o : ../win/shim/winshim.c $(HACK_H) - $(CC) $(CFLAGS) -c -o$@ $< +#dependency tool added this to Makefile.src +#$(TARGETPFX)winshim.o : ../win/shim/winshim.c $(HACK_H) +# $(CC) $(CFLAGS) -c -o$@ $< endif # WANT_LIBNH # diff --git a/sys/unix/hints/macOS.370 b/sys/unix/hints/macOS.370 index b77ea94f1..ad522bb2f 100755 --- a/sys/unix/hints/macOS.370 +++ b/sys/unix/hints/macOS.370 @@ -87,6 +87,13 @@ endif # HAVE_MACPORTS endif # QTDIR endif # WANT_WIN_QT +ifdef WANT_WIN_QT +ifeq "$(CLANGPPGTEQ16)" "1" +CXX=clang++ --std=c++17 +CCXXFLAGS +=-Wno-c++20-attribute-extensions +endif +endif + # misc.370 must come after compiler.370 # and after QTDIR is defined @@ -212,17 +219,30 @@ endif #-INCLUDE multisnd1-pre.370 # +# although not currently doing anything for macOS, this is +# kept in for consistency with layout of linux.370 +ifeq "$(CCISCLANG)" "1" +# clang-specific starts +# clang-specific ends +else +# gcc-specific starts +# LIBCFLAGS+= +# gcc-specific ends +endif + # WINCFLAGS set from multiw-2.370 # SNDCFLAGS set from multisnd-pre.370 CFLAGS+= $(PKGCFLAGS) $(WINCFLAGS) CFLAGS+= $(SNDCFLAGS) CFLAGS+= $(NHCFLAGS) +CFLAGS+= $(LIBCFLAGS) # WINCFLAGS set from multiw-2.370 # SNDCFLAGS set from multisnd-pre.370 CCXXFLAGS+= $(WINCFLAGS) CCXXFLAGS+= $(SNDCFLAGS) CCXXFLAGS+= $(NHCFLAGS) +CCXXFLAGS+= $(LIBCFLAGS) VARDATND = VARDATND0 = @@ -525,13 +545,14 @@ endif endif ifdef WANT_LIBNH -$(TARGETPFX)libnh.a: $(HOBJ) $(LIBNHSYSOBJ) ../lib/lua/liblua.a - $(AR) rcs $@ $(HOBJ) $(LIBNHSYSOBJ) ../lib/lua/liblua.a +$(TARGETPFX)libnh.a: $(HOBJ) $(LIBNHSYSOBJ) ../lib/lua/liblua-$(LUA_VERSION).a + $(AR) rcs $@ $(HOBJ) $(LIBNHSYSOBJ) ../lib/lua/liblua-$(LUA_VERSION).a @echo "$@ built." $(TARGETPFX)libnhmain.o : ../sys/libnh/libnhmain.c $(HACK_H) $(CC) $(CFLAGS) -c -o$@ $< -$(TARGETPFX)winshim.o : ../win/shim/winshim.c $(HACK_H) - $(CC) $(CFLAGS) -c -o$@ $< +# dependency tool added this to Makefile.src +#$(TARGETPFX)winshim.o : ../win/shim/winshim.c $(HACK_H) +# $(CC) $(CFLAGS) -c -o$@ $< endif # WANT_LIBNH ifdef MAKEFILE_DOC diff --git a/sys/unix/sysconf b/sys/unix/sysconf index e10b4e34c..34926903b 100644 --- a/sys/unix/sysconf +++ b/sys/unix/sysconf @@ -32,6 +32,10 @@ EXPLORERS=* # Uses the same syntax as the WIZARDS and EXPLORERS options above. #SHELLERS= +# Execute this program whenever a new message-window message is shown. +# The program will get the message text as the only parameter. +#MSGHANDLER=/usr/bin/espeak + # If the user name is found in this list, prompt for username instead. # Uses the same syntax as the WIZARDS option above. # A public server should probably disable this. diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c index 679f5e627..e74cfc8c5 100644 --- a/sys/unix/unixmain.c +++ b/sys/unix/unixmain.c @@ -8,7 +8,6 @@ #include "hack.h" #include "dlb.h" -#include #include #include #include @@ -37,7 +36,6 @@ 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; -static void opt_showpaths(const char *); ATTRNORETURN static void scores_only(int, char **, const char *) NORETURN; #ifdef SND_LIB_INTEGRATED uint32_t soundlibchoice = soundlib_nosound; @@ -557,7 +555,7 @@ process_options(int argc, char *argv[]) if (mx_ok) gl.locknum = mxplyrs; else - config_error_add("Invalid MAXPLATERS \"%s\"", argv[1]); + config_error_add("Invalid MAXPLAYERS \"%s\"", argv[1]); #endif } #ifdef MAX_NR_OF_PLAYERS @@ -644,15 +642,15 @@ early_options(int *argc_p, char ***argv_p, char **hackdir_p) ++arg; switch (arg[1]) { /* char after leading dash */ - case 'b': + case 'b': #ifdef CRASHREPORT - // --bidshow - if (argcheck(argc, argv, ARG_BIDSHOW) == 2){ + // --bidshow + if (argcheck(argc, argv, ARG_BIDSHOW) == 2){ opt_terminate(); /*NOTREACHED*/ - } + } #endif - break; + break; case 'd': if (argcheck(argc, argv, ARG_DEBUG) == 1) { consume_arg(ndx, argc_p, argv_p), consumed = 1; @@ -661,6 +659,12 @@ early_options(int *argc_p, char ***argv_p, char **hackdir_p) 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; @@ -703,9 +707,10 @@ early_options(int *argc_p, char ***argv_p, char **hackdir_p) break; case 's': if (argcheck(argc, argv, ARG_SHOWPATHS) == 2) { - opt_showpaths(*hackdir_p); - opt_terminate(); - /*NOTREACHED*/ + 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) @@ -786,18 +791,16 @@ opt_usage(const char *hackdir) /* show the sysconf file name, playground directory, run-time configuration file name, dumplog file name if applicable, and some other things */ -static void -opt_showpaths(const char *dir) +ATTRNORETURN void +after_opt_showpaths(const char *dir) { #ifdef CHDIR chdirx(dir, FALSE); #else nhUse(dir); #endif - iflags.initoptions_noterminate = TRUE; - initoptions(); - iflags.initoptions_noterminate = FALSE; - reveal_paths(); + opt_terminate(); + /*NOTREACHED*/ } /* handle "-s [character-names]" to show all the entries diff --git a/sys/vms/Makefile.src b/sys/vms/Makefile.src index df797c38c..dee166f8e 100644 --- a/sys/vms/Makefile.src +++ b/sys/vms/Makefile.src @@ -147,8 +147,8 @@ HACK_H = $(SRC)hack.h-t # all .c that are part of the main NetHack program and are not operating- or # windowing-system specific 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 \ + 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 \ explode.c extralev.c files.c fountain.c getpos.c glyphs.c hack.c \ hacklib.c insight.c invent.c light.c lock.c \ @@ -174,15 +174,18 @@ VERSOURCES = $(HACKCSRC) $(SYSSRC) $(WINSRC) $(RANDSRC) $(GENCSRC) # all .h files except date.h which would # cause dependency loops if run through "make depend" # -HACKINCL = align.h artifact.h artilist.h attrib.h color.h \ - config.h config1.h context.h coord.h decl.h defsym.h display.h \ - dlb.h dungeon.h engrave.h extern.h flag.h func_tab.h global.h \ - hack.h hacklib.h mextra.h mfndpos.h micro.h mkroom.h \ - monattk.h mondata.h monflag.h monst.h sym.h obj.h objclass.h \ - patchlevel.h pcconf.h permonst.h prop.h rect.h \ - region.h sym.h defsym.h rm.h sp_lev.h spell.h sys.h system.h \ - tcap.h timeout.h tradstdc.h trap.h unixconf.h vision.h \ - vmsconf.h wintty.h winX.h winprocs.h wintype.h you.h youprop.h +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 \ + micro.h mkroom.h monattk.h mondata.h monflag.h monst.h monsters.h \ + mfndpos.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 \ + rm.h sp_lev.h spell.h sndprocs.h seffects.h stairs.h sys.h \ + tcap.h timeout.h tradstdc.h trap.h unixconf.h vision.h vmsconf.h \ + wintty.h wincurs.h winX.h winprocs.h wintype.h you.h youprop.h \ + weight.h + #HSOURCES = $(HACKINCL) date.h onames.h pm.h @@ -191,9 +194,9 @@ FIRSTOBJ = vmsmisc.obj,vmsfiles.obj,monst.obj,objects.obj # split up long list so that we can write pieces of it into nethack.opt HOBJ1 = allmain.obj,alloc.obj,apply.obj,artifact.obj,attrib.obj, \ - ball.obj,bones.obj,botl.obj,calendar.obj,cmd.obj,coloratt.obj, \ - dbridge.obj,decl.obj,detect.obj,dig.obj,display.obj,dlb.obj, \ - do.obj,do_name.obj,do_wear.obj + ball.obj,bones.obj,botl.obj,calendar.obj,cfgfiles.obj,cmd.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, \ extralev.obj,files.obj,fountain.obj,getpos.obj,glyphs.obj,hack.obj, \ @@ -401,7 +404,7 @@ $(HACK_H) : $(INC)hack.h $(CONFIG_H) $(INC)align.h \ $(INC)monsters.h $(INC)timeout.h $(INC)trap.h \ $(INC)flag.h $(INC)rm.h $(INC)vision.h \ $(INC)display.h $(INC)engrave.h $(INC)rect.h $(INC)region.h \ - $(INC)winprocs.h $(INC)wintty.h $(INC)sys.h + $(INC)weight.h $(INC)winprocs.h $(INC)wintty.h $(INC)sys.h $(TOUCH) $(HACK_H) # VMS-specific code vmsmain.obj : $(VMS)vmsmain.c $(HACK_H) $(INC)dlb.h @@ -500,6 +503,7 @@ ball.obj : ball.c $(HACK_H) bones.obj : bones.c $(HACK_H) botl.obj : botl.c $(HACK_H) cmd.obj : cmd.c $(HACK_H) $(INC)func_tab.h +cfgfiles.obj : cfgfiles.c $(HACK_H) dbridge.obj : dbridge.c $(HACK_H) decl.obj : decl.c $(HACK_H) detect.obj : detect.c $(HACK_H) $(INC)artifact.h diff --git a/sys/vms/Makefile_src.vms b/sys/vms/Makefile_src.vms index b5a4d793e..bf7355722 100644 --- a/sys/vms/Makefile_src.vms +++ b/sys/vms/Makefile_src.vms @@ -114,10 +114,10 @@ HACK_H = $(addsuffix .h, $(addprefix $(INCL), $(CONFIGBASEH) $(HACKBASEH))) # all .c that are part of the main NetHack program and are not # operating-system or windowing-system specific. # Do not include date.c in this list. -HACKFILES := allmain alloc apply artifact attrib ball bones \ - botl calendar 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 \ +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 \ @@ -125,7 +125,7 @@ HACKFILES := allmain alloc apply artifact attrib ball bones \ nhlua nhlsel nhlobj objnam o_init objects \ options pager pickup pline polyself potion pray \ priest quest questpgr read rect region report restore \ - rip rnd role rumors save selvar sfstruct \ + rip rnd role rumors save selvar sfbase sfstruct \ shk shknam sit sounds \ sp_lev spell stairs steal steed strutil symbols sys teleport \ timeout topten track trap u_init utf8map \ @@ -319,7 +319,7 @@ hacklib.olb: $(HACKLIBOBJLIST) $(INCL)nhlua.h: echo "/* nhlua.h - generated by Makefile.vms */" > $@ @echo \#"include ""$(LUASRCINCDIR)lua.h""" >> $@ - @echo "LUA_API int (lua_error) (lua_State *L) NORETURN;" >>$@ + @echo "ATTRNORETURN LUA_API int (lua_error) (lua_State *L) NORETURN;" >>$@ @echo \#"include ""$(LUASRCINCDIR)lualib.h""" >> $@ @echo \#"include ""$(LUASRCINCDIR)lauxlib.h""" >> $@ @echo "/*nhlua.h*/" >> $@ @@ -327,7 +327,7 @@ $(INCL)nhlua.h: #$(INCL)nhlua.h: # echo "/* nhlua.h - generated by -vms9 */" > $@ # @echo \#"include ""sys$$common:[lua.include]lua.h""" >> $@ -# @echo "LUA_API int (lua_error) (lua_State *L) NORETURN;" >>$@ +# @echo "ATTRNORETURN LUA_API int (lua_error) (lua_State *L) NORETURN;" >>$@ # @echo \#"include ""sys$$common:[lua.include]lualib.h""" >> $@ # @echo \#"include ""sys$$common:[lua.include]lauxlib.h""" >> $@ # @echo "/*nhlua.h*/" >> $@ @@ -701,6 +701,7 @@ $(TARGETPFX)ball.obj: ball.c $(HACK_H) $(TARGETPFX)bones.obj: bones.c $(HACK_H) $(TARGETPFX)botl.obj: botl.c $(HACK_H) $(TARGETPFX)calendar.obj: calendar.c $(HACK_H) +$(TARGETPFX)cfgfiles.obj: cfgfiles.c $(HACK_H) $(TARGETPFX)cmd.obj: cmd.c $(HACK_H) $(INCL)func_tab.h $(TARGETPFX)coloratt.obj: coloratt.c $(HACK_H) $(TARGETPFX)dbridge.obj: dbridge.c $(HACK_H) @@ -750,7 +751,8 @@ $(TARGETPFX)mdlib.obj: mdlib.c $(CONFIG_H) $(INCL)permonst.h \ $(INCL)sndprocs.h $(INCL)seffects.h $(INCL)obj.h \ $(INCL)monst.h $(INCL)mextra.h $(INCL)you.h \ $(INCL)attrib.h $(INCL)prop.h $(INCL)skills.h \ - $(INCL)context.h $(INCL)flag.h $(INCL)dlb.h + $(INCL)context.h $(INCL)flag.h $(INCL)dlb.h \ + $(INCL)weight.h $(TARGETPFX)mhitm.obj: mhitm.c $(HACK_H) $(INCL)artifact.h $(TARGETPFX)mhitu.obj: mhitu.c $(HACK_H) $(INCL)artifact.h $(TARGETPFX)minion.obj: minion.c $(HACK_H) @@ -766,7 +768,7 @@ $(TARGETPFX)monmove.obj: monmove.c $(HACK_H) $(INCL)mfndpos.h \ $(TARGETPFX)monst.obj: monst.c $(CONFIG_H) $(INCL)permonst.h \ $(INCL)align.h $(INCL)monattk.h $(INCL)monflag.h \ $(INCL)monsters.h $(INCL)wintype.h $(INCL)sym.h \ - $(INCL)defsym.h $(INCL)color.h + $(INCL)defsym.h $(INCL)color.h $(INCL)weight.h $(TARGETPFX)mplayer.obj: mplayer.c $(HACK_H) $(TARGETPFX)mthrowu.obj: mthrowu.c $(HACK_H) $(TARGETPFX)muse.obj: muse.c $(HACK_H) @@ -803,7 +805,8 @@ $(TARGETPFX)role.obj: role.c $(HACK_H) $(TARGETPFX)rumors.obj: rumors.c $(HACK_H) $(INCL)dlb.h $(TARGETPFX)save.obj: save.c $(HACK_H) $(TARGETPFX)selvar.obj: selvar.c $(HACK_H) -$(TARGETPFX)sfstruct.obj: sfstruct.c $(HACK_H) +$(TARGETPFX)sfbase.obj: sfbase.c $(HACK_H) $(INCL)sfprocs.h $(INCL)sfmacros.h +$(TARGETPFX)sfstruct.obj: sfstruct.c $(HACK_H) $(INCL)sfprocs.h $(INCL)sfmacros.h $(TARGETPFX)shk.obj: shk.c $(HACK_H) $(TARGETPFX)shknam.obj: shknam.c $(HACK_H) $(TARGETPFX)sit.obj: sit.c $(HACK_H) $(INCL)artifact.h diff --git a/sys/vms/Makefile_top.vms b/sys/vms/Makefile_top.vms index 505b797b0..7a28ff098 100644 --- a/sys/vms/Makefile_top.vms +++ b/sys/vms/Makefile_top.vms @@ -314,7 +314,7 @@ dofiles-nodlb: $(INCL)nhlua.h: echo "/* nhlua.h - generated by top-level Makefile.vms */" > $@ @echo \#"include ""lua.h""" >> $@ - @echo "LUA_API int (lua_error) (lua_State *L) NORETURN;" >>$@ + @echo "ATTRNORETURN LUA_API int (lua_error) (lua_State *L) NORETURN;" >>$@ @echo \#"include ""lualib.h""" >> $@ @echo \#"include ""lauxlib.h""" >> $@ @echo "/*nhlua.h*/" >> $@ diff --git a/sys/vms/Makefile_utl.vms b/sys/vms/Makefile_utl.vms index 2585b1bb4..83c112c95 100644 --- a/sys/vms/Makefile_utl.vms +++ b/sys/vms/Makefile_utl.vms @@ -113,7 +113,7 @@ ODATE = $(OBJDIR)date.obj MAKEOBJS = []makedefs.obj $(OMONOBJ) $(ODATE) $(OALLOC) $(OPANIC) # object files for recovery utility -RECOVOBJS = $(TARGETPFX)recover.obj +RECOVOBJS = $(TARGETPFX)recover.obj $(TARGETPFX)recover_ver.obj # object files for the data librarian DLBOBJS = dlb_main.obj $(OBJDIR)dlb.obj $(OALLOC) $(OPANIC) @@ -140,9 +140,9 @@ $(HACKLIB): $(HACKLIBOBJS) [-.util]panic.obj # dependencies for makedefs # -makedefs.exe: $(HACKLIB) $(MAKEOBJS) placeholder.obj mdgrep.h +makedefs.exe: $(HACKLIB) $(MAKEOBJS) uplaceholder.obj mdgrep.h $(CLINK) $(LFLAGS) /EXE=$@ \ - $(addsuffix $(comma),$(MAKEOBJS)) placeholder.obj, \ + $(addsuffix $(comma),$(MAKEOBJS)) uplaceholder.obj, \ $(HACKLIB)/lib # note: the headers listed here are maintained manually rather than via @@ -158,10 +158,10 @@ makedefs.obj: makedefs.c $(SRC)mdlib.c $(CONFIG_H) \ $(INCL)dlb.h $(INCL)patchlevel.h mdgrep.h $(CC) $(CFLAGS) $(CSTD) makedefs.c /OBJ=$@ -placeholder.c: - echo int makedefs_placeholder = 1; >$@ +uplaceholder.c: + echo int uplaceholder = 1; >$@ -placeholder.obj: placeholder.c +uplaceholder.obj: uplaceholder.c # Don't require perl to build; that is why mdgrep.h is spelled wrong below. @@ -193,12 +193,18 @@ panic.obj: panic.c $(CONFIG_H) # # dependencies for recover # -$(TARGETPFX)recover.exe: $(RECOVOBJS) - $(TARGET_CLINK) $(TARGET_LFLAGS) /EXE=$@ $(RECOVOBJS) $(LIBS) +$(TARGETPFX)recover.exe: $(HACKLIB) $(RECOVOBJS) uplaceholder.obj + $(TARGET_CLINK) $(TARGET_LFLAGS) /EXE=$@ \ + $(addsuffix $(comma),$(RECOVOBJS)) uplaceholder.obj, \ + $(HACKLIB)/lib $(LIBS) $(TARGETPFX)recover.obj: recover.c $(CONFIG_H) $(TARGET_CC) $(TARGET_CFLAGS) $(CSTD) recover.c /OBJ=$@ +$(TARGETPFX)recover_ver.obj: [-.src]version.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(CSTD) \ + /DEFINE=(MINIMAL_FOR_RECOVER) \ + [-.src]version.c /OBJ=$@ # dependencies for dlb # diff --git a/sys/vms/vmsbuild.com b/sys/vms/vmsbuild.com index b9f8cc721..737fbf015 100755 --- a/sys/vms/vmsbuild.com +++ b/sys/vms/vmsbuild.com @@ -377,7 +377,7 @@ $ set file/att=(RFM:STM) [-.include]nhlua.h $ open/Append f [-.include]nhlua.h $ write f "/* nhlua.h - generated by vmsbuild.com */" $ write f "#include ""[-.lib.lua''luaver'.src]lua.h""" -$ write f "LUA_API int (lua_error) (lua_State *L) NORETURN;" +$ write f "ATTRNORETURN LUA_API int (lua_error) (lua_State *L) NORETURN;" $ write f "#include ""[-.lib.lua''luaver'.src]lualib.h""" $ write f "#include ""[-.lib.lua''luaver'.src]lauxlib.h""" $ write f "/*nhlua.h*/" diff --git a/sys/vms/vmsfiles.c b/sys/vms/vmsfiles.c index 885b4c5f1..38a80b9a8 100644 --- a/sys/vms/vmsfiles.c +++ b/sys/vms/vmsfiles.c @@ -8,7 +8,6 @@ * routines or substitute for ones where we want behavior modification. */ #include "config.h" -#include #ifdef VMSVSI #include diff --git a/sys/vms/vmsmail.c b/sys/vms/vmsmail.c index d29fdb888..24f9ad5fa 100644 --- a/sys/vms/vmsmail.c +++ b/sys/vms/vmsmail.c @@ -20,7 +20,6 @@ struct mail_info *parse_next_broadcast(void); #ifdef MAIL #include "wintype.h" #include "winprocs.h" -#include #include #include #ifndef __GNUC__ diff --git a/sys/vms/vmsmain.c b/sys/vms/vmsmain.c index dcb681caa..f540d3190 100644 --- a/sys/vms/vmsmain.c +++ b/sys/vms/vmsmain.c @@ -80,14 +80,8 @@ main(int argc, char *argv[]) exit(EXIT_SUCCESS); if (argcheck(argc, argv, ARG_SHOWPATHS) == 2) { -#ifdef CHDIR - chdirx((char *) 0, 0); -#endif - iflags.initoptions_noterminate = TRUE; - initoptions(); - iflags.initoptions_noterminate = FALSE; - reveal_paths(); - exit(EXIT_SUCCESS); + gd.deferred_showpaths = TRUE; + return 0; } if (argcheck(argc, argv, ARG_DEBUG) == 1) { argc--; diff --git a/sys/vms/vmsmisc.c b/sys/vms/vmsmisc.c index 2c1226174..fa705161c 100644 --- a/sys/vms/vmsmisc.c +++ b/sys/vms/vmsmisc.c @@ -16,8 +16,8 @@ int debuggable = 0; /* 1 if we can debug or show a call trace */ -ATTRNORETURN void vms_exit(int); -ATTRNORETURN void vms_abort(void); +ATTRNORETURN void vms_exit(int) NORETURN; +ATTRNORETURN void vms_abort(void) NORETURN; /* first arg should be unsigned long but has unsigned int */ diff --git a/sys/vms/vmstty.c b/sys/vms/vmstty.c index 9bd26a9a5..9b0051c0a 100644 --- a/sys/vms/vmstty.c +++ b/sys/vms/vmstty.c @@ -459,7 +459,7 @@ void settty(const char *s) { if (!bombing) - end_screen(); + term_end_screen(); if (s) raw_print(s); if (settty_needed) { @@ -513,7 +513,7 @@ setftty(void) sg.sm.tt2_char = tt2_char_active; setctty(); - start_screen(); + term_start_screen(); settty_needed = TRUE; } @@ -601,7 +601,7 @@ getwindowsz(void) #ifdef ENHANCED_SYMBOLS /* - * set in tty_start_screen() and allows + * set in term_start_screen() and allows * OS-specific changes that may be * required for support of utf8. * Currently a placeholder for VMS. diff --git a/sys/vms/vmsunix.c b/sys/vms/vmsunix.c index 69e5a3929..89b18a174 100644 --- a/sys/vms/vmsunix.c +++ b/sys/vms/vmsunix.c @@ -29,7 +29,6 @@ #include #undef umask #endif -#include extern int debuggable; /* defined in vmsmisc.c */ @@ -241,6 +240,7 @@ vms_define(const char *name, const char *value, int flag) switch (flag) { case ENV_JOB: /* job logical name */ tbl_dsc.len = strlen(tbl_dsc.adr = "LNM$JOB"); + FALLTHROUGH; /*FALLTHRU*/ case ENV_SUP: /* supervisor-mode process logical name */ result = lib$set_logical(&nam_dsc, &val_dsc, &tbl_dsc); @@ -603,8 +603,9 @@ vmscond lib$find_file_end(void **); /* collect a list of character names from all save files for this player */ int -vms_get_saved_games(const char *savetemplate, /* wildcarded save file name in native VMS format */ - char ***outarray) +vms_get_saved_games( + const char *savetemplate, /* wildcarded save name in native VMS format */ + char ***outarray) { struct dsc in, out; unsigned short l; diff --git a/sys/windows/Makefile.mingw32 b/sys/windows/GNUmakefile similarity index 76% rename from sys/windows/Makefile.mingw32 rename to sys/windows/GNUmakefile index 0c9a91e34..aac5608d4 100644 --- a/sys/windows/Makefile.mingw32 +++ b/sys/windows/GNUmakefile @@ -1,11 +1,11 @@ -# NetHack 3.7 Makefile.mingw32 +# NetHack 3.7 GNUmakefile # Copyright (c) 2022 by Feiyun Wang #-Copyright (c) 2022 by Michael Allison # NetHack may be freely redistributed. See license for details. # #============================================================================== # -# Win32 Compilers Tested with this Makefile.mingw32: +# Win32 Compilers Tested with this GNUmakefile: # mingw-w64 # from: # https://sourceforge.net/p/mingw-w64/wiki2/GeneralUsageInstructions/ @@ -97,6 +97,8 @@ GCC_EXTRA_WARNINGS = N # #============================================================================== +$(info Using $(lastword $(MAKEFILE_LIST))) + SKIP_NETHACKW = N USE_LUADLL = Y WANT_LUAC = N @@ -299,9 +301,12 @@ USE_DLB = Y ifdef CI_COMPILER cc = gcc -c +cxx = g++ -c ld = gcc +DEBUGINFO = N else cc = gcc -c +cxx = g++ -c ld = gcc endif ifeq "$(MSYSTEM)" "MINGW32" @@ -323,9 +328,11 @@ CFLAGS = -mms-bitfields -I../include -I../sys/windows LDFLAGS = ifeq "$(DEBUGINFO)" "Y" CFLAGS += -g -D_DEBUG +LIBUCRT = -lucrtbased else CFLAGS += -DNDEBUG LDFLAGS += -s +LIBUCRT= endif ifeq "$(USE_DLB)" "Y" @@ -334,15 +341,20 @@ else DLBFLG = endif + ifeq "$(GCC_EXTRA_WARNINGS)" "Y" # # These match the warnings enabled on the linux.370 and macOS.370 hints builds # -CFLAGSXTRA = -Wall -Wextra -Wno-missing-field-initializers -Wreturn-type \ - -Wunused -Wformat -Wswitch -Wshadow -Wwrite-strings -pedantic \ - -Wmissing-declarations -Wformat-nonliteral -Wunreachable-code \ - -Wimplicit -Wimplicit-function-declaration -Wimplicit-int \ - -Wmissing-prototypes -Wold-style-definition -Wstrict-prototypes +CFLAGSXTRA = -Wall -Wextra -Wreturn-type -Wunused -Wswitch -Wshadow \ + -Wwrite-strings -pedantic -Wmissing-declarations \ + -Wunreachable-code -Wimplicit \ + -Wimplicit-function-declaration -Wimplicit-int \ + -Wmissing-prototypes -Wold-style-definition \ + -Wstrict-prototypes -Wnonnull -Wformat-overflow \ + -Wmissing-parameter-type -Wimplicit-fallthrough \ + -Wno-cast-function-type -Wno-format + CPPFLAGSXTRA = -Wall -Wextra -Wno-missing-field-initializers -Wreturn-type \ -Wunused -Wformat -Wswitch -Wshadow -Wwrite-strings -pedantic \ -Wmissing-declarations -Wformat-nonliteral -Wunreachable-code @@ -355,7 +367,8 @@ CONSOLEDEF = $(COMMONDEF) -D_CONSOLE # To build util targets CFLAGSU = $(CFLAGS) $(CONSOLEDEF) $(DLBFLG) -LIBS = -lcomctl32 -lgdi32 -lole32 -lshell32 -luuid -lwinmm -lbcrypt +LIBS = -lcomctl32 -lgdi32 -lole32 -lshell32 -luserenv -luuid -lwinmm \ + -lrpcrt4 -lbcrypt $(LIBUCRT) $(GAMEDIR): @mkdir -p $@ @@ -426,9 +439,9 @@ CLEAN_FILE += $(LUATARGETS) $(LUAOBJS) $(OLUA)/lua.o $(OLUA)/luac.o NHLUAH = $(INCL)/nhlua.h $(NHLUAH): - echo "/* nhlua.h - generated by Makefile.mingw32 */" > $@ + echo "/* nhlua.h - generated by GNUmakefile */" > $@ @echo "#include \"$(LUASRC)/lua.h\"" >> $@ - @echo "LUA_API int (lua_error) (lua_State *L) NORETURN;" >>$@ + @echo "ATTRNORETURN LUA_API int (lua_error) (lua_State *L) NORETURN;" >>$@ @echo "#include \"$(LUASRC)/lualib.h\"" >> $@ @echo "#include \"$(LUASRC)/lauxlib.h\"" >> $@ @echo "/*nhlua.h*/" >> $@ @@ -442,6 +455,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-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 \ @@ -515,7 +529,7 @@ CLEAN_FILE += $(MTARGETS) $(MOBJS) # Recover #========================================== OR = $(OBJ) -ROBJS = $(OR)/recover.o +ROBJS = $(OR)/recover.o $(OR)/rversion.o RTARGETS = $(GAMEDIR)/recover.txt $(GAMEDIR)/recover.exe recover: $(RTARGETS) @@ -523,12 +537,15 @@ recover: $(RTARGETS) $(GAMEDIR)/recover.txt: $(DOC)/recover.txt | $(GAMEDIR) cp $< $@ -$(GAMEDIR)/recover.exe: $(ROBJS) | $(GAMEDIR) +$(GAMEDIR)/recover.exe: $(ROBJS) $(HLHACKLIB) | $(GAMEDIR) $(ld) $(LDFLAGS) $^ -o$@ $(OR)/recover.o: $(U)recover.c | $(OR) $(cc) $(CFLAGSU) $< -o$@ +$(OR)/rversion.o: $(SRC)/version.c | $(OR) + $(cc) $(CFLAGSU) -DMINIMAL_FOR_RECOVER $< -o$@ + # $(OR): # @mkdir -p $@ @@ -538,6 +555,10 @@ CLEAN_FILE += $(RTARGETS) $(ROBJS) # PDCurses #========================================== ifeq "$(ADD_CURSES)" "Y" + +#https://github.com/Bill-Gray/PDCursesMod/issues/333 +PDCMOD_WORKAROUND = -std=gnu17 + PDCCOMMONSRC = addch addchstr addstr attr beep bkgd border clear \ color debug delch deleteln getch getstr getyx inch \ inchstr initscr inopts insch insstr kernel keyname \ @@ -560,10 +581,10 @@ $(PDCLIB): $(PDCOBJS) ar rcs $@ $^ $(OP)/%.o: $(PDCSRC)/%.c | $(OP) - $(cc) $(CFLAGS) $(PDCURSESFLAGS) $(PDCINCL) -D_LIB $< -o$@ + $(cc) $(CFLAGS) $(PDCMOD_WORKAROUND) $(PDCURSESFLAGS) $(PDCINCL) -D_LIB $< -o$@ $(OP)/%.o: $(PDCWINCON)/%.c | $(OP) - $(cc) $(CFLAGS) $(PDCURSESFLAGS) $(PDCINCL) -I$(PDCWINCON) -D_LIB $< -o$@ + $(cc) $(CFLAGS) $(PDCMOD_WORKAROUND) $(PDCURSESFLAGS) $(PDCINCL) -I$(PDCWINCON) -D_LIB $< -o$@ $(OP): @mkdir -p $@ @@ -585,10 +606,10 @@ $(PDCWLIB): $(PDCWOBJS) ar rcs $@ $^ $(OPW)/%.o: $(PDCSRC)/%.c | $(OPW) - $(cc) $(CFLAGS) $(PDCURSESFLAGS) $(PDCINCL) -D_LIB $< -o$@ + $(cc) $(CFLAGS) $(PDCMOD_WORKAROUND) $(PDCURSESFLAGS) $(PDCINCL) -D_LIB $< -o$@ $(OPW)/%.o: $(PDCWINGUI)/%.c | $(OPW) - $(cc) $(CFLAGS) $(PDCURSESFLAGS) $(PDCINCL) -I$(PDCWINGUI) -D_LIB $< -o$@ + $(cc) $(CFLAGS) $(PDCMOD_WORKAROUND) $(PDCURSESFLAGS) $(PDCINCL) -I$(PDCWINGUI) -D_LIB $< -o$@ $(OPW): @mkdir -p $@ @@ -731,6 +752,203 @@ $(MSWIN)/NetHack.ico: $(U)uudecode.exe $(MSWSYS)/nhico.uu CLEAN_FILE += $(UTARGETS) $(UOBJS) +#========================================== +# sfctool +#========================================== +# +# Requires universal-ctags. +# pacman -S ctags +# + +.PHONY: sfctool + +CONFIG_H = ../include/color.h ../include/config.h ../include/config1.h \ + ../include/coord.h ../include/cstd.h ../include/fnamesiz.h \ + ../include/global.h ../include/integer.h ../include/micro.h \ + ../include/patchlevel.h ../include/pcconf.h \ + ../include/tradstdc.h ../include/unixconf.h \ + ../include/vmsconf.h ../include/warnings.h \ + ../include/windconf.h +HACK_H = $(CONFIG_H) ../include/align.h ../include/artilist.h \ + ../include/attrib.h ../include/botl.h ../include/context.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/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 \ + ../include/objects.h ../include/permonst.h ../include/prop.h \ + ../include/quest.h ../include/rect.h ../include/region.h \ + ../include/rm.h ../include/savefile.h ../include/sfprocs.h \ + ../include/seffects.h ../include/selvar.h \ + ../include/skills.h ../include/sndprocs.h ../include/spell.h \ + ../include/stairs.h ../include/sym.h ../include/sys.h \ + ../include/timeout.h ../include/trap.h ../include/vision.h \ + ../include/weight.h ../include/winprocs.h \ + ../include/wintype.h ../include/you.h ../include/youprop.h + +OSFC = $(O)sfctool +SFCTARGETS = $(U)sftags.exe $(GAMEDIR)/sfctool.exe +SFCCOREOBJS = +SFCCOREOBJS = sfctool sf-alloc sf-artifact sf-calendar sf-cfgfiles sfdata \ + sf-date sf-decl sf-dungeon sf-end sf-engrave \ + sf-files sf-light sf-mdlib sf-mkmaze sf-mkroom sf-monst \ + sf-nhlua sf-objects sf-o_init panic sf-region sf-restore \ + sf-rumors sfbase sfexpasc sf-struct strutil sf-sys \ + sf-timeout sf-track sf-version sf-worm sf-windsys +SFCTOOLOBJS = $(addprefix $(OSFC)/, $(addsuffix .o, $(SFCCOREOBJS))) +SFCTOOLBIN = $(GAMEDIR)/sfctool.exe +SFFLAGS = -DSFCTOOL -DNOPANICTRACE -DNOCRASHREPORT -DNO_CHRONICLE + +sfctool: $(SFCTARGETS) + +#$(info SFCTOOLOBJS=$(SFCTOOLOBJS)) + +$(SFCTOOLBIN): $(HLHACKLIB) $(SFCTOOLOBJS) + $(ld) $(LDFLAGS) $^ $(HLHACKLIB) $(LIBS) -o$@ + +$(OSFC)/sfctool.o: $(U)sfctool.c $(HACK_H) $(INCL)/sfprocs.h | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(U)sfctool.c -o$@ +$(OSFC)/sf-alloc.o: $(SRC)/alloc.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/alloc.c -o$@ +$(OSFC)/sf-artifact.o: $(SRC)/artifact.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/artifact.c -o$@ +$(OSFC)/sf-calendar.o: $(SRC)/calendar.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/calendar.c -o$@ +$(OSFC)/sf-cfgfiles.o: $(SRC)/cfgfiles.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/cfgfiles.c -o$@ +$(OSFC)/sfdata.o: $(U)sfdata.c $(HACK_H) $(INCL)/sfprocs.h $(INCL)/sfproto.h | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(U)sfdata.c -o$@ +$(OSFC)/sf-date.o: $(SRC)/date.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/date.c -o$@ +$(OSFC)/sf-decl.o: $(SRC)/decl.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/decl.c -o$@ +$(OSFC)/sf-dungeon.o: $(SRC)/dungeon.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/dungeon.c -o$@ +$(OSFC)/sf-end.o: $(SRC)/end.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/end.c -o$@ +$(OSFC)/sf-engrave.o: $(SRC)/engrave.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/engrave.c -o$@ +$(OSFC)/sf-files.o: $(SRC)/files.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/files.c -o$@ +$(OSFC)/sf-light.o: $(SRC)/light.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/light.c -o$@ +$(OSFC)/sf-mdlib.o: $(SRC)/mdlib.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/mdlib.c -o$@ +$(OSFC)/sf-mkmaze.o: $(SRC)/mkmaze.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/mkmaze.c -o$@ +$(OSFC)/sf-mkroom.o: $(SRC)/mkroom.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/mkroom.c -o$@ +$(OSFC)/sf-monst.o: $(SRC)/monst.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/monst.c -o$@ +$(OSFC)/sf-nhlua.o: $(SRC)/nhlua.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/nhlua.c -o$@ +$(OSFC)/sf-objects.o: $(SRC)/objects.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/objects.c -o$@ +$(OSFC)/sf-o_init.o: $(SRC)/o_init.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/o_init.c -o$@ +$(OSFC)/panic.o: $(U)panic.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(U)panic.c -o$@ +$(OSFC)/sf-region.o: $(SRC)/region.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/region.c -o$@ +$(OSFC)/sf-restore.o: $(SRC)/restore.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/restore.c -o$@ +$(OSFC)/sf-rumors.o: $(SRC)/\rumors.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/rumors.c -o$@ +$(OSFC)/sfbase.o: $(SRC)/sfbase.c $(HACK_H) $(INCL)/sfprocs.h $(INCL)/sfmacros.h | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/sfbase.c -o$@ +$(OSFC)/sfexpasc.o: $(U)sfexpasc.c $(HACK_H) $(INCL)/sfprocs.h $(INCL)/sfproto.h $(INCL)/sfmacros.h | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(U)sfexpasc.c -o$@ +$(OSFC)/sf-struct.o: $(SRC)/sfstruct.c $(HACK_H) $(INCL)/sfprocs.h $(INCL)/sfmacros.h | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/sfstruct.c -o$@ +$(OSFC)/strutil.o: $(SRC)/strutil.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/strutil.c -o$@ +$(OSFC)/sf-sys.o: $(SRC)/sys.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/sys.c -o$@ +$(OSFC)/sf-timeout.o: $(SRC)/timeout.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/timeout.c -o$@ +$(OSFC)/sf-track.o: $(SRC)/track.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/track.c -o$@ +$(OSFC)/sf-version.o: $(SRC)/version.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/version.c -o$@ +$(OSFC)/sf-worm.o: $(SRC)/worm.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(SRC)/worm.c -o$@ +$(OSFC)/sf-windsys.o: $(MSWSYS)/windsys.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) $(SFFLAGS) -c $(MSWSYS)/windsys.c -o$@ + +CTAGSCMD = ctags +CTAGSOPT = --language-force=c --sort=no -D"Bitfield(x,n)=unsigned x : n" --excmd=pattern + +$(U)sftags.exe: $(HLHACKLIB) $(OSFC)/sftags.o + $(ld) $(LDFLAGS) -mwindows $(OSFC)/sftags.o $(HLHACKLIB) $(LIBS) -o$@ +$(OSFC)/sftags.o: $(U)sftags.c $(HACK_H) | $(OSFC) + $(cc) $(CFLAGSU) -c $(U)sftags.c -o$@ +$(INCL)/sfproto.h: $(U)sf.tags $(U)sftags.exe + $(U)sftags +$(U)sfdata.c: $(U)sf.tags $(U)sftags.exe + $(U)sftags + +# dependencies for sftags +# +CTAGDEP = $(INCL)/align.h $(INCL)/artifact.h $(SRC)/artifact.c \ + $(INCL)/artilist.h $(INCL)/attrib.h $(SRC)/bones.c \ + $(INCL)/context.h $(INCL)/coord.h $(INCL)/decl.h \ + $(SRC)/decl.c $(INCL)/dungeon.h $(INCL)/engrave.h \ + $(SRC)/engrave.c $(INCL)/flag.h $(INCL)/func_tab.h \ + $(INCL)/global.h $(INCL)/hack.h $(INCL)/mextra.h \ + $(INCL)/mkroom.h $(INCL)/monst.h $(INCL)/defsym.h \ + $(INCL)/obj.h $(INCL)/objclass.h $(INCL)/prop.h \ + $(INCL)/quest.h $(INCL)/rect.h $(INCL)/region.h \ + $(INCL)/rm.h $(INCL)/skills.h $(INCL)/spell.h \ + $(INCL)/stairs.h $(INCL)/sys.h $(INCL)/timeout.h \ + $(INCL)/trap.h $(INCL)/you.h $(INCL)/wintype.h + +$(U)sf.tags: $(CTAGDEP) + $(CTAGSCMD) $(CTAGSOPT) -f $@ $(INCL)/align.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/artifact.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(SRC)/artifact.c + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/artilist.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/attrib.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(SRC)/bones.c + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/context.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/coord.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/decl.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(SRC)/decl.c + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/dungeon.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/engrave.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(SRC)/engrave.c + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/flag.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/func_tab.h + $(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)/mkroom.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/monst.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/defsym.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/obj.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/objclass.h +# $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/permonst.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/prop.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/quest.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/rect.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/region.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/rm.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/skills.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/spell.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/stairs.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/sys.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/timeout.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/trap.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/you.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)/wintype.h + +$(OSFC): + @mkdir -p $@ + +CLEAN_DIR += $(OSFC) +CLEAN_FILE += $(SFCTARGETS) $(SFCTOOLOBJS) $(U)sf.tags $(U)sfdata.c $(INCL)/sfproto.h + + #========================================== # Data librarian #========================================== @@ -830,7 +1048,7 @@ else # GIT_AVAILABLE no CURLLUASRC=http://www.lua.org/ftp/lua-5.4.6.tar.gz CURLLUADST=lua-5.4.6.tar.gz -CURLPDCSRC=https://github.com/Bill-Gray/PDCursesMod/archive/refs/tags/v4.4.0.zip +CURLPDCSRC=https://github.com/Bill-Gray/PDCursesMod/archive/refs/tags/v4.5.1.zip CURLPDCDST=$(PDCURSES).zip fetchlua: @@ -990,7 +1208,7 @@ endif # HAVE_SOUNDLIB # nethackw #========================================== COREOBJS = $(addsuffix .o, allmain alloc apply artifact attrib ball bones botl \ - calendar cmd coloratt cppregex \ + 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 \ @@ -1000,7 +1218,7 @@ COREOBJS = $(addsuffix .o, allmain alloc apply artifact attrib ball bones botl \ 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 sfstruct shk shknam sit selvar sounds sp_lev \ + safeproc 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 \ @@ -1142,12 +1360,13 @@ $(ONH)/%.o: $(SRC)/%.c $(NHLUAH) | $(ONH) TARGET_CPU = x64 NHV=370 PKGFILES = nethackrc.template Guidebook.txt license NetHack.exe NetHack.txt \ - NetHackW.exe opthelp nhdat370 record symbols sysconf.template + NetHackW.exe opthelp nhdat370 record symbols sysconf.template $(notdir $(LUADLL)) + FILESTOZIP = $(addprefix $(GAMEDIR)/, $(PKGFILES)) MAINZIP = $(PkgDir)/nethack-$(NHV)-win-$(TARGET_CPU)-msys2.zip package: binary $(FILESTOZIP) $(MAINZIP) - @echo NetHack Windows package created: $(MAINZIP) + @echo NetHack Windows package created: $(MAINZIP) $(MAINZIP): $(FILESTOZIP) | $(PkgDir) /c/Windows/System32/tar -a -cf $(MAINZIP) -C $(GAMEDIR) $(PKGFILES) @@ -1292,6 +1511,6 @@ clean: @$(foreach dir, $(CLEAN_DIR), \ if [ -d $(dir) ] ; then rmdir -p --ignore $(dir) ; fi ; ) --include Makefile.mingw32.depend +-include GNUmakefile.depend -include .depend # end of file diff --git a/sys/windows/Makefile.mingw32.depend b/sys/windows/GNUmakefile.depend similarity index 94% rename from sys/windows/Makefile.mingw32.depend rename to sys/windows/GNUmakefile.depend index 71c925c67..c28f9eb1b 100644 --- a/sys/windows/Makefile.mingw32.depend +++ b/sys/windows/GNUmakefile.depend @@ -2,12 +2,12 @@ cce = gcc -E -MM -MT "$(@:d=o) $@" -# Copy all $(cc) commands w/ their targets from Makefile.mingw32, 3rd party code unnecessary +# Copy all $(cc) commands w/ their targets from GNUmakefile, 3rd party code unnecessary # Replace .o w/ .d, $(cc) w/ $(cce) # Check extraordinary "OBJ): " and handle them properly # Check all CLEAN_FILE lines, add obj files to $(OBJS4DEP) # Run -# mingw32-make depend +# make depend $(HL)/%.d: $(SRC)/%.c | $(HL) $(cce) $(CFLAGSU) $< -o$@ @@ -21,6 +21,9 @@ $(OM)/%.d: $(U)%.c $(NHLUAH) | $(OM) $(OR)/recover.d: $(U)recover.c | $(OR) $(cce) $(CFLAGSU) $< -o$@ +$(OR)/rversion.d: $(SRC)/version.c | $(OR) + $(cce) $(CFLAGSU) -DMINIMAL_FOR_RECOVER $< -o$@ + $(OT)/tiletxt.d: $(WSHR)/tilemap.c $(NHLUAH) | $(OT) $(cce) $(CFLAGSU) -DTILETEXT $< -o$@ diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index 0fd85885d..2d92eb582 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-2024 +# Copyright (c) NetHack Development Team 1993-2025 # #============================================================================== # 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.38 -# - Microsoft Visual Studio 2022 Community Edition v 17.11.2 +# - Microsoft Visual Studio 2019 Community Edition v 16.11.42 +# - Microsoft Visual Studio 2022 Community Edition v 17.14.13 # #============================================================================== # This is used for building two distinct executables of NetHack: @@ -35,7 +35,7 @@ default : package # Where do you want the game to be built (which folder)? # -GAMEDIR = ..\binary # Default game build directory +R_GAMEDIR = ..\binary # Default game build directory # # @@ -120,6 +120,21 @@ GIT_AVAILABLE = N # #============================================================================== # +# Notes about Makefile variables related to paths in this Makefile.nmake: +# There are R_ prefixed directory path variables. +# +# This only applies to directory path variables. +# +# Variable What's that mean, or what is it used for? +# ---------- -------------------------------------------------------------- +# R_ contains a path without a backslash at the end +# (no prefix) contains a path with a trailing backslash at the end +# +# For example: Usage: +# R_SRC ..\src $(R_SRC)\allmain.c => ..\src\allmain.c +# SRC ..\src\ $(SRC)allmain.c => ..\src\allmain.c +# +#============================================================================== # if next line is commented out, full compiler command lines will be output Q=@ @@ -150,29 +165,87 @@ NETHACK_VERSION="3.7.0" NHV=$(NETHACK_VERSION:.=) NHV=$(NHV:"=) +#============================================================================== # # Source directories. Makedefs hardcodes these, don't change them. # -INCL = ..\include # NetHack include files -DAT = ..\dat # NetHack data files -DOC = ..\doc # NetHack documentation files -UTIL = ..\util # Utility source -SRC = ..\src # Main source -SSYS = ..\sys\share # Shared system files -MSWSYS = ..\sys\windows # MS windows specific files -TTY = ..\win\tty # window port files (tty) -MSWIN = ..\win\win32 # window port files (win32) -WCURSES = ..\win\curses # window port files (curses) -WSHR = ..\win\share # Tile support files -QT = ..\win\Qt # QT support files -X11 = ..\win\X11 # X11 support files -LIBDIR = ..\lib # libraries and external bits -SUBM = ..\submodules # NetHack git submodules -SndWavDir = ..\sound\wav # sound files that get integrated -BinDir = ..\binary -PkgDir = ..\package +R_INCL = ..\include # NetHack include files +R_DAT = ..\dat # NetHack data files +R_DOC = ..\doc # NetHack documentation files +R_UTIL = ..\util # Utility source +R_SRC = ..\src # Main source +R_SSYS = ..\sys\share # Shared system files +R_MSWSYS = ..\sys\windows # MS windows specific files +R_TTY = ..\win\tty # window port files (tty) +R_MSWIN = ..\win\win32 # window port files (win32) +R_WCURSES = ..\win\curses # window port files (curses) +R_WSHR = ..\win\share # Tile support files +R_QT = ..\win\Qt # QT support files +R_X11 = ..\win\X11 # X11 support files +R_LIBDIR = ..\lib # libraries and external bits +R_SUBM = ..\submodules # NetHack git submodules +R_SndWavDir = ..\sound\wav # sound files that get integrated +R_BinDir = ..\binary +R_PkgDir = ..\package +R_SOUNDDIR = ..\sound +#R_GAMEDIR is user option up near the top +#!MESSAGE $(R_SRC) +#!MESSAGE $(R_GAMEDIR) +#============================================================================== +# Ordinary path variations with trailing backslash +# +BACKSLASH=^\ +INCL = $(R_INCL)$(BACKSLASH) +DAT = $(R_DAT)$(BACKSLASH) +UTIL = $(R_UTIL)$(BACKSLASH) +SRC = $(R_SRC)$(BACKSLASH) +SSYS = $(R_SSYS)$(BACKSLASH) +MSWSYS = $(R_MSWSYS)$(BACKSLASH) +TTY = $(R_TTY)$(BACKSLASH) +MSWIN = $(R_MSWIN)$(BACKSLASH) +WCURSES = $(R_WCURSES)$(BACKSLASH) +WSHR = $(R_WSHR)$(BACKSLASH) +QT = $(R_QT)$(BACKSLASH) +X11 = $(R_X11)$(BACKSLASH) +LIBDIR = $(R_LIBDIR)$(BACKSLASH) +SUBM = $(R_SUBM)$(BACKSLASH) +DOC = $(R_DOC)$(BACKSLASH) +SndWavDir = $(R_SndWavDir)$(BACKSLASH) +BinDir = $(R_BinDir)$(BACKSLASH) +PkgDir = $(R_PkgDir)$(BACKSLASH) +SOUNDDIR = $(R_SOUNDDIR)$(BACKSLASH) +GAMEDIR = $(R_GAMEDIR)$(BACKSLASH) + +#!MESSAGE $(SRC) +#!MESSAGE $(GAMEDIR) +#============================================================================== +# Unix path variations with trailing forward slash +# +#U_INCL = $(INCL:\=/) +#U_DAT = $(DAT:\=/) +#U_UTIL = $(UTIL:\=/) +#U_SRC = $(SRC:\=/) +#U_SSYS = $(SSYS:\=/) +#U_MSWSYS = $(MSWSYS:\=/) +#U_TTY = $(TTY:\=/) +#U_MSWIN = $(MSWIN:\=/) +#U_WCURSES = $(WCURSES:\=/) +#U_WSHR = $(WSHR:\=/) +#U_QT = $(QT:\=/) +#U_X11 = $(X11:\=/) +#U_LIBDIR = $(LIBDIR:\=/) +#U_SUBM = $(SUBM:\=/) +#U_DOC = $(DOC:\=/) +#U_SndWavDir = $(SndWavDir:\=/) +#U_BinDir = $(BinDir:\=/) +#U_PkgDir = $(PkgDir:\=/) +#U_SOUNDDIR = $(SOUNDDIR:\=/) +U_GAMEDIR = $(GAMEDIR:\=/) +# +#!MESSAGE $(U_SRC) +#!MESSAGE $(U_GAMEDIR) #============================================================================== # Sanity checks for prerequisite Lua and pdcursesmod # @@ -182,12 +255,18 @@ ADD_CURSES=N # First, Lua !IF "$(INTERNET_AVAILABLE)" == "Y" !IF "$(GIT_AVAILABLE)" == "Y" -LUATOP=$(SUBM)\lua -LUASRC=$(LUATOP) +R_LUATOP=$(SUBM)lua +LUATOP=$(SUBM)lua$(BACKSLASH) +R_LUASRC=$(R_LUATOP) +LUASRC=$(R_LUASRC)$(BACKSLASH) LUA_MAY_PROCEED=Y !ELSE # GIT_AVAILABLE -LUATOP = $(LIBDIR)\lua-$(LUAVER) -LUASRC = $(LUATOP)\src +R_LUATOP=$(LIBDIR)lua-$(LUAVER) +LUATOP=$(R_LUATOP)$(BACKSLASH) +#U_LUATOP=$(LUATOP:\=/) +R_LUASRC=$(LUATOP)src +LUASRC=$(R_LUASRC)$(BACKSLASH) +#U_LUASRC=$(LUASRC:\=/) LUA_MAY_PROCEED=Y !ENDIF # GIT_AVAILABLE !ELSE # INTERNET_AVAILABLE is not Y below @@ -195,13 +274,21 @@ LUA_MAY_PROCEED=Y # method (git or download). Check to see if it is available already, with # precedence given to ../submodules, then ../lib. # -!IF EXIST("$(SUBM)\lua\lua.h") -LUATOP=$(SUBM)\lua -LUASRC=$(LUATOP) +!IF EXIST("$(SUBM)lua\lua.h") +R_LUATOP=$(SUBM)lua +LUATOP=$(R_LUATOP)$(BACKSLASH) +#U_LUATOP=$(LUATOP:\=/) +R_LUASRC=$(LUATOP) +LUASRC=$(LUATOP)$(BACKSLASH) +#U_LUASRC=$(LUASRC:\=/) LUA_MAY_PROCEED=Y -!ELSEIF EXIST("$(LIBDIR)\lua-$(LUAVER)\src\lua.h") -LUATOP = $(LIBDIR)\lua-$(LUAVER) -LUASRC = $(LUATOP)\src +!ELSEIF EXIST("$(LIBDIR)lua-$(LUAVER)\src\lua.h") +R_LUATOP=$(LIBDIR)lua-$(LUAVER) +LUATOP=$(R_LUATOP)$(BACKSLASH) +#U_LUATOP=$(LUATOP:\=/) +R_LUASRC=$(LUATOP)src +LUASRC=$(R_LUASRC)$(BACKSLASH) +#U_LUASRC=$(LUASRC:\=/) LUA_MAY_PROCEED=Y !ENDIF # Lua sources !IF "$(LUA_MAY_PROCEED)" == "Y" @@ -210,11 +297,16 @@ LUA_MAY_PROCEED=Y !ENDIF # LUA_MAY_PROCEED !ENDIF # INTERNET_AVAILABLE +#!MESSAGE R_LUATOP=$(R_LUATOP) +#!MESSAGE LUATOP=$(R_LUATOP) +#!MESSAGE R_LUASRC=$(R_LUASRC) +#!MESSAGE LUASRC=$(LUASRC) + !IF "$(LUA_MAY_PROCEED)" != "Y" !IF "$(INTERNET_AVAILABLE)" != "Y" !MESSAGE Your Makefile settings do not allow use of the internet to obtain Lua !ENDIF # INTERNET_AVAILABLE -!MESSAGE and no copy of Lua was found in either $(SUBM)\lua or $(LIBDIR)\lua-$(LUAVER). +!MESSAGE and no copy of Lua was found in either $(SUBM)lua or $(LIBDIR)lua-$(LUAVER). !MESSAGE Change your nmake command line to include: !MESSAGE GIT=1 !MESSAGE or modify your Makefile to set the following: @@ -231,37 +323,66 @@ WANT_CURSES=Y WANT_CURSES=Y !ENDIF +!IF "$(CURSES_CONSOLE)" != "Y" +!IF "$(CURSES_GRAPHICAL)" != "Y" +WANT_CURSES=N +ADD_CURSES=N +!ENDIF +!ENDIF + # Now, pdcursesmod !IF "$(WANT_CURSES)" == "Y" -PDCDIST=pdcursesmod -!IF "$(INTERNET_AVAILABLE)" == "Y" -!IF "$(GIT_AVAILABLE)" == "Y" -PDCURSES_TOP=$(SUBM)\$(PDCDIST) +R_PDCDIST=pdcursesmod +PDCDIST=$(R_PDCDIST)$(BACKSLASH) +#U_PDCDIST=$(PDCDIST:\=/) +! IF "$(INTERNET_AVAILABLE)" == "Y" +! IF "$(GIT_AVAILABLE)" == "Y" +#!MESSAGE debug spot1 +R_PDCURSES_TOP=$(SUBM)$(R_PDCDIST) +PDCURSES_TOP=$(R_PDCURSES_TOP)$(BACKSLASH) +#U_PDCURSES_TOP=$(PDCURSES_TOP:\=/) ADD_CURSES=Y -!ELSE # GIT_AVAILABLE -PDCURSES_TOP=$(LIBDIR)\$(PDCDIST) +! ELSE # GIT_AVAILABLE +#!MESSAGE debug spot2 +R_PDCURSES_TOP=$(LIBDIR)$(R_PDCDIST) +PDCURSES_TOP=$(R_PDCURSES_TOP)$(BACKSLASH) +#U_PDCURSES_TOP=$(PDCURSES_TOP:\=/) ADD_CURSES=Y -!ENDIF # GIT_AVAILABLE -!ELSE # INTERNET_AVAILABLE is not Y below +! ENDIF # GIT_AVAILABLE +! ELSE # NO internet available # Your Makefile settings do not allow $(PDCDIST) to be obtained by # git or by download. Check to see if it is available at one of # the expected locations already, with precedence given to ../submodules, # then ../lib. -# -!IF EXIST("$(SUBM)\$(PDCDIST)\curses.h") -PDCURSES_TOP=$(SUBM)\$(PDCDIST) +#! MESSAGE debug spot3 +! IF EXIST("$(LIBDIR)$(R_PDCDIST)curses.h") +R_PDCURSES_TOP=$(LIBDIR)$(PDCDIST) +PDCURSES_TOP=$(R_PDCURSES_TOP)$(BACKSLASH) ADD_CURSES=Y -!ELSEIF EXIST("$(LIBDIR)\$(PDCDIST)\curses.h") -PDCURSES_TOP=$(LIBDIR)\$(PDCDIST) +! ELSEIF EXIST("$(SUBM)$(R_PDCDIST)curses.h") +#!MESSAGE debug spot4 +R_PDCURSES_TOP=$(LIBDIR)$(PDCDIST) +PDCURSES_TOP=$(R_PDCURSES_TOP)$(BACKSLASH) ADD_CURSES=Y -!ENDIF # pdcursesmod sources available somewhere -!IF "$(ADD_CURSES)" == "Y" +! ENDIF # pdcursesmod sources available somewhere +! ENDIF # Internet dealt with +! IF "$(ADD_CURSES)" != "Y" +#!MESSAGE debug spot5 !MESSAGE Your Makefile settings do not allow $(PDCDIST) to be !MESSAGE obtained by git or by download, but a copy of pdcursesmod was !MESSAGE found in $(PDCURSES_TOP), !MESSAGE so that will be used. -!ENDIF # ADD_CURSES == Y -!ENDIF # INTERNET_AVAILABLE +! ELSE +#!MESSAGE debug spot6 +! IF EXIST("$(PDCURSES_TOP)curses.h") +INCLCURSES_H=-I$(R_PDCURSES_TOP) +! ELSE +# Cannot do it, sorry +PDCINCL= +INCLCURSES_H= +ADD_CURSES = N +! ENDIF # If we found a copy of curses.h +! ENDIF # ADD_CURSES == Y !ENDIF # WANT_CURSES !IF "$(ADD_CURSES)" != "Y" @@ -339,35 +460,51 @@ DLBDEF = # Object file directories. # -OBJTTY_B = objtty -OBJGUI_B = objgui -OBJUTIL_B = objutil -OBJLUA_B = objlua -OBJPDC_B = objpdc -OBJPDCC_B = objpdcc -OBJPDCG_B = objpdcg +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 -OBJTTY = $(OBJTTY_B)\$(TARGET_CPU) -OBJGUI = $(OBJGUI_B)\$(TARGET_CPU) -OBJUTIL = $(OBJUTIL_B)\$(TARGET_CPU) -OBJLUA = $(OBJLUA_B)\$(TARGET_CPU) -OBJPDC = $(OBJPDC_B)\$(TARGET_CPU) -OBJPDCC = $(OBJPDCC_B)\$(TARGET_CPU) -OBJPDCG = $(OBJPDCG_B)\$(TARGET_CPU) +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) + +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) + +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) # # Shorten up the location for some files # -OTTY = $(OBJTTY)^\ -OGUI = $(OBJGUI)^\ -OUTL = $(OBJUTIL)^\ -OLUA = $(OBJLUA)^\ -OPDC = $(OBJPDC)^\ -OPDCC = $(OBJPDCC)^\ -OPDCG = $(OBJPDCG)^\ +OTTY = $(OBJTTY) +OGUI = $(OBJGUI) +OUTL = $(OBJUTIL) +OLUA = $(OBJLUA) +OPDC = $(OBJPDC) +OPDCC = $(OBJPDCC) +OPDCG = $(OBJPDCG) -U = $(UTIL)^\ +U = $(UTIL) !IFDEF TEST_CROSSCOMPILE HOST=_host @@ -415,9 +552,9 @@ GITPREFIX = -DNETHACK_GIT_PREFIX=\"$(GIT_PREFIX)\" LUAVER=5.4.6 !ENDIF -LUALIB = $(LIBDIR)\lua$(LUAVER)-$(TARGET_CPU)-static.lib -LUADLL = $(LIBDIR)\lua$(LUAVER)-$(TARGET_CPU).dll -LUAINCL = /I$(LUASRC) +LUALIB = $(LIBDIR)lua$(LUAVER)-$(TARGET_CPU)-static.lib +LUADLL = $(LIBDIR)lua$(LUAVER)-$(TARGET_CPU).dll +R_LUAINCL = /I$(R_LUASRC) LUATARGETS = lua.exe $(LUADLL) $(LUALIB) LUASRCFILES = lapi.c lauxlib.c lbaselib.c lcode.c \ @@ -455,13 +592,20 @@ LUAOBJFILES = $(LUAOBJFILES) $(OLUA)lbitlib.o #================================================================= !IF "$(ADD_CURSES)" == "Y" -PDCURSES_CURSES_H = $(PDCURSES_TOP)\curses.h -PDCURSES_CURSPRIV_H = $(PDCURSES_TOP)\curspriv.h +PDCURSES_CURSES_H = $(PDCURSES_TOP)curses.h +PDCURSES_CURSPRIV_H = $(PDCURSES_TOP)curspriv.h PDCURSES_HEADERS = $(PDCURSES_CURSES_H) $(PDCURSES_CURSPRIV_H) -PDCSRC = $(PDCURSES_TOP)\pdcurses -PDCWINCON = $(PDCURSES_TOP)\wincon -PDCWINGUI = $(PDCURSES_TOP)\wingui -PDCDEP = $(PDCURSES_TOP)\curses.h +R_PDCSRC = $(PDCURSES_TOP)pdcurses +PDCSRC = $(R_PDCSRC)$(BACKSLASH) +!IF "$(CURSES_CONSOLE)" == "Y" +R_PDCWINCON = $(PDCURSES_TOP)wincon +PDCWINCON = $(PDCURSES_TOP)wincon$(BACKSLASH) +!ENDIF +!IF "$(CURSES_GRAPHICAL)" == "Y" +R_PDCWINGUI = $(PDCURSES_TOP)wingui +PDCWINGUI = $(PDCURSES_TOP)wingui$(BACKSLASH) +!ENDIF +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 \ @@ -473,107 +617,147 @@ PDCCOMMONOBJS = $(OPDC)addch.o $(OPDC)addchstr.o $(OPDC)addstr.o $(OPDC)attr.o $ $(OPDC)printw.o $(OPDC)refresh.o $(OPDC)scanw.o $(OPDC)scr_dump.o \ $(OPDC)scroll.o $(OPDC)slk.o $(OPDC)termattr.o $(OPDC)touch.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 - +!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 - -PDCINCL = /I$(PDCURSES_TOP) /I$(PDCSRC) PDCINCLGUI = /I$(PDCWINCON) -PDCINCLCON = /I$(PDCWINGUI) +!ENDIF !ELSE -PDCDEP = +PDCDEP= +PDCCOMMONOBJS= +PDCWINCONOBJS= +PDCWINGUIOBJS= +PDCINCL= +PDINCLGUI= +PDCINCLCON= !ENDIF #================================================================= -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)\decl.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)\mfndpos.h $(INCL)\micro.h $(INCL)\mkroom.h \ - $(INCL)\monattk.h $(INCL)\mondata.h $(INCL)\monflag.h $(INCL)\monst.h \ - $(INCL)\monsters.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)\sym.h $(INCL)\defsym.h $(INCL)\rm.h $(INCL)\selvar.h $(INCL)\sp_lev.h \ - $(INCL)\spell.h $(INCL)\stairs.h $(INCL)\sys.h $(INCL)\cstd.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)\sndprocs.h $(INCL)\seffects.h \ - $(INCL)\wintype.h $(INCL)\you.h $(INCL)\youprop.h - # all .c that are part of the main NetHack program and are not operating- or # windowing-system specific -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 \ - files.c fountain.c getpos.c glyphs.c hack.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 \ - nhlua.c nhlsel.c nhlobj.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 restore.c \ - rip.c rnd.c role.c rumors.c save.c selvar.c sfstruct.c \ - shk.c shknam.c sit.c sounds.c sp_lev.c spell.c \ - stairs.c steal.c steed.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 worm.c worn.c write.c zap.c -LUA_FILES = $(DAT)\air.lua $(DAT)\Arc-fila.lua $(DAT)\Arc-filb.lua \ - $(DAT)\Arc-goal.lua $(DAT)\Arc-loca.lua $(DAT)\Arc-strt.lua \ - $(DAT)\asmodeus.lua $(DAT)\astral.lua $(DAT)\baalz.lua \ - $(DAT)\Bar-fila.lua $(DAT)\Bar-filb.lua $(DAT)\Bar-goal.lua \ - $(DAT)\Bar-loca.lua $(DAT)\Bar-strt.lua $(DAT)\bigrm-1.lua \ - $(DAT)\bigrm-10.lua $(DAT)\bigrm-11.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 $(DAT)\Cav-fila.lua \ - $(DAT)\Cav-filb.lua $(DAT)\Cav-goal.lua $(DAT)\Cav-loca.lua \ - $(DAT)\Cav-strt.lua $(DAT)\dungeon.lua $(DAT)\earth.lua \ - $(DAT)\fakewiz1.lua $(DAT)\fakewiz2.lua $(DAT)\fire.lua \ - $(DAT)\Hea-fila.lua $(DAT)\Hea-filb.lua $(DAT)\Hea-goal.lua \ - $(DAT)\Hea-loca.lua $(DAT)\Hea-strt.lua $(DAT)\hellfill.lua \ - $(DAT)\juiblex.lua $(DAT)\Kni-fila.lua $(DAT)\Kni-filb.lua \ - $(DAT)\Kni-goal.lua $(DAT)\Kni-loca.lua $(DAT)\Kni-strt.lua \ - $(DAT)\knox.lua $(DAT)\medusa-1.lua $(DAT)\medusa-2.lua \ - $(DAT)\medusa-3.lua $(DAT)\medusa-4.lua $(DAT)\minefill.lua \ - $(DAT)\minend-1.lua $(DAT)\minend-2.lua $(DAT)\minend-3.lua \ - $(DAT)\minetn-1.lua $(DAT)\minetn-2.lua $(DAT)\minetn-3.lua \ - $(DAT)\minetn-4.lua $(DAT)\minetn-5.lua $(DAT)\minetn-6.lua \ - $(DAT)\minetn-7.lua $(DAT)\Mon-fila.lua $(DAT)\Mon-filb.lua \ - $(DAT)\Mon-goal.lua $(DAT)\Mon-loca.lua $(DAT)\Mon-strt.lua \ - $(DAT)\nhcore.lua $(DAT)\nhlib.lua $(DAT)\oracle.lua \ - $(DAT)\orcus.lua $(DAT)\Pri-fila.lua $(DAT)\Pri-filb.lua \ - $(DAT)\Pri-goal.lua $(DAT)\Pri-loca.lua $(DAT)\Pri-strt.lua \ - $(DAT)\quest.lua $(DAT)\Ran-fila.lua $(DAT)\Ran-filb.lua \ - $(DAT)\Ran-goal.lua $(DAT)\Ran-loca.lua $(DAT)\Ran-strt.lua \ - $(DAT)\Rog-fila.lua $(DAT)\Rog-filb.lua $(DAT)\Rog-goal.lua \ - $(DAT)\Rog-loca.lua $(DAT)\Rog-strt.lua $(DAT)\Sam-fila.lua \ - $(DAT)\Sam-filb.lua $(DAT)\Sam-goal.lua $(DAT)\Sam-loca.lua \ - $(DAT)\Sam-strt.lua $(DAT)\sanctum.lua $(DAT)\soko1-1.lua \ - $(DAT)\soko1-2.lua $(DAT)\soko2-1.lua $(DAT)\soko2-2.lua \ - $(DAT)\soko3-1.lua $(DAT)\soko3-2.lua $(DAT)\soko4-1.lua \ - $(DAT)\soko4-2.lua $(DAT)\themerms.lua $(DAT)\Tou-fila.lua \ - $(DAT)\Tou-filb.lua $(DAT)\Tou-goal.lua $(DAT)\Tou-loca.lua \ - $(DAT)\Tou-strt.lua $(DAT)\tower1.lua $(DAT)\tower2.lua \ - $(DAT)\tower3.lua $(DAT)\Val-fila.lua $(DAT)\Val-filb.lua \ - $(DAT)\Val-goal.lua $(DAT)\Val-loca.lua $(DAT)\Val-strt.lua \ - $(DAT)\valley.lua $(DAT)\water.lua $(DAT)\Wiz-fila.lua \ - $(DAT)\Wiz-filb.lua $(DAT)\Wiz-goal.lua $(DAT)\Wiz-loca.lua \ - $(DAT)\Wiz-strt.lua $(DAT)\wizard1.lua $(DAT)\wizard2.lua \ - $(DAT)\wizard3.lua $(DAT)\tut-1.lua $(DAT)\tut-2.lua +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 + +# 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 + +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-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 \ + $(DAT)fakewiz1.lua $(DAT)fakewiz2.lua $(DAT)juiblex.lua \ + $(DAT)knox.lua $(DAT)medusa-1.lua $(DAT)medusa-2.lua \ + $(DAT)medusa-3.lua $(DAT)medusa-4.lua $(DAT)minend-1.lua \ + $(DAT)minend-2.lua $(DAT)minend-3.lua $(DAT)minefill.lua \ + $(DAT)minetn-1.lua $(DAT)minetn-2.lua $(DAT)minetn-3.lua \ + $(DAT)minetn-4.lua $(DAT)minetn-5.lua $(DAT)minetn-6.lua \ + $(DAT)minetn-7.lua $(DAT)oracle.lua $(DAT)orcus.lua \ + $(DAT)sanctum.lua $(DAT)soko1-1.lua $(DAT)soko1-2.lua \ + $(DAT)soko2-1.lua $(DAT)soko2-2.lua $(DAT)soko3-1.lua \ + $(DAT)soko3-2.lua $(DAT)soko4-1.lua $(DAT)soko4-2.lua \ + $(DAT)tower1.lua $(DAT)tower2.lua $(DAT)tower3.lua \ + $(DAT)valley.lua $(DAT)wizard1.lua $(DAT)wizard2.lua \ + $(DAT)wizard3.lua $(DAT)nhcore.lua $(DAT)nhlib.lua \ + $(DAT)themerms.lua $(DAT)astral.lua $(DAT)air.lua \ + $(DAT)earth.lua $(DAT)fire.lua $(DAT)water.lua \ + $(DAT)hellfill.lua $(DAT)tut-1.lua $(DAT)tut-2.lua \ + $(DAT)Arc-goal.lua $(DAT)Bar-goal.lua $(DAT)Cav-goal.lua \ + $(DAT)Hea-goal.lua $(DAT)Kni-goal.lua $(DAT)Mon-goal.lua \ + $(DAT)Pri-goal.lua $(DAT)Ran-goal.lua $(DAT)Rog-goal.lua \ + $(DAT)Sam-goal.lua $(DAT)Tou-goal.lua $(DAT)Val-goal.lua \ + $(DAT)Wiz-goal.lua $(DAT)Arc-fila.lua $(DAT)Arc-filb.lua \ + $(DAT)Bar-fila.lua $(DAT)Bar-filb.lua $(DAT)Cav-fila.lua \ + $(DAT)Cav-filb.lua $(DAT)Hea-fila.lua $(DAT)Hea-filb.lua \ + $(DAT)Kni-fila.lua $(DAT)Kni-filb.lua $(DAT)Mon-fila.lua \ + $(DAT)Mon-filb.lua $(DAT)Pri-fila.lua $(DAT)Pri-filb.lua \ + $(DAT)Ran-fila.lua $(DAT)Ran-filb.lua $(DAT)Rog-fila.lua \ + $(DAT)Rog-filb.lua $(DAT)Sam-fila.lua $(DAT)Sam-filb.lua \ + $(DAT)Tou-fila.lua $(DAT)Tou-filb.lua $(DAT)Val-fila.lua \ + $(DAT)Val-filb.lua $(DAT)Wiz-fila.lua $(DAT)Wiz-filb.lua \ + $(DAT)Arc-loca.lua $(DAT)Bar-loca.lua $(DAT)Cav-loca.lua \ + $(DAT)Hea-loca.lua $(DAT)Kni-loca.lua $(DAT)Mon-loca.lua \ + $(DAT)Pri-loca.lua $(DAT)Ran-loca.lua $(DAT)Rog-loca.lua \ + $(DAT)Sam-loca.lua $(DAT)Tou-loca.lua $(DAT)Val-loca.lua \ + $(DAT)Wiz-loca.lua $(DAT)Arc-strt.lua $(DAT)Bar-strt.lua \ + $(DAT)Cav-strt.lua $(DAT)Hea-strt.lua $(DAT)Kni-strt.lua \ + $(DAT)Mon-strt.lua $(DAT)Pri-strt.lua $(DAT)Ran-strt.lua \ + $(DAT)Rog-strt.lua $(DAT)Sam-strt.lua $(DAT)Tou-strt.lua \ + $(DAT)Val-strt.lua $(DAT)Wiz-strt.lua $(DAT)dungeon.lua \ + $(DAT)quest.lua + +luawildcards = asmodeus.lua baalz.lua bigrm-*.lua castle.lua fakewiz?.lua \ + juiblex.lua knox.lua medusa-?.lua minend-?.lua minefill.lua \ + minetn-?.lua oracle.lua orcus.lua sanctum.lua soko?-?.lua tower?.lua \ + valley.lua wizard?.lua nhcore.lua nhlib.lua themerms.lua astral.lua \ + air.lua earth.lua fire.lua water.lua hellfill.lua tut-?.lua \ + ???-goal.lua ???-fil?.lua ???-loca.lua ???-strt.lua \ + dungeon.lua quest.lua + # -# Utility Objects. +# - Utility # MAKESRC = $(U)makedefs.c @@ -583,7 +767,7 @@ MAKEDEFSOBJS = $(OUTL)makedefs.o $(OUTL)monst$(HOST).o $(OUTL)objects$(HOST).o RECOVOBJS = $(OUTL)recover.o -TILEFILES = $(WSHR)\monsters.txt $(WSHR)\objects.txt $(WSHR)\other.txt +TILEFILES = $(WSHR)monsters.txt $(WSHR)objects.txt $(WSHR)other.txt # # These are not invoked during a normal game build in 3.7 @@ -600,7 +784,7 @@ GIFREADERS32_HOST = $(OUTL)gifrd32.o $(OUTL)alloc$(HOST).o $(OUTL)panic$(HOST) PPMWRITERS = $(OUTL)ppmwrite.o $(OUTL)alloc$(HOST).o $(OUTL)panic$(HOST).o -WINDHDR = $(MSWSYS)\win10.h $(MSWSYS)\winos.h $(MSWSYS)\win32api.h +WINDHDR = $(MSWSYS)win10.h $(MSWSYS)winos.h $(MSWSYS)win32api.h # # - Curses @@ -613,20 +797,21 @@ 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 -CURSESDEF1=-D"CURSES_GRAPHICS" -DPDC_NCMOUSE +PDCWINCONLIB = $(LIBDIR)$(PDCDIST)-wincon-$(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 -CURSESDEF1=-D"CURSES_GRAPHICS" -DPDC_NCMOUSE +PDCWINGUILIB = $(LIBDIR)$(PDCDIST)-wingui-$(TARGET_CPU)-static.lib +CURSESGUIDEF1=-D"CURSES_GRAPHICS" -DPDC_NCMOUSE #CURSESDEF2=-D"CURSES_BRIEF_INCLUDE" -DCHTYPE_32 CURSESDEF2=-DCURSES_UNICODE $(PDCURSESFLAGS) !ENDIF !ELSE -!UNDEF CURSDEF +!UNDEF CURSDEF1 +!UNDEF CURSDEF2 !UNDEF CURSESLIB !UNDEF CURSESOBJ !UNDEF CURSESWINCONOBJS @@ -642,14 +827,13 @@ OUTLHACKLIB = $(OUTL)hacklib-$(TARGET_CPU)-static.lib OTTYHACKLIB = $(OTTY)hacklib-$(TARGET_CPU)-static.lib OGUIHACKLIB = $(OGUI)hacklib-$(TARGET_CPU)-static.lib - # # - TTY # -TTYDEF= -D"_CONSOLE" -DWIN32CON $(CURSESDEF1) +TTYDEF= -D"_CONSOLE" -DWIN32CON $(CURSESCONDEF1) $(CURSESGUIDEF1) -RANDOMTTY = $(OTTY)random.o +#RANDOMTTY = $(OTTY)random.o MDLIBTTY = $(OTTY)mdlib.o DLBOBJTTY = $(OTTY)dlb.o REGEXTTY = $(OTTY)cppregex.o @@ -659,46 +843,41 @@ VVOBJTTY = $(OTTY)version.o SOBJTTY = $(OTTY)windmain.o $(OTTY)windsys.o $(OTTY)win10.o \ $(OTTY)safeproc.o -TTYOBJ = $(OTTY)topl.o $(OTTY)getline.o $(OTTY)wintty.o +TTYOBJTTY = $(OTTY)topl.o $(OTTY)getline.o $(OTTY)wintty.o -VTTYOBJ01 = $(OTTY)allmain.o $(OTTY)alloc.o $(OTTY)apply.o $(OTTY)artifact.o -VTTYOBJ02 = $(OTTY)attrib.o $(OTTY)ball.o $(OTTY)bones.o $(OTTY)botl.o -VTTYOBJ03 = $(OTTY)calendar.o $(OTTY)cmd.o $(OTTY)coloratt.o $(OTTY)dbridge.o -VTTYOBJ04 = $(OTTY)decl.o $(OTTY)detect.o $(OTTY)dig.o $(OTTY)display.o $(OTTY)do.o -VTTYOBJ05 = $(OTTY)do_name.o $(OTTY)do_wear.o $(OTTY)dog.o $(OTTY)dogmove.o $(OTTY)dokick.o -VTTYOBJ06 = $(OTTY)dothrow.o $(OTTY)drawing.o $(OTTY)dungeon.o $(OTTY)eat.o -VTTYOBJ07 = $(OTTY)end.o $(OTTY)engrave.o $(OTTY)exper.o $(OTTY)explode.o -VTTYOBJ08 = $(OTTY)extralev.o $(OTTY)files.o $(OTTY)fountain.o $(OTTY)getpos.o -VTTYOBJ09 = $(OTTY)glyphs.o $(OTTY)hack.o $(OTTY)insight.o $(OTTY)invent.o -VTTYOBJ10 = $(OTTY)isaac64.o $(OTTY)light.o $(OTTY)lock.o $(OTTY)mail.o -VTTYOBJ11 = $(OTTY)makemon.o $(OTTY)mcastu.o $(OTTY)mhitm.o $(OTTY)mhitu.o -VTTYOBJ12 = $(OTTY)minion.o $(OTTY)mklev.o $(OTTY)mkmap.o $(OTTY)mkmaze.o -VTTYOBJ13 = $(OTTY)mkobj.o $(OTTY)mkroom.o $(OTTY)mon.o $(OTTY)mondata.o -VTTYOBJ14 = $(OTTY)monmove.o $(OTTY)monst.o $(OTTY)mplayer.o $(OTTY)mthrowu.o -VTTYOBJ15 = $(OTTY)muse.o $(OTTY)music.o $(OTTY)o_init.o $(OTTY)objects.o -VTTYOBJ16 = $(OTTY)objnam.o $(OTTY)options.o $(OTTY)pager.o $(OTTY)pickup.o -VTTYOBJ17 = $(OTTY)pline.o $(OTTY)polyself.o $(OTTY)potion.o $(OTTY)pray.o -VTTYOBJ18 = $(OTTY)priest.o $(OTTY)quest.o $(OTTY)questpgr.o $(RANDOM) -VTTYOBJ19 = $(OTTY)read.o $(OTTY)rect.o $(OTTY)region.o $(OTTY)report.o $(OTTY)restore.o -VTTYOBJ20 = $(OTTY)rip.o $(OTTY)rnd.o $(OTTY)role.o $(OTTY)rumors.o -VTTYOBJ21 = $(OTTY)save.o $(OTTY)selvar.o $(OTTY)sfstruct.o $(OTTY)shk.o -VTTYOBJ22 = $(OTTY)shknam.o $(OTTY)sit.o $(OTTY)sounds.o $(OTTY)sp_lev.o -VTTYOBJ23 = $(OTTY)spell.o $(OTTY)stairs.o $(OTTY)steal.o $(OTTY)steed.o -VTTYOBJ24 = $(OTTY)strutil.o $(OTTY)symbols.o $(OTTY)sys.o $(OTTY)teleport.o $(OTTY)timeout.o -VTTYOBJ25 = $(OTTY)topten.o $(OTTY)track.o $(OTTY)trap.o $(OTTY)u_init.o -VTTYOBJ26 = $(OTTY)uhitm.o $(OTTY)utf8map.o $(OTTY)vault.o $(OTTY)vision.o -VTTYOBJ27 = $(OTTY)weapon.o $(OTTY)were.o $(OTTY)wield.o $(OTTY)windows.o -VTTYOBJ28 = $(OTTY)wizard.o $(OTTY)wizcmds.o $(OTTY)worm.o $(OTTY)worn.o -VTTYOBJ29 = $(OTTY)write.o $(OTTY)zap.o +COREOBJTTY = \ + $(OTTY)allmain.o $(OTTY)alloc.o $(OTTY)apply.o $(OTTY)artifact.o \ + $(OTTY)attrib.o $(OTTY)ball.o $(OTTY)bones.o $(OTTY)botl.o \ + $(OTTY)calendar.o $(OTTY)cfgfiles.o $(OTTY)cmd.o $(OTTY)coloratt.o \ + $(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 -OBJSTTY = $(MDLIBTTY) \ - $(VTTYOBJ01) $(VTTYOBJ02) $(VTTYOBJ03) $(VTTYOBJ04) $(VTTYOBJ05) \ - $(VTTYOBJ06) $(VTTYOBJ07) $(VTTYOBJ08) $(VTTYOBJ09) $(VTTYOBJ10) \ - $(VTTYOBJ11) $(VTTYOBJ12) $(VTTYOBJ13) $(VTTYOBJ14) $(VTTYOBJ15) \ - $(VTTYOBJ16) $(VTTYOBJ17) $(VTTYOBJ18) $(VTTYOBJ19) $(VTTYOBJ20) \ - $(VTTYOBJ21) $(VTTYOBJ22) $(VTTYOBJ23) $(VTTYOBJ24) $(VTTYOBJ25) \ - $(VTTYOBJ26) $(VTTYOBJ27) $(VTTYOBJ28) $(VTTYOBJ29) $(VTTYOBJ30) \ - $(REGEXTTY) +OBJSTTY = $(MDLIBTTY) $(COREOBJTTY) $(REGEXTTY) $(RANDOMTTY) ALLOBJTTY = $(SOBJTTY) $(DLBOBJTTY) $(OBJSTTY) $(VVOBJTTY) $(LUAOBJTTY) @@ -706,15 +885,15 @@ ALLOBJTTY = $(SOBJTTY) $(DLBOBJTTY) $(OBJSTTY) $(VVOBJTTY) $(LUAOBJTTY) # - GUI # -GUIDEF= -DTILES -DMSWIN_GRAPHICS -DNOTTYGRAPHICS $(CURSESDEF1) +GUIDEF= -DTILES -DMSWIN_GRAPHICS -DNOTTYGRAPHICS $(CURSESGUIDEF1) -ALL_GUIHDR = $(MSWIN)\mhaskyn.h $(MSWIN)\mhdlg.h $(MSWIN)\mhfont.h \ - $(MSWIN)\mhinput.h $(MSWIN)\mhmain.h $(MSWIN)\mhmap.h $(MSWIN)\mhmenu.h \ - $(MSWIN)\mhmsg.h $(MSWIN)\mhmsgwnd.h $(MSWIN)\mhrip.h \ - $(MSWIN)\mhsplash.h $(MSWIN)\mhstatus.h \ - $(MSWIN)\mhtext.h $(MSWIN)\resource.h $(MSWIN)\winMS.h +ALL_GUIHDR = $(MSWIN)mhaskyn.h $(MSWIN)mhdlg.h $(MSWIN)mhfont.h \ + $(MSWIN)mhinput.h $(MSWIN)mhmain.h $(MSWIN)mhmap.h $(MSWIN)mhmenu.h \ + $(MSWIN)mhmsg.h $(MSWIN)mhmsgwnd.h $(MSWIN)mhrip.h \ + $(MSWIN)mhsplash.h $(MSWIN)mhstatus.h \ + $(MSWIN)mhtext.h $(MSWIN)resource.h $(MSWIN)winMS.h -RANDOMGUI = $(OGUI)random.o +#RANDOMGUI = $(OGUI)random.o MDLIBGUI = $(OGUI)mdlib.o DLBOBJGUI = $(OGUI)dlb.o REGEXGUI = $(OGUI)cppregex.o @@ -729,44 +908,39 @@ GUIOBJ = $(OGUI)mhaskyn.o $(OGUI)mhdlg.o \ $(OGUI)mhmenu.o $(OGUI)mhmsgwnd.o $(OGUI)mhrip.o $(OGUI)mhsplash.o \ $(OGUI)mhstatus.o $(OGUI)mhtext.o $(OGUI)mswproc.o $(OGUI)NetHackW.o -VGUIOBJ01 = $(OGUI)allmain.o $(OGUI)alloc.o $(OGUI)apply.o $(OGUI)artifact.o -VGUIOBJ02 = $(OGUI)attrib.o $(OGUI)ball.o $(OGUI)bones.o $(OGUI)botl.o -VGUIOBJ03 = $(OGUI)calendar.o $(OGUI)cmd.o $(OGUI)coloratt.o $(OGUI)dbridge.o $(OGUI)decl.o -VGUIOBJ04 = $(OGUI)detect.o $(OGUI)dig.o $(OGUI)display.o $(OGUI)do.o $(OGUI)do_name.o -VGUIOBJ05 = $(OGUI)do_wear.o $(OGUI)dog.o $(OGUI)dogmove.o $(OGUI)dokick.o -VGUIOBJ06 = $(OGUI)dothrow.o $(OGUI)drawing.o $(OGUI)dungeon.o $(OGUI)eat.o -VGUIOBJ07 = $(OGUI)end.o $(OGUI)engrave.o $(OGUI)exper.o $(OGUI)explode.o -VGUIOBJ08 = $(OGUI)extralev.o $(OGUI)files.o $(OGUI)fountain.o $(OGUI)getpos.o -VGUIOBJ09 = $(OGUI)glyphs.o $(OGUI)hack.o $(OGUI)insight.o $(OGUI)invent.o -VGUIOBJ10 = $(OGUI)isaac64.o $(OGUI)light.o $(OGUI)lock.o $(OGUI)mail.o -VGUIOBJ11 = $(OGUI)makemon.o $(OGUI)mcastu.o $(OGUI)mhitm.o $(OGUI)mhitu.o -VGUIOBJ12 = $(OGUI)minion.o $(OGUI)mklev.o $(OGUI)mkmap.o $(OGUI)mkmaze.o -VGUIOBJ13 = $(OGUI)mkobj.o $(OGUI)mkroom.o $(OGUI)mon.o $(OGUI)mondata.o -VGUIOBJ14 = $(OGUI)monmove.o $(OGUI)monst.o $(OGUI)mplayer.o $(OGUI)mthrowu.o -VGUIOBJ15 = $(OGUI)muse.o $(OGUI)music.o $(OGUI)o_init.o $(OGUI)objects.o -VGUIOBJ16 = $(OGUI)objnam.o $(OGUI)options.o $(OGUI)pager.o $(OGUI)pickup.o -VGUIOBJ17 = $(OGUI)pline.o $(OGUI)polyself.o $(OGUI)potion.o $(OGUI)pray.o -VGUIOBJ18 = $(OGUI)priest.o $(OGUI)quest.o $(OGUI)questpgr.o $(RANDOMGUI) -VGUIOBJ19 = $(OGUI)read.o $(OGUI)rect.o $(OGUI)region.o $(OGUI)report.o $(OGUI)restore.o -VGUIOBJ20 = $(OGUI)rip.o $(OGUI)rnd.o $(OGUI)role.o $(OGUI)rumors.o -VGUIOBJ21 = $(OGUI)save.o $(OGUI)selvar.o $(OGUI)sfstruct.o $(OGUI)shk.o -VGUIOBJ22 = $(OGUI)shknam.o $(OGUI)sit.o $(OGUI)sounds.o $(OGUI)sp_lev.o -VGUIOBJ23 = $(OGUI)spell.o $(OGUI)stairs.o $(OGUI)steal.o $(OGUI)steed.o -VGUIOBJ24 = $(OGUI)strutil.o $(OGUI)symbols.o $(OGUI)sys.o $(OGUI)teleport.o $(OGUI)timeout.o -VGUIOBJ25 = $(OGUI)topten.o $(OGUI)track.o $(OGUI)trap.o $(OGUI)u_init.o -VGUIOBJ26 = $(OGUI)uhitm.o $(OGUI)utf8map.o $(OGUI)vault.o $(OGUI)vision.o -VGUIOBJ27 = $(OGUI)weapon.o $(OGUI)were.o $(OGUI)wield.o $(OGUI)windows.o -VGUIOBJ28 = $(OGUI)wizard.o $(OGUI)wizcmds.o $(OGUI)worm.o $(OGUI)worn.o -VGUIOBJ29 = $(OGUI)write.o $(OGUI)zap.o +COREOBJGUI = \ + $(OGUI)allmain.o $(OGUI)alloc.o $(OGUI)apply.o $(OGUI)artifact.o \ + $(OGUI)attrib.o $(OGUI)ball.o $(OGUI)bones.o $(OGUI)botl.o \ + $(OGUI)calendar.o $(OGUI)cfgfiles.o $(OGUI)cmd.o $(OGUI)coloratt.o \ + $(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 -OBJSGUI = $(MDLIBGUI) \ - $(VGUIOBJ01) $(VGUIOBJ02) $(VGUIOBJ03) $(VGUIOBJ04) $(VGUIOBJ05) \ - $(VGUIOBJ06) $(VGUIOBJ07) $(VGUIOBJ08) $(VGUIOBJ09) $(VGUIOBJ10) \ - $(VGUIOBJ11) $(VGUIOBJ12) $(VGUIOBJ13) $(VGUIOBJ14) $(VGUIOBJ15) \ - $(VGUIOBJ16) $(VGUIOBJ17) $(VGUIOBJ18) $(VGUIOBJ19) $(VGUIOBJ20) \ - $(VGUIOBJ21) $(VGUIOBJ22) $(VGUIOBJ23) $(VGUIOBJ24) $(VGUIOBJ25) \ - $(VGUIOBJ26) $(VGUIOBJ27) $(VGUIOBJ28) $(VGUIOBJ29) $(VGUIOBJ30) \ - $(REGEXGUI) +OBJSGUI = $(MDLIBGUI) $(COREOBJGUI) $(REGEXGUI) $(RANDOMGUI) ALLOBJGUI = $(SOBJGUI) $(DLBOBJGUI) $(OBJSGUI) $(VVOBJGUI) $(LUAOBJGUI) @@ -775,7 +949,7 @@ ALLOBJGUI = $(SOBJGUI) $(DLBOBJGUI) $(OBJSGUI) $(VVOBJGUI) $(LUAOBJGUI) # COMCTRL = comctl32.lib -OPTIONS_FILE = $(DAT)\options +OPTIONS_FILE = $(DAT)options !IFDEF TEST_CROSSCOMPILE DLBOBJ_HOST = $(OTTY)dlb$(HOST).o @@ -797,18 +971,21 @@ HAVE_SOUNDLIB=Y NEED_USERSOUNDS=Y NEED_SEAUTOMAP=Y NEED_WAV=Y -SOUNDLIBINCL = $(SOUNDLIBINCL) +R_SOUNDLIBINCL = $(R_SOUNDLIBINCL) +SOUNDLIBINCL = $(R_SOUNDLIBINCL)$(BACKSLASH) SOUNDLIBDEFS = $(SOUNDLIBDEFS) -DSND_LIB_WINDSOUND TTYSOUNDOBJS = $(TTYSOUNDOBJS) $(OTTY)windsound.o GUISOUNDOBJS = $(GUISOUNDOBJS) $(OGUI)windsound.o -#WINDSOUNDLIBDIR = +#WINDSOUNDLIBDIR = #TTYSOUNDLIBS = $(TTYSOUNDLIBS) #GUISOUNDLIBS = $(GUISOUNDLIBS) #WINDSOUNDLIBDIR = #WINDSOUNDLIBDLL = -WINDSOUNDDIR = ..\sound\windsound -SOUNDSRCS = $(SOUNDSRCS) $(WINDSOUNDDIR)\windsound.c -#GAMEDIRDLLS = $(GAMEDIRDLLS) $(GAMEDIR\$(WINDSOUNDLIBDLL) +R_WINDSOUNDDIR = $(SOUNDDIR)windsound +WINDSOUNDDIR=$(R_WINDSOUNDDIR)$(BACKSLASH) +#U_WINDSOUNDDIR = $(WINDSOUNDDIR:\=/) +SOUNDSRCS = $(SOUNDSRCS) $(WINDSOUNDDIR)windsound.c +#GAMEDIRDLLS = $(GAMEDIRDLLS) $(GAMEDIR)$(WINDSOUNDLIBDLL) !ENDIF SNDTEMP= @@ -825,40 +1002,47 @@ HAVE_SOUNDLIB=Y NEED_USERSOUNDS=Y NEED_SEAUTOMAP=Y NEED_WAV=Y -FMODROOT=..\lib\fmod\api\core +R_FMODROOT=..\lib\fmod\api\core +FMODROOT=$(R_FMODROOT)$(BACKSLASH) +U_FMODROOT=$(FMODROOT:\=/) FMODDLLBASENAME = fmod.dll -SOUNDLIBINCL = $(SOUNDLIBINCL) -I$(FMODROOT)/inc +R_SOUNDLIBINCL = $(R_SOUNDLIBINCL) -I$(FMODROOT)inc +SOUNDLIBINCL = $(R_SOUNDLIBINCL)$BACKSLASH) SOUNDLIBDEFS = $(SOUNDLIBDEFS) -DSND_LIB_FMOD TTYSOUNDOBJS = $(TTYSOUNDOBJS) $(OTTY)fmod.o GUISOUNDOBJS = $(GUISOUNDOBJS) $(OGUI)fmod.o -FMODLIBDIR = $(FMODROOT)\lib\$(TARGET_CPU) -FMODLIBDLL = $(FMODLIBDIR)\$(FMODDLLBASENAME) +R_FMODLIBDIR = $(FMODROOT)lib\$(TARGET_CPU) +FMODLIBDIR=$(R_FMODLIBDIR)$(BACKSLASH) +#U_FMODLIBDIR=$(FMODLIBDIR:\=/) +FMODLIBDLL = $(FMODLIBDIR)$(FMODDLLBASENAME) FMODFLAGS = -wd4201 -TTYSOUNDLIBS = $(TTYSOUNDLIBS) $(FMODLIBDIR)\fmod_vc.lib -GUISOUNDLIBS = $(GUISOUNDLIBS) $(FMODLIBDIR)\fmod_vc.lib +TTYSOUNDLIBS = $(TTYSOUNDLIBS) $(FMODLIBDIR)fmod_vc.lib +GUISOUNDLIBS = $(GUISOUNDLIBS) $(FMODLIBDIR)fmod_vc.lib GAMEDIRDLLS = $(GAMEDIRDLLS) $(GAMEDIR)\$(FMODDLLBASENAME) -FMODDIR = ..\sound\fmod -SOUNDSRCS = $(SOUNDSRCS) $(FMODDIR)\fmod.c +R_FMODDIR = $(SOUNDDIR)fmod +FMODDIR=$(R_FMODDIR)$(BACKSLASH) +#U_FMODDIR=$(FMODDIR:\=/) +SOUNDSRCS = $(SOUNDSRCS) $(FMODDIR)fmod.c !MESSAGE --------------------------------------------------------------------- !MESSAGE ** NOTES for fmod sound library integration ** !MESSAGE For fmod integration, this Makefile expects: -!MESSAGE fmod include directory : $(FMODROOT)\inc +!MESSAGE fmod include directory : $(FMODROOT)inc !MESSAGE fmod library directory : $(FMODLIBDIR) -!MESSAGE fmod library to link with : $(FMODLIBDIR)\fmod_vc.lib +!MESSAGE fmod library to link with : $(FMODLIBDIR)fmod_vc.lib !MESSAGE fmod library dll : $(FMODLIBDLL) !MESSAGE --------------------------------------------------------------------- FMOD_MISSING= -!IF !EXIST("$(FMODROOT)\inc") -FMOD_MISSING= $(FMOD_MISSING) $(FMODROOT)\inc -!MESSAGE Error: missing $(FMODROOT)\inc +!IF !EXIST("$(FMODROOT)inc") +FMOD_MISSING= $(FMOD_MISSING) $(FMODROOT)inc +!MESSAGE Error: missing $(FMODROOT)inc !ENDIF !IF !EXIST("$(FMODLIBDIR)") FMOD_MISSING= $(FMOD_MISSING) $(FMODLIBDIR) !MESSAGE Error: missing $(FMODLIBDIR) !ENDIF -!IF !EXIST("$(FMODLIBDIR)\fmod_vc.lib") -FMOD_MISSING= $(FMOD_MISSING) $(FMODLIBDIR)\fmod_vc.lib -!MESSAGE Error: missing $(FMODLIBDIR)\fmod_vc.lib +!IF !EXIST("$(FMODLIBDIR)fmod_vc.lib") +FMOD_MISSING= $(FMOD_MISSING) $(FMODLIBDIR)fmod_vc.lib +!MESSAGE Error: missing $(FMODLIBDIR)fmod_vc.lib !ENDIF !IF !EXIST("$(FMODLIBDLL)") FMOD_MISSING= $(FMOD_MISSING) $(FMODLIBDLL) @@ -874,38 +1058,38 @@ SNDTEMP= # SOUND Support #================================================================= -WAVS = $(SndWavDir)\se_squeak_A.wav $(SndWavDir)\se_squeak_B.wav \ - $(SndWavDir)\se_squeak_B_flat.wav $(SndWavDir)\se_squeak_C.wav \ - $(SndWavDir)\se_squeak_D.wav $(SndWavDir)\se_squeak_D_flat.wav \ - $(SndWavDir)\se_squeak_E.wav $(SndWavDir)\se_squeak_E_flat.wav \ - $(SndWavDir)\se_squeak_F.wav $(SndWavDir)\se_squeak_F_sharp.wav \ - $(SndWavDir)\se_squeak_G.wav $(SndWavDir)\se_squeak_G_sharp.wav \ - $(SndWavDir)\sound_Bell.wav $(SndWavDir)\sound_Bugle_A.wav \ - $(SndWavDir)\sound_Bugle_B.wav $(SndWavDir)\sound_Bugle_C.wav \ - $(SndWavDir)\sound_Bugle_D.wav $(SndWavDir)\sound_Bugle_E.wav \ - $(SndWavDir)\sound_Bugle_F.wav $(SndWavDir)\sound_Bugle_G.wav \ - $(SndWavDir)\sound_Drum_Of_Earthquake.wav \ - $(SndWavDir)\sound_Fire_Horn.wav $(SndWavDir)\sound_Frost_Horn.wav \ - $(SndWavDir)\sound_Leather_Drum.wav $(SndWavDir)\sound_Magic_Harp_A.wav \ - $(SndWavDir)\sound_Magic_Harp_B.wav $(SndWavDir)\sound_Magic_Harp_C.wav \ - $(SndWavDir)\sound_Magic_Harp_D.wav $(SndWavDir)\sound_Magic_Harp_E.wav \ - $(SndWavDir)\sound_Magic_Harp_F.wav $(SndWavDir)\sound_Magic_Harp_G.wav \ - $(SndWavDir)\sound_Magic_Flute_A.wav \ - $(SndWavDir)\sound_Magic_Flute_B.wav $(SndWavDir)\sound_Magic_Flute_C.wav \ - $(SndWavDir)\sound_Magic_Flute_D.wav $(SndWavDir)\sound_Magic_Flute_E.wav \ - $(SndWavDir)\sound_Magic_Flute_F.wav $(SndWavDir)\sound_Magic_Flute_G.wav \ - $(SndWavDir)\sound_Tooled_Horn_A.wav $(SndWavDir)\sound_Tooled_Horn_B.wav \ - $(SndWavDir)\sound_Tooled_Horn_C.wav $(SndWavDir)\sound_Tooled_Horn_D.wav \ - $(SndWavDir)\sound_Tooled_Horn_E.wav $(SndWavDir)\sound_Tooled_Horn_F.wav \ - $(SndWavDir)\sound_Tooled_Horn_G.wav $(SndWavDir)\sound_Wooden_Flute_A.wav \ - $(SndWavDir)\sound_Wooden_Flute_B.wav $(SndWavDir)\sound_Wooden_Flute_C.wav \ - $(SndWavDir)\sound_Wooden_Flute_D.wav $(SndWavDir)\sound_Wooden_Flute_E.wav \ - $(SndWavDir)\sound_Wooden_Flute_F.wav $(SndWavDir)\sound_Wooden_Flute_G.wav \ - $(SndWavDir)\sound_Wooden_Harp_A.wav $(SndWavDir)\sound_Wooden_Harp_B.wav \ - $(SndWavDir)\sound_Wooden_Harp_C.wav $(SndWavDir)\sound_Wooden_Harp_D.wav \ - $(SndWavDir)\sound_Wooden_Harp_E.wav $(SndWavDir)\sound_Wooden_Harp_F.wav \ - $(SndWavDir)\sound_Wooden_Harp_G.wav $(SndWavDir)\sa2_xpleveldown.wav \ - $(SndWavDir)\sa2_xplevelup.wav +WAVS = $(SndWavDir)se_squeak_A.wav $(SndWavDir)se_squeak_B.wav \ + $(SndWavDir)se_squeak_B_flat.wav $(SndWavDir)se_squeak_C.wav \ + $(SndWavDir)se_squeak_D.wav $(SndWavDir)se_squeak_D_flat.wav \ + $(SndWavDir)se_squeak_E.wav $(SndWavDir)se_squeak_E_flat.wav \ + $(SndWavDir)se_squeak_F.wav $(SndWavDir)se_squeak_F_sharp.wav \ + $(SndWavDir)se_squeak_G.wav $(SndWavDir)se_squeak_G_sharp.wav \ + $(SndWavDir)sound_Bell.wav $(SndWavDir)sound_Bugle_A.wav \ + $(SndWavDir)sound_Bugle_B.wav $(SndWavDir)sound_Bugle_C.wav \ + $(SndWavDir)sound_Bugle_D.wav $(SndWavDir)sound_Bugle_E.wav \ + $(SndWavDir)sound_Bugle_F.wav $(SndWavDir)sound_Bugle_G.wav \ + $(SndWavDir)sound_Drum_Of_Earthquake.wav \ + $(SndWavDir)sound_Fire_Horn.wav $(SndWavDir)sound_Frost_Horn.wav \ + $(SndWavDir)sound_Leather_Drum.wav $(SndWavDir)sound_Magic_Harp_A.wav \ + $(SndWavDir)sound_Magic_Harp_B.wav $(SndWavDir)sound_Magic_Harp_C.wav \ + $(SndWavDir)sound_Magic_Harp_D.wav $(SndWavDir)sound_Magic_Harp_E.wav \ + $(SndWavDir)sound_Magic_Harp_F.wav $(SndWavDir)sound_Magic_Harp_G.wav \ + $(SndWavDir)sound_Magic_Flute_A.wav \ + $(SndWavDir)sound_Magic_Flute_B.wav $(SndWavDir)sound_Magic_Flute_C.wav \ + $(SndWavDir)sound_Magic_Flute_D.wav $(SndWavDir)sound_Magic_Flute_E.wav \ + $(SndWavDir)sound_Magic_Flute_F.wav $(SndWavDir)sound_Magic_Flute_G.wav \ + $(SndWavDir)sound_Tooled_Horn_A.wav $(SndWavDir)sound_Tooled_Horn_B.wav \ + $(SndWavDir)sound_Tooled_Horn_C.wav $(SndWavDir)sound_Tooled_Horn_D.wav \ + $(SndWavDir)sound_Tooled_Horn_E.wav $(SndWavDir)sound_Tooled_Horn_F.wav \ + $(SndWavDir)sound_Tooled_Horn_G.wav $(SndWavDir)sound_Wooden_Flute_A.wav \ + $(SndWavDir)sound_Wooden_Flute_B.wav $(SndWavDir)sound_Wooden_Flute_C.wav \ + $(SndWavDir)sound_Wooden_Flute_D.wav $(SndWavDir)sound_Wooden_Flute_E.wav \ + $(SndWavDir)sound_Wooden_Flute_F.wav $(SndWavDir)sound_Wooden_Flute_G.wav \ + $(SndWavDir)sound_Wooden_Harp_A.wav $(SndWavDir)sound_Wooden_Harp_B.wav \ + $(SndWavDir)sound_Wooden_Harp_C.wav $(SndWavDir)sound_Wooden_Harp_D.wav \ + $(SndWavDir)sound_Wooden_Harp_E.wav $(SndWavDir)sound_Wooden_Harp_F.wav \ + $(SndWavDir)sound_Wooden_Harp_G.wav $(SndWavDir)sa2_xpleveldown.wav \ + $(SndWavDir)sa2_xplevelup.wav !IF "$(HAVE_SOUNDLIB)" == "Y" !IF "$(NEED_USERSOUNDS)" == "Y" @@ -922,37 +1106,40 @@ RCFLAGS = $(RCFLAGS) -dRCWAV # Header file macros #========================================== -CONFIG_H = $(INCL)\config.h $(INCL)\config1.h $(INCL)\patchlevel.h \ - $(INCL)\tradstdc.h $(INCL)\global.h $(INCL)\coord.h \ - $(INCL)\vmsconf.h $(INCL)\cstd.h $(INCL)\nhlua.h \ - $(INCL)\unixconf.h $(INCL)\pcconf.h $(INCL)\micro.h \ - $(INCL)\windconf.h $(INCL)\warnings.h \ - $(INCL)\fnamesiz.h +# config.h +CONFIG_H = $(INCL)color.h $(INCL)config.h $(INCL)config1.h \ + $(INCL)coord.h $(INCL)cstd.h $(INCL)fnamesiz.h \ + $(INCL)global.h $(INCL)integer.h $(INCL)micro.h \ + $(INCL)patchlevel.h $(INCL)pcconf.h \ + $(INCL)tradstdc.h $(INCL)unixconf.h \ + $(INCL)vmsconf.h $(INCL)warnings.h \ + $(INCL)windconf.h -HACK_H = $(INCL)\hack.h $(CONFIG_H) $(INCL)\lint.h $(INCL)\align.h \ - $(INCL)\dungeon.h $(INCL)\sym.h $(INCL)\defsym.h \ - $(INCL)\mkroom.h $(INCL)\artilist.h \ - $(INCL)\objclass.h $(INCL)\objects.h \ - $(INCL)\youprop.h $(INCL)\prop.h $(INCL)\permonst.h \ - $(INCL)\monattk.h $(INCL)\monflag.h \ - $(INCL)\monsters.h $(INCL)\mondata.h \ - $(INCL)\wintype.h $(INCL)\context.h $(INCL)\rm.h \ - $(INCL)\botl.h $(INCL)\rect.h $(INCL)\region.h \ - $(INCL)\display.h $(INCL)\vision.h $(INCL)\color.h \ - $(INCL)\decl.h $(INCL)\quest.h $(INCL)\spell.h \ - $(INCL)\obj.h $(INCL)\engrave.h $(INCL)\you.h \ - $(INCL)\attrib.h $(INCL)\monst.h $(INCL)\mextra.h \ - $(INCL)\skills.h $(INCL)\timeout.h $(INCL)\trap.h \ - $(INCL)\flag.h $(INCL)\winprocs.h $(INCL)\sndprocs.h \ - $(INCL)\seffects.h $(INCL)\sys.h +# hack.h +HACK_H = $(CONFIG_H) $(INCL)align.h $(INCL)artilist.h \ + $(INCL)attrib.h $(INCL)botl.h $(INCL)context.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)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 \ + $(INCL)objects.h $(INCL)permonst.h $(INCL)prop.h \ + $(INCL)quest.h $(INCL)rect.h $(INCL)region.h \ + $(INCL)rm.h $(INCL)seffects.h $(INCL)selvar.h \ + $(INCL)skills.h $(INCL)sndprocs.h $(INCL)spell.h \ + $(INCL)stairs.h $(INCL)sym.h $(INCL)sys.h \ + $(INCL)timeout.h $(INCL)trap.h $(INCL)vision.h \ + $(INCL)weight.h $(INCL)winprocs.h $(INCL)wintype.h \ + $(INCL)you.h $(INCL)youprop.h -TILE_H = ..\win\share\tile.h +TILE_H = $(WSHR)tile.h #========================================== # Miscellaneous #========================================== -DATABASE = $(DAT)\data.base +DATABASE = $(DAT)data.base #========================================== #========================================== @@ -965,17 +1152,20 @@ DATABASE = $(DAT)\data.base # #CTAGSCMD=ctags-orig.exe !IF "$(CI_BUILD_DIR)" != "" -CTAGSCMD=$(LIBDIR)\ctags\ctags.exe +R_CTAGSDIR=$(LIBDIR)ctags +CTAGSDIR=$(R_CTAGSDIR)$(BACKSLASH) +#U_CTAGSDIR=$(CTAGSDIR:\=/) !ELSE -CTAGSCMD=..\..\..\ctags\ctags.exe +R_CTAGSDIR=..\lib\ctags +CTAGSDIR=$(R_CTAGSDIR)$(BACKSLASH) +#U_CTAGSDIR=$(CTAGSDIR:\=/) !ENDIF +CTAGSCMD=$(CTAGSDIR)ctags.exe CTAGSOPT =--language-force=c --sort=no -D"Bitfield(x,n)=unsigned x : n" --excmd=pattern # # ctags wants unix-style pathnames # -TINC = $(INCL:\=/) -TSRC = $(SRC:\=/) cc=cl.exe cpp=cpp.exe @@ -988,8 +1178,8 @@ rc=Rc.exe # is too old or untested. # # Recently tested versions: -TESTEDVS2019 = 14.29.30154.0 -TESTEDVS2022 = 14.41.34120.0 +TESTEDVS2019 = 14.29.30157.0 +TESTEDVS2022 = 14.44.35215.0 VS2019CUR = $(TESTEDVS2019:.=) VS2022CUR = $(TESTEDVS2022:.=) @@ -1102,19 +1292,36 @@ scall = !IF ($(VSVER) >= 2012) # # 4100 unreferenced formal parameter +# 4101 'identifier': unreferenced local variable +# 4102 'label': unreferenced label # 4131 old-style declarator +# 4201 'nonstandard extension used : nameless struct/union' # 4244 conversion possible loss of data # 4245 conversion from 'char' to 'uchar', signed/unsigned mismatch # 4310 a constant value is cast to a smaller type +# 4324 'structname': structure was padded due to alignment specifier +# 4431 missing type specifier - int assumed. Note: C no longer supports default-int # 4706 assignment within conditional # 4774 format string is not a string literal (default is off at W4) # 4777 format string requires an argument of type 'type', # but variadic argument 'position' has type 'type' # 4820 padding in struct -ctmpflags = $(ctmpflags:-W3=-W4) -wd4100 -wd4244 -wd4245 -wd4310 -wd4706 -w44777 -wd4820 +# 5262 enable fallthrough warnings that lack [[fallthrough]] +# 5264 'variable-name': 'const' variable is not used +# 5266 'const' qualifier on return type has no effect +# 6001 'Using uninitialized memory' +# +#ctmpflags = $(ctmpflags:-W3=-W4) -wd4100 -wd4244 -wd4245 -wd4310 -wd4706 +ctmpflags = $(ctmpflags:-W3=-W4) -wd4244 -wd4245 -wd4310 -wd4706 -w44101 -w44102 !IF ($(VSVER) >= 2019) ctmpflags = $(ctmpflags) -w44774 !ENDIF +!IF ($(VSVER) >= 2022) +!IF ($(MAKEVERSION) >= 1440338120) +# warning 5262 became available starting in Visual Studio 2022 version 17.4. +ctmpflags = $(ctmpflags) -w45262 -w45264 -w45266 -w44431 -w44777 -wd4820 /std:clatest +!ENDIF +!ENDIF !ENDIF #More verbose warning output options below @@ -1143,7 +1350,7 @@ guilflags = $(lflags) -subsystem:windows # basic subsystem specific libraries, less the C Run-Time baselibs = kernel32.lib $(optlibs) $(winsocklibs) advapi32.lib gdi32.lib \ - ole32.lib Shell32.lib dbghelp.lib + ole32.lib Shell32.lib UserEnv.lib dbghelp.lib Rpcrt4.lib winlibs = $(baselibs) user32.lib comdlg32.lib winspool.lib # for Windows applications that use the C Run-Time libraries @@ -1151,14 +1358,14 @@ conlibs = $(baselibs) guilibs = $(winlibs) # -INCLDIR= /I..\include /I..\sys\windows $(LUAINCL) $(SOUNDLIBINCL) +INCLUSIONS= /I$(R_INCL) /I$(R_MSWSYS) $(R_LUAINCL) $(R_SOUNDLIBINCL) #========================================== # Util and console builds #========================================== -CFLAGS = $(ctmpflags) $(INCLDIR) $(DLBDEF) -DSAFEPROCS $(SOUNDLIBDEFS) -CPPFLAGS = $(cpptmpflags) $(INCLDIR) $(DLBDEF) -DSAFEPROCS $(SOUNDLIBDEFS) +CFLAGS = $(ctmpflags) $(INCLUSIONS) $(DLBDEF) -DSAFEPROCS $(SOUNDLIBDEFS) +CPPFLAGS = $(cpptmpflags) $(INCLUSIONS) $(DLBDEF) -DSAFEPROCS $(SOUNDLIBDEFS) LFLAGS = $(lflags) $(conlibs) $(MACHINE) #========================================== @@ -1183,176 +1390,194 @@ DLB = # Rules for files in src #========================================== -.c{$(OBJTTY)}.o: +.c{$(R_OBJTTY)}.o: $(Q)$(CC) $(CFLAGS) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< -.c{$(OBJGUI)}.o: +.c{$(R_OBJGUI)}.o: $(Q)$(CC) $(CFLAGS) $(GUIDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< -.c{$(OBJUTIL)}.o: +.c{$(R_OBJUTIL)}.o: $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< -{$(SRC)}.c{$(OBJTTY)}.o: +{$(R_SRC)}.c{$(R_OBJTTY)}.o: $(Q)$(CC) $(CFLAGS) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< -{$(SRC)}.c{$(OBJGUI)}.o: +{$(R_SRC)}.c{$(R_OBJGUI)}.o: $(Q)$(CC) $(CFLAGS) $(GUIDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< -{$(SRC)}.c{$(OBJUTIL)}.o: +{$(R_SRC)}.c{$(R_OBJUTIL)}.o: $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< #========================================== # Rules for files in sound\windsound #========================================== -{..\sound\windsound}.c{$(OBJTTY)}.o: - $(Q)$(CC) $(CFLAGS) -I$(WSHR) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< +{$(R_WINDSOUNDDIR)}.c{$(R_OBJTTY)}.o: + $(Q)$(CC) $(CFLAGS) -I$(R_WSHR) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< -{..\sound\windsound}.c{$(OBJGUI)}.o: - $(Q)$(CC) $(CFLAGS) -I$(WSHR) $(GUIDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< +{$(R_WINDSOUNDDIR)}.c{$(R_OBJGUI)}.o: + $(Q)$(CC) $(CFLAGS) -I$(R_WSHR) $(GUIDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< #========================================== # Rules for files in sound\sample #========================================== -{..\sound\sample}.c{$(OBJTTY)}.o: - $(Q)$(CC) $(CFLAGS) -I$(WSHR) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< +{$(SOUNDDIR)sample}.c{$(R_OBJTTY)}.o: + $(Q)$(CC) $(CFLAGS) -I$(R_WSHR) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< -{..\sound\sample}.c{$(OBJGUI)}.o: - $(Q)$(CC) $(CFLAGS) -I$(WSHR) $(GUIDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< +{$(SOUNDDIR)sample}.c{$(R_OBJGUI)}.o: + $(Q)$(CC) $(CFLAGS) -I$(R_WSHR) $(GUIDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< #========================================== # Rules for files in sys\share #========================================== -{$(SSYS)}.c{$(OBJTTY)}.o: +{$(R_SSYS)}.c{$(R_OBJTTY)}.o: $(Q)$(CC) $(CFLAGS) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< -{$(SSYS)}.c{$(OBJGUI)}.o: +{$(R_SSYS)}.c{$(R_OBJGUI)}.o: $(Q)$(CC) $(CFLAGS) $(GUIDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< -{$(SSYS)}.c{$(OBJUTIL)}.o: +{$(R_SSYS)}.c{$(R_OBJUTIL)}.o: $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< -{$(SSYS)}.cpp{$(OBJTTY)}.o: +{$(R_SSYS)}.cpp{$(R_OBJTTY)}.o: $(Q)$(CC) $(CPPFLAGS) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) /EHsc -Fo$@ $< -{$(SSYS)}.cpp{$(OBJGUI)}.o: +{$(R_SSYS)}.cpp{$(R_OBJGUI)}.o: $(Q)$(CC) $(CPPFLAGS) $(GUIDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) /EHsc -Fo$@ $< -{$(SSYS)}.cpp{$(OBJUTIL)}.o: +{$(R_SSYS)}.cpp{$(R_OBJUTIL)}.o: $(Q)$(CC) $(CPPFLAGS) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) /EHsc -Fo$@ $< #========================================== # Rules for files in sys\windows #========================================== -{$(MSWSYS)}.c{$(OBJTTY)}.o: +{$(R_MSWSYS)}.c{$(R_OBJTTY)}.o: $(Q)$(CC) $(CFLAGS) $(TTYDEF) -Fo$@ $< -{$(MSWSYS)}.c{$(OBJGUI)}.o: +{$(R_MSWSYS)}.c{$(R_OBJGUI)}.o: $(Q)$(CC) $(CFLAGS) -I$(MSWSYS) $(GUIDEF) -Fo$@ $< -{$(MSWSYS)}.uu{$(MSWSYS)}.bmp: +{$(R_MSWSYS)}.uu{$(R_MSWSYS)}.bmp: $(U)uudecode.exe $< - move $(SRC)\$(@B).bmp $@ + move $(SRC)$(@B).bmp $@ #========================================== # Rules for files in util #========================================== -{$(UTIL)}.c{$(OBJUTIL)}.o: +{$(R_UTIL)}.c{$(R_OBJUTIL)}.o: + echo R_UTIL=$(R_UTIL) + echo R_OBJUTIL=$(R_OBJUTIL) $(Q)$(CC) $(CFLAGS) -Fo$@ $< #========================================== # Rules for files in win\share #========================================== -{$(WSHR)}.c{$(OBJTTY)}.o: - $(Q)$(CC) $(CFLAGS) -I$(WSHR) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< +{$(R_WSHR)}.c{$(R_OBJTTY)}.o: + $(Q)$(CC) $(CFLAGS) -I$(R_WSHR) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< -{$(WSHR)}.c{$(OBJGUI)}.o: - $(Q)$(CC) $(CFLAGS) -I$(WSHR) $(GUIDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< +{$(R_WSHR)}.c{$(R_OBJGUI)}.o: + $(Q)$(CC) $(CFLAGS) -I$(R_WSHR) $(GUIDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< -{$(WSHR)}.c{$(OBJUTIL)}.o: - $(Q)$(CC) $(CFLAGS) -I$(WSHR) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< +{$(R_WSHR)}.c{$(R_OBJUTIL)}.o: + $(Q)$(CC) $(CFLAGS) -I$(R_WSHR) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< #========================================== # Rules for files in win\tty #========================================== -{$(TTY)}.c{$(OBJTTY)}.o: - $(Q)$(CC) $(CFLAGS) $(TTYDEF) -I$(MSWSYS) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< +{$(R_TTY)}.c{$(R_OBJTTY)}.o: + $(Q)$(CC) $(CFLAGS) $(TTYDEF) -I$(R_MSWSYS) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< #========================================== # Rules for files in win\win32 #========================================== -{$(MSWIN)}.c{$(OBJGUI)}.o: - $(Q)$(CC) $(CFLAGS) $(GUIDEF) -I$(MSWSYS) -I$(MSWIN) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< +{$(R_MSWIN)}.c{$(R_OBJGUI)}.o: + $(Q)$(CC) $(CFLAGS) $(GUIDEF) -I$(R_MSWSYS) -I$(R_MSWIN) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< -{$(MSWIN)}.uu{$(MSWIN)}.bmp: +{$(R_MSWIN)}.uu{$(R_MSWIN)}.bmp: $(U)uudecode.exe $< - move $(SRC)\$(@B).bmp $@ + move $(SRC)$(@B).bmp $@ #========================================== # Rules for files in win\curses #========================================== +#!MESSAGE INCLCURSES_H=$(INCLCURSES_H) -{$(WCURSES)}.c{$(OBJTTY)}.o: - $(Q)$(CC) $(PDCINCL) $(CFLAGS) $(CURSESDEF1) $(CURSESDEF2) $(TTYDEF) $(GUIDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< +{$(R_WCURSES)}.c{$(R_OBJTTY)}.o: + $(Q)$(CC) $(INCLCURSES_H) $(CFLAGS) $(CURSESCONDEF1) $(CURSESGUIDEF1) $(CURSESDEF2) $(TTYDEF) $(GUIDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< #========================================== # Rules for files in PDCurses #========================================== +#!MESSAGE {$(R_PDCURSES_TOP)}.c{$(R_OBJPDC)}.o: +#!MESSAGE {$(R_PDCSRC)}.c{$(R_OBJPDC)}.o: +#!MESSAGE PDCINCL = $(PDCINCL) +#!MESSAGE PDCINCLGUI = $(PDCINCLGUI) +#!MESSAGE PDCINCLCON = $(PDCINCLCON) +#!MESSAGE $(Q)$(CC) /wd4244 /wd4267 /wd4774 $(R_PDCINCL) $(CFLAGS:-w44774= ) $(CURSESDEF2) /wd5262 $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -FoX2 X1 # -{$(PDCURSES_TOP)}.c{$(OBJPDC)}.o: - $(Q)$(CC) /wd4244 $(PDCINCL) $(CFLAGS) $(CURSESDEF2) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< +!IF "$(ADD_CURSES)" == "Y" +{$(R_PDCURSES_TOP)}.c{$(R_OBJPDC)}.o: + $(Q)$(CC) /wd4244 $(PDCINCL) $(CFLAGS) $(CURSESDEF2) /wd5262 $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< -{$(PDCSRC)}.c{$(OBJPDC)}.o: - $(Q)$(CC) /wd4244 /wd4267 /wd4774 $(PDCINCL) $(CFLAGS:-w44774= ) $(CURSESDEF2) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< +{$(R_PDCSRC)}.c{$(R_OBJPDC)}.o: + $(Q)$(CC) /wd4244 /wd4267 /wd4774 $(PDCINCL) $(CFLAGS:-w44774= ) $(CURSESDEF2) /wd5262 $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< -{$(PDCWINCON)}.c{$(OBJPDCC)}.o: - $(Q)$(CC) /wd4244 /wd4267 /wd4774 $(PDCINCL) $(PDCINCLCON) $(CFLAGS) $(CURSESDEF2) $(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: +!ENDIF + +!IF "$(CURSES_GRAPHICAL)" == "Y" +{$(R_PDCWINGUI)}.c{$(R_OBJPDCG)}.o: + $(Q)$(CC) /wd4244 /wd4267 /wd4774 $(PDCINCL) $(PDCINCLGUI) $(CFLAGS) $(CURSESDEF2) /wd5262 $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< +#!MESSAGE {$(R_PDCWINGUI)}.c{$(R_OBJPDCG)}.o: +!ENDIF +!ENDIF -{$(PDCWINGUI)}.c{$(OBJPDCG)}.o: - $(Q)$(CC) /wd4244 /wd4267 /wd4774 $(PDCINCL) $(PDCINCLGUI) $(CFLAGS) $(CURSESDEF2) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< #========================================== # Rules for LUA files #========================================== -{$(LUASRC)}.c{$(OBJLUA)}.o: - $(Q)$(CC) $(CFLAGS) -wd4701 -wd4702 -wd4774 -wd4324 $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< +{$(R_LUASRC)}.c{$(R_OBJLUA)}.o: + $(Q)$(CC) $(CFLAGS) -wd4701 -wd4702 -wd4774 -wd4324 -wd5262 $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< #=============================================================================== # Rules for integrated sound files in sound/wav #=============================================================================== -{$(SndWavDir)}.uu{$(SndWavDir)}.wav: - $(U)uudecode.exe $(SndWavDir)\$(@B).uu - move $(SRC)\$(@B).wav $(SndWavDir)\$(@B).wav +{$(R_SndWavDir)}.uu{$(R_SndWavDir)}.wav: + $(U)uudecode.exe $(SndWavDir)$(@B).uu + move $(SRC)$(@B).wav $(SndWavDir)$(@B).wav #========================================== # Rules for files in sound\windsound #========================================== -{$(WINDSOUNDDIR)}.c{$(OBJTTY)}.o: - $(Q)$(CC) $(CFLAGS) -I$(WSHR) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< +{$(R_WINDSOUNDDIR)}.c{$(R_OBJTTY)}.o: + $(Q)$(CC) $(CFLAGS) -I$(R_WSHR) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< -{$(WINDSOUNDDIR)}.c{$(OBJGUI)}.o: - $(Q)$(CC) $(CFLAGS) -I$(WSHR) $(GUIDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< +{$(R_WINDSOUNDDIR)}.c{$(R_OBJGUI)}.o: + $(Q)$(CC) $(CFLAGS) -I$(R_WSHR) $(GUIDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< #========================================== # Rules for files in sound\fmod #========================================== -{$(FMODDIR)}.c{$(OBJTTY)}.o: - $(Q)$(CC) $(CFLAGS) $(FMODFLAGS) -I$(WSHR) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< +{$(R_FMODDIR)}.c{$(R_OBJTTY)}.o: + $(Q)$(CC) $(CFLAGS) $(FMODFLAGS) -I$(R_WSHR) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< -{$(FMODDIR)}.c{$(OBJGUI)}.o: - $(Q)$(CC) $(CFLAGS) $(FMODFLAGS) -I$(WSHR) $(GUIDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< +{$(R_FMODDIR)}.c{$(R_OBJGUI)}.o: + $(Q)$(CC) $(CFLAGS) $(FMODFLAGS) -I$(R_WSHR) $(GUIDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $< #========================================== #=============== TARGETS ================== @@ -1369,17 +1594,18 @@ all : package !IF "$(INTERNET_AVAILABLE)" == "Y" !IF "$(GIT_AVAILABLE)" == "Y" -$(LUASRC)\lua.h: - git submodule init ../submodules/lua - git submodule update ../submodules/lua -# git submodule update --remote ../submodules/lua +$(LUASRC)lua.h: + git submodule init $(SUBM)lua + git submodule update $(SUBM)lua +# git submodule update --remote $(SUBM)lua # #aka PDCDEP -$(PDCURSES_TOP)\curses.h: - git submodule init ../submodules/$(PDCDIST) - git submodule update ../submodules/$(PDCDIST) -# git submodule update --remote ../submodules/$(PDCDIST) - +!IF "$(ADD_CURSES)" == "Y" +$(PDCURSES_TOP)curses.h: + git submodule init $(SUBM)$(R_PDCDIST) + git submodule update $(SUBM)$(R_PDCDIST) +# 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 @@ -1388,23 +1614,27 @@ CURLLUADST=lua-5.4.6.tar.gz CURLPDCSRC=https://github.com/Bill-Gray/PDCursesMod/archive/refs/tags/v4.4.0.zip CURLPDCDST=$(PDCDIST).zip -$(LUASRC)\lua.h: - @if not exist $(LIBDIR)\*.* mkdir $(LIBDIR) - cd $(LIBDIR) +$(LUASRC)lua.h: + @if not exist $(LIBDIR)*.* mkdir $(R_LIBDIR) + cd $(R_LIBDIR) curl -L $(CURLLUASRC) -o $(CURLLUADST) tar -xvf $(CURLLUADST) - cd ..\src -$(PDCURSES_TOP)\curses.h: - @if not exist $(LIBDIR)\*.* mkdir $(LIBDIR) + cd $(R_SRC) +!IF "$(ADD_CURSES)" == "Y" +$(PDCURSES_TOP)curses.h: + @if not exist $(LIBDIR)*.* mkdir $(R_LIBDIR) cd $(LIBDIR) curl -L $(CURLPDCSRC) -o $(CURLPDCDST) - if not exist $(PDCDIST)\*.* mkdir $(PDCDIST) - tar -C $(PDCDIST) --strip-components=1 -xvf $(CURLPDCDST) - cd ..\src + if not exist $(PDCDIST)*.* mkdir $(R_PDCDIST) + tar -C $(R_PDCDIST) --strip-components=1 -xvf $(CURLPDCDST) + cd $(R_SRC) +!ENDIF # ADD_CURSES !ENDIF # GIT_AVAILABLE !ELSE # INTERNET_AVAILABLE -$(LUASRC)\lua.h: -$(PDCURSES_TOP)\curses.h: +$(LUASRC)lua.h: +!IF "$(ADD_CURSES)" == "Y" +$(PDCURSES_TOP)curses.h: +!ENDIF !ENDIF # INTERNET_AVAILABLE #========================================== @@ -1447,18 +1677,18 @@ GAMEOBJ=$(GAMEOBJ:^ =^ #-------- # -$(GAMEDIR)\NetHack.exe : gamedir.tag $(OTTY)consoletty.o \ +$(GAMEDIR)NetHack.exe : gamedir.tag $(OTTY)consoletty.o \ $(ALLOBJTTY) $(CURSESWINCONOBJS) \ $(TTYSOUNDOBJS) $(OTTY)date.o $(OTTY)console.res \ - $(LUALIB) $(TTYOBJ) $(PDCWINCONLIB) $(PDCWINCONOBJS) $(OTTYHACKLIB) - @if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR) + $(LUALIB) $(TTYOBJTTY) $(PDCWINCONLIB) $(PDCWINCONOBJS) $(OTTYHACKLIB) + @if not exist $(GAMEDIR)*.* mkdir $(R_GAMEDIR) @echo Linking $(@:\=/) - $(link) $(LFLAGS) $(conlflags) /STACK:2048 /PDB:$(GAMEDIR)\$(@B).PDB /MAP:$(OTTY)$(@B).MAP \ + $(link) $(LFLAGS) $(conlflags) /STACK:2048 /PDB:$(GAMEDIR)$(@B).PDB /MAP:$(OTTY)$(@B).MAP \ $(LIBS) $(PDCWINCONLIB) $(LUALIB) $(TTYSOUNDLIBS) $(OTTYHACKLIB) \ $(conlibs) $(BCRYPT) -out:$@ @<<$(@B).lnk $(GAMEOBJTTY) $(ALLOBJTTY) - $(TTYOBJ) + $(TTYOBJTTY) $(TTYSOUNDOBJS) $(CURSESWINCONOBJS) $(PDCWINCONOBJS) $(OTTY)consoletty.o $(OTTY)date.o @@ -1470,14 +1700,14 @@ $(GAMEDIR)\NetHack.exe : gamedir.tag $(OTTY)consoletty.o \ # NetHackW #--------- # -$(GAMEDIR)\NetHackW.exe : gamedir.tag $(OGUI)tile.o \ +$(GAMEDIR)NetHackW.exe : gamedir.tag $(OGUI)tile.o \ $(ALLOBJGUI) $(GAMEOBJGUI) $(GUIOBJ) $(GUISOUNDOBJS) \ $(CURSESWINGUIOBJS) $(OGUI)date.o \ $(OGUI)NetHackW.res \ $(LUALIB) $(PDCWINGUILIB) $(PDCWINGUIOBJS) $(OGUIHACKLIB) - @if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR) + @if not exist $(GAMEDIR)*.* mkdir $(R_GAMEDIR) @echo Linking $(@:\=/) - $(link) $(LFLAGS) $(guilflags) /STACK:2048 /PDB:$(GAMEDIR)\$(@B).PDB \ + $(link) $(LFLAGS) $(guilflags) /STACK:2048 /PDB:$(GAMEDIR)$(@B).PDB \ /MAP:$(OGUI)$(@B).MAP \ $(LIBS) $(PDCWINGUILIB) $(LUALIB) \ $(GUISOUNDLIBS) $(OGUIHACKLIB) \ @@ -1495,61 +1725,61 @@ $(GAMEDIR)\NetHackW.exe : gamedir.tag $(OGUI)tile.o \ # install #-------- # -binary.tag: $(DAT)\data $(DAT)\rumors $(DAT)\oracles $(DLB) \ +binary.tag: $(DAT)data $(DAT)rumors $(DAT)oracles $(DLB) \ $(HACKCSRC) $(SOUNDSRCS) -! IF ("$(USE_DLB)"=="Y") copy nhdat$(NHV) $(GAMEDIR) - copy $(DAT)\license $(GAMEDIR) - copy $(DAT)\opthelp $(GAMEDIR) + copy $(DAT)license $(GAMEDIR) + if exist $(DAT)opthelp copy $(DAT)opthelp $(GAMEDIR) + if exist $(MSWSYS)sysconf.template copy $(MSWSYS)sysconf.template $(GAMEDIR) + if exist $(DAT)symbols copy $(DAT)symbols $(GAMEDIR)symbols + if exist $(DOC)guidebook.txt copy $(DOC)guidebook.txt $(GAMEDIR)Guidebook.txt + if exist $(DOC)nethack.txt copy $(DOC)nethack.txt $(GAMEDIR)NetHack.txt +# if exist $(UTIL)sfctool.pdb copy $(UTIL)sfctool.pdb $(GAMEDIR)sfctool.pdb + @if exist $(GAMEDIR)NetHack.PDB echo NOTE: You may want to remove $(U_GAMEDIR)/NetHack.PDB to conserve space + @if exist $(GAMEDIR)NetHackW.PDB echo NOTE: You may want to remove $(U_GAMEDIR)/NetHackW.PDB to conserve space + -if exist $(MSWSYS)nethackrc.template copy $(MSWSYS)nethackrc.template $(R_GAMEDIR) + -if not exist $(GAMEDIR)record. goto>$(GAMEDIR)record. +! IF ("$(USE_DLB)" == "Y") + copy /Y nhdat$(NHV) $(GAMEDIR) ! ELSE - copy $(DAT)\bogusmon $(GAMEDIR) - copy $(DAT)\cmdhelp $(GAMEDIR) - copy $(DAT)\data $(GAMEDIR) - copy $(DAT)\dungeon $(GAMEDIR) - copy $(DAT)\engrave $(GAMEDIR) - copy $(DAT)\epitaph $(GAMEDIR) - copy $(DAT)\help $(GAMEDIR) - copy $(DAT)\hh $(GAMDEDIR) - copy $(DAT)\history $(GAMEDIR) - copy $(DAT)\license $(GAMEDIR) - copy $(DAT)\optmenu $(GAMEDIR) - copy $(DAT)\oracles $(GAMEDIR) - copy $(DAT)\rumors $(GAMEDIR) - copy $(DAT)\symbols $(GAMEDIR) - copy $(DAT)\tribute $(GAMEDIR) - copy $(DAT)\wizhelp $(GAMEDIR) - copy $(DAT)\*.lua $(GAMEDIR) - if exist $(DAT)\guioptions copy $(DAT)\guioptions $(GAMEDIR) - if exist $(DAT)\keyhelp copy $(DAT)\keyhelp $(GAMEDIR) - if exist $(DAT)\opthelp copy $(DAT)\opthelp $(GAMEDIR) - if exist $(DAT)\options copy $(DAT)\options $(GAMEDIR) - if exist $(DAT)\porthelp copy $(DAT)\porthelp $(GAMEDIR) - if exist $(DAT)\ttyoptions copy $(DAT)\ttyoptions $(GAMEDIR) + copy $(DAT)bogusmon $(GAMEDIR) + copy $(DAT)cmdhelp $(GAMEDIR) + copy $(DAT)data $(GAMEDIR) + copy $(DAT)engrave $(GAMEDIR) + copy $(DAT)epitaph $(GAMEDIR) + copy $(DAT)help $(GAMEDIR) + copy $(DAT)hh $(GAMEDIR) + copy $(DAT)history $(GAMEDIR) + copy $(DAT)license $(GAMEDIR) + copy $(DAT)optmenu $(GAMEDIR) + copy $(DAT)oracles $(GAMEDIR) + copy $(DAT)rumors $(GAMEDIR) + copy $(DAT)symbols $(GAMEDIR) + copy $(DAT)tribute $(GAMEDIR) + copy $(DAT)wizhelp $(GAMEDIR) + copy /Y $(DAT)*.lua $(GAMEDIR) + if exist $(DAT)guioptions copy $(DAT)guioptions $(GAMEDIR) + if exist $(DAT)keyhelp copy $(DAT)keyhelp $(GAMEDIR) + if exist $(DAT)options copy $(DAT)options $(GAMEDIR) + if exist $(DAT)porthelp copy $(DAT)porthelp $(GAMEDIR) + if exist $(DAT)ttyoptions copy $(DAT)ttyoptions $(GAMEDIR) ! ENDIF - if exist $(MSWSYS)\sysconf.template copy $(MSWSYS)\sysconf.template $(GAMEDIR) - if exist $(DAT)\symbols copy $(DAT)\symbols $(GAMEDIR)\symbols - if exist $(DOC)\guidebook.txt copy $(DOC)\guidebook.txt $(GAMEDIR)\Guidebook.txt - if exist $(DOC)\nethack.txt copy $(DOC)\nethack.txt $(GAMEDIR)\NetHack.txt - @if exist $(GAMEDIR)\NetHack.PDB echo NOTE: You may want to remove $(GAMEDIR:\=/)/NetHack.PDB to conserve space - @if exist $(GAMEDIR)\NetHackW.PDB echo NOTE: You may want to remove $(GAMEDIR:\=/)/NetHackW.PDB to conserve space - -if exist $(MSWSYS)\nethackrc.template copy $(MSWSYS)\nethackrc.template $(GAMEDIR) - -if not exist $(GAMEDIR)\record. goto>$(GAMEDIR)\record. echo binary built > $@ -# copy $(MSWSYS)\windsyshlp $(GAMEDIR) +# copy $(MSWSYS)windsyshlp $(GAMEDIR) -recover: $(U)recover.exe +recover: $(OUTLHACKLIB) $(U)recover.exe if exist $(U)recover.exe copy $(U)recover.exe $(GAMEDIR) - if exist $(DOC)\recover.txt copy $(DOC)\recover.txt $(GAMEDIR)\recover.txt + if exist $(DOC)recover.txt copy $(DOC)recover.txt $(GAMEDIR)recover.txt -$(OUTL)utility.tag: $(INCL)\nhlua.h outldir$(TARGET_CPU).tag $(OUTLHACKLIB) $(U)tile2bmp.exe $(U)makedefs.exe +$(OUTL)utility.tag: $(INCL)nhlua.h outldir$(TARGET_CPU).tag $(OUTLHACKLIB) $(U)tile2bmp.exe $(U)makedefs.exe @echo utilities made >$@ @echo utilities made. -$(INCL)\nhlua.h: +$(INCL)nhlua.h: @echo /* nhlua.h - generated by Makefile from Makefile.nmake */ > $@ @echo #include "lua.h" >> $@ - @echo LUA_API int (lua_error) (lua_State *L) NORETURN; >> $@ + @echo ATTRNORETURN LUA_API int (lua_error) (lua_State *L) NORETURN; >> $@ @echo #include "lualib.h" >> $@ @echo #include "lauxlib.h" >> $@ @echo /*nhlua.h*/ >> $@ @@ -1557,16 +1787,16 @@ $(INCL)\nhlua.h: tileutil: $(U)gif2txt.exe $(U)gif2tx32.exe $(U)txt2ppm.exe @echo Optional tile development utilities are up to date. -$(OGUI)NetHackW.res: $(SRC)\tiles.bmp $(MSWIN)\NetHackW.rc $(MSWIN)\NetHack.ico \ - $(MSWIN)\mnsel.bmp $(MSWIN)\mnselcnt.bmp $(MSWIN)\mnunsel.bmp \ - $(MSWIN)\petmark.bmp $(MSWIN)\pilemark.bmp $(MSWIN)\NetHack.ico \ - $(MSWIN)\rip.bmp $(MSWIN)\splash.bmp $(MSWIN)\NetHackW.exe.manifest $(WAV) +$(OGUI)NetHackW.res: $(SRC)tiles.bmp $(MSWIN)NetHackW.rc $(MSWIN)NetHack.ico \ + $(MSWIN)mnsel.bmp $(MSWIN)mnselcnt.bmp $(MSWIN)mnunsel.bmp \ + $(MSWIN)petmark.bmp $(MSWIN)pilemark.bmp $(MSWIN)NetHack.ico \ + $(MSWIN)rip.bmp $(MSWIN)splash.bmp $(MSWIN)NetHackW.exe.manifest $(WAV) @echo Building resource file $@ from $** - $(rc) -nologo -r -fo$@ -i$(MSWIN) -i$(SndWavDir) -dNDEBUG -dVIA_MAKE $(RCFLAGS) $(MSWIN)\NetHackW.rc + $(rc) -nologo -r -fo$@ -i$(MSWIN) -i$(SndWavDir) -dNDEBUG -dVIA_MAKE $(RCFLAGS) $(MSWIN)NetHackW.rc -$(OTTY)console.res: $(MSWSYS)\console.rc $(MSWSYS)\NetHack.ico $(WAV) +$(OTTY)console.res: $(MSWSYS)console.rc $(MSWSYS)NetHack.ico $(WAV) @echo Building resource file $@ from $** - $(rc) -nologo -r -fo$@ -i$(MSWSYS) -i$(SndWavDir) -dNDEBUG $(RCFLAGS) $(MSWSYS)\console.rc + $(rc) -nologo -r -fo$@ -i$(MSWSYS) -i$(SndWavDir) -dNDEBUG $(RCFLAGS) $(MSWSYS)console.rc # # Secondary Targets. @@ -1586,13 +1816,13 @@ $(U)makedefs.exe: $(OUTLHACKLIB) $(MAKEDEFSOBJS) @echo Linking $(@:\=/) @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" -out:$@ $(MAKEDEFSOBJS) $(OUTLHACKLIB) -$(OUTL)makedefs.o: $(U)makedefs.c $(SRC)\mdlib.c $(CONFIG_H) $(INCL)\permonst.h \ - $(INCL)\objclass.h $(INCL)\sym.h $(INCL)\defsym.h \ - $(INCL)\artilist.h $(INCL)\dungeon.h $(INCL)\obj.h \ - $(INCL)\monst.h $(INCL)\you.h $(INCL)\flag.h \ - $(INCL)\dlb.h - @if not exist $(OBJTTY)\*.* echo creating directory $(OBJTTY:\=/) - @if not exist $(OBJTTY)\*.* mkdir $(OBJTTY) +$(OUTL)makedefs.o: $(U)makedefs.c $(SRC)mdlib.c $(CONFIG_H) $(INCL)permonst.h \ + $(INCL)objclass.h $(INCL)sym.h $(INCL)defsym.h \ + $(INCL)artilist.h $(INCL)dungeon.h $(INCL)obj.h \ + $(INCL)monst.h $(INCL)you.h $(INCL)flag.h \ + $(INCL)dlb.h + @if not exist $(OBJTTY)*.* echo creating directory $(OBJTTY:\=/) + @if not exist $(OBJTTY)*.* mkdir $(R_OBJTTY) $(Q)$(CC) -DENUM_PM $(CFLAGS) $(TTYDEF) $(CROSSCOMPILE) -Fo$@ $(U)makedefs.c # $(Q)$(CC) -DENUM_PM $(CFLAGS) $(TTYDEF) $(CROSSCOMPILE) /EP -Fo$@ $(U)makedefs.c >$(OUTL)makedefs.c.preproc @@ -1648,18 +1878,26 @@ $(OGUI)date.o: $(HACKINCL) $(HACKSRC) $(HACKOBJ) $(ALLOBJGUI) @$(MAKE) /NOLOGO /A -f date.nmk !ENDIF +$(OGUI)sfbase.o: sfbase.c $(HACK_H) $(INCL)sfmacros.h + $(Q)$(CC) $(CFLAGS) /EP $(@B).c > $(OGUI)$(@B).c.preproc + $(Q)$(CC) $(CFLAGS) -Fo$@ $(@B).c + +$(OTTY)sfbase.o: sfbase.c $(HACK_H) $(INCL)sfmacros.h + $(Q)$(CC) $(CFLAGS) /EP $(@B).c > $(OTTY)$(@B).c.preproc + $(Q)$(CC) $(CFLAGS) -Fo$@ $(@B).c + # # date.h is not used with NetHack 3.7 # onames.h is not used with NetHack 3.7 # pm.h is not used with NetHack 3.7 -$(INCL)\date.h $(OPTIONS_FILE) : $(U)makedefs.exe +$(INCL)date.h $(OPTIONS_FILE) : $(U)makedefs.exe $(U)makedefs -v -$(INCL)\onames.h : $(U)makedefs.exe +$(INCL)onames.h : $(U)makedefs.exe $(U)makedefs -o -$(INCL)\pm.h : $(U)makedefs.exe +$(INCL)pm.h : $(U)makedefs.exe $(U)makedefs -p @@ -1685,25 +1923,27 @@ $(OGUIHACKLIB): $(OGUIHACKLIBOBJS) $(U)uudecode.exe: $(OUTL)uudecode.o @echo Linking $(@:\=/) - @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" -out:$@ $(OUTL)uudecode.o + @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" \ + -out:$@ $(OUTL)uudecode.o -$(OUTL)uudecode.o: $(SSYS)\uudecode.c - $(Q)$(CC) $(CFLAGS) -wd4702 $(TTYDEF) $(CROSSCOMPILE) /D_CRT_SECURE_NO_DEPRECATE -Fo$@ $(SSYS)\uudecode.c +$(OUTL)uudecode.o: $(SSYS)uudecode.c + $(Q)$(CC) $(CFLAGS) -wd4702 $(TTYDEF) $(CROSSCOMPILE) \ + /D_CRT_SECURE_NO_DEPRECATE -Fo$@ $(SSYS)uudecode.c -$(MSWIN)\mnsel.bmp: $(U)uudecode.exe $(MSWIN)\mnsel.uu -$(MSWIN)\mnselcnt.bmp: $(U)uudecode.exe $(MSWIN)\mnselcnt.uu -$(MSWIN)\mnunsel.bmp: $(U)uudecode.exe $(MSWIN)\mnunsel.uu -$(MSWIN)\petmark.bmp: $(U)uudecode.exe $(MSWIN)\petmark.uu -$(MSWIN)\pilemark.bmp: $(U)uudecode.exe $(MSWIN)\pilemark.uu -$(MSWIN)\rip.bmp: $(U)uudecode.exe $(MSWIN)\rip.uu -$(MSWIN)\splash.bmp: $(U)uudecode.exe $(MSWIN)\splash.uu +$(MSWIN)mnsel.bmp: $(U)uudecode.exe $(MSWIN)mnsel.uu +$(MSWIN)mnselcnt.bmp: $(U)uudecode.exe $(MSWIN)mnselcnt.uu +$(MSWIN)mnunsel.bmp: $(U)uudecode.exe $(MSWIN)mnunsel.uu +$(MSWIN)petmark.bmp: $(U)uudecode.exe $(MSWIN)petmark.uu +$(MSWIN)pilemark.bmp: $(U)uudecode.exe $(MSWIN)pilemark.uu +$(MSWIN)rip.bmp: $(U)uudecode.exe $(MSWIN)rip.uu +$(MSWIN)splash.bmp: $(U)uudecode.exe $(MSWIN)splash.uu -$(MSWIN)\NetHack.ico: $(U)uudecode.exe $(MSWSYS)\nhico.uu - $(U)uudecode.exe $(MSWSYS)\nhico.uu +$(MSWIN)NetHack.ico: $(U)uudecode.exe $(MSWSYS)nhico.uu + $(U)uudecode.exe $(MSWSYS)nhico.uu move nethack.ico $@ -$(MSWSYS)\NetHack.ico: $(U)uudecode.exe $(MSWSYS)\nhico.uu - $(U)uudecode.exe $(MSWSYS)\nhico.uu +$(MSWSYS)NetHack.ico: $(U)uudecode.exe $(MSWSYS)nhico.uu + $(U)uudecode.exe $(MSWSYS)nhico.uu move nethack.ico $@ #================================================= @@ -1711,53 +1951,53 @@ $(MSWSYS)\NetHack.ico: $(U)uudecode.exe $(MSWSYS)\nhico.uu #================================================= gamedir.tag: - @if not exist $(GAMEDIR)\*.* echo creating directory $(GAMEDIR:\=/) - @if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR) + @if not exist $(GAMEDIR)*.* echo creating directory $(GAMEDIR:\=/) + @if not exist $(GAMEDIR)*.* mkdir $(R_GAMEDIR) @echo directory created > $@ ottydir$(TARGET_CPU).tag: - @if not exist $(OBJTTY)\*.* echo creating directory $(OBJTTY:\=/) - @if not exist $(OBJTTY)\*.* mkdir $(OBJTTY) + @if not exist $(OBJTTY)*.* echo creating directory $(OBJTTY:\=/) + @if not exist $(OBJTTY)*.* mkdir $(R_OBJTTY) @echo directory created >$@ oguidir$(TARGET_CPU).tag: - @if not exist $(OBJGUI)\*.* echo creating directory $(OBJGUI:\=/) - @if not exist $(OBJGUI)\*.* mkdir $(OBJGUI) + @if not exist $(OBJGUI)*.* echo creating directory $(OBJGUI:\=/) + @if not exist $(OBJGUI)*.* mkdir $(R_OBJGUI) @echo directory created >$@ outldir$(TARGET_CPU).tag: - @if not exist $(OBJUTIL)\*.* echo creating directory $(OBJUTIL:\=/) - @if not exist $(OBJUTIL)\*.* mkdir $(OBJUTIL) + @if not exist $(OBJUTIL)*.* echo creating directory $(OBJUTIL:\=/) + @if not exist $(OBJUTIL)*.* mkdir $(R_OBJUTIL) @echo directory created >$@ oluadir$(TARGET_CPU).tag: - @if not exist $(OBJLUA)\*.* echo creating directory $(OBJLUA:\=/) - @if not exist $(OBJLUA)\*.* mkdir $(OBJLUA) + @if not exist $(OBJLUA)*.* echo creating directory $(OBJLUA:\=/) + @if not exist $(OBJLUA)*.* mkdir $(R_OBJLUA) @echo directory created >$@ opdcdir$(TARGET_CPU).tag: - @if not exist $(OBJPDC)\*.* echo creating directory $(OBJPDC:\=/) - @if not exist $(OBJPDC)\*.* mkdir $(OBJPDC) + @if not exist $(OBJPDC)*.* echo creating directory $(OBJPDC:\=/) + @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 $(OBJPDCC) + @if not exist $(OBJPDCC)*.* echo creating directory $(OBJPDCC:\=/) + @if not exist $(OBJPDCC)*.* mkdir $(R_OBJPDCC) @echo directory created >$@ opdcgdir$(TARGET_CPU).tag: - @if not exist $(OBJPDCG)\*.* echo creating directory $(OBJPDCG:\=/) - @if not exist $(OBJPDCG)\*.* mkdir $(OBJPDCG) + @if not exist $(OBJPDCG)*.* echo creating directory $(OBJPDCG:\=/) + @if not exist $(OBJPDCG)*.* mkdir $(R_OBJPDCG) @echo directory created >$@ libdir.tag: - @if not exist $(LIBDIR)\*.* echo creating directory $(LIB:\=/) - @if not exist $(LIBDIR)\*.* mkdir $(LIBDIR) + @if not exist $(LIBDIR)*.* echo creating directory $(LIB:\=/) + @if not exist $(LIBDIR)*.* mkdir $(R_LIBDIR) @echo directory created >$@ pkgdir.tag: - if NOT exist $(PkgDir)\*.* echo creating directory $(PkgDir:\=/) - if NOT exist $(PkgDir)\*.* mkdir $(PkgDir) + if NOT exist $(PkgDir)*.* echo creating directory $(PkgDir:\=/) + if NOT exist $(PkgDir)*.* mkdir $(R_PkgDir) @echo directory created >$@ #========================================== @@ -1767,10 +2007,13 @@ pkgdir.tag: #========================================== envchk.tag: cpu.tag -!IFDEF TTYOBJ +!IFDEF TTYOBJTTY @echo tty window support included -! IF "$(ADD_CURSES)"=="Y" - @echo curses window support also included +! IF "$(CURSES_CONSOLE)" == "Y" + @echo curses console window support also included +! ENDIF +! IF "$(CURSES_GRAPHICAL)" == "Y" + @echo curses graphical window support also included ! ENDIF !ENDIF ! IF "$(CL)"!="" @@ -1796,28 +2039,30 @@ fetch-lua: fetch-actual-Lua fetch-Lua: fetch-actual-Lua fetch-actual-Lua: - @if not exist $(LIBDIR)\*.* mkdir $(LIBDIR) + @if not exist $(LIBDIR)*.* mkdir $(R_LIBDIR) cd $(LIBDIR) curl -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 - cd ..\src - @echo Lua has been fetched into $(LIBDIR)\lua-$(LUAVER) + cd $(R_SRC) + @echo Lua has been fetched into $(LIBDIR)lua-$(LUAVER) +!IF "$(ADD_CURSES)" == "Y" fetch-pdcurses: - @if not exist $(LIBDIR)\*.* mkdir $(LIBDIR) + @if not exist $(LIBDIR)*.* mkdir $(R_LIBDIR) cd $(LIBDIR) # curl -L -R https://codeload.github.com/wmcbrine/PDCurses/zip/master -o pdcurses.zip - curl -L -R https://github.com/Bill-Gray/PDCursesMod/archive/refs/tags/v4.3.5.zip -o $(PDCDIST).zip + curl -L -R https://github.com/Bill-Gray/PDCursesMod/archive/refs/tags/v4.5.1.zip -o $(PDCDIST).zip powershell -command "Expand-Archive -Path .\$(PDCDIST).zip -DestinationPath ./$(PDCDIST)-temp" if exist .\$(PDCDIST)\* rd .\$(PDCDIST) /s /Q move .\$(PDCDIST)-temp\PDCurses-master . ren PDCurses-master $(PDCDIST) if exist .\$(PDCDIST)-temp\* rd .\$(PDCDIST)-temp /s /Q if exist .\$(PDCDIST).zip del .\$(PDCDIST).zip - cd ..\src - @echo $(PDCDIST) has been fetched into $(LIBDIR)\$(PDCDIST) + cd $(R_SRC) + @echo $(PDCDIST) has been fetched into $(LIBDIR)$(PDCDIST) +!ENDIF #========================================== # DLB utility and nhdatNNN file creation @@ -1833,35 +2078,36 @@ $(U)dlb.exe: $(DLBOBJ_HOST) $(OUTL)dlb$(HOST).o $(OUTLHACKLIB) << !IFDEF TEST_CROSSCOMPILE -$(OUTL)dlb$(HOST).o: $(OUTL)dlb_main$(HOST).o $(OUTL)alloc$(HOST).o $(OUTL)panic$(HOST).o $(INCL)\dlb.h - $(Q)$(CC) $(CFLAGS) $(TTYDEF) $(CROSSCOMPILE) /Fo$@ $(SRC)\dlb.c +$(OUTL)dlb$(HOST).o: $(OUTL)dlb_main$(HOST).o $(OUTL)alloc$(HOST).o $(OUTL)panic$(HOST).o $(INCL)dlb.h + $(Q)$(CC) $(CFLAGS) $(TTYDEF) $(CROSSCOMPILE) /Fo$@ $(SRC)dlb.c !ENDIF -$(OUTL)dlb.o: $(OUTL)dlb_main.o $(OUTL)alloc.o $(OUTL)panic.o $(INCL)\dlb.h - $(Q)$(CC) $(CFLAGS) $(TTYDEF) /Fo$@ $(SRC)\dlb.c +$(OUTL)dlb.o: $(OUTL)dlb_main.o $(OUTL)alloc.o $(OUTL)panic.o $(INCL)dlb.h + $(Q)$(CC) $(CFLAGS) $(TTYDEF) /Fo$@ $(SRC)dlb.c -$(OTTY)dlb.o: $(OTTY)dlb_main.o $(OTTY)alloc.o $(OTTY)panic.o $(INCL)\dlb.h - $(Q)$(CC) $(CFLAGS) $(TTYDEF) /Fo$@ $(SRC)\dlb.c +$(OTTY)dlb.o: $(OTTY)dlb_main.o $(OTTY)alloc.o $(OTTY)panic.o $(INCL)dlb.h + $(Q)$(CC) $(CFLAGS) $(TTYDEF) /Fo$@ $(SRC)dlb.c -$(OGUI)dlb.o: $(OGUI)dlb_main.o $(OGUI)alloc.o $(OGUI)panic.o $(INCL)\dlb.h - $(Q)$(CC) $(CFLAGS) $(TTYDEF) /Fo$@ $(SRC)\dlb.c +$(OGUI)dlb.o: $(OGUI)dlb_main.o $(OGUI)alloc.o $(OGUI)panic.o $(INCL)dlb.h + $(Q)$(CC) $(CFLAGS) $(TTYDEF) /Fo$@ $(SRC)dlb.c -$(OUTL)dlb_main.o: $(UTIL)\dlb_main.c $(INCL)\config.h $(INCL)\dlb.h - $(Q)$(CC) $(CFLAGS) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) /Fo$@ $(UTIL)\dlb_main.c +$(OUTL)dlb_main.o: $(UTIL)dlb_main.c $(INCL)config.h $(INCL)dlb.h + $(Q)$(CC) $(CFLAGS) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) /Fo$@ $(UTIL)dlb_main.c -$(OTTY)dlb_main.o: $(UTIL)\dlb_main.c $(INCL)\config.h $(INCL)\dlb.h - $(Q)$(CC) $(CFLAGS) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) /Fo$@ $(UTIL)\dlb_main.c +$(OTTY)dlb_main.o: $(UTIL)dlb_main.c $(INCL)config.h $(INCL)dlb.h + $(Q)$(CC) $(CFLAGS) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) /Fo$@ $(UTIL)dlb_main.c -$(OGUI)dlb_main.o: $(UTIL)\dlb_main.c $(INCL)\config.h $(INCL)\dlb.h - $(Q)$(CC) $(CFLAGS) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) /Fo$@ $(UTIL)\dlb_main.c +$(OGUI)dlb_main.o: $(UTIL)dlb_main.c $(INCL)config.h $(INCL)dlb.h + $(Q)$(CC) $(CFLAGS) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) /Fo$@ $(UTIL)dlb_main.c -$(DAT)\porthelp: $(MSWSYS)\porthelp - @copy $(MSWSYS)\porthelp $@ >nul +$(DAT)porthelp: $(MSWSYS)porthelp + @copy $(MSWSYS)porthelp $@ >nul -nhdat$(NHV): $(U)dlb.exe $(DAT)\data $(DAT)\oracles $(OPTIONS_FILE) $(LUA_FILES) \ - $(DAT)\rumors $(DAT)\help $(DAT)\hh $(DAT)\cmdhelp $(DAT)\keyhelp \ - $(DAT)\history $(DAT)\opthelp $(DAT)\optmenu $(DAT)\wizhelp $(DAT)\porthelp \ - $(DAT)\license $(DAT)\engrave $(DAT)\epitaph $(DAT)\bogusmon $(DAT)\tribute +!IF ("$(USE_DLB)"=="Y") +nhdat$(NHV): $(U)dlb.exe $(DAT)data $(DAT)oracles $(OPTIONS_FILE) $(LUA_FILES) \ + $(DAT)rumors $(DAT)help $(DAT)hh $(DAT)cmdhelp $(DAT)keyhelp \ + $(DAT)history $(DAT)opthelp $(DAT)optmenu $(DAT)wizhelp $(DAT)porthelp \ + $(DAT)license $(DAT)engrave $(DAT)epitaph $(DAT)bogusmon $(DAT)tribute @echo Building $@ @cd $(DAT) @echo data >dlb.lst @@ -1884,19 +2130,22 @@ nhdat$(NHV): $(U)dlb.exe $(DAT)\data $(DAT)\oracles $(OPTIONS_FILE) $(LUA_FILES) @echo epitaph >>dlb.lst @echo bogusmon >>dlb.lst @echo tribute >>dlb.lst +# @for %%N in ($(luawildcards)) do dir /b %%N >>dlb.lst @for %%N in (*.lua) do @echo %%N >>dlb.lst - @$(U)dlb cIf dlb.lst $(SRC)\nhdat + @$(U)dlb cIf dlb.lst $(SRC)nhdat @cd $(SRC) +!ENDIF #========================================== # Recover Utility #========================================== -$(U)recover.exe: $(RECOVOBJS) +$(U)recover.exe: $(RECOVOBJS) $(OUTLHACKLIB) @echo Linking $(@:\=/) - @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" -out:$@ $(RECOVOBJS) + @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" \ + -out:$@ $(RECOVOBJS) $(OUTLHACKLIB) -$(OUTL)recover.o: $(CONFIG_H) $(U)recover.c $(MSWSYS)\win32api.h +$(OUTL)recover.o: $(CONFIG_H) $(U)recover.c $(MSWSYS)win32api.h $(Q)$(CC) $(CFLAGS) $(TTYDEF) -Fo$@ $(U)recover.c #========================================== @@ -1912,29 +2161,29 @@ $(U)tilemap.exe: $(OUTL)tilemap.o $(OUTL)monst.o $(OUTL)objects.o $(OUTL)drawing @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" $(HACKLIB) -out:$@ \ $(OUTL)tilemap.o $(OUTL)monst.o $(OUTL)objects.o $(OUTL)drawing.o $(OUTLHACKLIB) -$(OUTL)tilemap.o: $(WSHR)\tilemap.c $(HACK_H) - $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) -Fo$@ $(WSHR)\tilemap.c +$(OUTL)tilemap.o: $(WSHR)tilemap.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) -Fo$@ $(WSHR)tilemap.c -$(OUTL)tiletx32.o: $(WSHR)\tilemap.c $(HACK_H) - $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) /DTILETEXT /DTILE_X=32 /DTILE_Y=32 -Fo$@ $(WSHR)\tilemap.c +$(OUTL)tiletx32.o: $(WSHR)tilemap.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) /DTILETEXT /DTILE_X=32 /DTILE_Y=32 -Fo$@ $(WSHR)tilemap.c -$(OUTL)tiletxt.o: $(WSHR)\tilemap.c $(HACK_H) - $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) /DTILETEXT -Fo$@ $(WSHR)\tilemap.c +$(OUTL)tiletxt.o: $(WSHR)tilemap.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) /DTILETEXT -Fo$@ $(WSHR)tilemap.c -$(OUTL)gifread.o: $(WSHR)\gifread.c $(CONFIG_H) $(TILE_H) - $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) -I$(WSHR) -Fo$@ $(WSHR)\gifread.c +$(OUTL)gifread.o: $(WSHR)gifread.c $(CONFIG_H) $(TILE_H) + $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) -I$(WSHR) -Fo$@ $(WSHR)gifread.c -$(OUTL)gifrd32.o: $(WSHR)\gifread.c $(CONFIG_H) $(TILE_H) - $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) -I$(WSHR) /DTILE_X=32 /DTILE_Y=32 -Fo$@ $(WSHR)\gifread.c +$(OUTL)gifrd32.o: $(WSHR)gifread.c $(CONFIG_H) $(TILE_H) + $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) -I$(WSHR) /DTILE_X=32 /DTILE_Y=32 -Fo$@ $(WSHR)gifread.c -$(OUTL)ppmwrite.o: $(WSHR)\ppmwrite.c $(CONFIG_H) $(TILE_H) - $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) -I$(WSHR) -Fo$@ $(WSHR)\ppmwrite.c +$(OUTL)ppmwrite.o: $(WSHR)ppmwrite.c $(CONFIG_H) $(TILE_H) + $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) -I$(WSHR) -Fo$@ $(WSHR)ppmwrite.c -$(OUTL)tiletext.o: $(WSHR)\tiletext.c $(CONFIG_H) $(TILE_H) - $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) -I$(WSHR) -Fo$@ $(WSHR)\tiletext.c +$(OUTL)tiletext.o: $(WSHR)tiletext.c $(CONFIG_H) $(TILE_H) + $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) -I$(WSHR) -Fo$@ $(WSHR)tiletext.c -$(OUTL)tilete32.o: $(WSHR)\tiletext.c $(CONFIG_H) $(TILE_H) - $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) -I$(WSHR) /DTILE_X=32 /DTILE_Y=32 -Fo$@ $(WSHR)\tiletext.c +$(OUTL)tilete32.o: $(WSHR)tiletext.c $(CONFIG_H) $(TILE_H) + $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) -I$(WSHR) /DTILE_X=32 /DTILE_Y=32 -Fo$@ $(WSHR)tiletext.c #========================================== # Optional Tile Utilities @@ -1967,16 +2216,18 @@ $(U)txt2ppm.exe: $(PPMWRITERS) $(TEXT_IO) ) << -$(SRC)\tiles.bmp: $(U)tile2bmp.exe $(TILEFILES) +$(SRC)tiles.bmp: $(U)tile2bmp.exe $(TILEFILES) @echo Creating 16x16 binary tile files (this may take some time) @$(U)tile2bmp $@ -$(U)tile2bmp.exe: $(OUTL)tile2bmp.o $(TEXT_IO) +$(U)tile2bmp.exe: $(OUTL)tile2bmp.o $(TEXT_IO) $(OUTL)alloc.o $(OUTL)panic.o @echo Linking $(@:\=/) @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" $(OUTLHACKLIB) -out:$@ @<<$(@B).lnk $(OUTL)tile2bmp.o $(TEXT_IO:^ =^ ) + $(OUTL)alloc.o + $(OUTL)panic.o << $(U)til2bm32.exe: $(OUTL)til2bm32.o $(TEXT_IO32) @@ -1987,11 +2238,11 @@ $(U)til2bm32.exe: $(OUTL)til2bm32.o $(TEXT_IO32) ) << -$(OUTL)tile2bmp.o: $(WSHR)\tile2bmp.c $(HACK_H) $(TILE_H) $(MSWSYS)\win32api.h - $(Q)$(CC) $(CFLAGS) $(TTYDEF) $(CROSSCOMPILE) -I$(WSHR) /DPACKED_FILE /Fo$@ $(WSHR)\tile2bmp.c +$(OUTL)tile2bmp.o: $(WSHR)tile2bmp.c $(HACK_H) $(TILE_H) $(MSWSYS)win32api.h + $(Q)$(CC) $(CFLAGS) $(TTYDEF) $(CROSSCOMPILE) -I$(WSHR) /DPACKED_FILE /Fo$@ $(WSHR)tile2bmp.c -#$(OUTL)til2bm32.o: $(WSHR)\tile2bmp.c $(HACK_H) $(TILE_H) $(MSWSYS)\win32api.h -# $(Q)$(CC) $(CFLAGS) $(TTYDEF) $(CROSSCOMPILE) -I$(WSHR) /DPACKED_FILE /DTILE_X=32 /DTILE_Y=32 /Fo$@ $(WSHR)\tile2bmp.c +#$(OUTL)til2bm32.o: $(WSHR)tile2bmp.c $(HACK_H) $(TILE_H) $(MSWSYS)win32api.h +# $(Q)$(CC) $(CFLAGS) $(TTYDEF) $(CROSSCOMPILE) -I$(WSHR) /DPACKED_FILE /DTILE_X=32 /DTILE_Y=32 /Fo$@ $(WSHR)tile2bmp.c $(U)tile2x11.exe: $(OUTL)tile2x11.o $(OUTL)tiletext.o $(OUTL)tiletxt.o $(OUTL)alloc.o \ $(OUTL)panic.o $(OUTL)monst.o $(OUTL)objects.o @@ -2007,28 +2258,32 @@ $(U)tile2x11.exe: $(OUTL)tile2x11.o $(OUTL)tiletext.o $(OUTL)tiletxt.o $(OUTL)al $(OUTL)panic.o << -$(OUTL)tile2x11.o: $(X11)\tile2x11.c $(HACK_H) $(TILE_H) $(INCL)\tile2x11.h - $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) -I$(WSHR) /DPACKED_FILE /Fo$@ $(X11)\tile2x11.c +$(OUTL)tile2x11.o: $(X11)tile2x11.c $(HACK_H) $(TILE_H) $(INCL)tile2x11.h + $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) -I$(WSHR) /DPACKED_FILE /Fo$@ $(X11)tile2x11.c -$(SRC)\x11tiles: $(U)tile2x11.exe $(WSHR)\monsters.txt $(WSHR)\objects.txt \ - $(WSHR)\other.txt \ - $(WSHR)\monsters.txt - $(U)tile2x11 $(WSHR)\monsters.txt $(WSHR)\objects.txt \ - $(WSHR)\other.txt \ - -grayscale $(WSHR)\monsters.txt +$(SRC)x11tiles: $(U)tile2x11.exe $(WSHR)monsters.txt $(WSHR)objects.txt \ + $(WSHR)other.txt \ + $(WSHR)monsters.txt + $(U)tile2x11 $(WSHR)monsters.txt $(WSHR)objects.txt \ + $(WSHR)other.txt \ + -grayscale $(WSHR)monsters.txt #=============================================================================== # PDCurses #=============================================================================== - +!IF "$(ADD_CURSES)" == "Y" +!IF "$(CURSES_CONSOLE)" == "Y" $(PDCWINCONLIB) : $(PDCCOMMONOBJS) $(PDCWINCONOBJS) @echo Building library $@ from $** @$(librarian) -nologo /out:$@ $(PDCCOMMONOBJS) $(PDCWINCONOBJS) +!ENDIF +!IF "$(CURSES_GRAPHICAL)" == "Y" $(PDCWINGUILIB) : $(PDCCOMMONOBJS) $(PDCWINGUIOBJS) @echo Building library $@ from $** @$(librarian) -nologo /out:$@ $(PDCCOMMONOBJS) $(PDCWINGUIOBJS) - -$(OGUI)guitty.o: $(MSWSYS)\guitty.c $(WINDHDR) $(HACK_H) $(TILE_H) +!ENDIF +!ENDIF +$(OGUI)guitty.o: $(MSWSYS)guitty.c $(WINDHDR) $(HACK_H) $(TILE_H) #=============================================================================== # LUA @@ -2042,91 +2297,91 @@ lua.exe: $(OLUA)lua.o $(LUALIB) # @echo Linking $(@:\=/) # @$(link) /OUT:$@ $(OLUA)luac.o $(LUALIB) -$(LIBDIR)\lua$(LUAVER)-$(TARGET_CPU).dll: $(LUAOBJFILES) +$(LIBDIR)lua$(LUAVER)-$(TARGET_CPU).dll: $(LUAOBJFILES) @echo Linking $(@:\=/) - @$(link) /DLL /IMPLIB:$(LIBDIR)\lua$(LUAVER).lib /OUT:$@ $(LUAOBJFILES) + @$(link) /DLL /IMPLIB:$(LIBDIR)lua$(LUAVER).lib /OUT:$@ $(LUAOBJFILES) -$(LIBDIR)\lua$(LUAVER)-$(TARGET_CPU)-static.lib: $(LUAOBJFILES) +$(LIBDIR)lua$(LUAVER)-$(TARGET_CPU)-static.lib: $(LUAOBJFILES) @echo Building library $@ from $** @$(librarian) /OUT:$@ $(LUAOBJFILES) -$(OLUA)lua.o: $(LUASRC)\lua.c -#$(OLUA)luac.o: $(LUASRC)\luac.c -$(OLUA)lapi.o: $(LUASRC)\lapi.c - $(Q)$(CC) $(CFLAGS) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -wd4244 -wd4701 -wd4702 -Fo$@ $(LUASRC)\lapi.c +$(OLUA)lua.o: $(LUASRC)lua.c +#$(OLUA)luac.o: $(LUASRC)luac.c +$(OLUA)lapi.o: $(LUASRC)lapi.c + $(Q)$(CC) $(CFLAGS) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -wd4244 -wd4701 -wd4702 -Fo$@ $(LUASRC)lapi.c #=================================================================== # windsound dependencies #=================================================================== !IF "$(SOUND_WINDSOUND)" == "Y" -$(OTTY)windsound.o: ..\sound\windsound\windsound.c $(HACK_H) -$(OGUI)windsound.o: ..\sound\windsound\windsound.c $(HACK_H) +$(OTTY)windsound.o: $(WINDSOUNDDIR)windsound.c $(HACK_H) +$(OGUI)windsound.o: $(WINDSOUNDDIR)windsound.c $(HACK_H) !ENDIF #=================================================================== # fmod dependencies #=================================================================== !IF "$(SOUND_FMOD)" == "Y" -$(OTTY)fmod.o: $(FMODDIR)\fmod.c $(HACK_H) -$(OGUI)fmod.o: $(FMODDIR)\fmod.c $(HACK_H) +$(OTTY)fmod.o: $(FMODDIR)fmod.c $(HACK_H) +$(OGUI)fmod.o: $(FMODDIR)fmod.c $(HACK_H) # Copy the DLL to GAMEDIR -$(GAMEDIR)\$(FMODDLLBASENAME): +$(GAMEDIR)$(FMODDLLBASENAME): copy $(FMODLIBDLL) $@ !ENDIF #=================================================================== # sys/windows dependencies #=================================================================== -$(OTTY)consoletty.o: $(MSWSYS)\consoletty.c $(WINDHDR) $(HACK_H) $(TILE_H) -$(OTTY)win10.o: $(MSWSYS)\win10.c $(WINDHDR) $(HACK_H) -$(OTTY)windsys.o: $(MSWSYS)\windsys.c $(WINDHDR) $(HACK_H) -$(OTTY)windsound.o: ..\sound\windsound\windsound.c $(HACK_H) -#$(OTTY)sample.o: ..\sound\sample\sample.c $(HACK_H) -$(OTTY)windmain.o: $(MSWSYS)\windmain.c $(WINDHDR) $(HACK_H) -$(OTTY)safeproc.o: $(WSHR)\safeproc.c $(WINDHDR) $(HACK_H) +$(OTTY)consoletty.o: $(MSWSYS)consoletty.c $(WINDHDR) $(HACK_H) $(TILE_H) +$(OTTY)win10.o: $(MSWSYS)win10.c $(WINDHDR) $(HACK_H) +$(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: ..\sound\windsound\windsound.c $(HACK_H) -$(OGUI)windmain.o: $(MSWSYS)\windmain.c $(WINDHDR) $(HACK_H) -$(OGUI)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 #=================================================================== -$(OGUI)mhaskyn.o: $(MSWIN)\mhaskyn.c $(MSWIN)\$(@B).h $(WINDHDR) $(HACK_H) -$(OGUI)mhdlg.o: $(MSWIN)\mhdlg.c $(MSWIN)\$(@B).h $(MSWIN)\resource.h $(WINDHDR) $(HACK_H) -$(OGUI)mhfont.o: $(MSWIN)\mhfont.c $(MSWIN)\$(@B).h $(WINDHDR) $(HACK_H) -$(OGUI)mhinput.o: $(MSWIN)\mhinput.c $(MSWIN)\$(@B).h $(MSWIN)\winMS.h $(WINDHDR) $(HACK_H) -$(OGUI)mhmain.o: $(MSWIN)\mhmain.c $(ALL_GUIHDR) $(WINDHDR) $(HACK_H) -$(OGUI)mhmap.o: $(MSWIN)\mhmap.c $(MSWIN)\$(@B).h $(MSWIN)\mhfont.h $(MSWIN)\mhinput.h \ - $(MSWIN)\mhmsg.h $(MSWIN)\resource.h $(WINDHDR) $(HACK_H) -$(OGUI)mhmenu.o: $(MSWIN)\mhmenu.c $(MSWIN)\$(@B).h $(MSWIN)\mhmain.h $(MSWIN)\mhmsg.h \ - $(MSWIN)\mhfont.h $(MSWIN)\mhdlg.h $(MSWIN)\resource.h $(WINDHDR) $(HACK_H) -$(OGUI)mhmsgwnd.o: $(MSWIN)\mhmsgwnd.c $(MSWIN)\$(@B).h $(MSWIN)\mhmsg.h $(MSWIN)\mhfont.h \ - $(MSWIN)\winMS.h $(WINDHDR) $(HACK_H) -$(OGUI)mhrip.o: $(MSWIN)\mhrip.c $(MSWIN)\$(@B).h $(MSWIN)\mhmsg.h $(MSWIN)\mhfont.h \ - $(MSWIN)\resource.h $(WINDHDR) $(HACK_H) -$(OGUI)mhsplash.o: $(MSWIN)\mhsplash.c $(MSWIN)\$(@B).h $(MSWIN)\mhmsg.h $(MSWIN)\mhfont.h \ - $(MSWIN)\resource.h $(WINDHDR) $(HACK_H) -$(OGUI)mhstatus.o: $(MSWIN)\mhstatus.c $(MSWIN)\$(@B).h $(MSWIN)\mhmsg.h $(MSWIN)\mhfont.h \ +$(OGUI)mhaskyn.o: $(MSWIN)mhaskyn.c $(MSWIN)$(@B).h $(WINDHDR) $(HACK_H) +$(OGUI)mhdlg.o: $(MSWIN)mhdlg.c $(MSWIN)$(@B).h $(MSWIN)resource.h $(WINDHDR) $(HACK_H) +$(OGUI)mhfont.o: $(MSWIN)mhfont.c $(MSWIN)$(@B).h $(WINDHDR) $(HACK_H) +$(OGUI)mhinput.o: $(MSWIN)mhinput.c $(MSWIN)$(@B).h $(MSWIN)winMS.h $(WINDHDR) $(HACK_H) +$(OGUI)mhmain.o: $(MSWIN)mhmain.c $(ALL_GUIHDR) $(WINDHDR) $(HACK_H) +$(OGUI)mhmap.o: $(MSWIN)mhmap.c $(MSWIN)$(@B).h $(MSWIN)mhfont.h $(MSWIN)mhinput.h \ + $(MSWIN)mhmsg.h $(MSWIN)resource.h $(WINDHDR) $(HACK_H) +$(OGUI)mhmenu.o: $(MSWIN)mhmenu.c $(MSWIN)$(@B).h $(MSWIN)mhmain.h $(MSWIN)mhmsg.h \ + $(MSWIN)mhfont.h $(MSWIN)mhdlg.h $(MSWIN)resource.h $(WINDHDR) $(HACK_H) +$(OGUI)mhmsgwnd.o: $(MSWIN)mhmsgwnd.c $(MSWIN)$(@B).h $(MSWIN)mhmsg.h $(MSWIN)mhfont.h \ + $(MSWIN)winMS.h $(WINDHDR) $(HACK_H) +$(OGUI)mhrip.o: $(MSWIN)mhrip.c $(MSWIN)$(@B).h $(MSWIN)mhmsg.h $(MSWIN)mhfont.h \ + $(MSWIN)resource.h $(WINDHDR) $(HACK_H) +$(OGUI)mhsplash.o: $(MSWIN)mhsplash.c $(MSWIN)$(@B).h $(MSWIN)mhmsg.h $(MSWIN)mhfont.h \ + $(MSWIN)resource.h $(WINDHDR) $(HACK_H) +$(OGUI)mhstatus.o: $(MSWIN)mhstatus.c $(MSWIN)$(@B).h $(MSWIN)mhmsg.h $(MSWIN)mhfont.h \ $(WINDHDR) $(HACK_H) -$(OGUI)mhtext.o: $(MSWIN)\mhtext.c $(MSWIN)\$(@B).h $(MSWIN)\mhmsg.h $(MSWIN)\mhfont.h \ +$(OGUI)mhtext.o: $(MSWIN)mhtext.c $(MSWIN)$(@B).h $(MSWIN)mhmsg.h $(MSWIN)mhfont.h \ $(WINDHDR) $(HACK_H) -$(OGUI)mswproc.o: $(MSWIN)\mswproc.c $(ALL_GUIHDR) $(MSWIN)\resource.h $(WINDHDR) $(HACK_H) -$(OGUI)NetHackW.o: $(MSWIN)\NetHackW.c $(ALL_GUIHDR) $(MSWIN)\resource.h $(WINDHDR) $(HACK_H) +$(OGUI)mswproc.o: $(MSWIN)mswproc.c $(ALL_GUIHDR) $(MSWIN)resource.h $(WINDHDR) $(HACK_H) +$(OGUI)NetHackW.o: $(MSWIN)NetHackW.c $(ALL_GUIHDR) $(MSWIN)resource.h $(WINDHDR) $(HACK_H) #=================================================================== # sys/share dependencies #=================================================================== -#$(OTTY)cppregex.o: $(SSYS)\cppregex.cpp $(HACK_H) -# $(Q)$(CC) $(CPPFLAGS) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $(SSYS)\cppregex.cpp +#$(OTTY)cppregex.o: $(SSYS)cppregex.cpp $(HACK_H) +# $(Q)$(CC) $(CPPFLAGS) $(TTYDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $(SSYS)cppregex.cpp -#$(OGUI)cppregex.o: $(SSYS)\cppregex.cpp $(HACK_H) -# $(Q)$(CC) $(CPPFLAGS) $(GUIDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $(SSYS)\cppregex.cpp +#$(OGUI)cppregex.o: $(SSYS)cppregex.cpp $(HACK_H) +# $(Q)$(CC) $(CPPFLAGS) $(GUIDEF) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $(SSYS)cppregex.cpp #=============================================================================== # CROSSCOMPILE @@ -2143,13 +2398,13 @@ $(OGUI)NetHackW.o: $(MSWIN)\NetHackW.c $(ALL_GUIHDR) $(MSWIN)\resource.h $(WINDH # !IFDEF TEST_CROSSCOMPILE -$(OUTL)mdlib$(HOST).o: $(SRC)\mdlib.c - $(Q)$(CC) $(CFLAGS) $(TTYDEF) $(CROSSCOMPILE) -Fo$@ $(SRC)\mdlib.c +$(OUTL)mdlib$(HOST).o: $(SRC)mdlib.c + $(Q)$(CC) $(CFLAGS) $(TTYDEF) $(CROSSCOMPILE) -Fo$@ $(SRC)mdlib.c !ENDIF -$(OUTL)mdlib.o: $(SRC)\mdlib.c - $(Q)$(CC) $(CFLAGS) $(TTYDEF) -Fo$@ $(SRC)\mdlib.c -# $(Q)$(CC) $(CFLAGS) $(TTYDEF) /EP -Fo$@ $(SRC)\mdlib.c >$(OUTL)mdlib.c.preproc +$(OUTL)mdlib.o: $(SRC)mdlib.c + $(Q)$(CC) $(CFLAGS) $(TTYDEF) -Fo$@ $(SRC)mdlib.c +# $(Q)$(CC) $(CFLAGS) $(TTYDEF) /EP -Fo$@ $(SRC)mdlib.c >$(OUTL)mdlib.c.preproc #============================================ # util dual-role CROSSCOMPILE dependencies @@ -2170,28 +2425,28 @@ $(OGUI)panic.o: $(U)panic.c $(CONFIG_H) #$(OUTL)drawing_host.o: drawing.c $(CONFIG_H) # $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) -Fo$@ drawing.c -$(OUTL)drawing.o: drawing.c $(CONFIG_H) $(INCL)\color.h \ - $(INCL)\sym.h $(INCL)\defsym.h $(INCL)\rm.h \ - $(INCL)\objclass.h +$(OUTL)drawing.o: drawing.c $(CONFIG_H) $(INCL)color.h \ + $(INCL)sym.h $(INCL)defsym.h $(INCL)rm.h \ + $(INCL)objclass.h $(Q)$(CC) $(CFLAGS) -Fo$@ drawing.c -#$(OUTL)monst_host.o: monst.c $(CONFIG_H) $(INCL)\permonst.h $(INCL)\align.h \ -# $(INCL)\monattk.h $(INCL)\monflag.h $(INCL)\sym.h \ -# $(INCL)\defsym.h $(INCL)\color.h +#$(OUTL)monst_host.o: monst.c $(CONFIG_H) $(INCL)permonst.h $(INCL)align.h \ +# $(INCL)monattk.h $(INCL)monflag.h $(INCL)sym.h \ +# $(INCL)defsym.h $(INCL)color.h # $(Q)$(CC) $(CFLAGS) $(TTYDEF) -Fo$@ monst.c -$(OUTL)monst.o: monst.c $(CONFIG_H) $(INCL)\permonst.h $(INCL)\align.h \ - $(INCL)\monattk.h $(INCL)\monflag.h $(INCL)\sym.h \ - $(INCL)\defsym.h $(INCL)\color.h +$(OUTL)monst.o: monst.c $(CONFIG_H) $(INCL)permonst.h $(INCL)align.h \ + $(INCL)monattk.h $(INCL)monflag.h $(INCL)sym.h \ + $(INCL)defsym.h $(INCL)color.h $(Q)$(CC) $(CFLAGS) -Fo$@ monst.c -#$(OUTL)objects_host.o: objects.c $(CONFIG_H) $(INCL)\obj.h $(INCL)\objclass.h \ -# $(INCL)\prop.h $(INCL)\skills.h $(INCL)\color.h +#$(OUTL)objects_host.o: objects.c $(CONFIG_H) $(INCL)obj.h $(INCL)objclass.h \ +# $(INCL)prop.h $(INCL)skills.h $(INCL)color.h # $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) /EP $(@B).c > $(OUTL)$(@B).c.preproc # $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $(@B).c -$(OUTL)objects.o: objects.c $(CONFIG_H) $(INCL)\obj.h $(INCL)\objclass.h \ - $(INCL)\prop.h $(INCL)\skills.h $(INCL)\color.h +$(OUTL)objects.o: objects.c $(CONFIG_H) $(INCL)obj.h $(INCL)objclass.h \ + $(INCL)prop.h $(INCL)skills.h $(INCL)color.h $(Q)$(CC) $(CFLAGS) /EP $(@B).c > $(OUTL)$(@B).c.preproc $(Q)$(CC) $(CFLAGS) -Fo$@ $(@B).c @@ -2209,122 +2464,291 @@ $(OUTL)alloc.o: alloc.c $(CONFIG_H) # dat dependencies # -$(DAT)\data: $(U)makedefs.exe $(DATABASE) +$(DAT)data: $(U)makedefs.exe $(DATABASE) $(U)makedefs -d -$(DAT)\rumors: $(U)makedefs.exe $(DAT)\rumors.tru $(DAT)\rumors.fal +$(DAT)rumors: $(U)makedefs.exe $(DAT)rumors.tru $(DAT)rumors.fal $(U)makedefs -r -$(DAT)\oracles: $(U)makedefs.exe $(DAT)\oracles.txt +$(DAT)oracles: $(U)makedefs.exe $(DAT)oracles.txt $(U)makedefs -h -$(DAT)\engrave: $(U)makedefs.exe $(DAT)\engrave.txt - $(U)makedefs -s +$(DAT)engrave: $(U)makedefs.exe $(DAT)engrave.txt + $(U)makedefs -2 -$(DAT)\epitaph: $(U)makedefs.exe $(DAT)\epitaph.txt - $(U)makedefs -s +$(DAT)epitaph: $(U)makedefs.exe $(DAT)epitaph.txt + $(U)makedefs -1 -$(DAT)\bogusmon: $(U)makedefs.exe $(DAT)\bogusmon.txt - $(U)makedefs -s +$(DAT)bogusmon: $(U)makedefs.exe $(DAT)bogusmon.txt + $(U)makedefs -3 + +# This is the universal ctags utility which produces the tags in the +# format that util/readtags requires. +# https://github.com/universal-ctags/ctags.git + +#=============================================================================== +# sfutil +#=============================================================================== +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 \ + $(OUTL)sf-cfgfiles.o $(OUTL)sf-dungeon.o $(OUTL)sf-end.o \ + $(OUTL)sf-engrave.o $(OUTL)sf-files.o $(OUTL)sf-light.o \ + $(OUTL)sf-mdlib.o $(OUTL)sf-mkmaze.o $(OUTL)sf-mkroom.o \ + $(OUTL)sf-monst.o $(OUTL)sf-nhlua.o $(OUTL)sf-objects.o \ + $(OUTL)sf-o_init.o $(OUTL)panic.o $(OUTL)sf-region.o \ + $(OUTL)sf-restore.o $(OUTL)sf-rumors.o $(OUTL)sfbase.o \ + $(OUTL)sfexpasc.o $(OUTL)sf-struct.o $(OUTL)strutil.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 + +# +# dependencies for optional sfctool +# +# $(Q)$(CC) $(CFLAGS) -Fo$@ monst.c + +#$(UTIL)sfutil.exe: $(SFCTOOLBIN) +#$(UTIL)sfctool.exe: $(SFCTOOLBIN) + +$(SFCTOOLBIN): $(OUTLHACKLIB) $(SFCTOOLOBJS) + $(link) $(lflags) -out:$@ /PDB:$(BinDir)$(@B).PDB $(SFCTOOLOBJS) \ + $(LIBS) ole32.lib Shell32.lib userenv.lib advapi32.lib $(OUTLHACKLIB) + @echo '$(SFCTOOLBIN) is up to date.' +$(OUTL)sfctool.o: $(UTIL)sfctool.c $(HACK_H) $(INCL)sfprocs.h + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(UTIL)sfctool.c +$(OUTL)sfdata.o: $(UTIL)sfdata.c $(HACK_H) $(INCL)sfprocs.h $(INCL)sfproto.h + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(UTIL)sfdata.c +$(OUTL)sfexpasc.o: $(UTIL)sfexpasc.c $(HACK_H) $(INCL)sfprocs.h $(INCL)sfproto.h $(INCL)sfmacros.h + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(UTIL)sfexpasc.c +$(OUTL)sf-alloc.o: $(SRC)alloc.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)alloc.c +$(OUTL)sf-date.o: $(SRC)date.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)date.c +$(OUTL)sf-decl.o: $(SRC)decl.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)decl.c +$(OUTL)sf-monst.o: $(SRC)monst.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)monst.c +$(OUTL)sf-objects.o: $(SRC)objects.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)objects.c +$(OUTL)sfbase.o: $(SRC)sfbase.c $(HACK_H) $(INCL)sfmacros.h + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)sfbase.c +$(OUTL)sf-struct.o: $(SRC)sfstruct.c $(HACK_H) $(INCL)sfmacros.h + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)sfstruct.c +$(OUTL)sf-artifact.o: $(SRC)artifact.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)artifact.c +$(OUTL)sf-calendar.o: $(SRC)calendar.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)calendar.c +$(OUTL)sf-cfgfiles.o: $(SRC)cfgfiles.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)cfgfiles.c +$(OUTL)sf-dungeon.o: $(SRC)dungeon.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)dungeon.c +$(OUTL)sf-end.o: $(SRC)end.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)end.c +$(OUTL)sf-engrave.o: $(SRC)engrave.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)engrave.c +$(OUTL)sf-files.o: $(SRC)files.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)files.c +$(OUTL)sf-light.o: $(SRC)light.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)light.c +$(OUTL)sf-mdlib.o: $(SRC)mdlib.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)mdlib.c +$(OUTL)sf-mkmaze.o: $(SRC)mkmaze.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)mkmaze.c +$(OUTL)sf-mkroom.o: $(SRC)mkroom.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)mkroom.c +$(OUTL)sf-o_init.o: $(SRC)o_init.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)o_init.c +$(OUTL)sf-region.o: $(SRC)region.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)region.c +$(OUTL)sf-restore.o: $(SRC)restore.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)restore.c +$(OUTL)sf-rumors.o: $(SRC)\rumors.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)rumors.c +$(OUTL)sf-timeout.o: $(SRC)timeout.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)timeout.c +$(OUTL)sf-version.o: $(SRC)version.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)version.c +$(OUTL)sf-worm.o: $(SRC)worm.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)worm.c +$(OUTL)sf-nhlua.o: $(SRC)nhlua.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)nhlua.c +$(OUTL)sf-track.o: $(SRC)track.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)track.c +$(OUTL)sf-sys.o: $(SRC)sys.c $(HACK_H) + $(Q)$(CC) $(CFLAGS) $(SFFLAGS) -Fo$@ -c $(SRC)sys.c +$(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) +$(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 + $(UTIL)sftags.exe +$(UTIL)sfdata.c: $(UTIL)sftags.exe $(UTIL)sf.tags + $(UTIL)sftags.exe +# dependencies for sftags +# # +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)mkroom.h $(INCL)monst.h \ + $(INCL)obj.h $(INCL)objclass.h $(INCL)prop.h \ + $(INCL)quest.h $(INCL)rect.h $(INCL)region.h \ + $(INCL)rm.h $(INCL)skills.h $(INCL)spell.h \ + $(INCL)stairs.h $(INCL)sys.h $(INCL)timeout.h \ + $(INCL)trap.h $(INCL)you.h $(INCL)onames.h \ + $(INCL)wintype.h +# $(INCL)permonst.h +CTAGSOPT = --language-force=c --sort=no -D"Bitfield(x,n)=unsigned x : n" --excmd=pattern +# +$(UTIL)sf.tags: $(CTAGDEP) + $(CTAGSCMD) $(CTAGSOPT) -f $@ $(INCL)align.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)artifact.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(SRC)artifact.c + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)artilist.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)attrib.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(SRC)bones.c + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)context.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)coord.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)decl.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(SRC)decl.c + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)dungeon.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)engrave.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(SRC)engrave.c + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)flag.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)func_tab.h + $(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)mkroom.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)monst.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)defsym.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)obj.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)objclass.h +# $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)permonst.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)prop.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)quest.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)rect.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)region.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)rm.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)skills.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)spell.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)stairs.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)sys.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)timeout.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)trap.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)you.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)onames.h + $(CTAGSCMD) $(CTAGSOPT) -a -f $@ $(INCL)wintype.h #=============================================================================== # Integrated sound files #=============================================================================== -$(SndWavDir)\se_squeak_A.wav: $(SndWavDir)\se_squeak_A.uu $(U)uudecode.exe -$(SndWavDir)\se_squeak_B.wav: $(SndWavDir)\se_squeak_B.uu $(U)uudecode.exe -$(SndWavDir)\se_squeak_B_flat.wav: $(SndWavDir)\se_squeak_B_flat.uu $(U)uudecode.exe -$(SndWavDir)\se_squeak_C.wav: $(SndWavDir)\se_squeak_C.uu $(U)uudecode.exe -$(SndWavDir)\se_squeak_D.wav: $(SndWavDir)\se_squeak_D.uu $(U)uudecode.exe -$(SndWavDir)\se_squeak_D_flat.wav: $(SndWavDir)\se_squeak_D_flat.uu $(U)uudecode.exe -$(SndWavDir)\se_squeak_E.wav: $(SndWavDir)\se_squeak_E.uu $(U)uudecode.exe -$(SndWavDir)\se_squeak_E_flat.wav: $(SndWavDir)\se_squeak_E_flat.uu $(U)uudecode.exe -$(SndWavDir)\se_squeak_F.wav: $(SndWavDir)\se_squeak_F.uu $(U)uudecode.exe -$(SndWavDir)\se_squeak_F_sharp.wav: $(SndWavDir)\se_squeak_F_sharp.uu $(U)uudecode.exe -$(SndWavDir)\se_squeak_G.wav: $(SndWavDir)\se_squeak_G.uu $(U)uudecode.exe -$(SndWavDir)\se_squeak_G_sharp.wav: $(SndWavDir)\se_squeak_G_sharp.uu $(U)uudecode.exe -$(SndWavDir)\sound_Bell.wav: $(SndWavDir)\sound_Bell.uu $(U)uudecode.exe -$(SndWavDir)\sound_Bugle_A.wav: $(SndWavDir)\sound_Bugle_A.uu $(U)uudecode.exe -$(SndWavDir)\sound_Bugle_B.wav: $(SndWavDir)\sound_Bugle_B.uu $(U)uudecode.exe -$(SndWavDir)\sound_Bugle_C.wav: $(SndWavDir)\sound_Bugle_C.uu $(U)uudecode.exe -$(SndWavDir)\sound_Bugle_D.wav: $(SndWavDir)\sound_Bugle_D.uu $(U)uudecode.exe -$(SndWavDir)\sound_Bugle_E.wav: $(SndWavDir)\sound_Bugle_E.uu $(U)uudecode.exe -$(SndWavDir)\sound_Bugle_F.wav: $(SndWavDir)\sound_Bugle_F.uu $(U)uudecode.exe -$(SndWavDir)\sound_Bugle_G.wav: $(SndWavDir)\sound_Bugle_G.uu $(U)uudecode.exe -$(SndWavDir)\sound_Drum_Of_Earthquake.wav: $(SndWavDir)\sound_Drum_Of_Earthquake.uu $(U)uudecode.exe -$(SndWavDir)\sound_Fire_Horn.wav: $(SndWavDir)\sound_Fire_Horn.uu $(U)uudecode.exe -$(SndWavDir)\sound_Frost_Horn.wav: $(SndWavDir)\sound_Frost_Horn.uu $(U)uudecode.exe -$(SndWavDir)\sound_Leather_Drum.wav: $(SndWavDir)\sound_Leather_Drum.uu $(U)uudecode.exe -$(SndWavDir)\sound_Magic_Harp_A.wav: $(SndWavDir)\sound_Magic_Harp_A.uu $(U)uudecode.exe -$(SndWavDir)\sound_Magic_Harp_B.wav: $(SndWavDir)\sound_Magic_Harp_B.uu $(U)uudecode.exe -$(SndWavDir)\sound_Magic_Harp_C.wav: $(SndWavDir)\sound_Magic_Harp_C.uu $(U)uudecode.exe -$(SndWavDir)\sound_Magic_Harp_D.wav: $(SndWavDir)\sound_Magic_Harp_D.uu $(U)uudecode.exe -$(SndWavDir)\sound_Magic_Harp_E.wav: $(SndWavDir)\sound_Magic_Harp_E.uu $(U)uudecode.exe -$(SndWavDir)\sound_Magic_Harp_F.wav: $(SndWavDir)\sound_Magic_Harp_F.uu $(U)uudecode.exe -$(SndWavDir)\sound_Magic_Harp_G.wav: $(SndWavDir)\sound_Magic_Harp_G.uu $(U)uudecode.exe -$(SndWavDir)\sound_Magic_Flute_A.wav: $(SndWavDir)\sound_Magic_Flute_A.uu $(U)uudecode.exe -$(SndWavDir)\sound_Magic_Flute_B.wav: $(SndWavDir)\sound_Magic_Flute_B.uu $(U)uudecode.exe -$(SndWavDir)\sound_Magic_Flute_C.wav: $(SndWavDir)\sound_Magic_Flute_C.uu $(U)uudecode.exe -$(SndWavDir)\sound_Magic_Flute_D.wav: $(SndWavDir)\sound_Magic_Flute_D.uu $(U)uudecode.exe -$(SndWavDir)\sound_Magic_Flute_E.wav: $(SndWavDir)\sound_Magic_Flute_E.uu $(U)uudecode.exe -$(SndWavDir)\sound_Magic_Flute_F.wav: $(SndWavDir)\sound_Magic_Flute_F.uu $(U)uudecode.exe -$(SndWavDir)\sound_Magic_Flute_G.wav: $(SndWavDir)\sound_Magic_Flute_G.uu $(U)uudecode.exe -$(SndWavDir)\sound_Tooled_Horn_A.wav: $(SndWavDir)\sound_Tooled_Horn_A.uu $(U)uudecode.exe -$(SndWavDir)\sound_Tooled_Horn_B.wav: $(SndWavDir)\sound_Tooled_Horn_B.uu $(U)uudecode.exe -$(SndWavDir)\sound_Tooled_Horn_C.wav: $(SndWavDir)\sound_Tooled_Horn_C.uu $(U)uudecode.exe -$(SndWavDir)\sound_Tooled_Horn_D.wav: $(SndWavDir)\sound_Tooled_Horn_D.uu $(U)uudecode.exe -$(SndWavDir)\sound_Tooled_Horn_E.wav: $(SndWavDir)\sound_Tooled_Horn_E.uu $(U)uudecode.exe -$(SndWavDir)\sound_Tooled_Horn_F.wav: $(SndWavDir)\sound_Tooled_Horn_F.uu $(U)uudecode.exe -$(SndWavDir)\sound_Tooled_Horn_G.wav: $(SndWavDir)\sound_Tooled_Horn_G.uu $(U)uudecode.exe -$(SndWavDir)\sound_Wooden_Flute_A.wav: $(SndWavDir)\sound_Wooden_Flute_A.uu $(U)uudecode.exe -$(SndWavDir)\sound_Wooden_Flute_B.wav: $(SndWavDir)\sound_Wooden_Flute_B.uu $(U)uudecode.exe -$(SndWavDir)\sound_Wooden_Flute_C.wav: $(SndWavDir)\sound_Wooden_Flute_C.uu $(U)uudecode.exe -$(SndWavDir)\sound_Wooden_Flute_D.wav: $(SndWavDir)\sound_Wooden_Flute_D.uu $(U)uudecode.exe -$(SndWavDir)\sound_Wooden_Flute_E.wav: $(SndWavDir)\sound_Wooden_Flute_E.uu $(U)uudecode.exe -$(SndWavDir)\sound_Wooden_Flute_F.wav: $(SndWavDir)\sound_Wooden_Flute_F.uu $(U)uudecode.exe -$(SndWavDir)\sound_Wooden_Flute_G.wav: $(SndWavDir)\sound_Wooden_Flute_G.uu $(U)uudecode.exe -$(SndWavDir)\sound_Wooden_Harp_A.wav: $(SndWavDir)\sound_Wooden_Harp_A.uu $(U)uudecode.exe -$(SndWavDir)\sound_Wooden_Harp_B.wav: $(SndWavDir)\sound_Wooden_Harp_B.uu $(U)uudecode.exe -$(SndWavDir)\sound_Wooden_Harp_C.wav: $(SndWavDir)\sound_Wooden_Harp_C.uu $(U)uudecode.exe -$(SndWavDir)\sound_Wooden_Harp_D.wav: $(SndWavDir)\sound_Wooden_Harp_D.uu $(U)uudecode.exe -$(SndWavDir)\sound_Wooden_Harp_E.wav: $(SndWavDir)\sound_Wooden_Harp_E.uu $(U)uudecode.exe -$(SndWavDir)\sound_Wooden_Harp_F.wav: $(SndWavDir)\sound_Wooden_Harp_F.uu $(U)uudecode.exe -$(SndWavDir)\sound_Wooden_Harp_G.wav: $(SndWavDir)\sound_Wooden_Harp_G.uu $(U)uudecode.exe -$(SndWavDir)\sa2_xpleveldown.wav: $(SndWavDir)\sa2_xpleveldown.uu $(U)uudecode.exe -$(SndWavDir)\sa2_xplevelup.wav: $(SndWavDir)\sa2_xplevelup.uu $(U)uudecode.exe +$(SndWavDir)se_squeak_A.wav: $(SndWavDir)se_squeak_A.uu $(U)uudecode.exe +$(SndWavDir)se_squeak_B.wav: $(SndWavDir)se_squeak_B.uu $(U)uudecode.exe +$(SndWavDir)se_squeak_B_flat.wav: $(SndWavDir)se_squeak_B_flat.uu $(U)uudecode.exe +$(SndWavDir)se_squeak_C.wav: $(SndWavDir)se_squeak_C.uu $(U)uudecode.exe +$(SndWavDir)se_squeak_D.wav: $(SndWavDir)se_squeak_D.uu $(U)uudecode.exe +$(SndWavDir)se_squeak_D_flat.wav: $(SndWavDir)se_squeak_D_flat.uu $(U)uudecode.exe +$(SndWavDir)se_squeak_E.wav: $(SndWavDir)se_squeak_E.uu $(U)uudecode.exe +$(SndWavDir)se_squeak_E_flat.wav: $(SndWavDir)se_squeak_E_flat.uu $(U)uudecode.exe +$(SndWavDir)se_squeak_F.wav: $(SndWavDir)se_squeak_F.uu $(U)uudecode.exe +$(SndWavDir)se_squeak_F_sharp.wav: $(SndWavDir)se_squeak_F_sharp.uu $(U)uudecode.exe +$(SndWavDir)se_squeak_G.wav: $(SndWavDir)se_squeak_G.uu $(U)uudecode.exe +$(SndWavDir)se_squeak_G_sharp.wav: $(SndWavDir)se_squeak_G_sharp.uu $(U)uudecode.exe +$(SndWavDir)sound_Bell.wav: $(SndWavDir)sound_Bell.uu $(U)uudecode.exe +$(SndWavDir)sound_Bugle_A.wav: $(SndWavDir)sound_Bugle_A.uu $(U)uudecode.exe +$(SndWavDir)sound_Bugle_B.wav: $(SndWavDir)sound_Bugle_B.uu $(U)uudecode.exe +$(SndWavDir)sound_Bugle_C.wav: $(SndWavDir)sound_Bugle_C.uu $(U)uudecode.exe +$(SndWavDir)sound_Bugle_D.wav: $(SndWavDir)sound_Bugle_D.uu $(U)uudecode.exe +$(SndWavDir)sound_Bugle_E.wav: $(SndWavDir)sound_Bugle_E.uu $(U)uudecode.exe +$(SndWavDir)sound_Bugle_F.wav: $(SndWavDir)sound_Bugle_F.uu $(U)uudecode.exe +$(SndWavDir)sound_Bugle_G.wav: $(SndWavDir)sound_Bugle_G.uu $(U)uudecode.exe +$(SndWavDir)sound_Drum_Of_Earthquake.wav: $(SndWavDir)sound_Drum_Of_Earthquake.uu $(U)uudecode.exe +$(SndWavDir)sound_Fire_Horn.wav: $(SndWavDir)sound_Fire_Horn.uu $(U)uudecode.exe +$(SndWavDir)sound_Frost_Horn.wav: $(SndWavDir)sound_Frost_Horn.uu $(U)uudecode.exe +$(SndWavDir)sound_Leather_Drum.wav: $(SndWavDir)sound_Leather_Drum.uu $(U)uudecode.exe +$(SndWavDir)sound_Magic_Harp_A.wav: $(SndWavDir)sound_Magic_Harp_A.uu $(U)uudecode.exe +$(SndWavDir)sound_Magic_Harp_B.wav: $(SndWavDir)sound_Magic_Harp_B.uu $(U)uudecode.exe +$(SndWavDir)sound_Magic_Harp_C.wav: $(SndWavDir)sound_Magic_Harp_C.uu $(U)uudecode.exe +$(SndWavDir)sound_Magic_Harp_D.wav: $(SndWavDir)sound_Magic_Harp_D.uu $(U)uudecode.exe +$(SndWavDir)sound_Magic_Harp_E.wav: $(SndWavDir)sound_Magic_Harp_E.uu $(U)uudecode.exe +$(SndWavDir)sound_Magic_Harp_F.wav: $(SndWavDir)sound_Magic_Harp_F.uu $(U)uudecode.exe +$(SndWavDir)sound_Magic_Harp_G.wav: $(SndWavDir)sound_Magic_Harp_G.uu $(U)uudecode.exe +$(SndWavDir)sound_Magic_Flute_A.wav: $(SndWavDir)sound_Magic_Flute_A.uu $(U)uudecode.exe +$(SndWavDir)sound_Magic_Flute_B.wav: $(SndWavDir)sound_Magic_Flute_B.uu $(U)uudecode.exe +$(SndWavDir)sound_Magic_Flute_C.wav: $(SndWavDir)sound_Magic_Flute_C.uu $(U)uudecode.exe +$(SndWavDir)sound_Magic_Flute_D.wav: $(SndWavDir)sound_Magic_Flute_D.uu $(U)uudecode.exe +$(SndWavDir)sound_Magic_Flute_E.wav: $(SndWavDir)sound_Magic_Flute_E.uu $(U)uudecode.exe +$(SndWavDir)sound_Magic_Flute_F.wav: $(SndWavDir)sound_Magic_Flute_F.uu $(U)uudecode.exe +$(SndWavDir)sound_Magic_Flute_G.wav: $(SndWavDir)sound_Magic_Flute_G.uu $(U)uudecode.exe +$(SndWavDir)sound_Tooled_Horn_A.wav: $(SndWavDir)sound_Tooled_Horn_A.uu $(U)uudecode.exe +$(SndWavDir)sound_Tooled_Horn_B.wav: $(SndWavDir)sound_Tooled_Horn_B.uu $(U)uudecode.exe +$(SndWavDir)sound_Tooled_Horn_C.wav: $(SndWavDir)sound_Tooled_Horn_C.uu $(U)uudecode.exe +$(SndWavDir)sound_Tooled_Horn_D.wav: $(SndWavDir)sound_Tooled_Horn_D.uu $(U)uudecode.exe +$(SndWavDir)sound_Tooled_Horn_E.wav: $(SndWavDir)sound_Tooled_Horn_E.uu $(U)uudecode.exe +$(SndWavDir)sound_Tooled_Horn_F.wav: $(SndWavDir)sound_Tooled_Horn_F.uu $(U)uudecode.exe +$(SndWavDir)sound_Tooled_Horn_G.wav: $(SndWavDir)sound_Tooled_Horn_G.uu $(U)uudecode.exe +$(SndWavDir)sound_Wooden_Flute_A.wav: $(SndWavDir)sound_Wooden_Flute_A.uu $(U)uudecode.exe +$(SndWavDir)sound_Wooden_Flute_B.wav: $(SndWavDir)sound_Wooden_Flute_B.uu $(U)uudecode.exe +$(SndWavDir)sound_Wooden_Flute_C.wav: $(SndWavDir)sound_Wooden_Flute_C.uu $(U)uudecode.exe +$(SndWavDir)sound_Wooden_Flute_D.wav: $(SndWavDir)sound_Wooden_Flute_D.uu $(U)uudecode.exe +$(SndWavDir)sound_Wooden_Flute_E.wav: $(SndWavDir)sound_Wooden_Flute_E.uu $(U)uudecode.exe +$(SndWavDir)sound_Wooden_Flute_F.wav: $(SndWavDir)sound_Wooden_Flute_F.uu $(U)uudecode.exe +$(SndWavDir)sound_Wooden_Flute_G.wav: $(SndWavDir)sound_Wooden_Flute_G.uu $(U)uudecode.exe +$(SndWavDir)sound_Wooden_Harp_A.wav: $(SndWavDir)sound_Wooden_Harp_A.uu $(U)uudecode.exe +$(SndWavDir)sound_Wooden_Harp_B.wav: $(SndWavDir)sound_Wooden_Harp_B.uu $(U)uudecode.exe +$(SndWavDir)sound_Wooden_Harp_C.wav: $(SndWavDir)sound_Wooden_Harp_C.uu $(U)uudecode.exe +$(SndWavDir)sound_Wooden_Harp_D.wav: $(SndWavDir)sound_Wooden_Harp_D.uu $(U)uudecode.exe +$(SndWavDir)sound_Wooden_Harp_E.wav: $(SndWavDir)sound_Wooden_Harp_E.uu $(U)uudecode.exe +$(SndWavDir)sound_Wooden_Harp_F.wav: $(SndWavDir)sound_Wooden_Harp_F.uu $(U)uudecode.exe +$(SndWavDir)sound_Wooden_Harp_G.wav: $(SndWavDir)sound_Wooden_Harp_G.uu $(U)uudecode.exe +$(SndWavDir)sa2_xpleveldown.wav: $(SndWavDir)sa2_xpleveldown.uu $(U)uudecode.exe +$(SndWavDir)sa2_xplevelup.wav: $(SndWavDir)sa2_xplevelup.uu $(U)uudecode.exe #=============================================================================== # packaging #=============================================================================== PKGFILES = nethackrc.template Guidebook.txt license NetHack.exe NetHack.txt \ - NetHackW.exe opthelp nhdat370 record symbols sysconf.template -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 -DBGSYMS = NetHack.PDB NetHackW.PDB -PDBTOZIP = $(BinDir)\NetHack.PDB $(BinDir)\NetHackW.PDB -MAINZIP = $(PkgDir)\nethack-$(NHV)-win-$(TARGET_CPU).zip -DBGSYMZIP = $(PkgDir)\nethack-$(NHV)-win-$(TARGET_CPU)-debugsymbols.zip + NetHackW.exe opthelp nhdat370 record symbols sysconf.template \ + sfctool.exe +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 + +DBGSYMS = NetHack.PDB NetHackW.PDB sfctool.PDB +PDBTOZIP = $(BinDir)NetHack.PDB $(BinDir)NetHackW.PDB $(BinDir)sfctool.PDB +MAINZIP = $(PkgDir)nethack-$(NHV)-win-$(TARGET_CPU).zip +DBGSYMZIP = $(PkgDir)nethack-$(NHV)-win-$(TARGET_CPU)-debugsymbols.zip package: binary $(FILESTOZIP) $(MAINZIP) $(DBGSYMZIP) @echo NetHack Windows package created: $(MAINZIP) $(MAINZIP): $(FILESTOZIP) - if not exist $(PkgDir)\*.* mkdir $(PkgDir) - tar -a -cf $(MAINZIP) -C $(BinDir) $(PKGFILES) + if not exist $(PkgDir)*.* mkdir $(R_PkgDir) + tar -a -cf $(MAINZIP) -C $(R_BinDir) $(PKGFILES) $(DBGSYMZIP): $(PDBTOZIP) - tar -a -cf $(DBGSYMZIP) -C $(BinDir) $(DBGSYMS) + tar -a -cf $(DBGSYMZIP) -C $(R_BinDir) $(DBGSYMS) 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 \ - $(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 $(GAMEDIRDLLS) binary.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 @echo NetHack is up to date. #=============================================================================== @@ -2332,21 +2756,21 @@ binary: envchk.tag libdir.tag ottydir$(TARGET_CPU).tag \ #=============================================================================== spotless: clean - 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.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 $(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 @@ -2360,51 +2784,53 @@ spotless: clean 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 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)-wingui-$(TARGET_CPU)-static.lib del $(LIBDIR)\$(PDCDIST)-wingui-$(TARGET_CPU)-static.lib + if exist $(LIBDIR)$(PDCDIST)-wincon-$(TARGET_CPU)-static.lib del $(LIBDIR)$(PDCDIST)-wincon-$(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) - if exist $(DAT)\oracles del $(DAT)\oracles - if exist $(DAT)\rumors del $(DAT)\rumors - if exist $(DAT)\options del $(DAT)\options - if exist $(DAT)\ttyoptions del $(DAT)\ttyoptions - if exist $(DAT)\guioptions del $(DAT)\guioptions - if exist $(DAT)\data del $(DAT)\data + if exist $(DAT)oracles del $(DAT)oracles + if exist $(DAT)rumors del $(DAT)rumors + if exist $(DAT)options del $(DAT)options + if exist $(DAT)ttyoptions del $(DAT)ttyoptions + 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 $(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 clean: if exist binary.tag del binary.tag @@ -2416,14 +2842,14 @@ 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 @@ -2457,7 +2883,7 @@ clean: 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 $(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 @@ -2468,7 +2894,7 @@ clean: # The rest are stolen from sys/unix/Makefile.src, # twice, with the following changes: # * the CONFIG_H and HACK_H sections comment out or removed completely -# * ../include/ changed to $(INCL)\ +# * ../include/ changed to $(INCL) # * slashes changed to back-slashes # * -c (which is included in cflagsBuild) substituted with -Fo$@ # * "-o $@ " is removed @@ -2496,15 +2922,15 @@ MOCPATH = moc.exe #$(OTTY)cppregex.o: ..\sys\share\cppregex.cpp $(CONFIG_H) # $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\sys\share\cppregex.cpp -$(OTTY)ioctl.o: ..\sys\share\ioctl.c $(HACK_H) $(INCL)\tcap.h +$(OTTY)ioctl.o: ..\sys\share\ioctl.c $(HACK_H) $(INCL)tcap.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\ioctl.c -$(OTTY)pcmain.o: ..\sys\share\pcmain.c $(HACK_H) $(INCL)\dlb.h +$(OTTY)pcmain.o: ..\sys\share\pcmain.c $(HACK_H) $(INCL)dlb.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\pcmain.c -$(OTTY)pcsys.o: ..\sys\share\pcsys.c $(HACK_H) $(INCL)\wintty.h +$(OTTY)pcsys.o: ..\sys\share\pcsys.c $(HACK_H) $(INCL)wintty.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\pcsys.c -$(OTTY)pctty.o: ..\sys\share\pctty.c $(HACK_H) $(INCL)\wintty.h +$(OTTY)pctty.o: ..\sys\share\pctty.c $(HACK_H) $(INCL)wintty.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\pctty.c -$(OTTY)pcunix.o: ..\sys\share\pcunix.c $(HACK_H) $(INCL)\wintty.h +$(OTTY)pcunix.o: ..\sys\share\pcunix.c $(HACK_H) $(INCL)wintty.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\pcunix.c $(OTTY)pmatchregex.o: ..\sys\share\pmatchregex.c $(HACK_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\pmatchregex.c @@ -2514,14 +2940,14 @@ $(OTTY)random.o: ..\sys\share\random.c $(HACK_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\random.c $(OTTY)unixtty.o: ..\sys\share\unixtty.c $(HACK_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\unixtty.c -$(OTTY)unixmain.o: ..\sys\unix\unixmain.c $(HACK_H) $(INCL)\dlb.h +$(OTTY)unixmain.o: ..\sys\unix\unixmain.c $(HACK_H) $(INCL)dlb.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\unix\unixmain.c $(OTTY)unixres.o: ..\sys\unix\unixres.c $(CONFIG_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\unix\unixres.c $(OTTY)unixunix.o: ..\sys\unix\unixunix.c $(HACK_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\unix\unixunix.c $(OTTY)qt_bind.o: ..\win\Qt\qt_bind.cpp $(HACK_H) $(QTn_H) \ - $(INCL)\dlb.h ..\win\Qt\qt_bind.h ..\win\Qt\qt_click.h \ + $(INCL)dlb.h ..\win\Qt\qt_bind.h ..\win\Qt\qt_click.h \ ..\win\Qt\qt_clust.h ..\win\Qt\qt_delay.h ..\win\Qt\qt_icon.h \ ..\win\Qt\qt_kde0.h ..\win\Qt\qt_key.h ..\win\Qt\qt_line.h \ ..\win\Qt\qt_main.h ..\win\Qt\qt_map.h ..\win\Qt\qt_menu.h \ @@ -2540,7 +2966,7 @@ $(OTTY)qt_delay.o: ..\win\Qt\qt_delay.cpp $(HACK_H) $(QTn_H) \ ..\win\Qt\qt_delay.h ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_delay.cpp $(OTTY)qt_glyph.o: ..\win\Qt\qt_glyph.cpp $(HACK_H) $(QTn_H) \ - $(INCL)\tile2x11.h ..\win\Qt\qt_bind.h \ + $(INCL)tile2x11.h ..\win\Qt\qt_bind.h \ ..\win\Qt\qt_clust.h ..\win\Qt\qt_glyph.h ..\win\Qt\qt_inv.h \ ..\win\Qt\qt_kde0.h ..\win\Qt\qt_main.h ..\win\Qt\qt_map.h \ ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h ..\win\Qt\qt_set.h \ @@ -2633,7 +3059,7 @@ $(OTTY)qt_win.o: ..\win\Qt\qt_win.cpp $(HACK_H) $(QTn_H) \ ..\win\Qt\qt_set.h ..\win\Qt\qt_win.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_win.cpp $(OTTY)qt_xcmd.o: ..\win\Qt\qt_xcmd.cpp $(HACK_H) $(QTn_H) \ - $(INCL)\func_tab.h ..\win\Qt\qt_bind.h ..\win\Qt\qt_kde0.h \ + $(INCL)func_tab.h ..\win\Qt\qt_bind.h ..\win\Qt\qt_kde0.h \ ..\win\Qt\qt_key.h ..\win\Qt\qt_main.h ..\win\Qt\qt_post.h \ ..\win\Qt\qt_pre.h ..\win\Qt\qt_set.h ..\win\Qt\qt_str.h \ ..\win\Qt\qt_xcmd.h qt_xcmd.moc @@ -2642,119 +3068,120 @@ $(OTTY)qt_yndlg.o: ..\win\Qt\qt_yndlg.cpp $(HACK_H) $(QTn_H) \ ..\win\Qt\qt_key.h ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h \ ..\win\Qt\qt_str.h ..\win\Qt\qt_yndlg.h qt_yndlg.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_yndlg.cpp -#$(OTTY)Window.o: ..\win\X11\Window.c $(CONFIG_H) $(INCL)\lint.h \ -# $(INCL)\winX.h $(INCL)\wintype.h $(INCL)\xwindow.h \ -# $(INCL)\xwindowp.h +#$(OTTY)Window.o: ..\win\X11\Window.c $(CONFIG_H) $(INCL)lint.h \ +# $(INCL)winX.h $(INCL)wintype.h $(INCL)xwindow.h \ +# $(INCL)xwindowp.h # $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\Window.c -$(OTTY)dialogs.o: ..\win\X11\dialogs.c $(CONFIG_H) $(INCL)\lint.h \ - $(INCL)\winX.h $(INCL)\wintype.h +$(OTTY)dialogs.o: ..\win\X11\dialogs.c $(CONFIG_H) $(INCL)lint.h \ + $(INCL)winX.h $(INCL)wintype.h # $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\dialogs.c -$(OTTY)winX.o: ..\win\X11\winX.c $(HACK_H) $(INCL)\dlb.h \ - $(INCL)\winX.h $(INCL)\xwindow.h ..\win\X11\nh32icon \ +$(OTTY)winX.o: ..\win\X11\winX.c $(HACK_H) $(INCL)dlb.h \ + $(INCL)winX.h $(INCL)xwindow.h ..\win\X11\nh32icon \ ..\win\X11\nh56icon ..\win\X11\nh72icon # $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winX.c -$(OTTY)winmap.o: ..\win\X11\winmap.c $(HACK_H) $(INCL)\dlb.h \ - $(INCL)\tile2x11.h $(INCL)\winX.h $(INCL)\xwindow.h +$(OTTY)winmap.o: ..\win\X11\winmap.c $(HACK_H) $(INCL)dlb.h \ + $(INCL)tile2x11.h $(INCL)winX.h $(INCL)xwindow.h # $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winmap.c -$(OTTY)winmenu.o: ..\win\X11\winmenu.c $(HACK_H) $(INCL)\winX.h +$(OTTY)winmenu.o: ..\win\X11\winmenu.c $(HACK_H) $(INCL)winX.h # $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winmenu.c -$(OTTY)winmesg.o: ..\win\X11\winmesg.c $(HACK_H) $(INCL)\winX.h \ - $(INCL)\xwindow.h +$(OTTY)winmesg.o: ..\win\X11\winmesg.c $(HACK_H) $(INCL)winX.h \ + $(INCL)xwindow.h # $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winmesg.c -$(OTTY)winmisc.o: ..\win\X11\winmisc.c $(HACK_H) $(INCL)\func_tab.h \ - $(INCL)\winX.h +$(OTTY)winmisc.o: ..\win\X11\winmisc.c $(HACK_H) $(INCL)func_tab.h \ + $(INCL)winX.h # $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winmisc.c -$(OTTY)winstat.o: ..\win\X11\winstat.c $(HACK_H) $(INCL)\winX.h \ - $(INCL)\xwindow.h +$(OTTY)winstat.o: ..\win\X11\winstat.c $(HACK_H) $(INCL)winX.h \ + $(INCL)xwindow.h # $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winstat.c -$(OTTY)wintext.o: ..\win\X11\wintext.c $(HACK_H) $(INCL)\winX.h \ - $(INCL)\xwindow.h +$(OTTY)wintext.o: ..\win\X11\wintext.c $(HACK_H) $(INCL)winX.h \ + $(INCL)xwindow.h # $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\wintext.c -$(OTTY)winval.o: ..\win\X11\winval.c $(HACK_H) $(INCL)\winX.h +$(OTTY)winval.o: ..\win\X11\winval.c $(HACK_H) $(INCL)winX.h # $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winval.c $(OTTY)wc_chainin.o: ..\win\chain\wc_chainin.c $(HACK_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\chain\wc_chainin.c $(OTTY)wc_chainout.o: ..\win\chain\wc_chainout.c $(HACK_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\chain\wc_chainout.c $(OTTY)wc_trace.o: ..\win\chain\wc_trace.c $(HACK_H) \ - $(INCL)\func_tab.h $(INCL)\wintty.h + $(INCL)func_tab.h $(INCL)wintty.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\chain\wc_trace.c $(OTTY)cursdial.o: ..\win\curses\cursdial.c $(HACK_H) \ - $(INCL)\func_tab.h $(INCL)\wincurs.h \ + $(INCL)func_tab.h $(INCL)wincurs.h \ ..\win\curses\cursdial.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursdial.c $(OTTY)cursinit.o: ..\win\curses\cursinit.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\cursinit.h + $(INCL)wincurs.h ..\win\curses\cursinit.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursinit.c $(OTTY)cursinvt.o: ..\win\curses\cursinvt.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\cursinvt.h + $(INCL)wincurs.h ..\win\curses\cursinvt.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursinvt.c -$(OTTY)cursmain.o: ..\win\curses\cursmain.c $(HACK_H) $(INCL)\wincurs.h +$(OTTY)cursmain.o: ..\win\curses\cursmain.c $(HACK_H) $(INCL)wincurs.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursmain.c $(OTTY)cursmesg.o: ..\win\curses\cursmesg.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\cursmesg.h + $(INCL)wincurs.h ..\win\curses\cursmesg.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursmesg.c -$(OTTY)cursmisc.o: ..\win\curses\cursmisc.c $(HACK_H) $(INCL)\dlb.h \ - $(INCL)\func_tab.h $(INCL)\wincurs.h \ +$(OTTY)cursmisc.o: ..\win\curses\cursmisc.c $(HACK_H) $(INCL)dlb.h \ + $(INCL)func_tab.h $(INCL)wincurs.h \ ..\win\curses\cursmisc.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursmisc.c $(OTTY)cursstat.o: ..\win\curses\cursstat.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\cursstat.h + $(INCL)wincurs.h ..\win\curses\cursstat.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursstat.c $(OTTY)curswins.o: ..\win\curses\curswins.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\curswins.h + $(INCL)wincurs.h ..\win\curses\curswins.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\curswins.c $(OTTY)winshim.o: ..\win\shim\winshim.c $(HACK_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\shim\winshim.c -$(OTTY)getline.o: ..\win\tty\getline.c $(HACK_H) $(INCL)\func_tab.h \ - $(INCL)\wintty.h +$(OTTY)getline.o: ..\win\tty\getline.c $(HACK_H) $(INCL)func_tab.h \ + $(INCL)wintty.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\tty\getline.c -$(OTTY)termcap.o: ..\win\tty\termcap.c $(HACK_H) $(INCL)\tcap.h \ - $(INCL)\wintty.h +$(OTTY)termcap.o: ..\win\tty\termcap.c $(HACK_H) $(INCL)tcap.h \ + $(INCL)wintty.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\tty\termcap.c -$(OTTY)topl.o: ..\win\tty\topl.c $(HACK_H) $(INCL)\tcap.h \ - $(INCL)\wintty.h +$(OTTY)topl.o: ..\win\tty\topl.c $(HACK_H) $(INCL)tcap.h \ + $(INCL)wintty.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\tty\topl.c -$(OTTY)wintty.o: ..\win\tty\wintty.c $(HACK_H) $(INCL)\dlb.h \ - $(INCL)\tcap.h $(INCL)\wintty.h +$(OTTY)wintty.o: ..\win\tty\wintty.c $(HACK_H) $(INCL)dlb.h \ + $(INCL)tcap.h $(INCL)wintty.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\tty\wintty.c $(OTTY)allmain.o: allmain.c $(HACK_H) -$(OTTY)alloc.o: alloc.c $(CONFIG_H) $(INCL)\nhlua.h +$(OTTY)alloc.o: alloc.c $(CONFIG_H) $(INCL)nhlua.h $(OTTY)apply.o: apply.c $(HACK_H) -$(OTTY)artifact.o: artifact.c $(HACK_H) $(INCL)\artifact.h +$(OTTY)artifact.o: artifact.c $(HACK_H) $(INCL)artifact.h $(OTTY)attrib.o: attrib.c $(HACK_H) $(OTTY)ball.o: ball.c $(HACK_H) $(OTTY)bones.o: bones.c $(HACK_H) $(OTTY)botl.o: botl.c $(HACK_H) $(OTTY)calendar.o: calendar.c $(HACK_H) -$(OTTY)cmd.o: cmd.c $(HACK_H) $(INCL)\func_tab.h +$(OTTY)cfgfiles.o: cfgfiles.c $(HACK_H) +$(OTTY)cmd.o: cmd.c $(HACK_H) $(INCL)func_tab.h $(OTTY)coloratt.o: coloratt.c $(HACK_H) $(OTTY)dbridge.o: dbridge.c $(HACK_H) $(OTTY)decl.o: decl.c $(HACK_H) -$(OTTY)detect.o: detect.c $(HACK_H) $(INCL)\artifact.h +$(OTTY)detect.o: detect.c $(HACK_H) $(INCL)artifact.h $(OTTY)dig.o: dig.c $(HACK_H) $(OTTY)display.o: display.c $(HACK_H) -$(OTTY)dlb.o: dlb.c $(CONFIG_H) $(INCL)\dlb.h +$(OTTY)dlb.o: dlb.c $(CONFIG_H) $(INCL)dlb.h $(OTTY)do.o: do.c $(HACK_H) $(OTTY)do_name.o: do_name.c $(HACK_H) $(OTTY)do_wear.o: do_wear.c $(HACK_H) $(OTTY)dog.o: dog.c $(HACK_H) -$(OTTY)dogmove.o: dogmove.c $(HACK_H) $(INCL)\mfndpos.h +$(OTTY)dogmove.o: dogmove.c $(HACK_H) $(INCL)mfndpos.h $(OTTY)dokick.o: dokick.c $(HACK_H) $(OTTY)dothrow.o: dothrow.c $(HACK_H) -$(OTTY)drawing.o: drawing.c $(CONFIG_H) $(INCL)\defsym.h \ - $(INCL)\objclass.h $(INCL)\objects.h $(INCL)\rm.h \ - $(INCL)\sym.h $(INCL)\wintype.h -$(OTTY)dungeon.o: dungeon.c $(HACK_H) $(INCL)\dgn_file.h \ - $(INCL)\dlb.h +$(OTTY)drawing.o: drawing.c $(CONFIG_H) $(INCL)defsym.h \ + $(INCL)objclass.h $(INCL)objects.h $(INCL)rm.h \ + $(INCL)sym.h $(INCL)wintype.h +$(OTTY)dungeon.o: dungeon.c $(HACK_H) $(INCL)dgn_file.h \ + $(INCL)dlb.h $(OTTY)eat.o: eat.c $(HACK_H) -$(OTTY)end.o: end.c $(HACK_H) $(INCL)\dlb.h +$(OTTY)end.o: end.c $(HACK_H) $(INCL)dlb.h $(OTTY)engrave.o: engrave.c $(HACK_H) $(OTTY)exper.o: exper.c $(HACK_H) $(OTTY)explode.o: explode.c $(HACK_H) $(OTTY)extralev.o: extralev.c $(HACK_H) -$(OTTY)files.o: files.c $(HACK_H) $(INCL)\dlb.h \ - $(INCL)\wintty.h +$(OTTY)files.o: files.c $(HACK_H) $(INCL)dlb.h \ + $(INCL)wintty.h $(OTTY)fountain.o: fountain.c $(HACK_H) $(OTTY)getpos.o: getpos.c $(HACK_H) $(OTTY)glyphs.o: glyphs.c $(HACK_H) @@ -2762,107 +3189,111 @@ $(OTTY)hack.o: hack.c $(HACK_H) $(OTTY)hacklib.o: hacklib.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 +$(OTTY)isaac64.o: isaac64.c $(CONFIG_H) $(INCL)isaac64.h $(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)mail.o: mail.c $(HACK_H) $(INCL)mail.h $(OTTY)makemon.o: makemon.c $(HACK_H) $(OTTY)mcastu.o: mcastu.c $(HACK_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 \ - $(INCL)\dungeon.h $(INCL)\flag.h $(INCL)\hacklib.h \ - $(INCL)\mextra.h $(INCL)\monattk.h $(INCL)\monflag.h \ - $(INCL)\monst.h $(INCL)\monsters.h $(INCL)\obj.h \ - $(INCL)\objclass.h $(INCL)\objects.h \ - $(INCL)\permonst.h $(INCL)\prop.h $(INCL)\seffects.h \ - $(INCL)\skills.h $(INCL)\sndprocs.h $(INCL)\sym.h \ - $(INCL)\wintype.h $(INCL)\you.h -$(OTTY)mhitm.o: mhitm.c $(HACK_H) $(INCL)\artifact.h -$(OTTY)mhitu.o: mhitu.c $(HACK_H) $(INCL)\artifact.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 \ + $(INCL)dungeon.h $(INCL)flag.h $(INCL)hacklib.h \ + $(INCL)mextra.h $(INCL)monattk.h $(INCL)monflag.h \ + $(INCL)monst.h $(INCL)monsters.h $(INCL)obj.h \ + $(INCL)objclass.h $(INCL)objects.h \ + $(INCL)permonst.h $(INCL)prop.h $(INCL)seffects.h \ + $(INCL)skills.h $(INCL)sndprocs.h $(INCL)sym.h \ + $(INCL)wintype.h $(INCL)you.h +$(OTTY)mhitm.o: mhitm.c $(HACK_H) $(INCL)artifact.h +$(OTTY)mhitu.o: mhitu.c $(HACK_H) $(INCL)artifact.h $(OTTY)minion.o: minion.c $(HACK_H) $(OTTY)mklev.o: mklev.c $(HACK_H) -$(OTTY)mkmap.o: mkmap.c $(HACK_H) $(INCL)\sp_lev.h -$(OTTY)mkmaze.o: mkmaze.c $(HACK_H) $(INCL)\sp_lev.h +$(OTTY)mkmap.o: mkmap.c $(HACK_H) $(INCL)sp_lev.h +$(OTTY)mkmaze.o: mkmaze.c $(HACK_H) $(INCL)sp_lev.h $(OTTY)mkobj.o: mkobj.c $(HACK_H) $(OTTY)mkroom.o: mkroom.c $(HACK_H) -$(OTTY)mon.o: mon.c $(HACK_H) $(INCL)\mfndpos.h +$(OTTY)mon.o: mon.c $(HACK_H) $(INCL)mfndpos.h $(OTTY)mondata.o: mondata.c $(HACK_H) -$(OTTY)monmove.o: monmove.c $(HACK_H) $(INCL)\artifact.h \ - $(INCL)\mfndpos.h -$(OTTY)monst.o: monst.c $(CONFIG_H) $(INCL)\align.h \ - $(INCL)\defsym.h $(INCL)\monattk.h $(INCL)\monflag.h \ - $(INCL)\monsters.h $(INCL)\permonst.h $(INCL)\sym.h \ - $(INCL)\wintype.h +$(OTTY)monmove.o: monmove.c $(HACK_H) $(INCL)artifact.h \ + $(INCL)mfndpos.h +$(OTTY)monst.o: monst.c $(CONFIG_H) $(INCL)align.h \ + $(INCL)defsym.h $(INCL)monattk.h $(INCL)monflag.h \ + $(INCL)monsters.h $(INCL)permonst.h $(INCL)sym.h \ + $(INCL)wintype.h $(OTTY)mplayer.o: mplayer.c $(HACK_H) $(OTTY)mthrowu.o: mthrowu.c $(HACK_H) $(OTTY)muse.o: muse.c $(HACK_H) $(OTTY)music.o: music.c $(HACK_H) -$(OTTY)nhlobj.o: nhlobj.c $(HACK_H) $(INCL)\sp_lev.h -$(OTTY)nhlsel.o: nhlsel.c $(HACK_H) $(INCL)\sp_lev.h -$(OTTY)nhlua.o: nhlua.c $(HACK_H) $(INCL)\dlb.h +$(OTTY)nhlobj.o: nhlobj.c $(HACK_H) $(INCL)sp_lev.h +$(OTTY)nhlsel.o: nhlsel.c $(HACK_H) $(INCL)sp_lev.h +$(OTTY)nhlua.o: nhlua.c $(HACK_H) $(INCL)dlb.h $(Q)$(CC) $(CFLAGS) $(TTYDEF) -wd4324 $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $(@B).c -$(OTTY)nhmd4.o: nhmd4.c $(HACK_H) $(INCL)\nhmd4.h +$(OTTY)nhmd4.o: nhmd4.c $(HACK_H) $(INCL)nhmd4.h $(OTTY)o_init.o: o_init.c $(HACK_H) -$(OTTY)objects.o: objects.c $(CONFIG_H) $(INCL)\defsym.h \ - $(INCL)\obj.h $(INCL)\objclass.h $(INCL)\objects.h \ - $(INCL)\prop.h $(INCL)\skills.h +$(OTTY)objects.o: objects.c $(CONFIG_H) $(INCL)defsym.h \ + $(INCL)obj.h $(INCL)objclass.h $(INCL)objects.h \ + $(INCL)prop.h $(INCL)skills.h $(OTTY)objnam.o: objnam.c $(HACK_H) -$(OTTY)options.o: options.c $(CONFIG_H) $(HACK_H) $(INCL)\defsym.h \ - $(INCL)\flag.h $(INCL)\objclass.h $(INCL)\objects.h \ - $(INCL)\optlist.h $(INCL)\tcap.h -$(OTTY)pager.o: pager.c $(HACK_H) $(INCL)\dlb.h +$(OTTY)options.o: options.c $(CONFIG_H) $(HACK_H) $(INCL)defsym.h \ + $(INCL)flag.h $(INCL)objclass.h $(INCL)objects.h \ + $(INCL)optlist.h $(INCL)tcap.h +$(OTTY)pager.o: pager.c $(HACK_H) $(INCL)dlb.h $(OTTY)pickup.o: pickup.c $(HACK_H) $(OTTY)pline.o: pline.c $(HACK_H) $(OTTY)polyself.o: polyself.c $(HACK_H) $(OTTY)potion.o: potion.c $(HACK_H) $(OTTY)pray.o: pray.c $(HACK_H) -$(OTTY)priest.o: priest.c $(HACK_H) $(INCL)\mfndpos.h +$(OTTY)priest.o: priest.c $(HACK_H) $(INCL)mfndpos.h $(OTTY)quest.o: quest.c $(HACK_H) -$(OTTY)questpgr.o: questpgr.c $(HACK_H) $(INCL)\dlb.h \ - $(INCL)\wintty.h +$(OTTY)questpgr.o: questpgr.c $(HACK_H) $(INCL)dlb.h \ + $(INCL)wintty.h $(OTTY)read.o: read.c $(HACK_H) $(OTTY)rect.o: rect.c $(HACK_H) $(OTTY)region.o: region.c $(HACK_H) -$(OTTY)report.o: report.c $(HACK_H) $(INCL)\dlb.h $(INCL)\nhmd4.h -$(OTTY)restore.o: restore.c $(HACK_H) $(INCL)\tcap.h +$(OTTY)report.o: report.c $(HACK_H) $(INCL)dlb.h $(INCL)nhmd4.h +$(OTTY)restore.o: restore.c $(HACK_H) $(INCL)tcap.h $(OTTY)rip.o: rip.c $(HACK_H) -$(OTTY)rnd.o: rnd.c $(HACK_H) $(INCL)\isaac64.h +$(OTTY)rnd.o: rnd.c $(HACK_H) $(INCL)isaac64.h $(OTTY)role.o: role.c $(HACK_H) -$(OTTY)rumors.o: rumors.c $(HACK_H) $(INCL)\dlb.h +$(OTTY)rumors.o: rumors.c $(HACK_H) $(INCL)dlb.h $(OTTY)save.o: save.c $(HACK_H) -$(OTTY)selvar.o: selvar.c $(HACK_H) $(INCL)\sp_lev.h -$(OTTY)sfstruct.o: sfstruct.c $(HACK_H) +$(OTTY)selvar.o: selvar.c $(HACK_H) $(INCL)sp_lev.h +$(OTTY)sfstruct.o: sfstruct.c $(HACK_H) $(INCL)sfmacros.h + $(Q)$(CC) $(CFLAGS) /EP $(@B).c > $(OTTY)$(@B).c.preproc + $(Q)$(CC) $(CFLAGS) -Fo$@ $(@B).c $(OTTY)shk.o: shk.c $(HACK_H) $(OTTY)shknam.o: shknam.c $(HACK_H) -$(OTTY)sit.o: sit.c $(HACK_H) $(INCL)\artifact.h +$(OTTY)sit.o: sit.c $(HACK_H) $(INCL)artifact.h + $(Q)$(CC) $(CFLAGS) /EP $(@B).c > $(OTTY)$(@B).c.preproc + $(Q)$(CC) $(CFLAGS) -Fo$@ $(@B).c $(OTTY)sounds.o: sounds.c $(HACK_H) -$(OTTY)sp_lev.o: sp_lev.c $(HACK_H) $(INCL)\sp_lev.h +$(OTTY)sp_lev.o: sp_lev.c $(HACK_H) $(INCL)sp_lev.h $(OTTY)spell.o: spell.c $(HACK_H) $(OTTY)stairs.o: stairs.c $(HACK_H) $(OTTY)steal.o: steal.c $(HACK_H) $(OTTY)steed.o: steed.c $(HACK_H) $(OTTY)strutil.o: strutil.c $(HACK_H) -$(OTTY)symbols.o: symbols.c $(HACK_H) $(INCL)\tcap.h +$(OTTY)symbols.o: symbols.c $(HACK_H) $(INCL)tcap.h $(OTTY)sys.o: sys.c $(HACK_H) $(OTTY)teleport.o: teleport.c $(HACK_H) #$(OTTY)tile.o: tile.c $(HACK_H) $(OTTY)timeout.o: timeout.c $(HACK_H) -$(OTTY)topten.o: topten.c $(HACK_H) $(INCL)\dlb.h +$(OTTY)topten.o: topten.c $(HACK_H) $(INCL)dlb.h $(OTTY)track.o: track.c $(HACK_H) $(OTTY)trap.o: trap.c $(HACK_H) $(OTTY)u_init.o: u_init.c $(HACK_H) $(OTTY)uhitm.o: uhitm.c $(HACK_H) $(OTTY)utf8map.o: utf8map.c $(HACK_H) $(OTTY)vault.o: vault.c $(HACK_H) -$(OTTY)version.o: version.c $(HACK_H) $(INCL)\dlb.h +$(OTTY)version.o: version.c $(HACK_H) $(INCL)dlb.h $(OTTY)vision.o: vision.c $(HACK_H) $(OTTY)weapon.o: weapon.c $(HACK_H) $(OTTY)were.o: were.c $(HACK_H) $(OTTY)wield.o: wield.c $(HACK_H) -$(OTTY)windows.o: windows.c $(HACK_H) $(INCL)\dlb.h $(INCL)\wintty.h +$(OTTY)windows.o: windows.c $(HACK_H) $(INCL)dlb.h $(INCL)wintty.h $(OTTY)wizard.o: wizard.c $(HACK_H) -$(OTTY)wizcmds.o: wizcmds.c $(HACK_H) $(INCL)\func_tab.h +$(OTTY)wizcmds.o: wizcmds.c $(HACK_H) $(INCL)func_tab.h $(OTTY)worm.o: worm.c $(HACK_H) $(OTTY)worn.o: worn.c $(HACK_H) $(OTTY)write.o: write.c $(HACK_H) @@ -2870,15 +3301,15 @@ $(OTTY)zap.o: zap.c $(HACK_H) # #$(OGUI)cppregex.o: ..\sys\share\cppregex.cpp $(CONFIG_H) # $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\sys\share\cppregex.cpp -$(OGUI)ioctl.o: ..\sys\share\ioctl.c $(HACK_H) $(INCL)\tcap.h +$(OGUI)ioctl.o: ..\sys\share\ioctl.c $(HACK_H) $(INCL)tcap.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\ioctl.c -$(OGUI)pcmain.o: ..\sys\share\pcmain.c $(HACK_H) $(INCL)\dlb.h +$(OGUI)pcmain.o: ..\sys\share\pcmain.c $(HACK_H) $(INCL)dlb.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\pcmain.c -$(OGUI)pcsys.o: ..\sys\share\pcsys.c $(HACK_H) $(INCL)\wintty.h +$(OGUI)pcsys.o: ..\sys\share\pcsys.c $(HACK_H) $(INCL)wintty.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\pcsys.c -$(OGUI)pctty.o: ..\sys\share\pctty.c $(HACK_H) $(INCL)\wintty.h +$(OGUI)pctty.o: ..\sys\share\pctty.c $(HACK_H) $(INCL)wintty.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\pctty.c -$(OGUI)pcunix.o: ..\sys\share\pcunix.c $(HACK_H) $(INCL)\wintty.h +$(OGUI)pcunix.o: ..\sys\share\pcunix.c $(HACK_H) $(INCL)wintty.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\pcunix.c $(OGUI)pmatchregex.o: ..\sys\share\pmatchregex.c $(HACK_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\pmatchregex.c @@ -2888,14 +3319,14 @@ $(OGUI)random.o: ..\sys\share\random.c $(HACK_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\random.c $(OGUI)unixtty.o: ..\sys\share\unixtty.c $(HACK_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\unixtty.c -$(OGUI)unixmain.o: ..\sys\unix\unixmain.c $(HACK_H) $(INCL)\dlb.h +$(OGUI)unixmain.o: ..\sys\unix\unixmain.c $(HACK_H) $(INCL)dlb.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\unix\unixmain.c $(OGUI)unixres.o: ..\sys\unix\unixres.c $(CONFIG_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\unix\unixres.c $(OGUI)unixunix.o: ..\sys\unix\unixunix.c $(HACK_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\unix\unixunix.c $(OGUI)qt_bind.o: ..\win\Qt\qt_bind.cpp $(HACK_H) $(QTn_H) \ - $(INCL)\dlb.h ..\win\Qt\qt_bind.h ..\win\Qt\qt_click.h \ + $(INCL)dlb.h ..\win\Qt\qt_bind.h ..\win\Qt\qt_click.h \ ..\win\Qt\qt_clust.h ..\win\Qt\qt_delay.h ..\win\Qt\qt_icon.h \ ..\win\Qt\qt_kde0.h ..\win\Qt\qt_key.h ..\win\Qt\qt_line.h \ ..\win\Qt\qt_main.h ..\win\Qt\qt_map.h ..\win\Qt\qt_menu.h \ @@ -2914,7 +3345,7 @@ $(OGUI)qt_delay.o: ..\win\Qt\qt_delay.cpp $(HACK_H) $(QTn_H) \ ..\win\Qt\qt_delay.h ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_delay.cpp $(OGUI)qt_glyph.o: ..\win\Qt\qt_glyph.cpp $(HACK_H) $(QTn_H) \ - $(INCL)\tile2x11.h ..\win\Qt\qt_bind.h \ + $(INCL)tile2x11.h ..\win\Qt\qt_bind.h \ ..\win\Qt\qt_clust.h ..\win\Qt\qt_glyph.h ..\win\Qt\qt_inv.h \ ..\win\Qt\qt_kde0.h ..\win\Qt\qt_main.h ..\win\Qt\qt_map.h \ ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h ..\win\Qt\qt_set.h \ @@ -3007,7 +3438,7 @@ $(OGUI)qt_win.o: ..\win\Qt\qt_win.cpp $(HACK_H) $(QTn_H) \ ..\win\Qt\qt_set.h ..\win\Qt\qt_win.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_win.cpp $(OGUI)qt_xcmd.o: ..\win\Qt\qt_xcmd.cpp $(HACK_H) $(QTn_H) \ - $(INCL)\func_tab.h ..\win\Qt\qt_bind.h ..\win\Qt\qt_kde0.h \ + $(INCL)func_tab.h ..\win\Qt\qt_bind.h ..\win\Qt\qt_kde0.h \ ..\win\Qt\qt_key.h ..\win\Qt\qt_main.h ..\win\Qt\qt_post.h \ ..\win\Qt\qt_pre.h ..\win\Qt\qt_set.h ..\win\Qt\qt_str.h \ ..\win\Qt\qt_xcmd.h qt_xcmd.moc @@ -3016,119 +3447,120 @@ $(OGUI)qt_yndlg.o: ..\win\Qt\qt_yndlg.cpp $(HACK_H) $(QTn_H) \ ..\win\Qt\qt_key.h ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h \ ..\win\Qt\qt_str.h ..\win\Qt\qt_yndlg.h qt_yndlg.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_yndlg.cpp -#$(OGUI)Window.o: ..\win\X11\Window.c $(CONFIG_H) $(INCL)\lint.h \ -# $(INCL)\winX.h $(INCL)\wintype.h $(INCL)\xwindow.h \ -# $(INCL)\xwindowp.h +#$(OGUI)Window.o: ..\win\X11\Window.c $(CONFIG_H) $(INCL)lint.h \ +# $(INCL)winX.h $(INCL)wintype.h $(INCL)xwindow.h \ +# $(INCL)xwindowp.h # $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\Window.c -$(OGUI)dialogs.o: ..\win\X11\dialogs.c $(CONFIG_H) $(INCL)\lint.h \ - $(INCL)\winX.h $(INCL)\wintype.h +$(OGUI)dialogs.o: ..\win\X11\dialogs.c $(CONFIG_H) $(INCL)lint.h \ + $(INCL)winX.h $(INCL)wintype.h # $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\dialogs.c -$(OGUI)winX.o: ..\win\X11\winX.c $(HACK_H) $(INCL)\dlb.h \ - $(INCL)\winX.h $(INCL)\xwindow.h ..\win\X11\nh32icon \ +$(OGUI)winX.o: ..\win\X11\winX.c $(HACK_H) $(INCL)dlb.h \ + $(INCL)winX.h $(INCL)xwindow.h ..\win\X11\nh32icon \ ..\win\X11\nh56icon ..\win\X11\nh72icon # $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winX.c -$(OGUI)winmap.o: ..\win\X11\winmap.c $(HACK_H) $(INCL)\dlb.h \ - $(INCL)\tile2x11.h $(INCL)\winX.h $(INCL)\xwindow.h +$(OGUI)winmap.o: ..\win\X11\winmap.c $(HACK_H) $(INCL)dlb.h \ + $(INCL)tile2x11.h $(INCL)winX.h $(INCL)xwindow.h # $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winmap.c -$(OGUI)winmenu.o: ..\win\X11\winmenu.c $(HACK_H) $(INCL)\winX.h +$(OGUI)winmenu.o: ..\win\X11\winmenu.c $(HACK_H) $(INCL)winX.h # $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winmenu.c -$(OGUI)winmesg.o: ..\win\X11\winmesg.c $(HACK_H) $(INCL)\winX.h \ - $(INCL)\xwindow.h +$(OGUI)winmesg.o: ..\win\X11\winmesg.c $(HACK_H) $(INCL)winX.h \ + $(INCL)xwindow.h # $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winmesg.c -$(OGUI)winmisc.o: ..\win\X11\winmisc.c $(HACK_H) $(INCL)\func_tab.h \ - $(INCL)\winX.h +$(OGUI)winmisc.o: ..\win\X11\winmisc.c $(HACK_H) $(INCL)func_tab.h \ + $(INCL)winX.h # $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winmisc.c -$(OGUI)winstat.o: ..\win\X11\winstat.c $(HACK_H) $(INCL)\winX.h \ - $(INCL)\xwindow.h +$(OGUI)winstat.o: ..\win\X11\winstat.c $(HACK_H) $(INCL)winX.h \ + $(INCL)xwindow.h # $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winstat.c -$(OGUI)wintext.o: ..\win\X11\wintext.c $(HACK_H) $(INCL)\winX.h \ - $(INCL)\xwindow.h +$(OGUI)wintext.o: ..\win\X11\wintext.c $(HACK_H) $(INCL)winX.h \ + $(INCL)xwindow.h # $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\wintext.c -$(OGUI)winval.o: ..\win\X11\winval.c $(HACK_H) $(INCL)\winX.h +$(OGUI)winval.o: ..\win\X11\winval.c $(HACK_H) $(INCL)winX.h # $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winval.c $(OGUI)wc_chainin.o: ..\win\chain\wc_chainin.c $(HACK_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\chain\wc_chainin.c $(OGUI)wc_chainout.o: ..\win\chain\wc_chainout.c $(HACK_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\chain\wc_chainout.c $(OGUI)wc_trace.o: ..\win\chain\wc_trace.c $(HACK_H) \ - $(INCL)\func_tab.h $(INCL)\wintty.h + $(INCL)func_tab.h $(INCL)wintty.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\chain\wc_trace.c $(OGUI)cursdial.o: ..\win\curses\cursdial.c $(HACK_H) \ - $(INCL)\func_tab.h $(INCL)\wincurs.h \ + $(INCL)func_tab.h $(INCL)wincurs.h \ ..\win\curses\cursdial.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursdial.c $(OGUI)cursinit.o: ..\win\curses\cursinit.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\cursinit.h + $(INCL)wincurs.h ..\win\curses\cursinit.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursinit.c $(OGUI)cursinvt.o: ..\win\curses\cursinvt.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\cursinvt.h + $(INCL)wincurs.h ..\win\curses\cursinvt.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursinvt.c -$(OGUI)cursmain.o: ..\win\curses\cursmain.c $(HACK_H) $(INCL)\wincurs.h +$(OGUI)cursmain.o: ..\win\curses\cursmain.c $(HACK_H) $(INCL)wincurs.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursmain.c $(OGUI)cursmesg.o: ..\win\curses\cursmesg.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\cursmesg.h + $(INCL)wincurs.h ..\win\curses\cursmesg.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursmesg.c -$(OGUI)cursmisc.o: ..\win\curses\cursmisc.c $(HACK_H) $(INCL)\dlb.h \ - $(INCL)\func_tab.h $(INCL)\wincurs.h \ +$(OGUI)cursmisc.o: ..\win\curses\cursmisc.c $(HACK_H) $(INCL)dlb.h \ + $(INCL)func_tab.h $(INCL)wincurs.h \ ..\win\curses\cursmisc.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursmisc.c $(OGUI)cursstat.o: ..\win\curses\cursstat.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\cursstat.h + $(INCL)wincurs.h ..\win\curses\cursstat.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursstat.c $(OGUI)curswins.o: ..\win\curses\curswins.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\curswins.h + $(INCL)wincurs.h ..\win\curses\curswins.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\curswins.c $(OGUI)winshim.o: ..\win\shim\winshim.c $(HACK_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\shim\winshim.c -$(OGUI)getline.o: ..\win\tty\getline.c $(HACK_H) $(INCL)\func_tab.h \ - $(INCL)\wintty.h +$(OGUI)getline.o: ..\win\tty\getline.c $(HACK_H) $(INCL)func_tab.h \ + $(INCL)wintty.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\tty\getline.c -$(OGUI)termcap.o: ..\win\tty\termcap.c $(HACK_H) $(INCL)\tcap.h \ - $(INCL)\wintty.h +$(OGUI)termcap.o: ..\win\tty\termcap.c $(HACK_H) $(INCL)tcap.h \ + $(INCL)wintty.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\tty\termcap.c -$(OGUI)topl.o: ..\win\tty\topl.c $(HACK_H) $(INCL)\tcap.h \ - $(INCL)\wintty.h +$(OGUI)topl.o: ..\win\tty\topl.c $(HACK_H) $(INCL)tcap.h \ + $(INCL)wintty.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\tty\topl.c -$(OGUI)wintty.o: ..\win\tty\wintty.c $(HACK_H) $(INCL)\dlb.h \ - $(INCL)\tcap.h $(INCL)\wintty.h +$(OGUI)wintty.o: ..\win\tty\wintty.c $(HACK_H) $(INCL)dlb.h \ + $(INCL)tcap.h $(INCL)wintty.h # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\tty\wintty.c $(OGUI)allmain.o: allmain.c $(HACK_H) -$(OGUI)alloc.o: alloc.c $(CONFIG_H) $(INCL)\nhlua.h +$(OGUI)alloc.o: alloc.c $(CONFIG_H) $(INCL)nhlua.h $(OGUI)apply.o: apply.c $(HACK_H) -$(OGUI)artifact.o: artifact.c $(HACK_H) $(INCL)\artifact.h +$(OGUI)artifact.o: artifact.c $(HACK_H) $(INCL)artifact.h $(OGUI)attrib.o: attrib.c $(HACK_H) $(OGUI)ball.o: ball.c $(HACK_H) $(OGUI)bones.o: bones.c $(HACK_H) $(OGUI)botl.o: botl.c $(HACK_H) $(OGUI)calendar.o: calendar.c $(HACK_H) -$(OGUI)cmd.o: cmd.c $(HACK_H) $(INCL)\func_tab.h +$(OGUI)cfgfiles.o: cfgfiles.c $(HACK_H) +$(OGUI)cmd.o: cmd.c $(HACK_H) $(INCL)func_tab.h $(OGUI)coloratt.o: coloratt.c $(HACK_H) $(OGUI)dbridge.o: dbridge.c $(HACK_H) $(OGUI)decl.o: decl.c $(HACK_H) -$(OGUI)detect.o: detect.c $(HACK_H) $(INCL)\artifact.h +$(OGUI)detect.o: detect.c $(HACK_H) $(INCL)artifact.h $(OGUI)dig.o: dig.c $(HACK_H) $(OGUI)display.o: display.c $(HACK_H) -$(OGUI)dlb.o: dlb.c $(CONFIG_H) $(INCL)\dlb.h +$(OGUI)dlb.o: dlb.c $(CONFIG_H) $(INCL)dlb.h $(OGUI)do.o: do.c $(HACK_H) $(OGUI)do_name.o: do_name.c $(HACK_H) $(OGUI)do_wear.o: do_wear.c $(HACK_H) $(OGUI)dog.o: dog.c $(HACK_H) -$(OGUI)dogmove.o: dogmove.c $(HACK_H) $(INCL)\mfndpos.h +$(OGUI)dogmove.o: dogmove.c $(HACK_H) $(INCL)mfndpos.h $(OGUI)dokick.o: dokick.c $(HACK_H) $(OGUI)dothrow.o: dothrow.c $(HACK_H) -$(OGUI)drawing.o: drawing.c $(CONFIG_H) $(INCL)\defsym.h \ - $(INCL)\objclass.h $(INCL)\objects.h $(INCL)\rm.h \ - $(INCL)\sym.h $(INCL)\wintype.h -$(OGUI)dungeon.o: dungeon.c $(HACK_H) $(INCL)\dgn_file.h \ - $(INCL)\dlb.h +$(OGUI)drawing.o: drawing.c $(CONFIG_H) $(INCL)defsym.h \ + $(INCL)objclass.h $(INCL)objects.h $(INCL)rm.h \ + $(INCL)sym.h $(INCL)wintype.h +$(OGUI)dungeon.o: dungeon.c $(HACK_H) $(INCL)dgn_file.h \ + $(INCL)dlb.h $(OGUI)eat.o: eat.c $(HACK_H) -$(OGUI)end.o: end.c $(HACK_H) $(INCL)\dlb.h +$(OGUI)end.o: end.c $(HACK_H) $(INCL)dlb.h $(OGUI)engrave.o: engrave.c $(HACK_H) $(OGUI)exper.o: exper.c $(HACK_H) $(OGUI)explode.o: explode.c $(HACK_H) $(OGUI)extralev.o: extralev.c $(HACK_H) -$(OGUI)files.o: files.c $(HACK_H) $(INCL)\dlb.h \ - $(INCL)\wintty.h +$(OGUI)files.o: files.c $(HACK_H) $(INCL)dlb.h \ + $(INCL)wintty.h $(OGUI)fountain.o: fountain.c $(HACK_H) $(OGUI)getpos.o: getpos.c $(HACK_H) $(OGUI)glyphs.o: glyphs.c $(HACK_H) @@ -3136,106 +3568,108 @@ $(OGUI)hack.o: hack.c $(HACK_H) $(OGUI)hacklib.o: hacklib.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 +$(OGUI)isaac64.o: isaac64.c $(CONFIG_H) $(INCL)isaac64.h $(OGUI)light.o: light.c $(HACK_H) $(OGUI)lock.o: lock.c $(HACK_H) -$(OGUI)mail.o: mail.c $(HACK_H) $(INCL)\mail.h +$(OGUI)mail.o: mail.c $(HACK_H) $(INCL)mail.h $(OGUI)makemon.o: makemon.c $(HACK_H) $(OGUI)mcastu.o: mcastu.c $(HACK_H) -$(OGUI)mdlib.o: mdlib.c $(CONFIG_H) $(INCL)\align.h \ - $(INCL)\artilist.h $(INCL)\attrib.h \ - $(INCL)\context.h $(INCL)\defsym.h $(INCL)\dlb.h \ - $(INCL)\dungeon.h $(INCL)\flag.h $(INCL)\hacklib.h \ - $(INCL)\mextra.h $(INCL)\monattk.h $(INCL)\monflag.h \ - $(INCL)\monst.h $(INCL)\monsters.h $(INCL)\obj.h \ - $(INCL)\objclass.h $(INCL)\objects.h \ - $(INCL)\permonst.h $(INCL)\prop.h $(INCL)\seffects.h \ - $(INCL)\skills.h $(INCL)\sndprocs.h $(INCL)\sym.h \ - $(INCL)\wintype.h $(INCL)\you.h -$(OGUI)mhitm.o: mhitm.c $(HACK_H) $(INCL)\artifact.h -$(OGUI)mhitu.o: mhitu.c $(HACK_H) $(INCL)\artifact.h +$(OGUI)mdlib.o: mdlib.c $(CONFIG_H) $(INCL)align.h \ + $(INCL)artilist.h $(INCL)attrib.h \ + $(INCL)context.h $(INCL)defsym.h $(INCL)dlb.h \ + $(INCL)dungeon.h $(INCL)flag.h $(INCL)hacklib.h \ + $(INCL)mextra.h $(INCL)monattk.h $(INCL)monflag.h \ + $(INCL)monst.h $(INCL)monsters.h $(INCL)obj.h \ + $(INCL)objclass.h $(INCL)objects.h \ + $(INCL)permonst.h $(INCL)prop.h $(INCL)seffects.h \ + $(INCL)skills.h $(INCL)sndprocs.h $(INCL)sym.h \ + $(INCL)wintype.h $(INCL)you.h +$(OGUI)mhitm.o: mhitm.c $(HACK_H) $(INCL)artifact.h +$(OGUI)mhitu.o: mhitu.c $(HACK_H) $(INCL)artifact.h $(OGUI)minion.o: minion.c $(HACK_H) $(OGUI)mklev.o: mklev.c $(HACK_H) -$(OGUI)mkmap.o: mkmap.c $(HACK_H) $(INCL)\sp_lev.h -$(OGUI)mkmaze.o: mkmaze.c $(HACK_H) $(INCL)\sp_lev.h +$(OGUI)mkmap.o: mkmap.c $(HACK_H) $(INCL)sp_lev.h +$(OGUI)mkmaze.o: mkmaze.c $(HACK_H) $(INCL)sp_lev.h $(OGUI)mkobj.o: mkobj.c $(HACK_H) $(OGUI)mkroom.o: mkroom.c $(HACK_H) -$(OGUI)mon.o: mon.c $(HACK_H) $(INCL)\mfndpos.h +$(OGUI)mon.o: mon.c $(HACK_H) $(INCL)mfndpos.h $(OGUI)mondata.o: mondata.c $(HACK_H) -$(OGUI)monmove.o: monmove.c $(HACK_H) $(INCL)\artifact.h \ - $(INCL)\mfndpos.h -$(OGUI)monst.o: monst.c $(CONFIG_H) $(INCL)\align.h \ - $(INCL)\defsym.h $(INCL)\monattk.h $(INCL)\monflag.h \ - $(INCL)\monsters.h $(INCL)\permonst.h $(INCL)\sym.h \ - $(INCL)\wintype.h +$(OGUI)monmove.o: monmove.c $(HACK_H) $(INCL)artifact.h \ + $(INCL)mfndpos.h +$(OGUI)monst.o: monst.c $(CONFIG_H) $(INCL)align.h \ + $(INCL)defsym.h $(INCL)monattk.h $(INCL)monflag.h \ + $(INCL)monsters.h $(INCL)permonst.h $(INCL)sym.h \ + $(INCL)wintype.h $(OGUI)mplayer.o: mplayer.c $(HACK_H) $(OGUI)mthrowu.o: mthrowu.c $(HACK_H) $(OGUI)muse.o: muse.c $(HACK_H) $(OGUI)music.o: music.c $(HACK_H) -$(OGUI)nhlobj.o: nhlobj.c $(HACK_H) $(INCL)\sp_lev.h -$(OGUI)nhlsel.o: nhlsel.c $(HACK_H) $(INCL)\sp_lev.h -$(OGUI)nhlua.o: nhlua.c $(HACK_H) $(INCL)\dlb.h -$(OGUI)nhmd4.o: nhmd4.c $(HACK_H) $(INCL)\nhmd4.h +$(OGUI)nhlobj.o: nhlobj.c $(HACK_H) $(INCL)sp_lev.h +$(OGUI)nhlsel.o: nhlsel.c $(HACK_H) $(INCL)sp_lev.h +$(OGUI)nhlua.o: nhlua.c $(HACK_H) $(INCL)dlb.h +$(OGUI)nhmd4.o: nhmd4.c $(HACK_H) $(INCL)nhmd4.h $(OGUI)o_init.o: o_init.c $(HACK_H) -$(OGUI)objects.o: objects.c $(CONFIG_H) $(INCL)\defsym.h \ - $(INCL)\obj.h $(INCL)\objclass.h $(INCL)\objects.h \ - $(INCL)\prop.h $(INCL)\skills.h +$(OGUI)objects.o: objects.c $(CONFIG_H) $(INCL)defsym.h \ + $(INCL)obj.h $(INCL)objclass.h $(INCL)objects.h \ + $(INCL)prop.h $(INCL)skills.h $(OGUI)objnam.o: objnam.c $(HACK_H) -$(OGUI)options.o: options.c $(CONFIG_H) $(HACK_H) $(INCL)\defsym.h \ - $(INCL)\flag.h $(INCL)\objclass.h $(INCL)\objects.h \ - $(INCL)\optlist.h $(INCL)\tcap.h -$(OGUI)pager.o: pager.c $(HACK_H) $(INCL)\dlb.h +$(OGUI)options.o: options.c $(CONFIG_H) $(HACK_H) $(INCL)defsym.h \ + $(INCL)flag.h $(INCL)objclass.h $(INCL)objects.h \ + $(INCL)optlist.h $(INCL)tcap.h +$(OGUI)pager.o: pager.c $(HACK_H) $(INCL)dlb.h $(OGUI)pickup.o: pickup.c $(HACK_H) $(OGUI)pline.o: pline.c $(HACK_H) $(OGUI)polyself.o: polyself.c $(HACK_H) $(OGUI)potion.o: potion.c $(HACK_H) $(OGUI)pray.o: pray.c $(HACK_H) -$(OGUI)priest.o: priest.c $(HACK_H) $(INCL)\mfndpos.h +$(OGUI)priest.o: priest.c $(HACK_H) $(INCL)mfndpos.h $(OGUI)quest.o: quest.c $(HACK_H) -$(OGUI)questpgr.o: questpgr.c $(HACK_H) $(INCL)\dlb.h \ - $(INCL)\wintty.h +$(OGUI)questpgr.o: questpgr.c $(HACK_H) $(INCL)dlb.h \ + $(INCL)wintty.h $(OGUI)read.o: read.c $(HACK_H) $(OGUI)rect.o: rect.c $(HACK_H) $(OGUI)region.o: region.c $(HACK_H) -$(OGUI)report.o: report.c $(HACK_H) $(INCL)\dlb.h $(INCL)\nhmd4.h -$(OGUI)restore.o: restore.c $(HACK_H) $(INCL)\tcap.h +$(OGUI)report.o: report.c $(HACK_H) $(INCL)dlb.h $(INCL)nhmd4.h +$(OGUI)restore.o: restore.c $(HACK_H) $(INCL)tcap.h $(OGUI)rip.o: rip.c $(HACK_H) -$(OGUI)rnd.o: rnd.c $(HACK_H) $(INCL)\isaac64.h +$(OGUI)rnd.o: rnd.c $(HACK_H) $(INCL)isaac64.h $(OGUI)role.o: role.c $(HACK_H) -$(OGUI)rumors.o: rumors.c $(HACK_H) $(INCL)\dlb.h +$(OGUI)rumors.o: rumors.c $(HACK_H) $(INCL)dlb.h $(OGUI)save.o: save.c $(HACK_H) -$(OGUI)selvar.o: selvar.c $(HACK_H) $(INCL)\sp_lev.h -$(OGUI)sfstruct.o: sfstruct.c $(HACK_H) +$(OGUI)selvar.o: selvar.c $(HACK_H) $(INCL)sp_lev.h +$(OGUI)sfstruct.o: sfstruct.c $(HACK_H) $(INCL)sfmacros.h + $(Q)$(CC) $(CFLAGS) /EP $(@B).c > $(OGUI)$(@B).c.preproc + $(Q)$(CC) $(CFLAGS) -Fo$@ $(@B).c $(OGUI)shk.o: shk.c $(HACK_H) $(OGUI)shknam.o: shknam.c $(HACK_H) -$(OGUI)sit.o: sit.c $(HACK_H) $(INCL)\artifact.h +$(OGUI)sit.o: sit.c $(HACK_H) $(INCL)artifact.h $(OGUI)sounds.o: sounds.c $(HACK_H) -$(OGUI)sp_lev.o: sp_lev.c $(HACK_H) $(INCL)\sp_lev.h +$(OGUI)sp_lev.o: sp_lev.c $(HACK_H) $(INCL)sp_lev.h $(OGUI)spell.o: spell.c $(HACK_H) $(OGUI)stairs.o: stairs.c $(HACK_H) $(OGUI)steal.o: steal.c $(HACK_H) $(OGUI)steed.o: steed.c $(HACK_H) $(OGUI)strutil.o: strutil.c $(HACK_H) -$(OGUI)symbols.o: symbols.c $(HACK_H) $(INCL)\tcap.h +$(OGUI)symbols.o: symbols.c $(HACK_H) $(INCL)tcap.h $(OGUI)sys.o: sys.c $(HACK_H) $(OGUI)teleport.o: teleport.c $(HACK_H) #$(OGUI)tile.o: tile.c $(HACK_H) $(OGUI)timeout.o: timeout.c $(HACK_H) -$(OGUI)topten.o: topten.c $(HACK_H) $(INCL)\dlb.h +$(OGUI)topten.o: topten.c $(HACK_H) $(INCL)dlb.h $(OGUI)track.o: track.c $(HACK_H) $(OGUI)trap.o: trap.c $(HACK_H) $(OGUI)u_init.o: u_init.c $(HACK_H) $(OGUI)uhitm.o: uhitm.c $(HACK_H) $(OGUI)utf8map.o: utf8map.c $(HACK_H) $(OGUI)vault.o: vault.c $(HACK_H) -$(OGUI)version.o: version.c $(HACK_H) $(INCL)\dlb.h +$(OGUI)version.o: version.c $(HACK_H) $(INCL)dlb.h $(OGUI)vision.o: vision.c $(HACK_H) $(OGUI)weapon.o: weapon.c $(HACK_H) $(OGUI)were.o: were.c $(HACK_H) $(OGUI)wield.o: wield.c $(HACK_H) -$(OGUI)windows.o: windows.c $(HACK_H) $(INCL)\dlb.h $(INCL)\wintty.h +$(OGUI)windows.o: windows.c $(HACK_H) $(INCL)dlb.h $(INCL)wintty.h $(OGUI)wizard.o: wizard.c $(HACK_H) -$(OGUI)wizcmds.o: wizcmds.c $(HACK_H) $(INCL)\func_tab.h +$(OGUI)wizcmds.o: wizcmds.c $(HACK_H) $(INCL)func_tab.h $(OGUI)worm.o: worm.c $(HACK_H) $(OGUI)worn.o: worn.c $(HACK_H) $(OGUI)write.o: write.c $(HACK_H) diff --git a/sys/windows/build-msys2.txt b/sys/windows/build-msys2.txt index c9efc6859..1258f7be9 100644 --- a/sys/windows/build-msys2.txt +++ b/sys/windows/build-msys2.txt @@ -11,6 +11,8 @@ Prerequisite Requirements: pacman -S make pacman -S vim (or your editor of choice) pacman -S man (otherwise "git help foo" will not work) + pacman -S perl-doc (otherwise "git nhhelp foo" will not work, + so only needed if you use nhgitset.pl) o Lua o pdcursesmod (Only required if curses interface support is desired) Instructions for obtaining these are later in this file. @@ -90,9 +92,11 @@ I. Dispelling the Myths: as it looks, however it will behoove you to read this entire section through before beginning the task. - We have provided Makefile.mingw32 in - sys/windows/Makefile.mingw32, which you use from the bash Windows command - shell included with MSYS2. + We have provided GNUmakefile in sys/windows/GNUmakefile, which you use + from the bash Windows command shell included with MSYS2. It is called + GNUmakefile because that is the first name searched for by the GNU + make utility thus preventing conflict with the Makefile used by the + Microsoft nmake utility. II. To compile your copy of NetHack on a Windows machine using MSYS2: @@ -104,7 +108,7 @@ Setting Up 2. Execute the following command to place copies of the Makefiles in the src subfolder. - cp sys/windows/Makefile.mingw32* src + cp sys/windows/GNUmakefile* src 3. Change your current directory to the src subfolder of the nethack source tree. The following command assumes you are still in the @@ -118,10 +122,12 @@ Compiling Your current directory should be the NetHack src directory. - Issue these following commands: - make -f Makefile.mingw32 clean - make -f Makefile.mingw32 depend - make -f Makefile.mingw32 + Issue these following commands, which will find and use GNUmakefile + by default: + + make clean + make depend + make If all goes well, intermediate NetHack files will be placed in the binary subfolder of the NetHack tree, and the final NetHack package @@ -130,10 +136,8 @@ Compiling Notes: 1. To rebuild NetHack after changing something, change your current directory - to src and issue the appropriate command for your compiler: - - For gcc: - make -f Makefile.mingw32 + to src and issue the following command: + make 2. An alternative to MSYS2 may be MinGW-w64 - winlibs standalone build. That has not been tested by us at time of writing. diff --git a/sys/windows/consoletty.c b/sys/windows/consoletty.c index 770c0f19b..7754b3d1f 100644 --- a/sys/windows/consoletty.c +++ b/sys/windows/consoletty.c @@ -24,6 +24,7 @@ #include "winos.h" #include "hack.h" #include "wintty.h" +#include #include #include #ifdef VIRTUAL_TERMINAL_SEQUENCES @@ -96,11 +97,27 @@ cell_t clear_cell = { CONSOLE_CLEAR_CHARACTER, CONSOLE_CLEAR_ATTRIBUTE }; 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, 0L, 0, "\x1b[0m" }; -cell_t undefined_cell = { { CONSOLE_UNDEFINED_CHARACTER, 0, 0, 0, 0, 0, 0 }, - CONSOLE_UNDEFINED_CHARACTER, 0, 0L, 0, (const char *) 0 }; +cell_t clear_cell = { + { CONSOLE_CLEAR_CHARACTER, 0, 0, 0, 0, 0, 0 }, + CONSOLE_CLEAR_CHARACTER, /* wcharacter */ + 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, /* wcharacter */ + 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 }; +#endif #endif /* VIRTUAL_TERMINAL_SEQUENCES */ /* @@ -129,12 +146,14 @@ static void xputc_core(int); #endif /* VIRTUAL_TERMINAL_SEQUENCES */ void cmov(int, int); void nocmov(int, int); -int process_keystroke(INPUT_RECORD *, boolean *, boolean numberpad, +int process_keystroke(INPUT_RECORD *, boolean *, uchar numberpad, int portdebug); static void init_ttycolor(void); static void really_move_cursor(void); static void check_and_set_font(void); +#ifndef VIRTUAL_TERMINAL_SEQUENCES 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); @@ -144,9 +163,12 @@ extern void (*ibmgraphics_mode_callback)(void); /* symbols.c */ extern void (*utf8graphics_mode_callback)(void); /* symbols.c */ #endif /* VIRTUAL_TERMINAL_SEQUENCES */ +static void init_custom_colors(void); +static void free_custom_colors(void); + /* Win32 Screen buffer,coordinate,console I/O information */ COORD ntcoord; -INPUT_RECORD ir; +INPUT_RECORD gbl_ir; static boolean orig_QuickEdit; /* Support for changing console font if existing glyph widths are too wide */ @@ -209,11 +231,11 @@ struct console_t { 0, /* current_nhcolor */ 0, /* current_nhbkcolor */ 0, /* current_colorflags */ - {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE}, - {0, 0}, /* cursor */ + { FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE }, + { 0, 0 }, /* cursor */ NULL, /* hConOut*/ NULL, /* hConIn */ - { 0 }, /* cbsi */ + { { 0, 0}, { 0, 0}, 0, { 0, 0, 0, 0 }, { 0, 0 } }, /* cbsi */ 0, /* width */ 0, /* height */ FALSE, /* has_unicode */ @@ -238,7 +260,9 @@ struct console_t { #endif /* VIRTUAL_TERMINAL_SEQUENCES */ }; +#if 0 static const char default_name[] = "default"; +#endif const char *const legal_key_handling[] = { "none", "default", @@ -249,27 +273,27 @@ const char *const legal_key_handling[] = { enum windows_key_handling keyh[] = { no_keyhandling, default_keyhandling, ray_keyhandling, nh340_keyhandling }; -int default_processkeystroke(HANDLE, INPUT_RECORD *, boolean *, boolean, int); +int default_processkeystroke(HANDLE, INPUT_RECORD *, boolean *, uchar, int); int default_kbhit(HANDLE, INPUT_RECORD *); -int default_checkinput(HANDLE, INPUT_RECORD *, DWORD *, boolean, +int default_checkinput(HANDLE, INPUT_RECORD *, DWORD *, uchar, int, int *, coord *); -int ray_processkeystroke(HANDLE, INPUT_RECORD *, boolean *, boolean, int); +int ray_processkeystroke(HANDLE, INPUT_RECORD *, boolean *, uchar, int); int ray_kbhit(HANDLE, INPUT_RECORD *); -int ray_checkinput(HANDLE, INPUT_RECORD *, DWORD *, boolean, +int ray_checkinput(HANDLE, INPUT_RECORD *, DWORD *, uchar, int, int *, coord *); -int nh340_processkeystroke(HANDLE, INPUT_RECORD *, boolean *, boolean, int); +int nh340_processkeystroke(HANDLE, INPUT_RECORD *, boolean *, uchar, int); int nh340_kbhit(HANDLE, INPUT_RECORD *); -int nh340_checkinput(HANDLE, INPUT_RECORD *, DWORD *, boolean, +int nh340_checkinput(HANDLE, INPUT_RECORD *, DWORD *, uchar, int, int *, coord *); struct keyboard_handling_t { enum windows_key_handling khid; int (*pProcessKeystroke)(HANDLE, INPUT_RECORD *, boolean *, - boolean, int); + uchar, int); int (*pNHkbhit)(HANDLE, INPUT_RECORD *); - int (*pCheckInput)(HANDLE, INPUT_RECORD *, DWORD *, boolean, + int (*pCheckInput)(HANDLE, INPUT_RECORD *, DWORD *, uchar, int, int *, coord *); } keyboard_handling = { no_keyhandling, @@ -278,7 +302,10 @@ struct keyboard_handling_t { default_checkinput }; -static DWORD ccount, acount; +static DWORD ccount; +#if 0 +static DWORD acount; +#endif #ifndef CLR_MAX #define CLR_MAX 16 #endif @@ -288,7 +315,9 @@ int ttycolors_inv[CLR_MAX]; #define MAX_OVERRIDES 256 unsigned char key_overrides[MAX_OVERRIDES]; +#if 0 static char nullstr[] = ""; +#endif char erase_char, kill_char; #define DEFTEXTCOLOR ttycolors[7] static INPUT_RECORD bogus_key; @@ -296,7 +325,7 @@ static INPUT_RECORD bogus_key; /* Windows console palette: Color Name Console Legacy RGB Values New Default RGB Values -BLACK 0,0,0 12,12,12 +BLACK 0,0,0 12,12,12 DARK_BLUE 0,0,128 0,55,218 DARK_GREEN 0,128,0 19,161,14 DARK_CYAN 0,128,128 58,150,221 @@ -316,8 +345,8 @@ WHITE 255,255,255 242,242,242 #ifdef VIRTUAL_TERMINAL_SEQUENCES long customcolors[CLR_MAX]; -const char *esc_seq_colors[CLR_MAX]; -const char *esc_seq_bkcolors[CLR_MAX]; +const char *esc_seq_colors[CLR_MAX] = { 0 }; +const char *esc_seq_bkcolors[CLR_MAX] = { 0 }; struct rgbvalues { int idx; @@ -466,7 +495,17 @@ struct rgbvalues { { 138, "white", "#FFFFFF", 255, 255, 255 }, }; -long +void buffer_fill_to_end(cell_t * buffer, cell_t * fill, int x, int y); +void buffer_write(cell_t * buffer, cell_t * cell, COORD pos); +static long rgbtable_to_long(struct rgbvalues *); +void term_start_256color(int idx); +void set_cp_map(void); +#ifdef PORT_DEBUG +void win32con_debug_keystrokes(void); +void win32con_toggle_cursor_info(void); +#endif + +static long rgbtable_to_long(struct rgbvalues *tbl) { long rgblong = (tbl->r << 0) | (tbl->gn << 8) | (tbl->b << 16); @@ -550,6 +589,32 @@ init_custom_colors(void) esc_seq_bkcolors[CLR_BRIGHT_CYAN] = dupstr(bkcolorbuf); } +static void +free_custom_colors(void) +{ +#define CLR_FREE(c) \ + if (esc_seq_bkcolors[(c)] != 0) \ + free((genericptr_t) esc_seq_bkcolors[(c)]), esc_seq_bkcolors[(c)] = 0 + + CLR_FREE(CLR_BLACK); + CLR_FREE(CLR_RED); + CLR_FREE(CLR_GREEN); + CLR_FREE(CLR_YELLOW); + CLR_FREE(CLR_BLUE); + CLR_FREE(CLR_MAGENTA); + CLR_FREE(CLR_CYAN); + CLR_FREE(CLR_WHITE); + CLR_FREE(CLR_BROWN); + CLR_FREE(CLR_GRAY); + CLR_FREE(NO_COLOR); + CLR_FREE(CLR_ORANGE); + CLR_FREE(CLR_BRIGHT_GREEN); + CLR_FREE(CLR_BRIGHT_BLUE); + CLR_FREE(CLR_BRIGHT_MAGENTA); + CLR_FREE(CLR_BRIGHT_CYAN); +#undef CLR_FREE +} + void emit_start_bold(void); void emit_stop_bold(void); void emit_start_dim(void); @@ -565,125 +630,125 @@ void emit_start_256color(int u256coloridx); void emit_default_color(void); void emit_return_to_default(void); void emit_hide_cursor(void); -void emit_show_curor(void); +void emit_show_cursor(void); void emit_hide_cursor(void) { - DWORD unused, reserved; + DWORD unused; static const char escseq[] = "\x1b[?25l"; WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq), - &unused, &reserved); + &unused, NULL); } void emit_show_cursor(void) { - DWORD unused, reserved; + DWORD unused; static const char escseq[] = "\x1b[?25h"; WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq), - &unused, &reserved); + &unused, NULL); } void emit_start_bold(void) { - DWORD unused, reserved; + DWORD unused; static const char escseq[] = "\x1b[1m"; WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq), - &unused, &reserved); + &unused, NULL); } void emit_stop_bold(void) { - DWORD unused, reserved; + DWORD unused; static const char escseq[] = "\x1b[24m"; WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq), - &unused, &reserved); + &unused, NULL); } #if 0 emit_start_dim(void) { - DWORD unused, reserved; + DWORD unused; static const char escseq[] = "\x1b[4m"; WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq), - &unused, &reserved); + &unused, NULL); } void emit_stop_dim(void) { - DWORD unused, reserved; + DWORD unused; static const char escseq[] = "\x1b[24m"; WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq), - &unused, &reserved); + &unused, NULL); } #endif void emit_start_blink(void) { - DWORD unused, reserved; + DWORD unused; static const char escseq[] = "\x1b[5m"; WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq), - &unused, &reserved); + &unused, NULL); } void emit_stop_blink(void) { - DWORD unused, reserved; + DWORD unused; static const char escseq[] = "\x1b[25m"; WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq), - &unused, &reserved); + &unused, NULL); } void emit_start_underline(void) { - DWORD unused, reserved; + DWORD unused; static const char escseq[] = "\x1b[4m"; WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq), - &unused, &reserved); + &unused, NULL); } void emit_stop_underline(void) { - DWORD unused, reserved; + DWORD unused; static const char escseq[] = "\x1b[24m"; WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq), - &unused, &reserved); + &unused, NULL); } void emit_start_inverse(void) { - DWORD unused, reserved; + DWORD unused; static const char escseq[] = "\x1b[7m"; WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq), - &unused, &reserved); + &unused, NULL); } void emit_stop_inverse(void) { - DWORD unused, reserved; + DWORD unused; static const char escseq[] = "\x1b[27m"; WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq), - &unused, &reserved); + &unused, NULL); } #if 0 @@ -700,21 +765,21 @@ emit_stop_inverse(void) #define tcfmtstr24bit "\x1b[38:2:%ld:%ld:%ldm" #define tcfmtstr256 "\x1b[38:5:%dm" #endif - + void emit_start_256color(int u256coloridx) { - DWORD unused, reserved; + DWORD unused; static char tcolorbuf[QBUFSZ]; Snprintf(tcolorbuf, sizeof tcolorbuf, tcfmtstr256, u256coloridx); WriteConsoleA(console.hConOut, (LPCSTR) tcolorbuf, - (int) strlen(tcolorbuf), &unused, &reserved); + (int) strlen(tcolorbuf), &unused, NULL); } void emit_start_24bitcolor(long color24bit) { - DWORD unused, reserved; + DWORD unused; static char tcolorbuf[QBUFSZ]; uint32 mcolor = COLORVAL(color24bit); Snprintf(tcolorbuf, sizeof tcolorbuf, tcfmtstr24bit, @@ -722,33 +787,35 @@ emit_start_24bitcolor(long color24bit) ((mcolor >> 8) & 0xFF), /* green */ ((mcolor >> 0) & 0xFF)); /* blue */ WriteConsoleA(console.hConOut, (LPCSTR) tcolorbuf, - (int) strlen(tcolorbuf), &unused, &reserved); + (int) strlen(tcolorbuf), &unused, NULL); } void emit_default_color(void) { - DWORD unused, reserved; + DWORD unused; static char escseq[] = "\x1b[39m"; WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq), - &unused, &reserved); + &unused, NULL); } void emit_return_to_default(void) { - DWORD unused, reserved; + DWORD unused; static char escseq[] = "\x1b[0m"; WriteConsoleA(console.hConOut, (LPCSTR) escseq, (int) strlen(escseq), - &unused, &reserved); + &unused, NULL); } +#if 0 static boolean newattr_on = TRUE; +#endif static boolean color24_on = TRUE; /* for debugging */ WORD what_is_there_now; -BOOL success; +//BOOL success; DWORD error_result; #endif /* VIRTUAL_TERMINAL_SEQUENCES */ @@ -778,7 +845,6 @@ back_buffer_flip(void) cell_t *front = console.front_buffer; COORD pos; DWORD unused; - DWORD reserved; unsigned do_anything, did_anything; if (!console.is_ready) @@ -843,15 +909,15 @@ back_buffer_flip(void) did_anything |= did_colorseq; WriteConsoleA(console.hConOut, back->colorseq, (int) strlen(back->colorseq), &unused, - &reserved); + NULL); } if (back->bkcolorseq) { did_anything |= did_bkcolorseq; WriteConsoleA(console.hConOut, back->bkcolorseq, (int) strlen(back->bkcolorseq), &unused, - &reserved); + NULL); } - if ((did_anything | (did_colorseq | did_bkcolorseq | did_color24)) == 0) { + if ((did_anything & (did_colorseq | did_bkcolorseq | did_color24)) == 0) { did_anything &= ~(did_bkcolorseq | did_color24); did_anything |= did_colorseq; emit_default_color(); @@ -862,12 +928,12 @@ back_buffer_flip(void) if (SYMHANDLING(H_UTF8) || !console.has_unicode) { WriteConsoleA(console.hConOut, (LPCSTR) back->utf8str, (int) strlen((char *) back->utf8str), - &unused, &reserved); + &unused, NULL); did_anything |= did_utf8_content; } else { #endif WriteConsoleW(console.hConOut, &back->wcharacter, 1, - &unused, &reserved); + &unused, NULL); did_anything |= did_wide_content; #ifdef UTF8_FROM_CORE } @@ -986,7 +1052,7 @@ void settty(const char *s) { cmov(ttyDisplay->curx, ttyDisplay->cury); - end_screen(); + term_end_screen(); if (s) raw_print(s); restore_original_console_font(); @@ -1009,11 +1075,11 @@ settty(const char *s) void setftty(void) { - start_screen(); + term_start_screen(); } void -tty_startup(int *wid, int *hgt) +term_startup(int *wid, int *hgt) { *wid = console.width; *hgt = console.height; @@ -1021,13 +1087,43 @@ tty_startup(int *wid, int *hgt) } void -tty_number_pad(int state) +tty_number_pad(int state UNUSED) { // do nothing } +/* stub tcap replacements for linkage from wintty.c */ + void -tty_start_screen(void) +term_shutdown(void) +{ + consoletty_exit(); +} + +#ifdef ASCIIGRAPH +void +graph_on(void) +{ +} + +void +graph_off(void) +{ +} +#endif + +void +term_end_screen(void) +{ + term_clear_screen(); + really_move_cursor(); + buffer_fill_to_end(console.back_buffer, &clear_cell, 0, 0); + back_buffer_flip(); + FlushConsoleInputBuffer(console.hConIn); +} + +void +term_start_screen(void) { if (iflags.num_pad) tty_number_pad(1); /* make keypad send digits */ @@ -1041,16 +1137,6 @@ tty_start_screen(void) #endif /* VIRTUAL_TERMINAL_SEQUENCES */ } -void -tty_end_screen(void) -{ - term_clear_screen(); - really_move_cursor(); - buffer_fill_to_end(console.back_buffer, &clear_cell, 0, 0); - back_buffer_flip(); - FlushConsoleInputBuffer(console.hConIn); -} - static BOOL CtrlHandler(DWORD ctrltype) { @@ -1058,6 +1144,8 @@ CtrlHandler(DWORD ctrltype) /* case CTRL_C_EVENT: */ case CTRL_BREAK_EVENT: term_clear_screen(); + FALLTHROUGH; + /* FALLTHRU */ case CTRL_CLOSE_EVENT: case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: @@ -1076,7 +1164,7 @@ CtrlHandler(DWORD ctrltype) /* called by pcmain() and process_options() */ void -consoletty_open(int mode) +consoletty_open(int mode UNUSED) { int debugvar; @@ -1094,6 +1182,7 @@ consoletty_open(int mode) CO = console.width; really_move_cursor(); + nhUse(debugvar); } void @@ -1101,13 +1190,18 @@ 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); + free((genericptr_t) console.localestr); + free((genericptr_t) console.orig_localestr); } int process_keystroke( INPUT_RECORD *ir, boolean *valid, - boolean numberpad, + uchar numberpad, int portdebug) { int ch; @@ -1130,7 +1224,7 @@ process_keystroke( int consoletty_kbhit(void) { - return keyboard_handling.pNHkbhit(console.hConIn, &ir); + return keyboard_handling.pNHkbhit(console.hConIn, &gbl_ir); } int @@ -1139,20 +1233,20 @@ tgetch(void) int mod; coord cc; DWORD count; - boolean numpad = iflags.num_pad; + uchar numberpad = iflags.num_pad; really_move_cursor(); if (iflags.debug_fuzzer) return randomkey(); #ifdef QWERTZ_SUPPORT if (gc.Cmd.swap_yz) - numpad |= 0x10; + numberpad |= 0x10; #endif return (program_state.done_hup) ? '\033' : keyboard_handling.pCheckInput( - console.hConIn, &ir, &count, numpad, 0, &mod, &cc); + console.hConIn, &gbl_ir, &count, numberpad, 0, &mod, &cc); } int @@ -1161,7 +1255,7 @@ console_poskey(coordxy *x, coordxy *y, int *mod) int ch; coord cc = { 0, 0 }; DWORD count; - boolean numpad = iflags.num_pad; + boolean numberpad = iflags.num_pad; really_move_cursor(); if (iflags.debug_fuzzer) { @@ -1175,19 +1269,21 @@ console_poskey(coordxy *x, coordxy *y, int *mod) } #ifdef QWERTZ_SUPPORT if (gc.Cmd.swap_yz) - numpad |= 0x10; + numberpad |= 0x10; #endif + term_curs_set(1); ch = (program_state.done_hup) ? '\033' : keyboard_handling.pCheckInput( - console.hConIn, &ir, &count, numpad, 1, mod, &cc); + console.hConIn, &gbl_ir, &count, numberpad, 1, mod, &cc); #ifdef QWERTZ_SUPPORT - numpad &= ~0x10; + numberpad &= ~0x10; #endif if (!ch) { *x = cc.x; *y = cc.y; } + term_curs_set(0); return ch; } @@ -1210,9 +1306,11 @@ really_move_cursor(void) if (GetConsoleTitle(oldtitle, BUFSZ)) { oldtitle[39] = '\0'; } - Sprintf(newtitle, "%-55s tty=(%02d,%02d) consoletty=(%02d,%02d)", oldtitle, - ttyDisplay->curx, ttyDisplay->cury, - console.cursor.X, console.cursor.Y); + Snprintf(newtitle, sizeof newtitle, + "%-55s tty=(%02d,%02d) consoletty=(%02d,%02d)", + oldtitle, + ttyDisplay->curx, ttyDisplay->cury, + console.cursor.X, console.cursor.Y); (void) SetConsoleTitle(newtitle); } #endif @@ -1280,7 +1378,7 @@ xputc(int ch) #ifndef VIRTUAL_TERMINAL_SEQUENCES void -xputc_core(char ch) +xputc_core(char ch) #else void xputc_core(int ch) @@ -1301,7 +1399,8 @@ xputc_core(int ch) case '\n': if (console.cursor.Y < console.height - 1) console.cursor.Y++; - /* fall through */ + FALLTHROUGH; + /* FALLTHRU */ case '\r': console.cursor.X = 1; break; @@ -1385,29 +1484,26 @@ xputc_core(int ch) */ #endif void -g_putch(int in_ch) +console_g_putch(int in_ch) { + unsigned char ch = (unsigned char) in_ch; + cell_t cell; #ifndef VIRTUAL_TERMINAL_SEQUENCES boolean inverse = FALSE; #else /* VIRTUAL_TERMINAL_SEQUENCES */ - int ccount = 0; + ccount = 0; WCHAR wch[2]; + boolean usemap = (ch >= 0 && ch < SIZE(console.cpMap)); #endif /* VIRTUAL_TERMINAL_SEQUENCES */ - unsigned char ch = (unsigned char) in_ch; set_console_cursor(ttyDisplay->curx, ttyDisplay->cury); #ifndef VIRTUAL_TERMINAL_SEQUENCES - inverse = (console.current_nhattr[ATR_INVERSE] && iflags.wc_inverse); console.attr = (console.current_nhattr[ATR_INVERSE] && iflags.wc_inverse) ? ttycolors_inv[console.current_nhcolor] : ttycolors[console.current_nhcolor]; if (console.current_nhattr[ATR_BOLD]) console.attr |= (inverse) ? BACKGROUND_INTENSITY : FOREGROUND_INTENSITY; - -#endif /* ! VIRTUAL_TERMINAL_SEQUENCES */ - cell_t cell; -#ifndef VIRTUAL_TERMINAL_SEQUENCES cell.attribute = console.attr; cell.character = (console.has_unicode ? cp437[ch] : ch); #else @@ -1418,7 +1514,7 @@ g_putch(int in_ch) cell.color256idx = 0; wch[1] = 0; if (console.has_unicode) { - wch[0] = (ch >= 0 && ch < SIZE(console.cpMap)) ? console.cpMap[ch] : ch; + wch[0] = (usemap) ? console.cpMap[ch] : ch; #ifdef UTF8_FROM_CORE if (SYMHANDLING(H_UTF8)) { /* we have to convert it to UTF-8 for cell.utf8str */ @@ -1440,7 +1536,7 @@ g_putch(int in_ch) cell.utf8str[1] = 0; ccount = 2; } -#endif +#endif /* VIRTUAL_TERMINAL_SEQUENCES */ buffer_write(console.back_buffer, &cell, console.cursor); } @@ -1486,10 +1582,10 @@ term_start_extracolor(uint32 nhcolor, uint16 color256idx) term_start_color(console.current_nhcolor); #ifdef VIRTUAL_TERMINAL_SEQUENCES } -#endif +#endif } -void term_start_256color(int idx) +void term_start_256color(int idx UNUSED) { } @@ -1506,7 +1602,7 @@ term_end_extracolor(void) void cl_end(void) { - if (ttyDisplay->curx < console.width + if (ttyDisplay->curx < console.width && ttyDisplay->cury < console.height) { set_console_cursor(ttyDisplay->curx, ttyDisplay->cury); buffer_clear_to_end_of_line(console.back_buffer, console.cursor.X, @@ -1524,9 +1620,6 @@ raw_clear_screen(void) cell_t * front = console.front_buffer; COORD pos; DWORD unused; -#ifdef VIRTUAL_TERMINAL_SEQUENCES - DWORD reserved; -#endif #ifdef VIRTUAL_TERMINAL_SEQUENCES pos.Y = 0; @@ -1552,10 +1645,10 @@ raw_clear_screen(void) *back = clear_cell; if (console.has_unicode) WriteConsoleW(console.hConOut, &back->wcharacter, 1, - &unused, &reserved); + &unused, NULL); else WriteConsoleA(console.hConOut, (LPCSTR) back->utf8str, - (int) strlen((char *) back->utf8str), &unused, &reserved); + (int) strlen((char *) back->utf8str), &unused, NULL); #endif /* VIRTUAL_TERMINAL_SEQUENCES */ *front = *back; back++; @@ -1572,6 +1665,25 @@ term_clear_screen(void) home(); } +void +term_curs_set(int visibility) +{ + static int vis = -1; + + if (vis == visibility) + return; + + static CONSOLE_CURSOR_INFO cursorinfo = { 0, 0 }; + + if (!cursorinfo.dwSize) { + GetConsoleCursorInfo(console.hConOut, &cursorinfo); + vis = cursorinfo.bVisible ? 1 : 0; + } + cursorinfo.bVisible = visibility ? (BOOL) TRUE : (BOOL) FALSE; + SetConsoleCursorInfo(console.hConOut, &cursorinfo); + vis = visibility; +} + void home(void) { @@ -1619,6 +1731,7 @@ tty_delay_output(void) while (goal > clock()) { k = junk; /* Do nothing */ } + nhUse(k); } /* @@ -1848,6 +1961,7 @@ toggle_mouse_support(void) #endif /* VIRTUAL_TERMINAL_SEQUENCES */ break; case 0: + FALLTHROUGH; /*FALLTHRU*/ default: #ifndef VIRTUAL_TERMINAL_SEQUENCES @@ -2010,9 +2124,9 @@ win32con_debug_keystrokes(void) xputs("\n"); while (!valid || ch != 27) { nocmov(ttyDisplay->curx, ttyDisplay->cury); - ReadConsoleInput(console.hConIn, &ir, 1, &count); - if ((ir.EventType == KEY_EVENT) && ir.Event.KeyEvent.bKeyDown) - ch = process_keystroke(&ir, &valid, iflags.num_pad, 1); + ReadConsoleInput(console.hConIn, &gbl_ir, 1, &count); + if ((gbl_ir.EventType == KEY_EVENT) && gbl_ir.Event.KeyEvent.bKeyDown) + ch = process_keystroke(&gbl_ir, &valid, (uchar) iflags.num_pad, 1); } (void) doredraw(); } @@ -2056,7 +2170,7 @@ map_subkeyvalue(char *op) key_overrides[idx] = val; } - +#if 0 /* fatal error */ /*VARARGS1*/ void consoletty_error @@ -2075,6 +2189,7 @@ VA_DECL(const char *, s) VA_END(); exit(EXIT_FAILURE); } +#endif void synch_cursor(void) @@ -2082,13 +2197,16 @@ synch_cursor(void) really_move_cursor(); } +#ifndef VIRTUAL_TERMINAL_SEQUENCES static int CALLBACK EnumFontCallback( - const LOGFONTW * lf, const TEXTMETRICW * tm, DWORD fontType, LPARAM lParam) + const LOGFONTW * lf, const TEXTMETRICW * tm UNUSED, + DWORD fontType UNUSED, LPARAM lParam) { LOGFONTW * lf_ptr = (LOGFONTW *) lParam; *lf_ptr = *lf; return 0; } +#endif /* check_and_set_font ensures that the current font will render the symbols * that are currently being used correctly. If they will not be rendered @@ -2113,15 +2231,12 @@ check_and_set_font(void) #endif } +#ifndef VIRTUAL_TERMINAL_SEQUENCES /* check_font_widths returns TRUE if all glyphs in current console font * fit within the width of a single console cell. */ boolean -#ifndef VIRTUAL_TERMINAL_SEQUENCES check_font_widths(void) -#else /* VIRTUAL_TERMINAL_SEQUENCES */ -check_font_widths(void) -#endif /* VIRTUAL_TERMINAL_SEQUENCES */ { CONSOLE_FONT_INFOEX console_font_info; console_font_info.cbSize = sizeof(console_font_info); @@ -2189,7 +2304,7 @@ check_font_widths(void) int wcUsedCount = 0; wchar_t wcUsed[256]; - for (int i = 0; i < sizeof(used); i++) + for (int i = 0; i < (int) sizeof(used); i++) if (used[i]) wcUsed[wcUsedCount++] = cp437[i]; @@ -2218,6 +2333,7 @@ clean_up: return all_glyphs_fit; } +#endif /* set_known_good_console_font sets the code page and font used by the console * to settings know to work well with NetHack. It also saves the original @@ -2431,7 +2547,7 @@ void nethack_enter_consoletty(void) { #ifdef VIRTUAL_TERMINAL_SEQUENCES char buf[BUFSZ], *bp, *localestr; - BOOL success; + BOOL apisuccess; #endif /* VIRTUAL_TERMINAL_SEQUENCES */ #if 0 /* set up state needed by early_raw_print() */ @@ -2511,7 +2627,7 @@ void nethack_enter_consoletty(void) buffer_fill_to_end(console.back_buffer, &clear_cell, 0, 0); /* determine whether OS version has unicode support */ - console.has_unicode = ((GetVersion() & 0x80000000) == 0); + console.has_unicode = (IsWindows8OrGreater()); #ifdef VIRTUAL_TERMINAL_SEQUENCES /* store the original code page*/ @@ -2525,7 +2641,7 @@ void nethack_enter_consoletty(void) /* store the original font */ console.orig_font_info.cbSize = sizeof(console.orig_font_info); - success = GetCurrentConsoleFontEx(console.hConOut, + apisuccess = GetCurrentConsoleFontEx(console.hConOut, FALSE, &console.orig_font_info); console.font_info = console.orig_font_info; @@ -2542,9 +2658,9 @@ void nethack_enter_consoletty(void) console.code_page = console.orig_code_page; if (console.has_unicode) { if (console.code_page != 437) - success = SetConsoleOutputCP(437); + apisuccess = SetConsoleOutputCP(437); } else if (console.code_page != 1252) { - success = SetConsoleOutputCP(1252); + apisuccess = SetConsoleOutputCP(1252); } console.code_page = GetConsoleOutputCP(); #endif /* VIRTUAL_TERMINAL_SEQUENCES */ @@ -2626,6 +2742,7 @@ void nethack_enter_consoletty(void) #endif /* VIRTUAL_TERMINAL_SEQUENCES */ console.current_nhcolor = NO_COLOR; console.is_ready = TRUE; + nhUse(apisuccess); } #endif /* TTY_GRAPHICS */ @@ -2651,7 +2768,7 @@ VA_DECL(const char *, fmt) if (console.cursor.Y > console.height - 4) { xputs("Hit to continue."); while (pgetchar() != '\n') - ; + ; raw_clear_screen(); set_console_cursor(1, 0); } @@ -2684,7 +2801,7 @@ VA_DECL(const char *, fmt) #ifdef QWERTZ_SUPPORT /* when 'numberpad' is 0 and Cmd.swap_yz is True - (signaled by setting 0x10 on boolean numpad argument) + (signaled by setting 0x10 on uchar numberpad argument) treat keypress of numpad 7 as 'z' rather than 'y' */ static boolean qwertz = FALSE; #endif @@ -2905,10 +3022,10 @@ set_keyhandling_via_option(void) int default_processkeystroke( - HANDLE hConIn, + HANDLE hConIn UNUSED, INPUT_RECORD *ir, boolean *valid, - boolean numberpad, + uchar numberpad, int portdebug) { int k = 0; @@ -3070,12 +3187,12 @@ default_kbhit(HANDLE hConIn, INPUT_RECORD *ir) return retval; } -int +int default_checkinput( HANDLE hConIn, INPUT_RECORD *ir, DWORD *count, - boolean numpad, + uchar numberpad, int mode, int *mod, coord *cc) @@ -3087,8 +3204,8 @@ default_checkinput( boolean valid = 0, done = 0; #ifdef QWERTZ_SUPPORT - if (numpad & 0x10) { - numpad &= ~0x10; + if (numberpad & 0x10) { + numberpad &= ~0x10; qwertz = TRUE; } else { qwertz = FALSE; @@ -3105,21 +3222,21 @@ default_checkinput( ReadConsoleInput(hConIn, ir, 1, count); if (mode == 0) { if ((ir->EventType == KEY_EVENT) && ir->Event.KeyEvent.bKeyDown) { - ch = default_processkeystroke(hConIn, ir, &valid, numpad, 0); + ch = default_processkeystroke(hConIn, ir, &valid, numberpad, 0); done = valid; } } else { - if (count > 0) { + if (*count > 0) { if (ir->EventType == KEY_EVENT && ir->Event.KeyEvent.bKeyDown) { #ifdef QWERTZ_SUPPORT if (qwertz) - numpad |= 0x10; + numberpad |= 0x10; #endif - ch = default_processkeystroke(hConIn, ir, &valid, numpad, 0); + ch = default_processkeystroke(hConIn, ir, &valid, numberpad, 0); #ifdef QWERTZ_SUPPORT - numpad &= ~0x10; -#endif + numberpad &= ~0x10; +#endif if (valid) return ch; } else if (ir->EventType == MOUSE_EVENT) { @@ -3333,7 +3450,7 @@ is_altseq(unsigned long shiftstate) case RIGHT_ALT_PRESSED: case RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED: - return (GetVersion() & 0x80000000) == 0; + return (IsWindows8OrGreater()); default: return 0; @@ -3344,7 +3461,7 @@ int ray_processkeystroke( HANDLE hConIn, INPUT_RECORD *ir, boolean *valid, - boolean numberpad, + uchar numberpad, int portdebug) { int keycode, vk; @@ -3531,7 +3648,7 @@ ray_checkinput( HANDLE hConIn, INPUT_RECORD *ir, DWORD *count, - boolean numpad, + uchar numberpad, int mode, int *mod, coord *cc) @@ -3543,8 +3660,8 @@ ray_checkinput( boolean valid = 0, done = 0; #ifdef QWERTZ_SUPPORT - if (numpad & 0x10) { - numpad &= ~0x10; + if (numberpad & 0x10) { + numberpad &= ~0x10; qwertz = TRUE; } else { qwertz = FALSE; @@ -3566,22 +3683,22 @@ ray_checkinput( ReadConsoleInput(hConIn, ir, 1, count); } else { ch = 0; - if (count > 0) { + if (*count > 0) { if (ir->EventType == KEY_EVENT && ir->Event.KeyEvent.bKeyDown) { #ifdef QWERTZ_SUPPORT if (qwertz) - numpad |= 0x10; + numberpad |= 0x10; #endif - ch = ray_processkeystroke(hConIn, ir, &valid, numpad, + ch = ray_processkeystroke(hConIn, ir, &valid, numberpad, #ifdef PORTDEBUG 1); #else 0); #endif #ifdef QWERTZ_SUPPORT - numpad &= ~0x10; -#endif + numberpad &= ~0x10; +#endif if (valid) return ch; } else { @@ -3601,7 +3718,7 @@ ray_checkinput( else if (ir->Event.MouseEvent.dwButtonState & RIGHTBUTTON) *mod = CLICK_2; -#if 0 /* middle button */ +#if 0 /* middle button */ else if (ir->Event.MouseEvent.dwButtonState & MIDBUTTON) *mod = CLICK_3; #endif @@ -3699,12 +3816,12 @@ ray_kbhit( * shift values below. */ -int +int nh340_processkeystroke( - HANDLE hConIn, + HANDLE hConIn UNUSED, INPUT_RECORD *ir, boolean *valid, - boolean numberpad, + uchar numberpad, int portdebug) { int keycode, vk; @@ -3850,7 +3967,7 @@ nh340_checkinput( HANDLE hConIn, INPUT_RECORD *ir, DWORD *count, - boolean numpad, + uchar numberpad, int mode, int *mod, coord *cc) @@ -3862,8 +3979,8 @@ nh340_checkinput( boolean valid = 0, done = 0; #ifdef QWERTZ_SUPPORT - if (numpad & 0x10) { - numpad &= ~0x10; + if (numberpad & 0x10) { + numberpad &= ~0x10; qwertz = TRUE; } else { qwertz = FALSE; @@ -3882,19 +3999,19 @@ nh340_checkinput( if ((ir->EventType == KEY_EVENT) && ir->Event.KeyEvent.bKeyDown) { #ifdef QWERTZ_SUPPORT if (qwertz) - numpad |= 0x10; + numberpad |= 0x10; #endif - ch = nh340_processkeystroke(hConIn, ir, &valid, numpad, 0); + ch = nh340_processkeystroke(hConIn, ir, &valid, numberpad, 0); #ifdef QWERTZ_SUPPORT - numpad &= ~0x10; -#endif + numberpad &= ~0x10; +#endif done = valid; } } else { - if (count > 0) { + if (*count > 0) { if (ir->EventType == KEY_EVENT && ir->Event.KeyEvent.bKeyDown) { - ch = nh340_processkeystroke(hConIn, ir, &valid, numpad, 0); + ch = nh340_processkeystroke(hConIn, ir, &valid, numberpad, 0); if (valid) return ch; } else if (ir->EventType == MOUSE_EVENT) { diff --git a/sys/windows/fetch.cmd b/sys/windows/fetch.cmd index 7399ed31c..c484f9b13 100644 --- a/sys/windows/fetch.cmd +++ b/sys/windows/fetch.cmd @@ -5,7 +5,7 @@ if [%1] == [lua] ( set LUA_VERSION=5.4.6 set LUASRC=../lib/lua set CURLLUASRC=http://www.lua.org/ftp/lua-%LUA_VERSION%.tar.gz - set CURLLUADST=lua-%LUA_VERSION.tar.gz + set CURLLUADST=lua-%LUA_VERSION%.tar.gz if NOT exist lib/lua.h ( cd lib curl -L %CURLLUASRC% -o %CURLLUADST% diff --git a/sys/windows/fetch.sh b/sys/windows/fetch.sh new file mode 100644 index 000000000..4d9b36202 --- /dev/null +++ b/sys/windows/fetch.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +if [ ! -d lib ]; then +mkdir -p lib +fi + +if [ $1 == "lua" ]; then + if [ -z "$LUA_VERSION" ]; then + export LUA_VERSION=5.4.6 + 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 + + if [ ! -f lib/lua.h ] ;then + cd lib + curl -L $CURLLUASRC -o $CURLLUADST + /c/Windows/System32/tar -xvf $CURLLUADST + cd .. + fi +fi + +if [ $1 == "pdcursesmod" ]; then + export CURLPDCSRC=https://github.com/Bill-Gray/PDCursesMod/archive/refs/tags/v4.4.0.zip + export CURLPDCDST=pdcursesmod.zip + + if [ ! -f lib/pdcursesmod/curses.h ] ; then + cd lib + curl -L $CURLPDCSRC -o $CURLPDCDST + /c/Windows/System32/tar -xvf $CURLPDCDST + mkdir -p pdcursesmod + /c/Windows/System32/tar -C pdcursesmod --strip-components=1 -xvf $CURLPDCDST + cd .. + fi +fi diff --git a/sys/windows/nethackrc.template b/sys/windows/nethackrc.template index 7cfdd1b4b..60db0ba77 100644 --- a/sys/windows/nethackrc.template +++ b/sys/windows/nethackrc.template @@ -135,10 +135,6 @@ OPTIONS=hilite_pet,!toptenwin # window, windowframe, windowtext. #OPTIONS=windowcolors:status windowtext/window message windowtext/window -# "Nethack mode" colors -OPTIONS=windowcolors:status white/#000000 message white/#000000 text white/#000000 menu white/#000000 menutext white/#000000 -OPTIONS=vary_msgcount:4 - # *** LOCATIONS *** # IMPORTANT: If you change any of these locations, the directories they # point at must exist. NetHack will not create them for you. diff --git a/sys/windows/nhsetup.bat b/sys/windows/nhsetup.bat index d365c1558..ed41d2361 100755 --- a/sys/windows/nhsetup.bat +++ b/sys/windows/nhsetup.bat @@ -47,10 +47,10 @@ echo ..\..\src\Makefile-orig copy /Y Makefile.nmake ..\..\src\Makefile >nul echo Microsoft nmake Makefile.nmake copy to ..\..\src\Makefile completed. -echo Copying mingw-w64 Makefile.mingw32 to ..\..\src\Makefile.mingw32 -copy /Y Makefile.mingw32 ..\..\src\Makefile.mingw32 >nul -echo Copying mingw-w64 Makefile.mingw32.depend to ..\..\src\Makefile.mingw32.depend -copy /Y Makefile.mingw32.depend ..\..\src\Makefile.mingw32.depend >nul +echo Copying mingw-w64 GNUmakefile to ..\..\src\GNUmakefile +copy /Y GNUmakefile ..\..\src\GNUmakefile >nul +echo Copying mingw-w64 GNUmakefile.depend to ..\..\src\GNUmakefile.depend +copy /Y GNUmakefile.depend ..\..\src\GNUmakefile.depend >nul echo mingw-w64 Makefile copies to ..\..\src completed. echo Done copying files. diff --git a/sys/windows/porthelp b/sys/windows/porthelp index a251e14b5..7861b4062 100644 --- a/sys/windows/porthelp +++ b/sys/windows/porthelp @@ -1,38 +1,35 @@ Microsoft Windows specific help file for NetHack 3.7 - Copyright (c) NetHack PC Development Team 1993-2020. + Copyright (c) NetHack PC Development Team 1993-2025. NetHack may be freely distributed. See license for details. - (Last Revision: August 8, 2020) + (Last Revision: August 19, 2025) -This file details specifics for NetHack built for Windows 95, 98, NT, -Me, 2000, and XP. Users of really early 16-bit Windows versions should -use the MSDOS NetHack. +This file details some specifics for NetHack built for Windows. -Please note that "NetHack for Windows - Graphical Interface" requires -an installation of Internet Explorer 4 or an installation of -version 4.71 of the common controls. See the following internet page: - http://www.nethack.org/v340/ports/download-win.html#cc -for more information. If the game runs for you, you are not affected. - -New players should be sure to read GuideBook.txt which contains +New players should be sure to read GuideBook.txt which contains essential information about playing NetHack. It can be found in the same directory as your NetHack executable. -The NetHack for Windows port supports some additional or enhanced -commands as well as some defaults.nh file options specific to -configuration choices used during the building of NetHack for -Windows. Listed below are those commands and defaults.nh file -options. +The NetHack for Windows port supports some additional or enhanced +commands as well as some NetHack config file options. -Some options are applicable only to the "Graphical Interface." -These are discussed separately in their own section. +The active NetHack config file is stored in the nethack subfolder of the +user's User Profile Folder. The config file can be referenced +in the legacy CMD command prompt as: + %USERPROFILE%\nethack\.nethackrc + +Under powershell, the User's config file can be referenced +as: + $env:USERPROFILE\nethack\.nethackrc + +Some options are applicable only to the "Graphical Interface." +These are discussed separately in their own section. Contents 1. ALT Key Combinations -2. Boolean options - Option that you can toggle on or off -3. Graphical Interface - Options you can assign a value to -4. Graphical Interface - Additional/Enhanced Commands -5. Graphical Interface - Menus -6. Numeric Keypad (for number_pad mode) +2. Graphical Interface - Options you can assign a value to +3. Graphical Interface - Additional/Enhanced Commands +4. Graphical Interface - Menus +5. Numeric Keypad (for number_pad mode) 1. ALT Key Combinations @@ -42,7 +39,7 @@ while the "NetHack for Windows - Graphical Interface" lets you toggle the mode. In non-NetHack mode, all ALT-key combinations are sent to the Windows itself, rather than to NetHack. -While playing in NetHack mode you can press the ALT key in +While playing in NetHack mode you can press the ALT key in combination with another key to execute an extended command as an alternative method to pressing a # key sequence. The available commands are: @@ -57,7 +54,7 @@ The available commands are: Alt-i #invoke - invoke an object's powers. Alt-j #jump - jump to a location. Alt-l #loot - loot a box on the floor. - Alt-m #monster - use a monster's special ability. + Alt-m #monster - use a monster's special ability. Alt-n #name - name an item or type of object. Alt-o #offer - offer a sacrifice to the gods. Alt-p #pray - pray to the gods for help. @@ -71,41 +68,14 @@ The available commands are: Alt-w #wipe - wipe off your face. Alt-? #? - display list of extended menu commands -2. Boolean Options (Options that can be toggled on or off) ----------------------------------------------------------- - -Listed here are any options not discussed in the main help, options -which may be slightly different from the main help file, and options -which may need a slightly more explanatory note: - - color Use color when displaying non-tiled maps. Tiled - maps (available in the graphical port) are always - rendered in color. Default: [TRUE] - - hilite_pet Using tiled graphics, displays a small heart symbol - next to your pet. Using ascii graphics, the pet is - hilited in a white background. - Default: [TRUE] - - IBMgraphics Use IBM extended characters for the dungeon - Default: [TRUE] - - msg_window When ^P is pressed, it shows menu in a full window. - Available only in the non-graphical (tty) version. - Default: [FALSE] - - toptenwin Write top ten list to a window, as opposed to stdout. - Default in tty interface: [FALSE] - Default in graphical interface: [TRUE] (and cannot be changed) - -3. Options that you assign a value to (Graphical Interface only) +2. Options that you assign a value to (Graphical Interface only) ---------------------------------------------------------------- -"NetHack for Windows - Graphical Interface" recognizes the following +"NetHack for Windows - Graphical Interface" recognizes the following additional options, which the non-graphical (tty) version will silently ignore. These are options that specify attributes of various -windows. The windows that you can tailor include menu windows (such -as the inventory list), text windows (such as "It is written in the +windows. The windows that you can tailor include menu windows (such +as the inventory list), text windows (such as "It is written in the book of ..." screens), the message window (where events of the game are displayed), the status window (where your character name and attributes are displayed), and the map window (where the map @@ -113,47 +83,47 @@ is drawn). Window Alignment options: - align_message Specifies at which side of the NetHack screen the - message window is aligned. This option can be used + align_message Specifies at which side of the NetHack screen the + message window is aligned. This option can be used to align the window to "top" or "bottom". - Default: [TOP] + Default: [TOP] - align_status Specifies at which side of the NetHack screen the + align_status Specifies at which side of the NetHack screen the status window is aligned. This option can be used to align the window to "top" or "bottom". - Default: [BOTTOM] + Default: [BOTTOM] Map Window options: - map_mode Specifies which map mode to use. - The following map modes are available: - tiles (display things on the map with colored tiles), + map_mode Specifies which map mode to use. + The following map modes are available: + tiles (display things on the map with colored tiles), ascii4x6, ascii6x8, ascii8x8, ascii16x8, ascii7x12, ascii8x12, ascii16x12, ascii12x16, ascii10x18 - (which use that size font to display things on + (which use that size font to display things on the map), or fit_to_screen (an ascii mode which forces things to fit on a single screen). Default: [tiles] scroll_margin Specifies the number of map cells from the edge of the map window where scrolling will take place. - Default: [5] + Default: [5] - tile_file An alternative file containing bitmap to use for - tiles. This file should be a .bmp file and should - be organized as 40 rectangular tiles wide. It is - beyond the scope of this document to describe the + tile_file An alternative file containing bitmap to use for + tiles. This file should be a .bmp file and should + be organized as 40 rectangular tiles wide. It is + beyond the scope of this document to describe the exact contents of each tile in the .bmp, which must match the object lists used when building NetHack. - tile_height Used with tile_file to specify the height of each + tile_height Used with tile_file to specify the height of each tile in pixels. This option may only be specified in the defaults.nh config file. - Default: [16] + Default: [16] - tile_width Used with tile_file to specify the width of each + tile_width Used with tile_file to specify the width of each tile in pixels. This option may only be specified - in the defaults.nh config file. + in the defaults.nh config file. Default: [16] Other Window options: @@ -177,12 +147,12 @@ Other Window options: cyan, gray (or grey), orange, brightgreen, yellow, brightblue, brightmagenta, brightcyan, white, trueblack, purple, silver, maroon, fuchsia, - lime, olive, navy, teal, aqua. In addition, you - can use the following names to refer to default - Windows settings: activeborder, activecaption, - appworkspace, background, btnface, btnshadow, btntext, - captiontext, graytext, highlight, highlighttext, - inactiveborder, inactivecaption, menu, menutext, + lime, olive, navy, teal, aqua. In addition, you + can use the following names to refer to default + Windows settings: activeborder, activecaption, \ + appworkspace, background, btnface, btnshadow, btntext, + captiontext, graytext, highlight, highlighttext, + inactiveborder, inactivecaption, menu, menutext, scrollbar, window, windowframe, windowtext. Example: @@ -203,56 +173,56 @@ Other Window options: font_size_text Specifies the size of the text font. -Miscellaneous options: +Miscellaneous options: - vary_msgcount Number of lines to display in message window. + vary_msgcount Number of lines to display in message window. -4. NetHack for Windows - Graphical Interface, Additional/Enhanced Commands +3. NetHack for Windows - Graphical Interface, Additional/Enhanced Commands ------------------------------------------------------------------------- The following function keys are active in -the "NetHack for Windows - Graphical Interface": +the "NetHack for Windows - Graphical Interface": - F4 Toggle level overview mode on/off - This key will toggle the map between a view that - is mapped to fit exactly to the window, and the - view that shows the various symbols in their - normal size. This is useful for getting an idea - of where you are in a level. + F4 Toggle level overview mode on/off + This key will toggle the map between a view that + is mapped to fit exactly to the window, and the + view that shows the various symbols in their + normal size. This is useful for getting an idea + of where you are in a level. - F5 Toggle tiled display on/off. - This key switches between the tiled and the - traditional ASCII display. This is equivalent to - using the "map_mode" option. + F5 Toggle tiled display on/off. + This key switches between the tiled and the + traditional ASCII display. This is equivalent to + using the "map_mode" option. - F10 Activate menu bar. - This key will activate the menu bar, allowing you - to select between the menus: File, Map, - Window Settings, and Help. + F10 Activate menu bar. + This key will activate the menu bar, allowing you + to select between the menus: File, Map, + Window Settings, and Help. -5. Graphical Port Menus +4. Graphical Port Menus ----------------------- File Save - Allows you to save and exit the game Quit - Allows you to quit the game -Map - Provides for selection of map mode. Equivalent to using -the map_mode option. +Map - Provides for selection of map mode. Equivalent to using +the map_mode option. Window Settings - Changes your logged-on user's settings for NetHack. -In 3.5.0, only one setting is available: NetHack mode, which can be +Only one setting is available: NetHack mode, which can be checked or unchecked. NetHack mode allows you to use the ALT key for game key commands [see list above]. You can use F10 to access the menu bar while in NetHack mode. You can also clear your logged-on user's settings for NetHack. Settings in this window are saved in -your logged-on user's registry. +your logged-on user's registry. Help - Provides help about various portions of NetHack. -6. Numeric Keypad (for "OPTION=number_pad" mode) +5. Numeric Keypad (for "OPTION=number_pad" mode) ------------------------------------------------ The numeric keypad and surrounding characters act as macros for different @@ -297,4 +267,3 @@ most of these keys: Ctrl-Shift-PgDn (3) Scroll (Pan) map left to lowermost corner - diff --git a/sys/windows/sysconf.template b/sys/windows/sysconf.template index 36f3651bc..90095dc1b 100644 --- a/sys/windows/sysconf.template +++ b/sys/windows/sysconf.template @@ -15,6 +15,10 @@ WIZARDS=* # Uses the same syntax as the WIZARDS option above. #SHELLERS= +# Execute this program whenever a new message-window message is shown. +# The program will get the message text as the only parameter. +#MSGHANDLER=\path\program + # Show debugging information originating from these source files. # Use '*' for all, or list source files separated by spaces. # Only available if game has been compiled with DEBUG. diff --git a/sys/windows/vs/FetchPrereq/fetchprereq.nmake b/sys/windows/vs/FetchPrereq/fetchprereq.nmake index 3b0ea1b8c..07c62dbb6 100644 --- a/sys/windows/vs/FetchPrereq/fetchprereq.nmake +++ b/sys/windows/vs/FetchPrereq/fetchprereq.nmake @@ -59,7 +59,7 @@ fetch-pdcurses: ..\..\..\..\include\nhlua.h: @echo /* nhlua.h - generated by Makefile from fetchprereq.nmake */ > $@ @echo #include "lua.h" >> $@ - @echo LUA_API int (lua_error) (lua_State *L) NORETURN; >> $@ + @echo ATTRNORETURN LUA_API int (lua_error) (lua_State *L) NORETURN; >> $@ @echo #include "lualib.h" >> $@ @echo #include "lauxlib.h" >> $@ @echo /*nhlua.h*/ >> $@ diff --git a/sys/windows/vs/NetHack.sln b/sys/windows/vs/NetHack.sln index 732d87c12..3bb69f828 100644 --- a/sys/windows/vs/NetHack.sln +++ b/sys/windows/vs/NetHack.sln @@ -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} + {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} = {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dlb", "dlb\dlb.vcxproj", "{0303A585-3F83-4BB7-AF6B-1E12C8FB54AC}" @@ -60,6 +61,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NetHack", "NetHack\NetHack. {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} + {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} = {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} {BAA70D0F-3EC7-4D10-91F0-974F1F49308B} = {BAA70D0F-3EC7-4D10-91F0-974F1F49308B} EndProjectSection EndProject @@ -101,6 +103,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "package\package. {CEC5D360-8804-454F-8591-002184C23499} = {CEC5D360-8804-454F-8591-002184C23499} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lualib", "lualib\lualib.vcxproj", "{B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}" + ProjectSection(ProjectDependencies) = postProject + {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -213,6 +220,14 @@ Global {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|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|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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/sys/windows/vs/NetHack/NetHack.vcxproj b/sys/windows/vs/NetHack/NetHack.vcxproj index b777fb0b0..7b28b0837 100644 --- a/sys/windows/vs/NetHack/NetHack.vcxproj +++ b/sys/windows/vs/NetHack/NetHack.vcxproj @@ -40,37 +40,24 @@ - /Gs /Oi- /w44774 %(AdditionalOptions) + /Gs /Oi- /w44774 /w45262 %(AdditionalOptions) + 4100;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) + stdclatest - hacklib.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;bcrypt.lib;%(AdditionalDependencies) + 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);%(AdditionalIncludeDirectories) - - - $(SndWavDir);%(AdditionalIncludeDirectories) - - - $(SndWavDir);%(AdditionalIncludeDirectories) + $(SndWavDir);%(AdditionalIncludeDirectories) - - 4701;4702;4244;4310;4774 - %(AdditionalOptions) /wd4774 - 4701;4702;4244;4310;4774;4324 - 4701;4702;4244;4310;4774;4324 - @@ -80,6 +67,7 @@ + @@ -136,10 +124,7 @@ - - 4820;4706;4244;4245;4100;4310;4324 - 4820;4706;4244;4245;4100;4310;4324 - + @@ -217,6 +202,7 @@ + @@ -243,6 +229,8 @@ + + @@ -275,6 +263,7 @@ + @@ -288,6 +277,9 @@ + + + @@ -308,4 +300,4 @@ - \ No newline at end of file + diff --git a/sys/windows/vs/NetHackW/NetHackW.vcxproj b/sys/windows/vs/NetHackW/NetHackW.vcxproj index 091dbb94e..11e6baa99 100644 --- a/sys/windows/vs/NetHackW/NetHackW.vcxproj +++ b/sys/windows/vs/NetHackW/NetHackW.vcxproj @@ -48,13 +48,16 @@ - /Gs /Oi- /w44774 %(AdditionalOptions) + /Gs /Oi- /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) - 4820;4706;4244;4245;4100;4310;6001 - 4820;4706;4244;4245;4100;4310 + stdclatest + stdclatest + stdclatest + stdclatest NDEBUG;%(PreprocessorDefinitions) @@ -66,20 +69,13 @@ Windows - hacklib.lib;dbghelp.lib;comctl32.lib;winmm.lib;bcrypt.lib;%(AdditionalDependencies) + hacklib.lib;lualib.lib;dbghelp.lib;comctl32.lib;winmm.lib;UserEnv.lib;bcrypt.lib;%(AdditionalDependencies) $(WinWin32Dir)NethackW.exe.manifest;%(AdditionalManifestFiles) - - - 4701;4702;4244;4310;4774 - %(AdditionalOptions) /wd4774 - 4701;4702;4244;4310;4774;4324;6011;6297;6305;6385;6386;6387;28182 - 4701;4702;4244;4310;4774;4324 - @@ -89,6 +85,7 @@ + @@ -146,17 +143,15 @@ - 4820;4706;4244;4245;4100;4310;4324 - 4820;4706;4244;4245;4100;4310;4324 + 4100;4244;4245;4310;4706;4820;4324 + 4100;4244;4245;4310;4706;4820;4324 - - 4820;4706;4244;4245;4100;4310;6001 - + @@ -190,9 +185,7 @@ - - 4820;4706;4244;4245;4100;4310;6001 - + @@ -216,20 +209,19 @@ - - 4820;4706;4244;4245;4100;4310;6001 - + - 4820;4706;4244;4245;4100;4310;4201 - 4820;4706;4244;4245;4100;4310;4201 - 4820;4706;4244;4245;4100;4310;4201 - 4820;4706;4244;4245;4100;4310;4201 + 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 + @@ -249,6 +241,7 @@ + @@ -339,6 +332,7 @@ + @@ -349,6 +343,9 @@ + + + @@ -372,4 +369,4 @@ - \ No newline at end of file + diff --git a/sys/windows/vs/cpp.hint b/sys/windows/vs/cpp.hint index 4f77429fe..3be7cc99a 100644 --- a/sys/windows/vs/cpp.hint +++ b/sys/windows/vs/cpp.hint @@ -4,3 +4,28 @@ #define E #define NDECL(x) x #define FDECL(x, y) x y +#define staticfn +#define NONNULL +#define NONNULLPTRS +#define NONNULLARG1 +#define NONNULLARG2 +#define NONNULLARG3 +#define NONNULLARG4 +#define NONNULLARG5 +#define NONNULLARG6 +#define NONNULLARG7 +#define NONNULLARG12 +#define NONNULLARG23 +#define NONNULLARG123 +#define NONNULLARG13 +#define NONNULLARG14 +#define NONNULLARG134 +#define NONNULLARG145 +#define NONNULLARG17 +#define NONNULLARG24 +#define NONNULLARG45 +#define NO_NNARGS +#define ATTRNORETURN +#define NORETURN +#define PRINTF_F + diff --git a/sys/windows/vs/dirs.props b/sys/windows/vs/dirs.props index dc3f7ca7b..8f1f17062 100644 --- a/sys/windows/vs/dirs.props +++ b/sys/windows/vs/dirs.props @@ -29,6 +29,8 @@ $(RootDir)win\curses\ $(RootDir)submodules\ $(LibDir)lua-$(LUA_VERSION)\src\ + $(RootDir)\sys\windows\vs\sftags\ + $(RootDir)\sys\windows\vs\sfctool\ $(LibDir)pdcursesmod\ diff --git a/sys/windows/vs/dlb/dlb.vcxproj b/sys/windows/vs/dlb/dlb.vcxproj index d54510ff0..71b96d823 100644 --- a/sys/windows/vs/dlb/dlb.vcxproj +++ b/sys/windows/vs/dlb/dlb.vcxproj @@ -28,6 +28,10 @@ $(IncDir);$(SysWindDir);$(SysShareDir);$(LuaDir);%(AdditionalIncludeDirectories) WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;%(PreprocessorDefinitions) + stdclatest + stdclatest + stdclatest + stdclatest $(ToolsDir);%(AdditionalLibraryDirectories) @@ -64,4 +68,4 @@ - \ No newline at end of file + diff --git a/sys/windows/vs/fetchctags/fetchctags.nmake b/sys/windows/vs/fetchctags/fetchctags.nmake new file mode 100644 index 000000000..70023ce1d --- /dev/null +++ b/sys/windows/vs/fetchctags/fetchctags.nmake @@ -0,0 +1,49 @@ +# NetHack 3.7 fetchctags.nmake +#============================================================================== +# +# The version of the game this Makefile was designed for +NETHACK_VERSION="3.7.0" + +# A brief version for use in macros +NHV=$(NETHACK_VERSION:.=) +NHV=$(NHV:"=) + +# The location of the ctags that we want +CTAGS_VERSION=6.1.0 +CURLCTAGSSRC=https://github.com/universal-ctags/ctags-win32/releases/download/v$(CTAGS_VERSION)/ctags-v$(CTAGS_VERSION)-x64.zip +CURLCTAGSDST=ctags-v$(CTAGS_VERSION)-x64.zip + +# +# relative directories from root of NetHack tree. +# + +ROOTDIR=..\..\..\.. # root of NetHack tree relative to project file +LIBDIR=$(ROOTDIR)\lib +CTAGSDIR=$(LIBDIR)\ctags + +default: fetchall + +fetchall: fetch-ctags + +fetch-ctags: $(CTAGSDIR) + cd $(LIBDIR) + curl --insecure -L -R -O $(CURLCTAGSSRC) +# cd ctags + tar zxf $(CURLCTAGSDST) -C ctags + cd ..\sys\windows\vs\fetchctags + @echo ctags.exe has been fetched into $(LIBDIR)\ctags + +$(CTAGSDIR): $(LIBDIR) + @if not exist $(CTAGSDIR)\*.* echo creating directory $(CTAGSDIR:\=/) + @if not exist $(CTAGSDIR)\*.* mkdir $(CTAGSDIR) + +$(LIBDIR): + @if not exist $(LIBDIR)\*.* echo creating directory $(LIBDIR:\=/) + @if not exist $(LIBDIR)\*.* mkdir $(LIBDIR) + +rebuild: + @if exist $(CTAGSDIR)\ctags.exe echo nothing to do for $(CTAGSDIR)\ctags.exe + +clean: + if exist $(CURLCTAGSDST) del $(CURLCTAGSDST) + diff --git a/sys/windows/vs/fetchctags/fetchctags.vcxproj b/sys/windows/vs/fetchctags/fetchctags.vcxproj new file mode 100644 index 000000000..58df9ec83 --- /dev/null +++ b/sys/windows/vs/fetchctags/fetchctags.vcxproj @@ -0,0 +1,71 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + 17.0 + {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD} + Win32Proj + 10.0 + + + + Makefile + true + + + Makefile + false + + + Makefile + true + + + Makefile + false + + + + + + + + + + + + echo ..\lib\ctags is already present + pushd $(vsDir)fetchctags %26%26 nmake -F fetchctags.nmake clean %26%26 popd + echo rebuilding $(vsDir)fetchctags + _DEBUG;$(NMakePreprocessorDefinitions) + + + pushd $(vsDir)fetchctags %26%26 nmake -F fetchctags.nmake %26%26 popd + pushd $(vsDir)fetchctags %26%26 nmake -F fetchctags.nmake clean %26%26 popd + pushd $(vsDir)fetchprereq %26%26 nmake -F fetchctags.nmake rebuild %26%26 popd + _DEBUG;$(NMakePreprocessorDefinitions) + + + + + + diff --git a/sys/windows/vs/files.props b/sys/windows/vs/files.props index 9dd50ca4c..16a62721e 100644 --- a/sys/windows/vs/files.props +++ b/sys/windows/vs/files.props @@ -32,6 +32,7 @@ + diff --git a/sys/windows/vs/hacklib/hacklib.vcxproj b/sys/windows/vs/hacklib/hacklib.vcxproj index b43c429ea..23b673271 100644 --- a/sys/windows/vs/hacklib/hacklib.vcxproj +++ b/sys/windows/vs/hacklib/hacklib.vcxproj @@ -25,23 +25,23 @@ - - + + - - - - - - - - - - - - - + + + + + + + + + + + + + 17.0 @@ -54,13 +54,13 @@ StaticLibrary true - v143 + $(DefaultPlatformToolset) Unicode StaticLibrary false - v143 + $(DefaultPlatformToolset) true Unicode @@ -102,7 +102,9 @@ WIN32;_DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) - + stdclatest + /w45262 %(AdditionalOptions) + @@ -118,6 +120,8 @@ WIN32;NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) + stdclatest + /w45262 %(AdditionalOptions) @@ -134,6 +138,8 @@ _DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) + stdclatest + /w45262 %(AdditionalOptions) @@ -150,6 +156,8 @@ NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) true $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) + stdclatest + /w45262 %(AdditionalOptions) @@ -164,11 +172,11 @@ - + - \ No newline at end of file + diff --git a/sys/windows/vs/lualib/lualib.vcxproj b/sys/windows/vs/lualib/lualib.vcxproj new file mode 100644 index 000000000..48cbb9f35 --- /dev/null +++ b/sys/windows/vs/lualib/lualib.vcxproj @@ -0,0 +1,204 @@ + + + + + + + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + /Gs /Oi- /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) + stdclatest + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + true + $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) + /Gs /Oi- /w44774 %(AdditionalOptions) + + + + + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + true + $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) + /Gs /Oi- /w44774 %(AdditionalOptions) + + + + + true + true + true + + + + + Level3 + true + _DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + true + $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) + /Gs /Oi- /w44774 %(AdditionalOptions) + + + + + true + + + + + Level3 + true + true + true + NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + true + $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) + /Gs /Oi- /w44774 %(AdditionalOptions) + + + + + true + true + true + + + + + + + + + + + + + + + diff --git a/sys/windows/vs/makedefs/makedefs.vcxproj b/sys/windows/vs/makedefs/makedefs.vcxproj index 9c3abce14..5b3c90584 100644 --- a/sys/windows/vs/makedefs/makedefs.vcxproj +++ b/sys/windows/vs/makedefs/makedefs.vcxproj @@ -28,21 +28,20 @@ $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + stdclatest + /w45262 %(AdditionalOptions) - $(ToolsDir);%(AdditionalLibraryDirectories) + $(ToolsDir);%(AdditionalLibraryDirectories) hacklib.lib;%(AdditionalDependencies) - $(ToolsDir);%(AdditionalLibraryDirectories) - $(ToolsDir);%(AdditionalLibraryDirectories) - $(ToolsDir);%(AdditionalLibraryDirectories) - - - - - + + + + + @@ -69,4 +68,4 @@ - \ No newline at end of file + diff --git a/sys/windows/vs/recover/recover.vcxproj b/sys/windows/vs/recover/recover.vcxproj index a3697a4b0..d03553015 100644 --- a/sys/windows/vs/recover/recover.vcxproj +++ b/sys/windows/vs/recover/recover.vcxproj @@ -19,11 +19,20 @@ $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) - WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;%(PreprocessorDefinitions) + WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;MINIMAL_FOR_RECOVER;%(PreprocessorDefinitions) + stdclatest + /w45262 %(AdditionalOptions) + + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) + + + WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;MINIMAL_FOR_RECOVER;%(PreprocessorDefinitions) + @@ -44,4 +53,4 @@ - \ No newline at end of file + diff --git a/sys/windows/vs/sfctool.sln b/sys/windows/vs/sfctool.sln new file mode 100644 index 000000000..06ef30f69 --- /dev/null +++ b/sys/windows/vs/sfctool.sln @@ -0,0 +1,68 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35913.81 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sfctool", "sfctool\sfctool.vcxproj", "{3BFA3C14-6DA2-4750-B1C6-028B9BCE825E}" + ProjectSection(ProjectDependencies) = postProject + {628C594A-7565-4366-9FCA-41DB67C6B615} = {628C594A-7565-4366-9FCA-41DB67C6B615} + {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD} = {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sftags", "sftags\sftags.vcxproj", "{628C594A-7565-4366-9FCA-41DB67C6B615}" + ProjectSection(ProjectDependencies) = postProject + {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD} = {AAE63AD3-8185-4E19-B6A3-13F4CB891AAD} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fetchctags", "fetchctags\fetchctags.vcxproj", "{AAE63AD3-8185-4E19-B6A3-13F4CB891AAD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hacklib", "hacklib\hacklib.vcxproj", "{096FD6BB-256A-4E68-9B09-2ACA7C606FF3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {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|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|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|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|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|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|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|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 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1000CD98-D3C0-493F-9EA7-E2D81FA96432} + EndGlobalSection +EndGlobal diff --git a/sys/windows/vs/sfctool/sfctool.vcxproj b/sys/windows/vs/sfctool/sfctool.vcxproj new file mode 100644 index 000000000..73fee3c05 --- /dev/null +++ b/sys/windows/vs/sfctool/sfctool.vcxproj @@ -0,0 +1,198 @@ + + + + + + + + + + + $(BinDir) + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 17.0 + Win32Proj + {3bfa3c14-6da2-4750-b1c6-028b9bce825e} + sfctool + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + $(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) + Level3 + stdclatest + true + true + + + Console + true + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) + + + + + $(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) + Level3 + stdclatest + true + true + true + true + + + Console + true + true + 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) + Level3 + stdclatest + true + true + + + Console + true + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;userenv.lib;advapi32.lib;%(AdditionalDependencies) + + + + + $(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) + Level3 + stdclatest + true + true + true + true + + + Console + true + true + true + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;userenv.lib;advapi32.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + + \ No newline at end of file diff --git a/sys/windows/vs/sftags/aftersftags.proj b/sys/windows/vs/sftags/aftersftags.proj new file mode 100644 index 000000000..a79669104 --- /dev/null +++ b/sys/windows/vs/sftags/aftersftags.proj @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sys/windows/vs/sftags/sftags.vcxproj b/sys/windows/vs/sftags/sftags.vcxproj new file mode 100644 index 000000000..f2979e613 --- /dev/null +++ b/sys/windows/vs/sftags/sftags.vcxproj @@ -0,0 +1,164 @@ + + + + + + + + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + 17.0 + Win32Proj + {628c594a-7565-4366-9fca-41db67c6b615} + sftags + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + $(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) + 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) + Level3 + true + true + true + true + + + Console + true + true + 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) + 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) + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + diff --git a/sys/windows/vs/tile2bmp/tile2bmp.vcxproj b/sys/windows/vs/tile2bmp/tile2bmp.vcxproj index 113567b86..8207ebceb 100644 --- a/sys/windows/vs/tile2bmp/tile2bmp.vcxproj +++ b/sys/windows/vs/tile2bmp/tile2bmp.vcxproj @@ -28,23 +28,13 @@ $(IncDir);$(SysWindDir);$(SysShareDir);$(LuaDir);%(AdditionalIncludeDirectories) WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;%(PreprocessorDefinitions) + stdclatest + /w45262 %(AdditionalOptions) - $(ToolsDir);%(AdditionalLibraryDirectories) - hacklib.lib;%(AdditionalDependencies) - - - $(ToolsDir);%(AdditionalLibraryDirectories) - hacklib.lib;%(AdditionalDependencies) - - - $(ToolsDir);%(AdditionalLibraryDirectories) - hacklib.lib;%(AdditionalDependencies) - - - $(ToolsDir);%(AdditionalLibraryDirectories) - hacklib.lib;%(AdditionalDependencies) - + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) + @@ -66,4 +56,4 @@ - \ No newline at end of file + diff --git a/sys/windows/vs/tilemap/tilemap.vcxproj b/sys/windows/vs/tilemap/tilemap.vcxproj index ee79ffc68..fdbbc92e7 100644 --- a/sys/windows/vs/tilemap/tilemap.vcxproj +++ b/sys/windows/vs/tilemap/tilemap.vcxproj @@ -30,22 +30,12 @@ $(IncDir);$(SysWindDir);$(SysShareDir);$(LuaDir);%(AdditionalIncludeDirectories) WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;%(PreprocessorDefinitions) + stdclatest + /w45262 %(AdditionalOptions) - $(ToolsDir);%(AdditionalLibraryDirectories) - hacklib.lib;%(AdditionalDependencies) - - - $(ToolsDir);%(AdditionalLibraryDirectories) - hacklib.lib;%(AdditionalDependencies) - - - $(ToolsDir);%(AdditionalLibraryDirectories) - hacklib.lib;%(AdditionalDependencies) - - - $(ToolsDir);%(AdditionalLibraryDirectories) - hacklib.lib;%(AdditionalDependencies) + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) @@ -89,6 +79,7 @@ + @@ -105,4 +96,4 @@ - \ No newline at end of file + diff --git a/sys/windows/vs/uudecode/uudecode.vcxproj b/sys/windows/vs/uudecode/uudecode.vcxproj index f99aa84b1..0975cacdf 100644 --- a/sys/windows/vs/uudecode/uudecode.vcxproj +++ b/sys/windows/vs/uudecode/uudecode.vcxproj @@ -26,15 +26,12 @@ - $(IncDir);$(SysShareDir); + $(IncDir);$(SysShareDir);$(WinWin32Dir) - 4820;4702;4706;4244;4245;4100;4310 - 4820;4702;4706;4244;4245;4100;4310 - 4820;4702;4706;4244;4245;4100;4310 - 4820;4702;4706;4244;4245;4100;4310 + 4820;4702;4706;4244;4245;4100;4310 diff --git a/sys/windows/win10.h b/sys/windows/win10.h index 22a4c0f12..cc8a0bf48 100644 --- a/sys/windows/win10.h +++ b/sys/windows/win10.h @@ -1,5 +1,5 @@ /* NetHack 3.7 win10.h $NHDT-Date: 1596498319 2020/08/03 23:45:19 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.8 $ */ -/* Copyright (C) 2018 by Bart House */ +/* Copyright (C) 2018 by Bart House */ /* NetHack may be freely redistributed. See license for details. */ #ifndef WIN10_H diff --git a/sys/windows/win32api.h b/sys/windows/win32api.h index d37daf7f3..1089f8b45 100644 --- a/sys/windows/win32api.h +++ b/sys/windows/win32api.h @@ -30,6 +30,8 @@ #error win32api.h should be included first #endif +#include "config.h" + #if defined(_MSC_VER) #pragma warning(disable : 4142) /* Warning, Benign redefinition of type */ #pragma pack(8) @@ -37,7 +39,6 @@ #ifdef DEBUG #define _CRTDBG_MAP_ALLOC -#include #include #endif diff --git a/sys/windows/windmain.c b/sys/windows/windmain.c index efa09ebb9..a2fe2da5e 100644 --- a/sys/windows/windmain.c +++ b/sys/windows/windmain.c @@ -10,8 +10,6 @@ #ifdef DLB #include "dlb.h" #endif -#include -#include #include #include #include @@ -20,14 +18,11 @@ #error You must #define SAFEPROCS to build windmain.c #endif -static void process_options(int argc, char **argv); static void nhusage(void); -static char *get_executable_path(void); -char *translate_path_variables(const char *, char *); +static void early_options(int argc, char **argv); char *exename(void); boolean fakeconsole(void); void freefakeconsole(void); -ATTRNORETURN extern void nethack_exit(int) NORETURN; #if defined(MSWIN_GRAPHICS) extern void mswin_destroy_reg(void); #endif @@ -63,12 +58,13 @@ extern void (*utf8graphics_mode_callback)(void); #ifdef PC_LOCKING static int eraseoldlocks(void); #endif + int windows_nhgetch(void); void windows_nhbell(void); int windows_nh_poskey(int *, int *, int *); void windows_raw_print(const char *); char windows_yn_function(const char *, const char *, char); -static void windows_getlin(const char *, char *); +/* static void windows_getlin(const char *, char *); */ #ifdef WIN32CON extern int windows_console_custom_nhgetch(void); @@ -100,407 +96,51 @@ static struct stat hbuf; extern char orgdir[]; -int get_known_folder_path(const KNOWNFOLDERID * folder_id, - char * path, size_t path_size); -void create_directory(const char * path); -int build_known_folder_path(const KNOWNFOLDERID * folder_id, - char * path, size_t path_size, boolean versioned); -void build_environment_path(const char * env_str, const char * folder, - char * path, size_t path_size); -boolean folder_file_exists(const char * folder, const char * file_name); -boolean test_portable_config(const char *executable_path, - char *portable_device_path, size_t portable_device_path_size); -void set_default_prefix_locations(const char *programPath); +extern void set_default_prefix_locations(const char *programPath); void copy_sysconf_content(void); void copy_config_content(void); void copy_hack_content(void); void copy_symbols_content(void); +void copy_file(const char *, const char *, + const char *, const char *, boolean); +void update_file(const char *, const char *, + const char *, const char *, BOOL); +void windows_raw_print_bold(const char *); #ifdef PORT_HELP void port_help(void); #endif void windows_raw_print(const char *str); - - - -DISABLE_WARNING_UNREACHABLE_CODE - -int -get_known_folder_path( - const KNOWNFOLDERID * folder_id, - char * path - , size_t path_size) -{ - PWSTR wide_path; - if (FAILED(SHGetKnownFolderPath(folder_id, 0, NULL, &wide_path))) { - error("Unable to get known folder path"); - return FALSE; - } - - size_t converted; - errno_t err; - - err = wcstombs_s(&converted, path, path_size, wide_path, _TRUNCATE); - - CoTaskMemFree(wide_path); - - if (err == STRUNCATE || err == EILSEQ) { - // silently handle this problem - return FALSE; - } else if (err != 0) { - error("Failed folder (%lu) path string conversion, unexpected err = %d", - folder_id->Data1, err); - return FALSE; - } - - return TRUE; -} - -void -create_directory(const char * path) -{ - - BOOL dres = CreateDirectoryA(path, NULL); - - if (!dres) { - DWORD dw = GetLastError(); - - if (dw != ERROR_ALREADY_EXISTS) - error("Unable to create directory '%s'", path); - } -} - -RESTORE_WARNING_UNREACHABLE_CODE - -int -build_known_folder_path( - const KNOWNFOLDERID * folder_id, - char * path, - size_t path_size, - boolean versioned) -{ - if(!get_known_folder_path(folder_id, path, path_size)) - return FALSE; - - strcat(path, "\\NetHack\\"); - create_directory(path); - if (versioned) { - Sprintf(eos(path), "%d.%d\\", - VERSION_MAJOR, VERSION_MINOR); - create_directory(path); - } - return TRUE; -} - -void -build_environment_path( - const char * env_str, - const char * folder, - char * path, - size_t path_size) -{ - path[0] = '\0'; - - const char * root_path = nh_getenv(env_str); - - if (root_path == NULL) return; - - strcpy_s(path, path_size, root_path); - - char * colon = strchr(path, ';'); - if (colon != NULL) path[0] = '\0'; - - if (strlen(path) == 0) return; - - append_slash(path); - - if (folder != NULL) { - strcat_s(path, path_size, folder); - strcat_s(path, path_size, "\\"); - } -} - -boolean -folder_file_exists(const char * folder, const char * file_name) -{ - char path[MAX_PATH]; - - if (folder[0] == '\0') return FALSE; - - strcpy(path, folder); - strcat(path, file_name); - return file_exists(path); -} - -boolean -test_portable_config( - const char *executable_path, - char *portable_device_path, - size_t portable_device_path_size) -{ - int lth = 0; - const char *sysconf = "sysconf"; - char tmppath[MAX_PATH]; - boolean retval = FALSE, - save_initoptions_noterminate = iflags.initoptions_noterminate; - - if (portable_device_path && folder_file_exists(executable_path, "sysconf")) { - /* - There is a sysconf file (not just sysconf.template) present in - the exe path, which is not the way NetHack is initially distributed, - so assume it means that the admin/installer wants to override - something, perhaps set up for a fully-portable configuration that - leaves no traces behind elsewhere on this computer's hard drive - - delve into that... - */ - - *portable_device_path = '\0'; - lth = sizeof tmppath - strlen(sysconf); - (void) strncpy(tmppath, executable_path, lth - 1); - tmppath[lth - 1] = '\0'; - (void) strcat(tmppath, sysconf); - - iflags.initoptions_noterminate = 1; - /* assure_syscf_file(); */ - config_error_init(TRUE, tmppath, FALSE); - /* ... and _must_ parse correctly. */ - if (read_config_file(tmppath, set_in_sysconf) - && sysopt.portable_device_paths) - retval = TRUE; - (void) config_error_done(); - iflags.initoptions_noterminate = save_initoptions_noterminate; - sysopt_release(); /* the real sysconf processing comes later */ - } - if (retval) { - lth = strlen(executable_path); - if (lth <= (int) portable_device_path_size - 1) - Strcpy(portable_device_path, executable_path); - else - retval = FALSE; - } - return retval; -} - -static char portable_device_path[MAX_PATH]; - -const char *get_portable_device(void) -{ - return (const char *) portable_device_path; -} - -void -set_default_prefix_locations(const char *programPath UNUSED) -{ - static char executable_path[MAX_PATH]; - static char profile_path[MAX_PATH]; - static char versioned_profile_path[MAX_PATH]; - static char versioned_user_data_path[MAX_PATH]; - static char versioned_global_data_path[MAX_PATH]; -/* static char versioninfo[20] UNUSED; */ - - strcpy(executable_path, get_executable_path()); - append_slash(executable_path); - - if (test_portable_config(executable_path, - portable_device_path, sizeof portable_device_path)) { - gf.fqn_prefix[SYSCONFPREFIX] = executable_path; - gf.fqn_prefix[CONFIGPREFIX] = portable_device_path; - gf.fqn_prefix[HACKPREFIX] = portable_device_path; - gf.fqn_prefix[SAVEPREFIX] = portable_device_path; - gf.fqn_prefix[LEVELPREFIX] = portable_device_path; - gf.fqn_prefix[BONESPREFIX] = portable_device_path; - gf.fqn_prefix[SCOREPREFIX] = portable_device_path; - gf.fqn_prefix[LOCKPREFIX] = portable_device_path; - gf.fqn_prefix[TROUBLEPREFIX] = portable_device_path; - gf.fqn_prefix[DATAPREFIX] = executable_path; - } else { - if(!build_known_folder_path(&FOLDERID_Profile, profile_path, - sizeof(profile_path), FALSE)) - strcpy(profile_path, executable_path); - - if(!build_known_folder_path(&FOLDERID_Profile, versioned_profile_path, - sizeof(profile_path), TRUE)) - strcpy(versioned_profile_path, executable_path); - - if(!build_known_folder_path(&FOLDERID_LocalAppData, - versioned_user_data_path, sizeof(versioned_user_data_path), TRUE)) - strcpy(versioned_user_data_path, executable_path); - - if(!build_known_folder_path(&FOLDERID_ProgramData, - versioned_global_data_path, sizeof(versioned_global_data_path), TRUE)) - strcpy(versioned_global_data_path, executable_path); - - gf.fqn_prefix[SYSCONFPREFIX] = versioned_global_data_path; - gf.fqn_prefix[CONFIGPREFIX] = profile_path; - gf.fqn_prefix[HACKPREFIX] = versioned_profile_path; - gf.fqn_prefix[SAVEPREFIX] = versioned_user_data_path; - gf.fqn_prefix[LEVELPREFIX] = versioned_user_data_path; - gf.fqn_prefix[BONESPREFIX] = versioned_global_data_path; - gf.fqn_prefix[SCOREPREFIX] = versioned_global_data_path; - gf.fqn_prefix[LOCKPREFIX] = versioned_global_data_path; - gf.fqn_prefix[TROUBLEPREFIX] = versioned_profile_path; - gf.fqn_prefix[DATAPREFIX] = executable_path; - } -} - -/* copy file if destination does not exist */ -void -copy_file( - const char * dst_folder, - const char * dst_name, - const char * src_folder, - const char * src_name, - boolean copy_even_if_it_exists) -{ - char dst_path[MAX_PATH]; - strcpy(dst_path, dst_folder); - strcat(dst_path, dst_name); - - char src_path[MAX_PATH]; - strcpy(src_path, src_folder); - strcat(src_path, src_name); - - if(!file_exists(src_path)) - error("Unable to copy file '%s' as it does not exist", src_path); - - if(file_exists(dst_path) && !copy_even_if_it_exists) - return; - - BOOL success = CopyFileA(src_path, dst_path, !copy_even_if_it_exists); - if(!success) error("Failed to copy '%s' to '%s' (%d)", src_path, dst_path, errno); -} - -/* update file copying if it does not exist or src is newer then dst */ -void -update_file( - const char * dst_folder, - const char * dst_name, - const char * src_folder, - const char * src_name, - BOOL save_copy) -{ - char dst_path[MAX_PATH]; - strcpy(dst_path, dst_folder); - strcat(dst_path, dst_name); - - char src_path[MAX_PATH]; - strcpy(src_path, src_folder); - strcat(src_path, src_name); - - char save_path[MAX_PATH]; - strcpy(save_path, dst_folder); - strcat(save_path, dst_name); - strcat(save_path, ".save"); - - if(!file_exists(src_path)) - error("Unable to copy file '%s' as it does not exist", src_path); - - if (!file_newer(src_path, dst_path)) - return; - - if (file_exists(dst_path) && save_copy) - CopyFileA(dst_path, save_path, FALSE); - - BOOL success = CopyFileA(src_path, dst_path, FALSE); - if(!success) error("Failed to update '%s' to '%s'", src_path, dst_path); - -} - -void -copy_symbols_content(void) -{ - char dst_path[MAX_PATH], interim_path[MAX_PATH], orig_path[MAX_PATH]; - - boolean no_template = FALSE; - - /* Using the SYSCONFPREFIX path, lock it so that it does not change */ - fqn_prefix_locked[SYSCONFPREFIX] = TRUE; - - strcpy(orig_path, gf.fqn_prefix[DATAPREFIX]); - strcat(orig_path, SYMBOLS_TEMPLATE); - strcpy(interim_path, gf.fqn_prefix[SYSCONFPREFIX]); - strcat(interim_path, SYMBOLS_TEMPLATE); - strcpy(dst_path, gf.fqn_prefix[SYSCONFPREFIX]); - strcat(dst_path, SYMBOLS); - - if (!file_exists(orig_path)) { - char alt_orig_path[MAX_PATH]; - - strcpy(alt_orig_path, gf.fqn_prefix[DATAPREFIX]); - strcat(alt_orig_path, SYMBOLS); - if (file_exists(alt_orig_path)) { - no_template = TRUE; - /* symbols -> symbols.template */ - copy_file(gf.fqn_prefix[DATAPREFIX], SYMBOLS_TEMPLATE, - gf.fqn_prefix[DATAPREFIX], SYMBOLS, TRUE); - } - } - if (!file_exists(interim_path) || file_newer(orig_path, interim_path)) { - /* symbols.template -> symbols.template */ - copy_file(gf.fqn_prefix[SYSCONFPREFIX], SYMBOLS_TEMPLATE, - gf.fqn_prefix[DATAPREFIX], SYMBOLS_TEMPLATE, TRUE); - } - if (!file_exists(dst_path) || file_newer(interim_path, dst_path)) { - /* symbols.template -> symbols */ - copy_file(gf.fqn_prefix[SYSCONFPREFIX], SYMBOLS, - gf.fqn_prefix[SYSCONFPREFIX], SYMBOLS_TEMPLATE, TRUE); - } -} - -void copy_sysconf_content(void) -{ - /* Using the SYSCONFPREFIX path, lock it so that it does not change */ - fqn_prefix_locked[SYSCONFPREFIX] = TRUE; - - update_file(gf.fqn_prefix[SYSCONFPREFIX], SYSCF_TEMPLATE, - gf.fqn_prefix[DATAPREFIX], SYSCF_TEMPLATE, FALSE); - - /* If the required early game file does not exist, copy it */ - copy_file(gf.fqn_prefix[SYSCONFPREFIX], SYSCF_FILE, - gf.fqn_prefix[DATAPREFIX], SYSCF_TEMPLATE, FALSE); - -} - -void copy_config_content(void) -{ - /* Using the CONFIGPREFIX path, lock it so that it does not change */ - fqn_prefix_locked[CONFIGPREFIX] = TRUE; - - /* Keep templates up to date */ - update_file(gf.fqn_prefix[CONFIGPREFIX], CONFIG_TEMPLATE, - gf.fqn_prefix[DATAPREFIX], CONFIG_TEMPLATE, FALSE); - - /* If the required early game file does not exist, copy it */ - /* NOTE: We never replace .nethackrc or sysconf */ - copy_file(gf.fqn_prefix[CONFIGPREFIX], CONFIG_FILE, - gf.fqn_prefix[DATAPREFIX], CONFIG_TEMPLATE, FALSE); -} - -void -copy_hack_content(void) -{ - nhassert(fqn_prefix_locked[HACKPREFIX]); - - /* Keep Guidebook and opthelp up to date */ - update_file(gf.fqn_prefix[HACKPREFIX], GUIDEBOOK_FILE, - gf.fqn_prefix[DATAPREFIX], GUIDEBOOK_FILE, FALSE); - update_file(gf.fqn_prefix[HACKPREFIX], OPTIONFILE, - gf.fqn_prefix[DATAPREFIX], OPTIONFILE, FALSE); -} extern const char *known_handling[]; /* symbols.c */ extern const char *known_restrictions[]; /* symbols.c */ -/* - * __MINGW32__ Note - * If the graphics version is built, we don't need a main; it is skipped - * to help MinGW decide which entry point to choose. If both main and - * WinMain exist, the resulting executable won't work correctly. - */ + +/* --------------------------------------------------------------------------- */ DISABLE_WARNING_UNREACHABLE_CODE -#if defined(__MINGW32__) && defined(MSWIN_GRAPHICS) -#define MAIN mingw_main +/* + * NetHack main + * + * The following function is used in both the nongraphical nethack.exe, and + * in the graphical nethackw.exe. + * + * The function below is called main() in the non-graphical build of + * NetHack for Windows and is the primary entry point for the program. + * + * It is called nethackw_main() in the graphical build of NetHack for + * Windows, where WinMain() is the primary entry point for the program + * and this nethackw_main() is called as a sub function. + * + * The code in WinMain() (in win/win32/nethackw.c) is primarily focused + * on setting up the graphical windows environment, and leaves the + * NetHack-specific startup code to this function. + * + */ + +#if defined(MSWIN_GRAPHICS) +#define MAIN nethackw_main +int nethackw_main(int, char **); #else #define MAIN main #endif @@ -530,7 +170,9 @@ MAIN(int argc, char *argv[]) safe_routines(); #endif /* WIN32CON */ - early_init(argc, argv); +#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(); @@ -573,7 +215,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ if (getcwd(orgdir, sizeof orgdir) == (char *) 0) error("NetHack: current directory path too long"); #endif - initoptions_init(); // This allows OPTIONS in syscf on Windows. + initoptions_init(); // This allows OPTIONS in syscf on Windows. set_default_prefix_locations(argv[0]); #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) @@ -587,6 +229,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ 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 @@ -594,7 +237,6 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ fqn_prefix_locked[TROUBLEPREFIX] = TRUE; copy_config_content(); - process_options(argc, argv); /* did something earlier flag a need to exit without starting a game? */ if (windows_startup_state > 0) { @@ -718,12 +360,15 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ (void) fname_encode( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.", '%', fnamebuf, encodedfnamebuf, BUFSZ); - Sprintf(gl.lock, "%s", encodedfnamebuf); + Snprintf(gl.lock, sizeof gl.lock, "%s", encodedfnamebuf); /* regularize(lock); */ /* we encode now, rather than substitute */ if ((getlock_result = getlock()) == 0) nethack_exit(EXIT_SUCCESS); if (getlock_result < 0) { + if (program_state.in_self_recover) { + program_state.in_self_recover = FALSE; + } set_savefile_name(TRUE); } /* Set up level 0 file to keep the game state. @@ -807,23 +452,18 @@ attempt_restore: RESTORE_WARNING_UNREACHABLE_CODE static void -process_options(int argc, char * argv[]) +early_options(int argc, char *argv[]) { int i; - /* - * Process options. - */ if (argc > 1) { if (argcheck(argc, argv, ARG_VERSION) == 2) nethack_exit(EXIT_SUCCESS); if (argcheck(argc, argv, ARG_SHOWPATHS) == 2) { - iflags.initoptions_noterminate = TRUE; - initoptions(); - iflags.initoptions_noterminate = FALSE; - reveal_paths(); - nethack_exit(EXIT_SUCCESS); + gd.deferred_showpaths = TRUE; + /* gd.deferred_showpaths is not used by windows */ + return; } #ifndef NODUMPENUMS if (argcheck(argc, argv, ARG_DUMPENUMS) == 2) { @@ -835,6 +475,12 @@ process_options(int argc, char * argv[]) } #endif #endif + if (argcheck(argc, argv, ARG_DUMPMONGEN) == 2) { + nethack_exit(EXIT_SUCCESS); + } + if (argcheck(argc, argv, ARG_DUMPWEIGHTS) == 2) { + nethack_exit(EXIT_SUCCESS); + } if (argcheck(argc, argv, ARG_DEBUG) == 1) { argc--; argv++; @@ -844,9 +490,9 @@ process_options(int argc, char * argv[]) argv++; } #if defined(CRASHREPORT) - if (argcheck(argc, argv, ARG_BIDSHOW) == 2) { - nethack_exit(EXIT_SUCCESS); - } + if (argcheck(argc, argv, ARG_BIDSHOW) == 2) { + nethack_exit(EXIT_SUCCESS); + } #endif if (argc > 1 && !strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') { /* avoid matching "-dec" for DECgraphics; since the man page @@ -854,7 +500,7 @@ process_options(int argc, char * argv[]) */ argc--; argv++; - const char * dir = argv[0] + 2; + const char *dir = argv[0] + 2; if (*dir == '=' || *dir == ':') dir++; if (!*dir && argc > 1) { @@ -923,7 +569,8 @@ process_options(int argc, char * argv[]) #endif case 'u': if (argv[0][2]) - (void) strncpy(svp.plname, argv[0] + 2, sizeof(svp.plname) - 1); + (void) strncpy(svp.plname, argv[0] + 2, + sizeof(svp.plname) - 1); else if (argc > 1) { argc--; argv++; @@ -979,7 +626,8 @@ process_options(int argc, char * argv[]) break; } else raw_printf("\nUnknown switch: %s", argv[0]); - /* FALL THROUGH */ + FALLTHROUGH; + /* FALLTHRU */ case '?': nhusage(); nethack_exit(EXIT_SUCCESS); @@ -1024,6 +672,147 @@ nhusage(void) #undef ADD_USAGE } +/* copy file if destination does not exist */ +void +copy_file(const char *dst_folder, const char *dst_name, + const char *src_folder, const char *src_name, + boolean copy_even_if_it_exists) +{ + char dst_path[MAX_PATH]; + strcpy(dst_path, dst_folder); + strcat(dst_path, dst_name); + + char src_path[MAX_PATH]; + strcpy(src_path, src_folder); + strcat(src_path, src_name); + + if (!file_exists(src_path)) + error("Unable to copy file '%s' as it does not exist", src_path); + + if (file_exists(dst_path) && !copy_even_if_it_exists) + return; + + BOOL success = CopyFileA(src_path, dst_path, !copy_even_if_it_exists); + if (!success) + error("Failed to copy '%s' to '%s' (%d)", src_path, dst_path, errno); +} + +/* update file copying if it does not exist or src is newer then dst */ +void +update_file(const char *dst_folder, const char *dst_name, + const char *src_folder, const char *src_name, BOOL save_copy) +{ + char dst_path[MAX_PATH]; + strcpy(dst_path, dst_folder); + strcat(dst_path, dst_name); + + char src_path[MAX_PATH]; + strcpy(src_path, src_folder); + strcat(src_path, src_name); + + char save_path[MAX_PATH]; + strcpy(save_path, dst_folder); + strcat(save_path, dst_name); + strcat(save_path, ".save"); + + if (!file_exists(src_path)) + error("Unable to copy file '%s' as it does not exist", src_path); + + if (!file_newer(src_path, dst_path)) + return; + + if (file_exists(dst_path) && save_copy) + CopyFileA(dst_path, save_path, FALSE); + + BOOL success = CopyFileA(src_path, dst_path, FALSE); + if (!success) + error("Failed to update '%s' to '%s'", src_path, dst_path); +} + +void +copy_symbols_content(void) +{ + char dst_path[MAX_PATH], interim_path[MAX_PATH], orig_path[MAX_PATH]; + + boolean no_template = FALSE; + + /* Using the SYSCONFPREFIX path, lock it so that it does not change */ + fqn_prefix_locked[SYSCONFPREFIX] = TRUE; + + strcpy(orig_path, gf.fqn_prefix[DATAPREFIX]); + strcat(orig_path, SYMBOLS_TEMPLATE); + strcpy(interim_path, gf.fqn_prefix[SYSCONFPREFIX]); + strcat(interim_path, SYMBOLS_TEMPLATE); + strcpy(dst_path, gf.fqn_prefix[SYSCONFPREFIX]); + strcat(dst_path, SYMBOLS); + + if (!file_exists(orig_path)) { + char alt_orig_path[MAX_PATH]; + + strcpy(alt_orig_path, gf.fqn_prefix[DATAPREFIX]); + strcat(alt_orig_path, SYMBOLS); + if (file_exists(alt_orig_path)) { + no_template = TRUE; + /* symbols -> symbols.template */ + copy_file(gf.fqn_prefix[DATAPREFIX], SYMBOLS_TEMPLATE, + gf.fqn_prefix[DATAPREFIX], SYMBOLS, TRUE); + } + } + if (!file_exists(interim_path) || file_newer(orig_path, interim_path)) { + /* symbols.template -> symbols.template */ + copy_file(gf.fqn_prefix[SYSCONFPREFIX], SYMBOLS_TEMPLATE, + gf.fqn_prefix[DATAPREFIX], SYMBOLS_TEMPLATE, TRUE); + } + if (!file_exists(dst_path) || file_newer(interim_path, dst_path)) { + /* symbols.template -> symbols */ + copy_file(gf.fqn_prefix[SYSCONFPREFIX], SYMBOLS, + gf.fqn_prefix[SYSCONFPREFIX], SYMBOLS_TEMPLATE, TRUE); + } + nhUse(no_template); +} + +void +copy_sysconf_content(void) +{ + /* Using the SYSCONFPREFIX path, lock it so that it does not change */ + fqn_prefix_locked[SYSCONFPREFIX] = TRUE; + + update_file(gf.fqn_prefix[SYSCONFPREFIX], SYSCF_TEMPLATE, + gf.fqn_prefix[DATAPREFIX], SYSCF_TEMPLATE, FALSE); + + /* If the required early game file does not exist, copy it */ + copy_file(gf.fqn_prefix[SYSCONFPREFIX], SYSCF_FILE, + gf.fqn_prefix[DATAPREFIX], SYSCF_TEMPLATE, FALSE); +} + +void +copy_config_content(void) +{ + /* Using the CONFIGPREFIX path, lock it so that it does not change */ + fqn_prefix_locked[CONFIGPREFIX] = TRUE; + + /* Keep templates up to date */ + update_file(gf.fqn_prefix[CONFIGPREFIX], CONFIG_TEMPLATE, + gf.fqn_prefix[DATAPREFIX], CONFIG_TEMPLATE, FALSE); + + /* If the required early game file does not exist, copy it */ + /* NOTE: We never replace .nethackrc or sysconf */ + copy_file(gf.fqn_prefix[CONFIGPREFIX], CONFIG_FILE, + gf.fqn_prefix[DATAPREFIX], CONFIG_TEMPLATE, FALSE); +} + +void +copy_hack_content(void) +{ + nhassert(fqn_prefix_locked[HACKPREFIX]); + + /* Keep Guidebook and opthelp up to date */ + update_file(gf.fqn_prefix[HACKPREFIX], GUIDEBOOK_FILE, + gf.fqn_prefix[DATAPREFIX], GUIDEBOOK_FILE, FALSE); + update_file(gf.fqn_prefix[HACKPREFIX], OPTIONFILE, + gf.fqn_prefix[DATAPREFIX], OPTIONFILE, FALSE); +} + #ifdef WIN32CON void safe_routines(void) @@ -1064,7 +853,9 @@ authorize_explore_mode(void) return TRUE; /* no restrictions on explore mode */ } +#ifndef PATH_SEPARATOR #define PATH_SEPARATOR '\\' +#endif #if defined(WIN32) && !defined(WIN32CON) static char exenamebuf[PATHLEN]; @@ -1098,10 +889,10 @@ boolean fakeconsole(void) { if (!hStdOut) { - HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); - HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE); + HANDLE fkhStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + HANDLE fkhStdIn = GetStdHandle(STD_INPUT_HANDLE); - if (!hStdOut && !hStdIn) { + if (!fkhStdOut && !fkhStdIn) { /* Bool rval; */ AllocConsole(); AttachConsole(GetCurrentProcessId()); @@ -1131,96 +922,6 @@ void freefakeconsole(void) } #endif -static boolean path_buffer_set = FALSE; -static char path_buffer[MAX_PATH]; - -char * -get_executable_path(void) -{ - -#ifdef UNICODE - { - TCHAR wbuf[BUFSZ]; - GetModuleFileName((HANDLE) 0, wbuf, BUFSZ); - WideCharToMultiByte(CP_ACP, 0, wbuf, -1, path_buffer, sizeof(path_buffer), NULL, NULL); - } -#else - DWORD length = GetModuleFileName((HANDLE) 0, path_buffer, MAX_PATH); - if (length == ERROR_INSUFFICIENT_BUFFER) error("Unable to get module name"); - path_buffer[length] = '\0'; -#endif - - char * seperator = strrchr(path_buffer, PATH_SEPARATOR); - if (seperator) - *seperator = '\0'; - - path_buffer_set = TRUE; - return path_buffer; -} - -char * -windows_exepath(void) -{ - char *p = (char *) 0; - - if (path_buffer_set) - p = path_buffer; - return p; -} - -char * -translate_path_variables(const char *str, char *buf) -{ - const char *src; - char evar[BUFSZ], *dest, *envp, *eptr = (char *) 0; - boolean in_evar; - size_t ccount, ecount, destcount, slen = str ? strlen(str) : 0; - - if (!slen || !buf) { - if (buf) - *buf = '\0'; - return buf; - } - - dest = buf; - src = str; - in_evar = FALSE; - destcount = ecount = 0; - for (ccount = 0; ccount < slen && destcount < (BUFSZ - 1) && - ecount < (BUFSZ - 1); ++ccount, ++src) { - if (*src == '%') { - if (in_evar) { - *eptr = '\0'; - envp = nh_getenv(evar); - if (envp) { - size_t elen = strlen(envp); - - if ((elen + destcount) < (size_t) (BUFSZ - 1)) { - Strcpy(dest, envp); - dest += elen; - destcount += elen; - } - } - } else { - eptr = evar; - ecount = 0; - } - in_evar = !in_evar; - continue; - } - if (in_evar) { - *eptr++ = *src; - ecount++; - } else { - *dest++ = *src; - destcount++; - } - } - *dest = '\0'; - return buf; -} - - /*ARGSUSED*/ void windows_raw_print(const char *str) @@ -1268,11 +969,13 @@ windows_yn_function(const char *query UNUSED, const char *resp UNUSED, } /*ARGSUSED*/ +#if 0 static void windows_getlin(const char *prompt UNUSED, char *outbuf) { Strcpy(outbuf, "\033"); } +#endif #ifdef PC_LOCKING static int @@ -1435,18 +1138,6 @@ gotlock: } #endif /* PC_LOCKING */ -boolean -file_exists(const char *path) -{ - struct stat sb; - - /* Just see if it's there */ - if (stat(path, &sb)) { - return FALSE; - } - return TRUE; -} - RESTORE_WARNING_UNREACHABLE_CODE /* diff --git a/sys/windows/windsys.c b/sys/windows/windsys.c index 0634796b5..6be9b6407 100644 --- a/sys/windows/windsys.c +++ b/sys/windows/windsys.c @@ -13,6 +13,7 @@ #include "win10.h" #include "winos.h" +#include #define NEED_VARARGS #include "hack.h" @@ -20,7 +21,6 @@ #ifndef __BORLANDC__ #include #endif -#include #ifdef TTY_GRAPHICS #include "wintty.h" #endif @@ -28,6 +28,7 @@ #ifdef WIN32 #include +#include /* * The following WIN32 API routines are used in this file. @@ -41,6 +42,12 @@ * */ +static char portable_device_path[MAX_PATH]; + +static boolean path_buffer_set = FALSE; +static char path_buffer[MAX_PATH]; + +#ifndef SFCTOOL /* runtime cursor display control switch */ boolean win32_cursorblink; @@ -50,13 +57,17 @@ WIN32_FIND_DATA ffd; extern int GUILaunched; extern boolean getreturn_enabled; int redirect_stdout; +static char *get_executable_path(void); + + #ifdef WIN32CON typedef HWND(WINAPI *GETCONSOLEWINDOW)(void); -#ifdef WIN32CON +#if 0 static HWND GetConsoleHandle(void); static HWND GetConsoleHwnd(void); -#endif +#endif /* 0 */ +#endif /* WIN32CON */ #if !defined(TTY_GRAPHICS) extern void backsp(void); #endif @@ -68,7 +79,17 @@ unsigned long sys_random_seed(void); static int max_filename(void); #endif - +int get_known_folder_path(const KNOWNFOLDERID *folder_id, char *path, + size_t path_size); +void create_directory(const char *path); +int build_known_folder_path(const KNOWNFOLDERID *folder_id, char *path, + size_t path_size, boolean versioned); +void build_environment_path(const char *env_str, const char *folder, + char *path, size_t path_size); +boolean folder_file_exists(const char *folder, const char *file_name); +boolean test_portable_config(const char *executable_path, + char *portable_device_path, + size_t portable_device_path_size); /* The function pointer nt_kbhit contains a kbhit() equivalent * which varies depending on which window port is active. * For the tty port it is tty_kbhit() [from consoletty.c] @@ -78,7 +99,6 @@ static int max_filename(void); int def_kbhit(void); int (*nt_kbhit)(void) = def_kbhit; -#endif /* WIN32CON */ #ifndef WIN32CON /* this is used as a printf() replacement when the window @@ -92,7 +112,7 @@ VA_DECL(const char *, fmt) VA_END(); return; } -#endif +#endif /* WIN32CON */ char switchar(void) @@ -232,8 +252,10 @@ VA_DECL(const char *, s) VA_START(s); VA_INIT(s, const char *); /* error() may get called before tty is initialized */ +#ifdef TTY_GRAPHICS if (iflags.window_inited) - end_screen(); + term_end_screen(); +#endif if (WINDOWPORT(tty)) { buf[0] = '\n'; (void) vsnprintf(&buf[1], sizeof buf - (1 + sizeof "\n"), s, VA_ARGS); @@ -328,6 +350,7 @@ interject_assistance(int num, int interjection_type, genericptr_t ptr1, genericp } } break; } + nhUse(interjection_type); } void @@ -412,16 +435,19 @@ void port_insert_pastebuf(char *buf) } #ifdef WIN32CON +#if 0 static HWND GetConsoleHandle(void) { HMODULE hMod = GetModuleHandle("kernel32.dll"); - GETCONSOLEWINDOW pfnGetConsoleWindow = - (GETCONSOLEWINDOW) GetProcAddress(hMod, "GetConsoleWindow"); - if (pfnGetConsoleWindow) - return pfnGetConsoleWindow(); - else - return GetConsoleHwnd(); + + if (hMod) { + GETCONSOLEWINDOW pfnGetConsoleWindow = + (GETCONSOLEWINDOW) GetProcAddress(hMod, "GetConsoleWindow"); + if (pfnGetConsoleWindow) + return pfnGetConsoleWindow(); + } + return GetConsoleHwnd(); } static HWND @@ -434,7 +460,7 @@ GetConsoleHwnd(void) /* Get current window title */ GetConsoleTitle(OldTitle, sizeof OldTitle); - (void) sprintf(NewTitle, "NETHACK%ld/%ld", GetTickCount(), + (void) sprintf(NewTitle, "NETHACK%lld/%ld", GetTickCount64(), GetCurrentProcessId()); SetConsoleTitle(NewTitle); @@ -449,8 +475,9 @@ GetConsoleHwnd(void) /* printf("%d iterations\n", iterations); */ return hwndFound; } +#endif /* 0 */ #endif /* WIN32CON */ -#endif +#endif /* RUNTIME_PASTEBUF_SUPPORT */ #ifdef RUNTIME_PORT_ID /* @@ -481,6 +508,10 @@ get_port_id(char *buf) } #endif /* RUNTIME_PORT_ID */ +#ifdef MSWIN_GRAPHICS +extern void free_winmain_stuff(void); +#endif + void nethack_exit(int code) { @@ -504,6 +535,11 @@ nethack_exit(int code) if (iflags.window_inited) wait_synch(); } + /* frees some status tracking data */ + genl_status_finish(); +#ifdef MSWIN_GRAPHICS + free_winmain_stuff(); +#endif exit(code); } @@ -682,25 +718,6 @@ windows_early_options(const char *window_opt) return 0; } -/* - * Add a backslash to any name not ending in /, \ or : There must - * be room for the \ - */ -void -append_slash(char *name) -{ - char *ptr; - - if (!*name) - return; - ptr = name + (strlen(name) - 1); - if (*ptr != '\\' && *ptr != '/' && *ptr != ':') { - *++ptr = '\\'; - *++ptr = '\0'; - } - return; -} - #include /* Windows Crypto Next Gen (CNG) */ #ifndef STATUS_SUCCESS @@ -737,7 +754,7 @@ sys_random_seed(void) time_t datetime = 0; const char *emsg; - if (status == STATUS_NOT_FOUND) + if (status == (NTSTATUS) STATUS_NOT_FOUND) emsg = "BCRYPT_RNG_ALGORITHM not avail, falling back"; else emsg = "Other failure than algorithm not avail"; @@ -770,7 +787,370 @@ nt_assert_failed(const char *expression, const char *filepath, int line) impossible("nhassert(%s) failed in file '%s' at line %d", expression, filename, line); } +#endif /* SFCTOOL */ +/* used by util/sfctool.c as well as files.c */ +boolean +get_user_home_folder(char *homebuf, size_t sz) +{ + static char szHomeDirBuf[MAX_PATH] = { 0 }; + // We need a process with query permission set + HANDLE hToken = 0; + DWORD result = + OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken); + DWORD BufSize = MAX_PATH; + + result = GetUserProfileDirectoryA(hToken, szHomeDirBuf, &BufSize); + // Close handle opened via OpenProcessToken + CloseHandle(hToken); + if (result != 0) { + Snprintf(homebuf, sz, "%s", szHomeDirBuf); + } + + return (result != 0); +} + +char * +get_executable_path(void) +{ +#ifdef UNICODE + { + TCHAR wbuf[BUFSZ]; + GetModuleFileName((HANDLE) 0, wbuf, BUFSZ); + WideCharToMultiByte(CP_ACP, 0, wbuf, -1, path_buffer, + sizeof(path_buffer), NULL, NULL); + } +#else + DWORD length = GetModuleFileName((HANDLE) 0, path_buffer, MAX_PATH); + if (length == ERROR_INSUFFICIENT_BUFFER) + error("Unable to get module name"); + path_buffer[length] = '\0'; +#endif + + char *seperator = strrchr(path_buffer, PATH_SEPARATOR); + if (seperator) + *seperator = '\0'; + + path_buffer_set = TRUE; + return path_buffer; +} + +char * +windows_exepath(void) +{ + char *p = (char *) 0; + + if (path_buffer_set) + p = path_buffer; + return p; +} + +char * +translate_path_variables(const char *str, char *buf) +{ + const char *src; + char evar[BUFSZ], *dest, *envp, *eptr = (char *) 0; + boolean in_evar; + size_t ccount, ecount, destcount, slen = str ? strlen(str) : 0; + + if (!slen || !buf) { + if (buf) + *buf = '\0'; + return buf; + } + + dest = buf; + src = str; + in_evar = FALSE; + destcount = ecount = 0; + for (ccount = 0; + ccount < slen && destcount < (BUFSZ - 1) && ecount < (BUFSZ - 1); + ++ccount, ++src) { + if (*src == '%') { + if (in_evar) { + *eptr = '\0'; + envp = nh_getenv(evar); + if (envp) { + size_t elen = strlen(envp); + + if ((elen + destcount) < (size_t) (BUFSZ - 1)) { + Strcpy(dest, envp); + dest += elen; + destcount += elen; + } + } + } else { + eptr = evar; + ecount = 0; + } + in_evar = !in_evar; + continue; + } + if (in_evar) { + *eptr++ = *src; + ecount++; + } else { + *dest++ = *src; + destcount++; + } + } + *dest = '\0'; + return buf; +} + +DISABLE_WARNING_UNREACHABLE_CODE + +int +get_known_folder_path(const KNOWNFOLDERID *folder_id, char *path, + size_t path_size) +{ + PWSTR wide_path; + if (FAILED(SHGetKnownFolderPath(folder_id, 0, NULL, &wide_path))) { + error("Unable to get known folder path"); + return FALSE; + } + + size_t converted; + errno_t err; + + err = wcstombs_s(&converted, path, path_size, wide_path, _TRUNCATE); + + CoTaskMemFree(wide_path); + + if (err == STRUNCATE || err == EILSEQ) { + // silently handle this problem + return FALSE; + } else if (err != 0) { + error( + "Failed folder (%lu) path string conversion, unexpected err = %d", + folder_id->Data1, err); + return FALSE; + } + + return TRUE; +} + +void +create_directory(const char *path) +{ + BOOL dres = CreateDirectoryA(path, NULL); + + if (!dres) { + DWORD dw = GetLastError(); + + if (dw != ERROR_ALREADY_EXISTS) + error("Unable to create directory '%s'", path); + } +} + +RESTORE_WARNING_UNREACHABLE_CODE + +int +build_known_folder_path(const KNOWNFOLDERID *folder_id, char *path, + size_t path_size, boolean versioned) +{ + if (!get_known_folder_path(folder_id, path, path_size)) + return FALSE; + + strcat(path, "\\NetHack\\"); + create_directory(path); + if (versioned) { + Sprintf(eos(path), "%d.%d\\", VERSION_MAJOR, VERSION_MINOR); + create_directory(path); + } + return TRUE; +} + +void +build_environment_path(const char *env_str, const char *folder, char *path, + size_t path_size) +{ + path[0] = '\0'; + + const char *root_path = nh_getenv(env_str); + + if (root_path == NULL) + return; + + strcpy_s(path, path_size, root_path); + + char *colon = strchr(path, ';'); + if (colon != NULL) + path[0] = '\0'; + + if (strlen(path) == 0) + return; + + append_slash(path); + + if (folder != NULL) { + strcat_s(path, path_size, folder); + strcat_s(path, path_size, "\\"); + } +} + +boolean +folder_file_exists(const char *folder, const char *file_name) +{ + char path[MAX_PATH]; + + if (folder[0] == '\0') + return FALSE; + + strcpy(path, folder); + strcat(path, file_name); + return file_exists(path); +} + +boolean +file_exists(const char *path) +{ + struct stat sb; + + /* Just see if it's there */ + if (stat(path, &sb)) { + return FALSE; + } + return TRUE; +} + +void +set_default_prefix_locations(const char *programPath UNUSED) +{ + static char executable_path[MAX_PATH]; + static char profile_path[MAX_PATH]; + static char versioned_profile_path[MAX_PATH]; + static char versioned_user_data_path[MAX_PATH]; + static char versioned_global_data_path[MAX_PATH]; + /* static char versioninfo[20] UNUSED; */ + + strcpy(executable_path, get_executable_path()); + append_slash(executable_path); + +#ifndef SFCTOOL + if (test_portable_config(executable_path, portable_device_path, + sizeof portable_device_path)) { + gf.fqn_prefix[SYSCONFPREFIX] = executable_path; + gf.fqn_prefix[CONFIGPREFIX] = portable_device_path; + gf.fqn_prefix[HACKPREFIX] = portable_device_path; + gf.fqn_prefix[SAVEPREFIX] = portable_device_path; + gf.fqn_prefix[LEVELPREFIX] = portable_device_path; + gf.fqn_prefix[BONESPREFIX] = portable_device_path; + gf.fqn_prefix[SCOREPREFIX] = portable_device_path; + gf.fqn_prefix[LOCKPREFIX] = portable_device_path; + gf.fqn_prefix[TROUBLEPREFIX] = portable_device_path; + gf.fqn_prefix[DATAPREFIX] = executable_path; + } else { +#endif /* SFCTOOL */ + if (!build_known_folder_path(&FOLDERID_Profile, profile_path, + sizeof(profile_path), FALSE)) + strcpy(profile_path, executable_path); + + if (!build_known_folder_path(&FOLDERID_Profile, + versioned_profile_path, + sizeof(profile_path), TRUE)) + strcpy(versioned_profile_path, executable_path); + + if (!build_known_folder_path(&FOLDERID_LocalAppData, + versioned_user_data_path, + sizeof(versioned_user_data_path), TRUE)) + strcpy(versioned_user_data_path, executable_path); + + if (!build_known_folder_path( + &FOLDERID_ProgramData, versioned_global_data_path, + sizeof(versioned_global_data_path), TRUE)) + strcpy(versioned_global_data_path, executable_path); + + gf.fqn_prefix[SYSCONFPREFIX] = versioned_global_data_path; + gf.fqn_prefix[CONFIGPREFIX] = profile_path; + gf.fqn_prefix[HACKPREFIX] = versioned_profile_path; + gf.fqn_prefix[SAVEPREFIX] = versioned_user_data_path; + gf.fqn_prefix[LEVELPREFIX] = versioned_user_data_path; + gf.fqn_prefix[BONESPREFIX] = versioned_global_data_path; + gf.fqn_prefix[SCOREPREFIX] = versioned_global_data_path; + gf.fqn_prefix[LOCKPREFIX] = versioned_global_data_path; + gf.fqn_prefix[TROUBLEPREFIX] = versioned_profile_path; + gf.fqn_prefix[DATAPREFIX] = executable_path; +#ifndef SFCTOOL + } +#endif /* SFCTOOL */ +} + +/* + * Add a backslash to any name not ending in /, \ or : There must + * be room for the \ + */ +void +append_slash(char *name) +{ + char *ptr; + + if (!*name) + return; + ptr = name + (strlen(name) - 1); + if (*ptr != '\\' && *ptr != '/' && *ptr != ':') { + *++ptr = '\\'; + *++ptr = '\0'; + } + return; +} + +void set_default_prefix_locations(const char *programPath); +boolean +test_portable_config(const char *executable_path, char *portable_device_path, + size_t portable_device_path_size) +{ + int lth = 0; + const char *sysconf = "sysconf"; + char tmppath[MAX_PATH]; + boolean retval = FALSE, + save_initoptions_noterminate = iflags.initoptions_noterminate; + + if (portable_device_path + && folder_file_exists(executable_path, "sysconf")) { + /* + There is a sysconf file (not just sysconf.template) present in + the exe path, which is not the way NetHack is initially + distributed, so assume it means that the admin/installer wants to + override something, perhaps set up for a fully-portable + configuration that leaves no traces behind elsewhere on this + computer's hard drive - delve into that... + */ + + *portable_device_path = '\0'; + lth = sizeof tmppath - strlen(sysconf); + (void) strncpy(tmppath, executable_path, lth - 1); + tmppath[lth - 1] = '\0'; + (void) strcat(tmppath, sysconf); + + iflags.initoptions_noterminate = 1; + /* assure_syscf_file(); */ + config_error_init(TRUE, tmppath, FALSE); + /* ... and _must_ parse correctly. */ + if (read_config_file(tmppath, set_in_sysconf) + && sysopt.portable_device_paths) + retval = TRUE; + (void) config_error_done(); + iflags.initoptions_noterminate = save_initoptions_noterminate; + sysopt_release(); /* the real sysconf processing comes later */ + } + if (retval) { + lth = strlen(executable_path); + if (lth <= (int) portable_device_path_size - 1) + Strcpy(portable_device_path, executable_path); + else + retval = FALSE; + } + return retval; +} + +const char * +get_portable_device(void) +{ + return (const char *) portable_device_path; +} + +#ifndef SFCTOOL /* Windows helpers for CRASHREPORT etc */ #ifdef CRASHREPORT struct CRctxt { @@ -785,11 +1165,11 @@ struct CRctxt { } ctxp_ = { NULL, NULL, NULL, 0, 0, 0, NULL, 0 }; struct CRctxt *ctxp = &ctxp_; // XXX should this now be in gc.* ? -#define win32err(fn) errname = fn; goto error +#define win32err(fn) errname = (char *) fn; goto error int -win32_cr_helper(char cmd, struct CRctxt *ctxp, void *p, int d){ - char *errname = "unknown"; +win32_cr_helper(char cmd, struct CRctxt *contextp, void *p, int d){ + char *errname = (char *) "unknown"; switch (cmd) { default: /* Not panic - we don't want to upgrade an impossible to a @@ -804,77 +1184,77 @@ win32_cr_helper(char cmd, struct CRctxt *ctxp, void *p, int d){ return MessageBoxW(NULL, lbidstr, L"bidshow", MB_SETFOREGROUND); } break; - case 'i': /* HASH_INIT(ctxp) */ + case 'i': /* HASH_INIT(contextp) */ if (!IsWindowsVistaOrGreater()) return 1; // CNG not available. - ctxp->bah = NULL; - ctxp->bhh = NULL; - ctxp->pbhashobj = NULL; - ctxp->cbhashobj = 0; - ctxp->cbhash = 0; - ctxp->cbdata = 0; - ctxp->pbhash = NULL; - ctxp->st = 0; + contextp->bah = NULL; + contextp->bhh = NULL; + contextp->pbhashobj = NULL; + contextp->cbhashobj = 0; + contextp->cbhash = 0; + contextp->cbdata = 0; + contextp->pbhash = NULL; + contextp->st = 0; // win32err("test"); // TESTING - FAKE AN ERROR - if (0 > (ctxp->st = BCryptOpenAlgorithmProvider( - &ctxp->bah, BCRYPT_MD4_ALGORITHM, NULL, 0))) { + if (0 > (contextp->st = BCryptOpenAlgorithmProvider( + &contextp->bah, BCRYPT_MD4_ALGORITHM, NULL, 0))) { win32err("BCryptOpenAlgorithmProvider"); }; - if (0 > (ctxp->st = - BCryptGetProperty(ctxp->bah, BCRYPT_OBJECT_LENGTH, - (unsigned char *) &ctxp->cbhashobj, - sizeof(DWORD), &ctxp->cbdata, 0))) { + if (0 > (contextp->st = + BCryptGetProperty(contextp->bah, BCRYPT_OBJECT_LENGTH, + (unsigned char *) &contextp->cbhashobj, + sizeof(DWORD), &contextp->cbdata, 0))) { win32err("BCryptGetProperty1"); }; if (0 - == (ctxp->pbhashobj = - HeapAlloc(GetProcessHeap(), 0, ctxp->cbhashobj))) { + == (contextp->pbhashobj = + HeapAlloc(GetProcessHeap(), 0, contextp->cbhashobj))) { win32err("HeapAlloc1"); }; - if (0 > (ctxp->st = BCryptGetProperty( - ctxp->bah, BCRYPT_HASH_LENGTH, (PBYTE) &ctxp->cbhash, - sizeof(DWORD), &ctxp->cbdata, 0))) { + if (0 > (contextp->st = BCryptGetProperty( + contextp->bah, BCRYPT_HASH_LENGTH, (PBYTE) &contextp->cbhash, + sizeof(DWORD), &contextp->cbdata, 0))) { win32err("BCryptGetProperty2"); } if (0 - == (ctxp->pbhash = - HeapAlloc(GetProcessHeap(), 0, ctxp->cbhash))) { + == (contextp->pbhash = + HeapAlloc(GetProcessHeap(), 0, contextp->cbhash))) { win32err("HeapAlloc2\n"); } - if (0 > BCryptCreateHash(ctxp->bah, &ctxp->bhh, ctxp->pbhashobj, - ctxp->cbhashobj, NULL, 0, 0)) { + if (0 > BCryptCreateHash(contextp->bah, &contextp->bhh, contextp->pbhashobj, + contextp->cbhashobj, NULL, 0, 0)) { win32err("BCryptCreateHash"); } break; - case 'u': /* HASH_UPDATE(ctxp, ptr, len) */ - if (0 > (ctxp->st = BCryptHashData(ctxp->bhh, p, d, 0))) { + case 'u': /* HASH_UPDATE(contextp, ptr, len) */ + if (0 > (contextp->st = BCryptHashData(contextp->bhh, p, d, 0))) { win32err("BCryptHashData"); } break; - case 'f': /* HASH_FINISH(ctxp) */ - if (0 > BCryptFinishHash(ctxp->bhh, ctxp->pbhash, ctxp->cbhash, 0)) { + case 'f': /* HASH_FINISH(contextp) */ + if (0 > BCryptFinishHash(contextp->bhh, contextp->pbhash, contextp->cbhash, 0)) { win32err("BCryptFinishHash"); } break; - case 'c': /* HASH_CLEANUP(ctxp) */ - if (ctxp->bah) { - BCryptCloseAlgorithmProvider(ctxp->bah, 0); + case 'c': /* HASH_CLEANUP(contextp) */ + if (contextp->bah) { + BCryptCloseAlgorithmProvider(contextp->bah, 0); } - if (ctxp->bhh) { - BCryptDestroyHash(ctxp->bhh); + if (contextp->bhh) { + BCryptDestroyHash(contextp->bhh); } - if (ctxp->pbhashobj) { - HeapFree(GetProcessHeap(), 0, ctxp->pbhashobj); + if (contextp->pbhashobj) { + HeapFree(GetProcessHeap(), 0, contextp->pbhashobj); } - if (ctxp->pbhash) { - HeapFree(GetProcessHeap(), 0, ctxp->pbhash); + if (contextp->pbhash) { + HeapFree(GetProcessHeap(), 0, contextp->pbhash); } break; - case 's': /* HASH_RESULT_SIZE(ctxp) */ - return ctxp->cbhash; - case 'r': /* HASH_RESULT(ctxp, resp) */ - *(unsigned char **)p = ctxp->pbhash; + case 's': /* HASH_RESULT_SIZE(contextp) */ + return contextp->cbhash; + case 'r': /* HASH_RESULT(contextp, resp) */ + *(unsigned char **)p = contextp->pbhash; break; case 'b': /* HASH_BINFILE(NULL,&binfile,0) */ // XXX This buffer should be allocated, not static (and freed in @@ -895,7 +1275,7 @@ win32_cr_helper(char cmd, struct CRctxt *ctxp, void *p, int d){ return 0; /* ok */ error: raw_printf("WIN32 function %s failed: status=%" PRIx64 "\n", - errname, (uint64)ctxp->st); + errname, (uint64)contextp->st); return 1; /* fail */ } #undef win32err @@ -949,8 +1329,17 @@ printf("E2: M=%s e=%d\n",msg,errnum); } #endif +#ifdef USE_BACKTRACE +#define USED_IF_BACKTRACE +#else +#define USED_IF_BACKTRACE UNUSED +#endif + int -win32_cr_gettrace(int maxframes, char *out, int outsize){ +win32_cr_gettrace(int maxframes USED_IF_BACKTRACE, + char *out USED_IF_BACKTRACE, + int outsize USED_IF_BACKTRACE) +{ #ifdef USE_BACKTRACE userstate.error_count = 0; userstate.good_count = 0; @@ -1053,6 +1442,7 @@ win32_cr_shellexecute(const char *url){ return rv; } #endif /* CRASHREPORT */ +#endif /* SFCTOOL */ #endif /* WIN32 */ diff --git a/sys/windows/winos.h b/sys/windows/winos.h index 132b153e0..aed062035 100644 --- a/sys/windows/winos.h +++ b/sys/windows/winos.h @@ -7,6 +7,10 @@ #include "win32api.h" +#ifndef PATH_SEPARATOR +#define PATH_SEPARATOR '\\' +#endif + extern const WCHAR cp437[256]; WCHAR * diff --git a/test/README.md b/test/README.md new file mode 100644 index 000000000..68f41b1ca --- /dev/null +++ b/test/README.md @@ -0,0 +1,7 @@ +### how to use + + * compile NetHack without DLB + * install + * copy the test lua files into the nethack playground dir + * start nethack in wizmode + * use wizloadlua extended command to load and run one of the test files. diff --git a/util/.gitignore b/util/.gitignore index 6852dfc87..a16d5c640 100644 --- a/util/.gitignore +++ b/util/.gitignore @@ -11,6 +11,7 @@ lev_comp dlb dlb_main recover +stripbs tilemap tileedit tile2x11 @@ -29,4 +30,8 @@ heaputil heaputil.c tilemappings.lst clang_rt.asan*.dll - +*.sln +sf.tags +sfdata.c +sfctool +sftags diff --git a/util/dlb_main.c b/util/dlb_main.c index 57ea1c78a..1926b871d 100644 --- a/util/dlb_main.c +++ b/util/dlb_main.c @@ -18,7 +18,7 @@ ATTRNORETURN static void xexit(int) NORETURN; ATTRNORETURN extern void panic(const char *, ...) NORETURN; -FILE *fopen_datafile(const char *, const char *); +FILE *fopen_datafile(const char *, const char *, int); #ifdef DLB #ifdef DLBLIB @@ -67,14 +67,14 @@ static char origdir[255] = ""; * * dlb COMMANDoptions arg... files... * commands: - * dlb x extract all files - * dlb c build the archive - * dlb t list the archive + * dlb x extract all files + * dlb c build the archive + * dlb t list the archive * options: - * v verbose - * f file specify archive file (default DLBFILE) - * I file specify file for list of files (default LIBLISTFILE) - * C dir chdir to dir (used ONCE, not like tar's -C) + * v verbose + * f file specify archive file (default DLBFILE) + * I file specify file for list of files (default LIBLISTFILE) + * C dir chdir to dir (used ONCE, not like tar's -C) */ ATTRNORETURN static void @@ -134,7 +134,7 @@ Write(int out, char *buf, long len) /* open_library(dlb.c) needs this (which normally comes from src/files.c) */ FILE * -fopen_datafile(const char *filename, const char *mode) +fopen_datafile(const char *filename, const char *mode, int prefix UNUSED) { return fopen(filename, mode); } diff --git a/util/makedefs.c b/util/makedefs.c index 6dfb582f2..bc86614bd 100644 --- a/util/makedefs.c +++ b/util/makedefs.c @@ -339,7 +339,7 @@ do_makedefs(char *options) rafile(*options); break; #if defined(OLD_MAKEDEFS_OPTIONS) - case 'o': + case 'o': case 'O': do_objs(); break; @@ -361,7 +361,7 @@ do_makedefs(char *options) do_questtxt(); break; #else - case 'o': case 'O': case 'e': case 'E': case 'v': case 'V': + case 'o': case 'O': case 'e': case 'E': case 'v': case 'V': case 'p': case 'P': case 'q': case 'Q': Fprintf(stderr, "Old makedefs option.\n" "Rebuild makedefs with '-DOLD_MAKEDEFS_OPTIONS'" @@ -393,7 +393,7 @@ oldfunctionality(char sought) char ucoflet; const char *ofnam; } ofn[] = { - { 'e', 'E', DGN_O_FILE }, + { 'e', 'E', DGN_O_FILE }, { 'o', 'O', ONAME_FILE }, { 'p', 'P', MONST_FILE }, { 'q', 'Q', QTXT_O_FILE }, @@ -661,15 +661,15 @@ do_ext_makedefs(int argc, char **argv) } CONTINUE; } - IS_OPTION("grep-defined"){ - struct grep_var *p; + IS_OPTION("grep-defined"){ + struct grep_var *p; - CONSUME; - p = grepsearch(argv[0]); - // NB: Exit status is ready for the shell: - // 0=defined, 1=not defined - makedefs_exit(!(p && p->is_defined)); - } + CONSUME; + p = grepsearch(argv[0]); + // NB: Exit status is ready for the shell: + // 0=defined, 1=not defined + makedefs_exit(!(p && p->is_defined)); + } #ifdef notyet IS_OPTION("help") { } @@ -846,7 +846,8 @@ do_grep_control(char *buf) break; case '!': /* if not ID */ isif = 0; - /* FALLTHROUGH */ + FALLTHROUGH; + /* FALLTHRU */ case '?': /* if ID */ if (grep_sp == GREP_STACK_SIZE - 2) { Fprintf(stderr, "stack overflow at line %d.", grep_lineno); @@ -1868,18 +1869,6 @@ do_date(void) } Fprintf(ofp, "#define VERSION_SANITY1 0x%08lx%s\n", version.entity_count, ul_sfx); -#ifndef __EMSCRIPTEN__ - Fprintf(ofp, "#define VERSION_SANITY2 0x%08lx%s\n", version.struct_sizes1, - ul_sfx); - Fprintf(ofp, "#define VERSION_SANITY3 0x%08lx%s\n", version.struct_sizes2, - ul_sfx); -#else /* __EMSCRIPTEN__ */ - Fprintf(ofp, "#define VERSION_SANITY2 0x%08llx%s\n", version.struct_sizes1, - ul_sfx); - Fprintf(ofp, "#define VERSION_SANITY3 0x%08llx%s\n", version.struct_sizes2, - ul_sfx); -#endif /* !__EMSCRIPTEN__ */ - Fprintf(ofp, "\n"); Fprintf(ofp, "#define VERSION_STRING \"%s\"\n", mdlib_version_string(buf, ".")); @@ -2302,6 +2291,7 @@ do_objs(void) n_glass_gems++; break; } + FALLTHROUGH; /*FALLTHRU*/ case VENOM_CLASS: /* fall-through from gem class is ok; objects[] used to have @@ -2311,6 +2301,7 @@ do_objs(void) so strip the extra "splash of " off to keep same macros */ if (!strncmp(objnam, "SPLASH_OF_", 10)) objnam += 10; + FALLTHROUGH; /*FALLTHRU*/ default: Fprintf(ofp, "#define\t"); diff --git a/util/recover.c b/util/recover.c index 08d8258c9..508263675 100644 --- a/util/recover.c +++ b/util/recover.c @@ -1,5 +1,5 @@ /* NetHack 3.7 recover.c $NHDT-Date: 1687547437 2023/06/23 19:10:37 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.33 $ */ -/* Copyright (c) Janet Walz, 1992. */ +/* Copyright (c) Janet Walz, 1992. */ /* NetHack may be freely redistributed. See license for details. */ /* @@ -12,7 +12,16 @@ #include "win32api.h" #endif +#define RECOVER_C + #include "config.h" +#include "hacklib.h" + +#include "artifact.h" +#include "rect.h" +#include "dungeon.h" +#include "hack.h" + #if !defined(O_WRONLY) && !defined(LSC) && !defined(AZTEC_C) #include #endif @@ -26,12 +35,15 @@ extern int vms_open(const char *, int, unsigned); #define nhUse(arg) (void)(arg) #endif +/* copy_bytes() has been moved to hacklib */ + int restore_savefile(char *); void set_levelfile_name(int); int open_levelfile(int); int create_savefile(void); -void copy_bytes(int, int); -static void store_formatindicator(int); + +extern int get_critical_size_count(void); +extern uchar cscbuf[]; #ifndef WIN_CE #define Fprintf (void) fprintf @@ -194,37 +206,19 @@ create_savefile(void) return fd; } -void -copy_bytes(int ifd, int ofd) -{ - char buf[BUFSIZ]; - int nfrom, nto; - - do { - nfrom = read(ifd, buf, BUFSIZ); - nto = write(ofd, buf, nfrom); - if (nto != nfrom) { - Fprintf(stderr, "file copy failed!\n"); - exit(EXIT_FAILURE); - } - } while (nfrom == BUFSIZ); -} - int restore_savefile(char *basename) { int gfd, lfd, sfd; - int res = 0, lev, savelev, hpid, pltmpsiz, filecmc; + int res = 0, lev, savelev, hpid, pltmpsiz; xint8 levc; struct version_info version_data; - struct savefile_info sfi; - char plbuf[PL_NSIZ], indicator; + char plbuf[PL_NSIZ_PLUS], indicator, cscsize; /* level 0 file contains: * pid of creating process (ignored here) * level number for current level of save file * name of save file nethack would have created - * savefile info * player name * and game state */ @@ -265,13 +259,14 @@ restore_savefile(char *basename) != sizeof savename) || (read(gfd, (genericptr_t) &indicator, sizeof indicator) != sizeof indicator) - || (read(gfd, (genericptr_t) &filecmc, sizeof filecmc) - != sizeof filecmc) + || (read(gfd, (genericptr_t) &cscsize, sizeof cscsize) + != sizeof cscsize) + || (read(gfd, (genericptr_t) &cscbuf, cscsize) + != cscsize) || (read(gfd, (genericptr_t) &version_data, sizeof version_data) != sizeof version_data) - || (read(gfd, (genericptr_t) &sfi, sizeof sfi) != sizeof sfi) || (read(gfd, (genericptr_t) &pltmpsiz, sizeof pltmpsiz) - != sizeof pltmpsiz) || (pltmpsiz > PL_NSIZ) + != sizeof pltmpsiz) || (pltmpsiz > PL_NSIZ_PLUS) || (read(gfd, (genericptr_t) plbuf, pltmpsiz) != pltmpsiz)) { Fprintf(stderr, "Error reading %s -- can't recover.\n", lock); Close(gfd); @@ -279,10 +274,12 @@ restore_savefile(char *basename) } /* save file should contain: - * format indicator and cmc + * format indicator (1 byte) + * n = count of critical size list (1 byte) + * n bytes of critical sizes (n bytes) * version info - * savefile info - * player name + * plnametmp = player name size (int, 2 bytes) + * player name (PL_NSIZ_PLUS) * current level (including pets) * (non-level-based) game state * other levels @@ -302,20 +299,35 @@ restore_savefile(char *basename) return -1; } - store_formatindicator(sfd); - if (write(sfd, (genericptr_t) &version_data, sizeof version_data) - != sizeof version_data) { - Fprintf(stderr, "Error writing %s; recovery failed.\n", savename); + if (write(sfd, (genericptr_t) &indicator, sizeof indicator) + != sizeof indicator) { + Fprintf(stderr, "Error writing %s %s; recovery failed.\n", + savename, "indicator"); Close(gfd); Close(sfd); Close(lfd); return -1; } - - if (write(sfd, (genericptr_t) &sfi, sizeof sfi) != sizeof sfi) { - Fprintf(stderr, - "Error writing %s; recovery failed (savefile_info).\n", - savename); + if (write(sfd, (genericptr_t) &cscsize, sizeof cscsize) != sizeof cscsize) { + Fprintf(stderr, "Error writing %s %s; recovery failed.\n", + savename, "cscsize"); + Close(gfd); + Close(sfd); + Close(lfd); + return -1; + } + if (write(sfd, (genericptr_t) &cscbuf, cscsize) != cscsize) { + Fprintf(stderr, "Error writing %s %s; recovery failed.\n", + savename, "critical bytes"); + Close(gfd); + Close(sfd); + Close(lfd); + return -1; + } + if (write(sfd, (genericptr_t) &version_data, sizeof version_data) + != sizeof version_data) { + Fprintf(stderr, "Error writing %s %s; recovery failed.\n", + savename, "version_data"); Close(gfd); Close(sfd); Close(lfd); @@ -333,6 +345,7 @@ restore_savefile(char *basename) return -1; } + assert((size_t) pltmpsiz <= sizeof plbuf); if (write(sfd, (genericptr_t) plbuf, pltmpsiz) != pltmpsiz) { Fprintf(stderr, "Error writing %s; recovery failed (player name).\n", savename); @@ -342,11 +355,17 @@ restore_savefile(char *basename) return -1; } - copy_bytes(lfd, sfd); + if (!copy_bytes(lfd, sfd)) { + Fprintf(stderr, "file copy failed!\n"); + exit(EXIT_FAILURE); + } Close(lfd); (void) unlink(lock); - copy_bytes(gfd, sfd); + if (!copy_bytes(gfd, sfd)) { + Fprintf(stderr, "file copy failed!\n"); + exit(EXIT_FAILURE); + } Close(gfd); set_levelfile_name(0); (void) unlink(lock); @@ -361,10 +380,14 @@ restore_savefile(char *basename) /* any or all of these may not exist */ levc = (xint8) lev; if (write(sfd, (genericptr_t) &levc, sizeof levc) - != sizeof levc) + != sizeof levc) { res = -1; - else - copy_bytes(lfd, sfd); + } else { + if (!copy_bytes(lfd, sfd)) { + Fprintf(stderr, "file copy failed!\n"); + exit(EXIT_FAILURE); + } + } Close(lfd); (void) unlink(lock); } @@ -386,7 +409,10 @@ restore_savefile(char *basename) in = open("NetHack:default.icon", O_RDONLY); out = open(iconfile, O_WRONLY | O_TRUNC | O_CREAT); if (in > -1 && out > -1) { - copy_bytes(in, out); + if (!copy_bytes(in, out)) { + Fprintf(stderr, "file copy failed!\n"); + exit(EXIT_FAILURE); + } } if (in > -1) close(in); @@ -398,18 +424,6 @@ restore_savefile(char *basename) return res; } -static void -store_formatindicator(int fd) -{ - char indicate = 'h'; /* historical */ - int cmc = 0; - - write(fd, (genericptr_t) &indicate, sizeof indicate); - write(fd, (genericptr_t) &cmc, sizeof cmc); -} - - - #ifdef EXEPATH #ifdef __DJGPP__ #define PATH_SEPARATOR '/' diff --git a/util/sfctool.c b/util/sfctool.c new file mode 100644 index 000000000..8bfc3ccde --- /dev/null +++ b/util/sfctool.c @@ -0,0 +1,1280 @@ +/* NetHack 3.7 sfctool.c */ +/* Copyright (c) Michael Allison, 2025. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Utility for reading a binary save file in the native historical + * format of the accompanying NetHack executable, and writing it out + * in an export format, possibly destined for a different platform, + * architecture, or data model. + * + * The resulting export file will only be useful for transport + * between different platforms and architectures that share the exact + * same version of NetHack with the same features. That is, the same + * fields must be present in the NetHack data structures, in the same + * sequence. The fields do not have to be the same size or use the same + * data model. + * + */ + +#if defined(WIN32) && !defined(__GNUC__) +#include "win32api.h" +#endif + +#include "hack.h" + +#include +#include +#include +#ifdef UNIX +#include +#define O_BINARY 0 +#endif +#include "integer.h" +#include "sfprocs.h" + +#ifdef WIN32 +#include +#endif + +#ifndef RDTMODE +#define RDTMODE "r" +#endif +#ifndef WRTMODE +#if (defined(MSDOS) || defined(WIN32)) +#define WRTMODE "w+b" +#else +#define WRTMODE "w+" +#endif +#endif +#ifndef RDBMODE +#if (defined(MICRO) || defined(WIN32)) +#define RDBMODE "rb" +#else +#define RDBMODE "r" +#endif +#endif +#ifndef WRBMODE +#if (defined(MICRO) || defined(WIN32)) +#define WRBMODE "w+b" +#else +#define WRBMODE "w+" +#endif +#endif + +#ifdef PANICTRACE +#error PANICTRACE is defined +#endif +#ifdef CRASHREPORT +#error CRASHREPORT is defined +#endif + +/* functions in this file */ +static int process_savefile(const char *, enum saveformats, char *, enum saveformats, boolean); +static void my_sf_init(void); +static NHFILE *open_srcfile(const char *, enum saveformats); +static NHFILE *create_dstfile(char *, enum saveformats); +static const char *style_to_text(enum saveformats style); +static void read_sysconf(void); +static int length_without_val(const char *user_string, int len); +static void usage(int argc, char **argv); +static const char *briefname(const char *fnam); + +extern void init_nhfile(NHFILE *); /* files.c */ +extern NHFILE *new_nhfile(void); /* files.c */ +extern void free_nhfile(NHFILE *); /* files.c */ + +/* + * This is a replacement tempered down version of same-named + * function in files.c within #ifndef SFCTOOL blocks. + */ +int delete_savefile(void); + +// int nhclose(int fd); +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 *); +#else +ATTRNORETURN extern void nethack_exit(int) NORETURN; +#ifdef WIN32 +boolean get_user_home_folder(char *homebuf, size_t sz); +int GUILaunched; +#endif /* WIN32 */ +#endif +#define Fprintf (void) fprintf + +/* Global data */ + +struct link_compat1 { + volatile int done_hup; +}; + +/* worm segment structure */ +struct wseg { + struct wseg *nseg; + coordxy wx, wy; /* the segment's position */ +}; + +enum { UNCONVERTED = 0, CONVERTED }; +char *unconverted_filename = 0; +char *converted_filename = 0; + +/* from sfstruct.c */ +extern struct restore_info restoreinfo; +/* from sfbase.c */ +extern struct sf_structlevel_procs sfoprocs[NUM_SAVEFORMATS], sfiprocs[NUM_SAVEFORMATS]; +extern struct sf_structlevel_procs zerosfoprocs, zerosfiprocs; +extern struct sf_fieldlevel_procs sfoflprocs[NUM_SAVEFORMATS], sfiflprocs[NUM_SAVEFORMATS]; +extern struct sf_structlevel_procs zerosfodlprocs, zerosfidlprocs; +extern boolean close_check(int); +extern void bclose(int); +extern void config_error_init(boolean, const char *, boolean); /* files.c */ +extern boolean get_user_home_folder(char *, size_t); +extern void make_version(void); + +char plname[PL_NSIZ_PLUS]; +struct version_info vers_info; +int renidx = -1; + +const char *const rensuffixes[] = { + "IL32LLP64", /* (3) Windows x64 savefile on x86 */ + "I32LP64", /* (4) Unix 64 savefile on x86 */ + "ILP32LL64", /* (5) x86 savefile on Unix 64 */ + "ILP32LL64", /* (6) x86 savefile on Windows x64 */ + "I32LP64", /* (7) Unix 64 savefile on Windows x64 */ + "IL32LLP64", /* (8) Windows x64 savefile on Unix 64 */ + "OTHER", /* (9) */ +}; + +#ifdef WIN32 +extern boolean get_user_home_folder(char *homebuf, size_t sz); /* files.c */ +extern void set_default_prefix_locations(const char *programPath); +#endif +enum saveformats convertstyle = exportascii; + +boolean chosen_unconvert = FALSE, explicit_option = FALSE; +const char *thisdatamodel; +static char srclogfilenm[BUFSZ], dstlogfilenm[BUFSZ]; + +/********* + * main * + *********/ + +int +main(int argc, char *argv[]) +{ + int arg; + char folderbuf[5000]; + const char *suffix = (convertstyle == exportascii) ? ".exportascii" : ""; + boolean add_folder = TRUE, add_extension = FALSE; + +#ifdef WIN32 + const char *default_extension = ".NetHack-saved-game"; + size_t sz; +#else + const char *default_extension = ""; +#endif + + runtime_info_init(); /* mdlib.c */ +#ifdef UNIX + folderbuf[0] = '.'; + folderbuf[1] = '/'; + folderbuf[2] = '\0'; +#ifdef CHDIR + chdirx(HACKDIR); +#endif +#endif +#ifdef UNIX + Strcpy(folderbuf, "save/"); +#endif +#ifdef WIN32 + if (!get_user_home_folder(folderbuf, sizeof folderbuf)) + exit(EXIT_FAILURE); + sz = strlen(folderbuf); + (void) snprintf(eos(folderbuf), sizeof folderbuf - sz, + "\\AppData\\Local\\NetHack\\3.7\\"); + // initoptions_init(); // This allows OPTIONS in syscf on Windows. + set_default_prefix_locations(argv[0]); +#endif + + read_sysconf(); + thisdatamodel = datamodel(0); + if (argc < 3 && !(argc == 2 && !strcmp(argv[1], "-d"))) { + usage(argc, argv); + exit(EXIT_FAILURE); + } + for (arg = 1; arg < argc; ++arg) { + if (arg == 1 && !strcmp(argv[arg], "-d")) { + fprintf( + stdout, + "\nThe historical savefile datamodel supported by this utility is %s (%s).\n", + thisdatamodel, datamodel(1)); + exit(EXIT_SUCCESS); + } + + if (arg == 1 && !strcmp(argv[arg], "-u")) { + explicit_option = TRUE; + chosen_unconvert = TRUE; + continue; + } + if (arg == 1 && !strcmp(argv[arg], "-c")) { + if (explicit_option && chosen_unconvert) { + fprintf(stderr, "\nsfctool error - conflicting options.\n"); + exit(EXIT_FAILURE); /* both -u and -c not allowed */ + } + explicit_option = TRUE; + chosen_unconvert = FALSE; + continue; + } + + if (arg == 2) { + size_t ln = strlen(argv[arg]); + boolean addseparator = FALSE; + + if (!contains_directory(argv[arg])) { + char finalchar = *(eos(folderbuf) - 1); + + if (!(finalchar == '\\' || finalchar == '/')) { + ln += 1; + addseparator = TRUE; + } + } else { + add_folder = FALSE; + } +#ifdef WIN32 + /* On Windows we allow specifying the savefile name without the extention + * in the arguments */ + if (strstr(argv[arg], default_extension) == 0) + add_extension = TRUE; +#endif + if (explicit_option) { + if (add_folder) + ln += strlen(folderbuf); + if (add_extension) + ln += strlen(default_extension); + unconverted_filename = (char *) alloc((int) ln + 1); + Snprintf(unconverted_filename, ln + 1, "%s%s%s%s", + add_folder ? folderbuf : "", + addseparator ? "/" : "", + argv[arg], + add_extension ? default_extension : ""); + ln += strlen(suffix); + converted_filename = (char *) alloc((int) ln + 1); + Snprintf(converted_filename, ln + 1, "%s%s", + unconverted_filename, suffix); + } else { + fprintf(stderr, "\nsfctool error - missing -c or -u before " + "save filename.\n"); + exit(EXIT_FAILURE); /* need both filenames */ + } + } + } + if (!converted_filename || !unconverted_filename) { + fprintf(stderr, "\nsfctool error - missing %sconverted file name.\n", + !converted_filename ? "" : "un"); + exit(EXIT_FAILURE); /* need both filenames */ + } + + my_sf_init(); + if (chosen_unconvert) { + process_savefile(converted_filename, convertstyle, + unconverted_filename, historical, chosen_unconvert); + } else { + process_savefile(unconverted_filename, historical, converted_filename, + convertstyle, chosen_unconvert); + } +} + +/* ======================================================================== */ +/* Process the src savefile and create the dst file */ +/* Return 1 for success, 0 for failure */ +/* ======================================================================== */ + +static int +process_savefile(const char *srcfnam, enum saveformats srcstyle, + char *dstfnam, enum saveformats cvtstyle, boolean unconvert) +{ + NHFILE *nhfp[2]; /* one for UNCONVERTED, one for CONVERTED */ + int srcidx = unconvert ? CONVERTED : UNCONVERTED, + dstidx = unconvert ? UNCONVERTED : CONVERTED, + sfstatus = 0, i; + char indicator, file_csc_count; + extern struct version_info vers_info; + extern uchar cscbuf[]; + /* nh_uncompress(fq_save); */ + const char *dmfile; + + if ((nhfp[srcidx] = open_srcfile(srcfnam, srcstyle)) == 0) + return 0; + sfstatus = validate(nhfp[srcidx], srcfnam, FALSE); + dmfile = what_datamodel_is_this(0, + cscbuf[1], /* short */ + cscbuf[2], /* int */ + cscbuf[3], /* long */ + cscbuf[4], /* long long */ + cscbuf[5]); /* ptr */ + if (sfstatus > SF_UPTODATE + && ((sfstatus <= SF_CRITICAL_BYTE_COUNT_MISMATCH) || !unconvert)) { + if (sfstatus == SF_OUTDATED) { + fprintf(stderr, + "The %s savefile is outdated with respect to this %d.%d.%d EDITLEVEL %ld " + "%s%s%s.\n", + briefname(srcfnam), + VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL, + (long) EDITLEVEL, + thisdatamodel ? thisdatamodel : "", + thisdatamodel ? " " : "", + "sfctool"); + return 0; + } else { + fprintf(stderr, + "The %s savefile %s%s%s not compatible with this %s%s %s utility.\n", + briefname(srcfnam), + dmfile ? "is a " : "", + dmfile ? dmfile : "", + dmfile ? " savefile, thus" : " is", + thisdatamodel ? thisdatamodel : "", + thisdatamodel ? " " : "", + "sfctool"); + return 0; + } + } + if (sfstatus >= SF_DM_IL32LLP64_ON_ILP32LL64) { + renidx = sfstatus - SF_DM_IL32LLP64_ON_ILP32LL64; + } else if (sfstatus == SF_UPTODATE) { + renidx = -2; + } + if ((nhfp[dstidx] = create_dstfile(dstfnam, cvtstyle)) == 0) { + close_nhfile(nhfp[dstidx]); + nh_compress(unconverted_filename); + return 0; + } + if (unconvert) { + nhfp[srcidx]->nhfpconvert = nhfp[UNCONVERTED]; + } else { + /* converting */ + nhfp[srcidx]->nhfpconvert = nhfp[CONVERTED]; + } + if (unconvert) + fprintf(stdout, "\n\nunconverting %s to %s savefile called %s.\n", + briefname((const char *) converted_filename), + dmfile, + briefname((const char *) unconverted_filename)); + else + fprintf(stdout, "\n\nconverting %s %s format %s to %s format\n", + style_to_text(srcstyle), + thisdatamodel, + briefname((const char *) unconverted_filename), + style_to_text(cvtstyle)); + + rewind_nhfile(nhfp[srcidx]); +#ifdef SAVEFILE_DEBUGGING + nhfp[srcidx]->fplog = fopen(srclogfilenm, "w"); +#endif + if (unconvert) { + nhfp[srcidx]->mode |= UNCONVERTING; + } else { + /* converting */ + nhfp[srcidx]->mode |= CONVERTING; + } + nhfp[srcidx]->rcount = 0; + Sfi_char(nhfp[srcidx], &indicator, "indicate-format", 1); + Sfi_char(nhfp[srcidx], &file_csc_count, "count-critical_sizes", 1); + for (i = 0; i < (int) file_csc_count; ++i) { + Sfi_uchar(nhfp[srcidx], &cscbuf[i], "critical_sizes"); + } + rewind_nhfile(nhfp[dstidx]); +#ifdef SAVEFILE_DEBUGGING + nhfp[dstidx]->fplog = fopen(dstlogfilenm, "w"); +#endif + /* + * store_critical_bytes() will take care of inserting the + * indicate-format, count-critical_sizes, and critical_sizes for + * this platform/data-model destination, instead of copying those + * values from the savefile that was converted. + */ + store_critical_bytes(nhfp[dstidx]); + + Sfi_version_info(nhfp[srcidx], &vers_info, "version_info"); + svm.moves = 1L; /* match u_init.c */ + + /******************** + * player name info * + ********************/ + + get_plname_from_file(nhfp[srcidx], plname, TRUE); + + { + /******************** + * lev 0 * + ********************/ + xint8 lev = 0; + + getlev(nhfp[srcidx], lev, FALSE); + } + + + { + /******************** + * gamestate * + ********************/ +/* unsigned int stuckid, steedid; */ + (void) restgamestate(nhfp[srcidx]); + } + + { + /******************** + * Do all levels * + ********************/ + xint8 ltmp; + + restoreinfo.mread_flags = 1; /* return despite error */ + while (1) { + ltmp = -1; + Sfi_xint8(nhfp[srcidx], <mp, "gamestate-level_number"); + if (nhfp[srcidx]->eof || ltmp == -1) + break; + + getlev(nhfp[srcidx], 0, ltmp); + } + restoreinfo.mread_flags = 0; + } + nhfp[srcidx]->mode &= ~(CONVERTING | UNCONVERTING); + nhfp[srcidx]->nhfpconvert = (NHFILE *) 0; + close_nhfile(nhfp[srcidx]); + close_nhfile(nhfp[dstidx]); + nh_compress(dstfnam); + nh_compress(srcfnam); + return 1; +} + +/* open srcfile for reading */ +static NHFILE * +open_srcfile(const char *fnam, enum saveformats mystyle) +{ + int fd; + const char *fq_name; + NHFILE *nhfp = (NHFILE *) 0; + + nhfp = new_nhfile(); + if (nhfp) { + nhfp->mode = READING; + nhfp->structlevel = (mystyle == historical); + nhfp->fieldlevel = (mystyle > historical); + nhfp->ftype = NHF_SAVEFILE; + nhfp->fnidx = mystyle; + nhfp->fd = -1; + nhfp->addinfo = + (nhfp->fieldlevel && (mystyle = exportascii)) + ? TRUE + : FALSE; + (void) snprintf(srclogfilenm, sizeof srclogfilenm, "srcfile.%s.log", + (mystyle == historical) ? "historical" : "exportascii"); + } + + fq_name = fqname(fnam, SAVEPREFIX, 0); + nh_uncompress(fq_name); + if (nhfp && nhfp->structlevel) { + fd = open(fq_name, O_RDONLY | O_BINARY, 0); + if (fd < 0) { + free_nhfile(nhfp); + fprintf(stderr, + "\nsfctool error - unable to open historical-style " + "source file %s.\n", + fnam); + nhfp = (NHFILE *) 0; + nh_compress(fq_name); + } else { + nhfp->fd = fd; +#if defined(MSDOS) + setmode(nhfp->fd, O_BINARY); +#endif + } + } + if (nhfp && nhfp->fieldlevel) { + /* char savenamebuf[BUFSZ]; */ + + nhfp->fpdef = fopen(fnam, RDBMODE); + if (!nhfp->fpdef) { + free_nhfile(nhfp); + fprintf(stderr, + "\nsfctool error - unable to open fieldlevel-style " + "source file %s.\n", + fnam); + nhfp = (NHFILE *) 0; + nh_compress(fq_name); + } + } + return nhfp; +} + +/* create dst file, overwriting one if it already exists */ +static NHFILE * +create_dstfile(char *fnam, enum saveformats mystyle) +{ + int fd, ret; + unsigned ln = 0; + FILE *cf; + NHFILE *nhfp = (NHFILE *) 0; + const char *fq_name; + char dstfnam[2048]; + char *dsttmp; + boolean dst_file_exists = FALSE, ren_file_exists = FALSE; + + nhUse(ret); + Snprintf(dstfnam, sizeof dstfnam, "%s", fnam); + if ((cf = fopen(dstfnam, RDBMODE)) != (FILE *) 0) { + dst_file_exists = TRUE; + (void) fclose(cf); + } + + if (dst_file_exists) { + if (chosen_unconvert) { + ln = strlen(fnam); + if (renidx >= 0) { + ln += strlen(rensuffixes[renidx]) + 1; /* +1 for '.' */ + } else if (renidx == -2) { + ln += strlen(thisdatamodel) + 1; /* +1 for '.' */ + } else { + ln += strlen(thisdatamodel) + 1 + + 4; /* +1 for '.'; +4 for "not_" */ + } + dsttmp = (char *) alloc(ln + 1); + Strcpy(dsttmp, fnam); + Strcat(dsttmp, "."); + if (renidx >= 0) { + Strcat(dsttmp, rensuffixes[renidx]); + } else if (renidx == -2) { + Strcat(dsttmp, thisdatamodel); + } else { + Strcat(dsttmp, "not_"); + Strcat(dsttmp, thisdatamodel); + } + if ((cf = fopen(dsttmp, RDBMODE)) != (FILE *) 0) { + ren_file_exists = TRUE; + (void) fclose(cf); + } + if (ren_file_exists) { + (void) unlink(dsttmp); + } + ret = rename(fnam, dsttmp); + free((genericptr_t) dsttmp), dsttmp = 0; + } else { + if ((cf = fopen(dstfnam, RDBMODE)) != (FILE *) 0) { + ren_file_exists = TRUE; + (void) fclose(cf); + } + if (ren_file_exists) { + (void) unlink(dstfnam); + } + } + } + + nhfp = new_nhfile(); + if (nhfp) { + nhfp->mode = WRITING; + nhfp->ftype = NHF_SAVEFILE; + nhfp->structlevel = (mystyle == historical); + nhfp->fieldlevel = (mystyle > historical); + nhfp->fnidx = mystyle; + nhfp->fd = -1; + (void) snprintf(dstlogfilenm, sizeof dstlogfilenm, "dstfile.%s.log", + (mystyle == historical) ? "historical" : "exportascii"); + } + + if (nhfp && nhfp->structlevel) { + fq_name = fqname(dstfnam, SAVEPREFIX, 0); +#if defined(MICRO) || defined(WIN32) + fd = open(dstfnam, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK); +#else + fd = creat(dstfnam, FCMASK); +#endif + if (fd < 0) { + free_nhfile(nhfp); + fprintf( + stderr, + "Unable to create historical-style destination file %s.\n", + fnam); + nhfp = (NHFILE *) 0; + } else { + nhfp->fd = fd; +#if defined(MSDOS) + setmode(nhfp->fd, O_BINARY); +#endif + } + } else if (nhfp && nhfp->fieldlevel) { + fq_name = fqname(dstfnam, SAVEPREFIX, 0); + nhfp->fpdef = fopen(fq_name, WRBMODE); + if (!nhfp->fpdef) { + free_nhfile(nhfp); + fprintf( + stderr, + "Unable to create fieldlevel-style destination file %s.\n", + fnam); + nhfp = (NHFILE *) 0; + } + } + return nhfp; +} + +static const char * +briefname(const char *fnam) +{ + const char *bn = fnam, *sep = (const char *) 0; + + if ((sep = strrchr(fnam, '/')) || (sep = strrchr(fnam, '\\')) + || (sep = strrchr(fnam, ':'))) + bn = sep + 1; + return bn; +} + +static const char * +style_to_text(enum saveformats style) +{ + const char *txt; + + switch (style) { + case historical: + txt = "historical"; + break; + case exportascii: + txt = "exportascii"; + break; + case invalid: + default: + txt = "invalid"; + break; + } + return txt; +} + +void +my_sf_init(void) +{ + decl_globals_init(); + sfoprocs[invalid] = zerosfoprocs; + sfiprocs[invalid] = zerosfiprocs; + sfoprocs[historical] = historical_sfo_procs; + sfiprocs[historical] = historical_sfi_procs; + sfoflprocs[exportascii] = exportascii_sfo_procs; + sfiflprocs[exportascii] = exportascii_sfi_procs; +} + +/* delete savefile */ +int +delete_savefile(void) +{ + return 0; /* for restore_saved_game() (ex-xxxmain.c) test */ +} + +static void +read_sysconf(void) +{ +#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); + 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 */ +} + +/* provided for linkage only */ + +DISABLE_WARNING_FORMAT_NONLITERAL + +void +raw_printf(const char *line, ...) +{ + va_list the_args; + + va_start(the_args, line); + vfprintf(stdout, line, the_args); + va_end(the_args); +} + +void +error(const char *s, ...) +{ + va_list the_args; + + va_start(the_args, s); + vprintf(s, the_args); + va_end(the_args); + exit(EXIT_FAILURE); +} + +void +pline(const char *s, ...) +{ + va_list the_args; + + va_start(the_args, s); + vprintf(s, the_args); + va_end(the_args); +} + +void +impossible(const char *s, ...) +{ + va_list the_args; + + va_start(the_args, s); + vprintf(s, the_args); + va_end(the_args); + exit(EXIT_FAILURE); +} + +RESTORE_WARNING_FORMAT_NONLITERAL + +#ifdef UNIX +/* normalize file name - we don't like .'s, /'s, spaces */ +void +regularize(char *s) +{ + register char *lp; + + while ((lp = strchr(s, '.')) != 0 || (lp = strchr(s, '/')) != 0 + || (lp = strchr(s, ' ')) != 0) + *lp = '_'; +#if defined(SYSV) && !defined(AIX_31) && !defined(SVR4) && !defined(LINUX) \ + && !defined(__APPLE__) +/* avoid problems with 14 character file name limit */ +#ifdef COMPRESS + /* leave room for .e from error and .Z from compress appended to + * save files */ + { +#ifdef COMPRESS_EXTENSION + int i = 12 - strlen(COMPRESS_EXTENSION); +#else + int i = 10; /* should never happen... */ +#endif + if (strlen(s) > i) + s[i] = '\0'; + } +#else + if (strlen(s) > 11) + /* leave room for .nn appended to level files */ + s[11] = '\0'; +#endif +#endif +} +#endif /* UNIX */ + +int +util_strncmpi(const char *s1, const char *s2, size_t sz) +{ + register char t1, t2; + + while (sz--) { + if (!*s2) + return (*s1 != 0); /* s1 >= s2 */ + else if (!*s1) + return -1; /* s1 < s2 */ + t1 = lowc(*s1++); + t2 = lowc(*s2++); + if (t1 != t2) + return (t1 > t2) ? 1 : -1; + } + return 0; /* s1 == s2 */ +} + +/* should be called with either EXIT_SUCCESS or EXIT_FAILURE */ +void +nh_terminate(int status) +{ + nethack_exit(status); +} + +#ifndef UNIX +void +nethack_exit(int code) +{ + exit(code); +} +#endif /* UNIX */ + +#ifdef UNIX +#ifdef CHDIR +static void +chdirx(const char *dir) +{ + if (dir) { +#ifdef SECURE + (void) setgid(getgid()); + (void) setuid(getuid()); /* Ron Wessels */ +#endif + } else { + /* non-default data files is a sign that scores may not be + * compatible, or perhaps that a binary not fitting this + * system's layout is being used. + */ +#ifdef VAR_PLAYGROUND + int len = strlen(VAR_PLAYGROUND); + + gf.fqn_prefix[SCOREPREFIX] = (char *) alloc(len + 2); + Strcpy(gf.fqn_prefix[SCOREPREFIX], VAR_PLAYGROUND); + if (gf.fqn_prefix[SCOREPREFIX][len - 1] != '/') { + gf.fqn_prefix[SCOREPREFIX][len] = '/'; + gf.fqn_prefix[SCOREPREFIX][len + 1] = '\0'; + } + +#endif + } + +#ifdef HACKDIR + if (dir == (const char *) 0) + dir = HACKDIR; +#endif + + if (dir && chdir(dir) < 0) { + perror(dir); + error("Cannot chdir to %s.", dir); + } +} +#endif /* CHDIR */ +#endif /* UNIX */ + +#ifdef WIN32 + +/* + * Strip out troublesome file system characters. + */ + +void nt_regularize(char* s) /* normalize file name */ +{ + unsigned char *lp; + + for (lp = (unsigned char *) s; *lp; lp++) + if (*lp == '?' || *lp == '"' || *lp == '\\' || *lp == '/' + || *lp == '>' || *lp == '<' || *lp == '*' || *lp == '|' + || *lp == ':' || (*lp > 127)) + *lp = '_'; +} +#endif /* WIN32 */ + +/* duplicated code from options.c */ +/* most environment variables will eventually be printed in an error + * message if they don't work, and most error message paths go through + * BUFSZ buffers, which could be overflowed by a maliciously long + * environment variable. If a variable can legitimately be long, or + * if it's put in a smaller buffer, the responsible code will have to + * bounds-check itself. + */ +char * +nh_getenv(const char *ev) +{ + char *getev = getenv(ev); + + if (getev && strlen(getev) <= (BUFSZ / 2)) + return getev; + else + return (char *) 0; +} + +void +done1(int sig_unused UNUSED) +{ +#ifndef NO_SIGNAL + (void) signal(SIGINT, SIG_IGN); +#endif + if (flags.ignintr) { +#ifndef NO_SIGNAL + (void) signal(SIGINT, (SIG_RET_TYPE) done1); +#endif + } +} + +/* + * I hate having to duplicate this code here, but it is much simpler to + * add these here than take steps to link with mkobj.c, do_name.c, priest.c, + * vault.c, shknam.c, minion.c, dog.c, etc. + */ + +/* allocate space for a monster's name; removes old name if there is one */ +void +new_mgivenname(struct monst *mon, + int lth) /* desired length (caller handles adding 1 + for terminator) */ +{ + if (lth) { + /* allocate mextra if necessary; otherwise get rid of old name */ + if (!mon->mextra) + mon->mextra = newmextra(); + else + free_mgivenname(mon); /* already has mextra, might also have name */ + MGIVENNAME(mon) = (char *) alloc((unsigned) lth); + } else { + /* zero length: the new name is empty; get rid of the old name */ + if (has_mgivenname(mon)) + free_mgivenname(mon); + } +} + +/* release a monster's name; retains mextra even if all fields are now null */ +void +free_mgivenname(struct monst *mon) +{ + if (has_mgivenname(mon)) { + free((genericptr_t) MGIVENNAME(mon)); + MGIVENNAME(mon) = (char *) 0; + } +} +void +newegd(struct monst *mtmp) +{ + if (!mtmp->mextra) + mtmp->mextra = newmextra(); + if (!EGD(mtmp)) { + EGD(mtmp) = (struct egd *) alloc(sizeof (struct egd)); + (void) memset((genericptr_t) EGD(mtmp), 0, sizeof (struct egd)); + } +} + +void +free_egd(struct monst *mtmp) +{ + if (mtmp->mextra && EGD(mtmp)) { + free((genericptr_t) EGD(mtmp)); + EGD(mtmp) = (struct egd *) 0; + } + mtmp->isgd = 0; +} +void +newepri(struct monst *mtmp) +{ + if (!mtmp->mextra) + mtmp->mextra = newmextra(); + if (!EPRI(mtmp)) { + EPRI(mtmp) = (struct epri *) alloc(sizeof(struct epri)); + (void) memset((genericptr_t) EPRI(mtmp), 0, sizeof(struct epri)); + } +} + +void +free_epri(struct monst *mtmp) +{ + if (mtmp->mextra && EPRI(mtmp)) { + free((genericptr_t) EPRI(mtmp)); + EPRI(mtmp) = (struct epri *) 0; + } + mtmp->ispriest = 0; +} +void +neweshk(struct monst* mtmp) +{ + if (!mtmp->mextra) + mtmp->mextra = newmextra(); + if (!ESHK(mtmp)) + ESHK(mtmp) = (struct eshk *) alloc(sizeof(struct eshk)); + (void) memset((genericptr_t) ESHK(mtmp), 0, sizeof(struct eshk)); + ESHK(mtmp)->bill_p = (struct bill_x *) 0; +} + +void +free_eshk(struct monst* mtmp) +{ + if (mtmp->mextra && ESHK(mtmp)) { + free((genericptr_t) ESHK(mtmp)); + ESHK(mtmp) = (struct eshk *) 0; + } + mtmp->isshk = 0; +} + +void +newemin(struct monst *mtmp) +{ + if (!mtmp->mextra) + mtmp->mextra = newmextra(); + if (!EMIN(mtmp)) { + EMIN(mtmp) = (struct emin *) alloc(sizeof(struct emin)); + (void) memset((genericptr_t) EMIN(mtmp), 0, sizeof(struct emin)); + } +} + +void +free_emin(struct monst *mtmp) +{ + if (mtmp->mextra && EMIN(mtmp)) { + free((genericptr_t) EMIN(mtmp)); + EMIN(mtmp) = (struct emin *) 0; + } + mtmp->isminion = 0; +} + +void +newedog(struct monst *mtmp) +{ + if (!mtmp->mextra) + mtmp->mextra = newmextra(); + if (!EDOG(mtmp)) { + EDOG(mtmp) = (struct edog *) alloc(sizeof(struct edog)); + (void) memset((genericptr_t) EDOG(mtmp), 0, sizeof(struct edog)); + } +} + +void +free_edog(struct monst *mtmp) +{ + if (mtmp->mextra && EDOG(mtmp)) { + free((genericptr_t) EDOG(mtmp)); + EDOG(mtmp) = (struct edog *) 0; + } + mtmp->mtame = 0; +} + +void +newebones(struct monst *mtmp) +{ + if (!mtmp->mextra) + mtmp->mextra = newmextra(); + if (!EBONES(mtmp)) { + EBONES(mtmp) = (struct ebones *) alloc(sizeof(struct ebones)); + (void) memset((genericptr_t) EBONES(mtmp), 0, sizeof(struct ebones)); + } +} + +void +free_ebones(struct monst *mtmp) +{ + if (mtmp->mextra && EBONES(mtmp)) { + free((genericptr_t) EBONES(mtmp)); + EBONES(mtmp) = (struct ebones *) 0; + } +} + +static const struct mextra zeromextra = DUMMY; + +static void +init_mextra(struct mextra *mex) +{ + *mex = zeromextra; + mex->mcorpsenm = NON_PM; +} + +struct mextra * +newmextra(void) +{ + struct mextra *mextra; + + mextra = (struct mextra *) alloc(sizeof(struct mextra)); + init_mextra(mextra); + return mextra; +} + +void +newomonst(struct obj* otmp) +{ + if (!otmp->oextra) + otmp->oextra = newoextra(); + + if (!OMONST(otmp)) { + struct monst *m = newmonst(); + + *m = cg.zeromonst; + OMONST(otmp) = m; + } +} + +void +free_omonst(struct obj* otmp) +{ + if (otmp->oextra) { + struct monst *m = OMONST(otmp); + + if (m) { + if (m->mextra) + dealloc_mextra(m); + free((genericptr_t) m); + OMONST(otmp) = (struct monst *) 0; + } + } +} + +void +newomid(struct obj* otmp) +{ + if (!otmp->oextra) + otmp->oextra = newoextra(); + OMID(otmp) = 0; +} + +void +free_omid(struct obj* otmp) +{ + OMID(otmp) = 0; +} + +void +new_omailcmd(struct obj* otmp, const char * response_cmd) +{ + if (!otmp->oextra) + otmp->oextra = newoextra(); + if (OMAILCMD(otmp)) + free_omailcmd(otmp); + OMAILCMD(otmp) = dupstr(response_cmd); +} + +void +free_omailcmd(struct obj* otmp) +{ + if (otmp->oextra && OMAILCMD(otmp)) { + free((genericptr_t) OMAILCMD(otmp)); + OMAILCMD(otmp) = (char *) 0; + } +} + +static const struct oextra zerooextra = DUMMY; + +static void +init_oextra(struct oextra* oex) +{ + *oex = zerooextra; +} + + +struct oextra * +newoextra(void) +{ + struct oextra *oextra; + + oextra = (struct oextra *) alloc(sizeof (struct oextra)); + init_oextra(oextra); + return oextra; +} + +void +dealloc_mextra(struct monst* m) +{ + struct mextra *x = m->mextra; + + if (x) { + if (x->mgivenname) + free((genericptr_t) x->mgivenname); + if (x->egd) + free((genericptr_t) x->egd); + if (x->epri) + free((genericptr_t) x->epri); + if (x->eshk) + free((genericptr_t) x->eshk); + if (x->emin) + free((genericptr_t) x->emin); + if (x->edog) + free((genericptr_t) x->edog); + if (x->ebones) + free((genericptr_t) x->ebones); + /* [no action needed for x->mcorpsenm] */ + + free((genericptr_t) x); + m->mextra = (struct mextra *) 0; + } +} + +void +dealloc_monst(struct monst* mon) +{ + if (mon->mextra) + dealloc_mextra(mon); + free((genericptr_t) mon); +} + +/* allocate space for an object's name; removes old name if there is one */ +void +new_oname(struct obj *obj, + int lth) /* desired length (caller handles adding 1 + for terminator) */ +{ + if (lth) { + /* allocate oextra if necessary; otherwise get rid of old name */ + if (!obj->oextra) + obj->oextra = newoextra(); + else + free_oname(obj); /* already has oextra, might also have name */ + ONAME(obj) = (char *) alloc((unsigned) lth); + } else { + /* zero length: the new name is empty; get rid of the old name */ + if (has_oname(obj)) + free_oname(obj); + } +} + +/* release an object's name; retains oextra even if all fields are now null */ +void +free_oname(struct obj *obj) +{ + if (has_oname(obj)) { + free((genericptr_t) ONAME(obj)); + ONAME(obj) = (char *) 0; + } +} + +#ifdef WIN32 +void +win32_abort(void) +{ + abort(); +} +#endif + +static int +length_without_val(const char *user_string, int len) +{ + const char *p = strchr(user_string, ':'), *q = strchr(user_string, '='); + + if (!p || (q && q < p)) + p = q; + if (p) { + /* 'user_string' hasn't necessarily been through mungspaces() + so might have tabs or consecutive spaces */ + while (p > user_string && isspace((uchar) * (p - 1))) + p--; + len = (int) (p - user_string); + } + return len; +} + +/* check whether a user-supplied option string is a proper leading + substring of a particular option name; option string might have + a colon or equals sign and arbitrary value appended to it */ +boolean +match_optname(const char *user_string, const char *optn_name, int min_length, + boolean val_allowed) +{ + int len = (int) strlen(user_string); + + if (val_allowed) + len = length_without_val(user_string, len); + + return (boolean) (len >= min_length + && !strncmpi(optn_name, user_string, len)); +} + +staticfn void +usage(int argc UNUSED, char **argv) +{ + char *cp = argv[0], *sep = (char *) 0; + + if ((sep = strrchr(cp, '/')) || (sep = strrchr(cp, '\\')) + || (sep = strrchr(cp, ':'))) + cp = sep + 1; + fprintf(stderr, + "\nTo convert a savefile to export format:\n %s %s %s\n", cp, + "-c", "savefile"); + fprintf(stderr, + "\nTo unconvert an exported savefile back into a savefile:\n %s %s %s\n", cp, + "-u", "savefile"); +} +/* sfctool.c */ diff --git a/util/sfexpasc.c b/util/sfexpasc.c new file mode 100644 index 000000000..9f390633f --- /dev/null +++ b/util/sfexpasc.c @@ -0,0 +1,1296 @@ +/* NetHack 3.7 sfexpasc.c.c $NHDT-Date$ $NHDT-Branch$:$NHDT-Revision$ */ +/* Copyright (c) Michael Allison, 2025. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* avoid the global.h define */ +#define STRNCMPI + +#include "hack.h" +#include "integer.h" +#include "sfprocs.h" +#include "sfproto.h" + +#if defined(MACOSX) || defined(VMS) +extern long long atoll(const char *); +#endif + +static void put_savefield(NHFILE *, char *, size_t); +char *get_savefield(NHFILE *, char *, size_t); +#ifdef SAVEFILE_DEBUGGING +void report_problem_exportascii(NHFILE *, const char *, const char *, + const char *); +#endif + +#ifdef _MSC_VER +#define strcmpi _stricmp +#else +#define strcmpi strcasecmp +#endif + +static char linebuf[BUFSZ * 10]; +static char outbuf[BUFSZ * 10]; + +#if 0 +void exportascii_sfo_any(NHFILE *nhfp, union any *d_any, const char *myname UNUSED); +void exportascii_sfi_any(NHFILE *nhfp, union any *d_any, const char *myname UNUSED); +void exportascii_sfo_aligntyp(NHFILE *nhfp, aligntyp *d_aligntyp, const char *myname UNUSED); +void exportascii_sfo_bitfield(NHFILE *nhfp, uint8_t *d_bitfield, const char *myname UNUSED, int); +void exportascii_sfo_boolean(NHFILE *nhfp, boolean *d_boolean, const char *myname UNUSED); +void exportascii_sfo_genericptr(NHFILE *nhfp, genericptr_t *d_genericptr, const char *myname UNUSED); +void exportascii_sfo_int32(NHFILE *nhfp, int *d_int, const char *myname UNUSED); +void exportascii_sfo_long(NHFILE *nhfp, long *d_long, const char *myname UNUSED); +void exportascii_sfo_schar(NHFILE *nhfp, schar *d_schar, const char *myname UNUSED); +void exportascii_sfo_int16(NHFILE *nhfp, short *d_short, const char *myname UNUSED); +void exportascii_sfo_size_t(NHFILE *nhfp, size_t *d_size_t, const char *myname UNUSED); +void exportascii_sfo_time_t(NHFILE *nhfp, time_t *d_time_t, const char *myname UNUSED); +void exportascii_sfo_uint32(NHFILE *nhfp, unsigned *d_unsigned, const char *myname); +void exportascii_sfo_uint(NHFILE *nhfp, unsigned *d_unsigned, const char *myname); +void exportascii_sfo_uchar(NHFILE *nhfp, unsigned char *d_uchar, const char *myname UNUSED); +void exportascii_sfo_uint(NHFILE *nhfp, unsigned int *d_uint, const char *myname UNUSED); +void exportascii_sfo_ulong(NHFILE *nhfp, unsigned long *d_ulong, const char *myname UNUSED); +void exportascii_sfo_ushort(NHFILE *nhfp, unsigned short *d_ushort, const char *myname UNUSED); +void exportascii_sfo_coordxy(NHFILE *nhfp, coordxy *d_coordxy, const char *myname UNUSED); +void exportascii_sfo_char(NHFILE *nhfp, xint8 *d_xint8, const char *myname UNUSED); +void exportascii_sfo_xint16(NHFILE *nhfp, xint16 *d_xint16, const char *myname UNUSED); +void exportascii_sfo_char(NHFILE *nhfp, char *d_str, const char *myname UNUSED, int cnt); +void exportascii_sfo_addinfo(NHFILE *nhfp UNUSED, const char *parent UNUSED, const char *action UNUSED, const char *myname UNUSED, int indx UNUSED); +void exportascii_sfi_aligntyp(NHFILE *nhfp, aligntyp *d_aligntyp, const char *myname UNUSED); +void exportascii_sfi_bitfield(NHFILE *nhfp, uint8_t *d_bitfield, const char *myname UNUSED, int); +void exportascii_sfi_boolean(NHFILE *nhfp, boolean *d_boolean, const char *myname UNUSED); +void exportascii_sfi_genericptr(NHFILE *nhfp, genericptr_t *d_genericptr, const char *myname UNUSED); +void exportascii_sfi_int32(NHFILE *nhfp, int *d_int, const char *myname UNUSED); +void exportascii_sfi_long(NHFILE *nhfp, long *d_long, const char *myname UNUSED); +void exportascii_sfi_schar(NHFILE *nhfp, schar *d_schar, const char *myname UNUSED); +void exportascii_sfi_int16(NHFILE *nhfp, short *d_short, const char *myname UNUSED); +void exportascii_sfi_size_t(NHFILE *nhfp, size_t *d_size_t, const char *myname UNUSED); +void exportascii_sfi_time_t(NHFILE *nhfp, time_t *d_time_t, const char *myname UNUSED); +void exportascii_sfi_uint32(NHFILE *nhfp, unsigned *d_unsigned, const char *myname); +void exportascii_sfi_uint(NHFILE *nhfp, unsigned *d_unsigned, const char *myname); +void exportascii_sfi_uchar(NHFILE *nhfp, unsigned char *d_uchar, const char *myname UNUSED); +void exportascii_sfi_uint(NHFILE *nhfp, unsigned int *d_uint, const char *myname UNUSED); +void exportascii_sfi_ulong(NHFILE *nhfp, unsigned long *d_ulong, const char *myname UNUSED); +void exportascii_sfi_ushort(NHFILE *nhfp, unsigned short *d_ushort, const char *myname UNUSED); +void exportascii_sfi_coordxy(NHFILE *nhfp, coordxy *d_coordxy, const char *myname UNUSED); +void exportascii_sfi_xint8(NHFILE *nhfp, xint8 *d_xint8, const char *myname UNUSED); +void exportascii_sfi_xint16(NHFILE *nhfp, xint16 *d_xint16, const char *myname UNUSED); +void exportascii_sfi_cnt(NHFILE *nhfp, char *d_str, const char *myname UNUSED, int cnt); +#endif + +#define SFO_BODY(dt) \ +{ \ +} + +#define SFI_BODY(dt) \ +{ \ +} + +#define SF_A(dtyp) \ +void exportascii_sfo_##dtyp(NHFILE *, dtyp *d_##dtyp, \ + const char *); \ +void exportascii_sfi_##dtyp(NHFILE *, dtyp *d_##dtyp, \ + const char *); + +/* +void exportascii_sfo_##dtyp(NHFILE *nhfp, dtyp *d_##dtyp, \ + const char *myname UNUSED) \ + SFO_BODY(dtyp) \ + \ +void exportascii_sfi_##dtyp(NHFILE *nhfp, dtyp *d_##dtyp, \ + const char *myname UNUSED) \ + SFI_BODY(dtyp) +*/ + +#define SF_C(keyw, dtyp) \ +void exportascii_sfo_##dtyp(NHFILE *, keyw dtyp *d_##dtyp UNUSED, \ + const char *); \ +void exportascii_sfi_##dtyp(NHFILE *, keyw dtyp *d_##dtyp, \ + const char *); \ + \ +void exportascii_sfo_##dtyp(NHFILE *nhfp UNUSED, keyw dtyp *d_##dtyp UNUSED, \ + const char *myname UNUSED) \ + SFO_BODY(dtyp) \ + \ +void exportascii_sfi_##dtyp(NHFILE *nhfp UNUSED, keyw dtyp *d_##dtyp UNUSED, \ + const char *myname UNUSED) \ + SFI_BODY(dtyp) + + +#define SF_X(xxx, dtyp) \ +void exportascii_sfo_##dtyp(NHFILE *, xxx *d_##dtyp, \ + const char *); \ +void exportascii_sfi_##dtyp(NHFILE *, xxx *d_##dtyp, \ + const char *); \ + \ +void exportascii_sfo_##dtyp(NHFILE *nhfp UNUSED, xxx *d_##dtyp UNUSED, \ + const char *myname UNUSED) \ + SFO_BODY(dtyp) \ + \ +void exportascii_sfi_##dtyp(NHFILE *nhfp, xxx *d_##dtyp, \ + const char *myname UNUSED) \ + SFI_BODY(dtyp) + + +#define SF_BF(xxx, dtyp) \ +void exportascii_sfo_##dtyp(NHFILE *, xxx *d_##dtyp, \ + const char *, int); \ +void exportascii_sfi_##dtyp(NHFILE *, xxx *d_##dtyp, \ + const char *, int); + +SF_C(struct, arti_info) +SF_C(struct, nhrect) +SF_C(struct, branch) +SF_C(struct, bubble) +SF_C(struct, cemetery) +SF_C(struct, context_info) +SF_C(struct, nhcoord) +SF_C(struct, damage) +SF_C(struct, dest_area) +SF_C(struct, dgn_topology) +SF_C(struct, dungeon) +SF_C(struct, d_level) +SF_C(struct, ebones) +SF_C(struct, edog) +SF_C(struct, egd) +SF_C(struct, emin) +SF_C(struct, engr) +SF_C(struct, epri) +SF_C(struct, eshk) +SF_C(struct, fe) +SF_C(struct, flag) +SF_C(struct, fruit) +SF_C(struct, gamelog_line) +SF_C(struct, kinfo) +SF_C(struct, levelflags) +SF_C(struct, ls_t) +SF_C(struct, linfo) +SF_C(struct, mapseen_feat) +SF_C(struct, mapseen_flags) +SF_C(struct, mapseen_rooms) +SF_C(struct, mkroom) +SF_C(struct, monst) +SF_C(struct, mvitals) +SF_C(struct, obj) +SF_C(struct, objclass) +SF_C(struct, q_score) +SF_C(struct, rm) +SF_C(struct, spell) +SF_C(struct, stairway) +SF_C(struct, s_level) +SF_C(struct, trap) +SF_C(struct, version_info) +SF_C(struct, you) +/* SF_C(union, any) */ +void exportascii_sfo_any(NHFILE *nhfp, union any *d_any, const char *myname UNUSED); +void exportascii_sfi_any(NHFILE *nhfp, union any *d_any, const char *myname UNUSED); + +SF_A(aligntyp) +SF_A(boolean) +SF_A(coordxy) +SF_A(genericptr) +SF_A(int) +SF_A(int16) +SF_A(int32) +SF_A(int64) +SF_A(long) +SF_A(schar) +SF_A(short) +SF_A(size_t) +SF_A(time_t) +SF_A(uchar) +SF_A(uint16) +SF_A(uint32) +SF_A(uint64) +SF_A(ulong) +SF_A(unsigned) +SF_A(ushort) +SF_A(xint16) +SF_A(xint8) +void exportascii_sfo_char(NHFILE *nhfp, char *d_char, const char *myname UNUSED, int cnt); +void exportascii_sfi_char(NHFILE *nhfp, char *d_char, const char *myname UNUSED, int cnt); +void exportascii_sfo_bitfield(NHFILE *nhfp, uint8_t *d_bitfield, + const char *myname UNUSED, int bflen UNUSED); +void exportascii_sfi_bitfield(NHFILE *nhfp, uint8_t *d_bitfield, + const char *myname UNUSED, int bflen UNUSED); +int critical_members_count_core(NHFILE *nhfp); + + +/* + *---------------------------------------------------------------------------- + * Sfo_def_ routines + * + * Default output routines. + * + *---------------------------------------------------------------------------- + */ + + +void +exportascii_sfo_any(NHFILE *nhfp, union any *d_any, const char *myname UNUSED) +{ + /* const char *parent = "any"; */ + + /* nhUse(parent); */ + Sprintf(outbuf, "%llx", (unsigned long long) d_any->a_void); + put_savefield(nhfp, outbuf, sizeof outbuf); + + Sprintf(outbuf, "%lu", d_any->a_ulong); + put_savefield(nhfp, outbuf, sizeof outbuf); + + Sprintf(outbuf, "%ld", d_any->a_long); + put_savefield(nhfp, outbuf, sizeof outbuf); + + Sprintf(outbuf, "%d", d_any->a_uint); + put_savefield(nhfp, outbuf, sizeof outbuf); + + Sprintf(outbuf, "%d", d_any->a_int);; + put_savefield(nhfp, outbuf, sizeof outbuf); + + Sprintf(outbuf, "%hd", (short) d_any->a_char); + put_savefield(nhfp, outbuf, sizeof outbuf); + +#if 0 + Sfo_genericptr(nhfp, d_any->a_void, parent, "a_void", 1); /* (genericptr_t) */ + Sfo_genericptr(nhfp, d_any->a_obj, parent, "a_obj", 1); /* (struct obj *) */ + Sfo_genericptr(nhfp, d_any->a_monst, parent, "a_monst", 1); /* (struct monst *) */ + Sfo_int32(nhfp, &d_any->a_int, parent, "a_int", 1); /* (int) */ + Sfo_char(nhfp, &d_any->a_char, parent, "a_char", 1); /* (char) */ + Sfo_schar(nhfp, &d_any->a_schar, parent, "a_schar", 1); /* (schar) */ + Sfo_uchar(nhfp, &d_any->a_uchar, parent, "a_uchar", 1); /* (uchar) */ + Sfo_uint(nhfp, &d_any->a_uint, parent, "a_uint", 1); /* (unsigned int) */ + Sfo_long(nhfp, &d_any->a_long, parent, "a_long", 1); /* (long) */ + Sfo_ulong(nhfp, &d_any->a_ulong, parent, "a_ulong", 1); /* (unsigned long) */ + Sfo_genericptr(nhfp, d_any->a_iptr, parent, "a_iptr", 1); /* (int *) */ + Sfo_genericptr(nhfp, d_any->a_lptr, parent, "a_lptr", 1); /* (long *) */ + Sfo_genericptr(nhfp, d_any->a_ulptr, parent, "a_ulptr", 1); /* (unsigned long *) */ + Sfo_genericptr(nhfp, d_any->a_uptr, parent, "a_uptr", 1); /* (unsigned *) */my + Sfo_genericptr(nhfp, d_any->a_string, parent, "a_string", 1); /* (const char *) */ + Sfo_ulong(nhfp, &d_any->a_mask32, parent, "a_mask32", 1); /* (unsigned long) */ +#endif +} + +void +exportascii_sfo_aligntyp(NHFILE *nhfp, aligntyp *d_aligntyp, const char *myname UNUSED) +{ + int itmp; + itmp = (int) *d_aligntyp; + Sprintf(outbuf, "%d", (short) itmp); + put_savefield(nhfp, outbuf, sizeof outbuf); +} + +void +exportascii_sfo_bitfield(NHFILE *nhfp, uint8_t *d_bitfield, const char *myname UNUSED, int bflen UNUSED) +{ + /* for bitfields, cnt is the number of bits, not an array */ + Sprintf(outbuf, "%hu", (unsigned short) *d_bitfield); + put_savefield(nhfp, outbuf, sizeof outbuf); +} + +void +exportascii_sfo_boolean(NHFILE *nhfp, boolean *d_boolean, const char *myname UNUSED) +{ + if (nhfp->fpdebug) + fprintf(nhfp->fpdebug, "(%s)\n", (*d_boolean) ? "TRUE" : "FALSE"); + Sprintf(outbuf, "%s", *d_boolean ? "true" : "false"); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_boolean++; +} + +void exportascii_sfo_genericptr(NHFILE *nhfp, genericptr_t *d_genericptr, + const char *myname UNUSED); + +void +exportascii_sfo_genericptr(NHFILE *nhfp, genericptr_t *d_genericptr, const char *myname UNUSED) +{ + unsigned long tmp; + char *byteptr = (char *) d_genericptr; + /* + * sbrooms is an array of pointers to mkroom. + * That array dimension is MAX_SUBROOMS. + * Even though the pointers themselves won't + * be valid, we need to account for the existence + * of that array and perhaps zero or non-zero. + */ + tmp = (*d_genericptr) ? 1UL : 0UL; + Sprintf(outbuf, "%08lu", tmp); + put_savefield(nhfp, outbuf, sizeof outbuf); + byteptr += sizeof(void *); + d_genericptr = (genericptr_t) byteptr; +} + +#if 0 +void +exportascii_sfo_int32(NHFILE *nhfp, int *d_int, const char *myname UNUSED) +{ + Sprintf(outbuf, "%d", *d_int); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_int++; +} +#endif + +void +exportascii_sfo_long(NHFILE *nhfp, long *d_long, const char *myname UNUSED) +{ + Sprintf(outbuf, "%ld", *d_long); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_long++; +} + +void +exportascii_sfo_schar(NHFILE *nhfp, schar *d_schar, const char *myname UNUSED) +{ + int itmp; + itmp = (int) *d_schar; + Sprintf(outbuf, "%d", itmp); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_schar++; +} + +void +exportascii_sfo_int16(NHFILE *nhfp, short *d_short, const char *myname UNUSED) +{ + Sprintf(outbuf, "%hd", *d_short); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_short++; +} + +void +exportascii_sfo_size_t(NHFILE *nhfp, size_t *d_size_t, const char *myname UNUSED) +{ + unsigned long ul = (unsigned long) *d_size_t; + + Sprintf(outbuf, "%lu", ul); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_size_t++; +} + +void +exportascii_sfo_time_t(NHFILE *nhfp, time_t *d_time_t, const char *myname UNUSED) +{ + Sprintf(outbuf, "%s", yyyymmddhhmmss(*d_time_t)); + put_savefield(nhfp, outbuf, sizeof outbuf); +} +void +exportascii_sfo_xint8(NHFILE *nhfp, int8 *d_int8, const char *myname UNUSED) +{ + Sprintf(outbuf, "%d", (int) *d_int8); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_int8++; +} +#if 0 +void +exportascii_sfo_int16(NHFILE *nhfp, int16 *d_int16, const char *myname UNUSED) +{ + Sprintf(outbuf, "%d", (int) *d_int16); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_uint16++; +} +#endif + +#if 0 +void +exportascii_sfo_char(NHFILE *nhfp, xint8 *d_xint8, const char *myname UNUSED, int cnt) +{ + short tmp; + + tmp = (int) *d_xint8; + Sprintf(outbuf, "%d", tmp); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_xint8++; +} +#endif + +void +exportascii_sfo_int32(NHFILE *nhfp, int32 *d_int32, const char *myname UNUSED) +{ + Sprintf(outbuf, "%d", *d_int32); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_int32++; +} + +void +exportascii_sfo_int64(NHFILE *nhfp, int64 *d_int64, const char *myname UNUSED) +{ + Sprintf(outbuf, "%lld", (long long) *d_int64); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_int64++; +} + +void +exportascii_sfo_int(NHFILE *nhfp, int *d_int, const char *myname UNUSED) +{ + Sprintf(outbuf, "%d", *d_int); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_int++; +} +void +exportascii_sfo_short(NHFILE *nhfp, short *d_short, const char *myname UNUSED) +{ + Sprintf(outbuf, "%d", (int) *d_short); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_short++; +} +/* aka exportascii_sfo_uint8 */ +void +exportascii_sfo_uchar(NHFILE *nhfp, unsigned char *d_uchar, const char *myname UNUSED) +{ + unsigned x_uint32 = (uint32) *d_uchar; + + Sprintf(outbuf, "%u", x_uint32); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_uchar++; +} + +void +exportascii_sfo_uint16(NHFILE *nhfp, uint16 *d_uint16, const char *myname UNUSED) +{ + Sprintf(outbuf, "%u", (uint32) *d_uint16); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_uint16++; +} + +void +exportascii_sfo_uint32(NHFILE *nhfp, uint32 *d_uint32, const char *myname UNUSED) +{ + Sprintf(outbuf, "%u", *d_uint32); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_uint32++; +} + +void +exportascii_sfo_uint64(NHFILE *nhfp, uint64 *d_uint64, const char *myname UNUSED) +{ + Sprintf(outbuf, "%llu", (unsigned long long) *d_uint64); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_uint64++; +} + +void +exportascii_sfo_ulong(NHFILE *nhfp, unsigned long *d_ulong, const char *myname UNUSED) +{ + Sprintf(outbuf, "%lu", *d_ulong); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_ulong++; +} + + +void +exportascii_sfo_ushort(NHFILE *nhfp, unsigned short *d_ushort, const char *myname UNUSED) +{ + Sprintf(outbuf, "%hu", *d_ushort); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_ushort++; +} + +void +exportascii_sfo_unsigned(NHFILE *nhfp, unsigned *d_unsigned, const char *myname UNUSED) +{ + Sprintf(outbuf, "%u", *d_unsigned); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_unsigned++; +} + +void +exportascii_sfo_coordxy(NHFILE *nhfp, coordxy *d_coordxy, const char *myname UNUSED) +{ + short tmp; + + tmp = (short) *d_coordxy; + Sprintf(outbuf, "%hu", tmp); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_coordxy++; +} + + +void +exportascii_sfo_xint16(NHFILE *nhfp, xint16 *d_xint16, const char *myname UNUSED) +{ + short tmp; + + tmp = (short) *d_xint16; + Sprintf(outbuf, "%hu", tmp); + put_savefield(nhfp, outbuf, sizeof outbuf); + d_xint16++; +} + +static char strbuf[BUFSZ * 10]; + +void +exportascii_sfo_char(NHFILE *nhfp, char *d_char, const char *myname UNUSED, int cnt) +{ + int i, j; + uint uintval; + char sval[QBUFSZ]; + uchar *usrc = (uchar *) d_char, *udest = (uchar *)strbuf; + + /* cnt is the number of characters */ + for (i = 0; i < cnt; ++i) { + if ((*usrc < 32) || (*usrc == '\\') || (*usrc == '|')) { + *udest++ = '\\'; + uintval = *usrc++; + Sprintf(sval, "%03u", uintval); + for (j = 0; j < 3; ++j) + *udest++ = (uchar) sval[j]; + } else { + *udest++ = *usrc++; + } + } + *udest = '\0'; + put_savefield(nhfp, strbuf, strlen(strbuf)); +} + +static void +put_savefield(NHFILE *nhfp, char *obuf, size_t outbufsz UNUSED) +{ + nhfp->wcount++; + fprintf(nhfp->fpdef, "%07ld|%s\n", nhfp->wcount, obuf); + fflush(nhfp->fpdef); +} + +/* + *---------------------------------------------------------------------------- + * exportascii_sfi_ routines called from functions in Sfi_base.c + *---------------------------------------------------------------------------- + */ + +void exportascii_sfi_any(NHFILE *nhfp, union any *d_any, const char *myname UNUSED); + +void +exportascii_sfi_any(NHFILE *nhfp, union any *d_any, const char *myname UNUSED) +{ + char *rstr; + long long tmp; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + tmp = atoll(rstr); + d_any->a_uint64 = (uint64) tmp; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + tmp = atoll(rstr); + d_any->a_ulong = (unsigned long) tmp; + rstr = get_savefield(nhfp, linebuf, BUFSZ); + d_any->a_long = atol(rstr); + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + tmp = atoll(rstr); + d_any->a_uint = (unsigned int) tmp; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + d_any->a_int = atoi(rstr); + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + d_any->a_char = (char) atoi(rstr); + +#if 0 + Sfi_genericptr(nhfp, d_any->a_void, parent, "a_void", 1); + Sfi_genericptr(nhfp, d_any->a_obj, parent, "a_obj", 1); + Sfi_genericptr(nhfp, d_any->a_monst, parent, "a_monst", 1); + Sfi_int32(nhfp, &d_any->a_int, parent, "a_int", 1); + Sfi_char(nhfp, &d_any->a_char, parent, "a_char", 1); + Sfi_schar(nhfp, &d_any->a_schar, parent, "a_schar", 1); + Sfi_uchar(nhfp, &d_any->a_uchar, parent, "a_uchar", 1); + Sfi_uint(nhfp, &d_any->a_uint, parent, "a_uint", 1); + Sfi_long(nhfp, &d_any->a_long, parent, "a_long", 1); + Sfi_ulong(nhfp, &d_any->a_ulong, parent, "a_ulong", 1); + Sfi_genericptr(nhfp, d_any->a_iptr, parent, "a_iptr", 1); + Sfi_genericptr(nhfp, d_any->a_lptr, parent, "a_lptr", 1); + Sfi_genericptr(nhfp, d_any->a_ulptr, parent, "a_ulptr", 1); + Sfi_genericptr(nhfp, d_any->a_uptr, parent, "a_uptr", 1); + Sfi_genericptr(nhfp, d_any->a_string, parent, "a_string", 1); + Sfi_ulong(nhfp, &d_any->a_mask32, parent, "a_mask32", 1); +#endif +} + +void +exportascii_sfi_aligntyp(NHFILE *nhfp, aligntyp *d_aligntyp, const char *myname UNUSED) +{ + char *rstr; + aligntyp tmp; + long long lltmp; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + lltmp = atoll(rstr); + tmp = (aligntyp) lltmp; +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel && tmp != *d_aligntyp) + report_problem_ascii(nhfp, myparent, myname, parent); + else +#endif + *d_aligntyp = tmp; +} + +void +exportascii_sfi_bitfield(NHFILE *nhfp, uint8_t *d_bitfield, const char *myname UNUSED, int bflen UNUSED) +{ + char *rstr; + uint8_t tmp; + + /* cnt is the number of bits in the bitfield, not an array dimension */ + rstr = get_savefield(nhfp, linebuf, BUFSZ); + tmp = (uint8_t) atoi(rstr); +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel && tmp != *d_bitfield) + report_problem_ascii(nhfp, myparent, myname, parent); + else +#endif + *d_bitfield = tmp; +} + +void +exportascii_sfi_boolean(NHFILE *nhfp, boolean *d_boolean, const char *myname UNUSED) +{ + char *rstr; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); +#ifdef SAVEFILE_DEBUGGING + if (!strcmpi(rstr, "false") && + !strcmpi(rstr, "true")) + report_problem_ascii(nhfp, myparent, myname, parent); + else +#endif + if (!strcmpi(rstr, "false")) + *d_boolean = FALSE; + else + *d_boolean = TRUE; + d_boolean++; +} + +void exportascii_sfi_genericptr(NHFILE *nhfp, genericptr_t *d_genericptr, + const char *myname UNUSED); + +void +exportascii_sfi_genericptr(NHFILE *nhfp, genericptr_t *d_genericptr, const char *myname UNUSED) +{ + long long lltmp; + char *rstr; + static const char *glorkum = "glorkum"; + + /* + * sbrooms is an array of pointers to mkroom. + * That array dimension is MAX_SUBROOMS. + * Even though the pointers themselves won't + * be valid, we need to account for the existence + * of that array. + */ + /* these pointers can't actually be valid */ + rstr = get_savefield(nhfp, linebuf, sizeof linebuf); + lltmp = atoll(rstr); + *d_genericptr = lltmp ? (genericptr_t) glorkum : (genericptr_t) 0; +} + +void +exportascii_sfi_int32(NHFILE *nhfp, int32 *d_int32, const char *myname UNUSED) +{ + int32 tmp; + char *rstr; + long ltmp; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + ltmp = atol(rstr); + tmp = (int32) ltmp; +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel && tmp != *d_int32) + report_problem_ascii(nhfp, myparent, myname, parent); + else +#endif + *d_int32 = (int32) tmp; + d_int32++; +} + +void +exportascii_sfi_int(NHFILE *nhfp, int *d_int, const char *myname UNUSED) +{ + int tmp; + char *rstr; + long ltmp; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + ltmp = atol(rstr); + tmp = (int) ltmp; +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel && tmp != *d_int) + report_problem_ascii(nhfp, myparent, myname, parent); + else +#endif + *d_int = (int32) tmp; + d_int++; +} +void +exportascii_sfi_long(NHFILE *nhfp, long *d_long, const char *myname UNUSED) +{ + long tmp; + long long lltmp; + char *rstr; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + lltmp = atoll(rstr); + tmp = (long) lltmp; +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel && tmp != *d_long) + report_problem_ascii(nhfp, myparent, myname, parent); + else +#endif + *d_long = tmp; + d_long++; +} + +void +exportascii_sfi_schar(NHFILE *nhfp, schar *d_schar, const char *myname UNUSED) +{ + schar tmp; + char *rstr; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + tmp = (schar) atoi(rstr); +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel && tmp != *d_schar) + report_problem_ascii(nhfp, myparent, myname, parent); + else +#endif + *d_schar = tmp; + d_schar++; +} + +void +exportascii_sfi_int16(NHFILE *nhfp, short *d_short, const char *myname UNUSED) +{ + short tmp; + char *rstr; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + tmp = (short) atoi(rstr); +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel && tmp != *d_short) + report_problem_ascii(nhfp, myparent, myname, parent); + else +#endif + *d_short = tmp; + d_short++; +} + +void +exportascii_sfi_int64(NHFILE *nhfp, int64 *d_int64, const char *myname UNUSED) +{ + int64 tmp; + char *rstr; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + tmp = (int64) atol(rstr); +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel && tmp != *d_int64) + report_problem_ascii(nhfp, myparent, myname, parent); + else +#endif + *d_int64 = tmp; + d_int64++; +} +void +exportascii_sfi_size_t(NHFILE *nhfp, size_t *d_size_t, const char *myname UNUSED) +{ + size_t tmp; + char *rstr; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + tmp = (size_t) atol(rstr); +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel && tmp != *d_size_t) + report_problem_ascii(nhfp, myparent, myname, parent); + else +#endif + *d_size_t = tmp; + d_size_t++; +} + +void +exportascii_sfi_time_t(NHFILE *nhfp, time_t *d_time_t, const char *myname UNUSED) +{ + time_t tmp; + char *rstr; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + tmp = time_from_yyyymmddhhmmss(rstr); +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel && tmp != *d_time_t) + report_problem_ascii(nhfp, myparent, myname, parent); + else +#endif + *d_time_t = tmp; + d_time_t++; +} + +#if 0 +void +exportascii_sfi_uint32(NHFILE *nhfp, unsigned *d_unsigned, const char *myname) +{ + /* deferal */ + exportascii_sfi_uint(nhfp, d_unsigned, myname); +} +#endif + +void +exportascii_sfi_uchar(NHFILE *nhfp, unsigned char *d_uchar, const char *myname UNUSED) +{ + uchar tmp; + int itmp; + char *rstr; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + itmp = atoi(rstr); + tmp = (char ) itmp; +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel && tmp != *d_uchar) + report_problem_ascii(nhfp, myparent, myname, parent); + else +#endif + *d_uchar = tmp; + d_uchar++; +} + +void +exportascii_sfi_uint16(NHFILE *nhfp, uint16 *d_uint16, const char *myname UNUSED) +{ + char *rstr; + unsigned int tmp; + long long lltmp; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + lltmp = atoll(rstr); + tmp = (unsigned int) lltmp; +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel && tmp != *d_uint) + report_problem_ascii(nhfp, myparent, myname, parent); + else +#endif + *d_uint16 = tmp; + d_uint16++; +} + +void +exportascii_sfi_uint32(NHFILE *nhfp, uint32 *d_uint32, const char *myname UNUSED) +{ + char *rstr; + uint32 tmp; + long long lltmp; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + lltmp = atoll(rstr); + tmp = (uint32) lltmp; +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel && tmp != *d_uint) + report_problem_ascii(nhfp, myparent, myname, parent); + else +#endif + *d_uint32 = tmp; + d_uint32++; +} + +void +exportascii_sfi_uint64(NHFILE *nhfp, uint64 *d_uint64, const char *myname UNUSED) +{ + char *rstr; + uint64 tmp; + long long lltmp; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + lltmp = atoll(rstr); + tmp = (uint64) lltmp; +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel && tmp != *d_uint) + report_problem_ascii(nhfp, myparent, myname, parent); + else +#endif + *d_uint64 = tmp; + d_uint64++; +} + +void +exportascii_sfi_unsigned(NHFILE *nhfp, unsigned *d_unsigned, const char *myname UNUSED) +{ + char *rstr; + uint32 tmp; + long long lltmp; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + lltmp = atoll(rstr); + tmp = (uint32) lltmp; +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel && tmp != *d_unsigned) + report_problem_ascii(nhfp, "", myname, parent); + else +#endif + *d_unsigned = tmp; + d_unsigned++; +} + +void +exportascii_sfi_ulong(NHFILE *nhfp, unsigned long *d_ulong, const char *myname UNUSED) +{ + unsigned long tmp; + long long lltmp; + char *rstr; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + lltmp = atoll(rstr); + tmp = (unsigned long) lltmp; +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel && tmp != *d_ulong) + report_problem_ascii(nhfp, myparent, myname, parent); + else +#endif + *d_ulong = tmp; + d_ulong++; +} + +void +exportascii_sfi_ushort(NHFILE *nhfp, unsigned short *d_ushort, const char *myname UNUSED) +{ + short tmp; + long long lltmp; + char *rstr; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + lltmp = atoll(rstr); + tmp = (unsigned short) lltmp; +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel && tmp != *d_ushort) + report_problem_ascii(nhfp, myparent, myname, parent); + else +#endif + *d_ushort = tmp; + d_ushort++; +} + +void +exportascii_sfi_coordxy(NHFILE *nhfp, coordxy *d_coordxy, const char *myname UNUSED) +{ + coordxy tmp; + int itmp; + char *rstr; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + itmp = atoi(rstr); + tmp = (coordxy) itmp; +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel && tmp != *d_coordxy) + report_problem_ascii(nhfp, myparent, myname, parent); + else +#endif + *d_coordxy = tmp; + d_coordxy++; +} + +void +exportascii_sfi_xint8(NHFILE *nhfp, xint8 *d_xint8, const char *myname UNUSED) +{ + xint8 tmp; + int itmp; + char *rstr; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + if (!nhfp->eof) { + itmp = atoi(rstr); + tmp = (xint8) itmp; + } else { + return; + } +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel && tmp != *d_xint8) + report_problem_ascii(nhfp, myparent, myname, parent); + else +#endif + *d_xint8 = tmp; + d_xint8++; +} + +void +exportascii_sfi_xint16(NHFILE *nhfp, xint16 *d_xint16, const char *myname UNUSED) +{ + xint16 tmp; + int itmp; + char *rstr; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + itmp = atoi(rstr); + tmp = (xint16) itmp; +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel && tmp != *d_xint16) + report_problem_ascii(nhfp, myparent, myname, parent); + else +#endif + *d_xint16 = tmp; + d_xint16++; +} + +void +exportascii_sfi_short(NHFILE *nhfp, short *d_short, const char *myname UNUSED) +{ + xint16 tmp; + int itmp; + char *rstr; + + rstr = get_savefield(nhfp, linebuf, BUFSZ); + itmp = atoi(rstr); + tmp = (xint16) itmp; +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel && tmp != *d_short) + report_problem_ascii(nhfp, "", myname, parent); + else +#endif + *d_short = tmp; + d_short++; +} + + +void +exportascii_sfi_char(NHFILE *nhfp, char *d_char, const char *myname UNUSED, int cnt) +{ + int i, j, sval; + char n[4], *rstr; + char *src, *dest; + + /* cnt is the length of the string */ + rstr = get_savefield(nhfp, strbuf, sizeof strbuf); + if (!rstr) { + nhfp->eof = TRUE; + return; + } + src = rstr; + dest = +#ifdef SAVEFILE_DEBUGGING + testbuf; +#else + d_char; +#endif + + for (i = 0; i < cnt; ++i) { + if (*src == '\\') { + src++; + for (j = 0; j < 4; ++j) { + if (j < 3) + n[j] = *src++; + else + n[j] = '\0'; + } + sval = atoi(n); + *dest++ = (char) sval; + } else + *dest++ = *src++; + } +#ifdef SAVEFILE_DEBUGGING + if (nhfp->structlevel) { + src = testbuf; + dest = d_char; + match = TRUE; + for (i = 0; i < cnt; ++i) { + if (*src++ != *dest++) + match = FALSE; + } + if (!match) + report_problem_ascii(nhfp, myparent, myname, parent); + else { + src = testbuf; + dest = d_char; + for (i = 0; i < cnt; ++i) + *dest++ = *src++; + } + } +#endif +} + +char * +get_savefield(NHFILE *nhfp, char *inbuf, size_t inbufsz) +{ + char *ep, *sep; + char *res = 0; + + if ((res =fgets(inbuf, (int) inbufsz, nhfp->fpdef)) != 0) { + nhfp->rcount++; + ep = strchr(inbuf, '\n'); + if (!ep) { /* newline missing */ + if (strlen(inbuf) < (inbufsz - 2)) { + /* likely the last line of file is just + missing a newline; process it anyway */ + ep = eos(inbuf); + } + } + if (ep) + *ep = '\0'; /* remove newline */ + sep = strchr(inbuf, '|'); + if (sep) + sep++; + + return sep; + } + inbuf[0] = '\0'; + nhfp->eof = TRUE; + return inbuf; +} + +#ifdef SAVEFILE_DEBUGGING +void +report_problem_ascii(NHFILE *nhfp, const char *s1, const char *s2, const char *s3) +{ + fprintf(nhfp->fpdebug, "faulty value preservation " + "(%ld, %s, %s, %s)\n", + ((nhfp->mode & READING) != 0) ? nhfp->rcount : nhfp->wcount, s1, s2, s3); +} +#endif + +int exportascii_critical_members_count(void); + +int +exportascii_critical_members_count(void) +{ + return 0; +} + +struct sf_fieldlevel_procs exportascii_sfo_procs = { + "exportascii", + /* sf_x */ + { + sfo_x_arti_info, + sfo_x_nhrect, + sfo_x_branch, + sfo_x_bubble, + sfo_x_cemetery, + sfo_x_context_info, + sfo_x_nhcoord, + sfo_x_damage, + sfo_x_dest_area, + sfo_x_dgn_topology, + sfo_x_dungeon, + sfo_x_d_level, + sfo_x_ebones, + sfo_x_edog, + sfo_x_egd, + sfo_x_emin, + sfo_x_engr, + sfo_x_epri, + sfo_x_eshk, + sfo_x_fe, + sfo_x_flag, + sfo_x_fruit, + sfo_x_gamelog_line, + sfo_x_kinfo, + sfo_x_levelflags, + sfo_x_ls_t, + sfo_x_linfo, + sfo_x_mapseen_feat, + sfo_x_mapseen_flags, + sfo_x_mapseen_rooms, + sfo_x_mkroom, + sfo_x_monst, + sfo_x_mvitals, + sfo_x_obj, + sfo_x_objclass, + sfo_x_q_score, + sfo_x_rm, + sfo_x_spell, + sfo_x_stairway, + sfo_x_s_level, + sfo_x_trap, + sfo_x_version_info, + sfo_x_you, + + exportascii_sfo_any, + exportascii_sfo_aligntyp, + exportascii_sfo_boolean, + exportascii_sfo_coordxy, + exportascii_sfo_genericptr, + exportascii_sfo_int, + exportascii_sfo_int16, + exportascii_sfo_int32, + exportascii_sfo_int64, + exportascii_sfo_long, + exportascii_sfo_schar, + exportascii_sfo_short, + exportascii_sfo_size_t, + exportascii_sfo_time_t, + exportascii_sfo_uchar, + exportascii_sfo_uint16, + exportascii_sfo_uint32, + exportascii_sfo_uint64, + exportascii_sfo_ulong, + exportascii_sfo_unsigned, + exportascii_sfo_ushort, + exportascii_sfo_xint16, + exportascii_sfo_xint8, + exportascii_sfo_char, + exportascii_sfo_bitfield, + } +}; + +struct sf_fieldlevel_procs exportascii_sfi_procs = { + "le", + /* sf_x */ + { + sfi_x_arti_info, + sfi_x_nhrect, + sfi_x_branch, + sfi_x_bubble, + sfi_x_cemetery, + sfi_x_context_info, + sfi_x_nhcoord, + sfi_x_damage, + sfi_x_dest_area, + sfi_x_dgn_topology, + sfi_x_dungeon, + sfi_x_d_level, + sfi_x_ebones, + sfi_x_edog, + sfi_x_egd, + sfi_x_emin, + sfi_x_engr, + sfi_x_epri, + sfi_x_eshk, + sfi_x_fe, + sfi_x_flag, + sfi_x_fruit, + sfi_x_gamelog_line, + sfi_x_kinfo, + sfi_x_levelflags, + sfi_x_ls_t, + sfi_x_linfo, + sfi_x_mapseen_feat, + sfi_x_mapseen_flags, + sfi_x_mapseen_rooms, + sfi_x_mkroom, + sfi_x_monst, + sfi_x_mvitals, + sfi_x_obj, + sfi_x_objclass, + sfi_x_q_score, + sfi_x_rm, + sfi_x_spell, + sfi_x_stairway, + sfi_x_s_level, + sfi_x_trap, + sfi_x_version_info, + sfi_x_you, + + exportascii_sfi_any, + exportascii_sfi_aligntyp, + exportascii_sfi_boolean, + exportascii_sfi_coordxy, + exportascii_sfi_genericptr, + exportascii_sfi_int, + exportascii_sfi_int16, + exportascii_sfi_int32, + exportascii_sfi_int64, + exportascii_sfi_long, + exportascii_sfi_schar, + exportascii_sfi_short, + exportascii_sfi_size_t, + exportascii_sfi_time_t, + exportascii_sfi_uchar, + exportascii_sfi_uint16, + exportascii_sfi_uint32, + exportascii_sfi_uint64, + exportascii_sfi_ulong, + exportascii_sfi_unsigned, + exportascii_sfi_ushort, + exportascii_sfi_xint16, + exportascii_sfi_xint8, + exportascii_sfi_char, + exportascii_sfi_bitfield, + } +}; diff --git a/util/sftags.c b/util/sftags.c new file mode 100644 index 000000000..39bfe24f9 --- /dev/null +++ b/util/sftags.c @@ -0,0 +1,2283 @@ +/* NetHack 3.6 sftags.c $Date$ $Revision$ */ +/* Copyright (c) Michael Allison, 2025 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Read the given ctags file and generate: + * Intermediate temp files: + * include/sfo_proto.tmp + * include/sfi_proto.tmp + * util/sfi_data.tmp + * util/sfo_data.tmp + * util/sfnormalize.tmp + * Final files: + * sfdata.c + * sfproto.h + * + */ + +/* avoid global.h define */ +#define STRNCMPI + +#include "hack.h" +#include "integer.h" +#include "wintype.h" + +#ifdef __GNUC__ +#include +#ifndef WIN32 +#define strncmpi strncasecmp +#endif +#define strcmpi strcasecmp +#elif defined(_MSC_VER) +#define strcmpi _stricmp +#ifndef strncmpi +#define strncmpi _strnicmp +#endif +#endif + +#if 0 +/* version information */ +#ifdef SHORT_FILENAMES +#include "patchlev.h" +#else +#include "patchlevel.h" +#endif +#endif + +#define NHTYPE_SIMPLE 1 +#define NHTYPE_COMPLEX 2 +struct nhdatatypes_t { + uint dtclass; + char *dtype; + size_t dtsize; +}; + +struct tagstruct { + uint marker; + int linenum; + char ptr[128]; + char tag[100]; + char filename[128]; + char searchtext[255]; + char tagtype; + char parent[100]; + char parenttype[100]; + char arraysize1[100]; + char arraysize2[100]; + struct tagstruct *next; +}; + +struct needs_array_handling { + const char *nm; + const char *parent; +}; + +#define SFO_DATA c_sfodata +#define SFI_DATA c_sfidata +#define SFDATATMP c_sfdatatmp +#define SFO_PROTO c_sfoproto +#define SFI_PROTO c_sfiproto +#define SFDATA c_sfdata +#define SFPROTO c_sfproto +#define SF_NORMALIZE_POINTERS c_sfnormalize +#define SFPROTO_NAME "../include/sfproto.h" +#define SFDATA_NAME "../util/sfdata.c" + +static char *fgetline(FILE*); +ATTRNORETURN static void quit(void) NORETURN; +static void out_of_memory(void); +static void doline(char *); +static void chain(struct tagstruct *); +#if 0 +static void showthem(void); +static char *stripspecial(char *); +#endif +static char *deblank(char *); +static char *deeol(char *); +static void generate_c_files(void); +static char *findtype(char *, char *); +#if 0 +static boolean is_prim(char *); +#endif +static void taglineparse(char *, struct tagstruct *); +static void parseExtensionFields(struct tagstruct *, char *); +static void set_member_array_size(struct tagstruct *); +#if 0 +static char *member_array_dims(struct tagstruct *, char *); +static char *member_array_size(struct tagstruct *, char *); +#endif +static void output_types(FILE *); +#if 0 +static char *dtmacro(const char *,int); +#endif +static char *dtfn(const char *,int, boolean *); +static char *bfsize(const char *); +static char *fieldfix(char *,char *); +static boolean listed(struct tagstruct *t); +static const char *fn(const char *f); +static boolean no_x(const char *s); + +#ifdef VMS +static FILE *vms_fopen(name, mode) const char *name, *mode; +{ + return fopen(name, mode, "mbc=64", "shr=nil"); +} +# define fopen(f,m) vms_fopen(f,m) +#endif + +#define Fprintf (void) fprintf +#ifndef __GO32__ +#define DEFAULTTAGNAME "../util/sf.tags" +#else +#define DEFAULTTAGNAME "../util/sftags.tag" +#endif +#ifndef _MAX_PATH +#define _MAX_PATH 120 +#endif + +#define TAB '\t' +#define SPACE ' ' + +#ifdef MACOS +#define ALIGN32 __attribute__((aligned(32))) +#else +#define ALIGN32 +#endif + +struct tagstruct *first; +struct tagstruct zerotag = { 0 }; + +static int tagcount; +static const char *infilenm; +static FILE *infile; +static char line[2048]; +static long lineno; +static char ssdef[BUFSZ]; +static char fieldfixbuf[BUFSZ]; +static boolean suppress_count; +static char folderprefix[BUFSZ]; +static char filenmbuf[ +#if defined(UNIX) && defined(PATHLEN) + PATHLEN +#elif defined(WIN32) && defined(_MAX_PATH) + _MAX_PATH +#else + BUFSZ * 4 +#endif + ]; + +#define NHTYPE_SIMPLE 1 +#define NHTYPE_COMPLEX 2 + +struct nhdatatypes_t readtagstypes[] = { + { NHTYPE_SIMPLE, (char *) "any", sizeof(anything) }, + { NHTYPE_SIMPLE, (char *) "genericptr_t", sizeof(genericptr_t) }, + { NHTYPE_SIMPLE, (char *) "aligntyp", sizeof(aligntyp) }, + { NHTYPE_SIMPLE, (char *) "Bitfield", sizeof(uint8_t) }, + { NHTYPE_SIMPLE, (char *) "boolean", sizeof(boolean) }, + { NHTYPE_SIMPLE, (char *) "char", sizeof(char) }, + { NHTYPE_SIMPLE, (char *) "int", sizeof(int) }, + { NHTYPE_SIMPLE, (char *) "long", sizeof(long) }, + { NHTYPE_SIMPLE, (char *) "schar", sizeof(schar) }, + { NHTYPE_SIMPLE, (char *) "short", sizeof(short) }, + { NHTYPE_SIMPLE, (char *) "size_t", sizeof(size_t) }, + { NHTYPE_SIMPLE, (char *) "string", 1 }, + { NHTYPE_SIMPLE, (char *) "time_t", sizeof(time_t) }, + { NHTYPE_SIMPLE, (char *) "uchar", sizeof(uchar) }, + { NHTYPE_SIMPLE, (char *) "unsigned char", sizeof(unsigned char) }, + { NHTYPE_SIMPLE, (char *) "uint", sizeof(uint) }, + { NHTYPE_SIMPLE, (char *) "unsigned long", sizeof(unsigned long) }, + { NHTYPE_SIMPLE, (char *) "unsigned short", sizeof(unsigned short) }, + { NHTYPE_SIMPLE, (char *) "unsigned", sizeof(unsigned) }, + { NHTYPE_SIMPLE, (char *) "xint8", sizeof(xint8) }, + { NHTYPE_SIMPLE, (char *) "xint16", sizeof(xint16) }, + { NHTYPE_SIMPLE, (char *) "coordxy", sizeof(coordxy) }, + { NHTYPE_COMPLEX, (char *) "align", sizeof(struct align) }, + /* { NHTYPE_COMPLEX, (char *) "attack", sizeof(struct attack) }, */ + /* ^ permonst affil */ + { NHTYPE_COMPLEX, (char *) "arti_info", sizeof(struct arti_info) }, + { NHTYPE_COMPLEX, (char *) "attribs", sizeof(struct attribs) }, + { NHTYPE_COMPLEX, (char *) "bill_x", sizeof(struct bill_x) }, + { NHTYPE_COMPLEX, (char *) "branch", sizeof(struct branch) }, + { NHTYPE_COMPLEX, (char *) "bubble", sizeof(struct bubble) }, + { NHTYPE_COMPLEX, (char *) "cemetery", sizeof(struct cemetery) }, + /*{ NHTYPE_COMPLEX, (char *) "container", sizeof(struct container) }, */ + { NHTYPE_COMPLEX, (char *) "context_info", sizeof(struct context_info) }, + /* context sub-structures */ + { NHTYPE_COMPLEX, (char *) "achievement_tracking", + sizeof(struct achievement_tracking) }, + { NHTYPE_COMPLEX, (char *) "book_info", sizeof(struct book_info) }, + { NHTYPE_COMPLEX, (char *) "dig_info", + sizeof(struct dig_info) }, /* context */ + { NHTYPE_COMPLEX, (char *) "engrave_info", sizeof(struct engrave_info) }, + { NHTYPE_COMPLEX, (char *) "lifelists", sizeof(struct lifelists) }, + { NHTYPE_COMPLEX, (char *) "obj_split", sizeof(struct obj_split) }, + { NHTYPE_COMPLEX, (char *) "polearm_info", sizeof(struct polearm_info) }, + { NHTYPE_COMPLEX, (char *) "takeoff_info", sizeof(struct takeoff_info) }, + { NHTYPE_COMPLEX, (char *) "tin_info", sizeof(struct tin_info) }, + { NHTYPE_COMPLEX, (char *) "tribute_info", sizeof(struct tribute_info) }, + { NHTYPE_COMPLEX, (char *) "victual_info", sizeof(struct victual_info) }, + { NHTYPE_COMPLEX, (char *) "warntype_info", + sizeof(struct warntype_info) }, + /* end of context sub-structures */ + { NHTYPE_COMPLEX, (char *) "d_flags", sizeof(struct d_flags) }, + { NHTYPE_COMPLEX, (char *) "d_level", sizeof(struct d_level) }, + { NHTYPE_COMPLEX, (char *) "damage", sizeof(struct damage) }, + { NHTYPE_COMPLEX, (char *) "dest_area", sizeof(struct dest_area) }, + { NHTYPE_COMPLEX, (char *) "dgn_topology", sizeof(struct dgn_topology) }, + { NHTYPE_COMPLEX, (char *) "dungeon", sizeof(struct dungeon) }, + { NHTYPE_COMPLEX, (char *) "ebones", sizeof(struct ebones) }, + { NHTYPE_COMPLEX, (char *) "edog", sizeof(struct edog) }, + { NHTYPE_COMPLEX, (char *) "egd", sizeof(struct egd) }, + { NHTYPE_COMPLEX, (char *) "emin", sizeof(struct emin) }, + { NHTYPE_COMPLEX, (char *) "engr", sizeof(struct engr) }, + { NHTYPE_COMPLEX, (char *) "epri", sizeof(struct epri) }, + { NHTYPE_COMPLEX, (char *) "eshk", sizeof(struct eshk) }, + { NHTYPE_COMPLEX, (char *) "fakecorridor", sizeof(struct fakecorridor) }, + { NHTYPE_COMPLEX, (char *) "fe", sizeof(struct fe) }, + { NHTYPE_COMPLEX, (char *) "flag", sizeof(struct flag) }, + { NHTYPE_COMPLEX, (char *) "fruit", sizeof(struct fruit) }, + { NHTYPE_COMPLEX, (char *) "gamelog_line", sizeof(struct gamelog_line) }, + { NHTYPE_COMPLEX, (char *) "kinfo", sizeof(struct kinfo) }, + { NHTYPE_COMPLEX, (char *) "levelflags", sizeof(struct levelflags) }, + { NHTYPE_COMPLEX, (char *) "linfo", sizeof(struct linfo) }, + { NHTYPE_COMPLEX, (char *) "ls_t", sizeof(struct ls_t) }, + { NHTYPE_COMPLEX, (char *) "mapseen_feat", sizeof(struct mapseen_feat) }, + { NHTYPE_COMPLEX, (char *) "mapseen_flags", + sizeof(struct mapseen_flags) }, + { NHTYPE_COMPLEX, (char *) "mapseen_rooms", + sizeof(struct mapseen_rooms) }, + { NHTYPE_COMPLEX, (char *) "mapseen", sizeof(mapseen) }, + { NHTYPE_COMPLEX, (char *) "mextra", sizeof(struct mextra) }, + { NHTYPE_COMPLEX, (char *) "mkroom", sizeof(struct mkroom) }, + { NHTYPE_COMPLEX, (char *) "monst", sizeof(struct monst) }, + { NHTYPE_COMPLEX, (char *) "mvitals", sizeof(struct mvitals) }, + { NHTYPE_COMPLEX, (char *) "nhcoord", sizeof(struct nhcoord) }, + { NHTYPE_COMPLEX, (char *) "nhrect", sizeof(struct nhrect) }, + { NHTYPE_COMPLEX, (char *) "novel_tracking", + sizeof(struct novel_tracking) }, + { NHTYPE_COMPLEX, (char *) "obj", sizeof(struct obj) }, + { NHTYPE_COMPLEX, (char *) "objclass", sizeof(struct objclass) }, + { NHTYPE_COMPLEX, (char *) "oextra", sizeof(struct oextra) }, + /* {NHTYPE_COMPLEX, (char *) "permonst", sizeof(struct permonst)}, */ + { NHTYPE_COMPLEX, (char *) "prop", sizeof(struct prop) }, + { NHTYPE_COMPLEX, (char *) "q_score", sizeof(struct q_score) }, + { NHTYPE_COMPLEX, (char *) "rm", sizeof(struct rm) }, + { NHTYPE_COMPLEX, (char *) "s_level", sizeof(struct s_level) }, + { NHTYPE_COMPLEX, (char *) "skills", sizeof(struct skills) }, + { NHTYPE_COMPLEX, (char *) "spell", sizeof(struct spell) }, + { NHTYPE_COMPLEX, (char *) "stairway", sizeof(struct stairway) }, +#ifdef SYSFLAGS + { NHTYPE_COMPLEX, (char *) "sysflag", sizeof(struct sysflag) }, +#endif + { NHTYPE_COMPLEX, (char *) "trap", sizeof(struct trap) }, + /* {NHTYPE_COMPLEX, (char *) "u_achieve", sizeof(struct u_achieve)}, */ + { NHTYPE_COMPLEX, (char *) "u_conduct", sizeof(struct u_conduct) }, + { NHTYPE_COMPLEX, (char *) "u_event", sizeof(struct u_event) }, + { NHTYPE_COMPLEX, (char *) "u_have", sizeof(struct u_have) }, + { NHTYPE_COMPLEX, (char *) "u_realtime", sizeof(struct u_realtime) }, + { NHTYPE_COMPLEX, (char *) "u_roleplay", sizeof(struct u_roleplay) }, + { NHTYPE_COMPLEX, (char *) "version_info", sizeof(struct version_info) }, + { NHTYPE_COMPLEX, (char *) "vlaunchinfo", sizeof(union vlaunchinfo) }, + { NHTYPE_COMPLEX, (char *) "vptrs", sizeof(union vptrs) }, + { NHTYPE_COMPLEX, (char *) "you", sizeof(struct you) }, +}; + + +/* + * These have arrays of other structs, not just arrays of + * simple types. We need to put array handling right into + * the code for these ones. + */ +struct needs_array_handling nah[] = { + {"fakecorr", (char *) "egd"}, + {"bill", "eshk"}, + {"msrooms", "mapseen"}, + {"mtrack", "monst"}, + {"ualignbase", "you"}, + {"weapon_skills", "you"}, +}; + +/* conditional code tags - eecch */ +const char *condtag[] = { +#ifdef SYSFLAGS + "sysflag","altmeta","#ifdef AMIFLUSH", "", + "sysflag","amiflush","","#endif /*AMIFLUSH*/", + "sysflag","numcols", "#ifdef AMII_GRAPHICS", "", + "sysflag","amii_dripens","","", + "sysflag","amii_curmap","","#endif", + "sysflag","fast_map", "#ifdef OPT_DISMAP", "#endif", + "sysflag","asksavedisk","#ifdef MFLOPPY","#endif", + "sysflag","page_wait", "#ifdef MAC", "#endif", +#endif + "linfo","where","#ifdef MFLOPPY","", + "linfo","time","","", + "linfo","size","","#endif /*MFLOPPY*/", + "obj","oinvis","#ifdef INVISIBLE_OBJECTS", "#endif", + (char *)0,(char *)0,(char *)0, (char *)0 +}; + +DISABLE_WARNING_UNREACHABLE_CODE + +int main(int argc, char *argv[]) +{ + tagcount = 0; +#if defined(WIN32) + char fbuf[_MAX_PATH]; +#endif + + folderprefix[0] = '\0'; + if (argc > 1) infilenm = argv[1]; + if (!infilenm || !*infilenm) infilenm = DEFAULTTAGNAME; + +#if defined(WIN32) + if (!file_exists(infilenm)) { + /* (void) GetCurrentDirectoryA(sizeof cdir, (LPSTR) cdir); */ + Strcpy(folderprefix, "../../../"); + (void) snprintf(fbuf, sizeof fbuf, "%s%s", folderprefix, DEFAULTTAGNAME); + if (!file_exists(fbuf)) { + fprintf(stdout, "%s doesn't exist\n", fbuf); + exit(EXIT_SUCCESS); + } + infilenm = (const char *) fbuf; + } +#endif + + infile = fopen(infilenm,"r"); + if (!infile) { + printf("%s not found or unavailable\n", infilenm); + quit(); + } else { + while (fgets(line, sizeof line, infile)) { + ++lineno; + /* if (lineno == 868) DebugBreak(); */ + doline(line); + } + + fclose(infile); + printf("\nRead in %ld lines and stored %d tags in memory.\n", lineno, + tagcount); +#if 0 + showthem(); +#endif + generate_c_files(); + printf("Created %s\n", SFDATA_NAME); + printf("Created %s\n", SFPROTO_NAME); + exit(EXIT_SUCCESS); + /*NOTREACHED*/ + return 0; + } +} + +#ifdef WIN32 +boolean +file_exists(const char *path) +{ + struct stat sb; + + /* Just see if it's there */ + if (stat(path, &sb)) { + return FALSE; + } + return TRUE; +} +#endif + +RESTORE_WARNINGS + +static void doline(char *aline) +{ + char buf[255], *cp; + struct tagstruct * ALIGN32 tmptag; + size_t slen; + + if (!aline || (aline && *aline == '!')) { + return; + } + cp = deeol(aline); + slen = strlen(cp); + if (slen > sizeof buf - 1) { + slen = sizeof buf - 1; + } + + tmptag = malloc(sizeof *tmptag); + if (!tmptag) { + out_of_memory(); + } + assert(tmptag != 0); + *tmptag = zerotag; + tmptag->marker = 0xDEADBEEF; + + strncpy(buf, cp, slen); + buf[sizeof buf - 1] = '\0'; + taglineparse(buf, tmptag); + chain(tmptag); + return; +} + +struct tagstruct * ALIGN32 prevtag = 0; + +static void +chain(struct tagstruct *tag) +{ + if (!first) { + tag->next = (struct tagstruct *) 0; + first = tag; + } else { + tag->next = (struct tagstruct *) 0; + + if (prevtag->marker == 0xDEADBEEF) { + prevtag->next = tag; + } else { + printf("Error - No previous tag at %s\n", tag->tag); + } + } + prevtag = tag; + ++tagcount; +} +static void quit(void) +{ + exit(EXIT_FAILURE); +} + +static void out_of_memory(void) +{ + printf("maketags: out of memory at line %ld of %s\n", + lineno, infilenm); + quit(); +} + +#if 0 +static char empt[] = {0, 0, 0, 0, 0, 0, 0, 0}; +#endif + +#if 0 +static char *member_array_dims(struct tagstruct *tmptag, char *buf) +{ + if (buf && tmptag) { + if (tmptag->arraysize1[0]) + Sprintf(buf, "[%s]", tmptag->arraysize1); + if (tmptag->arraysize2[0]) + Sprintf(eos(buf), "[%s]", tmptag->arraysize2); + return buf; + } + return empt; +} + +static char *member_array_size(struct tagstruct *tmptag, char *buf) +{ + if (buf && tmptag) { + if (tmptag->arraysize1[0]) + strcpy(buf, tmptag->arraysize1); + if (tmptag->arraysize2[0]) + Sprintf(eos(buf), " * %s", tmptag->arraysize2); + return buf; + } + return empt; +} +#endif + +void set_member_array_size(struct tagstruct *tmptag) +{ + char buf[BUFSZ]; + /* static char result[49]; */ + char *arr1 = (char *)0, *arr2 = (char *)0, *tmp; + int cnt = 0; + + if (!tmptag) return; + strcpy(buf, tmptag->searchtext); + + tmptag->arraysize1[0] = '\0'; + tmptag->arraysize2[0] = '\0'; + + /* find left-open square bracket */ + tmp = strchr(buf, '['); + if (tmp) { + arr1 = tmp; + *tmp = '\0'; + --tmp; + /* backup and make sure the [] are on the right tag */ + while (!(*tmp == SPACE || *tmp == TAB || *tmp ==',' || cnt > 50)) { + --tmp; + cnt++; + } + if (cnt > 50) return; + tmp++; + if (strcmp(tmp, tmptag->tag) == 0) { + ++arr1; + tmp = strchr(arr1, ']'); + if (tmp) { + arr2 = tmp; + ++arr2; + *tmp = '\0'; + if (*arr2 == '[') { /* two-dimensional array */ + ++arr2; + tmp = strchr(arr2, ']'); + if (tmp) *tmp = '\0'; + } else { + arr2 = (char *)0; + } + } + } else { + arr1 = (char *)0; + } + } + if (arr1) (void)strcpy(tmptag->arraysize1, arr1); + if (arr2) (void)strcpy(tmptag->arraysize2, arr2); +} + +static void parseExtensionFields (struct tagstruct *tmptag, char *buf) +{ + char *p = buf; +#ifdef WIN32 + int debug_catch = 0; +#endif + + while (p != (char *)0 && *p != '\0') { + while (*p == TAB) + *p++ = '\0'; + if (*p != '\0') { + char *colon; + char *field = p; + + p = strchr (p, TAB); + if (p != (char *)0) + *p++ = '\0'; + colon = strchr (field, ':'); + if (colon == (char *)0) { + tmptag->tagtype = *field; + } else { + const char *key = field; + const char *value = colon + 1; + *colon = '\0'; + if ((strcmp (key, "struct") == 0) || + (strcmp (key, "union") == 0)) { +#ifdef WIN32 + if (!strcmp(key, "union")) + debug_catch = 1; +#endif + colon = strstr(value,"::"); + if (colon) + value = colon +2; + strcpy(tmptag->parenttype, key); + strcpy(tmptag->parent, value); + } + } + } + } +} + +void +taglineparse(char *p, struct tagstruct *tmptag) +{ + int fieldsPresent = 0; + char *pattern = 0, *tmp1 = 0; + int linenumber = 0; + char *tab = strchr (p, TAB); + + if (tab != NULL) { + *tab = '\0'; + strcpy(tmptag->tag,p); + p = tab + 1; + tab = strchr (p, TAB); + if (tab != NULL) { + *tab = '\0'; + p = tab + 1; + if (*p == '/' || *p == '?') { + /* parse pattern */ + int delimiter = *(unsigned char *) p; + linenumber = 0; + pattern = p; + do { + p = strchr (p + 1, delimiter); + } while (p != (char *)0 && *(p - 1) == '\\'); + + if (p == (char *)0) { + /* invalid pattern */ + } else + ++p; + } else if (isdigit ((int) *(unsigned char *) p)) { + /* parse line number */ + pattern = p; + linenumber = atol(p); + while (isdigit((int) *(unsigned char *) p)) + ++p; + } else { + /* invalid pattern */ + } + fieldsPresent = (strncmp (p, ";\"", 2) == 0); + *p = '\0'; + + if (fieldsPresent) + parseExtensionFields (tmptag, p + 2); + } + } + assert(pattern != NULL); + + strcpy(tmptag->searchtext, pattern); + tmptag->linenum = linenumber; + + /* add the array dimensions */ + set_member_array_size(tmptag); + + /* determine if this is a pointer and mark it as such */ + if (tmptag->searchtext[0] && + (tmptag->tagtype == 'm' || tmptag->tagtype == 's')) { + char ptrbuf[BUFSZ], searchbuf[BUFSZ]; + + (void) strcpy(ptrbuf, tmptag->searchtext); + Sprintf(searchbuf,"*%s", tmptag->tag); + tmp1 = strstr(ptrbuf, searchbuf); + if (!tmp1) { + Sprintf(searchbuf,"* %s", tmptag->tag); + tmp1 = strstr(ptrbuf, searchbuf); + } + if (tmp1) { + while ((tmp1 > ptrbuf) && (*tmp1 != SPACE) && + (*tmp1 != TAB) && (*tmp1 != ',')) + tmp1--; + tmp1++; + while (*tmp1 == '*') + tmp1++; + *tmp1 = '\0'; + /* now find the first * before this in case multiple things + are declared on this line */ + tmp1 = strchr(ptrbuf+2, '*'); + if (tmp1) { + tmp1++; + *tmp1 = '\0'; + tmp1 = ptrbuf + 2; + while (*tmp1 == SPACE || *tmp1 == TAB || *tmp1 == ',') + ++tmp1; + (void)strcpy(tmptag->ptr, tmp1); + } + } + } +} + +static char stripbuf[255]; + +#if 0 +static char *stripspecial(char *st) +{ + char *out = stripbuf; + *out = '\0'; + if (!st) return st; + while(*st) { + if (*st >= SPACE) + *out++ = *st++; + else + st++; + } + *out = '\0'; + return stripbuf; +} +#endif + +static char *deblank(char *st) +{ + char *out = stripbuf; + *out = '\0'; + if (!st) return st; + while(*st) { + if (*st == SPACE) { + *out++ = '_'; + st++; + } else + *out++ = *st++; + } + *out = '\0'; + return stripbuf; +} + +static char *deeol(char *st) +{ + char *out = stripbuf; + *out = '\0'; + if (!st) return st; + while(*st) { + if ((*st == '\r') || (*st == '\n')) { + st++; + } else + *out++ = *st++; + } + *out = '\0'; + return stripbuf; +} + +#if 0 +static void showthem(void) +{ + char buf[BUFSZ], *tmp; + struct tagstruct *t = first; + while(t) { + printf("%-28s %c, %-16s %-10s", t->tag, t->tagtype, + t->parent, t->parenttype); +#if 0 + t->parent[0] ? t->searchtext : ""); +#endif + buf[0] = '\0'; + tmp = member_array_dims(t,buf); + if (tmp) printf("%s", tmp); + printf("%s","\n"); + t = t->next; + } +} +#endif + +#if 0 +static boolean +is_prim(char *sdt) +{ + int k = 0; + if (sdt) { + /* special case where we don't match entire thing */ + if (!strncmpi(sdt, "Bitfield",8)) + return TRUE; + for (k = 0; k < SIZE(readtagstypes); ++k) { + if (!strcmpi(readtagstypes[k].dtype, sdt)) { + if (readtagstypes[k].dtclass == NHTYPE_SIMPLE) + return TRUE; + else + return FALSE; + } + } + } + return FALSE; +} +#endif + +char * +findtype(char *st, char *tag) +{ + static char ftbuf[512]; + static char prevbuf[512]; + char *tmp1, *tmp2, *tmp3, *tmp4; + const char *r; + + if (!st) return (char *)0; + +#if 0 + if (st && strstr(st, "mapseen")) { + int xx = 0; + + xx++; + } +#endif + if (st[0] == '/' && st[1] == '^') { + tmp2 = tmp3 = tmp4 = (char *)0; + tmp1 = &st[3]; + while (*tmp1) { + if (isspace(*tmp1)) + ; /* skip it */ + else + break; + ++tmp1; + } + if (!strncmp(tmp1, tag, strlen(tag))) { + if(strlen(tag) == 1) { + char *sc = tmp1; + /* Kludge: single char match is too iffy, + check to make sure its a complete + token that we're comparing to. */ + ++sc; + if (!(*sc == '_' || (*sc > 'a' && *sc < 'z') || + (*sc > 'A' && *sc < 'Z') || (*sc > '0' && *sc < '9'))) + return (char *)0; + } else { + return (char *)0; + } + } + if (*tmp1) { + if (!strncmp(tmp1, "Bitfield", 8)) { + strcpy(ftbuf, tmp1); + tmp1 = ftbuf; + tmp3 = strchr(tmp1, ')'); + if (tmp3) { + tmp3++; + *tmp3 = '\0'; + return ftbuf; + } + return (char *)0; + } + } + if (*tmp1) { + int prevchar = 0; + strcpy(ftbuf, tmp1); + tmp1 = ftbuf; + /* find space separating first word with second */ + while (!isspace(*tmp1)) { + prevchar = *tmp1; + ++tmp1; + } + + prevchar = 0; + /* some oddball cases */ + if (prevchar == ',' || prevchar == ';') { + tmp3 = strchr(ftbuf, ','); + tmp2 = strstr(ftbuf, tag); + return prevbuf; + } else { + int chkcnt = 0; + + /* a comma means that more than one thing declared on ine */ + tmp3 = strchr(tmp1, ','); + while (chkcnt < 3 && (tmp2 = strstr(tmp1, tag)) + && (prevchar = *(tmp2 - 1)) + && ((prevchar == '_') + || (prevchar >= 'a' && prevchar <= 'z') + || (prevchar >= 'A' && prevchar <= 'Z') + || (prevchar >= '0' && prevchar <= '9'))) { + tmp1 = tmp2 + 1; + chkcnt++; + } + } + /* make sure we're matching a complete token */ + if (tmp2) { + tmp4 = tmp2 + strlen(tag); + if ((*tmp4 == '_') || (*tmp4 >= 'a' && *tmp4 <= 'z') || + (*tmp4 >= 'A' && *tmp4 <= 'Z') || (*tmp4 >= '0' && *tmp4 <= '9')) + /* jump to next occurence then */ + tmp2 = strstr(tmp4, tag); + } + /* tag w/o comma OR tag found w comma and tag before comma */ + if ((tmp2 && !tmp3) || ((tmp2 && tmp3) && (tmp2 < tmp3))) { + *tmp2 = '\0'; + --tmp2; + while (isspace(*tmp2)) + --tmp2; + tmp2++; + *tmp2 = '\0'; + } + /* comma and no tag OR tag w comma and comma before tag */ + else if ((tmp3 && !tmp2) || ((tmp2 && tmp3) && (tmp3 < tmp2))) { + --tmp3; + if (isspace(*tmp3)) { + while (isspace(*tmp3)) + --tmp3; + } + while (!isspace(*tmp3) && (*tmp3 != '*')) + --tmp3; + while (isspace(*tmp3)) + --tmp3; + tmp3++; + *tmp3 = '\0'; + } + /* comma or semicolon immediately following tag */ + else { + volatile int y = 0; + nhUse(y); + y = 1; + } + if (strncmpi(ftbuf, "struct ", 7) == 0) + r = (const char *) (ftbuf + 7); + else if (strncmpi(ftbuf, "union ", 6) == 0) + r = (const char *) (ftbuf + 6); + /* a couple of kludges follow unfortunately */ + else if (strncmpi(ftbuf, "coord", 5) == 0 + && strncmpi(ftbuf, "coordxy", 7) != 0) + r = "nhcoord"; + else if (strncmpi(ftbuf, "anything", 8) == 0) + r = "any"; + else if (strncmpi(ftbuf, "const char", 10) == 0) + r = "char"; + else + r = (const char *) ftbuf; + strcpy(prevbuf, r); + return prevbuf; + } + } + prevbuf[0] = '\0'; + return (char *)0; +} + +int current_type = 64, gotit = 0; + +boolean +listed(struct tagstruct *t) +{ + int k; + + if ((strncmpi(t->tag, "Bitfield", 8) == 0) || + (strcmpi(t->tag, "string") == 0)) + return TRUE; + for (k = 0; k < SIZE(readtagstypes); ++k) { + if (k == current_type) + gotit = k; + /* This needs to be case-sensitive to avoid generating collision + * between 'align' and 'Align'. + */ + if (strcmp(readtagstypes[k].dtype, t->tag) == 0) + return TRUE; + } + return FALSE; +} + +/* TIME_type: type of the argument to time(); we actually use &(time_t) */ +#if defined(BSD) && !defined(POSIX_TYPES) +#define TIME_type long * +#else +#define TIME_type time_t * +#endif +/* LOCALTIME_type: type of the argument to localtime() */ +#if (defined(ULTRIX) && !(defined(ULTRIX_PROTO) || defined(NHSTDC))) \ + || (defined(BSD) && !defined(POSIX_TYPES)) +#define LOCALTIME_type long * +#else +#define LOCALTIME_type time_t * +#endif + +const char *preamble[] = { + "/* Copyright (c) NetHack Development Team %d. */\n", + "/* NetHack may be freely redistributed. See license for details. */\n\n", + "/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE! */\n\n", + "#include \"hack.h\"\n", + "#include \"artifact.h\"\n", + "#include \"func_tab.h\"\n", + "#include \"integer.h\"\n", + "#include \"wintype.h\"\n", + (char *)0 +}; +char crbuf[BUFSZ]; + +DISABLE_WARNING_FORMAT_NONLITERAL + +static const char *get_preamble(int n) +{ + const char *r = preamble[n]; + + if (!n) { + time_t datetime = 0; + struct tm *lt; + + (void) time((TIME_type) &datetime); + lt = localtime((LOCALTIME_type) &datetime); + Sprintf(crbuf, preamble[0], (1900 + lt->tm_year)); + r = crbuf; + } + return r; +} + +RESTORE_WARNING_FORMAT_NONLITERAL + +static void output_types(FILE *fp1) +{ + int k, cnt /*, hcnt = 1 */; + struct tagstruct *t = first; + + Fprintf(fp1, "%s", + "struct nhdatatypes_t nhdatatypes[] = {\n"); + + for (k = 0; k < SIZE(readtagstypes); ++k) { + if (readtagstypes[k].dtclass == NHTYPE_SIMPLE) { + Fprintf(fp1,"\t{NHTYPE_SIMPLE, (char *) \"%s\", sizeof(%s)},\n", + readtagstypes[k].dtype, + (strncmpi(readtagstypes[k].dtype, "Bitfield", 8) == 0) ? + "uint8_t" : + (strcmpi(readtagstypes[k].dtype, "string") == 0) ? + "uchar" : + (strcmpi(readtagstypes[k].dtype, "any") == 0) ? + "anything" : readtagstypes[k].dtype); +/* dtmacro(readtagstypes[k].dtype,0)); */ +#if 0 + Fprintf(fp2, "#define %s\t%s%d\n", dtmacro(readtagstypes[k].dtype,1), + (strlen(readtagstypes[k].dtype) > 12) ? "" : + (strlen(readtagstypes[k].dtype) < 5) ? "\t\t" : + "\t", hcnt++); +#endif + } + } + cnt = 0; + while(t) { + if (listed(t) && ((t->tagtype == 's') || (t->tagtype == 'u'))) { + if (!strcmp(t->tag, "any")) { + t = t->next; + continue; + } + if (cnt > 0) + Fprintf(fp1, "%s", ",\n"); + Fprintf(fp1, "\t{NHTYPE_COMPLEX, (char *) \"%s\", sizeof(%s %s)}", + t->tag, + (t->tagtype == 's') ? "struct" : "union", t->tag); + cnt += 1; + } + t = t->next; + } + Fprintf(fp1, "%s", "\n};\n\n"); + Fprintf(fp1, "int nhdatatypes_size(void)\n{\n\treturn SIZE(nhdatatypes);\n}\n\n"); +} + +static void generate_c_files(void) +{ + struct tagstruct *t = first; +#ifdef KR1ED + long clocktim = 0; +#else + time_t clocktim = 0; +#endif + char *c, cbuf[60], sfparent[BUFSZ], funcnam[2][BUFSZ], *substruct, *gline, + norm_param_buf[BUFSZ]; + FILE *SFO_DATA, *SFI_DATA, *SFDATATMP, *SFO_PROTO, *SFI_PROTO, + *SFDATA, *SFPROTO, *SF_NORMALIZE_POINTERS; + int k = 0, j /*, opening, , closetag = 0 */; + const char *pt; + /* char *layout; */ + char *ft, *last_ft = (char *)0; + int okeydokey, x, a; + boolean did_i; + boolean normalize_param_used; + +#ifdef WIN32 + int debug_catch = 0; +#endif + + Snprintf(filenmbuf, sizeof filenmbuf, "%s%s", folderprefix,SFDATA_NAME); + SFDATA = fopen(filenmbuf, "w"); + if (!SFDATA) return; + + Snprintf(filenmbuf, sizeof filenmbuf, "%s%s", folderprefix, SFPROTO_NAME); + SFPROTO = fopen(filenmbuf, "w"); + if (!SFPROTO) return; + + Snprintf(filenmbuf, sizeof filenmbuf, "%s%s", folderprefix, + "../util/sfo_data.tmp"); + SFO_DATA = fopen(filenmbuf, "w"); + if (!SFO_DATA) return; + + Snprintf(filenmbuf, sizeof filenmbuf, "%s%s", folderprefix, "../include/sfo_proto.tmp"); + SFO_PROTO = fopen(filenmbuf, "w"); + if (!SFO_PROTO) return; + + Snprintf(filenmbuf, sizeof filenmbuf, "%s%s", folderprefix, + "../include/sfi_proto.tmp"); + SFI_PROTO = fopen(filenmbuf, "w"); + if (!SFI_PROTO) return; + + Snprintf(filenmbuf, sizeof filenmbuf, "%s%s", folderprefix, + "../util/sfi_data.tmp"); + SFI_DATA = fopen(filenmbuf, "w"); + if (!SFI_DATA) return; + + Snprintf(filenmbuf, sizeof filenmbuf, "%s%s", folderprefix, + "../util/sfdata.tmp"); + SFDATATMP = fopen(filenmbuf, "w"); + if (!SFDATATMP) return; + + Snprintf(filenmbuf, sizeof filenmbuf, "%s%s", folderprefix, + "../util/sfnormptrs.tmp"); + SF_NORMALIZE_POINTERS = fopen(filenmbuf, "w"); + if (!SF_NORMALIZE_POINTERS) return; + + (void) time(&clocktim); + Strcpy(cbuf, ctime(&clocktim)); + + for (c = cbuf; *c; c++) + if (*c == '\n') + break; + *c = '\0'; /* strip off the '\n' */ + + /* begin sfproto.h */ + Fprintf(SFPROTO,"/* NetHack %d.%d sfproto.h */\n", + VERSION_MAJOR, VERSION_MINOR); + for (j = 0; j < 3; ++j) + Fprintf(SFPROTO, "%s", get_preamble(j)); + Fprintf(SFPROTO, "#ifndef SFPROTO_H\n#define SFPROTO_H\n\n"); + Fprintf(SFPROTO, "#include \"hack.h\"\n#include \"integer.h\"\n#include \"wintype.h\"\n\n"); + + Fprintf(SFPROTO,"%s\n", "extern int critical_members_count(void);"); + Fprintf(SFPROTO,"%s\n", "extern void sfo_bitfield(NHFILE *, uint8_t *, const char *, int);"); + Fprintf(SFPROTO,"%s\n", "extern void sfi_bitfield(NHFILE *, uint8_t *, const char *, int);"); + Fprintf(SFO_PROTO, "/* generated output functions */\n"); + Fprintf(SFI_PROTO, "/* generated input functions */\n"); + + /* begin sfdata.c */ + Fprintf(SFDATA,"/* NetHack %d.%d sfdata.c */\n", + VERSION_MAJOR, VERSION_MINOR); + for (j = 0; preamble[j]; ++j) + Fprintf(SFDATA, "%s", get_preamble(j)); + Fprintf(SFDATA, "#include \"sfprocs.h\"\n"); + Fprintf(SFDATA, "#include \"sfproto.h\"\n\n"); + Fprintf(SFDATA, "#define NHTYPE_SIMPLE 1\n"); + Fprintf(SFDATA, "#define NHTYPE_COMPLEX 2\n\n"); + Fprintf(SFDATA, "struct nhdatatypes_t {\n"); + Fprintf(SFDATA, " uint dtclass;\n"); + Fprintf(SFDATA, " char *dtype;\n"); + Fprintf(SFDATA, " size_t dtsize;\n};\n\n"); + Fprintf(SFDATA,"static uint8_t bitfield = 0;\n"); + + /* begin sfnormptrs.tmp */ + Fprintf(SF_NORMALIZE_POINTERS, + "void norm_ptrs_any(union any *d_any);\n\n"); + Fprintf(SF_NORMALIZE_POINTERS, + "void\n" + "norm_ptrs_any(union any *d_any UNUSED)\n" + "{\n" + "}\n"); + + output_types(SFDATATMP); + Fprintf(SFDATATMP, "const char *critical_members[] = {\n"); + + k = 0; + did_i = FALSE; + while(k < SIZE(readtagstypes)) { + boolean insert_const = FALSE; + suppress_count = TRUE; + + if (readtagstypes[k].dtclass == NHTYPE_COMPLEX) { + + + if (!strncmpi(readtagstypes[k].dtype,"Bitfield",8)) { + Fprintf(SFO_DATA, + "\nvoid\nsfo_bitfield(nhfp,\n" + "NHFILE *nhfp,\n" + "uint8_t *d_bitfield,\n" + "const char *myname\n" + "int bflen)\n" + "{\n"); + + Fprintf(SFI_DATA, + "\nvoid\nsfi_bitfield(\n" + "NHFILE *nhfp,\n" + "uint8_t *d_bitfield,\n" + "const char *myname\n" + "int bflen)\n" + "{\n"); + } + + +#if 0 + if (!strncmpi(readtagstypes[k].dtype,"version_info",8)) + insert_const = TRUE; +#endif +#ifdef WIN32 + if (!strncmpi(readtagstypes[k].dtype, "vlaunchinfo", 11) + || !strncmpi(readtagstypes[k].dtype, "vptrs", 5) + || k == SIZE(readtagstypes) - 2) + debug_catch = TRUE; +#endif + + pt = (const char *) 0; + t = first; + while(t) { + if (!strcmp(t->tag, readtagstypes[k].dtype)) { + pt = (t->tagtype == 'u') ? "union" : "struct"; + break; + } + t = t->next; + } + if (!pt) { + pt = "struct"; + } + + (void) snprintf(funcnam[0], sizeof funcnam[0], "sfo_x_%s", readtagstypes[k].dtype); + Fprintf(SFO_PROTO, + "extern void %s(NHFILE *, %s%s %s *, const char *);\n", + fn(funcnam[0]), + insert_const ? "const " : "", + pt, readtagstypes[k].dtype); + + Fprintf(SFO_DATA, + "\nvoid\n%s(\n" + "NHFILE *nhfp,\n" + "%s%s %s *d_%s,\n" + "const char *myname)\n" + "{\n", + fn(funcnam[0]), +/* deblank(readtagstypes[k].dtype), */ + insert_const ? "const " : "", + pt, readtagstypes[k].dtype, + deblank(readtagstypes[k].dtype)); + +#if 0 + Fprintf(SFO_DATA, + " const char *parent = \"%s\";\n", + readtagstypes[k].dtype); +#endif + + (void) snprintf(funcnam[0], sizeof funcnam[0], "sfi_x_%s", + readtagstypes[k].dtype); + Fprintf(SFI_PROTO, + "extern void %s(NHFILE *, %s%s %s *, const char *);\n", + fn(funcnam[0]), + insert_const ? "const " : "", + pt, readtagstypes[k].dtype); + + Fprintf(SFI_DATA, + "\nvoid\n%s(\n" + "NHFILE *nhfp,\n" + "%s%s %s *d_%s,\n" + "const char *myname)\n" + "{\n", + fn(funcnam[0]), +/* deblank(readtagstypes[k].dtype), */ + insert_const ? "const " : "", + pt, readtagstypes[k].dtype, + deblank(readtagstypes[k].dtype)); + +#if 0 + Fprintf(SFI_DATA, + " const char *parent = \"%s\";\n", + readtagstypes[k].dtype); +#endif + + Sprintf(sfparent, "%s %s", pt, readtagstypes[k].dtype); + + + Fprintf(SF_NORMALIZE_POINTERS, + "\nvoid norm_ptrs_%s(%s " + "*d_%s);\n", + readtagstypes[k].dtype, sfparent, readtagstypes[k].dtype); + Fprintf(SF_NORMALIZE_POINTERS, + "\nvoid\nnorm_ptrs_%s(%s " + "*d_%s)\n{\n", + readtagstypes[k].dtype, sfparent, readtagstypes[k].dtype); + Snprintf(norm_param_buf, sizeof norm_param_buf, "d_%s", + readtagstypes[k].dtype); + + for (a = 0; a < SIZE(nah); ++a) { + if (!strcmp(nah[a].parent, readtagstypes[k].dtype)) { + if (!did_i) { + Fprintf(SFO_DATA, " int i;\n"); + Fprintf(SFI_DATA, " int i;\n"); + did_i = TRUE; + } + } + } + + Fprintf(SFO_DATA, "\n"); + Fprintf(SFI_DATA, "\n"); + + Fprintf(SFO_DATA, " nhUse(myname);\n"); + Fprintf(SFI_DATA, " nhUse(myname);\n"); + Fprintf(SFO_DATA, "\n"); + Fprintf(SFI_DATA, "\n"); + + /******************************************************** + * cycle through all the tags and find every tag with * + * a parent matching readtagstypes[k].dtype * + ********************************************************/ + + t = first; + normalize_param_used = FALSE; + + while(t) { + x = 0; + okeydokey = 0; +/* + if (!strcmp(t->tag, "nextc") + && !strcmp(readtagstypes[k].dtype, "engrave_info")) + __debugbreak(); +*/ + + if (t->tagtype == 's') { + char *ss = strstr(t->searchtext,"{$/"); + + if (ss) { + strcpy(ssdef, t->tag); + } + t = t->next; + continue; + } + + /************insert opening conditional if needed ********/ + while(condtag[x]) { + if (!strcmp(condtag[x],readtagstypes[k].dtype) && + !strcmp(condtag[x+1],t->tag)) { + okeydokey = 1; + break; + } + x = x + 4; + } + + /* some structs are entirely defined within another struct declaration. + * Legal, but a greater challenge for us here. + */ + substruct = strstr(t->parent, "::"); + if (substruct) { + substruct += 2; + } + + if ((strcmp(readtagstypes[k].dtype, t->parent) == 0) || + (substruct && strcmp(readtagstypes[k].dtype, substruct) == 0)) { + ft = (char *)0; + + if (t->ptr[0] != '\0') + ft = &t->ptr[0]; + else + ft = findtype(t->searchtext, t->tag); + + if (ft) { + last_ft = ft; + if (okeydokey && condtag[x+2] && strlen(condtag[x+2]) > 0) { + Fprintf(SFO_DATA,"%s\n", condtag[x+2]); + Fprintf(SFI_DATA,"%s\n", condtag[x+2]); + Fprintf(SFDATATMP,"%s\n", condtag[x+2]); + } + } else { + /* use the last found one as last resort then */ + ft = last_ft; + } + + /***************** Bitfield *******************/ + if (!strncmpi(ft, "Bitfield", 8)) { + char lbuf[BUFSZ]; + int j2, z; + + Sprintf(lbuf, + " " + "bitfield = d_%s->%s;", + readtagstypes[k].dtype, t->tag); + z = (int) strlen(lbuf); + for (j2 = 0; j2 < (65 - z); ++j2) + Strcat(lbuf, " "); + Sprintf(eos(lbuf), "/* (%s) */\n", ft); + Fprintf(SFO_DATA, "%s", lbuf); + Fprintf(SFO_DATA, + " " + "sfo_bitfield(nhfp, &bitfield, \"%s\", %s);\n", + t->tag, bfsize(ft)); +#if 0 + Fprintf(SFI_DATA, + " " + "bitfield = 0;\n"); +#else + Fprintf(SFI_DATA, + " " + "bitfield = d_%s->%s; /* set it to current value for testing */\n", + readtagstypes[k].dtype, t->tag); +#endif + Fprintf(SFI_DATA, + " " + "sfi_bitfield(nhfp, &bitfield, \"%s\", %s);\n", + t->tag, bfsize(ft)); + + Fprintf(SFI_DATA, + " " + "d_%s->%s = bitfield;\n\n", + readtagstypes[k].dtype, t->tag); + Fprintf(SFDATATMP, + "\t\"%s:%s:%s\",\n", + sfparent, t->tag, ft); + } else { + /**************** not a bitfield ****************/ + char arrbuf[BUFSZ]; + char lbuf[BUFSZ * 2]; /* sprintf target for others, gcc + complaint */ + char fnbuf[BUFSZ]; + char altbuf[BUFSZ]; + boolean isptr = FALSE, kludge_sbrooms = FALSE, array_of_ptrs = FALSE; + boolean insert_loop = FALSE; + int j2, z; + + altbuf[0] = '\0'; + /*************** kludge for sbrooms *************/ + if (!strcmp(t->tag, "sbrooms")) { + kludge_sbrooms = TRUE; + (void) strcpy(t->arraysize1, "MAX_SUBROOMS"); + insert_loop = TRUE; + array_of_ptrs = TRUE; + } + if (!strcmp(t->parent, "engr") + && !strcmp(t->tag, "engr_txt")) { + (void) strcpy(t->arraysize1, "text_states"); + insert_loop = TRUE; + array_of_ptrs = TRUE; + } + if (t->arraysize2[0]) { + Sprintf(arrbuf, "(%s * %s)", t->arraysize1, + t->arraysize2); + isptr = TRUE; /* suppress the & in function args */ + } else if (t->arraysize1[0]) { + Sprintf(arrbuf, "%s", t->arraysize1); + isptr = TRUE; /* suppress the & in function args */ + } else { + Strcpy(arrbuf, "1"); + } + Strcpy(fnbuf, dtfn(ft, 0, &isptr)); + /* + * determine if this is one of the special cases + * where there's an array of structs instead of + * an array of simple types. We need to insert + * a for loop in those cases. + */ + for (a = 0; a < SIZE(nah); ++a) { + if (!strcmp(nah[a].parent, t->parent)) + if (!strcmp(nah[a].nm, t->tag)) + insert_loop = TRUE; + } + if (isptr && !strcmp(fnbuf, readtagstypes[k].dtype)) { + Strcpy(altbuf, "genericptr"); + } else if (isptr + && (!strcmp(t->ptr, "struct permonst *") + || !strcmp(t->ptr, "struct monst *") + || !strcmp(t->ptr, "struct obj *") + || !strcmp(t->ptr, "struct cemetery *") + || !strcmp(t->ptr, "struct container *") + || !strcmp(t->ptr, "struct mextra *") + || !strcmp(t->ptr, "struct oextra *") + || !strcmp(t->ptr, "struct s_level *") + || !strcmp(t->ptr, "struct bill_x *") + || !strcmp(t->ptr, "struct trap *") + || !strcmp(t->ptr, "struct egd *") + || !strcmp(t->ptr, "struct epri *") + || !strcmp(t->ptr, "struct eshk *") + || !strcmp(t->ptr, "struct emin *") + || !strcmp(t->ptr, "struct ebones *") + || !strcmp(t->ptr, "struct edog *"))) { + Strcpy(altbuf, "genericptr"); + } else if (isptr + && (!strcmp(t->parent, "engr") + && !strcmp(t->tag, "engr_txt"))) { + Strcpy(altbuf, "genericptr"); + } else if (isptr + && (!strcmp(t->parent, "mapseen") + && !strcmp(t->tag, "br"))) { + Strcpy(altbuf, "genericptr"); + + // } else if (isptr + // && + // (!strcmp(t->parent, + // "engrave_info") + // && + // !strcmp(t->tag, + // "nextc"))) { + // Strcpy(altbuf, + // "genericptr"); + + } else if ( + (isptr && !strcmp(t->ptr, "char *")) + && ((!strcmp(t->parent, "engrave_info") + && !strcmp(t->tag, "nextc")) + || (!strcmp(t->parent, "mapseen") + && !strcmp(t->tag, "custom")) + || (!strcmp(t->parent, "mapseen") + && !strcmp(t->tag, "custom")) + || (!strcmp(t->parent, "mextra") + && !strcmp(t->tag, "mgivenname")) + || (!strcmp(t->parent, "gamelog_line") + && !strcmp(t->tag, "text")) + || (!strcmp(t->parent, "oextra") + && !strcmp(t->tag, "oname")) + || (!strcmp(t->parent, "oextra") + && !strcmp(t->tag, "omailcmd")))) { + Strcpy(altbuf, "genericptr"); + } else if (isptr && !strcmp(t->tag, "oc_uname")) { + Strcpy(altbuf, "genericptr"); + } else { + Strcpy(altbuf, fnbuf); + } + if (isptr && (strcmp(altbuf, "genericptr") != 0) + && (t->ptr[0] != 0 + && (*(t->ptr + (strlen(t->ptr) - 1)) == '*') + && (strcmp(t->ptr, altbuf) != 0))) { + fprintf( + stderr, + "WARNING - \"%s\" in %s called \"%s\" " + "resulted in an unexpected set of inputs/outputs " + "(%s)\n", + (t->ptr[0] != 0 && strlen(t->ptr)) ? t->ptr : "unknown", + (t->parent[0] != 0 && strlen(t->parent)) ? t->parent + : "?", + (t->tag[0] != 0 && strlen(t->tag)) ? t->tag : "?", + altbuf); + } + /* kludge for attribs */ + if (!strcmp(readtagstypes[k].dtype, "attribs") + && !strcmp(arrbuf, "A_MAX")) { + insert_loop = TRUE; + } + if (insert_loop) { + Fprintf(SFO_DATA, " for (%si = 0; i < %s; ++i)\n", + did_i ? "" : "int ", arrbuf); + Fprintf(SFI_DATA, " for (%si = 0; i < %s; ++i)\n", + did_i ? "" : "int ", arrbuf); + if (array_of_ptrs) { + Fprintf(SF_NORMALIZE_POINTERS, + " for (%si = 0; i < %s; ++i)\n", + did_i ? "" : "int ", arrbuf); + } + arrbuf[0] = '1'; + arrbuf[1] = '\0'; + } + if (isptr + && (t->ptr[0] != 0 + && (*(t->ptr + (strlen(t->ptr) - 1)) == '*') + && (strcmp(t->ptr, altbuf) != 0))) { + Fprintf( + SF_NORMALIZE_POINTERS, + " %sd_%s->%s%s%s%s = d_%s->%s%s%s%s ? (%s) 1 : (%s) 0;\n", + (insert_loop && array_of_ptrs) ? " " : "", + t->parent, t->tag, + (insert_loop && array_of_ptrs) ? "[" : "", + (insert_loop && array_of_ptrs) ? "i" : "", + (insert_loop && array_of_ptrs) ? "]" : "", + t->parent, t->tag, + (insert_loop && array_of_ptrs) ? "[" : "", + (insert_loop && array_of_ptrs) ? "i" : "", + (insert_loop && array_of_ptrs) ? "]" : "", + t->ptr, + t->ptr); + normalize_param_used = TRUE; + } + Snprintf(lbuf, sizeof lbuf, + " " + "%ssfo%s_%s(nhfp, %s%sd_%s->%s%s, \"%s\"%s%s);", + insert_loop ? " " : "", + (readtagstypes[k].dtclass == NHTYPE_SIMPLE || no_x(altbuf)) ? "" + : "_x", + altbuf, + (isptr && !strcmp(altbuf, "genericptr")) + ? "(genericptr_t) " + : "", + (isptr && !insert_loop && !kludge_sbrooms + && strcmp(altbuf, "genericptr")) + ? "" + : "&", + readtagstypes[k].dtype, t->tag, + insert_loop ? "[i]" + : "", + t->tag, strcmp(altbuf, "char") != 0 ? "" : ", ", + strcmp(altbuf, "char") != 0 ? "" : arrbuf); + /* align comments */ + z = (int) strlen(lbuf); + for (j2 = 0; j2 < (65 - z); ++j2) + Strcat(lbuf, " "); + Sprintf(eos(lbuf), "/* (%s) */\n", ft); + Fprintf(SFO_DATA, "%s", lbuf); + + Snprintf( + lbuf, sizeof lbuf, + " " + "%ssfi%s_%s(nhfp, %s%sd_%s->%s%s, \"%s\"%s%s);\n", + insert_loop ? " " : "", + (readtagstypes[k].dtclass == NHTYPE_SIMPLE + || no_x(altbuf)) + ? "" + : "_x", + altbuf, + (isptr && !strcmp(altbuf, "genericptr")) + ? "(genericptr_t) " + : "", + (isptr && !insert_loop && !kludge_sbrooms + && strcmp(altbuf, "genericptr")) + ? "" + : "&", + readtagstypes[k].dtype, t->tag, + kludge_sbrooms ? "[0]" + : insert_loop ? "[i]" + : "", + t->tag, strcmp(altbuf, "char") != 0 ? "" : ", ", + strcmp(altbuf, "char") != 0 ? "" : arrbuf); + Fprintf(SFI_DATA, "%s", lbuf); + Fprintf(SFDATATMP, + "\t\"%s:%s:%s\",\n", + sfparent, t->tag,fieldfix(ft,ssdef)); + kludge_sbrooms = FALSE; + array_of_ptrs = FALSE; + altbuf[0] = '\0'; + } + + /************insert closing conditional if needed ********/ + if (okeydokey && condtag[x+3] && strlen(condtag[x+3]) > 0) { + Fprintf(SFO_DATA,"%s\n", condtag[x+3]); + Fprintf(SFI_DATA,"%s\n", condtag[x+3]); + Fprintf(SFDATATMP,"%s\n", condtag[x+3]); + } + } + t = t->next; + } + + Fprintf(SFO_DATA, "\n"); + Fprintf(SFI_DATA, "\n"); + + Fprintf(SFO_DATA, "}\n"); + Fprintf(SFI_DATA, "}\n"); + if (!normalize_param_used) { + Fprintf(SF_NORMALIZE_POINTERS, " nhUse(%s);\n", + norm_param_buf); + } + Fprintf(SF_NORMALIZE_POINTERS, "}\n"); + } + ++k; + did_i = FALSE; + } + + Fprintf(SFDATATMP,"};\n\n"); + Fprintf(SFDATATMP, "int critical_members_count(void)\n{\n\treturn SIZE(critical_members);\n}\n\n"); + + fclose(SFO_DATA); + fclose(SFI_DATA); + fclose(SFO_PROTO); + fclose(SFI_PROTO); + fclose(SFDATATMP); + fclose(SF_NORMALIZE_POINTERS); + + /* Consolidate SFO_* and SFI_* etc into single files */ + + Snprintf(filenmbuf, sizeof filenmbuf, "%s%s", folderprefix, + "../util/sfo_data.tmp"); + SFO_DATA = fopen(filenmbuf, "r"); + if (!SFO_DATA) return; + while ((gline = fgetline(SFO_DATA)) != 0) { + (void) fputs(gline, SFDATA); + free(gline); + } + (void) fclose(SFO_DATA); + (void) remove(filenmbuf); + + Snprintf(filenmbuf, sizeof filenmbuf, "%s%s", folderprefix, + "../util/sfi_data.tmp"); + SFI_DATA = fopen(filenmbuf, "r"); + if (!SFI_DATA) return; + while ((gline = fgetline(SFI_DATA)) != 0) { + (void) fputs(gline, SFDATA); + free(gline); + } + (void) fclose(SFI_DATA); + (void) remove(filenmbuf); + + Snprintf(filenmbuf, sizeof filenmbuf, "%s%s", folderprefix, + "../include/sfo_proto.tmp"); + SFO_PROTO = fopen(filenmbuf, "r"); + if (!SFO_PROTO) return; + while ((gline = fgetline(SFO_PROTO)) != 0) { + (void) fputs(gline, SFPROTO); + free(gline); + } + (void) fclose(SFO_PROTO); + (void) remove(filenmbuf); + + Snprintf(filenmbuf, sizeof filenmbuf, "%s%s", folderprefix, + "../include/sfi_proto.tmp"); + SFI_PROTO = fopen(filenmbuf, "r"); + if (!SFI_PROTO) return; + while ((gline = fgetline(SFI_PROTO)) != 0) { + (void) fputs(gline, SFPROTO); + free(gline); + } + (void) fclose(SFI_PROTO); + (void) remove(filenmbuf); + + Snprintf(filenmbuf, sizeof filenmbuf, "%s%s", folderprefix, + "../util/sfdata.tmp"); + SFDATATMP = fopen(filenmbuf, "r"); + if (!SFDATATMP) return; + while ((gline = fgetline(SFDATATMP)) != 0) { + (void) fputs(gline, SFDATA); + free(gline); + } + (void) fclose(SFDATATMP); + (void) remove(filenmbuf); + + Snprintf(filenmbuf, sizeof filenmbuf, "%s%s", folderprefix, + "../util/sfnormptrs.tmp"); + SF_NORMALIZE_POINTERS = fopen(filenmbuf, "r"); + if (!SF_NORMALIZE_POINTERS) + return; + while ((gline = fgetline(SF_NORMALIZE_POINTERS)) != 0) { + (void) fputs(gline, SFDATA); + free(gline); + } + (void) fclose(SF_NORMALIZE_POINTERS); + (void) remove(filenmbuf); + + Fprintf(SFDATA, "/*sfdata.c*/\n"); + Fprintf(SFPROTO,"#endif /* SFPROTO_H */\n"); + (void) fclose(SFDATA); + (void) fclose(SFPROTO); +} + +#if 0 +static char * +dtmacro(const char *str, + int n) /* 1 = supress appending |SF_PTRMASK */ +{ + static char buf[128], buf2[128]; + char *nam, *c; + int ispointer = 0; + + if (!str) + return (char *)0; + (void)strncpy(buf, str, 127); + + c = buf; + while (*c) + c++; /* eos */ + + c--; + if (*c == '*') { + ispointer = 1; + *c = '\0'; + c--; + } + while(isspace(*c)) { + c--; + } + *(c+1) = '\0'; + c = buf; + + if (strncmpi(c, "Bitfield", 8) == 0) { + *(c+8) = '\0'; + } else if (strcmpi(c, "genericptr_t") == 0) { + ispointer = 1; + } else if (strncmpi(c, "const ", 6) == 0) { + c = buf + 6; + } else if ((strncmpi(c, "struct ", 7) == 0) || + (strncmpi(c, "struct\t", 7) == 0)) { + c = buf + 7; + } else if (strncmpi(c, "union ", 6) == 0) { + c = buf + 6; + } + + /* end of substruct within struct definition */ + if (strcmp(buf,"}") == 0 && strlen(ssdef) > 0) { + strcpy(buf,ssdef); + c = buf; + } + + for (nam = c; *c; c++) { + if (*c >= 'a' && *c <= 'z') + *c -= (char)('a' - 'A'); + else if (*c < 'A' || *c > 'Z') + *c = '_'; + } + (void)sprintf(buf2, "SF_%s%s", nam, + (ispointer && (n == 0)) ? " | SF_PTRMASK" : ""); + return buf2; +} +#endif + +static char * +dtfn(const char *str, + int n, /* 1 = supress appending |SF_PTRMASK */ + boolean *isptr) +{ + static char buf[128], buf2[128]; + const char *nam; + char *c; + int ispointer = 0; + + if (!str) + return (char *)0; + (void)strncpy(buf, str, 127); + buf[sizeof buf - 1] = '\0'; + + c = buf; + while (*c) c++; /* eos */ + + c--; + if (*c == '*') { + ispointer = 1; + *c = '\0'; + c--; + } + while(isspace(*c)) { + c--; + } + *(c+1) = '\0'; + c = buf; + + if (strncmpi(c, "Bitfield", 8) == 0) { + *(c+8) = '\0'; + } else if (strcmpi(c, "genericptr_t") == 0) { + ispointer = 1; + } else if (strncmpi(c, "const ", 6) == 0) { + c = buf + 6; + } else if ((strncmpi(c, "struct ", 7) == 0) || + (strncmpi(c, "struct\t", 7) == 0)) { + c = buf + 7; + } else if (strncmpi(c, "union ", 6) == 0) { + c = buf + 6; + } + + /* end of substruct within struct definition */ + if (strcmp(buf,"}") == 0 && strlen(ssdef) > 0) { + strcpy(buf,ssdef); + c = buf; + } + + for (nam = (const char *) c; *c; c++) { + if (*c >= 'A' && *c <= 'Z') + *c = tolower(*c); + else if (*c == ' ') + *c = '_'; + } + /* some fix-ups */ + if (!strcmp(nam, "genericptr_t")) + nam = "genericptr"; + else if (!strcmp(nam, "unsigned_int")) + nam = "uint"; + else if (!strcmp(nam, "unsigned_long")) + nam = "ulong"; + else if (!strcmp(nam, "unsigned_char")) + nam = "uchar"; + else if (!strcmp(nam, "unsigned_short")) + nam = "ushort"; + + if (ispointer && isptr && n == 0) + *isptr = TRUE; + (void)sprintf(buf2, "%s%s", nam, ""); + return buf2; +} + +static char * +fieldfix(char *f, char *ss) +{ + char *c /*, *dest = fieldfixbuf */; + + if (strcmp(f,"}") == 0 && strlen(ss) > 0 && strlen(ss) < BUFSZ - 1) { + /* (void)sprintf(fieldfixbuf,"struct %s", ss); */ + strcpy(fieldfixbuf,ss); + } else { + if (strlen(f) < BUFSZ - 1) strcpy(fieldfixbuf,f); + } + + /* converting any tabs to space */ + for (c = fieldfixbuf; *c; c++) + if (*c == TAB) *c = SPACE; + + return fieldfixbuf; +} + +static char * +bfsize(const char *str) +{ + static char buf[128]; + const char *c1; + char *c2, *subst; + + if (!str) + return (char *)0; + + /* kludge */ + subst = strstr(str, ",$/"); + if (subst != 0) { + subst++; + *subst++ = ' '; + *subst++ = '1'; + } + + c2 = buf; + c1 = str; + while (*c1) { + if (*c1 == ',') + break; + c1++; + } + + if (*c1 == ',') { + c1++; + while (*c1 && *c1 != ')') { + *c2++ = *c1++; + } + *c2 = '\0'; + } else { + return (char *)0; + } + return buf; +} + +/* Read one line from input, up to and including the next newline + * character. Returns a pointer to the heap-allocated string, or a + * null pointer if no characters were read. + */ +static char * +fgetline(FILE *fd) +{ + static const int inc = 256; + int len = inc; + char *c = malloc(len), *ret, *loc, *avoidleak; + + if (!c) { + fprintf(stderr, "Memory issue\n"); + quit(); + } + for (;;) { + loc = c + len - inc; + if (!loc) { + quit(); + } + ret = fgets(loc, inc, fd); + if (!ret) { + free(c); + c = NULL; + break; + } else if (strchr(c, '\n')) { + /* normal case: we have a full line */ + break; + } + len += inc; + avoidleak = c; + c = realloc(c, len); + if (!c) { + free(avoidleak); + quit(); + } + } + return c; +} + +#ifndef _MSC_VER +int +strncmpi(const char *s1, const char *s2, size_t n) +{ + register char t1, t2; + + while (n--) { + if (!*s2) + return (*s1 != 0); /* s1 >= s2 */ + else if (!*s1) + return -1; /* s1 < s2 */ + t1 = lowc(*s1++); + t2 = lowc(*s2++); + if (t1 != t2) + return (t1 > t2) ? 1 : -1; + } + return 0; /* s1 == s2 */ +} +#endif + + +struct already_in_sfbase { + int typ; + const char *actual; + const char *replacement; +}; + +/* The ones that can't be broken down into subfields + * are NHTYPE_SIMPLE. + */ +const struct already_in_sfbase already[] = { + /* input */ + { NHTYPE_SIMPLE, "Sfi_any", "Sfi_any" }, + { NHTYPE_SIMPLE, "Sfi_boolean", "Sfi_boolean" }, + { NHTYPE_SIMPLE, "Sfi_char", "Sfi_char" }, + { NHTYPE_SIMPLE, "Sfi_coordxy", "Sfi_coordxy" }, + { NHTYPE_SIMPLE, "Sfi_int16", "Sfi_int16" }, + { NHTYPE_SIMPLE, "Sfi_int32", "Sfi_int32" }, + { NHTYPE_SIMPLE, "Sfi_long", "Sfi_long" }, + { NHTYPE_SIMPLE, "Sfi_nhcoord", "Sfi_nhcoord" }, + { NHTYPE_SIMPLE, "Sfi_schar", "Sfi_schar" }, + { NHTYPE_SIMPLE, "Sfi_uint32", "Sfi_uint32" }, + { NHTYPE_SIMPLE, "Sfi_ulong", "Sfi_ulong" }, + { NHTYPE_SIMPLE, "Sfi_xint8", "Sfi_xint8" }, + { NHTYPE_COMPLEX, "Sfi_arti_info", "Sfi_x_arti_info" }, + { NHTYPE_COMPLEX, "Sfi_branch", "Sfi_x_branch" }, + { NHTYPE_COMPLEX, "Sfi_bubble", "Sfi_x_bubble" }, + { NHTYPE_COMPLEX, "Sfi_cemetery", "Sfi_x_cemetery" }, + { NHTYPE_COMPLEX, "Sfi_context_info", "Sfi_x_context_info" }, + { NHTYPE_COMPLEX, "Sfi_d_level", "Sfi_x_d_level" }, + { NHTYPE_COMPLEX, "Sfi_damage", "Sfi_x_damage" }, + { NHTYPE_COMPLEX, "Sfi_dest_area", "Sfi_x_dest_area" }, + { NHTYPE_COMPLEX, "Sfi_dgn_topology", "Sfi_x_dgn_topology" }, + { NHTYPE_COMPLEX, "Sfi_dungeon", "Sfi_x_dungeon" }, + { NHTYPE_COMPLEX, "Sfi_ebones", "Sfi_x_ebones" }, + { NHTYPE_COMPLEX, "Sfi_edog", "Sfi_x_edog" }, + { NHTYPE_COMPLEX, "Sfi_egd", "Sfi_x_egd" }, + { NHTYPE_COMPLEX, "Sfi_emin", "Sfi_x_emin" }, + { NHTYPE_COMPLEX, "Sfi_engr", "Sfi_x_engr" }, + { NHTYPE_COMPLEX, "Sfi_epri", "Sfi_x_epri" }, + { NHTYPE_COMPLEX, "Sfi_eshk", "Sfi_x_eshk" }, + { NHTYPE_COMPLEX, "Sfi_fe", "Sfi_x_fe" }, + { NHTYPE_COMPLEX, "Sfi_flag", "Sfi_x_flag" }, + { NHTYPE_COMPLEX, "Sfi_fruit", "Sfi_x_fruit" }, + { NHTYPE_COMPLEX, "Sfi_gamelog_line", "Sfi_x_gamelog_line" }, + { NHTYPE_COMPLEX, "Sfi_kinfo", "Sfi_x_kinfo" }, + { NHTYPE_COMPLEX, "Sfi_levelflags", "Sfi_x_levelflags" }, + { NHTYPE_COMPLEX, "Sfi_linfo", "Sfi_x_linfo" }, + { NHTYPE_COMPLEX, "Sfi_ls_t", "Sfi_x_ls_t" }, + { NHTYPE_COMPLEX, "Sfi_mapseen_feat", "Sfi_x_mapseen_feat" }, + { NHTYPE_COMPLEX, "Sfi_mapseen_flags", "Sfi_x_mapseen_flags" }, + { NHTYPE_COMPLEX, "Sfi_mapseen_rooms", "Sfi_x_mapseen_rooms" }, + { NHTYPE_COMPLEX, "Sfi_mkroom", "Sfi_x_mkroom" }, + { NHTYPE_COMPLEX, "Sfi_monst", "Sfi_x_monst" }, + { NHTYPE_COMPLEX, "Sfi_mvitals", "Sfi_x_mvitals" }, + { NHTYPE_COMPLEX, "Sfi_nhrect", "Sfi_x_nhrect" }, + { NHTYPE_COMPLEX, "Sfi_obj", "Sfi_x_obj" }, + { NHTYPE_COMPLEX, "Sfi_objclass", "Sfi_x_objclass" }, + { NHTYPE_COMPLEX, "Sfi_q_score", "Sfi_x_q_score" }, + { NHTYPE_COMPLEX, "Sfi_rm", "Sfi_x_rm" }, + { NHTYPE_COMPLEX, "Sfi_s_level", "Sfi_x_s_level" }, + { NHTYPE_COMPLEX, "Sfi_spell", "Sfi_x_spell" }, + { NHTYPE_COMPLEX, "Sfi_stairway", "Sfi_x_stairway" }, + { NHTYPE_COMPLEX, "Sfi_trap", "Sfi_x_trap" }, + { NHTYPE_COMPLEX, "Sfi_version_info", "Sfi_x_version_info" }, + { NHTYPE_COMPLEX, "Sfi_you", "Sfi_x_you" }, + /* output */ + { NHTYPE_SIMPLE, "Sfo_any", "Sfo_any" }, + { NHTYPE_SIMPLE, "Sfo_boolean", "Sfo_boolean" }, + { NHTYPE_SIMPLE, "Sfo_char", "Sfo_char" }, + { NHTYPE_SIMPLE, "Sfo_coordxy", "Sfo_coordxy" }, + { NHTYPE_SIMPLE, "Sfo_int16", "Sfo_int16" }, + { NHTYPE_SIMPLE, "Sfo_int32", "Sfo_int32" }, + { NHTYPE_SIMPLE, "Sfo_long", "Sfo_long" }, + { NHTYPE_SIMPLE, "Sfo_nhcoord", "Sfo_nhcoord" }, + { NHTYPE_SIMPLE, "Sfo_schar", "Sfo_schar" }, + { NHTYPE_SIMPLE, "Sfo_uint32", "Sfo_uint32" }, + { NHTYPE_SIMPLE, "Sfo_ulong", "Sfo_ulong" }, + { NHTYPE_SIMPLE, "Sfo_xint8", "Sfo_xint8" }, + { NHTYPE_COMPLEX, "Sfo_arti_info", "Sfo_x_arti_info" }, + { NHTYPE_COMPLEX, "Sfo_branch", "Sfo_x_branch" }, + { NHTYPE_COMPLEX, "Sfo_bubble", "Sfo_x_bubble" }, + { NHTYPE_COMPLEX, "Sfo_cemetery", "Sfo_x_cemetery" }, + { NHTYPE_COMPLEX, "Sfo_context_info", "Sfo_x_context_info" }, + { NHTYPE_COMPLEX, "Sfo_d_level", "Sfo_x_d_level" }, + { NHTYPE_COMPLEX, "Sfo_damage", "Sfo_x_damage" }, + { NHTYPE_COMPLEX, "Sfo_dest_area", "Sfo_x_dest_area" }, + { NHTYPE_COMPLEX, "Sfo_dgn_topology", "Sfo_x_dgn_topology" }, + { NHTYPE_COMPLEX, "Sfo_dungeon", "Sfo_x_dungeon" }, + { NHTYPE_COMPLEX, "Sfo_ebones", "Sfo_x_ebones" }, + { NHTYPE_COMPLEX, "Sfo_edog", "Sfo_x_edog" }, + { NHTYPE_COMPLEX, "Sfo_egd", "Sfo_x_egd" }, + { NHTYPE_COMPLEX, "Sfo_emin", "Sfo_x_emin" }, + { NHTYPE_COMPLEX, "Sfo_engr", "Sfo_x_engr" }, + { NHTYPE_COMPLEX, "Sfo_epri", "Sfo_x_epri" }, + { NHTYPE_COMPLEX, "Sfo_eshk", "Sfo_x_eshk" }, + { NHTYPE_COMPLEX, "Sfo_fe", "Sfo_x_fe" }, + { NHTYPE_COMPLEX, "Sfo_flag", "Sfo_x_flag" }, + { NHTYPE_COMPLEX, "Sfo_fruit", "Sfo_x_fruit" }, + { NHTYPE_COMPLEX, "Sfo_gamelog_line", "Sfo_x_gamelog_line" }, + { NHTYPE_COMPLEX, "Sfo_kinfo", "Sfo_x_kinfo" }, + { NHTYPE_COMPLEX, "Sfo_levelflags", "Sfo_x_levelflags" }, + { NHTYPE_COMPLEX, "Sfo_linfo", "Sfo_x_linfo" }, + { NHTYPE_COMPLEX, "Sfo_ls_t", "Sfo_x_ls_t" }, + { NHTYPE_COMPLEX, "Sfo_mapseen_feat", "Sfo_x_mapseen_feat" }, + { NHTYPE_COMPLEX, "Sfo_mapseen_flags", "Sfo_x_mapseen_flags" }, + { NHTYPE_COMPLEX, "Sfo_mapseen_rooms", "Sfo_x_mapseen_rooms" }, + { NHTYPE_COMPLEX, "Sfo_mkroom", "Sfo_x_mkroom" }, + { NHTYPE_COMPLEX, "Sfo_monst", "Sfo_x_monst" }, + { NHTYPE_COMPLEX, "Sfo_mvitals", "Sfo_x_mvitals" }, + { NHTYPE_COMPLEX, "Sfo_nhrect", "Sfo_x_nhrect" }, + { NHTYPE_COMPLEX, "Sfo_obj", "Sfo_x_obj" }, + { NHTYPE_COMPLEX, "Sfo_objclass", "Sfo_x_objclass" }, + { NHTYPE_COMPLEX, "Sfo_q_score", "Sfo_x_q_score" }, + { NHTYPE_COMPLEX, "Sfo_rm", "Sfo_x_rm" }, + { NHTYPE_COMPLEX, "Sfo_s_level", "Sfo_x_s_level" }, + { NHTYPE_COMPLEX, "Sfo_spell", "Sfo_x_spell" }, + { NHTYPE_COMPLEX, "Sfo_stairway", "Sfo_x_stairway" }, + { NHTYPE_COMPLEX, "Sfo_trap", "Sfo_x_trap" }, + { NHTYPE_COMPLEX, "Sfo_version_info", "Sfo_x_version_info" }, + { NHTYPE_COMPLEX, "Sfo_you", "Sfo_x_you" }, +}; + +static const char * +fn(const char *f) +{ + int i; + + for (i = 0; i < SIZE(already); ++i) { + if (!strcmp(already[i].actual, f)) + return already[i].replacement; + } + return f; +} + +const char *force_no_x[] = { + "aligntyp", "any", "boolean", "char", "coordxy", "genericptr", + "int", "int16", "int32", "int64", "long", "schar", + "short", "size_t", "time_t", "uchar", "uint32", "uint64", + "ulong", "unsigned", "ushort", "xint16", "xint8", +}; + +static boolean +no_x(const char *s) +{ + int i; + + for (i = 0; i < SIZE(force_no_x); ++i) { + if (!strcmp(force_no_x[i], s)) + return TRUE; + } + return FALSE; +} + +struct nh_classification { + int in_sfbase; + uint dtclass; + const char *fn; + const char *replacement_fn; +}; + +struct nh_classification nhdatatypes[] = { + { 0, NHTYPE_COMPLEX, "sfi_achievement_tracking", + "sfi_achievement_tracking" }, + { 0, NHTYPE_COMPLEX, "sfi_align", "sfi_align" }, + { 0, NHTYPE_COMPLEX, "sfi_attribs", "sfi_attribs" }, + { 0, NHTYPE_COMPLEX, "sfi_bill_x", "sfi_bill_x" }, + { 0, NHTYPE_COMPLEX, "sfi_book_info", "sfi_book_info" }, + { 0, NHTYPE_COMPLEX, "sfi_branch", "sfi_branch" }, + { 0, NHTYPE_COMPLEX, "sfi_bubble", "sfi_bubble" }, + { 0, NHTYPE_COMPLEX, "sfi_cemetery", "sfi_cemetery" }, + { 0, NHTYPE_COMPLEX, "sfi_context_info", "sfi_context_info" }, + { 0, NHTYPE_COMPLEX, "sfi_d_flags", "sfi_d_flags" }, + { 0, NHTYPE_COMPLEX, "sfi_d_level", "sfi_d_level" }, + { 0, NHTYPE_COMPLEX, "sfi_damage", "sfi_damage" }, + { 0, NHTYPE_COMPLEX, "sfi_dest_area", "sfi_dest_area" }, + { 0, NHTYPE_COMPLEX, "sfi_dgn_topology", "sfi_dgn_topology" }, + { 0, NHTYPE_COMPLEX, "sfi_dig_info", "sfi_dig_info" }, + { 0, NHTYPE_COMPLEX, "sfi_dungeon", "sfi_dungeon" }, + { 0, NHTYPE_COMPLEX, "sfi_ebones", "sfi_ebones" }, + { 0, NHTYPE_COMPLEX, "sfi_edog", "sfi_edog" }, + { 0, NHTYPE_COMPLEX, "sfi_egd", "sfi_egd" }, + { 0, NHTYPE_COMPLEX, "sfi_emin", "sfi_emin" }, + { 0, NHTYPE_COMPLEX, "sfi_engr", "sfi_engr" }, + { 0, NHTYPE_COMPLEX, "sfi_engrave_info", "sfi_engrave_info" }, + { 0, NHTYPE_COMPLEX, "sfi_epri", "sfi_epri" }, + { 0, NHTYPE_COMPLEX, "sfi_eshk", "sfi_eshk" }, + { 0, NHTYPE_COMPLEX, "sfi_fakecorridor", "sfi_fakecorridor" }, + { 0, NHTYPE_COMPLEX, "sfi_fe", "sfi_fe" }, + { 0, NHTYPE_COMPLEX, "sfi_flag", "sfi_flag" }, + { 0, NHTYPE_COMPLEX, "sfi_fruit", "sfi_fruit" }, + { 0, NHTYPE_COMPLEX, "sfi_kinfo", "sfi_kinfo" }, + { 0, NHTYPE_COMPLEX, "sfi_levelflags", "sfi_levelflags" }, + { 0, NHTYPE_COMPLEX, "sfi_linfo", "sfi_linfo" }, + { 0, NHTYPE_COMPLEX, "sfi_ls_t", "sfi_ls_t" }, + { 0, NHTYPE_COMPLEX, "sfi_mapseen", "sfi_mapseen" }, + { 0, NHTYPE_COMPLEX, "sfi_mapseen_feat", "sfi_mapseen_feat" }, + { 0, NHTYPE_COMPLEX, "sfi_mapseen_flags", "sfi_mapseen_flags" }, + { 0, NHTYPE_COMPLEX, "sfi_mapseen_rooms", "sfi_mapseen_rooms" }, + { 0, NHTYPE_COMPLEX, "sfi_mextra", "sfi_mextra" }, + { 0, NHTYPE_COMPLEX, "sfi_mkroom", "sfi_mkroom" }, + { 0, NHTYPE_COMPLEX, "sfi_monst", "sfi_monst" }, + { 0, NHTYPE_COMPLEX, "sfi_mvitals", "sfi_mvitals" }, + { 0, NHTYPE_COMPLEX, "sfi_nhcoord", "sfi_nhcoord" }, + { 0, NHTYPE_COMPLEX, "sfi_nhrect", "sfi_nhrect" }, + { 0, NHTYPE_COMPLEX, "sfi_novel_tracking", "sfi_novel_tracking" }, + { 0, NHTYPE_COMPLEX, "sfi_obj", "sfi_obj" }, + { 0, NHTYPE_COMPLEX, "sfi_obj_split", "sfi_obj_split" }, + { 0, NHTYPE_COMPLEX, "sfi_objclass", "sfi_objclass" }, + { 0, NHTYPE_COMPLEX, "sfi_oextra", "sfi_oextra" }, + { 0, NHTYPE_COMPLEX, "sfi_polearm_info", "sfi_polearm_info" }, + { 0, NHTYPE_COMPLEX, "sfi_prop", "sfi_prop" }, + { 0, NHTYPE_COMPLEX, "sfi_q_score", "sfi_q_score" }, + { 0, NHTYPE_COMPLEX, "sfi_rm", "sfi_rm" }, + { 0, NHTYPE_COMPLEX, "sfi_s_level", "sfi_s_level" }, + { 0, NHTYPE_COMPLEX, "sfi_skills", "sfi_skills" }, + { 0, NHTYPE_COMPLEX, "sfi_spell", "sfi_spell" }, + { 0, NHTYPE_COMPLEX, "sfi_stairway", "sfi_stairway" }, + { 0, NHTYPE_COMPLEX, "sfi_takeoff_info", "sfi_takeoff_info" }, + { 0, NHTYPE_COMPLEX, "sfi_tin_info", "sfi_tin_info" }, + { 0, NHTYPE_COMPLEX, "sfi_trap", "sfi_trap" }, + { 0, NHTYPE_COMPLEX, "sfi_tribute_info", "sfi_tribute_info" }, + { 0, NHTYPE_COMPLEX, "sfi_u_conduct", "sfi_u_conduct" }, + { 0, NHTYPE_COMPLEX, "sfi_u_event", "sfi_u_event" }, + { 0, NHTYPE_COMPLEX, "sfi_u_have", "sfi_u_have" }, + { 0, NHTYPE_COMPLEX, "sfi_u_realtime", "sfi_u_realtime" }, + { 0, NHTYPE_COMPLEX, "sfi_u_roleplay", "sfi_u_roleplay" }, + { 0, NHTYPE_COMPLEX, "sfi_version_info", "sfi_version_info" }, + { 0, NHTYPE_COMPLEX, "sfi_victual_info", "sfi_victual_info" }, + { 0, NHTYPE_COMPLEX, "sfi_vlaunchinfo", "sfi_vlaunchinfo" }, + { 0, NHTYPE_COMPLEX, "sfi_vptrs", "sfi_vptrs" }, + { 0, NHTYPE_COMPLEX, "sfi_warntype_info", "sfi_warntype_info" }, + { 0, NHTYPE_COMPLEX, "sfi_you", "sfi_you" }, + { 0, NHTYPE_COMPLEX, "sfo_achievement_tracking", + "sfo_achievement_tracking" }, + { 0, NHTYPE_COMPLEX, "sfo_align", "sfo_align" }, + { 0, NHTYPE_COMPLEX, "sfo_attribs", "sfo_attribs" }, + { 0, NHTYPE_COMPLEX, "sfo_bill_x", "sfo_bill_x" }, + { 0, NHTYPE_COMPLEX, "sfo_book_info", "sfo_book_info" }, + { 0, NHTYPE_COMPLEX, "sfo_branch", "sfo_branch" }, + { 0, NHTYPE_COMPLEX, "sfo_bubble", "sfo_bubble" }, + { 0, NHTYPE_COMPLEX, "sfo_cemetery", "sfo_cemetery" }, + { 0, NHTYPE_COMPLEX, "sfo_context_info", "sfo_context_info" }, + { 0, NHTYPE_COMPLEX, "sfo_d_flags", "sfo_d_flags" }, + { 0, NHTYPE_COMPLEX, "sfo_d_level", "sfo_d_level" }, + { 0, NHTYPE_COMPLEX, "sfo_damage", "sfo_damage" }, + { 0, NHTYPE_COMPLEX, "sfo_dest_area", "sfo_dest_area" }, + { 0, NHTYPE_COMPLEX, "sfo_dgn_topology", "sfo_dgn_topology" }, + { 0, NHTYPE_COMPLEX, "sfo_dig_info", "sfo_dig_info" }, + { 0, NHTYPE_COMPLEX, "sfo_dungeon", "sfo_dungeon" }, + { 0, NHTYPE_COMPLEX, "sfo_ebones", "sfo_ebones" }, + { 0, NHTYPE_COMPLEX, "sfo_edog", "sfo_edog" }, + { 0, NHTYPE_COMPLEX, "sfo_egd", "sfo_egd" }, + { 0, NHTYPE_COMPLEX, "sfo_emin", "sfo_emin" }, + { 0, NHTYPE_COMPLEX, "sfo_engr", "sfo_engr" }, + { 0, NHTYPE_COMPLEX, "sfo_engrave_info", "sfo_engrave_info" }, + { 0, NHTYPE_COMPLEX, "sfo_epri", "sfo_epri" }, + { 0, NHTYPE_COMPLEX, "sfo_eshk", "sfo_eshk" }, + { 0, NHTYPE_COMPLEX, "sfo_fakecorridor", "sfo_fakecorridor" }, + { 0, NHTYPE_COMPLEX, "sfo_fe", "sfo_fe" }, + { 0, NHTYPE_COMPLEX, "sfo_flag", "sfo_flag" }, + { 0, NHTYPE_COMPLEX, "sfo_fruit", "sfo_fruit" }, + { 0, NHTYPE_COMPLEX, "sfo_kinfo", "sfo_kinfo" }, + { 0, NHTYPE_COMPLEX, "sfo_levelflags", "sfo_levelflags" }, + { 0, NHTYPE_COMPLEX, "sfo_linfo", "sfo_linfo" }, + { 0, NHTYPE_COMPLEX, "sfo_ls_t", "sfo_ls_t" }, + { 0, NHTYPE_COMPLEX, "sfo_mapseen", "sfo_mapseen" }, + { 0, NHTYPE_COMPLEX, "sfo_mapseen_feat", "sfo_mapseen_feat" }, + { 0, NHTYPE_COMPLEX, "sfo_mapseen_flags", "sfo_mapseen_flags" }, + { 0, NHTYPE_COMPLEX, "sfo_mapseen_rooms", "sfo_mapseen_rooms" }, + { 0, NHTYPE_COMPLEX, "sfo_mextra", "sfo_mextra" }, + { 0, NHTYPE_COMPLEX, "sfo_mkroom", "sfo_mkroom" }, + { 0, NHTYPE_COMPLEX, "sfo_monst", "sfo_monst" }, + { 0, NHTYPE_COMPLEX, "sfo_mvitals", "sfo_mvitals" }, + { 0, NHTYPE_COMPLEX, "sfo_nhcoord", "sfo_nhcoord" }, + { 0, NHTYPE_COMPLEX, "sfo_nhrect", "sfo_nhrect" }, + { 0, NHTYPE_COMPLEX, "sfo_novel_tracking", "sfo_novel_tracking" }, + { 0, NHTYPE_COMPLEX, "sfo_obj", "sfo_obj" }, + { 0, NHTYPE_COMPLEX, "sfo_obj_split", "sfo_obj_split" }, + { 0, NHTYPE_COMPLEX, "sfo_objclass", "sfo_objclass" }, + { 0, NHTYPE_COMPLEX, "sfo_oextra", "sfo_oextra" }, + { 0, NHTYPE_COMPLEX, "sfo_polearm_info", "sfo_polearm_info" }, + { 0, NHTYPE_COMPLEX, "sfo_prop", "sfo_prop" }, + { 0, NHTYPE_COMPLEX, "sfo_q_score", "sfo_q_score" }, + { 0, NHTYPE_COMPLEX, "sfo_rm", "sfo_rm" }, + { 0, NHTYPE_COMPLEX, "sfo_s_level", "sfo_s_level" }, + { 0, NHTYPE_COMPLEX, "sfo_skills", "sfo_skills" }, + { 0, NHTYPE_COMPLEX, "sfo_spell", "sfo_spell" }, + { 0, NHTYPE_COMPLEX, "sfo_stairway", "sfo_stairway" }, + { 0, NHTYPE_COMPLEX, "sfo_takeoff_info", "sfo_takeoff_info" }, + { 0, NHTYPE_COMPLEX, "sfo_tin_info", "sfo_tin_info" }, + { 0, NHTYPE_COMPLEX, "sfo_trap", "sfo_trap" }, + { 0, NHTYPE_COMPLEX, "sfo_tribute_info", "sfo_tribute_info" }, + { 0, NHTYPE_COMPLEX, "sfo_u_conduct", "sfo_u_conduct" }, + { 0, NHTYPE_COMPLEX, "sfo_u_event", "sfo_u_event" }, + { 0, NHTYPE_COMPLEX, "sfo_u_have", "sfo_u_have" }, + { 0, NHTYPE_COMPLEX, "sfo_u_realtime", "sfo_u_realtime" }, + { 0, NHTYPE_COMPLEX, "sfo_u_roleplay", "sfo_u_roleplay" }, + { 0, NHTYPE_COMPLEX, "sfo_version_info", "sfo_version_info" }, + { 0, NHTYPE_COMPLEX, "sfo_victual_info", "sfo_victual_info" }, + { 0, NHTYPE_COMPLEX, "sfo_vlaunchinfo", "sfo_vlaunchinfo" }, + { 0, NHTYPE_COMPLEX, "sfo_vptrs", "sfo_vptrs" }, + { 0, NHTYPE_COMPLEX, "sfo_warntype_info", "sfo_warntype_info" }, + { 0, NHTYPE_COMPLEX, "sfo_you", "sfo_you" }, + { 0, NHTYPE_SIMPLE, "sfi_any", "sfi_any" }, + { 0, NHTYPE_SIMPLE, "sfi_aligntyp", "Sfi_aligntyp" }, + { 0, NHTYPE_SIMPLE, "sfi_Bitfield", "Sfi_Bitfield" }, + { 0, NHTYPE_SIMPLE, "sfi_boolean", "Sfi_boolean" }, + { 0, NHTYPE_SIMPLE, "sfi_char", "Sfi_char" }, + { 0, NHTYPE_SIMPLE, "sfi_coordxy", "Sfi_Coordxy" }, + { 0, NHTYPE_SIMPLE, "sfi_int", "Sfi_int32" }, + { 0, NHTYPE_SIMPLE, "sfi_long", "Sfi_long" }, + { 0, NHTYPE_SIMPLE, "sfi_schar", "Sfi_schar" }, + { 0, NHTYPE_SIMPLE, "sfi_short", "Sfi_int16" }, + { 0, NHTYPE_SIMPLE, "sfi_size_t", "Sfi_size_t" }, + { 0, NHTYPE_SIMPLE, "sfi_string", "Sfi_string" }, + { 0, NHTYPE_SIMPLE, "sfi_time_t", "Sfi_time_t" }, + { 0, NHTYPE_SIMPLE, "sfi_uchar", "Sfi_uchar" }, + { 0, NHTYPE_SIMPLE, "sfi_unsigned", "Sfi_uint32" }, + { 0, NHTYPE_SIMPLE, "sfi_xint16", "Sfi_int16" }, + { 0, NHTYPE_SIMPLE, "sfi_xint8", "Sfi_xint8" }, + { 0, NHTYPE_SIMPLE, "sfo_aligntyp", "Sfo_aligntyp" }, + { 0, NHTYPE_SIMPLE, "sfo_any", "Sfo_any" }, + { 0, NHTYPE_SIMPLE, "sfo_Bitfield", "Sfo_Bitfield" }, + { 0, NHTYPE_SIMPLE, "sfo_boolean", "Sfo_boolean" }, + { 0, NHTYPE_SIMPLE, "sfo_char", "Sfo_char" }, + { 0, NHTYPE_SIMPLE, "sfo_coordxy", "Sfo_coordxy" }, + { 0, NHTYPE_SIMPLE, "sfo_int", "Sfo_int32" }, + { 0, NHTYPE_SIMPLE, "sfo_long", "sfo_long" }, + { 0, NHTYPE_SIMPLE, "sfo_schar", "Sfo_schar" }, + { 0, NHTYPE_SIMPLE, "sfo_short", "Sfo_short" }, + { 0, NHTYPE_SIMPLE, "sfo_size_t", "Sfo_size_t" }, + { 0, NHTYPE_SIMPLE, "sfo_string", "Sfo_string" }, + { 0, NHTYPE_SIMPLE, "sfo_time_t", "Sfo_time_t" }, + { 0, NHTYPE_SIMPLE, "sfo_uchar", "Sfo_uchar" }, + { 0, NHTYPE_SIMPLE, "sfo_unsigned", "Sfo_uint32" }, + { 0, NHTYPE_SIMPLE, "sfo_xint16", "Sfo_int16" }, + { 0, NHTYPE_SIMPLE, "sfo_xint8", "Sfo_xint8" } +}; + + /*sftags.c*/ + + diff --git a/util/stripbs.c b/util/stripbs.c new file mode 100644 index 000000000..15d319566 --- /dev/null +++ b/util/stripbs.c @@ -0,0 +1,48 @@ +/* NetHack 3.7 stripbs.c */ +/* Copyright (c) Michael Allison, 2025. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * a simple filter to strip character-backspace-character + * from stdin and write the final character to stdout. + */ + +#include +#include +#include +#include + +#if defined(__GNUC__) +#define UNUSED __attribute__((unused)) +#else +#define UNUSED +#endif + +int main(int argc UNUSED, char *argv[] UNUSED) +{ + int stop = 0, trouble = 0; + char buf[2]; + char *cp = &buf[0], *prev = &buf[1]; + + *prev = 0; + while (!stop) { + if ((fread(buf, 1, 1, stdin)) > 0) { + if (*cp == 8) { + *prev = 0; + } else { + if (*prev) + fputc(*prev, stdout); + *prev = *cp; + } + } else { + if (errno != EOF) + trouble = 1; + if (*prev) + fputc(*prev, stdout); + stop = 1; + } + } + fflush(stdout); + fclose(stdout); + return trouble ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/win/Qt/Qt-issues.txt b/win/Qt/Qt-issues.txt index a7418f984..1bac3106c 100644 --- a/win/Qt/Qt-issues.txt +++ b/win/Qt/Qt-issues.txt @@ -59,4 +59,10 @@ The status window can't be resized while hitpointbar is active. Toggling it off, resizing as desired, then toggling it back on is a viable workaround. +When choosing a saved game to restore ('selectsaved' option), the +selection dialog does not provide any way to scroll if there are more +games than will fit on the screen. Access to "New game" and "Quit" +isn't affected because those both have distinct buttons rather than +rely on fake save file entries at the end of the list. + ----- diff --git a/win/Qt/qt_bind.cpp b/win/Qt/qt_bind.cpp index d0128cc62..58dc96476 100644 --- a/win/Qt/qt_bind.cpp +++ b/win/Qt/qt_bind.cpp @@ -70,6 +70,8 @@ static struct key_macro_rec { { 0, 0U, (const char *) 0, (const char *) 0 } }; +static QPen *pen = (QPen *) 0; + NetHackQtBind::NetHackQtBind(int& argc, char** argv) : #ifdef KDE KApplication(argc,argv) @@ -269,6 +271,7 @@ void NetHackQtBind::qt_askname() // success; handle plname[] verification below prior to returning break; } + FALLTHROUGH; /*FALLTHRU*/ case -2: // Quit @@ -578,6 +581,45 @@ void NetHackQtBind::qt_raw_print_bold(const char *str) qt_raw_print(str); } +const QPen NetHackQtBind::nhcolor_to_pen(uint32_t c) +{ + if (!pen) { + pen = new QPen[17]; + + pen[ 0] = QColor(64, 64, 64); // black + pen[ 1] = QColor(Qt::red); + pen[ 2] = QColor(0, 191, 0); // green + pen[ 3] = QColor(127, 127, 0); // brownish + pen[ 4] = QColor(Qt::blue); + pen[ 5] = QColor(Qt::magenta); + pen[ 6] = QColor(Qt::cyan); + pen[ 7] = QColor(Qt::gray); + // on tty, "light" variations are "bright" instead; here they're paler + pen[ 8] = QColor(Qt::white); // no color + pen[ 9] = QColor(255, 127, 0); // orange + pen[10] = QColor(127, 255, 127); // light green + pen[11] = QColor(Qt::yellow); + pen[12] = QColor(127, 127, 255); // light blue + pen[13] = QColor(255, 127, 255); // light magenta + pen[14] = QColor(127, 255, 255); // light cyan + pen[15] = QColor(Qt::white); + // ? out of range for 0..15 + pen[16] = QColor(Qt::black); + } + +#ifdef ENHANCED_SYMBOLS + if (c & 0x80000000) { + return QColor( + (c >> 16) & 0xFF, + (c >> 8) & 0xFF, + (c >> 0) & 0xFF); + } else +#endif + { + return pen[c]; + } +} + int NetHackQtBind::qt_nhgetch() { if (main) @@ -682,9 +724,14 @@ char NetHackQtBind::qt_more() int complain = 0; do { ch = NetHackQtBind::qt_nhgetch(); + if (::program_state.savefile_completed) { + retry = false; + break; + } switch (ch) { case '\0': // hypothetical ch = '\033'; + FALLTHROUGH; /*FALLTHRU*/ case ' ': case '\n': @@ -901,16 +948,28 @@ void NetHackQtBind::qt_delay_output() #endif } -void NetHackQtBind::qt_start_screen() +#ifdef CHANGE_COLOR +void NetHackQtBind::qt_change_color(int color, long rgb, int reverse UNUSED) { - // Ignore. + int r, g, b; + + r = (rgb >> 16) & 0xFF; + g = (rgb >> 8) & 0xFF; + b = rgb & 0xFF; + if (!pen) { + (void) NetHackQtBind::nhcolor_to_pen(0); /* init pen[] */ + } + pen[color % 16] = QColor(r, g, b); } -void NetHackQtBind::qt_end_screen() +char * +NetHackQtBind::qt_get_color_string(void) { - // Ignore. + return (char *) 0; } +#endif + void NetHackQtBind::qt_outrip(winid wid, int how, time_t when) { NetHackQtWindow* window=id_to_window[(int)wid]; @@ -1158,15 +1217,14 @@ struct window_procs Qt_procs = { nethack_qt_::NetHackQtBind::qt_get_ext_cmd, nethack_qt_::NetHackQtBind::qt_number_pad, nethack_qt_::NetHackQtBind::qt_delay_output, -#ifdef CHANGE_COLOR /* only a Mac option currently */ - donull, - donull, +#ifdef CHANGE_COLOR + nethack_qt_::NetHackQtBind::qt_change_color, +#ifdef MAC /* old OS 9, not OSX */ donull, donull, #endif - /* other defs that really should go away (they're tty specific) */ - nethack_qt_::NetHackQtBind::qt_start_screen, - nethack_qt_::NetHackQtBind::qt_end_screen, + nethack_qt_::NetHackQtBind::qt_get_color_string, +#endif #ifdef GRAPHIC_TOMBSTONE nethack_qt_::NetHackQtBind::qt_outrip, #else diff --git a/win/Qt/qt_bind.h b/win/Qt/qt_bind.h index b0b9f4f3a..d0c71759c 100644 --- a/win/Qt/qt_bind.h +++ b/win/Qt/qt_bind.h @@ -71,6 +71,9 @@ public: const glyph_info *bkglyphinfo); static void qt_raw_print(const char *str); static void qt_raw_print_bold(const char *str); + static const QPen nhcolor_to_pen(uint32_t c); + static void qt_change_color(int color, long rgb, int reverse UNUSED); + static char *qt_get_color_string(void); static int qt_nhgetch(); static int qt_nh_poskey(coordxy *x, coordxy *y, int *mod); static void qt_nhbell(); @@ -82,8 +85,6 @@ public: static int qt_get_ext_cmd(); static void qt_number_pad(int); static void qt_delay_output(); - static void qt_start_screen(); - static void qt_end_screen(); static void qt_preference_update(const char *optname); static char *qt_getmsghistory(boolean init); diff --git a/win/Qt/qt_main.cpp b/win/Qt/qt_main.cpp index 73bade00f..14a4f51f6 100644 --- a/win/Qt/qt_main.cpp +++ b/win/Qt/qt_main.cpp @@ -886,8 +886,8 @@ NetHackQtMainWindow::NetHackQtMainWindow(NetHackQtKeyBuffer& ks) : if (qt_settings != NULL) { auto glyphs = &qt_settings->glyphs(); if (glyphs != NULL) { - maxwn = glyphs->width() * COLNO + 10; - maxhn = glyphs->height() * ROWNO * 6/4; + maxwn = glyphs->width() * (COLNO + 1); + maxhn = glyphs->height() * ROWNO * 6/4 + glyphs->height() * 10; } } @@ -1456,7 +1456,7 @@ void NetHackQtMainWindow::ShowIfReady() } else { layout(); } - showMaximized(); + showNormal(); } else if (isVisible()) { hide(); } diff --git a/win/Qt/qt_map.cpp b/win/Qt/qt_map.cpp index a2162504f..85817dabf 100644 --- a/win/Qt/qt_map.cpp +++ b/win/Qt/qt_map.cpp @@ -77,47 +77,6 @@ extern int qt_compact_mode; namespace nethack_qt_ { -static const QPen nhcolor_to_pen(uint32_t c) -{ - static QPen *pen = (QPen *) 0; - if (!pen) { - pen = new QPen[17]; - // - // FIXME: these are duplicated in qt_menu.cpp - // - pen[ 0] = QColor(64, 64, 64); // black - pen[ 1] = QColor(Qt::red); - pen[ 2] = QColor(0, 191, 0); // green - pen[ 3] = QColor(127, 127, 0); // brownish - pen[ 4] = QColor(Qt::blue); - pen[ 5] = QColor(Qt::magenta); - pen[ 6] = QColor(Qt::cyan); - pen[ 7] = QColor(Qt::gray); - // on tty, "light" variations are "bright" instead; here they're paler - pen[ 8] = QColor(Qt::white); // no color - pen[ 9] = QColor(255, 127, 0); // orange - pen[10] = QColor(127, 255, 127); // light green - pen[11] = QColor(Qt::yellow); - pen[12] = QColor(127, 127, 255); // light blue - pen[13] = QColor(255, 127, 255); // light magenta - pen[14] = QColor(127, 255, 255); // light cyan - pen[15] = QColor(Qt::white); - // ? out of range for 0..15 - pen[16] = QColor(Qt::black); - } - -#ifdef ENHANCED_SYMBOLS - if (c & 0x80000000) { - return QColor( - (c >> 16) & 0xFF, - (c >> 8) & 0xFF, - (c >> 0) & 0xFF); - } else -#endif - { - return pen[c]; - } -} NetHackQtMapViewport::NetHackQtMapViewport(NetHackQtClickBuffer& click_sink) : QWidget(NULL), @@ -202,7 +161,7 @@ void NetHackQtMapViewport::paintEvent(QPaintEvent* event) ch = cp437(ch); } color = Glyphcolor(i, j); - painter.setPen(nhcolor_to_pen(color)); + painter.setPen(NetHackQtBind::nhcolor_to_pen(color)); if (!DrawWalls(painter, i * gW, j * gH, gW, gH, ch)) { ushort utf16[3]; if (ch < 0x10000) { @@ -226,7 +185,7 @@ void NetHackQtMapViewport::paintEvent(QPaintEvent* event) } framecolor = GlyphFramecolor(i, j); if (framecolor != NO_COLOR) { - painter.setPen(nhcolor_to_pen(framecolor)); + painter.setPen(NetHackQtBind::nhcolor_to_pen(framecolor)); painter.drawRect(i * qt_settings->glyphs().width(), j * qt_settings->glyphs().height(), qt_settings->glyphs().width() - 1, @@ -255,7 +214,7 @@ void NetHackQtMapViewport::paintEvent(QPaintEvent* event) } framecolor = GlyphFramecolor(i, j); if (framecolor != NO_COLOR) { - painter.setPen(nhcolor_to_pen(framecolor)); + painter.setPen(NetHackQtBind::nhcolor_to_pen(framecolor)); painter.drawRect(i * qt_settings->glyphs().width(), j * qt_settings->glyphs().height(), qt_settings->glyphs().width() - 1, diff --git a/win/Qt/qt_menu.cpp b/win/Qt/qt_menu.cpp index 6a0e7a524..93a9064f6 100644 --- a/win/Qt/qt_menu.cpp +++ b/win/Qt/qt_menu.cpp @@ -483,69 +483,11 @@ void NetHackQtMenuWindow::UpdateCountColumn(long newcount) table->repaint(); } -struct qcolor { - QColor q; - const char *nm; -}; -// these match the tty colors, or better versions of same; -// [0] is used for black, and [8] (the first white) corresponds to "no color" -static const struct qcolor colors[] = { - { QColor(64, 64, 64), "64,64,64" }, // black - { QColor(Qt::red), "red" }, - { QColor(0, 191, 0), "0,191,0" }, // green - { QColor(127, 127, 0), "127,127,0" }, // brownish - { QColor(Qt::blue), "blue" }, - { QColor(Qt::magenta), "magenta" }, - { QColor(Qt::cyan), "cyan" }, - { QColor(Qt::gray), "gray" }, - // on tty, the "light" variations are "bright" instead; here they're paler - { QColor(Qt::white), "white" }, // no-color, so not rendered - { QColor(255, 127, 0), "255,127,0" }, // orange - { QColor(127, 255, 127), "127,255,127" }, // light green - { QColor(Qt::yellow), "yellow" }, - { QColor(127, 127, 255), "127,127,255" }, // light blue - { QColor(255, 127, 255), "255,127,255" }, // light magenta - { QColor(127, 255, 255), "127,255,255" }, // light cyan - { QColor(Qt::white), "white" }, -}; - -#if 0 /* available for debugging */ -static const char *color_name(const QColor q) -{ - for (int i = 0; i < SIZE(colors); ++i) - if (q == colors[i].q) - return colors[i].nm; - // these are all the enum GlobalColor values ; - // black and white have been moved in front of color0 and color1 here - const char *nm = (q == Qt::black) ? "black" - : (q == Qt::white) ? "white" - : (q == Qt::color0) ? "color0" // doesn't duplicate white? - : (q == Qt::color1) ? "color1" // does duplicate black - : (q == Qt::darkGray) ? "darkGray" - : (q == Qt::gray) ? "gray" - : (q == Qt::lightGray) ? "lightGray" - : (q == Qt::red) ? "red" - : (q == Qt::green) ? "green" - : (q == Qt::blue) ? "blue" - : (q == Qt::cyan) ? "cyan" - : (q == Qt::magenta) ? "magenta" - : (q == Qt::yellow) ? "yellow" - : (q == Qt::darkRed) ? "darkRed" - : (q == Qt::darkGreen) ? "darkGreen" - : (q == Qt::darkBlue) ? "darkBlue" - : (q == Qt::darkCyan) ? "darkCyan" - : (q == Qt::darkMagenta) ? "darkMagenta" - : (q == Qt::darkYellow) ? "darkYellow" - : (q == Qt::transparent) ? "transparent" - : "other"; - return nm; -} -#endif - void NetHackQtMenuWindow::SetTwiAttr(QTableWidgetItem *twi, int color, int attr) { if (color != NO_COLOR) { - twi->setForeground(colors[color].q); + const QPen qp = NetHackQtBind::nhcolor_to_pen(color); + twi->setForeground(qp.color()); } if (attr != ATR_NONE) { @@ -1173,8 +1115,7 @@ void NetHackQtTextWindow::Display(bool block UNUSED) #endif int mh = screensize.height()*3/5; if ( (qt_compact_mode && lines->TotalHeight() > mh) || use_rip ) { - // big, so make it fill - showMaximized(); + showNormal(); } else { move(0, 0); adjustSize(); diff --git a/win/Qt/qt_svsel.cpp b/win/Qt/qt_svsel.cpp index ffe22a19c..882fabce7 100644 --- a/win/Qt/qt_svsel.cpp +++ b/win/Qt/qt_svsel.cpp @@ -23,13 +23,9 @@ // ... as many buttons as needed //---- // -// TODO? -// Character names are sorted alphabetically. It would be useful to -// be able to sort by role or by game start date or by save date. -// The core fetches character names from inside the files; it could -// obtain the information needed for alternate sorting. Simpler -// enhancement: instead of just showing the character name, show -// "name-role-race-gender-alignment". +// FIXME: +// If there are a lot of saved games available, the selection dialog +// needs vertical scrolling capability. // // Note: // The code in this file is not used if the program is built without diff --git a/win/X11/NetHack.ad b/win/X11/NetHack.ad index 22e2c6bae..4c5f9b191 100644 --- a/win/X11/NetHack.ad +++ b/win/X11/NetHack.ad @@ -1,4 +1,4 @@ -! $NHDT-Date: 1542244983 2018/11/15 01:23:03 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.20 $ +! $NHDT-Date: 1750598195 2025/06/22 05:16:35 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.27 $ ! Copyright (c) 2017 by Pasi Kallinen ! NetHack may be freely redistributed. See license for details. @@ -56,7 +56,7 @@ NetHack.tile_file: x11tiles !NetHack.tombtext_y: 78 !NetHack.tombtext_dx: 0 !NetHack.tombtext_dy: 13 -! The color to use for the text on the hero's tombstone +! The color to use for the text on the tombstone of the hero (end of game). NetHack*rip*foreground: black ! The icon to use; supported values are nh72, nh56, and nh32; nh72 is the @@ -80,7 +80,7 @@ NetHack*rip*foreground: black !NetHack*autofocus: True ! If 'slow' is True, setting 'highlight_prompt' to True will cause the line -! between map and message display that's used for prompting to be "hidden" +! between map and message display that is used for prompting to be "hidden" ! as part of the map when no prompt is active, then invert foreground and ! background to stand out when a prompt is issued and waiting for a response. ! If 'slow' is False, 'highlight_prompt' will have no effect. @@ -116,8 +116,8 @@ NetHack*message*translations: : input() ! !Down: input(j) ! Specify the number of rows and columns of the map window. The default -! is the standard 80x21 window. Note: this _does_not_ change nethack's -! level size, only what you see of it. +! is the standard 80x21 window. Note: this _does_not_ change the size of +! map levels, 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 f808541d5..1021c6ce3 100644 --- a/win/X11/winX.c +++ b/win/X11/winX.c @@ -100,7 +100,18 @@ int click_x, click_y, click_button; /* Click position on a map window int updated_inventory; /* used to indicate perm_invent updating */ color_attr X11_menu_promptstyle = { NO_COLOR, ATR_NONE }; -static void X11_error_handler(String) NORETURN; +/* X11/Intrinsic.h prototype has an issue if [[NORETURN]] is used + * rather than the old __attribute((noreturn)) under c23 */ +#if defined(__GNUC__) || defined(__clang__) +#define USE_GNUC_PROTO +#endif + +#ifdef USE_GNUC_PROTO +static void X11_error_handler(String) __attribute__((noreturn)); +#else +ATTRNORETURN static XtErrorHandler X11_error_handler(String); +#endif + static int X11_io_error_handler(Display *); static int (*old_error_handler)(Display *, XErrorEvent *); @@ -146,8 +157,6 @@ struct window_procs X11_procs = { #ifdef CHANGE_COLOR /* only a Mac option currently */ donull, donull, #endif - /* other defs that really should go away (they're tty specific) */ - X11_start_screen, X11_end_screen, #ifdef GRAPHIC_TOMBSTONE X11_outrip, #else @@ -177,7 +186,7 @@ static void X11_sig_cb(XtPointer, XtSignalId *); #endif static void d_timeout(XtPointer, XtIntervalId *); static void X11_hangup(Widget, XEvent *, String *, Cardinal *); -static void X11_bail(const char *) NORETURN; +ATTRNORETURN static void X11_bail(const char *) NORETURN; static void askname_delete(Widget, XEvent *, String *, Cardinal *); static void askname_done(Widget, XtPointer, XtPointer); static void done_button(Widget, XtPointer, XtPointer); @@ -993,7 +1002,8 @@ X11_putstr(winid window, int attr, const char *str) X11_destroy_nhwindow(window); *wp = window_list[new_win]; window_list[new_win].type = NHW_NONE; /* allow re-use */ - /* fall through */ + FALLTHROUGH; + /*FALLTHRU*/ case NHW_TEXT: add_to_text_window(wp, attr, str); break; @@ -1396,20 +1406,6 @@ X11_number_pad(int state) /* called from options.c */ return; } -/* called from setftty() in unixtty.c */ -void -X11_start_screen(void) -{ - return; -} - -/* called from settty() in unixtty.c */ -void -X11_end_screen(void) -{ - return; -} - #ifdef GRAPHIC_TOMBSTONE void X11_outrip(winid window, int how, time_t when) diff --git a/win/X11/winmap.c b/win/X11/winmap.c index d27f0ac88..39b980b28 100644 --- a/win/X11/winmap.c +++ b/win/X11/winmap.c @@ -56,6 +56,9 @@ extern int total_tiles_used, Tile_corr; #define COL0_OFFSET 1 /* change to 0 to revert to displaying unused column 0 */ +#define NH_INVERSE_COLOR 0x40000000 +#define NH_ENHANCED_COLOR 0x80000000 + static X11_map_symbol glyph_char(const glyph_info *glyphinfo); static GC X11_make_gc(struct xwindow *wp, struct text_map_info_t *text_map, X11_color color, boolean inverted); @@ -170,9 +173,9 @@ X11_print_glyph( ? CLR_MAX : 0; color += colordif; if (nhcolor != 0) - color = nhcolor | 0x80000000; + color = nhcolor | NH_ENHANCED_COLOR; if (colordif != 0) - color |= 0x40000000; + color |= NH_INVERSE_COLOR; if (*co_ptr != color) { *co_ptr = color; @@ -1444,7 +1447,7 @@ map_update(struct xwindow *wp, int start_row, int stop_row, int start_col, int s BlackPixelOfScreen(screen)); } { - uint32_t fc = tile_map->glyphs[row][cur_col].framecolor; + uint32_t fc = COLORVAL(tile_map->glyphs[row][cur_col].framecolor); if (fc != NO_COLOR) XDrawRectangle(dpy, XtWindow(wp->w), @@ -1528,9 +1531,9 @@ X11_make_gc( GC ggc; #ifdef ENHANCED_SYMBOLS - if ((color & 0x80000000) != 0) { + if ((color & NH_ENHANCED_COLOR) != 0) { /* We need a new GC */ - if ((color & 0x40000000) != 0) { + if ((color & NH_INVERSE_COLOR) != 0) { cur_inv = !cur_inv; } if (iflags.use_color) { @@ -1540,7 +1543,7 @@ X11_make_gc( /* FIXME: Does this still work when the display does not support true color? */ - fgpixel = color & 0xFFFFFF; + fgpixel = COLORVAL(color); XtSetArg(arg[0], XtNbackground, &bgpixel); XtGetValues(wp->w, arg, 1); if (cur_inv) { @@ -1561,14 +1564,16 @@ X11_make_gc( } else #endif { - if (color >= CLR_MAX) { - color -= CLR_MAX; + uint32 nhcolor = COLORVAL(color); /* strip flag bits */ + + if (nhcolor >= CLR_MAX) { + nhcolor -= CLR_MAX; cur_inv = !cur_inv; } ggc = (iflags.use_color ? (cur_inv - ? text_map->inv_color_gcs[color] - : text_map->color_gcs[color]) + ? text_map->inv_color_gcs[nhcolor] + : text_map->color_gcs[nhcolor]) : (cur_inv ? text_map->inv_copy_gc : text_map->copy_gc)); @@ -1580,7 +1585,7 @@ X11_make_gc( static void X11_free_gc(struct xwindow *wp, GC ggc, X11_color color) { - if ((color & 0x80000000) != 0 && iflags.use_color) { + if ((color & NH_ENHANCED_COLOR) != 0 && iflags.use_color) { /* X11_make_gc allocated a new GC */ XtReleaseGC(wp->w, ggc); } diff --git a/win/X11/winval.c b/win/X11/winval.c index c333a5483..651ba2cbb 100644 --- a/win/X11/winval.c +++ b/win/X11/winval.c @@ -6,7 +6,7 @@ * Routines that define a name-value label widget pair that fit inside a * form widget. */ -#include +/* #include */ #ifndef SYSV #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */ diff --git a/win/chain/wc_chainin.c b/win/chain/wc_chainin.c index 6573fe826..ab63cf3c7 100644 --- a/win/chain/wc_chainin.c +++ b/win/chain/wc_chainin.c @@ -59,9 +59,6 @@ short chainin_set_font_name(winid, char *); char *chainin_get_color_string(void); #endif - /* other defs that really should go away (they're tty specific) */ -void chainin_start_screen(void); -void chainin_end_screen(void); void chainin_outrip(winid, int, time_t); void chainin_preference_update(const char *); char *chainin_getmsghistory(boolean); @@ -489,19 +486,6 @@ trace_get_color_string(void) #endif -/* other defs that really should go away (they're tty specific) */ -void -chainin_start_screen(void) -{ - (*cibase->nprocs->win_start_screen)(cibase->ndata); -} - -void -chainin_end_screen(void) -{ - (*cibase->nprocs->win_end_screen)(cibase->ndata); -} - void chainin_outrip( winid tmpwin, @@ -629,8 +613,6 @@ struct window_procs chainin_procs = { chainin_get_color_string, #endif - chainin_start_screen, chainin_end_screen, - chainin_outrip, chainin_preference_update, chainin_getmsghistory, chainin_putmsghistory, chainin_status_init, chainin_status_finish, chainin_status_enablefield, diff --git a/win/chain/wc_chainout.c b/win/chain/wc_chainout.c index a0198173c..72459b883 100644 --- a/win/chain/wc_chainout.c +++ b/win/chain/wc_chainout.c @@ -59,9 +59,6 @@ short chainout_set_font_name(void *,winid, char *); char *chainout_get_color_string(void *); #endif - /* other defs that really should go away (they're tty specific) */ -void chainout_start_screen(void *); -void chainout_end_screen(void *); void chainout_outrip(void *,winid, int, time_t); void chainout_preference_update(void *,const char *); char *chainout_getmsghistory(void *,boolean); @@ -582,23 +579,6 @@ trace_get_color_string(void *vp) #endif -/* other defs that really should go away (they're tty specific) */ -void -chainout_start_screen(void *vp) -{ - struct chainout_data *tdp = vp; - - (*tdp->nprocs->win_start_screen)(); -} - -void -chainout_end_screen(void *vp) -{ - struct chainout_data *tdp = vp; - - (*tdp->nprocs->win_end_screen)(); -} - void chainout_outrip( void *vp, @@ -755,8 +735,6 @@ struct chain_procs chainout_procs = { chainout_get_color_string, #endif - chainout_start_screen, chainout_end_screen, - chainout_outrip, chainout_preference_update, chainout_getmsghistory, chainout_putmsghistory, chainout_status_init, chainout_status_finish, chainout_status_enablefield, diff --git a/win/chain/wc_trace.c b/win/chain/wc_trace.c index 6738d0b0b..7287e82a4 100644 --- a/win/chain/wc_trace.c +++ b/win/chain/wc_trace.c @@ -6,7 +6,6 @@ #include "wintty.h" #include "func_tab.h" -#include #include #ifdef WIN32 @@ -92,9 +91,6 @@ short trace_set_font_name(void *,winid, char *); char *trace_get_color_string(void *); #endif - /* other defs that really should go away (they're tty specific) */ -void trace_start_screen(void *); -void trace_end_screen(void *); void trace_outrip(void *,winid, int, time_t); void trace_preference_update(void *,const char *); char *trace_getmsghistory(void *,boolean); @@ -1012,31 +1008,6 @@ trace_get_color_string(void *vp) #endif -/* other defs that really should go away (they're tty specific) */ -void -trace_start_screen(void *vp) -{ - struct trace_data *tdp = vp; - - fprintf(wc_tracelogf, "%sstart_screen()\n", INDENT); - - PRE; - (*tdp->nprocs->win_start_screen)(tdp->ndata); - POST; -} - -void -trace_end_screen(void *vp) -{ - struct trace_data *tdp = vp; - - fprintf(wc_tracelogf, "%send_screen()\n", INDENT); - - PRE; - (*tdp->nprocs->win_end_screen)(tdp->ndata); - POST; -} - void trace_outrip( void *vp, @@ -1246,8 +1217,6 @@ struct chain_procs trace_procs = { trace_get_color_string, #endif - trace_start_screen, trace_end_screen, - trace_outrip, trace_preference_update, trace_getmsghistory, trace_putmsghistory, trace_status_init, trace_status_finish, trace_status_enablefield, diff --git a/win/curses/Readme.txt b/win/curses/Readme.txt index 2cc207238..a9ae0be7b 100644 --- a/win/curses/Readme.txt +++ b/win/curses/Readme.txt @@ -33,7 +33,7 @@ NetHack 3.7 and above. Windows build instructions: -By default, the Makefile.msc and Makefile.mingw32 are set up to build +By default, the Makefile.nmake and GNUmakefile are set up to build curses from the submodules/pdcurses submodules folder (assumes you obtained your NetHack sources via cloning a git repository), If you obtained your NetHack by another means, such as a zip download, diff --git a/win/curses/cursdial.c b/win/curses/cursdial.c index 5dbe9d796..b6a51eb2a 100644 --- a/win/curses/cursdial.c +++ b/win/curses/cursdial.c @@ -11,7 +11,6 @@ #include "cursmisc.h" #include "cursdial.h" #include "func_tab.h" -#include #if defined(FILENAME_CMP) && !defined(strcasecmp) #define strcasecmp FILENAME_CMP @@ -61,6 +60,7 @@ typedef struct nhm { unsigned long mbehavior; /* menu flags */ boolean reuse_accels; /* Non-unique accelerators per page */ boolean bottom_heavy; /* display multi-page menu starting at end */ + boolean show_obj_syms; /* handle iflags.menuobjsyms */ struct nhm *prev_menu; /* Pointer to previous menu */ struct nhm *next_menu; /* Pointer to next menu */ } nhmenu; @@ -308,6 +308,7 @@ curses_character_input_dialog( answer = curses_read_char(); #endif if (answer == ERR) { + iflags.term_gone = 1; answer = def; break; } @@ -450,8 +451,9 @@ curses_ext_cmd(void) letter = pgetchar(); /* pgetchar(cmd.c) implements do-again */ curs_set(0); prompt_width = (int) strlen(cur_choice); - matches = 0; + if (letter == ERR) + iflags.term_gone = 1; if (letter == '\033' || letter == ERR) { ret = -1; break; @@ -554,6 +556,7 @@ curses_create_nhmenu(winid wid, unsigned long mbehavior) new_menu->mbehavior = mbehavior; new_menu->reuse_accels = FALSE; new_menu->bottom_heavy = FALSE; + new_menu->show_obj_syms = FALSE; return; } @@ -567,6 +570,7 @@ curses_create_nhmenu(winid wid, unsigned long mbehavior) new_menu->mbehavior = mbehavior; new_menu->reuse_accels = FALSE; new_menu->bottom_heavy = FALSE; + new_menu->show_obj_syms = FALSE; new_menu->next_menu = NULL; if (nhmenus == NULL) { /* no menus in memory yet */ @@ -668,7 +672,7 @@ curs_pad_menu( boolean do_pad UNUSED) { nhmenu_item *menu_item_ptr; - int numpages = current_menu->num_pages; + int numpages; /* caller has already called menu_win_size() */ menu_determine_pages(current_menu); /* sets 'menu->num_pages' */ @@ -943,7 +947,7 @@ menu_is_multipage(nhmenu *menu, int width, int height) int curline = 0, accel_per_page = 0; nhmenu_item *menu_item_ptr = menu->entries; - if (*menu->prompt) { + if (menu->prompt && *menu->prompt) { curline += curses_num_lines(menu->prompt, width) + 1; } @@ -979,13 +983,12 @@ menu_determine_pages(nhmenu *menu) int tmpline, num_lines, accel_per_page; int curline = 0; int page_num = 1; - nhmenu_item *menu_item_ptr = menu->entries; + nhmenu_item *menu_item_ptr; int width = menu->width; int height = menu->height; int page_end = height; - - if (*menu->prompt) { + if (menu->prompt && *menu->prompt) { curline += curses_num_lines(menu->prompt, width) + 1; } tmpline = curline; @@ -1032,7 +1035,20 @@ menu_win_size(nhmenu *menu) int maxentrywidth = 0; int maxheaderwidth = menu->prompt ? (int) strlen(menu->prompt) : 0; nhmenu_item *menu_item_ptr, *last_item_ptr = NULL; + boolean only_if_no_headers = (iflags.menuobjsyms & 4) != 0; + /* check entire menu rather than one page at a time */ + menu->show_obj_syms = iflags.use_menu_glyphs; + if (only_if_no_headers) { + for (menu_item_ptr = menu->entries; menu_item_ptr != NULL; + menu_item_ptr = menu_item_ptr->next_item) + if (menu_item_ptr->identifier.a_void == 0) { + menu->show_obj_syms = FALSE; + break; + } + } + +#if 0 /* maxwidth is set below, so the value calculated here isn't used */ if (program_state.gameover) { /* for final inventory disclosure, use full width */ maxwidth = term_cols - 2; /* +2: borders assumed */ @@ -1047,6 +1063,7 @@ menu_win_size(nhmenu *menu) if ((term_cols / 2) > maxwidth) maxwidth = (term_cols / 2); /* Half the screen */ } +#endif maxheight = menu_max_height(); /* First, determine the width of the longest menu entry */ @@ -1061,11 +1078,9 @@ menu_win_size(nhmenu *menu) } else { /* Add space for accelerator (selector letter) */ curentrywidth += 4; -#if 0 /* FIXME: menu glyphs */ if (menu_item_ptr->glyphinfo.glyph != NO_GLYPH - && iflags.use_menu_glyphs) + && menu->show_obj_syms) curentrywidth += 2; -#endif } if (curentrywidth > maxentrywidth) { maxentrywidth = curentrywidth; @@ -1284,19 +1299,17 @@ menu_display_page( entry_cols -= 4; start_col += 4; } -#if 0 - /* FIXME: menuglyphs not implemented yet */ if (menu_item_ptr->glyphinfo.glyph != NO_GLYPH - && iflags.use_menu_glyphs) { - color = (int) menu_item_ptr->glyphinfo.color; + && menu->show_obj_syms) { + color = menu_item_ptr->glyphinfo.gm.sym.color; curses_toggle_color_attr(win, color, NONE, ON); - mvwaddch(win, menu_item_ptr->line_num + 1, start_col, curletter); + mvwaddch(win, menu_item_ptr->line_num + 1, start_col, + menu_item_ptr->glyphinfo.ttychar); curses_toggle_color_attr(win, color, NONE, OFF); mvwaddch(win, menu_item_ptr->line_num + 1, start_col + 1, ' '); entry_cols -= 2; start_col += 2; } -#endif color = menu_item_ptr->color; if (color == NO_COLOR) color = NONE; @@ -1476,8 +1489,6 @@ curs_nonselect_menu_action( } menu_item_ptr = menu_item_ptr->next_item; } - - menu_item_ptr = menu->entries; break; } /* case MENU_SEARCH */ default: @@ -1501,7 +1512,7 @@ menu_get_selections(WINDOW *win, nhmenu *menu, int how) int num_selected = 0; boolean dismiss = FALSE; char selectors[256], groupaccels[256]; - nhmenu_item *menu_item_ptr = menu->entries; + nhmenu_item *menu_item_ptr; activemenu = win; menu_display_page(win, menu, curpage, selectors, groupaccels); @@ -1510,6 +1521,7 @@ menu_get_selections(WINDOW *win, nhmenu *menu, int how) curletter = curses_getch(); if (curletter == ERR) { + iflags.term_gone = 1; num_selected = -1; dismiss = TRUE; } @@ -1556,6 +1568,7 @@ menu_get_selections(WINDOW *win, nhmenu *menu, int how) break; } } + FALLTHROUGH; /*FALLTHRU*/ default: if (curletter > 0 && curletter < 256 diff --git a/win/curses/cursinit.c b/win/curses/cursinit.c index de1811e7f..529b46182 100644 --- a/win/curses/cursinit.c +++ b/win/curses/cursinit.c @@ -8,8 +8,6 @@ #include "wincurs.h" #include "cursinit.h" -#include - /* Initialization and startup functions for curses interface */ /* Private declarations */ @@ -114,6 +112,7 @@ curses_create_main_windows(void) case 3: noperminv_borders = TRUE; + FALLTHROUGH; /*FALLTHRU*/ case 1: /* On */ borders = TRUE; @@ -121,6 +120,7 @@ curses_create_main_windows(void) case 4: noperminv_borders = TRUE; + FALLTHROUGH; /*FALLTHRU*/ case 2: /* Auto */ borders = (term_cols >= 80 + 2 && term_rows >= 24 + 2); diff --git a/win/curses/cursinvt.c b/win/curses/cursinvt.c index 3b0749d5c..4095ceb5a 100644 --- a/win/curses/cursinvt.c +++ b/win/curses/cursinvt.c @@ -187,6 +187,7 @@ curs_scroll_invt(WINDOW *win UNUSED) res = 1; break; } + FALLTHROUGH; /*FALLTHRU*/ case KEY_RIGHT: case KEY_NPAGE: diff --git a/win/curses/cursmain.c b/win/curses/cursmain.c index 5dee9900b..77d1013f6 100644 --- a/win/curses/cursmain.c +++ b/win/curses/cursmain.c @@ -126,8 +126,6 @@ struct window_procs curses_procs = { #endif curses_get_color_string, #endif - curses_start_screen, - curses_end_screen, genl_outrip, curses_preference_update, curses_getmsghistory, @@ -269,6 +267,8 @@ curses_init_nhwindows( # endif/* DEF_GAME_NAME */ PDC_set_title(window_title); PDC_set_blink(TRUE); /* Only if the user asks for it! */ + /* disable the default paste function so control-V works as expected */ + PDC_set_function_key(FUNCTION_KEY_PASTE, 0); timeout(1); (void) getch(); timeout(-1); @@ -622,7 +622,7 @@ curses_putmixed(winid window, int attr, const char *str) /* now send buf to the normal putstr */ curses_putstr(window, attr, buf); done_output = TRUE; - } + } } if (!done_output) { @@ -824,11 +824,11 @@ curses_ctrl_nhwindow( case request_settings: break; case set_menu_promptstyle: - curses_menu_promptstyle.color = wri->fromcore.menu_promptstyle.color; + curses_menu_promptstyle.color = wri->fromcore.menu_promptstyle.color; if (curses_menu_promptstyle.color == NO_COLOR) curses_menu_promptstyle.color = NONE; - attr = wri->fromcore.menu_promptstyle.attr; - curses_menu_promptstyle.attr = curses_convert_attr(attr);; + attr = wri->fromcore.menu_promptstyle.attr; + curses_menu_promptstyle.attr = curses_convert_attr(attr);; break; default: break; @@ -1207,27 +1207,6 @@ curses_delay_output(void) #endif } -/* -start_screen() -- Only used on Unix tty ports, but must be declared for - completeness. Sets up the tty to work in full-screen - graphics mode. Look at win/tty/termcap.c for an - example. If your window-port does not need this function - just declare an empty function. -*/ -void -curses_start_screen(void) -{ -} - -/* -end_screen() -- Only used on Unix tty ports, but must be declared for - completeness. The complement of start_screen(). -*/ -void -curses_end_screen(void) -{ -} - /* outrip(winid, int) -- The tombstone code. We use genl_outrip() from rip.c diff --git a/win/curses/cursmesg.c b/win/curses/cursmesg.c index 894a33721..adc039f52 100644 --- a/win/curses/cursmesg.c +++ b/win/curses/cursmesg.c @@ -8,7 +8,6 @@ #include "wincurs.h" #include "cursmesg.h" #include "curswins.h" -#include /* defined in sys//tty.c or cursmain.c as last resort; set up by curses_init_nhwindows() */ @@ -214,8 +213,9 @@ curses_message_win_puts(const char *message, boolean recursed) mvwadd_wch(win, my, mx, mixed_leadin_cchar); ++mx; message_length--; - have_mixed_leadin = FALSE; mesg_mixed = 0; + have_mixed_leadin = FALSE; + nhUse(have_mixed_leadin); } #endif mvwprintw(win, my, mx, "%s", tmpstr), mx += (int) strlen(tmpstr); @@ -235,8 +235,9 @@ curses_message_win_puts(const char *message, boolean recursed) mvwadd_wch(win, my, mx, mixed_leadin_cchar); ++mx; message_length--; - have_mixed_leadin = FALSE; mesg_mixed = 0; + have_mixed_leadin = FALSE; + nhUse(have_mixed_leadin); } #endif mvwprintw(win, my, mx, "%s", message), mx += message_length; @@ -276,7 +277,8 @@ curscolor(int nhcolor, boolean *boldon) } #endif -void curses_got_input(void) +void +curses_got_input(void) { /* if messages are being suppressed, reenable them */ curs_mesg_suppress_seq = -1L; @@ -346,6 +348,8 @@ curses_block( ret = '\n'; else ret = curses_read_char(); + if (ret == ERR) + iflags.term_gone = 1; if (ret == ERR || ret == '\0') ret = '\n'; /* msgtype=stop should require space/enter rather than any key, @@ -773,6 +777,7 @@ curses_message_win_getline(const char *prompt, char *answer, int buffer) switch (ch) { case ERR: /* should not happen */ + iflags.term_gone = 1; *answer = '\0'; goto alldone; case '\033': /* DOESCAPE */ @@ -932,6 +937,8 @@ mesg_add_line(const char *mline) } else { /* instead of discarding list element being forced out, reuse it */ current_mesg = first_mesg; + assert(current_mesg != NULL); + assert(current_mesg->str != NULL); /* whenever new 'mline' is shorter, extra allocation size of the original element will be frittered away, until eventually we'll discard this 'str' and dupstr() a replacement; we could easily @@ -1069,11 +1076,12 @@ curses_putmsghistory(const char *msg, boolean restoring_msghist) however, we aren't only called when restoring history; core uses putmsghistory() for other stuff during play and those messages should have a normal turn value */ - if (last_mesg) /* appease static analyzer */ + if (last_mesg) { /* appease static analyzer */ last_mesg->turn = restoring_msghist ? (1L << 3) : gh.hero_seq; #ifdef DUMPLOG_CORE - dumplogmsg(last_mesg->str); + dumplogmsg(last_mesg->str); #endif + } } else if (stash_count) { nhprev_mesg *mesg; long mesg_turn; diff --git a/win/curses/cursmisc.c b/win/curses/cursmisc.c index 73a939f2a..4b4fe97c1 100644 --- a/win/curses/cursmisc.c +++ b/win/curses/cursmisc.c @@ -10,8 +10,6 @@ #include "func_tab.h" #include "dlb.h" -#include - /* Misc. curses interface functions */ /* Private declarations */ @@ -291,7 +289,7 @@ curses_break_str(const char *str, int width, int line_num) char *retstr; int curline = 0; int strsize = (int) strlen(str) + 1; -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) && !defined(_MSC_VER) char substr[strsize]; char curstr[strsize]; char tmpstr[strsize]; @@ -345,7 +343,12 @@ curses_break_str(const char *str, int width, int line_num) } if (curline < line_num) { +#if 0 return NULL; +#else + /* callers aren't prepared to handle NULL return */ + Strcpy(curstr, ""); +#endif } retstr = curses_copy_of(curstr); @@ -363,7 +366,7 @@ curses_str_remainder(const char *str, int width, int line_num) char *retstr; int curline = 0; int strsize = strlen(str) + 1; -#if __STDC_VERSION__ >= 199901L +#if (__STDC_VERSION__ >= 199901L) && !defined(_MSC_VER) char substr[strsize]; char tmpstr[strsize]; @@ -801,6 +804,7 @@ curses_convert_keys(int key) a value for ^H greater than 255 is passed back to core's readchar() and stripping the value down to 0..255 yields ^G! */ ret = C('H'); + FALLTHROUGH; /*FALLTHRU*/ default: if (modifiers_available) @@ -1022,6 +1026,7 @@ parse_escape_sequence(int key, boolean *keypadnum) ret = getch(); if (ret == ERR) { + iflags.term_gone = 1; /* there was no additional char; treat as M-O or M-^O below */ ret = (key == '\033') ? 'O' : C('O'); } else if (ret >= 112 && ret <= 121) { /* 'p'..'y' */ diff --git a/win/curses/cursstat.c b/win/curses/cursstat.c index 15168f845..67691bb59 100644 --- a/win/curses/cursstat.c +++ b/win/curses/cursstat.c @@ -173,6 +173,7 @@ curses_status_update( /* decode once instead of every time it's displayed */ status_vals[BL_GOLD][0] = ' '; text = decode_mixed(&status_vals[BL_GOLD][1], text); + nhUse(text); } else if ((fldidx == BL_HUNGER || fldidx == BL_CAP) && (!*text || !strcmp(text, " "))) { /* fieldfmt[] is " %s"; avoid lone space when empty */ @@ -415,6 +416,7 @@ draw_horizontal(boolean border) w -= (t - 30); /* '+= strlen()' below will add 't'; * functional result being 'w += 30' */ } + FALLTHROUGH; /*FALLTHRU*/ case BL_ALIGN: case BL_LEVELDESC: @@ -1231,6 +1233,7 @@ curs_vert_status_vals(int win_width) if (fld_width < hp_width + 3) /* +3: " " gap and "("...")" */ Sprintf(leadingspace, "%*s", (hp_width + 3) - fld_width, " "); + FALLTHROUGH; /*FALLTHRU*/ case BL_VERS: case BL_EXP: diff --git a/win/curses/curswins.c b/win/curses/curswins.c index fa99b58c4..9fe04edd4 100644 --- a/win/curses/curswins.c +++ b/win/curses/curswins.c @@ -98,6 +98,7 @@ curses_create_window(int wid, int width, int height, orient orientation) switch (orientation) { default: impossible("curses_create_window: Bad orientation"); + FALLTHROUGH; /*FALLTHRU*/ case CENTER: startx = (term_cols / 2) - (width / 2); @@ -507,20 +508,27 @@ curs_destroy_all_wins(void) { curses_count_window((char *) 0); /* clean up orphan */ +#if 0 /* this works but confuses the static analyzer (from llvm-19, + * which reports "warning: Use of memory after it is freed") */ while (nhwids) curses_del_wid(nhwids->nhwid); +#else + while (nhwids) { + nethack_wid *tmpptr = nhwids; + + curses_del_wid(tmpptr->nhwid); + } +#endif } /* Print a single character in the given window at the given coordinates */ void -#ifdef ENHANCED_SYMBOLS curses_putch(winid wid, int x, int y, int ch, +#ifdef ENHANCED_SYMBOLS struct unicode_representation *unicode_representation, - int color, int framecolor, int attr) -#else -curses_putch(winid wid, int x, int y, int ch, int color, int framecolor, int attr) #endif + int color, int framecolor, int attr) { static boolean map_initted = FALSE; int sx, sy, ex, ey; diff --git a/win/share/monsters.txt b/win/share/monsters.txt index 1a655b6dd..acc435a2f 100644 --- a/win/share/monsters.txt +++ b/win/share/monsters.txt @@ -50,7 +50,7 @@ Z = (195, 195, 195) ................ } # -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female ants are slightly larger than male ants, just like in real # life. I could have added wings to the male ants, but I felt that # doing so would lead to some confusion. @@ -816,7 +816,7 @@ Z = (195, 195, 195) ................ } # -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female wolves are slightly smaller than male wolves. # There wasn't a great way to show this without making winter wolves # look very similar to winter wolf cubs, so I just made the female @@ -860,7 +860,7 @@ Z = (195, 195, 195) ...L.LLAA..PP... .....LL......... } -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female werewolves wear slightly different clothing. # # tile 43 (werewolf,female) @@ -1395,7 +1395,7 @@ Z = (195, 195, 195) .....CCA...CA... ................ } -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Calico cats are almost exclusively female, so I turned the female cats # into calico cats. The other piece of logic behind this choice was that # players will probably really enjoy seeing different variants of their @@ -1781,7 +1781,7 @@ Z = (195, 195, 195) ....LLL.LLL..... ................ } -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female hobbits wear slightly different clothing. # # tile 91 (hobbit,female) @@ -1803,7 +1803,7 @@ Z = (195, 195, 195) ....LLL.LLL..... ................ } -# Note from contributing artist: NullCGT 27-Dec-2020 +# Note from contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Male and female dwarfs are not differentiated in any way whatsoever. # According to Terry Pratchett (in Unseen Academicals, if I remember # correctly) it is almost impossible to tell what gender a dwarf is, @@ -2551,7 +2551,7 @@ Z = (195, 195, 195) ................ ................ } -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female leprechauns are clean-shaven. Although of course not one # hundred percent accurate, it's convenient visual shorthand. # @@ -4797,7 +4797,7 @@ Z = (195, 195, 195) ..BF.LLAALLAFB.. ................ } -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female Aleax wears slightly different clothing. # # tile 249 (Aleax,female) @@ -4914,7 +4914,7 @@ Z = (195, 195, 195) ................ ................ } -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female archons are clean-shaven. Although of course not one # hundred percent accurate, it's convenient visual shorthand. # @@ -5108,7 +5108,7 @@ Z = (195, 195, 195) ....CA...C...... ................ } -# Note from contributing artist: NullCGT 27-Dec-2020 +# Note from contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Centaur tiles have no differentiation because the different types of # centaurs are already extremely difficult to tell apart from one another. # @@ -6518,7 +6518,7 @@ Z = (195, 195, 195) ................ ................ } -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female gnomes are clean-shaven. Although of course not one # hundred percent accurate, it's convenient visual shorthand. # @@ -6560,7 +6560,7 @@ Z = (195, 195, 195) ................ ................ } -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female gnome leaders are clean-shaven. Although of course not one # hundred percent accurate, it's convenient visual shorthand. # @@ -6640,7 +6640,7 @@ Z = (195, 195, 195) ................ ................ } -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female gnome rulers are clean-shaven. Although of course not one # hundred percent accurate, it's convenient visual shorthand. # @@ -6834,7 +6834,7 @@ Z = (195, 195, 195) .....CLJACLJAAAA ...LLLLJ.CLLLKAA } -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female frost giants are clean-shaven. Although of course not one # hundred percent accurate, it's convenient visual shorthand. # @@ -6990,7 +6990,7 @@ Z = (195, 195, 195) ....CLCACLCAAAAA ..LLLLL.LLLLLAA. } -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female minotaurs wear slightly different clothing. # # tile 363 (minotaur,female) @@ -7449,7 +7449,7 @@ Z = (195, 195, 195) ................ ................ } -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female gnome mummies are clean-shaven. Although of course not one # hundred percent accurate, it's convenient visual shorthand. # @@ -8099,7 +8099,7 @@ Z = (195, 195, 195) ....CJJJCLAAAAAA ..LLLLL.LLLLLAA. } -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female ogre tyrants have slightly different crowns. # # tile 421 (ogre tyrant,female) @@ -8292,7 +8292,7 @@ Z = (195, 195, 195) ....NAAEBAANN... ................ } -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female quantum mechanics have a different hairstyle and no beard. # # tile 431 (quantum mechanic,female) @@ -8314,7 +8314,7 @@ Z = (195, 195, 195) ....NAAEBAANN... ................ } -# Note from contributing artist: NullCGT 27-Dec-2020 +# Note from contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Male and female genetic engineers look the same, because the genetic # engineer tile is perfect. # @@ -9059,7 +9059,7 @@ Z = (195, 195, 195) .J.....LLAA..... ................ } -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female barrow wights look like old grandmothers with flyaway hair. I # kept the hair color the same and used a similar quantity of pixels so # that they look similar enough to the males that you can tell they are @@ -10243,7 +10243,7 @@ Z = (195, 195, 195) ................ ................ } -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female humans wear slightly different clothing. # # tile 533 (human,female) @@ -10322,7 +10322,7 @@ Z = (195, 195, 195) ................ ................ } -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female werejackals wear slightly different clothing. # # tile 537 (werejackal,female) @@ -10363,7 +10363,7 @@ Z = (195, 195, 195) ................ ................ } -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female werewolves wear slightly different clothing. # # tile 539 (werewolf,female) @@ -10594,7 +10594,7 @@ Z = (195, 195, 195) ................ ................ } -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female elven monarchs have slightly different crowns. # # tile 551 (elven monarch,female) @@ -10711,7 +10711,7 @@ Z = (195, 195, 195) .....BPPABPPAAAA ....BPPP.BPPPAAA } -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female guards are clean-shaven. Although of course not one # hundred percent accurate, it's convenient visual shorthand. # @@ -12995,7 +12995,7 @@ Z = (195, 195, 195) .....CJJ.JKJ.... ................ } -# Contributing artist: NullCGT 27-Dec-2020 +# Contributing artist: Kestrel Gregorich-Trevor 27-Dec-2020 # Female archeologist tile is a reference to a certain archeologist # known for raiding tombs. # diff --git a/win/share/objects.txt b/win/share/objects.txt index 1b3f3b556..9c1a3e736 100644 --- a/win/share/objects.txt +++ b/win/share/objects.txt @@ -1456,7 +1456,26 @@ Z = (195, 195, 195) ...A............ ................ } -# tile 74 (morning star) +# tile 74 (silver mace) +{ + ................ + ................ + ................ + .........BPB.... + ........B.N.BP.. + ........PN.N.P.. + ........B.N.BP.. + ........PB.BPP.. + .......KJQPPP... + ......KJA....... + .....KJA........ + ....KJA......... + ...KJA.......... + ..KJA........... + ...A............ + ................ +} +# tile 75 (morning star) { ................ ................ @@ -1475,7 +1494,7 @@ Z = (195, 195, 195) ......KA........ ................ } -# tile 75 (war hammer) +# tile 76 (war hammer) { ..........O..... .........PPOA... @@ -1494,7 +1513,7 @@ Z = (195, 195, 195) .JA............. ................ } -# tile 76 (club) +# tile 77 (club) { ................ ................ @@ -1513,7 +1532,7 @@ Z = (195, 195, 195) .JA............. ................ } -# tile 77 (rubber hose) +# tile 78 (rubber hose) { ................ ................ @@ -1532,7 +1551,7 @@ Z = (195, 195, 195) ....AAA......... ................ } -# tile 78 (staff / quarterstaff) +# tile 79 (staff / quarterstaff) { ................ .............JK. @@ -1551,7 +1570,7 @@ Z = (195, 195, 195) .JJAA........... ..A............. } -# tile 79 (thonged club / aklys) +# tile 80 (thonged club / aklys) { ................ ................ @@ -1570,7 +1589,7 @@ Z = (195, 195, 195) ......OA.OA..... .......OOA...... } -# tile 80 (flail) +# tile 81 (flail) { ................ ............KJ.. @@ -1589,7 +1608,7 @@ Z = (195, 195, 195) ..JA............ ................ } -# tile 81 (bullwhip) +# tile 82 (bullwhip) { ................ ................ @@ -1608,7 +1627,7 @@ Z = (195, 195, 195) ......AAA....... ................ } -# tile 82 (bow) +# tile 83 (bow) { ................ .....K.......... @@ -1627,7 +1646,7 @@ Z = (195, 195, 195) .....KAA........ ................ } -# tile 83 (runed bow / elven bow) +# tile 84 (runed bow / elven bow) { .....K.......... .....KP......... @@ -1646,7 +1665,7 @@ Z = (195, 195, 195) .....KPAAA...... .....KAA........ } -# tile 84 (crude bow / orcish bow) +# tile 85 (crude bow / orcish bow) { ................ ................ @@ -1665,7 +1684,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 85 (long bow / yumi) +# tile 86 (long bow / yumi) { .....L.......... .....LP......... @@ -1684,7 +1703,7 @@ Z = (195, 195, 195) .....LPAAA...... .....LAA........ } -# tile 86 (sling) +# tile 87 (sling) { ................ ................ @@ -1703,7 +1722,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 87 (crossbow) +# tile 88 (crossbow) { ................ ................ @@ -1722,7 +1741,7 @@ Z = (195, 195, 195) ........OA...... ................ } -# tile 88 (leather hat / elven leather helm) +# tile 89 (leather hat / elven leather helm) { ................ ................ @@ -1741,7 +1760,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 89 (iron skull cap / orcish helm) +# tile 90 (iron skull cap / orcish helm) { ................ ................ @@ -1760,7 +1779,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 90 (hard hat / dwarvish iron helm) +# tile 91 (hard hat / dwarvish iron helm) { ................ ................ @@ -1779,7 +1798,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 91 (fedora) +# tile 92 (fedora) { ................ ................ @@ -1798,7 +1817,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 92 (conical hat / cornuthaum) +# tile 93 (conical hat / cornuthaum) { ................ .......E........ @@ -1817,7 +1836,7 @@ Z = (195, 195, 195) ....EEEEEEE..... ................ } -# tile 93 (conical hat / dunce cap) +# tile 94 (conical hat / dunce cap) { ................ .......E........ @@ -1836,7 +1855,7 @@ Z = (195, 195, 195) ....EEEEEEE..... ................ } -# tile 94 (dented pot) +# tile 95 (dented pot) { ................ ................ @@ -1855,7 +1874,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 95 (crystal helmet / helm of brilliance) +# tile 96 (crystal helmet / helm of brilliance) { ................ ................ @@ -1874,7 +1893,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 96 (plumed helmet / helmet) +# tile 97 (plumed helmet / helmet) { .......DID...... ......DIBI...... @@ -1893,7 +1912,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 97 (etched helmet / helm of caution) +# tile 98 (etched helmet / helm of caution) { ................ ................ @@ -1912,7 +1931,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 98 (crested helmet / helm of opposite alignment) +# tile 99 (crested helmet / helm of opposite alignment) { ................ ................ @@ -1931,7 +1950,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 99 (visored helmet / helm of telepathy) +# tile 100 (visored helmet / helm of telepathy) { ................ ................ @@ -1950,7 +1969,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 100 (gray dragon scale mail) +# tile 101 (gray dragon scale mail) { ................ ................ @@ -1969,7 +1988,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 101 (gold dragon scale mail) +# tile 102 (gold dragon scale mail) { ................ ................ @@ -1988,7 +2007,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 102 (silver dragon scale mail) +# tile 103 (silver dragon scale mail) { ................ ................ @@ -2007,7 +2026,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 103 (shimmering dragon scale mail) +# tile 104 (shimmering dragon scale mail) { ................ ................ @@ -2026,7 +2045,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 104 (red dragon scale mail) +# tile 105 (red dragon scale mail) { ................ ................ @@ -2045,7 +2064,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 105 (white dragon scale mail) +# tile 106 (white dragon scale mail) { ................ ................ @@ -2064,7 +2083,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 106 (orange dragon scale mail) +# tile 107 (orange dragon scale mail) { ................ ................ @@ -2083,7 +2102,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 107 (black dragon scale mail) +# tile 108 (black dragon scale mail) { ................ ................ @@ -2102,7 +2121,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 108 (blue dragon scale mail) +# tile 109 (blue dragon scale mail) { ................ ................ @@ -2121,7 +2140,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 109 (green dragon scale mail) +# tile 110 (green dragon scale mail) { ................ ................ @@ -2140,7 +2159,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 110 (yellow dragon scale mail) +# tile 111 (yellow dragon scale mail) { ................ ................ @@ -2159,7 +2178,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 111 (gray dragon scales) +# tile 112 (gray dragon scales) { ................ ................ @@ -2178,7 +2197,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 112 (gold dragon scales) +# tile 113 (gold dragon scales) { ................ ................ @@ -2197,7 +2216,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 113 (silver dragon scales) +# tile 114 (silver dragon scales) { ................ ................ @@ -2216,7 +2235,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 114 (shimmering dragon scales) +# tile 115 (shimmering dragon scales) { ................ ................ @@ -2235,7 +2254,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 115 (red dragon scales) +# tile 116 (red dragon scales) { ................ ................ @@ -2254,7 +2273,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 116 (white dragon scales) +# tile 117 (white dragon scales) { ................ ................ @@ -2273,7 +2292,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 117 (orange dragon scales) +# tile 118 (orange dragon scales) { ................ ................ @@ -2292,7 +2311,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 118 (black dragon scales) +# tile 119 (black dragon scales) { ................ ................ @@ -2311,7 +2330,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 119 (blue dragon scales) +# tile 120 (blue dragon scales) { ................ ................ @@ -2330,7 +2349,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 120 (green dragon scales) +# tile 121 (green dragon scales) { ................ ................ @@ -2349,7 +2368,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 121 (yellow dragon scales) +# tile 122 (yellow dragon scales) { ................ ................ @@ -2368,7 +2387,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 122 (plate mail) +# tile 123 (plate mail) { ................ ................ @@ -2387,7 +2406,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 123 (crystal plate mail) +# tile 124 (crystal plate mail) { ................ ................ @@ -2406,7 +2425,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 124 (bronze plate mail) +# tile 125 (bronze plate mail) { ................ ................ @@ -2425,7 +2444,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 125 (splint mail) +# tile 126 (splint mail) { ................ ................ @@ -2444,7 +2463,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 126 (banded mail) +# tile 127 (banded mail) { ................ ................ @@ -2463,7 +2482,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 127 (dwarvish mithril-coat) +# tile 128 (dwarvish mithril-coat) { ................ ................ @@ -2482,7 +2501,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 128 (elven mithril-coat) +# tile 129 (elven mithril-coat) { ................ ...N.N.NO.O.O... @@ -2501,7 +2520,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 129 (chain mail) +# tile 130 (chain mail) { ................ ................ @@ -2520,7 +2539,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 130 (crude chain mail / orcish chain mail) +# tile 131 (crude chain mail / orcish chain mail) { ................ ................ @@ -2539,7 +2558,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 131 (scale mail) +# tile 132 (scale mail) { ................ ................ @@ -2558,7 +2577,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 132 (studded leather armor) +# tile 133 (studded leather armor) { ................ ................ @@ -2577,7 +2596,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 133 (ring mail) +# tile 134 (ring mail) { ................ ................ @@ -2596,7 +2615,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 134 (crude ring mail / orcish ring mail) +# tile 135 (crude ring mail / orcish ring mail) { ................ ................ @@ -2615,7 +2634,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 135 (leather armor) +# tile 136 (leather armor) { ................ ................ @@ -2634,7 +2653,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 136 (leather jacket) +# tile 137 (leather jacket) { ................ ................ @@ -2653,7 +2672,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 137 (Hawaiian shirt) +# tile 138 (Hawaiian shirt) { ................ ................ @@ -2672,7 +2691,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 138 (T-shirt) +# tile 139 (T-shirt) { ................ ................ @@ -2691,7 +2710,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 139 (mummy wrapping) +# tile 140 (mummy wrapping) { ................ ................ @@ -2710,7 +2729,7 @@ Z = (195, 195, 195) ..N.AAN.NA.OO... ................ } -# tile 140 (faded pall / elven cloak) +# tile 141 (faded pall / elven cloak) { ................ ................ @@ -2729,7 +2748,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 141 (coarse mantelet / orcish cloak) +# tile 142 (coarse mantelet / orcish cloak) { ................ ................ @@ -2748,7 +2767,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 142 (hooded cloak / dwarvish cloak) +# tile 143 (hooded cloak / dwarvish cloak) { ................ ................ @@ -2767,7 +2786,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 143 (slippery cloak / oilskin cloak) +# tile 144 (slippery cloak / oilskin cloak) { ................ ................ @@ -2786,7 +2805,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 144 (robe) +# tile 145 (robe) { ................ ......CCC....... @@ -2805,7 +2824,7 @@ Z = (195, 195, 195) ...ACCCCCCAAA... ....AAAAAAAA.... } -# tile 145 (apron / alchemy smock) +# tile 146 (apron / alchemy smock) { ................ ................ @@ -2824,7 +2843,7 @@ Z = (195, 195, 195) .....NNNNNAA.... ......AAAAA..... } -# tile 146 (leather cloak) +# tile 147 (leather cloak) { ................ ................ @@ -2843,7 +2862,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 147 (tattered cape / cloak of protection) +# tile 148 (tattered cape / cloak of protection) { ................ ................ @@ -2862,7 +2881,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 148 (opera cloak / cloak of invisibility) +# tile 149 (opera cloak / cloak of invisibility) { ................ ......NNN....... @@ -2881,7 +2900,7 @@ Z = (195, 195, 195) ..AANNNNNNAOOAA. ....AAAAAAAAAA.. } -# tile 149 (ornamental cope / cloak of magic resistance) +# tile 150 (ornamental cope / cloak of magic resistance) { ................ ................ @@ -2900,7 +2919,7 @@ Z = (195, 195, 195) ..AAAAAAAAAAAP.. ...PPPPPPPPPPP.. } -# tile 150 (piece of cloth / cloak of displacement) +# tile 151 (piece of cloth / cloak of displacement) { ................ ................ @@ -2919,7 +2938,7 @@ Z = (195, 195, 195) .....PPPPPAA.... .......PPAA..... } -# tile 151 (small shield) +# tile 152 (small shield) { ................ ................ @@ -2938,7 +2957,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 152 (blue and green shield / elven shield) +# tile 153 (blue and green shield / elven shield) { ................ ................ @@ -2957,7 +2976,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 153 (white-handed shield / Uruk-hai shield) +# tile 154 (white-handed shield / Uruk-hai shield) { ................ ...K.KKKJJJ.J... @@ -2976,7 +2995,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 154 (red-eyed shield / orcish shield) +# tile 155 (red-eyed shield / orcish shield) { ................ ................ @@ -2995,7 +3014,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 155 (large shield) +# tile 156 (large shield) { ................ ...N.NNNOOO.O... @@ -3014,7 +3033,7 @@ Z = (195, 195, 195) ........AA...... ................ } -# tile 156 (large round shield / dwarvish roundshield) +# tile 157 (large round shield / dwarvish roundshield) { ................ ................ @@ -3033,7 +3052,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 157 (polished silver shield / shield of reflection) +# tile 158 (polished silver shield / shield of reflection) { ................ ................ @@ -3052,7 +3071,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 158 (old gloves / leather gloves) +# tile 159 (old gloves / leather gloves) { ................ ................ @@ -3071,7 +3090,7 @@ Z = (195, 195, 195) .........AAA.... ................ } -# tile 159 (padded gloves / gauntlets of fumbling) +# tile 160 (padded gloves / gauntlets of fumbling) { ................ ................ @@ -3090,7 +3109,7 @@ Z = (195, 195, 195) .........AAA.... ................ } -# tile 160 (riding gloves / gauntlets of power) +# tile 161 (riding gloves / gauntlets of power) { ................ ................ @@ -3109,7 +3128,7 @@ Z = (195, 195, 195) .........AAA.... ................ } -# tile 161 (fencing gloves / gauntlets of dexterity) +# tile 162 (fencing gloves / gauntlets of dexterity) { ................ ................ @@ -3128,7 +3147,7 @@ Z = (195, 195, 195) .........AAA.... ................ } -# tile 162 (walking shoes / low boots) +# tile 163 (walking shoes / low boots) { ................ ................ @@ -3147,7 +3166,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 163 (hard shoes / iron shoes) +# tile 164 (hard shoes / iron shoes) { ................ ................ @@ -3166,7 +3185,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 164 (jackboots / high boots) +# tile 165 (jackboots / high boots) { .......CCKKKK... ......CKAAAAJJ.. @@ -3185,7 +3204,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 165 (combat boots / speed boots) +# tile 166 (combat boots / speed boots) { ................ ................ @@ -3204,7 +3223,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 166 (jungle boots / water walking boots) +# tile 167 (jungle boots / water walking boots) { ................ ................ @@ -3223,7 +3242,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 167 (hiking boots / jumping boots) +# tile 168 (hiking boots / jumping boots) { ................ ................ @@ -3242,7 +3261,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 168 (mud boots / elven boots) +# tile 169 (mud boots / elven boots) { ................ ................ @@ -3261,7 +3280,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 169 (buckled boots / kicking boots) +# tile 170 (buckled boots / kicking boots) { ................ ................ @@ -3280,7 +3299,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 170 (riding boots / fumble boots) +# tile 171 (riding boots / fumble boots) { ................ ................ @@ -3299,7 +3318,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 171 (snow boots / levitation boots) +# tile 172 (snow boots / levitation boots) { ................ ................ @@ -3318,7 +3337,7 @@ Z = (195, 195, 195) ...A.A.A.A.A.A.. ................ } -# tile 172 (wooden / adornment) +# tile 173 (wooden / adornment) { ................ ................ @@ -3337,7 +3356,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 173 (granite / gain strength) +# tile 174 (granite / gain strength) { ................ ................ @@ -3356,7 +3375,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 174 (opal / gain constitution) +# tile 175 (opal / gain constitution) { ................ ................ @@ -3375,7 +3394,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 175 (clay / increase accuracy) +# tile 176 (clay / increase accuracy) { ................ ................ @@ -3394,7 +3413,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 176 (coral / increase damage) +# tile 177 (coral / increase damage) { ................ ................ @@ -3413,7 +3432,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 177 (black onyx / protection) +# tile 178 (black onyx / protection) { ................ ................ @@ -3432,7 +3451,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 178 (moonstone / regeneration) +# tile 179 (moonstone / regeneration) { ................ ................ @@ -3451,7 +3470,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 179 (tiger eye / searching) +# tile 180 (tiger eye / searching) { ................ ................ @@ -3470,7 +3489,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 180 (jade / stealth) +# tile 181 (jade / stealth) { ................ ................ @@ -3489,7 +3508,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 181 (bronze / sustain ability) +# tile 182 (bronze / sustain ability) { ................ ................ @@ -3508,7 +3527,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 182 (agate / levitation) +# tile 183 (agate / levitation) { ................ ................ @@ -3527,7 +3546,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 183 (topaz / hunger) +# tile 184 (topaz / hunger) { ................ ................ @@ -3546,7 +3565,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 184 (sapphire / aggravate monster) +# tile 185 (sapphire / aggravate monster) { ................ ................ @@ -3565,7 +3584,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 185 (ruby / conflict) +# tile 186 (ruby / conflict) { ................ ................ @@ -3584,7 +3603,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 186 (diamond / warning) +# tile 187 (diamond / warning) { ................ ................ @@ -3603,7 +3622,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 187 (pearl / poison resistance) +# tile 188 (pearl / poison resistance) { ................ ................ @@ -3622,7 +3641,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 188 (iron / fire resistance) +# tile 189 (iron / fire resistance) { ................ ................ @@ -3641,7 +3660,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 189 (brass / cold resistance) +# tile 190 (brass / cold resistance) { ................ ................ @@ -3660,7 +3679,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 190 (copper / shock resistance) +# tile 191 (copper / shock resistance) { ................ ................ @@ -3679,7 +3698,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 191 (twisted / free action) +# tile 192 (twisted / free action) { ................ ................ @@ -3698,7 +3717,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 192 (steel / slow digestion) +# tile 193 (steel / slow digestion) { ................ ................ @@ -3717,7 +3736,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 193 (silver / teleportation) +# tile 194 (silver / teleportation) { ................ ................ @@ -3736,7 +3755,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 194 (gold / teleport control) +# tile 195 (gold / teleport control) { ................ ................ @@ -3755,7 +3774,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 195 (ivory / polymorph) +# tile 196 (ivory / polymorph) { ................ ................ @@ -3774,7 +3793,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 196 (emerald / polymorph control) +# tile 197 (emerald / polymorph control) { ................ ................ @@ -3793,7 +3812,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 197 (wire / invisibility) +# tile 198 (wire / invisibility) { ................ ................ @@ -3812,7 +3831,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 198 (engagement / see invisible) +# tile 199 (engagement / see invisible) { ................ ................ @@ -3831,7 +3850,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 199 (shiny / protection from shape changers) +# tile 200 (shiny / protection from shape changers) { ................ ................ @@ -3850,7 +3869,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 200 (circular / amulet of ESP) +# tile 201 (circular / amulet of ESP) { ................ ......LLLLLAA... @@ -3869,7 +3888,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 201 (spherical / amulet of life saving) +# tile 202 (spherical / amulet of life saving) { ................ ......LLLLLAA... @@ -3888,7 +3907,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 202 (oval / amulet of strangulation) +# tile 203 (oval / amulet of strangulation) { ................ ......LLLLLLAA.. @@ -3907,7 +3926,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 203 (triangular / amulet of restful sleep) +# tile 204 (triangular / amulet of restful sleep) { ................ ......LLLLLAA... @@ -3926,7 +3945,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 204 (pyramidal / amulet versus poison) +# tile 205 (pyramidal / amulet versus poison) { ................ ......LLLLLAA... @@ -3945,7 +3964,7 @@ Z = (195, 195, 195) ....KJJJJJJJJA.. .......AAAAAAA.. } -# tile 205 (square / amulet of change) +# tile 206 (square / amulet of change) { ................ ......LLLLLAA... @@ -3964,7 +3983,7 @@ Z = (195, 195, 195) .......AAAAA.... ................ } -# tile 206 (concave / amulet of unchanging) +# tile 207 (concave / amulet of unchanging) { ................ ......LLLLLAA... @@ -3983,7 +4002,7 @@ Z = (195, 195, 195) .......KCCAA.... ........AAA..... } -# tile 207 (hexagonal / amulet of reflection) +# tile 208 (hexagonal / amulet of reflection) { ................ ......LLLLLAA... @@ -4002,7 +4021,7 @@ Z = (195, 195, 195) ........AAA..... ................ } -# tile 208 (octagonal / amulet of magical breathing) +# tile 209 (octagonal / amulet of magical breathing) { ................ ......LLLLLAA... @@ -4021,7 +4040,7 @@ Z = (195, 195, 195) .......KKKAA.... ........AAA..... } -# tile 209 (perforated / amulet of guarding) +# tile 210 (perforated / amulet of guarding) { ................ ......LLLLLAA... @@ -4040,7 +4059,7 @@ Z = (195, 195, 195) .......KKKAA.... ........AAA..... } -# tile 210 (cubical / amulet of flying) +# tile 211 (cubical / amulet of flying) { ................ ......LLLLLAA... @@ -4059,7 +4078,7 @@ Z = (195, 195, 195) ......CKKKKA.... .......AAAAA.... } -# tile 211 (Amulet of Yendor / cheap plastic imitation of the Amulet of Yendor) +# tile 212 (Amulet of Yendor / cheap plastic imitation of the Amulet of Yendor) { ................ ......HHHHHAA... @@ -4078,7 +4097,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 212 (Amulet of Yendor / Amulet of Yendor) +# tile 213 (Amulet of Yendor / Amulet of Yendor) { ................ ......HHHHHAA... @@ -4097,7 +4116,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 213 (large box) +# tile 214 (large box) { ................ ................ @@ -4116,7 +4135,7 @@ Z = (195, 195, 195) CKKKKKKKKKKJAA.. .AAAAAAAAAAAA... } -# tile 214 (chest) +# tile 215 (chest) { ................ ................ @@ -4135,7 +4154,7 @@ Z = (195, 195, 195) CKKKKKKKKKKJAA.. .AAAAAAAAAAAA... } -# tile 215 (ice box) +# tile 216 (ice box) { ................ ................ @@ -4154,7 +4173,7 @@ Z = (195, 195, 195) NBBBBBBBBBBPAA.. .AAAAAAAAAAAA... } -# tile 216 (bag / sack) +# tile 217 (bag / sack) { ................ ................ @@ -4173,7 +4192,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 217 (bag / oilskin sack) +# tile 218 (bag / oilskin sack) { ................ ................ @@ -4192,7 +4211,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 218 (bag / bag of holding) +# tile 219 (bag / bag of holding) { ................ ................ @@ -4211,7 +4230,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 219 (bag / bag of tricks) +# tile 220 (bag / bag of tricks) { ................ ................ @@ -4230,7 +4249,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 220 (key / skeleton key) +# tile 221 (key / skeleton key) { ................ ................ @@ -4249,7 +4268,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 221 (lock pick) +# tile 222 (lock pick) { ................ ................ @@ -4268,7 +4287,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 222 (credit card) +# tile 223 (credit card) { ................ ................ @@ -4287,7 +4306,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 223 (candle / tallow candle) +# tile 224 (candle / tallow candle) { ................ ................ @@ -4306,7 +4325,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 224 (candle / wax candle) +# tile 225 (candle / wax candle) { ................ ................ @@ -4325,7 +4344,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 225 (brass lantern) +# tile 226 (brass lantern) { ................ ................ @@ -4344,7 +4363,7 @@ Z = (195, 195, 195) .....AAAAAAA.... ................ } -# tile 226 (lamp / oil lamp) +# tile 227 (lamp / oil lamp) { ................ ................ @@ -4363,7 +4382,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 227 (lamp / magic lamp) +# tile 228 (lamp / magic lamp) { ................ ................ @@ -4382,7 +4401,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 228 (expensive camera) +# tile 229 (expensive camera) { ................ ................ @@ -4401,7 +4420,7 @@ Z = (195, 195, 195) ...PPPPPPPPPPPP. ................ } -# tile 229 (looking glass / mirror) +# tile 230 (looking glass / mirror) { ................ ................ @@ -4420,7 +4439,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 230 (glass orb / crystal ball) +# tile 231 (glass orb / crystal ball) { ................ ................ @@ -4439,7 +4458,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 231 (lenses) +# tile 232 (lenses) { ................ ................ @@ -4458,7 +4477,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 232 (blindfold) +# tile 233 (blindfold) { ................ ................ @@ -4477,7 +4496,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 233 (towel) +# tile 234 (towel) { ................ ................ @@ -4496,7 +4515,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 234 (saddle) +# tile 235 (saddle) { ................ ................ @@ -4515,7 +4534,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 235 (leash) +# tile 236 (leash) { ................ ................ @@ -4534,7 +4553,7 @@ Z = (195, 195, 195) .....AAAA....... ................ } -# tile 236 (stethoscope) +# tile 237 (stethoscope) { ................ ................ @@ -4553,7 +4572,7 @@ Z = (195, 195, 195) ........AAA..... ................ } -# tile 237 (tinning kit) +# tile 238 (tinning kit) { ................ ................ @@ -4572,7 +4591,7 @@ Z = (195, 195, 195) ......AAA....... ................ } -# tile 238 (tin opener) +# tile 239 (tin opener) { ................ ................ @@ -4591,7 +4610,7 @@ Z = (195, 195, 195) ........AA...... ................ } -# tile 239 (can of grease) +# tile 240 (can of grease) { ................ ................ @@ -4610,7 +4629,7 @@ Z = (195, 195, 195) .......AAAA..... ................ } -# tile 240 (figurine) +# tile 241 (figurine) { ................ ................ @@ -4629,7 +4648,7 @@ Z = (195, 195, 195) .....JJJJJAA.... ................ } -# tile 241 (magic marker) +# tile 242 (magic marker) { ................ ................ @@ -4648,7 +4667,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 242 (land mine) +# tile 243 (land mine) { ................ ................ @@ -4667,7 +4686,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 243 (beartrap) +# tile 244 (beartrap) { ................ ................ @@ -4686,7 +4705,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 244 (whistle / tin whistle) +# tile 245 (whistle / tin whistle) { ................ ................ @@ -4705,7 +4724,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 245 (whistle / magic whistle) +# tile 246 (whistle / magic whistle) { ................ ................ @@ -4724,7 +4743,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 246 (flute / wooden flute) +# tile 247 (flute / wooden flute) { ................ ................ @@ -4743,7 +4762,7 @@ Z = (195, 195, 195) ..A............. ................ } -# tile 247 (flute / magic flute) +# tile 248 (flute / magic flute) { ................ ................ @@ -4762,7 +4781,7 @@ Z = (195, 195, 195) ..A............. ................ } -# tile 248 (horn / tooled horn) +# tile 249 (horn / tooled horn) { ................ ................ @@ -4781,7 +4800,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 249 (horn / frost horn) +# tile 250 (horn / frost horn) { ................ ................ @@ -4800,7 +4819,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 250 (horn / fire horn) +# tile 251 (horn / fire horn) { ................ ................ @@ -4819,7 +4838,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 251 (horn / horn of plenty) +# tile 252 (horn / horn of plenty) { ................ ................ @@ -4838,7 +4857,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 252 (harp / wooden harp) +# tile 253 (harp / wooden harp) { ................ ................ @@ -4857,7 +4876,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 253 (harp / magic harp) +# tile 254 (harp / magic harp) { ................ ................ @@ -4876,7 +4895,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 254 (bell) +# tile 255 (bell) { ................ .......KA....... @@ -4895,7 +4914,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 255 (bugle) +# tile 256 (bugle) { ................ ................ @@ -4914,7 +4933,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 256 (drum / leather drum) +# tile 257 (drum / leather drum) { ................ ................ @@ -4933,7 +4952,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 257 (drum / drum of earthquake) +# tile 258 (drum / drum of earthquake) { ................ ................ @@ -4952,7 +4971,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 258 (pick-axe) +# tile 259 (pick-axe) { ................ ................ @@ -4971,7 +4990,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 259 (grappling hook) +# tile 260 (grappling hook) { .............N.. ..............P. @@ -4990,7 +5009,7 @@ Z = (195, 195, 195) ..OOA..OOOA..... ....OOOAA....... } -# tile 260 (unicorn horn) +# tile 261 (unicorn horn) { ................ ................ @@ -5009,7 +5028,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 261 (candelabrum / Candelabrum of Invocation) +# tile 262 (candelabrum / Candelabrum of Invocation) { .......N........ .......D........ @@ -5028,7 +5047,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 262 (silver bell / Bell of Opening) +# tile 263 (silver bell / Bell of Opening) { ................ .......OA....... @@ -5047,7 +5066,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 263 (tripe ration) +# tile 264 (tripe ration) { ................ ................ @@ -5066,7 +5085,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 264 (corpse) +# tile 265 (corpse) { ................ .....D.DPLN..... @@ -5085,7 +5104,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 265 (egg) +# tile 266 (egg) { ................ ................ @@ -5104,7 +5123,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 266 (meatball) +# tile 267 (meatball) { ................ ................ @@ -5123,7 +5142,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 267 (meat stick) +# tile 268 (meat stick) { ................ ................ @@ -5142,7 +5161,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 268 (enormous meatball) +# tile 269 (enormous meatball) { ................ ................ @@ -5161,7 +5180,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 269 (meat ring) +# tile 270 (meat ring) { ................ ................ @@ -5180,7 +5199,7 @@ Z = (195, 195, 195) ......AAAAA..... ................ } -# tile 270 (glob of gray ooze) +# tile 271 (glob of gray ooze) { ................ ................ @@ -5199,7 +5218,7 @@ Z = (195, 195, 195) ...AAA.AAAAA.... ................ } -# tile 271 (glob of brown pudding) +# tile 272 (glob of brown pudding) { ................ ................ @@ -5218,7 +5237,7 @@ Z = (195, 195, 195) ...AAA.AAAAA.... ................ } -# tile 272 (glob of green slime) +# tile 273 (glob of green slime) { ................ ................ @@ -5237,7 +5256,7 @@ Z = (195, 195, 195) ...AAA.AAAAA.... ................ } -# tile 273 (glob of black pudding) +# tile 274 (glob of black pudding) { ................ ................ @@ -5256,7 +5275,7 @@ Z = (195, 195, 195) ...AAA.AAAAA.... ................ } -# tile 274 (kelp frond) +# tile 275 (kelp frond) { ....FA.......... ....FFA......... @@ -5275,7 +5294,7 @@ Z = (195, 195, 195) .....FFFFA...... ......FFFFA..... } -# tile 275 (eucalyptus leaf) +# tile 276 (eucalyptus leaf) { ................ ................ @@ -5294,7 +5313,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 276 (apple) +# tile 277 (apple) { ................ ................ @@ -5313,7 +5332,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 277 (orange) +# tile 278 (orange) { ................ ................ @@ -5332,7 +5351,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 278 (pear) +# tile 279 (pear) { ................ ................ @@ -5351,7 +5370,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 279 (melon) +# tile 280 (melon) { ................ ................ @@ -5370,7 +5389,7 @@ Z = (195, 195, 195) ......AAA....... ................ } -# tile 280 (banana) +# tile 281 (banana) { ................ ................ @@ -5389,7 +5408,7 @@ Z = (195, 195, 195) .....AAAAA...... ................ } -# tile 281 (carrot) +# tile 282 (carrot) { ................ ..........F..F.. @@ -5408,7 +5427,7 @@ Z = (195, 195, 195) ...A............ ................ } -# tile 282 (sprig of wolfsbane) +# tile 283 (sprig of wolfsbane) { ................ ................ @@ -5427,7 +5446,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 283 (clove of garlic) +# tile 284 (clove of garlic) { ................ ................ @@ -5446,7 +5465,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 284 (slime mold) +# tile 285 (slime mold) { ................ ................ @@ -5465,7 +5484,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 285 (lump of royal jelly) +# tile 286 (lump of royal jelly) { ................ ................ @@ -5484,7 +5503,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 286 (cream pie) +# tile 287 (cream pie) { ................ ................ @@ -5503,7 +5522,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 287 (candy bar) +# tile 288 (candy bar) { ................ ................ @@ -5522,7 +5541,7 @@ Z = (195, 195, 195) ....A........... ................ } -# tile 288 (fortune cookie) +# tile 289 (fortune cookie) { ................ ................ @@ -5541,7 +5560,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 289 (pancake) +# tile 290 (pancake) { ................ ................ @@ -5560,7 +5579,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 290 (lembas wafer) +# tile 291 (lembas wafer) { ................ ................ @@ -5579,7 +5598,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 291 (cram ration) +# tile 292 (cram ration) { ................ ...JKA.......... @@ -5598,7 +5617,7 @@ Z = (195, 195, 195) .....AAAAAA..... ................ } -# tile 292 (food ration) +# tile 293 (food ration) { ...JJA.......... ...BPA.......... @@ -5617,7 +5636,7 @@ Z = (195, 195, 195) ....KKKKKKKKKA.. .....AAAAAAAA... } -# tile 293 (K-ration) +# tile 294 (K-ration) { ................ ................ @@ -5636,7 +5655,7 @@ Z = (195, 195, 195) ....KKKKKKKKKA.. .....AAAAAAAA... } -# tile 294 (C-ration) +# tile 295 (C-ration) { ................ ................ @@ -5655,7 +5674,7 @@ Z = (195, 195, 195) ....KKKKKKKKKA.. .....AAAAAAAA... } -# tile 295 (tin) +# tile 296 (tin) { ................ ................ @@ -5674,7 +5693,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 296 (ruby / gain ability) +# tile 297 (ruby / gain ability) { ................ ................ @@ -5693,7 +5712,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 297 (pink / restore ability) +# tile 298 (pink / restore ability) { ................ ................ @@ -5712,7 +5731,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 298 (orange / confusion) +# tile 299 (orange / confusion) { ................ ................ @@ -5731,7 +5750,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 299 (yellow / blindness) +# tile 300 (yellow / blindness) { ................ ................ @@ -5750,7 +5769,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 300 (emerald / paralysis) +# tile 301 (emerald / paralysis) { ................ ................ @@ -5769,7 +5788,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 301 (dark green / speed) +# tile 302 (dark green / speed) { ................ ................ @@ -5788,7 +5807,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 302 (cyan / levitation) +# tile 303 (cyan / levitation) { ................ ................ @@ -5807,7 +5826,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 303 (sky blue / hallucination) +# tile 304 (sky blue / hallucination) { ................ ................ @@ -5826,7 +5845,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 304 (brilliant blue / invisibility) +# tile 305 (brilliant blue / invisibility) { ................ ................ @@ -5845,7 +5864,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 305 (magenta / see invisible) +# tile 306 (magenta / see invisible) { ................ ................ @@ -5864,7 +5883,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 306 (purple-red / healing) +# tile 307 (purple-red / healing) { ................ ................ @@ -5883,7 +5902,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 307 (puce / extra healing) +# tile 308 (puce / extra healing) { ................ ................ @@ -5902,7 +5921,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 308 (milky / gain level) +# tile 309 (milky / gain level) { ................ ................ @@ -5921,7 +5940,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 309 (swirly / enlightenment) +# tile 310 (swirly / enlightenment) { ................ ................ @@ -5940,7 +5959,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 310 (bubbly / monster detection) +# tile 311 (bubbly / monster detection) { ................ ................ @@ -5959,7 +5978,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 311 (smoky / object detection) +# tile 312 (smoky / object detection) { ................ ................ @@ -5978,7 +5997,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 312 (cloudy / gain energy) +# tile 313 (cloudy / gain energy) { ................ ................ @@ -5997,7 +6016,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 313 (effervescent / sleeping) +# tile 314 (effervescent / sleeping) { ................ ................ @@ -6016,7 +6035,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 314 (black / full healing) +# tile 315 (black / full healing) { ................ ................ @@ -6035,7 +6054,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 315 (golden / polymorph) +# tile 316 (golden / polymorph) { ................ ................ @@ -6054,7 +6073,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 316 (brown / booze) +# tile 317 (brown / booze) { ................ ................ @@ -6073,7 +6092,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 317 (fizzy / sickness) +# tile 318 (fizzy / sickness) { ................ ................ @@ -6092,7 +6111,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 318 (dark / fruit juice) +# tile 319 (dark / fruit juice) { ................ ................ @@ -6111,7 +6130,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 319 (white / acid) +# tile 320 (white / acid) { ................ ................ @@ -6130,7 +6149,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 320 (murky / oil) +# tile 321 (murky / oil) { ................ ................ @@ -6149,7 +6168,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 321 (clear / water) +# tile 322 (clear / water) { ................ ................ @@ -6168,7 +6187,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 322 (ZELGO MER / enchant armor) +# tile 323 (ZELGO MER / enchant armor) { ................ ................ @@ -6187,7 +6206,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 323 (JUYED AWK YACC / destroy armor) +# tile 324 (JUYED AWK YACC / destroy armor) { ................ ................ @@ -6206,7 +6225,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 324 (NR 9 / confuse monster) +# tile 325 (NR 9 / confuse monster) { ................ ................ @@ -6225,7 +6244,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 325 (XIXAXA XOXAXA XUXAXA / scare monster) +# tile 326 (XIXAXA XOXAXA XUXAXA / scare monster) { ................ ................ @@ -6244,7 +6263,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 326 (PRATYAVAYAH / remove curse) +# tile 327 (PRATYAVAYAH / remove curse) { ................ ................ @@ -6263,7 +6282,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 327 (DAIYEN FOOELS / enchant weapon) +# tile 328 (DAIYEN FOOELS / enchant weapon) { ................ ................ @@ -6282,7 +6301,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 328 (LEP GEX VEN ZEA / create monster) +# tile 329 (LEP GEX VEN ZEA / create monster) { ................ ................ @@ -6301,7 +6320,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 329 (PRIRUTSENIE / taming) +# tile 330 (PRIRUTSENIE / taming) { ................ ................ @@ -6320,7 +6339,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 330 (ELBIB YLOH / genocide) +# tile 331 (ELBIB YLOH / genocide) { ................ ................ @@ -6339,7 +6358,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 331 (VERR YED HORRE / light) +# tile 332 (VERR YED HORRE / light) { ................ ................ @@ -6358,7 +6377,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 332 (VENZAR BORGAVVE / teleportation) +# tile 333 (VENZAR BORGAVVE / teleportation) { ................ ................ @@ -6377,7 +6396,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 333 (THARR / gold detection) +# tile 334 (THARR / gold detection) { ................ ................ @@ -6396,7 +6415,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 334 (YUM YUM / food detection) +# tile 335 (YUM YUM / food detection) { ................ ................ @@ -6415,7 +6434,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 335 (KERNOD WEL / identify) +# tile 336 (KERNOD WEL / identify) { ................ ................ @@ -6434,7 +6453,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 336 (ELAM EBOW / magic mapping) +# tile 337 (ELAM EBOW / magic mapping) { ................ ................ @@ -6453,7 +6472,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 337 (DUAM XNAHT / amnesia) +# tile 338 (DUAM XNAHT / amnesia) { ................ ................ @@ -6472,7 +6491,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 338 (ANDOVA BEGARIN / fire) +# tile 339 (ANDOVA BEGARIN / fire) { ................ ................ @@ -6491,7 +6510,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 339 (KIRJE / earth) +# tile 340 (KIRJE / earth) { ................ ................ @@ -6510,7 +6529,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 340 (VE FORBRYDERNE / punishment) +# tile 341 (VE FORBRYDERNE / punishment) { ................ ................ @@ -6529,7 +6548,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 341 (HACKEM MUCHE / charging) +# tile 342 (HACKEM MUCHE / charging) { ................ ................ @@ -6548,7 +6567,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 342 (VELOX NEB / stinking cloud) +# tile 343 (VELOX NEB / stinking cloud) { ................ ................ @@ -6567,7 +6586,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 343 (FOOBIE BLETCH) +# tile 344 (FOOBIE BLETCH) { ................ ................ @@ -6586,7 +6605,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 344 (TEMOV) +# tile 345 (TEMOV) { ................ ................ @@ -6605,7 +6624,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 345 (GARVEN DEH) +# tile 346 (GARVEN DEH) { ................ ................ @@ -6624,7 +6643,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 346 (READ ME) +# tile 347 (READ ME) { ................ ................ @@ -6643,7 +6662,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 347 (ETAOIN SHRDLU) +# tile 348 (ETAOIN SHRDLU) { ................ ................ @@ -6662,7 +6681,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 348 (LOREM IPSUM) +# tile 349 (LOREM IPSUM) { ................ ................ @@ -6681,7 +6700,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 349 (FNORD) +# tile 350 (FNORD) { ................ ................ @@ -6700,7 +6719,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 350 (KO BATE) +# tile 351 (KO BATE) { ................ ................ @@ -6719,7 +6738,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 351 (ABRA KA DABRA) +# tile 352 (ABRA KA DABRA) { ................ ................ @@ -6738,7 +6757,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 352 (ASHPD SODALG) +# tile 353 (ASHPD SODALG) { ................ ................ @@ -6757,7 +6776,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 353 (ZLORFIK) +# tile 354 (ZLORFIK) { ................ ................ @@ -6776,7 +6795,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 354 (GNIK SISI VLE) +# tile 355 (GNIK SISI VLE) { ................ ................ @@ -6795,7 +6814,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 355 (HAPAX LEGOMENON) +# tile 356 (HAPAX LEGOMENON) { ................ ................ @@ -6814,7 +6833,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 356 (EIRIS SAZUN IDISI) +# tile 357 (EIRIS SAZUN IDISI) { ................ ................ @@ -6833,7 +6852,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 357 (PHOL ENDE WODAN) +# tile 358 (PHOL ENDE WODAN) { ................ ................ @@ -6852,7 +6871,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 358 (GHOTI) +# tile 359 (GHOTI) { ................ ................ @@ -6871,7 +6890,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 359 (MAPIRO MAHAMA DIROMAT) +# tile 360 (MAPIRO MAHAMA DIROMAT) { ................ ................ @@ -6890,7 +6909,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 360 (VAS CORP BET MANI) +# tile 361 (VAS CORP BET MANI) { ................ ................ @@ -6909,7 +6928,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 361 (XOR OTA) +# tile 362 (XOR OTA) { ................ ................ @@ -6928,7 +6947,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 362 (STRC PRST SKRZ KRK) +# tile 363 (STRC PRST SKRZ KRK) { ................ ................ @@ -6947,7 +6966,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 363 (stamped / mail) +# tile 364 (stamped / mail) { ................ ................ @@ -6966,7 +6985,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 364 (unlabeled / blank paper) +# tile 365 (unlabeled / blank paper) { ................ ................ @@ -6985,7 +7004,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 365 (parchment / dig) +# tile 366 (parchment / dig) { ................ ................ @@ -7004,7 +7023,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 366 (vellum / magic missile) +# tile 367 (vellum / magic missile) { ................ ................ @@ -7023,7 +7042,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 367 (ragged / fireball) +# tile 368 (ragged / fireball) { ................ ................ @@ -7042,7 +7061,7 @@ Z = (195, 195, 195) ......OOJJAA.... ................ } -# tile 368 (dog eared / cone of cold) +# tile 369 (dog eared / cone of cold) { ................ ................ @@ -7061,7 +7080,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 369 (mottled / sleep) +# tile 370 (mottled / sleep) { ................ ................ @@ -7080,7 +7099,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 370 (stained / finger of death) +# tile 371 (stained / finger of death) { ................ ................ @@ -7099,7 +7118,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 371 (cloth / light) +# tile 372 (cloth / light) { ................ ................ @@ -7118,7 +7137,7 @@ Z = (195, 195, 195) .......PPPAA.... ................ } -# tile 372 (leathery / detect monsters) +# tile 373 (leathery / detect monsters) { ................ ................ @@ -7137,7 +7156,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 373 (white / healing) +# tile 374 (white / healing) { ................ ................ @@ -7156,7 +7175,7 @@ Z = (195, 195, 195) .......PNNAA.... ................ } -# tile 374 (pink / knock) +# tile 375 (pink / knock) { ................ ................ @@ -7175,7 +7194,7 @@ Z = (195, 195, 195) .......IIIAA.... ................ } -# tile 375 (red / force bolt) +# tile 376 (red / force bolt) { ................ ................ @@ -7194,7 +7213,7 @@ Z = (195, 195, 195) .......DDDAA.... ................ } -# tile 376 (orange / confuse monster) +# tile 377 (orange / confuse monster) { ................ ................ @@ -7213,7 +7232,7 @@ Z = (195, 195, 195) .......CCCAA.... ................ } -# tile 377 (yellow / cure blindness) +# tile 378 (yellow / cure blindness) { ................ ................ @@ -7232,7 +7251,7 @@ Z = (195, 195, 195) .......HHHAA.... ................ } -# tile 378 (velvet / drain life) +# tile 379 (velvet / drain life) { ................ ................ @@ -7251,7 +7270,7 @@ Z = (195, 195, 195) .......EEEAA.... ................ } -# tile 379 (light green / slow monster) +# tile 380 (light green / slow monster) { ................ ................ @@ -7270,7 +7289,7 @@ Z = (195, 195, 195) .......GGGAA.... ................ } -# tile 380 (dark green / wizard lock) +# tile 381 (dark green / wizard lock) { ................ ................ @@ -7289,7 +7308,7 @@ Z = (195, 195, 195) .......FFFAA.... ................ } -# tile 381 (turquoise / create monster) +# tile 382 (turquoise / create monster) { ................ ................ @@ -7308,7 +7327,7 @@ Z = (195, 195, 195) .......FBBAA.... ................ } -# tile 382 (cyan / detect food) +# tile 383 (cyan / detect food) { ................ ................ @@ -7327,7 +7346,7 @@ Z = (195, 195, 195) .......BBBAA.... ................ } -# tile 383 (light blue / cause fear) +# tile 384 (light blue / cause fear) { ................ ................ @@ -7346,7 +7365,7 @@ Z = (195, 195, 195) .......BBBAA.... ................ } -# tile 384 (dark blue / clairvoyance) +# tile 385 (dark blue / clairvoyance) { ................ ................ @@ -7365,7 +7384,7 @@ Z = (195, 195, 195) .......EEEAA.... ................ } -# tile 385 (indigo / cure sickness) +# tile 386 (indigo / cure sickness) { ................ ................ @@ -7384,7 +7403,7 @@ Z = (195, 195, 195) .......EEEAA.... ................ } -# tile 386 (magenta / charm monster) +# tile 387 (magenta / charm monster) { ................ ................ @@ -7403,7 +7422,7 @@ Z = (195, 195, 195) .......IIIAA.... ................ } -# tile 387 (purple / haste self) +# tile 388 (purple / haste self) { ................ ................ @@ -7422,7 +7441,7 @@ Z = (195, 195, 195) .......IIIAA.... ................ } -# tile 388 (violet / detect unseen) +# tile 389 (violet / detect unseen) { ................ ................ @@ -7441,7 +7460,7 @@ Z = (195, 195, 195) .......IIIAA.... ................ } -# tile 389 (tan / levitation) +# tile 390 (tan / levitation) { ................ ................ @@ -7460,7 +7479,7 @@ Z = (195, 195, 195) .......KKKAA.... ................ } -# tile 390 (plaid / extra healing) +# tile 391 (plaid / extra healing) { ................ ................ @@ -7479,7 +7498,7 @@ Z = (195, 195, 195) .......EFDAA.... ................ } -# tile 391 (light brown / restore ability) +# tile 392 (light brown / restore ability) { ................ ................ @@ -7498,7 +7517,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 392 (dark brown / invisibility) +# tile 393 (dark brown / invisibility) { ................ ................ @@ -7517,7 +7536,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 393 (gray / detect treasure) +# tile 394 (gray / detect treasure) { ................ ................ @@ -7536,7 +7555,7 @@ Z = (195, 195, 195) .......PPPAA.... ................ } -# tile 394 (wrinkled / remove curse) +# tile 395 (wrinkled / remove curse) { ................ ................ @@ -7555,7 +7574,7 @@ Z = (195, 195, 195) ......JJKKAA.... ................ } -# tile 395 (dusty / magic mapping) +# tile 396 (dusty / magic mapping) { ................ ................ @@ -7574,7 +7593,7 @@ Z = (195, 195, 195) .KAKA..JJJAA.... ................ } -# tile 396 (bronze / identify) +# tile 397 (bronze / identify) { ................ ................ @@ -7593,7 +7612,7 @@ Z = (195, 195, 195) .......CCCAA.... ................ } -# tile 397 (copper / turn undead) +# tile 398 (copper / turn undead) { ................ ................ @@ -7612,7 +7631,7 @@ Z = (195, 195, 195) .......JCJAA.... ................ } -# tile 398 (silver / polymorph) +# tile 399 (silver / polymorph) { ................ ................ @@ -7631,7 +7650,7 @@ Z = (195, 195, 195) .......PPPAA.... ................ } -# tile 399 (gold / teleport away) +# tile 400 (gold / teleport away) { ................ ................ @@ -7650,7 +7669,7 @@ Z = (195, 195, 195) .......HHHAA.... ................ } -# tile 400 (glittering / create familiar) +# tile 401 (glittering / create familiar) { ................ ................ @@ -7669,7 +7688,7 @@ Z = (195, 195, 195) .......PPPAN.... .......N........ } -# tile 401 (shining / cancellation) +# tile 402 (shining / cancellation) { ....N........... .......N........ @@ -7688,7 +7707,7 @@ Z = (195, 195, 195) .......PPPAA.... ................ } -# tile 402 (dull / protection) +# tile 403 (dull / protection) { ................ ................ @@ -7707,7 +7726,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 403 (thin / jumping) +# tile 404 (thin / jumping) { ................ ................ @@ -7726,7 +7745,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 404 (thick / stone to flesh) +# tile 405 (thick / stone to flesh) { ................ ................ @@ -7745,7 +7764,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 405 (checkered / chain lightning) +# tile 406 (checkered / chain lightning) { ................ ................ @@ -7764,7 +7783,7 @@ Z = (195, 195, 195) .......AAA...... ................ } -# tile 405 (plain / blank paper) +# tile 406 (plain / blank paper) { ................ ................ @@ -7783,7 +7802,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 406 (paperback / novel) +# tile 407 (paperback / novel) { ................ ................ @@ -7802,7 +7821,7 @@ Z = (195, 195, 195) .......EEEAA.... ................ } -# tile 407 (papyrus / Book of the Dead) +# tile 408 (papyrus / Book of the Dead) { ................ ................ @@ -7821,7 +7840,7 @@ Z = (195, 195, 195) .......AAA...... ................ } -# tile 408 (glass / light) +# tile 409 (glass / light) { ................ ................ @@ -7840,7 +7859,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 409 (balsa / secret door detection) +# tile 410 (balsa / secret door detection) { ................ ................ @@ -7859,7 +7878,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 410 (crystal / enlightenment) +# tile 411 (crystal / enlightenment) { ................ ................ @@ -7878,7 +7897,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 411 (maple / create monster) +# tile 412 (maple / create monster) { ................ ................ @@ -7897,7 +7916,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 412 (pine / wishing) +# tile 413 (pine / wishing) { ................ ................ @@ -7916,7 +7935,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 413 (oak / nothing) +# tile 414 (oak / nothing) { ................ ................ @@ -7935,7 +7954,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 414 (ebony / striking) +# tile 415 (ebony / striking) { ................ ................ @@ -7954,7 +7973,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 415 (marble / make invisible) +# tile 416 (marble / make invisible) { ................ ................ @@ -7973,7 +7992,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 416 (tin / slow monster) +# tile 417 (tin / slow monster) { ................ ................ @@ -7992,7 +8011,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 417 (brass / speed monster) +# tile 418 (brass / speed monster) { ................ ................ @@ -8011,7 +8030,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 418 (copper / undead turning) +# tile 419 (copper / undead turning) { ................ ................ @@ -8030,7 +8049,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 419 (silver / polymorph) +# tile 420 (silver / polymorph) { ................ ................ @@ -8049,7 +8068,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 420 (platinum / cancellation) +# tile 421 (platinum / cancellation) { ................ ................ @@ -8068,7 +8087,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 421 (iridium / teleportation) +# tile 422 (iridium / teleportation) { ................ ................ @@ -8087,7 +8106,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 422 (zinc / opening) +# tile 423 (zinc / opening) { ................ ................ @@ -8106,7 +8125,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 423 (aluminum / locking) +# tile 424 (aluminum / locking) { ................ ................ @@ -8125,7 +8144,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 424 (uranium / probing) +# tile 425 (uranium / probing) { ................ ................ @@ -8144,7 +8163,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 425 (iron / digging) +# tile 426 (iron / digging) { ................ ................ @@ -8163,7 +8182,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 426 (steel / magic missile) +# tile 427 (steel / magic missile) { ................ ................ @@ -8182,7 +8201,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 427 (hexagonal / fire) +# tile 428 (hexagonal / fire) { ................ ................ @@ -8201,7 +8220,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 428 (short / cold) +# tile 429 (short / cold) { ................ ................ @@ -8220,7 +8239,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 429 (runed / sleep) +# tile 430 (runed / sleep) { ................ ................ @@ -8239,7 +8258,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 430 (long / death) +# tile 431 (long / death) { ................ .............NO. @@ -8258,7 +8277,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 431 (curved / lightning) +# tile 432 (curved / lightning) { ................ .......NO....... @@ -8277,7 +8296,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 432 (forked) +# tile 433 (forked) { ................ ................ @@ -8296,7 +8315,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 433 (spiked) +# tile 434 (spiked) { ................ ................ @@ -8315,7 +8334,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 434 (jeweled) +# tile 435 (jeweled) { ................ ................ @@ -8334,7 +8353,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 435 (gold piece) +# tile 436 (gold piece) { ................ ................ @@ -8353,7 +8372,7 @@ Z = (195, 195, 195) .........HA..... ...........HA... } -# tile 436 (white / dilithium crystal) +# tile 437 (white / dilithium crystal) { ................ ................ @@ -8372,7 +8391,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 437 (white / diamond) +# tile 438 (white / diamond) { ................ ................ @@ -8391,7 +8410,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 438 (red / ruby) +# tile 439 (red / ruby) { ................ ................ @@ -8410,7 +8429,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 439 (orange / jacinth) +# tile 440 (orange / jacinth) { ................ ................ @@ -8429,7 +8448,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 440 (blue / sapphire) +# tile 441 (blue / sapphire) { ................ ................ @@ -8448,7 +8467,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 441 (black / black opal) +# tile 442 (black / black opal) { ................ ................ @@ -8467,7 +8486,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 442 (green / emerald) +# tile 443 (green / emerald) { ................ ................ @@ -8486,7 +8505,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 443 (green / turquoise) +# tile 444 (green / turquoise) { ................ ................ @@ -8505,7 +8524,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 444 (yellow / citrine) +# tile 445 (yellow / citrine) { ................ ................ @@ -8524,7 +8543,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 445 (green / aquamarine) +# tile 446 (green / aquamarine) { ................ ................ @@ -8543,7 +8562,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 446 (yellowish brown / amber) +# tile 447 (yellowish brown / amber) { ................ ................ @@ -8562,7 +8581,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 447 (yellowish brown / topaz) +# tile 448 (yellowish brown / topaz) { ................ ................ @@ -8581,7 +8600,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 448 (black / jet) +# tile 449 (black / jet) { ................ ................ @@ -8600,7 +8619,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 449 (white / opal) +# tile 450 (white / opal) { ................ ................ @@ -8619,7 +8638,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 450 (yellow / chrysoberyl) +# tile 451 (yellow / chrysoberyl) { ................ ................ @@ -8638,7 +8657,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 451 (red / garnet) +# tile 452 (red / garnet) { ................ ................ @@ -8657,7 +8676,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 452 (violet / amethyst) +# tile 453 (violet / amethyst) { ................ ................ @@ -8676,7 +8695,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 453 (red / jasper) +# tile 454 (red / jasper) { ................ ................ @@ -8695,7 +8714,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 454 (violet / fluorite) +# tile 455 (violet / fluorite) { ................ ................ @@ -8714,7 +8733,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 455 (black / obsidian) +# tile 456 (black / obsidian) { ................ ................ @@ -8733,7 +8752,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 456 (orange / agate) +# tile 457 (orange / agate) { ................ ................ @@ -8752,7 +8771,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 457 (green / jade) +# tile 458 (green / jade) { ................ ................ @@ -8771,7 +8790,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 458 (white / worthless piece of white glass) +# tile 459 (white / worthless piece of white glass) { ................ ................ @@ -8790,7 +8809,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 459 (blue / worthless piece of blue glass) +# tile 460 (blue / worthless piece of blue glass) { ................ ................ @@ -8809,7 +8828,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 460 (red / worthless piece of red glass) +# tile 461 (red / worthless piece of red glass) { ................ ................ @@ -8828,7 +8847,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 461 (yellowish brown / worthless piece of yellowish brown glass) +# tile 462 (yellowish brown / worthless piece of yellowish brown glass) { ................ ................ @@ -8847,7 +8866,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 462 (orange / worthless piece of orange glass) +# tile 463 (orange / worthless piece of orange glass) { ................ ................ @@ -8866,7 +8885,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 463 (yellow / worthless piece of yellow glass) +# tile 464 (yellow / worthless piece of yellow glass) { ................ ................ @@ -8885,7 +8904,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 464 (black / worthless piece of black glass) +# tile 465 (black / worthless piece of black glass) { ................ ................ @@ -8904,7 +8923,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 465 (green / worthless piece of green glass) +# tile 466 (green / worthless piece of green glass) { ................ ................ @@ -8923,7 +8942,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 466 (violet / worthless piece of violet glass) +# tile 467 (violet / worthless piece of violet glass) { ................ ................ @@ -8942,7 +8961,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 467 (gray / luckstone) +# tile 468 (gray / luckstone) { ................ ................ @@ -8961,7 +8980,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 468 (gray / loadstone) +# tile 469 (gray / loadstone) { ................ ................ @@ -8980,7 +8999,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 469 (gray / touchstone) +# tile 470 (gray / touchstone) { ................ ................ @@ -8999,7 +9018,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 470 (gray / flint) +# tile 471 (gray / flint) { ................ ................ @@ -9018,7 +9037,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 471 (rock) +# tile 472 (rock) { ................ ................ @@ -9037,7 +9056,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 472 (boulder) +# tile 473 (boulder) { ................ ................ @@ -9056,7 +9075,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 473 (statue) +# tile 474 (statue) { ................ ........JJ...... @@ -9075,7 +9094,7 @@ Z = (195, 195, 195) .....JJJJJJAA... ................ } -# tile 474 (heavy iron ball) +# tile 475 (heavy iron ball) { ................ ................ @@ -9094,7 +9113,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 475 (iron chain) +# tile 476 (iron chain) { ................ ................ @@ -9113,7 +9132,7 @@ Z = (195, 195, 195) ...........PP.PA ............AA.. } -# tile 476 (splash of venom / splash of blinding venom) +# tile 477 (splash of venom / splash of blinding venom) { ................ ................ @@ -9132,7 +9151,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 477 (splash of venom / splash of acid venom) +# tile 478 (splash of venom / splash of acid venom) { ................ ................ diff --git a/win/share/safeproc.c b/win/share/safeproc.c index 32dec1a5b..19e9aa9b6 100644 --- a/win/share/safeproc.c +++ b/win/share/safeproc.c @@ -65,8 +65,38 @@ * *********************************************************** */ +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), 0L, 0L, + 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, @@ -93,7 +123,7 @@ struct window_procs safe_procs = { #endif safe_get_color_string, #endif - safe_start_screen, safe_end_screen, safe_outrip, + safe_outrip, safe_preference_update, safe_getmsghistory, safe_putmsghistory, safe_status_init, @@ -200,7 +230,7 @@ safe_curs(winid window UNUSED, int x UNUSED, int y UNUSED) } void -safe_putstr(winid window, int attr, const char *str) +safe_putstr(winid window UNUSED, int attr UNUSED, const char *str UNUSED) { return; } @@ -399,7 +429,7 @@ safe_get_ext_cmd(void) } void -safe_number_pad(int mode) +safe_number_pad(int mode UNUSED) { return; } @@ -410,18 +440,6 @@ safe_delay_output(void) return; } -void -safe_start_screen(void) -{ - return; -} - -void -safe_end_screen(void) -{ - return; -} - void safe_outrip(winid tmpwin UNUSED, int how UNUSED, time_t when UNUSED) { @@ -480,13 +498,23 @@ 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 } /************************************************************** diff --git a/win/share/tile2bmp.c b/win/share/tile2bmp.c index 10561a2e7..4b7312aff 100644 --- a/win/share/tile2bmp.c +++ b/win/share/tile2bmp.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 tile2bmp.c $NHDT-Date: 1596498340 2020/08/03 23:45:40 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.32 $ */ +/* NetHack 3.7 tile2bmp.c $NHDT-Date: 1737281026 2025/01/19 02:03:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.51 $ */ /* Copyright (c) NetHack PC Development Team 1995 */ /* NetHack may be freely redistributed. See license for details. */ @@ -60,8 +60,6 @@ lelong(int32_t x) #endif } -unsigned FITSuint_(unsigned long long, const char *, int); - #ifdef __GNUC__ typedef struct tagBMIH { uint32_t biSize; diff --git a/win/share/tilemap.c b/win/share/tilemap.c index ec26f513b..832bae769 100644 --- a/win/share/tilemap.c +++ b/win/share/tilemap.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 tilemap.c $NHDT-Date: 1640987372 2021/12/31 21:49:32 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.64 $ */ +/* NetHack 3.7 tilemap.c $NHDT-Date: 1737720923 2025/01/24 04:15:23 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.87 $ */ /* Copyright (c) 2016 by Michael Allison */ /* NetHack may be freely redistributed. See license for details. */ @@ -54,10 +54,6 @@ extern void exit(int); #endif #endif -#if defined(CROSSCOMPILE) && defined(ENHANCED_SYMBOLS) -#undef ENHANCED_SYMBOLS -#endif - struct { int idx; const char *tilelabel; @@ -197,14 +193,14 @@ struct conditionals_t { const char * tilename(int set, const int file_entry, int gend UNUSED) { - int i, k, cmap, condnum, tilenum, gendnum; + int i, k, cmap, condnum, tilenum; static char buf[BUFSZ]; #if 0 - int offset; + int offset, gendnum; #endif (void) def_char_to_objclass(']'); - condnum = tilenum = gendnum = 0; + tilenum = 0; buf[0] = '\0'; if (set == MON_GLYPH) { @@ -320,7 +316,7 @@ tilename(int set, const int file_entry, int gend UNUSED) /* cmap A */ for (cmap = S_ndoor; cmap <= S_brdnladder; cmap++) { - i = cmap - S_ndoor; + i = cmap - S_ndoor; nhUse(i); if (tilenum == file_entry) { if (*defsyms[cmap].explanation) { return defsyms[cmap].explanation; @@ -368,7 +364,7 @@ tilename(int set, const int file_entry, int gend UNUSED) /* cmap B */ for (cmap = S_grave; cmap < S_arrow_trap + MAXTCHARS; cmap++) { - i = cmap - S_grave; + i = cmap - S_grave; nhUse(i); if (tilenum == file_entry) { if (*defsyms[cmap].explanation) { return defsyms[cmap].explanation; @@ -425,7 +421,7 @@ tilename(int set, const int file_entry, int gend UNUSED) /* cmap C */ for (cmap = S_digbeam; cmap <= S_goodpos; cmap++) { - i = cmap - S_digbeam; + i = cmap - S_digbeam; nhUse(i); if (tilenum == file_entry) { if (*defsyms[cmap].explanation) { return defsyms[cmap].explanation; @@ -471,7 +467,7 @@ tilename(int set, const int file_entry, int gend UNUSED) /* explosions */ for (k = expl_dark; k <= expl_frosty; k++) { for (cmap = S_expl_tl; cmap <= S_expl_br; cmap++) { - i = cmap - S_expl_tl; + i = cmap - S_expl_tl; nhUse(i); if (tilenum == file_entry) { /* substitute "explosion " in the tilelabel with "explosion dark " etc */ @@ -1315,7 +1311,7 @@ extern void objects_globals_init(void); DISABLE_WARNING_UNREACHABLE_CODE int -main(int argc UNUSED, char *argv[] UNUSED) +main(int argc, char *argv[]) { int i, tilenum; char filename[30]; @@ -1366,7 +1362,7 @@ main(int argc UNUSED, char *argv[] UNUSED) #ifdef ENHANCED_SYMBOLS enhanced = ", 0"; /* replace ", utf8rep" since we're done with that */ #endif - Fprintf(ofp, "const glyph_info nul_glyphinfo = { \n"); + Fprintf(ofp, "const glyph_info nul_glyphinfo = {\n"); Fprintf(ofp, "%sNO_GLYPH, ' ', NO_COLOR,\n", indent); Fprintf(ofp, "%s%s/* glyph_map */\n", indent, indent); Fprintf(ofp, "%s%s{ %s, TILE_UNEXPLORED%s }\n", indent, indent, @@ -1399,6 +1395,8 @@ main(int argc UNUSED, char *argv[] UNUSED) free_tilerefs(); exit(EXIT_SUCCESS); /*NOTREACHED*/ + nhUse(argc); + nhUse(argv); return 0; } @@ -1495,7 +1493,7 @@ add_tileref( const char *prefix) { struct tiles_used temp = { 0 }; - static const char ellipsis[] UNUSED = "..."; + static const char ellipsis[] = "..."; char buf[BUFSZ]; if (!tilelist[n]) { @@ -1523,6 +1521,7 @@ add_tileref( (strlen(temp.references) >= (sizeof temp.references - 7) - 1) ? buf : ""); + nhUse(ellipsis); } void diff --git a/win/shim/winshim.c b/win/shim/winshim.c index d83c62a45..ae4caed12 100644 --- a/win/shim/winshim.c +++ b/win/shim/winshim.c @@ -115,7 +115,7 @@ void name fn_args { \ #endif /* __EMSCRIPTEN__ */ VDECLCB(shim_init_nhwindows,(int *argcp, char **argv), "vpp", P2V argcp, P2V argv) -VDECLCB(shim_player_selection,(void), "v") +DECLCB(boolean, shim_player_selection_or_tty,(void), "b") VDECLCB(shim_askname,(void), "v") VDECLCB(shim_get_nh_event,(void), "v") VDECLCB(shim_exit_nhwindows,(const char *str), "vs", P2V str) @@ -123,33 +123,33 @@ VDECLCB(shim_suspend_nhwindows,(const char *str), "vs", P2V str) VDECLCB(shim_resume_nhwindows,(void), "v") DECLCB(winid, shim_create_nhwindow, (int type), "ii", A2P type) VDECLCB(shim_clear_nhwindow,(winid window), "vi", A2P window) -VDECLCB(shim_display_nhwindow,(winid window, boolean blocking), "vii", A2P window, A2P blocking) +VDECLCB(shim_display_nhwindow,(winid window, boolean blocking), "vib", A2P window, A2P blocking) VDECLCB(shim_destroy_nhwindow,(winid window), "vi", A2P window) VDECLCB(shim_curs,(winid a, int x, int y), "viii", A2P a, A2P x, A2P y) VDECLCB(shim_putstr,(winid w, int attr, const char *str), "viis", A2P w, A2P attr, P2V str) -VDECLCB(shim_display_file,(const char *name, boolean complain), "vsi", P2V name, A2P complain) +VDECLCB(shim_display_file,(const char *name, boolean complain), "vsb", P2V name, A2P complain) VDECLCB(shim_start_menu,(winid window, unsigned long mbehavior), "vii", A2P window, A2P mbehavior) VDECLCB(shim_add_menu, (winid window, const glyph_info *glyphinfo, const ANY_P *identifier, char ch, char gch, int attr, int clr, const char *str, unsigned int itemflags), - "vippiiiisi", + "vipi00iisi", A2P window, P2V glyphinfo, P2V identifier, A2P ch, A2P gch, A2P attr, A2P clr, P2V str, A2P itemflags) VDECLCB(shim_end_menu,(winid window, const char *prompt), "vis", A2P window, P2V prompt) /* XXX: shim_select_menu menu_list is an output */ -DECLCB(int, shim_select_menu,(winid window, int how, MENU_ITEM_P **menu_list), "iiio", A2P window, A2P how, P2V menu_list) +DECLCB(int, shim_select_menu,(winid window, int how, MENU_ITEM_P **menu_list), "iiip", A2P window, A2P how, P2V menu_list) DECLCB(char, shim_message_menu,(char let, int how, const char *mesg), "ciis", A2P let, A2P how, P2V mesg) VDECLCB(shim_mark_synch,(void), "v") VDECLCB(shim_wait_synch,(void), "v") VDECLCB(shim_cliparound,(int x, int y), "vii", A2P x, A2P y) -VDECLCB(shim_update_positionbar,(char *posbar), "vp", P2V posbar) -VDECLCB(shim_print_glyph,(winid w, coordxy x, coordxy y, const glyph_info *glyphinfo, const glyph_info *bkglyphinfo), "viiipp", A2P w, A2P x, A2P y, P2V glyphinfo, P2V bkglyphinfo) +VDECLCB(shim_update_positionbar,(char *posbar), "vs", P2V posbar) +VDECLCB(shim_print_glyph,(winid w, coordxy x, coordxy y, const glyph_info *glyphinfo, const glyph_info *bkglyphinfo), "vi11pp", A2P w, A2P x, A2P y, P2V glyphinfo, P2V bkglyphinfo) VDECLCB(shim_raw_print,(const char *str), "vs", P2V str) VDECLCB(shim_raw_print_bold,(const char *str), "vs", P2V str) DECLCB(int, shim_nhgetch,(void), "i") -DECLCB(int, shim_nh_poskey,(coordxy *x, coordxy *y, int *mod), "iooo", P2V x, P2V y, P2V mod) +DECLCB(int, shim_nh_poskey,(coordxy *x, coordxy *y, int *mod), "ippp", P2V x, P2V y, P2V mod) VDECLCB(shim_nhbell,(void), "v") DECLCB(int, shim_doprev_message,(void),"iv") -DECLCB(char, shim_yn_function,(const char *query, const char *resp, char def), "cssi", P2V query, P2V resp, A2P def) -VDECLCB(shim_getlin,(const char *query, char *bufp), "vso", P2V query, P2V bufp) +DECLCB(char, shim_yn_function,(const char *query, const char *resp, char def), "css0", P2V query, P2V resp, A2P def) +VDECLCB(shim_getlin,(const char *query, char *bufp), "vsp", P2V query, P2V bufp) DECLCB(int,shim_get_ext_cmd,(void),"iv") VDECLCB(shim_number_pad,(int state), "vi", A2P state) VDECLCB(shim_delay_output,(void), "v") @@ -158,23 +158,19 @@ VDECLCB(shim_change_background,(int white_or_black), "vi", A2P white_or_black) DECLCB(short, set_shim_font_name,(winid window_type, char *font_name),"2is", A2P window_type, P2V font_name) DECLCB(char *,shim_get_color_string,(void),"sv") -/* other defs that really should go away (they're tty specific) */ -VDECLCB(shim_start_screen, (void), "v") -VDECLCB(shim_end_screen, (void), "v") VDECLCB(shim_preference_update, (const char *pref), "vp", P2V pref) -DECLCB(char *,shim_getmsghistory, (boolean init), "si", A2P init) -VDECLCB(shim_putmsghistory, (const char *msg, boolean restoring_msghist), "vsi", P2V msg, A2P restoring_msghist) +DECLCB(char *,shim_getmsghistory, (boolean init), "sb", A2P init) +VDECLCB(shim_putmsghistory, (const char *msg, boolean restoring_msghist), "vsb", P2V msg, A2P restoring_msghist) VDECLCB(shim_status_init, (void), "v") VDECLCB(shim_status_enablefield, (int fieldidx, const char *nm, const char *fmt, boolean enable), - "vippi", + "vippb", A2P fieldidx, P2V nm, P2V fmt, A2P enable) /* XXX: the second argument to shim_status_update is sometimes an integer and sometimes a pointer */ VDECLCB(shim_status_update, (int fldidx, genericptr_t ptr, int chg, int percent, int color, unsigned long *colormasks), - "vioiiip", + "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() */ @@ -183,19 +179,28 @@ void shim_update_inventory(int a1 UNUSED) { display_inventory(NULL, FALSE); } } + +void shim_player_selection() { + boolean do_genl_player_setup = shim_player_selection_or_tty(); + if (do_genl_player_setup) { + genl_player_setup(80); + } +} + win_request_info * shim_ctrl_nhwindow( winid window UNUSED, int request UNUSED, - win_request_info *wri UNUSED) { + win_request_info *wri) { return (win_request_info *) 0; } #else /* !__EMSCRIPTEN__ */ -VDECLCB(shim_update_inventory,(int a1 UNUSED) -DECLB(win_request_info *, shim_ctrl_nhwindow, +VDECLCB(shim_player_selection, (void), "v") +VDECLCB(shim_update_inventory,(int a1 UNUSED), "vi", A2P a1) +DECLCB(win_request_info *, shim_ctrl_nhwindow, (winid window, int request, win_request_info *wri), "viip", - A2P window UNUSED, A2P request UNUSED, P2V wri UNUSED) + A2P window, A2P request, P2V wri) #endif /* Interface definition used in windows.c */ @@ -203,6 +208,7 @@ struct window_procs shim_procs = { WPID(shim), (0 | WC_ASCII_MAP + | WC_MOUSE_SUPPORT | WC_COLOR | WC_HILITE_PET | WC_INVERSE | WC_EIGHT_BIT_IN), (0 #if defined(SELECTSAVED) @@ -238,8 +244,7 @@ struct window_procs shim_procs = { shim_get_color_string, #endif - /* other defs that really should go away (they're tty specific) */ - shim_start_screen, shim_end_screen, genl_outrip, + genl_outrip, shim_preference_update, shim_getmsghistory, shim_putmsghistory, shim_status_init, @@ -262,7 +267,7 @@ EM_JS(void, local_callback, (const char *cb_name, const char *shim_name, void *r // unrolling and it crashes. Thus we use Asyncify.handleSleep() and wakeUp() to make sure that async doesn't break // Asyncify. For details, see: https://emscripten.org/docs/porting/asyncify.html#optimizing Asyncify.handleSleep(wakeUp => { - // convert callback arguments to proper JavaScript varaidic arguments + // convert callback arguments to proper JavaScript variadic arguments let name = UTF8ToString(shim_name); let fmt = UTF8ToString(fmt_str); let cbName = UTF8ToString(cb_name); @@ -287,40 +292,25 @@ EM_JS(void, local_callback, (const char *cb_name, const char *shim_name, void *r // do the callback let userCallback = globalThis[cbName]; - runJsEventLoop(() => userCallback.call(this, name, ... jsArgs)).then((retVal) => { + userCallback.call(this, name, ... jsArgs).then((retVal) => { // save the return value setPointerValue(name, ret_ptr, retType, retVal); - // return - setTimeout(() => { - reentryMutexUnlock(); + reentryMutexUnlock(); + try { wakeUp(); - }, 0); + } catch (e) { + + } }); function getArg(name, ptr, type) { - return (type === "o")?ptr:getPointerValue(name, getValue(ptr, "*"), type); - } - - // setTimeout() with value of '0' is similar to setImmediate() (but setImmediate isn't standard) - // this lets the JS loop run for a tick so that other events can occur - // XXX: I also tried replacing the for(;;) in allmain.c:moveloop() with emscripten_set_main_loop() - // unfortunately that won't work -- if the simulate_infinite_loop arg is false, it falls through - // and the program ends; - // if is true, it throws an exception to break out of main(), but doesn't get caught because - // the stack isn't running under main() anymore... - // I think this is suboptimal, but we will have to live with it (for now?) - async function runJsEventLoop(cb) { - return new Promise((resolve) => { - setTimeout(() => { - resolve(cb()); - }, 0); - }); + return (type === "p") ? getValue(ptr, "*") : getPointerValue(name, getValue(ptr, "*"), type); } function reentryMutexLock(name) { globalThis.nethackGlobal = globalThis.nethackGlobal || {}; if(globalThis.nethackGlobal.shimFunctionRunning) { - throw new Error(`'${name}' attempting second call to 'local_callback' before '${globalThis.nethackGlobal.shimFunctionRunning}' has finished, will crash emscripten Asyncify. For details see: emscripten.org/docs/porting/asyncify.html#reentrancy`); + console.error(`'${name}' attempting second call to 'local_callback' before '${globalThis.nethackGlobal.shimFunctionRunning}' has finished, will crash emscripten Asyncify. For details see: emscripten.org/docs/porting/asyncify.html#reentrancy`); } globalThis.nethackGlobal.shimFunctionRunning = name; } diff --git a/win/tty/getline.c b/win/tty/getline.c index 1a8215c06..acb3f7f14 100644 --- a/win/tty/getline.c +++ b/win/tty/getline.c @@ -83,6 +83,8 @@ hooked_tty_getlin( c = pgetchar(); term_curs_set(0); if (c == '\033' || c == EOF) { + if (c == EOF) + iflags.term_gone = 1; if (c == '\033' && obufp[0] != '\0') { obufp[0] = '\0'; bufp = obufp; diff --git a/win/tty/termcap.c b/win/tty/termcap.c index b3b141881..3af6333f9 100644 --- a/win/tty/termcap.c +++ b/win/tty/termcap.c @@ -78,7 +78,7 @@ static char tgotobuf[20]; static char tty_standout_on[16], tty_standout_off[16]; void -tty_startup(int *wid, int *hgt) +term_startup(int *wid, int *hgt) { #ifdef TERMLIB const char *term; @@ -336,7 +336,7 @@ tty_startup(int *wid, int *hgt) */ /* deallocate resources prior to final termination */ void -tty_shutdown(void) +term_shutdown(void) { /* we only attempt to clean up a few individual termcap variables */ #if defined(TERMLIB) || defined(ANSI_DEFAULT) @@ -403,8 +403,14 @@ tty_decgraphics_termcap_fixup(void) * Do not select NA ASCII as the primary font since people may * reasonably be using the UK character set. */ - if (SYMHANDLING(H_DEC)) + if (SYMHANDLING(H_DEC)) { xputs("\033)0"); /* "\e)0" load line drawing chars as secondary set */ + /* TI doesn't necessarily do this; explicitly switch to primary + font in case previous program (either before starting nethack + or during a shell escape) left the alternate font active */ + xputs(AE); + } + #ifdef PC9800 init_hilite(); #endif @@ -499,7 +505,7 @@ tty_ascgraphics_hilite_fixup(void) #endif /* PC9800 */ void -tty_start_screen(void) +term_start_screen(void) { xputs(TI); xputs(VS); @@ -531,7 +537,7 @@ tty_start_screen(void) } void -tty_end_screen(void) +term_end_screen(void) { term_clear_screen(); xputs(VE); @@ -658,7 +664,7 @@ void term_clear_screen(void) { /* note: if CL is null, then termcap initialization failed, - so don't attempt screen-oriented I/O during final cleanup. + * so don't attempt screen-oriented I/O during final cleanup. */ if (CL) { xputs(CL); @@ -877,7 +883,7 @@ cl_eos(void) /* free after Robert Viduya */ about reconstructing them after the header file inclusion. */ #undef TRUE #undef FALSE -#define m_move curses_m_move /* Some curses.h decl m_move(), not used here */ +#define m_move curses_m_move /* some curses.h decl m_move(), not used here */ #include @@ -935,7 +941,7 @@ init_hilite(void) iflags.colorcount = colors; int md_len = 0; - if (colors < 8 || (MD == NULL) || (strlen(MD) == 0) + if (colors < 8 || !MD || !*MD || ((setf = tgetstr(nhStr("AF"), (char **) 0)) == (char *) 0 && (setf = tgetstr(nhStr("Sf"), (char **) 0)) == (char *) 0)) { /* Fallback when colors not available @@ -963,23 +969,16 @@ init_hilite(void) if (colors >= 16) { for (c = 0; c < SIZE(ti_map); c++) { - char *work; - /* system colors */ scratch = tparm(setf, ti_map[c].nh_color); - work = (char *) alloc(strlen(scratch) + 1); - Strcpy(work, scratch); - hilites[ti_map[c].nh_color] = work; - + hilites[ti_map[c].nh_color] = dupstr(scratch); /* bright colors */ scratch = tparm(setf, ti_map[c].nh_bright_color); - work = (char *) alloc(strlen(scratch) + 1); - Strcpy(work, scratch); - hilites[ti_map[c].nh_bright_color] = work; + hilites[ti_map[c].nh_bright_color] = dupstr(scratch); } } else { /* 8 system colors */ - md_len = strlen(MD); + md_len = (int) strlen(MD); c = 6; while (c--) { @@ -996,9 +995,8 @@ init_hilite(void) } if (colors >= 16) { - scratch = tparm(setf, COLOR_WHITE|BRIGHT); - hilites[CLR_WHITE] = (char *) alloc(strlen(scratch) + 1); - Strcpy(hilites[CLR_WHITE], scratch); + scratch = tparm(setf, COLOR_WHITE | BRIGHT); + hilites[CLR_WHITE] = dupstr(scratch); } else { scratch = tparm(setf, COLOR_WHITE); hilites[CLR_WHITE] = (char *) alloc(strlen(scratch) + md_len + 1); @@ -1012,8 +1010,7 @@ init_hilite(void) if (iflags.wc2_darkgray) { if (colors >= 16) { scratch = tparm(setf, COLOR_BLACK|BRIGHT); - hilites[CLR_BLACK] = (char *) alloc(strlen(scratch) + 1); - Strcpy(hilites[CLR_BLACK], scratch); + hilites[CLR_BLACK] = dupstr(scratch); } else { /* On many terminals, esp. those using classic PC CGA/EGA/VGA * textmode, specifying "hilight" and "black" simultaneously @@ -1043,9 +1040,11 @@ kill_hilite(void) if (hilites[CLR_BLACK] == nh_HI) return; + /* hilites[] will be set to NULL below, whether freed here or not */ + if (hilites[CLR_BLACK]) { if (hilites[CLR_BLACK] != hilites[CLR_BLUE]) - free(hilites[CLR_BLACK]); + free(hilites[CLR_BLACK]), hilites[CLR_BLACK] = NULL; } if (tgetnum(nhStr("Co")) >= 16) { if (hilites[CLR_BLUE]) @@ -1086,7 +1085,7 @@ kill_hilite(void) free(hilites[CLR_WHITE]); for (c = 0; c < CLR_MAX; c++) - hilites[c] = 0; + hilites[c] = NULL; } #else /* UNIX && TERMINFO */ @@ -1150,7 +1149,7 @@ analyze_seq(char *str, int *fg, int *bg) /* * Sets up highlighting sequences, using ANSI escape sequences (highlight code - * found in print.c). The nh_HI and nh_HE sequences (usually from SO) are + * found in wintty.c). The nh_HI and nh_HE sequences (usually from SO) are * scanned to find foreground and background colors. */ @@ -1173,6 +1172,7 @@ init_hilite(void) hilites[0] = NOCOL; for (c = 1; c < SIZE(hilites); c++) { char *foo; + foo = (char *) alloc(sizeof "\033b0"); if (tos_numcolors > 4) Sprintf(foo, "\033b%c", (c & ~BRIGHT) + '0'); @@ -1216,9 +1216,9 @@ init_hilite(void) if (c == CLR_BLUE) continue; #endif - if (c == foreg) + if (c == foreg) { hilites[c] = (char *) 0; - else if (c != hi_foreg || backg != hi_backg) { + } else if (c != hi_foreg || backg != hi_backg) { hilites[c] = (char *) alloc(sizeof "\033[%d;3%d;4%dm"); Sprintf(hilites[c], "\033[%d", !!(c & BRIGHT)); if ((c | BRIGHT) != (foreg | BRIGHT)) @@ -1341,6 +1341,7 @@ s_atr2str(int n) /* if italic isn't available, fall through to underline */ if (ZH && *ZH) return ZH; + FALLTHROUGH; /*FALLTHRU*/ case ATR_BLINK: case ATR_ULINE: @@ -1351,6 +1352,7 @@ s_atr2str(int n) if (nh_US && *nh_US) return nh_US; } + FALLTHROUGH; /*FALLTHRU*/ case ATR_BOLD: if (MD && *MD) @@ -1378,15 +1380,18 @@ e_atr2str(int n) /* send ZR unless we didn't have ZH and substituted US */ if (ZR && *ZR && ZH && *ZH) return ZR; + FALLTHROUGH; /*FALLTHRU*/ case ATR_ULINE: if (nh_UE && *nh_UE) return nh_UE; + FALLTHROUGH; /*FALLTHRU*/ case ATR_BOLD: case ATR_BLINK: if (nh_HE && *nh_HE) return nh_HE; + FALLTHROUGH; /*FALLTHRU*/ case ATR_DIM: case ATR_INVERSE: @@ -1489,10 +1494,16 @@ term_start_bgcolor(int color) void term_curs_set(int visibility) { + static int vis = -1; + + if (vis == visibility) + return; + if (!visibility && nh_VI) xputs(nh_VI); else if (visibility && nh_VE) xputs(nh_VE); + vis = visibility; } #ifdef CHANGE_COLOR diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 17dcd1cc2..61338542b 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 wintty.c $NHDT-Date: 1717967340 2024/06/09 21:09:00 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.410 $ */ +/* NetHack 3.7 wintty.c $NHDT-Date: 1737691300 2025/01/23 20:01:40 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.420 $ */ /* Copyright (c) David Cohrs, 1991 */ /* NetHack may be freely redistributed. See license for details. */ @@ -27,12 +27,6 @@ extern void msmsg(const char *, ...); #endif #endif -#ifdef MSDOS -#ifdef ENHANCED_SYMBOLS -#undef ENHANCED_SYMBOLS -#endif -#endif /* MSDOS */ - #ifndef NO_TERMS #include "tcap.h" #endif @@ -154,8 +148,7 @@ struct window_procs tty_procs = { tty_get_color_string, #endif - /* other defs that really should go away (they're tty specific) */ - tty_start_screen, tty_end_screen, genl_outrip, + genl_outrip, tty_preference_update, tty_getmsghistory, tty_putmsghistory, tty_status_init, @@ -203,7 +196,7 @@ static int clipy = 0, clipymax = 0; extern void adjust_cursor_flags(struct WinDesc *); #endif -#if defined(ASCIIGRAPH) && !defined(NO_TERMS) +#if defined(ASCIIGRAPH) boolean GFlag = FALSE; boolean HE_resets_AS; /* see termcap.c */ #endif @@ -524,9 +517,9 @@ tty_init_nhwindows(int *argcp UNUSED, char **argv UNUSED) /* * Remember tty modes, to be restored on exit. * - * gettty() must be called before tty_startup() + * gettty() must be called before term_startup() * due to ordering of LI/CO settings - * tty_startup() must be called before initoptions() + * term_startup() must be called before initoptions() * due to ordering of graphics settings */ #if defined(UNIX) || defined(VMS) @@ -535,8 +528,8 @@ tty_init_nhwindows(int *argcp UNUSED, char **argv UNUSED) gettty(); /* to port dependant tty setup */ - tty_startup(&wid, &hgt); - setftty(); /* calls start_screen */ + term_startup(&wid, &hgt); + setftty(); /* calls term_start_screen */ term_curs_set(0); /* set up tty descriptor */ @@ -665,6 +658,7 @@ tty_askname(void) case -1: bail("Until next time then..."); /* quit */ /*NOTREACHED*/ + break; case 0: break; /* no game chosen; start new game */ case 1: @@ -798,7 +792,7 @@ void tty_resume_nhwindows(void) { gettty(); - setftty(); /* calls start_screen */ + setftty(); /* calls term_start_screen */ term_curs_set(0); docrt(); } @@ -845,12 +839,7 @@ tty_exit_nhwindows(const char *str) ttyDisplay = (struct DisplayDesc *) 0; #endif -#ifndef NO_TERMS /*(until this gets added to the window interface)*/ - tty_shutdown(); /* cleanup termcap/terminfo/whatever */ -#endif -#ifdef WIN32CON - consoletty_exit(); -#endif + term_shutdown(); /* cleanup termcap/terminfo/whatever */ iflags.window_inited = 0; } @@ -1084,6 +1073,7 @@ tty_clear_nhwindow(winid window) case NHW_MAP: /* cheap -- clear the whole thing and tell nethack to redraw botl */ disp.botlx = TRUE; + FALLTHROUGH; /*FALLTHRU*/ case NHW_BASE: /* if erasing_tty_screen is True, calling sequence is @@ -1340,7 +1330,8 @@ process_menu_window(winid window, struct WinDesc *cw) tty_menu_item *page_start, *page_end, *curr; long count; int n, attr_n, curr_page, page_lines, resp_len, previous_page_lines; - boolean finished, counting, reset_count; + boolean finished, counting, reset_count, show_obj_syms, + only_if_no_headers = (iflags.menuobjsyms & 4) != 0; char *cp, *rp, resp[QBUFSZ], gacc[QBUFSZ], *msave, *morestr, really_morc; #define MENU_EXPLICIT_CHOICE 0x7f /* pseudo menu manipulation char */ @@ -1388,6 +1379,15 @@ process_menu_window(winid window, struct WinDesc *cw) } resp_len = 0; /* lint suppression */ + show_obj_syms = iflags.use_menu_glyphs; + if (only_if_no_headers) { + for (curr = cw->mlist; curr; curr = curr->next) + if (curr->identifier.a_void == 0) { + show_obj_syms = FALSE; + break; + } + } + /* loop until finished */ while (!finished) { HUPSKIP(); @@ -1442,7 +1442,7 @@ process_menu_window(winid window, struct WinDesc *cw) if (curr->str[0] && curr->str[1] == ' ' && curr->str[2] && strchr("-+#", curr->str[2]) && curr->str[3] == ' ') - /* [0]=letter, [1]==space, [2]=[-+#], [3]=space */ + /* [0]=letter, [1]=space, [2]=[-+#], [3]=space */ attr_n = 4; /* [4:N]=entry description */ /* @@ -1464,15 +1464,25 @@ process_menu_window(winid window, struct WinDesc *cw) if (n == attr_n && (color != NO_COLOR || attr != ATR_NONE)) toggle_menu_attr(TRUE, color, attr); - if (n == 2 - && curr->identifier.a_void != 0 + if (n == 2 && curr->identifier.a_void != 0 && curr->selected) { - if (curr->count == -1L) - (void) putchar('+'); /* all selected */ - else - (void) putchar('#'); /* count selected */ - } else + char c = (curr->count == -1L) ? '*' : '#'; + + /* all selected: '*' vs count selected: '#' */ + (void) putchar(c); + } else if (n == 2 && curr->identifier.a_void != 0 + && show_obj_syms + && curr->glyphinfo.glyph != NO_GLYPH) { + int gcolor = curr->glyphinfo.gm.sym.color; + + /* tty_print_glyph could be used, but is overkill + and requires referencing the cursor location */ + toggle_menu_attr(TRUE, gcolor, ATR_NONE); + (void) putchar(curr->glyphinfo.ttychar); + toggle_menu_attr(FALSE, gcolor, ATR_NONE); + } else { (void) putchar(*cp); + } } /* for *cp */ if (n > attr_n && (color != NO_COLOR || attr != ATR_NONE)) toggle_menu_attr(FALSE, color, attr); @@ -1721,6 +1731,7 @@ process_menu_window(winid window, struct WinDesc *cw) break; case MENU_EXPLICIT_CHOICE: morc = really_morc; + FALLTHROUGH; /*FALLTHRU*/ default: if (cw->how == PICK_NONE || !strchr(resp, morc)) { @@ -1878,12 +1889,14 @@ tty_display_nhwindow( tty_display_nhwindow(WIN_MESSAGE, TRUE); return; } + FALLTHROUGH; /*FALLTHRU*/ case NHW_BASE: (void) fflush(stdout); break; case NHW_TEXT: cw->maxcol = ttyDisplay->cols; /* force full-screen mode */ + FALLTHROUGH; /*FALLTHRU*/ case NHW_MENU: cw->active = 1; @@ -1951,6 +1964,7 @@ tty_dismiss_nhwindow(winid window) if (ttyDisplay->toplin != TOPLINE_EMPTY) tty_display_nhwindow(WIN_MESSAGE, TRUE); nhassert(ttyDisplay->toplin == TOPLINE_EMPTY); + FALLTHROUGH; /*FALLTHRU*/ case NHW_STATUS: case NHW_BASE: @@ -2042,8 +2056,7 @@ erase_tty_screen(void) void tty_curs( winid window, - int x, int y) /* not xchar: perhaps xchar is unsigned - * then curx-x would be unsigned too */ + int x, int y) /* coordinates (and curx-x) must be signed */ { struct WinDesc *cw = 0; int cx = ttyDisplay->curx; @@ -2544,8 +2557,8 @@ tty_start_menu(winid window, unsigned long mbehavior) void tty_add_menu( winid window, /* window to use, must be of type NHW_MENU */ - const glyph_info *glyphinfo UNUSED, /* glyph info with glyph to - * display with item */ + const glyph_info *glyphinfo, /* glyph info with glyph to + * display with item */ const anything *identifier, /* what to return if selected */ char ch, /* selector letter (0 = pick our own) */ char gch, /* group accelerator (0 = no group) */ @@ -2602,6 +2615,7 @@ tty_add_menu( item->attr = attr; item->color = clr; item->str = dupstr(newstr); + item->glyphinfo = *glyphinfo; item->next = cw->mlist; cw->mlist = item; @@ -2861,21 +2875,28 @@ tty_ctrl_nhwindow( wri->tocore = zero_tocore; tty_ok = assesstty(ttyinvmode, &offx, &offy, &rows, &cols, &maxcol, &minrow, &maxrow); - wri->tocore.needrows = (int) (minrow + 1 + ROWNO + StatusRows()); - wri->tocore.needcols = (int) tty_perminv_mincol; - wri->tocore.haverows = (int) ttyDisplay->rows; - wri->tocore.havecols = (int) ttyDisplay->cols; - if (!tty_ok) { -#ifdef RESIZABLE - /* terminal isn't big enough right now but player might resize it - and then use 'm O' to try to set 'perm_invent' again */ - wri->tocore.tocore_flags |= too_small; -#else - wri->tocore.tocore_flags |= prohibited; -#endif + if (!tty_ok && rows == 0 && cols == 0) { + /* something is terribly wrong, possibly too early in startup */ + wri->tocore.tocore_flags |= too_early; } else { - maxslot = (maxrow - 2) * (!inuse_only ? 2 : 1); - wri->tocore.maxslot = maxslot; + wri->tocore.needrows = (int) (minrow + 1 + ROWNO + StatusRows()); + wri->tocore.needcols = (int) tty_perminv_mincol; + wri->tocore.haverows = (int) ttyDisplay->rows; + wri->tocore.havecols = (int) ttyDisplay->cols; + if (!tty_ok) { +#ifdef RESIZABLE + /* terminal isn't big enough right now but player might + * resize it and then use 'm O' to try to set 'perm_invent' + * again + */ + wri->tocore.tocore_flags |= too_small; +#else + wri->tocore.tocore_flags |= prohibited; +#endif + } else { + maxslot = (maxrow - 2) * (!inuse_only ? 2 : 1); + wri->tocore.maxslot = maxslot; + } } #endif /* TTY_PERM_INVENT */ break; @@ -3394,7 +3415,7 @@ ttyinv_populate_slot( if (cell->color != color + 1) { /* offset color by 1 so 0 is not valid */ if (ccnt >= (col + clroffset)) - cell->color = color + 1; + cell->color = color + 1; else cell->color = NO_COLOR + 1; cell->refresh = 1; @@ -3542,6 +3563,14 @@ assesstty( show_gold = (invmode & InvShowGold) != 0 && !inuse_only; int perminv_minrow = tty_perminv_minrow + (show_gold ? 1 : 0); + if (!ttyDisplay) { + /* too early */ + *offx = *offy = *rows = *cols = 0; + *maxcol = 0; + *minrow = *maxrow = 0; + return !(*rows < perminv_minrow || *cols < tty_perminv_mincol); + } + *offx = 0; /* topline + map rows + status lines */ *offy = 1 + ROWNO + StatusRows(); /* 1 + 21 + (2 or 3) */ @@ -3594,7 +3623,7 @@ tty_wait_synch(void) { HUPSKIP(); /* we just need to make sure all windows are synch'd */ - if (!ttyDisplay || ttyDisplay->rawprint) { + if (WIN_MAP == WIN_ERR || !ttyDisplay || ttyDisplay->rawprint) { getret(); if (ttyDisplay) ttyDisplay->rawprint = 0; @@ -3705,15 +3734,15 @@ end_glyphout(void) } } -#ifndef WIN32CON void g_putch(int in_ch) { char ch = (char) in_ch; +#ifndef WIN32CON HUPSKIP(); -#if defined(ASCIIGRAPH) && !defined(NO_TERMS) +#if defined(ASCIIGRAPH) if (SYMHANDLING(H_UTF8)) { (void) putchar(ch); } else if (SYMHANDLING(H_IBM) @@ -3747,14 +3776,16 @@ g_putch(int in_ch) (void) putchar(ch); } -#else +#else /* ?ASCIIGRAPH */ (void) putchar(ch); -#endif /* ASCIIGRAPH && !NO_TERMS */ - +#endif /* ASCIIGRAPH */ +#else /* WIN32CON */ + console_g_putch(in_ch); + nhUse(ch); +#endif /* WIN32CON */ return; } -#endif /* !WIN32CON */ #if defined(UNIX) || defined(VMS) #if defined(ENHANCED_SYMBOLS) @@ -3950,13 +3981,12 @@ term_start_bgcolor(int color) { /* placeholder for now */ } -#endif /* !MSDOS && !WIN32 */ - void term_curs_set(int visibility UNUSED) { /* nothing */ } +#endif #ifdef CHANGE_COLOR void @@ -4060,8 +4090,10 @@ tty_nhgetch(void) term_curs_set(0); if (!i) i = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */ - else if (i == EOF) + else if (i == EOF) { + iflags.term_gone = 1; i = '\033'; /* same for EOF */ + } /* topline has been seen - we can clear the need for --More-- */ if (ttyDisplay && ttyDisplay->toplin == TOPLINE_NEED_MORE) ttyDisplay->toplin = TOPLINE_NON_EMPTY; @@ -4438,6 +4470,7 @@ tty_status_update( switch (fldidx) { case BL_RESET: reset_state = FORCE_RESET; + FALLTHROUGH; /*FALLTHRU*/ case BL_FLUSH: if (make_things_fit(reset_state) || truncation_expected) { @@ -4458,6 +4491,7 @@ tty_status_update( break; case BL_GOLD: text = decode_mixed(goldbuf, text); + FALLTHROUGH; /*FALLTHRU*/ default: attrmask = (color >> 8) & 0x00FF; @@ -4506,6 +4540,7 @@ tty_status_update( break; case BL_LEVELDESC: dlvl_shrinklvl = 0; /* caller is passing full length string */ + FALLTHROUGH; /*FALLTHRU*/ case BL_HUNGER: /* The core sends trailing blanks for some fields. @@ -5095,7 +5130,7 @@ render_status(void) if (hpbar_crit_hp) repad_with_dashes(bar); bar_len = (int) strlen(bar); /* always 30 */ - tlth = bar_len + 2; + /*tlth = bar_len + 2; // not needed within this 'if'*/ attrmask = 0; /* for the second part only case: dead */ /* when at full HP, the whole title will be highlighted; when injured or dead, there will be a second portion diff --git a/win/win32/NetHackW.c b/win/win32/NetHackW.c index 3103f8045..45f8bf709 100644 --- a/win/win32/NetHackW.c +++ b/win/win32/NetHackW.c @@ -53,7 +53,6 @@ Version _WIN_32IE Platform/IE /*#define COMCTL_URL * "http://www.microsoft.com/msdownload/ieplatform/ie/comctrlx86.asp"*/ -ATTRNORETURN extern void nethack_exit(int) NORETURN; static TCHAR *_get_cmd_arg(TCHAR *pCmdLine); static HRESULT GetComCtlVersion(LPDWORD pdwMajor, LPDWORD pdwMinor); BOOL WINAPI @@ -73,7 +72,7 @@ int GUILaunched = TRUE; /* We tell shared startup code in windmain.c // Forward declarations of functions included in this code module: extern boolean main(int, char **); -static void __cdecl mswin_moveloop(void *); +//static void __cdecl mswin_moveloop(void *); #define MAX_CMDLINE_PARAM 255 @@ -82,13 +81,16 @@ static void __cdecl mswin_moveloop(void *); #pragma warning( disable : 28251) #endif +extern int nethackw_main(int argc, char *argv[]); + +static char *argv[MAX_CMDLINE_PARAM]; + int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { INITCOMMONCONTROLSEX InitCtrls; int argc; - char *argv[MAX_CMDLINE_PARAM]; size_t len; TCHAR *p; TCHAR wbuf[BUFSZ]; @@ -128,7 +130,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, windowprocs.win_wait_synch = mswin_wait_synch; win10_init(); - early_init(0, NULL); /* Change as needed to support CRASHREPORT */ + early_init(0, NULL); /* Change as needed to support CRASHREPORT */ /* init application structure */ _nethack_app.hApp = hInstance; @@ -163,6 +165,8 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, _nethack_app.bNoHScroll = FALSE; _nethack_app.bNoVScroll = FALSE; + if (_nethack_app.saved_text) + free(_nethack_app.saved_text), _nethack_app.saved_text = 0; _nethack_app.saved_text = strdup(""); _nethack_app.bAutoLayout = TRUE; @@ -210,14 +214,14 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, for (argc = 1; p && argc < MAX_CMDLINE_PARAM; argc++) { len = _tcslen(p); if (len > 0) { - argv[argc] = _strdup(NH_W2A(p, buf, BUFSZ)); + argv[argc] = strdup(NH_W2A(p, buf, BUFSZ)); } else { - argv[argc] = ""; + argv[argc] = strdup(""); } p = _get_cmd_arg(NULL); } GetModuleFileName(NULL, wbuf, BUFSZ); - argv[0] = _strdup(NH_W2A(wbuf, buf, BUFSZ)); + argv[0] = strdup(NH_W2A(wbuf, buf, BUFSZ)); if (argc == 2) { TCHAR *savefile = strdup(argv[1]); @@ -232,8 +236,8 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, if (*p) { if (strcmp(p + 1, "NetHack-saved-game") == 0) { *p = '\0'; - argv[1] = "-u"; - argv[2] = _strdup(name); + argv[1] = strdup("-u"); + argv[2] = strdup(name); argc = 3; } } @@ -241,16 +245,36 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, free(savefile); } GUILaunched = 1; - /* let main do the argument processing */ -#ifndef __MINGW32__ - main(argc, argv); -#else - int mingw_main(int argc, char *argv[]); - mingw_main(argc, argv); -#endif + /* let nethackw_main do the argument processing */ + nethackw_main(argc, argv); + /* not reached */ return 0; } +extern void free_menu_data(void); + +void +free_winmain_stuff(void) +{ + int cnt; + + for (cnt = 0; cnt < MAX_CMDLINE_PARAM; ++cnt) { + if (argv[cnt]) + free((genericptr_t) argv[cnt]), argv[cnt] = 0; + } + if (_nethack_app.saved_text) + free((genericptr_t) _nethack_app.saved_text), + _nethack_app.saved_text = 0; + for (cnt = 0; cnt < MAXWINDOWS; ++cnt) { + if (windowdata[cnt].address) { + if (!windowdata[cnt].isstatic) + free(windowdata[cnt].address); + windowdata[cnt].address = 0; + } + } + free_menu_data(); +} + #ifdef _MSC_VER #pragma warning( pop ) #endif @@ -302,7 +326,7 @@ _get_cmd_arg(TCHAR *pCmdLine) } else { pArgs = NULL; } - + nhUse(bQuoted); return pRetArg; } diff --git a/win/win32/mhdlg.c b/win/win32/mhdlg.c index afa55c1c9..94a7ecf3e 100644 --- a/win/win32/mhdlg.c +++ b/win/win32/mhdlg.c @@ -12,7 +12,10 @@ #include "mhdlg.h" #include - +int list_view_height(HWND hWnd, int count); +void get_rect_size(RECT *rect, SIZE *size); +void center_dialog(HWND dialog); +void size_dialog(HWND dialog, SIZE new_client_size); /*---------------------------------------------------------------*/ /* data for getlin dialog */ @@ -146,7 +149,8 @@ GetlinDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) (WPARAM) sizeof(wbuf2), (LPARAM) wbuf2); NH_W2A(wbuf2, data->result, data->result_size); - /* Fall through. */ + FALLTHROUGH; + /* FALLTHRU */ /* cancel button was pressed */ case IDCANCEL: @@ -246,7 +250,8 @@ ExtCmdDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) hWnd, IDC_EXTCMD_LIST, LB_GETCURSEL, (WPARAM) 0, (LPARAM) 0); if (*data->selection == LB_ERR) *data->selection = -1; - /* Fall through. */ + FALLTHROUGH; + /* FALLTHRU */ /* CANCEL button ws clicked */ case IDCANCEL: @@ -344,6 +349,11 @@ INT_PTR CALLBACK PlayerSelectorDlgProc(HWND, UINT, WPARAM, LPARAM); static void plselAdjustSelections(HWND hWnd); static boolean plselRandomize(plsel_data_t * data); static BOOL plselDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam); +void calculate_player_selector_layout(plsel_data_t * data); +void move_controls(control_t * controls, int count); +void do_player_selector_layout(plsel_data_t * data); +void plselInitDialog(struct plsel_data * data); +int plselFinalSelection(HWND hWnd); boolean mswin_player_selection_window(void) @@ -872,7 +882,7 @@ plselAdjustSelections(HWND hWnd) /* player made up his mind - get final selection here */ int -plselFinalSelection(HWND hWnd) +plselFinalSelection(HWND hWnd UNUSED) { int role, race, gender, alignment; @@ -982,7 +992,7 @@ plselDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam) struct plsel_data * data = (plsel_data_t *) GetWindowLongPtr(hWnd, GWLP_USERDATA); /* If there are no list box items, skip this message. */ - if (lpdis->itemID < 0) + if (lpdis->itemID == (UINT) -1) return FALSE; HWND control = GetDlgItem(hWnd, (int) wParam); diff --git a/win/win32/mhdlg.h b/win/win32/mhdlg.h index 0b08e8fcb..7eb0b8eb9 100644 --- a/win/win32/mhdlg.h +++ b/win/win32/mhdlg.h @@ -12,6 +12,6 @@ int mswin_getlin_window(const char *question, char *result, size_t result_size); int mswin_ext_cmd_window(int *selection); -boolean mswin_player_selection_window(); +boolean mswin_player_selection_window(void); #endif /* MSWINDlgWindow_h */ diff --git a/win/win32/mhmain.c b/win/win32/mhmain.c index bd1a5be79..f3eae350c 100644 --- a/win/win32/mhmain.c +++ b/win/win32/mhmain.c @@ -25,8 +25,9 @@ extern winid WIN_STATUS; static TCHAR szMainWindowClass[] = TEXT("MSNHMainWndClass"); static TCHAR szTitle[MAX_LOADSTRING]; +struct window_tracking_data windowdata[MAXWINDOWS]; extern void mswin_display_splash_window(BOOL); - +extern void free_menu_data(void); /* mhmenu.c */ LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); static LRESULT onWMCommand(HWND hWnd, WPARAM wParam, LPARAM lParam); @@ -208,9 +209,11 @@ MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) data = (PNHMainWindow) malloc(sizeof(NHMainWindow)); if (!data) panic("out of memory"); + ZeroMemory(data, sizeof(NHMainWindow)); data->mapAcsiiModeSave = MAP_MODE_ASCII12x16; SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) data); + windowdata[NHW_MAIN].address = (genericptr_t) data; /* update menu items */ CheckMenuItem( @@ -509,7 +512,7 @@ MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) /* clean up */ free((PNHMainWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA)); SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) 0); - + windowdata[NHW_MAIN].address = 0; // PostQuitMessage(0); exit(1); break; @@ -541,6 +544,7 @@ onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) mswin_select_map_mode(iflags.wc_map_mode); child = GetNHApp()->windowlist[msg_param->wid].win; + nhUse(child); } break; case MSNH_MSG_RANDOM_INPUT: { @@ -750,14 +754,18 @@ mswin_layout_main_window(HWND changed_child) } if (IsWindow(changed_child)) SetForegroundWindow(changed_child); + nhUse(data); } +VOID CALLBACK FuzzTimerProc( _In_ HWND hwnd, + _In_ UINT uMsg, _In_ UINT_PTR idEvent, + _In_ DWORD dwTime); + VOID CALLBACK FuzzTimerProc( _In_ HWND hwnd, - _In_ UINT uMsg, - _In_ UINT_PTR idEvent, - _In_ DWORD dwTime - ) + _In_ UINT uMsg UNUSED, + _In_ UINT_PTR idEvent UNUSED, + _In_ DWORD dwTime UNUSED) { INPUT input[16]; int i_pos = 0; @@ -1019,7 +1027,8 @@ onWMCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) pFile = _tfopen(filename, TEXT("wt+,ccs=UTF-8")); if (!pFile) { TCHAR buf[4096]; - _stprintf(buf, TEXT("Cannot open %s for writing!"), filename); + nh_stprintf(buf, sizeof buf, + TEXT("Cannot open %s for writing!"), filename); NHMessageBox(hWnd, buf, MB_OK | MB_ICONERROR); if (text) free(text); @@ -1110,6 +1119,7 @@ onWMCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) default: return 1; } + nhUse(wmEvent); return 0; } diff --git a/win/win32/mhmap.c b/win/win32/mhmap.c index 741cea720..d5e6b116e 100644 --- a/win/win32/mhmap.c +++ b/win/win32/mhmap.c @@ -641,6 +641,7 @@ MapWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) DeleteDC(data->backBufferDC); free(data); SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) 0); + windowdata[NHW_MAP].address = 0; break; case WM_TIMER: @@ -763,6 +764,7 @@ onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) msg_data->buffer[index++] = '\r'; msg_data->buffer[index++] = '\n'; } + nhUse(mgch); } break; #ifdef ENHANCED_SYMBOLS @@ -834,6 +836,7 @@ onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam) ReleaseDC(hWnd, hDC); SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) data); + windowdata[NHW_MAP].address = (genericptr_t) data; clearAll(data); diff --git a/win/win32/mhmenu.c b/win/win32/mhmenu.c index 89d90e2ab..d39bdc1b4 100644 --- a/win/win32/mhmenu.c +++ b/win/win32/mhmenu.c @@ -95,6 +95,8 @@ static void SelectMenuItem(HWND hwndList, PNHMenuWindow data, int item, int count); static void reset_menu_count(HWND hwndList, PNHMenuWindow data); static BOOL onListChar(HWND hWnd, HWND hwndList, WORD ch); +static genericptr_t menu_data_to_free; +void free_menu_data(void); /*-----------------------------------------------------------------------------*/ HWND @@ -298,6 +300,7 @@ MenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) data->bmpDC = CreateCompatibleDC(hdc); data->is_active = FALSE; SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) data); + windowdata[NHW_MENU].address = (genericptr_t) data; } /* set font for the text control */ cached_font * font = mswin_get_font(NHW_MENU, ATR_NONE, hdc, FALSE); @@ -519,6 +522,7 @@ MenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } free(data); SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) 0); + windowdata[NHW_MENU].address = 0; } return TRUE; } @@ -625,6 +629,8 @@ onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) data->menui.menu.items, data->menui.menu.allocated * sizeof(NHMenuItem)); if (!data->menui.menu.items) free(was); + else + menu_data_to_free = (genericptr_t) data->menui.menu.items; } if (data->menui.menu.items) { @@ -717,6 +723,14 @@ onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) } } + +void +free_menu_data(void) +{ + if (menu_data_to_free != 0) + free(menu_data_to_free), menu_data_to_free = 0; +} + /*-----------------------------------------------------------------------------*/ void LayoutMenu(HWND hWnd) @@ -909,7 +923,7 @@ SetMenuListType(HWND hWnd, int how) for (i = 0; i < data->menui.menu.size; i++) { LVITEM lvitem; ZeroMemory(&lvitem, sizeof(lvitem)); - sprintf(buf, "%c - %s", max(data->menui.menu.items[i].accelerator, ' '), + Snprintf(buf, sizeof buf, "%c - %s", max(data->menui.menu.items[i].accelerator, ' '), data->menui.menu.items[i].str); lvitem.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT; @@ -926,6 +940,7 @@ SetMenuListType(HWND hWnd, int how) } if (data->is_active) SetFocus(control); + nhUse(nItem); } /*-----------------------------------------------------------------------------*/ HWND @@ -1022,7 +1037,7 @@ onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam) lpdis = (LPDRAWITEMSTRUCT) lParam; /* If there are no list box items, skip this message. */ - if (lpdis->itemID == -1) + if (lpdis->itemID == (UINT) -1) return FALSE; data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA); @@ -1186,10 +1201,12 @@ onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam) && data->menui.menu.items[lpdis->itemID].count != 0 && item->glyphinfo.glyph != NO_GLYPH) { if (data->menui.menu.items[lpdis->itemID].count == -1) { - _stprintf(wbuf, TEXT("Count: All")); + nh_stprintf(wbuf, sizeof wbuf, + TEXT("Count: All")); } else { - _stprintf(wbuf, TEXT("Count: %d"), - data->menui.menu.items[lpdis->itemID].count); + nh_stprintf(wbuf, sizeof wbuf, + TEXT("Count: %d"), + data->menui.menu.items[lpdis->itemID].count); } /* TODO: add blinking for blink text */ @@ -1532,6 +1549,8 @@ onListChar(HWND hWnd, HWND hwndList, WORD ch) int iter = topIndex; do { i = iter % data->menui.menu.size; + if (iflags.debug_fuzzer && iter > 1000000) + ch = data->menui.menu.items[i].accelerator; if (data->menui.menu.items[i].accelerator == ch) { if (data->how == PICK_ANY) { SelectMenuItem( diff --git a/win/win32/mhmsgwnd.c b/win/win32/mhmsgwnd.c index 2707facc3..e4d62b5ab 100644 --- a/win/win32/mhmsgwnd.c +++ b/win/win32/mhmsgwnd.c @@ -171,6 +171,7 @@ NHMessageWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) data = (PNHMessageWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA); free(data); SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) 0); + windowdata[NHW_MESSAGE].address = 0; } break; case WM_SIZE: { @@ -480,8 +481,8 @@ onMSNH_VScroll(HWND hWnd, WPARAM wParam, LPARAM lParam) // of the scroll box, and update the window. UpdateWindow // sends the WM_PAINT message. - if (yInc = max(MSG_VISIBLE_LINES - data->yPos, - min(yInc, data->yMax - data->yPos))) { + if ((yInc = max(MSG_VISIBLE_LINES - data->yPos, + min(yInc, data->yMax - data->yPos)))) { data->yPos += yInc; /* ScrollWindowEx(hWnd, 0, -data->yChar * yInc, (CONST RECT *) NULL, (CONST RECT *) NULL, @@ -729,6 +730,7 @@ onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam) ZeroMemory(data, sizeof(NHMessageWindow)); data->max_text = MAXWINDOWTEXT; SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) data); + windowdata[NHW_MESSAGE].address = (genericptr_t) data; // for cleanup at the end /* re-calculate window size (+ font size) */ mswin_message_window_size(hWnd, &dummy); diff --git a/win/win32/mhrip.c b/win/win32/mhrip.c index 3fb9ac9ba..28036307b 100644 --- a/win/win32/mhrip.c +++ b/win/win32/mhrip.c @@ -56,6 +56,7 @@ mswin_init_RIP_window(void) ZeroMemory(data, sizeof(NHRIPWindow)); SetWindowLongPtr(ret, GWLP_USERDATA, (LONG_PTR) data); + windowdata[NHW_RIP].address = (genericptr_t) data; return ret; } @@ -240,6 +241,7 @@ NHRIPWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) DeleteObject(data->rip_bmp); free(data); SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) 0); + windowdata[NHW_RIP].address = 0; } break; } @@ -295,6 +297,7 @@ onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) break; } + nhUse(InRipText); } void diff --git a/win/win32/mhsplash.c b/win/win32/mhsplash.c index 6d81bd17a..5609bab89 100644 --- a/win/win32/mhsplash.c +++ b/win/win32/mhsplash.c @@ -135,6 +135,8 @@ mswin_display_splash_window(BOOL show_ver) mswin_set_splash_data(hWnd, &splashData, monitorInfo.scale); SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) &splashData); + windowdata[NHW_SPLASH].address = (genericptr_t) &splashData; + windowdata[NHW_SPLASH].isstatic = 1; GetNHApp()->hPopupWnd = hWnd; @@ -257,6 +259,7 @@ NHSplashWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) DrawText(hdc, VersionString, strlen(VersionString), &rt, DT_LEFT | DT_NOPREFIX); EndPaint(hWnd, &ps); + nhUse(OldFont); } break; case WM_COMMAND: diff --git a/win/win32/mhstatus.c b/win/win32/mhstatus.c index d96992c93..eee9047f8 100644 --- a/win/win32/mhstatus.c +++ b/win/win32/mhstatus.c @@ -19,7 +19,6 @@ extern COLORREF nhcolor_to_RGB(int c); /* from mhmap */ - typedef struct back_buffer { HWND hWnd; HDC hdc; @@ -29,6 +28,11 @@ typedef struct back_buffer { int height; } back_buffer_t; +void back_buffer_free(back_buffer_t * back_buffer); +void back_buffer_allocate(back_buffer_t * back_buffer, int width, int height); +void back_buffer_size(back_buffer_t * back_buffer, int width, int height); +void back_buffer_init(back_buffer_t * back_buffer, HWND hWnd, int width, int height); + void back_buffer_free(back_buffer_t * back_buffer) { if (back_buffer->bm != NULL) { @@ -127,6 +131,7 @@ mswin_init_status_window(void) if (!data) panic("out of memory"); + windowdata[NHW_STATUS].address = (genericptr_t) data; ZeroMemory(data, sizeof(NHStatusWindow)); SetWindowLongPtr(ret, GWLP_USERDATA, (LONG_PTR) data); @@ -253,6 +258,7 @@ StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_DESTROY: free(data); SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) 0); + windowdata[NHW_STATUS].address = 0; break; case WM_SETFOCUS: @@ -272,7 +278,7 @@ StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } static LRESULT -onWMPaint(HWND hWnd, WPARAM wParam, LPARAM lParam) +onWMPaint(HWND hWnd, WPARAM wParam UNUSED, LPARAM lParam UNUSED) { SIZE sz; WCHAR wbuf[BUFSZ]; diff --git a/win/win32/mhstatus.h b/win/win32/mhstatus.h index a70a90f6e..ec3473334 100644 --- a/win/win32/mhstatus.h +++ b/win/win32/mhstatus.h @@ -17,7 +17,9 @@ static const int fieldorder2[] = { BL_LEVELDESC, BL_GOLD, BL_HP, BL_HPMAX BL_ENE, BL_ENEMAX, BL_AC, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_HUNGER, BL_CAP, BL_CONDITION, -1 }; +#ifdef MSWPROC_C static const int *fieldorders[] = { fieldorder1, fieldorder2, NULL }; +#endif static const int fieldcounts[NHSW_LINES] = { SIZE(fieldorder1) - 1, SIZE(fieldorder2) - 1}; #define MSWIN_MAX_LINE1_STRINGS (SIZE(fieldorder1) - 1) diff --git a/win/win32/mhtext.c b/win/win32/mhtext.c index c51b50eb1..8463a187e 100644 --- a/win/win32/mhtext.c +++ b/win/win32/mhtext.c @@ -83,8 +83,10 @@ NHTextWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) data = (PNHTextWindow)malloc(sizeof(NHTextWindow)); if (!data) panic("out of memory"); + ZeroMemory(data, sizeof(NHTextWindow)); SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)data); + windowdata[NHW_TEXT].address = (genericptr_t) data; // for cleanup at the end HWND control = GetDlgItem(hWnd, IDC_TEXT_CONTROL); HDC hdc = GetDC(control); @@ -173,6 +175,7 @@ NHTextWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) free(data->window_text); free(data); SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) 0); + windowdata[NHW_TEXT].address = 0; } break; diff --git a/win/win32/mswproc.c b/win/win32/mswproc.c index 40bfeb7ba..06464c6de 100644 --- a/win/win32/mswproc.c +++ b/win/win32/mswproc.c @@ -7,6 +7,7 @@ * code in the mswin port and the rest of the nethack game engine. */ +#define MSWPROC_C #include "hack.h" #include "color.h" #include "dlb.h" @@ -28,6 +29,7 @@ #include "mhmain.h" #include "mhfont.h" #include "resource.h" +#undef MSWPROC_C #define LLEN 128 @@ -117,8 +119,7 @@ struct window_procs mswin_procs = { #ifdef CHANGE_COLOR /* only a Mac option currently */ mswin_change_color, mswin_get_color_string, #endif - /* other defs that really should go away (they're tty specific) */ - mswin_start_screen, mswin_end_screen, mswin_outrip, + mswin_outrip, mswin_preference_update, mswin_getmsghistory, mswin_putmsghistory, mswin_status_init, mswin_status_finish, mswin_status_enablefield, mswin_status_update, @@ -371,14 +372,15 @@ prompt_for_player_selection(void) /* tty_putsym(BASE_WINDOW, (int)strlen(prompt)+1, echoline, * pick4u); */ /* tty_putstr(BASE_WINDOW, 0, ""); */ - } else + } else { /* Otherwise it's hard to tell where to echo, and things are * wrapping a bit messily anyway, so (try to) make sure the next * question shows up well and doesn't get wrapped at the * bottom of the window. */ - /* tty_clear_nhwindow(BASE_WINDOW) */; - + /* tty_clear_nhwindow(BASE_WINDOW) */ + ; + } if (pick4u != 'y' && pick4u != 'n') { give_up: /* Quit */ if (selected) @@ -450,7 +452,7 @@ prompt_for_player_selection(void) any.a_int = i + 1; /* must be non-zero */ add_menu(win, &nul_glyphinfo, &any, 'q', 0, ATR_NONE, clr, "Quit", MENU_ITEMFLAGS_NONE); - Sprintf(pbuf, "Pick a role for your %s", plbuf); + Snprintf(pbuf, sizeof pbuf, "Pick a role for your %s", plbuf); end_menu(win, pbuf); n = select_menu(win, PICK_ONE, &selected); destroy_nhwindow(win); @@ -524,7 +526,7 @@ prompt_for_player_selection(void) any.a_int = i + 1; /* must be non-zero */ add_menu(win, &nul_glyphinfo, &any, 'q', 0, ATR_NONE, clr, "Quit", MENU_ITEMFLAGS_NONE); - Sprintf(pbuf, "Pick the race of your %s", plbuf); + Snprintf(pbuf, sizeof pbuf, "Pick the race of your %s", plbuf); end_menu(win, pbuf); n = select_menu(win, PICK_ONE, &selected); destroy_nhwindow(win); @@ -599,7 +601,7 @@ prompt_for_player_selection(void) any.a_int = i + 1; /* must be non-zero */ add_menu(win, &nul_glyphinfo, &any, 'q', 0, ATR_NONE, clr, "Quit", MENU_ITEMFLAGS_NONE); - Sprintf(pbuf, "Pick the gender of your %s", plbuf); + Snprintf(pbuf, sizeof pbuf, "Pick the gender of your %s", plbuf); end_menu(win, pbuf); n = select_menu(win, PICK_ONE, &selected); destroy_nhwindow(win); @@ -673,7 +675,7 @@ prompt_for_player_selection(void) any.a_int = i + 1; /* must be non-zero */ add_menu(win, &nul_glyphinfo, &any, 'q', 0, ATR_NONE, clr, "Quit", MENU_ITEMFLAGS_NONE); - Sprintf(pbuf, "Pick the alignment of your %s", plbuf); + Snprintf(pbuf, sizeof pbuf, "Pick the alignment of your %s", plbuf); end_menu(win, pbuf); n = select_menu(win, PICK_ONE, &selected); destroy_nhwindow(win); @@ -1021,6 +1023,7 @@ mswin_putstr_ex(winid wid, int attr, const char *text, int app) if (GetNHApp()->windowlist[wid].win != NULL) { MSNHMsgPutstr data; + ZeroMemory(&data, sizeof(data)); data.attr = attr; data.text = text; @@ -1060,8 +1063,9 @@ mswin_display_file(const char *filename, boolean must_exist) if (!f) { if (must_exist) { TCHAR message[90]; - _stprintf(message, TEXT("Warning! Could not find file: %s\n"), - NH_A2W(filename, wbuf, sizeof(wbuf))); + nh_stprintf(message, sizeof message, + TEXT("Warning! Could not find file: %s\n"), + NH_A2W(filename, wbuf, sizeof(wbuf))); NHMessageBox(GetNHApp()->hMainWnd, message, MB_OK | MB_ICONEXCLAMATION); } @@ -1258,7 +1262,7 @@ mswin_update_inventory(int arg) win_request_info * mswin_ctrl_nhwindow( - winid window, + winid window UNUSED, int request, win_request_info *wri) { @@ -1363,10 +1367,12 @@ mswin_print_glyph(winid wid, coordxy x, coordxy y, * mswin_raw_print_accumulate() accumulate the given text into * raw_print_strbuf. */ +void mswin_raw_print_accumulate(const char * str, boolean bold); + void mswin_raw_print_accumulate(const char * str, boolean bold) { - bold; // ignored for now + nhUse(bold); // ignored for now if (raw_print_strbuf.str != NULL) strbuf_append(&raw_print_strbuf, "\n"); strbuf_append(&raw_print_strbuf, str); @@ -1702,7 +1708,7 @@ mswin_yn_function(const char *question, const char *choices, char def) res_ch[1] = '\x0'; mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, res_ch, 1); } - + nhUse(yn_esc_map); return ch; } @@ -1914,32 +1920,7 @@ char * mswin_get_color_string(void) { logDebug("mswin_get_color_string()\n"); - return (""); -} - -/* -start_screen() -- Only used on Unix tty ports, but must be declared for - completeness. Sets up the tty to work in full-screen - graphics mode. Look at win/tty/termcap.c for an - example. If your window-port does not need this function - just declare an empty function. -*/ -void -mswin_start_screen(void) -{ - /* Do Nothing */ - logDebug("mswin_start_screen()\n"); -} - -/* -end_screen() -- Only used on Unix tty ports, but must be declared for - completeness. The complement of start_screen(). -*/ -void -mswin_end_screen(void) -{ - /* Do Nothing */ - logDebug("mswin_end_screen()\n"); + return (char *) ""; } /* @@ -2110,30 +2091,32 @@ mswin_preference_update(const char *pref) } } +static PMSNHMsgGetText history_text = 0; +static char *next_message = 0; + char * mswin_getmsghistory(boolean init) { - static PMSNHMsgGetText text = 0; - static char *next_message = 0; - if (init) { - text = (PMSNHMsgGetText) malloc(sizeof(MSNHMsgGetText) + if (history_text) + free((genericptr_t) history_text), history_text = 0; + history_text = (PMSNHMsgGetText) malloc(sizeof(MSNHMsgGetText) + TEXT_BUFFER_SIZE); - if (text) { - text->max_size = + if (history_text) { + history_text->max_size = TEXT_BUFFER_SIZE - 1; /* make sure we always have 0 at the end of the buffer */ - ZeroMemory(text->buffer, TEXT_BUFFER_SIZE); + ZeroMemory(history_text->buffer, TEXT_BUFFER_SIZE); SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND, - (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) text); + (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) history_text); - next_message = text->buffer; + next_message = history_text->buffer; } } if (!(next_message && next_message[0])) { - free(text); + free(history_text), history_text = 0; next_message = 0; return (char *) 0; } else { @@ -2553,7 +2536,7 @@ mswin_write_reg(void) if (RegOpenKeyEx(HKEY_CURRENT_USER, keystring, 0, KEY_WRITE, &key) != ERROR_SUCCESS) { - RegCreateKeyEx(HKEY_CURRENT_USER, keystring, 0, "", + RegCreateKeyEx(HKEY_CURRENT_USER, keystring, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, &disposition); } @@ -2690,7 +2673,7 @@ static color_table_value color_table[] = { }; typedef struct ctbv { - char *colorstring; + const char *colorstring; int syscolorvalue; } color_table_brush_value; @@ -2725,7 +2708,7 @@ mswin_color_from_string(char *colorstring, HBRUSH *brushptr, color_table_value *ctv_ptr = color_table; color_table_brush_value *ctbv_ptr = color_table_brush; int red_value, blue_value, green_value; - static char *hexadecimals = "0123456789abcdef"; + static const char *hexadecimals = "0123456789abcdef"; if (colorstring == NULL) return; @@ -2876,36 +2859,36 @@ static mswin_status_string _condition_strings[CONDITION_COUNT]; static mswin_status_field _status_fields[MAXBLSTATS]; static mswin_condition_field _condition_fields[CONDITION_COUNT] = { - { BL_MASK_BAREH, "Bare" }, - { BL_MASK_BLIND, "Blind" }, - { BL_MASK_BUSY, "Busy" }, - { BL_MASK_CONF, "Conf" }, - { BL_MASK_DEAF, "Deaf" }, - { BL_MASK_ELF_IRON, "Iron" }, - { BL_MASK_FLY, "Fly" }, - { BL_MASK_FOODPOIS, "FoodPois" }, - { BL_MASK_GLOWHANDS, "Glow" }, - { BL_MASK_GRAB, "Grab" }, - { BL_MASK_HALLU, "Hallu" }, - { BL_MASK_HELD, "Held" }, - { BL_MASK_ICY, "Icy" }, - { BL_MASK_INLAVA, "Lava" }, - { BL_MASK_LEV, "Lev" }, - { BL_MASK_PARLYZ, "Parlyz" }, - { BL_MASK_RIDE, "Ride" }, - { BL_MASK_SLEEPING, "Zzz" }, - { BL_MASK_SLIME, "Slime" }, - { BL_MASK_SLIPPERY, "Slip" }, - { BL_MASK_STONE, "Stone" }, - { BL_MASK_STRNGL, "Strngl" }, - { BL_MASK_STUN, "Stun" }, - { BL_MASK_SUBMERGED, "Sub" }, - { BL_MASK_TERMILL, "TermIll" }, - { BL_MASK_TETHERED, "Teth" }, - { BL_MASK_TRAPPED, "Trap" }, - { BL_MASK_UNCONSC, "Out" }, - { BL_MASK_WOUNDEDL, "Legs" }, - { BL_MASK_HOLDING, "Uhold" }, + { BL_MASK_BAREH, "Bare", 0}, + { BL_MASK_BLIND, "Blind", 0 }, + { BL_MASK_BUSY, "Busy", 0 }, + { BL_MASK_CONF, "Conf", 0 }, + { BL_MASK_DEAF, "Deaf", 0 }, + { BL_MASK_ELF_IRON, "Iron", 0 }, + { BL_MASK_FLY, "Fly", 0 }, + { BL_MASK_FOODPOIS, "FoodPois", 0 }, + { BL_MASK_GLOWHANDS, "Glow", 0 }, + { BL_MASK_GRAB, "Grab", 0 }, + { BL_MASK_HALLU, "Hallu", 0 }, + { BL_MASK_HELD, "Held", 0 }, + { BL_MASK_ICY, "Icy", 0 }, + { BL_MASK_INLAVA, "Lava", 0 }, + { BL_MASK_LEV, "Lev", 0 }, + { BL_MASK_PARLYZ, "Parlyz", 0 }, + { BL_MASK_RIDE, "Ride", 0 }, + { BL_MASK_SLEEPING, "Zzz", 0 }, + { BL_MASK_SLIME, "Slime", 0 }, + { BL_MASK_SLIPPERY, "Slip", 0 }, + { BL_MASK_STONE, "Stone", 0 }, + { BL_MASK_STRNGL, "Strngl", 0 }, + { BL_MASK_STUN, "Stun", 0 }, + { BL_MASK_SUBMERGED, "Sub", 0 }, + { BL_MASK_TERMILL, "TermIll", 0 }, + { BL_MASK_TETHERED, "Teth", 0 }, + { BL_MASK_TRAPPED, "Trap", 0 }, + { BL_MASK_UNCONSC, "Out", 0 }, + { BL_MASK_WOUNDEDL, "Legs", 0 }, + { BL_MASK_HOLDING, "Uhold", 0 }, }; extern winid WIN_STATUS; diff --git a/win/win32/winMS.h b/win/win32/winMS.h index 9e92d92ec..1de3db042 100644 --- a/win/win32/winMS.h +++ b/win/win32/winMS.h @@ -43,8 +43,19 @@ #define MAXWINDOWS 15 #endif -#define NHW_RIP 32 -#define NHW_INVEN 33 +struct window_tracking_data { + genericptr_t address; + int isstatic; +}; + +/* these are only in MSWIN_GRAPHICS, not the core */ +enum mswin_window_types { + NHW_MAIN = (NHW_LAST_TYPE + 1), + NHW_INVEN, + NHW_RIP, + NHW_SPLASH +}; +extern struct window_tracking_data windowdata[MAXWINDOWS]; #ifndef TILE_X #define TILE_X 16 @@ -131,12 +142,9 @@ typedef struct mswin_nhwindow_app { LPTRANSPARENTBLT lpfnTransparentBlt; /* transparent blt function */ } NHWinApp, *PNHWinApp; -#define E extern - -E PNHWinApp GetNHApp(void); -E struct window_procs mswin_procs; - -#undef E +extern PNHWinApp GetNHApp(void); +extern struct window_procs mswin_procs; +extern void free_winmain_stuff(void); /* Some prototypes */ void mswin_init_nhwindows(int *argc, char **argv); @@ -168,7 +176,7 @@ void mswin_print_glyph(winid wid, coordxy x, coordxy y, const glyph_info *glyph, const glyph_info *bkglyph); void mswin_raw_print(const char *str); void mswin_raw_print_bold(const char *str); -void mswin_raw_print_flush(); +void mswin_raw_print_flush(void); int mswin_nhgetch(void); int mswin_nh_poskey(coordxy *x, coordxy *y, int *mod); void mswin_nhbell(void); @@ -180,8 +188,6 @@ void mswin_number_pad(int state); void mswin_delay_output(void); void mswin_change_color(int color, long rgb, int reverse); char *mswin_get_color_string(void); -void mswin_start_screen(void); -void mswin_end_screen(void); void mswin_outrip(winid wid, int how, time_t when); void mswin_preference_update(const char *pref); char *mswin_getmsghistory(boolean init); @@ -213,6 +219,8 @@ void mswin_get_window_placement(int type, LPRECT rt); void mswin_update_window_placement(int type, LPRECT rt); void mswin_apply_window_style(HWND hwnd); +//boolean mswin_player_selection_window(void); + int NHMessageBox(HWND hWnd, LPCTSTR text, UINT type); extern HBRUSH menu_bg_brush; @@ -238,12 +246,14 @@ extern COLORREF message_fg_color; /* unicode stuff */ #define NH_CODEPAGE (SYMHANDLING(H_IBM) ? GetOEMCP() : GetACP()) #ifdef _UNICODE +#define nh_stprintf swprintf #define NH_W2A(w, a, cb) \ (WideCharToMultiByte(NH_CODEPAGE, 0, (w), -1, (a), (cb), NULL, NULL), (a)) #define NH_A2W(a, w, cb) \ (MultiByteToWideChar(NH_CODEPAGE, 0, (a), -1, (w), (cb)), (w)) #else +#define nh_stprintf snprintf #define NH_W2A(w, a, cb) (strncpy((a), (w), (cb))) #define NH_A2W(a, w, cb) (strncpy((w), (a), (cb)))