diff --git a/.gitignore b/.gitignore index 8f50ccfb05..8a3e01ccf3 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,9 @@ src/xnethack Debug/ Release/ binary/ +package/* +vspackage/ +vsbinary/ build/ ipch/ lib/* @@ -94,3 +97,4 @@ bundle/* *.user util/*.lib util/*.exp +submodules/CHKSUMS.tmp diff --git a/Cross-compiling b/Cross-compiling index 364fbdf182..38998a937f 100644 --- a/Cross-compiling +++ b/Cross-compiling @@ -20,6 +20,7 @@ Part B Contents: B4. Case sample: msdos B5. Case sample: amiga (started but incomplete) B6. Case sample: Web Assembly, libnh + B7. Case sample: mips -------------------------------------------------------------------------------- Part A - Cross-compiling NetHack @@ -360,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/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/explode.c, src/extralev.c, src/files.c, + src/fountain.c, src/getpos.c, src/glyphs.c, + src/hack.c, src/hacklib.c, src/insight.c, + src/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 @@ -409,7 +413,7 @@ Using the cross-compiler, build the following targets: b) Lua (mandatory in 3.7) - lib/lua-5.4.4/src + lib/lua-5.4.6/src from sources: lua.c, lapi.c, lauxlib.c, lbaselib.c, lcode.c, lcorolib.c, lctype.c, ldblib.c, ldebug.c, @@ -468,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 @@ -479,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 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. @@ -554,7 +558,7 @@ Cross-compiler url: https://github.com/bebbo/amiga-gcc make update [Note that you may have to take ownership of the files in the bebbo - repo via chown before succesfully carrying out the next steps] + repo via chown before successfully carrying out the next steps] make clean make clean-prefix @@ -692,5 +696,38 @@ Cross-compiler url: https://emscripten.org/docs/getting_started/downloads.html and shouldn't interfere with the non-cross-compile builds using hints/linux.370 or hints/macOS.370. + + +--------------------------------+ + | B7. Case sample: mips | + +--------------------------------+ + +Cross-compiler used: gcc-mipsel-linux-gnu, g++-mipsel-linux-gnu +Cross-compiler url: + + Here's a brief guide to obtaining the cross-compiler sources on an + Ubuntu system and building NetHack with it. + + For Ubuntu, the build prerequisite packages for building the compiler can + be easily obtained: + sudo apt install gcc-mipsel-linux-gnu + sudo apt install g++-mipsel-linux-gnu + + On your linux host, prepare to cross-compile NetHack as follows: + cd sys/unix ; sh setup.sh hints/linux.370 ; cd ../.. + make fetch-lua + + Then, cross-compile to targets/mips as follows: + make CROSS_TO_MIPS=1 fetch-ncurses + make CROSS_TO_MIPS=1 package + + Do not add any additional windowport interfaces to your build + (such as WANT_WIN_TTY=1 WANT_WIN_CURSES=1 WANT_WIN_X11=1 or + WANT_WIN_QT=1) as those aren't applicable to the mips cross-compile. + + The cross-compiler hints additions are enclosed inside ifdef sections + and shouldn't interfere with the non-cross-compile builds using + hints/linux.370 or hints/macOS.370. + + --- diff --git a/DEVEL/Developer.txt b/DEVEL/Developer.txt index 99846d8c3d..37fd69c591 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.) @@ -65,11 +70,24 @@ A. If you have never set up git on this machine before: git config --global user.name "MY NAME" git config --global user.email USER@EXAMPLE.COM You probably want to set up a credential cache. - macOS (10 - 12): - git config --global credential.helper osxkeychain - Windows: - git config --global credential.helper store -XXX linux + macOS (10 or greater): + git config --global credential.helper osxkeychain + Linux: + (This will vary by distribution.) + cd /usr/share/doc/git/contrib/credentail/libsecret + sudo apt-get install libglib-2.0-dev libsecret-1-dev + sudo make + git config --global credential.helper `pwd`/git-credential-libsecret + OR + sudo yum install git-credential-libsecret + git config --global credential.helper /usr/libexec/git-core/git-credential-libsecret + Windows: (The following three assume that Git for Windows is already installed on + the underlying Windows system https://git-scm.com/download/win): + git config --global credential.helper store + MSYS2 UCRT64 bash shell: + git config --global credential.helper "/c/Program\ Files/Git/mingw64/bin/git-credential-manager.exe" + Windows Subsystem for Linux 2 (WSL2) bash shell: + git config --global credential.helper "/mnt/c/Program\ Files/Git/mingw64/bin/git-credential-manager.exe" B. Specify the prefix for variable substitution: (This assumes you are not a member of DevTeam or any variant's development team. If you are, this may be wrong. Look for more specific documentation. @@ -79,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: @@ -156,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 0000000000..3788cb6d58 --- /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/code_features.txt b/DEVEL/code_features.txt index 25859a5f13..6c7100096e 100644 --- a/DEVEL/code_features.txt +++ b/DEVEL/code_features.txt @@ -101,6 +101,23 @@ engine are incompatible. Additional regular expression implementations can be written. The full interface documentation is in sys/share/posixregex.c +=========================================================== +HEADER FILE NOTES + +hack.h defines values that are available to all NetHack source files, +contains enums for use in all NetHack source files, and contains a +number of struct definitions for use in all NetHack source files. +hack.h does not contain variable declarations or variable definitions. + +decl.h and decl.c are related: decl.h contains the extern declarations +for variables that are defined in decl.c. These variables are global +and available to all NetHack source files. +decl.c variable definitions are generally laid out in much the same +order as their corresponding declarations in decl.h. + +A new header file cstd.h was added to coincide with 3.7's switch to +C99. It contains calls to some C99 standard header files. + =================== NEXT FEATURE ========================== diff --git a/DEVEL/code_style.txt b/DEVEL/code_style.txt index e0aaaf20be..574af2cc87 100644 --- a/DEVEL/code_style.txt +++ b/DEVEL/code_style.txt @@ -1,4 +1,4 @@ -# NetHack 3.7 code_style.txt $NHDT-Date: 1596498264 2020/08/03 23:44:24 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.5 $ +# NetHack 3.7 code_style.txt $NHDT-Date: 1694890786 2023/09/16 18:59:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.8 $ # Copyright (c) 2015 by Derek S. Ray # NetHack may be freely redistributed. See license for details. @@ -45,7 +45,7 @@ possible: Single blank lines should be used wherever convenient to improve readability. Functions and Control Statements -------------------------------- +-------------------------------- For a function definition, the return type, declarator, and opening brace should each appear on a line of their own. Arguments are defined in the following @@ -170,6 +170,19 @@ Variable names to avoid processors with segmented architectures may treat those as keywords. It is safest to just avoid them. + NEARDATA Some data is marked with this define; the Amiga port uses + it to mark data items to be used with a short addressing mode. + You don't need to use this. + +static vs staticfn +------------------ + +The staticfn macro evaluates to either "static" or to nothing (see config.h). +If possible, functions in src/*.c that would otherwise be marked static should +be marked staticfn so platforms that do not name static functions in their +stack traces can be forced to do so; this means function names cannot be +reused. Never use staticfn with data. + Spaces in Expressions --------------------- @@ -183,6 +196,63 @@ immediately inside a pair of parentheses: /* body */ } +Casts and sizeof +---------------- + +Casts should separate the cast operator and its expression with a space: + '(char *) str' +'sizeof (type)' requires the parentheses. 'sizeof expression' does not; +using them is not wrong but omitting them avoids some visual clutter. +Using them without the separating space gives the false impression of a +function call or macro-with-argument(s) expansion; 'sizeof' is an operator +and the parentheses required for '(type)' are to treat it like a cast. + +Comments +-------- + +Some block comments are undecorated, just split into reasonable width lines: + /* this is + a comment */ +They usually don't include sentence punctuation. + +Others are more elaborate: + /* + * This is + * another comment. + */ +This style is usually used with sentence punctuation, particularly if they +contain more than one sentence. + +End-of-line comments which need to span lines + somecode(); /* this comment + * is ok */ +should start every continuation line with an asterisk, otherwise clang-format +would convert them into a block comment + othercode(); /* this comment + should be avoided */ +because it would be converted into + othercode(); + /* this comment + should be avoided */ +if another bulk reformatting ever gets performed. Similarly, multiple +comments intended to read as one + morecode(); /* start of comment */ + /* more of comment */ + /* end of comment */ +are deprecated because they will end up losing the indentation of the +followup lines if reformatted. + +Many files end with + /*filename*/ +usually preceded by a blank line. This was intended as a workaround for a +comment--somewhere, possibly in Amiga or Atari code--that stated that some +compiler or other didn't always process the last line of the file. If that +last line is a comment, nothing is lost. The real issue almost certainly +was source file(s) that didn't end in newline. These days we try to force +the final newline for every file, prior to release if not always maintained +day-to-day. The name at the end can still be worthwhile when editing or +browsing multiple files. + Vim Configuration ================= @@ -202,7 +272,7 @@ ensure that indentation is done correctly: Visual Studio Configuration =========================== -In Visual Studio under Tools->Options->Text Editor->C/C++, you can set the +In Visual Studio under Tools->Options->Text Editor->C/C++, you can set the following options to obtain desired behavior: [Tabs] @@ -213,8 +283,8 @@ Insert Spaces There are a number of other options under [Formatting] that should be checked (Indentation, New Lines, Spacing, and Wrapping), but there are so -many entries that reproducing them here is impractical. Fortunately, the -options are in plain English, so walking through them with a copy of +many entries that reproducing them here is impractical. Fortunately, the +options are in plain English, so walking through them with a copy of this Guide handy and making changes as required will suffice. Emacs Configuration diff --git a/DEVEL/hooksdir/NHadd b/DEVEL/hooksdir/NHadd index c4d6e547d4..f4386c81d2 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 c7c3456f57..065fcd60ab 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 99621fc57d..0c7aeb067c 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; - - 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, $_); + 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, $_); } - 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 8e0d9a9c2f..01848675b9 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. -# get relevent config info -#XXX +# XX for now, there isn't any - if we get called, we subst. No options for now. +# get relevant config info +#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 0000000000..90fb7d68fa --- /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 7e5fdbd2fb..7aa183b657 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, - - '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 - - '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 - - '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 - - '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 - - # ------------------------------------------------- - # 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, - - 'f!!'=>1, # !! ignored - 'a!!'=>0, 'd!!'=>0, 'c!!'=>0, - - 'f@@'=>1, # @@ internal ignored - 'a@@'=>0, 'd@@'=>0, 'c@@'=>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 + + '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 + + '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 + + '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 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 + # ------------------------------------------------- + # ?? untracked + 'a??'=>1, 'f??'=>1, + 'd??'=>0, 'c??'=>0, + + # !! ignored + 'f!!'=>1, + + '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); - - # 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]; - - my $ofile = $file . ".nht"; - open(TOUT, ">", $ofile) or die "Can't open $ofile"; - -# 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"; - } - - 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"; + 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; + + 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 $ + + # $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; + } + + 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 + + # 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) = @_; - - # 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; - } + 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; + } # add: -n -v # commit: --dry-run -v # nhsub: -n -v - while($in[0] =~ m/^-/){ - local($_) = $in[0]; - if($_ eq '--'){ - shift @in; - last; + 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(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(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; } -# 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}++; - } - - 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}); + + 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 +} - ($mode) = ($opt{cmd} =~ m/^(.)/); - $mode = 'f' if($opt{cmd} eq 'date' && ($opt{f}||$opt{F})); - $mode = 'f' if($opt{cmd} eq 'add' && $opt{f}); +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"; +} - 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; +# (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; + + { + local $::x = $_[0] ne $out; + if ($::x){ + $count += $::x; +# warn "COUNT++"; } - 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 + } +#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 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"; +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) = @_; -# print "HIT '$var' '$val'\n" if($debug2); - - 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"; + 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 ec6af70be2..37170a48e5 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 d7e6c57242..db8578f946 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 cb37b38006..753cb4a046 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 a634f51efd..ca9e45e5f8 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 655c1a0cd4..3dcc7c18c1 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 3b57a6f532..2ec0588a95 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 d1ab9b005e..22e45b6663 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 bb5dbfe842..b19fc2fd4b 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 c2639fa2ec..a578ce5292 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 7f447a0825..4c472ef636 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 b6813851a2..a1ff142929 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 86ddfe3dc3..e235a33ce0 100755 --- a/DEVEL/nhgitset.pl +++ b/DEVEL/nhgitset.pl @@ -1,34 +1,31 @@ #!/usr/bin/perl -# $NHDT-Date: 1524689669 2018/04/25 20:54:29 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.2 $ +# $NHDT-Date: 1693357449 2023/08/30 01:04:09 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.3 $ # 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 @@ BEGIN # # 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; +} + +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 $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(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,170 +166,210 @@ BEGIN #&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); - - 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); - - 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"); +# 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); + + 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"); + } } +&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; +# @files: [0] is the name under .git/hooks; others are places to +# check during configuration +sub add_help { + my($cmd, @files) = @_; + + &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"; + } + } +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($srcdir, $plname) = @_; + return 0 unless(length $gitadddir); - 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); + my $plpath = catfile($gitadddir, $srcdir, $plname); + return 0 unless(-x $plpath); - print STDERR "Running $plpath\n" if($opt_v); - # current directory is top of target repo + print STDERR "RunningOverride $plpath\n" if($opt_v); - unless($opt_n){ - system("$plpath $opt_v") and die "Callout $plpath failed: $?\n"; - } - return 1; + # 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_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 - } - print "\n\nUsing prefix '$lcl' - PLEASE MAKE SURE THIS IS CORRECT\n\n"; +sub check_gitvars { + &check_prefix("substprefix"); + &check_prefix("projectname"); } -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; +sub check_prefix { + 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 { - 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"; + &add_config('nethack.$which', $other); + print STDERR "Copying prefix '$other' to local repository.\n" if($opt_v); } + $lcl = $other; # for display below + } + print "Using $which '$lcl' - PLEASE MAKE SURE THIS IS CORRECT\n"; } + +sub do_dir_DOTGIT { + 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) = @_; - for my $path ( glob("$srcdir$DS*") ){ + 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"; + } - next if( $path =~ m!.*${DS}TARGET$! ); + for my $path ( glob("$srcdir$DS*") ){ + next if( $path =~ m!.*${DS}TARGET$! ); - my $file = $path; + my $file = $path; - $file =~ s!.*$DS!!; + $file =~ s!.*$DS!!; + $file = catfile($targetdir, $file); - $file = catfile($targetdir, $file); + next if($opt_n); - 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; + 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; } + } } -__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 - - +sub do_file_nhgitset { + my $infile = "DEVEL/nhgitset.pl"; + 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__ =head1 NAME nhgitset.pl - Setup program for NetHack git repositories @@ -293,17 +382,36 @@ =head1 SYNOPSIS =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: + +=over + +=item B<-f> + +Force. Do not use this unless the program requests it or the hooks are broken. + +=back -The follwing options are available: +=over -B<-f> Force. Do not use this unless the program requests it. +=item B<-n> -B<-n> Make no changes. +Dry-run - make no changes. -B<-v> Verbose output. +=back + +=over + +=item B<-v> + +Verbose output. + +=back =head1 CONFIG @@ -320,8 +428,35 @@ =head1 CONFIG 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 @@ -330,3 +465,5 @@ =head1 EXIT STATUS 1 Fail. 2 Intervention required. + +=for nhgitset nhgitset NetHack git helper installer diff --git a/Files b/Files index 69890b8faf..cfba8ab9b3 100644 --- a/Files +++ b/Files @@ -14,8 +14,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) @@ -25,11 +25,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) @@ -49,20 +49,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 -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) @@ -92,13 +93,14 @@ align.h artifact.h artilist.h attrib.h botl.h color.h config.h config1.h context.h coord.h cstd.h decl.h defsym.h dgn_file.h display.h dlb.h dungeon.h engrave.h extern.h flag.h -fnamesiz.h func_tab.h global.h hack.h 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 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 -skills.h sndprocs.h sp_lev.h spell.h sym.h +fnamesiz.h func_tab.h global.h hack.h hacklib.h +integer.h isaac64.h lint.h mail.h mextra.h +mfndpos.h micro.h mkroom.h monattk.h mondata.h +monflag.h monst.h monsters.h nhmd4.h nhregex.h +obj.h objclass.h objects.h optlist.h patchlevel.h +pcconf.h permonst.h prop.h quest.h rect.h +region.h rm.h 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 wincurs.h windconf.h winprocs.h wintype.h @@ -181,6 +183,10 @@ outdated/sys/unix/hints/include: (files that are no longer maintained for current game code) cross-amiga-post cross-amiga-pre +outdated/sys/vms: +(files that are no longer maintained for current game code) +lev_lex.h + outdated/sys/wince: (files for Windows CE and PocketPC - untested for 3.7) Install.ce bootstrp.mak celib.c cesetup.bat cesound.c @@ -233,6 +239,10 @@ gnopts.h gnplayer.c gnplayer.h gnsignal.c gnsignal.h gnstatus.c gnstatus.h gntext.c gntext.h gnworn.c gnworn.h gnyesno.c gnyesno.h mapbg.xpm +sound/fmod: +(file in top directory) +fmod.c + sound/macsound: (file in top directory) macsound.m @@ -284,29 +294,30 @@ windsound.c src: (files for all versions) allmain.c alloc.c apply.c artifact.c attrib.c ball.c -bones.c botl.c cmd.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 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 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 sfstruct.c shk.c shknam.c sit.c sounds.c -sp_lev.c spell.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 +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 submodules: (files in top directory) -lua pdcurses pdcursesmod +CHKSUMS lua pdcurses pdcursesmod sys/libnh: (files in top directory) @@ -374,11 +385,11 @@ pmatchregex.c sys/unix: (files for UNIX versions) -Install.unx Makefile.dat Makefile.doc Makefile.src -Makefile.top Makefile.utl NewInstall.unx README.xcode -XCode.xcconfig depend.awk gitinfo.sh mkmkfile.sh -nethack.sh setup.sh sysconf unixmain.c -unixres.c unixunix.c +Install.unx Makefile.check Makefile.dat Makefile.doc +Makefile.src Makefile.top Makefile.utl NewInstall.unx +README-hints README.xcode XCode.xcconfig depend.awk +gitinfo.sh mkmkfile.sh nethack.sh setup.sh +sysconf unixmain.c unixres.c unixunix.c sys/unix/NetHack.xcodeproj: (file for macOS Xcode) @@ -404,28 +415,33 @@ unix sys/unix/hints/include: (files for configuring UNIX NetHack versions) -compiler.370 cross-post.370 cross-pre.370 gbdates-post.370 -gbdates-pre.370 multisnd-post.370 multisnd1-pre.370 multisnd2-pre.370 -multiw-1.370 multiw-2.370 multiw-3.370 +compiler.370 cross-post.370 cross-pre1.370 cross-pre2.370 +gbdates-post.370 gbdates-pre.370 misc.370 multisnd-post.370 +multisnd1-pre.370 multisnd2-pre.370 multiw-1.370 multiw-2.370 sys/vms: (files for VMS version) -Install.vms Makefile.dat Makefile.doc Makefile.src Makefile.top -Makefile.utl install.com lev_lex.h nethack.com oldcrtl.c -spec_lev.com sysconf vmsbuild.com vmsfiles.c vmsmail.c -vmsmain.c vmsmisc.c vmstty.c vmsunix.c +Install.vms Install370.vms Makefile.dat Makefile.doc +Makefile.src Makefile.top Makefile.utl Makefile_dat.vms +Makefile_doc.vms Makefile_src.vms Makefile_top.vms Makefile_utl.vms +install.com nethack.com oldcrtl.c spec_lev.com +sysconf vmsbuild.com vmsfiles.c vmsmail.c +vmsmain.c vmsmisc.c vmssetup.com vmstty.c +vmsunix.c sys/windows: (files for Windows 7/8.x/10/11 version) -.nethackrc.template Install.windows Makefile.mingw32 -Makefile.mingw32.depend Makefile.nmake console.rc -consoletty.c nethack.def 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 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 2017 or 2019 or 2022 Community Edition builds) +(files for Visual Studio 2019 or 2022 Community Edition builds) NetHack.sln NetHackPackage.appxmanifest NetHackPackage.wapproj NetHackProperties.props Package.StoreAssociation.xml ScreenShot.PNG @@ -436,8 +452,12 @@ default_dll.props default_lib.props dirs.props dll.props files.props +sys/windows/vs/FetchPrereq: +(files for Visual Studio 2019 or 2022 Community Edition builds) +fetchprereq.nmake fetchprereq.vcxproj + sys/windows/vs/Images: -(files for Visual Studio 2017 or 2019 or 2022 Community Edition builds) +(files for Visual Studio 2019 or 2022 Community Edition builds) BadgeLogo.scale-100.png BadgeLogo.scale-125.png BadgeLogo.scale-150.png @@ -492,43 +512,59 @@ Wide310x150Logo.scale-200.png Wide310x150Logo.scale-400.png sys/windows/vs/NetHack: -(files for Visual Studio 2017 or 2019 or 2022 Community Edition builds) +(files for Visual Studio 2019 or 2022 Community Edition builds) NetHack.vcxproj afternethack.proj sys/windows/vs/NetHackW: -(file for Visual Studio 2017 or 2019 or 2022 Community Edition builds) +(file for Visual Studio 2019 or 2022 Community Edition builds) NetHackW.vcxproj sys/windows/vs/PDCurses: -(file for Visual Studio 2017 or 2019 or 2022 Community Edition builds) +(file for Visual Studio 2019 or 2022 Community Edition builds) PDCurses.vcxproj +sys/windows/vs/PDCursesGui: +(file for Visual Studio 2019 or 2022 Community Edition builds) +pdcursesgui.vcxproj + sys/windows/vs/dlb: -(files for Visual Studio 2017 or 2019 or 2022 Community Edition builds) +(files for Visual Studio 2019 or 2022 Community Edition builds) afterdlb.proj dlb.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 2017 or 2019 or 2022 Community Edition builds) +(files for Visual Studio 2019 or 2022 Community Edition builds) aftermakedefs.proj makedefs.vcxproj +sys/windows/vs/package: +(files for Visual Studio 2019 or 2022 Community Edition builds) +package.nmake package.vcxproj + sys/windows/vs/recover: -(files for Visual Studio 2017 or 2019 or 2022 Community Edition builds) +(files for Visual Studio 2019 or 2022 Community Edition builds) afterrecover.proj recover.vcxproj sys/windows/vs/tile2bmp: -(files for Visual Studio 2017 or 2019 or 2022 Community Edition builds) +(files for Visual Studio 2019 or 2022 Community Edition builds) aftertile2bmp.proj tile2bmp.vcxproj sys/windows/vs/tilemap: -(files for Visual Studio 2017 or 2019 or 2022 Community Edition builds) +(files for Visual Studio 2019 or 2022 Community Edition builds) aftertilemap.proj tilemap.vcxproj sys/windows/vs/tiles: -(file for Visual Studio 2017 or 2019 or 2022 Community Edition builds) +(file for Visual Studio 2019 or 2022 Community Edition builds) tiles.vcxproj sys/windows/vs/uudecode: -(files for Visual Studio 2017 or 2019 or 2022 Community Edition builds) +(files for Visual Studio 2019 or 2022 Community Edition builds) afteruudecode.proj uudecode.vcxproj test: @@ -635,7 +671,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/Porting b/Porting index 5be93f0e51..1abba0d931 100644 --- a/Porting +++ b/Porting @@ -201,7 +201,7 @@ need to be included in the packaging of the game. 4.3. Lua Compile and link into a library, or obtain a prebuilt Lua library for -your platform. Place the Lua source into lib/lua-5.4.4 (or other folder +your platform. Place the Lua source into lib/lua-5.4.6 (or other folder representing an appropriate Lua version); place the compiled Lua library into lib. @@ -242,13 +242,13 @@ interpreter during game execution. There is no longer a build-time level compiler. Instead, the level descriptions have been converted to Lua and are inserted into the game package -for processing by the embeded Lua interpreter during game execution. +for processing by the embedded Lua interpreter during game execution. 5.3 Dungeon Compiler There is no longer a build-time dungeon compiler. Instead, the dungeon description has been converted to Lua and is inserted into the game package for -processing by the embeded Lua interpreter during game execution. +processing by the embedded Lua interpreter during game execution. 5.4 Run-time Options diff --git a/README b/README index 7863dd0b7a..c3d9759817 100644 --- a/README +++ b/README @@ -109,8 +109,9 @@ Please read items (1), (2) and (3) BEFORE doing anything with your new code. Intel Pentium or better running Linux, BSDI Intel Pentium or better running Windows 10 or 11 - Intel-based, or Apple M1 or M2 Macs running macOS 10.11 (El Capitan) to - 13 (Ventura) (follow the instructions in sys/unix/NewInstall.unx) + Intel-based, or Apple M1, M2, M3 Macs running + macOS 10.11 (El Capitan) to macOS 14 (Sonoma) + (follow the instructions in sys/unix/NewInstall.unx) Intel 80386 or greater running MS-DOS with DPMI built via djgpp compiler (native or Linux-hosted cross-compiler) OpenVMS (aka VMS) V8.4 on Alpha and on Integrity/Itanium/IA64 diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b630ec8933..cdec5d7d54 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -4,28 +4,24 @@ strategy: imageName: 'ubuntu-latest' toolchainName: gcc9 buildTargetName: minimal - linux_focal_clang_all: - imageName: 'ubuntu-20.04' + linux_noble_clang_all: + imageName: 'ubuntu-24.04' toolchainName: clang buildTargetName: all - linux_focal_gcc9_all: - imageName: 'ubuntu-20.04' + linux_jammy_gcc9_all: + imageName: 'ubuntu-22.04' toolchainName: gcc9 buildTargetName: all - linux_focal_gcc10_all: - imageName: 'ubuntu-20.04' - toolchainName: gcc10 - buildTargetName: all - linux_jammy_gcc11_all: - imageName: 'ubuntu-22.04' - toolchainName: gcc11 + linux_noble_gcc13_all: + imageName: 'ubuntu-24.04' + toolchainName: gcc13 buildTargetName: all macOS_latest_clang_all: imageName: 'macOS-latest' toolchainName: clang buildTargetName: all - macOS_monterey_clang13_all: - imageName: 'macOS-12' + macOS_Sequoia_clang15_all: + imageName: 'macOS-15' toolchainName: clang buildTargetName: all windows-visualstudio: @@ -36,12 +32,12 @@ strategy: imageName: 'windows-2019' 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 @@ -64,25 +60,15 @@ 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" - fi - if [ "$(toolchain)" == "gcc11" ] - then - echo "##vso[task.setvariable variable=CC]gcc-11" - echo "##vso[task.setvariable variable=CXX]g++-11" + echo "##vso[task.setvariable variable=CC]gcc-13" + echo "##vso[task.setvariable variable=CXX]g++-13" fi if [ "$(toolchain)" == "clang" ] then @@ -91,8 +77,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' @@ -137,17 +123,19 @@ steps: export LUATOP=../submodules/lua export LUASRC=../submodules/lua export ADD_CURSES=Y - export PDCURSES_TOP=../submodules/pdcurses - export LUA_VERSION=5.4.4 + export PDCURSES_TOP=../submodules/pdcursesmod + 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 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/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 CURLDST=mingw-x86.zip export MINGWBIN=mingw32 export MSYSTEM=MINGW32 @@ -163,19 +151,19 @@ 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' - bash: | sudo apt-get -qq -y update - sudo apt-get -qq -y install libncurses5-dev + sudo apt-get -qq -y install libncurses-dev sudo apt-get -qq -y install libx11-dev libxaw7-dev xfonts-utils qtbase5-dev qtmultimedia5-dev qtbase5-dev-tools - condition: eq( variables['Agent.OS'], 'Linux' ) + condition: and(eq( variables['Agent.OS'], 'Linux' ), eq( variables.buildTarget, 'all')) workingDirectory: $(Agent.BuildDirectory)/$(netHackPath) displayName: 'Getting linux build dependencies' @@ -193,28 +181,29 @@ steps: cd sys/unix sh setup.sh hints/linux-minimal cd ../.. - sed -i '/^#define CLIPPING/d' include/config.h - sed -i '/^#define COMPRESS/d' include/config.h - #sed -i '/^#define DOAGAIN/d' include/config.h - sed -i '/^#define DUMPLOG/d' include/config.h - #sed -i '/^#define GDBPATH/d' include/config.h - #sed -i '/^#define GREPPATH/d' include/config.h - sed -i '/^#define INSURANCE/d' include/config.h - sed -i '/^#define ENHANCED_SYMBOLS/d' include/config.h - sed -i '/^#define LOGFILE/d' include/config.h - sed -i '/^#define NEWS/d' include/config.h - sed -i '/^#define PANICLOG/d' include/config.h - #sed -i '/^#define STATUS_HILITES/d' include/config.h - sed -i '/^#define SYSCF/d' include/config.h - sed -i '/^#define USER_SOUNDS/d' include/config.h - sed -i '/^#define XLOGFILE/d' include/config.h - sed -i '/^#define HANGUPHANDLING/d' include/global.h - sed -i '/^#define MAIL/d' include/unixconf.h - sed -i '/^#define SHELL/d' include/unixconf.h - sed -i '/^#define SUSPEND/d' include/unixconf.h - sed -i 's/^#define TEXTCOLOR//' include/unixconf.h + sed -i '/^#[ ]*define CLIPPING/d' include/config.h + sed -i '/^#[ ]*define COMPRESS/d' include/config.h + sed -i '/^#[ ]*define CRASHREPORT/d' include/config.h + #sed -i '/^#[ ]*define DOAGAIN/d' include/config.h + sed -i '/^#[ ]*define DUMPLOG/d' include/config.h + sed -i '/^#[ ]*define DUMPLOG_CORE/d' include/config.h + #sed -i '/^#[ ]*define GDBPATH/d' include/config.h + #sed -i '/^#[ ]*define GREPPATH/d' include/config.h + sed -i '/^#[ ]*define INSURANCE/d' include/config.h + sed -i '/^#[ ]*define ENHANCED_SYMBOLS/d' include/config.h + sed -i '/^#[ ]*define LOGFILE/d' include/config.h + sed -i '/^#[ ]*define NEWS/d' include/config.h + sed -i '/^#[ ]*define PANICLOG/d' include/config.h + #sed -i '/^#[ ]*define STATUS_HILITES/d' include/config.h + sed -i '/^#[ ]*define SYSCF/d' include/config.h + sed -i '/^#[ ]*define USER_SOUNDS/d' include/config.h + sed -i '/^#[ ]*define XLOGFILE/d' include/config.h + sed -i '/^#[ ]*define HANGUPHANDLING/d' include/global.h + sed -i '/^#[ ]*define MAIL/d' include/unixconf.h + sed -i '/^#[ ]*define SHELL/d' include/unixconf.h + sed -i '/^#[ ]*define SUSPEND/d' include/unixconf.h make fetch-lua - make WANT_WIN_ALL=1 all + make WANT_WIN_ALL=1 NOCRASHREPORT=1 all condition: and(eq( variables['Agent.OS'], 'Linux' ), eq( variables.buildTarget, 'minimal')) displayName: 'Building linux minimal build' workingDirectory: $(Agent.BuildDirectory)/$(netHackPath) @@ -230,15 +219,16 @@ steps: displayName: 'Building mac full build' - bash: | - export GCCVER=gcc1210 + 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.4 WANT_WIN_TTY=1 WANT_WIN_CURSES=1 CROSS_TO_MSDOS=1 package + make LUA_VERSION=5.4.6 WANT_WIN_TTY=1 WANT_WIN_CURSES=1 CROSS_TO_MSDOS=1 package fi condition: and(eq( variables['Agent.OS'], 'Linux' ), eq( variables.toolchain, 'cross')) workingDirectory: $(Agent.BuildDirectory)/$(netHackPath) diff --git a/dat/Mon-strt.lua b/dat/Mon-strt.lua index 689539bcb5..7171925595 100644 --- a/dat/Mon-strt.lua +++ b/dat/Mon-strt.lua @@ -87,14 +87,13 @@ des.object({ id = "chest", trapped = 0, coord = {51,08}, }) des.object({ id = "chest", trapped = 0, coord = {51,11}, contents = function() - for i=1,5 do - des.object("%") - end + -- ensure enough vegetarian food generates for vegetarian games + des.object({ id="food ration", coord = {46, 4}, quantity = 4}) end }) -- next to leader, so possibly tricky to pick up if not ready for quest yet; -- there's no protection against a xorn eating these tins; BUC state is random -des.object({ id="tin", coord = {29, 10}, quantity=2, montype="spinach" }) +des.object({ id="tin", coord = {50, 09}, quantity=2, montype="spinach" }) -- Non diggable walls - try to hit as few trees as possible des.non_diggable(selection.area(16,00,18,19)) diff --git a/dat/Pri-goal.lua b/dat/Pri-goal.lua index 9a40778394..9aac8ae7e5 100644 --- a/dat/Pri-goal.lua +++ b/dat/Pri-goal.lua @@ -1,4 +1,4 @@ --- NetHack Priest Pri-goal.lua $NHDT-Date: 1652196008 2022/05/10 15:20:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1 $ +-- NetHack Priest Pri-goal.lua $NHDT-Date: 1687033651 2023/06/17 20:27:31 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.2 $ -- Copyright (c) 1989 by Jean-Christophe Collet -- Copyright (c) 1991-2 by M. Stephenson -- NetHack may be freely redistributed. See license for details. diff --git a/dat/Ran-strt.lua b/dat/Ran-strt.lua index 7a869d63e6..1707095c75 100644 --- a/dat/Ran-strt.lua +++ b/dat/Ran-strt.lua @@ -75,7 +75,7 @@ des.replace_terrain({ selection=grass, fromterrain='.', toterrain='g' }) des.monster({ id = "Orion", coord = {20, 10}, inventory = function() des.object({ id = "light armor", spe = 4 }); des.object({ id = "yumi", spe = 4 }); - des.object({ id = "arrow", spe = 4, quantity = 50 }); + des.object({ id = "ya", spe = 4, quantity = 50 }); end }) des.monster({ id = "large dog", x=20, y=11, name="Sirius", peaceful=1 }) -- The treasure of Orion diff --git a/dat/Rog-strt.lua b/dat/Rog-strt.lua index 3262371c70..3af852640f 100644 --- a/dat/Rog-strt.lua +++ b/dat/Rog-strt.lua @@ -45,7 +45,7 @@ des.map([[ local streets = selection.floodfill(0,12) -- The down stairs is at one of the 4 "exits". The others are mimics, --- mimicing stairwells. +-- mimicking stairwells. local place = { {33,0}, {0,12}, {25,20}, {75,05} } shuffle(place) diff --git a/dat/Sam-strt.lua b/dat/Sam-strt.lua index cfc04083b7..fb1b6772ae 100644 --- a/dat/Sam-strt.lua +++ b/dat/Sam-strt.lua @@ -1,4 +1,4 @@ --- NetHack Samurai Sam-strt.lua $NHDT-Date: 1652196014 2022/05/10 15:20:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.3 $ +-- NetHack Samurai Sam-strt.lua $NHDT-Date: 1695932714 2023/09/28 20:25:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.4 $ -- Copyright (c) 1989 by Jean-Christophe Collet -- Copyright (c) 1991-92 by M. Stephenson, P. Winner -- NetHack may be freely redistributed. See license for details. @@ -9,6 +9,10 @@ -- Here you meet your (besieged) class leader, Lord Sato -- and receive your quest assignment. -- +-- The throne room designation produces random atmospheric +-- messages (until the room is entered) but this one doesn't +-- actually contain any throne. +-- des.level_init({ style = "solidfill", fg = " " }); des.level_flags("mazelevel", "noteleport", "hardfloor", "outdoors") @@ -55,8 +59,8 @@ des.door("closed",50,04) des.door("closed",50,06) -- Lord Sato des.monster({ id = "Lord Sato", coord = {20, 04}, inventory = function() - des.object({ id = "splint mail", spe = 5 }); - des.object({ id = "katana", spe = 4 }); + des.object({ id = "splint mail", spe = 5, eroded=-1, buc="not-cursed" }); + des.object({ id = "katana", spe = 4, eroded=-1, buc="not-cursed" }); end }) -- The treasure of Lord Sato des.object("chest", 20, 04) diff --git a/dat/bigrm-12.lua b/dat/bigrm-12.lua index 5747229ecd..0e41bd9e4c 100644 --- a/dat/bigrm-12.lua +++ b/dat/bigrm-12.lua @@ -1,87 +1,85 @@ --- "Tea Party" by jonadab +-- 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.level_flags("mazelevel", "noflip"); + des.map([[ ------------------------------------- ------------------------------------ -|..................................| |..................................| -|--..............................-------..............................--| -|.........--..--....................|....................--..--.........| -|-....---..|S-|..---..............-----..............---..|-S|..---....-| -|.......|--|..|--|........--..--.........--..--........|--|..|--|.......| -----..---..|--|..---...--..|--|..--...--..|--|..--...---..|--|..---..---- - |........--..--........|--| |--|.....|--| |--|........--..--........| - --....................--..|--|..--...--..|--|..--....................-- - |.......................--..--.........--..--.......................| - |...................................................................| - |.......................--..--.........--..--.......................| - --....................--..|--|..--...--..|--|..--....................-- - |........--..--........|--| |--|.....|--| |--|........--..--........| -----..---..|--|..---...--..|--|..--...--..|--|..--...---..|--|..---..---- -|.......|--|..|--|........--..--.........--..--........|--|..|--|.......| -|-....---..|S-|..---..............-----..............---..|-S|..---....-| -|.........--..--....................|....................--..--.........| -|--..............................-------..............................--| -|..................................| |..................................| ------------------------------------- ------------------------------------ + + ....................... ....................... + ......................... ......................... + ........................... ........................... + ............................. ............................. + ........PPPPPPPPPPPPPPP........ ........LLLLLLLLLLLLLLL........ + ........PPPPPPPPPPPPPPPPP........ ........LLLLLLLLLLLLLLLLL........ + ........PPPWWWWWWWWWWWWWPPP...............LLLZZZZZZZZZZZZZLLL........ + ........PPPWWWWWWWWWWWWWWWPPP.............LLLZZZZZZZZZZZZZZZLLL........ + ........PPPWWWWWWWWWWWWWWWWWPPP...........LLLZZZZZZZZZZZZZZZZZLLL........ + ........PPPWWWWWWWWWWWWWWWPPP.............LLLZZZZZZZZZZZZZZZLLL........ + ........PPPWWWWWWWWWWWWWPPP...............LLLZZZZZZZZZZZZZLLL........ + ........PPPPPPPPPPPPPPPPP........ ........LLLLLLLLLLLLLLLLL........ + ........PPPPPPPPPPPPPPP........ ........LLLLLLLLLLLLLLL........ + ............................. ............................. + ........................... ........................... + ......................... ......................... + ....................... ....................... + ]]); --- Dungeon Description -des.region(selection.area(01,01,72,20),"lit"); --- Stairs + +-- 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"); --- Non diggable walls -des.non_diggable(); ----------------------------------------------------------------------------------- -des.door({ state="locked", x=12, y=04 }) -des.door({ state="locked", x=12, y=16 }) -des.door({ state="locked", x=60, y=04 }) -des.door({ state="locked", x=60, y=16 }) --- Objects for i = 1,15 do des.object(); end --- Random traps for i = 1,6 do des.trap(); end --- Random monsters for i = 1,28 do - des.monster(); -end - --- Guests. --- Most of these names came directly from an 8 year old girl. And in one case, --- her father, who obsesses over woodchucks named Carl. -rndslimy = function() - classes = { "b", "j", "P" }; - return classes[d(#classes)]; -end -guests = { - { name = "Blinky Pinky", montype = "pony" }, - { name = "Mister Frosty", montype = nh.rn2(2) and "frost giant" or "ice troll" }, - { name = "Miss Betsy" }, - { name = "Fred" }, - { name = "Bob" }, - { name = "Joe" }, - { name = "Gooey", montype=rndslimy() }, - { name = "Goopy", montype=rndslimy() }, - { name = "Goopifer", montype=rndslimy() }, - { name = "Susie" }, - { name = "Tootles" }, - { name = "Cindy Lou" }, - { name = "Mr Spotty Pants" }, - { name = "Banana Joe", montype="Y" }, - { name = "Carl", montype="woodchuck" } -} -places = { {03,10},{69,10},{25,01},{47,01},{25,19},{47,19} } -shuffle(guests) -shuffle(places) -for i = 1, d(3) + 1 do - statue = { id="statue", name=guests[i].name, x=places[i][1], y=places[i][2] }; - if guests[i].montype ~= nil then - statue.montype = guests[i].montype; - end - des.object(statue) + des.monster(); end diff --git a/dat/bigrm-14.lua b/dat/bigrm-14.lua new file mode 100644 index 0000000000..5747229ecd --- /dev/null +++ b/dat/bigrm-14.lua @@ -0,0 +1,87 @@ +-- "Tea Party" by jonadab + +des.level_init({ style = "solidfill", fg = " " }); +des.level_flags("mazelevel", "noflip"); +des.map([[ +------------------------------------ ------------------------------------ +|..................................| |..................................| +|--..............................-------..............................--| +|.........--..--....................|....................--..--.........| +|-....---..|S-|..---..............-----..............---..|-S|..---....-| +|.......|--|..|--|........--..--.........--..--........|--|..|--|.......| +----..---..|--|..---...--..|--|..--...--..|--|..--...---..|--|..---..---- + |........--..--........|--| |--|.....|--| |--|........--..--........| + --....................--..|--|..--...--..|--|..--....................-- + |.......................--..--.........--..--.......................| + |...................................................................| + |.......................--..--.........--..--.......................| + --....................--..|--|..--...--..|--|..--....................-- + |........--..--........|--| |--|.....|--| |--|........--..--........| +----..---..|--|..---...--..|--|..--...--..|--|..--...---..|--|..---..---- +|.......|--|..|--|........--..--.........--..--........|--|..|--|.......| +|-....---..|S-|..---..............-----..............---..|-S|..---....-| +|.........--..--....................|....................--..--.........| +|--..............................-------..............................--| +|..................................| |..................................| +------------------------------------ ------------------------------------ +]]); +-- Dungeon Description +des.region(selection.area(01,01,72,20),"lit"); +-- Stairs +des.stair("up"); +des.stair("down"); +-- Non diggable walls +des.non_diggable(); +---------------------------------------------------------------------------------- +des.door({ state="locked", x=12, y=04 }) +des.door({ state="locked", x=12, y=16 }) +des.door({ state="locked", x=60, y=04 }) +des.door({ state="locked", x=60, y=16 }) + +-- Objects +for i = 1,15 do + des.object(); +end +-- Random traps +for i = 1,6 do + des.trap(); +end +-- Random monsters +for i = 1,28 do + des.monster(); +end + +-- Guests. +-- Most of these names came directly from an 8 year old girl. And in one case, +-- her father, who obsesses over woodchucks named Carl. +rndslimy = function() + classes = { "b", "j", "P" }; + return classes[d(#classes)]; +end +guests = { + { name = "Blinky Pinky", montype = "pony" }, + { name = "Mister Frosty", montype = nh.rn2(2) and "frost giant" or "ice troll" }, + { name = "Miss Betsy" }, + { name = "Fred" }, + { name = "Bob" }, + { name = "Joe" }, + { name = "Gooey", montype=rndslimy() }, + { name = "Goopy", montype=rndslimy() }, + { name = "Goopifer", montype=rndslimy() }, + { name = "Susie" }, + { name = "Tootles" }, + { name = "Cindy Lou" }, + { name = "Mr Spotty Pants" }, + { name = "Banana Joe", montype="Y" }, + { name = "Carl", montype="woodchuck" } +} +places = { {03,10},{69,10},{25,01},{47,01},{25,19},{47,19} } +shuffle(guests) +shuffle(places) +for i = 1, d(3) + 1 do + statue = { id="statue", name=guests[i].name, x=places[i][1], y=places[i][2] }; + if guests[i].montype ~= nil then + statue.montype = guests[i].montype; + end + des.object(statue) +end diff --git a/dat/bigrm-3.lua b/dat/bigrm-3.lua index e7c99a28a0..ee7e0f3100 100644 --- a/dat/bigrm-3.lua +++ b/dat/bigrm-3.lua @@ -30,6 +30,14 @@ des.map([[ -- Dungeon Description des.region(selection.area(01,01,73,16), "lit"); +-- replace some walls +if percent(66) then + local sel = selection.match("[.w.]"); + local terrains = { "F", "T", "W", "Z" }; + local choice = terrains[math.random(1, #terrains)]; + des.terrain(sel, choice); +end + -- Stairs des.stair("up"); des.stair("down"); diff --git a/dat/bigrm-4.lua b/dat/bigrm-4.lua index 777eeb30f9..a6b9340d1b 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 f67cc1638a..c6063a8186 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 6dc828c839..07038d8d62 100644 --- a/dat/bigrm-7.lua +++ b/dat/bigrm-7.lua @@ -14,11 +14,11 @@ des.map([[ ---------.................................--- ---------...........................................--- ---------.....................................................--- -|--------...............................................................--| +---------...............................................................--- |.........................................................................| |.L.....................................................................L.| |.........................................................................| -|--...............................................................--------| +---...............................................................--------- ---.....................................................--------- ---...........................................--------- ---.................................--------- diff --git a/dat/data.base b/dat/data.base index 1fed81c78f..f3848cc7d4 100644 --- a/dat/data.base +++ b/dat/data.base @@ -21,7 +21,7 @@ # prevents "orc mummy" and "orc zombie" from matching. # # A citation of "[]" indicates arbitrary text written by developers or -# contributers. Falling off the end of an entry without any citation +# contributors. Falling off the end of an entry without any citation # implies "[]". # # Some entries include blank lines to separate paragraphs but many don't. @@ -337,34 +337,36 @@ asmodeus or unhappiness. [ Brewer's Concise Dictionary of Phrase and Fable ] athame - The consecrated ritual knife of a Wiccan initiate (one of - four basic tools, together with the wand, chalice and - pentacle). Traditionally, the athame is a double-edged, - black-handled, cross-hilted dagger of between six and - eighteen inches length. + The consecrated ritual knife of a Wiccan initiate (one of four + basic tools, together with the wand, chalice and pentacle). + Traditionally, the athame is a double-edged, black-handled, + cross-hilted dagger of between six and eighteen inches length. + [] A belt made from a twisted braid of silken threads of gold wound around her waist, and a dark-handled knife rested on a slant at her hip through a loop in the belt. [...] - She arched her brows. "You should know better, dear godchild. You - know I cannot speak what is untrue. During our last encounter I - returned to Faerie with great power and upset vital balances. Those - balances had to be redressed, and your debt was the mechanism that - the Queen chose to employ." - I frowned at her for a minute. "Returned with great power." My eyes - fell to the knife at her waist. "That thing the vampires gave you?" - She rested her fingers lightly on the knife's hilt. "Don't cheapen - it. This athame was no creation of theirs. And it was less a gift - than a trade." - "Amoracchius and that thing are in the same league?" Gulp. My faerie - godmother was dangerous enough without a big-time artifact of magic. + She arched her brows. "You should know better, dear godchild. + You know I cannot speak what is untrue. During our last + encounter I returned to Faerie with great power and upset vital + balances. Those balances had to be redressed, and your debt was + the mechanism that the Queen chose to employ." + I frowned at her for a minute. "Returned with great power." My + eyes fell to the knife at her waist. "That thing the vampires + gave you?" + She rested her fingers lightly on the knife's hilt. "Don't + cheapen it. This athame was no creation of theirs. And it was + less a gift than a trade." + "Amoracchius and that thing are in the same league?" Gulp. My + faerie godmother was dangerous enough without a big-time + artifact of magic. [ Summer Knight, by Jim Butcher ] athen* Athene was the offspring of Zeus, and without a mother. She sprang forth from his head completely armed. Her favourite bird was the owl, and the plant sacred to her is the olive. - [ Bulfinch's Mythology, by Thomas Bulfinch ] + [ Bulfinch's Mythology, by Thomas Bulfinch ] atheist It was all very well going on about pure logic and how the universe was ruled by logic and the harmony of numbers, but the @@ -432,7 +434,7 @@ balrog was a blade like a stabbing tongue of fire; in its left it held a whip of many thongs. 'Ai, ai!' wailed Legolas. 'A Balrog! A Balrog is come!' - [ The Fellowship of the Ring, by J.R.R. Tolkien ] + [ The Fellowship of the Ring, by J.R.R. Tolkien ] baluchitherium titanothere Extinct rhinos include a variety of forms, the most @@ -542,7 +544,6 @@ bell of opening Or wonder, till it drives you mad, What would have followed if you had. [ The Magician's Nephew, by C.S. Lewis ] -~*engraved*bell* *bell* Hear the sledges with the bells -- Silver bells! @@ -697,6 +698,7 @@ boomerang [ The Last Continent, by Terry Pratchett ] ~*jack*boot* *boot* +*shoes In Fantasyland these are remarkable in that they seldom or never wear out and are suitable for riding or walking in without the need of Socks. Boots never pinch, rub, or get @@ -907,6 +909,7 @@ kitten predatory felines (_Felis ochreata domestica_), with a thick, soft pelt; often kept as a pet. Various folklores have the cat associated with magic and the gods of ancient Egypt. + [] So Ulthar went to sleep in vain anger; and when the people awakened at dawn - behold! Every cat was back at his @@ -965,7 +968,11 @@ gnom* cave*man These strange creatures were said to live in the caves and clefts of the mountains, myths associating them especially with the hills of Thessaly and the range of Erymanthos. - [ Mythology of all races, Vol. 1, pp. 270-271 ] + [ Mythology of all races, Vol. 1, pp. 270-271 ] +# [ capitalization of the title seems to be all over the place, +# including all lower case and conventional mixed case; +# it appears that Volume 1 (of 13) was written by +# William Sherwood Fox and edited by Louis Herbert Gray ] centipede I observed here, what I had often seen before, that certain districts abound in centipedes. Here they have light @@ -1157,7 +1164,6 @@ zorkmid* Empire ]." On the other side, the coin depicts Egreth Castle, and says "In Frobs We Trust" in several languages. [ Zork Zero, by Infocom ] -# not "stethoscope" combat fight fracas @@ -1173,6 +1179,12 @@ tiff Blind Pew: Oh. Must have been more of a tiff then. [ Yellowbeard, directed by Mel Damski, screenplay by Graham Chapman, Peter Cook, Bernard McKenna ] +conical hat + You couldn't escape the pointy hat, though. There was nothing magical + about a pointy hat except that it said that the woman underneath it + was a witch. People paid attention to a pointy hat. + [ Wintersmith, by Terry Pratchett ] +# not "*cope" because that would match "stethoscope" cope * cope The cope is a liturgical vestment which may be worn by any @@ -1189,6 +1201,8 @@ brass slightly worse than iron and steel in sheer durability, it has the advantage that it won't rust, although it is still vulnerable to corrosion. +# the word "cornuthaum" was coined by devteam member jwalz and adopted in +# 3.2.0, after searching for an actual name for pointy hat came up empty cornuthaum He was dressed in a flowing gown with fur tippets which had the signs of the zodiac embroidered over it, with various @@ -1199,16 +1213,16 @@ cornuthaum like the headgear worn by ladies of that time, except that the ladies were accustomed to have a bit of veil floating from the top of it. - [ The Once and Future King, by T.H. White ] + [ The Once and Future King, by T.H. White ] - "A wizard!" Dooley exclaimed, astounded. - "At your service, sirs," said the wizard. "How + "A wizard!" Dooley exclaimed, astounded. + "At your service, sirs," said the wizard. "How perceptive of you to notice. I suppose my hat rather gives me away. Something of a beacon, I don't doubt." His hat was pretty much that, tall and cone-shaped with stars and crescent moons all over it. All in all, it couldn't have been more wizardish. - [ The Elfin Ship, James P. Blaylock ] + [ The Elfin Ship, James P. Blaylock ] couatl A mythical feathered serpent. The couatl are very rare. coyote @@ -1222,14 +1236,15 @@ cram* exercise. It was made by the Lake-men for long journeys. [ The Hobbit, by J.R.R. Tolkien ] cream pie - Gregor stared at the pastry tray, and sighed. "I suppose + Gregor stared at the pastry tray, and sighed. "I suppose it would disturb the guards if I tried to shove a cream torte up your nose." - "Deeply. You should have done it when we were eight and + "Deeply. You should have done it when we were eight and twelve, you could have gotten away with it then. The cream pie of justice flies one way," Miles snickered. [ The Vor Game, by Lois McMaster Bujold ] credit card +charge card "We are not worried about the expiration date," repeated the barman, satisfied that he now had Ford Prefect's full attention; "we are worried about the entire piece of plastic." @@ -1408,6 +1423,19 @@ major demon them difficult to deal with, even on the rare occasions when they are friendly. [ The Tough Guide to Fantasyland, by Diana Wynne Jones ] +# added by PR #1108 by NullCGT then replaced by PR #1109 by entrez +# (Demonbane has been changed from a sword to a mace so the new quote doesn't +# seem to be very appropriate) +# Do not be deceived, Wormwood. Our cause is never more in danger +# than when a human, no longer desiring, but still intending, to do +# our Enemy's will, looks round upon a universe from which every +# trace of Him seems to have vanished, and asks why he has been +# forsaken, and still obeys. +# [ The Screwtape Letters, by C.S. Lewis ] +# + Think not that I am come to send peace on earth: I came not to + send peace, but a sword. + [ The Gospel According to Matthew, 10:34 ] dented pot Here was a sight to beat all: a man in the prime of life rambling barefoot down the wilderness road, dressed in raggedy pantaloons @@ -1512,7 +1540,8 @@ pup* loyalty to man and gentleness with children, it is the world's most popular domestic animal. It can easily be trained to perform various tasks. -# typing "spellbook or a closed door" shouldn't yield this entry +# typing "spellbook or a closed door" (map description for "+" when using +# the default symbol set) shouldn't yield this entry ~trap*door ~*spellbook* *door @@ -1526,8 +1555,8 @@ doorway Before me things create were none, save things Eternal, and eternal I endure. All hope abandon ye who enter here. - [ The Inferno, from The Divine Comedy of Dante - Alighieri, translated by H.F. Cary ] + [ The Inferno, from The Divine Comedy of Dante Alighieri, + translated by H.F. Cary ] doppelganger "Then we can only give thanks that this is Antarctica, where there is not one, single, solitary, living thing for it to @@ -1586,6 +1615,14 @@ doppelganger make of steel. [ The Last Book of Swords: Shieldbreaker's Story, by Fred Saberhagen ] +dragonbane + And now the youth + was to enter the line of battle with his lord, + his first time to be tested as a fighter. + His spirit did not break and the ancestral blade + would keep its edge, as the dragon discovered + as soon as they came together in combat. + [ Beowulf, translated by Seamus Heaney ] *drum* Many travelers have seen the drums of the great apes, and some have heard the sounds of their beating and the noise of @@ -1818,8 +1855,23 @@ A.S* [ Wikipedia, the free encyclopedia ] erinys erinyes - These female-seeming devils named after the Furies of mythology - attack hand to hand and poison their unwary victims as well. +furies + Erinyes are the Furies of ancient myth. + (Erinys is the singular form.) + [] + + Holy and pure, from Zeus Khthonios born + and Persephone, whom lovely locks adorn: + Whose piercing sight, with vision unconfin'd, + surveys the deeds of all the impious kind: + On Fate attendant, punishing the race + (with wrath severe) of deeds unjust and base. + Dark-colour'd queens, whose glittering eyes are bright + with dreadful, radiant, life-destroying, light: + Eternal rulers, terrible and strong, + to whom revenge, and tortures dire belong. + [ LXIX. To the Furies, The Orphic Hymns, + translated by Thomas Taylor ] ettin The two-headed giant, or ettin, is a vicious and unpredictable hunter that stalks by night and eats any meat it can catch. @@ -2051,6 +2103,7 @@ frost giant a sugar donut. [ The Last Olympian, by Rick Riordan ] fruit +fruitname They say this is edible. Some adventurers have strange tastes. *fung* Fungi, division of simple plants that lack chlorophyll, true @@ -2171,8 +2224,8 @@ geryon With sting like scorpion's arm'd. Then thus my guide: "Now need our way must turn few steps apart, Far as to that ill beast, who couches there." - [ The Inferno, from The Divine Comedy of Dante - Alighieri, translated by H.F. Cary ] + [ The Inferno, from The Divine Comedy of Dante Alighieri, + translated by H.F. Cary ] *ghost valley of *dea* And now the souls of the dead who had gone below came swarming @@ -2219,6 +2272,11 @@ glass throwing glass objects any distance will definitely break them. There have been reports of magically enhanced glass that is protected from breaking. +*gloves +gauntlets* + And her old Uncle William used to say a lady is known by her + shoes and her gloves. + [ Mrs. Dalloway, by Virginia Woolf ] # note: "gnomish wizard" is a monster ~gnome ??m* #~gnom* cave*man @@ -2394,6 +2452,7 @@ hachi statue was erected on the station platform in his honor. It is said to bring you luck if you touch his statue. hallucinat* +potion of hallucination All at once, and without further warning, my reason forsook me altogether, and I started from Fr. Moffitt's house to go to my boarding place. The sidewalks were to me one mass of living, @@ -2498,6 +2557,33 @@ hell hound* dark form and savage face which broke upon us out of the wall of fog. [ The Hound of the Baskervilles, by Sir Arthur Conan Doyle ] +~helm of * +~crystal helmet +helm* +* helmet + A piece of armor designed to protect the head. + (What were you expecting?) +helm of telepathy + A helmet which allows the wearer to detect sentient creatures + within a modest radius, or within the whole level when blind. +helm of opposite alignment + A helmet which changes the wearer's alignment while worn. + Alignment will change back when the helmet is taken off. +helm of caution + A helmet which confers the "warning" property, sensing the + strength of threats posed by unseen hostile creatures. +# note: this quotation had "high of steel" but we're going to cheat--it is +# now deliberately mistranslated as "high of crystal" to match nethack 3.7's +# change to helm of brilliance and become more useful to players. +# PR #1109 wanted to change it to be for all helms but being covered with +# gems and gold is not appropriate for ordinary helmets (plus it states that +# it is unique, so is somewhat inappropriate for helm of brilliance too) +helm of brilliance +crystal helmet + A helm he set on his head, high of crystal; thereon was many + gemstone, all encompassed with gold; it was Uther's, the noble + king's; it was named Goswhit, each other unlike. + [ Layamon's Brut, translated by Sir Frederic Madden, K.H. ] hermes Messenger and herald of the Olympians. Being required to do a great deal of travelling and speaking in public, he became @@ -2722,6 +2808,43 @@ hunter Where is the haste that ye hurry by? Brother, I go to my lair to die. [ The Jungle Book, by Rudyard Kipling ] +# added in PR #1108 by NullCGT for ice box object but has been changed to +# ice terrain +# note: this might need to be reformatted; it's much wider than other entries +# but should be able to fit within a normal-width screen once nethack strips +# off the leading . +~ice box +ice +# thawing ice: "solid ice", "thin ice", &c +* ice +# hallucinating: "frozen " +frozen * + Ice, white ice, like a winding-sheet, sheathing each smoke-grimed wall; + Ice on the stove-pipe, ice on the bed, ice gleaming over all; + Sparkling ice on the dead man's chest, glittering ice in his hair, + Ice on his fingers, ice in his heart, ice in his glassy stare; + Hard as a log and trussed like a frog, with his arms and legs outspread. + I gazed at the coffin I'd brought for him, and I gazed at the gruesome dead, + And at last I spoke: "Bill liked his joke; but still, goldarn his eyes, + A man had ought to consider his mates in the way he goes and dies." + [ The Ballad of Blasphemous Bill, by Robert W. Service ] +# part of PR #1109 by entrez +ice box + I have eaten + the plums + that were in + the icebox + + and which + you were probably + saving + for breakfast + + Forgive me + they were delicious + so sweet + and so cold + [ This Is Just To Say, by William Carlos Williams ] ice devil Ice devils are large semi-insectoid creatures, who are equally at home in the fires of Hell and the cold of Limbo, @@ -4440,12 +4563,11 @@ platinum might make it useful on certain types of weapons. platinum yendorian express card This is an ancient artifact made of an unknown material. It - is rectangular in shape, very thin, and inscribed with - unreadable ancient runes. When carried, it grants the one - who carries it ESP, and reduces all spell induced damage done to - the carrier by half. It also protects from magic missile - attacks. Finally, its power is such that when invoked, it - can charge other objects. + is rectangular in shape, very thin, and inscribed with archaic + runes of power. When carried, it grants the bearer ESP, reduces + incoming damage from spells by half, and protects from magic + missile attacks. Additionally, its power is such that when + invoked, it can charge other objects. # playing style, rather vague topic but these quotes are too apt to pass up player play* style @@ -4463,6 +4585,16 @@ user Might just kill you. [ It's a Jungle Out There, by Randy Newman ] # [ theme song from "Monk" ] +poison +poisoned +potion of sickness +*venom + Fate intervened. Some of us, that day, she led inexorably + through the gates of death. Some of us, innocent and unsuspecting, + took, unwillingly, that one last step to oblivion. Some of us took + very little sugar. + [ We Have Always Lived in the Castle, + by Shirley Jackson ] polearm * polearm partisan @@ -4810,6 +4942,7 @@ robe 3. As the garb of Desert Nomads. [...] [ The Tough Guide to Fantasyland, by Diana Wynne Jones ] rock +large rock Bilbo saw that the moment had come when he must do something. He could not get up at the brutes and he had nothing to shoot with; but looking about he saw that in this place there were @@ -5073,6 +5206,7 @@ scorpius As Scorpius rises in the east, Orion sets in the west. [ 365 Starry Nights, by Chet Raymo ] scroll of punishment +punish* It matters not how strait the gate, How charged with punishments the scroll, I am the master of my fate; @@ -5114,25 +5248,25 @@ seth hippopotamus, and the pig. [ Encyclopedia Mythica, ed. M.F. Lindemans ] shad* - For the shades -- figures of jet black streaking acrossthe - white-knobbed ground -- were upon her. Strength was meaningless - against shades. They had no real substance. Only two things - mattered: moving quickly and not letting yourself be - frightened. Shades were dangerous, but so long as you had silver, - you could fight. [...] + For the shades--figures of jet black streaking across the + white-knobbed ground--were upon her. Strength was meaningless + against shades. They had no real substance. Only two things + mattered: moving quickly and not letting yourself be + frightened. Shades were dangerous, but so long as you had silver, + you could fight. [...] Her knife passed through the shade with a slight tugging feeling, creating a shower of bright white sparks that sprayed out of the - shade. The shade pulled back, its black tendrils writhing about + shade. The shade pulled back, its black tendrils writhing about one another. - Silence spun on the other. The pitch sky let her see only the - thing's eyes, a horrid green, as it reached for her. She lunged. + Silence spun on the other. The pitch sky let her see only the + thing's eyes, a horrid green, as it reached for her. She lunged. Its spectral hands were upon her, the icy cold of its fingers - gripping her arm below the elbow. She could feel it. Shade fingers - had substance; they could grab you, hold you back. Only silver - warded them away. Only with silver could you fight. + gripping her arm below the elbow. She could feel it. Shade + fingers had substance; they could grab you, hold you back. Only + silver warded them away. Only with silver could you fight. [ Shadows for Silence in the Forests of Hell, by Brandon Sanderson ] shaman karnov @@ -5161,6 +5295,26 @@ shark that no other creature in nature could make -- a bomb from the depths. [ Close to Shore, by Michael Capuzzo ] +~uruk* +~white* +*shield + In his hands he took his shield, all glittering: no one ever + broke it with a blow or crushed it. And a wonder it was to see; + for its whole orb shimmered with enamel and white ivory and + electrum, and it glowed with shining gold; and there were zones + of cyanus drawn upon it. In the center was Fear worked in adamant, + unspeakable, staring backwards with eyes that glowed with fire. + His mouth was full of teeth in a white row, fearful and daunting, + and upon his grim brow hovered frightful Strife who arrays the + throng of men: pitiless she, for she took away the mind and senses + of poor wretches who made war against the son of Zeus. Their souls + passed beneath the earth and went down into the house of Hades; + but their bones, when the skin is rotted about them, crumble away + on the dark earth under parching Sirius. + [ Hesiod, The Homeric Hymns and Homerica, + translation by Hugh G. Evelyn-White ] +shito + A Japanese stabbing knife. shopkeeper There have been three general theories put forward to explain the phenomenon of the wandering shops or, as they are @@ -5443,32 +5597,31 @@ stair* [ Ghostbusters, directed by Ivan Reitman, written by Dan Ackroyd and Harold Ramis ] *stalker - "You don't understand," he said, "who I am or what I am. I'll show - you. By Heaven! I'll show you." Then he put his open palm over his - face and withdrew it. The centre of his face became a black - cavity. "Here," he said. He stepped forward and handed Mrs. Hall + "You don't understand," he said, "who I am or what I am. I'll show + you. By Heaven! I'll show you." Then he put his open palm over + his face and withdrew it. The centre of his face became a black + cavity. "Here," he said. He stepped forward and handed Mrs. Hall something which she, staring at his metamorphosed face, accepted - automatically. Then, when she saw what it was, she screamed - loudly, dropped it, and staggered back. The nose--it was the + automatically. Then, when she saw what it was, she screamed + loudly, dropped it, and staggered back. The nose--it was the stranger's nose! pink and shining--rolled on the floor. - Then he removed his spectacles, and everyone in the bar gasped. He + Then he removed his spectacles, and everyone in the bar gasped. He took off his hat, and with a violent gesture tore at his whiskers - and bandages. For a moment they resisted him. A flash of horrible - anticipation passed through the bar. "Oh, my Gard!" said some one. + and bandages. For a moment they resisted him. A flash of horrible + anticipation passed through the bar. "Oh, my Gard!" said some one. Then off they came. - It was worse than anything. Mrs. Hall, standing open-mouthed and + It was worse than anything. Mrs. Hall, standing open-mouthed and horror-struck, shrieked at what she saw, and made for the door of - the house. Everyone began to move. They were prepared for scars, - disfigurements, tangible horrors, but nothing! The bandages and + the house. Everyone began to move. They were prepared for scars, + disfigurements, tangible horrors, but nothing! The bandages and false hair flew across the passage into the bar, making a - hobbledehoy jump to avoid them. Everyone tumbled on everyone else - down the steps. For the man who stood there shouting some + hobbledehoy jump to avoid them. Everyone tumbled on everyone else + down the steps. For the man who stood there shouting some incoherent explanation, was a solid gesticulating figure up to the - coat-collar of him, and then--nothingness, no visible thing at - all! - [ The Invisible Man, by H. G. Wells ] + coat-collar of him, and then--nothingness, no visible thing at all! + [ The Invisible Man, by H.G. Wells ] ~statue trap statue* Then at last he began to wonder why the lion was standing so @@ -5488,6 +5641,22 @@ statue* the lion's back and the top of its head were covered with snow. Of course it must be only a statue! [ The Lion, the Witch and the Wardrobe, by C.S. Lewis ] +# the original stethoscope was a fixed tube open at both ends; subsequent +# early ones were adaptations of an 'earhorn' used by the hard of hearing, +# but players are unlikely to picture anything like that when they find one +stethoscope + Tool used to listen closely to adjacent sound, typically by + a medical practitioner to listen to a patient's heart, lungs, + or pulse. +# joke suggested by stethoscope; a zillion things could be added but these +# ought to suffice +microscope +telescope +telephone +television +#tricorder +#phaser + Not invented yet. sting There was the usual dim grey light of the forest-day about him when he came to his senses. The spider lay dead beside @@ -5526,6 +5695,8 @@ stormbringer great black battle-blade Stormbringer, drank their souls. [ The Lands Beyond the World, by Michael Moorcock ] *strange object +strange +glorkum* He walked for some time through a long narrow corridor without finding any one and was just going to call out, when suddenly in a dark corner between an old cupboard @@ -5918,6 +6089,15 @@ unicorn horn unreconnoitered Area of map which is beyond limited perception range when underwater or engulfed by a monster. +uruk*hai shield +white-handed shield + They were armed with short broad-bladed swords, not with the + curved scimitars usual with Orcs: and they had bows of yew, + in length and shape like the bows of Men. Upon their shields + they bore a strange device: a small white hand in the centre + of a black field; on the front of their iron helms was set an + S-rune, wrought of some white metal. + [ The Two Towers, by J.R.R. Tolkien ] valkyrie * valkyrie The Valkyries were the thirteen choosers of the slain, the diff --git a/dat/dungeon.lua b/dat/dungeon.lua index 39b1f881ec..84145dd3a1 100644 --- a/dat/dungeon.lua +++ b/dat/dungeon.lua @@ -79,7 +79,7 @@ dungeon = { base = 13, range = 3, chance = 40, - nlevels = 13 + nlevels = 14 }, { name = "medusa", diff --git a/dat/fire.lua b/dat/fire.lua index c97973c023..1cd3db7a29 100644 --- a/dat/fire.lua +++ b/dat/fire.lua @@ -1,4 +1,4 @@ --- NetHack endgame fire.lua $NHDT-Date: 1652196026 2022/05/10 15:20:26 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1 $ +-- NetHack endgame fire.lua $NHDT-Date: 1700398454 2023/11/19 12:54:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.4 $ -- Copyright (c) 1989 by Jean-Christophe Collet -- Copyright (c) 1992,1993 by Izchak Miller, David Cohrs, -- and Timo Hakulinen @@ -12,30 +12,32 @@ des.level_flags("mazelevel", "noteleport", "hardfloor", "shortsighted", "hot", " -- portal to the next level is randomly chosen. -- This map has no visible outer boundary, and -- is mostly open area, with lava lakes and bunches of fire traps. +-- It fills the entire mappable area. des.map([[ -............................................................................ -....LLLLLLLL............L.......................LLL......................... -...LL...................L......................LLLL................LL....... -...L.............LLLL...LL....LL...............LLLLL.............LLL........ -.LLLL..............LL....L.....LLL..............LLLL..............LLLL...... -..........LLLL...LLLL...LLL....LLL......L........LLLL....LL........LLL...... -........LLLLLLL...LL.....L......L......LL.........LL......LL........LL...L.. -........LL..LLL..LL......LL......LLLL..L.........LL......LLL............LL.. -....L..LL....LLLLL.................LLLLLLL.......L......LL............LLLLLL -....L..L.....LL.LLLL.......L............L........LLLLL.LL......LL.........LL -....LL........L...LL......LL.............LLL.....L...LLL.......LLL.........L -.....LLLLLL........L.......LLL.............L....LL...L.LLL......LLLLLLL..... -..........LLLL............LL.L.............L....L...LL.........LLL..LLL..... -...........................LLLLL...........LL...L...L........LLLL..LLLLLL... -.....LLLL.............LL....LL.......LLL...LL.......L..LLL....LLLLLLL....... -.......LLL.........LLLLLLLLLLL......LLLLL...L...........LL...LL...LL........ -.........LL.......LL.........LL.......LLL....L..LLL....LL.........LL........ -..........LLLLLLLLL...........LL....LLL.......LLLLL.....LL........LL........ -.................L.............LLLLLL............LL...LLLL.........LL....... -.................................LL....................LL................... +LL.............LL..............L...LL.........LL.................LL...........L +LL....LLLLLLLL............L...L.............LL....LLL.......................LL. +L....LL...................L......................LLLL................LL........ +.....L.............LLLL...LL....LL...............LLLLL.............LLL......... +.L.LLLL..............LL....L.....LLL..............LLLL..............LLLL......L +LL..........LLLL...LLLL...LLL....LLL......L........LLLL....LL........LLL......L +LL........LLLLLLL...LL.....L......L......LL.........LL......LL........LL...L... +L.........LL..LLL..LL......LL......LLLL..L.........LL......LLL............LL... +......L..LL....LLLLL.................LLLLLLL.......L......LL............LLLLLL. +......L..L.....LL.LLLL.......L............L........LLLLL.LL......LL.........LL. +......LL........L...LL......LL.............LLL.....L...LLL.......LLL.........L. +.L.....LLLLLL........L.......LLL.............L....LL...L.LLL......LLLLLLL...... +LL..........LLLL............LL.L.............L....L...LL.........LLL..LLL...... +.L...........................LLLLL...........LL...L...L........LLLL..LLLLLL...L +.L.....LLLL.............LL....LL.......LLL...LL.......L..LLL....LLLLLLL.......L +.........LLL.........LLLLLLLLLLL......LLLLL...L...........LL...LL...LL......... +...........LL.......LL.........LL.......LLL....L..LLL....LL.........LL......... +............LLLLLLLLL...........LL....LLL.......LLLLL.....LL........LL......... +.LL...............L.............LLLLLL............LL...LLLL.........LL.......L. +LL.....L..........................LL....................LL..................LLL +L.....LLL......................LLLLL.........L.........LLLLLLLL..............LL ]]); -des.teleport_region({ region = {69,16,69,16} }) -des.levregion({ region = {0,0,75,19}, exclude = {65,13,75,19}, type="portal", name="water" }) +des.teleport_region({ region = {71,16,71,16} }) +des.levregion({ region = {0,0,78,19}, exclude = {67,13,78,19}, type="portal", name="water" }) des.trap("fire") des.trap("fire") diff --git a/dat/hellfill.lua b/dat/hellfill.lua index 3407540a8e..9948486f25 100644 --- a/dat/hellfill.lua +++ b/dat/hellfill.lua @@ -98,6 +98,7 @@ xxxxxx.xxxxxx ]], contents = function() des.non_diggable(selection.area(2,2, 10,8)); des.region(selection.area(4,4, 8,6), "lit"); + des.exclusion({ type = "teleport", region = { 2,2, 10,8 } }); local dblocs = { { x = 1, y = 5, dir="east", state="closed" }, { x = 11, y = 5, dir="west", state="closed" }, @@ -161,6 +162,7 @@ x........x x........x xx......xx xxxx..xxxx]], contents = function() + des.exclusion({ type = "teleport", region = { 4,4, 5,5 } }); local mons = { "Angel", "D", "H", "L" }; des.monster(mons[math.random(1, #mons)], 4,4); end }); @@ -178,6 +180,7 @@ x.......x ..}}}}}.. x.......x ]], contents = function(rm) + des.exclusion({ type = "teleport", region = { 3,3, 5,5 } }); des.monster("L",04,04) des.object('"',04,04) end }) diff --git a/dat/history b/dat/history index 8d96e132dd..f9cde2c10f 100644 --- a/dat/history +++ b/dat/history @@ -179,7 +179,10 @@ 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 3.4.1. -Ron Van Iwaarden maintained 3.4 for OS/2. +Ron Van Iwaarden was the sole maintainer of NetHack for OS/2 the past several +releases. Unfortunately Ron's last OS/2 machine stopped working in early +2006. A great many thanks to Ron for keeping NetHack alive on OS/2 all these +years. Janne Salmijarvi and Teemu Suikki maintained and enhanced the Amiga port of 3.4 after Janne Salmijarvi resurrected it for 3.3.1. @@ -229,7 +232,7 @@ Kevin Smolkowski, ensured that NetHack 3.6 continued to operate on various UNIX flavors and maintained the X11 interface. Ken Lorber, Haoyang Wang, Pat Rankin, and Dean Luick maintained the port -of NetHack 3.6 for Mac OSX. +of NetHack 3.6 for MacOS. Michael Allison, David Cohrs, Bart House, Pasi Kallinen, Alex Kompel, Dion Nicolaas, Derek S. Ray and Yitzhak Sapir maintained the port of diff --git a/dat/luahelper b/dat/luahelper new file mode 100644 index 0000000000..7f8a653fa7 --- /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 75aa7f736f..282d3668bf 100644 --- a/dat/medusa-3.lua +++ b/dat/medusa-3.lua @@ -1,4 +1,4 @@ --- NetHack medusa medusa-3.lua $NHDT-Date: 1652196028 2022/05/10 15:20:28 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.7 $ +-- NetHack medusa medusa-3.lua $NHDT-Date: 1716152250 2024/05/19 20:57:30 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.8 $ -- Copyright (c) 1989 by Jean-Christophe Collet -- Copyright (c) 1990, 1991 by M. Stephenson -- NetHack may be freely redistributed. See license for details. @@ -32,11 +32,21 @@ des.map([[ ]]); local place = selection.new(); +-- each of these spots are inside a distinct room place:set(08,06); place:set(66,05); place:set(46,15); -local medloc = place:rndcoord(1); +-- location of Medusa and downstairs and Perseus's statue +local medloc = place:rndcoord(1,1); +-- specific location for some other statue in a different downstairs-eligible +-- room, to prevent object detection from becoming a trivial way to pinpoint +-- Medusa's location +-- [usefulness depends on future STATUE->dknown changes in nethack's core] +local altloc = place:rndcoord(1,1); +-- location of a fountain, in the remaining of three downstairs-eligible rooms +local othloc = place:rndcoord(1,1); +-- once here, all three points set in 'place' have been used up des.region(selection.area(00,00,74,19),"lit") -- fixup_special hack: the first room defined on a Medusa level gets some @@ -56,15 +66,20 @@ des.non_diggable(selection.area(44,13,48,17)) des.teleport_region({ region = {33,02,38,07}, dir="down" }) des.levregion({ region = {32,01,39,07}, type="stair-up" }); +-- place the downstairs at the same spot where Medusa will be placed des.stair("down", medloc); +-- des.door("locked",08,08) des.door("locked",64,05) des.door("random",50,13) des.door("locked",48,15) -- -des.feature("fountain", place:rndcoord(1)); +-- in one of the three designated rooms, but not the one with Medusa plus +-- downstairs and also not 'altloc' where a random statue will be placed +des.feature("fountain", othloc); -- -des.object({ id="statue", coord=place:rndcoord(1), buc = "uncursed", +-- same spot as Medusa plus downstairs +des.object({ id="statue", coord=medloc, buc="uncursed", montype="knight", historic=1, male=1,name="Perseus", contents = function() if percent(75) then @@ -82,7 +97,9 @@ des.object({ id="statue", coord=place:rndcoord(1), buc = "uncursed", end }); -- -des.object({ id = "statue", contents=0 }) +-- first random statue is in one of the three designated rooms but not the +-- one with Medusa plus downstairs or the one with the fountain +des.object({ id = "statue", coord=altloc, contents=0 }) des.object({ id = "statue", contents=0 }) des.object({ id = "statue", contents=0 }) des.object({ id = "statue", contents=0 }) @@ -102,6 +119,8 @@ des.trap("board") des.trap("board") des.trap() -- +-- place Medusa before placing other monsters so that they won't be able to +-- unintentionally steal her spot on the downstairs des.monster({ id = "Medusa", coord=medloc, asleep=1 }) des.monster("giant eel") des.monster("giant eel") @@ -113,6 +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/medusa-4.lua b/dat/medusa-4.lua index bc49f0d9b0..acf6c6d3fc 100644 --- a/dat/medusa-4.lua +++ b/dat/medusa-4.lua @@ -1,4 +1,4 @@ --- NetHack medusa medusa-4.lua $NHDT-Date: 1652196028 2022/05/10 15:20:28 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.6 $ +-- NetHack medusa medusa-4.lua $NHDT-Date: 1716152274 2024/05/19 20:57:54 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.8 $ -- Copyright (c) 1989 by Jean-Christophe Collet -- Copyright (c) 1990, 1991 by M. Stephenson -- NetHack may be freely redistributed. See license for details. @@ -32,17 +32,30 @@ des.map([[ }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}.}}}}}}....}}}}}}}}}}}}}}}}}}}...}}}}}} }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} ]]); + -- +-- place handling is similar to medusa-3.lua except that there are 4 +-- downstairs-eligible rooms rather than 3, and only 2 of them are used local place = selection.new(); +-- each of these spots are inside a distinct room place:set(04,08); place:set(10,04); place:set(10,08); place:set(10,12); + +-- location of Medusa and downstairs and Perseus's statue +local medloc = place:rndcoord(1,1); +-- specific location for some other statue in a different downstairs-eligible +-- room, to prevent object detection from becoming a trivial way to pinpoint +-- Medusa's location +-- [usefulness depends on future STATUE->dknown changes in nethack's core] +local altloc = place:rndcoord(1,1); + -- des.region(selection.area(00,00,74,19),"lit") --- fixup_special hack: The first "room" region in Medusa levels gets filled with --- some leaderboard statues, so this needs to be a room; setting irregular=1 --- will force this +-- fixup_special hack: The first "room" region in Medusa levels gets filled +-- with some leaderboard statues, so this needs to be a room; setting +-- irregular=1 will force this des.region({ region={13,03, 18,13}, lit=1, type="ordinary", irregular=1 }) -- des.teleport_region({ region = {64,01,74,17}, dir="down" }); @@ -50,7 +63,8 @@ des.teleport_region({ region = {02,02,18,13}, dir="up" }); -- des.levregion({ region = {67,01,74,20}, type="stair-up" }); -des.stair("down", place:rndcoord(1)) +-- place the downstairs at the same spot where Medusa will be placed +des.stair("down", medloc) -- des.door("locked",04,06) des.door("locked",04,10) @@ -66,7 +80,8 @@ des.non_diggable(selection.area(01,01,22,14)); -- des.object("crystal ball", 07,08) -- -des.object({ id="statue",coord=place:rndcoord(1), buc="uncursed", +-- same spot as Medusa plus downstairs +des.object({ id="statue", coord=medloc, buc="uncursed", montype="knight", historic=1, male=1,name="Perseus", contents = function() if percent(75) then @@ -84,7 +99,9 @@ des.object({ id="statue",coord=place:rndcoord(1), buc="uncursed", end }); -- -des.object({ id = "statue", contents=0 }) +-- first random statue is in one of the designated stair rooms but not the +-- one with Medusa plus downstairs +des.object({ id = "statue", coord=altloc, contents=0 }) des.object({ id = "statue", contents=0 }) des.object({ id = "statue", contents=0 }) des.object({ id = "statue", contents=0 }) @@ -99,7 +116,9 @@ for i=1,7 do des.trap() end -- -des.monster("Medusa", place:rndcoord(1)) +-- place Medusa before placing other monsters so that they won't be able to +-- unintentionally steal her spot on the downstairs +des.monster({ id = "Medusa", coord=medloc, asleep=1 }) des.monster("kraken", 07,07) -- -- the nesting dragon @@ -129,3 +148,5 @@ for i=1,4 do des.monster("black naga hatchling") des.monster("black naga") end + +--#medusa-4.lua diff --git a/dat/minend-2.lua b/dat/minend-2.lua index 16bc272b2c..e85e45870e 100644 --- a/dat/minend-2.lua +++ b/dat/minend-2.lua @@ -55,6 +55,11 @@ if percent(50) then end end +-- uncontrolled arrival (via trap door, level teleport) will be in the central +-- portion of level to prevent ending up stuck in the treasure area, whether +-- arriving from above or below (despite this being bottom of Mines branch, +-- hero might arrive from below by invoking Wiz role's Eye of the Aethiopica) +des.teleport_region({ region={23,03,48,16}, region_islev=1 }) -- Dungeon Description des.feature("fountain", {14,13}) diff --git a/dat/minetn-1.lua b/dat/minetn-1.lua index 0f239e8407..83e443ee86 100644 --- a/dat/minetn-1.lua +++ b/dat/minetn-1.lua @@ -37,11 +37,11 @@ des.map([[ -- Don't let the player fall into his likely death; used to explicitly exclude -- the town, but that meant that you couldn't teleport out as well as not in. -des.teleport_region({ region={01,01,20,19}, region_islev=1 }) +des.teleport_region({ region={01,01,75,19}, exclude={01,00,35,21}, region_islev=1 }) des.region(selection.area(01,01,35,17), "lit") -des.levregion({ type="stair-up", region={01,03,20,19}, region_islev=1, +des.levregion({ type="stair-up", region={01,03,21,19}, region_islev=1, exclude={00,01,36,17} }); -des.levregion({ type="stair-down", region={61,03,75,19}, region_islev=1, +des.levregion({ type="stair-down", region={57,03,75,19}, region_islev=1, exclude={00,01,36,17} }) -- Define areas of the map: diff --git a/dat/nhcore.lua b/dat/nhcore.lua index 5b188efb92..0fc778f6c4 100644 --- a/dat/nhcore.lua +++ b/dat/nhcore.lua @@ -136,5 +136,9 @@ nhcore = { -- getpos_tip is called the first time the code enters getpos() getpos_tip = show_getpos_tip, + + -- enter_tutorial and leave_tutorial + enter_tutorial = tutorial_enter, + leave_tutorial = tutorial_leave, }; diff --git a/dat/nhlib.lua b/dat/nhlib.lua index e3777f0848..0ef0664d10 100644 --- a/dat/nhlib.lua +++ b/dat/nhlib.lua @@ -145,17 +145,23 @@ end function tutorial_enter() -- nh.pline("TUT:enter"); + + -- add the tutorial branch callbacks + nh.callback("cmd_before", "tutorial_cmd_before"); + nh.callback("end_turn", "tutorial_turn"); + + -- save state for later restore nh.gamestate(); end function tutorial_leave() -- nh.pline("TUT:leave"); - -- remove the tutorial level callbacks + -- remove the tutorial branch callbacks nh.callback("cmd_before", "tutorial_cmd_before", true); - nh.callback("level_enter", "tutorial_enter", true); - nh.callback("level_leave", "tutorial_leave", true); nh.callback("end_turn", "tutorial_turn", true); + + -- restore state for regular play nh.gamestate(true); end diff --git a/dat/opthelp b/dat/opthelp index 1234921843..56cb323372 100644 --- a/dat/opthelp +++ b/dat/opthelp @@ -14,8 +14,10 @@ blind your character is permanently blind [False] bones allow loading bones files [True] clicklook look at map by clicking right mouse button [False] cmdassist give help for errors on direction & other commands [True] +color use different colors for objects on screen [True for micros] confirm ask before hitting tame or peaceful monsters [True] dark_room show floor not in sight in different color [True] +dropped_nopick exclude dropped objects from autopickup [True] eight_bit_tty send 8-bit characters straight to terminal [False] extmenu tty, curses: use menu for # (extended commands) [False] X11: menu has all commands (T) or traditional subset (F) @@ -27,6 +29,8 @@ goldX when filtering objects by bless/curse state, [False] help print all available info when using the / command [True] herecmd_menu show menu of some possible commands when clicking on yourself or next to you with mouse [False] +hilite_pet display pets in a highlighted manner [False] +hilite_pile display item piles in a highlighted manner [False] ignintr ignore interrupt signal, including breaks [False] implicit_uncursed omit "uncursed" from inventory, if possible [True] invweight show weights of objects in your inventory [TRUE] @@ -47,6 +51,7 @@ null allow nulls to be sent to your terminal [True] try turning this option off (forcing NetHack to use its own delay code) if moving objects seem to teleport across rooms perm_invent keep inventory in a permanent window [False] +pickup_stolen override pickup_types for stolen objects [True] pickup_thrown override pickup_types for thrown objects [True] pushweapon when wielding a new weapon, put your previously [False] wielded weapon into the secondary weapon slot @@ -60,6 +65,7 @@ safe_wait require use of 'm' prefix before '.' or 's' to [True] sanity_check perform data sanity checks [False] showexp display your accumulated experience points [False] showrace show yourself by your race rather than by role [False] +showvers show version number on status lines [False] silent don't use your terminal's bell sound [True] sortpack group similar kinds of objects in inventory [True] sparkle display sparkly effect for resisted magical [True] @@ -92,11 +98,6 @@ news print any news from game administrator on startup [True] Boolean option if SCORE_ON_BOTL was set at compile time: showscore display your approximate accumulated score [False] -Boolean options if TEXTCOLOR was set at compile time: -color use different colors for objects on screen [True for micros] -hilite_pet display pets in a highlighted manner [False] -hilite_pile display item piles in a highlighted manner [False] - Boolean option if TIMED_DELAY was set at compile time (tty or curses only): timed_delay on unix and VMS, use a timer instead of sending [True] extra screen output when attempting to pause for @@ -136,6 +137,7 @@ tiled_map show map as tiles, forces ascii_map Off; Qt, X11 Boolean options available when running in debug mode (aka wizard mode): menu_tab_sep menu formatting--do not touch monpolycontrol have player choose shape-changing monsters' new shapes +montelecontrol let player choose destination for teleported monsters travel_debug show state of travel pathfinding algorithm on the map wizweight include item weights in inventory display @@ -163,6 +165,9 @@ autounlock when attempting to open a locked door or loot [Apply-Key] omitted or skipped and Apply-Key is omitted or you aren't carrying an unlocking tool or you decline to use one boulder override the default boulder symbol [`] +crash_email email address to use when filling in crash reports [] +crash_name name to use when filling in crash reports [] +crash_urlmax length of longest url we can generate for a crash report [] disclose the types of information you want [ni na nv ng nc no] offered at the end of the game (space separated list of two-character values; @@ -211,9 +216,10 @@ packorder a list of default symbols for kinds of [")[%?+!=/(*`0_] some other things) gets shown if the 'sortpack' option is on (If you specify only some kinds of items, the others from the default order will be appended to the end.) -paranoid_confirmation space separated list [paranoid_confirmation:pray] +paranoid_confirmation space separated list [paranoid_confirm:pray swim] of situations where alternate prompting is desired - Confirm -- when requiring "yes", also require "no" to reject + Confirm -- when requiring "yes", also require "no" to reject; + also requires yes rather than y for pray, trap, Autoall quit -- yes vs y to confirm quitting or to enter explore mode die -- yes vs y to confirm dying (for explore or debug mode) bones -- yes vs y to confirm saving bones data in debug mode @@ -223,11 +229,23 @@ paranoid_confirmation space separated list [paranoid_confirmation:pray] Were-change -- yes vs y to confirm changing form due to lycanthropy when hero has polymorph control; pray -- y to confirm an attempt to pray; on by default + trap -- y to enter a known trap unless it is harmless; + swim -- require m prefix to move into water or lava when + hero has seen it and isn't impaired; on by default; + AutoAll -- y to confirm if using menustyle:Full and choice 'A' + in the object class filtering menu is selected; Remove -- always pick from inventory for 'R' and 'T' even when wearing just one applicable item to remove or take off - swim -- y to walk into a water or lava space when 'm'-moving - trap -- yes vs y to move onto a trap Throw -- y to throw ammo when not wielding its launcher +perminv_mode if the interface supports a persistent inventory window [a] + and the perm_invent option is true, this controls what + will be shown: + none/off -- behave as if perm_invent is false + all/on -- show inventory except for gold (default) + full -- show inventory including gold + in-use -- only show worn and wielded items + (the tty interface's optional perm_invent support includes + a couple of additional choices that vary all and full) pickup_burden when you pick up an item that exceeds this encumbrance [S] level (Unencumbered, Burdened, streSsed, straiNed, overTaxed, or overLoaded), you will be asked if you want to continue. @@ -274,6 +292,8 @@ suppress_alert disable various version-specific warnings about changes [] for the 'Q' command that quitting is now done via #quit (e.g., use suppress_alert:3.3.1 to stop that and any other notifications added in that version or earlier) +versinfo selects what information is displayed when showvers [1 or 4] + is true (default depends on program development status) whatis_coord controls whether to include map coordinates when [n] autodescribe is active for the '/' and ';' commands. Value is the first letter of one of diff --git a/dat/optmenu b/dat/optmenu index ac44de33fa..f08df0f2e6 100644 --- a/dat/optmenu +++ b/dat/optmenu @@ -1,12 +1,16 @@ How dynamically setting options works: - The options menu shows the current value for all options and lets - you pick ones that you'd like to change. Picking them doesn't make - any immediate changes though. That will take place once you close - the menu. For most of NetHack's interfaces, closing the menu is - done by pressing the key or key; others might - require clicking on [ok]. Pressing the key or clicking - on [cancel] will close the menu and discard any pending changes. + The simple options menu shows a relatively small subset of options + and operates on each choice you make immediately, then is put back + up to allow further changes. + + The full options menu shows the current value for all options and + lets you pick ones that you'd like to change. Picking them doesn't + make any changes though. That will take place once you close the + menu. For most of NetHack's interfaces, closing the menu is done + by pressing the key or key; others might require + clicking on [ok]. Pressing the key or clicking on [cancel] + will close the menu and discard any pending changes. The options menu is too long to fit on one screen. Some interfaces paginate menus; use the '>' key to advance a page or '<' to back diff --git a/dat/quest.lua b/dat/quest.lua index 40e98e3538..81eb96f4d5 100644 --- a/dat/quest.lua +++ b/dat/quest.lua @@ -1,4 +1,4 @@ --- NetHack quest.lua $NHDT-Date: 1652196288 2022/05/10 15:24:48 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.7 $ +-- NetHack quest.lua $NHDT-Date: 1726894904 2024/09/21 05:01:44 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.10 $ -- Copyright (c) 2021 by Pasi Kallinen -- NetHack may be freely redistributed. See license for details. -- TODO: @@ -228,6 +228,28 @@ to recover the Amulet for your deity, or die in the attempt. Your hour of destiny has come. For the sake of us all: Go bravely with %d!]], }, +-- starting with 'pauper' option set, last paragraph differs from normal legacy + pauper_legacy = { + synopsis = "[%dC has chosen you to recover the Amulet of Yendor for %dI.]", + output = "menu", + text = [[It is written in the Book of %d: + + After the Creation, the cruel god Moloch rebelled + against the authority of Marduk the Creator. + Moloch stole from Marduk the most powerful of all + the artifacts of the gods, the Amulet of Yendor, + and he hid it in the dark cavities of Gehennom, the + Under World, where he now lurks, and bides his time. + +Your %G %d seeks to possess the Amulet, and with it +to gain deserved ascendance over the other gods. + +You, an untrained %r, have been unable to adequately +prepare to be the instrument of %d. Nevertheless, you +are destined to recover the Amulet for your deity, or die +in the attempt. Your hour of destiny has come. For the +sake of us all: Go bravely with %d!]], + }, quest_complete_no_bell = { text = [["The silver bell which was hoarded by %n will be essential in locating the Amulet of Yendor."]], @@ -1239,7 +1261,7 @@ with good conscience. "\"I will impale your head on my caduceus for all to see.\"", "\"There is no materia medica in your sack which will cure you of me!\"", "\"Do not fight too hard, I want your soul strong, not weakened!\"", - "\"You should have stopped studying at vetenary.\"", + "\"You should have stopped studying at veterinary.\"", }, encourage = { "\"Remember, %p, to always wash your hands before operating.\"", @@ -2992,7 +3014,7 @@ the best excursions happen to the audacious."]], %o from me! "It only proves how desperate he has become that he sends %ra to -try and defeat me. When this day is over, I will have you enslaved +try to defeat me. When this day is over, I will have you enslaved in the mines where you will rue the day that you ever entered %i."]], }, diff --git a/dat/rumors.fal b/dat/rumors.fal index dfec891ce9..b2605baf18 100644 --- a/dat/rumors.fal +++ b/dat/rumors.fal @@ -246,6 +246,7 @@ If Juiblex eats you, don't panic. You brought your pick-axe, didn't you? If it looks like fruit juice and tastes like fruit juice... it is fruit juice. If the prize isn't behind the first Sokoban door, there's a 2/3 chance that it's behind the second one. If it says "Closed for inventory", it'll be open again within 2000 turns. +If there's a spot of blood on your hands, why not wash them? [cookie] If this cookie satiated you, please consider leaving a good review. If you are the shopkeeper, you can take things for free. If you are too charismatic some monsters might be tempted to embrace you. diff --git a/dat/rumors.tru b/dat/rumors.tru index 853f641ee9..bf9bf19edf 100644 --- a/dat/rumors.tru +++ b/dat/rumors.tru @@ -168,6 +168,7 @@ If you want to rob a shop, train your dog. If you're blind, don't expect your dog to become a seeing-eye dog. If you're going through hell, keep going... until you find the Amulet of Yendor, then come back up. If you're lost, try buying a map next time you're in a shop. +If your hands are greasy, why not wash them? Inside a shop you better take a look at the price tags before buying anything. It's easy to fool an enchant weapon scroll - it thinks anything you wield is a weapon! It's worth reading the label on tins; it might even say what's inside. @@ -494,6 +495,8 @@ They say that the view from a fog cloud is really very moving. They say that the walls in shops are made of extra hard material. They say that there are at least 15 ways to lose a pair of levitation boots. They say that throwing glass gems is the same as throwing rocks. +They say that trespassing a boulder is probably beneath you. +They say that true power comes from within...except when it doesn't. They say that unicorns are fond of precious gems. They say that what goes down the drain might come back up. They say that wielded, a long sword named Fire Brand makes you feel cooler. @@ -514,6 +517,7 @@ They say that you should try to invoke an artifact, just to see what it does. They say that you will never get healthy by eating geckos. They say that you'll never lose a ring of searching in a sink. They say that zapping yourself with a wand of undead turning is dreadful. +They say the Furies are more furious if you've been sinning. They say the gods get angry if you kill your dog. They say the gods get angry if you pray too much. They say there is a powerful magic item hidden in a castle deep down! @@ -579,5 +583,6 @@ You will never go to space. You won't always get a second chance, even with life saving. You're going into the graveyard at midnight??? Your dog knows what to eat; maybe you should take lessons. +Your game may go down the drain if you have extra potions or rings. Zap yourself and see what happens... Zapping a wand of undead turning might bring your dog back to life. diff --git a/dat/soko1-1.lua b/dat/soko1-1.lua index 0b86e6be4c..564da50575 100644 --- a/dat/soko1-1.lua +++ b/dat/soko1-1.lua @@ -4,7 +4,7 @@ -- des.level_init({ style = "solidfill", fg = " " }); -des.level_flags("mazelevel", "noteleport", "premapped", "solidify", "noflip"); +des.level_flags("mazelevel", "noteleport", "premapped", "sokoban", "solidify", "noflip"); des.map([[ -------------------------- |........................| @@ -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 7595f5bf68..852683d11d 100644 --- a/dat/soko1-2.lua +++ b/dat/soko1-2.lua @@ -4,7 +4,7 @@ -- des.level_init({ style = "solidfill", fg = " " }); -des.level_flags("mazelevel", "noteleport", "premapped", "solidify", "noflip"); +des.level_flags("mazelevel", "noteleport", "premapped", "sokoban", "solidify", "noflip"); des.map([[ ------------------------ @@ -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 48a008d8fb..cc29e99ad4 100644 --- a/dat/soko2-1.lua +++ b/dat/soko2-1.lua @@ -4,7 +4,7 @@ -- des.level_init({ style = "solidfill", fg = " " }); -des.level_flags("mazelevel", "noteleport", "premapped", "solidify", "noflip"); +des.level_flags("mazelevel", "noteleport", "premapped", "sokoban", "solidify", "noflip"); des.map([[ -------------------- @@ -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 fa75bf390d..b25df599ce 100644 --- a/dat/soko2-2.lua +++ b/dat/soko2-2.lua @@ -4,7 +4,7 @@ -- des.level_init({ style = "solidfill", fg = " " }); -des.level_flags("mazelevel", "noteleport", "premapped", "solidify", "noflip"); +des.level_flags("mazelevel", "noteleport", "premapped", "sokoban", "solidify", "noflip"); des.map([[ -------- @@ -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 35270c15da..725d7e94f5 100644 --- a/dat/soko3-1.lua +++ b/dat/soko3-1.lua @@ -4,7 +4,7 @@ -- des.level_init({ style = "solidfill", fg = " " }); -des.level_flags("mazelevel", "noteleport", "premapped", "solidify", "noflip"); +des.level_flags("mazelevel", "noteleport", "premapped", "sokoban", "solidify", "noflip"); des.map([[ ----------- ----------- @@ -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 e5794ebb93..b357f5dd37 100644 --- a/dat/soko3-2.lua +++ b/dat/soko3-2.lua @@ -4,7 +4,7 @@ -- des.level_init({ style = "solidfill", fg = " " }); -des.level_flags("mazelevel", "noteleport", "premapped", "solidify", "noflip"); +des.level_flags("mazelevel", "noteleport", "premapped", "sokoban", "solidify", "noflip"); des.map([[ ---- ----------- @@ -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 3e651a586e..9102b91fc8 100644 --- a/dat/soko4-1.lua +++ b/dat/soko4-1.lua @@ -37,7 +37,7 @@ des.level_init({ style = "solidfill", fg = " " }); des.message("The floor here is covered in deep perpendicular grooves.") -des.level_flags("mazelevel", "noteleport", "hardfloor", "premapped", "solidify", "noflip"); +des.level_flags("mazelevel", "noteleport", "hardfloor", "premapped", "sokoban", "solidify", "noflip"); des.map([[ ------ ----- @@ -74,6 +74,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 8e48749699..201d41929d 100644 --- a/dat/soko4-2.lua +++ b/dat/soko4-2.lua @@ -6,7 +6,7 @@ des.level_init({ style = "solidfill", fg = " " }); des.message("The floor here is covered in deep perpendicular grooves.") -des.level_flags("mazelevel", "noteleport", "hardfloor", "premapped", "solidify", "noflip"); +des.level_flags("mazelevel", "noteleport", "hardfloor", "premapped", "sokoban", "solidify", "noflip"); des.map([[ -------- ------ @@ -43,6 +43,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/symbols b/dat/symbols index a60ffc1ddc..f99fa8df98 100644 --- a/dat/symbols +++ b/dat/symbols @@ -1,4 +1,4 @@ -# NetHack 3.7 symbols $NHDT-Date: 1596498253 2020/08/03 23:44:13 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.22 $ +# NetHack 3.7 symbols $NHDT-Date: 1725052751 2024/08/30 21:19:11 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.36 $ # Copyright (c) 2006 by Michael Allison # NetHack may be freely redistributed. See license for details. # @@ -33,6 +33,10 @@ # NetHack ttychar symbol as usual. That is true even when the UTF-8 # handler is in effect. # +# \xnn specifies a hexadecimal value for nn. +# \0nn specifies a decimal value for nn, not octal as leading 0 indicates +# in C and programming languages influenced by it. + # plain looks decent for room+corridor levels where there aren't a lot # of wall corners and ones present tend to be spread out, but it looks # awful for wallified mazes @@ -91,6 +95,8 @@ start: Blank S_dragon: \032 S_eel: \032 S_elemental: \032 + S_engroom: \032 + S_engrcorr: \032 S_expl_tl: \032 S_expl_tc: \032 S_expl_tr: \032 @@ -275,6 +281,50 @@ start: IBMgraphics S_sw_mr: \xb3 # meta-3, vertical rule S_expl_ml: \xb3 # meta-3, vertical rule S_expl_mr: \xb3 # meta-3, vertical rule + G_vwall_sokoban: /blue + G_hwall_sokoban: /blue + G_tlcorn_sokoban: /blue + G_trcorn_sokoban: /blue + G_blcorn_sokoban: /blue + G_brcorn_sokoban: /blue + G_crwall_sokoban: /blue + G_tuwall_sokoban: /blue + G_tdwall_sokoban: /blue + G_tlwall_sokoban: /blue + G_trwall_sokoban: /blue + G_vwall_gehennom: /red + G_hwall_gehennom: /red + G_tlcorn_gehennom: /red + G_trcorn_gehennom: /red + G_blcorn_gehennom: /red + G_brcorn_gehennom: /red + G_crwall_gehennom: /red + G_tuwall_gehennom: /red + G_tdwall_gehennom: /red + G_tlwall_gehennom: /red + G_trwall_gehennom: /red + G_vwall_knox: /yellow + G_hwall_knox: /yellow + G_tlcorn_knox: /yellow + G_trcorn_knox: /yellow + G_blcorn_knox: /yellow + G_brcorn_knox: /yellow + G_crwall_knox: /yellow + G_tuwall_knox: /yellow + G_tdwall_knox: /yellow + G_tlwall_knox: /yellow + G_trwall_knox: /yellow + G_vwall_mines: /brown + G_hwall_mines: /brown + G_tlcorn_mines: /brown + G_trcorn_mines: /brown + G_blcorn_mines: /brown + G_brcorn_mines: /brown + G_crwall_mines: /brown + G_tuwall_mines: /brown + G_tdwall_mines: /brown + G_tlwall_mines: /brown + G_trwall_mines: /brown finish start: IBMGraphics_1 @@ -298,6 +348,50 @@ start: IBMGraphics_1 S_sw_mr: \xb3 # meta-3, vertical rule S_expl_ml: \xb3 # meta-3, vertical rule S_expl_mr: \xb3 # meta-3, vertical rule + G_vwall_sokoban: /blue + G_hwall_sokoban: /blue + G_tlcorn_sokoban: /blue + G_trcorn_sokoban: /blue + G_blcorn_sokoban: /blue + G_brcorn_sokoban: /blue + G_crwall_sokoban: /blue + G_tuwall_sokoban: /blue + G_tdwall_sokoban: /blue + G_tlwall_sokoban: /blue + G_trwall_sokoban: /blue + G_vwall_gehennom: /red + G_hwall_gehennom: /red + G_tlcorn_gehennom: /red + G_trcorn_gehennom: /red + G_blcorn_gehennom: /red + G_brcorn_gehennom: /red + G_crwall_gehennom: /red + G_tuwall_gehennom: /red + G_tdwall_gehennom: /red + G_tlwall_gehennom: /red + G_trwall_gehennom: /red + G_vwall_knox: /yellow + G_hwall_knox: /yellow + G_tlcorn_knox: /yellow + G_trcorn_knox: /yellow + G_blcorn_knox: /yellow + G_brcorn_knox: /yellow + G_crwall_knox: /yellow + G_tuwall_knox: /yellow + G_tdwall_knox: /yellow + G_tlwall_knox: /yellow + G_trwall_knox: /yellow + G_vwall_mines: /brown + G_hwall_mines: /brown + G_tlcorn_mines: /brown + G_trcorn_mines: /brown + G_blcorn_mines: /brown + G_brcorn_mines: /brown + G_crwall_mines: /brown + G_tuwall_mines: /brown + G_tdwall_mines: /brown + G_tlwall_mines: /brown + G_trwall_mines: /brown finish start: IBMGraphics_2 @@ -326,6 +420,50 @@ start: IBMGraphics_2 S_sw_mr: \xb3 # meta-3, vertical rule S_expl_ml: \xb3 # meta-3, vertical rule S_expl_mr: \xb3 # meta-3, vertical rule + G_vwall_sokoban: /blue + G_hwall_sokoban: /blue + G_tlcorn_sokoban: /blue + G_trcorn_sokoban: /blue + G_blcorn_sokoban: /blue + G_brcorn_sokoban: /blue + G_crwall_sokoban: /blue + G_tuwall_sokoban: /blue + G_tdwall_sokoban: /blue + G_tlwall_sokoban: /blue + G_trwall_sokoban: /blue + G_vwall_gehennom: /red + G_hwall_gehennom: /red + G_tlcorn_gehennom: /red + G_trcorn_gehennom: /red + G_blcorn_gehennom: /red + G_brcorn_gehennom: /red + G_crwall_gehennom: /red + G_tuwall_gehennom: /red + G_tdwall_gehennom: /red + G_tlwall_gehennom: /red + G_trwall_gehennom: /red + G_vwall_knox: /yellow + G_hwall_knox: /yellow + G_tlcorn_knox: /yellow + G_trcorn_knox: /yellow + G_blcorn_knox: /yellow + G_brcorn_knox: /yellow + G_crwall_knox: /yellow + G_tuwall_knox: /yellow + G_tdwall_knox: /yellow + G_tlwall_knox: /yellow + G_trwall_knox: /yellow + G_vwall_mines: /brown + G_hwall_mines: /brown + G_tlcorn_mines: /brown + G_trcorn_mines: /brown + G_blcorn_mines: /brown + G_brcorn_mines: /brown + G_crwall_mines: /brown + G_tuwall_mines: /brown + G_tdwall_mines: /brown + G_tlwall_mines: /brown + G_trwall_mines: /brown finish start: curses @@ -347,10 +485,10 @@ start: curses S_bars: \xfc # meta-|, not-equals sign (was '#') S_tree: \xe7 # meta-g, plus or minus sign S_room: \xfe # meta-z, centered dot - S_engroom: \xee # epsilon - S_corr: \xe1 # meta-a, solid block - S_litcorr: \xe1 # meta-a, solid block - S_engrcorr: \xe1 # meta-a, solid block + # S_engroom: \xee # epsilon [\xee is actually cross wall] + S_corr: \xe1 # meta-a, checkerboard + S_litcorr: \xe1 # meta-a, checkerboard + S_engrcorr: \xe1 # meta-a, checkerboard S_ice: \xfe # meta-z, centered dot S_vodbridge: \xfe # meta-z, centered dot S_hodbridge: \xfe # meta-z, centered dot @@ -360,6 +498,50 @@ start: curses S_sw_mr: \xf8 # meta-3, vertical rule S_expl_ml: \xf8 # meta-3, vertical rule S_expl_mr: \xf8 # meta-3, vertical rule + G_vwall_sokoban: /blue + G_hwall_sokoban: /blue + G_tlcorn_sokoban: /blue + G_trcorn_sokoban: /blue + G_blcorn_sokoban: /blue + G_brcorn_sokoban: /blue + G_crwall_sokoban: /blue + G_tuwall_sokoban: /blue + G_tdwall_sokoban: /blue + G_tlwall_sokoban: /blue + G_trwall_sokoban: /blue + G_vwall_gehennom: /red + G_hwall_gehennom: /red + G_tlcorn_gehennom: /red + G_trcorn_gehennom: /red + G_blcorn_gehennom: /red + G_brcorn_gehennom: /red + G_crwall_gehennom: /red + G_tuwall_gehennom: /red + G_tdwall_gehennom: /red + G_tlwall_gehennom: /red + G_trwall_gehennom: /red + G_vwall_knox: /yellow + G_hwall_knox: /yellow + G_tlcorn_knox: /yellow + G_trcorn_knox: /yellow + G_blcorn_knox: /yellow + G_brcorn_knox: /yellow + G_crwall_knox: /yellow + G_tuwall_knox: /yellow + G_tdwall_knox: /yellow + G_tlwall_knox: /yellow + G_trwall_knox: /yellow + G_vwall_mines: /brown + G_hwall_mines: /brown + G_tlcorn_mines: /brown + G_trcorn_mines: /brown + G_blcorn_mines: /brown + G_brcorn_mines: /brown + G_crwall_mines: /brown + G_tuwall_mines: /brown + G_tdwall_mines: /brown + G_tlwall_mines: /brown + G_trwall_mines: /brown finish start: DECgraphics @@ -378,8 +560,8 @@ start: DECgraphics S_tlwall: \xf5 # meta-u, T left S_trwall: \xf4 # meta-t, T right S_ndoor: \xfe # meta-~, centered dot - S_vodoor: \xe1 # meta-a, solid block - S_hodoor: \xe1 # meta-a, solid block + S_vodoor: \xe1 # meta-a, checkerboard + S_hodoor: \xe1 # meta-a, checkerboard S_bars: \xfc # meta-|, not-equals (used to be pi) S_tree: \xe7 # meta-g, plus-or-minus S_room: \xfe # meta-~, centered dot @@ -405,6 +587,50 @@ start: DECgraphics S_expl_ml: \xf8 # meta-x, vertical rule S_expl_mr: \xf8 # meta-x, vertical rule S_expl_bc: \xf3 # meta-s, low horizontal line + G_vwall_sokoban: /blue + G_hwall_sokoban: /blue + G_tlcorn_sokoban: /blue + G_trcorn_sokoban: /blue + G_blcorn_sokoban: /blue + G_brcorn_sokoban: /blue + G_crwall_sokoban: /blue + G_tuwall_sokoban: /blue + G_tdwall_sokoban: /blue + G_tlwall_sokoban: /blue + G_trwall_sokoban: /blue + G_vwall_gehennom: /red + G_hwall_gehennom: /red + G_tlcorn_gehennom: /red + G_trcorn_gehennom: /red + G_blcorn_gehennom: /red + G_brcorn_gehennom: /red + G_crwall_gehennom: /red + G_tuwall_gehennom: /red + G_tdwall_gehennom: /red + G_tlwall_gehennom: /red + G_trwall_gehennom: /red + G_vwall_knox: /yellow + G_hwall_knox: /yellow + G_tlcorn_knox: /yellow + G_trcorn_knox: /yellow + G_blcorn_knox: /yellow + G_brcorn_knox: /yellow + G_crwall_knox: /yellow + G_tuwall_knox: /yellow + G_tdwall_knox: /yellow + G_tlwall_knox: /yellow + G_trwall_knox: /yellow + G_vwall_mines: /brown + G_hwall_mines: /brown + G_tlcorn_mines: /brown + G_trcorn_mines: /brown + G_blcorn_mines: /brown + G_brcorn_mines: /brown + G_crwall_mines: /brown + G_tuwall_mines: /brown + G_tdwall_mines: /brown + G_tlwall_mines: /brown + G_trwall_mines: /brown finish start: MACgraphics @@ -481,28 +707,28 @@ start: Enhanced1 S_expl_ml: U+258f # LEFT ONE EIGHTH BLOCK S_expl_mr: U+2595 # RIGHT ONE EIGHTH BLOCK S_expl_bc: U+2581 # LOWER ONE EIGHTH BLOCK - G_vwall_sokoban: U+2502/0-0-190 - G_hwall_sokoban: U+2500/0-0-190 - G_tlcorn_sokoban: U+250c/0-0-190 - G_trcorn_sokoban: U+2510/0-0-190 - G_blcorn_sokoban: U+2514/0-0-190 - G_brcorn_sokoban: U+2518/0-0-190 - G_crwall_sokoban: U+253C/0-0-190 - G_tuwall_sokoban: U+2534/0-0-190 - G_tdwall_sokoban: U+252C/0-0-190 - G_tlwall_sokoban: U+2524/0-0-190 - G_trwall_sokoban: U+251C/0-0-190 - G_vwall_gehennom: U+2502/190-0-0 - G_hwall_gehennom: U+2500/190-0-0 - G_tlcorn_gehennom: U+250c/190-0-0 - G_trcorn_gehennom: U+2510/190-0-0 - G_blcorn_gehennom: U+2514/190-0-0 - G_brcorn_gehennom: U+2518/190-0-0 - G_crwall_gehennom: U+253C/190-0-0 - G_tuwall_gehennom: U+2534/190-0-0 - G_tdwall_gehennom: U+252C/190-0-0 - G_tlwall_gehennom: U+2524/190-0-0 - G_trwall_gehennom: U+251C/190-0-0 + G_vwall_sokoban: U+2502/blue + G_hwall_sokoban: U+2500/blue + G_tlcorn_sokoban: U+250c/blue + G_trcorn_sokoban: U+2510/blue + G_blcorn_sokoban: U+2514/blue + G_brcorn_sokoban: U+2518/blue + G_crwall_sokoban: U+253C/blue + G_tuwall_sokoban: U+2534/blue + G_tdwall_sokoban: U+252C/blue + G_tlwall_sokoban: U+2524/blue + G_trwall_sokoban: U+251C/blue + G_vwall_gehennom: U+2502/red + G_hwall_gehennom: U+2500/red + G_tlcorn_gehennom: U+250c/red + G_trcorn_gehennom: U+2510/red + G_blcorn_gehennom: U+2514/red + G_brcorn_gehennom: U+2518/red + G_crwall_gehennom: U+253C/red + G_tuwall_gehennom: U+2534/red + G_tdwall_gehennom: U+252C/red + G_tlwall_gehennom: U+2524/red + G_trwall_gehennom: U+251C/red G_vwall_knox: U+2502/150-75-0 G_hwall_knox: U+2500/150-75-0 G_tlcorn_knox: U+250c/150-75-0 diff --git a/dat/themerms.lua b/dat/themerms.lua index 0bb9fc5bd3..00ec450d7a 100644 --- a/dat/themerms.lua +++ b/dat/themerms.lua @@ -20,10 +20,13 @@ -- for each level, the core first calls pre_themerooms_generate(), -- then it calls themerooms_generate() multiple times until it decides -- enough rooms have been generated, and then it calls --- post_themerooms_generate(). The lua state is persistent through --- the gameplay, but not across saves, so remember to reset any variables. +-- post_themerooms_generate(). When the level has been generated, with +-- joining corridors and rooms filled, the core calls post_level_generate(). +-- The lua state is persistent through the gameplay, but not across saves, +-- so remember to reset any variables. -local buried_treasure = { }; + +local postprocess = { }; themeroom_fills = { @@ -40,6 +43,15 @@ themeroom_fills = { 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 { -- rolling boulder traps only generate on DL2 and below; bump the difficulty up a bit @@ -81,13 +93,29 @@ themeroom_fills = { locs:iterate(func); end, + -- Garden + { + eligible = function(rm) return rm.lit == true; end, + contents = function(rm) + local s = selection.room(); + local npts = (s:numpoints() / 6); + for i = 1, npts do + des.monster({ id = "wood nymph", asleep = true }); + if (percent(30)) then + des.feature("fountain"); + end + end + 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 - buried_treasure = { x = xobj.ox, y = xobj.oy }; + table.insert(postprocess, { handler = make_dig_engraving, data = { x = xobj.ox, y = xobj.oy }}); end for i = 1, d(3,4) do des.object(); @@ -194,6 +222,19 @@ themeroom_fills = { 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 } }); + end + end + end, }; ------------------------------- HELPER FUNCTIONS ------------------------------- @@ -365,7 +406,7 @@ themerooms = { }); end, - -- Random dungeon feature in the middle of a odd-sized room + -- 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); @@ -654,12 +695,41 @@ xx|.....|xx }----} }}}}}}]], 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" }; - des.object("chest", 2, 2); - des.object("chest", 3, 2); - des.object("chest", 2, 3); - des.object("chest", 3, 3); + 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); + + 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 } }); way_out_method(false); end }); end, @@ -1842,30 +1912,11 @@ end -- called before any rooms are generated function pre_themerooms_generate() - -- reset the buried treasure location - buried_treasure = { }; end -- called after all rooms have been generated +-- but before creating connecting corridors/doors, or filling rooms function post_themerooms_generate() - if (buried_treasure.x ~= nil) then - local floors = selection.negate():filter_mapchar("."); - local pos = floors:rndcoord(0); - local tx = buried_treasure.x - pos.x - 1; - local ty = buried_treasure.y - pos.y; - local dig = ""; - if (tx == 0 and ty == 0) then - dig = " here"; - else - if (tx < 0 or tx > 0) then - dig = string.format(" %i %s", math.abs(tx), (tx > 0) and "east" or "west"); - end - if (ty < 0 or ty > 0) then - dig = dig .. string.format(" %i %s", math.abs(ty), (ty > 0) and "south" or "north"); - end - end - des.engraving({ coord = pos, type = "burn", text = "Dig" .. dig }); - end end function themeroom_fill(rm) @@ -1897,3 +1948,47 @@ function themeroom_fill(rm) end end +-- postprocess callback: create an engraving pointing at a location +function make_dig_engraving(data) + local floors = selection.negate():filter_mapchar("."); + local pos = floors:rndcoord(0); + local tx = data.x - pos.x - 1; + local ty = data.y - pos.y; + local dig = ""; + if (tx == 0 and ty == 0) then + dig = " here"; + else + if (tx < 0 or tx > 0) then + dig = string.format(" %i %s", math.abs(tx), (tx > 0) and "east" or "west"); + end + if (ty < 0 or ty > 0) then + dig = dig .. string.format(" %i %s", math.abs(ty), (ty > 0) and "south" or "north"); + end + end + des.engraving({ coord = pos, type = "burn", text = "Dig" .. dig }); +end + +-- postprocess callback: turn room walls into trees +function make_garden_walls(data) + local sel = data.sel:grow(); + des.replace_terrain({ selection = sel, fromterrain="w", toterrain = "T" }); +end + +-- postprocess callback: make a trap +function make_a_trap(data) + if (data.teledest == 1 and data.type == "teleport") then + local locs = selection.negate():filter_mapchar("."); + repeat + data.teledest = locs:rndcoord(1); + until (data.teledest.x ~= data.coord.x and data.teledest.y ~= data.coord.y); + end + des.trap(data); +end + +-- called once after the whole level has been generated +function post_level_generate() + for i, v in ipairs(postprocess) do + v.handler(v.data); + end + postprocess = { }; +end diff --git a/dat/tower1.lua b/dat/tower1.lua index 01a697f343..8f6bd65294 100644 --- a/dat/tower1.lua +++ b/dat/tower1.lua @@ -1,4 +1,4 @@ --- NetHack tower tower1.lua $NHDT-Date: 1652196037 2022/05/10 15:20:37 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1 $ +-- NetHack tower tower1.lua $NHDT-Date: 1717178759 2024/05/31 18:05:59 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.3 $ -- Copyright (c) 1989 by Jean-Christophe Collet -- NetHack may be freely redistributed. See license for details. -- @@ -30,9 +30,21 @@ des.monster("Vlad the Impaler", 06, 05) des.monster("V",niches[1]) des.monster("V",niches[2]) des.monster("V",niches[3]) -des.monster("V",niches[4]) -des.monster("V",niches[5]) -des.monster("V",niches[6]) +-- The brides; they weren't named in Bram Stoker's original _Dracula_ +-- and when appearing in umpteen subsequent books and movies there is +-- no consensus for their names. According to the Wikipedia entry for +-- "Brides of Dracula", the "Czechoslovakian TV film Hrabe Drakula (1971)" +-- 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. +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/tower2.lua b/dat/tower2.lua index f458e29062..708a594669 100644 --- a/dat/tower2.lua +++ b/dat/tower2.lua @@ -57,7 +57,17 @@ if percent(60) then des.object("crystal plate mail",place[8]) end if percent(60) then - des.object("spellbook of invisibility",place[9]) + local spbooks = { + "spellbook of invisibility", + "spellbook of cone of cold", + "spellbook of create familiar", + "spellbook of clairvoyance", + "spellbook of charm monster", + "spellbook of stone to flesh", + "spellbook of polymorph" + } + shuffle(spbooks); + des.object(spbooks[1],place[9]) end -- Walls in the tower are non diggable des.non_diggable(selection.area(00,00,14,10)) diff --git a/dat/tribute b/dat/tribute index f05076019d..e8b34a8ec5 100644 --- a/dat/tribute +++ b/dat/tribute @@ -1,4 +1,4 @@ -# NetHack 3.7 tribute $NHDT-Date: 1626142354 2021/07/13 02:12:34 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.108 $ +# NetHack 3.7 tribute $NHDT-Date: 1726809275 2024/09/20 05:14:35 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.118 $ # Copyright (c) 2017 by Robert Patrick Rankin # NetHack may be freely redistributed. See license for details. # A tribute introduced in NetHack 3.6.0 to: @@ -1817,7 +1817,7 @@ yards away rather than the innocent bystander at whom it was aimed. %e passage # p. 26 (first and second paragraphs are actually end of one section, # start of next one; first 'Thunder rolled...' had three dot -# ellipsis, second has four, elipsis plus final period-- +# ellipsis, second has four, ellipsis plus final period-- # first changed to four here so that they match) %passage 3 Thunder rolled.... @@ -3337,7 +3337,7 @@ the impossibility of reality, and so on: what this means is that they have got hold of something hot and are gabbling the physics as they go along. (1) It was here that the thaum, hitherto believed to be the smallest -possible particle of magic, was successfully demonstrated to made up of +possible particle of magic, was successfully demonstrated to be made up of /resons/(2) or reality fragments. Currently research indicates that each reson is itself made up of a combination of at least five "flavors," known as "up," "down," "sideways," "sex appeal," and "peppermint." @@ -4198,7 +4198,7 @@ Cheese with Chunky Pickle, I think." THEY DO SUCH MARVELOUS THINGS WITH SANDWICHES THESE DAYS. -"Oh... and Bacon Surpise." +"Oh... and Bacon Surprise." REALLY? WHAT IS SO SURPRISING ABOUT BACON? @@ -5789,7 +5789,7 @@ saw what happened to the others! /You/ got your fingers burned!." # # 4 new passages added for 3.7 # -# p. 24 (elipsis occurs at start of paragraph; Quite Reverend Oats has opened +# p. 24 (ellipsis occurs at start of paragraph; Quite Reverend Oats has opened # /The Book of Om/ at random and is reading the passage found there) %passage 9 "... silence is an answer that begs three more questions. Seek and you will @@ -6934,7 +6934,7 @@ merely didn't know where everything else was. [The Amazing Maurice and His Educated Rodents, by Terry Pratchett] %e passage -# pp. 298-300 (Keith has challanged the professional rat piper and offered +# pp. 298-300 (Keith has challenged the professional rat piper and offered # to rid the town of rats for a much lower price; Sardines is # one of the Educated Rodents, known for dancing all the time; # "hwun/two/three/four/" is run-together "one /two/ three /four/"; @@ -7512,7 +7512,7 @@ very thought of written-down things. "They think written words are even more powerful," whispered the toad. "They think all writing is magic. Words worry them. See their swords? -They glow in the presense of lawyers." +They glow in the presence of lawyers." "All /right/," said Tiffany. "We're getting somewhere. I promise not to write his name down. [...]" @@ -7788,7 +7788,7 @@ Cradle and the House and the Flock and the Three Old Ladies, One With a Squint, Carrying the Bucket of Fish to Market When They Meet the Donkey, although you need two people for that one, and I only ever did it once, and Betsy Tupper scratched her nose at the wrong moment and I had to get -some scissors to to cut her loose..." +some scissors to cut her loose..." [A Hat Full of Sky, by Terry Pratchett] %e passage diff --git a/dat/tut-1.lua b/dat/tut-1.lua index cd18c3e207..0d56fa7c67 100644 --- a/dat/tut-1.lua +++ b/dat/tut-1.lua @@ -1,4 +1,31 @@ +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"); @@ -40,15 +67,15 @@ 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 }); @@ -59,7 +86,7 @@ des.engraving({ coord = { 2,4 }, type = "engrave", text = "Some actions may requ 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,12 +96,18 @@ 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" }); +-- 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 }); -- @@ -98,17 +131,17 @@ end 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 "gloves" or "light 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 }); @@ -129,13 +162,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 }); -- @@ -155,14 +188,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 }); @@ -174,35 +207,35 @@ 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 = { 42,6 }, id = "large box", broken = true, trapped = false, contents = function(obj) des.object({ id = "secret door detection", class = "/", spe = 30 }); end }); -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 }); -- @@ -210,23 +243,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" }); @@ -234,30 +266,34 @@ 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 }); ---------------- -nh.callback("cmd_before", "tutorial_cmd_before"); -nh.callback("level_enter", "tutorial_enter"); -nh.callback("level_leave", "tutorial_leave"); -nh.callback("end_turn", "tutorial_turn"); +-- entering and leaving tutorial _branch_ now handled by core +-- // nh.callback("cmd_before", "tutorial_cmd_before"); +-- // nh.callback("level_enter", "tutorial_enter"); +-- // nh.callback("level_leave", "tutorial_leave"); +-- // nh.callback("end_turn", "tutorial_turn"); ---------------- diff --git a/dat/usagehlp b/dat/usagehlp index 27b0c431f7..cfa8793516 100644 --- a/dat/usagehlp +++ b/dat/usagehlp @@ -61,8 +61,10 @@ nethack --windowtype:Iii designation itself may be any case; variations '--windowtype Iii' and '-w Iii' work too. - On Windows, nethack.exe might support both tty and curses; nethackW.exe - supports mswin only. MS-DOS is tty only. + On Windows, nethack.exe supports tty or curses or both depending on + settings at the time the program is built from source; nethackW.exe + supports mswin (also referred to as Win GUI) and optionally curses. + For MS-DOS, the program supports tty or curses or both. nethack -n don't show the 'news' file if one is present in nethack's directory. @@ -110,13 +112,15 @@ nethack -dDir -s nethack --directory:Dir -s as above; alternate directory, if specified, should come first. -nethack --version -nethack --version:paste - '--version' display the program's version number and exit; - '--version:paste' display version number and also copy it into system +nethack --version or --version:copy or --version:dump or --version:show + '--version' display the program's version number plus the date and + time it was built from source code, then exit; + '--version:copy' display version number and also copy it into system pasteboard (should work on macOS and Windows; might not work on other - systems) so that it could be copied from there into a subsequent email - or web contact form, then exit. + systems) so that it could be pasted from there into a subsequent email + or web contact form, then exit; + '--version:dump' display several internal values, then exit; + '--version:show' same as '--version'. nethack --showpaths list expected locations for various files and directories, then exit; diff --git a/doc/.gitattributes b/doc/.gitattributes index 19574823a6..1d1afaeab6 100644 --- a/doc/.gitattributes +++ b/doc/.gitattributes @@ -1,8 +1,8 @@ *.mn NHSUBST +*.tex NHSUBST *.6 NHSUBST *.7 NHSUBST fixes* NHSUBST -window.txt NHSUBST config.nh NHSUBST Guidebook.txt NH_header=no tmac.n NH_header=no @@ -11,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/Gbk-1pg-sfx.mn b/doc/Gbk-1pg-sfx.mn index d00b37f5fa..9008ac440e 100644 --- a/doc/Gbk-1pg-sfx.mn +++ b/doc/Gbk-1pg-sfx.mn @@ -1,8 +1,3 @@ .\" suffux to Guidebook.mn for 'roff version of Guidebook written as one page .br -.\" add a marker that precedes hundreds of blank lines which pad out the page -EOF--EOF -. -.\" post process with "sed -e '/EOF--EOF/,12345D'" to remove trailing junk -.\" (also removes Unix trademark footnote; that ought to be kept but the -.\" generic "all trademarks are held by respective owners" is still present) +.pl \n(nlu diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 2a88a0261e..07e9b46401 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -1,4 +1,4 @@ -.\" $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.464 $ $NHDT-Date: 1655461129 2022/06/17 10:18:49 $ +.\" $NHDT-Branch: master $:$NHDT-Revision: 1.596 $ $NHDT-Date: 1735103892 2024/12/25 00:18:12 $ .\" .\" This is an excerpt from the 'roff' man page from the 'groff' package. .\"+-- @@ -25,19 +25,29 @@ .\" with an optional filtering step between 'cat' and 'tbl': .\" cat... |../util/makedefs --grep --input - --output - |tbl... . -.so tmac.nh \" extra macros which aren't in tmac.n +.if n \{\ +.po 5n +.ll 70n +.lt 70n +.\} +. +.so tmac.nh \" extra macros which aren't in tmac.n +.if !\n(nH .so doc/tmac.nh . -.\" \n(fF=1: fixed-width font (Guidebook.txt), =0: proportional-width font -.nr fF (\w'f'p)=(\w'F'p) \" compare width of 'f' to that of 'F' +.\" building Guidebook.txt doesn't have CR font available; groff 1.23 issues +.\" a warning each time any font can't be loaded; earlier versions silently +.\" proceeded so lack of CR (fixed-width, moot for plain text) didn't matter +.if n .do ftr CR R \" if formatting for a terminal, substitute R for CR +. \" (the substitution affects both '\f' and '.ft') . -.ds h0 "NetHack Guidebook -.ds h1 +.ds h0 NetHack Guidebook +.ds h1 \" empty .ds h2 % -.ds vr "NetHack 3.7.0 -.ds f0 "\*(vr -.ds f1 -.\"DO NOT REMOVE NH_DATESUB .ds f2 "DATE(%B %-d, %Y) -.ds f2 "May 7, 2023 +.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 December 25, 2024 . .\" A note on some special characters: .\" \(lq = left double quote @@ -45,9 +55,11 @@ .\" \(oq = left (open) single quote .\" \(cq = right (close) single quote \(aq = apostrophe / single quote .\" - = hyphen \- = minus sign -.\" \(em = M-sized dash +.\" \(rs = backslash \(em = dash as wide as 'M' is tall .\" ^ = small circumflex punctuation \(ha = larger circumflex character .\" ~ = small tilde punctuation \(ti = larger tilde character +.\" +.\" See groff_man_style(7) or groff_char(7) for more on these. . . .\" finally, start the actual Guidebook... @@ -246,16 +258,16 @@ The way the screen looks for you depends on your platform. .\" situations. Originally the character had only reached a second room .\" (unchanged here) by turn 257 (now changed to 752) and was already .\" Weak from hunger (now changed to just Hungry) and also lacked any of -\." Tourist's starting gold. Confusion is added to include a condition.) +.\" Tourist's starting gold. Confusion is added to include a +.\" condition.) .\" -.\" Width is constrained by the margins of plain text output (Guidebook.txt). -.\" Text output shows an extra space in front of the right hand boundary box -.\" which isn't present in this data nor in the Postscript output. It also -.\" overwrites the label on the bottom boundary box line so that ends ups as -.\" ------Figure-N------. Odd but acceptable. +.\" Width is constrained by the margins of plain text output +.\" (Guidebook.txt). Text output might show an extra space inboard of +.\" the left and right box boundaries that does not appear in PostScript +.\" output. This behavior is specific to GNU tbl. .BR 2 .ft CR \" set font to constant-width Roman -.TS S +.TS center box tab(~); L. The bat bites! @@ -272,22 +284,24 @@ The bat bites! Player the Rambler St:12 Dx:7 Co:18 In:11 Wi:9 Ch:15 Neutral Dlvl:1 $:993 HP:9(12) Pw:3(3) AC:10 Exp:1/19 T:752 Hungry Conf .TE -.ft \" revert to previous font +.ft R .ce 1 +.if t .sp 0.5v Figure 1 . -.\" Figure 2 uses trailing spaces to force the same width as Figure 1. .BR 1 .ft CR -.TS S +.TS center box tab(~); L. -Player the Rambler St:12 Dx:7 Co:18 In:11 Wi:9 Ch:15 \" -Neutral $:993 HP:9(12) Pw:3(3) AC:10 Exp:1/19 Hungry \" -Dlvl:1 T:752 Conf \" +.\" Use trailing spaces to force the same width as Figure 1. +Player the Rambler St:12 Dx:7 Co:18 In:11 Wi:9 Ch:15 \& +Neutral $:993 HP:9(12) Pw:3(3) AC:10 Exp:1/19 Hungry \& +Dlvl:1 T:752 Conf \& .TE -.ft +.ft R .ce 1 +.if t .sp 0.5v Figure 2 . .hn 2 @@ -396,7 +410,7 @@ Not shown when \fBNormal\fP. .lp "" Encumbrance: an indication of how what you are carrying affects your ability to move. -Values are \fBUnencumbered\fP, \fBEncumbered\fP, \fBStressed\fP, +Values are \fBUnencumbered\fP, \fBBurdened\fP, \fBStressed\fP, \fBStrained\fP, \fBOvertaxed\fP, and \fBOverloaded\fP. Not shown when \fBUnencumbered\fP. .lp "" @@ -416,13 +430,15 @@ Movement modifiers: .lp "" Other conditions and modifiers exist, but there isn't enough room to display them with the other status fields. +.\" Don't give the next paragraph a first-line indent. +.nr @p \n(pi \" Save mn's paragraph indentation. +.nr pi 0 .pg -.in -5n .\" outdent this paragraph The \f(CR#attributes\fP command (default key \f(CR\(haX\fP) 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. -.in 0 .\" reset indentation +.nr pi \n(@p \" Restore mn's paragraph indentation. .hn 2 The message line (top) .pg @@ -442,69 +458,89 @@ option. The map (rest of the screen) .pg 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 list of what the -default symbols mean: -.lp "\\- and | " -The walls of a room, or an open door. Or a grave (|). -.lp . -The floor of a room, ice, or a doorless doorway. -.lp # -A corridor, or iron bars, or a tree, or possibly a kitchen sink (if -your dungeon has sinks), or a drawbridge. -.lp > +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 list of what the default symbols mean: +.lp \f(CR\\-\fP +The horizontal or corner walls of a room, or an open east/west door. +.lp \f(CR|\fP +The vertical walls of a room, or an open north/south door, or a grave. +.lp \f(CR.\fP +The floor of a room, or ice, or a doorless doorway, or the span of an +open drawbridge. +.lp \f(CR#\fP +A corridor, or iron bars, or a tree, or the portcullis of a closed +drawbridge. +.lp "" +Note: engravings in corridors also appear as \f(CR#\fP but are shown in +a different color from normal corridor locations. +.lp \f(CR>\fP Stairs down: a way to the next level. -.lp < +.lp \f(CR<\fP Stairs up: a way to the previous level. -.lp + +.lp \f(CR+\fP A closed door, or a spellbook containing a spell you may be able to learn. -.lp @ -Your character or a human. -.lp $ +.lp \f(CR@\fP +Your character or a human or an elf. +.lp \f(CR$\fP A pile of gold. -.lp \(ha \" ^ +.lp \f(CR\(ha\fP \" \(ha == 'hat' == full sized caret ^ A trap (once you have detected it). -.lp ) +.lp \f(CR)\fP A weapon. -.lp [ +.lp \f(CR[\fP A suit or piece of armor. -.lp % +.lp \f(CR%\fP Something edible (not necessarily healthy). -.lp ? +.lp \f(CR?\fP A scroll. -.lp / +.lp \f(CR/\fP A wand. -.lp = +.lp \f(CR=\fP A ring. -.lp ! +.lp \f(CR!\fP A potion. -.lp ( +.lp \f(CR(\fP A useful item (pick-axe, key, lamp...). -.lp \(dq \" \(dq == double quote +.lp \f(CR\(dq\fP \" \(dq == double quote An amulet or a spider web. -.lp * +.lp \f(CR*\fP A gem or rock (possibly valuable, possibly worthless). -.lp \` -A boulder or statue. -.lp 0 +.lp \f(CR\`\fP +A boulder or statue or an engraving on the floor of a room. +.lp "" +Note: statues are displayed as if they were the monsters they depict +so won't appear as a \fIgrave accent\fP (aka \fIback-tick\fP). +.lp \f(CR0\fP An iron ball. -.lp _ +.lp \f(CR_\fP An altar, or an iron chain. -.lp { -A fountain. -.lp } -A pool of water or moat or a pool of lava. -.lp \\\\ +.lp \f(CR{\fP +A fountain or a sink. +.lp \f(CR}\fP +A pool of water or moat or a wall of water +or a pool of lava or a wall of lava. +.lp \f(CR\\\\\fP An opulent throne. -.lp "a-zA-Z and other symbols" +.lp "\f(CRa\fP-\f(CRz\fP\ \ \fIand\fP" +.lp "\f(CRA\fP-\f(CRH\fP\f(CRJ\fP-\f(CRZ\fP\ \ \fIand\fP" +.lp "\f(CR@&\(aq:;\fP" \" \(aq == apostrophe / single quote Letters and certain other symbols represent the various inhabitants -of the Mazes of Menace. Watch out, they can be nasty and vicious. +of the Mazes of Menace. +Watch out, they can be nasty and vicious. Sometimes, however, they can be helpful. -.lp I -This marks the last known location of an invisible or otherwise unseen -monster. Note that the monster could have moved. -The \(oqF\(cq and \(oqm\(cq commands may be useful here. +.lp \f(CRI\fP +Rather than a specific type of monster, this marks the last known +location of an invisible or otherwise unseen monster. +Note that the monster could have moved. +The \(oqs\(cq, \(oqF\(cq, and \(oqm\(cq commands may be useful here. +.lp \f(CR1\fP-\f(CR5\fP +The digits 1 through 5 may be displayed, marking unseen monsters sensed +via the \fIWarning\fP attribute. +Less dangerous monsters are indicated by lower values, more dangerous by +higher values. .pg You need not memorize all these symbols; you can ask the game what any symbol represents with the \(oq/\(cq command (see the next section for @@ -610,30 +646,23 @@ If you sense or remember a monster there, you will fight the monster instead. Only these one-step movement commands cause you to fight monsters; the others (below) are \(lqsafe.\(rq -.SD n -.\" note: the two number pad layouts are separated by a tab on each line; -.\" [that's out of date; it's now tab,backslash,space,tab] -.\" converting those tabs to spaces will screw up the formatting; -.\" the '(if ... set)' line starts with -.\" and is intended to line up with the right hand layout rather -.\" than be centered. Second column forces the label to be evenly -.\" spaced between first and third; Phantom fourth column forces -.\" the whole to shift left since fully centered doesn't look good. -.\" "Figure 3 " includes a trailing space to shift left a little. .ft CR -.TS S -center; -ce c ce ce. -y k u \ 7 8 9\ \" -\\ | / \ \\ | /\ \" -h\- . \-l \ 4\- . \-6\ \" -/ | \\ \ / | \\\ \" -b j n \ 1 2 3\ \" -\fR\ \ (if \fBnumber_pad\fR is set)\ \" -\fR\ Figure 3\ \ \" +.tr -\-@\(rs +.TS +box center expand; +C C. +y k u 7 8 9 +@ | / @ | / +h- . -l 4- . -6 +/ | @ / | @ +b j n 1 2 3 +\fR(\fBnumber_pad\fP off) \fR(\fBnumber_pad\fP on) .TE -.ft -.ED +.tr --@@ +.ft R +.if t .sp 0.5v +.ce 1 +Figure 3 . .lp [YUHJKLBN] Go in that direction until you hit a wall or run into something. @@ -924,7 +953,7 @@ If you're wearing more than one, you'll be prompted for which one to remove. When you're only wearing one, then by default it will be removed without asking, but you can set the -.op paranoid_confirmation +.op paranoid_confirmation:Remove option to require a prompt. .lp "" This command may also be used to take off armor. @@ -979,7 +1008,7 @@ and/or a shirt, or a suit covering a shirt, as if the underlying items weren't there.) When you're only wearing one, then by default it will be taken off without asking, but you can set the -.op paranoid_confirmation +.op paranoid_confirmation:Remove option to require a prompt. .lp "" This command may also be used to remove accessories. @@ -1222,6 +1251,10 @@ Toggle the .op autopickup option on/off. Default key is \(oq@\(cq. +.lp "#bugreport" +Bring up a browser window to submit a report to the NetHack Development Team. +Can be disabled at the time the program is built; when enabled, +CRASHREPORTURL must be set in the system configuration file. .lp "#call " Call (name) a monster, or an object in inventory, on the floor, or in the discoveries list, or add an annotation for the @@ -1380,6 +1413,8 @@ Debug mode only. .lp "#look " Look at what is here, under you. Default key is \(oq:\(cq. +.lp #lookaround +Describe what you can see, or remember, of your surroundings. .lp "#loot " Loot a box or bag on the floor beneath you, or the saddle from a steed standing next to you. @@ -1570,21 +1605,44 @@ Default key is \(oqs\(cq. .lp "#seeall " Show all equipment in use. Default key is \(oq*\(cq. +.lp "" +Will display in-use items in a menu even when there is only one. .lp #seeamulet Show the amulet currently worn. Default key is \(oq\(dq\(cq. \" double quote +.lp "" +Using the \(oq\f(CRm\fP\(cq prefix will force the display of a worn +amulet in a menu rather than with just a message. .lp #seearmor Show the armor currently worn. Default key is \(oq[\(cq. +.lp "" +Will display worn armor in a menu even when there is only thing worn. .lp #seerings Show the ring(s) currently worn. Default key is \(oq=\(cq. +.lp "" +Will display worn rings in a menu if there are two (or there is +just one and is a meat ring rather than a \(lqreal\(rq ring). +Use the \(oq\f(CRm\fP\(cq prefix to force a menu for one ring. .lp #seetools Show the tools currently in use. Default key is \(oq(\(cq. +.lp "" +Will display the result in a message if there is one tool in use (worn +blindfold or towel or lenses, lit lamp(s) and/or candle(s), leashes +attached to pets). +Will display a menu if there are more than one or if the command is +preceded by the \(oq\f(CRm\fP\(cq prefix. .lp #seeweapon Show the weapon currently wielded. Default key is \(oq)\(cq. +.lp "" +If dual-wielding, a separate message about the secondary weapon will be +given. +Using the \(oq\f(CRm\fP\(cq prefix will force a menu and it will include +primary weapon, alternate weapon even when not dual-wielding, and also +whatever is currently assigned to the quiver slot. .lp "#shell " Do a shell escape, switching from NetHack to a subprocess. Can be disabled at the time the program is built. @@ -1641,6 +1699,9 @@ 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. .lp "" +If there are visible clouds of gas in view, they are treated like traps +when deciding whether to show them or the floor underneath them. +.lp "" In explore mode, you can choose to view the full map rather than just its explored portion. In debug mode there are additional choices. @@ -2328,7 +2389,7 @@ moving into its location, you'll be asked to confirm your intent. By default an answer of \(oqy\(cq acknowledges that intent, which can be error prone if you're using \(oqy\(cq to move. You can set the -.op paranoid_confirmation +.op paranoid_confirmation:attack option to require a response of \(lqyes\(rq instead. .pg If you can't see a monster (if it is invisible, or if you are blinded), @@ -2765,17 +2826,17 @@ Here is a list of the armor class values provided by suits of armor: .\" it to get the second column (number) closer to the text on most lines. .\" Both AC4 and AC7 have trailing tab plus empty comment on their first .\" line and leading spaces to indent their second line. -.TS S +.TS center; -a n. +l r. Dragon scale mail 1 Plate mail, Crystal plate mail 3 -Bronze plate mail, Splint mail, \" -\ \ \ Banded mail, Dwarvish mithril-coat 4 +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 +Ring mail, Studded leather armor, \& + Dragon scales 7 Leather armor, Orcish ring mail 8 Leather jacket 9 none 10 @@ -3045,6 +3106,30 @@ 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 \(lq#untrap\(rq extended command. +.pg +When the contents of a container are known, that container will be +described as something like \(lqa sack containing 3 items\(rq. +In this example, the 3 refers to number of \fIstacks\fP 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). +.pg +If a chest or large box is described as \(lqbroken\(rq, that means that it +can't be locked rather than that it no longer functions as a container. +.pg +The \fIapply\fP and \fIloot\fP 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 +\(lq#tip\(rq 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 +contents into another container. +(As of this writing, the other container must be carried rather than on +the floor.) .hn 2 Amulets (\(oq"\(cq) .pg @@ -3308,85 +3393,47 @@ it because they fall into the same category of \(lqbragging rights\(rq 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. -.\" Vary the output between Guidebook.txt and Guidebook.{ps,pdf} -.ie \n(fF \{\ -.\" fixed-width font: default key width is fine; -.\" display longest entries (below) across two lines for Guidebook.txt -.PS "Mines'\~End" -.\} -.el \{\ -.\" proportional font: force blank line, indent, and use slightly wider key .sp -.in +5n -.PS "Mines'\~End\~" -.\} -.fi -.PL "" -Attained rank title . -.PL Shop -Entered a shop. -.PL Temple -Entered a temple. -.PL Mines -Entered the Gnomish Mines. -.PL Town -Entered Mine Town. -.PL Oracle -Consulted the Oracle of Delphi. -.PL Novel -Read a passage from a Discworld Novel. -.PL Sokoban -Entered Sokoban. -.PL "Big\~Room" -Entered the Big Room. -.ie \n(fF \{\ -.PL "Soko-Prize" -Explored to the top of Sokoban -.br -and found a special item there. -.PL "Mines'\~End" -Explored to the bottom of the Gnomish Mines -.br -and found a special item there. -.\} -.el \{\ -.PL "Soko-Prize" +.TS +center; +L Lz2 L. +\fIRank\fP \- Attained rank title \fIRank\fP. +Shop \- Entered a shop. +Temple \- Entered a temple. +Mines \- Entered the Gnomish Mines. +Town \- Entered Mine Town. +Oracle \- Consulted the Oracle of Delphi. +Novel \- Read a passage from a Discworld Novel. +Sokoban \- Entered Sokoban. +Big Room \- Entered the Big Room. +Soko-Prize \- T{ Explored to the top of Sokoban and found a special item there. -.PL "Mines'\~End" -Explored to the bottom of the Gnomish Mines and found a special item there. -.\} -.fi -.PL Medusa -Defeated Medusa. -.PL Tune +T} +Mines' End \- T{ +Explored to the bottom of the Gnomish Mines and found a special item +there. +T} +Medusa \- Defeated Medusa. +Tune \- T{ Discovered the tune that can be used to open and close the drawbridge on the Castle level. -.PL Bell -Acquired the Bell of Opening. -.PL Gehennom -Entered Gehennom. -.PL Candle -Acquired the Candelabrum of Invocation. -.PL Book -Acquired the Book of the Dead. -.PL Invocation +T} +Bell \- Acquired the Bell of Opening. +Gehennom \- Entered Gehennom. +Candle \- Acquired the Candelabrum of Invocation. +Book \- Acquired the Book of the Dead. +Invocation \- T{ Gained access to the bottommost level of Gehennom. -.PL Amulet -Acquired the fabled Amulet of Yendor. -.PL Endgame -Reached the Elemental Planes. -.PL Astral -Reached the Astral Plane level. -.PL Blind -Blind from birth. -.PL Deaf -Deaf from birth. -.PL Nudist -Never wore any armor. -.PL Ascended -Delivered the Amulet to its final destination. -.PE -.if !\n(fF .in -5n .fi \" undo proportional-width font-specific indentation +T} +Amulet \- Acquired the fabled Amulet of Yendor. +Endgame \- Reached the Elemental Planes. +Astral \- Reached the Astral Plane level. +Blind \- Blind from birth. +Deaf \- Deaf from birth. +Nudist \- Never wore any armor. +Pauper \- Started out with no possessions. +Ascended \- Delivered the Amulet to its final destination. +.TE .sp .lp "Notes: " .pg @@ -3419,7 +3466,8 @@ instrument played closely enough\(embut not too close!\(emto the Castle level's drawbridge or can be given to you via prayer boon. .pg -\fIBlind\fP, \fIDeaf\fP, and \fINudist\fP are also conducts, and they can only be +\fIBlind\fP, \fIDeaf\fP, \fINudist\fP, +and \fIPauper\fP are also conducts, and they can only be enabled by setting the correspondingly named option in XNETHACKOPTIONS or run-time configuration file prior to game start. In the case of \fIBlind\fP and \fIDeaf\fP, the option also enforces the conduct. @@ -3464,7 +3512,7 @@ The file may not exist, but it is a normal ASCII text file can can be created with any text editor. After running xNetHack for the first time, you should find a default -template for the configuration file named \(lq.xnethackrc.template\(rq +template for the configuration file named \(lqxnethackrc.template\(rq in \(lq%USERPROFILE%\\xNetHack\\\(rq. If you have not created the configuration file, xNetHack will create one for you using the default template file. @@ -3592,7 +3640,7 @@ See the \(lqConfiguring Menu Colors\(rq section. Change the way messages are shown in the top status line. See the \(lqConfiguring Message Types\(rq section. .lp ROGUESYMBOLS -Custom symbols for for the rogue level's symbol set. +Custom symbols for the rogue level's symbol set. See \fISYMBOLS\fP below. .lp SOUND Define a sound mapping. @@ -3624,13 +3672,12 @@ Example: .pg \ \" dummy paragraph to force some separation [.BR isn't working as intended] .pg -.SD n \" suppress indentation +.SD n \" begin display without indentation Here is an example of configuration file contents: .ED .\" [conditional indentation; see description of XNETHACKOPTIONS below] -.ds sD i \" assume proportional, indentation acceptable and preferred -.\" Check for fixed-width font; fF set up at top of file -.if \n(fF .ds sD n \" if same width, suppress indentation +.ds sD i \" typesetter; display indentation acceptable and preferred +.if n .ds sD n \" terminal; suppress indentation .SD \*(sD \" string variable sD will expand to either 'i' or 'n' .ft CR \" set font to constant-width Roman # Set your character's role, race, gender, and alignment. @@ -3676,12 +3723,8 @@ you would enter the command .\" but the 'setenv' example is too wide for Guidebook.txt unless the .\" indentation is suppressed (.SD n). Even though the second example .\" can be indented, it should match the first or they'll both look odd. -.\" groff has a built-in register allowing recognition of '-T', but we -.\" can't rely on that. Assume Guidebook.ps uses a proportional font -.\" and Guidebook.txt a fixed-width one and test which sort we're using. -.ds sD i \" assume proportional, indentation acceptable and preferred -.\" Check for fixed-width font; fF set up at top of file -.if \n(fF .ds sD n \" if same width, suppress indentation +.ds sD i \" typesetter; display indentation acceptable and preferred +.if n .ds sD n \" terminal; suppress indentation .SD \*(sD \f(CR% setenv XNETHACKOPTIONS "color,\\!leg,name:Blue Meanie,fruit:lime"\fP .ED @@ -3722,6 +3765,8 @@ applies only to new games. .\" Ones less that 8 should be enclosed within double quotes and padded with .\" trailing spaces. . +.lp accessiblemsg +Add location or direction information to messages (default is off). .lp acoustics Enable messages about what your character hears (default on). Note that this has nothing to do with your computer's audio capabilities. @@ -3844,6 +3889,13 @@ Show out-of-sight areas of lit rooms (default on). Persistent. .lp "deaf " Start the character permanently deaf (default false). Persistent. +.lp dropped_nopick +If this option is on, items you dropped will not be automatically +picked up, even if +.op autopickup +is also on and they are in +.op pickup_types +or match a positive autopickup exception (default on). Persistent. .lp disclose Controls what information the program reveals when the game ends. Value is a space separated list of prompting/category pairs @@ -3982,7 +4034,7 @@ The behavior of this option depends on the type of windowing you use. In text windowing, text highlighting or inverse video is often used; with tiles, generally displays a heart symbol near pets. .lp "" -With the curses interface, the +With the tty or curses interface, the .op petattr option controls how to highlight pets and setting it will turn the .op hilite_pet @@ -3994,8 +4046,29 @@ In text windowing, text highlighting or inverse video is often used; with tiles, generally displays a small plus-symbol beside the object on the top of the pile. .lp hitpointbar -Show a hit point bar graph behind your name and title. -Only available for TTY and Windows GUI, and only when statushilites is on. +Show a hit point bar graph behind your name and title in the status +display (default off). +.lp "" +The \(lqcurses\(rq interface supports it even if the status highlighting +feature has been disabled when building the program. +The \(lqtty\(rq and \(lqmswin\(rq (aka \(lqWindows GUI\(rq) 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 \fIinverse\fP. +For tty and curses, \fIblink\fP will also be used if the current +hitpoint value is at or below the \fIcritical HP\fP threshold. +.lp "" +The \(lqQt\(rq interface also supports hitpointbar, by drawing +a solid bar above the name and title with a hard-coded color scheme. +(As of this writing, having the bar enabled unintentionally inhibits +resizing the status panel. +To resize that, use the \f(CR#optionsfull\fP command to toggle the +\fIhitpointbar\fP option off, perform the resize while it's off, then +use the same command to toggle it back on.) .lp horsename Name your starting horse (for example \(lqhorsename:Trigger\(rq). Cannot be set with the \(oq\f(CRO\fP\(cq command. @@ -4033,6 +4106,8 @@ fountains, or altars which are ordinarily only described when covered by one or more objects (default off). Cannot be set with the \(oq\f(CRO\fP\(cq command. Persistent. +.lp mention_map +Give feedback when interesting map locations change (default off). .lp mention_walls Give feedback when walking against a wall (default off). Persistent. @@ -4040,13 +4115,14 @@ Persistent. Enable coloring menu lines (default off). See \(lqConfiguring Menu Colors\(rq on how to configure the colors. .lp menustyle -Controls the interface used when you need to choose various objects (in -response to the Drop command, for instance). +Controls the method used when you need to choose various objects (in +response to the \f(CRDrop\fP (aka \f(CRdroptype\fP) command, for instance). The value specified should be the first letter of one of the following: traditional, combination, full, or partial. +Default is \f(CRfull\fP. Persistent. .lp "" -Traditional was the only interface available for very +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). @@ -4056,6 +4132,9 @@ prompting one-by-one. Full displays a menu of object classes rather than a character prompt, and then a menu of matching objects for selection. +(Choosing its \(oqA\(cq (Autoselect-All) choice skips the second menu. +To avoid choosing that by accident, +set \f(CRparanoid_confirm:AutoAll\fP to require confirmation.) Partial skips the object class filtering and immediately displays a menu of all objects. .lp menu_deselect_all @@ -4069,8 +4148,8 @@ Key to jump to the first page in a menu. Default \(oq\(ha\(cq. .lp menu_headings Controls how the headings in a menu are highlighted. -Values are \(lqnone\(rq, \(lqbold\(rq, \(lqdim\(rq, \(lqitalic\(rq, -\(lqunderline\(rq, \(lqblink\(rq, or \(lqinverse\(rq. +Takes a text attribute, or text color and attribute separated by ampersand. +For allowed attributes and colors, see \(lqConfiguring Menu Colors\(rq. Not all ports can actually display all types. .lp menu_invert_all Key to invert all items in a menu. @@ -4121,9 +4200,14 @@ Default \(oq}\(cq. ." Format menu entries using TAB to separate columns (default off). ." Only applicable to some menus, and only useful to some interfaces. ." Debug mode only. +.lp mon_movement +Show a message when hero notices a monster movement (default is off). .lp monpolycontrol Prompt for new form whenever any monster changes shape (default off). Debug mode only. +.lp montelecontrol +Prompt for destination whenever any monster gets teleported (default off). +Debug mode only. .lp mouse_support Allow use of the mouse for input and travel. Valid settings are: @@ -4211,12 +4295,13 @@ symbols for the various object types. Any omitted types are filled in at the end from the previous order. .lp paranoid_confirmation A space separated list of specific situations where alternate -prompting is desired. The default is \(lqparanoid_confirmation:pray swim\(rq. +prompting is desired. +The default is \(lqparanoid_confirmation:pray swim trap\(rq. .PS Were-change .PL Confirm for any prompts which are set to require \(lqyes\(rq rather than \(oqy\(cq, also require \(lqno\(rq to reject instead of accepting any non-yes response -as no +as no; changes pray and AutoAll to require \(lqyes\(rq or \(oqno\(cq too; .PL quit require \(lqyes\(rq rather than \(oqy\(cq to confirm quitting the game or switching into non-scoring explore mode; @@ -4227,34 +4312,98 @@ useful in normal play; applies to explore mode); require \(lqyes\(rq rather than \(oqy\(cq to confirm saving bones data when dying in debug mode; .PL attack -require \(lqyes\(rq rather than \(oqy\(cq to confirm attacking a peaceful monster; +require \(lqyes\(rq rather than \(oqy\(cq to confirm attacking +a peaceful monster; .PL wand-break -require \(lqyes\(rq rather than \(oqy\(cq to confirm breaking a wand; +require \(lqyes\(rq rather than \(oqy\(cq to confirm breaking +a wand with the \f(CRapply\fP command; .PL eating -require \(lqyes\(rq rather than \(oqy\(cq to confirm whether to continue eating; +require \(lqyes\(rq rather than \(oqy\(cq to confirm whether to +continue eating; .PL Were-change -require \(lqyes\(rq rather than \(oqy\(cq to confirm changing form due to lycanthropy -when hero has polymorph control; +require \(lqyes\(rq rather than \(oqy\(cq to confirm changing form due +to lycanthropy when hero has polymorph control; .PL pray require \(oqy\(cq to confirm an attempt to pray rather than immediately praying; on by default; +(to require \(lqyes\(rq rather than just \(oqy\(cq, set Confirm too); +.PL trap +require \(oqy\(cq 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 \(lqyes\(rq rather than just \(oqy\(cq, set Confirm too); +confirmation can be skipped by using the \(oq\f(CRm\fP\(cq movement prefix; +.PL swim +prevent walking into water or lava; on by default; (to deliberately step +onto/into such terrain when this is set, use the \(oq\f(CRm\fP\(cq +movement prefix when adjacent); +.PL AutoAll +require confirmation when the \(oqA\(cq (Autoselect-All) choice is selected +in object class filtering menus for \f(CRmenustyle:Full\fP; +(to require \(lqyes\(rq rather than just \(oqy\(cq, set Confirm too); .PL Remove require selection from inventory for \(oqR\(cq and \(oqT\(cq -commands even when wearing just one applicable item. -.PL swim -prevent walking into water or lava. +commands even when wearing just one applicable item; .PL all turn on all of the above. .PE -By default, the pray choice is enabled, the others disabled. -To disable it without setting -any of the other choices, use \(lqparanoid_confirmation:none\(rq. -To keep -it enabled while setting any of the others, include it in the list, -such as \(lqparanoid_confirmation:attack pray Remove\(rq. +By default, the pray, swim, and trap choices are enabled, the others disabled. +To disable them without setting +any of the other choices, use \f(CRparanoid_confirmation:none\fP. +To keep them enabled while setting any of the others, you can +include them in the new list, such as +\f(CRparanoid_confirmation:attack pray swim Remove\fP +or you can precede the first entry in the list with a plus sign, +\f(CRparanoid_confirmation:+attack Remove\fP. +To remove an entry that has been previously set without removing others, +precede the first entry in the list with a minus sign, +\f(CRparanoid_confirmation:-swim\fP. +To both add some new entries and remove some old ones, you can use +multiple \fIparanoid_confirmation\fP option settings, or you can +use the \(oq\f(CR+\fP\(cq form and list entries to be added by their name +and entries to be removed by \(oq\f(CR!\fP\(cq and name. +The positive (no \(oq!\(cq) and negative (with \(oq!\(cq) entries +can be intermixed. +.lp pauper +Start the character with no possessions (default false). Persistent. .lp perm_invent -If true, always display your current inventory in a window. This only +If true, always display your current inventory in a window (default false). +.lp "" +This only makes sense for windowing system interfaces that implement this feature. +For those that do, the +.op perminv_mode +option can be used to refine what gets displayed +for \fIperm_invent\fP. +Setting that to a value other than \fInone\fP +while \fIperm_invent\fP is false will change it to true. +.lp perminv_mode +Augments the +.op perm_invent +option. +Value is one of +.PS "\f(CRin-use\fP" +.PL "\f(CRnone\fP" +behave as if \fIperm_invent\fP is false; +.PL "\f(CRall\fP" +show all inventory except for gold; +.PL "\f(CRfull\fP" +show full inventory including gold; +.PL "\f(CRin-use\fP" +only show items which are in use (worn, wielded, lit lamp). +.\" %if these get uncommented, change .PS argument to "\f(CRgold+grid\fP" +.\" %and the terminator for in-use to semi-colon. +.\" .PL "\f(CRon+grid\fP" +.\" special for tty only, show \fIall\fP plus unused inventory letters; +.\" .PL "\f(CRgold+grid\fP" +.\" special for tty only, show \fIfull\fP plus unused inventory letters. +.PE +Default is \fInone\fP but if \fIperm_invent\fP gets set to true +while it is \fInone\fP it will be changed to \fIall\fP. +.lp "" +Note: if gold has been equipped in quiver/ammo-pouch then it will be +included for \fIall\fP despite that mode normally omitting gold. .\" petattr is a wincap option but we'll document it here... .lp "petattr " Specifies one or more text highlighting attributes to use when showing @@ -4262,29 +4411,10 @@ pets on the map. Effectively a superset of the .op hilite_pet boolean option. -Curses interface only; value is one or more of the following letters. -.sd -.si -.CC n "Normal text (no highlighting)" -.CC i "Inverse video (default)" -.CC b "Bold text" -.CC u "Underlined text" -.CC k "blinKing text" -.CC d "Dim text" -.CC t "iTalic text" -.CC l "Left line indicator" -.CC r "Right line indicator" -.ei -.ed -Some of those choices might not work, particularly the final three, +Curses or tty interface only; value is one of +none, bold, dim, underline, italic, blink, and inverse. +Some of those choices might not work, depending upon terminal hardware or terminal emulation software. -.lp "" -Currently multiple highlight-style letters can be combined by simply -stringing them together (for example, \(lqbk\(rq), but in the future -they might require being separated by plus signs (such as \(lqb+k\(rq, -which works already). -When using the \(oqn\(cq choice, it should be specified on its own, -not in combination with any of the other letters. .lp "pettype " Specify the type of your initial pet, if you are playing a character class that uses multiple types of pets; or choose to have no initial pet at all. @@ -4299,6 +4429,15 @@ level (Unencumbered, Burdened, streSsed, straiNed, overTaxed, or overLoaded), you will be asked if you want to continue. (Default \(oqS\(cq). Persistent. +.lp pickup_stolen +If this option is on and +.op autopickup +is also on, try to pick up things that a monster stole from you, even if they +aren't in +.op pickup_types +or match an autopickup exception. +Default is on. +Persistent. .lp pickup_thrown If this option is on and .op autopickup @@ -4352,6 +4491,8 @@ something pushes the old item into your alternate weapon slot (default off). Likewise for the \(oqa\(cq (apply) command if it causes the applied item to become wielded. Persistent. +.lp query_menu +Use a menu when asked specific yes/no queries, instead of a prompt. .lp quick_farsight When set, usually prevents the \(lqyou sense your surroundings\(rq message where play pauses to allow you to browse the map whenever clairvoyance @@ -4447,16 +4588,31 @@ Control what parts of the score list you are shown at the end (for example Only the first letter of each category (\(oqt\(cq, \(oqa\(cq, or \(oqo\(cq) is necessary. Persistent. +.lp showdamage +Whenever your character takes damage, show a message of the damage taken, +and the amount of hit points left. .lp "showexp " Show your accumulated experience points on bottom line (default off). Persistent. .lp showrace Display yourself as the glyph for your race, rather than the glyph -for your role (default off). Note that this setting affects only +for your role (default off). +Note that this setting affects only the appearance of the display, not the way the game treats you. Persistent. .lp showscore Show your approximate accumulated score on bottom line (default off). +By default, this feature is suppressed when building the program. +Persistent. +.lp showvers +Include the game's version number on the status lines (default off). +Potentially useful if you switch between different versions or variants, +or you are making screenshots or streaming video. +Using the +.op statuslines:3 +option is recommended so that there will be more room available for +status information, unless you're using nethack's \fIQt\fP interface +or your terminal emulator window displays fewer than 25 lines. Persistent. .lp "silent " Suppress terminal beeps (default on). @@ -4540,6 +4696,8 @@ Allow sounds to be emitted from an integrated sound library (default on). Display a sparkly effect when a monster (including yourself) is hit by an attack to which it is resistant (default on). Persistent. +.lp spot_monsters +Show a message when hero notices a monster (default is off). .lp standout Boldface monsters and \(lq\fB\-\-More\-\-\fP\(rq (default off). Persistent. @@ -4724,7 +4882,7 @@ If NetHack can, it should use this size font for the status window. .lp font_size_text If NetHack can, it should use this size font for text windows. .lp fullscreen -If NetHack can, it should try and display on the entire screen rather than +If NetHack can, it should try to display on the entire screen rather than in a window. .lp "guicolor " Use color text and/or highlighting attributes when displaying some @@ -4852,23 +5010,24 @@ setting the value to 3 or 4 instead will keep borders for the map, message, and status windows but have room for two additional lines of inventory plus widen each inventory line by two columns. .lp windowcolors -If NetHack can, it should display windows with the specified -foreground/background colors. -Windows GUI only. +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 .si -.lp "OPTION=windowcolors:wintype foreground/background" +.lp "\f(CROPTION=windowcolors:\fIstyle foreground\f(CR/\fIbackground\fR" .ei -.pg -where wintype is one of \(lqmenu\(rq, \(lqmessage\(rq, \(lqstatus\(rq, +.lp "" +where \fIstyle\fP is one of \(lqmenu\(rq, \(lqmessage\(rq, \(lqstatus\(rq, or \(lqtext\(rq, and -foreground and background are colors, either a hexadecimal \\'#rrggbb', +\fIforeground\fP and \fIbackground\fP are colors, either numeric (hash +sign followed by three pairs of hexadecimal digits, \fI#rrggbb\fP), one of the named colors (black, red, green, brown, -blue, magenta, cyan, orange, brightgreen, yellow, brightblue, -brightmagenta, brightcyan, white, trueblack, gray, purple, +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 one of Windows UI colors (activeborder, activecaption, -appworkspace, background, btnface, btnshadow, btntext, +or (for Windows only) one of Windows UI colors (trueblack, activeborder, +activecaption, appworkspace, background, btnface, btnshadow, btntext, captiontext, graytext, greytext, highlight, highlighttext, inactiveborder, inactivecaption, menu, menutext, scrollbar, window, windowframe, windowtext). @@ -4876,6 +5035,22 @@ window, windowframe, windowtext). If NetHack can, it should wrap long lines of text if they don't fit in the visible area of the window. .hn 2 +Crash Report Options +.pg +Please note that NetHack does not send \fBany\fP information off your +computer unless you manually click submit on a form. +.si +.lp "OPTION=crash_email:\fIemail_address\fP +.lp "OPTION=crash_name:\fIyour_name\fP +.ei +These options are used only to save you some typing on the crash +report and #bugreport forms. +.si +.lp "OPTION=crash_urlmax:\fIbytes\fP +.ei +This option is used to limit the length of the URLs generated and is only +needed if your browser cannot handle arbitrarily long URLs. +.hn 2 Platform-specific Customization options .pg Here are explanations of options that are used by specific platforms or ports @@ -4954,7 +5129,9 @@ Regular expressions are normally POSIX extended regular expressions. 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. This applies to Autopickup exceptions, Message types, Menu colors, +patterns; regardless, this document refers to both as \(oqregular +expressions.\(cq +This applies to Autopickup exceptions, Message types, Menu colors, and User sounds. .hn 2 Configuring Autopickup Exceptions @@ -5003,9 +5180,11 @@ rules and not saved with the game. .lp "Here are some examples:" .sd .si +.ft CR autopickup_exception="<*arrow" autopickup_exception=">*corpse" autopickup_exception=">* cursed*" +.ft .ei .ed .\" (this paragraph would look better unindented but can't use .lp hack...) @@ -5198,8 +5377,10 @@ Here's an example of message types using NetHack's internal pattern matching facility: .sd .si +.ft CR MSGTYPE=stop "You feel hungry." MSGTYPE=hide "You displaced *." +.ft .ei .ed specifies that whenever a message \(lqYou feel hungry\(rq is shown, @@ -5300,17 +5481,19 @@ the volume to be set while playing the sound file; optional; the index corresponding to a sound file. .PE .lp "" -The pattern should be a POSIX extended regular expression. +The pattern should be a regular expression. .pg For example: .sd .si +.ft CR SOUNDDIR=C:\\nethack\\sounds SOUND=MESG "This door is locked" "lock.wav" 100 SOUND=MESG hide "^You miss the " "swing.wav" 75 +.ft .ei .ed -.pg +.BR 0 \" without this, the next section seems too close to this one .hn 2 Configuring Status Hilites .pg @@ -5344,7 +5527,7 @@ orange, light-green, yellow, light-blue, light-magenta, light-cyan, and white. And \(lqno-color\(rq, the default foreground color on the display, which is not necessarily the same as black or white or any of the other colors. .pg -Allowed attributes are none, bold, dim, underline, blink, and inverse. +Allowed attributes are none, bold, dim, underline, italic, blink, and inverse. \(lqNormal\(rq is a synonym for \(lqnone\(rq; they should not be used in combination with any of the other attributes. .pg @@ -5364,7 +5547,7 @@ the \f(CRtty\fP interface) rather than all at once, the only way a situation like that can be controlled is to specify just one attribute. .pg You can adjust the appearance of the following status fields: -.TS S +.TS center; c c c. .\"TABLE_START @@ -5395,6 +5578,11 @@ for stone through termill, \(lqminor_troubles\(rq for blind through hallu, Allowed behaviors are \(lqalways\(rq, \(lqup\(rq, \(lqdown\(rq, \(lqchanged\(rq, a percentage or absolute number threshold, or text to match against. +For the \fIhitpoints\fP field, the additional behavior \(lqcriticalhp\(rq +is available. +It overrides other behavior rules if +hit points are at or below the \fImajor problem\fP threshold +(which varies depending upon maximum hit points and experience level). .si .lp "*" \(lqalways\(rq will set the default attributes for that field. @@ -5450,6 +5638,12 @@ it also matches when value is below or above. If the prefix is \(oq<\(cq or \(oq>\(cq, only match when strictly above or below. .lp "*" +criticalhp only applies to the hitpoints field and only +when current hit points are below a threshold (which varies by maximum +hit points and experience level). +When the threshold is met, a criticalhp rule takes precedence over all +other hitpoints rules. +.lp "*" text match sets the attribute when the field value matches the text. Text matches can only be used for \(lqalignment\(rq, @@ -5469,6 +5663,7 @@ to 0. Example hilites: .sd .si +.ft CR OPTION=hilite_status: gold/up/yellow/down/brown OPTION=hilite_status: characteristics/up/green/down/red OPTION=hilite_status: hitpoints/100%/gray&normal @@ -5479,9 +5674,10 @@ OPTION=hilite_status: hitpoints/<33%/red&bold OPTION=hilite_status: hitpoints/<15%/red&inverse OPTION=hilite_status: condition/major/orange&inverse OPTION=hilite_status: condition/lev+fly/red&inverse +.ft .ei .ed -.pg +.BR 0 \" without this, the next section seems too close to this one .hn 2 Modifying NetHack Symbols .pg @@ -5513,7 +5709,7 @@ control character. .\" to be too wide when generating plain text output. Two spaces leaves .\" some room between the symbol character and name.) Entries in first .\" column now use constant-width Roman font to approximate TeX tt font. -.TS S +.TS center; l s s l1fCR l1 l. @@ -5562,6 +5758,8 @@ d S_dog (dog or other canine) D S_dragon (dragon) ; S_eel (sea monster) E S_elemental (elemental) +# S_engrcorr (engraving in a corridor) +\` S_engroom (engraving in a room) / S_expl_tl (explosion top left) \- S_expl_tc (explosion top center) \\ S_expl_tr (explosion top right) @@ -5720,7 +5918,6 @@ 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. -.pg .hn 2 Customizing Map Glyph Representations Using Unicode .pg @@ -5730,11 +5927,12 @@ codepoint values and \fIred-green-blue\fP colors to glyph representations. 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. - .pg The format for defining a glyph representation is: .SD n -\f(CROPTIONS=glyph:glyphid\fIU+nnnn\fP/\fIR-G-B\fP\fP +.ft CR +OPTIONS=glyph:\fIglyphid\fP/\fIU+nnnn\fP/\fIR-G-B\fP +.ft .ED .pg The window port that is active needs to provide support for displaying @@ -5742,9 +5940,11 @@ 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 representation for glyphid G_pool to use Unicode codepoint -U+224B and the color represented by R-G-B value 0-0-160 +U+224B and the color represented by R-G-B value 0-0-160: .SD n -\f(CROPTIONS=glyph:G_pool/U+224B/0-0-160\fP +.ft CR +OPTIONS=glyph:G_pool/U+224B/0-0-160 +.ft .ED The list of acceptable glyphid's can be produced by \fBnethack \-\-dumpglyphids\fP. @@ -5752,10 +5952,9 @@ Individual NetHack glyphs can be specified using the G_ prefix, or you can use an S_ symbol for a glyphid and store the custom representation for all NetHack glyphs that would map to that particular symbol. - +.pg You will need to select a symset with a UTF8 handler to enable the display of the customizations, such as the Enhanced symset. -.pg .hn 2 Configuring NetHack for Play by the Blind .pg @@ -5803,8 +6002,17 @@ option and use the traditional Rogue-like commands. .lp paranoid_confirmation:swim Prevent walking into water or lava. +.lp accessiblemsg +Adds direction or location information to messages. +.lp spot_monsters +Shows a message when hero notices a monster; combine with accessiblemsg. +.lp mon_movement +Shows a message when hero notices a monster movement; +combine with spot_monsters and accessiblemsg. .lp autodescribe Automatically describe the terrain under the cursor when targeting. +.lp mention_map +Give feedback messages when interesting map locations change. .lp mention_walls Give feedback messages when walking towards a wall or when travel command was interrupted. @@ -5821,6 +6029,8 @@ of moving 8 units at a time. 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 \(lq#attributes\(rq command. +.lp showdamage +Give a message of damage taken and how many hit points are left. .hn 2 Global Configuration for System Administrators .pg @@ -5850,6 +6060,10 @@ 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 @@ -5953,26 +6167,13 @@ 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. . -.ei .lp -LIVELOG\ =\ A bitmask indicating which, if any, in-game events to record -in the livelog file, which must be compiled into the game as LIVELOGFILE -to enable this feature. The following values may be combined: -.sd -.si -0x01 - Wishes -0x02 - Significant achievements (complete sokoban, perform invocation, etc) -0x04 - Kill, destroy or bribe a unique monster. -0x08 - Significant religious events (sacrifice gifts, crowning) -0x10 - Life-saving -0x20 - Break conduct -0x40 - Artifact obtained (#name Sting, dip for Excalibur) -0x80 - Genocides -0x8000 - Livelog debug msgs (currently only 'enter new level') -.ei -.ed -.lp - +CRASHREPORTURL\ =\ If set to +\f(CRhttps://www.nethack.org/links/cr-37BETA.html\fP +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 internally inconsistent state, or if the #bugreport command is +invoked. .hn 1 Scoring .pg @@ -6417,7 +6618,7 @@ Development Team sometimes makes note of the names of the worst of these miscreants in this, the list of Dungeoneers: .BR 1 . -.TS S +.TS center; c2 c2 c. .\"TABLE_START diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index cf103a7991..a07acd1e4c 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -1,5 +1,5 @@ \documentstyle[titlepage,longtable]{article} -% NetHack 3.7 Guidebook.tex $NHDT-Date: 1431192762 2015/12/16 17:32:42 $ $NHDT-Branch: master $:$NHDT-Revision: 1.60 $ */ +% NetHack 3.7 Guidebook.tex $NHDT-Date: 1711040379 2024/03/21 16:59:39 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.562 $ */ %+% we're still limping along in LaTeX 2.09 compatibility mode %-%\documentclass{article} %-%\usepackage{hyperref} % before longtable @@ -26,6 +26,8 @@ \newcommand{\elist}{\end{list}} +\hyphenation{CRASHREPORTURL} + % this will make \tt underscores look better, but requires that % math subscripts will never be used in this document \catcode`\_=12 @@ -45,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{May 7, 2023} +%DO NOT REMOVE NH_DATESUB \date{Date(%B %-d, %Y)} +\date{November 16, 2024} \maketitle @@ -451,7 +453,7 @@ \subsection*{The status lines (bottom)} %.lp "" Encumbrance: an indication of how what you are carrying affects your ability to move. -Values are {\it Unencumbered}, {\it Encumbered}, {\it Stressed}, +Values are {\it Unencumbered}, {\it Burdened}, {\it Stressed}, {\it Strained}, {\it Overtaxed}, and {\it Overloaded}. Not shown when {\it Unencumbered}. @@ -512,92 +514,85 @@ \subsection*{The map (rest of the screen)} symbols mean: \blist{} -%.lp -\item[\tb{- {\rm and} |}] -The walls of a room, or an open door. Or a grave ({\tt |}). -%.lp +\item[\tb{-}] +The horizontal or corner walls of a room, or an open east/west door. +\item[\tb{|}] +The vertical walls of a room, or an open north/south door, or a grave. \item[\tb{.}] -The floor of a room, ice, or a doorless doorway. -%.lp +The floor of a room, or ice, or a doorless doorway, or the span of an +open drawbridge. \item[\tb{\#}] -A corridor, or iron bars, or a tree, or possibly a kitchen sink (if -your dungeon has sinks), or a drawbridge. -%.lp +A corridor, or iron bars, or a tree, or the portcullis of a closed +drawbridge.\\ +%.lp "" +Note: engravings in corridors also appear as \# but are shown in +a different color from normal corridor locations. \item[\tb{>}] Stairs down: a way to the next level. -%.lp \item[\tb{<}] Stairs up: a way to the previous level. -%.lp \item[\tb{+}] A closed door, or a spellbook containing a spell you may be able to learn. -%.lp \item[\tb{@}] -Your character or a human. -%.lp +Your character or a human or an elf. \item[\tb{\$}] A pile of gold. -%.lp \item[\tb{\^}] A trap (once you have detected it). -%.lp \item[\tb{)}] A weapon. -%.lp \item[\tb{[}] A suit or piece of armor. -%.lp \item[\tb{\%}] Something edible (not necessarily healthy). -%.lp \item[\tb{?}] A scroll. -%.lp \item[\tb{/}] A wand. -%.lp \item[\tb{=}] A ring. -%.lp \item[\tb{!}] A potion. -%.lp \item[\tb{(}] A useful item (pick-axe, key, lamp \ldots). -%.lp \item[\tb{"}] An amulet or a spider web. -%.lp \item[\tb{*}] A gem or rock (possibly valuable, possibly worthless). -%.lp \item[\tb{\`}] -A boulder or statue. -%.lp +A boulder or statue or an engraving on the floor of a room.\\ +%.lp "" +Note: statues are displayed as if they were the monsters they depict +so won't appear as a {\it grave accent\/} (aka {\it back-tick}). \item[\tb{0}] An iron ball. -%.lp \item[\tb{\verb+_+}] An altar, or an iron chain. -%.lp \item[\tb{\{}] -A fountain. -%.lp +A fountain or a sink. \item[\tb{\}}] -A pool of water or moat or a pool of lava. -%.lp +A pool of water or moat or a wall of water +or a pool of lava or a wall of lava. \item[\tb{$\backslash$}] An opulent throne. -%.lp -\item[\tb{a-zA-Z {\rm \& other symbols}}] +\item[\tb{a-z}] {\normalfont and}] +\item[\tb{A-HJ-Z}] {\normalfont and}] +%should probably change \item[\tb{@\&\verb+'+:;}] to \item[\tb{\verb+@&':;+}] +\item[\tb{@\&\verb+'+:;}] Letters and certain other symbols represent the various inhabitants -of the Mazes of Menace. Watch out, they can be nasty and vicious. +of the Mazes of Menace. +Watch out, they can be nasty and vicious. Sometimes, however, they can be helpful. -%.lp \item[\tb{I}] -This marks the last known location of an invisible or otherwise unseen -monster. Note that the monster could have moved. -The `{\tt F}' and `{\tt m}' commands may be useful here. +Rather than a specific type of monster, this marks the last known +location of an invisible or otherwise unseen monster. +Note that the monster could have moved. +The `{\tt s}', `{\tt F}', and `{\tt m}' commands may be useful here. +\item[\tb{1-5}] +The digits 1 through 5 may be displayed, marking unseen monsters sensed +via the {\it Warning\/} attribute. +Less dangerous monsters are indicated by lower values, more dangerous by +higher values. \elist %.pg @@ -1024,7 +1019,7 @@ \section{Commands} If you're wearing more than one, you'll be prompted for which one to remove. When you're only wearing one, then by default it will be removed without asking, but you can set the -{\it paranoid\verb+_+confirmation\/} +{\it paranoid\verb+_+confirmation:Remove\/} option to require a prompt.\\ %.lp "" This command may also be used to take off armor. The prompt for which @@ -1082,7 +1077,7 @@ \section{Commands} weren't there.) When you're only wearing one, then by default it will be taken off without asking, but you can set the -{\it paranoid\verb+_+confirmation\/} +{\it paranoid\verb+_+confirmation:Remove\/} option to require a prompt.\\ %.lp "" This command may also be used to remove accessories. The prompt @@ -1332,6 +1327,12 @@ \section{Commands} \item[\tb{\#autopickup}] Toggle the {\it autopickup\/} option. Default key is `{\tt @}'. %.lp +\item[\tb{\#bugreport}] +Bring up a browser window to submit a report to the {\it NetHack Development +Team}. +Can be disabled at the time the program is built; when enabled, +CRASHREPORTURL must be set in the system configuration file. +%.lp \item[\tb{\#call}] Call (name) a monster, or an object in inventory, on the floor, or in the discoveries list, or add an annotation for the @@ -1498,6 +1499,9 @@ \section{Commands} \item[\tb{\#look}] Look at what is here, under you. Default key is `{\tt :}'. %.lp +\item[\tb{\#lookaround}] +Describe what you can see, or remember, of your surroundings. +%.lp \item[\tb{\#loot}] Loot a box or bag on the floor beneath you, or the saddle from a steed standing next to you. Autocompletes. @@ -1697,21 +1701,47 @@ \section{Commands} %.lp \item[\tb{\#seeall}] Show all equipment in use. Default key is `{\tt *}'. +%.lp "" +\\ +Will display in-use items in a menu even when there is only one. %.lp \item[\tb{\#seeamulet}] Show the amulet currently worn. Default key is `{\tt "}'. +%.lp "" +\\ +Using the `{\tt m}' prefix will force the display of a worn +amulet in a menu rather than with just a message. %.lp \item[\tb{\#seearmor}] Show the armor currently worn. Default key is `{\tt [}'. +%.lp "" +\\ +Will display worn armor in a menu even when there is only thing worn. %.lp \item[\tb{\#seerings}] Show the ring(s) currently worn. Default key is `{\tt =}'. +%.lp "" +Will display worn rings in a menu if there are two (or there is +just one and is a meat ring rather than a ``real'' ring). +Use the `{\tt m}' prefix to force a menu for one ring. %.lp \item[\tb{\#seetools}] Show the tools currently in use. Default key is `{\tt (}'. +%.lp "" +Will display the result in a message if there is one tool in use (worn +blindfold or towel or lenses, lit lamp(s) and/or candle(s), leashes +attached to pets). +Will display a menu if there are more than one or if the command is +preceded by the `{\tt m}' prefix. %.lp \item[\tb{\#seeweapon}] Show the weapon currently wielded. Default key is `{\tt )}'. +%.lp "" +If dual-wielding, a separate message about the secondary weapon will be +given. +Using the `{\tt m}' prefix will force a menu and it will include +primary weapon, alternate weapon even when not dual-wielding, and also +whatever is currently assigned to the quiver slot. %.lp \item[\tb{\#shell}] Do a shell escape, switching from NetHack to a subprocess. @@ -1775,6 +1805,9 @@ \section{Commands} map without monsters; without monsters and objects; or without monsters, objects, and traps.\\ %.lp "" +If there are visible clouds of gas in view, they are treated like traps +when deciding whether to show them or the floor underneath them.\\ +%.lp "" In explore mode, you can choose to view the full map rather than just its explored portion. In debug mode there are additional choices.\\ @@ -2572,7 +2605,7 @@ \subsection*{Fighting} moving into its location, you'll be asked to confirm your intent. By default an answer of `{\tt y}' acknowledges that intent, which can be error prone if you're using `{\tt y}' to move. You can set the -{\it paranoid\verb+_+confirmation\/} +{\it paranoid\verb+_+confirmation:attack\/} option to require a response of ``{\tt yes}'' instead. %.pg @@ -3337,6 +3370,33 @@ \subsection*{Containers} unlock or open them. You can check for and try to deactivate traps with the ``{\tt \#untrap}'' extended command. +%.pg +When the contents of a container are known, that container will be +described as something like ``a sack containing 3 items''. +In this example, the 3 refers to number of {\it stacks\/} of compatible +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). + +%.pg +If a chest or large box is described as ``broken'', that means that it +can't be locked rather than that it no longer functions as a container. + +%.pg +The {\it apply\/} and {\it loot\/} commands allow you to take out and/or +put in an arbitrary number of items in a single operation. +If you want to take everything out of a container, you can use the +``{\tt \#tip}'' command to pour the contents onto the floor. +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 +contents into another container. +(As of this writing, the other container must be carried rather than on +the floor.) + %.hn 2 \subsection*{Amulets (`{\tt "}')} @@ -3671,6 +3731,8 @@ \subsection*{Achievements} Deaf from birth. \item[{\tt Nudist}] Never wore any armor. +\item[{\tt Pauper}] +Started out with no possessions. \item[{\tt Ascended}] Delivered the Amulet to its final destination. \elist @@ -3717,7 +3779,7 @@ \subsection*{Achievements} the Castle level's drawbridge or can be given to you via prayer boon. %.pg -{\it Blind\/}, {\it Deaf\/}, and {\it Nudist\/} are also conducts, and they can only be +{\it Blind\/}, {\it Deaf\/}, {\it Nudist\/}, and {\it Pauper\/} are also conducts, and they can only be enabled by setting the correspondingly named option in {\tt XNETHACKOPTIONS} or run-time configuration file prior to game start. In the case of {\it Blind\/} and {\it Deaf\/}, the option also enforces the conduct. @@ -3763,8 +3825,8 @@ \subsection*{Using a configuration file} The file may not exist, but it is a normal ASCII text file and can be created with any text editor. -After runing {\it xNetHack\/} for the first time, you should find a default -template for ths configuration file named \mbox{``.xnethackrc.template''} in +After running {\it xNetHack\/} for the first time, you should find a default +template for ths configuration file named \mbox{``xnethackrc.template''} in \mbox{{``\%USERPROFILE\%\textbackslash xNetHack\textbackslash''}}. If you have not created the configuration file, {\it xNetHack\/} will create the configuration file for you using the default template file.\\ @@ -3921,7 +3983,7 @@ \subsection*{Using a configuration file} See the ``Configuring Message Types`` section. %.lp \item[\bb{ROGUESYMBOLS}] -Custom symbols for for the rogue level's symbol set. +Custom symbols for the rogue level's symbol set. See {\it SYMBOLS} below. %.lp \item[\bb{SOUND}] @@ -4056,6 +4118,9 @@ \subsection*{Customization options} \blist{} %.lp +\item[\ib{accessiblemsg}] +Add location or direction information to messages (default is off). +%.lp \item[\ib{acoustics}] Enable messages about what your character hears (default on). Note that this has nothing to do with your computer's audio capabilities. @@ -4198,6 +4263,12 @@ \subsection*{Customization options} \item[\ib{deaf}] Start the character permanently deaf (default false). Persistent. %.lp +\item[\ib{dropped\verb+_+nopick}] +If this option is on, items you dropped will not be automatically picked up, +even if ``{\it autopickup\/}'' is also on and they are in +``{\it pickup\verb+_+types\/}'' or match a positive autopickup exception +(default on). Persistent. +%.lp \item[\ib{disclose}] Controls what information the program reveals when the game ends. Value is a space separated list of prompting/category pairs @@ -4348,7 +4419,7 @@ \subsection*{Customization options} with tiles, generally displays a heart symbol near pets. %.lp "" -With the curses interface, the {\it petattr\/} +With the tty or curses interface, the {\it petattr\/} option controls how to highlight pets and setting it will turn the {\it hilite\verb+_+pet\/} option on or off as warranted. %.lp @@ -4360,8 +4431,31 @@ \subsection*{Customization options} on the top of the pile. %.lp \item[\ib{hitpointbar}] -Show a hit point bar graph behind your name and title. -Only available for TTY and Windows GUI, and only when statushilites is on. +Show a hit point bar graph behind your name and title in the status +display (default off). +\\ +%.lp +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 {\it inverse}. +For tty and curses, {\it blink\/} will also be used if the current +hitpoint value is at or below the {\it critical HP\/} threshold. +\\ +%.lp +The ``Qt'' interface also supports hitpointbar, by drawing +a solid bar above the name and title with a hard-coded color scheme. +(As of this writing, having the bar enabled unintentionally inhibits +resizing the status panel. +To resize that, use the {\tt \#optionsfull} command to toggle the +{\it hitpointbar\/} option off, perform the resize while it's off, then +use the same command to toggle it back on.) %.lp \item[\ib{horsename}] Name your starting horse (for example, ``{\tt horsename:Trigger}''). @@ -4404,6 +4498,9 @@ \subsection*{Customization options} fountains, or altars which are ordinarily only described when covered by one or more objects (default off). Persistent. %.lp +\item[\ib{mention\verb+_+map}] +Give feedback when interesting map locations change (default off). +%.lp \item[\ib{mention\verb+_+walls}] Give feedback when walking against a wall (default off). Persistent. %.lp @@ -4412,14 +4509,15 @@ \subsection*{Customization options} See ``{\it Configuring Menu Colors\/}'' on how to configure the colors. %.lp \item[\ib{menustyle}] -Controls the interface used when you need to choose various objects (in -response to the Drop command, for instance). +Controls the method used when you need to choose various objects (in +response to the {\tt Drop} (aka {\tt droptype}) command, for instance). The value specified should be the first letter of one of the following: traditional, combination, full, or partial. +Default is {\tt full}. Persistent. \\ %.lp "" -Traditional was the only interface available for very +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). @@ -4429,6 +4527,9 @@ \subsection*{Customization options} Full displays a menu of object classes rather than a character prompt, and then a menu of matching objects for selection. +(Choosing its `A' (Autoselect-All) choice skips the second menu. +To avoid choosing that by accident, +set {\it paranoid\verb+_+confirm:AutoAll\/} to require confirmation.) Partial skips the object class filtering and immediately displays a menu of all objects. \item[\ib{menu\verb+_+deselect\verb+_+all}] @@ -4445,9 +4546,8 @@ \subsection*{Customization options} Default `\verb+^+'. \item[\ib{menu\verb+_+headings}] Controls how the headings in a menu are highlighted. -Values are ``{\tt none}'', ``{\tt bold}'', ``{\tt dim}'', -``{\tt italic}'', ``{\tt underline}'',``{\tt blink}'', or -``{\tt inverse}''. +Takes a text attribute, or text color and attribute separated by ampersand. +For allowed attributes and colors, see ``{\it Configuring Menu Colors\/}``. Not all ports can actually display all types. \item[\ib{menu\verb+_+invert\verb+_+all}] Key to invert all items in a menu. @@ -4507,10 +4607,17 @@ \subsection*{Customization options} % Only applicable to some menus, and only useful to some interfaces. % Debug mode only. %.lp +\item[\ib{mon\verb+_+movement}] +Show a message when hero notices a monster movement (default is off). +%.lp \item[\ib{monpolycontrol}] Prompt for new form whenever any monster changes shape (default off). Debug mode only. %.lp +\item[\ib{montelecontrol}] +Prompt for destination whenever any monster gets teleported (default off). +Debug mode only. +%.lp \item[\ib{mouse\verb+_+support}] Allow use of the mouse for input and travel. Valid settings are: @@ -4611,7 +4718,8 @@ \subsection*{Customization options} %.lp \item[\ib{paranoid\verb+_+confirmation}] A space separated list of specific situations where alternate -prompting is desired. The default is ``{\it paranoid\verb+_+confirmation:pray swim}''. +prompting is desired. +The default is ``{\it paranoid\verb+_+confirmation:pray swim trap}''. %.sd %.si \newlength{\pcwidth} @@ -4619,9 +4727,9 @@ \subsection*{Customization options} \addtolength{\pcwidth}{\labelsep} \blist{\leftmargin \pcwidth \topsep 1mm \itemsep 0mm} \item[{\tt 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; +for any prompts which are set to require ``yes'' rather than `y', +also require ``no'' to reject instead of accepting any non-yes response +as no; changes pray and AutoAll to require ``yes'' or ``no'' too; \item[{\tt quit~~~}] require ``{\tt yes}'' rather than `{\tt y}' to confirm quitting the game or switching into non-scoring explore mode; @@ -4636,69 +4744,111 @@ \subsection*{Customization options} a peaceful monster; \item[{\tt wand-break}] require ``{\tt yes}'' rather than `{\tt y}' to confirm breaking -a wand; -\item[{\tt eating}] +a wand with the {\it apply} command; +\item[{\tt eating~}] require ``{\tt yes}'' rather than `{\tt y}' to confirm whether to continue eating; \item[{\tt Were-change}] -require ``{\tt yes}'' rather than `{\tt y}' to confirm changing form -due to lycanthropy -when hero has polymorph control; +require ``{\tt yes}'' rather than `{\tt y}' to confirm changing form due +to lycanthropy when hero has polymorph control; \item[{\tt pray~~~}] require `{\tt y}' to confirm an attempt to pray rather than immediately praying; on by default; -\item[{\tt Remove~}] require selection from inventory for `{\tt R}' -and `{\tt T}' -commands even when wearing just one applicable item. +(to require ``yes'' rather than just `y', set Confirm too); +\item[{\tt trap~~~}] +require `{\tt y}' to confirm an attempt to move into or onto a known trap, +unless doing so is considered to be harmless; +when enabled, this confirmation is also used for moving into visible +gas cloud regions; +(to require ``yes'' rather than just `y', set Confirm too); +confirmation can be skipped by using the `{\tt m}' movement prefix; \item[{\tt swim~~~}] -prevent walking into water or lava. +prevent walking into water or lava; on by default; (to deliberately step +onto/into such terrain when this is set, use the `{\tt m}' +movement prefix when adjacent); +\item[{\tt AutoAll}] +require confirmation when the `A' (Autoselect-All) choice is selected +in object class filtering menus for {\it menustyle:Full}; +(to require ``yes'' rather than just `y', set Confirm too); +\item[{\tt Remove~}] +require selection from inventory for `{\tt R}' and `{\tt T}' +commands even when wearing just one applicable item; \item[{\tt all~~~~}] turn on all of the above. \elist %.ei %.ed -By default, the pray choice is enabled, the others disabled. -To disable it without setting -any of the other choices, use ``{\it paranoid\verb+_+confirmation:none}''. To keep -it enabled while setting any of the others, include it in the list, -such as ``{\it par\-a\-noid\verb+_+con\-fir\-ma\-tion:attack~pray~Remove}''. +By default, the pray, swim, and trap choices are enabled, the others disabled. +To disable them without setting +any of the other choices, use ``{\it paranoid\verb+_+confirmation:none}''. +To keep them enabled while setting any of the others, you can +include them in the list, such as +``{\it par\-a\-noid\verb+_+con\-fir\-ma\-tion:attack~pray~swim~Remove\/}'' +or you can precede the first entry in the list with a plus sign, +``{\it paranoid\verb+_+confirmation:\verb|+|attack~Remove\/}''. +To remove an entry that has been previously set without removing others, +precede the first entry in the list with a minus sign, +``{\it paranoid\verb+_+confirmation:-swim\/}. +To both add some new entries and remove some old ones, you can use +multiple {\it paranoid\verb+_+confirmation\/} option settings, or you can +use the `{\tt \verb|+|}' form and list entries to be added by their name +and entries to be removed by `{\tt !}' and name. +The positive (no `!') and negative (with `!') entries +can be intermixed. +%.lp +\item[\ib{pauper}] +Start the character with no possessions (default false). Persistent. %.lp \item[\ib{perm\verb+_+invent}] -If true, always display your current inventory in a window. This only +If true, always display your current inventory in a window (default is false). +%.lp "" +\\ +This only makes sense for windowing system interfaces that implement this feature. +For those that do, the +{\tt perminv\verb+_+mode} +option can be used to refine what gets displayed +for {\it perm\verb+_+invent\/}. +Setting that to a value other than {\it none\/} +while {\it perm\verb+_+invent\/} is false will change it to true. +%.lp +\item[\ib{perminv\verb+_+mode}] +Augments the +{\tt perm\verb+_+invent} +option. +Value is one of +%.PS "\f(CRin-use\fP" +\settowidth{\pcwidth}{\tt in-use} %reuse the paranoid_confirm width +\addtolength{\pcwidth}{\labelsep} +\blist{\leftmargin \pcwidth \topsep 1mm \itemsep 0mm} +%.PL +\item[{\tt none}] +behave as if {\it perm\verb+_+invent\/} is false; +\item[{all}] +show all inventory except for gold; +\item[{full}] +show full inventory including gold; +\item[{in-use}] +only show items which are in use (worn, wielded, lit lamp). +%.PE +\elist +Default is {\it none\/} but if {\it perm\verb+_+invent\/} gets set to true +while it is {\it none\/} it will be changed to {\it all\/}. +%.lp "" +\\ +Note: if gold has been equipped in quiver/ammo-pouch then it will be +included for {\it all\/} despite that mode normally omitting gold. %.lp %.\" petattr is a wincap option but we'll document it here... \item[\ib{petattr}] Specifies one or more text highlighting attributes to use when showing pets on the map. Effectively a superset of the {\it hilite\verb+_+pet\/} boolean option. -Curses interface only; value is one or more of the following letters. - -%.sd -%.si -{\tt n} --- Normal text (no highlighting)\\ -{\tt i} --- Inverse video (default)\\ -{\tt b} --- Bold text\\ -{\tt u} --- Underlined text\\ -{\tt k} --- blinKing text\\ -{\tt d} --- Dim text\\ -{\tt t} --- iTalic text\\ -{\tt l} --- Left line indicator\\ -{\tt r} --- Right line indicator\\ -%.ei -%.ed - -Some of those choices might not work, particularly the final three, +Curses or tty interface only; value is one of +none, bold, dim, underline, italic, blink, and inverse. +Some of those choices might not work, depending upon terminal hardware or terminal emulation software. -%.lp "" -Currently multiple highlight-style letters can be combined by simply -stringing them together (for example, ``bk''), but in the future -they might require being separated by plus signs (such as ``b\verb&+&k'', -which works already). -When using the `n' choice, it should be specified on its own, -not in combination with any of the other letters. - %.lp \item[\ib{pettype}] Specify the type of your initial pet, if you are playing a character class @@ -4716,6 +4866,14 @@ \subsection*{Customization options} or overLoaded), you will be asked if you want to continue. (Default `S'). Persistent. %.lp +\item[\ib{pickup\verb+_+stolen}] +If this option is on and ``{\it autopickup\/}'' is also on, try to pick up +things that a monster stole from you, even if they aren't in +``{\it pickup\verb+_+types\/}'' or +match an autopickup exception. +Default is on. +Persistent. +%.lp \item[\ib{pickup\verb+_+thrown}] If this option is on and ``{\it autopickup\/}'' is also on, try to pick up things that you threw, even if they aren't in @@ -4771,6 +4929,9 @@ \subsection*{Customization options} Likewise for the `{\tt a}' (apply) command if it causes the applied item to become wielded. Persistent. %.lp +\item[\ib{query\verb+_+menu}] +Use a menu when asked specific yes/no queries, instead of a prompt. +%.lp \item[\ib{quick\verb+_+farsight}] When set, usually prevents the ``you sense your surroundings'' message where play pauses to allow you to browse the map whenever clairvoyance @@ -4868,6 +5029,10 @@ \subsection*{Customization options} letter of each category (`{\tt t}', `{\tt a}' or `{\tt o}') is necessary. Persistent. %.lp +\item[\ib{showdamage}] +Whenever your character takes damage, show a message of the damage taken, +and the amount of hit points left. +%.lp \item[\ib{showexp}] Show your accumulated experience points on bottom line (default off). Persistent. @@ -4880,6 +5045,18 @@ \subsection*{Customization options} %.lp \item[\ib{showscore}] Show your approximate accumulated score on bottom line (default off). +By default, this feature is suppressed when building the program. +Persistent. +%.lp +\item[\ib{showvers}] +Include the game's version number on the status lines (default off). +Potentially useful if you switch between different versions or variants, +or you are making screenshots or streaming video. +Using the +{\it statuslines:3\/} +option is recommended so that there will be more room available for +status information, unless you're using nethack's {\it Qt\/} interface +or your terminal emulator window displays fewer than 25 lines. Persistent. %.lp \item[\ib{silent}] @@ -4977,6 +5154,9 @@ \subsection*{Customization options} Display a sparkly effect when a monster (including yourself) is hit by an attack to which it is resistant (default on). Persistent. %.lp +\item[\ib{spot\verb+_+monsters}] +Show a message when hero notices a monster (default is off). +%.lp \item[\ib{standout}] Boldface monsters and ``{\tt --More--}'' (default off). Persistent. %.lp @@ -5202,7 +5382,7 @@ \subsection*{Window Port Customization options} If {\it NetHack\/} can, it should use this size font for text windows. %.lp \item[\ib{fullscreen}] -If {\it NetHack\/} can, it should try and display on the entire screen rather than in a window. +If {\it NetHack\/} can, it should try to display on the entire screen rather than in a window. %.lp \item[\ib{guicolor}] Use color text and/or highlighting attributes when displaying some @@ -5355,27 +5535,28 @@ \subsection*{Window Port Customization options} plus widen each inventory line by two columns. %.lp \item[\ib{windowcolors}] -If {\it NetHack\/} can, it should display windows with the specified -foreground/background colors. -Windows GUI only. -The format is -\begin{verbatim} - OPTION=windowcolors:wintype foreground/background -\end{verbatim} - -%.pg -where wintype is one of {\it menu}, {\it message}, {\it status}, or {\it text}, and -foreground and background are colors, either a hexadecimal {\it \#rrggbb}, +If {\it NetHack\/} can, it should display all windows of a particular style +with the specified foreground and background colors. +Windows GUI and curses windowport only. +The format is\\ +{\tt ~~~~OPTION=windowcolors:}{\it style foreground\/}{\tt /}{\it background}\\ +where {\it style} is one of {\tt menu}, {\tt message}, {\tt status}, +or {\tt text}, and +{\it foreground} and {\it background} are colors, either numeric (hash +sign followed by three pairs of hexadecimal digits, {\it \#rrggbb\/}), one of the named colors ({\it black}, {\it red}, {\it green}, {\it brown}, {\it blue}, {\it magenta}, {\it cyan}, {\it orange}, -{\it brightgreen}, {\it yellow}, {\it brightblue}, {\it brightmagenta}, -{\it brightcyan}, {\it white}, {\it trueblack}, {\it gray}, {\it purple}, +{\it bright-green}, {\it yellow}, {\it bright-blue}, {\it bright-magenta}, +{\it bright-cyan}, {\it white}, {\it gray}, {\it purple}, {\it silver}, {\it maroon}, {\it fuchsia}, {\it lime}, {\it olive}, -{\it navy}, {\it teal}, {\it aqua}), or one of Windows UI colors ({\it activeborder}, -{\it activecaption}, {\it appworkspace}, {\it background}, {\it btnface}, {\it btnshadow}, -{\it btntext}, {\it captiontext}, {\it graytext}, {\it greytext}, {\it highlight}, +{\it navy}, {\it teal}, {\it aqua}), +or (for Windows only) one of Windows UI colors ({\it trueblack}, +{\it activeborder}, {\it activecaption}, {\it appworkspace}, {\it background}, +{\it btnface}, {\it btnshadow}, {\it btntext}, {\it captiontext}, +{\it graytext}, {\it greytext}, {\it highlight}, {\it highlighttext}, {\it inactiveborder}, {\it inactivecaption}, {\it menu}, -{\it menutext}, {\it scrollbar}, {\it window}, {\it windowframe}, {\it windowtext}). +{\it menutext}, {\it scrollbar}, {\it window}, {\it windowframe}, +{\it windowtext}). %.lp \item[\ib{wraptext}] @@ -5383,6 +5564,31 @@ \subsection*{Window Port Customization options} in the visible area of the window. \elist +%.hn 2 +\subsection*{Crash Report Options} +%.pg + +Please note that NetHack does not send {\textbf any} information off your +computer unless you manually click submit on a form. +%.si +\blist{} +%.lp +\item[OPTION=crash_email:{\it email_address}] +%.lp +\item[OPTION=crash_name:{\it your_name}] +%.ei +\elist +These options are used only to save you some typing on the crash +report and \#bugreport forms. +%.si +\blist{} +%.lp +\item[OPTION=crash_urlmax:{\it bytes}] +%.ei +\elist +This option is used to limit the length of the URLs generated and is only +needed if your browser cannot handle arbitrarily long URLs. + %.hn 2 \subsection*{Platform-specific Customization options} @@ -5484,7 +5690,8 @@ \subsection*{Regular Expressions} a platform where there is no regular expression library. While this is not true of any modern platform, if your {\it NetHack\/} was built this way, patterns are instead glob -patterns. This applies to Autopickup exceptions, Message types, Menu colors, +patterns; regardless, this document refers to both as ``regular expressions.'' +This applies to Autopickup exceptions, Message types, Menu colors, and User sounds. %.hn 2 @@ -5881,12 +6088,12 @@ \subsection*{Configuring User Sounds} \elist %.lp "" -The pattern should be a POSIX extended regular expression. +The pattern should be a regular expression. For example: \begin{verbatim} - SOUNDDIR=C:\\nethack\\sounds + SOUNDDIR=C:\nethack\sounds SOUND=MESG "This door is locked" "lock.wav" 100 SOUND=MESG hide "^You miss the " "swing.wav" 75 \end{verbatim} @@ -5927,7 +6134,7 @@ \subsection*{Configuring Status Hilites} And {\it 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, blink, and inverse. +Allowed attributes are none, bold, dim, underline, italic, blink, and inverse. ``Normal'' is a synonym for ``none''; they should not be used in combination with any of the other attributes. @@ -5984,6 +6191,11 @@ \subsection*{Configuring Status Hilites} %.lp "" Allowed behaviors are ``always'', ``up'', ``down'', ``changed'', a percentage or absolute number threshold, or text to match against. +For the {\it hitpoints\/} field, the additional behavior ``criticalhp'' +is available. +It overrides other behavior rules if +hit points are at or below the {\it major problem\/} threshold +(which varies depending upon maximum hit points and experience level). \blist{} %.lp "*" @@ -6036,7 +6248,14 @@ \subsection*{Configuring Status Hilites} If the prefix is `{\tt <}' or `{\tt >}', only match when strictly above or below. %.lp "*" -\item[{\tt text}] match sets the attribute when the field value matches the text. +\item[{\tt criticalhp}] only applies to the hitpoints field and only +when current hit points are below a threshold (which varies by maximum +hit points and experience level). +When the threshold is met, a criticalhp rule takes precedence over all +other hitpoints rules. +%.lp "*" +\item[{\tt text}] match sets the attribute when the field value +matches the text. Text matches can only be used for ``{\it alignment\/}'', ``{\it carrying-capacity\/}'', ``{\it hunger\/}'', ``{\it dungeon-level\/}'', and ``{\it title\/}''. @@ -6143,6 +6362,8 @@ \subsection*{Modifying {\it NetHack\/} Symbols} \verb@D@ & S\verb+_+dragon & (dragon)\\ \verb@;@ & S\verb+_+eel & (sea monster)\\ \verb@E@ & S\verb+_+elemental & (elemental)\\ +\# & S\verb+_+engrcorr & (engraving in a corridor)\\ +\verb@`@ & S\verb+_+engroom & (engraving in a room)\\ \verb@/@ & S\verb+_+expl\verb+_+tl & (explosion top left)\\ \verb@-@ & S\verb+_+expl\verb+_+tc & (explosion top center)\\ \verb@\@ & S\verb+_+expl\verb+_+tr & (explosion top right)\\ @@ -6326,8 +6547,8 @@ \subsection*{Customizing Map Glyph Representations Using Unicode} representation to be visible as specified. For example, the following line in your configuration file will cause -the glyph representation for glyphid G\verb+_+pool to use Unicode codepoint U+224B -and the color represented by R-G-B value 0-0-160:\\ +the glyph representation for glyphid G\verb+_+pool to use Unicode codepoint +U+224B and the color represented by R-G-B value 0-0-160:\\ \begin{verbatim} OPTIONS=glyph:G_pool/U+224B/0-0-160 \end{verbatim} @@ -6400,9 +6621,22 @@ \subsection*{Configuring {\it NetHack\/} for Play by the Blind} \item[\ib{paranoid\verb+_+confirmation:swim}] Prevent walking into water or lava. %.lp +\item[\ib{accessiblemsg}] +Adds direction or location information to messages. +%.lp +\item[\ib{spot\verb+_+monsters}] +Shows a message when hero notices a monster; combine with accessiblemsg. +%.lp +\item[\ib{mon\verb+_+movement}] +Shows a message when hero notices a monster movement; +combine with spot\verb+_+monsters and accessiblemsg. +%.lp \item[\ib{autodescribe}] Automatically describe the terrain under the cursor when targeting. %.lp +\item[\ib{mention\verb+_+map}] +Give feedback messages when interesting map locations change. +%.lp \item[\ib{mention\verb+_+walls}] Give feedback messages when walking towards a wall or when travel command was interrupted. @@ -6423,6 +6657,9 @@ \subsection*{Configuring {\it NetHack\/} for Play by the Blind} Prevent updates to the status lines at the bottom of the screen, if your screen-reader reads those lines. The same information can be seen via the {\tt \#attributes} command. +%.lp +\item[\ib{showdamage}] +Give a message of damage taken and how many hit points are left. \elist %.hn2 @@ -6456,6 +6693,11 @@ \subsection*{Global Configuration for System Administrators} 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 @@ -6564,6 +6806,14 @@ \subsection*{Global Configuration for System Administrators} When available, it should be left commented out on single player installations because over time the file could grow to be extremely large unless it is actively maintained. +%.lp +\item[\ib{CRASHREPORTURL}] +If set to +{\tt https://www.nethack.org/links/cr-37BETA.html} +and support is compiled in, brings up a browser window populated with +the information needed to report a problem if the game panics or ends +up in an internally inconsistent state, or if the \#bugreport command is +invoked. \elist %.hn 1 diff --git a/doc/Guidebook.txt b/doc/Guidebook.txt new file mode 100644 index 0000000000..66ca0a2ae1 --- /dev/null +++ b/doc/Guidebook.txt @@ -0,0 +1,7590 @@ + + + + + + + + + + + A Guide to the Mazes of Menace + (Guidebook for NetHack) + + + Original version - Eric S. Raymond + (Edited and expanded for NetHack 3.7.0 by Mike Stephenson and others) + + June 23, 2024 + + + + 1. Introduction + + Recently, you have begun to find yourself unfulfilled and distant + in your daily occupation. Strange dreams of prospecting, stealing, + crusading, and combat have haunted you in your sleep for many months, + but you aren't sure of the reason. You wonder whether you have in + fact been having those dreams all your life, and somehow managed to + forget about them until now. Some nights you awaken suddenly and cry + out, terrified at the vivid recollection of the strange and powerful + creatures that seem to be lurking behind every corner of the dungeon + in your dream. Could these details haunting your dreams be real? As + each night passes, you feel the desire to enter the mysterious caverns + near the ruins grow stronger. Each morning, however, you quickly put + the idea out of your head as you recall the tales of those who entered + the caverns before you and did not return. Eventually you can resist + the yearning to seek out the fantastic place in your dreams no longer. + After all, when other adventurers came back this way after spending + time in the caverns, they usually seemed better off than when they + passed through the first time. And who was to say that all of those + who did not return had not just kept going? + + Asking around, you hear about a bauble, called the Amulet of Yen- + dor by some, which, if you can find it, will bring you great wealth. + One legend you were told even mentioned that the one who finds the + amulet will be granted immortality by the gods. The amulet is rumored + to be somewhere beyond the Valley of Gehennom, deep within the Mazes + of Menace. Upon hearing the legends, you immediately realize that + there is some profound and undiscovered reason that you are to descend + into the caverns and seek out that amulet of which they spoke. Even + if the rumors of the amulet's powers are untrue, you decide that you + should at least be able to sell the tales of your adventures to the + local minstrels for a tidy sum, especially if you encounter any of the + terrifying and magical creatures of your dreams along the way. You + spend one last night fortifying yourself at the local inn, becoming + more and more depressed as you watch the odds of your success being + posted on the inn's walls getting lower and lower. + + In the morning you awake, collect your belongings, and set off + for the dungeon. After several days of uneventful travel, you see the + + + NetHack Guidebook 1 + + + + + + NetHack Guidebook 2 + + + + ancient ruins that mark the entrance to the Mazes of Menace. It is + late at night, so you make camp at the entrance and spend the night + sleeping under the open skies. In the morning, you gather your gear, + eat what may be your last meal outside, and enter the dungeon.... + + 2. What is going on here? + + You have just begun a game of NetHack. Your goal is to grab as + 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 + 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 + equipped with the tools for a proper scientific expedition. + + Barbarians are warriors out of the hinterland, hardened to bat- + tle. They begin their quests with naught but uncommon strength, a + trusty hauberk, and a great two-handed sword. + + Cavemen and Cavewomen start with exceptional strength but, unfor- + tunately, with neolithic weapons. + + Healers are wise in medicine and apothecary. They know the herbs + and simples that can restore vitality, ease pain, anesthetize, and + neutralize poisons; and with their instruments, they can divine a + being's state of health or sickness. Their medical practice earns + them quite reasonable amounts of money, with which they enter the dun- + geon. + + 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- + tal disciplines have become capable of fighting as effectively without + weapons as with. They wear no armor but make up for it with increased + 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- + 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 + 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 + employ to great advantage. + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 3 + + + + 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- + 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 + 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 + is a deadly foe. + + 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 + quality not even to the mithril armor of the Elves. + + 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- + 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 + they have no special abilities, they can succeed in any role. + + Orcs are a cruel and barbaric race that hate every living thing + (including other orcs). Above all others, Orcs hate Elves with a pas- + sion unequalled, and will go out of their way to kill one at any + opportunity. The armor and weapons fashioned by the Orcs are typi- + 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 + 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 + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 4 + + + + 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 + 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 + authors still find it an entertaining and exciting game despite having + won several times. + + 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 + 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 + 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 + 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. + + + + + + + + + + + + + + + + + + + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 5 + + + + +----------------------------------------------------------------+ + | The bat bites! | + | | + | ------ | + | |....| ---------- | + | |.<..|####...@...$.| | + | |....-# |...B....+ | + | |....| |.d......| | + | ------ -------|-- | + | | + | | + | | + | Player the Rambler St:12 Dx:7 Co:18 In:11 Wi:9 Ch:15 Neutral | + | Dlvl:1 $:993 HP:9(12) Pw:3(3) AC:10 Exp:1/19 T:752 Hungry Conf | + +----------------------------------------------------------------+ + Figure 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 6 + + + + +----------------------------------------------------------------+ + | Player the Rambler St:12 Dx:7 Co:18 In:11 Wi:9 Ch:15 | + | Neutral $:993 HP:9(12) Pw:3(3) AC:10 Exp:1/19 Hungry | + | Dlvl:1 T:752 Conf | + +----------------------------------------------------------------+ + Figure 2 + + 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 + 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 + enough for the third line. + + Here are explanations of what the various status items mean: + + Title + Your character's name and professional ranking (based on role and + experience level, see below). + + Strength + A measure of your character's strength; one of your six basic + attributes. A human character's attributes can range from 3 to + 18 inclusive; non-humans may exceed these limits (occasionally + 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, + 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- + 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- + books. + + Wisdom + Wisdom comes from your practical experience (especially when + dealing with magic). It affects your magical energy. + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 7 + + + + Charisma + Charisma affects how certain creatures react toward you. In par- + 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 + 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 + Amulet of Yendor is reputed to be somewhere beneath the twentieth + level. + + Gold + 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 + 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 + parentheses is the maximum number your hit points can reach. + + Power + Spell points. This tells you how much mystic energy (mana) you + have available for spell casting. Again, resting will regenerate + the amount available. + + Armor Class + A measure of how effectively your armor stops blows from + unfriendly creatures. The lower this number is, the more effec- + tive the armor; it is quite possible to have negative armor + class. See the Armor subsection of Objects for more information. + + Experience + 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 + 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 + status line space.) + + Time + The number of turns elapsed so far, displayed if you have the + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 8 + + + + time option set. + + Status + 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 + 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 + terminal illness). + + 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- + 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 + 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, + just press the space bar. + + To change how and what messages are shown on the message line, + see "Configuring Message Types" and the verbose option. + + 3.3. The map (rest of the screen) + + The rest of the screen is the map of the level as you have + explored it so far. Each symbol on the screen represents something. + 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 + list of what the default symbols mean: + + - The horizontal or corner walls of a room, or an open east/west + door. + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 9 + + + + | 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 + of an open drawbridge. + + # 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 + different color from normal corridor locations. + + > Stairs down: a way to the next level. + + < Stairs up: a way to the previous level. + + + A closed door, or a spellbook containing a spell you may be able + to learn. + + @ Your character or a human or an elf. + + $ A pile of gold. + + ^ A trap (once you have detected it). + + ) A weapon. + + [ A suit or piece of armor. + + % Something edible (not necessarily healthy). + + ? A scroll. + + / A wand. + + = A ring. + + ! A potion. + + ( A useful item (pick-axe, key, lamp...). + + " An amulet or a spider web. + + * A gem or rock (possibly valuable, possibly worthless). + + ` A boulder or statue or an engraving on the floor of a room. + + 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. + + _ An altar, or an iron chain. + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 10 + + + + { A fountain or a sink. + + } A pool of water or moat or a wall of water or a pool of lava or a + wall of lava. + + \ An opulent throne. + + a-z and + + 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 + 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 + 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 + 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 + 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 + 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 `*' + 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 June 23, 2024 + + + + + + NetHack Guidebook 11 + + + + You can put a number before some commands to repeat them that + many times; for example, "10s" will search ten times. If you have the + number_pad option set, you must type `n' to prefix a count, so the + example above would be typed "n10s" instead. Commands for which + counts make no sense ignore them. In addition, movement commands can + be prefixed for greater control (see below). To cancel a count or a + prefix, press the ESC key. + + 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 + `.', `,', `;', or `:'. `.' will explain the symbol at the chosen + location, conditionally check for "More info?" depending upon + whether the help option is on, and then you will be asked to pick + another location; `,' will explain the symbol but skip any addi- + tional information, then let you pick another location; `;' will + skip additional info and also not bother asking you to choose + another location to examine; `:' will show additional info, if + any, without asking for confirmation. When picking a location, + 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 + includes map coordinates. + + 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 + included with their descriptions. + + & Tell what a command does. + + < Go up to the previous level (if you are on a staircase or lad- + der). + + > Go down to the next level (if you are on a staircase or ladder). + + [yuhjklbn] + Go one step in the direction indicated (see Figure 3). If you + sense or remember a monster there, you will fight the monster + instead. Only these one-step movement commands cause you to + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 12 + + + + fight monsters; the others (below) are "safe." + +-----------------------------------------------------+ + | y k u 7 8 9 | + | \ | / \ | / | + | h- . -l 4- . -6 | + | / | \ / | \ | + | b j n 1 2 3 | + | (number_pad off) (number_pad on) | + +-----------------------------------------------------+ + Figure 3 + + [YUHJKLBN] + Go in that direction until you hit a wall or run into something. + + m[yuhjklbn] + Prefix: move without picking up objects or fighting (even if you + 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 + 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 + a list of all discovered objects) and the ``' (knownclass, show a + list of discovered objects in a particular class) commands to + offer a menu of several sorting alternatives (which sets a new + 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 + containers and go straight to adjacent monsters. + + In debug mode (aka "wizard mode"), the `m' prefix may also be + used with the "#teleport" and "#wizlevelport" commands. + + F[yuhjklbn] + Prefix: fight a monster (even if you only guess one is there). + + g[yuhjklbn] + Prefix: move until something interesting is found. + + G[yuhjklbn] or +[yuhjklbn] + 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- + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 13 + + + + ing . ^ is used as shorthand elsewhere in the + 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 + supported as a prefix but similar effect can be achieved by using + `m' and G in combination. m can also be used in com- + bination with g, +, or + +. + + _ Travel to a map location via a shortest-path algorithm. + + The shortest path is computed over map locations the hero knows + about (e.g. seen or previously traversed). If there is no known + path, a guess is made instead. Stops on most of the same condi- + tions as the `G' prefix, but without picking up objects, so + implicitly forces the `m' prefix. For ports with mouse support, + the command is also invoked when a mouse-click takes place on a + 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 + 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 + 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' + (remove) to take off only one accessory. + + ^A Repeat the previous command. + + c Close a door. + + C Call (name) a monster, an individual object, or a type of object. + + Same as extended command "#name". + + ^C Panic button. Quit the game. + + d Drop something. + + For example "d7a" means drop seven items of object a. + + D Drop several things. + + In answer to the question + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 14 + + + + "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 + of the blessed/uncursed/cursed groups may be typed. + + DB - drop all objects known to be blessed. + DU - drop all objects known to be uncursed. + DC - drop all objects known to be cursed. + DX - drop all objects of unknown B/U/C status. + DP - drop objects picked up last. + Da - drop all objects, without asking for confirmation. + Di - examine your inventory before dropping anything. + Du - drop only unpaid objects (when in a shop). + 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 + of object filtering: class (`!' for potions, `?' for scrolls, and + so on), shop status (`u' for unpaid, in other words, owned by the + shop), bless/curse state (`B', `U', `C', and `X' as shown above), + and novelty (`P', recently picked up items; controlled by picking + up or dropping things rather than by any time factor). + + If you specify more than one value in a category (such as "!?" + for potions and scrolls or "BU" for blessed and uncursed), an + inventory object will meet the criteria if it matches any of the + 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- + tions behaved differently.) + + ^D Kick something (usually a door). + + e Eat food. + + 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- + 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 + instead of just y. + + E Engrave a message on the floor. + + E- - write in the dust with your fingers. + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 15 + + + + Engraving the word "Elbereth" will cause most monsters to not + attack you hand-to-hand (but if you attack, you will rub it out); + this is often useful to give yourself a breather. + + f Fire (shoot or throw) one of the objects placed in your quiver + (or quiver sack, or that you have at the ready). You may select + ammunition with a previous `Q' command, or let the computer pick + something appropriate if autoquiver is true. If your wielded + weapon has the throw-and-return property, your quiver is empty, + and autoquiver is false, you will throw that wielded weapon + instead of filling the quiver. This will also automatically use + a polearm if wielded. If fireassist is true, firing will auto- + 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 + `!' for potions. + + I* - list all gems in inventory; + Iu - list all unpaid items; + Ix - list all used up items that are on your shopping bill; + IB - list all items known to be blessed; + IU - list all items known to be uncursed; + IC - list all items known to be cursed; + IX - list all items whose bless/curse status is unknown; + IP - list items picked up last; + I$ - count your money. + + o Open a door. + + O Set options. + + A menu showing the current option values will be displayed. You + can change most values simply by selecting the menu entry for the + given option (ie, by typing its letter or clicking upon it, + depending on your user interface). For the non-boolean choices, + a further menu or prompt will appear once you've closed this + menu. The available options are listed later in this Guidebook. + Options are usually set before the game rather than with the `O' + command; see the section on options below. Precede `O' with the + `m' prefix to show advanced options. + + ^O Show overview. + + Shortcut for "#overview": list interesting dungeon levels vis- + ited. + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 16 + + + + (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 + choosing an unlisted item of armor will attempt to wear it. (See + 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, + 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 + drink from that. If that is declined, then it offers a chance to + 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 + 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. + + r Read a scroll or spellbook. + + R Remove a worn accessory (ring, amulet, or blindfold). + + If you're wearing more than one, you'll be prompted for which one + to remove. When you're only wearing one, then by default it will + be removed without asking, but you can set the 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 + 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 + 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 + at an adjacent "remembered, unseen monster" marker. + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 17 + + + + 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 + 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 + 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- + erally be less effective than when shot. + + See also `f' (fire) for throwing or shooting an item pre-selected + via the `Q' (quiver) command, with some extra assistance. + + T Take off armor. + + 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 + the paranoid_confirmation:Remove option to require a 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 + item of armor and attempt to take it off.) + + ^T Teleport, if you have the ability. + + v Display version number. + + V Display the game history. + + w Wield weapon. + + w- - wield nothing, use your bare (or gloved) hands. + + 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 June 23, 2024 + + + + + + NetHack Guidebook 18 + + + + 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 + use will only list armor, but choosing an unlisted accessory will + attempt to put it on. (See the `P' command above. It lists + accessories as the inventory choices but will accept an item of + armor and attempt to wear it.) + + 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 + exchange still takes place. + + 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 + 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 + 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, + the role and status feedback is augmented by the information pro- + vided by enlightenment magic. + + z Zap a wand. + + z. - to aim at yourself, use `.' for the direction. + + Z Zap (cast) a spell. + + Z. - to cast at yourself, use `.' for the direction. + + ^Z Suspend the game (UNIX(R) versions with job control only). See + "#suspend" below for more details. + + : Look at what is here. + + + + __________ + (R)UNIX is a registered trademark of The Open Group. + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 19 + + + + ; Show what type of thing a visible symbol corresponds to. + + , Pick up some things from the floor beneath you. + + May be preceded by `m' to force a selection menu. + + @ Toggle the autopickup option on and off. + + ^ Ask for the type of an adjacent trap you found earlier. + + ) Tell what weapon you are wielding. + + [ Tell what armor you are wearing. + + = Tell what rings you are wearing. + + " Tell what amulet you are wearing. + + ( Tell what tools you are using. + + * Tell what equipment you are using. + + Combines the preceding five type-specific commands into one. + + $ Report the gold you're carrying, possibly shop credit and/or debt + too. + + + 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 + list rather than be inserted into the sorted ordering.) + + \ Show what types of objects have been discovered. + + May be preceded by `m' to select preferred display order. + + ` Show discovered types for one class of objects. + + 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 + map. + + Allows scrolling with the menu_first_page, menu_previous_page, + menu_next_page, and menu_last_page keys (`^', `<', `>', `|' by + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 20 + + + + default). Some interfaces also support menu_shift_left and + 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. + + Del Show map without obstructions. You can view the explored portion + of the current level's map without monsters; without monsters and + objects; or without monsters, objects, and traps. + + 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 com- + mand. If that happens, you can use the extended command "#ter- + rain" instead. + + # Perform an extended command. + + + + 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 + compiled with. + + #adjust + Adjust inventory letters (most useful when the fixinv option is + "on"). Autocompletes. Default key is `M-a'. + + This command allows you to move an item from one particular + inventory slot to another so that it has a letter which is more + meaningful for you or that it will appear in a particular loca- + tion when inventory listings are displayed. You can move to a + currently empty slot, or if the destination is occupied--and + won't merge--the item there will swap slots with the one being + moved. "#adjust" can also be used to split a stack of objects; + 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 + 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 + 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 + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 21 + + + + 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', + and also `^N' if number_pad is on. + + #apply + Apply (use) a tool such as a pick-axe, a key, or a lamp. Default + key is `a'. + + 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 + in the process. Confirmation is required. + + #attributes + Show your attributes. Default key is `^X'. + + #autopickup + Toggle the autopickup option on/off. Default key is `@'. + + #bugreport + Bring up a browser window to submit a report to the NetHack + Development Team. Can be disabled at the time the program is + built; when enabled, CRASHREPORTURL must be set in the system + 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 + level (same as "#annotate"). Default key is `C'. + + #cast + Cast a spell. Default key is `Z'. + + #chat + Talk to someone. Default key is `M-c'. + + #chronicle + Show a list of important game events. + + #close + Close a door. Default key is `c'. + + #conduct + List voluntary challenges you have maintained. Autocompletes. + Default key is `M-C'. + + See the section below entitled "Conduct" for details. + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 22 + + + + #debugfuzzer + Start the fuzz tester. Debug mode only. + + #dip + Dip an object into something. Autocompletes. Default key is `M- + d'. + + The `m' prefix skips dipping into a fountain or pool if there is + one at your location. + + #down + Go down a staircase. Default key is `>'. + + #drop + Drop an item. Default key is `d'. + + #droptype + Drop specific item types. Default key is `D'. + + #eat + 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. + 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 + instead. + + #fight + Prefix key to force fight a direction, even if you see nothing to + fight there. Default key is `F', or `-' with number_pad + + #fire + Fire ammunition from quiver, possibly autowielding a launcher, or + hit with a wielded polearm. Default key is `f'. + + #force + 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 + extinct. + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 23 + + + + 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 + both. + + If the sorting order is "count high to low" or "count low to + high" (which are applicable for #vanquished), that will be + ignored for #genocided and alphabetical will be used instead. + The menu omits those two choices when used for #genocide. + + Autocompletes. Default key is `M-g'. + + #glance + Show what type of thing a map symbol corresponds to. Default key + is `;'. + + #help + 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, + 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 + command. + + #history + Show long version and game history. Default key is `V'. + + #inventory + Show your inventory. Default key is `i'. + + #inventtype + Inventory specific item types. Default key is `I'. + + #invoke + Invoke an object's special powers. Autocompletes. Default key + is `M-i'. + + #jump + 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 + on. + + #known + Show what object types have been discovered. Default key is `\'. + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 24 + + + + 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 + ``'. + + The `m' prefix operates the same as for "#known". + + #levelchange + Change your experience level. Autocompletes. Debug mode only. + + #lightsources + Show mobile light sources. Autocompletes. Debug mode only. + + #look + Look at what is here, under you. Default key is `:'. + + #lookaround + 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 + steed standing next to you. Autocompletes. Precede with the `m' + prefix to skip containers at your location and go directly to + removing a saddle. Default key is `M-l', and also `l' if num- + ber_pad is on. + + #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 + as "#call". Autocompletes. Default keys are `N', `M-n', and `M- + N'. + + #offer + 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. + Corpses of recently killed monsters are the fodder of choice. + + The `m' prefix skips offering any items which are on the altar. + + #open + Open a door. Default key is `o'. + + #options + Show and change option settings. Default key is `O'. Precede + with the `m' prefix to show advanced options. + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 25 + + + + #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' + 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 + 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 + than just the "interesting" subset. + + Autocompletes. Default keys are `^O', and `M-O'. + + #panic + Test the panic routine. Terminates the current game. Autocom- + pletes. Debug mode only. + + 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. + + #pay + 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. + Default key is `|'. + + #pickup + Pick up things at the current location. Default key is `,'. The + `m' prefix forces use of a menu. + + #polyself + Polymorph self. Autocompletes. Debug mode only. + + #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 + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 26 + + + + 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 + Show previously displayed game messages. Default key is `^P'. + + #puton + Put on an accessory (ring, amulet, etc). Default key is `P'. + + #quaff + Quaff (drink) something. Default key is `q'. + + The `m' prefix skips drinking from a fountain or sink if there is + one at your location. + + #quit + Quit the program without saving your game. Autocompletes. + + Since using this command by accident would throw away the current + game, you are asked to confirm your intent before quitting. + Default response is n (no); continue playing. To really quit, + respond with y. You can set the paranoid_confirmation:quit + 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 + `r'. + + #redraw + Redraw the screen. Default key is `^R', and also `^L' if num- + ber_pad is on. + + #remove + Remove an accessory (ring, amulet, etc). Default key is `R'. + + #repeat + 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. + Default key is `m'. + + #retravel + Travel to a previously selected travel destination. Default key + is `C-_'. See also #travel. + + #ride + Ride (or stop riding) a saddled creature. Autocompletes. + Default key is `M-R'. + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 27 + + + + #rub + 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- + wise `M-5' when it is set to 2 or 4. + + #rush + Prefix key to rush towards a direction. Default is `g' when num- + ber_pad is off, `M-5' when number_pad is set to 1 or 3, otherwise + `5' when it is set to 2 or 4. + + #save + 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 + edited the config file, don't use this. + + #search + Search for traps and secret doors around you. Default key is + `s'. + + #seeall + Show all equipment in use. Default key is `*'. + + Will display in-use items in a menu even when there is only one. + + #seeamulet + Show the amulet currently worn. Default key is `"'. + + Using the `m' prefix will force the display of a worn amulet in a + menu rather than with just a message. + + #seearmor + Show the armor currently worn. Default key is `['. + + 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 + `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 + (worn blindfold or towel or lenses, lit lamp(s) and/or candle(s), + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 28 + + + + leashes attached to pets). Will display a menu if there are more + than one or if the command is preceded by the `m' prefix. + + #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 + will include primary weapon, alternate weapon even when not dual- + 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, + 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 + 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- + 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- + ered traps.) Default key is `^'. + + #sit + Sit down. Autocompletes. Default key is `M-s'. + + #stats + 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 + `fg' to return to the game. Default key is `^Z'. + + #swap + Swap wielded and secondary weapons. Default key is `x'. + + #takeoff + Take off one piece of armor. Default key is `T'. + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 29 + + + + #takeoffall + Remove all armor. Default key is `A'. + + #teleport + 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 + 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 + them. + + 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 + 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, + not an exhaustive set of all possibilities. Autocompletes. + + #throw + Throw something. Default key is `t'. + + #timeout + Look at the timeout queue. Autocompletes. Debug mode only. + + #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- + 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 + situation will start with the floor container menu. + + Autocompletes. Default key is `M-T'. + + #travel + Travel to a specific location on the map. Default key is `_'. + Using the "request menu" prefix shows a menu of interesting tar- + gets in sight without asking to move the cursor. When picking a + target with cursor and the autodescribe option is on, the top + line will show "(no travel path)" if your character does not know + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 30 + + + + of a path to that location. See also #retravel. + + #turn + Turn undead away. Autocompletes. Default key is `M-t'. + + #twoweapon + Toggle two-weapon combat on or off. Autocompletes. Default key + is `X', and also `M-2' if number_pad is off. + + 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', + and `u' if number_pad is on. + + In some circumstances it can also be used to rescue trapped mon- + sters. + + #up + Go up a staircase. Default key is `<'. + + #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) + 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). + Whichever 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 + menu. + + Autocompletes. Default key is `M-V'. + + #version + Print compile time options for this version of NetHack. + + The second paragraph lists the user interface(s) that are + included. If there are more than one, you can use the windowtype + option in your run-time configuration file to select the one you + want. + + Autocompletes. Default key is `M-v'. + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 31 + + + + #versionshort + Show the program's version number, plus the date and time that + the running copy was built from sources (not the version's + release date). Default key is `v'. + + #vision + Show vision array. Autocompletes. Debug mode only. + + #wait + Rest one move while doing nothing. Default key is `.', and also + ` ' if rest_on_space is on. + + #wear + Wear a piece of armor. Default key is `W'. + + #whatdoes + Tell what a key does. Default key is `&'. + + #whatis + Show what type of thing a symbol corresponds to. Default key is + `/'. + + #wield + Wield a weapon. Default key is `w'. + + #wipe + Wipe off your face. Autocompletes. Default key is `M-w'. + + #wizborn + Show monster birth, death, genocide, and extinct statistics. + Debug mode only. + + #wizbury + Bury objects under and around you. Autocompletes. Debug mode + only. + + #wizcast + Cast any spell. Debug mode only. + + #wizdetect + Reveal hidden things (secret doors or traps or unseen monsters) + within a modest radius. No time elapses. Autocompletes. Debug + mode only. Default key is `^E'. + + #wizgenesis + Create a monster. May be prefixed by a count to create more than + one. Autocompletes. Debug mode only. Default key is `^G'. + + #wizidentify + Identify all items in inventory. Autocompletes. Debug mode + only. Default key is `^I'. + + #wizintrinsic + Set one or more intrinsic attributes. Autocompletes. Debug mode + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 32 + + + + 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- + pletes. Debug mode only. + + #wizlevelport + Teleport to another level. Autocompletes. Debug mode only. + Default key is `^V'. + + #wizmap + Map the level. Autocompletes. Debug mode only. Default key is + `^F'. + + #wizrumorcheck + 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- + taphs, and hallucinatory monsters. + + Autocompletes. Debug mode only. + + #wizseenv + 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 + only. + + #wizwish + Wish for something. Autocompletes. Debug mode only. Default + key is `^W'. + + #wmode + Show wall modes. Autocompletes. Debug mode only. + + #zap + Zap a wand. Default key is `z'. + + #? + Help menu: get the list of available extended commands. + + + + 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 June 23, 2024 + + + + + + NetHack Guidebook 33 + + + + 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 + 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 + used for another command, so the three key combination + meta+Shift+ is needed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 34 + + + + M-? #? (not supported by all platforms) + + M-2 #twoweapon (unless the number_pad option is enabled) + + M-a #adjust + + M-A #annotate + + M-c #chat + + M-C #conduct + + M-d #dip + + M-e #enhance + + M-f #force + + M-g #genocided + + M-i #invoke + + M-j #jump + + M-l #loot + + M-m #monster + + M-n #name + + M-o #offer + + M-O #overview + + M-p #pray + + M-r #rub + + M-R #ride + + M-s #sit + + M-t #turn + + M-T #tip + + M-u #untrap + + M-v #version + + M-V #vanquished + + M-w #wipe + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 35 + + + + M-X #exploremode + + + + If the number_pad option is on, some additional letter commands + are available: + + h #help + + j #jump + + k #kick + + l #loot + + N #name + + u #untrap + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 36 + + + + 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- + 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, + 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 + 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 + (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 + 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 + 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 + example, ghosts can walk through doors and fog clouds can flow under + them). Some monsters who can open doors can also use unlocking tools. + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 37 + + + + 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 + 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 + 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 + 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 + 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 + 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. + 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 + 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 + 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 + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 38 + + + + 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 + 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 + back up to the main dungeon. + + 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 + 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 + into stasis. + + Ordinarily when you climb a set of stairs, you will arrive on the + 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 + play. + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 39 + + + + 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 + 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 + until you have paid any debt you owe. + + 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 + 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 + 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 + 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 + 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 + shop fees, if any. + + 5.4.1. Shop idiosyncrasies + + Several aspects of shop behavior might be unexpected. + + * 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 + were outside the shop. + + * 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 + accord. + + * Shops do not get restocked with new items, regardless of inventory + depletion. + + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 40 + + + + 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 + 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 + 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 + 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 + 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 + 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 + 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, + in which case the back on land circumstance is implied. + + 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- + 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 + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 41 + + + + 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, + running, or traveling). + + 5.6. Rogue level + + 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 + 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 + 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, + which may be useful to help distinguish one from another when multiple + monsters are present. Assigning a name which is just a space will + remove any prior name. + + 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 + 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 + 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 June 23, 2024 + + + + + + NetHack Guidebook 42 + + + + 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 + monster that you can see; of course, if the monster has moved, you + will attack empty air. If you guess that the monster has moved and + you don't wish to fight, you can use the `m' command to move without + fighting; likewise, if you don't remember a monster but want to try + fighting anyway, you can use the `F' command. + + 6.2. Your pet + + You start the game with a little dog (`d'), kitten (`f'), or pony + (`u'), which follows you about the dungeon and fights monsters with + you. Like you, your pet needs food to survive. Dogs and cats usually + feed themselves on fresh carrion and other meats; horses need vegetar- + ian food which is harder to come by. If you're worried about your pet + or want to train it, you can feed it, too, by throwing it food. A + properly trained pet can be very useful under certain circumstances. + + Your pet also gains experience from killing monsters, and can + grow over time, gaining hit points and doing more damage. Initially, + your pet may even be better at killing things than you, which makes + pets useful for low-level characters. + + Your pet will follow you up and down staircases if it is next to + you when you move. Otherwise your pet will be stranded and may become + wild. Similarly, when you trigger certain types of traps which alter + your location (for instance, a trap door which drops you to a lower + dungeon level), any adjacent pet will accompany you and any non-adja- + cent pet will be left behind. Your pet may trigger such traps itself; + you will not be carried along with it even if adjacent at the time. + + 6.3. Steeds + + 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 + 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 + on the map. + + Riding skill is managed by the "#enhance" command. See the sec- + tion on Weapon proficiency for more information about that. + + Use the `a' (apply) command and pick a saddle in your inventory + to attempt to put that saddle on an adjacent creature. If successful, + it will be transferred to that creature's inventory. + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 43 + + + + 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 + even former incarnations of yourself!) and their personal effects. + Ghosts are hard to kill, but easy to avoid, since they're slow and do + little damage. You can plunder the deceased adventurer's possessions; + however, they are likely to be cursed. Beware of whatever killed the + former player; it is probably still lurking around, gloating over its + last victory. + + 6.5. Persistence of Monsters + + Monsters (a generic reference which also includes humans and + pets) are only shown while they can be seen or otherwise sensed. Mov- + ing to a location where you can't see or sense a monster any more will + result in it disappearing from your map, similarly if it is the one + who moved rather than you. + + However, if you encounter a monster which you can't see or + sense--perhaps it is invisible and has just tapped you on the noggin-- + 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 + location ordinarily wouldn't be seen any more. + + 7. Objects + + When you find something in the dungeon, it is common to want to + pick it up. In NetHack, this is accomplished by using the `,' com- + mand. If autopickup option is on, you will automatically pick up the + object by walking over, unless you move with the `m' prefix. + + If you're carrying too many items, NetHack will tell you so and + you won't be able to pick up anything more. Otherwise, it will add + the object(s) to your pack and tell you what you just picked up. + + As you add items to your inventory, you also add the weight of + that object to your load. The amount that you can carry depends on + your strength and your constitution. The stronger and sturdier you + are, the less the additional load will affect you. There comes a + point, though, when the weight of all of that stuff you are carrying + around with you through the dungeon will encumber you. Your reactions + will get slower and you'll burn calories faster, requiring food more + frequently to cope with it. Eventually, you'll be so overloaded that + you'll either have to discard some of what you're carrying or collapse + under its weight. + + NetHack will tell you how badly you have loaded yourself. If you + are encumbered, one of the conditions Burdened, Stressed, Strained, + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 44 + + + + 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 + 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 + 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 + 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 + 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 + detrimentally in other ways. + + 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 + uncursed. They could just as easily have been described as unblessed, + 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 + 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 June 23, 2024 + + + + + + NetHack Guidebook 45 + + + + 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 + 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 + "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 + 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 + 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 + weapon's enchantment is to have it magically identified somehow. Most + 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 + 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 + 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 + 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 June 23, 2024 + + + + + + NetHack Guidebook 46 + + + + ters (barbarians, for instance) have the necessary skill available. + Even with that skill, using two weapons at once incurs a penalty in + the chance to hit your target compared to using just one weapon at a + 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 + other worn items. + + 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 + 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" + (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 + 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, + 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 + 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. + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 47 + + + + Some characters have the ability to throw or shoot a volley of + multiple items (from the same stack) in a single action. Knowing how + to load several rounds of ammunition at once--or hold several missiles + 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 + 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, + the others can still continue beyond that spot. + + 7.2.2. Weapon proficiency + + You will have varying degrees of skill in the weapons available. + Weapon proficiency, or weapon skills, affect how well you can use par- + 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 + 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 + 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 + 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 + 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 + the next skill level (unless you've already reached the limit for this + skill). Once such training reaches the threshold for that next level, + you'll be told that you feel more confident in your skills. At that + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 48 + + + + point you can use "#enhance" to increase one or more skills. Such + skills are not increased automatically because there is a limit to + your total overall skills, so you need to actively choose which skills + to enhance and which to ignore. + + 7.2.3. Two-Weapon combat + + 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 + 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 + 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 + 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 + weapons 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 + 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 + armor: + Dragon scale mail 1 + Plate mail, Crystal plate mail 3 + Bronze plate mail, Splint mail, + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 49 + + + + 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 + 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 + attacks. + + 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- + 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 + 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- + 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 + 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 + 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 + what you eat." + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 50 + + + + Some character roles and some monsters are vegetarian. Vegetar- + ian monsters will typically never eat animal corpses, while vegetarian + players can, but with some rather unpleasant side-effects. + + 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 + 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 + enchantment are difficult to identify without these. + + 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 + environment variables are ignored. You can disable the mail daemon by + turning off the mail option. + + The command to read a scroll is `r'. + + 7.6. Potions (`!') + + 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 + 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 + 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 + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 51 + + + + 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 + catastrophic release of magical energies. + + 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 + case indicating that the wand has been cancelled. + + 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 + wands. + + 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 + 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- + wear them after. That's done implicitly to avoid unnecessary tedium. + + 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! + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 52 + + + + 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 + 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 + 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 + 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 + 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 + level, skill category, chance of failure when casting, and an estimate + 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 + 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- + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 53 + + + + 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 + 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" + 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 + 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 + 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- + 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- + 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. + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 54 + + + + 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 + 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 + throw them by hand. + + 7.13. Large rocks (``') + + Statues and boulders are not particularly useful, and are gener- + ally heavy. It is rumored that some statues are not what they seem. + + 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 + 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 + prefix's usual action of suppressing auto-pickup at the destination. + + 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 + though. + + 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 + 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 + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 55 + + + + 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 + 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 + 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- + 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 + 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 + challenges the first time they win the game. + + 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 + 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 + 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. + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 56 + + + + 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 + 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 + animal byproduct. + + 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- + 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 + 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 + 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 + 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 + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 57 + + + + engraving, or any item that is absolutely necessary to win the game, + is not counted against this conduct. The identity of scrolls and + spellbooks (and knowledge of spells) in your starting inventory is + assumed to be learned from your teachers prior to the start of the + game and isn't counted. + + There is a side-branch to the main dungeon called "Sokoban," + briefly described in the earlier section about Traps. As mentioned + there, the goal is to push boulders into pits and/or holes to plug + those in order to both get the boulders out of the way and be able to + go past the traps. There are some special "rules" that are active + when in that branch of the dungeon. Some rules can't be bypassed, + such as being unable to push a boulder diagonally. Other rules can, + such as not smashing boulders with magic or tools, but doing so causes + you to receive a luck penalty. No message about that is given at the + time, but it is tracked as a conduct. The #conduct command and end of + game disclosure will report whether you have abided by the special + rules of Sokoban, and if not, how many times you violated them, pro- + viding you with a way to discover which actions incur bad luck so that + you can be better informed about whether or not to avoid repeating + those actions in the future. (Note: the Sokoban conduct will only be + displayed if you have entered the Sokoban branch of the dungeon during + the current game. Once that has happened, it becomes part of dis- + closed conduct even if you haven't done anything interesting there. + Ending the game with "never broke the Sokoban rules" conduct is most + meaningful if you also manage to perform the "obtained the Sokoban + prize" achievement (see Achievements below).) + + There are several other challenges tracked by the game. It is + possible to eliminate one or more species of monsters by genocide; + playing without this feature is considered a challenge. When the game + offers you an opportunity to genocide monsters, you may respond with + the monster type "none" if you want to decline. You can change the + form of an item into another item of the same type ("polypiling") or + the form of your own body into another creature ("polyself") by wand, + spell, or potion of polymorph; avoiding these effects are each consid- + ered challenges. Polymorphing monsters, including pets, does not + break either of these challenges. Finally, you may sometimes receive + wishes; a game without an attempt to wish for any items is a chal- + lenge, as is a game without wishing for an artifact (even if the arti- + fact immediately disappears). When the game offers you an opportunity + to make a wish for an item, you may choose "nothing" if you want to + decline. + + 8.1. Achievements + + End of game disclosure will also display various achievements + representing progress toward ultimate ascension, if any have been + attained. They aren't directly related to conduct but are grouped + with it because they fall into the same category of "bragging rights" + and to limit the number of questions during disclosure. Listed here + roughly in order of difficulty and not necessarily in the order in + which you might accomplish them. + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 58 + + + + Rank - Attained rank title Rank. + Shop - Entered a shop. + Temple - Entered a temple. + Mines - Entered the Gnomish Mines. + Town - Entered Mine Town. + Oracle - Consulted the Oracle of Delphi. + 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 + special item there. + 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 + and close the drawbridge on the Castle level. + Bell - Acquired the Bell of Opening. + Gehennom - Entered Gehennom. + Candle - Acquired the Candelabrum of Invocation. + Book - Acquired the Book of the Dead. + Invocation - Gained access to the bottommost level of + Gehennom. + Amulet - Acquired the fabled Amulet of Yendor. + Endgame - Reached the Elemental Planes. + Astral - Reached the Astral Plane level. + Blind - Blind from birth. + Deaf - Deaf from birth. + Nudist - Never wore any armor. + Pauper - Started out with no possessions. + Ascended - Delivered the Amulet to its final destination. + + + Notes: + + 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 + 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 June 23, 2024 + + + + + + NetHack Guidebook 59 + + + + The Medusa achievement is recorded if she dies for any reason, + even if you are not directly responsible, and only if she dies. + + The 5-note tune can be learned via trial and error with a musical + instrument played closely enough--but not too close!--to the Castle + level's drawbridge or can be given to you via prayer boon. + + Blind, Deaf, Nudist, and Pauper are also conducts, and they can + only be enabled by setting the correspondingly named option in + XNETHACKOPTIONS or run-time configuration file prior to game start. + In the case of Blind and Deaf, the option also enforces the conduct. + They aren't really significant accomplishments unless/until you make + substantial progress into the dungeon. + + 9. Options + + Due to variations in personal tastes and conceptions of how + NetHack should do things, there are options you can set to change how + NetHack behaves. + + 9.1. Setting the options + + Options may be set in a number of ways. Within the game, the `O' + command allows you to view all options and change most of them. You + can also set options automatically by placing them in a configuration + file, or in the XNETHACKOPTIONS environment variable. Some versions + of NetHack also have front-end programs that allow you to set options + before starting the game or a global configuration for system adminis- + trators. + + 9.2. Using a configuration file + + The default name of the configuration file varies on different + operating systems. + + On UNIX, Linux, and macOS it is ".xnethackrc" 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 ".xnethackrc" located in the folder + "%USERPROFILE%\xNetHack\". The file may not exist, but it is a normal + ASCII text file can can be created with any text editor. After run- + ning xNetHack for the first time, you should find a default template + for the configuration file named "xnethackrc.template" in + "%USERPROFILE%\xNetHack\". If you have not created the configuration + file, xNetHack 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. + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 60 + + + + Any line beginning with `[' and ending in `]' is a section marker + (the closing `]' can be followed by whitespace and then an arbitrary + comment beginning with `#'). The text between the square brackets is + the section name. Section markers are only valid after a CHOOSE + directive and their names are case insensitive. Lines after a section + marker belong to that section up until another section starts or a + marker without a name is encountered or the file ends. Lines within + sections are ignored unless a CHOOSE directive has selected that sec- + tion. + + You can use different configuration directives in the file, some + of which can be used multiple times. In general, the directives are + written in capital letters, followed by an equals sign, followed by + settings particular to that directive. + + Here is a list of allowed directives: + + OPTIONS + There are two types of options, boolean and compound options. + Boolean options toggle a setting on or off, while compound options + take more diverse values. Prefix a boolean option with "no" or `!' + to turn it off. For compound options, the option name and value are + separated by a colon. Some options are persistent, and apply only + to new games. You can specify multiple OPTIONS directives, and mul- + tiple options separated by commas in a single OPTIONS directive. + (Comma separated options are processed from right to left.) + + Example: + + OPTIONS=dogname:Fido + OPTIONS=!legacy,autopickup,pickup_types:$"=/!?+ + + HACKDIR + Default location of files NetHack needs. On Windows HACKDIR defaults + to the location of the xNetHack.exe or xNetHackw.exe file so setting + HACKDIR to override that is not usually necessary or recommended. + + LEVELDIR + The location that in-progress level files are stored. Defaults to + HACKDIR, must be writable. + + SAVEDIR + The location where saved games are kept. Defaults to HACKDIR, must + be writable. + + BONESDIR + The location that bones files are kept. Defaults to HACKDIR, must be + writable. + + LOCKDIR + The location that file synchronization locks are stored. Defaults to + HACKDIR, must be writable. + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 61 + + + + TROUBLEDIR + The location that a record of game aborts and self-diagnosed game + problems is kept. Defaults to HACKDIR, must be writable. + + AUTOCOMPLETE + Enable or disable an extended command autocompletion. Autocomple- + tion has no effect for the X11 windowport. You can specify multiple + autocompletions. To enable autocompletion, list the extended com- + mand. Prefix the command with "!" to disable the autocompletion for + that command. + + Example: + + AUTOCOMPLETE=zap,!annotate + + AUTOPICKUP_EXCEPTION + Set exceptions to the pickup_types option. See the "Configuring + Autopickup Exceptions" section. + + BINDINGS + 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: + + BIND=^X:getpos.autodescribe + + CHOOSE + Chooses at random one of the comma-separated parameters as an active + section name. Lines in other sections are ignored. + + Example: + + OPTIONS=color + CHOOSE=char A,char B + [char A] + OPTIONS=role:arc,race:dwa,align:law,gender:fem + [char B] + OPTIONS=role:wiz,race:elf,align:cha,gender:mal + [] #end of CHOOSE + 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 + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 62 + + + + "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- + 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 + NetHack Symbols" section. + + Example: + + # replace small punctuation (tick marks) with digits + SYMBOLS=S_boulder:0,S_golem:7 + + WIZKIT + Debug mode only: extra items to add to initial inventory. Value is + the name of a text file containing a list of item names, one per + line, up to a maximum of 128 lines. Each line is processed by the + function that handles wishing. + + Example: + + WIZKIT=~/wizkit.txt + + + + Here is an example of configuration file contents: + + # Set your character's role, race, gender, and alignment. + OPTIONS=role:Valkyrie, race:Human, gender:female, align:lawful + # + # Turn on autopickup, set automatically picked up object types + OPTIONS=autopickup,pickup_types:$"=/!?+ + # + # Map customization + OPTIONS=color # Display things in color if possible + OPTIONS=lit_corridor # Show lit corridors differently + OPTIONS=hilite_pet,hilite_pile + # Replace small punctuation (tick marks) with digits + SYMBOLS=S_boulder:0,S_golem:7 + # + # No startup splash screen. Windows GUI only. + OPTIONS=!splash_screen + + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 63 + + + + 9.3. Using the XNETHACKOPTIONS environment variable + + The XNETHACKOPTIONS variable is a comma-separated list of initial + values for the various options. Some can only be turned on or off. + You turn one of these on by adding the name of the option to the list, + and turn it off by typing a `!' 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 + fruit is set to "lime", you would enter the command + + % setenv XNETHACKOPTIONS "color,\!leg,name:Blue Meanie,fruit:lime" + + in csh (note the need to escape the `!' since it's special to that + shell), or the pair of commands + + $ XNETHACKOPTIONS="color,!leg,name:Blue Meanie,fruit:lime" + $ export XNETHACKOPTIONS + + in sh, ksh, or bash. + + The XNETHACKOPTIONS 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, XNETHACKOPTIONS 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. + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 64 + + + + 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. + + autodescribe + 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 + Automatically dig if you are wielding a digging tool and moving into + a place that can be dug (default false). Persistent. + + autoopen + Walking into a closed door attempts to open it (default true). Per- + sistent. + + autopickup + Automatically pick up things onto which you move (default off). + Persistent. + + 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 + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 65 + + + + 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 + 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 + weapon or both. + + The default is Apply-Key. Persistent. + + blind + Start the character permanently blind (default false). Persistent. + + bones + Allow saving and loading bones files (default true). Persistent. + + boulder + 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 + 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. + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 66 + + + + 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. + 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: + + i - disclose your inventory; + a - disclose your attributes; + v - summarize monsters that have been vanquished; + g - list monster species that have been genocided; + c - display your conduct; also achievements, if any; + o - display dungeon overview. + + 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; + n - prompt you and default to no on the prompt; + + - disclose it without prompting; + - - do not disclose it and do not prompt. + + 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 + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 67 + + + + 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 + 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 + 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 + An obsolete synonym for "gender:female". Cannot be set with the `O' + 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 + 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 + inventory letters. Persistent. + + force_invmenu + 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. + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 68 + + + + 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- + 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. + 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. + 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 + off as warranted. + + hilite_pile + Visually distinguish piles of objects from individual objects + (default off). The behavior of this option depends on the type of + windowing you use. In text windowing, text highlighting or inverse + video is often used; with tiles, generally displays a small plus- + 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 + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 69 + + + + 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 + be set with the `O' command. + + ignintr + Ignore interrupt signals, including breaks (default off). Persis- + tent. + + implicit_uncursed + Omit "uncursed" from object descriptions when it can be deduced from + other aspects of the description (default on). Persistent. + + If you use menu coloring, you may want to turn this off. + + invweight + Augment inventory object descriptions with their objects' weight + (default on). + + legacy + Display an introductory message when starting the game (default on). + Persistent. + + lit_corridor + Show corridor squares seen by night vision or a light source held by + 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', + 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- + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 70 + + + + 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 + class(es). Combination starts with a prompt for object class(es) of + 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 + 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 `-'. + + menu_deselect_page + Key to deselect all items on this page of a menu. Default `\'. + + menu_first_page + 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 + all ports can actually display all types. + + menu_invert_all + Key to invert all items in a menu. Default `@'. + + menu_invert_page + Key to invert all items on this page of a menu. Default `~'. + + menu_last_page + Key to jump to the last page in a menu. Default `|'. + + menu_next_page + Key to go to the next menu page. Default `>'. + + menu_objsyms + Show object symbols in menu headings in menus where the object sym- + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 71 + + + + bols act as menu accelerators (default off). + + 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. (default on) + + menu_previous_page + Key to go to the previous menu page. Default `<'. + + menu_search + Key to search for some text and toggle selection state of matching + menu items. Default `:'. + + menu_select_all + Key to select all items in a menu. Default `.'. + + menu_select_page + Key to select all items on this page of a menu. Default `,'. + + 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 + `{'. + + menu_shift_right + 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 + off). + + monpolycontrol + Prompt for new form whenever any monster changes shape (default + off). Debug mode only. + + montelecontrol + Prompt for destination whenever any monster gets teleported (default + off). Debug mode only. + + mouse_support + Allow use of the mouse for input and travel. Valid settings are: + + 0 - disabled + 1 - enabled and make OS adjustments to support mouse use + 2 - like 1 but does not make any OS adjustments + + Omitting a value is the same as specifying 1 and negating mouse_sup- + port is the same as specifying 0. + + msghistory + The number of top line messages to keep (and be able to recall with + `^P') (default 20). Cannot be set with the `O' command. + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 72 + + + + 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 + curses (`f' and `r' choices, default `r'). The possible values are: + + 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- + 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. + + 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 + this with the `O' command. + + nudist + Start the character with no armor (default false). Persistent. + + null + Send padding nulls to the terminal (default on). Persistent. + + number_pad + Use digit keys instead of letters to move (default 0 or off). Valid + settings are: + + 0 - move by letters; "yuhjklbn" + 1 - move by numbers; digit `5' acts as `G' movement prefix + 2 - like 1 but `5' works as `g' prefix instead of as `G' + 3 - by numbers using phone key layout; 123 above, 789 below + 4 - combines 3 with 2; phone layout plus MS-DOS compatibility + -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 + with the letter `n' ("n12s"). + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 73 + + + + packorder + 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. + + paranoid_confirmation + 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 + accepting 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 + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 74 + + + + 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. + + perm_invent + If true, always display your current inventory in a window (default + false). + + This only makes sense for windowing system interfaces that implement + this feature. For those that do, the perminv_mode option can be + used to refine what gets displayed for perm_invent. Setting that to + a value other than none while perm_invent is false will change it to + true. + + perminv_mode + Augments the perm_invent option. Value is one of + + none - behave as if perm_invent is false; + all - show all inventory except for gold; + full - show full inventory including gold; + in-use - only show items which are in use (worn, wielded, lit lamp). + + Default is none but if perm_invent gets set to true while it is none + it will be changed to all. + + Note: if gold has been equipped in quiver/ammo-pouch then it will be + included for all despite that mode normally omitting gold. + + petattr + Specifies one or more text highlighting attributes to use when show- + ing pets on the map. Effectively a superset of the hilite_pet + boolean option. Curses or tty interface only; value is one of none, + bold, dim, underline, italic, blink, and inverse. Some of those + choices might not work, depending upon terminal hardware or terminal + emulation software. + + pettype + Specify the type of your initial pet, if you are playing a character + class that uses multiple types of pets; or choose to have no initial + pet at all. Possible values are "cat", "dog", "horse", and "none". + If the choice is not allowed for the role you are currently playing, + it will be silently ignored. For example, "horse" will only be hon- + ored when playing a knight. Cannot be set with the `O' command. + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 75 + + + + pickup_burden + When you pick up an item that would exceed this encumbrance level + (Unencumbered, Burdened, streSsed, straiNed, overTaxed, or over- + Loaded), you will be asked if you want to continue. (Default `S'). + Persistent. + + 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 + pickup_types or match an autopickup exception. Default is on. Per- + sistent. + + pickup_thrown + If this option is on and autopickup is also on, try to pick up + 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. + 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- + tion configuration file lines to further refine autopickup behavior. + + There is no way to set pickup_types to "none". (Setting it to an + empty value reverts to "all".) If you want to avoid automatically + picking up any types of items but do want to have autopickup on in + order to have autopickup_exception settings control what you do and + don't pick up, you can set pickup_types to `.'. That is the type + symbol for venom and you won't come across any venom items so won't + unintentionally pick such up. + + pile_limit + When walking across a pile of objects on the floor, threshold at + which the message "there are few/several/many objects here" is given + instead of showing a popup list of those objects. A value of 0 + means "no limit" (always list the objects); a value of 1 effectively + means "never show the objects" since the pile size will always be at + least that big; default value is 5. Persistent. + + playmode + 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. + Default is normal play. + + pushweapon + Using the `w' (wield) command when already wielding something pushes + the old item into your alternate weapon slot (default off). Like- + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 76 + + + + wise for the `a' (apply) command if it causes the applied item to + become wielded. Persistent. + + query_menu + Use a menu when asked specific yes/no queries, instead of a prompt. + + quick_farsight + When set, usually prevents the "you sense your surroundings" message + where play pauses to allow you to browse the map whenever clairvoy- + ance randomly activates. Some situations, such as being underwater + or engulfed, ignore this option. It does not affect the clairvoy- + ance spell where pausing to examine revealed objects or monsters is + less intrusive. Default is off. Persistent. + + 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. + + rest_on_space + Make the space bar a synonym for the `.' (#wait) command (default + off). Persistent. + + role + Pick your type of character (for example, role:Samurai); synonym for + character. See name for an alternate method of specifying your + role. + + This option can also be used to limit selection when role is chosen + randomly. Use a space-separated list of roles and either negate + each one or negate the option itself instead. Negation is accom- + plished in the same manner as with boolean options, by prefixing the + option or its value(s) with `!' or "no". + Examples: + + OPTIONS=role:!arc !bar !kni + OPTIONS=!role:arc bar kni + + 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. + + rlecomp + When writing out a save file, perform run length compression of the + map. Not all ports support run length compression. It has no effect + on reading an existing save file. + + runmode + Controls the amount of screen updating for the map window when + engaged in multi-turn movement (running via shift+direction or con- + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 77 + + + + 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- + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 78 + + + + 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. + + silent + Suppress terminal beeps (default on). Persistent. + + sortdiscoveries + Controls the sorting behavior for the output of the `\' and ``' com- + mands. Persistent. + + The possible values are: + + 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 + 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' + prefix before the `\' or ``' command. + + sortloot + 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 + 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 + on). Persistent. + + sortvanquished + Controls the sorting behavior for the output of the #vanquished com- + mand and also for the #genocided command. Persistent. + + The possible values are: + + t - traditional--order by monster level; ties are broken by internal + monster index; default; + d - order by monster difficulty rating; ties broken by internal + index; + a - order alphabetically, first any unique monsters then all the + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 79 + + + + others; + 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- + mand. + + sounds + Allow sounds to be emitted from an integrated sound library (default + on). + + sparkle + Display a sparkly effect when a monster (including yourself) is hit + by an attack to which it is resistant (default on). Persistent. + + spot_monsters + Show a message when hero notices a monster (default is off). + + standout + Boldface monsters and "--More--" (default off). Persistent. + + statushilites + Controls how many turns status hilite behaviors highlight the field. + If negated or set to zero, disables status hiliting. See "Configur- + ing Status Hilites" for further information. + + status_updates + 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- + 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 + "symset:default" to explicitly select the default symbols. + + time + 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 + if configured into the program.) Persistent. + + + + NetHack 3.7.0 June 23, 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. + + toptenwin + 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 + affect traveling via the `_' ("#travel") command. Persistent. + + tutorial + Play a tutorial level at the start of the game. Setting this option + on or off in the config file will skip the query. + + verbose + 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 + works in other situations where you are asked to pick a location. + + The possible settings are: + + c - compass ("east" or "3s" or "2n,4w"); + f - full compass ("east" or "3south" or "2north,4west"); + m - map (map column x=0 is not used); + 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 + 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 + 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 June 23, 2024 + + + + + + NetHack Guidebook 81 + + + + 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, + 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 XNETHACKOPTIONS 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) + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 82 + + + + align_status + Where to align or place the status window (top, bottom, left, or + right). + + 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 + True forces tiled_map to be False. + + color + If NetHack can, it should display color if it can for different mon- + sters, objects, and dungeon features (default on). + + 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- + minal (default off). + + font_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 + windows. + + font_message + If NetHack can, it should use a font by the chosen name for the mes- + sage window. + + font_status + If NetHack can, it should use a font by the chosen name for the sta- + tus window. + + font_text + If NetHack can, it should use a font by the chosen name for text + windows. + + font_size_map + If NetHack can, it should use this size font for the map window. + + font_size_menu + If NetHack can, it should use this size font for menu windows. + + font_size_message + If NetHack can, it should use this size font for the message window. + + font_size_status + If NetHack can, it should use this size font for the status window. + + + + + NetHack 3.7.0 June 23, 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 + 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 + only; default is on. + + large_font + If NetHack can, it should use a large font. + + map_mode + 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 + 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, + 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). + Cannot be set with the `O' command. + + scroll_amount + If NetHack can, it should scroll the display by this number of cells + when the hero reaches the scroll_margin. + + scroll_margin + If NetHack can, it should scroll the display when the hero or cursor + is this number of cells away from the edge of the window. + + selectsaved + If NetHack can, it should display a menu of existing saved games for + the player to choose from at game startup, if it can. Not all ports + support this option. + + softkeyboard + 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 + starts up (default yes). + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 84 + + + + statuslines + 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 XNETHACKOPTIONS, 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). + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 85 + + + + use_inverse + 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 + 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. + 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 + columns 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 + columns. + + 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 June 23, 2024 + + + + + + NetHack Guidebook 86 + + + + 9.6. Crash Report Options + + 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 compen- + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 87 + + + + 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" + 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. + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 88 + + + + 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 + an object at your location. + + 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 June 23, 2024 + + + + + + NetHack Guidebook 89 + + + + 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", + "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 June 23, 2024 + + + + + + NetHack Guidebook 90 + + + + getpos.door.prev + When asked for a location, the key to go to previous closest door or + doorway. Default is `D'. + + 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. + 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 + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 91 + + + + `;'. + + getpos.pick.verbose + When asked for a location, the key to choose the location, and show + more info without asking. Default is `:'. + + getpos.self + 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 + location. Default is `x'. + + getpos.unexplored.prev + 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- + tions. Default is `$'. + + getpos.valid.next + When asked for a location, the key to go to next closest valid loca- + tion. Default is `z'. + + getpos.valid.prev + 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 + area, when the message matches a user-defined pattern. + + 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; + pattern - the pattern to match. + + The pattern should be a regular expression. + + Allowed types are: + + 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 + shown in between. + + Here's an example of message types using NetHack's internal pattern + matching facility: + + + + NetHack 3.7.0 June 23, 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- + 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 June 23, 2024 + + + + + + NetHack Guidebook 93 + + + + 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. + + 9.13. Configuring User Sounds + + 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 + ports support the use of user sounds. + + The following configuration file entries are relevant to mapping + user sounds to messages: + + SOUNDDIR + The directory that houses the sound files to be played. + + SOUND + 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 + 3.7.0); + msgtype - optional; message type to use, see "Configuring Mes- + sage Types" + pattern - the pattern to match; + sound file - the sound file to play; + volume - the volume to be set while playing the sound file; + sound index - optional; the index corresponding to a sound file. + + The pattern should be a regular expression. + + For example: + + SOUNDDIR=C:\nethack\sounds + SOUND=MESG "This door is locked" "lock.wav" 100 + SOUND=MESG hide "^You miss the " "swing.wav" 75 + + + + 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 + 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 hit- + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 94 + + + + 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- + color&normal for >30%.) + + For another example, the following line in your configuration + file will cause wisdom to be displayed red if it drops and green if it + rises: + + 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 + 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 + 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 + example: "magenta&inverse+dim". + + Note that the display may substitute or ignore particular attrib- + utes 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, 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 + strength gold experience + dexterity hitpoints HD + constitution hitpoints-max time + intelligence power hunger + wisdom power-max carrying-capacity + 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. + "experience", "time", and "score" are conditionally displayed + depending upon your other option settings. + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 95 + + + + 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 + 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 + 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. + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 96 + + + + * 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- + 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 + 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 + to 0. + + Example hilites: + + OPTION=hilite_status: gold/up/yellow/down/brown + OPTION=hilite_status: characteristics/up/green/down/red + OPTION=hilite_status: hitpoints/100%/gray&normal + OPTION=hilite_status: hitpoints/<100%/green&normal + OPTION=hilite_status: hitpoints/<66%/yellow&normal + OPTION=hilite_status: hitpoints/<50%/orange&normal + OPTION=hilite_status: hitpoints/<33%/red&bold + OPTION=hilite_status: hitpoints/<15%/red&inverse + OPTION=hilite_status: condition/major/orange&inverse + OPTION=hilite_status: condition/lev+fly/red&inverse + + + + 9.15. Modifying NetHack Symbols + + NetHack can load entire symbol sets from the symbol file. + + 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. + + 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 + as a control character. + + NetHack Symbols + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 97 + + + + Symbol Name Description + ----------------------------------------------------------------- + S_air (air) + _ S_altar (altar) + " S_amulet (amulet) + A S_angel (angelic being) + a S_ant (ant or other insect) + ^ S_anti_magic_trap (anti-magic field) + [ S_armor (suit or piece of armor) + [ S_armour (suit or piece of armor) + ^ S_arrow_trap (arrow trap) + 0 S_ball (iron ball) + # S_bars (iron bars) + B S_bat (bat or bird) + ^ S_bear_trap (bear trap) + - S_blcorn (bottom left corner) + b S_blob (blob) + + S_book (spellbook) + ) S_boomleft (boomerang open left) + ( S_boomright (boomerang open right) + ` S_boulder (boulder) + - S_brcorn (bottom right corner) + > S_brdnladder (branch ladder down) + > S_brdnstair (branch staircase down) + < S_brupladder (branch ladder up) + < S_brupstair (branch staircase up) + C S_centaur (centaur) + _ S_chain (iron chain) + # S_cloud (cloud) + c S_cockatrice (cockatrice) + $ S_coin (pile of coins) + # S_corr (corridor) + - S_crwall (wall) + # S_darkroom (dark room) + ^ S_dart_trap (dart trap) + & S_demon (major demon) + * S_digbeam (dig beam) + > S_dnladder (ladder down) + > S_dnstair (staircase down) + d S_dog (dog or other canine) + D S_dragon (dragon) + ; S_eel (sea monster) + E S_elemental (elemental) + # S_engrcorr (engraving in a corridor) + ` S_engroom (engraving in a room) + / S_expl_tl (explosion top left) + - S_expl_tc (explosion top center) + \ S_expl_tr (explosion top right) + | S_expl_ml (explosion middle left) + S_expl_mc (explosion middle center) + | S_expl_mr (explosion middle right) + \ S_expl_bl (explosion bottom left) + - S_expl_bc (explosion bottom center) + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 98 + + + + / S_expl_br (explosion bottom right) + e S_eye (eye or sphere) + ^ S_falling_rock_trap (falling rock trap) + f S_feline (cat or other feline) + ^ S_fire_trap (fire trap) + ! S_flashbeam (flash beam) + % S_food (piece of food) + { S_fountain (fountain) + F S_fungus (fungus or mold) + * S_gem (gem or rock) + S_ghost (ghost) + H S_giant (giant humanoid) + G S_gnome (gnome) + ' S_golem (golem) + | S_grave (grave) + g S_gremlin (gremlin) + - S_hbeam (horizontal beam [zap animation]) + # S_hcdbridge (horizontal raised drawbridge) + + S_hcdoor (closed door in horizontal wall) + . S_hodbridge (horizontal lowered drawbridge) + | S_hodoor (open door in horizontal wall) + ^ S_hole (hole) + @ S_human (human or elf) + h S_humanoid (humanoid) + - S_hwall (horizontal wall) + . S_ice (ice) + i S_imp (imp or minor demon) + I S_invisible (invisible monster) + J S_jabberwock (jabberwock) + j S_jelly (jelly) + k S_kobold (kobold) + K S_kop (Keystone Kop) + ^ S_land_mine (land mine) + } S_lava (molten lava) + } S_lavawall (wall of lava) + l S_leprechaun (leprechaun) + ^ S_level_teleporter (level teleporter) + L S_lich (lich) + y S_light (light) + # S_litcorr (lit corridor) + : S_lizard (lizard) + \ S_lslant (diagonal beam [zap animation]) + ^ S_magic_portal (magic portal) + ^ S_magic_trap (magic trap) + m S_mimic (mimic) + ] S_mimic_def (mimic) + M S_mummy (mummy) + N S_naga (naga) + . S_ndoor (doorway without door) + n S_nymph (nymph) + O S_ogre (ogre) + o S_orc (orc) + p S_piercer (piercer) + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 99 + + + + ^ S_pit (pit) + # S_poisoncloud (poison cloud) + ^ S_polymorph_trap (polymorph trap) + } S_pool (water) + ! S_potion (potion) + P S_pudding (pudding or ooze) + q S_quadruped (quadruped) + Q S_quantmech (quantum mechanic) + = S_ring (ring) + ` S_rock (boulder or statue) + r S_rodent (rodent) + ^ S_rolling_boulder_trap (rolling boulder trap) + . S_room (floor of a room) + / S_rslant (diagonal beam [zap animation]) + ^ S_rust_trap (rust trap) + R S_rustmonst (rust monster or disenchanter) + ? S_scroll (scroll) + # S_sink (sink) + ^ S_sleeping_gas_trap (sleeping gas trap) + S S_snake (snake) + s S_spider (arachnid or centipede) + ^ S_spiked_pit (spiked pit) + ^ S_squeaky_board (squeaky board) + 0 S_ss1 (magic shield 1 of 4) + # S_ss2 (magic shield 2 of 4) + @ S_ss3 (magic shield 3 of 4) + * S_ss4 (magic shield 4 of 4) + ^ S_statue_trap (statue trap) + S_stone (solid rock) + ] S_strange_obj (strange object) + - S_sw_bc (swallow bottom center) + \ S_sw_bl (swallow bottom left) + / S_sw_br (swallow bottom right) + | S_sw_ml (swallow middle left) + | S_sw_mr (swallow middle right) + - S_sw_tc (swallow top center) + / S_sw_tl (swallow top left) + \ S_sw_tr (swallow top right) + - S_tdwall (wall) + ^ S_teleportation_trap (teleportation trap) + \ S_throne (opulent throne) + - S_tlcorn (top left corner) + | S_tlwall (wall) + ( S_tool (useful item (pick-axe, key, lamp...)) + ^ S_trap_door (trap door) + t S_trapper (trapper or lurker above) + - S_trcorn (top right corner) + # S_tree (tree) + T S_troll (troll) + | S_trwall (wall) + - S_tuwall (wall) + U S_umber (umber hulk) + S_unexplored (unexplored terrain) + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 100 + + + + u S_unicorn (unicorn or horse) + < S_upladder (ladder up) + < S_upstair (staircase up) + V S_vampire (vampire) + | S_vbeam (vertical beam [zap animation]) + # S_vcdbridge (vertical raised drawbridge) + + S_vcdoor (closed door in vertical wall) + . S_venom (splash of venom) + ^ S_vibrating_square (vibrating square) + . S_vodbridge (vertical lowered drawbridge) + - S_vodoor (open door in vertical wall) + v S_vortex (vortex) + | S_vwall (vertical wall) + / S_wand (wand) + } S_water (water) + ) S_weapon (weapon) + " S_web (web) + w S_worm (worm) + ~ S_worm_tail (long worm tail) + W S_wraith (wraith) + x S_xan (xan or other extraordinary insect) + X S_xorn (xorn) + Y S_yeti (apelike creature) + Z S_zombie (zombie) + z S_zruty (zruty) + S_pet_override (any pet if ACCESSIBILITY=1 is set) + S_hero_override (hero if ACCESSIBILITY=1 is set) + + Notes: + + * 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 + "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 + more specific S_boulder. + + 9.16. Customizing Map Glyph Representations Using Unicode + + 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, + or individually within your nethack.rc file. + + The format for defining a glyph representation is: + + OPTIONS=glyph:glyphid/U+nnnn/R-G-B + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 101 + + + + 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: + + symset:plain + Load a symbol set appropriate for use by blind players. + + menustyle:traditional + This will assist in the interface to speech synthesizers. + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 102 + + + + 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. + + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 103 + + + + 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. + + 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 + option. When disabled, incubi and succubi behave like nymphs. + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 104 + + + + SERVERSEED = A number that is added into the game's deterministic + hash functions. Intended for use on multi-user systems where the + administrator wants to prevent players from being able to calculate + the hash result. + + 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 + (used identification number) checking for save files (to verify that + the user who is restoring is the same one who saved). + + The following four options affect the score file: + + PERSMAX = Maximum number of entries for one person. + + ENTRYMAX = Maximum number of entries in the score file. + + 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- + tively, to identify unique people for the score file. + + 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 + 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 + 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 + available if your game is compiled with DUMPLOG. Allows the follow- + ing placeholders: + + %% - literal `%' + %v - version (eg. "3.7.0-0") + %u - game UID + %t - game start time, UNIX timestamp format + %T - current time, UNIX timestamp format + %d - game start time, YYYYMMDDhhmmss format + %D - current time, YYYYMMDDhhmmss format + %n - player name + %N - first character of player name + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 105 + + + + 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 + internally inconsistent state, or if the #bugreport command is + invoked. + + 10. Scoring + + NetHack maintains a list of the top scores or scorers on your + machine, depending on how it is set up. In the latter case, each + account on the machine can post only one non-winning score on this + list. If you score higher than someone else on this list, or better + your previous score, you will be inserted in the proper place under + your current name. How many scores are kept can also be set up when + 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; + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 106 + + + + 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 + result in falling back to explore mode instead. + + 12. Credits + + 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 + Dungeons of Doom, by Ken Arromdee. + + 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- + 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 + 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 + 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- + sion 1.03g, and went on to produce at least four more versions (3.0, + 3.2, 3.51, and 3.6; note that these are old Hack version numbers, not + contemporary NetHack ones). + + R. Black ported PC HACK 3.51 to Lattice C and the Atari + 520/1040ST, producing ST Hack 1.03. + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 107 + + + + 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. + + 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 + Threepoint, and Janet Walz, to produce NetHack 3.0c. + + NetHack 3.0 was ported to the Atari by Eric R. Smith, to OS/2 by + Timo Hakulinen, and to VMS by David Gentzel. The three of them and + Kevin Darcy later joined the main NetHack Development Team to produce + subsequent revisions of 3.0. + + 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. + Along with various other Dungeoneers, they continued to enhance the + PC, Macintosh, and Amiga ports through the later revisions of 3.0. + + Version 3.0 went through ten relatively rapidly released "patch- + level" revisions. Versions at the time were known as 3.0 for the base + release and variously as "3.0a" through "3.0j", "3.0 patchlevel 1" + through "3.0 patchlevel 10", or "3.0pl1" through "3.0pl10" rather than + 3.0.0 and 3.0.1 through 3.0.10; the three component numbering scheme + 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 + 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 + added multiple dungeons, a new display, special individual character + quests, a new endgame and many other new features, and produced + NetHack 3.1. Version 3.1.0 was released in January of 1993. + + Ken Lorber, Gregg Wonderly and Greg Olson, with help from Richard + Addison, Mike Passaretti, and Olaf Seibert, developed NetHack 3.1 for + the Amiga. + + Norm Meluch and Kevin Smolkowski, with help from Carl Schelin, + Stephen Spackman, Steve VanDevender, and Paul Winner, ported NetHack + 3.1 to the PC. + + Jon W{tte and Hao-yang Wang, with help from Ross Brown, Mike Eng- + ber, David Hairston, Michael Hamel, Jonathan Handler, Johnny Lee, Tim + Lennan, Rob Menke, and Andy Swanson, developed NetHack 3.1 for the + Macintosh, porting it for MPW. Building on their development, Bart + House added a Think C port. + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 108 + + + + Timo Hakulinen ported NetHack 3.1 to OS/2. Eric Smith ported + NetHack 3.1 to the Atari. Pat Rankin, with help from Joshua + Delahunty, was responsible for the VMS version of NetHack 3.1. + 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 + 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" + 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 + were distinct for specific types of monsters and objects rather than + 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- + port was then implemented on other platforms (initially MS-DOS but + eventually Windows, Qt, and X11 too). + + The 3.2 NetHack Development Team, comprised of Michael Allison, + Ken Arromdee, David Cohrs, Jessie Collet, Steve Creps, Kevin Darcy, + Timo Hakulinen, Steve Linhart, Dean Luick, Pat Rankin, Eric Smith, + Mike Stephenson, Janet Walz, and Paul Winner, released version 3.2.0 + in April of 1996. + + Version 3.2 marked the tenth anniversary of the formation of the + development team. In a testament to their dedication to the game, all + thirteen members of the original NetHack Development Team remained on + the team at the start of work on that release. During the interval + between the release of 3.1.3 and 3.2.0, one of the founding members of + the NetHack Development Team, Dr. Izchak Miller, was diagnosed with + cancer and passed away. That release of the game was dedicated to him + by the development and porting teams. + + Version 3.2 proved to be more stable than previous versions. + Many bugs were fixed, abuses eliminated, and game features tuned for + better game play. + + During the lifespan of NetHack 3.1 and 3.2, several enthusiasts + of the game added their own modifications to the game and made these + "variants" publicly available: + + Tom Proudfoot and Yuval Oren created NetHack++, which was quickly + renamed NetHack-- when some people incorrectly assumed that it was a + conversion of the C source code to C++. Working independently, + Stephen White wrote NetHack Plus. Tom Proudfoot later merged NetHack + Plus and his own NetHack-- to produce SLASH. Larry Stewart-Zerba and + Warwick Allison improved the spell casting system with the Wizard + Patch. Warwick Allison also ported NetHack to use the Qt interface. + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 109 + + + + Warren Cheung combined SLASH with the Wizard Patch to produce + Slash'EM, and with the help of Kevin Hugo, added more features. Kevin + later joined the NetHack Development Team and incorporated the best of + these ideas into NetHack 3.3. + + 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 + 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. + + (To anyone considering resurrecting an old version: all versions + before 3.2.3 had a Y2K bug. The high scores file and the log file + contained dates which were formatted using a two-digit year, and + 1999's year 99 was followed by 2000's year 100. 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 + retrieval of old character names to use for random ghost and statue + names in the current game.) + + 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, + 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 + an elf race, and the races of dwarves, gnomes, and orcs made their + first appearance in the game alongside the familiar human race. Monk + and Ranger roles joined Archeologists, Barbarians, Cavemen, Healers, + Knights, Priests, Rogues, Samurai, Tourists, Valkyries and of course, + Wizards. It was also the first version to allow you to ride a steed, + and was the first version to have a publicly available web-site list- + ing all the bugs that had been discovered. Despite that constantly + growing bug list, 3.3 proved stable enough to last for more than a + year and a half. + + The 3.4 NetHack Development Team initially consisted of Michael + Allison, Ken Arromdee, David Cohrs, Jessie Collet, Kevin Hugo, Ken + Lorber, Dean Luick, Pat Rankin, Mike Stephenson, Janet Walz, and Paul + Winner, with Warwick Allison joining just before the release of + NetHack 3.4.0 in March 2002. + + As with version 3.3, various people contributed to the game as a + whole as well as supporting ports on the different platforms that + NetHack runs on: + + Pat Rankin maintained 3.4 for VMS. + + Michael Allison maintained NetHack 3.4 for the MS-DOS platform. + Paul Winner and Yitzhak Sapir provided encouragement. + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 110 + + + + Dean Luick, Mark Modrall, and Kevin Hugo maintained and enhanced + the Macintosh port of 3.4. + + Michael Allison, David Cohrs, Alex Kompel, Dion Nicolaas, and + 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 + 3.4.1. + + Ron Van Iwaarden was the sole maintainer of NetHack for OS/2 the + past several releases. Unfortunately Ron's last OS/2 machine stopped + working in early 2006. A great many thanks to Ron for keeping NetHack + alive on OS/2 all these years. + + Janne Salmijarvi and Teemu Suikki maintained and enhanced the + Amiga port of 3.4 after Janne Salmijarvi resurrected it for 3.3.1. + + Christian "Marvin" Bressler maintained 3.4 for the Atari after he + resurrected it for 3.3.1. + + 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, + 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 + work-in-progress and had not gone through the process of debugging it + as a suitable release, it was decided that the version numbers present + on that code snapshot would be retired and never used in an official + NetHack release. An announcement was posted on the NetHack Develop- + ment Team's official nethack.org website to that effect, stating that + there would never be a 3.4.4, 3.5, or 3.5.0 official release version. + + In January 2015, preparation began for the release of NetHack + 3.6. + + 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 + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 111 + + + + game, author Terry Pratchett, passed away. NetHack 3.6.0 introduced a + tribute to him. + + 3.6.0 was released in December 2015, and merged work done by the + development team since the release of 3.4.3 with some of the beloved + community patches. Many bugs were fixed and some code was restruc- + tured. + + 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. + + Ken Lorber, Haoyang Wang, Pat Rankin, and Dean Luick maintained + the port of NetHack 3.6 for MacOS. + + Michael Allison, David Cohrs, Bart House, Pasi Kallinen, Alex + Kompel, Dion Nicolaas, Derek S. Ray and Yitzhak Sapir maintained the + port of NetHack 3.6 for Microsoft Windows. + + 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 + writing) on Alpha and Integrity (aka Itanium aka IA64) but not VAX. + + Ray Chason resurrected the MS-DOS port for 3.6 and contributed + the necessary updates to the community at large. + + In late April 2018, several hundred bug fixes for 3.6.0 and some + new features were assembled and released as NetHack 3.6.1. The + NetHack Development Team at the time of release of 3.6.1 consisted of + Warwick Allison, Michael Allison, Ken Arromdee, David Cohrs, Jessie + Collet, Pasi Kallinen, Ken Lorber, Dean Luick, Patric Mueller, Pat + Rankin, Derek S. Ray, Alex Smith, Mike Stephenson, Janet Walz, and + Paul Winner. + + In early May 2019, another 320 bug fixes along with some enhance- + ments and the adopted curses window port, were released as 3.6.2. + + Bart House, who had contributed to the game as a porting team + participant for decades, joined the NetHack Development Team in late + May 2019. + + 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 + 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. + + NetHack 3.6.6 was released on March 8, 2020 containing a security + fix and some bug fixes. + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 112 + + + + 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/. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 12.1. Special Thanks + + On behalf of the NetHack community, thank you very much once + again to M. Drew Streib and Pasi Kallinen for providing a public + 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, + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 113 + + + + devnull.net (gone for now, but not forgotten). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 12.2. Dungeoneers + + From time to time, some depraved individual out there in netland + sends a particularly intriguing modification to help out with the + game. The NetHack Development Team sometimes makes note of the names + of the worst of these miscreants in this, the list of Dungeoneers: + + + + + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 114 + + + + Adam Aronow J. Ali Harlow Mikko Juola + Alex Kompel Janet Walz Nathan Eady + Alex Smith Janne Salmijarvi Norm Meluch + Andreas Dorn Jean-Christophe Collet Olaf Seibert + Andy Church Jeff Bailey Pasi Kallinen + Andy Swanson Jochen Erwied Pat Rankin + Andy Thomson John Kallen Patric Mueller + Ari Huttunen John Rupley Paul Winner + Bart House John S. Bien Pierre Martineau + Benson I. Margulies Johnny Lee Ralf Brown + Bill Dyer Jon W{tte Ray Chason + Boudewijn Waijers Jonathan Handler Richard Addison + Bruce Cox Joshua Delahunty Richard Beigel + Bruce Holloway Karl Garrison Richard P. Hughey + Bruce Mewborne Keizo Yamamoto Rob Menke + Carl Schelin Keith Simpson Robin Bandy + Chris Russo Ken Arnold Robin Johnson + David Cohrs Ken Arromdee Roderick Schertler + David Damerell Ken Lorber Roland McGrath + David Gentzel Ken Washikita Ron Van Iwaarden + David Hairston Kevin Darcy Ronnen Miller + Dean Luick Kevin Hugo Ross Brown + Del Lamb Kevin Sitze Sascha Wostmann + Derek S. Ray Kevin Smolkowski Scott Bigham + Deron Meranda Kevin Sweet Scott R. Turner + Dion Nicolaas Lars Huttar Sean Hunt + Dylan O'Donnell Leon Arnott Stephen Spackman + Eric Backus M. Drew Streib Stefan Thielscher + Eric Hendrickson Malcolm Ryan Stephen White + Eric R. Smith Mark Gooderum Steve Creps + Eric S. Raymond Mark Modrall Steve Linhart + Erik Andersen Marvin Bressler Steve VanDevender + Fredrik Ljungdahl Matthew Day Teemu Suikki + Frederick Roeber Merlyn LeRoy Tim Lennan + Gil Neiger Michael Allison Timo Hakulinen + Greg Laskin Michael Feir Tom Almy + Greg Olson Michael Hamel Tom West + Gregg Wonderly Michael Sokolov Warren Cheung + Hao-yang Wang Mike Engber Warwick Allison + Helge Hafting Mike Gallop Yitzhak Sapir + Irina Rempt-Drijfhout Mike Passaretti + Izchak Miller Mike Stephenson + + + + + + + + + + + + + + + NetHack 3.7.0 June 23, 2024 + + + + + + NetHack Guidebook 115 + + + + Brand and product names are trademarks or registered trademarks + of their respective holders. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NetHack 3.7.0 June 23, 2024 + + + diff --git a/doc/config.nh b/doc/config.nh index dda4f56bf3..c159250496 100644 --- a/doc/config.nh +++ b/doc/config.nh @@ -123,7 +123,7 @@ # Objects will try to keep their inventory letters #OPTIONS=fixinv -# Try and display on the entire screen rather than in a window. +# Try to display on the entire screen rather than in a window. #OPTIONS=fullscreen # If more information is available for an object looked at with @@ -528,3 +528,12 @@ # Use highlighting in the status lines when it changes? #OPTIONS=hilite_status:hitpoints/30%/bright-magenta/normal +### Crash reports +# CRASHREPORTURL must be set in syscf to enable these options. +# These identify you in crash reports +#OPTIONS=crash_name:Your Name +#OPTIONS=crash_email:user@example.com +# This limits the size of the URL generated for a crash report. Only +# use this if your browser can't handle very long URLs. It should be as +# large as possible. +#OPTIONS=crash_maxurl:4000. diff --git a/doc/dlb.6 b/doc/dlb.6 index 6fa4d17615..b58b121648 100644 --- a/doc/dlb.6 +++ b/doc/dlb.6 @@ -1,39 +1,73 @@ -.\"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.10 $ +.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 @@ -47,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 296762d996..412c198201 100644 --- a/doc/dlb.txt +++ b/doc/dlb.txt @@ -16,10 +16,8 @@ DESCRIPTION specified as a continuous string and are followed by any arguments required in the same order as the option specifiers. - ^?ALLDOCS This facility is optional and may be excluded during NetHack - configuration. ^: ^?DLB This facility is optional but is included in - this NetHack configuration. ^: This facility is optional and was - excluded from this NetHack configuration. ^. ^. + This facility is optional and may be excluded during NetHack configura- + tion. COMMANDS The x command causes dlb to extract the contents of the archive into @@ -63,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.10. 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 70088ac9e9..a4db524296 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1,4 +1,4 @@ -HDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1181 $ $NHDT-Date: 1684621591 2023/05/20 22:26:31 $ +NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1484 $ $NHDT-Date: 1726862062 2024/09/20 19:54:22 $ General Fixes and Modified Features ----------------------------------- @@ -118,7 +118,8 @@ tribute (Discworld snippets) typos, in book order rather than fix order: Moving Pictures passage #10 initial single quote should be double, #12 "or" -> "of", #14 second instance of "megalomaniac" misspelled Lords and Ladies passage #5, near end add missing opening double - quote, passage #7 last paragraph, "to" -> "be" + 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' Interesting Times passage #1, italicize several words @@ -176,12 +177,12 @@ prevent searching or waiting next to a hostile monster if boolean option safe_wait is on - override with 'm' prevent searching or waiting if hero is slimed, stoning, strangled, or deadly ill if safe_wait is on - override with 'm' -allow random mimics to show up mimicing more furniture than just stairs +allow random mimics to show up mimicking more furniture than just stairs scatter exploding bag of holding contents instead of outright deleting them male hero poly'd into nymph chooses charm vs seduce message based on being male rather than on all nymphs being female but charm message was using hardcoded pronouns She,her for target monster--wrong for male - target and noticable if " finishes taking off his suit" is given + target and noticeable if " finishes taking off his suit" is given hostile monsters with a ranged attack try to stay away from melee range allow displacing peaceful creatures unicorn horns don't restore attribute loss anymore @@ -324,12 +325,13 @@ hero poly'd into rust monster could implicitly eat bars when adjacent by can explicitly eat them via 'e' after moving onto their spot monster hiding under an egg that hatched was kept hidden restful sleep regenerates hit points +restful sleep gives a warning message few turns before you fall asleep attacking non-adjacent concealed mimic by applying a polearm would make the hero be stuck to that mimic hero could break a wand ("raising the wand high over your head, you break it in two") even if hands were welded to a two-handed weapon or to a one-handed weapon and also to a shield -if a monster threw a cocktrice egg at the hero but hit and petrified another +if a monster threw a cockatrice egg at the hero but hit and petrified another monster, the hero would get credit/blame for killing it since ki-rin look quite a bit like unicorns, make them be more like one: allow them to use their own horn to cure themselves; remove M1_ANIMAL, @@ -363,7 +365,7 @@ allow monsters to use wand of undead turning to revive corpses on floor in some situations selling a container to a shop for gold leaves any contents that the shop doesn't ordinarily buy and sell owned by the hero, but selling the - container for credit resulted in the shop taking poesession of such + container for credit resulted in the shop taking possession of such contents without giving any additional credit; mark out of place contents 'no_charge' so that hero can reclaim them without buying add some new demonic and angelic maledictions @@ -511,6 +513,8 @@ any blessed key was behaving as if was the rogue's Master Key when unlocking when an unseen non-pet picks up or uses an item, hero loses known/dknown/ bknown/cknown/lknown memory of that item (so becomes unidentified; in particular, player won't be asked what to call unseen thrown potion) +when picking up a stackable item, it can be identified by comparing it to + another identical item that is already identified wishing for a partly eaten wraith corpse yielded "partly eaten food (1) more nutritious than untouched food (0)" if PREFIXES_IN_USE was defined (and VAR_PLAYGROUND forces it to be) when @@ -564,6 +568,8 @@ perm_invent: when buying shop goods using itemized purchasing while persistent soon as any item was bought (actual item-by-item purchase worked ok) perm_invent: making an engraving which reduced known enchantment of a weapon or known charge count of a marker didn't update persistent inventory +perm_invent: over-reading a spellbook so that in faded to blank didn't update + persistent inventory to show that if blank spellbook was already known change getloc fastmove keys in number_pad mode from hardcoded HJKL to the run/rush movement keys (meta+number) allow using rush/run prefix key in getloc to fastmove the cursor @@ -614,6 +620,7 @@ yet another fix for display problems during restore: if game is saved while being obfuscated by hallucination, but when displaying the hero there instead it would access steed pointer before that has been set up resistances gained from worn or wielded items also protect hero's inventory +dwarvish cloaks somewhat protect hero's inventory from cold and fire non-metallic gloves protect worn rings from shock message "Oops! food rations out of your grasp!" occurred due to perm_invent in mid-operation overwriting all of xname's/doname's obufs; fixed by @@ -629,7 +636,7 @@ gas clouds are a little random in how they spread out from a point Izchak occasionally stocks wands/scrolls/spellbooks of light data tracking for #overview was mis-using u.urooms[] and after being in a situation where hero was in multiple rooms at once, visiting other - levels might flag unvisisted rooms as having been visited + levels might flag unvisited rooms as having been visited special damage attacks by the Riders and by fatal-illness inflictors such as Demogorgon did no damage against other monsters, only against the hero using obj->o_id to control 'random' behavior of a helm of opposite alignment @@ -772,9 +779,8 @@ covetous monsters will teleport to downstairs or upstairs to heal have fake player monsters use verbalize instead of pline when reacting to chat fix mention_walls distinguishing unseen walls from solid stone don't push unknown boulders when moving -in flush_screen, reorder the code slightly to complete the bot() and - timebot() calls prior to the window port call to place the cursor - on the hero +in flush_screen, reorder the code slightly to complete the bot() and timebot() + calls prior to the window port call to place the cursor on the hero magic traps can toggle intrinsic invisibility Death attacking a monster does drain life attack add unique Rider revival messages @@ -814,7 +820,7 @@ elves and rangers get alignment penalty for cutting down trees casting a forgotten spell uses some random amount of power heroes starting with a spell have at least one level one spell, and have just enough power to cast it -huge monsters get easily out of pits +huge monsters and pit fiends get easily out of pits give a message when a trapped monster frees itself from some trap change kitchen sink glyph to a white { killed wood golem has a chance to also drop small shields, clubs, @@ -838,8 +844,9 @@ clear obj->bypass for buried objects [a giant on ice triggers a fire trap, other inventory, ice is melted, boulder plugs resulting pool burying rest of giant's dropped inventory, subsequent sanity checks report that there are buried objects which are 'flagged bypass'] -only give Sunsword or Demonbane as starting gear to lawful Angels -for #knownclass with menustyle=Tradtional, allow player to ask for `a even if +give Sunsword as starting gear only to lawful Angels; since Demonbane has + become a mace and Angels only get swords, they won't start with it +for #knownclass with menustyle=Traditional, allow player to ask for `a even if no artifacts have been discovered yet, same as `; likewise for `u to ask to see unique items reduce eucalyptus leaf nutrition to 1 @@ -952,6 +959,8 @@ 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 @@ -968,7 +977,7 @@ similarly, if #wizfliplevel was used to transpose an active level while a blessed potion of polymorph will prompt user for monster to poly into out of array bounds access attempt occurred when deciding whether to bounce if wand or spell zap reached edge of map -if blind hero was challanged by a vault guard, it wasn't possible to see how +if blind hero was challenged by a vault guard, it wasn't possible to see how to start following that guard out of the vault make taming via magic harp be consistent with scroll of taming and charm spell: an angry shopkeeper becomes pacified (but never tamed) @@ -976,6 +985,7 @@ wielding a bec de corbin makes ravens generate peaceful moving with 'm' prefix allows hero to enter a known pit carefully rangers always succeed in disarming bear traps, unless impaired bigroom variant 2 may have ice floor in unlit areas +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 @@ -994,10 +1004,10 @@ when invisible without see invisible you could see your hands glowing red greased saddle makes it impossible to mount the steed if an item-using monster zaps a wand of digging downward on a level that doesn't allow holes but does allow pits, create a pit and trigger it -no longer override the effect of a new moon by simply carring a lizard corpse +no longer override the effect of a new moon by simply carrying a lizard corpse make explosions burn monster's armor just like they do hero's armor make healing and extra healing better by upping the average amount healed -lifesaving healing amount depends on the consitution +lifesaving healing amount depends on the constitution hitting a monster with a wielded potion hits you far less pyrolisks get a mundane bite attack chances of random item being an artifact depends on already existing artifacts @@ -1013,16 +1023,17 @@ trappers and lurkers above enfold themselves around and crush their victims, not swallow and digest attempting to move up or down when poly'd into a holder and holding a monster rejected the move; release the monster instead -thowing a non-weapon while engulfed by an ochre jelly reported that the item +throwing a non-weapon while engulfed by an ochre jelly reported that the item vanished into the jelly's "currents" if a pet gelatinous cube eats a container, treat it the same as when a hostile - one does: the container is destroyed but its conters are engulfed + one does: the container is destroyed but its contents are engulfed when breaking a wand of sleep hits the hero with the explosion, don't describe that as "the sleep ray hits you" expose fuzz tester to wizard-mode as #debugfuzzer extended command monsters which cannot move due to boulders or walls try to escape intelligent monsters see and remember when others trigger traps -random figurines are of harder monsters +random figurines are of harder monsters by 5..10 difficulty points +statues for statue traps are of harder monsters too, but only by 3..6 monsters can blind you with a camera lit candles generated by wishing could have wrong light radius better feedback from detect unseen @@ -1067,7 +1078,7 @@ phrasing of the "you can't" message when attempting to name any monster to remove the existing name without assigning a new one some instances of using up or stealing shop-owned boulder didn't charge for it when hero hears an unseen monster reading a scroll, only describe the monster - acccurately if hero is not hallucinating and monster is same species + accurately if hero is not hallucinating and monster is same species as hero's current form don't allow monsters to disarm hero with bullwhip if hero is engulfed teleporting an object out of a shop put it on the shop bill instead of dealing @@ -1112,7 +1123,7 @@ giants occasionally get a battle axe or a two-handed sword give gremlin the property it stole, if possible 'F'orcefighting with a war hammer has a small chance of breaking iron bars player assigned name for monsters, specific objects, or object types could be - longer than what was intented to be allowed; for 'curses', much longer + longer than what was intended to be allowed; for 'curses', much longer very rarely random items are generated eroded, erodeproof, or greased Nazgul can see invisible fix a case where punished iron ball yanked hero on top of a monster @@ -1126,7 +1137,6 @@ give feedback when some types of damage are avoided due to MC (aka negation) feedback if a named, shape-shifted vampire reverted to original shape rather than dying when engulfed could say "Dracula turns into Dracula" adjust archeologist and valkyrie starting intrinsics -once per game if receiving killing blow from near-full hp, leave 1 hp spell of knock can knock back small monsters protection from shape changers now prevents the Wizard from mimicking monsters having worn levitation boots removed and destroyed (scroll, monster spell, @@ -1153,6 +1163,8 @@ prevent hug attacks and touch or engulf attacks for wrap, stick-to, and vortices, a few others) or against worm tails wand of speed gives temporary speed, potion gives intrinsic some monsters (riders, shopkeepers, priests, quest leader) can break boulders +if hero owes shop for a boulder and shopkeeper breaks it, continue to bill hero + for the boulder rather than switching to the resulting rocks corpse-eating monsters will go out of their way to eat corpses on the floor warnings via impossible() would be unseen if message suppression via ESC at --More-- prompt was in effect @@ -1198,12 +1210,301 @@ skip sanity_check handling when current command is ^P, otherwise it might applying a cream pie (always) or lump of royal jelly (sometimes) would use up the object and then access its memory after that had been freed keep track of hero's pending movement points across save and restore +give feedback if hero sees a monster become hidden under an object or water +hero might hear unseen monster read scroll of create monster or scroll of + teleportation; when it was create monster, player was given a chance + to call it something but not when it was teleportation, allowing the + player to deduce which type of scroll it actually was +wizard mode wish for terrain while on a fountain|sink spot made the counter + for number of fountains|sinks become one too big; would affect level + sound messages if all fountains|sinks were eventually destroyed +add a level arrival region to the Gnome King's Wine Cellar variation of the + Mines' End level so that hero can't end up in the treasure chamber +make potion of water become discovered if dipping a carried container into an + uncursed one reports that water gets inside or slides right off +a monster which was temporarily asleep wouldn't be affected by taming (either + food or magic), but one that was indefinitely asleep would be; when + tamable via food, it even caught and ate the food without waking up +in wizard mode, #terrain has offers to view all map locations as single- + letter codes corresponding to the levl[][].typ numbers and to view + a legend showing letter-to-type correspondence; adding new type + "lava wall" didn't update that legend and misdescribed all the types + which have higher values +restore the ability for trap creation via magic which creates pits to destroy + 'furniture' +allow #sit while flying over a squeaky board trap to trigger it +weight of statues of wraiths and of monsters which never leave a corpse was 0 +when a werecreature in human form attacked hero, it could transform to critter + despite hero having the Protection_from_shape_changers_attibute +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 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 + corpse and be turned to stone +ring of hunger prevents choking on your food +paranoid_confirm:pray can be changed to require yes/no response instead of y/n + by also setting paranoid_confirm:Confirm +wand of probing reveals map locations and traps in the ray path; also reveals + secret doors and secret corridors if the ray hits such +wand of probing zapped downward while hero is over water shows items under that +wand of probing reveals tin and egg contents +applying a wielded, lit potion of oil to unlight it while other unlit + potion(s) of oil were quivered would trigger panic + "addinv: null obj after quiver merge otyp=N" where N is POT_OIL +casting stone-to-flesh at self turned wielded or quivered rocks into unwielded, + unquivered meatballs, merging stacks if there were some in each slot +the throne room on the Samurai quest home level lacked a throne but gave + "You enter an opulent throne room!" message the first time it was + entered; vary the message rather than add a throne +coughing due to stinking cloud wakes up nearby monsters +stop occupation when timed levitation or choking issues a message +use #monster to make dragon steed breathe +steam vortices and fog clouds leave steam clouds behind +fog clouds maintain any clouds they are in, even poisonous ones +bone devils summon skeletons +adjust wand of make invisible and potion of invisibility effects +barbed devils have an attack that sticks you to them +balrogs prefer bullwhip if hero is wielding a weapon +ice devils have an additional attack, a slowing touch +buff scroll of confuse monster and blessed potion of monster detection +if a temple was entered while blind, #overview could show a line of just "." + when describing altars if no other interesting features on that level + were known +rolling boulders ignored walls and trees +shopkeepers consider the monster type when charging for tins, eggs and corpses +separate level flags premapped and sokoban +hero had worn amulet of magical breathing become unworn during theft by nymph, + dropped it to be able to crawl out of water, so it wasn't there when + game tried to transfer it to thief, triggering an "object lost" panic +digging down on a magical trap causes it to explode +thrown potion of sickness now bypasses target's innate magic resistance but + only affects current hit points, no longer also reducing maximum HP +lightning has a small chance of melting iron bars +make spell menu work with repeat +when attempting to look up a named fruit in data.base, try harder +set and check mon->mstate flags more consistently +fix sanity error when cloud was created over an engraving +docall naming of the monster that currently has the hero swallowed by + using their visible interior +give missiles thrown or shot by monsters the same chance to get used up upon + hitting as when thrown or shot by the hero +change menu_headings to also accept a color or color&attribute +issue feedback for encumbrance more consistently during pickup up +if hero destroys an altar by breaking a wand of digging or applying a drum of + earthquake, provoke divine wrath +if hero destroys a shop wall by breaking a wand of digging or applying a drum + of earthquake, have shopkeeper demand payment +mimics sometimes woke up and came out of hiding when they shouldn't have if + hero without protection from shape changers returned to a previously + visited level (though they would usually rehide so it wasn't obvious) +Wizards no longer have a bonus to writing unknown spellbooks, but now + learn what spellbooks look like as they gain skill in their + spell schools (allowing a guaranteed write with enough skill) +starting inventory: magic markers are more likely (guaranteed for + Wizards), but have fewer charges +early dungeon (pre-Sokoban) generates extra useful survivability items +potion of healing is much more common, and has a unique price +alchemy may affect only a portion of the dipped potions if a large + stack is dipped (especially if that stack is diluted) +monster kills can no longer deathdrop comestibles (other than the + monster's corpse) unless the monster collects food or + generates with food +about 10% of the heroes will now be left-handed; track the handedness of the + hero in the 'you' struct +if a monster fled from hero by intentionally jumping into a vault teleporter, + it would teleport randomly instead of into the vault, and if the + teleport trap's niche wasn't mapped yet, the trap would become mapped + but the spot would remain a secret corridor and not become accessible +boomerang travels in a clockwise arc when thrown by a left-handed hero and in + a counterclockwise arc when thrown by a right-handed hero +spellbooks weight 50 units but Book of the Dead only 20, and novels only 1; + the Book of the Dead has been changed to 50 and novels to 10 +save and restore hero tracks, increase track length +change vrock and hezrou from red to green, adjust vrock tile to have green +change wolf and werewolf to grey, warg to black +change [master] mind flayer, the Wizard, and the riders to bright magenta +change Nalzok and Minion of Huhetotl to bright red +walking into a shopkeeper tries to pay the bill +show billed items in a menu when paying with non-traditional menustyle +prioritize paying shopkeeper next to you even if multiple are detected +avoid impossible "trapped without a trap (fmon)" from 'sanity_check' when a + drum of earthquake made a pit at a monster's spot and pit creation + caused adjacent pool/moat/lava to flood that spot; if a non-floater, + non-flyer monster survived that, it got marked as trapped even though + flooding deleted the trap +demons cannot be frightened by showing them their reflection +HP regeneration formula has changed, primarily to be less fast in the endgame +riding negates stealth unless the steed is flying +previous hero rising as undead in bones retains intrinsics +level temperature affects monster generation +amulet of unchanging cannot be polymorphed +wishing for a "lit candle" provided one, but the feedback as it was added into + invent was "partly used candle (lit)" because of how 'lit' timer works +don't fall off steed because of Fumbling if saddle is cursed +cursed welded quarterstaff doesn't prevent spellcasting +wielded quarterstaff gives a small spellcasting bonus +if polymorphing a potion turned it into oil, dipping potions produced oil + randomly, or a horn of plenty produced a magic potion and converted + it into oil, the resulting potion of oil had its age (amount left) + set of the original potion's age (turn it was created) and could burn + for an arbitrarily long time if applied; conversely, polymorphing oil + into non-oil kept its relative age (no noticeable effect though) +an engulfer capable of passing through iron bars could do so even when hero + was engulfed +some uses of mons[obj->corpsenm] weren't excluding obj->corpsenm==NON_PM (-1) +if a covetous monster tried to teleport next to the hero but the level was + completely full, it would be booted off the level (set to migrate + back if the hero left and returned) instead of just staying put +if loadstone was unIDed but had been assigned a type name and you failed to + pick one up, the message referred to it as "gray stone" rather than + "stone called " +when a vision blocking gas cloud dissipated, the screen didn't necessarily + get updated to show newly visible locations in a timely fashion +if confused #loot while on a throne moved whole stack of quivered gold into + 'coffers' chest, the gold wasn't unworn from quiver slot, potentially + leading to crash when quiver was subsequently accessed +when filling quiver slot, don't bother asking "what do you want to ready?" if + invent is empty +no secret doors or corridors on the first two dungeon levels +the number of items destroyed by elemental effects is based on the damage +randomize the spellbook in the second level of vlad's tower +monsters weren't randomly generating if exactly one square was outside LOS, + and it contained a monster +when a spellbook was polymorphed into a novel and then incrementing spestudied + field turned it into a blank spellbook, the novel's title would stick: + "{spellbook of blank paper|plain spellbook} named " +walking on ice can make you slide in a random direction +if an adjacent statue was a in a pit, you could break it with a pick-axe even + though you're conceptually at the wrong elevation to reach it +using '#adjust c c' to collect all invent items compatible with the one in + slot c prefixed the inventory update message with "Merging:" rather + than "Collecting:" if there was at least one compatible stack; when + there weren't any compatible stacks and it was effectively a no-op, + the prefix used was "Collecting", lacking its intended colon +using '#adjust c d' or '#adjust d c' after splitting slot c via '#adjust Nc d' + for N less than c's stack size swapped c and d instead of re-merging + them even though merging was the intended behavior (the 3.6 change + that caused this was intended to avoid collecting other compatible + stacks while still merging the two specified ones) +ring of aggravate monster increases the level difficulty +if hero shattered an unseen monster's weapon, the [also unseen] weapon was + described in the feedback +don't self-genocide if a monk picks "master-lich" or "masterlich" as target + (was matching "Master" rank) +Sunsword can be invoked to create a blinding ray +Excalibur is much harder to get if hero is not a knight +pets considered any noise made by hero made as whistling +silent monsters in stinking clouds don't cough +unblind telepathy range depends on number of telepathy granting items worn +gold thrown or kicked at a sleeping monster with the 'greedy' attribute gets + caught instead of being treated as an attack but the catch message + neglected to report that target monster was awakened in the process +hero movement affects the water bubble movement direction +pets and peacefuls avoid a location hero just kicked +pets avoid a possible boulder pushing location in sokoban +shopkeepers bill you for using their bear trap or land mine +when engraving with a stack of eligible weapons, split one off the stack and + dull it rather than dull the whole stack +when engraving with a stack of cursed weapons, treat it differently if that + stack is welded to hero's hand: write in dust and leave whole stack + welded rather than split one off stack to engrave on floor +hero or monster didn't fall into pit if water at the location was boiled away +shopkeepers magnified the cost of shop items used up as a group; normally + using an item splits 1 from its stack, but applying a stack of N + candles to light them all charged N * N * single_candle_price +the tile for Ixoth (knight's quest nemesis) depicted a demon rather than a + dragon; change it to be a red dragon +untrapping containers or doors gives some experience +only honor DEBUGFILES (for activating debugpline() calls) when in wizard mode +if hero is on scroll of scare monster or Elbereth, werecreature switching from + human to beast form or general monster polymorphing into a susceptible + form will become scared right away instead on its next move +'altmeta' option didn't work as intended when 'number_pad' was On; typing + "n ESC c" wasn't treated as "n M-c" because reading + the 'n' changed program_state.input_state from commandInp to otherInp, + preventing special ESC handling; not an issue when number_pad was Off +a 3.6 fix to avoid a potential "object lost" panic when drinking potions had + unintended side-effect of making used up potions on shop's bill become + separate bill entries all with count 1 despite having come from same + unpaid stack; affected Ix inventory listing and itemized shop billing +buying shop items which include any unpaid ones inside containers would reveal + them even when the containers hadn't been opened (obj->cknown==0); + recent change to pay via menu made the problem become more visible; + shopping has been changed such that buying anything that is inside a + container requires that the whole container be bought as a unit +using #loot -> 'i'n to put multiple items into a shop-owned container would + ask whether to sell each item to the shop, and was prepared to accept + 'a' to sell the current one plus all the rest beyond it, or to accept + 'q' to not sell the current one or any beyond it, but the sell vs + don't-sell state was being reset for each item so 'a' and 'q' didn't + stick beyond the current one +join wall "spines" with walls of water and lava +some theft messages by nymphs force "she", others use default monster naming + which yields "it" when unseen; change the latter to "someone" which + still differs from "she" in a series of messages but isn't as jarring +if a nymph stole worn armor and got killed (perhaps by pet) before hero's next + turn, feedback would be "You finish taking off your suit." regardless + of the type of armor being taken off +when setting an option interactively [via O or 3.7's mO], if the particular + option uses a prompt to get a line of input (for compounds: 'fruit', + 'scores', most numeric ones), input was treated as a comma-separated + list of option[:value] rather than just the new value of that option +when there was a trap on a no-dig level, the floor beneath it was always + "too hard to dig into", making it impossible to remove the trap +the #terrain command didn't know how to cope with visible gas/cloud regions; + treat as traps as far as player choice of whether to show or hide; + if/when a spot contains both region and trap, show the trap +region expiration reported "the gas cloud around you dissipates" even when the + hero was swallowed +region expiration could report "the gas cloud around you dissipates" and also + "you see a gas cloud dissipate" for the same cloud spot +reduce shopkeeper's innate speed from 18 to 16 so that a hasted shopkeeper + doesn't always get 2 moves per turn +when a secret corridor was discovered by wand of secret door detection or by + wizard mode ^E and converted into a regular corridor, if there was a + formerly embedded object at the spot, presence of the object would be + forgotten unless within range of a light source +when poly'd into a giant, kicking a closed door always succeeds in breaking it +reduce crystal plate mail weight +interactively setting a status highlight for hunger with 'O' and choosing + 'text match' could crash while setting up the menu of hunger status + value strings; happened for curses or if the program was built to + 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 Fixes to 3.7.0-x General Problems Exposed Via git Repository ------------------------------------------------------------ incrementing EDITLEVEL to invalidate incompatible save files was not working - as intended because VERSION_COMPABILITY was defined as 3.7.0-0 and up + as intended because VERSION_COMPATIBILITY was defined as 3.7.0-0 and up rather than as 3.7.0-N for the current EDITLEVEL 'N'; undefine it fix compile when DLB isn't defined urealtime.realtime was being incorrectly calculated @@ -1224,10 +1525,10 @@ if running and Blind or Stunned or Fumbling or Dex < 10, encountering a closed repeatedly until eventually interrupted by approaching monster or hunger or ^C data.base lookup of an entry with any blank lines would falsely claim that - "'data' file in wrong fromat or corrupted" after some extra checks + "'data' file in wrong format or corrupted" after some extra checks were added while investigating tab handling anomalies using nhl_error() to report a Lua processing problem would clobber the stack -level teleporation's "You materialize on a different level!" could be given +level teleportation's "You materialize on a different level!" could be given out of sequence with other arrival messages more sequencing: if wielding Sting or similar and level teleporting to a level with different warning effect, the start-glowing or stop-glowing @@ -1298,7 +1599,7 @@ the checks and handling for fountains, sinks, and drawbridges were being missed during liquid_flow monster movement flags unification allowed displacer beasts to displace Riders a long worm with no visible segments (but one internal segment) might trigger - warning: tail 'segement' at <0,some_y>, worm at if teleported + warning: tail 'segment' at <0,some_y>, worm at if teleported adding displacer beast inadvertently introduced a regression in swapping with pets, allowing them to be pulled into water by hero on/over water splitting #if MAIL into #if MAIL_STRUCTURES and #if MAIL made it possible to @@ -1401,7 +1702,7 @@ an item thrown or dropped while swallowed was treated as being picked up by an unseen monster so object fields {known, dknown, bknown} got cleared segfault if gremlin fled weaponless hero wearing gold dragon scales/mail 'disambiguate WHACK' patch affected field layout of objects[] but EDITLEVEL - wasn't incremented, allowing incompatable save files to be restored + wasn't incremented, allowing incompatible save files to be restored restore the boulder exception for piletops that had been unintentionally removed during the expanded-glyphs changes misaligned potion colors due to lack of reset_glyphmap() following obj shuffle; @@ -1413,14 +1714,14 @@ unpaid shop-owned glob that shrank to nothing had weight 0 which triggered onbill warnings when 'sanity_check' was On; for 'Ix' and itemized billing, the empty weight was shown to player if 'wizweight' was On if 'showrace' was On and invisible hero couldn't see invisible so didn't see - self, the color of wharever could be seen underneath changed to white + self, the color of whatever could be seen underneath changed to white in wizard mode, a poly'd priest/priestess attempting to polymorph into priest or priestess in order to rehumanize became a new man or woman instead because "priest" matched "aligned cleric" rather than the role monster if a glob which has just shrunk away to nothing was wielded or quivered, unwield it before destroying it after changes to gender tracking for corpses and statues, when a unique - monster got turned into a statue if was erroneouly flagged as female + monster got turned into a statue if was erroneously flagged as female rather than 'historic' changing engraving to an occupation resulted in not dulling a weapon used to engrave a single character @@ -1432,7 +1733,7 @@ turning movement into commands broke the rest_on_space option; it also cursed scroll of light had special message when wielding Sunsword that didn't work for wearing gold dragon scales/mail giving a prefix keystroke other than 'm' prior to a command that doesn't use - prefixes was siliently ignored instead of being rejected + prefixes was silently ignored instead of being rejected prior revision broke all prefix usage movement command revamp broke 'm>' and 'm<' on stairs to avoid auto-pickup at the destination @@ -1451,8 +1752,8 @@ rearranging the feedback for first-kill and first-hit introduced a bug by other non-weapon hits for as long as never-hit conduct stayed unbroken counting "just picked up" items when deciding what pseudo-classes should be included for the 'I' command's object class prompt was operating on - an uninitialized varaible -changes to stair internals resulted in summoned Kops blockcading the stairs up + an uninitialized variable +changes to stair internals resulted in summoned Kops blockading the stairs up rather than intended stairs down dumplog's list of "major events" showed all logged events, not just major ones pickup via menu ignored player-specified count when picking up gold @@ -1483,7 +1784,7 @@ one-shot food testing ability conferred by blessed scroll of food detection worn ring of slow digestion blocks per-turn hunger but not the hunger caused by wearing it; white dragon scales/mail was blocking per-turn hunger and didn't cause any hunger itself; change to treat it like the ring -if the progarm was built with EXTRA_SANITY_CHECKS enabled, changing levels +if the program was built with EXTRA_SANITY_CHECKS enabled, changing levels while riding would give impossible warning "no monster to remove" the change to protect Rider corpses from being destroyed by exploding chests inadvertently prevented them from being used up when Riders revived @@ -1586,7 +1887,7 @@ anti-magic field's reduction of hero's maximum energy could result in current if hero was killed by a wand zapped by a monster, cause of death was reported as "killed by a bolt of imagined by " throttle the difficulty of the monsters in theme room "buried zombies" -buried troll whose auto-revive timer expired might triger panic with +buried troll whose auto-revive timer expired might trigger panic with "revive default case 6" throwing a helm of brilliance could yield "breaking odd object?" sanity checking of engravings was stopping after first problem found @@ -1600,6 +1901,193 @@ quest nemesis and a few other special monsters are suppressed from bones; running the tutorial until completion and returning to normal play but left the 'no followers' flag set; adjacent pets wouldn't tag along when hero moved to other levels +when a magic whistle moved a hidden pet it brought that out of hiding and + gave a message about "your appears" but the map was updated + while it was still hidden so nothing seemed to appear +entering the tutorial stashed hero's equipment for eventual return to normal + play but going down stairs to the second tutorial level returned it + too soon and allowed any items gathered on the first level to be kept +entering the tutorial disables the Save command; exiting it is supposed to + re-enable that but the change to fix the second level broke that +entering the tutorial and then returning to play made the tutorial branch be + eligible for a portal destination via wizard's Eye of the Aethiopica +'hard helmet' was based on being metallic so overlooked crystal helmet +don't blame/credit hero for stinking cloud if dying quest nemesis releases one +Lua selection operations 'subtract' and 'xor' didn't always work as intended +accept 'true' or 'false' as value for des.object field 'trapped' +special level loader didn't check for bad coordinates supplied by .lua + file thorougnly enough +hero might not be credited with "entered Mine Town" achievement for the town + variations which treat the whole level as the town if that level gets + entered via falling or level teleport +in the theme room "water-surrounded vault", guarantee an item which can + be used to escape from the room in one of the four chests +pets capable of using items would pick up and wear cursed armor +if something breakable was set up as alternate weapon and the second of two + bare-handed hits succeeded, it would be broken and trigger impossible + "objfree: deleting worn obj" +strength cap for hero poly'd into an orc captain was 18/50 even though it is + 10/100 for Uruk-Hai and monster Uruk-Hai can grow into orc captains +status_hilite rule for critical-hp takes precedence over up/down/changed HP +livelog/#chronicle of multiple high priests reported their deaths as + "killed high priest" and "killed high priest (2nd time)" instead of + "killed high priest of Foo" and "killed high priest of Bar" +using wizard mode #wizkill outside the endgame followed by m^V to enter the + endgame without any intervening moves would result in impossible + "dmonsfree: 0 removed doesn't match N pending on " +dwarf/elf/orc/gnome hero killed by zombie would rise as human zombie for bones +paranoid_confirm:Autoall for menustyle:full has the test handling backward and + was treating 'yes' as no and 'n' or ESC as 'y' +applying a wielded lump of royal jelly but not picking something to rub it on + while other lumps of jelly were quivered would trigger a panic, + "addinv: null obj after quiver merge" (like applying lit potion of oil) +ceiling hider could become hidden when there was no ceiling, triggering a + warning if sanity_check was On +hero could be forced onto a monster's location if a level was full and an + engulfer was occupying an adjacent but inaccessible spot, engulfed + hero, then expelled hero while now occupying hero's accessible spot, + triggering sanity_check warning "you over monster" +experimental #saveoptions wasn't saving the set of cond_xyz options correctly; + if player viewed the 'm O' submenu for status conditions, they would + get saved even if not changed; otherwise, ones that came in from the + old RC file wouldn't be included in the new one +knockback or recoil while levitating could hurtle the hero through a wall of + water to the spot beyond unless it happened on the Plane of Water +if recoil from throwing an item while levitating sent hero into a wall of + water, the thrown item got placed in that water rather than at its + intended destination +include '-' as suggested item in "what to dip?" prompt if dipping at pool or + fountain or sink with slippery hands +don't attempt a second hit for bare handed/martial arts if wearing a shield +when using 'm #overview' to annotate a level other than current the one, + include level number, and dungeon branch if not current one, in the + prompt instead of generic "this level" +when using 'm #overview' in the endgame, don't include non-endgame levels +when a tethered aklys gets caught in a web, make sure it doesn't return + and that the display of the tether gets cleaned up +add 'X' as a potential context-sensitive item-action for uwep and uswapwep +monsters weren't noticing hero's possession or lack of magic resistance when + hero getting zapped by a wand of striking was or wasn't resisted +discard monsters' observations about hero's resistances when saving bones +autopickup while levitating shares code with 'mention_decor'; after being told + about ice covered by an object, moving over some other object later + when mention_decor is Off could give "you are back on solid ground" + long after leaving the ice +duration of temporary stoning resistance would be extended while eating a + cockatrice corpse, as intended, but not while eating Medusa's corpse + so if it timed out during the meal, player got the message about hero + no longer being protected against stoning but was able to finish the + meal safely +farlook at something that was on top of an engraving or grave would report + the engraving text or headstone epitaph along with the 'something' +when carrying 52 items (excluding gold), hero couldn't pick up a thrown, + stolen, or dropped item which was split off from a still-carried stack +supply chests on early levels weighed the same as empty chests until something + was added or removed +stacked pair of potions of healing in supply chest weighed same as one potion; + drinking one of the two gave a healing message, then both had their + weight updated as their stack was split; if close to an encumbrance + threshold, new weight would trigger an encumbrance increase message, + immediately followed a decrease message when the potion got used up +bounds checking for object name formatting inadvertently introduced a change + that hid BUC prefix for charged rings +debug fuzzer was triggering out of bounds array access in loseexp() if + life-saving at level 1 used blessed restore ability to regain lost + levels and restored those all the way to level 30; introducing an + assert(u.ulevel < MAXULEV) changed bounds issue to assertion failure +strength less than 25 was unintentionally being capped at 18/07 +if an amulet of flying got stolen while hero was over lava without other + protection against lava, it would be destroyed even though it's not + flammable, then attempting to format it for messages would trigger an + impossible warning about "glorkum", then a crash occurred +poison gas breath from green dragon or iron golem that hit and was reflected + left target enveloped in a gas cloud (reflection now takes precedence; + somewhat odd though: gas that misses still leaves target enveloped) +Warning didn't show nearby monsters who were inside poison gas/steam/smoke + regions +map didn't show adjacent monsters if they were inside a gas region unless + they were seen via telepathy or extended monster detection, but they + were described as if visible in messages (combat, for instance) +OPTIONS=symset:blank and symset:plain didn't load the specified symbols if + used from the RC file or NETHACKOPTIONS +for OPTIONS=symset:blank, S_engroom (engraving on a room floor spot) and + S_engrcorr (engraving on a corridor spot) retained their default + symbols ('`' and '#', respectively) instead of switching to space +setting perm_invent in RC file before setting windowtype (whether later in + that file or via NETHACKOPTIONS) could result in it staying False + depending upon whether the default windowtype supported it +if hero was wielding potion(s) of unholy water and a magic trap gave its + remove curse effect, scroll of remove curse would become discovered +object discoveries during the tutorial carried over when normal play resumed; + counts of monsters created and vanquished carried over too +end-of-game attribute disclosure in wizard mode reported incorrect text for + for apron/alchemy smock's conferral of poison+acid resistances + (3.6.x was susceptible to this for T-shirt text but since T-shirts + don't confer any attributes, it wasn't noticeable) +if a tame or peaceful monster was trapped and blind hero hadn't seen the trap + yet, attempting to swap places would report "You stop. can't + move out of that trap." and if the 'tips' option was On, #untrap would + be suggested; but #untrap would fail due "you know of no traps there" +camera flash hitting a hidden mimic mentioned it as being a mimic but didn't + bring it out of hiding +when chain lightning hit a sleeping monster, the victim didn't wake up +on medusa-4, Medusa and the downstairs were located in different chambers +on medusa-3 and medusa-4, Perseus's statue was located in a different chamber + from Medusa +omit "a" from "drowned in a limitless water" when drowning on Plane of Water +restoring a save file could trigger impossible "rnd(0) attempted" if it tried + to make an unhidden mimic hide as an object; if the code was marked as + a released version, it would crash due to divide by 0 error instead +fix regression of a post-3.6 fix: if 2 Wizards of Yendor were in play and 1 + escaped the dungeon, bookkeeping for current number of Wizards stayed + at 2, interferring with future Wizard behavior +sometimes a repeat count from the preceding command carried over to most + recent one when using do-again (^A); if the most recent one was an + extended command, the spurious repeat was for '#' +if peaceful monsters react when seeing hero attack a peaceful monster, don't + have quest guardians run away; also, quest leader only becomes angry + if the monster being attacked is a quest guardian +farlook of water/lava location listed wall of lava before molten lava; because + of that, lava was omitted ("molten" suppressed to reduce vebosity, + resulting in "lava" which got skipped as substring of "wall of lava") +having #terrain display gas cloud regions as if they were traps didn't work + for monsters in such regions that are shown when adjacent to hero or + sensed via ESP +when a monster within a gas cloud was displayed on the map because the hero + was next to it, it remained displayed if hero moved away +eating a pyrolisk egg on the floor triggered an "object lost" panic +core object creation and the curses interface's window handling both became + confused by the 'pauper' option/conduct because they assumed that + invent being Null meant that the game hadn't started yet +wizards were discovering unread spellbooks whenever any skill was advanced; + do so only when a spell skill is advanced +if tips were enabled, the getpos/farlook tip clobbered the initial prompt (at + least for tty's one line message window), so reissue the prompt after + the tip has been displayed +a post-3.6 change to try to cope with invalid light source data when restoring + corrupted save file caused "panic: relink_light_sources" for valid + save file if saved while hero was poly'd into a light emitting monster +priestname() didn't handle "a" vs" "an" prefix correctly; normally priests are + named as "the priest of " but hearing an unseen priest read a + scroll doesn't force "the" and yielded "You hear an priest of + incant ." +non-fireproof water walking boots wouldn't be burnt up if fire resistant hero + walked on molten lava +1x1 poison gas clouds for green dragon and iron golem breath could be placed + 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?" Fixes to 3.7.0-x Platform and/or Interface Problems Exposed Via git Repository @@ -1638,6 +2126,11 @@ curses: typing ESC to cancel something issued a beep; if the terminal was set curses: with window borders on and align_status:left, restoring brought up a screen where the message window's border wasn't shown; once it needed to be scrolled to fit a new message, the border appeared +curses: after changing curses_mark_synch() to do something, making a menu + selection by entering a count that uses more than 1 digit caused the + menu to vanish +curses: setting up default roguesymset at startup clobbered primary symset if + config file picked Enhanced1 for primary (core bug exposed by curses) Qt: at Xp levels above 20 with 'showexp' On, the combined status field "Level:NN/nnnnnnnn" was too big and truncated by a char at each end Qt: searching a text window for something that wasn't found and then searching @@ -1652,6 +2145,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 @@ -1667,8 +2161,15 @@ tty: changes to support TTY_PERM_INVENT resulted in blank inventory menu when tty: for the !DEF_PAGER configuration, redraw screen properly if attempting to display a file fails and tty_display_file()'s caller requests feedback for that situation +tty: if a group accelerator matched a menu command ('^' in menu for '/') + it wouldn't work to select, just to manipulate the menu +tty: after resize changes which included screen erasure changes, ^C and yes|y + to "Really quit?" while a menu was open would lead to a panic +tty: make cursor visible again when using ! or ^Z to temporarily leave nethack Unix: after lua changes to Makefiles, 'make spotless' for dat subdirectory left some generated data files which should have been deleted +Unix: reject "--sX" on command line except if "X" is "cores" (so "--scores"); + single dash form still accepts "-sX" to lookup scores for "X" Windows: new tile additions in win/share did not trigger the creation of a new NetHackW.res file Windows: nhl_loadlua() was missing the RDBMODE argument on the [dlb_]fopen(), @@ -1679,6 +2180,11 @@ Windows: when VIRTUAL_TERMINAL_SEQUENCES was not defined, the preprocessing would fail on consoletty.c Windows: original fix for GLYPH_UNEXPLORED background tile merging resulted in a new issue where the map wasn't being cleared on level changes +Windows: don't dupstr the return value of setlocale if it is NULL +Windows: nethackw.exe calculated vertical spacing for menu items based on + the height of the tileset in use, even though the tiles in the + menu were drawn at the default height of 16 anyway; get rid of + the extraneous vertical spacing between menu rows X11: fix build failure if STATUS_HILITES is disabled X11: was still initializing map to 'stone' instead of 'unexplored' after they became separate glyphs @@ -1691,6 +2197,8 @@ X11: use visctrl(response) when X11_yn_function() echoes prompt+response in X11: when trying to lookup scrollbars in order to handle scrolling via keys, menu handling always ended up finding the top window 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+macOS: after the "bad Atom" fix (below), the persistent inventory window crept downward every time it got updated @@ -1728,11 +2236,32 @@ curses: fix an off-by-one error when deciding whether a long line on the bottom line of the message window can fit ">>" (curses' "--More--") curses: selecting a partial stack, unselecting it, and then selecting it normally did not reset the quantity to the whole stack +curses: if user's terminal was set to 'application keypad mode' (DEC VTxxx + nomenclature; set via 2 char "ESC =") and the terminfo or termcap + entry told the terminal to send 8-bit escape sequences (via 3 char + "ESC SPC G"), nethack wasn't recognizing number pad keys +curses: change petattr attributes, dropping support for curses-only ones +curses: swap the grey and no-color color initialization +curses+Qt: allow changing default colors with the 'palette' config option + (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 + so that they can be read before curses erases the screen +curses: when the map was narrower than the space set aside for it, wide popup + windows could write into the unused space and whatever was written + there would stick after the popup was removed +documentation: when building plain text Guidebook.txt from Guidebook.mn, avoid + attempting to use CR font; change doesn't affect building Guidebook.ps + which utilizes CR to get various instances of fixed-width text +documentation: improve Guidebook *roff formatting and prepare for use of + future release of groff 1.24.0 (pr #1280 by g-branden-robinson) macOS: Xcode project was failing to build if the path to the NetHack source tree contained a space; the issue was within some shell script code contained within the project -msdos: add -DSTATUES_LOOK_LIKE_MONSTERS to Makefile1.cross so the VESA mode +MS-DOS: add -DSTATUES_LOOK_LIKE_MONSTERS to Makefile1.cross so the VESA mode can display statue glyphs +MS-DOS: sample config file had same MENUCOLOR flaw as Windows (see below) +MS-DOS: implement inverse text attribute Qt: quit if can't load tiles file instead of continuing and then segfaulting Qt: [later] tiles load failure at startup now continues using an ascii map Qt: use more columns for extended command selection dialog so that the number @@ -1828,7 +2357,7 @@ Qt: force the 'toptenwin' option On so that high scores display at end of game Qt: during role/race/&c selection, update role titles with their icons, and also Valk eligibility, when gender is toggled Qt: if a menu of objects contains at least one iron ball, and player is not - alreadly in the midst of entering a count, recognize '0' as a group + already in the midst of entering a count, recognize '0' as a group accelerator rather than the start of a count Qt: {maybe just Qt+macOS:} when viewing a text window ('V' to look at 'history' for instance), clicking on [Search], entering a search target in the @@ -1836,6 +2365,12 @@ Qt: {maybe just Qt+macOS:} when viewing a text window ('V' to look at 'history' window got pushed underneath the main window so seemed to go away Qt: use idPressed signal instead of buttonPressed and mappedString instead of mapped for recent Qt (pr #913 by chasonr) +Qt: make all NetHack menu and text windows modal +Qt: support Unicode supplementary characters (pr #1047 by chasonr) +Qt: qt_tilewidth and qt_tileheight are allocated with alloc() and need to be + freed with free(); don't use qt_tilewidth and qt_tileheight after + they have been freed; instead, retrieve the size from the glyphs + object (pr #1049 by chasonr) Qt+macOS: fix control key (fixed all except for ^V); handle ^V as a shortcut Qt+macOS: rename menu entry "nethack->Preferences..." for invoking nethack's 'O' command to "Game->Run-time options" and entry "Game->Qt settings" @@ -1849,6 +2384,20 @@ Qt+macOS: since menu entry help->"About Qt NetHack" gets hijacked and becomes which stays where intended and brings up the same information Qt+macOS: suppress unwanted "Search [______]" action from being inserted as the first entry in the menubar's "Help" dropdown menu +sound: add a soundlib interface that consists of a specification + documented in doc/sound.txt; default 'nosound' soundlib implementation + of the specification in src/sounds.c +sound: added 'macsound' soundlib implementation for use on macOS; the + interface implementation resides in the objective C file + sound/macsound/macsound.m; built on top of the built-in AppKit + NSSound, #import , linked using -framework AppKit. +sound: added 'windsound' soundlib implementation for use on Windows; the + interface implementation resides in the file + sound/windsound/windsound.c; built on top of the win32 api, + header file mmsystem.h, linked with winmm. +sound: add 'qtsound' soundlib implementation for use on any platform + with Qt; the interface implementation resides win/Qt/qt_bind.cpp; + built on top of Qt. tiles: add indicator of thonged portion to aklys tile tty: role and race selection menus weren't filtering out potential choices which got excluded by OPTIONS=align:!lawful or !neutral or !chaotic @@ -1858,6 +2407,16 @@ tty: if a menu used full screen and brought up another menu that wasn't full it with most of the map left blank until after the menu was dismissed tty: have dismiss pick-none menus instead of acting like '>' (not only wouldn't dismiss when not on last page, wouldn't dismiss at all) +tty: menu search via ':' would clobber part of the menu with a status line + refresh if the menu was tall enough to cover that +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; 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) 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) @@ -1866,6 +2425,9 @@ Unix: work-around a build issue in ubuntu 21.10 by using ifdef to skip the define of warn_unused_result to empty string in tradstdc.h whenever __linux__ is defined during build unless GCC_URWARN is also defined 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 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 @@ -1879,6 +2441,8 @@ Windows: When there was no interesting background in get_bk_glyph() GLYPH_UNEXPLORED as a background tile to merge with the foreground tile, so check for GLYPH_UNEXPLORED and bypass that Windows: fix range error detected by address sanitizer in plselInitDialog() +Windows: the .nethackrc template contained a sample MENUCOLOR pattern for + "cursed .* (being worn)" which didn't work; parentheses need quotes X11: substantial overhaul of status display, both 'fancy' and 'tty-style' X11: extend fancy status one-turn inverse video status-change highlighting to hunger, encumbrance, and conditions @@ -1897,20 +2461,6 @@ X11: (possibly X11+macOS): try harder to resize the getlin() prompt, if needed, X11: set all selectable menu lines to the same length, left justified X11: initializing the get-extended-command widget modified memory beyond what it dynamically allocated -sound: add a soundlib interface that consists of a specification - documented in doc/sound.txt; default 'nosound' soundlib implementation - of the specification in src/sounds.c -sound: added 'macsound' soundlib implementation for use on macOS; the - interface implementation resides in the objective C file - sound/macsound/macsound.m; built on top of the built-in AppKit - NSSound, #import , linked using -framework AppKit. -sound: added 'windsound' soundlib implementation for use on Windows; the - interface implementation resides in the file - sound/windsound/windsound.c; built on top of the win32 api, - header file mmsystem.h, linked with winmm. -sound: add 'qtsound' soundlib implementation for use on any platform - with Qt; the interface implementation resides win/Qt/qt_bind.cpp; - built on top of Qt. General New Features @@ -2057,7 +2607,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 @@ -2080,7 +2630,14 @@ reading a blessed scroll of light has a chance to improve bless/curse state of wielded Sunsword or worn gold dragon scales/mail similar to dipping those into holy water; cursed scroll has chance to worsen the state added a chronicle of major events, and optional live logging of those -paranoid:swim to prevent accidental dunking into dangerous liquids +paranoid_confirm:swim to prevent accidental dunking into dangerous liquids; + joins paranoid_confirm:pray as the default setting +paranoid_confirm:trap to confirm entering a known trap unless it is harmless; + like revised paranoid_confirm:pray, requires y/n response; add + paranoid_confirm:Confirm to require yes/no instead +extend 'paranoid_confirm:trap' to request confirmation when entering visible + gas cloud regions +paranoid_confirm:Autoall to confirm picking 'A' in menustyle:Full filter menu looking at a monster will indicate whether it is asleep, and waking up a monster yields a message extend farlook's ", asleep" to ", can't move (paralyzed or sleeping @@ -2113,6 +2670,8 @@ for ranger characters, shooting any type of arrow while wielding the Longbow change the #vanquished command from debug-only to general user command add 'sortvanquished' option to be able to set the preferred sort order without using 'm #vanquished' and to have it persist across save/restore +when sorting vanquished monsters by monster class, treat the Riders as a + separate class from major demons add #genocided command have 'I u' mention whether there are any unpaid items on the floor (unusual but not impossible); it doesn't itemize them or show shop price @@ -2121,13 +2680,89 @@ make music improvisations more varied and interesting, as well as useful give a helpful tip when first entering "farlook" mode add a boolean option tips to disable all of the helpful tips add a tutorial level -engravings appear on the map display +engravings appear on the map display; 'whatis' can list them ('/e', '/E') option to create the character deaf add conduct for petless preceding #overview with the 'm' prefix brings up the overview display as a menu allowing selection of any visited level to #annotate; it also shows every level visited rather than just the "interesting" ones include monster size in feedback for wand of probing and stethoscope +crystal armor is now subject to cracking damage rather than outright breakage +when a doppelganger takes on mplayer (fake hero) form, usually choose role + from the high scores file; name too if visible at the time +can now write unknown spellbook without need of Luck when spell is known; + if spell becomes known without having read its book (potential divine + gift), writing the book while the spell is still fresh will succeed +new status highlight rule type for hitpoints: "criticalhp" rule overrides + hit point rules if current HP is so low that prayer will consider it + to be major problem; applies to hitpointbar as well as HP status +high skill level in martial arts or bare-handed combat sometimes hits twice +answering '?' at the "genocide what?" prompt (for either monster class + or single species) runs the #genocided command to show what types of + monsters have already been genocided and then re-prompts +throwing the silver bell (or other invocation item, or the Amulet) to the + quest leader will identify it instead of being treated as an attack +applying gold pieces will flip one and report "heads" or "tails"; with a stack + of more than one, normally the flipped coin will rejoin the stack +during enlightenment and end-of-game disclosure, use contraction "n't" + for " not" +give feedback about the thaw status of ice terrain +allow #dip '-' in a fountain, pool, or sink to clean slippery fingers/gloves +allow #dip potions into a sink to get a hint about what they are, similar to + dropping rings into a sink; dipping other stuff causes water damage + so could be used to blank out scrolls and books +when restoring, show current level's player-applied annotation if there is one +have farlook of a grave report the engraving on the headstone if it's known +the '*' command (list inventory items in use) now shows items in a specific + order: amulet, ring on main hand (potentially affected by cursed + one-handed weapon), ring on off hand, blindfold, wielded weapon, + secondary or alternate weapon, quiver, suit, cloak, shield, helmet, + gloves, boots, shirt, lit lamps and candles, leashes attached to pets, + instead of by object class (sortpack) or inventory letter (!sortpack); + new perm_invent+perminv_mode==inuse does the same +allow the ')', '[', '(', '=', '"', and '*' commands to be preceded by the 'm' + prefix to force a menu where the player can pick an item and choose + a context-sensitive item action for it; some give a menu even without + the prefix and for those, same item selection and action can be used +hot ground (Gehennom / Plane of Fire) often destroys dropped potions +erinyes overhaul; they now attempt to punish heroes who have violated their + alignment and get stronger with greater alignment abuse +accessibility options to tell where on map a message happened (accessiblemsg), + to notify when a monster is spotted (spot_monsters), and when + a monster moved (mon_movement), when hero takes damage (showdamage), + when interesting map locations change (mention_map) +accessibility command #lookaround which will describe what hero can see +if hero is punished or tethered to a buried iron ball and has no inventory (or + only carries gold or embedded dragon scales or both), a nymph might + remove the chain when finding nothing else to steal +'showvers' option (and companion 'versinfo' option) to include program version + in the status display so that it will be visible for screenshots or + during streaming video +'query_menu' option to use a menu when asked certain yes/no questions +pyrolisk eggs explode when broken +pauper-option to start the character with no possessions +wand of secret door detection, spell of detect unseen, and wizard mode ^E now + flash the cursor at each location where detection finds something +saving-grace: once per game if receiving a killing blow from full or nearly + full HP, survive with 1 HP +livelog/#chronicle for saving-grace: if saving-grace prevents hero's death, + report it [at present, it uses the livelog classification for breaking + a conduct] +enlightenment/attribute disclosure for saving-grace: include a line for have + or haven't been saved (game in progress) or did or didn't get saved + (game over) via saving-grace +'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 Platform- and/or Interface-Specific New Features @@ -2145,6 +2780,7 @@ curses: if panictrace is triggered, reset the terminal before giving backtrace curses: if a message is marked urgent, override message suppression initiated by user having typed ESC at previous More>> prompt curses: implement a dialog for the windowborders option +curses: implement the windowcolors option Qt: the "paper doll" inventory subset can be controlled via the "Qt Settings" dialog box ("Preferences..." on macOS) Qt: draw a border around each tile in the paper doll inventory; when BUC is @@ -2185,11 +2821,15 @@ Unix: support --nethackrc=filename on the command line; same effect as NETHACKOPTIONS='@filename' but leaves NETHACKOPTIONS available for specifying options; --no-nethackrc is same as --nethackrc=/dev/null Windows: implement MSGHANDLER (pr #749 by argrath) +Windows: Add configuration to support Curses on WinGUI (pr #1028 by chasonr) X11: implement 'selectsaved', restore via menu of saved games X11: echo getline prompt and response (wishes, applying names) to message window and dumplog message history X11: fix map expose area, no longer leaving black bars on the map tty and curses: support italic as text attribute +tty and curses: when hitpointbar is displayed, make it blink if current HP is + below the "critical HP" threshold (where prayer treats being injured + as major trouble) NetHack Community Patches (or Variation) Included @@ -2219,7 +2859,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 @@ -2233,7 +2873,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) @@ -2260,9 +2900,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 @@ -2301,6 +2940,26 @@ add Unicode support to DOS Curses port (pr #889 by chasonr) add Unicode support to Win32 (pr #903 by chasonr) add Unicode support to Qt (pr #910 by chasonr) add Unicode, IBMgraphics and DECgraphics support to X11 (pr #923 by chasonr) +add some glue code for those that might wish to build with 3rd party fmod + sound library support (pr #962 by MrEveryDay98) +add new options 'nopick_dropped' and 'pickup_stolen' options to avoid + auto-pickup of items dropped by hero and to auto-pickup things + previously stolen from hero if moved upon while autopickup is On; both + bypass pickup_types and autopickup_exceptions (pr #1140 by entrez) +fix msg_window:combination and TTY getlin() ^P (pr #1155 by entrez) +remove a null sobj check from gold_detect that was situated after + dereferences had already been done (pr #1173 by argrath) and + adjust prototype in extern.h +check for valid indexes of def_monsyms[] and def_oc_syms[] in + choose_classes_menu() (pr #1179 by argrath) +split damage from acid potion into separate function (pr #1195 by argrath) +add swallow and explosion glyphs to glyph_to_cmap() (pr #1277 by ars3niy) +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) Code Cleanup and Reorganization @@ -2333,7 +2992,7 @@ options overhaul: moved the option definitions into include/optlist.h; for retrieving the option value, and for processing the option following its selection in the 'O' menu, added doc/options.txt file. function reglyph_darkroom() relocated from options.c to display.c -resurrect 'makedefs -m' to be able to derive default mons[].diffculty values +resurrect 'makedefs -m' to be able to derive default mons[].difficulty values suitable for assigning to new or changed monsters convert obj->oextra->omid from pointer to scalar get rid of unused obj->oextra->olong @@ -2426,4 +3085,12 @@ replace some old 'time_t' hackery in system.h and hacklib.c with something remove the per dungeon level door limit remove various '#if LINT' hacks used to suppress warnings from pre-ANSI 'lint' VMS: removed NO_VSNPRINTF conditional code +remove the window port get_menu_coloring() calls from all the existing window + ports, and implement the functionality on the core-side of the + interface prior to calling the window port's foo_add_menu() function +remove TEXTCOLOR build option +relocate general-purpose function choose_classes_menu(), from + options.c to windows.c +remove register from variable declarations +make glyph_to_cmap() a function instead of a macro diff --git a/doc/fixesX-X-X.txt b/doc/fixesX-X-X.txt index b6a0125774..4ce05819f5 100644 --- a/doc/fixesX-X-X.txt +++ b/doc/fixesX-X-X.txt @@ -4,7 +4,7 @@ General Fixes and Modified Features ----------------------------------- -Fixes to Post-X.X.X Problems that Were Exposed Via git Respository +Fixes to Post-X.X.X Problems that Were Exposed Via git Repository ------------------------------------------------------------------ diff --git a/doc/lua.adoc b/doc/lua.adoc index 8bf2a63d5e..00a21a4f08 100644 --- a/doc/lua.adoc +++ b/doc/lua.adoc @@ -249,6 +249,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. @@ -528,6 +537,19 @@ Example: des.engraving({x,y}, "engrave", "Foo"); +=== exclusion + +Exclude an area of the map from being randomly chosen target when +falling or teleporting into the level, or creating a monster. +Multiple exclusions per level are allowed. + +* type is one of "teleport", "teleport-up", "teleport-down", or "monster-generation". + +Example: + + des.exclusion({ type = "teleport", region = { 0,0, 10,5 } }); + + === feature Create a feature, and set flags for it. @@ -557,6 +579,21 @@ Example: des.finalize_level(); +=== gas_cloud + +Create a gas cloud. +The `damage` and `ttl` fields are optional. +Defaults to non-poisonous and infinite lifetime. + +Example: + + des.gas_cloud({ x = XX, y = YY }); + des.gas_cloud({ coord = { XX, YY } }); + des.gas_cloud({ selection = SEL }); + des.gas_cloud({ selection = SEL, damage = 5 }); + des.gas_cloud({ selection = SEL, damage = 5, ttl = 200 }); + + === gold Create a pile of gold. @@ -611,6 +648,7 @@ Set flags for this level. | graveyard | Treats the level as a graveyard level (causes graveyard sounds and undead have a reduced chance of leaving corpses). | icedpools | Ice generated with the level will be treated as frozen pools instead of frozen moats. | premapped | Map, including traps and boulders, is revealed on entrance. +| sokoban | Level has special Sokoban rules | solidify | Areas outside the specified level map are made undiggable and unphaseable. | inaccessibles | If inaccessible areas are generated, generate ways for them to connect to the "accessible" area. | noflip | Prevent flipping the level. @@ -755,7 +793,8 @@ The hash parameter accepts the following keys: | 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 +| 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: @@ -809,7 +848,8 @@ The table parameter accepts the following: | lit | boolean | Is the object lit? | eroded | int | Object erosion | locked | boolean | Is the object locked? -| trapped | boolean | Is the object trapped? (If the object is a statue, a statue trap will be created underneath it.) +| 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? @@ -851,7 +891,7 @@ Example: Create a room region, which can be irregular; use the boundary <<_map_characters,map character>> to restrict the floodfilled area. If using the first form with a selection and "lit", the lit area will extend -outward 1 space from the selection to attempt to accomodate adjacent walls, +outward 1 space from the selection to attempt to accommodate adjacent walls, regardless of whether they are actually walls or not. If using "unlit", this will not happen. @@ -972,7 +1012,8 @@ Example: === trap -Create a trap. The `launchfrom` is relative to the rolling boulder trap coord. +Create a trap. The `launchfrom` is relative to the rolling boulder trap coord, +but `teledest` is absolute. Example: @@ -981,6 +1022,7 @@ Example: des.trap({ type = "web", coord = {2, 2}, spider_on_web = false, seen = true }); des.trap({ type = "falling rock", victim = false }); des.trap({ type = "rolling boulder", coord = {7, 5}, launchfrom = {-2, -2} }); + des.trap({ type = "teleport", coord = {7, 5}, teledest = {2, 2} }); des.trap("hole", 3, 4); des.trap("level teleport", {5, 8}); des.trap("rust") diff --git a/doc/makedefs.6 b/doc/makedefs.6 index 50a8a16380..1360844730 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 .. @@ -9,7 +9,12 @@ .ds Nr \\$2 .. .NB $NHDT-Branch: NetHack-3.7 $ -.NR $NHDT-Revision: 1.18 $ +.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 @@ -112,11 +117,25 @@ file. .TP .B -s Generate the -.I bogusmon -, -.I engrave -and -.IR epitaph files. +.IR bogusmon ", " engrave ", and " epitaph " files." +.br +.TP +.B -1 +Generate the +.IR epitaph +file. +.br +.TP +.B -2 +Generate the +.IR engrave +file. +.br +.TP +.B -3 +Generate the +.IR bogusmon +file. .br .TP .B -h @@ -172,6 +191,11 @@ Turn on debug tracing for the grep function ( must be specified as well). .br .TP +.BI --grep-defined " symbol" +Exit shell true (0) if +.I symbol +is known and defined, otherwise exit shell false (1). +.TP .BI --grep-define " symbol" Force the value of .I symbol diff --git a/doc/makedefs.txt b/doc/makedefs.txt index 83df9ff9bf..e784946c3f 100644 --- a/doc/makedefs.txt +++ b/doc/makedefs.txt @@ -38,7 +38,13 @@ SHORT COMMANDS -q Generate the rumors file. - -s Generate the bogusmon , engrave and epitaphfiles. + -s Generate the bogusmon, engrave, and epitaph files. + + -1 Generate the epitaph file. + + -2 Generate the engrave file. + + -3 Generate the bogusmon file. -h Generate the oracles file. @@ -75,42 +81,46 @@ LONG COMMANDS 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 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: @@ -133,10 +143,10 @@ AUTHOR The NetHack Development Team COPYRIGHT - This file is Copyright (C) Kenneth Lorber, 2022 for version - NetHack-3.7:1.18. NetHack may be freely redistributed. See license + This file is Copyright (C) Kenneth Lorber, 2024 for version + NetHack-3.7:1.22. NetHack may be freely redistributed. See license for details. -NETHACK 8 February 2022 MAKEDEFS(6) +NETHACK 25 December 2024 MAKEDEFS(6) diff --git a/doc/mn.txt b/doc/mn.txt index 502a0f3bb6..1c42c2a04a 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 daed162838..df7fe58565 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/options.txt b/doc/options.txt index a0bed376b1..a07b316cc9 100644 --- a/doc/options.txt +++ b/doc/options.txt @@ -100,7 +100,7 @@ and the optfn_xxxx() function is expected to react and set the option based on the string values that parseoptions() passes to it. req get_val is passed a buffer from its caller that the optfn_xxxx() is -expected to fill with the current value of the opton. +expected to fill with the current value of the option. req do_handler is called during doset() operations processing in response to player selections, most likely from the 'O' option-setting menu. The diff --git a/doc/recover.6 b/doc/recover.6 index b94efda65d..3829760e98 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 95f05dc17a..e13d2995f1 100644 --- a/doc/recover.txt +++ b/doc/recover.txt @@ -22,50 +22,43 @@ 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). - - ^?ALLDOCS 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. ^: ^?INSURANCE For recovery to be possible, nethack must - have been compiled with the INSURANCE option (this configuration was), - and the run-time option checkpoint must also have been on. ^: This - configuration of nethack 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 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 - identity, locking, and, when checkpointing, for the game state. Vari- - ous OSes use different strategies for constructing the base name. - Microcomputers use the character name, possibly truncated and modified - to be a legal filename on that system. Multi-user systems use the + 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 + identity, locking, and, when checkpointing, for the game state. Vari- + ous OSes use different strategies for constructing the base name. + Microcomputers use the character name, possibly truncated and modified + to be a legal filename on that system. Multi-user systems use the (modified) character name prefixed by a user number to avoid conflicts, - or "xlock" if the number of concurrent players is being limited. It - may be necessary to look in the playground to find the correct base + or "xlock" if the number of concurrent players is being limited. It + may be necessary to look in the playground to find the correct base name of the interrupted game. recover will transform these level files into a save file of the same name as nethack would have used. Since recover must be able to read and delete files from the playground and create files in the save directory, it has interesting interactions - with game security. Giving ordinary players access to recover through - setuid or setgid is tantamount to leaving the playground world- - writable, with respect to both cheating and messing up other players. - For a single-user system, this of course does not change anything, so + with game security. Giving ordinary players access to recover through + setuid or setgid is tantamount to leaving the playground world- + writable, with respect to both cheating and messing up other players. + For a single-user system, this of course does not change anything, so some of the microcomputer ports install recover by default. For a multi-user system, the game administrator may want to arrange for - all .0 files in the playground to be fed to recover when the host + all .0 files in the playground to be fed to recover when the host machine boots, and handle game crashes individually. If the user popu- - lation is sufficiently trustworthy, recover can be installed with the - same permissions the nethack executable has. In either case, recover + lation is sufficiently trustworthy, recover can be installed with the + same permissions the nethack executable has. In either case, recover is easily compiled from the distribution utility directory. NOTES - Like nethack itself, recover will overwrite existing savefiles of the - same name. Savefiles created by recover are uncompressed; they may be - compressed afterwards if desired, but even a compression-using nethack + Like nethack itself, recover will overwrite existing savefiles of the + same name. Savefiles created by recover are uncompressed; they may be + compressed afterwards if desired, but even a compression-using nethack will find them in the uncompressed form. SEE ALSO @@ -73,17 +66,17 @@ SEE ALSO BUGS recover makes no attempt to find out if a base name specifies a game in - progress. If multiple machines share a playground, this would be + progress. If multiple machines share a playground, this would be impossible to determine. - recover should be taught to use the nethack playground locking mecha- + recover should be taught to use the nethack playground locking mecha- nism to avoid conflicts. COPYRIGHT - This file is Copyright (C) Kenneth Lorber, 2022 for version - NetHack-3.7:1.12. NetHack may be freely redistributed. See license + 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 30498a58a6..2f90d33347 100644 --- a/doc/sound.txt +++ b/doc/sound.txt @@ -1,4 +1,4 @@ -NetHack 3.7 sound.txt $NHDT-Date: $ $NHDT-Branch: $:$NHDT-Revision: $ +NetHack 3.7 sound.txt $NHDT-Date: 1693253363 2023/08/28 20:09:23 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.7 $ Introduction @@ -282,7 +282,7 @@ gc.chosen_soundlib ga.active_soundlib a usersound mappings reference -iflags.sounds is the master on/off swith to control whether any audio +iflags.sounds is the master on/off switch to control whether any audio is produced by the soundlib interface iflags.voice is the master on/off switch for voices produced by @@ -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.n b/doc/tmac.n index 270a78a06f..4effecdc6b 100644 --- a/doc/tmac.n +++ b/doc/tmac.n @@ -1,4 +1,4 @@ -\" @(#)$Id: tmac.n,v 1.4 2002/01/19 13:41:15 michael.allison Exp $ +.\" @(#)$Id: tmac.n,v 1.4 1989/03/15 Matt Bishop .\" The News macro package .\" .\" This is the macro package that is used to format news documents. It @@ -761,4 +761,3 @@ .\" couple of miscellaneous requests .bd S 3 3 \" embolden special font chars if B .hy 2 \" don't hyphenate last lines - diff --git a/doc/tmac.nh b/doc/tmac.nh index 1a7ae12314..86bc643b44 100644 --- a/doc/tmac.nh +++ b/doc/tmac.nh @@ -8,6 +8,10 @@ .\" cluttered as their number increased. It now uses the '.so' directive .\" to include this file. (tmac.n is passed to 'roff on the command line.) . +.\" Protect against being sourced twice. +.nr nH +1 +.if \n(nH>1 .nx +. .\" labeled paragraph start .\" .PS word .\" set the width for the label column @@ -85,7 +89,8 @@ . if \\n(id=0 \{\ . di \" end diversion . fi \" resume filling -. in -\\n(piu \" dedent +. ie \\n(.i<\\n(pi .in 0 +. el .in -\\n(piu \" dedent . ev \" pop environment . ne \\n(dnu \" be sure you have room . nf \" don't reprocess display @@ -118,7 +123,7 @@ .\" $1 - repeat count for amount of padding (optional; default is 1) .de BR .ie \\.$==0 .nr bR 1v -.el .nr bR (\\$1-0)v +.el .nr bR 0\\$1v \0 .sp \\n(bR .br @@ -129,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 d3af425605..45593eb88e 100644 --- a/doc/window.txt +++ b/doc/window.txt @@ -124,10 +124,13 @@ putstr(window, attr, str) Multiple putstr()s are output on separate lines. Attributes can be one of ATR_NONE (or 0) - ATR_ULINE ATR_BOLD + ATR_DIM + ATR_ITALIC + ATR_ULINE ATR_BLINK ATR_INVERSE + If a window-port does not support all of these, it may map unsupported attributes to a supported one (e.g. map them all to ATR_INVERSE). putstr() may compress spaces out of @@ -158,10 +161,13 @@ putmixed(window, attr, str) Multiple putmixed()s are output on separate lines. Attributes can be one of ATR_NONE (or 0) - ATR_ULINE ATR_BOLD + ATR_DIM + ATR_ITALIC + ATR_ULINE ATR_BLINK ATR_INVERSE + If a window-port does not support all of these, it may map unsupported attributes to a supported one (e.g. map them all to ATR_INVERSE). putmixed() may compress spaces out of @@ -226,7 +232,10 @@ print_glyph(window, x, y, glyphinfo, bkglyphinfo) unsigned glyphflags; /* more detail about the entity */ -char yn_function(const char *ques, const char *choices, char default) +char win_yn_function(const char *ques, const char *choices, char default) + -- Not to be confused with the core yn_function() + added in 3.4.2 and no yn_function() macro is defined + for this. -- Print a prompt made up of ques, choices and default. Read a single character response that is contained in choices or default. If choices is NULL, all possible @@ -371,27 +380,17 @@ add_menu(windid window, glyphinfo, const anything identifier, char accelerator, with the default object class symbols. -- attr attributes can be one of ATR_NONE (or 0) - ATR_ULINE ATR_BOLD + ATR_DIM + ATR_ITALIC + ATR_ULINE ATR_BLINK ATR_INVERSE + If a window-port does not support all of these, it may map unsupported attributes to a supported one (e.g. map them all to ATR_INVERSE). - -- clr color will hold a color value including 24-bit - true color values. To do that, and make it - distinguishable on the windowport side of things, the - windowport needs to treat it as follows: - 0 = not used, just ignore the clr value. - 1-16 = indicates a normal NetHack color value from - 0 - 15, so subtract 1 to get the NetHack - color value. - 17... = indicates a 24-bit RGB color value, so - subtract 17 to get the actual 24-bit - color value. - When placing the values into this parameter, the core - will add 1 to a NetHack color value, and add 17 to a - 24-bit RGB color value. + -- clr is color and is one of the NetHack CLR_ defines. -- str is a pointer to the menu item text. -- itemflags on this item (such as MENU_ITEMFLAGS_SELECTED etc.). @@ -454,7 +453,7 @@ status_enablefield(int fldindex, char fldname, char fieldfmt, boolean enable) BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX, BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX, - BL_LEVELDESC, BL_EXP, BL_CONDITION + BL_LEVELDESC, BL_EXP, BL_CONDITION, BL_VERS -- There are MAXBLSTATS status fields (from botl.h) status_update(int fldindex, genericptr_t ptr, int chg, int percentage, \ int color, long *colormasks) @@ -465,13 +464,13 @@ status_update(int fldindex, genericptr_t ptr, int chg, int percentage, \ BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX, BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX, - BL_LEVELDESC, BL_EXP, BL_CONDITION + BL_LEVELDESC, BL_EXP, BL_CONDITION, BL_VERS -- fldindex could also be BL_FLUSH (-1), which is not really a field index, but is a special trigger to tell the windowport that it should output all changes received to this point. It marks the end of a bot() cycle. -- fldindex could also be BL_RESET (-2), which is not really - a field index, but is a special advisory to to tell the + a field index, but is a special advisory to tell the windowport that it should redisplay all its status fields, even if no changes have been presented to it. -- ptr is usually a "char *", unless fldindex is BL_CONDITION. @@ -585,17 +584,24 @@ status_update(int fldindex, genericptr_t ptr, int chg, int percentage, \ +------+----------------------+--------------------+ | 15 | CLR_WHITE | | +------+----------------------+--------------------+ - | 16 | HL_ATTCLR_DIM | | CLR_MAX + | 16 | HL_ATTCLR_UNDEF | | CLR_MAX + +------+----------------------+--------------------+ + | 17 | HL_ATTCLR_NONE | | CLR_MAX + 1 + +------+----------------------+--------------------+ + | 18 | HL_ATTCLR_BOLD | | CLR_MAX + 2 +------+----------------------+--------------------+ - | 17 | HL_ATTCLR_BLINK | | + | 19 | HL_ATTCLR_DIM | | CLR_MAX + 3 +------+----------------------+--------------------+ - | 18 | HL_ATTCLR_ULINE | | + | 20 | HL_ATTCLR_ITALIC | | CLR_MAX + 4 +------+----------------------+--------------------+ - | 19 | HL_ATTCLR_INVERSE | 00010110 | + | 21 | HL_ATTCLR_ULINE | | CLR_MAX + 5 +------+----------------------+--------------------+ - | 20 | HL_ATTCLR_BOLD | | + | 22 | HL_ATTCLR_BLINK | | CLR_MAX + 6 +------+----------------------+--------------------+ - | 21 | beyond array boundary| | BL_ATTCLR_MAX + | 23 | HL_ATTCLR_INVERSE | | CLR_MAX + 7 + +------+----------------------+--------------------+ + | 24 | beyond array boundary| | BL_ATTCLR_MAX + The window port can AND (&) the bits passed in the ptr argument to status_update() with any non-zero @@ -643,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. @@ -675,7 +673,7 @@ getmsghistory(init) with the most recent. If init is TRUE, start over again from most recent message. -putmsghistory(msg) +putmsghistory(const char *msg, boolean restoring) -- This is the counterpart to getmsghistory() for restores used to reload the port's message recall buffer. The routine is called repeatedly from the core restore @@ -684,14 +682,16 @@ putmsghistory(msg) savefile. The window port routine is expected to load the message recall buffers in such a way that the ordering remains correct. The window port routine should make no - assumptions about how - many messages are forthcoming, nor should it assume that - another message will follow this one, so it must be careful - to keep all pointers/indexes intact at the end of each call. + assumptions about how many messages are forthcoming, nor + should it assume that another message will follow this one, + so it must be careful to keep all pointers/indexes intact + at the end of each call. If the window port receives more messages that can fit in its buffers, it is expected to scroll away the oldest from its buffers, much like it would with new messages being produced. + At the end of restoring messages, this will be called + one more time with msg NULL. III. Global variables diff --git a/doc/xnethack.6 b/doc/xnethack.6 index c57b92a5c4..958de169f2 100644 --- a/doc/xnethack.6 +++ b/doc/xnethack.6 @@ -1,7 +1,7 @@ .\"DO NOT REMOVE NH_DATESUB .TH XNETHACK 6 "DATE(%-d %B %Y)" XNETHACK -.TH XNETHACK 6 "21 February 2022" XNETHACK +.TH XNETHACK 6 "26 January 2025" XNETHACK .\"DO NOT REMOVE NH_DATESUB .ds Nd DATE(%Y) -.ds Nd 2022 +.ds Nd 2025 .de NB .ds Nb \\$2 .. @@ -9,7 +9,12 @@ .ds Nr \\$2 .. .NB $NHDT-Branch: NetHack-3.7 $ -.NR $NHDT-Revision: 1.27 $ +.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 xnethack \- Exploring The Mazes of Menace @@ -123,8 +128,10 @@ Also [ .BR \-\-showpaths ] +.\" force line wrap now rather than have that happen after the opening brace +.br [ -.BR \-\-version [ :paste ] +.BR \-\-version [ :copy | :dump | :show ] ] .ad .hy 14 @@ -367,23 +374,33 @@ that many top scores. Combining names with role or race or both will report entries which match any of those rather than just the ones which match all. .PP -.B \-\-version -can be used to cause xNetHack to show the version information it -was compiled with, then exit. -That will include the -.I git -commit hash if the information was available when the game was compiled. -On some platforms, such as Windows and macOS, a variation -.B \-\-version:paste +.\" avoid hyphenating "version" in this paragraph (and beyond; don't care) +.hw version +.BR \-\-version " or " \-\-version:show +can be used to cause xNetHack to show the version number, the date and +time that the program was built from its source code, and possibly +some auxiliary information about that source code, then exit. +The optional auxiliary information is \fIgit\fP +commit hash (reflecting the source code's most recent modification when +extracted from the \fIgit\fP version control system, if that is in use) +if available when the program was built. +On some platforms such as Windows and macOS, a variation, +.BR \-\-version:copy , can be used to cause xNetHack to show the version information, then exit, -while also leaving a copy of the version information in the paste buffer +while also leaving a copy of that information in the paste buffer or clipboard for potential insertion into things like bug reports. +On any platform, +.B \-\-version:dump +can be used to show most of the data used when checking whether a save +file or bones file is compatible with the program. +The program will display a line containing five numbers expressed in +hexadecimal, then exit. .PP .B \-\-showpaths can be used to cause xNetHack to show where it is expecting to find various files. Among other things it shows the path to and name for the player's -run-time configuration file, a text file which can be editted to +run-time configuration file, a text file which can be edited to customize aspects of how the game operates. .PP .BR \-\-usage " or " \-\-help diff --git a/doc/xnethack.txt b/doc/xnethack.txt index 4e7ad00ef2..7ba3ab1ac3 100644 --- a/doc/xnethack.txt +++ b/doc/xnethack.txt @@ -1,30 +1,31 @@ -XNETHACK(6) Games Manual XNETHACK(6) +4mXNETHACK24m(6) Games Manual 4mXNETHACK24m(6) -NAME +1mNAME0m xnethack - Exploring The Mazes of Menace -SYNOPSIS - xnethack [ -d|--directory directory ] [ -w|--windowtype interface ] - [ --xnethackrc:RC-file | --no-xnethackrc ] [ -n ] [ -dec | -ibm ] - [ -u playername ] [ -X | -D ] [ -p profession ] [ -r race ] [ -@ ] +1mSYNOPSIS0m + 1mxnethack 22m[ 1m-d22m|1m--directory 4m22mdirectory24m ] [ 1m-w22m|1m--windowtype 4m22minterface24m ] + [ 1m--xnethackrc:4m22mRC-file24m | 1m--no-xnethackrc 22m] [ 1m-n 22m] [ 1m-dec 22m| 1m-ibm 22m] + [ 1m-u 4m22mplayername24m ] [ 1m-X 22m| 1m-D 22m] [ 1m-p 4m22mprofession24m ] [ 1m-r 4m22mrace24m ] [ 1m-@ 22m] - Also [ -A|-Arc | -B|-Bar | -C|-Cav | -H|-Hea | -K|-Kni | -M|-Mon | - -P|-Pri | -R|-Rog | -Ran | -S|-Sam | -T|-Tou | -V|-Val | -W|-Wiz ] + Also [ 1m-A22m|1m-Arc 22m| 1m-B22m|1m-Bar 22m| 1m-C22m|1m-Cav 22m| 1m-H22m|1m-Hea 22m| 1m-K22m|1m-Kni 22m| 1m-M22m|1m-Mon 22m| + 1m-P22m|1m-Pri 22m| 1m-R22m|1m-Rog 22m| 1m-Ran 22m| 1m-S22m|1m-Sam 22m| 1m-T22m|1m-Tou 22m| 1m-V22m|1m-Val 22m| 1m-W22m|1m-Wiz 22m] - xnethack [ -d|--directory directory ] -s|--scores [ -v ] - [ -p profession ] [ -r race ] [ playernames ] + 1mxnethack 22m[ 1m-d22m|1m--directory 4m22mdirectory24m ] 1m-s22m|1m--scores 22m[ 1m-v 22m] + [ 1m-p 4m22mprofession24m ] [ 1m-r 4m22mrace24m ] [ 4mplayernames24m ] - xnethack [ --usage | --help ] [ --showpaths ] [ --version[:paste] ] + 1mxnethack 22m[ 1m--usage 22m| 1m--help 22m] [ 1m--showpaths 22m] + [ 1m--version22m[1m:copy22m|1m:dump22m|1m:show22m] ] -DESCRIPTION - xNetHack is a display oriented Dungeons & Dragons(tm) - like game. The +1mDESCRIPTION0m + 4mxNetHack24m is a display oriented Dungeons & Dragons(tm) - like game. The standard tty display and command structure resemble rogue. Other, more graphical display options exist for most platforms. To get started you really only need to know two commands. The command - ? will give you a list of the available commands (as well as other - information) and the command / will identify the things you see on the + 1m? 22mwill give you a list of the available commands (as well as other + information) and the command 1m/ 22mwill identify the things you see on the screen. To win the game (as opposed to merely playing to beat other people's @@ -32,11 +33,11 @@ DESCRIPTION below the 20th level of the dungeon and get it out. Few people achieve this; most never do. Those who have done so go down in history as heroes among heroes — and then they find ways of making the game even - harder. See the Guidebook section on Conduct if this game has gotten + harder. See the 4mGuidebook24m section on Conduct if this game has gotten too easy for you. When the game ends, whether by your dying, quitting, or escaping from - the caves, xNetHack will give you (a fragment of) the list of top scor‐ + the caves, 4mxNetHack24m will give you (a fragment of) the list of top scor‐ ers. The scoring is based on many aspects of your behavior, but a rough estimate is obtained by taking the amount of gold you've found in the cave plus four times your (real) experience. Precious stones may @@ -45,8 +46,8 @@ DESCRIPTION The environment variable XNETHACKOPTIONS can be used to initialize many run-time options. The ? command provides a description of these - options and syntax. (The -dec and -ibm command line options are mutu‐ - ally exclusive and are equivalent to the decgraphics and ibmgraphics + options and syntax. (The 1m-dec 22mand 1m-ibm 22mcommand line options are mutu‐ + ally exclusive and are equivalent to the 1mdecgraphics 22mand 1mibmgraphics0m run-time options described there, and are provided purely for conve‐ nience on systems supporting multiple types of terminals.) @@ -54,15 +55,15 @@ DESCRIPTION in a configuration file. The default is located in your home directory and named .xnethackrc on UNIX systems (including descendants such as linux, NetBSD, and macOS). On Windows, the name is also .xnethackrc - but the location can vary (see --showpaths below). On other systems, + but the location can vary (see 1m--showpaths 22mbelow). On other systems, the default may be different, possibly xNetHack.cnf. On MS-DOS, the name is defaults.nh in xNetHack's directory (folder), while on VMS|OpenVMS it is xnethack.ini in your home directory. The default - configuration file may be overridden via the --xnethackrc:rc-file com‐ + configuration file may be overridden via the 1m--xnethackrc:4m22mrc-file24m com‐ mand line option or by setting XNETHACKOPTIONS in your environment to a string consisting of an @ character followed by the path and filename. - The -u playername option supplies the answer to the question "Who are + The 1m-u 4m22mplayername24m option supplies the answer to the question "Who are you?". It overrides any name from the options or configuration file, USER, LOGNAME, or getlogin(), which will otherwise be tried in order. If none of these provides a useful name, the player will be asked for @@ -71,101 +72,107 @@ DESCRIPTION versely, you must use the appropriate player name to restore a saved game. - A playername suffix can be used to specify the profession, race, align‐ + A 4mplayername24m suffix can be used to specify the profession, race, align‐ ment and/or gender of the character. The full syntax of the playername that includes a suffix is "name-ppp-rrr-aaa-ggg". "ppp" are at least the first three letters of the profession (this can also be specified - using a separate -p profession option). "rrr" are at least the first + using a separate 1m-p 4m22mprofession24m option). "rrr" are at least the first three letters of the character's race (this can also be specified using - a separate -r race option). "aaa" are at least the first three letters + a separate 1m-r 4m22mrace24m option). "aaa" are at least the first three letters of the character's alignment, and "ggg" are at least the first three letters of the character's gender. Any of the parts of the suffix may be left out. - -p profession can be used to determine the character profession, also + 1m-p 4m22mprofession24m can be used to determine the character profession, also known as the role. You can specify either the male or female name for the character role, or the first three characters of the role as an abbreviation. - Likewise, -r race can be used to explicitly request that a race be cho‐ + Likewise, 1m-r 4m22mrace24m can be used to explicitly request that a race be cho‐ sen. - The -A|-Arc | -B|-Bar | -C|-Cav | -H|-Hea | -K|-Kni | -M|-Mon | -P|-Pri - | -R|-Rog | -Ran | -S|-Sam | -T|-Tou | -V|-Val | -W|-Wiz options for + The 1m-A22m|1m-Arc 22m| 1m-B22m|1m-Bar 22m| 1m-C22m|1m-Cav 22m| 1m-H22m|1m-Hea 22m| 1m-K22m|1m-Kni 22m| 1m-M22m|1m-Mon 22m| 1m-P22m|1m-Pri0m + | 1m-R22m|1m-Rog 22m| 1m-Ran 22m| 1m-S22m|1m-Sam 22m| 1m-T22m|1m-Tou 22m| 1m-V22m|1m-Val 22m| 1m-W22m|1m-Wiz 22moptions for role selection are maintained for compatibility with older versions of the program. They are mutually exclusive and the single-letter form - must be uppercase. Ranger has no single-letter choice because -R is - already used for the Rogue role. + must be uppercase. 4mRanger24m has no single-letter choice because 1m-R 22mis + already used for the 4mRogue24m role. - -@ tells xnethack to choose any omitted characteristics (profes‐ + 1m-@ 22mtells xnethack to choose any omitted characteristics (profes‐ sion/role, race, gender, alignment) randomly without prompting. Other‐ wise, leaving out any of these characteristics will result in you being prompted during game startup for the information. - The -n option suppresses printing of any news from the game administra‐ + The 1m-n 22moption suppresses printing of any news from the game administra‐ tor. - The -X option will start the game in a special non-scoring discovery - mode (also known as explore mode). -D will start the game in debug + The 1m-X 22moption will start the game in a special non-scoring discovery + mode (also known as explore mode). 1m-D 22mwill start the game in debug mode (also known as wizard mode) after changing the character name to - “wizard”, if the player is allowed. Otherwise it will switch to -X. - Control of who is allowed to use debug mode is done via the “WIZARDS=” - line in xnethack's sysconf file. + “wizard”, if the player is allowed. Otherwise it will switch to 1m-X22m. + Control of who is allowed to use debug mode is done via the “4mWIZARDS=24m” + line in xnethack's 4msysconf24m file. - The -d or --directory option, which must be the first argument if it + The 1m-d 22mor 1m--directory 22moption, which must be the first argument if it appears, supplies a directory which is to serve as the playground. It overrides the value from NETHACKDIR, HACKDIR, or the directory speci‐ fied by the game administrator during compilation (usually /usr/games/lib/nethackdir). This option is usually only useful to the game administrator. The playground must contain several auxiliary files such as help files, the list of top scorers, and a subdirectory - save where games are saved. + 4msave24m where games are saved. - The -w or --windowtype interface option can be used to specify which + The 1m-w 22mor 1m--windowtype 4m22minterface24m option can be used to specify which interface to use if the program has been built with support for more than one. Specifying a value on the command line overrides any value specified in the run-time configuration file. xNetHack's #version com‐ mand shows available interfaces. - The --xnethackrc:RC-file option will use RC-file instead of the default - run-time configuration file (typically ~/.xnethackrc) and the - --no-xnethackrc option can be used to skip any run-time configuration + The 1m--xnethackrc:4m22mRC-file24m option will use 4mRC-file24m instead of the default + run-time configuration file (typically 4m~/.xnethackrc24m) and the + 1m--no-xnethackrc 22moption can be used to skip any run-time configuration file. Some options provide feedback and then exit rather than play the game: - The -s or --scores option alone will print out the list of your scores - on the current version. An immediately following -v reports on all - versions present in the score file. ‘-s|-s -v’ may also be followed by - arguments -p profession and -r race to print the scores of particular + The 1m-s 22mor 1m--scores 22moption alone will print out the list of your scores + on the current version. An immediately following 1m-v 22mreports on all + versions present in the score file. ‘1m-s22m|1m-s -v22m’ may also be followed by + arguments 1m-p 4m22mprofession24m and 1m-r 4m22mrace24m to print the scores of particular roles and races only. Either can be specified multiple times to include more than one role or more than one race. When both are speci‐ fied, score entries which match either the role or the race (or both) - are printed rather than just entries which match both. ‘-s|-s -v’ may + are printed rather than just entries which match both. ‘1m-s22m|1m-s -v22m’ may be followed by one or more player names to print the scores of the players mentioned, by 'all' to print out all scores, or by a number to print that many top scores. Combining names with role or race or both will report entries which match any of those rather than just the ones which match all. - --version can be used to cause xNetHack to show the version information - it was compiled with, then exit. That will include the git commit hash - if the information was available when the game was compiled. On some - platforms, such as Windows and macOS, a variation --version:paste can - be used to cause xNetHack to show the version information, then exit, - while also leaving a copy of the version information in the paste - buffer or clipboard for potential insertion into things like bug - reports. - - --showpaths can be used to cause xNetHack to show where it is expecting + 1m--version 22mor 1m--version:show 22mcan be used to cause xNetHack to show the + version number, the date and time that the program was built from its + source code, and possibly some auxiliary information about that source + code, then exit. The optional auxiliary information is 4mgit24m commit hash + (reflecting the source code's most recent modification when extracted + from the 4mgit24m version control system, if that is in use) if available + when the program was built. On some platforms such as Windows and + macOS, a variation, 1m--version:copy22m, can be used to cause xNetHack to + show the version information, then exit, while also leaving a copy of + that information in the paste buffer or clipboard for potential inser‐ + tion into things like bug reports. On any platform, 1m--version:dump 22mcan + be used to show most of the data used when checking whether a save file + or bones file is compatible with the program. The program will display + a line containing five numbers expressed in hexadecimal, then exit. + + 1m--showpaths 22mcan be used to cause xNetHack to show where it is expecting to find various files. Among other things it shows the path to and name for the player's run-time configuration file, a text file which - can be editted to customize aspects of how the game operates. + can be edited to customize aspects of how the game operates. - --usage or --help will display information similar to this manual page, - then exit. Use ‘xnethack --usage | more’ to read it a page at a time. + 1m--usage 22mor 1m--help 22mwill display information similar to this manual page, + then exit. Use ‘1mxnethack --usage | more22m’ to read it a page at a time. -AUTHORS +1mAUTHORS0m Jay Fenlason (+ Kenny Woodland, Mike Thome and Jon Payne) wrote the original hack, very much like rogue (but full of bugs). @@ -182,7 +189,7 @@ AUTHORS the Usenet. Andries Brouwer has made this request for the distinction, as he may eventually release a new version of his own. -FILES +1mFILES0m Run-time configuration options were discussed above and use a platform specific name for a file in a platform specific location. For Unix, the name is '.xnethackrc' in the user's home directory. @@ -242,7 +249,7 @@ FILES In a perfect world, 'paniclog' would remain empty. -ENVIRONMENT +1mENVIRONMENT0m USER or LOGNAME Your login name. HOME Your home directory. SHELL Your shell. @@ -260,17 +267,17 @@ ENVIRONMENT SHOPTYPE and SPLEVTYPE can be used in debugging (wizard) mode. DEBUGFILES can be used if the program was built with 'DEBUG' enabled. -SEE ALSO +1mSEE ALSO0m recover(6) -BUGS +1mBUGS0m Probably infinite. -COPYRIGHT - This file is Copyright (C) Robert Patrick Rankin, 2022 for version - NetHack-3.7:1.27. xNetHack may be freely redistributed. See license +1mCOPYRIGHT0m + This file is Copyright (C) Robert Patrick Rankin, 2025 for version + NetHack-3.7:1.31. xNetHack may be freely redistributed. See license for details. Dungeons & Dragons is a Trademark of Wizards of the Coast, Inc. -XNETHACK 21 February 2022 XNETHACK(6) +XNETHACK 26 January 2025 4mXNETHACK24m(6) diff --git a/include/align.h b/include/align.h index 9ee336f4eb..6eb99c0cce 100644 --- a/include/align.h +++ b/include/align.h @@ -10,10 +10,11 @@ typedef schar aligntyp; /* basic alignment type */ typedef struct align { /* alignment & record */ aligntyp type; int record; + unsigned abuse; } align; /* bounds for "record" -- respect initial alignments of 10 */ -#define ALIGNLIM (10L + (gm.moves / 200L)) +#define ALIGNLIM (10L + (svm.moves / 200L)) #define A_NONE (-128) /* the value range of type */ @@ -59,6 +60,13 @@ typedef struct align { /* alignment & record */ #define Msa2amask(x) (((x) == 3) ? 4 : (x)) #define MSA_NONE 0 /* unaligned or multiple alignments */ +/* alignment change reasons for uchangealign(attrib.c) */ +enum uchangealign_reasons { + A_CG_CONVERT = 0, /* permanently converted */ + A_CG_HELM_ON = 1, /* donned helm of opposite alignment */ + A_CG_HELM_OFF = 2, /* doffed helm of opposite alignment */ +}; + /* for altars */ #define a_align(x, y) ((aligntyp) Amask2align(levl[x][y].altarmask & AM_MASK)) diff --git a/include/artifact.h b/include/artifact.h index 9d8bab68cc..05a8c5da57 100644 --- a/include/artifact.h +++ b/include/artifact.h @@ -50,6 +50,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' */ short material; /* material it's made of (0 = base type's default) */ @@ -67,6 +69,7 @@ enum invoke_prop_types { ENLIGHTENING, CREATE_AMMO, BANISH, + BLINDING_RAY, /* xNetHack specific ones: */ LIGHTNING_BOLT, SMOKE_CLOUD diff --git a/include/artilist.h b/include/artilist.h index 5fd4b5647c..ed74e40626 100644 --- a/include/artilist.h +++ b/include/artilist.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 artilist.h $NHDT-Date: 1596498526 2020/08/03 23:48:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.23 $ */ +/* NetHack 3.7 artilist.h $NHDT-Date: 1710957374 2024/03/20 17:56:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.30 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2017. */ /* NetHack may be freely redistributed. See license for details. */ @@ -7,22 +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, mat, bn) nam + gs, gv, cost, clr, mat, 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, mat, bn) \ + gs, gv, cost, clr, mat, bn) \ ART_##bn + +#elif defined(DUMP_ARTI_ENUM) +#define A(nam, typ, s1, s2, mt, atk, dfn, cry, inv, al, cl, rac, \ + gs, gv, cost, clr, mat, bn) \ + { ART_##bn, "ART_" #bn } #else -/* in artifact.c, set up the actual artifact list structure */ +/* 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, mat, bn) \ + gs, gv, cost, clr, mat, bn) \ { \ typ, nam, s1, s2, mt, atk, dfn, cry, inv, al, cl, rac, \ - cost, clr, mat \ + gs, gv, cost, clr, mat \ } /* clang-format off */ @@ -49,24 +55,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, DEFAULT_MAT, PLACEHOLDER), + NON_PM, NON_PM, + 0, 0, 0L, NO_COLOR, DEFAULT_MAT, 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, DEFAULT_MAT, EXCALIBUR), + 0, 10, 4000L, NO_COLOR, DEFAULT_MAT, 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, DEFAULT_MAT, STORMBRINGER), + DRLI(5, 2), DRLI(0, 0), NO_CARY, 0, A_CHAOTIC, NON_PM, NON_PM, + 0, 9, 8000L, NO_COLOR, DEFAULT_MAT, STORMBRINGER), /* * Mjollnir can be thrown when wielded if hero has 25 Strength * (usually via gauntlets of power but possible with rings of @@ -81,12 +110,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, - LIGHTNING_BOLT, A_NEUTRAL, PM_VALKYRIE, NON_PM, 4000L, NO_COLOR, - DEFAULT_MAT, MJOLLNIR), + LIGHTNING_BOLT, A_NEUTRAL, PM_VALKYRIE, NON_PM, + 0, 8, 4000L, NO_COLOR, DEFAULT_MAT, 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, DEFAULT_MAT, - CLEAVER), + 0, A_NEUTRAL, PM_BARBARIAN, NON_PM, + 0, 8, 1500L, NO_COLOR, DEFAULT_MAT, CLEAVER), /* * Grimtooth glows in warning when elves are present, but its @@ -95,8 +124,8 @@ static NEARDATA struct artifact artilist[] = { */ A("Grimtooth", ORCISH_DAGGER, (SPFX_RESTR | SPFX_WARN | SPFX_DFLAG2), 0, M2_ELF, PHYS(2, 6), POIS(0, 0), - NO_CARY, 0, A_CHAOTIC, NON_PM, PM_ORC, 1000L, CLR_RED, DEFAULT_MAT, - GRIMTOOTH), + NO_CARY, 0, A_CHAOTIC, NON_PM, PM_ORC, + 0, 5, 300L, CLR_RED, DEFAULT_MAT, GRIMTOOTH), /* * Orcrist and Sting have same alignment as elves. * @@ -105,70 +134,70 @@ 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, DEFAULT_MAT, 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, DEFAULT_MAT, 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, - DEFAULT_MAT, STING), + 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, + 3, 1, 800L, CLR_BRIGHT_BLUE, DEFAULT_MAT, 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, DEFAULT_MAT, MAGICBANE), + 0, 7, 3500L, NO_COLOR, DEFAULT_MAT, MAGICBANE), A("Frost Brand", SHORT_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, DEFAULT_MAT, FROST_BRAND), + COLD(5, 0), COLD(0, 0), NO_CARY, 0, A_NONE, NON_PM, NON_PM, + 0, 9, 3000L, NO_COLOR, DEFAULT_MAT, FROST_BRAND), A("Fire Brand", SHORT_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, DEFAULT_MAT, FIRE_BRAND), + FIRE(5, 0), FIRE(0, 0), NO_CARY, 0, A_NONE, NON_PM, NON_PM, + 0, 5, 3000L, NO_COLOR, DEFAULT_MAT, FIRE_BRAND), A("Mirror Brand", SHORT_SWORD, (SPFX_RESTR | SPFX_REFLECT), 0, 0, - NO_ATTK, NO_DFNS, NO_CARY, 0, A_NONE, NON_PM, NON_PM, 2500L, - NO_COLOR, GLASS, MIRROR_BRAND), /* special damage added in artifact_hit() */ + NO_ATTK, NO_DFNS, NO_CARY, 0, A_NONE, NON_PM, NON_PM, + 0, 0, 2500L, NO_COLOR, GLASS, MIRROR_BRAND), /* special damage added in artifact_hit() */ A("Dragonbane", DWARVISH_SPEAR, (SPFX_WARN | SPFX_RESTR | SPFX_DCLAS | SPFX_REFLECT), 0, S_DRAGON, - PHYS(5, 10), NO_DFNS, NO_CARY, 0, A_NONE, NON_PM, NON_PM, 2500L, - NO_COLOR, DEFAULT_MAT, DRAGONBANE), + PHYS(5, 10), NO_DFNS, NO_CARY, 0, A_NONE, NON_PM, NON_PM, + 2, 5, 2500L, NO_COLOR, DEFAULT_MAT, DRAGONBANE), A("Demonbane", 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, SILVER, DEMONBANE), + PHYS(5, 0), NO_DFNS, NO_CARY, BANISH, A_LAWFUL, PM_CLERIC, NON_PM, + 1, 3, 2500L, NO_COLOR, SILVER, DEMONBANE), A("Werebane", SABER, (SPFX_WARN | SPFX_RESTR | SPFX_DFLAG2), 0, M2_WERE, - PHYS(5, 10), DFNS(AD_WERE), NO_CARY, 0, A_NONE, NON_PM, NON_PM, 1500L, - NO_COLOR, SILVER, WEREBANE), + PHYS(5, 10), DFNS(AD_WERE), NO_CARY, 0, A_NONE, NON_PM, NON_PM, + 1, 4, 1500L, NO_COLOR, SILVER, WEREBANE), A("Grayswandir", SABER, (SPFX_RESTR | SPFX_HALRES), 0, 0, - PHYS(5, 0), NO_DFNS, NO_CARY, 0, A_LAWFUL, NON_PM, NON_PM, 8000L, - NO_COLOR, SILVER, GRAYSWANDIR), + PHYS(5, 0), NO_DFNS, NO_CARY, 0, A_LAWFUL, NON_PM, NON_PM, + 0, 10, 8000L, NO_COLOR, SILVER, GRAYSWANDIR), A("Giantslayer", LONG_SWORD, (SPFX_WARN | SPFX_RESTR | SPFX_DFLAG2), 0, M2_GIANT, - PHYS(5, 10), NO_DFNS, NO_CARY, 0, A_NEUTRAL, NON_PM, NON_PM, 500L, - NO_COLOR, DEFAULT_MAT, GIANTSLAYER), + PHYS(5, 10), NO_DFNS, NO_CARY, 0, A_NEUTRAL, NON_PM, NON_PM, + 2, 4, 500L, NO_COLOR, DEFAULT_MAT, GIANTSLAYER), A("Ogresmasher", WAR_HAMMER, (SPFX_RESTR | SPFX_HEAVYHIT), 0, 0, - PHYS(3, 8), NO_DFNS, NO_CARY, 0, A_NONE, NON_PM, NON_PM, 1200L, - NO_COLOR, DEFAULT_MAT, OGRESMASHER), + PHYS(3, 8), NO_DFNS, NO_CARY, 0, A_NONE, NON_PM, NON_PM, + 2, 1, 1200L, NO_COLOR, DEFAULT_MAT, OGRESMASHER), A("Trollsbane", MORNING_STAR, (SPFX_WARN | SPFX_RESTR | SPFX_DCLAS | SPFX_REGEN), 0, S_TROLL, - PHYS(5, 10), NO_DFNS, NO_CARY, 0, A_NONE, NON_PM, NON_PM, 1000L, - NO_COLOR, DEFAULT_MAT, TROLLSBANE), + PHYS(5, 10), NO_DFNS, NO_CARY, 0, A_NONE, NON_PM, NON_PM, + 2, 1, 1000L, NO_COLOR, DEFAULT_MAT, TROLLSBANE), /* * Two problems: 1) doesn't let trolls regenerate heads, * 2) doesn't give unusual message for 2-headed monsters (but * allowing those at all causes more problems than worth the effort). */ A("Vorpal Blade", LONG_SWORD, (SPFX_RESTR | SPFX_BEHEAD), 0, 0, - PHYS(5, 8), NO_DFNS, NO_CARY, 0, A_NEUTRAL, NON_PM, NON_PM, 4000L, - NO_COLOR, DEFAULT_MAT, VORPAL_BLADE), + PHYS(5, 8), NO_DFNS, NO_CARY, 0, A_NEUTRAL, NON_PM, NON_PM, + 1, 5, 4000L, NO_COLOR, DEFAULT_MAT, VORPAL_BLADE), /* * Ah, never shall I forget the cry, * or the shriek that shrieked he, @@ -178,86 +207,97 @@ 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, DEFAULT_MAT, - SNICKERSNEE), + 0, A_LAWFUL, PM_SAMURAI, NON_PM, + 0, 8, 1200L, NO_COLOR, DEFAULT_MAT, 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, 0, A_LAWFUL, NON_PM, NON_PM, 1500L, - NO_COLOR, GOLD, SUNSWORD), + PHYS(5, 0), DFNS(AD_BLND), NO_CARY, BLINDING_RAY, A_LAWFUL, NON_PM, + NON_PM, + 0, 6, 1500L, NO_COLOR, GOLD, SUNSWORD), A("The Apple of Discord", APPLE, SPFX_RESTR, 0, 0, NO_ATTK, NO_DFNS, - NO_CARY, CONFLICT, A_CHAOTIC, NON_PM, NON_PM, 4000L, NO_COLOR, GOLD, - APPLE_OF_DISCORD), + NO_CARY, CONFLICT, A_CHAOTIC, NON_PM, NON_PM, + 0, 0, 4000L, NO_COLOR, GOLD, APPLE_OF_DISCORD), A("The Amulet of Storms", AMULET_OF_FLYING, (SPFX_RESTR | SPFX_DEFN), 0, 0, - NO_ATTK, DFNS(AD_ELEC), NO_CARY, 0, A_CHAOTIC, NON_PM, - NON_PM, 600L, NO_COLOR, DEFAULT_MAT, AMULET_OF_STORMS), + NO_ATTK, DFNS(AD_ELEC), NO_CARY, 0, A_CHAOTIC, NON_PM, NON_PM, + 0, 0, 600L, NO_COLOR, DEFAULT_MAT, AMULET_OF_STORMS), /* * The artifacts for the quest dungeon, all self-willed. + * gen_spe should be 0; gift_value irrelevant and set to 12. */ A("Itlachiayaque", SHIELD_OF_REFLECTION, (SPFX_NOGEN | SPFX_RESTR | SPFX_INTEL), SPFX_WARN, 0, NO_ATTK, NO_DFNS, CARY(AD_FIRE), SMOKE_CLOUD, A_LAWFUL, PM_ARCHEOLOGIST, - NON_PM, 3500L, NO_COLOR, GOLD, ITLACHIAYAQUE), + NON_PM, + 0, 12, 3500L, NO_COLOR, GOLD, ITLACHIAYAQUE), 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, DEFAULT_MAT, HEART_OF_AHRIMAN), + NON_PM, + 0, 12, 2500L, NO_COLOR, DEFAULT_MAT, HEART_OF_AHRIMAN), A("Big Stick", CLUB, (SPFX_NOGEN | SPFX_RESTR | SPFX_INTEL | SPFX_DEFN), SPFX_STLTH, 0, /* "speak softly..." */ PHYS(5, 12), DFNS(AD_MAGM), NO_CARY, CONFLICT, A_CHAOTIC, PM_CAVE_DWELLER, - NON_PM, 2500L, NO_COLOR, DEFAULT_MAT, BIG_STICK), - -#if 0 /* OBSOLETE */ -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 ), + NON_PM, + 0, 12, 2500L, NO_COLOR, DEFAULT_MAT, BIG_STICK), + +#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 + * and the Elf role along with its quest was eliminated; it's a bit + * overpowered to be an ordinary artifact so leave it excluded */ + 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, + 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, DEFAULT_MAT, STAFF_OF_AESCULAPIUS), + NON_PM, + 0, 12, 5000L, NO_COLOR, DEFAULT_MAT, 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, DEFAULT_MAT, MAGIC_MIRROR_OF_MERLIN), + NO_ATTK, NO_DFNS, CARY(AD_MAGM), 0, A_LAWFUL, PM_KNIGHT, NON_PM, + 0, 12, 1500L, NO_COLOR, DEFAULT_MAT, 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, DEFAULT_MAT, EYES_OF_THE_OVERWORLD), + 0, 12, 2500L, NO_COLOR, DEFAULT_MAT, EYES_OF_THE_OVERWORLD), A("The Sceptre of Might", MACE, (SPFX_NOGEN | SPFX_RESTR | SPFX_INTEL | SPFX_DALIGN), 0, 0, PHYS(5, 0), DRLI(0,0), NO_CARY, ENERGY_BOOST, A_LAWFUL, PM_CLERIC, NON_PM, /* METAL so it doesn't hurt elvish Priests */ - 2500L, NO_COLOR, METAL, SCEPTRE_OF_MIGHT), + 0, 12, 2500L, NO_COLOR, METAL, SCEPTRE_OF_MIGHT), /* Removed for now. This isn't a bad artifact, but the Sceptre of Might * offers more interesting choices for Priests than this does. 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, SILVER, MITRE_OF_HOLINESS), + PM_CLERIC, NON_PM, + 0, 12, 2000L, NO_COLOR, SILVER, 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, DEFAULT_MAT, LONGBOW_OF_DIANA), + 0, 12, 4000L, NO_COLOR, DEFAULT_MAT, 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 @@ -265,47 +305,49 @@ A("The Palantir of Westernesse", CRYSTAL_BALL, 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, DEFAULT_MAT, - MASTER_KEY_OF_THIEVERY), + UNTRAP, A_CHAOTIC, PM_ROGUE, NON_PM, + 0, 12, 3500L, NO_COLOR, DEFAULT_MAT, 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, DEFAULT_MAT, TSURUGI_OF_MURAMASA), + 0, 12, 4500L, NO_COLOR, DEFAULT_MAT, 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, PLATINUM, - YENDORIAN_EXPRESS_CARD), + CHARGE_OBJ, A_NEUTRAL, PM_TOURIST, NON_PM, + 0, 12, 7000L, NO_COLOR, PLATINUM, YENDORIAN_EXPRESS_CARD), /* While this is the Valkyrie artifact, it's chaotic because it belongs to * Lord Surtur. */ A("Sol Valtiva", TWO_HANDED_SWORD, (SPFX_NOGEN | SPFX_RESTR | SPFX_ATTK | SPFX_INTEL), (SPFX_HSPDAM | SPFX_HPHDAM), 0, FIRE(3, 5), NO_DFNS, NO_CARY, - LEV_TELE, A_CHAOTIC, PM_VALKYRIE, NON_PM, 5000L, NO_COLOR, MITHRIL, - SOL_VALTIVA), + LEV_TELE, A_CHAOTIC, PM_VALKYRIE, NON_PM, + 0, 12, 5000L, NO_COLOR, MITHRIL, SOL_VALTIVA), 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, DEFAULT_MAT, EYE_OF_THE_AETHIOPICA), + NON_PM, + 0, 12, 4000L, NO_COLOR, DEFAULT_MAT, EYE_OF_THE_AETHIOPICA), -#if !defined(ARTI_ENUM) +#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, - 0, 0, TERMINATOR) /* 0 is CLR_BLACK rather than NO_COLOR but it doesn't matter here */ + A(0, 0, 0, 0, 0, NO_ATTK, NO_DFNS, NO_CARY, 0, A_NONE, NON_PM, NON_PM, + 0, 0, 0L, 0, 0, TERMINATOR) /* 0 is CLR_BLACK rather than NO_COLOR but it + doesn't matter here */ }; /* artilist[] (or artifact_names[]) */ #endif #undef A -#if !defined(MAKEDEFS_C) && !defined(MDLIB_C) && !defined(ARTI_ENUM) +#ifdef NO_ATTK #undef NO_ATTK #undef NO_DFNS #undef DFNS diff --git a/include/botl.h b/include/botl.h index 8d9a789946..8d8872bca8 100644 --- a/include/botl.h +++ b/include/botl.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 botl.h $NHDT-Date: 1596498528 2020/08/03 23:48:48 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.34 $ */ +/* NetHack 3.7 botl.h $NHDT-Date: 1694893330 2023/09/16 19:42:10 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.37 $ */ /* Copyright (c) Michael Allison, 2003 */ /* NetHack may be freely redistributed. See license for details. */ @@ -41,12 +41,15 @@ enum statusfields { BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX, /* 7..12 */ BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, /* 13..18 */ BL_HPMAX, BL_LEVELDESC, BL_EXP, BL_CONDITION, /* 19..22 */ - MAXBLSTATS /* [23] */ + BL_VERS, /* 23 */ + MAXBLSTATS, /* [24] */ }; -enum relationships { NO_LTEQGT = -1, - EQ_VALUE, LT_VALUE, LE_VALUE, - GE_VALUE, GT_VALUE, TXT_VALUE }; +enum relationships { + NO_LTEQGT = -1, + EQ_VALUE, LT_VALUE, LE_VALUE, + GE_VALUE, GT_VALUE, TXT_VALUE +}; enum blconditions { bl_bareh, @@ -201,10 +204,18 @@ extern int cond_idx[CONDITION_COUNT]; /* #ifdef STATUS_HILITES */ /* hilite status field behavior - coloridx values */ -#define BL_HILITE_NONE -1 /* no hilite of this field */ -#define BL_HILITE_INVERSE -2 /* inverse hilite */ -#define BL_HILITE_BOLD -3 /* bold hilite */ - /* or any CLR_ index (0 - 15) */ +#define BL_HILITE_NONE -1 /* no hilite of this field */ + +#if 0 +#define BL_HILITE_BOLD -2 /* bold hilite */ +#define BL_HILITE_DIM -3 /* dim hilite */ +#define BL_HILITE_ITALIC -4 /* italic hilite */ +#define BL_HILITE_ULINE -5 /* underline hilite */ +#define BL_HILITE_BLINK -6 /* blink hilite */ +#define BL_HILITE_INVERSE -7 /* inverse hilite */ + /* or any CLR_ index (0 - 15) */ +#endif + #define BL_TH_NONE 0 #define BL_TH_VAL_PERCENTAGE 100 /* threshold is percentage */ #define BL_TH_VAL_ABSOLUTE 101 /* threshold is particular value */ @@ -212,25 +223,30 @@ extern int cond_idx[CONDITION_COUNT]; #define BL_TH_CONDITION 103 /* threshold is bitmask of conditions */ #define BL_TH_TEXTMATCH 104 /* threshold text value to match against */ #define BL_TH_ALWAYS_HILITE 105 /* highlight regardless of value */ +#define BL_TH_CRITICALHP 106 /* highlight critically low HP */ +#define HL_ATTCLR_NONE CLR_MAX + 1 +#define HL_ATTCLR_BOLD CLR_MAX + 2 +#define HL_ATTCLR_DIM CLR_MAX + 3 +#define HL_ATTCLR_ITALIC CLR_MAX + 4 +#define HL_ATTCLR_ULINE CLR_MAX + 5 +#define HL_ATTCLR_BLINK CLR_MAX + 6 +#define HL_ATTCLR_INVERSE CLR_MAX + 7 +#define BL_ATTCLR_MAX CLR_MAX + 8 -#define HL_ATTCLR_DIM CLR_MAX + 0 -#define HL_ATTCLR_BLINK CLR_MAX + 1 -#define HL_ATTCLR_ULINE CLR_MAX + 2 -#define HL_ATTCLR_INVERSE CLR_MAX + 3 -#define HL_ATTCLR_BOLD CLR_MAX + 4 -#define BL_ATTCLR_MAX CLR_MAX + 5 - -enum hlattribs { HL_UNDEF = 0x00, - HL_NONE = 0x01, - HL_BOLD = 0x02, - HL_INVERSE = 0x04, - HL_ULINE = 0x08, - HL_BLINK = 0x10, - HL_DIM = 0x20 }; +enum hlattribs { + HL_UNDEF = 0x00, + HL_NONE = 0x01, + HL_BOLD = 0x02, + HL_DIM = 0x04, + HL_ITALIC = 0x08, + HL_ULINE = 0x10, + HL_BLINK = 0x20, + HL_INVERSE = 0x40 +}; #define MAXVALWIDTH 80 /* actually less, but was using 80 to allocate title - * and leveldesc then using QBUFSZ everywhere else */ + * and leveldesc then using QBUFSZ everywhere else */ #ifdef STATUS_HILITES struct hilite_s { enum statusfields fld; @@ -245,6 +261,11 @@ struct hilite_s { }; #endif +/* + * Note: If you add/change/remove fields in istat_s, you need to + * update the initialization of the istat_s struct blstats[][] + * array in instance_globals_b (decl.c). + */ struct istat_s { const char *fldname; const char *fldfmt; @@ -253,7 +274,7 @@ struct istat_s { boolean percent_matters; short percent_value; unsigned anytype; - anything a; + anything a, rawval; char *val; int valwidth; enum statusfields idxmax; diff --git a/include/color.h b/include/color.h index 045f93df5e..e304467e5f 100644 --- a/include/color.h +++ b/include/color.h @@ -36,7 +36,7 @@ /* color aliases used in monsters.h and display.c */ #define HI_DOMESTIC CLR_WHITE /* for player + pets */ #define HI_LORD CLR_MAGENTA /* for high-end monsters */ -#define HI_BOSS CLR_BRIGHT_MAGENTA +#define HI_OVERLORD CLR_BRIGHT_MAGENTA /* for few uniques */ /* these can be configured */ #define HI_OBJ CLR_MAGENTA @@ -58,4 +58,25 @@ * configured for a given monster and it should use the default */ #define MONSTERCOLOR_DEFAULT (CLR_MAX + 1) +#define NH_BASIC_COLOR 0x1000000 +#define NH_ALTPALETTE 0x2000000 +#define COLORVAL(x) ((x) & 0xFFFFFF) + +enum nhcolortype { no_color, nh_color, rgb_color }; + +struct nethack_color { + enum nhcolortype colortyp; + int tableindex; + int rgbindex; + const char *name; + const char *hexval; + long r, g, b; +}; + +typedef struct color_and_attr { + int color, attr; +} color_attr; + #endif /* COLOR_H */ + +/*color.h*/ diff --git a/include/config.h b/include/config.h index 050b287fc0..3160cbc1c6 100644 --- a/include/config.h +++ b/include/config.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 config.h $NHDT-Date: 1610141601 2021/01/08 21:33:21 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.148 $ */ +/* NetHack 3.7 config.h $NHDT-Date: 1710344316 2024/03/13 15:38:36 $ $NHDT-Branch: keni-staticfn $:$NHDT-Revision: 1.188 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2016. */ /* NetHack may be freely redistributed. See license for details. */ @@ -24,7 +24,7 @@ /* #define TOS */ /* define for Atari ST/TT */ /* #define STUPID */ /* avoid some complicated expressions if - your C compiler chokes on them */ + * your C compiler chokes on them */ /* #define MINIMAL_TERM */ /* if a terminal handles highlighting or tabs poorly, try this define, used in pager.c and termcap.c */ @@ -96,8 +96,8 @@ #ifdef QT_GRAPHICS #ifndef DEFAULT_WC_TILED_MAP -#define DEFAULT_WC_TILED_MAP /* Default to tiles if users doesn't say \ - wc_ascii_map */ +#define DEFAULT_WC_TILED_MAP /* Default to tiles if users doesn't request + * wc_ascii_map */ #endif #ifndef USE_XPM #define USE_XPM /* Use XPM format for images (required) */ @@ -154,7 +154,8 @@ * the free XPM library. The second option allows you to then use other * programs to generate tiles files. For example, the PBMPlus tools * would allow: - * xpmtoppm x11tiles_big.xpm + * xpmtoppm x11tiles_big.xpm */ /* # define USE_XPM */ /* Disable if you do not have the XPM library */ #ifdef USE_XPM @@ -205,6 +206,10 @@ * The following options pertain to crash reporting: * GREPPATH (the path to the system grep(1) utility) * GDBPATH (the path to the system gdb(1) program) + * CRASHREPORT (use CRASHREPORTURL if defined in syscf; this + * define specifies the name of the helper program + * used to launch the browser and enables the + * feature)) * Regular nethack options can also be specified in order to * provide system-wide default values local to your system: * OPTIONS (same as in users' .xnethackrc or defaults.nh) @@ -235,6 +240,59 @@ #define GREPPATH "/bin/grep" #endif +#ifndef NOCRASHREPORT +# ifndef CRASHREPORT +# ifdef MACOS +# define CRASHREPORT "/usr/bin/open" +# endif +# ifdef __linux__ +# define CRASHREPORT "/usr/bin/xdg-open" + /* Define this if the terminal is filled with useless error messages + * when the browser launches. */ +# define CRASHREPORT_EXEC_NOSTDERR +# endif +# ifdef WIN32 +# define CRASHREPORT /* builtin helper */ +# endif +# endif +#else +# ifdef CRASHREPORT +# undef CRASHREPORT +# endif +# ifdef MSDOS +# undef PANICTRACE +# endif +#endif + +#ifdef CRASHREPORT +# ifndef DUMPLOG_CORE +# define DUMPLOG_CORE // required to get ^P info +# endif +# ifdef MACOS +# define PANICTRACE +# endif +# ifdef __linux__ +# define PANICTRACE +# define NOSTATICFN +# endif +// This test isn't quite right: CNG is only available from Windows 2000 on. +// But we'll check that at runtime. +# ifdef WIN32 +# define PANICTRACE +# define NOSTATICFN +# endif +#endif + +#ifdef NONOSTATICFN +# define staticfn static +#else +# ifdef NOSTATICFN +# define staticfn +# else +# define staticfn static +# endif +#endif + /* note: "larger" is in comparison with 'record', the high-scores file (whose name can be overridden via #define in global.h if desired) */ #define LOGFILE "logfile" /* larger file for debugging purposes */ @@ -246,6 +304,21 @@ many players, as it saves the player name and the game start time */ /* #define PANICLOG_FMT2 */ +/* + * When building the program, whether the 'makedefs' utility + * checks for non-ASCII or non-printable (control) characters + * in various data files (data.base, rumors.tru, rumors.fal, + * {oracles,epitaphs,engravings,bogusmons}.txt and warns about them. + * They also get changed to '#' instead of possibly remaining + * unprintable. + * + * If you modify the data files to intentionally add accented + * letters or something comparable, comment this out. (Such things + * won't necessarily work as intended within nethack but at least + * makedefs wouldn't reject them.) + */ +#define MAKEDEFS_FILTER_NONASCII + /* * PERSMAX, POINTSMIN, ENTRYMAX, PERS_IS_UID: * These control the contents of 'record', the high-scores file. @@ -285,9 +358,8 @@ /* * ENHANCED_SYMBOLS - * Support the enhanced display of symbols by utilizing utf8 and 24-bit - * color sequences. Enabled by default, but it can be disabled by - * commenting it out. + * Support the enhanced display of symbols by utilizing utf8. + * Enabled by default, but it can be disabled by commenting it out. */ #define ENHANCED_SYMBOLS @@ -391,7 +463,7 @@ */ #define INSURANCE /* allow crashed game recovery */ -#ifndef MAC +#if !defined(MAC) && !defined(SHIM_GRAPHICS) #define CHDIR /* delete if no chdir() available */ #endif @@ -532,7 +604,7 @@ typedef unsigned char uchar; #define SELECTSAVED /* support for restoring via menu */ /* TTY_TILES_ESCCODES: Enable output of special console escape codes - * which act as hints for external programs such as EbonHack, or hterm. + * which act as hints for external programs such as EbonHack or hterm. * * TTY_SOUND_ESCCODES: Enable output of special console escape codes * which act as hints for theoretical external programs to play sound effect. @@ -557,10 +629,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_xdata 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 */ @@ -568,12 +640,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 */ @@ -582,13 +648,13 @@ typedef unsigned char uchar; #if defined(DEBUG) && !defined(DEBUG_MIGRATING_MONS) #define DEBUG_MIGRATING_MONS /* add a wizard-mode command to help debug - migrating monsters */ + * migrating monsters */ #endif /* SCORE_ON_BOTL is neither experimental nor inadequately tested, but doesn't seem to fit in any other section... */ /* #define SCORE_ON_BOTL */ /* enable the 'showscore' option to - show estimated score on status line */ + * show estimated score on status line */ /* FREE_ALL_MEMORY is neither experimental nor inadequately tested, but it isn't necessary for successful operation of the program */ @@ -658,12 +724,19 @@ typedef unsigned char uchar; /* #define DUMPLOG */ /* End-of-game dump logs */ -#define USE_ISAAC64 /* Use cross-plattform, bundled RNG */ +#define USE_ISAAC64 /* Use cross-platform, bundled RNG */ /* TEMPORARY - MAKE UNCONDITIONAL BEFORE RELEASE */ /* undef this to check if sandbox breaks something */ #define NHL_SANDBOX +#ifdef NHL_SANDBOX +#ifdef CHRONICLE + /* LIVELOG (and therefore CHRONICLE) is needed for --loglua */ +#define LIVELOG +#endif +#endif + /* End of Section 4 */ #ifdef TTY_TILES_ESCCODES @@ -677,7 +750,7 @@ typedef unsigned char uchar; #include "global.h" /* Define everything else according to choices above */ /* Place the following after #include [platform]conf.h in global.h so that - overrides are possible in there, for things like unix-specfic file + overrides are possible in there, for things like unix-specific file paths. */ #ifdef LIVELOG @@ -687,9 +760,7 @@ typedef unsigned char uchar; #endif /* LIVELOG */ #ifdef DUMPLOG -#ifndef DUMPLOG_MSG_COUNT -#define DUMPLOG_MSG_COUNT 50 -#endif /* DUMPLOG_MSG_COUNT */ +#define DUMPLOG_CORE #ifndef DUMPLOG_FILE #define DUMPLOG_FILE "/tmp/xnethack.%n.%d.log" /* DUMPLOG_FILE allows following placeholders: @@ -708,6 +779,7 @@ typedef unsigned char uchar; #endif /* DUMPLOG */ #ifdef DUMPHTML +#define DUMPLOG_CORE #ifndef DUMPHTML_FILE #define DUMPHTML_FILE "/tmp/xnethack.%n.%d.html" /* Placeholders as above @@ -716,6 +788,12 @@ typedef unsigned char uchar; #endif /* DUMPHTML_FILE */ #endif /* DUMPHTML */ +#ifdef DUMPLOG_CORE +#ifndef DUMPLOG_MSG_COUNT +#define DUMPLOG_MSG_COUNT 50 +#endif /* DUMPLOG_MSG_COUNT */ +#endif + #define USE_ISAAC64 /* Use cross-plattform, bundled RNG */ /* TEMPORARY - MAKE UNCONDITIONAL BEFORE RELEASE */ diff --git a/include/config1.h b/include/config1.h index c4e14610c0..3a8632564b 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 c72de3c81a..61f442a25f 100644 --- a/include/context.h +++ b/include/context.h @@ -168,7 +168,7 @@ struct context_info { * 3: FH, 4: ff+, 5: ff-, 6: FF+, 7: FF-, * 8: travel */ unsigned startingpet_mid; /* monster id number for initial pet */ - int current_fruit; /* fruit->fid corresponding to gp.pl_fruit[] */ + 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 warnlevel; /* threshold (digit) to warn about unseen mons */ @@ -185,8 +185,6 @@ struct context_info { boolean move; boolean mv; boolean bypasses; /* bypass flag is set on at least one fobj */ - boolean botl; /* partially redo status line */ - boolean botlx; /* print an entirely new bottom line */ boolean door_opened; /* set to true if door was opened during test_move */ boolean tips[NUM_TIPS]; struct dig_info digging; diff --git a/include/cstd.h b/include/cstd.h index 10951d1d21..3e4764b524 100644 --- a/include/cstd.h +++ b/include/cstd.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 cstd.h $NHDT-Date: 1596498562 2020/08/03 23:49:22 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.24 $ */ +/* NetHack 3.7 cstd.h $NHDT-Date: 1725652996 2024/09/06 20:03:16 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.6 $ */ /*-Copyright (c) Robert Patrick Rankin, 2017. */ /* NetHack may be freely redistributed. See license for details. */ @@ -8,11 +8,10 @@ /* * The list of standard (C99 unless noted otherwise) header files: * - * Conditionally compiled macro that compares its argument - * to zero + * Conditionally compiled macro that calls abort if its + * argument evaluates to zero * (C99) Complex number arithmetic - * Functions to determine the type contained in character - * data + * Functions to categorize single characters * Macros reporting error conditions * (C99) Floating-point environment * Limits of floating-point types @@ -28,21 +27,23 @@ * Common macro definitions * (C99) Fixed-width integer types * Input/output program utilities - * - * General utilities: memory management, program utilities, string conversions, + * General utilities: memory management, + * program utilities, string conversions, * random numbers, algorithms * String handling - * (C99) Type-generic math (macros wrapping math.h and complex.h) + * (C99) Type-generic math (macros wrapping math.h and + * complex.h) * Time/date utilities * (C95) Extended multibyte and wide character utilities - * (C95) Functions to determine the type contained in wide character data + * (C95) Functions to categorize single wide character * We watch these and try not to conflict with them, or make it tough to adopt * these in future: * * (C11) alignas and alignof convenience macros * (C11) Atomic operations - * (C23) Macros to work with the byte and bit representations of types + * (C23) Macros to work with the byte and bit representations + * of types * (C23) Macros for performing checked integer arithmetic * (C11) noreturn convenience macro * (C11) Thread library @@ -50,10 +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 c8b93f0877..945aeebda3 100644 --- a/include/decl.h +++ b/include/decl.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 decl.h $NHDT-Date: 1657918080 2022/07/15 20:48:00 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.303 $ */ +/* NetHack 3.7 decl.h $NHDT-Date: 1725653004 2024/09/06 20:03:24 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.377 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2007. */ /* NetHack may be freely redistributed. See license for details. */ @@ -29,6 +29,7 @@ extern NEARDATA const struct c_color_names c_color_names; /* common_strings */ extern const struct c_common_strings c_common_strings; #define nothing_happens c_common_strings.c_nothing_happens +#define nothing_seems_to_happen c_common_strings.c_nothing_seems_to_happen #define thats_enough_tries c_common_strings.c_thats_enough_tries #define silly_thing_to c_common_strings.c_silly_thing_to #define shudder_for_moment c_common_strings.c_shudder_for_moment @@ -67,6 +68,9 @@ extern const char *fqn_prefix_names[PREFIX_COUNT]; extern NEARDATA boolean has_strong_rngseed; extern struct engr *head_engr; +/* used by coloratt.c, options.c, utf8map.c, windows.c */ +extern const char hexdd[33]; + /* material strings */ extern const char *materialnm[]; @@ -84,6 +88,8 @@ extern const char ynchars[]; extern const char ynqchars[]; extern const char ynaqchars[]; extern const char ynNaqchars[]; +extern const char rightleftchars[]; +extern const char hidespinchars[]; extern NEARDATA long yn_number; extern struct restore_info restoreinfo; extern NEARDATA struct savefile_info sfcap, sfrestinfo, sfsaveinfo; @@ -124,6 +130,13 @@ extern char *fuzzer_log[FUZZER_LOG_SIZE]; extern long fuzzer_log_idx; #endif /* FUZZER_LOG */ +struct display_hints { + boolean botl; /* partially redo status line */ + boolean botlx; /* print an entirely new bottom line */ + boolean time_botl; /* context.botl for 'time' (moves) only */ +}; +extern struct display_hints disp; + /* * 'gX' -- instance_globals holds engine state that does not need to be * persisted upon game exit. The initialization state is well defined @@ -155,7 +168,14 @@ struct instance_globals_a { short *animal_list; /* list of PM values for animal monsters */ int animal_list_count; +#ifdef CHANGE_COLOR + /* options.c */ + uint32 altpalette[CLR_MAX]; +#endif + /* pickup.c */ + int A_first_hint; /* menustyle:Full plus 'A' response + !paranoid:A */ + int A_second_hint; /* menustyle:Full plus 'A' response + paranoid:A */ boolean abort_looting; /* shk.c */ @@ -184,13 +204,9 @@ struct instance_globals_b { #endif /* decl.c */ - int bases[MAXOCLASSES + 1]; coord bhitpos; /* place where throw or zap hits or stops */ struct obj *billobjs; /* objects not yet paid for */ - /* dungeon.c */ - branch *branches; /* dungeon branch list */ - /* files.c */ char bones[BONESSIZE]; @@ -199,15 +215,15 @@ struct instance_globals_b { long bldrpushtime; /* turn that a message was given for pushing * a boulder; used in lieu of Norep() */ - /* mkmaze.c */ - struct bubble *bbubbles; - /* pickup.c */ boolean bucx_filter; /* zap.c */ struct monst *buzzer; /* zapper/caster/breather who initiates buzz() */ + /* new */ + boolean bot_disabled; + boolean havestate; unsigned long magic; /* validate that structure layout is preserved */ }; @@ -231,18 +247,22 @@ struct instance_globals_c { coord clicklook_cc; /* decl.c */ char chosen_windowtype[WINTYPELEN]; - char command_line[COLNO]; + int cmd_key; /* parse() / rhack() */ cmdcount_nht command_count; /* some objects need special handling during destruction or placement */ struct obj *current_wand; /* wand currently zapped/applied */ #ifdef DEF_PAGER const char *catmore; /* external pager; from getenv() or DEF_PAGER */ #endif - struct context_info context; /* dog.c */ char catname[PL_PSIZ]; + /* end.c */ + char *crash_email; // email for crash reports + char *crash_name; // human name for crash reports + int crash_urlmax; // maximum length for the url of a crash report + /* symbols.c */ int currentgraphics; @@ -256,7 +276,7 @@ struct instance_globals_c { /* invent.c */ /* for perm_invent when operating on a partial inventory display, so that persistent one doesn't get shrunk during filtering for item selection - then regrown to full inventory, possibly being resized in the process */ + then regrown to full inventory, possibly being resized in process */ winid cached_pickinv_win; int core_invent_state; @@ -297,15 +317,7 @@ struct instance_globals_d { long domove_succeeded; #define DOMOVE_WALK 0x00000001 #define DOMOVE_RUSH 0x00000002 - dungeon dungeons[MAXDUNGEON]; /* ini'ed by init_dungeon() */ - dest_area dndest; boolean defer_see_monsters; - struct dgn_topology dungeon_topology; - int doors_alloc; /* doors-array allocated size */ - /* Holds the coordinates of all doors on the level. - * mkroom structs each have a fdoor which is their first door in this array. - */ - coord *doors; /* array of door locations */ /* dig.c */ boolean did_dig_msg; @@ -324,19 +336,25 @@ struct instance_globals_d { /* mon.c */ boolean disintegested; - /* o_init.c */ - short disco[NUM_OBJECTS]; - /* objname.c */ /* distantname used by distant_name() to pass extra information to xname_flags(); it would be much cleaner if this were a parameter, but that would require all xname() and doname() calls to be modified */ int distantname; + /* pickup.c */ + boolean decor_fumble_override; + boolean decor_levitate_override; + + /* windows.c */ #ifdef DUMPHTML boolean dumping_list; #endif + /* new */ + boolean deferred_showpaths; + char *deferred_showpaths_dir; + boolean havestate; unsigned long magic; /* validate that structure layout is preserved */ }; @@ -395,6 +413,7 @@ struct instance_globals_g { coordxy gbuf_stop[ROWNO]; /* do_name.c */ + coordxy getposx, getposy; /* cursor position in case of async resize */ struct selectionvar *gloc_filter_map; int gloc_filter_floodfill_match_glyph; @@ -413,6 +432,12 @@ struct instance_globals_g { /* invent.c */ long glyph_reset_timestamp; + /* nhlua.c */ + boolean gmst_stored; + long gmst_moves; + struct obj *gmst_invent; + genericptr_t *gmst_ubak, *gmst_disco, *gmst_mvitals; + /* pline.c */ struct gamelog_line *gamelog; @@ -432,7 +457,6 @@ struct instance_globals_h { /* decl.c */ const char *hname; /* name of the game (argv[0] of main) */ - int hackpid; /* current process id */ #if defined(MICRO) || defined(WIN32) char hackdir[PATHLEN]; /* where rumors, help, record are */ #endif /* MICRO || WIN32 */ @@ -454,7 +478,6 @@ struct instance_globals_i { /* decl.c */ int in_doagain; - coord inv_pos; boolean in_mklev; boolean in_steed_dismounting; struct obj *invent; @@ -469,12 +492,18 @@ struct instance_globals_i { unsigned invbufsiz; int in_sync_perminvent; + /* mon.c */ + struct monst **itermonarr; /* temporary array of all N monsters + * on the current level */ + /* restore.c */ struct bucket *id_map; /* sp_lev.c */ boolean in_mk_themerooms; + /* new */ + boolean havestate; unsigned long magic; /* validate that structure layout is preserved */ }; @@ -490,9 +519,10 @@ struct instance_globals_j { struct instance_globals_k { + coord kickedloc; /* location hero just kicked */ + /* decl.c */ struct obj *kickedobj; /* object in flight due to kicking */ - struct kinfo killer; /* read.c */ boolean known; @@ -506,10 +536,7 @@ struct instance_globals_l { /* cmd.c */ cmdcount_nht last_command_count; - /* dbridge.c */ - schar lastseentyp[COLNO][ROWNO]; /* last seen/touched dungeon typ */ - struct linfo level_info[MAXLINFO]; - dlevel_t level; /* level map */ + /* decl.c (before being incorporated into instance_globals_*) */ #if defined(UNIX) || defined(VMS) int locknum; /* max num of simultaneous users */ #endif @@ -530,6 +557,9 @@ struct instance_globals_l { /* mklev.c */ genericptr_t luathemes[MAXDUNGEON]; + /* mon.c */ + unsigned last_hider; /* m_id of hides-under mon seen going into hiding */ + /* nhlan.c */ #ifdef MAX_LAN_USERNAME char lusername[MAX_LAN_USERNAME]; @@ -539,6 +569,8 @@ struct instance_globals_l { /* nhlua.c */ genericptr_t luacore; /* lua_State * */ char lua_warnbuf[BUFSZ]; + int loglua; + int lua_sid; /* options.c */ boolean loot_reset_justpicked; @@ -582,7 +614,6 @@ struct instance_globals_m { struct multishot m_shot; boolean mrg_to_wielded; /* weapon picked is merged with wielded one */ struct menucoloring *menu_colorings; - long moves; /* turn counter */ struct obj *migrating_objs; /* objects moving to another dungeon level */ /* display.c */ @@ -591,14 +622,10 @@ struct instance_globals_m { /* dog.c */ struct monst *mydogs; /* monsters that went down/up together with @ */ struct monst *migrating_mons; /* monsters moving to another level */ - struct mvitals mvitals[NUMMONS]; /* dokick.c */ struct rm *maploc; - /* dungeon.c */ - mapseen *mapseenchn; /*DUNGEON_OVERVIEW*/ - /* mhitu.c */ int mhitu_dieroll; @@ -606,10 +633,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 */ @@ -631,6 +658,11 @@ struct instance_globals_m { /* region.c */ int max_regions; + /* trap.c */ + boolean mentioned_water; /* set to True by water_damage() if it issues + * a message about water; dodip() should make + * POT_WATER should become discovered */ + boolean havestate; unsigned long magic; /* validate that structure layout is preserved */ }; @@ -642,15 +674,11 @@ struct instance_globals_n { /* decl.c */ const char *nomovemsg; - int nroom; int nsubroom; /* dokick.c */ struct rm nowhere; - /* dungeon.c */ - int n_dgns; /* number of dungeons (also used in mklev.c and do.c) */ - /* files.c */ int nesting; int no_sound_notified; /* run-time option processing: warn once if built @@ -673,9 +701,6 @@ struct instance_globals_n { /* questpgr.c */ char nambuf[CVT_BUF_SIZE]; - /* region.c */ - int n_regions; - /* restore.c */ int n_ids_mapped; @@ -694,6 +719,8 @@ struct instance_globals_n { struct instance_globals_o { + struct obj *objs_deleted; + /* dbridge.c */ struct entity occupants[ENTITIES]; @@ -706,7 +733,6 @@ struct instance_globals_o { /* symbols.c */ nhsym ov_primary_syms[SYM_MAX]; /* loaded primary symbols */ - nhsym ov_rogue_syms[SYM_MAX]; /* loaded rogue symbols */ /* invent.c */ /* query objlist callback: return TRUE if obj is at given location */ @@ -722,9 +748,14 @@ struct instance_globals_o { boolean opt_from_file; boolean opt_need_redraw; /* for doset() */ boolean opt_need_glyph_reset; + boolean opt_need_promptstyle; + boolean opt_reset_customcolors; + boolean opt_reset_customsymbols; + boolean opt_update_basic_palette; + boolean opt_symset_changed; /* pickup.c */ - int oldcap; /* last encumberance */ + int oldcap; /* last encumbrance */ /* restore.c */ struct fruit *oldfruit; @@ -732,7 +763,6 @@ struct instance_globals_o { /* rumors.c */ int oracle_flg; /* -1=>don't use, 0=>need init, 1=>init done */ - unsigned oracle_cnt; /* oracles are handled differently from rumors... */ unsigned long *oracle_loc; /* uhitm.c */ @@ -752,13 +782,9 @@ struct instance_globals_p { int polearm_range_max; /* decl.c */ - char plname[PL_NSIZ]; /* player name */ int plnamelen; /* length of plname[] if that came from getlogin() */ - char pl_character[PL_CSIZ]; char pl_race; /* character's race */ - char pl_fruit[PL_FSIZ]; struct plinemsg_type *plinemsg_types; - struct sinfo program_state; /* flags describing game's current state */ /* dog.c */ int petname_used; /* user preferred pet name has been used */ @@ -772,6 +798,8 @@ struct instance_globals_p { /* pickup.c */ boolean picked_filter; + int pickup_encumbrance; /* when picking up multiple items in a single + * operation, encumbrance after previous item */ /* pline.c */ unsigned pline_flags; @@ -800,24 +828,12 @@ struct instance_globals_p { struct instance_globals_q { - /* quest.c */ - struct q_score quest_status; - boolean havestate; unsigned long magic; /* validate that structure layout is preserved */ }; struct instance_globals_r { - /* decl.c */ - struct mkroom rooms[(MAXNROFROOMS + 1) * 2]; - - /* symbols.c */ - nhsym rogue_syms[SYM_MAX]; /* loaded rogue symbols */ - - /* extralev.c */ - struct rogueroom r[3][3]; - /* mkmaze.c */ boolean ransacked; @@ -846,14 +862,12 @@ struct instance_globals_s { messages in artifact_hit() */ /* decl.c */ - s_level * sp_levchn; stairway *stairs; /* smeq - stores room numbers for the purposes of determining which rooms have * been connected yet, and which haven't. * Not sure why this isn't just stored in struct mkroom directly. */ int smeq[MAXNROFROOMS + 1]; boolean stoned; /* done to monsters hit by 'c' */ - struct spell spl_book[MAXSPELL + 1]; struct mkroom *subrooms; /* do.c */ @@ -861,9 +875,9 @@ struct instance_globals_s { /* symbols.c */ struct symsetentry symset[NUM_GRAPHICS]; -#ifdef ENHANCED_SYMBOLS - struct symset_customization sym_customizations[NUM_GRAPHICS + 1]; /* adds UNICODESET */ -#endif + /* adds UNICODESET */ + struct symset_customization + sym_customizations[NUM_GRAPHICS + 1][custom_count]; nhsym showsyms[SYM_MAX]; /* symbols to be displayed */ /* files.c */ @@ -892,10 +906,11 @@ struct instance_globals_s { boolean simple_options_help; /* pickup.c */ + boolean sellobj_first; /* True => need sellobj_state(); False => don't */ boolean shop_filter; /* pline.c */ -#ifdef DUMPLOG +#ifdef DUMPLOG_CORE unsigned saved_pline_index; /* slot in saved_plines[] to use next */ char *saved_plines[DUMPLOG_MSG_COUNT]; #endif @@ -912,7 +927,7 @@ struct instance_globals_s { /* spells.c */ int spl_sortmode; /* index into spl_sortchoices[] */ - int *spl_orderindx; /* array of gs.spl_book[] indices */ + int *spl_orderindx; /* array of svs.spl_book[] indices */ /* steal.c */ unsigned int stealoid; /* object to be stolen */ @@ -931,7 +946,6 @@ struct instance_globals_t { struct trapinfo trapinfo; /* decl.c */ - char tune[6]; schar tbx; /* mthrowu: target x */ schar tby; /* mthrowu: target y */ char toplines[TBUFSZ]; @@ -949,19 +963,19 @@ 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; /* pickup.c */ struct obj *transfer_container; /* rumors.c */ long true_rumor_size; /* rumor size variables are signed so that value -1 - can be used as a flag */ - unsigned long true_rumor_start; /* rumor start offsets are unsigned because - they're handled via %lx format */ + * can be used as a flag */ + unsigned long true_rumor_start; /* rumor start offsets are unsigned due + * to use of %lx format */ long true_rumor_end; /* rumor end offsets are signed because they're - compared with [dlb_]ftell() */ + * compared with [dlb_]ftell() */ /* sp_lev.c */ boolean themeroom_failed; @@ -969,11 +983,13 @@ struct instance_globals_t { /* timeout.c */ /* ordered timer list */ struct fe *timer_base; /* "active" */ - unsigned long timer_id; /* topten.c */ winid toptenwin; + /* uhitm.c */ + int twohits; /* 0: single hit; 1: first of 2; 2: second of 2 */ + boolean havestate; unsigned long magic; /* validate that structure layout is preserved */ }; @@ -984,7 +1000,6 @@ struct instance_globals_u { boolean update_all; /* decl.c */ - dest_area updest; boolean unweapon; /* role.c */ @@ -992,8 +1007,6 @@ struct instance_globals_u { struct Race urace; /* player's race. May be munged in role_init() */ /* save.c */ - unsigned ustuck_id; /* need to preserve during save */ - unsigned usteed_id; /* need to preserve during save */ d_level uz_save; /* new stuff */ @@ -1044,6 +1057,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 */ @@ -1074,8 +1090,11 @@ struct instance_globals_x { /* lock.c */ struct xlock_s xlock; - /* mkmaze.c */ - int xmin, xmax; /* level boundaries x */ + /* objnam.c */ + char *xnamep; /* obuf[] returned by xname(), for use in doname() for + * bounds checking; differs from xname() return value + * due to reserving PREFIX bytes at start and possibly + * skipping leading "the " after constructing result */ /* sp_lev.c */ coordxy xstart, xsize; @@ -1090,9 +1109,6 @@ struct instance_globals_y { int y_maze_max; struct monst youmonst; - /* mkmaze.c */ - int ymin, ymax; /* level boundaries y */ - /* pline.c */ /* work buffer for You(), &c and verbalize() */ char *you_buf; @@ -1122,6 +1138,126 @@ struct instance_globals_z { unsigned long magic; /* validate that structure layout is preserved */ }; +struct instance_globals_saved_b { + /* dungeon.c */ + branch *branches; /* dungeon branch list */ + /* mkmaze.c */ + struct bubble *bbubbles; + /* o_init.c */ + int bases[MAXOCLASSES + 2]; /* make bases[MAXOCLASSES+1] available */ +}; + +struct instance_globals_saved_c { + /* decl.c */ + struct context_info context; +}; + +struct instance_globals_saved_d { + /* dungeon.c */ + dungeon dungeons[MAXDUNGEON]; /* ini'ed by init_dungeon() */ + struct dgn_topology dungeon_topology; + /* decl.c */ + dest_area dndest; + coord *doors; /* array of door locations */ + int doors_alloc; /* doors-array allocated size */ + /* o_init.c */ + short disco[NUM_OBJECTS]; +}; + +struct instance_globals_saved_e { + /* decl.c */ + struct exclusion_zone *exclusion_zones; +}; + +struct instance_globals_saved_h { + /* decl.c */ + int hackpid; /* current process id */ +}; + +struct instance_globals_saved_i { + /* decl.c */ + coord inv_pos; +}; + +struct instance_globals_saved_k { + /* decl.c */ + struct kinfo killer; +}; + +struct instance_globals_saved_l { + /* decl.c */ + schar lastseentyp[COLNO][ROWNO]; /* last seen/touched dungeon typ */ + dlevel_t level; /* level map */ + struct linfo level_info[MAXLINFO]; +}; + +struct instance_globals_saved_m { + /* dungeon.c */ + mapseen *mapseenchn; /*DUNGEON_OVERVIEW*/ + /* decl.c */ + long moves; /* turn counter */ + struct mvitals mvitals[NUMMONS]; +}; + +struct instance_globals_saved_n { + /* dungeon.c */ + int n_dgns; /* number of dungeons (also used in mklev.c and do.c) */ + /* mkroom.c */ + int nroom; + /* region.c */ + int n_regions; +}; + +struct instance_globals_saved_o { + /* rumors.c */ + unsigned oracle_cnt; /* oracles are handled differently from rumors... */ +}; + +struct instance_globals_saved_p { + /* decl.c */ + char plname[PL_NSIZ]; /* player name */ + char pl_character[PL_CSIZ]; + char pl_fruit[PL_FSIZ]; +}; + +struct instance_globals_saved_q { + /* quest.c */ + struct q_score quest_status; +}; + +struct instance_globals_saved_r { + /* mkroom.c */ + struct mkroom rooms[(MAXNROFROOMS + 1) * 2]; +}; + +struct instance_globals_saved_s { + /* decl.c */ + struct spell spl_book[MAXSPELL + 1]; + s_level *sp_levchn; +}; + +struct instance_globals_saved_t { + /* decl.c */ + char tune[6]; + /* timeout.c */ + unsigned long timer_id; +}; + +struct instance_globals_saved_u { + /* decl.c */ + dest_area updest; +}; + +struct instance_globals_saved_x { + /* mkmaze.c */ + int xmin, xmax; /* level boundaries x */ +}; + +struct instance_globals_saved_y { + /* mkmaze.c */ + int ymin, ymax; /* level boundaries y */ +}; + extern struct instance_globals_a ga; extern struct instance_globals_b gb; extern struct instance_globals_c gc; @@ -1148,15 +1284,36 @@ extern struct instance_globals_w gw; extern struct instance_globals_x gx; extern struct instance_globals_y gy; extern struct instance_globals_z gz; +extern struct instance_globals_saved_b svb; +extern struct instance_globals_saved_c svc; +extern struct instance_globals_saved_d svd; +extern struct instance_globals_saved_e sve; +extern struct instance_globals_saved_h svh; +extern struct instance_globals_saved_i svi; +extern struct instance_globals_saved_k svk; +extern struct instance_globals_saved_l svl; +extern struct instance_globals_saved_m svm; +extern struct instance_globals_saved_n svn; +extern struct instance_globals_saved_o svo; +extern struct instance_globals_saved_p svp; +extern struct instance_globals_saved_q svq; +extern struct instance_globals_saved_r svr; +extern struct instance_globals_saved_s svs; +extern struct instance_globals_saved_t svt; +extern struct instance_globals_saved_u svu; +extern struct instance_globals_saved_x svx; +extern struct instance_globals_saved_y svy; +extern struct sinfo program_state; /* flags describing game's current state */ struct const_globals { const struct obj zeroobj; /* used to zero out a struct obj */ const struct monst zeromonst; /* used to zero out a struct monst */ const anything zeroany; /* used to zero out union any */ + const NhRect zeroNhRect; /* used to zero out NhRect */ }; extern const struct const_globals cg; -#endif /* DECL_H */ - +extern struct obj hands_obj; +#endif /* DECL_H */ diff --git a/include/defsym.h b/include/defsym.h index 969495eecc..e1d84cb893 100644 --- a/include/defsym.h +++ b/include/defsym.h @@ -1,8 +1,7 @@ -/* NetHack 3.7 defsym.h */ +/* NetHack 3.7 defsym.h $NHDT-Date: 1725653007 2024/09/06 20:03:27 $ $NHDT-Branch: NetHack-3.7 $ $NHDT-Revision: 1.25 $ */ /* Copyright (c) 2016 by Pasi Kallinen */ /* NetHack may be freely redistributed. See license for details. */ - /* This header is included in multiple places to produce different code depending on its use. Its purpose is to @@ -28,20 +27,17 @@ to #include defsym.h) - in win/share/tilemap.c for processing a tile file (define PCHAR_TILES prior to #include defsym.h). + - in src/allmain.c for setting up the dumping of several enums + (define DUMP_ENUMS_PCHAR, DUMP_ENUMS_MONSYS, DUMP_ENUMS_MONSYMS_DEFCHAR + DUMP_ENUMS_OBJCLASS_DEFCHARS, DUMP_ENUMS_OBJCLASS_DEFCHARS + DUMP_ENUMS_OBJCLASS_CLASSES, DUMP_ENUMS_OBJCLASS_SYMS) */ -#ifdef CLR -#undef CLR -#endif - -#ifdef TEXTCOLOR -#define CLR(n) n -#else -#define CLR(n) -#endif - -#if defined(PCHAR_S_ENUM) || defined(PCHAR_PARSE) \ - || defined(PCHAR_DRAWING) || defined(PCHAR_TILES) +#if defined(PCHAR_S_ENUM) \ + || defined(PCHAR_PARSE) \ + || defined(PCHAR_DRAWING) \ + || defined(PCHAR_TILES) \ + || defined(DUMP_ENUMS_PCHAR) /* PCHAR(idx, ch, sym, desc, clr) @@ -76,138 +72,139 @@ #elif defined(PCHAR_TILES) /* win/share/tilemap.c */ #define PCHAR(idx, ch, sym, desc, clr) { sym, desc, desc }, +#define PCHAR2(idx, ch, sym, tilenm, desc, clr) { sym, tilenm, desc }, + +#elif defined(DUMP_ENUMS_PCHAR) +/* allmain.c */ +#define PCHAR(idx, ch, sym, desc, clr) { sym, #sym }, +#ifndef PCHAR2 +#define PCHAR2(idx, ch, sym, tilenm, desc, clr) { sym, #sym }, +#endif #endif /* PCHAR with extra arg */ -#if defined(PCHAR_TILES) -#define PCHAR2(idx, ch, sym, tilenm, desc, clr) { sym, tilenm, desc }, -#else +#ifndef PCHAR2 #define PCHAR2(idx, ch, sym, tilenm, desc, clr) PCHAR(idx, ch, sym, desc, clr) #endif - PCHAR2( 0, ' ', S_stone, "dark part of a room", "stone", CLR(NO_COLOR)) - PCHAR2( 1, '|', S_vwall, "vertical wall", "wall", CLR(CLR_GRAY)) - PCHAR2( 2, '-', S_hwall, "horizontal wall", "wall", CLR(CLR_GRAY)) - PCHAR2( 3, '-', S_tlcorn, "top left corner wall", "wall", CLR(CLR_GRAY)) - PCHAR2( 4, '-', S_trcorn, "top right corner wall", "wall", CLR(CLR_GRAY)) - PCHAR2( 5, '-', S_blcorn, "bottom left corner wall", - "wall", CLR(CLR_GRAY)) - PCHAR2( 6, '-', S_brcorn, "bottom right corner wall", - "wall", CLR(CLR_GRAY)) - PCHAR2( 7, '-', S_crwall, "cross wall", "wall", CLR(CLR_GRAY)) - PCHAR2( 8, '-', S_tuwall, "tuwall", "wall", CLR(CLR_GRAY)) - PCHAR2( 9, '-', S_tdwall, "tdwall", "wall", CLR(CLR_GRAY)) - PCHAR2(10, '|', S_tlwall, "tlwall", "wall", CLR(CLR_GRAY)) - PCHAR2(11, '|', S_trwall, "trwall", "wall", CLR(CLR_GRAY)) + PCHAR2( 0, ' ', S_stone, "dark part of a room", "stone", NO_COLOR) + PCHAR2( 1, '|', S_vwall, "vertical wall", "wall", CLR_GRAY) + PCHAR2( 2, '-', S_hwall, "horizontal wall", "wall", CLR_GRAY) + PCHAR2( 3, '-', S_tlcorn, "top left corner wall", "wall", CLR_GRAY) + PCHAR2( 4, '-', S_trcorn, "top right corner wall", "wall", CLR_GRAY) + PCHAR2( 5, '-', S_blcorn, "bottom left corner wall", "wall", CLR_GRAY) + PCHAR2( 6, '-', S_brcorn, "bottom right corner wall", "wall", CLR_GRAY) + PCHAR2( 7, '-', S_crwall, "cross wall", "wall", CLR_GRAY) + PCHAR2( 8, '-', S_tuwall, "tuwall", "wall", CLR_GRAY) + PCHAR2( 9, '-', S_tdwall, "tdwall", "wall", CLR_GRAY) + PCHAR2(10, '|', S_tlwall, "tlwall", "wall", CLR_GRAY) + PCHAR2(11, '|', S_trwall, "trwall", "wall", CLR_GRAY) /* start cmap A */ - PCHAR2(12, '.', S_ndoor, "no door", "doorway", CLR(CLR_GRAY)) - PCHAR2(13, '-', S_vodoor, "vertical open door", - "open door", CLR(CLR_BROWN)) - PCHAR2(14, '|', S_hodoor, "horizontal open door", - "open door", CLR(CLR_BROWN)) + PCHAR2(12, '.', S_ndoor, "no door", "doorway", CLR_GRAY) + PCHAR2(13, '-', S_vodoor, "vertical open door", "open door", CLR_BROWN) + PCHAR2(14, '|', S_hodoor, "horizontal open door", "open door", CLR_BROWN) PCHAR2(15, '+', S_vcdoor, "vertical closed door", - "closed door", CLR(CLR_BROWN)) + "closed door", CLR_BROWN) PCHAR2(16, '+', S_hcdoor, "horizontal closed door", - "closed door", CLR(CLR_BROWN)) - PCHAR( 17, '#', S_bars, "iron bars", CLR(HI_METAL)) - PCHAR( 18, '#', S_tree, "tree", CLR(CLR_GREEN)) - PCHAR( 19, '.', S_room, "floor of a room", CLR(CLR_GRAY)) - PCHAR( 20, '.', S_darkroom, "dark part of a room", CLR(CLR_BLACK)) - PCHAR( 21, '`', S_engroom, "engraved part of a room", CLR(CLR_BRIGHT_BLUE)) - PCHAR2(22, '#', S_corr, "dark corridor", "corridor", CLR(CLR_GRAY)) - PCHAR( 23, '#', S_litcorr, "lit corridor", CLR(CLR_GRAY)) - PCHAR( 24, '#', S_engrcorr, "engraved part of a corridor", CLR(CLR_BRIGHT_BLUE)) - PCHAR2(25, '<', S_upstair, "up stairs", "staircase up", CLR(CLR_GRAY)) - PCHAR2(26, '>', S_dnstair, "down stairs", "staircase down", CLR(CLR_GRAY)) - PCHAR2(27, '<', S_upladder, "up ladder", "ladder up", CLR(CLR_BROWN)) - PCHAR2(28, '>', S_dnladder, "down ladder", "ladder down", CLR(CLR_BROWN)) - PCHAR( 29, '<', S_brupstair, "branch staircase up", CLR(CLR_YELLOW)) - PCHAR( 30, '>', S_brdnstair, "branch staircase down", CLR(CLR_YELLOW)) - PCHAR( 31, '<', S_brupladder, "branch ladder up", CLR(CLR_YELLOW)) - PCHAR( 32, '>', S_brdnladder, "branch ladder down", CLR(CLR_YELLOW)) + "closed door", CLR_BROWN) + PCHAR( 17, '#', S_bars, "iron bars", HI_METAL) + PCHAR( 18, '#', S_tree, "tree", CLR_GREEN) + PCHAR( 19, '.', S_room, "floor of a room", CLR_GRAY) + PCHAR( 20, '.', S_darkroom, "dark part of a room", CLR_BLACK) + PCHAR2(21, '~', S_engroom, "engraving in a room", "engraving", + CLR_BRIGHT_BLUE) + PCHAR2(22, '#', S_corr, "dark corridor", "corridor", CLR_GRAY) + PCHAR( 23, '#', S_litcorr, "lit corridor", CLR_GRAY) + PCHAR2(24, '~', S_engrcorr, "engraving in a corridor", "engraving", + CLR_BRIGHT_BLUE) + PCHAR2(25, '<', S_upstair, "up stairs", "staircase up", CLR_GRAY) + PCHAR2(26, '>', S_dnstair, "down stairs", "staircase down", CLR_GRAY) + PCHAR2(27, '<', S_upladder, "up ladder", "ladder up", CLR_BROWN) + PCHAR2(28, '>', S_dnladder, "down ladder", "ladder down", CLR_BROWN) + PCHAR( 29, '<', S_brupstair, "branch staircase up", CLR_YELLOW) + PCHAR( 30, '>', S_brdnstair, "branch staircase down", CLR_YELLOW) + PCHAR( 31, '<', S_brupladder, "branch ladder up", CLR_YELLOW) + PCHAR( 32, '>', S_brdnladder, "branch ladder down", CLR_YELLOW) /* end cmap A */ - PCHAR( 33, '_', S_altar, "altar", CLR(CLR_GRAY)) - PCHAR( 34, '~', S_engraving, "engraving", CLR(CLR_BROWN)) - PCHAR( 35, '.', S_magicplatform, "magic platform", CLR(CLR_BRIGHT_MAGENTA)) + PCHAR( 33, '_', S_altar, "altar", CLR_GRAY) /* start cmap B */ - PCHAR( 36, '|', S_grave, "grave", CLR(CLR_WHITE)) - PCHAR2(37, '\\', S_throne, "throne", "opulent throne", CLR(HI_GOLD)) - PCHAR( 38, '{', S_sink, "sink", CLR(CLR_WHITE)) - PCHAR( 39, '{', S_fountain, "fountain", CLR(CLR_BRIGHT_BLUE)) - PCHAR2(40, '}', S_pool, "pool", "water", CLR(CLR_BLUE)) - PCHAR( 41, '.', S_ice, "ice", CLR(CLR_CYAN)) - PCHAR( 42, ',', S_grass, "grass", CLR(CLR_GREEN)) - PCHAR( 43, '}', S_lava, "molten lava", CLR(CLR_RED)) - PCHAR( 44, '}', S_lavawall, "wall of lava", CLR(CLR_ORANGE)) - PCHAR2(45, '.', S_vodbridge, "vertical open drawbridge", - "lowered drawbridge", CLR(CLR_BROWN)) - PCHAR2(46, '.', S_hodbridge, "horizontal open drawbridge", - "lowered drawbridge", CLR(CLR_BROWN)) - PCHAR2(47, '#', S_vcdbridge, "vertical closed drawbridge", - "raised drawbridge", CLR(CLR_BROWN)) - PCHAR2(48, '#', S_hcdbridge, "horizontal closed drawbridge", - "raised drawbridge", CLR(CLR_BROWN)) - PCHAR( 49, ' ', S_air, "air", CLR(CLR_BLACK)) - PCHAR( 50, '#', S_cloud, "cloud", CLR(CLR_GRAY)) - PCHAR( 51, '}', S_water, "water", CLR(CLR_BLUE)) + PCHAR( 34, '|', S_grave, "grave", CLR_WHITE) + PCHAR2(35, '\\', S_throne, "throne", "opulent throne", HI_GOLD) + PCHAR( 36, '{', S_sink, "sink", CLR_WHITE) + PCHAR( 37, '{', S_fountain, "fountain", CLR_BRIGHT_BLUE) + /* the S_pool symbol is used for both POOL terrain and MOAT terrain */ + PCHAR2(38, '}', S_pool, "pool", "water", CLR_BLUE) + PCHAR( 39, '.', S_ice, "ice", CLR_CYAN) + PCHAR( 40, '}', S_lava, "molten lava", CLR_RED) + PCHAR( 41, '}', S_lavawall, "wall of lava", CLR_ORANGE) + PCHAR2(42, '.', S_vodbridge, "vertical open drawbridge", + "lowered drawbridge", CLR_BROWN) + PCHAR2(43, '.', S_hodbridge, "horizontal open drawbridge", + "lowered drawbridge", CLR_BROWN) + PCHAR2(44, '#', S_vcdbridge, "vertical closed drawbridge", + "raised drawbridge", CLR_BROWN) + PCHAR2(45, '#', S_hcdbridge, "horizontal closed drawbridge", + "raised drawbridge", CLR_BROWN) + PCHAR( 46, ' ', S_air, "air", CLR_CYAN) + PCHAR( 47, '#', S_cloud, "cloud", CLR_GRAY) + /* the S_water symbol is used for WATER terrain: wall of water in the + dungeon and Plane of Water in the endgame */ + PCHAR( 48, '}', S_water, "water", CLR_BRIGHT_BLUE) /* end dungeon characters */ /* */ /* begin traps */ /* */ - PCHAR( 52, '^', S_arrow_trap, "arrow trap", CLR(HI_METAL)) - PCHAR( 53, '^', S_dart_trap, "dart trap", CLR(HI_METAL)) - PCHAR( 54, '^', S_falling_rock_trap, "falling rock trap", CLR(CLR_GRAY)) - PCHAR( 55, '^', S_squeaky_board, "squeaky board", CLR(CLR_BROWN)) - PCHAR( 56, '^', S_bear_trap, "bear trap", CLR(HI_METAL)) - PCHAR( 57, '^', S_land_mine, "land mine", CLR(CLR_RED)) - PCHAR( 58, '^', S_rolling_boulder_trap, "rolling boulder trap", - CLR(CLR_GRAY)) - PCHAR( 59, '^', S_sleeping_gas_trap, "sleeping gas trap", CLR(HI_ZAP)) - PCHAR( 60, '^', S_rust_trap, "rust trap", CLR(CLR_BLUE)) - PCHAR( 61, '^', S_fire_trap, "fire trap", CLR(CLR_ORANGE)) - PCHAR( 62, '^', S_cold_trap, "cold trap", CLR(CLR_WHITE)) - PCHAR( 63, '^', S_pit, "pit", CLR(CLR_BLACK)) - PCHAR( 64, '^', S_spiked_pit, "spiked pit", CLR(CLR_BLACK)) - PCHAR( 65, '^', S_hole, "hole", CLR(CLR_BROWN)) - PCHAR( 66, '^', S_trap_door, "trap door", CLR(CLR_BROWN)) - PCHAR( 67, '^', S_teleportation_trap, "teleportation trap", - CLR(CLR_MAGENTA)) - PCHAR( 68, '^', S_level_teleporter, "level teleporter", CLR(CLR_MAGENTA)) - PCHAR( 69, '^', S_magic_portal, "magic portal", CLR(CLR_BRIGHT_MAGENTA)) - PCHAR( 70, '"', S_web, "web", CLR(CLR_GRAY)) - PCHAR( 71, '^', S_statue_trap, "statue trap", CLR(CLR_GRAY)) - PCHAR( 72, '^', S_magic_trap, "magic trap", CLR(HI_ZAP)) - PCHAR2(73, '^', S_anti_magic_trap, "anti magic trap", "anti-magic field", - CLR(HI_ZAP)) - PCHAR( 74, '^', S_polymorph_trap, "polymorph trap", CLR(CLR_BRIGHT_GREEN)) - PCHAR( 75, '~', S_vibrating_square, "vibrating square", CLR(CLR_YELLOW)) - PCHAR( 76, '^', S_trapped_door, "trapped door", CLR(CLR_ORANGE)) - PCHAR( 77, '^', S_trapped_chest, "trapped chest", CLR(CLR_ORANGE)) + PCHAR( 49, '^', S_arrow_trap, "arrow trap", HI_METAL) + PCHAR( 50, '^', S_dart_trap, "dart trap", HI_METAL) + PCHAR( 51, '^', S_falling_rock_trap, "falling rock trap", CLR_GRAY) + PCHAR( 52, '^', S_squeaky_board, "squeaky board", CLR_BROWN) + PCHAR( 53, '^', S_bear_trap, "bear trap", HI_METAL) + PCHAR( 54, '^', S_land_mine, "land mine", CLR_RED) + PCHAR( 55, '^', S_rolling_boulder_trap, "rolling boulder trap", CLR_GRAY) + PCHAR( 56, '^', S_sleeping_gas_trap, "sleeping gas trap", HI_ZAP) + PCHAR( 57, '^', S_rust_trap, "rust trap", CLR_BLUE) + PCHAR( 58, '^', S_fire_trap, "fire trap", CLR_ORANGE) + PCHAR( 59, '^', S_pit, "pit", CLR_BLACK) + PCHAR( 60, '^', S_spiked_pit, "spiked pit", CLR_BLACK) + PCHAR( 61, '^', S_hole, "hole", CLR_BROWN) + PCHAR( 62, '^', S_trap_door, "trap door", CLR_BROWN) + PCHAR( 63, '^', S_teleportation_trap, "teleportation trap", CLR_MAGENTA) + PCHAR( 64, '^', S_level_teleporter, "level teleporter", CLR_MAGENTA) + PCHAR( 65, '^', S_magic_portal, "magic portal", CLR_BRIGHT_MAGENTA) + PCHAR( 66, '"', S_web, "web", CLR_GRAY) + PCHAR( 67, '^', S_statue_trap, "statue trap", CLR_GRAY) + PCHAR( 68, '^', S_magic_trap, "magic trap", HI_ZAP) + PCHAR2(69, '^', S_anti_magic_trap, "anti magic trap", "anti-magic field", + HI_ZAP) + PCHAR( 70, '^', S_polymorph_trap, "polymorph trap", CLR_BRIGHT_GREEN) + PCHAR( 71, '~', S_vibrating_square, "vibrating square", CLR_MAGENTA) + PCHAR( 72, '^', S_trapped_door, "trapped door", CLR_ORANGE) + PCHAR( 73, '^', S_trapped_chest, "trapped chest", CLR_ORANGE) /* end traps */ /* end cmap B */ - /* */ - /* begin special effects */ - /* */ - /* zap colors are changed by map_glyphinfo() to match type of beam */ - /* */ - PCHAR2(78, '|', S_vbeam, "vertical beam", "", CLR(CLR_GRAY)) - PCHAR2(79, '-', S_hbeam, "horizontal beam", "", CLR(CLR_GRAY)) - PCHAR2(80, '\\', S_lslant, "left slant beam", "", CLR(CLR_GRAY)) - PCHAR2(81, '/', S_rslant, "right slant beam", "", CLR(CLR_GRAY)) + /* */ + /* begin special effects */ + /* */ + /* zap colors are changed by reset_glyphmap() to match type of beam */ + /* */ + PCHAR2(74, '|', S_vbeam, "vertical beam", "", CLR_GRAY) + PCHAR2(75, '-', S_hbeam, "horizontal beam", "", CLR_GRAY) + PCHAR2(76, '\\', S_lslant, "left slant beam", "", CLR_GRAY) + PCHAR2(77, '/', S_rslant, "right slant beam", "", CLR_GRAY) /* start cmap C */ - PCHAR2(82, '*', S_digbeam, "dig beam", "", CLR(CLR_WHITE)) - PCHAR2(83, '!', S_flashbeam, "flash beam", "", CLR(CLR_WHITE)) - PCHAR2(84, ')', S_boomleft, "boom left", "", CLR(HI_WOOD)) - PCHAR2(85, '(', S_boomright, "boom right", "", CLR(HI_WOOD)) - /* */ + PCHAR2(78, '*', S_digbeam, "dig beam", "", CLR_WHITE) + PCHAR2(79, '!', S_flashbeam, "flash beam", "", CLR_WHITE) + PCHAR2(80, ')', S_boomleft, "boom left", "", HI_WOOD) + PCHAR2(81, '(', S_boomright, "boom right", "", HI_WOOD) /* 4 magic shield symbols */ - /* */ - PCHAR2(86, '0', S_ss1, "shield1", "", CLR(HI_ZAP)) - PCHAR2(87, '#', S_ss2, "shield2", "", CLR(HI_ZAP)) - PCHAR2(88, '@', S_ss3, "shield3", "", CLR(HI_ZAP)) - PCHAR2(89, '*', S_ss4, "shield4", "", CLR(HI_ZAP)) - PCHAR( 90, '#', S_poisoncloud, "poison cloud", CLR(CLR_BRIGHT_GREEN)) - PCHAR( 91, '?', S_goodpos, "valid position", CLR(CLR_BRIGHT_GREEN)) + PCHAR2(82, '0', S_ss1, "shield1", "", HI_ZAP) + PCHAR2(83, '#', S_ss2, "shield2", "", HI_ZAP) + PCHAR2(84, '@', S_ss3, "shield3", "", HI_ZAP) + PCHAR2(85, '*', S_ss4, "shield4", "", HI_ZAP) + PCHAR( 86, '#', S_poisoncloud, "poison cloud", CLR_BRIGHT_GREEN) + /* for a time S_goodpos was a question mark, but dollar sign is the + default keystroke for getpos() to toggle goodpos glyphs on or off */ + PCHAR( 87, '$', S_goodpos, "valid position", HI_ZAP) /* end cmap C */ /* */ /* The 8 swallow symbols. Do NOT separate. */ @@ -221,14 +218,14 @@ /* 4 5 6 */ /* 7 8 9 */ /* */ - PCHAR2(92, '/', S_sw_tl, "swallow top left", "", CLR(CLR_GREEN)) /* 1 */ - PCHAR2(93, '-', S_sw_tc, "swallow top center", "", CLR(CLR_GREEN)) /* 2 */ - PCHAR2(94, '\\', S_sw_tr, "swallow top right", "", CLR(CLR_GREEN)) /* 3 */ - PCHAR2(95, '|', S_sw_ml, "swallow middle left", "", CLR(CLR_GREEN)) /* 4 */ - PCHAR2(96, '|', S_sw_mr, "swallow middle right", "", CLR(CLR_GREEN)) /* 6 */ - PCHAR2(97, '\\', S_sw_bl, "swallow bottom left", "", CLR(CLR_GREEN)) /* 7 */ - PCHAR2(98, '-', S_sw_bc, "swallow bottom center", "", CLR(CLR_GREEN)) /* 8 */ - PCHAR2(99, '/', S_sw_br, "swallow bottom right", "", CLR(CLR_GREEN)) /* 9 */ + PCHAR2(88, '/', S_sw_tl, "swallow top left", "", CLR_GREEN) /*1*/ + PCHAR2(89, '-', S_sw_tc, "swallow top center", "", CLR_GREEN) /*2*/ + PCHAR2(90, '\\', S_sw_tr, "swallow top right", "", CLR_GREEN) /*3*/ + PCHAR2(91, '|', S_sw_ml, "swallow middle left", "", CLR_GREEN) /*4*/ + PCHAR2(92, '|', S_sw_mr, "swallow middle right", "", CLR_GREEN) /*6*/ + PCHAR2(93, '\\', S_sw_bl, "swallow bottom left", "", CLR_GREEN) /*7*/ + PCHAR2(94, '-', S_sw_bc, "swallow bottom center", "", CLR_GREEN) /*8*/ + PCHAR2(95, '/', S_sw_br, "swallow bottom right", "", CLR_GREEN) /*9*/ /* */ /* explosion colors are changed by reset_glyphmap() to match */ /* the type of expl. */ @@ -239,21 +236,31 @@ /* |@| */ /* \-/ */ /* */ - PCHAR2(100, '/', S_expl_tl, "explosion top left", "", CLR(CLR_ORANGE)) - PCHAR2(101, '-', S_expl_tc, "explosion top center", "", CLR(CLR_ORANGE)) - PCHAR2(102, '\\', S_expl_tr, "explosion top right", "", CLR(CLR_ORANGE)) - PCHAR2(103, '|', S_expl_ml, "explosion middle left", "", CLR(CLR_ORANGE)) - PCHAR2(104, ' ', S_expl_mc, "explosion middle center", "", CLR(CLR_ORANGE)) - PCHAR2(105, '|', S_expl_mr, "explosion middle right", "", CLR(CLR_ORANGE)) - PCHAR2(106, '\\', S_expl_bl, "explosion bottom left", "", CLR(CLR_ORANGE)) - PCHAR2(107, '-', S_expl_bc, "explosion bottom center", "", CLR(CLR_ORANGE)) - PCHAR2(108, '/', S_expl_br, "explosion bottom right", "", CLR(CLR_ORANGE)) + PCHAR2(96, '/', S_expl_tl, "explosion top left", "", CLR_ORANGE) + PCHAR2(97, '-', S_expl_tc, "explosion top center", "", CLR_ORANGE) + PCHAR2(98, '\\', S_expl_tr, "explosion top right", "", CLR_ORANGE) + PCHAR2(99, '|', S_expl_ml, "explosion middle left", "", CLR_ORANGE) + PCHAR2(100, ' ', S_expl_mc, "explosion middle center", "", CLR_ORANGE) + PCHAR2(101, '|', S_expl_mr, "explosion middle right", "", CLR_ORANGE) + PCHAR2(102, '\\', S_expl_bl, "explosion bottom left", "", CLR_ORANGE) + PCHAR2(103, '-', S_expl_bc, "explosion bottom center", "", CLR_ORANGE) + PCHAR2(104, '/', S_expl_br, "explosion bottom right", "", CLR_ORANGE) + /* begin cmap D: xNetHack-specific symbols moved to the end here to avoid + * colliding with upstream's numbering */ + PCHAR(105, ',', S_grass, "grass", CLR_GREEN) + PCHAR(106, '.', S_magicplatform, "magic platform", CLR_BRIGHT_MAGENTA) + /* end cmap D */ #undef PCHAR #undef PCHAR2 -#endif /* PCHAR_S_ENUM || PCHAR_PARSE || PCHAR_DRAWING || PCHAR_TILES */ +#endif /* PCHAR_S_ENUM || PCHAR_PARSE || PCHAR_DRAWING || PCHAR_TILES + * || DUMP_ENUMS_PCHAR */ -#if defined(MONSYMS_S_ENUM) || defined(MONSYMS_DEFCHAR_ENUM) \ - || defined(MONSYMS_PARSE) || defined(MONSYMS_DRAWING) +#if defined(MONSYMS_S_ENUM) \ + || defined(MONSYMS_DEFCHAR_ENUM) \ + || defined(MONSYMS_PARSE) \ + || defined(MONSYMS_DRAWING) \ + || defined(DUMP_ENUMS_MONSYMS) \ + || defined(DUMP_ENUMS_MONSYMS_DEFCHAR) /* MONSYM(idx, ch, sym desc) @@ -279,6 +286,15 @@ #elif defined(MONSYMS_DRAWING) /* drawing.c */ #define MONSYM(idx, ch, basename, sym, desc) { DEF_##basename, "", desc }, + +/* allmain.c */ +#elif defined(DUMP_ENUMS_MONSYMS) +#define MONSYM(idx, ch, basename, sym, desc) { sym, #sym }, + +#elif defined(DUMP_ENUMS_MONSYMS_DEFCHAR) +#define MONSYM(idx, ch, basename, sym, desc) \ + { DEF_##basename, "DEF_" #basename }, + #endif MONSYM( 1, 'a', ANT, S_ANT, "ant or other insect") @@ -355,11 +371,17 @@ #undef MONSYM #endif /* MONSYMS_S_ENUM || MONSYMS_DEFCHAR_ENUM || MONSYMS_PARSE - * || MONSYMS_DRAWING */ - -#if defined(OBJCLASS_S_ENUM) || defined(OBJCLASS_DEFCHAR_ENUM) \ - || defined(OBJCLASS_CLASS_ENUM) || defined(OBJCLASS_PARSE) \ - || defined (OBJCLASS_DRAWING) + * || MONSYMS_DRAWING || DUMP_ENUMS_MONSYMS) + * || DUMP_ENUMS_MONSYMS_DEFCHAR */ + +#if defined(OBJCLASS_S_ENUM) \ + || defined(OBJCLASS_DEFCHAR_ENUM) \ + || defined(OBJCLASS_CLASS_ENUM) \ + || defined(OBJCLASS_PARSE) \ + || defined(OBJCLASS_DRAWING) \ + || defined(DUMP_ENUMS_OBJCLASS_DEFCHARS) \ + || defined(DUMP_ENUMS_OBJCLASS_CLASSES) \ + || defined(DUMP_ENUMS_OBJCLASS_SYMS) /* OBJCLASS(idx, ch, basename, sym, name, explain) @@ -407,6 +429,21 @@ /* drawing.c */ #define OBJCLASS(idx, ch, basename, sym, name, explain) \ { basename##_SYM, name, explain }, + +#elif defined(DUMP_ENUMS_OBJCLASS_DEFCHARS) +/* allmain.c */ +#define OBJCLASS(idx, ch, basename, sym, name, explain) \ + { basename##_SYM, #basename "_SYM" }, + +#elif defined(DUMP_ENUMS_OBJCLASS_CLASSES) +/* allmain.c */ +#define OBJCLASS(idx, ch, basename, sym, name, explain) \ + { basename##_CLASS, #basename "_CLASS" }, + +#elif defined(DUMP_ENUMS_OBJCLASS_SYMS) +/* allmain.c */ +#define OBJCLASS(idx, ch, basename, sym, name, explain) \ + { sym , #sym }, #endif /* OBJCLASS with extra arg */ @@ -416,6 +453,15 @@ #elif defined(OBJCLASS_DRAWING) #define OBJCLASS2(idx, ch, basename, sname, sym, name, explain) \ { sname, name, explain }, +#elif defined(DUMP_ENUMS_OBJCLASS_DEFCHARS) +#define OBJCLASS2(idx, ch, basename, sname, sym, name, explain) \ + { sname, #sname }, +#elif defined(DUMP_ENUMS_OBJCLASS_CLASSES) +#define OBJCLASS2(idx, ch, basename, sname, sym, name, explain) \ + { basename##_CLASS, #basename "_CLASS" }, +#elif defined(DUMP_ENUMS_OBJCLASS_SYMS) +#define OBJCLASS2(idx, ch, basename, sname, sym, name, explain) \ + { sym , #sym }, #else #define OBJCLASS2(idx, ch, basename, sname, sym, name, explain) \ OBJCLASS(idx, ch, basename, sym, name, explain) @@ -444,20 +490,26 @@ #undef OBJCLASS #undef OBJCLASS2 #endif /* OBJCLASS_S_ENUM || OBJCLASS_DEFCHAR_ENUM || OBJCLASS_CLASS_ENUM - * || OBJCLASS_PARSE || OBJCLASS_DRAWING */ - -#undef CLR + * || OBJCLASS_PARSE || OBJCLASS_DRAWING + * || DUMP_ENUMS_OBJCLASS_DEFCHARS || DUMP_ENUMS_OBJCLASS_CLASSES + * || DUMP_ENUMS_OBJCLASS_SYMS */ #ifdef DEBUG #if !defined(PCHAR_S_ENUM) && !defined(PCHAR_DRAWING) \ && !defined(PCHAR_PARSE) && !defined(PCHAR_TILES) \ + && !defined(DUMP_ENUMS_PCHAR) \ && !defined(MONSYMS_S_ENUM) && !defined(MONSYMS_DEFCHAR_ENUM) \ && !defined(MONSYMS_PARSE) && !defined(MONSYMS_DRAWING) \ + && !defined(DUMP_ENUMS_MONSYMS) \ + && !defined(DUMP_ENUMS_MONSYMS_DEFCHAR) \ && !defined(OBJCLASS_S_ENUM) && !defined(OBJCLASS_DEFCHAR_ENUM) \ && !defined(OBJCLASS_CLASS_ENUM) && !defined(OBJCLASS_PARSE) \ - && !defined (OBJCLASS_DRAWING) + && !defined (OBJCLASS_DRAWING) \ + && !defined(DUMP_ENUMS_OBJCLASS_DEFCHARS) \ + && !defined(DUMP_ENUMS_OBJCLASS_CLASSES) \ + && !defined(DUMP_ENUMS_OBJCLASS_SYMS) #error Non-productive inclusion of defsym.h #endif -#endif +#endif /* DEBUG */ /* end of defsym.h */ diff --git a/include/display.h b/include/display.h index 7c25af949f..f347b5b38e 100644 --- a/include/display.h +++ b/include/display.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 display.h $NHDT-Date: 1661295667 2022/08/23 23:01:07 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.77 $ */ +/* NetHack 3.7 display.h $NHDT-Date: 1725653008 2024/09/06 20:03:28 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.106 $ */ /* Copyright (c) Dean Luick, with acknowledgements to Kevin Darcy */ /* and Dave Cohrs, 1990. */ /* NetHack may be freely redistributed. See license for details. */ @@ -19,13 +19,13 @@ * Returns the head of the list of objects that the player can see * at location (x,y). [Vestige of unimplemented invisible objects.] */ -#define vobj_at(x, y) (gl.level.objects[x][y]) +#define vobj_at(x, y) (svl.level.objects[x][y]) /* * sensemon() * * Returns true if the hero can sense the given monster. This includes - * monsters that are hiding or mimicing other monsters. + * monsters that are hiding or mimicking other monsters. * * [3.7] Note: the map doesn't display any monsters when hero is swallowed * (or display non-adjacent, non-submerged ones when hero is underwater), @@ -49,7 +49,7 @@ /* OR 3b. hero is using a telepathy inducing */ \ /* object and in range */ \ || (Unblind_telepat \ - && (mdistu(mon) <= (BOLT_LIM * BOLT_LIM)))))) + && (mdistu(mon) <= u.unblind_telepat_range))))) /* organized to perform cheaper tests first; is_pool() vs is_pool_or_lava(): hero who is underwater can see adjacent @@ -65,7 +65,7 @@ */ #define _mon_warning(mon) \ (Warning && !(mon)->mpeaceful && (mdistu(mon) < 100) \ - && (((int) ((mon)->m_lev / 4)) >= gc.context.warnlevel)) + && (((int) ((mon)->m_lev / 4)) >= svc.context.warnlevel)) /* * mon_visible() @@ -89,7 +89,7 @@ (/* The hero can see the monster IF the monster */ \ (!mon->minvis || See_invisible) /* 1. is not invisible */ \ && !mon->mundetected /* AND 2. not an undetected hider */ \ - && !(mon->mburied || u.uburied)) /* AND 3. neither you nor it is buried */ + && !(mon->mburied || u.uburied)) /* AND 3. neither you nor it buried */ #else /* without 'mburied' and 'uburied' */ #define _mon_visible(mon) \ (/* The hero can see the monster IF the monster */ \ @@ -106,7 +106,7 @@ * canseemon() or canspotmon() which already check that. */ #define _see_with_infrared(mon) \ - (!Blind && Infravision && mon && infravisible(mon->data) \ + (!Blind && Infravision && infravisible(mon->data) \ && couldsee(mon->mx, mon->my)) /* @@ -159,7 +159,7 @@ * definition here is convenient. No longer limited to pets. */ #define _is_safemon(mon) \ - (flags.safe_dog && (mon) && (mon)->mpeaceful && canspotmon(mon) \ + (flags.safe_dog && (mon)->mpeaceful && canspotmon(mon) \ && !Confusion && !Hallucination && !Stunned) /* @@ -183,10 +183,10 @@ * * Respectively return a random monster or object. * random_object() won't return STRANGE_OBJECT or the generic objects. - * -/+ MAXOCLASSES is used to skip it and them. + * -/+ FIRST_OBJECT is used to skip it and them. */ #define random_monster(rng) ((*rng)(NUMMONS)) -#define random_object(rng) ((*rng)(NUM_OBJECTS - MAXOCLASSES) + MAXOCLASSES) +#define random_object(rng) ((*rng)(NUM_OBJECTS - FIRST_OBJECT) + FIRST_OBJECT) /* * what_obj() @@ -230,14 +230,14 @@ #define DISP_ALL (-2) /* Like beam, but still displayed if not visible. */ #define DISP_TETHER (-3) /* Like beam, but tether glyph differs from final */ #define DISP_FLASH (-4) /* Clean up each glyph before displaying new one. */ -#define DISP_ALWAYS (-5) /* Like flash, but still displayed if not visible. */ +#define DISP_ALWAYS (-5) /* Like flash, but still displayed if not visible */ #define DISP_CHANGE (-6) /* Change glyph. */ #define DISP_END (-7) /* Clean up. */ #define DISP_FREEMEM (-8) /* Free all memory during exit only. */ /* Total number of cmap indices in the shield_static[] array. */ #define SHIELD_COUNT 21 -#define BACKTRACK (-1) /* flag for DISP_END to display each prior location */ +#define BACKTRACK (-1) /* for DISP_END to display each prior location */ /* * display_self() @@ -327,8 +327,7 @@ enum engraving_colors { engraving_color_blood = CLR_RED, }; -enum magicplatform_colors { - platform_color_default = CLR_BRIGHT_MAGENTA, +enum magicplatform_extra_colors { platform_color_red = CLR_RED, platform_color_orange = CLR_ORANGE, platform_color_yellow = CLR_YELLOW, @@ -377,16 +376,16 @@ enum altar_types { altar_other }; enum engraving_types { - engr_dust, + engr_dust = 0, engr_engrave, engr_burn, engr_mark1, engr_mark2, engr_mark3, - engr_blood + engr_blood, + max_engraving_glyphtypes }; -enum magicplatform_types { - platform_default, +enum magicplatform_extra_types { platform_red, platform_orange, platform_yellow, @@ -477,15 +476,6 @@ enum glyphmap_change_triggers { gm_nochange, gm_newgame, gm_levelchange, * Altars Altar (unaligned, chaotic, neutral, lawful, other) * Count: 5 * - * Engravings Engraving types (dust, engrave, burn, mark1, mark2, mark3, - * blood) - * HEADSTONE is always on GRAVE terrain and never renders as - * an engraving - * Count: 7 - * - * Magic platforms Default appearance plus six colors - * Count: 7 - * * cmap B S_grave through S_arrow_trap + TRAPNUM - 1 * Count: (S_arrow_trap + (TRAPNUM - 1) - S_grave) = 39 * @@ -522,6 +512,18 @@ enum glyphmap_change_triggers { gm_nochange, gm_newgame, gm_levelchange, * frosty explosions A set of nine. * Count: MAXEXPCHAR * + * cmap D xNetHack-specific defsyms + * Count: (S_magicplatform - S_grass) + 1 = 2 + * + * Engravings Engraving types (dust, engrave, burn, mark1, mark2, + * mark3, blood) + * HEADSTONE is always on GRAVE terrain and never + * renders as an engraving + * Count: 2 defsyms x 7 engraving types = 14 + * + * Colored Six colors ("default" appearance is in cmap D) + * magic platforms Count: 6 + * * warning A set of six representing the different warning * levels. * Count: 6 @@ -582,9 +584,7 @@ enum glyph_offsets { GLYPH_CMAP_SOKO_OFF = (((S_trwall - S_vwall) + 1) + GLYPH_CMAP_KNOX_OFF), GLYPH_CMAP_A_OFF = (((S_trwall - S_vwall) + 1) + GLYPH_CMAP_SOKO_OFF), GLYPH_ALTAR_OFF = (((S_brdnladder - S_ndoor) + 1) + GLYPH_CMAP_A_OFF), - GLYPH_ENGRAVING_OFF = (5 + GLYPH_ALTAR_OFF), - GLYPH_MAGICPLATFORM_OFF = (7 + GLYPH_ENGRAVING_OFF), - GLYPH_CMAP_B_OFF = (7 + GLYPH_MAGICPLATFORM_OFF), + GLYPH_CMAP_B_OFF = (5 + GLYPH_ALTAR_OFF), GLYPH_ZAP_OFF = ((S_arrow_trap + MAXTCHARS - S_grave) + GLYPH_CMAP_B_OFF), GLYPH_CMAP_C_OFF = ((NUM_ZAP << 2) + GLYPH_ZAP_OFF), GLYPH_SWALLOW_OFF = (((S_goodpos - S_digbeam) + 1) + GLYPH_CMAP_C_OFF), @@ -596,7 +596,21 @@ enum glyph_offsets { GLYPH_EXPLODE_MAGICAL_OFF = (MAXEXPCHARS + GLYPH_EXPLODE_WET_OFF), GLYPH_EXPLODE_FIERY_OFF = (MAXEXPCHARS + GLYPH_EXPLODE_MAGICAL_OFF), GLYPH_EXPLODE_FROSTY_OFF = (MAXEXPCHARS + GLYPH_EXPLODE_FIERY_OFF), - GLYPH_WARNING_OFF = (MAXEXPCHARS + GLYPH_EXPLODE_FROSTY_OFF), + GLYPH_CMAP_D_OFF = (MAXEXPCHARS + GLYPH_EXPLODE_FROSTY_OFF), + /* bit of weirdness: prior to S_engroom and S_engrcorr existing in upstream + * NetHack, xNetHack had a S_engraving and S_magicplatform following altars + * in between cmaps A and B, with their corresponding glyphs along with + * them. + * S_engraving no longer exists and S_magicplatform is moved to cmap D. + * There are still 2 cmap A glyphs (and tiles) for "engraving in room" and + * "engraving in corridor", but they won't actually be shown in real play + * because engraving_to_glyph will only return things in the + * GLYPH_ENGRAVING_OFF range. + */ + GLYPH_ENGRAVING_OFF = (((S_magicplatform - S_grass) + 1) + + GLYPH_CMAP_D_OFF), + GLYPH_MAGICPLATFORM_OFF = (14 + GLYPH_ENGRAVING_OFF), + GLYPH_WARNING_OFF = (6 + GLYPH_MAGICPLATFORM_OFF), GLYPH_STATUE_OFF = (WARNCOUNT + GLYPH_WARNING_OFF), GLYPH_STATUE_MALE_OFF = (GLYPH_STATUE_OFF), GLYPH_STATUE_FEM_OFF = (NUMMONS + GLYPH_STATUE_MALE_OFF), @@ -646,17 +660,6 @@ enum glyph_offsets { /* (((amsk) & AM_MASK) == AM_UNALIGNED) */ \ : (GLYPH_ALTAR_OFF + altar_unaligned)) -#define engr_to_glyph(type, x, y, z) \ - ((type == DUST) \ - ? GLYPH_ENGRAVING_OFF + engr_dust \ - : (type == ENGRAVE) \ - ? GLYPH_ENGRAVING_OFF + engr_engrave \ - : (type == BURN) \ - ? GLYPH_ENGRAVING_OFF + engr_burn \ - : (type == ENGR_BLOOD) \ - ? GLYPH_ENGRAVING_OFF + engr_blood \ - : GLYPH_ENGRAVING_OFF + engr_mark1 + (x + y + z) % 3) - /* magicplatform_to_glyph is in display.c */ /* not used, nor is it correct @@ -699,16 +702,17 @@ enum glyph_offsets { #define cmap_c_to_glyph(cmap_idx) \ (((cmap_idx) - S_digbeam) + GLYPH_CMAP_C_OFF) +#define cmap_d_to_glyph(cmap_idx) \ + (((cmap_idx) - S_grass) + GLYPH_CMAP_D_OFF) + #define cmap_to_glyph(cmap_idx) \ ( ((cmap_idx) == S_stone) ? GLYPH_CMAP_STONE_OFF \ : ((cmap_idx) <= S_trwall) ? cmap_walls_to_glyph(cmap_idx) \ : ((cmap_idx) < S_altar) ? cmap_a_to_glyph(cmap_idx) \ : ((cmap_idx) == S_altar) ? altar_to_glyph(AM_NEUTRAL) \ - : ((cmap_idx) == S_engraving) ? (engr_to_glyph(ENGRAVE, 0, 0, 0)) \ - : ((cmap_idx) == S_magicplatform) \ - ? (GLYPH_MAGICPLATFORM_OFF + platform_default) \ : ((cmap_idx) < S_arrow_trap + MAXTCHARS) ? cmap_b_to_glyph(cmap_idx) \ : ((cmap_idx) <= S_goodpos) ? cmap_c_to_glyph(cmap_idx) \ + : ((cmap_idx) <= S_magicplatform) ? cmap_d_to_glyph(cmap_idx) \ : NO_GLYPH ) #define trap_to_glyph(trap) \ @@ -740,7 +744,7 @@ enum glyph_offsets { (Ugender)) /* - * Change the given glyph into it's given type. Note: + & * Change the given glyph into its given type. Note: * 1) Pets, detected, and ridden monsters are animals and are converted * to the proper monster number. * 2) Bodies are all mapped into the generic CORPSE object @@ -777,11 +781,6 @@ enum glyph_offsets { && (glyph) < (((S_brdnladder - S_ndoor) + 1) + GLYPH_CMAP_A_OFF)) #define glyph_is_cmap_altar(glyph) \ ((glyph) >= GLYPH_ALTAR_OFF && (glyph) < (5 + GLYPH_ALTAR_OFF)) -#define glyph_is_cmap_engraving(glyph) \ - ((glyph) >= GLYPH_ENGRAVING_OFF && (glyph) < (7 + GLYPH_ENGRAVING_OFF)) -#define glyph_is_cmap_magicplatform(glyph) \ - ((glyph) >= GLYPH_MAGICPLATFORM_OFF \ - && (glyph) < (7 + GLYPH_MAGICPLATFORM_OFF)) #define glyph_is_cmap_b(glyph) \ ((glyph) >= GLYPH_CMAP_B_OFF \ && ((glyph) < ((S_arrow_trap + MAXTCHARS - S_grave) + GLYPH_CMAP_B_OFF))) @@ -796,6 +795,15 @@ enum glyph_offsets { #define glyph_is_explosion(glyph) \ ((glyph) >= GLYPH_EXPLODE_OFF \ && (glyph) < (MAXEXPCHARS + GLYPH_EXPLODE_FROSTY_OFF)) +#define glyph_is_cmap_d(glyph) \ + ((glyph) >= GLYPH_CMAP_D_OFF \ + && (glyph) < (S_magicplatform + 1 - S_grass) + GLYPH_CMAP_D_OFF) +#define glyph_is_cmap_engraving(glyph) \ + ((glyph) >= GLYPH_ENGRAVING_OFF \ + && (glyph) < (14 + GLYPH_ENGRAVING_OFF)) +#define glyph_is_cmap_magicplatform(glyph) \ + ((glyph) >= GLYPH_MAGICPLATFORM_OFF \ + && (glyph) < (6 + GLYPH_MAGICPLATFORM_OFF)) #if 0 /* this is more precise but expands to a lot of unnecessary code */ #define glyph_is_cmap(glyph) \ (((glyph) == GLYPH_CMAP_STONE_OFF) \ @@ -811,51 +819,27 @@ enum glyph_offsets { || glyph_is_cmap_b(glyph) \ || glyph_is_cmap_c(glyph)) #endif +/* note: cmap d isn't contiguous with cmaps a-c and the extra + * engraving/magicplatform glyphs are beyond even it */ #define glyph_is_cmap(glyph) \ - ((glyph) >= GLYPH_CMAP_STONE_OFF \ - && (glyph) < (GLYPH_CMAP_C_OFF + ((S_goodpos - S_digbeam) + 1))) - -/* final MAXPCHARS is legal array index because of trailing fencepost entry */ -#define glyph_to_cmap(glyph) \ - (((glyph) == GLYPH_CMAP_STONE_OFF) \ - ? S_stone \ - : glyph_is_cmap_main(glyph) \ - ? (((glyph) - GLYPH_CMAP_MAIN_OFF) + S_vwall) \ - : glyph_is_cmap_mines(glyph) \ - ? (((glyph) - GLYPH_CMAP_MINES_OFF) + S_vwall) \ - : glyph_is_cmap_gehennom(glyph) \ - ? (((glyph) - GLYPH_CMAP_GEH_OFF) + S_vwall) \ - : glyph_is_cmap_knox(glyph) \ - ? (((glyph) - GLYPH_CMAP_KNOX_OFF) + S_vwall) \ - : glyph_is_cmap_sokoban(glyph) \ - ? (((glyph) - GLYPH_CMAP_SOKO_OFF) + S_vwall) \ - : glyph_is_cmap_a(glyph) \ - ? (((glyph) - GLYPH_CMAP_A_OFF) + S_ndoor) \ - : glyph_is_cmap_altar(glyph) \ - ? (S_altar) \ - : glyph_is_cmap_engraving(glyph) \ - ? (S_engraving) \ - : glyph_is_cmap_magicplatform(glyph) \ - ? (S_magicplatform) \ - : glyph_is_cmap_b(glyph) \ - ? (((glyph) - GLYPH_CMAP_B_OFF) + S_grave) \ - : glyph_is_cmap_c(glyph) \ - ? (((glyph) - GLYPH_CMAP_C_OFF) + S_digbeam) \ - : glyph_is_cmap_zap(glyph) \ - ? ((((glyph) - GLYPH_ZAP_OFF) % 4) + S_vbeam) \ - : MAXPCHARS) - + (((glyph) >= GLYPH_CMAP_STONE_OFF \ + && (glyph) < (GLYPH_CMAP_C_OFF + ((S_goodpos - S_digbeam) + 1))) \ + || ((glyph) >= GLYPH_CMAP_D_OFF \ + && (glyph) < GLYPH_MAGICPLATFORM_OFF + 6)) #define glyph_to_swallow(glyph) \ (glyph_is_swallow(glyph) ? (((glyph) - GLYPH_SWALLOW_OFF) & 0x7) : 0) +#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 * considered objects. */ #define glyph_is_normal_male_monster(glyph) \ - ((glyph) >= GLYPH_MON_MALE_OFF && (glyph) < (GLYPH_MON_MALE_OFF + NUMMONS)) + ((glyph) >= GLYPH_MON_MALE_OFF \ + && (glyph) < (GLYPH_MON_MALE_OFF + NUMMONS)) #define glyph_is_normal_female_monster(glyph) \ ((glyph) >= GLYPH_MON_FEM_OFF && (glyph) < (GLYPH_MON_FEM_OFF + NUMMONS)) #define glyph_is_normal_monster(glyph) \ @@ -864,7 +848,8 @@ enum glyph_offsets { #define glyph_is_female_pet(glyph) \ ((glyph) >= GLYPH_PET_FEM_OFF && (glyph) < (GLYPH_PET_FEM_OFF + NUMMONS)) #define glyph_is_male_pet(glyph) \ - ((glyph) >= GLYPH_PET_MALE_OFF && (glyph) < (GLYPH_PET_MALE_OFF + NUMMONS)) + ((glyph) >= GLYPH_PET_MALE_OFF \ + && (glyph) < (GLYPH_PET_MALE_OFF + NUMMONS)) #define glyph_is_pet(glyph) \ (glyph_is_male_pet(glyph) || glyph_is_female_pet(glyph)) #define glyph_is_female_peaceful(glyph) \ @@ -880,8 +865,8 @@ enum glyph_offsets { ((glyph) >= GLYPH_RIDDEN_MALE_OFF \ && (glyph) < (GLYPH_RIDDEN_MALE_OFF + NUMMONS)) #define glyph_is_ridden_monster(glyph) \ - (glyph_is_ridden_male_monster(glyph) \ - || glyph_is_ridden_female_monster(glyph)) + (glyph_is_ridden_male_monster(glyph) \ + || glyph_is_ridden_female_monster(glyph)) #define glyph_is_detected_female_monster(glyph) \ ((glyph) >= GLYPH_DETECT_FEM_OFF \ && (glyph) < (GLYPH_DETECT_FEM_OFF + NUMMONS)) @@ -889,7 +874,7 @@ enum glyph_offsets { ((glyph) >= GLYPH_DETECT_MALE_OFF \ && (glyph) < (GLYPH_DETECT_MALE_OFF + NUMMONS)) #define glyph_is_detected_monster(glyph) \ - (glyph_is_detected_male_monster(glyph) \ + (glyph_is_detected_male_monster(glyph) \ || glyph_is_detected_female_monster(glyph)) #define glyph_is_monster(glyph) \ (glyph_is_normal_monster(glyph) || glyph_is_pet(glyph) \ @@ -925,8 +910,9 @@ enum glyph_offsets { the otg_otmp assignment might occur multiple times in the same expression but there will always be sequence points in between */ #define obj_is_piletop(obj) \ - ((obj)->where == OBJ_FLOOR \ - && (go.otg_otmp = gl.level.objects[(obj)->ox][(obj)->oy]->nexthere) != 0 \ + ((obj)->where == OBJ_FLOOR \ + && ((go.otg_otmp = svl.level.objects[(obj)->ox][(obj)->oy]->nexthere) \ + != 0) \ && ((obj)->otyp != BOULDER || go.otg_otmp->otyp == BOULDER)) /* used to hide info such as potion and gem color when not seen yet; stones and rock are excluded for gem class; LAST_SPELL includes blank @@ -963,22 +949,22 @@ enum glyph_offsets { #define glyph_is_statue(glyph) \ (glyph_is_male_statue(glyph) || glyph_is_fem_statue(glyph)) /* generic objects are after strange object (GLYPH_OBJ_OFF) and before - other objects (GLYPH_OBJ_OFF + MAXOCLASSES) */ + other objects (GLYPH_OBJ_OFF + FIRST_OBJECT) */ #define glyph_is_normal_generic_obj(glyph) \ - ((glyph) > GLYPH_OBJ_OFF && (glyph) < GLYPH_OBJ_OFF + MAXOCLASSES) + ((glyph) > GLYPH_OBJ_OFF && (glyph) < GLYPH_OBJ_OFF + FIRST_OBJECT - 1) #define glyph_is_piletop_generic_obj(glyph) \ ((glyph) > GLYPH_OBJ_PILETOP_OFF \ - && (glyph) < GLYPH_OBJ_PILETOP_OFF + MAXOCLASSES) + && (glyph) < GLYPH_OBJ_PILETOP_OFF + FIRST_OBJECT - 1) #define glyph_is_generic_object(glyph) \ (glyph_is_normal_generic_obj(glyph) \ || glyph_is_piletop_generic_obj(glyph)) #define glyph_is_normal_piletop_obj(glyph) \ ((glyph) == GLYPH_OBJ_PILETOP_OFF \ - || ((glyph) > GLYPH_OBJ_PILETOP_OFF + MAXOCLASSES \ + || ((glyph) > GLYPH_OBJ_PILETOP_OFF + FIRST_OBJECT - 1 \ && (glyph) < (GLYPH_OBJ_PILETOP_OFF + NUM_OBJECTS))) #define glyph_is_normal_object(glyph) \ ((glyph) == GLYPH_OBJ_OFF \ - || ((glyph) >= GLYPH_OBJ_OFF + MAXOCLASSES \ + || ((glyph) >= GLYPH_OBJ_OFF + FIRST_OBJECT - 1 \ && (glyph) < (GLYPH_OBJ_OFF + NUM_OBJECTS)) \ || glyph_is_normal_piletop_obj(glyph)) @@ -1109,9 +1095,10 @@ enum glyph_offsets { #if 0 #define glyph_is_piletop(glyph) \ (glyph_is_body_piletop(glyph) \ - || glyph_is_statue_piletop(glyph) \ + || glyph_is_fem_statue_piletop(glyph) \ + || glyph_is_male_statue_piletop(glyph) \ || glyph_is_piletop_generic_obj(glyph) \ - || glyph_is_obj_piletop(glyph)) + || glyph_is_normal_piletop_obj(glyph)) #endif /* mgflags for altering map_glyphinfo() internal behavior */ @@ -1132,6 +1119,7 @@ enum glyph_offsets { #define MG_BW_ICE 0x00200 /* similar for ice vs floor */ #define MG_BW_SINK 0x00200 /* identical for sink vs fountain [note: someday * this may become a distinct flag */ +#define MG_BW_ENGR 0x00200 /* likewise for corridor engravings */ #define MG_NOTHING 0x00400 /* char represents GLYPH_NOTHING */ #define MG_UNEXPL 0x00800 /* char represents GLYPH_UNEXPLORED */ #define MG_MALE 0x01000 /* represents a male mon or statue of one */ @@ -1141,19 +1129,26 @@ enum glyph_offsets { #define MG_STAIRS 0x08000 /* hidden stairs */ #define MG_PEACEFUL 0x10000 /* peaceful monster */ +/* docrt(): re-draw whole screen; docrt_flags(): docrt() with more control */ +enum docrt_flags_bits { + docrtRecalc = 0, /* full docrt(), recalculate what the map should show */ + docrtRefresh = 1, /* redraw_map(), draw what we think the map shows */ + docrtMapOnly = 2, /* ORed with Recalc or Refresh; draw the map but not + * status or perminv */ + docrtNocls = 4, +}; + typedef struct { xint8 gnew; /* perhaps move this bit into the rm structure. */ glyph_info glyphinfo; } gbuf_entry; -#ifdef TEXTCOLOR extern const int altarcolors[]; extern const int engravingcolors[]; extern const int magicplatformcolors[]; extern const int zapcolors[]; extern const int explodecolors[]; extern int wallcolors[]; -#endif /* If TILES_IN_GLYPHMAP is defined during build, this is defined * in the generated tile.c, complete with appropriate tile references in diff --git a/include/dungeon.h b/include/dungeon.h index 15596f7ba6..e22ee0dda7 100644 --- a/include/dungeon.h +++ b/include/dungeon.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 dungeon.h $NHDT-Date: 1596498535 2020/08/03 23:48:55 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.39 $ */ +/* NetHack 3.7 dungeon.h $NHDT-Date: 1685863327 2023/06/04 07:22:07 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.47 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -30,15 +30,6 @@ typedef struct s_level { /* special dungeon level element */ d_flags flags; /* type flags */ } s_level; -typedef struct stairway { /* basic stairway identifier */ - coordxy sx, sy; /* x / y location of the stair */ - d_level tolev; /* where does it go */ - boolean up; /* up or down? */ - boolean isladder; /* ladder or stairway? */ - boolean u_traversed; /* hero has traversed this stair */ - struct stairway *next; -} stairway; - /* level region types */ enum level_region_types { LR_DOWNSTAIR = 0, @@ -47,7 +38,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 */ @@ -57,6 +49,14 @@ typedef struct dest_area { /* non-stairway level change identifier */ coordxy nhx, nhy; /* opposite corner of invalid area */ } dest_area; +/* teleportation exclusion zones in the level */ +typedef struct exclusion_zone { + xint16 zonetype; /* level_region_types */ + coordxy lx, ly; + coordxy hx, hy; + struct exclusion_zone *next; +} exclusion_zone; + typedef struct dungeon { /* basic dungeon identifier */ char dname[24]; /* name of the dungeon (eg. "Hell") */ char proto[15]; /* name of prototype file (eg. "tower") */ @@ -155,6 +155,7 @@ typedef struct branch { #define Inhell In_hell(&u.uz) /* now gehennom */ #define In_main_gehennom(x) ((x)->dnum == gehennom_dnum) #define In_endgame(x) ((x)->dnum == astral_level.dnum) +#define In_tutorial(x) ((x)->dnum == tutorial_dnum) #define In_cocytus(x) ((x)->dnum == asmodeus_level.dnum) #define In_abyss(x) ((x)->dnum == abyss_dnum) /* fiery hell: Inhell && !In_cocytus; icy hell: Inhell && In_cocytus @@ -236,7 +237,8 @@ typedef struct mapseen { Bitfield(shoptype, 5); } feat; struct mapseen_flags { - Bitfield(unreachable, 1); /* can't get back to this level */ + 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); @@ -266,7 +268,7 @@ typedef struct mapseen { struct mapseen_rooms { Bitfield(seen, 1); Bitfield(untended, 1); /* flag for shop without shk */ - } msrooms[(MAXNROFROOMS + 1) * 2]; /* same size as gr.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 c83c5b87f1..f32181bd1c 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) \ diff --git a/include/extern.h b/include/extern.h index c263104ed4..04138dc627 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,16 +1,88 @@ -/* NetHack 3.7 extern.h $NHDT-Date: 1684138080 2023/05/15 08:08:00 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1263 $ */ +/* NetHack 3.7 extern.h $NHDT-Date: 1723580890 2024/08/13 20:28:10 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1435 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ #ifndef EXTERN_H #define EXTERN_H +/* + * The placements of the NONNULLARG* and NONNULLPTRS macros were done + * using the following rules: + * These were the rules that were followed when determining which function + * parameters should be nonnull, and which are nullable: + * + * 1. If the first use of, or reference to, the pointer parameter in the + * function is a dereference, then the parameter will be considered + * nonnull. + * + * 2. If there is code in the function that tests for the pointer parameter + * being null, and adjusts the code-path accordingly so that no segfault + * will occur, then the parameter will not be considered nonnull (it can + * be null). + * + * Note that if an arg is declared nonnull, any tests inside the function + * for the variable being null, will likely trigger a compiler warning + * diagnostic about the unnecessary test. + * + * Description of the NONNULL macros: + * + * NONNULL The function return value is never NULL. + * NONNULLPTRS Every pointer argument is declared nonnull. + * NONNULLARG1 The 1st argument is declared nonnull. + * NONNULLARG2 The 2nd argument is declared nonnull. + * NONNULLARG3 The 3rd argument is declared nonnull. + * NONNULLARG4 The 4th argument is declared nonnull (not used). + * NONNULLARG5 The 5th argument is declared nonnull. + * NONNULLARG6 The 6th argument is declared nonnull. + * NONNULLARG7 The 7th argument is declared nonnull (bhit). + * NONNULLARG12 The 1st and 2nd arguments are declared nonnull. + * NONNULLARG23 The 2nd and 3rd arguments are declared nonnull. + * NONNULLARG13 The 1st and 3rd arguments are declared nonnull. + * NONNULLARG123 The 1st, 2nd and 3rd arguments are declared nonnull. + * NONNULLARG14 The 1st and 4th arguments are declared nonnull. + * NONNULLARG134 The 1st, 3rd and 4th arguments are declared nonnull. + * NONNULLARG17 The 1st and 7th arguments are declared nonnull (this + * was a special-case added for askchain(), where the + * arguments are spread out that way. This macro + * could be removed if the askchain arguments in the + * prototype and callers were changed to make the + * nonnull arguments side-by-side). + * NONNULLARG145 The 1st, 4th and 5th arguments are declared nonnull + * (this was a special-case added for find_roll_to_hit(), + * in uhitm.c, where the arguments are spread out that way. + * We can't just use NONNULLPTRS there because the 3rd + * argument 'weapon' can be NULL). + * NONNULLARG24 The 2nd and 4th arguments are declared nonnull (this + * was a special-case added for query_objlist() in invent.c). + * NONNULLARG45 The 4th and 5th arguments are declared nonnull (this + * was a special-case added for do_screen_description(), + * in pager.c, where the arguments are spread out that way. + * We can't just use NONNULLPTRS there because the 6th + * argument can be NULL). + * NO_NNARGS This macro expands to nothing. It is just used to + * mark that analysis has been done on the function, + * and concluded that none of the arguments could be + * marked nonnull.That distinguishes a function that has + * not been analyzed (yet), from one that has. + * + */ + /* ### alloc.c ### */ #if 0 -extern long *alloc(unsigned int); +/* routines in alloc.c depend on MONITOR_HEAP and are declared in global.h */ +extern long *alloc(unsigned int) NONNULL; #endif extern char *fmt_ptr(const void *) NONNULL; +/* moved from hacklib.c to alloc.c so that utility programs have access */ +#define FITSint(x) FITSint_(x, __func__, __LINE__) +extern int FITSint_(long long, const char *, int) NONNULLARG2; +#define FITSuint(x) FITSuint_(x, __func__, __LINE__) +extern unsigned FITSuint_(unsigned long long, const char *, int) NONNULLARG2; +/* for Strlen() which returns unsigned instead of size_t and panics for + strings of length INT_MAX (32K - 1) or longer */ + +#include "hacklib.h" /* This next pre-processor directive covers almost the entire file, * interrupted only occasionally to pick up specific functions as needed. */ @@ -18,7 +90,7 @@ extern char *fmt_ptr(const void *) NONNULL; /* ### allmain.c ### */ -extern void early_init(void); +extern void early_init(int, char *[]); extern void moveloop_core(void); extern void moveloop(boolean); extern void stop_occupation(void); @@ -31,30 +103,32 @@ extern long timet_delta(time_t, time_t); /* ### apply.c ### */ +extern void do_blinding_ray(struct obj *) NONNULLPTRS; extern int doapply(void); extern int dorub(void); extern int dojump(void); extern int jump(int); extern int number_leashed(void); -extern void o_unleash(struct obj *); -extern void m_unleash(struct monst *, boolean); +extern void o_unleash(struct obj *) NONNULLPTRS; +extern void m_unleash(struct monst *, boolean) NONNULLPTRS; extern void unleash_all(void); -extern boolean leashable(struct monst *); +extern boolean leashable(struct monst *) NONNULLARG1; extern boolean next_to_u(void); -extern struct obj *get_mleash(struct monst *); +extern struct obj *get_mleash(struct monst *) NONNULLARG1; extern const char *beautiful(void); extern void check_leash(coordxy, coordxy); extern boolean um_dist(coordxy, coordxy, xint16); -extern boolean snuff_candle(struct obj *); -extern boolean snuff_lit(struct obj *); -extern boolean splash_lit(struct obj *); -extern boolean catch_lit(struct obj *); +extern boolean snuff_candle(struct obj *) NONNULLPTRS; +extern boolean snuff_lit(struct obj *) NONNULLPTRS; +extern boolean splash_lit(struct obj *) NONNULLPTRS; +extern boolean catch_lit(struct obj *) NONNULLPTRS; extern void use_unicorn_horn(struct obj **, boolean); -extern boolean tinnable(struct obj *); +extern boolean tinnable(struct obj *) NONNULLPTRS; extern void reset_trapset(void); -extern int use_whip(struct obj *); -extern int use_pole(struct obj *, boolean); -extern void fig_transform(union any *, long); +extern int use_whip(struct obj *) NONNULLPTRS; +extern boolean could_pole_mon(void); +extern int use_pole(struct obj *, boolean) NONNULLPTRS; +extern void fig_transform(union any *, long) NONNULLARG1; extern int unfixable_trouble_count(boolean); /* ### artifact.c ### */ @@ -63,52 +137,54 @@ 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 const char *artifact_name(const char *, short *, boolean); -extern boolean exist_artifact(int, const char *); -extern void artifact_exists(struct obj *, const char *, boolean, unsigned); +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) ; extern void found_artifact(int); -extern void find_artifact(struct obj *); +extern void find_artifact(struct obj *) NONNULLPTRS; extern int nartifact_exist(void); -extern void artifact_origin(struct obj *, unsigned); +extern void artifact_origin(struct obj *, unsigned) NONNULLPTRS; extern boolean arti_immune(struct obj *, int); extern boolean spec_ability(struct obj *, unsigned long); -extern boolean confers_luck(struct obj *); +extern boolean confers_luck(struct obj *) NONNULLPTRS; extern boolean arti_reflects(struct obj *); -extern boolean shade_glare(struct obj *); -extern boolean restrict_name(struct obj *, const char *); +extern boolean shade_glare(struct obj *) NONNULLPTRS; +extern boolean restrict_name(struct obj *, const char *) NONNULLPTRS; +extern boolean attacks(int, struct obj *); extern boolean defends(int, struct obj *); extern boolean defends_when_carried(int, struct obj *); extern boolean protects(struct obj *, boolean); extern void set_artifact_intrinsic(struct obj *, boolean, long); -extern int touch_artifact(struct obj *, struct monst *); -extern int spec_abon(struct obj *, struct monst *); -extern int spec_dbon(struct obj *, struct monst *, int); +extern int touch_artifact(struct obj *, struct monst *) NONNULLARG2; +extern int spec_abon(struct obj *, struct monst *) NONNULLARG2; +extern int spec_dbon(struct obj *, struct monst *, int) NONNULLARG2; extern void discover_artifact(xint16); extern boolean undiscovered_artifact(xint16); extern int disp_artifact_discoveries(winid); extern void dump_artifact_info(winid); extern int artifact_hit(struct monst *, struct monst *, struct obj *, - int *, int); + int *, int) NONNULLARG2; extern int doinvoke(void); extern boolean finesse_ahriman(struct obj *); extern int arti_speak(struct obj *); extern boolean artifact_light(struct obj *); extern long spec_m2(struct obj *); extern boolean artifact_has_invprop(struct obj *, uchar); -extern long arti_cost(struct obj *); -extern struct obj *what_gives(long *); +extern long arti_cost(struct obj *) NONNULLARG1; +extern struct obj *what_gives(long *) NONNULLARG1; extern const char *glow_color(int); extern const char *glow_verb(int, boolean); extern void Sting_effects(int); -extern int retouch_object(struct obj **, boolean, boolean); +extern int retouch_object(struct obj **, boolean, boolean) NONNULLARG1; extern void retouch_equipment(int); 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 struct obj *uhave_magic_apple(void); -extern boolean permapoisoned(struct obj *); -extern boolean arti_starts_with_the(struct obj *); +extern boolean permapoisoned(struct obj *) NONNULLARG1; +extern boolean arti_starts_with_the(struct obj *) NONNULLARG1; extern short arti_material(int); /* ### attrib.c ### */ @@ -118,7 +194,7 @@ extern void gainstr(struct obj *, int, boolean); extern void losestr(int, const char *, schar); extern void poison_strdmg(int, int, const char *, schar); extern void poisontell(int, boolean); -extern void poisoned(const char *, int, const char *, int, boolean); +extern void poisoned(const char *, int, const char *, int, boolean) NONNULLARG1; extern void change_luck(schar); extern int stone_luck(boolean); extern void set_moreluck(void); @@ -127,10 +203,12 @@ extern void exercise(int, boolean); extern void exerchk(void); extern void init_attr(int); extern void redist_attr(void); +extern void vary_init_attr(void); extern void adjabil(int, int); extern int newhp(void); extern int minuhpmax(int); -extern void setuhpmax(int); +extern void setuhpmax(int, boolean); +extern int adjuhploss(int, int); extern schar acurr(int); extern schar acurrstr(void); extern boolean extremeattr(int); @@ -159,20 +237,21 @@ extern void lift_covet_and_placebc(int); extern void set_bc(int); extern void move_bc(int, int, coordxy, coordxy, coordxy, coordxy); extern boolean drag_ball(coordxy, coordxy, int *, coordxy *, coordxy *, - coordxy *, coordxy *, boolean *, boolean); + coordxy *, coordxy *, boolean *, boolean) NONNULLPTRS; extern void drop_ball(coordxy, coordxy); extern void drag_down(void); extern void bc_sanity_check(void); /* ### bones.c ### */ -extern void sanitize_name(char *); +extern void sanitize_name(char *) NONNULLARG1; extern void drop_upon_death(struct monst *, struct obj *, coordxy, coordxy); extern boolean can_make_bones(void); extern void savebones(int, time_t, struct obj *); extern int getbones(void); -extern void newebones(struct monst *); -extern boolean bones_include_name(const char *); +extern void newebones(struct monst *) NONNULLARG1; +extern boolean bones_include_name(const char *) NONNULLARG1; +extern void fix_ghostly_obj(struct obj *) NONNULLARG1; /* ### botl.c ### */ @@ -196,9 +275,10 @@ extern boolean exp_percent_changing(void); extern int stat_cap_indx(void); extern int stat_hunger_indx(void); extern const char *bl_idx_to_fldname(int); +extern void repad_with_dashes(char *); extern void condopt(int, boolean *, boolean); extern int parse_cond_option(boolean, char *); -extern void cond_menu(void); +extern boolean cond_menu(void); extern boolean opt_next_cond(int, char *); #ifdef STATUS_HILITES extern void status_eval_next_unhilite(void); @@ -211,6 +291,54 @@ extern void all_options_statushilites(strbuf_t *); extern boolean status_hilite_menu(void); #endif /* STATUS_HILITES */ +/* ### calendar.c ### */ + +extern time_t getnow(void); +extern int getyear(void); +#if 0 +extern char *yymmdd(time_t) NONNULL; +#endif +extern long yyyymmdd(time_t); +extern long hhmmss(time_t); +extern char *yyyymmddhhmmss(time_t) NONNULL; +extern time_t time_from_yyyymmddhhmmss(char *); +extern int phase_of_the_moon(void); +extern boolean friday_13th(void); +extern int night(void); +extern int midnight(void); +extern int current_holidays(void); + +/* ### coloratt.c ### */ + +extern char *color_attr_to_str(color_attr *); +extern boolean color_attr_parse_str(color_attr *, char *); +extern int32 colortable_to_int32(struct nethack_color *); +extern int query_color(const char *, int) NO_NNARGS; +extern int query_attr(const char *, int) NO_NNARGS; +extern boolean query_color_attr(color_attr *, const char *) NONNULLARG1; +extern const char *attr2attrname(int); +extern void basic_menu_colors(boolean); +extern boolean add_menu_coloring_parsed(const char *, int, int); +extern const char *clr2colorname(int); +extern int match_str2clr(char *, boolean) NONNULLARG1; +extern int match_str2attr(const char *, boolean) NONNULLARG1; +extern boolean add_menu_coloring(char *) NONNULLARG1; +extern void free_one_menu_coloring(int); +extern void free_menu_coloring(void); +extern int count_menucolors(void); +extern int32 check_enhanced_colors(char *) NONNULLARG1; +extern const char *wc_color_name(int32) NONNULL; +extern int32_t rgbstr_to_int32(const char *rgbstr); +extern boolean closest_color(uint32_t lcolor, uint32_t *closecolor, uint16 *clridx); +extern int color_distance(uint32_t, uint32_t); +extern boolean onlyhexdigits(const char *buf); +extern uint32 get_nhcolor_from_256_index(int idx); +#ifdef CHANGE_COLOR +extern int count_alt_palette(void); +extern int alternative_palette(char *); +extern void change_palette(void); +#endif + /* ### cmd.c ### */ extern void set_move_cmd(int, int); @@ -257,6 +385,7 @@ extern char *cmd_from_ecname(const char *); extern const char *cmdname_from_func(int(*)(void), char *, boolean); extern boolean redraw_cmd(char); extern const char *levltyp_to_name(int); +extern int dolookaround(void); extern void reset_occupations(void); extern void set_occupation(int(*)(void), const char *, cmdcount_nht); extern void cmdq_add_ec(int, int(*)(void)); @@ -275,23 +404,26 @@ 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 *); extern void parseautocomplete(char *, boolean); +extern void all_options_autocomplete(strbuf_t *); extern void lock_mouse_buttons(boolean); extern void reset_commands(boolean); extern void update_rest_on_space(void); -extern void rhack(char *); +extern void rhack(int); extern int doextlist(void); extern int extcmd_via_menu(void); extern int enter_explore_mode(void); extern boolean bind_mousebtn(int, const char *); extern boolean bind_key(uchar, const char *); extern 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); @@ -309,15 +441,16 @@ extern void end_of_input(void); #endif extern char readchar(void); extern char readchar_poskey(coordxy *, coordxy *, int *); -extern void sanity_check(void); extern char* key2txt(uchar, char *); extern char yn_function(const char *, const char *, char, boolean); +extern char paranoid_ynq(boolean, const char *, boolean); extern boolean paranoid_query(boolean, const char *); extern void makemap_prepost(boolean); +extern const char *ecname_from_fn(int (*)(void)); /* ### date.c ### */ -extern void populate_nomakedefs(struct version_info *); +extern void populate_nomakedefs(struct version_info *) NONNULLARG1; extern void free_nomakedefs(void); /* ### dbridge.c ### */ @@ -332,7 +465,7 @@ extern boolean is_open_air(coordxy, coordxy); extern schar db_under_typ(int); extern int is_drawbridge_wall(coordxy, coordxy); extern boolean is_db_wall(coordxy, coordxy); -extern boolean find_drawbridge(coordxy *, coordxy *); +extern boolean find_drawbridge(coordxy *, coordxy *) NONNULLPTRS; extern boolean create_drawbridge(coordxy, coordxy, int, boolean); extern boolean open_drawbridge(coordxy, coordxy); extern void close_drawbridge(coordxy, coordxy); @@ -347,30 +480,31 @@ extern void sa_victual(volatile struct victual_info *); extern boolean trapped_chest_at(int, coordxy, coordxy); extern boolean trapped_door_at(int, coordxy, coordxy); -extern struct obj *o_in(struct obj *, char); -extern struct obj *o_material(struct obj *, unsigned); -extern int gold_detect(struct obj *, boolean); +extern struct obj *o_in(struct obj *, char) NONNULLARG1; +extern struct obj *o_material(struct obj *, unsigned) NONNULLARG1; +extern int gold_detect(struct obj *, boolean) NONNULLARG1; extern int food_detect(struct obj *); extern int object_detect(struct obj *, int); extern int monster_detect(struct obj *, int); extern int trap_detect(struct obj *); extern int look_in_crystal_ball(void); -extern void use_crystal_ball(struct obj **); +extern void use_crystal_ball(struct obj **) NONNULLARG1; +extern void show_map_spot(coordxy, coordxy, boolean); extern void do_mapping(void); extern void do_vicinity_map(struct obj *); -extern void cvt_sdoor_to_door(struct rm *); +extern void cvt_sdoor_to_door(struct rm *) NONNULLARG1; extern int findit(void); extern int openit(void); extern boolean detecting(void(*)(coordxy, coordxy, void *)); -extern void find_trap(struct trap *); +extern void find_trap(struct trap *) NONNULLARG1; extern void warnreveal(void); extern int dosearch0(int); extern int dosearch(void); -extern void sokoban_detect(void); +extern void premap_detect(void); #if defined(DUMPLOG) || defined(DUMPHTML) extern void dump_map(void); #endif -extern void reveal_terrain(int, int); +extern void reveal_terrain(unsigned); extern int wiz_mgender(void); extern void passive_gold_detect(void); @@ -379,32 +513,34 @@ extern void passive_gold_detect(void); extern int dig_typ(struct obj *, coordxy, coordxy); extern boolean is_digging(void); extern int holetime(void); -extern boolean dig_check(struct monst *, boolean, coordxy, coordxy); +extern enum digcheck_result dig_check(struct monst *, coordxy, coordxy); +extern void digcheck_fail_message(enum digcheck_result, struct monst *, + coordxy, coordxy); extern void digactualhole(coordxy, coordxy, struct monst *, int); extern boolean dighole(boolean, boolean, coord *); -extern int use_pick_axe(struct obj *); -extern int use_pick_axe2(struct obj *); -extern boolean mdig_tunnel(struct monst *); +extern int use_pick_axe(struct obj *) NONNULLARG1; +extern int use_pick_axe2(struct obj *) NONNULLARG1; +extern boolean mdig_tunnel(struct monst *) NONNULLARG1; extern void draft_message(boolean); extern void watch_dig(struct monst *, coordxy, coordxy, boolean); extern void zap_dig(void); -extern struct obj *bury_an_obj(struct obj *, boolean *); +extern struct obj *bury_an_obj(struct obj *, boolean *) NONNULLARG1; extern void bury_objs(int, int); extern void unearth_objs(int, int); -extern void rot_organic(union any *, long); -extern void rot_corpse(union any *, long); -extern struct obj *buried_ball(coord *); +extern void rot_organic(union any *, long) NONNULLARG1; +extern void rot_corpse(union any *, long) NONNULLARG1; +extern struct obj *buried_ball(coord *) NONNULLARG1; extern void buried_ball_to_punishment(void); extern void buried_ball_to_freedom(void); extern schar fillholetyp(coordxy, coordxy, boolean); extern void liquid_flow(coordxy, coordxy, schar, struct trap *, const char *); extern boolean conjoined_pits(struct trap *, struct trap *, boolean); #if 0 -extern void bury_monst(struct monst *); +extern void bury_monst(struct monst *) NONNULLARG1; extern void bury_you(void); extern void unearth_you(void); extern void escape_tomb(void); -extern void bury_obj(struct obj *); +extern void bury_obj(struct obj *) NONNULLARG1; #endif #ifdef DEBUG extern int wiz_debug_cmd_bury(void); @@ -413,19 +549,20 @@ extern boolean create_pit_under(struct monst *, struct monst *); /* ### display.c ### */ -extern int tp_sensemon(struct monst *); -extern int sensemon(struct monst *); -extern int mon_warning(struct monst *); -extern int mon_visible(struct monst *); -extern int see_with_infrared(struct monst *); -extern int canseemon(struct monst *); -extern int knowninvisible(struct monst *); -extern int is_safemon(struct monst *); +extern int tp_sensemon(struct monst *) NONNULLARG1; +extern int sensemon(struct monst *) NONNULLARG1; +extern int mon_warning(struct monst *) NONNULLARG1; +extern int mon_visible(struct monst *) NONNULLARG1; +extern int see_with_infrared(struct monst *) NONNULLARG1; +extern int canseemon(struct monst *) NONNULLARG1; +extern int knowninvisible(struct monst *) NONNULLARG1; +extern int is_safemon(struct monst *) NONNULLARG1; extern void magic_map_background(coordxy, coordxy, int); extern void map_background(coordxy, coordxy, int); -extern void map_trap(struct trap *, int); -extern void map_object(struct obj *, int); +extern void map_trap(struct trap *, int) NONNULLARG1; +extern void map_object(struct obj *, int) NONNULLARG1; extern void map_invisible(coordxy, coordxy); +extern void map_engraving(struct engr *, int); extern boolean unmap_invisible(coordxy, coordxy); extern void unmap_object(coordxy, coordxy); extern void map_location(coordxy, coordxy, int); @@ -448,7 +585,8 @@ extern void see_traps(void); extern void curs_on_u(void); extern int doredraw(void); extern void docrt(void); -extern void redraw_map(void); +extern void docrt_flags(int); +extern void redraw_map(boolean); extern void show_glyph(coordxy, coordxy, int); extern void clear_glyph_buffer(void); extern void row_refresh(coordxy, coordxy, coordxy); @@ -462,8 +600,9 @@ extern void reglyph_darkroom(void); extern void xy_set_wall_state(coordxy, coordxy); extern void set_wall_state(void); extern void unset_seenv(struct rm *, coordxy, coordxy, coordxy, coordxy); -extern int warning_of(struct monst *); -extern void map_glyphinfo(coordxy, coordxy, int, unsigned, glyph_info *); +extern int engraving_to_glyph(struct engr *) NONNULLARG1; +extern int warning_of(struct monst *) NONNULLARG1; +extern void map_glyphinfo(coordxy, coordxy, int, unsigned, glyph_info *) NONNULLPTRS; extern void reset_glyphmap(enum glyphmap_change_triggers trigger); extern int fn_cmap_to_glyph(int); @@ -471,14 +610,16 @@ extern int fn_cmap_to_glyph(int); extern int dodrop(void); extern boolean boulder_hits_pool(struct obj *, coordxy, coordxy, boolean); -extern boolean flooreffects(struct obj *, coordxy, coordxy, const char *); -extern void doaltarobj(struct obj *); -extern void trycall(struct obj *); -extern boolean canletgo(struct obj *, const char *); -extern void dropx(struct obj *); -extern void dropy(struct obj *); -extern void dropz(struct obj *, boolean); -extern boolean obj_drops_at(struct obj *, int, int); +extern boolean flooreffects(struct obj *, coordxy, coordxy, + const char *) NONNULLPTRS; +extern void doaltarobj(struct obj *) NONNULLARG1; +extern void polymorph_sink(void); +extern void trycall(struct obj *) NONNULLARG1; +extern boolean canletgo(struct obj *, const char *) NONNULLPTRS; +extern void dropx(struct obj *) NONNULLARG1; +extern void dropy(struct obj *) NONNULLARG1; +extern void dropz(struct obj *, boolean) NONNULLARG1; +extern boolean obj_drops_at(struct obj *, int, int) NONNULLARG1; extern void obj_no_longer_held(struct obj *); extern int doddrop(void); extern int dodown(void); @@ -487,109 +628,108 @@ extern int doup(void); extern void save_currentstate(void); #endif extern void u_collide_m(struct monst *); -extern void goto_level(d_level *, boolean, boolean, boolean); +extern void goto_level(d_level *, boolean, boolean, boolean) NONNULLARG1; extern void hellish_smoke_mesg(void); extern void maybe_lvltport_feedback(void); -extern void schedule_goto(d_level *, int, const char *, const char *); +extern void schedule_goto(d_level *, int, const char *, const char *) NONNULLARG1; extern void deferred_goto(void); -extern boolean revive_corpse(struct obj *, boolean); -extern void revive_mon(union any *, long); -extern void moldy_corpse(union any *, long); -extern void zombify_mon(union any *, long); +extern boolean revive_corpse(struct obj *, boolean) NONNULLARG1; +extern void revive_mon(union any *, long) NONNULLARG1; +extern void moldy_corpse(union any *, long) NONNULLARG1; +extern void zombify_mon(union any *, long) NONNULLARG1; extern boolean cmd_safety_prevention(const char *, const char *, - const char *, int *); + const char *, int *) NONNULLPTRS; extern int donull(void); extern int dowipe(void); -extern void legs_in_no_shape(const char *, boolean); +extern void legs_in_no_shape(const char *, boolean) NONNULLARG1; extern void set_wounded_legs(long, int); extern void heal_legs(int); extern void polymorph_sink(void); extern void trycall(struct obj *); -extern boolean obj_aireffects(struct obj *, boolean); +extern boolean obj_aireffects(struct obj *, boolean) NONNULLARG1; extern void restore_valk_locate(xint8); /* ### do_name.c ### */ -extern char *dxdy_to_dist_descr(coordxy, coordxy, boolean); -extern char *coord_desc(coordxy, coordxy, char *, char); -extern void auto_describe(coordxy, coordxy); -extern boolean getpos_menu(coord *, int); -extern int getpos(coord *, boolean, const char *); -extern void getpos_sethilite(void(*f)(int), boolean(*d)(coordxy,coordxy)); -extern void new_mgivenname(struct monst *, int); -extern void free_mgivenname(struct monst *); -extern void new_oname(struct obj *, int); -extern void free_oname(struct obj *); -extern const char *safe_oname(struct obj *); -extern struct monst *christen_monst(struct monst *, const char *); -extern struct obj *weapon_oname(struct obj *); -extern struct obj *oname(struct obj *, const char *, unsigned); +extern void new_mgivenname(struct monst *, int) NONNULLARG1; +extern void free_mgivenname(struct monst *) NONNULLARG1; +extern void new_oname(struct obj *, int) NONNULLARG1; +extern void free_oname(struct obj *) NONNULLARG1; +extern const char *safe_oname(struct obj *) NONNULLARG1; +extern struct monst *christen_monst(struct monst *, const char *) NONNULLARG1; +extern struct obj *weapon_oname(struct obj *) NONNULLARG1; +extern struct obj *oname(struct obj *, const char *, unsigned) NONNULLPTRS; extern boolean objtyp_is_callable(int); extern int name_ok(struct obj *); extern int call_ok(struct obj *); extern int docallcmd(void); -extern void docall(struct obj *); +extern void docall(struct obj *) NONNULLARG1; extern const char *rndghostname(void); -extern char *x_monnam(struct monst *, int, const char *, int, boolean); -extern char *l_monnam(struct monst *); -extern char *mon_nam(struct monst *); -extern char *noit_mon_nam(struct monst *); -extern char *some_mon_nam(struct monst *); -extern char *Monnam(struct monst *); -extern char *noit_Monnam(struct monst *); -extern char *Some_Monnam(struct monst *); -extern char *noname_monnam(struct monst *, int); -extern char *m_monnam(struct monst *); -extern char *y_monnam(struct monst *); -extern char *adj_monnam(struct monst *, const char *); -extern char *Adjmonnam(struct monst *, const char *); -extern char *Amonnam(struct monst *); -extern char *a_monnam(struct monst *); -extern char *distant_monnam(struct monst *, int, char *); -extern char *mon_nam_too(struct monst *, struct monst *); -extern char *monverbself(struct monst *, char *, const char *, const char *); +extern char *x_monnam(struct monst *, int, const char *, int, boolean) NONNULLARG1; +extern char *l_monnam(struct monst *) NONNULLARG1; +extern char *mon_nam(struct monst *) NONNULLARG1; +extern char *noit_mon_nam(struct monst *) NONNULLARG1; +extern char *some_mon_nam(struct monst *) NONNULLARG1; +extern char *Monnam(struct monst *) NONNULLARG1; +extern char *noit_Monnam(struct monst *) NONNULLARG1; +extern char *Some_Monnam(struct monst *) NONNULLARG1; +extern char *noname_monnam(struct monst *, int) NONNULLARG1; +extern char *m_monnam(struct monst *) NONNULLARG1; +extern char *y_monnam(struct monst *) NONNULLARG1; +extern char *YMonnam(struct monst *) NONNULLARG1; +extern char *adj_monnam(struct monst *, const char *) NONNULLARG1; +extern char *Adjmonnam(struct monst *, const char *) NONNULLARG1; +extern char *Amonnam(struct monst *) NONNULLARG1; +extern char *a_monnam(struct monst *) NONNULLARG1; +extern char *distant_monnam(struct monst *, int, char *) NONNULLARG1; +extern char *mon_nam_too(struct monst *, struct monst *) NONNULLPTRS; +extern char *monverbself(struct monst *, char *, + const char *, const char *) NONNULLARG123; extern char *minimal_monnam(struct monst *, boolean); -extern char *bogusmon(char *, char *, int); +extern char *bogusmon(char *, char *, int) NONNULLARG1; extern char *rndmonnam(char *); extern const char *hcolor(const char *); extern const char *rndcolor(void); extern const char *hliquid(const char *); extern const char *roguename(void); +/* extern struct obj *realloc_obj(struct obj *, int, genericptr_t, int, const char *); +*/ extern char *coyotename(struct monst *, char *); extern char *quantmechname(struct monst *, char *); extern char *rndorcname(char *); -extern struct monst *christen_orc(struct monst *, const char *, const char *); +extern struct monst *christen_orc(struct monst *, const char *, + const char *) NONNULLARG1; extern const char *noveltitle(int *); -extern const char *lookup_novel(const char *, int *); -extern void mintroduce(struct monst *); -extern char *mon_wounds(struct monst *, boolean, boolean); -extern void print_mon_wounded(struct monst *, int); +extern const char *lookup_novel(const char *, int *) NONNULLARG1; +extern void mintroduce(struct monst *) NONNULLARG1; +extern char *mon_wounds(struct monst *, boolean, boolean) NONNULLARG1; +extern void print_mon_wounded(struct monst *, int) NONNULLARG1; #ifndef PMNAME_MACROS -extern int Mgender(struct monst *); -extern const char *pmname(struct permonst *, int); +extern int Mgender(struct monst *) NONNULLARG1; +extern const char *pmname(struct permonst *, int) NONNULLARG1; #endif -extern const char *mon_pmname(struct monst *); -extern const char *obj_pmname(struct obj *); -extern boolean mapxy_valid(coordxy, coordxy); +extern const char *mon_pmname(struct monst *) NONNULLARG1; +extern const char *obj_pmname(struct obj *) NONNULLARG1; /* ### do_wear.c ### */ extern const char *fingers_or_gloves(boolean); -extern void off_msg(struct obj *); +extern void off_msg(struct obj *) NONNULLARG1; extern void toggle_displacement(struct obj *, long, boolean); extern void cursed_gear_welds(struct obj *); extern void set_wear(struct obj *); -extern boolean donning(struct obj *); -extern boolean doffing(struct obj *); -extern void cancel_doff(struct obj *, long); +extern boolean donning(struct obj *) NONNULLARG1; +extern boolean doffing(struct obj *) NONNULLARG1; +extern void cancel_doff(struct obj *, long) NONNULLARG1; extern void cancel_don(void); -extern int stop_donning(struct obj *); -extern void dragon_armor_handling(struct obj *, boolean, boolean); +extern int stop_donning(struct obj *); /* doseduce() calls with NULL */ +extern void dragon_armor_handling(struct obj *, boolean, boolean) NONNULLARG1; extern int Armor_off(void); extern int Armor_gone(void); extern int Helmet_off(void); +extern boolean hard_helmet(struct obj *); extern void wielding_corpse(struct obj *, struct obj *, boolean); extern int Gloves_off(void); extern int Boots_on(void); @@ -598,98 +738,104 @@ extern int Cloak_off(void); extern int Shield_off(void); extern int Shirt_off(void); extern void Amulet_off(void); -extern void Ring_on(struct obj *); -extern void Ring_off(struct obj *); -extern void Ring_gone(struct obj *); -extern void Blindf_on(struct obj *); +extern void Ring_on(struct obj *) NONNULLARG1; +extern void Ring_off(struct obj *) NONNULLARG1; +extern void Ring_gone(struct obj *) NONNULLARG1; +extern void Blindf_on(struct obj *) NONNULLARG1; extern void Blindf_off(struct obj *); extern int dotakeoff(void); extern int doremring(void); extern int cursed(struct obj *); extern int armoroff(struct obj *); -extern int canwearobj(struct obj *, long *, boolean); +extern int canwearobj(struct obj *, long *, boolean) NONNULLPTRS; extern boolean will_touch_skin(long); extern int dowear(void); extern int doputon(void); extern void find_ac(void); extern void glibr(void); -extern struct obj *some_armor(struct monst *); +extern struct obj *some_armor(struct monst *) NONNULLARG1; extern struct obj *stuck_ring(struct obj *, int); extern struct obj *unchanger(void); extern void reset_remarm(void); extern int doddoremarm(void); extern int remarm_swapwep(void); -extern int destroy_arm(struct obj *, boolean); -extern void adj_abon(struct obj *, schar); +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 ringbon(short); +extern int any_worn_armor_ok(struct obj *); +extern int count_worn_armor(void); /* ### dog.c ### */ -extern void newedog(struct monst *); -extern void free_edog(struct monst *); -extern void initedog(struct monst *); +extern void newedog(struct monst *) NONNULLARG1; +extern void free_edog(struct monst *) NONNULLARG1; +extern void initedog(struct monst *) NONNULLARG1; extern struct monst *make_familiar(struct obj *, coordxy, coordxy, boolean); extern struct monst *makedog(void); extern void update_mlstmv(void); extern void losedogs(void); -extern void mon_arrive(struct monst *, int); -extern void mon_catchup_elapsed_time(struct monst *, long); +extern void mon_arrive(struct monst *, int) NONNULLARG1; +extern void mon_catchup_elapsed_time(struct monst *, long) NONNULLARG1; extern void keepdogs(boolean, boolean); -extern void migrate_to_level(struct monst *, xint16, xint16, coord *); +extern void migrate_to_level(struct monst *, xint16, xint16, coord *) NONNULLARG1; extern void discard_migrations(void); -extern int dogfood(struct monst *, struct obj *); -extern boolean tamedog(struct monst *, struct obj *, boolean); -extern void abuse_dog(struct monst *); -extern void wary_dog(struct monst *, boolean); +extern int dogfood(struct monst *, struct obj *) NONNULLPTRS; +extern boolean tamedog(struct monst *, struct obj *, boolean, boolean) NONNULLARG1; +extern void abuse_dog(struct monst *) NONNULLARG1; +extern void wary_dog(struct monst *, boolean) NONNULLARG1; /* ### dogmove.c ### */ extern boolean cursed_object_at(coordxy, coordxy); -extern struct obj *droppables(struct monst *); -extern int dog_nutrition(struct monst *, struct obj *); -extern int dog_eat(struct monst *, struct obj *, coordxy, coordxy, boolean); -extern int dog_move(struct monst *, int); -extern boolean could_reach_item(struct monst *, coordxy, coordxy); -extern void finish_meating(struct monst *); -extern void quickmimic(struct monst *); +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 *, 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; +extern void quickmimic(struct monst *) NONNULLARG1; /* ### dokick.c ### */ -extern boolean ghitm(struct monst *, struct obj *); -extern void container_impact_dmg(struct obj *, coordxy, coordxy); +extern boolean ghitm(struct monst *, struct obj *) NONNULLPTRS; +extern void container_impact_dmg(struct obj *, coordxy, coordxy) NONNULLARG1; extern int dokick(void); extern boolean ship_object(struct obj *, coordxy, coordxy, boolean); extern void obj_delivery(boolean); -extern void deliver_obj_to_mon(struct monst *mtmp, int, unsigned long); +extern void deliver_obj_to_mon(struct monst *mtmp, int, unsigned long) NONNULLARG1; extern schar down_gate(coordxy, coordxy); extern void impact_drop(struct obj *, coordxy, coordxy, xint16); /* ### dothrow.c ### */ -extern int multishot_class_bonus(int, struct obj *, struct obj *); +extern int multishot_class_bonus(int, struct obj *, struct obj *) NONNULLARG2; extern int dothrow(void); extern int dofire(void); extern void endmultishot(boolean); -extern void hitfloor(struct obj *, boolean); -extern boolean hurtle_jump(genericptr_t, coordxy, coordxy); -extern boolean hurtle_step(genericptr_t, coordxy, coordxy); -extern boolean will_hurtle(struct monst *, coordxy, coordxy); +extern void hitfloor(struct obj *, boolean) NONNULLARG1; +extern boolean hurtle_jump(genericptr_t, coordxy, coordxy) NONNULLARG1; +extern boolean hurtle_step(genericptr_t, coordxy, coordxy) NONNULLARG1; +extern boolean will_hurtle(struct monst *, coordxy, coordxy) NONNULLARG1; extern void hurtle(int, int, int, boolean); -extern void mhurtle(struct monst *, int, int, int); -extern boolean harmless_missile(struct obj *); -extern boolean throwing_weapon(struct obj *); -extern void throwit(struct obj *, long, boolean, struct obj *); -extern int omon_adj(struct monst *, struct obj *, boolean); -extern int thitmonst(struct monst *, struct obj *); +extern void mhurtle(struct monst *, int, int, int) NONNULLARG1; +extern boolean harmless_missile(struct obj *) NONNULLARG1; +extern boolean throwing_weapon(struct obj *) NONNULLARG1; +extern void throwit(struct obj *, long, boolean, struct obj *) NONNULLARG1; +extern int omon_adj(struct monst *, struct obj *, boolean) NONNULLPTRS; +extern boolean should_mulch_missile(struct obj *); +extern int thitmonst(struct monst *, struct obj *) NONNULLPTRS; extern int hero_breaks(struct obj *, coordxy, coordxy, unsigned); -extern int breaks(struct obj *, coordxy, coordxy); -extern void release_camera_demon(struct obj *, coordxy, coordxy); -extern void breakobj(struct obj *, coordxy, coordxy, boolean, boolean); -extern boolean breaktest(struct obj *); -extern void breakmsg(struct obj *, boolean); +extern int breaks(struct obj *, coordxy, coordxy) NONNULLARG1; +extern void release_camera_demon(struct obj *, coordxy, coordxy) NONNULLARG1; +extern int breakobj(struct obj *, coordxy, coordxy, boolean, boolean) NONNULLARG1; +extern boolean breaktest(struct obj *) NONNULLARG1; +extern void breakmsg(struct obj *, boolean) NONNULLARG1; +extern boolean break_glass_obj(struct obj *) NONNULLARG1; extern boolean walk_path(coord *, coord *, - boolean(*)(void *, coordxy, coordxy), genericptr_t); + boolean(*)(void *, coordxy, coordxy), genericptr_t) NONNULLARG12; /* ### drawing.c ### */ @@ -699,83 +845,70 @@ extern int def_char_is_furniture(char); /* ### dungeon.c ### */ -extern void save_dungeon(NHFILE *, boolean, boolean); -extern void restore_dungeon(NHFILE *); -extern void insert_branch(branch *, boolean); +extern void save_dungeon(NHFILE *, boolean, boolean) NONNULLARG1; +extern void restore_dungeon(NHFILE *) NONNULLARG1; +extern void insert_branch(branch *, boolean) NONNULLARG1; extern void init_dungeons(void); -extern s_level *find_level(const char *); -extern s_level *Is_special(d_level *); -extern branch *Is_branchlev(d_level *); -extern boolean builds_up(d_level *); -extern xint16 ledger_no(d_level *); +extern s_level *find_level(const char *) NONNULLARG1; +extern s_level *Is_special(d_level *) NONNULLARG1; +extern branch *Is_branchlev(d_level *) NONNULLARG1; +extern boolean builds_up(d_level *) NONNULLARG1; +extern xint16 ledger_no(d_level *) NONNULLARG1; extern xint16 maxledgerno(void); -extern schar depth(d_level *); -extern xint16 dunlev(d_level *); -extern xint16 dunlevs_in_dungeon(d_level *); +extern schar depth(d_level *) NONNULLARG1; +extern xint16 dunlev(d_level *) NONNULLARG1; +extern xint16 dunlevs_in_dungeon(d_level *) NONNULLARG1; extern xint16 ledger_to_dnum(xint16); extern xint16 ledger_to_dlev(xint16); extern xint16 deepest_lev_reached(boolean); -extern boolean on_level(d_level *, d_level *); -extern void find_level_beneath(const d_level *, d_level *); +extern boolean on_level(d_level *, d_level *) NONNULLARG12; +extern void find_level_beneath(const d_level *, d_level *) NONNULLARG12; extern void next_level(boolean); extern void prev_level(boolean); extern void u_on_newpos(coordxy, coordxy); extern void u_on_rndspot(int); -extern void stairway_add(coordxy, coordxy, boolean, boolean, d_level *); -extern void stairway_print(void); -extern void stairway_free_all(void); -extern stairway *stairway_at(coordxy, coordxy); -extern stairway *stairway_find(d_level *); -extern stairway *stairway_find_from(d_level *, boolean); -extern stairway *stairway_find_dir(boolean); -extern stairway *stairway_find_type_dir(boolean, boolean); -extern stairway *stairway_find_special_dir(boolean); -extern void u_on_sstairs(int); -extern void u_on_upstairs(void); -extern void u_on_dnstairs(void); -extern boolean On_stairs(coordxy, coordxy); -extern boolean On_ladder(coordxy, coordxy); -extern boolean On_stairs_up(coordxy, coordxy); -extern boolean On_stairs_dn(coordxy, coordxy); -extern void get_level(d_level *, int); -extern boolean Is_botlevel(d_level *); -extern boolean Can_fall_thru(d_level *); -extern boolean Can_dig_down(d_level *); -extern boolean Can_rise_up(coordxy, coordxy, d_level *); -extern boolean has_ceiling(d_level *); -extern boolean avoid_ceiling(d_level *); +extern void get_level(d_level *, int) NONNULLARG1; +extern boolean Is_botlevel(d_level *) NONNULLARG1; +extern boolean Can_fall_thru(d_level *) NONNULLARG1; +extern boolean Can_dig_down(d_level *) NONNULLARG1; +extern boolean Can_rise_up(coordxy, coordxy, d_level *) NONNULLARG3; +extern boolean has_ceiling(d_level *) NONNULLARG1; +extern boolean avoid_ceiling(d_level *) NONNULLARG1; extern const char *surface(coordxy, coordxy); extern const char *ceiling(coordxy, coordxy); -extern boolean In_quest(d_level *); -extern boolean In_mines(d_level *); -extern branch *dungeon_branch(const char *); -extern boolean at_dgn_entrance(const char *); -extern boolean In_hell(d_level *); -extern boolean In_V_tower(d_level *); -extern boolean On_W_tower_level(d_level *); -extern void find_hell(d_level *); +extern boolean In_quest(d_level *) NONNULLARG1; +extern boolean In_mines(d_level *) NONNULLARG1; +extern branch *dungeon_branch(const char *) NONNULL NONNULLARG1; +extern boolean at_dgn_entrance(const char *) NONNULLARG1; +extern boolean In_hell(d_level *) NONNULLARG1; +extern boolean In_V_tower(d_level *) NONNULLARG1; +extern boolean On_W_tower_level(d_level *) NONNULLARG1; +extern void find_hell(d_level *) NONNULLARG1; extern void goto_hell(boolean, boolean); -extern boolean single_level_branch(d_level *); -extern void assign_level(d_level *, d_level *); -extern void assign_rnd_level(d_level *, d_level *, int); +extern boolean single_level_branch(d_level *) NONNULLARG1; +extern void assign_level(d_level *, d_level *) NONNULLPTRS; +extern void assign_rnd_level(d_level *, d_level *, int) NONNULLARG12; extern unsigned int induced_align(int); -extern boolean Invocation_lev(d_level *); +extern boolean Invocation_lev(d_level *) NONNULLARG1; extern xint16 level_difficulty(void); extern schar lev_by_name(const char *); -extern boolean known_branch_stairs(stairway *); -extern char *stairs_description(stairway *, char *, boolean); extern schar print_dungeon(boolean, schar *, xint16 *); -extern char *get_annotation(d_level *); +extern void print_level_annotation(void); extern int donamelevel(void); +extern void free_exclusions(void); +extern void save_exclusions(NHFILE *) NONNULLARG1; +extern void load_exclusions(NHFILE *) NONNULLARG1; extern int dooverview(void); extern void show_overview(int, int); extern void rm_mapseen(int); -extern void init_mapseen(d_level *); +extern void init_mapseen(d_level *) NONNULLARG1; +extern void update_lastseentyp(coordxy, coordxy); +extern int update_mapseen_for(coordxy, coordxy); extern void recalc_mapseen(void); extern void mapseen_temple(struct monst *); extern void room_discovered(int); -extern void recbranch_mapseen(d_level *, d_level *); -extern void overview_stats(winid, const char *, long *, long *); +extern void recbranch_mapseen(d_level *, d_level *) NONNULLPTRS; +extern void overview_stats(winid, const char *, long *, long *) NONNULLPTRS; extern void remdun_mapseen(int); extern const char *endgamelevelname(char *, int); extern boolean sokoban_solved(d_level *); @@ -784,38 +917,39 @@ extern boolean undiscovered_bones(void); /* ### eat.c ### */ extern void eatmupdate(void); -extern boolean is_edible(struct obj *); +extern boolean is_edible(struct obj *) NONNULLARG1; extern void init_uhunger(void); extern int Hear_again(void); extern boolean eating_glob(struct obj *); extern void reset_eat(void); -extern unsigned obj_nutrition(struct obj *); +extern unsigned obj_nutrition(struct obj *) NONNULLARG1; extern int doeat(void); -extern int use_tin_opener(struct obj *); +extern int use_tin_opener(struct obj *) NONNULLARG1; extern void gethungry(void); extern void morehungry(int); extern void lesshungry(int); extern boolean is_fainted(void); extern void reset_faint(void); -extern int corpse_intrinsic(struct permonst *); +extern int corpse_intrinsic(struct permonst *) NONNULLARG1; extern void violated_vegetarian(void); extern void newuhs(boolean); -extern struct obj *floorfood(const char *, int); +extern struct obj *floorfood(const char *, int) NONNULLARG1; extern void vomit(void); -extern int eaten_stat(int, struct obj *); -extern void food_disappears(struct obj *); -extern void food_substitution(struct obj *, struct obj *); +extern int eaten_stat(int, struct obj *) NONNULLARG2; +extern void food_disappears(struct obj *) NONNULLARG1; +extern void food_substitution(struct obj *, struct obj *) NONNULLPTRS; extern long temp_resist(int); -extern void eating_conducts(struct permonst *); -extern int eat_brains(struct monst *, struct monst *, boolean, int *); +extern boolean eating_dangerous_corpse(int); +extern void eating_conducts(struct permonst *) NONNULLARG1; +extern int eat_brains(struct monst *, struct monst *, boolean, + int *) NONNULLARG12; extern void fix_petrification(void); -extern int intrinsic_possible(int, struct permonst *); -extern boolean should_givit(int, struct permonst *); -extern int corpse_intrinsic(struct permonst *); -extern void consume_oeaten(struct obj *, int); +extern int intrinsic_possible(int, struct permonst *) NONNULLARG2; +extern boolean should_givit(int, struct permonst *) NONNULLARG2; +extern void consume_oeaten(struct obj *, int) NONNULLARG1; extern boolean maybe_finished_meal(boolean); -extern void cant_finish_meal(struct obj *); -extern void set_tin_variety(struct obj *, int); +extern void cant_finish_meal(struct obj *) NONNULLARG1; +extern void set_tin_variety(struct obj *, int) NONNULLARG1; extern int tin_variety_txt(char *, int *); extern int tin_variety(struct obj *, boolean); extern void tin_details(struct obj *, int, char *); @@ -827,9 +961,10 @@ extern int Finish_digestion(void); extern void done1(int); extern int done2(void); -extern void format_monkiller(struct monst *); -extern void done_in_by(struct monst *, int); +extern void format_monkiller(struct monst *) NONNULLARG1; +extern void done_in_by(struct monst *, int) NONNULLARG1; extern void done_object_cleanup(void); +extern void NH_abort(char *); #endif /* !MAKEDEFS_C && MDLIB_C && !CPPREGEX_C */ #if !defined(CPPREGEX_C) ATTRNORETURN extern void panic(const char *, ...) PRINTF_F(1, 2) NORETURN; @@ -841,47 +976,45 @@ ATTRNORETURN extern void nh_terminate(int) NORETURN; extern void delayed_killer(int, int, const char *); extern struct kinfo *find_delayed_killer(int); extern void dealloc_killer(struct kinfo *); -extern void save_killers(NHFILE *); -extern void restore_killers(NHFILE *); -extern char *build_english_list(char *); -#if defined(PANICTRACE) && !defined(NO_SIGNAL) -extern void panictrace_setsignals(boolean); -#endif +extern void save_killers(NHFILE *) NONNULLARG1; +extern void restore_killers(NHFILE *) NONNULLARG1; +extern char *build_english_list(char *) NONNULLARG1; /* ### engrave.c ### */ -extern char *random_engraving(char *); -extern void wipeout_text(char *, int, unsigned); +extern char *random_engraving(char *) NONNULLARG1; +extern void wipeout_text(char *, int, unsigned) NONNULLARG1; extern boolean can_reach_floor(boolean); extern void cant_reach_floor(coordxy, coordxy, boolean, boolean); extern struct engr *engr_at(coordxy, coordxy); -extern struct engr *sengr_at(const char *, coordxy, coordxy, boolean); +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); +extern void make_engr_at(coordxy, coordxy, 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 *); -extern void rest_engravings(NHFILE *); -extern void engr_stats(const char *, char *, long *, long *); -extern void del_engr(struct engr *); -extern void rloc_engr(struct engr *); +extern void save_engravings(NHFILE *) NONNULLARG1; +extern void rest_engravings(NHFILE *) NONNULLARG1; +extern void engr_stats(const char *, char *, long *, long *) NONNULLPTRS; +extern void del_engr(struct engr *) NONNULLARG1; +extern void rloc_engr(struct engr *) NONNULLARG1; extern void make_grave(coordxy, coordxy, const char *); extern void disturb_grave(coordxy, coordxy); -extern void map_engraving(struct engr *, int); -extern void see_engraving(struct engr *); -extern void feel_engraving(struct engr *); +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 ### */ extern int newhp(void); extern long newuexp(int); extern int newpw(void); -extern int experience(struct monst *, int); +extern int experience(struct monst *, int) NONNULLARG1; extern void more_experienced(int, int); extern void losexp(const char *); extern void newexplevel(void); @@ -893,37 +1026,38 @@ extern long rndexp(boolean); extern void explode(coordxy, coordxy, int, int, char, int); extern long scatter(coordxy, coordxy, int, unsigned int, struct obj *); extern void splatter_burning_oil(coordxy, coordxy, boolean); -extern void explode_oil(struct obj *, coordxy, coordxy); +extern void explode_oil(struct obj *, coordxy, coordxy) NONNULLARG1; extern int adtyp_to_expltype(const int); -extern void mon_explodes(struct monst *, struct attack *); -extern void mon_explodes_nodmg(struct monst *, struct attack *); +extern void mon_explodes(struct monst *, struct attack *) NONNULLPTRS; +extern void mon_explodes_nodmg(struct monst *, struct attack *) NONNULLPTRS; /* ### files.c ### */ -extern const char *nh_basename(const char *, boolean); +extern const char *nh_basename(const char *, boolean) NONNULLARG1; #if !defined(CROSSCOMPILE) || defined(CROSSCOMPILE_TARGET) -extern int l_get_config_errors(lua_State *); +extern int l_get_config_errors(lua_State *) NONNULLARG1; #endif -extern char *fname_encode(const char *, char, char *, char *, int); -extern char *fname_decode(char, char *, char *, int); +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); -extern void zero_nhfile(NHFILE *); -extern void close_nhfile(NHFILE *); -extern void rewind_nhfile(NHFILE *); -extern void set_levelfile_name(char *, int); +extern FILE *fopen_datafile(const char *, const char *, int) NONNULLPTRS; +extern void zero_nhfile(NHFILE *) NONNULLARG1; +extern void close_nhfile(NHFILE *) NONNULLARG1; +extern void rewind_nhfile(NHFILE *) NONNULLARG1; +extern void set_levelfile_name(char *, int) NONNULLARG1; extern NHFILE *create_levelfile(int, char *); extern NHFILE *open_levelfile(int, char *); extern void delete_levelfile(int); extern void clearlocks(void); -extern NHFILE *create_bonesfile(d_level *, char **, char *); -extern void commit_bonesfile(d_level *); -extern NHFILE *open_bonesfile(d_level *, char **); -extern int delete_bonesfile(d_level *); +extern NHFILE *create_bonesfile(d_level *, char **, char *) NONNULLARG12; +extern void commit_bonesfile(d_level *) NONNULLARG1; +extern NHFILE *open_bonesfile(d_level *, char **) NONNULLPTRS; +extern int delete_bonesfile(d_level *) NONNULLARG1; extern void compress_bonesfile(void); extern void set_savefile_name(boolean); #ifdef INSURANCE -extern void save_savefile_name(NHFILE *); +extern void save_savefile_name(NHFILE *) NONNULLARG1; #endif #ifndef MICRO extern void set_error_savefile(void); @@ -932,32 +1066,34 @@ extern NHFILE *create_savefile(void); extern NHFILE *open_savefile(void); extern int delete_savefile(void); extern NHFILE *restore_saved_game(void); +extern int check_panic_save(void); +#ifdef SELECTSAVED +extern char *plname_from_file(const char *, boolean) NONNULLARG1; +#endif +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 boolean lock_file(const char *, int, int); -extern void unlock_file(const char *); +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 *); +extern boolean parse_config_line(char *) NONNULLARG1; #ifdef USER_SOUNDS -extern boolean can_read_file(const char *); +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 int parse_sym_line(char *, int); -extern void paniclog(const char *, const char *); +extern void paniclog(const char *, const char *) NONNULLPTRS; extern void testinglog(const char *, const char *, const char *); extern int validate_prefix_locations(char *); -#ifdef SELECTSAVED -extern char *plname_from_file(const char *); -#endif -extern char **get_saved_games(void); -extern void free_saved_games(char **); #ifdef SELF_RECOVER extern boolean recover_savefile(void); extern void assure_syscf_file(void); @@ -974,49 +1110,97 @@ extern void signal_whereis(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); -#ifdef EXTRAINFO_FN -extern void mk_dgl_extrainfo(void); -#endif -extern void livelog_add(long ll_type, const char *); +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; /* ### fountain.c ### */ -extern void floating_above(const char *); +extern void floating_above(const char *) NONNULLARG1; extern void dogushforth(int); extern void dryup(coordxy, coordxy, boolean); extern void drinkfountain(void); -extern void dipfountain(struct obj *); +extern void dipfountain(struct obj *) NONNULLARG1; +extern int wash_hands(void); extern void breaksink(coordxy, coordxy); extern void drinksink(void); extern struct obj *ring_from_sink(coordxy, coordxy); +extern void dipsink(struct obj *) NONNULLARG1; +extern void sink_backs_up(coordxy, coordxy); + +/* ### getpos.c ### */ + +extern char *dxdy_to_dist_descr(coordxy, coordxy, boolean); +extern char *coord_desc(coordxy, coordxy, char *, char) NONNULLARG3; +extern void auto_describe(coordxy, coordxy); +extern boolean getpos_menu(coord *, int) NONNULLARG1; +extern int getpos(coord *, boolean, const char *) NONNULLARG1; +extern void getpos_sethilite(void(*f)(boolean), boolean(*d)(coordxy,coordxy)); +extern boolean mapxy_valid(coordxy, coordxy); +extern boolean gather_locs_interesting(coordxy, coordxy, int); + +/* ### glyphs.c ### */ + +extern int glyphrep_to_custom_map_entries(const char *op, + int *glyph) NONNULLPTRS; +extern int add_custom_urep_entry(const char *symset_name, int glyphidx, + uint32 utf32ch, const uint8 *utf8str, + enum graphics_sets which_set) NONNULLARG1; +extern int add_custom_nhcolor_entry(const char *customization_name, + int glyphidx, uint32 nhcolor, + enum graphics_sets which_set) NONNULLARG1; +struct customization_detail *find_matching_customization( + const char *customization_name, + enum customization_types custtype, + enum graphics_sets which_set); +int set_map_customcolor(glyph_map *gm, uint32 nhcolor) NONNULLARG1; +extern int unicode_val(const char *); +extern int glyphrep(const char *) NONNULLARG1; +extern int match_glyph(char *) NONNULLARG1; +extern void dump_all_glyphids(FILE *fp) NONNULLARG1; +extern void wizcustom_glyphids(winid win); +extern void fill_glyphid_cache(void); +extern void free_glyphid_cache(void); +extern boolean glyphid_cache_status(void); +extern void apply_customizations(enum graphics_sets which_set, + enum do_customizations docustomize); +extern void purge_custom_entries(enum graphics_sets which_set); +extern void purge_all_custom_entries(void); +extern void dump_glyphids(void); +extern void clear_all_glyphmap_colors(void); +extern void reset_customcolors(void); +extern int glyph_to_cmap(int); /* ### hack.c ### */ extern boolean is_valid_travelpt(coordxy, coordxy); extern anything *uint_to_any(unsigned); extern anything *long_to_any(long); -extern anything *monst_to_any(struct monst *); -extern anything *obj_to_any(struct obj *); +extern anything *monst_to_any(struct monst *) NONNULLARG1; +extern anything *obj_to_any(struct obj *) NONNULLARG1; extern boolean revive_nasty(coordxy, coordxy, const char *); extern int still_chewing(coordxy, coordxy); extern void movobj(struct obj *, coordxy, coordxy); extern boolean may_dig(coordxy, coordxy); extern boolean may_passwall(coordxy, coordxy); -extern boolean bad_rock(struct permonst *, coordxy, coordxy); -extern int cant_squeeze_thru(struct monst *); +extern boolean bad_rock(struct permonst *, coordxy, coordxy) NONNULLARG1; +extern int cant_squeeze_thru(struct monst *) NONNULLARG1; extern boolean invocation_pos(coordxy, coordxy); extern boolean test_move(coordxy, coordxy, coordxy, coordxy, int); #ifdef DEBUG extern int wiz_debug_cmd_traveldisplay(void); #endif extern boolean u_rooted(void); +extern void notice_mon(struct monst *) NONNULLARG1; +extern void notice_all_mons(boolean); +extern void impact_disturbs_zombies(struct obj *, boolean) NONNULLARG1; +extern void disturb_buried_zombies(coordxy, coordxy); extern boolean u_maybe_impaired(void); -extern const char *u_locomotion(const char *); -extern void handle_tip(int); +extern const char *u_locomotion(const char *) NONNULLARG1; +extern boolean handle_tip(int); extern void domove(void); extern void runmode_delay_output(void); extern void overexert_hp(void); @@ -1039,7 +1223,8 @@ extern void end_running(boolean); extern void nomul(int); extern void unmul(const char *); extern int saving_grace(int); -extern void losehp(int, const char *, schar); +extern void showdamage(int); +extern void losehp(int, const char *, schar) ; extern int weight_cap(void); extern int inv_weight(void); extern int near_capacity(void); @@ -1047,109 +1232,33 @@ extern int calc_capacity(int); extern int max_capacity(void); extern boolean check_capacity(const char *); extern int inv_cnt(boolean); -extern long money_cnt(struct obj *); +/* sometimes money_cnt(gi.invent) which can be null */ +extern long money_cnt(struct obj *) NO_NNARGS; extern void nauseating_loc_effects(void); extern void spot_checks(coordxy, coordxy, schar); +extern int rounddiv(long, int); extern void environment_damages_u(void); -/* ### hacklib.c ### */ - -extern boolean digit(char); -extern boolean letter(char); -extern char highc(char); -extern char lowc(char); -extern char *lcase(char *); -extern char *ucase(char *); -extern char *upstart(char *); -extern char *upwords(char *); -extern char *mungspaces(char *); -extern char *trimspaces(char *); -extern char *strip_newline(char *); -extern char *stripchars(char *, const char *, const char *); -extern char *stripdigits(char *); -extern char *eos(char *); -extern const char *c_eos(const char *); -extern unsigned Strlen_(const char *, const char *, int); -extern boolean str_start_is(const char *, const char *, boolean); -extern boolean str_end_is(const char *, const char *); -extern int str_lines_maxlen(const char *); -extern char *strkitten(char *, char); -extern void copynchars(char *, const char *, int); -extern char chrcasecpy(int, int); -extern char *strcasecpy(char *, const char *); -extern char *s_suffix(const char *); -extern char *ing_suffix(const char *); -extern char *xcrypt(const char *, char *); -extern boolean onlyspace(const char *); -extern char *tabexpand(char *); -extern char *visctrl(char); -extern char *strsubst(char *, const char *, const char *); -extern int strNsubst(char *, const char *, const char *, int); -extern const char *findword(const char *, const char *, int, boolean); -extern const char *ordin(int); -extern char *sitoa(int); -extern int sgn(int); -extern int rounddiv(long, int); -extern int dist2(coordxy, coordxy, coordxy, coordxy); -extern int isqrt(int); -extern int distmin(coordxy, coordxy, coordxy, coordxy); -extern boolean online2(coordxy, coordxy, coordxy, coordxy); -extern unsigned int coord_hash(int, int, int); -extern unsigned int hash1(int); -extern int int_hash1(int); -extern boolean pmatch(const char *, const char *); -extern boolean pmatchi(const char *, const char *); -extern boolean pmatchz(const char *, const char *); -#ifndef STRNCMPI -extern int strncmpi(const char *, const char *, int); -#endif -#ifndef STRSTRI -extern char *strstri(const char *, const char *); -#endif -extern boolean fuzzymatch(const char *, const char *, const char *, boolean); -extern void init_random(int(*fn)(int)); -extern void reseed_random(int(*fn)(int)); -extern time_t getnow(void); -extern int getyear(void); -#if 0 -extern char *yymmdd(time_t); -#endif -extern long yyyymmdd(time_t); -extern long hhmmss(time_t); -extern char *yyyymmddhhmmss(time_t); -extern time_t time_from_yyyymmddhhmmss(char *); -extern int phase_of_the_moon(void); -extern boolean friday_13th(void); -extern int night(void); -extern int midnight(void); -extern int current_holidays(void); -extern void strbuf_init(strbuf_t *); -extern void strbuf_append(strbuf_t *, const char *); -extern void strbuf_reserve(strbuf_t *, int); -extern void strbuf_empty(strbuf_t *); -extern void strbuf_nl_to_crlf(strbuf_t *); -extern int swapbits(int, int, int); -extern void shuffle_int_array(int *, int); -/* note: the snprintf CPP wrapper includes the "fmt" argument in "..." - (__VA_ARGS__) to allow for zero arguments after fmt */ -#define Snprintf(str, size, ...) \ - nh_snprintf(__func__, __LINE__, str, size, __VA_ARGS__) -extern void nh_snprintf(const char *func, int line, char *str, size_t size, - const char *fmt, ...) PRINTF_F(5, 6); -#define FITSint(x) FITSint_(x, __func__, __LINE__) -extern int FITSint_(long long, const char *, int); -#define FITSuint(x) FITSuint_(x, __func__, __LINE__) -extern unsigned FITSuint_(unsigned long long, const char *, int); -#ifdef ENHANCED_SYMBOLS -extern int unicodeval_to_utf8str(int, uint8 *, size_t); -#endif +/* ### strutil.c ### */ + +extern void strbuf_init(strbuf_t *) NONNULLARG1; +extern void strbuf_append(strbuf_t *, const char *) NONNULLPTRS; +extern void strbuf_reserve(strbuf_t *, int) NONNULLARG1; +extern void strbuf_empty(strbuf_t *) NONNULLARG1; +extern void strbuf_nl_to_crlf(strbuf_t *) NONNULLARG1; +extern unsigned Strlen_(const char *, const char *, int) NONNULLPTRS; +extern boolean pmatch(const char *, const char *) NONNULLPTRS; +extern boolean pmatchi(const char *, const char *) NONNULLPTRS; +/* +extern boolean pmatchz(const char *, const char *) NONNULLPTRS; +*/ /* ### insight.c ### */ extern int doattributes(void); extern void enlightenment(int, int); extern void youhiding(boolean, int); -extern char *trap_predicament(char *, int, boolean); +extern char *trap_predicament(char *, int, boolean) NONNULLARG1; extern int doconduct(void); extern void show_conduct(int); extern void record_achievement(schar); @@ -1168,73 +1277,77 @@ extern void list_genocided(char, boolean); extern int dogenocided(void); extern const char *align_str(aligntyp); extern char *piousness(boolean, const char *); -extern void mstatusline(struct monst *); +extern void mstatusline(struct monst *) NONNULLARG1; extern void ustatusline(void); /* ### invent.c ### */ -extern void loot_classify(Loot *, struct obj *); +extern void loot_classify(Loot *, struct obj *) NONNULLPTRS; extern Loot *sortloot(struct obj **, unsigned, boolean, - boolean(*)(struct obj *)); -extern void unsortloot(Loot **); -extern void assigninvlet(struct obj *); -extern struct obj *merge_choice(struct obj *, struct obj *); -extern int merged(struct obj **, struct obj **); -extern void addinv_core1(struct obj *); -extern void addinv_core2(struct obj *); -extern struct obj *addinv(struct obj *); -extern struct obj *addinv_before(struct obj *, struct obj *); + boolean(*)(struct obj *)) NONNULLARG1; +extern void unsortloot(Loot **) NONNULLARG1; +extern void assigninvlet(struct obj *) NONNULLARG1; +extern struct obj *merge_choice(struct obj *, struct obj *) NONNULLARG2; +extern int merged(struct obj **, struct obj **) NONNULLPTRS; +extern void addinv_core1(struct obj *) NONNULLARG1; +extern void addinv_core2(struct obj *) NONNULLARG1; +extern struct obj *addinv(struct obj *) NONNULLARG1; +extern struct obj *addinv_before(struct obj *, struct obj *) NONNULLARG1; +extern struct obj *addinv_nomerge(struct obj *) NONNULLARG1; extern struct obj *hold_another_object(struct obj *, const char *, - const char *, const char *); -extern void useupall(struct obj *); -extern void useup(struct obj *); -extern void consume_obj_charge(struct obj *, boolean); -extern void freeinv_core(struct obj *); -extern void freeinv(struct obj *); + const char *, const char *) NONNULLARG1; +/* nhlua.c calls useupall(gi.invent), but checks gi.invent against NULL + * before doing so. useupall() won't handle NULL*/ +extern void useupall(struct obj *) NONNULLARG1; +extern void useup(struct obj *) NONNULLARG1; +extern void consume_obj_charge(struct obj *, boolean) NONNULLARG1; +extern void freeinv_core(struct obj *) NONNULLARG1; +extern void freeinv(struct obj *) NONNULLARG1; extern void delallobj(coordxy, coordxy); -extern void delobj(struct obj *); -extern void delobj_core(struct obj *, boolean); +extern void delobj(struct obj *) NONNULLARG1; +extern void delobj_core(struct obj *, boolean) NONNULLARG1; extern struct obj *sobj_at(int, coordxy, coordxy); -extern struct obj *nxtobj(struct obj *, int, boolean); +extern struct obj *nxtobj(struct obj *, int, boolean) NONNULLARG1; extern struct obj *carrying(int); extern struct obj *u_carried_gloves(void); extern struct obj *u_have_novel(void); extern struct obj *o_on(unsigned int, struct obj *); -extern boolean obj_here(struct obj *, coordxy, coordxy); +extern boolean obj_here(struct obj *, coordxy, coordxy) NONNULLARG1; extern boolean wearing_armor(void); -extern boolean is_worn(struct obj *); +extern boolean is_worn(struct obj *) NONNULLARG1; +extern boolean is_inuse(struct obj *) NONNULLARG1; extern struct obj *g_at(coordxy, coordxy); -extern boolean splittable(struct obj *); +extern boolean splittable(struct obj *) NONNULLARG1; extern int any_obj_ok(struct obj *); extern int any_obj_or_hands_ok(struct obj *); extern struct obj *getobj(const char *, int(*)(struct obj *), unsigned int); extern int ggetobj(const char *, int(*)(struct obj *), int, boolean, - unsigned *); + unsigned *) NONNULLARG1; extern int askchain(struct obj **, const char *, int, int(*)(struct obj *), - int(*)(struct obj *), int, const char *); -extern void set_cknown_lknown(struct obj *); -extern void fully_identify_obj(struct obj *); -extern int identify(struct obj *); -extern int count_unidentified(struct obj *); + int(*)(struct obj *), int, const char *) NONNULLARG17; +extern void set_cknown_lknown(struct obj *) NONNULLARG1; +extern void fully_identify_obj(struct obj *) NONNULLARG1; +extern int identify(struct obj *) NONNULLARG1; +extern int count_unidentified(struct obj *) NO_NNARGS; extern void identify_pack(int, boolean); extern void learn_unseen_invent(void); extern void update_inventory(void); extern int doperminv(void); -extern void prinv(const char *, struct obj *, long); +extern void prinv(const char *, struct obj *, long) NONNULLARG2; extern char *xprname(struct obj *, const char *, char, boolean, long, long); extern int ddoinv(void); extern char display_inventory(const char *, boolean); extern int display_binventory(coordxy, coordxy, boolean); -extern struct obj *display_cinventory(struct obj *); -extern struct obj *display_minventory(struct monst *, int, char *); +extern struct obj *display_cinventory(struct obj *) NONNULLARG1; +extern struct obj *display_minventory(struct monst *, int, char *) NONNULLARG1; extern int dotypeinv(void); -extern const char *dfeature_at(coordxy, coordxy, char *); +extern const char *dfeature_at(coordxy, coordxy, char *) NONNULLARG3; extern int look_here(int, unsigned); extern int dolook(void); -extern boolean will_feel_cockatrice(struct obj *, boolean); -extern void feel_cockatrice(struct obj *, boolean); -extern void stackobj(struct obj *); -extern boolean mergable(struct obj *, struct obj *); +extern boolean will_feel_cockatrice(struct obj *, boolean) NONNULLARG1; +extern void feel_cockatrice(struct obj *, boolean) NONNULLARG1; +extern void stackobj(struct obj *) NONNULLARG1; +extern boolean mergable(struct obj *, struct obj *) NONNULLPTRS; extern int doprgold(void); extern int doprwep(void); extern int doprarm(void); @@ -1242,25 +1355,28 @@ extern int doprring(void); extern int dopramulet(void); extern int doprtool(void); extern int doprinuse(void); -extern void useupf(struct obj *, long); +extern void useupf(struct obj *, long) NONNULLARG1; extern char *let_to_name(char, boolean, boolean); extern void free_invbuf(void); extern void reassign(void); -extern boolean check_invent_gold(const char *); +extern boolean check_invent_gold(const char *) NONNULLARG1; extern int doorganize(void); extern int adjust_split(void); extern void free_pickinv_cache(void); -extern int count_unpaid(struct obj *); +/* sometimes count_unpaid(gi.invent) which can be null */ +extern int count_unpaid(struct obj *) NO_NNARGS; extern int count_buc(struct obj *, int, boolean(*)(struct obj *)); extern void tally_BUCX(struct obj *, boolean, int *, int *, int *, int *, int *, int *); -extern long count_contents(struct obj *, boolean, boolean, boolean, boolean); -extern void carry_obj_effects(struct obj *); +extern long count_contents(struct obj *, boolean, + boolean, boolean, boolean) NONNULLARG1; +extern void carry_obj_effects(struct obj *) NONNULLARG1; extern const char *currency(long); -extern void silly_thing(const char *, struct obj *); +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 ### */ @@ -1275,43 +1391,44 @@ extern int dosuspend(void); /* ### light.c ### */ -extern void new_light_source(coordxy, coordxy, int, int, union any *); -extern void del_light_source(int, union any *); -extern void do_light_sources(seenV **); +extern void new_light_source(coordxy, coordxy, + int, int, union any *) NONNULLPTRS; +extern void del_light_source(int, union any *) NONNULLARG2; +extern void do_light_sources(seenV **) NONNULLARG1; extern void show_transient_light(struct obj *, coordxy, coordxy); extern void transient_light_cleanup(void); extern struct monst *find_mid(unsigned, unsigned); extern void save_light_sources(NHFILE *, int); -extern void restore_light_sources(NHFILE *); -extern void light_stats(const char *, char *, long *, long *); +extern void restore_light_sources(NHFILE *) NONNULLARG1; +extern void light_stats(const char *, char *, long *, long *) NONNULLPTRS; extern void relink_light_sources(boolean); extern void light_sources_sanity_check(void); -extern void obj_move_light_source(struct obj *, struct obj *); +extern void obj_move_light_source(struct obj *, struct obj *) NONNULLARG12; extern boolean any_light_source(void); extern void snuff_light_source(coordxy, coordxy); -extern boolean obj_sheds_light(struct obj *); -extern boolean obj_is_burning(struct obj *); -extern void obj_split_light_source(struct obj *, struct obj *); -extern void obj_merge_light_sources(struct obj *, struct obj *); -extern void obj_adjust_light_radius(struct obj *, int); -extern int candle_light_range(struct obj *); -extern int arti_light_radius(struct obj *); -extern const char *arti_light_description(struct obj *); +extern boolean obj_sheds_light(struct obj *) NONNULLARG1; +extern boolean obj_is_burning(struct obj *) NONNULLARG1; +extern void obj_split_light_source(struct obj *, struct obj *) NONNULLARG12; +extern void obj_merge_light_sources(struct obj *, struct obj *) NONNULLARG12; +extern void obj_adjust_light_radius(struct obj *, int) NONNULLARG1; +extern int candle_light_range(struct obj *) NONNULLARG1; +extern int arti_light_radius(struct obj *) NONNULLARG1; +extern const char *arti_light_description(struct obj *) NONNULLARG1; extern int wiz_light_sources(void); /* ### lock.c ### */ extern boolean picking_lock(coordxy *, coordxy *); extern boolean picking_at(coordxy, coordxy); -extern void breakchestlock(struct obj *, boolean); +extern void breakchestlock(struct obj *, boolean) NONNULLARG1; extern void reset_pick(void); extern void maybe_reset_pick(struct obj *); extern struct obj *autokey(boolean); extern int pick_lock(struct obj *, coordxy, coordxy, struct obj *); extern boolean u_have_forceable_weapon(void); extern int doforce(void); -extern boolean boxlock(struct obj *, struct obj *); -extern boolean doorlock(struct obj *, struct monst *, coordxy, coordxy); +extern boolean boxlock(struct obj *, struct obj *) NONNULLARG12; +extern boolean doorlock(struct obj *, struct monst *, coordxy, coordxy) NONNULLARG12; extern int doopen(void); extern boolean stumble_on_door_mimic(coordxy, coordxy); extern int doopen_indir(coordxy, coordxy); @@ -1341,104 +1458,107 @@ extern void readmail(struct obj *); /* ### makemon.c ### */ -extern void dealloc_monst(struct monst *); -extern boolean is_home_elemental(struct permonst *); -extern struct monst *clone_mon(struct monst *, coordxy, coordxy); -extern int monhp_per_lvl(struct monst *); -extern int monmaxhp(struct permonst *, uchar); -extern void newmonhp(struct monst *, int); -extern struct mextra *newmextra(void); -extern void copy_mextra(struct monst *, struct monst *); -extern void dealloc_mextra(struct monst *); +extern boolean is_home_elemental(struct permonst *) NONNULLARG1; +extern struct monst *clone_mon(struct monst *, coordxy, coordxy) NONNULLARG1; +extern int monhp_per_lvl(struct monst *) NONNULLARG1; +extern int monmaxhp(struct permonst *, uchar) NONNULLARG1; +extern void newmonhp(struct monst *, int) NONNULLARG1; +extern struct mextra *newmextra(void) NONNULL; extern struct monst *makemon(struct permonst *, coordxy, coordxy, mmflags_nht); -extern struct monst *unmakemon(struct monst *, mmflags_nht); -extern int create_critters(int, struct permonst *, boolean, struct monst *); +extern struct monst *unmakemon(struct monst *, mmflags_nht) NONNULLARG1; +extern int create_critters(int, struct permonst *, boolean, + struct monst *) NONNULLARG4; extern struct permonst *rndmonst_adj(int, int); extern struct permonst *rndmonst(void); extern struct permonst *mkclass(char, int); extern struct permonst *mkclass_aligned(char, int, aligntyp); extern int mkclass_poly(int); -extern int adj_lev(struct permonst *); -extern struct permonst *grow_up(struct monst *, struct monst *); -extern struct obj *mongets(struct monst *, int); +extern int adj_lev(struct permonst *) NONNULLARG1; +extern struct permonst *grow_up(struct monst *, struct monst *) NONNULLARG1; +extern struct obj* mongets(struct monst *, int) NONNULLARG1; extern int golemhp(int); -extern boolean peace_minded(struct permonst *); -extern void set_malign(struct monst *); -extern void newmcorpsenm(struct monst *); -extern void freemcorpsenm(struct monst *); -extern void set_mimic_sym(struct monst *); +extern boolean peace_minded(struct permonst *) NONNULLARG1; +extern void set_malign(struct monst *) NONNULLARG1; +extern void newmcorpsenm(struct monst *) NONNULLARG1; +extern void freemcorpsenm(struct monst *) NONNULLARG1; +extern void set_mimic_sym(struct monst *) NO_NNARGS; /* tests for NULL mtmp */ extern int mbirth_limit(int); -extern void mimic_hit_msg(struct monst *, short); -extern void mkmonmoney(struct monst *, long); +extern void mkmonmoney(struct monst *, long) NONNULLARG1; extern int bagotricks(struct obj *, boolean, int *); extern boolean propagate(int, boolean, boolean); -extern boolean usmellmon(struct permonst *); +extern void summon_furies(int); /* ### mcastu.c ### */ -extern int castmu(struct monst *, struct attack *, boolean, boolean); -extern void touch_of_death(struct monst *); -extern char *death_inflicted_by(char *, const char *, struct monst *); -extern int buzzmu(struct monst *, struct attack *); +extern int castmu(struct monst *, struct attack *, + boolean, boolean) NONNULLARG12; +extern void touch_of_death(struct monst *) NONNULLARG1; +extern char *death_inflicted_by(char *, const char *, + struct monst *) NONNULLARG12; +extern int buzzmu(struct monst *, struct attack *) NONNULLARG12; /* ### mdlib.c ### */ extern void runtime_info_init(void); -extern const char *do_runtime_info(int *); +extern const char *do_runtime_info(int *) NO_NNARGS; extern void release_runtime_info(void); -#ifdef ENHANCED_SYMBOLS -extern void dump_glyphids(void); -#endif +extern char *mdlib_version_string(char *, const char *) NONNULL NONNULLPTRS; /* ### mhitm.c ### */ -extern int fightm(struct monst *); +extern int fightm(struct monst *) NONNULLARG1; extern int mdisplacem(struct monst *, struct monst *, boolean); extern int mattackm(struct monst *, struct monst *); -extern boolean failed_grab(struct monst *, struct monst *, struct attack *); -extern boolean engulf_target(struct monst *, struct monst *); -extern int mon_poly(struct monst *, struct monst *, int); -extern void paralyze_monst(struct monst *, int); -extern int sleep_monst(struct monst *, int, int); -extern void slept_monst(struct monst *); -extern void xdrainenergym(struct monst *, boolean); +extern boolean failed_grab(struct monst *, struct monst *, + struct attack *) NONNULLPTRS; +extern boolean engulf_target(struct monst *, struct monst *) NONNULLARG12; +extern int mon_poly(struct monst *, struct monst *, int) NONNULLARG12; +extern void paralyze_monst(struct monst *, int) NONNULLARG1; +extern int sleep_monst(struct monst *, int, int) NONNULLARG1; +extern void slept_monst(struct monst *) NONNULLARG1; +extern void xdrainenergym(struct monst *, boolean) NONNULLARG1; extern long attk_protection(int); extern void rustm(struct monst *, struct obj *); /* ### mhitu.c ### */ -extern const char *weaphitmsg(struct obj *, struct monst *); -extern const char *barehitmsg(struct monst *); -extern void hitmsg(struct monst *, struct attack *); -extern const char *mswings_verb(struct obj *, boolean); -extern const char *mpoisons_subj(struct monst *, struct attack *); +extern const char *weaphitmsg(struct obj *, struct monst *) NONNULLARG12; +extern const char *barehitmsg(struct monst *) NONNULLARG1; +extern void hitmsg(struct monst *, struct attack *) NONNULLARG12; +extern const char *mswings_verb(struct obj *, boolean) NONNULLARG1; +extern const char *mpoisons_subj(struct monst *, struct attack *) NONNULLARG12; extern void u_slow_down(void); extern struct monst *cloneu(void); -extern void expels(struct monst *, struct permonst *, boolean); +extern void expels(struct monst *, struct permonst *, boolean) NONNULLARG12; extern struct attack *getmattk(struct monst *, struct monst *, int, int *, - struct attack *); -extern int mattacku(struct monst *); -boolean diseasemu(struct permonst *); -boolean u_slip_free(struct monst *, struct attack *); -extern int magic_negation(struct monst *); + struct attack *) NONNULLARG12; +extern int mattacku(struct monst *) NONNULLARG1; +boolean diseasemu(struct permonst *) NONNULLARG1; +boolean u_slip_free(struct monst *, struct attack *) NONNULLARG12; +extern int magic_negation(struct monst *) NONNULLARG1; extern boolean gulp_blnd_check(void); -extern int gazemu(struct monst *, struct attack *); -extern void mdamageu(struct monst *, int); -extern int could_seduce(struct monst *, struct monst *, struct attack *); -extern int doseduce(struct monst *); -extern long attack_contact_slots(struct monst *, int); -extern void piercer_hit(struct monst *, struct monst *); +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; +extern long attack_contact_slots(struct monst *, int) NONNULLARG1; +extern void piercer_hit(struct monst *, struct monst *) NONNULLARG12; /* ### minion.c ### */ -extern void newemin(struct monst *); -extern void free_emin(struct monst *); +extern void newemin(struct monst *) NONNULLARG1; +extern void free_emin(struct monst *) NONNULLARG1; extern int monster_census(boolean); extern int msummon(struct monst *); extern void summon_minion(aligntyp, boolean); -extern boolean boss_entrance(struct monst *); -extern int demon_talk(struct monst *); -extern long bribe(struct monst *); +extern boolean boss_entrance(struct monst *) NONNULLARG1; +extern int demon_talk(struct monst *) NONNULLARG1; +extern long bribe(struct monst *) NONNULLARG1; extern int dprince(aligntyp); extern int dlord(aligntyp); extern int llord(void); @@ -1456,28 +1576,33 @@ extern void start_fiend_harassment(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); -extern void free_luathemes(boolean); +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 *); +extern void add_door(coordxy, coordxy, struct mkroom *) NONNULLARG3; extern xint8 random_door_mask(int, boolean); extern void clear_nonsense_doortraps(coordxy, coordxy); extern int rand_roomtype(void); +extern void count_level_features(void); extern void clear_level_structures(void); extern void level_finalize_topology(void); extern void mklev(void); #ifdef SPECIALIZATION -extern void topologize(struct mkroom *, boolean); +extern void topologize(struct mkroom *, boolean) NONNULLARG1; #else -extern void topologize(struct mkroom *); +extern void topologize(struct mkroom *) NONNULLARG1; #endif -extern void place_branch(branch *, coordxy, coordxy); +/* place_branch() has tests for NULL branch arg, preventing NONNULLARG1 */ +extern void place_branch(branch *, coordxy, coordxy) NO_NNARGS; extern boolean occupied(coordxy, coordxy); extern int okdoor(coordxy, coordxy); -extern void dodoor(coordxy, coordxy, struct mkroom *); -extern void mktrap(int, int, struct mkroom *, coord *); +extern boolean maybe_sdoor(int); +extern void dodoor(coordxy, coordxy, struct mkroom *) NONNULLARG3; +extern void mktrap(int, unsigned, struct mkroom *, coord *) NO_NNARGS; extern void mkstairs(coordxy, coordxy, char, struct mkroom *, boolean); extern void bury_sink_ring(coordxy, coordxy); extern void mkfount(struct mkroom *); @@ -1490,8 +1615,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 ### */ @@ -1503,36 +1628,41 @@ extern void wallification(coordxy, coordxy, coordxy, coordxy); extern void fix_wall_spines(coordxy, coordxy, coordxy, coordxy); extern void walkfrom(coordxy, coordxy, schar); extern void pick_vibrasquare_location(void); -extern void makemaz(const char *); -extern void mazexy(coord *); -extern void get_level_extends(coordxy *, coordxy *, coordxy *, coordxy *); +extern void makemaz(const char *) NONNULLARG1; +extern void mazexy(coord *) NONNULLARG1; +extern void get_level_extends(coordxy *, coordxy *, coordxy *, coordxy *) NONNULLPTRS; 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 *); + coordxy, coordxy, coordxy, xint16, d_level *) NO_NNARGS; extern void fixup_special(void); extern boolean maze_inbounds(coordxy, coordxy); extern void fumaroles(void); extern void movebubbles(void); extern void water_friction(void); -extern void save_waterlevel(NHFILE *); -extern void restore_waterlevel(NHFILE *); +extern void save_waterlevel(NHFILE *) NONNULLARG1; +extern void restore_waterlevel(NHFILE *) NONNULLARG1; +extern void maybe_adjust_hero_bubble(void); /* ### mkobj.c ### */ -extern struct oextra *newoextra(void); +extern struct oextra *newoextra(void) NONNULL; extern void copy_oextra(struct obj *, struct obj *); -extern void dealloc_oextra(struct obj *); -extern void newomonst(struct obj *); -extern void free_omonst(struct obj *); -extern void newomid(struct obj *); -extern void free_omid(struct obj *); +extern void dealloc_oextra(struct obj *) NONNULLARG1; +extern void newomonst(struct obj *) NONNULLARG1; +extern void free_omonst(struct obj *) NONNULLARG1; +extern void newomid(struct obj *) NONNULLARG1; +extern void free_omid(struct obj *) NONNULLARG1; +/* extern void newolong(struct obj *); extern void free_olong(struct obj *); -extern void new_omailcmd(struct obj *, const char *); -extern void free_omailcmd(struct obj *); +*/ +extern void new_omailcmd(struct obj *, const char *) NONNULLPTRS; +extern void free_omailcmd(struct obj *) NONNULLARG1; extern struct obj *mkobj_at(char, coordxy, coordxy, boolean); extern struct obj *mksobj_at(int, coordxy, coordxy, boolean, boolean); extern struct obj *mksobj_migr_to_species(int, unsigned, boolean, boolean); @@ -1540,175 +1670,184 @@ extern struct obj *mkobj(int, boolean) NONNULL; extern int rndmonnum_adj(int, int); extern int rndmonnum(void); extern boolean bogon_is_pname(char); -extern struct obj *splitobj(struct obj *, long) NONNULL; +extern struct obj *splitobj(struct obj *, long) NONNULLARG1; extern unsigned next_ident(void); -extern struct obj *unsplitobj(struct obj *); +extern struct obj *unsplitobj(struct obj *) NONNULLARG1; extern void clear_splitobjs(void); -extern void replace_object(struct obj *, struct obj *); -extern struct obj *unknwn_contnr_contents(struct obj *); -extern void bill_dummy_object(struct obj *); -extern void costly_alteration(struct obj *, int); +extern void replace_object(struct obj *, struct obj *) NONNULLARG12; +extern struct obj *unknwn_contnr_contents(struct obj *) NONNULLARG1; +extern void bill_dummy_object(struct obj *) NONNULLARG1; +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 int bcsign(struct obj *); -extern int weight(struct obj *); +extern int bcsign(struct obj *) NONNULLARG1; +extern int weight(struct obj *) NONNULLARG1; extern struct obj *mkgold(long, coordxy, coordxy); +extern void fixup_oil(struct obj *, struct obj *) NONNULLARG1; extern struct obj *mkcorpstat(int, struct monst *, struct permonst *, - coordxy, coordxy, unsigned); -extern int corpse_revive_type(struct obj *); + coordxy, coordxy, unsigned) NONNULL; +extern int corpse_revive_type(struct obj *) NONNULLARG1; extern struct obj *obj_attach_mid(struct obj *, unsigned); -extern struct monst *get_mtraits(struct obj *, boolean); +extern struct monst *get_mtraits(struct obj *, boolean) NONNULLARG1; extern struct obj *mk_tt_object(int, coordxy, coordxy); extern struct obj *mk_named_object(int, struct permonst *, coordxy, coordxy, - const char *); -extern int material_bonus(struct obj *); + const char *) ; +extern int material_bonus(struct obj *) NONNULLARG1; extern struct obj *rnd_treefruit_at(coordxy, coordxy); -extern void set_corpsenm(struct obj *, int); -extern long rider_revival_time(struct obj *, boolean); -extern void start_corpse_timeout(struct obj *); -extern void start_glob_timeout(struct obj *, long); -extern void shrink_glob(anything *, long); -extern void maybe_adjust_light(struct obj *, int); -extern void bless(struct obj *); -extern void unbless(struct obj *); -extern void curse(struct obj *); -extern void uncurse(struct obj *); -extern void blessorcurse(struct obj *, int); -extern void set_bknown(struct obj *, unsigned); -extern boolean is_flammable(struct obj *); -extern boolean is_rottable(struct obj *); -extern void place_object(struct obj *, coordxy, coordxy); +extern void set_corpsenm(struct obj *, int) NONNULLARG1; +extern long rider_revival_time(struct obj *, boolean) NONNULLARG1; +extern void start_corpse_timeout(struct obj *) NONNULLARG1; +extern void start_glob_timeout(struct obj *, long) NONNULLARG1; +extern void shrink_glob(anything *, long) NONNULLARG1; +extern void maybe_adjust_light(struct obj *, int) NONNULLARG1; +extern void bless(struct obj *) NONNULLARG1; +extern void unbless(struct obj *) NONNULLARG1; +extern void curse(struct obj *) NONNULLARG1; +extern void uncurse(struct obj *) NONNULLARG1; +extern void blessorcurse(struct obj *, int) NONNULLARG1; +extern void set_bknown(struct obj *, unsigned) NONNULLARG1; +extern boolean is_flammable(struct obj *) NONNULLARG1; +extern boolean is_rottable(struct obj *) NONNULLARG1; +extern void place_object(struct obj *, coordxy, coordxy) NONNULLARG1; extern void recreate_pile_at(coordxy, coordxy); -extern void remove_object(struct obj *); -extern void discard_minvent(struct monst *, boolean); -extern void obj_extract_self(struct obj *); -extern void extract_nobj(struct obj *, struct obj **); -extern void extract_nexthere(struct obj *, struct obj **); -extern int add_to_minv(struct monst *, struct obj *); -extern struct obj *add_to_container(struct obj *, struct obj *); -extern void add_to_migration(struct obj *); -extern void add_to_buried(struct obj *); -extern void dealloc_obj(struct obj *); +extern void remove_object(struct obj *) NONNULLARG1; +extern void discard_minvent(struct monst *, boolean) NONNULLARG1; +extern void obj_extract_self(struct obj *) NONNULLARG1; +extern void extract_nobj(struct obj *, struct obj **) NONNULLARG12; +extern void extract_nexthere(struct obj *, struct obj **) NONNULLARG12; +extern int add_to_minv(struct monst *, struct obj *) NONNULLARG12; +extern struct obj *add_to_container(struct obj *, struct obj *) NONNULLARG12; +extern void add_to_migration(struct obj *) NONNULLARG1; +extern void add_to_buried(struct obj *) NONNULLARG1; +extern void container_weight(struct obj *) NONNULLARG1; +extern void dealloc_obj(struct obj *) NONNULLARG1; extern void obj_ice_effects(coordxy, coordxy, boolean); -extern long peek_at_iced_corpse_age(struct obj *); +extern long peek_at_iced_corpse_age(struct obj *) NONNULLARG1; +extern void dobjsfree(void); extern int hornoplenty(struct obj *, boolean, struct obj *); extern void obj_sanity_check(void); extern struct obj *obj_nexto(struct obj *); -extern struct obj *obj_nexto_xy(struct obj *, coordxy, coordxy, boolean); +extern struct obj *obj_nexto_xy(struct obj *, coordxy, coordxy, boolean) NONNULLARG1; extern struct obj *obj_absorb(struct obj **, struct obj **); extern struct obj *obj_meld(struct obj **, struct obj **); -extern void pudding_merge_message(struct obj *, struct obj *); -extern void choose_thiefstone_loc(coord *); -extern void init_obj_material(struct obj *); -extern boolean valid_obj_material(struct obj *, uchar); -extern void set_material(struct obj *, uchar); +extern void pudding_merge_message(struct obj *, struct obj *) NONNULLARG12; +extern void choose_thiefstone_loc(coord *) NONNULLARG1; +extern void init_obj_material(struct obj *) NONNULLARG1; +extern boolean valid_obj_material(struct obj *, uchar) NONNULLARG1; +extern void set_material(struct obj *, uchar) NONNULLARG1; extern struct obj *init_dummyobj(struct obj *, short, long); /* ### mkroom.c ### */ extern void do_mkroom(int); -extern void fill_zoo(struct mkroom *); +extern void fill_zoo(struct mkroom *) NONNULLARG1; extern struct permonst *antholemon(void); extern boolean nexttodoor(int, int); -extern boolean has_stairs(struct mkroom *, boolean); +extern boolean has_stairs(struct mkroom *, boolean) NONNULLARG1; #define has_upstairs(croom) has_stairs(croom, TRUE) #define has_dnstairs(croom) has_stairs(croom, FALSE) -extern int somex(struct mkroom *); -extern int somey(struct mkroom *); -extern boolean inside_room(struct mkroom *, coordxy, coordxy); -extern boolean somexy(struct mkroom *, coord *); -extern boolean somexyspace(struct mkroom *, coord *); -extern void mkundead(coord *, boolean, int); +extern int somex(struct mkroom *) NONNULLARG1; +extern int somey(struct mkroom *) NONNULLARG1; +extern boolean inside_room(struct mkroom *, coordxy, coordxy) NONNULLARG1; +extern boolean somexy(struct mkroom *, coord *) NONNULLARG12; +extern boolean somexyspace(struct mkroom *, coord *) NONNULLARG12; +extern void mkundead(coord *, boolean, int) NONNULLARG1; extern struct permonst *courtmon(void); -extern void save_rooms(NHFILE *); -extern void rest_rooms(NHFILE *); +extern void save_rooms(NHFILE *) NONNULLARG1; +extern void rest_rooms(NHFILE *) NONNULLARG1; extern struct mkroom *search_special(schar); extern int cmap_to_type(int); /* ### mon.c ### */ +extern void dealloc_monst(struct monst *) NONNULLARG1; +extern void copy_mextra(struct monst *, struct monst *); +extern void dealloc_mextra(struct monst *) NONNULLARG1; extern void mon_sanity_check(void); -extern boolean zombie_maker(struct monst *); -extern int zombie_form(struct permonst *); -extern int m_poisongas_ok(struct monst *); +extern boolean zombie_maker(struct monst *) NONNULLARG1; +extern int zombie_form(struct permonst *) NONNULLARG1; +extern int m_poisongas_ok(struct monst *) NONNULLARG1; extern int undead_to_corpse(int); extern int genus(int, int); extern int pm_to_cham(int); -extern int minliquid(struct monst *); -extern boolean movemon_singlemon(struct monst *); +extern int minliquid(struct monst *) NONNULLARG1; +extern boolean movemon_singlemon(struct monst *) NONNULLARG1; extern int movemon(void); -extern void meatbox(struct monst *, struct obj *); -extern void m_consume_obj(struct monst *, struct obj *); -extern int meatmetal(struct monst *); -extern int meatobj(struct monst *); -extern int meatrocks(struct monst *); -extern int meatcorpse(struct monst *); -extern boolean mon_wants_prop(struct permonst *, struct monst *); -extern void mon_give_prop(struct monst *, int); -extern void mon_givit(struct monst *, struct permonst *); -extern void mpickgold(struct monst *); -extern boolean mpickstuff(struct monst *); -extern int curr_mon_load(struct monst *); -extern int max_mon_load(struct monst *); -extern boolean can_touch_safely(struct monst *, struct obj *); -extern int can_carry(struct monst *, struct obj *); -extern long mon_allowflags(struct monst *); -extern boolean m_in_air(struct monst *); -extern int mfndpos(struct monst *, coord *, long *, long); -extern long mm_aggression(struct monst *, struct monst *); -extern boolean monnear(struct monst *, coordxy, coordxy); +extern void meatbox(struct monst *, struct obj *) NONNULLPTRS; +extern void m_consume_obj(struct monst *, struct obj *) NONNULLPTRS; +extern int meatmetal(struct monst *) NONNULLARG1; +extern int meatobj(struct monst *) NONNULLARG1; +extern int meatrocks(struct monst *) NONNULLARG1; +extern int meatcorpse(struct monst *) NONNULLARG1; +extern boolean mon_wants_prop(struct permonst *, struct monst *) NONNULLARG12; +extern void mon_give_prop(struct monst *, int) NONNULLARG1; +extern void mon_givit(struct monst *, struct permonst *) NONNULLARG12; +extern void mpickgold(struct monst *) NONNULLARG1; +extern boolean mpickstuff(struct monst *) NONNULLARG1; +extern int curr_mon_load(struct monst *) NONNULLARG1; +extern int max_mon_load(struct monst *) NONNULLARG1; +extern boolean can_touch_safely(struct monst *, struct obj *) NONNULLARG12; +extern int can_carry(struct monst *, struct obj *) NONNULLARG12; +extern long mon_allowflags(struct monst *) NONNULLARG1; +extern boolean m_in_air(struct monst *) NONNULLARG1; +extern int mfndpos(struct monst *, coord *, long *, long) NONNULLPTRS; +extern long mm_aggression(struct monst *, struct monst *) NONNULLARG12; +extern boolean monnear(struct monst *, coordxy, coordxy) NONNULLARG1; extern void dmonsfree(void); -extern void elemental_clog(struct monst *); -extern int mcalcmove(struct monst *, boolean); +extern void elemental_clog(struct monst *) NONNULLARG1; +extern int mcalcmove(struct monst *, boolean) NONNULLARG1; extern void mcalcdistress(void); -extern void replmon(struct monst *, struct monst *); -extern void relmon(struct monst *, struct monst **); -extern boolean faulty_lifesaver(struct obj *); -extern struct obj *mlifesaver(struct monst *); -extern boolean corpse_chance(struct monst *, struct monst *, boolean); -extern void mondead(struct monst *); -extern void mondied(struct monst *); -extern void mongone(struct monst *); -extern void monstone(struct monst *); -extern void monkilled(struct monst *, const char *, int); +extern void replmon(struct monst *, struct monst *) NONNULLARG12; +extern void relmon(struct monst *, struct monst **) NONNULLARG1; +extern boolean faulty_lifesaver(struct obj *) NONNULLARG1; +extern struct obj *mlifesaver(struct monst *) NONNULLARG1; +extern boolean corpse_chance(struct monst *, struct monst *, boolean) NONNULLARG1; +extern void mondead(struct monst *) NONNULLARG1; +extern void mondied(struct monst *) NONNULLARG1; +extern void mongone(struct monst *) NONNULLARG1; +extern void monstone(struct monst *) NONNULLARG1; +extern void monkilled(struct monst *, const char *, int) NONNULLARG1; extern void set_ustuck(struct monst *); -extern void unstuck(struct monst *); -extern void killed(struct monst *); -extern void xkilled(struct monst *, int); -extern void mon_to_stone(struct monst *); -extern void m_into_limbo(struct monst *); -extern void migrate_mon(struct monst *, xint16, xint16); -extern void mnexto(struct monst *, unsigned); -extern void deal_with_overcrowding(struct monst *); -extern void maybe_mnexto(struct monst *); -extern int mnearto(struct monst *, coordxy, coordxy, boolean, unsigned); -extern void m_respond(struct monst *); -extern void setmangry(struct monst *, boolean); -extern void wakeup(struct monst *, boolean, boolean); -extern void wake_nearby(void); +extern void unstuck(struct monst *) NONNULLARG1; +extern void killed(struct monst *) NONNULLARG1; +extern void xkilled(struct monst *, int) NONNULLARG1; +extern void mon_to_stone(struct monst *) NONNULLARG1; +extern void m_into_limbo(struct monst *) NONNULLARG1; +extern void migrate_mon(struct monst *, xint16, xint16) NONNULLARG1; +extern void mnexto(struct monst *, unsigned) NONNULLARG1; +extern void deal_with_overcrowding(struct monst *) NONNULLARG1; +extern void maybe_mnexto(struct monst *) NONNULLARG1; +extern int mnearto(struct monst *, coordxy, coordxy, boolean, unsigned) NONNULLARG1; +extern void m_respond(struct monst *) NONNULLARG1; +extern void setmangry(struct monst *, boolean) NONNULLARG1; +extern void wake_msg(struct monst *, boolean) NONNULLARG1; +extern void wakeup(struct monst *, boolean, boolean) NONNULLARG1; +extern void wake_nearby(boolean); extern void wake_nearto(coordxy, coordxy, int); -extern void seemimic(struct monst *); -extern void normal_shape(struct monst *); +extern void seemimic(struct monst *) NONNULLARG1; +extern void normal_shape(struct monst *) NONNULLARG1; +extern void alloc_itermonarr(unsigned); extern void iter_mons_safe(boolean (*)(struct monst *)); extern void iter_mons(void (*)(struct monst *)); 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 *); +extern void restore_cham(struct monst *) NONNULLARG1; extern void maybe_unhide_at(coordxy, coordxy); -extern boolean hideunder(struct monst *); -extern void hide_monst(struct monst *); +extern boolean hideunder(struct monst *) NONNULLARG1; +extern void hide_monst(struct monst *) NONNULLARG1; extern void mon_animal_list(boolean); extern boolean valid_vampshiftform(int, int); -extern boolean validvamp(struct monst *, int *, int); -extern int select_newcham_form(struct monst *); -extern void mgender_from_permonst(struct monst *, struct permonst *); -extern int newcham(struct monst *, struct permonst *, unsigned); +extern boolean validvamp(struct monst *, int *, int) NONNULLARG12; +extern int select_newcham_form(struct monst *) NONNULLARG1; +extern void mgender_from_permonst(struct monst *, struct permonst *) NONNULLARG12; +extern int newcham(struct monst *, struct permonst *, unsigned) NONNULLARG1; extern int can_be_hatched(int); extern int egg_type_from_parent(int, boolean); extern boolean dead_species(int, boolean); @@ -1716,117 +1855,134 @@ extern void kill_genocided_monsters(void); extern void golemeffects(struct monst *, int, int); extern boolean angry_guards(boolean); extern void pacify_guards(void); -extern void decide_to_shapeshift(struct monst *, int); -extern boolean vamp_stone(struct monst *); -extern void check_gear_next_turn(struct monst *); -extern void mon_aireffects(struct monst *); +extern void decide_to_shapeshift(struct monst *) NONNULLARG1; +extern boolean vamp_stone(struct monst *) NONNULLARG1; +extern void check_gear_next_turn(struct monst *) NONNULLARG1; +extern void mon_aireffects(struct monst *) NONNULLARG1; extern int geryon_bonus(void); +extern void copy_mextra(struct monst *, struct monst *); +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 *) NONNULLARG1; +extern void see_nearby_monsters(void); +extern void shieldeff_mon(struct monst *) NONNULLARG1; +extern void flash_mon(struct monst *) NONNULLARG1; /* ### mondata.c ### */ -extern void set_mon_data(struct monst *, struct permonst *); -extern struct attack *attacktype_fordmg(struct permonst *, int, int); -extern boolean attacktype(struct permonst *, int); -extern boolean noattacks(struct permonst *); -extern boolean poly_when_stoned(struct permonst *); -extern boolean defended(struct monst *, int); -extern boolean resists_drli(struct monst *); -extern boolean resists_magm(struct monst *); -extern boolean resists_fire(struct monst *); -extern boolean resists_blnd(struct monst *); -extern boolean can_blnd(struct monst *, struct monst *, uchar, struct obj *); -extern boolean resists_light_halu(struct monst *); -extern boolean ranged_attk(struct permonst *); -extern boolean hates_material(struct permonst *, int); -extern boolean mon_hates_material(struct monst *, int); +extern void set_mon_data(struct monst *, struct permonst *) NONNULLARG12; +extern struct attack *attacktype_fordmg(struct permonst *, int, int) NONNULLARG1; +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_drli(struct monst *) NONNULLARG1; +extern boolean resists_magm(struct monst *) NONNULLARG1; +extern boolean resists_fire(struct monst *) NONNULLARG1; +extern boolean resists_blnd(struct monst *) NONNULLARG1; +extern boolean resists_blnd_by_arti(struct monst *) NONNULLARG1; +extern boolean can_blnd(struct monst *, struct monst *, + uchar, struct obj *) NONNULLARG2; +extern boolean resists_light_halu(struct monst *) NONNULLARG1; +extern boolean ranged_attk(struct permonst *) NONNULLARG1; +extern boolean hates_material(struct permonst *, int) NONNULLARG1; +extern boolean mon_hates_material(struct monst *, int) NONNULLARG1; extern int sear_damage(int); -extern boolean mon_hates_blessings(struct monst *); -extern boolean hates_blessings(struct permonst *); -extern boolean mon_hates_light(struct monst *); -extern boolean passes_bars(struct permonst *); -extern boolean can_blow(struct monst *); -extern boolean can_chant(struct monst *); -extern boolean can_be_strangled(struct monst *); -extern boolean can_track(struct permonst *); -extern boolean breakarm(struct permonst *); -extern boolean sliparm(struct permonst *); -extern boolean sticks(struct permonst *); -extern boolean cantvomit(struct permonst *); -extern int num_horns(struct permonst *); -extern struct attack *dmgtype_fromattack(struct permonst *, int, int); -extern boolean dmgtype(struct permonst *, int); -extern int max_passive_dmg(struct monst *, struct monst *); -extern boolean same_race(struct permonst *, struct permonst *); -extern int monsndx(struct permonst *); -extern int name_to_mon(const char *, int *); -extern int name_to_monplus(const char *, const char **, int *); +extern boolean mon_hates_blessings(struct monst *) NONNULLARG1; +extern boolean hates_blessings(struct permonst *) NONNULLARG1; +extern boolean mon_hates_light(struct monst *) NONNULLARG1; +extern boolean passes_bars(struct permonst *) NONNULLARG1; +extern boolean can_blow(struct monst *) NONNULLARG1; +extern boolean can_chant(struct monst *) NONNULLARG1; +extern boolean can_be_strangled(struct monst *) NONNULLARG1; +extern boolean can_track(struct permonst *) NONNULLARG1; +extern boolean breakarm(struct permonst *) NONNULLARG1; +extern boolean sliparm(struct permonst *) NONNULLARG1; +extern boolean sticks(struct permonst *) NONNULLARG1; +extern boolean cantvomit(struct permonst *) NONNULLARG1; +extern int num_horns(struct permonst *) NONNULLARG1; +extern struct attack *dmgtype_fromattack(struct permonst *, int, int) NONNULLARG1; +extern boolean dmgtype(struct permonst *, int) NONNULLARG1; +extern int max_passive_dmg(struct monst *, struct monst *) NONNULLARG12; +extern boolean same_race(struct permonst *, struct permonst *) NONNULLARG12; +extern int name_to_mon(const char *, int *) NONNULLARG1; +extern int name_to_monplus(const char *, const char **, int *) NONNULLARG1; extern int name_to_monclass(const char *, int *); -extern int gender(struct monst *); -extern int pronoun_gender(struct monst *, unsigned); -extern boolean levl_follower(struct monst *); +extern int gender(struct monst *) NONNULLARG1; +extern int pronoun_gender(struct monst *, unsigned) NONNULLARG1; +extern boolean levl_follower(struct monst *) NONNULLARG1; extern int little_to_big(int); extern int big_to_little(int); extern boolean big_little_match(int, int); -extern const char *locomotion(const struct permonst *, const char *); -extern const char *stagger(const struct permonst *, const char *); -extern const char *on_fire(struct permonst *, struct attack *); -extern const char *msummon_environ(struct permonst *, const char **); -extern struct permonst *raceptr(struct monst *); -extern boolean olfaction(struct permonst *); +extern const char *locomotion(const struct permonst *, const char *) NONNULLARG12; +extern const char *stagger(const struct permonst *, const char *) NONNULLARG12; +extern const char *on_fire(struct permonst *, struct attack *) NONNULLARG12; +extern const char *msummon_environ(struct permonst *, const char **) NONNULLARG12; +extern struct permonst *raceptr(struct monst *) NONNULLARG1; +extern boolean olfaction(struct permonst *) NONNULLARG1; extern int monmaterial(int); -extern int emits_light(struct permonst *); -extern boolean is_fleshy(const struct permonst *); +extern int emits_light(struct permonst *) NONNULLARG1; +extern boolean is_fleshy(const struct permonst *) NONNULLARG1; unsigned long cvt_adtyp_to_mseenres(uchar); +unsigned long cvt_prop_to_mseenres(uchar); extern void monstseesu(unsigned long); -extern boolean resist_conflict(struct monst *); -extern boolean mon_knows_traps(struct monst *, int); -extern void mon_learns_traps(struct monst *, int); -extern void mons_see_trap(struct trap *); +extern void monstunseesu(unsigned long); +extern void give_u_to_m_resistances(struct monst *) NONNULLARG1; +extern boolean resist_conflict(struct monst *) NONNULLARG1; +extern boolean mon_knows_traps(struct monst *, int) NONNULLARG1; +extern void mon_learns_traps(struct monst *, int) NONNULLARG1; +extern void mons_see_trap(struct trap *) NONNULLARG1; extern int get_atkdam_type(int); #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) -extern int mstrength(struct permonst *); +extern int mstrength(struct permonst *) NONNULLARG1; #endif /* ### monmove.c ### */ #ifdef FUZZER_LOG -extern void fuzl_mtmp(const char *, struct monst *); +extern void fuzl_mtmp(const char *, struct monst *) NONNULLPTRS; extern void fuzl_p2(const char *, const char *, struct monst *, const char *, - struct monst *); -extern void fuzl_xy(const char *, int, int); -extern void fuzl_xyi(const char *, int, int, int); -#endif -extern boolean mon_would_take_item(struct monst *, struct obj *); -extern boolean mon_would_consume_item(struct monst *, struct obj *); -extern boolean itsstuck(struct monst *); -extern boolean mb_trapped(struct monst *, boolean); -extern void mon_track_add(struct monst *, coordxy, coordxy); -extern void mon_track_clear(struct monst *); -extern boolean monhaskey(struct monst *, boolean); -extern void mon_regen(struct monst *, boolean); -extern int dochugw(struct monst *, boolean); -extern boolean onscary(coordxy, coordxy, struct monst *); + struct monst *) NONNULLPTRS; +extern void fuzl_xy(const char *, int, int) NONNULLARG1; +extern void fuzl_xyi(const char *, int, int, int)NONNULLARG1; +#endif +extern boolean mon_would_take_item(struct monst *, struct obj *) NONNULLARG12; +extern boolean mon_would_consume_item(struct monst *, struct obj *) NONNULLARG12; +extern boolean itsstuck(struct monst *) NONNULLARG1; +extern boolean mb_trapped(struct monst *, boolean) NONNULLARG1; +extern void mon_track_add(struct monst *, coordxy, coordxy) NONNULLARG1; +extern void mon_track_clear(struct monst *) NONNULLARG1; +extern boolean monhaskey(struct monst *, boolean) NONNULLARG1; +extern void mon_regen(struct monst *, boolean) NONNULLARG1; +extern void m_everyturn_effect(struct monst *) NONNULLARG1; +extern void m_postmove_effect(struct monst *) NONNULLARG1; +extern int dochugw(struct monst *, boolean) NONNULLARG1; +extern boolean onscary(coordxy, coordxy, struct monst *) NONNULLARG3; extern struct monst *find_pmmonst(int); -extern int bee_eat_jelly(struct monst *, struct obj *); -extern void monflee(struct monst *, int, boolean, boolean); -extern void mon_yells(struct monst *, const char *); -extern boolean m_can_break_boulder(struct monst *); -extern void m_break_boulder(struct monst *, coordxy, coordxy); -extern int dochug(struct monst *); -extern boolean m_digweapon_check(struct monst *, coordxy, coordxy); -extern int m_move(struct monst *, int); -extern int m_move_aggress(struct monst *, coordxy, coordxy); +extern int bee_eat_jelly(struct monst *, struct obj *) NONNULLARG12; +extern void monflee(struct monst *, int, boolean, boolean) NONNULLARG1; +extern void mon_yells(struct monst *, const char *) NONNULLARG12; +extern boolean m_can_break_boulder(struct monst *) NONNULLARG1; +extern void m_break_boulder(struct monst *, coordxy, coordxy) NONNULLARG1; +extern int dochug(struct monst *) NONNULLARG1; +extern boolean m_digweapon_check(struct monst *, coordxy, coordxy) NONNULLARG1; +extern boolean m_avoid_kicked_loc(struct monst *, coordxy, coordxy) NONNULLARG1; +extern boolean m_avoid_soko_push_loc(struct monst *, coordxy, coordxy) NONNULLARG1; +extern int m_move(struct monst *, int) NONNULLARG1; +extern int m_move_aggress(struct monst *, coordxy, coordxy) NONNULLARG1; extern int concealed_spot(coordxy, coordxy); extern void dissolve_bars(coordxy, coordxy); extern boolean closed_door(coordxy, coordxy); extern boolean accessible(coordxy, coordxy); -extern void set_apparxy(struct monst *); -extern boolean can_ooze(struct monst *); -extern boolean can_fog(struct monst *); +extern void set_apparxy(struct monst *) NONNULLARG1; +extern boolean can_ooze(struct monst *) NONNULLARG1; +extern boolean can_fog(struct monst *) NONNULLARG1; extern boolean should_displace(struct monst *, coord *, long *, int, coordxy, - coordxy); -extern boolean undesirable_disp(struct monst *, coordxy, coordxy); -extern boolean mon_open_door(struct monst *, coordxy, coordxy); + coordxy) NONNULLPTRS; +extern boolean undesirable_disp(struct monst *, coordxy, coordxy) NONNULLARG1; +extern boolean mon_open_door(struct monst *, coordxy, coordxy) NONNULLARG1; extern void remove_monster(coordxy, coordxy); /* ### monst.c ### */ @@ -1835,9 +1991,10 @@ extern void monst_globals_init(void); /* ### mplayer.c ### */ -extern struct monst *mk_mplayer(struct permonst *, coordxy, coordxy, boolean); +extern struct monst *mk_mplayer(struct permonst *, + coordxy, coordxy, boolean) NONNULLARG1; extern void create_mplayers(int, boolean); -extern void mplayer_talk(struct monst *); +extern void mplayer_talk(struct monst *) NONNULLARG1; #if defined(MICRO) || defined(WIN32) @@ -1886,6 +2043,13 @@ extern int dosuspend(void); extern void nt_regularize(char *); extern int(*nt_kbhit)(void); extern void Delay(int); +# ifdef CRASHREPORT +struct CRctxt; +extern struct CRctxt *ctxp; +extern int win32_cr_helper(char, struct CRctxt *, void *, int); +extern int win32_cr_gettrace(int, char *, int); +extern int *win32_cr_shellexecute(const char *); +# endif #endif /* WIN32 */ #endif /* MICRO || WIN32 */ @@ -1893,71 +2057,70 @@ extern void Delay(int); /* ### mthrowu.c ### */ extern const char *rnd_hallublast(void); -extern boolean m_has_launcher_and_ammo(struct monst *); -extern int thitu(int, int, struct obj **, const char *); -extern int ohitmon(struct monst *, struct obj *, int, boolean); -extern boolean thrwmu(struct monst *); -extern int spitmu(struct monst *, struct attack *); -extern int breamu(struct monst *, struct attack *); +extern boolean m_has_launcher_and_ammo(struct monst *) NONNULLARG1; +extern int thitu(int, int, struct obj **, const char *) NO_NNARGS; +extern boolean ohitmon(struct monst *, struct obj *, + int, boolean) NONNULLARG12; +extern boolean thrwmu(struct monst *) NONNULLARG1; +extern int spitmu(struct monst *, struct attack *) NONNULLPTRS; +extern int breamu(struct monst *, struct attack *) NONNULLPTRS; extern boolean linedup_callback(coordxy, coordxy, coordxy, coordxy, boolean(*)(coordxy, coordxy)); extern boolean linedup(coordxy, coordxy, coordxy, coordxy, int); -extern boolean lined_up(struct monst *); -extern struct obj *m_carrying(struct monst *, int); -extern int thrwmm(struct monst *, struct monst *); -extern int spitmm(struct monst *, struct attack *, struct monst *); -extern int breamm(struct monst *, struct attack *, struct monst *); -extern void m_useupall(struct monst *, struct obj *); -extern void m_useup(struct monst *, struct obj *); +extern boolean lined_up(struct monst *) NONNULLARG1; +extern struct obj *m_carrying(struct monst *, int) NONNULLARG1; +extern int thrwmm(struct monst *, struct monst *) NONNULLARG12; +extern int spitmm(struct monst *, struct attack *, struct monst *) NONNULLPTRS; +extern int breamm(struct monst *, struct attack *, struct monst *) NONNULLPTRS; +extern void m_useupall(struct monst *, struct obj *) NONNULLARG12; +extern void m_useup(struct monst *, struct obj *) NONNULLARG12; extern void m_throw(struct monst *, coordxy, coordxy, coordxy, coordxy, - int, struct obj *); + int, struct obj *) NONNULLPTRS; extern void hit_bars(struct obj **, coordxy, coordxy, coordxy, coordxy, - unsigned); + unsigned) NONNULLARG1; extern boolean hits_bars(struct obj **, coordxy, coordxy, coordxy, coordxy, - int, int); + int, int) NONNULLARG1; /* ### muse.c ### */ -extern boolean find_defensive(struct monst *, boolean); -extern int use_defensive(struct monst *); -extern int rnd_defensive_item(struct monst *); -extern boolean find_offensive(struct monst *); -extern int use_offensive(struct monst *); -extern int rnd_offensive_item(struct monst *); -extern boolean find_misc(struct monst *); -extern int use_misc(struct monst *); -extern int rnd_misc_item(struct monst *); -extern boolean searches_for_item(struct monst *, struct obj *); -extern boolean mon_reflects(struct monst *, const char *); +extern boolean find_defensive(struct monst *, boolean) NONNULLARG1; +extern int use_defensive(struct monst *) NONNULLARG1; +extern int rnd_defensive_item(struct monst *) NONNULLARG1; +extern boolean find_offensive(struct monst *) NONNULLARG1; +extern int use_offensive(struct monst *) NONNULLARG1; +extern int rnd_offensive_item(struct monst *) NONNULLARG1; +extern boolean find_misc(struct monst *) NONNULLARG1; +extern int use_misc(struct monst *) NONNULLARG1; +extern int rnd_misc_item(struct monst *) NONNULLARG1; +extern boolean searches_for_item(struct monst *, struct obj *) NONNULLARG12; +extern boolean mon_reflects(struct monst *, const char *) NONNULLARG1; extern const char* ureflectsrc(void); -extern void mcureblindness(struct monst *, boolean); -extern boolean munstone(struct monst *, boolean); -extern boolean munslime(struct monst *, boolean); +extern void mcureblindness(struct monst *, boolean) NONNULLARG1; +extern boolean munstone(struct monst *, boolean) NONNULLARG1; +extern boolean munslime(struct monst *, boolean) NONNULLARG1; /* ### music.c ### */ -extern void put_monsters_to_sleep(struct monst *, int); -extern void awaken_soldiers(struct monst *); -extern int do_play_instrument(struct obj *); -enum instruments obj_to_instr(struct obj *); +extern void put_monsters_to_sleep(struct monst *, int) NONNULLARG1; +extern void awaken_soldiers(struct monst *) NONNULLARG1; +extern int do_play_instrument(struct obj *) NONNULLARG1; +enum instruments obj_to_instr(struct obj *) NONNULLARG1; /* ### nhlsel.c ### */ #if !defined(CROSSCOMPILE) || defined(CROSSCOMPILE_TARGET) -extern struct selectionvar *l_selection_check(lua_State *, int); -extern int l_selection_register(lua_State *); -extern void l_selection_push_copy(lua_State *, struct selectionvar *); -extern void nhl_push_obj(lua_State *, struct obj *); -extern int nhl_obj_u_giveobj(lua_State *); -extern int l_obj_register(lua_State *); +extern struct selectionvar *l_selection_check(lua_State *, int) NONNULLARG1; +extern int l_selection_register(lua_State *) NONNULLARG1; +extern void l_selection_push_copy(lua_State *, struct selectionvar *) NONNULLARG12; +extern int l_obj_register(lua_State *) NONNULLARG1; #endif /* ### nhlobj.c ### */ #if !defined(CROSSCOMPILE) || defined(CROSSCOMPILE_TARGET) -extern void nhl_push_obj(lua_State *, struct obj *); -extern int nhl_obj_u_giveobj(lua_State *); -extern int l_obj_register(lua_State *); +extern void nhl_push_obj(lua_State *, struct obj *) NONNULLARG12; +extern int nhl_obj_u_giveobj(lua_State *) NONNULLARG1; +extern int l_obj_register(lua_State *) NONNULLARG1; #endif /* ### nhlua.c ### */ @@ -1966,50 +2129,61 @@ extern int l_obj_register(lua_State *); extern void l_nhcore_init(void); extern void l_nhcore_done(void); extern void l_nhcore_call(int); -extern lua_State * nhl_init(nhl_sandbox_info *); -extern void nhl_done(lua_State *); -extern boolean nhl_loadlua(lua_State *, const char *); +extern lua_State * nhl_init(nhl_sandbox_info *) NONNULLARG1; +/* nhl_done contains a test for NULL arg1, preventing NONNULLARG1 */ +extern void nhl_done(lua_State *) NO_NNARGS; +extern boolean nhl_loadlua(lua_State *, const char *) NONNULLARG12; extern char *get_nh_lua_variables(void); -extern void save_luadata(NHFILE *); -extern void restore_luadata(NHFILE *); -extern int nhl_pcall(lua_State *, int, int); -extern boolean load_lua(const char *, nhl_sandbox_info *); -ATTRNORETURN extern void nhl_error(lua_State *, const char *) NORETURN; -extern void lcheck_param_table(lua_State *); -extern schar get_table_mapchr(lua_State *, const char *); -extern schar get_table_mapchr_opt(lua_State *, const char *, schar); -extern short nhl_get_timertype(lua_State *, int); -extern boolean nhl_get_xy_params(lua_State *, lua_Integer *, lua_Integer *); -extern void nhl_add_table_entry_int(lua_State *, const char *, lua_Integer); -extern void nhl_add_table_entry_char(lua_State *, const char *, char); -extern void nhl_add_table_entry_str(lua_State *, const char *, const char *); -extern void nhl_add_table_entry_bool(lua_State *, const char *, boolean); +extern void save_luadata(NHFILE *) NONNULLARG1; +extern void restore_luadata(NHFILE *) NONNULLARG1; +extern int nhl_pcall(lua_State *, int, int, const char *) NONNULLARG1; +extern int nhl_pcall_handle(lua_State *, int, int, const char *, + NHL_pcall_action) NONNULLARG1; +extern boolean load_lua(const char *, nhl_sandbox_info *) NONNULLARG12; +ATTRNORETURN extern void nhl_error(lua_State *, const char *) + NORETURN NONNULLARG12; +extern void lcheck_param_table(lua_State *) NONNULLARG1; +extern schar get_table_mapchr(lua_State *, const char *) NONNULLARG12; +extern schar get_table_mapchr_opt(lua_State *, const char *, schar) + NONNULLARG12; +extern short nhl_get_timertype(lua_State *, int) NONNULLARG1; +extern boolean nhl_get_xy_params(lua_State *, lua_Integer *, lua_Integer *) + NONNULLARG123; +extern void nhl_add_table_entry_int(lua_State *, const char *, lua_Integer) + NONNULLARG12; +extern void nhl_add_table_entry_char(lua_State *, const char *, char) + NONNULLARG12; +extern void nhl_add_table_entry_str(lua_State *, const char *, const char *) + NONNULLARG123; +extern void nhl_add_table_entry_bool(lua_State *, const char *, boolean) + NONNULLARG12; extern void nhl_add_table_entry_region(lua_State *, const char *, - coordxy, coordxy, coordxy, coordxy); + coordxy, coordxy, coordxy, coordxy) + NONNULLARG12; extern schar splev_chr2typ(char); -extern schar check_mapchr(const char *); -extern int get_table_int(lua_State *, const char *); -extern int get_table_int_opt(lua_State *, const char *, int); -extern char *get_table_str(lua_State *, const char *); -extern char *get_table_str_opt(lua_State *, const char *, char *); -extern int get_table_boolean(lua_State *, const char *); -extern int get_table_boolean_opt(lua_State *, const char *, int); +extern schar check_mapchr(const char *) NO_NNARGS; +extern int get_table_int(lua_State *, const char *) NONNULLARG12; +extern int get_table_int_opt(lua_State *, const char *, int) NONNULLARG12; +extern char *get_table_str(lua_State *, const char *) NONNULLARG12; +/* dungeon.c init_dungeon_levels() passes NULL to get_table_str_opt arg3 */ +extern char *get_table_str_opt(lua_State *, const char *, char *) NONNULLARG12; +extern int get_table_boolean(lua_State *, const char *) NONNULLARG12; +extern int get_table_boolean_opt(lua_State *, const char *, int) NONNULLARG12; +/* lspo_feature calls get_table_option(L, "type", NULL, features), + so arg3 can be NULL. NONNULLARG124 is not currently defined */ extern int get_table_option(lua_State *, const char *, const char *, - const char *const *); -extern int str_lines_max_width(const char *); -extern char *stripdigits(char *); + const char *const *) NO_NNARGS; +/* extern int str_lines_max_width(const char *); */ extern const char *get_lua_version(void); -extern void nhl_pushhooked_open_table(lua_State *L); +extern void nhl_pushhooked_open_table(lua_State *L) NONNULLARG1; +extern void free_tutorial(void); +extern void tutorial(boolean); #endif /* !CROSSCOMPILE || CROSSCOMPILE_TARGET */ -#endif /* MAKEDEFS_C MDLIB_C CPPREGEX_C */ -/* ### nhregex.c ### */ +#endif /* MAKEDEFS_C MDLIB_C CPPREGEX_C */ -extern struct nhregex *regex_init(void); -extern boolean regex_compile(const char *, struct nhregex *); -extern char *regex_error_desc(struct nhregex *, char *); -extern boolean regex_match(const char *, struct nhregex *); -extern void regex_free(struct nhregex *); +/* ### {cpp,pmatch,posix}regex.c ### */ +#include "nhregex.h" #if !defined(MAKEDEFS_C) && !defined(MDLIB_C) && !defined(CPPREGEX_C) @@ -2022,6 +2196,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); @@ -2037,11 +2212,12 @@ extern void tty_ibmgraphics_fixup(void); extern void init_objects(void); extern void init_oclass_probs(void); -extern void obj_shuffle_range(int, int *, int *); -extern boolean objdescr_is(struct obj *, const char *); +extern void obj_shuffle_range(int, int *, int *) NONNULLPTRS; +/* objdescr_is() contains a test for NULL arg1, so can't be NONNULLARG12 */ +extern boolean objdescr_is(struct obj *, const char *) NONNULLARG2; extern void oinit(void); -extern void savenames(NHFILE *); -extern void restnames(NHFILE *); +extern void savenames(NHFILE *) NONNULLARG1; +extern void restnames(NHFILE *) NONNULLARG1; extern void discover_object(int, boolean, boolean); extern void undiscover_object(int); extern boolean interesting_to_discover(int); @@ -2049,6 +2225,7 @@ extern int choose_disco_sort(int); extern int dodiscovered(void); extern int doclassdisco(void); extern void rename_disco(void); +extern void get_sortdisco(char *opts, boolean cnf) NONNULLARG1; /* ### objects.c ### */ @@ -2056,141 +2233,162 @@ extern void objects_globals_init(void); /* ### objnam.c ### */ -extern void maybereleaseobuf(char *); +extern void maybereleaseobuf(char *) NONNULLARG1; extern char *obj_typename(int); extern char *simple_typename(int); extern char *safe_typename(int); -extern boolean obj_is_pname(struct obj *); -extern char *distant_name(struct obj *, char *(*)(struct obj *)); +extern boolean obj_is_pname(struct obj *) NONNULLARG1; +extern char *distant_name(struct obj *, char *(*)(struct obj *)) NONNULLPTRS; extern char *fruitname(boolean); extern struct fruit *fruit_from_indx(int); -extern struct fruit *fruit_from_name(const char *, boolean, int *); +extern struct fruit *fruit_from_name(const char *, boolean, int *) NONNULLARG1; extern void reorder_fruit(boolean); -extern char *xname(struct obj *); -extern char *mshot_xname(struct obj *); -extern boolean the_unique_obj(struct obj *); -extern boolean the_unique_pm(struct permonst *); -extern boolean erosion_matters(struct obj *); -extern char *doname(struct obj *); -extern char *doname_with_price(struct obj *); -extern char *doname_vague_quan(struct obj *); -extern boolean not_fully_identified(struct obj *); -extern char *corpse_xname(struct obj *, const char *, unsigned); -extern char *cxname(struct obj *); -extern char *cxname_singular(struct obj *); -extern char *killer_xname(struct obj *); +extern char *xname(struct obj *) NONNULLARG1; +extern char *mshot_xname(struct obj *) NONNULLARG1; +extern boolean the_unique_obj(struct obj *) NONNULLARG1; +extern boolean the_unique_pm(struct permonst *) NONNULLARG1; +extern boolean erosion_matters(struct obj *) NONNULLARG1; +extern char *doname(struct obj *) NONNULLARG1; +extern char *doname_with_price(struct obj *) NONNULLARG1; +extern char *doname_vague_quan(struct obj *) NONNULLARG1; +extern boolean not_fully_identified(struct obj *) NONNULLARG1; +extern char *corpse_xname(struct obj *, const char *, unsigned) NONNULLARG1; +extern char *cxname(struct obj *) NONNULLARG1; +extern char *cxname_singular(struct obj *) NONNULLARG1; +extern char *killer_xname(struct obj *) NONNULLARG1; extern char *short_oname(struct obj *, char *(*)(struct obj *), - char *(*)(struct obj *), unsigned); -extern const char *singular(struct obj *, char *(*)(struct obj *)); -extern char *just_an(char *, const char *); -extern char *an(const char *); -extern char *An(const char *); -extern char *The(const char *); -extern char *the(const char *); -extern char *aobjnam(struct obj *, const char *); -extern char *yobjnam(struct obj *, const char *); -extern char *Yobjnam2(struct obj *, const char *); -extern char *Tobjnam(struct obj *, const char *); -extern char *otense(struct obj *, const char *); -extern char *vtense(const char *, const char *); -extern char *Doname2(struct obj *); -extern char *yname(struct obj *); -extern char *Yname2(struct obj *); -extern char *ysimple_name(struct obj *); -extern char *Ysimple_name2(struct obj *); -extern char *simpleonames(struct obj *); -extern char *ansimpleoname(struct obj *); -extern char *thesimpleoname(struct obj *); -extern char *actualoname(struct obj *); -extern char *bare_artifactname(struct obj *); -extern char *makeplural(const char *); -extern char *makesingular(const char *); -extern short name_to_otyp(const char *); -extern boolean object_not_monster(const char *); -extern struct obj *readobjnam(char *, struct obj *); + char *(*)(struct obj *), unsigned) NONNULLARG12; +extern const char *singular(struct obj *, char *(*)(struct obj *)) NONNULLPTRS; +extern char *just_an(char *, const char *) NONNULL NONNULLARG12; +/* an(), the() contain tests for NULL arg, preventing NONNULLARG1 */ +extern char *an(const char *) NONNULL NO_NNARGS; +extern char *An(const char *) NONNULL NO_NNARGS; +extern char *The(const char *) NONNULL NO_NNARGS; +extern char *the(const char *) NONNULL NO_NNARGS; +extern char *aobjnam(struct obj *, const char *) NONNULL NONNULLARG1; +extern char *yobjnam(struct obj *, const char *) NONNULL NONNULLARG1; +extern char *Yobjnam2(struct obj *, const char *) NONNULL NONNULLARG1; +extern char *Tobjnam(struct obj *, const char *) NONNULL NONNULLARG1; +extern char *otense(struct obj *, const char *) NONNULL NONNULLARG12; +extern char *vtense(const char *, const char *) NONNULL NONNULLARG2; +extern char *Doname2(struct obj *) NONNULL NONNULLARG1; +extern char *paydoname(struct obj *) NONNULL NONNULLARG1; +extern char *yname(struct obj *) NONNULL NONNULLARG1; +extern char *Yname2(struct obj *) NONNULL NONNULLARG1; +extern char *ysimple_name(struct obj *) NONNULL NONNULLARG1; +extern char *Ysimple_name2(struct obj *) NONNULL NONNULLARG1; +extern char *simpleonames(struct obj *) NONNULL NONNULLARG1; +extern char *ansimpleoname(struct obj *) NONNULL NONNULLARG1; +extern char *thesimpleoname(struct obj *) NONNULL NONNULLARG1; +extern char *actualoname(struct obj *) NONNULL NONNULLARG1; +extern char *bare_artifactname(struct obj *) NONNULL NONNULLARG1; +/* makeplural() and makesingular() never return NULL but have tests for NULL + arg1, and code path that leads to impossible(), preventing NONNULLARG1 */ +extern char *makeplural(const char *) NONNULL NO_NNARGS; +extern char *makesingular(const char *) NONNULL NO_NNARGS; +extern short name_to_otyp(const char *) NONNULLARG1; +extern boolean object_not_monster(const char *) NONNULLARG1; +/* readobjnam() can return NULL and allows a NULL to trigger code path for + random object */ +extern struct obj *readobjnam(char *, struct obj *) NO_NNARGS; extern int rnd_class(int, int); -extern const char *Japanese_item_name(int, const char *); -extern const char *armor_simple_name(struct obj *); -extern const char *suit_simple_name(struct obj *); -extern const char *cloak_simple_name(struct obj *); -extern const char *helm_simple_name(struct obj *); -extern const char *gloves_simple_name(struct obj *); -extern const char *boots_simple_name(struct obj *); -extern const char *shield_simple_name(struct obj *); -extern const char *shirt_simple_name(struct obj *); -extern char *dragon_scales_color(struct obj *); -extern const char *mimic_obj_name(struct monst *); +/* discover_object() passes NULL arg2 to Japanese_item_name(), + * preventing NONNULLARG2 */ +extern const char *Japanese_item_name(int, const char *) NO_NNARGS; +extern const char *armor_simple_name(struct obj *) NONNULL NONNULLARG1; +/* suit_simple_name has its code in a NULL arg test + conditional block, preventing NONNULLARG1 */ +extern const char *suit_simple_name(struct obj *) NONNULL NO_NNARGS; +/* cloak_simple_name has its code in a NULL arg test + conditional block, preventing NONNULLARG1 */ +extern const char *cloak_simple_name(struct obj *) NONNULL NO_NNARGS; +/* helm_simple_name always just returns hardcoded literals */ +extern const char *helm_simple_name(struct obj *) NONNULL NO_NNARGS; +/* gloves_simple_name has its code in a NULL arg test + conditional block, preventing NONNULLARG1 */ +extern const char *gloves_simple_name(struct obj *) NONNULL NO_NNARGS; +/* boots_simple_name has its code in a NULL arg test + conditional block, preventing NONNULLARG1 */ +extern const char *boots_simple_name(struct obj *) NONNULL NO_NNARGS; +/* shield_simple_name has its code in a NULL arg test + conditional block, preventing NONNULLARG1 */ +extern const char *shield_simple_name(struct obj *) NONNULL NO_NNARGS; +/* shirt_simple_name always just returns hardcoded "shirt" */ +extern const char *shirt_simple_name(struct obj *) NONNULL NO_NNARGS; +extern char *dragon_scales_color(struct obj *) NONNULLARG1; +extern const char *mimic_obj_name(struct monst *) NONNULL NONNULLARG1; +/* safe_qbuf() contains tests for NULL arg2 and arg3, qprefix and qsuffix, + preventing use of NONNULLPTRS. */ extern char *safe_qbuf(char *, const char *, const char *, struct obj *, - char *(*)(struct obj *), char *(*)(struct obj *), - const char *); + char * (*)(struct obj *), char * (*)(struct obj *), + const char *) NONNULL NONNULLARG14; extern int shiny_obj(char); /* ### options.c ### */ extern boolean ask_do_tutorial(void); -extern boolean match_optname(const char *, const char *, int, boolean); -extern uchar txt2key(char *); +extern boolean match_optname(const char *, const char *, int, boolean) NONNULLARG12; +extern uchar txt2key(char *) NONNULLARG1; extern void initoptions(void); extern void initoptions_init(void); extern void initoptions_finish(void); -extern boolean parseoptions(char *, boolean, boolean); +extern boolean parseoptions(char *, boolean, boolean) NONNULLARG1; extern void freeroleoptvals(void); -extern char *get_option_value(const char *, boolean); +extern char *get_option_value(const char *, boolean) NONNULLARG1; extern int doset_simple(void); extern int doset(void); extern int dotogglepickup(void); extern void option_help(void); -extern void all_options_strbuf(strbuf_t *); -extern void next_opt(winid, const char *); -extern int fruitadd(char *, struct fruit *); -extern int choose_classes_menu(const char *, int, boolean, char *, char *); -extern boolean parsebindings(char *); -extern void oc_to_str(char *, char *); +extern void all_options_strbuf(strbuf_t *) NONNULLARG1; +extern void next_opt(winid, const char *) NONNULLARG2; +extern int fruitadd(char *, struct fruit *) NONNULLARG1; +extern boolean parsebindings(char *) NONNULLARG1; +extern void oc_to_str(char *, char *) NONNULLARG12; extern void add_menu_cmd_alias(char, char); extern char get_menu_cmd_key(char); extern char map_menu_cmd(char); -extern char *collect_menu_keys(char *, unsigned, boolean); +extern char *collect_menu_keys(char *, unsigned, boolean) NONNULLARG1; extern void show_menu_controls(winid, boolean); -extern void assign_warnings(uchar *); -extern char *nh_getenv(const char *); +extern void assign_warnings(uchar *) NONNULLARG1; +extern char *nh_getenv(const char *) NONNULLARG1; extern void reset_duplicate_opt_detection(void); extern void set_wc_option_mod_status(unsigned long, int); extern void set_wc2_option_mod_status(unsigned long, int); -extern void set_option_mod_status(const char *, int); -extern int add_autopickup_exception(const char *); +extern void set_option_mod_status(const char *, int) NONNULLARG1; +extern int add_autopickup_exception(const char *) NONNULLARG1; extern void free_autopickup_exceptions(void); extern void set_playmode(void); -extern int sym_val(const char *); -extern int query_color(const char *); -extern int query_attr(const char *); -extern const char *clr2colorname(int); -extern int match_str2clr(char *); -extern int match_str2attr(const char *, boolean); -extern boolean add_menu_coloring(char *); -extern boolean get_menu_coloring(const char *, int *, int *); -extern void free_menu_coloring(void); -extern boolean msgtype_parse_add(char *); -extern int msgtype_type(const char *, boolean); +extern int sym_val(const char *) NONNULLARG1; +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 boolean add_monstercolor(char *); +extern void options_free_window_colors(void); +#ifdef TTY_PERM_INVENT +extern void check_perm_invent_again(void); +#endif +extern boolean add_monstercolor(char *) NONNULLARG1; /* ### pager.c ### */ -extern char *self_lookat(char *); -extern char *monhealthdescr(struct monst *mon, boolean, char *); -extern void mhidden_description(struct monst *, boolean, char *); -extern boolean object_from_map(int, coordxy, coordxy, struct obj **); -extern void checkfile(char *, struct permonst *, boolean, boolean, char *); -extern const char *waterbody_name(coordxy, coordxy); +extern char *self_lookat(char *) NONNULL NONNULLARG1; +extern char *monhealthdescr(struct monst *mon, boolean, + char *) NONNULL NONNULLARG3; +extern void mhidden_description(struct monst *, unsigned, char *) NONNULLPTRS; +extern boolean object_from_map(int, coordxy, coordxy, + struct obj **) NONNULLPTRS; +extern const char *waterbody_name(coordxy, coordxy) NONNULL; +extern char *ice_descr(coordxy, coordxy, char *) NONNULL NONNULLARG3; +extern boolean ia_checkfile(struct obj *) NONNULLARG1; +extern boolean checkfile(char *, struct permonst *, unsigned, char *) NONNULLARG1; extern int do_screen_description(coord, boolean, int, char *, const char **, - struct permonst **); + struct permonst **) NONNULLARG45; extern int do_look(int, coord *); extern int dowhatis(void); extern int doquickwhatis(void); extern int doidtrap(void); extern int dowhatdoes(void); -extern char *dowhatdoes_core(char, char *); +extern char *dowhatdoes_core(char, char *) NONNULLARG2; /*might return NULL*/ extern int dohelp(void); extern int dohistory(void); @@ -2201,13 +2399,21 @@ extern int dohistory(void); extern void chdirx(char *, boolean); #endif /* CHDIR */ extern boolean authorize_wizard_mode(void); +extern boolean authorize_explore_mode(void); #endif #if defined(WIN32) 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); @@ -2243,49 +2449,61 @@ extern void getlock(void); /* ### pickup.c ### */ extern int collect_obj_classes(char *, struct obj *, boolean, - boolean(*)(struct obj *), int *); -extern boolean rider_corpse_revival(struct obj *, boolean); + boolean(*)(struct obj *), int *) NONNULLARG5; +extern boolean rider_corpse_revival(struct obj *, boolean) NO_NNARGS; +extern void force_decor(boolean); extern void deferred_decor(boolean); extern boolean menu_class_present(int); extern void add_valid_menu_class(int); -extern boolean allow_all(struct obj *); -extern boolean allow_category(struct obj *); -extern boolean is_worn_by_type(struct obj *); -extern int ck_bag(struct obj *); -extern void removed_from_icebox(struct obj *); -extern void reset_justpicked(struct obj *); -extern int count_justpicked(struct obj *); -extern struct obj *find_justpicked(struct obj *); +extern boolean allow_all(struct obj *) NO_NNARGS; +extern boolean allow_category(struct obj *) NONNULLARG1; +extern boolean is_worn_by_type(struct obj *) NONNULLARG1; +extern int ck_bag(struct obj *) NONNULLARG1; +extern void removed_from_icebox(struct obj *) NONNULLARG1; +/* reset_justpicked() is sometimes passed gi.invent + * which can be null */ +extern void reset_justpicked(struct obj *) NO_NNARGS; +/* sometimes count_justpicked(gi.invent) which can be null */ +extern int count_justpicked(struct obj *) NO_NNARGS; +/* sometimes find_justpicked(gi.invent) which can be null */ +extern struct obj *find_justpicked(struct obj *) NO_NNARGS; extern int pickup(int); -extern int pickup_object(struct obj *, long, boolean); -extern boolean thiefstone_accepts(struct obj *, struct obj *); -extern void thiefstone_teleport(struct obj *, struct obj *, boolean); -extern boolean thiefstone_tele_mon(struct obj *, struct monst *); -extern int query_category(const char *, struct obj *, int, menu_item **, int); +extern int pickup_object(struct obj *, long, boolean) NONNULLARG1; +extern boolean thiefstone_accepts(struct obj *, struct obj *) NONNULLARG12; +extern void thiefstone_teleport(struct obj *, struct obj *, boolean) NONNULLARG12; +extern boolean thiefstone_tele_mon(struct obj *, struct monst *) NONNULLARG12; +extern int query_category(const char *, struct obj *, int, menu_item **, int) NONNULLARG14; +/* dotypeinv() call query_objlist with NULL arg1 */ extern int query_objlist(const char *, struct obj **, int, menu_item **, int, - boolean(*)(struct obj *)); -extern struct obj *pick_obj(struct obj *); + boolean(*)(struct obj *)) NONNULLARG24; +extern struct obj *pick_obj(struct obj *) NONNULLARG1; extern int encumber_msg(void); extern int container_at(coordxy, coordxy, boolean); extern int doloot(void); -extern void observe_quantum_cat(struct obj *, boolean, boolean); -extern boolean container_gone(int(*)(struct obj *)); +extern void observe_quantum_cat(struct obj *, boolean, boolean) NONNULLARG1; +extern boolean container_gone(int(*)(struct obj *)) NONNULLARG1; extern boolean u_handsy(void); -extern int use_container(struct obj **, boolean, boolean); -extern int loot_mon(struct monst *, int *, boolean *); +extern int use_container(struct obj **, boolean, boolean) NONNULLARG1; +extern int loot_mon(struct monst *, int *, boolean *) NO_NNARGS; extern int dotip(void); -extern struct autopickup_exception *check_autopickup_exceptions(struct obj *); -extern boolean autopick_testobj(struct obj *, boolean); -extern void tipcontainer(struct obj *); -extern void dump_container(struct obj *, struct obj *, int); +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; +extern void tipcontainer(struct obj *) NONNULLARG1; +extern void dump_container(struct obj *, struct obj *, int) NONNULLARG1; /* ### pline.c ### */ -#if defined(DUMPLOG) || defined(DUMPHTML) +#ifdef DUMPLOG_CORE extern void dumplogmsg(const char *); extern void dumplogfreemessages(void); #endif extern void pline(const char *, ...) PRINTF_F(1, 2); +extern void pline_dir(int, const char *, ...) PRINTF_F(2, 3); +extern void pline_xy(coordxy, coordxy, const char *, ...) PRINTF_F(3, 4); +extern void pline_mon(struct monst *, const char *, ...) PRINTF_F(2, 3) NONNULLARG1; +extern void set_msg_dir(int); +extern void set_msg_xy(coordxy, coordxy); extern void custompline(unsigned, const char *, ...) PRINTF_F(2, 3); extern void urgent_pline(const char *, ...) PRINTF_F(1, 2); extern void Norep(const char *, ...) PRINTF_F(1, 2); @@ -2310,6 +2528,7 @@ extern void nhassert_failed(const char *, const char *, int); extern void set_uasmon(void); extern void float_vs_flight(void); +extern void steed_vs_stealth(void); extern void change_sex(void); extern void livelog_newform(boolean, int, int); extern void polyself(int); @@ -2328,7 +2547,7 @@ extern int domindblast(void); extern int doselfexplode(void); extern void uunstick(void); extern void skinback(boolean); -extern const char *mbodypart(struct monst *, int); +extern const char *mbodypart(struct monst *, int) NONNULLARG1; extern const char *body_part(int); extern int poly_gender(void); extern void ugolemeffects(int, int); @@ -2338,13 +2557,13 @@ extern const char *udeadinside(void); /* ### potion.c ### */ -extern void set_itimeout(long *, long); -extern void incr_itimeout(long *, int); +extern void set_itimeout(long *, long) NONNULLARG1; +extern void incr_itimeout(long *, int) NONNULLARG1; extern void make_confused(long, boolean); extern void make_stunned(long, boolean); -extern void make_sick(long, const char *, boolean, int); -extern void make_slimed(long, const char *); -extern void make_stoned(long, const char *, int, const char *); +extern void make_sick(long, const char *, boolean, int) NO_NNARGS; +extern void make_slimed(long, const char *) NO_NNARGS; +extern void make_stoned(long, const char *, int, const char *) NO_NNARGS; extern void make_vomiting(long, boolean); extern void make_blinded(long, boolean); extern void toggle_blindness(void); @@ -2355,19 +2574,19 @@ extern void make_glib(int); extern void make_withering(long, boolean); extern void self_invis_message(void); extern int dodrink(void); -extern int dopotion(struct obj *); -extern int peffects(struct obj *); +extern int dopotion(struct obj *) NONNULLARG1; +extern int peffects(struct obj *) NONNULLARG1; extern void healup(int, int, boolean, boolean); -extern void strange_feeling(struct obj *, const char *); -extern void impact_arti_light(struct obj *, boolean, boolean); -extern void potionhit(struct monst *, struct obj *, int); -extern void potionbreathe(struct obj *); +extern void strange_feeling(struct obj *, const char *) NO_NNARGS; +extern void impact_arti_light(struct obj *, boolean, boolean) NONNULLARG1; +extern void potionhit(struct monst *, struct obj *, int) NONNULLARG12; +extern void potionbreathe(struct obj *) NONNULLARG1; extern int dodip(void); extern void ferment(union any *, long); extern int dip_into(void); /* altdip */ -extern void mongrantswish(struct monst **); -extern void djinni_from_bottle(struct obj *); -extern struct monst *split_mon(struct monst *, struct monst *); +extern void mongrantswish(struct monst **) NONNULLARG1; +extern void djinni_from_bottle(struct obj *) NONNULLARG1; +extern struct monst *split_mon(struct monst *, struct monst *) NONNULLARG1; extern const char *bottlename(void); extern void speed_up(long); @@ -2375,6 +2594,7 @@ extern void speed_up(long); extern boolean critically_low_hp(boolean); extern boolean stuck_in_wall(void); +extern void desecrate_altar(boolean, aligntyp); extern int dosacrifice(void); extern boolean can_pray(boolean); extern int dopray(void); @@ -2391,43 +2611,45 @@ extern void altar_wrath(coordxy, coordxy); /* ### priest.c ### */ extern int move_special(struct monst *, boolean, schar, boolean, boolean, - coordxy, coordxy, coordxy, coordxy); -extern char temple_occupied(char *); -extern boolean inhistemple(struct monst *); -extern int pri_move(struct monst *); -extern void priestini(d_level *, struct mkroom *, int, int, boolean); -extern aligntyp mon_aligntyp(struct monst *); -extern char *priestname(struct monst *, int, char *); -extern boolean p_coaligned(struct monst *); + coordxy, coordxy, coordxy, coordxy) NONNULLARG1; +extern char temple_occupied(char *) NONNULLARG1; +extern boolean inhistemple(struct monst *) NO_NNARGS; +extern int pri_move(struct monst *) NONNULLARG1; +extern void priestini(d_level *, struct mkroom *, int, int, boolean) NONNULLARG12; +extern aligntyp mon_aligntyp(struct monst *) NONNULLARG1; +extern char *priestname(struct monst *, int, boolean, char *) NONNULLARG1; +extern boolean p_coaligned(struct monst *) NONNULLARG1; extern struct monst *findpriest(char); extern void intemple(int); -extern void scary_ghost(struct monst *); -extern void forget_temple_entry(struct monst *); -extern void priest_talk(struct monst *); +extern void scary_ghost(struct monst *) NONNULLARG1; +extern void forget_temple_entry(struct monst *) NONNULLARG1; +extern void priest_talk(struct monst *) NONNULLARG1; extern struct monst *mk_roamer(struct permonst *, aligntyp, coordxy, coordxy, - boolean); -extern void reset_hostility(struct monst *); -extern boolean in_your_sanctuary(struct monst *, coordxy, coordxy); -extern void ghod_hitsu(struct monst *); + boolean) NO_NNARGS; +extern void reset_hostility(struct monst *) NONNULLARG1; +extern boolean in_your_sanctuary(struct monst *, coordxy, coordxy) NO_NNARGS; +extern void ghod_hitsu(struct monst *) NONNULLARG1; extern void angry_priest(void); extern void clearpriests(void); -extern void restpriest(struct monst *, boolean); -extern void newepri(struct monst *); -extern void free_epri(struct monst *); +extern void restpriest(struct monst *, boolean) NONNULLARG1; +extern void newepri(struct monst *) NONNULLARG1; +extern void free_epri(struct monst *) NONNULLARG1; /* ### quest.c ### */ extern void onquest(void); extern void nemdead(void); extern void leaddead(void); -extern void artitouch(struct obj *); +extern void artitouch(struct obj *) NONNULLARG1; extern boolean ok_to_quest(void); -extern void leader_speaks(struct monst *); +extern void leader_speaks(struct monst *) NONNULLARG1; extern void nemesis_speaks(void); -extern void quest_chat(struct monst *); -extern void quest_talk(struct monst *); -extern void quest_stat_check(struct monst *); -extern void leader_sees_qarti(struct obj *); +extern void nemesis_stinks(coordxy, coordxy); +extern void quest_chat(struct monst *) NONNULLARG1; +extern void quest_talk(struct monst *) NONNULLARG1; +extern void quest_stat_check(struct monst *) NONNULLARG1; +extern void finish_quest(struct obj *) NO_NNARGS; +extern void leader_sees_qarti(struct obj *) NO_NNARGS; /* ### questpgr.c ### */ @@ -2435,7 +2657,7 @@ extern void load_qtlist(void); extern void unload_qtlist(void); extern short quest_info(int); extern const char *ldrname(void); -extern boolean is_quest_artifact(struct obj *); +extern boolean is_quest_artifact(struct obj *) NONNULLARG1; extern struct obj *find_quest_artifact(unsigned); extern void convert_line(const char *,char *); extern int stinky_nemesis(struct monst *); @@ -2443,6 +2665,7 @@ extern void com_pager(const char *); extern void qt_pager(const char *); extern struct permonst *qt_montype(void); extern void deliver_splev_message(void); + /* ### random.c ### */ #if defined(RANDOM) && !defined(__GO32__) /* djgpp has its own random */ @@ -2456,76 +2679,102 @@ extern long random(void); /* ### read.c ### */ -extern void learnscroll(struct obj *); -extern char *tshirt_text(struct obj *, char *); -extern char *hawaiian_motif(struct obj *, char *); -extern char *apron_text(struct obj *, char *); -extern const char *candy_wrapper_text(struct obj *); -extern void assign_candy_wrapper(struct obj *); +extern void learnscroll(struct obj *) NONNULLARG1; +extern char *tshirt_text(struct obj *, char *) NONNULLARG12; +extern char *hawaiian_motif(struct obj *, char *) NONNULLARG12; +extern char *apron_text(struct obj *, char *) NONNULLARG12; +extern const char *candy_wrapper_text(struct obj *) NONNULLARG1; +extern void assign_candy_wrapper(struct obj *) NONNULLARG1; extern int doread(void); -extern int charge_ok(struct obj *); -extern void recharge(struct obj *, int); +extern int charge_ok(struct obj *) NO_NNARGS; +extern void recharge(struct obj *, int) NONNULLARG1; extern boolean valid_cloud_pos(coordxy, coordxy); -extern int seffects(struct obj *); +extern int seffects(struct obj *) NONNULLARG1; extern void drop_boulder_on_player(boolean, boolean, boolean, boolean); extern boolean drop_boulder_on_monster(coordxy, coordxy, boolean, boolean); -extern void wand_explode(struct obj *, int); -extern void litroom(boolean, struct obj *); +extern void wand_explode(struct obj *, int) NONNULLARG1; +extern void litroom(boolean, struct obj *) NO_NNARGS; extern void do_genocide(int); -extern void punish(struct obj *); +extern void punish(struct obj *) NO_NNARGS; extern void unpunish(void); -extern xint8 do_stinking_cloud(struct obj *, boolean); -extern boolean cant_revive(int *, boolean, struct obj *); +extern xint8 do_stinking_cloud(struct obj *, boolean) NONNULLARG1; +extern boolean cant_revive(int *, boolean, struct obj *) NO_NNARGS; extern struct monst *create_particular(void); -extern int find_boss(boolean *); +extern int find_boss(boolean *) NO_NNARGS; extern boolean can_magic_map(void); /* ### rect.c ### */ extern void init_rect(void); extern void free_rect(void); -extern NhRect *get_rect(NhRect *); +extern NhRect *get_rect(NhRect *) NONNULLARG1; extern NhRect *rnd_rect(void); -extern void rect_bounds(NhRect, NhRect, NhRect *); -extern void remove_rect(NhRect *); -extern void add_rect(NhRect *); -extern void split_rects(NhRect *, NhRect *); +extern void rect_bounds(NhRect, NhRect, NhRect *) NONNULLARG3; +extern void remove_rect(NhRect *) NONNULLARG1; +extern void add_rect(NhRect *) NONNULLARG1; +extern void split_rects(NhRect *, NhRect *) NONNULLARG12; /* ## region.c ### */ -extern boolean inside_region(NhRegion *, int, int); +extern boolean inside_region(NhRegion *, int, int) NO_NNARGS; extern void clear_regions(void); extern void run_regions(void); extern boolean in_out_region(coordxy, coordxy); -extern boolean m_in_out_region(struct monst *, coordxy, coordxy); +extern boolean m_in_out_region(struct monst *, coordxy, coordxy) NONNULLARG1; extern void update_player_regions(void); -extern void update_monster_region(struct monst *); +extern void update_monster_region(struct monst *) NONNULLARG1; +extern int reg_damg(NhRegion *) NONNULLARG1; +extern boolean any_visible_region(void); +extern void visible_region_summary(winid); extern NhRegion *visible_region_at(coordxy, coordxy); -extern void show_region(NhRegion *, coordxy, coordxy); -extern void save_regions(NHFILE *); -extern void rest_regions(NHFILE *); -extern void region_stats(const char *, char *, long *, long *); +extern void show_region(NhRegion *, coordxy, coordxy) NONNULLARG1; +extern void save_regions(NHFILE *) NONNULLARG1; +extern void rest_regions(NHFILE *) NONNULLARG1; +extern void region_stats(const char *, char *, long *, long *) NONNULLPTRS; extern NhRegion *create_gas_cloud(coordxy, coordxy, int, int); +extern NhRegion *create_gas_cloud_selection(struct selectionvar *, int); extern boolean region_danger(void); extern void region_safety(void); +/* ### report.c ### */ + +#ifdef CRASHREPORT +extern boolean submit_web_report(int, const char *, const char *); +extern boolean submit_web_report(int, const char *, const char *); +extern void crashreport_init(int, char *[]); +extern void crashreport_bidshow(void); +extern boolean swr_add_uricoded(const char *, char **, int *, char *); +extern int dobugreport(void); +#endif /* CRASHREPORT */ +# ifndef NO_SIGNAL +extern void panictrace_handler(int); +# endif +#ifdef PANICTRACE +extern const char *get_saved_pline(int); +extern boolean NH_panictrace_libc(void); +extern boolean NH_panictrace_gdb(void); +#if defined(PANICTRACE) && !defined(NO_SIGNAL) +extern void panictrace_setsignals(boolean); +#endif +#endif /* PANICTRACE */ + /* ### restore.c ### */ extern void inven_inuse(boolean); -extern int dorecover(NHFILE *); -extern void restcemetery(NHFILE *, struct cemetery **); -extern void trickery(char *); -extern void getlev(NHFILE *, int, xint8); -extern void get_plname_from_file(NHFILE *, char *); +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 *, boolean) NONNULLARG12; #ifdef SELECTSAVED extern int restore_menu(winid); #endif extern void minit(void); -extern boolean lookup_id_mapping(unsigned, unsigned *); -extern int validate(NHFILE *, const char *); -extern void reset_restpref(void); -extern void set_restpref(const char *); -extern void set_savepref(const char *); +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 *); */ /* ### rip.c ### */ @@ -2547,6 +2796,12 @@ extern int rne(int); extern int rnz(int); extern boolean percent(int); extern boolean rnf(int, int); +extern void init_random(int(*fn)(int)); +extern void reseed_random(int(*fn)(int)); +extern void shuffle_int_array(int *, int) NONNULLARG1; +extern unsigned int coord_hash(int, int, int); +extern unsigned int hash1(int); +extern int int_hash1(int); /* ### role.c ### */ @@ -2558,11 +2813,11 @@ extern int randrole(boolean); extern int randrace(int); extern int randgend(int, int); extern int randalign(int, int); -extern int str2role(const char *); -extern int str2race(const char *); -extern int str2gend(const char *); -extern int str2orientation(const char *); -extern int str2align(const char *); +extern int str2role(const char *) NO_NNARGS; +extern int str2race(const char *) NO_NNARGS; +extern int str2gend(const char *) NO_NNARGS; +extern int str2orientation(const char *) NO_NNARGS; +extern int str2align(const char *) NO_NNARGS; extern boolean ok_role(int, int, int, int); extern int pick_role(int, int, int, int); extern boolean ok_race(int, int, int, int); @@ -2572,17 +2827,17 @@ extern int pick_gend(int, int, int, int); extern boolean ok_align(int, int, int, int); extern int pick_align(int, int, int, int); extern void rigid_role_checks(void); -extern boolean setrolefilter(const char *); +extern boolean setrolefilter(const char *) NONNULLARG1; extern boolean gotrolefilter(void); -extern char *rolefilterstring(char *, int); +extern char *rolefilterstring(char *, int) NONNULLARG1; extern void clearrolefilter(int); -extern char *root_plselection_prompt(char *, int, int, int, int, int); -extern char *build_plselection_prompt(char *, int, int, int, int, int); +extern char *root_plselection_prompt(char *, int, int, int, int, int) NO_NNARGS; +extern char *build_plselection_prompt(char *, int, int, int, int, int) NONNULLARG1; extern void plnamesuffix(void); extern void role_selection_prolog(int, winid); extern void role_menu_extra(int, winid, boolean); extern void role_init(void); -extern const char *Hello(struct monst *); +extern const char *Hello(struct monst *) NO_NNARGS; extern const char *Goodbye(void); extern const struct Race *character_race(short); extern void genl_player_selection(void); @@ -2590,35 +2845,35 @@ extern int genl_player_setup(int); /* ### rumors.c ### */ -extern char *getrumor(int, char *, boolean); -extern char *get_rnd_text(const char *, char *, int, int(*)(int), unsigned); +extern char *getrumor(int, char *, boolean) NONNULLARG2; +extern char *get_rnd_text(const char *, char *, int, int(*)(int), + unsigned) NONNULLARG12; extern void outrumor(int, int); extern void outoracle(boolean, boolean); -extern void save_oracles(NHFILE *); -extern void restore_oracles(NHFILE *); -extern int doconsult(struct monst *); +extern void save_oracles(NHFILE *) NONNULLARG1; +extern void restore_oracles(NHFILE *) NONNULLARG1; +extern int doconsult(struct monst *) NO_NNARGS; extern void rumor_check(void); -extern boolean CapitalMon(const char *); +extern boolean CapitalMon(const char *) NO_NNARGS; extern void free_CapMons(void); /* ### save.c ### */ extern int dosave(void); extern int dosave0(void); -extern boolean tricked_fileremoved(NHFILE *, char *); +extern boolean tricked_fileremoved(NHFILE *, char *) NONNULLARG2; #ifdef INSURANCE extern void savestateinlock(void); #endif -extern void savelev(NHFILE *, xint8); -extern genericptr_t mon_to_buffer(struct monst *, int *); -extern boolean close_check(int); -extern void savecemetery(NHFILE *, struct cemetery **); -extern void savefruitchn(NHFILE *); -extern void store_plname_in_file(NHFILE *); +extern void savelev(NHFILE *, xint8) NONNULLARG1; +/* extern genericptr_t mon_to_buffer(struct monst *, int *); */ +extern void savecemetery(NHFILE *, struct cemetery **) NONNULLARG12; +extern void savefruitchn(NHFILE *) NONNULLARG1; +extern void store_plname_in_file(NHFILE *) NONNULLARG1; extern void free_dungeons(void); extern void freedynamicdata(void); -extern void store_savefileinfo(NHFILE *); -extern void store_savefileinfo(NHFILE *); +extern void store_savefileinfo(NHFILE *) NONNULLARG1; +extern void store_savefileinfo(NHFILE *) NONNULLARG1; extern int nhdatatypes_size(void); #if 0 extern void assignlog(char *, char*, int); @@ -2626,14 +2881,51 @@ extern FILE *getlog(NHFILE *); extern void closelog(NHFILE *); #endif +/* ### selvar.c ### */ + +extern struct selectionvar *selection_new(void); +extern void selection_free(struct selectionvar *, boolean) NO_NNARGS; +extern void selection_clear(struct selectionvar *, int) NONNULLARG1; +extern struct selectionvar *selection_clone(struct selectionvar *) NONNULLARG1; +extern void selection_getbounds(struct selectionvar *, NhRect *) NO_NNARGS; +extern void selection_recalc_bounds(struct selectionvar *) NONNULLARG1; +extern coordxy selection_getpoint(coordxy, coordxy, struct selectionvar *) NO_NNARGS; +extern void selection_setpoint(coordxy, coordxy, struct selectionvar *, int); +extern struct selectionvar * selection_not(struct selectionvar *); +extern struct selectionvar *selection_filter_percent(struct selectionvar *, + int); +extern struct selectionvar *selection_filter_mapchar(struct selectionvar *, + xint16, int); +extern int selection_rndcoord(struct selectionvar *, coordxy *, coordxy *, + boolean); +extern void selection_do_grow(struct selectionvar *, int); +extern void set_selection_floodfillchk(int(*)(coordxy, coordxy)); +extern void selection_floodfill(struct selectionvar *, coordxy, coordxy, + boolean); +extern void selection_do_ellipse(struct selectionvar *, int, int, int, int, + int); +extern void selection_do_gradient(struct selectionvar *, long, long, long, + long, long, long, long); +extern void selection_do_line(coordxy, coordxy, coordxy, coordxy, + struct selectionvar *); +extern void selection_do_randline(coordxy, coordxy, coordxy, coordxy, + schar, schar, struct selectionvar *); +extern void selection_iterate(struct selectionvar *, select_iter_func, + genericptr_t); +extern boolean selection_is_irregular(struct selectionvar *); +extern char *selection_size_description(struct selectionvar *, char *); +extern struct selectionvar *selection_from_mkroom(struct mkroom *) NO_NNARGS; +extern void selection_force_newsyms(struct selectionvar *) NONNULLARG1; + /* ### sfstruct.c ### */ -extern void newread(NHFILE *, int, int, genericptr_t, unsigned); +extern boolean close_check(int); +/* extern void newread(NHFILE *, int, int, genericptr_t, unsigned); */ extern void bufon(int); extern void bufoff(int); extern void bflush(int); -extern void bwrite(int, const genericptr_t, unsigned); -extern void mread(int, genericptr_t, unsigned); +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) @@ -2642,90 +2934,98 @@ extern void zerocomp_bclose(int); /* ### shk.c ### */ -extern void setpaid(struct monst *); -extern long money2mon(struct monst *, long); -extern void money2u(struct monst *, long); -extern void shkgone(struct monst *); -extern void set_residency(struct monst *, boolean); -extern void replshk(struct monst *, struct monst *); -extern void restshk(struct monst *, boolean); +/* setpaid() has a conditional code block near the end of the + function, where arg1 is tested for NULL, preventing NONNULLARG1 */ +extern void setpaid(struct monst *) NO_NNARGS; +extern long money2mon(struct monst *, long) NONNULLARG1; +extern void money2u(struct monst *, long) NONNULLARG1; +extern void shkgone(struct monst *) NONNULLARG1; +extern void set_residency(struct monst *, boolean) NONNULLARG1; +extern void replshk(struct monst *, struct monst *) NONNULLARG12; +extern void restshk(struct monst *, boolean) NONNULLARG1; extern char inside_shop(coordxy, coordxy); -extern void u_left_shop(char *, boolean); +extern void u_left_shop(char *, boolean) NONNULLARG1; extern void remote_burglary(coordxy, coordxy); extern void u_entered_shop(char *); -extern void pick_pick(struct obj *); -extern boolean same_price(struct obj *, struct obj *); +extern void pick_pick(struct obj *) NONNULLARG1; +extern boolean same_price(struct obj *, struct obj *) NONNULLARG12; extern void shopper_financial_report(void); -extern int inhishop(struct monst *); +extern int inhishop(struct monst *) NONNULLARG1; extern struct monst *shop_keeper(char); -extern struct monst *find_objowner(struct obj *, coordxy x, coordxy y); -extern boolean tended_shop(struct mkroom *); -extern boolean onshopbill(struct obj *, struct monst *, boolean); -extern boolean is_unpaid(struct obj *); -extern void delete_contents(struct obj *); -extern void obfree(struct obj *, struct obj *); -extern void make_happy_shk(struct monst *, boolean); +extern struct monst *find_objowner(struct obj *, + coordxy x, coordxy y) NONNULLARG1; +extern boolean tended_shop(struct mkroom *) NONNULLARG1; +extern boolean onshopbill(struct obj *, struct monst *, boolean) NONNULLARG1; +extern boolean is_unpaid(struct obj *) NONNULLARG1; +extern void delete_contents(struct obj *) NONNULLARG1; +extern void obfree(struct obj *, struct obj *) NONNULLARG1; +extern void make_happy_shk(struct monst *, boolean) NONNULLARG1; extern void make_happy_shoppers(boolean); -extern void hot_pursuit(struct monst *); -extern void make_angry_shk(struct monst *, coordxy, coordxy); +extern void hot_pursuit(struct monst *) NONNULLARG1; +extern void make_angry_shk(struct monst *, coordxy, coordxy) NONNULLARG1; extern int dopay(void); extern boolean paybill(int, boolean); extern void finish_paybill(void); extern struct obj *find_oid(unsigned); extern long contained_cost(struct obj *, struct monst *, long, boolean, - boolean); -extern long contained_gold(struct obj *, boolean); -extern void picked_container(struct obj *); + boolean) NONNULLARG12; +extern long contained_gold(struct obj *, boolean) NONNULLARG1; +extern void picked_container(struct obj *) NONNULLARG1; extern void gem_learned(int); -extern void alter_cost(struct obj *, long); -extern long unpaid_cost(struct obj *, boolean); -extern boolean billable(struct monst **, struct obj *, char, boolean); -extern void addtobill(struct obj *, boolean, boolean, boolean); -extern void splitbill(struct obj *, struct obj *); -extern void subfrombill(struct obj *, struct monst *); -extern long stolen_value(struct obj *, coordxy, coordxy, boolean, boolean); -extern void donate_gold(long, struct monst *, boolean); +extern void alter_cost(struct obj *, long) NONNULLARG1; +extern long unpaid_cost(struct obj *, uchar) NONNULLARG1; +extern boolean billable(struct monst **, struct obj *, char, + boolean) NONNULLARG12; +extern void addtobill(struct obj *, boolean, boolean, boolean) NONNULLARG1; +extern void splitbill(struct obj *, struct obj *) NONNULLARG12; +extern void subfrombill(struct obj *, struct monst *) NONNULLARG12; +extern long stolen_value(struct obj *, coordxy, coordxy, + boolean, boolean) NONNULLARG1; +extern void donate_gold(long, struct monst *, boolean) NONNULLARG2; extern void sellobj_state(int); -extern void sellobj(struct obj *, coordxy, coordxy); +extern void sellobj(struct obj *, coordxy, coordxy) NONNULLARG1; extern int doinvbill(int); -extern struct monst *shkcatch(struct obj *, coordxy, coordxy); +extern struct monst *shkcatch(struct obj *, coordxy, coordxy) NONNULLARG1; extern void add_damage(coordxy, coordxy, long); extern void fix_shop_damage(void); -extern int shk_move(struct monst *); -extern void after_shk_move(struct monst *); -extern boolean is_fshk(struct monst *); +extern int shk_move(struct monst *) NONNULLARG1; +extern void after_shk_move(struct monst *) NONNULLARG1; +extern boolean is_fshk(struct monst *) NONNULLARG1; extern void shopdig(int); extern void pay_for_damage(const char *, boolean); extern boolean costly_spot(coordxy, coordxy); -extern boolean costly_adjacent(struct monst *, coordxy, coordxy); +/* costly_adjacent() has checks for null 1st arg, and an early return, + so it cannot be NONNULLARG1 */ +extern boolean costly_adjacent(struct monst *, coordxy, coordxy) NO_NNARGS; extern struct obj *shop_object(coordxy, coordxy); -extern void price_quote(struct obj *); -extern void shk_chat(struct monst *); -extern void check_unpaid_usage(struct obj *, boolean); -extern void check_unpaid(struct obj *); +extern void price_quote(struct obj *) NONNULLARG1; +extern void shk_chat(struct monst *) NONNULLARG1; +extern void check_unpaid_usage(struct obj *, boolean) NONNULLARG1; +extern void check_unpaid(struct obj *) NONNULLARG1; extern void costly_gold(coordxy, coordxy, long, boolean); -extern long get_cost_of_shop_item(struct obj *, int *); -extern int oid_price_adjustment(struct obj *, unsigned); +extern long get_cost_of_shop_item(struct obj *, int *) NONNULLARG1; +extern int oid_price_adjustment(struct obj *, unsigned) NONNULLARG1; extern boolean block_door(coordxy, coordxy); extern boolean block_entry(coordxy, coordxy); -extern char *shk_your(char *, struct obj *); -extern char *Shk_Your(char *, struct obj *); -extern void globby_bill_fixup(struct obj *, struct obj *); -extern void globby_donation(struct obj *, struct obj *); -extern void credit_report(struct monst *shkp, int idx, boolean silent); +extern char *shk_your(char *, struct obj *) NONNULLPTRS; +extern char *Shk_Your(char *, struct obj *) NONNULLPTRS; +extern void globby_bill_fixup(struct obj *, struct obj *) NONNULLARG12; +/*extern void globby_donation(struct obj *, struct obj *); */ +extern void credit_report(struct monst *shkp, int idx, + boolean silent) NONNULLARG1; +extern void use_unpaid_trapobj(struct obj *, coordxy, coordxy) NONNULLARG1; /* ### shknam.c ### */ -extern void neweshk(struct monst *); -extern void free_eshk(struct monst *); -extern void stock_room(int, struct mkroom *); -extern boolean saleable(struct monst *, struct obj *); +extern void neweshk(struct monst *) NONNULLARG1; +extern void free_eshk(struct monst *) NONNULLARG1; +extern void stock_room(int, struct mkroom *) NONNULLARG2; +extern boolean saleable(struct monst *, struct obj *) NONNULLARG12; extern int get_shop_item(int); -extern char *Shknam(struct monst *); -extern char *shkname(struct monst *); -extern boolean shkname_is_pname(struct monst *); -extern boolean is_izchak(struct monst *, boolean); -extern const struct shclass *get_shtype(int); +extern char *Shknam(struct monst *) NONNULLARG1; +extern char *shkname(struct monst *) NONNULLARG1; +extern boolean shkname_is_pname(struct monst *) NONNULLARG1; +extern boolean is_izchak(struct monst *, boolean) NONNULLARG1; /* ### sit.c ### */ @@ -2737,20 +3037,21 @@ extern int attrcurse(void); /* ### sounds.c ### */ extern void dosounds(void); -extern const char *growl_sound(struct monst *); -extern void growl(struct monst *); -extern void yelp(struct monst *); -extern void whimper(struct monst *); -extern void beg(struct monst *); -extern const char *maybe_gasp(struct monst *); -extern const char *cry_sound(struct monst *); +extern const char *growl_sound(struct monst *) NONNULLARG1; +extern void growl(struct monst *) NONNULLARG1; +extern void yelp(struct monst *) NONNULLARG1; +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 getroomtype(coordxy, coordxy); extern int tiphat(void); #ifdef USER_SOUNDS -extern int add_sound_mapping(const char *); -extern void play_sound_for_message(const char *); -extern void maybe_play_sound(const char *); +extern int add_sound_mapping(const char *) NONNULLARG1; +extern void play_sound_for_message(const char *) NONNULLARG1; +extern void maybe_play_sound(const char *) NONNULLARG1; extern void release_sound_mappings(void); #if defined(WIN32) || defined(QT_GRAPHICS) extern void play_usersound(const char *, int); @@ -2761,88 +3062,67 @@ extern void play_usersound_via_idx(int, int); #endif /* USER SOUNDS */ extern void assign_soundlib(int); extern void activate_chosen_soundlib(void); -extern void get_soundlib_name(char *dest, int maxlen); +extern void get_soundlib_name(char *dest, int maxlen) NONNULLARG1; #ifdef SND_SOUNDEFFECTS_AUTOMAP extern char *get_sound_effect_filename(int32_t seidint, char *buf, size_t bufsz, int32_t); #endif -extern char *base_soundname_to_filename(char *, char *, size_t, int32_t); -extern void set_voice(struct monst *, int32_t, int32_t, int32_t); -extern void sound_speak(const char *); +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 ### */ #if !defined(CROSSCOMPILE) || defined(CROSSCOMPILE_TARGET) +extern boolean match_maptyps(xint16, xint16); extern void create_des_coder(void); extern void reset_xystart_size(void); -extern struct mapfragment *mapfrag_fromstr(char *); -extern void mapfrag_free(struct mapfragment **); -extern schar mapfrag_get(struct mapfragment *, int, int); -extern boolean mapfrag_canmatch(struct mapfragment *); -extern const char * mapfrag_error(struct mapfragment *); -extern boolean mapfrag_match(struct mapfragment *, int, int); +extern struct mapfragment *mapfrag_fromstr(char *) NONNULLARG1; +extern void mapfrag_free(struct mapfragment **) NO_NNARGS; +extern schar mapfrag_get(struct mapfragment *, int, int) NONNULLARG1; +extern boolean mapfrag_canmatch(struct mapfragment *) NONNULLARG1; +extern const char * mapfrag_error(struct mapfragment *) NO_NNARGS; +extern boolean mapfrag_match(struct mapfragment *, int, int) NONNULLARG1; extern void lvlfill_maze_grid(int, int, int, int, schar); extern void lvlfill_solid(schar, schar); extern void flip_level(int, boolean); extern void flip_level_rnd(int, boolean); -extern boolean check_room(coordxy *, coordxy *, coordxy *, coordxy *, 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); -extern void fill_special_room(struct mkroom *); +extern boolean dig_corridor(coord *, coord *, boolean, schar, schar) NONNULLARG12; +extern void fill_special_room(struct mkroom *) NO_NNARGS; extern void wallify_map(coordxy, coordxy, coordxy, coordxy); -extern int find_montype(lua_State *, const char *, int *); -extern boolean load_special(const char *); -extern coordxy selection_getpoint(coordxy, coordxy, struct selectionvar *); -extern struct selectionvar *selection_new(void); -extern void selection_free(struct selectionvar *, boolean); -extern void selection_clear(struct selectionvar *, int); -extern struct selectionvar *selection_clone(struct selectionvar *); -extern void selection_getbounds(struct selectionvar *, NhRect *); -extern void selection_recalc_bounds(struct selectionvar *); -extern void set_selection_floodfillchk(int(*)(coordxy, coordxy)); -extern void selection_floodfill(struct selectionvar *, coordxy, coordxy, - boolean); -extern boolean pm_good_location(coordxy, coordxy, struct permonst *); +extern int find_montype(lua_State *, const char *, int *) NONNULLARG2; +extern boolean load_special(const char *) NONNULLARG1; +extern coordxy random_wdir(void); +extern boolean pm_good_location(coordxy, coordxy, struct permonst *) NONNULLARG3; extern void get_location_coord(coordxy *, coordxy *, int, struct mkroom *, - long); -extern void selection_setpoint(coordxy, coordxy, struct selectionvar *, int); -extern struct selectionvar * selection_not(struct selectionvar *); -extern struct selectionvar *selection_filter_percent(struct selectionvar *, - int); -extern int selection_rndcoord(struct selectionvar *, coordxy *, coordxy *, - boolean); -extern void selection_do_grow(struct selectionvar *, int); -extern void selection_do_line(coordxy, coordxy, coordxy, coordxy, - struct selectionvar *); -extern void selection_do_randline(coordxy, coordxy, coordxy, coordxy, - schar, schar, struct selectionvar *); -extern struct selectionvar *selection_filter_mapchar(struct selectionvar *, - xint16, int); + long) NONNULLARG12; extern void set_floodfillchk_match_under(coordxy); -extern void selection_do_ellipse(struct selectionvar *, int, int, int, int, - int); -extern void selection_do_gradient(struct selectionvar *, long, long, long, - long, long, long, long); -extern int lspo_reset_level(lua_State *); -extern int lspo_finalize_level(lua_State *); -extern boolean get_coord(lua_State *, int, lua_Integer *, lua_Integer *); -extern void cvt_to_abscoord(coordxy *, coordxy *); -extern void cvt_to_relcoord(coordxy *, coordxy *); -extern int nhl_abs_coord(lua_State *); -extern struct selectionvar *selection_from_mkroom(struct mkroom *); +extern int lspo_reset_level(lua_State *) NO_NNARGS; /* wiz_load_splua NULL */ +/* lspo_finalize_level() has tests for whether arg1 L is null, and chooses + code paths to follow based on that. Also preventing NONNULLARG1 is it + being called from wiz_load_splua() with a NULL arg. + Side note: The parameter is also marked as UNUSED, but apparently it is */ +extern int lspo_finalize_level(lua_State *) NO_NNARGS; +extern boolean get_coord(lua_State *, int, lua_Integer *, lua_Integer *) NONNULLPTRS; +extern void cvt_to_abscoord(coordxy *, coordxy *) NONNULLPTRS; +extern void cvt_to_relcoord(coordxy *, coordxy *) NONNULLPTRS; +extern int nhl_abs_coord(lua_State *) NONNULLARG1; extern void update_croom(void); extern const char *get_trapname_bytype(int); -extern void l_register_des(lua_State *); +extern void l_register_des(lua_State *) NONNULLARG1; extern boolean in_splev_map(coordxy, coordxy); #endif /* !CROSSCOMPILE || CROSSCOMPILE_TARGET */ /* ### spell.c ### */ -extern void book_cursed(struct obj *); -extern int study_book(struct obj *); -extern void book_disappears(struct obj *); -extern void book_substitution(struct obj *, struct obj *); +extern void book_cursed(struct obj *) NONNULLARG1; +extern int study_book(struct obj *) NONNULLARG1; +extern void book_disappears(struct obj *) NONNULLARG1; +extern void book_substitution(struct obj *, struct obj *) NONNULLARG12; extern void age_spells(void); extern int dowizcast(void); extern int docast(void); @@ -2852,42 +3132,67 @@ extern int spelleffects(int, boolean, boolean); extern int tport_spell(int); extern void losespells(void); extern int dovspell(void); -extern void initialspell(struct obj *); +extern void initialspell(struct obj *) NONNULLARG1; extern int known_spell(short); extern int spell_idx(short); extern char force_learn_spell(short); extern int num_spells(void); +extern void skill_based_spellbook_id(void); + +/* ### stairs.c ### */ + +extern void stairway_add(coordxy, coordxy, + boolean, boolean, d_level *) NONNULLPTRS; +extern void stairway_free_all(void); +extern stairway *stairway_at(coordxy, coordxy); +extern stairway *stairway_find(d_level *) NONNULLARG1; +extern stairway *stairway_find_from(d_level *, boolean) NONNULLARG1; +extern stairway *stairway_find_dir(boolean); +extern stairway *stairway_find_type_dir(boolean, boolean); +extern stairway *stairway_find_special_dir(boolean); +extern void u_on_sstairs(int); +extern void u_on_upstairs(void); +extern void u_on_dnstairs(void); +extern boolean On_stairs(coordxy, coordxy); +extern boolean On_ladder(coordxy, coordxy); +extern boolean On_stairs_up(coordxy, coordxy); +extern boolean On_stairs_dn(coordxy, coordxy); +extern boolean known_branch_stairs(stairway *); +extern char *stairs_description(stairway *, char *, boolean) NONNULLARG1; /* ### steal.c ### */ extern long somegold(long); -extern void stealgold(struct monst *); +extern void stealgold(struct monst *) NONNULLARG1; extern void thiefdead(void); extern boolean unresponsive(void); -extern void remove_worn_item(struct obj *, boolean); -extern int steal(struct monst *, char *); -extern int mpickobj(struct monst *, struct obj *); -extern void remove_outer_gear(struct obj *target); -extern void stealamulet(struct monst *); -extern void maybe_absorb_item(struct monst *, struct obj *, int, int); -extern void mdrop_obj(struct monst *, struct obj *, boolean); -extern void mdrop_special_objs(struct monst *); -extern void relobj(struct monst *, int, boolean); -extern struct obj *findgold(struct obj *, boolean); +extern void remove_worn_item(struct obj *, boolean) NONNULLARG1; +extern int steal(struct monst *, char *) NONNULLARG1; +/* mpickobj() contains a test for NULL arg2 obj and a code path + that leads to impossible(). Prevents NONNULLARG12. */ +extern int mpickobj(struct monst *, struct obj *) NONNULLARG1; +extern void remove_outer_gear(struct monst *, struct obj *) NONNULLARG12; +extern void stealamulet(struct monst *) NONNULLARG1; +extern void maybe_absorb_item(struct monst *, struct obj *, int, int) NONNULLARG12; +extern void mdrop_obj(struct monst *, struct obj *, boolean) NONNULLARG12; +extern void mdrop_special_objs(struct monst *) NONNULLARG1; +extern void relobj(struct monst *, int, boolean) NONNULLARG1; +extern struct obj *findgold(struct obj *, boolean) NO_NNARGS; /* ### steed.c ### */ extern void rider_cant_reach(void); -extern boolean can_saddle(struct monst *); -extern int use_saddle(struct obj *); -extern void put_saddle_on_mon(struct obj *, struct monst *); -extern boolean can_ride(struct monst *); +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 boolean can_ride(struct monst *) NONNULLARG1; extern int doride(void); -extern boolean mount_steed(struct monst *, boolean); +extern boolean mount_steed(struct monst *, boolean) NO_NNARGS; extern void exercise_steed(void); extern void kick_steed(void); extern void dismount_steed(int); -extern void place_monster(struct monst *, coordxy, coordxy); +extern void place_monster(struct monst *, coordxy, coordxy) NONNULLARG1; +extern void poly_steed(struct monst *, struct permonst *) NONNULLARG12; extern boolean stucksteed(boolean); /* ### symbols.c ### */ @@ -2901,27 +3206,22 @@ extern void init_rogue_symbols(void); extern void init_ov_primary_symbols(void); extern void init_ov_rogue_symbols(void); extern void clear_symsetentry(int, boolean); -extern void update_primary_symset(const struct symparse *, int); -extern void update_rogue_symset(const struct symparse *, int); -extern void update_ov_primary_symset(const struct symparse *, int); -extern void update_ov_rogue_symset(const struct symparse *, int); +extern void update_primary_symset(const struct symparse *, int) NONNULLARG1; +extern void update_rogue_symset(const struct symparse *, int) NONNULLARG1; +extern void update_ov_primary_symset(const struct symparse *, int) NONNULLARG1; +extern void update_ov_rogue_symset(const struct symparse *, int) NONNULLARG1; +extern int parse_sym_line(char *, int) NONNULLARG1; extern nhsym get_othersym(int, int); extern boolean symset_is_compatible(enum symset_handling_types, unsigned long); -extern void set_symhandling(char *handling, int which_set); -extern boolean proc_symset_line(char *); +extern void set_symhandling(char *handling, int which_set) NONNULLARG1; +extern boolean proc_symset_line(char *) NONNULLARG1; extern int do_symset(void); -extern int load_symset(const char *, int); +extern int load_symset(const char *, int) NONNULLARG1; extern void free_symsets(void); -extern const struct symparse *match_sym(char *); +extern const struct symparse *match_sym(char *) NONNULLARG1; extern void savedsym_free(void); -extern void savedsym_strbuf(strbuf_t *); -extern boolean parsesymbols(char *, int); -#ifdef ENHANCED_SYMBOLS -extern struct customization_detail *find_matching_symset_customiz( - const char *symset_name, int custtype, - enum graphics_sets which_set); -extern void apply_customizations_to_symset(enum graphics_sets which_set); -#endif +extern void savedsym_strbuf(strbuf_t *) NONNULLARG1; +extern boolean parsesymbols(char *, int) NONNULLARG1; /* ### sys.c ### */ @@ -2931,71 +3231,74 @@ extern void sysopt_seduce_set(int); /* ### teleport.c ### */ -extern boolean noteleport_level(struct monst *); -extern boolean goodpos(coordxy, coordxy, struct monst *, mmflags_nht); -extern boolean enexto(coord *, coordxy, coordxy, struct permonst *); +extern boolean noteleport_level(struct monst *) NONNULLARG1; +/* rloc_engr() passes NULL monst arg to goodpos()*/ +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); + mmflags_nht) NONNULLARG1; extern void teleds(coordxy, coordxy, int); extern int collect_coords(coord *, coordxy, coordxy, int, unsigned, - boolean (*)(coordxy, coordxy)); + boolean (*)(coordxy, coordxy)) NONNULLARG1; extern boolean safe_teleds(int); -extern boolean teleport_pet(struct monst *, boolean); +extern boolean teleport_pet(struct monst *, boolean) NONNULLARG1; extern void tele(void); -extern void scrolltele(struct obj *); +extern void scrolltele(struct obj *) NO_NNARGS; extern int dotelecmd(void); extern int dotele(boolean); extern void level_tele(void); -extern void domagicportal(struct trap *); -extern void tele_trap(struct trap *); -extern void level_tele_trap(struct trap *, unsigned); -extern void rloc_to(struct monst *, coordxy, coordxy); -extern void rloc_to_flag(struct monst *, coordxy, coordxy, unsigned); -extern boolean rloc(struct monst *, unsigned); -extern boolean tele_restrict(struct monst *); -extern void mtele_trap(struct monst *, struct trap *, int); -extern int mlevel_tele_trap(struct monst *, struct trap *, boolean, int); -extern boolean rloco(struct obj *); +extern void domagicportal(struct trap *) NONNULLARG1; +extern void tele_trap(struct trap *) NONNULLARG1; +extern void level_tele_trap(struct trap *, unsigned) NONNULLARG1; +extern void rloc_to(struct monst *, coordxy, coordxy) NONNULLARG1; +extern void rloc_to_flag(struct monst *, coordxy, coordxy, + unsigned) NONNULLARG1; +extern boolean rloc(struct monst *, unsigned) NONNULLARG1; +extern boolean control_mon_tele(struct monst *, coord *cc, unsigned, + boolean) NONNULLARG1; +extern boolean tele_restrict(struct monst *) NONNULLARG1; +extern void mtele_trap(struct monst *, struct trap *, int) NONNULLARG12; +extern int mlevel_tele_trap(struct monst *, struct trap *, + boolean, int) NONNULLARG1; +extern boolean rloco(struct obj *) NONNULLARG1; extern int random_teleport_level(void); -extern boolean u_teleport_mon(struct monst *, boolean); - -/* ### tile.c ### */ - -#ifdef TILES_IN_GLYPHMAP -extern void substitute_tiles(d_level *); -#endif +extern boolean u_teleport_mon(struct monst *, boolean) NONNULLARG1; /* ### timeout.c ### */ extern boolean can_slime_with_unchanging(void); -extern const char *property_by_index(int, int *); +extern const char *property_by_index(int, int *) NO_NNARGS; extern void burn_away_slime(void); extern void nh_timeout(void); extern void fall_asleep(int, boolean); -extern void attach_egg_hatch_timeout(struct obj *, long); -extern void attach_fig_transform_timeout(struct obj *); -extern void kill_egg(struct obj *); -extern void hatch_egg(union any *, long); +extern void attach_egg_hatch_timeout(struct obj *, long) NONNULLARG1; +extern void attach_fig_transform_timeout(struct obj *) NONNULLARG1; +extern void kill_egg(struct obj *) NONNULLARG1; +extern void hatch_egg(union any *, long) NONNULLARG1; extern void learn_egg_type(int); -extern void burn_object(union any *, long); -extern void begin_burn(struct obj *, boolean); -extern void end_burn(struct obj *, boolean); +extern void burn_object(union any *, long) NONNULLARG1; +extern void begin_burn(struct obj *, boolean) NONNULLARG1; +extern void end_burn(struct obj *, boolean) NONNULLARG1; extern void do_storms(void); -extern boolean start_timer(long, short, short, union any *); -extern long stop_timer(short, union any *); -extern long peek_timer(short, union any *); +extern boolean start_timer(long, short, short, union any *) NONNULLARG4; +extern long stop_timer(short, union any *) NONNULLARG2; +extern long peek_timer(short, union any *) NONNULLARG2; extern void run_timers(void); -extern void obj_move_timers(struct obj *, struct obj *); -extern void obj_split_timers(struct obj *, struct obj *); -extern void obj_stop_timers(struct obj *); -extern boolean obj_has_timer(struct obj *, short); +extern void obj_move_timers(struct obj *, struct obj *) NONNULLARG12; +extern void obj_split_timers(struct obj *, struct obj *) NONNULLARG12; +extern void obj_stop_timers(struct obj *) NONNULLARG1; +extern boolean obj_has_timer(struct obj *, short) NONNULLARG1; extern void spot_stop_timers(coordxy, coordxy, short); extern long spot_time_expires(coordxy, coordxy, short); extern long spot_time_left(coordxy, coordxy, short); -extern boolean obj_is_local(struct obj *); -extern void save_timers(NHFILE *, int); -extern void restore_timers(NHFILE *, int, long); -extern void timer_stats(const char *, char *, long *, long *); +extern boolean obj_is_local(struct obj *) NONNULLARG1; +extern void save_timers(NHFILE *, int) NONNULLARG1; +extern void restore_timers(NHFILE *, int, long) NONNULLARG1; +extern void timer_stats(const char *, char *, long *, long *) NONNULLPTRS; extern void relink_timers(boolean); extern int wiz_timeout_queue(void); extern void timer_sanity_check(void); @@ -3003,88 +3306,103 @@ extern void timer_sanity_check(void); /* ### topten.c ### */ extern long encodeconduct(void); -extern void formatkiller(char *, unsigned, int, boolean); -extern int observable_depth(d_level *); +extern void formatkiller(char *, unsigned, int, boolean) NONNULLARG1; +extern int observable_depth(d_level *) NONNULLARG1; extern void topten(int, time_t); extern void prscore(int, char **); extern struct toptenentry *get_rnd_toptenentry(void); extern const char *tt_name(void); -extern struct obj *tt_oname(struct obj *); +extern struct obj *tt_oname(struct obj *) NO_NNARGS; +extern int tt_doppel(struct monst *) NONNULLARG1; /* ### track.c ### */ extern void initrack(void); extern void settrack(void); extern coord *gettrack(coordxy, coordxy); +extern boolean hastrack(coordxy, coordxy); +extern void save_track(NHFILE *) NONNULLARG1; +extern void rest_track(NHFILE *) NONNULLARG1; /* ### trap.c ### */ -extern boolean burnarmor(struct monst *); -extern int erode_obj(struct obj *, const char *, int, int); +extern boolean burnarmor(struct monst *) NO_NNARGS; +extern int erode_obj(struct obj *, const char *, int, int) NO_NNARGS; extern void mk_trap_statue(coordxy, coordxy); -extern boolean grease_protect(struct obj *, const char *, struct monst *); +extern boolean grease_protect(struct obj *, const char *, + struct monst *) NONNULLARG1; extern struct trap *maketrap(coordxy, coordxy, int); -extern void set_trap_ammo(struct trap *, struct obj *); -extern d_level *clamp_hole_destination(d_level *); +extern void set_trap_ammo(struct trap *, struct obj *) NONNULLARG1; +extern d_level *clamp_hole_destination(d_level *) NONNULLARG1; extern void fall_through(boolean, unsigned); extern struct monst *animate_statue(struct obj *, coordxy, coordxy, - int, int *); + int, int *) NONNULLARG1; extern struct monst *activate_statue_trap(struct trap *, coordxy, coordxy, - boolean); -extern enum trap_immunities immune_to_trap(struct monst *, int); + boolean) NONNULLARG1; +extern int immune_to_trap(struct monst *, unsigned) NO_NNARGS; /* revisit */ extern void set_utrap(unsigned, unsigned); extern void reset_utrap(boolean); -extern boolean strip_cold_resistance(struct monst *mtmp); -extern void dotrap(struct trap *, unsigned); -extern char *trapnote(struct trap *, boolean); -extern void seetrap(struct trap *); -extern void feeltrap(struct trap *); -extern int mintrap(struct monst *, unsigned); -extern void instapetrify(const char *); -extern void minstapetrify(struct monst *, boolean); -extern void selftouch(const char *); -extern void mselftouch(struct monst *, const char *, boolean); +extern boolean strip_cold_resistance(struct monst *mtmp) NONNULLARG1; +extern boolean m_harmless_trap(struct monst *, struct trap *) NONNULLPTRS; +extern void dotrap(struct trap *, unsigned) NONNULLARG1; +extern char *trapnote(struct trap *, boolean) NONNULLARG1; +extern void seetrap(struct trap *) NONNULLARG1; +extern void feeltrap(struct trap *) NONNULLARG1; +extern int mintrap(struct monst *, unsigned) NONNULLARG1; +extern void instapetrify(const char *) NO_NNARGS; +extern void minstapetrify(struct monst *, boolean) NONNULLARG1; +extern void selftouch(const char *) NONNULLARG1; +extern void mselftouch(struct monst *, const char *, boolean) NONNULLARG1; extern void float_up(void); extern void fill_pit(coordxy, coordxy); extern int float_down(long, long); extern void climb_pit(void); -extern boolean fire_damage(struct obj *, boolean, coordxy, coordxy); -extern int fire_damage_chain(struct obj *, boolean, boolean, coordxy, coordxy); -extern boolean lava_damage(struct obj *, coordxy, coordxy); -extern void acid_damage(struct obj *); -extern int water_damage(struct obj *, const char *, boolean); -extern void water_damage_chain(struct obj *, boolean, int, boolean); -extern boolean rnd_nextto_goodpos(coordxy *, coordxy *, struct monst *); -extern void back_on_ground(int); +extern boolean fire_damage(struct obj *, boolean, + coordxy, coordxy) NONNULLARG1; +extern int fire_damage_chain(struct obj *, boolean, boolean, + coordxy, coordxy) NO_NNARGS; +extern boolean lava_damage(struct obj *, coordxy, coordxy) NONNULLARG1; +/* acid_damage() has a test for NULL arg and early return if so, + preventing NONNULLARG1 */ +extern void acid_damage(struct obj *) NO_NNARGS; +extern int water_damage(struct obj *, const char *, boolean) NO_NNARGS; +extern void water_damage_chain(struct obj *, boolean, int, boolean) NO_NNARGS; +extern boolean rnd_nextto_goodpos(coordxy *, coordxy *, + struct monst *) NONNULLPTRS; +extern void back_on_ground(boolean); +extern void rescued_from_terrain(int); extern boolean drown(void); extern void drain_en(int, boolean); extern int dountrap(void); extern int could_untrap(boolean, boolean); -extern int untrap(boolean, coordxy, coordxy, struct obj *); -extern boolean openholdingtrap(struct monst *, boolean *); -extern boolean closeholdingtrap(struct monst *, boolean *); -extern boolean openfallingtrap(struct monst *, boolean, boolean *); -extern boolean chest_trap(struct obj *, int, boolean); -extern void deltrap(struct trap *); -extern struct obj *deltrap_with_ammo(struct trap *, int); +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; +extern boolean openfallingtrap(struct monst *, boolean, boolean *) NONNULLARG3; +extern boolean chest_trap(struct obj *, int, boolean) NONNULLARG1; +extern void deltrap(struct trap *) NONNULLARG1; +extern struct obj *deltrap_with_ammo(struct trap *, int) NONNULLARG1; extern int getdoortrap(int, int); -extern xint8 doortrapped(int, int, struct monst *, int, int, xint8); -extern boolean delfloortrap(struct trap *); +/* doortrapped() 3rd argument can be null to mean player */ +extern xint8 doortrapped(int, int, struct monst *, int, int, xint8) NO_NNARGS; +extern boolean delfloortrap(struct trap *) NO_NNARGS; extern struct trap *t_at(coordxy, coordxy); extern int count_traps(int); -extern void b_trapped(const char *, int); +extern void b_trapped(const char *, int) NONNULLARG1; extern boolean unconscious(void); -extern void blow_up_landmine(struct trap *); +extern void blow_up_landmine(struct trap *) NONNULLARG1; extern int launch_obj(short, coordxy, coordxy, coordxy, coordxy, int); extern boolean launch_in_progress(void); extern void force_launch_placement(void); -extern boolean uteetering_at_seen_pit(struct trap *); -extern boolean uescaped_shaft(struct trap *); +extern boolean uteetering_at_seen_pit(struct trap *) NO_NNARGS; +extern boolean uescaped_shaft(struct trap *) NO_NNARGS; extern boolean lava_effects(void); extern void sink_into_lava(void); extern void sokoban_guilt(void); extern const char * trapname(int, boolean); -extern void ignite_items(struct obj *); +extern void ignite_items(struct obj *) NO_NNARGS; extern void trap_ice_effects(coordxy x, coordxy y, boolean ice_is_melting); extern void trap_sanity_check(void); @@ -3094,119 +3412,122 @@ extern void u_init(void); /* ### uhitm.c ### */ -extern void dynamic_multi_reason(struct monst *, const char *, boolean); -extern void erode_armor(struct monst *, int); -extern boolean attack_checks(struct monst *, struct obj *); -extern void check_caitiff(struct monst *); -extern void mon_maybe_unparalyze(struct monst *); -extern int find_roll_to_hit(struct monst *, uchar, struct obj *, int *, int *); -extern boolean force_attack(struct monst *, boolean); -extern boolean do_attack(struct monst *); -extern boolean hmon(struct monst *, struct obj *, int, int); +extern void dynamic_multi_reason(struct monst *, const char *, boolean) NONNULLARG12; +extern void erode_armor(struct monst *, int) NONNULLARG1; +extern boolean attack_checks(struct monst *, struct obj *) NONNULLARG1; +extern void check_caitiff(struct monst *) NONNULLARG1; +extern void mon_maybe_unparalyze(struct monst *) NONNULLARG1; +extern int find_roll_to_hit(struct monst *, uchar, struct obj *, + int *, int *) NONNULLARG145; +extern boolean force_attack(struct monst *, boolean) NONNULLARG1; +extern boolean do_attack(struct monst *) NONNULLARG1; +extern boolean hmon(struct monst *, struct obj *, int, int) NONNULLARG1; extern boolean shade_miss(struct monst *, struct monst *, struct obj *, - boolean, boolean); + boolean, boolean) NONNULLARG12; extern void mhitm_ad_rust(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_corr(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_dcay(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_dren(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_drli(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_fire(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_cold(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_elec(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_acid(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_sgld(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_tlpt(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; +/* gazemm() calls mhitm_ad_blnd with a NULL 4th arg */ extern void mhitm_ad_blnd(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLARG123; extern void mhitm_ad_curs(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_drst(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_drin(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_stck(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_wrap(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_plys(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_slee(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_slim(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_ench(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_slow(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_conf(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_poly(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_pits(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_wthr(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_famn(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_pest(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_deth(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_halu(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_phys(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_ston(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_were(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_heal(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_stun(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_legs(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_dgst(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_samu(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_dise(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_sedu(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_ad_ssex(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); + struct mhitm_data *) NONNULLPTRS; extern void mhitm_adtyping(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); -extern boolean do_stone_u(struct monst *); + struct mhitm_data *) NONNULLPTRS; +extern boolean do_stone_u(struct monst *) NONNULLARG1; extern void do_stone_mon(struct monst *, struct attack *, struct monst *, - struct mhitm_data *); -extern int damageum(struct monst *, struct attack *, int); -extern int explum(struct monst *, struct attack *); -extern const char *attack_blocker(struct monst *); -extern void missum(struct monst *, struct attack *, boolean); -extern boolean m_is_steadfast(struct monst *); + struct mhitm_data *) NONNULLARG134; +extern int damageum(struct monst *, struct attack *, int) NONNULLARG12; +/* domove_fight_empty passes NULL to explum arg1 */ +extern int explum(struct monst *, struct attack *) NONNULLARG2; +extern const char *attack_blocker(struct monst *) NONNULLARG1; +extern void missum(struct monst *, struct attack *, boolean) NONNULLARG12; +extern boolean m_is_steadfast(struct monst *) NONNULLARG1; extern boolean mhitm_knockback(struct monst *, struct monst *,struct attack *, - int *, boolean); + int *, boolean) NONNULLPTRS; extern int passive(struct monst *, struct obj *, boolean, boolean, uchar, - boolean); -extern void passive_obj(struct monst *, struct obj *, struct attack *); -extern boolean item_catches_drain(struct monst *); -extern boolean break_glass_obj(struct obj *); -extern void stumble_onto_mimic(struct monst *); -extern int flash_hits_mon(struct monst *, struct obj *); -extern void light_hits_gremlin(struct monst *, int); + boolean) NONNULLARG1; +extern void passive_obj(struct monst *, struct obj *, struct attack *) NONNULLARG1; +extern boolean item_catches_drain(struct monst *) NONNULLARG1; +extern void that_is_a_mimic(struct monst *, boolean) 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; /* ### unixmain.c ### */ @@ -3216,17 +3537,19 @@ extern void port_help(void); #endif extern void sethanguphandler(void(*)(int)); extern boolean authorize_wizard_mode(void); -extern void append_slash(char *); -extern boolean check_user_string(const char *); +extern boolean authorize_explore_mode(void); +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 ### */ #if defined(UNIX) || defined(__BEOS__) extern void gettty(void); -extern void settty(const char *); +extern void settty(const char *) NO_NNARGS; extern void setftty(void); extern void intron(void); extern void introff(void); @@ -3240,7 +3563,8 @@ extern void tty_utf8graphics_fixup(void); #ifdef UNIX extern void getlock(void); -extern void regularize(char *); +extern void ask_about_panic_save(void); +extern void regularize(char *) NONNULLARG1; #if defined(TIMED_DELAY) && !defined(msleep) && defined(SYSV) extern void msleep(unsigned); #endif @@ -3251,7 +3575,7 @@ extern int dosh(void); extern int child(int); #endif #ifdef PANICTRACE -extern boolean file_exists(const char *); +extern boolean file_exists(const char *) NONNULLARG1; #endif #endif /* UNIX */ @@ -3266,33 +3590,24 @@ extern int hide_privileges(boolean); /* ### utf8map.c ### */ #ifdef ENHANCED_SYMBOLS -extern int glyphrep(const char *); -extern char *mixed_to_utf8(char *buf, size_t bufsz, const char *str, int *); -extern int match_glyph(char *); -extern void dump_all_glyphids(FILE *fp); -extern void fill_glyphid_cache(void); -extern void free_glyphid_cache(void); -extern boolean glyphid_cache_status(void); -extern int glyphrep_to_custom_map_entries(const char *op, int *glyph); +extern char *mixed_to_utf8(char *buf, size_t bufsz, const char *str, + int *) NONNULLARG1; void free_all_glyphmap_u(void); -int add_custom_urep_entry(const char *symset_name, int glyphidx, - uint32 utf32ch, const uint8 *utf8str, long ucolor, - enum graphics_sets which_set); -int set_map_u(glyph_map *gm, uint32 utf32ch, const uint8 *utf8str, - long ucolor); +int set_map_u(glyph_map *gm, uint32 utf32ch, const uint8 *utf8str) NONNULLPTRS; #endif /* ENHANCED_SYMBOLS */ +extern void reset_customsymbols(void); /* ### vault.c ### */ -extern void newegd(struct monst *); -extern void free_egd(struct monst *); -extern boolean grddead(struct monst *); +extern void newegd(struct monst *) NONNULLARG1; +extern void free_egd(struct monst *) NONNULLARG1; +extern boolean grddead(struct monst *) NONNULLARG1; extern struct monst *findgd(void); extern void vault_summon_gd(void); -extern char vault_occupied(char *); -extern void uleftvault(struct monst *); +extern char vault_occupied(char *) NONNULLARG1; +extern void uleftvault(struct monst *); /* NULL leads to impossible() */ extern void invault(void); -extern int gd_move(struct monst *); +extern int gd_move(struct monst *) NONNULLARG1; extern void paygd(boolean); extern long hidden_gold(boolean); extern boolean gd_sound(void); @@ -3300,33 +3615,29 @@ extern void vault_gd_watching(unsigned int); /* ### version.c ### */ -extern char *version_string(char *, size_t bufsz); -extern char *getversionstring(char *, size_t bufsz); +extern char *version_string(char *, size_t bufsz) NONNULL NONNULLARG1; +extern char *getversionstring(char *, size_t bufsz) NONNULL NONNULLARG1; +extern char *status_version(char *, size_t, boolean) NONNULL NONNULLARG1; extern int doversion(void); extern int doextversion(void); #ifdef MICRO extern boolean comp_times(long); #endif extern boolean check_version(struct version_info *, const char *, boolean, - unsigned long); -extern boolean uptodate(NHFILE *, const char *, unsigned long); -extern void store_formatindicator(NHFILE *); -extern void store_version(NHFILE *); -extern unsigned long get_feature_notice_ver(char *); + unsigned long) NONNULLARG1; +extern boolean uptodate(NHFILE *, const char *, unsigned long) NONNULLARG1; +extern void store_formatindicator(NHFILE *) 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); +extern const char *copyright_banner_line(int) NONNULL; extern void early_version_info(boolean); -#ifdef RUNTIME_PORT_ID -extern char *get_port_id(char *); -#endif -#ifdef RUNTIME_PASTEBUF_SUPPORT -extern void port_insert_pastebuf(char *); -#endif +extern void dump_version_info(void); /* ### video.c ### */ #ifdef MSDOS -extern int assign_video(char *); +extern int assign_video(char *) NONNULLARG1; #ifdef NO_TERMS extern void gr_init(void); extern void gr_finish(void); @@ -3334,22 +3645,24 @@ extern void gr_finish(void); extern void tileview(boolean); #endif #ifdef VIDEOSHADES -extern int assign_videoshades(char *); -extern int assign_videocolors(char *); +extern int assign_videoshades(char *) NONNULLARG1; +extern int assign_videocolors(char *) NONNULLARG1; #endif /* ### vision.c ### */ +extern boolean get_viz_clear(int, int); extern void vision_init(void); -extern int does_block(int, int, struct rm *); +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); -extern unsigned howmonseen(struct monst *); +extern unsigned howmonseen(struct monst *) NONNULLARG1; #ifdef VMS @@ -3380,6 +3693,7 @@ extern void chdirx(const char *, boolean); #endif /* CHDIR */ extern void sethanguphandler(void(*)(int)); extern boolean authorize_wizard_mode(void); +extern boolean authorize_explore_mode(void); /* ### vmsmisc.c ### */ @@ -3402,6 +3716,9 @@ ATTRNORETURN extern void error (const char *, ...) PRINTF_F(1, 2) NORETURN; #ifdef TIMED_DELAY extern void msleep(unsigned); #endif +#ifdef SIGWINCH +extern void getwindowsz(void); +#endif #ifdef ENHANCED_SYMBOLS extern void tty_utf8graphics_fixup(void); #endif @@ -3439,22 +3756,24 @@ extern int vms_get_saved_games(const char *, char ***); /* ### weapon.c ### */ -extern const char *weapon_descr(struct obj *); -extern int hitval(struct obj *, struct monst *); -extern int dmgval(struct obj *, struct monst *); -extern int special_dmgval(struct monst *, struct monst *, long, struct obj **); -extern void searmsg(struct monst *, struct monst *, struct obj *, boolean); -extern struct obj *select_rwep(struct monst *); -extern boolean monmightthrowwep(struct obj *); -extern struct obj *select_hwep(struct monst *); -extern void possibly_unwield(struct monst *, boolean); -extern void mwepgone(struct monst *); -extern int mon_wield_item(struct monst *); +extern const char *weapon_descr(struct obj *) NONNULLARG1; +extern int hitval(struct obj *, struct monst *) NONNULLARG12; +extern int dmgval(struct obj *, struct monst *) NONNULLARG12; +extern int special_dmgval(struct monst *, struct monst *, long, + struct obj **) NONNULLARG12; /* 4th can be null */ +extern void searmsg(struct monst *, struct monst *, struct obj *, + boolean) NONNULLARG23; +extern struct obj *select_rwep(struct monst *) NONNULLARG1; +extern boolean monmightthrowwep(struct obj *) NONNULLARG1; +extern struct obj *select_hwep(struct monst *) NONNULLARG1; +extern void possibly_unwield(struct monst *, boolean) NONNULLARG1; +extern int mon_wield_item(struct monst *) NONNULLARG1; +extern void mwepgone(struct monst *) NONNULLARG1; extern int abon(void); extern int dbon(void); -extern void wet_a_towel(struct obj *, int, boolean); -extern void dry_a_towel(struct obj *, int, boolean); -extern char *skill_level_name(int, char *); +extern void wet_a_towel(struct obj *, int, boolean) NONNULLARG1; +extern void dry_a_towel(struct obj *, int, boolean) NONNULLARG1; +extern char *skill_level_name(int, char *) NONNULLARG2; extern const char *skill_name(int); extern boolean can_advance(int, boolean); extern int enhance_weapon_skill(void); @@ -3463,35 +3782,38 @@ extern void use_skill(int, int); extern void add_weapon_skill(int); extern void lose_weapon_skill(int); extern void drain_weapon_skill(int); -extern int weapon_type(struct obj *); +extern int weapon_type(struct obj *) NO_NNARGS; extern int uwep_skill_type(void); -extern int weapon_hit_bonus(struct obj *); -extern int weapon_dam_bonus(struct obj *); -extern void skill_init(const struct def_skill *); +/* find_roll_to_hit() calls weapon_hit_bonus() with a NULL argument, + preventing NONNULLARG1 */ +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; /* ### were.c ### */ -extern void were_change(struct monst *); +extern void were_change(struct monst *) NONNULLARG1; extern int counter_were(int); extern int were_beastie(int); -extern void new_were(struct monst *); -extern int were_summon(struct permonst *, boolean, int *, char *); +extern void new_were(struct monst *) NONNULLARG1; +extern int were_summon(struct permonst *, boolean, int *, char *) NONNULLARG13; extern void you_were(void); extern void you_unwere(boolean); extern void set_ulycn(int); /* ### wield.c ### */ -extern void setuwep(struct obj *); +extern void setuwep(struct obj *) NO_NNARGS; /* NULL:ball.c, do.c */ extern const char *empty_handed(void); -extern void setuqwep(struct obj *); -extern void setuswapwep(struct obj *); -extern boolean cant_wield_corpse(struct obj *); +extern void setuqwep(struct obj *) NO_NNARGS; /* NULL:ball.c, do.c */ +extern void setuswapwep(struct obj *) NO_NNARGS; /* NULL: ball.c, do.c */ +extern boolean cant_wield_corpse(struct obj *) NONNULLARG1; extern int dowield(void); extern int doswapweapon(void); extern int dowieldquiver(void); -extern int doquiver_core(const char *); -extern boolean wield_tool(struct obj *, const char *); +extern int doquiver_core(const char *) NONNULLARG1; +extern boolean wield_tool(struct obj *, const char *) NONNULLARG1; extern int can_twoweapon(void); extern void drop_uswapwep(void); extern int dotwoweapon(void); @@ -3500,35 +3822,39 @@ extern void uswapwepgone(void); extern void uqwepgone(void); extern void set_twoweap(boolean); extern void untwoweapon(void); -extern int chwepon(struct obj *, int); -extern int welded(struct obj *); -extern void weldmsg(struct obj *); -extern void setmnotwielded(struct monst *, struct obj *); -extern boolean mwelded(struct obj *); +extern int chwepon(struct obj *, int) NO_NNARGS; +extern int welded(struct obj *) NO_NNARGS; +extern void weldmsg(struct obj *) NONNULLARG1; +extern boolean mwelded(struct obj *) NO_NNARGS; /* ### windows.c ### */ -extern void choose_windows(const char *); +extern void choose_windows(const char *) NONNULLARG1; #ifdef WINCHAIN -void addto_windowchain(const char *s); +void addto_windowchain(const char *s) NONNULLARG1; void commit_windowchain(void); #endif +#ifdef TTY_GRAPHICS +extern boolean check_tty_wincap(unsigned long); +extern boolean check_tty_wincap2(unsigned long); +#endif extern boolean genl_can_suspend_no(void); extern boolean genl_can_suspend_yes(void); -extern char genl_message_menu(char, int, const char *); -extern void genl_preference_update(const char *); +extern char genl_message_menu(char, int, const char *) NONNULLARG3; +extern void genl_preference_update(const char *) NO_NNARGS; extern char *genl_getmsghistory(boolean); -extern void genl_putmsghistory(const char *, boolean); +extern void genl_putmsghistory(const char *, boolean) NONNULLARG1; #ifdef HANGUPHANDLING extern void nhwindows_hangup(void); #endif extern void genl_status_init(void); extern void genl_status_finish(void); -extern void genl_status_enablefield(int, const char *, const char *, boolean); +extern void genl_status_enablefield(int, const char *, const char *, + boolean) NONNULLPTRS; extern void genl_status_update(int, genericptr_t, int, int, int, - unsigned long *); + unsigned long *) NONNULLARG2; #ifdef DUMPLOG -extern char *dump_fmtstr(const char *, char *, boolean); +extern char *dump_fmtstr(const char *, char *, boolean) NONNULLPTRS; extern void dump_start_screendump(void); extern void dump_end_screendump(void); #ifdef DUMPHTML @@ -3540,18 +3866,32 @@ extern void livelog_dump_url(unsigned int); extern void dump_open_log(time_t); extern void dump_close_log(void); extern void dump_redirect(boolean); -extern void dump_forward_putstr(winid, int, const char *, int); +extern void dump_forward_putstr(winid, int, const char*, int) NONNULLARG3; extern int has_color(int); +#ifdef EXTRAINFO_FN +extern void mk_dgl_extrainfo(void); +#endif extern int glyph2ttychar(int); extern int glyph2symidx(int); extern char *encglyph(int); -extern int decode_glyph(const char *str, int *glyph_ptr); -extern char *decode_mixed(char *, const char *); -extern void genl_putmixed(winid, int, const char *); -extern void genl_display_file(const char *, boolean); +extern int decode_glyph(const char *str, int *glyph_ptr) NONNULLPTRS; +extern char *decode_mixed(char *, const char *) NONNULLARG1; +extern void genl_putmixed(winid, int, const char *) NONNULLARG3; +extern void genl_display_file(const char *, boolean) NONNULLARG1; extern boolean menuitem_invert_test(int, unsigned, boolean); - -/* ### windows.c ### */ +extern const char *mixed_to_glyphinfo(const char *str, + glyph_info *gip) NO_NNARGS; +extern void adjust_menu_promptstyle(winid, color_attr *) NONNULLARG2; +extern int choose_classes_menu(const char *, int, boolean, + char *, char *) NONNULLARG1; +extern void add_menu(winid, const glyph_info *, const ANY_P *, + char, char, int, int, const char *, unsigned int); +extern void add_menu_heading(winid, const char *) NONNULLARG2; +extern void add_menu_str(winid, const char *) NONNULLARG2; +extern int select_menu(winid, int, menu_item **) NONNULLARG3; +extern void getlin(const char *, char *) NONNULLARG2; + +/* ### windsys.c ### */ #ifdef WIN32 extern void nethack_enter_windows(void); @@ -3560,144 +3900,206 @@ extern void nethack_enter_windows(void); /* ### wizard.c ### */ extern void amulet(void); -extern int mon_has_amulet(struct monst *); -extern int mon_has_special(struct monst *); -extern void choose_stairs(coordxy *, coordxy *, boolean); -extern int tactics(struct monst *); -extern boolean has_aggravatables(struct monst *); +extern int mon_has_amulet(struct monst *) NONNULLARG1; +extern int mon_has_special(struct monst *) NONNULLARG1; +extern void choose_stairs(coordxy *, coordxy *, boolean) NONNULLARG12; +extern int tactics(struct monst *) NONNULLARG1; +extern boolean has_aggravatables(struct monst *) NONNULLARG1; extern void aggravate(void); extern void clonewiz(void); extern int pick_nasty(int); -extern int nasty(struct monst *); +extern int nasty(struct monst *) NO_NNARGS; extern void resurrect(void); extern void intervene(void); -extern void wizdead(void); -extern void cuss(struct monst *); +extern void wizdeadorgone(void); +extern void cuss(struct monst *) NONNULLARG1; extern void wizpuzzle_enterchamber(int); extern void wizpuzzle_activate_mechanism(coordxy, coordxy); +/* ### wizcmds.c ### */ + +extern int wiz_custom(void); +extern int wiz_detect(void); +extern int wiz_flip_level(void); +extern int wiz_fuzzer(void); +extern int wiz_genesis(void); +extern int wiz_identify(void); +extern int wiz_intrinsic(void); +extern int wiz_kill(void); +extern int wiz_level_change(void); +extern int wiz_level_tele(void); +extern int wiz_load_lua(void); +extern int wiz_load_splua(void); +extern int wiz_makemap(void); +extern int wiz_map(void); +extern int wiz_migrate_mons(void); +extern int wiz_panic(void); +extern int wiz_polyself(void); +extern int wiz_rumor_check(void); +extern int wiz_show_seenv(void); +extern int wiz_show_stats(void); +extern int wiz_show_vision(void); +extern int wiz_show_wmodes(void); +extern int wiz_smell(void); +extern int wiz_telekinesis(void); +extern int wiz_where(void); +extern int wiz_wish(void); +extern void makemap_remove_mons(void); +extern void wiz_levltyp_legend(void); +extern void wiz_map_levltyp(void); +extern void wizcustom_callback(winid win, int glyphnum, char *id); +#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) +extern int wiz_display_macros(void); +extern int wiz_mon_diff(void); +#endif +extern void sanity_check(void); + /* ### worm.c ### */ extern int get_wormno(void); -extern void initworm(struct monst *, int); -extern void worm_move(struct monst *); -extern void worm_nomove(struct monst *); -extern void wormgone(struct monst *); -extern int wormhitu(struct monst *); -extern void cutworm(struct monst *, coordxy, coordxy, boolean); -extern void see_wsegs(struct monst *); -extern void detect_wsegs(struct monst *, boolean); -extern void save_worm(NHFILE *); -extern void rest_worm(NHFILE *); -extern void place_wsegs(struct monst *, struct monst *); -extern void sanity_check_worm(struct monst *); +extern void initworm(struct monst *, int) NONNULLARG1; +extern void worm_move(struct monst *) NONNULLARG1; +extern void worm_nomove(struct monst *) NONNULLARG1; +extern void wormgone(struct monst *) NONNULLARG1; +extern int wormhitu(struct monst *) NONNULLARG1; +extern void cutworm(struct monst *, coordxy, coordxy, boolean) NONNULLARG1; +extern void see_wsegs(struct monst *) NONNULLARG1; +extern void detect_wsegs(struct monst *, boolean) NONNULLARG1; +extern void save_worm(NHFILE *) NONNULLARG1; +extern void rest_worm(NHFILE *) NONNULLARG1; +extern void place_wsegs(struct monst *, struct monst *) NONNULLARG1; +extern void sanity_check_worm(struct monst *); /* NULL leads to impossible */ extern void wormno_sanity_check(void); -extern void remove_worm(struct monst *); -extern void place_worm_tail_randomly(struct monst *, coordxy, coordxy); -extern int size_wseg(struct monst *); -extern int count_wsegs(struct monst *); -extern boolean worm_known(struct monst *); +extern void remove_worm(struct monst *) NONNULLARG1; +extern void place_worm_tail_randomly(struct monst *, coordxy, coordxy) NONNULLARG1; +extern int size_wseg(struct monst *) NONNULLARG1; +extern int count_wsegs(struct monst *) NONNULLARG1; +extern boolean worm_known(struct monst *) NONNULLARG1; extern boolean worm_cross(int, int, int, int); -extern int wseg_at(struct monst *, int, int); -extern void flip_worm_segs_vertical(struct monst *, int, int); -extern void flip_worm_segs_horizontal(struct monst *, int, int); -extern void place_worm_seg(struct monst *, coordxy, coordxy); +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 place_worm_seg(struct monst *, coordxy, coordxy) NONNULLARG1; +extern void redraw_worm(struct monst *); /* ### worn.c ### */ -extern void setworn(struct obj *, long); -extern void setnotworn(struct obj *); +extern void recalc_telepat_range(void); +extern void setworn(struct obj *, long) NO_NNARGS; /* has tests for obj */ +extern void setnotworn(struct obj *) NO_NNARGS; /* has tests for obj */ extern void allunworn(void); extern struct obj *wearmask_to_obj(long); -extern long wearslot(struct obj *); -extern void mon_set_minvis(struct monst *); -extern void mon_adjust_speed(struct monst *, int, struct obj *); +extern long wearslot(struct obj *) NONNULLARG1; +extern void check_wornmask_slots(void); +extern void mon_set_minvis(struct monst *) NONNULLARG1; +extern void mon_adjust_speed(struct monst *, int, struct obj *) NONNULLARG1; extern void update_mon_extrinsics(struct monst *, struct obj *, boolean, - boolean); -extern int find_mac(struct monst *); -extern void m_dowear(struct monst *, boolean); -extern struct obj *which_armor(struct monst *, long); -extern void mon_break_armor(struct monst *, boolean); -extern void bypass_obj(struct obj *); + boolean) NONNULLARG12; +extern int find_mac(struct monst *) NONNULLARG1; +extern void m_dowear(struct monst *, boolean) NONNULLARG1; +extern struct obj *which_armor(struct monst *, long) NONNULLARG1; +extern void mon_break_armor(struct monst *, boolean) NONNULLARG1; +extern void bypass_obj(struct obj *) NONNULLARG1; extern void clear_bypasses(void); -extern void bypass_objlist(struct obj *, boolean); -extern struct obj *nxt_unbypassed_obj(struct obj *); -extern struct obj *nxt_unbypassed_loot(Loot *, struct obj *); -extern int racial_exception(struct monst *, struct obj *); +/* callers don't check gi.invent before passing to bypass_objlist */ +extern void bypass_objlist(struct obj *, boolean) NO_NNARGS; +extern struct obj *nxt_unbypassed_obj(struct obj *) NO_NNARGS; +extern struct obj *nxt_unbypassed_loot(Loot *, struct obj *) NONNULLARG1; +extern int racial_exception(struct monst *, struct obj *) NONNULLARG12; extern void extract_from_minvent(struct monst *, struct obj *, boolean, - boolean); -extern int armor_bonus(struct obj *); -extern long armor_provides_extrinsic(struct obj *); + boolean) NONNULLARG12; +extern int armor_bonus(struct obj *) NONNULLARG1; +extern long armor_provides_extrinsic(struct obj *) NONNULLARG1; /* ### write.c ### */ extern int ink_cost(short); -extern int dowrite(struct obj *); +extern int dowrite(struct obj *) NONNULLARG1; /* ### zap.c ### */ -extern void learnwand(struct obj *); -extern int bhitm(struct monst *, struct obj *); +extern void learnwand(struct obj *) NONNULLARG1; +extern int bhitm(struct monst *, struct obj *) NONNULLARG12; extern void release_hold(void); -extern void probe_monster(struct monst *); -extern boolean get_obj_location(struct obj *, coordxy *, coordxy *, int); -extern boolean get_mon_location(struct monst *, coordxy *, coordxy *, int); -extern struct monst *get_container_location(struct obj * obj, int *, int *); -extern struct monst *montraits(struct obj *, coord *, boolean); -extern struct monst *revive(struct obj *, boolean); -extern void revive_egg(struct obj *); -extern int unturn_dead(struct monst *); +extern void probe_monster(struct monst *) NONNULLARG1; +extern boolean get_obj_location(struct obj *, coordxy *, coordxy *, + int) NONNULLPTRS; +extern boolean get_mon_location(struct monst *, coordxy *, coordxy *, + int) NONNULLPTRS; +extern struct monst *get_container_location(struct obj *, + int *, int *) NONNULLARG2; +extern struct monst *montraits(struct obj *, coord *, boolean) NONNULLARG12; +extern struct monst *revive(struct obj *, boolean) NONNULLARG1; +extern void revive_egg(struct obj *) NONNULLARG1; +extern int unturn_dead(struct monst *) NONNULLARG1; extern void unturn_you(void); -extern void cancel_item(struct obj *); -extern boolean drain_item(struct obj *, boolean); -extern boolean obj_unpolyable(struct obj *); -extern struct obj *poly_obj(struct obj *, int); -extern boolean obj_resists(struct obj *, int, int); -extern boolean obj_shudders(struct obj *); -extern void do_osshock(struct obj *); -extern int bhito(struct obj *, struct obj *); +extern void cancel_item(struct obj *) NONNULLARG1; +extern void blank_novel(struct obj *) NONNULLARG1; +extern boolean drain_item(struct obj *, boolean) NO_NNARGS; /* tests !obj */ +extern boolean obj_unpolyable(struct obj *) NONNULLARG1; +extern struct obj *poly_obj(struct obj *, int) NONNULLARG1; +extern boolean obj_resists(struct obj *, int, int) NONNULLARG1; +extern boolean obj_shudders(struct obj *) NONNULLARG1; +extern void do_osshock(struct obj *) NONNULLARG1; +extern int bhito(struct obj *, struct obj *) NONNULLARG12; extern int bhitpile(struct obj *, int(*)(struct obj *, struct obj *), - coordxy, coordxy, schar); -extern int zappable(struct obj *); + coordxy, coordxy, schar) NONNULLARG12; +extern int zappable(struct obj *) NONNULLARG1; extern void do_enlightenment_effect(void); -extern void zapnodir(struct obj *); +extern void zapnodir(struct obj *) NONNULLARG1; extern int dozap(void); -extern int zapyourself(struct obj *, boolean); -extern void ubreatheu(struct attack *); -extern int lightdamage(struct obj *, boolean, int); -extern boolean flashburn(long); +extern int zapyourself(struct obj *, boolean) NONNULLARG1; +extern void ubreatheu(struct attack *) NONNULLARG1; +extern int lightdamage(struct obj *, boolean, int) NONNULLARG1; +extern boolean flashburn(long, boolean); extern boolean cancel_monst(struct monst *, struct obj *, boolean, boolean, - boolean); + boolean) NONNULLARG12; extern void zapsetup(void); extern void zapwrapup(void); -extern void weffects(struct obj *); +extern void weffects(struct obj *) NONNULLARG1; extern int spell_damage_bonus(int); -extern const char *exclam(int force); -extern void hit(const char *, struct monst *, const char *); -extern void miss(const char *, struct monst *); +extern const char *exclam(int force) NONNULL; +extern void hit(const char *, struct monst *, const char *) NONNULLPTRS; +extern void miss(const char *, struct monst *) NONNULLPTRS; extern struct monst *bhit(coordxy, coordxy, int, enum bhit_call_types, int(*)(struct monst *, struct obj *), - int(*)(struct obj *, struct obj *), struct obj **); -extern struct monst *boomhit(struct obj *, coordxy, coordxy); -extern int zhitm(struct monst *, int, int, struct obj **); + int(*)(struct obj *, struct obj *), + struct obj **) NONNULLARG7; +extern struct monst *boomhit(struct obj *, coordxy, coordxy) NONNULLARG1; +extern int zhitm(struct monst *, int, int, struct obj **) NONNULLPTRS; extern int burn_floor_objects(coordxy, coordxy, boolean, boolean); extern void ubuzz(int, int); extern void buzz(int, int, coordxy, coordxy, int, int); extern void dobuzz(int, int, coordxy, coordxy, int, int, boolean); -extern void melt_ice(coordxy, coordxy, const char *); +extern void melt_ice(coordxy, coordxy, const char *) NO_NNARGS; extern void start_melt_ice_timeout(coordxy, coordxy, long); -extern void melt_ice_away(union any *, long); -extern int zap_over_floor(coordxy, coordxy, int, boolean *, boolean, short); -extern void fracture_rock(struct obj *); +extern void melt_ice_away(union any *, long) NONNULLARG1; +extern int zap_over_floor(coordxy, coordxy, int, boolean *, + boolean, short) NONNULLARG4; +extern void mon_spell_hits_spot(struct monst *, int, coordxy x, coordxy y); +extern void fracture_rock(struct obj *) NONNULLARG1; extern boolean destroyable_oclass(char); -extern boolean break_statue(struct obj *); -extern int destroy_items(struct monst *, int, int); -extern boolean adtyp_resistance_obj(struct monst *, int); +extern boolean break_statue(struct obj *) NONNULLARG1; +extern int destroy_items(struct monst *, int, int) NONNULLARG1; +extern int adtyp_resistance_obj(struct monst *, int) NONNULLARG1; +extern boolean inventory_resistance_check(struct monst *, int); extern char *item_what(int); -extern int resist(struct monst *, char, int, int); +extern int destroy_items(struct monst *, int, int) NONNULLARG1; +extern int resist(struct monst *, char, int, int) NONNULLARG1; extern void makewish(void); -extern const char *flash_str(int, boolean); +extern const char *flash_str(int, boolean) NONNULL; + +/* ### unixmain.c, windsys.c ### */ + +#ifdef RUNTIME_PORT_ID +extern char *get_port_id(char *); +#endif +#ifdef RUNTIME_PASTEBUF_SUPPORT +extern void port_insert_pastebuf(char *); +#endif #endif /* !MAKEDEFS_C && !MDLIB_C */ #endif /* EXTERN_H */ + +/*extern.h*/ diff --git a/include/flag.h b/include/flag.h index 63c232eb7d..fa6da7742b 100644 --- a/include/flag.h +++ b/include/flag.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 flag.h $NHDT-Date: 1684791761 2023/05/22 21:42:41 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.217 $ */ +/* NetHack 3.7 flag.h $NHDT-Date: 1715979826 2024/05/17 21:03:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.246 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -49,8 +49,10 @@ struct flag { boolean mention_decor; /* give feedback for unobscured furniture */ boolean mention_walls; /* give feedback when bumping walls */ boolean nap; /* `timed_delay' option for display effects */ + boolean nopick_dropped; /* items you dropped may be autopicked */ boolean null; /* OK to send nulls to the terminal */ boolean pickup; /* whether you pickup or move and look */ + boolean pickup_stolen; /* auto-pickup items stolen by a monster */ boolean pickup_thrown; /* auto-pickup items you threw */ boolean pushweapon; /* When wielding, push old weapon into second slot */ boolean quick_farsight; /* True disables map browsing during random @@ -60,6 +62,7 @@ struct flag { boolean safe_wait; /* prevent wait or search next to hostile */ boolean showexp; /* show experience points */ boolean showscore; /* show score */ + boolean showvers; /* show version on status lines */ boolean silent; /* whether the bell rings or not */ boolean sortpack; /* sorted inventory */ boolean sparkle; /* show "resisting" special FX (Scott Bigham) */ @@ -90,7 +93,14 @@ struct flag { #define PARANOID_EATING 0x0200 #define PARANOID_SWIM 0x0400 #define PARANOID_TRAP 0x0800 -#define PARANOID_THROW 0x1000 +#define PARANOID_AUTOALL 0x1000 +#define PARANOID_THROW 0x2000 + unsigned versinfo; /* flag mask for 'showvers' option */ + /* mask bits for 'versinfo'; numeric order does not match display order + which is "name branch number" */ +#define VI_NUMBER 1 /* x.y.z */ +#define VI_NAME 2 /* game's name (ie, "nethack") */ +#define VI_BRANCH 4 /* development branch (from git, via Makefile -CFLAGS) */ int pickup_burden; /* maximum burden before prompt */ int pile_limit; /* controls feedback when walking over objects */ char discosort; /* order of dodiscovery/doclassdisco output: o,s,c,a */ @@ -191,6 +201,41 @@ struct debug_flags { #endif }; +enum windowcolors_windows { + wcolor_menu, wcolor_message, wcolor_status, wcolor_text, + WC_COUNT +}; + +struct windowcolors_struct { + char *fg; + char *bg; +}; + +struct accessibility_data { + boolean accessiblemsg; /* use msg_loc for plined messages */ + coord msg_loc; /* accessiblemsg: location */ + boolean mon_notices; /* msg when hero notices a monster */ + int mon_notices_blocked; /* temp disable mon_notices */ + boolean mon_movement; /* msg when hero sees monster move */ + boolean glyph_updates; /* msg when map glyphs change */ +}; + +/* Use notice_mon_off() / notice_mon_on() to temporarily disable + noticing the monsters in the vision code - perhaps the game + needs to output some other messages in between. + Call notice_all_mons() afterwards to catch up. */ +#define notice_mon_off() do { a11y.mon_notices_blocked++; } while(0) +#define notice_mon_on() do { if (--a11y.mon_notices_blocked < 0) { \ + impossible("mon_notices_blocked<0"); \ + 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 @@ -198,8 +243,9 @@ struct debug_flags { * and probably warrant a structure of their own elsewhere some day. */ struct instance_flags { - boolean debug_fuzzer; /* fuzz testing */ - boolean defer_plname; /* X11 hack: askname() might not set gp.plname */ + boolean query_menu; /* use a menu for yes/no queries */ + boolean showdamage; + boolean defer_plname; /* X11 hack: askname() might not set svp.plname */ boolean herecmd_menu; /* use menu when mouseclick on yourself */ boolean invis_goldsym; /* gold symbol is ' '? */ boolean in_lua; /* executing a lua script */ @@ -209,27 +255,30 @@ struct instance_flags { * indirectly so we can't use xname_flags() */ boolean remember_getpos; /* save getpos() positioning in do-again queue */ boolean sad_feeling; /* unseen pet is dying */ + 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 */ int failing_untrap; /* move_into_trap() -> spoteffects() -> dotrap() */ + int getdir_click; /* as input to getdir(): non-zero, accept simulated + * click that's not adjacent to or on hero; + * as output from getdir(): simulated button used + * 0 (none) or CLICK_1 (left) or CLICK_2 (right) */ + int getloc_filter; /* GFILTER_foo */ int in_lava_effects; /* hack for Boots_off() */ int last_msg; /* indicator of last message player saw */ 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 */ + int raw_printed; /* count of messages issued before window_inited */ int suppress_price; /* controls doname() for unpaid objects */ - int terrainmode; /* for getpos()'s autodescribe when #terrain is active */ -#define TER_MAP 0x01 -#define TER_TRP 0x02 -#define TER_OBJ 0x04 -#define TER_MON 0x08 -#define TER_DETECT 0x10 /* detect_foo magic rather than #terrain */ -#define TER_VISIT 0x20 - int getdir_click; /* as input to getdir(): non-zero, accept simulated - * click that's not adjacent to or on hero; - * as output from getdir(): simulated button used - * 0 (none) or CLICK_1 (left) or CLICK_2 (right) */ - int getloc_filter; /* GFILTER_foo */ + unsigned terrainmode; /* for getpos()'s autodescribe during #terrain */ +#define TER_MAP 0x01U +#define TER_TRP 0x02U +#define TER_OBJ 0x04U +#define TER_MON 0x08U +#define TER_FULL 0x10U /* explore|wizard mode view full map */ +#define TER_DETECT 0x20U /* detect_foo magic rather than #terrain */ +#define TER_VISIT 0x40U boolean bgcolors; /* display background colors on a map position */ boolean getloc_moveskip; boolean getloc_travelmode; @@ -244,6 +293,7 @@ struct instance_flags { boolean debug_mongen; /* debug: prevent monster generation */ boolean debug_hunger; /* debug: prevent hunger */ boolean mon_polycontrol; /* debug: control monster polymorphs */ + boolean mon_telecontrol; /* debug: control monster teleports */ boolean in_dumplog; /* doing the dumplog right now? */ boolean in_parse; /* is a command being parsed? */ /* suppress terminate during options parsing, for --showpaths */ @@ -255,7 +305,7 @@ struct instance_flags { int getpos_coords; /* show coordinates when getting cursor position */ int menuinvertmode; /* 0 = invert toggles every item; 1 = invert skips 'all items' item */ - int menu_headings; /* ATR for menu headings */ + 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 @@ -275,7 +325,8 @@ struct instance_flags { boolean menu_tab_sep; /* Use tabs to separate option menu fields */ boolean news; /* print news */ boolean num_pad; /* use numbers for movement commands */ - boolean perm_invent; /* keep full inventories up until dismissed */ + 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 */ @@ -294,8 +345,16 @@ struct instance_flags { boolean zerocomp; /* write zero-compressed save files */ boolean rlecomp; /* alternative to zerocomp; run-length encoding * compression of levels when writing savefile */ + schar ice_rating; /* ice_descr()'s classification of ice terrain */ schar prev_decor; /* 'mention_decor' just mentioned this */ - uchar num_pad_mode; + uchar num_pad_mode; /* for num_pad==True, controls how 5 behaves + * and/or 789456123 vs phone-style 123456789; + * for False, qwertY vs qwertZ */ + uchar perminv_mode; /* what to display in persistent invent window + * 0: nothing, 1: all inventory except gold, + * 2: full including gold, 8: in-use items only, + * 5|6: 1|2 with invent letters shown in empty + * slots (TTY only: 'sparse' modes) */ uchar bouldersym; /* symbol for boulder display */ char prevmsg_window; /* type of old message window to use */ boolean extmenu; /* extended commands use menu interface */ @@ -329,11 +388,12 @@ struct instance_flags { #endif boolean cmdassist; /* provide detailed assistance for some comnds */ boolean fireassist; /* autowield launcher when using fire-command */ - boolean time_botl; /* context.botl for 'time' (moves) only */ boolean wizmgender; /* test gender info from core in window port */ boolean invweight; /* display weights of items in inventory */ boolean msg_is_alert; /* suggest windowport should grab player's attention * and request acknowlegement */ + boolean customcolors; /* support customcolors defined in glyphmap */ + boolean customsymbols; /* support customsymbols defined in glyphmap */ /* * Window capability support. */ @@ -350,6 +410,7 @@ struct instance_flags { int wc_align_status; /* status win at top|bot|right|left */ int wc_align_message; /* message win at top|bot|right|left */ int wc_vary_msgcount; /* show more old messages at a time */ +#if 0 char *wc_foregrnd_menu; /* points to foregrnd color name for menu win */ char *wc_backgrnd_menu; /* points to backgrnd color name for menu win */ char *wc_foregrnd_message; /* points to foregrnd color name for msg win */ @@ -358,6 +419,9 @@ struct instance_flags { char *wc_backgrnd_status; /* points to backgrnd color name for status */ char *wc_foregrnd_text; /* points to foregrnd color name for text win */ char *wc_backgrnd_text; /* points to backgrnd color name for text win */ +#else + struct windowcolors_struct wcolors[WC_COUNT]; +#endif char *wc_font_map; /* points to font name for the map win */ char *wc_font_message; /* points to font name for message win */ char *wc_font_status; /* points to font name for status win */ @@ -411,6 +475,8 @@ struct instance_flags { chosen_windowport[], but do not switch to it in the midst of options processing */ genericptr_t returning_missile; /* 'struct obj *'; Mjollnir or aklys */ + boolean wiz_error_flag; /* flag for tracking failed wizmode auth */ + boolean explore_error_flag; /* ditto for explore mode */ boolean obsolete; /* obsolete options can point at this, it isn't used */ }; @@ -434,6 +500,7 @@ struct instance_flags { extern NEARDATA struct flag flags; extern NEARDATA struct instance_flags iflags; +extern NEARDATA struct accessibility_data a11y; /* last_msg values * Usage: @@ -448,12 +515,15 @@ enum plnmsg_types { PLNMSG_ONE_ITEM_HERE, /* "you see here" */ PLNMSG_TOWER_OF_FLAME, /* scroll of fire */ PLNMSG_CAUGHT_IN_EXPLOSION, /* explode() feedback */ + PLNMSG_ENVELOPED_IN_GAS, /* create_gas_cloud() feedback */ PLNMSG_OBJ_GLOWS, /* "the glows " */ PLNMSG_OBJNAM_ONLY, /* xname/doname only, for #tip */ PLNMSG_OK_DONT_DIE, /* overriding death in explore/wizard mode */ PLNMSG_BACK_ON_GROUND, /* leaving water */ PLNMSG_GROWL, /* growl() gave some message */ - PLNMSG_enum /* allows inserting new entries with unconditional trailing comma */ + PLNMSG_HIDE_UNDER, /* hero saw a monster hide under something */ + PLNMSG_MON_TAKES_OFF_ITEM, /* thief (nymph, monkey) taking worn item */ + PLNMSG_enum /* 'none of the above' */ }; /* runmode options */ @@ -491,8 +561,10 @@ enum runmode_types { #define ParanoidEating ((flags.paranoia_bits & PARANOID_EATING) != 0) /* Prevent going into lava or water without explicitly forcing it */ #define ParanoidSwim ((flags.paranoia_bits & PARANOID_SWIM) != 0) -/* trap: move onto a trap that you know is there */ +/* Prevent going onto/into known trap unless it is harmless */ #define ParanoidTrap ((flags.paranoia_bits & PARANOID_TRAP) != 0) +/* Require confirmation for choosing 'A' in class menu for menustyle:Full */ +#define ParanoidAutoAll ((flags.paranoia_bits & PARANOID_AUTOALL) != 0U) /* throw: throw ammo without a corresponding launcher wielded */ #define ParanoidThrow ((flags.paranoia_bits & PARANOID_THROW) != 0) diff --git a/include/func_tab.h b/include/func_tab.h index 5078d5c8d3..2924b3bf37 100644 --- a/include/func_tab.h +++ b/include/func_tab.h @@ -21,6 +21,7 @@ #define MOVEMENTCMD 0x0400 /* used to move hero/cursor */ #define MOUSECMD 0x0800 /* cmd allowed to be bound to mouse button */ #define CMD_INSANE 0x1000 /* suppress sanity check (for ^P and ^R) */ +#define AUTOCOMP_ADJ 0x2000 /* user changed command autocompletion */ /* flags for extcmds_match() */ #define ECM_NOFLAGS 0 diff --git a/include/global.h b/include/global.h index 4eb9d99bbb..df599f52f6 100644 --- a/include/global.h +++ b/include/global.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 global.h $NHDT-Date: 1657918090 2022/07/15 20:48:10 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.144 $ */ +/* NetHack 3.7 global.h $NHDT-Date: 1704225560 2024/01/02 19:59:20 $ $NHDT-Branch: keni-luabits2 $:$NHDT-Revision: 1.159 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -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). @@ -321,9 +319,10 @@ typedef uchar nhsym; if nethack is built with MONITOR_HEAP enabled and they aren't; this declaration has been moved out of the '#else' below to avoid getting a complaint from -Wmissing-prototypes when building with MONITOR_HEAP */ -extern char *dupstr(const char *) NONNULL; -/* same, but return strlen(string) */ -extern char *dupstr_n(const char *string, unsigned int *lenout) NONNULL; +extern char *dupstr(const char *) NONNULL NONNULLARG1; +/* same, but return strlen(string) in extra argument */ +extern char *dupstr_n(const char *string, + unsigned *lenout) NONNULL NONNULLPTRS; /* * MONITOR_HEAP is conditionally used for primitive memory leak debugging. @@ -335,20 +334,15 @@ extern char *dupstr_n(const char *string, unsigned int *lenout) NONNULL; */ #ifdef MONITOR_HEAP /* plain alloc() is not declared except in alloc.c */ -extern long *nhalloc(unsigned int, const char *, int) NONNULL; -extern long *nhrealloc(long *, unsigned int, const char *, int) NONNULL; -extern void nhfree(genericptr_t, const char *, int); -extern char *nhdupstr(const char *, const char *, int) NONNULL; +extern long *nhalloc(unsigned int, const char *, int) NONNULL NONNULLARG2; +extern long *nhrealloc(long *, unsigned int, const char *, + int) NONNULL NONNULLARG3; +extern void nhfree(genericptr_t, const char *, int) NONNULLARG2; +extern char *nhdupstr(const char *, const char *, int) NONNULL NONNULLPTRS; /* this predates C99's __func__; that is trickier to use conditionally because it is not implemented as a preprocessor macro; MONITOR_HEAP wouldn't gain much benefit from it anyway so continue to live without it; if func's caller were accessible, that would be a very different issue */ -#ifndef __FILE__ -#define __FILE__ "" -#endif -#ifndef __LINE__ -#define __LINE__ 0 -#endif #define alloc(a) nhalloc(a, __FILE__, (int) __LINE__) #define re_alloc(a,n) nhrealloc(a, n, __FILE__, (int) __LINE__) #define free(a) nhfree(a, __FILE__, (int) __LINE__) @@ -392,6 +386,7 @@ struct nomakedefs_s { const char *copyright_banner_c; const char *git_sha; const char *git_branch; + const char *git_prefix; const char *version_string; const char *version_id; unsigned long version_number; @@ -418,12 +413,12 @@ extern struct nomakedefs_s nomakedefs; #define MAXNROFROOMS 40 /* max number of rooms per level */ #define MAX_SUBROOMS 24 /* max # of subrooms in a given room */ -#define DOORINC 120 /* number of doors per level, increment */ +#define DOORINC 20 /* number of doors per level, increment */ #define BUFSZ 256 /* for getlin buffers */ #define QBUFSZ 128 /* for building question text */ -#define TBUFSZ 300 /* gt.toplines[] buffer max msg: 3 81char names */ -/* plus longest prefix plus a few extra words */ +#define TBUFSZ 300 /* gt.toplines[] buffer max msg: 3 81-char names + * plus longest prefix plus a few extra words */ /* COLBUFSZ is the larger of BUFSZ and COLNO */ #if BUFSZ > COLNO @@ -436,6 +431,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 32 /* current maximum number of dungeons */ #define MAXLEVEL 32 /* max number of levels in one dungeon */ @@ -447,6 +444,12 @@ extern struct nomakedefs_s nomakedefs; #define MAXMONNO 120 /* extinct monst after this number created */ #define MHPMAX 500 /* maximum monster hp */ +#ifndef MAX_MSG_HISTORY +#define MAX_MSG_HISTORY 128 /* max # of lines in msg_history */ +#endif + +#include "color.h" + /* * Version 3.7.x has aspirations of portable file formats. We * make a distinction between MAIL functionality and MAIL_STRUCTURES @@ -460,11 +463,11 @@ extern struct nomakedefs_s nomakedefs; #ifdef UNIX #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) /* see end.c */ -#if !defined(CROSS_TO_WASM) +#if !defined(CROSS_TO_WASM) && !defined(CROSS_TO_MSDOS) #ifndef PANICTRACE #define PANICTRACE #endif /* PANICTRACE */ -#endif /* CROSS_TO_WASM */ +#endif /* CROSS_TO_WASM | CROSS_TO_MSDOS */ #endif /* NH_DEVEL_STATUS != NH_STATUS_RELEASED */ #endif /* UNIX */ @@ -479,6 +482,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 */ @@ -508,7 +514,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 */ @@ -519,7 +524,7 @@ extern struct nomakedefs_s nomakedefs; #define LL_DIVINEGIFT 0x0008L /* Sacrifice gifts, crowning */ #define LL_LIFESAVE 0x0010L /* Use up amulet of lifesaving */ #define LL_CONDUCT 0x0020L /* Break conduct - not reported early-game */ -#define LL_ARTIFACT 0x0040L /* bestowed, found, or manifactured */ +#define LL_ARTIFACT 0x0040L /* bestowed, found, or manufactured */ #define LL_GENOCIDE 0x0080L /* Logging of genocides */ #define LL_KILLEDPET 0x0100L /* Killed a tame monster */ #define LL_ALIGNMENT 0x0200L /* changed alignment, temporary or permanent */ @@ -555,10 +560,6 @@ typedef struct nhl_sandbox_info { #define NHL_SB_VERSION 0x40000000 /* Debugging library - mostly unsafe. */ #define NHL_SB_DEBUGGING 0x08000000 - /* Use with memlimit/steps/perpcall to get usage. */ -#define NHL_SB_REPORT 0x04000000 - /* As above, but do full gc on each nhl_pcall. */ -#define NHL_SB_REPORT2 0x02000000 /* Low level groups. If you need these, you probably need to define * a new high level group instead. */ @@ -594,4 +595,10 @@ typedef struct nhl_sandbox_info { #define NHL_SBRV_ACCEPT 2 #define NHL_SBRV_FAIL 3 +/* NHL_pcall_handle action values */ +typedef enum NHL_pcall_action { + NHLpa_panic, + NHLpa_impossible +} NHL_pcall_action; + #endif /* GLOBAL_H */ diff --git a/include/hack.h b/include/hack.h index c9a44c44f7..048f76d0aa 100644 --- a/include/hack.h +++ b/include/hack.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 hack.h $NHDT-Date: 1684374685 2023/05/18 01:51:25 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.218 $ */ +/* 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. */ @@ -11,9 +11,9 @@ #endif #include "lint.h" -#include "color.h" #include "align.h" #include "dungeon.h" +#include "stairs.h" #include "objclass.h" #include "wintype.h" #include "flag.h" @@ -25,7 +25,6 @@ #include "botl.h" #include "context.h" -#include "dungeon.h" #include "engrave.h" #include "mkroom.h" #include "obj.h" @@ -33,6 +32,7 @@ #include "rect.h" #include "region.h" #include "rm.h" +#include "selvar.h" #include "sndprocs.h" #include "spell.h" #include "sym.h" @@ -61,7 +61,7 @@ /* bitmask flags for corpse_xname(); PFX_THE takes precedence over ARTICLE, NO_PFX takes precedence over both */ #define CXN_NORMAL 0 /* no special handling */ -#define CXN_SINGULAR 1 /* override quantity if greather than 1 */ +#define CXN_SINGULAR 1 /* override quantity if greater than 1 */ #define CXN_NO_PFX 2 /* suppress "the" from "the Unique Monst */ #define CXN_PFX_THE 4 /* prefix with "the " (unless pname) */ #define CXN_ARTICLE 8 /* include a/an/the prefix */ @@ -132,27 +132,27 @@ enum bhit_call_types { /* Macros for messages referring to hands, eyes, feet, etc... */ enum bodypart_types { - NO_PART = 0, - ARM = 1, - EYE = 2, - FACE = 3, - FINGER = 4, - FINGERTIP = 5, - FOOT = 6, - HAND = 7, - HANDED = 8, - HEAD = 9, - LEG = 10, - LIGHT_HEADED = 11, - NECK = 12, - SPINE = 13, - TOE = 14, - HAIR = 15, - BLOOD = 16, - LUNG = 17, - NOSE = 18, - STOMACH = 19, - TORSO = 20 + NO_PART = -1, + ARM = 0, + EYE = 1, + FACE = 2, + FINGER = 3, + FINGERTIP = 4, + FOOT = 5, + HAND = 6, + HANDED = 7, + HEAD = 8, + LEG = 9, + LIGHT_HEADED = 10, + NECK = 11, + SPINE = 12, + TOE = 13, + HAIR = 14, + BLOOD = 15, + LUNG = 16, + NOSE = 17, + STOMACH = 18, + TORSO = 19 }; #define MAX_BMASK 4 @@ -271,11 +271,11 @@ struct c_color_names { }; struct c_common_strings { - const char *const c_nothing_happens, *const c_thats_enough_tries, - *const c_silly_thing_to, *const c_shudder_for_moment, - *const c_something, *const c_Something, *const c_You_can_move_again, - *const c_Never_mind, *c_vision_clears, *const c_the_your[2], - *const c_fakename[2]; + const char *const c_nothing_happens, *const c_nothing_seems_to_happen, + *const c_thats_enough_tries, *const c_silly_thing_to, + *const c_shudder_for_moment, *const c_something, *const c_Something, + *const c_You_can_move_again, *const c_Never_mind, + *const c_vision_clears, *const c_the_your[2], *const c_fakename[2]; }; struct container { @@ -306,7 +306,15 @@ enum cost_alteration_types { COST_RUST = 16, /* rust damage */ COST_ROT = 17, /* rotting attack */ COST_CORRODE = 18, /* acid damage */ - COST_FERMENT = 19 /* fermentation of potion into new potion */ + COST_CRACK = 19, /* damage to crystal armor */ + COST_FERMENT = 20 /* fermentation of potion into new potion */ +}; + +/* used by unpaid_cost(shk.h) */ +enum unpaid_cost_flags { + COST_NOCONTENTS = 0, + COST_CONTENTS = 1, + COST_SINGLEOBJ = 2, }; /* read.c, create_particular() & create_particular_parse() */ @@ -321,6 +329,27 @@ struct _create_particular_data { boolean sleeping, saddled, invisible, hidden; }; +/* dig_check() results */ + +enum digcheck_result { + DIGCHECK_PASSED = 1, + DIGCHECK_PASSED_DESTROY_TRAP = 2, + DIGCHECK_PASSED_PITONLY = 3, + DIGCHECK_FAILED = 4, + DIGCHECK_FAIL_ONSTAIRS = DIGCHECK_FAILED, + DIGCHECK_FAIL_ONLADDER, + DIGCHECK_FAIL_THRONE, + DIGCHECK_FAIL_ALTAR, + DIGCHECK_FAIL_AIRLEVEL, + DIGCHECK_FAIL_WATERLEVEL, + DIGCHECK_FAIL_TOOHARD, + DIGCHECK_FAIL_UNDESTROYABLETRAP, + DIGCHECK_FAIL_CANTDIG, + DIGCHECK_FAIL_BOULDER, + DIGCHECK_FAIL_OBJ_POOL_OR_TRAP +}; + + /* Dismount: causes for why you are no longer riding */ enum dismount_types { DISMOUNT_GENERIC = 0, @@ -363,6 +392,7 @@ struct dgn_topology { /* special dungeon levels for speed */ xint16 d_gehennom_dnum; xint16 d_sokoban_dnum; xint16 d_mines_dnum, d_quest_dnum; + xint16 d_tutorial_dnum; xint16 d_abyss_dnum; d_level d_qstart_level, d_qlocate_level, d_nemesis_level; d_level d_knox_level; @@ -372,60 +402,67 @@ struct dgn_topology { /* special dungeon levels for speed */ /* macros for accessing the dungeon levels by their old names */ /* clang-format off */ -#define oracle_level (gd.dungeon_topology.d_oracle_level) -#define bigroom_level (gd.dungeon_topology.d_bigroom_level) -#define medusa_level (gd.dungeon_topology.d_medusa_level) -#define stronghold_level (gd.dungeon_topology.d_stronghold_level) -#define valley_level (gd.dungeon_topology.d_valley_level) -#define hellgate_level (gd.dungeon_topology.d_hellgate_level) -#define styxmarsh_level (gd.dungeon_topology.d_styxmarsh_level) -#define wiz1_level (gd.dungeon_topology.d_wiz1_level) -#define wiz2_level (gd.dungeon_topology.d_wiz2_level) -#define wiz3_level (gd.dungeon_topology.d_wiz3_level) -#define yeenoghu_level (gd.dungeon_topology.d_yeenoghu_level) -#define juiblex_level (gd.dungeon_topology.d_juiblex_level) -#define orcus_level (gd.dungeon_topology.d_orcus_level) -#define geryon_level (gd.dungeon_topology.d_geryon_level) -#define demogorgon_level (gd.dungeon_topology.d_demogorgon_level) -#define dis_level (gd.dungeon_topology.d_dis_level) -#define dispater_level (gd.dungeon_topology.d_dispater_level) -#define baalzebub_level (gd.dungeon_topology.d_baalzebub_level) -#define asmodeus_level (gd.dungeon_topology.d_asmodeus_level) -#define sanctum_level (gd.dungeon_topology.d_sanctum_level) -#define earth_level (gd.dungeon_topology.d_earth_level) -#define water_level (gd.dungeon_topology.d_water_level) -#define fire_level (gd.dungeon_topology.d_fire_level) -#define air_level (gd.dungeon_topology.d_air_level) -#define astral_level (gd.dungeon_topology.d_astral_level) -#define tower_dnum (gd.dungeon_topology.d_tower_dnum) -#define gehennom_dnum (gd.dungeon_topology.d_gehennom_dnum) -#define abyss_dnum (gd.dungeon_topology.d_abyss_dnum) -#define sokoban_dnum (gd.dungeon_topology.d_sokoban_dnum) -#define mines_dnum (gd.dungeon_topology.d_mines_dnum) -#define quest_dnum (gd.dungeon_topology.d_quest_dnum) -#define qstart_level (gd.dungeon_topology.d_qstart_level) -#define qlocate_level (gd.dungeon_topology.d_qlocate_level) -#define nemesis_level (gd.dungeon_topology.d_nemesis_level) -#define knox_level (gd.dungeon_topology.d_knox_level) -#define mineend_level (gd.dungeon_topology.d_mineend_level) -#define sokoend_level (gd.dungeon_topology.d_sokoend_level) +#define oracle_level (svd.dungeon_topology.d_oracle_level) +#define bigroom_level (svd.dungeon_topology.d_bigroom_level) +#define medusa_level (svd.dungeon_topology.d_medusa_level) +#define stronghold_level (svd.dungeon_topology.d_stronghold_level) +#define valley_level (svd.dungeon_topology.d_valley_level) +#define hellgate_level (svd.dungeon_topology.d_hellgate_level) +#define styxmarsh_level (svd.dungeon_topology.d_styxmarsh_level) +#define wiz1_level (svd.dungeon_topology.d_wiz1_level) +#define wiz2_level (svd.dungeon_topology.d_wiz2_level) +#define wiz3_level (svd.dungeon_topology.d_wiz3_level) +#define yeenoghu_level (svd.dungeon_topology.d_yeenoghu_level) +#define juiblex_level (svd.dungeon_topology.d_juiblex_level) +#define orcus_level (svd.dungeon_topology.d_orcus_level) +#define geryon_level (svd.dungeon_topology.d_geryon_level) +#define demogorgon_level (svd.dungeon_topology.d_demogorgon_level) +#define dis_level (svd.dungeon_topology.d_dis_level) +#define dispater_level (svd.dungeon_topology.d_dispater_level) +#define baalzebub_level (svd.dungeon_topology.d_baalzebub_level) +#define asmodeus_level (svd.dungeon_topology.d_asmodeus_level) +#define sanctum_level (svd.dungeon_topology.d_sanctum_level) +#define earth_level (svd.dungeon_topology.d_earth_level) +#define water_level (svd.dungeon_topology.d_water_level) +#define fire_level (svd.dungeon_topology.d_fire_level) +#define air_level (svd.dungeon_topology.d_air_level) +#define astral_level (svd.dungeon_topology.d_astral_level) +#define tower_dnum (svd.dungeon_topology.d_tower_dnum) +#define gehennom_dnum (svd.dungeon_topology.d_gehennom_dnum) +#define abyss_dnum (svd.dungeon_topology.d_abyss_dnum) +#define sokoban_dnum (svd.dungeon_topology.d_sokoban_dnum) +#define mines_dnum (svd.dungeon_topology.d_mines_dnum) +#define quest_dnum (svd.dungeon_topology.d_quest_dnum) +#define tutorial_dnum (svd.dungeon_topology.d_tutorial_dnum) +#define qstart_level (svd.dungeon_topology.d_qstart_level) +#define qlocate_level (svd.dungeon_topology.d_qlocate_level) +#define nemesis_level (svd.dungeon_topology.d_nemesis_level) +#define knox_level (svd.dungeon_topology.d_knox_level) +#define mineend_level (svd.dungeon_topology.d_mineend_level) +#define sokoend_level (svd.dungeon_topology.d_sokoend_level) /* clang-format on */ -#define dunlev_reached(x) (gd.dungeons[(x)->dnum].dunlev_ureached) +#define dunlev_reached(x) (svd.dungeons[(x)->dnum].dunlev_ureached) #define MAXLINFO (MAXDUNGEON * MAXLEVEL) +enum lua_theme_group { + all_themes = 1, /* for end of game */ + most_themes = 2, /* for entering endgame */ + tut_themes = 3, /* for leaving tutorial */ +}; enum earlyarg { ARG_DEBUG, ARG_VERSION, ARG_SHOWPATHS #ifndef NODUMPENUMS , ARG_DUMPENUMS #endif -#ifdef ENHANCED_SYMBOLS , ARG_DUMPGLYPHIDS -#endif #ifdef WIN32 , ARG_WINDOWS #endif +#if defined(CRASHREPORT) + , ARG_BIDSHOW +#endif }; struct early_opt { @@ -459,7 +496,7 @@ struct enum_dump { /* * This is the way the game ends. If these are rearranged, the arrays * in end.c and topten.c will need to be changed. Some parts of the - * code assume that PANIC separates the deaths from the non-deaths. + * code assume that PANICKED separates the deaths from the non-deaths. */ enum game_end_types { DIED = 0, @@ -553,6 +590,18 @@ enum hunger_state_types { STARVED = 6 }; +/* inventory counts (slots in tty parlance) + * a...zA..Z invlet_basic (52) + * $a...zA..Z# 2 special additions + */ +enum inventory_counts { + invlet_basic = 52, + invlet_gold = 1, + invlet_overflow = 1, + invlet_max = invlet_basic + invlet_gold + invlet_overflow, + /* 2023/11/30 invlet_max is not yet used anywhere */ +}; + struct kinfo { struct kinfo *next; /* chain of delayed killers */ int id; /* uprop keys to ID a delayed killer */ @@ -639,6 +688,7 @@ struct mvitals { uchar born; uchar died; uchar mvflags; + Bitfield(seen_close, 1); }; @@ -649,6 +699,8 @@ enum nhcore_calls { NHCORE_MOVELOOP_TURN, NHCORE_GAME_EXIT, NHCORE_GETPOS_TIP, + NHCORE_ENTER_TUTORIAL, + NHCORE_LEAVE_TUTORIAL, NUM_NHCORE_CALLS }; @@ -673,7 +725,7 @@ enum optset_restrictions { set_viaprog = 2, /* may be set via extern program, not seen in game */ set_gameview = 3, /* may be set via extern program, displayed in game */ set_in_game = 4, /* may be set via extern program or set in the game */ - set_wizonly = 5, /* may be set set in the game if wizmode */ + set_wizonly = 5, /* may be set in the game if wizmode */ set_wiznofuz = 6, /* wizard-mode only, but not by fuzzer */ set_hidden = 7 /* placeholder for prefixed entries, never show it */ }; @@ -745,8 +797,9 @@ struct restore_info { }; enum restore_stages { - REST_GSTATE = 1, /* restoring current level and game state */ - REST_LEVELS = 2, /* restoring remainder of dungeon */ + REST_GSTATE = 1, /* restoring game state + first pass of current level */ + REST_LEVELS = 2, /* restoring remainder of dungeon */ + REST_CURRENT_LEVEL = 3, /* final pass of restoring current level */ }; struct rogueroom { @@ -792,13 +845,16 @@ struct sinfo { int exiting; /* an exit handler is executing */ int saving; /* creating a save file */ int restoring; /* reloading a save file */ + int in_getlev; /* in getlev() */ int in_moveloop; /* normal gameplay in progress */ - int in_impossible; /* reportig a warning */ + int in_impossible; /* reporting a warning */ int in_docrt; /* in docrt(): redrawing the whole screen */ - int in_self_recover; /* processsing orphaned level files */ + int in_self_recover; /* processing orphaned level files */ int in_checkpoint; /* saving insurance checkpoint */ int in_parseoptions; /* in parseoptions */ int in_role_selection; /* role/race/&c selection menus in progress */ + int in_getlin; /* inside interface getlin routine */ + 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 */ #ifdef PANICLOG @@ -809,8 +865,15 @@ struct sinfo { used in the curses interface to avoid arrow keys when user is doing something other than entering a command or direction and in the Qt interface to suppress menu commands in similar conditions; - readchar() alrways resets it to 'otherInp' prior to returning */ + readchar() always resets it to 'otherInp' prior to returning */ int input_state; /* whether next key pressed will be entering a command */ +#ifdef TTY_GRAPHICS + /* resize_pending only matters when handling a SIGWINCH signal for tty; + getting_char is used along with that and also separately for UNIX; + we minimize #if conditionals for them to avoid unnecessary clutter */ + volatile int resize_pending; /* set by signal handler */ + volatile int getting_char; /* referenced during signal handling */ +#endif }; /* value of program_state.input_state, significant during readchar(); @@ -826,20 +889,33 @@ enum InputState { struct sortloot_item { struct obj *obj; char *str; /* result of loot_xname(obj) in some cases, otherwise null */ - int indx; /* signed int, because sortloot()'s qsort comparison routine - assumes (a->indx - b->indx) might yield a negative result */ - xint16 orderclass; /* order rather than object class; 0 => not yet init'd */ - xint16 subclass; /* subclass for some classes */ - xint16 disco; /* discovery status */ + /* these need to be signed; 'indx' should be big enough to hold a count + of the largest pile of items, the others fit within char */ + int indx; /* index into original list (used as tie-breaker) */ + int8 orderclass; /* order rather than object class; 0 => not yet init'd */ + int8 subclass; /* subclass for some classes */ + int8 disco; /* discovery status */ + int8 inuse; /* 0: not in-use or not sorting by inuse_only; + * 1: lit candle/lamp or attached leash; 2: worn armor; + * 3: wielded weapon (including uswapwep and uquiver); + * 4: worn accessory (amulet, rings, blindfold). */ }; typedef struct sortloot_item Loot; typedef struct strbuf { int len; - char * str; + char *str; 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 trapinfo { struct obj *tobj; coordxy tx, ty; @@ -866,6 +942,7 @@ typedef struct { #define UTD_CHECKFIELDCOUNTS 0x02 #define UTD_SKIP_SANITY1 0x04 #define UTD_SKIP_SAVEFILEINFO 0x08 +#define UTD_WITHOUT_WAITSYNCH_PERFILE 0x10 #define ENTITIES 2 struct valuable_data { @@ -898,185 +975,6 @@ struct autopickup_exception { struct autopickup_exception *next; }; -#if !defined(NO_VERBOSE_GRANULARITY) -#define VB_ELEMENTS 5 -/* - * Maintenance Notes: - * - if one of the function's involved has a name change, - * and the Verbose() macro use instance is updated to match, - * it will have to be reflected below. If the use instance - * isn't updated to reflect the function name change, - * it will continue to work using the old name if it matches - * one of the entries below. - */ - -enum verbosity_values { - vb0interrupt_multi = 0x00000001, - vb0use_stethoscope = 0x00000002, - vb0Mb_hit = 0x00000004, - vb0adjattrib = 0x00000008, - vb0ballfall = 0x00000010, - vb0use_crystal_ball1 = 0x00000020, - vb0use_crystal_ball2 = 0x00000040, - vb0digactualhole1 = 0x00000080, - vb0digactualhole2 = 0x00000100, - vb0mdig_tunnel1 = 0x00000200, - vb0mdig_tunnel2 = 0x00000400, - vb0boulder_hits_pool1 = 0x00000800, - vb0boulder_hits_pool2 = 0x00001000, - vb0drop1 = 0x00002000, - vb0drop2 = 0x00004000, - vb0drop3 = 0x00008000, - vb0go_to_level1 = 0x00010000, - vb0go_to_level2 = 0x00020000, - vb0go_to_level3 = 0x00040000, - vb0rot_corpse = 0x00080000, - vb0getpos1 = 0x00100000, - vb0getpos2 = 0x00200000, - vb0off_msg = 0x00400000, - vb0on_msg = 0x00800000, - vb0Blindf_on = 0x01000000, - vb0dog_eat = 0x02000000, - vb0dog_invent = 0x04000000, - vb0dokick = 0x08000000, - vb0toss_up = 0x10000000, - vb0consume_tin1 = 0x20000000, - vb0consume_tin2 = 0x40000000, - - vb1doengrave1 = 0x00000001, - vb1doengrave2 = 0x00000002, - vb1doengrave3 = 0x00000004, - vb1explode = 0x00000008, - vb1moverock = 0x00000010, - vb1still_chewing = 0x00000020, - vb1trapmove1 = 0x00000040, - vb1trapmove2 = 0x00000080, - vb1trapmove3 = 0x00000100, - vb1trapmove4 = 0x00000200, - vb1trapmove5 = 0x00000400, - vb1getobj1 = 0x00000800, - vb1getobj2 = 0x00001000, - vb1doprgold = 0x00002000, - vb1doorlock1 = 0x00004000, - vb1doorlock2 = 0x00008000, - vb1monpoly1 = 0x00010000, - vb1monpoly2 = 0x00020000, - vb1mswingsm = 0x00040000, - vb1missmu = 0x00080000, - vb1mswings = 0x00100000, - vb1wildmiss = 0x00200000, - vb1gulpmu = 0x00400000, - vb1explmu = 0x00800000, - vb1meatmetal1 = 0x01000000, - vb1meatmetal2 = 0x02000000, - vb1meatmetal3 = 0x04000000, - vb1meatmetal4 = 0x08000000, - vb1relobj = 0x10000000, - vb1ready_weapon = 0x20000000, - vb1wield_tool = 0x40000000, - - vb2meatobj1 = 0x00000001, - vb2meatobj2 = 0x00000002, - vb2meatobj3 = 0x00000004, - vb2meatobj4 = 0x00000008, - vb2meatcorpse1 = 0x00000010, - vb2meatcorpse2 = 0x00000020, - vb2mpickgold = 0x00000040, - vb2mpickstuff = 0x00000080, - vb2setmangry = 0x00000100, - vb2mb_trapped = 0x00000200, - /* because xNetHack split mon_open_door out of m_move, the middle 3 of these - * are now mon_open_door flags. */ - vb2m_move1 = 0x00000400, - vb2mon_open_door1 = 0x00000800, - vb2mon_open_door2 = 0x00001000, - vb2mon_open_door3 = 0x00002000, - vb2m_move5 = 0x00004000, - vb2thitu1 = 0x00008000, - vb2thitu2 = 0x00010000, - vb2m_throw = 0x00020000, - vb2handler_menustyle = 0x00040000, - vb2handler_autounlock = 0x00080000, - vb2handler_msg_window = 0x00100000, - vb2handler_whatis_coord1 = 0x00200000, - vb2handler_whatis_coord2 = 0x00400000, - vb2dolook = 0x00800000, - vb2describe_decor1 = 0x01000000, - vb2describe_decor2 = 0x02000000, - vb2loot_mon = 0x04000000, - vb2dotip = 0x08000000, - vb2polymon = 0x10000000, - vb2teleds = 0x20000000, - vb2level_tele = 0x40000000, - - vb3ghost_from_bottle = 0x00000001, - vb3dodip1 = 0x00000002, - vb3dodip2 = 0x00000004, - vb3dodip3 = 0x00000008, - vb3intemple = 0x00000010, - vb3doread1 = 0x00000020, - vb3doread2 = 0x00000040, - vb3doread3 = 0x00000080, - vb3doread4 = 0x00000100, - vb3doread5 = 0x00000200, - vb3doread6 = 0x00000400, - vb3doread7 = 0x00000800, - vb3drop_boulder_on_player= 0x00001000, - vb3do_genocide = 0x00002000, - vb3call_kops1 = 0x00004000, - vb3call_kops2 = 0x00008000, - vb3call_kops3 = 0x00010000, - vb3erode_obj1 = 0x00020000, - vb3erode_obj2 = 0x00040000, - vb3erode_obj3 = 0x00080000, - vb3trapeffect_rocktrap = 0x00100000, - vb3climb_pit = 0x00200000, - vb3drown = 0x00400000, - vb3mon_adjust_speed = 0x00800000, - vb3hit = 0x01000000, - vb3miss = 0x02000000, - vb3makewish = 0x04000000, - vb3prinv = 0x08000000, - /* 3 available bits*/ - - vb4do_attack = 0x00000001, - vb4known_hitum = 0x00000002, - vb4hmon_hitmon1 = 0x00000004, - vb4hmon_hitmon2 = 0x00000008, - vb4mhitm_ad_tlpt = 0x00000010, - vb4mhitm_ad_wrap1 = 0x00000020, - vb4mhitm_ad_wrap2 = 0x00000040, - vb4mhitm_ad_dgst = 0x00000080, - vb4damageum = 0x00000100, - vb4missum = 0x00000200, - vb4hmonas1 = 0x00000400, - vb4hmonas2 = 0x00000800, - vb4hmonas3 = 0x00001000, - vb4hmonas4 = 0x00002000, - vb4passive = 0x00004000, - vb4flash_hits_mon = 0x00008000, - /* xNetHack ones start here */ - vb4create_pit_under = 0x00010000, - vb4dodip4 = 0x00020000, - vb4adjattrib2 = 0x00040000, - vb4hmon_hitmon_splitmon = 0x00080000, - vb4newuhs = 0x00100000, - /* 6 available bits */ - - vb_elements = VB_ELEMENTS -}; -#undef VB_ELEMENTS -extern long verbosity_suppressions[vb_elements]; /* in decl.c */ - -#define Verbose(n,s) (flags.verbose && \ - (((n) >= 0 && (n) < vb_elements) && \ - !(verbosity_suppressions[(n)] & vb##n##s))) - -#else /* NO_VERBOSE_GRANULARITY */ -#define Verbose(n,s) (flags.verbose) -#endif /* !NO_VERBOSE_GRANULARITY */ - - /* at most one of `door' and `box' should be non-null at any given time */ struct xlock_s { struct rm *door; @@ -1116,7 +1014,7 @@ typedef struct { long count; /* holds current line count for default style file, field count for binary style */ boolean structlevel; /* traditional structure binary saves */ - boolean fieldlevel; /* fieldlevel saves saves each field individually */ + boolean fieldlevel; /* fieldlevel saves each field individually */ boolean addinfo; /* if set, some additional context info from core */ boolean eof; /* place to mark eof reached */ boolean bendian; /* set to true if executing on big-endian machine */ @@ -1134,13 +1032,14 @@ typedef struct { #define ARTICLE_YOUR 3 /* x_monnam() monster name suppress masks */ -#define SUPPRESS_IT 0x01 -#define SUPPRESS_INVISIBLE 0x02 +#define SUPPRESS_IT 0x01 +#define SUPPRESS_INVISIBLE 0x02 #define SUPPRESS_HALLUCINATION 0x04 -#define SUPPRESS_SADDLE 0x08 -#define EXACT_NAME 0x0F -#define SUPPRESS_NAME 0x10 -#define AUGMENT_IT 0x20 /* use "someone" or "something" instead of "it" */ +#define SUPPRESS_SADDLE 0x08 +#define SUPPRESS_MAPPEARANCE 0x10 +#define EXACT_NAME 0x1F +#define SUPPRESS_NAME 0x20 +#define AUGMENT_IT 0x40 /* use "someone" or "something" instead of "it" */ /* pline (et al) for a single string argument (suppress compiler warning) */ #define pline1(cstr) pline("%s", cstr) @@ -1213,45 +1112,95 @@ typedef struct { #define UNDEFINED_VALUES { 0 } #define UNDEFINED_PTR NULL -#define MATCH_WARN_OF_MON(mon) \ - (Warn_of_mon \ - && ((gc.context.warntype.obj & (mon)->data->mflags2) != 0 \ - || (gc.context.warntype.obj_mlet \ - == (unsigned int) (mon)->data->mlet) \ - || (gc.context.warntype.polyd & (mon)->data->mflags2) != 0 \ - || (gc.context.warntype.species \ - && (gc.context.warntype.species == (mon)->data)))) +/* The UNDEFINED_ROLE macro is used to initialize Role variables */ +#define UNDEFINED_ROLE \ + { \ + /* role name, set of rank names */ \ + { NULL, NULL }, { { NULL, NULL } }, \ + /* strings: pantheon deity names */ \ + NULL, NULL, NULL, \ + /* file code, quest home+goal names */ \ + NULL, NULL, NULL, \ + /* indices: base mon type, pet */ \ + NON_PM, NON_PM, \ + /* quest leader, guardians, nemesis */ \ + NON_PM, NON_PM, NON_PM, \ + /* quest enemy types (index, symbol) */ \ + NON_PM, NON_PM, '\0', '\0', \ + /* quest artifact object index */ \ + STRANGE_OBJECT, \ + /* Bitmasks */ \ + 0, \ + /* Attributes */ \ + {0}, {0}, {0}, {0}, 0, \ + /* spell statistics */ \ + 0 } + +/* The UNDEFINED_RACE macro is used to initialize Race variables */ +#define UNDEFINED_RACE \ + { \ + /* strings */ \ + NULL, NULL, NULL, NULL, { NULL, NULL }, \ + /* Indices: base race, mummy, zombie */ \ + NON_PM, NON_PM, NON_PM, \ + /* Bitmasks */ \ + 0, 0, 0, 0, \ + /* Characteristic limits */ \ + {0}, {0}, \ + /* Level change HP and Pw adjustments */ \ + {0}, {0} \ + } + +#define MATCH_WARN_OF_MON(mon) \ + (Warn_of_mon \ + && ((svc.context.warntype.obj & (mon)->data->mflags2) != 0 \ + || (svc.context.warntype.obj_mlet \ + == (unsigned int) (mon)->data->mlet) \ + || (svc.context.warntype.polyd & (mon)->data->mflags2) != 0 \ + || (svc.context.warntype.species \ + && (svc.context.warntype.species == (mon)->data)))) typedef uint32_t mmflags_nht; /* makemon MM_ flags */ /* flags to control makemon(); goodpos() uses some plus has some of its own*/ -#define NO_MM_FLAGS 0x000000L /* use this rather than plain 0 */ -#define NO_MINVENT 0x000001L /* suppress minvent when creating mon */ -#define MM_NOWAIT 0x000002L /* don't set STRAT_WAITMASK flags */ -#define MM_NOCOUNTBIRTH 0x0004L /* don't increment born count (for revival) */ -#define MM_IGNOREWATER 0x0008L /* ignore water when positioning */ -#define MM_ADJACENTOK 0x0010L /* acceptable to use adjacent coordinates */ -#define MM_ANGRY 0x000020L /* monster is created angry */ -#define MM_NONAME 0x000040L /* monster is not christened */ -#define MM_EGD 0x000100L /* add egd structure */ -#define MM_EPRI 0x000200L /* add epri structure */ -#define MM_ESHK 0x000400L /* add eshk structure */ -#define MM_EMIN 0x000800L /* add emin structure */ -#define MM_EDOG 0x001000L /* add edog structure */ -#define MM_ASLEEP 0x002000L /* monsters should be generated asleep */ -#define MM_NOGRP 0x004000L /* suppress creation of monster groups */ -#define MM_NOTAIL 0x008000L /* if a long worm, don't give it a tail */ -#define MM_MALE 0x010000L /* male variation */ -#define MM_FEMALE 0x020000L /* female variation */ -#define MM_NOMSG 0x040000L /* no appear message */ +#define NO_MM_FLAGS 0x00000000L /* use this rather than plain 0 */ +#define NO_MINVENT 0x00000001L /* suppress minvent when creating mon */ +#define MM_NOWAIT 0x00000002L /* don't set STRAT_WAITMASK flags */ +#define MM_NOCOUNTBIRTH 0x00000004L /* don't incr born count (for revival) */ +#define MM_IGNOREWATER 0x00000008L /* ignore water when positioning */ +#define MM_ADJACENTOK 0x00000010L /* ok to use adjacent coordinates */ +#define MM_ANGRY 0x00000020L /* monster is created angry */ +#define MM_NONAME 0x00000040L /* monster is not christened */ +#define MM_EGD 0x00000080L /* add egd structure */ +#define MM_EPRI 0x00000100L /* add epri structure */ +#define MM_ESHK 0x00000200L /* add eshk structure */ +#define MM_EMIN 0x00000400L /* add emin structure */ +#define MM_EDOG 0x00000800L /* add edog structure */ +#define MM_ASLEEP 0x00001000L /* monsters should be generated asleep */ +#define MM_NOGRP 0x00002000L /* suppress creation of monster groups */ +#define MM_NOTAIL 0x00004000L /* if a long worm, don't give it a tail */ +#define MM_MALE 0x00008000L /* male variation */ +#define MM_FEMALE 0x00010000L /* female variation */ +#define MM_NOMSG 0x00020000L /* no appear message */ +#define MM_NOEXCLAM 0x00040000L /* more sedate " appears." + * mesg for ^G */ +#define MM_IGNORELAVA 0x00080000L /* ignore lava when positioning */ +#define MM_MINVIS 0x00100000L /* for ^G/create_particular */ /* if more MM_ flag masks are added, skip or renumber the GP_ one(s) */ -#define GP_ALLOW_XY 0x080000L /* [actually used by enexto() to decide - * whether to make extra call to goodpos()] */ -#define GP_ALLOW_U 0x100000L /* don't reject hero's location */ -#define GP_CHECKSCARY 0x200000L /* check monster for onscary() */ -#define MM_NOEXCLAM 0x400000L /* more sedate " appears." mesg for ^G */ -#define MM_IGNORELAVA 0x800000L /* ignore lava when positioning */ +#define GP_ALLOW_XY 0x00200000L /* [actually used by enexto() to decide + * whether to make an extra call to + * goodpos()] */ +#define GP_ALLOW_U 0x00400000L /* don't reject hero's location */ +#define GP_CHECKSCARY 0x00800000L /* check monster for onscary() */ +#define GP_AVOID_MONPOS 0x01000000L /* don't accept existing mon location */ +/* 25 bits used */ + +/* flags for mhidden_description() (pager.c; used for mimics and hiders) */ +#define MHID_PREFIX 1 /* include ", mimicking " prefix */ +#define MHID_ARTICLE 2 /* include "a " or "an " after prefix */ +#define MHID_ALTMON 4 /* if mimicking a monster, include that */ +#define MHID_REGION 8 /* include region when mon is in one */ /* flags for make_corpse() and mkcorpstat(); 0..7 are recorded in obj->spe */ #define CORPSTAT_NONE 0x00 @@ -1356,16 +1305,17 @@ typedef uint32_t mmflags_nht; /* makemon MM_ flags */ #define ONAME_RANDOM 0x0080U /* something created an artifact randomly * with mk_artifact() (mksboj or mk_player) * or m_initweap() (lawful Angel) */ -/* flag congrolling potential livelog event of finding an artifact */ +/* flag controlling potential livelog event of finding an artifact */ #define ONAME_KNOW_ARTI 0x0100U /* hero is already aware of this artifact */ /* 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 @@ -1432,6 +1382,7 @@ typedef uint32_t mmflags_nht; /* makemon MM_ flags */ #define SORTLOOT_PACK 0x01 #define SORTLOOT_INVLET 0x02 #define SORTLOOT_LOOT 0x04 +#define SORTLOOT_INUSE 0x08 /* for inventory, in-use items first */ #define SORTLOOT_PETRIFY 0x20 /* override filter func for c-trice corpses */ /* flags for xkilled() [note: meaning of first bit used to be reversed, @@ -1609,11 +1560,11 @@ enum concealed_spot_returnflags { *********************************************************************/ /* flags for mktrap() */ -#define MKTRAP_NOFLAGS 0x0 -#define MKTRAP_MAZEFLAG 0x1 /* trap placed on coords as if in maze */ -#define MKTRAP_NOSPIDERONWEB 0x2 /* web will not generate a spider */ -#define MKTRAP_SEEN 0x4 /* trap is seen */ -#define MKTRAP_NOVICTIM 0x8 /* no victim corpse or items on it */ +#define MKTRAP_NOFLAGS 0x0U +#define MKTRAP_SEEN 0x1U /* trap is seen */ +#define MKTRAP_MAZEFLAG 0x2U /* choose random coords instead of room */ +#define MKTRAP_NOSPIDERONWEB 0x4U /* web will not generate a spider */ +#define MKTRAP_NOVICTIM 0x8U /* no victim corpse or items on it */ #define MON_POLE_DIST 5 /* How far monsters can use pole-weapons */ #define PET_MISSILE_RANGE2 36 /* Square of distance within which pets shoot */ @@ -1621,9 +1572,9 @@ enum concealed_spot_returnflags { /* flags passed to getobj() to control how it responds to player input */ #define GETOBJ_NOFLAGS 0x0 #define GETOBJ_ALLOWCNT 0x1 /* is a count allowed with this command? */ -#define GETOBJ_PROMPT 0x2 /* should it force a prompt for input? (prevents it - exiting early with "You don't have anything to - foo" if nothing in inventory is valid) */ +#define GETOBJ_PROMPT 0x2 /* should it force a prompt for input? (prevents + * it exiting early with "You don't have anything + * to foo" if nothing in inventory is valid) */ /* flags for hero_breaks() and hits_bars(); BRK_KNOWN* let callers who have already called breaktest() prevent it from being called again since it @@ -1646,11 +1597,11 @@ enum concealed_spot_returnflags { #define NC_SHOW_MSG 0x01U #define NC_VIA_WAND_OR_SPELL 0x02U -/* constant passed to explode() for gas spores because gas spores are weird - * Specifically, this is an exception to the whole "explode() uses dobuzz types" - * system (the range -1 to -9 isn't used by it, for some reason), where this is - * effectively an extra dobuzz type, and some zap.c code needs to be aware of - * it. */ +/* Constant passed to explode() for gas spores because gas spores are weird. + * Specifically, this is an exception to whole "explode() uses dobuzz types" + * system (the range -1 to -9 isn't used by it, for some reason), where this + * is effectively an extra dobuzz type, and some zap.c code needs to be aware + * of it. */ #define PHYS_EXPL_TYPE -1 /* macros for dobuzz() type */ @@ -1672,6 +1623,18 @@ enum concealed_spot_returnflags { /* monster shooting a wand; note: not -9 to -0 because -0 is ambiguous */ #define BZ_M_WAND(bztyp) (-30 - (bztyp)) /* -39..-30 */ +/* pick a random entry from array */ +#define ROLL_FROM(array) array[rn2(SIZE(array))] +/* array with terminator variation */ +/* #define ROLL_FROMT(array) array[rn2(SIZE(array) - 1)] */ + +/* validate index of array */ +#define IndexOk(idx, array) \ + ((idx) >= 0 && (idx) < SIZE(array)) +/* array with terminator variation */ +#define IndexOkT(idx, array) \ + ((idx) >= 0 && (idx) < (SIZE(array) - 1)) + #define FEATURE_NOTICE_VER(major, minor, patch) \ (((unsigned long) major << 24) | ((unsigned long) minor << 16) \ | ((unsigned long) patch << 8) | ((unsigned long) 0)) diff --git a/include/hacklib.h b/include/hacklib.h new file mode 100644 index 0000000000..2912035a4b --- /dev/null +++ b/include/hacklib.h @@ -0,0 +1,84 @@ +/* NetHack 3.7 hacklib.h $NHDT-Date: 1725653010 2024/09/06 20:03:30 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.0 $ */ +/* Copyright (c) Steve Creps, 1988. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef HACKLIB_H +#define HACKLIB_H + +/* + * hacklib true library functions + */ +extern boolean digit(char); +extern boolean letter(char); +extern char highc(char); +extern char lowc(char); +extern char *lcase(char *) NONNULL NONNULLARG1; +extern char *ucase(char *) NONNULL NONNULLARG1; +extern char *upstart(char *); /* ought to be changed to NONNULL NONNULLARG1 + * and the code changed to not allow NULL arg */ +extern char *upwords(char *) NONNULL NONNULLARG1; +extern char *mungspaces(char *) NONNULL NONNULLARG1; +extern char *trimspaces(char *) NONNULL NONNULLARG1; +extern char *strip_newline(char *) NONNULL NONNULLARG1; +extern char *eos(char *) NONNULL NONNULLARG1; +extern const char *c_eos(const char *) NONNULL NONNULLARG1; +extern boolean str_start_is(const char *, const char *, boolean) NONNULLPTRS; +extern boolean str_end_is(const char *, const char *) NONNULLPTRS; +extern int str_lines_maxlen(const char *); +extern char *strkitten(char *, char) NONNULL NONNULLARG1; +extern void copynchars(char *, const char *, int) NONNULLARG12; +extern char chrcasecpy(int, int); +extern char *strcasecpy(char *, const char *) NONNULL NONNULLPTRS; +extern char *s_suffix(const char *) NONNULL NONNULLARG1; +extern char *ing_suffix(const char *) NONNULL NONNULLARG1; +extern char *xcrypt(const char *, char *) NONNULL NONNULLPTRS; +extern boolean onlyspace(const char *) NONNULLARG1; +extern char *tabexpand(char *) NONNULL NONNULLARG1; +extern char *visctrl(char) NONNULL; +extern char *stripchars(char *, const char *, + const char *) NONNULL NONNULLPTRS; +extern char *stripdigits(char *) NONNULL NONNULLARG1; +extern char *strsubst(char *, const char *, const char *) NONNULL NONNULLPTRS; +extern int strNsubst(char *, const char *, const char *, int) NONNULLPTRS; +extern const char *findword(const char *, const char *, int, + boolean) NONNULLARG2; +extern const char *ordin(int) NONNULL; +extern char *sitoa(int) NONNULL; +extern int sgn(int); +extern int distmin(coordxy, coordxy, coordxy, coordxy); +extern int dist2(coordxy, coordxy, coordxy, coordxy); +extern int isqrt(int); +extern boolean online2(coordxy, coordxy, coordxy, coordxy); +#ifndef STRNCMPI +extern int strncmpi(const char *, const char *, int) NONNULLPTRS; +#endif +#ifndef STRSTRI +extern char *strstri(const char *, const char *) NONNULLPTRS; +#endif +#define FITSint(x) FITSint_(x, __func__, __LINE__) +extern int FITSint_(long long, const char *, int); +#define FITSuint(x) FITSuint_(x, __func__, __LINE__) +extern unsigned FITSuint_(unsigned long long, const char *, int); +extern int case_insensitive_comp(const char *, const char *); +extern boolean fuzzymatch(const char *, const char *, + const char *, boolean) NONNULLPTRS; +extern int swapbits(int, int, int); +/* note: the snprintf CPP wrapper includes the "fmt" argument in "..." + (__VA_ARGS__) to allow for zero arguments after fmt */ +extern void nh_snprintf(const char *func, int line, char *str, + size_t size, const char *fmt, ...) PRINTF_F(5, 6); +extern void nh_snprintf_w_impossible(const char *func, int line, char *str, + size_t size, const char *fmt, ...) PRINTF_F(5, 6); + +#define Snprintf(str, size, ...) \ + nh_snprintf(__func__, __LINE__, str, size, __VA_ARGS__) + +#if 0 +/*#define Strlen(s) Strlen_(s, __func__, __LINE__)*/ +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); + +#endif /* HACKLIB_H */ + diff --git a/include/integer.h b/include/integer.h index 43e8d22d82..32a3b32668 100644 --- a/include/integer.h +++ b/include/integer.h @@ -1,11 +1,11 @@ -/* NetHack 3.7 integer.h $NHDT-Date: 1596498539 2020/08/03 23:48:59 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.8 $ */ +/* NetHack 3.7 integer.h $NHDT-Date: 1720397754 2024/07/08 00:15:54 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.13 $ */ /* Copyright (c) 2016 by Michael Allison */ /* NetHack may be freely redistributed. See license for details. */ /* integer.h -- provide sized integer types * * We try to sort out a way to provide sized integer types - * in here. The strong preference is to try and let a + * in here. The strong preference is to try to let a * compiler-supplied header file set up the types. * * If your compiler is C99 conforming and sets a value of @@ -101,4 +101,18 @@ typedef uint32_t uint32; typedef int64_t int64; typedef uint64_t uint64; +/* for non-negative L, calculate L * 10 + D, avoiding signed overflow; + yields -1 if overflow would have happened; + assumes compiler will optimize the constants */ +#define AppendLongDigit(L,D) \ + (((L) < LONG_MAX / 10L \ + || ((L) == LONG_MAX / 10L && (D) <= LONG_MAX % 10L)) \ + ? (L) * 10L + (D) \ + : -1L) + +/* add a and b, return max long value if overflow would have occurred; + assumes that both a and b are non-negative; caller should apply + cast(s) to (long) in the arguments if any are needed */ +#define nowrap_add(a,b) ((a) <= (LONG_MAX - (b)) ? ((a) + (b)) : LONG_MAX) + #endif /* INTEGER_H */ diff --git a/include/isaac64.h b/include/isaac64.h index 4a650f1dd4..4b53467b19 100644 --- a/include/isaac64.h +++ b/include/isaac64.h @@ -36,7 +36,7 @@ struct isaac64_ctx{ /** - * isaac64_init - Initialize an instance of the ISAAC64 random number generator. + * isaac64_init - Initialize an instance of ISAAC64 random number generator. * @_ctx: The ISAAC64 instance to initialize. * @_seed: The specified seed bytes. * This may be NULL if _nseed is less than or equal to zero. @@ -77,8 +77,8 @@ uint64_t isaac64_next_uint(isaac64_ctx *_ctx,uint64_t _n); * @_ctx: The ISAAC64 instance to generate the value with. * Returns a high-quality float uniformly distributed between 0 (inclusive) * and 1 (exclusive). - * All of the float's mantissa bits are random, e.g., the least significant bit - * may still be non-zero even if the value is less than 0.5, and any + * All of the float's mantissa bits are random, e.g., the least significant + * bit may still be non-zero even if the value is less than 0.5, and any * representable float in the range [0,1) has a chance to be returned, though * values very close to zero become increasingly unlikely. * To generate cheaper float values that do not have these properties, use diff --git a/include/mextra.h b/include/mextra.h index 9663ab63d2..296676aeeb 100644 --- a/include/mextra.h +++ b/include/mextra.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 mextra.h $NHDT-Date: 1596498545 2020/08/03 23:49:05 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.30 $ */ +/* NetHack 3.7 mextra.h $NHDT-Date: 1720717969 2024/07/11 17:12:49 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.40 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -122,8 +122,8 @@ struct eshk { long credit; /* amount credited to customer */ long debit; /* amount of debt for using unpaid items */ long loan; /* shop-gold picked (part of debit) */ - int shoptype; /* the value of gr.rooms[shoproom].rtype */ - schar shoproom; /* index in gr.rooms; set by inshop() */ + int shoptype; /* the value of svr.rooms[shoproom].rtype */ + schar shoproom; /* index in svr.rooms; set by inshop() */ schar unused; /* to force alignment for stupid compilers */ boolean following; /* following customer since he owes us sth */ boolean surcharge; /* angry shk inflates prices */ diff --git a/include/mkroom.h b/include/mkroom.h index a1ec581092..93730eed31 100644 --- a/include/mkroom.h +++ b/include/mkroom.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 mkroom.h $NHDT-Date: 1596498547 2020/08/03 23:49:07 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.22 $ */ +/* NetHack 3.7 mkroom.h $NHDT-Date: 1725653011 2024/09/06 20:03:31 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.33 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Pasi Kallinen, 2016. */ /* NetHack may be freely redistributed. See license for details. */ @@ -26,7 +26,7 @@ struct mkroom { struct shclass { const char *name; /* name of the shop type */ - const char *noun_name; /* shop type in #overview */ + const char *annotation; /* simpler name for #overview; Null if same */ char symb; /* this identifies the shop type */ int prob; /* the shop type probability in % */ schar shdist; /* object placement type */ @@ -40,10 +40,10 @@ struct shclass { const char *const *shknms; /* list of shopkeeper names for this type */ }; -/* the normal rooms on the current level are described in gr.rooms[0..n] for +/* the normal rooms on the current level are described in svr.rooms[0..n] for * some n= gr.rooms && (x) < gr.rooms + MAXNROFROOMS) +#define IS_ROOM_PTR(x) \ + ((x) >= svr.rooms && (x) < svr.rooms + MAXNROFROOMS) #define IS_ROOM_INDEX(x) ((x) >= 0 && (x) < MAXNROFROOMS) #define IS_SUBROOM_PTR(x) \ ((x) >= gs.subrooms && (x) < gs.subrooms + MAXNROFROOMS) #define IS_SUBROOM_INDEX(x) ((x) > MAXNROFROOMS && (x) <= (MAXNROFROOMS * 2)) -#define ROOM_INDEX(x) ((x) - gr.rooms) +#define ROOM_INDEX(x) ((x) - svr.rooms) #define SUBROOM_INDEX(x) ((x) - gs.subrooms) -#define IS_LAST_ROOM_PTR(x) (ROOM_INDEX(x) == gn.nroom) -#define IS_LAST_SUBROOM_PTR(x) (!gn.nsubroom || SUBROOM_INDEX(x) == gn.nsubroom) +#define IS_LAST_ROOM_PTR(x) (ROOM_INDEX(x) == svn.nroom) +#define IS_LAST_SUBROOM_PTR(x) \ + (!gn.nsubroom || SUBROOM_INDEX(x) == gn.nsubroom) #endif /* MKROOM_H */ diff --git a/include/mondata.h b/include/mondata.h index 975ed2c709..5dc061428d 100644 --- a/include/mondata.h +++ b/include/mondata.h @@ -1,44 +1,30 @@ -/* NetHack 3.7 mondata.h $NHDT-Date: 1606473485 2020/11/27 10:38:05 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.45 $ */ +/* NetHack 3.7 mondata.h $NHDT-Date: 1703845738 2023/12/29 10:28:58 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.63 $ */ /* Copyright (c) 1989 Mike Threepoint */ /* NetHack may be freely redistributed. See license for details. */ #ifndef MONDATA_H #define MONDATA_H +/* The macros in here take a permonst * as an argument */ + +#define monsndx(ptr) ((ptr)->pmidx) #define verysmall(ptr) ((ptr)->msize < MZ_SMALL) #define bigmonst(ptr) ((ptr)->msize >= MZ_LARGE) #define pm_resistance(ptr, typ) (((ptr)->mresists & (typ)) != 0) -/* mresists from any source - innate, intrinsic, or extrinsic */ -#define mon_resistancebits(mon) \ - ((mon)->data->mresists | (mon)->mextrinsics | (mon)->mintrinsics) -/* resists_fire() turned into function to handle artifact fire resistance */ -#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) - /* for MR2 stuff, the mresists part of mon_resistancebits is 0 because it's a * uchar */ #define has_telepathy(mon) \ (telepathic((mon)->data) || (mon_resistancebits(mon) & MR2_TELEPATHY) != 0) -/* as of 3.2.0: gray dragons, Angels, Oracle, Yeenoghu */ +/* as of 3.2.0: gray dragons, Angels, Oracle, Yeenoghu + * this was moved out of resists_magm so monster lookup can show it */ #define resists_mgc(ptr) \ (dmgtype(ptr, AD_MAGM) || ptr == &mons[PM_BABY_GRAY_DRAGON] \ || dmgtype(ptr, AD_RBRE)) /* Tiamat */ +/* similarly, this was moved out of resists_drli for monster lookup */ #define resists_drain(ptr) \ (is_undead(ptr) || is_demon(ptr) || is_were(ptr) \ || ptr == &mons[PM_DEATH]) @@ -47,8 +33,6 @@ #define immune_poisongas(ptr) ((ptr) == &mons[PM_HEZROU] \ || (ptr) == &mons[PM_VROCK]) -#define is_lminion(mon) \ - (is_minion((mon)->data) && mon_aligntyp(mon) == A_LAWFUL) #define is_flyer(ptr) (((ptr)->mflags1 & M1_FLY) != 0L) #define is_floater(ptr) ((ptr)->mlet == S_EYE || (ptr)->mlet == S_LIGHT) /* clinger: piercers, mimics, wumpus -- generally don't fall down holes */ @@ -210,6 +194,8 @@ #define is_rider(ptr) \ ((ptr) == &mons[PM_DEATH] || (ptr) == &mons[PM_FAMINE] \ || (ptr) == &mons[PM_PESTILENCE]) +/* note: placeholder monsters are used for corpses of zombies and mummies; + PM_DWARF and PM_GNOME are normal monsters, not placeholders */ #define is_placeholder(ptr) \ ((ptr) == &mons[PM_ORC] || (ptr) == &mons[PM_GIANT] \ || (ptr) == &mons[PM_ELF] || (ptr) == &mons[PM_HUMAN]) @@ -233,6 +219,8 @@ #define touch_petrifies(ptr) \ ((ptr) == &mons[PM_COCKATRICE] || (ptr) == &mons[PM_CHICKATRICE]) +/* Medusa doesn't pass touch_petrifies() but does petrify if eaten */ +#define flesh_petrifies(pm) (touch_petrifies(pm) || (pm) == &mons[PM_MEDUSA]) /* missiles made of rocks don't harm these: xorns and earth elementals (but not ghosts and shades because that would impact all missile use @@ -293,6 +281,12 @@ || obj->material == VEGGY \ || ((obj)->otyp == CORPSE && (obj)->corpsenm == PM_LICHEN)))) +#ifdef PMNAME_MACROS +#define pmname(ptr,g) ((((g) == MALE || (g) == FEMALE) && (ptr)->pmnames[g]) \ + ? (ptr)->pmnames[g] : (ptr)->pmnames[NEUTRAL]) +#endif +#define monsym(ptr) (def_monsyms[(int) (ptr)->mlet].sym) + /* Wielding and opening doors use the same flags: handed and not verysmall. Exception: zombies cannot open doors. */ diff --git a/include/monflag.h b/include/monflag.h index 2252e6da51..05f88865ed 100644 --- a/include/monflag.h +++ b/include/monflag.h @@ -68,6 +68,9 @@ enum ms_sounds { #define MR_POISON 0x20 /* resists poison */ #define MR_ACID 0x40 /* resists acid */ #define MR_STONE 0x80 /* resists petrification */ +/* NB: the above resistances correspond to the first 8 hero properties in + prop_types (FIRE_RES through STONE_RES), which can be converted to their + MR_foo equivalents with the macro res_to_mr() defined in prop.h */ /* other resistances: magic, sickness */ /* other conveyances: teleport, teleport control, telepathy */ @@ -204,7 +207,7 @@ enum ms_sounds { passed to mkclass() as if it dealt with mons[].geno bits */ #define G_IGNORE 0x8000 /* for mkclass(), ignore G_GENOD|G_EXTINCT */ -/* for gm.mvitals[].mvflags (variant during game), along with G_NOCORPSE */ +/* for svm.mvitals[].mvflags (variant during game), along with G_NOCORPSE */ #define G_KNOWN 0x04 /* have been encountered */ #define G_GENOD 0x02 /* have been genocided */ #define G_EXTINCT 0x01 /* population control; create no more */ diff --git a/include/monst.h b/include/monst.h index 3263434c63..c3eee89245 100644 --- a/include/monst.h +++ b/include/monst.h @@ -64,7 +64,6 @@ enum m_ap_types { #define MON_ENDGAME_FREE 0x20 #define MON_ENDGAME_MIGR 0x40 #define MON_OBLITERATE 0x80 -#define MSTATE_MASK 0xFF #define M_AP_TYPMASK 0x7 #define M_AP_F_DKNOWN 0x8 @@ -88,7 +87,10 @@ enum m_seen_resistance { #define m_seenres(mon, mask) ((mon)->seen_resistance & (mask)) #define m_setseenres(mon, mask) ((mon)->seen_resistance |= (mask)) +#define m_clearseenres(mon, mask) ((mon)->seen_resistance &= ~(mask)) #define monstseesu_ad(adtyp) monstseesu(cvt_adtyp_to_mseenres(adtyp)) +#define monstunseesu_ad(adtyp) monstunseesu(cvt_adtyp_to_mseenres(adtyp)) +#define monstunseesu_prop(prop) monstunseesu(cvt_prop_to_mseenres(prop)) struct monst { struct monst *nmon; @@ -156,9 +158,11 @@ struct monst { Bitfield(iswiz, 1); /* is the Wizard of Yendor */ Bitfield(wormno, 5); /* at most 31 worms on any level */ Bitfield(mtemplit, 1); /* temporarily seen; only valid during bhit() */ - Bitfield(mwither_from_u, 1); /* is withering due to player */ Bitfield(meverseen, 1); /* mon has been seen at some point */ - /* 7 free bits */ + + Bitfield(mspotted, 1); /* mon is currently seen by hero */ + Bitfield(mwither_from_u, 1); /* is withering due to player */ + /* 6 free bits */ uchar mwither; /* withering; amount of turns left till recovery */ #define MAX_NUM_WORMS 32 /* should be 2^(wormno bitfield size) */ @@ -208,25 +212,29 @@ struct monst { #define MON_WEP(mon) ((mon)->mw) #define MON_NOWEP(mon) ((mon)->mw = (struct obj *) 0) +/* dead monsters stay on the fmon list until dmonsfree() at end of turn */ #define DEADMONSTER(mon) ((mon)->mhp < 1) -#define is_starting_pet(mon) ((mon)->m_id == gc.context.startingpet_mid) -#define is_vampshifter(mon) \ + +#define is_starting_pet(mon) ((mon)->m_id == svc.context.startingpet_mid) +#define is_vampshifter(mon) \ ((mon)->cham == PM_VAMPIRE || (mon)->cham == PM_VAMPIRE_LEADER \ || (mon)->cham == PM_VLAD_THE_IMPALER) #define vampshifted(mon) (is_vampshifter((mon)) && !is_vampire((mon)->data)) +/* Vlad might be vampshifted so just checking monst->data is insufficient */ +#define is_Vlad(m) ((m)->data == &mons[PM_VLAD_THE_IMPALER] \ + || (m)->cham == PM_VLAD_THE_IMPALER) -/* monsters which cannot be displaced: priests, shopkeepers, vault guards, - Oracle, quest leader */ -#define mundisplaceable(mon) ((mon)->ispriest \ - || (mon)->isshk \ - || (mon)->isgd \ - || (mon)->data == &mons[PM_ORACLE] \ - || (mon)->m_id == gq.quest_status.leader_m_id) +/* monsters which cannot be displaced: temple priests, shopkeepers, + vault guards, the Oracle, quest leader */ +#define mundisplaceable(mon) \ + ((mon)->ispriest || (mon)->isshk \ + || (mon)->isgd || (mon)->data == &mons[PM_ORACLE] \ + || (mon)->m_id == svq.quest_status.leader_m_id) /* mimic appearances that block vision/light */ -#define is_lightblocker_mappear(mon) \ +#define is_lightblocker_mappear(mon) \ (is_obj_mappear(mon, BOULDER) \ - || (M_AP_TYPE(mon) == M_AP_FURNITURE \ + || (M_AP_TYPE(mon) == M_AP_FURNITURE \ && ((mon)->mappearance == S_hcdoor \ || (mon)->mappearance == S_vcdoor \ || (mon)->mappearance < S_ndoor /* = walls */ \ @@ -253,7 +261,9 @@ struct monst { #define engulfing_u(mon) (u.uswallow && (u.ustuck == (mon))) #define helpless(mon) ((mon)->msleeping || !(mon)->mcanmove) -#define mon_offmap(mon) (((mon)->mstate & (MON_DETACH|MON_MIGRATING|MON_LIMBO|MON_OFFMAP)) != 0) +#define mon_perma_blind(mon) (!mon->mcansee && !mon->mblinded) + +#define mon_offmap(mon) ((mon)->mstate != MON_FLOOR) /* Get the maximum difficulty monsters that can currently be generated, given the current level difficulty and the hero's level. */ @@ -273,5 +283,29 @@ struct monst { #ifdef PMNAME_MACROS #define Mgender(mon) ((mon)->female ? FEMALE : MALE) #endif +/* mresists from any source - innate, intrinsic, or extrinsic */ +#define mon_resistancebits(mon) \ + ((mon)->data->mresists | (mon)->mextrinsics | (mon)->mintrinsics) +/* resists_fire() turned into function to handle artifact fire resistance */ +#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 is_lminion(mon) \ + (is_minion((mon)->data) && mon_aligntyp(mon) == A_LAWFUL) + +/* x is a valid index into mons[] array */ +#define ismnum(x) ((x) >= LOW_PM && (x) < NUMMONS) #endif /* MONST_H */ diff --git a/include/monsters.h b/include/monsters.h index ad50d4b507..479b3b2fd2 100644 --- a/include/monsters.h +++ b/include/monsters.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 monsters.h $NHDT-Date: 1665130023 2022/10/07 08:07:03 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.103 $ */ +/* NetHack 3.7 monsters.h $NHDT-Date: 1723945838 2024/08/18 01:50:38 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.124 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -7,17 +7,10 @@ #define MON(nam, sym, lvl, gen, atk, siz, mr1, mr2, flg1, flg2, flg3, d, \ col, bn) PM_##bn -#define MON3(namm, namf, namn, sym, lvl, gen, atk, siz, mr1, mr2, flg1, \ - flg2, flg3, d, col, bn) PM_##bn - #elif defined(DUMP_ENUMS) #define MON(nam, sym, lvl, gen, atk, siz, mr1, mr2, flg1, flg2, flg3, d, \ col, bn) { PM_##bn, #bn} -#define MON3(namm, namf, namn, sym, lvl, gen, atk, siz, mr1, mr2, flg1, \ - flg2, flg3, d, col, bn) { PM_##bn, #bn } - - #elif !defined(MON) #error Non-productive inclusion of monsters.h #endif @@ -33,7 +26,7 @@ * sounds made (MS_* defines), physical size (MZ_* defines), * resistances, resistances conferred (both MR_* defines), * 3 * flag bitmaps (M1_*, M2_*, and M3_* defines respectively), - * difficulty, symbol color (C(x) macro). + * difficulty, symbol color. * * The difficulty was generated in separate array monstr[] with * values calculated by makedefs, but has been moved into mons[] @@ -44,10 +37,10 @@ * then the value(s) can be plugged in here and monstr.c deleted. * [Note that some monsters might warrant manually calculated * difficulty, on a case by case basis, instead of blindly using - * the default value produced by makedefs. Or fix the algoritm + * the default value produced by makedefs. Or fix the algorithm * used by makedefs to generate a more appropriate value....] * - * TODO: difficulty is closely releated to level; its field ought + * TODO: difficulty is closely related to level; its field ought * to be moved sooner in the permonst struct so that it can become * part of LVL() instead of remaining an orphan near the end. * @@ -91,199 +84,237 @@ /* * ants */ - MON("giant ant", S_ANT, LVL(2, 18, 3, 0, 0), (G_GENO | G_SGROUP | 3), - A(ATTK(AT_BITE, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("giant ant"), S_ANT, + LVL(2, 18, 3, 0, 0), (G_GENO | G_SGROUP | 3), + A(ATTK(AT_BITE, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(10, 10, MS_SILENT, MZ_SMALL), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_OVIPAROUS | M1_CARNIVORE, M2_HOSTILE, 0, 4, CLR_BROWN, GIANT_ANT), - MON("killer bee", S_ANT, LVL(1, 18, -1, 0, 0), (G_GENO | G_LGROUP | 2), - A(ATTK(AT_STNG, AD_DRST, 1, 3), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("killer bee"), S_ANT, + LVL(1, 18, -1, 0, 0), (G_GENO | G_LGROUP | 2), + A(ATTK(AT_STNG, AD_DRST, 1, 3), + 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), - MON("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), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + 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), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), 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), - MON("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), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M2_HOSTILE, 0, + 6, 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), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(30, 10, MS_SILENT, MZ_TINY), MR_FIRE, MR_FIRE, M1_ANIMAL | M1_NOHANDS | M1_OVIPAROUS | M1_CARNIVORE, M2_HOSTILE, - M3_INFRAVISIBLE, 6, CLR_RED, FIRE_ANT), - MON("giant beetle", S_ANT, LVL(5, 6, 4, 0, 0), (G_GENO | 3), - A(ATTK(AT_BITE, AD_PHYS, 3, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE, + 6, CLR_RED, FIRE_ANT), + MON(NAM("giant beetle"), S_ANT, + LVL(5, 6, 4, 0, 0), (G_GENO | 3), + A(ATTK(AT_BITE, AD_PHYS, 3, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(200, 50, MS_SILENT, MZ_LARGE), MR_POISON, MR_POISON, M1_ANIMAL | M1_NOHANDS | M1_POIS | M1_CARNIVORE, M2_HOSTILE, 0, 6, CLR_BLACK, GIANT_BEETLE), /* giant fly is no-generate, no-geno because it's more of a feature for * Baalzebub than a random monster */ - MON("giant fly", S_ANT, LVL(1, 20, 3, 0, 0), G_NOGEN, - A(ATTK(AT_BITE, AD_PHYS, 2, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("giant fly"), S_ANT, LVL(1, 20, 3, 0, 0), G_NOGEN, + A(ATTK(AT_BITE, AD_PHYS, 2, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(10, 10, MS_BUZZ, MZ_TINY), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_OVIPAROUS | M1_CARNIVORE | M1_FLY, M2_HOSTILE, 0, 7, CLR_GRAY, GIANT_FLY), - MON("queen bee", S_ANT, LVL(9, 24, -4, 0, 0), (G_GENO | G_NOGEN), - A(ATTK(AT_STNG, AD_DRST, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("queen bee"), S_ANT, + LVL(9, 24, -4, 0, 0), (G_GENO | G_NOGEN), + A(ATTK(AT_STNG, AD_DRST, 1, 8), + 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_OVIPAROUS | M1_POIS, - M2_HOSTILE | M2_FEMALE | M2_PRINCE, 0, 12, HI_LORD, QUEEN_BEE), + M2_HOSTILE | M2_FEMALE | M2_PRINCE, 0, + 12, HI_LORD, QUEEN_BEE), /* * blobs */ - MON("acid blob", S_BLOB, LVL(1, 3, 8, 0, 0), (G_GENO | 2), - A(ATTK(AT_NONE, AD_ACID, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("acid blob"), S_BLOB, + LVL(1, 3, 8, 0, 0), (G_GENO | 2), + A(ATTK(AT_NONE, AD_ACID, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(30, 10, MS_SILENT, MZ_TINY), MR_SLEEP | MR_POISON | MR_ACID | MR_STONE, MR_ACID | MR_STONE, M1_BREATHLESS | M1_AMORPHOUS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_ACID, - M2_WANDER | M2_NEUTER, 0, 2, CLR_GREEN, ACID_BLOB), - MON("quivering blob", S_BLOB, LVL(5, 1, 8, 0, 0), (G_GENO | 2), - A(ATTK(AT_TUCH, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_WANDER | M2_NEUTER, 0, + 2, CLR_GREEN, ACID_BLOB), + MON(NAM("quivering blob"), S_BLOB, + LVL(5, 1, 8, 0, 0), (G_GENO | 2), + A(ATTK(AT_TUCH, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(200, 100, MS_SILENT, MZ_SMALL), MR_SLEEP | MR_POISON, MR_POISON, M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS, - M2_WANDER | M2_HOSTILE | M2_NEUTER, 0, 6, CLR_WHITE, QUIVERING_BLOB), - MON("gelatinous cube", S_BLOB, LVL(6, 6, 8, 0, 0), (G_GENO | 2), - A(ATTK(AT_TUCH, AD_PLYS, 2, 4), ATTK(AT_NONE, AD_PLYS, 1, 4), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M2_WANDER | M2_HOSTILE | M2_NEUTER, 0, + 6, CLR_WHITE, QUIVERING_BLOB), + MON(NAM("gelatinous cube"), S_BLOB, + LVL(6, 6, 8, 0, 0), (G_GENO | 2), + A(ATTK(AT_TUCH, AD_PLYS, 2, 4), ATTK(AT_NONE, AD_PLYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(600, 150, MS_SILENT, MZ_LARGE), MR_FIRE | MR_COLD | MR_ELEC | MR_SLEEP | MR_POISON | MR_ACID | MR_STONE, MR_FIRE | MR_COLD | MR_ELEC | MR_SLEEP, M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_OMNIVORE | M1_ACID, - M2_WANDER | M2_HOSTILE | M2_NEUTER, 0, 8, CLR_CYAN, GELATINOUS_CUBE), + M2_WANDER | M2_HOSTILE | M2_NEUTER, 0, + 8, CLR_CYAN, GELATINOUS_CUBE), /* * cockatrice */ - MON("chickatrice", S_COCKATRICE, LVL(4, 4, 8, 30, 0), - (G_GENO | G_SGROUP | 1), + MON(NAM("chickatrice"), S_COCKATRICE, + LVL(4, 4, 8, 30, 0), (G_GENO | G_SGROUP | 1), A(ATTK(AT_BITE, AD_PHYS, 1, 2), ATTK(AT_TUCH, AD_STON, 0, 0), ATTK(AT_NONE, AD_STON, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(10, 10, MS_HISS, MZ_TINY), MR_POISON | MR_STONE, MR_POISON | MR_STONE, M1_ANIMAL | M1_NOHANDS | M1_OMNIVORE, - M2_HOSTILE, M3_INFRAVISIBLE, 7, CLR_BROWN, CHICKATRICE), - MON("cockatrice", S_COCKATRICE, LVL(5, 6, 6, 30, 0), (G_GENO | 5), + M2_HOSTILE, M3_INFRAVISIBLE, + 7, CLR_BROWN, CHICKATRICE), + MON(NAM("cockatrice"), S_COCKATRICE, + LVL(5, 6, 6, 30, 0), (G_GENO | 5), A(ATTK(AT_BITE, AD_PHYS, 1, 3), ATTK(AT_TUCH, AD_STON, 0, 0), ATTK(AT_NONE, AD_STON, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(30, 30, MS_HISS, MZ_SMALL), MR_POISON | MR_STONE, MR_POISON | MR_STONE, M1_ANIMAL | M1_NOHANDS | M1_OMNIVORE | M1_OVIPAROUS, M2_HOSTILE, - M3_INFRAVISIBLE, 8, CLR_YELLOW, COCKATRICE), - MON("pyrolisk", S_COCKATRICE, LVL(6, 6, 6, 30, 0), (G_GENO | 1), + M3_INFRAVISIBLE, + 8, CLR_YELLOW, COCKATRICE), + MON(NAM("pyrolisk"), S_COCKATRICE, + LVL(6, 6, 6, 30, 0), (G_GENO | 1), A(ATTK(AT_GAZE, AD_FIRE, 2, 6), ATTK(AT_BITE, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(30, 30, MS_HISS, MZ_SMALL), MR_POISON | MR_FIRE, MR_POISON | MR_FIRE, M1_ANIMAL | M1_NOHANDS | M1_OMNIVORE | M1_OVIPAROUS, M2_HOSTILE, - M3_INFRAVISIBLE, 8, CLR_RED, PYROLISK), + M3_INFRAVISIBLE, + 8, CLR_RED, PYROLISK), /* * dogs & other canines */ - MON("jackal", S_DOG, LVL(0, 12, 7, 0, 0), (G_GENO | G_SGROUP | 3), - A(ATTK(AT_BITE, AD_PHYS, 1, 2), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("jackal"), S_DOG, + LVL(0, 12, 7, 0, 0), (G_GENO | G_SGROUP | 3), + A(ATTK(AT_BITE, AD_PHYS, 1, 2), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(300, 250, MS_BARK, MZ_SMALL), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, 1, CLR_BROWN, JACKAL), - MON("fox", S_DOG, LVL(0, 15, 7, 0, 0), (G_GENO | 1), - A(ATTK(AT_BITE, AD_PHYS, 1, 3), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("fox"), S_DOG, + LVL(0, 15, 7, 0, 0), (G_GENO | 1), + A(ATTK(AT_BITE, AD_PHYS, 1, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(300, 250, MS_BARK, MZ_SMALL), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, 1, CLR_RED, FOX), - MON("coyote", S_DOG, LVL(1, 12, 7, 0, 0), (G_GENO | G_SGROUP | 1), - A(ATTK(AT_BITE, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("coyote"), S_DOG, + LVL(1, 12, 7, 0, 0), (G_GENO | G_SGROUP | 1), + A(ATTK(AT_BITE, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(300, 250, MS_BARK, MZ_SMALL), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, 2, CLR_BLUE, COYOTE), - MON("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), + 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, M1_NOHANDS | M1_POIS | M1_REGEN | M1_CARNIVORE, - M2_NOPOLY | M2_WERE | M2_HOSTILE, M3_INFRAVISIBLE, 4, CLR_GREEN, - WEREJACKAL), - MON("little dog", S_DOG, LVL(2, 18, 6, 0, 0), (G_GENO | 1), - A(ATTK(AT_BITE, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_NOPOLY | M2_WERE | M2_HOSTILE, M3_INFRAVISIBLE, + 4, CLR_GREEN, WEREJACKAL), + MON(NAM("little dog"), S_DOG, + LVL(2, 18, 6, 0, 0), (G_GENO | 1), + A(ATTK(AT_BITE, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(150, 150, MS_BARK, MZ_SMALL), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_DOMESTIC, M3_INFRAVISIBLE, 3, HI_DOMESTIC, LITTLE_DOG), - MON("dingo", S_DOG, LVL(4, 16, 5, 0, 0), (G_GENO | 1), - A(ATTK(AT_BITE, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("dingo"), S_DOG, + LVL(4, 16, 5, 0, 0), (G_GENO | 1), + A(ATTK(AT_BITE, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(400, 200, MS_BARK, MZ_MEDIUM), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, 5, CLR_YELLOW, DINGO), - MON("dog", S_DOG, LVL(4, 16, 5, 0, 0), (G_GENO | 1), - A(ATTK(AT_BITE, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("dog"), S_DOG, + LVL(4, 16, 5, 0, 0), (G_GENO | 1), + A(ATTK(AT_BITE, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(400, 200, MS_BARK, MZ_MEDIUM), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_DOMESTIC, M3_INFRAVISIBLE, 5, HI_DOMESTIC, DOG), - MON("large dog", S_DOG, LVL(6, 15, 4, 0, 0), (G_GENO | 1), - A(ATTK(AT_BITE, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("large dog"), S_DOG, + LVL(6, 15, 4, 0, 0), (G_GENO | 1), + A(ATTK(AT_BITE, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(800, 250, MS_BARK, MZ_MEDIUM), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_STRONG | M2_DOMESTIC, - M3_INFRAVISIBLE, 7, HI_DOMESTIC, LARGE_DOG), - MON("wolf", S_DOG, LVL(5, 12, 4, 0, 0), (G_GENO | G_SGROUP | 2), - A(ATTK(AT_BITE, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE, + 7, HI_DOMESTIC, LARGE_DOG), + MON(NAM("wolf"), S_DOG, + LVL(5, 12, 4, 0, 0), (G_GENO | G_SGROUP | 2), + A(ATTK(AT_BITE, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(500, 250, MS_BARK, MZ_MEDIUM), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, 6, CLR_GRAY, WOLF), - MON("werewolf", S_DOG, LVL(5, 12, 4, 20, -7), (G_NOGEN | G_NOCORPSE), - A(ATTK(AT_BITE, AD_WERE, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + /* werewolf in wolf form; one in human form uses a different monster + type (in the S_HUMAN section below); switching back and forth is + a shape-change that sets monst->data to the appropriate monster */ + MON(NAM("werewolf"), S_DOG, + LVL(5, 12, 4, 20, -7), (G_NOGEN | G_NOCORPSE), + A(ATTK(AT_BITE, AD_WERE, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(500, 250, MS_BARK, MZ_MEDIUM), MR_POISON | MR_SLEEP, 0, M1_NOHANDS | M1_POIS | M1_REGEN | M1_CARNIVORE, - M2_NOPOLY | M2_WERE | M2_HOSTILE, M3_INFRAVISIBLE, 7, CLR_ORANGE, - WEREWOLF), - MON("winter wolf cub", S_DOG, LVL(5, 12, 4, 0, -5), + M2_NOPOLY | M2_WERE | M2_HOSTILE, M3_INFRAVISIBLE, + 7, CLR_ORANGE, WEREWOLF), + MON(NAM("winter wolf cub"), S_DOG, + 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), + A(ATTK(AT_BITE, AD_PHYS, 1, 8), ATTK(AT_BREA, AD_COLD, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(250, 200, MS_BARK, MZ_SMALL), MR_COLD, MR_COLD, - M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, 0, 7, CLR_CYAN, - WINTER_WOLF_CUB), - MON("warg", S_DOG, LVL(7, 12, 4, 0, -5), (G_GENO | G_SGROUP | 2), - A(ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, 0, + 7, CLR_CYAN, WINTER_WOLF_CUB), + MON(NAM("warg"), S_DOG, + LVL(7, 12, 4, 0, -5), (G_GENO | G_SGROUP | 2), + A(ATTK(AT_BITE, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(850, 350, MS_BARK, MZ_MEDIUM), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, 8, CLR_BLACK, WARG), - MON("winter wolf", S_DOG, LVL(7, 12, 4, 20, 0), (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), + MON(NAM("winter wolf"), S_DOG, + 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("hell hound pup", S_DOG, LVL(7, 12, 4, 20, -5), - (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), + MON(NAM("hell hound pup"), S_DOG, + 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("hell hound", S_DOG, LVL(12, 14, 2, 20, 0), (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), + MON(NAM("hell hound"), S_DOG, + 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, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE | M2_STRONG, M3_INFRAVISIBLE, 14, CLR_RED, HELL_HOUND), - MON("Cerberus", S_DOG, LVL(15, 15, 2, 80, -7), + MON(NAM("Cerberus"), S_DOG, LVL(15, 15, 2, 80, -7), (G_NOGEN | G_UNIQ | G_HELL), A(ATTK(AT_BITE, AD_PHYS, 5, 6), ATTK(AT_BITE, AD_DRCO, 3, 4), ATTK(AT_BITE, AD_FIRE, 5, 6), ATTK(AT_CLAW, AD_PHYS, 6, 6), @@ -291,530 +322,645 @@ SIZ(1000, 350, MS_BARK, MZ_LARGE), MR_FIRE, MR_FIRE, M1_ANIMAL | M1_NOHANDS | M1_REGEN | M1_CARNIVORE, M2_NOPOLY | M2_HOSTILE | M2_STRONG | M2_PNAME | M2_MALE, - M3_INFRAVISIBLE, 20, CLR_RED, CERBERUS), + M3_INFRAVISIBLE, + 20, CLR_RED, CERBERUS), /* * eyes */ - MON("gas spore", S_EYE, LVL(1, 3, 10, 0, 0), (G_NOCORPSE | G_GENO | 1), - A(ATTK(AT_BOOM, AD_PHYS, 4, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("gas spore"), S_EYE, + LVL(1, 3, 10, 0, 0), (G_NOCORPSE | G_GENO | 1), + A(ATTK(AT_BOOM, AD_PHYS, 4, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(10, 10, MS_SILENT, MZ_SMALL), 0, 0, M1_FLY | M1_BREATHLESS | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS, - M2_HOSTILE | M2_NEUTER, 0, 2, CLR_GRAY, GAS_SPORE), - MON("floating eye", S_EYE, LVL(2, 1, 9, 10, 0), (G_GENO | 5), - A(ATTK(AT_NONE, AD_PLYS, 0, 10), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_HOSTILE | M2_NEUTER, 0, + 2, CLR_GRAY, GAS_SPORE), + MON(NAM("floating eye"), S_EYE, + LVL(2, 1, 9, 10, 0), (G_GENO | 5), + A(ATTK(AT_NONE, AD_PLYS, 0, 10), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(10, 10, MS_SILENT, MZ_SMALL), 0, 0, M1_FLY | M1_AMPHIBIOUS | M1_NOLIMBS | M1_NOHEAD | M1_NOTAKE, - M2_HOSTILE | M2_NEUTER, M3_INFRAVISIBLE, 3, CLR_CYAN, FLOATING_EYE), - MON("freezing sphere", S_EYE, LVL(6, 13, 4, 0, 0), - (G_NOCORPSE | G_NOHELL | G_GENO | 2), - A(ATTK(AT_EXPL, AD_COLD, 4, 6), ATTK(AT_BOOM, AD_COLD, 4, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M2_HOSTILE | M2_NEUTER, M3_INFRAVISIBLE, + 3, CLR_CYAN, FLOATING_EYE), + MON(NAM("freezing sphere"), S_EYE, + LVL(6, 13, 4, 0, 0), (G_NOCORPSE | G_NOHELL | G_GENO | 2), + A(ATTK(AT_EXPL, AD_COLD, 4, 6), ATTK(AT_BOOM, AD_COLD, 4, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(10, 10, MS_SILENT, MZ_SMALL), MR_COLD, MR_COLD, M1_FLY | M1_BREATHLESS | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_NOTAKE, - M2_HOSTILE | M2_NEUTER, M3_INFRAVISIBLE, 9, CLR_WHITE, - FREEZING_SPHERE), - MON("flaming sphere", S_EYE, LVL(6, 13, 4, 0, 0), - (G_NOCORPSE | G_GENO | 2), - A(ATTK(AT_EXPL, AD_FIRE, 4, 6), ATTK(AT_BOOM, AD_FIRE, 4, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M2_HOSTILE | M2_NEUTER, M3_INFRAVISIBLE, + 9, CLR_WHITE, FREEZING_SPHERE), + MON(NAM("flaming sphere"), S_EYE, + LVL(6, 13, 4, 0, 0), (G_NOCORPSE | G_GENO | 2), + A(ATTK(AT_EXPL, AD_FIRE, 4, 6), ATTK(AT_BOOM, AD_FIRE, 4, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(10, 10, MS_SILENT, MZ_SMALL), MR_FIRE, MR_FIRE, M1_FLY | M1_BREATHLESS | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_NOTAKE, M2_HOSTILE | M2_NEUTER, M3_INFRAVISIBLE, 10, CLR_RED, FLAMING_SPHERE), - MON("shocking sphere", S_EYE, LVL(6, 13, 4, 0, 0), - (G_NOCORPSE | G_GENO | 2), - A(ATTK(AT_EXPL, AD_ELEC, 4, 6), ATTK(AT_BOOM, AD_ELEC, 4, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + MON(NAM("shocking sphere"), S_EYE, + LVL(6, 13, 4, 0, 0), (G_NOCORPSE | G_GENO | 2), + A(ATTK(AT_EXPL, AD_ELEC, 4, 6), ATTK(AT_BOOM, AD_ELEC, 4, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(10, 10, MS_SILENT, MZ_SMALL), MR_ELEC, MR_ELEC, M1_FLY | M1_BREATHLESS | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_NOTAKE, - M2_HOSTILE | M2_NEUTER, M3_INFRAVISIBLE, 11, HI_ZAP, SHOCKING_SPHERE), + M2_HOSTILE | M2_NEUTER, M3_INFRAVISIBLE, + 11, HI_ZAP, SHOCKING_SPHERE), #if 0 /* not yet implemented */ - MON("beholder", S_EYE, + MON(NAM("beholder"), S_EYE, LVL(6, 3, 4, 0, -10), (G_GENO | 2), A(ATTK(AT_GAZE, AD_SLOW, 0, 0), ATTK(AT_GAZE, AD_SLEE, 2,25), ATTK(AT_GAZE, AD_DISN, 0, 0), ATTK(AT_GAZE, AD_STON, 0, 0), ATTK(AT_GAZE, AD_CNCL, 2, 4), ATTK(AT_BITE, AD_PHYS, 2, 4)), SIZ(10, 10, MS_SILENT, MZ_SMALL), MR_COLD, 0, M1_FLY | M1_BREATHLESS | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS, - M2_NOPOLY | M2_HOSTILE | M2_NEUTER, M3_INFRAVISIBLE, 13, CLR_BROWN, - BEHOLDER), + M2_NOPOLY | M2_HOSTILE | M2_NEUTER, M3_INFRAVISIBLE, + 13, CLR_BROWN, BEHOLDER), #endif /* * felines */ - MON("kitten", S_FELINE, LVL(2, 18, 6, 0, 0), (G_GENO | 1), + MON(NAM("kitten"), S_FELINE, + LVL(2, 18, 6, 0, 0), (G_GENO | 1), A(ATTK(AT_BITE, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(150, 150, MS_MEW, MZ_SMALL), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_WANDER | M2_DOMESTIC, - M3_INFRAVISIBLE | M3_INFRAVISION, 3, HI_DOMESTIC, KITTEN), - MON("housecat", S_FELINE, LVL(4, 16, 5, 0, 0), (G_GENO | 1), + M3_INFRAVISIBLE | M3_INFRAVISION, + 3, HI_DOMESTIC, KITTEN), + MON(NAM("housecat"), S_FELINE, + LVL(4, 16, 5, 0, 0), (G_GENO | 1), A(ATTK(AT_BITE, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(200, 200, MS_MEW, MZ_SMALL), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_DOMESTIC, - M3_INFRAVISIBLE | M3_INFRAVISION, 5, HI_DOMESTIC, HOUSECAT), - MON("jaguar", S_FELINE, LVL(4, 15, 6, 0, 0), (G_GENO | 2), + M3_INFRAVISIBLE | M3_INFRAVISION, + 5, HI_DOMESTIC, HOUSECAT), + MON(NAM("jaguar"), S_FELINE, + LVL(4, 15, 6, 0, 0), (G_GENO | 2), A(ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_BITE, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(600, 300, MS_GROWL, MZ_LARGE), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, - M3_INFRAVISIBLE | M3_INFRAVISION, 6, CLR_BROWN, JAGUAR), - MON("lynx", S_FELINE, LVL(5, 15, 6, 0, 0), (G_GENO | 1), + M3_INFRAVISIBLE | M3_INFRAVISION, + 6, CLR_BROWN, JAGUAR), + MON(NAM("lynx"), S_FELINE, + LVL(5, 15, 6, 0, 0), (G_GENO | 1), A(ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_BITE, AD_PHYS, 1, 10), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(600, 300, MS_GROWL, MZ_SMALL), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, - M3_INFRAVISIBLE | M3_INFRAVISION, 7, CLR_CYAN, LYNX), - MON("panther", S_FELINE, LVL(5, 15, 6, 0, 0), (G_GENO | 1), + M3_INFRAVISIBLE | M3_INFRAVISION, + 7, CLR_CYAN, LYNX), + MON(NAM("panther"), S_FELINE, + LVL(5, 15, 6, 0, 0), (G_GENO | 1), A(ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_BITE, AD_PHYS, 1, 10), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(600, 300, MS_GROWL, MZ_LARGE), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, - M3_INFRAVISIBLE | M3_INFRAVISION, 7, CLR_BLACK, PANTHER), - MON("large cat", S_FELINE, LVL(6, 15, 4, 0, 0), (G_GENO | 1), + M3_INFRAVISIBLE | M3_INFRAVISION, + 7, CLR_BLACK, PANTHER), + MON(NAM("large cat"), S_FELINE, + LVL(6, 15, 4, 0, 0), (G_GENO | 1), A(ATTK(AT_BITE, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(250, 250, MS_MEW, MZ_SMALL), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_STRONG | M2_DOMESTIC, - M3_INFRAVISIBLE | M3_INFRAVISION, 7, HI_DOMESTIC, LARGE_CAT), - MON("tiger", S_FELINE, LVL(6, 12, 6, 0, 0), (G_GENO | 2), + M3_INFRAVISIBLE | M3_INFRAVISION, + 7, HI_DOMESTIC, LARGE_CAT), + MON(NAM("tiger"), S_FELINE, + LVL(6, 12, 6, 0, 0), (G_GENO | 2), A(ATTK(AT_CLAW, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_PHYS, 2, 4), ATTK(AT_BITE, AD_PHYS, 1, 10), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(600, 300, MS_GROWL, MZ_LARGE), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, - M3_INFRAVISIBLE | M3_INFRAVISION, 8, CLR_YELLOW, TIGER), - MON("displacer beast", S_FELINE, LVL(12, 12, -10, 0, -3), (G_GENO | 1), + M3_INFRAVISIBLE | M3_INFRAVISION, + 8, CLR_YELLOW, TIGER), + MON(NAM("displacer beast"), S_FELINE, + LVL(12, 12, -10, 0, -3), (G_GENO | 1), A(ATTK(AT_CLAW, AD_PHYS, 4, 4), ATTK(AT_CLAW, AD_PHYS, 4, 4), ATTK(AT_BITE, AD_PHYS, 2, 10), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(750, 400, MS_GROWL, MZ_LARGE), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE | M2_NASTY, - M3_INFRAVISIBLE | M3_INFRAVISION | M3_DISPLACES, 14, CLR_BLUE, - DISPLACER_BEAST), + M3_INFRAVISIBLE | M3_INFRAVISION | M3_DISPLACES, + 14, CLR_BLUE, DISPLACER_BEAST), /* * gremlins and gargoyles */ - MON("gremlin", S_GREMLIN, LVL(5, 12, 2, 25, -9), (G_GENO | 2), + MON(NAM("gremlin"), S_GREMLIN, + LVL(5, 12, 2, 25, -9), (G_GENO | 2), A(ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_CLAW, AD_PHYS, 1, 6), - ATTK(AT_BITE, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_CURS, 0, 0), NO_ATTK, - NO_ATTK), + ATTK(AT_BITE, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_CURS, 0, 0), + NO_ATTK, NO_ATTK), SIZ(100, 20, MS_LAUGH, MZ_SMALL), MR_POISON, MR_POISON, M1_SWIM | M1_HUMANOID | M1_POIS, M2_STALK, M3_INFRAVISIBLE, 8, CLR_GREEN, GREMLIN), - MON("gargoyle", S_GREMLIN, LVL(6, 10, -4, 0, -9), (G_GENO | 2), + /* gargoyle and winged gargoyle are inspired by a cheesy made-for-TV + horror movie (starring Cornel Wilde and Jennifer Salt, 1972) */ + MON(NAM("gargoyle"), S_GREMLIN, + LVL(6, 10, -4, 0, -9), (G_GENO | 2), A(ATTK(AT_CLAW, AD_PHYS, 2, 6), ATTK(AT_CLAW, AD_PHYS, 2, 6), ATTK(AT_BITE, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1000, 200, MS_GRUNT, MZ_HUMAN), MR_STONE, MR_STONE, - M1_HUMANOID | M1_THICK_HIDE | M1_BREATHLESS, M2_HOSTILE | M2_STRONG, - 0, 8, CLR_BROWN, GARGOYLE), - MON("winged gargoyle", S_GREMLIN, LVL(9, 15, -2, 0, -12), (G_GENO | 1), + M1_HUMANOID | M1_THICK_HIDE | M1_BREATHLESS, + M2_HOSTILE | M2_STRONG, 0, + 8, CLR_BROWN, GARGOYLE), + MON(NAM("winged gargoyle"), S_GREMLIN, + LVL(9, 15, -2, 0, -12), (G_GENO | 1), A(ATTK(AT_CLAW, AD_PHYS, 3, 6), ATTK(AT_CLAW, AD_PHYS, 3, 6), ATTK(AT_BITE, AD_PHYS, 3, 4), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1200, 300, MS_GRUNT, MZ_HUMAN), MR_STONE, MR_STONE, M1_FLY | M1_HUMANOID | M1_THICK_HIDE | M1_BREATHLESS | M1_OVIPAROUS, - M2_LORD | M2_HOSTILE | M2_STRONG | M2_MAGIC, 0, 11, HI_LORD, - WINGED_GARGOYLE), + M2_LORD | M2_HOSTILE | M2_STRONG | M2_MAGIC, 0, + 11, HI_LORD, WINGED_GARGOYLE), /* * humanoids */ - MON("hobbit", S_HUMANOID, LVL(1, 9, 10, 0, 6), (G_GENO | 2), - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("hobbit"), S_HUMANOID, + LVL(1, 9, 10, 0, 6), (G_GENO | 2), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(500, 200, MS_HUMANOID, MZ_SMALL), 0, 0, M1_HUMANOID | M1_OMNIVORE, - M2_COLLECT, M3_INFRAVISIBLE | M3_INFRAVISION, 2, CLR_GREEN, HOBBIT), - MON("dwarf", S_HUMANOID, LVL(2, 6, 10, 10, 4), (G_GENO | 3), - A(ATTK(AT_WEAP, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_COLLECT, M3_INFRAVISIBLE | M3_INFRAVISION, + 2, CLR_GREEN, HOBBIT), + /* unlike plain human|elf|orc, plain "dwarf" is an ordinary monster */ + MON(NAM("dwarf"), S_HUMANOID, + LVL(2, 6, 10, 10, 4), (G_GENO | 3), + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(900, 300, MS_HUMANOID, MZ_HUMAN), 0, 0, M1_TUNNEL | M1_NEEDPICK | M1_HUMANOID | M1_OMNIVORE, - M2_NOPOLY | M2_DWARF | M2_STRONG | M2_GREEDY | M2_JEWELS | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 4, CLR_RED, DWARF), - MON3("dwarf lord", "dwarf lady", "dwarf leader", - S_HUMANOID, LVL(4, 6, 10, 10, 5), (G_GENO | 2), - A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_WEAP, AD_PHYS, 2, 4), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M2_DWARF | M2_STRONG | M2_GREEDY | M2_JEWELS | M2_COLLECT, + M3_INFRAVISIBLE | M3_INFRAVISION, + 4, CLR_RED, DWARF), + MON(NAMS("dwarf lord", "dwarf lady", "dwarf leader"), S_HUMANOID, + LVL(4, 6, 10, 10, 5), (G_GENO | 2), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(900, 300, MS_HUMANOID, MZ_HUMAN), 0, 0, M1_TUNNEL | M1_NEEDPICK | M1_HUMANOID | M1_OMNIVORE, M2_DWARF | M2_STRONG | M2_LORD | M2_GREEDY | M2_JEWELS | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 6, CLR_BLUE, DWARF_LEADER), - MON3("dwarf king", "dwarf queen", "dwarf ruler", + M3_INFRAVISIBLE | M3_INFRAVISION, + 6, CLR_BLUE, DWARF_LEADER), + MON(NAMS("dwarf king", "dwarf queen", "dwarf ruler"), S_HUMANOID, LVL(6, 6, 10, 20, 6), (G_GENO | 1), - A(ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_WEAP, AD_PHYS, 2, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + A(ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_WEAP, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(900, 300, MS_HUMANOID, MZ_HUMAN), 0, 0, M1_TUNNEL | M1_NEEDPICK | M1_HUMANOID | M1_OMNIVORE, M2_DWARF | M2_STRONG | M2_PRINCE | M2_GREEDY | M2_JEWELS | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 8, HI_LORD, DWARF_RULER), + M3_INFRAVISIBLE | M3_INFRAVISION, + 8, HI_LORD, DWARF_RULER), /* * imps & other minor demons/devils */ - MON("manes", S_IMP, LVL(1, 3, 7, 0, -7), - (G_GENO | G_LGROUP | G_NOCORPSE | 1), + MON(NAM("manes"), S_IMP, + LVL(1, 3, 7, 0, -7), (G_GENO | G_LGROUP | G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_BITE, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(100, 100, MS_SILENT, MZ_SMALL), MR_SLEEP | MR_POISON, 0, M1_POIS, - M2_HOSTILE | M2_STALK, M3_INFRAVISIBLE | M3_INFRAVISION, 3, CLR_RED, - MANES), - MON("homunculus", S_IMP, LVL(2, 12, 6, 10, -7), (G_GENO | 2), - A(ATTK(AT_BITE, AD_SLEE, 1, 3), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_HOSTILE | M2_STALK, M3_INFRAVISIBLE | M3_INFRAVISION, + 3, CLR_RED, MANES), + MON(NAM("homunculus"), S_IMP, + LVL(2, 12, 6, 10, -7), (G_GENO | 2), + A(ATTK(AT_BITE, AD_SLEE, 1, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(60, 100, MS_SILENT, MZ_TINY), MR_SLEEP | MR_POISON, MR_SLEEP | MR_POISON, M1_FLY | M1_POIS | M1_CARNIVORE, M2_STALK, - M3_INFRAVISIBLE | M3_INFRAVISION, 3, CLR_GREEN, HOMUNCULUS), - MON("imp", S_IMP, LVL(3, 12, 2, 20, -7), (G_GENO | 1), - A(ATTK(AT_CLAW, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE | M3_INFRAVISION, + 3, CLR_GREEN, HOMUNCULUS), + MON(NAM("imp"), S_IMP, + LVL(3, 12, 2, 20, -7), (G_GENO | 1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(20, 10, MS_CUSS, MZ_TINY), 0, 0, M1_REGEN | M1_CARNIVORE, - M2_WANDER | M2_STALK, M3_INFRAVISIBLE | M3_INFRAVISION, 4, CLR_ORANGE, - IMP), - MON("lemure", S_IMP, LVL(3, 3, 7, 0, -7), - (G_HELL | G_GENO | G_LGROUP | G_NOCORPSE | 1), - A(ATTK(AT_CLAW, AD_PHYS, 1, 3), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_WANDER | M2_STALK, M3_INFRAVISIBLE | M3_INFRAVISION, + 4, CLR_ORANGE, IMP), + MON(NAM("lemure"), S_IMP, + LVL(3, 3, 7, 0, -7), (G_HELL | G_GENO | G_LGROUP | G_NOCORPSE | 1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(150, 100, MS_SILENT, MZ_SMALL), MR_SLEEP | MR_POISON, MR_SLEEP, M1_POIS | M1_REGEN, M2_HOSTILE | M2_WANDER | M2_STALK | M2_NEUTER, - M3_INFRAVISIBLE | M3_INFRAVISION, 5, CLR_BROWN, LEMURE), - MON("tengu", S_IMP, LVL(6, 13, 5, 30, 7), (G_GENO | 3), - A(ATTK(AT_BITE, AD_PHYS, 1, 7), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE | M3_INFRAVISION, + 5, CLR_BROWN, LEMURE), + MON(NAM("tengu"), S_IMP, + LVL(6, 13, 5, 30, 7), (G_GENO | 3), + A(ATTK(AT_BITE, AD_PHYS, 1, 7), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(300, 200, MS_SQAWK, MZ_SMALL), MR_POISON, MR_POISON, M1_TPORT | M1_TPORT_CNTRL | M1_CARNIVORE, M2_STALK, - M3_INFRAVISIBLE | M3_INFRAVISION, 7, CLR_CYAN, TENGU), - MON("quasit", S_IMP, LVL(3, 18, 2, 20, -7), (G_GENO | G_SGROUP | 2), - A(ATTK(AT_CLAW, AD_DRDX, 1, 4), ATTK(AT_CLAW, AD_DRDX, 1, 4), + M3_INFRAVISIBLE | M3_INFRAVISION, + 7, CLR_CYAN, TENGU), + MON(NAM("quasit"), S_IMP, + LVL(3, 15, 2, 20, -7), (G_GENO | 2), + A(ATTK(AT_CLAW, AD_DRDX, 1, 2), ATTK(AT_CLAW, AD_DRDX, 1, 2), ATTK(AT_BITE, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(200, 200, MS_SILENT, MZ_SMALL), MR_POISON, MR_POISON, M1_REGEN | M1_CARNIVORE | M1_SEE_INVIS, M2_STALK, - M3_INFRAVISIBLE | M3_INFRAVISION, 9, CLR_BLUE, QUASIT), + M3_INFRAVISIBLE | M3_INFRAVISION, + 9, CLR_BLUE, QUASIT), /* * jellies */ - MON("blue jelly", S_JELLY, 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), + MON(NAM("blue jelly"), S_JELLY, + 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, MR_COLD | MR_POISON, M1_BREATHLESS | M1_AMORPHOUS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_NOTAKE, - M2_HOSTILE | M2_NEUTER, 0, 5, CLR_BLUE, BLUE_JELLY), - MON("spotted jelly", S_JELLY, LVL(5, 0, 8, 10, 0), (G_GENO | 1), + M2_HOSTILE | M2_NEUTER, 0, + 5, CLR_BLUE, BLUE_JELLY), + MON(NAM("spotted jelly"), S_JELLY, + 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), MR_ACID | MR_STONE, MR_ACID | MR_STONE, M1_BREATHLESS | M1_AMORPHOUS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_ACID | M1_NOTAKE, - M2_HOSTILE | M2_NEUTER, 0, 6, CLR_GREEN, SPOTTED_JELLY), - MON("ochre jelly", S_JELLY, LVL(6, 3, 8, 20, 0), (G_GENO | 2), + M2_HOSTILE | M2_NEUTER, 0, + 6, CLR_GREEN, SPOTTED_JELLY), + MON(NAM("ochre jelly"), S_JELLY, + 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), MR_ACID | MR_STONE, MR_ACID | MR_STONE, M1_BREATHLESS | M1_AMORPHOUS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_ACID | M1_NOTAKE, - M2_HOSTILE | M2_NEUTER, 0, 8, CLR_BROWN, OCHRE_JELLY), + M2_HOSTILE | M2_NEUTER, 0, + 8, CLR_BROWN, OCHRE_JELLY), /* * kobolds */ - MON("kobold", S_KOBOLD, LVL(0, 6, 10, 0, -2), (G_GENO | 1), - A(ATTK(AT_WEAP, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("kobold"), S_KOBOLD, + LVL(0, 6, 10, 0, -2), (G_GENO | 1), + A(ATTK(AT_WEAP, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(400, 100, MS_ORC, MZ_SMALL), MR_POISON, 0, M1_HUMANOID | M1_POIS | M1_OMNIVORE, M2_HOSTILE | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 1, CLR_BROWN, KOBOLD), - MON("large kobold", S_KOBOLD, LVL(1, 6, 10, 0, -3), (G_GENO | 1), - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE | M3_INFRAVISION, + 1, CLR_BROWN, KOBOLD), + MON(NAM("large kobold"), S_KOBOLD, + LVL(1, 6, 10, 0, -3), (G_GENO | 1), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(450, 150, MS_ORC, MZ_SMALL), MR_POISON, 0, M1_HUMANOID | M1_POIS | M1_OMNIVORE, M2_HOSTILE | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 2, CLR_RED, LARGE_KOBOLD), - MON3("kobold lord", "kobold lady", "kobold leader", - S_KOBOLD, LVL(2, 6, 10, 0, -4), (G_GENO | 1), - A(ATTK(AT_WEAP, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE | M3_INFRAVISION, + 2, CLR_RED, LARGE_KOBOLD), + MON(NAMS("kobold lord", "kobold lady", "kobold leader"), S_KOBOLD, + LVL(2, 6, 10, 0, -4), (G_GENO | 1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(500, 200, MS_ORC, MZ_SMALL), MR_POISON, 0, M1_HUMANOID | M1_POIS | M1_OMNIVORE, M2_HOSTILE | M2_LORD | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 3, HI_LORD, KOBOLD_LEADER), - MON("kobold shaman", S_KOBOLD, LVL(2, 6, 6, 10, -4), (G_GENO | 1), - A(ATTK(AT_MAGC, AD_SPEL, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE | M3_INFRAVISION, + 3, HI_LORD, KOBOLD_LEADER), + MON(NAM("kobold shaman"), S_KOBOLD, + LVL(2, 6, 6, 10, -4), (G_GENO | 1), + A(ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(450, 150, MS_ORC, MZ_SMALL), MR_POISON, 0, M1_HUMANOID | M1_POIS | M1_OMNIVORE, M2_HOSTILE | M2_MAGIC, - M3_INFRAVISIBLE | M3_INFRAVISION, 4, HI_ZAP, KOBOLD_SHAMAN), + M3_INFRAVISIBLE | M3_INFRAVISION, + 4, HI_ZAP, KOBOLD_SHAMAN), /* * leprechauns */ - MON("leprechaun", S_LEPRECHAUN, LVL(5, 15, 8, 20, 0), (G_GENO | 4), - A(ATTK(AT_CLAW, AD_SGLD, 1, 2), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("leprechaun"), S_LEPRECHAUN, + LVL(5, 15, 8, 20, 0), (G_GENO | 4), + A(ATTK(AT_CLAW, AD_SGLD, 1, 2), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(60, 30, MS_LAUGH, MZ_TINY), 0, 0, M1_HUMANOID | M1_TPORT | M1_CARNIVORE, M2_HOSTILE | M2_GREEDY, - M3_INFRAVISIBLE, 4, CLR_GREEN, LEPRECHAUN), + M3_INFRAVISIBLE, + 4, CLR_GREEN, LEPRECHAUN), /* * mimics */ - MON("small mimic", S_MIMIC, LVL(7, 3, 7, 0, 0), (G_GENO | 2), - A(ATTK(AT_CLAW, AD_PHYS, 3, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("small mimic"), S_MIMIC, + LVL(7, 3, 7, 0, 0), (G_GENO | 2), + A(ATTK(AT_CLAW, AD_PHYS, 3, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(300, 200, MS_SILENT, MZ_MEDIUM), MR_ACID, 0, M1_BREATHLESS | M1_AMORPHOUS | M1_HIDE | M1_ANIMAL | M1_NOEYES | M1_NOHEAD | M1_NOLIMBS | M1_THICK_HIDE | M1_CARNIVORE, - M2_HOSTILE, 0, 8, CLR_BROWN, SMALL_MIMIC), - MON("large mimic", S_MIMIC, LVL(8, 3, 7, 10, 0), (G_GENO | 1), - A(ATTK(AT_CLAW, AD_STCK, 3, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_HOSTILE, 0, + 8, CLR_BROWN, SMALL_MIMIC), + MON(NAM("large mimic"), S_MIMIC, + LVL(8, 3, 7, 10, 0), (G_GENO | 1), + A(ATTK(AT_CLAW, AD_STCK, 3, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(600, 400, MS_SILENT, MZ_LARGE), MR_ACID, 0, M1_CLING | M1_BREATHLESS | M1_AMORPHOUS | M1_HIDE | M1_ANIMAL | M1_NOEYES | M1_NOHEAD | M1_NOLIMBS | M1_THICK_HIDE | M1_CARNIVORE, - M2_HOSTILE | M2_STRONG, 0, 9, CLR_RED, LARGE_MIMIC), - MON("giant mimic", S_MIMIC, LVL(9, 3, 7, 20, 0), (G_GENO | 1), - A(ATTK(AT_CLAW, AD_STCK, 3, 6), ATTK(AT_CLAW, AD_STCK, 3, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M2_HOSTILE | M2_STRONG, 0, + 9, CLR_RED, LARGE_MIMIC), + MON(NAM("giant mimic"), S_MIMIC, + LVL(9, 3, 7, 20, 0), (G_GENO | 1), + A(ATTK(AT_CLAW, AD_STCK, 3, 6), ATTK(AT_CLAW, AD_STCK, 3, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(800, 500, MS_SILENT, MZ_LARGE), MR_ACID, 0, M1_CLING | M1_BREATHLESS | M1_AMORPHOUS | M1_HIDE | M1_ANIMAL | M1_NOEYES | M1_NOHEAD | M1_NOLIMBS | M1_THICK_HIDE | M1_CARNIVORE, - M2_HOSTILE | M2_STRONG, 0, 11, HI_LORD, GIANT_MIMIC), + M2_HOSTILE | M2_STRONG, 0, + 11, HI_LORD, GIANT_MIMIC), /* * nymphs */ - MON("wood nymph", S_NYMPH, LVL(3, 10, 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), + MON(NAM("wood nymph"), S_NYMPH, + LVL(3, 10, 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 | M1_HERBIVORE, - M2_HOSTILE | M2_FEMALE | M2_COLLECT, M3_INFRAVISIBLE, 5, CLR_GREEN, - WOOD_NYMPH), - MON("water nymph", S_NYMPH, LVL(4, 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), + M2_HOSTILE | M2_FEMALE | M2_COLLECT, M3_INFRAVISIBLE, + 5, CLR_GREEN, WOOD_NYMPH), + MON(NAM("water nymph"), S_NYMPH, + LVL(4, 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 | M1_SWIM | M1_HERBIVORE, - M2_HOSTILE | M2_FEMALE | M2_COLLECT, M3_INFRAVISIBLE, 6, CLR_BLUE, - WATER_NYMPH), - MON("mountain nymph", S_NYMPH, LVL(5, 15, 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), + M2_HOSTILE | M2_FEMALE | M2_COLLECT, M3_INFRAVISIBLE, + 6, CLR_BLUE, WATER_NYMPH), + MON(NAM("mountain nymph"), S_NYMPH, + LVL(5, 15, 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 | M1_HERBIVORE, - M2_HOSTILE | M2_FEMALE | M2_COLLECT, M3_INFRAVISIBLE, 7, CLR_BROWN, - MOUNTAIN_NYMPH), + M2_HOSTILE | M2_FEMALE | M2_COLLECT, M3_INFRAVISIBLE, + 7, CLR_BROWN, MOUNTAIN_NYMPH), /* * orcs */ - MON("goblin", S_ORC, LVL(0, 6, 10, 0, -3), (G_GENO | G_SGROUP | 2), + MON(NAM("goblin"), S_ORC, + LVL(0, 6, 10, 0, -3), (G_GENO | G_SGROUP | 2), A(ATTK(AT_WEAP, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(400, 100, MS_ORC, MZ_SMALL), 0, 0, M1_HUMANOID | M1_OMNIVORE, - M2_ORC | M2_COLLECT, M3_INFRAVISIBLE | M3_INFRAVISION, 1, CLR_GRAY, - GOBLIN), - MON("hobgoblin", S_ORC, LVL(1, 9, 10, 0, -4), (G_GENO | 2), + M2_ORC | M2_COLLECT, M3_INFRAVISIBLE | M3_INFRAVISION, + 1, CLR_GRAY, GOBLIN), + MON(NAM("hobgoblin"), S_ORC, LVL(1, 9, 10, 0, -4), (G_GENO | 2), A(ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1000, 200, MS_ORC, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_ORC | M2_STRONG | M2_COLLECT, M3_INFRAVISIBLE | M3_INFRAVISION, 3, CLR_BROWN, HOBGOBLIN), - /* plain "orc" for zombie corpses only; not created at random; - * orcs (but not goblins and hobgoblins) are granted poison resistance; - * however, their corpses don't confer it - */ - MON("orc", S_ORC, LVL(1, 9, 10, 0, -3), (G_GENO | G_NOGEN | G_LGROUP), + /* Plain "orc" for zombie and mummy corpses only; not created at random. + * Orcs (but not goblins and hobgoblins) are granted poison resistance; + * however, their corpses don't confer it. */ + MON(NAM("orc"), S_ORC, + LVL(1, 9, 10, 0, -3), (G_GENO | G_NOGEN | G_LGROUP), A(ATTK(AT_WEAP, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(850, 150, MS_ORC, MZ_HUMAN), MR_POISON, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_ORC | M2_STRONG | M2_GREEDY | M2_JEWELS | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 3, CLR_BLUE, ORC), - MON("hill orc", S_ORC, LVL(2, 9, 10, 0, -4), (G_GENO | G_LGROUP | 2), + M3_INFRAVISIBLE | M3_INFRAVISION, + 3, CLR_BLUE, ORC), + MON(NAM("hill orc"), S_ORC, + LVL(2, 9, 10, 0, -4), (G_GENO | G_LGROUP | 2), A(ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1000, 200, MS_ORC, MZ_HUMAN), MR_POISON, 0, M1_HUMANOID | M1_OMNIVORE, M2_ORC | M2_STRONG | M2_GREEDY | M2_JEWELS | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 4, CLR_YELLOW, HILL_ORC), - MON("bugbear", S_ORC, LVL(3, 9, 5, 0, -6), (G_GENO | 1), + M3_INFRAVISIBLE | M3_INFRAVISION, + 4, CLR_YELLOW, HILL_ORC), + MON(NAM("bugbear"), S_ORC, + LVL(3, 9, 5, 0, -6), (G_GENO | 1), A(ATTK(AT_WEAP, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1250, 250, MS_ORC, MZ_LARGE), MR_POISON, 0, M1_HUMANOID | M1_OMNIVORE, M2_ORC | M2_STRONG | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 4, CLR_ORANGE, BUGBEAR), - MON("Mordor orc", S_ORC, LVL(3, 5, 10, 0, -5), (G_GENO | G_LGROUP | 1), + M3_INFRAVISIBLE | M3_INFRAVISION, + 5, CLR_BROWN, BUGBEAR), + MON(NAM("Mordor orc"), S_ORC, + LVL(3, 5, 10, 0, -5), (G_GENO | G_LGROUP | 1), A(ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1200, 200, MS_ORC, MZ_HUMAN), MR_POISON, 0, M1_HUMANOID | M1_OMNIVORE, M2_ORC | M2_STRONG | M2_GREEDY | M2_JEWELS | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 5, CLR_RED, MORDOR_ORC), - MON("Uruk-hai", S_ORC, LVL(3, 7, 10, 0, -4), (G_GENO | G_LGROUP | 1), + M3_INFRAVISIBLE | M3_INFRAVISION, + 5, CLR_RED, MORDOR_ORC), + MON(NAM("Uruk-hai"), S_ORC, + LVL(3, 7, 10, 0, -4), (G_GENO | G_LGROUP | 1), A(ATTK(AT_WEAP, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1300, 300, MS_ORC, MZ_HUMAN), MR_POISON, 0, M1_HUMANOID | M1_OMNIVORE, M2_ORC | M2_STRONG | M2_GREEDY | M2_JEWELS | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 5, CLR_BLACK, URUK_HAI), - MON("orc shaman", S_ORC, LVL(3, 9, 5, 10, -5), (G_GENO | 1), + M3_INFRAVISIBLE | M3_INFRAVISION, + 5, CLR_BLACK, URUK_HAI), + MON(NAM("orc shaman"), S_ORC, + LVL(3, 9, 5, 10, -5), (G_GENO | 1), A(ATTK(AT_MAGC, AD_SPEL, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1000, 300, MS_ORC, MZ_HUMAN), MR_POISON, 0, M1_HUMANOID | M1_OMNIVORE, M2_ORC | M2_GREEDY | M2_JEWELS | M2_MAGIC, - M3_INFRAVISIBLE | M3_INFRAVISION, 5, HI_ZAP, ORC_SHAMAN), - MON("orc-captain", S_ORC, LVL(9, 5, 10, 0, -5), (G_GENO | 1), + M3_INFRAVISIBLE | M3_INFRAVISION, + 5, HI_ZAP, ORC_SHAMAN), + MON(NAM("orc-captain"), S_ORC, + LVL(5, 5, 10, 0, -5), (G_GENO | 1), A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_WEAP, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1350, 350, MS_ORC, MZ_HUMAN), MR_POISON, 0, M1_HUMANOID | M1_OMNIVORE, M2_ORC | M2_STRONG | M2_GREEDY | M2_JEWELS | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 7, HI_LORD, ORC_CAPTAIN), + M3_INFRAVISIBLE | M3_INFRAVISION, + 7, HI_LORD, ORC_CAPTAIN), /* * piercers */ - MON("rock piercer", S_PIERCER, LVL(3, 1, 3, 0, 0), (G_GENO | 4), - A(ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("rock piercer"), S_PIERCER, + LVL(3, 1, 3, 0, 0), (G_GENO | 4), + A(ATTK(AT_BITE, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(200, 200, MS_SILENT, MZ_SMALL), 0, 0, M1_CLING | M1_HIDE | M1_ANIMAL | M1_NOEYES | M1_NOLIMBS | M1_CARNIVORE | M1_NOTAKE, - M2_HOSTILE, 0, 4, CLR_GRAY, ROCK_PIERCER), - MON("iron piercer", S_PIERCER, LVL(5, 1, 0, 0, 0), (G_GENO | 2), - A(ATTK(AT_BITE, AD_PHYS, 3, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_HOSTILE, 0, + 4, CLR_GRAY, ROCK_PIERCER), + MON(NAM("iron piercer"), S_PIERCER, + LVL(5, 1, 0, 0, 0), (G_GENO | 2), + A(ATTK(AT_BITE, AD_PHYS, 3, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(400, 300, MS_SILENT, MZ_MEDIUM), 0, 0, M1_CLING | M1_HIDE | M1_ANIMAL | M1_NOEYES | M1_NOLIMBS | M1_CARNIVORE | M1_NOTAKE, - M2_HOSTILE, 0, 6, CLR_CYAN, IRON_PIERCER), - MON("glass piercer", S_PIERCER, LVL(7, 1, 0, 0, 0), (G_GENO | 1), - A(ATTK(AT_BITE, AD_PHYS, 4, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_HOSTILE, 0, + 6, CLR_CYAN, IRON_PIERCER), + MON(NAM("glass piercer"), S_PIERCER, + LVL(7, 1, 0, 0, 0), (G_GENO | 1), + A(ATTK(AT_BITE, AD_PHYS, 4, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(400, 300, MS_SILENT, MZ_MEDIUM), MR_ACID, 0, M1_CLING | M1_HIDE | M1_ANIMAL | M1_NOEYES | M1_NOLIMBS | M1_CARNIVORE | M1_NOTAKE, - M2_HOSTILE, 0, 9, CLR_WHITE, GLASS_PIERCER), + M2_HOSTILE, 0, + 9, CLR_WHITE, GLASS_PIERCER), /* * quadrupeds + * (note: horses are grouped with unicorns in another section below) */ - MON("rothe", S_QUADRUPED, LVL(2, 9, 7, 0, 0), (G_GENO | G_SGROUP | 4), + MON(NAM("rothe"), S_QUADRUPED, + LVL(2, 9, 7, 0, 0), (G_GENO | G_SGROUP | 4), A(ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_BITE, AD_PHYS, 1, 3), ATTK(AT_BITE, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(400, 100, MS_MOO, MZ_LARGE), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_OMNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, 4, CLR_BROWN, ROTHE), - MON("leocrotta", S_QUADRUPED, LVL(6, 18, 4, 10, 0), (G_GENO | 2), + MON(NAM("leocrotta"), S_QUADRUPED, + LVL(6, 18, 4, 10, 0), (G_GENO | 2), A(ATTK(AT_CLAW, AD_PHYS, 2, 6), ATTK(AT_BITE, AD_PHYS, 2, 6), ATTK(AT_CLAW, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1200, 500, MS_IMITATE, MZ_LARGE), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_OMNIVORE, M2_HOSTILE | M2_STRONG, - M3_INFRAVISIBLE, 8, CLR_RED, LEOCROTTA), - MON("wumpus", S_QUADRUPED, LVL(8, 3, 2, 10, 0), (G_GENO | 1), - A(ATTK(AT_BITE, AD_PHYS, 3, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE, + 8, CLR_RED, LEOCROTTA), + MON(NAM("wumpus"), S_QUADRUPED, + LVL(8, 3, 2, 10, 0), (G_GENO | 1), + A(ATTK(AT_BITE, AD_PHYS, 3, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2500, 500, MS_BURBLE, MZ_LARGE), 0, 0, M1_CLING | M1_ANIMAL | M1_NOHANDS | M1_OMNIVORE, - M2_HOSTILE | M2_STRONG, M3_INFRAVISIBLE, 9, CLR_CYAN, WUMPUS), - MON("titanothere", S_QUADRUPED, LVL(12, 12, 6, 0, 0), (G_GENO | 2), - A(ATTK(AT_CLAW, AD_PHYS, 2, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_HOSTILE | M2_STRONG, M3_INFRAVISIBLE, + 9, CLR_CYAN, WUMPUS), + MON(NAM("titanothere"), S_QUADRUPED, + LVL(12, 12, 6, 0, 0), (G_GENO | 2), + A(ATTK(AT_CLAW, AD_PHYS, 2, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2650, 650, MS_BELLOW, MZ_LARGE), 0, 0, M1_ANIMAL | M1_THICK_HIDE | M1_NOHANDS | M1_HERBIVORE, - M2_HOSTILE | M2_STRONG, M3_INFRAVISIBLE, 13, CLR_YELLOW, - TITANOTHERE), - MON("baluchitherium", S_QUADRUPED, LVL(14, 12, 5, 0, 0), (G_GENO | 2), - A(ATTK(AT_CLAW, AD_PHYS, 5, 4), ATTK(AT_CLAW, AD_PHYS, 5, 4), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M2_HOSTILE | M2_STRONG, M3_INFRAVISIBLE, + 13, CLR_YELLOW, TITANOTHERE), + MON(NAM("baluchitherium"), S_QUADRUPED, + LVL(14, 12, 5, 0, 0), (G_GENO | 2), + A(ATTK(AT_CLAW, AD_PHYS, 5, 4), ATTK(AT_CLAW, AD_PHYS, 5, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(3800, 800, MS_BELLOW, MZ_LARGE), 0, 0, M1_ANIMAL | M1_THICK_HIDE | M1_NOHANDS | M1_HERBIVORE, - M2_HOSTILE | M2_STRONG, M3_INFRAVISIBLE, 15, CLR_ORANGE, - BALUCHITHERIUM), - MON("mumak", S_QUADRUPED, LVL(10, 9, 0, 0, -2), (G_GENO | 1), + M2_HOSTILE | M2_STRONG, M3_INFRAVISIBLE, + 15, CLR_ORANGE, BALUCHITHERIUM), + MON(NAM("mumak"), S_QUADRUPED, + LVL(10, 9, 0, 0, -2), (G_GENO | 1), A(ATTK(AT_BUTT, AD_PHYS, 4, 12), ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2500, 500, MS_TRUMPET, MZ_GIGANTIC), 0, 0, M1_ANIMAL | M1_THICK_HIDE | M1_NOHANDS | M1_HERBIVORE, - M2_HOSTILE | M2_STRONG, M3_INFRAVISIBLE, 15, CLR_GRAY, MUMAK), - MON("mastodon", S_QUADRUPED, LVL(20, 12, 5, 0, 0), (G_GENO | 1), - A(ATTK(AT_BUTT, AD_PHYS, 4, 8), ATTK(AT_BUTT, AD_PHYS, 4, 8), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M2_HOSTILE | M2_STRONG, M3_INFRAVISIBLE, + 15, CLR_GRAY, MUMAK), + MON(NAM("mastodon"), S_QUADRUPED, + LVL(20, 12, 5, 0, 0), (G_GENO | 1), + A(ATTK(AT_BUTT, AD_PHYS, 4, 8), ATTK(AT_BUTT, AD_PHYS, 4, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(3800, 800, MS_TRUMPET, MZ_HUGE), 0, 0, M1_ANIMAL | M1_THICK_HIDE | M1_NOHANDS | M1_HERBIVORE, - M2_HOSTILE | M2_STRONG, M3_INFRAVISIBLE, 22, CLR_BLACK, MASTODON), + M2_HOSTILE | M2_STRONG, M3_INFRAVISIBLE, + 22, CLR_BLACK, MASTODON), /* * rodents */ - MON("sewer rat", S_RODENT, LVL(0, 12, 7, 0, 0), (G_GENO | G_SGROUP | 1), - A(ATTK(AT_BITE, AD_PHYS, 1, 3), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("sewer rat"), S_RODENT, + LVL(0, 12, 7, 0, 0), (G_GENO | G_SGROUP | 1), + A(ATTK(AT_BITE, AD_PHYS, 1, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(20, 12, MS_SQEEK, MZ_TINY), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE | M1_CONCEAL, M2_HOSTILE, - M3_INFRAVISIBLE, 1, CLR_BROWN, SEWER_RAT), - MON("giant rat", S_RODENT, LVL(1, 10, 7, 0, 0), (G_GENO | G_SGROUP | 2), - A(ATTK(AT_BITE, AD_PHYS, 1, 3), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE, + 1, CLR_BROWN, SEWER_RAT), + MON(NAM("giant rat"), S_RODENT, + LVL(1, 10, 7, 0, 0), (G_GENO | G_SGROUP | 2), + A(ATTK(AT_BITE, AD_PHYS, 1, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(30, 30, MS_SQEEK, MZ_SMALL), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, 2, CLR_RED, GIANT_RAT), - MON("rabid rat", S_RODENT, LVL(2, 12, 6, 0, 0), (G_GENO | 1), - A(ATTK(AT_BITE, AD_DRCO, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("rabid rat"), S_RODENT, + LVL(2, 12, 6, 0, 0), (G_GENO | 1), + A(ATTK(AT_BITE, AD_DRCO, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(30, 5, MS_SQEEK, MZ_TINY), MR_POISON, 0, M1_ANIMAL | M1_NOHANDS | M1_POIS | M1_CARNIVORE | M1_CONCEAL, - M2_HOSTILE, M3_INFRAVISIBLE, 4, CLR_GREEN, RABID_RAT), - MON("wererat", S_RODENT, LVL(2, 12, 6, 10, -7), (G_NOGEN | G_NOCORPSE), - A(ATTK(AT_BITE, AD_WERE, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_HOSTILE, M3_INFRAVISIBLE, + 4, CLR_GREEN, RABID_RAT), + MON(NAM("wererat"), S_RODENT, + LVL(2, 12, 6, 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(40, 30, MS_SQEEK, MZ_TINY), MR_POISON, 0, M1_NOHANDS | M1_POIS | M1_REGEN | M1_CARNIVORE, - M2_NOPOLY | M2_WERE | M2_HOSTILE, M3_INFRAVISIBLE, 4, CLR_YELLOW, - WERERAT), - MON("rock mole", S_RODENT, LVL(3, 3, 0, 20, 0), (G_GENO | 2), - A(ATTK(AT_BITE, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_NOPOLY | M2_WERE | M2_HOSTILE, M3_INFRAVISIBLE, + 4, CLR_YELLOW, WERERAT), + MON(NAM("rock mole"), S_RODENT, + LVL(3, 3, 0, 20, 0), (G_GENO | 2), + A(ATTK(AT_BITE, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(30, 30, MS_SILENT, MZ_SMALL), 0, 0, M1_TUNNEL | M1_ANIMAL | M1_NOHANDS | M1_METALLIVORE, M2_HOSTILE | M2_GREEDY | M2_JEWELS | M2_COLLECT, M3_INFRAVISIBLE, 4, CLR_GRAY, ROCK_MOLE), - MON("woodchuck", S_RODENT, LVL(3, 3, 0, 20, 0), (G_NOGEN | G_GENO), - A(ATTK(AT_BITE, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("woodchuck"), S_RODENT, + LVL(3, 3, 0, 20, 0), (G_NOGEN | G_GENO), + A(ATTK(AT_BITE, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(30, 30, MS_SILENT, MZ_SMALL), 0, 0, M1_TUNNEL /*LOGGING*/ | M1_ANIMAL | M1_NOHANDS | M1_SWIM | M1_HERBIVORE, /* In reality, they tunnel instead of cutting lumber. Oh, well. */ - M2_WANDER | M2_HOSTILE, M3_INFRAVISIBLE, 4, CLR_BROWN, WOODCHUCK), + M2_WANDER | M2_HOSTILE, M3_INFRAVISIBLE, + 4, CLR_BROWN, WOODCHUCK), /* * spiders & scorpions (keep webmaker() in sync if new critters are added) */ - MON("cave spider", S_SPIDER, LVL(1, 12, 3, 0, 0), (G_GENO | G_SGROUP | 2), - A(ATTK(AT_BITE, AD_PHYS, 1, 2), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("cave spider"), S_SPIDER, + LVL(1, 12, 3, 0, 0), (G_GENO | G_SGROUP | 2), + A(ATTK(AT_BITE, AD_PHYS, 1, 2), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(50, 50, MS_SILENT, MZ_TINY), MR_POISON, MR_POISON, M1_CONCEAL | M1_ANIMAL | M1_NOHANDS | M1_OVIPAROUS | M1_CARNIVORE, - M2_HOSTILE, 0, 3, CLR_GRAY, CAVE_SPIDER), - MON("centipede", S_SPIDER, LVL(2, 4, 3, 0, 0), (G_GENO | 1), - A(ATTK(AT_BITE, AD_DRST, 1, 3), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_HOSTILE, 0, + 3, CLR_GRAY, CAVE_SPIDER), + MON(NAM("centipede"), S_SPIDER, + LVL(2, 4, 3, 0, 0), (G_GENO | 1), + A(ATTK(AT_BITE, AD_DRST, 1, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(50, 50, MS_SILENT, MZ_SMALL), MR_POISON, MR_POISON, M1_CONCEAL | M1_ANIMAL | M1_NOHANDS | M1_OVIPAROUS | M1_CARNIVORE, - M2_HOSTILE, 0, 4, CLR_YELLOW, CENTIPEDE), - MON("giant spider", S_SPIDER, LVL(5, 15, 4, 0, 0), (G_GENO | 1), - A(ATTK(AT_BITE, AD_DRST, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_HOSTILE, 0, + 4, CLR_YELLOW, CENTIPEDE), + MON(NAM("giant spider"), S_SPIDER, + LVL(5, 15, 4, 0, 0), (G_GENO | 1), + A(ATTK(AT_BITE, AD_DRST, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(200, 100, MS_SILENT, MZ_LARGE), MR_POISON, MR_POISON, M1_ANIMAL | M1_NOHANDS | M1_OVIPAROUS | M1_POIS | M1_CARNIVORE, - M2_HOSTILE | M2_STRONG, 0, 7, CLR_MAGENTA, GIANT_SPIDER), - MON("scorpion", S_SPIDER, LVL(5, 15, 3, 0, 0), (G_GENO | 2), + M2_HOSTILE | M2_STRONG, 0, + 7, CLR_MAGENTA, GIANT_SPIDER), + MON(NAM("scorpion"), S_SPIDER, + LVL(5, 15, 3, 0, 0), (G_GENO | 2), A(ATTK(AT_CLAW, AD_PHYS, 1, 2), ATTK(AT_CLAW, AD_PHYS, 1, 2), ATTK(AT_STNG, AD_DRST, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(50, 100, MS_SILENT, MZ_SMALL), MR_POISON, MR_POISON, M1_CONCEAL | M1_ANIMAL | M1_NOHANDS | M1_OVIPAROUS | M1_POIS | M1_CARNIVORE, - M2_HOSTILE, 0, 8, CLR_RED, SCORPION), + M2_HOSTILE, 0, + 8, CLR_RED, SCORPION), /* * trappers, lurkers, &c * Note: prior to 3.7, these were defined to do AD_DGST damage, @@ -823,238 +969,282 @@ * The Monster Manual states that someone engulfed by a trapper * can't use weapons but we do not enforce that. */ - MON("lurker above", S_TRAPPER, LVL(10, 3, 3, 0, 0), (G_GENO | 2), + MON(NAM("lurker above"), S_TRAPPER, + LVL(10, 3, 3, 0, 0), (G_GENO | 2), A(ATTK(AT_ENGL, AD_WRAP, 1, 6), ATTK(AT_ENGL, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(800, 350, MS_SILENT, MZ_HUGE), 0, 0, M1_HIDE | M1_FLY | M1_ANIMAL | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_CARNIVORE, - M2_HOSTILE | M2_STALK | M2_STRONG, 0, 12, CLR_GRAY, LURKER_ABOVE), - MON("trapper", S_TRAPPER, LVL(12, 3, 3, 0, 0), (G_GENO | 2), + M2_HOSTILE | M2_STALK | M2_STRONG, 0, + 12, CLR_GRAY, LURKER_ABOVE), + MON(NAM("trapper"), S_TRAPPER, + LVL(12, 3, 3, 0, 0), (G_GENO | 2), A(ATTK(AT_ENGL, AD_WRAP, 1, 8), ATTK(AT_ENGL, AD_PHYS, 2, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(800, 350, MS_SILENT, MZ_HUGE), 0, 0, M1_HIDE | M1_ANIMAL | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_CARNIVORE, - M2_HOSTILE | M2_STALK | M2_STRONG, 0, 14, CLR_GREEN, TRAPPER), + M2_HOSTILE | M2_STALK | M2_STRONG, 0, + 14, CLR_GREEN, TRAPPER), /* * unicorns and horses */ - MON("pony", S_UNICORN, LVL(3, 16, 6, 0, 0), (G_GENO | 2), - A(ATTK(AT_KICK, AD_PHYS, 1, 6), ATTK(AT_BITE, AD_PHYS, 1, 2), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + MON(NAM("pony"), S_UNICORN, + LVL(3, 16, 6, 0, 0), (G_GENO | 2), + A(ATTK(AT_KICK, AD_PHYS, 1, 6), ATTK(AT_BITE, AD_PHYS, 1, 2), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1300, 250, MS_NEIGH, MZ_MEDIUM), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_HERBIVORE, - M2_WANDER | M2_STRONG | M2_DOMESTIC, M3_INFRAVISIBLE, 4, CLR_BROWN, - PONY), - MON("white unicorn", S_UNICORN, LVL(4, 24, 2, 70, 7), (G_GENO | 2), + M2_WANDER | M2_STRONG | M2_DOMESTIC, M3_INFRAVISIBLE, + 4, CLR_BROWN, PONY), + MON(NAM("white unicorn"), S_UNICORN, + LVL(4, 24, 2, 70, 7), (G_GENO | 2), A(ATTK(AT_BUTT, AD_PHYS, 1, 12), ATTK(AT_KICK, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1300, 300, MS_NEIGH, MZ_LARGE), MR_POISON, MR_POISON, M1_NOHANDS | M1_HERBIVORE, M2_WANDER | M2_STRONG | M2_JEWELS, - M3_INFRAVISIBLE, 6, CLR_WHITE, WHITE_UNICORN), - MON("gray unicorn", S_UNICORN, LVL(4, 24, 2, 70, 0), (G_GENO | 1), + M3_INFRAVISIBLE, + 6, CLR_WHITE, WHITE_UNICORN), + MON(NAM("gray unicorn"), S_UNICORN, + LVL(4, 24, 2, 70, 0), (G_GENO | 1), A(ATTK(AT_BUTT, AD_PHYS, 1, 12), ATTK(AT_KICK, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1300, 300, MS_NEIGH, MZ_LARGE), MR_POISON, MR_POISON, M1_NOHANDS | M1_HERBIVORE, M2_WANDER | M2_STRONG | M2_JEWELS, - M3_INFRAVISIBLE, 6, CLR_GRAY, GRAY_UNICORN), - MON("black unicorn", S_UNICORN, LVL(4, 24, 2, 70, -7), (G_GENO | 1), + M3_INFRAVISIBLE, + 6, CLR_GRAY, GRAY_UNICORN), + MON(NAM("black unicorn"), S_UNICORN, + LVL(4, 24, 2, 70, -7), (G_GENO | 1), A(ATTK(AT_BUTT, AD_PHYS, 1, 12), ATTK(AT_KICK, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1300, 300, MS_NEIGH, MZ_LARGE), MR_POISON, MR_POISON, M1_NOHANDS | M1_HERBIVORE, M2_WANDER | M2_STRONG | M2_JEWELS, - M3_INFRAVISIBLE, 6, CLR_BLACK, BLACK_UNICORN), - MON("horse", S_UNICORN, LVL(5, 20, 5, 0, 0), (G_GENO | 2), - A(ATTK(AT_KICK, AD_PHYS, 1, 8), ATTK(AT_BITE, AD_PHYS, 1, 3), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISIBLE, + 6, CLR_BLACK, BLACK_UNICORN), + MON(NAM("horse"), S_UNICORN, + LVL(5, 20, 5, 0, 0), (G_GENO | 2), + A(ATTK(AT_KICK, AD_PHYS, 1, 8), ATTK(AT_BITE, AD_PHYS, 1, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1500, 300, MS_NEIGH, MZ_LARGE), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_HERBIVORE, - M2_WANDER | M2_STRONG | M2_DOMESTIC, M3_INFRAVISIBLE, 7, CLR_BROWN, - HORSE), - MON("warhorse", S_UNICORN, LVL(7, 24, 4, 0, 0), (G_GENO | 2), + M2_WANDER | M2_STRONG | M2_DOMESTIC, M3_INFRAVISIBLE, + 7, CLR_BROWN, HORSE), + MON(NAM("warhorse"), S_UNICORN, + LVL(7, 24, 4, 0, 0), (G_GENO | 2), A(ATTK(AT_KICK, AD_PHYS, 1, 10), ATTK(AT_BITE, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1800, 350, MS_NEIGH, MZ_LARGE), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_HERBIVORE, - M2_WANDER | M2_STRONG | M2_DOMESTIC, M3_INFRAVISIBLE, 9, CLR_BROWN, - WARHORSE), + M2_WANDER | M2_STRONG | M2_DOMESTIC, M3_INFRAVISIBLE, + 9, CLR_BROWN, WARHORSE), /* * vortices */ - MON("fog cloud", S_VORTEX, LVL(3, 1, 0, 0, 0), (G_GENO | G_NOCORPSE | 2), + MON(NAM("fog cloud"), S_VORTEX, + 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_LARGE), 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, 4, CLR_GRAY, FOG_CLOUD), - MON("dust vortex", S_VORTEX, LVL(4, 20, 2, 30, 0), - (G_GENO | G_NOCORPSE | 2), + M2_HOSTILE | M2_NEUTER, 0, + 4, CLR_GRAY, FOG_CLOUD), + MON(NAM("dust vortex"), S_VORTEX, + 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_LARGE), MR_SLEEP | MR_POISON | MR_STONE, 0, M1_FLY | M1_BREATHLESS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS, - M2_HOSTILE | M2_NEUTER, 0, 6, CLR_BROWN, DUST_VORTEX), - MON("ice vortex", S_VORTEX, 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), + M2_HOSTILE | M2_NEUTER, 0, + 6, CLR_BROWN, DUST_VORTEX), + MON(NAM("ice vortex"), S_VORTEX, + 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_LARGE), MR_COLD | MR_SLEEP | MR_POISON | MR_STONE, 0, M1_FLY | M1_BREATHLESS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS, - M2_HOSTILE | M2_NEUTER, M3_INFRAVISIBLE, 7, CLR_CYAN, ICE_VORTEX), - MON("energy vortex", S_VORTEX, LVL(6, 20, 2, 30, 0), - (G_GENO | G_NOCORPSE | 1), + M2_HOSTILE | M2_NEUTER, M3_INFRAVISIBLE, + 7, CLR_CYAN, ICE_VORTEX), + MON(NAM("energy vortex"), S_VORTEX, + 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_LARGE), 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, - M2_HOSTILE | M2_NEUTER, 0, 9, HI_ZAP, ENERGY_VORTEX), - MON("steam vortex", S_VORTEX, 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), + M2_HOSTILE | M2_NEUTER, 0, + 9, HI_ZAP, ENERGY_VORTEX), + MON(NAM("steam vortex"), S_VORTEX, + 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_LARGE), MR_FIRE | MR_SLEEP | MR_POISON | MR_STONE, 0, M1_FLY | M1_BREATHLESS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_UNSOLID, - M2_HOSTILE | M2_NEUTER, M3_INFRAVISIBLE, 9, CLR_BLUE, STEAM_VORTEX), - MON("fire vortex", S_VORTEX, LVL(8, 22, 2, 30, 0), - (G_HELL | G_GENO | G_NOCORPSE | 1), + M2_HOSTILE | M2_NEUTER, M3_INFRAVISIBLE, + 9, CLR_BLUE, STEAM_VORTEX), + MON(NAM("fire vortex"), S_VORTEX, + 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_LARGE), MR_FIRE | MR_SLEEP | MR_POISON | MR_STONE, 0, M1_FLY | M1_BREATHLESS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_UNSOLID, - M2_HOSTILE | M2_NEUTER, M3_INFRAVISIBLE, 10, CLR_YELLOW, - FIRE_VORTEX), + M2_HOSTILE | M2_NEUTER, M3_INFRAVISIBLE, + 10, CLR_YELLOW, FIRE_VORTEX), /* * worms */ - MON("baby long worm", S_WORM, LVL(5, 3, 5, 0, 0), G_GENO, - A(ATTK(AT_BITE, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("baby long worm"), S_WORM, + LVL(5, 3, 5, 0, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(600, 250, MS_SILENT, MZ_LARGE), 0, 0, M1_ANIMAL | M1_SLITHY | M1_NOLIMBS | M1_CARNIVORE | M1_NOTAKE, - M2_HOSTILE, 0, 6, CLR_BROWN, BABY_LONG_WORM), - MON("baby purple worm", S_WORM, LVL(8, 3, 5, 0, 0), G_GENO, - A(ATTK(AT_BITE, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_HOSTILE, 0, + 6, CLR_BROWN, BABY_LONG_WORM), + MON(NAM("baby purple worm"), S_WORM, + LVL(8, 3, 5, 0, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(600, 250, MS_SILENT, MZ_LARGE), 0, 0, M1_ANIMAL | M1_SLITHY | M1_NOLIMBS | M1_CARNIVORE, M2_HOSTILE, 0, 9, CLR_MAGENTA, BABY_PURPLE_WORM), - MON("long worm", S_WORM, LVL(9, 3, 5, 10, 0), (G_GENO | 2), - A(ATTK(AT_BITE, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("long worm"), S_WORM, + LVL(9, 3, 5, 10, 0), (G_GENO | 2), + A(ATTK(AT_BITE, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1500, 500, MS_SILENT, MZ_GIGANTIC), 0, 0, M1_ANIMAL | M1_SLITHY | M1_NOLIMBS | M1_OVIPAROUS | M1_CARNIVORE | M1_NOTAKE, - M2_HOSTILE | M2_STRONG | M2_NASTY, 0, 10, CLR_BROWN, LONG_WORM), - MON("purple worm", S_WORM, LVL(15, 9, 6, 20, 0), (G_GENO | 2), + M2_HOSTILE | M2_STRONG | M2_NASTY, 0, + 10, CLR_BROWN, LONG_WORM), + MON(NAM("purple worm"), S_WORM, + LVL(15, 9, 6, 20, 0), (G_GENO | 2), A(ATTK(AT_BITE, AD_PHYS, 2, 8), ATTK(AT_ENGL, AD_DGST, 1, 10), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2700, 700, MS_SILENT, MZ_GIGANTIC), 0, 0, M1_ANIMAL | M1_SLITHY | M1_NOLIMBS | M1_OVIPAROUS | M1_CARNIVORE, - M2_HOSTILE | M2_STRONG | M2_NASTY, 0, 17, CLR_MAGENTA, PURPLE_WORM), + M2_HOSTILE | M2_STRONG | M2_NASTY, 0, + 17, CLR_MAGENTA, PURPLE_WORM), /* * xan, &c */ - MON("grid bug", S_XAN, LVL(0, 12, 9, 0, 0), - (G_GENO | G_SGROUP | G_NOCORPSE | 3), - A(ATTK(AT_BITE, AD_ELEC, 1, 1), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("grid bug"), S_XAN, + LVL(0, 12, 9, 0, 0), (G_GENO | G_SGROUP | G_NOCORPSE | 3), + A(ATTK(AT_BITE, AD_ELEC, 1, 1), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(15, 10, MS_BUZZ, MZ_TINY), MR_ELEC | MR_POISON, 0, M1_ANIMAL | M1_NOHANDS, - M2_HOSTILE, M3_INFRAVISIBLE, 1, CLR_MAGENTA, GRID_BUG), - MON("xan", S_XAN, LVL(7, 18, -4, 0, 0), (G_GENO | 3), - A(ATTK(AT_STNG, AD_LEGS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_HOSTILE, M3_INFRAVISIBLE, + 1, CLR_MAGENTA, GRID_BUG), + MON(NAM("xan"), S_XAN, + LVL(7, 18, -4, 0, 0), (G_GENO | 3), + A(ATTK(AT_STNG, AD_LEGS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(300, 300, MS_BUZZ, MZ_TINY), MR_POISON, MR_POISON, M1_FLY | M1_ANIMAL | M1_NOHANDS | M1_POIS, M2_HOSTILE, - M3_INFRAVISIBLE, 9, CLR_RED, XAN), + M3_INFRAVISIBLE, + 9, CLR_RED, XAN), /* * lights */ - MON("yellow light", S_LIGHT, 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), + /* yellow light is visible and its suicidal explosion causes blindness */ + MON(NAM("yellow light"), S_LIGHT, + 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), 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 | M1_NOHEAD | M1_MINDLESS | M1_UNSOLID | M1_NOTAKE, - M2_HOSTILE | M2_NEUTER, M3_INFRAVISIBLE, 5, CLR_YELLOW, YELLOW_LIGHT), - MON("black light", S_LIGHT, 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), + M2_HOSTILE | M2_NEUTER, M3_INFRAVISIBLE, + 5, CLR_YELLOW, YELLOW_LIGHT), + /* black light is invisible and causes hallucination */ + MON(NAM("black light"), S_LIGHT, + 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), 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 | M1_NOHEAD | M1_MINDLESS | M1_UNSOLID | M1_SEE_INVIS | M1_NOTAKE, - M2_HOSTILE | M2_NEUTER, 0, 7, CLR_BLACK, BLACK_LIGHT), + M2_HOSTILE | M2_NEUTER, 0, + 7, CLR_BLACK, BLACK_LIGHT), /* * zruty */ - MON("zruty", S_ZRUTY, LVL(9, 8, 3, 0, 0), (G_GENO | 2), + MON(NAM("zruty"), S_ZRUTY, + LVL(9, 8, 3, 0, 0), (G_GENO | 2), A(ATTK(AT_CLAW, AD_PHYS, 3, 4), ATTK(AT_CLAW, AD_PHYS, 3, 4), ATTK(AT_BITE, AD_PHYS, 3, 6), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1200, 600, MS_SILENT, MZ_LARGE), 0, 0, M1_ANIMAL | M1_HUMANOID | M1_CARNIVORE, M2_HOSTILE | M2_STRONG, - M3_INFRAVISIBLE, 11, CLR_BROWN, ZRUTY), + M3_INFRAVISIBLE, + 11, CLR_BROWN, ZRUTY), /* * Angels and other lawful minions */ - MON("couatl", S_ANGEL, LVL(8, 10, 5, 30, 7), - (G_NOHELL | G_SGROUP | G_NOCORPSE | 1), + MON(NAM("couatl"), S_ANGEL, + LVL(8, 10, 5, 30, 7), (G_NOHELL | G_SGROUP | G_NOCORPSE | 1), A(ATTK(AT_BITE, AD_DRST, 2, 4), ATTK(AT_BITE, AD_PHYS, 1, 3), ATTK(AT_HUGS, AD_WRAP, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(900, 400, MS_HISS, MZ_LARGE), MR_ELEC | MR_SLEEP | MR_POISON, 0, M1_FLY | M1_NOHANDS | M1_SLITHY | M1_POIS, M2_MINION | M2_STALK | M2_STRONG | M2_NASTY, - M3_INFRAVISIBLE | M3_INFRAVISION, 11, CLR_GREEN, COUATL), - MON("Aleax", S_ANGEL, LVL(10, 8, 0, 30, 7), (G_NOHELL | G_NOCORPSE | 1), + M3_INFRAVISIBLE | M3_INFRAVISION, + 11, CLR_GREEN, COUATL), + MON(NAM("Aleax"), S_ANGEL, + LVL(10, 8, 0, 30, 7), (G_NOHELL | G_NOCORPSE | 1), A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_KICK, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_IMITATE, MZ_HUMAN), MR_COLD | MR_ELEC | MR_SLEEP | MR_POISON, 0, M1_HUMANOID | M1_SEE_INVIS, M2_MINION | M2_STALK | M2_NASTY | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 12, CLR_GRAY, ALEAX), + M3_INFRAVISIBLE | M3_INFRAVISION, + 12, CLR_GRAY, ALEAX), /* Angels start with the emin extension attached, and usually have the isminion flag set; however, non-minion Angels can be tamed and will switch to edog (guardian Angel is handled specially and always sticks with emin) */ - MON("Angel", S_ANGEL, LVL(14, 10, -4, 55, 12), - (G_NOHELL | G_NOCORPSE | 1), + MON(NAM("Angel"), S_ANGEL, + LVL(14, 10, -4, 55, 12), (G_NOHELL | G_NOCORPSE | 1), A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), - ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_MAGC, AD_MAGM, 2, 6), NO_ATTK, - NO_ATTK), + ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_MAGC, AD_MAGM, 2, 6), + NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_CUSS, MZ_HUMAN), MR_COLD | MR_ELEC | MR_SLEEP | MR_POISON, 0, M1_FLY | M1_HUMANOID | M1_SEE_INVIS, M2_NOPOLY | M2_MINION | M2_STALK | M2_STRONG | M2_NASTY | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 19, CLR_WHITE, ANGEL), + M3_INFRAVISIBLE | M3_INFRAVISION, + 19, CLR_WHITE, ANGEL), /* the AD&D Monster Manual depicts ki-rin as very similar to unicorns - except that they fly (without wings) and can cast spells */ - MON("ki-rin", S_ANGEL, LVL(16, 18, -5, 90, 15), - (G_NOHELL | G_NOCORPSE | 1), + except that they fly (without wings) and can cast spells; nethack's + ki-rin doesn't leave its horn when killed, but it can use the horn + while alive to cure itself of various maladies */ + MON(NAM("ki-rin"), S_ANGEL, + LVL(16, 18, -5, 90, 15), (G_NOHELL | G_NOCORPSE | 1), A(ATTK(AT_KICK, AD_PHYS, 2, 4), ATTK(AT_KICK, AD_PHYS, 2, 4), - ATTK(AT_BUTT, AD_PHYS, 3, 6), ATTK(AT_MAGC, AD_SPEL, 2, 6), NO_ATTK, - NO_ATTK), + ATTK(AT_BUTT, AD_PHYS, 3, 6), ATTK(AT_MAGC, AD_SPEL, 2, 6), + NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_SPELL, MZ_LARGE), MR_COLD | MR_ELEC | MR_SLEEP | MR_POISON, 0, M1_FLY | M1_NOHANDS | M1_SEE_INVIS, M2_NOPOLY | M2_MINION | M2_STALK | M2_STRONG | M2_NASTY | M2_LORD, - M3_INFRAVISIBLE | M3_INFRAVISION, 21, HI_GOLD, KI_RIN), - MON("Archon", S_ANGEL, LVL(19, 16, -6, 80, 15), - (G_NOHELL | G_NOCORPSE | 1), + M3_INFRAVISIBLE | M3_INFRAVISION, + 21, HI_GOLD, KI_RIN), + MON(NAM("Archon"), S_ANGEL, + LVL(19, 16, -6, 80, 15), (G_NOHELL | G_NOCORPSE | 1), A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_GAZE, AD_BLND, 2, 6), ATTK(AT_CLAW, AD_PHYS, 1, 8), ATTK(AT_MAGC, AD_SPEL, 4, 6), NO_ATTK), @@ -1063,63 +1253,75 @@ M1_FLY | M1_HUMANOID | M1_SEE_INVIS | M1_REGEN, M2_NOPOLY | M2_MINION | M2_STALK | M2_STRONG | M2_NASTY | M2_LORD | M2_COLLECT | M2_MAGIC, - M3_INFRAVISIBLE | M3_INFRAVISION, 26, HI_LORD, ARCHON), + M3_INFRAVISIBLE | M3_INFRAVISION, + 26, HI_LORD, ARCHON), /* - * Bats + * Bats and birds */ - MON("bat", S_BAT, LVL(0, 22, 8, 0, 0), (G_GENO | G_SGROUP | 1), - A(ATTK(AT_BITE, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("bat"), S_BAT, + LVL(0, 22, 8, 0, 0), (G_GENO | G_SGROUP | 1), + A(ATTK(AT_BITE, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(20, 20, MS_SQEEK, MZ_TINY), 0, 0, M1_FLY | M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_WANDER, M3_INFRAVISIBLE, 2, CLR_BROWN, BAT), - MON("giant bat", S_BAT, LVL(2, 22, 7, 0, 0), (G_GENO | 2), - A(ATTK(AT_BITE, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("giant bat"), S_BAT, + LVL(2, 22, 7, 0, 0), (G_GENO | 2), + A(ATTK(AT_BITE, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(30, 30, MS_SQEEK, MZ_SMALL), 0, 0, M1_FLY | M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, - M2_WANDER | M2_HOSTILE, M3_INFRAVISIBLE, 3, CLR_RED, GIANT_BAT), - MON("raven", S_BAT, LVL(4, 20, 6, 0, 0), (G_GENO | 2), - A(ATTK(AT_BITE, AD_PHYS, 1, 6), ATTK(AT_CLAW, AD_BLND, 1, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M2_WANDER | M2_HOSTILE, M3_INFRAVISIBLE, + 3, CLR_RED, GIANT_BAT), + MON(NAM("raven"), S_BAT, + LVL(4, 20, 6, 0, 0), (G_GENO | 2), + A(ATTK(AT_BITE, AD_PHYS, 1, 6), ATTK(AT_CLAW, AD_BLND, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(40, 20, MS_SQAWK, MZ_SMALL), 0, 0, M1_FLY | M1_ANIMAL | M1_NOHANDS | M1_OVIPAROUS | M1_CARNIVORE, M2_WANDER | M2_HOSTILE, M3_INFRAVISIBLE, 6, CLR_BLACK, RAVEN), - MON("vampire bat", S_BAT, LVL(5, 20, 6, 0, 0), (G_GENO | 2), - A(ATTK(AT_BITE, AD_PHYS, 1, 6), ATTK(AT_BITE, AD_DRST, 0, 0), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + MON(NAM("vampire bat"), S_BAT, LVL(5, 20, 6, 0, 0), (G_GENO | 2), + A(ATTK(AT_BITE, AD_PHYS, 1, 6), ATTK(AT_BITE, AD_DRST, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(30, 20, MS_SQEEK, MZ_SMALL), MR_SLEEP | MR_POISON, 0, M1_FLY | M1_ANIMAL | M1_NOHANDS | M1_POIS | M1_REGEN | M1_OMNIVORE, - M2_HOSTILE, M3_INFRAVISIBLE, 7, CLR_BLACK, VAMPIRE_BAT), - MON("phoenix", S_BAT, LVL(15, 20, -7, 40, 7), (G_GENO | G_NOCORPSE | 2), + M2_HOSTILE, M3_INFRAVISIBLE, + 7, CLR_BLACK, VAMPIRE_BAT), + MON(NAM("phoenix"), S_BAT, + LVL(15, 20, -7, 40, 7), (G_GENO | G_NOCORPSE | 2), A(ATTK(AT_BITE, AD_PHYS, 2, 6), ATTK(AT_CLAW, AD_PHYS, 3, 6), ATTK(AT_CLAW, AD_FIRE, 3, 6), ATTK(AT_BOOM, AD_FIRE, 8, 6), NO_ATTK, NO_ATTK), SIZ(40, 20, MS_SQAWK, MZ_SMALL), MR_FIRE, MR_FIRE, M1_FLY | M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE | M1_OVIPAROUS, - M2_WANDER | M2_STALK | M2_NASTY, M3_INFRAVISIBLE, 20, CLR_ORANGE, - PHOENIX), + M2_WANDER | M2_STALK | M2_NASTY, M3_INFRAVISIBLE, + 20, CLR_ORANGE, PHOENIX), /* * Centaurs */ - MON("plains centaur", S_CENTAUR, LVL(4, 18, 4, 0, 0), (G_GENO | 1), - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_KICK, AD_PHYS, 1, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + MON(NAM("plains centaur"), S_CENTAUR, + LVL(4, 18, 4, 0, 0), (G_GENO | 1), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_KICK, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2500, 500, MS_HUMANOID, MZ_LARGE), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_STRONG | M2_GREEDY | M2_COLLECT, M3_INFRAVISIBLE, 6, CLR_BROWN, PLAINS_CENTAUR), - MON("forest centaur", S_CENTAUR, LVL(5, 18, 3, 10, -1), (G_GENO | 1), - A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_KICK, AD_PHYS, 1, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + MON(NAM("forest centaur"), S_CENTAUR, + LVL(5, 18, 3, 10, -1), (G_GENO | 1), + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_KICK, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2550, 600, MS_HUMANOID, MZ_LARGE), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_STRONG | M2_GREEDY | M2_COLLECT, - M3_INFRAVISIBLE, 8, CLR_GREEN, FOREST_CENTAUR), - MON("mountain centaur", S_CENTAUR, LVL(6, 20, 2, 10, -3), (G_GENO | 1), + M3_INFRAVISIBLE, + 8, CLR_GREEN, FOREST_CENTAUR), + MON(NAM("mountain centaur"), S_CENTAUR, + LVL(6, 20, 2, 10, -3), (G_GENO | 1), A(ATTK(AT_WEAP, AD_PHYS, 1, 10), ATTK(AT_KICK, AD_PHYS, 1, 6), ATTK(AT_KICK, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2550, 500, MS_HUMANOID, MZ_LARGE), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_STRONG | M2_GREEDY | M2_COLLECT, - M3_INFRAVISIBLE, 9, CLR_CYAN, MOUNTAIN_CENTAUR), + M3_INFRAVISIBLE, + 9, CLR_CYAN, MOUNTAIN_CENTAUR), /* * Dragons */ @@ -1136,98 +1338,111 @@ * infravision. Red and gold dragons (also Chromatic Dragon) are * the exceptions because they breathe fire. */ - MON("baby gray dragon", S_DRAGON, 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), + MON(NAM("baby gray dragon"), S_DRAGON, + 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_LARGE), 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), - MON("baby gold dragon", S_DRAGON, 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), + M2_HOSTILE | M2_STRONG | M2_GREEDY | M2_JEWELS, 0, + 13, CLR_GRAY, BABY_GRAY_DRAGON), + MON(NAM("baby gold dragon"), S_DRAGON, + 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_LARGE), 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), - MON("baby silver dragon", S_DRAGON, 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), + MON(NAM("baby silver dragon"), S_DRAGON, + 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_LARGE), 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), + M2_HOSTILE | M2_STRONG | M2_GREEDY | M2_JEWELS, 0, + 13, DRAGON_SILVER, BABY_SILVER_DRAGON), #if 0 /* DEFERRED */ /* [see "shimmering dragon" below] */ - MON("baby shimmering dragon", S_DRAGON, + MON(NAM("baby shimmering dragon"), S_DRAGON, 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_LARGE), 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), + M2_HOSTILE | M2_STRONG | M2_GREEDY | M2_JEWELS, 0, + 13, CLR_CYAN, BABY_SHIMMERING_DRAGON), #endif - MON("baby red dragon", S_DRAGON, 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), + MON(NAM("baby red dragon"), S_DRAGON, + 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_LARGE), 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), - MON("baby white dragon", S_DRAGON, 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), + MON(NAM("baby white dragon"), S_DRAGON, + 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_LARGE), 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), - MON("baby orange dragon", S_DRAGON, 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), + M2_HOSTILE | M2_STRONG | M2_GREEDY | M2_JEWELS, 0, + 13, CLR_WHITE, BABY_WHITE_DRAGON), + MON(NAM("baby orange dragon"), S_DRAGON, + 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_LARGE), 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), - MON("baby black dragon", S_DRAGON, 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), + M2_HOSTILE | M2_STRONG | M2_GREEDY | M2_JEWELS, 0, + 13, CLR_ORANGE, BABY_ORANGE_DRAGON), + MON(NAM("baby black dragon"), S_DRAGON, + 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_LARGE), 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), - MON("baby blue dragon", S_DRAGON, 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), + M2_HOSTILE | M2_STRONG | M2_GREEDY | M2_JEWELS, 0, + 13, CLR_BLACK, BABY_BLACK_DRAGON), + MON(NAM("baby blue dragon"), S_DRAGON, + 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_LARGE), 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), - MON("baby green dragon", S_DRAGON, 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), + M2_HOSTILE | M2_STRONG | M2_GREEDY | M2_JEWELS, 0, + 13, CLR_BLUE, BABY_BLUE_DRAGON), + MON(NAM("baby green dragon"), S_DRAGON, + 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_LARGE), 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), - MON("baby yellow dragon", S_DRAGON, LVL(12, 9, 2, 10, 0), G_GENO, + M2_HOSTILE | M2_STRONG | M2_GREEDY | M2_JEWELS, 0, + 13, CLR_GREEN, BABY_GREEN_DRAGON), + MON(NAM("baby yellow dragon"), S_DRAGON, + 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_LARGE), 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), - MON("gray dragon", S_DRAGON, LVL(15, 9, -1, 20, 4), (G_GENO | 1), + M2_HOSTILE | M2_STRONG | M2_GREEDY | M2_JEWELS, 0, + 13, CLR_YELLOW, BABY_YELLOW_DRAGON), + MON(NAM("gray dragon"), S_DRAGON, + LVL(15, 9, -1, 20, 4), (G_GENO | 1), A(ATTK(AT_BREA, AD_MAGM, 4, 6), ATTK(AT_BITE, AD_PHYS, 3, 8), - ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), NO_ATTK, - NO_ATTK), + ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK), SIZ(WT_DRAGON, 1500, MS_ROAR, MZ_GIGANTIC), 0, 0, M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_SEE_INVIS | M1_OVIPAROUS | M1_CARNIVORE, M2_HOSTILE | M2_STRONG | M2_NASTY | M2_GREEDY | M2_JEWELS | M2_MAGIC, - 0, 20, CLR_GRAY, GRAY_DRAGON), + 0, + 20, CLR_GRAY, GRAY_DRAGON), /* gold dragon can be seen via infravision but doesn't have infravision */ - MON("gold dragon", S_DRAGON, LVL(15, 9, -1, 20, 4), (G_GENO | 1), + MON(NAM("gold dragon"), S_DRAGON, + LVL(15, 9, -1, 20, 4), (G_GENO | 1), A(ATTK(AT_BREA, AD_FIRE, 4, 6), ATTK(AT_BITE, AD_PHYS, 3, 8), ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK), @@ -1235,22 +1450,25 @@ M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_SEE_INVIS | M1_OVIPAROUS | M1_CARNIVORE, M2_HOSTILE | M2_STRONG | M2_NASTY | M2_GREEDY | M2_JEWELS | M2_MAGIC, - M3_INFRAVISIBLE, 20, HI_GOLD, GOLD_DRAGON), - MON("silver dragon", S_DRAGON, LVL(15, 9, -1, 20, 4), (G_GENO | 1), + M3_INFRAVISIBLE, + 20, HI_GOLD, GOLD_DRAGON), + MON(NAM("silver dragon"), S_DRAGON, + LVL(15, 9, -1, 20, 4), (G_GENO | 1), A(ATTK(AT_BREA, AD_COLD, 4, 6), ATTK(AT_BITE, AD_PHYS, 3, 8), - ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), NO_ATTK, - NO_ATTK), + ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK), SIZ(WT_DRAGON, 1500, MS_ROAR, MZ_GIGANTIC), MR_COLD, 0, M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_SEE_INVIS | M1_OVIPAROUS | M1_CARNIVORE, M2_HOSTILE | M2_STRONG | M2_NASTY | M2_GREEDY | M2_JEWELS | M2_MAGIC, - 0, 20, DRAGON_SILVER, SILVER_DRAGON), + 0, + 20, DRAGON_SILVER, SILVER_DRAGON), #if 0 /* DEFERRED */ /* shimmering scales/scale-mail would confer displacement when worn by the hero, so shimmering dragon ought to be displaced (hero who can see one might misjudge its location) but monster displacement hasn't been implemented so we don't include it */ - MON("shimmering dragon", S_DRAGON, + MON(NAM("shimmering dragon"), S_DRAGON, LVL(15, 9, -1, 20, 4), (G_GENO | 1), A(ATTK(AT_BREA, AD_MAGM, 4, 6), ATTK(AT_BITE, AD_PHYS, 3, 8), ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), @@ -1259,65 +1477,79 @@ M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_SEE_INVIS | M1_OVIPAROUS | M1_CARNIVORE, M2_HOSTILE | M2_STRONG | M2_NASTY | M2_GREEDY | M2_JEWELS | M2_MAGIC, - 0, 20, CLR_CYAN, SHIMMERING_DRAGON), + 0, + 20, CLR_CYAN, SHIMMERING_DRAGON), #endif /* red dragon has infravision and can be seen via infravision */ - MON("red dragon", S_DRAGON, LVL(15, 9, -1, 20, -4), (G_GENO | 1), + MON(NAM("red dragon"), S_DRAGON, + LVL(15, 9, -1, 20, -4), (G_GENO | 1), A(ATTK(AT_BREA, AD_FIRE, 6, 6), ATTK(AT_BITE, AD_PHYS, 3, 8), - ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), NO_ATTK, - NO_ATTK), + ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK), SIZ(WT_DRAGON, 1500, MS_ROAR, MZ_GIGANTIC), MR_FIRE, MR_FIRE, M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_SEE_INVIS | M1_OVIPAROUS | M1_CARNIVORE, M2_HOSTILE | M2_STRONG | M2_NASTY | M2_GREEDY | M2_JEWELS | M2_MAGIC, - M3_INFRAVISION | M3_INFRAVISIBLE, 20, CLR_RED, RED_DRAGON), - MON("white dragon", S_DRAGON, LVL(15, 9, -1, 20, -5), (G_GENO | 1), + M3_INFRAVISION | M3_INFRAVISIBLE, + 20, CLR_RED, RED_DRAGON), + MON(NAM("white dragon"), S_DRAGON, + LVL(15, 9, -1, 20, -5), (G_GENO | 1), A(ATTK(AT_BREA, AD_COLD, 4, 6), ATTK(AT_BITE, AD_PHYS, 3, 8), - ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), NO_ATTK, - NO_ATTK), + ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK), SIZ(WT_DRAGON, 1500, MS_ROAR, MZ_GIGANTIC), MR_COLD, MR_COLD, M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_SEE_INVIS | M1_OVIPAROUS | M1_CARNIVORE, M2_HOSTILE | M2_STRONG | M2_NASTY | M2_GREEDY | M2_JEWELS | M2_MAGIC, - 0, 20, CLR_WHITE, WHITE_DRAGON), - MON("orange dragon", S_DRAGON, LVL(15, 9, -1, 20, 5), (G_GENO | 1), + 0, + 20, CLR_WHITE, WHITE_DRAGON), + MON(NAM("orange dragon"), S_DRAGON, + LVL(15, 9, -1, 20, 5), (G_GENO | 1), A(ATTK(AT_BREA, AD_SLEE, 4, 25), ATTK(AT_BITE, AD_PHYS, 3, 8), - ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), NO_ATTK, - NO_ATTK), + ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK), SIZ(WT_DRAGON, 1500, MS_ROAR, MZ_GIGANTIC), MR_SLEEP, MR_SLEEP, M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_SEE_INVIS | M1_OVIPAROUS | M1_CARNIVORE, M2_HOSTILE | M2_STRONG | M2_NASTY | M2_GREEDY | M2_JEWELS | M2_MAGIC, - 0, 20, CLR_ORANGE, ORANGE_DRAGON), + 0, + 20, CLR_ORANGE, ORANGE_DRAGON), /* disintegration breath is actually all or nothing, not 1d255 */ - MON("black dragon", S_DRAGON, LVL(15, 9, -1, 20, -6), (G_GENO | 1), + MON(NAM("black dragon"), S_DRAGON, + LVL(15, 9, -1, 20, -6), (G_GENO | 1), A(ATTK(AT_BREA, AD_DISN, 1, 255), ATTK(AT_BITE, AD_PHYS, 3, 8), - ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), NO_ATTK, - NO_ATTK), + ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK), SIZ(WT_DRAGON, 1500, MS_ROAR, MZ_GIGANTIC), MR_DISINT, MR_DISINT, M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_SEE_INVIS | M1_OVIPAROUS | M1_CARNIVORE, M2_HOSTILE | M2_STRONG | M2_NASTY | M2_GREEDY | M2_JEWELS | M2_MAGIC, - 0, 20, CLR_BLACK, BLACK_DRAGON), - MON("blue dragon", S_DRAGON, LVL(15, 9, -1, 20, -7), (G_GENO | 1), + 0, + 20, CLR_BLACK, BLACK_DRAGON), + MON(NAM("blue dragon"), S_DRAGON, + LVL(15, 9, -1, 20, -7), (G_GENO | 1), A(ATTK(AT_BREA, AD_ELEC, 4, 6), ATTK(AT_BITE, AD_PHYS, 3, 8), - ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), NO_ATTK, - NO_ATTK), + ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK), SIZ(WT_DRAGON, 1500, MS_ROAR, MZ_GIGANTIC), MR_ELEC, MR_ELEC, M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_SEE_INVIS | M1_OVIPAROUS | M1_CARNIVORE, M2_HOSTILE | M2_STRONG | M2_NASTY | M2_GREEDY | M2_JEWELS | M2_MAGIC, - 0, 20, CLR_BLUE, BLUE_DRAGON), - MON("green dragon", S_DRAGON, LVL(15, 9, -1, 20, 6), (G_GENO | 1), + 0, + 20, CLR_BLUE, BLUE_DRAGON), + MON(NAM("green dragon"), S_DRAGON, + LVL(15, 9, -1, 20, 6), (G_GENO | 1), A(ATTK(AT_BREA, AD_DRST, 4, 6), ATTK(AT_BITE, AD_PHYS, 3, 8), - ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), NO_ATTK, - NO_ATTK), + ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK), SIZ(WT_DRAGON, 1500, MS_ROAR, MZ_GIGANTIC), MR_POISON, MR_POISON, M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_SEE_INVIS | M1_OVIPAROUS | M1_CARNIVORE | M1_POIS, M2_HOSTILE | M2_STRONG | M2_NASTY | M2_GREEDY | M2_JEWELS | M2_MAGIC, - 0, 20, CLR_GREEN, GREEN_DRAGON), - MON("yellow dragon", S_DRAGON, LVL(15, 9, -1, 20, 7), (G_GENO | 1), + 0, + 20, CLR_GREEN, GREEN_DRAGON), + MON(NAM("yellow dragon"), S_DRAGON, + LVL(15, 9, -1, 20, 7), (G_GENO | 1), A(ATTK(AT_BREA, AD_ACID, 4, 6), ATTK(AT_BITE, AD_PHYS, 3, 8), ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK), @@ -1326,197 +1558,244 @@ M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_SEE_INVIS | M1_OVIPAROUS | M1_CARNIVORE | M1_ACID, M2_HOSTILE | M2_STRONG | M2_NASTY | M2_GREEDY | M2_JEWELS | M2_MAGIC, - 0, 20, CLR_YELLOW, YELLOW_DRAGON), + 0, + 20, CLR_YELLOW, YELLOW_DRAGON), /* * Elementals */ - MON("stalker", S_ELEMENTAL, LVL(8, 12, 3, 0, 0), (G_GENO | 3), - A(ATTK(AT_CLAW, AD_PHYS, 4, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("stalker"), S_ELEMENTAL, + LVL(8, 12, 3, 0, 0), (G_GENO | 3), + A(ATTK(AT_CLAW, AD_PHYS, 4, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(900, 400, MS_SILENT, MZ_LARGE), 0, 0, M1_ANIMAL | M1_FLY | M1_SEE_INVIS, M2_WANDER | M2_STALK | M2_HOSTILE | M2_STRONG, M3_INFRAVISION, 9, CLR_WHITE, STALKER), - MON("air elemental", S_ELEMENTAL, 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), + MON(NAM("air elemental"), S_ELEMENTAL, + 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_LARGE), MR_POISON | MR_STONE, 0, M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_BREATHLESS | M1_UNSOLID | M1_FLY, - M2_STRONG | M2_NEUTER, 0, 10, CLR_CYAN, AIR_ELEMENTAL), - MON("fire elemental", S_ELEMENTAL, 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), + M2_STRONG | M2_NEUTER, 0, + 10, CLR_CYAN, AIR_ELEMENTAL), + MON(NAM("fire elemental"), S_ELEMENTAL, + 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_LARGE), 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, 10, CLR_YELLOW, - FIRE_ELEMENTAL), - MON("earth elemental", S_ELEMENTAL, LVL(8, 6, 2, 30, 0), (G_NOCORPSE | 1), - A(ATTK(AT_CLAW, AD_PHYS, 4, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_STRONG | M2_NEUTER, M3_INFRAVISIBLE, + 10, CLR_YELLOW, FIRE_ELEMENTAL), + MON(NAM("earth elemental"), S_ELEMENTAL, + LVL(8, 6, 2, 30, 0), (G_NOCORPSE | 1), + A(ATTK(AT_CLAW, AD_PHYS, 4, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2500, 0, MS_SILENT, MZ_LARGE), MR_FIRE | MR_COLD | MR_POISON | MR_STONE, 0, M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_BREATHLESS | M1_WALLWALK | M1_THICK_HIDE, - M2_STRONG | M2_NEUTER, 0, 10, CLR_BROWN, EARTH_ELEMENTAL), - MON("water elemental", S_ELEMENTAL, LVL(8, 6, 2, 30, 0), (G_NOCORPSE | 1), + M2_STRONG | M2_NEUTER, 0, + 10, CLR_BROWN, EARTH_ELEMENTAL), + MON(NAM("water elemental"), S_ELEMENTAL, + LVL(8, 6, 2, 30, 0), (G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_PHYS, 5, 6), ATTK(AT_NONE, AD_RUST, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2500, 0, MS_SILENT, MZ_LARGE), MR_POISON | MR_STONE, 0, M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_BREATHLESS | M1_UNSOLID | M1_AMPHIBIOUS | M1_SWIM, - M2_STRONG | M2_NEUTER, 0, 10, CLR_BLUE, WATER_ELEMENTAL), + M2_STRONG | M2_NEUTER, 0, + 10, CLR_BLUE, WATER_ELEMENTAL), /* * Fungi */ - MON("lichen", S_FUNGUS, LVL(0, 1, 9, 0, 0), (G_GENO | 4), + MON(NAM("lichen"), S_FUNGUS, + LVL(0, 1, 9, 0, 0), (G_GENO | 4), A(ATTK(AT_TUCH, AD_STCK, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(20, 200, MS_SILENT, MZ_SMALL), 0, 0, M1_BREATHLESS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_NOTAKE, - M2_HOSTILE | M2_NEUTER, 0, 1, CLR_BRIGHT_GREEN, LICHEN), - MON("brown mold", S_FUNGUS, LVL(1, 0, 9, 0, 0), (G_GENO | 1), + M2_HOSTILE | M2_NEUTER, 0, + 1, CLR_BRIGHT_GREEN, LICHEN), + MON(NAM("brown mold"), S_FUNGUS, + LVL(1, 0, 9, 0, 0), (G_GENO | 1), A(ATTK(AT_NONE, AD_COLD, 0, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(50, 30, MS_SILENT, MZ_SMALL), MR_COLD | MR_POISON, MR_COLD | MR_POISON, M1_BREATHLESS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_NOTAKE, - M2_HOSTILE | M2_NEUTER, 0, 2, CLR_BROWN, BROWN_MOLD), - MON("yellow mold", S_FUNGUS, LVL(1, 0, 9, 0, 0), (G_GENO | 2), + M2_HOSTILE | M2_NEUTER, 0, + 2, CLR_BROWN, BROWN_MOLD), + MON(NAM("yellow mold"), S_FUNGUS, + LVL(1, 0, 9, 0, 0), (G_GENO | 2), A(ATTK(AT_NONE, AD_STUN, 0, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(50, 30, MS_SILENT, MZ_SMALL), MR_POISON, MR_POISON, M1_BREATHLESS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_POIS | M1_NOTAKE, - M2_HOSTILE | M2_NEUTER, 0, 2, CLR_YELLOW, YELLOW_MOLD), - MON("green mold", S_FUNGUS, LVL(1, 0, 9, 0, 0), (G_GENO | 1), + M2_HOSTILE | M2_NEUTER, 0, + 2, CLR_YELLOW, YELLOW_MOLD), + MON(NAM("green mold"), S_FUNGUS, + LVL(1, 0, 9, 0, 0), (G_GENO | 1), A(ATTK(AT_NONE, AD_ACID, 0, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(50, 30, MS_SILENT, MZ_SMALL), MR_ACID | MR_STONE, MR_ACID | MR_STONE, M1_BREATHLESS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_ACID | M1_NOTAKE, - M2_HOSTILE | M2_NEUTER, 0, 2, CLR_GREEN, GREEN_MOLD), - MON("red mold", S_FUNGUS, LVL(1, 0, 9, 0, 0), (G_GENO | 1), + M2_HOSTILE | M2_NEUTER, 0, + 2, CLR_GREEN, GREEN_MOLD), + MON(NAM("red mold"), S_FUNGUS, + LVL(1, 0, 9, 0, 0), (G_GENO | 1), A(ATTK(AT_NONE, AD_FIRE, 0, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(50, 30, MS_SILENT, MZ_SMALL), MR_FIRE | MR_POISON, MR_FIRE | MR_POISON, M1_BREATHLESS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_NOTAKE, - M2_HOSTILE | M2_NEUTER, M3_INFRAVISIBLE, 2, CLR_RED, RED_MOLD), - MON("shrieker", S_FUNGUS, LVL(3, 1, 7, 0, 0), (G_GENO | 1), + M2_HOSTILE | M2_NEUTER, M3_INFRAVISIBLE, + 2, CLR_RED, RED_MOLD), + MON(NAM("shrieker"), S_FUNGUS, + LVL(3, 1, 7, 0, 0), (G_GENO | 1), A(NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(100, 100, MS_SHRIEK, MZ_SMALL), MR_POISON, MR_POISON, M1_BREATHLESS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_NOTAKE, - M2_HOSTILE | M2_NEUTER, 0, 2, CLR_BRIGHT_MAGENTA, SHRIEKER), - MON("violet fungus", S_FUNGUS, LVL(3, 1, 7, 0, 0), (G_GENO | 2), + M2_HOSTILE | M2_NEUTER, 0, + 2, CLR_BRIGHT_MAGENTA, SHRIEKER), + MON(NAM("violet fungus"), S_FUNGUS, + LVL(3, 1, 7, 0, 0), (G_GENO | 2), A(ATTK(AT_TUCH, AD_PHYS, 1, 4), ATTK(AT_TUCH, AD_STCK, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(100, 100, MS_SILENT, MZ_SMALL), MR_POISON, MR_POISON, M1_BREATHLESS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_NOTAKE, - M2_HOSTILE | M2_NEUTER, 0, 5, CLR_MAGENTA, VIOLET_FUNGUS), - MON("black mold", S_FUNGUS, LVL(3, 0, 7, 0, 0), (G_GENO | 1), - A(ATTK(AT_NONE, AD_DISE, 0, 2), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_HOSTILE | M2_NEUTER, 0, + 5, CLR_MAGENTA, VIOLET_FUNGUS), + MON(NAM("black mold"), S_FUNGUS, + LVL(3, 0, 7, 0, 0), (G_GENO | 1), + A(ATTK(AT_NONE, AD_DISE, 0, 2), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(50, 30, MS_SILENT, MZ_SMALL), MR_POISON, 0, M1_BREATHLESS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_NOTAKE, - M2_HOSTILE | M2_NEUTER, 0, 8, CLR_BLACK, BLACK_MOLD), + M2_HOSTILE | M2_NEUTER, 0, + 8, CLR_BLACK, BLACK_MOLD), /* * Gnomes + * Unlike plain human|elf|orc, plain "gnome" is an ordinary monster. */ - MON("gnome", S_GNOME, LVL(1, 6, 10, 4, 0), (G_GENO | G_SGROUP | 1), - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("gnome"), S_GNOME, + LVL(1, 6, 10, 4, 0), (G_GENO | G_SGROUP | 1), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(650, 100, MS_ORC, MZ_SMALL), 0, 0, M1_HUMANOID | M1_OMNIVORE, - M2_NOPOLY | M2_GNOME | M2_COLLECT, M3_INFRAVISIBLE | M3_INFRAVISION, + M2_GNOME | M2_COLLECT, M3_INFRAVISIBLE | M3_INFRAVISION, 3, CLR_BROWN, GNOME), - MON3("gnome lord", "gnome lady", "gnome leader", + MON(NAMS("gnome lord", "gnome lady", "gnome leader"), S_GNOME, LVL(3, 8, 10, 4, 0), (G_GENO | 2), - A(ATTK(AT_WEAP, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(700, 120, MS_ORC, MZ_SMALL), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_GNOME | M2_LORD | M2_COLLECT, M3_INFRAVISIBLE | M3_INFRAVISION, 4, CLR_BLUE, GNOME_LEADER), - MON("gnomish wizard", S_GNOME, LVL(3, 10, 4, 10, 0), (G_GENO | 1), - A(ATTK(AT_MAGC, AD_SPEL, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("gnomish wizard"), S_GNOME, + LVL(3, 10, 4, 10, 0), (G_GENO | 1), + A(ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(700, 120, MS_ORC, MZ_SMALL), 0, 0, M1_HUMANOID | M1_OMNIVORE, - M2_GNOME | M2_MAGIC, M3_INFRAVISIBLE | M3_INFRAVISION, 5, HI_ZAP, - GNOMISH_WIZARD), - MON3("gnome king", "gnome queen", "gnome ruler", + M2_GNOME | M2_MAGIC, M3_INFRAVISIBLE | M3_INFRAVISION, + 5, HI_ZAP, GNOMISH_WIZARD), + MON(NAMS("gnome king", "gnome queen", "gnome ruler"), S_GNOME, LVL(5, 10, 10, 20, 0), (G_GENO | 1), - A(ATTK(AT_WEAP, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + A(ATTK(AT_WEAP, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(750, 150, MS_ORC, MZ_SMALL), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_GNOME | M2_PRINCE | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 6, HI_LORD, GNOME_RULER), + M3_INFRAVISIBLE | M3_INFRAVISION, + 6, HI_LORD, GNOME_RULER), /* * giant Humanoids */ - MON("giant", S_GIANT, LVL(6, 6, 0, 0, 2), (G_GENO | G_NOGEN | 1), - A(ATTK(AT_WEAP, AD_PHYS, 2, 10), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + /* plain giant is a placeholder for zombie and mummy corpses */ + MON(NAM("giant"), S_GIANT, + LVL(6, 6, 0, 0, 2), (G_GENO | G_NOGEN | 1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 10), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2250, 750, MS_BOAST, MZ_HUGE), 0, 0, M1_HUMANOID | M1_CARNIVORE, M2_GIANT | M2_STRONG | M2_ROCKTHROW | M2_NASTY | M2_COLLECT | M2_JEWELS, - M3_INFRAVISIBLE | M3_INFRAVISION, 8, CLR_CYAN, GIANT), - MON("hill giant", S_GIANT, LVL(6, 6, 0, 0, -2), (G_GENO | G_SGROUP | 1), - A(ATTK(AT_WEAP, AD_PHYS, 2, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE | M3_INFRAVISION, + 8, CLR_CYAN, GIANT), + MON(NAM("hill giant"), S_GIANT, + LVL(6, 6, 0, 0, -2), (G_GENO | G_SGROUP | 1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2200, 700, MS_BOAST, MZ_HUGE), 0, 0, M1_HUMANOID | M1_CARNIVORE, M2_GIANT | M2_STRONG | M2_ROCKTHROW | M2_NASTY | M2_COLLECT | M2_JEWELS, - M3_INFRAVISIBLE | M3_INFRAVISION, 8, CLR_CYAN, HILL_GIANT), - MON("stone giant", S_GIANT, LVL(8, 10, 6, 0, 2), (G_GENO | G_SGROUP | 1), - A(ATTK(AT_WEAP, AD_PHYS, 2, 10), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE | M3_INFRAVISION, + 8, CLR_CYAN, HILL_GIANT), + MON(NAM("stone giant"), S_GIANT, + LVL(8, 10, 6, 0, 2), (G_GENO | G_SGROUP | 1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 10), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2250, 750, MS_BOAST, MZ_HUGE), 0, 0, M1_HUMANOID | M1_CARNIVORE, M2_GIANT | M2_STRONG | M2_ROCKTHROW | M2_NASTY | M2_COLLECT | M2_JEWELS, - M3_INFRAVISIBLE | M3_INFRAVISION, 10, CLR_GRAY, STONE_GIANT), - MON("fire giant", S_GIANT, LVL(9, 12, 4, 5, 2), (G_GENO | G_SGROUP | 1), + M3_INFRAVISIBLE | M3_INFRAVISION, + 10, CLR_GRAY, STONE_GIANT), + MON(NAM("fire giant"), S_GIANT, + LVL(9, 12, 4, 5, 2), (G_GENO | G_SGROUP | 1), A(ATTK(AT_WEAP, AD_PHYS, 2, 10), ATTK(AT_TUCH, AD_FIRE, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2250, 750, MS_BOAST, MZ_HUGE), MR_FIRE, MR_FIRE, M1_HUMANOID | M1_CARNIVORE, M2_GIANT | M2_STRONG | M2_ROCKTHROW | M2_NASTY | M2_COLLECT | M2_JEWELS, - M3_INFRAVISIBLE | M3_INFRAVISION, 11, CLR_YELLOW, FIRE_GIANT), - MON("frost giant", S_GIANT, LVL(10, 12, 3, 10, -3), + M3_INFRAVISIBLE | M3_INFRAVISION, + 11, CLR_YELLOW, FIRE_GIANT), + MON(NAM("frost giant"), + S_GIANT, LVL(10, 12, 3, 10, -3), (G_NOHELL | G_GENO | G_SGROUP | 1), A(ATTK(AT_WEAP, AD_PHYS, 2, 12), ATTK(AT_TUCH, AD_COLD, 3, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2250, 750, MS_BOAST, MZ_HUGE), MR_COLD, MR_COLD, M1_HUMANOID | M1_CARNIVORE, M2_GIANT | M2_STRONG | M2_ROCKTHROW | M2_NASTY | M2_COLLECT | M2_JEWELS, - M3_INFRAVISIBLE | M3_INFRAVISION, 13, CLR_WHITE, FROST_GIANT), - MON("ettin", S_GIANT, LVL(10, 12, 3, 0, 0), (G_GENO | 1), - A(ATTK(AT_WEAP, AD_PHYS, 2, 8), ATTK(AT_WEAP, AD_PHYS, 3, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISIBLE | M3_INFRAVISION, + 13, CLR_WHITE, FROST_GIANT), + /* ettin is a two-headed giant but its corpse doesn't confer strength */ + MON(NAM("ettin"), S_GIANT, + LVL(10, 12, 3, 0, 0), (G_GENO | 1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 8), ATTK(AT_WEAP, AD_PHYS, 3, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1700, 500, MS_GRUNT, MZ_HUGE), 0, 0, M1_ANIMAL | M1_HUMANOID | M1_CARNIVORE, M2_HOSTILE | M2_STRONG | M2_NASTY | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 13, CLR_BROWN, ETTIN), - MON("storm giant", S_GIANT, LVL(16, 12, 3, 10, -3), - (G_GENO | G_SGROUP | 1), + M3_INFRAVISIBLE | M3_INFRAVISION, + 13, CLR_BROWN, ETTIN), + MON(NAM("storm giant"), S_GIANT, + LVL(16, 12, 3, 10, -3), (G_GENO | G_SGROUP | 1), A(ATTK(AT_WEAP, AD_PHYS, 2, 12), ATTK(AT_MAGC, AD_ELEC, 4, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2250, 750, MS_BOAST, MZ_HUGE), MR_ELEC, MR_ELEC, M1_HUMANOID | M1_CARNIVORE, M2_GIANT | M2_STRONG | M2_ROCKTHROW | M2_NASTY | M2_COLLECT | M2_JEWELS, - M3_INFRAVISIBLE | M3_INFRAVISION, 19, CLR_BLUE, STORM_GIANT), - MON("titan", S_GIANT, LVL(16, 18, -3, 70, 9), (1), - A(ATTK(AT_WEAP, AD_PHYS, 2, 8), ATTK(AT_MAGC, AD_SPEL, 0, 0), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISIBLE | M3_INFRAVISION, + 19, CLR_BLUE, STORM_GIANT), + MON(NAM("titan"), S_GIANT, + LVL(16, 18, -3, 70, 9), (1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 8), ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2300, 900, MS_SPELL, MZ_HUGE), 0, 0, M1_FLY | M1_HUMANOID | M1_OMNIVORE, M2_STRONG | M2_ROCKTHROW | M2_NASTY | M2_COLLECT | M2_MAGIC, - M3_INFRAVISIBLE | M3_INFRAVISION, 20, CLR_MAGENTA, TITAN), - MON("minotaur", S_GIANT, LVL(15, 15, 6, 0, 0), (G_GENO | G_NOGEN), + M3_INFRAVISIBLE | M3_INFRAVISION, + 20, CLR_MAGENTA, TITAN), + MON(NAM("minotaur"), S_GIANT, + LVL(15, 15, 6, 0, 0), (G_GENO | G_NOGEN), A(ATTK(AT_CLAW, AD_PHYS, 3, 10), ATTK(AT_CLAW, AD_PHYS, 3, 10), ATTK(AT_BUTT, AD_PHYS, 2, 8), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1500, 700, MS_MOO, MZ_LARGE), 0, 0, @@ -1535,7 +1814,8 @@ */ /* the illustration from _Through_the_Looking_Glass_ depicts hands as well as wings */ - MON("jabberwock", S_JABBERWOCK, LVL(15, 12, -2, 50, 0), (G_GENO | 1), + MON(NAM("jabberwock"), S_JABBERWOCK, + LVL(15, 12, -2, 50, 0), (G_GENO | 1), A(ATTK(AT_BITE, AD_PHYS, 2, 10), ATTK(AT_BITE, AD_PHYS, 2, 10), ATTK(AT_CLAW, AD_PHYS, 2, 10), ATTK(AT_CLAW, AD_PHYS, 2, 10), NO_ATTK, NO_ATTK), @@ -1544,7 +1824,7 @@ M2_HOSTILE | M2_STRONG | M2_NASTY | M2_COLLECT, M3_INFRAVISIBLE, 18, CLR_ORANGE, JABBERWOCK), #if 0 /* DEFERRED */ - MON("vorpal jabberwock", S_JABBERWOCK, + MON(NAM("vorpal jabberwock"), S_JABBERWOCK, LVL(20, 12, -2, 50, 0), (G_GENO | 1), A(ATTK(AT_BITE, AD_PHYS, 3, 10), ATTK(AT_BITE, AD_PHYS, 3, 10), ATTK(AT_CLAW, AD_PHYS, 3, 10), ATTK(AT_CLAW, AD_PHYS, 3, 10), @@ -1557,67 +1837,74 @@ /* * Kops */ - MON("Keystone Kop", S_KOP, LVL(1, 6, 10, 10, 9), - (G_GENO | G_LGROUP | G_NOGEN), - A(ATTK(AT_WEAP, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("Keystone Kop"), S_KOP, + LVL(1, 6, 10, 10, 9), (G_GENO | G_LGROUP | G_NOGEN), + A(ATTK(AT_WEAP, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 200, MS_ARREST, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_CARNIVORE, M2_HUMAN | M2_WANDER | M2_HOSTILE | M2_MALE | M2_COLLECT, - M3_INFRAVISIBLE, 3, CLR_GRAY, KEYSTONE_KOP), - MON("Kop Sergeant", S_KOP, LVL(2, 8, 10, 10, 10), - (G_GENO | G_SGROUP | G_NOGEN), - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE, + 3, CLR_GRAY, KEYSTONE_KOP), + MON(NAM("Kop Sergeant"), S_KOP, + LVL(2, 8, 10, 10, 10), (G_GENO | G_SGROUP | G_NOGEN), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 200, MS_ARREST, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_CARNIVORE, M2_HUMAN | M2_WANDER | M2_HOSTILE | M2_STRONG | M2_MALE | M2_COLLECT, - M3_INFRAVISIBLE, 4, CLR_BLUE, KOP_SERGEANT), - MON("Kop Lieutenant", S_KOP, LVL(3, 10, 10, 20, 11), (G_GENO | G_NOGEN), - A(ATTK(AT_WEAP, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE, + 4, CLR_BLUE, KOP_SERGEANT), + MON(NAM("Kop Lieutenant"), S_KOP, + LVL(3, 10, 10, 20, 11), (G_GENO | G_NOGEN), + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 200, MS_ARREST, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_CARNIVORE, M2_HUMAN | M2_WANDER | M2_HOSTILE | M2_STRONG | M2_MALE | M2_COLLECT, - M3_INFRAVISIBLE, 5, CLR_CYAN, KOP_LIEUTENANT), - MON("Kop Kaptain", S_KOP, LVL(4, 12, 10, 20, 12), (G_GENO | G_NOGEN), - A(ATTK(AT_WEAP, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE, + 5, CLR_CYAN, KOP_LIEUTENANT), + MON(NAM("Kop Kaptain"), S_KOP, + LVL(4, 12, 10, 20, 12), (G_GENO | G_NOGEN), + A(ATTK(AT_WEAP, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 200, MS_ARREST, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_CARNIVORE, M2_HUMAN | M2_WANDER | M2_HOSTILE | M2_STRONG | M2_MALE | M2_COLLECT, - M3_INFRAVISIBLE, 6, HI_LORD, KOP_KAPTAIN), + M3_INFRAVISIBLE, + 6, HI_LORD, KOP_KAPTAIN), /* * Liches */ - MON("lich", S_LICH, LVL(11, 6, 0, 30, -9), (G_GENO | G_NOCORPSE | 1), + MON(NAM("lich"), S_LICH, + LVL(11, 6, 0, 30, -9), (G_GENO | G_NOCORPSE | 1), A(ATTK(AT_TUCH, AD_COLD, 1, 10), ATTK(AT_MAGC, AD_SPEL, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1200, 100, MS_MUMBLE, MZ_HUMAN), MR_COLD | MR_SLEEP | MR_POISON, MR_COLD, M1_BREATHLESS | M1_HUMANOID | M1_POIS | M1_REGEN, - M2_UNDEAD | M2_HOSTILE | M2_MAGIC, M3_INFRAVISION, 14, CLR_BROWN, - LICH), - MON("demilich", S_LICH, LVL(14, 9, -2, 60, -12), - (G_GENO | G_NOCORPSE | 1), - A(ATTK(AT_TUCH, AD_COLD, 3, 4), ATTK(AT_MAGC, AD_SPEL, 0, 0), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M2_UNDEAD | M2_HOSTILE | M2_MAGIC, M3_INFRAVISION, + 14, CLR_BROWN, LICH), + MON(NAM("demilich"), S_LICH, + LVL(14, 9, -2, 60, -12), (G_GENO | G_NOCORPSE | 1), + A(ATTK(AT_TUCH, AD_COLD, 3, 4), ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1200, 100, MS_MUMBLE, MZ_HUMAN), MR_COLD | MR_SLEEP | MR_POISON, MR_COLD, M1_BREATHLESS | M1_HUMANOID | M1_POIS | M1_REGEN, - M2_UNDEAD | M2_HOSTILE | M2_MAGIC, M3_INFRAVISION, 18, CLR_RED, - DEMILICH), - MON("master lich", S_LICH, LVL(17, 9, -4, 90, -15), - (G_HELL | G_GENO | G_NOCORPSE | 1), - A(ATTK(AT_TUCH, AD_COLD, 3, 6), ATTK(AT_MAGC, AD_SPEL, 0, 0), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M2_UNDEAD | M2_HOSTILE | M2_MAGIC, M3_INFRAVISION, + 18, CLR_RED, DEMILICH), + MON(NAM("master lich"), S_LICH, + LVL(17, 9, -4, 90, -15), (G_HELL | G_GENO | G_NOCORPSE | 1), + A(ATTK(AT_TUCH, AD_COLD, 3, 6), ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1200, 100, MS_MUMBLE, MZ_HUMAN), MR_FIRE | MR_COLD | MR_SLEEP | MR_POISON, MR_FIRE | MR_COLD, M1_BREATHLESS | M1_HUMANOID | M1_POIS | M1_REGEN, M2_UNDEAD | M2_HOSTILE | M2_MAGIC, M3_WANTSBOOK | M3_INFRAVISION, 21, HI_LORD, MASTER_LICH), - MON("arch-lich", S_LICH, LVL(25, 9, -6, 90, -15), - (G_HELL | G_GENO | G_NOCORPSE | 1), - A(ATTK(AT_TUCH, AD_COLD, 5, 6), ATTK(AT_MAGC, AD_SPEL, 0, 0), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + MON(NAM("arch-lich"), S_LICH, + LVL(25, 9, -6, 90, -15), (G_HELL | G_GENO | G_NOCORPSE | 1), + A(ATTK(AT_TUCH, AD_COLD, 5, 6), ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1200, 100, MS_MUMBLE, MZ_HUMAN), MR_FIRE | MR_COLD | MR_SLEEP | MR_ELEC | MR_POISON, MR_FIRE | MR_COLD, M1_BREATHLESS | M1_HUMANOID | M1_POIS | M1_REGEN, @@ -1626,163 +1913,188 @@ /* * Mummies */ - MON("kobold mummy", S_MUMMY, LVL(3, 8, 6, 20, -2), - (G_GENO | G_NOCORPSE | 1), + MON(NAM("kobold mummy"), S_MUMMY, + LVL(3, 8, 6, 20, -2), (G_GENO | G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_TUCH, AD_WTHR, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(400, 50, MS_SILENT, MZ_SMALL), MR_COLD | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID | M1_POIS, - M2_UNDEAD | M2_HOSTILE, M3_INFRAVISION, 7, CLR_BROWN, KOBOLD_MUMMY), - MON("gnome mummy", S_MUMMY, LVL(4, 10, 6, 20, -3), - (G_GENO | G_NOCORPSE | 1), + M2_UNDEAD | M2_HOSTILE, M3_INFRAVISION, + 7, CLR_BROWN, KOBOLD_MUMMY), + MON(NAM("gnome mummy"), S_MUMMY, + LVL(4, 10, 6, 20, -3), (G_GENO | G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_TUCH, AD_WTHR, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(650, 50, MS_SILENT, MZ_SMALL), MR_COLD | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID | M1_POIS, - M2_UNDEAD | M2_HOSTILE | M2_GNOME, M3_INFRAVISION, 8, CLR_ORANGE, - GNOME_MUMMY), - MON("orc mummy", S_MUMMY, LVL(5, 10, 5, 20, -4), - (G_GENO | G_NOCORPSE | 1), + M2_UNDEAD | M2_HOSTILE | M2_GNOME, M3_INFRAVISION, + 8, CLR_ORANGE, GNOME_MUMMY), + MON(NAM("orc mummy"), S_MUMMY, + LVL(5, 10, 5, 20, -4), (G_GENO | G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_TUCH, AD_WTHR, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(850, 75, MS_SILENT, MZ_HUMAN), MR_COLD | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID | M1_POIS, M2_UNDEAD | M2_HOSTILE | M2_STRONG | M2_ORC | M2_GREEDY | M2_JEWELS, - M3_INFRAVISION, 9, CLR_GRAY, ORC_MUMMY), - MON("dwarf mummy", S_MUMMY, LVL(5, 10, 5, 20, -4), - (G_GENO | G_NOCORPSE | 1), + M3_INFRAVISION, + 9, CLR_GRAY, ORC_MUMMY), + MON(NAM("dwarf mummy"), S_MUMMY, + LVL(5, 10, 5, 20, -4), (G_GENO | G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_TUCH, AD_WTHR, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(900, 150, MS_SILENT, MZ_HUMAN), MR_COLD | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID | M1_POIS, M2_UNDEAD | M2_HOSTILE | M2_DWARF | M2_GREEDY | M2_JEWELS, - M3_INFRAVISION, 9, CLR_RED, DWARF_MUMMY), - MON("elf mummy", S_MUMMY, LVL(6, 12, 4, 30, -5), - (G_GENO | G_NOCORPSE | 1), + M3_INFRAVISION, + 9, CLR_RED, DWARF_MUMMY), + MON(NAM("elf mummy"), S_MUMMY, + LVL(6, 12, 4, 30, -5), (G_GENO | G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_PHYS, 2, 4), ATTK(AT_TUCH, AD_WTHR, 3, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_ELF, 175, MS_SILENT, MZ_HUMAN), MR_COLD | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID | M1_POIS, - M2_UNDEAD | M2_HOSTILE | M2_ELF, M3_INFRAVISION, 10, CLR_GREEN, - ELF_MUMMY), - MON("human mummy", S_MUMMY, LVL(6, 12, 4, 30, -5), - (G_GENO | G_NOCORPSE | 1), + M2_UNDEAD | M2_HOSTILE | M2_ELF, M3_INFRAVISION, + 10, CLR_GREEN, ELF_MUMMY), + MON(NAM("human mummy"), S_MUMMY, + LVL(6, 12, 4, 30, -5), (G_GENO | G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_PHYS, 2, 4), ATTK(AT_TUCH, AD_WTHR, 3, 4), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 200, MS_SILENT, MZ_HUMAN), MR_COLD | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID | M1_POIS, - M2_UNDEAD | M2_HOSTILE, M3_INFRAVISION, 11, HI_DOMESTIC, - HUMAN_MUMMY), - MON("ettin mummy", S_MUMMY, LVL(7, 12, 4, 30, -6), - (G_GENO | G_NOCORPSE | 1), + M2_UNDEAD | M2_HOSTILE, M3_INFRAVISION, + 11, HI_DOMESTIC, HUMAN_MUMMY), + MON(NAM("ettin mummy"), S_MUMMY, + LVL(7, 12, 4, 30, -6), (G_GENO | G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_PHYS, 2, 6), ATTK(AT_CLAW, AD_PHYS, 2, 6), ATTK(AT_TUCH, AD_WTHR, 3, 4), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1700, 250, MS_SILENT, MZ_HUGE), MR_COLD | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID | M1_POIS, - M2_UNDEAD | M2_HOSTILE | M2_STRONG, M3_INFRAVISION, 12, CLR_BLUE, - ETTIN_MUMMY), - MON("giant mummy", S_MUMMY, LVL(8, 14, 3, 30, -7), - (G_GENO | G_NOCORPSE | 1), + M2_UNDEAD | M2_HOSTILE | M2_STRONG, M3_INFRAVISION, + 12, CLR_BLUE, ETTIN_MUMMY), + MON(NAM("giant mummy"), S_MUMMY, + LVL(8, 14, 3, 30, -7), (G_GENO | G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_PHYS, 3, 4), ATTK(AT_CLAW, AD_PHYS, 3, 4), ATTK(AT_TUCH, AD_WTHR, 5, 4), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2050, 375, MS_SILENT, MZ_HUGE), MR_COLD | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID | M1_POIS, M2_UNDEAD | M2_HOSTILE | M2_GIANT | M2_STRONG | M2_JEWELS, - M3_INFRAVISION, 13, CLR_CYAN, GIANT_MUMMY), + M3_INFRAVISION, + 13, CLR_CYAN, GIANT_MUMMY), /* * Nagas */ - MON("red naga hatchling", S_NAGA, LVL(3, 10, 6, 0, 0), G_GENO, + MON(NAM("red naga hatchling"), S_NAGA, + LVL(3, 10, 6, 0, 0), G_GENO, A(ATTK(AT_BITE, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(500, 100, MS_MUMBLE, MZ_SMALL), MR_FIRE | MR_POISON, MR_POISON, M1_NOLIMBS | M1_SLITHY | M1_THICK_HIDE | M1_NOTAKE | M1_OMNIVORE, - M2_STRONG, M3_INFRAVISIBLE, 4, CLR_RED, RED_NAGA_HATCHLING), - MON("black naga hatchling", S_NAGA, LVL(3, 10, 6, 0, 0), G_GENO, + M2_STRONG, M3_INFRAVISIBLE, + 4, CLR_RED, RED_NAGA_HATCHLING), + MON(NAM("black naga hatchling"), S_NAGA, + LVL(3, 10, 6, 0, 0), G_GENO, A(ATTK(AT_BITE, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(500, 100, MS_MUMBLE, MZ_SMALL), MR_POISON | MR_ACID | MR_STONE, MR_POISON, M1_NOLIMBS | M1_SLITHY | M1_THICK_HIDE | M1_ACID | M1_NOTAKE | M1_CARNIVORE, - M2_STRONG, 0, 4, CLR_BLACK, BLACK_NAGA_HATCHLING), - MON("golden naga hatchling", S_NAGA, LVL(3, 10, 6, 0, 0), G_GENO, - A(ATTK(AT_BITE, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_STRONG, 0, + 4, CLR_BLACK, BLACK_NAGA_HATCHLING), + MON(NAM("golden naga hatchling"), S_NAGA, + LVL(3, 10, 6, 0, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(500, 100, MS_MUMBLE, MZ_SMALL), MR_POISON, MR_POISON, M1_NOLIMBS | M1_SLITHY | M1_THICK_HIDE | M1_NOTAKE | M1_OMNIVORE, - M2_STRONG, 0, 4, HI_GOLD, GOLDEN_NAGA_HATCHLING), - MON("guardian naga hatchling", S_NAGA, LVL(3, 10, 6, 0, 0), G_GENO, - A(ATTK(AT_BITE, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_STRONG, 0, + 4, HI_GOLD, GOLDEN_NAGA_HATCHLING), + MON(NAM("guardian naga hatchling"), S_NAGA, + LVL(3, 10, 6, 0, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(500, 100, MS_MUMBLE, MZ_SMALL), MR_POISON, MR_POISON, M1_NOLIMBS | M1_SLITHY | M1_THICK_HIDE | M1_NOTAKE | M1_OMNIVORE, - M2_STRONG, 0, 4, CLR_GREEN, GUARDIAN_NAGA_HATCHLING), - MON("red naga", S_NAGA, LVL(6, 12, 4, 0, -4), (G_GENO | 1), + M2_STRONG, 0, + 4, CLR_GREEN, GUARDIAN_NAGA_HATCHLING), + MON(NAM("red naga"), S_NAGA, + LVL(6, 12, 4, 0, -4), (G_GENO | 1), A(ATTK(AT_BITE, AD_PHYS, 2, 4), ATTK(AT_BREA, AD_FIRE, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2600, 400, MS_MUMBLE, MZ_HUGE), MR_FIRE | MR_POISON, MR_FIRE | MR_POISON, M1_NOLIMBS | M1_SLITHY | M1_THICK_HIDE | M1_OVIPAROUS | M1_NOTAKE | M1_OMNIVORE, - M2_STRONG, M3_INFRAVISIBLE, 8, CLR_RED, RED_NAGA), - MON("black naga", S_NAGA, LVL(8, 14, 2, 10, 4), (G_GENO | 1), + M2_STRONG, M3_INFRAVISIBLE, + 8, CLR_RED, RED_NAGA), + MON(NAM("black naga"), S_NAGA, + LVL(8, 14, 2, 10, 4), (G_GENO | 1), A(ATTK(AT_BITE, AD_PHYS, 2, 6), ATTK(AT_SPIT, AD_ACID, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2600, 400, MS_MUMBLE, MZ_HUGE), MR_POISON | MR_ACID | MR_STONE, MR_POISON | MR_ACID | MR_STONE, M1_NOLIMBS | M1_SLITHY | M1_THICK_HIDE | M1_OVIPAROUS | M1_ACID | M1_NOTAKE | M1_CARNIVORE, - M2_STRONG, 0, 10, CLR_BLACK, BLACK_NAGA), - MON("golden naga", S_NAGA, LVL(10, 14, 2, 70, 5), (G_GENO | 1), + M2_STRONG, 0, + 10, CLR_BLACK, BLACK_NAGA), + MON(NAM("golden naga"), S_NAGA, + LVL(10, 14, 2, 70, 5), (G_GENO | 1), A(ATTK(AT_BITE, AD_PHYS, 2, 6), ATTK(AT_MAGC, AD_SPEL, 4, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2600, 400, MS_MUMBLE, MZ_HUGE), MR_POISON, MR_POISON, M1_NOLIMBS | M1_SLITHY | M1_THICK_HIDE | M1_OVIPAROUS | M1_NOTAKE | M1_OMNIVORE, - M2_STRONG, 0, 13, HI_GOLD, GOLDEN_NAGA), + M2_STRONG, 0, + 13, HI_GOLD, GOLDEN_NAGA), /* 3.7: guardian naga used to have three attacks: bite, spit, hug but in order for the hug to succeed the two preceding attacks had - to have hit, and its not possible to both bite and spit, hence + to have hit, and it's not possible to both bite and spit, hence the hug never hit; change to spit, bite, touch, hug; if the bite and touch hit, the hug will too */ - MON("guardian naga", S_NAGA, LVL(12, 16, 0, 50, 7), (G_GENO | 1), + MON(NAM("guardian naga"), S_NAGA, + LVL(12, 16, 0, 50, 7), (G_GENO | 1), A(ATTK(AT_SPIT, AD_DRST, 1, 6), ATTK(AT_BITE, AD_PLYS, 1, 6), ATTK(AT_TUCH, AD_PHYS, 0, 0), ATTK(AT_HUGS, AD_WRAP, 2, 4), NO_ATTK, NO_ATTK), SIZ(2600, 400, MS_MUMBLE, MZ_HUGE), MR_POISON, MR_POISON, M1_NOLIMBS | M1_SLITHY | M1_THICK_HIDE | M1_OVIPAROUS | M1_POIS | M1_NOTAKE | M1_OMNIVORE, - M2_STRONG, 0, 17, CLR_GREEN, GUARDIAN_NAGA), + M2_STRONG, 0, + 17, CLR_GREEN, GUARDIAN_NAGA), /* * Ogres */ - MON("ogre", S_OGRE, LVL(5, 10, 5, 0, -3), (G_SGROUP | G_GENO | 1), - A(ATTK(AT_WEAP, AD_PHYS, 2, 5), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("ogre"), S_OGRE, + LVL(5, 10, 5, 0, -3), (G_SGROUP | G_GENO | 1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 5), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1600, 500, MS_GRUNT, MZ_LARGE), 0, 0, M1_HUMANOID | M1_CARNIVORE, M2_STRONG | M2_GREEDY | M2_JEWELS | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 7, CLR_BROWN, OGRE), - MON3("ogre lord", "ogre lady", "ogre leader", - S_OGRE, LVL(7, 12, 3, 30, -5), (G_GENO | 2), - A(ATTK(AT_WEAP, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE | M3_INFRAVISION, + 7, CLR_BROWN, OGRE), + MON(NAMS("ogre lord", "ogre lady", "ogre leader"), S_OGRE, + LVL(7, 12, 3, 30, -5), (G_GENO | 2), + A(ATTK(AT_WEAP, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1700, 700, MS_GRUNT, MZ_LARGE), 0, 0, M1_HUMANOID | M1_CARNIVORE, M2_STRONG | M2_LORD | M2_GREEDY | M2_JEWELS | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 9, CLR_RED, OGRE_LEADER), - MON3("ogre king", "ogre queen", "ogre tyrant", - S_OGRE, LVL(9, 14, 4, 60, -7), (G_GENO | 2), - A(ATTK(AT_WEAP, AD_PHYS, 3, 5), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE | M3_INFRAVISION, + 9, CLR_RED, OGRE_LEADER), + MON(NAMS("ogre king", "ogre queen", "ogre tyrant"), S_OGRE, + LVL(9, 14, 4, 60, -7), (G_GENO | 2), + A(ATTK(AT_WEAP, AD_PHYS, 3, 5), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1700, 750, MS_GRUNT, MZ_LARGE), 0, 0, M1_HUMANOID | M1_CARNIVORE, M2_STRONG | M2_PRINCE | M2_GREEDY | M2_JEWELS | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 11, HI_LORD, OGRE_TYRANT), + M3_INFRAVISIBLE | M3_INFRAVISION, + 11, HI_LORD, OGRE_TYRANT), /* * Puddings * * must be in the same order as the pudding globs in objects.c */ - MON("gray ooze", S_PUDDING, LVL(3, 1, 8, 0, 0), (G_GENO | G_NOCORPSE | 2), + MON(NAM("gray ooze"), S_PUDDING, + LVL(3, 1, 8, 0, 0), (G_GENO | G_NOCORPSE | 2), A(ATTK(AT_BITE, AD_RUST, 2, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(500, 250, MS_SILENT, MZ_MEDIUM), @@ -1790,9 +2102,10 @@ MR_FIRE | MR_COLD | MR_POISON, M1_BREATHLESS | M1_AMORPHOUS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_OMNIVORE | M1_ACID, - M2_HOSTILE | M2_NEUTER, 0, 4, CLR_GRAY, GRAY_OOZE), - MON("brown pudding", S_PUDDING, LVL(5, 3, 8, 0, 0), - (G_GENO | G_NOCORPSE | 1), + M2_HOSTILE | M2_NEUTER, 0, + 4, CLR_GRAY, GRAY_OOZE), + MON(NAM("brown pudding"), S_PUDDING, + LVL(5, 3, 8, 0, 0), (G_GENO | G_NOCORPSE | 1), A(ATTK(AT_BITE, AD_DCAY, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(500, 250, MS_SILENT, MZ_MEDIUM), @@ -1800,9 +2113,10 @@ MR_COLD | MR_ELEC | MR_POISON, M1_BREATHLESS | M1_AMORPHOUS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_OMNIVORE | M1_ACID, - M2_HOSTILE | M2_NEUTER, 0, 6, CLR_BROWN, BROWN_PUDDING), - MON("green slime", S_PUDDING, LVL(6, 6, 6, 0, 0), - (G_HELL | G_GENO | G_NOCORPSE | 1), + M2_HOSTILE | M2_NEUTER, 0, + 6, CLR_BROWN, BROWN_PUDDING), + MON(NAM("green slime"), S_PUDDING, + LVL(6, 6, 6, 0, 0), (G_HELL | G_GENO | G_NOCORPSE | 1), A(ATTK(AT_TUCH, AD_SLIM, 1, 4), ATTK(AT_ENGL, AD_SLIM, 1, 1), ATTK(AT_NONE, AD_SLIM, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(400, 150, MS_SILENT, MZ_LARGE), @@ -1810,9 +2124,10 @@ M1_BREATHLESS | M1_AMORPHOUS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_OMNIVORE | M1_ACID | M1_POIS | M1_CLING | M1_HIDE, - M2_HOSTILE | M2_NEUTER, 0, 8, CLR_GREEN, GREEN_SLIME), - MON("black pudding", S_PUDDING, LVL(10, 6, 6, 0, 0), - (G_GENO | G_NOCORPSE | 1), + M2_HOSTILE | M2_NEUTER, 0, + 8, CLR_GREEN, GREEN_SLIME), + MON(NAM("black pudding"), S_PUDDING, + LVL(10, 6, 6, 0, 0), (G_GENO | G_NOCORPSE | 1), A(ATTK(AT_BITE, AD_CORR, 3, 8), ATTK(AT_NONE, AD_CORR, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(900, 250, MS_SILENT, MZ_LARGE), @@ -1820,214 +2135,259 @@ MR_COLD | MR_ELEC | MR_POISON, M1_BREATHLESS | M1_AMORPHOUS | M1_NOEYES | M1_NOLIMBS | M1_NOHEAD | M1_MINDLESS | M1_OMNIVORE | M1_ACID, - M2_HOSTILE | M2_NEUTER, 0, 12, CLR_BLACK, BLACK_PUDDING), + M2_HOSTILE | M2_NEUTER, 0, + 12, CLR_BLACK, BLACK_PUDDING), /* * Q - elves (Quendi) */ - MON("elf", S_ELF, LVL(10, 12, 10, 2, -3), G_NOGEN, /* for corpses */ - A(ATTK(AT_WEAP, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + /* plain "elf" is a placeholder, not a normal monster */ + MON(NAM("elf"), S_ELF, + LVL(10, 12, 10, 2, -3), G_NOGEN, /* for corpses */ + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_ELF, 350, MS_HUMANOID, MZ_HUMAN), MR_SLEEP, MR_SLEEP, M1_HUMANOID | M1_OMNIVORE | M1_SEE_INVIS, M2_NOPOLY | M2_ELF | M2_COLLECT, - M3_INFRAVISION | M3_INFRAVISIBLE, 1, HI_DOMESTIC, ELF), - MON("Woodland-elf", S_ELF, LVL(4, 12, 10, 10, -5), - (G_GENO | G_SGROUP | 2), A(ATTK(AT_WEAP, AD_PHYS, 2, 4), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISION | M3_INFRAVISIBLE, + 1, HI_DOMESTIC, ELF), + MON(NAM("Woodland-elf"), S_ELF, + LVL(4, 12, 10, 10, -5), (G_GENO | G_SGROUP | 2), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_ELF, 350, MS_HUMANOID, MZ_HUMAN), MR_SLEEP, MR_SLEEP, M1_HUMANOID | M1_OMNIVORE | M1_SEE_INVIS, M2_ELF | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 6, CLR_GREEN, WOODLAND_ELF), - MON("Green-elf", S_ELF, LVL(5, 12, 10, 10, -6), (G_GENO | G_SGROUP | 2), - A(ATTK(AT_WEAP, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE | M3_INFRAVISION, + 6, CLR_GREEN, WOODLAND_ELF), + MON(NAM("Green-elf"), S_ELF, + LVL(5, 12, 10, 10, -6), (G_GENO | G_SGROUP | 2), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_ELF, 350, MS_HUMANOID, MZ_HUMAN), MR_SLEEP, MR_SLEEP, M1_HUMANOID | M1_OMNIVORE | M1_SEE_INVIS, M2_ELF | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 7, CLR_BRIGHT_GREEN, GREEN_ELF), - MON("Grey-elf", S_ELF, LVL(6, 12, 10, 10, -7), (G_GENO | G_SGROUP | 2), - A(ATTK(AT_WEAP, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE | M3_INFRAVISION, + 7, CLR_BRIGHT_GREEN, GREEN_ELF), + MON(NAM("Grey-elf"), S_ELF, + LVL(6, 12, 10, 10, -7), (G_GENO | G_SGROUP | 2), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_ELF, 350, MS_HUMANOID, MZ_HUMAN), MR_SLEEP, MR_SLEEP, M1_HUMANOID | M1_OMNIVORE | M1_SEE_INVIS, M2_ELF | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 8, CLR_GRAY, GREY_ELF), - MON3("elf-lord", "elf-lady", "elf-noble", - S_ELF, LVL(8, 12, 10, 20, -9), (G_GENO | G_SGROUP | 2), - A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_WEAP, AD_PHYS, 2, 4), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISIBLE | M3_INFRAVISION, + 8, CLR_GRAY, GREY_ELF), + MON(NAMS("elf-lord", "elf-lady", "elf-noble"), S_ELF, + LVL(8, 12, 10, 20, -9), (G_GENO | G_SGROUP | 2), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_ELF, 350, MS_HUMANOID, MZ_HUMAN), MR_SLEEP, MR_SLEEP, M1_HUMANOID | M1_OMNIVORE | M1_SEE_INVIS, M2_ELF | M2_STRONG | M2_LORD | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 11, CLR_BRIGHT_BLUE, ELF_NOBLE), - MON3("Elvenking", "Elvenqueen", "elven monarch", - S_ELF, LVL(9, 12, 10, 25, -10), (G_GENO | 1), + M3_INFRAVISIBLE | M3_INFRAVISION, + 11, CLR_BRIGHT_BLUE, ELF_NOBLE), + MON(NAMS("Elvenking", "Elvenqueen", "elven monarch"), S_ELF, + LVL(9, 12, 10, 25, -10), (G_GENO | 1), A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_WEAP, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_ELF, 350, MS_HUMANOID, MZ_HUMAN), MR_SLEEP, MR_SLEEP, M1_HUMANOID | M1_OMNIVORE | M1_SEE_INVIS, M2_ELF | M2_STRONG | M2_PRINCE | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 11, HI_LORD, ELVEN_MONARCH), + M3_INFRAVISIBLE | M3_INFRAVISION, + 11, HI_LORD, ELVEN_MONARCH), /* * Rust monster or disenchanter */ - MON("rust monster", S_RUSTMONST, LVL(5, 18, 2, 0, 0), (G_GENO | 2), + MON(NAM("rust monster"), S_RUSTMONST, + LVL(5, 18, 2, 0, 0), (G_GENO | 2), A(ATTK(AT_TUCH, AD_RUST, 0, 0), ATTK(AT_TUCH, AD_RUST, 0, 0), ATTK(AT_NONE, AD_RUST, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1000, 250, MS_SILENT, MZ_MEDIUM), 0, 0, M1_SWIM | M1_ANIMAL | M1_NOHANDS | M1_METALLIVORE, M2_HOSTILE, - M3_INFRAVISIBLE, 8, CLR_BROWN, RUST_MONSTER), - MON("disenchanter", S_RUSTMONST, LVL(12, 12, -10, 0, -3), - (G_HELL | G_GENO | 2), - A(ATTK(AT_CLAW, AD_ENCH, 4, 4), ATTK(AT_NONE, AD_ENCH, 0, 0), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISIBLE, + 8, CLR_BROWN, RUST_MONSTER), + MON(NAM("disenchanter"), S_RUSTMONST, + LVL(12, 12, -10, 0, -3), (G_HELL | G_GENO | 2), + A(ATTK(AT_CLAW, AD_ENCH, 4, 4), ATTK(AT_NONE, AD_ENCH, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(750, 200, MS_GROWL, MZ_LARGE), 0, 0, M1_ANIMAL | M1_CARNIVORE, - M2_HOSTILE, M3_INFRAVISIBLE, 14, CLR_BLUE, DISENCHANTER), + M2_HOSTILE, M3_INFRAVISIBLE, + 14, CLR_BLUE, DISENCHANTER), /* * Snakes + * Note: in the real world, most snakes are oviparous but some aren't; + * we treat all of these as if they are. */ - MON("garter snake", S_SNAKE, LVL(1, 8, 8, 0, 0), (G_LGROUP | G_GENO | 1), - A(ATTK(AT_BITE, AD_PHYS, 1, 2), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("garter snake"), S_SNAKE, + LVL(1, 8, 8, 0, 0), (G_LGROUP | G_GENO | 1), + A(ATTK(AT_BITE, AD_PHYS, 1, 2), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(50, 60, MS_HISS, MZ_TINY), 0, 0, M1_SWIM | M1_CONCEAL | M1_NOLIMBS | M1_ANIMAL | M1_SLITHY | M1_OVIPAROUS | M1_CARNIVORE | M1_NOTAKE, - 0, 0, 3, CLR_GREEN, GARTER_SNAKE), - MON("snake", S_SNAKE, LVL(4, 15, 3, 0, 0), (G_GENO | 2), - A(ATTK(AT_BITE, AD_DRST, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + 0, 0, + 3, CLR_GREEN, GARTER_SNAKE), + MON(NAM("snake"), S_SNAKE, + LVL(4, 15, 3, 0, 0), (G_GENO | 2), + A(ATTK(AT_BITE, AD_DRST, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(100, 80, MS_HISS, MZ_SMALL), MR_POISON, MR_POISON, M1_SWIM | M1_CONCEAL | M1_NOLIMBS | M1_ANIMAL | M1_SLITHY | M1_POIS | M1_OVIPAROUS | M1_CARNIVORE | M1_NOTAKE, - M2_HOSTILE, 0, 6, CLR_BROWN, SNAKE), - MON("water moccasin", S_SNAKE, LVL(4, 15, 3, 0, 0), - (G_GENO | G_NOGEN | G_LGROUP), - A(ATTK(AT_BITE, AD_DRST, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_HOSTILE, 0, + 6, CLR_BROWN, SNAKE), + MON(NAM("water moccasin"), S_SNAKE, + LVL(4, 15, 3, 0, 0), (G_GENO | G_NOGEN | G_LGROUP), + A(ATTK(AT_BITE, AD_DRST, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(150, 80, MS_HISS, MZ_SMALL), MR_POISON, MR_POISON, M1_SWIM | M1_CONCEAL | M1_NOLIMBS | M1_ANIMAL | M1_SLITHY | M1_POIS | M1_CARNIVORE | M1_OVIPAROUS | M1_NOTAKE, - M2_HOSTILE, 0, 7, CLR_RED, WATER_MOCCASIN), - MON("python", S_SNAKE, LVL(6, 3, 5, 0, 0), (G_GENO | 1), + M2_HOSTILE, 0, + 7, CLR_RED, WATER_MOCCASIN), + MON(NAM("python"), S_SNAKE, + LVL(6, 3, 5, 0, 0), (G_GENO | 1), A(ATTK(AT_BITE, AD_PHYS, 1, 4), ATTK(AT_TUCH, AD_PHYS, 0, 0), - ATTK(AT_HUGS, AD_WRAP, 1, 4), ATTK(AT_HUGS, AD_PHYS, 2, 4), NO_ATTK, - NO_ATTK), + ATTK(AT_HUGS, AD_WRAP, 1, 4), ATTK(AT_HUGS, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK), SIZ(250, 100, MS_HISS, MZ_LARGE), 0, 0, M1_SWIM | M1_NOLIMBS | M1_ANIMAL | M1_SLITHY | M1_CARNIVORE | M1_OVIPAROUS | M1_NOTAKE, - M2_HOSTILE | M2_STRONG, M3_INFRAVISION, 8, CLR_MAGENTA, PYTHON), - MON("pit viper", S_SNAKE, LVL(6, 15, 2, 0, 0), (G_GENO | 1), - A(ATTK(AT_BITE, AD_DRST, 1, 4), ATTK(AT_BITE, AD_DRST, 1, 4), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M2_HOSTILE | M2_STRONG, M3_INFRAVISION, + 8, CLR_MAGENTA, PYTHON), + MON(NAM("pit viper"), S_SNAKE, + LVL(6, 15, 2, 0, 0), (G_GENO | 1), + A(ATTK(AT_BITE, AD_DRST, 1, 4), ATTK(AT_BITE, AD_DRST, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(100, 60, MS_HISS, MZ_MEDIUM), MR_POISON, MR_POISON, M1_SWIM | M1_CONCEAL | M1_NOLIMBS | M1_ANIMAL | M1_SLITHY | M1_POIS | M1_CARNIVORE | M1_OVIPAROUS | M1_NOTAKE, - M2_HOSTILE, M3_INFRAVISION, 9, CLR_BLACK, PIT_VIPER), - MON("cobra", S_SNAKE, LVL(6, 18, 2, 0, 0), (G_GENO | 1), - A(ATTK(AT_BITE, AD_DRST, 2, 4), ATTK(AT_SPIT, AD_BLND, 0, 0), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M2_HOSTILE, M3_INFRAVISION, + 9, CLR_BLACK, PIT_VIPER), + MON(NAM("cobra"), S_SNAKE, + LVL(6, 18, 2, 0, 0), (G_GENO | 1), + A(ATTK(AT_BITE, AD_DRST, 2, 4), ATTK(AT_SPIT, AD_BLND, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(250, 100, MS_HISS, MZ_LARGE), MR_POISON, MR_POISON, M1_SWIM | M1_CONCEAL | M1_NOLIMBS | M1_ANIMAL | M1_SLITHY | M1_POIS | M1_CARNIVORE | M1_OVIPAROUS | M1_NOTAKE, - M2_HOSTILE, 0, 10, CLR_BLUE, COBRA), + M2_HOSTILE, 0, + 10, CLR_BLUE, COBRA), /* * Trolls */ - MON("troll", S_TROLL, LVL(7, 12, 4, 0, -3), (G_GENO | 2), + MON(NAM("troll"), S_TROLL, + LVL(7, 12, 4, 0, -3), (G_GENO | 2), A(ATTK(AT_WEAP, AD_PHYS, 4, 2), ATTK(AT_CLAW, AD_PHYS, 4, 2), ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(800, 350, MS_TROLL, MZ_LARGE), 0, 0, M1_HUMANOID | M1_REGEN | M1_CARNIVORE, M2_STRONG | M2_STALK | M2_HOSTILE, M3_INFRAVISIBLE | M3_INFRAVISION, 9, CLR_BROWN, TROLL), - MON("ice troll", S_TROLL, LVL(9, 10, 2, 20, -3), (G_NOHELL | G_GENO | 1), + MON(NAM("ice troll"), S_TROLL, + LVL(9, 10, 2, 20, -3), (G_NOHELL | G_GENO | 1), A(ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_CLAW, AD_COLD, 2, 6), ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1000, 300, MS_TROLL, MZ_LARGE), MR_COLD, MR_COLD, M1_HUMANOID | M1_REGEN | M1_CARNIVORE, M2_STRONG | M2_STALK | M2_HOSTILE, M3_INFRAVISIBLE | M3_INFRAVISION, 12, CLR_WHITE, ICE_TROLL), - MON("rock troll", S_TROLL, LVL(9, 12, 0, 0, -3), (G_GENO | 1), + MON(NAM("rock troll"), S_TROLL, + LVL(9, 12, 0, 0, -3), (G_GENO | 1), A(ATTK(AT_WEAP, AD_PHYS, 3, 6), ATTK(AT_CLAW, AD_PHYS, 2, 8), ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1200, 300, MS_TROLL, MZ_LARGE), MR_STONE, 0, M1_HUMANOID | M1_REGEN | M1_CARNIVORE, M2_STRONG | M2_STALK | M2_HOSTILE | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 12, CLR_CYAN, ROCK_TROLL), - MON("water troll", S_TROLL, LVL(11, 14, 4, 40, -3), (G_NOGEN | G_GENO), + M3_INFRAVISIBLE | M3_INFRAVISION, + 12, CLR_CYAN, ROCK_TROLL), + MON(NAM("water troll"), S_TROLL, + LVL(11, 14, 4, 40, -3), (G_NOGEN | G_GENO), A(ATTK(AT_WEAP, AD_PHYS, 2, 8), ATTK(AT_CLAW, AD_PHYS, 2, 8), ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1200, 350, MS_TROLL, MZ_LARGE), 0, 0, M1_HUMANOID | M1_REGEN | M1_CARNIVORE | M1_SWIM, M2_STRONG | M2_STALK | M2_HOSTILE, M3_INFRAVISIBLE | M3_INFRAVISION, 13, CLR_BLUE, WATER_TROLL), - MON("Olog-hai", S_TROLL, LVL(13, 12, -4, 0, -7), (G_GENO | 1), + MON(NAM("Olog-hai"), S_TROLL, + LVL(13, 12, -4, 0, -7), (G_GENO | 1), A(ATTK(AT_WEAP, AD_PHYS, 3, 6), ATTK(AT_CLAW, AD_PHYS, 2, 8), ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1500, 400, MS_TROLL, MZ_LARGE), 0, 0, M1_HUMANOID | M1_REGEN | M1_CARNIVORE, M2_STRONG | M2_STALK | M2_HOSTILE | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 16, HI_LORD, OLOG_HAI), + M3_INFRAVISIBLE | M3_INFRAVISION, + 16, HI_LORD, OLOG_HAI), /* * Umber hulk */ - MON("umber hulk", S_ABERRATION, LVL(9, 6, 2, 25, 0), (G_GENO | 2), + MON(NAM("umber hulk"), S_ABERRATION, + LVL(9, 6, 2, 25, 0), (G_GENO | 2), A(ATTK(AT_CLAW, AD_PHYS, 3, 4), ATTK(AT_CLAW, AD_PHYS, 3, 4), - ATTK(AT_BITE, AD_PHYS, 2, 5), ATTK(AT_GAZE, AD_CONF, 0, 0), NO_ATTK, - NO_ATTK), + ATTK(AT_BITE, AD_PHYS, 2, 5), ATTK(AT_GAZE, AD_CONF, 0, 0), + NO_ATTK, NO_ATTK), SIZ(1200, 500, MS_SILENT, MZ_LARGE), 0, 0, M1_TUNNEL | M1_CARNIVORE, - M2_STRONG, M3_INFRAVISIBLE, 12, CLR_BROWN, UMBER_HULK), - MON("quantum mechanic", S_ABERRATION, LVL(7, 12, 3, 10, 0), (G_GENO | 3), - A(ATTK(AT_CLAW, AD_TLPT, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_STRONG, M3_INFRAVISIBLE, + 12, CLR_BROWN, UMBER_HULK), + MON(NAM("quantum mechanic"), S_ABERRATION, + LVL(7, 12, 3, 10, 0), (G_GENO | 3), + A(ATTK(AT_CLAW, AD_TLPT, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 20, MS_HUMANOID, MZ_HUMAN), MR_POISON, 0, M1_HUMANOID | M1_OMNIVORE | M1_POIS | M1_TPORT, M2_HOSTILE, - M3_INFRAVISIBLE, 9, CLR_CYAN, QUANTUM_MECHANIC), - MON("mind flayer", S_ABERRATION, LVL(9, 12, 5, 90, -8), (G_GENO | 1), + M3_INFRAVISIBLE, + 9, CLR_CYAN, QUANTUM_MECHANIC), + MON(NAM("mind flayer"), S_ABERRATION, + LVL(9, 12, 5, 90, -8), (G_GENO | 1), A(ATTK(AT_WEAP, AD_PHYS, 1, 4), ATTK(AT_TENT, AD_DRIN, 2, 1), - ATTK(AT_TENT, AD_DRIN, 2, 1), ATTK(AT_TENT, AD_DRIN, 2, 1), NO_ATTK, - NO_ATTK), + ATTK(AT_TENT, AD_DRIN, 2, 1), ATTK(AT_TENT, AD_DRIN, 2, 1), + NO_ATTK, NO_ATTK), SIZ(1450, 400, MS_HISS, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_FLY | M1_SEE_INVIS | M1_OMNIVORE, M2_HOSTILE | M2_NASTY | M2_GREEDY | M2_JEWELS | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 13, CLR_MAGENTA, MIND_FLAYER), - MON("genetic engineer", S_ABERRATION, LVL(12, 12, 3, 10, 0), (G_GENO | 1), + M3_INFRAVISIBLE | M3_INFRAVISION, + 13, CLR_MAGENTA, MIND_FLAYER), + /* 3.7: from slash'em, to expand Q class; hit polymorphs target */ + MON(NAM("genetic engineer"), S_ABERRATION, + LVL(12, 12, 3, 10, 0), (G_GENO | 1), A(ATTK(AT_CLAW, AD_POLY, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 20, MS_HUMANOID, MZ_HUMAN), MR_POISON, 0, M1_HUMANOID | M1_OMNIVORE | M1_POIS | M1_TPORT, M2_HOSTILE | M2_NASTY, - M3_INFRAVISIBLE, 14, CLR_GREEN, GENETIC_ENGINEER), - MON("master mind flayer", S_ABERRATION, LVL(13, 12, 0, 90, -8), - (G_GENO | 1), + M3_INFRAVISIBLE, + 14, CLR_GREEN, GENETIC_ENGINEER), + MON(NAM("master mind flayer"), S_ABERRATION, + LVL(13, 12, 0, 90, -8), (G_GENO | 1), A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_TENT, AD_DRIN, 2, 1), ATTK(AT_TENT, AD_DRIN, 2, 1), ATTK(AT_TENT, AD_DRIN, 2, 1), ATTK(AT_TENT, AD_DRIN, 2, 1), ATTK(AT_TENT, AD_DRIN, 2, 1)), SIZ(1450, 400, MS_HISS, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_FLY | M1_SEE_INVIS | M1_OMNIVORE, M2_HOSTILE | M2_NASTY | M2_GREEDY | M2_JEWELS | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 19, CLR_BRIGHT_MAGENTA, - MASTER_MIND_FLAYER), + M3_INFRAVISIBLE | M3_INFRAVISION, + 19, CLR_BRIGHT_MAGENTA, MASTER_MIND_FLAYER), /* * Vampires */ - MON("vampire", S_VAMPIRE, LVL(10, 12, 1, 25, -8), - (G_GENO | G_NOCORPSE | 1), - A(ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_BITE, AD_DRLI, 1, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + MON(NAM("vampire"), S_VAMPIRE, + LVL(10, 12, 1, 25, -8), (G_GENO | G_NOCORPSE | 1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_BITE, AD_DRLI, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_VAMPIRE, MZ_HUMAN), MR_SLEEP | MR_POISON, 0, M1_FLY | M1_BREATHLESS | M1_HUMANOID | M1_POIS | M1_REGEN, M2_UNDEAD | M2_STALK | M2_HOSTILE | M2_STRONG | M2_NASTY | M2_SHAPESHIFTER, - M3_INFRAVISIBLE, 12, CLR_RED, VAMPIRE), - MON3("vampire lord", "vampire lady", "vampire leader", - S_VAMPIRE, LVL(12, 14, 0, 50, -9), - (G_GENO | G_NOCORPSE | 1), - A(ATTK(AT_CLAW, AD_PHYS, 1, 8), ATTK(AT_BITE, AD_DRLI, 1, 8), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISIBLE, + 12, CLR_RED, VAMPIRE), + MON(NAMS("vampire lord", "vampire lady", "vampire leader"), S_VAMPIRE, + LVL(12, 14, 0, 50, -9), (G_GENO | G_NOCORPSE | 1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 8), ATTK(AT_BITE, AD_DRLI, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_VAMPIRE, MZ_HUMAN), MR_SLEEP | MR_POISON, 0, M1_FLY | M1_BREATHLESS | M1_HUMANOID | M1_POIS | M1_REGEN, M2_UNDEAD | M2_STALK | M2_HOSTILE | M2_STRONG | M2_NASTY | M2_LORD | M2_SHAPESHIFTER, - M3_INFRAVISIBLE, 14, CLR_BLUE, VAMPIRE_LEADER), + M3_INFRAVISIBLE, + 14, CLR_BLUE, VAMPIRE_LEADER), #if 0 /* DEFERRED */ - MON("vampire mage", S_VAMPIRE, + MON(NAM("vampire mage"), S_VAMPIRE, LVL(20, 14, -4, 50, -9), (G_GENO | G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_DRLI, 2, 8), ATTK(AT_BITE, AD_DRLI, 1, 8), ATTK(AT_MAGC, AD_SPEL, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), @@ -2035,40 +2395,43 @@ M1_FLY | M1_BREATHLESS | M1_HUMANOID | M1_POIS | M1_REGEN, M2_UNDEAD | M2_STALK | M2_HOSTILE | M2_STRONG | M2_NASTY | M2_LORD | M2_MALE | M2_MAGIC | M2_SHAPESHIFTER, - M3_INFRAVISIBLE, 26, HI_ZAP, VAMPIRE_MAGE), + M3_INFRAVISIBLE, + 26, HI_ZAP, VAMPIRE_MAGE), #endif - MON("Vlad the Impaler", S_VAMPIRE, LVL(28, 26, -6, 80, -10), - (G_NOGEN | G_NOCORPSE | G_UNIQ), + MON(NAM("Vlad the Impaler"), S_VAMPIRE, + LVL(28, 26, -6, 80, -10), (G_NOGEN | G_NOCORPSE | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 2, 10), ATTK(AT_BITE, AD_DRLI, 1, 12), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_VAMPIRE, MZ_HUMAN), MR_SLEEP | MR_POISON, 0, M1_FLY | M1_BREATHLESS | M1_HUMANOID | M1_POIS | M1_REGEN, M2_NOPOLY | M2_UNDEAD | M2_STALK | M2_HOSTILE | M2_PNAME | M2_STRONG | M2_NASTY | M2_PRINCE | M2_MALE | M2_SHAPESHIFTER, - M3_WAITFORU | M3_WANTSCAND | M3_INFRAVISIBLE, 32, HI_LORD, - VLAD_THE_IMPALER), + M3_WAITFORU | M3_WANTSCAND | M3_INFRAVISIBLE, + 32, HI_LORD, VLAD_THE_IMPALER), /* * Wraiths */ - MON("barrow wight", S_WRAITH, LVL(3, 12, 5, 5, -3), - (G_GENO | G_NOCORPSE | 1), + MON(NAM("barrow wight"), S_WRAITH, + LVL(3, 12, 5, 5, -3), (G_GENO | G_NOCORPSE | 1), A(ATTK(AT_WEAP, AD_DRLI, 0, 0), ATTK(AT_MAGC, AD_SPEL, 0, 0), ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_TUCH, AD_COLD, 1, 4), NO_ATTK, NO_ATTK), SIZ(1200, 0, MS_SPELL, MZ_HUMAN), MR_COLD | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_HUMANOID, - M2_UNDEAD | M2_STALK | M2_HOSTILE | M2_COLLECT, 0, 8, HI_ZAP, - BARROW_WIGHT), - MON("wraith", S_WRAITH, 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), + M2_UNDEAD | M2_STALK | M2_HOSTILE | M2_COLLECT, 0, + 8, HI_ZAP, BARROW_WIGHT), + /* wraiths weigh 0 but can leave corpses */ + MON(NAM("wraith"), S_WRAITH, + 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), MR_COLD | MR_SLEEP | MR_POISON | MR_STONE, 0, M1_BREATHLESS | M1_FLY | M1_HUMANOID | M1_UNSOLID, - M2_UNDEAD | M2_STALK | M2_HOSTILE, 0, 8, CLR_BLACK, - WRAITH), - MON("Nazgul", S_WRAITH, LVL(13, 12, 0, 25, -17), - (G_GENO | G_NOCORPSE | 1), + M2_UNDEAD | M2_STALK | M2_HOSTILE, 0, + 8, CLR_BLACK, WRAITH), + MON(NAM("Nazgul"), S_WRAITH, + LVL(13, 12, 0, 25, -17), (G_GENO | G_NOCORPSE | 1), A(ATTK(AT_WEAP, AD_DRLI, 1, 4), ATTK(AT_BREA, AD_SLEE, 2, 25), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 0, MS_SPELL, MZ_HUMAN), MR_COLD | MR_SLEEP | MR_POISON, @@ -2076,294 +2439,364 @@ M2_NOPOLY | M2_UNDEAD | M2_STALK | M2_STRONG | M2_HOSTILE | M2_MALE | M2_COLLECT, 0, 17, HI_LORD, NAZGUL), - MON("ghost", S_WRAITH, LVL(4, 5, 5, 80, -5), (G_NOCORPSE | G_NOGEN), - A(ATTK(AT_TUCH, AD_PLYS, 1, 1), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + /* + * ghosts and shades don't leave corpses; assigning human weight + * to them matters for statues + */ + MON(NAM("ghost"), S_WRAITH, + LVL(4, 5, 5, 80, -5), (G_NOCORPSE | G_NOGEN), + A(ATTK(AT_TUCH, AD_PLYS, 1, 1), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 0, MS_SILENT, MZ_HUMAN), MR_COLD | MR_DISINT | MR_SLEEP | MR_POISON | MR_STONE, 0, M1_FLY | M1_BREATHLESS | M1_WALLWALK | M1_HUMANOID | M1_UNSOLID, M2_NOPOLY | M2_UNDEAD | M2_STALK | M2_HOSTILE, - M3_INFRAVISION | M3_NONCORPOREAL, 12, CLR_WHITE, GHOST), - MON("shade", S_WRAITH, LVL(12, 10, 10, 0, 0), (G_NOCORPSE | G_NOGEN), - A(ATTK(AT_TUCH, AD_PLYS, 2, 6), ATTK(AT_TUCH, AD_SLOW, 1, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISION | M3_NONCORPOREAL, + 12, CLR_WHITE, GHOST), + MON(NAM("shade"), S_WRAITH, + LVL(12, 10, 10, 0, 0), (G_NOCORPSE | G_NOGEN), + A(ATTK(AT_TUCH, AD_PLYS, 2, 6), ATTK(AT_TUCH, AD_SLOW, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 0, MS_WAIL, MZ_HUMAN), MR_COLD | MR_DISINT | MR_SLEEP | MR_POISON | MR_STONE, 0, M1_FLY | M1_BREATHLESS | M1_WALLWALK | M1_HUMANOID | M1_UNSOLID | M1_SEE_INVIS, M2_NOPOLY | M2_UNDEAD | M2_WANDER | M2_STALK | M2_HOSTILE | M2_NASTY, - M3_INFRAVISION | M3_NONCORPOREAL, 14, CLR_BLACK, SHADE), + M3_INFRAVISION | M3_NONCORPOREAL, + 14, CLR_BLACK, SHADE), /* * Xorn */ - MON("xorn", S_XORN, LVL(8, 9, -2, 20, 0), (G_GENO | 1), + MON(NAM("xorn"), S_XORN, + LVL(8, 9, -2, 20, 0), (G_GENO | 1), A(ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_CLAW, AD_PHYS, 1, 3), - ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_BITE, AD_PHYS, 4, 6), NO_ATTK, - NO_ATTK), + ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_BITE, AD_PHYS, 4, 6), + NO_ATTK, NO_ATTK), SIZ(1200, 700, MS_ROAR, MZ_LARGE), MR_FIRE | MR_COLD | MR_STONE, MR_STONE, M1_BREATHLESS | M1_WALLWALK | M1_THICK_HIDE | M1_METALLIVORE, - M2_HOSTILE | M2_STRONG, 0, 11, CLR_BROWN, XORN), + M2_HOSTILE | M2_STRONG, 0, + 11, CLR_BROWN, XORN), /* * Apelike beasts */ /* tameable via banana; does not grow up into ape... not flagged as domestic, so no guilt penalty for eating non-pet one */ - MON("monkey", S_YETI, LVL(2, 12, 6, 0, 0), (G_GENO | 1), - A(ATTK(AT_CLAW, AD_SITM, 0, 0), ATTK(AT_BITE, AD_PHYS, 1, 3), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + MON(NAM("monkey"), S_YETI, + LVL(2, 12, 6, 0, 0), (G_GENO | 1), + A(ATTK(AT_CLAW, AD_SITM, 0, 0), ATTK(AT_BITE, AD_PHYS, 1, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(100, 50, MS_GROWL, MZ_SMALL), 0, 0, M1_ANIMAL | M1_HUMANOID | M1_OMNIVORE, 0, M3_INFRAVISIBLE, 4, CLR_GRAY, MONKEY), - MON("ape", S_YETI, LVL(4, 12, 6, 0, 0), (G_GENO | G_SGROUP | 2), + MON(NAM("ape"), S_YETI, + LVL(4, 12, 6, 0, 0), (G_GENO | G_SGROUP | 2), A(ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_BITE, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1100, 500, MS_GROWL, MZ_LARGE), 0, 0, M1_ANIMAL | M1_HUMANOID | M1_OMNIVORE, M2_STRONG, M3_INFRAVISIBLE, 6, CLR_BROWN, APE), - MON("owlbear", S_YETI, LVL(5, 12, 5, 0, 0), (G_GENO | 3), + MON(NAM("owlbear"), S_YETI, + LVL(5, 12, 5, 0, 0), (G_GENO | 3), A(ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_HUGS, AD_PHYS, 2, 8), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1700, 700, MS_ROAR, MZ_LARGE), 0, 0, M1_ANIMAL | M1_HUMANOID | M1_CARNIVORE, - M2_HOSTILE | M2_STRONG | M2_NASTY, M3_INFRAVISIBLE, 7, CLR_ORANGE, - OWLBEAR), - MON("yeti", S_YETI, LVL(5, 15, 6, 0, 0), (G_GENO | 2), + M2_HOSTILE | M2_STRONG | M2_NASTY, M3_INFRAVISIBLE, + 7, CLR_ORANGE, OWLBEAR), + MON(NAM("yeti"), S_YETI, LVL(5, 15, 6, 0, 0), (G_GENO | 2), A(ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_BITE, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1600, 700, MS_GROWL, MZ_LARGE), MR_COLD, MR_COLD, M1_ANIMAL | M1_HUMANOID | M1_CARNIVORE, M2_HOSTILE | M2_STRONG, - M3_INFRAVISIBLE, 7, CLR_WHITE, YETI), - MON("carnivorous ape", S_YETI, LVL(6, 12, 6, 0, 0), (G_GENO | 1), + M3_INFRAVISIBLE, + 7, CLR_WHITE, YETI), + MON(NAM("carnivorous ape"), S_YETI, + LVL(6, 12, 6, 0, 0), (G_GENO | 1), A(ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_HUGS, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1250, 550, MS_GROWL, MZ_LARGE), 0, 0, M1_ANIMAL | M1_HUMANOID | M1_CARNIVORE, M2_HOSTILE | M2_STRONG, - M3_INFRAVISIBLE, 8, CLR_BLACK, CARNIVOROUS_APE), - MON("sasquatch", S_YETI, LVL(7, 15, 6, 0, 2), (G_GENO | 1), + M3_INFRAVISIBLE, + 8, CLR_BLACK, CARNIVOROUS_APE), + MON(NAM("sasquatch"), S_YETI, + LVL(7, 15, 6, 0, 2), (G_GENO | 1), A(ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_KICK, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1550, 750, MS_GROWL, MZ_LARGE), 0, 0, M1_ANIMAL | M1_HUMANOID | M1_SEE_INVIS | M1_OMNIVORE, M2_STRONG, - M3_INFRAVISIBLE, 9, CLR_RED, SASQUATCH), + M3_INFRAVISIBLE, + 9, CLR_RED, SASQUATCH), /* * Zombies */ - MON("kobold zombie", S_ZOMBIE, LVL(0, 6, 10, 0, -2), - (G_GENO | G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_PHYS, 1, 4), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + MON(NAM("kobold zombie"), S_ZOMBIE, + LVL(0, 6, 10, 0, -2), (G_GENO | G_NOCORPSE | 1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(400, 50, MS_GROAN, MZ_SMALL), MR_COLD | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID | M1_POIS, - M2_UNDEAD | M2_STALK | M2_HOSTILE, M3_INFRAVISION, 1, CLR_BROWN, - KOBOLD_ZOMBIE), - MON("gnome zombie", S_ZOMBIE, LVL(1, 6, 10, 0, -2), - (G_GENO | G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_PHYS, 1, 5), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + M2_UNDEAD | M2_STALK | M2_HOSTILE, M3_INFRAVISION, + 1, CLR_BROWN, KOBOLD_ZOMBIE), + MON(NAM("gnome zombie"), S_ZOMBIE, + LVL(1, 6, 10, 0, -2), (G_GENO | G_NOCORPSE | 1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 5), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(650, 50, MS_GROAN, MZ_SMALL), MR_COLD | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID | M1_POIS, M2_UNDEAD | M2_STALK | M2_HOSTILE | M2_GNOME, M3_INFRAVISION, 2, CLR_ORANGE, GNOME_ZOMBIE), - MON("orc zombie", S_ZOMBIE, LVL(2, 6, 9, 0, -3), - (G_GENO | G_SGROUP | G_NOCORPSE | 1), - A(ATTK(AT_CLAW, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("orc zombie"), S_ZOMBIE, + LVL(2, 6, 9, 0, -3), (G_GENO | G_SGROUP | G_NOCORPSE | 1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(850, 75, MS_GROAN, MZ_HUMAN), MR_COLD | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID | M1_POIS, - M2_UNDEAD | M2_STALK | M2_HOSTILE | M2_ORC, M3_INFRAVISION, 3, - CLR_GRAY, ORC_ZOMBIE), - MON("dwarf zombie", S_ZOMBIE, LVL(2, 6, 9, 0, -3), - (G_GENO | G_SGROUP | G_NOCORPSE | 1), - A(ATTK(AT_CLAW, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_UNDEAD | M2_STALK | M2_HOSTILE | M2_ORC, M3_INFRAVISION, + 3, CLR_GRAY, ORC_ZOMBIE), + MON(NAM("dwarf zombie"), S_ZOMBIE, + LVL(2, 6, 9, 0, -3), (G_GENO | G_SGROUP | G_NOCORPSE | 1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(900, 150, MS_GROAN, MZ_HUMAN), MR_COLD | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID | M1_POIS, M2_UNDEAD | M2_STALK | M2_HOSTILE | M2_DWARF, M3_INFRAVISION, 3, CLR_RED, DWARF_ZOMBIE), - MON("elf zombie", S_ZOMBIE, LVL(3, 6, 9, 0, -3), - (G_GENO | G_SGROUP | G_NOCORPSE | 1), - A(ATTK(AT_CLAW, AD_PHYS, 1, 7), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("elf zombie"), S_ZOMBIE, + LVL(3, 6, 9, 0, -3), (G_GENO | G_SGROUP | G_NOCORPSE | 1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 7), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_ELF, 175, MS_GROAN, MZ_HUMAN), MR_COLD | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID, M2_UNDEAD | M2_STALK | M2_HOSTILE | M2_ELF, M3_INFRAVISION, 4, CLR_GREEN, ELF_ZOMBIE), - MON("human zombie", S_ZOMBIE, LVL(4, 6, 8, 0, -3), - (G_GENO | G_SGROUP | G_NOCORPSE | 1), - A(ATTK(AT_CLAW, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("human zombie"), S_ZOMBIE, + LVL(4, 6, 8, 0, -3), (G_GENO | G_SGROUP | G_NOCORPSE | 1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 200, MS_GROAN, MZ_HUMAN), MR_COLD | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID, - M2_UNDEAD | M2_STALK | M2_HOSTILE, M3_INFRAVISION, 5, HI_DOMESTIC, - HUMAN_ZOMBIE), - MON("ghoul", S_ZOMBIE, LVL(3, 6, 10, 0, -2), (G_GENO | G_NOCORPSE | 1), - A(ATTK(AT_CLAW, AD_PLYS, 1, 2), ATTK(AT_CLAW, AD_PHYS, 1, 3), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(400, 50, MS_GROAN, MZ_SMALL), MR_COLD | MR_SLEEP | MR_POISON, 0, + M2_UNDEAD | M2_STALK | M2_HOSTILE, M3_INFRAVISION, + 5, HI_DOMESTIC, HUMAN_ZOMBIE), + MON(NAM("ghoul"), S_ZOMBIE, + LVL(3, 6, 10, 0, -2), (G_GENO | G_NOCORPSE | 1), + A(ATTK(AT_CLAW, AD_PLYS, 1, 2), ATTK(AT_CLAW, AD_PHYS, 1, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(400, 50, MS_SILENT, MZ_SMALL), MR_COLD | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID | M1_POIS | M1_OMNIVORE, - M2_UNDEAD | M2_WANDER | M2_HOSTILE, M3_INFRAVISION, 5, CLR_BLACK, - GHOUL), - MON("ettin zombie", S_ZOMBIE, LVL(6, 8, 6, 0, -4), - (G_GENO | G_NOCORPSE | 1), + M2_UNDEAD | M2_WANDER | M2_HOSTILE, M3_INFRAVISION, + 5, CLR_BLACK, GHOUL), + MON(NAM("ettin zombie"), S_ZOMBIE, + LVL(6, 8, 6, 0, -4), (G_GENO | G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_PHYS, 1, 10), ATTK(AT_CLAW, AD_PHYS, 1, 10), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1700, 250, MS_GROAN, MZ_HUGE), MR_COLD | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID, M2_UNDEAD | M2_STALK | M2_HOSTILE | M2_STRONG, M3_INFRAVISION, 7, CLR_BLUE, ETTIN_ZOMBIE), - MON("giant zombie", S_ZOMBIE, LVL(8, 8, 6, 0, -4), - (G_GENO | G_NOCORPSE | 1), - A(ATTK(AT_CLAW, AD_PHYS, 2, 8), ATTK(AT_CLAW, AD_PHYS, 2, 8), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + MON(NAM("giant zombie"), S_ZOMBIE, + LVL(8, 8, 6, 0, -4), (G_GENO | G_NOCORPSE | 1), + A(ATTK(AT_CLAW, AD_PHYS, 2, 8), ATTK(AT_CLAW, AD_PHYS, 2, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2050, 375, MS_GROAN, MZ_HUGE), MR_COLD | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID, M2_UNDEAD | M2_STALK | M2_HOSTILE | M2_GIANT | M2_STRONG, - M3_INFRAVISION, 9, CLR_CYAN, GIANT_ZOMBIE), - MON("skeleton", S_ZOMBIE, LVL(12, 8, 4, 0, 0), (G_NOCORPSE | G_NOGEN), - A(ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_TUCH, AD_SLOW, 1, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISION, + 9, CLR_CYAN, GIANT_ZOMBIE), + MON(NAM("skeleton"), S_ZOMBIE, + LVL(12, 8, 4, 0, 0), (G_NOCORPSE | G_NOGEN), + A(ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_TUCH, AD_SLOW, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(300, 5, MS_BONES, MZ_HUMAN), MR_COLD | MR_SLEEP | MR_POISON | MR_STONE, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID | M1_THICK_HIDE, M2_UNDEAD | M2_WANDER | M2_HOSTILE | M2_STRONG | M2_COLLECT | M2_NASTY, - M3_INFRAVISION, 14, CLR_WHITE, SKELETON), + M3_INFRAVISION, + 14, CLR_WHITE, SKELETON), /* * golems */ - MON("straw golem", S_GOLEM, LVL(3, 12, 10, 0, 0), (G_NOCORPSE | 1), - A(ATTK(AT_CLAW, AD_PHYS, 1, 2), ATTK(AT_CLAW, AD_PHYS, 1, 2), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + MON(NAM("straw golem"), S_GOLEM, + LVL(3, 12, 10, 0, 0), (G_NOCORPSE | 1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 2), ATTK(AT_CLAW, AD_PHYS, 1, 2), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(400, 0, MS_SILENT, MZ_LARGE), MR_COLD | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID, M2_HOSTILE | M2_NEUTER, 0, 4, CLR_BRIGHT_BLUE, STRAW_GOLEM), - MON("paper golem", S_GOLEM, LVL(3, 12, 10, 0, 0), (G_NOCORPSE | 1), - A(ATTK(AT_CLAW, AD_PHYS, 1, 3), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("paper golem"), S_GOLEM, + LVL(3, 12, 10, 0, 0), (G_NOCORPSE | 1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(400, 0, MS_SILENT, MZ_LARGE), MR_COLD | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID, M2_HOSTILE | M2_NEUTER, 0, 4, HI_PAPER, PAPER_GOLEM), - MON("rope golem", S_GOLEM, LVL(4, 9, 8, 0, 0), (G_NOCORPSE | 1), + MON(NAM("rope golem"), S_GOLEM, + LVL(4, 9, 8, 0, 0), (G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_HUGS, AD_PHYS, 6, 1), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(450, 0, MS_SILENT, MZ_LARGE), MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID, M2_HOSTILE | M2_NEUTER, 0, 6, CLR_BLACK, ROPE_GOLEM), - MON("gold golem", S_GOLEM, LVL(5, 9, 6, 0, 0), (G_NOCORPSE | 1), + MON(NAM("gold golem"), S_GOLEM, + LVL(5, 9, 6, 0, 0), (G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_PHYS, 2, 3), ATTK(AT_CLAW, AD_PHYS, 2, 3), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(450, 0, MS_SILENT, MZ_LARGE), MR_SLEEP | MR_POISON | MR_ACID, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID | M1_THICK_HIDE, - M2_HOSTILE | M2_NEUTER, 0, 6, HI_GOLD, GOLD_GOLEM), - MON("leather golem", S_GOLEM, LVL(6, 6, 6, 0, 0), (G_NOCORPSE | 1), + M2_HOSTILE | M2_NEUTER, 0, + 6, HI_GOLD, GOLD_GOLEM), + MON(NAM("leather golem"), S_GOLEM, + LVL(6, 6, 6, 0, 0), (G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_CLAW, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(800, 0, MS_SILENT, MZ_LARGE), MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID, M2_HOSTILE | M2_NEUTER, 0, 7, HI_LEATHER, LEATHER_GOLEM), - MON("wood golem", S_GOLEM, LVL(7, 3, 4, 0, 0), (G_NOCORPSE | 1), + MON(NAM("wood golem"), S_GOLEM, + LVL(7, 3, 4, 0, 0), (G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_PHYS, 3, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(900, 0, MS_SILENT, MZ_LARGE), MR_COLD | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID | M1_THICK_HIDE, - M2_HOSTILE | M2_NEUTER, 0, 8, HI_WOOD, WOOD_GOLEM), - MON("flesh golem", S_GOLEM, LVL(9, 8, 9, 30, 0), (1), - A(ATTK(AT_CLAW, AD_PHYS, 2, 8), ATTK(AT_CLAW, AD_PHYS, 2, 8), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M2_HOSTILE | M2_NEUTER, 0, + 8, HI_WOOD, WOOD_GOLEM), + MON(NAM("flesh golem"), S_GOLEM, + LVL(9, 8, 9, 30, 0), (1), + A(ATTK(AT_CLAW, AD_PHYS, 2, 8), ATTK(AT_CLAW, AD_PHYS, 2, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1400, 600, MS_SILENT, MZ_LARGE), MR_FIRE | MR_COLD | MR_ELEC | MR_SLEEP | MR_POISON, MR_FIRE | MR_COLD | MR_ELEC | MR_SLEEP | MR_POISON, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID, M2_HOSTILE | M2_STRONG, 0, 10, CLR_RED, FLESH_GOLEM), - MON("clay golem", S_GOLEM, LVL(11, 7, 7, 40, 0), (G_NOCORPSE | 1), - A(ATTK(AT_CLAW, AD_PHYS, 3, 10), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("clay golem"), S_GOLEM, + LVL(11, 7, 7, 40, 0), (G_NOCORPSE | 1), + A(ATTK(AT_CLAW, AD_PHYS, 3, 10), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1550, 0, MS_SILENT, MZ_LARGE), MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID | M1_THICK_HIDE, - M2_HOSTILE | M2_STRONG, 0, 12, CLR_ORANGE, CLAY_GOLEM), - MON("stone golem", S_GOLEM, LVL(14, 6, 5, 50, 0), (G_NOCORPSE | 1), - A(ATTK(AT_CLAW, AD_PHYS, 3, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_HOSTILE | M2_STRONG, 0, + 12, CLR_ORANGE, CLAY_GOLEM), + MON(NAM("stone golem"), S_GOLEM, + LVL(14, 6, 5, 50, 0), (G_NOCORPSE | 1), + A(ATTK(AT_CLAW, AD_PHYS, 3, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1900, 0, MS_SILENT, MZ_LARGE), MR_SLEEP | MR_POISON | MR_STONE, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID | M1_THICK_HIDE, - M2_HOSTILE | M2_STRONG, 0, 15, CLR_GRAY, STONE_GOLEM), - MON("glass golem", S_GOLEM, LVL(16, 6, 1, 50, 0), (G_NOCORPSE | 1), - A(ATTK(AT_CLAW, AD_PHYS, 2, 8), ATTK(AT_CLAW, AD_PHYS, 2, 8), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M2_HOSTILE | M2_STRONG, 0, + 15, CLR_GRAY, STONE_GOLEM), + MON(NAM("glass golem"), S_GOLEM, + LVL(16, 6, 1, 50, 0), (G_NOCORPSE | 1), + A(ATTK(AT_CLAW, AD_PHYS, 2, 8), ATTK(AT_CLAW, AD_PHYS, 2, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1800, 0, MS_SILENT, MZ_LARGE), MR_SLEEP | MR_POISON | MR_ACID, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID | M1_THICK_HIDE, - M2_HOSTILE | M2_STRONG, 0, 18, CLR_BRIGHT_CYAN, GLASS_GOLEM), - MON("iron golem", S_GOLEM, LVL(18, 6, 3, 60, 0), (G_NOCORPSE | 1), + M2_HOSTILE | M2_STRONG, 0, + 18, CLR_BRIGHT_CYAN, GLASS_GOLEM), + MON(NAM("iron golem"), S_GOLEM, + LVL(18, 6, 3, 60, 0), (G_NOCORPSE | 1), A(ATTK(AT_WEAP, AD_PHYS, 4, 10), ATTK(AT_BREA, AD_DRST, 4, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2000, 0, MS_SILENT, MZ_LARGE), MR_FIRE | MR_COLD | MR_ELEC | MR_SLEEP | MR_POISON, 0, M1_BREATHLESS | M1_MINDLESS | M1_HUMANOID | M1_THICK_HIDE | M1_POIS, - M2_HOSTILE | M2_STRONG | M2_COLLECT, 0, 22, HI_METAL, IRON_GOLEM), + M2_HOSTILE | M2_STRONG | M2_COLLECT, 0, + 22, HI_METAL, IRON_GOLEM), /* * humans, including were-critters; * the '@' class does not obey rule #2. + * Plain "human" is a placeholder, not a normal monster. */ - MON("human", S_HUMAN, LVL(0, 12, 10, 0, 0), G_NOGEN, /* for corpses */ + MON(NAM("human"), S_HUMAN, /* for corpses */ + LVL(0, 12, 10, 0, 0), G_NOGEN, A(ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_HUMANOID, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, - M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_COLLECT, M3_INFRAVISIBLE, + M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_COLLECT, + M3_INFRAVISIBLE, 2, HI_DOMESTIC, HUMAN), - MON("wererat", S_HUMAN, LVL(2, 12, 10, 10, -7), (1), + MON(NAM("wererat"), S_HUMAN, + LVL(2, 12, 10, 10, -7), (1), A(ATTK(AT_WEAP, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_WERE, MZ_HUMAN), MR_POISON, 0, M1_HUMANOID | M1_POIS | M1_REGEN | M1_OMNIVORE, M2_NOPOLY | M2_WERE | M2_HOSTILE | M2_HUMAN | M2_COLLECT, - M3_INFRAVISIBLE, 3, CLR_BROWN, HUMAN_WERERAT), - MON("werejackal", S_HUMAN, LVL(2, 12, 10, 10, -7), (1), + M3_INFRAVISIBLE, + 3, CLR_BROWN, HUMAN_WERERAT), + MON(NAM("werejackal"), S_HUMAN, + LVL(2, 12, 10, 10, -7), (1), A(ATTK(AT_WEAP, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_WERE, MZ_HUMAN), MR_POISON, 0, M1_HUMANOID | M1_POIS | M1_REGEN | M1_OMNIVORE, M2_NOPOLY | M2_WERE | M2_HOSTILE | M2_HUMAN | M2_COLLECT, - M3_INFRAVISIBLE, 3, CLR_BROWN, HUMAN_WEREJACKAL), - MON("werewolf", S_HUMAN, LVL(5, 12, 10, 20, -7), (1), + M3_INFRAVISIBLE, + 3, CLR_BROWN, HUMAN_WEREJACKAL), + MON(NAM("werewolf"), S_HUMAN, + LVL(5, 12, 10, 20, -7), (1), A(ATTK(AT_WEAP, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_WERE, MZ_HUMAN), MR_POISON | MR_SLEEP, 0, M1_HUMANOID | M1_POIS | M1_REGEN | M1_OMNIVORE, M2_NOPOLY | M2_WERE | M2_HOSTILE | M2_HUMAN | M2_COLLECT, - M3_INFRAVISIBLE, 6, CLR_ORANGE, HUMAN_WEREWOLF), - MON("doppelganger", S_HUMAN, LVL(9, 12, 5, 20, 0), (G_GENO | 1), + M3_INFRAVISIBLE, + 6, CLR_ORANGE, HUMAN_WEREWOLF), + MON(NAM("doppelganger"), S_HUMAN, + LVL(9, 12, 5, 20, 0), (G_GENO | 1), A(ATTK(AT_WEAP, AD_PHYS, 1, 12), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_IMITATE, MZ_HUMAN), MR_SLEEP, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_HOSTILE | M2_STRONG | M2_COLLECT | M2_SHAPESHIFTER, - M3_INFRAVISIBLE, 11, HI_DOMESTIC, DOPPELGANGER), - MON("shopkeeper", S_HUMAN, LVL(13, 18, 0, 50, 0), G_NOGEN, + M3_INFRAVISIBLE, + 11, HI_DOMESTIC, DOPPELGANGER), + /* 3.7: shopkeepers used to have speed 18, but if/when they were + hasted they always got 2 moves per turn and had a tendency to move + away from blocking the door and then move right back; since they + might start with a potion of speed and drink that as soon as the + hero gets close, once inside the shop the hero could have trouble + getting out again; also, being slowed still guaranteed one move + per turn; reduce their innate speed from 18 to 16 for a hasted + speed of 22 rather than 24 and slowed speed of 11 rather than 12; + they will still block the shop door, but not as tenaciously */ + MON(NAM("shopkeeper"), S_HUMAN, + LVL(13, 16, 0, 50, 0), G_NOGEN, A(ATTK(AT_WEAP, AD_PHYS, 4, 4), ATTK(AT_WEAP, AD_PHYS, 4, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_SELL, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_COLLECT | M2_MAGIC, - M3_INFRAVISIBLE, 15, HI_DOMESTIC, SHOPKEEPER), - MON("guard", S_HUMAN, LVL(12, 12, 10, 40, 10), G_NOGEN, + M3_INFRAVISIBLE, + 15, HI_DOMESTIC, SHOPKEEPER), + /* vault guard */ + MON(NAM("guard"), S_HUMAN, + LVL(12, 12, 10, 40, 10), G_NOGEN, A(ATTK(AT_WEAP, AD_PHYS, 4, 10), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_GUARD, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_MERC | M2_PEACEFUL | M2_STRONG | M2_COLLECT, - M3_INFRAVISIBLE, 14, CLR_BLUE, GUARD), + M3_INFRAVISIBLE, + 14, CLR_BLUE, GUARD), /* prisoner is used on some special levels */ - MON("prisoner", S_HUMAN, LVL(12, 12, 10, 0, 0), G_NOGEN, + MON(NAM("prisoner"), S_HUMAN, + LVL(12, 12, 10, 0, 0), G_NOGEN, A(ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_DJINNI, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_COLLECT, - M3_INFRAVISIBLE | M3_CLOSE, 14, HI_DOMESTIC, PRISONER), - MON("Oracle", S_HUMAN, LVL(12, 0, 0, 50, 0), (G_NOGEN | G_UNIQ), + M3_INFRAVISIBLE | M3_CLOSE, + 14, HI_DOMESTIC, PRISONER), + /* the Oracle of Delphi doesn't move */ + MON(NAM("Oracle"), S_HUMAN, + LVL(12, 0, 0, 50, 0), (G_NOGEN | G_UNIQ), A(ATTK(AT_NONE, AD_MAGM, 0, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_ORACLE, MZ_HUMAN), 0, 0, @@ -2372,90 +2805,109 @@ 13, HI_ZAP, ORACLE), /* aligned priests always have the epri extension attached; individual instantiations should always have either ispriest - or isminion set */ - MON3("priest", "priestess", "aligned cleric", - S_HUMAN, LVL(12, 12, 10, 50, 0), G_NOGEN, + or isminion set; role monster priests are separate (below) */ + MON(NAMS("priest", "priestess", "aligned cleric"), S_HUMAN, + LVL(12, 12, 10, 50, 0), G_NOGEN, A(ATTK(AT_WEAP, AD_PHYS, 4, 10), ATTK(AT_KICK, AD_PHYS, 1, 4), ATTK(AT_MAGC, AD_CLRC, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_PRIEST, MZ_HUMAN), MR_ELEC, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_LORD | M2_PEACEFUL | M2_COLLECT, - M3_INFRAVISIBLE, 15, CLR_YELLOW, ALIGNED_CLERIC), - /* high priests always have epri and always have ispriest set */ - MON3("high priest", "high priestess", "high cleric", - S_HUMAN, LVL(25, 15, 7, 70, 0), (G_NOGEN | G_UNIQ), + M3_INFRAVISIBLE, + 15, CLR_YELLOW, ALIGNED_CLERIC), + /* high priests always have epri and always have ispriest set; + they are flagged as unique even through they aren't */ + MON(NAMS("high priest", "high priestess", "high cleric"), S_HUMAN, + LVL(25, 15, 7, 70, 0), (G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 4, 10), ATTK(AT_KICK, AD_PHYS, 2, 8), - ATTK(AT_MAGC, AD_CLRC, 2, 8), ATTK(AT_MAGC, AD_CLRC, 2, 8), NO_ATTK, - NO_ATTK), + ATTK(AT_MAGC, AD_CLRC, 2, 8), ATTK(AT_MAGC, AD_CLRC, 2, 8), + NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_PRIEST, MZ_HUMAN), MR_FIRE | MR_ELEC | MR_SLEEP | MR_POISON, 0, M1_HUMANOID | M1_SEE_INVIS | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_MINION | M2_PRINCE | M2_NASTY | M2_COLLECT | M2_MAGIC, - M3_INFRAVISIBLE, 30, CLR_YELLOW, HIGH_CLERIC), - MON("soldier", S_HUMAN, LVL(6, 10, 10, 0, -2), (G_SGROUP | G_GENO | 1), - A(ATTK(AT_WEAP, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE, + 30, CLR_YELLOW, HIGH_CLERIC), + MON(NAM("soldier"), S_HUMAN, + LVL(6, 10, 10, 0, -2), (G_SGROUP | G_GENO | 1), + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_SOLDIER, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_MERC | M2_STALK | M2_HOSTILE | M2_STRONG | M2_COLLECT, - M3_INFRAVISIBLE, 8, CLR_GRAY, SOLDIER), - MON("sergeant", S_HUMAN, LVL(8, 10, 10, 5, -3), (G_SGROUP | G_GENO | 1), - A(ATTK(AT_WEAP, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE, + 8, CLR_GRAY, SOLDIER), + MON(NAM("sergeant"), S_HUMAN, + LVL(8, 10, 10, 5, -3), (G_SGROUP | G_GENO | 1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_SOLDIER, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_MERC | M2_STALK | M2_HOSTILE | M2_STRONG | M2_COLLECT, - M3_INFRAVISIBLE, 10, CLR_RED, SERGEANT), - MON("nurse", S_HUMAN, LVL(11, 6, 0, 0, 0), (G_GENO | 3), - A(ATTK(AT_CLAW, AD_HEAL, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE, + 10, CLR_RED, SERGEANT), + MON(NAM("nurse"), S_HUMAN, + LVL(11, 6, 0, 0, 0), (G_GENO | 3), + A(ATTK(AT_CLAW, AD_HEAL, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_NURSE, MZ_HUMAN), MR_POISON, MR_POISON, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_HOSTILE, - M3_INFRAVISIBLE, 13, HI_DOMESTIC, NURSE), - MON("lieutenant", S_HUMAN, LVL(10, 10, 10, 15, -4), (G_GENO | 1), - A(ATTK(AT_WEAP, AD_PHYS, 3, 4), ATTK(AT_WEAP, AD_PHYS, 3, 4), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISIBLE, + 13, HI_DOMESTIC, NURSE), + MON(NAM("lieutenant"), S_HUMAN, + LVL(10, 10, 10, 15, -4), (G_GENO | 1), + A(ATTK(AT_WEAP, AD_PHYS, 3, 4), ATTK(AT_WEAP, AD_PHYS, 3, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_SOLDIER, MZ_HUMAN), 0, 0, - M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_MERC | M2_STALK - | M2_LORD | M2_HOSTILE | M2_STRONG | M2_COLLECT, - M3_INFRAVISIBLE, 12, CLR_GREEN, LIEUTENANT), - MON("captain", S_HUMAN, LVL(12, 10, 10, 15, -5), (G_GENO | 1), - A(ATTK(AT_WEAP, AD_PHYS, 4, 4), ATTK(AT_WEAP, AD_PHYS, 4, 4), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M1_HUMANOID | M1_OMNIVORE, + M2_NOPOLY | M2_HUMAN | M2_MERC | M2_STALK | M2_LORD | M2_HOSTILE + | M2_STRONG | M2_COLLECT, + M3_INFRAVISIBLE, + 12, CLR_GREEN, LIEUTENANT), + MON(NAM("captain"), S_HUMAN, + LVL(12, 10, 10, 15, -5), (G_GENO | 1), + A(ATTK(AT_WEAP, AD_PHYS, 4, 4), ATTK(AT_WEAP, AD_PHYS, 4, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_SOLDIER, MZ_HUMAN), 0, 0, - M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_MERC | M2_STALK + M1_HUMANOID | M1_OMNIVORE, + M2_NOPOLY | M2_HUMAN | M2_MERC | M2_STALK | M2_PRINCE | M2_HOSTILE | M2_STRONG | M2_COLLECT, - M3_INFRAVISIBLE, 14, CLR_BLUE, CAPTAIN), - MON("watchman", S_HUMAN, LVL(6, 10, 10, 0, -2), - (G_SGROUP | G_NOGEN | G_GENO | 1), - A(ATTK(AT_WEAP, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE, + 14, CLR_BLUE, CAPTAIN), + MON(NAM("watchman"), S_HUMAN, + LVL(6, 10, 10, 0, -2), (G_SGROUP | G_NOGEN | G_GENO | 1), + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_SOLDIER, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_MERC | M2_STALK | M2_PEACEFUL | M2_STRONG | M2_COLLECT, - M3_INFRAVISIBLE, 8, CLR_GRAY, WATCHMAN), - MON("watch captain", S_HUMAN, LVL(10, 10, 10, 15, -4), - (G_NOGEN | G_GENO | 1), - A(ATTK(AT_WEAP, AD_PHYS, 3, 4), ATTK(AT_WEAP, AD_PHYS, 3, 4), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISIBLE, + 8, CLR_GRAY, WATCHMAN), + MON(NAM("watch captain"), S_HUMAN, + LVL(10, 10, 10, 15, -4), (G_NOGEN | G_GENO | 1), + A(ATTK(AT_WEAP, AD_PHYS, 3, 4), ATTK(AT_WEAP, AD_PHYS, 3, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_SOLDIER, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_MERC | M2_STALK | M2_PEACEFUL | M2_STRONG | M2_COLLECT, - M3_INFRAVISIBLE, 12, CLR_GREEN, WATCH_CAPTAIN), + M3_INFRAVISIBLE, + 12, CLR_GREEN, WATCH_CAPTAIN), /* Unique humans not tied to quests. */ - MON("Medusa", S_HUMAN, LVL(20, 12, 2, 50, -15), (G_NOGEN | G_UNIQ), + MON(NAM("Medusa"), S_HUMAN, + LVL(20, 12, 2, 50, -15), (G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_PHYS, 1, 8), - ATTK(AT_GAZE, AD_STON, 0, 0), ATTK(AT_BITE, AD_DRST, 1, 6), NO_ATTK, - NO_ATTK), + ATTK(AT_GAZE, AD_STON, 0, 0), ATTK(AT_BITE, AD_DRST, 1, 6), + NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_HISS, MZ_HUMAN), MR_POISON | MR_STONE, MR_POISON | MR_STONE, M1_FLY | M1_SWIM | M1_AMPHIBIOUS | M1_HUMANOID | M1_POIS | M1_OMNIVORE, M2_NOPOLY | M2_HOSTILE | M2_STRONG | M2_PNAME | M2_FEMALE, - M3_WAITFORU | M3_INFRAVISIBLE, 25, CLR_BRIGHT_GREEN, MEDUSA), - MON("Wizard of Yendor", S_HUMAN, LVL(30, 12, -8, 100, A_NONE), - (G_NOGEN | G_UNIQ), + M3_WAITFORU | M3_INFRAVISIBLE, + 25, CLR_BRIGHT_GREEN, MEDUSA), + MON(NAM("Wizard of Yendor"), S_HUMAN, + LVL(30, 12, -8, 100, A_NONE), (G_NOGEN | G_UNIQ), A(ATTK(AT_CLAW, AD_SAMU, 2, 12), ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_MAGC, AD_SPEL, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_CUSS, MZ_HUMAN), MR_FIRE | MR_POISON, @@ -2464,161 +2916,186 @@ | M1_TPORT | M1_TPORT_CNTRL | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_HOSTILE | M2_STRONG | M2_NASTY | M2_PRINCE | M2_MALE | M2_MAGIC, - M3_COVETOUS | M3_WAITFORU | M3_INFRAVISIBLE, 34, HI_BOSS, - WIZARD_OF_YENDOR), - MON("Croesus", S_HUMAN, LVL(20, 15, 0, 40, 15), (G_UNIQ | G_NOGEN), - A(ATTK(AT_WEAP, AD_PHYS, 4, 10), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_COVETOUS | M3_WAITFORU | M3_INFRAVISIBLE, + 34, HI_OVERLORD, WIZARD_OF_YENDOR), + MON(NAM("Croesus"), S_HUMAN, + LVL(20, 15, 0, 40, 15), (G_UNIQ | G_NOGEN), + A(ATTK(AT_WEAP, AD_PHYS, 4, 10), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_GUARD, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_SEE_INVIS | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_STALK | M2_HOSTILE | M2_STRONG | M2_NASTY | M2_PNAME | M2_PRINCE | M2_MALE | M2_GREEDY | M2_JEWELS | M2_COLLECT | M2_MAGIC, - M3_INFRAVISIBLE | M3_DISPLACES, 22, HI_LORD, CROESUS), + M3_INFRAVISIBLE | M3_DISPLACES, + 22, HI_LORD, CROESUS), #ifdef CHARON - MON("Charon", S_HUMAN, LVL(76, 18, -5, 120, 0), - (G_HELL | G_NOCORPSE | G_NOGEN | G_UNIQ), - A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_TUCH, AD_PLYS, 1, 8), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + MON(NAM("Charon"), S_HUMAN, + LVL(76, 18, -5, 120, 0), (G_HELL | G_NOCORPSE | G_NOGEN | G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_TUCH, AD_PLYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_FERRY, MZ_HUMAN), MR_FIRE | MR_COLD | MR_POISON | MR_STONE, 0, M1_BREATHLESS | M1_SEE_INVIS | M1_HUMANOID, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_PNAME | M2_MALE | M2_GREEDY | M2_COLLECT, - M3_INFRAVISIBLE, 38, CLR_WHITE, CHARON), + M3_INFRAVISIBLE, + 38, CLR_WHITE, CHARON), #endif /* * (major) demons */ - MON("water demon", S_DEMON, LVL(8, 12, -4, 30, -7), - (G_NOCORPSE | G_NOGEN), + MON(NAM("water demon"), S_DEMON, + LVL(8, 12, -4, 30, -7), (G_NOCORPSE | G_NOGEN), A(ATTK(AT_WEAP, AD_PHYS, 1, 3), ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_BITE, AD_PHYS, 1, 3), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_DJINNI, MZ_HUMAN), MR_FIRE | MR_POISON, 0, M1_HUMANOID | M1_POIS | M1_SWIM, M2_NOPOLY | M2_DEMON | M2_STALK | M2_HOSTILE | M2_NASTY | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 11, CLR_BLUE, WATER_DEMON), + M3_INFRAVISIBLE | M3_INFRAVISION, + 11, CLR_BLUE, WATER_DEMON), /* standard demons & devils */ -#define SEDUCTION_ATTACKS_YES \ +#define SEDUCTION_ATTACKS_YES \ A(ATTK(AT_BITE, AD_SSEX, 0, 0), ATTK(AT_CLAW, AD_PHYS, 1, 3), \ ATTK(AT_CLAW, AD_PHYS, 1, 3), NO_ATTK, NO_ATTK, NO_ATTK) -#define SEDUCTION_ATTACKS_NO \ +#define SEDUCTION_ATTACKS_NO \ A(ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_CLAW, AD_PHYS, 1, 3), \ ATTK(AT_BITE, AD_DRLI, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK) - /* incubus and succubus */ - MON3("incubus", "succubus", "amorous demon", - S_DEMON, LVL(6, 12, 0, 70, -9), (G_NOCORPSE | 1), - SEDUCTION_ATTACKS_YES, SIZ(WT_HUMAN, 400, MS_SEDUCE, MZ_HUMAN), + /* incubus and succubus; prior to 3.7, succubus and incubus were + distinct monsters; "amorous demon" is considered to be a temporary + placeholder but may be here to stay... */ + MON(NAMS("incubus", "succubus", "amorous demon"), S_DEMON, + LVL(6, 12, 0, 70, -9), (G_NOCORPSE | 1), + SEDUCTION_ATTACKS_YES, + SIZ(WT_HUMAN, 400, MS_SEDUCE, MZ_HUMAN), MR_FIRE | MR_POISON, 0, M1_HUMANOID | M1_FLY | M1_POIS, M2_DEMON | M2_STALK | M2_HOSTILE | M2_NASTY, - M3_INFRAVISIBLE | M3_INFRAVISION, 8, CLR_GRAY, AMOROUS_DEMON), - MON("horned devil", S_DEMON, LVL(6, 9, -5, 50, 11), - (G_HELL | G_NOCORPSE | 2), + M3_INFRAVISIBLE | M3_INFRAVISION, + 8, CLR_GRAY, AMOROUS_DEMON), + MON(NAM("horned devil"), S_DEMON, + LVL(6, 9, -5, 50, 11), (G_HELL | G_NOCORPSE | 2), A(ATTK(AT_WEAP, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), - ATTK(AT_BITE, AD_PHYS, 2, 3), ATTK(AT_STNG, AD_PHYS, 1, 3), NO_ATTK, - NO_ATTK), + ATTK(AT_BITE, AD_PHYS, 2, 3), ATTK(AT_STNG, AD_PHYS, 1, 3), + NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_SILENT, MZ_HUMAN), MR_FIRE | MR_POISON, 0, M1_FLY | M1_POIS | M1_THICK_HIDE, M2_DEMON | M2_STALK | M2_HOSTILE | M2_NASTY, - M3_INFRAVISIBLE | M3_INFRAVISION, 9, CLR_BROWN, HORNED_DEVIL), + M3_INFRAVISIBLE | M3_INFRAVISION, + 9, CLR_BROWN, HORNED_DEVIL), /* Used by AD&D for a type of demon, originally one of the Furies - and spelled this way */ - MON("erinys", S_DEMON, LVL(7, 12, 2, 30, 10), - (G_HELL | G_NOCORPSE | G_NOGEN), - A(ATTK(AT_WEAP, AD_DRST, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + and spelled this way; plural is erinyes */ + MON(NAM("erinys"), S_DEMON, + LVL(7, 12, 2, 30, 10), (G_HELL | G_NOCORPSE | G_NOGEN), + /* erinys attacks (among other things) are variable depending on your + alignment abuse, can be increased from here by adj_erinys(mon.c) */ + A(ATTK(AT_WEAP, AD_DRST, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_SILENT, MZ_HUMAN), MR_FIRE | MR_POISON, 0, M1_FLY | M1_HUMANOID | M1_POIS, M2_NOPOLY | M2_DEMON | M2_STALK | M2_HOSTILE | M2_STRONG | M2_NASTY | M2_FEMALE | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 10, CLR_GREEN, ERINYS), - MON("barbed devil", S_DEMON, LVL(8, 12, 0, 35, 8), - (G_HELL | G_NOCORPSE | G_SGROUP | 2), - A(ATTK(AT_CLAW, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_PHYS, 2, 4), + M3_INFRAVISIBLE | M3_INFRAVISION, + 10, CLR_GREEN, ERINYS), + MON(NAM("barbed devil"), S_DEMON, + LVL(8, 12, 0, 35, 8), (G_HELL | G_NOCORPSE | G_SGROUP | 2), + A(ATTK(AT_CLAW, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_STCK, 2, 4), ATTK(AT_STNG, AD_PHYS, 3, 4), ATTK(AT_NONE, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_SILENT, MZ_HUMAN), MR_FIRE | MR_POISON, 0, M1_POIS | M1_THICK_HIDE, M2_DEMON | M2_STALK | M2_HOSTILE | M2_NASTY, - M3_INFRAVISIBLE | M3_INFRAVISION, 10, CLR_CYAN, BARBED_DEVIL), - MON("marilith", S_DEMON, LVL(7, 12, -6, 80, -12), - (G_HELL | G_NOCORPSE | 1), + M3_INFRAVISIBLE | M3_INFRAVISION, + 10, CLR_CYAN, BARBED_DEVIL), + MON(NAM("marilith"), S_DEMON, + LVL(7, 12, -6, 80, -12), (G_HELL | G_NOCORPSE | 1), A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_PHYS, 2, 4)), SIZ(WT_HUMAN, 400, MS_CUSS, MZ_LARGE), MR_FIRE | MR_POISON, 0, M1_HUMANOID | M1_SLITHY | M1_SEE_INVIS | M1_POIS, M2_DEMON | M2_STALK | M2_HOSTILE | M2_NASTY | M2_FEMALE | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 11, CLR_BRIGHT_CYAN, MARILITH), - MON("vrock", S_DEMON, LVL(8, 12, 0, 50, -9), - (G_HELL | G_NOCORPSE | G_SGROUP | 2), + M3_INFRAVISIBLE | M3_INFRAVISION, + 11, CLR_BRIGHT_CYAN, MARILITH), + MON(NAM("vrock"), S_DEMON, + LVL(8, 12, 0, 50, -9), (G_HELL | G_NOCORPSE | G_SGROUP | 2), A(ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 8), ATTK(AT_CLAW, AD_PHYS, 1, 8), ATTK(AT_BITE, AD_PHYS, 1, 6), NO_ATTK), SIZ(WT_HUMAN, 400, MS_SILENT, MZ_LARGE), MR_FIRE | MR_POISON, 0, M1_FLY | M1_POIS, M2_DEMON | M2_STALK | M2_HOSTILE | M2_NASTY, - M3_INFRAVISIBLE | M3_INFRAVISION, 11, CLR_ORANGE, VROCK), - MON("hezrou", S_DEMON, LVL(9, 6, -2, 55, -10), - (G_HELL | G_NOCORPSE | G_SGROUP | 2), + M3_INFRAVISIBLE | M3_INFRAVISION, + 11, CLR_ORANGE, VROCK), + MON(NAM("hezrou"), S_DEMON, + LVL(9, 6, -2, 55, -10), (G_HELL | G_NOCORPSE | G_SGROUP | 2), A(ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_BITE, AD_PHYS, 4, 4), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_SILENT, MZ_LARGE), MR_FIRE | MR_POISON, 0, M1_HUMANOID | M1_POIS, M2_DEMON | M2_STALK | M2_HOSTILE | M2_NASTY, - M3_INFRAVISIBLE | M3_INFRAVISION, 12, CLR_YELLOW, HEZROU), - MON("bone devil", S_DEMON, LVL(9, 15, -1, 40, 9), - (G_HELL | G_NOCORPSE | G_SGROUP | 2), - A(ATTK(AT_WEAP, AD_PHYS, 3, 4), ATTK(AT_STNG, AD_DRST, 2, 4), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISIBLE | M3_INFRAVISION, + 12, CLR_YELLOW, HEZROU), + MON(NAM("bone devil"), S_DEMON, + LVL(9, 15, -1, 40, 9), (G_HELL | G_NOCORPSE | G_SGROUP | 2), + A(ATTK(AT_WEAP, AD_PHYS, 3, 4), ATTK(AT_STNG, AD_DRST, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_SILENT, MZ_LARGE), MR_FIRE | MR_POISON, 0, M1_FLY | M1_POIS, M2_DEMON | M2_STALK | M2_HOSTILE | M2_NASTY | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 13, CLR_GRAY, BONE_DEVIL), - MON("ice devil", S_DEMON, LVL(11, 6, -4, 55, 12), - (G_HELL | G_NOCORPSE | 2), - A(ATTK(AT_CLAW, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_PHYS, 2, 4), - ATTK(AT_BITE, AD_PHYS, 2, 4), ATTK(AT_STNG, AD_COLD, 3, 4), NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE | M3_INFRAVISION, + 13, CLR_GRAY, BONE_DEVIL), + MON(NAM("ice devil"), S_DEMON, + LVL(11, 6, -4, 55, -12), (G_HELL | G_NOCORPSE | 2), + A(ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + ATTK(AT_BITE, AD_PHYS, 2, 4), ATTK(AT_STNG, AD_COLD, 3, 4), + NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_SILENT, MZ_LARGE), MR_FIRE | MR_COLD | MR_POISON, 0, M1_SEE_INVIS | M1_POIS, M2_DEMON | M2_STALK | M2_HOSTILE | M2_NASTY, - M3_INFRAVISIBLE | M3_INFRAVISION, 14, CLR_WHITE, ICE_DEVIL), - MON("nalfeshnee", S_DEMON, LVL(11, 9, -1, 65, -11), - (G_HELL | G_NOCORPSE | 1), + M3_INFRAVISIBLE | M3_INFRAVISION, + 15, CLR_WHITE, ICE_DEVIL), + MON(NAM("nalfeshnee"), S_DEMON, + LVL(11, 9, -1, 65, -11), (G_HELL | G_NOCORPSE | 1), A(ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), - ATTK(AT_BITE, AD_PHYS, 2, 4), ATTK(AT_MAGC, AD_SPEL, 0, 0), NO_ATTK, - NO_ATTK), + ATTK(AT_BITE, AD_PHYS, 2, 4), ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_SPELL, MZ_LARGE), MR_FIRE | MR_POISON, 0, M1_FLY | M1_HUMANOID | M1_POIS, M2_DEMON | M2_STALK | M2_HOSTILE | M2_NASTY, - M3_INFRAVISIBLE | M3_INFRAVISION, 15, HI_ZAP, NALFESHNEE), - MON("pit fiend", S_DEMON, LVL(13, 6, -3, 65, -13), - (G_HELL | G_NOCORPSE | 2), + M3_INFRAVISIBLE | M3_INFRAVISION, + 15, HI_ZAP, NALFESHNEE), + MON(NAM("pit fiend"), S_DEMON, + LVL(13, 6, -3, 65, -13), (G_HELL | G_NOCORPSE | 2), A(ATTK(AT_WEAP, AD_PHYS, 4, 2), ATTK(AT_WEAP, AD_PHYS, 4, 2), ATTK(AT_HUGS, AD_PITS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_GROWL, MZ_LARGE), MR_FIRE | MR_POISON, 0, M1_FLY | M1_SEE_INVIS | M1_POIS, M2_DEMON | M2_STALK | M2_HOSTILE | M2_NASTY | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 16, CLR_BLACK, PIT_FIEND), - MON("sandestin", S_DEMON, LVL(13, 12, 4, 60, -5), - (G_HELL | G_NOCORPSE | 1), - A(ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_WEAP, AD_PHYS, 2, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISIBLE | M3_INFRAVISION, + 16, CLR_BLACK, PIT_FIEND), + /* from Jack Vance's _Rhialto_the_Marvellous_, one of the sequels + to _The_Dying_Earth_ */ + MON(NAM("sandestin"), S_DEMON, + LVL(13, 12, 4, 60, -5), (G_HELL | G_NOCORPSE | 1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_WEAP, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1500, 400, MS_CUSS, MZ_HUMAN), MR_STONE, 0, M1_HUMANOID, M2_NOPOLY | M2_STALK | M2_STRONG | M2_COLLECT | M2_SHAPESHIFTER, - M3_INFRAVISIBLE | M3_INFRAVISION, 15, CLR_GRAY, SANDESTIN), - MON("balrog", S_DEMON, LVL(16, 5, -2, 75, -14), (G_HELL | G_NOCORPSE | 1), - A(ATTK(AT_WEAP, AD_PHYS, 8, 4), ATTK(AT_WEAP, AD_PHYS, 4, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISIBLE | M3_INFRAVISION, + 15, CLR_GRAY, SANDESTIN), + MON(NAM("balrog"), S_DEMON, + LVL(16, 5, -2, 75, -14), (G_HELL | G_NOCORPSE | 1), + A(ATTK(AT_WEAP, AD_PHYS, 8, 4), ATTK(AT_WEAP, AD_PHYS, 4, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_SILENT, MZ_HUGE), MR_FIRE | MR_POISON, 0, M1_FLY | M1_SEE_INVIS | M1_POIS, M2_DEMON | M2_STALK | M2_HOSTILE | M2_STRONG | M2_NASTY | M2_COLLECT, - M3_INFRAVISIBLE | M3_INFRAVISION, 20, CLR_RED, BALROG), + M3_INFRAVISIBLE | M3_INFRAVISION, + 20, CLR_RED, BALROG), /* Named demon lords & princes plus Arch-Devils. * (their order matters; see dlord() and dprince()) * If adding or removing, make sure to update NUM_ARCHFIENDS, * FIRST_ARCHFIEND, and LAST_ARCHFIEND. */ - MON("Juiblex", S_DEMON, LVL(50, 6, -7, 65, -15), + MON(NAM("Juiblex"), S_DEMON, LVL(50, 6, -7, 65, -15), (G_HELL | G_NOCORPSE | G_NOGEN | G_UNIQ), A(ATTK(AT_ENGL, AD_DISE, 4, 10), ATTK(AT_SPIT, AD_ACID, 3, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), @@ -2628,21 +3105,21 @@ | M1_ACID | M1_POIS | M1_CLING | M1_HIDE, M2_NOPOLY | M2_DEMON | M2_STALK | M2_HOSTILE | M2_PNAME | M2_NASTY | M2_LORD | M2_MALE, - M3_WAITFORU | M3_WANTSAMUL | M3_INFRAVISION, 26, CLR_BRIGHT_GREEN, - JUIBLEX), - MON("Yeenoghu", S_DEMON, LVL(56, 18, -15, 80, -15), - (G_HELL | G_NOCORPSE | G_NOGEN | G_UNIQ), + M3_WAITFORU | M3_WANTSAMUL | M3_INFRAVISION, + 26, CLR_BRIGHT_GREEN, JUIBLEX), + MON(NAM("Yeenoghu"), S_DEMON, + LVL(56, 18, -15, 80, -15), (G_HELL | G_NOCORPSE | G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 5, 6), ATTK(AT_WEAP, AD_CONF, 2, 8), - ATTK(AT_CLAW, AD_PLYS, 1, 6), ATTK(AT_MAGC, AD_MAGM, 6, 6), NO_ATTK, - NO_ATTK), + ATTK(AT_CLAW, AD_PLYS, 1, 6), ATTK(AT_MAGC, AD_MAGM, 6, 6), + NO_ATTK, NO_ATTK), SIZ(900, 500, MS_ORC, MZ_LARGE), MR_FIRE | MR_POISON, 0, M1_FLY | M1_SEE_INVIS | M1_POIS, M2_NOPOLY | M2_DEMON | M2_STALK | M2_HOSTILE | M2_PNAME | M2_NASTY | M2_LORD | M2_MALE | M2_COLLECT, - M3_WANTSAMUL | M3_INFRAVISIBLE | M3_INFRAVISION, 31, HI_BOSS, - YEENOGHU), - MON("Orcus", S_DEMON, LVL(66, 9, -6, 85, -20), - (G_HELL | G_NOCORPSE | G_NOGEN | G_UNIQ), + M3_WANTSAMUL | M3_INFRAVISIBLE | M3_INFRAVISION, + 31, HI_OVERLORD, YEENOGHU), + MON(NAM("Orcus"), S_DEMON, + LVL(66, 9, -6, 85, -20), (G_HELL | G_NOCORPSE | G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 3, 6), ATTK(AT_CLAW, AD_PHYS, 3, 4), ATTK(AT_CLAW, AD_PHYS, 3, 4), ATTK(AT_MAGC, AD_SPEL, 8, 6), ATTK(AT_STNG, AD_DRST, 2, 4), NO_ATTK), @@ -2652,29 +3129,29 @@ | M2_PRINCE | M2_MALE | M2_COLLECT, M3_WAITFORU | M3_WANTSBOOK | M3_WANTSAMUL | M3_INFRAVISIBLE | M3_INFRAVISION, - 36, HI_BOSS, ORCUS), - MON("Geryon", S_DEMON, LVL(72, 3, -3, 75, 15), - (G_HELL | G_NOCORPSE | G_NOGEN | G_UNIQ), + 36, HI_OVERLORD, ORCUS), + MON(NAM("Geryon"), S_DEMON, + LVL(72, 3, -3, 75, 15), (G_HELL | G_NOCORPSE | G_NOGEN | G_UNIQ), A(ATTK(AT_CLAW, AD_PHYS, 3, 6), ATTK(AT_CLAW, AD_PHYS, 3, 6), ATTK(AT_STNG, AD_DRST, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1500, 500, MS_BRIBE, MZ_HUGE), MR_FIRE | MR_POISON, 0, M1_FLY | M1_SEE_INVIS | M1_POIS | M1_SLITHY, M2_NOPOLY | M2_DEMON | M2_STALK | M2_HOSTILE | M2_PNAME | M2_NASTY | M2_PRINCE | M2_MALE, - M3_WANTSAMUL | M3_INFRAVISIBLE | M3_INFRAVISION, 36, HI_BOSS, - GERYON), - MON("Dispater", S_DEMON, LVL(78, 15, -2, 80, 15), - (G_HELL | G_NOCORPSE | G_NOGEN | G_UNIQ), - A(ATTK(AT_WEAP, AD_PHYS, 4, 6), ATTK(AT_MAGC, AD_SPEL, 6, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_WANTSAMUL | M3_INFRAVISIBLE | M3_INFRAVISION, + 36, HI_OVERLORD, GERYON), + MON(NAM("Dispater"), S_DEMON, + LVL(78, 15, -2, 80, 15), (G_HELL | G_NOCORPSE | G_NOGEN | G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 4, 6), ATTK(AT_MAGC, AD_SPEL, 6, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1500, 500, MS_BRIBE, MZ_HUMAN), MR_FIRE | MR_POISON, 0, M1_FLY | M1_SEE_INVIS | M1_POIS | M1_HUMANOID, M2_NOPOLY | M2_DEMON | M2_STALK | M2_HOSTILE | M2_PNAME | M2_NASTY | M2_PRINCE | M2_MALE | M2_COLLECT, - M3_WANTSAMUL | M3_INFRAVISIBLE | M3_INFRAVISION, 40, HI_BOSS, - DISPATER), - MON("Baalzebub", S_DEMON, LVL(89, 9, -5, 85, 20), - (G_HELL | G_NOCORPSE | G_NOGEN | G_UNIQ), + M3_WANTSAMUL | M3_INFRAVISIBLE | M3_INFRAVISION, + 40, HI_OVERLORD, DISPATER), + MON(NAM("Baalzebub"), S_DEMON, + LVL(89, 9, -5, 85, 20), (G_HELL | G_NOCORPSE | G_NOGEN | G_UNIQ), A(ATTK(AT_BITE, AD_PHYS, 6, 6), ATTK(AT_GAZE, AD_STUN, 2, 6), ATTK(AT_STNG, AD_DRST, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1500, 500, MS_BRIBE, MZ_LARGE), MR_FIRE | MR_POISON, 0, @@ -2682,19 +3159,19 @@ M2_NOPOLY | M2_DEMON | M2_STALK | M2_HOSTILE | M2_PNAME | M2_NASTY | M2_PRINCE | M2_MALE, M3_WANTSAMUL | M3_WAITFORU | M3_INFRAVISIBLE | M3_INFRAVISION, - 45, HI_BOSS, BAALZEBUB), - MON("Asmodeus", S_DEMON, LVL(105, 12, -7, 90, 20), - (G_HELL | G_NOCORPSE | G_NOGEN | G_UNIQ), - A(ATTK(AT_CLAW, AD_PHYS, 4, 4), ATTK(AT_MAGC, AD_SPEL, 6, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + 45, HI_OVERLORD, BAALZEBUB), + MON(NAM("Asmodeus"), S_DEMON, + LVL(105, 12, -7, 90, 20), (G_HELL | G_NOCORPSE | G_NOGEN | G_UNIQ), + A(ATTK(AT_CLAW, AD_PHYS, 4, 4), ATTK(AT_MAGC, AD_SPEL, 6, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1500, 500, MS_BRIBE, MZ_HUGE), MR_FIRE | MR_COLD | MR_POISON, 0, M1_FLY | M1_SEE_INVIS | M1_HUMANOID | M1_POIS, M2_NOPOLY | M2_DEMON | M2_STALK | M2_HOSTILE | M2_PNAME | M2_STRONG | M2_NASTY | M2_PRINCE | M2_MALE, M3_WANTSAMUL | M3_WAITFORU | M3_INFRAVISION, - 53, HI_BOSS, ASMODEUS), - MON("Demogorgon", S_DEMON, LVL(106, 15, -8, 95, -20), - (G_HELL | G_NOCORPSE | G_NOGEN | G_UNIQ), + 53, HI_OVERLORD, ASMODEUS), + MON(NAM("Demogorgon"), S_DEMON, + LVL(106, 15, -8, 95, -20), (G_HELL | G_NOCORPSE | G_NOGEN | G_UNIQ), A(ATTK(AT_MAGC, AD_SPEL, 8, 6), ATTK(AT_STNG, AD_DRLI, 1, 4), ATTK(AT_BITE, AD_WTHR, 4, 4), ATTK(AT_CLAW, AD_DISE, 1, 6), ATTK(AT_CLAW, AD_DISE, 1, 6), NO_ATTK), @@ -2702,43 +3179,46 @@ M1_FLY | M1_SEE_INVIS | M1_NOHANDS | M1_POIS, M2_NOPOLY | M2_DEMON | M2_STALK | M2_HOSTILE | M2_PNAME | M2_NASTY | M2_PRINCE | M2_MALE, - M3_WANTSAMUL | M3_INFRAVISIBLE | M3_INFRAVISION, 57, HI_BOSS, - DEMOGORGON), + M3_WANTSAMUL | M3_INFRAVISIBLE | M3_INFRAVISION, + 57, HI_OVERLORD, DEMOGORGON), /* Riders -- the Four Horsemen of the Apocalypse ("War" == player); * depicted with '&' but do not have M2_DEMON set. */ - MON("Death", S_DEMON, LVL(30, 12, -5, 100, 0), (G_UNIQ | G_NOGEN), - A(ATTK(AT_TUCH, AD_DETH, 8, 8), ATTK(AT_TUCH, AD_DETH, 8, 8), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + MON(NAM("Death"), S_DEMON, + LVL(30, 12, -5, 100, 0), (G_UNIQ | G_NOGEN), + A(ATTK(AT_TUCH, AD_DETH, 8, 8), ATTK(AT_TUCH, AD_DETH, 8, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 1, MS_RIDER, MZ_HUMAN), MR_FIRE | MR_COLD | MR_ELEC | MR_SLEEP | MR_POISON | MR_STONE, 0, M1_FLY | M1_HUMANOID | M1_REGEN | M1_SEE_INVIS | M1_TPORT_CNTRL, M2_NOPOLY | M2_STALK | M2_HOSTILE | M2_PNAME | M2_STRONG | M2_NASTY, - M3_INFRAVISIBLE | M3_INFRAVISION | M3_DISPLACES, 34, HI_BOSS, - DEATH), - MON("Pestilence", S_DEMON, LVL(30, 12, -5, 100, 0), (G_UNIQ | G_NOGEN), - A(ATTK(AT_TUCH, AD_PEST, 8, 8), ATTK(AT_TUCH, AD_PEST, 8, 8), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISIBLE | M3_INFRAVISION | M3_DISPLACES, + 34, HI_OVERLORD, DEATH), + MON(NAM("Pestilence"), S_DEMON, + LVL(30, 12, -5, 100, 0), (G_UNIQ | G_NOGEN), + A(ATTK(AT_TUCH, AD_PEST, 8, 8), ATTK(AT_TUCH, AD_PEST, 8, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 1, MS_RIDER, MZ_HUMAN), MR_FIRE | MR_COLD | MR_ELEC | MR_SLEEP | MR_POISON | MR_STONE, 0, M1_FLY | M1_HUMANOID | M1_REGEN | M1_SEE_INVIS | M1_TPORT_CNTRL, M2_NOPOLY | M2_STALK | M2_HOSTILE | M2_PNAME | M2_STRONG | M2_NASTY, - M3_INFRAVISIBLE | M3_INFRAVISION | M3_DISPLACES, 34, HI_BOSS, - PESTILENCE), - MON("Famine", S_DEMON, LVL(30, 12, -5, 100, 0), (G_UNIQ | G_NOGEN), - A(ATTK(AT_TUCH, AD_FAMN, 8, 8), ATTK(AT_TUCH, AD_FAMN, 8, 8), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISIBLE | M3_INFRAVISION | M3_DISPLACES, + 34, HI_OVERLORD, PESTILENCE), + MON(NAM("Famine"), S_DEMON, + LVL(30, 12, -5, 100, 0), (G_UNIQ | G_NOGEN), + A(ATTK(AT_TUCH, AD_FAMN, 8, 8), ATTK(AT_TUCH, AD_FAMN, 8, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 1, MS_RIDER, MZ_HUMAN), MR_FIRE | MR_COLD | MR_ELEC | MR_SLEEP | MR_POISON | MR_STONE, 0, M1_FLY | M1_HUMANOID | M1_REGEN | M1_SEE_INVIS | M1_TPORT_CNTRL, M2_NOPOLY | M2_STALK | M2_HOSTILE | M2_PNAME | M2_STRONG | M2_NASTY, - M3_INFRAVISIBLE | M3_INFRAVISION | M3_DISPLACES, 34, HI_BOSS, - FAMINE), + M3_INFRAVISIBLE | M3_INFRAVISION | M3_DISPLACES, + 34, HI_OVERLORD, FAMINE), /* other demons */ #ifdef MAIL_STRUCTURES - MON("mail daemon", S_DEMON, LVL(56, 24, 10, 127, 0), - (G_NOGEN | G_NOCORPSE), + MON(NAM("mail daemon"), S_DEMON, + LVL(56, 24, 10, 127, 0), (G_NOGEN | G_NOCORPSE), A(NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(600, 300, MS_SILENT, MZ_HUMAN), MR_FIRE | MR_COLD | MR_ELEC | MR_SLEEP | MR_POISON | MR_STONE, 0, @@ -2747,110 +3227,142 @@ M2_NOPOLY | M2_STALK | M2_PEACEFUL, M3_INFRAVISIBLE | M3_INFRAVISION, 26, CLR_BRIGHT_BLUE, MAIL_DAEMON), #endif - MON("djinni", S_DEMON, LVL(7, 12, 4, 30, 0), (G_NOGEN | G_NOCORPSE), - A(ATTK(AT_WEAP, AD_PHYS, 2, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + /* djinni is singular; plural is djinn */ + MON(NAM("djinni"), S_DEMON, + LVL(7, 12, 4, 30, 0), (G_NOGEN | G_NOCORPSE), + A(ATTK(AT_WEAP, AD_PHYS, 2, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1500, 400, MS_DJINNI, MZ_HUMAN), MR_POISON | MR_STONE, 0, M1_HUMANOID | M1_FLY | M1_POIS, M2_NOPOLY | M2_STALK | M2_COLLECT, M3_INFRAVISIBLE, 8, CLR_YELLOW, DJINNI), /* * sea monsters - */ - MON("jellyfish", S_EEL, LVL(3, 3, 6, 0, 0), (G_GENO | G_NOGEN | 2), - A(ATTK(AT_STNG, AD_DRST, 3, 3), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + * + * 3.7: all the fish except kraken used to specify M1_SLITHY, presumably + * cloned from giant eel. Using "slither" to describe their movement + * wasn't appropriate. Unfortunately, locomotion() isn't able to choose + * "swim" as their movement description because it is only passed a + * monster type, not a specific monster (for ) or the relevant + * location, and therefore doesn't know whether water is involved. + */ + MON(NAM("jellyfish"), S_EEL, + LVL(3, 3, 6, 0, 0), (G_GENO | G_NOGEN | 2), + A(ATTK(AT_STNG, AD_DRST, 3, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(80, 20, MS_SILENT, MZ_SMALL), MR_POISON, MR_POISON, - M1_SWIM | M1_AMPHIBIOUS | M1_SLITHY | M1_NOLIMBS | M1_NOHEAD + M1_SWIM | M1_AMPHIBIOUS | M1_NOLIMBS | M1_NOHEAD | M1_NOTAKE | M1_POIS, - M2_HOSTILE, 0, 5, CLR_BLUE, JELLYFISH), - MON("piranha", S_EEL, LVL(5, 18, 4, 0, 0), - (G_GENO | G_NOGEN | G_SGROUP | 3), + M2_HOSTILE, 0, + 5, CLR_BLUE, JELLYFISH), + MON(NAM("piranha"), S_EEL, + LVL(5, 18, 4, 0, 0), (G_GENO | G_NOGEN | G_SGROUP | 3), A(ATTK(AT_BITE, AD_PHYS, 2, 6), ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(60, 30, MS_SILENT, MZ_SMALL), 0, 0, M1_SWIM | M1_AMPHIBIOUS | M1_ANIMAL | M1_SLITHY | M1_NOLIMBS | M1_CARNIVORE | M1_OVIPAROUS | M1_NOTAKE, - M2_HOSTILE, 0, 7, CLR_ORANGE, PIRANHA), - MON("shark", S_EEL, LVL(7, 12, 2, 0, 0), (G_GENO | G_NOGEN | 2), - A(ATTK(AT_BITE, AD_PHYS, 5, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_HOSTILE, 0, + 7, CLR_ORANGE, PIRANHA), + MON(NAM("shark"), S_EEL, + LVL(7, 12, 2, 0, 0), (G_GENO | G_NOGEN | 2), + A(ATTK(AT_BITE, AD_PHYS, 5, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(500, 350, MS_SILENT, MZ_LARGE), 0, 0, - M1_SWIM | M1_AMPHIBIOUS | M1_ANIMAL | M1_SLITHY | M1_NOLIMBS + M1_SWIM | M1_AMPHIBIOUS | M1_ANIMAL | M1_NOLIMBS | M1_CARNIVORE | M1_OVIPAROUS | M1_THICK_HIDE | M1_NOTAKE, - M2_HOSTILE, 0, 9, CLR_GRAY, SHARK), - MON("giant eel", S_EEL, LVL(5, 9, -1, 0, 0), (G_GENO | G_NOGEN | 4), - A(ATTK(AT_BITE, AD_PHYS, 3, 6), ATTK(AT_TUCH, AD_WRAP, 0, 0), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M2_HOSTILE, 0, + 9, CLR_GRAY, SHARK), + MON(NAM("giant eel"), S_EEL, + LVL(5, 9, -1, 0, 0), (G_GENO | G_NOGEN | 4), + A(ATTK(AT_BITE, AD_PHYS, 3, 6), ATTK(AT_TUCH, AD_WRAP, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(200, 250, MS_SILENT, MZ_HUGE), 0, 0, M1_SWIM | M1_AMPHIBIOUS | M1_ANIMAL | M1_SLITHY | M1_NOLIMBS | M1_CARNIVORE | M1_OVIPAROUS | M1_NOTAKE, - M2_HOSTILE, M3_INFRAVISIBLE, 7, CLR_CYAN, GIANT_EEL), - MON("electric eel", S_EEL, LVL(7, 10, -3, 0, 0), (G_GENO | G_NOGEN | 2), - A(ATTK(AT_BITE, AD_ELEC, 4, 6), ATTK(AT_TUCH, AD_WRAP, 0, 0), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M2_HOSTILE, M3_INFRAVISIBLE, + 7, CLR_CYAN, GIANT_EEL), + MON(NAM("electric eel"), S_EEL, + LVL(7, 10, -3, 0, 0), (G_GENO | G_NOGEN | 2), + A(ATTK(AT_BITE, AD_ELEC, 4, 6), ATTK(AT_TUCH, AD_WRAP, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(200, 250, MS_SILENT, MZ_LARGE), MR_ELEC, MR_ELEC, M1_SWIM | M1_AMPHIBIOUS | M1_ANIMAL | M1_SLITHY | M1_NOLIMBS | M1_CARNIVORE | M1_OVIPAROUS | M1_NOTAKE, - M2_HOSTILE, M3_INFRAVISIBLE, 10, CLR_BRIGHT_BLUE, ELECTRIC_EEL), - MON("kraken", S_EEL, LVL(20, 3, 6, 0, -3), (G_GENO | G_NOGEN | 1), + M2_HOSTILE, M3_INFRAVISIBLE, + 10, CLR_BRIGHT_BLUE, ELECTRIC_EEL), + MON(NAM("kraken"), S_EEL, + LVL(20, 3, 6, 0, -3), (G_GENO | G_NOGEN | 1), A(ATTK(AT_CLAW, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_PHYS, 2, 4), - ATTK(AT_HUGS, AD_WRAP, 2, 6), ATTK(AT_BITE, AD_PHYS, 5, 4), NO_ATTK, - NO_ATTK), + ATTK(AT_HUGS, AD_WRAP, 2, 6), ATTK(AT_BITE, AD_PHYS, 5, 4), + NO_ATTK, NO_ATTK), SIZ(1800, 1000, MS_SILENT, MZ_GIGANTIC), 0, 0, M1_SWIM | M1_AMPHIBIOUS | M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, - M2_NOPOLY | M2_HOSTILE | M2_STRONG, M3_INFRAVISIBLE, 22, CLR_RED, - KRAKEN), + M2_NOPOLY | M2_HOSTILE | M2_STRONG, M3_INFRAVISIBLE, + 22, CLR_RED, KRAKEN), /* * lizards, &c */ - MON("newt", S_LIZARD, LVL(0, 6, 8, 0, 0), (G_GENO | 5), - A(ATTK(AT_BITE, AD_PHYS, 1, 2), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("newt"), S_LIZARD, + LVL(0, 6, 8, 0, 0), (G_GENO | 5), + A(ATTK(AT_BITE, AD_PHYS, 1, 2), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(10, 20, MS_SILENT, MZ_TINY), 0, 0, M1_SWIM | M1_AMPHIBIOUS | M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, - M2_HOSTILE, 0, 1, CLR_YELLOW, NEWT), - MON("gecko", S_LIZARD, LVL(1, 6, 8, 0, 0), (G_GENO | 5), - A(ATTK(AT_BITE, AD_PHYS, 1, 3), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_HOSTILE, 0, + 1, CLR_YELLOW, NEWT), + MON(NAM("gecko"), S_LIZARD, + LVL(1, 6, 8, 0, 0), (G_GENO | 5), + A(ATTK(AT_BITE, AD_PHYS, 1, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(10, 20, MS_SQEEK, MZ_TINY), 0, 0, - M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, 0, 2, - CLR_BRIGHT_GREEN, GECKO), - MON("iguana", S_LIZARD, LVL(2, 6, 7, 0, 0), (G_GENO | 5), - A(ATTK(AT_BITE, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, 0, + 2, CLR_BRIGHT_GREEN, GECKO), + MON(NAM("iguana"), S_LIZARD, + LVL(2, 6, 7, 0, 0), (G_GENO | 5), + A(ATTK(AT_BITE, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(30, 30, MS_SILENT, MZ_TINY), 0, 0, - M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, 0, 3, CLR_BROWN, - IGUANA), - MON("baby crocodile", S_LIZARD, LVL(3, 6, 7, 0, 0), G_GENO, - A(ATTK(AT_BITE, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, 0, + 3, CLR_BROWN, IGUANA), + MON(NAM("baby crocodile"), S_LIZARD, + LVL(3, 6, 7, 0, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(200, 200, MS_CHIRP, MZ_SMALL), 0, 0, M1_SWIM | M1_AMPHIBIOUS | M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, - M2_HOSTILE, 0, 4, CLR_RED, BABY_CROCODILE), - MON("lizard", S_LIZARD, LVL(5, 6, 6, 10, 0), (G_GENO | 5), - A(ATTK(AT_BITE, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M2_HOSTILE, 0, + 4, CLR_RED, BABY_CROCODILE), + /* trivia: before monster corpses were implemented, "lizard corpse" + was a specific type of item */ + MON(NAM("lizard"), S_LIZARD, + LVL(5, 6, 6, 10, 0), (G_GENO | 5), + A(ATTK(AT_BITE, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(10, 40, MS_SILENT, MZ_TINY), MR_STONE, MR_STONE, - M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, 0, 6, CLR_GREEN, - LIZARD), - MON("chameleon", S_LIZARD, LVL(6, 5, 6, 10, 0), (G_GENO | 2), - A(ATTK(AT_BITE, AD_PHYS, 4, 2), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, M2_HOSTILE, 0, + 6, CLR_GREEN, LIZARD), + MON(NAM("chameleon"), S_LIZARD, + LVL(6, 5, 6, 10, 0), (G_GENO | 2), + A(ATTK(AT_BITE, AD_PHYS, 4, 2), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(100, 100, MS_SILENT, MZ_TINY), 0, 0, M1_ANIMAL | M1_NOHANDS | M1_CARNIVORE, - M2_NOPOLY | M2_HOSTILE | M2_SHAPESHIFTER, 0, 7, CLR_GRAY, - CHAMELEON), - MON("crocodile", S_LIZARD, LVL(6, 9, 5, 0, 0), (G_GENO | 1), + M2_NOPOLY | M2_HOSTILE | M2_SHAPESHIFTER, 0, + 7, CLR_GRAY, CHAMELEON), + MON(NAM("crocodile"), S_LIZARD, + LVL(6, 9, 5, 0, 0), (G_GENO | 1), A(ATTK(AT_BITE, AD_PHYS, 4, 2), ATTK(AT_CLAW, AD_PHYS, 1, 12), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_BELLOW, MZ_LARGE), 0, 0, M1_SWIM | M1_AMPHIBIOUS | M1_ANIMAL | M1_THICK_HIDE | M1_NOHANDS | M1_OVIPAROUS | M1_CARNIVORE, - M2_STRONG | M2_HOSTILE, 0, 7, CLR_RED, CROCODILE), - MON("salamander", S_LIZARD, LVL(8, 12, -1, 0, -9), (G_HELL | 1), + M2_STRONG | M2_HOSTILE, 0, + 7, CLR_RED, CROCODILE), + MON(NAM("salamander"), S_LIZARD, + LVL(8, 12, -1, 0, -9), (G_HELL | 1), A(ATTK(AT_WEAP, AD_PHYS, 2, 8), ATTK(AT_TUCH, AD_FIRE, 1, 6), - ATTK(AT_HUGS, AD_PHYS, 2, 6), ATTK(AT_HUGS, AD_FIRE, 3, 6), NO_ATTK, - NO_ATTK), + ATTK(AT_HUGS, AD_PHYS, 2, 6), ATTK(AT_HUGS, AD_FIRE, 3, 6), + NO_ATTK, NO_ATTK), SIZ(1500, 400, MS_MUMBLE, MZ_HUMAN), MR_SLEEP | MR_FIRE, MR_FIRE, M1_HUMANOID | M1_SLITHY | M1_THICK_HIDE | M1_POIS | M1_CARNIVORE, M2_STALK | M2_HOSTILE | M2_COLLECT | M2_MAGIC, M3_INFRAVISIBLE, @@ -2860,11 +3372,11 @@ * dummy monster needed for visual interface * (marking it unique prevents figurines) */ - MON("long worm tail", S_WORM_TAIL, LVL(0, 0, 0, 0, 0), - (G_NOGEN | G_NOCORPSE | G_UNIQ), + MON(NAM("long worm tail"), S_WORM_TAIL, + LVL(0, 0, 0, 0, 0), (G_NOGEN | G_NOCORPSE | G_UNIQ), A(NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(0, 0, 0, 0), 0, 0, 0L, M2_NOPOLY, 0, 1, CLR_BROWN, - LONG_WORM_TAIL), + SIZ(0, 0, 0, 0), 0, 0, 0L, M2_NOPOLY, 0, + 1, CLR_BROWN, LONG_WORM_TAIL), /* Note: * Worm tail must be between the normal monsters and the special * quest & pseudo-character ones because an optimization in the @@ -2875,81 +3387,95 @@ /* * character classes */ - MON("archeologist", S_HUMAN, LVL(10, 12, 10, 1, 3), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + MON(NAM("archeologist"), S_HUMAN, + LVL(10, 12, 10, 1, 3), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_HUMANOID, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_TUNNEL | M1_NEEDPICK | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_COLLECT, M3_INFRAVISIBLE, 12, HI_DOMESTIC, ARCHEOLOGIST), - MON("barbarian", S_HUMAN, LVL(10, 12, 10, 1, 0), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + MON(NAM("barbarian"), S_HUMAN, + LVL(10, 12, 10, 1, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_HUMANOID, MZ_HUMAN), MR_POISON, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_COLLECT, M3_INFRAVISIBLE, 12, HI_DOMESTIC, BARBARIAN), - MON3("caveman", "cavewoman", "cave dweller", - S_HUMAN, LVL(10, 12, 10, 0, 1), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAMS("caveman", "cavewoman", "cave dweller"), S_HUMAN, + LVL(10, 12, 10, 0, 1), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_HUMANOID, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_COLLECT, - M3_INFRAVISIBLE, 12, HI_DOMESTIC, CAVE_DWELLER), - MON("healer", S_HUMAN, LVL(10, 12, 10, 1, 0), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE, + 12, HI_DOMESTIC, CAVE_DWELLER), + MON(NAM("healer"), S_HUMAN, + LVL(10, 12, 10, 1, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_HUMANOID, MZ_HUMAN), MR_POISON, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_COLLECT, M3_INFRAVISIBLE, 12, HI_DOMESTIC, HEALER), - MON("knight", S_HUMAN, LVL(10, 12, 10, 1, 3), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + MON(NAM("knight"), S_HUMAN, + LVL(10, 12, 10, 1, 3), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_HUMANOID, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_COLLECT, M3_INFRAVISIBLE, 12, HI_DOMESTIC, KNIGHT), - MON("monk", S_HUMAN, LVL(10, 12, 10, 2, 0), G_NOGEN, - A(ATTK(AT_CLAW, AD_PHYS, 1, 8), ATTK(AT_KICK, AD_PHYS, 1, 8), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + MON(NAM("monk"), S_HUMAN, + LVL(10, 12, 10, 2, 0), G_NOGEN, + A(ATTK(AT_CLAW, AD_PHYS, 1, 8), ATTK(AT_KICK, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_HUMANOID, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_HERBIVORE, M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_COLLECT, - M3_INFRAVISIBLE, 11, HI_DOMESTIC, MONK), - MON3("priest", "priestess", "cleric", - S_HUMAN, LVL(10, 12, 10, 2, 0), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE, + 11, HI_DOMESTIC, MONK), + /* monster priests are separate monsters (above; "aligned cleric") */ + MON(NAMS("priest", "priestess", "cleric"), S_HUMAN, + LVL(10, 12, 10, 2, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_HUMANOID, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_COLLECT, - M3_INFRAVISIBLE, 12, HI_DOMESTIC, CLERIC), - MON("ranger", S_HUMAN, LVL(10, 12, 10, 2, -3), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE, + 12, HI_DOMESTIC, CLERIC), + MON(NAM("ranger"), S_HUMAN, + LVL(10, 12, 10, 2, -3), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_HUMANOID, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_COLLECT, M3_INFRAVISIBLE, 12, HI_DOMESTIC, RANGER), - MON("rogue", S_HUMAN, LVL(10, 12, 10, 1, -3), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + MON(NAM("rogue"), S_HUMAN, + LVL(10, 12, 10, 1, -3), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_HUMANOID, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_GREEDY | M2_JEWELS | M2_COLLECT, - M3_INFRAVISIBLE, 12, HI_DOMESTIC, ROGUE), - MON("samurai", S_HUMAN, LVL(10, 12, 10, 1, 3), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISIBLE, + 12, HI_DOMESTIC, ROGUE), + MON(NAM("samurai"), S_HUMAN, + LVL(10, 12, 10, 1, 3), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_HUMANOID, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_COLLECT, M3_INFRAVISIBLE, 12, HI_DOMESTIC, SAMURAI), - MON("tourist", S_HUMAN, LVL(10, 12, 10, 1, 0), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + MON(NAM("tourist"), S_HUMAN, + LVL(10, 12, 10, 1, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_HUMANOID, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_COLLECT, M3_INFRAVISIBLE, @@ -2959,51 +3485,58 @@ if a neutral valk leaves a bones file containing neutral warriors, the latter will magically turn lawful if encountered by a lawful valk or any non-valk (for bones on the dungeon side of the portal) */ - MON("valkyrie", S_HUMAN, LVL(10, 12, 10, 1, 1), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + MON(NAM("valkyrie"), S_HUMAN, + LVL(10, 12, 10, 1, 1), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_HUMANOID, MZ_HUMAN), MR_COLD, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_FEMALE | M2_COLLECT, - M3_INFRAVISIBLE, 12, HI_DOMESTIC, VALKYRIE), - MON("wizard", S_HUMAN, LVL(10, 12, 10, 3, 0), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE, + 12, HI_DOMESTIC, VALKYRIE), + MON(NAM("wizard"), S_HUMAN, + LVL(10, 12, 10, 3, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_HUMANOID, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_COLLECT | M2_MAGIC, - M3_INFRAVISIBLE, 12, HI_DOMESTIC, WIZARD), + M3_INFRAVISIBLE, + 12, HI_DOMESTIC, WIZARD), /* * quest leaders */ - MON("Lord Carnarvon", S_HUMAN, LVL(20, 15, 0, 90, 20), (G_NOGEN | G_UNIQ), + MON(NAM("Lord Carnarvon"), S_HUMAN, + LVL(20, 15, 0, 90, 20), (G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 4, 10), ATTK(AT_MAGC, AD_SPEL, 4, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_LEADER, MZ_HUMAN), 0, 0, M1_TUNNEL | M1_NEEDPICK | M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PNAME | M2_PEACEFUL | M2_STRONG | M2_MALE - | M2_COLLECT | M2_MAGIC, - M3_CLOSE | M3_INFRAVISIBLE, 24, HI_LORD, LORD_CARNARVON), - MON("Pelias", S_HUMAN, LVL(20, 15, 0, 90, 0), (G_NOGEN | G_UNIQ), + | M2_COLLECT | M2_MAGIC, M3_CLOSE | M3_INFRAVISIBLE, + 24, HI_LORD, LORD_CARNARVON), + MON(NAM("Pelias"), S_HUMAN, + LVL(20, 15, 0, 90, 0), (G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 4, 10), ATTK(AT_WEAP, AD_PHYS, 4, 10), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_LEADER, MZ_HUMAN), MR_POISON, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PNAME | M2_PEACEFUL | M2_STRONG | M2_MALE - | M2_COLLECT | M2_MAGIC, - M3_CLOSE | M3_INFRAVISIBLE, 24, HI_LORD, PELIAS), - MON("Shaman Karnov", S_HUMAN, LVL(20, 15, 0, 90, 20), (G_NOGEN | G_UNIQ), + | M2_COLLECT | M2_MAGIC, M3_CLOSE | M3_INFRAVISIBLE, + 24, HI_LORD, PELIAS), + MON(NAM("Shaman Karnov"), S_HUMAN, + LVL(20, 15, 0, 90, 20), (G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 4, 10), ATTK(AT_MAGC, AD_CLRC, 2, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_LEADER, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PNAME | M2_PEACEFUL | M2_STRONG | M2_MALE - | M2_COLLECT | M2_MAGIC, - M3_CLOSE | M3_INFRAVISIBLE, 24, HI_LORD, SHAMAN_KARNOV), + | M2_COLLECT | M2_MAGIC, M3_CLOSE | M3_INFRAVISIBLE, + 24, HI_LORD, SHAMAN_KARNOV), #if 0 /* OBSOLETE -- leaders for 3.1.x/3.2.x elf quest when elf was a role */ /* Two for elves - one of each sex. */ - MON("Earendil", S_HUMAN, + MON(NAM("Earendil"), S_HUMAN, LVL(20, 12, 0, 50, -20), (G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), @@ -3011,9 +3544,9 @@ M1_HUMANOID | M1_SEE_INVIS | M1_OMNIVORE, M2_NOPOLY | M2_ELF | M2_HUMAN | M2_PNAME | M2_PEACEFUL | M2_STRONG | M2_MALE | M2_COLLECT | M2_MAGIC, - M3_CLOSE | M3_INFRAVISION | M3_INFRAVISIBLE, 22, HI_LORD, - EARENDIL), - MON("Elwing", S_HUMAN, + M3_CLOSE | M3_INFRAVISION | M3_INFRAVISIBLE, + 22, HI_LORD, EARENDIL), + MON(NAM("Elwing"), S_HUMAN, LVL(20, 12, 0, 50, -20), (G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), @@ -3021,104 +3554,112 @@ M1_HUMANOID | M1_SEE_INVIS | M1_OMNIVORE, M2_NOPOLY | M2_ELF | M2_HUMAN | M2_PNAME | M2_PEACEFUL | M2_STRONG | M2_FEMALE | M2_COLLECT | M2_MAGIC, - M3_CLOSE | M3_INFRAVISION | M3_INFRAVISIBLE, 22, HI_LORD, - ELWING), + M3_CLOSE | M3_INFRAVISION | M3_INFRAVISIBLE, + 22, HI_LORD, ELWING), #endif - MON("Hippocrates", S_HUMAN, LVL(20, 15, 0, 90, 0), (G_NOGEN | G_UNIQ), + MON(NAM("Hippocrates"), S_HUMAN, + LVL(20, 15, 0, 90, 0), (G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_MAGC, AD_CLRC, 3, 8), ATTK(AT_MAGC, AD_CLRC, 3, 8), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_LEADER, MZ_HUMAN), MR_POISON, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PNAME | M2_PEACEFUL | M2_STRONG | M2_MALE - | M2_COLLECT | M2_MAGIC, - M3_CLOSE | M3_INFRAVISIBLE, 26, HI_LORD, HIPPOCRATES), - MON("King Arthur", S_HUMAN, LVL(20, 15, 0, 90, 20), (G_NOGEN | G_UNIQ), + | M2_COLLECT | M2_MAGIC, M3_CLOSE | M3_INFRAVISIBLE, + 26, HI_LORD, HIPPOCRATES), + MON(NAM("King Arthur"), S_HUMAN, + LVL(20, 15, 0, 90, 20), (G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 4, 10), ATTK(AT_WEAP, AD_PHYS, 4, 10), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_LEADER, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PNAME | M2_PEACEFUL | M2_STRONG | M2_MALE - | M2_COLLECT | M2_MAGIC, - M3_CLOSE | M3_INFRAVISIBLE, 24, HI_LORD, KING_ARTHUR), - MON("Grand Master", S_HUMAN, LVL(25, 15, 0, 90, 0), (G_NOGEN | G_UNIQ), + | M2_COLLECT | M2_MAGIC, M3_CLOSE | M3_INFRAVISIBLE, + 24, HI_LORD, KING_ARTHUR), + MON(NAM("Grand Master"), S_HUMAN, + LVL(25, 15, 0, 90, 0), (G_NOGEN | G_UNIQ), A(ATTK(AT_CLAW, AD_PHYS, 4, 10), ATTK(AT_KICK, AD_PHYS, 2, 8), - ATTK(AT_MAGC, AD_CLRC, 2, 8), ATTK(AT_MAGC, AD_CLRC, 2, 8), NO_ATTK, - NO_ATTK), + ATTK(AT_MAGC, AD_CLRC, 2, 8), ATTK(AT_MAGC, AD_CLRC, 2, 8), + NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_LEADER, MZ_HUMAN), MR_FIRE | MR_ELEC | MR_SLEEP | MR_POISON, 0, M1_HUMANOID | M1_SEE_INVIS | M1_HERBIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_MALE | M2_NASTY - | M2_MAGIC, - M3_CLOSE | M3_INFRAVISIBLE, 30, CLR_BLACK, GRAND_MASTER), - MON("Arch Priest", S_HUMAN, LVL(25, 15, 7, 90, 0), (G_NOGEN | G_UNIQ), + | M2_MAGIC, M3_CLOSE | M3_INFRAVISIBLE, + 30, CLR_BLACK, GRAND_MASTER), + MON(NAM("Arch Priest"), S_HUMAN, + LVL(25, 15, 7, 90, 0), (G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 4, 10), ATTK(AT_KICK, AD_PHYS, 2, 8), - ATTK(AT_MAGC, AD_CLRC, 2, 8), ATTK(AT_MAGC, AD_CLRC, 2, 8), NO_ATTK, - NO_ATTK), + ATTK(AT_MAGC, AD_CLRC, 2, 8), ATTK(AT_MAGC, AD_CLRC, 2, 8), + NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_LEADER, MZ_HUMAN), MR_FIRE | MR_ELEC | MR_SLEEP | MR_POISON, 0, M1_HUMANOID | M1_SEE_INVIS | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_MALE | M2_COLLECT - | M2_MAGIC, - M3_CLOSE | M3_INFRAVISIBLE, 30, CLR_WHITE, ARCH_PRIEST), - MON("Orion", S_HUMAN, LVL(20, 15, 0, 90, 0), (G_NOGEN | G_UNIQ), + | M2_MAGIC, M3_CLOSE | M3_INFRAVISIBLE, + 30, CLR_WHITE, ARCH_PRIEST), + MON(NAM("Orion"), S_HUMAN, LVL(20, 15, 0, 90, 0), (G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 4, 10), ATTK(AT_MAGC, AD_SPEL, 4, 8), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2200, 700, MS_LEADER, MZ_HUGE), 0, 0, M1_HUMANOID | M1_OMNIVORE | M1_SEE_INVIS | M1_SWIM | M1_AMPHIBIOUS, M2_NOPOLY | M2_HUMAN | M2_PNAME | M2_PEACEFUL | M2_STRONG | M2_MALE | M2_COLLECT | M2_MAGIC, - M3_CLOSE | M3_INFRAVISION | M3_INFRAVISIBLE, 24, HI_LORD, ORION), + M3_CLOSE | M3_INFRAVISION | M3_INFRAVISIBLE, + 24, HI_LORD, ORION), /* Note: Master of Thieves is also the Tourist's nemesis. */ - MON("Master of Thieves", S_HUMAN, LVL(20, 15, 0, 90, -20), - (G_NOGEN | G_UNIQ), + MON(NAM("Master of Thieves"), S_HUMAN, + LVL(20, 15, 0, 90, -20), (G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 4, 10), ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_CLAW, AD_SAMU, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_LEADER, MZ_HUMAN), MR_STONE, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_MALE | M2_GREEDY | M2_JEWELS | M2_COLLECT | M2_MAGIC, - M3_CLOSE | M3_INFRAVISIBLE, 24, HI_LORD, MASTER_OF_THIEVES), - MON("Lord Sato", S_HUMAN, LVL(20, 15, 0, 90, 20), (G_NOGEN | G_UNIQ), + M3_CLOSE | M3_INFRAVISIBLE, + 24, HI_LORD, MASTER_OF_THIEVES), + MON(NAM("Lord Sato"), S_HUMAN, LVL(20, 15, 0, 90, 20), (G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 4, 10), ATTK(AT_WEAP, AD_PHYS, 4, 10), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_LEADER, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PNAME | M2_PEACEFUL | M2_STRONG | M2_MALE - | M2_COLLECT | M2_MAGIC, - M3_CLOSE | M3_INFRAVISIBLE, 24, HI_LORD, LORD_SATO), - MON("Twoflower", S_HUMAN, LVL(20, 15, 10, 90, 0), (G_NOGEN | G_UNIQ), - A(ATTK(AT_WEAP, AD_PHYS, 4, 10), NO_ATTK, NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + | M2_COLLECT | M2_MAGIC, M3_CLOSE | M3_INFRAVISIBLE, + 24, HI_LORD, LORD_SATO), + MON(NAM("Twoflower"), S_HUMAN, + LVL(20, 15, 10, 90, 0), (G_NOGEN | G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 4, 10), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_LEADER, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PNAME | M2_PEACEFUL | M2_STRONG | M2_MALE - | M2_COLLECT | M2_MAGIC, - M3_CLOSE | M3_INFRAVISIBLE, 22, HI_DOMESTIC, TWOFLOWER), + | M2_COLLECT | M2_MAGIC, M3_CLOSE | M3_INFRAVISIBLE, + 22, HI_DOMESTIC, TWOFLOWER), /* for a valkyrie hero, Norn's alignment will be changed to match hero's starting alignment */ - MON("Norn", S_HUMAN, LVL(20, 15, 0, 90, 0), (G_NOGEN | G_UNIQ), + MON(NAM("Norn"), S_HUMAN, + LVL(20, 15, 0, 90, 0), (G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 4, 10), ATTK(AT_WEAP, AD_PHYS, 4, 10), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1800, 550, MS_LEADER, MZ_HUGE), MR_COLD, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_FEMALE - | M2_COLLECT | M2_MAGIC, - M3_CLOSE | M3_INFRAVISIBLE, 24, HI_LORD, NORN), - MON("Neferet the Green", S_HUMAN, LVL(20, 15, 0, 90, 0), - (G_NOGEN | G_UNIQ), + | M2_COLLECT | M2_MAGIC, M3_CLOSE | M3_INFRAVISIBLE, + 24, HI_LORD, NORN), + MON(NAM("Neferet the Green"), S_HUMAN, + LVL(20, 15, 0, 90, 0), (G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 4, 10), ATTK(AT_MAGC, AD_SPEL, 2, 8), ATTK(AT_MAGC, AD_SPEL, 2, 8), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_LEADER, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_FEMALE | M2_PNAME | M2_PEACEFUL | M2_STRONG - | M2_COLLECT | M2_MAGIC, - M3_CLOSE | M3_INFRAVISIBLE, 25, CLR_GREEN, NEFERET_THE_GREEN), + | M2_COLLECT | M2_MAGIC, M3_CLOSE | M3_INFRAVISIBLE, + 25, CLR_GREEN, NEFERET_THE_GREEN), /* * quest nemeses */ - MON("Schliemann", S_HUMAN, LVL(16, 12, 0, 5, -14), - (G_NOGEN | G_UNIQ), + MON(NAM("Schliemann"), S_HUMAN, + LVL(16, 12, 0, 5, -14), (G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 2, 8), ATTK(AT_WEAP, AD_PHYS, 2, 8), ATTK(AT_CLAW, AD_SAMU, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_NEMESIS, MZ_HUMAN), @@ -3128,23 +3669,23 @@ | M2_STRONG | M2_NASTY | M2_GREEDY | M2_COLLECT | M2_MAGIC, M3_WANTSARTI | M3_WAITFORU | M3_INFRAVISIBLE, 23, HI_LORD, SCHLIEMANN), - MON("Thoth Amon", S_HUMAN, LVL(16, 12, 0, 10, -14), - (G_NOGEN | G_UNIQ | G_NOCORPSE), + MON(NAM("Thoth Amon"), S_HUMAN, + LVL(16, 12, 0, 10, -14), (G_NOGEN | G_UNIQ | G_NOCORPSE), A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_MAGC, AD_SPEL, 0, 0), - ATTK(AT_MAGC, AD_SPEL, 0, 0), ATTK(AT_CLAW, AD_SAMU, 1, 4), NO_ATTK, - NO_ATTK), + ATTK(AT_MAGC, AD_SPEL, 0, 0), ATTK(AT_CLAW, AD_SAMU, 1, 4), + NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_NEMESIS, MZ_HUMAN), MR_POISON | MR_STONE, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PNAME | M2_STRONG | M2_MALE | M2_STALK | M2_HOSTILE | M2_NASTY | M2_COLLECT | M2_MAGIC, - M3_WANTSARTI | M3_WAITFORU | M3_INFRAVISIBLE, 22, HI_LORD, - THOTH_AMON), + M3_WANTSARTI | M3_WAITFORU | M3_INFRAVISIBLE, + 22, HI_LORD, THOTH_AMON), /* Multi-headed, possessing the breath attacks of all the other dragons * (selected at random when attacking). Despite being a superset of - * gold dragon, does not emit light. + * gold dragon, does not emit light. Also does not fly. */ - MON("Tiamat", S_DRAGON, LVL(16, 12, 0, 30, -14), - (G_NOGEN | G_UNIQ), + MON(NAM("Tiamat"), S_DRAGON, + LVL(16, 12, 0, 30, -14), (G_NOGEN | G_UNIQ), A(ATTK(AT_BREA, AD_RBRE, 6, 6), ATTK(AT_MAGC, AD_SPEL, 0, 0), ATTK(AT_CLAW, AD_SAMU, 2, 8), ATTK(AT_BITE, AD_PHYS, 4, 8), ATTK(AT_BITE, AD_PHYS, 4, 8), ATTK(AT_STNG, AD_PHYS, 1, 6)), @@ -3159,7 +3700,7 @@ M3_WANTSARTI | M3_WAITFORU | M3_INFRAVISIBLE, 23, HI_LORD, TIAMAT), #if 0 /* OBSOLETE -- nemesis for 3.1.x/3.2.x elf quest when elf was a role */ - MON("Goblin King", S_ORC, + MON(NAM("Goblin King"), S_ORC, LVL(15, 12, 10, 0, -15), (G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_CLAW, AD_SAMU, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK), @@ -3170,7 +3711,8 @@ M3_WANTSARTI | M3_WAITFORU | M3_INFRAVISION | M3_INFRAVISIBLE, 18, HI_LORD, GOBLIN_KING), #endif - MON("Cyclops", S_GIANT, LVL(18, 12, 0, 0, -15), (G_NOGEN | G_UNIQ), + MON(NAM("Cyclops"), S_GIANT, + LVL(18, 12, 0, 0, -15), (G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 4, 8), ATTK(AT_WEAP, AD_PHYS, 4, 8), ATTK(AT_CLAW, AD_SAMU, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1900, 700, MS_NEMESIS, MZ_HUGE), MR_STONE, 0, @@ -3179,7 +3721,8 @@ | M2_HOSTILE | M2_NASTY | M2_MALE | M2_JEWELS | M2_COLLECT, M3_WANTSARTI | M3_WAITFORU | M3_INFRAVISION | M3_INFRAVISIBLE, 23, CLR_GRAY, CYCLOPS), - MON("Ixoth", S_DRAGON, LVL(15, 12, -1, 20, -14), (G_NOGEN | G_UNIQ), + MON(NAM("Ixoth"), S_DRAGON, + LVL(15, 12, -1, 20, -14), (G_NOGEN | G_UNIQ), A(ATTK(AT_BREA, AD_FIRE, 8, 6), ATTK(AT_BITE, AD_PHYS, 4, 8), ATTK(AT_MAGC, AD_SPEL, 0, 0), ATTK(AT_CLAW, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_SAMU, 2, 4), NO_ATTK), @@ -3188,64 +3731,68 @@ M1_FLY | M1_THICK_HIDE | M1_NOHANDS | M1_CARNIVORE | M1_SEE_INVIS, M2_NOPOLY | M2_MALE | M2_PNAME | M2_HOSTILE | M2_STRONG | M2_NASTY | M2_STALK | M2_GREEDY | M2_JEWELS | M2_MAGIC, - M3_WANTSARTI | M3_WAITFORU | M3_INFRAVISIBLE, 22, CLR_RED, - IXOTH), - MON("Master Kaen", S_HUMAN, LVL(25, 12, -10, 10, -20), (G_NOGEN | G_UNIQ), + M3_WANTSARTI | M3_WAITFORU | M3_INFRAVISIBLE, + 22, CLR_RED, IXOTH), + MON(NAM("Master Kaen"), S_HUMAN, + LVL(25, 12, -10, 10, -20), (G_NOGEN | G_UNIQ), A(ATTK(AT_CLAW, AD_PHYS, 16, 2), ATTK(AT_CLAW, AD_PHYS, 16, 2), - ATTK(AT_MAGC, AD_CLRC, 0, 0), ATTK(AT_CLAW, AD_SAMU, 1, 4), NO_ATTK, - NO_ATTK), + ATTK(AT_MAGC, AD_CLRC, 0, 0), ATTK(AT_CLAW, AD_SAMU, 1, 4), + NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_NEMESIS, MZ_HUMAN), MR_POISON | MR_STONE, MR_POISON, M1_HUMANOID | M1_HERBIVORE | M1_SEE_INVIS, M2_NOPOLY | M2_HUMAN | M2_MALE | M2_PNAME | M2_HOSTILE | M2_STRONG | M2_NASTY | M2_STALK | M2_COLLECT | M2_MAGIC, - M3_WANTSARTI | M3_WAITFORU | M3_INFRAVISIBLE, 31, HI_LORD, - MASTER_KAEN), - MON("Nalzok", S_DEMON, LVL(16, 12, -2, 85, -127), + M3_WANTSARTI | M3_WAITFORU | M3_INFRAVISIBLE, + 31, HI_LORD, MASTER_KAEN), + MON(NAM("Nalzok"), S_DEMON, LVL(16, 12, -2, 85, -127), (G_NOGEN | G_UNIQ | G_NOCORPSE), A(ATTK(AT_WEAP, AD_PHYS, 8, 4), ATTK(AT_WEAP, AD_PHYS, 4, 6), - ATTK(AT_MAGC, AD_SPEL, 0, 0), ATTK(AT_CLAW, AD_SAMU, 2, 6), NO_ATTK, - NO_ATTK), + ATTK(AT_MAGC, AD_SPEL, 0, 0), ATTK(AT_CLAW, AD_SAMU, 2, 6), + NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_NEMESIS, MZ_LARGE), MR_FIRE | MR_POISON | MR_STONE, 0, M1_FLY | M1_SEE_INVIS | M1_POIS, M2_NOPOLY | M2_DEMON | M2_MALE | M2_PNAME | M2_HOSTILE | M2_STRONG | M2_STALK | M2_NASTY | M2_COLLECT, M3_WANTSARTI | M3_WAITFORU | M3_INFRAVISION | M3_INFRAVISIBLE, - 23, CLR_RED, NALZOK), - MON("Scorpius", S_SPIDER, LVL(15, 12, 10, 0, -15), (G_NOGEN | G_UNIQ), + 23, CLR_ORANGE, NALZOK), + MON(NAM("Scorpius"), S_SPIDER, + LVL(15, 12, 10, 0, -15), (G_NOGEN | G_UNIQ), A(ATTK(AT_CLAW, AD_PHYS, 2, 6), ATTK(AT_CLAW, AD_SAMU, 2, 6), ATTK(AT_STNG, AD_DISE, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(750, 350, MS_NEMESIS, MZ_HUMAN), MR_POISON | MR_STONE, MR_POISON, M1_ANIMAL | M1_NOHANDS | M1_OVIPAROUS | M1_POIS | M1_CARNIVORE, M2_NOPOLY | M2_MALE | M2_PNAME | M2_HOSTILE | M2_STRONG | M2_STALK | M2_NASTY | M2_COLLECT | M2_MAGIC, - M3_WANTSARTI | M3_WAITFORU, 17, HI_LORD, SCORPIUS), - MON("Master Assassin", S_HUMAN, LVL(15, 12, 0, 30, 18), - (G_NOGEN | G_UNIQ), + M3_WANTSARTI | M3_WAITFORU, + 17, HI_LORD, SCORPIUS), + MON(NAM("Master Assassin"), S_HUMAN, + LVL(15, 12, 0, 30, 18), (G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_DRST, 2, 6), ATTK(AT_WEAP, AD_PHYS, 2, 8), ATTK(AT_CLAW, AD_SAMU, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_NEMESIS, MZ_HUMAN), MR_POISON | MR_STONE, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_MALE | M2_HOSTILE | M2_STALK | M2_NASTY | M2_COLLECT | M2_MAGIC, - M3_WANTSARTI | M3_WAITFORU | M3_INFRAVISIBLE, 20, HI_LORD, - MASTER_ASSASSIN), + M3_WANTSARTI | M3_WAITFORU | M3_INFRAVISIBLE, + 20, HI_LORD, MASTER_ASSASSIN), /* A renegade daimyo who led a 13 year civil war against the shogun * of his time. */ - MON("Ashikaga Takauji", S_HUMAN, LVL(15, 12, 0, 40, -13), - (G_NOGEN | G_UNIQ | G_NOCORPSE), + MON(NAM("Ashikaga Takauji"), S_HUMAN, + LVL(15, 12, 0, 40, -13), (G_NOGEN | G_UNIQ | G_NOCORPSE), A(ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_CLAW, AD_SAMU, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_NEMESIS, MZ_HUMAN), MR_STONE, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PNAME | M2_HOSTILE | M2_STRONG | M2_STALK | M2_NASTY | M2_MALE | M2_COLLECT | M2_MAGIC, - M3_WANTSARTI | M3_WAITFORU | M3_INFRAVISIBLE, 19, HI_LORD, - ASHIKAGA_TAKAUJI), + M3_WANTSARTI | M3_WAITFORU | M3_INFRAVISIBLE, + 19, HI_LORD, ASHIKAGA_TAKAUJI), /* * Note: the Master of Thieves was defined above. */ - MON("Lord Surtur", S_GIANT, LVL(15, 12, 2, 50, 12), (G_NOGEN | G_UNIQ), + MON(NAM("Lord Surtur"), S_GIANT, + LVL(15, 12, 2, 50, 12), (G_NOGEN | G_UNIQ), A(ATTK(AT_WEAP, AD_PHYS, 2, 10), ATTK(AT_WEAP, AD_PHYS, 2, 10), ATTK(AT_CLAW, AD_SAMU, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(2250, 850, MS_NEMESIS, MZ_HUGE), MR_FIRE | MR_STONE, MR_FIRE, @@ -3254,137 +3801,171 @@ | M2_STRONG | M2_NASTY | M2_ROCKTHROW | M2_JEWELS | M2_COLLECT, M3_WANTSARTI | M3_WAITFORU | M3_INFRAVISION | M3_INFRAVISIBLE, 19, HI_LORD, LORD_SURTUR), - MON("Anaraxis the Black", S_HUMAN, LVL(15, 12, 0, 80, -10), - (G_NOGEN | G_UNIQ | G_NOCORPSE), + MON(NAM("Anaraxis the Black"), S_HUMAN, + LVL(15, 12, 0, 80, -10), (G_NOGEN | G_UNIQ | G_NOCORPSE), A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), - ATTK(AT_CLAW, AD_SAMU, 1, 4), ATTK(AT_MAGC, AD_SPEL, 0, 0), NO_ATTK, - NO_ATTK), + ATTK(AT_CLAW, AD_SAMU, 1, 4), ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_NEMESIS, MZ_HUMAN), MR_STONE, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_STRONG | M2_PNAME | M2_HOSTILE | M2_STALK | M2_NASTY | M2_COLLECT | M2_MAGIC, - M3_WANTSARTI | M3_WAITFORU | M3_INFRAVISIBLE, 20, CLR_BLACK, - ANARAXIS_THE_BLACK), + M3_WANTSARTI | M3_WAITFORU | M3_INFRAVISIBLE, + 20, CLR_BLACK, ANARAXIS_THE_BLACK), /* * quest "guardians" */ - MON("student", S_HUMAN, LVL(5, 12, 10, 10, 3), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("student"), S_HUMAN, + LVL(5, 12, 10, 10, 3), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_GUARDIAN, MZ_HUMAN), 0, 0, M1_TUNNEL | M1_NEEDPICK | M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_COLLECT, - M3_INFRAVISIBLE, 7, HI_DOMESTIC, STUDENT), - MON("chieftain", S_HUMAN, LVL(5, 12, 10, 10, 0), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE, + 7, HI_DOMESTIC, STUDENT), + MON(NAM("chieftain"), S_HUMAN, + LVL(5, 12, 10, 10, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_GUARDIAN, MZ_HUMAN), MR_POISON, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_COLLECT, - M3_INFRAVISIBLE, 7, HI_DOMESTIC, CHIEFTAIN), - MON("neanderthal", S_HUMAN, LVL(5, 12, 10, 10, 1), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE, + 7, HI_DOMESTIC, CHIEFTAIN), + MON(NAM("neanderthal"), S_HUMAN, + LVL(5, 12, 10, 10, 1), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_GUARDIAN, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_COLLECT, - M3_INFRAVISIBLE, 7, HI_DOMESTIC, NEANDERTHAL), + M3_INFRAVISIBLE, + 7, HI_DOMESTIC, NEANDERTHAL), #if 0 /* OBSOLETE -- guardian for 3.1.x/3.2.x elf quest when elf was a role */ - MON("High-elf", S_HUMAN, + MON(NAM("High-elf"), S_HUMAN, LVL(5, 12, 10, 10, -7), G_NOGEN, A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_MAGC, AD_CLRC, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_ELF, 350, MS_GUARDIAN, MZ_HUMAN), MR_SLEEP, MR_SLEEP, M1_HUMANOID | M1_SEE_INVIS | M1_OMNIVORE, M2_NOPOLY | M2_ELF | M2_PEACEFUL | M2_COLLECT, - M3_INFRAVISION | M3_INFRAVISIBLE, 7, HI_DOMESTIC, HIGH_ELF), + M3_INFRAVISION | M3_INFRAVISIBLE, + 7, HI_DOMESTIC, HIGH_ELF), #endif - /* attendants used to lawful but have been changed to netural because + /* attendants used to be lawful but have been changed to neutral because grow_up() promotes them to healer and the latter is always neutral */ - MON("attendant", S_HUMAN, LVL(5, 12, 10, 10, 0), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + MON(NAM("attendant"), S_HUMAN, + LVL(5, 12, 10, 10, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_GUARDIAN, MZ_HUMAN), MR_POISON, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_COLLECT, - M3_INFRAVISIBLE, 7, HI_DOMESTIC, ATTENDANT), - MON("page", S_HUMAN, LVL(5, 12, 10, 10, 3), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISIBLE, + 7, HI_DOMESTIC, ATTENDANT), + MON(NAM("page"), S_HUMAN, + LVL(5, 12, 10, 10, 3), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_GUARDIAN, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_COLLECT, - M3_INFRAVISIBLE, 7, HI_DOMESTIC, PAGE), - MON("abbot", S_HUMAN, LVL(5, 12, 10, 20, 0), G_NOGEN, + M3_INFRAVISIBLE, + 7, HI_DOMESTIC, PAGE), + MON(NAM("abbot"), S_HUMAN, + LVL(5, 12, 10, 20, 0), G_NOGEN, A(ATTK(AT_CLAW, AD_PHYS, 8, 2), ATTK(AT_KICK, AD_STUN, 3, 2), ATTK(AT_MAGC, AD_CLRC, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_GUARDIAN, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_HERBIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_COLLECT, - M3_INFRAVISIBLE, 8, HI_DOMESTIC, ABBOT), - MON("acolyte", S_HUMAN, LVL(5, 12, 10, 20, 0), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_MAGC, AD_CLRC, 0, 0), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISIBLE, + 8, HI_DOMESTIC, ABBOT), + MON(NAM("acolyte"), S_HUMAN, + LVL(5, 12, 10, 20, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_MAGC, AD_CLRC, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_GUARDIAN, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_COLLECT, - M3_INFRAVISIBLE, 8, HI_DOMESTIC, ACOLYTE), - MON("hunter", S_HUMAN, LVL(5, 12, 10, 10, -7), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, - NO_ATTK), + M3_INFRAVISIBLE, + 8, HI_DOMESTIC, ACOLYTE), + MON(NAM("hunter"), S_HUMAN, + LVL(5, 12, 10, 10, -7), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_GUARDIAN, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_SEE_INVIS | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_COLLECT, - M3_INFRAVISION | M3_INFRAVISIBLE, 7, HI_DOMESTIC, HUNTER), - MON("thug", S_HUMAN, LVL(5, 12, 10, 10, -3), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISION | M3_INFRAVISIBLE, + 7, HI_DOMESTIC, HUNTER), + MON(NAM("thug"), S_HUMAN, + LVL(5, 12, 10, 10, -3), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_GUARDIAN, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_GREEDY | M2_COLLECT, - M3_INFRAVISIBLE, 7, HI_DOMESTIC, THUG), - MON("ninja", S_HUMAN, LVL(5, 12, 10, 10, 3), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISIBLE, + 7, HI_DOMESTIC, THUG), + MON(NAM("ninja"), S_HUMAN, + LVL(5, 12, 10, 10, 3), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_HUMANOID, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_HOSTILE | M2_STRONG | M2_COLLECT, - M3_INFRAVISIBLE, 7, HI_DOMESTIC, NINJA), - MON("roshi", S_HUMAN, LVL(5, 12, 10, 10, 3), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISIBLE, + 7, HI_DOMESTIC, NINJA), + MON(NAM("roshi"), S_HUMAN, + LVL(5, 12, 10, 10, 3), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_GUARDIAN, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_COLLECT, - M3_INFRAVISIBLE, 7, HI_DOMESTIC, ROSHI), - MON("guide", S_HUMAN, LVL(5, 12, 10, 20, 0), G_NOGEN, + M3_INFRAVISIBLE, + 7, HI_DOMESTIC, ROSHI), + MON(NAM("guide"), S_HUMAN, + LVL(5, 12, 10, 20, 0), G_NOGEN, A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_MAGC, AD_SPEL, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_GUARDIAN, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_COLLECT | M2_MAGIC, - M3_INFRAVISIBLE, 8, HI_DOMESTIC, GUIDE), + M3_INFRAVISIBLE, + 8, HI_DOMESTIC, GUIDE), /* warriors used to be chaotic but have been changed to lawful because grow_up() promotes them to valkyrie; for a valkyrie hero, they might be changed to neutral at game start; see the valkyrie comment above */ - MON("warrior", S_HUMAN, LVL(5, 12, 10, 10, 1), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + MON(NAM("warrior"), S_HUMAN, + LVL(5, 12, 10, 10, 1), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_GUARDIAN, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_COLLECT | M2_FEMALE, - M3_INFRAVISIBLE, 7, HI_DOMESTIC, WARRIOR), - MON("apprentice", S_HUMAN, LVL(5, 12, 10, 30, 0), G_NOGEN, - A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_MAGC, AD_SPEL, 0, 0), NO_ATTK, - NO_ATTK, NO_ATTK, NO_ATTK), + M3_INFRAVISIBLE, + 7, HI_DOMESTIC, WARRIOR), + MON(NAM("apprentice"), S_HUMAN, + LVL(5, 12, 10, 30, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_GUARDIAN, MZ_HUMAN), 0, 0, M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_COLLECT | M2_MAGIC, - M3_INFRAVISIBLE, 8, HI_DOMESTIC, APPRENTICE), + M3_INFRAVISIBLE, + 8, HI_DOMESTIC, APPRENTICE), + + /* + * mons_init() in monst.c adds a terminator here, mons[NUMMONS]. + * It is part of the mons[] array without introducing another type + * of monster. + */ #if defined(MONS_ENUM) || defined(DUMP_ENUMS) #undef MON -#undef MON3 #endif /*monsters.h*/ diff --git a/include/nhmd4.h b/include/nhmd4.h new file mode 100644 index 0000000000..f235a1bb77 --- /dev/null +++ b/include/nhmd4.h @@ -0,0 +1,37 @@ +/* NetHack 3.7 nhmd4.h $NHDT-Date: 1708811386 2024/02/24 21:49:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.0 $ */ +/*- Copyright (c) Kenneth Lorber, Kensington, Maryland, 2024 */ +/* NetHack may be freely redistributed. See license for details. */ + +// Derived from: +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, + * Inc. MD4 Message-Digest Algorithm. + * + * Written by Solar Designer in 2001, and placed in + * the public domain. See md4.c for more information. + */ + +#ifndef NHMD4_H +#define NHMD4_H + +#define NHMD4_DIGEST_LENGTH 128 +#define NHMD4_RESULTLEN (128 / 8) /* 16 */ + +typedef uint32_t quint32; + +struct nhmd4_context { + quint32 lo, hi; + quint32 a, b, c, d; + unsigned char buffer[64]; + quint32 block[NHMD4_RESULTLEN]; +}; +typedef struct nhmd4_context NHMD4_CTX; + +extern void nhmd4_init(NHMD4_CTX *ctx); +extern void nhmd4_update(NHMD4_CTX *, const unsigned char *, size_t); +extern void nhmd4_final(NHMD4_CTX *, unsigned char result[NHMD4_RESULTLEN]); + +#endif /* NHMD4_H */ + +/*nhmd4.h*/ + diff --git a/include/nhregex.h b/include/nhregex.h new file mode 100644 index 0000000000..6dc989213b --- /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 0958d10bbd..4fe19abd75 100644 --- a/include/obj.h +++ b/include/obj.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 obj.h $NHDT-Date: 1633802062 2021/10/09 17:54:22 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.94 $ */ +/* NetHack 3.7 obj.h $NHDT-Date: 1718999845 2024/06/21 19:57:25 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.116 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -80,11 +80,15 @@ struct obj { #define OBJ_MIGRATING 5 /* object sent off to another level */ #define OBJ_BURIED 6 /* object buried */ #define OBJ_ONBILL 7 /* object on shk bill */ -#define OBJ_INTRAP 8 /* object is trap ammo */ -#define OBJ_LUAFREE 9 /* object has been dealloc'd, but is ref'd by lua */ -#define NOBJ_STATES 10 +#define OBJ_LUAFREE 8 /* object has been dealloc'd, but is ref'd by lua */ +#define OBJ_DELETED 9 /* object is marked for deletion by dobjsfree() */ +#define OBJ_INTRAP 10 /* object is trap ammo */ + /* note: OBJ_xxx values are used in obj_state_names[] in mkobj.c + so adding, removing, or renumbering these needs to change that too */ +#define NOBJ_STATES 11 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 @@ -96,6 +100,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 */ @@ -103,6 +111,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 +139,24 @@ struct obj { #define opoisoned otrapped /* object (weapon) is coated with poison */ #define zombie_corpse otrapped /* corpse is a zombie that might revive */ - 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(was_thrown, 1); /* thrown by hero since last picked up */ - - Bitfield(material, 5); /* material this obj is made of */ + Bitfield(greased, 1); /* covered with grease */ 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() */ + Bitfield(material, 5); /* material this obj is made of */ #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] */ - /* 4 free bits */ + /* 0 free bits */ #else - /* 6 free bits */ + /* 1 free bit */ #endif int corpsenm; /* type of corpse is mons[corpsenm] */ @@ -165,6 +172,7 @@ struct obj { int usecount; /* overloaded for various things that tally */ #define spestudied usecount /* # of times a spellbook has been studied */ #define tinseed usecount /* seed for tin labels */ +#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 @@ -274,7 +282,7 @@ struct obj { /* 'missile' aspect is up to the caller and does not imply is_missile(); rings might be launched as missiles when being scattered by an explosion */ #define stone_missile(o) \ - ((o) && (objects[(o)->otyp].oc_material == GEMSTONE \ + ((objects[(o)->otyp].oc_material == GEMSTONE \ || (objects[(o)->otyp].oc_material == MINERAL)) \ && (o)->oclass != RING_CLASS) @@ -316,14 +324,14 @@ struct obj { /* Eggs and other food */ #define MAX_EGG_HATCH_TIME 200 /* longest an egg can remain unhatched */ #define stale_egg(egg) \ - ((gm.moves - (egg)->age) > (2 * MAX_EGG_HATCH_TIME)) + ((svm.moves - (egg)->age) > (2 * MAX_EGG_HATCH_TIME)) #define ofood(o) ((o)->otyp == CORPSE || (o)->otyp == EGG || (o)->otyp == TIN) /* note: sometimes eggs and tins have special corpsenm values that shouldn't be used as an index into mons[] */ -#define polyfodder(obj) \ +#define polyfood(obj) \ (ofood(obj) && (obj)->corpsenm >= LOW_PM \ && (pm_to_cham((obj)->corpsenm) != NON_PM \ - || dmgtype(&mons[(obj)->corpsenm], AD_POLY))) + || dmgtype(&mons[(obj)->corpsenm], AD_POLY))) #define mlevelgain(obj) (ofood(obj) && (obj)->corpsenm == PM_WRAITH) #define mhealup(obj) (ofood(obj) && (obj)->corpsenm == PM_NURSE) #define Is_pudding(o) \ @@ -460,13 +468,16 @@ struct obj { #define unpolyable(o) ((o)->otyp == WAN_POLYMORPH \ || (o)->otyp == SPE_POLYMORPH \ - || (o)->otyp == POT_POLYMORPH) + || (o)->otyp == POT_POLYMORPH \ + || (o)->otyp == AMULET_OF_UNCHANGING) /* achievement tracking; 3.6.x did this differently */ -#define is_mines_prize(o) ((o)->o_id == gc.context.achieveo.mines_prize_oid) -#define is_soko_prize(o) ((o)->o_id == gc.context.achieveo.soko_prize_oid) +#define is_mines_prize(o) ((o)->o_id == svc.context.achieveo.mines_prize_oid) +#define is_soko_prize(o) ((o)->o_id == svc.context.achieveo.soko_prize_oid) + +/* is_art() is now a function in artifact.c */ +/* #define is_art(o,art) ((o) && (o)->oartifact == (art)) */ -#define is_art(o,art) ((o) && (o)->oartifact == (art)) #define u_wield_art(art) is_art(uwep, art) /* mummy wrappings are more versatile sizewise than other cloaks */ @@ -484,6 +495,7 @@ struct obj { #define ERODE_RUST 1 #define ERODE_ROT 2 #define ERODE_CORRODE 3 +#define ERODE_CRACK 4 /* crystal armor */ /* erosion flags for erode_obj() */ #define EF_NONE 0 @@ -504,6 +516,18 @@ 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 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 */ + /* special thiefstone coordinate accessors (uses corpsenm) */ #define set_keyed_loc(stone, xx, yy) \ ((stone)->corpsenm = ((int)(xx) << 8) | ((int)(yy) & 0xFF)) diff --git a/include/objclass.h b/include/objclass.h index 6fb6f87252..fd2935c971 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 */ @@ -67,7 +68,7 @@ struct objclass { Bitfield(oc_tough, 1); /* hard gems/rings */ Bitfield(oc_spare1, 6); /* padding to align oc_dir + oc_material; - * can be canabalized for other use; + * can be cannibalized for other use; * aka 6 free bits */ Bitfield(oc_dir, 3); @@ -203,15 +204,18 @@ extern NEARDATA struct objdescr obj_descr[NUM_OBJECTS + 1]; /* primary damage: fire/rust/--- */ /* is_flammable(otmp), is_rottable(otmp) in mkobj.c */ #define is_rustprone(otmp) ((otmp)->material == IRON) - +#define is_crackable(otmp) \ + ((otmp)->material == GLASS \ + && (otmp)->oclass == ARMOR_CLASS) /* erosion_matters() */ /* secondary damage: rot/acid/acid */ #define is_corrodeable(otmp) \ ((otmp)->material == COPPER || (otmp)->material == SILVER \ || (otmp)->material == IRON) - -#define is_damageable(otmp) \ - (is_rustprone(otmp) || is_flammable(otmp) || is_rottable(otmp) \ - || is_corrodeable(otmp)) +/* subject to any damage */ +#define is_damageable(otmp) \ + (is_rustprone(otmp) || is_flammable(otmp) \ + || is_rottable(otmp) || is_corrodeable(otmp) \ + || is_crackable(otmp)) /* Force rendering of materials on certain items where the object name * wouldn't make as much sense without a material (e.g. "leather jacket" vs diff --git a/include/objects.h b/include/objects.h index 256a0d8244..b6d1c5d126 100644 --- a/include/objects.h +++ b/include/objects.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 objects.h $NHDT-Date: 1596498192 2020/08/03 23:43:12 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.66 $ */ +/* NetHack 3.7 objects.h $NHDT-Date: 1725653011 2024/09/06 20:03:31 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.26 $ */ /* Copyright (c) Mike Threepoint, 1989. */ /* NetHack may be freely redistributed. See license for details. */ @@ -35,7 +35,6 @@ #define MARKER(tag,sn) /*empty*/ #elif defined(OBJECTS_INIT) -#define COLOR_FIELD(X) X, /* notes: 'sub' was once a bitfield but got changed to separate schar when it was overloaded to hold negative weapon skill indices; the first zero is padding for oc_prediscovered which has variable init at run-time; @@ -44,7 +43,7 @@ nmkn,mrg,uskn,0,mgc,chrg,uniq,nwsh,big,tuf,0,dir,mtrl,sub /*cpp fodder*/ #define OBJECT(obj,bits,prp,sym,prob,dly,wt, \ cost,sdam,ldam,oc1,oc2,nut,color,sn) \ - { 0, 0, (char *) 0, bits, prp, sym, dly, COLOR_FIELD(color) prob, wt, \ + { 0, 0, (char *) 0, bits, prp, sym, dly, color, prob, wt, \ cost, sdam, ldam, oc1, oc2, nut } #define MARKER(tag,sn) /*empty*/ @@ -98,6 +97,14 @@ GENERIC("iron ball", BALL_CLASS, GENERIC_BALL), /* [15] */ GENERIC("iron chain", CHAIN_CLASS, GENERIC_CHAIN), /* [16] */ GENERIC("venom", VENOM_CLASS, GENERIC_VENOM), /* [17] */ #undef GENERIC +/* FIRST_OBJECT: it would be simpler just to use MARKER(FIRST_OBJECT,ARROW) + below but that is vulnerable to neglecting to update the marker enum + after inserting something in front of arrow */ +MARKER(LAST_GENERIC, GENERIC_VENOM) +MARKER(FIRST_OBJECT, LAST_GENERIC + 1) +/* this definition of FIRST_OBJECT advances the default value for next enum; + backtrack to fix that, otherwise ARROW and the rest would be off by 1 */ +MARKER(OBJCLASS_HACK, FIRST_OBJECT - 1) /* weapons ... */ #define WEAPON(name,desc,kn,mg,bi,prob,wt, \ @@ -371,8 +378,8 @@ BOW("crossbow", NoDes, 1, 45, 50, 40, 0, WOOD, P_CROSSBOW, HI_WOOD, #define CLOAK(name,desc,kn,mgc,power,prob,delay,wt,cost,ac,can,metal,c,sn) \ ARMOR(name, desc, kn, mgc, 0, power, prob, delay, wt, \ cost, ac, can, ARM_CLOAK, metal, c,sn) -#define SHIELD(name,desc,kn,mgc,blk,power,prob,delay,wt,cost,ac,can,metal,c,sn) \ - ARMOR(name, desc, kn, mgc, blk, power, prob, delay, wt, \ +#define SHIELD(name,desc,kn,mgc,blk,pow,prob,delay,wt,cost,ac,can,metal,c,sn) \ + ARMOR(name, desc, kn, mgc, blk, pow, prob, delay, wt, \ cost, ac, can, ARM_SHIELD, metal, c,sn) #define GLOVES(name,desc,kn,mgc,power,prob,delay,wt,cost,ac,can,metal,c,sn) \ ARMOR(name, desc, kn, mgc, 0, power, prob, delay, wt, \ @@ -467,7 +474,7 @@ ARMOR("plate mail", NoDes, 1, 0, 1, 0, 44, 5, 450, 600, 3, 2, ARM_SUIT, IRON, HI_METAL, PLATE_MAIL), ARMOR("crystal plate mail", NoDes, - 1, 0, 1, 0, 10, 5, 450, 820, 3, 2, ARM_SUIT, GLASS, CLR_WHITE, + 1, 0, 1, 0, 10, 5, 415, 820, 3, 2, ARM_SUIT, GLASS, CLR_WHITE, CRYSTAL_PLATE_MAIL), ARMOR("splint mail", NoDes, 1, 0, 1, 0, 62, 5, 400, 80, 4, 1, ARM_SUIT, IRON, HI_METAL, @@ -513,34 +520,41 @@ ARMOR("T-shirt", NoDes, /* cloaks */ CLOAK("mummy wrapping", NoDes, - 1, 0, 0, 0, 0, 3, 2, 10, 1, CLOTH, CLR_GRAY, MUMMY_WRAPPING), + 1, 0, 0, 0, 0, 3, 2, 10, 1, CLOTH, CLR_GRAY, + MUMMY_WRAPPING), /* worn mummy wrapping blocks invisibility */ CLOAK("elven cloak", "faded pall", 0, 1, STEALTH, 8, 0, 10, 60, 9, 1, CLOTH, CLR_BLACK, ELVEN_CLOAK), CLOAK("orcish cloak", "coarse mantelet", - 0, 0, 0, 8, 0, 10, 40, 10, 1, CLOTH, CLR_BLACK, ORCISH_CLOAK), + 0, 0, 0, 8, 0, 10, 40, 10, 1, CLOTH, CLR_BLACK, + ORCISH_CLOAK), CLOAK("dwarvish cloak", "hooded cloak", - 0, 0, 0, 8, 0, 10, 50, 10, 1, CLOTH, HI_CLOTH, DWARVISH_CLOAK), + 0, 0, 0, 8, 0, 10, 50, 10, 1, CLOTH, HI_CLOTH, + DWARVISH_CLOAK), CLOAK("oilskin cloak", "slippery cloak", - 0, 0, 0, 8, 0, 10, 50, 9, 2, CLOTH, HI_CLOTH, OILSKIN_CLOAK), + 0, 0, 0, 8, 0, 10, 50, 9, 2, CLOTH, HI_CLOTH, + OILSKIN_CLOAK), CLOAK("robe", NoDes, 1, 1, 0, 3, 0, 15, 50, 8, 2, CLOTH, CLR_RED, ROBE), /* robe was adopted from slash'em, where it's worn as a suit rather than as a cloak and there are several variations */ CLOAK("alchemy smock", "apron", - 0, 1, POISON_RES, 9, 0, 10, 50, 9, 1, CLOTH, CLR_WHITE, ALCHEMY_SMOCK), + 0, 1, POISON_RES, 9, 0, 10, 50, 9, 1, CLOTH, CLR_WHITE, + ALCHEMY_SMOCK), CLOAK("plain cloak", NoDes, - 1, 0, 0, 8, 0, 15, 40, 9, 1, LEATHER, CLR_BROWN, PLAIN_CLOAK), + 1, 0, 0, 8, 0, 15, 40, 9, 1, LEATHER, CLR_BROWN, + PLAIN_CLOAK), /* with shuffled appearances... */ CLOAK("cloak of protection", "tattered cape", - 0, 1, PROTECTION, 9, 0, 10, 50, 7, 3, CLOTH, HI_CLOTH, CLOAK_OF_PROTECTION), + 0, 1, PROTECTION, 9, 0, 10, 50, 7, 3, CLOTH, HI_CLOTH, + CLOAK_OF_PROTECTION), /* cloak of protection is now the only item conferring MC 3 */ CLOAK("cloak of invisibility", "opera cloak", 0, 1, INVIS, 10, 0, 10, 60, 9, 1, CLOTH, CLR_BRIGHT_MAGENTA, CLOAK_OF_INVISIBILITY), CLOAK("cloak of magic resistance", "ornamental cope", 0, 1, ANTIMAGIC, 2, 0, 10, 60, 9, 1, CLOTH, CLR_WHITE, - CLOAK_OF_MAGIC_RESISTANCE), + CLOAK_OF_MAGIC_RESISTANCE), /* 'cope' is not a spelling mistake... leave it be */ CLOAK("cloak of displacement", "dusty cloak", 0, 1, DISPLACED, 10, 0, 10, 50, 9, 1, CLOTH, HI_CLOTH, @@ -733,9 +747,9 @@ MARKER(FIRST_AMULET, AMULET_OF_ESP) AMULET("amulet of life saving", "spherical", LIFESAVED, 75, AMULET_OF_LIFE_SAVING), AMULET("amulet of strangulation", "oval", STRANGLED, 115, - AMULET_OF_STRANGULATION), + AMULET_OF_STRANGULATION), AMULET("amulet of restful sleep", "triangular", SLEEPY, 115, - AMULET_OF_RESTFUL_SLEEP), + AMULET_OF_RESTFUL_SLEEP), AMULET("amulet versus poison", "pyramidal", POISON_RES, 115, AMULET_VERSUS_POISON), AMULET("amulet of change", "square", 0, 115, @@ -896,7 +910,7 @@ TOOL("bugle", NoDes, 1, 0, 0, 0, 4, 10, 15, COPPER, HI_COPPER, TOOL("leather drum", "drum", 0, 0, 0, 0, 4, 25, 25, LEATHER, HI_LEATHER, LEATHER_DRUM), TOOL("drum of earthquake","drum", 0, 0, 1, 1, 2, 25, 100, LEATHER, HI_LEATHER, - DRUM_OF_EARTHQUAKE), + DRUM_OF_EARTHQUAKE), /* tools useful as weapons */ WEPTOOL("pick-axe", NoDes, 1, 0, 0, 20, 100, 50, 6, 3, WHACK, P_PICK_AXE, IRON, HI_METAL, @@ -915,22 +929,22 @@ WEPTOOL("unicorn horn", NoDes, OBJECT(OBJ("Candelabrum of Invocation", "candelabrum"), BITS(0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, P_NONE, GOLD), 0, TOOL_CLASS, 0, 0, 10, 5000, 0, 0, 0, 0, 200, HI_GOLD, - CANDELABRUM_OF_INVOCATION), + CANDELABRUM_OF_INVOCATION), OBJECT(OBJ("Bell of Opening", "engraved silver bell"), BITS(0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, P_NONE, SILVER), 0, TOOL_CLASS, 0, 0, 10, 5000, 0, 0, 0, 0, 50, HI_SILVER, - BELL_OF_OPENING), + BELL_OF_OPENING), #undef TOOL #undef WEPTOOL /* Comestibles ... */ -#define FOOD(name, prob, delay, wt, unk, tin, nutrition, color, sn) \ - OBJECT(OBJ(name, NoDes), \ +#define FOOD(name, prob, delay, wt, unk, tin, nutrition, color, sn) \ + OBJECT(OBJ(name, NoDes), \ BITS(1, 1, unk, 0, 0, 0, 0, 0, 0, 0, 0, P_NONE, tin), 0, \ FOOD_CLASS, prob, delay, wt, nutrition / 20 + 5, 0, 0, 0, 0, \ nutrition, color, sn) /* All types of food (except tins & corpses) must have a delay of at least 1. - * Delay on corpses is computed and is weight dependant. + * Delay on corpses is computed and is weight dependent. * Domestic pets prefer tripe rations above all others. * Fortune cookies can be read, using them up without ingesting them. * Carrots improve your vision. @@ -952,45 +966,48 @@ FOOD("meat stick", 0, 1, 1, 0, FLESH, 5, CLR_BROWN, /* formerly "huge chunk of meat" */ FOOD("enormous meatball", 0, 20,400, 0, FLESH,2000, CLR_BROWN, ENORMOUS_MEATBALL), -/* special case because it's not mergable */ +/* special case because it's not mergeable */ OBJECT(OBJ("meat ring", NoDes), BITS(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, FLESH), 0, FOOD_CLASS, 0, 1, 5, 1, 0, 0, 0, 0, 5, CLR_BROWN, MEAT_RING), /* pudding 'corpses' will turn into these and combine; must be in same order as the pudding monsters */ FOOD("glob of gray ooze", 0, 2, 20, 0, FLESH, 20, CLR_GRAY, - GLOB_OF_GRAY_OOZE), + GLOB_OF_GRAY_OOZE), FOOD("glob of brown pudding", 0, 2, 20, 0, FLESH, 20, CLR_BROWN, - GLOB_OF_BROWN_PUDDING), + GLOB_OF_BROWN_PUDDING), FOOD("glob of green slime", 0, 2, 20, 0, FLESH, 20, CLR_GREEN, - GLOB_OF_GREEN_SLIME), + GLOB_OF_GREEN_SLIME), FOOD("glob of black pudding", 0, 2, 20, 0, FLESH, 20, CLR_BLACK, - GLOB_OF_BLACK_PUDDING), + GLOB_OF_BLACK_PUDDING), /* fruits & veggies */ FOOD("kelp frond", 0, 1, 1, 0, VEGGY, 30, CLR_GREEN, KELP_FROND), FOOD("eucalyptus leaf", 3, 1, 1, 0, VEGGY, 1, CLR_GREEN, - EUCALYPTUS_LEAF), + EUCALYPTUS_LEAF), FOOD("apple", 15, 1, 2, 0, VEGGY, 50, CLR_RED, APPLE), FOOD("orange", 10, 1, 2, 0, VEGGY, 80, CLR_ORANGE, ORANGE), FOOD("pear", 10, 1, 2, 0, VEGGY, 50, CLR_BRIGHT_GREEN, - PEAR), + PEAR), FOOD("melon", 10, 1, 5, 0, VEGGY, 100, CLR_BRIGHT_GREEN, - MELON), + MELON), FOOD("banana", 10, 1, 2, 0, VEGGY, 80, CLR_YELLOW, BANANA), FOOD("carrot", 15, 1, 2, 0, VEGGY, 50, CLR_ORANGE, CARROT), FOOD("sprig of wolfsbane", 7, 1, 1, 0, VEGGY, 40, CLR_GREEN, SPRIG_OF_WOLFSBANE), -FOOD("clove of garlic", 7, 1, 1, 0, VEGGY, 40, CLR_WHITE, CLOVE_OF_GARLIC), +FOOD("clove of garlic", 7, 1, 1, 0, VEGGY, 40, CLR_WHITE, + CLOVE_OF_GARLIC), /* name of slime mold is changed based on player's OPTION=fruit:something and bones data might have differently named ones from prior games */ -FOOD("slime mold", 75, 1, 5, 0, VEGGY, 250, HI_ORGANIC, SLIME_MOLD), +FOOD("slime mold", 75, 1, 5, 0, VEGGY, 250, HI_ORGANIC, + SLIME_MOLD), /* people food */ FOOD("lump of royal jelly", 0, 1, 2, 0, VEGGY, 200, CLR_MAGENTA, LUMP_OF_ROYAL_JELLY), FOOD("cream pie", 25, 1, 10, 0, VEGGY, 100, CLR_WHITE, CREAM_PIE), -FOOD("candy bar", 13, 1, 2, 0, VEGGY, 100, CLR_BRIGHT_BLUE, CANDY_BAR), +FOOD("candy bar", 13, 1, 2, 0, VEGGY, 100, CLR_BRIGHT_BLUE, + CANDY_BAR), FOOD("fortune cookie", 55, 1, 1, 0, VEGGY, 40, CLR_YELLOW, FORTUNE_COOKIE), FOOD("pancake", 25, 2, 2, 0, VEGGY, 200, CLR_YELLOW, PANCAKE), @@ -1013,29 +1030,29 @@ FOOD("tin", 75, 0, 10, 1, METAL, 0, HI_METAL, TIN), OBJECT(OBJ(name, desc), \ BITS(0, 1, 0, 0, mgc, 0, 0, 0, 0, 0, 0, P_NONE, GLASS), \ power, POTION_CLASS, prob, 0, 20, cost, 0, 0, 0, 0, 10, color, sn) -POTION("gain ability", "ruby", 1, 0, 42, 300, CLR_RED, +POTION("gain ability", "ruby", 1, 0, 40, 300, CLR_RED, POT_GAIN_ABILITY), POTION("restore ability", "pink", 1, 0, 40, 100, CLR_BRIGHT_MAGENTA, POT_RESTORE_ABILITY), -POTION("confusion", "orange", 1, CONFUSION, 42, 100, CLR_ORANGE, +POTION("confusion", "orange", 1, CONFUSION, 40, 100, CLR_ORANGE, POT_CONFUSION), -POTION("blindness", "yellow", 1, BLINDED, 40, 150, CLR_YELLOW, +POTION("blindness", "yellow", 1, BLINDED, 30, 150, CLR_YELLOW, POT_BLINDNESS), -POTION("paralysis", "emerald", 1, 0, 42, 300, CLR_BRIGHT_GREEN, +POTION("paralysis", "emerald", 1, 0, 40, 300, CLR_BRIGHT_GREEN, POT_PARALYSIS), -POTION("speed", "dark green", 1, FAST, 42, 200, CLR_GREEN, +POTION("speed", "dark green", 1, FAST, 40, 200, CLR_GREEN, POT_SPEED), -POTION("levitation", "cyan", 1, LEVITATION, 42, 200, CLR_CYAN, +POTION("levitation", "cyan", 1, LEVITATION, 40, 200, CLR_CYAN, POT_LEVITATION), -POTION("hallucination", "sky blue", 1, HALLUC, 40, 100, CLR_CYAN, +POTION("hallucination", "sky blue", 1, HALLUC, 30, 100, CLR_CYAN, POT_HALLUCINATION), POTION("invisibility", "brilliant blue", 1, INVIS, 40, 150, CLR_BRIGHT_BLUE, POT_INVISIBILITY), -POTION("see invisible", "magenta", 1, SEE_INVIS, 42, 50, CLR_MAGENTA, +POTION("see invisible", "magenta", 1, SEE_INVIS, 40, 50, CLR_MAGENTA, POT_SEE_INVISIBLE), -POTION("healing", "purple-red", 1, 0, 57, 100, CLR_MAGENTA, +POTION("healing", "purple-red", 1, 0, 115, 20, CLR_MAGENTA, POT_HEALING), -POTION("extra healing", "puce", 1, 0, 47, 100, CLR_RED, +POTION("extra healing", "puce", 1, 0, 45, 100, CLR_RED, POT_EXTRA_HEALING), POTION("gain level", "milky", 1, 0, 20, 300, CLR_WHITE, POT_GAIN_LEVEL), @@ -1043,21 +1060,21 @@ POTION("enlightenment", "swirly", 1, 0, 20, 200, CLR_BROWN, POT_ENLIGHTENMENT), POTION("monster detection", "bubbly", 1, 0, 40, 150, CLR_WHITE, POT_MONSTER_DETECTION), -POTION("object detection", "smoky", 1, 0, 42, 150, CLR_GRAY, +POTION("object detection", "smoky", 1, 0, 40, 150, CLR_GRAY, POT_OBJECT_DETECTION), -POTION("gain energy", "cloudy", 1, 0, 42, 150, CLR_WHITE, +POTION("gain energy", "cloudy", 1, 0, 40, 150, CLR_WHITE, POT_GAIN_ENERGY), -POTION("sleeping", "effervescent", 1, 0, 42, 100, CLR_GRAY, +POTION("sleeping", "effervescent", 1, 0, 40, 100, CLR_GRAY, POT_SLEEPING), POTION("full healing", "black", 1, 0, 10, 200, CLR_BLACK, POT_FULL_HEALING), POTION("polymorph", "golden", 1, 0, 10, 200, CLR_YELLOW, POT_POLYMORPH), -POTION("booze", "brown", 0, 0, 42, 50, CLR_BROWN, +POTION("booze", "brown", 0, 0, 40, 50, CLR_BROWN, POT_BOOZE), -POTION("sickness", "fizzy", 0, 0, 42, 50, CLR_CYAN, +POTION("sickness", "fizzy", 0, 0, 40, 50, CLR_CYAN, POT_SICKNESS), -POTION("fruit juice", "dark", 0, 0, 42, 50, CLR_BLACK, +POTION("fruit juice", "dark", 0, 0, 40, 50, CLR_BLACK, POT_FRUIT_JUICE), POTION("acid", "white", 0, 0, 10, 250, CLR_WHITE, POT_ACID), @@ -1065,7 +1082,7 @@ POTION("oil", "murky", 0, 0, 30, 250, CLR_BROWN, POT_OIL), /* fixed description */ -POTION("water", "clear", 0, 0, 92, 100, CLR_CYAN, +POTION("water", "clear", 0, 0, 80, 100, CLR_CYAN, POT_WATER), #undef POTION @@ -1123,30 +1140,33 @@ SCROLL("stinking cloud", "VELOX NEB", 1, 15, 300, * Code in win/share/tilemap.c depends on SCR_STINKING_CLOUD preceding * these and on how many of them there are. If a real scroll gets added * after stinking cloud or the number of extra descriptions changes, - * tilemap.c must be modified to match. + * tilemap.c must be modified to match. Mgc,Prob,Cost are superfluous. + * SC values must be distinct but are only used by 'nethack --dumpenums'. */ -SCROLL(NoDes, "FOOBIE BLETCH", 1, 0, 100, SC01), -SCROLL(NoDes, "TEMOV", 1, 0, 100, SC02), -SCROLL(NoDes, "GARVEN DEH", 1, 0, 100, SC03), -SCROLL(NoDes, "READ ME", 1, 0, 100, SC04), -SCROLL(NoDes, "ETAOIN SHRDLU", 1, 0, 100, SC05), -SCROLL(NoDes, "LOREM IPSUM", 1, 0, 100, SC06), -SCROLL(NoDes, "FNORD", 1, 0, 100, SC07), /* Illuminati */ -SCROLL(NoDes, "KO BATE", 1, 0, 100, SC08), /* Kurd Lasswitz */ -SCROLL(NoDes, "ABRA KA DABRA", 1, 0, 100, SC09), /* traditional incantation */ -SCROLL(NoDes, "ASHPD SODALG", 1, 0, 100, SC10), /* Portal */ -SCROLL(NoDes, "ZLORFIK", 1, 0, 100, SC11), /* Zak McKracken */ -SCROLL(NoDes, "GNIK SISI VLE", 1, 0, 100, SC12), /* Zak McKracken */ -SCROLL(NoDes, "HAPAX LEGOMENON", 1, 0, 100, SC13), -SCROLL(NoDes, "EIRIS SAZUN IDISI", 1, 0, 100, SC14), /* Merseburg Incantations */ -SCROLL(NoDes, "PHOL ENDE WODAN", 1, 0, 100, SC15), /* Merseburg Incantations */ -SCROLL(NoDes, "GHOTI", 1, 0, 100, SC16), /* pronounced as 'fish', - George Bernard Shaw */ -SCROLL(NoDes, "MAPIRO MAHAMA DIROMAT", 1, 0, 100, SC17), /* Wizardry */ -SCROLL(NoDes, "VAS CORP BET MANI", 1, 0, 100, SC18), /* Ultima */ -SCROLL(NoDes, "XOR OTA", 1, 0, 100, SC19), /* Aarne Haapakoski */ -SCROLL(NoDes, "STRC PRST SKRZ KRK", 1, 0, 100, SC20), /* Czech and Slovak - tongue-twister */ +#define XTRA_SCROLL_LABEL(text, sn) SCROLL(NoDes, text, 1, 0, 100, sn) +XTRA_SCROLL_LABEL( "FOOBIE BLETCH", SC01), +XTRA_SCROLL_LABEL( "TEMOV", SC02), +XTRA_SCROLL_LABEL( "GARVEN DEH", SC03), +XTRA_SCROLL_LABEL( "READ ME", SC04), +XTRA_SCROLL_LABEL( "ETAOIN SHRDLU", SC05), +XTRA_SCROLL_LABEL( "LOREM IPSUM", SC06), +XTRA_SCROLL_LABEL( "FNORD", SC07), /* Illuminati */ +XTRA_SCROLL_LABEL( "KO BATE", SC08), /* Kurd Lasswitz */ +XTRA_SCROLL_LABEL( "ABRA KA DABRA", SC09), /* traditional incantation */ +XTRA_SCROLL_LABEL( "ASHPD SODALG", SC10), /* Portal */ +XTRA_SCROLL_LABEL( "ZLORFIK", SC11), /* Zak McKracken */ +XTRA_SCROLL_LABEL( "GNIK SISI VLE", SC12), /* Zak McKracken */ +XTRA_SCROLL_LABEL( "HAPAX LEGOMENON", SC13), +XTRA_SCROLL_LABEL( "EIRIS SAZUN IDISI", SC14), /* Merseburg Incantations */ +XTRA_SCROLL_LABEL( "PHOL ENDE WODAN", SC15), /* Merseburg Incantations */ +XTRA_SCROLL_LABEL( "GHOTI", SC16), /* pronounced as 'fish', + * George Bernard Shaw */ +XTRA_SCROLL_LABEL("MAPIRO MAHAMA DIROMAT", SC17), /* Wizardry */ +XTRA_SCROLL_LABEL( "VAS CORP BET MANI", SC18), /* Ultima */ +XTRA_SCROLL_LABEL( "XOR OTA", SC19), /* Aarne Haapakoski */ +XTRA_SCROLL_LABEL("STRC PRST SKRZ KRK", SC20), /* Czech and Slovak + * tongue-twister */ +#undef XTRA_SCROLL_LABEL /* These must come last because they have special fixed descriptions. */ #ifdef MAIL_STRUCTURES @@ -1204,10 +1224,10 @@ SPELL("healing", "white", P_HEALING_SPELL, 40, 1, 1, IMMEDIATE, CLR_WHITE, SPE_HEALING), SPELL("knock", "pink", - P_MATTER_SPELL, 35, 1, 1, IMMEDIATE, CLR_BRIGHT_MAGENTA, + P_MATTER_SPELL, 25, 1, 1, IMMEDIATE, CLR_BRIGHT_MAGENTA, SPE_KNOCK), SPELL("force bolt", "red", - P_ATTACK_SPELL, 35, 1, 1, IMMEDIATE, CLR_RED, + P_ATTACK_SPELL, 30, 1, 1, IMMEDIATE, CLR_RED, SPE_FORCE_BOLT), SPELL("confuse monster", "orange", P_ENCHANTMENT_SPELL, 30, 2, 1, IMMEDIATE, CLR_ORANGE, @@ -1222,7 +1242,7 @@ SPELL("slow monster", "light green", P_ENCHANTMENT_SPELL, 30, 1, 1, IMMEDIATE, CLR_BRIGHT_GREEN, SPE_SLOW_MONSTER), SPELL("wizard lock", "dark green", - P_MATTER_SPELL, 30, 2, 1, IMMEDIATE, CLR_GREEN, + P_MATTER_SPELL, 25, 2, 1, IMMEDIATE, CLR_GREEN, SPE_WIZARD_LOCK), SPELL("create monster", "turquoise", P_CLERIC_SPELL, 35, 2, 1, NODIR, CLR_BRIGHT_CYAN, @@ -1293,6 +1313,10 @@ SPELL("jumping", "thin", SPELL("stone to flesh", "thick", P_HEALING_SPELL, 15, 3, 1, IMMEDIATE, HI_PAPER, SPE_STONE_TO_FLESH), +SPELL("chain lightning", "checkered", + P_ATTACK_SPELL, 25, 2, 1, NODIR, CLR_GRAY, + SPE_CHAIN_LIGHTNING), + #if 0 /* DEFERRED */ /* from slash'em, create a tame critter which explodes when attacking, damaging adjacent creatures--friend or foe--and dying in the process */ @@ -1312,15 +1336,15 @@ SPELL("blank paper", "plain", P_NONE, 18, 0, 0, 0, HI_PAPER, SPE_BLANK_PAPER), even if hero learns every spell, spl_book[] will have at least one unused slot at end; an unused slot is needed for use as terminator */ MARKER(LAST_SPELL, SPE_BLANK_PAPER) -/* tribute book for 3.6 */ +/* tribute book added in 3.6 */ OBJECT(OBJ("novel", "paperback"), BITS(0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, P_NONE, PAPER), - 0, SPBOOK_CLASS, 1, 0, 0, 20, 0, 0, 0, 1, 20, CLR_BRIGHT_BLUE, + 0, SPBOOK_CLASS, 1, 0, 10, 20, 0, 0, 0, 1, 20, CLR_BRIGHT_BLUE, SPE_NOVEL), /* a special, one of a kind, spellbook */ OBJECT(OBJ("Book of the Dead", "papyrus"), BITS(0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, P_NONE, PAPER), - 0, SPBOOK_CLASS, 0, 0, 20, 10000, 0, 0, 0, 7, 20, HI_PAPER, + 0, SPBOOK_CLASS, 0, 0, 50, 10000, 0, 0, 0, 7, 20, HI_PAPER, SPE_BOOK_OF_THE_DEAD), #undef SPELL @@ -1402,11 +1426,11 @@ COIN("gold piece", 1000, GOLD, 1, GOLD_PIECE), HARDGEM(mohs), 0, -P_SLING, glass), \ 0, GEM_CLASS, prob, 0, 1, gval, 3, 3, 0, 0, nutr, color, sn) #define ROCK(name,desc,kn,prob,wt,gval,sdam,ldam,mgc,nutr,mohs,mrg,uskn,\ - glass,color,sn) \ + glass,colr,sn) \ OBJECT(OBJ(name, desc), \ BITS(kn, mrg, uskn, 0, mgc, 0, 0, 0, 0, \ HARDGEM(mohs), 0, -P_SLING, glass), \ - 0, GEM_CLASS, prob, 0, wt, gval, sdam, ldam, 0, 0, nutr, color, sn) + 0, GEM_CLASS, prob, 0, wt, gval, sdam, ldam, 0, 0, nutr, colr, sn) GEM("dilithium crystal", "white", 2, 1, 4500, 15, 5, GEMSTONE, CLR_WHITE, DILITHIUM_CRYSTAL), MARKER(FIRST_REAL_GEM, DILITHIUM_CRYSTAL) @@ -1532,7 +1556,6 @@ OBJECT(OBJ(NoDes, NoDes), BITS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, P_NONE, 0), 0, ILLOBJ_CLASS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) #undef BITS -#undef COLOR_FIELD #endif #undef OBJ diff --git a/include/optlist.h b/include/optlist.h index daa5fc8333..0ec71009fc 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -4,6 +4,10 @@ #ifndef OPTLIST_H #define OPTLIST_H +#ifdef OPTIONS_C +static int optfn_boolean(int, int, boolean, char *, char *); +#endif + /* * NOTE: If you add (or delete) an option, please review: * doc/options.txt @@ -12,7 +16,6 @@ * updates that should accompany your change. */ -static int optfn_boolean(int, int, boolean, char *, char *); enum OptType { BoolOpt, CompOpt, OthrOpt }; enum Y_N { No, Yes }; enum Off_On { Off, On }; @@ -93,10 +96,12 @@ static int optfn_##a(int, int, boolean, char *, char *); #endif #endif -/* B:nm, ln, opt_*, setwhere?, on?, negat?, val?, dup?, hndlr? Alias, bool_p, term */ -/* C:nm, ln, opt_*, setwhere?, negateok?, valok?, dupok?, hndlr? Alias, desc */ -/* P:pfx, ln, opt_*, setwhere?, negateok?, valok?, dupok?, hndlr? Alias, desc*/ - +/* B:nm, sec, ln, opt_*, setwhere?, on?, negat?, val?, dup?, hndlr? Alias, + bool_p, term */ +/* C:nm, sec, ln, opt_*, setwhere?, negateok?, valok?, dupok?, hndlr? Alias, + desc */ +/* P:pfx, sec, ln, opt_*, setwhere?, negateok?, valok?, dupok?, hndlr? Alias, + desc*/ /* * Most of the options are in alphabetical order; a few are forced * to the top of list so that doset() will list them first and @@ -132,6 +137,9 @@ static int optfn_##a(int, int, boolean, char *, char *); "your starting alignment (lawful, neutral, or chaotic)") /* end of special ordering; remainder of entries are in alphabetical order */ + NHOPTB(accessiblemsg, Advanced, 0, opt_out, set_in_game, + Off, Yes, No, No, NoAlias, &a11y.accessiblemsg, Term_False, + "add location information to messages") NHOPTB(acoustics, Advanced, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias, &flags.acoustics, Term_False, "can your character hear anything") @@ -159,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") @@ -226,11 +236,28 @@ static int optfn_##a(int, int, boolean, char *, char *); NHOPTB(confirm, Advanced, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias, &flags.confirm, Term_False, "ask before hitting tame or peaceful monsters") +#ifdef CRASHREPORT + NHOPTC(crash_email, Advanced, PL_NSIZ, opt_in, set_in_game, + No, Yes, No, No, NoAlias, + "email address for reporting") + NHOPTC(crash_name, Advanced, PL_NSIZ, opt_in, set_in_game, + No, Yes, No, No, NoAlias, + "your name for reporting") + NHOPTC(crash_urlmax, Advanced, PL_NSIZ, opt_in, set_in_game, + No, Yes, No, No, NoAlias, + "length of longest url we can generate") +#endif #ifdef CURSES_GRAPHICS NHOPTC(cursesgraphics, Advanced, 70, opt_in, set_in_config, No, Yes, No, No, NoAlias, "load curses display symbols into symset") #endif + NHOPTB(customcolors, Map, 0, opt_out, set_in_game, + On, Yes, No, No, "customcolours", &iflags.customcolors, + Term_False, "use custom colors in map") + NHOPTB(customsymbols, Map, 0, opt_out, set_in_game, + On, Yes, No, No, "customsymbols", &iflags.customsymbols, + Term_False, "use custom utf8 symbols in map") NHOPTB(dark_room, Advanced, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias, &flags.dark_room, Term_False, "show floor outside line of sight differently") @@ -258,6 +285,9 @@ static int optfn_##a(int, int, boolean, char *, char *); NHOPTC(dogname, Advanced, PL_PSIZ, opt_in, set_gameview, No, Yes, No, No, NoAlias, "name of your starting pet if it is a little dog") + NHOPTB(dropped_nopick, Behavior, 0, opt_out, set_in_game, + On, Yes, No, No, NoAlias, &flags.nopick_dropped, Term_False, + "don't autopickup dropped items") NHOPTC(dungeon, Advanced, MAXDCHARS + 1,opt_in, set_in_config, No, Yes, No, No, NoAlias, "list of symbols to use in drawing the dungeon map") @@ -394,6 +424,9 @@ static int optfn_##a(int, int, boolean, char *, char *); NHOPTB(mention_decor, Advanced, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias, &flags.mention_decor, Term_False, "give feedback when walking over interesting features") + NHOPTB(mention_map, Advanced, 0, opt_in, set_in_game, + Off, Yes, No, No, NoAlias, &a11y.glyph_updates, Term_False, + "give feedback when interesting map locations change") NHOPTB(mention_walls, Advanced, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias, &flags.mention_walls, Term_False, "give feedback when walking into walls") @@ -405,7 +438,7 @@ static int optfn_##a(int, int, boolean, char *, char *); NHOPTC(menu_first_page, Advanced, 4, opt_in, set_in_config, No, Yes, No, No, NoAlias, "jump to the first page in a menu") NHOPTC(menu_headings, Advanced, 4, opt_in, set_in_game, - No, Yes, No, Yes, NoAlias, "display style for menu headings") + Yes, Yes, No, Yes, NoAlias, "display style for menu headings") NHOPTC(menu_invert_all, Advanced, 4, opt_in, set_in_config, No, Yes, No, No, NoAlias, "invert all items in a menu") NHOPTC(menu_invert_page, Advanced, 4, opt_in, set_in_config, @@ -457,12 +490,18 @@ static int optfn_##a(int, int, boolean, char *, char *); NHOPTO("message types", Advanced, o_message_types, BUFSZ, opt_in, set_in_game, No, Yes, No, NoAlias, "edit message types") + NHOPTB(mon_movement, Advanced, 0, opt_in, set_in_game, + Off, Yes, No, No, NoAlias, &a11y.mon_movement, Term_False, + "message when hero sees monster movement") NHOPTB(monpolycontrol, Advanced, 0, opt_in, set_wizonly, Off, Yes, No, No, NoAlias, &iflags.mon_polycontrol, Term_False, "control monster polymorphs") NHOPTO("monster colors", Advanced, o_monstercolor, BUFSZ, opt_in, set_in_game, No, Yes, No, NoAlias, "edit monster colors") + NHOPTB(montelecontrol, Advanced, 0, opt_in, set_wizonly, + Off, Yes, No, No, NoAlias, &iflags.mon_telecontrol, Term_False, + "control monster teleport destinations") NHOPTC(monsters, Advanced, MAXMCLASSES, opt_in, set_in_config, No, Yes, No, No, NoAlias, "list of symbols to use for monsters") @@ -508,31 +547,40 @@ static int optfn_##a(int, int, boolean, char *, char *); No, Yes, No, No, NoAlias, "the inventory order of the items in your pack") #ifdef CHANGE_COLOR -#ifndef WIN32 +#ifndef MAC /* not old Mac OS9 */ + NHOPTC(palette, Advanced, 15, opt_in, set_gameview, + No, Yes, Yes, No, "hicolor", + "palette (adjust an RGB color in palette (color/R-G-B)") +#else NHOPTC(palette, Advanced, 15, opt_in, set_in_game, - No, Yes, No, No, "hicolor", + No, Yes, Yes, No, "hicolor", "palette (00c/880/-fff is blue/yellow/reverse white)") -#else - NHOPTC(palette, Advanced, 15, opt_in, set_in_config, - No, Yes, No, No, "hicolor", - "palette (adjust an RGB color in palette (color-R-G-B)") #endif #endif /* prior to paranoid_confirmation, 'prayconfirm' was a distinct option */ NHOPTC(paranoid_confirmation, Advanced, 28, opt_in, set_in_game, Yes, Yes, Yes, Yes, "prayconfirm", "extra prompting in certain situations") + NHOPTB(pauper, Advanced, 0, opt_in, set_in_config, + Off, Yes, No, No, NoAlias, &u.uroleplay.pauper, Term_False, + "start your character without any items") NHOPTB(perm_invent, Advanced, 0, opt_in, set_in_game, - Off, Yes, No, No, NoAlias, &iflags.perm_invent, Term_False, - "show permanent inventory window") - NHOPTC(petattr, Advanced, 88, opt_in, set_in_game, /* curses only */ - No, Yes, No, No, NoAlias, "attributes for highlighting pets") + Off, Yes, No, No, NoAlias, &iflags.perm_invent, Term_Off, + "show persistent inventory window") + NHOPTC(perminv_mode, Advanced, 20, opt_in, set_in_game, + Yes, Yes, No, Yes, NoAlias, + "what to show in persistent inventory window") + NHOPTC(petattr, Advanced, 88, opt_in, set_in_game, /* tty/curses only */ + No, Yes, No, Yes, NoAlias, "attributes for highlighting pets") /* pettype is ignored for some roles */ NHOPTC(pettype, Advanced, 4, opt_in, set_gameview, Yes, Yes, No, No, "pet", "your preferred initial pet type") NHOPTC(pickup_burden, Advanced, 20, opt_in, set_in_game, No, Yes, No, Yes, NoAlias, "maximum burden picked up before prompt") + NHOPTB(pickup_stolen, Behavior, 0, opt_out, set_in_game, + On, Yes, No, No, NoAlias, &flags.pickup_stolen, Term_False, + "autopickup stolen items") NHOPTB(pickup_thrown, Behavior, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias, &flags.pickup_thrown, Term_False, "autopickup thrown items") @@ -558,6 +606,9 @@ static int optfn_##a(int, int, boolean, char *, char *); NHOPTB(pushweapon, Behavior, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias, &flags.pushweapon, Term_False, "previous weapon goes to secondary slot") + NHOPTB(query_menu, Advanced, 0, opt_in, set_in_game, + Off, Yes, No, No, NoAlias, &iflags.query_menu, Term_False, + "use a menu for yes/no queries") NHOPTB(quick_farsight, Advanced, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias, &flags.quick_farsight, Term_False, "skip map browse when forced to looked at map") @@ -599,6 +650,9 @@ static int optfn_##a(int, int, boolean, char *, char *); NHOPTB(selectsaved, Advanced, 0, opt_out, set_in_config, On, Yes, No, No, NoAlias, &iflags.wc2_selectsaved, Term_False, (char *)0) + NHOPTB(showdamage, Advanced, 0, opt_in, set_in_game, + Off, Yes, No, No, NoAlias, &iflags.showdamage, Term_False, + "show damage hero takes in message line") NHOPTB(showexp, Status, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias, &flags.showexp, Term_False, "show experience points in status line") @@ -614,6 +668,9 @@ static int optfn_##a(int, int, boolean, char *, char *); Off, Yes, No, No, NoAlias, (boolean *) 0, Term_False, (char *)0) #endif + NHOPTB(showvers, Advanced, 0, opt_in, set_in_game, + Off, Yes, No, No, NoAlias, &flags.showvers, Term_False, + "show version info on status line") NHOPTB(silent, Advanced, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias, &flags.silent, Term_False, "don't use terminal bell") @@ -647,6 +704,9 @@ static int optfn_##a(int, int, boolean, char *, char *); NHOPTB(sparkle, Map, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias, &flags.sparkle, Term_False, "display sparkly effect when resisting magic") + NHOPTB(spot_monsters, Advanced, 0, opt_in, set_in_game, + Off, Yes, No, No, NoAlias, &a11y.mon_notices, Term_False, + "message when hero spots a monster") NHOPTB(splash_screen, Advanced, 0, opt_out, set_in_config, On, Yes, No, No, NoAlias, &iflags.wc_splash_screen, Term_False, (char *)0) @@ -749,11 +809,11 @@ static int optfn_##a(int, int, boolean, char *, char *); (char *)0) NHOPTC(vary_msgcount, Advanced, 20, opt_in, set_gameview, No, Yes, No, No, NoAlias, "show more old messages at a time") -#if defined(NO_VERBOSE_GRANULARITY) NHOPTB(verbose, Advanced, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias, &flags.verbose, Term_False, (char *)0) -#endif + NHOPTC(versinfo, Advanced, 80, opt_out, set_in_game, + No, Yes, No, Yes, NoAlias, "extra information for 'showvers'") #ifdef MSDOS NHOPTC(video, Advanced, 20, opt_in, set_in_config, No, Yes, No, No, NoAlias, "method of video updating") @@ -820,7 +880,7 @@ static int optfn_##a(int, int, boolean, char *, char *); No, Yes, No, No, NoAlias, "window processor to use") #endif NHOPTC(windowcolors, Advanced, 80, opt_in, set_gameview, - No, Yes, No, No, NoAlias, + No, Yes, Yes, No, NoAlias, "the foreground/background colors of windows") /* NHOPTC(windowtype) -- moved to top */ NHOPTB(wizmgender, Advanced, 0, opt_in, set_wizonly, @@ -843,10 +903,7 @@ static int optfn_##a(int, int, boolean, char *, char *); NHOPTP(IBM_, Advanced, 0, opt_in, set_hidden, No, No, Yes, No, NoAlias, "prefix for old micro IBM_ options") #endif /* MICRO */ -#if !defined(NO_VERBOSE_GRANULARITY) - NHOPTP(verbose, Advanced, 0, opt_out, set_in_game, - Yes, Yes, Yes, Yes, NoAlias, "suppress verbose messages") -#endif + #undef NoAlias #undef NHOPTB #undef NHOPTC @@ -857,4 +914,4 @@ static int optfn_##a(int, int, boolean, char *, char *); /* clang-format on */ #endif /* NHOPT_PROTO || NHOPT_ENUM || NHOPT_PARSE */ -/* end of optlist */ +/*optlist.h*/ diff --git a/include/patchlevel.h b/include/patchlevel.h index 890a69c220..5d9a6505ed 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 patchlevel.h $NHDT-Date: 1651297020 2022/04/30 05:37:00 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.207 $ */ +/* NetHack 3.7 patchlevel.h $NHDT-Date: 1725653013 2024/09/06 20:03:33 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.264 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -95,14 +95,15 @@ * ubuntu impish 21.10; disable that for any Linux unless GCC_URWARN is * defined to force it back into effect * update_inventory() after leash goes slack - * player assigned name for monsters, specific objects, or object types could be - longer than what was intented to be allowed; for 'curses', much longer - * windows: added winflexbison to travis-ci configuration to permit full build of - * levcomp and dgncomp - * windows: a bad chdir specified in win/win32/dgnstuff.mak caused full build to - * abort - * windows: the console.rc file had outdated information stating 3.6.3 when the - * official 3.6.6 binary was built. + * player assigned name for monsters, specific objects, or object types + * could be longer than what was intended to be allowed; for 'curses', + * it could be much longer + * windows: added winflexbison to travis-ci configuration to permit full + * build of levcomp and dgncomp + * windows: a bad chdir specified in win/win32/dgnstuff.mak caused full build + * to abort + * windows: the console.rc file had outdated information stating 3.6.3 when + * the official 3.6.6 binary was built. * windows: switch from using keyhandling dll's to incorporating the three * variations (default, ray, 340) in sys/winnt/nttty.c * curses: cherry-picked selectsaved code from 3.7 for menu of save files @@ -114,7 +115,8 @@ * invalid status highlight color could be maliciously used to corrupt memory * formatting corpse names used internal buffers differently from formatting * other objects and could potentially clobber memory - * avoid divide by 0 crash if 'bogusmon' (file of bogus monster types) is empty + * avoid divide by 0 crash if 'bogusmon' (file of bogus monster types) is + * empty * avoid #wizrumorcheck crash if either 'rumors.tru' or 'rumors.fal' or both * were empty when makedefs built 'rumors' */ @@ -124,10 +126,12 @@ * fix accessing mons[-1] when trying to gate in a non-valid demon * fix accessing mons[-1] when monster figures out if a tin cures stoning * have string_for_opt() return empty_optstr on failure - * ensure existing callers of string_for_opt() check return value before using it + * ensure existing callers of string_for_opt() check return value before + * using it * fix potential buffer overflow in add_menu_coloring() * fix potential buffer overflow in sym_val() - * fix potential buffer overflow in pline(), raw_printf(), and config_error_add() + * fix potential buffer overflow in pline(), raw_printf(), and + * config_error_add() * fix potential buffer overflow in choose_windows() * use vsnprintf instead of vsprintf in pline.c where possible * Windows: includes a fix from a 3.6.4 post-release update where @@ -138,24 +142,24 @@ /* Patch 4, December 18, 2019 * * fix potential buffer overflow when parsing run-time configuration file - * GDBPATH and GREPPATH from sysconf or -D... on compilation command line were - * being processed even if PANICTRACE was disabled but only being freed - * at end of game when that was enabled + * GDBPATH and GREPPATH from sysconf or -D... on compilation command line + * were being processed even if PANICTRACE was disabled but only being + * freed at end of game when that was enabled * fix the article used in the message when your steed encounters a polymorph * trap * allow teleporting onto the vibrating square - * message "your knapsack can't accommodate any more items" when picking stuff - * up or removing such from container was inaccurate if there was some - * gold pending; vary the message rather than add more convoluted pickup - * code + * message "your knapsack can't accommodate any more items" when picking + * stuff up or removing such from container was inaccurate if there was + * some gold pending; vary the message rather than add more convoluted + * pickup code * dozen-ish assorted spelling/typo fixes in messages and source comments * wizard mode wishing for terrain would leave it unmapped if done while blind * wizard mode terrain wish could leave hero in water (severe vision limits) * or in lava (trapped, sinking) which wasn't there any more * flying hero can go down (via '>') holes or trap doors instead of escaping * trap - * polymorphed hero hiding on the ceiling can now use '>' to unhide instead of - * being told "you can't go down here" + * polymorphed hero hiding on the ceiling can now use '>' to unhide instead + * of being told "you can't go down here" * fix compilation on platforms that split the ncurses and tinfo libraries * Windows: allow all game files to be on a portable device via the sysconf * option 'portable_device_paths' @@ -166,9 +170,10 @@ * Fixed stale 'thrownobj' pointer for returning thrown aklys while engulfed * Fixed uarmh null pointer dereference if a helm of opposite alignment came * off due to being polymorphed - * Fixed 'object lost' panic when attempting to crawl of of the water during + * Fixed 'object lost' panic when attempting to crawl out of the water during * emergency disrobing/dropping - * Running now stops when moving over engravings so you can tell where they are + * Running now stops when moving over engravings so you can tell where they + * are * Fixed detection of unseen/secret doors which failed to find monsters hiding * under objects and failed to find monsters hiding at trap locations * Ensured fatal status conditions made it to disclosure and/or dumplog @@ -193,13 +198,14 @@ * Improved performance of some tty versions by reducing the number of * function calls made from mapglyph * Allowed the msdos implementation to build with curses and PDCurses - * Included over 100 other fixes and improvements as outlined in doc/fixes36.3 + * Included over 100 other fixes and improvements as outlined in + * doc/fixes36.3 (later renamed doc/fixes3-6-3.txt) */ /* Patch 2, May 7, 2019 * * Over 320 bug fixes including a couple of crash bug fixes as outlined in - * doc/fixes36.2 + * doc/fixes36.2 (later renamed to doc/fixes-3-6-2.txt) * More than 15 enhancements or improvements * Ensuring that unix Makefiles do not rely on features unique to gnu make * Improvements to hilite_status parsing in an effort to ensure that expected @@ -213,13 +219,13 @@ /* Patch 1, April 27, 2018 * * Over four hundred and seventy bug fixes and improvements as outlined in - * doc/fixes36.1 + * doc/fixes36.1 (later renamed to doc/fixes3-6-1.txt) */ /* * NetHack 3.6.0, December 7, 2015 * - * Hundreds of bug fixes as outlined in doc/fixes36.0. + * Hundreds of bug fixes as outlined in doc/fixes36.0 (doc/fixes3-6-0.txt). * Some code reorganization. * Some new features. * Variations of some community patches rolled in. diff --git a/include/pcconf.h b/include/pcconf.h index 46a6123930..6736d8d9d4 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 @@ -246,7 +230,6 @@ #include #ifdef MSDOS -#define TEXTCOLOR /* */ #define PORT_HELP "msdoshlp.txt" /* msdos port specific help file */ #endif @@ -288,9 +271,7 @@ #endif #endif #define ASCIIGRAPH -#ifdef TEXTCOLOR #define VIDEOSHADES -#endif /* SCREEN_8514, SCREEN_VESA are only placeholders presently - sub VGA instead */ #if defined(SCREEN_8514) diff --git a/include/permonst.h b/include/permonst.h index cbb507e642..9f11bb1fc2 100644 --- a/include/permonst.h +++ b/include/permonst.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 permonst.h $NHDT-Date: 1596498555 2020/08/03 23:49:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.14 $ */ +/* NetHack 3.7 permonst.h $NHDT-Date: 1725653014 2024/09/06 20:03:34 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.26 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Kenneth Lorber, Kensington, Maryland, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -6,6 +6,25 @@ #ifndef PERMONST_H #define PERMONST_H +enum monnums { +#define MONS_ENUM +#include "monsters.h" +#undef MONS_ENUM + NUMMONS, + NON_PM = -1, /* "not a monster" */ + LOW_PM = NON_PM + 1, /* first monster in mons */ + LEAVESTATUE = NON_PM - 1, /* leave statue instead of corpse; + * there are two lower values assigned + * in end.c so that (x == LEAVESTATUE) + * will test FALSE in bones.c: + * (NON_PM - 2) for no corpse + * (NON_PM - 3) for no corpse, no grave */ + HIGH_PM = NUMMONS - 1, + SPECIAL_PM = PM_LONG_WORM_TAIL /* [normal] < ~ < [special] */ + /* mons[SPECIAL_PM] through mons[NUMMONS-1], inclusive, are + never generated randomly and cannot be polymorphed into */ +}; + /* This structure covers all attack forms. * aatyp is the gross attack type (eg. claw, bite, breath, ...) * adtyp is the damage type (eg. physical, fire, cold, spell, ...) @@ -30,9 +49,9 @@ struct attack { /* Weight of human body, elf, dragon */ -#define WT_HUMAN 1450 -#define WT_ELF 800 -#define WT_DRAGON 4500 +#define WT_HUMAN 1450U +#define WT_ELF 800U +#define WT_DRAGON 4500U #ifndef ALIGN_H #include "align.h" @@ -42,6 +61,7 @@ struct attack { struct permonst { const char *pmnames[NUM_MGENDERS]; + const enum monnums pmidx; /* mons array index aka PM_ identifier */ char mlet; /* symbol */ schar mlevel, /* base monster level */ mmove, /* move speed */ @@ -60,19 +80,11 @@ struct permonst { mflags2; /* more boolean bitflags */ unsigned short mflags3; /* yet more boolean bitflags */ uchar difficulty; /* toughness (formerly from makedefs -m) */ -#ifdef TEXTCOLOR - uchar mcolor; /* color to use */ -#endif + uchar mcolor; /* color to use */ }; -extern NEARDATA struct permonst mons[]; /* the master list of monster types */ - -enum monnums { -#define MONS_ENUM -#include "monsters.h" -#undef MONS_ENUM - NUMMONS -}; +extern NEARDATA struct permonst mons[NUMMONS + 1]; /* the master list of + * monster types */ #define VERY_SLOW 3 #define SLOW_SPEED 9 @@ -80,15 +92,4 @@ enum monnums { #define FAST_SPEED 15 #define VERY_FAST 24 -#define NON_PM (-1) /* "not a monster" */ -#define LOW_PM (NON_PM + 1) /* first monster in mons[] */ -#define SPECIAL_PM PM_LONG_WORM_TAIL /* [normal] < ~ < [special] */ -/* mons[SPECIAL_PM] through mons[NUMMONS-1], inclusive, are - never generated randomly and cannot be polymorphed into */ - -#ifdef PMNAME_MACROS -#define pmname(pm,g) ((((g) == MALE || (g) == FEMALE) && (pm)->pmnames[g]) \ - ? (pm)->pmnames[g] : (pm)->pmnames[NEUTRAL]) -#endif - #endif /* PERMONST_H */ diff --git a/include/prop.h b/include/prop.h index 56a50d0540..0597ce7dd7 100644 --- a/include/prop.h +++ b/include/prop.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 prop.h $NHDT-Date: 1596498555 2020/08/03 23:49:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.22 $ */ +/* NetHack 3.7 prop.h $NHDT-Date: 1702274027 2023/12/11 05:53:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.24 $ */ /* Copyright (c) 1989 Mike Threepoint */ /* NetHack may be freely redistributed. See license for details. */ @@ -20,7 +20,10 @@ enum prop_types { POISON_RES = 6, ACID_RES = 7, STONE_RES = 8, - /* note: for the first eight properties, MR_xxx == (1 << (xxx_RES - 1)) */ + /* note: the first eight properties above are equivalent to MR_xxx bits + * MR_FIRE through MR_STONE, and can be directly converted to them: */ +#define res_to_mr(r) \ + ((FIRE_RES <= (r) && (r) <= STONE_RES) ? (uchar) (1 << ((r) - 1)) : 0x00) DRAIN_RES = 9, SICK_RES = 10, INVULNERABLE = 11, @@ -52,43 +55,45 @@ 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, - WITHERING = 67, - DOOMED = 68, - LIFESAVED = 69 + 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, + /* xNetHack properties */ + WITHERING = 69, + DOOMED = 70, + LAST_PROP = DOOMED }; -#define LAST_PROP (LIFESAVED) /*** Where the properties come from ***/ /* Definitions were moved here from obj.h and you.h */ @@ -131,15 +136,15 @@ struct prop { long intrinsic; /* Timed properties */ #define TIMEOUT 0x00ffffffL /* Up to 16 million turns */ - /* Permanent properties */ -#define FROMEXPER 0x01000000L /* Gain/lose with experience, for role */ -#define FROMRACE 0x02000000L /* Gain/lose with experience, for race */ +/* Permanent properties */ +#define FROMEXPER 0x01000000L /* Gain/lose with experience, for role */ +#define FROMRACE 0x02000000L /* Gain/lose with experience, for race */ #define FROMOUTSIDE 0x04000000L /* By corpses, prayer, thrones, etc. */ #define FROMROLEPLAY 0x08000000L /* From a roleplay birth option */ #define INTRINSIC (FROMOUTSIDE | FROMRACE | FROMEXPER | FROMROLEPLAY) /* Control flags */ -#define FROMFORM 0x10000000L /* Polyd; conferred by monster form */ -#define I_SPECIAL 0x20000000L /* Property is controllable */ +#define FROMFORM 0x10000000L /* Polyd; conferred by monster form */ +#define I_SPECIAL 0x20000000L /* Property is controllable */ }; /*** Definitions for backwards compatibility ***/ diff --git a/include/rm.h b/include/rm.h index 50a6f95914..2c0cd96204 100644 --- a/include/rm.h +++ b/include/rm.h @@ -31,10 +31,27 @@ * +- -+- -+ | */ -/* Level location types. [Some debugging code in src/display.c - defines array type_names[] which contains an entry for each of - these, so needs to be kept in sync if any new types are added - or existing ones renumbered.] */ +/* + * Level location types, values for 'level.locations[x][y].typ'. + * + * These are different from display symbols and while there is + * similarity between the cmap subset of those, there isn't any + * one-to-one correspondence between the two encodings. For instance, + * the DOOR type has multiple symbols (closed|open|gone); so does the + * STAIRS type (down|up). Conversely, DRAWBRIDGE_UP represents the + * location of the projected span if the drawbridge were down but + * there is no symbol for that; it is displayed as water, ice, lava, or + * floor depending on what is at that spot when the bridge is 'closed'. + * + * [Some debugging code in src/display.c defines array type_names[] + * which contains an entry for each of these, so needs to be kept in + * sync if any new types are added or existing ones renumbered. The + * #terrain command also has a menu choice to display each map spot by + * a letter derived from these numeric values and another choice to + * display a legend showing the letter-to-type correspondence. If any + * types are added, removed, or reordered, that needs to be updated to + * keep in synch.] + */ enum levl_typ_types { STONE = 0, VWALL = 1, @@ -88,16 +105,18 @@ 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 || (gl.level.flags.arboreal && (typ) == STONE)) + ((typ) == TREE || (svl.level.flags.arboreal && (typ) == STONE)) #define ACCESSIBLE(typ) ((typ) >= DOOR) /* good position */ #define IS_ROOM(typ) ((typ) >= ROOM) /* ROOM, STAIRS, furniture.. */ #define ZAP_POS(typ) ((typ) >= POOL) #define SPACE_POS(typ) ((typ) > DOOR) #define IS_POOL(typ) ((typ) >= POOL && (typ) <= DRAWBRIDGE_UP) +#define IS_LAVA(typ) ((typ) == LAVAPOOL || (typ) == LAVAWALL) #define IS_THRONE(typ) ((typ) == THRONE) #define IS_FOUNTAIN(typ) ((typ) == FOUNTAIN) #define IS_SINK(typ) ((typ) == SINK) @@ -121,6 +140,58 @@ 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, 6); /* 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 */ + Bitfield(uvisited, 1); /* player has visited this space */ + Bitfield(unused, 6); /* currently unused - take bits out of this rather + than overloading it as if it were a second flags + field! */ +}; + +/* + * rm flags field overloads: + * + * +--------+-------------+-------------+--------+----------+-----------+ + * | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | + * | 0x20 | 0x10 | 0x8 | 0x4 | 0x2 | 0x1 | + * +--------+-------------+-------------+--------+----------+-----------+ + * door |D_WARNED|D_IRON | D_TRAPPED |D_LOCKED| ~~~~~doorstate~~~~~~ | + * drawbr. | |DB_FLOOR | DB_ICE |DB_LAVA |DB_DIR |DB_DIR | + * wall | |W_NONPASSWALL|W_NONDIGGABLE|W_MASK |W_MASK |W_MASK | + * 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 | + * corr | | | | | |is_niche | + * | | | | | | | + * +--------+-------------+-------------+--------+----------+-----------+ + * + * + * 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 @@ -133,6 +204,17 @@ enum levl_typ_types { * with W_NONPASSWALL but doors never block phasing. * D_SECRET would not fit within struct rm's 6-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 */ +#define is_niche flags /* used to mark corridor spaces that are niches */ + /* * The possible states of doors - lowest 2 bits of doormask * @@ -229,33 +311,6 @@ enum door_states { #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, 6); /* 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 */ - Bitfield(uvisited, 1); /* player has visited this space */ - Bitfield(unused, 6); /* currently unused - take bits out of this rather - than overloading it as if it were a second flags - field! */ -}; - /* some door macros, intended to be called on a struct rm* */ #define doorstate(x) ((x)->doormask & D_STATEMASK) #define door_is_closed(x) (doorstate(x) == D_CLOSED) @@ -289,7 +344,7 @@ struct rm { /* * Add wall angle viewing by defining "modes" for each wall type. Each - * mode describes which parts of a wall are finished (seen as as wall) + * mode describes which parts of a wall are finished (seen as wall) * and which are unfinished (seen as rock). * * We use the bottom 3 bits of the flags field for the mode. This comes @@ -366,18 +421,7 @@ 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 is_niche flags /* used to mark corridor spaces that are niches */ -#define emptygrave flags /* no corpse in grave */ -/* horizonal applies to walls, doors (including sdoor); also to iron bars +/* 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 */ #define disturbed horizontal /* kicking or engraving on a grave's headstone @@ -395,7 +439,7 @@ struct damage { an existing bones level; if so, most recent victim will be first in list */ struct cemetery { struct cemetery *next; /* next struct is previous dead character... */ - /* "gp.plname" + "-ROLe" + "-RACe" + "-GENder" + "-ALIgnment" + \0 */ + /* "svp.plname" + "-ROLe" + "-RACe" + "-GENder" + "-ALIgnment" + \0 */ char who[PL_NSIZ + 4 * (1 + 3) + 1]; /* death reason, same as in score/log file */ char how[100 + 1]; /* [DTHSZ+1] */ @@ -431,6 +475,7 @@ struct levelflags { Bitfield(is_maze_lev, 1); Bitfield(is_cavernous_lev, 1); Bitfield(arboreal, 1); /* Trees replace rock */ + Bitfield(has_town, 1); /* level contains a town */ Bitfield(wizard_bones, 1); /* set if level came from a bones file which was created in wizard mode (or normal mode descendant of such) */ @@ -442,6 +487,7 @@ struct levelflags { Bitfield(rndmongen, 1); /* random monster generation allowed? */ Bitfield(deathdrops, 1); /* monsters may drop corpses/death drops */ + Bitfield(noautosearch, 1); /* automatic searching disabled */ Bitfield(fumaroles, 1); /* lava emits poison gas at random */ Bitfield(stormy, 1); /* clouds create lightning bolts at random */ @@ -480,9 +526,9 @@ typedef struct { /* * Macros for compatibility with old code. Someday these will go away. */ -#define levl gl.level.locations -#define fobj gl.level.objlist -#define fmon gl.level.monlist +#define levl svl.level.locations +#define fobj svl.level.objlist +#define fmon svl.level.monlist /* * Covert a trap number into the defsym graphics array. @@ -492,28 +538,28 @@ typedef struct { #define trap_to_defsym(t) (S_arrow_trap + (t) - 1) #define defsym_to_trap(d) ((d) - S_arrow_trap + 1) -#define OBJ_AT(x, y) (gl.level.objects[x][y] != (struct obj *) 0) +#define OBJ_AT(x, y) (svl.level.objects[x][y] != (struct obj *) 0) /* * Macros for encapsulation of level.monsters references. */ #if 0 /* these wouldn't allow buried monster and surface monster at same location */ #define MON_AT(x, y) \ - (gl.level.monsters[x][y] && !gl.level.monsters[x][y]->mburied) + (svl.level.monsters[x][y] && !svl.level.monsters[x][y]->mburied) #define MON_BURIED_AT(x, y) \ - (gl.level.monsters[x][y] && gl.level.monsters[x][y]->mburied) + (svl.level.monsters[x][y] && svl.level.monsters[x][y]->mburied) #define m_at(x, y) \ - (MON_AT(x, y) ? gl.level.monsters[x][y] : (struct monst *) 0) + (MON_AT(x, y) ? svl.level.monsters[x][y] : (struct monst *) 0) #define m_buried_at(x, y) \ - (MON_BURIED_AT(x, y) ? gl.level.monsters[x][y] : (struct monst *) 0) + (MON_BURIED_AT(x, y) ? svl.level.monsters[x][y] : (struct monst *) 0) #else /* without 'mburied' */ -#define MON_AT(x, y) (gl.level.monsters[x][y] != (struct monst *) 0) -#define m_at(x, y) (gl.level.monsters[x][y]) +#define MON_AT(x, y) (svl.level.monsters[x][y] != (struct monst *) 0) +#define m_at(x, y) (svl.level.monsters[x][y]) #define m_buried_at(x, y) ((struct monst *) 0) #endif /* restricted movement, potential luck penalties */ -#define Sokoban gl.level.flags.sokoban_rules +#define Sokoban svl.level.flags.sokoban_rules /* * These prototypes are in extern.h but some of the code which uses them diff --git a/include/seffects.h b/include/seffects.h index ebb60adace..67f0e7f53f 100644 --- a/include/seffects.h +++ b/include/seffects.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 seffects.h $NHDT-Date: $ $NHDT-Branch: $:$NHDT-Revision: $ */ +/* NetHack 3.7 seffects.h $NHDT-Date: 1693253118 2023/08/28 20:05:18 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.3 $ */ /* Copyright (c) Michael Allison, 2023 */ /* NetHack may be freely redistributed. See license for details. */ @@ -193,6 +193,7 @@ seffect(squeak_G), seffect(squeak_G_sharp), seffect(squeal), + seffect(squelch), seffect(stone_breaking), seffect(stone_crumbling), seffect(swoosh), diff --git a/include/selvar.h b/include/selvar.h new file mode 100644 index 0000000000..2551efa344 --- /dev/null +++ b/include/selvar.h @@ -0,0 +1,11 @@ +/* NetHack 3.7 selvar.h $NHDT-Date: 1709677544 2024/03/05 22:25:44 $ $NHDT-Branch: keni-mdlib-followup $:$NHDT-Revision: 1.0 $ */ +/* Copyright (c) 2024 by Pasi Kallinen */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef SELVAR_H +#define SELVAR_H + +typedef void (*select_iter_func)(coordxy, coordxy, genericptr); + +#endif /* SELVAR_H */ + diff --git a/include/skills.h b/include/skills.h index ecce9542dd..307694cfb9 100644 --- a/include/skills.h +++ b/include/skills.h @@ -82,7 +82,7 @@ enum p_skills { /* * These are the standard weapon skill levels. It is important that - * the lowest "valid" skill be be 1. The code calculates the + * the lowest "valid" skill be 1. The code calculates the * previous amount to practice by calling practice_needed_to_advance() * with the current skill-1. To work out for the UNSKILLED case, * a value of 0 needed. diff --git a/include/sndprocs.h b/include/sndprocs.h index 9d9d981c08..e27793e45c 100755 --- a/include/sndprocs.h +++ b/include/sndprocs.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 sndprocs.h $NHDT-Date: $ $NHDT-Branch: $:$NHDT-Revision: $ */ +/* NetHack 3.7 sndprocs.h $NHDT-Date: 1725653015 2024/09/06 20:03:35 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.26 $ */ /* Copyright (c) Michael Allison, 2022 */ /* NetHack may be freely redistributed. See license for details. */ @@ -69,7 +69,8 @@ struct sound_voice { extern struct sound_procs sndprocs; -#define SOUNDID(soundname) #soundname, ((enum soundlib_ids) soundlib_##soundname) +#define SOUNDID(soundname) \ + #soundname, ((enum soundlib_ids) soundlib_##soundname) /* * Types of triggers @@ -157,11 +158,12 @@ enum ambiences { enum voice_moreinfo { voice_nothing_special, - voice_talking_artifact = 0x0001, - voice_deity = 0x0002, - voice_oracle = 0x0004, - voice_throne = 0x0008, - voice_death = 0x0010 + voice_audioassistant = 0x0001, /* accessibility */ + voice_talking_artifact = 0x0002, + voice_deity = 0x0004, + voice_oracle = 0x0008, + voice_throne = 0x0010, + voice_death = 0x0020 }; enum achievements_arg2 { @@ -294,7 +296,7 @@ enum findsound_approaches { enum sound_file_flags { sff_default, /* add dir prefix + '/' + sound + suffix */ sff_base_only, /* base sound name only, no dir, no suffix */ - sff_havedir_append_rest, /* dir provided, append base sound name + suffix */ + sff_havedir_append_rest, /* dir provided, append base sound name+suffix */ sff_baseknown_add_rest /* base is already known, add dir and suffix */ }; diff --git a/include/sp_lev.h b/include/sp_lev.h index 6ae1488b5d..3b77359998 100644 --- a/include/sp_lev.h +++ b/include/sp_lev.h @@ -84,6 +84,11 @@ enum lvlinit_types { #define SPACELOC 0x40 /* like DRY, but accepts furniture too */ #define NOFLOOR 0x80 /* allows open air to be selected */ +/* 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)) @@ -166,7 +171,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; xint8 material; } object; diff --git a/include/spell.h b/include/spell.h index 66208434c4..9299282e2a 100644 --- a/include/spell.h +++ b/include/spell.h @@ -28,9 +28,9 @@ enum spellknowledge { #define ALL_MAP 0x1 #define ALL_SPELLS 0x2 -#define decrnknow(spell) gs.spl_book[spell].sp_know-- -#define spellid(spell) gs.spl_book[spell].sp_id -#define spellknow(spell) gs.spl_book[spell].sp_know +#define decrnknow(spell) svs.spl_book[spell].sp_know-- +#define spellid(spell) svs.spl_book[spell].sp_id +#define spellknow(spell) svs.spl_book[spell].sp_know /* how much Pw a spell of level lvl costs to cast? */ #define SPELL_LEV_PW(lvl) ((lvl) * 5) diff --git a/include/stairs.h b/include/stairs.h new file mode 100644 index 0000000000..bb37a4dee2 --- /dev/null +++ b/include/stairs.h @@ -0,0 +1,18 @@ +/* NetHack 3.7 stairs.h $NHDT-Date: 1685863327 2023/06/04 07:22:07 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.47 $ */ +/* Copyright (c) 2024 by Pasi Kallinen */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef STAIRS_H +#define STAIRS_H + +typedef struct stairway { /* basic stairway identifier */ + coordxy sx, sy; /* x / y location of the stair */ + d_level tolev; /* where does it go */ + boolean up; /* up or down? */ + boolean isladder; /* ladder or stairway? */ + boolean u_traversed; /* hero has traversed this stair */ + struct stairway *next; +} stairway; + +#endif /* STAIRS_H */ + diff --git a/include/sym.h b/include/sym.h index 1af9f2d854..34ee334a4d 100644 --- a/include/sym.h +++ b/include/sym.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 sym.h */ +/* NetHack 3.7 sym.h $NHDT-Date: $ $NHDT-Branch: $ $NHDT-Revision: $ */ /* Copyright (c) 2016 by Pasi Kallinen */ /* NetHack may be freely redistributed. See license for details. */ @@ -52,12 +52,11 @@ enum symset_handling_types { struct symdef { uchar sym; const char *explanation; -#ifdef TEXTCOLOR uchar color; -#endif }; enum symparse_range { + SYM_INVALID = 0, SYM_CONTROL = 1, /* start/finish markers */ SYM_PCHAR = 2, /* index into showsyms */ SYM_OC = 3, /* index into oc_syms */ @@ -104,9 +103,9 @@ struct symsetentry { #define is_cmap_corr(i) ((i) >= S_corr && (i) <= S_litcorr) #define is_cmap_furniture(i) ((i) >= S_upstair && (i) <= S_fountain) #define is_cmap_water(i) ((i) == S_pool || (i) == S_water) -#define is_cmap_lava(i) ((i) == S_lava) -#define is_cmap_stairs(i) ((i) == S_upstair || (i) == S_dnstair || \ - (i) == S_upladder || (i) == S_dnladder) +#define is_cmap_lava(i) ((i) == S_lava || (i) == S_lavawall) +#define is_cmap_stairs(i) ((i) >= S_upstair && (i) <= S_brdnladder) +#define is_cmap_engraving(i) ((i) == S_engroom || (i) == S_engrcorr) /* misc symbol definitions */ enum misc_symbols { @@ -130,8 +129,14 @@ enum graphics_sets { UNICODESET = NUM_GRAPHICS }; -#ifdef ENHANCED_SYMBOLS -enum customization_types { custom_none, custom_symbols, custom_ureps }; +enum do_customizations { + do_custom_none, + do_custom_colors, + do_custom_symbols +}; + +enum customization_types { custom_none, custom_symbols, + custom_ureps, custom_nhcolor, custom_count }; struct custom_symbol { const struct symparse *symparse; @@ -141,9 +146,14 @@ struct custom_urep { int glyphidx; struct unicode_representation u; }; +struct custom_nhcolor { + int glyphidx; + uint32 nhcolor; +}; union customization_content { struct custom_symbol sym; struct custom_urep urep; + struct custom_nhcolor ccolor; }; struct customization_detail { union customization_content content; @@ -155,11 +165,10 @@ struct customization_detail { struct symset_customization { const char *customization_name; int count; - int custtype; + enum customization_types custtype; struct customization_detail *details; struct customization_detail *details_end; }; -#endif /* ENHANCED_SYMBOLS */ extern const struct symdef defsyms[MAXPCHARS + 1]; /* defaults */ #define WARNCOUNT 6 /* number of different warning levels */ diff --git a/include/sys.h b/include/sys.h index 137f365b8f..552eea4194 100644 --- a/include/sys.h +++ b/include/sys.h @@ -1,11 +1,11 @@ -/* NetHack 3.7 sys.h $NHDT-Date: 1646255373 2022/03/02 21:09:33 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.38 $ */ +/* NetHack 3.7 sys.h $NHDT-Date: 1693083207 2023/08/26 20:53:27 $ $NHDT-Branch: keni-crashweb2 $:$NHDT-Revision: 1.41 $ */ /* Copyright (c) Kenneth Lorber, Kensington, Maryland, 2008. */ /* NetHack may be freely redistributed. See license for details. */ #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 */ char *dumplogurl; /* url path for the above */ @@ -46,6 +47,7 @@ struct sysopt { /* panic options */ char *gdbpath; char *greppath; + char *crashreporturl; int panictrace_gdb; int panictrace_libc; @@ -64,7 +66,7 @@ struct sysopt { * 1: suppress it */ }; -extern struct sysopt sysopt; +extern struct sysopt_s sysopt; #define SYSOPT_SEDUCE sysopt.seduce diff --git a/include/tcap.h b/include/tcap.h index 508d9432ef..896c00e6f0 100644 --- a/include/tcap.h +++ b/include/tcap.h @@ -47,12 +47,10 @@ extern struct tc_lcl_data { /* defined and set up in termcap.c */ extern short ospeed; /* set up in termcap.c */ -#ifdef TEXTCOLOR #ifdef TOS extern const char *hilites[CLR_MAX]; #else extern NEARDATA char *hilites[CLR_MAX]; #endif -#endif #endif /* TCAP_H */ diff --git a/include/timeout.h b/include/timeout.h index 9223e0c790..4897d8b5b8 100644 --- a/include/timeout.h +++ b/include/timeout.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 timeout.h $NHDT-Date: 1596498564 2020/08/03 23:49:24 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.13 $ */ +/* NetHack 3.7 timeout.h $NHDT-Date: 1705087443 2024/01/12 19:24:03 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.23 $ */ /* Copyright 1994, Dean Luick */ /* NetHack may be freely redistributed. See license for details. */ @@ -10,11 +10,12 @@ typedef void (*timeout_proc)(ANY_P *, long); /* kind of timer */ enum timer_type { - TIMER_LEVEL = 0, /* event specific to level [melting ice] */ - TIMER_GLOBAL = 1, /* event follows current play [not used] */ - TIMER_OBJECT = 2, /* event follows an object [various] */ - TIMER_MONSTER = 3, /* event follows a monster [not used] */ - NUM_TIMER_KINDS /* 4 */ + TIMER_NONE = 0, + TIMER_LEVEL = 1, /* event specific to level [melting ice] */ + TIMER_GLOBAL = 2, /* event follows current play [not used] */ + TIMER_OBJECT = 3, /* event follows an object [various] */ + TIMER_MONSTER = 4, /* event follows a monster [not used] */ + NUM_TIMER_KINDS /* 5 */ }; /* save/restore timer ranges */ @@ -22,8 +23,16 @@ enum timer_type { #define RANGE_GLOBAL 1 /* save/restore timers following global play */ /* - * Timeout functions. Add a define here, then put it in the table + * Timeout functions. Add an enum here, then put it in the table * in timeout.c. "One more level of indirection will fix everything." + * Also add it to timerstr[] in nhl_get_timertype(nhlua.c); the entries + * there match these but are spelled differently. + * + * Note: if any are inserted, removed, or reordered then EDITLEVEL + * needs to be incremented because timeout indices get written into save + * and bones files if any timers are present while saving. (Adding new + * ones at the end isn't restricted this way since new indices won't be + * present in old data.) */ enum timeout_types { ROT_ORGANIC = 0, /* for buried organics */ @@ -33,10 +42,10 @@ enum timeout_types { BURN_OBJECT, HATCH_EGG, FIG_TRANSFORM, - MELT_ICE_AWAY, SHRINK_GLOB, MOLDY_CORPSE, FERMENT, + MELT_ICE_AWAY, NUM_TIME_FUNCS }; @@ -49,7 +58,9 @@ enum timeout_types { || (ttype) == BURN_OBJECT \ || (ttype) == HATCH_EGG \ || (ttype) == FIG_TRANSFORM \ - || (ttype) == SHRINK_GLOB) + || (ttype) == SHRINK_GLOB \ + || (ttype) == MOLDY_CORPSE \ + || (ttype) == FERMENT) /* used in timeout.c */ typedef struct fe { diff --git a/include/tradstdc.h b/include/tradstdc.h index 13302c604d..f2ebeb74e1 100644 --- a/include/tradstdc.h +++ b/include/tradstdc.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 tradstdc.h $NHDT-Date: 1596498565 2020/08/03 23:49:25 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.37 $ */ +/* NetHack 3.7 tradstdc.h $NHDT-Date: 1685522034 2023/05/31 08:33:54 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.54 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -172,77 +172,8 @@ typedef const char *vA; #endif /* NEED_VARARGS */ -#if defined(NHSTDC) || defined(MSDOS) || defined(MAC) \ - || defined(ULTRIX_PROTO) || defined(__BEOS__) - -/* - * Used for robust ANSI parameter forward declarations: - * int VDECL(sprintf, (char *, const char *, ...)); - * - * VDECL() is used for functions with a variable number of arguments. - * Separate macros are needed because ANSI will mix old-style declarations - * with prototypes, except in the case of varargs - */ - -#if defined(MSDOS) || defined(USE_STDARG) -#define VDECL(f, p) f p -#else -#define VDECL(f, p) f() -#endif - /* generic pointer, always a macro; genericptr_t is usually a typedef */ #define genericptr void * - -#if (defined(ULTRIX_PROTO) && !defined(__GNUC__)) || defined(OS2_CSET2) -/* Cover for Ultrix on a DECstation with 2.0 compiler, which coredumps on - * typedef void * genericptr_t; - * extern void a(void(*)(int, genericptr_t)); - * Using the #define is OK for other compiler versions too. - */ -/* And IBM CSet/2. The redeclaration of free hoses the compile. */ -#define genericptr_t genericptr -#else -#if !defined(NHSTDC) && !defined(MAC) -#define const -#define signed -#define volatile -#endif -#endif - -/* - * Suppress `const' if necessary and not handled elsewhere. - * Don't use `#if defined(xxx) && !defined(const)' - * because some compilers choke on `defined(const)'. - * This has been observed with Lattice, MPW, and High C. - */ -#if (defined(ULTRIX_PROTO) && !defined(NHSTDC)) || defined(apollo) -/* the system header files don't use `const' properly */ -#ifndef const -#define const -#endif -#endif - -#else /* NHSTDC */ /* a "traditional" C compiler */ - -#define VDECL(f, p) f() - -#if defined(AMIGA) || defined(HPUX) || defined(POSIX_TYPES) \ - || defined(__DECC) || defined(__BORLANDC__) -#define genericptr void * -#endif -#ifndef genericptr -#define genericptr char * -#endif - -/* - * Traditional C compilers don't have "signed", "const", or "volatile". - */ -#define signed -#define const -#define volatile - -#endif /* NHSTDC */ - #ifndef genericptr_t typedef genericptr genericptr_t; /* (void *) or (char *) */ #endif @@ -386,27 +317,100 @@ typedef genericptr genericptr_t; /* (void *) or (char *) */ #undef signed #endif -#ifdef __clang__ -/* clang's gcc emulation is sufficient for nethack's usage */ -#ifndef __GNUC__ -#define __GNUC__ 5 /* high enough for returns_nonnull */ -#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 /* - * Give first priority to standard + * noreturn */ #ifndef ATTRNORETURN -#if defined(__STDC_VERSION__) || defined(__cplusplus) -#if (__STDC_VERSION__ > 202300L) || defined(__cplusplus) #define ATTRNORETURN [[noreturn]] -#endif +/* #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) */ +#endif /* NH_C >= 202300L */ + +/* + * Compiler-specific + */ + +#ifdef __clang__ +/* clang's gcc emulation is sufficient for nethack's usage */ +#ifndef __GNUC__ +#define __GNUC__ 5 /* high enough for returns_nonnull */ #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) @@ -420,26 +424,84 @@ typedef genericptr genericptr_t; /* (void *) or (char *) */ #ifndef ATTRNORETURN #ifndef NORETURN #define NORETURN __attribute__((noreturn)) -#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 /* __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 -#endif -#if __GNUC__ >= 5 +#endif /* GCC_URWARN || !__linux || !MACOS */ +#endif /* __GNUC__ || clang masquerading as __GNUC__==5 */ + +/* + * 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)) -#endif -#endif +#define NONNULLPTRS __attribute__((nonnull)) +#define NONNULLARG1 __attribute__((nonnull (1))) +#define NONNULLARG2 __attribute__((nonnull (2))) +#define NONNULLARG3 __attribute__((nonnull (3))) +#define NONNULLARG4 __attribute__((nonnull (4))) +#define NONNULLARG5 __attribute__((nonnull (5))) +#define NONNULLARG6 __attribute__((nonnull (6))) +#define NONNULLARG7 __attribute__((nonnull (7))) /* for bhit() */ +#define NONNULLARG12 __attribute__((nonnull (1, 2))) +#define NONNULLARG23 __attribute__((nonnull (2, 3))) +#define NONNULLARG123 __attribute__((nonnull (1, 2, 3))) +#define NONNULLARG13 __attribute__((nonnull (1, 3))) +#define NONNULLARG14 __attribute__((nonnull (1, 4))) /* for query_category */ +#define NONNULLARG134 __attribute__((nonnull (1, 3, 4))) /* for do_stone_mon */ +#define NONNULLARG145 __attribute__((nonnull (1, 4, 5))) /* find_roll_to_hit */ +#define NONNULLARG17 __attribute__((nonnull (1, 7))) /* for askchain() */ +#define NONNULLARG24 __attribute__((nonnull (2, 4))) /* query_objlist() */ +#define NONNULLARG45 __attribute__((nonnull (4, 5))) /* do_screen_descri... */ +#define NONNULLS_DEFINED +#undef DO_DEFINE_NONNULLS +#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 -#endif +/* #pragma message is available */ +#define NH_PRAGMA_MESSAGE 1 +#endif /* _MSC_VER */ + +/* Fallback implementations */ #ifndef PRINTF_F #define PRINTF_F(f, v) #endif @@ -449,15 +511,40 @@ typedef genericptr genericptr_t; /* (void *) or (char *) */ #ifndef UNUSED #define UNUSED #endif +#ifndef FALLTHROUGH +#define FALLTHROUGH +#endif #ifndef ATTRNORETURN #define ATTRNORETURN #endif #ifndef NORETURN #define NORETURN #endif -#ifndef NONNULL +#ifndef NONNULLS_DEFINED #define NONNULL -#endif +#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 NONNULLS_DEFINED +#endif /* NONNULLS_DEFINED */ +#ifndef NO_NNARGS +#define NO_NNARGS /*empty*/ +#endif /* NO_NNARGS */ /* * Allow gcc and clang to catch the use of non-C99 functions that diff --git a/include/trap.h b/include/trap.h index 68346b8008..aba92d5c53 100644 --- a/include/trap.h +++ b/include/trap.h @@ -103,13 +103,22 @@ enum trap_types { }; /* some trap-related function return results */ -enum { Trap_Effect_Finished = 0, - Trap_Is_Gone = 0, - Trap_Caught_Mon = 1, - Trap_Killed_Mon = 2, - Trap_Moved_Mon = 3, /* new location, or new level */ +enum trap_result { + Trap_Effect_Finished = 0, + Trap_Is_Gone = 0, + Trap_Caught_Mon = 1, + Trap_Killed_Mon = 2, + Trap_Moved_Mon = 3, /* new location, or new level */ }; +/* return codes from immune_to_trap() */ +enum trap_immunities { + TRAP_NOT_IMMUNE = 0, + TRAP_CLEARLY_IMMUNE = 1, + TRAP_HIDDEN_IMMUNE = 2, +}; + + #define is_pit(ttyp) ((ttyp) == PIT || (ttyp) == SPIKED_PIT) #define is_hole(ttyp) ((ttyp) == HOLE || (ttyp) == TRAPDOOR) #define unhideable_trap(ttyp) ((ttyp) == HOLE \ @@ -123,6 +132,8 @@ enum { Trap_Effect_Finished = 0, || (ttyp) == POLY_TRAP) /* "transportation" traps */ #define is_xport(ttyp) ((ttyp) >= TELEP_TRAP && (ttyp) <= MAGIC_PORTAL) +#define fixed_tele_trap(t) ((t)->ttyp == TELEP_TRAP \ + && isok((t)->teledest.x,(t)->teledest.y)) /* List of traps that can be triggered by interacting with a door. */ enum doortrap_types { @@ -140,13 +151,6 @@ enum doortrap_types { * hurtle(u.ux - doorx, u.uy - doory, 1, FALSE) */ }; -/* Return codes from immune_to_trap. */ -enum trap_immunities { - TRAP_NOT_IMMUNE = 0, - TRAP_CLEARLY_IMMUNE, - TRAP_HIDDEN_IMMUNE -}; - /* Values for deltrap_with_ammo */ enum deltrap_handle_ammo { DELTRAP_RETURN_AMMO = 0, /* return ammo to caller; do nothing with it */ diff --git a/include/unixconf.h b/include/unixconf.h index 30edb10755..362c46afdc 100644 --- a/include/unixconf.h +++ b/include/unixconf.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 unixconf.h $NHDT-Date: 1607461111 2020/12/08 20:58:31 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.49 $ */ +/* NetHack 3.7 unixconf.h $NHDT-Date: 1711213886 2024/03/23 17:11:26 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.57 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Pasi Kallinen, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -55,11 +55,6 @@ * particular, it should NOT be defined for the UNIXPC * unless you remove the use of the shared library in * the Makefile */ -#define TEXTCOLOR /* Use System V r3.2 terminfo color support - * and/or ANSI color support on termcap systems - * and/or X11 color. Note: if you get compiler - * warnings about 'has_colors()' being implicitly - * declared, uncomment NEED_HAS_COLORS_DECL below. */ #define POSIX_JOB_CONTROL /* use System V / Solaris 2.x / POSIX job control * (e.g., VSUSP) */ #define POSIX_TYPES /* use POSIX types for system calls and termios */ @@ -101,7 +96,7 @@ /* #define DEF_PAGER "/usr/bin/less" */ /* - * Define PORT_HELP to be the name of the port-specfic help file. + * Define PORT_HELP to be the name of the port-specific help file. * This file is found in HACKDIR. * Normally, you shouldn't need to change this. * There is currently no port-specific help for Unix systems. @@ -123,6 +118,21 @@ #define TIMED_DELAY #endif +/* + * At start of game, if there are lock and level files for current + * character in the playground directory, ask whether to recover them + * (into a save file). + */ +/* #define SELF_RECOVER */ + +/* + * At start of game, if there is no save file to restore or lock and + * level files to recover but there is a panic save file for the current + * character, tell the player that it exists and ask whether to start a + * new game. Does not attempt to rename and restore the panic save file. + */ +#define CHECK_PANIC_SAVE + /* #define AVOID_WIN_IOCTL */ /* ensure USE_WIN_IOCTL remains undefined */ /* @@ -292,8 +302,6 @@ #if defined(BSD) || defined(ULTRIX) #include -#else -#include #endif /* these might be needed for include/system.h; @@ -314,10 +322,7 @@ #define SHELL /* do not delete the '!' command */ #endif -/* #include "system.h" */ - #if defined(POSIX_TYPES) || defined(__GNUC__) -#include #include #endif @@ -365,7 +370,7 @@ #endif #endif -/* Relevant for some systems which enable TEXTCOLOR: some older versions +/* Relevant for some systems: some older versions of curses (the run-time library optionally used by nethack's tty interface in addition to its curses interface) supply 'has_colors()' but corresponding doesn't declare it. has_colors() is used diff --git a/include/vision.h b/include/vision.h index f2cd9fa6ea..a9be002dc8 100644 --- a/include/vision.h +++ b/include/vision.h @@ -12,8 +12,12 @@ /* * Light source sources */ -#define LS_OBJECT 0 -#define LS_MONSTER 1 +enum ls_sources { + LS_NONE = 0, + LS_OBJECT = 1, + LS_MONSTER = 2, + NUM_LS_SOURCES +}; /* * cansee() - Returns true if the hero can see the location. diff --git a/include/vmsconf.h b/include/vmsconf.h index 1ab86b3c5e..57b8a7b051 100644 --- a/include/vmsconf.h +++ b/include/vmsconf.h @@ -92,17 +92,6 @@ PANICTRACE_GDB=2 #at conclusion of panic, show a call traceback and then */ #define SELECTSAVED -/* - * You may define TEXTCOLOR if your system has any terminals that recognize - * ANSI color sequences of the form ``[#;#m'', where the first # is - * a number between 40 and 47 represented background color, and the second - * # is a number between 30 and 37 representing the foreground color. - * GIGI terminals and DECterm windows on color VAXstations support these - * color escape sequences, as do some 3rd party terminals and many micro - * computers. - */ -/* #define TEXTCOLOR */ - /* * If you define USE_QIO_INPUT, then you'll get raw characters from the * keyboard, not unlike those of the unix version of Nethack. This will @@ -224,7 +213,6 @@ PANICTRACE_GDB=2 #at conclusion of panic, show a call traceback and then #include #include #include -#include #include #include #include @@ -257,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 @@ -311,7 +297,7 @@ typedef int32_t off_t; #define link(f1, f2) vms_link(f1, f2) /* vmsfiles.c */ #define open(f, k, m) vms_open(f, k, m) /* vmsfiles.c */ #define fopen(f, m) vms_fopen(f, m) /* vmsfiles.c */ -/* #define unlink(f0) vms_unlink(f0) /* vmsfiles.c */ +/* #define unlink(f0) vms_unlink(f0) */ /* vmsfiles.c */ #ifdef VERYOLD_VMS #define unlink(f0) delete (f0) /* vaxcrtl */ #else diff --git a/include/warnings.h b/include/warnings.h index d0971893ce..b35a037ae9 100644 --- a/include/warnings.h +++ b/include/warnings.h @@ -42,11 +42,11 @@ #ifdef ACTIVATE_WARNING_PRAGMAS #if defined(__clang__) #define DISABLE_WARNING_UNREACHABLE_CODE \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Wunreachable-code\"") + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wunreachable-code\"") #define DISABLE_WARNING_FORMAT_NONLITERAL \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Wformat-nonliteral\"") + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wformat-nonliteral\"") #define DISABLE_WARNING_CONDEXPR_IS_CONSTANT #define RESTORE_WARNING_CONDEXPR_IS_CONSTANT #define RESTORE_WARNING_FORMAT_NONLITERAL _Pragma("clang diagnostic pop") @@ -55,13 +55,14 @@ #define STDC_Pragma_AVAILABLE #elif defined(__GNUC__) -/* unlike in clang, -Wunreachable-code does not function in later versions of gcc */ +/* unlike in clang, -Wunreachable-code does not function in later versions + of gcc [this may be an issue of requiring -O1 or higher] */ #define DISABLE_WARNING_UNREACHABLE_CODE \ - _Pragma("GCC diagnostic push") \ - _Pragma("GCC diagnostic ignored \"-Wunreachable-code\"") + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wunreachable-code\"") #define DISABLE_WARNING_FORMAT_NONLITERAL \ - _Pragma("GCC diagnostic push") \ - _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") #define DISABLE_WARNING_CONDEXPR_IS_CONSTANT #define RESTORE_WARNING_CONDEXPR_IS_CONSTANT #define RESTORE_WARNING_FORMAT_NONLITERAL _Pragma("GCC diagnostic pop") @@ -72,14 +73,14 @@ #elif defined(_MSC_VER) #if _MSC_VER > 1916 #define DISABLE_WARNING_UNREACHABLE_CODE \ - _Pragma("warning( push )") \ - _Pragma("warning( disable : 4702 )") + _Pragma("warning( push )") \ + _Pragma("warning( disable : 4702 )") #define DISABLE_WARNING_FORMAT_NONLITERAL \ - _Pragma("warning( push )") \ - _Pragma("warning( disable : 4774 )") + _Pragma("warning( push )") \ + _Pragma("warning( disable : 4774 )") #define DISABLE_WARNING_CONDEXPR_IS_CONSTANT \ - _Pragma("warning( push )") \ - _Pragma("warning( disable : 4127 )") + _Pragma("warning( push )") \ + _Pragma("warning( disable : 4127 )") #define RESTORE_WARNING_CONDEXPR_IS_CONSTANT _Pragma("warning( pop )") #define RESTORE_WARNING_FORMAT_NONLITERAL _Pragma("warning( pop )") #define RESTORE_WARNING_UNREACHABLE_CODE _Pragma("warning( pop )") @@ -87,14 +88,14 @@ #define STDC_Pragma_AVAILABLE #else /* Visual Studio prior to 2019 below */ #define DISABLE_WARNING_UNREACHABLE_CODE \ - __pragma(warning(push)) \ - __pragma(warning(disable:4702)) + __pragma(warning(push)) \ + __pragma(warning(disable:4702)) #define DISABLE_WARNING_FORMAT_NONLITERAL \ - __pragma(warning(push)) \ - __pragma(warning(disable:4774)) + __pragma(warning(push)) \ + __pragma(warning(disable:4774)) #define DISABLE_WARNING_CONDEXPR_IS_CONSTANT \ - __pragma(warning(push)) \ - __pragma(warning(disable:4127)) + __pragma(warning(push)) \ + __pragma(warning(disable:4127)) #define RESTORE_WARNING_CONDEXPR_IS_CONSTANT __pragma(warning(pop)) #define RESTORE_WARNING_FORMAT_NONLITERAL __pragma(warning(pop)) #define RESTORE_WARNING_UNREACHABLE_CODE __pragma(warning(pop)) diff --git a/include/winX.h b/include/winX.h index 98bf68e9f9..3da329d7d0 100644 --- a/include/winX.h +++ b/include/winX.h @@ -49,7 +49,6 @@ typedef unsigned char X11_color; #endif struct text_map_info_t { X11_map_symbol text[ROWNO][COLNO]; /* Actual displayed screen. */ -#ifdef TEXTCOLOR X11_color colors[ROWNO][COLNO]; /* Color of each character. */ X11_color framecolors[ROWNO][COLNO]; /* Color of background behind text */ @@ -57,7 +56,8 @@ struct text_map_info_t { inv_color_gcs[CLR_MAX]; /* GC for each inverse color */ #define copy_gc color_gcs[NO_COLOR] #define inv_copy_gc inv_color_gcs[NO_COLOR] -#else +#if 0 + /* was else from old textcolor days */ GC copy_gc, /* Drawing GC */ inv_copy_gc; /* Inverse drawing GC */ #endif @@ -157,6 +157,7 @@ typedef struct x11_mi { long pick_count; /* specific selection count; -1 if none */ char *str; /* The text of the item. */ int attr; /* Attribute for the line. */ + int color; /* Color for the line. */ boolean selected; /* Been selected? */ boolean preselected; /* in advance? */ unsigned itemflags; /* MENU_ITEMFLAGS_foo */ @@ -266,7 +267,6 @@ struct xwindow { /* event into a character(s) */ #define DEFAULT_LINES_DISPLAYED 12 /* # of lines displayed message window */ -#define MAX_HISTORY 60 /* max history saved on message window */ /* flags for X11_yn_function_core() */ #define YN_NORMAL 0U /* no flags */ @@ -489,10 +489,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 07e6083d22..4c5b7f19cf 100644 --- a/include/wincurs.h +++ b/include/wincurs.h @@ -15,7 +15,6 @@ extern WINDOW *mapwin, *statuswin, *messagewin; /* Main windows */ extern WINDOW *activemenu; /* curses window for menu requesting a * count; affects count_window refresh */ -#define TEXTCOLOR /* Allow color */ #define NHW_END 19 #define OFF 0 #define ON 1 @@ -33,8 +32,9 @@ extern WINDOW *activemenu; /* curses window for menu requesting a #define STATUS_WIN 2 #define MAP_WIN 3 #define INV_WIN 4 -#define NHWIN_MAX 5 -#define MESG_HISTORY_MAX 200 +#define TEXT_WIN 5 +#define MENU_WIN 6 +#define NHWIN_MAX 7 #if !defined(__APPLE__) || !defined(NCURSES_VERSION) # define USE_DARKGRAY /* Allow "bright" black; delete if not visible */ #endif /* !__APPLE__ && !PDCURSES */ @@ -47,6 +47,9 @@ extern WINDOW *activemenu; /* curses window for menu requesting a #if !defined(A_RIGHTLINE) && defined(A_RIGHT) #define A_RIGHTLINE A_RIGHT #endif +#ifndef A_ITALIC +#define A_ITALIC A_UNDERLINE +#endif typedef enum orient_type { @@ -79,6 +82,7 @@ extern void curses_display_nhwindow(winid wid, boolean block); extern void curses_destroy_nhwindow(winid wid); extern void curses_curs(winid wid, int x, int y); extern void curses_putstr(winid wid, int attr, const char *text); +extern void curses_putmixed(winid window, int attr, const char *str); extern void curses_display_file(const char *filename, boolean must_exist); extern void curses_start_menu(winid wid, unsigned long); extern void curses_add_menu(winid wid, const glyph_info *, @@ -104,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); @@ -115,7 +117,9 @@ extern win_request_info *curses_ctrl_nhwindow(winid, int, win_request_info *); /* curswins.c */ -extern WINDOW *curses_create_window(int width, int height, orient orientation); +extern WINDOW *curses_create_window(int, int, int, orient); +extern void curses_set_wid_colors(int wid, WINDOW *win); +extern void curses_parse_wid_colors(int wid, char *fg, char *bg); extern void curses_destroy_win(WINDOW *win); extern WINDOW *curses_get_nhwin(winid wid); extern void curses_add_nhwin(winid wid, int height, int width, int y, @@ -143,6 +147,7 @@ extern void curses_puts(winid wid, int attr, const char *text); extern void curses_clear_nhwin(winid wid); extern void curses_alert_win_border(winid wid, boolean onoff); extern void curses_alert_main_borders(boolean onoff); +extern int get_framecolor(int nhcolor, int framecolor); extern void curses_draw_map(int sx, int sy, int ex, int ey); extern boolean curses_map_borders(int *sx, int *sy, int *ex, int *ey, int ux, int uy); @@ -170,9 +175,7 @@ extern void curses_posthousekeeping(void); extern void curses_view_file(const char *filename, boolean must_exist); extern void curses_rtrim(char *str); extern long curses_get_count(int first_digit); -extern int curses_convert_attr(int attr); -extern int curses_read_attrs(const char *attrs); -extern char *curses_fmt_attrs(char *); +extern attr_t curses_convert_attr(int attr); extern int curses_convert_keys(int key); extern int curses_get_mouse(coordxy *mousex, coordxy *mousey, int *mod); extern void curses_mouse_support(int); @@ -187,7 +190,7 @@ extern int curses_ext_cmd(void); extern void curses_create_nhmenu(winid wid, unsigned long); extern void curses_add_nhmenu_item(winid wid, const glyph_info *, const ANY_P *identifier, char accelerator, - char group_accel, int attr, + char group_accel, int attr, int clr, const char *str, unsigned itemflags); extern void curs_menu_set_bottom_heavy(winid); extern void curses_finalize_nhmenu(winid wid, const char *prompt); @@ -206,11 +209,13 @@ extern void curses_status_update(int, genericptr_t, int, int, int, extern void curs_purge_perminv_data(boolean); extern void curs_update_invt(int); -extern void curs_add_invt(int, char, attr_t, const char *); +extern void curs_add_invt(int, char, attr_t, int, const char *); /* cursinit.c */ extern void curses_create_main_windows(void); +extern int curses_init_rgb(int r, int g, int b); +extern int curses_init_pair(int fg, int bg); extern void curses_init_nhcolors(void); extern void curses_choose_character(void); extern int curses_character_dialog(const char **choices, const char *prompt); diff --git a/include/windconf.h b/include/windconf.h index e02d77059f..6fe9248d26 100644 --- a/include/windconf.h +++ b/include/windconf.h @@ -7,8 +7,6 @@ /* #define SHELL */ /* nt use of pcsys routines caused a hang */ -#define TEXTCOLOR /* Color text */ - #define EXEPATH /* Allow .exe location to be used as HACKDIR */ #define TRADITIONAL_GLYPHMAP /* Store glyph mappings at level change time */ @@ -82,7 +80,7 @@ */ #define CONFIG_FILE ".xnethackrc" -#define CONFIG_TEMPLATE ".xnethackrc.template" +#define CONFIG_TEMPLATE "xnethackrc.template" #define SYSCF_TEMPLATE "sysconf.template" #define SYMBOLS_TEMPLATE "symbols.template" #define GUIDEBOOK_FILE "Guidebook.txt" @@ -100,13 +98,8 @@ extern char *windows_exepath(void); *=============================================== */ -#ifdef __MINGW32__ -#if 0 +#ifdef __GNUC__ #define MD_USE_TMPFILE_S -#if !defined(__cplusplus) -extern errno_t tmpfile_s(FILE * restrict * restrict streamptr); -#endif -#endif # #ifdef strncasecmp #undef strncasecmp @@ -114,13 +107,9 @@ extern errno_t tmpfile_s(FILE * restrict * restrict streamptr); #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 @@ -176,8 +165,6 @@ typedef SSIZE_T ssize_t; #include -#include -#include #ifdef __BORLANDC__ #undef randomize #undef random @@ -196,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. */ @@ -264,9 +247,6 @@ open(const char _FAR *__path, int __access, ... /*unsigned mode*/); long _RTLENTRY _EXPFUNC lseek(int __handle, long __offset, int __fromwhere); int _RTLENTRY _EXPFUNC read(int __handle, void _FAR *__buf, unsigned __len); #endif -#ifndef CURSES_GRAPHICS -#include /* conflicting definitions with curses.h */ -#endif #undef kbhit /* Use our special NT kbhit */ #define kbhit (*nt_kbhit) @@ -288,7 +268,6 @@ 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 diff --git a/include/winprocs.h b/include/winprocs.h index 04b24a1b85..a025f6e97d 100644 --- a/include/winprocs.h +++ b/include/winprocs.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 winprocs.h $NHDT-Date: 1683748057 2023/05/10 19:47:37 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.74 $ */ +/* 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. */ @@ -14,8 +14,9 @@ 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 - , wp_mac, wp_Gem, wp_Gnome, wp_amii, wp_amiv + wp_mac, wp_Gem, wp_Gnome, wp_amii, wp_amiv, #endif + wp_trace // XXX do we need this? should chainin/out get an id? TBD }; /* NB: this MUST match chain_procs below */ @@ -79,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); @@ -107,6 +104,7 @@ extern /* * If you wish to only support one window system and not use procedure * pointers, add the appropriate #ifdef below. + * XXX which is what? */ #define init_nhwindows (*windowprocs.win_init_nhwindows) @@ -125,9 +123,14 @@ extern #define putmixed (*windowprocs.win_putmixed) #define display_file (*windowprocs.win_display_file) #define start_menu (*windowprocs.win_start_menu) -#define add_menu (*windowprocs.win_add_menu) #define end_menu (*windowprocs.win_end_menu) -#define select_menu (*windowprocs.win_select_menu) +/* 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. + * add_menu() and select_menu() are in windows.c + */ +/* #define add_menu (*windowprocs.win_add_menu) */ +/* #define select_menu (*windowprocs.win_select_menu) */ #define message_menu (*windowprocs.win_message_menu) #define mark_synch (*windowprocs.win_mark_synch) @@ -145,7 +148,12 @@ extern #define nh_poskey (*windowprocs.win_nh_poskey) #define nhbell (*windowprocs.win_nhbell) #define nh_doprev_message (*windowprocs.win_doprev_message) -#define getlin (*windowprocs.win_getlin) +/* 3.7: There is a real getlin() in the core now, which does + * some before and after activities. + * [alternative fix for menu search via ':'.] + * getlin() is in windows.c + */ +/* #define getlin (*windowprocs.win_getlin) */ #define get_ext_cmd (*windowprocs.win_get_ext_cmd) #define number_pad (*windowprocs.win_number_pad) #define nh_delay_output (*windowprocs.win_delay_output) @@ -164,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) @@ -247,13 +251,13 @@ extern #define WC2_GUICOLOR 0x2000L /* 14 display colours outside map win */ /* pline() can overload the display attributes argument passed to putstr() with one or more flags and at most one of bold/blink/inverse/&c */ -#define WC2_URGENT_MESG 0x4000L /* 15 putstr(WIN_MESSAGE) supports urgency - * via non-display attribute flag */ -#define WC2_SUPPRESS_HIST 0x8000L /* 16 putstr(WIN_MESSAGE) supports history - * suppression via non-disp attr */ +#define WC2_URGENT_MESG 0x4000L /* 15 putstr(WIN_MESSAGE) supports urgency + * via non-display attribute flag */ +#define WC2_SUPPRESS_HIST 0x8000L /* 16 putstr(WIN_MESSAGE) supports history + * suppression via non-disp attr */ #define WC2_MENU_SHIFT 0x010000L /* 17 horizontal menu scrolling */ #define WC2_U_UTF8STR 0x020000L /* 18 utf8str support */ -#define WC2_U_24BITCOLOR 0x040000L /* 19 24-bit color support available */ +#define WC2_EXTRACOLORS 0x040000L /* 19 color support beyond NH_BASIC_COLOR */ /* 13 free bits */ #define ALIGN_LEFT 1 @@ -359,7 +363,6 @@ struct chain_procs { void (*win_end_menu)(CARGS, winid, const char *); int (*win_select_menu)(CARGS, winid, int, MENU_ITEM_P **); char (*win_message_menu)(CARGS, char, int, const char *); - void (*win_update_inventory)(CARGS, int); void (*win_mark_synch)(CARGS); void (*win_wait_synch)(CARGS); #ifdef CLIPPING @@ -392,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); @@ -407,6 +406,9 @@ struct chain_procs { void (*win_status_update)(CARGS, int, genericptr_t, int, int, int, unsigned long *); boolean (*win_can_suspend)(CARGS); + void (*win_update_inventory)(CARGS, int); + win_request_info *(*win_ctrl_nhwindow)(CARGS, winid, int, + win_request_info *); }; #endif /* WINCHAIN */ @@ -466,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 ad7f7ce05d..c98db6958e 100644 --- a/include/wintty.h +++ b/include/wintty.h @@ -5,8 +5,6 @@ #ifndef WINTTY_H #define WINTTY_H -#define E extern - #ifndef WINDOW_STRUCTS #define WINDOW_STRUCTS @@ -23,7 +21,7 @@ struct tty_perminvent_cell { Bitfield(text, 1); Bitfield(glyph, 1); union ttycellcontent content; - int32_t color; /* adjusted color 0 = ignore + uint32 color; /* adjusted color 0 = ignore * 1-16 = NetHack color + 1 * 17..16,777,233 = 24-bit color + 17 */ @@ -37,6 +35,7 @@ typedef struct tty_mi { long count; /* user count */ char *str; /* description string (including accelerator) */ int attr; /* string attribute */ + int color; /* string color */ boolean selected; /* TRUE if selected by user */ unsigned itemflags; /* item flags */ char selector; /* keyboard accelerator */ @@ -45,16 +44,17 @@ typedef struct tty_mi { /* descriptor for tty-based windows */ struct WinDesc { - int flags; /* window flags */ - xint16 type; /* type of window */ - boolean active; /* true if window is active */ - short offx, offy; /* offset from topleft of display */ - long rows, cols; /* dimensions */ - long curx, cury; /* current cursor position */ - long maxrow, maxcol; /* the maximum size used -- for MENU wins */ + int flags; /* window flags */ + xint16 type; /* type of window */ + boolean active; /* true if window is active */ + boolean blanked; /* for erase_tty_screen(); not used [yet?] */ + short offx, offy; /* offset from topleft of display */ + long rows, cols; /* dimensions */ + long curx, cury; /* current cursor position */ + long maxrow, maxcol; /* the maximum size used -- for MENU wins; + * maxcol is also used by WIN_MESSAGE for + * tracking the ^P command */ unsigned long mbehavior; /* menu behavior flags (MENU) */ - /* maxcol is also used by WIN_MESSAGE for */ - /* tracking the ^P command */ short *datlen; /* allocation size for *data */ char **data; /* window data [row][column] */ char *morestr; /* string to display instead of default */ @@ -88,21 +88,21 @@ struct WinDesc { /* descriptor for tty-based displays -- all the per-display data */ struct DisplayDesc { - short rows, cols; /* width and height of tty display */ - short curx, cury; /* current cursor position on the screen */ -#ifdef TEXTCOLOR - int color; /* current color */ - uint32 framecolor; /* current background color */ -#endif - int attrs; /* attributes in effect */ - int toplin; /* flag for topl stuff */ - int rawprint; /* number of raw_printed lines since synch */ - int inmore; /* non-zero if more() is active */ - int inread; /* non-zero if reading a character */ - int intr; /* non-zero if inread was interrupted */ - winid lastwin; /* last window used for I/O */ - char dismiss_more; /* extra character accepted at --More-- */ - int topl_utf8; /* non-zero if utf8 in str */ + short rows, cols; /* width and height of tty display */ + short curx, cury; /* current cursor position on the screen */ + uint32 color; /* current color */ + uint32 colorflags; /* NH_BASIC_COLOR or 24-bit color */ + uint32 framecolor; /* current background color */ + int attrs; /* attributes in effect */ + int toplin; /* flag for topl stuff */ + int rawprint; /* number of raw_printed lines since synch */ + int inmore; /* non-zero if more() is active */ + int inread; /* non-zero if reading a character */ + int intr; /* non-zero if inread was interrupted */ + winid lastwin; /* last window used for I/O */ + char dismiss_more; /* extra character accepted at --More-- */ + int topl_utf8; /* non-zero if utf8 in str */ + int mixed; /* we are processing mixed output */ }; #endif /* WINDOW_STRUCTS */ @@ -129,6 +129,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,151 +146,159 @@ extern char defmorestr[]; /* default --more-- prompt */ /* port specific external function references */ /* ### getline.c ### */ -E void xwaitforspace(const char *); -/* ### termcap.c, video.c ### */ +extern void xwaitforspace(const char *); -E void tty_startup(int *, int *); -#ifndef NO_TERMS -E void tty_shutdown(void); -#endif -E int xputc(int); -E void xputs(const char *); -#if defined(SCREEN_VGA) || defined(SCREEN_8514) || defined(SCREEN_VESA) -E void xputg(const glyph_info *, const glyph_info *); -#endif -E void cl_end(void); -E void term_clear_screen(void); -E void home(void); -E void standoutbeg(void); -E void standoutend(void); -#if 0 -E void revbeg(void); -E void boldbeg(void); -E void blinkbeg(void); -E void dimbeg(void); -E void m_end(void); -#endif -E void backsp(void); -E void graph_on(void); -E void graph_off(void); -E void cl_eos(void); +/* ### termcap.c, video.c ### */ /* - * 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. + * 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 + * */ -E int term_attr_fixup(int); -E void term_start_attr(int attr); -E void term_end_attr(int attr); -E void term_start_raw_bold(void); -E void term_end_raw_bold(void); - -#ifdef TEXTCOLOR -E void term_end_color(void); -E void term_start_color(int color); -E void term_start_bgcolor(int color); -#endif /* TEXTCOLOR */ -#ifdef ENHANCED_SYMBOLS -extern void term_start_24bitcolor(struct unicode_representation *); -extern void term_end_24bitcolor(void); /* termcap.c, consoletty.c */ +extern void backsp(void); +extern void cl_end(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); +extern void blinkbeg(void); +extern void dimbeg(void); +extern void m_end(void); +#endif +#if defined(SCREEN_VGA) || defined(SCREEN_8514) || defined(SCREEN_VESA) +extern void xputg(const glyph_info *, const glyph_info *); #endif /* ### topl.c ### */ -E void show_topl(const char *); -E void remember_topl(void); -E void addtopl(const char *); -E void more(void); -E void update_topl(const char *); -E void putsyms(const char *); +extern void show_topl(const char *); +extern void remember_topl(void); +extern void addtopl(const char *); +extern void more(void); +extern void update_topl(const char *); +extern void putsyms(const char *); /* ### wintty.c ### */ + #ifdef CLIPPING -E void setclipped(void); +extern void setclipped(void); #endif -E void docorner(int, int, int); -E void end_glyphout(void); -E void g_putch(int); +extern void docorner(int, int, int); +extern void end_glyphout(void); +extern void g_putch(int); #ifdef ENHANCED_SYMBOLS -#if defined(WIN32) || defined(UNIX) -E void g_pututf8(uint8 *); +#if defined(WIN32) || defined(UNIX) || defined(MSDOS) +extern void g_pututf8(uint8 *); #endif #endif /* ENHANCED_SYMBOLS */ -E void win_tty_init(int); - -/* external declarations */ -E void tty_init_nhwindows(int *, char **); -E void tty_preference_update(const char *); -E void tty_player_selection(void); -E void tty_askname(void); -E void tty_get_nh_event(void); -E void tty_exit_nhwindows(const char *); -E void tty_suspend_nhwindows(const char *); -E void tty_resume_nhwindows(void); -E winid tty_create_nhwindow(int); -E void tty_clear_nhwindow(winid); -E void tty_display_nhwindow(winid, boolean); -E void tty_dismiss_nhwindow(winid); -E void tty_destroy_nhwindow(winid); -E void tty_curs(winid, int, int); -E void tty_putstr(winid, int, const char *); -E void tty_putmixed(winid window, int attr, const char *str); -E void tty_display_file(const char *, boolean); -E void tty_start_menu(winid, unsigned long); -E void tty_add_menu(winid, const glyph_info *, const ANY_P *, char, char, - int, int, const char *, unsigned int); -E void tty_end_menu(winid, const char *); -E int tty_select_menu(winid, int, MENU_ITEM_P **); -E char tty_message_menu(char, int, const char *); -E void tty_mark_synch(void); -E void tty_wait_synch(void); +extern void erase_tty_screen(void); +extern void win_tty_init(int); + +/* tty interface */ +extern void tty_init_nhwindows(int *, char **); +extern void tty_preference_update(const char *); +extern void tty_player_selection(void); +extern void tty_askname(void); +extern void tty_get_nh_event(void); +extern void tty_exit_nhwindows(const char *); +extern void tty_suspend_nhwindows(const char *); +extern void tty_resume_nhwindows(void); +extern winid tty_create_nhwindow(int); +extern void tty_clear_nhwindow(winid); +extern void tty_display_nhwindow(winid, boolean); +extern void tty_dismiss_nhwindow(winid); +extern void tty_destroy_nhwindow(winid); +extern void tty_curs(winid, int, int); +extern void tty_putstr(winid, int, const char *); +extern void tty_putmixed(winid window, int attr, const char *str); +extern void tty_display_file(const char *, boolean); +extern void tty_start_menu(winid, unsigned long); +extern void tty_add_menu(winid, const glyph_info *, const ANY_P *, char, + char, int, int, const char *, unsigned int); +extern void tty_end_menu(winid, const char *); +extern int tty_select_menu(winid, int, MENU_ITEM_P **); +extern char tty_message_menu(char, int, const char *); +extern void tty_mark_synch(void); +extern void tty_wait_synch(void); #ifdef CLIPPING -E void tty_cliparound(int, int); +extern void tty_cliparound(int, int); #endif #ifdef POSITIONBAR -E void tty_update_positionbar(char *); +extern void tty_update_positionbar(char *); #endif -E void tty_print_glyph(winid, coordxy, coordxy, const glyph_info *, - const glyph_info *); -E void tty_raw_print(const char *); -E void tty_raw_print_bold(const char *); -E int tty_nhgetch(void); -E int tty_nh_poskey(coordxy *, coordxy *, int *); -E void tty_nhbell(void); -E int tty_doprev_message(void); -E char tty_yn_function(const char *, const char *, char); -E void tty_getlin(const char *, char *); -E int tty_get_ext_cmd(void); -E void tty_number_pad(int); -E void tty_delay_output(void); +extern void tty_print_glyph(winid, coordxy, coordxy, const glyph_info *, + const glyph_info *); +extern void tty_raw_print(const char *); +extern void tty_raw_print_bold(const char *); +extern int tty_nhgetch(void); +extern int tty_nh_poskey(coordxy *, coordxy *, int *); +extern void tty_nhbell(void); +extern int tty_doprev_message(void); +extern char tty_yn_function(const char *, const char *, char); +extern void tty_getlin(const char *, char *); +extern int tty_get_ext_cmd(void); +extern void tty_number_pad(int); +extern void tty_delay_output(void); #ifdef CHANGE_COLOR -E void tty_change_color(int color, long rgb, int reverse); +extern void tty_change_color(int color, long rgb, int reverse); #ifdef MAC -E void tty_change_background(int white_or_black); -E short set_tty_font_name(winid, char *); +extern void tty_change_background(int white_or_black); +extern short set_tty_font_name(winid, char *); #endif -E char *tty_get_color_string(void); +extern char *tty_get_color_string(void); #endif -E void tty_status_enablefield(int, const char *, const char *, boolean); -E void tty_status_init(void); -E void tty_status_update(int, genericptr_t, int, int, int, unsigned long *); +extern void tty_status_enablefield(int, const char *, const char *, boolean); +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) */ -E void tty_start_screen(void); -E void tty_end_screen(void); +extern void genl_outrip(winid, int, time_t); -E void genl_outrip(winid, int, time_t); - -E char *tty_getmsghistory(boolean); -E void tty_putmsghistory(const char *, boolean); -E void tty_update_inventory(int); -E win_request_info *tty_ctrl_nhwindow(winid, int, win_request_info *); +extern char *tty_getmsghistory(boolean); +extern void tty_putmsghistory(const char *, boolean); +extern void tty_update_inventory(int); +extern win_request_info *tty_ctrl_nhwindow(winid, int, win_request_info *); #ifdef TTY_PERM_INVENT -E void tty_refresh_inventory(int start, int stop, int y); +extern void tty_refresh_inventory(int start, int stop, int y); #endif /* termcap is implied if NO_TERMS is not defined */ @@ -328,9 +338,9 @@ extern char *tgoto(const char *, int, int); #define putchar term_putc #define fflush term_flush #define puts term_puts -E int term_putc(int c); -E int term_flush(void *desc); -E int term_puts(const char *str); +extern int term_putc(int c); +extern int term_flush(void *desc); +extern int term_puts(const char *str); #endif /* MAC */ #if defined(MSDOS) || defined(WIN32) #if defined(SCREEN_BIOS) || defined(SCREEN_DJGPPFAST) || defined(WIN32) @@ -342,11 +352,9 @@ E int term_puts(const char *str); #define puts(x) xputs(x) #endif /*SCREEN_BIOS || SCREEN_DJGPPFAST || WIN32 */ #ifdef POSITIONBAR -E void video_update_positionbar(char *); +extern void video_update_positionbar(char *); #endif #endif /*MSDOS*/ #endif /* NO_TERMS */ -#undef E - #endif /* WINTTY_H */ diff --git a/include/wintype.h b/include/wintype.h index cd9592eb52..fc28d50aa2 100644 --- a/include/wintype.h +++ b/include/wintype.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 wintype.h $NHDT-Date: 1596498573 2020/08/03 23:49:33 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.23 $ */ +/* NetHack 3.7 wintype.h $NHDT-Date: 1717880364 2024/06/08 20:59:24 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.52 $ */ /* Copyright (c) David Cohrs, 1991 */ /* NetHack may be freely redistributed. See license for details. */ @@ -34,8 +34,8 @@ typedef union any { unsigned long a_mask32; /* used by status highlighting */ /* add types as needed */ } anything; -#define ANY_P union any /* avoid typedef in prototypes */ - /* (buggy old Ultrix compiler) */ +#define ANY_P union any /* avoid typedef in prototypes + * (buggy old Ultrix compiler) */ /* symbolic names for the data types housed in anything */ enum any_types { @@ -76,8 +76,6 @@ struct classic_representation { }; struct unicode_representation { - uint32 ucolor; - uint16 u256coloridx; uint32 utf32ch; uint8 *utf8str; }; @@ -85,6 +83,8 @@ struct unicode_representation { typedef struct glyph_map_entry { unsigned glyphflags; struct classic_representation sym; + uint32 customcolor; + uint16 color256idx; short int tileidx; #ifdef ENHANCED_SYMBOLS struct unicode_representation *u; @@ -94,15 +94,15 @@ typedef struct glyph_map_entry { /* glyph plus additional info if you add fields or change the ordering, fix up the following: g_info initialization in display.c - nul_glyphinfo initialization in diplay.c + nul_glyphinfo initialization in display.c */ -typedef struct gi { +typedef struct glyphinfo { int glyph; /* the display entity */ int ttychar; uint32 framecolor; glyph_map gm; } glyph_info; -#define GLYPH_INFO_P struct gi +/*#define GLYPH_INFO_P struct glyphinfo //not used*/ /* select_menu() "how" argument types */ /* [MINV_PICKMASK in monst.h assumes these have values of 0, 1, 2] */ @@ -175,9 +175,10 @@ typedef struct gi { #define MENU_INVERT_PAGE '~' #define MENU_SEARCH ':' -#define MENU_ITEMFLAGS_NONE 0x0000000U -#define MENU_ITEMFLAGS_SELECTED 0x0000001U -#define MENU_ITEMFLAGS_SKIPINVERT 0x0000002U +#define MENU_ITEMFLAGS_NONE 0x0000000U +#define MENU_ITEMFLAGS_SELECTED 0x0000001U +#define MENU_ITEMFLAGS_SKIPINVERT 0x0000002U +#define MENU_ITEMFLAGS_SKIPMENUCOLORS 0x0000004U /* 3.7+ enhanced menu flags that not all window ports are likely to * support initially. @@ -190,20 +191,45 @@ typedef struct gi { #define MENU_BEHAVE_STANDARD 0x0000000U #define MENU_BEHAVE_PERMINV 0x0000001U -enum perm_invent_toggles {toggling_off = -1, toggling_not = 0, toggling_on = 1 }; +enum perm_invent_toggles { + toggling_off = -1, + toggling_not = 0, + toggling_on = 1 +}; -/* inventory modes */ -enum inv_modes { InvNormal = 0, InvShowGold = 1, InvSparse = 2, InvInUse = 4 }; +/* perm_invent modes */ +enum inv_mode_bits { + InvNormal = 1, + InvShowGold = 2, + InvSparse = 4, /* must be ORed with Normal or ShowGold to be valid */ + InvInUse = 8 +}; +enum inv_modes { /* 'perminv_mode' option settings */ + InvOptNone = 0, /* no perm_invent */ + InvOptOn = InvNormal, /* 1 */ + InvOptFull = InvShowGold, /* 2 */ +#if 1 /*#ifdef TTY_PERM_INVENT*/ + /* confusingly-named "sparse mode" shows all inventory letters, even when + their slots are empty; only meaningful for tty's perm_invent */ + InvOptOn_grid = InvNormal | InvSparse, /* 5 */ + InvOptFull_grid = InvShowGold | InvSparse, /* 6 */ +#endif + InvOptInUse = InvInUse, /* 8 */ +}; enum to_core_flags { active = 0x001, - prohibited = 0x002, - no_init_done = 0x004 + too_small = 0x002, + prohibited = 0x004, + no_init_done = 0x008, + too_early = 0x010, }; enum from_core_requests { - set_mode = 1, - request_settings = 2, + invalid_core_request = 0, + set_mode = 1, + request_settings = 2, + set_menu_promptstyle = 3, }; struct to_core { @@ -218,6 +244,7 @@ struct to_core { struct from_core { enum from_core_requests core_request; enum inv_modes invmode; + color_attr menu_promptstyle; }; struct win_request_info_t { @@ -226,6 +253,7 @@ struct win_request_info_t { }; typedef struct win_request_info_t win_request_info; +extern win_request_info zerowri; /* windows.c */ /* #define CORE_INVENT */ diff --git a/include/you.h b/include/you.h index 4de1271c72..014d90a33c 100644 --- a/include/you.h +++ b/include/you.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 you.h $NHDT-Date: 1596498576 2020/08/03 23:49:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.48 $ */ +/* NetHack 3.7 you.h $NHDT-Date: 1702349061 2023/12/12 02:44:21 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.75 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2016. */ /* NetHack may be freely redistributed. See license for details. */ @@ -46,8 +46,8 @@ struct u_event { Bitfield(qcalled, 1); /* called by Quest leader to do task */ Bitfield(qexpelled, 1); /* expelled from the Quest dungeon */ Bitfield(qcompleted, 1); /* successfully completed Quest task */ - Bitfield(uheard_tune, 2); /* 1=know about, 2=heard passtune */ - + Bitfield(uheard_tune, 2); /* 1=know about, 2=heard passtune, 3=bridge has + * been destroyed so tune has become useless */ Bitfield(uopened_dbridge, 1); /* opened the drawbridge */ Bitfield(invoked, 1); /* invoked Gate to the Sanctum level */ Bitfield(gehennom_entered, 1); /* entered Gehennom via Valley */ @@ -171,6 +171,7 @@ struct u_roleplay { boolean nudist; /* has not worn any armor, ever */ boolean hallu; /* permanently hallucinating */ boolean deaf; /* permanently deaf */ + boolean pauper; /* no starting inventory */ long numbones; /* # of bones files loaded */ }; @@ -398,6 +399,7 @@ struct you { /* These ranges can never be more than MAX_RANGE (vision.h). */ int nv_range; /* current night vision range */ int xray_range; /* current xray vision range */ + int unblind_telepat_range; /* * These variables are valid globally only when punished and blind. @@ -432,7 +434,12 @@ struct you { Bitfield(ufalldamage, 1); /* fell into air; take damage on level below */ Bitfield(usaving_grace, 1); /* prevents death once */ - /* 7 free bits */ + Bitfield(uhandedness, 1); /* There is no advantage for either handedness. + The distinction is only for flavor variation + and for use in messages. */ +#define RIGHT_HANDED 0x00 +#define LEFT_HANDED 0x01 + /* 6 free bits */ unsigned udg_cnt; /* how long you have been demigod */ struct u_event uevent; /* certain events have happened */ @@ -484,6 +491,8 @@ struct you { int uinvault; struct monst *ustuck; /* engulfer or grabber, maybe grabbee if Upolyd */ struct monst *usteed; /* mount when riding */ + unsigned ustuck_mid; /* u.ustuck->m_id, used during save/restore */ + unsigned usteed_mid; /* u.usteed->m_id, used during save/restore */ long ugallop; /* turns steed will run after being kicked */ int urideturns; /* time spent riding, for skill advancement */ int umortality; /* how many times you died */ @@ -514,6 +523,7 @@ struct you { struct _hitmon_data { int dmg; /* damage */ int thrown; + int twohits; /* 0: 1 of 1; 1: 1 of 2; 2: 2 of 2 */ int dieroll; struct permonst *mdat; boolean use_weapon_skill; @@ -545,7 +555,14 @@ struct _hitmon_data { /* point px,py is adjacent to (or same location as) hero */ #define next2u(px,py) (distu((px),(py)) <= 2) +/* is monster on top of or next to hero? */ +#define m_next2u(m) (distu((m)->mx,(m)->my) <= 2) /* hero at (x,y)? */ #define u_at(x,y) ((x) == u.ux && (y) == u.uy) +#define URIGHTY (u.uhandedness == RIGHT_HANDED) +#define ULEFTY (u.uhandedness == LEFT_HANDED) +#define RING_ON_PRIMARY (ULEFTY ? uleft : uright) +#define RING_ON_SECONDARY (ULEFTY ? uright : uleft) + #endif /* YOU_H */ diff --git a/include/youprop.h b/include/youprop.h index a434251d34..26bfc552fb 100644 --- a/include/youprop.h +++ b/include/youprop.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 youprop.h $NHDT-Date: 1596498577 2020/08/03 23:49:37 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.32 $ */ +/* NetHack 3.7 youprop.h $NHDT-Date: 1725653018 2024/09/06 20:03:38 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.45 $ */ /* Copyright (c) 1989 Mike Threepoint */ /* NetHack may be freely redistributed. See license for details. */ @@ -138,6 +138,7 @@ #define EWounded_legs u.uprops[WOUNDED_LEGS].extrinsic #define Wounded_legs (HWounded_legs || EWounded_legs) +/* Sleepy: prone to falling asleep periodically; not necessarily asleep now */ #define HSleepy u.uprops[SLEEPY].intrinsic #define ESleepy u.uprops[SLEEPY].extrinsic #define Sleepy (HSleepy || ESleepy) @@ -156,6 +157,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) @@ -201,6 +206,7 @@ #define HStealth u.uprops[STEALTH].intrinsic #define EStealth u.uprops[STEALTH].extrinsic +/* BStealth has FROMOUTSIDE set if mounted on non-flying steed */ #define BStealth u.uprops[STEALTH].blocked #define Stealth ((HStealth || EStealth) && !BStealth) @@ -391,6 +397,10 @@ * Some pseudo-properties. */ +/* the code will needs lots of updating to use this so leave it commented +#define Riding (u.usteed != NULL) +*/ + /* unconscious() includes u.usleep but not is_fainted(); the multi test is redundant but allows the function calls to be skipped most of the time */ #define Unaware (gm.multi < 0 && (unconscious() || is_fainted())) @@ -398,8 +408,9 @@ /* Whether the hero is in a form that dislikes a certain material */ #define Hate_material(material) mon_hates_material(&gy.youmonst, material) -/* _Hitchhikers_Guide_to_the_Galaxy_ on uses for 'towel': "wrap it round +/* _The_Hitchhikers_Guide_to_the_Galaxy_ on uses for 'towel': "wrap it round your head to ward off noxious fumes" [we require it to be damp or wet] */ -#define Half_gas_damage (ublindf && ublindf->otyp == TOWEL && ublindf->spe > 0) +#define Half_gas_damage \ + (ublindf && ublindf->otyp == TOWEL && ublindf->spe > 0) #endif /* YOUPROP_H */ diff --git a/outdated/.travis.yml b/outdated/.travis.yml index 57cecc451f..8890512582 100644 --- a/outdated/.travis.yml +++ b/outdated/.travis.yml @@ -3,7 +3,7 @@ matrix: include: - name: linux-xenial-gcc-win-all os: linux - env: HINTS=linux.370 LUA_VERSION=5.4.4 + env: HINTS=linux.370 LUA_VERSION=5.4.6 compiler: gcc addons: apt: @@ -21,7 +21,7 @@ matrix: - make LUA_VERSION=$LUA_VERSION WANT_WIN_ALL=1 QT_SELECT=5 MOC=moc install - name: linux-bionic-gcc-win-all os: linux - env: HINTS=linux.370 LUA_VERSION=5.4.4 + env: HINTS=linux.370 LUA_VERSION=5.4.6 dist: bionic compiler: gcc addons: @@ -40,7 +40,7 @@ matrix: - make LUA_VERSION=$LUA_VERSION WANT_WIN_ALL=1 QT_SELECT=5 MOC=moc install - name: linux-focal-clang-win-all os: linux - env: HINTS=linux.370 LUA_VERSION=5.4.4 + env: HINTS=linux.370 LUA_VERSION=5.4.6 dist: focal compiler: clang addons: @@ -59,7 +59,7 @@ matrix: - make LUA_VERSION=$LUA_VERSION WANT_WIN_ALL=1 QT_SELECT=5 MOC=moc install - name: linux-xenial-gcc-nocommon os: linux - env: HINTS=linux.370 LUA_VERSION=5.4.4 + env: HINTS=linux.370 LUA_VERSION=5.4.6 dist: xenial compiler: gcc script: @@ -70,7 +70,7 @@ matrix: - make install - name: linux-focal-gcc9-win-all os: linux - env: HINTS=linux.370 LUA_VERSION=5.4.4 + env: HINTS=linux.370 LUA_VERSION=5.4.6 dist: focal compiler: gcc addons: @@ -91,7 +91,7 @@ matrix: - make LUA_VERSION=$LUA_VERSION WANT_WIN_ALL=1 QT_SELECT=5 MOC=moc install - name: linux-xenial-gcc-minimal os: linux - env: HINTS=linux-minimal LUA_VERSION=5.4.4 + env: HINTS=linux-minimal LUA_VERSION=5.4.6 compiler: gcc script: | cd sys/unix/ && sh setup.sh hints/$HINTS && cd ../../ @@ -113,7 +113,6 @@ matrix: sed -i '/^#define MAIL/d' include/unixconf.h sed -i '/^#define SHELL/d' include/unixconf.h sed -i '/^#define SUSPEND/d' include/unixconf.h - sed -i 's/^#define TEXTCOLOR//' include/unixconf.h make fetch-lua LUA_VERSION=$LUA_VERSION test -d "lib/lua-$LUA_VERSION/src" || exit 0 make LUA_VERSION=$LUA_VERSION WANT_WIN_ALL=1 install @@ -129,7 +128,7 @@ matrix: script: - export ADD_CURSES=Y - export PDCURSES_TOP=../lib/pdcurses - - export LUA_VERSION=5.4.4 + - export LUA_VERSION=5.4.6 - sh sys/windows/travis-gcc.sh - test -d "lib/lua-$LUA_VERSION/src" || exit 0 - test -d "lib/pdcurses" || exit 0 @@ -138,7 +137,7 @@ matrix: - mingw32-make LUA_VERSION=$LUA_VERSION install - name: msdos-linux-focal-djgpp-crosscompile os: linux - env: HINTS=linux.370 LUA_VERSION=5.4.4 + env: HINTS=linux.370 LUA_VERSION=5.4.6 dist: focal compiler: gcc script: diff --git a/outdated/include/amiconf.h b/outdated/include/amiconf.h index a23a56f0a7..d098015091 100644 --- a/outdated/include/amiconf.h +++ b/outdated/include/amiconf.h @@ -147,7 +147,6 @@ extern char *gets(char *); * (Possibly) configurable Amiga options: */ -#define TEXTCOLOR /* Use colored monsters and objects */ #define HACKFONT /* Use special hack.font */ #ifndef CROSS_TO_AMIGA /* issues with prototype and spawnl */ #define SHELL /* Have a shell escape command (!) */ @@ -164,13 +163,7 @@ extern char *gets(char *); #endif #define CHANGE_COLOR 1 - -#ifdef TEXTCOLOR #define DEPTH 6 /* Maximum depth of the screen allowed */ -#else -#define DEPTH 2 /* Four colors...sigh... */ -#endif - #define AMII_MAXCOLORS (1L << DEPTH) typedef unsigned short AMII_COLOR_TYPE; diff --git a/outdated/include/beconf.h b/outdated/include/beconf.h index 29f5eec3de..bc248fac4b 100644 --- a/outdated/include/beconf.h +++ b/outdated/include/beconf.h @@ -17,7 +17,6 @@ #define tgetch getchar #define FCMASK 0666 #define PORT_ID "BeOS" -#define TEXTCOLOR #define POSIX_TYPES #define SIG_RET_TYPE __signal_func_ptr diff --git a/outdated/include/macconf.h b/outdated/include/macconf.h index fbcd3aded8..fdde526c38 100644 --- a/outdated/include/macconf.h +++ b/outdated/include/macconf.h @@ -36,8 +36,6 @@ #define NO_SIGNAL /* You wouldn't believe our signals ... */ #define FILENAME 256 #define NO_TERMS /* For tty port (see wintty.h) */ - -#define TEXTCOLOR /* For Mac TTY interface */ #define CHANGE_COLOR /* Use these two includes instead of system.h. */ diff --git a/outdated/include/os2conf.h b/outdated/include/os2conf.h index 20b2fe82d4..d9983b163e 100644 --- a/outdated/include/os2conf.h +++ b/outdated/include/os2conf.h @@ -40,7 +40,6 @@ #define SHELL /* shell escape */ /* #define TERMLIB /* use termcap file */ #define ANSI_DEFAULT /* allows NetHack to run without termcap file */ -#define TEXTCOLOR /* allow color */ /* * The remaining code shouldn't need modification. diff --git a/outdated/include/tosconf.h b/outdated/include/tosconf.h index 0d436a4b87..1ea893f4d2 100644 --- a/outdated/include/tosconf.h +++ b/outdated/include/tosconf.h @@ -38,7 +38,6 @@ #define RANDOM /* improved random numbers */ #define SHELL /* allow spawning of shell */ #define TERMLIB /* use termcap */ -#define TEXTCOLOR /* allow color */ #define MAIL /* enable the fake maildemon */ #ifdef MINT #define SUSPEND /* allow suspending the game */ @@ -75,9 +74,7 @@ extern int strncmpi(const char *, const char *, size_t); #include "pcconf.h" /* remainder of stuff is same as the PC */ #endif -#ifdef TEXTCOLOR extern boolean colors_changed; /* in tos.c */ -#endif #endif /* TOSCONF_H */ #endif /* TOS */ diff --git a/outdated/include/trampoli.h b/outdated/include/trampoli.h index 344dacc934..f45d3bb98b 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/wceconf.h b/outdated/include/wceconf.h index 4dadc0f096..b7a6ea7446 100644 --- a/outdated/include/wceconf.h +++ b/outdated/include/wceconf.h @@ -29,7 +29,6 @@ /* #define SHELL /* nt use of pcsys routines caused a hang */ #define RANDOM /* have Berkeley random(3) */ -#define TEXTCOLOR /* Color text */ #define EXEPATH /* Allow .exe location to be used as HACKDIR */ #define TRADITIONAL_GLYPHMAP /* Store glyph mappings at level change time */ diff --git a/outdated/include/wingem.h b/outdated/include/wingem.h index 86b3f6ba3b..4c1378c0e3 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/amirip.c b/outdated/sys/amiga/amirip.c index ddfd405ee3..b14689f805 100644 --- a/outdated/sys/amiga/amirip.c +++ b/outdated/sys/amiga/amirip.c @@ -185,7 +185,7 @@ time_t when; SetDrMd(rp, JAM1); /* Put name on stone */ - Sprintf(buf, "%s", gp.plname); + Sprintf(buf, "%s", svp.plname); buf[STONE_LINE_LEN] = 0; tomb_text(buf); diff --git a/outdated/sys/amiga/winamenu.c b/outdated/sys/amiga/winamenu.c index 106d523cc2..453e259845 100644 --- a/outdated/sys/amiga/winamenu.c +++ b/outdated/sys/amiga/winamenu.c @@ -357,7 +357,7 @@ menu_item **retmip; nw->Screen = HackScreen; if (win == WIN_INVEN) { - sprintf(title, "%s the %s's Inventory", gp.plname, gp.pl_character); + sprintf(title, "%s the %s's Inventory", svp.plname, svp.pl_character); nw->Title = title; if (lastinvent.MaxX != 0) { nw->LeftEdge = lastinvent.MinX; diff --git a/outdated/sys/amiga/winami.c b/outdated/sys/amiga/winami.c index f80490a83e..4b78663468 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, @@ -438,10 +436,10 @@ amii_askname() amii_getlin("Who are you?", plnametmp); } while (strlen(plnametmp) == 0); - strncpy(gp.plname, plnametmp, PL_NSIZ - 1); /* Avoid overflowing plname[] */ - gp.plname[PL_NSIZ - 1] = 0; + strncpy(svp.plname, plnametmp, PL_NSIZ - 1); /* Avoid overflowing plname[] */ + svp.plname[PL_NSIZ - 1] = 0; - if (*gp.plname == '\33') { + if (*svp.plname == '\33') { clearlocks(); exit_nhwindows(NULL); nh_terminate(0); @@ -475,9 +473,9 @@ amii_player_selection() #if 0 /* Don't query the user ... instead give random character -jhsa */ #if 0 /* OBSOLETE */ - if( *gp.pl_character ){ - gp.pl_character[ 0 ] = toupper( gp.pl_character[ 0 ] ); - if( strchr( pl_classes, gp.pl_character[ 0 ] ) ) + if( *svp.pl_character ){ + svp.pl_character[ 0 ] = toupper( svp.pl_character[ 0 ] ); + if( strchr( pl_classes, svp.pl_character[ 0 ] ) ) return; } #endif @@ -531,19 +529,19 @@ amii_player_selection() case VANILLAKEY: if( strchr( pl_classes, toupper( code ) ) ) { - gp.pl_character[0] = toupper( code ); + svp.pl_character[0] = toupper( code ); aredone = 1; } else if( code == ' ' || code == '\n' || code == '\r' ) { flags.initrole = randrole(FALSE); #if 0 /* OBSOLETE */ - strcpy( gp.pl_character, roles[ rnd( 11 ) ] ); + strcpy( svp.pl_character, roles[ rnd( 11 ) ] ); #endif aredone = 1; amii_clear_nhwindow( WIN_BASE ); CloseShWindow( cwin ); - RandomWindow( gp.pl_character ); + RandomWindow( svp.pl_character ); return; } else if( code == 'q' || code == 'Q' ) @@ -563,15 +561,15 @@ amii_player_selection() case 1: /* Random Character */ flags.initrole = randrole(FALSE); #if 0 /* OBSOLETE */ - strcpy( gp.pl_character, roles[ rnd( 11 ) ] ); + strcpy( svp.pl_character, roles[ rnd( 11 ) ] ); #endif amii_clear_nhwindow( WIN_BASE ); CloseShWindow( cwin ); - RandomWindow( gp.pl_character ); + RandomWindow( svp.pl_character ); return; default: - gp.pl_character[0] = gd->GadgetID; + svp.pl_character[0] = gd->GadgetID; break; } aredone = 1; diff --git a/outdated/sys/amiga/winami.p b/outdated/sys/amiga/winami.p index 0fd69a38f1..2e26cfb106 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/winchar.c b/outdated/sys/amiga/winchar.c index 245eaee41c..e4e56b8a22 100644 --- a/outdated/sys/amiga/winchar.c +++ b/outdated/sys/amiga/winchar.c @@ -932,7 +932,6 @@ struct amii_glyph_node { static struct amii_glyph_node amii_g_nodes[NUMBER_GLYPH_NODES]; static char amii_glyph_buffer[GLYPH_BUFFER_SIZE]; -#ifdef TEXTCOLOR /* * Map our amiga-specific colormap into the colormap specified in color.h. * See winami.c for the amiga specific colormap. @@ -963,7 +962,6 @@ int backg[AMII_MAXCOLORS] = { #define CLR_WHITE 15 #define CLR_MAX 16 #endif -#endif #ifndef TESTING /* @@ -1062,13 +1060,8 @@ int color_index, glyph; curx = cw->curx; cury = cw->cury; -#ifdef TEXTCOLOR fg_color = foreg[color_index]; bg_color = backg[color_index]; -#else - fg_color = 1; - bg_color = 0; -#endif /* TEXTCOLOR */ /* See if we have enough character buffer space... */ if (glyph_buffer_index >= GLYPH_BUFFER_SIZE) diff --git a/outdated/sys/amiga/winfuncs.c b/outdated/sys/amiga/winfuncs.c index 8f5e5ef97a..9500c1a920 100644 --- a/outdated/sys/amiga/winfuncs.c +++ b/outdated/sys/amiga/winfuncs.c @@ -68,11 +68,7 @@ int dir; return; if (!WINVERS_AMIV) { -#ifdef TEXTCOLOR amii_numcolors = 8; -#else - amii_numcolors = 4; -#endif amii_defpens[0] = C_BLACK; /* DETAILPEN */ amii_defpens[1] = C_BLUE; /* BLOCKPEN */ amii_defpens[2] = C_BROWN; /* TEXTPEN */ @@ -1225,11 +1221,7 @@ char **argv; /* While openscreen fails try fewer colors to see if that is the problem. */ while ((HackScreen = OpenScreen((void *) &NewHackScreen)) == NULL) { -#ifdef TEXTCOLOR if (--NewHackScreen.Depth < 3) -#else - if (--NewHackScreen.Depth < 2) -#endif Abort(AN_OpenScreen & ~AT_DeadEnd); } amii_numcolors = 1L << NewHackScreen.Depth; @@ -1523,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; @@ -1939,7 +1930,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; @@ -1994,11 +1985,7 @@ if(u.uz.dlevel != x){ /* Move the cursor. */ amii_curs(win, x, y + 2); -#ifdef TEXTCOLOR amiga_print_glyph(win, color, ch); -#else - g_putch(ch); /* print the character */ -#endif cw->curx++; /* one character over */ } } diff --git a/outdated/sys/amiga/winproto.h b/outdated/sys/amiga/winproto.h index eb2b588d8a..efa1e9b586 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/amiga/winstr.c b/outdated/sys/amiga/winstr.c index adf9f49f7e..ea7d65343e 100644 --- a/outdated/sys/amiga/winstr.c +++ b/outdated/sys/amiga/winstr.c @@ -191,7 +191,7 @@ const char *str; if (cw->data[cw->cury] == NULL) panic("NULL pointer for status window"); ob = &cw->data[cw->cury][j = cw->curx]; - if (gc.context.botlx) + if (disp.botlx) *ob = 0; /* Display when beam at top to avoid flicker... */ diff --git a/outdated/sys/atari/tos.c b/outdated/sys/atari/tos.c index bb0f2639d5..9bc9b4ab8c 100644 --- a/outdated/sys/atari/tos.c +++ b/outdated/sys/atari/tos.c @@ -28,9 +28,7 @@ static char DOSgetch(void); static char BIOSgetch(void); static void init_aline(void); char *_a_line; /* for Line A variables */ -#ifdef TEXTCOLOR boolean colors_changed = FALSE; -#endif int tgetch() @@ -312,7 +310,6 @@ init_aline() #endif } -#ifdef TEXTCOLOR /* used in termcap.c to decide how to set up the hilites */ unsigned long tos_numcolors = 2; @@ -343,7 +340,6 @@ restore_colors() nh_HE = plainHE; colors_changed = FALSE; } -#endif /* TEXTCOLOR */ #ifdef SUSPEND diff --git a/outdated/sys/be/bemain.c b/outdated/sys/be/bemain.c index a480c9e1a0..cf29af8093 100644 --- a/outdated/sys/be/bemain.c +++ b/outdated/sys/be/bemain.c @@ -132,14 +132,14 @@ whoami(void) */ char *s; - if (*gp.plname) + if (*svp.plname) return; if (s = nh_getenv("USER")) { - (void) strncpy(gp.plname, s, sizeof(gp.plname) - 1); + (void) strncpy(svp.plname, s, sizeof(svp.plname) - 1); return; } if (s = nh_getenv("LOGNAME")) { - (void) strncpy(gp.plname, s, sizeof(gp.plname) - 1); + (void) strncpy(svp.plname, s, sizeof(svp.plname) - 1); return; } } @@ -177,11 +177,11 @@ process_options(int argc, char **argv) #endif case 'u': if (argv[0][2]) - (void) strncpy(gp.plname, argv[0] + 2, sizeof(gp.plname) - 1); + (void) strncpy(svp.plname, argv[0] + 2, sizeof(svp.plname) - 1); else if (argc > 1) { argc--; argv++; - (void) strncpy(gp.plname, argv[0], sizeof(gp.plname) - 1); + (void) strncpy(svp.plname, argv[0], sizeof(svp.plname) - 1); } else raw_print("Player name expected after -u"); break; @@ -236,15 +236,15 @@ getlock(void) { int fd; - Sprintf(gl.lock, "%d%s", getuid(), gp.plname); + Sprintf(gl.lock, "%d%s", getuid(), svp.plname); regularize(gl.lock); set_levelfile_name(gl.lock, 0); fd = creat(gl.lock, FCMASK); if (fd == -1) { error("cannot creat lock file."); } else { - if (write(fd, (genericptr_t) &gh.hackpid, sizeof(gh.hackpid)) - != sizeof(gh.hackpid)) { + if (write(fd, (genericptr_t) &svh.hackpid, sizeof(svh.hackpid)) + != sizeof(svh.hackpid)) { error("cannot write lock"); } if (close(fd) == -1) { diff --git a/outdated/sys/mac/macfile.c b/outdated/sys/mac/macfile.c index bdf400b604..ce0d0e69d1 100644 --- a/outdated/sys/mac/macfile.c +++ b/outdated/sys/mac/macfile.c @@ -228,7 +228,7 @@ macopen(const char *name, int flags, long fileType) Handle name; Str255 plnamep; - C2P(gp.plname, plnamep); + C2P(svp.plname, plnamep); name = (Handle)NewString(plnamep); if (name) replace_resource(name, 'STR ', PLAYER_NAME_RES_ID, diff --git a/outdated/sys/mac/macmain.c b/outdated/sys/mac/macmain.c index 5ffa3b72cd..cd624543d6 100644 --- a/outdated/sys/mac/macmain.c +++ b/outdated/sys/mac/macmain.c @@ -225,7 +225,7 @@ process_openfile(short src_vol, long src_dir, Str255 fName, OSType ftype) Handle name = Get1Resource('STR ', PLAYER_NAME_RES_ID); if (name) { Str255 save_f_p; - P2C(*(StringHandle) name, gp.plname); + P2C(*(StringHandle) name, svp.plname); set_savefile_name(TRUE); C2P(fqname(gs.SAVEF, SAVEPREFIX, 0), save_f_p); force_hdelete(theDirs.dataRefNum, theDirs.dataDirID, diff --git a/outdated/sys/mac/macmenu.c b/outdated/sys/mac/macmenu.c index 74d06fc33b..497f9b53a2 100644 --- a/outdated/sys/mac/macmenu.c +++ b/outdated/sys/mac/macmenu.c @@ -493,8 +493,8 @@ mac_askname() SetPortDialogPort(askdialog); /* Initialize the name text item */ - ask_restring(gp.plname, str); - if (gp.plname[0]) { + ask_restring(svp.plname, str); + if (svp.plname[0]) { GetDialogItem(askdialog, RSRC_ASK_NAME, &type, &handle, &rect); SetDialogItemText(handle, str); } @@ -502,8 +502,8 @@ mac_askname() { Str32 pName; pName [0] = 0; - if (gp.plname && gp.plname [0]) { - strcpy ((char *) pName, gp.plname); + if (svp.plname && svp.plname [0]) { + strcpy ((char *) pName, svp.plname); c2pstr ((char *) pName); } else { Handle h; @@ -548,7 +548,7 @@ mac_askname() if (flags.initrole >= 0) currrole = flags.initrole; /* Check for backward compatibility */ - else if ((currrole = str2role(gp.pl_character)) < 0) + else if ((currrole = str2role(svp.pl_character)) < 0) currrole = randrole(FALSE); /* Initialize the race popup menu */ @@ -735,8 +735,8 @@ mac_askname() GetDialogItemText(handle, str); if (str[0] > PL_NSIZ - 1) str[0] = PL_NSIZ - 1; - BlockMove(&str[1], gp.plname, str[0]); - gp.plname[str[0]] = '\0'; + BlockMove(&str[1], svp.plname, str[0]); + svp.plname[str[0]] = '\0'; /* Destroy the dialog */ for (i = RSRC_ASK_ROLE; i <= RSRC_ASK_MODE; i++) { @@ -758,14 +758,14 @@ mac_askname() break; case 2: /* Debug */ wizard = 1; - strcpy(gp.plname, WIZARD_NAME); + strcpy(svp.plname, WIZARD_NAME); break; default: /* Quit */ ExitToShell(); } /* Process the role */ - strcpy(gp.pl_character, roles[currrole].name.m); + strcpy(svp.pl_character, roles[currrole].name.m); flags.initrole = currrole; /* Process the race */ diff --git a/outdated/sys/mac/macunix.c b/outdated/sys/mac/macunix.c index a0b5c0d026..f590cfeffe 100644 --- a/outdated/sys/mac/macunix.c +++ b/outdated/sys/mac/macunix.c @@ -24,7 +24,7 @@ getlock(void) int fd; int pid = getpid(); /* Process ID */ - Sprintf(gl.lock, "%d%s", getuid(), gp.plname); + Sprintf(gl.lock, "%d%s", getuid(), svp.plname); set_levelfile_name(gl.lock, 0); if ((fd = open(gl.lock, O_RDWR | O_EXCL | O_CREAT, LEVL_TYPE)) == -1) { diff --git a/outdated/sys/mac/macwin.c b/outdated/sys/mac/macwin.c index 1da056a89c..517b7f5a68 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 01ea7587b9..9a42c48890 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 2cd59a9d26..0020dccd1a 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/unix/hints/include/cross-amiga-pre b/outdated/sys/unix/hints/include/cross-amiga-pre index fb04be1c2e..5c59f102df 100644 --- a/outdated/sys/unix/hints/include/cross-amiga-pre +++ b/outdated/sys/unix/hints/include/cross-amiga-pre @@ -24,9 +24,9 @@ endif ifdef BUILD_TARGET_LUA #===============-================================================= # LUA library -# Source from http://www.lua.org/ftp/lua-5.4.4.tar.gz +# Source from http://www.lua.org/ftp/lua-5.4.6.tar.gz #================================================================= -LUA_VERSION ?=5.4.4 +LUA_VERSION ?=5.4.6 LUATOP ?= ../lib/lua-$(LUA_VERSION) LUASRCDIR ?= $(LUATOP)/src LUAOBJFILES1 = $(TARGETPFX)lapi.o $(TARGETPFX)lauxlib.o \ diff --git a/sys/vms/lev_lex.h b/outdated/sys/vms/lev_lex.h similarity index 100% rename from sys/vms/lev_lex.h rename to outdated/sys/vms/lev_lex.h diff --git a/outdated/sys/wince/mhdlg.c b/outdated/sys/wince/mhdlg.c index 60ce3f65dc..b569232228 100644 --- a/outdated/sys/wince/mhdlg.c +++ b/outdated/sys/wince/mhdlg.c @@ -459,7 +459,7 @@ plselInitDialog(HWND hWnd) TCHAR wbuf[BUFSZ]; /* set player name */ - SetDlgItemText(hWnd, IDC_PLSEL_NAME, NH_A2W(gp.plname, wbuf, sizeof(wbuf))); + SetDlgItemText(hWnd, IDC_PLSEL_NAME, NH_A2W(svp.plname, wbuf, sizeof(wbuf))); /* check flags for consistency */ if (flags.initrole >= 0) { diff --git a/outdated/sys/wince/mhinput.c b/outdated/sys/wince/mhinput.c index 8758324e78..a7b1693421 100644 --- a/outdated/sys/wince/mhinput.c +++ b/outdated/sys/wince/mhinput.c @@ -39,7 +39,7 @@ mswin_have_input() return #ifdef SAFERHANGUP /* we always have input (ESC) if hangup was requested */ - gp.program_state.done_hup || + program_state.done_hup || #endif (nhi_read_pos != nhi_write_pos); } @@ -69,7 +69,7 @@ mswin_input_pop() #ifdef SAFERHANGUP /* always return ESC when hangup was requested */ - if (gp.program_state.done_hup) { + if (program_state.done_hup) { static MSNHEvent hangup_event; hangup_event.type = NHEVENT_CHAR; hangup_event.kbd.ch = '\033'; @@ -98,7 +98,7 @@ mswin_input_peek() #ifdef SAFERHANGUP /* always return ESC when hangup was requested */ - if (gp.program_state.done_hup) { + if (program_state.done_hup) { static MSNHEvent hangup_event; hangup_event.type = NHEVENT_CHAR; hangup_event.kbd.ch = '\033'; diff --git a/outdated/sys/wince/mhmain.c b/outdated/sys/wince/mhmain.c index 74cebcd40e..d6513ad0f4 100644 --- a/outdated/sys/wince/mhmain.c +++ b/outdated/sys/wince/mhmain.c @@ -826,7 +826,7 @@ mswin_layout_main_window(HWND changed_child) /* show command window only if it exists and the game is ready (plname is set) */ if (GetNHApp()->bCmdPad && cmd_size.cx > 0 && cmd_size.cy > 0 - && *gp.plname) { + && *svp.plname) { MoveWindow(GetNHApp()->hCmdWnd, cmd_org.x, cmd_org.y, cmd_size.cx, cmd_size.cy, TRUE); ShowWindow(GetNHApp()->hCmdWnd, SW_SHOW); diff --git a/outdated/sys/wince/mhmap.c b/outdated/sys/wince/mhmap.c index 2adcf3d550..649cc4d693 100644 --- a/outdated/sys/wince/mhmap.c +++ b/outdated/sys/wince/mhmap.c @@ -861,8 +861,6 @@ void nhglyph2charcolor(short g, uchar *ch, int *color) { int offset; -#ifdef TEXTCOLOR - #define zap_color(n) *color = iflags.use_color ? zapcolors[n] : NO_COLOR #define cmap_color(n) *color = iflags.use_color ? defsyms[n].color : NO_COLOR #define obj_color(n) \ @@ -872,17 +870,6 @@ nhglyph2charcolor(short g, uchar *ch, int *color) #define warn_color(n) \ *color = iflags.use_color ? def_warnsyms[n].color : NO_COLOR -#else /* no text color */ - -#define zap_color(n) -#define cmap_color(n) -#define obj_color(n) -#define mon_color(n) -#define pet_color(c) -#define warn_color(c) - *color = CLR_WHITE; -#endif - if ((offset = (g - GLYPH_WARNING_OFF)) >= 0) { /* a warning flash */ *ch = showsyms[offset + SYM_OFF_W]; warn_color(offset); diff --git a/outdated/sys/wince/mhmenu.c b/outdated/sys/wince/mhmenu.c index 8db3afdcce..4aa5f19f40 100644 --- a/outdated/sys/wince/mhmenu.c +++ b/outdated/sys/wince/mhmenu.c @@ -533,7 +533,7 @@ onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) if (!data->text.text) { data->text.text = mswin_init_text_buffer( - gp.program_state.gameover ? FALSE : GetNHApp()->bWrapText); + program_state.gameover ? FALSE : GetNHApp()->bWrapText); if (!data->text.text) break; } diff --git a/outdated/sys/wince/mhstatus.c b/outdated/sys/wince/mhstatus.c index 41f4e5d1ba..7cdcc0b5c5 100644 --- a/outdated/sys/wince/mhstatus.c +++ b/outdated/sys/wince/mhstatus.c @@ -182,7 +182,7 @@ FormatStatusString(char *text, int format) int hp, hpmax; int cap = near_capacity(); - Strcpy(text, gp.plname); + Strcpy(text, svp.plname); if ('a' <= text[0] && text[0] <= 'z') text[0] += 'A' - 'a'; text[10] = 0; @@ -253,7 +253,7 @@ FormatStatusString(char *text, int format) /* forth line */ if (flags.time) - Sprintf(nb = eos(nb), "T:%ld ", gm.moves); + Sprintf(nb = eos(nb), "T:%ld ", svm.moves); if (strcmp(hu_stat[u.uhs], " ")) { Strcat(text, hu_stat[u.uhs]); diff --git a/outdated/sys/wince/mhtext.c b/outdated/sys/wince/mhtext.c index 4dfcf65759..a6b813507b 100644 --- a/outdated/sys/wince/mhtext.c +++ b/outdated/sys/wince/mhtext.c @@ -39,7 +39,7 @@ mswin_init_text_window() ZeroMemory(data, sizeof(NHTextWindow)); data->window_text = mswin_init_text_buffer( - gp.program_state.gameover ? FALSE : GetNHApp()->bWrapText); + program_state.gameover ? FALSE : GetNHApp()->bWrapText); SetWindowLong(ret, GWL_USERDATA, (LONG) data); return ret; } diff --git a/outdated/sys/wince/mswproc.c b/outdated/sys/wince/mswproc.c index 2379f85f96..b97b1a7e70 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, @@ -646,7 +645,7 @@ mswin_askname(void) { logDebug("mswin_askname()\n"); - if (mswin_getlin_window("who are you?", gp.plname, PL_NSIZ) == IDCANCEL) { + if (mswin_getlin_window("who are you?", svp.plname, PL_NSIZ) == IDCANCEL) { bail("bye-bye"); /* not reached */ } @@ -1533,31 +1532,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 3ee5622df9..0ff0dd9166 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/sys/wince/winhack.c b/outdated/sys/wince/winhack.c index 2e1f9266ae..72f225a5fd 100644 --- a/outdated/sys/wince/winhack.c +++ b/outdated/sys/wince/winhack.c @@ -226,8 +226,8 @@ getlock() if (fd == -1) { error("cannot creat lock file (%s.)", fq_lock); } else { - if (write(fd, (char *) &gh.hackpid, sizeof(gh.hackpid)) - != sizeof(gh.hackpid)) { + if (write(fd, (char *) &svh.hackpid, sizeof(svh.hackpid)) + != sizeof(svh.hackpid)) { error("cannot write lock (%s)", fq_lock); } if (close(fd) == -1) { diff --git a/outdated/sys/windows/travis-gcc.sh b/outdated/sys/windows/travis-gcc.sh index fdbd5637c5..c611377905 100644 --- a/outdated/sys/windows/travis-gcc.sh +++ b/outdated/sys/windows/travis-gcc.sh @@ -3,6 +3,6 @@ mkdir -p lib cd lib git clone --depth 1 https://github.com/wmcbrine/PDCurses.git pdcurses #git clone --depth 1 https://github.com/universal-ctags/ctags.git ctags -curl -R -O http://www.lua.org/ftp/lua-5.4.4.tar.gz -tar zxf lua-5.4.4.tar.gz +curl -R -O http://www.lua.org/ftp/lua-5.4.6.tar.gz +tar zxf lua-5.4.6.tar.gz cd ../ diff --git a/outdated/win/Qt3/qt3_win.cpp b/outdated/win/Qt3/qt3_win.cpp index eda81a2b39..3383762759 100644 --- a/outdated/win/Qt3/qt3_win.cpp +++ b/outdated/win/Qt3/qt3_win.cpp @@ -1031,9 +1031,9 @@ NetHackQtPlayerSelector::NetHackQtPlayerSelector(NetHackQtKeyBuffer& ks) : QButtonGroup* namebox = new QButtonGroup(1,Horizontal,"Name",this); QLineEdit* name = new QLineEdit(namebox); - name->setMaxLength(sizeof(gp.plname)-1); - if ( strncmp(gp.plname,"player",6) && strncmp(gp.plname,"games",5) ) - name->setText(gp.plname); + name->setMaxLength(sizeof(svp.plname)-1); + if ( strncmp(svp.plname,"player",6) && strncmp(svp.plname,"games",5) ) + name->setText(svp.plname); connect(name, SIGNAL(textChanged(const QString&)), this, SLOT(selectName(const QString&)) ); name->setFocus(); @@ -1189,7 +1189,7 @@ NetHackQtPlayerSelector::NetHackQtPlayerSelector(NetHackQtKeyBuffer& ks) : void NetHackQtPlayerSelector::selectName(const QString& n) { - strncpy(gp.plname,n.latin1(),sizeof(gp.plname)-1); + strncpy(svp.plname,n.latin1(),sizeof(svp.plname)-1); } void NetHackQtPlayerSelector::selectRole() @@ -1623,7 +1623,6 @@ void NetHackQtMapWindow::mousePressEvent(QMouseEvent* event) qApp->exit_loop(); } -#ifdef TEXTCOLOR static const QPen& nhcolor_to_pen(int c) { @@ -1651,7 +1650,6 @@ const QPen& nhcolor_to_pen(int c) return pen[c]; } -#endif void NetHackQtMapWindow::paintEvent(QPaintEvent* event) { @@ -1710,9 +1708,7 @@ void NetHackQtMapWindow::paintEvent(QPaintEvent* event) /* map glyph to character and color */ (void)mapglyph(g, &och, &color, &special, i, j, 0); ch = (uchar)och; -#ifdef TEXTCOLOR painter.setPen( nhcolor_to_pen(color) ); -#endif painter.drawText( i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height(), @@ -1722,10 +1718,7 @@ void NetHackQtMapWindow::paintEvent(QPaintEvent* event) (const char*)&ch, 1 ); if (glyph_is_pet(g) -#ifdef TEXTCOLOR - && ::iflags.hilite_pet -#endif - ) { + && ::iflags.hilite_pet) { painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); @@ -1740,10 +1733,7 @@ void NetHackQtMapWindow::paintEvent(QPaintEvent* event) unsigned short g=Glyph(i,j); qt_settings->glyphs().drawCell(painter, g, i, j); if (glyph_is_pet(g) -#ifdef TEXTCOLOR - && ::iflags.hilite_pet -#endif - ) { + && ::iflags.hilite_pet) { painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); @@ -2540,7 +2530,7 @@ void NetHackQtStatusWindow::updateStats() encumber.setLabel(enc); encumber.show(); } - Strcpy(buf, gp.plname); + Strcpy(buf, svp.plname); if ('a' <= buf[0] && buf[0] <= 'z') buf[0] += 'A'-'a'; Strcat(buf, " the "); if (u.mtimedone) { @@ -2603,7 +2593,7 @@ void NetHackQtStatusWindow::updateStats() align.setLabel("Lawful"); } - if (::flags.time) time.setLabel("Time:",(long)gm.moves); + if (::flags.time) time.setLabel("Time:",(long)svm.moves); else time.setLabel(""); #ifdef SCORE_ON_BOTL if (::flags.showscore) { @@ -3315,7 +3305,7 @@ static char** rip_line=0; long year; /* Put name on stone */ - Sprintf(rip_line[NAME_LINE], "%s", gp.plname); + Sprintf(rip_line[NAME_LINE], "%s", svp.plname); /* Put $ on stone */ Sprintf(rip_line[GOLD_LINE], "%ld Au", done_money); @@ -4042,7 +4032,7 @@ void NetHackQtMainWindow::keyPressEvent(QKeyEvent* event) void NetHackQtMainWindow::closeEvent(QCloseEvent* e) { - if ( gp.program_state.something_worth_saving ) { + if ( program_state.something_worth_saving ) { switch ( QMessageBox::information( this, "NetHack", "This will end your NetHack session", "&Save", "&Cancel", 0, 1 ) ) @@ -4634,7 +4624,7 @@ void NetHackQtBind::qt_askname() NetHackQtSavedGameSelector sgsel((const char**)saved); ch = sgsel.choose(); if ( ch >= 0 ) - strcpy(gp.plname,saved[ch]); + strcpy(svp.plname,saved[ch]); } free_saved_games(saved); @@ -4854,7 +4844,7 @@ void NetHackQtBind::qt_update_inventory() if (main) main->updateInventory(); /* doesn't work yet - if (gp.program_state.something_worth_saving && iflags.perm_invent) + if (program_state.something_worth_saving && iflags.perm_invent) display_inventory(NULL, FALSE); */ } @@ -4908,14 +4898,14 @@ int NetHackQtBind::qt_nhgetch() // while (keybuffer.Empty() #ifdef SAFERHANGUP - && !gp.program_state.done_hup + && !program_state.done_hup #endif ) { qApp->enter_loop(); } #ifdef SAFERHANGUP - if (gp.program_state.done_hup && keybuffer.Empty()) return '\033'; + if (program_state.done_hup && keybuffer.Empty()) return '\033'; #endif return keybuffer.GetAscii(); } @@ -4929,13 +4919,13 @@ int NetHackQtBind::qt_nh_poskey(int *x, int *y, int *mod) // while (keybuffer.Empty() && clickbuffer.Empty() #ifdef SAFERHANGUP - && !gp.program_state.done_hup + && !program_state.done_hup #endif ) { qApp->enter_loop(); } #ifdef SAFERHANGUP - if (gp.program_state.done_hup && keybuffer.Empty()) return '\033'; + if (program_state.done_hup && keybuffer.Empty()) return '\033'; #endif if (!keybuffer.Empty()) { return keybuffer.GetAscii(); @@ -5157,16 +5147,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]; @@ -5184,7 +5164,7 @@ bool NetHackQtBind::notify(QObject *receiver, QEvent *event) bool result=QApplication::notify(receiver,event); #ifdef SAFERHANGUP - if (gp.program_state.done_hup) { + if (program_state.done_hup) { keybuffer.Put('\033'); qApp->exit_loop(); return TRUE; @@ -5290,9 +5270,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 522f57d699..c5548e37c3 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 77f10bfd58..c81c8f6680 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, @@ -501,7 +500,7 @@ Gem_player_selection() void Gem_askname() { - strncpy(gp.plname, mar_ask_name(), PL_NSIZ); + strncpy(svp.plname, mar_ask_name(), PL_NSIZ); } void @@ -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. */ @@ -912,21 +901,19 @@ int glyph, bkglyph; if (mar_set_tile_mode(-1)) { mar_print_glyph(window, x, y, glyph2tile[glyph], glyph2tile[bkglyph]); if ( -#ifdef TEXTCOLOR iflags.hilite_pet && -#endif glyph_is_pet(glyph)) mar_add_pet_sign(window, x, y); } else 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; @@ -1072,7 +1059,7 @@ time_t when; } /* Follows same algorithm as genl_outrip() */ /* Put name on stone */ - Sprintf(rip_line[NAME_LINE], "%s", gp.plname); + Sprintf(rip_line[NAME_LINE], "%s", svp.plname); /* Put $ on stone */ Sprintf(rip_line[GOLD_LINE], "%ld Au", done_money); /* Put together death description */ diff --git a/outdated/win/gem/wingem1.c b/outdated/win/gem/wingem1.c index 75c96918d8..594c3ccbfc 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 49ed102519..a298af5a0b 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, @@ -354,7 +353,7 @@ gnome_askname() /* Ask for a name and stuff the response into plname, a nethack global */ ret = ghack_ask_string_dialog("What is your name?", "gandalf", - "GnomeHack", gp.plname); + "GnomeHack", svp.plname); /* Quit if they want to quit... */ if (ret == -1) { @@ -907,7 +906,7 @@ gnome_nhgetch() g_askingQuestion = 1; /* Process events until a key press event arrives. */ while (g_numKeys == 0) { - if (gp.program_state.done_hup) + if (program_state.done_hup) return '\033'; gtk_main_iteration(); } @@ -945,7 +944,7 @@ gnome_nh_poskey(int *x, int *y, int *mod) g_askingQuestion = 0; /* Process events until a key or map-click arrives. */ while (g_numKeys == 0 && g_numClicks == 0) { - if (gp.program_state.done_hup) + if (program_state.done_hup) return '\033'; gtk_main_iteration(); } @@ -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 @@ -1169,7 +1145,7 @@ gnome_outrip(winid wid, int how, time_t when) long year; /* Put name on stone */ - Sprintf(buf, "%s\n", gp.plname); + Sprintf(buf, "%s\n", svp.plname); Strcat(ripString, buf); /* Put $ on stone */ diff --git a/outdated/win/gnome/gnbind.h b/outdated/win/gnome/gnbind.h index b0f56e0358..e39a56380d 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/outdated/win/gnome/gnmap.c b/outdated/win/gnome/gnmap.c index 98e80b485b..cd123e3d20 100644 --- a/outdated/win/gnome/gnmap.c +++ b/outdated/win/gnome/gnmap.c @@ -358,11 +358,7 @@ ghack_map_print_glyph(GtkObject *win, guint x, guint y, GdkImlibImage *im, if (x == u.ux && y == u.uy) ghack_map_cliparound(NULL, x, y, NULL); - if (glyph_is_pet(glyph) -#ifdef TEXTCOLOR - && iflags.hilite_pet -#endif - ) { + if (glyph_is_pet(glyph) && iflags.hilite_pet) { gnome_canvas_item_raise_to_top(GNOME_CANVAS_ITEM(canvas_image)); gnome_canvas_item_show(GNOME_CANVAS_ITEM(canvas_image)); } else { diff --git a/outdated/win/gnome/gnstatus.c b/outdated/win/gnome/gnstatus.c index 0ac29823a0..045b40fa6c 100644 --- a/outdated/win/gnome/gnstatus.c +++ b/outdated/win/gnome/gnstatus.c @@ -440,7 +440,7 @@ ghack_status_window_update_stats() long umoney; /* First, fill in the player name and the dungeon level */ - strcpy(buf, gp.plname); + strcpy(buf, svp.plname); if ('a' <= buf[0] && buf[0] <= 'z') buf[0] += 'A' - 'a'; strcat(buf, " the "); @@ -662,7 +662,7 @@ ghack_status_window_update_stats() } if (flags.time) { - sprintf(buf, "Time:%ld", gm.moves); + sprintf(buf, "Time:%ld", svm.moves); gtk_label_set(GTK_LABEL(timeLabel), buf); } else gtk_label_set(GTK_LABEL(timeLabel), ""); diff --git a/sound/fmod/fmod.c b/sound/fmod/fmod.c new file mode 100644 index 0000000000..26ad2fff3b --- /dev/null +++ b/sound/fmod/fmod.c @@ -0,0 +1,104 @@ +/* fmod.c */ +/* Copyright (c) Taylor Daley, 2023. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifdef SND_LIB_FMOD + +#include "hack.h" +#include "fmod.h" + +static FMOD_SYSTEM *systemvar; +static FMOD_SOUND *soundvar; +static FMOD_CHANNEL *channel1; +static FMOD_CHANNEL *channelMus; + +static void fmod_init_nhsound(void); +static void fmod_exit_nhsound(const char *); +static void fmod_achievement(schar, schar, int32_t); +static void fmod_soundeffect(char *, int32_t, int32_t); +static void fmod_hero_playnotes(int32_t instrument, const char *str, int32_t volume); +static void fmod_play_usersound(char *, int32_t, int32_t); +static void fmod_ambience(int32_t, int32_t, int32_t); +static void fmod_verbal(char *, int32_t, int32_t, int32_t, int32_t); + +struct sound_procs fmod_procs = { + SOUNDID(fmod), + SOUND_TRIGGER_USERSOUNDS | 0, + fmod_init_nhsound, + fmod_exit_nhsound, + fmod_achievement, + fmod_soundeffect, + fmod_hero_playnotes, + fmod_play_usersound, + fmod_ambience, + fmod_verbal +}; + +static void +fmod_init_nhsound(void) +{ + /* Initialize external sound library */ + FMOD_System_Create(&systemvar, FMOD_VERSION); + FMOD_System_Init(systemvar, 32, FMOD_INIT_NORMAL, 0); +} + +static void +fmod_exit_nhsound(const char *reason) +{ + /* Close / Terminate external sound library */ + FMOD_System_Close(systemvar); + FMOD_System_Release(systemvar); +} + +/* fulfill SNDCAP_ACHIEVEMENTS */ +static void +fmod_achievement(schar ach1, schar ach2, int32_t repeat) +{ + // to be added in future +} + +/* fulfill SNDCAP_SOUNDEFFECTS */ +static void +fmod_soundeffect(char *desc, int32_t seid, int32_t volume) +{ + // to be added in future +} + +/* fulfill SNDCAP_HEROMUSIC */ +static void fmod_hero_playnotes(int32_t instrument, const char *str, int32_t volume) +{ + // to be added in future +} + +/* fulfill SOUND_TRIGGER_USERSOUNDS */ +static void +fmod_play_usersound(char *filename, int32_t volume UNUSED, int32_t idx UNUSED) +{ + FMOD_System_CreateSound(systemvar, filename, FMOD_CREATESAMPLE, 0, + &soundvar); + if (strstr(filename, "music_") != NULL) { + FMOD_Channel_Stop(channelMus); + FMOD_System_PlaySound(systemvar, soundvar, 0, 0, &channelMus); + FMOD_Channel_SetMode(channelMus, FMOD_LOOP_NORMAL); + } else { + FMOD_Channel_Stop(channel1); + FMOD_System_PlaySound(systemvar, soundvar, 0, 0, &channel1); + } +} + +static void +fmod_ambience(int32_t ambienceid, int32_t ambience_action, + int32_t hero_proximity) +{ + // to be added in future +} + +static void +fmod_verbal(char *text, int32_t gender, int32_t tone, + int32_t vol, int32_t moreinfo) +{ + // to be added in future +} + +#endif /* SND_LIB_FMOD */ + /* end of fmod.c */ diff --git a/sound/wav/sa2_xplevelup.uu b/sound/wav/sa2_xplevelup.uu index c10aa4e922..04e23cfed5 100644 --- a/sound/wav/sa2_xplevelup.uu +++ b/sound/wav/sa2_xplevelup.uu @@ -1,10276 +1,4722 @@ begin 666 sa2_xplevelup.wav -M4DE&1J`-!P!7059%9FUT(!`````#``(`@+L```#$32<'A$T)GY!-"9^030F?D$T)GY! -M-)P>D32<'I$TYW&'-.=QAS12RYHT4LN:-(V;@C2-FX(T)G[!-"9^P320U]0T -MD-?4-/HPZ#3Z,.@T98K[-&6*^S1A3BDU84XI-9P>$36<'A$U4LL:-5++&C4' -M>"0U!W@D-;PD+C6\)"XU<=$W-7'1-S6OW7$UK]UQ-9+U?362]7TUN@:%-;H& -MA36K$HLUJQ*+-?HP:#7Z,&@UK]UQ-:_=<35_-ITU?S:=-7!"HS5P0J,U84ZI -M-6%.J3526J\U4EJO-41FM35$9K4U-7*[-35RNS4F?L$U)G[!-1>*QS47BL4US<7E-=E]#3;9 -M?0TVG!X1-IP>$39@OQ0V8+\4-B1@D8!@VZ``<-N@`'#:LH1\VK*$?-BUX -M/C8M>#XV-.,F-C3C)C9,[T8V3.]&-MLJ2S;;*DLV:F9/-FIF3S;YH5,V^:%3 -M-HG=5S:)W5*1S;;*DLV -MVRI+-I_+3C:?RTXV8VQ2-F-L4C8G#58V)PU6-D1F-39$9C4VKTY=-J].739S -M[V`V<^]@-C:09#8VD&0V^C!H-OHP:#:^T6LVOM%K-A>*1S87BD/#HVGCPZ-LNG/#;+ISPV -M^1(_-OD2/S8F?D$V)GY!-E/I0S93Z4,V@%1&-H!41C;"CQ8VPH\6-B1@D -M8!@V");--0B6S34V`=`U-@'0-6-LTC5C;-(UK*$?-JRA'S:]0MNM -MV37KK=DU&!E<-1@97#5%A-XU183>-0````````````````````#-Q66US<5E -MM?HP:#7Z,&@U``````````!5!VTU50=M-0````````````````````#=2'2U -MW4ATM0``````````-Q]YM3;5EBGNU98I[M9+U_;62]?VU8#``MF`P`+;Q -M&$*V\1A"ME/I0[93Z4.V(]&#MB/1@[87BD>V%XI'ME$\AK91/(:VVRI+MMLJ -M2[9^IXBV?J>(MA7=B;85W8FV5M>MME;7K;926J^V4EJOMD_=L+9/W;"V2V"R -MMDM@LK9'X[.V1^.SMD1FM;9$9K6V37[;MDU^V[:O3MVVKT[=MA$?W[81']^V -M<^_@MG/OX+8\102W/$4$MR!4!;<@5`6WNY49M[N5&;=2RQJW4LL:M^@`'+?H -M`!RW?S8=MW\V';<5;!ZW%6P>MZRA'[>LH1^W*_(TMROR-+=U3C:W=4XVMPP3 -M3+<,$TRW")9-MPB63;<%&4^W!1E/MP&<4+"0;W2WW+E?M]RY7[?8/&&WV#QAM]2_8K?4OV*WT4)DM]%"9+?- -MQ66WS<5EM\E(9[?)2&>WK0F`MZT)@+>$WH"WA-Z`M[[1:[>^T6NWNU1MM[M4 -M;;>WUVZWM]=NM[-:<+>S6G"WK]UQMZ_=<;>2VX6WDMN%MZCC=+>HXW2W086' -MMT&%A[<86HBW&%J(M_`NB;?P+HFWQP.*M\<#BK>?V(JWG]B*MW>MB[=WK8NW -M54B9MU5(F;>&,)JWAC":M[<8F[>W&)NWU0"/M]4`C[<9Z9RW&>F[F>MWNYGK1IY6W:7R6MVE\EK>OCXFWKX^)MRU1BKKHE8RWJFE^MZII?K?TQ7^W],5_MQ^1@+#M_NEA+?[I82W(%2%MR!4A;=$ -M`H:W1`*&MVFPAK=IL(:WCEZ'MXY>A[>S#(BWLPR(MT83<[=&$W.WW4ATM]U( -M=+>ESE:WIQ8MVWL6+#ZW^7H?M_EZ'[=W/""W=SP@M_;](+?V_2"W -M]F4!M_9E`;?!``*WP0`"MU/IP[93Z<.VA-'$MH31Q+8CT8.V(]&#MN]KA+;O -M:X2VN@:%MKH&A;:%H06VA:$%ME$\!K91/`:V'-<&-AS7!C;G<0(-GZGB#9)0HDV24*)-I_+SC:?R\XVT+//-M"SSS:K$@LWJQ(+ -M-]28+C?4F"XW8VQ2-V-L4C?1&S`WT1LP-T_=,#=/W3`W]B15-_8D53<#NGDW -M`[IY-^;(>C?FR'HW!NF/-P;ICS=+E*(W2Y2B-W!"HS=P0J,WE?"C-Y7PHS>Y -MGJ0WN9ZD-]Y,I3?>3*4W/&RX-SQLN#>Z+;DWNBVY-R6'S#CU^$W)J7U-R:E]3>QH/8WL:#V-R!4!3@@5`4XDML% -M.)+;!3A?_`\X7_P/.'Z-$#A^C1`X4LL:.%++&C@=9ALX'68;./?`)3CWP"4X -M;V4F.&]E)CA/W3`X3]TP.(AH.SB(:#LX&P=&.!L'1CB9R$8XF.*O17CB"IE\X@J9?.*CC -M=#BHXW0X!8YK.`6.:S@*M'8X"K1V.#N<=S@[G'4X,X7E.#.$W,@SA-S(,XKX^).*^/B3B^7(\X -MOER/.%G?CSA9WX\X]6&0./5AD#B0Y)`XD.20."QGD3@L9Y$XQ^F1.,?ID3BR -MV).))=GCC;Z9XXV^F>."-VGS@C=I\XZH:E..J& -MI3@)&*8X"1BF.%0WK#A4-ZPX1CJG.$8ZIS@^8ZTX/F.M.#/YK3@S^:TXK3"T -M.*TPM#AXR[0X>,NT.'81NSAV$;LX#P&V.`\!MCC:F[8XVINV.*8VMSBF-K0]@X;>S8.&WLV#B[E=DXNY79.`D_VC@)/]HX6.C:.%CHVCBF -MD=LXII';./0ZW#CT.MPX0^3<.$/DW#@1.=2;9./'*V3CQRMDX:6_:.&EOVCCA$]LXX1/;.%FXVSA9N-LXT5S< -M.-%3U#C7D]0XRS_<.,L_W#@4 -MMM4X%+;5.#-'UC@S1]8X^J[/./JNSSB?E]XXGY?>.(K'T#B*Q]`XK8O8.*V+ -MV#@;X-$X&^#1.&-LTCAC;-(X3;++.$VRRSCSA-,X\X33.#'!S#@QPSCBESM8XI<[6.-SMSSC<[<\X -M->?7.#7GUSC`_-`XP/S0.%I[X#A:>^`X#HS9.`Z,V3A6&-HX5AC:.)^DVCB? -MI-HXPD[J.,).ZCCS4.,X\U#C.*QZZSBL>NLX,'/D.#!SY#C=2/0XW4CT.*CC -M]#BHX_0XKJ%#GJZA0Y9T05.6=$ -M%3GEG14YY9T5.?T$&CG]!!HYYF`:.>9@&CE`SQXY0,\>.;<8&SFW&!LYZ(L? -M.>B+'SD[ZA\Y.^H?.:UD)#FM9"0Y;,4D.6S%)#D@1RDY($<"HYGG`J.6K^+CEJ_BXY"Y$S.0N1,SD,^3,Y#/DS.>^2.#GO -MDC@Y6_TX.5O].#E_GCTY?YX].7I$0CEZ1$(YO;-".;VS0CGY8$4!1.:3^53FD_E4YI<%:.:7!6CG_/%LY_SQ; -M.5FX6SE9N%LY6]9D.5O69#G,`F$YS`)A.;O693F[UF4Y@:]J.8&O:CD=C6\Y -M'8UO.202<#DD$G`Y`?=T.0'W=#FUX'DYM>!Y.9)J>CF2:GHYB%M_.8A;?SFJ -M*((YJBB".01P@CD$<((YBNZ$.8KNA#E\;XI#G,]:0YS/6D.5;DJ3E6Y*DYU#VJ.=0]JCEXY:PY>.6L.2Q`K3DL -M0*TY`SRR.0,\LCGJZK0YZNJT.3U)M3D]2;4YQ/NW.<3[MSFWL+HYM["Z.781 -MNSEV$;LY".8?SWCF'\]XY8M#A.6+0X3G:0.(YVD#B.5F1YSE9D>=^@Y'G?H.:9=ZSFF7>LYOM'K.;[1ZSG71>PYUT7L.30Q[SDT,>\Y@J;O -M.8*F[SG1&_`YT1OP.04,\SD%#/,YB(+S.8B"\SE==O8Y77;V.9UL^3F=;/DY -MSV7W.<]E]SE[7OHY>U[Z.6G7^CEIU_HYM=/].;73_3G:3?XYVDW^.>.F`#KC -MI@`ZD.0`.I#D`#H](@$Z/2(!.I^D`CJ?I`(Z-B@$.C8H!#H89P0Z&&<$.ONE -M!#K[I00Z_2L&.OTK!CI[:P8Z>VL&.DWS!SI-\P -M"!(ZG@@2.KQ+$CJ\2Q(ZW.`3.MS@$SJ5)!0ZE204.H:[%3J&NQ4ZV?\5.MG_ -M%3HM1!8Z+406.HG=%SJ)W1_APZ -M`D4=.@)%'3K5YAXZU>8>.I0M'SJ4+1\Z4W0?.E-T'SJ271XZDET>.M`!(#K0 -M`2`ZV^D>.MOI'CI.CR`Z3H\@.B-V'SHC=A\Z1[P?.D>\'SIK`B`Z:P(@.H]( -M(#J/2"`ZLXX@.K..(#K8U"`ZV-0@.G&W'SIQMQ\Z^OP?.OK\'SK#W1XZP]T> -M.K$B'SJQ(A\ZH&.C3]'CKM -M0!\Z[4`?.J:$'SJFA!\Z7\@?.E_('SI%GAXZ19X>.F3A'CIDX1XZ@B0?.H(D -M'SJ@9Q\ZH&.@1]'CH43ATZ%$X=.OR/'3K\CQTZY=$= -M.N71'3K.$QXZSA,>.K95'CJV51XZ)2,=.B4C'3I?[QLZ7^\;.A(P'#H2,!PZ -MQ7`<.L5P'#J4.ALZE#H;.BT#&CHM`QHZQ+H;.L2Z&SHG@AHZ)X(:.J7!&CJE -MP1HZ.(<9.CB'&3H:QADZ&L89.MV)&#K=B1@Z)<@8.B7(&#K"#18ZP@T6.L3' -M%SK$QQU$..E&*#CI1B@XZ6SX-.EL^#3HP\0LZ,/$+.LP<"3K,'`DZ.%,).CA3"3JC -MB0DZHXD).@_`"3H/P`DZG^4&.I_E!CK5&@+^ -M.9GB_CG>"_DYW@OY.9UL^3F=;/DY_JWV.?ZM]CD;+OHY&R[Z.4A+]#E(2_0Y -MG8;Q.9V&\3F'O^XYA[_N.7`;[SEP&^\Y&BKI.1HJZ3F7@^DYEX/I.0JTYCD* -MM.8Y4@SG.5(,YSFN#>$YK@WA.8MCX3F+8^$YAXO>.8>+WCDNX-XY+N#>.8D$ -MW#F)!-PY^U?<.?M7W#FU>-DYM7C9.027UCD$E]8YZ++3.>BRTSE@S-`Y8,S0 -M.6[CS3ENX\TY$/C*.1#XRCE("L@Y2`K(.10:Q3D4&L4Y="?".70GPCG0]+LY -MT/2[.?0ZO#GT.KPY%$&Y.11!N3G(1+8YR$2V.1%&LSD11K,YE(BS.92(LSEA -M0:TY84&M.7F!K3EY@:TY!3.G.04SISFR<*%.3C(A3DXR(4Y -M+I^".2Z?@CDP,G@Y,#)X.:V+>#FMBW@Y%2MR.14K.4;L7CD7/%\Y%SQ?.;K%6#FZQ5@Y[8%+.>V!2SE]RDLY?5C(Y([3"4Y2Z$>.4NA'CGLV!XY -M[-@>.1@F&#D8)A@Y;6X1.6UN$3DXH1$Y.*$1.0/4$3D#U!$YJQ(+.:L2"SE] -M3`0Y?4P$.7)Z!#ER>@0Y9J@$.6:H!#D7L?LX%['[.+8'[CBV!^XXIU3@.*=4 -MX#@-HN`X#:+@.'O@TCA[X-(X"RG3.`LITSCV6,4X]EC%.#5_MS@U?[;J3C3U:DXT]6I..+CFSCBXYLX&!F<.!@9G#BD&(XXI!B..`-)CC@#28XX -M##J`.`PZ@#B598`XE66`.#:09#@VD&0XG-UD.)S=9#@"*V4X`BME.%K)2#A: -MR4@X6E0L.%I4+#AFCBPX9HXL.%_\#SA?_`\X_JWF-_ZMYC<>71`X'ET0.,E( -MYS?)2.PZJTWL.JM-[PDKC>\)*XWR%ZN-\A>KC?4F*XWU)BN -M-^#2KC?@TJXW[0RO-^T,KS?Y1J\W^4:O-UP!:C=<`6HWPDYJ-\).:C<>]:\W -M'O6O-RHOL#^T6LW)!]L-R0?;#=G -M4;$W9U&Q-^^Y;#?ON6PW@,6Q-X#%L3>,_[$WC/^Q-R"B;3<@HFTWAN]M-X;O -M;3?L/&XW[#QN-U&*;C=1BFXWR2&S-\DALS<<)6\W'"5O-X)R;S>"S6O`W$WZT-Q-^M#=^]?`W?O7P-\_)%CC/R18X -M+OH6."[Z%CB.*AWCR-WMX\C>LNQHX_0WRN2W-\KDMS?7'K@WUQZX-]G+]3?9R_4W[Y*X-^^2N#?[S+@W^\RX -M-P<'N3<'![DW%$&Y-Q1!N3?53G#=LA'@W79VZ-UV=NC=IU[HW:=>Z-YUL^3>=;/DW@DN[-X)+NS>. -MA;LWCH6[-YJ_NS>:O[LW-*+Z-S2B^C>9[_HWF>_Z-Q\&'3@?!ATX?S8=.'\V -M'3C>9ATXWF8=.#Z7'3@^EQTX\%4]./!5/3C\CSTX_(\].+5K73BU:UTX;J]= -M.&ZO73@LJ'TX+*A].)+U?3B2]7TX]T)^./="?C@T,8\X-#&/.)I*GSB:2I\X -M^7J?./EZGSA9JY\X6:N?.!C8KS@8V*\X30VP.$T-L#B/2,`XCTC`.)R"P#B< -M@L`XJ+S`.*B\P#BT]L`XM/;`."9*T3@F2M$X"(G1.`B)T3CKQ]$XZ\?1.,T& -MTCC-!M(XL$72.+!%TCB3A-(XDX32..'YXCCA^>(X6`+3.%@"TSA3@>,X4X'C -M.!V`TS@=@-,XQ0CD.,4(Y#A]3.0X?4SD.#:0Y#@VD.0XIWO4.*=[U#B*NM0X -MBKK4.&%;Y3AA6^4XY07V..4%]C@R=]4X,G?5.(PFYCB,)N8X16KF.$5JYCC^ -MK>8X_JWF.+?QYCBW\>8X0;GW.$&Y]S@I>>%08YGA4&.5$\!CE1/`8Y -M-,D..33)#CE2\@XY4O(..7`;#SEP&P\YCD0/.8Y$#SFL;0\YK&T/.B_#SD&Z0\Y!ND/.:V+&#FMBQ@Y-[<8.3>W&#G`XA@YP.(8.4D. -M&3E)#ADYTCD9.=(Y&3G]ZB$Y_>HA.>60&3GED!DYYD8B.>9&(CG:="(YVG0B -M.%N/SGA;C\Y%Z0_.1>D -M/SE-V3\Y3=D_.:Q3-SFL4S.#FA43@YH5$X.6R$.#ELA#@Y-[W.#D!ZC@Y`>HX.`.CDBLSHY(K,Z.>SE.CGLY3HYMQ@[.;<8.SF"2SLY@DL[.:N0 -M,CFKD#(Y%[$[.1>Q.SEJ\3(Y:O$R.BR,SGHLC,Y1^,S.4?C,SFG$S0YIQ,T.09$-#D&1#0Y9G0T.69T-#G%I#0Y -MQ:0T.7W**SE]RBLY7NLB.5[K(CEE)BPY928L.5I4+#E:5"PY3H(L.4Z"+#E# -ML"PY0[`L.6+W-3EB]S4YPB"X -M-CG]PRTY_<,M.?'Q+3GQ\2TYY1\N.>4?+CG:32XYVDTN.=Y,)3G>3"4Y9W@E -M.6=X)3GQHR4Y\:,E.7K/)3EZSR4Y9L(<.6;"'#F,)B8YC"8F.152)CD54B8Y -MGGTF.9Y])CG>9ATYWF8=.;'4)CFQU"8Y&KD=.1JY'3DXXATY..(=.58+'CE6 -M"QXY=#0>.70T'CG&#!4YQ@P5.;"&'CFPAAXYSJ\>..0H" -M'SD*`A\Y*"L?.2@K'SE&5!\Y1E0?.61]'SED?1\Y@J8?.8*F'SF@SQ\YH,\? -M.<*/%CG"CQ8YW"$@.=PA(#G-N"DYS;@I.1AT(#D8="`Y-IT@.3:=(#E5QB`Y -M5<8@.7/O(#ES[R`YD1@A.9$8(3FO02$YKT$A..:1,7CFD3%XY -M^C!H.?HP:#D':V@Y!VMH.1.E:#D3I6@Y']]H.1_?:#F-SW(YC<]R.04,8XY8WF. -M.8NQB3F+L8DYA[^..8>_CCF9XHXYF>*..:L%CSFK!8\YO2B/.;THCSG%/)0Y -MQ3R4.0QAE#D,890Y5(64.52%E#DRGIDY,IZ9.7NYGCE[N9XY+>F9.2WIF3GA -M!I\YX0:?.0`GI#D`)Z0YZ4ZD.>E.I#GY>I\Y^7J?.;F>I#FYGJ0YHL:D.:+& -MI#F*[J0YBNZD.2$7JCDA%ZHY/T"J.3]`JCE=::HY76FJ.7N2JCE[DJHY'<&O -M.1W!KSEQZZ\Y<>NO.<45L#G%%;`Y/4FU.3U)M3EL:K`Y;&JP.5"@M3E0H+4Y -MV!;LY7@6[.1TRNSD=,KLYVUZ[.=M>NSF:B[LYFHN[.5FXNSE9 -MN+LY&.6[.1CENSG6$;PYUA&\.0A5P3D(5<$Y5&N\.51KO#GQL,$Y\;#!.>7> -MP3GEWL$Y(BC'.2(HQSE,5\-0Y!WC4.<1YSSG$><\Y/PS:.3\,VCG&#-4YQ@S5.=1QVCG4<=HY\&_5.?!O -MU3EIU]HY:=?:.0&EHW#E^FMPY?IK<.1/,W#D3S-PYJ/W<.:C]W#E+ -ME.(Y2Y3B.=)@W3G28-TY:)+=.6B2W3G]P]TY_.2.5&*WCE1BMXYYKO>.>:[WCEL7>0Y;%WD.3:0Y#DVD.0YIE#? -M.:90WSD[@M\Y.X+?.="SWSG0L]\Y9>7?.67EWSGZ%N`Y^A;@.8](X#F/2.`Y -M)'K@.21ZX#FZJ^`YNJO@.4_=X#E/W>`YY`[A.>0.X3EY0.$Y>4#A.0YRX3D. -M$Y.-7A.(Y^&GB.8V; -MXCF-F^(Y(LWB.2+-XCEA==TY877=.<&EW3G!I=TY(=;=.2'6W3F`!MXY@`;> -M.>`VWCG@-MXY/V?>.3]GWCDV*.0Y-BCD.0Y7OC>.5[XWCGUO.0Y -M];SD.8KNY#F*[N0Y'R#E.1\@Y3G^8Y,WOF.1A-[#D83>PY7M[F.5[> -MYCFNLNPYKK+L.7CE[#EXY>PY:;WR.6F]\CD.2^TY#DOM.=E][3G9?>TYH[#M -M.:.P[3EJC?,Y:HWS.3D6[CDY%NXY:_7S.6OU\SG.>^XYSGON.6Q=]#EL7?0Y -M9.'N.63A[CDN%.\Y+A3O.6WY]#EM^?0Y%N'Z.1;A^CF.K.\YCJSO.6Z5]3EN -ME?4Y;LGU.6[)]3GNM?LY[K7[.6XQ]CEN,?8Y62#\.5D@_#F/5?PYCU7\.6_- -M]CEOS?8Y<`'W.7`!]SDQ]?PY,?7\.6$$.@W]!#H-_00ZWA@%.MX8!3JN-`4Z -MKC0%.G]0!3I_4`4ZUU((.M=2"#I";P@Z0F\(.FIS"SIJ.D;L'CKM#!\Z[0P?.C,@(CHS("(Z -M=$$B.G1!(CJ+5B4ZBU8E.F=X)3IG>"4Z3H\H.DZ/*#K%L2@ZQ;$H.GW**SI] -MRBLZ:N0N.FKD+CJ,_S$ZC/\Q.M0C,CK4(S(Z&T@R.AM(,CJI934ZJ64U.HN* -M-3J+BC4Z;J\U.FZO-3IFSS@Z9L\X.N3T.#KD]#@Z81HY.F$:.3K>/SDZWC\Y -M.MUB/#K=8CPZ]8@\.O6(/#H-KSPZ#:\\.B75/#HEU3PZ/?L\.CW[/#K<(4`Z -MW"%`.H](0#J/2$`Z0F]`.D)O0#KUE4`Z]95`.IJ_0SJ:OT,ZZ.9#.NCF0SHV -M#D0Z-@Y$.H,U1#J#-40ZT5Q$.M%<1#H7BDB7SIKSE\Z:\Y?.@4,8SH% -M#&,ZPSAC.L,X8SHM>&8Z+7AF.H>E9CJ'I68ZP>9I.L'F:3JV%&HZMA1J.L%7 -M;3K!5VTZ4(9M.E"&;3HKRW`Z*\MP.E7Z<#I5^G`Z`$%T.@!!=#KBB'CKXT7HZ\P)[.O,">SK:37XZVDU^.F]_?CIO?WXZ$^:`.A/F@#H*C8(Z -M"HV".F^F@CIOIH(Z3DZ$.DY.A#K']H4ZQ_:%.L@0ACK($(8Z*;J'.BFZASHF -M9(DZ)F2).KT.BSJ]#HLZ7)F).ER9B3J.1(LZCD2+.G9?BSIV7XLZD`N-.I`+ -MC3K&)HTZQB:-.OQ!C3K\08TZ3.^..DSOCCK/"H\ZSPJ/.E(FCSI2)H\ZV-20 -M.MC4D#I878\Z6%V/.MMXCSK;>(\Z22B1.DDHD3H:1)$Z&D21.NI?D3KJ7Y$Z -MNWN1.KM[D3J+EY$ZBY>1.LE(DSK)2),ZYV23.N=DDSH%@9,Z!8&3.B.=DSHC -MG9,Z0;F3.D&YDSI0;)4Z4&R5.KN(E3J[B)4ZFPV4.IL-E#JY*90ZN2F4.M=% -ME#K7190Z:/J5.FCZE3K3%I8ZTQ:6.C\SECH_,Y8Z3K:4.DZVE#ILTI0Z;-*4 -M.HKNE#J*[I0ZJ`J5.J@*E3HUC),Z-8R3.@6HDSH%J),ZJBB2.JHHDCJGWY,Z -MI]^3.G?[DSIW^Y,Z,WN2.C-[DCJVEI(ZMI:2.NE.E#KI3I0ZN6J4.KEJE#J* -MAI0ZBH:4.O$_ECKQ/Y8Z*[Z4.BN^E#K[V90Z^]F4.LSUE#K,]90Z:;"6.FFP -MECJ'S)8ZA\R6.CU)E3H]294ZPP27.L,$ESK?@)4ZWX"5.O\\ESK_/)6.K+EESJRY9Y8Z,WN6.OOSE#K[\Y0Z?P^5.G\/E3I?AY,Z7X>3.I2BDSJ4 -MHI,ZC!F2.HP9DCIT-)(Z=#22.EU/DCI=3Y(Z'\60.A_%D#JZWY`ZNM^0.E7Z -MD#I5^I`ZX6Z/.N%NCSHOB8\Z+XF/.GRCCSI\HX\ZTQ:..M,6CCJ0B8PZD(F, -M.K'[BCJQ^XHZ.&V).CAMB3I0AHDZ4(:).FB?B3IHGXDZN0^(.KD/B#J#*(@Z -M@RB(.NR7ACKLEX8Z:;"&.FFPACKJ'H4ZZAZ%.AHWA3H:-X4ZLJ2#.K*D@SJ4 -MO(,ZE+R#.D4I@CI%*8(ZVD"".MI`@CJBK(`ZHJR`.NG#@#KIPX`ZDEU^.I)= -M?CH=,GLZ'3)[.G9?>SIV7WLZ,#)X.C`R>#JT`W4ZM`-U.M@O=3K8+W4ZC/]Q -M.HS_<3H5*W(Z%2MR.OGX;CKY^&XZIL5K.J;%:SH?D6@Z'Y%H.M>Z:#K7NF@Z -MD.1H.I#D:#J=K64ZG:UE.KO693J[UF4Z^)UB.OB=8CI[QF(Z>\9B.NB+7SKH -MBU\Z'E!<.AY07#IL=UPZ;'=<.M(Y63K2.5DZ`_M5.@/[53H;(58Z&R%6.L1Y -M3SK$>4\ZIIY/.J:>3SJ;6TPZFUM,.N-_3#KC?TPZ"#M).@@[23JU7DDZM5Y) -M.K&M0CJQK4(Z*-!".BC00CJ?\D(ZG_)".NZH/SKNJ#\Z!UX\.@=>/#I(?SPZ -M2'\\.I$R.3J1,CDZI.0U.J3D-3JP!#8ZL`0V.O*T,CKRM#(Z8]0R.F/4,CK5 -M\S(ZU?,R.JRA+SJLH2\Z3DXL.DY.+#JZ^2@ZNODH.O&C)3KQHR4Z\DPB.O), -M(CJ]]!XZO?0>.E.;&SI3FQLZLT`8.K-`&#K>Y!0ZWN04.M[^%#K>_A0Z.*$1 -M.CBA$3K>,A4ZWC(5.B=;#CHG6PXZ?/H*.GSZ"CJ:F`')0Y'AR4.0D8ACD)&(8Y`R^&.0,OACE:1W`Y -M6D=P.;Q+8CF\2V(Y(21&.2$D1CF0;U0YD&]4.=T[.#G=.S@YMHE&.;:)1CGL -M22HY[$DJ.?)F*CGR9BHYI!@..:08#CGI:!PYZ6@<.8,.`#F##@`YN,[C.+C. -MXS@0^*HX$/BJ.!85JS@6%:LXPJF..,*ICCB$:60XA&ED."EL*S@I;"LXZ;;D -M-^FVY#>U4>6WM5'EMTX:++A.&BRX -M5#]$$+GO1!"YU

N=7,'KF162VYD5DMN23K -M.[DDZSNYE0H\N94*/+EIHTJY::-*N4;%2KE&Q4JY6V59N5ME6;E("FBY2`IH -MN?HP:+GZ,&BY*-UVN2C==KE&!G>Y1@9WN60O=[ED+W>Y_3B*N?TXBKGX3XJY -M^$^*N;NOD;F[KY&YZA&9N>H1F;E/*YFY3RN9N1^1H+D?D:"Y6?FGN5GYI[G_ -M8Z^Y_V.ON1#1MKD0T;:Y3.^VN4SOMKG^7[ZY_E^^N6]_OKEO?[ZYP?/%N<'S -MQ;E_:LVY?VK-N5R,S;E'`0NL4\%+K% -M/!2Z]504NO54%+H2(QBZ$B,8NF7R&[IE\ANZ[L(?NN["'[JLE".ZK)0CND>O -M([I'KR.ZUH(GNM:")[J:5RNZFEZ"S8WNG81.[IV$3NZ37$WNDUQ-[KM33NZ -M[4T[NBEL.[HI;#NZFDH_NII*/[IP:3^Z<&D_NK))0[JR24.Z(VE#NB-I0[HU -M2T>Z-4M'ND%K1[I!:T>Z(T]+NB-/2[K*;TNZRF]+NGQ53[I\54^ZOG9/NKYV -M3[K_ET^Z_Y=/ND&Y3[I!N4^Z<&E7NG!I5[K6PU.ZUL-3NE^N5[I?KE>ZUM!7 -MNM;05[HOO5NZ+[U;ND'@6[I!X%NZ4P-*1@NGBD8+HER&"Z)JAQNGJH<;J2SG&ZDLYQ -MNJKT<;JJ]'&ZC/)UNHSR=;H_&7:Z/QEVNO$8>KKQ&'JZV1E^NMD9?KK\#8&Z -M_`V!NCTB@;H](H&Z-"2#NC0D@[K#.(.ZPSB#NJ,[A;JC.X6Z?U"%NG]0A;I; -M986Z6V6%NG!IA[IP:8>Z(&Z)NB!NB;J7@XFZEX.)N@Z9B;H.F8FZA:Z)NH6N -MB;JXM(NZN+2+NGW*B[I]RHNZ0>"+ND'@B[JJYXVZJN>-NJWOC[JM[X^Z#`:0 -MN@P&D+IK')"Z:QR0NJ0EDKJD)9*Z43R2NE$\DKJ)7Y"ZB5^0NJIIDKJJ:9*Z -M5X"2NE>`DKI@BY2Z8(N4NK&MDKJQK9*Z5+F4NE2YE+I/T)2Z3]"4NDGGE+I) -MYY2Z0_Z4ND/^E+H]%96Z/165NCZ0%&7NB9Q -ME;HF<96Z((B5NB"(E;H:GY6Z&I^5NLJ]D[K*O9.Z=]23NG?4D[HDZY.Z).N3 -MNM`!E+K0`92Z?1B4NGT8E+HJ+Y2Z*B^4NO$_EKKQ/Y:Z[%:6NNQ6EKKF;9:Z -MYFV6NN"$EKK@A):ZVIN6NMJ;EKHWMY2Z-[>4NL_)EKK/R9:ZR>"6NLG@EKK# -M]Y:ZP_>6NNH1E;KJ$96ZMR67NKZL3R7NJQ3E[JL4Y>ZIFJ7NJ9J -ME[KV?YFZ]G^9NIJ8E[J:F)>ZE*^7NI2OE[I0QY6Z4,>5NHG=E[J)W9>ZJO25 -MNJKTE;I6"Y:Z5@N6NH\AE+J/(92Z[C>4NNXWE+I.3I2Z3DZ4N@IFEKH*9I:Z -M#7N4N@U[E+ILD92Z;)&4NLNGE+K+IY2ZO<"6NKW`EKJ*U)2ZBM24NNKJE+KJ -MZI2ZS_V2NL_]DKJH%Y6ZJ!>5NO,ID[KS*9.Z9T25NF=$E;H75I.Z%U:3NBEL -MD[HI;).Z.X*3NCN"D[I-F).Z39B3NGJHD;IZJ)&Z<<23NG'$D[H#U)&Z`]21 -MNL?ID;K'Z9&ZC/^1NHS_D;I0%9*Z4!62NA4KDKH5*Y*ZVD"2NMI`DKI.3I"Z -M3DZ0NL5CD+K%8Y"Z/'F0NCQYD+JSCI"ZLXZ0NJ6:CKJEFHZZSJ^.NLZOCKK7 -MNHRZU[J,NB+:CKHBVHZZD.2,NI#DC+IM^8RZ;?F,NO,"B[KS`HNZ)2.-NB4C -MC;H1+(NZ$2R+NJ!`B[J@0(NZHTB)NJ-(B;KD7(FZY%R)NO]CA[K_8X>Z\W>' -MNO-WA[KHBX>ZZ(N'NMR?A[KZ=*6%NG2EA;H:N86Z&KF%NLJ]@[K*O8.Z -M(]&#NB/1@[I]Y(.Z?>2#NM;W@[K6]X.Z`_N!N@/[@;H/#H*Z#PZ"NALA@KH; -M(8*Z)S2"NB"NC]:@KH_6H*Z2VV"NDMM@KI7@(*Z5X""NLN` -M@+K+@("ZBI.`NHJ3@+I[N8*Z>[F"N@>Y@+H'N8"ZQLN`NL;+@+J?\H*ZG_*" -MND/Q@+I#\8"Z`@2!N@($@;IZ`WZZ>@-^NETH?KI=*'ZZ`R)ZN@,B>KHAKJ2:GJZVHYZNMJ.>KI[A7:Z>X5VNBBI=KHHJ7:ZUZ\UUGNC5_9[HU?V>Z=Z!G -MNG>@9[JXP6>ZN,%GNOKB9[KZXF>Z/`1HNCP$:+I])6BZ?25HNK]&:+J_1FBZ -M`6AHN@%H:+KI3F2ZZ4YDNH2J:+J$JFBZQLMHNL;+:+HQ*6VZ,2EMN@Y+;;H. -M2VVZZFQMNNIL;;K'CFVZQXYMNJ.P;;JCL&VZ@-)MNH#2;;I<]&VZ7/1MNCD6 -M;KHY%FZZ%GERNA9Y1G>Z7D9W -MNFHE<[IJ)7.ZX4=SNN%'<[I8:G.Z6&ISNM",<[K0C'.Z1Z]SND>O<[J^T7.Z -MOM%SNC;T<[HV]'.Z[UYXNN]>>+H!@GBZ`8)XNA.E>+H3I7BZ)+HW -MZWBZ-^MXNDD.>;I)#GFZ6S%YNELQ>;IM5'FZ;51YNG]W>;I_=WFZWDQUNMY, -M=;JCO7FZH[UYNK7@>;JUX'FZQP-ZNL<#>KK:)GJZVB9ZNNQ)>KKL27JZ_FQZ -MNOYL>KH0D'JZ$)!ZNJL%?[JK!7^Z--9ZNC36>KI&^7JZ1OEZNE@<>[I8''NZ -M:C][NFH_>[KM#'>Z[0QWNHZ%>[J.A7NZVU%WNMM1=[JRRWNZLLM[NLJ6=[K* -MEG>ZUA%\NM81?+JXVW>ZN-MWNC#^=[HP_G>ZIR!XNJ<@>+H>0WBZ'D-XNI5E -M>+J597BZ0^1\ND/D?+J$JGBZA*IXNF4U]NGE-?;J6YX"ZEN>` -MNIV3?;J=DWVZ0PN!ND,+@;K!V7VZP=E]NM/\?;K3_'VZY1]^NN4??KKW0GZZ -M]T)^NBT#>KHM`WJZ'(E^NAR)?KH;2'JZ&TAZNI)J>KJ2:GJZ"HUZN@J->KJ! -MKWJZ@:]ZNGMK=KI[:W:ZB%M_NHA;?[KG%GNZYQ9[NEXY>[I>.7NZ[?)VNNWR -M=KK)%'>ZR11WNJ8V=[JF-G>Z.\-[NCO#>[I>>G>Z7GIWNCN<=[H[G'>Z%[YW -MNA>^=[KTWW>Z]-]WNA&4<[H1E'.Z4[5SNE.U<[J5UG.ZE=9SNF9G>+IF9WBZ -M&!ETNA@9=+H?JWBZ'ZMXNOO,>+K[S'BZV.YXNMCN>+H?GG2Z'YYTNI$R>;J1 -M,GFZHN!TNJ+@=+KD`76ZY`%UNB4C=;HE(W6Z`[IYN@.Z>;K?VWFZW]MYNKS] -M>;J\_7FZF!]ZNI@?>KIT07JZ=$%ZNK#J=;JPZG6Z\0MVNO$+=KH*IWJZ"J=Z -MNG5.=KIU3G:ZMF]VNK9O=KI0%7*Z4!5RNO]-VNGO3=KI%=W*Z -M17=RNNR76ZZB!ISNH@:<[HN -M.W.Z+CMSNM5;<[K56W.Z+OINNB[Z;KHZ&F^Z.AIOND8Z;[I&.F^Z<=YSNG'> -M<[H8_W.Z&/]SNFN:;[IKFF^Z=[IONG>Z;[J#VF^Z@]IONH_Z;[J/^F^ZFQIP -MNIL:<+JG.G"ZISIPNK-:<+JS6G"Z3P1UND\$=;K+FG"ZRYIPNM>Z<+K7NG"Z -MY-IPNN3:<+KP^G"Z\/IPNOP:<;K\&G&Z"#MQN@@[<;K?Z'6ZW^AUNH8)=KJ& -M"7:Z+)MQNBR;<;HXNW&Z.+MQNGMK=KI[:W:Z(HQVNB*,=KK(K':ZR*QVNFD[ -MZO0YWNHV;ZLG!W -MNK'[@;[KH -MOV^ZZ+]ONG!":[IP0FNZ1V%KND=A:[H=@&NZ'8!KNCH`9[HZ`&>Z=AYGNG8> -M9[JQ/&>ZL3QGNNU:9[KM6F>Z*7EGNBEY9[IEEV>Z99=GND838[I&$V.ZYS!C -MNNZ&/)GNBEL8[HI;&.ZCRYHNH\N:+K+3&BZRTQHN@=K:+H':VBZ -MK.)CNJSB8[I-`&2Z30!DNNX=9+KN'62ZCSMDNH\[9+HP662Z,%EDNM%V9+K1 -M=F2Z.^I?NCOJ7[H3LF2Z$[)DND@D8+I()&"Z5>UDNE7M9+I47F"Z5%Y@NI0QA -MNG\I8;I_*6&Z#95=8;JZ!EVZ -MN@9=NJ/78;JCUV&ZD3]=NI$_7;K\6UVZ_%M=NAG"6+H9PEBZZ=U8NNG=6+JZ -M^5BZNOE8NHL56;J+%5FZH7A4NJ%X5+K7DU2ZUY-4N@VO5+H-KU2ZN0]0NKD/ -M4+I4*E"Z5"I0NB^)2[HOB4NZU>9&NM7F1KHZ`$>Z.@!'NI\91[J?&4>ZVG1" -MNMIT0KK?SCVZW\X]N@_G/;H/YSVZ/_\]NC__/;I"EC2Z0I8TNCVM-+H]K32Z -M:P(PNFL",+ID5BNZ9%8KNBEL*[HI;"NZ4;XFNE&^)KI%#R*Z10\BN@)?';H" -M7QVZBJT8NHJM&+KCP!BZX\`8NIL-%+J;#12Z'5D/NAU9#[IIHPJZ::,*NH#L -M!;J`[`6ZJ-;XN:C6^+D9]OBY&?;XN06![[D%@>^YA@GFN88)YKF$MT[E\2-.Y?$C3N1=CT[D78].YM>#)N;7@R;GI6\"Y -MZ5O`N1ATP+D8=,"YJ^NVN:OKMKFE`K>YI0*WN9=VK;F7=JVY'NBCN1[HH[FM -M_*.YK?RCN9)JFKF2:IJY#=:0N0W6D+D=/X>Y'3^'N8)+>[F"2WNY\VI[N?-J -M>[F0UU2YD-=4N5:60;E6ED&YA:Y!N86N0;D*9BZY"F8NN;<8&[FW&!NY$2P; -MN1$L&[D?W^BX']_HN"7\Z+@E_.BXI#_"N*0_PKAV>9NX=GF;N#A3:;@X4VFX -M*:";MRF@F[>#LYNW@[.;M]S&F[?/[9LWC^V;-^@`G#?H -M`)PW0A0<.$(4'#B;)QPXFR<<./0ZG#CT.IPX3DZ<.$Y.G#@1>L,X$7K#.$&2 -MPSA!DL,XA\SJ.(?,ZCB-Z>HXC>GJ."L9"3DK&0DY&BH).1HJ"3EX<#`Y>'`P -M.3R&,#D\AC`YCSM$.8\[1#F_4T0YOU-$.5,06#E3$%@YOM%K.;[1:SG$[FLY -MQ.YK.7&W?SEQMW\Y>L*).7K"B3FFJY,YIJN3.3Z7G3D^EYTY[L^3.>[/DSGQ -MO9TY\;V=.5^NISE?KJU-DYGM39.2_7XSDOU^,Y`//C -M.0#SXSF5X_/W.=`!^#G0`?@Y;08!.FT&`3HE%@$Z)18!.GH=!CIZ'08Z -M!28+.@4F"SKS-@LZ\S8+.N%'"SKA1PLZUU(0.M=2$#H"7Q4Z`E\5.B9Q%3HF -M<14Z2H,5.DJ#%3K@D1HZX)$:.JRA'SJLH1\ZKK(D.JZR)#JBQB0ZHL8D.G39 -M*3ITV2DZ`^XI.@/N*3JE`B\ZI0(O.GT8-#I]�Z0BXT.D(N-#KJ13DZZD4Y -M.DE<.3I)7#DZJ7(Y.JER.3J\C#XZO(P^.K>C/CJWHSXZL;H^.K&Z/CHOUT,Z -M+]=#.L3N0SK$[D,ZG_\^.I__/CKN'40Z[AU$.G,]23IS/4DZHU5).J-523K2 -M;4DZTFU).L*/3CK"CTXZC:A..HVH3CI7P4XZ5\%..K+E4SJRY5,Z&/]3.AC_ -M4SI#)5DZ0R59.D,_63I#/UDZ0UE9.D-963I$.D;L7CI&[%XZ?1AD.GT89#JS,V0ZLS-D.NE.9#KI3F0Z -M'FID.AYJ9#IQI.IX(:CJ> -M"&HZ;B1J.FXD:CH_0&HZ/T!J.@]<:CH/7&HZX'=J.N!W:CHIK6\Z*:UO.I7) -M;SJ5R6\Z`.9O.@#F;SIK`G`Z:P)P.M<><#K7'G`Z0CMP.D([<#JM5W`ZK5=P -M.AAT<#H8='`ZA)!P.H20<#K9RW4ZV78Z_GEV.@27=CH$EW8Z"K1V.@JT=CH0 -MT78Z$-%V.A;N=CH6[G8Z'`MW.AP+=SHB*'#K9L7TZV;%].GK/?3IZSWTZ -MWXV!.M^-@3J\"GXZO`I^.ETH?CI=*'XZ_45^.OU%?CJ>8WXZGF-^.C^!?CH_ -M@7XZX)Y^.N">?CJ!O'XZ@;Q^.B+:?CHBVGXZP_=^.L/W?CID%7\Z9!5_.@4S -M?SH%,W\ZIE!_.J90?SI';G\Z1VY_.NB+?SKHBW\ZB*E_.HBI?SK:CGHZVHYZ -M.N"K>CK@JWHZYLAZ.N;(>CH&$(`Z!A"`.O,">SKS`GLZIRV`.JSKI:(`ZZ6B`.A>Q>SH7L7LZBH:`.HJ&@#HD -MZWLZ).M[.BND@#HKI(`Z^[*`.ONR@#I\8H,Z?&*#.IIQ@SJ:<8,ZN("#.KB` -M@SK6CX,ZUH^#.O2>@SKTGH,Z$:Z#.A&N@SHOO8,Z+[V#.G\I@3I_*8$Z3SB! -M.D\X@3J)ZH,ZB>J#.J?Y@SJG^8,ZP62!.L%D@3KB%X0ZXA>$.@`GA#H`)X0Z -M'C:$.AXVA#H\180Z/$6$.EI4A#I:5(0Z>&.$.GACA#JW&('.HXWASKY1H$H,Z+B&#.BXA@SIB -MW84Z8MV%.L\^@SK//H,ZH$V#.J!-@SIP7(,Z<%R#.D%K@SI!:X,Z*\N`.BO+ -M@#KBB(,ZXHB#.K*7@SJREX,Z@J:#.H*F@SI3M8,Z4[6#.B/$@SHCQ(,Z]-*# -M.O32@SK`,($ZP#"!.D,_@3I#/X$ZQDV!.L9-@3I)7($Z25R!.B9Q?3HF<7TZ -MD8U].I&-?3I41'@Z5$1X.FC&?3IHQGTZ%A5S.A85^U>.GOM7CJ[E5DZNY59.A$?7SH1'U\Z&L99.AK&63I*WEDZ -M2MY9.H2#5#J$@U0ZJ@Y:.JH.6CJNLE0ZKK)4.D/*5#I#RE0Z=FQ/.G9L3SIP -M@T\Z<(-/.FN:3SIKFD\Z,SI*.C,Z2CJ24$HZDE!*.O)F2CKR9DHZ3P1%.D\$ -M13H4&D4Z%!I%.M@O13K8+T4ZRLH_.LK*/SKTWS\Z]-\_.A9Y.CH6>3HZI8TZ -M.J6-.CHTHCHZ-*(Z.NHX-3KJ.#4ZWDPU.MY,-3K$X2\ZQ.$O.A[U+SH>]2\Z -M,X@J.C.(*CKRFBHZ\IHJ.CKS.8GJ\SFB[>@YHNWH.5#NW3E0[MTYD^S2.9/LTCF-`],Y -MC0/3.3#^QSDP_L4O+,WE+RS-P`````` -M````1^.SMT?CL[>A]K.WH?:SM_H)M+?Z";2W5!TTN%0=-+BM,#2XK3`TN`9$ -M-+@&1#2XB$&'N(A!A[@+4(>X"U"'N(Y>A[B.7H>X$6V'N!%MA[B4>X>XE'N' -MN!^XM+@?N+2X5O[AN%;^X;@=IP>Y':<'N:"U![F@M0>Y(\0'N2/$![G"=1ZY -MPG4>N3KD_#'JY(]&3N2/1 -MD[G+@(BYRX"(N97PD[F5\).Y30"4N4T`E+D&$)2Y!A"4N;\?E+F_'Y2YE)6? -MN925G[F"II^Y@J:?N7&WG[EQMY^Y7\B?N5_(G[E`1*NY0$2KN616J[ED5JNY -MB&BKN8AHJ[FL>JNYK'JKN=",J[G0C*NY])ZKN?2>J[D7L:NY%[&KN3O#J[D[ -MPZNY7]6KN5_5J[F#YZNY@^>KN9R"H+F<@J"YBI.@N8J3H+EXI*"Y>*2@N;HZ -ME;FZ.I6Y@N3'HH+DQZ*"YG7F5N9UYE;E6B96Y5HF5 -MN?P:H;G\&J&YQZB5N<>HE;F`N)6Y@+B5N3C(E;DXR)6Y\=>5N?'7E;FJYY6Y -MJN>5N6+WE;EB]Y6YMGR*N;9\BKDYBXJY.8N*N;R9BKF\F8JY/ZB*N3^HBKD/ -M3WZY#T]^N:II?KFJ:7ZY181^N46$?KG@GGZYX)Y^N5F19[E9D6>YB*EGN8BI -M9[F_E%"YOY10N82J4+F$JE"Y2V!"[EPD`NY<)`+N9:SZ+B6L^BX -MQLOHN,;+Z+CUX^BX]>/HN"7\Z+@E_.BXJG:ZN*IVNK@$BKJX!(JZN`;VB[@& -M]HNXM[`ZN+>P.K@0Q#JX$,0ZN&G7.KAIUSJXP^JZM\/JNK<<_CJX'/XZN'81 -MN[=V$;NWSR2[M\\DN["2[NWVUZ[M]M>N[:O[LW]-*[-_32NS<````` -M`````*?YNS>G^;LW``V\-P`-O#=9(#PX62`\.+,SO#>S,[PW#$<\.`Q'/#AF -M6CPX9EH\.+]M/#B_;3PX&8$\.!F!/#ARE#PXAT..?W=)3G]W24Y -MZ^XE.>ON)3G9_R4YV?\E.0G*/3D)RCTY8MT].6+=/3F[\#TYN_`].9?$53F7 -MQ%4Y7-I5.5S:53EZM6TY>K5M.;?*@CFWRH(Y!-B".038@CE2Y8(Y4N6".2+: -MCCDBVHXYI>B..:7HCCDH]XXY*/>..<_PFCG/\)HYAP";.8<`FSE`$)LY0!"; -M.;T.ISF]#J_.=#GOSEW^[,Y=_NS.9L-M#F;#;0YW"'`.=PA -MP#DV-<`Y-C7`.8](P#F/2,`YZ5O`.>E;P#E";\`Y0F_`.<6*S#G%BLPY5)_, -M.52?S#GCL\PYX[/,.3W4V#D]U-@Y`??D.0'WY#G\#>4Y_`WE.?8DY3GV).4Y -M\#OE.?`[Y3G!9/$YP63Q.?R/_3G\C_TY()7Q.2"5\3G'POTYQ\+].6WY!#IM -M^00ZN@8%.KH&!3J4(`LZE"`+.GPN"SI\+@LZ9#P+.F0\"SJI6!$ZJ5@1.BQG -M$3HL9Q$Z0847.D&%%SI?E!.2LZ3$HK.DQ**SH[6RLZ.ULK.BEL*SHI;"LZ,ETE -M.C)=)3H%CBLZ!8XK.O2>*SKTGBLZXJ\K.N*O*SK0P"LZT,`K.K[1*SJ^T2LZ -M,P8R.C,&,CJ;\RLZF_,K.D4I,CI%*3(ZSCHR.LXZ,CI73#(Z5TPR.N!=,CK@ -M73(ZD)8X.I"6.#KR@#(Z\H`R.M>Z.#K7NC@Z^\PX.OO,.#H?WS@Z']\X.D/Q -M.#I#\3@Z9P,Y.F<#.3J+%3DZBQ4Y.JQ3/SJL4S\Z:F8_.FIF/SHI>3\Z*7D_ -M.NB+/SKHBS\Z#\U%.@_-13IEL3\Z9;$_.B/$/SHCQ#\ZXM8_.N+6/SIT&D8Z -M=!I&.E_\/SI?_#\Z'@]`.AX/0#HX[SDZ..\Y.ILT0#J;-$`Z@!,Z.H`3.CH8 -M6D`Z&%I`.M=L0#K7;$`ZEG]`.I9_0#H/7#HZ#UPZ.C-N.CHS;CHZ5X`Z.E>` -M.CI[DCHZ>Y(Z.N]K-#KO:S0Z>'TT.GA]-#H!CS0Z`8\T.@K;.CH*VSHZ$[(T -M.A.R-#KFARXZYH86.M7F%CH?JQ`Z'ZL0.@>Y$#H'N1`Z[\80.N_&$#KA(!0Y',K* -M.1S*RCE-B]+XY -M;M:Q.6[6L3EUG+XY=9R^.?Q!I3G\0:4YM5&E.;51I3EM8:4Y;6&E.29QI3DF -M<:4YNL68.;K%F#D]U)@Y/=28.<#BF#G`XI@Y"+"E.0BPI3G+/XPYRS^,.4D. -MF3E)#IDY,JME.3*K93DO2'\Y+TA_.1A-3#D834PYCGA_.8YX?SE$`F8Y1`)F -M.>ZH?SGNJ'\Y?II,.7Z:3#F20V8YDD-F.3'!3#DQP4PYBM1,.8K43#GG2C,Y -MYTHS.=5;,SG56S,Y\O89.7KV&3E^ -M60`Y?ED`.8`3&CF`$QHYK7$`.:UQ`#G%?0`YQ7T`.=V)`#G=B0`Y]94`.?65 -M`#D5T,TX%=#-.&[CS3ANX\TXQ_;-.,?VS3@A"LXX(0K..'H=SCAZ'?R\XW```````````````````````````` -M````!1G/MP49S[=>+,^W7BS/M[<_S[>W/\^W$5-/N!%33[AJ9D^X:F9/N%.; -MF[A3FYNXUJF;N-:IF[AWH,^X=Z#/N-"SS[C0L\^X*"1@KGXG8*Y^)V"N1&[C[D1NX^Y7\B/N5_( -MC[D9Z9RY&>FYFF2W -MN9IDM[F(=;>YB'6WN9)JJKF2:JJY2WJJN4MZJKE3J+>Y4ZBWN;R9JKF\F:JY -M+\JWN2_*M[D>V[>Y'MNWN0SLM[D,[+>Y^ORWN?K\M[GH#;BYZ`VXN=<>N+G7 -M'KBYP5?%N<%7Q;FS0+BYLT"XN0A\Q;D(?,6YCV*XN8]BN+E0H,6Y4*#%N72R -MQ;ETLL6YE\3%N9?$Q;E(IKBY2*:XN=_HQ;G?Z,6Y)"[FYW@NYN]1K+D^ -MI+FY/J2YN6!QK+E@<:RY&L:YN1K&N;G1D*RYT9"LN8J@K+F*H*RY0["LN4.P -MK+G3";JYTPFZN;3/K+FTSZRY;-^LN6S?K+DE[ZRY)>^LN=[^K+G>_JRYE@ZM -MN98.K;E/'JVY3QZMN0@NK;D(+JVYP#VMN<`]K;EY3:VY>4VMN4('H+E"!Z"Y -MQ16@N<45H+GLRY*Y[,N2N3K9DKDZV9*YP8N%N<&+A;G5\Y*YU?.2N2(!D[DB -M`9.Y0J-PN4*C<+D'N7"Y![EPN2<-5KDG#5:Y<%P[N7!<.[E>;3NY7FT[N6:U -M(+EFM2"YZ<,@N>G#(+FP!`:YL`0&N3^!UK@_@=:X<^^@N'/OH+CV_:"X]OV@ -MN$R[5KA,NU:XI=H3@C -MD-W@8Y7MX&.77J!CEUZ@8YJO0A.:KT -M(3DM`R(Y+0,B.9476#F5%U@YD24].9$E/3E(/E@Y2#Y8.:%16#FA45@YFG%S -M.9IQ8CIB'F(Z$PUI.A,- -M:3JB(6DZHB%I.C$V:3HQ-FDZP$II.L!*:3J?&7.KWTGCJE`I\ZI0*?.HT0GSJ-$)\Z_I.B.OZ3HCH)&*8Z"1BF -M.HPFICJ,)J8Z@*NI.H"KJ3I0NJDZ4+JI.BQ`K3HL0*TZ24^M.DE/K3H-UK`Z -M#=:P.GCEL#IXY;`Z)&VT.B1MM#IK];735.@<'V3H' -M!]DZ7)G5.ER9U3J%+-DZA2S9.D,_V3I#/]DZ5=/<.E73W#K!9-DZP639.FWY -MW#IM^=PZ>0S=.GD,W3J%']TZA1_=.F:UX#IFM>`ZP,C@.L#(X#J)7^0ZB5_D -M.C!SY#HP<^0ZS`+A.LP"X3I^FN0Z?IKD.B6NY#HEKN0ZS,'D.LS!Y#IRU>0Z -MPZ?EGL.K]M[#J_;>PZK0GP.JT)\#H\'O`Z/![P.H2J[#J$JNPZ6D?P.EI' -M\#KI6_`ZZ5OP.GAP\#IX%;X.GA6^#HD^/LZ)/C[.IL-_#J;#?PZ$B/\.A(C_#J)./PZB3C\.KC;_SJX -MV_\Z>&/\.GAC_#KO>/PZ[WC\.L;_^#K&__@ZW:/\.MVC_#I4N?PZ5+G\.LS. -M_#K,SOPZ0^3\.D/D_#JZ^?PZNOG\.C$/_3HQ#_TZJ"3].J@D_3H@.OTZ(#K] -M.CZ^^3H^OODZ#F7].@YE_3J%>OTZA7K].OR/_3K\C_TZ=*7].G2E_3KKNOTZ -MZ[K].F+0_3IBT/TZJ+P`.ZB\`#N*QP`[BL<`.VS2`#MLT@`[3]T`.T_=`#LQ -MZ``[,>@`.Q/S`#L3\P`[I&;^.J1F_CH$O\ZZE\! -M.^I?`3O-:@$[S6H!.Z]U`3NO=0$[D8`!.Y&``3L.6`,[#E@#.Q=C`SL78P,[ -M(&X#.R!N`SLW1@4[-T8%.S*$`SLRA`,[EUP%.Y=0<[4'D'.Z:$ -M!SNFA`<[1,$%.T3!!3M3FP<[4YL'.ZFF!SNII@<[`+('.P"R!SM6O0<[5KT' -M.ZS(!SNLR`<[`]0'.P/4!SL@KPD[(*\).[#J!SNPZ@<[&L8).QK&"3M<`0@[ -M7`$(.Q7="3L5W0D["1@(.PD8"#L/]`D[#_0).[8N"#NV+@@[##H(.PPZ"#MC -M10@[8T4(.P,B"CL#(@H[#UP(.P]<"#MF9P@[9F<(.[QR"#N\<@@[$WX(.Q-^ -M"#M=MP8[7;<&.[^4"#N_E`@[%J`(.Q:@"#OLV`8[[-@&.QSD!CL!3N^:04[OFD%.\=T!3O'=`4[IJL#.Z:K`SO9B@4[V8H%.VO!`SMK -MP0,[3W@".WMX`CNBK``[HJP`.S>W`#LWMP`[S,$`.\S!`#M@S``[ -M8,P`.XS__3J,__TZ:!3^.F@4_CI%*?XZ12G^.MJ.^CK:COHZ::/Z.FFC^CH6 -M"/>DZ -M5M?E.E;7Y3K"-.(ZPC3B.C-'XCHS1^(ZMZ/>.K>CWCK:M=XZVK7>.G81VSIV -M$=LZ3"/;.DPCVSK_?=[, -M.M[^S#K>_LPZ"%7).@A5R3I;9A.O'7H3KQY*$Z\>2A.LXMGCK. -M+9XZ@#J>.H`ZGCIU@IHZ=8*:.MJ.FCK:CIHZC1"3.HT0DSI8'),Z6!R3.B]B -MCSHO8H\ZK&V/.JQMCSJ:LHLZFK*+.LJ]BSK*O8LZT`&(.M`!B#HIU(LZ*=2+ -M.M%/A#K13X0Z=R*(.GCK@JWHZU+]Z.M2_ -M>CKY.7,Z^3ES.KWG>CJ]YWHZK&!S.JQ@)=D.GB79#J -MX54Z=4Y..G5.3CH6ND8Z%KI&.L_)1CK/R48ZH#,_.J`S/SI`Z48Z0.E&.MM1 -M/SK;43\Z^6`_.OE@/SK$QS$CH6 -M>0HZ%GD*.OB#"CKX@PHZ0-P".D#<`CJD9O8YI&;V.5X2YSE>$N- -M]CFF-NN3D:7KDYY?BI.>7XJ3DS!JHY,P:J.5V=FCE=G9HY -M=:F:.76IFCG_/(LY_SR+.>%'BSGA1XLYE*]W.92O=SGNPGQ8.6WL6#D<_AHY'/X:.3\,.CD_##HY3!8;.4P6&SED(ALY9"(;.6!* -M^#A@2O@XE#H;.90Z&SD3&SGS:ALY\VH; -M.0MW&SD+=QLY79TZ.5V=.CD[CQLY.X\;.5.;&SE3FQLYW@OY.-X+^3B#LQLY -M@[,;.9$R^3B1,ODXLLL;.;++&SG*UQLYRM<;.>+C&SGBXQLY]G_Y./9_^3@2 -M_!LY$OP;.:FF^3BIIODX`[KY.`.Z^3@%6KLX!5J[.(AHNSB(:+LX"W>[.`MW -MNSB.A;LXCH6[.,(:>CC"&GHXE**[.)2BNSAT07HX=$%Z.,Y4>CC.5'HX)VAZ -M."=H>CB!>WHX@7MZ.-J.>CC:CGHX-*)Z.#2B>CB-M?HWC;7Z-^;(>CCFR'HX -M0-SZ-T#<^C>9[_HWF>_Z-P`````````````````````````````````````` -M``````````````````````````MW^[<+=_NW``````````"^G?NWOIW[MQ>Q -M^[<7L?NW<<3[MW'$^[<```````````````````````````````#6$?RWUA'\ -MMP``````````B3C\MXDX_+?C2_RWXTO\MP``````````EG+\MY9R_+?OA?RW -M[X7\MP``````````HJS\MZ*L_+?[O_RW^[_\MU73_+=5T_RWKN;\MZ[F_+<` -M`````````&$-_;=A#?VW`````````````````````&U'_;=M1_VW```````` -M```@;OVW(&[]MP``````````TY3]M].4_;&N_VWW\[] -MM]_._;2]?VWZPA^N.L(?KA%'/ZW11S^MYXO?KB>+WZX -M]T)^N/="?KA15GZX459^N*II?KBJ:7ZX!'U^N`1]?KA=D/ZW79#^M[>C?KBW -MHWZX$+?^MQ"W_K=IRGZX:"/O^W@C[_M]M1_[?;4?^W-67_MS5E_[<````````` -M```````````````````````````````````````````````````````````` -M`````````#@````X````.````#@````X````.````#@````X``"`.```@#@` -M`(`X``"`.```@#@``(`X``"`.```@#@``,`X``#`.```P#@``,`X````.0`` -M`#D``,`X``#`.````#D````Y````.0```#D``"`Y```@.0```#D````Y```@ -M.0``(#D``"`Y```@.0``0#D``$`Y```@.0``(#D``"`Y```@.0``(#D``"`Y -M``!`.0``0#D``"`Y```@.0``(#D``"`Y``!`.0``0#D``$`Y``!`.0``0#D` -M`$`Y``!@.0``8#D``(`Y``"`.0``8#D``&`Y``!@.0``8#D``&`Y``!@.0`` -M@#D``(`Y``"`.0``@#D``(`Y``"`.0``@#D``(`Y``"`.0``@#D``(`Y``"` -M.0``@#D``(`Y``!@.0``8#D``(`Y``"`.0``@#D``(`Y``!@.0``8#D``(`Y -M``"`.0``@#D``(`Y``!@.0``8#D``(`Y``"`.0``D#D``)`Y``"0.0``D#D` -M`(`Y``"`.0``D#D``)`Y``"`.0``@#D``)`Y``"0.0``@#D``(`Y``"0.0`` -MD#D``)`Y``"0.0``D#D``)`Y``"0.0``D#D``*`Y``"@.0``D#D``)`Y``"@ -M.0``H#D``*`Y``"@.0``H#D``*`Y``"0.0``D#D``)`Y``"0.0``H#D``*`Y -M``"@.0``H#D``*`Y``"@.0``H#D``*`Y``"0.0``D#D``)`Y``"0.0``@#D` -M`(`Y``"0.0``D#D``)`Y``"0.0``D#D``)`Y``"`.0``@#D``(`Y``"`.0`` -M8#D``&`Y``!@.0``8#D``$`Y``!`.0``(#D``"`Y```@.0``(#D``"`Y```@ -M.0``P#@``,`X````.0```#D``,`X``#`.```P#@``,`X``"`.```@#@``(`X -M``"`.````#@````X`````````````````````````+@```"X````N````+@` -M`("X``"`N```P+@``,"X``#`N```P+@```"Y````N0``(+D``""Y```@N0`` -M(+D``$"Y``!`N0``8+D``&"Y``"0N0``D+D``)"Y``"0N0``H+D``*"Y``"@ -MN0``H+D``,"Y``#`N0``P+D``,"Y``#@N0``X+D``."Y``#@N0```+H```"Z -M````N@```+H```BZ```(N@``$+H``!"Z```@N@``(+H``""Z```@N@``*+H` -M`"BZ```PN@``,+H``#BZ```XN@``2+H``$BZ``!(N@``2+H``%"Z``!0N@`` -M6+H``%BZ``!@N@``8+H``&BZ``!HN@``>+H``'BZ``!XN@``>+H``'BZ``!X -MN@``A+H``(2Z``"(N@``B+H``(BZ``"(N@``C+H``(RZ``"0N@``D+H``)2Z -M``"4N@``F+H``)BZ``"8N@``F+H``)RZ``"]`$"YO0!`N;T`\(B]`/"(O0"` -MR+P`@,B\`"@%/@`H!3X`4-<]`%#7/0"`DKP`@)*\`&"DO0!@I+T`T+:]`-"V -MO0`PC3T`,(T]`$#W/0!`]ST`8#*]`&`RO0#0\+T`T/"]``""O```@KP``*$\ -M``"A/```UCT``-8]`$!@/0!`8#T`4"&^`%`AO@`@\KT`(/*]``"]/```O3P` -M8(X]`&"./0`P@CT`,((]`&!4O0!@5+T`D+2]`)"TO0``B3T``(D]`""%/0`@ -MA3T``(*\``""O```$#L``!`[`$!^O0!`?KT`@*P\`("L/`!P\#T`]`.!7O0"`,3P`@#$\`"`IO0`@*;T``!P[```<.P"@D3T`H)$] -M`.!8O0#@6+T`P%&]`,!1O0!`]KP`0/:\`$!YO0!`>;T`8!P]`&`\```WO``@(;T`("&]`+"\/0"PO#T`(`\^`"`//@#` -MHKT`P**]`%@8O@!8&+X`0!T]`$`=/0`@@CT`(((]`$">/0!`GCT`(%4]`"!5 -M/0!`&KX`0!J^`&!6O0!@5KT`@*,]`("C/0``;#P``&P\`.`BO0#@(KT`L,Z] -M`+#.O0!`/+T`0#R]`("^/0"`OCT`0/L\`$#[/```EKT``):]```MO```+;P` -M`*B\``"HO`#`J+P`P*B\`.`!/@#@`3X`@.8\`(#F/`"`MKP`@+:\```3/``` -M$SP`($"]`"!`O0!`'CT`0!X]`""*/0`@BCT`P"2]`,`DO0``)+T``"2]``!P -MO```<+P``&"\``!@O`#0D#T`T)`]```TO```-+P`$#Z^`!`^O@`@(;T`("&] -M`*`V/0"@-CT`,(`]`#"`/0``UST``-<]`%"LO0!0K+T`X-&]`.#1O0#@E3T` -MX)4]`+"%/0"PA3T``!*\```2O```D+D``)"Y``!;O0``6[T``%T\``!=/`#@ -M\CT`X/(]`*`*/0"@"CT`0)>\`$"7O`!@J;T`8*F]`/##O0#PP[T`T(P]`-", -M/0``VSP``-L\`,!HO0#`:+T``'2[``!TNP!@\`$#7O```Y;P``.6\`%"=O0!0G;T``&$]``!A/0#(*CX`R"H^`(!6O0"` -M5KT`^"R^`/@LO@`@#+T`(`R]``"7/```ESP`T*,]`-"C/0#@@#T`X(`]`(#F -MO0"`YKT`P$B]`,!(O0!`] -M`)"$O0"0A+T`(!P]`"`/0`@WCT``.>\``#GO`"PH;T`L*&]`(`[/0"`.ST` -M@&T]`(!M/0``@[P``(.\`(#+O`"`R[P`4)J]`%":O0"`O;P`@+V\`*`]/0"@ -M/3T`P(0\`,"$/`"@/CT`H#X]`.""O0#@@KT`"`2^``@$O@`@(CT`("(]`."# -M/0#@@ST``!6\```5O```;3P``&T\``#FO```YKP`@!*]`(`2O0`@[CT`(.X] -M`&"8/0!@F#T`($J]`"!*O0!`#[T`0`^]`*`WO0"@-[T`H#H]`*`Z/0"`R3T` -M@,D]`.`0O0#@$+T`H*B]`*"HO0``.;T``#F]`("+O`"`B[P``%@]``!8/0`` -M=3P``'4\`%"6O0!0EKT`@#6\`(`UO```1;P``$6\`$",O`!`C+P`<*,]`'"C -M/0"`0+P`@$"\``"@O0``H+T`@%D]`(!9/0!@BCT`8(H]`*`5/0"@%3T`8#P] -M`&`\/0"`5;T`@%6]`&!FO0!@9KT`8!T]`&`=/0``[3P``.T\`(`-/0"`#3T` -M@":]`(`FO0!0^[T`4/N]`"`'/0`@!ST`8)X]`&">/0`0E+T`$)2]``"GO0`` -MI[T`P`>]`,`'O0``0+H``$"Z`-#0/0#0T#T`H%0]`*!4/0`@;;T`(&V]`$#- -MO`!`S;P`@`6\`(`%O`#PC#T`\(P]`!"F/0`0ICT``$>]``!'O0``'KP``!Z\ -M```X/0``.#T``)J\``":O```?SP``'\\`,`(/0#`"#T`D+N]`)"[O0``=KT` -M`':]`(`N/`"`+CP`P)N\`,";O``@;ST`(&\]``!DO```9+P`D/^]`)#_O0!` -MRKP`0,J\`*!C/0"@8ST`$(\]`!"//0"`PCP`@,(\`"!.O0`@3KT``)N[``"; -MNP!`PCT`0,(]`,`;/0#`&ST`0(F\`$")O`!`W;P`0-V\`-"0O0#0D+T`X+8] -M`."V/0!@QST`8,<]`"#+O0`@R[T`L):]`+"6O0``F;L``)F[`(!HO`"`:+P` -M`&8[``!F.P#`J;P`P*F\`!"!O0`0@;T``"H[```J.P``ESP``)<\`,"7/`#` -MESP`@(,]`("#/0"`7+T`@%R]`.!'O0#@1[T`4($]`%"!/0#`"CT`P`H]`("" -M/0"`@CT`P&P]`,!L/0"0F+T`D)B]`!":O0`0FKT`P",]`,`C/0"@63T`H%D] -M`("W/`"`MSP`8*V]`&"MO0`8`;X`&`&^```[/0``.ST`H)0]`*"4/0#`I+P` -MP*2\```[O0``.[T``,^]``#/O0"`0;P`@$&\`/`'/@#P!SX`H#H]`*`Z/0`@ -M+KT`("Z]`(`2O`"`$KP`P#2]`,`TO0`@"ST`(`L]`(#=/0"`W3T`@%&\`(!1 -MO`#`^;P`P/F\`("\/`"`O#P``-6[``#5NP``E+L``)2[`(!#O`"`0[P`P$*] -M`,!"O0"`[;P`@.V\`*`UO0"@-;T`@!,\`(`3/`#0MCT`T+8]`,!#O0#`0[T` -M(*>]`""GO0`@(CT`("(]``"^NP``OKL``.R[``#LNP"`23T`@$D]``!]O``` -M?;P``*N[``"KNP#PNCT`\+H]`(#O/`"`[SP`X!B]`.`8O0`@;;T`(&V]`*`9 -MO0"@&;T`$,H]`!#*/0#@5ST`X%<]``"IO0``J;T`X!V]`.`=O0`@"+T`(`B] -M`,"'O`#`A[P``%H]``!:/0``L;L``+&[`+"EO0"PI;T`P)6\`,"5O```NSP` -M`+L\`(!C/0"`8ST`@'T]`(!]/0!PF;T`<)F]`.!LO0#@;+T`P.T\`,#M/``@ -M'#T`(!P]`!#5/0`0U3T`P.`\`,#@/`!0V[T`4-N]``!(O0``2+T`0.<\`$#G -M/`#`#CT`P`X]`&`D/0!@)#T`P&B]`,!HO0"PU;T`L-6]`%"2/0!0DCT`@&L] -M`(!K/0"@1+T`H$2]``#HO```Z+P`X%F]`.!9O0#`O[P`P+^\`,!`/0#`0#T` -MP#@]`,`X/0"`KCP`@*X\`(`#O0"``[T`(#&]`"`QO0#`03T`P$$]``!'/0`` -M1ST`0"6]`$`EO0"@"CT`H`H]```,/```##P`@(&]`("!O0``:ST``&L]`(#? -M/`"`WSP`T+2]`-"TO0!`);T`0"6]`(#@/`"`X#P``$X]``!./0"`"3T`@`D] -M`!"@O0`0H+T`H)B]`*"8O0#`#ST`P`\]``"B/```HCP``%T\``!=/```3;P` -M`$V\``"1O0``D;T`8&(]`&!B/0"@W#T`H-P]```;O```&[P`0%^]`$!?O0#@ -M0+T`X$"]``!YO```>;P`4+T]`%"]/0"@,3T`H#$]```CO0``([T``$J[``!* -MNP#`$KT`P!*]`(#CO`"`X[P```,]```#/0#`E[P`P)>\`&`1O0!@$;T`@%6\ -M`(!5O`!`!KT`0`:]`,!-/0#`33T`,*4]`#"E/0``F;T``)F]`'"$O0!PA+T` -M`#T\```]/`"`-3P`@#4\``"Z/0``NCT``"L]```K/0!`K;T`0*V]`(`UO0"` -M-;T`<(@]`'"(/0`PD#T`,)`]``#"NP``PKL`H,:]`*#&O0``MKP``+:\`!#( -M/0`0R#T``"0[```D.P``4+T``%"]`,`PO0#`,+T``+F]``"YO0#`^CP`P/H\ -M`)"R/0"0LCT``+*\``"RO`"`"+T`@`B]`(`0/`"`$#P``'6\``!UO`!`G3P` -M0)T\`,#A/`#`X3P``.>\``#GO```K#P``*P\`(!4/`"`5#P```(\```"/`!P -MG3T`<)T]`(`8/`"`&#P``&^]``!OO0#`8[T`P&.]`$`8O0!`&+T`4(D]`%") -M/0`P@#T`,(`]`/"RO0#PLKT`(+2]`""TO0#`USP`P-<\``!E/```93P`0,8\ -M`$#&/`!`@;P`0(&\`+"7O0"PE[T`X!(]`.`2/0#0HST`T*,]`(##/`"`PSP` -M``&\```!O`#@<+T`X'"]```//```#SP`8,,]`&##/0``TSL``-,[`*`6O0"@ -M%KT`0!\]`$`?/0"`\KP`@/*\``!CO0``8[T`P*H\`,"J/`"`R[P`@,N\`$`9 -MO0!`&;T``!*\```2O```0CL``$([`.`2/0#@$CT```"\````O```9;T``&6] -M`(`7O`"`%[P`@#J\`(`ZO`!`NCP`0+H\`$#8/0!`V#T``#D\```Y/`"`:KT` -M@&J]`*`]/0"@/3T`0!8]`$`6/0``#SP```\\`(`XO`"`.+P`T*>]`-"GO0"` -MM[P`@+>\`""G/0`@IST``*D[``"I.P!`([T`0".]`*`IO0"@*;T`@(^]`("/ -MO0``FSL``)L[`(!*/0"`2CT``#P\```\/```3+P``$R\``!4O0``5+T``"R[ -M```LNP!0LST`4+,]``#(/```R#P`0.N\`$#KO`!`PCP`0,(\`(`TO`"`-+P` -M@*P\`("L/``0V#T`$-@]`,"3O`#`D[P`,,6]`##%O0`@:KT`(&J]``#_.P`` -M_SL`H'<]`*!W/0``B+H``(BZ`'"EO0!PI;T`P)^\`,"?O`"`+CP`@"X\`,"] -MO`#`O;P`P`(]`,`"/0``ZKL``.J[`*"#O0"@@[T`('4]`"!U/0#0DCT`T)(] -M`("$/`"`A#P``$`[``!`.P``6[P``%N\```DNP``)+L``!4\```5/```@#D` -M`(`Y`(#8/`"`V#P`@'X\`(!^/`"0BKT`D(J]`(!!O`"`0;P`P.,\`,#C/`"@ -M*KT`H"J]`,"OO`#`K[P`(!J]`"`:O0"`^;P`@/F\`*"=/0"@G3T`@/$\`(#Q -M/`!@D;T`8)&]`(#RO`"`\KP`H`H]`*`*/0#`7ST`P%\]`*"@/0"@H#T`@+^\ -M`("_O`#@@[T`X(.]`"`1/0`@$3T`0'\]`$!_/0``4#P``%`\`%"4O0!0E+T` -M8+*]`&"RO0``/ST``#\]``"-/0``C3T`@'"\`(!PO`"`2[P`@$N\```KO0`` -M*[T`H+6]`*"UO0``E3L``)4[`&!8/0!@6#T`@%0\`(!4/`"`8SP`@&,\``#+ -MO```R[P``&J\``!JO`"@:CT`H&H]```X/0``.#T`@!4\`(`5/`!@$;T`8!&] -M`*"GO0"@I[T``#\]```_/0!H"3X`:`D^``!HNP``:+L`<,:]`'#&O0!@>+T` -M8'B]``#)NP``R;L`L(0]`+"$/0"`ICP`@*8\`#"*O0`PBKT`0!R]`$`]`,`P/0#`,#T`H&0]`*!D/0#@'3T`X!T] -M`(`4O0"`%+T`P*2]`,"DO0``43P``%$\`.!)/0#@23T`0+P\`$"\/```0CL` -M`$([``!WO0``=[T``)V]``"=O0"`USP`@-<\`(`Q/0"`,3T``*P[``"L.P"` -M:#P`@&@\``#^O```_KP`@)6\`("5O`"`_CP`@/X\``"P.0``L#D``("X``"` -MN```IKP``*:\`(`+O0"`"[T`8$(]`&!"/0``N#T``+@]``"$N@``A+H``%6] -M``!5O0!`1;T`0$6]``"TN@``M+H`@&D]`(!I/0``8;P``&&\`&`VO0!@-KT` -M@-0\`(#4/```R+L``,B[```_O```/[P`0-L\`$#;/`#@4;T`X%&]`!"1O0`0 -MD;T`H!\]`*`?/0``8ST``&,]`(`%/`"`!3P``&J\``!JO`"`T;P`@-&\`(`K -M/`"`*SP`0(4\`$"%/`#`OSP`P+\\`&`\/0!@/#T`P!&]`,`1O0!PA+T`<(2] -M`(!H/0"`:#T`P%$]`,!1/0``*[T``"N]`$`_O0!`/[T``"F]```IO0``1CP` -M`$8\`."3/0#@DST``(^[``"/NP``:[T``&N]`(`_O0"`/[T`@`6]`(`%O0!@ -M#ST`8`\]`,!1/0#`43T`0.*\`$#BO```I+L``*2[```@/0``(#T``&4\``!E -M/`!`QCP`0,8\``#KNP``Z[L`0/R\`$#\O`"`-CP`@#8\``"`N0``@+D``!D\ -M```9/`#@*3T`X"D]``#4O```U+P`X(*]`.""O0``^;L``/F[``"R.P``LCL` -M`#*[```RNP``>;P``'F\`,!4O0#`5+T``$<\``!'/`!@)#T`8"0]``"7NP`` -ME[L``)`Y``"0.0#@$KT`X!*]``#*O```RKP`D.`]`)#@/0#@P#T`X,`]`&!L -MO0!@;+T`X(B]`."(O0``#CP```X\```)/0``"3T`0!P]`$`/0#`'CT`@#0\`(`T/`"@+;T`H"V]`*!* -MO0"@2KT`("^]`"`OO0``,KP``#*\``"6/```ECP``&$\``!A/```;CP``&X\ -M`("ZO`"`NKP`0/*\`$#RO`#`,#T`P#`]`(`\/`"`/#P`P!>]`,`7O0"`B#P` -M@(@\`&`V/0!@-CT`0/4\`$#U/```TSP``-,\```7O```%[P`H!.]`*`3O0`` -M+KP``"Z\``">.P``GCL`0.X\`$#N/```;#P``&P\`*!_O0"@?[T`0$N]`$!+ -MO0``GCL``)X[`$"E/`!`I3P```8\```&/```%[T``!>]`"``O0`@`+T``!8] -M```6/0!`/3T`0#T]`(!K/`"`:SP``'Z[``!^NP!`;;T`0&V]``!IO```:;P` -MP,H]`,#*/0``3CT``$X]`$`=O0!`';T``("\``"`O```9CL``&8[``#PNP`` -M\+L``%Z\``!>O```,;P``#&\``#@.@``X#H``(V\``"-O`!`S;P`0,V\`,`0 -M/0#`$#T`P)8\`,"6/`"`)[T`@">]`,#>O`#`WKP`@(Z\`(".O```TSL``-,[ -M`$"#/0!`@ST`0!8]`$`6/0#`"KT`P`J]`(#1O`"`T;P`0)4\`$"5/```@3T` -M`($]`,"U/`#`M3P`$):]`!"6O0#`N[P`P+N\`!"#/0`0@ST``/8\``#V/``` -M^;P``/F\`,!IO0#`:;T`(`:]`"`&O0"`"#T`@`@]`"`I/0`@*3T`P)&\`,"1 -MO```8+P``&"\`(!UO`"`=;P`@#F\`(`YO`!`G#P`0)P\``#KNP``Z[L`@'2\ -M`(!TO`"`J#P`@*@\`*`"/0"@`CT``,T\``#-/```,#P``#`\```D.P``)#L` -M`.0Z``#D.@``!KT```:]```_O0``/[T`(%T]`"!=/0#`?#T`P'P]`&`TO0!@ -M-+T`P`&]`,`!O0``$CP``!(\``!2.P``4CL``"`Y```@.0#`A+P`P(2\`"`7 -MO0`@%[T``,"Z``#`N@#`/3T`P#T]```P/0``,#T`0(F\`$")O``0B;T`$(F] -M``#4.@``U#H`0)0]`$"4/0``<#P``'`\`,"5O`#`E;P``$:[``!&NP!`BCP` -M0(H\`,#^/`#`_CP``""Y```@N0``Y+P``.2\``#H.@``Z#H``-,[``#3.P`` -M#+L```R[`,"Y/`#`N3P``$J\``!*O`#@3KT`X$Z]``!LO```;+P``/"Y``#P -MN0``HSL``*,[`,#K/`#`ZSP``%L\``!;/```_+H``/RZ`(!>O`"`7KP``"BZ -M```HN@"@33T`H$T]`$#1/`!`T3P`X'2]`.!TO0"`.[P`@#N\`'"D/0!PI#T` -M(&4]`"!E/0#`@KP`P(*\`,!`O0#`0+T`P.V\`,#MO```VCL``-H[`(!D/`"` -M9#P`P)(\`,"2/`#`BKP`P(J\`&".O0!@CKT``/&[``#QNP"@@3T`H($]``#7 -MNP``U[L`(!J]`"`:O0!`C#P`0(P\`(!0/`"`4#P``.\[``#O.P`@+CT`("X] -M`.`G/0#@)ST```Z\```.O`"@$;T`H!&]```Z.P``.CL`0(0]`$"$/0#`^SP` -MP/L\`,#3O`#`T[P`P(&\`,"!O```7+P``%R\`("8O`"`F+P``.N[``#KNP#` -MI[P`P*>\`(`NO`"`+KP``,`Y``#`.0``]CL``/8[``"E/```I3P``,D[``#) -M.P#@'KT`X!Z]``#!.P``P3L`H%`]`*!0/0"`"CP`@`H\`(!!/`"`03P``"D] -M```I/0"``3T`@`$]``"X.P``N#L``*:[``"FNP``2CL``$H[`,"+O`#`B[P` -M@#F]`(`YO0``C#L``(P[`*!W/0"@=ST`@"B\`(`HO`!0G;T`4)V]`$#!O`!` -MP;P`@`(\`(`"/```H3P``*$\``"7/```ESP``)R[``"\`(!C/`"`8SP`@'P\`(!\/```[;L``.V[`,#XO`#`^+P``*>\``"GO``` -M9#L``&0[``#,.@``S#H`@)<\`("7/`"`BCP`@(H\`(`8O`"`&+P``(0\``"$ -M/```<3T``'$]`(!K/0"`:ST``!X\```>/`!`7;T`0%V]```EO```);P`H(,] -M`*"#/0"`@#P`@(`\`.!'O0#@1[T``$J[``!*NP"`0#P`@$`\`$"+O`!`B[P` -MP/.\`,#SO`#`][P`P/>\``!KO```:[P`0)T\`$"=/`#`[CP`P.X\``#S.P`` -M\SL`H!*]`*`2O0"`)KP`@":\`&"?/0!@GST``&X]``!N/0"`\+P`@/"\``"N -MNP``KKL`($@]`"!(/0``U3P``-4\``#!NP``P;L`@%>\`(!7O```P+D``,"Y -M`,"7O`#`E[P`H`*]`*`"O0"`73P`@%T\```W/0``-ST``'N\``![O`#`*KT` -MP"J]`"`1O0`@$;T``/N\``#[O`"`Y#P`@.0\`""`/0`@@#T``!"Z```0N@"` -M\;P`@/&\``#?.P``WSL`,(P]`#",/0#@BST`X(L]`,"GO`#`I[P`$(F]`!") -MO0``73P``%T\`$!\/0!`?#T``),\``"3/`"`/KP`@#Z\`$"\O`!`O+P`@(N\ -M`("+O`"`B[P`@(N\`(`)O`"`";P`@)`\`("0/`"`8KP`@&*\`.`3O0#@$[T` -MP-<\`,#7/```VCP``-H\`.`#O0#@`[T``(,[``"#.P!`83T`0&$]``"4N@`` -ME+H``+B[``"XNP#`$#T`P!`]`&`4/0!@%#T``&&\``!AO`#`^KP`P/J\`(#8 -M/`"`V#P`X"D]`.`I/0``P[P``,.\`,"XO`#`N+P`0(@\`$"(/```Z;L``.F[ -M`("UO`"`M;P``-0Z``#4.@``QKP``,:\`*`'O0"@![T``"T\```M/`"@:CT` -MH&H]`,`P/0#`,#T`@!V]`(`=O0"`$;T`@!&]`.`9/0#@&3T`@`D]`(`)/0"` -M+;P`@"V\``!I/```:3P``%8\``!6/```3+L``$R[`(!0/`"`4#P``+(\``"R -M/```X#L``.`[`,#,O`#`S+P``#*]```RO0!`C3P`0(T\`,#\/`#`_#P`0*"\ -M`$"@O`"`FKP`@)J\`(!"/`"`0CP``(&\``"!O`"`/[P`@#^\`$#I/`!`Z3P` -M@/H\`(#Z/`"`-+P`@#2\`("%O`"`A;P`(!H]`"`:/0!`MSP`0+<\``!+O0`` -M2[T``-2[``#4NP"`BCT`@(H]`(`%/`"`!3P`@-&\`(#1O`#`WCP`P-X\`(!- -M/`"`33P`@`R]`(`,O0"`<;P`@'&\``!L/```;#P``+X[``"^.P"`Y;P`@.6\ -M``"0.0``D#D``!L]```;/0"`"CP`@`H\``#6O```UKP``!R[```#T`0/\\`$#_/`#`*KT`P"J]`("EO`"`I;P`P,@\`,#( -M/```NCL``+H[```,NP``#+L``#P\```\/`"`$[P`@!.\`(!ZO`"`>KP``$*\ -M``!"O`"`#CP`@`X\```T/```-#P``)"\``"0O```W+L``-R[```M/```+3P` -M@.:\`(#FO```"[P```N\`%"+/0!0BST`(%4]`"!5/0"`$+T`@!"]`$#!O`!` -MP;P`@!(]`(`2/0``^3P``/D\`&`(O0!@"+T``*>\``"GO```+CT``"X]``!1 -M/```43P`0*F\`$"IO`"`%#P`@!0\``"HNP``J+L`(`Z]`"`.O0``"#H```@Z -M``#1.P``T3L`H#.]`*`SO0``KKP``*Z\`$!-/0!`33T`(&0]`"!D/0``7[P` -M`%^\`$!OO0!`;[T`@$`\`(!`/`#@=3T`X'4]`$#//`!`SSP``)*[``"2NP`` -MF#L``)@[``#,.@``S#H``%0[``!4.P``'SP``!\\``!&/```1CP``+Z[``"^ -MNP"``;T`@`&]`("*O`"`BKP``%8\``!6/`#`FKP`P)J\`$"EO`!`I;P``"L\ -M```K/`!`GKP`0)Z\`$`LO0!`++T`8`,]`&`#/0#@M#T`X+0]`(#[/`"`^SP` -M8%N]`&!;O0#`&+T`P!B]``!6/0``5CT`0#8]`$`V/0"`*[P`@"N\``#L.P`` -M[#L`@+X\`("^/```M[L``+>[```E/```)3P`@(`\`("`/`"@+[T`H"^]`)"& -MO0"0AKT``,*[``#"NP"`O3P`@+T\`,"EO`#`I;P`P/R\`,#\O`"`.ST`@#L] -M`(`+/0"`"ST`P">]`,`GO0"`?;P`@'V\`&!]``"L.@``K#H`8`(]`&`"/0#`FKP`P)J\``"U -MNP``M;L`@*\\`("O/`!`WKP`0-Z\```!O0```;T`0"\]`$`O/0!@8ST`8&,] -M`(`%O`"`!;P``#Z\```^O``@/CT`(#X]`,`@/0#`(#T`0(.\`$"#O```B#H` -M`(@Z`$!"/0!`0CT`@!B\`(`8O`!@,;T`8#&]``!PN@``<+H``+PZ``"\.@!` -M(;T`0"&]`(`+O0"`"[T``),[``"3.P``H#P``*`\``#(.@``R#H``%BZ``!8 -MN@!`E3P`0)4\``#LN@``[+H`0.J\`$#JO`#`ECP`P)8\``!//0``3ST``/8[ -M``#V.P``B+L``(B[`$!6/0!`5CT`X',]`.!S/0``R+P``,B\`$!UO0!`=;T` -M`!X[```>.P``SSP``,\\`$#QO`!`\;P`@)*\`("2O```3CP``$X\`,"=O`#` -MG;P``)>\``"7O``@`CT`(`(]``"%.P``A3L`H$Z]`*!.O0"`([P`@".\`(!Q -M/0"`<3T`@/L\`(#[/`#`Y;P`P.6\```:/```&CP`0(L]`$"+/0``&CP``!H\ -M````O0```+T``-L\``#;/```(3T``"$]``#"O```PKP`0#&]`$`QO0``5+L` -M`%2[``#).P``R3L``(N[``"+NP``Q#H``,0Z`(`@/`"`(#P`@&.\`(!CO`"` -M*[P`@"N\`$#V/`!`]CP``/F[``#YNP#@?;T`X'V]``"(NP``B+L`4)`]`%"0 -M/0#@5CT`X%8]`$"#O`!`@[P`@!N\`(`;O`#@-3T`X#4]`,`:/0#`&CT`0!F] -M`$`9O0!`+KT`0"Z]`(!\/`"`?#P`@'L\`(![/```X+D``."Y`(![/`"`>SP` -M`'P[``!\.P!`^[P`0/N\``"3O```D[P``-`\``#0/```0#H``$`Z```9O0`` -M&;T`@``\`(``/`"`,CT`@#(]`$""O`!`@KP`(`"]`"``O0`@93T`(&4]`.!- -M/0#@33T`8`F]`&`)O0#`VKP`P-J\`,`N/0#`+CT`P`0]`,`$/0!`P+P`0,"\ -M`,#1O`#`T;P``$P[``!,.P``L#D``+`Y``!(/```2#P`0"\]`$`O/0``+3P` -M`"T\``!HO0``:+T`@,:\`(#&O`"`"#T`@`@]``"L.@``K#H`8#*]`&`RO0`` -MI+H``*2Z`$!;/0!`6ST`@,X\`(#./`!`I;P`0*6\``"<.P``G#L``,<\``#' -M/```D+P``)"\`(#4O`"`U+P``(<\``"'/`#`MSP`P+<\```<.P``'#L`P)@\ -M`,"8/`"`[3P`@.T\``"4NP``E+L`X!6]`.`5O0``D#D``)`Y`,`!/0#``3T` -M`(.\``"#O```+KT``"Z]`$"B/`!`HCP``*T\``"M/`!`%+T`0!2]``#)NP`` -MR;L`P'D]`,!Y/0``B3P``(D\`,!IO0#`:;T``'^\``!_O`!@,3T`8#$]``#( -M.P``R#L`P*J\`,"JO`#`RSP`P,L\`("T/`"`M#P`0*&\`$"AO`"@!#T`H`0] -M`#"./0`PCCT``).\``"3O`!0G;T`4)V]`,"SO`#`L[P`X`(]`.`"/0``#CL` -M``X[`(`.O`"`#KP`@)8\`("6/```<#L``'`[`*`CO0"@([T``-F[``#9NP"` -M-3T`@#4]`(`>O`"`'KP`(&Z]`"!NO0``GCL``)X[`.!U/0#@=3T`0.X\`$#N -M/`"`'[P`@!^\`$"Q/`!`L3P`P,\\`,#//`!`P+P`0,"\`,"GO`#`I[P`X`P] -M`.`,/0!`OCP`0+X\`(`&O0"`!KT`@'2\`(!TO`"`'SP`@!\\`(!NO`"`;KP` -M@**\`("BO```?SP``'\\``!@/```8#P`@#6]`(`UO0#@&;T`X!F]```B/0`` -M(CT`@-P\`(#/0"`K#P`@*P\`(#O -MO`"`[[P``-T\``#=/`!`3CT`0$X]`$"OO`!`K[P`0%:]`$!6O0``T#D``-`Y -M``!S/```\`/">O0#PGKT` -M`)N[``";NP#@>ST`X'L]```4.P``%#L```6]```%O0!`[CP`0.X\`("#/0"` -M@ST``)(\``"2/`"`@;P`@(&\```]/```/3P``"D\```I/`!`FKP`0)J\``"] -M.P``O3L`P`D]`,`)/0``3;P``$V\`$#$O`!`Q+P`(`$]`"`!/0``R+H``,BZ -M`!"SO0`0L[T`0%2]`$!4O0`@#ST`(`\]`$"M/`!`K3P`8#>]`&`WO0```+@` -M``"X`&!Z/0!@>CT`P)`\`,"0/`!`O[P`0+^\`,"D/`#`I#P`P!0]`,`4/0`` -M2+L``$B[``#PN@``\+H`P)@\`,"8/```[3L``.T[``"`.0``@#D``/`[``#P -M.P``B3L``(D[`(`*O0"`"KT`H!R]`*`] -M``!7O0"PA;T`L(6]`("'/`"`ASP`("X]`"`N/0``8+L``&"[```!O````;P` -MP"D]`,`I/0!@#3T`8`T]`("6O`"`EKP``""Z```@N@#@"3T`X`D]`(`!O`"` -M`;P``)&\``"1O`#`$#T`P!`]`&`$/0!@!#T`0/^\`$#_O`#`G+P`P)R\``"D -M/```I#P`@*2\`("DO`#PBKT`\(J]``"EO```I;P`H!(]`*`2/0``!;P```6\ -M`$#]O`!`_;P`@`X]`(`./0!`$#T`0!`]`(`/O0"`#[T``%>\``!7O`"@53T` -MH%4]`(#`/`"`P#P``,R\``#,O```*CP``"H\`&`1/0!@$3T``!F\```9O`"` -MG+P`@)R\`*`?/0"@'ST`@+(\`("R/`"@6;T`H%F]`*`BO0"@(KT`0*X\`$"N -M/```!KL```:[`$`OO0!`+[T``*^\``"OO`!`BCP`0(H\``!4/```5#P``&0[ -M``!D.P"`\3P`@/$\`,#9/`#`V3P`(!2]`"`4O0!`&;T`0!F]`,`//0#`#ST` -MX"$]`.`A/0!`DKP`0)*\``!X.@``>#H``$@]``!(/0"`2SP`@$L\`"`?O0`@ -M'[T``!.\```3O`"`ICP`@*8\`.`:O0#@&KT``#B]```XO0!`VCP`0-H\`("] -M/`"`O3P`@`^]`(`/O0``&#H``!@Z`&`S/0!@,ST`0)R\`$"O0``GKT`@$2\ -M`(!$O`"@>3T`H'D]`,`$/0#`!#T``#>\```WO```(+D``""Y``"=/```G3P` -M``"[````NP``?+P``'R\`(`E/`"`)3P```P[```,.P!@`+T`8`"]```7/``` -M%SP`8!<]`&`7/0``8[P``&.\`$#LO`!`[+P``.P\``#L/`"`@SP`@(,\`"!V -MO0`@=KT`H!R]`*`\`(`7O`!`Z[P` -M0.N\`,`Q/0#`,3T`0`@]`$`(/0`@.[T`(#N]`(`FO0"`)KT``%D\``!9/``` -MT[L``-.[`$`TO0!`-+T`@#V\`(`]O`!``ST`0`,]``#F.P``YCL`P,N\`,#+ -MO```#3P```T\`$"?/`!`GSP`0-B\`$#8O```$;P``!&\`"!`/0`@0#T`@"0] -M`(`D/0``QKL``,:[``"U.P``M3L`H`P]`*`,/0``O#L``+P[`,#?O`#`W[P` -M`+@[``"X.P``NCP``+H\`,`(O0#`"+T``$&]``!!O0``A#P``(0\`(!O/`"` -M;SP``""]```@O0"`U;P`@-6\`,#=/`#`W3P`@!`\`(`0/```_+P``/R\``"# -M/```@SP`8%T]`&!=/0``0[P``$.\`,`BO0#`(KT`X$,]`.!#/0#`DCT`P)(] -M```TNP``-+L`@">\`(`GO`#`\#P`P/`\``#ZNP``^KL``#*]```RO0``8KL` -M`&*[``!8/```6#P`P"&]`,`AO0#`Y;P`P.6\`*`M/0"@+3T`P)<\`,"7/``@ -M;;T`(&V]`(##O`"`P[P`P!@]`,`8/0``&#H``!@Z`,`5O0#`%;T``)\\``"? -M/`!P@CT`<((]`,#M/`#`[3P``"B[```HNP"`H3P`@*$\`(#"/`"`PCP``(*\ -M``""O`"`-+P`@#2\``#%/```Q3P``$B[``!(NP"`(KT`@"*]``#FNP``YKL` -M0)H\`$":/```TKP``-*\`*`EO0"@);T`@*(\`("B/`!`Z#P`0.@\`,`6O0#` -M%KT`X"6]`.`EO0``-ST``#<]`$!#/0!`0ST``)^\``"?O```SKL``,Z[`(!& -M/0"`1CT`@`@]`(`(/0``.#L``#@[``"C/```HSP`@"`\`(`@/```-[T``#>] -M`,##O`#`P[P`H#4]`*`U/0``E#P``)0\`&!NO0!@;KT`@'J\`(!ZO`"@0CT` -MH$(]`,"KO`#`J[P`<(N]`'"+O0``4#L``%`[`.`V/0#@-CT`@":\`(`FO`#` -MGKP`P)Z\`.`7/0#@%ST``"\]```O/0``OCL``+X[`(`@/`"`(#P``*`\``"@ -M/```0[P``$.\`(`JO`"`*KP`X!4]`.`5/0"`G3P`@)T\```'O0``![T`@+6\ -M`("UO`!`R3P`0,D\```3/```$SP`H"B]`*`HO0"`O;P`@+V\```2/0``$CT` -M`#@\```X/``@9+T`(&2]`$#6O`!`UKP`P#D]`,`Y/0"`!#T`@`0]``"?NP`` -MG[L``$,\``!#/`#`UCP`P-8\``#U.P``]3L`@!H\`(`:/`#`X#P`P.`\``!4 -MO```5+P`H$^]`*!/O0``%SP``!<\`'""/0!P@CT``(T[``"-.P#@'KT`X!Z] -M``#K.P``ZSL``$$\``!!/`#`0[T`P$.]`"`(O0`@"+T`@!(]`(`2/0#`V3P` -MP-D\`,`%O0#`!;T``-^[``#?NP#`0ST`P$,]`(!M/`"`;3P`P)*\`,"2O`"` -MG#P`@)P\`(!#/`"`0SP`P/>\`,#WO```ACL``(8[`.!K/0#@:ST``(4\``"% -M/`!`++T`0"R]`(`EO`"`);P`P!8]`,`6/0``W3L``-T[```&O0``!KT``%B[ -M``!8NP"`2#P`@$@\`$"XO`!`N+P``+:\``"VO`"`BCP`@(H\`,"V/`#`MCP` -M`':[``!VNP``%+L``!2[`(##/`"`PSP``!`[```0.P``^[P``/N\``"$.P`` -MA#L`8#,]`&`S/0``$KP``!*\`.`)O0#@";T``!T]```=/0!@9ST`8&<]`(!' -MO`"`1[P``/F\``#YO```2CP``$H\``"$NP``A+L`@#.]`(`SO0``E#L``)0[ -M`$!-/0!`33T``+&[``"QNP"`,KT`@#*]`(!M/`"`;3P`P.D\`,#I/`"`X;P` -M@.&\`(!9O`"`6;P`@.4\`(#E/```.+H``#BZ`*`.O0"@#KT``)$\``"1/`"` -M;CT`@&X]``!(.@``2#H`@`R]`(`,O0!`DCP`0)(\`&`+/0!@"ST`@#*\`(`R -MO```6KL``%J[`$#3/`!`TSP`P*2\`,"DO`"@0KT`H$*]```=/```'3P`P$$] -M`,!!/0``D+L``)"[`$`SO0!`,[T``(`X``"`.`"`Q3P`@,4\`,"HO`#`J+P` -M@`N]`(`+O0"`93P`@&4\``#0/```T#P``.BZ``#HN@"`*#P`@"@\`&`?/0!@ -M'ST`@'(\`(!R/```G;P``)V\``!,.P``3#L`P*(\`,"B/```S[P``,^\`,"J -MO`#`JKP`P%`]`,!0/0!@!CT`8`8]`,!;O0#`6[T`0/>\`$#WO`#`%CT`P!8] -M``#PNP``\+L`,(B]`#"(O0``$+P``!"\`,`P/0#`,#T```.\```#O`!@![T` -M8`>]`,#N/`#`[CP`8"D]`&`I/0``]KL``/:[``"].P``O3L`0`4]`$`%/0`` -M_+L``/R[`$"6O`!`EKP`X`T]`.`-/0!`\CP`0/(\`*`IO0"@*;T`X!:]`.`6 -MO0#`+3T`P"T]`*`NP``'KL``-`\``#0/```_CL``/X[```..P``#CL` -M`-X\``#>/`!`OCP`0+X\`(!8O`"`6+P``&2[``!DNP``_CP``/X\`(!(/`"` -M2#P`@+"\`("PO`"`<#P`@'`\`*!$/0"@1#T``!J\```:O`#`;+T`P&R]`,"` -MO`#`@+P`P*T\`,"M/```WKP``-Z\`*!6O0"@5KT``-H[``#:.P!`PSP`0,,\ -M`$"EO`!`I;P``.B[``#HNP"`]CP`@/8\``#H.@``Z#H``!"\```0O`#@/#T` -MX#P]`$#[/`!`^SP`0!N]`$`;O0``:[P``&N\``"`/0``@#T`0`@]`$`(/0#@ -M.[T`X#N]``"4O```E+P`H"(]`*`B/0``0#D``$`Y`"!,O0`@3+T`P-2\`,#4 -MO`"`(+P`@""\`"`1O0`@$;T`P*2\`,"DO`"`RCP`@,H\`$#)/`!`R3P``*.[ -M``"CNP``Z#H``.@Z``#(/```R#P``*"Z``"@N@"`@;P`@(&\`$#6/`!`UCP` -M@"0]`(`D/0``!KP```:\`(!AO`"`8;P`@!D]`(`9/0``$#T``!`]````O0`` -M`+T`X$N]`.!+O0``C[L``(^[```&.P``!CL`0!^]`$`?O0"`M+P`@+2\`,"3 -M/`#`DSP```F\```)O```_[P``/^\``"-/```C3P`P!$]`,`1/0"`);P`@"6\ -M``!9O```6;P`0!L]`$`;/0!`U3P`0-4\`$#UO`!`];P``"P[```L.P``=3T` -M`'4]`$"(/`!`B#P`8`*]`&`"O0"``#P`@``\```R/```,CP`P#:]`,`VO0"@ -M![T`H`>]``!?/```7SP``'&\``!QO`#`2KT`P$J]```8.P``&#L`X%4]`.!5 -M/0``?CL``'X[```4O0``%+T``"`Y```@.0"`E#P`@)0\``!=O```7;P``!H[ -M```:.P"`1CT`@$8]`&`%/0!@!3T`@#:\`(`VO```I+L``*2[``#7/```USP` -M@`$\`(`!/````;T```&]``"1O```D;P```R[```,NP"`\[P`@/.\``"WO``` -MM[P`@.8\`(#F/```9#P``&0\``!$O0``1+T`@-B\`(#8O`#`'CT`P!X]`,#L -M/`#`[#P`P.>\`,#GO```/[P``#^\`$`!/0!``3T``,@Z``#(.@#`A+P`P(2\ -M`"`;/0`@&ST`X"\]`.`O/0``B+P``(B\``"$O```A+P`P-4\`,#5/`"`-;P` -M@#6\`"!BO0`@8KT``-&[``#1NP``$ST``!,]`(#!O`"`P;P`H$Z]`*!.O0`` -M^#P``/@\``!`/0``0#T`P`"]`,``O0!@'[T`8!^]``!*/```2CP``%0[``!4 -M.P``:[P``&N\`,#7/`#`USP`P"P]`,`L/0``++L``"R[`(!ZO`"`>KP``)X\ -M``">/`"`8SP`@&,\``#OO```[[P```^\```/O`"`SSP`@,\\```[O```.[P` -MP#B]`,`XO0``D3L``)$[`"!%/0`@13T``'0[``!T.P"@6KT`H%J]``"TO``` -MM+P`@`$]`(`!/0"`##P`@`P\`(#>O`"`WKP``*2[``"DNP``NSL``+L[``"Z -MNP``NKL``,,\``##/`!@-3T`8#4]``!`N0``0+D`(`:]`"`&O0``E#L``)0[ -M``"C/```HSP`@/R\`(#\O``@!+T`(`2]`"`D/0`@)#T`(`T]`"`-/0!`#KT` -M0`Z]`,"5O`#`E;P`@"(]`(`B/0``-#L``#0[`,!6O0#`5KT`P*Z\`,"NO`!` -MDCP`0)(\``"-NP``C;L`@!F\`(`9O`!`\3P`0/$\`$">/`!`GCP`@.R\`(#L -MO```$+L``!"[`(#[/`"`^SP``$>\``!'O`!``;T`0`&]`$#./`!`SCP`X!P] -M`.`.P``#;T```V]`(#MO`"`[;P` -MP(@\`,"(/`!`BSP`0(L\`("[O`"`N[P``+Z\``"^O`"`1CP`@$8\``"W.P`` -MMSL`0-:\`$#6O```'KP``!Z\`,"@/`#`H#P``,@[``#(.P``6#L``%@[`*`N -M/0"@+CT`H#8]`*`V/0"`*+P`@"B\`(#/O`"`S[P`0(`\`$"`/```E#L``)0[ -M```LO0``++T`@!6\`(`5O`#`(#T`P"`]``!QO```<;P`@&*]`(!BO0``ECL` -M`)8[````/0```#T`H`>]`*`'O0``2+T``$B]``#[.P``^SL``*4\``"E/``` -M`KP```*\``!I/```:3P`P"H]`,`J/0``ASL``(<[`(`VO`"`-KP`P"(]`,`B -M/0```3T```$]`,`#O0#``[T`P+:\`,"VO```WCP``-X\``!(.@``2#H`H%&] -M`*!1O0"`BKP`@(J\`$`1/0!`$3T``%X[``!>.P"`2+T`@$B]``#*O```RKP` -M`,@[``#(.P#`C+P`P(R\`$"1O`!`D;P`@'`\`(!P/`"`;3P`@&T\``!2.P`` -M4CL`P,L\`,#+/`#`1#T`P$0]``!F/```9CP`0+^\`$"_O```]+H``/2Z`,"K -M/`#`JSP`P)"\`,"0O`"`^[P`@/N\`$#!/`!`P3P`@,P\`(#,/`#`)[T`P">] -M`,!"O0#`0KT``/0[``#T.P``&SP``!L\`&`;O0!@&[T`P.>\`,#GO`"`$3P` -M@!$\``#)NP``R;L``&R[``!LNP!@1#T`8$0]```)/0``"3T`@/B\`(#XO``` -MM#H``+0Z``!I/0``:3T``#T\```]/```,[T``#.]``#HNP``Z+L`@/`\`(#P -M/`"`.KP`@#J\`*`0O0"@$+T``$$\``!!/`#`GSP`P)\\`.`+O0#@"[T``#&] -M```QO0``4KP``%*\`,"5O`#`E;P`0*J\`$"JO`"`4CP`@%(\`(#'/`"`QSP` -M``"Y````N0``M#H``+0Z`$`?/0!`'ST`X`(]`.`"/0#`Y;P`P.6\``#VO``` -M]KP``-<\``#7/```KCP``*X\`$"MO`!`K;P``,2[``#$NP``\CP``/(\```> -M.P``'CL`(#J]`"`ZO0"`"[T`@`N]```^.P``/CL`@%&\`(!1O`!`"+T`0`B] -M```2NP``$KL``,T[``#-.P!`K+P`0*R\`(`./`"`#CP`($0]`"!$/0"`%#P` -M@!0\`(`,O0"`#+T``(([``"".P!`)ST`0"<]```HNP``*+L`P/.\`,#SO`"` -M@#P`@(`\``"R/```LCP`@..\`(#CO`"`,;P`@#&\`*`"/0"@`CT`@$>\`(!' -MO`#`9+T`P&2]`("ZO`"`NKP``.`Z``#@.@!`G+P`0)R\`,"`O`#`@+P``,$\ -M``#!/```FSP``)L\``"@O```H+P``/6[``#UNP``^SP``/L\``#E.P``Y3L` -M`/*\``#RO```J+L``*B[`("^/`"`OCP``"8\```F/```[+H``.RZ`(`=/`"` -M'3P``"0\```D/`!`S;P`0,V\`$#YO`!`^;P``(8[``"&.P``A+L``(2[`,`C -MO0#`([T``&V\``!MO`#`TCP`P-(\``"%NP``A;L`@/:\`(#VO`"`%#P`@!0\ -M```./0``#CT`@!:\`(`6O`!@&KT`8!J]``#'.P``QSL``+`\``"P/```CKP` -M`(Z\```SP``'L\``"GNP``I[L` -M@#@\`(`X/`"`]SP`@/<\`(`MO`"`+;P`H#^]`*`_O0``=+L``'2[`$`'/0!` -M!ST`@`B\`(`(O``@%[T`(!>]````N@```+H`@,8\`(#&/```#[P```^\`(#R -MO`"`\KP``,"Z``#`N@``C3L``(T[`(#8O`"`V+P`0(.\`$"#O```5CP``%8\ -M```5O```%;P``&"\``!@O`!@$#T`8!`]````/0```#T`0,J\`$#*O`"`HKP` -M@**\``#W/```]SP`@$0\`(!$/`"@.;T`H#F]`$"(O`!`B+P`("H]`"`J/0"` -M!#P`@`0\`*`.O0"@#KT``*H[``"J.P#`OSP`P+\\`(#\O`"`_+P`@#.]`(`S -MO0"``[P`@`.\``#ANP``X;L`@(^\`("/O```1#P``$0\``#H/```Z#P`@':\ -M`(!VO```O[P``+^\`(`//0"`#ST`P!,]`,`3/0``^KP``/J\`(#ZO`"`^KP` -MP-@\`,#8/`#`PCP`P,(\`(!=O`"`7;P``/^[``#_NP``=#P``'0\``!@.0`` -M8#D`@.6\`(#EO```7;P``%V\``!6.P``5CL`@-&\`(#1O`"`%+T`@!2]```> -MNP``'KL``(4[``"%.P"`F+P`@)B\``"F.P``ICL`H"@]`*`H/0``5CP``%8\ -M`*`(O0"@"+T`@`V\`(`-O``@%3T`(!4]``"H.P``J#L`P-B\`,#8O`!`I#P` -M0*0\`.`7/0#@%ST``"Z\```NO`"`B+P`@(B\`(`_/`"`/SP`@&V\`(!MO`!` -M4KT`0%*]`$#2O`!`TKP``(@[``"(.P``A[P``(>\`$"BO`!`HKP`0-T\`$#= -M/`"`O3P`@+T\```7O0``%[T`0+V\`$"]O`#@'#T`X!P]`,"U/`#`M3P`@+.\ -M`("SO```0KL``$*[`(#Y/`"`^3P`P(\\`,"//```&CL``!H[``"0/```D#P` -M`!L\```;/```!;T```6]```&O0``!KT``%H[``!:.P``#[P```^\`"`CO0`@ -M([T`P*>\`,"GO```D#L``)`[``"JNP``JKL``"*\```BO`"`:3P`@&D\``"[ -M/```NSP`@'B\`(!XO`!`^[P`0/N\``"#/```@SP`@!H]`(`:/0``:CL``&H[ -M``"4.P``E#L`(!(]`"`2/0"`2SP`@$L\`,"VO`#`MKP``%"[``!0NP"`.#P` -M@#@\`&`'O0!@![T`H&*]`*!BO0``T#D``-`Y`$"K/`!`JSP`P..\`,#CO``` -MTKP``-*\``"R/```LCP``/@Z``#X.@!`$KT`0!*]``#(N@``R+H`0/$\`$#Q -M/```PCL``,([``#LN@``[+H``.L\``#K/`#`U3P`P-4\`(!%O`"`1;P``,@[ -M``#(.P!`-#T`0#0]``#,N@``S+H``&2]``!DO0!`I[P`0*>\`("#L``'@[`$`^ -MO0!`/KT``'&\``!QO```(#T``"`]``!A/```83P`0.*\`$#BO```NKL``+J[ -M`,"1/`#`D3P``(*[``""NP#`C+P`P(R\``"DNP``I+L`@$R\`(!,O`"@`+T` -MH`"]``"QNP``L;L`8`@]`&`(/0``=#L``'0[`("\O`"`O+P`@+L\`("[/`!@ -M(#T`8"`]``!4O```5+P`P)>\`,"7O`"`\SP`@/,\``"+/```BSP`8`2]`&`$ -MO0"`?+P`@'R\``#_/```_SP`@&<\`(!G/`!`E[P`0)>\`(`5/`"`%3P`@)T\ -M`("=/`#`T[P`P-.\``#UO```];P``%`[``!0.P``Q+L``,2[`$#%O`!`Q;P` -M`-([``#2.P``!#T```0]``#P.P``\#L``&Z\``!NO`"`.#P`@#@\`,#5/`#` -MU3P``'*\``!RO`!``KT`0`*]`(`=/`"`'3P``.4\``#E/`"`)CP`@"8\```; -M/```&SP``($\``"!/```L;L``+&[`,#,O`#`S+P``-"Z``#0N@#`D#P`P)`\ -M`,"2O`#`DKP`X"6]`.`EO0``L#L``+`[`,#_/`#`_SP``+"[``"PNP``8+P` -M`&"\``!^/```?CP``)P[``"<.P"`S;P`@,V\``#!NP``P;L`P-X\`,#>/``` -MCCL``(X[`$"KO`!`J[P`0)(\`$"2/`#@)CT`X"8]````.P```#L``':[``!V -MNP#`T#P`P-`\``"CNP``H[L`P$*]`,!"O0``5KP``%:\``#-/```S3P``#R[ -M```\NP#`TKP`P-*\```W/```-SP`0.,\`$#C/`#`@[P`P(.\`$#IO`!`Z;P` -M`+$[``"Q.P``\#D``/`Y`,"SO`#`L[P``*,[``"C.P!@!CT`8`8]``![/``` -M>SP``)<[``"7.P``RSP``,L\`(#*/`"`RCP`P(>\`,"'O`"``;T`@`&]`(!@ -M/`"`8#P`0-4\`$#5/```EKP``):\`,#2O`#`TKP``/8[``#V.P``]SL``/<[ -M`(!7O`"`5[P``*"[``"@NP``%CL``!8[`,"[O`#`N[P``/*\``#RO```1SP` -M`$<\`,#J/`#`ZCP`@""\`(`@O```[+H``.RZ`&`S/0!@,ST`0,<\`$#'/`"` -M5+P`@%2\`(!3/`"`4SP`H`0]`*`$/0#`B[P`P(N\`*`^O0"@/KT``(D[``") -M.P`@(#T`("`]````N0```+D``,6\``#%O```Z#H``.@Z`(`#O`"``[P`X`B] -M`.`(O0``]+L``/2[```:.P``&CL`P.N\`,#KO```=KP``':\`(`G/0"`)ST` -M`"T]```M/0``;+L``&R[``#PN@``\+H`(!8]`"`6/0``BSP``(L\``#VO``` -M]KP```^\```/O`#`_3P`P/T\`(`7/`"`%SP`@(2\`("$O````KL```*[``#_ -M.P``_SL`@"V\`(`MO```LKP``+*\`(`.O`"`#KP`@&:\`(!FO`"@'KT`H!Z] -M`(""O`"`@KP`@-L\`(#;/```1SP``$<\```$O```!+P``.@\``#H/```,3T` -M`#$]``"DN@``I+H``)*\``"2O`#`I#P`P*0\`,#?/`#`WSP`@*>\`("GO`#` -ML;P`P+&\`(#R/`"`\CP`0,P\`$#,/```:[P``&N\`(`/O`"`#[P`@".\`(`C -MO```/+T``#R]`$`:O0!`&KT``#D\```Y/```_SL``/\[``#DO```Y+P``-6[ -M``#5NP"@/#T`H#P]`,#T/`#`]#P``(V\``"-O```*CL``"H[`,#E/`#`Y3P` -M`!*[```2NP"`&[P`@!N\`(#*/`"`RCP`@-$\`(#1/```KKL``*Z[``#9NP`` -MV;L``/,[``#S.P``\+L``/"[``#RO```\KP`P(^\`,"/O```O[L``+^[```% -MO0``!;T`@`.]`(`#O0"`8#P`@&`\``#2/```TCP``#2[```TNP``#KL```Z[ -M`,#C/`#`XSP`@,4\`(#%/`"`6;P`@%F\`(!!O`"`0;P``,$\``#!/```C3P` -M`(T\```AO```(;P``&H\``!J/`!`^#P`0/@\``"VNP``MKL`0**\`$"BO``` -M]+H``/2Z`$"9O`!`F;P``%B]``!8O0"`Y;P`@.6\`("T/`"`M#P`@!H\`(`: -M/`#`M+P`P+2\``"K.P``JSL`0/8\`$#V/```'+L``!R[`(`SO`"`,[P`@*<\ -M`("G/`"`%SP`@!<\`("-O`"`C;P`@!$\`(`1/`#`+ST`P"\]`,"//`#`CSP` -M`)B\``"8O```H#D``*`Y`(",/`"`C#P`@*J\`("JO`#@&[T`X!N]``"XNP`` -MN+L``$J[``!*NP`@`;T`(`&]`(![O`"`>[P`P*\\`,"O/`"`ESP`@)<\``"X -MNP``N+L``(Z[``".NP``\SL``/,[``"\NP``O+L``%2\``!4O```@3P``($\ -M``#)/```R3P``"`Z```@.@``%CP``!8\`"`6/0`@%CT``%(\``!2/`#`![T` -MP`>]`(#-O`"`S;P``-L[``#;.P"`,;P`@#&\`.`HO0#@*+T``.F[``#INP#` -M`3T`P`$]``!`.P``0#L`P)"\`,"0O```S3L``,T[``",N@``C+H`@-*\`(#2 -MO```_+H``/RZ`$#//`!`SSP``*2Z``"DN@``?KP``'Z\`(#./`"`SCP`@"\] -M`(`O/0``)+L``"2[`$"6O`!`EKP`0(D\`$")/```CSL``(\[`.`8O0#@&+T` -M0):\`$"6O```?3P``'T\``#`N@``P+H`P+B\`,"XO```L+D``+"Y`$"1/`!` -MD3P``-RZ``# -MO`!`(3T`0"$]`(#C/`"`XSP`0,>\`$#'O```&+P``!B\`$"[/`!`NSP``/Z[ -M``#^NP!`Y+P`0.2\``#H.P``Z#L`@-\\`(#?/```$CL``!([``#MNP``[;L` -M`#(\```R/```X+H``."Z``#?O```W[P`@!.\`(`3O```@#D``(`Y```(O0`` -M"+T`0`"]`$``O0``Q#P``,0\`"`G/0`@)ST``#8[```V.P``>;P``'F\``"S -M.P``LSL```H\```*/`!`@[P`0(.\```.O```#KP``*(\``"B/```^SL``/L[ -M``#7NP``U[L``)P\``"\```^.P``/CL``#B[```XNP!`PKP`0,*\`(`R/`"` -M,CP`P%D]`,!9/0``]3P``/4\`(`-O`"`#;P``)X[``">.P"`U3P`@-4\```@ -M.0``(#D`P-^\`,#?O`"`#+P`@`R\``"U.P``M3L`@&R\`(!LO`#`A;P`P(6\ -M``"..P``CCL``."Y``#@N0``K;P``*V\``"=O```G;P``-J[``#:NP"`E;P` -M@)6\`("@O`"`H+P``'P\``!\/`!`Y#P`0.0\``#(NP``R+L``&:[``!FNP#@ -M.CT`X#H]`(`L/0"`+#T``#^\```_O`#`J+P`P*B\``"B.P``HCL``#Z[```^ -MNP#`PKP`P,*\``!(NP``2+L`@'L\`(![/```.KP``#J\`("HO`"`J+P``)([ -M``"2.P``Y;L``.6[`.`JO0#@*KT`0+"\`$"PO`"`6#P`@%@\``",NP``C+L` -M@'R\`(!\O`!`S3P`0,T\`.`_/0#@/ST``!<\```7/```8KP``&*\`(!&/`"` -M1CP``%T\``!=/`"`:[P`@&N\`(`1O`"`$;P``$\\``!//```$+L``!"[`,"( -MO`#`B+P````Z````.@``ESL``)<[`$"8O`!`F+P``,B\``#(O```PKL``,*[ -M```4O```%+P```.]```#O0``4;P``%&\`$#\/`!`_#P`P,4\`,#%/```#;P` -M``V\```D.P``)#L`@/<\`(#W/`"`:CP`@&H\`(!HO`"`:+P`@!B\`(`8O``` -MS#H``,PZ```TO```-+P``&Z[``!NNP``Y#P``.0\`(!S/`"`\``#7 -MO```G;P``)V\``"8N@``F+H`P,B\`,#(O`#`%;T`P!6]``"\N@``O+H`@',\ -M`(!S/```%KP``!:\``"NNP``KKL`H`0]`*`$/0"`S3P`@,T\`$"2O`!`DKP` -M@&F\`(!IO```1#P``$0\```TNP``-+L``$F\``!)O```=3P``'4\`$"D/`!` -MI#P```J\```*O```3+P``$R\``!*/```2CP``'`[``!P.P#`\;P`P/&\`,#+ -MO`#`R[P``$"Z``!`N@``.KP``#J\``"TO```M+P``)\[``"?.P``L3P``+$\ -M``!:.P``6CL``-R[``#\``!GO```\[L` -M`/.[`(!W/`"`=SP``(D[``").P``O```?KL``'Z[`(!G -M/`"`9SP``!`[```0.P!`J;P`0*F\``!-O```3;P```0[```$.P``DKP``)*\ -M`,#WO`#`][P``+0[``"T.P"`WCP`@-X\``#@.@``X#H``*"[``"@NP#`L#P` -MP+`\`,"W/`#`MSP``%F\``!9O```UKP``-:\``":NP``FKL``+0Z``"T.@`` -M*;P``"F\``!2.P``4CL`P(4\`,"%/`"`"[P`@`N\``"ZO```NKP```R[```, -MNP``XKL``.*[`.`.O0#@#KT`0,"\`$#`O`"`0#P`@$`\```T.P``-#L`@*^\ -M`("OO`"`'#P`@!P\`*`K/0"@*ST``,P[``#,.P``YKP``.:\``!XN@``>+H` -MP(\\`,"//```H+L``*"[```/`"``;P`@`&\ -M``#@.0``X#D`@*`\`("@/```3#L``$P[`,"LO`#`K+P``*N[``"KNP"`<#P` -M@'`\``"#.P``@SL``"`[```@.P"`@3P`@($\``"H.@``J#H`P.2\`,#DO`"` -MN+P`@+B\``#3NP``T[L`0,"\`$#`O`!@';T`8!V]```GO```)[P``&<\``!G -M/```[[L``.^[``",O```C+P`0(L\`$"+/`#`NCP`P+H\`(`;O`"`&[P`@#N\ -M`(`[O```5CP``%8\``#&.P``QCL`@`"\`(``O```53P``%4\`$"[/`!`NSP` -M`/2[``#TNP``CKP``(Z\```;/```&SP``,PZ``#,.@`@*[T`("N]`.`FO0#@ -M)KT``"B\```HO```3;P``$V\`,"DO`#`I+P``(@[``"(.P!`H3P`0*$\```8 -MNP``&+L`@&2\`(!DO```D3L``)$[```3/```$SP`@!.\`(`3O```<+L``'"[ -M`("H/`"`J#P`0(P\`$",/```^#H``/@Z``!./```3CP``'T\``!]/`#`A;P` -MP(6\`(`DO0"`)+T``+"\``"PO```S+L``,R[`,#!O`#`P;P`0-V\`$#=O``` -M(#D``"`Y``"[.P``NSL``%*\``!2O```J;L``*F[`(!0/`"`4#P``$J[``!* -MNP"`C;P`@(V\``";.P``FSL`@*`\`("@/```J#H``*@Z````N@```+H``/8\ -M``#V/`#`TCP`P-(\`(![O`"`>[P``*B\``"HO```J#H``*@Z``!EO```9;P` -M8!N]`&`;O0"`M+P`@+2\``"INP``J;L`@&Z\`(!NO```Y;L``.6[``"%/``` -MA3P``*D[``"I.P#`X+P`P."\`("-O`"`C;P``$,\``!#/```%CL``!8[```0 -MO```$+P`0)D\`$"9/```"ST```L]``#R.P``\CL``/2[``#TNP"`)SP`@"<\ -M```=/```'3P`@+B\`("XO``@$KT`(!*]`(!/O`"`3[P``("Z``"`N@``-+P` -M`#2\``#*NP``RKL```"[````NP``A+P``(2\`("'O`"`A[P``-@[``#8.P`` -MOCL``+X[`$"UO`!`M;P`0(Z\`$".O`!`J#P`0*@\`$##/`!`PSP``$R[``!, -MNP``ZSL``.L[`$`+/0!`"ST`@!$\`(`1/```TKP``-*\`(!RO`"`[``"'NP``J#H``*@Z``#^/```_CP``-`\``#0/`"`$KP`@!*\`(`8 -MO`"`&+P`@#8\`(`V/```2+H``$BZ`("ZO`"`NKP``$2\``!$O```F#H``)@Z -M``"@NP``H+L``&`Z``!@.@"`53P`@%4\``#0NP``T+L`(`*]`"`"O0#`EKP` -MP):\`(`O/`"`+SP``&`Y``!@.0"`U[P`@->\`(`.O`"`#KP`@+H\`("Z/``` -M;CP``&X\``"$.@``A#H``'(\``!R/`"`;3P`@&T\``!RO```]`$#WO`!`][P` -M@#L\`(`[/```B#P``(@\``"PNP``L+L``)X[``">.P#`N#P`P+@\```*.P`` -M"CL`@*>\`("GO```0KL``$*[```$/```!#P`@`^\`(`/O```DKL``)*[`$"" -M/`!`@CP`@"0\`(`D/```H[L``*.[``#C.P``XSL`@'L\`(![/```2[P``$N\ -M```&O0``!KT``$R\``!,O```FCL``)H[```)O```";P`@$V\`(!-O```RSL` -M`,L[``#O.P``[SL``("[``"`NP``1#L``$0[``!4/```5#P``+:[``"VNP#` -MT[P`P-.\``#ENP``Y;L`0)D\`$"9/```,SP``#,\``!F.P``9CL`0*8\`$"F -M/`#`F#P`P)@\`(!"O`"`0KP`P(.\`,"#O```#CL```X[``#CNP``X[L`@.2\ -M`(#DO```6+P``%B\``!1/```43P``*8[``"F.P``HKL``**[``##.P``PSL` -M`+2Z``"TN@``T+P``-"\``!TO```=+P``&,\``!C/```Q3L``,4[`(`BO`"` -M(KP`@%D\`(!9/`!@"ST`8`L]`(!4/`"`5#P``/R[``#\NP``]#L``/0[``!" -M/```0CP`@&Z\`(!NO`#`PKP`P,*\``"GNP``I[L``$"Y``!`N0``X+L``."[ -M```V.P``-CL```H\```*/```1KP``$:\``"NO```KKP``/RZ``#\N@``B#L` -M`(@[`,"0O`#`D+P`P)6\`,"5O`"`?3P`@'T\`(#N/`"`[CP``&@\``!H/``` -M>3P``'D\`$#4/`!`U#P``-4[``#5.P!`K+P`0*R\`(!6O`"`5KP``+T[``"] -M.P``2+L``$B[```[O```.[P``!X[```>.P``HCL``*([`(!,O`"`3+P```&\ -M```!O```7#L``%P[`,"MO`#`K;P`P"*]`,`BO0``G;L``)V[`(#H/`"`Z#P` -M@"T\`(`M/```F;L``)F[`,"E/`#`I3P`P``]`,``/0``_3L``/T[``!4NP`` -M5+L`@`L\`(`+/```%#L``!0[``!#O```0[P```B[```(NP``E3L``)4[```$ -MO```!+P``-B[``#8NP"`'#P`@!P\```RNP``,KL`8`N]`&`+O0!`Z[P`0.N\ -M``"4.@``E#H``!"Z```0N@#`D[P`P).\``!0.@``4#H`(`,]`"`#/0"`PCP` -M@,(\``#H.P``Z#L``(`\``"`/`#`ESP`P)<\``"GNP``I[L`0)"\`$"0O``` -MH+D``*"Y`(`C/`"`(SP``%H[``!:.P``VCL``-H[``#N.P``[CL`0(B\`$"( -MO`"`Z[P`@.N\``"ZNP``NKL``+"Z``"PN@!``[T`0`.]`$`*O0!`"KT```4\ -M```%/`"`ZCP`@.H\```5/```%3P``-X[``#>.P#`OCP`P+X\`(`_/`"`/SP` -M`,B[``#(NP"``CP`@`(\``!Z/```>CP``.V[``#MNP"`-[P`@#>\`(!1/`"` -M43P``%8\``!6/```^KL``/J[``#9NP``V;L``"0[```D.P"`I;P`@*6\```H -MO0``*+T`@)R\`("\```B.P``(CL`P)\\`,"?/`"`"SP`@`L\``"@.0``H#D`@#L\`(`[/``` -M"#L```@[`,#QO`#`\;P`(`J]`"`*O0"``;P`@`&\```VNP``-KL`P*>\`,"G -MO```4[P``%.\``!,/```3#P``&4\``!E/```G3L``)T[``!0/```4#P``!(\ -M```2/`"`8KP`@&*\`(`O`"`'KP``)>[``"7 -MNP"`73P`@%T\``#8N@``V+H``-V\``#=O`"`$;P`@!&\`$#)/`!`R3P`0)4\ -M`$"5/```=KL``':[``#8.P``V#L`P(\\`,"//```W+H``-RZ``!BO```8KP` -M`*6[``"ENP"`*[P`@"N\`$#$O`!`Q+P`@"V\`(`MO```2#P``$@\```%/``` -M!3P``#BZ```XN@``SSL``,\[``#`N0``P+D`0-2\`$#4O`!`LKP`0+*\`(`' -M/`"`!SP``.T[``#M.P"`7KP`@%Z\``"`.0``@#D`@/8\`(#V/`!`QSP`0,<\ -M``!6NP``5KL``!R\```\`,"'O`"`6+P`@%B\``#$.P``Q#L``#`Z```P.@!`DKP` -M0)*\```^O```/KP``#P[```\.P``RKL``,J[```UO```-;P`@)H\`(":/`!@ -M*#T`8"@]``"U/```M3P``+`Y``"P.0``0CL``$([``"B.P``HCL``,&[``#! -MNP``*[P``"N\`(`-O`"`#;P`@'6\`(!UO`"`([P`@".\`(!6/`"`5CP``%D\ -M``!9/`!`G;P`0)V\``#HO```Z+P``'J[``!ZNP```+H```"Z``"HO```J+P` -M`#*\```RO```J3P``*D\`$"_/`!`OSP``-D[``#9.P``33P``$T\``"O/``` -MKSP``*H[``"J.P``Y+L``.2[`````````````)6[``"5NP!`D+P`0)"\``"S -MNP``L[L``%4\``!5/```'CL``!X[``!AO```8;P``*6[``"ENP``X#D``.`Y -M`$"8O`!`F+P`P-&\`,#1O```1+L``$2[``#8.P``V#L``-.[``#3NP``O+H` -M`+RZ``#//```SSP``.\\``#O/```!3P```4\``!(NP``2+L``'Z[``!^NP`` -M+KP``"Z\`(`%O`"`!;P``!<\```7/`"`(3P`@"$\```UO```-;P```:\```& -MO`"`;3P`@&T\``#G.P``YSL``,.\``##O`#`X+P`P."\```)O```";P``,N[ -M``#+NP"`#KP`@`Z\``#J.P``ZCL`P(D\`,")/```D#L``)`[``#`.0``P#D` -M@%D\`(!9/```!3P```4\```[O```.[P``.V[``#MNP``OCL``+X[``"NP``.#P``#@\`$"-/`!`C3P``(2[``"$NP``7+P``%R\``#@ -MN@``X+H``/"[``#PNP!`VKP`0-J\``"8O```F+P``+4[``"U.P``U3L``-4[ -M``"(N@``B+H```,\```#/```:SP``&L\```H.@``*#H```*\```"O```I+H` -M`*2Z``"]`,"NO`#` -MKKP`@#&\`(`QO`!`H[P`0*.\`,"4O`#`E+P``&BZ``!HN@``0CL``$([```@ -M.@``(#H`@%D\`(!9/`"`GCP`@)X\```(N@``"+H``%B\``!8O```$CL``!([ -M``!J/```:CP``)4[``"5.P``D#L``)`[`,"+/`#`BSP``$(\``!"/```VKL` -M`-J[```&O```!KP``$2[``!$NP!`C[P`0(^\`(#ZO`"`^KP`P)*\`,"2O``` -MG+L``)R[`(`?O`"`'[P``-V[``#=NP``/3P``#T\``!)/```23P``&"[``!@ -MNP``<+H``'"Z`(`8/`"`&#P``*"Y``"@N0``%[P``!>\```K/```*SP``-(\ -M``#2/`"`'#P`@!P\```VNP``-KL`@!@\`(`8/```$SP``!,\`("#O`"`@[P` -MP.F\`,#IO`#`I;P`P*6\`("6O`"`EKP``'B\``!XO```?CL``'X[`(!'/`"` -M1SP``+F[``"YNP"`.[P`@#N\```#/````SP`@#D\`(`Y/`"`)[P`@">\``!G -MO```9[P``/8[``#V.P#`B3P`P(D\`(`S/`"`,SP`@&T\`(!M/`#`BCP`P(H\ -M```P.@``,#H``%.\``!3O`"``;P`@`&\``#TNP``]+L`0+&\`$"QO```K[P` -M`*^\``"ANP``H;L``-RZ``#[``#'NP``!KP```:\ -M```\O```/+P``!P[```<.P``"3P```D\```%O```!;P``$6\``!%O```^CL` -M`/H[``#\.P``_#L``*^\``"OO`!`[KP`0.Z\``!&NP``1KL``$`\``!`/``` -M2CL``$H[``"=.P``G3L`P(@\`,"(/`"`-CP`@#8\``"`.```@#@``-@Z``#8 -M.@``BKL``(J[``"?O```G[P``%N\``!;O`"`*SP`@"L\```C/```(SP``.V[ -M``#MNP``8+H``&"Z`(!C/`"`8SP``-RZ``#3P`@'D\``#).P``R3L``&*\``!BO`"` -M1;P`@$6\``"@.P``H#L``!X[```>.P``"[P```N\``!H.P``:#L``*X\``"N -M/```9CP``&8\```F.P``)CL``.<[``#G.P``A#H``(0Z`("LO`"`K+P`0+R\ -M`$"\O```;+L``&R[``"$N@``A+H`@#.\`(`SO```N+H``+BZ``!-/```33P` -M`.`Z``#@.@``-[P``#>\``#LN@``[+H``),[``"3.P"`$[P`@!.\`(`=O`"` -M';P`@"`\`(`@/`#`@#P`P(`\``#7.P``USL``#L\```[/`#`K3P`P*T\`(`( -M/`"`"#P``#N\```[O`"`-;P`@#6\``#CNP``X[L``%J\``!:O```/KP``#Z\ -M``#L.@``[#H``"`Y```@.0"`*+P`@"B\``!@.0``8#D`@&L\`(!K/```/KL` -M`#Z[`(#BO`"`XKP``(:\``"&O```0#P``$`\``"%/```A3P`@!$\`(`1/`"` -M=#P`@'0\`$">/`!`GCP``"4\```E/```O#L``+P[``#;.P``VSL`@`2\`(`$ -MO`!`MKP`0+:\```PO```,+P``+D[``"Y.P``0+H``$"Z``#INP``Z;L``$0[ -M``!$.P``V#H``-@Z`("/O`"`C[P`P)"\`,"0O```&#L``!@[```J.P``*CL` -M@$*\`(!"O```<+H``'"Z`("^/`"`OCP`@-`\`(#0/```=3P``'4\`(!I/`"` -M:3P``"X\```N/```;KL``&Z[``#4NP``U+L```"X````N```_KL``/Z[`,"' -MO`#`A[P``&`Y``!@.0!`BCP`0(H\``"`.```@#@`P*N\`,"KO```,KP``#*\ -M``#8.@``V#H``#V\```]O`!`M+P`0+2\``!LNP``;+L`@(4\`("%/`"`G3P` -M@)T\`("P/`"`L#P`0,`\`$#`/`"`'3P`@!T\```,NP``#+L``+X[``"^.P`` -M"SP```L\`(`.O`"`#KP``$V\``!-O```T3L``-$[`(`F/`"`)CP``/&[``#Q -MNP"`!;P`@`6\``"2.P``DCL```N\```+O`"`^[P`@/N\`("8O`"`F+P```H\ -M```*/```_CL``/X[```XNP``.+L````\````/```MCP``+8\`$"H/`!`J#P` -M`(L\``"+/`"`6CP`@%H\```VNP``-KL`@%.\`(!3O```?#L``'P[`(!R/`"` -MCP``'H\ -M``!K/```:SP`@$<\`(!'/```DSP``),\`$#T/`!`]#P`X%V]`.!=O0"@L+T` -MH+"]`*!^/0"@?CT``->[``#7NP``PCL``,([`"AN/@`H;CX`0)*]`$"2O0#` -M3KX`P$Z^`)"=/0"0G3T``"4]```E/0#`V+T`P-B]``#I/```Z3P`"`L^``@+ -M/@``$KT``!*]`%`0O@!0$+X`(#D]`"`Y/0``,+L``#"[`)"DO0"0I+T`0/,] -M`$#S/0!X$3X`>!$^`("JO`"`JKP``$2]``!$O0#`#[T`P`^]```]`"!'O0`@GST`()\]`.`C/0#@(ST` -MX).]`."3O0!`GSP`0)\\`("UO`"`M;P``#BZ```XN@"@%#T`H!0]`(`#O`"` -M`[P`0/6\`$#UO`"`U+P`@-2\`"#&O0`@QKT`8%`]`&!0/0!0[CT`4.X]`.`E -MO0#@);T``(8[``"&.P!PX#T`<.`]`"`MO0`@+;T`0)*]`$"2O0!@N3T`8+D] -M`(#_O`"`_[P`D-B]`)#8O0"`?CP`@'X\`$#Z/0!`^CT`@*8]`("F/0#`J[P` -MP*N\`.@)O@#H";X``!>]```7O0!`K[P`0*^\``"(.P``B#L``-0\``#4/``` -M>CT``'H]``">.P``GCL``+$[``"Q.P``%CP``!8\`(#3/`"`TSP`T+6]`-"U -MO0``O;P``+V\`%#$/0!0Q#T`(&4]`"!E/0!@8[T`8&.]`"`;/0`@&ST``*"Z -M``"@N@!`'+X`0!R^``!*.P``2CL`L`T^`+`-/@``9;P``&6\``#LO```[+P` -MX``]`.``/0``X[P``..\``!1O```4;P`0'"]`$!PO0"`)[P`@">\`-#;/0#0 -MVST`T,`]`-#`/0!@$KT`8!*]`(`!/`"``3P`H`"]`*``O0#`W;T`P-V]`*`J -MO0"@*KT`P+0]`,"T/0#`I+P`P*2\``#INP``Z;L`L+,]`+"S/0"0E3T`D)4] -M`#"TO0`PM+T`,.^]`##OO0``.CP``#H\`$!;/0!`6ST``#.\```SO``PN#T` -M,+@]`!"1/0`0D3T`X'"]`.!PO0!@4+T`8%"]```*/```"CP`@`$]`(`!/0`` -M5KP``%:\`."/0``U3P``-4\`&!#O0!@0[T`X`:]`.`&O0!`CKP`0(Z\`*"HO0"@ -MJ+T`(*.]`""CO0``E#H``)0Z`$"1/0!`D3T`0(X]`$"./0"@#CT`H`X]``"@ -M.P``H#L``)B[``"8NP#PE[T`\)>]`*`[O0"@.[T`X-`]`.#0/0!@$CT`8!(] -M`(`VO`"`-KP`$+T]`!"]/0"`"CP`@`H\`'"9O0!PF;T``'B[``!XNP"`*+T` -M@"B]`-"YO0#0N;T`P.B\`,#HO`!@;ST`8&\]`)"&/0"0ACT`P)N\`,";O``P -M![X`,`>^`*!4O0"@5+T``-P]``#O0!@`[T`8`.]`##&/0`PQCT`\,(]`/#"/0#`CCP`P(X\``",O``` -MC+P``+BZ``"XN@!`X#P`0.`\```(.P``"#L`@+.]`("SO0#`I#P`P*0\``@5 -M/@`(%3X``/D[``#Y.P"`;[T`@&^]`(!"/`"`0CP`0.*\`$#BO`#@A+T`X(2] -M``"XO```N+P``.`[``#@.P#`C3P`P(T\`,"EO`#`I;P```([```".P``/#L` -M`#P[`(`2O0"`$KT`@/&\`(#QO`"`G#T`@)P]`,!,/0#`3#T``%H[``!:.P`` -MNSP``+L\`,``/0#``#T`H$2]`*!$O0"`0+T`@$"]`(`3O0"`$[T`8&\]`&!O -M/0#@)3T`X"4]`"`VO0`@-KT`@+P\`("\/`#PACT`\(8]`,#,O0#`S+T`D,6] -M`)#%O0``*#H``"@Z``#2NP``TKL``)D\``"9/`!@S#T`8,P]`("*/0"`BCT` -M@+2\`("TO`#`(;T`P"&]```OO0``+[T``*N[``"KNP"`8;P`@&&\```>.P`` -M'CL`D(@]`)"(/0!@1ST`8$<]`$`XO0!`.+T``+H\``"Z/`"`+;P`@"V\`("V -MO0"`MKT`H+6]`*"UO0#@?3T`X'T]`(@(/@"("#X`@.,\`(#C/`"@Y;T`H.6] -M```8O```&+P``)@[``"8.P#`)[T`P">]`,`C/0#`(ST`4-L]`%#;/0!@0KT` -M8$*]`(!-O0"`3;T``$`]``!`/0"`#+P`@`R\`/"PO0#PL+T`0%V]`$!=O0!` -M^CP`0/H\`'`)/@!P"3X`X)8]`."6/0!`/[T`0#^]`$#PO`!`\+P`T,N]`-#+ -MO0!0N[T`4+N]`%"M/0!0K3T`,`T^`#`-/@#@*;T`X"F]`(`5O0"`%;T`@)L] -M`(";/0!@<3T`8'$]`%`!O@!0`;X`ST``'L]`,`#/0#``ST`X(>]`."'O0!`.KT`0#J]`&`\/0!@ -M/#T``%:[``!6NP```3P```$\`(!-/`"`33P`X!@]`.`8/0#`)CT`P"8]`*!U -MO0"@=;T`H'^]`*!_O0"@6CT`H%H]``"X.P``N#L``"N]```KO0!`JCT`0*H] -M`,"]/`#`O3P`X`V^`.`-O@`@:+T`(&B]`!"./0`0CCT`@/>\`(#WO```$;T` -M`!&]`,`:/0#`&CT`@.<]`(#G/0#PACT`\(8]`,"]``!WO0#@ -M)+T`X"2]`("+O0"`B[T`0"`]`$`@/0#PJ#T`\*@]`*`0/0"@$#T``-&\``#1 -MO`#`C#P`P(P\`(!6/`"`5CP`L)*]`+"2O0"`$+X`@!"^``#:NP``VKL`H*T] -M`*"M/0``P3L``,$[``"1NP``D;L`(-,]`"#3/0``DKP``)*\`*`$O@"@!+X` -MX"$]`.`A/0`0LST`$+,]``!:NP``6KL``$8[``!&.P#`O#P`P+P\``",.P`` -MC#L``(&\``"!O`#`K[T`P*^]`(`>O`"`'KP`8$L]`&!+/0"`'3P`@!T\`(`` -MO`"``+P`X$<]`.!'/0``8;T``&&]`,#IO0#`Z;T`@,Z\`(#.O```.CT``#H] -M``",.@``C#H`$(0]`!"$/0"`\#T`@/`]`,!7/0#`5ST`L/*]`+#RO0#0L[T` -MT+.]```M/0``+3T``!@[```8.P``-+T``#2]`$!Z/0!`>CT`('@]`"!X/0`` -ME3P``)4\`,#'O`#`Q[P`@*V]`("MO0`@;;T`(&V]`(!_O`"`?[P``&2[``!D -MNP"@B3T`H(D]`/";/0#PFST`@'J]`(!ZO0#@!KT`X`:]`,!5/0#`53T`@+*\ -M`("RO`!@$+T`8!"]`""G/0`@IST``*"[``"@NP`@1+T`($2]`(!>/`"`7CP` -M0!`]`$`0/0#`![T`P`>]`(!PO0"`<+T`@&N\`(!KO`#0K#T`T*P]`$#;/`!` -MVSP``-R\``#]`$"I -M/`!`J3P`L(<]`+"'/0"`UCP`@-8\```8.@``&#H`@.,\`(#C/`"`D;P`@)&\ -M`*#BO0"@XKT`@):\`("6O`"@&#T`H!@]`(!RO0"`[T`8'N]`&!=O0!@7;T``+PZ``"\.@"` -M2#P`@$@\`"!+/0`@2ST`4)T]`%"=/0``ACL``(8[`&#%O0!@Q;T`@#@\`(`X -M/`!@!CT`8`8]`""\ -M``"WO`"PF;T`L)F]`,"PO`#`L+P``':\``!VO`!`D3P`0)$\`(#C/0"`XST` -M$)P]`!"\``"'O`!PQ3T`<,4]`.`G/0#@)ST``,<\ -M``#'/`"@R#T`H,@]`.`'O0#@![T`,.F]`##IO0"`83P`@&$\`$`[/0!`.ST` -MH)F]`*"9O0"`QKP`@,:\`$!2/0!`4CT`P$`]`,!`/0``Z+P``.B\`.!%O0#@ -M1;T`('.]`"!SO0``<[T``'.]`("RO0"`LKT`0'\]`$!_/0"(%#X`B!0^`("X -M/`"`N#P`@&"]`(!@O0!@:ST`8&L]`&`8/0!@&#T`4(B]`%"(O0#`CKT`P(Z] -M`.!S/0#@O0``SCL``,X[`$#+/0!`RST`H&\]`*!O -M/0``T+P``-"\`,"H/`#`J#P`<(D]`'")/0!PR[T`<,N]``#=O0``W;T``#P\ -M```\/`"`!3T`@`4]``"(O```B+P`0&(]`$!B/0!`.CT`0#H]`$!FO0!`9KT` -M8/6]`&#UO0"@&KT`H!J]``"O/```KSP``$4\``!%/```^#P``/@\`'#)/0!P -MR3T`0`,]`$`#/0#`.+T`P#B]``#&NP``QKL`0).\`$"3O```7[T``%^]``"L -MO```K+P`@#X]`(`^/0#`=#T`P'0]``#ST```\\ -M```//`!0X;T`4.&]`'"VO0!PMKT`((D]`"")/0"@H#T`H*`]`&`]O0!@/;T` -M0(P\`$",/```O#T``+P]`,"KO`#`J[P`P,2]`,#$O0!@2[T`8$N]`&!\O0!@ -M?+T```:\```&O`#@"3T`X`D]`&`NP"`);P`@"6\`.!ZO0#@>KT`(``]`"`` -M/0"`L#T`@+`]`("GO`"`I[P`D(&]`)"!O0``6#P``%@\`&"+O0!@B[T`8-:] -M`O0"`O3P`@+T\`!#2/0`0TCT`@.<\`(#G/```2KL``$J[`$#6/`!`UCP` -M`&@[``!H.P`@OKT`(+Z]`,#:O`#`VKP`0+H]`$"Z/0!`JSP`0*L\`*!/O0"@ -M3[T`@-@\`(#8/`!`TSP`0-,\`"")O0`@B;T`(*6]`""EO0``E[L``)>[`&"( -M/0!@B#T`0.8\`$#F/`#`W[P`P-^\`(#'/`"`QSP`@#X\`(`^/`"0^+T`D/B] -M`&`*O0!@"KT`P+L]`,"[/0#`F#P`P)@\```4NP``%+L`@+(]`("R/0"`1KP` -M@$:\`$#BO0!`XKT`@'V]`(!]O0"`]#P`@/0\``"`.@``@#H`@/.\`(#SO`"@ -M&3T`H!D]`+#W/0"P]ST``)`Z``"0.@"@T+T`H-"]`$`0O0!`$+T`@'T\`(!] -M/`!@([T`8".]`(`X/`"`.#P`8*X]`&"N/0#`&3T`P!D]`/"2O0#PDKT`X'&] -M`.!QO0#`83T`P&$]``"A.P``H3L`(-N]`"#;O0"`";P`@`F\`$"D/0!`I#T` -M`'Z\``!^O```0KL``$*[`(#]/`"`_3P`($N]`"!+O0"PA+T`L(2]`&"&/0!@ -MACT`P+\]`,"_/0#`D#P`P)`\`"".O0`@CKT`0)B\`$"8O`"@3#T`H$P]``#I -MO```Z;P``.B]``#HO0#`PCP`P,(\`#"./0`PCCT``#^\```_O`"`*+P`@"B\ -M`,#4/`#`U#P`@%&]`(!1O0!PO;T`<+V]`*``O0"@`+T`P'D]`,!Y/0"@(3T` -MH"$]`,"G/`#`ISP`T*X]`-"N/0"`0#T`@$`]`""GO0`@I[T`P'>]`,!WO0"` -M?SP`@'\\`$"DO`!`I+P``%`[``!0.P!`B#T`0(@]`$`(/0!`"#T`@'6]`(!U -MO0!PK+T`<*R]`(#;O`"`V[P`0#T]`$`]/0"`6+T`@%B]`,!DO0#`9+T`H+D] -M`*"Y/0`PJCT`,*H]`+""O0"P@KT`0`*]`$`"O0``^SL``/L[`(`;O`"`&[P` -M`-X\``#>/`!PPCT`<,(]`*`D/0"@)#T`0&.]`$!CO0!@E+T`8)2]``"L.P`` -MK#L``(0Z``"$.@"PAKT`L(:]``#8N@``V+H`L*4]`+"E/0``K#H``*PZ`"!= -MO0`@7;T``'2\``!TO`#`W[P`P-^\`&!UO0!@=;T`H`R]`*`,O0#@MCT`X+8] -M`'#X/0!P^#T``/J[``#ZNP!@+[T`8"^]`&!Y/0!@>3T`@+:\`("VO`!`W+T` -M0-R]``!*.P``2CL`0*8]`$"F/0``UKL``-:[`(!'O`"`1[P``,D[``#).P`` -M++P``"R\`"!+O0`@2[T`8$*]`&!"O0``LKL``+*[`,"#/`#`@SP`P.Z\`,#N -MO`!@)#T`8"0]`#"C/0`PHST`0!:]`$`6O0#0G+T`T)R]```F/0``)CT`D)4] -M`)"5/0"`%[P`@!>\`"`*O0`@"KT`@/@\`(#X/`"`]CP`@/8\`&"0O0!@D+T` -M8$^]`&!/O0#`#ST`P`\]``!9O```6;P`H%:]`*!6O0#0D#T`T)`]`#"#/0`P -M@ST`0$J]`$!*O0`@A[T`((>]`(#>O`"`WKP`@(J\`("*O`#`@CP`P((\`.`, -M/0#@##T`P*H]`,"J/0"@*#T`H"@]`*!-O0"@3;T`0)V\`$"=O`"`;#P`@&P\ -M`'#8O0!PV+T`8(V]`&"-O0`PT#T`,-`]`-"I/0#0J3T`X#"]`.`PO0``8+P` -M`&"\`(`:/0"`&CT``-.\``#3O`!@Q+T`8,2]`$""O`!`@KP`,+`]`#"P/0`` -MW3P``-T\`$"WO`!`M[P`H`T]`*`-/0``*[P``"N\`*"UO0"@M;T``%`Z``!0 -M.@`@FCT`()H]`(#S/`"`\SP`("V]`"`MO0#`M[P`P+>\`$"E/`!`I3P`(`6] -M`"`%O0#`Q;T`P,6]`,#Y/`#`^3P`(+8]`""V/0"`3[P`@$^\`("]/`"`O3P` -MT.4]`-#E/0!`;;T`0&V]`!`EO@`0);X`@%>\`(!7O```OST``+\]``#[/``` -M^SP`P,.\`,##O`"`6SP`@%L\`(!B/0"`8CT`8`N]`&`+O0#PL+T`\+"]``![ -MO```>[P``$J\``!*O``@/KT`(#Z]`*`X/0"@.#T`(,$]`"#!/0``"+H```BZ -M``!'O0``1[T``'$\``!Q/`!@)ST`8"<]`&`CO0!@([T`0#&]`$`QO0``IST` -M`*<]`$"&/0!`ACT`T+*]`-"RO0!`U;P`0-6\``!L/0``;#T`X%6]`.!5O0#` -MG[T`P)^]`$`]/0!`/3T`0!@]`$`8/0`@!+T`(`2]```^O0``/KT``!<\```7 -M/`!`TCP`0-(\```.O0``#KT`8`J]`&`*O0"@LCT`H+(]`&",/0!@C#T``!:[ -M```6NP`@+3T`("T]`(`>/`"`'CP`(/:]`"#VO0"`9[T`@&>]`("=/0"`G3T` -M0#(]`$`R/0``.KT``#J]``!RNP``]`-"_O0#0O[T``'BZ``!XN@``B+H``(BZ`&`?O0!@'[T```@[ -M```(.P!`.ST`0#L]`(!^/`"`?CP``,B\``#(O```H[L``*.[````.@```#H` -M(-Z]`"#>O0"0I[T`D*>]`%"E/0!0I3T`,)P]`#"]`.#GO0``Q;P``,6\`(#&/`"`QCP``$B]``!( -MO0"`K[P`@*^\`&!N/0!@;CT`L*8]`+"F/0``&#H``!@Z``!2O0``4KT`@%^\ -M`(!?O`!`FKP`0)J\`,#)O0#`R;T`P*4\`,"E/`#8$3X`V!$^`,#X/`#`^#P` -M$(F]`!")O0``H#L``*`[``!@.P``8#L`P#:]`,`VO0#`V;P`P-F\`,"U/`#` -MM3P``$D\``!)/`#`C;P`P(V\``!2/```4CP`P%0]`,!4/0#`-;T`P#6]`."\ -MO0#@O+T`P&4]`,!E/0"@Y#T`H.0]``"4NP``E+L``$2]``!$O0``PCL``,([ -M``"WNP``M[L`0(>]`$"'O0!`/;T`0#V]`/"C/0#PHST`,(8]`#"&/0``1KT` -M`$:]``!^O```?KP``!@]```8/0"0G+T`D)R]`&"1O0!@D;T`(`P]`"`,/0#` -M\SP`P/,\``")NP``B;L`X'T]`.!]/0"`F#T`@)@]``!4/```5#P`0)*]`$"2 -MO0`@-KT`(#:]`,#!/`#`P3P`@%6\`(!5O`#@$;T`X!&]`$"]`+"2O0"PDKT` -MH+`]`*"P/0#@/ST`X#\]`,`BO0#`(KT`P!T]`,`=/0"`5#T`@%0]`("HO0"` -MJ+T`T(V]`-"-O0"`-3P`@#4\`$"`/`!`@#P`P,V\`,#-O```/#L``#P[`*"- -M/0"@C3T`0$,]`$!#/0``;+T``&R]`.`2O0#@$KT``+T[``"].P"@7+T`H%R] -M`$"9O`!`F;P`X-<]`.#7/0``D3T``)$]`,`,O0#`#+T``'N\``![O````ST` -M``,]`$"LO`!`K+P`D,R]`)#,O0"`BKP`@(J\`/"Q/0#PL3T`@"(\`(`B/``` -M6KT``%J]`,"!/0#`@3T`H#`]`*`P/0#`?KT`P'Z]`.`2O0#@$KT`@`4]`(`% -M/0``F3L``)D[``"R.P``LCL`0+\\`$"_/`!`"ST`0`L]```@O0``(+T`4)Z] -M`%">O0"`XCP`@.(\`'"O/0!PKST``!R]```\``#6NP`` -MUKL`0+(]`$"R/0#@RCT`X,H]`("$O0"`A+T`0/F]`$#YO0``]#H``/0Z`*`: -M/0"@&CT`0.Z\`$#NO`#`KCP`P*X\`&!6/0!@5CT``.X[``#N.P!`&+T`0!B] -M`("UO`"`M;P`0*.\`$"CO`"@)KT`H":]`"`8O0`@&+T`0+D]`$"Y/0``S3T` -M`,T]`,#KO`#`Z[P`@.V\`(#MO```=CT``'8]`(#@O`"`X+P`8'N]`&![O0"` -M+CT`@"X]`)"-/0"0C3T`@%N\`(!;O``@,[T`(#.]`(`UO`"`-;P``#P[```\ -M.P"`5+T`@%2]`*`\O0"@/+T``'L]``![/0!@*3T`8"D]```GO0``)[T``/B[ -M``#XNP`@`#T`(``]`,#NO`#`[KP``*F\``"IO`"`)#T`@"0]`."1/0#@D3T` -M@.@\`(#H/`#`KCP`P*X\`.`,/0#@##T`@/"\`(#PO``@^KT`(/J]`(!&O`"` -M1KP`L)T]`+"=/0"`8;P`@&&\`,!3O0#`4[T``$`]``!`/0"`\#P`@/`\`(`W -MO0"`-[T`('>]`"!WO0"`B[P`@(N\``"'/```ASP``*@[``"H.P`@!CT`(`8] -M`"#3/0`@TST`@/X\`(#^/`!@B+T`8(B]`(!N/`"`;CP``%(]``!2/0#`&[T` -MP!N]```0O0``$+T`X#X]`.`^/0"`?#P`@'P\```NO0``+KT```N]```+O0`` -M@CP``((\`(#SO`"`\[P`$(:]`!"&O0``X#P``.`\`'"Z/0!PNCT`P`>]`,`' -MO0!@4[T`8%.]`,`G/0#`)ST`@.T\`(#M/`!@.;T`8#F]```".P```CL`(,(] -M`"#"/0"0OST`D+\]`$"XO`!`N+P`(%R]`"!]`*`7O0!@N;T`8+F]`,"5O`#`E;P``&$]``!A/0``6#T``%@]``#L -M.P``[#L`@%L]`(!;/0`@0ST`($,]`/"!O0#P@;T`@&J]`(!JO0"@A3T`H(4] -M`,#[/`#`^SP`8$*]`&!"O0"`![P`@`>\`&!"/0!@0CT``%V\``!=O``0I+T` -M$*2]`.`#O0#@`[T`P#H]`,`Z/0``9#L``&0[```TNP``-+L`D(L]`)"+/0`` -MB3L``(D[`""SO0`@L[T``$"Z``!`N@#`8CT`P&(]`,")O`#`B;P``'Z[``!^ -MNP`PD3T`,)$]`+""/0"P@CT`X!2]`.`4O0!`I+T`0*2]```NNP``+KL``,X\ -M``#./``PAKT`,(:]`(!`O`"`0+P`4,8]`%#&/0``JCP``*H\`.!VO0#@=KT` -M`'D\``!Y/`#`OSP`P+\\`"!?O0`@7[T``"F]```IO0"`:ST`@&L]`,"%/0#` -MA3T``*2\``"DO`!`O[P`0+^\`&`]/0!@/3T``#&\```QO`#PD[T`\).]``"# -M/```@SP`8"P]`&`L/0"`![T`@`>]`(`$O`"`!+P`@',]`(!S/0"`?3P`@'T\ -M`-"1O0#0D;T`8$Z]`&!.O0"@DCT`H)(]`%"+/0!0BST`X$"]`.!`O0``Y[L` -M`.>[`'"5/0!PE3T`0..\`$#CO`#`H+T`P*"]``!B/```8CP`P+$\`,"Q/`!` -MJ;P`0*F\`("]/`"`O3P``%$]``!1/0``^KL``/J[`+":O0"PFKT`@"6]`(`E -MO0"`%ST`@!<]```] -M`&`WO0`@*KT`("J]``!F/```9CP`@(J\`("*O`"`,;T`@#&]`,#'/`#`QSP` -MT+4]`-"U/0"`5SP`@%<\`*"TO0"@M+T`0/N\`$#[O`"`U3P`@-4\`.!&O0#@ -M1KT`P`2]`,`$O0!`?3T`0'T]`,`*/0#`"CT```*\```"O`#`S3P`P,T\```Z -M/```.CP`P/*\`,#RO`!`BKP`0(J\`$!5/0!`53T`\)T]`/"=/0``,+P``#"\ -M`,!TO0#`=+T`@)8\`("6/```4#H``%`Z`*"GO0"@I[T``*"\``"@O`!`=ST` -M0'<]``#].P``_3L`0/^\`$#_O`"`.3P`@#D\`("*/`"`BCP`0$6]`$!%O0`@ -M=+T`('2]`(!2/0"`4CT`8+0]`&"T/0``\#D``/`Y``"K.P``JSL``)(]``"2 -M/0``P#@``,`X`$![O0!`>[T`@*2\`("DO```J#L``*@[```_O```/[P``(BZ -M``"(N@``Z#P``.@\`,#0/`#`T#P`8&Z]`&!NO0`@?;T`('V]`,`0/0#`$#T` -M@%(\`(!2/`#`B;T`P(F]``#?/```WSP`,,T]`##-/0"`'SP`@!\\`*`KO0"@ -M*[T``"P\```L/`!``#T`0``]``!XNP``>+L``$R\``!,O`"@'CT`H!X]`,`3 -M/0#`$ST`@$2]`(!$O0`@*+T`("B]``!7/```5SP``/V\``#]O``@2;T`($F] -M`.`./0#@#CT`P&P]`,!L/0"`*#P`@"@\`$#O`"`?3T`@'T]`(#5/`"`U3P` -M,(^]`#"/O0``(+T``""]`.`5/0#@%3T`@.@\`(#H/```X;P``.&\```T/``` -M-#P`8$<]`&!'/0"`#;T`@`V]`.":O0#@FKT`X"$]`.`A/0#`23T`P$D]```H -MO0``*+T``&Z[``!NNP#@I#T`X*0]`(#:/`"`VCP`X&:]`.!FO0`@/``` -M@CL``(([`-"&O0#0AKT``%P[``!<.P!@>#T`8'@]``##O```P[P``&2]``!D -MO0#`FCP`P)H\```^O```/KP`@(:]`("&O0``,;P``#&\``"0/0``D#T`H#@] -M`*`X/0#`([T`P".]`$#%O`!`Q;P``(8]``"&/0#`G#P`P)P\`.!3O0#@4[T` -M0"(]`$`B/0#`JCT`P*H]``#KNP``Z[L`P`6]`,`%O0``J#L``*@[`&`MO0!@ -M+;T`H)&]`*"1O0"`*KP`@"J\`.!+/0#@2ST``,8[``#&.P#`[[P`P.^\``#5 -M/```U3P`P$0]`,!$/0``:;T``&F]`&!ZO0!@>KT`X``]`.``/0#`-CT`P#8] -M``"*.P``BCL`8#`]`&`P/0`@?ST`('\]``#<.P``W#L`$(R]`!",O0!`^;P` -M0/F\`$`%/0!`!3T`0)B\`$"8O`#@6KT`X%J]`.`D/0#@)#T`X$P]`.!,/0!@ -M(KT`8"*]`(#DO`"`Y+P``+([``"R.P"`RKP`@,J\`(`=O`"`';P``%X]``!> -M/0"@.CT`H#H]``!(O```2+P`0">]`$`GO0``K3P``*T\``#\/```_#P`H`N] -M`*`+O0"`9;P`@&6\`*"E/0"@I3T``-(\``#2/`#`:[T`P&N]`,`@O0#`(+T` -M`*"Z``"@N@`@";T`(`F]``"XO```N+P`@$8]`(!&/0!@H#T`8*`]``"4N@`` -ME+H`0,N\`$#+O`"`Q3P`@,4\``";O```F[P`D*F]`)"IO0``-CL``#8[`("3 -M/0"`DST``.P\``#L/````SP```,\`("-/`"`C3P``#^\```_O```;+T``&R] -M`(`]O0"`/;T`P+L\`,"[/```'ST``!\]`,`'O0#`![T``#4\```U/`#@ICT` -MX*8]```>.P``'CL`H*"]`*"@O0``*3P``"D\`*!I/0"@:3T`@!,\`(`3/`!` -MM+P`0+2\``",/```C#P```P\```,/``@+;T`("V]`.`*O0#@"KT`X!X]`.`> -M/0``33P``$T\`$#YO`!`^;P``+<\``"W/`"`(ST`@",]`&`JO0!@*KT`H&"] -M`*!@O0``[SL``.\[`$#P/`!`\#P`@%@\`(!8/`"`WCP`@-X\`$"A/0!`H3T` -M0!<]`$`7/0#@<;T`X'&]`(`1O0"`$;T`@.@\`(#H/```/;T``#V]`$!AO0!` -M8;T`P$@]`,!(/0#0B#T`T(@]`(!^O`"`?KP``-"\``#0O```J;L``*F[`,"# -MO`#`@[P`0$2]`$!$O0"`*[P`@"N\`$`W/0!`-ST`(`4]`"`%/0``%CL``!8[ -M`(!3/0"`4ST`(`T]`"`-/0!`,;T`0#&]`$">O`!`GKP``&H]``!J/0#`USP` -MP-<\`.`7O0#@%[T``*J\``"JO`#`ISP`P*<\``"VO```MKP`D)6]`)"5O0`` -M*3P``"D\`&!Z/0!@>CT`@&F\`(!IO`#`O[P`P+^\`$!+/0!`2ST``,&[``#! -MNP"0E+T`D)2]`,""O`#`@KP`((`]`""`/0``23T``$D]`,#D/`#`Y#P`@#H] -M`(`Z/0"@83T`H&$]```4O0``%+T`0*V]`$"MO0"`3+P`@$R\``!'/```1SP` -M(!Z]`"`>O0"`L3P`@+$\`""6/0`@ECT``%N\``!;O``PE[T`,)>]``#+NP`` -MR[L`8`P]`&`,/0!@![T`8`>]`(!$O0"`1+T`@'$]`(!Q/0!`O#T`0+P]`(`6 -MO`"`%KP`0+V\`$"]O``@%CT`(!8]```B/```(CP`P+2\`,"TO``@!CT`(`8] -M`$`3/0!`$ST`0.6\`$#EO`"@:[T`H&N]``#9NP``V;L`P*0\`,"D/`#`TKP` -MP-*\`$#WO`!`][P`(&4]`"!E/0#@!ST`X`<]`"`HO0`@*+T``!"Z```0N@#` -MS#P`P,P\`.!3O0#@4[T`@`2]`(`$O0``ECT``)8]`%"E/0!0I3T``!P[```< -M.P"`8;P`@&&\`&`//0!@#ST`@$8\`(!&/`#0DKT`T)*]`*!5O0"@5;T`P.(\ -M`,#B/`"`5#P`@%0\``#D.@``Y#H`0#H]`$`Z/0#`M#P`P+0\`.!*O0#@2KT` -MX!&]`.`1O0!`BCP`0(H\`(`A/`"`(3P``!.]```3O0``<#P``'`\`("M/0"` -MK3T`0(P\`$",/`#@7;T`X%V]`$"!/`!`@3P`@`X]`(`./0!@#+T`8`R]``#O -MNP``[[L`(&$]`"!A/0``MCL``+8[`*!IO0"@:;T`0,N\`$#+O`#@"#T`X`@] -M``!B/```8CP`P.:\`,#FO`"`X#P`@.`\`*"]`(!DO`"`9+P```:\```&O`#`R[P`P,N\`*`0/0"@$#T`X*4]`."E/0`` -MOSP``+\\`$`?O0!`'[T``!J\```:O```CCP``(X\``!/0"@'CT``&"\ -M``!@O`!`8[T`0&.]``!9O```6;P`H!<]`*`7/0"`3#P`@$P\`*`*O0"@"KT` -M`*8[``"F.P"@"ST`H`L]`.`#O0#@`[T``/V\``#]O```,#T``#`]`,">/`#` -MGCP`P/2\`,#TO```,3P``#$\``#S/```\SP`@):\`("6O```U+P``-2\`.`" -M/0#@`CT``%(]``!2/0``-CL``#8[``!8.P``6#L`(%`]`"!0/0``##L```P[ -M`/"VO0#PMKT`H$^]`*!/O0``MCP``+8\``#E.P``Y3L``"Z\```NO`"`U#P` -M@-0\```K/0``*ST``#J[```ZNP"@&+T`H!B]``#TN@``]+H``/`Z``#P.@#` -M(KT`P"*]``"+/```BSP`(,(]`"#"/0"@%ST`H!<]`,"2O`#`DKP`0*,\`$"C -M/```0SP``$,\`&!"O0!@0KT`0"&]`$`AO0"`F#P`@)@\`,"F/`#`ICP``.6\ -M``#EO`!`O;P`0+V\``#J/```ZCP``".\```CO`#@>;T`X'F]`,")/`#`B3P` -ML)H]`+":/0``*CL``"H[`&``O0!@`+T`@+X\`("^/``@`#T`(``]`,#$O`#` -MQ+P`@(Z\`(".O`"`7#T`@%P]`*!@/0"@8#T``,^[``#/NP``6#L``%@[```` -M.P```#L``*&]``"AO0"PH;T`L*&]``"^/```OCP`@"P]`(`L/0``[;L``.V[ -M```./```#CP`($4]`"!%/0``@3P``($\`&!KO0!@:[T`8"R]`&`LO0#@$#T` -MX!`]`(#D/`"`Y#P``*2[``"DNP#@;CT`X&X]`"!]/0`@?3T`H!&]`*`1O0"@ -M2[T`H$N]`$"-/`!`C3P`@"@\`(`H/`#@![T`X`>]`,"/O`#`C[P`0-$\`$#1 -M/```+CL``"X[`$`"O0!``KT``!"Z```0N@"`LSP`@+,\`$"6O`!`EKP``"8[ -M```F.P"`@CT`@((]`(";/`"`FSP`P%6]`,!5O0"`?+P`@'R\`&`E/0!@)3T` -M`#`\```P/`"`_+P`@/R\`(!E/`"`93P`,(,]`#"#/0``T#D``-`Y`&!3O0!@ -M4[T``(^[``"/NP"`%[P`@!>\`*!FO0"@9KT``,4[``#%.P``CST``(\]`,#( -M/`#`R#P``%J\``!:O`#`Q3P`P,4\`,#9/`#`V3P`@`6]`(`%O0#`;[T`P&^] -M``"#.P``@SL`0!$]`$`1/0``B+H``(BZ`(!F/`"`9CP`H%0]`*!4/0``DKP` -M`)*\`(".O0"`CKT````Y````.0!@+#T`8"P]``"$O```A+P```J]```*O0`` -M(ST``",]`,"!/0#`@3T`P)B\`,"8O``@*;T`("F]`(#R/`"`\CP`P-T\`,#= -M/`!`D[P`0).\`,"9/`#`F3P`@`X]`(`./0#@(+T`X""]``!ZO0``>KT``-2[ -M``#4NP#`E#P`P)0\``#1NP``T;L``'8[``!V.P#@5#T`X%0]`,`!/0#``3T` -MP$N]`,!+O0"`!KT`@`:]`(#D/`"`Y#P``/R[``#\NP#`C;P`P(V\`#"5/0`P -ME3T``*H]``"J/0``OKL``+Z[`,`7O0#`%[T``!R[```\`(#GO```.+H``#BZ -M`(`F/`"`)CP`P/B\`,#XO`"`/KP`@#Z\`.`H/0#@*#T`0/P\`$#\/```"KL` -M``J[`(!Y/`"`>3P`P#<]`,`W/0``ECL``)8[```EO0``);T``(<\``"'/`!@ -M;3T`8&T]`("7O`"`E[P`@#N]`(`[O0"`+3P`@"T\`(`0O`"`$+P`X(F]`.") -MO0!`][P`0/>\`(#S/`"`\SP`0,<\`$#'/```%KL``!:[`(#"/`"`PCP`P"T] -M`,`M/0"`8;P`@&&\`.`PO0#@,+T`P+4\`,"U/`#`,3T`P#$]````.````#@` -M`*@\``"H/`!`?3T`0'T]``"!/```@3P`("V]`"`MO0#`!;T`P`6]`(!0O`"` -M4+P`P#.]`,`SO0!@([T`8".]`*`M/0"@+3T``%4]``!5/0`@*KT`("J]```> -MO0``'KT`@"@]`(`H/0#`C3P`P(T\`.`+O0#@"[T``-X[``#>.P#`<3T`P'$] -M`.`=/0#@'3T``%@Z``!8.@``*#L``"@[```9/```&3P`0->\`$#7O`#`KKP` -MP*Z\`$`T/0!`-#T`@`8]`(`&/0`@.KT`(#J]`&`!O0!@`;T``,<[``#'.P`` -M\[P``/.\`&`SO0!@,[T`0)\\`$"?/`"@33T`H$T]`,"%/`#`A3P```H[```* -M.P`@#CT`(`X]``!:.P``6CL`L(.]`+"#O0"`DKP`@)*\`)"1/0"0D3T`(#\] -M`"`_/0``]+L``/2[```H/```*#P`P)8\`,"6/`#`WKP`P-Z\`,!9O0#`6;T` -M`*"\``"@O`"`83P`@&$\```(.P``"#L``"P\```L/``@-CT`(#8]``",NP`` -MC+L`@&N]`(!KO0``$CL``!([`(`S/0"`,ST`@$F\`(!)O```4KP``%*\`*`M -M/0"@+3T`0!`]`$`0/0!`O[P`0+^\``"XO```N+P`@(L\`("+/```Z3L``.D[ -M`,#)O`#`R;P`0)P\`$"O0``K+P``*R\`.`Z/0#@.CT`@`P]`(`,/0``][L``/>[`("9/`"`F3P` -MX!@]`.`8/0`@&;T`(!F]``!FO0``9KT``),\``"3/`"`_#P`@/P\`,"&O`#` -MAKP``*`Z``"@.@!`_#P`0/P\``#.NP``SKL`X$2]`.!$O0"`)KP`@":\`,`` -M/0#``#T``&([``!B.P``I#H``*0Z`"!%/0`@13T`@)<\`("7/`#`4+T`P%"] -M``"*O```BKP`8!@]`&`8/0"`,3P`@#$\`(!HO`"`:+P`0.P\`$#L/`"`1ST` -M@$<]``!6O```5KP`4)*]`%"2O0!`X;P`0.&\`(!%/`"`13P``(V\``"-O``` -MD+D``)"Y`*!J/0"@:CT`@(@\`("(/``@"KT`(`J]``"VNP``MKL`@#`\`(`P -M/`!`N;P`0+F\`(``O`"``+P`P%@]`,!8/0#`:3T`P&D]``"QNP``L;L``):\ -M``"6O```DSP``),\``!,O```3+P`P%J]`,!:O0``0CL``$([`.`[/0#@.ST` -M`/V[``#]NP!`%+T`0!2]``"W.P``MSL`@#D\`(`Y/`#@([T`X".]```(O0`` -M"+T`0"T]`$`M/0!`1ST`0$<]`,"1O`#`D;P``/`Y``#P.0#@,3T`X#$]``!* -MNP``2KL`H`.]`*`#O0!`L#P`0+`\`$#I/`!`Z3P``*Z[``"NNP``G3L``)T[ -M`(#>/`"`WCP`@'*\`(!RO`#@B;T`X(F]`$`%O0!`!;T`(!<]`"`7/0``_3L` -M`/T[`$`.O0!`#KT`0*L\`$"K/``@33T`($T]`$"BO`!`HKP`X"V]`.`MO0`` -M!SP```<\`$"5/`!`E3P``+6[``"UNP``LSP``+,\`%"&/0!0ACT`@,$\`(#! -M/`"@,KT`H#*]`("\O`"`O+P`@#\\`(`_/`!`W[P`0-^\`,#2O`#`TKP`0.X\ -M`$#N/`#`Q3P`P,4\``"*O```BKP`@!2\`(`4O```##L```P[`$#+O`!`R[P` -M@!J]`(`:O0``M3P``+4\`("&/0"`ACT``-\[``#?.P!`*+T`0"B]`,"1/`#` -MD3P`0/H\`$#Z/`!`\KP`0/*\``"8O```F+P`H"(]`*`B/0!`SCP`0,X\```F -MO```)KP``+N[``"[NP``M;L``+6[`,`JO0#`*KT`0"&]`$`AO0!``ST`0`,] -M`&!:/0!@6CT``->[``#7NP"`5KP`@%:\``#%/```Q3P``"2\```DO`"@3+T` -MH$R]``!+O```2[P`0-P\`$#SP`@'L\`*`OO0"@+[T` -MP->\`,#7O`#`Y#P`P.0\``#`N```P+@`P-V\`,#=O```M#P``+0\`$`F/0!` -M)CT`@"J\`(`JO`#`X[P`P..\`(!3/`"`4SP`@&L\`(!K/`"`X+P`@."\``!< -MNP``7+L`L(H]`+"*/0#@#CT`X`X]`*`6O0"@%KT`P**\`,"BO```ASL``(<[ -M`(`@O0"`(+T`8#2]`&`TO0``Q#L``,0[`,#U/`#`]3P``-([``#2.P``J#H` -M`*@Z``!G/```9SP``#B\```XO``@2KT`($J]```2NP``$KL`8$<]`&!'/0`` -M;3P``&T\``"4.@``E#H`(%8]`"!6/0``"3T```D]```HO0``*+T`P".]`,`C -MO0"`33P`@$T\``!6/```5CP`@-Z\`(#>O```.+P``#B\``"C/```HSP`0,*\ -M`$#"O`#`<;T`P'&]``#XN@``^+H``.(\``#B/```;KL``&Z[``"Y.P``N3L` -M("\]`"`O/0"`=#P`@'0\`(#NO`"`[KP`@%&\`(!1O```!ST```<]`,":/`#` -MFCP``)RZ``"]``#\ -MNP``_+L``*^\``"OO`"`5KT`@%:]``#E.P``Y3L`@'$]`(!Q/0``7CP``%X\ -M`.`JO0#@*KT``$^\``!/O```UCP``-8\`(`5O`"`%;P`0+R\`$"\O`"@$3T` -MH!$]`"!+/0`@2ST``)$[``"1.P``1SP``$<\``#B/```XCP``(^\``"/O`"@ -M1;T`H$6]```PNP``,+L``+,\``"S/`!`HKP`0**\`,`\O0#`/+T``)$[``"1 -M.P``1CP``$8\`*`>O0"@'KT`0("\`$"`O`!`0CT`0$(]`$"(/`!`B#P``)^\ -M``"?O`#`ESP`P)<\`&`'/0!@!ST`0)B\`$"8O`#@%[T`X!>]`$"./`!`CCP` -MH%$]`*!1/0``&3P``!D\``!'O```1[P`@*,\`("C/```)KL``":[`&!LO0!@ -M;+T`@""]`(`@O0``C+L``(R[`$"@O`!`H+P``'@[``!X.P#@9CT`X&8]`(`H -M/0"`*#T`@/*\`(#RO`"@#KT`H`Z]`(!D/`"`9#P``.L[``#K.P"@$;T`H!&] -M``#C.P``XSL`(&H]`"!J/0``D#P``)`\`("GO`"`I[P``(<[``"'.P``2KL` -M`$J[`,`?O0#`'[T``*:\``"FO`"`!ST`@`<]``"Y/```N3P`@`Z]`(`.O0`` -M[[P``.^\`,#%/`#`Q3P``"<\```G/```H+P``*"\`(#&/`"`QCP`P!8]`,`6 -M/0``)[P``">\``"#O```@[P``,D[``#).P"`U[P`@->\`,!&O0#`1KT``(@[ -M``"(.P#0@3T`T($]`(#I/`"`Z3P``.:\``#FO```O+L``+R[`,"1/`#`D3P` -M@!*]`(`2O0#`1;T`P$6]``"(/```B#P``"$]```A/0``"SP```L\``"./``` -MCCP`0!P]`$`O`"`FCP`@)H\```% -MO```!;P``-F\``#9O```HSP``*,\`,"Z/`#`NCP`0,:\`$#&O`"`);P`@"6\ -M`$"4/`!`E#P``.R[``#LNP#`L;P`P+&\`,"J/`#`JCP`8"D]`&`I/0``J;L` -M`*F[`(`1O0"`$;T`P(P\`,",/``@.CT`(#H]``#UNP``];L`@*F\`("IO``` -MWSP``-\\```H/```*#P``">]```GO0!`";T`0`F]```;O```&[P``*^\``"O -MO`!`BKP`0(J\`(`#/0"``ST`($D]`"!)/0``U#H``-0Z`("GO`"`I[P`@!<\ -M`(`7/```$+P``!"\```6O0``%KT``$0\``!$/`"@<#T`H'`]`,#5/`#`U3P` -M`/`Z``#P.@"`K#P`@*P\`(`Z/`"`.CP`@$B]`(!(O0`@@KT`((*]``#/0`@7CT`@*<\`("G/`!`_KP`0/Z\ -M`$"BO`!`HKP``*N[``"KNP#`,[T`P#.]`.!'O0#@1[T`P-`\`,#0/`!@/3T` -M8#T]```D.P``)#L``."Z``#@N@!`W#P`0-P\```4.P``%#L`0-Z\`$#>O``` -M@;L``(&[`"`./0`@#CT`0)D\`$"9/```G+H``)RZ`,"B/`#`HCP`P(<\`,"' -M/`!`#[T`0`^]`("@O`"`H+P`8`4]`&`%/0``!SP```<\```;O0``&[T`@#"\ -M`(`PO`"`1SP`@$<\`$"LO`!`K+P`(!J]`"`:O0"`(3P`@"$\`.`W/0#@-ST` -M`(@\``"(/```!3P```4\`,`8/0#`&#T```\\```//`"`5[T`@%>]``#-O``` -MS;P`0`<]`$`'/0!`VCP`0-H\````.@```#H`@)D\`("9/`"`R#P`@,@\`(!+ -MO`"`2[P`0!6]`$`5O0``3;P``$V\`$"=O`!`G;P`P!6]`,`5O0"`<#P`@'`\ -M`/"&/0#PACT`@"<\`(`G/`"@$+T`H!"]``"+.P``BSL`(`(]`"`"/0``LKL` -M`+*[``"JO```JKP``%0\``!4/```T#P``-`\``#*NP``RKL``(^[``"/NP"` -MV3P`@-D\``".NP``CKL`H!^]`*`?O0"`&3P`@!D\`(`[/0"`.ST``)>[``"7 -MNP!`#[T`0`^]``#8NP``V+L``,4[``#%.P``9;P``&6\``!$.P``1#L`X!`] -M`.`0/0#`I#P`P*0\`(`ZO`"`.KP``)D\``"9/`!`K#P`0*P\`.`WO0#@-[T` -M(&Z]`"!NO0"`F3P`@)D\`.`U/0#@-3T``-0[``#4.P```````````(``/0"` -M`#T`@%P\`(!NP``7KL`X',]`.!S/0!@ -M(CT`8"(]`.`/O0#@#[T`X`*]`.`"O0"`;SP`@&\\`(!F/`"`9CP`@"J\`(`J -MO`"`B#P`@(@\```,/0``##T``$Z\``!.O`#@';T`X!V]``".NP``CKL`@&^\ -M`(!OO`!@/;T`8#V]```@NP``(+L`0$8]`$!&/0``RCP``,H\```LNP``++L` -M`),\``"3/```H3P``*$\``"6O```EKP`0+V\`$"]O`"@#CT`H`X]`.`Q/0#@ -M,3T`@`^\`(`/O```![P```>\`*`!/0"@`3T``*J[``"JNP!@2+T`8$B]`$"2 -MO`!`DKP``'P\``!\/`"`>+P`@'B\`,#1O`#`T;P``#0\```T/`!`J#P`0*@\ -M``"XO```N+P``'N\``![O``@!ST`(`<]`(#B/`"`XCP``%`Z``!0.@"`_#P` -M@/P\`*`;/0"@&ST`@$V\`(!-O`!`S;P`0,V\``!-/```33P``(8\``"&/`#` -MF;P`P)F\`(`6O`"`%KP`X`(]`.`"/0```3P```$\``!HO0``:+T`8!>]`&`7 -MO0"`"3P`@`D\``!OO```;[P`@,F\`(#)O`"@&CT`H!H]`,!C/0#`8ST``#`\ -M```P/`"`$;P`@!&\``![``#7NP"`A#P`@(0\`,"+/`#`BSP`8!N]`&`;O0"` -M9[T`@&>]``#6.P``UCL`P``]`,``/0``P+@``,"X`(`0/`"`$#P`P!H]`,`: -M/0``ZSL``.L[`,#JO`#`ZKP``"J[```JNP#`P3P`P,$\``#:NP``VKL`@)F\ -M`("9O``@"ST`(`L]`.`L/0#@+#T`0+.\`$"SO`!@$+T`8!"]`(!7/`"`5SP` -M`"$\```A/`#`@[P`P(.\``#@.0``X#D`@,$\`(#!/```4CL``%([`(!'O`"` -M1[P``(\[``"/.P``K3L``*T[`$#RO`!`\KP`@":\`(`FO`"@2ST`H$L]`,#W -M/`#`]SP`X`N]`.`+O0!`@+P`0("\``#'/```QSP``(*[``""NP``XKP``.*\ -M``!#/```0SP`(!$]`"`1/0``OCL``+X[```.NP``#KL`P.@\`,#H/```Y3L` -M`.4[`,!6O0#`5KT`0-&\`$#1O`!`*CT`0"H]``#^/```_CP``#B\```XO``` -M[+H``.RZ```H.P``*#L`@+B\`("XO```QKP``,:\`(`J/`"`*CP`@"0\`(`D -M/```;[P``&^\``!R/```/`!`WCP``!&\```1O`!`YKP`0.:\``#L/```[#P`8!\]`&`?/0!`I+P` -M0*2\`,#[O`#`^[P``"X[```N.P``+[P``"^\`,`\``#GO```UCP``-8\`,#&/`#`QCP`P/2\`,#TO`!`H[P`0*.\``"=/``` -MG3P``/8[``#V.P``F+L``)B[`(#Z/`"`^CP`@#D]`(`Y/0``&+P``!B\`.`F -MO0#@)KT``(D[``").P"`\SP`@/,\``")NP``B;L``(`Z``"`.@#`[#P`P.P\ -M``"DN@``I+H``/B\``#XO````+D```"Y```J.P``*CL`H!^]`*`?O0!`VKP` -M0-J\`$!$/0!`1#T`@',]`(!S/0"`,KP`@#*\`"`;O0`@&[T``*D[``"I.P`` -MHSL``*,[`("FO`"`IKP`@$$\`(!!/```*CT``"H]```2/```$CP`@"J\`(`J -MO`#`@3P`P($\```T/```-#P`(`B]`"`(O0"`[KP`@.Z\`(#A/`"`X3P`P/4\ -M`,#U/`"`2KP`@$J\``"@NP``H+L`@'@\`(!X/`"`I;P`@*6\`(#8O`"`V+P` -M0*\\`$"O/`#`U3P`P-4\`(![O`"`>[P```&\```!O`#`WCP`P-X\``#8.P`` -MV#L`P#6]`,`UO0!`BKP`0(J\`*`^/0"@/CT`(``]`"``/0``9KP``&:\``"P -M.P``L#L``*,\``"C/`"`C+P`@(R\``"^O```OKP``"8\```F/```_CL``/X[ -M`(!.O`"`3KP`P(0\`,"$/``@-ST`(#<]````N0```+D`0%2]`$!4O0``KKP` -M`*Z\`(!`/`"`0#P``%:\``!6O`"`-+P`@#2\`$`%/0!`!3T`0*P\`$"L/``` -M@+P``("\``#X.@``^#H`8`4]`&`%/0``BKL``(J[`$`OO0!`+[T``!8[```6 -M.P"`;ST`@&\]``"E/```I3P`P-*\`,#2O```4+H``%"Z`(`Z/`"`.CP`0**\ -M`$"BO```(KP``"*\`("0/`"`D#P``,^[``#/NP#@`;T`X`&]``"/NP``C[L` -M`',\``!S/`"`H;P`@*&\`(#^O`"`_KP`P,8\`,#&/`#`1ST`P$<]``"R.P`` -MLCL``-"Y``#0N0"`U#P`@-0\```LNP``++L``"R]```LO0``*+L``"B[`,`T -M/0#`-#T`0+,\`$"S/`"`.+P`@#B\`(!+/`"`2SP``'X\``!^/```*KT``"J] -M`(!TO0"`=+T``,Z[``#.NP``Y3L``.4[`(!PO`"`<+P``#H\```Z/`"@&#T` -MH!@]```@.P``(#L`P)V\`,"=O```'3P``!T\`$"T/`!`M#P``-^[``#?NP`` -M/KL``#Z[`&`I/0!@*3T`@.P\`(#L/`#`Y+P`P.2\`,"8O`#`F+P`P,4\`,#% -M/```K+H``*RZ`,#LO`#`[+P``)$[``"1.P"`>SP`@'L\`.`;O0#@&[T`P%*] -M`,!2O0``I#H``*0Z``"]/```O3P`@$V\`(!-O```W+H``-RZ`(`^/0"`/CT` -M0`@]`$`(/0``&+P``!B\```4.P``%#L``+L[``"[.P``RKP``,J\`(!'O`"` -M1[P`8"\]`&`O/0!`(3T`0"$]`(!VO`"`=KP`P,6\`,#%O```$#P``!`\`(!" -MO`"`0KP`(&*]`"!BO0"`P[P`@,.\``"-/```C3P``!*\```2O`"`4;P`@%&\ -M`"`L/0`@+#T`P!8]`,`6/0"`XKP`@.*\```!O0```;T`@.(\`(#B/`"@$#T` -MH!`]``#&NP``QKL``.N[``#KNP!`J#P`0*@\``"`N```@+@`0(6\`$"%O``` -M*SP``"L\``#Z.P``^CL```F]```)O0``=+P``'2\`$#D/`!`Y#P```@Z```( -M.@"`7KT`@%Z]``#RO```\KP`@.8\`(#F/`!`K3P`0*T\``"`NP``@+L`@.\\ -M`(#O/`"`&CT`@!H]`("KO`"`J[P```R]```,O0``%CP``!8\```4.P``%#L` -M0.N\`$#KO```_SL``/\[`&!?/0!@7ST`P,$\`,#!/`!@";T`8`F]`,#;P` -M`'F\``#@N@``X+H`P`@]`,`(/0``"CL```H[`(`!O0"``;T``$8[``!&.P!` -MRSP`0,L\`,"=O`#`G;P`X":]`.`FO0``^#L``/@[```T/0``-#T``&X\``!N -M/```'KL``!Z[``"/.P``CSL``-^\``#?O`#@7+T`X%R]``"%NP``A;L`0,\\ -M`$#//```=[P``'>\`,"XO`#`N+P`H"8]`*`F/0#`63T`P%D]```EO```);P` -MP""]`,`@O0``(+D``""Y``#,.P``S#L`P)F\`,"9O```X3L``.$[`"`I/0`@ -M*3T``)T[``"=.P"`QKP`@,:\``"7.P``ESL``"@\```H/`"@`+T`H`"]`"`; -MO0`@&[T``%@[``!8.P``_SL``/\[`$"^O`!`OKP``".\```CO`!`RSP`0,L\ -M```H.@``*#H`@&B\`(!HO`!@`#T`8``]`,`R/0#`,CT``%V\``!=O`"`R;P` -M@,F\``!=/```73P``.T[``#M.P#`![T`P`>]`(!#O`"`0[P`(!\]`"`?/0#` -MV3P`P-D\`("\O`"`O+P`@(V\`("-O`"`(KP`@"*\`$!"O0!`0KT`0$&]`$!! -MO0``4#P``%`\``#V/```]CP``%H[``!:.P"`3#P`@$P\`$`@/0!`(#T`@$`\ -M`(!`/```_;P``/V\``!`O```0+P`P(D\`,")/```B[L``(N[``#>NP``WKL` -M8!4]`&`5/0"`I#P`@*0\```HO0``*+T`8`"]`&``O0``J3P``*D\``"$.P`` -MA#L`H""]`*`@O0"`S;P`@,V\`(!5/`"`53P``,J[``#*NP!`TKP`0-*\```Q -M/```,3P`(``]`"``/0``8+L``&"[``!>.P``7CL`P"L]`,`K/0#`A3P`P(4\ -M```AO0``(;T`@-V\`(#=O`"`+3P`@"T\``!,.P``3#L``+Z[``"^NP!`OSP` -M0+\\``#%/```Q3P``-F\``#9O`!`_[P`0/^\`(`3/`"`$SP`@%2\`(!4O`"` -M=KT`@':]`,"&O`#`AKP`@$L]`(!+/0#@"ST`X`L]```\O```/+P````````` -M``!`C#P`0(P\```PO```,+P``.&\``#AO```"CP```H\``!C/```8SP``'.\ -M``!SO```A#L``(0[`$`]``"TNP``M+L``**[``"BNP#`W;P`P-V\```P.@``,#H`0`H]`$`* -M/0``!KL```:[`(##O`"`P[P`@.T\`(#M/`#`+ST`P"\]`,"0O`#`D+P`X`*] -M`.`"O0"`L#P`@+`\`$#E/`!`Y3P``)&\``"1O`"`Y;P`@.6\``")NP``B;L` -M@&6\`(!EO`#`F+P`P)B\``"X/```N#P`P/`\`,#P/```VKP``-J\`,`%O0#` -M!;T`@$T\`(!-/```&#L``!@[`(`JO0"`*KT``&Z\``!NO`!`+ST`0"\]`$`5 -M/0!`%3T``%8[``!6.P"`K3P`@*T\``#K/```ZSP`@-B\`(#8O`"`/KT`@#Z] -M``"_.P``OSL`0(`\`$"`/`#`U+P`P-2\`(`GO`"`)[P`@`4]`(`%/0``(SP` -M`",\`"`2O0`@$KT`@,6\`(#%O```N+H``+BZ``"6O```EKP``)^\``"?O``` -MRCP``,H\``#8/```V#P`@&F\`(!IO```0#H``$`Z`"`W/0`@-ST``(`\``"` -M/```UKP``-:\```2.P``$CL`(`0]`"`$/0"``;P`@`&\`$`?O0!`'[T``/:[ -M``#VNP``^3L``/D[`(#1O`"`T;P``%B\``!8O`!@&#T`8!@]`(!F/`"`9CP` -M0#2]`$`TO0"`[KP`@.Z\`(`(/`"`"#P`@!>\`(`7O```H;P``*&\``"O/``` -MKSP`X#D]`.`Y/0``G3P``)T\`(`5/`"`%3P`P+,\`,"S/```/+P``#R\`(!D -MO0"`9+T`@'2\`(!TO`"`'ST`@!\]```%/```!3P``.6\``#EO```ESL``)<[ -M`$"+/`!`BSP`@-.\`(#3O`#`$;T`P!&]``#D.P``Y#L````\````/`"`X+P` -M@."\``"1NP``D;L`P"D]`,`I/0``,CP``#(\`$`&O0!`!KT``.@Z``#H.@"` -M)#T`@"0]`(!Z/`"`>CP``-V[``#=NP``A3L``(4[```&O```!KP`@`J]`(`* -MO0"`-KP`@#:\`(#2/`"`TCP``.RZ``#LN@"@`+T`H`"]`(!=/`"`73P`X"<] -M`.`G/0``I[P``*>\``!>O0``7KT```^\```/O`#`@#P`P(`\```$O```!+P` -M`%X[``!>.P`@'3T`(!T]`$"B/`!`HCP`@*:\`("FO```B+L``(B[``"0/``` -MD#P`P*V\`,"MO`"`);T`@"6]``!8/```6#P`X!H]`.`:/0``+KL``"Z[`("` -MO`"`@+P``/D[``#Y.P``&+L``!B[`,#*O`#`RKP```:[```&NP!`ESP`0)<\ -M``!SO```<[P``-N\``#;O`!`UCP`0-8\`,`&/0#`!CT`0/R\`$#\O`!`&KT` -M0!J]``#2/```TCP`X`4]`.`%/0``4+P``%"\`,"#O`#`@[P``)X[``">.P"` -M7;P`@%V\`(#.O`"`SKP`@'L\`(![/`#`%3T`P!4]``"XNP``N+L`P(&\`,"! -MO`!`YCP`0.8\``"3/```DSP`8":]`&`FO0`@&+T`(!B]``#=.P``W3L`@`L\ -M`(`+/```P#@``,`X`,"]/`#`O3P`P)L\`,";/```#+T```R]`,`EO0#`);T` -M`%<\``!7/```<3P``'$\`*`/O0"@#[T`@%:\`(!6O`"`2CT`@$H]`.`%/0#@ -M!3T`0*F\`$"IO`"`,[P`@#.\`("!/`"`@3P``/"Z``#PN@"`4+P`@%"\``"5 -M/```E3P``(T\``"-/`#`\[P`P/.\`(#BO`"`XKP``(L\``"+/```R#H``,@Z -M`,`/O0#`#[T``*6[``"ENP"`V#P`@-@\``#TNP``]+L`P.J\`,#JO```7CL` -M`%X[`(`0/`"`$#P`@*>\`("GO```\#D``/`Y`,!A/0#`83T`0!0]`$`4/0`` -MK[P``*^\``#:NP``VKL`0.0\`$#D/`"`/KP`@#Z\`"`[O0`@.[T``-^[``#? -MNP``M3P``+4\``"\NP``O+L`@&J\`(!JO```)#P``"0\``"\NP``O+L`X#N] -M`.`[O0"`J+P`@*B\``#-/```S3P``&P[``!L.P``GKP``)Z\`$#%/`!`Q3P` -M0"X]`$`N/0``H+H``*"Z```IO```*;P`P+P\`,"\/```43P``%$\`$"XO`!` -MN+P``,"Y``#`N0!@"#T`8`@]```6O```%KP`H&^]`*!OO0!`DKP`0)*\`(#, -M/`"`S#P``">\```GO```_+P``/R\```8/```&#P`@'\\`(!_/```I[P``*>\ -M``!QO```<;P`0)(\`$"2/```*CL``"H[`(!NO`"`;KP`0.L\`$#K/```<#T` -M`'`]`(`!/`"``3P``.6\``#EO```"#H```@Z``"X.@``N#H`0`6]`$`%O0"` -M1KP`@$:\`,#?/`#`WSP``,`X``#`.``@&+T`(!B]``#0NP``T+L`@+4\`("U -M/`#`O[P`P+^\`(!/O0"`3[T``!`\```0/`"@/ST`H#\]``"L.P``K#L``$&\ -M``!!O`#`B#P`P(@\`(`*/`"`"CP``(*\``""O```(#P``"`\``#N/```[CP` -M`(V[``"-NP``M+P``+2\`(!P/`"`<#P`0(`\`$"`/`"@'KT`H!Z]`(`JO0"` -M*KT`P(8\`,"&/`#`ISP`P*<\``".O```CKP``(^[``"/NP#`E3P`P)4\``!\ -MO```?+P`H`F]`*`)O0"`13P`@$4\`,`"/0#``CT``$B\``!(O`#`F;P`P)F\ -M`(`3/0"`$ST```\]```//0"`W[P`@-^\`(`!O0"``;T`@"X\`(`N/```ICL` -M`*8[`(`@O`"`(+P`@%L\`(!;/```=CP``'8\`$#(O`!`R+P`0+.\`$"SO`#` -MJ3P`P*D\`(`N/`"`+CP``"2]```DO0#`J;P`P*F\```9/0``&3T``+$\``"Q -M/```U+P``-2\```GO```)[P`@!L\`(`;/```D+P``)"\`,"&O`#`AKP`@/<\ -M`(#W/`"`WCP`@-X\``#"O```PKP`0)2\`$"4O`!``CT`0`(]``!Z/```>CP` -M8!:]`&`6O0#`A+P`P(2\`(#G/`"`YSP`@%<\`(!7/`"`$KP`@!*\``"=.P`` -MG3L`@`"\`(``O`!@(+T`8""]`,"/O`#`C[P`@.8\`(#F/`"`%3P`@!4\`&`! -MO0!@`;T``(6[``"%NP``$3T``!$]``"9NP``F;L`0"6]`$`EO0``3#L``$P[ -M`"`(/0`@"#T``!8[```6.P``K#H``*PZ`$`#/0!``ST`@"<\`(`G/`#@&;T` -MX!F]`,";O`#`F[P``,,\``##/```'CL``!X[``#RO```\KP``!@Z```8.@`` -MI3P``*4\``"LO```K+P`P`Z]`,`.O0``@+H``("Z```@.0``(#D`@)&\`("1 -MO```9CP``&8\`(`R/0"`,CT``(L[``"+.P#`!;T`P`6]``#>.P``WCL``!0] -M```4/0``\+L``/"[`,#+O`#`R[P`0,D\`$#)/`!`ZCP`0.H\`,"DO`#`I+P` -M@+"\`("PO```[SL``.\[`$"$O`!`A+P`8"J]`&`JO0``4KL``%*[``#V/``` -M]CP``-*[``#2NP`@!;T`(`6]``"K.P``JSL``+`\``"P/```.;P``#F\``"R -MNP``LKL`@/4\`(#U/```13P``$4\``!IO```:;P`0(`\`$"`/`"`]#P`@/0\ -M`$"QO`!`L;P`@"Z]`(`NO0"`1SP`@$<\`.`,/0#@##T``(F\``")O```Y;P` -M`.6\```]/```/3P``%"Z``!0N@#`*KT`P"J]``!XO```>+P``+X\``"^/``` -M`````````,"%O`#`A;P`P,P\`,#,/`#@'CT`X!X]`(`HO`"`*+P`P/R\`,#\ -MO```S#L``,P[``!:/```6CP``#R\```\O```_CL``/X[`$#T/`!`]#P`@!V\ -M`(`=O````+T```"]`(`;/`"`&SP`0(4\`$"%/`"`#;T`@`V]`.`(O0#@"+T` -M`.@\``#H/`#`]3P`P/4\``#@O```X+P``..\``#CO`"`C3P`@(T\`(`//`"` -M#SP``%N\``!;O`#`@3P`P($\``#F/```YCP`0("\`$"`O`"`TKP`@-*\``!4 -M/```5#P``"T\```M/`!`\+P`0/"\```^O```/KP``"8]```F/0"`VSP`@-L\ -M`("4O`"`E+P``/6[``#UNP``(CL``"([`$`!O0!``;T`P,Z\`,#.O`!`]#P` -M0/0\`$#*/`!`RCP`P-6\`,#5O```-+P``#2\`&`8/0!@&#T``"`\```@/``` -M0KT``$*]`(#.O`"`SKP`@,0\`(#$/`"`%CP`@!8\`(`_O`"`/[P`P($\`,"! -M/`"`ASP`@(<\`$"=O`!`G;P``)J[``":NP#`%#T`P!0]``!&/```1CP`8`B] -M`&`(O0"`&;P`@!F\`,#T/`#`]#P``$"Z``!`N@"`![T`@`>]``!BNP``8KL` -M@%H\`(!:/```=;P``'6\``"%NP``A;L`P.D\`,#I/```IKL``*:[`.!0O0#@ -M4+T``&"\``!@O`!`&3T`0!D]```G/```)SP`P,2\`,#$O`"`B#P`@(@\`"!6 -M/0`@5CT``$@\``!(/`#`G[P`P)^\```H.@``*#H``(N[``"+NP#`Y[P`P.>\ -M``#@.0``X#D`@`D]`(`)/0``0#H``$`Z`,#\O`#`_+P``,"X``#`N`"`:3P` -M@&D\``#LO```[+P`(""]`"`@O0``,CP``#(\`("M/`"`K3P`@"&\`(`AO``` -MV3L``-D[`*`9/0"@&3T``&X[``!N.P!`R[P`0,N\``"9/```F3P`H#X]`*`^ -M/0``X+D``."Y``#WO```][P``,`[``#`.P#`C3P`P(T\`,#)O`#`R;P``-6\ -M``#5O`"`5#P`@%0\``"].P``O3L`@**\`("BO```&CL``!H[```H/```*#P` -M@`J]`(`*O0!@(KT`8"*]`$"1/`!`D3P`@!\]`(`?/0``H#L``*`[`(`&/`"` -M!CP`8"\]`&`O/0!`H#P`0*`\`(#BO`"`XKP`@`:\`(`&O`#`I#P`P*0\``!% -MO```1;P`@-"\`(#0O`#`Q#P`P,0\`*`,/0"@##T`P+^\`,"_O`!`+KT`0"Z] -M```@.0``(#D``*([``"B.P``[+P``.R\``!NO```;KP`0+D\`$"Y/```O3L` -M`+T[```;O```&[P`@-8\`(#6/`#@"#T`X`@]`(`=O`"`';P`@!"\`(`0O``@ -M##T`(`P]`,"Z/`#`NCP``-"\``#0O`"`DKP`@)*\`("&/`"`ACP``%2[``!4 -MNP``SKP``,Z\``!*/```2CP`0.4\`$#E/`#`N[P`P+N\`,`;O0#`&[T`@``\ -M`(``/`"`(CP`@"(\`*`.O0"@#KT`@+.\`("SO`#@#CT`X`X]`*`F/0"@)CT` -M@"4\`(`E/`"`?SP`@'\\`$"1/`!`D3P`P**\`,"BO`"`TKP`@-*\`("$/`"` -MA#P``%0\``!4/`#`PKP`P,*\``#,N@``S+H`("P]`"`L/0"`43P`@%$\`"`= -MO0`@';T`@+:\`("VO```'SP``!\\`(!$O`"`1+P``+^\``"_O`"`C#P`@(P\ -M`,#!/`#`P3P``)B\``"8O```"KP```J\`.`D/0#@)#T`0+8\`$"V/`!`O[P` -M0+^\``!PNP``<+L`@/P\`(#\/```(#D``"`Y`,`#O0#``[T``-2[``#4NP"` -M93P`@&4\``!LNP``;+L``!X\```>/``@'ST`(!\]``"!.P``@3L`X#F]`.`Y -MO0"`DKP`@)*\`(":/`"`FCP`@%6\`(!5O`!`$+T`0!"]`("'/`"`ASP`H%$] -M`*!1/0``6#P``%@\`(!6O`"`5KP``/X[``#^.P``H;L``*&[`*`?O0"@'[T` -M`/B[``#XNP!`'3T`0!T]`(`C/`"`(SP`P,&\`,#!O`"`83P`@&$\`,`D/0#` -M)#T``#BZ```XN@!`Z;P`0.F\``!HN@``:+H``/L[``#[.P``=+P``'2\``!: -MNP``6KL`@)L\`(";/```,+P``#"\``#NO```[KP``)L\``";/`!`)ST`0"<] -M``!=O```7;P`@!>]`(`7O0"``3P`@`$\`("I/`"`J3P`0+&\`$"QO```H;P` -M`*&\`$#9/`!`V3P`@-,\`(#3/```,#H``#`Z`$"T/`!`M#P`8`4]`&`%/0"` -ME[P`@)>\`*`LO0"@++T``.RZ``#LN@``5#P``%0\`("1O`"`D;P`@"B\`(`H -MO```WSP``-\\`$")/`!`B3P`@)R\`("[`(`)O`"`";P` -M`!8]```6/0``PCP``,(\`$#$O`!`Q+P``%^\``!?O`#`J3P`P*D\``#0N0`` -MT+D`P,B\`,#(O```4CL``%([`(!!/`"`03P`@,^\`(#/O`#`K+P`P*R\`$#- -M/`!`S3P`@(T\`("-/`#`WKP`P-Z\`("GO`"`I[P``"@\```H/```?+L``'R[ -M`,"!O`#`@;P``,<\``#'/`#@-CT`X#8]``#+.P``RSL``)"Z``"0N@"@!#T` -MH`0]`(`,/`"`##P`X%*]`.!2O0`@#+T`(`R]``"W/```MSP`0(@\`$"(/``` -M;KP``&Z\``"@.P``H#L`0*T\`$"M/```@;P``(&\`,`.O0#`#KT``.BZ``#H -MN@``G#L``)P[``#&O```QKP``*6[``"ENP#@)CT`X"8]`,#@/`#`X#P```>\ -M```'O```OCL``+X[`,"Y/`#`N3P``)B[``"8NP!`@;P`0(&\``!D/```9#P` -M`/([``#R.P``";T```F]``",O```C+P`H`<]`*`'/0"`8SP`@&,\`,#[O`#` -M^[P```>\```'O`!`XCP`0.(\``#DN@``Y+H`0`R]`$`,O0``5+P``%2\``#/ -M.P``SSL``,Z[``#.NP``_#L``/P[```O/0``+ST`@-`\`(#0/`#`AKP`P(:\ -M``!4NP``5+L``)0\``"4/`"`CKP`@(Z\`,`DO0#`)+T``/@Z``#X.@"`^3P` -M@/D\`(`#/`"``SP``(T[``"-.P``P3P``,$\```6NP``%KL`8#*]`&`RO0"` -MC+P`@(R\`("S/`"`LSP``,F[``#)NP``"+T```B]`(`^/`"`/CP`P"P]`,`L -M/0``9#L``&0[`$"SO`!`L[P``+L[``"[.P"`7CP`@%X\`(`@O`"`(+P``/RZ -M``#\N@#`FSP`P)L\``#JNP``ZKL`0.*\`$#BO`"`=#P`@'0\`*`M/0"@+3T` -M``@[```(.P!`E;P`0)6\``!3/```4SP``"8\```F/```!;T```6]`*`&O0"@ -M!KT``$`Y``!`.0``<+L``'"[`(!`O`"`0+P`@-@\`(#8/`"`-CT`@#8]`(`6 -MO`"`%KP``".]```CO0``"CL```H[`$"S/`!`LSP``'*\``!RO```?[P``'^\ -M`$#U/`!`]3P`P!D]`,`9/0``ZSL``.L[``"9.P``F3L```0\```$/`!`L[P` -M0+.\`$`!O0!``;T``.L[``#K.P``0#P``$`\`,#PO`#`\+P`P,N\`,#+O`#` -MH#P`P*`\``!G/```9SP``*&\``"AO```A[L``(>[`("]/`"`O3P```H[```* -M.P!`B+P`0(B\`(!N/`"`;CP`@-0\`(#4/```)+P``"2\`(`4O`"`%+P`(!\] -M`"`?/0"@$CT`H!(]`(!.O`"`3KP`P(V\`,"-O```ASL``(<[``"#O```@[P` -M(!J]`"`:O0"`4[P`@%.\``#Q.P``\3L`@!N\`(`;O```X+H``."Z`(#]/`"` -M_3P``%,\``!3/`"@![T`H`>]`(")O`"`B;P`P,P\`,#,/`"`,#P`@#`\`(`G -MO`"`)[P``+L\``"[/`!@.CT`8#H]```,/```##P``'*\``!RO```"CP```H\ -M``!^.P``?CL`P!.]`,`3O0#`R[P`P,N\`("A/`"`H3P``,`[``#`.P#``[T` -MP`.]`(!'O`"`1[P`P(T\`,"-/```>+H``'BZ``!6O```5KP``',\``!S/`"` -MCCP`@(X\`(!*O`"`2KP``*J[``"JNP"`YCP`@.8\`(!+/`"`2SP``'^\``!_ -MO```<#P``'`\`.`T/0#@-#T``-D[``#9.P!`[+P`0.R\`(`O```7KP` -M`&2[``!DNP!`OSP`0+\\``!\NP``?+L`(`6]`"`%O0``D;L``)&[`$"]/`!` -MO3P``"4\```E/`"`)SP`@"<\`(#?/`"`WSP`@"L\`(`K/`#`G[P`P)^\```T -MNP``-+L`P)X\`,">/`"`+;P`@"V\`*`.O0"@#KT```([```".P#`U3P`P-4\ -M```(O```"+P`@+J\`("ZO```)CP``"8\`$"&/`!`ACP``!6\```5O```!KL` -M``:[``"4/```E#P``-N[``#;NP``_+P``/R\``!<.P``7#L`P`0]`,`$/0`` -MG#L``)P[```MO```+;P`0*D\`$"I/`!`PSP`0,,\```MO```+;P`P)&\`,"1 -MO```%+L``!2[`(!CO`"`8[P`0)&\`$"1O`#`ISP`P*<\`$`?/0!`'ST``!Z[ -M```>NP!`OKP`0+Z\``!"/```0CP``(D\``")/`!`[+P`0.R\`.``O0#@`+T` -M`$8\``!&/`#`ESP`P)<\``!`NP``0+L```T\```-/```I3P``*4\`(`3O`"` -M$[P``+6\``"UO`"`<#P`@'`\`(#?/`"`WSP`@#Z\`(`^O```H;P``*&\`("( -M/`"`B#P``*<\``"G/```%+P``!2\``";NP``F[L``&4\``!E/```_#H``/PZ -M```(O```"+P``"L\```K/```6CL``%H[`(`:O0"`&KT`P`:]`,`&O0!`LCP` -M0+(\`$#O/`!`[SP`@!R\`(`O`!` -MGKP``#2[```TNP"`U#P`@-0\`(`%/`"`!3P`@!N\`(`;O```+3P``"T\``!Y -M/```>3P``#6\```UO```<;P``'&\```R.P``,CL``$2\``!$O`#@$;T`X!&] -M`(`2O`"`$KP`@-(\`(#2/```"CP```H\`(`YO`"`.;P`@#(\`(`R/`"`ASP` -M@(<\`(!(O`"`2+P`@`V\`(`-O```SCP``,X\`("?/`"`GSP``.Z[``#NNP"` -M*3P`@"D\`,`4/0#`%#T``$([``!".P"@"[T`H`N]``!"O```0KP`@%$\`(!1 -M/```'KP``!Z\``";O```F[P``+P[``"\.P``N#L``+@[`$"EO`!`I;P`@$.\ -M`(!#O```<#P``'`\``"9.P``F3L`@$:\`(!&O```5SP``%<\```%/0``!3T` -M`(L[``"+.P"`1;P`@$6\```[/```.SP``%\\``!?/```Y;L``.6[``#,.P`` -MS#L`@.8\`(#F/```H#H``*`Z`"`)O0`@";T``&Z\``!NO`"`*SP`@"L\`(`Z -MO`"`.KP`@.2\`(#DO```I3L``*4[`(#-/`"`S3P``.0Z``#D.@"`&+P`@!B\ -M``"#.P``@SL``%J[``!:NP"`6KP`@%J\``!$/```1#P`P`(]`,`"/0``!#P` -M``0\```2O```$KP`@%L\`(!;/`#`DCP`P)(\`(!NO`"`;KP`P)&\`,"1O`"` -M&SP`@!L\``"G.P``ISL``("\``"`O```P+H``,"Z```W/```-SP``'Z\``!^ -MO`#`V[P`P-N\`(`1/`"`$3P`P-8\`,#6/```RKL``,J[`$"0O`!`D+P`@%4\ -M`(!5/`!`B3P`0(D\`(!-O`"`3;P``!B\```8O```<#P``'`\`(`3/`"`$SP` -M`.`Z``#@.@#`ICP`P*8\``",/```C#P`0*&\`$"AO`!`S[P`0,^\`(`//`"` -M#SP`@"@\`(`H/`!`GKP`0)Z\`(`VO`"`-KP`@*,\`("C/`"`!SP`@`<\`$"+ -MO`!`B[P``+>[``"WNP``;#L``&P[`,".O`#`CKP``'R\``!\O```N#P``+@\ -M`$#2/`!`TCP`@%2\`(!4O`"`A;P`@(6\`,"F/`#`ICP`0*<\`$"G/```'[P` -M`!^\``"*NP``BKL`0(<\`$"'/```I3L``*4[`(`#O`"``[P``#`Z```P.@`` -MMKL``+:[``#1O```T;P`@"*\`(`BO`"`N#P`@+@\`(`T/`"`-#P`0+N\`$"[ -MO`"`7+P`@%R\`(`5/`"`%3P```>\```'O`#`P[P`P,.\``"$.P``A#L`0,4\ -M`$#%/`"`$#P`@!`\``#I.P``Z3L``,D\``#)/`"`)3P`@"4\``"TO```M+P` -M`%6\``!5O`"`ASP`@(<\``#@.P``X#L``'>\``!WO```9KL``&:[``#7.P`` -MUSL`@$J\`(!*O`#`C[P`P(^\``#@N@``X+H``-:[``#6NP``F;P``)F\``"@ -M.0``H#D`@*0\`("D/```,KL``#*[``"YO```N;P``-$[``#1.P```ST```,] -M`(`I/`"`*3P``%*[``!2NP"`DSP`@),\`("!/`"`@3P`@'^\`(!_O`"`BKP` -M@(J\```Z.P``.CL``.>[``#GNP"`N[P`@+N\````.0```#D``*8\``"F/``` -MK+L``*R[``#IO```Z;P``"2\```DO```P#D``,`Y``!PO```<+P``,"[``#` -MNP#`J3P`P*D\``![/```>SP``*BZ``"HN@"`9SP`@&<\`$#)/`!`R3P``+^[ -M``"_NP#`R;P`P,F\``"5.P``E3L`0*4\`$"E/```$+P``!"\``"=O```G;P` -M`(L[``"+.P```#@````X``#)O```R;P`@$V\`(!-O```4#P``%`\``"DN@`` -MI+H`0*2\`$"DO```!+L```2[`(!V/`"`=CP``)2[``"4NP"`9;P`@&6\`(`@ -M/`"`(#P`0*0\`$"D/```S#L``,P[`(`./`"`#CP`@%H\`(!:/`"`3[P`@$^\ -M`$#PO`!`\+P``&R[``!LNP``;#P``&P\``#7NP``U[L`@&N\`(!KO`"`5SP` -M@%<\`$"(/`!`B#P`P(J\`,"*O`#`SKP`P,Z\``!DNP``9+L``%:[``!6NP`` -M,KP``#*\`(`P/`"`,#P`@,`\`(#`/```0+H``$"Z`(!ZO`"`>KP``+([``"R -M.P``,SP``#,\``!&O```1KP``&F\``!IO`"`%SP`@!<\```'/```!SP`@`"\ -M`(``O```0#H``$`Z`(`%/`"`!3P`@#:\`(`VO```-[P``#>\``"5/```E3P` -M`((\``""/```NKP``+J\``#7O```U[P``-`[``#0.P"`!#P`@`0\`$"*O`!` -MBKP``/&[``#QNP#`DCP`P)(\``#>.P``WCL``"F\```IO```,#H``#`Z```X -M.@``.#H`@,*\`(#"O`#`A;P`P(6\``"U/```M3P``.0\``#D/```D+D``)"Y -M``"(N@``B+H``(L\``"+/```$CL``!([`,"YO`#`N;P``%*\``!2O```"+L` -M``B[``!RO```]`,`'O0#`N+P`P+B\``#<.@``W#H` -M`+R[``"\NP``4[P``%.\``#N.P``[CL``$0\``!$/```5KP``%:\``!\O``` -M?+P``,0Z``#$.@``UKL``-:[`(";O`"`F[P`@"P\`(`L/`!`+3T`0"T]`,#7 -M/`#`USP``,"Z``#`N@``K#L``*P[`(`E/`"`)3P`@'B\`(!XO```TKP``-*\ -M``#DNP``Y+L``':[``!VNP"`>;P`@'F\``!PNP``<+L`@`L\`(`+/`"`.KP` -M@#J\`,#,O`#`S+P``+2Z``"TN@"`2CP`@$H\`(`+O`"`"[P`@%^\`(!?O``` -M5#P``%0\``!_/```?SP``#R[```\NP"`$3P`@!$\```!/0```3T``%`\``!0 -M/```.;P``#F\``#0.0``T#D``.4[``#E.P``R;P``,F\`,`>O0#`'KT```*\ -M```"O`"`*SP`@"L\``#,NP``S+L``,N[``#+NP"`03P`@$$\``"0N@``D+H` -MP,^\`,#/O```4+P``%"\``#+.P``RSL``'J[``!ZNP``=+L``'2[`$#7/`!` -MUSP`X`P]`.`,/0``JCL``*H[``"WNP``M[L``/T[``#].P``?KL``'Z[`(#B -MO`"`XKP``#B\```XO```/CP``#X\``"UNP``M;L``+V\``"]O```"+H```BZ -M``!8/```6#P``#>\```WO`!`N[P`0+N\``!H.@``:#H`@`P\`(`,/```(+P` -M`""\``#]NP``_;L``"8\```F/```@3L``($[``#HN@``Z+H``*$\``"A/`#` -MU3P`P-4\```DNP``)+L`@'R\`(!\O```@#D``(`Y``!LNP``;+L`P,V\`,#- -MO`"`@;P`@(&\``!3/```4SP``!8\```6/```M+L``+2[``"8.P``F#L``,$[ -M``#!.P``S[P``,^\`,`%O0#`!;T``,"X``#`N`"`8SP`@&,\``!^NP``?KL` -M`%@Z``!8.@"`RCP`@,H\`,"//`#`CSP`@!F\`(`9O```_+L``/R[``"\.@`` -MO#H``"N\```KO`"`)[P`@">\`(!H/`"`:#P`@#,\`(`S/`#`A+P`P(2\```D -MO```)+P`@(D\`(")/```OSL``+\[`("_O`"`O[P`@&J\`(!JO```P#L``,`[ -M```)O```";P`0*^\`$"OO````#L````[`(!0/`"`4#P``+Z[``"^NP``\#D` -M`/`Y``#O/```[SP`@'$\`(!Q/```S+P``,R\`("PO`"`L+P`@!,\`(`3/``` -MICL``*8[```HO```*+P``,P[``#,.P"`O#P`@+P\``#`.P``P#L``*.[``"C -MNP``(#H``"`Z`(")O`"`B;P``"6]```EO0``DKP``)*\`$"0/`!`D#P``.\[ -M``#O.P"`9;P`@&6\``"=.P``G3L`@,$\`(#!/```B+H``(BZ`,"GO`#`I[P` -M`/2Z``#TN@``WCL``-X[```2O```$KP``.`Y``#@.0"`PSP`@,,\```[/``` -M.SP``$V\``!-O```E#H``)0Z`$"0/`!`D#P``"J[```JNP"`Q[P`@,>\`(!. -MO`"`3KP``-:[``#6NP"`H+P`@*"\``!8O```6+P``!L\```;/```D#L``)`[ -M``#%NP``Q;L``%P\``!\``##.P``PSL``+<[``"W.P``HKP``**\ -M`$":O`!`FKP``"(\```B/`"`63P`@%D\``!F.P``9CL``)@\``"8/`!`W#P` -M0-P\``#&NP``QKL`@-"\`(#0O```3+L``$R[``!F.P``9CL``+F\``"YO``` -MG[P``)^\`(!1/`"`43P`@&4\`(!E/`"`,KP`@#*\``#MNP``[;L```$\```! -M/```W[L``-^[``">O```GKP``"`[```@.P``3CP``$X\```@O```(+P`@#&\ -M`(`QO`"`=SP`@'<\``"'/```ASP``'"[``!PNP``Z#H``.@Z`(!G/`"`9SP` -M`#"[```PNP#`G+P`P)R\``!TNP``=+L``+0Z``"T.@"`D;P`@)&\```;O``` -M&[P`P,L\`,#+/`!`DCP`0)(\`,"-O`#`C;P``(R\``",O```+#L``"P[`(`[ -MO`"`.[P``..\``#CO```I+L``*2[`$":/`!`FCP``/<[``#W.P``/CL``#X[ -M`,"B/`#`HCP``!(\```2/`!`NKP`0+J\``!QO```<;P``'8\``!V/```RCL` -M`,H[``!:O```6KP``&H[``!J.P"`HSP`@*,\```$NP``!+L`@(.\`("#O``` -MP#@``,`X```"NP```KL`0-R\`$#3P`@'D\``#RNP``\KL``%R\``!\`$"&O`!`AKP``&J[``!JNP``@;P``(&\``#NO```[KP``!N\```;O``` -MC#H``(PZ`$"6O`!`EKP`@&R\`(!LO`#`JCP`P*H\`(#./`"`SCP``*RZ``"L -MN@``T+D``-"Y``"-/```C3P``#0[```T.P``E;P``)6\``"0.@``D#H`0)P\ -M`$"\```'O```G;L``)V[``!*/```2CP``.:[ -M``#FNP#`];P`P/6\`(!DO`"`9+P``&8[``!F.P``&[P``!N\```WO```-[P` -M`&4\``!E/`"`43P`@%$\``!]O```?;P`@$V\`(!-O`"`43P`@%$\``!F.P`` -M9CL`@(J\`("*O```1+L``$2[`(!B/`"`8CP``."Z``#@N@"`'[P`@!^\``#0 -M.P``T#L``-D[``#9.P``2;P``$F\```PNP``,+L`@(@\`("(/```(+L``""[ -M`$#QO`!`\;P``"B\```HO`"`2SP`@$L\``!ZNP``>KL`@'*\`(!RO```_SL` -M`/\[`(!,/`"`3#P`@%R\`(!\```L.P``+#L``-^[``#?NP"` -MQKP`@,:\``#L.@``[#H`@/P\`(#\/```(SP``",\`(!QO`"`<;P````[```` -M.P"`/3P`@#T\`(`>O`"`'KP``&V\``!MO```Y#L``.0[``"E.P``I3L``"Z\ -M```NO```@#D``(`Y``!:/```6CP``&*\``!BO`!``[T`0`.]``"`NP``@+L` -M`&T\``!M/`"`2+P`@$B\`$"VO`!`MKP``)$[``"1.P``.CP``#H\```.O``` -M#KP``"2[```DNP#`GCP`P)X\```B/```(CP```"\````O```WSL``-\[``"! -M/```@3P``#>\```WO`#`N;P`P+F\```^.P``/CL``$@\``!(/```_;L``/V[ -M```1O```$;P``%H[``!:.P``#[P```^\`$#9O`!`V;P`@$*\`(!"O```U#H` -M`-0Z`(`ZO`"`.KP``">\```GO```L3P``+$\`$#-/`!`S3P``(>[``"'NP`` -M[KL``.Z[``!P/```<#P``/`[``#P.P"`0KP`@$*\``!HN@``:+H``'(\``!R -M/```4+H``%"Z`(!'O`"`1[P```([```".P``N#H``+@Z`,"RO`#`LKP``)*\ -M``"2O```X#L``.`[``!(N@``2+H`0+&\`$"QO`"`&+P`@!B\`(`C/`"`(SP` -M`.RZ``#LN@``Y+L``.2[``"&/```ACP`@,$\`(#!/```2+H``$BZ```\NP`` -M/+L`P(`\`,"`/```0CL``$([`(#&O`"`QKP```*\```"O```FSP``)L\``"H -M.P``J#L``'B\``!XO```(+H``""Z``"U.P``M3L`0)Z\`$">O`!`W[P`0-^\ -M``#TN@``]+H``(X[``"..P"`-[P`@#>\``!PN@``<+H`0*@\`$"H/```[SL` -M`.\[```VO```-KP``*T[``"M.P"`B3P`@(D\```/`"`'CP``,J[``#*NP"`&KP`@!J\``""/``` -M@CP``!\\```?/```*+H``"BZ`(!%O`"`1;P`0-0\`$#4/```Q3P``,4\`#@^ -MO@`X/KX`@"4\`(`E/``PC#T`,(P]``"(O0``B+T`F'P^`)A\/@"@BKT`H(J] -M`+B>O@"XGKX`\/`]`/#P/0"X&#X`N!@^`."$O0#@A+T`(`8]`"`&/0"`"CP` -M@`H\`("HO`"`J+P``'P[``!\.P``'#T``!P]``#Z.P``^CL`0("]`$"`O0"P -MGKT`L)Z]`,`R/0#`,CT`D"(^`)`B/@"@J+T`H*B]`'A5O@!X5;X`(",^`"`C -M/@#@?SX`X'\^`-@)O@#8";X`!("^``2`O@``]`)A^/@"8?CX`P!T^`,`=/@#8;KX`V&Z^ -M`!`HO@`0*+X`X/,]`.#S/0``0#X``$`^`,`4O0#`%+T``(>\``"'O`!8*#X` -M6"@^`$")O`!`B;P`H/*]`*#RO0!`-[T`0#>]``!!O```0;P`H,<]`*#'/0!` -M63T`0%D]`&`TO0!@-+T`2"^^`$@OO@"X&[X`N!N^`&#]/0!@_3T`H)D]`*"9 -M/0"0V;T`D-F]`'@#/@!X`SX`("D^`"`I/@"`_3T`@/T]`#ACO@`X8[X`#)V^ -M``R=O@!X7#X`>%P^`%">/@!0GCX`@(^]`("/O0!02;X`4$F^``#3NP``T[L` -M@'(]`(!R/0"(0[X`B$.^`/#>O0#PWKT`X(L^`."+/@"0K#T`D*P]`,`E/0#` -M)3T`\*"]`/"@O0#(5;X`R%6^`(""/`"`@CP`2$D^`$A)/@#`T#T`P-`]`$"\ -M/`!`O#P`L(:]`+"&O0!`LKT`0+*]`/`SO@#P,[X`*`*^`"@"O@!PGCT`<)X] -M`+A8/@"X6#X`"%P^``AO@!PE[X`<)>^`,A8/@#(6#X`D)T^`)"=/@#@3KT`X$Z]`,AZO@#(>KX` -MX!$^`.`1/@`H$SX`*!,^`$R$O@!,A+X`:`J^`&@*O@!PP#T`<,`]`&B)/@!H -MB3X`L)$]`+"1/0!0?+X`4'R^`$#IO0!`Z;T```@Z```(.@#`Z[P`P.N\`#@) -M/@`X"3X`@'4]`(!U/0``K#T``*P]`$!A/0!`83T`P-:]`,#6O0``O+T``+R] -M`"#BO0`@XKT`("$^`"`A/@"$@SX`A(,^`+@&O@"X!KX`"%F^``A9O@"`I[P` -M@*>\`(!GO`"`9[P`P$^]`,!/O0!`O;P`0+V\`*R$/@"LA#X`^#,^`/@S/@!( -MD[X`2).^`!!-O@`03;X`0!\]`$`?/0#H%#X`Z!0^`"!%V^`'A=O@`@93T`(&4]`)A./@"83CX`P"H]`,`J/0``,+P``#"\ -M`&`&/0!@!CT`(`\]`"`//0#`&;X`P!F^`)#=O0"0W;T`,!H^`#`:/@`H>CX` -M*'H^`(`1/`"`$3P`=+R^`'2\O@"0&;X`D!F^```G/@``)SX`@-B\`(#8O``` -MP#@``,`X`/A,/@#X3#X`P,0]`,#$/0!P!+X`<`2^`,`GO@#`)[X`@#$\`(`Q -M/`"@B3T`H(D]`,@,/@#(##X`^'@^`/AX/@"()+X`B"2^``B-O@`(C;X``+`Y -M``"P.0!`/@"@'CX`$,`]`!#`/0!`@+T`0("]`.A$O@#H1+X`(`6] -M`"`%O0`P0SX`,$,^`/#!/0#PP3T`4)0]`%"4/0"`[;T`@.V]`!2&O@`4AKX` -M`":[```FNP`@#3X`(`T^``#ZO```^KP`P,@\`,#(/`#@ZCT`X.H]`-#"/0#0 -MPCT`@%6^`(!5O@#@1[X`X$>^``!\`$#GO`!`'+T`0!R]`$"-O0!`C;T`P.&\`,#AO`#`_ST`P/\] -M`$`QO0!`,;T`P*:\`,"FO```S#P``,P\``#MO0``[;T`0%&]`$!1O0#P@ST` -M\(,]`%!*/@!02CX`H!H^`*`:/@!(#[X`2`^^`#"1O0`PD;T`P%8]`,!6/0#@ -MQKT`X,:]`&"GO0!@I[T`<)D]`'"9/0!X.#X`>#@^`(#4O`"`U+P`$$Z^`!!. -MO@!`X+P`0."\`,!&O0#`1KT``!X\```>/`"`1#X`@$0^`#"X/0`PN#T`X-^] -M`.#?O0!`R+T`0,B]```F/```)CP`8$0]`&!$/0#`T#P`P-`\`)@+/@"8"SX` -M$.4]`!#E/0`@T;T`(-&]`!!RO@`0\`,#4/0#`U#T`0(N]`$"+O0`0-;X`$#6^```I -M/0``*3T`0&4^`$!E/@#`&3T`P!D]`"!*O0`@2KT`<):]`'"6O0``,+X``#"^ -M`*""/0"@@CT`4$$^`%!!/@``&[T``!N]`#@*O@`X"KX`H#`]`*`P/0``X3T` -M`.$]`)#'O0"0Q[T`.$F^`#A)O@!(`#X`2``^`&R3/@!LDSX`((<]`""'/0!8 -M*[X`6"N^`*`/O@"@#[X``/4]``#U/0"`33P`@$T\`!!.O@`03KX`0&<]`$!G -M/0"H.CX`J#H^`$!5O0!`5;T`V!^^`-@?O@!`X[T`0..]`&`A/0!@(3T`6!`^ -M`%@0/@`@_ST`(/\]`#"D/0`PI#T`V`&^`-@!O@!`X;P`0.&\`("E/0"`I3T` -ML-.]`+#3O0``(;P``"&\`.!4/@#@5#X`T.X]`-#N/0!P*+X`<"B^`+3!O@"T -MP;X`X".]`.`CO0!PC3X`<(T^``##/```PSP`$+B]`!"XO0``.+H``#BZ`/`, -M/@#P##X``'F\``!YO`"P4;X`L%&^`/"U/0#PM3T`Y)T^`.2=/@"@:#T`H&@] -M`-`-O@#0#;X`"#&^``@QO@#@'[T`X!^]`.!O/0#@;ST`P/H\`,#Z/`#`F#P` -MP)@\`."9O0#@F;T``-(\``#2/`#@G3T`X)T]`*@9O@"H&;X`6!F^`%@9O@#` -M/SX`P#\^`'2!/@!T@3X``$V]``!-O0"P+[X`L"^^`.`F/0#@)CT`\+4]`/"U -M/0!PG[T`<)^]`&"YO0!@N;T`(%L]`"!;/0#@"SX`X`L^`.!5O0#@5;T`<$F^ -M`'!)O@"0AKT`D(:]`)#9/0"0V3T`.`<^`#@'/@#@H#T`X*`]`"!H/0`@:#T` -ML(R]`+",O0!`]KT`0/:]``"XNP``N+L``!>\```7O`#`3+T`P$R]`)!$/@"0 -M1#X`L`4^`+`%/@"X=[X`N'>^`-"`O@#0@+X`L(H]`+"*/0``,#X``#`^`(`@ -M/`"`(#P``):[``"6NP!`N3T`0+D]``#\.@``_#H`,`*^`#`"O@"`R[P`@,N\ -M`(#E/0"`Y3T`Z%4^`.A5/@"`2;P`@$F\`&!/O@!@3[X`H(>]`*"'O0#P@[T` -M\(.]`+"CO0"PH[T`$`(^`!`"/@#PPST`\,,]`-"(O0#0B+T``%F]``!9O0#@ -M5CT`X%8]`$#C/`!`XSP`>`B^`'@(O@!PN3T`<+D]`&AP/@!H<#X`0!6]`$`5 -MO0`@R[T`(,N]`*!?O0"@7[T`P.N\`,#KO```=#P``'0\`,#!O`#`P;P`X&,] -M`.!C/0"`-3T`@#4]`-#ZO0#0^KT`0"Z]`$`NO0``N+L``+B[```%/```!3P` -M((\]`""//0#`\#T`P/`]`,`(/@#`"#X`$/&]`!#QO0`X2+X`.$B^``#"/``` -MPCP`('H]`"!Z/0``"#H```@Z`""6/0`@ECT`X!<]`.`7/0`0QKT`$,:]`$AE -MO@!(9;X`@)6\`("5O`"`0CX`@$(^`##M/0`P[3T`@-<\`(#7/`"`%[P`@!>\ -M`#"6O0`PEKT`P)&]`,"1O0#@+CT`X"X]`&"9/0!@F3T``,,\``##/`#`G3P` -MP)T\`(!V/`"`=CP`""&^``@AO@"01;X`D$6^``#^/```_CP`(%8^`"!6/@"` -M3T`X'D]`+!D/@"P9#X`H&6] -M`*!EO0`H:KX`*&J^`,"ZO`#`NKP`H*<]`*"G/0``_CL``/X[`"!9O0`@6;T` -M`*<[``"G.P!`-CT`0#8]`,!'O0#`1[T```N]```+O0#@!;T`X`6]``#<.@`` -MW#H`4/H]`%#Z/0#8+3X`V"T^`"`'O0`@![T`<`2^`'`$O@#`E+P`P)2\`(!; -M/0"`6ST`4(.]`%"#O0!`B+T`0(B]`*`'/@"@!SX`@)T]`("=/0#(5[X`R%>^ -M`"!7O@`@5[X`<,D]`'#)/0"H/CX`J#X^`*`&/0"@!CT`$(*]`!""O0!@.#T` -M8#@]``"J/```JCP`$/"]`!#PO0``W;P``-V\`+@L/@"X+#X`(+L]`""[/0!` -MJ[T`0*N]`(!OO0"`;[T`@)F]`("9O0#0^;T`T/F]``#U/```]3P``"@^```H -M/@#`SCP`P,X\`'@"O@!X`KX`@#J\`(`ZO```MCT``+8]```(O0``"+T`P(Z\ -M`,".O`"0VST`D-L]`,!#/0#`0ST`P*R\`,"LO```";X```F^`.`NO0#@+KT` -MX%P]`.!]`.#'O0!` -MHCT`0*(]`%@7/@!8%SX``(&]``"!O0"0LKT`D+*]`$#R/`!`\CP`T)"]`-"0 -MO0"()KX`B":^``"#NP``@[L`T%8^`-!6/@"@C#T`H(P]`!@VO@`8-KX`@(^] -M`("/O0#PZST`\.L]``!K/0``:ST``%P\``!/0``'CT`"&>^``AGO@#8`[X`V`.^ -M`-`Q/@#0,3X``*(]``"B/0`8"+X`&`B^`(`PO`"`,+P`T.H]`-#J/0``8;T` -M`&&]`,@TO@#(-+X``,L\``#+/`"P4SX`L%,^`!#O/0`0[ST`0*,\`$"C/`"` -MH+T`@*"]`#`>O@`P'KX``#0\```T/`#`7CT`P%X]``#\N@``_+H`0,.\`$## -MO`!`X+P`0."\``#+/```RSP`4,"]`%#`O0`8'+X`&!R^`!#4/0`0U#T`N'<^ -M`+AW/@#`?#T`P'P]`$!"O@!`0KX`P+F]`,"YO0"`43X`@%$^``!N/```;CP` -ML/^]`+#_O0"`LCT`@+(]`$"]/0!`O3T`H"6]`*`EO0#@`KX`X`*^`/#5O0#P -MU;T`@",\`(`C/`"@;3T`H&T]`(`R/0"`,CT`0-2\`$#4O```GKT``)Z]`(". -M/0"`CCT`0)L]`$";/0#@,;T`X#&]``"XO```N+P`P)`]`,"0/0#8`CX`V`(^ -M`.`'O0#@![T`P%^^`,!?O@!@&KT`8!J]`-@:/@#8&CX```^]```/O0"`E;T` -M@)6]`/#+/0#PRST`\,T]`/#-/0`8%;X`&!6^`'A$O@!X1+X`X+P]`."\/0!H -M.#X`:#@^``"L/```K#P``'BZ``!XN@"`J+P`@*B\`)"9O0"0F;T`0-R\`$#< -MO`!@!#T`8`0]`%"=/0!0G3T``*F\``"IO``@@[T`((.]``##NP``P[L`@,*] -M`(#"O0#`O[T`P+^]`(@%/@"(!3X`\&(^`/!B/@"`,#P`@#`\`&`HO@!@*+X` -M@!2]`(`4O0#PHST`\*,]`.`1O0#@$;T`X`N]`.`+O0!@OCT`8+X]`$"-/0!` -MC3T`L,2]`+#$O0#0Q+T`T,2]`&!'/0!@1ST``,.[``##NP``?[T``'^]``#T -M.P``]#L`,)\]`#"?/0!`_[P`0/^\`."FO0#@IKT`((@]`""(/0"`M#T`@+0] -M``#O@"`5;T`@%6]`,!Y/0#`>3T` -ML(6]`+"%O0!P/KX`<#Z^``#!.P``P3L`J(L^`*B+/@#`PST`P,,]`+@4O@"X -M%+X`0%"]`$!0O0!PDST`<),]`)"./0"0CCT``"<\```G/`#@.+T`X#B]`(`@ -MO`"`(+P`P("]`,"`O0!@"[T`8`N]`,#@O`#`X+P`0'F]`$!YO0!`OST`0+\] -M`%@3/@!8$SX`P/>\`,#WO`#`#KX`P`Z^`"`VO0`@-KT`$!H^`!`:/@!0BST` -M4(L]`%`>O@!0'KX`@.0\`(#D/`"0,CX`D#(^`(`VO0"`-KT`.$F^`#A)O@!` -MS;P`0,V\`$#D/0!`Y#T`8!^]`&`?O0!`X+T`0."]`&!M/0!@;3T`P)T]`,"= -M/0``ZKP``.J\``"6.P``ECL`0`0]`$`$/0"0CST`D(\]`""G/0`@IST`@`F\ -M`(`)O`"@F+T`H)B]`%#HO0!0Z+T`P'Z]`,!^O0#@ICT`X*8]`$!!/0!`03T` -M(*B]`""HO0``N+P``+B\`,`H/0#`*#T`X"J]`.`JO0"X#[X`N`^^`'"B/0!P -MHCT`P'8^`,!V/@``(+H``""Z`-#ZO0#0^KT`@)\\`("?/`!@+#T`8"P]`(`^ -MO`"`/KP``",\```C/`!`+CT`0"X]``!XO```>+P`"`"^``@`O@``K[T``*^] -M`(!_O`"`?[P`0.$\`$#A/`#@.#T`X#@]`*"-/0"@C3T`H"`]`*`@/0#@[P``'N\`.`8O0#@&+T`H)X]`*">/0#0NCT`T+H] -M`-"RO0#0LKT`H#6^`*`UO@"`N[P`@+N\`("6/0"`ECT`X'&]`.!QO0#@';T` -MX!V]`(A%/@"(13X`H-(]`*#2/0#P]+T`\/2]`""&O0`@AKT`X+H]`."Z/0!0 -M\#T`4/`]`(`[O0"`.[T`,.>]`##GO0``%#P``!0\```*/```"CP`$*"]`!"@ -MO0"`(KT`@"*]`,#./`#`SCP`H%T]`*!=/0``X#D``.`Y`(!'O`"`1[P`8!F] -M`&`9O0!@GKT`8)Z]`*"D/0"@I#T`@-T]`(#=/0!`@KT`0(*]```^NP``/KL` -M.!(^`#@2/@!`C+P`0(R\`,!*O@#`2KX`((F]`"")O0`@&CX`(!H^`"!D/0`@ -M9#T`T/"]`-#PO0#`C[T`P(^]`"!$/0`@1#T`P`P]`,`,/0`@/KT`(#Z]`("0 -M/`"`D#P`@/\]`(#_/0!@DST`8),]`$"YO0!`N;T`T/B]`-#XO0``%+T``!2] -M`,`4/0#`%#T``-X]``#>/0!@/CT`8#X]`#@6O@`X%KX`L+&]`+"QO0`@OCT` -M(+X]``"`.0``@#D`D-R]`)#]````O````+P`(-4]`"#5/0"`NKP`@+J\`$`HO@!` -M*+X`P/>\`,#WO`"H&#X`J!@^`"`//0`@#ST`."B^`#@HO@"`B[T`@(N]`)#. -M/0"0SCT`0.B\`$#HO`!`H[T`0*.]`/#X/0#P^#T`2!L^`$@;/@"`0CP`@$(\ -M`)#]`!"WO0`@2;T`($F]`##N/0`P -M[CT`X*(]`."B/0"PG+T`L)R]`!#:O0`0VKT`0"H]`$`J/0"`/CP`@#X\`%@0 -MO@!8$+X`D(*]`)""O0"`U#T`@-0]`*#C/0"@XST`@#:\`(`VO`!`K[T`0*^] -M`(`%/0"`!3T`P$P]`,!,/0"`:CP`@&H\`$""/0!`@CT`@!0]`(`4/0"`^+P` -M@/B\`(`7O`"`%[P`@`*]`(`"O0!@!+X`8`2^`,`0O0#`$+T`*"`^`"@@/@!@ -M)CT`8"8]`(A=O@"(7;X`<,R]`'#,O0!P$3X` -MO0#PMCT`\+8]`("P/0"`L#T`P/V\`,#]O`#PBST`\(L]`.#7/0#@UST`8&:] -M`&!FO0#0]+T`T/2]``"+.P``BSL`X.L]`.#K/0``SKL``,Z[`("JO0"`JKT` -M`(>[``"'NP!`'KT`0!Z]`&"?O0!@G[T`@"\\`(`O/```?ST``'\]``#P/``` -M\#P``,:\``#&O```;#P``&P\`*!`/0"@0#T`8!J]`&`:O0"`A[P`@(>\`'"_ -M/0!POST`@'$]`(!Q/0!0F[T`4)N]`,#KO`#`Z[P`8&,]`&!C/0``/;T``#V] -M`.#OO0#@[[T`@*8\`("F/`!@L3T`8+$]`(#$O`"`Q+P`@$F]`(!)O0``(3T` -M`"$]``"U.P``M3L`@!R]`(`!.^`'@3O@!0@CT`4((]`.`)/@#@"3X`@""\ -M`(`@O`"`>3P`@'D\`."`/0#@@#T`(#B]`"`XO0!`C;T`0(V]```=/```'3P` -MP*T\`,"M/```Q;L``,6[``"7NP``E[L`0+&\`$"QO``PG[T`,)^]`)"?O0"0 -MG[T`@(X]`("./0"H`#X`J``^```KO```*[P`P)R]`,"O0"`'3T` -M@!T]`(`D/0"`)#T`@&8\`(!F/`"`YCP`@.8\`("RO`"`LKP``*@Z``"H.@`@ -MV3T`(-D]`(!K/0"`:ST`@'N]`(![O0#PK[T`\*^]`*`*/0"@"CT``"(]```B -M/0#`][T`P/>]`(!HO0"`:+T`Z`H^`.@*/@`@3#T`($P]`%`JO@!0*KX`T/&] -M`-#QO0#P[ST`\.\]`)`]``!'O0#@K+T`X*R]```NO0``+KT`H.`]`*#@/0`0Z3T`$.D]`(!N -MO0"`;KT`0!6]`$`5O0"P]3T`L/4]`*`>/0"@'CT`P-B]`,#8O0"`%KT`@!:] -M`/#O/0#P[ST`8%`]`&!0/0`('+X`"!R^`'#9O0!PV;T``(0]``"$/0!`R3P` -M0,D\`,#,O0#`S+T`X!R]`.`/0!PGCT` -M@*F\`("IO```<+L``'"[`%"//0!0CST`0,&\`$#!O`"@X+T`H."]```Q/0`` -M,3T`()0]`""4/0"``+T`@`"]`$`>O0!`'KT`P`2]`,`$O0!PB[T`<(N]`&!& -MO0!@1KT`T*`]`-"@/0`P^#T`,/@]`("PO`"`L+P``!"]```0O0!`0ST`0$,] -M``#8NP``V+L`8'^]`&!_O0#`G[P`P)^\`#"$/0`PA#T``%(\``!2/`"0[;T` -MD.V]`$#SO`!`\[P`,)L]`#";/0``*+L``"B[`*`^O0"@/KT`($L]`"!+/0!` -M>ST`0'L]`&`3O0!@$[T`0/>\`$#WO```G3T``)T]``#D/```Y#P`P%2]`,!4 -MO0#`HSP`P*,\``"!/0``@3T`P'J]`,!ZO0#8`;X`V`&^``"DO```I+P`0,H\ -M`$#*/`!`*+T`0"B]`$`*O0!`"KT`(+(]`""R/0!@C3T`8(T]`*!-O0"@3;T` -M0+*\`$"RO`"0FCT`D)H]`*!%/0"@13T`0-`\`$#0/```JCP``*H\`$`2O0!` -M$KT`,*^]`#"OO0!`Y[P`0.>\`.!`/0#@0#T``$"[``!`NP!@6;T`8%F]``#` -MN0``P+D``'P[``!\.P`0K+T`$*R]`&"=O0!@G;T``+\]``"_/0`0*3X`$"D^ -M``!(.P``2#L`,+V]`#"]O0"@*CT`H"H]`%"1/0!0D3T`X%6]`.!5O0"`#;T` -M@`V]`(!A/0"`83T``"^\```OO``PFKT`,)J]`&!#O0!@0[T``".\```CO`"` -M.CP`@#H\```D/0``)#T`H&P]`*!L/0``_KP``/Z\`"!:O0`@6KT``"(]```B -M/0#@73T`X%T]`"`"O0`@`KT`8&.]`&!CO0!`7CT`0%X]`+#\/0"P_#T`0*N] -M`$"KO0#X%KX`^!:^`&"!/0!@@3T`@*$]`("A/0#`O+T`P+R]`*"MO0"@K;T` -M$+T]`!"]/0!0H3T`4*$]`""HO0`@J+T``-R\``#/`!`L;P`0+&\`.")O0#@B;T`0'2]`$!TO0``=KP``':\`"`` -M/0`@`#T``).[``"3NP!`R+P`0,B\`(`O/0"`+ST`@+P\`("\/`!@S;T`8,V] -M`*"7O0"@E[T`\.\]`/#O/0!X#CX`>`X^`$!IO0!`:;T`@,&]`(#!O0#@GCT` -MX)X]`+"M/0"PK3T`0#R]`$`\O0!`#KT`0`Z]`$!E/0!`93T``#R[```\NP!` -MS;T`0,V]`.#&O0#@QKT``.0[``#D.P#`23T`P$D]`(#]/`"`_3P`P.,\`,#C -M/```P#@``,`X`(!!O`"`0;P`("$]`"`A/0#`!CT`P`8]`.`VO0#@-KT`P,B\ -M`,#(O``0FST`$)L]`"!`/0`@0#T`L-&]`+#1O0#@?+T`X'R]`(">/0"`GCT` -M8!H]`&`:/0`@O+T`(+R]`,!-O0#`3;T`H)L]`*";/0!`$CT`0!(]`/"4O0#P -ME+T`@(6\`("%O`#PW3T`\-T]`*"//0"@CST`P(J\`,"*O```0#L``$`[`,#A -MO`#`X;P`L,Z]`+#.O0``FKP``)J\`.!+/0#@2ST`P,Z\`,#.O`"`*+T`@"B] -M`$`[/0!`.ST`0#8]`$`V/0#`IKT`P*:]``"#O```@[P`2!,^`$@3/@#PK#T` -M\*P]`*!5O0"@5;T`H&Z]`*!NO0!``#T`0``]`(`[/0"`.ST`X&V]`.!MO0!` -MP;P`0,&\`*"'/0"@AST``#.]```SO0`0[KT`$.Z]`*`JO0"@*KT``/`\``#P -M/```%+P``!2\``!"/```0CP`(-<]`"#7/0"`)CT`@"8]```GO0``)[T`0(4] -M`$"%/0!PM#T`<+0]`$#9O`!`V;P`8&J]`&!JO0``VSL``-L[``"Y/```N3P` -M@)>]`("7O0#@P[T`X,.]`(!G/`"`9SP`H&@]`*!H/0!`QKP`0,:\`.`5O0#@ -M%;T`0"8]`$`F/0"@&ST`H!L]`,#EO`#`Y;P``(`X``"`.`#@C#T`X(P]`(`7 -M/0"`%ST``/`\``#P/`!@9#T`8&0]`.`AO0#@(;T`L/:]`+#VO0"`(KP`@"*\ -M`$!>/0!`7CT`8!F]`&`9O0"0I;T`D*6]`$"\/`!`O#P`4)P]`%"]```7 -MO0``PKP``,*\``!:/```6CP`@,N\`(#+O`"@!+T`H`2]`."E/0#@I3T`L-0] -M`+#4/0!`E[P`0)>\`(!OO0"`;[T`X)0]`."4/0"P\#T`L/`]``!=O0``7;T` -MT/Z]`-#^O0"`JCP`@*H\`$`G/0!`)ST`H.&]`*#AO0#0YKT`T.:]`,`T/0#` -M-#T``,4]``#%/0``9[P``&>\``!@N0``8+D`0(X]`$"./0`@$CT`(!(]`(`0 -M/`"`$#P``,&[``#!NP"`#+P`@`R\``#*/```RCP`(!`]`"`0/0!`HSP`0*,\ -M`"!GO0`@9[T`T-B]`-#8O0``?SP``'\\`""7/0`@EST`@`V]`(`-O0`PP[T` -M,,.]`(`(/0"`"#T`4,\]`%#//0#@:;T`X&F]`,"(O0#`B+T`6!L^`%@;/@!` -M%#X`0!0^`.!YO0#@>;T`$,^]`!#/O0"`#[P`@`^\`,"4/`#`E#P``,&\``#! -MO```M;P``+6\```!/````3P``)X[``">.P``.+H``#BZ``"8.@``F#H``"8[ -M```F.P"`LCP`@+(\`""-/0`@C3T``"X]```N/0`@3+T`($R]`""6O0`@EKT` -MX%X]`.!>/0!`M#T`0+0]`-",O0#0C+T`(*N]`""KO0#@CCT`X(X]`*`5/0"@ -M%3T`0-R]`$#\``!WO```9[P``&>\ -M``#$N@``Q+H`@"4\`(`E/```G[L``)^[`#"HO0`PJ+T`$+6]`!"UO0`@,ST` -M(#,]`*"L/0"@K#T`X`"]`.``O0``:KT``&J]`"!8/0`@6#T``"D]```I/0#` -MA;T`P(6]`(!VO`"`=KP`P`0^`,`$/@"0_ST`D/\]`.`/O0#@#[T`<(B]`'"( -MO0``_CL``/X[`(`8O`"`&+P`@#:]`(`VO0``++L``"R[```?/```'SP`8#:] -M`&`VO0"@5KT`H%:]`*`//0"@#ST`0/$\`$#Q/`"`L[P`@+.\`("1/0"`D3T` -M$)@]`!"8/0``2KT``$J]`,"7O0#`E[T`P"`]`,`@/0#@KCT`X*X]```UO``` -M-;P`P%F]`,!9O0"`,3T`@#$]`(`2/0"`$CT``&Z]``!NO0!@C;T`8(V]`(`& -M/`"`!CP`X`X]`.`./0"`E+P`@)2\`(`B/`"`(CP``%P]``!/```7CP`0"0]`$`D/0"`/;P`@#V\`$#PO`!`\+P`8%H]`&!: -M/0``P#T``,`]``!FO```9KP`@'.]`(!SO0"`X+P`@."\``"ZO```NKP``+,[ -M``"S.P"@.3T`H#D]`.`%O0#@!;T`0,>]`$#'O0!`CKP`0(Z\`("?/0"`GST` -M`'H[``!Z.P!`U[P`0->\`+#+/0"PRST`L/`]`+#P/0```KT```*]`,#-O0#` -MS;T``&,\``!C/`!@I3T`8*4]`,#%O`#`Q;P`4+R]`%"\O0``%#L``!0[`,`O -M/0#`+ST`@".]`(`CO0"`<[T`@'.]`$"D/`!`I#P`X`(]`.`"/0``-SP``#<\ -M`$#A/`!`X3P``$8[``!&.P"`4KP`@%*\`*`D/0"@)#T`$)4]`!"5/0``5[P` -M`%>\`$"#O0!`@[T`P"$]`,`A/0``.#T``#@]`*#8O0"@V+T`>`N^`'@+O@`` -MVSP``-L\`'#R/0!P\CT``#P[```\.P"`C+T`@(R]`&!W/0!@=ST`(*H]`""J -M/0"`E+P`@)2\``#\O```_+P`0$4]`$!%/0#@=3T`X'4]`$"ZO`!`NKP`H)*] -M`*"2O0!@;+T`8&R]`$"PO`!`L+P`P/X\`,#^/``@"CT`(`H]`$`SO0!`,[T` -MH'^]`*!_O0``F#H``)@Z`(!=/0"`73T`@#8\`(`V/```N#H``+@Z`+#?/0"P -MWST`$`$^`!`!/@"@8KT`H&*]`'#RO0!P\KT`H!8]`*`6/0!`D#T`0)`]`%") -MO0!0B;T`4,R]`%#,O0``A;L``(6[```\/```/#P`8$N]`&!+O0``S[P``,^\ -M`"`7/0`@%ST`8"P]`&`L/0!`&#T`0!@]`,#A/`#`X3P``'H[``!Z.P``$SP` -M`!,\`$"\/`!`O#P`@%8]`(!6/0``T;L``-&[`*"&O0"@AKT``%0\``!4/`!@ -M+3T`8"T]`-"VO0#0MKT`@/N]`(#[O0#@"ST`X`L]`##,/0`PS#T`0+B\`$"X -MO`!`1;T`0$6]``!]/0``?3T`0#,]`$`S/0!`M+P`0+2\```X.P``.#L`8&,] -M`&!C/0#`WCP`P-X\``#SO```\[P``%^]``!?O0#@/KT`X#Z]`(#*O`"`RKP` -MP.H\`,#J/`#P@3T`\($]```&.P``!CL`T(^]`-"/O0``#KP```Z\`/";/0#P -MFST``*L\``"K/`#`R+P`P,B\``!+/0``2ST`8)0]`&"4/0!@6[T`8%N]`!#. -MO0`0SKT``$<\``!'/`!`,ST`0#,]`,#$O`#`Q+P`P$Z]`,!.O0``R;P``,F\ -M``#ONP``[[L``+RZ``"\N@"`_3P`@/T\`(!E/0"`93T`@*P\`("L/```^CP` -M`/H\`."#/0#@@ST``"`[```@.P!@<[T`8'.]``#4NP``U+L`('0]`"!T/0!` -M_+P`0/R\`+#9O0"PV;T``"2\```DO`#PA#T`\(0]`"`OO0`@+[T``,"]``#` -MO0#`@SP`P(,\`#"\/0`PO#T`P,`\`,#`/```Z[P``.N\`$"N/`!`KCP`0%L] -M`$!;/0``E#P``)0\`$"1/`!`D3P`P!`]`,`0/0#`IKP`P*:\`.``O0#@`+T` -M`#6\```UO`"`<;T`@'&]`)"KO0"0J[T`@.X\`(#N/`!P\#T`]`$#I/`!`Z3P`D(\]`)"//0"`*;T`@"F]`!#/O0`0S[T``(Z[``". -MNP`0D#T`$)`]``"RO```LKP`D+&]`)"QO0#`C3P`P(T\`(#@/0"`X#T`8`,] -M`&`#/0#@+;T`X"V]```S/```,SP`P&<]`,!G/0#`DSP`P),\`(`F/`"`)CP` -M@%H\`(!:/`"@!KT`H`:]`$"BO`!`HKP``.BZ``#HN@#@?[T`X'^]`,"(O0#` -MB+T`P#0]`,`T/0#0JST`T*L]`*`4O0"@%+T`H+Z]`*"^O0``!3T```4]`##< -M/0`PW#T`P(4\`,"%/`!`V;P`0-F\`"!G/0`@9ST`()\]`""?/0!`1[T`0$>] -M`&"VO0!@MKT``*$\``"A/``@=ST`('<]`("8O`"`F+P`X%*]`.!2O0"`U;P` -M@-6\`("GO`"`I[P``+8[``"V.P!@(ST`8",]``"C/```HSP``/F[``#YNP#` -MYSP`P.<\`,!M/0#`;3T``(B[``"(NP"`=[T`@'>]`,"Y/`#`N3P`0)`]`$"0 -M/0#@!KT`X`:]`!"RO0`0LKT``*T[``"M.P`PAST`,(<]```#O0```[T`L(N] -M`+"+O0`@33T`($T]`#"?/0`PGST``-RZ``#\``!WO`"`U[P`@->\`,`CO0#`([T``",\```C/``P -MG3T`,)T]`.![/0#@>ST``,2Z``#$N@``>3P``'D\`!"?/0`0GST`@)T\`("= -M/`!`D[T`0).]``"$NP``A+L`P%4]`,!5/0`@/KT`(#Z]`-#(O0#0R+T``&Z\ -M``!NO`!@+#T`8"P]```'O```![P``+*\``"RO`!`VCP`0-H\```Q/0``,3T` -M@.0\`(#D/`!`UCP`0-8\``#8.P``V#L`@!2\`(`4O```+SP``"\\`&!$/0!@ -M1#T``!P\```O```'KP``!`Z```0.@`@1;T` -M($6]`.!CL``/`Z``#P.@`` -M-[P``#>\`(`YO0"`.;T``.8\``#F/`!0ZST`4.L]`,`P/0#`,#T`H#2]`*`T -MO0"`>SP`@'L\`%"G/0!0IST``,0Z``#$.@!0O+T`4+R]`(!-O`"`3;P`P&`] -M`,!@/0#``;T`P`&]`*#5O0"@U;T`P`6]`,`%O0"@9#T`H&0]`$##/`!`PSP` -M@,^\`(#/O```L3P``+$\`*!^/0"@?CT`@`,]`(`#/0"`;SP`@&\\``"QNP`` -ML;L`P.B\`,#HO```^#L``/@[`,`^/0#`/CT`P*F\`,"IO``PD+T`,)"]```H -MN@``*+H`P$P]`,!,/0!`Y[P`0.>\`%"*O0!0BKT`8!P]`&`;T`P'F]`,!BO0#`8KT``-X[``#>.P"` -MWCP`@-X\`("D/`"`I#P```\]```//0``"#L```@[`(#YO`"`^;P`8#`]`&`P -M/0!PQST`<,<]`(`0/`"`$#P`T(J]`-"*O0``$+P``!"\`*`J/0"@*CT`@+V\ -M`("]O`#PF+T`\)B]``#4.@``U#H`0%<]`$!7/0`@!KT`(`:]``#'O0``Q[T` -M`""[```@NP"@9ST`H&<]``#'.P``QSL``-0Z``#4.@!`>#T`0'@]`,![/0#` -M>ST`P)T\`,"=/```H#P``*`\``!5/```53P``#"]```PO0#`XKP`P.*\`$"B -M/`!`HCP`@-:\`(#6O`#`D+T`P)"]``#2O```TKP`H`0]`*`$/0``0+H``$"Z -M`$!/O0!`3[T``+8\``"V/``PI3T`,*4]``"5.P``E3L`(%N]`"!;O0``\CL` -M`/([`,"5/0#`E3T`H#`]`*`P/0``C3L``(T[```\/```/#P`0*&\`$"AO`#` -M8[T`P&.]``"NO```KKP```"[````NP"`J;P`@*F\`(!\O`"`?+P`@,@\`(#( -M/```V#L``-@[`&`DO0!@)+T``%@[``!8.P"0PST`D,,]`,!//0#`3ST`8&>] -M`&!GO0"`!;T`@`6]```J/0``*CT``#2[```TNP``9+T``&2]```6.P``%CL` -M`$H]``!*/0``[[L``.^[`.`^O0#@/KT``-^[``#?NP```#P````\```$.P`` -M!#L`0*(\`$"B/```A3P``(4\``"@N@``H+H``(8\``"&/`#@53T`X%4]``#! -M/```P3P`0'"]`$!PO0!`)[T`0">]`.`S/0#@,ST``(,[``"#.P#@R+T`X,B] -M`,!:O0#`6KT`P&$]`,!A/0"`S#P`@,P\`$`ZO0!`.KT`P-L\`,#;/`"PNCT` -ML+H]``#"/```PCP`H#2]`*`TO0``";P```F\`,#V/`#`]CP`P/0\`,#T/``` -M(#P``"`\`("!O`"`@;P`P`V]`,`-O0#`\;P`P/&\`(`+O`"`"[P``!.\```3 -MO```++T``"R]`,"JO`#`JKP`X"8]`.`F/0``H#L``*`[`.!-O0#@3;T`@*T\ -M`("M/`!X"#X`>`@^`/"'/0#PAST`@)"]`("0O0"@#KT`H`Z]`(`W/0"`-ST` -M0(:\`$"&O`"`EKT`@):]``#CO```X[P`P``]`,``/0``Y#L``.0[```1O0`` -M$;T```*\```"O```H#P``*`\``"'/```ASP``*P\``"L/```[SL``.\[```" -MO````KP``%,\``!3/`#`2ST`P$L]``!4/```5#P`((&]`""!O0#`SKP`P,Z\ -M`*!2/0"@4CT`P+B\`,"XO`#0W[T`T-^]`*`/O0"@#[T`X'4]`.!U/0"`;#P` -M@&P\`$`\`,"Z/`#`NCP`H`*]`*`"O0!0LKT`4+*]`$`8O0!` -M&+T`@'D\`(!Y/`"`S;P`@,V\`,`OO0#`+[T``)T\``"=/`!`,#T`0#`]``#L -M/```[#P`H#@]`*`X/0``#3T```T]`$"FO`!`IKP``"F\```IO``@&3T`(!D] -M``!VNP``=KL`0'*]`$!RO0"`C;P`@(V\`*`S/0"@,ST``(PZ``",.@"@F;T` -MH)F]`$`@O0!`(+T`0!H]`$`:/0``&#P``!@\`$#HO`!`Z+P`P*(\`,"B/`#@ -MD3T`X)$]``#>/```WCP`0)N\`$";O`"`9SP`@&<\``#Z.P``^CL``/>\``#W -MO`"`(KP`@"*\`$"=O`!`G;P`X)B]`."8O0"`3KT`@$Z]```:/0``&CT``&4] -M``!E/0``N[L``+N[``#9.P``V3L`\(`]`/"`/0"`DCP`@)(\`$!(O0!`2+T` -M``"\````O`!`BST`0(L]```./0``#CT``&6]``!EO0!@/KT`8#Z]`(!%/`"` -M13P`@'"\`(!PO`"`-[T`@#>]``!NO```;KP``+V[``"]NP!`N[P`0+N\``!( -MN@``2+H`@.\\`(#O/`"`O3P`@+T\`(#[/`"`^SP`,)$]`#"1/0"`0ST`@$,] -M`,`OO0#`+[T`H"N]`*`KO0#`[3P`P.T\``"<.P``G#L`D**]`)"BO0"@:[T` -MH&N]`(`[/0"`.ST`8`\]`&`//0"`\``"`.0``@#D` -M<*(]`'"B/0!`_#P`0/P\`/")O0#PB;T``(6\``"%O``0D#T`$)`]``!T/``` -M=#P`T(V]`-"-O0#`6+T`P%B]``!".P``0CL`@#@\`(`X/```PKL``,*[``!L -MNP``;+L``,2Z``#$N@``&KL``!J[`(!1/`"`43P``,H[``#*.P``>+H``'BZ -M`,#E/`#`Y3P`\(\]`/"//0"`-#T`@#0]`&`\O0!@/+T```Z]```.O0!@0#T` -M8$`]`(`,O`"`#+P`@-F]`(#9O0#`9+T`P&2]`.`N/0#@+CT`@`L\`(`+/`#@ -M@KT`X(*]``#CNP``X[L`P)@]`,"8/0!`%CT`0!8]```4O```%+P``$\\``!/ -M/```*ST``"L]`$"@/`!`H#P`@&.\`(!CO```_+L``/R[``#,.P``S#L``-4[ -M``#5.P"`ESP`@)<\`(!"O`"`0KP`$*Z]`!"NO0!@@;T`8(&]`(#?/`"`WSP` -MP-`\`,#0/`#`,[T`P#.]```%O```!;P`P)T]`,"=/0"@)3T`H"4]`$`9O0!` -M&;T`@(<\`("'/`#PG3T`\)T]`,"-/`#`C3P`@'B]`(!XO0"`/KT`@#Z]``!6 -M.P``5CL``/`Y``#P.0"`0[P`@$.\`(!W/`"`=SP`@`4\`(`%/`!`A+P`0(2\ -M```TNP``-+L``*4[``"E.P"`/[P`@#^\``#A.P``X3L`H$T]`*!-/0!`TCP` -M0-(\`*`HO0"@*+T`@'V\`(!]O`"`-3T`@#4]``"J.P``JCL`4(B]`%"(O0"@ -M*;T`H"F]`,#//`#`SSP``!(\```2/`"`$+T`@!"]```LNP``++L`X%,]`.!3 -M/0!`%CT`0!8]`,"$/`#`A#P`X!`]`.`0/0"`JSP`@*L\`$#;O`!`V[P``%J\ -M``!:O```BKL``(J[`,`8O0#`&+T`P!6]`,`5O0``@SP``(,\```+/```"SP` -M8'Z]`&!^O0#@0+T`X$"]`(`_/0"`/ST`($@]`"!(/0"`"+P`@`B\```HN@`` -M*+H`H&$]`*!A/0#`.CT`P#H]`$"AO`!`H;P``+L[``"[.P!`?CT`0'X]`(`$ -M/`"`!#P`X'J]`.!ZO0#@6;T`X%F]`.`]`(!,/`"`3#P``'\\``!_ -M/`!`SKP`0,Z\`(`XO`"`.+P`@`X]`(`./0!`*#T`0"@]`"`8/0`@&#T`H"8] -M`*`F/0``:#L``&@[`$"3O`!`D[P``'8[``!V.P"`C;P`@(V\`*!^O0"@?KT` -MX$"]`.!`O0#`HCP`P*(\``!] -M`(`RO0"`,KT``+>\``"WO```<3P``'$\`,">/`#`GCP`P(.\`,"#O```QKL` -M`,:[`"!Q/0`@<3T``((]``""/0``V;L``-F[``""NP``@KL`0(@]`$"(/0!` -MLCP`0+(\``"OO0``K[T`P&J]`,!JO0#`'3T`P!T]`$"M/`!`K3P`<(*]`'"" -MO0#`&;T`P!F]`,`%/0#`!3T`@'$\`(!Q/```M[P``+>\``"@N@``H+H``-<\ -M``#7/```\3P``/$\``!@/```8#P``'$\``!Q/```,#H``#`Z`,"&O`#`AKP` -M`(H[``"*.P``O#H``+PZ`.`ZO0#@.KT`@`2]`(`$O0``YCP``.8\`("+/`"` -MBSP`0%B]`$!8O0#`QKP`P,:\`/"A/0#PH3T``(4]``"%/0``K+P``*R\`(`_ -MO`"`/[P`X``]`.``/0``+#L``"P[`"`GO0`@)[T``,>\``#'O`!`@#P`0(`\ -M``#^NP``_KL`0$"]`$!`O0#`]KP`P/:\``"YNP``N;L``*$[``"A.P!@`3T` -M8`$]`&`//0!@#ST``-0Z``#4.@``>[P``'N\`.`//0#@#ST`P&,]`,!C/0`` -MIKL``*:[```/```\CL``/([``#H.@``Z#H`@`P\`(`,/```-[P` -M`#>\`&`/O0!@#[T`P,:\`,#&O```:+P``&B\`$`HO0!`*+T`@,Z\`(#.O``` -M0#T``$`]`$`P/0!`,#T`P/N\`,#[O`!@"+T`8`B]`,`X/0#`.#T`X'4]`.!U -M/0"`4KP`@%*\`,#KO`#`Z[P````]````/0"`>CP`@'H\`'"#O0!P@[T`(&2] -M`"!DO0#`PCP`P,(\`,#5/`#`U3P`P*B\`,"HO`#`G+P`P)R\```O/```+SP` -M`'8\``!V/`#`E#P`P)0\`$#_/`!`_SP``'@\``!X/```XKL``.*[`(!Y/`"` -M>3P``%,\``!3/```-[T``#>]`*!EO0"@9;T``)BZ``"8N@``=CP``'8\`&`Z -MO0!@.KT`H$"]`*!`O0"`^SP`@/L\`&`5/0!@%3T``+F\``"YO```)CL``"8[ -M`/",/0#PC#T`X'T]`.!]/0``.CL``#H[``#=NP``W;L`@`P\`(`,/```7KP` -M`%Z\`(#TO`"`]+P`@&6\`(!EO`"`UKP`@-:\```^O0``/KT`8`&]`&`!O0`` -M0[P``$.\`(",O`"`C+P``).[``"3NP``73T``%T]``"`/0``@#T``#"\```P -MO`"`EKP`@):\``!3/0``4ST`8%(]`&!2/0``'+L``!R[``!-O```3;P``.@[ -M``#H.P!`L[P`0+.\`+")O0"PB;T``#*]```RO0``@+D``("Y```6O```%KP` -M`!2\```4O`"``SP`@`,\`("%/`"`A3P``.H[``#J.P#`M3P`P+4\`.!;/0#@ -M6ST`0,,\`$##/`!`YKP`0.:\``"+.P``BSL``+4\``"U/`#`"[T`P`N]`&!M -MO0!@;;T``"8[```F.P!`ZCP`0.H\``#WO```][P``":]```FO0"`HSP`@*,\ -M`,#//`#`SSP``$>\``!'O```=#L``'0[`&`\/0!@/#T`8#(]`&`R/0``P3L` -M`,$[``"DN@``I+H``.F[``#INP``"+T```B]``#AO```X;P``/`[``#P.P!` -MC+P`0(R\`$!NO0!`;KT``*J\``"JO`!`!#T`0`0]``"$.P``A#L`@`Z\`(`. -MO`!`6ST`0%L]`+"6/0"PECT`@!*\`(`2O`!@-;T`8#6]``"3/```DSP``#H] -M```Z/0``A+L``(2[`$#LO`!`[+P``'^\``!_O```>KP``'J\`.`5O0#@%;T` -M`/>\``#WO```X+H``."Z``!@.P``8#L```H\```*/`"`C#P`@(P\``"HN@`` -MJ+H``(F[``")NP`@,ST`(#,]``!K/0``:ST``+@[``"X.P"`!+T`@`2]``#[ -M.P``^SL`0(\\`$"//`#`1KT`P$:]`,"#O0#`@[T``"P\```L/`!`"3T`0`D] -M``!?O```7[P`H`*]`*`"O0#`NSP`P+L\`.`M/0#@+3T``,Z[``#.NP"`-[P` -M@#>\`$#K/`!`ZSP`@+,\`("S/````#H````Z`(`GO`"`)[P`@+.\`("SO``` -MP[P``,.\```\.P``/#L`P,,\`,##/`!`P;P`0,&\`.!DO0#@9+T``"P[```L -M.P!@!#T`8`0]``#%NP``Q;L``#`Z```P.@`0ACT`$(8]`$"9/0!`F3T`0*>\ -M`$"GO``@6[T`(%N]`$"U/`!`M3P```\]```//0"`&[T`@!N]``!,O0``3+T` -M``Z\```.O`"`$+P`@!"\```TO0``-+T`P(2\`,"$O`!`S3P`0,T\`("E/`"` -MI3P``$@\``!(/`"`@3P`@($\``!(NP``2+L``)\[``"?.P``3#T``$P]`&!P -M/0!@<#T``*([``"B.P``SKP``,Z\``##.P``PSL``/$[``#Q.P!@8KT`8&*] -M`)"4O0"0E+T``,^[``#/NP#`ACP`P(8\`(#BO`"`XKP`0.*\`$#BO`"`\3P` -M@/$\`.`:/0#@&CT``%8[``!6.P``,SP``#,\`,`R/0#`,CT`0,\\`$#//``` -M@+@``("X``"\``"3.P``DSL`@#@\`(`X -M/`#`G+P`P)R\`&!(O0!@2+T`@)N\`(";O```Y3L``.4[`,"#O`#`@[P``"*\ -M```BO`!@33T`8$T]`"!8/0`@6#T`@#&\`(`QO`#`U[P`P->\`,#B/`#`XCP` -MH`L]`*`+/0#`C+P`P(R\`,"QO`#`L;P`@`R\`(`,O`!`!KT`0`:]`(#[O`"` -M^[P``'0\``!T/```ZCP``.H\``#1.P``T3L``'P[``!\.P"``CT`@`(]``"6 -M.P``ECL`P/J\`,#ZO`"`@#P`@(`\`&!0/0!@4#T``.RZ``#LN@#`'KT`P!Z] -M``"UNP``M;L`P(\\`,"//`"@"KT`H`J]`,!1O0#`4;T``%`[``!0.P"`*CP` -M@"H\`,"@O`#`H+P``)J[``":NP"`Z#P`@.@\`.`3/0#@$ST``-,\``#3/``` -M%ST``!<]`.`D/0#@)#T``/`Y``#P.0``=KP``':\``"ANP``H;L```B]```( -MO0!@>+T`8'B]`$"XO`!`N+P`@.\\`(#O/```!#L```0[`$!%O0!`1;T`@!2\ -M`(`4O`"@'ST`H!\]``!$/```1#P``.&[``#ANP`@#CT`(`X]`&!./0!@3CT` -M`"(\```B/`"`AKP`@(:\``!7/```5SP`0-X\`$#>/```'KL``!Z[`,#IO`#` -MZ;P`H!F]`*`9O0#`2;T`P$F]`*`(O0"@"+T`@`\\`(`//`!`J3P`0*D\`(`$ -M/`"`!#P`@/@\`(#X/`#@9ST`X&<]```^/```/CP`P/&\`,#QO`"`ESP`@)<\ -M`.!(/0#@2#T``*N\``"KO``P@KT`,(*]`(!5O`"`5;P`P)D\`,"9/`#`L+P` -MP+"\`"`8O0`@&+T``-0[``#4.P"`R#P`@,@\``"2NP``DKL``)V[``"=NP!` -MNSP`0+L\`(#=/`"`W3P`P.4\`,#E/`"@(#T`H"`]``#`/```P#P``!*\```2 -MO```-;P``#6\``!^NP``?KL`(%*]`"!2O0`@H[T`(*.]``!&O```1KP`P/(\ -M`,#R/`"`FKP`@)J\```4O0``%+T`@,L\`(#+/`#0B#T`T(@]`(#*/`"`RCP` -M`*2Z``"DN@!`13T`0$4]`&!&/0!@1CT`P(.\`,"#O`#@(+T`X""]``#\`(`7O`"`?CP`@'X\```+/```"SP``%8[``!6 -M.P!`@#P`0(`\`(`R/`"`,CP```Z\```.O```2KL``$J[``"[.P``NSL`P.N\ -M`,#KO`#@-[T`X#>]``!H.@``:#H`0+P\`$"\/```=KP``':\``#4O```U+P` -M0-P\`$#]`(!'O0"`0KT`@$*]`(`1O`"`$;P``":\```FO`"`:+P`@&B\``!8 -M.@``6#H``):[``"6NP!`Y+P`0.2\`(`4O`"`%+P`@`8]`(`&/0#`[SP`P.\\ -M```6NP``%KL``%,\``!3/`!`%CT`0!8]```R/```,CP`@"V\`(`MO```G3P` -M`)T\`"`@/0`@(#T`@*.\`("CO`#@B+T`X(B]`,`%O0#`!;T``%&\``!1O`!` -M"KT`0`J]`(!OO`"`;[P`P,0\`,#$/`#`X#P`P.`\``">.P``GCL`0(8\`$"& -M/`!`!CT`0`8]``!@.P``8#L``-^[``#?NP``M3P``+4\``#K.P``ZSL`P.B\ -M`,#HO`"`JKP`@*J\`$"!/`!`@3P``$B[``!(NP#`0KT`P$*]``!8O```6+P` -M8`X]`&`./0``K;L``*V[`,`7O0#`%[T```J[```*NP`@"ST`(`L]``#:/``` -MVCP``)`\``"0/`"`_CP`@/X\``!2/```4CP``,6\``#%O`!`R;P`0,F\`$"> -MO`!`GKP`@/B\`(#XO```@;P``(&\``#`.0``P#D``#V\```]O``@`KT`(`*] -M``##.P``PSL``&H]``!J/0"`#CT`@`X]`(`FO`"`)KP`@'D\`(!Y/`#`!#T` -MP`0]`(`0O`"`$+P`H`*]`*`"O0``7CP``%X\`"`:/0`@&CT``'F\``!YO`!` -M:+T`0&B]`"`*O0`@"KT`@("\`("`O`#`N;P`P+F\``"?O```G[P```BZ```( -MN@"`,3P`@#$\`("./`"`CCP`8!H]`&`:/0`@)ST`("<]`(!Y/`"`>3P`@)4\ -M`("5/`#@%ST`X!<]``"%NP``A;L`@&2]`(!DO0``[[P``.^\``!9/```63P` -MP-V\`,#=O`!`:KT`0&J]``#,N@``S+H``"T]```M/0``1;P``$6\`$!%O0!` -M1;T``"8[```F.P"@+CT`H"X]`(!'/`"`1SP``(L[``"+.P``"#T```@]`.`2 -M/0#@$CT``.N[``#KNP``L;P``+&\``"VNP``MKL`@'^\`(!_O`!`J+P`0*B\ -M``"VNP``MKL`P-:\`,#6O`!@)+T`8"2]`(`#/`"``SP`H%P]`*!]`&`7O0`@%+T`(!2]`(!S/`"` -M#H``'@Z``#\NP``_+L`0,0\`$#$/`!`T#P`0-`\``!ZNP``>KL` -M`/RZ``#\N@``CSP``(\\``!8.@``6#H`H#R]`*`\O0#@%+T`X!2]```LNP`` -M++L``&6\``!EO`"`]KP`@/:\``"*.P``BCL`X!8]`.`6/0``\#P``/`\``!, -M/```3#P`@.`\`(#@/`#`[SP`P.\\`(`X/`"`.#P``"T\```M/```7+L``%R[ -M`*`/O0"@#[T`H!R]`*`O0"`+[P`@"^\`,#V/`#`]CP``(^[``"/NP#`UKP`P-:\``!H -M/```:#P`0"$]`$`A/0"`03P`@$$\```5O```%;P`@&L\`(!K/`!`RSP`0,L\ -M``#%.P``Q3L``'R[``!\NP``V[L``-N[`$#,O`!`S+P`@`F]`(`)O0!`E[P` -M0)>\``!4O```5+P``("\``"`O```'CP``!X\`.`4/0#@%#T`@$D\`(!)/``` -M@[P``(.\``#&/```QCP`4($]`%"!/0!`W#P`0-P\`(!YO`"`>;P``!J[```: -MNP"`-3P`@#4\``"/O```C[P`0""]`$`@O0``\[L``/.[``#*.P``RCL`0/R\ -M`$#\O```++T``"R]`(!PO`"`<+P``(T[``"-.P``63P``%D\``#[/```^SP` -M8!0]`&`4/0!`BSP`0(L\``"2/```DCP`P!X]`,`>/0"`,3P`@#$\``"9O``` -MF;P``*0[``"D.P``*CP``"H\`$`*O0!`"KT`P%.]`,!3O0"`-KP`@#:\``!# -M/```0SP`@*Z\`("NO`"`T+P`@-"\`,"L/`#`K#P`@.,\`(#C/```<#H``'`Z -M``"..P``CCL`@.T\`(#M/`!`SCP`0,X\``"[.P``NSL`P(P\`,",/`#`BCP` -MP(H\`,#6O`#`UKP`0!6]`$`5O0``]KL``/:[```0O```$+P`@)V\`("=O``` -MQ+H``,2Z`,"M/`#`K3P``#X[```^.P#`BKP`P(J\`,"2/`#`DCP`8$8]`&!& -M/0!`KSP`0*\\``"ANP``H;L``/0Z``#T.@``4#L``%`[`,"YO`#`N;P`@.2\ -M`(#DO```J#L``*@[```".P```CL`(`.]`"`#O0#`M;P`P+6\``"].P``O3L` -M`)@[``"8.P"`##P`@`P\`&`=/0!@'3T`H"P]`*`L/0``JSL``*L[``"-NP`` -MC;L`@-\\`(#?/```KSP``*\\``";O```F[P``+"\``"PO```(+L``""[``"P -MO```L+P`8$*]`&!"O0#`U+P`P-2\``"!.P``@3L``&B[``!HNP``N+L``+B[ -M``#8/```V#P`P/X\`,#^/`!`BCP`0(H\`(#:/`"`VCP`P.\\`,#O/```6#L` -M`%@[``!@N@``8+H``'0\``!T/````````````$`OO0!`+[T`P"&]`,`AO0`` -ML+D``+"Y``")NP``B;L``-F\``#9O```:+L``&B[`,#?/`#`WSP`@"0\`(`D -M/`"`[``#WNP`` -MO;P``+V\`"!PO0`@<+T`8$:]`&!&O0``K;L``*V[``#3NP``T[L``+2\``"T -MO```E#P``)0\`$!6/0!`5CT`@,,\`(##/```AKL``(:[`$#H/`!`Z#P`P&(] -M`,!B/0``LSP``+,\``"-O```C;P`P(6\`,"%O`#`A+P`P(2\`$#%O`!`Q;P` -M0*R\`$"LO```2KP``$J\``!TO```=+P`0(R\`$",O```0+P``$"\``#.NP`` -MSKL``'*[``!RNP"`TCP`@-(\`,!1/0#`43T`@+H\`("Z/```%+P``!2\``#> -M.P``WCL`0+4\`$"U/```Y+L``.2[`$`$O0!`!+T`@'N\`(![O```$+H``!"Z -M`$#IO`!`Z;P`P`&]`,`!O0``[CL``.X[``#R/```\CP``#$\```Q/```V#H` -M`-@Z`$"0/`!`D#P`P*D\`,"I/```?KL``'Z[```,NP``#+L`@`L\`(`+/`"` -M,+P`@#"\`("0O`"`D+P```0[```$.P``Z+L``.B[`(`4O0"`%+T`P/>\`,#W -MO```Q#L``,0[``#Q.P``\3L```:\```&O`#`HCP`P*(\`&`O/0!@+ST`@&T\ -M`(!M/```7KL``%Z[``"./```CCP`0/8\`$#V/```:#H``&@Z``#\``"7O```6CL` -M`%H[``!#/```0SP``%T\``!=/`#`QCP`P,8\``#*/```RCP``-([``#2.P`` -M'+L``!R[`(`V/`"`-CP``#X[```^.P!`\KP`0/*\``#>O```WKP``(X[``". -M.P``<+H``'"Z`$#AO`!`X;P`0*F\`$"IO`"`43P`@%$\``!G/```9SP``-V[ -M``#=NP``H3L``*$[```"/0```CT`0(`\`$"`/```H+D``*"Y`(!Q/`"`<3P` -M@,<\`(#'/```<#L``'`[`(!8O`"`6+P``,V\``#-O``@-[T`(#>]`*!&O0"@ -M1KT`@"2\`(`DO```!3P```4\`(!4O`"`5+P``*N[``"KNP"`_SP`@/\\`,`$ -M/0#`!#T``,H[``#*.P"`@CP`@((\```^/0``/CT`@+T\`("]/`#`^[P`P/N\ -M`(">O`"`GKP``(L\``"+/```4CL``%([`,#]O`#`_;P`P.^\`,#OO`#`@[P` -MP(.\`(#:O`"`VKP`(!.]`"`3O0"`-[P`@#>\```B/```(CP``*<\``"G/``` -M\SP``/,\`(#5/`"`U3P`0*H\`$"J/`#`WSP`P-\\``#"/```PCP``.`Z``#@ -M.@!`Z;P`0.F\`,#/O`#`S[P``'Z[``!^NP``9;P``&6\`*`0O0"@$+T`P)"\ -M`,"0O`"`23P`@$D\`(`M/`"`+3P`@">\`(`GO```R#H``,@Z``"Z/```NCP` -M`-([``#2.P``#[P```^\``#4.P``U#L`@+<\`("W/```@3P``($\```TNP`` -M-+L`0*.\`$"CO`!`R[P`0,N\`("SO`"`L[P``/"[``#PNP``E;L``)6[`(`O -MO`"`+[P``$8[``!&.P#`VSP`P-L\`("/`"`J3P`@*D\`(#2/`"`TCP`@"X\`(`N -M/```\SL``/,[`(!@/`"`8#P``(R[``",NP"`';T`@!V]`*`HO0"@*+T`P+.\ -M`,"SO`"`[KP`@.Z\`"`RO0`@,KT``%.\``!3O`#`JSP`P*L\`,#5/`#`U3P` -MP,8\`,#&/`!@$#T`8!`]`$`9/0!`&3T``&H\``!J/```&[P``!N\``"4N@`` -ME+H``(([``"".P"`$KP`@!*\`$"%O`!`A;P``(R\``",O```N;P``+F\`$#< -MO`!`W+P`@#Z\`(`^O```Y3L``.4[``!2NP``4KL`@!2\`(`4O```1SP``$<\ -M`,"V/`#`MCP`0+$\`$"Q/```ZSP``.L\`,`"/0#``CT``$8\``!&/`!`QKP` -M0,:\`"`7O0`@%[T``-&\``#1O`"`OKP`@+Z\`("BO`"`HKP``!J\```:O``` -M;KL``&Z[``!>.P``7CL`0*<\`$"G/`!``3T`0`$]`$"F/`!`ICP``-X[``#> -M.P"`FSP`@)L\``!9/```63P``'2\``!TO`"`2+P`@$B\`(`@/`"`(#P``):[ -M``"6NP"@![T`H`>]`"`!O0`@`;T``#B\```XO`"`=[P`@'>\`$#9O`!`V;P` -M`/R[``#\NP!`CSP`0(\\`,#-/`#`S3P```(]```"/0"`(ST`@",]`*`%/0"@ -M!3T`@%T\`(!=/```H#D``*`Y``"UNP``M;L`@+*\`("RO`!`W[P`0-^\`(!5 -MO`"`5;P`0*&\`$"AO`!`(;T`0"&]`,"TO`#`M+P`P)`\`,"0/`"`RCP`@,H\ -M```:NP``&KL```R\```,O`"`ACP`@(8\``"H/```J#P``"@[```H.P"`(SP` -M@",\`$`&/0!`!CT``,4\``#%/```[``#WNP``>SP``'L\`(!E/`"` -M93P``.4[``#E.P``43P``%$\`$"%/`!`A3P`@&<\`(!G/```U#H``-0Z`(!H -MO`"`:+P`0+F\`$"YO```KKP``*Z\`("CO`"`H[P`@`N]`(`+O0!`#[T`0`^] -M``"H.@``J#H`@`<]`(`'/0``]CP``/8\`("?/`"`GSP`0+L\`$"[/```X3P` -M`.$\``"_.P``OSL`@"^\`(`OO```^CL``/H[`("//`"`CSP```2\```$O`!` -MY[P`0.>\``"WO```M[P`@`6\`(`%O```(;P``"&\``"8O```F+P`0(Z\`$". -MO`"`9KP`@&:\``#\```WO`!`FKP`0)J\``#BNP``XKL` -M@(&\`("!O`#@"KT`X`J]`$"]O`!`O;P``+0Z``"T.@``@CL``(([``"1NP`` -MD;L``-8[``#6.P``HSP``*,\`(`H/`"`*#P```8[```&.P"`8SP`@&,\`("% -M/`"`A3P``*\[``"O.P```KL```*[``#P.@``\#H``-2Z``#4N@"`=KP`@':\ -M`,"[O`#`N[P`@+2\`("TO```Z;P``.F\``"7O```E[P``"P\```L/`#`U3P` -MP-4\`(#(/`"`R#P`0+<\`$"W/`"`KSP`@*\\`$";/`!`FSP``$0\``!$/``` -MJCL``*H[``#4NP``U+L``+V\``"]O```Y+P``.2\`,#CO`#`X[P`0+Z\`$"^ -MO```S+L``,R[``"+.P``BSL`````````````"KP```J\``#ZNP``^KL``&<\ -M``!G/`!`Y3P`0.4\`,#&/`#`QCP`P+T\`,"]/```QSP``,<\`(!^/`"`?CP` -M`*`Y``"@.0"`8KP`@&*\``!?O```7[P`P+>\`,"WO`!@%KT`8!:]`$`7O0!` -M%[T`0*N\`$"KO```H+D``*"Y`(!O/`"`;SP``'@\``!X/`#`HSP`P*,\`("E -M/`"`I3P`@'X\`(!^/```1CP``$8\``"`N0``@+D`@`.\`(`#O```@+@``("X -M``!(.@``2#H`@"F\`(`IO`"`3[P`@$^\```NNP``+KL``%"[``!0NP``T;P` -M`-&\`$"SO`!`L[P`@"T\`(`M/`!`R#P`0,@\`(`W/`"`-SP``*@[``"H.P"` -M@3P`@($\`("^/`"`OCP``*D[``"I.P``'KP``!Z\``!)O```2;P`@*^\`("O -MO``@%+T`(!2]`,`.O0#`#KT`@(B\`("(O`"`+#P`@"P\`,"Y/`#`N3P``)$\ -M``"1/`"`'CP`@!X\``"*/```BCP`@-X\`(#>/`"`L#P`@+`\``#-.P``S3L` -M`%X[``!>.P``$+H``!"Z`(!_O`"`?[P`@-B\`(#8O`!`RKP`0,J\`(!ZO`"` -M>KP``(*\``""O````KT```*]`"`-O0`@#;T``+Z[``"^NP!`H#P`0*`\`$#8 -M/`!`V#P``+T\``"]/`#`"CT`P`H]`(`P/0"`,#T`P/$\`,#Q/```J3L``*D[ -M``!\NP``?+L`@""\`(`@O`#`G;P`P)V\`(`:O0"`&KT``"*]```BO0#`K[P` -MP*^\`(`PO`"`,+P``%N\``!;O`"`.+P`@#B\```".P```CL`P,@\`,#(/``` -MSCP``,X\`(`7/`"`%SP`@"@\`(`H/`"`JSP`@*L\`,"3/`#`DSP``)([``"2 -M.P``0#D``$`Y``"$.P``A#L`@$6\`(!%O``@&;T`(!F]`*`7O0"@%[T`P*>\ -M`,"GO```J+H``*BZ``"F.P``ICL``/PZ``#\.@``XCL``.([``".P``E[L``)>[``!.O```3KP`0(J\ -M`$"*O`"`O;P`@+V\`,#"O`#`PKP``$F\``!)O```$+L``!"[``#4.P``U#L` -M`"`\```@/`"`%CP`@!8\`(`;/`"`&SP``&<\``!G/`#`NSP`P+L\`,")/`#` -MB3P``,"Z``#`N@``3[P``$^\``"*O```BKP``)>\``"7O```7+P``%R\`(!` -MO`"`0+P``(:[``"&NP``L+H``+"Z``"BNP``HKL``+PZ``"\.@"`ACP`@(8\ -M`("D/`"`I#P`@&$\`(!A/```7#L``%P[``!F.P``9CL``&`[``!@.P``'KL` -M`!Z[`(`%O`"`!;P`P(&\`,"!O```I+P``*2\`(!TO`"`=+P`@("\`("`O``` -M<;P``'&\``##NP``P[L``,$[``#!.P"`;#P`@&P\``!$/```1#P``)@\``"8 -M/`"`'#T`@!P]``#V/```]CP``/"Y``#PN0``&;P``!F\``!@N0``8+D``*J[ -M``"JNP#`ZKP`P.J\``#OO```[[P`@'6\`(!UO`#`MKP`P+:\``#XO```^+P` -M@!^\`(`?O`"`8CP`@&(\`,"O/`#`KSP``#X\```^/```(CP``"(\`,";/`#` -MFSP`0,D\`$#)/`!`VCP`0-H\`("R/`"`LCP``!([```2.P"`-[P`@#>\`(!Q -MO`"`<;P`@+F\`("YO`"@`;T`H`&]``#/O```S[P`@`^\`(`/O`"`*;P`@"F\ -M`("?O`"`G[P``)2Z``"4N@``Y#P``.0\`,#J/`#`ZCP``%<\``!7/```'SP` -M`!\\``"B/```HCP`P($\`,"!/```L[L``+.[``!$O```1+P``.2Z``#DN@`` -M\KL``/*[``"PO```L+P`@+6\`("UO`"`&+P`@!B\``#,.@``S#H``!@[```8 -M.P``(+D``""Y``"N.P``KCL`@)P\`("\`,"ZO`#`NKP`0,2\`$#$O```UKP``-:\ -M`(`,O`"`#+P```0\```$/```.CL``#H[``#7NP``U[L`@$@\`(!(/`"`'3T` -M@!T]`$#D/`!`Y#P``#D\```Y/`#`F3P`P)D\`,"H/`#`J#P``)"Z``"0N@!` -ME+P`0)2\`(`HO`"`*+P``":\```FO`!`![T`0`>]`,`%O0#`!;T``!Z\```> -MO```_+H``/RZ``"`N@``@+H``#P\```\/`!`S#P`0,P\`$"^/`!`OCP``'(\ -M``!R/`"`J#P`@*@\`("\``#\NP``_+L``+PZ``"\.@``"+H```BZ``!0.P``4#L`@!X\ -M`(`>/```2CL``$H[``#0.P``T#L`@.8\`(#F/`"`LSP`@+,\```"NP```KL` -M`.,[``#C.P#`]3P`P/4\``!#/```0SP`P,6\`,#%O`#`SKP`P,Z\``"JNP`` -MJKL``)>\``"7O```$+T``!"]`,"5O`#`E;P``(L[``"+.P``%CL``!8[``"N -M.P``KCL`0-H\`$#:/`#`(#T`P"`]`(#!/`"`P3P``&L\``!K/```3#P``$P\ -M``"=NP``G;L``#B\```XO```W3L``-T[``"0.P``D#L`0+*\`$"RO`"`T;P` -M@-&\``"WNP``M[L``,F[``#)NP#`P+P`P,"\``"@NP``H+L`@,(\`(#"/`"` -M&SP`@!L\`(`KO`"`*[P`0(0\`$"$/`"@.3T`H#D]`$#K/`!`ZSP``+"Z``"P -MN@``K#L``*P[``"'.P``ASL`0.J\`$#JO`!@#KT`8`Z]```&O```!KP``!*\ -M```2O`"`J+P`@*B\``!VNP``=KL`0+8\`$"V/`"`A#P`@(0\```$/```!#P` -M0-`\`$#0/`"`SSP`@,\\```VNP``-KL``%*[``!2NP``@3P``($\``#F.P`` -MYCL`@)B\`("8O`!`@;P`0(&\``"@.@``H#H``(F\``")O`"`$+T`@!"]`(!C -MO`"`8[P``"`Z```@.@``9KP``&:\```NNP``+KL```T]```-/0``/#T``#P] -M`$#8/`!`V#P``,<\``#'/```V#P``-@\``#`N```P+@``+^\``"_O`"`+KP` -M@"Z\```,O```#+P`0/R\`$#\O`#@%+T`X!2]``!#O```0[P``*"Y``"@N0"` -M#+P`@`R\``#$.@``Q#H``+(\``"R/```]SL``/<[``#8NP``V+L``)`\``"0 -M/``@$CT`(!(]`("C/`"`HSP``"(\```B/`#`D3P`P)$\``!$.P``1#L``.V\ -M``#MO`"`UKP`@-:\``#LNP``[+L`@+Z\`("^O```#[T```^]``#`NP``P+L` -MP*<\`,"G/`"`&CP`@!H\``#K.P``ZSL`@`D]`(`)/0"@`ST`H`,]`(`NO`"` -M+KP`@'^\`(!_O```.SP``#L\``!X.@``>#H`0.N\`$#KO`!`A;P`0(6\``#T -M.P``]#L``#Z[```^NP``@+P``("\``"9.P``F3L`@#<\`(`W/````KP```*\ -M``!8.@``6#H`P-P\`,#\``#1.P``T3L`@+\\`("_/```UCL``-8[``"0/``` -MD#P``&0]``!D/0#`.CT`P#H]``"NNP``KKL`@!V\`(`=O```8CP``&(\`(!` -MO`"`0+P`P$Z]`,!.O0`@#[T`(`^]``#*NP``RKL`@"Z\`(`NO`"`>KP`@'J\ -M`(`T/`"`-#P`@+`\`("P/```.#H``#@Z``#L.@``[#H`P+<\`,"W/`"`+SP` -M@"\\``"[``!@.P``8#L`@)V\`("=O`"`";P`@`F\`$"R/`!`LCP`@#L\`(`[/``` -MD[L``).[`$"^/`!`OCP`0"H]`$`J/0``B3L``(D[`(".O`"`CKP`@#D\`(`Y -M/```B#L``(@[`(!,O0"`3+T`($&]`"!!O0``V#H``-@Z``"`.```@#@`H`>] -M`*`'O0"`-+P`@#2\`.`)/0#@"3T`@,D\`(#)/```.#L``#@[`$#A/`!`X3P` -M@!4]`(`5/0``\+H``/"Z`,"8O`#`F+P``-4[``#5.P``ZCL``.H[`("DO`"` -MI+P``(F\``")O```$#H``!`Z``#(O```R+P`("J]`"`JO0``PKL``,*[`(!L -M/`"`;#P``!B\```8O```K;L``*V[`,`(/0#`"#T`@`,]`(`#/0``T#D``-`Y -M`(`^/`"`/CP`8!(]`&`2/0``0+D``$"Y`*`DO0"@)+T`P*V\`,"MO```G+H` -M`)RZ``#,O```S+P`@.R\`(#LO`"``CP`@`(\`,"//`#`CSP`@!:\`(`6O``` -M=CL``'8[`(`*/0"`"CT`@%H\`(!:/```5;P``%6\`(`?/`"`'SP`@+,\`("S -M/```+KP``"Z\`$"]O`!`O;P``)X[``">.P``2KL``$J[`"`NO0`@+KT``/*\ -M``#RO```(CP``"(\```*NP``"KL``+N\``"[O```*CP``"H\`&`N/0!@+CT` -M@*8\`("F/`"``CP`@`(\```E/0``)3T`8`H]`&`*/0!`J+P`0*B\`(#OO`"` -M[[P````Z````.@!`@KP`0(*\`$!`O0!`0+T`P,R\`,#,O```&#L``!@[`$#* -MO`!`RKP`P`Z]`,`.O0``<#P``'`\`$`>/0!`'CT``*0[``"D.P``9CL``&8[ -M`*`H/0"@*#T`P!<]`,`7/0``M+H``+2Z``"V.P``MCL`0/$\`$#Q/```4+L` -M`%"[`&`MO0!@+;T`0+^\`$"_O`"`(;P`@"&\```7O0``%[T`8`^]`&`/O0`` -M$#L``!`[``!N.P``;CL``(^\``"/O```+3P``"T\`,`]/0#`/3T`0*,\`$"C -M/```[KL``.Z[`,"Z/`#`NCP`0`L]`$`+/0"`)KP`@":\``#EO```Y;P``.4[ -M``#E.P```CP```(\`&`,O0!@#+T`0/B\`$#XO```.SP``#L\``"`.P``@#L` -MP+^\`,"_O```H[L``*.[`$";/`!`FSP``*$[``"A.P``'#L``!P[``#5/``` -MU3P`0)`\`$"0/`"`A;P`@(6\``#LNP``[+L``%0\``!4/```>KP``'J\`.`0 -MO0#@$+T``*RZ``"LN@``9#P``&0\``#$O```Q+P``.6\``#EO```ZSP``.L\ -M`"`Q/0`@,3T``'"[``!PNP``W[L``-^[`(#W/`"`]SP``(H\``"*/`!`MKP` -M0+:\```;O```&[P``($\``"!/```+KP``"Z\`(`5O0"`%;T`@%*\`(!2O``` -M4KL``%*[``#/O```S[P``&.\``!CO```DSP``),\``#O.P``[SL``#2\```T -MO`"`K3P`@*T\`.!$/0#@1#T``#0\```T/```5;P``%6\`(#!/`"`P3P``-@\ -M``#8/```\;P``/&\`"`>O0`@'KT``)H[``":.P``H+H``*"Z`*`ZO0"@.KT` -M0.^\`$#OO```FCP``)H\``#5.P``U3L`0*:\`$"FO`"``3P`@`$\```%/0`` -M!3T`@"0\`(`D/```,CL``#([`,#B/`#`XCP``)P\``"NP``!CL` -M``8[`(`4/0"`%#T``&`\``!@/`#`L+P`P+"\```&NP``!KL`@'X\`(!^/`!` -MJ[P`0*N\`(`XO0"`.+T``'>\``!WO```4+H``%"Z``"UO```M;P``!6\```5 -MO`!`VSP`0-L\`$"'/`!`ASP``)N[``";NP``KSP``*\\```E/0``)3T``,([ -M``#".P``4[P``%.\`("'/`"`ASP``%(\``!2/```![T```>]`*`!O0"@`;T` -M`-`Z``#0.@#`B+P`P(B\`.!.O0#@3KT`0,"\`$#`O`!`ASP`0(<\``!`N0`` -M0+D`@&^\`(!OO`!`UCP`0-8\`"`\/0`@/#T`@&4\`(!E/`"`*3P`@"D\`&`6 -M/0!@%CT``((\``""/```][P``/>\``"QO```L;P``&X[``!N.P#`RKP`P,J\ -M`.`ZO0#@.KT``/B[``#XNP``\SL``/,[`(#JO`"`ZKP`@-&\`(#1O`"`S3P` -M@,T\`&`!/0!@`3T``)R[``";P`@'F\```"/````CP`0)$\`$"1/```M;P``+6\`&`9O0!@&;T``/.[``#S -MNP``X+L``."[```$O0``!+T```F\```)O`!`]3P`0/4\``#].P``_3L``'*\ -M``!RO`#`M#P`P+0\`$`K/0!`*ST``/BZ``#XN@``U;P``-6\``#5.P``U3L` -M``<\```'/`"`%[T`@!>]`(`4O0"`%+T``*D[``"I.P``2#H``$@Z`(#2O`"` -MTKP``+>[``"WNP!`OCP`0+X\``#G.P``YSL``#2[```TNP!`S#P`0,P\``#. -M/```SCP``+"[``"PNP``O#L``+P[`,#Q/`#`\3P``)"[``"0NP!@.+T`8#B] -M``"SO```L[P``,0Z``#$.@!@#;T`8`V]``!5O0``5;T``+RZ``"\N@``NCP` -M`+H\`(!!O`"`0;P``.Z[``#NNP`@/3T`(#T]`*!%/0"@13T``$`[``!`.P`` -M0+H``$"Z`("H/`"`J#P``#B[```XNP!`ZKP`0.J\``"_NP``O[L```([```" -M.P"`]KP`@/:\`,``O0#``+T``/BZ``#XN@``(KP``"*\`$#^O`!`_KP``#"Z -M```PN@!@"ST`8`L]``#1.P``T3L`@$^\`(!/O`"`_SP`@/\\``!+/0``2ST` -M`("Y``"`N0"`PKP`@,*\``#7.P``USL``*`Z``"@.@!@/+T`8#R]`*`BO0"@ -M(KT``(H[``"*.P``"+L```B[`$#&O`!`QKP``*<[``"G.P"``3T`@`$]```> -M/```'CP``#"[```PNP#`JSP`P*L\``"2/```DCP``%N\``!;O```D;L``)&[ -M`,"A/`#`H3P`@`2\`(`$O`"`';T`@!V]`(!9O`"`6;P``(8[``"&.P"`[KP` -M@.Z\`*`=O0"@';T``.H[``#J.P"`ECP`@)8\`(!VO`"`=KP``*0Z``"D.@!@ -M5CT`8%8]`(`K/0"`*ST``-`Y``#0.0``R#L``,@[`("K/`"`JSP`@'B\`(!X -MO``@*;T`("F]`(!.O`"`3KP``#"[```PNP``%;T``!6]`$`)O0!`";T``)L[ -M``";.P``5CL``%8[`("6O`"`EKP``-\[``#?.P`@##T`(`P]``"C.P``HSL` -M@%"\`(!0O`"`Y3P`@.4\`(`F/0"`)CT``!:[```6NP``*KP``"J\`,"J/`#` -MJCP``%X[``!>.P"@-+T`H#2]`*`(O0"@"+T``/PZ``#\.@``C+P``(R\`,`? -MO0#`'[T``-`Y``#0.0#`^SP`P/L\``#8.P``V#L``)"Z``"0N@"`[#P`@.P\ -M`$"N/`!`KCP``):\``"6O```/+P``#R\`,"5/`#`E3P``,6[``#%NP`@!KT` -M(`:]``"`N```@+@`0+X\`$"^/```'KP``!Z\``"XO```N+P``#8\```V/``` -M7CP``%X\`$"XO`!`N+P`@%:\`(!6O`"`[#P`@.P\`("V/`"`MCP``+Z[``"^ -MNP``USL``-<[`$"+/`!`BSP``(*\``""O`#`![T`P`>]``#NNP``[KL``-N[ -M``#;NP#@&[T`X!N]`,"MO`#`K;P`P,8\`,#&/```E#P``)0\```FNP``)KL` -M@.(\`(#B/`!@1CT`8$8]``#?.P``WSL`@+:\`("VO`"`+CP`@"X\`,"Q/`#` -ML3P`P)Z\`,">O```Y[P``.>\``!V.P``=CL``%2[``!4NP#@#KT`X`Z]`,#* -MO`#`RKP``)`Y``"0.0!`@;P`0(&\``#!O```P;P`@'<\`(!W/`!``CT`0`(] -M`(`9/`"`&3P`@+H\`("Z/``@8CT`(&(]`,#A/`#`X3P``*J\``"JO```RKL` -M`,J[`$"3/`!`DSP`@-*\`(#2O`#@=[T`X'>]`("7O`"`E[P``%4\``!5/`"` -MG;P`@)V\`$#CO`!`X[P`0(<\`$"'/`"`VCP`@-H\`(`3O`"`$[P``&*[``!B -MNP#@!ST`X`<]`(#"/`"`PCP``!R[```]``"0N0``D+D``!>\```7O`"``KT`@`*] -M``#0.0``T#D`(`P]`"`,/0``6#P``%@\``!`NP``0+L``,,\``##/`"`WCP` -M@-X\`(`$O`"`!+P``.2[``#DNP"`LCP`@+(\``#P.@``\#H`0`:]`$`&O0`` -M,KP``#*\``!7/```5SP``*^\``"OO`"`.[T`@#N]```9O```&;P``$,\``!# -M/`"`<;P`@'&\`(`'O`"`![P`X!\]`.`?/0"`+CT`@"X]``!-/```33P``*4\ -M``"E/`!`'ST`0!\]```5/```%3P`0,J\`$#*O`"`5;P`@%6\`(`VO`"`-KP` -M@#"]`(`PO0#`%;T`P!6]``"2.P``DCL```"Y````N0``[+P``.R\``"5NP`` -ME;L```8]```&/0``1SP``$<\`(`NO`"`+KP`@,8\`(#&/`#`-3T`P#4]``#] -M.P``_3L``/:[``#VNP#`LCP`P+(\``"C/```HSP`@*"\`("@O```W+P``-R\ -M`(`IO`"`*;P`0-F\`$#9O`!`(KT`0"*]``"WNP``M[L`P(`\`,"`/```G[L` -M`)^[``#`.@``P#H`0"`]`$`@/0``_SP``/\\```EO```);P``!"Z```0N@#` -M[#P`P.P\``!.NP``3KL`8"B]`&`HO0``$+P``!"\``#!/```P3P`@`J\`(`* -MO``@!+T`(`2]``"D.@``I#H`P)$\`,"1/`"`:[P`@&N\`("$O`"`A+P``*D\ -M``"I/```JSP``*L\``#HN@``Z+H``&H\``!J/`"`_SP`@/\\``#@.@``X#H` -M0*:\`$"FO````+H```"Z``#3NP``T[L`H#F]`*`YO0!@"KT`8`J]`(!@/`"` -M8#P``,([``#".P"`SKP`@,Z\``"&.P``ACL`@#@]`(`X/0#`GCP`P)X\```Z -MO```.KP`P)(\`,"2/``@#ST`(`\]``#:NP``VKL`@,*\`(#"O`"`*#P`@"@\ -M``!0/```4#P``,.\``##O`!`S;P`0,V\``!DNP``9+L``)F\``"9O`#`#+T` -MP`R]``"!NP``@;L`@(<\`("'/```@+L``("[``#$.@``Q#H`(!<]`"`7/0`` -M_CP``/X\``!.NP``3KL``-([``#2.P!`X3P`0.$\``"_NP``O[L`("B]`"`H -MO0``1[P``$>\```]/```/3P`P*B\`,"HO`!`![T`0`>]``#G.P``YSL`0)4\ -M`$"5/`"`8+P`@&"\`(`CO`"`([P`0-0\`$#4/`"`B3P`@(D\`(!'O`"`1[P` -M`)`[``"0.P"`PSP`@,,\``#4N@``U+H`P)2\`,"4O```^#H``/@Z``"(N@`` -MB+H`P`:]`,`&O0#`QKP`P,:\`(`^/`"`/CP``/"Y``#PN0``Q+P``,2\`(`3 -M/`"`$SP`@"L]`(`K/0"`:3P`@&D\``"_NP``O[L`P-,\`,#3/`"`]SP`@/<\ -M`("TO`"`M+P`@!&]`(`1O0``T#D``-`Y``")NP``B;L`("2]`"`DO0#`W[P` -MP-^\``"7.P``ESL```2\```$O`#`O+P`P+R\```J/```*CP`@/0\`(#T/``` -M?#L``'P[``!T.P``=#L``!L]```;/0``]SP``/<\``"HNP``J+L``),[``"3 -M.P``PSP``,,\`(!KO`"`:[P``%>]``!7O0#`U+P`P-2\``"@NP``H+L`0!&] -M`$`1O0`@*;T`("F]``"B.P``HCL`@*@\`("H/```E+L``)2[``"N.P``KCL` -MH"0]`*`D/0#`^#P`P/@\``"/O```C[P``#F]```YO0``MKP``+:\`,`Z/0#` -M.CT`@)X\`(">/```*+H``"BZ`""B/0`@HCT`@$B]`(!(O0!H++X`:"R^``"M -M.P``K3L`$,(]`!#"/0"PN;T`L+F]```"O0```KT`0`<^`$`'/@!`C3T`0(T] -M`)#UO0"0];T`,*B]`#"HO0`PQ3T`,,4]`/"3/0#PDST``""]```@O0#`Q+P` -MP,2\``"'NP``A[L`@"D\`(`I/`"0G3T`D)T]`,"@/`#`H#P`0.&]`$#AO0"@ -MIKT`H*:]`!#//0`0SST`"`@^``@(/@!X!+X`>`2^`"ASO@`H<[X``"^\```O -MO``P7CX`,%X^`-`//@#0#SX`P'N]`,![O0"P`+X`L`"^`,"JO`#`JKP`P(D] -M`,")/0!P%SX` -M/@``'CX`/)*^`#R2O@`,B[X`#(N^`(!P/`"`<#P`8`D^`&`)/@!`R#P`0,@\ -M`,#%O`#`Q;P`@(R\`(",O`#`O+P`P+R\``#WNP``][L`L/T]`+#]/0!@"SX` -M8`L^`$#)O`!`R;P`H*"]`*"@O0``X[L``..[`,"\O`#`O+P`8):]`&"6O0"@ -M,ST`H#,]`#"//0`PCST`R!6^`,@5O@#(3;X`R$V^`$![/0!`>ST`D'T^`)!] -M/@#@D#T`X)`]`%@\`("GO`"@$;T`H!&]`'"A/0!PH3T`$%(^`!!2/@``M#T` -M`+0]`!#=O0`0W;T`L)Z]`+">O0!`@SP`0(,\`,""/0#`@CT`<+0]`'"T/0`` -MD+H``)"Z`+!=O@"P7;X`A(N^`(2+O@!@##T`8`P]`,R./@#,CCX`,*L]`#"K -M/0`X4[X`.%.^`%@LO@!8++X`8.L]`&#K/0`0?3X`$'T^`.@)/@#H"3X`0$V] -M`$!-O0"0-[X`D#>^`!@"O@`8`KX`F",^`)@C/@`P6SX`,%L^`"#:O0`@VKT` -MI*^^`*2OO@!PS+T`<,R]`$!*/@!`2CX`V`T^`-@-/@`@)+T`("2]`!",O0`0 -MC+T`P"Z]`,`NO0!`(3T`0"$]`(`*/@"`"CX`^`<^`/@'/@#`(+T`P""]`'`F -MO@!P)KX`L*6]`+"EO0!@5ST`8%<]```8.P``&#L`P)&\`,"1O```2#L``$@[ -M`,#TO`#`]+P`X""^`.`@O@!@`+T`8`"]`)!R/@"0^ -M`*`'O@``"KT```J]`,`9/@#`&3X`R#<^`,@W/@`PMKT`,+:]`.23O@#DD[X` -MX-2]`.#4O0"H"SX`J`L^`&#)/0!@R3T`P(6]`,"%O0!@7[T`8%^]``#W.P`` -M]SL```````````"@'ST`H!\]`%@!/@!8`3X`H)0]`*"4/0!@?[T`8'^]`.#( -MO0#@R+T``-^[``#?NP``7#L``%P[``"`.```@#@``$L]``!+/0#`\;P`P/&\ -M`'!;O@!P6[X`T)B]`-"8O0"83T`0'D]`,#?O`#` -MW[P`H(2]`*"$O0`@E#T`()0]`)@0/@"8$#X`0*V\`$"MO`!@-KX`8#:^`%"4 -MO0!0E+T`P*8]`,"F/0"0UCT`D-8]`.!)/0#@23T`P)6]`,"5O0!P3KX`<$Z^ -M`,`TO0#`-+T`$'4^`!!U/@#(9CX`R&8^`#`'O@`P![X`P(B^`,"(O@#`L+P` -MP+"\`$!7/@!`5SX`@`@^`(`(/@!0@;T`4(&]`%`XO@!0.+X`*`*^`"@"O@#` -M'#T`P!P]`&@R/@!H,CX`@'D]`(!Y/0`X.KX`.#J^`$@LO@!(++X`L!4^`+`5 -M/@"X=3X`N'4^`(#$/`"`Q#P`\-J]`/#:O0"@&;T`H!F]`$#=O`!`W;P`@&:\ -M`(!FO``@B#T`((@]`*!$/0"@1#T`\/6]`/#UO0!(";X`2`F^``#2.P``TCL` -M8(\]`&"//0!`^3P`0/D\`,!D/0#`9#T`0!@]`$`8/0"@M[T`H+>]`(#YO0"` -M^;T`X-8]`.#6/0#0<3X`T'$^`(!4/`"`5#P`P'6^`,!UO@#X";X`^`F^`%#% -M/0!0Q3T`H/(]`*#R/0``)SP``"<\`+#-O0"PS;T`*#F^`"@YO@"@&[T`H!N] -M`%AU/@!8=3X`P'@^`,!X/@#PS;T`\,V]`.B#O@#H@[X``(BZ``"(N@`P]`##6/0`P -MUCT`B(`^`(B`/@"`+SP`@"\\`!"/O@`0C[X`>"6^`'@EO@!`\CT`0/(]`,`& -M/@#`!CX`8$"]`&!`O0!H'+X`:!R^`*@IO@"H*;X`@)Z\`(">O`#H:3X`Z&D^ -M`-AY/@#8>3X`<)Z]`'">O0"8<[X`F'.^``"XN@``N+H``'8^``!V/@"`[ST` -M@.\]`%#:O0!0VKT`H/R]`*#\O0"@(KT`H"*]`$!5O0!`5;T`0+V\`$"]O``@ -M`#T`(``]`.`BO0#@(KT`$/:]`!#VO0#`GSP`P)\\`"`9/@`@&3X`@+0]`("T -M/0!`ICP`0*8\`("B/0"`HCT`@)X\`(">/``@Y[T`(.>]`.!:O0#@6KT`L.P] -M`+#L/0!@!#T`8`0]`#A*O@`X2KX`R%2^`,A4O@``G3P``)T\`!@)/@`8"3X` -M<)4]`'"5/0``QKP``,:\`/"DO0#PI+T`$*"]`!"@O0#@`CX`X`(^`+"5/@"P -ME3X`0(P]`$",/0`#$^`'"H -MO0!PJ+T`>&2^`'ADO@"0(;X`D"&^`$"CO`!`H[P`X`<^`.`'/@`X%CX`.!8^ -M`*``O0"@`+T`\/6]`/#UO0``]3P``/4\`/A,/@#X3#X`@,P]`(#,/0#@Q+T` -MX,2]`&"ZO0!@NKT``%X[``!>.P"@3+T`H$R]`(!/@`4DCX`%)(^`("%/0"` -MA3T`>'N^`'A[O@"H%+X`J!2^`+`V/@"P-CX`^`!@U/@"PS;T`L,V]`+!6 -MO@"P5KX`X."]`.#@O0"`%[P`@!>\`(!4/0"`5#T`P*T]`,"M/0``A#L``(0[ -M`*#EO0"@Y;T``%:]``!6O0#`!#X`P`0^`!#\/0`0_#T`@&&\`(!AO```Z#L` -M`.@[`/"6/0#PECT`0$R]`$!,O0`0][T`$/>]`$#Y/`!`^3P`$+,]`!"S/0!@ -MY;T`8.6]`$!'O@!`1[X``,:\``#&O`"(`#X`B``^`%#+/0!0RST`P"(]`,`B -M/0``1[T``$>]`!#3O0`0T[T`@(0\`("$/`"04CX`D%(^`+#]/0"P_3T`&#Z^ -M`!@^O@"ST`8'L]`'@.O@!X#KX` -M\`R^`/`,O@#@([T`X".]`,#:/0#`VCT`8#@^`&`X/@``23T``$D]`+A$O@"X -M1+X`0/Z]`$#^O0"P)#X`L"0^`%@]/@!8/3X`P%B]`,!8O0"8'+X`F!R^`&![ -MO0!@>[T``+2[``"TNP"`[+P`@.R\```0/```$#P`@&H]`(!J/0"`QKP`@,:\ -M`$!0O0!`4+T``'8]``!V/0#PM3T`\+4]```F/```)CP``-`Y``#0.0#`XCP` -MP.(\`$"MO0!`K;T`(/R]`"#\O0#PF3T`\)D]`#`H/@`P*#X`\)F]`/"9O0`H -M:KX`*&J^```WO0``-[T`B#<^`(@W/@`($#X`"!`^`,#VO`#`]KP`$/V]`!#] -MO0"`W+T`@-R]`$#$/`!`Q#P`*$T^`"A-/@``&3X``!D^`'@5O@!X%;X`D(F^ -M`)")O@``DSP``),\`$!H/@!`:#X`($@]`"!(/0``,KX``#*^`$#EO0!`Y;T` -M`,<\``#'/`!@P#T`8,`]`+#Z/0"P^CT`L($]`+"!/0"0X;T`D.&]`*#NO0"@ -M[KT`8),]`&"3/0``^CT``/H]`*!$O0"@1+T`X/V]`.#]O0"`^;P`@/F\`$"! -M/`!`@3P`P#J]`,`ZO0``6CP``%H\`#``/@`P`#X`(!P]`"`"T^`("IO0"`J;T`=(2^`'2$O@`@ -MB;T`((F]`.A1/@#H43X`Z#<^`.@W/@"@#;T`H`V]`,``O@#``+X`0%R]`$!< -MO0#`4ST`P%,]`.`;/@#@&SX`0*T]`$"M/0!P+[X`<"^^`&"$O@!@A+X``)L[ -M``";.P#X8CX`^&(^``!Z/0``>CT`0!.^`$`3O@"`9KT`@&:]`&"'/0!@AST` -ML)(]`+"2/0`PF3T`,)D]`."*/0#@BCT`8%N]`&!;O0`P[KT`,.Z]``!&O``` -M1KP``+L]``"[/0``#+P```R\`/"EO0#PI;T`P-Z\`,#>O`!`WKP`0-Z\`+#: -MO0"PVKT`@+.\`("SO`"(*SX`B"L^`*#;/0"@VST`\/.]`/#SO0"`F+T`@)B] -M`)@>/@"8'CX`.#0^`#@T/@"`$SP`@!,\`(#1O0"`T;T`"`6^``@%O@``IKT` -M`*:]`."1/0#@D3T`D`@^`)`(/@"0QKT`D,:]`$20O@!$D+X`D)V]`)"=O0`P -M>SX`,'L^`"!G/@`@9SX`P$6]`,!%O0"X#+X`N`R^```.O```#KP`D*D]`)"I -M/0`0Y#T`$.0]`+"?/0"PGST`X*:]`."FO0!`0KX`0$*^`$!'O0!`1[T`T/\] -M`-#_/0#`[SP`P.\\`"@1O@`H$;X`8)"]`&"0O0"@4CT`H%(]`"`C/0`@(ST` -M@!,]`(`3/0"PSST`L,\]`+"7/0"PEST`X#:]`.`VO0#0B;T`T(F]`(`T/0"` -M-#T`0%8]`$!6/0"@';T`H!V]`""#X^`'@^/@!X -M13X`>$4^`&`HO0!@*+T`L.*]`+#BO0!`,CT`0#(]`-#K/0#0ZST`0)P]`$"< -M/0`0C3T`$(T]``!VNP``=KL`X`>^`.`'O@`PL;T`,+&]`*!F/0"@9CT``%P\ -M``!\`$`N/0!`+CT`>``^`'@` -M/@``Q3L``,4[`$@#O@!(`[X`8$"]`&!`O0"PB3T`L(D]`"`0/0`@$#T`<(R] -M`'",O0#PK[T`\*^]`.!?O0#@7[T```&\```!O`"@S#T`H,P]`.`-/@#@#3X` -M(%V]`"!=O0#X.KX`^#J^```$/```!#P`D'`^`)!P/@!P!CX`<`8^`&@!O@!H -M`;X`J!J^`*@:O@!@&KT`8!J]`&!!/0!@03T`$.`]`!#@/0!@*CT`8"H]`)@8 -MO@"8&+X`J"J^`*@JO@#`@3T`P($]`)!=/@"073X`("0]`"`D/0"H$+X`J!"^ -M`.!4O0#@5+T`0)<]`$"7/0``1CT``$8]`(`9/`"`&3P``.0Z``#D.@#@3+T` -MX$R]`'#:O0!PVKT``-6[``#5NP`PTCT`,-(]`,"X/`#`N#P`($B]`"!(O0"` -M.CP`@#H\`$"3/`!`DSP`0`R]`$`,O0!`\SP`0/,\`$#\/0!`_#T``#0\```T -M/`"8+KX`F"Z^`""TO0`@M+T`\/<]`/#W/0"PU3T`L-4]`$"4O0!`E+T`B`J^ -M`(@*O@`@9[T`(&>]`&`L/0!@+#T`N`0^`+@$/@!H%CX`:!8^`,!@O0#`8+T` -MH&*^`*!BO@#@&KT`X!J]``!Q/@``<3X`2!$^`$@1/@"P!KX`L`:^`,`NO@#` -M+KX`0+&\`$"QO`"@+ST`H"\]`&`[/0!@.ST``/$\``#Q/``0AKT`$(:]`/"_ -MO0#PO[T`P#T]`,`]/0"`,#X`@#`^`(!@/0"`8#T`D.2]`)#DO0#`:;T`P&F] -M`""5/0`@E3T``.@\``#H/`!@-+T`8#2]```VO```-KP`0*N\`$"KO`"0Y[T` -MD.>]`'"4O0!PE+T``-$]``#1/0``WCT``-X]`$"AO`!`H;P`@`^]`(`/O0`` -M.KL``#J[``">.P``GCL`\)0]`/"4/0!0$CX`4!(^`$"(/`!`B#P`$$F^`!!) -MO@!@%+X`8!2^`+#;/0"PVST`$`(^`!`"/@!`IKT`0*:]`!`PO@`0,+X`D(N] -M`)"+O0"@[T`@)F\`("9O``` -MVCP``-H\`("T/0"`M#T`<`4^`'`%/@#`OCP`P+X\`,`BO@#`(KX`(/V]`"#] -MO0#0X#T`T.`]`#`$/@`P!#X`T/R]`-#\O0#H=KX`Z':^`#"2O0`PDKT`,-L] -M`##;/0"P`SX`L`,^``";/0``FST``'Z\``!^O`!`TKT`0-*]``"0NP``D+L` -MJ$L^`*A+/@!P%3X`#V^`'@]O@"`.3P` -M@#D\`%@//@!8#SX`@!P\`(`]`&"[O0!@N[T`@+@\`("X/``@.ST`(#L]`$!RO0!` -M\`,#9O`#`V;P`0,N\`$#+O`"`DCT`@)(]`*@$/@"H!#X` -M@%@\`(!8/``PH;T`,*&]`$"`O`!`@+P`@-X\`(#>/```3KP``$Z\``#!O``` -MP;P`0/\\`$#_/`"`[KP`@.Z\`"@6O@`H%KX`X%*]`.!2O0`H$3X`*!$^`%#( -M/0!0R#T`8(B]`&"(O0!0I;T`4*6]`(#^/`"`_CP`0+@]`$"X/0"@RCT`H,H] -M``!X/0``>#T`P,&]`,#!O0`(.;X`"#F^`(`+/`"`"SP`8#`^`&`P/@"`]CP` -M@/8\`(A`O@"(0+X`N`.^`+@#O@#PLCT`\+(]`*`"/@"@`CX``-<\``#7/`#` -MS[P`P,^\`(!QO0"`<;T`8$J]`&!*O0!0C3T`4(T]`.@1/@#H$3X``(8[``"& -M.P!X&+X`>!B^`,"$O0#`A+T`H)4]`*"5/0#`[#P`P.P\`(#CO`"`X[P`0)4\ -M`$"5/`#`BCP`P(H\`(!VO0"`=KT`(!^]`"`?O0#PJ#T`\*@]`("?/0"`GST` -MX"&]`.`AO0#@;;T`X&V]``!`.0``0#D``#H[```Z.P``O+H``+RZ`.`//0#@ -M#ST`0)2\`$"4O`#H![X`Z`>^`*`YO0"@.;T`(#P^`"`\/@!@%CX`8!8^`$"Q -MO0!`L;T`(!>^`"`7O@``\CL``/([`&#J/0!@ZCT`L,,]`+##/0``.SP``#L\ -M`$#OO0!`[[T`^#.^`/@SO@``H;L``*&[`!`T/@`0-#X`8)<]`&"7/0!8$;X` -M6!&^`/#ZO0#P^KT`8*4]`&"E/0`X!CX`.`8^```+/```"SP`@-R\`(#/0#@'CT``#T] -M```]/0!`4[T`0%.]`$`[O0!`.[T`P-X]`,#>/0!P%3X`CT`('H]`)",O0"0C+T`\`J^`/`* -MO@`@1[T`($>]`(`R/`"`,CP``)(\``"2/`"0CST`D(\]`$!C/0!`8ST`T)F] -M`-"9O0``LKT``+*]`)#`/0"0P#T`^$8^`/A&/@#`W3P`P-T\`"@1O@`H$;X` -M<(^]`'"/O0"`*#T`@"@]```L/0``+#T``&*[``!BNP#0I;T`T*6]`,@:O@#( -M&KX`P(J]`,"*O0"X&#X`N!@^`$@[/@!(.SX`@"^]`(`OO0"(%;X`B!6^`$`= -M/0!`'3T`P#8^`,`V/@!PXCT`<.(]`(#CO`"`X[P`D+"]`)"PO0#0S;T`T,V] -M`#"-O0`PC;T`P/<\`,#W/`!@)ST`8"<]`'#$O0!PQ+T`4.:]`%#FO0`@#3T` -M(`T]`/#(/0#PR#T`P*$\`,"A/`!`GCP`0)X\`+"O/0"PKST`0/$\`$#Q/`"@ -M6KT`H%J]`.`Y/0#@.3T`L.\]`+#O/0``M;L``+6[`#`/O@`P#[X`$-J]`!#: -MO0!`H;P`0*&\``#0N0``T+D``.4[``#E.P``]3L``/4[`,"0O0#`D+T`D)F] -M`)"9O0!0X3T`4.$]`!!I/@`0:3X`X'X]`.!^/0#8$+X`V!"^`#"7O0`PE[T` -MT+X]`-"^/0`PGCT`,)X]`,#5O`#`U;P``+V]``"]O0"8"+X`F`B^`%"CO0!0 -MH[T`$+(]`!"R/0"H%#X`J!0^`,#IO`#`Z;P`0!6^`$`5O@``/#L``#P[`!@9 -M/@`8&3X`\*\]`/"O/0``AKP``(:\```7O```%[P``!"Z```0N@!`(KT`0"*] -M``"0O```D+P`0,L\`$#+/`"0A[T`D(>]`-#\O0#0_+T`0-.\`$#3O`#@?ST` -MX'\]`,#A/`#`X3P``(H\``"*/`"`C3T`@(T]`&`0/0!@$#T`X%&]`.!1O0!@ -M&CT`8!H]`!`=/@`0'3X`H$$]`*!!/0!H&[X`:!N^`,@GO@#()[X``)6\``"5 -MO`"PC#T`L(P]`,`2/0#`$CT``'V\``!]O``PR[T`,,N]`+#8O0"PV+T`<(X] -M`'"./0#`43X`P%$^`$"%/0!`A3T`H`B^`*`(O@"`A;T`@(6]`+`'/@"P!SX` -MD.H]`)#J/0```KT```*]`%"KO0!0J[T`P+N]`,"[O0"PA+T`L(2]`(`'/0"` -M!ST`H*$]`*"A/0"@#;T`H`V]`)@+O@"8"[X``&*\``!BO`#(%SX`R!<^`-#+ -M/0#0RST`@'&\`(!QO```Y;L``.6[```X/```.#P`@"B]`(`HO0"@'[T`H!^] -M`,!3/0#`4ST`P,D\`,#)/`!@T;T`8-&]`-"XO0#0N+T`@+`\`("P/`!`23T` -M0$D]`&`A/0!@(3T``"0]```D/0"`A+P`@(2\`/#(O0#PR+T``*2Z``"DN@#@ -M,3X`X#$^`!#U/0`0]3T`,.2]`##DO0#`'+X`P!R^``"9.P``F3L`@,0]`(#$ -M/0!`DSP`0),\`.!UO0#@=;T`P+V]`,"]O0"0I[T`D*>]`.`X/0#@.#T`$"L^ -M`!`K/@!`9CT`0&8]`)#[O0"0^[T`T)>]`-"7O0`P]ST`,/<]`/``/@#P`#X` -M``"]````O0#`P;T`P,&]`.!\O0#@?+T`0`&]`$`!O0#@`3T`X`$]`'"F/0!P -MICT``*H\``"J/`"@N+T`H+B]`!"*O0`0BKT`(&<]`"!G/0`@7ST`(%\]`(!L -MO`"`;+P`@&(\`(!B/`#`^CP`P/H\`(!/O0"`3[T`H(.]`*"#O0#P@#T`\(`] -M`'#&/0!PQCT``'"]``!PO0`@_;T`(/V]```$O```!+P`T(@]`-"(/0#`.P``7CL`^"8^`/@F/@!@ -MU3T`8-4]`/`;O@#P&[X`V$R^`-A,O@``B#L``(@[`+`(/@"P"#X`8"X]`&`N -M/0#@;[T`X&^]`&![O0!@>[T`@/&\`(#QO```'ST``!\]`!@//@`8#SX`,(8] -M`#"&/0"0VKT`D-J]`"#*O0`@RKT`0),]`$"3/0#PUST`\-<]`$#*O`!`RKP` -MH)F]`*"9O0#`B[P`P(N\`$##O`!`P[P``!"]```0O0`@%#T`(!0]`.!O/0#@ -M;ST`(!B]`"`8O0`0F;T`$)F]`(#Y/`"`^3P`,+P]`#"\/0#`Y3P`P.4\```F -M/```)CP`@%D\`(!9/`#@A+T`X(2]`%"MO0!0K;T`@#P]`(`\/0!@RST`8,L] -M``!XO0``>+T`0":^`$`FO@"`H+P`@*"\`%@*/@!8"CX`P.@]`,#H/0``ACP` -M`(8\`"!RO0`@] -M`#A(O@`X2+X`4(R]`%",O0!0_CT`4/X]`-#7/0#0UST`@%V\`(!=O`"`-[T` -M@#>]```KO```*[P`8%H]`&!:/0``!#X```0^`)#7/0"0UST`L)N]`+";O0"( -M/;X`B#V^`(`@O0"`(+T`D,`]`)#`/0``S[L``,^[`'#NO0!P[KT`(':]`"!V -MO0``S#P``,P\`,#M/`#`[3P`,($]`#"!/0#`SST`P,\]`(#X/`"`^#P`X$&] -M`.!!O0#`ACP`P(8\`#"D/0`PI#T`0.L\`$#K/```+[T``"^]`"`DO0`@)+T` -M@(*]`(""O0`@X+T`(."]`(#BO`"`XKP``,,]``##/0!`WSP`0-\\`$#CO0!` -MX[T`H(:]`*"&O0"0T#T`D-`]`)@./@"8#CX`4(0]`%"$/0``;CP``&X\``#Q -MO```\;P`(#Z]`"`^O0``3ST``$\]`""X/0`@N#T`@(F]`(")O0#86[X`V%N^ -M`&#%O0!@Q;T`X/0]`.#T/0!PW#T`<-P]`&!`O0!@0+T`H)*]`*"2O0``5#L` -M`%0[`-"`/0#0@#T`(-0]`"#4/0"`Q#T`@,0]``"/O```C[P``.^]``#OO0`@ -M!;T`(`6]`,"C/0#`HST``'0[``!T.P`0L;T`$+&]`&`BO0!@(KT``'@Z``!X -M.@#`%KT`P!:]`$"%O`!`A;P`<*<]`'"G/0#0GST`T)\]`(#FO`"`YKP`@"R\ -M`(`LO`!`M#T`0+0]`!"H/0`0J#T``'"[``!PNP`@*[T`("N]`!")O0`0B;T` -MH-:]`*#6O0#@/KT`X#Z]``"^/0``OCT`(#0]`"`T/0"@!+X`H`2^`$#2O0!` -MTKT`D,T]`)#-/0``+#X``"P^`$!L/0!`;#T`P-"\`,#0O`#`QKP`P,:\```F -MO```)KP`X%T]`.!=/0`PS#T`,,P]`$">O`!`GKP`N"^^`+@OO@"PWKT`L-Z] -M`#"5/0`PE3T`T*8]`-"F/0#@C[T`X(^]`."\O0#@O+T``!D\```9/```8ST` -M`&,]`,!_/0#`?ST`H+(]`*"R/0!@1#T`8$0]`*`OO0"@+[T``/&\``#QO`#` -M=ST`P'<]``#?/```WSP`$)&]`!"1O0"@:KT`H&J]``!6O```5KP`("Z]`"`N -MO0!@";T`8`F]`'"$/0!PA#T``+`]``"P/0!`+[T`0"^]`("@O0"`H+T`8%@] -M`&!8/0`8`3X`&`$^`,!./0#`3CT`P/&\`,#QO``0@+T`$("]`#"/O0`PC[T` -M@#2\`(`TO`!PT#T`<-`]`"!3/0`@4ST`X`>^`.`'O@#(";X`R`F^`"!Q/0`@ -M<3T`B`@^`(@(/@``]#P``/0\`"!&O0`@1KT``,2[``#$NP"`$CT`@!(]``!5 -M/0``53T`T(4]`-"%/0``T+L``-"[`"#UO0`@];T`D*>]`)"GO0#@@CT`X((] -M`)"C/0"0HST`0&.]`$!CO0"`L[T`@+.]`,"8/`#`F#P`0#`]`$`P/0``_CL` -M`/X[`&!>/0!@7CT``(H]``"*/0#`W;P`P-V\`&"9O0!@F;T``#<\```W/`!` -M>#T`0'@]``"\.@``O#H`P+*\`,"RO```K;L``*V[`.!,O0#@3+T`0$.]`$!# -MO0!`A3T`0(4]`!#3/0`0TST`X!6]`.`5O0#`^;T`P/F]``"#/```@SP`R!`^ -M`,@0/@"`=3T`@'4]``!4O0``5+T`$)&]`!"1O0#@/+T`X#R]```0NP``$+L` -MT)@]`-"8/0"`2#T`@$@]`$#"O0!`PKT``/"]``#PO0!0BCT`4(H]`(@B/@"( -M(CX`@!0]`(`4/0`@HKT`(**]```0O0``$+T`H!8]`*`6/0#@%ST`X!<]`,#Q -M/`#`\3P``%8[``!6.P``J;T``*F]`%"UO0!0M;T`0.D\`$#I/``PK3T`,*T] -M``"WNP``M[L`(%V]`"!=O0"`4CP`@%(\`("\/`"`O#P`P-B\`,#8O`!`$CT` -M0!(]`'#3/0!PTST`@(D\`(")/`!0M[T`4+>]`&`EO0!@);T``&8]``!F/0#` -M`3T`P`$]`$`,O0!`#+T`0":]`$`FO0#@+KT`X"Z]``"+O```B[P`H*`]`*"@ -M/0!`UCT`0-8]`$`"O0!``KT`4`2^`%`$O@``?CL``'X[`#@B/@`X(CX`T(P] -M`-",/0#0MKT`T+:]`$#.O0!`SKT`8!6]`&`5O0``)CP``"8\`"!J/0`@:CT` -MX$\]`.!//0#@2[T`X$N]`,#)O0#`R;T`X"`]`.`@/0`P$3X`,!$^`"!"/0`@ -M0CT`8&Z]`&!NO0``5;P``%6\`&`O/0!@+ST``/`[``#P.P#`M;P`P+6\`(`" -M/`"``CP``!J]```:O0!PS[T`<,^]`$`+O0!`"[T`H&@]`*!H/0!`JSP`0*L\ -M``#RO```\KP``$<\``!'/``@)3T`("4]``#=.P``W3L`X$H]`.!*/0`@^#T` -M(/@]`.`//0#@#ST`,.2]`##DO0"`J[T`@*N]`(!*/0"`2CT`H&L]`*!K/0`@ -M=+T`('2]`.#6O0#@UKT`@'>]`(!WO0``(+D``""Y``"C/0``HST`8-8]` -M/0``[[L``.^[`/#9O0#PV;T``(`[``"`.P#@+#X`X"P^`%#7/0!0UST`@)2] -M`("4O0#0P[T`T,.]``#+O```R[P``+6[``"UNP``/KL``#Z[```:.P``&CL` -MH"^]`*`OO0``M+T``+2]``"..P``CCL`H.H]`*#J/0!@@ST`8(,]`,#XO`#` -M^+P``,@[``#(.P"0A3T`D(4]`,#^/`#`_CP``#6\```UO`#`FSP`P)L\`,"H -MO`#`J+P`0/^]`$#_O0"@NKT`H+J]`,`F/0#`)CT`X%H]`.!:/0!`EKP`0):\ -M`*`:O0"@&KT``-^[``#?NP``[3L``.T[`%"//0!0CST`X!4^`.`5/@`@A3T` -M((4]`&#-O0!@S;T`@*:]`("FO0"PB3T`L(D]`$"@/0!`H#T`$(N]`!"+O0"` -M!+X`@`2^`$!>O0!`7KT``,`Y``#`.0"`SSP`@,\\``!2/0``4CT``!"\```0 -MO`#0HKT`T**]``!R/```]`!#'O0``TKL``-*[`$"!/0!`@3T``-"Y``#0N0`@$KT`(!*]`$"A/`!` -MH3P`8!H]`&`:/0``\#P``/`\`/"1/0#PD3T`P(4]`,"%/0"@=[T`H'>]`+#W -MO0"P][T`@((\`(""/`!`XCT`0.(]``#P.@``\#H`<.:]`'#FO0``GKT``)Z] -M``#].P``_3L`P&L]`,!K/0!@H3T`8*$]`$!#/0!`0ST`X&N]`.!KO0"`D+T` -M@)"]`+",/0"PC#T`,/H]`##Z/0!`MKP`0+:\`'#XO0!P^+T`X!*]`.`2O0#` -M6CT`P%H]`(!>/`"`7CP`0,*\`$#"O`"`:+P`@&B\`,`1O0#`$;T`(`>]`"`' -MO0"`:CT`@&H]`"#"/0`@PCT``'4\``!U/`!`)+T`0"2]``!7/```5SP`(`4] -M`"`%/0"`C;P`@(V\```,NP``#+L`@!H]`(`:/0``#KT```Z]`+#NO0"P[KT` -MX!:]`.`6O0!PLST`<+,]`,!D/0#`9#T`H!N]`*`;O0!`[[P`0.^\``!2/``` -M4CP`@/P\`(#\/`!PD#T`<)`]``!R/0``CT``-PZ``#<.@#@U;T`X-6]`*`RO0"@,KT` -MT-$]`-#1/0`@ICT`(*8]`,!BO0#`8KT``(F]``")O0!`L#P`0+`\``!2/0`` -M4CT`X#H]`.`Z/0"`^#P`@/@\`&!'O0!@1[T`8-2]`O0``@;L``(&[`"#V -M/0`@]CT`P#D]`,`Y/0`PZ+T`,.B]`'"SO0!PL[T`@%8]`(!6/0!0M#T`4+0] -M`&`,/0!@##T`@&<\`(!G/```L+H``+"Z`$`'O0!`![T``)4[``"5.P"`]`&#MO0!@[;T`X)J]`.":O0``JST``*L]`&#=/0!@W3T` -M0`F]`$`)O0``H;T``*&]`*!#/0"@0ST`D/(]`)#R/0"`93T`@&4]``"\.P`` -MO#L``"6\```EO``@-+T`(#2]`$#YO`!`^;P`X"@]`.`H/0#`PCP`P,(\`$#, -MO0!`S+T`L/&]`+#QO0``[#H``.PZ`,`S/0#`,ST``.N[``#KNP!`HCP`0*(\ -M`#"A/0`PH3T`@!@]`(`8/0``<;P``'&\`,!0/0#`4#T`L+L]`+"[/0``%;P` -M`!6\`+"XO0"PN+T`8&.]`&!CO0"`VKP`@-J\`,#BO`#`XKP`@%0\`(!4/`#` -M_#P`P/P\``!\ -M`$#GO```O+T``+R]`&"WO0!@M[T`X`T]`.`-/0#PCST`\(\]`,!^O0#`?KT` -M8.Z]`&#NO0``O#P``+P\``@./@`(#CX`,+(]`#"R/0#`ICP`P*8\``#!/``` -MP3P``!2\```4O`!`2+T`0$B]``#L.@``[#H`("@]`"`H/0!@&KT`8!J]`##0 -MO0`PT+T`H`R]`*`,O0!`M3P`0+4\``""O```@KP``*&[``"ANP#`;3T`P&T] -M`("Q/`"`L3P`(#Z]`"`^O0``DCP``)(\`+#E/0"PY3T``%@]``!8/0!`<;T` -M0'&]`$`QO0!`,;T`@),\`("3/`"`<3P`@'$\``!6NP``5KL`0*>\`$"GO``` -MM;T``+6]`,#9O0#`V;T`H`\]`*`//0#('CX`R!X^`$!//0!`3ST`,+6]`#"U -MO0!`^+P`0/B\`)"P/0"0L#T`D(8]`)"&/0"`+KP`@"Z\`"`,O0`@#+T`8&2] -M`&!DO0`PA+T`,(2]`$"2/`!`DCP`$)(]`!"2/0!`D+P`0)"\`$"UO0!`M;T` -M`#2[```TNP"0JST`D*L]`(`(/0"`"#T`0)6\`$"5O`"`V3P`@-D\`,#9/`#` -MV3P`H!B]`*`8O0``VKL``-J[`-"(/0#0B#T`@(,\`("#/`"@J[T`H*N]`"!] -MO0`@?;T``"8[```F.P``AKP``(:\`$"+O`!`B[P`@"\]`(`O/0``D#P``)`\ -M`$!;O0!`6[T``$X\``!./`!("CX`2`H^`%"M/0!0K3T`\)F]`/"9O0`PO+T` -M,+R]```[/```.SP``"4]```E/0``##P```P\```-O```#;P`H%:]`*!6O0`P -MKKT`,*Z]``#K.P``ZSL`@/@]`(#X/0``-ST``#<]`/#6O0#PUKT`8'Z]`&!^ -MO0`PGST`,)\]`&"+/0!@BST`P*"\`,"@O`#`K[P`P*^\``#U.P``]3L`@%^\ -M`(!?O````+@```"X`(`^/0"`/CT``*R[``"LNP!`N[T`0+N]`*`GO0"@)[T` -MD(`]`)"`/0"`1#T`@$0]``#[NP``^[L``+T\``"]/```\#P``/`\``!AO0`` -M8;T`(&6]`"!EO0"@6ST`H%L]``!I/0``:3T`X'&]`.!QO0#PK+T`\*R]``"P -M.0``L#D`@!X]`(`>/0``_CP``/X\``!!/0``03T`@#4\`(`U/`!PBKT`<(J] -M`(#@O`"`X+P`,,(]`##"/0!@BST`8(L]`'"OO0!PK[T`(-.]`"#3O0#@!CT` -MX`8]`&"=/0!@G3T``!8[```6.P"`#[T`@`^]`,`=O0#`';T`0%&]`$!1O0`` -M<#L``'`[`,#)/0#`R3T`8&P]`&!L/0#PFKT`\)J]`*!=O0"@7;T`,*,]`#"C -M/0#`D3T`P)$]`,`EO0#`);T`(%V]`"!=O0``%+P``!2\`&`#O0!@`[T`@#6] -M`(`UO0#`]3P`P/4\`*!6/0"@5CT`@-Z\`(#>O`#``[T`P`.]``!,/0``3#T` -M(&@]`"!H/0``7#L``%P[`(!R/`"`O0#`A+P`P(2\`$!: -M/0!`6CT`@,\\`(#//```&SP``!L\```O/```+SP``->\``#7O```S#H``,PZ -M`)#K/0"0ZST`4-$]`%#1/0"`D[T`@).]`!`,O@`0#+X``%Z[``!>NP`PE#T` -M,)0]`(!]O`"`?;P`L)V]`+"=O0#@-+T`X#2]`$#3O`!`T[P``)8[``"6.P`0 -MJCT`$*H]`&"H/0!@J#T``,>\``#'O`"@.[T`H#N]`&!&/0!@1CT`(&@]`"!H -M/0!@"KT`8`J]`*`MO0"@+;T``#`\```P/```I+P``*2\`-""O0#0@KT``#([ -M```R.P"@:CT`H&H]`$"MO`!`K;P``)B]``"8O0``_3L``/T[`-"3/0#0DST` -M8#$]`&`Q/0!`_3P`0/T\`&`(/0!@"#T`(`^]`"`/O0!`C;T`0(V]`$`(/0!` -M"#T`,)X]`#">/0#@@[T`X(.]`,@BO@#((KX`($.]`"!#O0"@D3T`H)$]`*`X -M/0"@.#T``/"Y``#PN0#`B3P`P(D\`(`W/`"`-SP`@$(\`(!"/`#0MCT`T+8] -M`/#&/0#PQCT`P$.]`,!#O0!H`;X`:`&^`(`WO`"`-[P`$),]`!"3/0#`I+P` -MP*2\`'"^O0!POKT`P"V]`,`MO0"`DKP`@)*\`&`%O0!@!;T`0,$\`$#!/`!0 -MF#T`4)@]``!S/```[P`0#6]`$`UO0"@#ST`H`\]`,##/0#` -MPST`@*.\`("CO`!`#[X`0`^^`$!VO0!`=KT``'X]``!^/0``Q#P``,0\`*!U -MO0"@=;T`X"B]`.`HO0``-#L``#0[``"0/```D#P`D)`]`)"0/0!`T3T`0-$] -M``"=.P``G3L`H*.]`*"CO0``:+H``&BZ`*"D/0"@I#T``$@Z``!(.@!0LKT` -M4+*]`"!)O0`@2;T`@*N\`("KO`#@;KT`X&Z]`$"PO`!`L+P`D)$]`)"1/0#@ -M33T`X$T]`(#QO`"`\;P``*`Z``"@.@!0FCT`4)H]```M/0``+3T``!>\```7 -MO`"`5SP`@%<\``!^NP``?KL`D)6]`)"5O0`@!KT`(`:]`+"(/0"PB#T``!T\ -M```=/`!X`KX`>`*^`&"OO0!@K[T`8'L]`&![/0#PF3T`\)D]``"1.P``D3L` -M`$"Y``!`N0``)#P``"0\```PNP``,+L`8#X]`&`^/0"PS3T`L,T]``"?.P`` -MGSL`&`2^`!@$O@"PE[T`L)>]`(!S/0"`/0"@ -M-+T`H#2]`.!#O0#@0[T`@&D]`(!I/0``I#T``*0]`("D/`"`I#P``+D[``"Y -M.P``%#L``!0[`,`[O0#`.[T`0+.\`$"SO`!0ACT`4(8]`,"\`$!.O0!`3KT`@)L\`(";/`"@!3T`H`4]`(`] -MO`"`/;P``!`]```0/0`@T3T`(-$]`$"CT`H'^]`*!_O0!`*[T`0"N]``"L.P``K#L`0+N\`$"[ -MO`#`S;P`P,V\``!P.@``<#H`X&"]`.!@O0!PS+T`<,R]```"NP```KL`8,$] -M`&#!/0!@%CT`8!8]``#)O```R;P`P!<]`,`7/0!`EST`0)<]``";.P``FSL` -M`/J[``#ZNP#@6CT`X%H]`(!&/`"`1CP`(+R]`""\O0"PB+T`L(B]``"C/``` -MHSP```"\````O`!PB+T`<(B]`$#GO`!`Y[P``*0[``"D.P"`H;P`@*&\`,#O -M/`#`[SP`4/,]`%#S/0``;CT``&X]`,"0O0#`D+T`0/V\`$#]O`"@PST`H,,] -M`,!_/0#`?ST``'*]``!RO0`PEKT`,):]`.`2O0#@$KT``#V]```]O0!`D;P` -M0)&\`$!`/0!`0#T```0[```$.P!PJ[T`<*N]``"@NP``H+L`>`8^`'@&/@!` -MJ#T`0*@]`&!'O0!@1[T`H!.]`*`3O0#`#CT`P`X]`(`%/`"`!3P`0.*\`$#B -MO```<#L``'`[`$"LO`!`K+P`@,.]`(##O0``6+T``%B]`&!G/0!@9ST`0"<] -M`$`G/0"@`;T`H`&]``"=.P``G3L`X'4]`.!U/0"`*CP`@"H\``#ONP``[[L` -MP)8]`,"6/0#`=#T`P'0]`!":O0`0FKT`T,"]`-#`O0"`>SP`@'L\`(#H/`"` -MZ#P`H':]`*!VO0!PB[T`<(N]`("1O`"`D;P`@'.\`(!SO`#`BCP`P(H\`##( -M/0`PR#T`8(8]`&"&/0`@9KT`(&:]`(`BO0"`(KT``,`]``#`/0!@MCT`8+8] -M`$`_O0!`/[T``*:]``"FO0``CKP``(Z\``!YO```>;P`P/:\`,#VO````SP` -M``,\``#+NP``R[L`4*2]`%"DO0!@&;T`8!F]`("\/0"`O#T`H*D]`*"I/0!` -M`+T`0`"]`(#)O`"`R;P`(&<]`"!G/0!``CT`0`(]`(#`O`"`P+P`@$8\`(!& -M/`"`E#P`@)0\`+":O0"PFKT`T+^]`-"_O0``,SP``#,\`&`I/0!@*3T`P,6\ -M`,#%O```.P!PH3T`<*$]```2/```$CP`P"B] -M`,`HO0``X+D``."Y``!F.P``9CL``)V\``"=O``@##T`(`P]`("#/0"`@ST` -M`"&]```AO0`PQ;T`,,6]`,"E/`#`I3P`D,`]`)#`/0"`:+P`@&B\`!#%O0`0 -MQ;T`0/^\`$#_O`!`WCP`0-X\`(!5/`"`53P`H!0]`*`4/0#`;#T`P&P]``"8 -MO```F+P`@':]`(!VO0``(ST``",]`""V/0`@MCT`@+J\`("ZO`!@M;T`8+6] -M`(`NO`"`+KP`P+X\`,"^/`!@`+T`8`"]``!NO```;KP`0#`]`$`P/0``O;L` -M`+V[`"!PO0`@<+T`0)H\`$":/```ICT``*8]``!`.P``0#L`8$^]`&!/O0"` -M*3P`@"D\`$#K/`!`ZSP`P->\`,#7O```F#H``)@Z`%"#/0!0@ST`@!^\`(`? -MO`#0XKT`T.*]```0O0``$+T``*T]``"M/0!`+#T`0"P]`$`HO0!`*+T`@!>\ -M`(`7O`#`\SP`P/,\``#E.P``Y3L``,(\``#"/`!`4ST`0%,]`.`/O0#@#[T` -M4.B]`%#HO0#`BKP`P(J\`#"R/0`PLCT``,T[``#-.P"`N;T`@+F]`$#?O`!` -MW[P`X&T]`.!M/0#@&CT`X!H]`,#//`#`SSP`H%L]`*!;/0``;CL``&X[`*"` -MO0"@@+T``#@[```X.P#0G3T`T)T]```:.P``&CL``*:]``"FO0#`Y;P`P.6\ -M``"./```CCP`0"&]`$`AO0!@,KT`8#*]`"`1/0`@$3T`@+4\`("U/`#`3[T` -MP$^]``"`.0``@#D`T-0]`-#4/0!@D3T`8)$]`,"6O`#`EKP``*F[``"INP!` -MO3P`0+T\`,#0O`#`T+P`@.B\`(#HO`!`U3P`0-4\``#"O```PKP`L/B]`+#X -MO0"`7[T`@%^]`'"U/0!PM3T`\(L]`/"+/0!@%+T`8!2]``!YO```>;P`0%`] -M`$!0/0"`_SP`@/\\`("2/`"`DCP`X$(]`.!"/0``;KL``&Z[`*"PO0"@L+T` -M`-6\``#5O``@H3T`(*$]`(!+/`"`2SP`\-Z]`/#>O0``B[T``(N]`"`#/0`@ -M`ST`0(\\`$"//```E+L``)2[`.!E/0#@93T``'D]``!Y/0!`D+P`0)"\```( -MN@``"+H``*T]``"M/0``,3T``#$]`&"'O0!@A[T`X&2]`.!DO0``9+L``&2[ -M`$`_O0!`/[T`\(F]`/")O0``ESP``)<\`.`@/0#@(#T`X#>]`.`WO0"`_KP` -M@/Z\`("Y/0"`N3T`T,4]`-#%/0"`*;P`@"F\``#HO```Z+P``.,\``#C/`#` -M@3P`P($\``"`.```@#@`0"0]`$`D/0``4#H``%`Z`+#LO0"P[+T`X,"]`.#` -MO0"`0ST`@$,]`&!D/0!@9#T`0'B]`$!XO0#`:[T`P&N]`"!%/0`@13T`@(H] -M`("*/0#@$#T`X!`]`"!?/0`@7ST`@!\]`(`?/0#`,KT`P#*]`.`OO0#@+[T` -MP#(]`,`R/0"`ECP`@)8\`(##O0"`P[T`$*B]`!"HO0#`ISP`P*<\`,"O/`#` -MKSP`0(Z\`$".O`"`#3T`@`T]`'"#/0!P@ST`P(.\`,"#O`!@#KT`8`Z]`."" -M/0#@@CT`4*`]`%"@/0#`O[P`P+^\`*`]O0"@/;T``,4[``#%.P``9[P``&>\ -M`(!5O0"`5;T``-RZ``#O0`0A#T` -M$(0]`##L/0`P[#T`@%0\`(!4/`"`W+P`@-R\`(`O/0"`+ST`(%X]`"!>/0"` -M*#P`@"@\``"=/```G3P````[````.P`PR;T`,,F]`,#7O0#`U[T``,<\``#' -M/``@=CT`('8]`&!8O0!@6+T`$)F]`!"9O0#`%3T`P!4]`#"//0`PCST``!8\ -M```6/```<3P``'$\`.`Z/0#@.CT``,*[``#"NP#`];P`P/6\`.!3/0#@4ST` -M<(D]`'")/0!`-KT`0#:]`-"IO0#0J;T``/"[``#PNP``0+L``$"[`.!CO0#@ -M8[T``""\```@O```7CT``%X]`(`*O`"`"KP`(&6]`"!EO0``-3T``#4]`-#G -M/0#0YST`P-(\`,#2/`!`-;T`0#6]```-O```#;P``""Z```@N@!@'+T`8!R] -M```6O```%KP`0+X\`$"^/`#@.[T`X#N]`!">O0`0GKT`8"H]`&`J/0!PX#T` -M<.`]``"@.@``H#H`0)^]`$"?O0``7+L``%R[`.!9/0#@63T``(D\``")/`"` -M7SP`@%\\`$`!/0!``3T`8!.]`&`3O0#@FKT`X)J]``",/```C#P``(4]``"% -M/0#@)+T`X"2]``#%O0``Q;T``."Z``#@N@!@7ST`8%\]```2.P``$CL``*0[ -M``"D.P#@=#T`X'0]``"B/```HCP`P#*]`,`RO0``.3P``#D\`$!^/0!`?CT` -M`):\``"6O`"0J+T`D*B]`,"GO`#`I[P`0(H\`$"*/`!`OKP`0+Z\``#<.@`` -MW#H`('\]`"!_/0``8CL``&([`,";O0#`F[T``#B[```XNP`@N#T`(+@]`("B -M/`"`HCP`X)"]`."0O0``M[P``+>\```=/0``'3T``.`[``#@.P``+KL``"Z[ -M`$#$/`!`Q#P`@/:\`(#VO`"0L;T`D+&]``#8.P``V#L`$-0]`!#4/0#`GSP` -MP)\\`)"GO0"0I[T``&R\``!LO``PCCT`,(X]`(#4/`"`U#P``'*\``!RO`"` -M0CP`@$(\`,"AO`#`H;P`T*"]`-"@O0``O[P``+^\`*!=/0"@73T``-0Z``#4 -M.@"@;;T`H&V]`(`M/`"`+3P`\(L]`/"+/0``^CL``/H[``"MO```K;P```X] -M```./0#`A3P`P(4\`#""O0`P@KT`@..\`(#CO`#`A#T`P(0]`$#H/`!`Z#P` -M`'R]``!\O0"@$[T`H!.]`("@/`"`H#P``)6[``"5NP``R+L``,B[`"`S/0`@ -M,ST``*0[``"D.P"PFKT`L)J]``!\O```?+P`P-`]`,#0/0``9#T``&0]`(") -MO0"`B;T`P%&]`,!1O0!`S#P`0,P\```H.@``*#H`H`V]`*`-O0``4+H``%"Z -M``#$NP``Q+L`($J]`"!*O0``8CP``&(\`&#E/0!@Y3T`H%L]`*!;/0#`@KT` -MP(*]`(``O0"``+T`H$4]`*!%/0``#3P```T\`"`[O0`@.[T``".\```CO``` -MH;L``*&[`*"!O0"@@;T`@!:]`(`6O0!`63T`0%D]``#G/```YSP``%R]``!< -MO0"`A[P`@(>\``!U/0``=3T`0.,\`$#C/```V[L``-N[``!4/0``5#T`8%X] -M`&!>/0`@0;T`($&]`$!9O0!`6;T`($,]`"!#/0#``ST`P`,]`,"XO0#`N+T` -M$,J]`!#*O0"`*[P`@"N\```&/```!CP``(BZ``"(N@#`/CT`P#X]`,`U/0#` -M-3T``/6\``#UO```Y+L``.2[`$#(/0!`R#T`0(L]`$"+/0#0B;T`T(F]``"6 -MO0``EKT`P,X\`,#./`#`O3P`P+T\`,`HO0#`*+T``.R\``#LO```#[P```^\ -M`(!%O0"`1;T`@-"\`(#0O`"@C3T`H(T]`.!O/0#@;ST`P":]`,`FO0#`VKP` -MP-J\`)"#/0"0@ST`X$8]`.!&/0``C[P``(^\``"<.P``G#L``*$\``"A/``` -M@+T``("]`""]O0`@O;T```"Z````N@#`^#P`P/@\`(`NO0"`+KT`X!V]`.`= -MO0!`2#T`0$@]`*!@/0"@8#T`@$4\`(!%/```,3T``#$]`,!H/0#`:#T`H`>] -M`*`'O0#@>+T`X'B]`*`=/0"@'3T`@%D]`(!9/0"0C;T`D(V]`)#[T`@*:\`("FO`!`5+T`0%2] -M`"!UO0`@=;T`H!0]`*`4/0#PGST`\)\]``!VNP``=KL``-V\``#=O`!@73T` -M8%T]`.!^/0#@?CT``!6\```5O```&+L``!B[`(`'/0"`!ST`X`:]`.`&O0`0 -MMKT`$+:]```MO```+;P``$$]``!!/0``![T```>]`*"HO0"@J+T``-"[``#0 -MNP`@*#T`("@]`(!(/`"`2#P`0-P\`$#]`("7O0#` -MG;P`P)V\`&!E/0!@93T`@$^\`(!/O`#`PKT`P,*]`.`*O0#@"KT`@"<]`(`G -M/0"`@CP`@((\```0.P``$#L```P]```,/0``/#L``#P[`,`JO0#`*KT`P,\\ -M`,#//`!`LST`0+,]``#`NP``P+L`@,Z]`(#.O0!@`KT`8`*]`,!+/0#`2ST` -M```[````.P#@+[T`X"^]``#TN@``]+H``#P[```\.P#`&KT`P!J]`(`'/`"` -M!SP`P)T]`,"=/0``OSP``+\\``!`O0``0+T``!0\```4/`#@?3T`X'T]``#@ -MN@``X+H`P#.]`,`SO0``0#L``$`[``#ONP``[[L`4)N]`%";O0!@'[T`8!^] -M`*!F/0"@9CT``/0\``#T/`"@4[T`H%.]`$"AO`!`H;P`0%$]`$!1/0"`YSP` -M@.<\``"\N@``O+H`@.P\`(#L/`"`(#P`@"`\`(!DO0"`9+T``**\``"BO`#P -M@ST`\(,]`$"'/`!`ASP`T*R]`-"LO0#`-KT`P#:]`.!`/0#@0#T`P,H\`,#* -M/`#`R;P`P,F\```@.@``(#H``+\[``"_.P!`!+T`0`2]``#8.P``V#L``*`] -M``"@/0#`JSP`P*L\`."DO0#@I+T`P"B]`,`HO0!`-CT`0#8]```V/```-CP` -M@"V]`(`MO0``_#H``/PZ`$#W/`!`]SP`@+N\`("[O`"`F;P`@)F\`$!B/0!` -M8CT`X!4]`.`5/0``.+T``#B]`$#-O`!`S;P`P!H]`,`:/0``B3L``(D[`$`Q -MO0!`,;T``#2[```TNP"`3SP`@$\\`(!*O0"`2KT`H!>]`*`7O0"`>CT`@'H] -M`(!A/0"`83T`@#B]`(`XO0!@-KT`8#:]`&`E/0!@)3T`@"H]`(`J/0``_KL` -M`/Z[``"X.P``N#L``-@[``#8.P"@8+T`H&"]`*`EO0"@);T`X%4]`.!5/0`@ -M#CT`(`X]`)"5O0"0E;T`P&R]`,!LO0#`5CT`P%8]`$!F/0!`9CT`@&.\`(!C -MO```CKL``(Z[``#(/```R#P``'>\``!WO`!`K;P`0*V\`*`E/0"@)3T``,,\ -M``##/```D;T``)&]`("%O0"`A;T``,`\``#`/```H3P``*$\`*`9O0"@&;T` -M`%:[``!6NP#@.ST`X#L]```6.P``%CL`0)R\`$"[``!1/```43P`P`R]`,`,O0`@9KT`(&:]```R/```,CP`0*,\`$"C/`#` -MD+T`P)"]`!">O0`0GKT`P``]`,``/0``@CT``((]``"QNP``L;L`@!&\`(`1 -MO`"`)ST`@"<]``"7/```ESP``&6\``!EO``@!ST`(`<]`,`W/0#`-ST`0#.] -M`$`SO0!PH[T`<*.]``#HNP``Z+L`@$P\`(!,/`"@++T`H"R]`,"VO`#`MKP` -M0!P]`$`]`,"G -MO`#`I[P`($:]`"!&O0`PA[T`,(>]``")/```B3P``(T]``"-/0"`*KP`@"J\ -M``!KT`H#&] -M`*`QO0!`M3P`0+4\``!6NP``5KL`H#B]`*`XO0"`;+P`@&R\``#4NP``U+L` -MP#J]`,`ZO0``.KP``#J\`-""/0#0@CT`@.$\`(#A/`#@9[T`X&>]`$"$O`!` -MA+P`$)D]`!"9/0``/CT``#X]`,#@O`#`X+P`P).\`,"3O```"+H```BZ`*`4 -MO0"@%+T`0.B\`$#HO`#`Q#P`P,0\``#&NP``QKL`P):]`,"6O0#`QKP`P,:\ -M`&")/0!@B3T``#(]```R/0``:;P``&F\``!M/```;3P`X!H]`.`:/0``>+P` -M`'B\``#LO```[+P`P+X\`,"^/```*#L``"@[`%"6O0!0EKT``$:]``!&O0`@ -M!3T`(`4]`("O0`@'KT`P!Z]`,`>O0"`7[P`@%^\```CO0``([T` -M`-Z\``#>O`"`,3T`@#$]`(`%/0"`!3T`H$6]`*!%O0"`[[P`@.^\`("*/0"` -MBCT`8&T]`&!M/0```;T```&]```4O0``%+T``,,[``##.P``5+P``%2\`,"O -MO`#`K[P`P/T\`,#]/`"`]3P`@/4\`,`NO0#`+KT`@"&]`(`AO0"@%ST`H!<] -M`$#N/`!`[CP`H!2]`*`4O0``FKP``)J\`(#3/`"`TSP```R\```,O`"`#;T` -M@`V]``"]/```O3P`0"$]`$`A/0`@(+T`(""]`,!NO0#`;KT`0)T\`$"=/`#@ -M&#T`X!@]`("9O`"`F;P`@(R\`(",O```N3P``+D\```<.P``'#L`@#6\`(`U -MO```0ST``$,]`*!1/0"@43T`X$"]`.!`O0#PGKT`\)Z]``"+.P``BSL`0/H\ -M`$#Z/`"`_+P`@/R\`*`AO0"@(;T``/H[``#Z.P``E3L``)4[``"ENP``I;L` -M(#`]`"`P/0!`.3T`0#D]```8O0``&+T`(%6]`"!5O0#@##T`X`P]`&!7/0!@ -M5ST`@-J\`(#:O`#`++T`P"R]`(`C/`"`(SP``+RZ``"\N@#`$;T`P!&]``"> -M.P``GCL```$]```!/0``[[P``.^\`&!0O0!@4+T`0.(\`$#B/`"@=3T`H'4] -M``#(.@``R#H``""\```@O`"`]3P`@/4\``!@.P``8#L`X#>]`.`WO0"`.KP` -M@#J\`$"\`"`^/0`@/CT``-0[``#4.P#@ -M%+T`X!2]``"O.P``KSL``!`]```0/0``E3P``)4\`*`J/0"@*CT`,)<]`#"7 -M/0``'SP``!\\`$"(O0!`B+T`@->\`(#7O`"`U#P`@-0\`*`!O0"@`;T`0+6] -M`$"UO0!`)KT`0":]`(`J/`"`*CP``(R[``",NP"`)SP`@"<\``!T/0``=#T` -MX`@]`.`(/0#`IKP`P*:\``"8/```F#P`L)(]`+"2/0``OCP``+X\`.`3O0#@ -M$[T`@"B\`(`HO```GSL``)\[`$`PO0!`,+T`("6]`"`EO0``7CP``%X\`(`[ -MO`"`.[P`@)F]`("9O0#`$;T`P!&]`"!3/0`@4ST``#L]```[/0``\#H``/`Z -M`(#J/`"`ZCP`0%P]`$!] -M`$`7O0``E3L``)4[`,#!O`#`P;P`0!V]`$`=O0``6CP``%H\``##/```PSP` -M`)V\``"=O`"`&CP`@!H\`/"H/0#PJ#T`T(`]`-"`/0"`';P`@!V\``!>O``` -M7KP``(8[``"&.P#@$;T`X!&]`,!-O0#`3;T```X[```..P``]#H``/0Z`$!\ -MO0!`?+T`0#2]`$`TO0#@0#T`X$`]`*!G/0"@9ST```B\```(O`"`&[P`@!N\ -M`&`!/0!@`3T`P(T\`,"-/```5#L``%0[`,`8/0#`&#T`0+$\`$"Q/```5KT` -M`%:]`.`_O0#@/[T`P`,]`,`#/0"`PCP`@,(\`&!SO0!@<[T`('"]`"!PO0`` -MISL``*<[```R/```,CP``*0Z``"D.@``+3T``"T]`&!9/0!@63T``(F[``") -MNP"`:+P`@&B\`*`I/0"@*3T``-T\``#=/`#`4[T`P%.]`&!\`(!,/`"` -M3#P``+X[``"^.P``@+P``("\`("&/`"`ACP`P*H\`,"J/```5KT``%:]`."0 -MO0#@D+T`@!L\`(`;/`!@(3T`8"$]`("MO`"`K;P`(`:]`"`&O0``_CP``/X\ -M`.!`/0#@0#T`0(,\`$"#/`!`S#P`0,P\`(#'/`"`QSP`8!2]`&`4O0"`6+T` -M@%B]``!T/```=#P`(!$]`"`1/0#`\KP`P/*\```WO0``-[T`@&0\`(!D/`#` -MOSP`P+\\```GO```)[P``-$[``#1.P!`VSP`0-L\`(";O`"`F[P`P#:]`,`V -MO0``83P``&$\`"`Q/0`@,3T``"V\```MO`!`X;P`0.&\`$"8/`!`F#P`@(4\ -M`("%/`"`O[P`@+^\```.NP``#KL``/`\``#P/`"`BKP`@(J\``!&O0``1KT` -M`+`\``"P/``@C3T`((T]```W/```-SP`8!"]`&`0O0``#[P```^\```@N@`` -M(+H`P.*\`,#BO`"`-[P`@#>\`(!M/`"`;3P`0.>\`$#GO`!@3KT`8$Z]`$#0 -M/`!`T#P`@)D]`("9/0"`:3P`@&D\`.`%O0#@!;T```X\```./`#``CT`P`(] -M``#'NP``Q[L``"F\```IO`"`0CP`@$(\``"HO```J+P`0$&]`$!!O0``O#L` -M`+P[```R/0``,CT`@!*\`(`2O`#@0+T`X$"]```ZNP``.KL``*D\``"I/``` -M,[P``#.\``#P.@``\#H`H#4]`*`U/0``?SP``'\\`"`)O0`@";T```"Y```` -MN0``+ST``"\]``#6NP``UKL`@&R]`(!LO0"`N;P`@+F\```)/```"3P`@#&\ -M`(`QO```9#L``&0[`(`N/0"`+CT`@&H\`(!J/`!`!;T`0`6]`(`E/`"`)3P` -ML(0]`+"$/0"`6#P`@%@\`,!GO0#`9[T`H!2]`*`4O0``H#D``*`Y`(!5O`"` -M5;P`@%"\`(!0O```@#P``(`\``"WNP``M[L``"J]```JO0``GSL``)\[`.!Y -M/0#@>3T``)L\``";/`!@!KT`8`:]``#1.P``T3L`8$T]`&!-/0"`CCP`@(X\ -M``` -M/```QCL``,8[`(!IO0"`:;T`P"^]`,`OO0``=CL``'8[``"+NP``B[L``":\ -M```FO`"`ICP`@*8\``!-/```33P`@.N\`(#KO`"`CKP`@(Z\`*`:/0"@&CT` -MX&`]`.!@/0``E+L``)2[`,`8O0#`&+T`0'8]`$!V/0"@"3T`H`D]`-"KO0#0 -MJ[T`8`6]`&`%O0"`MKP`@+:\`/"-O0#PC;T```&\```!O`!`GCP`0)X\```+ -MO```"[P`H`D]`*`)/0#`KST`P*\]```!/````3P``,L[``#+.P``GST``)\] -M`$#0/`!`T#P`0(&]`$"!O0`@3+T`($R]``"F/```ICP`@":\`(`FO`!PJKT` -M<*J]`&!8O0!@6+T``)D[``"9.P#`YSP`P.<\``"XNP``N+L```*[```"NP`` -MC3L``(T[``"7/```ESP`4)`]`%"0/0`@PST`(,,]`&`:/0!@&CT`8'J]`&!Z -MO0`0D+T`$)"]``"CNP``H[L``&&\``!AO`#`;KT`P&Z]`.!+O0#@2[T`H!B] -M`*`8O0`@]`!"GO0"`>;T`@'F]``!,/```3#P`@#`\`(`P/``@0+T`($"] -M``!JO```:KP``(T[``"-.P#`AKP`P(:\`,`?/0#`'ST`T(8]`-"&/0``JCL` -M`*H[`.`AO0#@(;T``*@Z``"H.@``ZSP``.L\```@.0``(#D``,Z\``#.O`!` -M#[T`0`^]```3O```$[P``%V\``!=O```8+H``&"Z`,"U/`#`M3P``!\\```? -M/`"`$[P`@!.\`,``/0#``#T`X",]`.`C/0``#KL```Z[```1O0``$;T`X&F] -M`.!IO0"`EKP`@):\```BNP``(KL`@+6\`("UO```&CL``!H[`("[/`"`NSP` -M@`L\`(`+/`!`^3P`0/D\`("Z/0"`NCT``+H\``"Z/`"`<[T`@'.]`&`)O0!@ -M";T`0.*\`$#BO`"`Q[P`@,>\`$"FO`!`IKP`0(.\`$"#O`"`V;P`@-F\```6 -MO```%KP`H$8]`*!&/0#`L#T`P+`]`,`*/0#`"CT`P!"]`,`0O0!`I;P`0*6\ -M``"4N@``E+H`@"R\`(`LO```';P``!V\`,`#O0#``[T`<(>]`'"'O0```+T` -M``"]`$#\/`!`_#P`@"\]`(`O/0#`-3T`P#4]`$"D/`!`I#P``#L\```[/``` -M`ST```,]``#./```SCP``$@[``!(.P!`YKP`0.:\`$!;O0!`6[T`D+R]`)"\ -MO0"`8[T`@&.]``!"/```0CP``$8\``!&/`"`(KP`@"*\``#K.P``ZSL`P%`] -M`,!0/0"@=CT`H'8]`(",/0"`C#T`<)(]`'"2/0``)[P``">\`."BO0#@HKT` -M(%V]`"!=O0``&+P``!B\`(#XO`"`^+P`X'J]`.!ZO0"@&+T`H!B]```,NP`` -M#+L`@"4\`(`E/`"`;ST`@&\]`,"L/0#`K#T``,PZ``#,.@#`([T`P".]`(!W -M/`"`=SP`P#@]`,`X/0``BKL``(J[`(`[O0"`.[T`0%.]`$!3O0``MKP``+:\ -M`(!W/`"`=SP``-\[``#?.P``*3P``"D\``#O.P``[SL`8!:]`&`6O0``.#L` -M`#@[`$!P/0!`<#T`0(D\`$")/`#@-;T`X#6]`(#7O`"`U[P``%<\``!7/``` -M,#P``#`\````N0```+D``%8[``!6.P``$SP``!,\``#>O```WKP`@&:\`(!F -MO`#@C3T`X(T]`$"5/`!`E3P`X,J]`.#*O0!@;+T`8&R]`(![T`,*6]`#"EO0#`@;P`P(&\``#0NP`` -MT+L`(&*]`"!BO0!`:KT`0&J]`*!+O0"@2[T``!"Z```0N@!@I#T`8*0]`"#7 -M/0`@UST``'8]``!V/0"`I[P`@*>\`("HO`"`J+P`@%(]`(!2/0"@1CT`H$8] -M`.!8O0#@6+T`<-6]`'#5O0"`B+T`@(B]`$!KO0!`:[T``)R\``"]``!ZO0``>KT`P,"\`,#`O`"@!CT`H`8]``";.P`` -MFSL`@!.\`(`3O`"`>3P`@'D\`(!7O`"`5[P`P+Z\`,"^O`"`-ST`@#<]`"`F -M/0`@)CT`X#V]`.`]O0#@!KT`X`:]``#\/```_#P`0-4\`$#5/```@KL``(*[ -M``#Z.P``^CL`P(0\`,"$/```2;P``$F\`.`AO0#@(;T``(>[``"'NP``R3P` -M`,D\`(!2O0"`4KT``&^]``!OO0``D#L``)`[``!D/```9#P`@!,\`(`3/``` -MFSP``)L\`("3P``'D\`.!#O0#@0[T`(&N] -M`"!KO0#`FCP`P)H\`(!1/0"`43T`("<]`"`G/0``4#T``%`]`"`4/0`@%#T` -M`&&\``!AO`!`%;T`0!6]```!/````3P`P(`\`,"`/`#`>+T`P'B]``#5O0`` -MU;T`X!:]`.`6O0#`S#P`P,P\`$#6/`!`UCP``$(]``!"/0``J#P``*@\``!K -MO```:[P`0-L\`$#;/`!@G#T`8)P]`.`N/0#@+CT`P$.]`,!#O0#0AKT`T(:] -M`(`AO0"`(;T`0)"\`$"0O`"`0KP`@$*\`(#'O`"`Q[P`P)*\`,"2O`"`3+P` -M@$R\``#H.P``Z#L`(&`]`"!@/0"PF3T`L)D]``"L/```K#P`8!*]`&`2O0"` -MPKP`@,*\```"O````KP`0(Z\`$".O`"`:+P`@&B\`,"BO`#`HKP`X$B]`.!( -MO0``O;P``+V\`$`B/0!`(CT`\(8]`/"&/0!`OCP`0+X\`,"4O`#`E+P``)`\ -M``"0/`"`&3P`@!D\`*`LO0"@++T``*J\``"JO```&#L``!@[`$!2O0!`4KT` -MH&"]`*!@O0``^3P``/D\`.`9/0#@&3T``#(\```R/`#`D#P`P)`\```!/``` -M`3P``)([``"2.P``N;L``+F[``#/NP``S[L``.8[``#F.P``?KP``'Z\`(`A -MO0"`(;T`@`^\`(`/O```;#L``&P[`("BO`"`HKP```$\```!/`#`-#T`P#0] -M``#".P``PCL`0-"\`$#0O```8+H``&"Z`(#W/`"`]SP`@(D\`(")/```3KT` -M`$Z]`(!)O0"`2;T`0(L\`$"+/`"`*+P`@"B\`(#]O`"`_;P``.T\``#M/`#` -MW3P`P-T\`,"*O`#`BKP`@'D\`(!Y/`"@6CT`H%H]```(NP``"+L`@"R]`(`L -MO0#`I+P`P*2\`(!MO`"`;;P`P!>]`,`7O0`@"+T`(`B]`,#"/`#`PCP`8!L] -M`&`;/0``\[L``/.[`$"$/`!`A#P`@%L]`(!;/0``0CP``$(\`,`,O0#`#+T` -M`&Z\``!NO`"`&+P`@!B\``!HO0``:+T`\)*]`/"2O0!`J+P`0*B\`,#W/`#` -M]SP`0-D\`$#9/`"`$3P`@!$\`(`>/0"`'CT`P'`]`,!P/0#`M3P`P+4\`(`5 -M/`"`%3P`0(L\`$"+/`"`][P`@/>\`$">O0!`GKT`P!J]`,`:O0``0+D``$"Y -M`$!3O0!`4[T`H&&]`*!AO0"`:3P`@&D\``!K/```:SP``-PZ``#<.@#`+ST` -MP"\]`+"X/0"PN#T``%<]``!7/0!`EKP`0):\`(`FO`"`)KP``&4\``!E/`!` -MU;P`0-6\`+"4O0"PE+T`8%>]`&!7O0#`&[T`P!N]```EO0``);T`@(`\`("` -M/`"`13T`@$4]`,#G/`#`YSP`P/,\`,#S/`"`!ST`@`<]``#'/```QSP``.,[ -M``#C.P"`%[T`@!>]```IO0``*;T`@%*\`(!2O```'+T``!R]`*`+O0"@"[T` -MP*H\`,"J/```NCP``+H\`(!5O`"`5;P``(X\``"./`"`^#P`@/@\`(!:O`"` -M6KP`@*J\`("JO```3#L``$P[`,`!/0#``3T``$@Z``!(.@#`+KT`P"Z]``"< -MNP``G+L``)D\``"9/```_CL``/X[``!Z/```>CP``(F[``")NP`0AKT`$(:] -M`$`CO0!`([T`8"X]`&`N/0"`=#P`@'0\`"`2O0`@$KT`P-&\`,#1O```&#H` -M`!@Z`$"Z/`!`NCP`P`$]`,`!/0!`LSP`0+,\`,"3/`#`DSP`@`V\`(`-O`"` -M3KP`@$Z\`*`>/0"@'CT`H!$]`*`1/0"@&;T`H!F]`*"KO0"@J[T``'^]``!_ -MO0!`#KT`0`Z]`,"0O`#`D+P`@$@\`(!(/```C3P``(T\`,"L/`#`K#P`X`<] -M`.`'/0#PGCT`\)X]`)"[/0"0NST``.4[``#E.P"`-KT`@#:]```!O0```;T` -M(":]`"`FO0"`JKT`@*J]`*!@O0"@8+T``+6[``"UNP!`\KP`0/*\`,#%O`#` -MQ;P`('`]`"!P/0#PLCT`\+(]`(`O/0"`+ST`0(<\`$"'/`!`E#P`0)0\`(`- -M/`"`#3P``&^\``!OO`"`^KP`@/J\`&``O0!@`+T`H#&]`*`QO0"@.+T`H#B] -M``"ZO```NKP``%2[``!4NP"`@;P`@(&\`(`./`"`#CP`P%H]`,!:/0#``ST` -MP`,]`(""/`"`@CP`0+\\`$"_/`!@$ST`8!,]`("+/`"`BSP`H`N]`*`+O0#@ -M/;T`X#V]``!:O```6KP``)B\``"8O```6[T``%N]`(!PO`"`<+P``.T\``#M -M/```BSL``(L[`,"J/`#`JCP`X!4]`.`5/0``&KP``!J\`$"CO`!`H[P`@`(\ -M`(`"/```"+H```BZ`,"DO`#`I+P`P**\`,"BO`"`3CP`@$X\`(`(/0"`"#T` -M`(T\``"-/```FSP``)L\`(!#/`"`0SP`8#.]`&`SO0#`@KT`P(*]```T/``` -M-#P``-X\``#>/`"@"KT`H`J]`.`FO0#@)KT```:[```&NP#`.CT`P#H]`."* -M/0#@BCT``,\\``#//`#`@KP`P(*\`(#FO`"`YKP``(J\``"*O```13P``$4\ -M``"O/```KSP`(#*]`"`RO0#PG[T`\)^]``"8NP``F+L`@#$]`(`Q/0``F3P` -M`)D\`(`X/`"`.#P`P(`\`,"`/```G+L``)R[``#KNP``Z[L`X$L]`.!+/0"@ -MEST`H)<]`$"&O`!`AKP`P+>]`,"WO0#`.[T`P#N]`(!=/`"`73P`@#.\`(`S -MO`"@-[T`H#>]`,#QO`#`\;P``'"\``!PO```?3P``'T\`""O/0`@KST`D*\] -M`)"O/0!`HSP`0*,\`("@O`"`H+P``-"Z``#0N@``(3P``"$\`(#)O`"`R;P` -M8&F]`&!IO0``;;T``&V]`"`,O0`@#+T``!R\```]``#+.P``RSL`8$T]`&!-/0``2#H``$@Z`$!H -MO0!`:+T`X!>]`.`7O0``0#D``$`Y``!H.P``:#L``).[``"3NP"`+3P`@"T\ -M`(`[/`"`.SP`@"J\`(`JO`#`A#P`P(0\`(!5/0"`53T```:[```&NP#`\KP` -MP/*\`("1/`"`D3P``+`[``"P.P"@$[T`H!.]`,""O`#`@KP``-BZ``#8N@!` -MI[P`0*>\`"`$O0`@!+T``!J[```:NP!@(3T`8"$]`,`G/0#`)ST```R[```, -MNP``(#D``"`Y`,`[/0#`.ST`@'L\`(![/`!`F+P`0)B\`,"SO`#`L[P`X"B] -M`.`HO0#`*[T`P"N]```=O```';P``/P\``#\/`#`&#T`P!@]``"#.P``@SL` -M`,R[``#,NP#`GCP`P)X\``"7/```ESP``.^[``#ONP``X+H``."Z``#4O``` -MU+P`0)"]`$"0O0"`=;P`@'6\`#"6/0`PECT`P#@]`,`X/0"`AKP`@(:\`("6 -MO`"`EKP`@#X\`(`^/`#`T3P`P-$\``"&.P``ACL`0)F\`$"9O`"`,KT`@#*] -M`&`>O0!@'KT``"BZ```HN@"`%ST`@!<]`$`M/0!`+3T``.$[``#A.P"`7[P` -M@%^\`(`;O`"`&[P``!H\```:/``@*#T`("@]`(#H/`"`Z#P`0-J\`$#:O``` -MB;T``(F]`.`%O0#@!;T``)4\``"5/`!`ASP`0(<\`(#QO`"`\;P`0$&]`$!! -MO0``-CL``#8[`,!>/0#`7CT`P%X]`,!>/0#@33T`X$T]`*`//0"@#ST`@(J\ -M`("*O`"`1+P`@$2\`(")/`"`B3P`P,B\`,#(O```E[T``)>]`#"`O0`P@+T` -MP"B]`,`HO0#`S;P`P,V\`.`//0#@#ST`D(T]`)"-/0`@'ST`(!\]`,":/`#` -MFCP`@!8]`(`6/0!`B3T`0(D]``"9.P``F3L``):]``"6O0!`.KT`0#J]`(`- -MO`"`#;P`0`"]`$``O0``%;T``!6]``#@NP``X+L`@`$\`(`!/```NSP``+L\ -M`(`1/0"`$3T`0-0\`$#4/`!`F#P`0)@\``#Q.P``\3L``+X[``"^.P!`W3P` -M0-T\``"-NP``C;L`@$V]`(!-O0"`\`,"K/`#`JSP`()0]`""4 -M/0"@-#T`H#0]`$#&O`!`QKP`0,R\`$#,O```@CP``((\`(`)O`"`";P``*:\ -M``"FO`"`)CP`@"8\``"JNP``JKL``+6\``"UO```FCL``)H[`("L/`"`K#P` -M`-6[``#5NP!`W[P`0-^\`$"NO`!`KKP``)>\``"7O`"`9KP`@&:\``"T.P`` -MM#L`P`X]`,`./0!@+#T`8"P]`$")/`!`B3P`@*$\`("A/`"`7CT`@%X]`,#8 -M/`#`V#P`8`^]`&`/O0`@5+T`(%2]`"`0O0`@$+T``$J]``!*O0``?;T``'V] -M```7O```%[P`@/\\`(#_/`#`F3P`P)D\`(!7/`"`5SP```D]```)/0"`4CT` -M@%(]`&`;/0!@&ST`@/L\`(#[/`"`)#P`@"0\`&!"O0!@0KT``%6]``!5O0`` -M``````````#/NP``S[L`X(>]`."'O0!@6+T`8%B]``!8.P``6#L``%<\``!7 -M/```ZSP``.L\`*!P/0"@<#T`8&8]`&!F/0!`!3T`0`4]`(`(/`"`"#P`@&D\ -M`(!I/`"`5SP`@%<\```IO0``*;T`X)^]`."?O0#@:;T`X&F]`*`:O0"@&KT` -M`->\``#7O```6#P``%@\`&`V/0!@-CT`@`0]`(`$/0"@!#T`H`0]`*`T/0"@ -M-#T`0`T]`$`-/0``]#H``/0Z`(#9O`"`V;P``#BZ```XN@``%KP``!:\`!"/ -MO0`0C[T`P`^]`,`/O0#@'ST`X!\]`(!_/`"`?SP`X`>]`.`'O0``;+L``&R[ -M`(#F/`"`YCP`@!T\`(`=/```!CP```8\`(`[/`"`.SP``':[``!VNP``H;P` -M`*&\`(`-O`"`#;P`@-\\`(#?/`"`L3P`@+$\`(!\`,#>/`#`WCP`(`<]`"`'/0"`1+P`@$2\`,#TO`#`]+P`P)<\ -M`,"7/``@#CT`(`X]`(`Z/`"`.CP`0+H\`$"Z/```93P``&4\`*`8O0"@&+T` -M(%R]`"!/0`@7CT`P`8]`,`&/0``1KP``$:\``!4 -MO```5+P``)0[``"4.P#`Z+P`P.B\`"";O0`@F[T`X$R]`.!,O0``:#P``&@\ -M`,"Z/`#`NCP``.R[``#LNP``Q#H``,0Z`(#O```";P```F\`$#8/`!`V#P`@%(\ -M`(!2/`!@,+T`8#"]`&!TO0!@=+T`P+B\`,"XO```0#D``$`Y`("7/`"`ESP` -M0$X]`$!./0#@(3T`X"$]``!0.P``4#L``($\``"!/```ZSP``.L\``"".P`` -M@CL``"&\```AO`#`VKP`P-J\`&!PO0!@<+T`H!V]`*`=O0``!#P```0\`&`. -M/0!@#CT`@&,\`(!C/`#`SKP`P,Z\``",N@``C+H`H$(]`*!"/0"@,CT`H#(] -M``!0N@``4+H`@"V\`(`MO```&+L``!B[`(".O`"`CKP``%>\``!7O```1#P` -M`$0\``!0/```4#P``(>\``"'O```\KP``/*\`(`@/`"`(#P`0.,\`$#C/`"` -M<#P`@'`\`,"-/`#`C3P``)H[``":.P!@!KT`8`:]`(!\O`"`?+P`0!H]`$`: -M/0#`HSP`P*,\`(!+O0"`2[T`H`>]`*`'O0``#CT```X]`.`0/0#@$#T``/,[ -M``#S.P``VCL``-H[`(`%/`"`!3P`0(V\`$"-O```I[P``*>\``!)/```23P` -M@"X\`(`N/`#`W;P`P-V\`.`0O0#@$+T`@)`\`("0/`!@7CT`8%X]`$#3/`!` -MTSP`@"X\`(`N/```]#L``/0[``"O`"`'KP`X&`]`.!@/0#@<#T`X'`]``#G.P``YSL``$\\``!//`"`0SP` -M@$,\`&`=O0!@';T`($"]`"!`O0"`/KP`@#Z\``"5NP``E;L`8!6]`&`5O0"` -M0KT`@$*]``#ONP``[[L`0!(]`$`2/0!@;CT`8&X]`$!A/0!`83T`0`X]`$`. -M/0``-+L``#2[``"..P``CCL`0#$]`$`Q/0``QSL``,<[`*"-O0"@C;T`L*F] -M`+"IO0"`.+T`@#B]``!D.P``9#L``&0\``!D/```<#P``'`\`$#T/`!`]#P` -M`.(\``#B/`#`ICP`P*8\``!0/0``4#T`((`]`""`/0``_KL``/Z[`.!YO0#@ -M>;T`P`6]`,`%O0``X+H``."Z``#@.0``X#D``':[``!VNP``SKP``,Z\`*`B -MO0"@(KT``">\```GO``@23T`($D]``!`/0``0#T`@#.\`(`SO`"`G+P`@)R\ -M``#!/```P3P`0/D\`$#Y/`"``CP`@`(\`(!#/`"`0SP`@*X\`("N/`"`FKP` -M@)J\`$`%O0!`!;T``-BZ``#8N@#`@CP`P((\```DNP``)+L`0/Z\`$#^O`!` -MS[P`0,^\``!VNP``=KL`@%(\`(!2/```D#P``)`\```8.P``&#L``%J\``!: -MO```\;L``/&[`&`-/0!@#3T`X'\]`.!_/0``MCP``+8\``#NNP``[KL`@#0\ -M`(`T/```]#H``/0Z`.`FO0#@)KT`0!Z]`$`>O0#`C;P`P(V\`&`+O0!@"[T` -MP$*]`,!"O0``"#P```@\`,!Y/0#`>3T`X$T]`.!-/0``CSP``(\\`,`!/0#` -M`3T`X$0]`.!$/0``+#P``"P\`,"&O`#`AKP`X`&]`.`!O0!`?[T`0'^]``!1 -MO0``4;T`@`.\`(`#O```%CL``!8[``!VO```=KP``+PZ``"\.@"`)#T`@"0] -M`*!8/0"@6#T`(!L]`"`;/0"`DSP`@),\`(#O/`"`[SP``'$\``!Q/`"`T[P` -M@-.\`("KO`"`J[P``)^[``"?NP!@,+T`8#"]`$"1O0!`D;T`8"&]`&`AO0#` -M@SP`P(,\`(#V/`"`]CP`@%X\`(!>/`"`>#P`@'@\`.`'/0#@!ST`0#L]`$`[ -M/0#@73T`X%T]`(`6/0"`%CT``/R\``#\O`!@EKT`8):]`("VO`"`MKP``)<[ -M``"7.P"`S;P`@,V\`("YO`"`N;P``!`Z```0.@"`.SP`@#L\``"(/```B#P` -M@"D]`(`I/0"@#CT`H`X]``",O```C+P`8#Z]`&`^O0"`;[P`@&^\`$#O/`!` -M[SP`P,<\`,#'/```U;L``-6[`(`$O`"`!+P``"P[```L.P``<#P``'`\``". -M/```CCP``,2Z``#$N@#`Y;P`P.6\`,#KO`#`Z[P``!:[```6NP"`NP``WKL``/"[``#PNP``D[L``).[ -M``!@/```8#P``!(\```2/```4[P``%.\``!.NP``3KL``#,\```S/```3#L` -M`$P[``"$O```A+P`@)R\`("+L``&F\``!IO```6KL``%J[````````````@!2\ -M`(`4O`!`L+P`0+"\`,!CT`@'H]`(`?/0"`'ST``/.[``#S -MNP``H+L``*"[``!I/```:3P`@"@\`(`H/```\;L``/&[`(`QO0"`,;T`X(B] -M`."(O0"@9[T`H&>]`("QO`"`L;P`P+`\`,"P/`"`]3P`@/4\`,"R/`#`LCP` -M8!8]`&`6/0"`@#T`@(`]`,!Z/0#`>CT`P!D]`,`9/0``]#H``/0Z`,!CO0#` -M8[T`0+V]`$"]O0#`5KT`P%:]``#,.@``S#H``+Z[``"^NP"`+KT`@"Z]`("6 -MO`"`EKP`@$,]`(!#/0#@;#T`X&P]`.`!/0#@`3T``*T\``"M/```I#H``*0Z -M`(!MO`"`;;P``-`Z``#0.@"`C#P`@(P\```6O```%KP`X`>]`.`'O0#`A+P` -MP(2\```/O```#[P`@$F\`(!)O```"CL```H[``"./```CCP```BZ```(N@#` -MLKP`P+*\``"U.P``M3L`H"\]`*`O/0!`N3P`0+D\`,"8O`#`F+P`0+*\`$"R -MO```=CL``'8[``!\NP``?+L`P,"\`,#`O```Q;L``,6[`,"$/`#`A#P```0\ -M```$/```>CL``'H[``"Q/```L3P`@(\\`("//`"`H[P`@*.\`(#,O`"`S+P` -M`,B[``#(NP"`>+P`@'B\`,"CO`#`H[P`@&`\`(!@/`"@"3T`H`D]``#J.P`` -MZCL``#<\```W/`#`*ST`P"L]``#O.P``[SL`8$R]`&!,O0!@';T`8!V]``!$ -MNP``1+L``%N\``!;O``@);T`("6]`(`=O`"`';P`@!<]`(`7/0"`,ST`@#,] -M`,#B/`#`XCP`@+,\`("S/`#`@CP`P((\```9O```&;P`0*N\`$"KO`"`&KP` -M@!J\`(!FO`"`9KP`0+"\`$"PO`#`H[P`P*.\`(!2O`"`4KP``"2\```DO``` -M"+H```BZ`,#\/`#`_#P`8`<]`&`'/0``%[P``!>\``!:O```6KP`H!L]`*`; -M/0#@,3T`X#$]``!7O```5[P`H!N]`*`;O0"`HKP`@**\`(#*O`"`RKP`@,V\ -M`(#-O```X+H``."Z```6/```%CP``(:[``"&NP``@3L``($[`"`T/0`@-#T` -M8&H]`&!J/0"`\SP`@/,\``#`N@``P+H`@`Z]`(`.O0`@2[T`($N]`(`(O0"` -M"+T``/>[``#WNP"`H[P`@*.\`"`5O0`@%;T```"[````NP`@13T`($4]`$!Z -M/0!`>CT``/H\``#Z/```NCL``+H[``"O.P``KSL`@`.\`(`#O```[[P``.^\ -M`,"HO`#`J+P``-R[``#[``#7 -MNP#@`;T`X`&]``!1O```4;P`0-H\`$#:/`!`I3P`0*4\``!F.P``9CL`@+\\ -M`("_/`#``ST`P`,]```!/````3P``#Z\```^O`"`:;P`@&F\`$">O`!`GKP` -M`->\``#7O```9KP``&:\``"<.@``G#H``&X[``!N.P``E#H``)0Z`(`Q/`"` -M,3P``&L\``!K/```T+H``-"Z```AO```(;P```0\```$/```$;P``!&\`"`; -MO0`@&[T``#&\```QO``@-#T`(#0]`(`P/0"`,#T``""Z```@N@``>#L``'@[ -M``#P/```\#P````[````.P`@%;T`(!6]`(#XO`"`^+P``)B\``"8O`#`&[T` -MP!N]`,#SO`#`\[P``,<[``#'.P"`+CP`@"X\`(!\/`"`?#P`@!,]`(`3/0"@ -M#ST`H`\]````/````#P``.T[``#M.P#`]SP`P/<\`("./`"`CCP`P`&]`,`! -MO0``*[T``"N]`,"HO`#`J+P`P+"\`,"PO`#`);T`P"6]`("]O`"`O;P``%$\ -M``!1/```7#P``%P\```2/```$CP`@"(]`(`B/0`0BST`$(L]`&`T/0!@-#T` -M`+^[``"_NP!`@KP`0(*\``"CO```H[P`8#J]`&`ZO0#@1KT`X$:]```$O0`` -M!+T`P/2\`,#TO`#`L[P`P+.\`$#=/`!`W3P``%L]``!;/0"`_#P`@/P\``#2 -M/```TCP`8#@]`&`X/0!`J#P`0*@\`(#;P`@&N\`(!K -MO```A3L``(4[```4NP``%+L``.R\``#LO`#`F[P`P)N\``#-/```S3P`P-,\ -M`,#3/`!`K[P`0*^\`$"_O`!`O[P``-T[``#=.P``%#P``!0\```D.P``)#L` -M@+H\`("Z/`!`ZSP`0.L\``"0N0``D+D`@`6\`(`%O```/SP``#\\``!N.P`` -M;CL`@,Z\`(#.O```([T``".]`"`HO0`@*+T``!R]```\`(#WO```.[P``#N\`(!;O`"` -M6[P```*\```"O`"`8CP`@&(\`$#R/`!`\CP``-$\``#1/`"`#SP`@`\\``"C -MNP``H[L`0)6\`$"5O```*KP``"J\``#C.P``XSL``/"Y``#PN0#@![T`X`>] -M`$#_O`!`_[P`0*<\`$"G/`#`"#T`P`@]```2NP``$KL`@'6\`(!UO```>CL` -M`'H[``!*.P``2CL``"V\```MO```7#L``%P[``!`/```0#P``%.\``!3O`#` -MG[P`P)^\````.@```#H`@#T\`(`]/```+SP``"\\`(`P/`"`,#P``!BZ```8 -MN@``@+P``("\``"TN@``M+H``,8\``#&/```0#H``$`Z`&`DO0!@)+T`(#F] -M`"`YO0"`<;P`@'&\```N.P``+CL``("X``"`N```9#P``&0\`,#$/`#`Q#P` -M`(L\``"+/```?CP``'X\`,#X/`#`^#P```<]```'/0``"[P```N\`,!*O0#` -M2KT`(#Z]`"`^O0``S+P``,R\`,"0O`#`D+P`@&2\`(!DO```-+L``#2[``!` -MN@``0+H`0)(\`$"2/`!`9#T`0&0]`.!"/0#@0CT`@!6\`(`5O`"`WKP`@-Z\ -M``",NP``C+L```R\```,O```(+T``""]``#>O```WKP``)<[``"7.P``][L` -M`/>[`("AO`"`H;P`@%<\`(!7/`#@*3T`X"D]`$"U/`!`M3P``-`Y``#0.0"` -M`3P`@`$\``"].P``O3L``)&[``"1NP"`3+P`@$R\`,#-O`#`S;P``"*]```B -MO0"@&;T`H!F]``"ONP``K[L`@$4\`(!%/````#D````Y``"A.P``H3L`8"@] -M`&`H/0"`5CT`@%8]``"//```CSP``*V[``"MNP``L#D``+`Y`$"\O`!`O+P` -MP&6]`,!EO0!`)KT`0":]``#@NP``X+L`@&J\`(!JO`#`R+P`P,B\``"U/``` -MM3P`0'L]`$![/0!`$ST`0!,]`(`-/`"`#3P``,P[``#,.P``#KL```Z[`$") -MO`!`B;P``%&\``!1O`!`C+P`0(R\`"`WO0`@-[T`X`Z]`.`.O0``2CP``$H\ -M`,#;/`#`VSP``$@Z``!(.@``C[L``(^[`,";/`#`FSP``(P\``",/```F;L` -M`)F[```^.P``/CL`@*4\`("E/```D3L``)$[`(#?O`"`W[P``(V\``"-O`"` -M8#P`@&`\``"+.P``BSL`@,2\`(#$O`!`K[P`0*^\```RNP``,KL``+$[``"Q -M.P"`4#P`@%`\`("C/`"`HSP``*R[``"LNP#`\[P`P/.\```KO```*[P`@%4\ -M`(!5/```,#H``#`Z```#O````[P`P)D\`,"9/```U3P``-4\``"D.P``I#L` -MP)$\`,"1/`!`"CT`0`H]``#RNP``\KL`P&B]`,!HO0`@4;T`(%&]`(!^O`"` -M?KP`@#R\`(`\O```JKP``*J\``"@NP``H+L``,(\``#"/```,3T``#$]`"!) -M/0`@23T`H"@]`*`H/0``@3P``($\``!>O```7KP`0+J\`$"ZO`#`_[P`P/^\ -M`(`DO0"`)+T`@`R]`(`,O0``=KP``':\``"BNP``HKL`@`Z\`(`.O```0SP` -M`$,\`(!6/0"`5CT`P$P]`,!,/0``*3P``"D\``"?.P``GSL`P.`\`,#@/``` -M.SP``#L\`$#?O`!`W[P`H`>]`*`'O0```+T```"]`,`KO0#`*[T``!*]```2 -MO0``4;P``%&\```0NP``$+L``)T[``"=.P!@$#T`8!`]`.!L/0#@;#T`@#,] -M`(`S/0"`$ST`@!,]`$`0/0!`$#T``*L[``"K.P"@,;T`H#&]`,!1O0#`4;T` -MH`"]`*``O0"@%KT`H!:]`.`GO0#@)[T``/F[``#YNP#@!ST`X`<]`*`)/0"@ -M"3T``"H\```J/```=SP``'<\`$#N/`!`[CP``#(\```R/```A;P``(6\`(!B -MO`"`8KP``!@Z```8.@``A+H``(2Z``#P.@``\#H``&T\``!M/```:#L``&@[ -M``!5O```5;P```"[````NP``B+H``(BZ`,##O`#`P[P`P,N\`,#+O```.CP` -M`#H\`(!;/`"`6SP`@'F\`(!YO```!#L```0[`"`S/0`@,ST`@`L]`(`+/0`` -M*+P``"B\`(!7O`"`5[P``"@\```H/```D3L``)$[```%O```!;P```F\```) -MO```3[P``$^\`,#$O`#`Q+P`@*R\`("LO```NKL``+J[``!`.@``0#H``(`Z -M``"`.@"`%SP`@!<\`,#-/`#`S3P`X`P]`.`,/0#@(CT`X"(]`$`M/0!`+3T` -M0)<\`$"7/`"`"+T`@`B]`&!,O0!@3+T`0/B\`$#XO``@";T`(`F]`"!IO0`@ -M:;T`8"N]`&`KO0"`5#P`@%0\`&`H/0!@*#T``"H]```J/0#@<#T`X'`]`$"$ -M/0!`A#T``.8\``#F/`"`9+P`@&2\`("CO`"`H[P``,2\``#$O`!`$[T`0!.] -M`(#ZO`"`^KP``.R\``#LO```]+P``/2\``#O```'KP`(`2]`"`$O0``N[P``+N\```SO```,[P` -MP-^\`,#?O```L[P``+.\``"I/```J3P`0/H\`$#Z/```]SL``/<[`(!R/`"` -M/`#`GCP``(P[``", -M.P"@`+T`H`"]`,"ZO`#`NKP``"@Z```H.@``*[P``"N\``"TO```M+P``(2Z -M``"$N@!`ICP`0*8\``#!/```P3P`P/T\`,#]/`!`]CP`0/8\``"F.P``ICL` -M`)J\``":O```4;P``%&\``!?O```7[P`0!^]`$`?O0``';T``!V]``"2NP`` -MDKL``(L\``"+/`#`OCP`P+X\`.`R/0#@,CT`<($]`'"!/0#@&3T`X!D]`(!N -MO`"`;KP``/Z\``#^O```8+P``&"\`("8O`"`F+P`H%*]`*!2O0!@2[T`8$N] -M```0O```$+P`P)\\`,"?/`"`!3T`@`4]`$`G/0!`)ST`P/4\`,#U/```3#P` -M`$P\`(!2/`"`4CP`P),\`,"3/```WKL``-Z[`$#>O`!`WKP``(.\``"#O`"` -M1+P`@$2\`(#4O`"`U+P``(B\``"(O`"`>#P`@'@\``!(/```2#P``&F\``!I -MO```"+H```BZ`"`9/0`@&3T``#4]```U/0"`OCP`@+X\``#;.P``VSL``#*[ -M```RNP``>;P``'F\`,#*O`#`RKP`@/"\`(#PO`"@`+T`H`"]`,##O`#`P[P` -M`)&[``"1NP#`I#P`P*0\```8/0``&#T`0!@]`$`8/0`@"3T`(`D]``!]/``` -M?3P``#2\```TO`!`@;P`0(&\``#\N@``_+H``#&\```QO`#`&+T`P!B]`,"T -MO`#`M+P``+D\``"Y/`!@!CT`8`8]`(`C/`"`(SP``-0Z``#4.@"`(#P`@"`\ -M``!DNP``9+L``*6\``"EO`"`:;P`@&F\```OO```+[P`@!>\`(`7O`"`3SP` -M@$\\`(#X/`"`^#P`@*0\`("D/`"`;3P`@&T\`$"Q/`!`L3P``,D[``#).P!` -MOKP`0+Z\`,":O`#`FKP``&`Y``!@.0``Z[L``.N[`,#UO`#`];P`P*>\`,"G -MO```BCP``(H\`("P/`"`L#P```"X````N```Z#H``.@Z`,"T/`#`M#P`0-P\ -M`$#KP``'J\`$#\O`!`_+P` -M`,Z\``#.O`!`U[P`0->\`*`FO0"@)KT``.:\``#FO```PSL``,,[`,#6/`#` -MUCP`P$H]`,!*/0"PHST`L*,]`,!]/0#`?3T``,H[``#*.P"`*;P`@"F\``#' -M.P``QSL`0*N\`$"KO`!@E;T`8)6]`%"8O0!0F+T`(`^]`"`/O0``Y[L``.>[ -M``"@.P``H#L`@,\\`(#//`!`,ST`0#,]`"`>/0`@'CT`@`L]`(`+/0"@%#T` -MH!0]`,#6/`#`UCP``*`Z``"@.@!`E;P`0)6\`(#$O`"`Q+P`0-^\`$#?O`!` -MF;P`0)F\``!XNP``>+L```F\```)O`#`W+P`P-R\`$"DO`!`I+P`@+`\`("P -M/`!`*ST`0"L]``!U/```=3P``$0[``!$.P!`S3P`0,T\``#%/```Q3P``$2[ -M``!$NP``^KL``/J[```BNP``(KL`@+B\`("XO`"``+T`@`"]````NP```+L` -M`*0\``"D/`#`A3P`P(4\`(`0/`"`$#P``.0[``#D.P``(#D``"`Y``"#NP`` -M@[L``'X[``!^.P``C[L``(^[`(#/O`"`S[P`@%:\`(!6O`#`X3P`P.$\`.`7 -M/0#@%ST``%\\``!?/```!3P```4\`("!/`"`@3P``.2Z``#DN@"``[T`@`.] -M`.`:O0#@&KT`0)"\`$"0O```);P``"6\``!^O```?KP``.<[``#G.P#`,#T` -MP#`]`"!!/0`@03T`(``]`"``/0"`P#P`@,`\`(!./`"`3CP``*B[``"HNP`` -M[KL``.Z[`,"`O`#`@+P`0#:]`$`VO0!`1+T`0$2]``!AO```8;P``,4[``#% -M.P``&[P``!N\``"FNP``IKL`P`H]`,`*/0!`.CT`0#H]`,#3P`(!$]`"`1/0``5#P` -M`%0\`$#.P`` -M,CL``#([`(`UO`"`-;P``$.\``!#O`!`ACP`0(8\`,#0/`#`T#P``#@[```X -M.P"`.KP`@#J\``!8.@``6#H`@"$\`(`A/```I+H``*2Z``"INP``J;L``(8[ -M``"&.P``L#D``+`Y`(!CO`"`8[P``(`Z``"`.@#`$ST`P!,]`*`'/0"@!ST` -M`'@Z``!X.@"`"[P`@`N\``").P``B3L``$@[``!(.P``@;P``(&\`$#,O`!` -MS+P`0*2\`$"DO`"`@KP`@(*\``"PN0``L+D`P,H\`,#*/`"`XSP`@.,\``"U -M/```M3P`(``]`"``/0!`TCP`0-(\`(`$O`"`!+P`0**\`$"BO```P+L``,"[ -M`(")O`"`B;P`H!>]`*`7O0``H;P``*&\`$"Z/`!`NCP`X!0]`.`4/0!`F3P` -M0)D\`(!0/`"`4#P`@*8\`("F/`"`BCP`@(H\``!@N@``8+H``'V\``!]O`#` -MNKP`P+J\``#!O```P;P`@%^\`(!?O```C+L``(R[``!@N@``8+H``.@[``#H -M.P!`K3P`0*T\`(#D/`"`Y#P`P+D\`,"Y/```ISP``*<\`,#>/`#`WCP``'\\ -M``!_/`"`>KP`@'J\`,"^O`#`OKP``!"\```0O```DKP``)*\`.`RO0#@,KT` -MP.J\`,#JO```.SP``#L\`$"]/`!`O3P``+\\``"_/```$#T``!`]`"`E/0`@ -M)3T``-@\``#8/`"`9CP`@&8\``#L.@``[#H`@,^\`(#/O`#@+;T`X"V]`,#] -MO`#`_;P`@(:\`("&O`"`;KP`@&Z\```4.P``%#L`P`T]`,`-/0!`'#T`0!P] -M`(";P`0"V]`$`MO0!@+;T`8"V]``"R -MO```LKP``.R[``#LNP``'CL``!X[`(!F/`"`9CP`0(D\`$")/```C#P``(P\ -M`.`)/0#@"3T`0/L\`$#[/```P+L``,"[``"&O```AKP``/P[``#\.P``*SP` -M`"L\``!6O```5KP`0(*\`$""O```0#L``$`[``#(.P``R#L``+.[``"SNP`` -M1+L``$2[``!+/```2SP`@#(\`(`R/```P+@``,"X``#B.P``XCL`P(L\`,"+ -M/```.#P``#@\``!@.P``8#L``,"Y``#`N0``7KL``%Z[```P.@``,#H`@#(\ -M`(`R/`"`8CP`@&(\```@.0``(#D`@#.\`(`SO```&KL``!J[``!&.P``1CL` -M`':\``!VO`!`NKP`0+J\````.0```#D``+([``"R.P``Q[L``,>[``"(/``` -MB#P`H%D]`*!9/0#`2ST`P$L]`$"C/`!`HSP`@"T\`(`M/`"`-3P`@#4\``#] -MNP``_;L`8`F]`&`)O0``/KT``#Z]`,!!O0#`0;T`(!"]`"`0O0``/+L``#R[ -M`$# -M/0"`^3P`@/D\`$#8/`!`V#P`0*T\`$"M/```N#L``+@[``!4NP``5+L``+6[ -M``"UNP!`L+P`0+"\`"`2O0`@$KT`P-*\`,#2O`"`!;P`@`6\``"L.@``K#H` -M`(L\``"+/`"`*3T`@"D]`"`N/0`@+CT`P(,\`,"#/`"`"3P`@`D\`,":/`#` -MFCP``("Z``"`N@#@#;T`X`V]`.`/O0#@#[T``#J\```ZO```"CL```H[``!L -M.P``;#L``!P\```+H``&T\``!M/``` -M0SP``$,\`(`MO`"`+;P``$^\``!/O```]#L``/0[`(`#/`"``SP``"J\```J -MO`"`";P`@`F\``#,.@``S#H``'B[``!XNP``F#H``)@Z`("//`"`CSP`0)0\ -M`$"4/`"`)SP`@"<\``!L/```;#P`@&@\`(!H/```@[L``(.[``!JO```:KP` -M@":\`(`FO`"`3+P`@$R\`("WO`"`M[P``!*\```2O`"`[SP`@.\\`.`D/0#@ -M)#T`P(8\`,"&/`"`,#P`@#`\`(#D/`"`Y#P`@)4\`("5/`!`I[P`0*>\```= -MO0``';T``.2\``#DO`"`N+P`@+B\`,"8O`#`F+P``)D[``"9.P!`\#P`0/`\ -M`(#\/`"`_#P`@-D\`(#9/`"@"ST`H`L]`,#./`#`SCP``&@Z``!H.@``P;L` -M`,&[`(!%O`"`1;P`X`V]`.`-O0``#KT```Z]``"<.@``G#H`@'<\`(!W/``` -MQ;L``,6[``"SNP``L[L`0-0\`$#4/`"`'ST`@!\]``"A/```H3P``+<[``"W -M.P"`.3P`@#D\``"U.P``M3L`@%"\`(!0O```7;P``%V\```-O```#;P`0+2\ -M`$"TO`!`!KT`0`:]``"*O```BKP``"(\```B/`"`USP`@-<\`(`4/0"`%#T` -MX"@]`.`H/0"`%#T`@!0]``#'/```QSP``(0\``"$/```Z#H``.@Z`"`8O0`@ -M&+T`0'6]`$!UO0!@$+T`8!"]```MO```+;P`@%^\`(!?O```)+L``"2[`"`! -M/0`@`3T`X"H]`.`J/0!`X#P`0.`\`("_/`"`OSP``,@\``#(/`"`#SP`@`\\ -M`(!LO`"`;+P`@)V\`("=O```#[P```^\```HNP``*+L```P[```,.P``M#L` -M`+0[```JNP``*KL``(&[``"!NP``"3P```D\`(`&/`"`!CP`@"*\`(`BO`"` -M:KP`@&J\``"Z.P``NCL`0)<\`$"7/`"`/3P`@#T\``"2.P``DCL``%`\``!0 -M/`#`@3P`P($\``",N@``C+H``+F[``"YNP"`"#P`@`@\``#$.P``Q#L`@"^\ -M`(`OO```%[P``!>\``!@.P``8#L``#@Z```X.@```[P```.\`(`5O`"`%;P` -M`/.[``#SNP``H#H``*`Z`$"I/`!`J3P`(`$]`"`!/0``OCP``+X\``"6/``` -MECP``/`\``#P/`#`ISP`P*<\`,"?O`#`G[P`8"R]`&`LO0"@$[T`H!.]`(`1 -MO0"`$;T`8!Z]`&`>O0``1[P``$>\`,#P/`#`\#P`P#X]`,`^/0#@-CT`X#8] -M`*`U/0"@-3T`H#H]`*`Z/0"``#T`@``]```XN@``.+H`P.*\`,#BO`#@);T` -MX"6]`$`IO0!`*;T``.^\``#OO`!`@KP`0(*\`$"#O`!`@[P`@!J\`(`:O`#` -MM#P`P+0\`$`W/0!`-ST`0`@]`$`(/0"`K3P`@*T\`(#C/`"`XSP`0,@\`$#( -M/```)#L``"0[``#_NP``_[L````Y````.0"`8[P`@&.\`&`OO0!@+[T`0!>] -M`$`7O0``!;P```6\``"\.@``O#H``#0[```T.P!`G3P`0)T\`"`&/0`@!CT` -MX`\]`.`//0`@&ST`(!L]`,#8/`#`V#P``!B[```8NP``R+P``,B\`$#"O`!` -MPKP`@(N\`("+O`"`NKP`@+J\`$"DO`!`I+P``.`[``#@.P!`P3P`0,$\``!6 -M/```5CP``#X\```^/`"`XSP`@.,\``"?/```GSP``'6\``!UO`!`Q+P`0,2\ -M``"@.0``H#D`P(D\`,")/`"`'#P`@!P\`(`%/`"`!3P``*L\``"K/`!`C3P` -M0(T\``!..P``3CL``"J[```JNP!`AKP`0(:\`(`&O0"`!KT``,&\``#!O``` -M!+L```2[``#8N@``V+H``""Z```@N@``MSP``+<\`"`./0`@#CT``+0\``"T -M/`"`@#P`@(`\``#NP"`JCP`@*H\`,"//`#`CSP`@-,\`(#3/`"`63T`@%D] -M`*!,/0"@3#T``',\``!S/`"`&KP`@!J\`(!KO`"`:[P`0+&\`$"QO`!`_[P` -M0/^\``#2O```TKP`@$&\`(!!O````+L```"[`(!0/`"`4#P`@.H\`(#J/`!` -MVSP`0-L\`(!X/`"`>#P`P(<\`,"'/```C3P``(T\``"4.@``E#H``)>[``"7 -MNP``N#L``+@[``"8.P``F#L``'B\``!XO`#`P;P`P,&\``!BNP``8KL`@!\\ -M`(`?/```F[L``)N[``#0NP``T+L``*@\``"H/`!`_#P`0/P\`(!B/`"`8CP` -M@`P\`(`,/`"`/#P`@#P\``!JNP``:KL`@)V\`("=O```>;P``'F\``#'NP`` -MQ[L``$:[``!&NP``P#L``,`[`("K/`"`JSP`0+$\`$"Q/`!`BSP`0(L\`(#5 -M/`"`U3P`P*@\`,"H/```9KP``&:\`,`,O0#`#+T``(*\``""O```1KL``$:[ -M``!BO```8KP`@`V\`(`-O`"`I#P`@*0\`$`6/0!`%CT`0-`\`$#0/`"`1SP` -M@$<\`(!0/`"`4#P``(P[``",.P"`B+P`@(B\`("]O`"`O;P`@#>\`(`WO``` -M)+L``"2[```D.P``)#L``%<\``!7/`"`ASP`@(<\```_/```/SP`0(@\`$"( -M/`#`J3P`P*D\``";.P``FSL`@$>\`(!'O```U+L``-2[``"&.P``ACL``,.[ -M``##NP"`5+P`@%2\``!@.0``8#D``+P[``"\.P``Q[L``,>[``!$NP``1+L` -MP*,\`,"C/`!`USP`0-<\``!\/```?#P`@&,\`(!C/`"`?3P`@'T\`(`//`"` -M#SP``)F[``"9NP!`K+P`0*R\`*`$O0"@!+T`@."\`(#@O```ZKL``.J[```_ -M/```/SP`@)8\`("6/`#`S3P`P,T\```J/0``*CT`H#P]`*`\/0"`JSP`@*L\ -M``"INP``J;L``"B\```HO`#`J[P`P*N\`&`MO0!@+;T`X#:]`.`VO0!`H[P` -M0*.\``!\.P``?#L`@"T\`(`M/```K3P``*T\`&`H/0!@*#T`0#P]`$`\/0`@ -M!3T`(`4]`,";/`#`FSP``*T[``"M.P"`/KP`@#Z\``!4O```5+P`@"Z\`(`N -MO`"`Q[P`@,>\`(#KO`"`Z[P`@":\`(`FO```Z#H``.@Z``!FNP``9KL``)`Y -M``"0.0``L#P``+`\`,#^/`#`_CP`0*8\`$"F/`#`D#P`P)`\``#3/```TSP` -M@'8\`(!V/```2KP``$J\``"HO```J+P``"J\```JO```([P``".\``!LO``` -M;+P``/^[``#_NP``XCL``.([``"2/```DCP`0*T\`$"M/`#`HCP`P*(\``#G -M.P``YSL``/"[``#PNP``M[L``+>[``",.@``C#H`@`"\`(``O```.+P``#B\ -M``!`/```0#P`P,T\`,#-/```"SP```L\```6.P``%CL`@'P\`(!\/```\CL` -M`/([`("SO`"`L[P`0.>\`$#GO```Z;L``.F[``!V.P``=CL``-0Z``#4.@`` -M(SP``",\``"\/```O#P`@-0\`(#4/`!`T#P`0-`\`(#5/`"`U3P``"D\```I -M/```@KP``(*\``"TO```M+P`0)2\`$"4O`!`R+P`0,B\`(#=O`"`W;P`@#&\ -M`(`QO```BCL``(H[``!T.P``=#L``#@\```X/`!@&#T`8!@]``!#/0``0ST` -M`,L\``#+/```'SP``!\\`(#./`"`SCP`@+T\`("]/```9KP``&:\`"`:O0`@ -M&KT`0!2]`$`4O0`@&[T`(!N]`"`:O0`@&KT`P).\`,"3O```\SL``/,[`,"K -M/`#`JSP`P`P]`,`,/0``4ST``%,]``!-/0``33T``/H\``#Z/`"`?#P`@'P\ -M``"<.@``G#H`0/B\`$#XO``@0;T`($&]``#CO```X[P`@%N\`(!;O`!`L+P` -M0+"\`(!UO`"`=;P``)$\``"1/``@(ST`(",]`(#S/`"`\SP`@'0\`(!T/`#` -MB3P`P(D\`("0/`"`D#P``&0[``!D.P``LKL``+*[``""NP``@KL`@#2\`(`T -MO`#`E+P`P)2\`(`FO`"`)KP``'J[``!ZNP``K+L``*R[````N0```+D`@!`\ -M`(`0/`"`"SP`@`L\``"].P``O3L``(H\``"*/```K#P``*P\```*.P``"CL` -M@!N\`(`;O```W#L``-P[`("#/`"`@SP``"J[```JNP"`-KP`@#:\``"2.P`` -MDCL`@`\\`(`//```C+H``(RZ``!LNP``;+L``+RZ``"\N@"`!;P`@`6\`(!U -MO`"`=;P``#B\```XO```C[L``(^[```2.P``$CL`@'<\`(!W/`!`VCP`0-H\ -M``#1/```T3P``*L\``"K/`#`S3P`P,T\`("6/`"`ECP``'R\``!\O```'+T` -M`!R]`(#0O`"`T+P``#.\```SO```HKP``**\``"QO```L;P``*`[``"@.P"` -MWSP`@-\\`,#7/`#`USP`@,X\`(#./`#`^#P`P/@\``"C/```HSP``'Z[``!^ -MNP``&;P``!F\``#DNP``Y+L`P*&\`,"AO`#`X;P`P.&\`$"3O`!`D[P`@#:\ -M`(`VO```H+L``*"[``!U/```=3P`@/,\`(#S/```K#P``*P\`(!N/`"`;CP` -MP.4\`,#E/`!``CT`0`(]``"M.P``K3L`@+>\`("WO`!`M+P`0+2\``"?O``` -MG[P`8`*]`&`"O0!`[+P`0.R\``"8NP``F+L``"\\```O/`"`0CP`@$(\`("B -M/`"`HCP`H!$]`*`1/0#`#ST`P`\]``"-/```C3P``+`Y``"P.0``#KP```Z\ -M`$"*O`!`BKP`@)>\`("7O`"`.KP`@#J\`(!4O`"`5+P`@&F\`(!IO`"`&CP` -M@!H\`(#Z/`"`^CP`P)L\`,";/```$#L``!`[``#,.P``S#L`@`T\`(`-/``` -M_+L``/R[`$"%O`!`A;P``+R[``"\NP``"+H```BZ``#ONP``[[L``(N[``"+ -MNP``+SP``"\\``!I/```:3P```L\```+/```+#P``"P\```Y/```.3P`@`<\ -M`(`'/```\CL``/([``!$.P``1#L``#^\```_O```J+P``*B\```OO```+[P` -M``BZ```(N@``O[L``+^[``#ONP``[[L`@"@\`(`H/`#`R3P`P,D\``!J/``` -M:CP``)T[``"=.P``9#P``&0\```A/```(3P``'*\``!RO```O+P``+R\``#= -MNP``W;L``-@Z``#8.@``6KL``%J[```L.P``+#L``(`\``"`/`!`GSP`0)\\ -M``"8/```F#P``%(\``!2/```;KL``&Z[`("FO`"`IKP``&2\``!DO```(KL` -M`"*[`(!2O`"`4KP`@)Z\`(">O```5CL``%8[``"^/```OCP`0)`\`$"0/``` -M63P``%D\``#!/```P3P`0*$\`$"A/```6KL``%J[`$"`O`!`@+P`@!B\`(`8 -MO`"`%;P`@!6\`,"TO`#`M+P`0+"\`$"PO```2+L``$B[``#S.P``\SL``$$\ -M``!!/`!`LCP`0+(\`,#)/`#`R3P`0(\\`$"//```E3P``)4\`,"Z/`#`NCP` -M`*([``"B.P``J;P``*F\`$#5O`!`U;P`@+N\`("[O`!`![T`0`>]`"`0O0`@ -M$+T``+:[``"VNP!`JCP`0*H\``",/```C#P`0+@\`$"X/`#`1#T`P$0]`&!* -M/0!@2CT`@)(\`("2/```IKL``*:[`(`DO`"`)+P``*.\``"CO`!``[T`0`.] -M`$#XO`!`^+P`@*^\`("OO```8;P``&&\``!P.@``<#H``+0\``"T/```VCP` -M`-H\``!^/```?CP``)(\``"2/`!`OCP`0+X\``#F.P``YCL``#N\```[O``` -MD;L``)&[``"1.P``D3L`@$:\`(!&O`"`RKP`@,J\``#CNP``X[L``"$\```A -M/```*+H``"BZ`(`)O`"`";P``"@\```H/`!`J#P`0*@\```9/```&3P``.,[ -M``#C.P"`53P`@%4\``!R.P``.P``Q#H``,0Z`(!MO`"` -M;;P``/B\``#XO`"`R[P`@,N\`(`:O`"`&KP``!>\```7O`"`)+P`@"2\``!V -M/```=CP`H"(]`*`B/0`@##T`(`P]`,"R/`#`LCP`0+L\`$"[/`"`)#P`@"0\ -M`$"YO`!`N;P`8!R]`&`\``"WO```*+P` -M`"B\`(`V/`"`-CP`@-L\`(#;/`!`"3T`0`D]`"`0/0`@$#T`0+8\`$"V/``` -M)CL``"8[```TNP``-+L``!BZ```8N@``=KP``':\`$#[O`!`^[P`0*B\`$"H -MO```C;L``(V[``#9NP``V;L``!&\```1O```I#L``*0[`(!\/`"`?#P``.$[ -M``#A.P``W3L``-T[`,#//`#`SSP`0-0\`$#4/```\#D``/`Y`(!$O`"`1+P` -M`.N[``#KNP"`0;P`@$&\``"IO```J;P`@&J\`(!JO```N+L``+B[``"@.0`` -MH#D`@(P\`(",/`"@!3T`H`4]``#)/```R3P``'([``!R.P``C+H``(RZ```( -MN@``"+H`@)R\`("#H``'@Z`(`@O`"`(+P``,`X``#`.`"`"3P`@`D\``"%NP``A;L`@!J\ -M`(`:O```&CP``!H\`,"1/`#`D3P``#Z[```^NP``7;P``%V\``!0.P``4#L` -M`&X\``!N/```J#L``*@[``!`.@``0#H``&8[``!F.P``I[L``*>[`(!JO`"` -M:KP``"^\```OO`"`%;P`@!6\`(!#O`"`0[P``$Z[``!.NP"`B#P`@(@\`$#* -M/`!`RCP`P)X\`,">/`"`J#P`@*@\`("5/`"`E3P``!N\```;O`"`$;T`@!&] -M`,#2O`#`TKP``""\```@O`!`K+P`0*R\`,"\O`#`O+P``!,\```3/`"@%3T` -MH!4]`,`0/0#`$#T``-D\``#9/`"`RCP`@,H\`(!+/`"`2SP`@"&\`(`AO`!` -MH[P`0*.\`,"YO`#`N;P`(`>]`"`'O0#@";T`X`F]``!BO```8KP``#@[```X -M.P``V3L``-D[``"3/```DSP``.T\``#M/`!`L3P`0+$\``!//```3SP``)P\ -M``"\`(#'O`"`>KP`@'J\```ZO```.KP`0,J\`$#*O`!`O;P`0+V\```^NP`` -M/KL``#@Z```X.@``7KL``%Z[``!7/```5SP`X`8]`.`&/0``$3T``!$]`,`` -M/0#``#T`@-@\`(#8/```RCL``,H[``"IO```J;P`0.>\`$#GO`!`V;P`0-F\ -M`*`DO0"@)+T`8$>]`&!'O0!`H[P`0*.\``":/```FCP`@-H\`(#:/`"`V#P` -M@-@\`"`I/0`@*3T`8"T]`&`M/0"`>SP`@'L\``#ZNP``^KL``#F\```YO``` -MHKP``**\`(`>O0"`'KT`(!B]`"`8O0``=;P``'6\```@NP``(+L``)`Y``"0 -M.0``3#P``$P\`$"L/`!`K#P`@&`\`(!@/`#`@#P`P(`\`(#7/`"`USP``'8\ -M``!V/```U;L``-6[``#@NP``X+L````X````.```=;P``'6\`&``O0!@`+T` -M@*B\`("HO```WKL``-Z[``!'O```1[P``"V\```MO```7#P``%P\``#4/``` -MU#P`@%8\`(!6/```]CL``/8[`(""/`"`@CP`@#`\`(`P/```KKL``*Z[`(!) -MO`"`2;P`@#V\`(`]O```0KP``$*\``":NP``FKL`@!$\`(`1/`"`!CP`@`8\ -M``!@NP``8+L``*BZ``"HN@``Z#L``.@[``#LNP``[+L`@.B\`(#HO`"`D[P` -M@).\```R.P``,CL``)`Z``"0.@``\#H``/`Z`$##/`!`PSP`@`P]`(`,/0!` -M@#P`0(`\```P.@``,#H``!8[```6.P``L[L``+.[`,#/O`#`S[P`P/N\`,#[ -MO`#`J+P`P*B\``!MO```;;P``-2[``#4NP"`7CP`@%X\`("J/`"`JCP`@#@\ -M`(`X/```9SP``&<\`$##/`!`PSP`@%8\`(!6/```T;L``-&[`(`"O`"``KP` -M`(.[``"#NP"`C;P`@(V\`$#EO`!`Y;P`@'J\`(!ZO```,KL``#*[``!&O``` -M1KP``%R\``!\`,"WO```#;T```V] -M`,#PO`#`\+P``*>[``"GNP"`%CP`@!8\```9/```&3P`@*\\`("O/`!@$CT` -M8!(]`$"K/`!`JSP``/"Z``#PN@``I+H``*2Z``!.NP``3KL`P-6\`,#5O`"` -M&+T`@!B]`$")O`!`B;P``(@[``"(.P``O#L``+P[```+/```"SP``*`\``"@ -M/`"`ICP`@*8\`(`>/`"`'CP``,\[``#/.P``N3L``+D[```+O```"[P`0):\ -M`$"6O`"`-;P`@#6\`(`4O`"`%+P``(*\``""O`"`(;P`@"&\``"$.P``A#L` -M`/0Z``#T.@``3+L``$R[`(!8/`"`6#P`@.$\`(#A/```5#P``%0\``"%NP`` -MA;L``(8[``"&.P"`+#P`@"P\```IO```*;P``,>\``#'O`"`1[P`@$>\``"; -MNP``F[L`@`N\`(`+O```P+@``,"X`(!P/`"`<#P``'(\``!R/```"#P```@\ -M``#_.P``_SL``+@Z``"X.@"`1;P`@$6\``!ZO```>KP``/J[``#ZNP"`!KP` -M@`:\`(!&O`"`1KP``(X[``"..P``OSP``+\\`(!T/`"`=#P``/0Z``#T.@`` -M&SP``!L\``!:/```6CP``,&[``#!NP"`LKP`@+*\`(!]O`"`?;P``/R[``#\ -MNP``$KP``!*\``!2NP``4KL`@$4\`(!%/`!`@3P`0($\```1/```$3P`@#4\ -M`(`U/```,CP``#(\``"1NP``D;L``%&\``!1O```(KP``"*\`(!TO`"`=+P` -M`**\``"BO```I+L``*2[```4/```%#P``"`Z```@.@``9KL``&:[`("3/`"` -MDSP`X`T]`.`-/0"`JSP`@*L\``"(.P``B#L``/X[``#^.P``D#L``)`[`$"1 -MO`!`D;P`P.R\`,#LO`#`U;P`P-6\`,#NO`#`[KP`0.^\`$#OO`"`&;P`@!F\ -M``!;/```6SP`P+8\`,"V/`"`^3P`@/D\`.`D/0#@)#T`0`<]`$`'/0"`53P` -M@%4\``"<.P``G#L``)@Z``"8.@!`T;P`0-&\`.!.O0#@3KT`(!"]`"`0O0`` -M$[P``!.\`(`KO`"`*[P``#V\```]O```7SP``%\\`(#[/`"`^SP`P*D\`,"I -M/`"`B3P`@(D\`$#2/`!`TCP`@(0\`("$/```$KP``!*\``"`O```@+P``/:[ -M``#VNP``[``#WNP"`4CP`@%(\ -M`(!6/`"`5CP`P*P\`,"L/`"@$ST`H!,]`"`)/0`@"3T`@*X\`("N/```1CP` -M`$8\``!DNP``9+L``,F\``#)O`!`YKP`0.:\``!KO```:[P``$V\``!-O`"` -MK;P`@*V\`(`HO`"`*+P`@!D\`(`9/`"``3P`@`$\``#0.0``T#D``#\\```_ -M/`!`F#P`0)@\```".P```CL``,^[``#/NP```#P````\``!1/```43P``(&[ -M``"!NP``2;P``$F\``#8N@``V+H``!0[```4.P``>KL``'J[```(.@``"#H` -M`'P[``!\.P``Z;L``.F[`(`PO`"`,+P``+@Z``"X.@``"+H```BZ`(!LO`"` -M;+P`@`6\`(`%O```^SL``/L[``"..P``CCL``+"Y``"PN0"`?3P`@'T\`,#: -M/`#`VCP``.H[``#J.P"`/[P`@#^\``!FNP``9KL``%BZ``!8N@``KKP``*Z\ -M`$#SO`!`\[P`@%6\`(!5O```X+H``."Z``#0.0``T#D`@%L\`(!;/`#`S3P` -MP,T\``"9/```F3P`@#`\`(`P/```1CP``$8\``").P``B3L`0)N\`$";O``` -MW+P``-R\``"#O```@[P``).\``"3O`!`U[P`0->\``#QNP``\;L`P)8\`,"6 -M/`"`>#P`@'@\``#:.P``VCL``+0\``"T/`#`Z#P`P.@\``#4.P``U#L`@!B\ -M`(`8O```+KL``"Z[``"3NP``D[L`0+V\`$"]O`#`V+P`P-B\``!5O```5;P` -M`#"\```PO`"`/;P`@#V\``";.P``FSL`0)X\`$">/```@3P``($\`,"`/`#` -M@#P``,(\``#"/`"`/#P`@#P\``!0O```4+P``):\``"6O`"`7KP`@%Z\`(#$ -MO`"`Q+P`@`*]`(`"O0``.+P``#B\``!=/```73P``#D\```Y/```_3L``/T[ -M`,#"/`#`PCP``/0\``#T/`"`!CP`@`8\``#_NP``_[L```B\```(O```E+P` -M`)2\`,#NO`#`[KP`0)Z\`$">O```P+L``,"[``#,NP``S+L``#Z[```^NP"` -M:3P`@&D\``"NP"`#CP`@`X\``#`N0``P+D```R[```,NP``/#P``#P\```U/```-3P` -M`&"[``!@NP``8+L``&"[``"=.P``G3L``+:[``"VNP``FKP``)J\``!+O``` -M2[P``,R[``#,NP``0KP``$*\```"NP```KL`P(\\`,"//```83P``&$\``#@ -MN@``X+H``(L[``"+.P``:SP``&L\``"RNP``LKL`@.*\`(#BO```>KP``'J\ -M```T.P``-#L``)&[``"1NP``IKL``*:[`("1/`"`D3P`@,4\`(#%/```4CL` -M`%([``"FNP``IKL``+"Z``"PN@``5[P``%>\``#OO```[[P``,6\``#%O``` -M4+P``%"\`(!*O`"`2KP``'Z[``!^NP#`JCP`P*H\`(#O/`"`[SP`@(P\`(", -M/`#`B3P`P(D\`$#0/`!`T#P`@`\\`(`//`"`EKP`@):\`("9O`"`F;P``$^\ -M``!/O`!`Z;P`0.F\```@O0``(+T``*2\``"DO```R+H``,BZ``"MNP``K;L` -M``"[````NP#`LSP`P+,\```'/0``!ST`@,0\`(#$/`#`JCP`P*H\`("M/`"` -MK3P```H[```*.P!`F;P`0)F\``"TO```M+P`@.*\`(#BO`#`);T`P"6]`$`` -MO0!``+T``,R[``#,NP``@CL``(([``"P.P``L#L``,X\``#./`!`,3T`0#$] -M``#O/```[SP``(@[``"(.P``:+H``&BZ``"$NP``A+L`@,^\`(#/O`!@%[T` -M8!>]`("YO`"`N;P`@#&\`(`QO`"`(KP`@"*\``#@.0``X#D`@&T\`(!M/``` -M8SP``&,\``#5.P``U3L`@!H\`(`:/```&SP``!L\``"XN@``N+H``'R[``!\ -MNP``T3L``-$[```J.P``*CL``%&\``!1O`"`([P`@".\```..P``#CL```B\ -M```(O```HKP``**\``#XNP``^+L``#H[```Z.P``^+L``/B[```GO```)[P` -M`.T[``#M.P"`:SP`@&L\```(.P``"#L``/"Y``#PN0``&SP``!L\```$/``` -M!#P``&`Y``!@.0``GCL``)X[``#Q.P``\3L``+&[``"QNP"`*[P`@"N\``": -MNP``FKL`@%&\`(!1O`!`[KP`0.Z\`(#(O`"`R+P``/2[``#TNP``:KL``&J[ -M``"(N@``B+H`P+@\`,"X/`!@*#T`8"@]``#Q/```\3P``$4\``!%/`"`5SP` -M@%<\``":.P``FCL`@,^\`(#/O`!`*[T`0"N]```0O0``$+T`X`&]`.`!O0#` -MW+P`P-R\```NNP``+KL`P+<\`,"W/`!`S3P`0,T\`$#-/`!`S3P`(!$]`"`1 -M/0``^3P``/D\``#&.P``QCL``.&[``#ANP``V;L``-F[`$"NO`!`KKP``!>] -M```7O0#`R+P`P,B\```5O```%;P``)R\``"[``#GNP``[KL``.Z[`("GO`"`I[P`0`R]`$`,O0!`P+P`0,"\`(`? -MO`"`'[P`@!*\`(`2O```X#H``.`Z``"Y/```N3P``+0\``"T/```R#L``,@[ -M```&/```!CP`@&8\`(!F/```P[L``,.[`(#=O`"`W;P`@&Z\`(!NO```@3L` -M`($[```LNP``++L``,>[``#'NP``;3P``&T\`,"J/`#`JCP``#8[```V.P`` -MK+L``*R[````.````#@`@`>\`(`'O`#`HKP`P**\``!"O```0KP``,0Z``#$ -M.@``8#D``&`Y```6.P``%CL`@'T\`(!]/```BSP``(L\``"L.@``K#H```:[ -M```&NP``GSL``)\[``#MNP``[;L`P+F\`,"YO```,KP``#*\``"Q.P``L3L` -M`$"[``!`NP``-KP``#:\``"4.P``E#L``(<\``"'/```O3L``+T[``#@.@`` -MX#H``&D\``!I/`"`>SP`@'L\``"P.0``L#D``#R[```\NP``0+H``$"Z`(!U -MO`"`=;P`0.6\`$#EO`#`IKP`P*:\``!NO```;KP``'"\``!PO```[#H``.PZ -M`,#9/`#`V3P`P-D\`,#9/```?3P``'T\`("X/`"`N#P`@.8\`(#F/```:#L` -M`&@[``#/O```S[P`@-6\`(#5O```HKP``**\`(#AO`"`X;P``+V\``"]O``` -M^#H``/@Z``!W/```=SP`@$P\`(!,/`!`GSP`0)\\`$#Q/`!`\3P`@((\`("" -M/```N;L``+F[```UO```-;P``$B\``!(O`#`L[P`P+.\`("BO`"`HKP``#2[ -M```TNP``E#H``)0Z```%O```!;P``)<[``"7.P!`S#P`0,P\`$",/`!`C#P` -M`,`Y``#`.0``^3L``/D[``!F/```9CP``'B[``!XNP"`AKP`@(:\``#?NP`` -MW[L``.>[``#GNP``P[P``,.\`$#`O`!`P+P``!J[```:NP``XCL``.([``!\ -M.P``?#L``&\\``!O/`#`TSP`P-,\`("0/`"`D#P``/\[``#_.P"``#P`@``\ -M``"9NP``F;L`P-Z\`,#>O```[;P``.V\``!"O```0KP```>\```'O`"`+;P` -M@"V\``"T.P``M#L`0-$\`$#1/`"`F#P`@)@\``"A.P``H3L`@"(\`(`B/``` -M(SP``",\`(!`O`"`0+P`@+*\`("RO```&;P``!F\``")NP``B;L``#.\```S -MO```<+L``'"[```G/```)SP``/P[``#\.P``$#H``!`Z``",.P``C#L``#H[ -M```Z.P``-KP``#:\``!+O```2[P``(`Z``"`.@``J#H``*@Z``#!NP``P;L` -M`,4[``#%.P``ECP``)8\``#^.P``_CL``)B[``"8NP``=CL``'8[``#4.P`` -MU#L`@">\`(`GO`#`F;P`P)F\``#RNP``\KL``)*[``"2NP"`B+P`@(B\`,"1 -MO`#`D;P``%:[``!6NP``'CL``!X[``"(.P``B#L`@(8\`("&/`"`QSP`@,<\ -M``"./```CCP`@(`\`("`/`#`HSP`P*,\``!V.P``=CL``,F\``#)O`"@`+T` -MH`"]`$#)O`!`R;P``/N\``#[O``@!KT`(`:]`(`-O`"`#;P`0(H\`$"*/`!` -MD3P`0)$\``"P/```L#P`X!\]`.`?/0``%3T``!4]``#@.P``X#L``#>\```W -MO```5+P``%2\`$"[O`!`N[P`P`N]`,`+O0#`U;P`P-6\`(!QO`"`<;P``$6\ -M``!%O```U+H``-2Z`$"M/`!`K3P`P.8\`,#F/`"`ACP`@(8\`(!Z/`"`>CP` -M`+$\``"Q/```[CL``.X[``!>O```7KP``$:\``!&O```#;P```V\`$#3O`!` -MT[P`P`V]`,`-O0``:+P``&B\``!P.@``<#H``,&[``#!NP``EKL``):[`,") -M/`#`B3P`P-,\`,#3/```BSP``(L\`,"$/`#`A#P`@)(\`("2/```@+D``("Y -M`(![O`"`>[P`@":\`(`FO```%;P``!6\``"QO```L;P`@(>\`("'O```%#L` -M`!0[````.P```#L`@!*\`(`2O```8+D``&"Y`(`[/`"`.SP```2[```$NP`` -MA[P``(>\```0NP``$+L``%<\``!7/```FCL``)H[``"X.@``N#H``(P\``", -M/```J#P``*@\``!,.P``3#L``("[``"`NP``R+H``,BZ``!HO```:+P`P/V\ -M`,#]O`!`T+P`0-"\`(!*O`"`2KP`@#R\`(`\O```V;L``-F[`(!W/`"`=SP` -MP,<\`,#'/```A#P``(0\`,"7/`#`ESP`P-X\`,#>/`"`+#P`@"P\``!FO``` -M9KP``%F\``!9O`"`#;P`@`V\`(#=O`"`W;P`8!N]`&`;O0``HKP``**\```* -MNP``"KL``#J[```ZNP``G3L``)T[`,#)/`#`R3P``/,\``#S/`#`M3P`P+4\ -M``"W/```MSP`@-L\`(#;/```%[T``!>]`)">O0"0GKT``,@Z``#(.@"`=KP` -M@':\``!1/```43P``!D]```9/0"@J;T`H*F]`("[O`"`N[P`@$@]`(!(/0#` -M'+T`P!R]``#1.P``T3L`@'<]`(!W/0!`,ST`0#,]```!/0```3T`H`:]`*`& -MO0"`(KP`@"*\`"`\/0`@/#T`X"F]`.`IO0"`@[T`@(.]`&`5O0!@%;T``/@[ -M``#X.P"@*CT`H"H]```9O```&;P``/>[``#WNP``OSP``+\\``#PN@``\+H` -M<)0]`'"4/0``V#P``-@\`+"/O0"PC[T`P!"]`,`0O0#`V;P`P-F\`(!@/`"` -M8#P`0*,\`$"C/`"`";T`@`F]`&`L/0!@+#T``&X[``!N.P``G+P``)R\``#! -M.P``P3L`0`B]`$`(O0#`H#P`P*`\`("AO`"`H;P`H%&]`*!1O0"`,CT`@#(] -M``!V/```=CP`8'L]`&![/0``0SP``$,\`&"OO0!@K[T``."Y``#@N0`@)+T` -M("2]`(!#/0"`0ST`D*H]`)"J/0!0];T`4/6]`$"]`)#'O0!`0KT`0$*]`(![``#WNP``SCL``,X[`"!XO0`@>+T`P*D]`,"I/0"@*#T`H"@] -M`&`;O0!@&[T``/.\``#SO`!PJ;T`<*F]``#J/```ZCP`H$`]`*!`/0#`N;P` -MP+F\``"X.P``N#L`X*2]`."DO0"`YSP`@.<\`(#B/0"`XCT``"$\```A/``` -MC[L``(^[`("[O0"`N[T`@`&\`(`!O`#`V3T`P-D]``#1O```T;P``+"Z``"P -MN@`0B;T`$(F]``#2O0``TKT`L(T]`+"-/0``73P``%T\`(!,/`"`3#P``-2Z -M``#4N@#@3[T`X$^]`+#=/0"PW3T``!(]```2/0```KT```*]`(!K/`"`:SP` -M@.Z]`(#NO0"`8[P`@&.\`,#$/`#`Q#P``.>[``#GNP`0D#T`$)`]`,",O0#` -MC+T`@,2\`(#$O`!`L#T`0+`]`,#7O`#`U[P`(%<]`"!7/0``(;P``"&\`,"; -MO0#`F[T`@'0\`(!T/``@)+T`("2]`(`K/0"`*ST`H`4]`*`%/0#PIKT`\*:] -M`(#N/`"`[CP`@(@\`("(/``@+ST`("\]`"!3/0`@4ST`0.F]`$#IO0``.KT` -M`#J]``!_O```?[P`@(8\`("&/``PSST`,,\]```5O0``%;T`0$>]`$!'O0"` -M=#P`@'0\`$"9/`!`F3P`8+@]`&"X/0"`.+T`@#B]`/"1O0#PD;T``+@[``"X -M.P``K+H``*RZ`-"'/0#0AST`0,B\`$#(O`!@MKT`8+:]`(!1O`"`4;P`(!"] -M`"`0O0!@K#T`8*P]`&!9/0!@63T``(V]``"-O0#`MCP`P+8\```8.P``&#L` -M``4]```%/0#@33T`X$T]`$`MO0!`+;T`@&@\`(!H/`#@;[T`X&^]`'"$O0!P -MA+T`0&$]`$!A/0#`<[T`P'.]`(!4O`"`5+P``)H[``":.P!`H+P`0*"\`!#A -M/0`0X3T`@`L]`(`+/0"`C3P`@(T\`(#[/`"`^SP`F`&^`)@!O@``"#H```@Z -M`&`./0!@#CT``+^\``"_O`"`4SP`@%,\`(#1O0"`T;T`@`N]`(`+O0!`=CT` -M0'8]`("+/`"`BSP``),]``"3/0`P@[T`,(.]`*!HO0"@:+T`0.D]`$#I/0#` -M/CT`P#X]`,`:/0#`&CT``)>]``"7O0!`!KX`0`:^`.!?/0#@7ST`@"D]`(`I -M/0``QSL``,<[`.`"O0#@`KT`8`.^`&`#O@``MCP``+8\`.!W/0#@=ST`H%X] -M`*!>/0"@"ST`H`L]`!"NO0`0KKT``$D\``!)/`#0F3T`T)D]``#T.P``]#L` -M`-.[``#3NP`PZ+T`,.B]`,#:O`#`VKP`('L]`"![/0``R#L``,@[`,!G/0#` -M9ST`0/*\`$#RO```C;T``(V]`("%/`"`A3P``+:[``"VNP"`%#T`@!0]`(#( -MO`"`R+P`H&:]`*!FO0!@2ST`8$L]`,"NO`#`KKP`P`L]`,`+/0"`9#T`@&0] -M`&`7O0!@%[T`0*$\`$"A/`#`K[P`P*^\``"@NP``H+L`H%P]`*!]`.!:/0#@6CT``!L\```;/`"@9#T`H&0]```WO```-[P`8(R]`&",O0`` -M/CP``#X\`$"9O`!`F;P``'@]``!X/0``H+D``*"Y`##TO0`P]+T``$Z[``!. -MNP"@$ST`H!,]``"*/0``BCT``"L]```K/0#0N;T`T+F]``#\N@``_+H``)RZ -M``"\``#WO`!`=[T`0'>]`$#& -M/`!`QCP`8'P]`&!\/0`@9[T`(&>]`,"R/`#`LCP``.2Z``#DN@``B#P``(@\ -M`$"_/0!`OST`@'2\`(!TO```EKP``):\`.`CO0#@([T`('*]`"!RO0"`>#T` -M@'@]```$O```!+P`8%^]`&!?O0`@-+T`(#2]`,`PO0#`,+T`,+(]`#"R/0#` -M=CT`P'8]``!M/```;3P``%0[``!4.P"`KKT`@*Z]`$"Q/`!`L3P`X'\]`.!_ -M/0#`Y[P`P.>\`(`2O0"`$KT`X,B]`.#(O0``3SP``$\\`!"?/0`0GST``$>\ -M``!'O```QSL``,<[`$`1O0!`$;T``)2Z``"4N@``Q#T``,0]``"L/```K#P` -MP)T\`,"=/`"0BKT`D(J]``"_O0``O[T`X`0]`.`$/0!`(KT`0"*]`("7O`"` -ME[P``+T\``"]/`!`-+T`0#2]`(`\/0"`/#T`@/D\`(#Y/`!`^#P`0/@\`""( -M/0`@B#T`P'.]`,!SO0``A+H``(2Z`(!$/`"`1#P`("V]`"`MO0!`OCP`0+X\ -M`+"`O0"P@+T``'*]``!RO0``E;L``)6[`(#:O`"`VKP`P/<]`,#W/0!@7ST` -M8%\]`&!+O0!@2[T``.H[``#J.P"`Y;P`@.6\`"`]/0`@/3T`P!T]`,`=/0!0 -MO+T`4+R]`$`UO0!`-;T`0$F]`$!)O0"`Z3P`@.D\`'"M/0!PK3T`(%>]`"!7 -MO0!`+;T`0"V]`,"4O`#`E+P`8'0]`&!T/0`P$SX`,!,^`,#FO`#`YKP`4*F] -M`%"IO0``2[T``$N]`(#MO`"`[;P`T)\]`-"?/0"`1+P`@$2\`/"7O0#PE[T` -MX`V]`.`-O0"`(+P`@""\`/#1/0#PT3T`0"<]`$`G/0#`@[T`P(.]`,"TO`#` -MM+P``,Z\``#.O`!PAST`<(<]`.`G/0#@)ST`8&6]`&!EO0``R;P``,F\`!"5 -MO0`0E;T`@`D\`(`)/`#`RCT`P,H]`(`^O`"`/KP``&*[``!BNP"`R+P`@,B\ -M`("(O`"`B+P`X#0]`.`T/0!@1KT`8$:]`(`?O`"`'[P`@&6\`(!EO``@E[T` -M()>]`,`N/0#`+CT`P.4\`,#E/`#`T#P`P-`\`("Z/`"`NCP`P)2]`,"4O0#` -M,CT`P#(]`"!-/0`@33T``&@[``!H.P``$ST``!,]`*#1O0"@T;T`4+.]`%"S -MO0"`-3P`@#4\``#(N@``R+H`P%`]`,!0/0!@4[T`8%.]``!$O0``1+T`T)\] -M`-"?/0`@-3T`(#4]`+"%/0"PA3T``&.\``!CO`!0L;T`4+&]`,"_/`#`OSP` -M`+P[``"\.P``FCP``)H\`("NO`"`KKP`"!.^``@3O@``!KT```:]`,#U/`#` -M]3P`8#L]`&`[/0"`=3T`@'4]`&`&O0!@!KT``)(\``"2/`!`+ST`0"\]``"" -M/```@CP``%P]``!\```GO```(;P``"&\`&!XO0!@>+T``)H\``":/``@ -M+ST`("\]`(!YO0"`>;T`0+V\`$"]O`!`5[T`0%>]`(`;O`"`&[P``*\]``"O -M/0``J;L``*F[```7O```%[P`@#B]`(`XO0``![T```>]`!#B/0`0XCT`0)8\ -M`$"6/`!@6;T`8%F]```PO0``,+T`@)2]`("4O0#`;CT`P&X]`(!$/0"`1#T` -M`/2Z``#TN@``E#L``)0[`%"5O0!0E;T`0.8\`$#F/`#@AST`X(<]`$`9O0!` -M&;T`P)Z\`,">O``@E[T`()>]`(`3O`"`$[P`\*4]`/"E/0```KP```*\``!: -M/```6CP``+R\``"\O`#@1+T`X$2]`,!F/0#`9CT``/4\``#U/`"`ZCP`@.H\ -M``#SO```\[P`D->]`)#7O0``@#L``(`[``!@.0``8#D``)"Y``"0N0``-3P` -M`#4\`&!TO0!@=+T``',\``!S/`#@03T`X$$]`.!`/0#@0#T`$)<]`!"7/0#@ -M?+T`X'R]`"!!O0`@0;T`0-X\`$#>/`"`JSP`@*L\`$#O/`!`[SP`D*N]`)"K -MO0!0V;T`4-F]`(#>O`"`WKP``!D\```9/`!@SST`8,\]`(`P/`"`,#P`\(.] -M`/"#O0"@&CT`H!H]`"!%/0`@13T`\)<]`/"7/0``Q[L``,>[`"#FO0`@YKT` -MP)V\`,"=O```.KP``#J\`(#&/`"`QCP`(",]`"`C/0#0M+T`T+2]`.`5O0#@ -M%;T``%$\``!1/``@3CT`($X]`%"J/0!0JCT`0"J]`$`JO0#`H[P`P*.\`,"5 -M/`#`E3P```>]```'O0"@)#T`H"0]``#0O```T+P``$2]``!$O0``'CP``!X\ -M```9O0``&;T`H"`]`*`@/0!`^CP`0/H\```@O0``(+T``&X\``!N/``@`+T` -M(`"]`(!`/`"`0#P`P'<]`,!W/0``,+H``#"Z``"*/```BCP`X(R]`.",O0!@ -MB+T`8(B]``!,/0``3#T``',\``!S/`#`KCP`P*X\`,"^O`#`OKP``'F]``!Y -MO0"`<#T`@'`]`.`B/0#@(CT`@*@\`("H/`#`XKP`P.*\`"#AO0`@X;T``+X[ -M``"^.P"PF#T`L)@]`*`=/0"@'3T`@..\`(#CO`"PW;T`L-V]`("T/`"`M#P` -M$*P]`!"L/0"`"3T`@`D]`,"_/`#`OSP`P**]`,"BO0!`,[T`0#.]`(`K/0"` -M*ST`P((\`,""/`"`CSP`@(\\`$"6O0!`EKT`X%^]`.!?O0"`F#T`@)@]```O -M/0``+ST`0/@\`$#X/`#`$KT`P!*]`!",O0`0C+T`@-<\`(#7/```?#P``'P\ -M`$`:/0!`&CT``$P\``!,/```N+T``+B]``"@.P``H#L`@$(\`(!"/```@CL` -M`(([`.`D/0#@)#T`(""]`"`@O0``;CL``&X[`(#=/`"`W3P``%`Z``!0.@!` -M?3T`0'T]`,#RO`#`\KP`P'^]`,!_O0``4[P``%.\`(#)O`"`R;P`P&`]`,!@ -M/0``PSL``,,[`"!5O0`@5;T``-"Y``#0N0"`W+P`@-R\`-""/0#0@CT`T,`] -M`-#`/0"`Q;P`@,6\`(#5O`"`U;P``$J]``!*O0"`.SP`@#L\`(!G/0"`9ST` -M,("]`#"`O0`@<+T`('"]``"=O```G;P`P,0\`,#$/```RCT``,H]``!?/``` -M7SP`@':\`(!VO`#@!;T`X`6]`*`3O0"@$[T`8)P]`&"O0``AKL``(:[`##H/0`P -MZ#T`P"@]`,`H/0"`D+P`@)"\`,#MO`#`[;P``'^]``!_O0"@5CT`H%8]`*!$ -M/0"@1#T``+>\``"WO`#`M;P`P+6\`"!QO0`@<;T`0+0\`$"T/`!@*CT`8"H] -M`(#UO`"`];P``(`[``"`.P"@![T`H`>]``#5.P``U3L``(@]``"(/0``OSL` -M`+\[``",/```C#P`0,"\`$#`O``@(KT`("*]`(!#/0"`0ST``!"Z```0N@"` -M!+P`@`2\````````````H#2]`*`TO0``P+L``,"[``!UO```=;P``-P\``#< -M/`#@93T`X&4]`,`(O0#`"+T`P(&\`,"!O```\SL``/,[`$"S/`!`LSP`0&4] -M`$!E/0``SKP``,Z\``#NO```[KP``(F[``")NP"`$3P`@!$\``"4/0``E#T` -M@)*\`("2O`#@R+T`X,B]``!;O0``6[T``+F[``"YNP!@HCT`8*(]`$#6/`!` -MUCP`P!*]`,`2O0``SSL``,\[``#\.P``_#L`@)<]`("7/0!@;3T`8&T]`$!8 -MO0!`6+T``">]```GO0#`#[T`P`^]`,#;/`#`VSP`H$L]`*!+/0``C[T``(^] -M`"`]O0`@/;T``-.[``#3NP!`I#P`0*0\`-"#T`H'@]`(")/`"`B3P`P`.]`,`#O0`` -MG;L``)V[`,#_O`#`_[P`@.H\`(#J/`!`FSP`0)L\`(`1O0"`$;T`@$@\`(!( -M/```"KT```J]``!H.@``:#H``(\]``"//0``S#P``,P\```W/```-SP`(%F] -M`"!9O0`@-KT`(#:]`(`3/0"`$ST``.,[``#C.P"`?CP`@'X\`$"AO`!`H;P` -M0'>]`$!WO0!@!ST`8`<]`(`G/0"`)ST`@-$\`(#1/```-+P``#2\`.")O0#@ -MB;T`0*L\`$"K/`"@0#T`H$`]`(!7/`"`5SP```"Z````N@#@>+T`X'B]`(!+ -MO`"`2[P`("(]`"`B/0!@13T`8$4]`&`6/0!@%CT`0(>]`$"'O0!@2[T`8$N] -M`,#,/`#`S#P``/`[``#P.P#`F3P`P)D\```"O0```KT`H`.]`*`#O0#`$3T` -MP!$]`,"L/`#`K#P`X$8]`.!&/0``,CL``#([`/""O0#P@KT``':[``!VNP`` -M?#P``'P\```I/0``*3T``&,\``!C/`"`AKT`@(:]```1O```$;P``*2[``"D -MNP```3P```$\`.!I/0#@:3T`@)*\`("2O`"`CKP`@(Z\```PN@``,+H``*"Z -M``"@N@#0@CT`T((]`(!2O`"`4KP`X$&]`.!!O0``W+L``-R[`("0O`"`D+P` -M8"X]`&`N/0"`F3P`@)D\`$`PO0!`,+T`@+Z\`("^O`!`Y[P`0.>\`.!V/0#@ -M=CT`0)X]`$">/0!`PKP`0,*\``#0NP``T+L`@*.\`("CO`"`"+P`@`B\``#; -M/```VSP`X`"]`.``O0#`J+P`P*B\``#9O```V;P`P):\`,"6O`#0ACT`T(8] -M`$#R/`!`\CP``,6[``#%NP``?+P``'R\`$"$O`!`A+P`($T]`"!-/0#`M#P` -MP+0\```;O```&[P`@%B\`(!8O`!@E;T`8)6]`$"%O`!`A;P``,T\``#-/``` -M9CP``&8\`,">/`#`GCP`@/F\`(#YO```)CP``"8\`$!:/0!`6CT`@*\\`("O -M/`!`N3P`0+D\`*`4O0"@%+T`H"F]`*`IO0#`BCP`P(H\`(!?/`"`7SP``.D\ -M``#I/`!@1;T`8$6]`+"IO0"PJ;T`@)$\`("1/`!@$#T`8!`]`*!N/0"@;CT` -M`.@\``#H/``@5KT`(%:]```(.@``"#H`0*,\`$"C/``@&CT`(!H]``">/``` -MGCP`@+"]`("PO0!@"+T`8`B]`.`D/0#@)#T`X"`]`.`@/0#`YSP`P.<\`%", -MO0!0C+T`P!R]`,`]`.!'O0#` -MQ+P`P,2\``"WO```M[P``(F[``")NP!`*CT`0"H]`("YO`"`N;P``$&\``!! -MO```\#L``/`[```0.@``$#H`(&P]`"!L/0"@&ST`H!L]``!`/```0#P``!"\ -M```0O`!`5[T`0%>]``!`N0``0+D```:[```&NP"@"KT`H`J]``#`O```P+P` -MP"&]`,`AO0#``CT`P`(]`&!]/0!@?3T`0`$]`$`!/0"`Z#P`@.@\```>O0`` -M'KT``+&[``"QNP#PBST`\(L]`,"KP` -M`,4[``#%.P``\KP``/*\`,";/`#`FSP`<*H]`'"J/0"`^#P`@/@\``">/``` -MGCP``,6\``#%O`"`2+T`@$B]`(!7/`"`5SP``/`[``#P.P"`(CP`@"(\`.`% -MO0#@!;T`X(J]`."*O0"`;SP`@&\\`,#:/`#`VCP`@*@\`("H/`"`JCP`@*H\ -M``"9O```F;P`P-(\`,#2/`#`'#T`P!P]`(!Q/`"`<3P`@$`\`(!`/`!@9[T` -M8&>]`$`IO0!`*;T``'0[``!T.P``@#H``(`Z`(!V/`"`=CP`@(V\`("-O`"` -M0;P`@$&\``!C/```8SP``/T[``#].P!`8#T`0&`]``#$/```Q#P`P..\`,#C -MO`!`A+P`0(2\`,#>O`#`WKP``'8[``!V.P``6CL``%H[`$#LO`!`[+P`@"T\ -M`(`M/```M#H``+0Z`,#M/`#`[3P`0((]`$""/0``/#L``#P[`,#:O`#`VKP` -M(&Z]`"!NO0"`H+P`@*"\`"!./0`@3CT``(&[``"!NP#`V[P`P-N\`(#IO`"` -MZ;P`@)&\`("1O``PC#T`,(P]`&`:/0!@&CT`@!R\`(`O0``'KT`8"6]`&`EO0``++T``"R] -M`$#B/`!`XCP``%@]``!8/0``([P``".\``!3O```4[P``!>]```7O0``[+H` -M`.RZ`)"-/0"0C3T`@%`\`(!0/```2[P``$N\`("6O`"`EKP`0(6\`$"%O``` -MZ#P``.@\`$"RO`!`LKP`8`F]`&`)O0``*KP``"J\`("3O`"`D[P`(!<]`"`7 -M/0"`5ST`@%<]`(#[/`"`^SP```P\```,/`"`$KT`@!*]``!8N@``6+H``."Y -M``#@N0```[T```.]``#ONP``[[L```*]```"O0#`U+P`P-2\```2/```$CP` -M`*L\``"K/`#@?ST`X'\]`$"J/`!`JCP``$*\``!"O`"`;#P`@&P\`(`8/`"` -M&#P`0(@\`$"(/``@`KT`(`*]`,"1O0#`D;T``.B\``#HO```R[L``,N[```T -M/0``-#T`P!@]`,`8/0!@$;T`8!&]``"TNP``M+L`0($\`$"!/`!`(3T`0"$] -M`(`Z/0"`.CT`@+J\`("ZO`"`<+P`@'"\``#'NP``Q[L``"R\```LO`!`@CP` -M0((\`&`YO0!@.;T``'2]``!TO0``GKP``)Z\``!5/```53P``)(]``"2/0#` -MR3P`P,D\``!3O```4[P`0.4\`$#E/```DSL``),[`(#$/`"`Q#P`@'H\`(!Z -M/`#@+KT`X"Z]`"`-O0`@#;T``#N]```[O0``SKL``,Z[`&`#/0!@`ST`@#2\ -M`(`TO```C#L``(P[`,"0/`#`D#P`0)T\`$"=/`#`(#T`P"`]```1O```$;P` -M@*.\`("CO``@![T`(`>]`.`JO0#@*KT`X``]`.``/0"`!CT`@`8]``"_.P`` -MOSL``-PZ``#<.@``R+P``,B\`(`)/`"`"3P`0(D\`$")/```8+D``&"Y```J -MO```*KP`X#6]`.`UO0``Q;L``,6[`&`3/0!@$ST``'8\``!V/```G+H``)RZ -M`"`0O0`@$+T```J[```*NP!@*ST`8"L]``!U/```=3P`@($\`("!/```S+P` -M`,R\`"`RO0`@,KT```([```".P"`1#P`@$0\`("K/`"`JSP`P*6\`,"EO`#` -M<+T`P'"]```..P``#CL`0+<\`$"W/`#`\SP`P/,\`,"G/`#`ISP`@$>\`(!' -MO```]CP``/8\`(`'/0"`!ST`P+(\`,"R/```?CP``'X\`+"@O0"PH+T`X*F] -M`."IO0#`_[P`P/^\``#)NP``R;L`0/D\`$#Y/```"KL```J[```JNP``*KL` -MP!\]`,`?/0`@$ST`(!,]``!]/0``?3T`0+4\`$"U/`!`,;T`0#&]`("BO`"` -MHKP``(B\``"(O```W#L``-P[```;O```&[P`\("]`/"`O0#`XKP`P.*\``!T -MNP``=+L`@-4\`(#5/`#@9ST`X&<]``">NP``GKL`@+Z\`("^O```L+H``+"Z -M`,#,/`#`S#P`X#X]`.`^/0"`#;P`@`V\`,#@O`#`X+P`@"&\`(`AO`"`E+P` -M@)2\``#@.@``X#H`P*Z\`,"NO`!``+T`0`"]`(`.O`"`#KP``""[```@NP`@ -M5ST`(%<]`(!9/0"`63T``%@Z``!8.@"`0+P`@$"\`,#1O`#`T;P`@`:\`(`& -MO```7#L``%P[``"(O```B+P``#:[```VNP#`N+P`P+B\``#]NP``_;L`8`0] -M`&`$/0``A#P``(0\`(!?/`"`7SP`P*6\`,"EO`"`![T`@`>]``!,/```3#P` -M`+2Z``"TN@``$CL``!([``#ANP``X;L`8!R]`&`SP``'L\``!,O```3+P``(<[``"'.P"`"3P`@`D\``"MO``` -MK;P``+N\``"[O`!@$KT`8!*]``#X.P``^#L`("(]`"`B/0"`++P`@"R\``"I -MO```J;P`P(V\`,"-O```@#H``(`Z`"`:/0`@&CT``'4\``!U/```E#L``)0[ -M``#\``"3O```D[P`0)N\`$";O``` -MR#L``,@[`&`P/0!@,#T``!,\```3/```4#H``%`Z``!:NP``6KL``$@Z``!( -M.@#`_CP`P/X\`$""O`!`@KP`H`Z]`*`.O0#`Q;P`P,6\`(#6O`"`UKP`P)0\ -M`,"4/`"`O3P`@+T\``#H.@``Z#H``)R[``"\``!7O`"`#CP`@`X\`(#&/`"`QCP` -M@!^\`(`?O```$CL``!([`(!M/`"`;3P`@'`\`(!P/`#`@CP`P((\`$`$O0!` -M!+T`X`.]`.`#O0#`S;P`P,V\``#`O```P+P`0/@\`$#X/`"`ASP`@(<\`$"4 -MO`!`E+P``/V[``#]NP``HSL``*,[`&!>/0!@7CT``"X]```N/0``)+P``"2\ -M`(`[O`"`.[P`0)>\`$"7O```-CL``#8[`(!3/`"`4SP`P!.]`,`3O0`@/;T` -M(#V]`&`!O0!@`;T``(<\``"'/``@3CT`($X]``"+NP``B[L`0(^\`$"/O``` -M\+H``/"Z``!=/```73P`0$@]`$!(/0``!CT```8]``!\`(!J -M/`"`:CP``$T\``!-/`#`ISP`P*<\``"L.@``K#H`X`2]`.`$O0``I3L``*4[ -M`("B/`"`HCP``((\``""/`"`1#P`@$0\`*`DO0"@)+T`(!^]`"`?O0``Z+L` -M`.B[``!N/```;CP`@/,\`(#S/```C;P``(V\``#%O```Q;P``($\``"!/`"` -M=#P`@'0\`$"N/`!`KCP``)F[``"9NP"`E;P`@)6\`(`H/`"`*#P``#8\```V -M/```"ST```L]`$"%/`!`A3P`($2]`"!$O0"`)KT`@":]``#=NP``W;L`@(0\ -M`("$/```(SP``",\`$#]O`!`_;P`@&:\`(!FO````SP```,\`$"V/`!`MCP` -M("0]`"`D/0``/SP``#\\``"4NP``E+L``-2Z``#4N@``BSL``(L[`(!R/`"` -M/`"`NSP`@+L\ -M``##NP``P[L```2\```$O```HKP``**\``"XNP``N+L``!8[```6.P"`<[P` -M@'.\``#[NP``^[L``)B\``"8O`"`"KP`@`J\`"`B/0`@(CT`0.T\`$#M/``` -M"3P```D\`(`ZO`"`.KP`0(B\`$"(O```3#P``$P\``!0.@``4#H``*RZ``"L -MN@``E#H``)0Z`"`.O0`@#KT`P("\`,"`O```(#P``"`\`$"6/`!`ECP``*`\ -M``"@/```A[P``(>\``"@N@``H+H`@-(\`(#2/```FCL``)H[```"NP```KL` -M0`&]`$`!O0!@+;T`8"V]``!\.P``?#L`P-(\`,#2/`"@%#T`H!0]```V.P`` -M-CL``.*\``#BO```.+H``#BZ```3/```$SP``&8\``!F/```RCL``,H[`(#Q -MO`"`\;P`P(F\`,")O```RCL``,H[`("Q/`"`L3P``&P\``!L/`!@#[T`8`^] -M`(#EO`"`Y;P`0)0\`$"4/`!`]3P`0/4\``!Y/```>3P`P,6\`,#%O```S[P` -M`,^\``"JNP``JKL`@`8\`(`&/`!`]CP`0/8\``"D.P``I#L``+N\``"[O`"` -M2[P`@$N\``#".P``PCL`@,`\`(#`/```5KL``%:[`$#O`#`WKP`@,^\ -M`(#/O```F3L``)D[`(`0/0"`$#T`@$(\`(!"/`"`23P`@$D\`(`&/`"`!CP` -M`)"Z``"0N@``1CP``$8\```"O````KP``'F\``!YO`!`C;P`0(V\`(`'O0"` -M![T``"P[```L.P``(3P``"$\``"RNP``LKL``+T[``"].P``D+D``)"Y``"F -M/```ICP`@/(\`(#R/`"`:CP`@&H\``"D.@``I#H`8#>]`&`WO0"@,;T`H#&] -M`(`(/`"`"#P`@`D\`(`)/```4+H``%"Z`$"BO`!`HKP`@(:\`("&O```K3P` -M`*T\`$"2/`!`DCP``%8\``!6/```SSL``,\[```BO```(KP``*4\``"E/``` -M`#T````]`(`3/`"`$SP`0/2\`$#TO`!`?;T`0'V]`$`'O0!`![T``/BZ``#X -MN@``$#L``!`[``!^.P``?CL``!:\```6O`"`#SP`@`\\`.`'/0#@!ST`@.@\ -M`(#H/`"`M3P`@+4\``!?O```7[P`0+>\`$"WO```!KL```:[```NNP``+KL` -M@"2\`(`DO```Z[P``.N\``#'O```Q[P``+L[``"[.P"`2SP`@$L\`$#V/`!` -M]CP``%@\``!8/```W[P``-^\`,"5O`#`E;P``+:[``"VNP``X#L``.`[``#< -M.P``W#L``!F\```9O`"`O`"` -MWKP``,*\``#"O`"`\`$"GO```&;T``!F]```HO```*+P``#J[```ZNP``O#L``+P[ -M``!X.@``>#H`0)"\`$"0O`"`$KP`@!*\``!TNP``=+L`P(`\`,"`/`#`S#P` -MP,P\``"@NP``H+L```6\```%O```S+H``,RZ```-/```#3P``!(\```2/`#` -ME;P`P)6\`$"]O`!`O;P``%Z\``!>O```F;L``)F[`("%/`"`A3P``(BZ``"( -MN@"`F+P`@)B\```@N@``(+H`0.@\`$#H/``@-CT`(#8]```\/```/#P`0+J\ -M`$"ZO`"`W;P`@-V\```'O0``![T`P)*\`,"2O```[+L``.R[``"1NP``D;L` -M`(8[``"&.P"`$CP`@!(\`*`;/0"@&ST`8!H]`&`:/0"`*+P`@"B\`$# -MO```'KP``$^\``!/O`#`#[T`P`^]``!6O0``5KT`@-J\`(#:O```@CL``(([ -M`$#9/`!`V3P`@#P\`(`\/```&;P``!F\``"@N@``H+H``*8[``"F.P"`MSP` -M@+<\````/0```#T``(@Z``"(.@"`)[P`@">\`(`RO`"`,KP``,"Y``#`N0`` -MU#H``-0Z`(`4O0"`%+T`X!&]`.`1O0``F+L``)B[``##.P``PSL``)@\``"8 -M/```1#P``$0\```@/```(#P`@"<\`(`G/```T#D``-`Y`(!Y/`"`>3P``(H[ -M``"*.P#`W;P`P-V\``##O```P[P`@+B\`("XO`"`0KP`@$*\``!".P``0CL` -M`+@[``"X.P``VCP``-H\`(""/`"`@CP``("[``"`NP``(+H``""Z`(`HO`"` -M*+P`0)N\`$";O`!`JKP`0*J\`(!EO`"`9;P`@%P\`(!\`$"!O`!`@;P``)2Z``"4N@``;+L``&R[ -M`,"8O`#`F+P`8!:]`&`6O0``A[P``(>\``#".P``PCL`@"$\`(`A/`"`A3P` -M@(4\``#R.P``\CL``'H[``!Z.P#`@SP`P(,\`,#F/`#`YCP``)L\``";/``` -MT+P``-"\`,`GO0#`)[T``!>\```7O```=CL``'8[```PN@``,+H`P-"\`,#0 -MO`"`"[T`@`N]```PO```,+P``'@[``!X.P``O3P``+T\``#H/```Z#P`@#\\ -M`(`_/`"`LCP`@+(\`(#C/`"`XSP`0(H\`$"*/```SP``$R[``!,NP``3+P``$R\`(#9O`"`V;P``%>\ -M``!7O```_3L``/T[```W/```-SP``!P[```<.P``+[P``"^\```0.P``$#L` -M`*X\``"N/```/3P``#T\```..P``#CL``("\``"`O`!`T;P`0-&\``!^O``` -M?KP``"*\```BO```F+L``)B[`(!OO`"`;[P`@"R\`(`LO```_SP``/\\`"`1 -M/0`@$3T`@)4\`("5/```-CL``#8[``"SO```L[P`P+R\`,"\O`"`F;P`@)F\ -M`(`7O`"`%[P``,@Z``#(.@#`BKP`P(J\`(!2O`"`4KP`@%H\`(!:/`"`63P` -M@%D\`(`?/`"`'SP``/BZ``#XN@``J3L``*D[`$"(/`!`B#P``(4[``"%.P`` -MFSL``)L[``#:NP``VKL```Z]```.O0!`[+P`0.R\`(!VO`"`=KP``&"Y``!@ -MN0``9CL``&8[``#:NP``VKL``'X[``!^.P``03P``$$\`(!W/`"`=SP`0-$\ -M`$#1/```5CP``%8\``#HN@``Z+H``$>\``!'O`!`@KP`0(*\``!JO```:KP` -M0.J\`$#JO```Q;P``,6\``#ENP``Y;L``%@[``!8.P!`S3P`0,T\```4/0`` -M%#T`@)H\`(":/`"`'[P`@!^\`(#@O`"`X+P``)N[``";NP``ISL``*<[``!4 -MO```5+P`P*V\`,"MO`#`@;P`P(&\``"6.P``ECL``$0\``!$/```T#L``-`[ -M```>/```'CP``,.[``##NP``F+H``)BZ``##P`P*8\`,"F/`"`=#P`@'0\``#XN@``^+H` -M`.&[``#ANP``RCL``,H[``#;NP``V[L`@%R\`(!\`$"WO``` -M,3P``#$\`,#!/`#`P3P`P(0\`,"$/```^[L``/N[```EO```);P``,N[``#+ -MNP``\;L``/&[``"..P``CCL``""Z```@N@``";P```F\```2/```$CP`@.0\ -M`(#D/`#`YCP`P.8\``"GNP``I[L`P.6\`,#EO```&;P``!F\``!0NP``4+L` -M`-^[``#?NP"`1[P`@$>\``!ZO```>KP``":[```FNP"`$CP`@!(\``"K/``` -MJSP`@/T\`(#]/```@3P``($\``#,.P``S#L``($[``"!.P``V+L``-B[`,"? -MO`#`G[P`P-*\`,#2O`"`6;P`@%F\``!LNP``;+L``!`Z```0.@"`CSP`@(\\ -M`(!E/`"`93P``*"Z``"@N@``LKL``+*[``".NP``CKL``&0[``!D.P``XCL` -M`.([`(!3/`"`4SP``%8\``!6/`"`$KP`@!*\``!UO```=;P``-"Y``#0N0`` -M<#H``'`Z`(`"O`"``KP`0(>\`$"'O```B3L``(D[``"K/```JSP`@#<\`(`W -M/`"`.#P`@#@\``!@.P``8#L``$6\``!%O```@[L``(.[```P.P``,#L``*"Z -M``"@N@"`O+P`@+R\`$`/O0!`#[T``#B[```XNP!`O3P`0+T\`$#&/`!`QCP` -MP*X\`,"N/```2#P``$@\```]/```/3P``#`[```P.P``(KP``"*\``!WO``` -M=[P```&]```!O0``F;P``)F\`(!:/`"`6CP`0),\`$"3/`"`+SP`@"\\``#` -M.```P#@``.*[``#BNP``K+H``*RZ```FNP``)KL``*`[``"@.P``A#L``(0[ -M```(O```"+P``":[```FNP``%CP``!8\```Y/```.3P``$@Z``!(.@``";P` -M``F\``!$.P``1#L``'`Z``!P.@``\KL``/*[```"NP```KL``(N[``"+NP"` -M&[P`@!N\``"!NP``@;L`@%T\`(!=/`!`SCP`0,X\``"G.P``ISL``&.\``!C -MO`"`/[P`@#^\``#JNP``ZKL``-"Y``#0N0``/#L``#P[`(!!/`"`03P`@!`\ -M`(`0/```5+L``%2[``").P``B3L````Y````.0!`Q;P`0,6\`$#6O`!`UKP` -M`%.\``!3O```.CP``#H\`$#:/`!`VCP`@,8\`(#&/```G#P``)P\```P.P`` -M,#L```B[```(NP``YSL``.<[```!O````;P`0,R\`$#,O``@!+T`(`2]`(## -MO`"`P[P``'R[``!\NP``,#H``#`Z`(!'/`"`1SP`0+`\`$"P/`"`+SP`@"\\ -M`,"6/`#`ECP`@.(\`(#B/```"3P```D\``"5O```E;P```:]```&O0"`*KP` -M@"J\``#P.P``\#L``$"Z``!`N@``I+L``*2[```IO```*;P``&2\``!DO``` -MO;L``+V[``!F.P``9CL`@&<\`(!G/```RCL``,H[``"E.P``I3L``.8\``#F -M/```VSP``-L\````N````+@`0+*\`$"RO```]+P``/2\`("QO`"`L;P``'F\ -M``!YO```W;L``-V[``#DN@``Y+H``)F[``"9NP"`!3P`@`4\``"]/```O3P` -MP-<\`,#7/```@SP``(,\``"@.0``H#D``!R[```+P`@'B\`,#CO`#`X[P`0+2\`$"TO```HKL``**[``!(O```2+P` -M0)^\`$"?O```;+L``&R[`"`$/0`@!#T`0%,]`$!3/0!`!#T`0`0]```A/``` -M(3P``/J[``#ZNP``RKP``,J\``#CO```X[P`0,Z\`$#.O`"`K[P`@*^\`("C -MO`"`H[P``#B\```XO`"`GCP`@)X\``#9/```V3P`@$$\`(!!/```,#P``#`\ -M``"1/```D3P``#P\```\/```P+L``,"[```0O```$+P``$R[``!,NP!`DKP` -M0)*\``"LO```K+P``,J[``#*NP``A3L``(4[``#3.P``TSL``'B[``!XNP`` -M`;P```&\``"INP``J;L``-J[``#:NP"`0#P`@$`\`("X/`"`N#P``!<\```7 -M/```X#H``.`Z``#$.@``Q#H``%P[``!<.P``*KP``"J\`$#9O`!`V;P``)2\ -M``"4O```O[L``+^[`(`>/`"`'CP`0-T\`$#=/```,CP``#(\`(`3O`"`$[P` -M@&"\`(!@O```4KL``%*[``#9.P``V3L``#:\```VO`#`FKP`P)J\``!P.@`` -M<#H`@"(\`(`B/```6SP``%L\``!0/```4#P``/,[``#S.P``X#D``.`Y``#3 -MNP``T[L``(`Z``"`.@``E;L``)6[``#XO```^+P`@/:\`(#VO`"`&;P`@!F\ -M`(!9/`"`63P`P-$\`,#1/`"`JCP`@*H\`("E/`"`I3P`@!<\`(`7/````KP` -M``*\```*O```"KP`@`>\`(`'O`#`AKP`P(:\``",O```C+P``*^[``"ONP`` -M_SL``/\[``!`N0``0+D``*6[``"ENP``!+L```2[``"3NP``D[L``,`[``#` -M.P"`L3P`@+$\`,"S/`#`LSP``/@[``#X.P``"[P```N\``!BNP``8KL``,RZ -M``#,N@"`:+P`@&B\`(!WO`"`=[P`@%6\`(!5O`"`9+P`@&2\``#DNP``Y+L` -M@!T\`(`=/`"`N#P`@+@\`(`I/`"`*3P``,$[``#!.P!`R#P`0,@\`,"!/`#` -M@3P`@`N\`(`+O```XKP``.*\`.`,O0#@#+T`0,2\`$#$O```/[P``#^\`(`' -M/`"`!SP`@.X\`(#N/`#`M#P`P+0\`(!V/`"`=CP`@'@\`(!X/```,#P``#`\ -M``#PN@``\+H`0+*\`$"RO`"`KKP`@*Z\`(`[O`"`.[P`@#B\`(`XO```K+H` -M`*RZ```,NP``#+L`@!:\`(`6O````#H````Z`(!L/`"`;#P`0-4\`$#5/``` -MB#P``(@\``".NP``CKL``*>[``"GNP``B+H``(BZ``#P.0``\#D``#H[```Z -M.P``Z;L``.F[`(!VO`"`=KP`@&:\`(!FO```##L```P[````/````#P``"J\ -M```JO```!+P```2\`$"(/`!`B#P`@-<\`(#7/`#`HCP`P*(\``#4.@``U#H` -M@%.\`(!3O`!`O;P`0+V\`,#&O`#`QKP``*2[``"DNP``K#L``*P[``"0N0`` -MD+D``,$[``#!.P"`KCP`@*X\``#1/```T3P``#8[```V.P``3KP``$Z\``!\ -MNP``?+L```J\```*O```0[P``$.\``"(NP``B+L``/2Z``#TN@``D+H``)"Z -M``"T.@``M#H`@"$\`(`A/`"`DCP`@)(\```3/```$SP``,P[``#,.P``)KL` -M`":[`,"NO`#`KKP``,2\``#$O`"`0+P`@$"\``">.P``GCL``.<[``#G.P`` -M`#H````Z`(!V/`"`=CP``*P\``"L/`"`0CP`@$(\``#B.P``XCL``*V[``"M -MNP"`++P`@"R\``"4N@``E+H```,\```#/```FSL``)L[`,#`O`#`P+P`H!R] -M`*`SP` -M``<\```'/```T;L``-&[`(`?O`"`'[P``#R\```\O```Q#H``,0Z`$";/`!` -MFSP``+<\``"W/```&#P``!@\```SO```,[P``"6\```EO```2+L``$B[`$"; -MO`!`F[P`0+V\`$"]O`"`0+P`@$"\``"(.P``B#L`@*X\`("N/```Y#P``.0\ -M``"V/```MCP``,H[``#*.P``9KL``&:[``#,.P``S#L``(4[``"%.P``,;P` -M`#&\`("DO`"`I+P``*N\``"KO```?+P``'R\`(`]O`"`/;P``"0[```D.P!` -MICP`0*8\`(!H/`"`:#P`@%P\`(!NP"`@KP`@(*\`(#FO`"` -MYKP`P.Z\`,#NO`#`I;P`P*6\```XNP``.+L`@!4\`(`5/```*CL``"H[``!T -M.P``=#L``%8\``!6/`!`BCP`0(H\`,"7/`#`ESP`@#(\`(`R/```.CL``#H[ -M``#ANP``X;L`0*R\`$"LO`"`#KP`@`Z\``!>.P``7CL``(6[``"%NP``C;L` -M`(V[``#F.P``YCL`@$T\`(!-/```ISL``*<[``#\`,"W -MO```9+P``&2\`(`V/`"`-CP``#P\```\/```P#L``,`[```3/```$SP``*P[ -M``"L.P``C#L``(P[``"'.P``ASL``+N[``"[NP#`N+P`P+B\`$#!O`!`P;P` -M`"@[```H.P``H3P``*$\```T/```-#P``%0[``!4.P``0#H``$`Z`(`>/`"` -M'CP`@&X\`(!N/```Z#L``.@[``!6NP``5KL``)*\``"2O`#`IKP`P*:\``#7 -MNP``U[L``-^[``#?NP"`1+P`@$2\``#@N@``X+H`@*D\`("I/`"`[SP`@.\\ -M`,"`/`#`@#P`@`X\`(`./```@SL``(,[```LO```++P``%"\``!0O```_;L` -M`/V[``!PNP``<+L``'R[``!\NP"`&;P`@!F\```.NP``#KL``&`Y``!@.0`` -M8KL``&*[```K/```*SP`@,8\`(#&/`"`B#P`@(@\```&.P``!CL``$BZ``!( -MN@``T#L``-`[```;O```&[P`@-^\`(#?O`!`L+P`0+"\`(`3O`"`$[P``,<[ -M``#'.P"`>CP`@'H\`(`Z/`"`.CP``/\[``#_.P``W#L``-P[`$"I/`!`J3P` -M`.@\``#H/```"CL```H[``"XO```N+P`P+.\`,"SO`"`=;P`@'6\``!OO``` -M;[P`@'J\`(!ZO```FKL``)J[``#..P``SCL`0)D\`$"9/`!`&#T`0!@]`(#. -M/`"`SCP``)^[``"?NP``;KP``&Z\```DNP``)+L``-,[``#3.P``GKL``)Z[ -M`(""O`"`@KP``-Z[``#>NP``-KL``#:[``!LNP``;+L``-`Z``#0.@```CP` -M``(\```)/```"3P``.D[``#I.P``C3P``(T\`("3/`"`DSP``$Z[``!.NP`` -M1KP``$:\`(`6O`"`%KP``/R[``#\NP``T[L``-.[``##NP``P[L``$`Z``!` -M.@``8#D``&`Y``#,NP``S+L``)<[``"7.P``FCP``)H\`(":/`"`FCP`@$X\ -M`(!./```F3L``)D[``!`N0``0+D`@"Z\`(`NO`"`-KP`@#:\``"]NP``O;L` -M`$.\``!#O```EKL``):[``"-/```C3P``*T\``"M/`"`'#P`@!P\`(`,O`"` -M#+P`@'B\`(!XO`"`(;P`@"&\``#LNP``[+L```H[```*.P"`#SP`@`\\```F -M.P``)CL``(L[``"+.P``BSP``(L\`(#(/`"`R#P`@`X\`(`./```#[P```^\ -M``"@.0``H#D``#P[```\.P``'KP``!Z\`,"!O`#`@;P`P(J\`,"*O`"`<+P` -M@'"\``#`NP``P+L`@!<\`(`7/`"`QSP`@,<\`(!O/`"`;SP``+PZ``"\.@`` -MW3L``-T[`(!]/`"`?3P``$\\``!//```DCL``)([```".P```CL``(6[``"% -MNP"`A[P`@(>\`(!LO`"`;+P`@'*\`(!RO`"`PKP`@,*\``!QO```<;P`@!L\ -M`(`;/`#`^SP`P/L\```4/0``%#T`P-T\`,#=/```CSP``(\\``!L.P``;#L` -M`..[``#CNP``^+L``/B[``!OO```;[P`@,N\`(#+O```Y[P``.>\`(`VO`"` -M-KP``/T[``#].P``Q3L``,4[```;/```&SP`P,@\`,#(/```PSP``,,\`$"1 -M/`!`D3P`@"X\`(`N/```M#H``+0Z`(`MO`"`+;P``)F\``"9O```S+L``,R[ -M``#H.P``Z#L``+0Z``"T.@``F+L``)B[``"PNP``L+L``(^[``"/NP``[+L` -M`.R[``!8NP``6+L``&H\``!J/`#`A#P`P(0\```//```#SP``'4\``!U/``` -M:SP``&L\``#L.@``[#H``!.\```3O`"`=;P`@'6\```4O```%+P``(`X``"` -M.`"`+SP`@"\\`(`O/`"`+SP``(*[``""NP"`';P`@!V\```L.P``+#L`@%`\ -M`(!0/```MSL``+<[```@O```(+P``)2[``"4NP``WSL``-\[```+/```"SP` -M`&4\``!E/```6#P``%@\```B.P``(CL``!@[```8.P``,3P``#$\``"R.P`` -MLCL``,6\``#%O``@)[T`(">]``"KO```J[P``,<[``#'.P#`FSP`P)L\`$"J -M/`!`JCP`P,\\`,#//`#`X3P`P.$\``"%/```A3P```$\```!/```:#H``&@Z -M`("=O`"`G;P`0+>\`$"WO`"`![P`@`>\``#@N0``X+D``,R[``#,NP"`3+P` -M@$R\``!TNP``=+L``/,[``#S.P"`(SP`@",\`(!X/`"`>#P`@(P\`(",/`"` -M@SP`@(,\```W/```-SP``)P[``"<.P``OSL``+\[``"(.@``B#H``)R[``"< -MNP``,KL``#*[`(!QO`"`<;P`P,N\`,#+O`"`4;P`@%&\``#<.@``W#H``.4[ -M``#E.P"`"SP`@`L\`("Y/`"`N3P`@`H]`(`*/0!`L#P`0+`\``#4.@``U#H` -M@%Z\`(!>O`#`R;P`P,F\`$"BO`!`HKP``&J[``!JNP"`6CP`@%H\`(`4/`"` -M%#P``!J[```:NP``0#P``$`\`(#"/`"`PCP`@`$\`(`!/```%+P``!2\```B -MO```(KP```BZ```(N@``F#L``)@[``"7.P``ESL```@Z```(.@``\+L``/"[ -M``"\N@``O+H``&\\``!O/`#`CSP`P(\\``"+.P``BSL``$N\``!+O`"`0KP` -M@$*\```@.@``(#H``-PZ``#<.@``ISL``*<[```T/```-#P``#<\```W/``` -M-#P``#0\```O/```+SP``-PZ``#<.@``7[P``%^\`$"%O`!`A;P``!0[```4 -M.P"`4CP`@%(\```_/```/SP`@",\`(`C/```CSL``(\[``"9NP``F;L`@'.\ -M`(!SO`"`1;P`@$6\``",.P``C#L``)@[``"8.P``)CL``"8[`$"`/`!`@#P` -M@+<\`("W/```=CP``'8\``"K.P``JSL``"H[```J.P``=KL``':[`(!SO`"` -M<[P``#:\```VO```/+L``#R[``#]NP``_;L``.2[``#DNP``P#L``,`[`(#= -M/`"`W3P`(`0]`"`$/0"`@SP`@(,\``!(.P``2#L```"\````O```G;P``)V\ -M``!DO```9+P``,2[``#$NP``I+L``*2[```0.@``$#H`P(<\`,"'/`#`T#P` -MP-`\``!8/```6#P``"@Z```H.@``O+H``+RZ``!>NP``7KL``#8[```V.P`` -M.SP``#L\`(`2/`"`$CP``$B[``!(NP"`4KP`@%*\``!RNP``\`(!7O`!`V[P`0-N\``"MO```K;P``'([``!R.P"`F3P`@)D\`,#$/`#` -MQ#P``.,\``#C/`#`G3P`P)T\``"T.P``M#L``-2Z``#4N@``N;L``+F[`(!T -MO`"`=+P`P*.\`,"CO```8+L``&"[`(!1/`"`43P`@"H\`(`J/```^SL``/L[ -M```<.P``'#L``$:[``!&NP``*+L``"B[``!$.P``1#L`@!H\`(`:/```ISL` -M`*<[``"LN@``K+H```T\```-/```5SP``%<\``#X.P``^#L``/8[``#V.P"` -M(CP`@"(\``"4.@``E#H``'*\``!RO```7+P``%R\``!BNP``8KL```J\```* -MO`"``+P`@`"\``#$.P``Q#L``(X\``"./`"`H3P`@*$\`(`X/`"`.#P``!@[ -M```8.P``FKL``)J[``#]NP``_;L``,X[``#..P!`M3P`0+4\`(!Q/`"`<3P` -M`-"Z``#0N@``KKL``*Z[``!H.@``:#H`@`:\`(`&O`"`L+P`@+"\`,"CO`#` -MH[P```J\```*O```+3P``"T\`,#G/`#`YSP`P-H\`,#:/```;SP``&\\``#L -M.P``[#L`@%0\`(!4/```?3P``'T\``".NP``CKL`0-F\`$#9O```[;P``.V\ -M`$"2O`!`DKP``-.[``#3NP``&CL``!H[``!?/```7SP`0(X\`$"./```HCP` -M`*(\`,#[/`#`^SP``*X\``"N/```N+L``+B[`,")O`#`B;P``/"[``#PNP`` -M7CL``%X[``#`N0``P+D``/>[``#WNP``'+L``!R[``#'NP``Q[L``&J\``!J -MO`"`'+P`@!R\``!T.P``=#L``#L\```[/```6CP``%H\`("X/`"`N#P``-8\ -M``#6/```'#P``!P\```(.P``"#L``!P[```<.P"`';P`@!V\`$":O`!`FKP` -M`'.\``!SO```A;L``(6[`````````````,B[``#(NP``++L``"R[```@/``` -M(#P`P)(\`,"2/`!`GSP`0)\\`(`,/`"`##P``,J[``#*NP"`:;P`@&F\``"L -MNP``K+L`@!4\`(`5/```USL``-<[``"@.@``H#H`@#P\`(`\/`"`@CP`@((\ -M``#/.P``SSL``$J\``!*O`"`UKP`@-:\``#'O```Q[P`@#*\`(`RO```"3P` -M``D\`,"J/`#`JCP`@'X\`(!^/```!#P```0\``!:/```6CP``*L\``"K/``` -MZ#L``.@[``!-O```3;P`@!R\`(`\```^O```/KP` -M`)R[``"+H``.*[``#BNP``';P``!V\```,NP``#+L``"`Y```@ -M.0``@+L``("[``"/NP``C[L``,"X``#`N```4#L``%`[```8.P``&#L`@`X\ -M`(`./```<3P``'$\```H/```*#P``("Z``"`N@``/KP``#Z\`(!:O`"`6KP` -M`"J\```JO```P+L``,"[``"P.@``L#H``(J[``"*NP``,+P``#"\``"G.P`` -MISL`P,(\`,#"/`#`J3P`P*D\`(`)/`"`"3P```0\```$/`"`'CP`@!X\```P -MN@``,+H``'6\``!UO`#`U[P`P->\`,`"O0#``KT``*2\``"DO```?#L``'P[ -M`$#+/`!`RSP`@+4\`("U/```XSL``.,[`(`2/`"`$CP``),\``"3/```'3P` -M`!T\``#=NP``W;L``&.\``!CO```([P``".\``#FNP``YKL``+B[``"XNP`` -MV[L``-N[``!2O```4KP``-.[``#3NP``/CP``#X\``"F/```ICP`0(T\`$"- -M/```U#L``-0[``"P.@``L#H``'8[``!V.P``'KL``!Z[``"TNP``M+L``""[ -M```@NP``HKL``**[```&O```!KP``,&[``#!NP``J;L``*F[``#8NP``V+L` -M`/RZ``#\N@"`4SP`@%,\``!Z/```>CP`@`$\`(`!/`"`!SP`@`<\``#%.P`` -MQ3L``,F[``#)NP``C[P``(^\``!IO```:;P``+8[``"V.P``=#P``'0\```/ -M/```#SP``,4[``#%.P``/CL``#X[``"FNP``IKL``,J[``#*NP``)+L``"2[ -M`(`*O`"`"KP``(6\``"%O```HKL``**[``!#/```0SP``"L\```K/```$#L` -M`!`[```<.P``'#L``%\\``!?/`#`HSP`P*,\`(!//`"`3SP``*RZ``"LN@"` -M?;P`@'V\``"_O```O[P``'6\``!UO```>KL``'J[``!BNP``8KL``+F[``"Y -MNP``L3L``+$[`,",/`#`C#P``&@\``!H/`"`+SP`@"\\`(`J/`"`*CP``+P[ -M``"\.P``SCL``,X[``#4.P``U#L``#"[```PNP"`EKP`@):\`.`"O0#@`KT` -M`-^\``#?O```/;P``#V\``"SP``*0Z``"D.@!`B;P`0(F\`("< -MO`"`G+P`P)&\`,"1O`"`JKP`@*J\`,"JO`#`JKP`@'^\`(!_O```G+H``)RZ -M``!)/```23P`0+4\`$"U/`#`\#P`P/`\`$"N/`!`KCP``(4[``"%.P``,KL` -M`#*[``#`N@``P+H``':[``!VNP``"[P```N\``#!NP``P;L``(6[``"%NP`` -M_;L``/V[``")NP``B;L```2[```$NP``WKL``-Z[``"*NP``BKL```\\```/ -M/`#`D3P`P)$\`(!I/`"`:3P``/0Z``#T.@``I+L``*2[`(`$O`"`!+P`@`Z\ -M`(`.O```,+L``#"[``#\.P``_#L``-8[``#6.P``5KL``%:[``"8.@``F#H` -M@"H\`(`J/```8#H``&`Z`(`QO`"`,;P``"&\```AO```:KL``&J[``!@N0`` -M8+D``""Z```@N@``P#@``,`X``#`N```P+@``+PZ``"\.@``<#P``'`\`(#4 -M/`"`U#P`P(D\`,")/```N+L``+B[`$">O`!`GKP`P(2\`,"$O`#`DKP`P)*\ -M`$"FO`!`IKP``"R\```LO````#D````Y```^/```/CP`@-`\`(#0/```OCP` -M`+X\```P/```,#P``+@Z``"X.@``#CL```X[``#3.P``TSL```"Y````N0`` -M.+P``#B\`$"%O`!`A;P`P)6\`,"5O`"`C[P`@(^\``!+O```2[P``'"Z``!P -MN@``U3L``-4[`(`2/`"`$CP``)\\``"?/`#`O3P`P+T\```T/```-#P``)D[ -M``"9.P``P3L``,$[```8N@``&+H``$V\``!-O`"`G+P`@)R\`,""O`#`@KP` -M@(2\`("$O`#`E[P`P)>\`(`5O`"`%;P``%8\``!6/`!`ZCP`0.H\``#G/``` -MYSP`@*H\`("J/```!3P```4\`(`FO`"`)KP``(*\``""O```1KP``$:\`$"" -MO`!`@KP`0)N\`$";O```^[L``/N[`(`>/`"`'CP`@&\\`(!O/```O3L``+T[ -M``"DN@``I+H``+`Y``"P.0``"#P```@\``!-/```33P``*0[``"D.P``^;L` -M`/F[`$".O`!`CKP`@!F\`(`9O```V#L``-@[``!(.P``2#L``.:[``#FNP`` -M(#D``"`Y``#N.P``[CL``+8[``"V.P``D+D``)"Y``!PNP``<+L``*N[``"K -MNP``7+L``%R[``#*.P``RCL``!4\```5/```N[L``+N[`("1O`"`D;P``%B\ -M``!8O```U#H``-0Z```2/```$CP```P\```,/`#`C#P`P(P\``"J/```JCP` -M`,H[``#*.P``S+L``,R[``!9O```6;P`0*>\`$"GO```IKP``*:\`(!(O`"` -M2+P``#BZ```XN@``ZCL``.H[`(!#/`"`0SP``&L\``!K/```0SP``$,\``"R -M.P``LCL``-PZ``#<.@``@#H``(`Z```ZNP``.KL``(*\``""O`!`J;P`0*F\ -M```RO```,KP``,"X``#`N```)#P``"0\`,"5/`#`E3P`@&L\`(!K/```QCL` -M`,8[``"X.P``N#L``'0[``!T.P"`-KP`@#:\``"XO```N+P``'*\``!RO``` -M:#H``&@Z``#<.P``W#L``#BZ```XN@``U[L``->[``!\NP``?+L``%"Z``!0 -MN@``^SL``/L[`("A/`"`H3P``'X\``!^/```HCL``*([``"0.P``D#L``,D[ -M``#).P``WKL``-Z[`$#&O`!`QKP``-"\``#0O```;KP``&Z\``"DNP``I+L` -M`(([``"".P"`"3P`@`D\```^/```/CP`@'T\`(!]/`"`D3P`@)$\``"L/``` -MK#P``$<\``!'/`"`(+P`@""\``"KO```J[P``,2\``#$O`"`TKP`@-*\`,". -MO`#`CKP``!B[```8NP``)CP``"8\`,"//`#`CSP`0,,\`$##/`"`OCP`@+X\ -M`(`-/`"`#3P``,R[``#,NP``&[P``!N\```%O```!;P``)*[``"2NP``4+L` -M`%"[``#\N@``_+H``'J[``!ZNP``2[P``$N\```ZO```.KP```([```".P`` -MLSL``+,[```*.P``"CL``.$[``#A.P"`7#P`@%P\`(`P/`"`,#P``*$[``"A -M.P``!CL```8[```6NP``%KL``.>[``#GNP``Y[L``.>[``"#NP``@[L``(J[ -M``"*NP``*[P``"N\`(`;P``'F\``"`.0``@#D`@&@\`(!H/`#`A3P`P(4\``#&.P``QCL``&X[``!N -M.P"`:SP`@&L\``"`/```@#P``#8\```V/```+SP``"\\``#+.P``RSL``/>[ -M``#WNP!`I+P`0*2\`,#1O`#`T;P`@.R\`(#LO```Q[P``,>\``#*NP``RKL` -M`#L\```[/`#`LSP`P+,\`(#G/`"`YSP`@.4\`(#E/`#`ESP`P)<\```N.P`` -M+CL```N\```+O`"`&;P`@!F\```KO```*[P`P(F\`,")O```D;P``)&\```X -MO```.+P``$Z[``!.NP``"SP```L\`$"L/`!`K#P``'\\``!_/```*CL``"H[ -M``"H.@``J#H``%8[``!6.P``8+L``&"[`(!;O`"`6[P`@$^\`(!/O```=#L` -M`'0[`$"`/`!`@#P`@$H\`(!*/```E#H``)0Z`(`SO`"`,[P`0(&\`$"!O``` -M&KP``!J\```L.P``+#L``-<[``#7.P``X#D``.`Y``"_.P``OSL``(8\``"& -M/`"`*3P`@"D\``"\N@``O+H``!:\```6O`"`![P`@`>\``#L.@``[#H``.([ -M``#B.P``2#L``$@[``!(NP``2+L``!"\```0O```G;L``)V[``!X.@``>#H` -M`/RZ``#\N@``\+L``/"[``"RNP``LKL``*`Z``"@.@``4#L``%`[``#-.P`` -MS3L`0(H\`$"*/`"`H3P`@*$\``!T/```=#P`@#D\`(`Y/```B#H``(@Z`$"& -MO`!`AKP`P.V\`,#MO`!`[[P`0.^\``":O```FKP``.>[``#GNP``TSL``-,[ -M``"^/```OCP`P.,\`,#C/`"`<3P`@'$\``"&.P``ACL``-L[``#;.P``@3L` -M`($[``"RNP``LKL`@!6\`(`5O`"`$KP`@!*\`(`7O`"`%[P``+N[``"[NP`` -M-+L``#2[``!:NP``6KL``':[``!VNP``;#L``&P[`(!:/`"`6CP``&0\``!D -M/```KCL``*X[```PNP``,+L``(`Y``"`.0"``SP`@`,\``#-.P``S3L``&"Y -M``!@N0"`)KP`@":\`("AO`"`H;P``&.\``!CO```>+L``'B[``#NP``7KL``/RZ``#\N@``@#D` -M`(`Y``#G.P``YSL``'L\``![/```8#P``&`\```H.P``*#L``-F[``#9NP"` -M![P`@`>\``#YNP``^;L``(*[``""NP``F#L``)@[```,/```##P``'0[``!T -M.P``]#H``/0Z``#`.```P#@``+^[``"_NP"`![P`@`>\```BNP``(KL``%([ -M``!2.P``VCL``-H[```D/```)#P```X\```./````+D```"Y`(`NO`"`+KP` -M`%:\``!6O```Y+H``.2Z``#;.P``VSL``(@[``"(.P``R3L``,D[```;/``` -M&SP``,,[``##.P``JSL``*L[``"Q.P``L3L``!"[```0NP``8KP``&*\`,"$ -MO`#`A+P``.6[``#ENP``\`(`6O`"`%KP``*&[``"ANP``K[L``*^[```<.P``'#L``$`\``!`/`"` -M;3P`@&T\`(`-/`"`#3P``(0Z``"$.@``'+L``!R[```@.@``(#H```4\```% -M/````SP```,\``!NNP``;KL``%^\``!?O```3KP``$Z\``"1NP``D;L``!R[ -M```CL` -M`'H[``!TNP``=+L``#R\```\O```(;P``"&\``#@N0``X+D```L\```+/``` -M7#L``%P[``!8NP``6+L``$`Z``!`.@``WCL``-X[``!7/```5SP`@(,\`("# -M/```NCL``+H[``#KNP``Z[L`@`.\`(`#O```"KL```J[``"WNP``M[L`@&>\ -M`(!GO```&+P``!B\``!<.P``7#L``!P\```\``"7O```#KL```Z[`(`B/`"`(CP``#D\```Y/``` -M43P``%$\``!4/```5#P``-8[``#6.P``/CL``#X[``!$.P``1#L``/`Y``#P -M.0``\+D``/"Y``!`.0``0#D``,@Z``#(.@``>KL``'J[`,"*O`#`BKP`@**\ -M`("BO```O+L``+R[``#P.P``\#L``"P\```L/`"`(3P`@"$\```N/```+CP` -M`.@[``#H.P``'CL``!X[``!*.P``2CL``,2Z``#$N@"`&;P`@!F\``#8NP`` -MV+L``&"Y``!@N0``N#H``+@Z``!XNP``>+L`@">\`(`GO```H[L``*.[``"J -M.P``JCL``/D[``#Y.P``F3L``)D[````NP```+L``/^[``#_NP``=+L``'2[ -M``"[.P``NSL`@"P\`(`L/```$#P``!`\```O`"`'KP`@)6\`("5O```N[P``+N\``"-O```C;P``)V[``"=NP`` -MP#L``,`[`(`@/`"`(#P``-P[``#<.P``Q#L``,0[```^/```/CP``#@\```X -M/```ICL``*8[``">.P``GCL``(D[``").P``KKL``*Z[`(!$O`"`1+P`@$"\ -M`(!`O```4+P``%"\`(!*O`"`2KP``->[``#7NP``V#H``-@Z``"V.P``MCL` -M`+4[``"U.P``SSL``,\[```#/````SP``.<[``#G.P``SSL``,\[``#@.P`` -MX#L``(<[``"'.P``"[P```N\`,"GO`#`I[P`P*&\`,"AO`"`8[P`@&.\``"9 -MNP``F;L`@`8\`(`&/`"`3CP`@$X\`(!+/`"`2SP``&X\``!N/`"`-CP`@#8\ -M``#0.0``T#D`@$>\`(!'O`#`A;P`P(6\`(``O`"``+P``/"Y``#PN0``'KL` -M`!Z[``#FNP``YKL``/>[``#WNP``-+L``#2[``"".P``@CL``#X\```^/``` -M)SP``"<\``!HN@``:+H``"R[```LNP``@CL``(([``"PN0``L+D``"*\```B -MO`"`1KP`@$:\``"5NP``E;L``*`[``"@.P``G3L``)T[``#XN@``^+H``+>[ -M``"WNP``P;L``,&[``"(N@``B+H``/\[``#_.P``%SP``!<\``#@.0``X#D` -M`+R[``"\NP``O+L``+R[```,O```#+P``$V\``!-O`"`%KP`@!:\``#`.``` -MP#@`@!D\`(`9/`"`\``!7O```4KP``%*\``#QNP``\;L``!2[```4NP`` -MD#D``)`Y``#T.P``]#L`@'\\`(!_/```6CP``%H\``"`N```@+@`@$R\`(!, -MO`"`%[P`@!>\````.````#@``'"Z``!PN@``H[L``*.[``#INP``Z;L`@"F\ -M`(`IO```V;L``-F[``!@N@``8+H``)"Y``"0N0``"#H```@Z``#@.P``X#L` -M`'P\``!\/`"`A#P`@(0\``!:.P``6CL``$*\``!"O`"`DKP`@)*\``!YO``` -M>;P`@#2\`(`TO```#+P```R\``#KNP``Z[L``+B[``"XNP``-#L``#0[``"$ -M/```A#P``)@\``"8/`"`2#P`@$@\```E/```)3P``-([``#2.P``J+L``*B[ -M``".O```CKP`@,N\`(#+O```V+P``-B\`("\`("'O`"`7;P`@%V\``!P.@``<#H``'T\``!]/`"`C3P`@(T\ -M```@/```(#P``"H[```J.P``X;L``.&[``!5O```5;P``#*\```RO`"`2;P` -M@$F\`$"*O`!`BKP`@#J\`(`ZO```I#H``*0Z`(`&/`"`!CP``/$[``#Q.P`` -MQSL``,<[`(`F/`"`)CP``'$\``!Q/`"`1#P`@$0\``#@.@``X#H`@!J\`(`: -MO```E+P``)2\``"EO```I;P``'2\``!TO```';P``!V\`(`2O`"`$KP``,>[ -M``#'NP``$#H``!`Z``").P``B3L``.L[``#K.P``93P``&4\`,"3/`#`DSP` -MP)4\`,"5/`"`?3P`@'T\``"#.P``@SL`@$*\`(!"O`"`S+P`@,R\`"`&O0`@ -M!KT`P/.\`,#SO`!`G[P`0)^\``##NP``P[L``$@\``!(/`#`PCP`P,(\``"K -M/```JSP``%P\``!\`$"WO`"`R;P` -M@,F\``!XO```>+P``-`Y``#0.0``X3L``.$[`(`//`"`#SP``"(\```B/``` -M$3P``!$\`(`%/`"`!3P``)T[``"=.P``"+H```BZ``!XN@``>+H``$BZ``!( -MN@``)+L``"2[``#FNP``YKL`@'6\`(!UO`#`B+P`P(B\``#BNP``XKL``(\[ -M``"/.P``^SL``/L[``#!.P``P3L``.X[``#N.P``(CP``"(\`(!%/`"`13P` -M`%,\``!3/```FSL``)L[`(``O`"``+P``%2\``!4O```8KP``&*\``"(O``` -MB+P`@**\`("BO```A[P``(>\``#`.@``P#H``+8\``"V/`!`Z#P`0.@\`("Q -M/`"`L3P``"T\```M/```3+L``$R[``!1O```4;P``'"\``!PO```<;P``'&\ -M``!BO```8KP``,.[``##NP``R#L``,@[``!0/```4#P``"@\```H/```DSL` -M`),[````.P```#L``$0[``!$.P``_#H``/PZ``!NP"`+KP`@"Z\```YO```.;P`@$>\`(!'O```*+P``"B\``#1 -MNP``T;L``-@Z``#8.@``0#P``$`\`(!U/`"`=3P`@$<\`(!'/```NCL``+H[ -M``"LN@``K+H``(.[``"#NP``KKL``*Z[``!6O```5KP`P+"\`,"PO`#`E[P` -MP)>\``!JNP``:KL`@#L\`(`[/`#`H#P`P*`\`("9/`"`F3P``#@\```X/``` -MKCL``*X[```@.@``(#H``!:\```6O`#`DKP`P)*\`,"-O`#`C;P``!2\```4 -MO```P#@``,`X```8.P``&#L```Z[```.NP``>KL``'J[``!8N@``6+H``)@[ -M``"8.P``73P``%T\`("+/`"`BSP`@#(\`(`R/```KSL``*\[```:.P``&CL` -M`,Z[``#.NP``E+P``)2\`$#`O`!`P+P`P)R\`,"\`,"3O`#`D[P`@-*\`(#2O`!`W+P`0-R\ -M``!QO```<;P``%@Z``!8.@``,#P``#`\``!?/```7SP``#,\```S/```\CL` -M`/([``!V.P``=CL``'BZ``!XN@``LKL``+*[``"_NP``O[L``%"Z``!0N@`` -MB#L``(@[```F.P``)CL``+6[``"UNP``4[P``%.\```-O```#;P``,0Z``#$ -M.@``;#L``&P[``"4.@``E#H``#`Z```P.@``C#H``(PZ``"$.P``A#L``-$[ -M``#1.P``)#L``"0[``!`NP``0+L``(.[``"#NP``G+H``)RZ```8.@``&#H` -M`)F[``"9NP"`-KP`@#:\``#$NP``Q+L``-$[``#1.P"`)SP`@"<\``!B.P`` -M8CL``+B[``"XNP"`1;P`@$6\``!0O```4+P``-^[``#?NP``2#H``$@Z``#E -M.P``Y3L`0(,\`$"#/`!`OCP`0+X\``"E/```I3P```0\```$/```!KP```:\ -M`,"LO`#`K+P`0,*\`$#"O```L;P``+&\`("3O`"`D[P``"^\```OO```3+L` -M`$R[``#'.P``QSL`0(L\`$"+/`#`L#P`P+`\`$"H/`!`J#P`@)L\`(";/``` -M7SP``%\\```0.@``$#H``&N\``!KO```J[P``*N\`,"?O`#`G[P`P(.\`,"# -MO```-KP``#:\``"CNP``H[L``-@Z``#8.@``SCL``,X[``"[.P``NSL``-H[ -M``#:.P"`&3P`@!D\```G/```)SP`@&P\`(!L/`"`B#P`@(@\``"0.P``D#L` -M@%Z\`(!>O`"`N[P`@+N\``"TO```M+P``(6\``"%O`"`);P`@"6\```XNP`` -M.+L``/\[``#_.P#`E#P`P)0\`,"M/`#`K3P`@'`\`(!P/```BCL``(H[``!T -MNP``=+L``*J[``"JNP``.+L``#B[``"VNP``MKL`@%&\`(!1O```6;P``%F\ -M``"VNP``MKL``&H[``!J.P"`)3P`@"4\`(`./`"`#CP``/@Z``#X.@``<+H` -M`'"Z``"8N@``F+H``)Z[``">NP```KP```*\``#,NP``S+L``#H[```Z.P`` -M9#P``&0\`(!R/`"`[``"UNP``M;L``*:[``"FNP``H+H``*"Z`(`9 -M/`"`&3P`P*4\`,"E/`#`L#P`P+`\``!F/```9CP``$`[``!`.P``#+P```R\ -M`,"#O`#`@[P`0**\`$"BO`!`J+P`0*B\``!GO```9[P``."Z``#@N@``0#P` -M`$`\``"E/```I3P`@'<\`(!W/```ACL``(8[``!4.P``5#L`@`4\`(`%/``` -MSCL``,X[``"`N@``@+H``/2[``#TNP``++P``"R\```3O```$[P``-J[``#: -MNP``Q+L``,2[``"7NP``E[L``*`Y``"@.0"`!3P`@`4\``!C/```8SP`@"T\ -M`(`M/```<#H``'`Z``!.NP``3KL``/@Z``#X.@``D#L``)`[```8N@``&+H` -M`,Z[``#.NP``++P``"R\```LO```++P``(*[``""NP``"#L```@[``#I.P`` -MZ3L``&0\``!D/```C3P``(T\``!I/```:3P``(P[``",.P``(+P``""\`,"4 -MO`#`E+P`@)2\`("4O```6;P``%F\``"4NP``E+L``)0[``"4.P``(CP``"(\ -M`(`Y/`"`.3P`@#@\`(`X/```!CP```8\```4.P``%#L``)PZ``"<.@``*#L` -M`"@[```LNP``++L`@`Z\`(`.O```#KP```Z\``"-NP``C;L``+PZ``"\.@`` -M7CL``%X[```J.P``*CL``)@[``"8.P``J3L``*D[``"`.@``@#H``&2[``!D -MNP``TKL``-*[``#(NP``R+L``,0Z``#$.@"`,#P`@#`\```E/```)3P``#@Z -M```X.@``LKL``+*[``!NNP``;KL``%@Z``!8.@``-#L``#0[``"T.@``M#H` -M`,PZ``#,.@``E#L``)0[``"-.P``C3L``/"Z``#PN@``'KP``!Z\``!@O``` -M8+P``."[``#@NP``]SL``/<[``!;/```6SP``!L\```;/```XCL``.([``"\ -M.P``O#L``(8[``"&.P```#L````[``"%NP``A;L`@#B\`(`XO`"`5[P`@%>\ -M```>O```'KP``*B[``"HNP``0+L``$"[``!H.@``:#H`@"\\`(`O/```KSP` -M`*\\``"]/```O3P``%D\``!9/````CL```([``#0NP``T+L``&2\``!DO`"` -M>[P`@'N\`(`KO`"`*[P``.2[``#DNP``/KL``#Z[``"D.@``I#H``,PZ``#, -M.@``I#H``*0Z```R.P``,CL``.D[``#I.P``8CP``&(\`$"`/`!`@#P``#H\ -M```Z/```NSL``+L[``"`N0``@+D`@`R\`(`,O```AKP``(:\`("0O`"`D+P` -M`$R\``!,O```B;L``(F[``!>.P``7CL``*$[``"A.P``D#L``)`[```F/``` -M)CP`P(@\`,"(/`!`A3P`0(4\`(`4/`"`%#P``&"Z``!@N@```KP```*\`(`J -MO`"`*KP`@%2\`(!4O`"`7[P`@%^\```+O```"[P``""Z```@N@"`#3P`@`T\ -M`(!^/`"`?CP``"4\```E/```2KL``$J[``#CNP``X[L``(`X``"`.```NCL` -M`+H[``"P.P``L#L``(4[``"%.P``BCL``(H[``!J.P``:CL``(0Z``"$.@`` -MC;L``(V[```>O```'KP`@"Z\`(`NO```X+L``."[````N0```+D``-PZ``#< -M.@``BKL``(J[``":NP``FKL``'X[``!^.P"`+3P`@"T\`(!6/`"`5CP`@#T\ -M`(`]/```Y#L``.0[``"#.P``@SL``+@Z``"X.@``8KL``&*[````O````+P` -M`!"\```0O```\;L``/&[``#KNP``Z[L``!N\```;O```0+P``$"\`(`5O`"` -M%;P``%`Z``!0.@``.#P``#@\`$"5/`!`E3P`@,4\`(#%/`!`N#P`0+@\`(`Y -M/`"`.3P``-BZ``#8N@"`:;P`@&F\`("]O`"`O;P`0+2\`$"TO```8+P``&"\ -M``"ONP``K[L``)"Y``"0N0``5CL``%8[`(`@/`"`(#P``(0\``"$/```;SP` -M`&\\```#/````SP``*([``"B.P``;#L``&P[``#DN@``Y+H``!.\```3O``` -M#H``'@Z```0N@``$+H``!J[```:NP``8+H``&"Z``!X -M.P``>#L``+L[``"[.P``5CL``%8[```4NP``%+L``)*[``"2NP``$#H``!`Z -M``",.@``C#H``+^[``"_NP"`&;P`@!F\```&O```!KP``,6[``#%NP``K+H` -M`*RZ``"\.P``O#L``$T\``!-/```?SP``'\\``!./```3CP``)@[``"8.P`` -MH[L``*.[```\O```/+P`@$>\`(!'O````+P```"\``!HNP``:+L```*[```" -MNP``E#H``)0Z``"6.P``ECL```@[```(.P``V+H``-BZ``#0N0``T+D``%H[ -M``!:.P"`#CP`@`X\`(!*/`"`2CP``-<[``#7.P``B+L``(B[`(`-O`"`#;P` -M`.V[``#MNP``S;L``,V[```)O```";P`@"J\`(`JO```Q;L``,6[```B.P`` -M(CL``/`[``#P.P``"SP```L\```0/```$#P``!@\```8/```*#P``"@\```2 -M/```$CP``*BZ``"HN@``CKP``(Z\`(#&O`"`QKP`0(2\`$"$O```@[L``(.[ -M``"=.P``G3L``!L\```;/`"`/SP`@#\\``!\`,"! -MO`#`@;P`@)V\`("=O`#`CKP`P(Z\```JO```*KP``'2[``!TNP``-#L``#0[ -M```I/```*3P`P(0\`,"$/`#`F#P`P)@\`$"!/`!`@3P``*P[``"L.P"`![P` -M@`>\`("3O`"`D[P``*6\``"EO`"`=[P`@'>\`(`>O`"`'KP``-^[``#?NP`` -MB+H``(BZ``"G.P``ISL``&@[``!H.P``U+H``-2Z``"%NP``A;L```R[```, -MNP``H#L``*`[```]/```/3P``!$\```1/```U+H``-2Z`(`@O`"`(+P`@"Z\ -M`(`NO```&;P``!F\`(`^O`"`/KP`@&&\`(!AO```$[P``!.\``!(.@``2#H` -M`+<[``"W.P``?#L``'P[```".P```CL``&`Z``!@.@``W#H``-PZ``"L.P`` -MK#L``!@[```8.P```KP```*\`(!1O`"`4;P```N\```+O```4+H``%"Z``!N -M.P``;CL``.0Z``#D.@``\+H``/"Z``"1NP``D;L`@!"\`(`0O```=KP``':\ -M`$"5O`!`E;P`@&B\`(!HO```#+L```R[`(!6/`"`5CP``+`\``"P/`"`DCP` -M@)(\`(`E/`"`)3P``&@[``!H.P``M[L``+>[``!LO```;+P`P)6\`,"5O`"` -MG+P`@)R\``"*O```BKP`@$*\`(!"O```YKL``.:[``#XN@``^+H``(H[``"* -M.P``-3P``#4\`,"(/`#`B#P``&\\``!O/```HSL``*,[```"NP```KL``*F[ -M``"INP"`.+P`@#B\`("+O`"`B[P``'2\``!TO`"`%[P`@!>\``#=NP``W;L` -M`-N[``#;NP``U;L``-6[``"5NP``E;L``"P[```L.P"`6CP`@%H\`,"B/`#` -MHCP`P(P\`,",/```USL``-<[``"/NP``C[L``#N\```[O`"`IKP`@*:\`(#I -MO`"`Z;P`0,Z\`$#.O`"`2KP`@$J\``#(.@``R#H`@#\\`(`_/`"`4#P`@%`\ -M``#S.P``\SL``*8[``"F.P``WCL``-X[``"G.P``ISL``$:[``!&NP"`+KP` -M@"Z\``!!O```0;P```B\```(O```K+L``*R[``"0NP``D+L``$B[``!(NP`` -M``````````#,.@``S#H``*@Z``"H.@``5KL``%:[``!.O```3KP``&J\``!J -MO```@[L``(.[``"\.P``O#L`@`<\`(`'/```XSL``.,[``"*.P``BCL``!`Z -M```0.@``C;L``(V[`(`-O`"`#;P`@!.\`(`3O```O+L``+R[``#$N@``Q+H` -M`-@Z``#8.@``(#H``"`Z``#!NP``P;L`@#2\`(`TO`"`";P`@`F\``"(NP`` -MB+L``":[```FNP``M#H``+0Z``#A.P``X3L``/P[``#\.P``K3L``*T[``#D -M.@``Y#H``!R[```/`"`7CP``'`[``!P.P``)KP``":\`,"BO`#`HKP`@+J\`("ZO`!` -MC;P`0(V\``"NNP``KKL``#X[```^.P``%CL``!8[``!`.0``0#D``#0[```T -M.P``ZCL``.H[``#<.P``W#L``,`X``#`.```'KL``!Z[``"(.@``B#H``,"X -M``#`N```W+L``-R[`(!6O`"`5KP`0(B\`$"(O```3[P``$^\``#\N@``_+H` -M`*@[``"H.P``\``"GO`"`@+P` -M@("\`(`"O`"``KP``!BZ```8N@``@3L``($[``"I.P``J3L``(L[``"+.P`` -MB3L``(D[``"I.P``J3L``!`[```0.P``J[L``*N[```.O```#KP``,R[``#, -MNP``T;L``-&[`(`4O`"`%+P``,N[``#+NP``.#H``#@Z``"H.P``J#L``*0[ -M``"D.P``$+H``!"Z``#CNP``X[L``.*[``#BNP``T+H``-"Z``!>.P``7CL` -M`"H[```J.P``4KL``%*[``"VNP``MKL``'B[``!XNP``Q;L``,6[```LO``` -M++P`@!"\`(`0O```"+H```BZ```&/```!CP``$(\``!"/```XSL``.,[``"P -MNP``L+L``%^\``!?O`"`/+P`@#R\``#?NP``W[L``+"[``"PNP``AKL``(:[ -M``"L.@``K#H``!H\```:/`"`/SP`@#\\``#8.P``V#L``,`Z``#`.@``-KL` -M`#:[``#XNP``^+L``".\```CO```8;P``&&\`("GO`"`I[P`P*>\`,"GO``` -M";P```F\``"].P``O3L`@&`\`(!@/`!`BSP`0(L\`,"//`#`CSP`@%4\`(!5 -M/```@CL``(([``#"NP``PKL`@$2\`(!$O`"`4+P`@%"\`(`VO`"`-KP``!B\ -M```8O```$[P``!.\``!`O```0+P`@$:\`(!&O```SKL``,Z[``!@.0``8#D` -M`+0[``"T.P"`6#P`@%@\``";/```FSP``(P\``",/```#SP```\\```\NP`` -M/+L``%.\``!3O`#`C;P`P(V\`("-O`"`C;P``(*\``""O```\`(!WO```?;P``'V\`(`_O`"`/[P`@#2\ -M`(`TO`"`4+P`@%"\```EO```);P``#R[```\NP``0#H``$`Z``#`N0``P+D` -M`(PZ``",.@``R#L``,@[`(`]/`"`/3P`@'X\`(!^/```)#P``"0\``"KNP`` -MJ[L`@(F\`(")O`#`CKP`P(Z\``!6O```5KP``">\```GO`"``KP`@`*\``#, -MN@``S+H``-X[``#>.P"`#SP`@`\\``!D.P``9#L``!Z[```>NP``MKL``+:[ -M``#(NP``R+L``(2[``"$NP``/KL``#Z[``#2NP``TKL```6\```%O```-+L` -M`#2[``!P.P``<#L``*D[``"I.P``7#L``%P[``"$.@``A#H``.RZ``#LN@`` -MU+L``-2[`(!(O`"`2+P``%R\``!\`(`KO`"`*[P``.2[``#DNP``%#L``!0[ -M`(!P/`"`<#P`0*L\`$"K/```>CP``'H\``!..P``3CL``-:[``#6NP``1[P` -M`$>\`,"+O`#`B[P`@*J\`("JO`#`B[P`P(N\``"NNP``KKL``,`[``#`.P"` -M.#P`@#@\```C/```(SP``$P[``!,.P``D+H``)"Z``"T.@``M#H``!`[```0 -M.P``1+L``$2[`(`*O`"`"KP``":\```FO```';P``!V\`(`=O`"`';P``!2\ -M```4O```E+L``)2[```P.P``,#L`@`$\`(`!/`"`*#P`@"@\`(`8/`"`&#P` -M`'0[``!T.P```+L```"[```JNP``*KL``%:[``!6NP``$;P``!&\`(!4O`"` -M5+P`@$2\`(!$O`"`*[P`@"N\```1O```$;P``$2[``!$NP``TSL``-,[``!T -M/```=#P`P(X\`,"./```3#P``$P\```T.P``-#L`@`F\`(`)O```>[P``'N\ -M`(!NO`"`;KP`@$*\`(!"O`"`&KP`@!J\```NP``<+L``'"[``#1NP``T;L`@`6\ -M`(`%O```BKL``(J[``"I.P``J3L`@'(\`(!R/`"`DSP`@),\``!F/```9CP` -M`"P\```L/`"`##P`@`P\```8.@``&#H`@$J\`(!*O`"`G+P`@)R\``"`O``` -M@+P```R\```,O```.+L``#B[``#0.0``T#D``-0Z``#4.@``OSL``+\[``!H -M/```:#P``)@\``"8/`"`6CP`@%H\``!F.P``9CL``%"[``!0NP``D;L``)&[ -M``"]NP``O;L```&\```!O```I+L``*2[```..P``#CL``/<[``#W.P"`#CP` -M@`X\``"!.P``@3L``,^[``#/NP``3KP``$Z\`(`(O`"`"+P``#BZ```XN@`` -M7CL``%X[``#?.P``WSL``$`\``!`/```8SP``&,\`(`K/`"`*SP``)8[``"6 -M.P``(#H``"`Z``"@N@``H+H``#J[```ZNP``E+L``)2[``#=NP``W;L`@"B\ -M`(`HO```*;P``"F\``!4NP``5+L``'8[``!V.P``RSL``,L[```=/```'3P` -M`&L\``!K/```:3P``&D\`(`*/`"`"CP```@[```(.P``@+D``("Y```0.@`` -M$#H``""Z```@N@``M+L``+2[`(!$O`"`1+P``(*\``""O`"`4[P`@%.\``!J -MNP``:KL``+@[``"X.P``0#P``$`\`(":/`"`FCP`0,X\`$#./`!`L3P`0+$\ -M```,/```##P``*F[``"INP"`:[P`@&N\`("#O`"`@[P`@#*\`(`RO```O[L` -M`+^[``"5NP``E;L``%"[``!0NP``/#L``#P[`(`J/`"`*CP`@$`\`(!`/``` -M]#L``/0[``##.P``PSL````\````/```S3L``,T[``!`.@``0#H``(.[``"# -MNP``Q;L``,6[``"+NP``B[L``/@Z``#X.@``KSL``*\[``!X.@``>#H``)J[ -M``":NP``A;L``(6[``#0.0``T#D``&8[``!F.P``I#L``*0[``#^.P``_CL` -M`"\\```O/```##P```P\``"T.@``M#H``+6[``"UNP"`(KP`@"*\`(`4O`"` -M%+P``,RZ``#,N@``RCL``,H[``#Z.P``^CL``.0[``#D.P``YCL``.8[``"6 -M.P``ECL``&BZ``!HN@``@+L``("[```DNP``)+L``(`X``"`.```S#H``,PZ -M```..P``#CL``!H[```:.P``/#L``#P[``"@.P``H#L``/P[``#\.P``SCL` -M`,X[``"LN@``K+H``-N[``#;NP``P[L``,.[``#(NP``R+L``/"[``#PNP`` -M,+L``#"[``#K.P``ZSL`@'@\`(!X/`#`A3P`P(4\`(`Q/`"`,3P```H[```* -M.P``C;L``(V[``"=NP``G;L``,BZ``#(N@``:+H``&BZ``!2NP``4KL``%*[ -M``!2NP``X#D``.`Y``!P.@``<#H``$Z[``!.NP``?+L``'R[``#0.@``T#H` -M`!`\```0/```8SP``&,\`(!)/`"`23P``'P[``!\.P``4KL``%*[``!RNP`` -M[``"WNP``+CL` -M`"X[```A/```(3P`@(P\`(",/`"`HCP`@*(\``!M/```;3P``)$[``"1.P`` -M9+L``&2[``#>NP``WKL``/6[``#UNP``O+L``+R[``!ZNP``>KL``)R[``"< -MNP``A;L``(6[``"0.@``D#H``)4[``"5.P``@SL``(,[``"C.P``HSL`@`T\ -M`(`-/```$SP``!,\``"0.P``D#L``$BZ``!(N@``6+L``%B[```,NP``#+L` -M`,`Z``#`.@``#CL```X[```$NP``!+L``.6[``#ENP``S+L``,R[````N0`` -M`+D``&([``!B.P``5#L``%0[``#0.P``T#L`@$$\`(!!/`"`/#P`@#P\``"+ -M.P``BSL``*.[``"CNP"`++P`@"R\`(`*O`"`"KP``(`X``"`.```X#L``.`[ -M``#(.P``R#L```([```".P``:#H``&@Z``#`.@``P#H``-RZ``#CL``'H[``!(.@``2#H``#J[```ZNP``@;L``(&[``#PNP`` -M\+L`@$B\`(!(O```-;P``#6\```ZNP``.KL``/<[``#W.P``=CP``'8\`$"1 -M/`!`D3P`P($\`,"!/`"`+CP`@"X\``"J.P``JCL``(`Z``"`.@``O[L``+^[ -M``!KO```:[P``(F\``")O```1[P``$>\``#_NP``_[L``*B[``"HNP``P#H` -M`,`Z``!'/```1SP`P*D\`,"I/`!`O#P`0+P\`("+/`"`BSP``*<[``"G.P`` -MK[L``*^[`(`/O`"`#[P``.^[``#ONP``V+L``-B[``#VNP``]KL``*2[``"D -MNP``*#H``"@Z``#\.@``_#H``.BZ``#HN@``9KL``&:[``"0N0``D+D``,([ -M``#".P``3#P``$P\``!C/```8SP``/T[``#].P``6#H``%@Z``!`N0``0+D` -M`(@Z``"(.@``F+H``)BZ``",NP``C+L``&"[``!@NP``(+H``""Z````N@`` -M`+H``!:[```6NP``0+L``$"[```.NP``#KL``,"X``#`N```ACL``(8[``#, -M.P``S#L``!P[```<.P``"+H```BZ``!>.P``7CL```H\```*/`"`'3P`@!T\ -M`(`0/`"`$#P``.@[``#H.P``]#H``/0Z``#JNP``ZKL``(&\``"!O`!`FKP` -M0)J\``!TO```=+P``)2[``"4NP"`"CP`@`H\`,"-/`#`C3P`@)H\`(":/`"` -MB3P`@(D\``!F/```9CP``.0[``#D.P``A;L``(6[```NO```+KP``"F\```I -MO```%;P``!6\```9O```&;P`@!:\`(`6O```S[L``,^[```H.@``*#H``",\ -M```C/```B#P``(@\``""/```@CP``!T\```=/```NCL``+H[``"F.P``ICL` -M`)`Z``"0.@``R[L``,N[`(`)O`"`";P``)Z[``">NP``)KL``":[``"$NP`` -MA+L``,^[``#/NP``^;L``/F[``")NP``B;L``-$[``#1.P!`B3P`0(D\``"2 -M/```DCP`@$`\`(!`/```V#L``-@[``!0.P``4#L``%J[``!:NP``/+P``#R\ -M``!:O```6KP``/*[``#RNP```#@````X``"U.P``M3L``.L[``#K.P``C3L` -M`(T[```>.P``'CL``*P[``"L.P``XSL``.,[```P.P``,#L```J[```*NP`` -M'KL``!Z[``"(N@``B+H``*BZ``"HN@``&+H``!BZ```D.P``)#L``,([``#" -M.P``Z#L``.@[``"C.P``HSL``("X``"`N```OKL``+Z[``#HNP``Z+L``."Z -M``#@N@``:CL``&H[``"3.P``DSL``+L[``"[.P"``CP`@`(\``"_.P``OSL` -M`!BZ```8N@``D[L``).[```,NP``#+L``%8[``!6.P``XSL``.,[``#=.P`` -MW3L``!8[```6.P``?KL``'Z[``#3NP``T[L``)B[``"8NP``;+L``&R[``"* -MNP``BKL``$"Y``!`N0"`%3P`@!4\``!A/```83P`@"P\`(`L/```SCL``,X[ -M``"8.P``F#L``'P[``!\.P``$#L``!`[```RNP``,KL`@"^\`(`OO`"`>;P` -M@'F\`(`IO`"`*;P``(`Y``"`.0``^SL``/L[```P/```,#P`@&L\`(!K/``` -MASP``(<\`(`R/`"`,CP``-"Y``#0N0"`)+P`@"2\``!LO```;+P`@$6\`(!% -MO```8+L``&"[``";.P``FSL``,8[``#&.P``H3L``*$[``#_.P``_SL`@"\\ -M`(`O/```#SP```\\``";.P``FSL``&([``!B.P``2#L``$@[``!0N@``4+H` -M`.6[``#ENP``,;P``#&\`(`YO`"`.;P`@`.\`(`#O```H+D``*"Y``#;.P`` -MVSL```T\```-/```&#P``!@\``!%/```13P`@%`\`(!0/```"SP```L\``"! -M.P``@3L``.@Z``#H.@``^+H``/BZ```+O```"[P``&6\``!EO`"`#H``%BZ``!8N@``N+H``+BZ``!.NP``3KL` -M@`N\`(`+O`"`:KP`@&J\``!JO```:KP`@`:\`(`&O```(KL``"*[````.P`` -M`#L``"<\```G/`!`ICP`0*8\`$#`/`!`P#P`@)$\`("1/```!#P```0\``"L -MN@``K+H``/6[``#UNP``%+P``!2\`(`@O`"`(+P`@%2\`(!4O`"`:;P`@&F\ -M``#PNP``\+L``'8[``!V.P``)CP``"8\`(`U/`"`-3P``$4\``!%/`"`4#P` -M@%`\```,/```##P```"Y````N0``]KL``/:[`(`KO`"`*[P``/"[``#PNP`` -M_#H``/PZ```A/```(3P``!8\```6/```ACL``(8[``"8.@``F#H``%BZ``!8 -MN@``LKL``+*[```1O```$;P``,6[``#%NP``0#D``$`Y``"#.P``@SL``*T[ -M``"M.P``LCL``+([``"9.P``F3L``)X[``">.P``[#L``.P[``#\.P``_#L` -M`"@[```H.P``7+L``%R[``"GNP``I[L``-B[``#8NP"`'+P`@!R\`(`,O`"` -M#+P```2[```$NP``FSL``)L[```%/```!3P`@`X\`(`./```_SL``/\[``#: -M.P``VCL`@`(\`(`"/```)3P``"4\``#<.P``W#L``!2[```4NP``%+P``!2\ -M```]O```/;P`@'6\`(!UO`#`E+P`P)2\``!5O```5;P``#@Z```X.@``;CP` -M`&X\`$"Y/`!`N3P``,$\``#!/`!`BSP`0(L\``#S.P``\SL``(@Z``"(.@`` -M0KL``$*[`(`8O`"`&+P``'R\``!\O`"`:[P`@&N\``#TNP``]+L``$"[``!` -MNP``!KL```:[``"P.@``L#H``/@[``#X.P``23P``$D\`(!B/`"`8CP``#4\ -M```U/```6CL``%H[``!PNP``<+L``'R[``!\NP```+H```"Z``"4N@``E+H` -M`(:[``"&NP``:+L``&B[``#$N@``Q+H``!*[```2NP``8KL``&*[``"LN@`` -MK+H``'([``!R.P``$3P``!$\`(!./`"`3CP`@#8\`(`V/```'CL``!X[``#5 -MNP``U;L`@`V\`(`-O```_KL``/Z[``#VNP``]KL``(>[``"'NP``=CL``'8[ -M```;/```&SP```T\```-/```L#L``+`[``!..P``3CL``$`Z``!`.@``R+H` -M`,BZ```>NP``'KL``(6[``"%NP``Y+L``.2[``#1NP``T;L``*"Y``"@N0`` -MK#L``*P[``#0.P``T#L``/@[``#X.P``*#P``"@\``#_.P``_SL``'BZ``!X -MN@``&+P``!B\``!*O```2KP`@">\`(`GO```>+L``'B[``!:.P``6CL``+4[ -M``"U.P``3#L``$P[```\.P``/#L``+$[``"Q.P``C3L``(T[``!`N0``0+D` -M``"Z````N@``6#L``%@[``!\.P``?#L``&"Z``!@N@``U[L``->[`(`JO`"` -M*KP`@"6\`(`EO```2KL``$J[``"W.P``MSL``/H[``#Z.P``MCL``+8[``#5 -M.P``U3L`@`,\`(`#/```8#L``&`[``"9NP``F;L```N\```+O`"``[P`@`.\ -M`(`%O`"`!;P```2\```$O```O[L``+^[```PNP``,+L``$8[``!&.P``43P` -M`%$\`$"B/`!`HCP`0(<\`$"'/```SCL``,X[```BNP``(KL`@"&\`(`AO`"` -ME+P`@)2\`$"RO`!`LKP`P(2\`,"$O```N;L``+F[``"#.P``@SL``"D\```I -M/`"`2#P`@$@\`(`?/`"`'SP``-H[``#:.P``PCL``,([``!D.P``9#L``$J[ -M``!*NP``_[L``/^[``#XNP``^+L`@`&\`(`!O`"`,+P`@#"\`(`FO`"`)KP` -M`&2[``!DNP``@#L``(`[````/````#P``!$\```1/```VCL``-H[```@.P`` -M(#L``(0Z``"$.@``(#L``"`[````.@```#H``,.[``##NP``&[P``!N\`(`# -MO`"``[P`@`:\`(`&O`"`-KP`@#:\`(`2O`"`$KP`````````````&#P``!@\ -M`(!R/`"`[``#GNP``Y[L``!>\```7O```Q+L``,2[```H.@``*#H``!([```2.P`` -ML+H``+"Z``"(NP``B+L``&R[``!LNP``O+H``+RZ``#$.@``Q#H``#8[```V -M.P``D+D``)"Y``!@NP``8+L``)"Y``"0N0``HCL``*([``"E.P``I3L``"X[ -M```N.P``,CL``#([```4.P``%#L``%B[``!8NP"`)+P`@"2\``!(O```2+P` -M@"J\`(`JO```SKL``,Z[``"0.0``D#D``)X[``">.P``4#L``%`[``#`N``` -MP+@``+`Z``"P.@``B3L``(D[``!F.P``9CL``&`[``!@.P``ZSL``.L[``#\ -M.P``_#L``#@Z```X.@``"KP```J\``!OO```;[P`@(Z\`(".O```>;P``'F\ -M`(``O`"``+P``$"Y``!`N0``D3L``)$[```=/```'3P`P(0\`,"$/`!`CCP` -M0(X\`(`T/`"`-#P``)4[``"5.P``6#H``%@Z``"LNP``K+L``'"\``!PO`"` -MK[P`@*^\`$"TO`!`M+P`0(>\`$"'O```I[L``*>[```&/```!CP``'@\``!X -M/```>3P``'D\`(!M/`"`;3P``%L\``!;/```X#L``.`[``!"NP``0KL``/N[ -M``#[NP``T+L``-"[``"\NP``O+L``/Z[``#^NP"`';P`@!V\`(`NO`"`+KP` -M`!:\```6O```Z+H``.BZ``#[.P``^SL`@#8\`(`V/```%CP``!8\```./``` -M#CP```\\```//```6#L``%@[``"=NP``G;L``/V[``#]NP``OKL``+Z[``!^ -MNP``?KL``#B[```XNP``_+H``/RZ```.NP``#KL``.RZ``#LN@``,#L``#`[ -M``#W.P``]SL``+P[``"\.P``"#H```@Z``"@N@``H+H``-2Z``#4N@``D[L` -M`).[``"FNP``IKL``/`Y``#P.0``\#L``/`[```F/```)CP`@!8\`(`6/``` -MB#L``(@[``"JNP``JKL`@$R\`(!,O`"`2;P`@$F\`(`/O`"`#[P``,B[``#( -MNP``4+H``%"Z`(`(/`"`"#P`@%T\`(!=/`"`-SP`@#<\```!/````3P``.T[ -M``#M.P``MSL``+<[```2.P``$CL``,RZ``#,N@``\;L``/&[``!@O```8+P` -M`&B\``!HO```"+P```B\``!4NP``5+L``)RZ``"#P``(L\``"+/`"`-CP` -M@#8\``!T.P``=#L``!"[```0NP``PKL``,*[``"VNP``MKL``&*[``!BNP`` -MG+L``)R[``#>NP``WKL``#Z[```^NP``?#L``'P[``"].P``O3L``)\[``"? -M.P``YCL``.8[```2/```$CP``*\[``"O.P``&+H``!BZ``"4NP``E+L``-*[ -M``#2NP``MKL``+:[``!PN@``<+H``$0[``!$.P``O#H``+PZ``#,N@``S+H` -M```X````.```(CL``"([``"8.@``F#H``)PZ``"<.@``RSL``,L[`(`7/`"` -M%SP``+`[``"P.P``H+H``*"Z``#%NP``Q;L```R\```,O```X[L``..[``!` -MN@``0+H``)<[``"7.P``N#L``+@[``#0.P``T#L`@`0\`(`$/```LCL``+([ -M```2NP``$KL``.*[``#BNP``H[L``*.[```.NP``#KL```B[```(NP``_+H` -M`/RZ``!XN@``>+H``%@Z``!8.@``PSL``,,[``!`/```0#P``"X\```N/``` -M6CL``%H[``#PN@``\+H``%R[``! -MO```\+D``/"Y```"/````CP`@#T\`(`]/```,3P``#$\``"T.P``M#L``+BZ -M``"XN@``<+L``'"[``#PN@``\+H``%"[``!0NP``T+L``-"[``"4NP``E+L` -M``"Y````N0``(+D``""Y``#TN@``]+H````Z````.@``NCL``+H[```3/``` -M$SP`@!\\`(`?/```^CL``/H[``"8.@``F#H``,2[``#$NP"``KP`@`*\``#D -MNP``Y+L`@`&\`(`!O````KP```*\```ZNP``.KL``"0[```D.P``6#L``%@[ -M``!P.P``<#L``.\[``#O.P``*3P``"D\```A/```(3P``-X[``#>.P``8#D` -M`&`Y`(`:O`"`&KP`P(&\`,"!O`"`<+P`@'"\`(`CO`"`([P``,>[``#'NP`` -M"#H```@Z`(`P/`"`,#P`P((\`,""/`"`.3P`@#D\``"8.P``F#L``,`Z``#` -M.@``0+H``$"Z``!(NP``2+L``'2[``!TNP``L[L``+.[`(`9O`"`&;P``".\ -M```CO```J[L``*N[```*NP``"KL``""[```@NP``@#@``(`X``#..P``SCL` -M``X\```./```HSL``*,[``"H.@``J#H``$@Z``!(.@``C#H``(PZ```H.P`` -M*#L``'8[``!V.P``,+H``#"Z`(`/O`"`#[P``#V\```]O```$;P``!&\``#W -MNP``][L``.*[``#BNP``I+H``*2Z``#0.P``T#L`@`@\`(`(/```H3L``*$[ -M```X.@``.#H``#J[```ZNP``BKL``(J[``!HN@``:+H``(([``"".P``NP``_+H``/RZ````N````+@``-0Z``#4 -M.@``9#L``&0[``#P.P``\#L``!T\```=/```JSL``*L[```&NP``!KL``,F[ -M``#)NP"`&;P`@!F\``!PO```<+P``(6\``"%O`"`'[P`@!^\``"LN@``K+H` -M`$([``!".P``FSL``)L[``"M.P``K3L``(([``"".P``B3L``(D[``#G.P`` -MYSL``-T[``#=.P``6#H``%@Z``"BNP``HKL``/.[``#SNP"`0+P`@$"\``"= -MO```G;P``*J\``"JO```3[P``$^\```.NP``#KL``+8[``"V.P"`.#P`@#@\ -M``!9/```63P``"\\```O/```"3P```D\``#P.P``\#L``)@Z``"8.@"`&+P` -M@!B\`(!TO`"`=+P``&R\``!LO```KP`P)N\`,";O`!` -MD[P`0).\`(!/O`"`3[P``&:[``!FNP``;#L``&P[``"].P``O3L``/L[``#[ -M.P``)SP``"<\``#\.P``_#L````Y````.0``F+L``)B[``!RNP``NP``H;L``*&[``#LN@``[+H```*[```"NP``M[L``+>[```+ -MO```"[P``""\```@O`"`$[P`@!.\``!8NP``6+L``*X[``"N.P"`#SP`@`\\ -M``#6.P``UCL``+$[``"Q.P``?CL``'X[``!>NP``7KL`@#>\`(`WO`"`/;P` -M@#V\``#ZNP``^KL``+&[``"QNP``@;L``(&[``#(N@``R+H```BZ```(N@`` -M@+@``("X```T.P``-#L``)\[``"?.P```CL```([```@NP``(+L``%*[``!2 -MNP``>+L``'B[`(`#O`"``[P``!N\```;O```5KL``%:[``!V.P``=CL``,`[ -M``#`.P``J3L``*D[```&.P``!CL``&:[``!FNP``Y+L``.2[``"YNP``N;L` -M`(N[``"+NP``K;L``*V[``",NP``C+L````X````.```C#H``(PZ``!.NP`` -M3KL``+&[``"QNP``Q+H``,2Z``!<.P``7#L``*\[``"O.P``L#L``+`[```0 -M.P``$#L``).[``"3NP"`!KP`@`:\``#7NP``U[L``+R[``"\NP``Y;L``.6[ -M``""NP``@KL``$P[``!,.P``R3L``,D[```\.P``/#L``("X``"`N```@#D` -M`(`Y``````````````*[```"NP``AKL``(:[`(``O`"``+P``$>\``!'O``` -M*[P``"N\```DNP``)+L``&X[``!N.P``W3L``-T[```[/```.SP``&D\``!I -M/```"CP```H\```>NP``'KL`@#"\`(`PO```=KP``':\`(!_O`"`?[P`@#R\ -M`(`\O```HKL``**[```XN@``.+H``+0Z``"T.@``N#L``+@[`(`>/`"`'CP` -M``,\```#/```BSL``(L[``"1.P``D3L``'@[``!X.P``1KL``$:[```OO``` -M+[P`@%J\`(!:O```4+P``%"\```DO```)+P``(>[``"'NP``Q#H``,0Z``!` -M.P``0#L``&8[``!F.P``VSL``-L[```(/```"#P``)P[``"<.P``O#H``+PZ -M```Z.P``.CL``"X[```N.P``D;L``)&[``!'O```1[P``'>\``!WO```:[P` -M`&N\```NP``7KL``*6[``"ENP``U;L``-6[``"DNP``I+L```BZ -M```(N@``"#H```@Z```PNP``,+L``#*[```RNP``\#D``/`Y````.````#@` -M`"R[```LNP``.+H``#BZ``"7.P``ESL``-L[``#;.P``ASL``(<[```````` -M`````)F[``"9NP``$[P``!.\`(`/O`"`#[P``)&[``"1NP``#KL```Z[```@ -MNP``(+L``)"Y``"0N0``$#L``!`[````N@```+H``("[``"`NP``D+H``)"Z -M``"6.P``ECL``.\[``#O.P``Z#L``.@[``"5.P``E3L``*"Z``"@N@``_;L` -M`/V[`(`TO`"`-+P``#^\```_O`"`3KP`@$Z\`(`UO`"`-;P``&Z[``!NNP`` -MCSL``(\[``#;.P``VSL```$\```!/`"`3#P`@$P\`(![/`"`>SP``$<\``!' -M/```LSL``+,[``!PNP``<+L`@'.\`(!SO```LKP``+*\`$"EO`!`I;P`@'N\ -M`(![O```,;P``#&\```RNP``,KL`@!0\`(`4/`"`@CP`@((\`(!KP`@'J\```]O```/;P`@`"\`(``O```L+L``+"[```P -M.@``,#H``!$\```1/```03P``$$\`(`%/`"`!3P``&@[``!H.P``6#H``%@Z -M``"TN@``M+H``#:[```VNP``-+L``#2[``"(NP``B+L``/*[``#RNP``V[L` -M`-N[``#TN@``]+H``-`Y``#0.0``@#D``(`Y``!2.P``4CL``,<[``#'.P`` -M0#L``$`[``!FNP``9KL``.N[``#KNP``[+L``.R[``"5NP``E;L``*0Z``"D -M.@``^3L``/D[```4/```%#P``+L[``"[.P``-#L``#0[```(N@``"+H``/B[ -M``#XNP``6+P``%B\```MO```+;P``)6[``"5NP``)+L``"2[``#$N@``Q+H` -M`/0Z``#T.@``H#L``*`[``#F.P``YCL`@!\\`(`?/`"`(SP`@",\``"E.P`` -MI3L``+"Z``"PN@``F[L``)N[``#[NP``^[L``%&\``!1O`"`:KP`@&J\``#_ -MNP``_[L``,`X``#`.```.CL``#H[``!4.P``5#L``(4[``"%.P``=#L``'0[ -M``"/.P``CSL``/@[``#X.P"``#P`@``\```F.P``)CL```R[```,NP``A;L` -M`(6[```'O```![P`@&N\`(!KO`"`=+P`@'2\``#RNP``\KL``,@Z``#(.@`` -MX#L``.`[`(`B/`"`(CP`@"L\`(`K/```Y3L``.4[``!P.P``<#L``#8[```V -M.P``<+H``'"Z``#YNP``^;L``".\```CO```_[L``/^[``#_NP``_[L`@!&\ -M`(`1O```HKL``**[``!$.P``1#L```T\```-/`"`)#P`@"0\`(`0/`"`$#P` -M`)D[``"9.P``R+H``,BZ``"\```7O```_[L``/^[``"+ -MNP``B[L``&@Z``!H.@``\#L``/`[`(`F/`"`)CP``/P[``#\.P``L3L``+$[ -M``!X.P``>#L```R[```,NP``%KP``!:\```DO```)+P``.Z[``#NNP``\+L` -M`/"[`(`+O`"`"[P``-V[``#=NP``-KL``#:[``#H.@``Z#H````\````/``` -M63P``%D\``!9/```63P``/L[``#[.P``X#H``.`Z``"*NP``BKL``%:\``!6 -MO`!`H+P`0*"\``"%O```A;P`@`&\`(`!O```%+L``!2[``#@.@``X#H``,,[ -M``##.P``!#P```0\```)/```"3P`@`\\`(`//```[SL``.\[```&.P``!CL` -M`&R[``!LNP``SKL``,Z[```-O```#;P``%:\``!6O`"`;KP`@&Z\`(`,O`"` -M#+P```BZ```(N@``4#L``%`[``",.P``C#L``)@[``"8.P``0#L``$`[```( -M.P``"#L``)$[``"1.P``ISL``*<[``#0.@``T#H``*"Z``"@N@``\+H``/"Z -M``"DNP``I+L``#F\```YO```7KP``%Z\`(`)O`"`";P``*2Z``"DN@``3#L` -M`$P[``#&.P``QCL``-0[``#4.P``+CL``"X[``!8N@``6+H``)"Y``"0N0`` -M.+H``#BZ``!RNP`` -MNP``DKL``)*[```2NP``$KL``!"Z```0N@``(+H``""Z``"LN@``K+H``$B[ -M``!(NP``NKL``+J[``"KNP``J[L``$BZ``!(N@``.#L``#@[``"<.P``G#L` -M@`T\`(`-/```-#P``#0\``"[.P``NSL``%B[``!8NP"`$+P`@!"\`(`RO`"` -M,KP`@#Z\`(`^O```([P``".\``#;NP``V[L``)2[``"4NP``'+L``!R[```> -M.P``'CL``/X[``#^.P``'3P``!T\```B/```(CP``$\\``!//`"`5#P`@%0\ -M``":.P``FCL``-^[``#?NP``8+P``&"\`$"+O`!`B[P`0)"\`$"0O```8+P` -M`&"\``#\NP``_+L``%"[``!0NP``)CL``"8[```K/```*SP`@'D\`(!Y/``` -M9#P``&0\`(`U/`"`-3P`@!\\`(`?/```MSL``+<[``"2NP``DKL``'&\``!Q -MO`"`FKP`@)J\`$"/O`!`C[P`@$*\`(!"O```%+L``!2[``#'.P``QSL`@!8\ -M`(`6/```*SP``"L\```V/```-CP``.0[``#D.P``H+D``*"Y``""NP``@KL` -M`$"[``!`NP``&+L``!B[``"*NP``BKL``+B[``"XNP``H;L``*&[``!VNP`` -M=KL``*2Z``"DN@``3CL``$X[``"L.P``K#L``$H[``!*.P``[#H``.PZ```6 -M.P``%CL```BZ```(N@``E+L``)2[```NNP``+KL``"P[```L.P``DCL``)([ -M```J.P``*CL``,`Y``#`.0``]+H``/2Z``!XNP``>+L``'B[``!XNP``Z+H` -M`.BZ``!XN@``>+H``.BZ``#HN@``.+H``#BZ``!@.@``8#H``,RZ``#,N@`` -M@;L``(&[``#0.0``T#D``/@[``#X.P``*#P``"@\`(`4/`"`%#P``,4[``#% -M.P``X#D``.`Y``"ONP``K[L```*\```"O```$;P``!&\`(`>O`"`'KP``/J[ -M``#ZNP``T+H``-"Z``!N.P``;CL``'P[``!\.P``8#L``&`[``#H.P``Z#L` -M@"X\`(`N/```'3P``!T\``#).P``R3L``*PZ``"L.@``N+L``+B[`(`DO`"` -M)+P`@!2\`(`4O```Y[L``.>[``#"NP``PKL``.BZ``#HN@``G3L``)T[``#Y -M.P``^3L``*P[``"L.P``1CL``$8[``"+.P``BSL``,,[``##.P``JSL``*L[ -M``!4.P``5#L``,"X``#`N```OKL``+Z[`(`AO`"`(;P`@!&\`(`1O```W+L` -M`-R[``"5NP``E;L``-`Z``#0.@"`&3P`@!D\``!`/```0#P```T\```-/``` -MJ3L``*D[``!*.P``2CL``"@Z```H.@``%+L``!2[``"(NP``B+L``+^[``"_ -MNP``]KL``/:[``#%NP``Q;L``(2Z``"$N@``-CL``#8[``"E.P``I3L``!\\ -M```?/```53P``%4\`(`'/`"`!SP``!BZ```8N@``U+L``-2[`(`3O`"`$[P` -M`!.\```3O```L+L``+"[``#0N0``T+D``#8[```V.P``E#L``)0[``#=.P`` -MW3L``/L[``#[.P``GSL``)\[``#L.@``[#H``%0[``!4.P``H3L``*$[``!@ -M.@``8#H``*B[``"HNP``\;L``/&[``#KNP``Z[L``+&[``"QNP```+H```"Z -M``"2.P``DCL``+P[``"\.P``OSL``+\[``#5.P``U3L``)D[``"9.P``@+D` -M`("Y``!4NP``5+L``*BZ``"HN@``T#H``-`Z``!@.@``8#H``'BZ``!XN@`` -M0+H``$"Z``"`.```@#@``/@Z``#X.@``O#L``+P[``#I.P``Z3L``&8[``!F -M.P```#D````Y``#8N@``V+H``+:[``"VNP"`(;P`@"&\`(`$O`"`!+P```BZ -M```(N@``WSL``-\[`(`K/`"`*SP``#@\```X/```'#P``!P\``"H.P``J#L` -M`!@Z```8.@``Q+H``,2Z``!ZNP``>KL``.V[``#MNP``X+L``."[``!$NP`` -M1+L```"[````NP``!KL```:[```R.P``,CL`@!X\`(`>/```3CP``$X\`(`V -M/`"`-CP``/@[``#X.P``$#L``!`[``!^NP``?KL``/"[``#PNP``];L``/6[ -M``#?NP``W[L``+F[``"YNP``P+H``,"Z``!4.P``5#L``&([``!B.P``"CL` -M``H[``"R.P``LCL``"T\```M/`"`1SP`@$<\`(`O/`"`+SP``-\[``#?.P`` -M(+D``""Y``#;NP``V[L`@!^\`(`?O`"`+[P`@"^\`(`QO`"`,;P``/F[``#Y -MNP``8+D``&"Y``#-.P``S3L``.([``#B.P``MSL``+<[``#[.P``^SL``"@\ -M```H/```&3P``!D\``#C.P``XSL``%H[``!:.P``0KL``$*[```"O````KP` -M@`&\`(`!O```X+L``."[``#;NP``V[L``%:[``!6NP``5CL``%8[``#:.P`` -MVCL``(\[``"/.P``M#H``+0Z``#P.@``\#H``'0[``!T.P``F3L``)D[``"Z -M.P``NCL``*8[``"F.P``>#H``'@Z```TNP``-+L``&*[``!BNP``G;L``)V[ -M``#.NP``SKL``$R[``!,NP``)CL``"8[``"#.P``@SL``-0Z``#4.@``H+D` -M`*"Y```(N@``"+H``"`Y```@.0``"#L```@[``">.P``GCL``,X[``#..P`` -ML3L``+$[``"..P``CCL``%@[``!8.P``<+H``'"Z``##NP``P[L``,&[``#! -MNP``;KL``&Z[``"ONP``K[L`@`2\`(`$O```V+L``-B[```(NP``"+L``%P[ -M``!<.P"`(3P`@"$\`(!X/`"`>#P`@($\`("!/`"`1SP`@$<\``#>.P``WCL` -M`("Y``"`N0"`(+P`@""\`("*O`"`BKP``("\``"`O```%;P``!6\``">NP`` -MGKL```*[```"NP``)CL``"8[``#@.P``X#L`@"`\`(`@/```33P``$T\``!- -M/```33P`@`4\`(`%/```1#L``$0[``"$N@``A+H``,N[``#+NP``2[P``$N\ -M``!PO```<+P`@"&\`(`AO```(+L``""[```,.P``##L``)H[``":.P``WSL` -M`-\[``#@.P``X#L``,,[``##.P``W3L``-T[``#%.P``Q3L``-`Z``#0.@`` -MZ+H``.BZ```^NP``/KL``,Z[``#.NP``+KP``"Z\`(`@O`"`(+P``'J[``!Z -MNP``##L```P[``"M.P``K3L``-,[``#3.P``N3L``+D[```2.P``$CL``("Z -M``"`N@``>+H``'BZ````N0```+D``$"Y``!`N0```#L````[``"@.P``H#L` -M`#P[```\.P``%KL``!:[``")NP``B;L``$R[``!,NP``.KL``#J[``!KL``'J[``!FNP``9KL``+2Z``"TN@``"CL```H[``"6 -M.P``ECL``,8[``#&.P"`#3P`@`T\```[``#GNP``RKL``,J[``"(NP``B+L```"[````NP```+L` -M``"[```0NP``$+L``,"Y``#`N0``K#H``*PZ``"T.@``M#H``"H[```J.P`` -MU#L``-0[```,/```##P``(\[``"/.P``(KL``"*[``#(NP``R+L``.B[``#H -MNP``]+L``/2[``"YNP``N;L``#*[```RNP``S+H``,RZ````N````+@``&([ -M``!B.P``JCL``*H[``"(.P``B#L``'P[``!\.P``GSL``)\[``!2.P``4CL` -M`"R[```LNP"`#[P`@`^\```QO```,;P`@!F\`(`9O```Q+L``,2[```HN@`` -M*+H``)`[``"0.P``P3L``,$[``"^.P``OCL``+T[``"].P``-CL``#8[```" -MNP```KL``"B[```HNP``P+D``,"Y``"(N@``B+H``(R[``",NP``TKL``-*[ -M``#5NP``U;L``**[``"BNP``S+H``,RZ``!0.P``4#L``-`[``#0.P``USL` -M`-<[``"^.P``OCL``(8[``"&.P``R+H``,BZ`(``O`"``+P`@`6\`(`%O``` -MDKL``)*[``!4NP``5+L``&J[``!JNP``!KL```:[``"0.0``D#D``"@[```H -M.P``MSL``+<[``#U.P``]3L``,P[``#,.P``2#L``$@[``#`N```P+@``'J[ -M``!ZNP``%[P``!>\``!)O```2;P```.\```#O```P#@``,`X``"?.P``GSL` -M`,T[``#-.P``R3L``,D[``!:.P``6CL```"Z````N@``#KL```Z[``!0NP`` -M4+L``)"[``"0NP``3KL``$Z[``#@.0``X#D```@[```(.P``&#H``!@Z``"P -MN0``L+D``/`Z``#P.@``CSL``(\[``!X.P``>#L``(@Z``"(.@``]+H``/2Z -M``"ONP``K[L``.^[``#ONP``J[L``*N[```(NP``"+L``("X``"`N```=#L` -M`'0[`(`#/`"``SP``/4[``#U.P``*CL``"H[``"`N@``@+H``)BZ``"8N@`` -M0+H``$"Z``#@N@``X+H``$:[``!&NP``@KL``(*[``"LNP``K+L``*N[``"K -MNP``!KL```:[``#T.@``]#H``*<[``"G.P"`(3P`@"$\`(!>/`"`7CP`@"D\ -M`(`I/```'#L``!P[``!PNP``<+L``.R[``#LNP``'KP``!Z\```CO```([P` -M``>\```'O```OKL``+Z[```>NP``'KL``!0[```4.P``XCL``.([`(`8/`"` -M&#P``"H\```J/`"`23P`@$D\`(!&/`"`1CP``+<[``"W.P``5+L``%2[`(`# -MO`"``[P``!F\```9O`"`%KP`@!:\``#4NP``U+L``#J[```ZNP``K+H``*RZ -M``"`N0``@+D``!8[```6.P``@SL``(,[``"".P``@CL``*X[``"N.P"`&CP` -M@!H\`(!#/`"`0SP`@`0\`(`$/```:#H``&@Z``"#NP``@[L``-Z[``#>NP"` -M"+P`@`B\``#6NP``UKL``$*[``!"NP``0+H``$"Z``#L.@``[#H``*L[``"K -M.P``OSL``+\[``!X.P``>#L``&H[``!J.P``M3L``+4[``"[.P``NSL``#([ -M```R.P``A+H``(2Z``!$NP``1+L``$Z[``!.NP``\+H``/"Z``"0.@``D#H` -M`%0[``!4.P``)#L``"0[``"\.@``O#H``&@Z``!H.@```KL```*[``"?NP`` -MG[L```2[```$NP``E#L``)0[```//```#SP`@!X\`(`>/`"`$CP`@!(\``#1 -M.P``T3L```P[```,.P``Y+H``.2Z``!^NP``?KL``*V[``"MNP``U+L``-2[ -M``"QNP``L;L``#"[```PNP``"KL```J[``#HN@``Z+H``&0[``!D.P"`,CP` -M@#(\``!I/```:3P`@%T\`(!=/```-3P``#4\``#).P``R3L``"`Y```@.0`` -ME+L``)2[```!O````;P``"J\```JO```*;P``"F\``#@NP``X+L``!J[```: -MNP`````````````J.P``*CL``!4\```5/`"`@SP`@(,\`("2/`"`DCP`@',\ -M`(!S/`"`$#P`@!`\``"PN0``L+D``!.\```3O```4KP``%*\``!:O```6KP` -M`$.\``!#O```P+L``,"[``!N.P``;CL`@"(\`(`B/```*CP``"H\`(`1/`"` -M$3P``!$\```1/`"`"3P`@`D\``"Q.P``L3L```P[```,.P``T+D``-"Y``!Z -MNP``>KL``,:[``#&NP``D[L``).[```6NP``%KL``)2Z``"4N@``_#H``/PZ -M``"].P``O3L``*X[``"N.P``O#H``+PZ``"`N@``@+H``*"Y``"@N0``_#H` -M`/PZ``"+.P``BSL``,8[``#&.P``U3L``-4[``"=.P``G3L```H[```*.P`` -M0#D``$`Y``#LN@``[+H``$Z[``!.NP``.+H``#BZ``!(.P``2#L``!8[```6 -M.P``S+H``,RZ``!2NP``4KL``"J[```JNP``A+H``(2Z```&.P``!CL``+H[ -M``"Z.P"``#P`@``\`(`(/`"`"#P```0\```$/```QSL``,<[``"H.@``J#H` -M`%"[``!0NP``1KL``$:[``!XN@``>+H``"2[```DNP``KKL``*Z[``"(NP`` -MB+L``-"Y``#0N0``3#L``$P[``#Q.P``\3L`@"P\`(`L/```(#P``"`\``#? -M.P``WSL``&P[``!L.P``>+H``'BZ``#.NP``SKL```J\```*O```G[L``)^[ -M```P.@``,#H``$([``!".P``0#L``$`[``!L.P``;#L``(P[``",.P``CCL` -M`(X[``"].P``O3L``.D[``#I.P``O#L``+P[``!T.P``=#L``!`[```0.P`` -M`+L```"[``#RNP``\KL```^\```/O```J;L``*F[``!0N@``4+H```P[```, -M.P``ECL``)8[``#O.P``[SL`@`L\`(`+/```"3P```D\`(`%/`"`!3P``-4[ -M``#5.P```CL```([```$NP``!+L``)"[``"0NP"``;P`@`&\`(`JO`"`*KP` -M`,J[``#*NP``,CL``#([`(`@/`"`(#P``%(\``!2/`"`3SP`@$\\`(`7/`"` -M%SP``&([``!B.P``(KL``"*[``#+NP``R[L``/B[``#XNP``V+L``-B[```F -MNP``)KL``"@[```H.P``@#L``(`[``!`.P``0#L``*([``"B.P``##P```P\ -M`(`3/`"`$SP``.D[``#I.P``CCL``(X[``"0.0``D#D``&Z[``!NNP``IKL` -M`*:[``"ENP``I;L``(N[``"+NP``O+H``+RZ``!F.P``9CL``-T[``#=.P`` -MLSL``+,[```L.P``+#L``#@[```X.P``DCL``)([``"+.P``BSL``%8[``!6 -M.P``$#L``!`[``!8N@``6+H``&B[``!HNP``1KL``$:[``"@N@``H+H````Z -M````.@``ASL``(<[`(`0/`"`$#P`@"`\`(`@/```ISL``*<[``"@N@``H+H` -M`)F[``"9NP``K+L``*R[``"6NP``EKL``-2Z``#4N@``4#L``%`[``#1.P`` -MT3L````\````/```%#P``!0\``#^.P``_CL``&([``!B.P``(#H``"`Z``"@ -MN0``H+D``%Z[``!>NP```KP```*\```9O```&;P``,J[``#*NP``$+H``!"Z -M``"W.P``MSL``#8\```V/`"`:3P`@&D\``!\``#TNP``]+L``.>[``#GNP``X+L``."[``!FNP``9KL` -M`*PZ``"L.@``O#L``+P[`(`I/`"`*3P``%D\``!9/`"`23P`@$D\```5/``` -M%3P``*D[``"I.P``,+H``#"Z``#ONP``[[L``"R\```LO```^+L``/B[``#H -MN@``Z+H```0[```$.P``6CL``%H[``"(.P``B#L``(`[``"`.P``1CL``$8[ -M``!L.P``;#L``)@[``"8.P``C3L``(T[``"7.P``ESL``*@[``"H.P``%#L` -M`!0[``!(NP``2+L``+6[``"UNP``>KL``'J[``"0N@``D+H``/`Y``#P.0`` -MY#H``.0Z``!".P``0CL``#H[```Z.P``X#H``.`Z``#X.@``^#H``"([```B -M.P``$#L``!`[``!6.P``5CL``+0[``"T.P``DCL``)([``"@.@``H#H``)`Z -M``"0.@``7CL``%X[``!P.P``<#L``,`Z``#`.@``Y+H``.2Z``"RNP``LKL` -M``V\```-O`"`&;P`@!F\``#[``"GNP``X#D``.`Y``":.P``FCL` -M`.<[``#G.P"`!#P`@`0\``#!.P``P3L```([```".P``0#H``$`Z``#T.@`` -M]#H``,`Z``#`.@``8+D``&"Y```XN@``.+H``*`Y``"@.0``V#H``-@Z``!* -M.P``2CL``),[``"3.P``FCL``)H[```X.P``.#L``+`Y``"P.0``V+H``-BZ -M``"0NP``D+L``,.[``##NP```KL```*[``"A.P``H3L`@`,\`(`#/```\#L` -M`/`[``#%.P``Q3L``'H[``!Z.P``0#H``$`Z``!HN@``:+H``#BZ```XN@`` -M(#D``"`Y``"D.@``I#H``"H[```J.P``(#L``"`[``#PN0``\+D``%J[``!: -MNP``W+H``-RZ```X.P``.#L``*T[``"M.P``OSL``+\[``#6.P``UCL``,,[ -M``##.P``>#L``'@[```".P```CL```"X````N```0KL``$*[``"+NP``B[L` -M`%B[``!8NP``,KL``#*[```ZNP``.KL```"Z````N@``N#L``+@[``!"/``` -M0CP`@&8\`(!F/```3SP``$\\`(`./`"`#CP``/@Z``#X.@``J[L``*N[`(`+ -MO`"`"[P``!"\```0O```];L``/6[```TNP``-+L``(4[``"%.P``[CL``.X[ -M``#4.P``U#L``,,[``##.P``Y#L``.0[``#I.P``Z3L``+8[``"V.P``8#L` -M`&`[``"D.@``I#H``-2Z``#4N@``B[L``(N[``"3NP``D[L``#Z[```^NP`` -M`+H```"Z``"(.P``B#L`@!D\`(`9/`"`)CP`@"8\``#H.P``Z#L``*$[``"A -M.P``ACL``(8[```4.P``%#L```BZ```(N@``0KL``$*[``"ANP``H;L``,*[ -M``#"NP``G[L``)^[``#$N@``Q+H``#P[```\.P"`!#P`@`0\``!L/```;#P` -M0)4\`$"5/```;3P``&T\``"V.P``MCL``/"Z``#PN@``TKL``-*[```7O``` -M%[P`@`B\`(`(O```A;L``(6[```(.@``"#H``)0[``"4.P"`!#P`@`0\```4 -M/```%#P``.`[``#@.P``ESL``)<[``"<.P``G#L``)\[``"?.P```CL```([ -M``#,N@``S+H``.2Z``#DN@``D#D``)`Y```&.P``!CL``(X[``"..P``U3L` -M`-4[``#".P``PCL``'0[``!T.P```#L````[``#PN0``\+D``$2[``!$NP`` -M_+H``/RZ```V.P``-CL``-H[``#:.P``XCL``.([``"N.P``KCL``(D[``") -M.P``5CL``%8[```6.P``%CL``#([```R.P``@CL``(([``!V.P``=CL``#H[ -M```Z.P``X#H``.`Z``#DN@``Y+H``+2[``"TNP``B[L``(N[``"H.@``J#H` -M`+X[``"^.P``!SP```<\```I/```*3P`@#L\`(`[/```*#P``"@\``#M.P`` -M[3L``'`[``!P.P``P+@``,"X``!\NP``?+L``,F[``#)NP``U;L``-6[``#G -MNP``Y[L``,J[``#*NP```#D````Y`(`7/`"`%SP``&\\``!O/```>SP``'L\ -M`(!B/`"`8CP`@"4\`(`E/```E3L``)4[``"PN0``L+D``%2[``!4NP``D[L` -M`).[``"$NP``A+L```J[```*NP``,+H``#"Z``#0N@``T+H``"B[```HNP`` -M&#H``!@Z``#5.P``U3L`@"X\`(`N/`"`33P`@$T\`(!7/`"`5SP`@#`\`(`P -M/```OCL``+X[``!0.@``4#H``'B[``!XNP``^;L``/F[```$O```!+P``*:[ -M``"FNP``^+H``/BZ``#`N```P+@``#`[```P.P```SP```,\`(!4/`"`5#P` -M`&@\``!H/```2CP``$H\`(`3/`"`$SP``%X[``!>.P``/KL``#Z[``#%NP`` -MQ;L``,N[``#+NP``G+L``)R[``"0N0``D+D``+@[``"X.P``]3L``/4[``"4 -M.P``E#L``,0Z``#$.@``2#H``$@Z``"`.@``@#H``,PZ``#,.@``;CL``&X[ -M``#=.P``W3L`@`H\`(`*/`"`!3P`@`4\``#L.P``[#L``*0[``"D.P``!CL` -M``8[``"0.@``D#H``,0Z``#$.@``<+H``'"Z``"/```'CP``,P[``#,.P``,#L``#`[``#(.@``R#H``(`Z -M``"`.@``-+L``#2[````O````+P`@!:\`(`6O```WKL``-Z[```BNP``(KL` -M`(D[``").P"`03P`@$$\`,"(/`#`B#P`0)`\`$"0/```=SP``'<\```//``` -M#SP``+"Y``"PN0``_+L``/R[```?O```'[P``/V[``#]NP``O+L``+R[``!J -MNP``:KL``(`Z``"`.@``RSL``,L[`(`9/`"`&3P`@#D\`(`Y/`"`13P`@$4\ -M```G/```)SP``-T[``#=.P``6CL``%H[``"8N@``F+H``*^[``"ONP``O;L` -M`+V[```.NP``#KL``-0Z``#4.@``0CL``$([```N.P``+CL``!@[```8.P`` -M[#H``.PZ``#`.@``P#H``$([``!".P``N3L``+D[``#P.P``\#L`@`8\`(`& -M/````CP```(\``"".P``@CL```*[```"NP``B[L``(N[```2NP``$KL``(`X -M``"`.```R#H``,@Z``!:.P``6CL``)D[``"9.P``BSL``(L[```N.P``+CL` -M`,@Z``#(.@``B#H``(@Z``!@.@``8#H```H[```*.P``?CL``'X[``!0.P`` -M4#L``(PZ``",.@``^#H``/@Z``"B.P``HCL``+\[``"_.P``ASL``(<[``#T -M.@``]#H``#BZ```XN@``1KL``$:[``!TNP``=+L``"J[```JNP`````````` -M``").P``B3L`@`X\`(`./```+#P``"P\``#Q.P``\3L```([```".P``4+H` -M`%"Z``"`N@``@+H``*BZ``"HN@``D+H``)"Z``!@.0``8#D``"`Z```@.@`` -MP+@``,"X````N0```+D`````````````@#@``(`X```<.P``'#L``-D[``#9 -M.P``$#P``!`\``#A.P``X3L``'`[``!P.P``_#H``/PZ```P.@``,#H``-BZ -M``#8N@``9+L``&2[``!JNP``:KL``%*[``!2NP``*+L``"B[```````````` -M`$H[``!*.P``N#L``+@[`(`./`"`#CP`@#,\`(`S/`"`"3P`@`D\``"T.@`` -MM#H``+B[``"XNP``%[P``!>\`(`8O`"`&+P``-2[``#4NP``@+H``("Z``"^ -M.P``OCL``#`\```P/`"`2CP`@$H\`(`O/`"`+SP``,`[``#`.P``@+@``("X -M```^NP``/KL``$B[``!(NP``E+L``)2[``#@NP``X+L``,^[``#/NP``:+L` -M`&B[```HN@``*+H``"0[```D.P``PCL``,([```'/```!SP```\\```//``` -M_CL``/X[``"K.P``JSL``%@Z``!8.@``/KL``#Z[```NNP``+KL```"Z```` -MN@``>+H``'BZ``!4NP``5+L``&J[``!JNP``'KL``!Z[``"#L``)P[``"<.P``ACL``(8[``"L.@``K#H``(BZ``"(N@``$+L``!"[```X -MNP``.+L``-RZ``#3P``'D\`(`_/`"`/SP``)@[``"8.P``>+H``'BZ -M``"*NP``BKL``-N[``#;NP``Y[L``.>[``"FNP``IKL``%J[``!:NP``+KL` -M`"Z[``#4N@``U+H``&"Z``!@N@``T+D``-"Y```$.P``!#L``-T[``#=.P"` -M(3P`@"$\```//```#SP``*H[``"J.P``%CL``!8[``"@N0``H+D``&J[``!J -MNP``N;L``+F[``"HNP``J+L``(.[``"#NP``.KL``#J[``"(N@``B+H``,`X -M``#`.```T#D``-`Y```:.P``&CL``*\[``"O.P``N3L``+D[```X.P``.#L` -M`$`Y``!`.0``T+D``-"Y``"P.0``L#D``.`Z``#@.@``.P``4CL``%([``"O -M.P``KSL``,X[``#..P``G#L``)P[```R.P``,CL``.@Z``#H.@``0#H``$`Z -M```8N@``&+H``)"Y``"0N0``N#H``+@Z``!`.P``0#L``)L[``";.P``PCL` -M`,([``!>.P``7CL``/2Z``#TN@``H+L``*"[``"!NP``@;L``+BZ``"XN@`` -MO#H``+PZ``"3.P``DSL``-T[``#=.P``WCL``-X[``"0.P``D#L``)`Z``"0 -M.@``O+H``+RZ``#XN@``^+H``"`Z```@.@``A3L``(4[``"<.P``G#L``#([ -M```R.P``K#H``*PZ``!X.@``>#H``("Y``"`N0``I+H``*2Z```8N@``&+H` -M`(`Z``"`.@``+#L``"P[``").P``B3L``*0[``"D.P``FCL``)H[``"..P`` -MCCL``)T[``"=.P``ASL``(<[``#`N```P+@``*^[``"ONP``Y;L``.6[``"B -MNP``HKL``.BZ``#HN@``+CL``"X[`(`&/`"`!CP`@#T\`(`]/`"`-3P`@#4\ -M````/````#P``!X[```>.P``9+L``&2[``#2NP``TKL``*.[``"CNP``X+H` -M`."Z``"`.0``@#D``*@Z``"H.@``2CL``$H[``"<.P``G#L``)\[``"?.P`` -ME3L``)4[``"@.P``H#L``)@[``"8.P``4CL``%([``"@.@``H#H``/2Z``#T -MN@``I;L``*6[``"JNP``JKL```J[```*NP``4#H``%`Z```0.P``$#L``&X[ -M``!N.P``O#L``+P[``#L.P``[#L``/4[``#U.P``ZCL``.H[``#+.P``RSL` -M`&0[``!D.P```+H```"Z``"?NP``G[L``!R\```\``#DNP`` -MY+L``.RZ``#LN@``<#L``'`[`(`4/`"`%#P``$4\``!%/`"`.3P`@#D\``#N -M.P``[CL``"`Z```@.@``R[L``,N[`(`:O`"`&KP``/F[``#YNP``:+L``&B[ -M```0N@``$+H``(PZ``",.@``!CL```8[``#0.@``T#H``(2Z``"$N@``:KL` -M`&J[``"`NP``@+L``"2[```DNP``P+@``,"X```8.P``&#L``#H[```Z.P`` -MV#H``-@Z``#4.@``U#H``$0[``!$.P``1#L``$0[```@.@``(#H```*[```" -MNP``8+L``&"[``"9NP``F;L``,F[``#)NP``R[L``,N[``"*NP``BKL``,"Z -M``#`N@``H#H``*`Z``!2.P``4CL``#X[```^.P``M#H``+0Z````.P```#L` -M`),[``"3.P``KCL``*X[``!..P``3CL``,"Y``#`N0``E;L``)6[`(`9O`"` -M&;P``%:\``!6O`"`6KP`@%J\```8O```&+P``/RZ``#\N@``VSL``-L[``!: -M/```6CP``'`\``!P/```-CP``#8\``#(.P``R#L``%@Z``!8.@``L;L``+&[ -M`(`RO`"`,KP``%>\``!7O```3+P``$R\`(`EO`"`);P``,R[``#,NP``Q+H` -M`,2Z``!&.P``1CL``.D[``#I.P"`(CP`@"(\```7/```%SP``)0[``"4.P`` -ME+H``)2Z``"`NP``@+L``(>[``"'NP``AKL``(:[``!TNP``=+L``!"[```0 -MNP``S+H``,RZ``!"NP``0KL``)J[``":NP``L[L``+.[``";NP``F[L``+BZ -M``"XN@``;CL``&X[``#7.P``USL``*H[``"J.P``X#H``.`Z``"PN@``L+H` -M`'B[``!XNP``Q+L``,2[``#/NP``S[L``'R[``!\NP``T+D``-"Y``#T.@`` -M]#H``#@[```X.P``&CL``!H[``!0.@``4#H``("Y``"`N0``$+H``!"Z```4 -MNP``%+L``*Z[``"NNP``X+L``."[``"SNP``L[L``"R[```LNP``H#D``*`Y -M``!(.P``2#L``*<[``"G.P``ISL``*<[```B.P``(CL``-RZ``#\`(`'O`"`&;P`@!F\``#@NP``X+L``-"Y``#0N0``WCL``-X[```N -M/```+CP``"L\```K/```\CL``/([``#<.@``W#H``,&[``#!NP``0;P``$&\ -M`(!;O`"`6[P``#*\```RO```J+L``*B[``#P.@``\#H``-4[``#5.P``]#L` -M`/0[``#I.P``Z3L``.`[``#@.P``I#L``*0[``!X.@``>#H``%*[``!2NP`` -MN+L``+B[``#BNP``XKL`@`.\`(`#O```_[L``/^[``"INP``J;L``*"Z``"@ -MN@``.#L``#@[``#!.P``P3L``,H[``#*.P``?#L``'P[```,.P``##L``!H[ -M```:.P``"CL```H[```@.0``(#D```R[```,NP``?KL``'Z[``"[NP``N[L` -M`/N[``#[NP"``KP`@`*\``"JNP``JKL``(`Y``"`.0``UCL``-8[`(`R/`"` -M,CP`@"L\`(`K/```PSL``,,[``"`.@``@#H``#B[```XNP``S;L``,V[`(`% -MO`"`!;P``-R[``#\`(`:O`"`&KP``.*[``#B -MNP``L+H``+"Z``!^.P``?CL``,8[``#&.P``OSL``+\[``"I.P``J3L``(L[ -M``"+.P``&CL``!H[``"$.@``A#H``.@Z``#H.@``4#L``%`[```\.P``/#L` -M`!@Z```8.@``3+L``$R[``#?NP``W[L`@`&\`(`!O```P[L``,.[``!BNP`` -M8KL``*BZ``"HN@``!#L```0[``#6.P``UCL``"(\```B/```+3P``"T\```9 -M/```&3P``.@[``#H.P``3#L``$P[```HNP``*+L```B\```(O`"`3+P`@$R\ -M`(!;O`"`6[P`@!>\`(`7O```B+H``(BZ``#7.P``USL`@#`\`(`P/```2CP` -M`$H\```_/```/SP```0\```$/```-CL``#8[``#$N@``Q+H``&"[``!@NP`` -M=+L``'2[``!VNP``=KL``(N[``"+NP``KKL``*Z[``#`NP``P+L``(&[``"! -MNP``\#D``/`Y``"7.P``ESL``/`[``#P.P"`&SP`@!L\`(`K/`"`*SP```T\ -M```-/```DSL``),[``#0.0``T#D``#2[```TNP``E[L``)>[``"RNP``LKL` -M`+R[``"\NP``N+L``+B[``!TNP``=+L``$`Z``!`.@``N#L``+@[```"/``` -M`CP``/`[``#P.P``NCL``+H[``!".P``0CL``)BZ``"8N@``C;L``(V[``"# -MNP``@[L``"BZ```HN@``A3L``(4[```"/````CP```@\```(/```D#L``)`[ -M``!HN@``:+H``)>[``"7NP``V+L``-B[``#SNP``\[L``+^[``"_NP``G+H` -M`)RZ``"/.P``CSL``/P[``#\.P"`#SP`@`\\`(`./`"`#CP``/@[``#X.P`` -ML3L``+$[```@.P``(#L```"[````NP``X;L``.&[```9O```&;P`@`.\`(`# -MO```F[L``)N[``"0N@``D+H``#`[```P.P``SCL``,X[```&/```!CP``/<[ -M``#W.P``L#L``+`[``!L.P``;#L``$0[``!$.P``6#L``%@[``!(.P``2#L` -M`.`Y``#@.0``D;L``)&[```&O```!KP`@!.\`(`3O```_[L``/^[``"9NP`` -MF;L``'@Z``!X.@``^3L``/D[``!&/```1CP`@$T\`(!-/```&SP``!L\``"4 -M.P``E#L``$"Y``!`N0``3KL``$Z[``"1NP``D;L``*Z[``"NNP``N;L``+F[ -M``!LNP``;+L``$`Y``!`.0``/#L``#P[``"$.P``A#L``*T[``"M.P``Q#L` -M`,0[``"8.P``F#L``,@Z``#(.@``\+H``/"Z``!\NP``?+L``$B[``!(NP`` -MP#@``,`X``!,.P``3#L``'8[``!V.P``&CL``!H[``!0.@``4#H``*"Y``"@ -MN0``N+H``+BZ``"XN@``N+H``(PZ``",.@``F#L``)@[``#:.P``VCL``,0[ -M``#$.P``(CL``"([```.NP``#KL``*:[``"FNP``FKL``)J[``!"NP``0KL` -M`)BZ``"8N@``D#H``)`Z``"&.P``ACL``,$[``#!.P``J3L``*D[```Z.P`` -M.CL``(PZ``",.@``P+@``,"X``"@N@``H+H``!2[```4NP``.+L``#B[```: -MNP``&KL``"`Z```@.@``K#L``*P[```&/```!CP``/`[``#P.P``D3L``)$[ -M``!H.@``:#H``%R[``!#H``'@Z``"PN0``L+D``,"Z``#`N@``H+H``*"Z````N````+@``"`Y```@ -M.0``*+H``"BZ``!HN@``:+H``"`Y```@.0``"CL```H[``"%.P``A3L``)<[ -M``"7.P``3CL``$X[``!X.@``>#H``&"Y``!@N0``\+D``/"Y``"TN@``M+H` -M`"J[```JNP``*+L``"B[``#4N@``U+H``(2Z``"$N@```#@````X```P.P`` -M,#L``-$[``#1.P"`'#P`@!P\`(`P/`"`,#P`@`@\`(`(/````#L````[``"[ -MNP``N[L``#*\```RO`"`1[P`@$>\`(`KO`"`*[P``,&[``#!NP``Q#H``,0Z -M```0/```$#P``$D\``!)/`"`.CP`@#H\```%/```!3P``(4[``"%.P```#H` -M```Z``#XN@``^+H``(*[``""NP``P;L``,&[``#&NP``QKL``'"[``!PNP`` -M4+H``%"Z``"`.@``@#H``"([```B.P``BCL``(H[``"G.P``ISL``(0[``"$ -M.P```#L````[```@.@``(#H``$`Z``!`.@``&CL``!H[``"%.P``A3L``%8[ -M``!6.P``D+D``)"Y``!PNP``<+L``*F[``"INP``O+L``+R[``"KNP``J[L` -M``2[```$NP``4#L``%`[``#P.P``\#L`@`8\`(`&/```QSL``,<[```V.P`` -M-CL``("X``"`N```H+H``*"Z```0N@``$+H``-`Y``#0.0``(#H``"`Z``!X -M.@``>#H``+PZ``"\.@``D#D``)`Y```*NP``"KL``%:[``!6NP``+KL``"Z[ -M``#8N@``V+H``&"Z``!@N@``P+@``,"X``"X.@``N#H``(0[``"$.P``[CL` -M`.X[```6/```%CP`@`$\`(`!/```?CL``'X[``#PN0``\+D``(N[``"+NP`` -M_+L``/R[```7O```%[P``-N[``#;NP``Q+H``,2Z``!<.P``7#L``,@[``#( -M.P``S#L``,P[``"*.P``BCL``!H[```:.P``'#L``!P[``!0.P``4#L``$H[ -M``!*.P``%CL``!8[``#<.@``W#H``(`X``"`.```>KL``'J[``#[NP``^[L` -M``B\```(O```OKL``+Z[``#PN@``\+H``#8[```V.P``XCL``.([```9/``` -M&3P``#`\```P/```.3P``#D\`(`7/`"`%SP``(([``"".P``(+L``""[``#[ -MNP``^[L``#F\```YO```6+P``%B\``!!O```0;P``,B[``#(NP``"CL```H[ -M```;/```&SP``%T\``!=/`"`4SP`@%,\`(`,/`"`##P``&P[``!L.P``@#D` -M`(`Y```*NP``"KL``%J[``!:NP``'+L``!R[`````````````'@Z``!X.@`` -MX+D``."Y```[``"GNP``C+H``(RZ``!X.P``>#L``.([``#B -M.P``W#L``-P[``"@.P``H#L``&([``!B.P```#L````[``"`.0``@#D``"`Y -M```@.0``&CL``!H[``",.P``C#L``%X[``!>.P```#D````Y``!BNP``8KL` -M`+F[``"YNP``J;L``*F[``#PN@``\+H``"0[```D.P``OCL``+X[`(`%/`"` -M!3P`@!0\`(`4/```W#L``-P[``#8.@``V#H``"B[```HNP``@[L``(.[``!P -MNP``<+L``#B[```XNP``T+H``-"Z``!@.0``8#D``"`[```@.P``I#L``*0[ -M``#B.P``XCL``-8[``#6.P``@CL``(([``"P.@``L#H``-"Y``#0N0``&+L` -M`!B[``!JNP``:KL```2[```$NP``\#H``/`Z``"J.P``JCL``,X[``#..P`` -MFSL``)L[``"D.@``I#H```:[```&NP``1KL``$:[``#@N@``X+H``*`Y``"@ -M.0``$CL``!([``"0.P``D#L``+H[``"Z.P``@3L``($[``"P.0``L#D``+RZ -M``"\N@``\+D``/"Y``#$.@``Q#H``&([``!B.P``G#L``)P[``"5.P``E3L` -M`%H[``!:.P``#CL```X[```X.@``.#H``-BZ``#8N@``6+L``%B[```NNP`` -M+KL``$"Z``!`N@``(#H``"`Z``#\.@``_#H``)4[``"5.P``]CL``/8[`(`+ -M/`"`"SP``.([``#B.P``/#L``#P[```@NP``(+L``,J[``#*NP``QKL``,:[ -M``!(NP``2+L``*@Z``"H.@``V3L``-D[`(`]/`"`/3P``$X\``!./```"CP` -M``H\``#<.@``W#H``(B[``"(NP``];L``/6[```"O````KP``+>[``"WNP`` -M]+H``/2Z``#<.@``W#H``*H[``"J.P``"SP```L\```>/```'CP```<\```' -M/```UCL``-8[``"V.P``MCL``&0[``!D.P``H+D``*"Y``!RNP``NP``K+L``*R[``";NP``F[L``."Z``#@N@``N#H``+@Z -M``!R.P``CL``"X[```N.P``@#H``(`Z``#,N@``S+H``)*[ -M``"2NP``T;L``-&[``#MNP``[;L``.J[``#JNP``L+L``+"[``!HN@``:+H` -M`(X[``"..P``]SL``/<[```'/```!SP`@`<\`(`'/```^SL``/L[``"W.P`` -MMSL``!0[```4.P``X+H``."Z``#$NP``Q+L`@`J\`(`*O```\[L``/.[``"% -MNP``A;L```"Z````N@``7#L``%P[``#K.P``ZSL```<\```'/```ESL``)<[ -M``"DN@``I+H``+*[``"RNP``TKL``-*[``"8NP``F+L``!"Z```0N@``@CL` -M`(([``#5.P``U3L``-L[``#;.P``JCL``*H[``#`.@``P#H``'*[``!RNP`` -MXKL``.*[``#3NP``T[L``(R[``",NP``'+L``!R[``!@N0``8+D``#0[```T -M.P``M#L``+0[``#F.P``YCL``.`[``#@.P``D#L``)`[``!@.0``8#D``&R[ -M``!LNP``N[L``+N[``#ENP``Y;L``.Z[``#NNP``F;L``)F[``"`N```@+@` -M`$8[``!&.P``5CL``%8[```..P``#CL``*@Z``"H.@``K#H``*PZ```X.P`` -M.#L``*([``"B.P``N#L``+@[``"#.P``@SL``)0Z``"4.@``+KL``"Z[``#] -MNP``_;L`@$*\`(!"O`"`/+P`@#R\``#7NP``U[L``!BZ```8N@``BSL``(L[ -M``#\.P``_#L`@!,\`(`3/````3P```$\``"Q.P``L3L```H[```*.P``"+L` -M``B[``"YNP``N;L``-^[``#?NP``R;L``,F[``"NNP``KKL``':[``!VNP`` -M``````````"+.P``BSL``,`[``#`.P``CCL``(X[``#`.@``P#H``-BZ``#8 -MN@``NP``7KL``-"Z``#0N@``@+@``("X``"<.@``G#H```H[ -M```*.P``8#H``&`Z``!`NP``0+L``+V[``"]NP``H[L``*.[``#0N@``T+H` -M`/0Z``#T.@``D3L``)$[``"P.P``L#L``($[``"!.P``8#H``&`Z```,NP`` -M#+L``)^[``"?NP``XKL``.*[``#0NP``T+L``#B[```XNP``"#H```@Z```` -M.P```#L``$([``!".P``D#L``)`[``"5.P``E3L``!H[```:.P``J+H``*BZ -M``"INP``J;L```:\```&O```#+P```R\``"VNP``MKL``)RZ``"NP`` -M[KL``.Z[``#FNP``YKL``)Z[``">NP``R+H``,BZ``"(.@``B#H```0[```$ -M.P``!#L```0[``"8.@``F#H``("Z``"`N@``/KL``#Z[```HNP``*+L``'BZ -M``!XN@``0+H``$"Z```$NP``!+L``!R[```[``"0N@``D+H``.@Z -M``#H.@``^#H``/@Z``!8.@``6#H``,`X``#`.```@+D``("Y```(.@``"#H` -M`!P[```<.P``9CL``&8[```L.P``+#L``"@Z```H.@``!+L```2[``#2NP`` -MTKL``#.\```SO`"`.;P`@#F\``#HNP``Z+L``.RZ``#LN@``##L```P[``"D -M.P``I#L``,([``#".P``E#L``)0[```*.P``"CL``,"X``#`N```$KL``!*[ -M``!DNP``9+L``#:[```VNP``G+H``)RZ``"8N@``F+H``!"[```0NP``"KL` -M``J[``"$N@``A+H``*RZ``"LN@``/KL``#Z[``".NP``CKL``*:[``"FNP`` -ME[L``)>[``#TN@``]+H```0[```$.P``GCL``)X[``#-.P``S3L``-\[``#? -M.P``FCL``)H[``!XN@``>+H``.&[``#ANP``%[P``!>\```%O```!;P``+N[ -M``"[NP``/+L``#R[``#`N```P+@``(0Z``"$.@``.#H``#@Z```H.@``*#H` -M`&@Z``!H.@``6#H``%@Z````.P```#L``)H[``":.P``O3L``+T[```X.P`` -M.#L``.2Z``#DN@``I[L``*>[``#XNP``^+L`@!V\`(`=O`"`([P`@".\```# -MO````[P``).[``"3NP``8#D``&`Y``"_.P``OSL`@"<\`(`G/```/SP``#\\ -M`(`U/`"`-3P```X\```./```*CL``"H[``#-NP``S;L``%J\``!:O```?[P` -M`'^\`(!>O`"`7KP`@`J\`(`*O```O+H``+RZ``";.P``FSL`@``\`(``/`"` -M!SP`@`<\``#;.P``VSL``$`[``!`.P``I+H``*2Z```NNP``+KL``+BZ``"X -MN@``:+H``&BZ```,NP``#+L``#Z[```^NP``-+L``#2[```PNP``,+L``!Z[ -M```>NP``G+H``)RZ```@N0``(+D``-`Y``#0.0``N#H``+@Z```:.P``&CL` -M`-@Z``#8.@``@#D``(`Y``#`.0``P#D``(PZ``",.@``L+D``+"Y``!0NP`` -M4+L``)2[``"4NP``>KL``'J[```4NP``%+L``"`Y```@.0``*CL``"H[``!6 -M.P``5CL````[````.P``P#@``,`X```4NP``%+L``*"[``"@NP``I+L``*2[ -M``!PN@``<+H``),[``"3.P``[CL``.X[``#E.P``Y3L``)<[``"7.P``T#D` -M`-`Y``"=NP``G;L```N\```+O```(;P``"&\```6O```%KP``,2[``#$NP`` -M&+H``!BZ``"".P``@CL``+X[``"^.P``WCL``-X[```'/```!SP`@``\`(`` -M/```BCL``(H[```8N@``&+H``)"[``"0NP``X+L``."[``#SNP``\[L``+B[ -M``"XNP``-+L``#2[```HN@``*+H``(0Z``"$.@``#CL```X[``"H.@``J#H` -M`*RZ``"LN@``[+H``.RZ``"$.@``A#H``(H[``"*.P``LCL``+([``"C.P`` -MHSL``#([```R.P``L+H``+"Z``"MNP``K;L``.^[``#ONP``[;L``.V[``"E -MNP``I;L``("Y``"`N0``J#L``*@[``#O.P``[SL``,4[``#%.P``B3L``(D[ -M```J.P``*CL``)"Y``"0N0``=KL``':[``"_NP``O[L``,^[``#/NP``M[L` -M`+>[``!`NP``0+L``'`Z``!P.@``>CL``'H[``"Y.P``N3L``.8[``#F.P`` -MTSL``-,[```4.P``%#L``#Z[```^NP``JKL``*J[``"-NP``C;L``":[```F -MNP``\+D``/"Y``#0.@``T#H```P[```,.P``<#H``'`Z``"0N0``D+D``(RZ -M``",N@``T+H``-"Z``#PN0``\+D``#H[```Z.P``J#L``*@[``!X.P``>#L` -M`/`Y``#P.0``!KL```:[``!PNP``<+L``)6[``"5NP``?KL``'Z[``#$N@`` -MQ+H``(PZ``",.@``9CL``&8[``"W.P``MSL``,$[``#!.P``NP"`$;P`@!&\``#0NP``T+L``,`X``#`.``` -MNCL``+H[```!/````3P`@`$\`(`!/```TCL``-([```T.P``-#L``,2Z``#$ -MN@``BKL``(J[``"SNP``L[L``+:[``"VNP``@+L``("[``"PN@``L+H````` -M````````,#H``#`Z```T.P``-#L``+0[``"T.P``OCL``+X[``!P.P``<#L` -M`-`Z``#0.@``(#D``"`Y``"HN@``J+H```*[```"NP``Y+H``.2Z``#XN@`` -M^+H``!Z[```>NP``#+L```R[``#[``"9NP``F;L``.2Z``#DN@``/#L``#P[``#A -M.P``X3L`@!(\`(`2/```&CP``!H\``#].P``_3L``%([``!2.P``&KL``!J[ -M``#:NP``VKL`@""\`(`@O```-KP``#:\```$O```!+P``(BZ``"(N@``I3L` -M`*4[``#J.P``ZCL``-,[``#3.P``BCL``(H[``"P.0``L#D``&"[``!@NP`` -MG+L``)R[``!RNP``NP``P[L``,.[```:NP``&KL``"`[```@.P``P3L``,$[``#Q -M.P``\3L``.`[``#@.P``:CL``&H[``"0N@``D+H``(6[``"%NP``I+L``*2[ -M``#'NP``Q[L``-Z[``#>NP``E[L``)>[```@N0``(+D``&H[``!J.P``NSL` -M`+L[``#5.P``U3L``*$[``"A.P``8#H``&`Z``!>NP``7KL``-*[``#2NP`` -M"+P```B\``#TNP``]+L``"B[```HNP``7#L``%P[``#$.P``Q#L``*H[``"J -M.P``7CL``%X[``!X.@``>#H```:[```&NP``<+L``'"[```ZNP``.KL``*"Z -M``"@N@``8#D``&`Y``#4.@``U#H``-`Z``#0.@``4+H``%"Z``!"NP``0KL` -M`""[```@NP``B+H``(BZ``"0N@``D+H``*2Z``"DN@``@+@``("X``!@.@`` -M8#H``"@Z```H.@```#H````Z```H.@``*#H``(`X``"`.```2+H``$BZ```X -MN@``.+H``&BZ``!HN@``U+H``-2Z```0N@``$+H``"P[```L.P``C3L``(T[ -M```D.P``)#L``&BZ``!HN@``A;L``(6[``#HNP``Z+L```R\```,O```U+L` -M`-2[``#$N@``Q+H``(,[``"#.P"`$#P`@!`\`(`Y/`"`.3P``!,\```3/``` -M(#L``"`[``!VNP``=KL``.R[``#LNP"`%;P`@!6\```>O```'KP``.N[``#K -MNP``,+L``#"[``"T.@``M#H``)`[``"0.P``UCL``-8[``#>.P``WCL``*L[ -M``"K.P``:CL``&H[``#@.@``X#H``.2Z``#DN@``K;L``*V[``"[NP``N[L` -M`&J[``!JNP``#+L```R[````NP```+L``."Z``#@N@```+L```"[``!"NP`` -M0KL``$*[``!"NP``8+H``&"Z```*.P``"CL``+`[``"P.P"`#SP`@`\\```> -M/```'CP``+L[``"[.P``0+H``$"Z``"WNP``M[L```:\```&O```&KP``!J\ -M```'O```![P``(^[``"/NP``P+@``,"X```L.P``+#L``(4[``"%.P``B3L` -M`(D[```H.P``*#L``'`Z``!P.@``6#H``%@Z```(.@``"#H``+"Z``"PN@`` -M)+L``"2[``"DN@``I+H``,`Y``#`.0``0#H``$`Z```(.@``"#H``(`X``"` -M.```S+H``,RZ``!NNP``;KL``(V[``"-NP``;KL``&Z[```(NP``"+L``,0Z -M``#$.@``Q3L``,4[``#Y.P``^3L``)L[``";.P```#H````Z```RNP``,KL` -M`+"[``"PNP``W+L``-R[``"ONP``K[L```R[```,NP``\#D``/`Y``#X.@`` -M^#H``!0[```4.P``4#H``%`Z``#`N@``P+H``+BZ``"XN@``E#H``)0Z``!& -M.P``1CL``%0[``!4.P``:CL``&H[``!V.P``=CL````[````.P``O+H``+RZ -M``"4NP``E+L``.V[``#MNP"`&+P`@!B\`(`8O`"`&+P``..[``#CNP``9KL` -M`&:[``"<.@``G#H````\````/`"`7#P`@%P\`(!F/`"`9CP``"$\```A/``` -M<#L``'`[``!,NP``3+L``!Z\```>O`"`5KP`@%:\``!$O```1+P`@`.\`(`# -MO```2KL``$J[```".P```CL``*,[``"C.P``CCL``(X[```:.P``&CL``/@Z -M``#X.@``"#L```@[``"(.@``B#H``$"Y``!`N0``8+D``&"Y``#PN0``\+D` -M`/2Z``#TN@``2+L``$B[``!:NP``6KL``&:[``!FNP``4+L``%"[``#8N@`` -MV+H``."Y``#@N0``(+H``""Z`````````````"8[```F.P``CCL``(X[``!6 -M.P``5CL``'`Z``!P.@``L+H``+"Z``!\NP``?+L``+V[``"]NP``L;L``+&[ -M``!*NP``2KL``,"Y``#`N0``'CL``!X[``"&.P``ACL``!8[```6.P``'+L` -M`!R[``"[NP``N[L``+N[``"[NP``AKL``(:[``#HN@``Z+H```([```".P`` -MM#L``+0[``#..P``SCL``)`[``"0.P``J#H``*@Z```RNP``,KL``-6[``#5 -MNP"``;P`@`&\``#CNP``X[L``,6[``#%NP``I[L``*>[```&NP``!KL``!H[ -M```:.P``H#L``*`[``"E.P``I3L``(8[``"&.P```CL```([``#8N@``V+H` -M`)R[``"KL``(*[``""NP``9KL``&:[``#D -MN@``Y+H``+@Z``"X.@``;CL``&X[``!<.P``7#L``)@Z``"8.@``&+L``!B[ -M``#:NP``VKL`@!6\`(`5O```_KL``/Z[``"9NP``F;L``!B[```8NP``@#@` -M`(`X``!<.P``7#L``+0[``"T.P``GSL``)\[```T.P``-#L``*`Y``"@.0`` -M2+L``$B[``#6NP``UKL`@`B\`(`(O`"`"[P`@`N\``#WNP``][L``(R[``", -MNP``<#H``'`Z``").P``B3L``&`[``!@.P``@#H``(`Z``"8N@``F+H``%J[ -M``!:NP``J;L``*F[``"=NP``G;L``"Z[```NNP``:+H``&BZ``"`N```@+@` -M`("Y``"`N0``#+L```R[``"9NP``F;L``)V[``"=NP``$+L``!"[``"`.``` -M@#@``"`Z```@.@``@#H``(`Z``!@.@``8#H``*2Z``"DN@``D[L``).[``#9 -MNP``V;L``/6[``#UNP``\;L``/&[``"RNP``LKL``!R[```.P``L#L``+`[``"$.P``A#L``)"Y``"0N0``D+L` -M`)"[``#TNP``]+L``!V\```=O`"`&+P`@!B\``"^NP``OKL``*BZ``"HN@`` -M&CL``!H[``"3.P``DSL``'`[``!P.P``A+H``(2Z``#4NP``U+L`@`^\`(`/ -MO`"``;P`@`&\``#&NP``QKL``$J[``!*NP``D#H``)`Z``"&.P``ACL``(@[ -M``"(.P``'#L``!P[`````````````$*[``!"NP``K[L``*^[``#'NP``Q[L` -M`-&[``#1NP``\+L``/"[``#DNP``Y+L``(*[``""NP``*+H``"BZ``!@.@`` -M8#H``,PZ``#,.@``!CL```8[``"<.@``G#H``&"Z``!@N@``&+L``!B[``!` -MNP``0+L``%R[``!\```WO``` -M1KP``$:\`(` -M.P``ACL``(8[``!J.P``:CL``#0[```T.P``R#H``,@Z``!@N@``8+H``$J[ -M``!*NP``B+L``(B[``"NP``H[L``*.[``#$NP``Q+L``*2[``"DNP``7KL``%Z[``!0N@`` -M4+H``$8[``!&.P``MSL``+<[``"H.P``J#L``!@[```8.P``_+H``/RZ``#B -MNP``XKL``"*\```BO`"`#KP`@`Z\``"KNP``J[L``"*[```BNP``H+D``*"Y -M````.P```#L``#P[```\.P``K#H``*PZ``!0N@``4+H```"[````NP``++L` -M`"R[``!*NP``2KL``!Z[```>NP``V+H``-BZ``#4N@``U+H``(RZ``",N@`` -M*#H``"@Z``!0.@``4#H```:[```&NP``N+L``+B[``#TNP``]+L```*\```" -MO```Y[L``.>[``!LNP``;+L``.@Z``#H.@``T#L``-`[`(`0/`"`$#P`@`X\ -M`(`./```J#L``*@[``!PN@``<+H``+:[``"VNP``Z;L``.F[``#TNP``]+L` -M`/Z[``#^NP``W;L``-V[``":NP``FKL``&2[``!DNP``/KL``#Z[``"PN@`` -ML+H``(PZ``",.@``8CL``&([``"].P``O3L``.P[``#L.P``O#L``+P[```` -M.P```#L``*2Z``"DN@``8+L``&"[``#-NP``S;L``!2\```4O`"`&;P`@!F\ -M``#TNP``]+L``*"[``"@NP``P+H``,"Z``!*.P``2CL``.,[``#C.P"`"SP` -M@`L\```#/````SP``)X[``">.P``O+H``+RZ`(`!O`"``;P``"Z\```NO`"` -M%[P`@!>\``#*NP``RKL```J[```*NP``,CL``#([``##.P``PSL``+H[``"Z -M.P``3CL``$X[````.0```#D``#"[```PNP``>KL``'J[```DNP``)+L``"BZ -M```HN@``D+D``)"Y``"PN0``L+D``!@Z```8.@``C#H``(PZ``!@N0``8+D` -M`.RZ``#LN@``'+L``!R[```RNP``,KL``$R[``!,NP``(KL``"*[``!@N@`` -M8+H``&@Z``!H.@``1#L``$0[``"F.P``ICL``)T[``"=.P``N#H``+@Z``#\ -MN@``_+H``#R[```\NP``*KL``"J[```"NP```KL``)"Y``"0N0``\#H``/`Z -M```(.P``"#L````Y````.0``'+L``!R[``"5NP``E;L``+*[``"RNP``6KL` -M`%J[``"H.@``J#H``*,[``"C.P``R#L``,@[``#!.P``P3L``)\[``"?.P`` -M]#H``/0Z```(NP``"+L``)6[``"5NP``J+L``*B[``"9NP``F;L``%2[``!4 -MNP``@+H``("Z``"`.@``@#H``"H[```J.P``G#L``)P[``#/.P``SSL``*([ -M``"B.P``M#H``+0Z``"\N@``O+H``#B[```XNP``=KL``':[``!LNP``;+L` -M`+RZ``"\N@``B#H``(@Z```(.P``"#L``/PZ``#\.@``8#H``&`Z``"+L``'B[````N````+@``)X[``">.P"`"SP`@`L\```C/```(SP``!(\```2 -M/```FCL``)H[``"XN@``N+H``+R[``"\NP``SKL``,Z[``"?NP``G[L``$*[ -M``!"NP``8+D``&"Y```P.P``,#L``&8[``!F.P``'#L``!P[``"H.@``J#H` -M`(`Z``"`.@``L#H``+`Z```T.P``-#L``)$[``"1.P``CCL``(X[```4.P`` -M%#L``$`Z``!`.@``H#D``*`Y```(N@``"+H``.2Z``#DN@``S+H``,RZ``"` -MN0``@+D``!`Z```0.@``<#H``'`Z``#8.@``V#H```X[```..P``%#L``!0[ -M```B.P``(CL```@[```(.P```+@```"X```0NP``$+L``.RZ``#LN@``<#H` -M`'`Z``!F.P``9CL``+,[``"S.P``XSL``.,[``#0.P``T#L``#`[```P.P`` -M#KL```Z[``#"NP``PKL``/>[``#WNP``P;L``,&[``!(N@``2+H``*([``"B -M.P"`!#P`@`0\`(`./`"`#CP```0\```$/```N#L``+@[``"<.@``G#H``$"[ -M``!`NP``G+L``)R[``"BNP``HKL``(V[``"-NP``)+L``"2[``#`.```P#@` -M`!H[```:.P``FCL``)H[``#L.P``[#L``/X[``#^.P``LSL``+,[```L.P`` -M+#L``*`Z``"@.@``X#D``.`Y``#0N0``T+D``."Y``#@N0``D#D``)`Y```` -M.````#@``,2Z``#$N@``6+L``%B[``"5NP``E;L``(V[``"-NP``@+H``("Z -M``"=.P``G3L`@!P\`(`.P``'CL``(`[``"`.P``MCL``+8[``#E.P``Y3L``,L[``#+ -M.P``<#L``'`[``#8.@``V#H``"@Z```H.@```+H```"Z``"(N@``B+H``,"X -M``#`N```M#H``+0Z``#P.@``\#H``+PZ``"\.@``@#D``(`Y``#@N@``X+H` -M`"J[```JNP``P+D``,"Y``!L.P``;#L``-0[``#4.P``]CL``/8[``#W.P`` -M]SL``+\[``"_.P``^#H``/@Z```$NP``!+L``'"[``!PNP``3+L``$R[``"8 -MN@``F+H``-`Z``#0.@``5#L``%0[```@.P``(#L``%@Z``!8.@``H#D``*`Y -M``"@.0``H#D``,"X``#`N```"#H```@Z``!$.P``1#L``+D[``"Y.P``Y3L` -M`.4[``#S.P``\SL``.@[``#H.P``JCL``*H[```&.P``!CL``+RZ``"\N@`` -MM;L``+6[`(`7O`"`%[P`@!Z\`(`>O```MKL``+:[```(.@``"#H``,L[``#+ -M.P``,SP``#,\``!>/```7CP`@$8\`(!&/```Z3L``.D[``"D.@``I#H``&2[ -M``!DNP``Q[L``,>[``"YNP``N;L``#B[```XNP``8+D``&"Y``"$.@``A#H` -M`!`[```0.P``=#L``'0[``"&.P``ACL``&0[``!D.P``<#L``'`[``"1.P`` -MD3L``(0[``"$.P``*CL``"H[``"0.@``D#H``/"Y``#PN0``Y+H``.2Z``#T -MN@``]+H``+2Z``"TN@``T+H``-"Z``#\N@``_+H``+"Y``"PN0``-CL``#8[ -M``"N.P``KCL``.$[``#A.P"``3P`@`$\``#T.P``]#L``(\[``"/.P``@#@` -M`(`X``!NNP``;KL``+V[``"]NP``J[L``*N[``#`N@``P+H``#H[```Z.P`` -MG#L``)P[``"-.P``C3L``%`[``!0.P``J#H``*@Z``"\N@``O+H``$*[``!" -MNP``V+H``-BZ``"@.@``H#H``'P[``!\.P``NCL``+H[``#-.P``S3L``*`[ -M``"@.P``/CL``#X[``#0.@``T#H`````````````++L``"R[``".NP``CKL` -M`':[``!VNP```+L```"[``!@N0``8+D```H[```*.P``ESL``)<[``"Z.P`` -MNCL``)@[``"8.P``&#L``!@[``#@N0``X+D``"Z[```NNP``(+L``""[``#@ -M.0``X#D``%@[``!8.P``B3L``(D[``!V.P``=CL``!X[```>.P``\+D``/"Y -M``!NNP``;KL``)B[``"8NP``-KL``#:[```(.@``"#H``(8[``"&.P``USL` -M`-<[``#4.P``U#L``($[``"!.P``H#H``*`Z``!(N@``2+H``$2[``!$NP`` -MH[L``*.[``"6NP``EKL```:[```&NP``X#D``.`Y```D.P``)#L``)X[``"> -M.P``UCL``-8[``#4.P``U#L``)X[``">.P``\#H``/`Z```0NP``$+L``+"[ -M``"PNP``L;L``+&[```VNP``-KL````Y````.0``&CL``!H[``"-.P``C3L` -M`*$[``"A.P``5#L``%0[```H.@``*#H``$BZ``!(N@``2+H``$BZ``"`.0`` -M@#D``.@Z``#H.@``&CL``!H[```P.@``,#H``.2Z``#DN@``-KL``#:[```N -MNP``+KL```J[```*NP``L+D``+"Y``!(.P``2#L``,4[``#%.P``WCL``-X[ -M``#`.P``P#L``($[``"!.P``D#H``)`Z``#PN@``\+H``'J[``!ZNP``K;L` -M`*V[``#4NP``U+L``+B[``"XNP``_+H``/RZ```D.P``)#L``+8[``"V.P`` -M\CL``/([``#_.P``_SL``+4[``"U.P``A#H``(0Z``!BNP``8KL``,"[``#` -MNP``M;L``+6[```>NP``'KL``!`[```0.P``K3L``*T[``"P.P``L#L``'`[ -M``!P.P``P#H``,`Z``#$N@``Q+H``(J[``"*NP``E+L``)2[```:NP``&KL` -M`)`Y``"0.0``%#L``!0[``!>.P``7CL``&`[``!@.P``+#L``"P[```&.P`` -M!CL``,`Z``#`.@``0+D``$"Y```:NP``&KL``$2[``!$NP```KL```*[``!@ -MN@``8+H``(`Y``"`.0``$CL``!([``"$.P``A#L``(8[``"&.P``+CL``"X[ -M``!@.@``8#H``%"Z``!0N@``I+H``*2Z```@.0``(#D```0[```$.P``"CL` -M``H[``#0.0``T#D``-BZ``#8N@``;+L``&R[``"PNP``L+L``+"[``"PNP`` -M%+L``!2[```R.P``,CL``.P[``#L.P``(CP``"(\`(`B/`"`(CP``.@[``#H -M.P``0CL``$([````N@```+H``%R[``![``"GNP``\+H``/"Z```^.P``/CL``.\[``#O.P"`(#P`@"`\ -M`(`4/`"`%#P``*X[``"N.P``:#H``&@Z```>NP``'KL``)^[``"?NP``P[L` -M`,.[``"7NP``E[L``+RZ``"\N@``I#H``*0Z```V.P``-CL``'@[``!X.P`` -MCCL``(X[``"#.P``@SL``$X[``!..P``^#H``/@Z````.````#@``,2Z``#$ -MN@``@+H``("Z``"4.@``E#H``#@[```X.P``6CL``%H[``!4.P``5#L```0[ -M```$.P``B+H``(BZ``"5NP``E;L``,F[``#)NP``I[L``*>[``#HN@``Z+H` -M`$`[``!`.P``X#L``.`[`(`"/`"``CP``-\[``#?.P``I#L``*0[```X.P`` -M.#L``"`Y```@.0``Z+H``.BZ``#TN@``]+H``*"Z``"@N@``N+H``+BZ```, -MNP``#+L``":[```FNP``&KL``!J[``"NP``'KL``*BZ``"HN@``(#D``"`Y```P -M.@``,#H``#@Z```X.@``8#H``&`Z``#0.0``T#D``(RZ``",N@``(+L``""[ -M```XNP``.+L``#"[```PNP``/KL``#Z[``!4NP``5+L``$J[``!*NP``$KL` -M`!*[```8N@``&+H``,`Z``#`.@``&#L``!@[``"(.@``B#H``)BZ``"8N@`` -M2+L``$B[``"8NP``F+L``+6[``"UNP``C+L``(R[``",N@``C+H```0[```$ -M.P``4#L``%`[``#\.@``_#H``*2Z``"DN@``I+L``*2[``#PNP``\+L``.>[ -M``#GNP``IKL``*:[```8NP``&+L``)@Z``"8.@``D3L``)$[``"S.P``LSL` -M`(([``"".P``R#H``,@Z``!HN@``:+H``&B[``!HNP``Q[L``,>[`(``O`"` -M`+P```Z\```.O```!+P```2\``"VNP``MKL``/RZ``#\N@``@#H``(`Z```> -M.P``'CL``$([``!".P``,#L``#`[``"(.@``B#H``%BZ``!8N@``M+H``+2Z -M```@N@``(+H`````````````H+D``*"Y```,NP``#+L``+&[``"QNP``#+P` -M``R\`(`;O`"`&[P`@`6\`(`%O```L+L``+"[``#(N@``R+H``$0[``!$.P`` -MR3L``,D[``#,.P``S#L``'8[``!V.P``$#H``!`Z```>NP``'KL``)Z[``"> -MNP``S;L``,V[``#=NP``W;L``-N[``#;NP``M;L``+6[``!`NP``0+L``$"Y -M``!`N0``M#H``+0Z```..P``#CL``"`[```@.P``O#H``+PZ``!XN@``>+H` -M`%:[``!6NP``B;L``(F[``!PNP``<+L``"*[```BNP``P+H``,"Z``#8N@`` -MV+H``%:[``!6NP``H;L``*&[``"JNP``JKL``(R[``",NP``)KL``":[``#0 -M.0``T#D``&X[``!N.P``I3L``*4[``!L.P``;#L```@Z```(.@``,+L``#"[ -M``"@NP``H+L``+&[``"QNP``C[L``(^[``!$NP``1+L```:[```&NP``L+H` -M`+"Z``!HN@``:+H``,RZ``#,N@``0KL``$*[``!LNP``;+L``#Z[```^NP`` -M"+L```B[``"LN@``K+H``("X``"`N```Z#H``.@Z``!J.P``:CL``*`[``"@ -M.P``DSL``),[``#,.@``S#H``$Z[``!.NP``]+L``/2[`(`FO`"`)KP``#*\ -M```RO`"`$KP`@!*\``""NP``@KL``!8[```6.P``XCL``.([```(/```"#P` -M`.,[``#C.P``:#L``&@[``#@N0``X+D``%R[``!.P`` -MR3L``,D[``"B.P``HCL``.PZ``#L.@``!+L```2[``":NP``FKL``*6[``"E -MNP``;KL``&Z[``"\N@``O+H``(@Z``"(.@``3CL``$X[``!(.P``2#L``$`Z -M``!`.@```+L```"[``!>NP``7KL``&J[``!JNP``/+L``#R[``#@N@``X+H` -M``"Z````N@``"#H```@Z``#0.@``T#H``!X[```>.P``&#L``!@[``",.@`` -MC#H``&"Y``!@N0``E+H``)2Z```2NP``$KL``%*[``!2NP``++L``"R[```` -MN@```+H```([```".P``7CL``%X[``!*.P``2CL``'`Z``!P.@```KL```*[ -M``"`NP``@+L``(6[``"%NP``/KL``#Z[```PN@``,+H```H[```*.P``@3L` -M`($[``!4.P``5#L````Z````.@``&KL``!J[``!^NP``?KL``'J[``!ZNP`` -M!KL```:[```H.@``*#H``#H[```Z.P``ACL``(8[``"6.P``ECL``'H[``!Z -M.P``P#H``,`Z``"LN@``K+H``$Z[``!.NP``BKL``(J[``"ENP``I;L``*2[ -M``"DNP``6+L``%B[```@N0``(+D``%([``!2.P``MCL``+8[``#%.P``Q3L` -M`(@[``"(.P``F#H``)@Z``#(N@``R+H``%B[``!8NP``A;L``(6[``!$NP`` -M1+L``*"Y``"@N0``!#L```0[```D.P``)#L``,0Z``#$.@``(#D``"`Y```0 -MN@``$+H``&"Y``!@N0``<#H``'`Z``#P.@``\#H``.@Z``#H.@``6#H``%@Z -M```0N@``$+H``"J[```JNP``E+L``)2[``"[``"' -MNP```+L```"[``!P.@``<#H``&8[``!F.P``KCL``*X[``"Z.P``NCL``(X[ -M``"..P``!CL```8[``"0.0``D#D``$"Z``!`N@``8+H``&"Z``"@.0``H#D` -M`"X[```N.P``D#L``)`[``!\.P``?#L``*`Z``"@.@``"+L```B[``"8NP`` -MF+L``+2[``"TNP``B;L``(F[``"XN@``N+H``,0Z``#$.@``=CL``'8[``"G -M.P``ISL``*`[``"@.P``6#L``%@[``#X.@``^#H``.PZ``#L.@``"#L```@[ -M``#@.@``X#H``$`Z``!`.@``$+H``!"Z``#LN@``[+H``"R[```LNP``-+L` -M`#2[```6NP``%KL``+2Z``"TN@``@#D``(`Y```<.P``'#L``(0[``"$.P`` -MG3L``)T[``"P.P``L#L``+P[``"\.P``E3L``)4[``"P.@``L#H``#:[```V -MNP``S[L``,^[```#O````[P``-^[``#?NP``0KL``$*[```&.P``!CL``-@[ -M``#8.P``'#P``!P\```@/```(#P``-\[``#?.P``_#H``/PZ```4NP``%+L` -M`(N[``"+NP``C[L``(^[``!>NP``7KL``."Z``#@N@``@+@``("X``"$.@`` -MA#H``-PZ``#<.@```CL```([``#8.@``V#H``)PZ``"<.@``L#H``+`Z``#, -M.@``S#H``*`Z``"@.@``4#H``%`Z``"@.@``H#H``!([```2.P``*CL``"H[ -M``#D.@``Y#H````Y````.0``[+H``.RZ``!2NP``4KL``$"[``!`NP``E+H` -M`)2Z``"<.@``G#H``&@[``!H.P``KSL``*\[``"G.P``ISL``!([```2.P`` -MO+H``+RZ``!VNP``=KL``'Z[``!^NP``%+L``!2[``"0.0``D#D``#([```R -M.P``@SL``(,[``!Z.P``>CL``"X[```N.P``8#H``&`Z``"(N@``B+H``.RZ -M``#LN@``<+H``'"Z``!@.0``8#D``#`Z```P.@``B#H``(@Z``#H.@``Z#H` -M`!H[```:.P``'CL``!X[``#T.@``]#H```@Z```(.@``F+H``)BZ```>NP`` -M'KL``"J[```JNP``]+H``/2Z``#PN0``\+D``-0Z``#4.@``=#L``'0[``") -M.P``B3L``"8[```F.P``H#D``*`Y``"`N@``@+H``(BZ``"(N@```#D````Y -M````.P```#L``%8[``!6.P``2CL``$H[``#<.@``W#H``""Z```@N@``4+L` -M`%"[``"=NP``G;L``(2[``"$NP``G+H``)RZ``#P.@``\#H``&X[``!N.P`` -MCCL``(X[``"(.P``B#L``#P[```\.P``:#H``&@Z``"0N@``D+H``#B[```X -MNP``;+L``&R[```\NP``/+L``("Z``"`N@``H#H``*`Z``!N.P``;CL``,D[ -M``#).P``[SL``.\[``"Z.P``NCL``-`Z``#0.@``4+L``%"[``#@NP``X+L` -M@`B\`(`(O```Z;L``.F[``!DNP``9+L``$@Z``!(.@``CCL``(X[``#6.P`` -MUCL``-@[``#8.P``CSL``(\[``#T.@``]#H``"@Z```H.@``"#H```@Z``"` -M.0``@#D``+"Y``"PN0``A+H``(2Z``#TN@``]+H``"Z[```NNP``-+L``#2[ -M```&NP``!KL``&BZ``!HN@``$#H``!`Z```4.P``%#L``%0[``!4.P``0#L` -M`$`[```<.P``'#L``!H[```:.P```#L````[```0.@``$#H``*BZ``"HN@`` -M/+L``#R[``!RNP``.P``ISL``*<[``#%.P``Q3L``+T[ -M``"].P``B3L``(D[``#D.@``Y#H``'BZ``!XN@``;KL``&Z[``"WNP``M[L` -M`,F[``#)NP``K[L``*^[``!LNP``;+L``$"Z``!`N@``/#L``#P[``"K.P`` -MJSL``*@[``"H.P``;CL``&X[```2.P``$CL``)PZ``"<.@``,#H``#`Z``!0 -M.@``4#H``%@Z``!8.@````````````#0N@``T+H``&J[``!JNP``M;L``+6[ -M``#2NP``TKL``)Z[``">NP``B+H``(BZ```Z.P``.CL``*@[``"H.P``NCL` -M`+H[``">.P``GCL``#0[```T.P``H#D``*`Y``"PN@``L+H```R[```,NP`` -M#+L```R[``"@N@``H+H``,"X``#`N```8#D``&`Y``#`N```P+@```"Y```` -MN0`````````````PN@``,+H``!*[```2NP``8+L``&"[``!+L``":[```FNP``(+H``""Z``!`.@``0#H``-PZ``#<.@``%CL` -M`!8[```<.P``'#L```P[```,.P```CL```([``#P.@``\#H``*0Z``"D.@`` -M@#D``(`Y``"$N@``A+H``$Z[``!.NP``N[L``+N[``#DNP``Y+L``+.[``"S -MNP``%KL``!:[``"8.@``F#H``(0[``"$.P``L3L``+$[``"2.P``DCL```([ -M```".P``6+H``%BZ``!&NP``1KL``(&[``"!NP``/KL``#Z[``!`N@``0+H` -M`%`Z``!0.@``4#H``%`Z``"`.0``@#D``""Y```@N0``.+H``#BZ``#$N@`` -MQ+H``.RZ``#LN@``U+H``-2Z``"XN@``N+H``'"Z``!PN@``(+D``""Y``#` -M.0``P#D``"@Z```H.@``D#H``)`Z``",.@``C#H``."Y``#@N0``,+L``#"[ -M``"`NP``@+L``%R[``!.P``'CL``(`X``"`.```)KL``":[``"8NP``F+L``*"[``"@NP`` -M+KL``"Z[```0.@``$#H``"0[```D.P``-CL``#8[``#\.@``_#H``)`Y``"0 -M.0```KL```*[``!PNP``<+L``(:[``"&NP``9KL``&:[```6NP``%KL```BZ -M```(N@``B#H``(@Z``#@.@``X#H````[````.P``&CL``!H[``#X.@``^#H` -M`""Y```@N0``+KL``"Z[``"'NP``A[L``)&[``"1NP``9KL``&:[``"XN@`` -MN+H``(@Z``"(.@``(CL``"([```F.P``)CL``*@Z``"H.@``D+H``)"Z``!R -MNP``[``#'NP`` -ME;L``)6[```LNP``++L```BZ```(N@``F#H``)@Z``"P.@``L#H``(`Y``"` -M.0``B+H``(BZ``#\N@``_+H``/BZ``#XN@``"+H```BZ``#8.@``V#H``"0[ -M```D.P``A#H``(0Z``#@N@``X+H``(R[``",NP``U+L``-2[``#_NP``_[L` -M`.Z[``#NNP``I[L``*>[```FNP``)KL``(`X``"`.```#CL```X[``!&.P`` -M1CL``#0[```T.P``%#L``!0[``#`.@``P#H``#"Z```PN@``;KL``&Z[``"] -MNP``O;L``-&[``#1NP``PKL``,*[``"7NP``E[L``":[```FNP``6+H``%BZ -M``#`N0``P+D``"BZ```HN@``D+H``)"Z``#@N@``X+H``-"Z``#0N@``(#D` -M`"`Y```,.P``##L``!([```2.P``(#D``"`Y```\NP``/+L``,F[``#)NP"` -M$;P`@!&\```AO```(;P```6\```%O```E;L``)6[``#PN0``\+D``$X[``!. -M.P``J#L``*@[``"8.P``F#L``#`[```P.P``.#H``#@Z``#`N@``P+H``(Z[ -M``".NP``WKL``-Z[``#ZNP``^KL``.N[``#KNP``P+L``,"[``!NNP``;KL` -M`)"Z``"0N@``$#H``!`Z``!H.@``:#H````Y````.0``X+H``."Z``"%NP`` -MA;L``*2[``"DNP``9KL``&:[``"4N@``E+H``+`Y``"P.0``8#H``&`Z``#` -M.0``P#D``,RZ``#,N@``BKL``(J[``"_NP``O[L``,.[``##NP``I;L``*6[ -M``!4NP``5+L``*2Z``"DN@``"+H```BZ``"PN@``L+H``!*[```2NP``#+L` -M``R[``#\N@``_+H```:[```&NP``_+H``/RZ``#0N@``T+H```"[````NP`` -M.KL``#J[``!FNP``9KL``(.[``"#NP``DKL``)*[``"(NP``B+L``%R[``!< -MNP``1KL``$:[```PNP``,+L``(RZ``",N@``N#H``+@Z``!&.P``1CL``#P[ -M```\.P``K#H``*PZ``#HN@``Z+H``,6[``#%NP"`(+P`@""\`(`XO`"`.+P` -M@">\`(`GO```X;L``.&[``#HN@``Z+H``$0[``!$.P``HCL``*([``",.P`` -MC#L``"8[```F.P``H#D``*`Y```8NP``&+L``(N[``"+NP``F[L``)N[``"1 -MNP``D;L``(>[``"'NP``<+L``'"[``!.NP``3KL``$:[``!&NP``/KL``#Z[ -M```.NP``#KL``."Z``#@N@``'KL``!Z[``!2NP``4KL``#"[```PNP``P+H` -M`,"Z````N@```+H``+`Y``"P.0``,#H``#`Z``"@N0``H+D``!Z[```>NP`` -MCKL``(Z[``"QNP``L;L``*Z[``"NNP``8+L``&"[``#`N```P+@``!@[```8 -M.P``$CL``!([``#0.0``T#D``.RZ``#LN@``BKL``(J[``#&NP``QKL``,2[ -M``#$NP``B+L``(B[````NP```+L``("X``"`N```D#H``)`Z``!X.@``>#H` -M`-"Y``#0N0``J+H``*BZ``"\N@``O+H``/"Z``#PN@``.+L``#B[``!(NP`` -M2+L``""[```@NP``Y+H``.2Z``"@N@``H+H``)"Y``"0N0``L#D``+`Y``#` -MN```P+@``)RZ``"#H``-`Y``#0.0``<+H` -M`'"Z``#XN@``^+H``"2[```DNP``,+L``#"[``#PN@``\+H``""Y```@N0`` -M>#H``'@Z``"D.@``I#H``/@Z``#X.@``2CL``$H[``!F.P``9CL``#H[```Z -M.P``U#H``-0Z```@N0``(+D``#2[```TNP``H[L``*.[``"VNP``MKL``)6[ -M``"5NP``"+L```B[``#L.@``[#H``*\[``"O.P``S#L``,P[``"9.P``F3L` -M`"0[```D.P``"#H```@Z``"8N@``F+H``,RZ``#,N@``(+D``""Y``#D.@`` -MY#H``#([```R.P``*#L``"@[``"\.@``O#H``$"Z``!`N@``-+L``#2[``!2 -MNP``4KL``"J[```JNP``"+L```B[``"0N@``D+H``)`Z``"0.@``:CL``&H[ -M``"H.P``J#L``,,[``##.P``R3L``,D[``"D.P``I#L``"8[```F.P``(+D` -M`""Y```8NP``&+L``&B[``!HNP``1KL``$:[```8N@``&+H``.0Z``#D.@`` -M%CL``!8[``#8.@``V#H``'`Z``!P.@`````````````8N@``&+H````Z```` -M.@``0CL``$([``"J.P``JCL``,X[``#..P``PSL``,,[``!N.P``;CL```"X -M````N```/KL``#Z[``!XNP``>+L``&*[``!BNP``)+L``"2[``"PN0``L+D` -M`#`[```P.P``I3L``*4[``#-.P``S3L``.([``#B.P``VCL``-H[``"?.P`` -MGSL``!0[```4.P``X+D``."Y``!,NP``3+L``**[``"BNP``C+L``(R[``#$ -MN@``Q+H``*PZ``"L.@``5#L``%0[``"6.P``ECL``*4[``"E.P``CCL``(X[ -M``!<.P``7#L``%X[``!>.P``@SL``(,[``"0.P``D#L``(\[``"/.P``5#L` -M`%0[``#`.0``P#D``#R[```\NP``B;L``(F[``!@NP``8+L``.2Z``#DN@`` -M<#H``'`Z``"*.P``BCL``-D[``#9.P``XCL``.([``"].P``O3L``(<[``"' -M.P``"CL```H[``#0.0``T#D``$"Y``!`N0```+@```"X``#`N```P+@``-`Y -M``#0.0``$CL``!([``"(.P``B#L``)@[``"8.P``B#L``(@[``!..P``3CL` -M`*`Z``"@.@``A+H``(2Z``#XN@``^+H``&BZ``!HN@``M#H``+0Z``"2.P`` -MDCL``/,[``#S.P"`!CP`@`8\``#".P``PCL``!0[```4.P``4+H``%"Z``!( -MNP``2+L``(6[``"%NP``/KL``#Z[``"`.```@#@``#P[```\.P``G3L``)T[ -M``#$.P``Q#L``,P[``#,.P``LSL``+,[``"?.P``GSL``)$[``"1.P``3CL` -M`$X[``"`.@``@#H``/"Y``#PN0``0+H``$"Z``!8N@``6+H``&"Z``!@N@`` -M`#@````X``"D.@``I#H``/0Z``#T.@``"#L```@[```V.P``-CL``&P[``!L -M.P``ESL``)<[``#).P``R3L``/$[``#Q.P``SCL``,X[``!:.P``6CL``)`Y -M``"0.0``\+H``/"Z``!.NP``3KL``"J[```JNP``@#@``(`X``!F.P``9CL` -M`+P[``"\.P``UCL``-8[``"Z.P``NCL``%H[``!:.P``,#H``#`Z``"`N0`` -M@+D``,`X``#`.````#H````Z``!X.@``>#H``!H[```:.P``8CL``&([``!P -M.P``<#L``&P[``!L.P``@3L``($[``!X.P``>#L``$P[``!,.P``)#L``"0[ -M````.P```#L``)PZ``"<.@``H#H``*`Z```H.P``*#L``'8[``!V.P``4CL` -M`%([``#P.@``\#H``"@Z```H.@```+H```"Z``"HN@``J+H``)"Y``"0N0`` -M(CL``"([``"M.P``K3L``.@[``#H.P``]CL``/8[``"\.P``O#L``!8[```6 -M.P``&+H``!BZ``"LN@``K+H``$"Z``!`N@``X#D``.`Y```F.P``)CL``*$[ -M``"A.P``OSL``+\[``"D.P``I#L``'0[``!T.P``'CL``!X[``!@.@``8#H` -M``"Y````N0``0+D``$"Y```@N0``(+D``(`X``"`.```W#H``-PZ``",.P`` -MC#L``,0[``#$.P``RCL``,H[``"W.P``MSL``(P[``",.P``]#H``/0Z``#` -MN0``P+D``&BZ``!HN@``P#@``,`X``#D.@``Y#H``'P[``!\.P``L3L``+$[ -M``"?.P``GSL``#@[```X.P``M#H``+0Z``!X.@``>#H``$`Z``!`.@``B#H` -M`(@Z```4.P``%#L``%`[``!0.P``.#L``#@[``#X.@``^#H``+`Z``"P.@`` -MG#H``)PZ``#P.@``\#H``'`[``!P.P``M#L``+0[``"\.P``O#L``*([``"B -M.P``C3L``(T[``!6.P``5CL``*0Z``"D.@``"+H```BZ``"CL``'H[``"M.P``K3L``.$[``#A.P``VCL``-H[``"3.P``DSL```H[```* -M.P``@#@``(`X``#LN@``[+H``"B[```HNP``H+H``*"Z``"<.@``G#H``$([ -M``!".P``=CL``'8[``!F.P``9CL```@[```(.P``$#H``!`Z``!(.@``2#H` -M`!P[```<.P``>CL``'H[``"=.P``G3L``+L[``"[.P``MCL``+8[``!T.P`` -M=#L``,`Z``#`.@```#D````Y```@N@``(+H``&"Z``!@N@``8+D``&"Y``!8 -M.@``6#H``,`Z``#`.@``&CL``!H[``"*.P``BCL``+P[``"\.P``M3L``+4[ -M``"/.P``CSL``$X[``!..P``O#H``+PZ```0N@``$+H``*2Z``"DN@``D+D` -M`)"Y``"8.@``F#H``"`[```@.P``9CL``&8[``!>.P``7CL```([```".P`` -ME#H``)0Z```,.P``##L``(([``"".P``L3L``+$[``#8.P``V#L``.<[``#G -M.P``K3L``*T[``#0.@``T#H```*[```"NP``D+L``)"[``"ONP``K[L``(>[ -M``"'NP``>+H``'BZ```6.P``%CL``)L[``";.P``YSL``.<[`(`2/`"`$CP` -M@!0\`(`4/```[SL``.\[``"E.P``I3L``"@[```H.P```+H```"Z``!*NP`` -M2KL``'"[``!PNP``)+L``"2[``#0N0``T+D``"([```B.P``H3L``*$[``"K -M.P``JSL``'`[``!P.P``&CL``!H[``#L.@``[#H``,`Z``#`.@``Z#H``.@Z -M``!:.P``6CL``)L[``";.P``E3L``)4[``!,.P``3#L``-0Z``#4.@``8#D` -M`&`Y````N@```+H``,`Y``#`.0``"CL```H[```\.P``/#L``#H[```Z.P`` -M2#L``$@[``!..P``3CL``!P[```<.P``^#H``/@Z```:.P``&CL``#P[```\ -M.P``,CL``#([```J.P``*CL``!X[```>.P``_#H``/PZ``#(.@``R#H``/0Z -M``#T.@``Z#H``.@Z``#P.0``\#D``%BZ``!8N@``&+H``!BZ````.@```#H` -M```[````.P``CCL``(X[``#M.P``[3L```H\```*/```[3L``.T[``"1.P`` -MD3L``#`Z```P.@``2KL``$J[``"=NP``G;L``&R[``!LNP``F+H``)BZ``"0 -M.@``D#H``%@[``!8.P``JSL``*L[``"R.P``LCL``(8[``"&.P``0#L``$`[ -M```@.P``(#L``/`Z``#P.@``H#H``*`Z``"8.@``F#H``'@Z``!X.@``$#H` -M`!`Z``"0.@``D#H``!H[```:.P``-#L``#0[```(.P``"#L``-`Z``#0.@`` -MM#H``+0Z``!8.@``6#H``"`Z```@.@``V#H``-@Z``!0.P``4#L``'8[``!V -M.P``7CL``%X[```(.P``"#L``/"Y``#PN0``/KL``#Z[``!*NP``2KL``)"Z -M``"0N@``I#H``*0Z``!D.P``9#L``+H[``"Z.P``TSL``-,[``"A.P``H3L` -M`!H[```:.P``X#D``.`Y``!0N@``4+H``*RZ``"LN@``0+H``$"Z``"`.0`` -M@#D``.`Y``#@.0``H#D``*`Y``"$.@``A#H``.@Z``#H.@``S#H``,PZ``"< -M.@``G#H``+`Z``"P.@``E#H``)0Z``!@.0``8#D```"Y````N0``&#H``!@Z -M``#$.@``Q#H``!@[```8.P``1#L``$0[```L.P``+#L``%@Z``!8.@``.+H` -M`#BZ```PN@``,+H``"`Y```@.0``8#H``&`Z``#X.@``^#H``#H[```Z.P`` -M"CL```H[``!@N0``8+D``!Z[```>NP``:+L``&B[``!FNP``9KL``.RZ``#L -MN@``I#H``*0Z``!N.P``;CL``(D[``").P``?CL``'X[``!2.P``4CL``,0Z -M``#$.@``,+H``#"Z``#[``"GNP``I[L``$2[``!$NP``W+H` -M`-RZ``#,N@``S+H``,BZ``#(N@``D+H``)"Z``#@N0``X+D``)0Z``"4.@`` -M7CL``%X[``"/.P``CSL``$P[``!,.P``8#H``&`Z``#(N@``R+H``(V[``"- -MNP``VKL``-J[``#;NP``V[L``)B[``"8NP``%+L``!2[``"@N0``H+D``+`Z -M``"P.@``!#L```0[``"T.@``M#H````Z````.@``0+D``$"Y``#[``"GNP``C;L` -M`(V[```XNP``.+L``)2Z``"4N@``P+D``,"Y`````````````'`Z``!P.@`` -MU#H``-0Z``"(.@``B#H``("X``"`N```$+H``!"Z``"HN@``J+H``#*[```R -MNP``9+L``&2[``!`NP``0+L``!Z[```>NP``'+L``!R[```@NP``(+L``#*[ -M```RNP``7KL``%Z[``!,NP``3+L``)BZ``"8N@``E#H``)0Z```6.P``%CL` -M`"P[```L.P``"CL```H[``!@N0``8+D``'2[``!TNP``Q+L``,2[``#+NP`` -MR[L``*&[``"ANP``*KL``"J[``"@.0``H#D```0[```$.P``[#H``.PZ``!8 -M.@``6#H```"X````N```M+H``+2Z```^NP``/KL``%:[``!6NP``++L``"R[ -M```BNP``(KL``#"[```PNP``$KL``!*[``#`N@``P+H``)BZ``"8N@``8+H` -M`&"Z```0N@``$+H``*"Z``"@N@``(+L``""[```@NP``(+L``*"Z``"@N@`` -M0+D``$"Y``#0.0``T#D``(PZ``",.@``<#H``'`Z``"4N@``E+H``'B[``!X -MNP``K[L``*^[``"QNP``L;L``(B[``"(NP``O+H``+RZ``"X.@``N#H``!P[ -M```<.P``L#H``+`Z``"`N0``@+D``!*[```2NP``EKL``):[``"_NP``O[L` -M`)R[``"+L``*R[``"LNP``R+L``,B[``"^NP`` -MOKL``%*[``!2NP``*#H``"@Z``!J.P``:CL``)H[``":.P``DSL``),[```> -M.P``'CL``-"Z``#0N@``L[L``+.[``#GNP``Y[L``-:[``#6NP``FKL``)J[ -M```"NP```KL``"@Z```H.@``Z#H``.@Z``#D.@``Y#H``-PZ``#<.@``R#H` -M`,@Z```8.@``&#H``)"Y``"0N0``D+D``)"Y``#0N0``T+D``,BZ``#(N@`` -M'+L``!R[```@NP``(+L``""[```@NP``*KL``"J[```CL``'H[```*.P``"CL``)"Z``"0N@``=+L``'2[``"8NP``F+L` -M`(R[``",NP``,+L``#"[``#`N0``P+D``(PZ``",.@``4#H``%`Z```````` -M``````BZ```(N@``G+H``)RZ``#8N@``V+H``$"Z``!`N@``A#H``(0Z``#X -M.@``^#H``-`Z``#0.@``C#H``(PZ``"0.0``D#D``'BZ``!XN@``[+H``.RZ -M```"NP```KL``""[```@NP``1+L``$2[```:NP``&KL``"BZ```HN@``:#H` -M`&@Z```,.P``##L``'`[``!P.P``CSL``(\[``!,.P``3#L``#@Z```X.@`` -MK+H``*RZ```PNP``,+L``%"[``!0NP``$+L``!"[```8N@``&+H``""Y```@ -MN0``8+H``&"Z``"XN@``N+H``."Z``#@N@```KL```*[``",N@``C+H``.0Z -M``#D.@``FCL``)H[``#,.P``S#L``,H[``#*.P``ESL``)<[``#,.@``S#H` -M`/"Z``#PN@``A;L``(6[``"FNP``IKL``+>[``"WNP``H;L``*&[```BNP`` -M(KL``-`Y``#0.0``(CL``"([``"..P``CCL``,0[``#$.P``Q#L``,0[``"! -M.P``@3L``)@Z``"8.@``S+H``,RZ``!\NP``?+L``(V[``"-NP``(KL``"*[ -M```@.0``(#D``/0Z``#T.@``0#L``$`[``!>.P``7CL``!P[```<.P``T#D` -M`-`Y```0N@``$+H````X````.```8#H``&`Z``"P.@``L#H``-@Z``#8.@`` -ME#H``)0Z``"PN0``L+D``+"Z``"PN@``E+H``)2Z```HN@``*+H```"Y```` -MN0``C#H``(PZ```D.P``)#L``#8[```V.P``#CL```X[````.P```#L```H[ -M```*.P``Z#H``.@Z``"P.@``L#H``)`Z``"0.@``\#D``/`Y``"@N0``H+D` -M```X````.```H#H``*`Z``#<.@``W#H``)PZ``"<.@``2#H``$@Z````N0`` -M`+D``/2Z``#TN@``1+L``$2[```$NP``!+L``!@Z```8.@``9CL``&8[``#- -M.P``S3L``/T[``#].P``V#L``-@[``!N.P``;CL``$@Z``!(.@``O+H``+RZ -M``!0NP``4+L``&B[``!HNP``^+H``/BZ```@.0``(#D``*0Z``"D.@``]#H` -M`/0Z```L.P``+#L``#X[```^.P``,CL``#([```N.P``+CL``!@[```8.P`` -MF#H``)@Z``!@.0``8#D``.`Y``#@.0``C#H``(PZ``"D.@``I#H``.`Z``#@ -M.@``(CL``"([```4.P``%#L``$@Z``!(.@``@+D``("Y``#`N```P+@``$@Z -M``!(.@``$CL``!([``").P``B3L``)\[``"?.P``9#L``&0[``"@.@``H#H` -M`!"Z```0N@``#KL```Z[``!$NP``1+L``.2Z``#DN@``F#H``)@Z``!H.P`` -M:#L``)<[``"7.P``G#L``)P[``"(.P``B#L``"P[```L.P``L#H``+`Z``!H -M.@``:#H``+`Y``"P.0``\+D``/"Y``#`N0``P+D``"`Z```@.@``I#H``*0Z -M``#@.@``X#H``#H[```Z.P``@#L``(`[``!P.P``<#L``!P[```<.P``F#H` -M`)@Z``#`.```P#@``!"Z```0N@``P#@``,`X``#$.@``Q#H```8[```&.P`` -MV#H``-@Z``"P.@``L#H``(PZ``",.@``L#D``+`Y``#P.0``\#D``!H[```: -M.P``E3L``)4[``"T.P``M#L``*D[``"I.P``?#L``'P[``"H.@``J#H``+"Z -M``"PN@``(+L``""[````NP```+L``)"Z``"0N@``H#D``*`Y```<.P``'#L` -M`'H[``!Z.P``:CL``&H[```\.P``/#L``"@[```H.P```CL```([``!@.@`` -M8#H``"`Y```@.0``@#@``(`X``!`.0``0#D``)`Z``"0.@``4CL``%([``"B -M.P``HCL``*8[``"F.P``@SL``(,[```<.P``'#L``$"Y``!`N0``2KL``$J[ -M``")NP``B;L``#:[```VNP``@+D``("Y```@.P``(#L``)T[``"=.P``MSL` -M`+<[``"..P``CCL``"@[```H.P``M#H``+0Z``#`.0``P#D``!"Z```0N@`` -MH+D``*"Y``!`.@``0#H``(`Z``"`.@```#H````Z```H.@``*#H``,0Z``#$ -M.@``%#L``!0[``!".P``0CL``'0[``!T.P``9CL``&8[```6.P``%CL``*0Z -M``"D.@``0#H``$`Z``!`N0``0+D``*2Z``"DN@``O+H``+RZ``"@N@``H+H` -M`,BZ``#(N@``R+H``,BZ`````````````"0[```D.P``H3L``*$[``#D.P`` -MY#L```$\```!/```TCL``-([``!,.P``3#L``("X``"`N```&KL``!J[``"# -MNP``@[L``(6[``"%NP``!KL```:[``"@.0``H#D``+PZ``"\.@``X#H``.`Z -M``#T.@``]#H``.`Z``#@.@``R#H``,@Z```,.P``##L``$8[``!&.P``3CL` -M`$X[```V.P``-CL``#H[```Z.P``*#L``"@[``"L.@``K#H``)`Y``"0.0`` -M`#D````Y``"0N0``D+D``*2Z``"DN@``W+H``-RZ``"`N@``@+H``)`Y``"0 -M.0``"CL```H[``",.P``C#L``+$[``"Q.P``DCL``)([```F.P``)CL``$`Z -M``!`.@``B+H``(BZ```6NP``%KL``-BZ``#8N@``(#H``"`Z```@.P``(#L` -M`$`[``!`.P``*CL``"H[``#0.@``T#H``(`X``"`.```0+H``$"Z````.``` -M`#@``*0Z``"D.@``$CL``!([``!>.P``7CL``(P[``",.P``>CL``'H[```2 -M.P``$CL``(@Z``"(.@``8#D``&`Y``"$N@``A+H``!B[```8NP``-+L``#2[ -M```@NP``(+L``,2Z``#$N@``0#H``$`Z``!V.P``=CL``+D[``"Y.P``Q3L` -M`,4[``"X.P``N#L``(\[``"/.P``[#H``.PZ```8N@``&+H``*2Z``"DN@`` -M2+H``$BZ``#`N0``P+D```"Y````N0```+@```"X```0N@``$+H``(BZ``"( -MN@``(+D``""Y``#P.@``\#H``&@[``!H.P``GSL``)\[``#!.P``P3L``+$[ -M``"Q.P``.CL``#H[``!@N0``8+D```B[```(NP``0KL``$*[``!`NP``0+L` -M`-BZ``#8N@``T#D``-`Y```*.P``"CL``&@[``!H.P``ISL``*<[``#`.P`` -MP#L``*8[``"F.P``9#L``&0[````.P```#L``("X``"`N```%KL``!:[``!, -MNP``3+L``/"Z``#PN@``8#D``&`Y```2.P``$CL``'H[``!Z.P``A#L``(0[ -M```D.P``)#L``#`Z```P.@``0+D``$"Y``"`N0``@+D``,`Y``#`.0``%#L` -M`!0[``"7.P``ESL``+4[``"U.P``ESL``)<[``!0.P``4#L``.0Z``#D.@`` -M@#D``(`Y```XN@``.+H``""Z```@N@```+H```"Z```PN@``,+H``)"Y``"0 -MN0``6#H``%@Z``#(.@``R#H``/@Z``#X.@``.#L``#@[``!V.P``=CL``&8[ -M``!F.P``*#L``"@[```(.P``"#L```P[```,.P``%CL``!8[```P.P``,#L` -M`$`[``!`.P``!#L```0[```@.0``(#D``)"Z``"0N@``O+H``+RZ``"LN@`` -MK+H```"Y````N0``(CL``"([``"<.P``G#L``*@[``"H.P``@#L``(`[```* -M.P``"CL``(`X``"`.```P+H``,"Z``"LN@``K+H``,`X``#`.```P#H``,`Z -M```J.P``*CL``'@[``!X.P``B3L``(D[``!8.P``6#L``!P[```<.P``%CL` -M`!8[```(.P``"#L``)0Z``"4.@``T#D``-`Y``!`.0``0#D````````````` -M(#D``"`Y``"8.@``F#H```0[```$.P``\#H``/`Z``"P.@``L#H``*@Z``"H -M.@``F#H``)@Z``"`.@``@#H``/0Z``#T.@``>#L``'@[``"D.P``I#L``)<[ -M``"7.P``3#L``$P[``!H.@``:#H``-"Z``#0N@``.KL``#J[``#XN@``^+H` -M`,`X``#`.```#CL```X[``",.P``C#L``+L[``"[.P``ICL``*8[```X.P`` -M.#L``$@Z``!(.@``\+D``/"Y``"TN@``M+H``."Z``#@N@``A+H``(2Z``#` -M.```P#@``)`Z``"0.@``*CL``"H[``"0.P``D#L``*8[``"F.P``DSL``),[ -M``!L.P``;#L``!H[```:.P``H#D``*`Y``"TN@``M+H``*2Z``"DN@``(#D` -M`"`Y``#(.@``R#H``"([```B.P``/CL``#X[```*.P``"CL``"`Z```@.@`` -MP+@``,"X``"P.0``L#D``(0Z``"$.@```CL```([``!>.P``7CL``(,[``"# -M.P``/#L``#P[``"@.@``H#H``-`Y``#0.0``H#D``*`Y```@.@``(#H``-@Z -M``#8.@``,CL``#([```X.P``.#L```X[```..P``[#H``.PZ``"L.@``K#H` -M`-`Y``#0.0````````````!`.@``0#H``+`Z``"P.@``I#H``*0Z``#(.@`` -MR#H``"8[```F.P``5CL``%8[``!@.P``8#L``$P[``!,.P``^#H``/@Z```` -MN@```+H``":[```FNP``.KL``#J[``#LN@``[+H``$`Y``!`.0``8#L``&`[ -M``#?.P``WSL`@`8\`(`&/```XSL``.,[``",.P``C#L``+`Z``"P.@``R+H` -M`,BZ``!2NP``4KL``$J[``!*NP``#+L```R[``"DN@``I+H``,"X``#`N``` -MI#H``*0Z``#X.@``^#H```0[```$.P``.CL``#H[``"#.P``@SL``(L[``"+ -M.P``<#L``'`[``!4.P``5#L``#0[```T.P``_#H``/PZ``"P.@``L#H``(0Z -M``"$.@```+@```"X``#8N@``V+H``"*[```BNP``'+L``!R[``#TN@``]+H` -M`/"Y``#PN0``&CL``!H[``"F.P``ICL``,([``#".P``I3L``*4[``!4.P`` -M5#L``&`Z``!@.@``I+H``*2Z``#DN@``Y+H``%"Z``!0N@``D#D``)`Y``"< -M.@``G#H``!`[```0.P``(#L``"`[``"T.@``M#H``+`Y``"P.0``"#H```@Z -M``"8.@``F#H``+@Z``"X.@``V#H``-@Z``#X.@``^#H``,PZ``#,.@``>#H` -M`'@Z``!@.@``8#H``%`Z``!0.@```#@````X```PN@``,+H``#BZ```XN@`` -M,+H``#"Z``!0N@``4+H``$`Y``!`.0``'CL``!X[``").P``B3L``)<[``"7 -M.P``ASL``(<[```R.P``,CL````Z````.@``G+H``)RZ``"HN@``J+H``!"Z -M```0N@```#D````Y``!P.@``<#H``+@Z``"X.@``L#D``+`Y``#8N@``V+H` -M`#2[```TNP``#+L```R[```HN@``*+H``,0Z``#$.@``@CL``(([``"X.P`` -MN#L``+8[``"V.P``ECL``)8[``!..P``3CL``%`Z``!0.@``]+H``/2Z``!B -MNP``8KL``'Z[``!^NP``A+L``(2[``!DNP``9+L``*BZ``"HN@``]#H``/0Z -M``"/.P``CSL``+X[``"^.P``P3L``,$[``"(.P``B#L``)`Z``"0.@``P+H` -M`,"Z```VNP``-KL``%"[``!0NP``)KL``":[```(N@``"+H``*0Z``"D.@`` -MQ#H``,0Z```X.@``.#H``,`Y``#`.0``H#D``*`Y```@.@``(#H``,0Z``#$ -M.@``,#L``#`[```V.P``-CL````[````.P``>#H``'@Z``!@N0``8+D``.BZ -M``#HN@``)+L``"2[``#\N@``_+H``'BZ``!XN@``0+H``$"Z``#`N0``P+D` -M`.`Y``#@.0``C#H``(PZ``"H.@``J#H``-@Z``#8.@``T#H``-`Z```H.@`` -M*#H``-"Y``#0N0```+H```"Z``!`N0``0+D``(`Y``"`.0``K#H``*PZ```V -M.P``-CL``#@[```X.P``>#H``'@Z``#`N@``P+H``%J[``!:NP``DKL``)*[ -M``"#NP``@[L``.2Z``#DN@``A#H``(0Z```T.P``-#L``&P[``!L.P``:CL` -M`&H[```&.P``!CL``("Y``"`N0``R+H``,BZ``"\N@``O+H``)"Z``"0N@`` -M6+H``%BZ````N0```+D``!@Z```8.@``8#H``&`Z``",.@``C#H``,`Z``#` -M.@``E#H``)0Z`````````````&"Z``!@N@``L+H``+"Z```$NP``!+L``!"[ -M```0NP``A+H``(2Z```H.@``*#H``*0Z``"D.@``4#H``%`Z````N````+@` -M`)2Z``"4N@``!+L```2[``#$N@``Q+H``.`Y``#@.0``$CL``!([``!8.P`` -M6#L``'([``!R.P``-#L``#0[`````````````#R[```\NP``A[L``(>[``"# -MNP``@[L``%2[``!4NP``[+H``.RZ`````````````(PZ``",.@``L#H``+`Z -M``#0.@``T#H``,PZ``#,.@``2#H``$@Z```@N0``(+D```"Z````N@``<+H` -M`'"Z``#DN@``Y+H``.RZ``#LN@``$+H``!"Z``!8.@``6#H``,@Z``#(.@`` -M[#H``.PZ``"L.@``K#H```"Y````N0``P+H``,"Z``#@N@``X+H``+2Z``"T -MN@``C+H``(RZ``#@N0``X+D``)`Y``"0.0``H+D``*"Y````NP```+L``#R[ -M```\NP``&+L``!B[``!PN@``<+H``'`Z``!P.@``1CL``$8[``"+.P``BSL` -M`&X[``!N.P``_#H``/PZ``"`.```@#@```:[```&NP``?+L``'R[``"#NP`` -M@[L``#Z[```^NP``\+H``/"Z``"4N@``E+H``,`X``#`.```M#H``+0Z``#T -M.@``]#H``-@Z``#8.@``F#H``)@Z```@N0``(+D``/"Z``#PN@``,+L``#"[ -M```4NP``%+L``*BZ``"HN@``(#D``"`Y```,.P``##L``%H[``!:.P``&#L` -M`!@[````N````+@``!*[```2NP``.P``,CL``#([```*.P``"CL``-`Y``#0 -M.0``F+H``)BZ``#`N@``P+H``+"Z``"PN@``[+H``.RZ```2NP``$KL```J[ -M```*NP``\+H``/"Z``"TN@``M+H``("Y``"`N0``B#H``(@Z``#8.@``V#H` -M`.PZ``#L.@``!CL```8[``#P.@``\#H``(`Z``"`.@``L#D``+`Y``"P.0`` -ML#D``+"Y``"PN0``!KL```:[``!ZNP``>KL``)Z[``">NP``J+L``*B[``"# -MNP``@[L``*"Z``"@N@``_#H``/PZ``"#.P``@SL``)\[``"?.P``DCL``)([ -M```<.P``'#L``"BZ```HN@``*KL``"J[``!`NP``0+L``"R[```LNP``#KL` -M``Z[``"DN@``I+H``!"Z```0N@``&+H``!BZ```XN@``.+H``+"Y``"PN0`` -M(+D``""Y``"@N0``H+D```"Y````N0``P#D``,`Y````.````#@``"BZ```H -MN@``&+H``!BZ``#`N```P+@``,"Y``#`N0``H+H``*"Z```$NP``!+L``#B[ -M```XNP``6+L``%B[```+H``,2Z``#$N@``$KL``!*[```@NP`` -M(+L``,BZ``#(N@``@+D``("Y``"P.0``L#D``-`Y``#0.0``@+@``("X``#( -MN@``R+H``&:[``!FNP``B;L``(F[``!$NP``1+L``)BZ``"8N@``X#D``.`Y -M``#D.@``Y#H```0[```$.P``.#H``#@Z``"NP``'KL``("[``"`NP``A[L``(>[``!,NP``3+L` -M`.BZ``#HN@``0+D``$"Y``"(.@``B#H``%@Z``!8.@``8+H``&"Z```LNP`` -M++L``("[``"`NP``F+L``)B[``"'NP``A[L``/2Z``#TN@```#H````Z``#( -M.@``R#H``,PZ``#,.@``K#H``*PZ``"`.0``@#D``-"Z``#0N@``5+L``%2[ -M``"3NP``D[L``+J[``"ZNP``S;L``,V[``"TNP``M+L``'Z[``!^NP``$+L` -M`!"[``!`N0``0+D``/@Z``#X.@``)CL``"8[``"<.@``G#H``%"Z``!0N@`` -M$+L``!"[``!.NP``3KL``%Z[``!>NP``++L``"R[``#+L``,J[``#*NP``^+L` -M`/B[``#3NP``T[L``'*[``!RNP``A+H``(2Z``"0.@``D#H``"([```B.P`` -M"CL```H[``#`.0``P#D``-"Z``#0N@``1+L``$2[``".NP``CKL``*6[``"E -MNP``G[L``)^[``"3NP``D[L``)N[``";NP``E+L``)2[``![``"WNP``B[L``(N[``!BNP``8KL``&"[``!@NP``2KL` -M`$J[```:NP``&KL``/2Z``#TN@``Q+H``,2Z``!PN@``<+H``"BZ```HN@`` -ME+H``)2Z``#8N@``V+H``.RZ``#LN@``%+L``!2[``!`NP``0+L``$J[``!* -MNP``6+L``%B[``"5NP``E;L``,6[``#%NP``S;L``,V[``"MNP``K;L``("[ -M``"`NP``"+L```B[``"`N0``@+D``/`Y``#P.0``H+D``*"Y``#`N@``P+H` -M`!R[```+H``!:[```6NP``>+L``'B[``"PNP``L+L``-V[``#=NP``W;L``-V[``"P -MNP``L+L``'*[``!RNP```KL```*[``"`N0``@+D``&@Z``!H.@``.#H``#@Z -M````N````+@``$"Z``!`N@``#+L```R[``"3NP``D[L``-"[``#0NP``XKL` -M`.*[``#1NP``T;L``*"[``"@NP``$KL``!*[``#`.0``P#D``,@Z``#(.@`` -MH#H``*`Z````.````#@``!"[```0NP``FKL``)J[``#"NP``PKL``*Z[``"N -MNP``DKL``)*[``"#NP``@[L``&J[``!JNP``4+L``%"[``!:NP``6KL``%J[ -M``!:NP``'KL``!Z[``"+L``*.[``"CNP``K[L``*^[``"? -MNP``G[L``$*[``!"NP``L+D``+"Y``"4.@``E#H``&@Z``!H.@``@+@``("X -M``"@N@``H+H``#2[```TNP``@+L``("[``"`NP``@+L``%J[``!:NP``/KL` -M`#Z[```4NP``%+L``+"Z``"PN@``>+H``'BZ``",N@``C+H``'"Z``!PN@`` -M:+H``&BZ````NP```+L``&Z[``!NNP``E+L``)2[``"5NP``E;L``(B[``"( -MNP``1+L``$2[``"@N@``H+H``,"X``#`N````+@```"X``"@N0``H+D``""Z -M```@N@``K+H``*RZ``#0N@``T+H``$BZ``!(N@``P#@``,`X```(N@``"+H` -M``R[```,NP``+L``+RZ``"\N@``"#H```@Z```(.P``"#L``!H[```:.P``E#H``)0Z```8 -MN@``&+H``,RZ``#,N@``(+L``""[``!JNP``:KL``(6[``"%NP``9+L``&2[ -M```LNP``++L``-BZ``#8N@``8+D``&"Y``!X.@``>#H``%@Z``!8.@``8+D` -M`&"Y``#(N@``R+H``%"[``!0NP``G+L``)R[``":NP``FKL``#J[```ZNP`` -M2+H``$BZ``#@.0``X#D``(0Z``"$.@``*#H``"@Z``!XN@``>+H``#"[```P -MNP``4KL``%*[```TNP``-+L```"[````NP``(+H``""Z``!8.@``6#H``(0Z -M``"$.@````````````!0N@``4+H``(BZ``"(N@``T+H``-"Z```6NP``%KL` -M`"*[```BNP``)KL``":[``!`NP``0+L``$:[``!&NP``&KL``!J[``#4N@`` -MU+H``(RZ``",N@``8+D``&"Y```8.@``&#H``,`Y``#`.0``(+D``""Y``"@ -M.0``H#D``)PZ``"<.@``M#H``+0Z``!P.@``<#H````X````.```[+H``.RZ -M``".NP``CKL``,.[``##NP``P[L``,.[``"=NP``G;L``"J[```JNP``&#H` -M`!@Z``!,.P``3#L``&8[``!F.P``)CL``"8[``"\.@``O#H``,`X``#`.``` -MQ+H``,2Z```:NP``&KL``!B[```8NP``&KL``!J[```JNP``*KL``!J[```: -MNP``\+H``/"Z``#4N@``U+H``)BZ``"8N@``@+@``("X```@.@``(#H``)`Y -M``"0.0```+@```"X``#`.0``P#D``!@Z```8.@``L#D``+`Y``#`.```P#@` -M``BZ```(N@``#+L```R[``"#NP``@[L``)^[``"?NP``G+L``)R[``!TNP`` -M=+L``*2Z``"DN@```#L````[``!^.P``?CL``'([``!R.P``'CL``!X[``!` -M.@``0#H``+BZ``"XN@``1+L``$2[``!,NP``3+L``!:[```6NP``U+H``-2Z -M``"$N@``A+H```"Z````N@``6+H``%BZ``#HN@``Z+H```*[```"NP``R+H` -M`,BZ``#,N@``S+H``.RZ``#LN@``M+H``+2Z``#PN0``\+D``$`Y``!`.0`` -M:#H``&@Z``#L.@``[#H``/@Z``#X.@``0#H``$`Z``!8N@``6+H``!:[```6 -MNP``>KL``'J[``"3NP``D[L``%B[``!8NP``G+H``)RZ```@.0``(#D``#@Z -M```X.@``6#H``%@Z``!@.0``8#D``("Z``"`N@``T+H``-"Z``"4N@``E+H` -M`#"Z```PN@``"+H```BZ``#@N0``X+D``'"Z``!PN@``'+L``!R[``!HNP`` -M:+L``%"[``!0NP``^+H``/BZ```XN@``.+H``$`Z``!`.@``$CL``!([```V -M.P``-CL```H[```*.P``F#H``)@Z````N````+@``/2Z``#TN@``<+L``'"[ -M``"8NP``F+L``*N[``"KNP``LKL``+*[``"+NP``B[L``+"Z``"PN@``P#H` -M`,`Z``!*.P``2CL``'([``!R.P``5#L``%0[``"D.@``I#H``)2Z``"4N@`` -M(KL``"*[```NNP``+KL``!*[```2NP``B+H``(BZ``"P.0``L#D``.`Y``#@ -M.0``:+H``&BZ```$NP``!+L``"*[```BNP``,KL``#*[```LNP``++L``-BZ -M``#8N@``\+D``/"Y```````````````Y````.0``\#D``/`Y``#0.0``T#D` -M`("X``"`N```(+D``""Y``"`N```@+@``$BZ``!(N@``W+H``-RZ``#8N@`` -MV+H``%BZ``!8N@``H+D``*"Y````.````#@``*`Y``"@.0``8+D``&"Y``#< -MN@``W+H``#:[```VNP``0+L``$"[```DNP``)+L``+BZ``"XN@``*#H``"@Z -M```0.P``$#L``/PZ``#\.@``T#D``-`Y``!PN@``<+H```"[````NP``,+L` -M`#"[```0NP``$+L``"BZ```HN@``.#H``#@Z``"@.@``H#H``+`Z``"P.@`` -M:#H``&@Z``"PN0``L+D``*RZ``"LN@``J+H``*BZ``"0N@``D+H``-"Z``#0 -MN@``[+H``.RZ``"@N@``H+H``"BZ```HN@``P+D``,"Y``"0.0``D#D``'`Z -M``!P.@``*#H``"@Z``!`N0``0+D``#BZ```XN@``D+H``)"Z``"TN@``M+H` -M`$"Z``!`N@``4#H``%`Z``#,.@``S#H``'@Z``!X.@```+D```"Y``",N@`` -MC+H```B[```(NP``'KL``!Z[``"DN@``I+H``"`Z```@.@``[#H``.PZ```6 -M.P``%CL```([```".P``H#D``*`Y```&NP``!KL``%R[``!#H``'@Z````.@```#H``""Z```@N@```+L```"[```[ -M``!(NP``2+L``""Z```@N@``X#H``.`Z``!(.P``2#L``&P[``!L.P``2#L` -M`$@[``"D.@``I#H``!"Z```0N@``K+H``*RZ``#8N@``V+H``!:[```6NP`` -M)KL``":[```(NP``"+L``-RZ``##H``'@Z``"8.@``F#H``&`Y``!@.0``>+H``'BZ``#XN@``^+H` -M`$2[``!$NP``9KL``&:[```BNP``(KL``#"Z```PN@``6#H``%@Z``#P.@`` -M\#H``!`[```0.P``G#H``)PZ```@N@``(+H``,2Z``#$N@``D+H``)"Z``#P -MN0``\+D``&`Y``!@.0``D#H``)`Z``#,.@``S#H``$`Z``!`.@``"+H```BZ -M``"XN@``N+H```"[````NP``&KL``!J[``#LN@``[+H``#"Z```PN@``P#@` -M`,`X```@.@``(#H``/0Z``#T.@``0CL``$([```Z.P``.CL``-@Z``#8.@`` -M8#D``&`Y``#HN@``Z+H``(.[``"#NP``I+L``*2[``"'NP``A[L``!B[```8 -MNP``@#@``(`X```P.P``,#L``(\[``"/.P``@CL``(([```D.P``)#L``*PZ -M``"L.@``\#D``/`Y```0N@``$+H``'BZ``!XN@``,+H``#"Z``!8N@``6+H` -M`-RZ``##H``+`Y``"P.0``P#@``,`X```PN@``,+H``,"Z``#`N@``L+H``+"Z -M```PN@``,+H``("X``"`N```A#H``(0Z```(.P``"#L``/`Z``#P.@```#D` -M```Y``#(N@``R+H``!B[```8NP``*+L``"B[```"NP```KL``,"X``#`N``` -M`CL```([```^.P``/CL``#([```R.P``"#L```@[``!(.@``2#H``#BZ```X -MN@``A+H``(2Z``!`N0``0+D``)`Y``"0.0``D#D``)`Y```0.@``$#H``$`Z -M``!`.@``8#D``&`Y``"PN0``L+D``*"Y``"@N0``D+D``)"Y``!(N@``2+H` -M`'BZ``!XN@``*+H``"BZ````N@```+H``""Y```@N0``D#H``)`Z```:.P`` -M&CL``!([```2.P``G#H``)PZ``"P.0``L#D``."Y``#@N0``G+H``)RZ``!H -MN@``:+H``!`Z```0.@``Y#H``.0Z```$.P``!#L``-@Z``#8.@``X#D``.`Y -M``#8N@``V+H``%*[``!2NP``0+L``$"[``#8N@``V+H``/"Y``#PN0``:#H` -M`&@Z```2.P``$CL``"8[```F.P``W#H``-PZ``!8.@``6#H``*`Y``"@.0`` -MX+D``."Y``"$N@``A+H``&"Z``!@N@``,+H``#"Z``!8N@``6+H``,"Y``#` -MN0``>#H``'@Z``#X.@``^#H``-PZ``#<.@``D#H``)`Z``"P.0``L#D``)"Z -M``"0N@``(KL``"*[```HNP``*+L``."Z``#@N@``8+H``&"Z````.0```#D` -M`(@Z``"(.@``>#H``'@Z````N````+@``,"Y``#`N0``T#D``-`Y``"4.@`` -ME#H``,@Z``#(.@``_#H``/PZ``#8.@``V#H`````````````!KL```:[``!: -MNP``6KL``'B[``!XNP``9KL``&:[````NP```+L``)`Y``"0.0``U#H``-0Z -M````.P```#L``!@[```8.P``&CL``!H[``"D.@``I#H``,"Y``#`N0``R+H` -M`,BZ```LNP``++L``':[``!VNP``@;L``(&[```XNP``.+L``)RZ``"[``"GNP``B+L``(B[ -M```TNP``-+L``."Z``#@N@``$+H``!"Z```@.@``(#H``(PZ``",.@``2#H` -M`$@Z``#0.0``T#D```BZ```(N@``,+L``#"[``"FNP``IKL``,J[``#*NP`` -MR;L``,F[``"RNP``LKL``&J[``!JNP``C+H``(RZ```0.@``$#H``"`Z```@ -M.@``0+D``$"Y``#,N@``S+H``%J[``!:NP``C+L``(R[``!LNP``;+L``""[ -M```@NP``^+H``/BZ``#0N@``T+H``*2Z``"DN@``T+H``-"Z```DNP``)+L` -M`#J[```ZNP``(KL``"*[```8NP``&+L``!B[```8NP``"KL```J[```.NP`` -M#KL``#B[```XNP``2+L``$B[```LNP``++L``"R[```LNP``7+L``%R[``!X -MNP``>+L``&Z[``!NNP``9+L``&2[``!(NP``2+L``-BZ``#8N@``8+D``&"Y -M``!`.0``0#D``*"Y``"@N0``J+H``*BZ``!&NP``1KL``)Z[``">NP``JKL` -M`*J[``"#NP``@[L``#*[```RNP``]+H``/2Z``"[ -M``"WNP``>+L``'B[````NP```+L``*"Z``"@N@``P+H``,"Z``#LN@``[+H` -M`!R[```+H``*"Z``"@N@`` -M^+H``/BZ```TNP``-+L``'B[``!XNP``@;L``(&[```DNP``)+L``#"Z```P -MN@``0#D``$`Y``"P.0``L#D``,"X``#`N```V+H``-BZ``"`NP``@+L``*J[ -M``"JNP``J[L``*N[``";NP``F[L``':[``!VNP``%+L``!2[``"@N@``H+H` -M`)BZ``"8N@``E+H``)2Z```XN@``.+H``%"Z``!0N@``V+H``-BZ```2NP`` -M$KL``""[```@NP``2+L``$B[``!LNP``;+L``%2[``!4NP``'KL``!Z[```$ -MNP``!+L``.RZ``#LN@``T+H``-"Z````NP```+L``"Z[```NNP``&+L``!B[ -M``!XN@``>+H``(`X``"`.```L#D``+`Y``!@.0``8#D``%"Z``!0N@``3+L` -M`$R[``"TNP``M+L``-:[``#6NP``SKL``,Z[``"FNP``IKL``#*[```RNP`` -M@+@``("X``#`.@``P#H``.PZ``#L.@``_#H``/PZ``#@.@``X#H```@Z```( -M.@``<+H``'"Z``#PN@``\+H``#2[```TNP``AKL``(:[``"=NP``G;L``)6[ -M``"5NP``A+L``(2[``!:NP``6KL```J[```*NP``D+H``)"Z``"4N@``E+H` -M`+BZ``"XN@``:+H``&BZ``"`N```@+@``*`Y``"@.0``,#H``#`Z``"$.@`` -MA#H``&`Y``!@.0``U+H``-2Z``!0NP``4+L``(&[``"!NP``A;L``(6[``!. -MNP``3KL``*2Z``"DN@``8#D``&`Y``!@.0``8#D``!"Z```0N@``I+H``*2Z -M```4NP``%+L``%"[``!0NP``0KL``$*[``#@N@``X+H``$"Z``!`N@``8+D` -M`&"Y``"`.0``@#D``(`Y``"`.0``$+H``!"Z``" -MNP``7KL``%Z[``!`NP``0+L```2[```$NP``4+H``%"Z``!`N0``0+D``%"Z -M``!0N@``R+H``,BZ``#4N@``U+H``."Z``#@N@```+L```"[``#,N@``S+H` -M`%BZ``!8N@``.+H``#BZ``"`N@``@+H``%"Z``!0N@``&+H``!BZ``!0N@`` -M4+H``'BZ``!XN@``@+H``("Z``#8N@``V+H``"R[```LNP``-+L``#2[``#T -MN@``]+H``)2Z``"4N@``0+H``$"Z``"PN0``L+D```"Z````N@``\+H``/"Z -M``!NP`` -M'KL``"J[```JNP``)+L``"2[``#DN@``Y+H``&BZ``!HN@``4+H``%"Z``!0 -MN@``4+H``""Y```@N0``4#H``%`Z``#H.@``Z#H``$P[``!,.P``A#L``(0[ -M``!8.P``6#L``)`Z``"0.@``E+H``)2Z``!&NP``1KL``).[``"3NP``F+L` -M`)B[``!&NP``1KL``'BZ``!XN@``$#H``!`Z``#(.@``R#H```([```".P`` -MM#H``+0Z``#0.0``T#D``````````````+D```"Y```XN@``.+H``)2Z``"4 -MN@``6+H``%BZ``#`N0``P+D``("Y``"`N0``8#D``&`Y``"$.@``A#H``)`Z -M``"0.@``H#D``*`Y````N@```+H``)RZ``"CL``'H[``!(.P``2#L``!0[```4.P`` -MB#H``(@Z``!`N@``0+H```Z[```.NP``&KL``!J[``#@N@``X+H``$"Z``!` -MN@``&#H``!@Z``#0.@``T#H``*@Z``"H.@``@#D``(`Y``"@N0``H+D``("Y -M``"`N0```#@````X``"$.@``A#H``"H[```J.P``:#L``&@[``!..P``3CL` -M``@[```(.P``:#H``&@Z```0N@``$+H``,RZ``#,N@``Q+H``,2Z``!HN@`` -M:+H``"BZ```HN@``@+D``("Y```H.@``*#H``+0Z``"T.@``R#H``,@Z``#@ -M.@``X#H```0[```$.P``W#H``-PZ```X.@``.#H```"X````N```P+D``,"Y -M```8N@``&+H``+"Y``"PN0```#H````Z``!8.@``6#H``&`Y``!@.0``$+H` -M`!"Z```PN@``,+H``!BZ```8N@``@+@``("X``"\.@``O#H``%@[``!8.P`` -M@SL``(,[``!@.P``8#L```([```".P``@+@``("X```+H``'BZ``!XN@``>+H``/"Y``#PN0``@#D``(`Y``!@.0``8#D` -M`*"Y``"@N0``2+H``$BZ``"LN@``K+H``!2[```4NP``*+L``"B[``#DN@`` -MY+H``/"Y``#PN0``"#H```@Z``"D.@``I#H``+`Z``"P.@``T#D``-`Y``!P -MN@``<+H``+"Z``"PN@``E+H``)2Z``!HN@``:+H``/"Y``#PN0``0#D``$`Y -M````.0```#D``&BZ``!HN@``Y+H``.2Z``#HN@``Z+H``,"Z``#`N@``.+H` -M`#BZ```(.@``"#H``+PZ``"\.@``H#H``*`Z``#@.0``X#D``)"Y``"0N0`` -MH+H``*"Z```>NP``'KL``$2[``!$NP``+KL``"Z[```6NP``%KL``.BZ``#H -MN@```+H```"Z``"D.@``I#H``"@[```H.P``8#L``&`[``!N.P``;CL``"0[ -M```D.P``@#@``(`X```8NP``&+L``':[``!VNP``D+L``)"[``"*NP``BKL` -M`#:[```VNP``>+H``'BZ``"`.```@#@``$`Y``!`.0``8#D``&`Y``#`.``` -MP#@````X````.```$#H``!`Z``#8.@``V#H```H[```*.P``Y#H``.0Z``"4 -M.@``E#H``+`Y``"P.0``@+H``("Z```,NP``#+L``!B[```8NP``!+L```2[ -M``#XN@``^+H``-2Z``#4N@``<+H``'"Z``"`N0``@+D``(`Y``"`.0``A#H` -M`(0Z``#4.@``U#H``)PZ``"<.@``(#D``"`Y```(N@``"+H``'"Z``!PN@`` -MH+H``*"Z``!0N@``4+H``-`Y``#0.0``L#H``+`Z``"D.@``I#H``!@Z```8 -M.@``P+D``,"Y``#,N@``S+H```R[```,NP``O+H``+RZ````N````+@``'@Z -M``!X.@``Q#H``,0Z``#<.@``W#H``(0Z``"$.@```+H```"Z``#4N@``U+H` -M`/BZ``#XN@``]+H``/2Z``#CL``.PZ``#L.@`` -M4+H``%"Z```NNP``+KL``&*[``!BNP``8+L``&"[```:NP``&KL``"BZ```H -MN@``.#H``#@Z``"L.@``K#H``/PZ``#\.@``&#L``!@[````.P```#L``)`Z -M``"0.@``"#H```@Z```@N0``(+D``)2Z``"4N@``X+H``."Z``"HN@``J+H` -M``"Z````N@``D#D``)`Y``"(.@``B#H``+PZ``"\.@``4#H``%`Z```@N0`` -M(+D``/"Y``#PN0```+D```"Y``"0.0``D#D``(@Z``"(.@``!CL```8[```2 -M.P``$CL``)@Z``"8.@``P+@``,"X``"$N@``A+H``-"Z``#0N@``R+H``,BZ -M```HN@``*+H``+`Y``"P.0``"#H```@Z``"P.0``L#D``+`Y``"P.0```#D` -M```Y``!`N0``0+D``"`Y```@.0``E#H``)0Z``#D.@``Y#H``/`Z``#P.@`` -M^#H``/@Z``#L.@``[#H``*0Z``"D.@``.#H``#@Z``"P.0``L#D``-"Y``#0 -MN0``S+H``,RZ```2NP``$KL``/RZ``#\N@``G+H``)RZ````N0```+D``-0Z -M``#4.@``4#L``%`[``!>.P``7CL``!X[```>.P``D#H``)`Z``"@N0``H+D` -M`+RZ``"\N@``L+H``+"Z``#`N```P+@``)`Z``"0.@``X#H``.`Z```*.P`` -M"CL```@[```(.P``G#H``)PZ``!@.0``8#D````X````.```8#D``&`Y```@ -M.0``(#D``,`X``#`.````#D````Y``#`N```P+@``."Y``#@N0``8+D``&"Y -M``#0.0``T#D``!@Z```8.@``(#H``"`Z``"$.@``A#H``+@Z``"X.@``O#H` -M`+PZ``#D.@``Y#H``"([```B.P``-#L``#0[```*.P``"CL``(`Z``"`.@`` -MT+D``-"Y``#TN@``]+H``"R[```LNP``_+H``/RZ``"PN0``L+D``)0Z``"4 -M.@``$#L``!`[```\.P``/#L``!H[```:.P``6#H``%@Z``#@N0``X+D``#BZ -M```XN@``&+H``!BZ``"`N```@+@``&@Z``!H.@``V#H``-@Z``#<.@``W#H` -M`-`Z``#0.@``X#H``.`Z``#<.@``W#H``)@Z``"8.@``2#H``$@Z```0.@`` -M$#H```"X````N```6+H``%BZ``!XN@``>+H```BZ```(N@``P+@``,"X``"0 -M.0``D#D``#@Z```X.@``,#H``#`Z``!`.0``0#D``&`Y``!@.0``A#H``(0Z -M``#L.@``[#H``!@[```8.P``.#L``#@[```P.P``,#L``+@Z``"X.@``P+D` -M`,"Y``"XN@``N+H``-BZ``#8N@``I+H``*2Z```@N0``(+D``*@Z``"H.@`` -M\#H``/`Z``#`.@``P#H``'`Z``!P.@``P#D``,`Y``#`N0``P+D``#"Z```P -MN@``P+@``,"X``!0.@``4#H``)0Z``"4.@``O#H``+PZ``#T.@``]#H```([ -M```".P``T#H``-`Z``"<.@``G#H``"`Z```@.@``$+H``!"Z``# -M.P``;#L``&P[``!T.P``=#L``#H[```Z.P``S#H``,PZ````.0```#D``)"Z -M``"0N@``I+H``*2Z``"@N0``H+D``#@Z```X.@``I#H``*0Z``#,.@``S#H` -M`+`Z``"P.@``P#D``,`Y```(N@``"+H```BZ```(N@``0#D``$`Y``!8.@`` -M6#H``,`Z``#`.@``\#H``/`Z``#`.@``P#H```@Z```(.@``@+@``("X``!@ -MN0``8+D``-"Y``#0N0``T+D``-"Y``!@.0``8#D``%`Z``!0.@``A#H``(0Z -M``"L.@``K#H```8[```&.P``)#L``"0[```8.P``&#L``/`Z``#P.@``A#H` -M`(0Z``"`N0``@+D``*BZ``"HN@``H+H``*"Z```8N@``&+H``,"X``#`N``` -MH#D``*`Y```H.@``*#H``(`Y``"`.0``2+H``$BZ``"8N@``F+H``."Y``#@ -MN0``4#H``%`Z```<.P``'#L``(([``"".P``FCL``)H[``"$.P``A#L``"([ -M```B.P``<#H``'`Z``!0N@``4+H``!B[```8NP``/KL``#Z[```JNP``*KL` -M``J[```*NP``U+H``-2Z```(N@``"+H``'@Z``!X.@``"#L```@[``!`.P`` -M0#L``&([``!B.P``2CL``$H[``#T.@``]#H``#@Z```X.@``P#@``,`X``"P -MN0``L+D```"Z````N@```+D```"Y``"0.0``D#D```"X````N```*+H``"BZ -M``!0N@``4+H``-"Y``#0N0``D#D``)`Y``"\.@``O#H``"P[```L.P``-#L` -M`#0[``#H.@``Z#H```@Z```(.@``0+H``$"Z``#TN@``]+H``!"[```0NP`` -MH+H``*"Z``!@.0``8#D``*`Z``"@.@``[#H``.PZ```*.P``"CL``/`Z``#P -M.@``G#H``)PZ``!0.@``4#H``!@Z```8.@```#@````X````N@```+H``-"Y -M``#0N0``@+@``("X``"`.```@#@````Z````.@``F#H``)@Z``"D.@``I#H` -M`-`Y``#0.0``(+H``""Z``"XN@``N+H``.BZ``#HN@``N+H``+BZ``"`.``` -M@#@``-@Z``#8.@``)#L``"0[```L.P``+#L``!`[```0.P``C#H``(PZ``#@ -MN0``X+D``)2Z``"4N@``4+H``%"Z``"PN0``L+D`````````````P#D``,`Y -M``#`.0``P#D```"Y````N0``\+D``/"Y```@N0``(+D``"`Y```@.0``D#D` -M`)`Y````.@```#H``%@Z``!8.@``,#H``#`Z``!@.0``8#D````Y````.0`` -ML#D``+`Y``!`.0``0#D``$"Y``!`N0``T+D``-"Y```HN@``*+H``%"Z``!0 -MN@```+D```"Y``"<.@``G#H```X[```..P``'CL``!X[```..P``#CL``)@Z -M``"8.@``,+H``#"Z```>NP``'KL``$"[``!`NP``'KL``!Z[``"TN@``M+H` -M``"X````N```G#H``)PZ``#$.@``Q#H``(@Z``"(.@``*#H``"@Z````.@`` -M`#H``$`Y``!`.0``(#D``"`Y```0.@``$#H``$@Z``!(.@``L#D``+`Y```` -MN0```+D```"Y````N0```+D```"Y``!`N0``0+D``,"X``#`N```@+@``("X -M``#@N0``X+D``&BZ``!HN@``0+H``$"Z``#`N0``P+D``$"Y``!`N0```#@` -M```X``"0.0``D#D``,"X``#`N```@+H``("Z``"XN@``N+H``'"Z``!PN@`` -MP#@``,`X``"X.@``N#H``#P[```\.P``8#L``&`[```H.P``*#L``&`Z``!@ -M.@``8+H``&"Z```@NP``(+L``&2[``!DNP``6+L``%B[```*NP``"KL``("Z -M``"`N@``8+D``&"Y``#@.0``X#D``&`Z``!@.@``6#H``%@Z``!0.@``4#H` -M`&@Z``!H.@``,#H``#`Z``"`.```@#@``$"Y``!`N0``P+@``,"X```````` -M```````X````.```\#D``/`Y```P.@``,#H``,`X``#`.```A+H``(2Z``#L -MN@``[+H``!"[```0NP``"+L```B[``"4N@``E+H``*`Y``"@.0``E#H``)0Z -M``"<.@``G#H``&@Z``!H.@``H#D``*`Y```0N@``$+H``'BZ``!XN@``T+D` -M`-"Y``#`.0``P#D``"@Z```H.@``\#D``/`Y````.````#@``$BZ``!(N@`` -MQ+H``,2Z``"TN@``M+H``#BZ```XN@`````````````H.@``*#H``+0Z``"T -M.@``S#H``,PZ``"$.@``A#H``+`Y``"P.0``P+@``,"X``!`N@``0+H``-RZ -M``#+H``'BZ``#LN@``[+H``!:[```6NP``%KL``!:[``#0 -MN@``T+H``!BZ```8N@``D+D``)"Y```8N@``&+H``%"Z``!0N@``8+H``&"Z -M``!XN@``>+H``%BZ``!8N@``\+D``/"Y``#`N0``P+D``$"Z``!`N@``>+H` -M`'BZ``"`N@``@+H``)RZ``"[ -M``"'NP``.+L``#B[``"8N@``F+H````Z````.@``Y#H``.0Z``#\.@``_#H` -M`'`Z``!P.@``$+H``!"Z``#LN@``[+H``#2[```TNP``2KL``$J[```FNP`` -M)KL``-"Z``#0N@``G+H``)RZ``"XN@``N+H``-BZ``#8N@``Z+H``.BZ``#L -MN@``[+H``+"Z``"PN@```+H```"Z``#`.```P#@````Y````.0``P+@``,"X -M```(N@``"+H``+"Z``"PN@``"KL```J[```NP``++L` -M`"R[```TNP``-+L``""[```@NP``Z+H``.BZ``"(N@``B+H``("X``"`N``` -M0#H``$`Z``!`.@``0#H````X````.```2+H``$BZ``"\N@``O+H``/BZ``#X -MN@``Z+H``.BZ``!XN@``>+H``)"Y``"0N0``T+D``-"Y``"#H``.`Y``#@.0``@+@``("X``#PN0``\+D``!"Z```0N@``0+D` -M`$"Y``"0.0``D#D``&`Y``!@.0```+D```"Y````N@```+H``&BZ``!HN@`` -ML+H``+"Z``"TN@``M+H``(BZ``"(N@``6+H``%BZ``!PN@``<+H``$BZ``!( -MN@``L+D``+"Y``#`.```P#@``/`Y``#P.0``6#H``%@Z```H.@``*#H``&"Y -M``!@N0``D+H``)"Z``#(N@``R+H``,BZ``#(N@``>+H``'BZ``"P.0``L#D` -M`.`Z``#@.@``$#L``!`[``#<.@``W#H``"@Z```H.@``&+H``!BZ``##L``'@[``!@.P`` -M8#L``"0[```D.P``S#H``,PZ``!(.@``2#H```@Z```(.@``D#H``)`Z```$ -M.P``!#L``"H[```J.P``,CL``#([```:.P``&CL``,0Z``#$.@``"#H```@Z -M``#`.```P#@``/`Y``#P.0``@#H``(`Z``#`.@``P#H``/0Z``#T.@``!#L` -M``0[``#P.@``\#H``.`Z``#@.@``_#H``/PZ```..P``#CL``!8[```6.P`` -M'CL``!X[```F.P``)CL``!@[```8.P```CL```([```".P```CL```X[```. -M.P```#L````[``"X.@``N#H``$`Z``!`.@``(#D``"`Y``"`N0``@+D``$`Y -M``!`.0``M#H``+0Z```P.P``,#L``&H[``!J.P``@CL``(([``!H.P``:#L` -M`!([```2.P``0#H``$`Z``"`.```@#@``&`Y``!@.0``>#H``'@Z````.P`` -M`#L``$0[``!$.P``8#L``&`[``!8.P``6#L``#P[```\.P``(#L``"`[``#L -M.@``[#H``*0Z``"D.@``@#H``(`Z```P.@``,#H``(`Y``"`.0```#D````Y -M```@.@``(#H``+PZ``"\.@``#CL```X[```\.P``/#L``%([``!2.P``1#L` -M`$0[```D.P``)#L``!([```2.P``$#L``!`[```,.P``##L```P[```,.P`` -M!CL```8[``#`.@``P#H``/`Y``#P.0``@+D``("Y```@N0``(+D``!@Z```8 -M.@``X#H``.`Z``!*.P``2CL``(([``"".P``?CL``'X[``!&.P``1CL```([ -M```".P``:#H``&@Z``#`.```P#@``(`X``"`.```.#H``#@Z``"D.@``I#H` -M`,PZ``#,.@``[#H``.PZ```,.P``##L``!0[```4.P``&#L``!@[```>.P`` -M'CL``!@[```8.P``_#H``/PZ``#4.@``U#H``-0Z``#4.@``Z#H``.@Z```` -M.P```#L``!X[```>.P``)CL``"8[``#T.@``]#H``"`Z```@.@``X+D``."Y -M``"`N@``@+H``%BZ``!8N@``0#D``$`Y``#P.@``\#H``%8[``!6.P``A#L` -M`(0[``"(.P``B#L``'0[``!T.P``/#L``#P[```..P``#CL```0[```$.P`` -M"CL```H[``#P.@``\#H``+`Z``"P.@``0#H``$`Z``"`.```@#@``/"Y``#P -MN0``X+D``."Y```@.0``(#D``'`Z``!P.@``X#H``.`Z```H.P``*#L``%([ -M``!2.P``8#L``&`[``!F.P``9CL``&P[``!L.P``6CL``%H[```:.P``&CL` -M`)@Z``"8.@``@#@``(`X```XN@``.+H``#BZ```XN@``H#D``*`Y``#@.@`` -MX#H``$([``!".P``<#L``'`[``!V.P``=CL``$([``!".P``W#H``-PZ```X -M.@``.#H````Z````.@``*#H``"@Z``!H.@``:#H``)@Z``"8.@``K#H``*PZ -M``"8.@``F#H``)0Z``"4.@``P#H``,`Z```(.P``"#L``#`[```P.P``7CL` -M`%X[``!Z.P``>CL``&H[``!J.P``.#L``#@[```,.P``##L``,@Z``#(.@`` -M<#H``'`Z``"0.0``D#D``$"Y``!`N0``$+H``!"Z```@N@``(+H``,"X``#` -MN```B#H``(@Z```6.P``%CL``%P[``!<.P``@SL``(,[``"".P``@CL``$@[ -M``!(.P```#L````[``"X.@``N#H``,`Z``#`.@``^#H``/@Z```F.P``)CL` -M`#P[```\.P``*#L``"@[``#,.@``S#H``/`Y``#P.0``L+D``+"Y```PN@`` -M,+H``)"Y``"0N0``8#H``&`Z```(.P``"#L``#X[```^.P``7#L``%P[``!R -M.P``#H``*PZ``"L.@``V#H``-@Z```$.P`` -M!#L``"8[```F.P``0CL``$([```Z.P``.CL```X[```..P``R#H``,@Z``"( -M.@``B#H``'`Z``!P.@``O#H``+PZ```>.P``'CL``%([``!2.P``8CL``&([ -M``!6.P``5CL``"@[```H.P``S#H``,PZ``!X.@``>#H``)@Z``"8.@``[#H` -M`.PZ```<.P``'#L``"H[```J.P``)#L``"0[``#X.@``^#H``)0Z``"4.@`` -M.#H``#@Z``!0.@``4#H``(PZ``",.@``T#H``-`Z```0.P``$#L``"@[```H -M.P``)#L``"0[```@.P``(#L``"H[```J.P``+CL``"X[```B.P``(CL``!`[ -M```0.P``]#H``/0Z``#(.@``R#H``+@Z``"X.@``Z#H``.@Z```:.P``&CL` -M`#([```R.P``+#L``"P[```..P``#CL``)@Z``"8.@```+@```"X```@N@`` -M(+H``$"Y``!`N0``8#H``&`Z```,.P``##L``%@[``!8.P``?CL``'X[``!L -M.P``;#L``#H[```Z.P``"CL```H[``#4.@``U#H``+0Z``"T.@``R#H``,@Z -M``#L.@``[#H``-PZ``#<.@``I#H``*0Z``"`.@``@#H``(`Z``"`.@``@#H` -M`(`Z``"4.@``E#H``*PZ``"L.@``K#H``*PZ``"0.@``D#H``'@Z``!X.@`` -M@#H``(`Z``"@.@``H#H``,@Z``#(.@```CL```([```4.P``%#L```@[```( -M.P``W#H``-PZ``#0.@``T#H``/0Z``#T.@``%#L``!0[```N.P``+CL``#`[ -M```P.P```#L````[```8.@``&#H``&BZ``!HN@``\+H``/"Z```.NP``#KL` -M`+2Z``"TN@``@#D``(`Y```,.P``##L``%8[``!6.P``>#L``'@[``!L.P`` -M;#L``$@[``!(.P``"#L```@[``"H.@``J#H``#`Z```P.@``(#D``"`Y``"@ -MN0``H+D``)"Y``"0N0``@+@``("X``"P.0``L#D``'@Z``!X.@``Y#H``.0Z -M```(.P``"#L``/0Z``#T.@``K#H``*PZ``!H.@``:#H``"`Z```@.@``.#H` -M`#@Z``"4.@``E#H``,@Z``#(.@``Q#H``,0Z``"`.@``@#H``+`Y``"P.0`` -ML+D``+"Y```PN@``,+H``("Y``"`N0``:#H``&@Z``#\.@``_#H``!X[```> -M.P``'CL``!X[``#\.@``_#H``)0Z``"4.@```#H````Z``"P.0``L#D``-`Y -M``#0.0```#H````Z```8.@``&#H``!`Z```0.@```#D````Y``"0N0``D+D` -M`,"Y``#`N0``@+@``("X```@.0``(#D``,`Y``#`.0``,#H``#`Z``"$.@`` -MA#H``*@Z``"H.@``[#H``.PZ```<.P``'#L``"P[```L.P``%CL``!8[``"\ -M.@``O#H``"`Y```@.0``J+H``*BZ```2NP``$KL```R[```,NP``G+H``)RZ -M``"`.```@#@``)@Z``"8.@``]#H``/0Z``#X.@``^#H``+`Z``"P.@``4#H` -M`%`Z```(.@``"#H``-`Y``#0.0```#H````Z```H.@``*#H``/`Y``#P.0`` -M0+D``$"Y``!`N@``0+H``%BZ``!8N@``&+H``!BZ``"`N```@+@``#`Z```P -M.@``N#H``+@Z``#H.@``Z#H``/`Z``#P.@``Z#H``.@Z``#$.@``Q#H``&`Z -M``!@.@```#@````X``!HN@``:+H``/RZ``#\N@``0+L``$"[``!.NP``3KL` -M`!B[```8NP``4+H``%"Z``"(.@``B#H``"P[```L.P``9#L``&0[``!6.P`` -M5CL``!8[```6.P``E#H``)0Z``"`.```@#@``!BZ```8N@``,+H``#"Z``#P -MN0``\+D```BZ```(N@``>+H``'BZ``"LN@``K+H``,RZ``#,N@``S+H``,RZ -M``"@N@``H+H``!"Z```0N@```#D````Y```(.@``"#H``$@Z``!(.@``A#H` -M`(0Z``",.@``C#H``)`Z``"0.@``H#H``*`Z``"8.@``F#H``!`Z```0.@`` -ML+D``+"Y``"(N@``B+H``+RZ``"\N@``Q+H``,2Z``"0N@``D+H``!BZ```8 -MN@``H+D``*"Y``#PN0``\+D``#BZ```XN@``:+H``&BZ``!8N@``6+H``)"Y -M``"0N0``0#H``$`Z``#0.@``T#H``-0Z``#4.@``>#H``'@Z```@N0``(+D` -M`+"Z``"PN@``#KL```Z[```2NP``$KL``,2Z``#$N@``\+D``/"Y```8.@`` -M&#H``+`Z``"P.@``R#H``,@Z``"8.@``F#H``"`Z```@.@```#D````Y```8 -MN@``&+H``+RZ``"\N@``#+L```R[```>NP``'KL``!B[```8NP``Y+H``.2Z -M```8N@``&+H``"@Z```H.@``O#H``+PZ``#<.@``W#H``+`Z``"P.@``(#H` -M`"`Z``"0N0``D+D``""Z```@N@``X+D``."Y``#`N```P+@``("X``"`N``` -M@+D``("Y``!(N@``2+H``,"Z``#`N@``_+H``/RZ``#+H``*RZ``"L -MN@``L+H``+"Z``"8N@``F+H``(2Z``"$N@``C+H``(RZ``!@N@``8+H``+"Y -M``"PN0``L#D``+`Y``"`.@``@#H``,`Z``#`.@``P#H``,`Z``!0.@``4#H` -M`&"Y``!@N0``C+H``(RZ``#(N@``R+H``+"Z``"PN@```+H```"Z```@.@`` -M(#H``)`Z``"0.@``4#H``%`Z``#`N```P+@``)"Z``"0N@``!KL```:[```> -MNP``'KL```2[```$NP``F+H``)BZ``!@N0``8+D``#@Z```X.@``O#H``+PZ -M``#D.@``Y#H``.PZ``#L.@``_#H``/PZ``#H.@``Z#H``)`Z``"0.@``@#@` -M`(`X``!HN@``:+H``-RZ``#+H``#BZ```XN@``D#D``)`Y``"\.@``O#H` -M``P[```,.P``##L```P[``#0.@``T#H``%@Z``!8.@```#D````Y```````` -M`````"`Z```@.@``S#H``,PZ```<.P``'#L``#8[```V.P``+CL``"X[``#X -M.@``^#H``(0Z``"$.@``X#D``.`Y``"0.0``D#D``)`Y``"0.0``P#D``,`Y -M```8.@``&#H``%@Z``!8.@``@#H``(`Z``"D.@``I#H``,PZ``#,.@``V#H` -M`-@Z``"P.@``L#H``&`Z``!@.@``(#D``"`Y```0N@``$+H``%"Z``!0N@`` -MD+D``)"Y```H.@``*#H``,0Z``#$.@``!CL```8[```0.P``$#L``/@Z``#X -M.@``O#H``+PZ``"<.@``G#H``*`Z``"@.@``L#H``+`Z``"P.@``L#H``(@Z -M``"(.@``0#D``$`Y``"(N@``B+H```"[````NP``"KL```J[``#,N@``S+H` -M``"Z````N@``8#H``&`Z```$.P``!#L``"P[```L.P``+#L``"P[```<.P`` -M'#L``/0Z``#T.@``I#H``*0Z```P.@``,#H``$`Y``!`.0``T+D``-"Y``!P -MN@``<+H``'BZ``!XN@```+H```"Z````.0```#D``"@Z```H.@``@#H``(`Z -M``!H.@``:#H``+`Y``"P.0``@+D``("Y````N@```+H``,"Y``#`N0``(#D` -M`"`Y``!P.@``<#H``+PZ``"\.@``J#H``*@Z```@.@``(#H``,"X``#`N``` -MX+D``."Y``#PN0``\+D`````````````(#H``"`Z``",.@``C#H``'@Z``!X -M.@``"#H```@Z```@N0``(+D``$"Z``!`N@``B+H``(BZ``!0N@``4+H``,"Y -M``#`N0```+@```"X````.0```#D``-`Y``#0.0``*#H``"@Z``!(.@``2#H` -M`$@Z``!(.@``,#H``#`Z``!`.0``0#D```BZ```(N@``E+H``)2Z``"PN@`` -ML+H``)"Z``"0N@``D+D``)"Y``!@.@``8#H``-PZ``#<.@``X#H``.`Z``", -M.@``C#H````Y````.0``6+H``%BZ``"XN@``N+H``*BZ``"HN@``.+H``#BZ -M````N0```+D``"`Y```@.0``0#D``$`Y```@N0``(+D``""Z```@N@``0+H` -M`$"Z``#@N0``X+D```"X````N```0#D``$`Y``"@.0``H#D``)`Y``"0.0`` -M(#D``"`Y``"`.```@#@``$`Y``!`.0``P#D``,`Y``#`.0``P#D``&`Y``!@ -M.0```+D```"Y```@N@``(+H``)"Z``"0N@``F+H``)BZ``!XN@``>+H``%BZ -M``!8N@``@+H``("Z``"#H``'@Z -M``#D.@``Y#H```P[```,.P``Y#H``.0Z```@.@``(#H``#BZ```XN@``[+H` -M`.RZ```#H``'@Z``!H.@``:#H``%`Z``!0.@``$#H``!`Z``!@.0``8#D` -M``"Y````N0``P+D``,"Y``"@N0``H+D``)`Y``"0.0``E#H``)0Z``#\.@`` -M_#H``!@[```8.P``#CL```X[``"T.@``M#H``*`Y``"@.0``P+D``,"Y``"@ -MN0``H+D``+`Y``"P.0``H#H``*`Z```&.P``!CL``"`[```@.P``##L```P[ -M``"T.@``M#H```@Z```(.@```+D```"Y``#PN0``\+D``-"Y``#0N0``P+@` -M`,"X```@.0``(#D``,`Y``#`.0``0#H``$`Z``"L.@``K#H``/`Z``#P.@`` -M"CL```H[```*.P``"CL``.@Z``#H.@``C#H``(PZ``#`.0``P#D``,"X``#` -MN```(+D``""Y``#`.```P#@``!`Z```0.@``8#H``&`Z``!@.@``8#H``"@Z -M```H.@``*#H``"@Z``!X.@``>#H``+@Z``"X.@``\#H``/`Z```$.P``!#L` -M`-0Z``#4.@``*#H``"@Z```0N@``$+H``,2Z``#$N@``]+H``/2Z``"XN@`` -MN+H``("Y``"`N0``D#H``)`Z```(.P``"#L``"P[```L.P``-#L``#0[```H -M.P``*#L```8[```&.P``R#H``,@Z``!X.@``>#H``*`Y``"@.0``\+D``/"Y -M``"(N@``B+H``+2Z``"TN@``L+H``+"Z``!PN@``<+H``("Y``"`N0``@#D` -M`(`Y``#0.0``T#D``,`Y``#`.0``\#D``/`Y``!(.@``2#H``*PZ``"L.@`` -M!#L```0[```H.P``*#L``"@[```H.P``\#H``/`Z```H.@``*#H``#BZ```X -MN@``W+H``-RZ``#\N@``_+H``+RZ``"\N@``.+H``#BZ````N0```+D``$`Y -M``!`.0``D#D``)`Y``#`.```P#@`````````````(#D``"`Y```(.@``"#H` -M`&@Z``!H.@``D#H``)`Z``"`.@``@#H``"@Z```H.@``8#D``&`Y````.``` -M`#@``,"X``#`N```D+D``)"Y```@N@``(+H``%BZ``!8N@``>+H``'BZ``!8 -MN@``6+H``,"Y``#`N0``\#D``/`Y``"8.@``F#H``+@Z``"X.@``C#H``(PZ -M``!`.0``0#D``)"Z``"0N@``#+L```R[```BNP``(KL```:[```&NP``I+H` -M`*2Z``"@N0``H+D``/`Y``#P.0``<#H``'`Z``!X.@``>#H``'`Z``!P.@`` -M8#H``&`Z``!H.@``:#H``#@Z```X.@``X#D``.`Y``#`N0``P+D``+BZ``"X -MN@``'+L``!R[```VNP``-KL``#:[```VNP``&KL``!J[``#8N@``V+H``$BZ -M``!(N@``@+@``("X```0.@``$#H``(`Z``"`.@``K#H``*PZ``"P.@``L#H` -M`(@Z``"(.@``D#D``)`Y``!8N@``6+H```J[```*NP``/+L``#R[```ZNP`` -M.KL```J[```*NP``D+H``)"Z``"`N```@+@```@Z```(.@``T#D``-`Y``"0 -MN0``D+D``(2Z``"$N@``Q+H``,2Z``#(N@``R+H``)"Z``"0N@```+H```"Z -M```@N0``(+D``)"Y``"0N0``&+H``!BZ``!XN@``>+H``*RZ``"LN@``R+H` -M`,BZ``#$N@``Q+H``+"Z``"PN@``J+H``*BZ``"4N@``E+H``&BZ``!HN@`` -M`+H```"Z````.````#@```@Z```(.@``,#H``#`Z``!`.0``0#D``$"Z``!` -MN@``V+H``-BZ```2NP``$KL``!Z[```>NP``#KL```Z[``#8N@``V+H``)RZ -M``"#H``'@Z``"0.@``D#H``"`Z```@.@``D+D``)"Y``"LN@`` -MK+H``!*[```2NP``+KL``"Z[```BNP``(KL```*[```"NP``P+H``,"Z``"( -MN@``B+H``%"Z``!0N@``0+H``$"Z```HN@``*+H```"Z````N@```+H```"Z -M``!(N@``2+H``)2Z``"4N@``N+H``+BZ``#$N@``Q+H``*2Z``"DN@``,+H` -M`#"Z````.````#@``/`Y``#P.0``\#D``/`Y````N````+@``%"Z``!0N@`` -MS+H``,RZ``#PN@``\+H``-"Z``#0N@``G+H``)RZ``"`N@``@+H``(BZ``"( -MN@``M+H``+2Z``#TN@``]+H```R[```,NP``]+H``/2Z``"4N@``E+H``$"Y -M``!`N0``(#H``"`Z``"$.@``A#H``%@Z``!8.@``8#D``&`Y``#0N0``T+D` -M`&BZ``!HN@``K+H``*RZ``#8N@``V+H``.BZ``#HN@``Z+H``.BZ``#NP``Z+H``.BZ``"0N@``D+H``/"Y``#PN0```#@` -M```X``#`.0``P#D``#`Z```P.@``,#H``#`Z``!`.0``0#D``#"Z```PN@`` -MT+H``-"Z```4NP``%+L``"J[```JNP``(KL``"*[``#\N@``_+H``*RZ``"L -MN@``<+H``'"Z``!`N@``0+H``#"Z```PN@``(+H``""Z``"`N0``@+D``.`Y -M``#@.0``B#H``(@Z``"<.@``G#H``#`Z```P.@``D+D``)"Y``#`N@``P+H` -M`"B[```HNP``2KL``$J[```^NP``/KL``!*[```2NP``N+H``+BZ```XN@`` -M.+H``+"Y``"PN0``H+D``*"Y``"0N0``D+D```"Y````N0``P#@``,`X``#` -M.```P#@```"X````N```@+D``("Y```8N@``&+H``&"Z``!@N@``<+H``'"Z -M``!`N@``0+H``!BZ```8N@``&+H``!BZ``!(N@``2+H``)BZ``"8N@``Q+H` -M`,2Z``"\N@``O+H``&"Z``!@N@``(+D``""Y``#0.0``T#D``!`Z```0.@`` -M8#D``&`Y```(N@``"+H``*BZ``"HN@``V+H``-BZ``#$N@``Q+H``'"Z``!P -MN@```+D```"Y``#@.0``X#D```@Z```(.@``8#D``&`Y`````````````("X -M``"`N````#@````X``"`.0``@#D``/`Y``#P.0``T#D``-`Y```````````` -M`"BZ```HN@``I+H``*2Z``#@N@``X+H``/BZ``#XN@``[+H``.RZ``#$N@`` -MQ+H``)2Z``"4N@``"+H```BZ``#P.0``\#D``-@Z``#8.@``,#L``#`[``!6 -M.P``5CL``%`[``!0.P``$CL``!([```@.@``(#H``)BZ``"8N@``)+L``"2[ -M``!(NP``2+L``#*[```RNP``Y+H``.2Z```HN@``*+H``"`Y```@.0``&#H` -M`!@Z``!H.@``:#H``(@Z``"(.@``F#H``)@Z``"L.@``K#H``,`Z``#`.@`` -ML#H``+`Z``!P.@``<#H``*`Y``"@.0``@+D``("Y```0N@``$+H``/"Y``#P -MN0```+D```"Y``!`.0``0#D``&`Y``!@.0``8#D``&`Y``"`.0``@#D``)`Y -M``"0.0``X#D``.`Y```X.@``.#H``%@Z``!8.@``0#H``$`Z``"P.0``L#D` -M`,"X``#`N```T+D``-"Y``"@N0``H+D``(`Y``"`.0``@#H``(`Z``"X.@`` -MN#H``+`Z``"P.@``8#H``&`Z``!`.0``0#D``+"Y``"PN0``X+D``."Y``"` -M.```@#@``&`Z``!@.@``R#H``,@Z``#P.@``\#H``-`Z``#0.@``:#H``&@Z -M``#`.```P#@``/"Y``#PN0``,+H``#"Z``!`N@``0+H``""Z```@N@``H+D` -M`*"Y````.0```#D```@Z```(.@``B#H``(@Z``#(.@``R#H``/`Z``#P.@`` -MZ#H``.@Z``"L.@``K#H``!@Z```8.@``0+D``$"Y```8N@``&+H``."Y``#@ -MN0``@#@``(`X```0.@``$#H``$@Z``!(.@``,#H``#`Z``"0.0``D#D``$"Y -M``!`N0``L+D``+"Y``"`N```@+@```@Z```(.@``C#H``(PZ``"L.@``K#H` -M`(0Z``"$.@``H#D``*`Y``#0N0``T+D``#BZ```XN@``*+H``"BZ``!@N0`` -M8+D``.`Y``#@.0``C#H``(PZ``"X.@``N#H``,`Z``#`.@``M#H``+0Z``"@ -M.@``H#H``'@Z``!X.@``&#H``!@Z``"`.```@#@``""Z```@N@``G+H``)RZ -M``"TN@``M+H``(2Z``"$N@``D+D``)"Y```(.@``"#H``)0Z``"4.@``J#H` -M`*@Z``"`.@``@#H``-`Y``#0.0````````````"`N```@+@``)`Y``"0.0`` -M2#H``$@Z``"0.@``D#H``'@Z``!X.@``\#D``/`Y``"`N```@+@``*"Y``"@ -MN0``L+D``+"Y````N````+@``+`Y``"P.0``$#H``!`Z``"0.0``D#D``)"Y -M``"0N0``>+H``'BZ``"PN@``L+H``+2Z``"TN@``:+H``&BZ``!@N0``8+D` -M`-`Y``#0.0``6#H``%@Z``"L.@``K#H``-0Z``#4.@``Y#H``.0Z``#4.@`` -MU#H``*@Z``"H.@``$#H``!`Z````N@```+H``,BZ``#(N@``#+L```R[```0 -MNP``$+L``-"Z``#0N@``(+H``""Z``"`.0``@#D``!`Z```0.@``P#D``,`Y -M```@N0``(+D``""Z```@N@``6+H``%BZ````N@```+H``(`Y``"`.0``A#H` -M`(0Z``"X.@``N#H``+0Z``"T.@``:#H``&@Z``!`.0``0#D``/"Y``#PN0`` -M6+H``%BZ``"8N@``F+H``,"Z``#`N@``W+H``-RZ``#8N@``V+H``,2Z``#$ -MN@``C+H``(RZ``#0N0``T+D``,`Y``#`.0``4#H``%`Z``!H.@``:#H``"`Z -M```@.@``@#@``(`X````N@```+H``#"Z```PN@``*+H``"BZ```@N@``(+H` -M`$BZ``!(N@``A+H``(2Z``"HN@``J+H``+BZ``"XN@``H+H``*"Z```(N@`` -M"+H``,`Y``#`.0``C#H``(PZ``"@.@``H#H``#@Z```X.@``T+D``-"Y``#4 -MN@``U+H``""[```@NP``+KL``"Z[```>NP``'KL``.RZ``#LN@``B+H``(BZ -M``#`N0``P+D``(`X``"`.```L#D``+`Y```8.@``&#H``$`Z``!`.@``*#H` -M`"@Z``"P.0``L#D``)"Y``"0N0``C+H``(RZ``#@N@``X+H``/BZ``#XN@`` -M[+H``.RZ``#,N@``S+H``+"Z``"PN@``H+H``*"Z``"LN@``K+H``+RZ``"\ -MN@``K+H``*RZ``!0N@``4+H``("X``"`N```*#H``"@Z``"`.@``@#H``"@Z -M```H.@``H+D``*"Y``"XN@``N+H``!"[```0NP``(+L``""[```0NP``$+L` -M`,BZ``#(N@``2+H``$BZ``"`N0``@+D```"Y````N0``@+D``("Y``#@N0`` -MX+D``""Z```@N@``(+H``""Z```@N@``(+H``%"Z``!0N@``E+H``)2Z``"\ -MN@``O+H``,2Z``#$N@``R+H``,BZ``"XN@``N+H``*BZ``"HN@``H+H``*"Z -M``"XN@``N+H``-2Z``#4N@``T+H``-"Z``"8N@``F+H``,"Y``#`N0``,#H` -M`#`Z``"T.@``M#H``+PZ``"\.@``.#H``#@Z``"PN0``L+D``,BZ``#(N@`` -M(KL``"*[``!&NP``1KL``$"[``!`NP``+KL``"Z[```6NP``%KL```2[```$ -MNP``V+H``-BZ``"@N@``H+H``!"Z```0N@``P#D``,`Y``"@.@``H#H``,@Z -M``#(.@``K#H``*PZ```P.@``,#H``)"Y``"0N0``J+H``*BZ```&NP``!KL` -M`""[```@NP``*+L``"B[```FNP``)KL``!:[```6NP``]+H``/2Z``"HN@`` -MJ+H```"Z````N@``L#D``+`Y``!0.@``4#H``!@Z```8.@``0+D``$"Y``"0 -MN@``D+H```"[````NP``'KL``!Z[```*NP``"KL``*"Z``"@N@``8+D``&"Y -M``#P.0``\#D``"@Z```H.@``0#D``$`Y```0N@``$+H``*"Z``"@N@``P+H` -M`,"Z``#`N@``P+H``*RZ``"LN@``F+H``)BZ``"0N@``D+H``*"Z``"@N@`` -ML+H``+"Z``"PN@``L+H``)BZ``"8N@``>+H``'BZ``!`N@``0+H``#BZ```X -MN@``0+H``$"Z``!(N@``2+H```BZ```(N@```+@```"X``#0.0``T#D```@Z -M```(.@``L#D``+`Y``!@N0``8+D``(2Z``"$N@``W+H``-RZ``#TN@``]+H` -M`-RZ``##H``'@Z``"( -M.@``B#H``!@Z```8.@``0+D``$"Y``!XN@``>+H``,"Z``#`N@``U+H``-2Z -M``#$N@``Q+H``+"Z``"PN@``H+H``*"Z``",N@``C+H``$BZ``!(N@``T+D` -M`-"Y```@N0``(+D``(`X``"`.```@#@``(`X``"0N0``D+D``%BZ``!8N@`` -MJ+H``*BZ``"XN@``N+H``)BZ``"8N@``,+H``#"Z````N0```+D````X```` -M.```8+D``&"Y```HN@``*+H``&"Z``!@N@``8+H``&"Z```(N@``"+H````X -M````.````#H````Z````.@```#H`````````````2+H``$BZ``"XN@``N+H` -M`.2Z``#DN@``Q+H``,2Z``!@N@``8+H``&"Y``!@N0``8#D``&`Y``#@.0`` -MX#D``,`Y``#`.0``0#D``$`Y````N0```+D``*"Y``"@N0``\+D``/"Y```@ -MN@``(+H``$BZ``!(N@``,+H``#"Z``#`N0``P+D``*`Y``"@.0``E#H``)0Z -M``#H.@``Z#H``.@Z``#H.@``F#H``)@Z``!`.0``0#D``'"Z``!PN@``Z+H` -M`.BZ```$NP``!+L``-"Z``#0N@``4+H``%"Z``"`N```@+@``.`Y``#@.0`` -M&#H``!@Z```0.@``$#H``"`Z```@.@``>#H``'@Z``"X.@``N#H``-@Z``#8 -M.@``U#H``-0Z``"H.@``J#H``#`Z```P.@``````````````N@```+H``!BZ -M```8N@``X+D``."Y``!`N0``0+D``("X``"`N```@+@``("X````N0```+D` -M````````````H#D``*`Y```X.@``.#H``(`Z``"`.@``C#H``(PZ``",.@`` -MC#H``'@Z``!X.@``:#H``&@Z``"0.@``D#H``+PZ``"\.@``Y#H``.0Z``#@ -M.@``X#H``*0Z``"D.@``P#D``,`Y```8N@``&+H``)BZ``"8N@``@+H``("Z -M``"0N0``D+D``#@Z```X.@``U#H``-0Z```,.P``##L```([```".P``M#H` -M`+0Z```X.@``.#H``$`Y``!`.0```+@```"X````.````#@``)`Y``"0.0`` -M`#H````Z``!(.@``2#H``)PZ``"<.@``Y#H``.0Z```..P``#CL``!P[```< -M.P``%CL``!8[``#H.@``Z#H``'@Z``!X.@``P#@``,`X````N@```+H``"BZ -M```HN@``\+D``/"Y````N````+@``(`Y``"`.0``D#D``)`Y``"`.0``@#D` -M``@Z```(.@``D#H``)`Z``#X.@``^#H``#8[```V.P``9#L``&0[``!F.P`` -M9CL``#@[```X.P``V#H``-@Z``#@.0``X#D``!BZ```8N@``>+H``'BZ``!0 -MN@``4+H``+"Y``"PN0``@#@``(`X``#P.0``\#D``&@Z``!H.@``L#H``+`Z -M``#D.@``Y#H```H[```*.P``&CL``!H[```6.P``%CL``/@Z``#X.@``L#H` -M`+`Z``!0.@``4#H```@Z```(.@``"#H```@Z``!(.@``2#H``&`Z``!@.@`` -M.#H``#@Z````.@```#H``-`Y``#0.0```#H````Z``!X.@``>#H``-0Z``#4 -M.@``&#L``!@[```J.P``*CL``!8[```6.P``S#H``,PZ```H.@``*#H```"X -M````N```0+D``$"Y````.0```#D```@Z```(.@``6#H``%@Z``!P.@``<#H` -M`&`Z``!@.@``*#H``"@Z```8.@``&#H``&`Z``!@.@``N#H``+@Z``#\.@`` -M_#H``!8[```6.P``&#L``!@[```(.P``"#L``-0Z``#4.@``H#H``*`Z```X -M.@``.#H``,`X``#`.```(+H``""Z``"0N@``D+H``+"Z``"PN@``C+H``(RZ -M``"@N0``H+D``(`Z``"`.@``%#L``!0[``!,.P``3#L``%8[``!6.P``,#L` -M`#`[``#(.@``R#H``,`Y``#`.0``P+D``,"Y```0N@``$+H``,"Y``#`N0`` -M`#@````X````.@```#H``#@Z```X.@``,#H``#`Z``!`.@``0#H``'`Z``!P -M.@``F#H``)@Z``"L.@``K#H``)PZ``"<.@``2#H``$@Z````.0```#D``/"Y -M``#PN0``*+H``"BZ```0N@``$+H``("Y``"`N0``@#D``(`Y```X.@``.#H` -M`'@Z``!X.@``B#H``(@Z``"<.@``G#H``,`Z``#`.@``V#H``-@Z``#8.@`` -MV#H``*@Z``"H.@```#H````Z````N@```+H``*"Z``"@N@``N+H``+BZ``"8 -MN@``F+H``!BZ```8N@``P#@``,`X````.@```#H``,`Y``#`.0```+@```"X -M``#@N0``X+D``/"Y``#PN0``P+@``,"X```P.@``,#H``+@Z``"X.@``Z#H` -M`.@Z``#8.@``V#H``*`Z``"@.@``*#H``"@Z``"`N```@+@``"BZ```HN@`` -MA+H``(2Z``"DN@``I+H``+RZ``"\N@``R+H``,BZ``"PN@``L+H``'BZ``!X -MN@``L+D``+"Y``#0.0``T#D``%@Z``!8.@``4#H``%`Z``#0.0``T#D````X -M````.```8+D``&"Y``"`N0``@+D`````````````H#D``*`Y``#0.0``T#D` -M````````````,+H``#"Z``"PN@``L+H``.RZ``#LN@``Y+H``.2Z``"8N@`` -MF+H```BZ```(N@``@+@``("X`````````````&"Y``!@N0``,+H``#"Z``"4 -MN@``E+H``*RZ``"LN@``F+H``)BZ``!PN@``<+H``#"Z```PN@``\+D``/"Y -M``#`N0``P+D``+"Y``"PN0``0+D``$"Y``"`N```@+@```"Y````N0``$+H` -M`!"Z``"NP``'KL``":[```FNP``.+L``#B[``!` -MNP``0+L``#"[```PNP```+L```"[``!@N@``8+H``&`Y``!@.0``.#H``#@Z -M```(.@``"#H``)"Y``"0N0``N+H``+BZ```LNP``++L``&J[``!JNP``@KL` -M`(*[``""NP``@KL``'J[``!ZNP``9KL``&:[``!&NP``1KL``"*[```BNP`` -MX+H``."Z``!HN@``:+H``$"Y``!`N0``@#@``(`X````N````+@``/"Y``#P -MN0``A+H``(2Z``#(N@``R+H``/2Z``#TN@```KL```*[```.NP``#KL``":[ -M```FNP``1+L``$2[``!NP``'KL``/RZ``#\N@``Q+H``,2Z``"L -MN@``K+H``+"Z``"PN@``K+H``*RZ``"@N@``H+H``)RZ``"#H``/`Y``#P.0``*+H``"BZ``#\N@``_+H``#Z[```^NP``5KL` -M`%:[``!"NP``0KL```Z[```.NP``O+H``+RZ``"`N@``@+H``%"Z``!0N@`` -M2+H``$BZ``!8N@``6+H``%BZ``!8N@``(+H``""Z``"@N0``H+D``&"Y``!@ -MN0``L+D``+"Y```PN@``,+H``(RZ``",N@``N+H``+BZ``#(N@``R+H``,RZ -M``#,N@``X+H``."Z```$NP``!+L``!*[```2NP``%KL``!:[```,NP``#+L` -M`."Z``#@N@``A+H``(2Z``#0N0``T+D```"Y````N0``8+D``&"Y```(N@`` -M"+H``&BZ``!HN@``@+H``("Z``!`N@``0+H``,"Y``#`N0``@+D``("Y```( -MN@``"+H``)"Z``"0N@``\+H``/"Z```DNP``)+L``#:[```VNP``)+L``"2[ -M``#\N@``_+H``*BZ``"HN@``6+H``%BZ```@N@``(+H``$BZ``!(N@``<+H` -M`'"Z``!PN@``<+H``$BZ``!(N@``.+H``#BZ```XN@``.+H``$"Z``!`N@`` -M8+H``&"Z``"(N@``B+H``(BZ``"(N@``:+H``&BZ``!(N@``2+H``&"Z``!@ -MN@``F+H``)BZ``#+H``'BZ``#`N0``P+D``"`Y```@.0``D#D``)`Y```@N0`` -M(+D``(2Z``"$N@``_+H``/RZ```FNP``)KL``#B[```XNP``+KL``"Z[```. -MNP``#KL``-2Z``#4N@``H+H``*"Z``"`N@``@+H``%"Z``!0N@``0+H``$"Z -M``!0N@``4+H``(2Z``"$N@``O+H``+RZ```$NP``!+L``"2[```DNP``)KL` -M`":[```(NP``"+L``*BZ``"HN@```+H```"Z````.0```#D``$`Y``!`.0`` -MT+D``-"Y``"TN@``M+H``!*[```2NP``-KL``#:[```\NP``/+L``"R[```L -MNP``&+L``!B[```0NP``$+L``!"[```0NP``#+L```R[````NP```+L``-BZ -M``#8N@``E+H``)2Z```8N@``&+H``+"Y``"PN0``(+H``""Z``"@N@``H+H` -M`/BZ``#XN@``'KL``!Z[```NNP``+KL``"B[```HNP``(+L``""[```8NP`` -M&+L``!"[```0NP``!+L```2[``#TN@``]+H``-BZ``#8N@``O+H``+RZ``"L -MN@``K+H``+RZ``"\N@``^+H``/BZ```NP``@KL``(*[``"3NP``D[L``)*[``"2NP``>+L``'B[```N -MNP``+KL``,RZ``#,N@``,+H``#"Z````N@```+H``%"Z``!0N@``R+H``,BZ -M```,NP``#+L``"R[```LNP``/KL``#Z[``!(NP``2+L``$:[``!&NP``2+L` -M`$B[``!(NP``2+L``$*[``!"NP``*KL``"J[```0NP``$+L``/BZ``#XN@`` -M\+H``/"Z```*NP``"KL``#"[```PNP``3KL``$Z[``!>NP``7KL``%Z[``!> -MNP``6+L``%B[``!*NP``2KL``$*[``!"NP``0KL``$*[``!(NP``2+L``#Z[ -M```^NP``(+L``""[``#HN@``Z+H``)BZ``"8N@``:+H``&BZ``"@N@``H+H` -M``B[```(NP``2+L``$B[``!XNP``>+L``(F[``")NP``B+L``(B[``!VNP`` -M=KL``%:[``!6NP``1KL``$:[``!(NP``2+L``$Z[``!.NP``4+L``%"[``!$ -MNP``1+L``"R[```LNP``#KL```Z[``#PN@``\+H``-BZ``#8N@``R+H``,BZ -M``#(N@``R+H``-RZ``#NP``'KL``$J[``!*NP``@[L` -M`(.[``">NP``GKL``*N[``"KNP``H[L``*.[``"%NP``A;L``#*[```RNP`` -MP+H``,"Z``!`N@``0+H``#"Z```PN@``E+H``)2Z``#TN@``]+H``"R[```L -MNP``3KL``$Z[``!8NP``6+L``%J[``!:NP``7KL``%Z[``!FNP``9KL``&B[ -M``!HNP``8+L``&"[``!$NP``1+L``!B[```8NP``Y+H``.2Z``"\N@``O+H` -M`,2Z``#$N@``\+H``/"Z```:NP``&KL``#Z[```^NP``5+L``%2[``!8NP`` -M6+L``%B[``!8NP``5KL``%:[``!4NP``5+L``$Z[``!.NP``0+L``$"[```< -MNP``'+L``."Z``#@N@``H+H``*"Z``"NP``1KL``$:[``!>NP``7KL``%R[``!NP``'KL``!"[```0NP```+L```"[``#XN@``^+H` -M``B[```(NP``)+L``"2[``!$NP``1+L``%:[``!6NP``2KL``$J[```FNP`` -M)KL``/"Z``#PN@``J+H``*BZ``"4N@``E+H``+RZ``"\N@``_+H``/RZ```2 -MNP``$KL``!2[```4NP```KL```*[``#$N@``Q+H``("Z``"`N@``4+H``%"Z -M``",N@``C+H``,RZ``#,N@``!+L```2[```>NP``'KL``"R[```LNP``,+L` -M`#"[```PNP``,+L``#:[```VNP``++L``"R[```2NP``$KL``-RZ``##L``'@[``!F -M.P``9CL``#([```R.P``W#H``-PZ``!`.@``0#H``$`Y``!`.0``P#@``,`X -M``"0.0``D#D````Z````.@``:#H``&@Z``"\.@``O#H````[````.P``)#L` -M`"0[``!*.P``2CL``&8[``!F.P``:CL``&H[``!8.P``6#L``#X[```^.P`` -M(CL``"([```2.P``$CL``!`[```0.P``%#L``!0[```*.P``"CL``-PZ``#< -M.@``F#H``)@Z```X.@``.#H```@Z```(.@``0#H``$`Z``#$.@``Q#H``!X[ -M```>.P``3CL``$X[``!J.P``:CL``'([``!R.P``9CL``&8[``!<.P``7#L` -M`%X[``!>.P``9#L``&0[``!>.P``7CL``$@[``!(.P``)#L``"0[``#L.@`` -M[#H``(@Z``"(.@``0#H``$`Z``"`.@``@#H``,PZ``#,.@``%#L``!0[``!" -M.P``0CL``&`[``!@.P``;#L``&P[``!J.P``:CL``&8[``!F.P``6CL``%H[ -M``!`.P``0#L``!P[```<.P``]#H``/0Z``#$.@``Q#H``,`Z``#`.@```#L` -M```[```\.P``/#L``'P[``!\.P``DSL``),[``"4.P``E#L``(([``"".P`` -M0CL``$([```&.P``!CL``,PZ``#,.@``R#H``,@Z``#L.@``[#H``!0[```4 -M.P``,CL``#([```^.P``/CL``#H[```Z.P``/#L``#P[``!(.P``2#L``%P[ -M``!<.P``<#L``'`[``!^.P``?CL``'@[``!X.P``9#L``&0[``!0.P``4#L` -M`$8[``!&.P``0CL``$([```\.P``/#L``#P[```\.P``-CL``#8[```F.P`` -M)CL``!H[```:.P``(#L``"`[```T.P``-#L``%8[``!6.P``.P`` -M<#L``'`[``!@.P``8#L``#P[```\.P``&#L``!@[```(.P``"#L``!([```2 -M.P``,CL``#([``!@.P``8#L``'X[``!^.P``@CL``(([``!Z.P``>CL``&H[ -M``!J.P``4#L``%`[```T.P``-#L``!X[```>.P``"CL```H[``#@.@``X#H` -M`+`Z``"P.@``J#H``*@Z``#,.@``S#H```H[```*.P``.#L``#@[``!>.P`` -M7CL``'`[``!P.P``:#L``&@[``!>.P``7CL``%([``!2.P``3#L``$P[``!, -M.P``3#L``%([``!2.P``1#L``$0[```>.P``'CL``-`Z``#0.@``<#H``'`Z -M```8.@``&#H``%@Z``!8.@``U#H``-0Z```N.P``+CL``&([``!B.P``@#L` -M`(`[``"!.P``@3L``'`[``!P.P``2#L``$@[```F.P``)CL```H[```*.P`` -MZ#H``.@Z``"X.@``N#H``)`Z``"0.@``>#H``'@Z``"(.@``B#H``,`Z``#` -M.@``$CL``!([``!$.P``1#L``&8[``!F.P``CL``'H[``!P.P``<#L``$P[``!,.P``(#L``"`[```& -M.P``!CL```0[```$.P``#CL```X[```:.P``&CL``!P[```<.P``#CL```X[ -M``#@.@``X#H``+@Z``"X.@``I#H``*0Z``"<.@``G#H``)PZ``"<.@``M#H` -M`+0Z``#4.@``U#H``/@Z``#X.@``&#L``!@[``!`.P``0#L``&8[``!F.P`` -M>#L``'@[``!N.P``;CL``$@[``!(.P``##L```P[``"H.@``J#H``&@Z``!H -M.@``B#H``(@Z``#0.@``T#H``!0[```4.P``/#L``#P[``!,.P``3#L``#X[ -M```^.P``(CL``"([```*.P``"CL``/PZ``#\.@``]#H``/0Z``#\.@``_#H` -M`/0Z``#T.@``W#H``-PZ``#(.@``R#H``-@Z``#8.@``!CL```8[```J.P`` -M*CL``%@[``!8.P``@#L``(`[``"(.P``B#L``(`[``"`.P``8CL``&([``!` -M.P``0#L``!X[```>.P``!#L```0[``#@.@``X#H``+`Z``"P.@``B#H``(@Z -M``!X.@``>#H``*0Z``"D.@``]#H``/0Z```N.P``+CL``&8[``!F.P``A3L` -M`(4[``"'.P``ASL``'0[``!T.P``3#L``$P[```N.P``+CL``"([```B.P`` -M,CL``#([``!(.P``2#L``%@[``!8.P``3CL``$X[```X.P``.#L``!H[```: -M.P``_#H``/PZ``#H.@``Z#H```0[```$.P``(#L``"`[```X.P``.#L``$8[ -M``!&.P``4#L``%`[``!0.P``4#L``%0[``!4.P``7CL``%X[``!H.P``:#L` -M`&8[``!F.P``7#L``%P[``!2.P``4CL``$P[``!,.P``2#L``$@[``!2.P`` -M4CL``&0[``!D.P``;CL``&X[``!B.P``8CL``$@[``!(.P``)CL``"8[```0 -M.P``$#L``!0[```4.P``.CL``#H[``!J.P``:CL``(D[``").P``D#L``)`[ -M``"*.P``BCL``'0[``!T.P``3#L``$P[```T.P``-#L``#8[```V.P``2#L` -M`$@[``!@.P``8#L``'([``!R.P``>CL``'H[``!V.P``=CL``'8[``!V.P`` -M>CL``'H[``!^.P``?CL``'H[``!Z.P``.P``=CL``'8[``"".P``@CL``(`[ -M``"`.P``:CL``&H[``!..P``3CL``$([``!".P``3#L``$P[``!@.P``8#L` -M`'@[``!X.P``A#L``(0[``"'.P``ASL``(`[``"`.P``#H``'@Z``"$.@``A#H``)@Z -M``"8.@``J#H``*@Z``"@.@``H#H``%@Z``!8.@``L#D``+`Y````N````+@` -M`("X``"`N```0#D``$`Y```0.@``$#H``%`Z``!0.@``4#H``%`Z```(.@`` -M"#H``&`Y``!@.0``P#@``,`X```@.0``(#D``.`Y``#@.0``8#H``&`Z``", -M.@``C#H``&@Z``!H.@``\#D``/`Y``"`N```@+@``."Y``#@N0``$+H``!"Z -M``"@N0``H+D````X````.```T#D``-`Y```P.@``,#H``&@Z``!H.@``A#H` -M`(0Z``!X.@``>#H``$`Z``!`.@``T#D``-`Y``"@N0``H+D``)BZ``"8N@`` -M`+L```"[```+H``."Y``#@N0`` -M@+D``("Y``#0N0``T+D``#"Z```PN@``8+H``&"Z``!PN@``<+H``'"Z``!P -MN@``<+H``'"Z``"`N@``@+H``*"Z``"@N@``W+H``-RZ```$NP``!+L```R[ -M```,NP``!+L```2[``#LN@``[+H``,RZ``#,N@``P+H``,"Z``#$N@``Q+H` -M`,2Z``#$N@``L+H``+"Z``"4N@``E+H``("Z``"`N@``>+H``'BZ``",N@`` -MC+H``+BZ``"XN@``[+H``.RZ```"NP```KL``.2Z``#DN@``K+H``*RZ``!( -MN@``2+H```"Z````N@``,+H``#"Z``"LN@``K+H```:[```&NP``*+L``"B[ -M```PNP``,+L``"*[```BNP``^+H``/BZ``"@N@``H+H``&BZ``!HN@``:+H` -M`&BZ``"0N@``D+H``+2Z``"TN@``V+H``-BZ``#PN@``\+H``/RZ``#\N@`` -M`+L```"[```"NP```KL``/"Z``#PN@``Q+H``,2Z``",N@``C+H``#"Z```P -MN@``T+D``-"Y``#PN0``\+D``(2Z``"$N@``\+H``/"Z```JNP``*KL``$B[ -M``!(NP``2+L``$B[```JNP``*KL```"[````NP``Q+H``,2Z``"@N@``H+H` -M`)2Z``"4N@``D+H``)"Z``"8N@``F+H``)"Z``"0N@``>+H``'BZ``!PN@`` -M<+H``*2Z``"DN@``[+H``.RZ```+H``'"Z``!PN@``8+H` -M`&"Z``!0N@``4+H``$BZ``!(N@``4+H``%"Z``!PN@``<+H``("Z``"`N@`` -M>+H``'BZ``!PN@``<+H``("Z``"`N@``E+H``)2Z``"4N@``E+H``'BZ``!X -MN@``$+H``!"Z`````````````/`Y``#P.0``&#H``!@Z``!@.0``8#D``."Y -M``#@N0``F+H``)BZ``#DN@``Y+H``/BZ``#XN@``R+H``,BZ``!PN@``<+H` -M`*"Y``"@N0``P#@``,`X``!@.0``8#D````Y````.0``P#@``,`X```@.0`` -M(#D``)`Y``"0.0``0#D``$`Y``"`N```@+@``!"Z```0N@``E+H``)2Z``#@ -MN@``X+H```"[````NP``X+H``."Z``"#H``(`Y``"`.0``\+D``/"Y``!8N@``6+H``%BZ``!8N@```+H` -M``"Z`````````````+`Y``"P.0``@#D``(`Y````N````+@``/"Y``#PN0`` -M2+H``$BZ``!8N@``6+H``""Z```@N@``T+D``-"Y``!@N0``8+D``("X``"` -MN```P#@``,`X``"`.0``@#D``/`Y``#P.0``0#H``$`Z``"`.@``@#H``'`Z -M``!P.@``(#H``"`Z```@.0``(#D``&"Y``!@N0```+H```"Z````N@```+H` -M`+"Y``"PN0``0+D``$"Y``"`N0``@+D``,"Y``#`N0``"+H```BZ```(N@`` -M"+H``+"Y``"PN0```#D````Y```8.@``&#H``%@Z``!8.@``0#H``$`Z``#@ -M.0``X#D``"`Y```@.0```#@````X``"0.0``D#D``%`Z``!0.@``H#H``*`Z -M``"T.@``M#H``)@Z``"8.@``&#H``!@Z``"0N0``D+D``("Z``"`N@``H+H` -M`*"Z``"4N@``E+H``$"Z``!`N@``8+D``&"Y``#@.0``X#D``(`Z``"`.@`` -MQ#H``,0Z``#T.@``]#H```@[```(.P``]#H``/0Z``"H.@``J#H``/`Y``#P -M.0``T+D``-"Y``",N@``C+H``*"Z``"@N@``4+H``%"Z````.````#@``%`Z -M``!0.@``L#H``+`Z``#`.@``P#H``*PZ``"L.@``@#H``(`Z``!H.@``:#H` -M`%@Z``!8.@``8#H``&`Z``!0.@``4#H``#`Z```P.@``@#D``(`Y``"0N0`` -MD+D``""Z```@N@``(+H``""Z``"0N0``D+D``(`Y``"`.0``2#H``$@Z``"< -M.@``G#H``+`Z``"P.@``O#H``+PZ``#$.@``Q#H``,@Z``#(.@``O#H``+PZ -M``"D.@``I#H``&@Z``!H.@``P#D``,`Y``!`N0``0+D``/"Y``#PN0``X+D` -M`."Y``!@N0``8+D``"`Y```@.0``T#D``-`Y````.@```#H``,`Y``#`.0`` -MT#D``-`Y```@.@``(#H``(@Z``"(.@``Q#H``,0Z``#X.@``^#H```([```" -M.P``S#H``,PZ``!8.@``6#H````Y````.0``P+D``,"Y``#PN0``\+D``$"Y -M``!`N0``@#D``(`Y```0.@``$#H``"@Z```H.@``(#H``"`Z```0.@``$#H` -M```Z````.@```#H````Z```@.@``(#H``"@Z```H.@``\#D``/`Y``"`.0`` -M@#D``(`X``"`.````#@````X```@.0``(#D``-`Y``#0.0``*#H``"@Z```X -M.@``.#H``"@Z```H.@``,#H``#`Z``!(.@``2#H``'@Z``!X.@``I#H``*0Z -M``#$.@``Q#H``*@Z``"H.@``(#H``"`Z``#0N0``T+D``+BZ``"XN@``"+L` -M``B[```*NP``"KL``,"Z``#`N@``X+D``."Y```H.@``*#H``,@Z``#(.@`` -M"#L```@[```4.P``%#L```H[```*.P``Z#H``.@Z``"T.@``M#H``%@Z``!8 -M.@``P#@``,`X```@N@``(+H``)RZ``" -M.P``'CL``"0[```D.P``*#L``"@[```<.P``'#L```8[```&.P``U#H``-0Z -M``"L.@``K#H``)@Z``"8.@``K#H``*PZ``#8.@``V#H```0[```$.P``#CL` -M``X[```(.P``"#L``/0Z``#T.@``V#H``-@Z``#@.@``X#H```0[```$.P`` -M(CL``"([```T.P``-#L``#`[```P.P``%#L``!0[``#4.@``U#H``'`Z``!P -M.@```#H````Z```(.@``"#H``'`Z``!P.@``N#H``+@Z````.P```#L``!@[ -M```8.P``+CL``"X[```^.P``/CL``%`[``!0.P``6CL``%H[``!8.P``6#L` -M`$([``!".P``'#L``!P[``#0.@``T#H``$@Z``!(.@``0#D``$`Y``"`.``` -M@#@``(`Y``"`.0``&#H``!@Z``"`.@``@#H``,0Z``#$.@``!#L```0[```J -M.P``*CL``%0[``!4.P``>#L``'@[``"`.P``@#L``&X[``!N.P``/#L``#P[ -M``#P.@``\#H``%@Z``!8.@``@#D``(`Y``"`.0``@#D``#@Z```X.@``M#H` -M`+0Z``#\.@``_#H```P[```,.P``!CL```8[``#H.@``Z#H``,0Z``#$.@`` -MK#H``*PZ``"L.@``K#H``,`Z``#`.@``W#H``-PZ``#L.@``[#H``/@Z``#X -M.@``!CL```8[```0.P``$#L``!8[```6.P``$CL``!([````.P```#L``,`Z -M``#`.@``>#H``'@Z```P.@``,#H``$@Z``!(.@``@#H``(`Z``"X.@``N#H` -M`.`Z``#@.@``[#H``.PZ``"\.@``O#H``'@Z``!X.@``$#H``!`Z``#P.0`` -M\#D``"@Z```H.@``C#H``(PZ``"\.@``O#H``-0Z``#4.@``U#H``-0Z``#, -M.@``S#H``,0Z``#$.@``R#H``,@Z``#4.@``U#H``/`Z``#P.@``Y#H``.0Z -M``"T.@``M#H``$`Z``!`.@``(#D``"`Y``"0N0``D+D``-"Y``#0N0``D+D` -M`)"Y``#`.```P#@``,`Y``#`.0``.#H``#@Z``"`.@``@#H``*@Z``"H.@`` -MP#H``,`Z``#@.@``X#H``.PZ``#L.@``S#H``,PZ``!X.@``>#H``*`Y``"@ -M.0``D+D``)"Y``#@N0``X+D``*"Y``"@N0``0#D``$`Y```(.@``"#H``#@Z -M```X.@``"#H```@Z``"0.0``D#D``""Y```@N0``D+D``)"Y``!`N0``0+D` -M`&`Y``!@.0```#H````Z```8.@``&#H``-`Y``#0.0```#D````Y``"`N0`` -M@+D``."Y``#@N0``P+D``,"Y```@N0``(+D`````````````(#D``"`Y``!@ -M.0``8#D``"`Y```@.0```#@````X`````````````("X``"`N```8+D``&"Y -M```(N@``"+H``%BZ``!8N@``B+H``(BZ``"$N@``A+H``#"Z```PN@```+D` -M``"Y``"P.0``L#D``/`Y``#P.0``8#D``&`Y``#`N0``P+D``)"Z``"0N@`` -MT+H``-"Z``#0N@``T+H``*"Z``"@N@``.+H``#BZ```@N0``(+D``$`Y``!` -M.0``8#D``&`Y``"`.```@#@``,"X``#`N```@+D``("Y``#PN0``\+D``"BZ -M```HN@``<+H``'"Z``"PN@``L+H``.RZ``#LN@``!KL```:[````NP```+L` -M`-BZ``#8N@``H+H``*"Z``!@N@``8+H``#"Z```PN@``&+H``!BZ```0N@`` -M$+H``/"Y``#PN0``L+D``+"Y``!@N0``8+D``""Y```@N0``H+D``*"Y``!` -MN@``0+H``*"Z``"@N@``S+H``,RZ``#8N@``V+H``,2Z``#$N@``J+H``*BZ -M``"+H``+RZ``"\N@``!KL```:[```FNP``)KL``#:[```VNP``*KL``"J[ -M```0NP``$+L``-RZ``#NP``'KL``!2[```4 -MNP``Z+H``.BZ``"DN@``I+H``&"Z``!@N@``X+D``."Y``"`N```@+@``(`Y -M``"`.0``X#D``.`Y``#P.0``\#D``&`Y``!@.0``8+D``&"Y```PN@``,+H` -M`(2Z``"$N@``E+H``)2Z``"`N@``@+H``"BZ```HN@``H+D``*"Y``!`N0`` -M0+D``&"Y``!@N0``L+D``+"Y``#0N0``T+D``,"Y``#`N0``@+D``("Y``!@ -MN0``8+D``,"Y``#`N0``0+H``$"Z``"(N@``B+H``*"Z``"@N@``D+H``)"Z -M```XN@``.+H`````````````(#H``"`Z``!X.@``>#H``%@Z``!8.@``\#D` -M`/`Y````.````#@``&"Y``!@N0``D+D``)"Y``#`N```P+@``(`X``"`.``` -M`#D````Y``"`.```@#@``""Y```@N0```+H```"Z```PN@``,+H``$"Z``!` -MN@``.+H``#BZ```XN@``.+H``!"Z```0N@``L+D``+"Y``#`.```P#@``#@Z -M```X.@``P#H``,`Z```".P```CL```H[```*.P``Y#H``.0Z``"$.@``A#H` -M`"`Y```@.0``&+H``!BZ``!HN@``:+H``$"Z``!`N@``X+D``."Y``#`N``` -MP+@``(`X``"`.```P#@``,`X````.````#@``(`X``"`.```P#D``,`Y``!0 -M.@``4#H``)PZ``"<.@``Q#H``,0Z``#0.@``T#H``+PZ``"\.@``D#H``)`Z -M``!@.@``8#H``$@Z``!(.@``0#H``$`Z```H.@``*#H``!@Z```8.@``\#D` -M`/`Y``"P.0``L#D``-`Y``#0.0``*#H``"@Z``!@.@``8#H``(@Z``"(.@`` -MB#H``(@Z``!H.@``:#H``!@Z```8.@``P#D``,`Y```(.@``"#H``(`Z``"` -M.@``Q#H``,0Z``#\.@``_#H```H[```*.P``_#H``/PZ``#$.@``Q#H``)0Z -M``"4.@``:#H``&@Z``!8.@``6#H``'@Z``!X.@``D#H``)`Z``"0.@``D#H` -M`(`Z``"`.@``4#H``%`Z``!(.@``2#H``(`Z``"`.@``M#H``+0Z``#D.@`` -MY#H```8[```&.P``!CL```8[``#P.@``\#H``,@Z``#(.@``J#H``*@Z``"( -M.@``B#H``(`Z``"`.@``A#H``(0Z``"(.@``B#H``(PZ``",.@``H#H``*`Z -M``#4.@``U#H```H[```*.P``)CL``"8[```V.P``-CL``"H[```J.P```CL` -M``([``"<.@``G#H``!`Z```0.@``8#D``&`Y``#`.0``P#D``'`Z``!P.@`` -MV#H``-@Z```0.P``$#L``!@[```8.P``$#L``!`[``#X.@``^#H``-@Z``#8 -M.@``S#H``,PZ``#@.@``X#H``/@Z``#X.@``!CL```8[```,.P``##L``!`[ -M```0.P``##L```P[```,.P``##L```P[```,.P``"CL```H[``#P.@``\#H` -M`,`Z``#`.@``D#H``)`Z``!X.@``>#H``(@Z``"(.@``P#H``,`Z```$.P`` -M!#L``"`[```@.P``)CL``"8[```@.P``(#L``!`[```0.P``^#H``/@Z``#D -M.@``Y#H``/`Z``#P.@``!#L```0[```(.P``"#L```([```".P``\#H``/`Z -M``#<.@``W#H``-PZ``#<.@```#L````[```>.P``'CL``#8[```V.P``.CL` -M`#H[```J.P``*CL```([```".P``I#H``*0Z```H.@``*#H``-`Y``#0.0`` -M`#H````Z``!H.@``:#H``+0Z``"T.@``^#H``/@Z```>.P``'CL``$([``!" -M.P``7CL``%X[``!P.P``<#L``&H[``!J.P``3CL``$X[```@.P``(#L``-0Z -M``#4.@``4#H``%`Z``#0.0``T#D``,`Y``#`.0``*#H``"@Z``!P.@``<#H` -M`)@Z``"8.@``G#H``)PZ``"<.@``G#H``*PZ``"L.@``X#H``.`Z```8.P`` -M&#L``$0[``!$.P``8#L``&`[``!F.P``9CL``$H[``!*.P``'#L``!P[``#0 -M.@``T#H``(0Z``"$.@``,#H``#`Z```@.@``(#H``#`Z```P.@``8#H``&`Z -M``"`.@``@#H``*0Z``"D.@``T#H``-`Z``#\.@``_#H```P[```,.P``%#L` -M`!0[```,.P``##L``/`Z``#P.@``P#H``,`Z``"L.@``K#H``+@Z``"X.@`` -MU#H``-0Z``#X.@``^#H```([```".P``[#H``.PZ``"\.@``O#H``)`Z``"0 -M.@``B#H``(@Z``"8.@``F#H``-`Z``#0.@``!#L```0[```6.P``%CL```H[ -M```*.P``Z#H``.@Z``"D.@``I#H``'`Z``!P.@``2#H``$@Z``!@.@``8#H` -M`(`Z``"`.@``D#H``)`Z``"8.@``F#H``*PZ``"L.@``N#H``+@Z``#8.@`` -MV#H```0[```$.P``'CL``!X[```J.P``*CL``!X[```>.P``!#L```0[``#$ -M.@``Q#H``(`Z``"`.@``*#H``"@Z``#@.0``X#D``*`Y``"@.0``8#D``&`Y -M``"0.0``D#D```@Z```(.@``>#H``'@Z``#0.@``T#H``!@[```8.P``/CL` -M`#X[``!(.P``2#L``"@[```H.P``X#H``.`Z``!(.@``2#H````````````` -MH+D``*"Y``"`N```@+@```@Z```(.@``F#H``)@Z``#8.@``V#H``/PZ``#\ -M.@``^#H``/@Z``#@.@``X#H``,PZ``#,.@``O#H``+PZ``"@.@``H#H``'@Z -M``!X.@``.#H``#@Z```(.@``"#H``/`Y``#P.0``(#H``"`Z``!H.@``:#H` -M`)`Z``"0.@``E#H``)0Z``"0.@``D#H``&@Z``!H.@``0#H``$`Z``!8.@`` -M6#H``)PZ``"<.@``W#H``-PZ```$.P``!#L```H[```*.P``\#H``/`Z``"L -M.@``K#H``$@Z``!(.@``T#D``-`Y``"@.0``H#D``*`Y``"@.0``X#D``.`Y -M``#@.0``X#D``+`Y``"P.0```#D````Y```@.0``(#D``.`Y``#@.0``>#H` -M`'@Z``#$.@``Q#H````[````.P``#CL```X[```&.P``!CL``-PZ``#<.@`` -ML#H``+`Z``"`.@``@#H``#@Z```X.@``"#H```@Z``#`.0``P#D``"`Y```@ -M.0``````````````.````#@``)`Y``"0.0``*#H``"@Z``!X.@``>#H``(PZ -M``",.@``>#H``'@Z```H.@``*#H``.`Y``#@.0``\#D``/`Y```X.@``.#H` -M`)0Z``"4.@``U#H``-0Z``#T.@``]#H``.`Z``#@.@``I#H``*0Z``!`.@`` -M0#H``,`Y``#`.0``@#D``(`Y``"@.0``H#D```@Z```(.@``(#H``"`Z```` -M.@```#H``*`Y``"@.0```#D````Y`````````````(`X``"`.```P#D``,`Y -M``!`.@``0#H``(`Z``"`.@``G#H``)PZ``"T.@``M#H``,0Z``#$.@``T#H` -M`-`Z``#,.@``S#H``*@Z``"H.@``2#H``$@Z````.0```#D``,"Y``#`N0`` -M&+H``!BZ````N@```+H````X````.```0#H``$`Z``"H.@``J#H``+`Z``"P -M.@``D#H``)`Z```@.@``(#H``"`Y```@.0``P+@``,"X````N````+@``&`Y -M``!@.0```#H````Z```P.@``,#H``%`Z``!0.@``6#H``%@Z``!@.@``8#H` -M`'@Z``!X.@``D#H``)`Z``"4.@``E#H``'@Z``!X.@``"#H```@Z``#`.``` -MP#@``+"Y``"PN0``"+H```BZ``#@N0``X+D``$"Y``!`N0``P#@``,`X``"@ -M.0``H#D``/`Y``#P.0``$#H``!`Z```P.@``,#H``'`Z``!P.@``E#H``)0Z -M``"8.@``F#H``'@Z``!X.@``"#H```@Z````.````#@``*"Y``"@N0``L+D` -M`+"Y````N````+@``-`Y``#0.0``0#H``$`Z``!8.@``6#H``!@Z```8.@`` -M`#D````Y``"@N0``H+D``-"Y``#0N0``8+D``&"Y``"0.0``D#D``$@Z``!( -M.@``E#H``)0Z``"<.@``G#H``(0Z``"$.@``,#H``#`Z``"@.0``H#D```"X -M````N```H+D``*"Y````N@```+H``!"Z```0N@```+H```"Z````N0```+D` -M`/`Y``#P.0``G#H``)PZ``#D.@``Y#H``/PZ``#\.@``V#H``-@Z``"`.@`` -M@#H``"`Y```@.0```+H```"Z``!HN@``:+H``("Z``"`N@``8+H``&"Z```@ -MN@``(+H``/"Y``#PN0``P+D``,"Y``"`N0``@+D``(`X``"`.```(#H``"`Z -M``"D.@``I#H``.0Z``#D.@``_#H``/PZ``#@.@``X#H``*`Z``"@.@``$#H` -M`!`Z``"`N```@+@``!BZ```8N@``6+H``%BZ``!@N@``8+H``&"Z``!@N@`` -M6+H``%BZ```PN@``,+H``*"Y``"@N0``8#D``&`Y```X.@``.#H``(PZ``", -M.@``G#H``)PZ``"$.@``A#H``$`Z``!`.@``\#D``/`Y``"@.0``H#D``+`Y -M``"P.0``\#D``/`Y```0.@``$#H``-`Y``#0.0``(#D``"`Y```@N0``(+D` -M`("Y``"`N0``P+@``,"X``"@.0``H#D``$`Z``!`.@``@#H``(`Z``!@.@`` -M8#H```@Z```(.@````````````#0N0``T+D```"Z````N@``D+D``)"Y```@ -M.0``(#D``!@Z```8.@``:#H``&@Z``",.@``C#H``)`Z``"0.@``E#H``)0Z -M``"8.@``F#H``)@Z``"8.@``A#H``(0Z``!(.@``2#H````Z````.@``@#D` -M`(`Y``#`.```P#@``,`X``#`.```8#D``&`Y``"@.0``H#D``"`Y```@.0`` -M@+@``("X``"@N0``H+D``+"Y``"PN0```#@````X``!8.@``6#H``.`Z``#@ -M.@``)#L``"0[```\.P``/#L``#0[```T.P``"CL```H[``"@.@``H#H``,`Y -M``#`.0``8+D``&"Y```0N@``$+H``"BZ```HN@``$+H``!"Z``"@N0``H+D` -M```X````.````#H````Z``"0.@``D#H``-@Z``#8.@``!#L```0[```*.P`` -M"CL``/PZ``#\.@``O#H``+PZ``!P.@``<#H``!`Z```0.@``T#D``-`Y``#0 -M.0``T#D``/`Y``#P.0```#H````Z````.@```#H```@Z```(.@``.#H``#@Z -M``"(.@``B#H``+0Z``"T.@``S#H``,PZ``#(.@``R#H``*`Z``"@.@``,#H` -M`#`Z```@.0``(#D``("X``"`N````#@````X``#P.0``\#D``'`Z``!P.@`` -MI#H``*0Z``"D.@``I#H``(@Z``"(.@``,#H``#`Z``#0.0``T#D``+`Y``"P -M.0``"#H```@Z``!8.@``6#H``)`Z``"0.@``F#H``)@Z``"8.@``F#H``)`Z -M``"0.@``C#H``(PZ``!X.@``>#H``$@Z``!(.@``X#D``.`Y````.````#@` -M`)"Y``"0N0``P+D``,"Y```@N0``(+D``)`Y``"0.0``6#H``%@Z``"H.@`` -MJ#H``+@Z``"X.@``D#H``)`Z```X.@``.#H``,`Y``#`.0``D#D``)`Y``#P -M.0``\#D``%`Z``!0.@``B#H``(@Z``"0.@``D#H``&`Z``!@.@``$#H``!`Z -M``!`.0``0#D``````````````#@````X``!@.0``8#D``,`Y``#`.0``P#D` -M`,`Y``"`.0``@#D````Y````.0``P#@``,`X```@.0``(#D``)`Y``"0.0`` -MT#D``-`Y``#0.0``T#D``.`Y``#@.0```#H````Z```8.@``&#H``$@Z``!( -M.@``A#H``(0Z``"8.@``F#H``(@Z``"(.@``&#H``!@Z````N````+@``!BZ -M```8N@``:+H``&BZ``!8N@``6+H``!"Z```0N@``8+D``&"Y````.````#@` -M`"`Y```@.0``P#@``,`X`````````````(`X``"`.```H#D``*`Y```H.@`` -M*#H``%@Z``!8.@``4#H``%`Z```(.@``"#H``"`Y```@.0``0+D``$"Y``"0 -MN0``D+D``("Y``"`N0``P+@``,"X``"`.```@#@``,`X``#`.``````````` -M``!@N0``8+D``/"Y``#PN0``(+H``""Z``!`N@``0+H``%BZ``!8N@``<+H` -M`'"Z``!XN@``>+H``$BZ``!(N@``L+D``+"Y``"P.0``L#D``(@Z``"(.@`` -MS#H``,PZ``#4.@``U#H``)PZ``"<.@``D#D``)`Y``!`N@``0+H``,RZ``#, -MN@``_+H``/RZ``#PN@``\+H``+RZ``"\N@``@+H``("Z```HN@``*+H```"Z -M````N@``X+D``."Y``"PN0``L+D``)"Y``"0N0``(+D``""Y```````````` -M```X````.```@+@``("X``"`N0``@+D``,"Y``#`N0``X+D``."Y``#0N0`` -MT+D``/"Y``#PN0``*+H``"BZ``"`N@``@+H``*BZ``"HN@``Q+H``,2Z``#$ -MN@``Q+H``*2Z``"DN@``4+H``%"Z``"PN0``L+D``,"X``#`N````+D```"Y -M``#`N0``P+D``!BZ```8N@``.+H``#BZ``!`N@``0+H``#BZ```XN@``2+H` -M`$BZ``"$N@``A+H``+2Z``"TN@``X+H``."Z``#LN@``[+H``-"Z``#0N@`` -MB+H``(BZ``#0N0``T+D``"`Y```@.0``L#D``+`Y```@.0``(#D``+"Y``"P -MN0``<+H``'"Z``"\N@``O+H``/"Z``#PN@``"+L```B[```2NP``$KL``!*[ -M```2NP```KL```*[``#0N@``T+H``)"Z``"0N@``"+H```BZ````N````+@` -M```Y````.0``P+@``,"X```0N@``$+H``(2Z``"$N@``K+H``*RZ``"XN@`` -MN+H``*RZ``"LN@``H+H``*"Z``"HN@``J+H``,2Z``#$N@``\+H``/"Z```$ -MNP``!+L```*[```"NP``T+H``-"Z``"(N@``B+H``/"Y``#PN0``8+D``&"Y -M``"PN0``L+D``#BZ```XN@``F+H``)BZ``#4N@``U+H``.RZ``#LN@``^+H` -M`/BZ``#LN@``[+H``-BZ``#8N@``M+H``+2Z``"4N@``E+H``$BZ``!(N@`` -MX+D``."Y```@N0``(+D``$"Y``!`N0```+H```"Z``"(N@``B+H``-BZ``#8 -MN@``"KL```J[```6NP``%KL```R[```,NP``]+H``/2Z``#0N@``T+H``+BZ -M``"XN@``O+H``+RZ``#(N@``R+H``,"Z``#`N@``E+H``)2Z```@N@``(+H` -M`,"X``#`N```8#D``&`Y```@.0``(#D``$"Y``!`N0``.+H``#BZ``"@N@`` -MH+H``-"Z``#0N@``\+H``/"Z````NP```+L```:[```&NP``$KL``!*[```< -MNP``'+L``!R[```+H``*2Z``"DN@``S+H``,RZ``#8N@`` -MV+H``-2Z``#4N@``L+H``+"Z``"`N@``@+H``$"Z``!`N@``2+H``$BZ``!H -MN@``:+H``(RZ``",N@``B+H``(BZ``!@N@``8+H```"Z````N@``0+D``$"Y -M``"`N```@+@``)"Y``"0N0``$+H``!"Z``!XN@``>+H``)BZ``"8N@``F+H` -M`)BZ``!HN@``:+H``#"Z```PN@``&+H``!BZ``!`N@``0+H``("Z``"`N@`` -MG+H``)RZ``"8N@``F+H``("Z``"`N@``(+H``""Z``"PN0``L+D``,"X``#` -MN```@+@``("X```@N0``(+D``+"Y``"PN0``T+D``-"Y``#@N0``X+D``."Y -M``#@N0``$+H``!"Z```PN@``,+H``%"Z``!0N@``0+H``$"Z```8N@``&+H` -M`&"Y``!@N0``P#@``,`X``!@.0``8#D``(`X``"`.```@+D``("Y```HN@`` -M*+H``&"Z``!@N@``6+H``%BZ````N@```+H```"Y````N0``(#D``"`Y```` -M.0```#D``,"X``#`N```X+D``."Y```0N@``$+H``/"Y``#PN0```+D```"Y -M``"0.0``D#D``"@Z```H.@``4#H``%`Z```P.@``,#H``,`Y``#`.0```#D` -M```Y``"`N```@+@``&"Y``!@N0``L+D``+"Y``#0N0``T+D``-"Y``#0N0`` -ML+D``+"Y``#`N```P+@``)`Y``"0.0``(#H``"`Z``!0.@``4#H``$`Z``!` -M.@``X#D``.`Y``"`.```@#@``$"Y``!`N0``@+@``("X``#@.0``X#D``(PZ -M``",.@``U#H``-0Z``#T.@``]#H``.`Z``#@.@``J#H``*@Z``!`.@``0#H` -M`$`Y``!`.0```+D```"Y``"0N0``D+D``)"Y``"0N0``@+D``("Y``"`N0`` -M@+D``,"X``#`N```D#D``)`Y``!8.@``6#H``,@Z``#(.@``##L```P[```D -M.P``)#L``"0[```D.P``$CL``!([``#H.@``Z#H``*PZ``"L.@``<#H``'`Z -M```P.@``,#H```@Z```(.@``L#D``+`Y``!@.0``8#D``(`Y``"`.0```#H` -M```Z``!P.@``<#H``+@Z``"X.@``[#H``.PZ````.P```#L``.PZ``#L.@`` -MQ#H``,0Z``"D.@``I#H``)@Z``"8.@``M#H``+0Z``#P.@``\#H``!8[```6 -M.P``(CL``"([```4.P``%#L``.PZ``#L.@``K#H``*PZ``"`.@``@#H``&`Z -M``!@.@``A#H``(0Z``"L.@``K#H``-PZ``#<.@``_#H``/PZ```(.P``"#L` -M``H[```*.P``"CL```H[```$.P``!#L``/PZ``#\.@``W#H``-PZ``"X.@`` -MN#H``*`Z``"@.@``K#H``*PZ``#4.@``U#H``!`[```0.P``-#L``#0[``!0 -M.P``4#L``%([``!2.P``1#L``$0[```D.P``)#L```0[```$.P``V#H``-@Z -M``#0.@``T#H``-@Z``#8.@``W#H``-PZ``#4.@``U#H``,`Z``#`.@``M#H` -M`+0Z``#(.@``R#H````[````.P``*#L``"@[``!0.P``4#L``&X[``!N.P`` -M>#L``'@[``!J.P``:CL``$X[``!..P``,#L``#`[```8.P``&#L```0[```$ -M.P``Z#H``.@Z``#0.@``T#H``,0Z``#$.@``R#H``,@Z``#L.@``[#H``!8[ -M```6.P``.CL``#H[``!8.P``6#L``&@[``!H.P``9#L``&0[``!2.P``4CL` -M`#@[```X.P``)CL``"8[```@.P``(#L``"`[```@.P``'#L``!P[```6.P`` -M%CL```8[```&.P``^#H``/@Z````.P```#L``!@[```8.P``/CL``#X[``!L -M.P``;#L``(8[``"&.P``BSL``(L[``"!.P``@3L``%P[``!<.P``,#L``#`[ -M```2.P``$CL```([```".P```#L````[```$.P``!#L```X[```..P``%CL` -M`!8[```D.P``)#L``#([```R.P``1#L``$0[``!4.P``5#L``&`[``!@.P`` -M8CL``&([``!>.P``7CL``%H[``!:.P``7#L``%P[``!F.P``9CL``'([``!R -M.P``=#L``'0[``!D.P``9#L``$`[``!`.P``$CL``!([``#4.@``U#H``*PZ -M``"L.@``P#H``,`Z```&.P``!CL``#H[```Z.P``9CL``&8[``"!.P``@3L` -M`(4[``"%.P``@3L``($[``!T.P``=#L``&8[``!F.P``6CL``%H[``!..P`` -M3CL``#X[```^.P``,#L``#`[```F.P``)CL``"@[```H.P``-#L``#0[``!* -M.P``2CL``&`[``!@.P``;#L``&P[``!D.P``9#L``%0[``!4.P``/CL``#X[ -M```N.P``+CL``"0[```D.P``)#L``"0[```F.P``)CL``"H[```J.P``+#L` -M`"P[```V.P``-CL``$8[``!&.P``9CL``&8[``"#.P``@SL``)([``"2.P`` -MDSL``),[``"'.P``ASL``&`[``!@.P``,#L``#`[```$.P``!#L``/`Z``#P -M.@``^#H``/@Z```2.P``$CL``"H[```J.P``/#L``#P[```\.P``/#L``#H[ -M```Z.P``-#L``#0[```Z.P``.CL``$8[``!&.P``5CL``%8[``!@.P``8#L` -M`&8[``!F.P``:#L``&@[``!D.P``9#L``&0[``!D.P``8CL``&([``!6.P`` -M5CL``$([``!".P``)#L``"0[```(.P``"#L``.@Z``#H.@``Z#H``.@Z```* -M.P``"CL``"X[```N.P``3#L``$P[``!:.P``6CL``%8[``!6.P``1CL``$8[ -M```V.P``-CL``#`[```P.P``.#L``#@[``!,.P``3#L``%X[``!>.P``8CL` -M`&([``!6.P``5CL``#P[```\.P``)#L``"0[```6.P``%CL``!([```2.P`` -M&#L``!@[```B.P``(CL``"@[```H.P``*CL``"H[```N.P``+CL``#`[```P -M.P``-CL``#8[```\.P``/#L``$`[``!`.P``.CL``#H[```L.P``+#L``!H[ -M```:.P``%#L``!0[```:.P``&CL``"H[```J.P``/#L``#P[``!".P``0CL` -M`#P[```\.P``*CL``"H[```8.P``&#L```X[```..P``$CL``!([```@.P`` -M(#L``#`[```P.P``.#L``#@[```R.P``,CL``"`[```@.P``$CL``!([```( -M.P``"#L```P[```,.P``&CL``!H[```F.P``)CL``"8[```F.P``&CL``!H[ -M```&.P``!CL``.@Z``#H.@``W#H``-PZ``#L.@``[#H``!`[```0.P``*CL` -M`"H[``!`.P``0#L``$X[``!..P``5#L``%0[``!4.P``5#L``$P[``!,.P`` -M-CL``#8[```4.P``%#L``-@Z``#8.@``B#H``(@Z```@.@``(#H```@Z```( -M.@``:#H``&@Z``#8.@``V#H``"0[```D.P``4CL``%([``!B.P``8CL``%8[ -M``!6.P``-CL``#8[```4.P``%#L``/@Z``#X.@``\#H``/`Z``#T.@``]#H` -M``0[```$.P``"#L```@[```&.P``!CL``/@Z``#X.@``Z#H``.@Z``#D.@`` -MY#H``/`Z``#P.@``]#H``/0Z``#P.@``\#H``.`Z``#@.@``S#H``,PZ``#$ -M.@``Q#H``-`Z``#0.@``Z#H``.@Z```$.P``!#L```P[```,.P``##L```P[ -M```$.P``!#L``/@Z``#X.@``]#H``/0Z```".P```CL``!([```2.P``'CL` -M`!X[```:.P``&CL```P[```,.P``[#H``.PZ``"\.@``O#H``*0Z``"D.@`` -MI#H``*0Z``"X.@``N#H``,0Z``#$.@``O#H``+PZ``"<.@``G#H``'`Z``!P -M.@``4#H``%`Z``"`.@``@#H``,0Z``#$.@``##L```P[```T.P``-#L``%`[ -M``!0.P``5#L``%0[``!$.P``1#L``"8[```F.P``!CL```8[``#$.@``Q#H` -M`'@Z``!X.@```#H````Z``!`.0``0#D``````````````#D````Y```8.@`` -M&#H``*0Z``"D.@``_#H``/PZ```>.P``'CL``"@[```H.P``)#L``"0[```6 -M.P``%CL```H[```*.P``!#L```0[```&.P``!CL```([```".P``[#H``.PZ -M``"P.@``L#H``%`Z``!0.@``H#D``*`Y```@.0``(#D``,`Y``#`.0``<#H` -M`'`Z``#0.@``T#H```P[```,.P``(#L``"`[```B.P``(CL``!([```2.P`` -M^#H``/@Z``#(.@``R#H``*@Z``"H.@``A#H``(0Z``!0.@``4#H``#`Z```P -M.@``2#H``$@Z``"(.@``B#H``,0Z``#$.@``!#L```0[```8.P``&#L``!H[ -M```:.P``##L```P[``#H.@``Z#H``+0Z``"T.@``E#H``)0Z``"4.@``E#H` -M`*0Z``"D.@``M#H``+0Z``"P.@``L#H``)`Z``"0.@``:#H``&@Z``!0.@`` -M4#H``&@Z``!H.@``G#H``)PZ``#8.@``V#H``/PZ``#\.@``!CL```8[```" -M.P```CL``.PZ``#L.@``T#H``-`Z``#(.@``R#H``-0Z``#4.@``Y#H``.0Z -M``#D.@``Y#H``,PZ``#,.@``I#H``*0Z``"$.@``A#H``%@Z``!8.@``6#H` -M`%@Z``"`.@``@#H``*`Z``"@.@``O#H``+PZ``#4.@``U#H``.@Z``#H.@`` -M^#H``/@Z```$.P``!#L```H[```*.P```CL```([``#0.@``T#H``(0Z``"$ -M.@```#H````Z``"0.0``D#D``/`Y``#P.0``A#H``(0Z``#H.@``Z#H``"([ -M```B.P``/CL``#X[```X.P``.#L``!@[```8.P``U#H``-0Z``!X.@``>#H` -M``@Z```(.@``P#D``,`Y``"P.0``L#D``.`Y``#@.0``&#H``!@Z``!@.@`` -M8#H``*`Z``"@.@``T#H``-`Z````.P```#L``!0[```4.P``&CL``!H[```4 -M.P``%#L```0[```$.P``X#H``.`Z``#(.@``R#H``,@Z``#(.@``Q#H``,0Z -M``"X.@``N#H``)0Z``"4.@``2#H``$@Z``#P.0``\#D``,`Y``#`.0```#H` -M```Z``!@.@``8#H``*PZ``"L.@``W#H``-PZ``#L.@``[#H``.0Z``#D.@`` -MS#H``,PZ``#(.@``R#H``-PZ``#<.@```#L````[```2.P``$CL``!0[```4 -M.P``!CL```8[``#4.@``U#H``)PZ``"<.@``8#H``&`Z```H.@``*#H``#`Z -M```P.@``4#H``%`Z``!P.@``<#H``'@Z``!X.@``A#H``(0Z``"8.@``F#H` -M`+@Z``"X.@``V#H``-@Z``#P.@``\#H``/@Z``#X.@``[#H``.PZ``#@.@`` -MX#H``-0Z``#4.@``U#H``-0Z``#@.@``X#H``/`Z``#P.@``]#H``/0Z``#4 -M.@``U#H``)@Z``"8.@``0#H``$`Z``#@.0``X#D````Z````.@``4#H``%`Z -M``"D.@``I#H``.`Z``#@.@``\#H``/`Z``#<.@``W#H``+PZ``"\.@``E#H` -M`)0Z``"$.@``A#H``)@Z``"8.@``O#H``+PZ``#<.@``W#H``/`Z``#P.@`` -M[#H``.PZ``#8.@``V#H``,0Z``#$.@``L#H``+`Z``"<.@``G#H``(0Z``"$ -M.@``6#H``%@Z```X.@``.#H``#@Z```X.@``:#H``&@Z``"D.@``I#H``-@Z -M``#8.@```#L````[````.P```#L``-@Z``#8.@``E#H``)0Z```@.@``(#H` -M`,`Y``#`.0``X#D``.`Y``!8.@``6#H``*PZ``"L.@``Y#H``.0Z``#X.@`` -M^#H``/0Z``#T.@``W#H``-PZ``#,.@``S#H``,`Z``#`.@``N#H``+@Z``"@ -M.@``H#H``'`Z``!P.@``$#H``!`Z``"@.0``H#D``$`Y``!`.0``H#D``*`Y -M```X.@``.#H``)@Z``"8.@``U#H``-0Z``#T.@``]#H``/0Z``#T.@``W#H` -M`-PZ``"\.@``O#H``)PZ``"<.@``@#H``(`Z``!(.@``2#H``!`Z```0.@`` -MH#D``*`Y``"`.0``@#D``-`Y``#0.0``6#H``%@Z``"P.@``L#H``.PZ``#L -M.@``_#H``/PZ``#@.@``X#H``)`Z``"0.@```#H````Z``"`.```@#@``("X -M``"`N```@#@``(`X``#0.0``T#D``"@Z```H.@``2#H``$@Z```P.@``,#H` -M``@Z```(.@``P#D``,`Y``#@.0``X#D``!@Z```8.@``:#H``&@Z``"4.@`` -ME#H``*PZ``"L.@``J#H``*@Z``"@.@``H#H``(@Z``"(.@``6#H``%@Z``#P -M.0``\#D```"X````N```(+H``""Z``"0N@``D+H``+"Z``"PN@``H+H``*"Z -M``!8N@``6+H```"Y````N0``$#H``!`Z``"$.@``A#H``)0Z``"4.@``D#H` -M`)`Z``!P.@``<#H``&`Z``!@.@``0#H``$`Z```P.@``,#H``.`Y``#@.0`` -M`#@````X```8N@``&+H``)BZ``"8N@``U+H``-2Z``#+H``'BZ``#@N0``X+D`````````````8#D``&`Y``"@.0``H#D``*`Y -M``"@.0``H#D``*`Y``!`.0``0#D`````````````L+D``+"Y```PN@``,+H` -M`(2Z``"$N@``F+H``)BZ``"4N@``E+H``&BZ``!HN@``&+H``!BZ``#`N0`` -MP+D``-"Y``#0N0``$+H``!"Z``!0N@``4+H``'"Z``!PN@``<+H``'"Z``!H -MN@``:+H``&BZ``!HN@``B+H``(BZ``"LN@``K+H``-2Z``#4N@``[+H``.RZ -M``#8N@``V+H``)RZ``"+H` -M`,"Z``#`N@``"+L```B[```FNP``)KL``"Z[```NNP``(+L``""[```"NP`` -M`KL``,"Z``#`N@``D+H``)"Z``!HN@``:+H``&"Z``!@N@``:+H``&BZ``"$ -MN@``A+H``)"Z``"0N@``J+H``*BZ``#,N@``S+H``/RZ``#\N@``%KL``!:[ -M```DNP``)+L``!R[```+H` -M`'BZ``!8N@``6+H``%BZ``!8N@``@+H``("Z``"4N@``E+H``+"Z``"PN@`` -MS+H``,RZ````NP```+L``!J[```:NP``-+L``#2[```XNP``.+L``"B[```H -MNP```KL```*[``"TN@``M+H``&BZ``!HN@``.+H``#BZ``!@N@``8+H``*2Z -M``"DN@``U+H``-2Z``#PN@``\+H``.BZ``#HN@``S+H``,RZ``"LN@``K+H` -M`*"Z``"@N@``L+H``+"Z``#0N@``T+H``.RZ``#LN@```+L```"[```(NP`` -M"+L```Z[```.NP``#KL```Z[```.NP``#KL```J[```*NP```+L```"[``#@ -MN@``X+H``+BZ``"XN@``D+H``)"Z``!XN@``>+H``(BZ``"(N@``K+H``*RZ -M``#,N@``S+H``-BZ``#8N@``R+H``,BZ``"HN@``J+H``)2Z``"4N@``F+H` -M`)BZ``#`N@``P+H```"[````NP``'KL``!Z[```PNP``,+L``"Z[```NNP`` -M%KL``!:[``#TN@``]+H``+RZ``"\N@``J+H``*BZ``"@N@``H+H``*2Z``"D -MN@``I+H``*2Z``"DN@``I+H``)BZ``"8N@``E+H``)2Z``"8N@``F+H``*BZ -M``"HN@``O+H``+RZ``#(N@``R+H``,BZ``#(N@``R+H``,BZ``#`N@``P+H` -M`-2Z``#4N@``]+H``/2Z```,NP``#+L``!*[```2NP``"KL```J[``#PN@`` -M\+H``+RZ``"\N@``D+H``)"Z``"`N@``@+H``(BZ``"(N@``F+H``)BZ``"@ -MN@``H+H``)2Z``"4N@``<+H``'"Z``!(N@``2+H``%"Z``!0N@``A+H``(2Z -M``"TN@``M+H``.2Z``#DN@```KL```*[```"NP```KL``.BZ``#HN@``O+H` -M`+RZ``"@N@``H+H``)BZ``"8N@``J+H``*BZ``#`N@``P+H``-BZ``#8N@`` -MU+H``-2Z``#$N@``Q+H``*2Z``"DN@``B+H``(BZ``!0N@``4+H```BZ```( -MN@``D+D``)"Y```@N0``(+D``,"X``#`N```P+D``,"Y``!@N@``8+H``-2Z -M``#4N@``&+L``!B[```TNP``-+L``"Z[```NNP``#+L```R[``"PN@``L+H` -M`!"Z```0N@```#@````X``"`.0``@#D``"`Y```@.0``8+D``&"Y```0N@`` -M$+H``%"Z``!0N@``<+H``'"Z``"$N@``A+H``(RZ``",N@``H+H``*"Z``"H -MN@``J+H``*2Z``"DN@``C+H``(RZ``!(N@``2+H``-"Y``#0N0``0+D``$"Y -M``#`N```P+@``&"Y``!@N0``L+D``+"Y``#0N0``T+D``-"Y``#0N0``T+D` -M`-"Y````N@```+H``$"Z``!`N@``>+H``'BZ``"#H``"@Z```H.@``D#D``)`Y```` -MN0```+D``("Y``"`N0``@+D``("Y`````````````)`Y``"0.0```#H````Z -M```8.@``&#H````Z````.@``D#D``)`Y``!@.0``8#D``+`Y``"P.0``*#H` -M`"@Z``!P.@``<#H``(@Z``"(.@``<#H``'`Z```8.@``&#H``&`Y``!@.0`` -M`+@```"X`````````````+`Y``"P.0``:#H``&@Z``"X.@``N#H``.0Z``#D -M.@``\#H``/`Z``#<.@``W#H``+PZ``"\.@``D#H``)`Z``!`.@``0#H``)`Y -M``"0.0``(+D``""Y``#PN0``\+D```BZ```(N@``L+D``+"Y``!`.0``0#D` -M`'`Z``!P.@``U#H``-0Z```,.P``##L``!@[```8.P``$CL``!([````.P`` -M`#L``.0Z``#D.@``S#H``,PZ``#`.@``P#H``*@Z``"H.@``@#H``(`Z```8 -M.@``&#H``&`Y``!@.0```+@```"X````.````#@``+`Y``"P.0``4#H``%`Z -M``"D.@``I#H``-`Z``#0.@``X#H``.`Z``#8.@``V#H``,PZ``#,.@``U#H` -M`-0Z``#H.@``Z#H```([```".P``!CL```8[````.P```#L``.`Z``#@.@`` -MM#H``+0Z``",.@``C#H``&`Z``!@.@``0#H``$`Z```P.@``,#H``#@Z```X -M.@``4#H``%`Z``"`.@``@#H``*PZ``"L.@``[#H``.PZ```4.P``%#L``"([ -M```B.P``&CL``!H[``#\.@``_#H``*PZ``"L.@``.#H``#@Z``#`.0``P#D` -M``@Z```(.@``>#H``'@Z``#(.@``R#H```0[```$.P``%CL``!8[```2.P`` -M$CL```([```".P``W#H``-PZ``"\.@``O#H``*@Z``"H.@``F#H``)@Z``", -M.@``C#H``(0Z``"$.@``<#H``'`Z``!X.@``>#H``)@Z``"8.@``O#H``+PZ -M``#<.@``W#H``.PZ``#L.@``Y#H``.0Z``#0.@``T#H``+@Z``"X.@``N#H` -M`+@Z``"\.@``O#H``-`Z``#0.@``V#H``-@Z``#0.@``T#H``+`Z``"P.@`` -MD#H``)`Z``!X.@``>#H``(@Z``"(.@``J#H``*@Z``#4.@``U#H``.@Z``#H -M.@``Y#H``.0Z``"\.@``O#H``)`Z``"0.@``4#H``%`Z``!(.@``2#H``'`Z -M``!P.@``G#H``)PZ``#`.@``P#H``-`Z``#0.@``P#H``,`Z``"X.@``N#H` -M`*PZ``"L.@``L#H``+`Z``"\.@``O#H``,@Z``#(.@``Q#H``,0Z``"T.@`` -MM#H``*0Z``"D.@``D#H``)`Z``"$.@``A#H``(0Z``"$.@``@#H``(`Z``!H -M.@``:#H``$`Z``!`.@``$#H``!`Z````.@```#H``!`Z```0.@``6#H``%@Z -M``"@.@``H#H``,`Z``#`.@``O#H``+PZ``"@.@``H#H``'`Z``!P.@``,#H` -M`#`Z```H.@``*#H``&@Z``!H.@``I#H``*0Z``#(.@``R#H``-0Z``#4.@`` -MM#H``+0Z``!H.@``:#H``-`Y``#0.0```#@````X``!`N0``0+D``$"Y``!` -MN0``@+@``("X``!`.0``0#D``-`Y``#0.0``*#H``"@Z``!H.@``:#H``)0Z -M``"4.@``K#H``*PZ``"T.@``M#H``*0Z``"D.@``A#H``(0Z``!`.@``0#H` -M`!@Z```8.@``*#H``"@Z``!@.@``8#H``(0Z``"$.@``@#H``(`Z```P.@`` -M,#H``$`Y``!`.0``L+D``+"Y```XN@``.+H``$BZ``!(N@``"+H```BZ``"` -MN```@+@``.`Y``#@.0``4#H``%`Z``"$.@``A#H``)@Z``"8.@``K#H``*PZ -M``#`.@``P#H``,@Z``#(.@``N#H``+@Z``",.@``C#H````Z````.@``(+D` -M`""Y``!`N@``0+H``("Z``"`N@``>+H``'BZ```HN@``*+H``&"Y``!@N0`` -M@#D``(`Y```8.@``&#H``%@Z``!8.@``C#H``(PZ``"8.@``F#H``)0Z``"4 -M.@``>#H``'@Z```0.@``$#H``(`X``"`.```L+D``+"Y````N@```+H``+"Y -M``"PN0```#D````Y```@.@``(#H``(`Z``"`.@``A#H``(0Z``!(.@``2#H` -M`,`Y``#`.0```#@````X``!`N0``0+D```"Y````N0``@#@``(`X``"@.0`` -MH#D``,`Y``#`.0``H#D``*`Y``"`.```@#@``,"X``#`N```P+@``,"X```` -M`````````"`Y```@.0``L#D``+`Y``#@.0``X#D``"`Z```@.@``0#H``$`Z -M``!H.@``:#H``'@Z``!X.@``<#H``'`Z```P.@``,#H``*`Y``"@.0``8+D` -M`&"Y```@N@``(+H``$BZ``!(N@``(+H``""Z``"0N0``D+D``$`Y``!`.0`` -M"#H```@Z```8.@``&#H```@Z```(.@``\#D``/`Y``#P.0``\#D``!`Z```0 -M.@``.#H``#@Z``!8.@``6#H``$`Z``!`.@``T#D``-`Y`````````````-"Y -M``#0N0``$+H``!"Z``#PN0``\+D``""Y```@N0``(#D``"`Y``#@.0``X#D` -M`!@Z```8.@``*#H``"@Z```H.@``*#H``"@Z```H.@``&#H``!@Z``#`.0`` -MP#D``(`X``"`.```L+D``+"Y```XN@``.+H``'BZ``!XN@``:+H``&BZ```0 -MN@``$+H``""Y```@N0``8#D``&`Y````.@```#H``#`Z```P.@``0#H``$`Z -M``!0.@``4#H``&@Z``!H.@``>#H``'@Z``!0.@``4#H``.`Y``#@.0``8+D` -M`&"Y``!XN@``>+H``-BZ``#8N@``_+H``/RZ``#DN@``Y+H``*"Z``"@N@`` -M`+H```"Z``"`.0``@#D``$`Z``!`.@``>#H``'@Z``!H.@``:#H``$`Z``!` -M.@```#H````Z``"0.0``D#D`````````````@+D``("Y````N@```+H``#"Z -M```PN@``,+H``#"Z```8N@``&+H```"Z````N@``\+D``/"Y```0N@``$+H` -M`%"Z``!0N@``C+H``(RZ``"+H``'BZ``"`N@``@+H` -M`(RZ``",N@``F+H``)BZ``"LN@``K+H``+BZ``"XN@``K+H``*RZ``"4N@`` -ME+H``%"Z``!0N@``"+H```BZ``#`N0``P+D``,"Y``#`N0``$+H``!"Z``!` -MN@``0+H``&BZ``!HN@``>+H``'BZ``!XN@``>+H``(2Z``"$N@``H+H``*"Z -M``#(N@``R+H``/"Z``#PN@```KL```*[``#PN@``\+H``,"Z``#`N@``B+H` -M`(BZ```XN@``.+H``!BZ```8N@``,+H``#"Z``!8N@``6+H``(2Z``"$N@`` -MA+H``(2Z``!HN@``:+H``#"Z```PN@``$+H``!"Z```HN@``*+H``'"Z``!P -MN@``J+H``*BZ``#NP``'KL``!R[ -M```NP`` -M7KL``':[``!VNP``>+L``'B[``!JNP``:KL``%*[``!2NP``/KL``#Z[```R -MNP``,KL``#*[```RNP``,+L``#"[```LNP``++L``":[```FNP``(+L``""[ -M```8NP``&+L``!Z[```>NP``++L``"R[``!$NP``1+L``%2[``!4NP``6+L` -M`%B[``!0NP``4+L``#Z[```^NP``++L``"R[```HNP``*+L``#*[```RNP`` -M1KL``$:[``!:NP``6KL``&J[``!JNP``;KL``&Z[``!FNP``9KL``%J[``!: -MNP``3KL``$Z[``!&NP``1KL``$*[``!"NP``0+L``$"[```\NP``/+L``#B[ -M```XNP``/+L``#R[``!(NP``2+L``%B[``!8NP``7+L``%R[``!8NP``6+L` -M`$J[``!*NP``-+L``#2[```DNP``)+L``":[```FNP``/+L``#R[``!DNP`` -M9+L``(B[``"(NP``F+L``)B[``" -MNP``7KL``$"[``!`NP``++L``"R[```DNP``)+L``"2[```DNP``)KL``":[ -M```JNP``*KL``#:[```VNP``2KL``$J[``!BNP``8KL``'R[``!\NP``B;L` -M`(F[``",NP``C+L``(J[``"*NP``A;L``(6[``"#NP``@[L``(&[``"!NP`` -M@;L``(&[``!\NP``?+L``'*[``!RNP``8+L``&"[``!,NP``3+L``#Z[```^ -MNP``0+L``$"[``!,NP``3+L``&*[``!BNP``=KL``':[``!\NP``?+L``'2[ -M``!TNP``:+L``&B[``!BNP``8KL``&:[``!FNP``=KL``':[``"'NP``A[L` -M`)*[``"2NP``E;L``)6[``"0NP``D+L``(:[``"&NP``>KL``'J[``!NNP`` -M;KL``&B[``!HNP``:KL``&J[``!FNP``9KL``&:[``!FNP``9+L``&2[``!B -MNP``8KL``&*[``!BNP``:KL``&J[``!\NP``?+L``(.[``"#NP``A;L``(6[ -M``"%NP``A;L``(6[``"%NP``A;L``(6[``"'NP``A[L``(N[``"+NP``B[L` -M`(N[``"&NP``AKL``':[``!VNP``7KL``%Z[``!0NP``4+L``%"[``!0NP`` -M9+L``&2[``"#NP``@[L``)*[``"2NP``FKL``)J[``"8NP``F+L``(R[``", -MNP``>KL``'J[``!@NP``8+L``%:[``!6NP``5KL``%:[``!BNP``8KL``&Z[ -M``!NNP``?KL``'Z[``""NP``@KL``(6[``"%NP``B+L``(B[``"*NP``BKL` -M`(N[``"+NP``B+L``(B[``"#NP``@[L``'J[``!ZNP``=+L``'2[``!ZNP`` -M>KL``(2[``"$NP``B[L``(N[``".NP``CKL``(B[``"(NP``?+L``'R[``!@ -MNP``8+L``$J[``!*NP``1KL``$:[``!4NP``5+L``&R[``!LNP``@;L``(&[ -M``")NP``B;L``(N[``"+NP``BKL``(J[``"*NP``BKL``(N[``"+NP``C+L` -M`(R[``",NP``C+L``(F[``")NP``@+L``("[``!HNP``:+L``%"[``!0NP`` -M2+L``$B[``!.NP``3KL``&*[``!BNP``>KL``'J[``"&NP``AKL``(>[``"' -MNP``AKL``(:[``"!NP``@;L``'2[``!TNP``9+L``&2[``!8NP``6+L``$J[ -M``!*NP``0+L``$"[```ZNP``.KL``$:[``!&NP``8+L``&"[``"#NP``@[L` -M`)6[``"5NP``HKL``**[``"@NP``H+L``)*[``"2NP``=KL``':[``!,NP`` -M3+L``"R[```LNP``'+L``!R[```@NP``(+L``"R[```LNP``-+L``#2[```X -MNP``.+L``#B[```XNP``/+L``#R[``!(NP``2+L``%Z[``!>NP``=KL``':[ -M``"%NP``A;L``(J[``"*NP``BKL``(J[``"&NP``AKL``'R[``!\NP``9+L` -M`&2[``!.NP``3KL``#2[```TNP``&+L``!B[``#XN@``^+H``-2Z``#4N@`` -MU+H``-2Z``#\N@``_+H``""[```@NP``1+L``$2[``!>NP``7KL``&:[``!F -MNP``8KL``&*[``!8NP``6+L``%"[``!0NP``2KL``$J[``!.NP``3KL``$R[ -M``!,NP``0KL``$*[```PNP``,+L``!2[```4NP``^+H``/BZ``#@N@``X+H` -M`.2Z``#DN@``!+L```2[```6NP``%KL``":[```FNP``++L``"R[```LNP`` -M++L``"*[```BNP``'+L``!R[```:NP``&KL``!:[```6NP``#KL```Z[```$ -MNP``!+L``/"Z``#PN@``W+H``-RZ``#NP``'KL``"*[```BNP``'KL``!Z[```2NP``$KL```J[```*NP```+L` -M``"[``#\N@``_+H``/2Z``#TN@``W+H``-RZ``"PN@``L+H``'BZ``!XN@`` -M,+H``#"Z```HN@``*+H``'BZ``!XN@``S+H``,RZ```0NP``$+L``#*[```R -MNP``0+L``$"[```RNP``,KL``!:[```6NP``X+H``."Z``"@N@``H+H``&"Z -M``!@N@``*+H``"BZ```@N@``(+H``#BZ```XN@``:+H``&BZ``"4N@``E+H` -M`,"Z``#`N@``X+H``."Z``#PN@``\+H``.BZ``#HN@``R+H``,BZ``"0N@`` -MD+H``$BZ``!(N@``*+H``"BZ``!(N@``2+H``(2Z``"$N@``I+H``*2Z``"P -MN@``L+H``)RZ``"+H``*"Z``"@N@``M+H``+2Z``"PN@``L+H``)BZ``"8N@``@+H` -M`("Z``!@N@``8+H``%"Z``!0N@``0+H``$"Z```PN@``,+H``."Y``#@N0`` -M0+D``$"Y````N````+@`````````````P+@``,"X``#0N0``T+D``%"Z``!0 -MN@``E+H``)2Z``"LN@``K+H``+2Z``"TN@``L+H``+"Z``"LN@``K+H``*"Z -M``"@N@``A+H``(2Z```PN@``,+H``""Y```@N0``L#D``+`Y```H.@``*#H` -M`"@Z```H.@``T#D``-`Y````N````+@```BZ```(N@``6+H``%BZ``!8N@`` -M6+H``#"Z```PN@``X+D``."Y``"0N0``D+D``*"Y``"@N0``$+H``!"Z``!( -MN@``2+H``'BZ``!XN@``@+H``("Z``!8N@``6+H```BZ```(N@``8+D``&"Y -M````N````+@``,`X``#`.```8#D``&`Y``"@.0``H#D``.`Y``#@.0``\#D` -M`/`Y``#0.0``T#D``$`Y``!`.0``@+@``("X``"`N0``@+D``)"Y``"0N0`` -M@+D``("Y```@N0``(+D```"Y````N0``8+D``&"Y``#PN0``\+D``#"Z```P -MN@``0+H``$"Z````N@```+H```"X````N```&#H``!@Z``"`.@``@#H``)PZ -M``"<.@``B#H``(@Z``!(.@``2#H``-`Y``#0.0```#D````Y````N````+@` -M```X````.```P#@``,`X``#`.```P#@``(`X``"`.```@#@``(`X````.0`` -M`#D``(`Y``"`.0``P#D``,`Y``#@.0``X#D``-`Y``#0.0``@#D``(`Y```` -M.0```#D``(`X``"`.```(#D``"`Y``"P.0``L#D``"`Z```@.@``4#H``%`Z -M``!H.@``:#H``%`Z``!0.@``0#H``$`Z```P.@``,#H``$@Z``!(.@``6#H` -M`%@Z``!8.@``6#H``!@Z```8.@``D#D``)`Y``#`N```P+@``*"Y``"@N0`` -MH+D``*"Y````.````#@``/`Y``#P.0``8#H``&`Z``",.@``C#H``(PZ``", -M.@``8#H``&`Z```H.@``*#H``/`Y``#P.0``"#H```@Z```H.@``*#H``%@Z -M``!8.@``<#H``'`Z``"`.@``@#H``&`Z``!@.@``2#H``$@Z```@.@``(#H` -M```Z````.@``D#D``)`Y``#`.```P#@```"Y````N0```+D```"Y````.``` -M`#@``!@Z```8.@``D#H``)`Z``#,.@``S#H``-PZ``#<.@``Q#H``,0Z``!X -M.@``>#H``+`Y``"P.0``0+D``$"Y``#`N0``P+D``+"Y``"PN0```+@```"X -M``"@.0``H#D``"@Z```H.@``8#H``&`Z``"(.@``B#H``)0Z``"4.@``I#H` -M`*0Z``"H.@``J#H``*0Z``"D.@``B#H``(@Z```P.@``,#H``"`Y```@.0`` -MD+D``)"Y````N@```+H```"Z````N@``P+D``,"Y``#`N```P+@``"`Y```@ -M.0``T#D``-`Y```P.@``,#H``(`Z``"`.@``J#H``*@Z``#(.@``R#H``-`Z -M``#0.@``N#H``+@Z``"$.@``A#H``/`Y``#P.0``@#@``(`X```@N0``(+D` -M`$"Y``!`N0``P+@``,"X````.````#@````X````.```@+@``("X````N0`` -M`+D```"X````N```0#D``$`Y```8.@``&#H``(0Z``"$.@``M#H``+0Z``#` -M.@``P#H``*PZ``"L.@``@#H``(`Z```H.@``*#H``,`Y``#`.0``@#D``(`Y -M``"`.0``@#D``(`Y``"`.0``@#D``(`Y``"P.0``L#D``.`Y``#@.0``$#H` -M`!`Z```P.@``,#H``$`Z``!`.@``.#H``#@Z```(.@``"#H``(`Y``"`.0`` -MP#@``,`X````.0```#D``,`Y``#`.0``.#H``#@Z``"$.@``A#H``)`Z``"0 -M.@``A#H``(0Z``!0.@``4#H``!@Z```8.@``$#H``!`Z```X.@``.#H``(`Z -M``"`.@``G#H``)PZ``"@.@``H#H``(`Z``"`.@``$#H``!`Z````.0```#D` -M`)"Y``"0N0```+H```"Z``#@N0``X+D``("Y``"`N0``@#@``(`X``#P.0`` -M\#D``&`Z``!@.@``I#H``*0Z``#<.@``W#H```([```".P``"CL```H[``#L -M.@``[#H``*@Z``"H.@``*#H``"@Z````.0```#D``&"Y``!@N0``D+D``)"Y -M``!`N0``0+D`````````````(#D``"`Y``#`.0``P#D````Z````.@``.#H` -M`#@Z``"`.@``@#H``+`Z``"P.@``S#H``,PZ``#0.@``T#H``*@Z``"H.@`` -M8#H``&`Z``#@.0``X#D``$`Y``!`.0``(#D``"`Y``#`.0``P#D``"@Z```H -M.@``<#H``'`Z``"`.@``@#H``'`Z``!P.@``0#H``$`Z```@.@``(#H``!`Z -M```0.@``$#H``!`Z```H.@``*#H``#@Z```X.@``2#H``$@Z``!0.@``4#H` -M`&@Z``!H.@``A#H``(0Z``"$.@``A#H``'`Z``!P.@``.#H``#@Z``#0.0`` -MT#D``"`Y```@.0``@#@``(`X``"@.0``H#D``$`Z``!`.@``I#H``*0Z``#8 -M.@``V#H``.@Z``#H.@``R#H``,@Z``"0.@``D#H``"@Z```H.@``L#D``+`Y -M``!@.0``8#D``)`Y``"0.0``\#D``/`Y```8.@``&#H``"`Z```@.@``(#H` -M`"`Z```P.@``,#H``%`Z``!0.@``:#H``&@Z``"(.@``B#H``(PZ``",.@`` -MB#H``(@Z``!X.@``>#H``'`Z``!P.@``>#H``'@Z``"(.@``B#H``)@Z``"8 -M.@``G#H``)PZ``",.@``C#H``'`Z``!P.@``.#H``#@Z```H.@``*#H``#`Z -M```P.@``6#H``%@Z``!P.@``<#H``'`Z``!P.@``0#H``$`Z````.@```#H` -M`+`Y``"P.0``P#D``,`Y```8.@``&#H``(0Z``"$.@``O#H``+PZ``#<.@`` -MW#H``-PZ``#<.@``Q#H``,0Z``"H.@``J#H``(PZ``",.@``A#H``(0Z``"$ -M.@``A#H``(`Z``"`.@``:#H``&@Z``!`.@``0#H```@Z```(.@``P#D``,`Y -M``#`.0``P#D````Z````.@``,#H``#`Z``!@.@``8#H``'@Z``!X.@``B#H` -M`(@Z``"4.@``E#H``*0Z``"D.@``M#H``+0Z``#$.@``Q#H``+@Z``"X.@`` -MI#H``*0Z``"$.@``A#H``%@Z``!8.@``0#H``$`Z``!@.@``8#H``)0Z``"4 -M.@``N#H``+@Z``#`.@``P#H``*`Z``"@.@``0#H``$`Z``"`.0``@#D```"Y -M````N0```+D```"Y```@.0``(#D``"`Z```@.@``D#H``)`Z``#,.@``S#H` -M`/0Z``#T.@```CL```([````.P```#L``/@Z``#X.@``X#H``.`Z``"\.@`` -MO#H``(PZ``",.@``.#H``#@Z``#0.0``T#D``)`Y``"0.0``P#D``,`Y```8 -M.@``&#H``$`Z``!`.@``6#H``%@Z``!8.@``6#H``#@Z```X.@``*#H``"@Z -M``!0.@``4#H``)0Z``"4.@``Q#H``,0Z``#H.@``Z#H``/@Z``#X.@``[#H` -M`.PZ``#`.@``P#H``)@Z``"8.@``>#H``'@Z``!8.@``6#H``$@Z``!(.@`` -M.#H``#@Z```8.@``&#H``-`Y``#0.0``D#D``)`Y``"P.0``L#D``"`Z```@ -M.@``@#H``(`Z``"\.@``O#H``.0Z``#D.@``\#H``/`Z``#<.@``W#H``+@Z -M``"X.@``B#H``(@Z```X.@``.#H``.`Y``#@.0``H#D``*`Y``"0.0``D#D` -M`*`Y``"@.0``"#H```@Z``!H.@``:#H``+`Z``"P.@``[#H``.PZ```&.P`` -M!CL```0[```$.P``T#H``-`Z``"(.@``B#H````Z````.@``(#D``"`Y```` -M.````#@``&`Y``!@.0``$#H``!`Z``!@.@``8#H``(`Z``"`.@``@#H``(`Z -M``"`.@``@#H``(0Z``"$.@``E#H``)0Z``"P.@``L#H``,@Z``#(.@``T#H` -M`-`Z``#$.@``Q#H``*@Z``"H.@``B#H``(@Z``!0.@``4#H``#`Z```P.@`` -M$#H``!`Z```(.@``"#H``/`Y``#P.0``\#D``/`Y```8.@``&#H``%@Z``!8 -M.@``F#H``)@Z``"X.@``N#H``,@Z``#(.@``N#H``+@Z``"0.@``D#H``$@Z -M``!(.@```#H````Z``#@.0``X#D``!@Z```8.@``8#H``&`Z``"(.@``B#H` -M`)0Z``"4.@``C#H``(PZ``!X.@``>#H``&@Z``!H.@``@#H``(`Z``"0.@`` -MD#H``*`Z``"@.@``H#H``*`Z``"(.@``B#H``#@Z```X.@``T#D``-`Y``#` -M.```P#@``````````````#@````X``!`.0``0#D``,`Y``#`.0``"#H```@Z -M```P.@``,#H``&@Z``!H.@``F#H``)@Z``#`.@``P#H``-`Z``#0.@``S#H` -M`,PZ``"T.@``M#H``(0Z``"$.@``&#H``!@Z``"`.0``@#D````````````` -M(+D``""Y``"@N0``H+D```"Z````N@``*+H``"BZ```PN@``,+H```"Z```` -MN@``@+@``("X```@.@``(#H``*@Z``"H.@``\#H``/`Z```".P```CL``.`Z -M``#@.@``D#H``)`Z``#@.0``X#D```"Y````N0```+H```"Z```@N@``(+H` -M`!"Z```0N@``X+D``."Y``!@N0``8+D``("X``"`N```(#D``"`Y``"P.0`` -ML#D``,`Y``#`.0``H#D``*`Y``!@.0``8#D``("X``"`N```@+D``("Y``"@ -MN0``H+D``("X``"`N```8#D``&`Y```(.@``"#H``"`Z```@.@``$#H``!`Z -M``"`.0``@#D```"X````N```H+D``*"Y``"PN0``L+D``*"Y``"@N0``(+D` -M`""Y``#`N```P+@```"Y````N0``D+D``)"Y``#`N0``P+D``/"Y``#PN0`` -ML+D``+"Y``!`N0``0+D``````````````#@````X````N````+@``""Y```@ -MN0``0+D``$"Y``"0N0``D+D``""Y```@N0`````````````@.0``(#D``,`X -M``#`.``````````````@N0``(+D``)"Y``"0N0``X+D``."Y````N@```+H` -M`""Z```@N@``0+H``$"Z``!PN@``<+H``'"Z``!PN@``8+H``&"Z```0N@`` -M$+H``$"Y``!`N0``8#D``&`Y``"P.0``L#D``&`Y``!@.0``(+D``""Y```0 -MN@``$+H``'"Z``!PN@``A+H``(2Z``!8N@``6+H```BZ```(N@``L+D``+"Y -M``"0N0``D+D``)"Y``"0N0``X+D``."Y```HN@``*+H``$"Z``!`N@``2+H` -M`$BZ```XN@``.+H``#"Z```PN@``0+H``$"Z``!0N@``4+H``%BZ``!8N@`` -M6+H``%BZ``!(N@``2+H``$BZ``!(N@``4+H``%"Z``!@N@``8+H``'BZ``!X -MN@``>+H``'BZ``!@N@``8+H``""Z```@N@``P+D``,"Y``!@N0``8+D``("Y -M``"`N0``"+H```BZ``!PN@``<+H``*BZ``"HN@``P+H``,"Z``"PN@``L+H` -M`)"Z``"0N@``8+H``&"Z```XN@``.+H``%"Z``!0N@``@+H``("Z``"@N@`` -MH+H``+2Z``"TN@``N+H``+BZ``"LN@``K+H``)BZ``"8N@``B+H``(BZ``!X -MN@``>+H``%BZ``!8N@``,+H``#"Z``#PN0``\+D``*"Y``"@N0``D+D``)"Y -M``#@N0``X+D``$BZ``!(N@``H+H``*"Z``#4N@``U+H``/BZ``#XN@``!+L` -M``2[```$NP``!+L``/RZ``#\N@``[+H``.RZ``#+H``'BZ``!(N@`` -M2+H``""Z```@N@``$+H``!"Z```PN@``,+H``&BZ``!HN@``I+H``*2Z``#D -MN@``Y+H```J[```*NP``%+L``!2[```,NP``#+L``.BZ``#HN@``I+H``*2Z -M``!@N@``8+H``#BZ```XN@``8+H``&"Z``"@N@``H+H``,RZ``#,N@``Z+H` -M`.BZ``#LN@``[+H``."Z``#@N@``U+H``-2Z``#$N@``Q+H``+RZ``"\N@`` -MM+H``+2Z``"@N@``H+H``(BZ``"(N@``8+H``&"Z```XN@``.+H``$"Z``!` -MN@``>+H``'BZ``"PN@``L+H``."Z``#@N@``!KL```:[```0NP``$+L``!2[ -M```4NP``$KL``!*[```,NP``#+L```"[````NP``Z+H``.BZ``#`N@``P+H` -M`)"Z``"0N@``0+H``$"Z```(N@``"+H``!"Z```0N@``6+H``%BZ``"DN@`` -MI+H``-RZ``#+H``(RZ``",N@``B+H``(BZ``"(N@``B+H``(BZ``"(N@`` -MF+H``)BZ``"@N@``H+H``*"Z``"@N@``C+H``(RZ``!@N@``8+H``""Z```@ -MN@``&+H``!BZ``!(N@``2+H``)2Z``"4N@``Q+H``,2Z``#HN@``Z+H``.2Z -M``#DN@``P+H``,"Z``"(N@``B+H``"BZ```HN@``D+D``)"Y````N0```+D` -M`,"X``#`N```(+D``""Y``"@N0``H+D``."Y``#@N0``(+H``""Z``!8N@`` -M6+H``(2Z``"$N@``D+H``)"Z``"0N@``D+H``'BZ``!XN@``2+H``$BZ```P -MN@``,+H``#BZ```XN@``:+H``&BZ``"0N@``D+H``*"Z``"@N@``C+H``(RZ -M```XN@``.+H``$"Y``!`N0``@#D``(`Y```(.@``"#H```@Z```(.@``D#D` -M`)`Y````N0```+D``."Y``#@N0``(+H``""Z```PN@``,+H``$"Z``!`N@`` -M4+H``%"Z``!8N@``6+H``%BZ``!8N@``4+H``%"Z```HN@``*+H``."Y``#@ -MN0``0+D``$"Y````N````+@``,`X``#`.```P#@``,`X````.0```#D``,`X -M``#`.```(#D``"`Y``"`.0``@#D``)`Y``"0.0```#D````Y````N0```+D` -M`+"Y``"PN0``X+D``."Y``#0N0``T+D``$"Y``!`N0`````````````@.0`` -M(#D````Y````.0```+@```"X``!@N0``8+D``*"Y``"@N0``8+D``&"Y```` -M.````#@``+`Y``"P.0```#H````Z````.@```#H``+`Y``"P.0```#D````Y -M````N````+@`````````````@#@``(`X``!@.0``8#D``(`Y``"`.0``8#D` -M`&`Y````.0```#D``,`X``#`.```P#@``,`X``!`.0``0#D``)`Y``"0.0`` -ML#D``+`Y``"P.0``L#D``*`Y``"@.0``D#D``)`Y``"P.0``L#D``+`Y``"P -M.0``L#D``+`Y``"0.0``D#D``,`X``#`.```8+D``&"Y``#0N0``T+D``-"Y -M``#0N0``P+@``,"X``#0.0``T#D``(`Z``"`.@``N#H``+@Z``#(.@``R#H` -M`*PZ``"L.@``8#H``&`Z``"@.0``H#D``&"Y``!@N0``\+D``/"Y``#PN0`` -M\+D``."Y``#@N0``P+D``,"Y``!@N0``8+D`````````````@#D``(`Y```( -M.@``"#H``$`Z``!`.@``4#H``%`Z``!`.@``0#H``!@Z```8.@``T#D``-`Y -M``"P.0``L#D``,`Y``#`.0```#H````Z```@.@``(#H``!@Z```8.@``X#D` -M`.`Y``!`.0``0#D``("X``"`N```8+D``&"Y``!`N0``0+D```"X````N``` -M(#D``"`Y``"`.0``@#D``(`Y``"`.0``(#D``"`Y``"`.```@#@``,`X``#` -M.```D#D``)`Y````.@```#H``#@Z```X.@``.#H``#@Z```0.@``$#H``)`Y -M``"0.0```#D````Y``"`.```@#@``"`Y```@.0``H#D``*`Y```(.@``"#H` -M`!@Z```8.@``$#H``!`Z``"@.0``H#D``,`X``#`.````+D```"Y``"0N0`` -MD+D``."Y``#@N0``$+H``!"Z```@N@``(+H``/"Y``#PN0```+D```"Y```` -M.@```#H``)`Z``"0.@``T#H``-`Z``#D.@``Y#H``-@Z``#8.@``H#H``*`Z -M```8.@``&#H`````````````P+D``,"Y```(N@``"+H```"Z````N@``X+D` -M`."Y``#0N0``T+D``,"Y``#`N0``H+D``*"Y```@N0``(+D``"`Y```@.0`` -M&#H``!@Z``!P.@``<#H``)PZ``"<.@``J#H``*@Z``"8.@``F#H``'@Z``!X -M.@``0#H``$`Z``#P.0``\#D``$`Y``!`.0```+D```"Y``"PN0``L+D``!"Z -M```0N@``"+H```BZ``"@N0``H+D``,`X``#`.````#H````Z``!8.@``6#H` -M`'@Z``!X.@``8#H``&`Z```8.@``&#H``*`Y``"@.0```#D````Y````.0`` -M`#D``(`Y``"`.0``L#D``+`Y``"@.0``H#D``$`Y``!`.0``@#@``(`X```` -M.````#@````Y````.0``H#D``*`Y```8.@``&#H``$@Z``!(.@``2#H``$@Z -M```H.@``*#H``/`Y``#P.0``D#D``)`Y```@.0``(#D``"`Y```@.0``0#D` -M`$`Y````.0```#D``,`X``#`.```@#@``(`X``"`.```@#@``"`Y```@.0`` -ML#D``+`Y```0.@``$#H``$`Z``!`.@``2#H``$@Z```X.@``.#H``#@Z```X -M.@``*#H``"@Z```X.@``.#H``%`Z``!0.@``0#H``$`Z````.@```#H````Y -M````.0``@+D``("Y```(N@``"+H```BZ```(N@``0+D``$"Y``"P.0``L#D` -M`&`Z``!@.@``F#H``)@Z``"<.@``G#H``(`Z``"`.@``*#H``"@Z``"0.0`` -MD#D`````````````(+D``""Y``!`N0``0+D``("X``"`N```(#D``"`Y``#0 -M.0``T#D``#@Z```X.@``A#H``(0Z``"@.@``H#H``*`Z``"@.@``@#H``(`Z -M```@.@``(#H``"`Y```@.0``0+D``$"Y``"PN0``L+D``)"Y``"0N0``P+@` -M`,"X````.0```#D``(`Y``"`.0``D#D``)`Y``!@.0``8#D``)`Y``"0.0`` -M\#D``/`Y``!`.@``0#H``(`Z``"`.@``E#H``)0Z``",.@``C#H``&`Z``!@ -M.@``&#H``!@Z``#`.0``P#D``)`Y``"0.0``@#D``(`Y``!`.0``0#D``"`Y -M```@.0``@#@``(`X````N0```+D``*"Y``"@N0``H+D``*"Y````N0```+D` -M`"`Y```@.0``X#D``.`Y```H.@``*#H``%@Z``!8.@``@#H``(`Z``"0.@`` -MD#H``*`Z``"@.@``G#H``)PZ``",.@``C#H``%`Z``!0.@``T#D``-`Y```` -M`````````)"Y``"0N0``D+D``)"Y````N0```+D``"`Y```@.0``T#D``-`Y -M```(.@``"#H``.`Y``#@.0``@#D``(`Y```@.0``(#D``(`Y``"`.0``X#D` -M`.`Y``!(.@``2#H``(PZ``",.@``I#H``*0Z``"<.@``G#H``(0Z``"$.@`` -M2#H``$@Z```8.@``&#H``.`Y``#@.0``P#D``,`Y``"@.0``H#D``&`Y``!@ -M.0``(#D``"`Y``!`.0``0#D``)`Y``"0.0``\#D``/`Y```@.@``(#H``#@Z -M```X.@``*#H``"@Z```8.@``&#H```@Z```(.@``(#H``"`Z``!8.@``6#H` -M`)0Z``"4.@``L#H``+`Z``"T.@``M#H``)0Z``"4.@``2#H``$@Z``#@.0`` -MX#D``&`Y``!@.0``8#D``&`Y``#0.0``T#D``#`Z```P.@``<#H``'`Z``", -M.@``C#H``(@Z``"(.@``@#H``(`Z``!H.@``:#H``&`Z``!@.@``6#H``%@Z -M``!0.@``4#H``#`Z```P.@``&#H``!@Z```(.@``"#H``!`Z```0.@``0#H` -M`$`Z``"(.@``B#H``*@Z``"H.@``O#H``+PZ``"\.@``O#H``*PZ``"L.@`` -MD#H``)`Z``!P.@``<#H``%`Z``!0.@``.#H``#@Z```0.@``$#H``,`Y``#` -M.0``8#D``&`Y``!`.0``0#D``*`Y``"@.0``*#H``"@Z``"0.@``D#H``,`Z -M``#`.@``U#H``-0Z``#(.@``R#H``)@Z``"8.@``2#H``$@Z``#0.0``T#D` -M`)`Y``"0.0``P#D``,`Y```@.@``(#H``&`Z``!@.@``C#H``(PZ``"0.@`` -MD#H``(@Z``"(.@``<#H``'`Z``!H.@``:#H``&`Z``!@.@``6#H``%@Z```X -M.@``.#H``"@Z```H.@``*#H``"@Z```P.@``,#H``%@Z``!8.@``>#H``'@Z -M``!X.@``>#H``&`Z``!@.@``&#H``!@Z``"P.0``L#D``&`Y``!@.0``P#D` -M`,`Y``!`.@``0#H``*`Z``"@.@``U#H``-0Z``#T.@``]#H``/@Z``#X.@`` -MT#H``-`Z``"8.@``F#H``%@Z``!8.@``"#H```@Z``"@.0``H#D``$`Y``!` -M.0```#@````X``#`N```P+@```"Y````N0```#@````X``#0.0``T#D``&@Z -M``!H.@``M#H``+0Z``#D.@``Y#H````[````.P``_#H``/PZ``#H.@``Z#H` -M`,@Z``#(.@``H#H``*`Z``!H.@``:#H``!@Z```8.@``D#D``)`Y``"`N``` -M@+@``&"Y``!@N0``0+D``$"Y````.0```#D```@Z```(.@``<#H``'`Z``"< -M.@``G#H``*@Z``"H.@``I#H``*0Z``"4.@``E#H``(PZ``",.@``D#H``)`Z -M``"8.@``F#H``*0Z``"D.@``G#H``)PZ``"$.@``A#H``$`Z``!`.@```#H` -M```Z``#0.0``T#D```@Z```(.@``2#H``$@Z``"$.@``A#H``)@Z``"8.@`` -MF#H``)@Z``",.@``C#H``&@Z``!H.@``,#H``#`Z```8.@``&#H```@Z```( -M.@```#H````Z```(.@``"#H``"`Z```@.@``2#H``$@Z``"0.@``D#H``+PZ -M``"\.@``V#H``-@Z``#@.@``X#H``-`Z``#0.@``H#H``*`Z``!8.@``6#H` -M``@Z```(.@``\#D``/`Y```0.@``$#H``$`Z``!`.@``8#H``&`Z``!8.@`` -M6#H``#@Z```X.@``"#H```@Z``#0.0``T#D``-`Y``#0.0``*#H``"@Z``"$ -M.@``A#H``+`Z``"P.@``R#H``,@Z``#,.@``S#H``,PZ``#,.@``O#H``+PZ -M``"H.@``J#H``(PZ``",.@``6#H``%@Z```(.@``"#H``)`Y``"0.0``P#@` -M`,`X``"`.```@#@``(`Y``"`.0``&#H``!@Z``"$.@``A#H``*PZ``"L.@`` -MO#H``+PZ``"X.@``N#H``*@Z``"H.@``E#H``)0Z``"$.@``A#H``'`Z``!P -M.@``0#H``$`Z```(.@``"#H``+`Y``"P.0``(#D``"`Y``!`.0``0#D``-`Y -M``#0.0``2#H``$@Z``"@.@``H#H``-0Z``#4.@``X#H``.`Z``"\.@``O#H` -M`(@Z``"(.@``*#H``"@Z``#0.0``T#D``)`Y``"0.0``@#D``(`Y``"P.0`` -ML#D``/`Y``#P.0``&#H``!@Z```H.@``*#H``#@Z```X.@``2#H``$@Z``!8 -M.@``6#H``%`Z``!0.@``0#H``$`Z```H.@``*#H``"`Z```@.@``.#H``#@Z -M``!H.@``:#H``)0Z``"4.@``J#H``*@Z``"D.@``I#H``(`Z``"`.@``"#H` -M``@Z````.0```#D``""Y```@N0``@+D``("Y````N0```+D``&`Y``!@.0`` -M\#D``/`Y```0.@``$#H````Z````.@``X#D``.`Y``#P.0``\#D``!@Z```8 -M.@``0#H``$`Z``!@.@``8#H``'`Z``!P.@``8#H``&`Z``!`.@``0#H``"`Z -M```@.@``\#D``/`Y``"P.0``L#D``)`Y``"0.0``(#D``"`Y```````````` -M`$"Y``!`N0``H+D``*"Y``"@N0``H+D``,"X``#`N```(#D``"`Y``"P.0`` -ML#D``-`Y``#0.0``P#D``,`Y``#`.0``P#D``,`Y``#`.0``T#D``-`Y```8 -M.@``&#H``$@Z``!(.@``8#H``&`Z``!`.@``0#H``.`Y``#@.0```#@````X -M``"`N0``@+D``,"Y``#`N0``L+D``+"Y``!@N0``8+D``,"X``#`N```(+D` -M`""Y``!@N0``8+D``*"Y``"@N0``D+D``)"Y``!@N0``8+D````````````` -M(#D``"`Y``#`.0``P#D``,`Y``#`.0``P#D``,`Y``"`.0``@#D``"`Y```@ -M.0``@#@``(`X````.````#@```"Y````N0``D+D``)"Y``#PN0``\+D``/"Y -M``#PN0``\+D``/"Y``"0N0``D+D``""Y```@N0``P+@``,"X``"`N0``@+D` -M``"Z````N@``4+H``%"Z``!XN@``>+H``&BZ``!HN@```+H```"Z``"`.``` -M@#@``!@Z```8.@``6#H``%@Z``!@.@``8#H``"@Z```H.@``@#D``(`Y``!@ -MN0``8+D``!"Z```0N@``8+H``&"Z``"(N@``B+H``)2Z``"4N@``E+H``)2Z -M``"$N@``A+H``#BZ```XN@``P+D``,"Y````N````+@``&`Y``!@.0``H#D` -M`*`Y``!`.0``0#D``("X``"`N```H+D``*"Y``#0N0``T+D``-"Y``#0N0`` -MT+D``-"Y``#PN0``\+D``!BZ```8N@``0+H``$"Z``!HN@``:+H``'"Z``!P -MN@``2+H``$BZ```0N@``$+H``*"Y``"@N0``8+D``&"Y``"`N0``@+D``."Y -M``#@N0``&+H``!BZ```XN@``.+H``$"Z``!`N@``*+H``"BZ```8N@``&+H` -M`"BZ```HN@``4+H``%"Z``"$N@``A+H``*2Z``"DN@``K+H``*RZ``"@N@`` -MH+H``'BZ``!XN@``&+H``!BZ``"@N0``H+D``""Y```@N0``@+@``("X```` -MN````+@``,"X``#`N```D+D``)"Y```@N@``(+H``(BZ``"(N@``Q+H``,2Z -M``#PN@``\+H```"[````NP``[+H``.RZ``"TN@``M+H``&"Z``!@N@``X+D` -M`."Y```@N0``(+D``""Y```@N0``H+D``*"Y```(N@``"+H``"BZ```HN@`` -M,+H``#"Z```@N@``(+H``"BZ```HN@``2+H``$BZ``!XN@``>+H``)BZ``"8 -MN@``K+H``*RZ``"LN@``K+H``*"Z``"@N@``B+H``(BZ``!PN@``<+H``%"Z -M``!0N@``2+H``$BZ``!(N@``2+H``%"Z``!0N@``2+H``$BZ``!@N@``8+H` -M`'BZ``!XN@``F+H``)BZ``"HN@``J+H``+"Z``"PN@``G+H``)RZ``!PN@`` -M<+H```"Z````N@``8+D``&"Y``#`N```P+@``("Y``"`N0``$+H``!"Z``!X -MN@``>+H``*"Z``"@N@``O+H``+RZ``#$N@``Q+H``,BZ``#(N@``R+H``,BZ -M``#,N@``S+H``,RZ``#,N@``P+H``,"Z``"@N@``H+H``&BZ``!HN@``X+D` -M`."Y````N0```+D`````````````@+@``("X``"`N0``@+D``!"Z```0N@`` -M2+H``$BZ``!XN@``>+H``)2Z``"4N@``I+H``*2Z``"XN@``N+H``-"Z``#0 -MN@``W+H``-RZ``#4N@``U+H``+BZ``"XN@``F+H``)BZ``!XN@``>+H``%"Z -M``!0N@``0+H``$"Z```HN@``*+H``/"Y``#PN0``0+D``$"Y````.````#@` -M`,`X``#`.````+D```"Y````N@```+H``(RZ``",N@``S+H``,RZ``#TN@`` -M]+H``/BZ``#XN@``V+H``-BZ``"+H``'BZ -M``"0N@``D+H``)2Z``"4N@``E+H``)2Z``"`N@``@+H``$BZ``!(N@``\+D` -M`/"Y``"@N0``H+D``("Y``"`N0``T+D``-"Y```HN@``*+H``'"Z``!PN@`` -MC+H``(RZ``",N@``C+H``'"Z``!PN@``4+H``%"Z``!`N@``0+H``%BZ``!8 -MN@``:+H``&BZ``!PN@``<+H``%BZ``!8N@``.+H``#BZ````N@```+H``+"Y -M``"PN0``P+D``,"Y```0N@``$+H``$BZ``!(N@``:+H``&BZ``!8N@``6+H` -M`#"Z```PN@``"+H```BZ``#@N0``X+D``."Y``#@N0```+H```"Z```@N@`` -M(+H``#BZ```XN@``2+H``$BZ``!0N@``4+H``%BZ``!8N@``8+H``&"Z``!P -MN@``<+H``&BZ``!HN@``6+H``%BZ```HN@``*+H``."Y``#@N0``H+D``*"Y -M``"PN0``L+D``/"Y``#PN0``*+H``"BZ```XN@``.+H``#BZ```XN@``$+H` -M`!"Z``"@N0``H+D``,"X``#`N```@+@``("X``!`N0``0+D```"Z````N@`` -M4+H``%"Z``"$N@``A+H``("Z``"`N@``8+H``&"Z```HN@``*+H```"Z```` -MN@``\+D``/"Y```(N@``"+H``!"Z```0N@``&+H``!BZ````N@```+H``-"Y -M``#0N0``H+D``*"Y``"0N0``D+D``("Y``"`N0``0+D``$"Y````N0```+D` -M````````````P#@``,`X````N````+@``*"Y``"@N0``*+H``"BZ``"`N@`` -M@+H``)2Z``"4N@``C+H``(RZ``!8N@``6+H``!BZ```8N@``P+D``,"Y``"` -MN0``@+D``*"Y``"@N0``T+D``-"Y``#PN0``\+D``,"Y``#`N0``0+D``$"Y -M````.````#@``$`Y``!`.0``@#D``(`Y``!@.0``8#D````Y````.0``@+@` -M`("X``"`N0``@+D``,"Y``#`N0``"+H```BZ```XN@``.+H``("Z``"`N@`` -MG+H``)RZ``"+H``$BZ``!(N@`` -M.+H``#BZ``!@N@``8+H``)2Z``"4N@``P+H``,"Z``#@N@``X+H``.BZ``#H -MN@``V+H``-BZ``"\N@``O+H``*2Z``"DN@``G+H``)RZ``"DN@``I+H``+"Z -M``"PN@``M+H``+2Z``"PN@``L+H``*"Z``"@N@``E+H``)2Z``",N@``C+H` -M`(RZ``",N@``D+H``)"Z``"8N@``F+H``)RZ``"Z@+IGNK[*'[J^RA^Z_*W?N?RMW[F'IM^YAZ;? -MN3J1_[DZD?^YY;P/NN6\#[H:N`^Z&K@/NLJJ'[K*JA^Z=Z4?NG>E'[HLB$>Z -M+(A'NOVIA[K]J8>Z?XVKNG^-J[HS=,>Z,W3'NL%DT[K!9-.ZXV;'NN-FQ[HU -M<+.Z-7"SNC!^F[HP?INZPXV#NL.-@[KA/5>ZX3U7NH-4-[J#5#>Z1VT7ND=M -M%[HR>`>Z,G@'NFIK#[IJ:P^Z^2I'NODJ1[HX9HNZ.&:+NK8FO[JV)K^Z#^WJ -MN@_MZKH]S?ZZ/[!>NL><9KK' -MG&:Z!GQVN@9\=KKN&8^Z[AF/NO+@KKKRX*ZZA)C6NH28UKJ5:.ZZE6CNNL!2 -M]KK`4O:ZN&;FNKAFYKHCBLZZ(XK.NGFVLKIYMK*ZK.26NJSDEKK+[HZZR^Z. -MND?BDKI'XI*Z?]66NG_5EKIRR)JZ6NB.WEKJ!H)ZZ@:">ND*2HKI"DJ*ZZ)Z:NNB>FKKF -MHI:ZYJ*6NC.+GKHSBYZZ*$VVNBA-MKI7^M6Z5_K5NH["Z;J.PNFZL[KINK.Z -MZ;K&Y-6ZQN35NN1,JKKD3*JZX&]]NN!O?;KYXTVZ^>--N@W=3;H-W4VZ3U9] -MND]6?;H,.Z:Z##NFNC/3R;HST\FZ3K;1NDZVT;K-T,6ZS=#%NJXDIKJN)*:Z -M+R-]NB\C?;K4O$6ZU+Q%NGG\+;IY_"VZG?8MNIWV+;K(DVZ,AM-NC(;3;K!VERZ -MP=IRZ2WGLNDMY[+K7 -MGN"ZUY[@NFX"Q;IN`L6Z5'>ENE1WI;J@OY&ZH+^1NK.ZD;JSNI&Z[J65NNZE -ME;K9L)&ZV;"1ND#T\NJ*,%;JBC!6Z)1;NAB0 -M@;H8D(&ZGV6)NI]EB;KU-'NZ]31[NN+&6[KBQENZT($TNM"!-+K5HBRZU:(L -MNH2P6[J$L%NZ3_J8ND_ZF+IG+<"Z9RW`NB6:V[HEFMNZL9+;NK&2V[I9R,^Z -M6Z;Q>PNF\7L+JFEYBZII>8NO;5C+KVU8RZB+J0 -MNHBZD+K%0Z2ZQ4.DNN"=O[K@G;^Z)@W7NB8-U[I^U]ZZ?M?>NAHMS[H:+<^Z -M%,JSNA3*L[HX(J2Z."*DNJ`SNK2WL[K2"<^ZT@G/NOE![KKY -M0>ZZ[-C]NNS8_;JBZ/FZHNCYNILI[KJ;*>ZZI)S:NJ2NLF5`KO)E0*[WEP*N]Y<"KMJ(Q*[ -M:B,2N^LX#KOK.`Z[#S0.NP\T#KNY9`:[N60&NWF(`+MYB`"[2X_MNDN/[;HD -M*M:Z)"K6NG<8L[IW&+.ZGV2GNI]DI[K')Z^ZQR>ONNY?RKKN7\JZO4+QNKU" -M\;I[]0^[>_4/NR-R(;LCZ7RNGNJ4EI[JE):>ZQ7&^NL5QOKJJ1^6ZJD?ENLW^![O-_@>[ -M[O]\JN[_?*KOCV2J[X]DJNQ_R)KL? -M\B:[UR@?N]"Z]P#=NO<`W;I@^=RZ8/G< -MNOD3^+KY$_BZGV8/NY]F#[O^D2B[_I$HNZ*[0;NBNT&[PP-7N\,#5[M?_%:[ -M7_Q6N[!F2;NP9DF[+!,TNRP3-+N`T1R[@-$[.`E' -MNS@)1[O=1S^[W4<_N^RI,[OLJ3.[R`PHN\@,*+L$@AJ[!((:NR[X#+LN^`R[ -MD5T!NY%=`;N"A^NZ@H?KNF)_Z[IB?^NZ`.CZN@#H^KK;\0J[V_$*N[%*'+NQ -M2AR[C<8INXW&*;LKBB^[*XHONUJ6+;M:EBV[-/XANS3^(;L;0AJ[&T(:NSA/ -M&+LX3QB[<1(>NW$2'KM#U2.[0]4CN_:\);OVO"6[\LDCN_+)([LG(AJ[)R(: -MNP9[$+L&>Q"[\(@.N_"(#KL?.!:['S@6NYV:);N=FB6[[^@VN^_H-KN3;T*[ -MDV]"N]IH0KO::$*[J,(XNZC".+L^5RF[/E[M+87NQ(L#+L2+`R[-B<,NS8G#+L2T!.[$M`3N]DE([O9)2.[ -M7\TJNU_-*KLXW"B[.-PHN[4I(;NU*2&[NG<9N[IW&;M8G!6[6)P5NPI8&[L* -M6!N[5Q,ANU<3(;L1N2B[$;DHNX!S+KN`BJ[Z)\FN^B?)KLP\1Z[,/$> -MNU47&[M5%QN[OST7N[\]%[M\.!>[?#@7NR@'&[LH!QN[\U`JN_-0*KNNF3F[ -MKIDYNX50/[N%4#^[=W8[NW=V.[MIR3.[:N^4#%[OE`Q>[<%D/NW!9#[L-W0.[#=T#NT&5]+I!E?2Z(Q;INB,6 -MZ;IH^O^Z:/K_NF#[%+M@^Q2[>FTUNWIM-;O\.4Z[_#E.N[)A7[NR85^[@8A; -MNX&(6[MJ)$Z[:B1.N_T>.;O]'CF[AS(BNXNX0,(+N$#""[[`8@N^P&(+NR&1Z[LAD>N^#* -M([O@RB.[J4HMNZE*+;L5@#R[%8`\N[3E1[NTY4>[['M/N^Q[3[MNC4V[;HU- -MN^1,/KOD3#Z[`7$GNP%Q)[O!?1*[P7T2N\CU"+O(]0B[%@H'NQ8*![O>N0R[ -MWKD,NWN"$+M[@A"[;'T0NVQ]$+NZD0Z[NI$.NYB_"KN8OPJ[O+H*N[RZ"KN? -M3Q*[GT\2N[>P';NWL!V[.:HPNSFJ,+NQ[SV[L>\]N[>!1;NW@46[RGI%N\IZ -M1;O!P3^[P<$_NR8C.+LF(SB[^VHRN_MJ,KM?S2J[7\TJNQ(6);L2%B6[#:X9 -MNPVN&;LR80R[,F$,N]?Z`+O7^@"[GB'^NIXA_KHZUP*[.M<"N]PR#KO<,@Z[ -M9*@7NV2H%[M_`B.[?P(CN_'A)+OQX22[\1$AN_$1(;LU0AV[-4(=NZX\';NN -M/!V[]P`AN_<`(;NJCBB[JHXHNPXW+KL.-RZ[HQ4PNZ,5,+N'82J[AV$JNQH` -M'[L:`!^[>9\3NWF?$[O7M1&[U[41N]5=%[O571>[;04=NVT%';LDY!Z[).0> -MNXS>'KN,WAZ[;&05NVQD%;OB(@B[XB((NYO\\;J;_/&Z=63JNG5DZKJ0Z_&Z -MD.OQNBI(!+LJ2`2[OYD/N[^9#[M`(Q>[0",7NSBL'KLXK!Z[I6TBNZ5M(KOK -M9R*[ZV[_=H'NRNY";LKN0F[('H-NR!Z#;NH'1.[J!T3N\K`&+O*P!B[^_44 -MN_OU%+LZO@>[.KX'NX.$[;J#A.VZ\LG'NO+)Q[H\=+RZ/'2\NE9%S[I61<^Z -M;W7\NF]U_+IILQ:[:;,6NP5F*[L%9BN[@\HTNX/*-+L)`#&["0`QN]]Q*;O? -M<2F[R)@6N\B8%KL&P0.[!L$#NTU_TKKLJL.Z[*K#NK$= -MO+JQ';RZ$YW#NA.=P[JTH=*ZM*'2NDBP\+I(L/"Z_"`+N_P@"[L#)AJ[`R8: -MNT.E(;M#I2&[4=T=NU'=';MJL!"[:K`0NP7#^[H%P_NZA:O=NH6KW;K75\.Z -MUU?#ND`,N+I`#+BZ=<&LNG7!K+H+^JBZ"_JHN@WTJ+H-]*BZ#^ZHN@_NJ+J] -M*K2ZO2JTNF@JTKIH*M*ZT6G[NM%I^[KG/';L'CQV[-LD9NS;) -M&;MOHPR[;Z,,NV$][+IA/>RZO;;&NKVVQKK6<:RZUG&LN@DMH;H)+:&Z=:B9 -MNG6HF;H`HYFZ`*.9NA!@CKH08(ZZR[U^NLN]?KJ8O6"ZF+U@NINU8+J;M6"Z -M825WNF$E=[I"!9*Z0@62NE"QL[I0L;.Z^%K5NOA:U;KRQ^NZ\L?KNF>W^KIG -MM_JZJ/#VNJCP]KIZ;.^Z>FSONK9MX+JV;>"ZN#?&NK@WQKH!B:2Z`8FDNA+0 -M9[H2T&>Z+(D5NBR)%;J97L*YF5["N9]DL[F?9+.Y3RCON4\H[[G424*ZU$E" -MNM'UC;K1]8VZ(UJDNB-:I+HOF*"Z+YB@NH<:F;J'&IFZ%JZ"NA:N@KJI[&ZZ -MJ>QNN@-M9[H#;6>Z&Z""NAN@@KI7S8VZ5\V-NL?YF+K'^9BZ*$V&NBA-AKJ\ -M0V>ZO$-GNEME2;I;94FZHG,ZNJ)S.KKZX4&Z^N%!NH7$4+J%Q%"ZAS%8NHE+F_WI2Y -MX?8%NN'V!;HFKV:Z)J]FNH(_G+J"/YRZ"[2]N@NTO;I`K;VZ0*V]NJ16I[JD -M5J>Z22*"NDDB@KKUDB.Z]9(CNF%KLKEA:[*Y?JD4N7ZI%+EWT^VW=]/MM_'* -M;3CQRFTX@YD4.8.9%#GGN6TYY[EM.0E%,CD)13(Y```````````[7J.Y.UZC -MN=TQ,KK=,3*Z7>J!NEWJ@;I7*IBZ5RJ8NN$DF+KA))BZ%TU\NA=-?+I2?3FZ -M4GTYNOLZ_+G[.ORYX]K`N>/:P+D-?X6Y#7^%N?1.%+GT3A2YFT)MN)M";;@` -M````````````````````R-ZQN,C>L;ADV+&X9-BQN`#2,;D`TC&Y>@]MN7H/ -M;;GU!FVY]09MN08?%+D&'Q2Y<+BQN'"XL;AE[6RX9>ULN*>K,;FGJS&Y"7/` -MN0ESP+G&G0RZQIT,NFQE&[IL91NZOOD3NK[Y$[IK]).Y:_23N;^Q;#B_L6PX -M?TG`.7])P#F_:OLYOVK[.:S.W3FLSMTYRMD3.L;GW -M7K&YMZ[=N;>NW;FZIMVYNJ;=N.+$X^4)L.?E";#DEL\XY);/..0WU^CD-]?HY_^OZ.?_K^CFXVK\YN-J_ -M.3J/$SDZCQ,YX@NQN.(+L;@>Q(2Y'L2$N5._A+E3OX2Y?W=.N7]W3KG"[6NX -MPNUKN#WEZS<]Y>LWN-QK.+C<:S@RU.LW,M3K-ZW+Z[>MR^NW)\/K-R?#ZS>B -MNFLXHKIK.-D[3CG9.TXYF`2B.9@$HCGP+,XY\"S..>#XH3G@^*$Y!AY..08> -M3CFAI;`XH:6P.#V?L#@]G[`X"BH3.0HJ$SF6VZ$YEMNA.16OW#D5K]PY&*?< -M.1BGW#F]%),YO123.=Q+ZSC<2^LX%@H3N18*$[D5482Y%5&$N92RH;F4LJ&Y -MC<1-N8W$3;GP6#"Y\%@PN;L8:[B[&&NX``````````#.Y!(YSN02.4:?33E& -MGTTYT9?-.=&7S3EC?0LZ8WT+.JXF&CJN)AHZ+_G^#G) -M\\PYR?/,.2E)OCDI2;XY)"KJ.20JZCGV`PLZ]@,+.D'Q(#I!\2`Z;XPO.F^, -M+SH+AB\Z"X8O.K*/&3JRCQDZJNH*.JKJ"CI^[NDY?N[I.=H*OCG:"KXYQ:%, -M.<6A3#GNU.FW[M3IMTY9+[E.62^YX\-IN>/#:;E=N^FX7;OIN")&+SDB1B\Y -MSF\#.LYO`SJ4;4PZE&U,.IX,ASJ>#(SQ$Z#?.N.0WSKCFI["XYJ>PN.>2_$3GDOQ$YX=\N.>'?+CD^M9$Y/K61 -M.4CVRSE(]LLYB!D#.H@9`SK*-2`ZRC4@.NP'-CKL!S8ZIJ]A.J:O83K238HZ -MTDV*.HYEISJ.9:KU -MO#G7S`(ZU\P".A!@+CH08"XZ"?]V.@G_=CJ,L:HZC+&J.NFHZ3*7J.J2(1.X=W%3N' -M=Q4[\3X7._$^%SO8;!X[V&P>.S::)3LVFB4[U&`G.]1@)SNC6B<[HUHG.Z#N -M(3N@[B$[3X03.T^$$SN63@,[EDX#._',Z3KQS.DZFROF.ILKYCI'A?0Z1X7T -M.B8[`SLF.P,[1\L/.T?+#SN+)AX[BR8>.[A0)3NX4"4[>N(H.WKB*#O8/RX[ -MV#\N.YYH-3N>:#4[%?1!.Q7T03N>?DX[GGY..\K94SO*V5,[KPQ).Z\,23L` -M1S4[`$.JBJTSJHJM,Z7\_:.E_/ -MVCH"MNPZ`K;L.JH8`3NJ&`$[+Y<6.R^7%CO[WBT[^]XM.ZJZ2#NJND@[YM-8 -M.^;36#LYP6$[.<%A.SKN7SLZ[E\[&EQ3.QI<4SM[:T$[>VM!.\>Q+3O'L2T[ -MX\,;.^/#&SM#7Q8[0U\6.TVX&SM-N!L[%MLB.Q;;(CM6_2D[5OTI.P/W*3L# -M]RD[ZL@B.^K((CM8FQL[6)L;.^X!&#ON`1@[PX\;.\./&SM<>B0[7'HD.X/W -M,#N#]S`[,:H[.S&J.SNB_T`[HO]`.W+X0#MR^$`[F;I".YFZ0COF148[YD5& -M.[D'2#NY!T@[1`!(.T0`2#O/^$<[S_A'.U&60CM1ED([8]DW.V/9-SM"'2T[ -M0ATM.[TJ)#N]*B0[7.TE.USM)3N1TBX[D=(N.W[N-3M^[C4[/[`W.S^P-SM: -M.CL[6CH[.UPS.SM<,SL[!F0Y.P9D.3L:73D[&ETY.V0>.SMD'CL[UV]`.]=O -M0#N[,$([NS!".W*9/CMRF3X[8,H\.V#*/#MQ^SH[9UX['F=>.Q%<3CL17$X[ -M+/PX.RS\.#NX\R@[N/,H.XG0(3N)T"$[QE@E.\98)3L2_2\[$OTO.YHN/CN: -M+CX['IA*.QZ82CLLY4\[+.5/.V+=3SMBW4\[F-5/.YC53SM]E%$[?911.Z*, -M43NBC%$[KO=-.Z[W33N+#T4[BP]%.U-[03M3>T$[N*T_.[BM/SL_X#T[/^`] -M.XYE03N.94$[6,E-.UC)33O?$U,[WQ-3._?15#OWT50[.NQ+.SKL2SMD>S\[ -M9'L_.^]<.#OO7#@[@\HT.X/*-#L63S@[%D\X.]WI0CO=Z4([OOA).[[X23LH -M\4D[*/%).R8D2#LF)$@[WT$_.]]!/SM#8#8[0V`V.S"4-#LPE#0[LQCD[3OP\.T[\/#M;?4`[6WU`.QEV0#L9 -M=D`[_"([._PB.SNYP"X[N<`N.PGG)3L)YR4[1EDB.T99(CN%VB4[A=HE.Q\? -M*SL?'RL[BG$W.XIQ-SN)_T$[B?]!.WE"1SMY0D<[3?Y(.TW^2#NW]D@[M_9( -M.Y%U3#N1=4P[3K=1.TZW43MBKU$[8J]1.W'D3SMQY$\[W$I%.]Q*13MJ:34[ -M:FDU.^-]'COC?1X[6MP,.UK<##OF1@([YD8"._E!`COY00([T@D+.]()"SMQ -MG1P[<9T<.[LO+CN[+RX[5OX].U;^/3N7PD8[E\)&.TI]2#M*?4@[*KQ-.RJ\ -M33N-.%$[C3A1.YQN3SN<;D\[XR!*.^,@2CN0TT0[D--$.UI_.#M:?S@[5G$Q -M.U9Q,3N,)2P[C"4L.\>;*#O'FR@[U-,F.]33)CL)B2$["8DA.V#Z%CM@^A8[ -M**L*.RBK"CMRWP$[YPL[7N<+.POB"SL+X@L[N-P+.[C<"SM< -MU1([7-42.^:,&SOFC!L[:80B.VF$(CN#/20[@STD.^]T*3OO="D[]*LN._2K -M+CNKH34[JZ$U.\P8.3O,&#D[TI,U.]*3-3N*$RL[BA,K.Y-7&SN35QL[AV`& -M.X=@!COOT.DZ[]#I.D!0WSI`4-\Z^4'F.OE!YCH<)_LZ'"?[.IQ`#3N<0`T[ -MO6\9.[UO&3O$I!X[Q*0>.[6>'CNUGAX[II@>.Z:8'CO@S",[X,PC.Z6$)3NE -MA"4[/OHH.S[Z*#N3;RP[DV\L.RBK*CLHJRH[370>.TUT'CL3_!,[$_P3.V,) -M!CMC"08[YZ#Z.N>@^CI8)O`Z6";P.DVB[#I-HNPZS*/E.LRCY3H#F^4Z`YOE -M.B2'[#HDA^PZMN'].K;A_3JK%PL[JQ<+.Z)T'#NB=!P[:]PF.VO<)COLDB@[ -M[)(H.WT?'CM]'QX[D'8..Y!V#COCJO8ZXZKV.ES7VCI)LZ`'.;.@!SFSK0/:DZT#VI.K'OO3JQ[[TZQOO<.L;[W#IU!?PZ -M=07\.@J?!CL*GP8[%.`$.Q3@!#M_`?4Z?P'U.J'JU3JAZM4ZD2_!.I$OP3H; -M0KHZ&T*Z.H:3Q#J&D\0Z9W'+.F=QRSI;P=4Z6\'5.AJYU3H:N=4Z+RKSJ35J4ZDU:E.H&)ESJ!B9C3H\H:@Z/*&H.FX#RSIN`\LZ -M`:#?.@&@WSHKZ>DZ*^GI.CD>W#HY'MPZ_9+`.OV2P#I,ZJLZ3.JK.EDCGCI9 -M(YXZ6#V7.E@]ESKHAJ$ZZ(:A.E@_KSI8/Z\Z+(>Y.BR'N3HKH;(Z*Z&R.E2/ -MFCI4CYHZ(T)W.B-"=SIZXE0Z>N)4.KD>1SJY'D7,,Z7ES#.DI]I#I*?:0ZOPV).K\-B3JM -M0%LZK4!;.N.J1CKCJD8Z*Z-&.BNC1CHYPC\Z.<(_.ML(,CK;"#(ZW7<=.MUW -M'3J]<1TZO7$=.C[,.#H^S#@Z@?Q:.H'\6CK>`((ZW@"".N&!ECKA@98Z-2JD -M.C4JI#HZCZ>!.I'G@3J"XH$Z@N*!.G/=@3ISW8$Z -M2=M\.DG;?#HL$XPZ+!.,.C)XCSHR>(\ZCS.%.H\SA3H$"V\Z!`MO.H\T/SJ/ -M-#\ZGPD=.I\)'3I_`QTZ?P,=.KS0(SJ\T",Z<9TJ.G&=*CJ>:3$ZGFDQ.M(' -M/SK2!S\ZJM)%.JK213H*05HZ"D%:.@W<9SH-W&@'.0-LV3<#;-DW?F-9 -M.'YC63CX6MDX^%K9.'-263ES4EDYL""^.;`@OCGAR`*MZ*XBK>BN#+L6+@R[%BXK>-8.*WC6#A=I*(X7:2B.*+2 -MV#BBTM@X',I8.!S*6#B7P=@WE\'8-\V*HCC-BJ(X>YH].7N:/3F%\Y0YA?.4 -M.7DIYCEY*>8YIID`.J:9`#I%H/,Y1:#S.3-UO3DS=;TY$5ZB.1%>HCF#@W,Y -M@X-S.;Q#ASF\0XE#M@XI0[8.)@$(CF8!"(Y-/XA.33^(3DM^08Y+?D&.9#LUSB0[-XA=M7N.#C!KG@XP:YR<-RN;3AKGFTX:Y -MZK#7N.JPU[ADJ-B84NGHF%+KN,%ZZ[C!>NKP5>;J\%7FZX@MYNN(+>;JT6U>ZM%M7NC`) -M%+HP"12Z5`.4N50#E+F;<:&XFW&AN)XY5SB>.5Y^/W6N?C]UKEMIRZZ -M;:7 -M5CD887$Y&&%Q.=>U.SG7M3LY)WY6N"=^5K@YV*"Y.=B@N3($!KHR!`:Z<MN4OMU;E+[=6Y$D/CN1)#X[F*6`RZBE@,NI`! -M$[J0`1.ZM?L2NK7[$KH$4B"Z!%(@NG'Y)KIQ^2:ZKY<9NJ^7&;K6N/VYUKC] -MN6K>DKEJWI*Y$N4ZN1+E.KF0CU6YD(]5N69TA;EF=(6YS':MN*Y -M7\WBN42[$KI$NQ*Z:+42NFBU$KJI!`RZJ00,NJ93_;FF4_VY'T\%NA]/!;HG -M\A^Z)_(?NCSI,[H\Z3.Z*#5!NB@U0;K:VC.ZVMHSNGXT!;I^-`6Z*B^%N2HO -MA;F^#]6WO@_5MVO%GSAKQ9\XM/[4.+3^U#@N]E2X+O94N*GM5+FI[52Y=C/B -MN78SXKEWI1^Z=Z4?NM>&1[K7AD>ZD\M4NI/+5+HF:5NZ)FE;NN!N1[K@;D>Z -M,W8SNC-V,[KNZ02Z[ND$NIODA+F;Y(2Y5G*?N%9RG[CMC]2W[8_4MZ'4!+FA -MU`2Y&*>LN1BGK+E@$1*Z8!$2NOHF1[KZ)D>Z*,)-NBC"3;HST3FZ,]$YND]7 -M"[I/5PNZCGVLN8Y]K+FDRVZYI,MNN0[";KD.PFZY7Y^$N5^?A+D,FH2Y#)J$ -MN;B4A+FXE(2Y>1*?N7D2G[D73:RY%TVLN1W'QKD=Q\:YY;[[N>6^^[GI^!ZZ -MZ?@>NB:O1KHFKT:Z@>53NH'E4[HLGT:Z+)]&NOQ]);K\?26ZUOT*NM;]"KK9 -M.^ZYV3ONN97VX+F5]N"YK2CNN:TH[KD7'^ZY%Q_NN4%GQKE!9\:YG+*>N9RR -MGKE+D%.Y2Y!3N=PT!+G<-`2YB"\$N8@O!+F2Y6VYDN5MN8O)J[F+R:NYL&73 -MN;!ET[D[&@2Z.QH$NC)*$;HR2A&ZVJTKNMJM*[JWW5FZM]U9N@&X@+H!N("Z -M5\R7NE?,E[HWQI>Z-\:7NLZW>KK.MWJZ%QTRNA<=,KIZ$-.Y>A#3N?4'T[CU -M!].XDS^>.),_GCCJ]E(XZO92.&3NTK=D[M*W9RP>N69T6ZGF=%NA\W.+H?-SBZJB\XNJHO.+HV*#BZ -M-B@XNMCY*KK8^2JZ*:80NBFF$+J(7=*YB%W2N0-54KD#55*Y7KF=N%ZYG;CX -M0](W^$/2-W,[TK=S.]*W2WELN4MY;+D.3=^Y#DW?N5L(%[I;"!>ZG+0JNIRT -M*KHI'22Z*1TDNCZ&';H^AAVZUI\JNM:?*KH92#ZZ&4@^NDQ?2[I,7TNZTI-E -MNM*39;J`BF6Z@(IENM^W1+K?MT2Z',L6NAS+%KJL_.NYK/SKN;E,G;FY3)VY -M(RN0N2,KD+E()9"Y2"60N8TYG;F-.9VYPDRJN<),JKD7JMZY%ZK>N9&YZ[F1 -MN>NY_7L)NOU[";K=C1:ZW8T6NA-!-[H303>Z.-M*NCC;2KKWTDJZ]])*N@J@ -M,+H*H#"ZU^0/NM?D#[I61-&Y5D31N584M[E6%+>YX@RWN>(,M[F\N@*ZO+H" -MNIY0%KJ>4!:ZZ.0INNCD*;KHS!RZZ,PZ[Q57NM^(4+K?B%"Z5'A#NE1X0[K?61RZWUDLYG+G2-ZFYTC>IN6LXP[EK.,.Y -MXS;=N>,VW;E/G`BZ3YP(NN*8%;KBF!6ZPI(5NL*2%;K&$^JYQA/JN5L'J;E; -M!ZFY7@"/N5X`C[D"^INY`OJ;N8;PPKF&\,*YR^X.NLON#KJQ8R^ZL6,ONH[8 -M0KJ.V$*Z4%4ONE!5+[I_O>FY?[WIN8#1CKF`T8ZYV+-/N=BS3[E2JT^Y4JM/ -MN1JZF[D:NINY[)/R3W+DUM`ZZ-;0.NC8XY!6>;.05GFSD11C4Y$48U.3U:F[@]6INXSRC"N<\H -MPKD$QB&Z!,8ANFL)3[IK"4^Z"EEON@I9;[KSQG6Z\\9UNM2\=;K4O'6Z0<1H -MND'$:+KC\$&ZX_!!NJ&\![JAO`>ZQLU.N<;-3KE(.P$Y2#L!.>_0P3GOT,$Y -M_W7U.?]U]3EKEMLY:Y;;.2T0CCDM$(XYI9K.-Z6:SC=D9&BY9&1HN9J)SKF: -MB?)[IT\O2Y=/+TN>')C;GAR8VY9#3.N&0TSKC?*\XWWRO. -M-X.:FCB#FIHXQ=``.<70`#E/$LXX3Q+..%>'FCA7AYHX\X":./.`FCB^^$VX -MOOA-N#(R-+DR,C2YQVV:N<=MFKD[`<&Y.P'!N1.TVKD3M-JY7Q2TN5\4M+F# -MFX"Y@YN`N=)-FKC239JXD[3-MY.TS;<.K,VW#JS-MS6&`+DUA@"YXH"`N>*` -M@+E]DLVY?9+-N5FQP+E9L<"Y+?FFN2WYIKFR&AJYLAH:N6AP3;AH<$VXZ@V: -M..H-FCB&!YHXA@>:.-A6S3C85LTXOOJ9.+[ZF3C-1[$L -M.FD/,SII#S,ZTJH,.M*J##JNR;\YKLF_.5:*3#E6BDPYT(',.-"!S#@````` -M`````,9PS+C&<,RX0&A,N4!H3+G0@8RYT(&,N6A!F;EH09FYL$Y,N;!.3+DK -M1LRW*T;,MUK%93E:Q64Y%G_R.19_\CD]Y"4Z/>0E.E'=)3I1W24ZS[,2.L^S -M$CH*$\PY"A/,.84*3#F%"DPY_P%,./\!3#AZ^OG+M_3PR[?T\,NW;^C+ -M-V_HRS?DU_XXY-?^.&372SEDUTLYE\)^.9?"?CGT";\Y]`F_.8XUY3F.->4Y -M_*X%.ORN!3K[E_XY^Y?^.42DRSE$I,LYKH)^.:Z"?CEKKA@Y:ZX8.;2*RSBT -MBLLXHZ$8.:.A&#D_FY@Y/YN8.4C?Y#E(W^0Y/(SQ.3R,\3D.OTU'CK&*C$ZQBHQ.O4=1#KU'40ZTV)= -M.M-B73J!65TZ@5E=.C$%1#HQ!40Z#`X>.@P.'CJ@V/PYH-C\.2V&XSDMAN,Y -M]!_P.?0?\#FKN/PYJ[C\.;<+\#FW"_`Y2QS*.4LE0Z -MH'I4.@;O>3H&[WDZQ<^2.L7/DCJDAJ4ZI(:E.ER^JSI)_.BWB?SI"UW\Z0M=_.E?,?SI7S'\Z*_^".BO_ -M@CJ!MG\Z@;9_.M0R3J9,(PZF3",.BR@F#HLH)@Z*M2>.BK4GCJ?!Z4ZGP>E.H\=J#J/':@Z -M@OFD.H+YI#JZU:$ZNM6A.@=YF#H'>9@Z->2(.C7DB#I[]U\Z>_=?.I2;.CJ4 -MFSHZ'+,A.ARS(3I5=!LZ570;.LGR1CK)\D8Z#\&(.@_!B#H-(;$Z#2&Q.LQ& -MTSK,1M,Z#<;B.@W&XCHTH=\Z-*'?.JF_QCJIO\8ZAJJG.H:JISK'LHLZQ[*+ -M.FX:Z+3KWNBTZ@K,M.H*S+3KJWS,Z -MZM\S.B?:4CHGVE(Z8SA^.F,X?CKZXIJCK=7JHZT"6D.M`EI#H&!J$Z!@:A.H'FG3J!YITZM]^=.K?? -MG3KMCY0Z[8^4.HL0A3J+$(4ZV?1D.MGT9#JL^T4ZK/M%.H[#/SJ.PS\ZH7A8 -M.J%X6#JTB7TZM(E].F0TCCID-(XZ$727.A%TESJ+;93'.K7DQSK& -MM<$ZQK7!.EQAM3I<8;4Z1.BB.D3HHCK&<)`ZQG"0.A8@A#H6((0ZU>I[.M7J -M>SK?<6DZWW%I.H+Z5CJ"^E8Z`=`E.@'0)3J%\0`ZA?$`.K-SQ#FS<\0YE/C< -M.93XW#G4`POCJ.'KXZ -M#CC$.@XXQ#I._;HZ3OVZ.O?3M#KWT[0ZQEB?.L98GSIRWXDZ]PY]7O<.>+SPSGB\\,YR6C<.CJ&J8DZAJF).H64ACJ%E(8ZBHAN.HJ(;CHP"%8Z,`A6 -M.C*G0SHRIT,ZK)Y#.JR>0SJ)ST\ZB<]/.C`X:#HP.&@Z8V9T.F-F=#KS(V@Z -M\R-H.M09:#K4&6@Z_=A;.OW86SI_ZF$Z?^IA.GG[9SIY^V/53H'CU4ZJ!X].J@>/3KIR2HZZ -M!3IYG@4Z>_4C.GOU(SHB7$@Z(EQ(.GB)6CIXB5HZ'&Y4.AQN5#K&04@ZQD%( -M.D(%-CI"!38Z4MLI.E+;*3K=TRDZW=,I.DOM-3I+[34Z>_4[.GOU.SH,#4@Z -M#`U(.D,$2#I#!$@ZV@I..MH*3CKI$%0ZZ1!4.F\66CIO%EHZ;1M@.FT;8#J3 -M$6`ZDQ%@.I7=33J5W4TZH8\O.J&/+SK2-@LZTC8+.FKWY3EJ]^4Y2^WE.4OM -MY3F\%P4ZO!<%.JM#'3JK0QTZ2V$O.DMA+SJH32DZJ$TI.HTZ(SJ-.B,ZTP4+ -M.M,%"SJS_PHZL_\*.B$:'3HA&ATZC#TU.HP]-3HF:5,Z)FE3.A>2<3H7DG$Z -M<(=Q.G"'<3JV5EDZME99.ETQ1SI=,4`HZT($<.M"!'#K,C30ZS(TT.GZI9#I^J60Z+%J!.BQ:@3I66HHZ -M5EJ*.KA.@3JX3H$Z!(%D.@2!9#K_8#HZ_V`Z.I!'%CJ01Q8Z=UW8.7==V#GF -M1;0YYD6T.8Q2<#F,4G`YYD?P..9'\#C_,,`W_S#`-P``````````]Q>0./<7 -MD#B3$9`YDQ&0.1X,'#H>#!PZ_0=P.OT'<#I-_I@Z3?Z8.MGVI#K9]J0ZN?"8 -M.KGPF#J'[(8ZA^R&.D+75SI"UU\YG3WO.?8R -M[SGV,N\YEGX/.I9^#SJ/82XXP-WN.$HE)SG*3H,Y -MRDZ#.0`7ISD`%ZG)RCGIRP?@SDI!0\Y*04/.0:I/C@&J3XX8?B.N&'X -MCKC[ESZX^Y<^N'://CAVCSXXSU=6.<]75CE29LHY4F;*.;WD"#J]Y`@ZG=X( -M.IW>"#I?Y/DY7^3Y.1M"RCD;0LHY&:2:.1FDFCE+2SXY2TL^.<5"OCC%0KXX -M0#H^N$`Z/KB#:R:Y@VLFN>B>CKGHGHZYI3ZRN:4^LKFB;6(ZXFUB.N+$S[;BQ,^VXTTL.N=-+#KEO10ZY;T4. -MN;T3;;F]$VVYF5Z"N9E>@KE#,HZY0S*.N>)2@KGB4H*Y3H<]N4Z'/;G)?KVX -MR7Z]N```````````OFT].+YM/3@``````````+-S7+VW+E2]N"Y4O;BI -M2SVYJ4L]N5KRC;E:\HVY2I.EN4J3I;E]J^"Y?:O@N94,`KJ5#`*ZVZH9NMNJ -M&;HJNQ.Z*KL3N@-4[+D#5.RY)O6!N2;U@;GX_CRX^/X\N-:X#3G6N`TYJXM4 -M.:N+5#D.K`TY#JP-.>+AN5+E29=2Y4F74N6.L![ICK`>Z -MQE<9NL97&;IL-A^Z;#8?NB;?,+HFWS"Z1H9"ND:&0KH(1TZZ"$=.NC592+HU -M64BZ;Z,VNF^C-KI60Q.Z5D,3NG4%U+EU!=2YE5*-N952C;E)\E.Y2?)3N55T -MZ[A5=.NXC%2\N(Q4O+@&3#RX!DP\N(%#/+B!0SRX^SJ\N/LZO+AV,CRY=C(\ -MN?$IO+GQ*;RY,KP>NC*\'KK*?%FZRGQ9ND)+@;I"2X&Z9T6!NF=%@;HK_VJZ -M*_]JNO253;KTE4VZ#Y$>N@^1'KJ3G?:YDYWVN4#=N[E`W;NYV)R8N=BON8#G -MK[D'U@:Z!]8&N@+[*;H"^RFZRJLUNLJK-;J4#R2ZE`\DNH>]!KJ'O0:Z2=S2 -MN4GNJ&/+[JACR^Z -M?F$UNGYA-;H]636Z/5DUNE>>*;I7GBFZ93(2NF4R$KJ4W^FYE-_IN;)?K[FR -M7Z^Y0&E2N4!I4KFS_SJXL_\ZN"[W.C@N]SHXJ.XZ.*CN.CB:+(RXFBR,N%QX -M@+E<>("YL#?UN;`W];DWQA>Z-\87NHQK([J,:R.Z.XX=NCN.';H26P"Z$EL` -MNLW_T;G-_]&Y^*&ZN?BANKD*0\:Y"D/&N2@UZ;DH->FY"-/TN0C3]+G;'^FY -MVQ_IN=,>QKG3'L:Y(M.+N2+3B[E29CJY4F8ZN4#UZ+A`]>BXF>KHN)GJZ+C" -M3+JXPDRZN#Q$NK@\1+JXR:R+N,FLB[AEIHNX9::+N%>UZ+A7M>BXXMTBN>+= -M(KDYDXNY.9.+N2ZRQ;DNLL6YO(KHN;R*Z+D5@.BY%8#HN7TVT;E]-M&Y17.+ -MN45SB[E1(U&Y42-1N?O=.;G[W3FY=M4YN7;5.;E3DR*Y4Y,BN88UZ+B&->BX -MYKLYN.:[.;A@L[DW8+.Y-]NJN3C;JKDX5J(Y.%:B.3@``````````)WUY[B= -M]>>XQ8@YN<6(.;E`@#FY0(`YN -MN3X9Z#GN,&5Y[C!E>>X#^T*N0_M"KF/,SFXCS,Y -MN`HK.3@**SDXA"*Y.(0BN3C_&;DX_QFY.!O-BC@;S8HX``````````!O`#FX -M;P`YN.^YBKCON8JXB[.*N(NSBKC?YCBXW^8XN%G>.+A9WCBX7Z"*N%^@BKA. -MS3BX3LTXN```````````,XV*.#.-BCA&G2$Y1ITA.6[K?3ENZWTYM]]].;?? -M?3ETK4\Y=*U/.1.VYC@3MN8X``````````!V8(JX=F"*N!):BK@26HJX```` -M`````````````````(A>N+>(7KBW@VOFN(-KYK@M5T^Y+5=/N5D\H;E9/*&Y -MY#2AN>0TH;EK.D^Y:SI/N6@KN+=H*[BWXB(X.>(B.#D#G,,Y`YS#.6N7\3EK -ME_$Y2,4X"^'E.+?> -MM[BWWK>X"%Z5N0A>E;D7P>6Y%\'EN=W3";K=TPFZE`\$NI0/!+K:.*RYVCBL -MN;'`";FQP`FY$:.W.!&CMSCILXDYZ;.).0:2MSD&DKD0K#GLY?#DS+GPY,RY\.9\V93F?-F4YR(`).HDX9'J).%9%-[A613>XT#RWN-`\M[@X9XFX.&>)N```````````0",W.$`C -M-SBZ&K)N$1'B;C@0`FYX$`)N7PZ";E\.@FY) -MN!@TB;B:YS8XFNSF'&HDYAQJ).99/>SF63WLY -M/VQD.3]L9#F8860YF&%D.7`L>SEP+'LY2DQD.4I,9#FD060YI$%D.;!D33FP -M9$TYH[@?.:.X'SFO(>0XKR'D.`D7Y#@)%^0X8@SD.&(,Y#B[`>0XNP'D.'/' -MB#ASQX@XOE:VM[Y6MKA,.1'H3#DM0-@Y+4#8.4!0'SI`4!\Z'/DD.ASY)#I701\Z -M5T$?.M/6[CG3UNXY=9&J.761JCGY*A\Y^2H?.7+?M3=RW[4W,F$(N3)A"+G. -M6HBYSEJ(N9Y^S+F>?LRY,AG!N3(9P;G7M+6YU[2UN=QA3+G<84RYS*.UM\RC -MM;>P3DPYL$Y,.98YJCF6.:HY*)37.2B4USD)BMM_USGK?]S#FIOL`YJ;[`.:VSGCFMLYXYXZQB.>.L8CGQ^HX -MVI4>N=J5'KFGVTNYI]M+N?&&'KGQAAZY^R.UM_LCM;<'>!XY!W@>.6,?DSEC -M'Y,YN:O+.;FKRSDBHLLY(J++.?9(P#GV2,`YV_"T.=OPM#E+2YXY2TN>.=GU -MDCG9]9(Y'0UB.1T-8CGM-!XY[30>.;"4ASBPE(E>T.7I7M#DW -M.X&`YZ>"1.>G@D3ER@K,YZ[]CGQ*0PZ\2D,.DY9 -M%SI.6122R3GDDLDY8\(`.F/"`#I0@RTZ4(,M.ERG5#I(W8Z -MGB-V.J.X7SJCN%\Z(XDX.B.).#I>71$Z7ET1.K?&ZCFWQNHYAP6^.8<%OCF! -MS[(Y@<^R.8R:ISF,FJ\MU#GO+=0YTY`+.M.0 -M"SI#,3@Z0S$X.G#-9#IPS60Z4QZ#.E,>@SHDY?,7I.<[\_SG. -M_/\Y_Q@+.O\8"SJ',A8ZAS(6.E,5.G'C%3I14?\Y45'_.1%%_SD11?\YQB@%.L8H!3I\K@HZ -M?*X*.JDS$#JI,Q`ZO"P0.KPL$#H=L14Z';$5.OZ_(#K^OR`ZDU@Q.I-8,3J/ -M!$TZCP1-.J8C8SJF(V,ZJRQN.JLL;CHX(6XZ."%N.E6,:#I5C&@Z)H%H.B:! -M:#KW=6@Z]W5H.B7B8CHEXF(Z>\97.GO&5SJMFT$ZK9M!.BKK)3HJZR4ZO;4$ -M.KVU!#K<,N@YW#+H.:TGZ#FM)^@Y5RD*.E8YRL'F.5!4"3I05`DZEL,D.I;#)#IZLCHZ>K(Z.LDA2SK)(4LZ3Q)6.D\2 -M5CJO?F8ZKWYF.M!L<3K0;'$Z&&%Q.AAA<3HB768Z(EUF.M!J13K0:D4Z@X09 -M.H.$&3I*3M`Y2D[0.7Q_CCE\?XXY^X*#.?N"@SG)4*\YR5"O.>'W^SGA]_LY -MP,4I.L#%*3J3$5`ZDQ%0.NJ`53KJ@%4Z)81*.B6$2CIPB#\Z<(@_.B,5+SHC -M%2\Z"BL9.@HK&3I4,PXZ5#,..JX\`SJN/`,Z8WW[.6-]^SDCLC.N7K(SH7JT0Z%ZM$.M+P7SK2\%\ZYUME.N=;93J&[U0ZAN]4 -M.N*N+CKBKBXZ*^<-.BOG#3I-1=HY347:.?QGQ#G\9\0Y9E[$.69>Q#E9)=HY -M627:.2KJ[SDJZN\Y<][O.7/>[SED!=HY9`7:.9L4SSF;%,\Y%_#9.1?PV3G= -MO`(ZW;P".M+Q'3K2\1TZ'PA$.A\(1#HY-U\Z.3=?.C\/:CH_#VHZ`'5O.@!U -M;SI8^&DZ6/AI.HT+7SJ-"U\Z:J]..FJO3CJ]Y#@ZO>0X.E*L'3I2K!TZ?$_D -M.7Q/Y#DL<((Y+'"".?A:V3CX6MDX9&.".&1C@CA5T:TX5=&M.)Q6@CF<5H(Y -M9^CN.6?H[CG%MRTZQ;<,.H]B -MK3F/8JTY"EHM.0I:+3DC_0$Y(_T!.3^;6#D_FU@Y@12X.8$4N#EO`/DY;P#Y -M.?SR'#K\\APZZ28M.NDF+3I7AS(Z5XZ&$Z7NAA.F=:3#IG -M6DPZDLXV.I+.-CK>1"$ZWD0A.OS<&SK\W!LZHG46.J)U%CISKPLZ6.X3FV@^$YMH/A.;CQ]CFX\?8YS2X&.LTN!CIQ018Z<4$6 -M.GM2)CI[4B8Z=J4@.E&`&SI1@!LZ=C$F.G8Q)CJ+X3`Z -MB^$P.H^0.SJ/D#LZX.)`.N#B0#KK?3LZZWT[.C`()CHP""8ZZMX%.NK>!3K0 -M)M8YT";6.?*RP#GRLL`Y7*G`.5RIP#G;!M8YVP;6.2!D`#H@9``Z^;8%.OFV -M!3I0L`4Z4+`%.JBI!3JHJ04ZX5,0.N%3$#H*_1HZ"OT:.@K]*CH*_2HZX)PE -M.N"<)3H?-Q`Z'S<0.EB;1BCEOTBHY;](J.6\7`#EO%P`Y -M9,$J.63!*CDUMHHY-;:*.7JRZCEZLNHY5OT?.E;]'SH]GCHZ/9XZ.NR4.CKL -ME#HZT3DE.M$Y)3H&-14Z!C45.@$4]3D!%/4YX!'5.>`1U3GE=XHYY7>*.7R5 -M_SA\E?\X(UNJ-R-;JC?L>W^X['M_N!A*JC<82JHW]]'4./?1U#AR#FHY<@YJ -M.8@PJCF(,*HY`BBJ.0(HJCED!=GCG7G9XY5`.4.50#E#G(K_TXR*_].``````` -M````.);]N#B6_;@4)SZY%"<^N0S>$[D,WA.YD#+3N)`RT[BZ[*BWNNRHMT]6 -M?3A/5GTXK]LH.*_;*#B_/'TXOSQ].*7**#BERB@X+R/]."\C_3@`Z%(Y`.A2 -M.0,FGCD#)IXY!AZ>.08>GCD(`HDY"`*).76J/3EUJCTY?M9\.'[6?#AYAJBX -M>8:HN.Z\_+CNO/RX'(0]N1R$/;F&>CVYAGH]N7U]4KE]?5*Y6F<]N5IG/;DO -M:%*Y+VA2N8A=4KF(75*Y!#J3N00ZD[FM5GRYK59\N;Q#9[F\0V>YUBT]N=8M -M/;E`)#VY0"0]N:`=4KF@'5*YWQ1GN=\49[E2"%*Y4@A2N:S]4;FL_5&Y;/#[ -MN&SP^[@8[:>X&.VGN-S6>[CY -M;*OFN6RKYKE4&_&Y5!OQN>88W+GF&-RYF1C'N9D8Q[ETE+RY=)2\N=I]T;G: -M?=&Y`.@"N@#H`KIJ#AVZ:@X=NC?V,;HW]C&Z*>TQNBGM,;K=,2*ZW3$BN@,] -M#;H#/0VZTQ[FN=,>YKDM/KRY+3Z\N3%+I[DQ2Z>Y@Y'"FGN1PII[G/S.6YS\SEN5#?(;I0WR&Z\II+NO*:2[K@X6JZ -MX.%JNL%]>KK!?7JZ23EUNDDY=;H_X%6Z/^!5NA@<++H8'"RZ%Y,'NA>3![J: -MT.^YFM#ON:17Y;FD5^6Y[4OEN>U+Y;F`%_JY@!?ZN0>F#+H'I@RZ:PD7NFL) -M%[K+U2NZR]4KNHX)2[J."4NZ'CIJNAXZ:KJA&8*ZH1F"NO2LA+KTK(2ZCGUT -MNHY]=+H."5"Z#@E0NO8Q(;KV,2&Z/2L'NCTK![JP\0&ZL/$!NF0=![ID'0>Z -MP:P6NL&L%KHLGC"Z+)XPNL\I0+K/*4"ZC()*NHR"2KHYVE2Z.=I4NIAA9+J8 -M862Z7N=SNE[G<[KC382ZXTV$N@P/C+H,#XRZM)^.NK2?CKKS:(FZ\VB)NB`' -M?KH@!WZZ>CYINGH^:;KV=U2Z]G=4NO'A1+KQX42Z;!\PNFP?,+K=C""ZW8P@ -MNC1S`;HTDONAGI+[KC\FVZ -MX_)MNBS/D+HLSY"ZB':ENHAVI;JZ`ZBZN@.HNN?/HKKGSZ*Z6;&0NEFQD+I/ -M*7VZ3RE]N@8>7KH&'EZZ"T!$N@M`1+J0CB^ZD(XONLH((+K*"""ZG(00NIR$ -M$+H=5`NZ'50+NNQ,"[KL3`NZQ>8KKSWF*Z7$IR -MNEQ*Z\3YWNBDR=[HI,G>Z -M?DM\NGY+?+HELH"Z);*`NM$+=[K1"W>Z:;1LNFFT;+H%.5VZ!3E=NLH(6+K* -M"%BZ'B)=NAXB7;II@VRZ:8-LNASC>[HDKH?"*2Z -M'PBDNI+/L+J2S["Z]W6XNO=UN+JGW+6ZI]RUNJ)UIKJB=::Z2O&1NDKQD;K. -M_'^ZSOQ_NL,:7+K#&ERZT99'NM&61[J3;D*ZDVY"NJ.?3+JCGTRZ4+)1NE"R -M4;JO.&NZKSAKNM"">KK0@GJZN1Z/NKD>C[HM^J"Z+?J@NL-AM;K#8;6ZY@&] -MNN8!O;H\H<2Z/*'$NC'NO+HQ[KRZ?*ZRNGRNLKJ"XJ6Z@N*ENJA,H[JH3*.Z -M(T2CNB-$H[IZ5:BZ>E6HNN2_I;KDOZ6ZZ)V@NNB=H+ID8Y:Z9&.6NHN=B;J+ -MG8FZE,I^NI3*?KH'`X>Z!P.'NO@KD;KX*Y&ZE&N@NI1KH+I3-;*Z4S6RNIMQ -MP;J;<<&Z2:S0NDFLT+H)N-6Z";C5NJZNZ6GNKNEI[K!V;:Z -MP=FVNB\,QKHO#,:ZG]K:ZHM'FNN@IW[KH*=^Z#0S:N@T,VKJI=]>ZJ7?7NO1]W+KT?=RZ"OO>N@K[ -MWKIF`.2Z9@#DN@-LX;H#;.&Z;^CCNF_HX[K>=.NZWG3KNH(0^+J"$/BZ:54" -MNVE5`KLJ&@:[*AH&NQL3!KL;$P:[4,@$NU#(!+M*9?^Z2F7_NM=7_[K75_^Z -M4L/\NE+#_+KQX0"[\>$`NUVE!+M=I02[#^\*NP_O"KN\YPJ[O.<*NVG@"KMI -MX`J[K0\'NZT/![MJ@@2[:H($NVFR`+MIL@"[C^X!NX_N`;NTYP&[M.5@$NTP9"+M,&0B[5J$/NU:A#[N3*!>[DR@7 -MN],S(;O3,R&[J#0FNZ@T)KO?*R:[WRLFNWA<(KMX7"*[4TL=NU-+';NE^!:[ -MI?@6NZCP%KNH\!:[J^@6NZOH%KML9!F[;&09NQN>&KL;GAJ[<1(8NW$2&+L* -MAQ6["H<5N^C[$KOH^Q*['O02NQ[T$KO-+12[S2T4NY(K&;N2*QF[?:L@NWVK -M(+O)9B2[R68DN^1B*;OD8BF["YLJNPN;*KLC%"V[(Q0MN_,*+;OS"BV[I8,O -MNZ6#+[L3_#&[$_PQNY_R,;N?\C&[\B8NN_(F+KO_&BF[_QHINY0/)+N4#R2[ -MQH4AN\:%(;NIO2*[J;TBN],U);O3-26[8&TFNV!M)KO,I">[S*0GNPN;(KL+ -MFR*[]]$>N_?1'KLVR1F[-LD9NP@!&[L(`1N[B_@?NXOX'[M&;R>[1F\GNV6E -M+;MEI2V[')LRNQR;,KOQ$#6[\1`UN^9&-KOF1C:[H_TTNZ/]-+MV,S:[=C,V -MNU7J-+M5ZC2[KN`TNZ[@-+MY6#*[>5@RNQY2+;L>4BV[`NS$'&+LQ!QB[(,`6NR#`%KOR]A>[\O87NW)L&KMR;!J[,60:NS%D&KM* -MV1R[2MD[-Z\VNS>O-KLRZS*[,NLRNXMM*[N+;2N[HBXE -MNZ(N);MD\!Z[9/`>NP!U%[L`=1>[:/H/NVCZ#[OP0@>[\$('NY\'`;N?!P&[ -M3X;_ND^&_[HVL@2[-K($NS41$;LU$1&[X&X=N^!N';LURRF[- -M";L[7@F[FJ`%NYJ@!;L.(`.[#B`#NV+<`;MBW`&[\1$#N_$1`[M).0F[23D) -MNXTC#KN-(PZ[^H45N_J%%;LSNA:[,[H6N]UU%;O==16[.'T0NSA]$+OS#`F[ -M\PP)NWW9`KM]V0*[+CW^NBX]_KJTM_NZM+?[NK%,`+NQ3`"[A($!NX2!`;N& -M>@&[AGH!NV*!^[IB@?NZ@P[TNH,.]+IH$^^Z:!/ONGX&[[I^!N^Z^.;SNOCF -M\[H+M/VZ"[3]NIA)`;N820&[FT(!NYM"`;OGBOVZYXK]NA@;]KH8&_:Z%JSN -MNA:L[KJEL^FZI;/INKN[Y+J[N^2ZVT[=NMM.W;II;=.Z:6W3NLT7Q[K-%\>Z -MGCB]NIXXO;IO1+BZ;T2XNDBONKI(K[JZJ>S&NJGLQKI&G=6Z1IW5NMS7X;K< -MU^&Z@BCINH(HZ;K<&^FZW!OINBZ;YKHNF^:Z#[_ZB\O7NDKQ -MS;I*\HNLB7J+JU -MXJ^ZM>*ONEQ*LKI<2K*ZO[&TNK^QM+KEI[2ZY:>TNH[PN[J.\+NZM\?`NK?' -MP+JA8,^ZH6#/NJ&FUKJAIM:ZAGO;NH9[V[H&']2Z!A_4NDA3RKI(4\JZXSBY -MNN,XN;H@D*JZ()"JNO?HF[KWZ)NZ[9&4NNV1E+I/S(JZ3\R*NCWFA;H]YH6Z -M+R-]NB\C?;J7UX6ZE]>%NJ*+C[JBBX^Z)VBENB=HI;K9L+VZV;"]NE)EV+I2 -M9=BZ4^_FNE/OYKK:4.FZVE#INML=W;K;'=VZ27[.NDE^SKK\EKBZ_):XNEV- -MI[I=C:>Z(\Z=NB/.G;KZ#Y2Z^@^4NEXMC[I>+8^Z+]Z'NB_>A[K+CX"ZRX^` -MNLDW?+K)-WRZ&EN%NAI;A;JK(G;KGB)VZ'@JLNAX*K+H&V;"Z -M!MFPNF6GM;IEI[6Z:)VUNFB=M;HS_[>Z,_^WN@4XO[H%.+^ZAP3$NH<$Q+H? -M9<:Z'V7&NO7NP[KU[L.ZF\RWNIO,M[JAU::ZH=6FNH/@E;J#X)6Z'IB.NAZ8 -MCKJ%4(>ZA5"'NA!)A[H028>Z/!:,NCP6C+H]MY6Z/;>5N@@9F+H(&9BZ>>2< -MNGGDG+IBKZ&Z8J^AN@Y-J[H.3:NZ)H"RNB:`LKIRLKFZI[IG@NBIWH+H^;J"Z/FZ@NG<`GKIW`)ZZ/"Z9NCPN -MF;J)7)2ZB5R4ND=4E+I'5)2ZK!29NJP4F;I7<)NZ5W";NJ$#F;JA`YFZ&_N8 -MNAO[F+H!*Y2Z`2N4NL`BE+K`(I2ZB^&8NHOAF+HR`Z"Z,@.@N@PDI[H,)*>Z -MVGVINMI]J;II$:>Z:1&GNE]"HKI?0J*ZWG.=NMYSG;KEI9BZY:68NF"=F+I@ -MG9BZ+O>:NB[WFKJX4)VZN%"=NNY'G;KN1YVZ-]V:NC?=FKHOKY.Z+Z^3ND1% -MD;I$19&Z%WJ,NA=ZC+IKEI.Z:Y:3NJ]0F+JO4)BZ:PJ=NFL*G;JD/YBZI#^8 -MNHH4D;J*%)&Z.^J)NCOJB;J"XHFZ@N*)NLG:B;K)VHFZ#?20N@WTD+I2K)6Z -M4JR5NA[$G+H>Q)RZ9UN:NF=;FKHHDY6Z*).5NALKD[H;*Y.Z88*5NF&"E;HO -MF)RZ+YBNG%8E;IQ6)6Z[[B$NN^X -MA+K)\VRZR?-LNL'R6;K!\EFZA+!+NH2P2[JKZ$:ZJ^A&NIV92[J=F4NZP<%9 -MNL'!6;JT+&.ZM"QCNNI1<;KJ47&ZU76$NM5UA+K&GI*ZQIZ2NMIHGKK::)ZZ -M,AJCNC(:H[H"M*"Z`K2@NI_:E+J?VI2ZU*6&NM2EAKH2+&RZ$BQLN@0[6;H$ -M.UFZ;KU/NFZ]3[I/^4JZ3_E*NG/%/+ISQ3RZ,),NNC"3+KI1\Q:Z4?,6NLG$ -M"+K)Q`BZ_@4$NOX%!+HE;`VZ)6P-NE`^(+I0/B"Z;^=%NF_G1;I-C&NZ38QK -MNI4[AKJ5.X:Z=^F*NG?IBKIL\9&Z;/&1NCHTC;HZ-(VZD'>(NI!WB+JC67FZ -MHUEYNHA>6+J(7EBZADLINH9+*;KIXOVYZ>+]N;H%O+FZ!;RYGN&Y5^0,NE?D#+IR;#*Z -M1;KQ$D6Z\1)%NMVE.[K=I3NZVCDRNMHY,KKGSBBZY\XHNN"T&KK@M!JZ4TP1 -MNE-,$;HY-0.Z.34#NB2>\[DDGO.Y2Y#SN4N0\[D4X/RY%.#\N0TC$;H-(Q&Z -M[M,CNN[3([JE)BVZI28MNDK!([I*P2.ZC%0,NHQ4#+H.(->Y#B#7N6'XGKEA -M^)ZY->."N37C@KG`VX*YP-N"N>F$E;GIA)6Y\"NHN?`KJ+G6T+JYUM"ZN9IS -MS;F:<\VY$+[6N1"^UKD:7>FY&EWIN0+Z^[D"^ONYK_0+NJ_T"[JXEA"ZN)80 -MNK7D"[JUY`NZO(D"NKR)`KJJ7_*YJE_RN2<*S;DG"LVY20JQN4D*L;EW7IZY -M=UZ>N<:TB[G&M(NY2=M,N4G;3+D-]!2Y#?04N8CK%+B(ZQ2X`N.4-P+CE#>[ -M1U\XNT=?./?1%+CWT12XH&(GN:!B)[G[#)ZY^PR>N13)U;D4R=6YSYWQN<^= -M\;F`1>BY@$7HN7H0P[EZ$,.YM]^=N;??G;EL9G&Y;&9QN>\5)[GO%2>Y\9NY -MN/&;N;@``````````+9KE#BV:Y0XR]8!.."92E#@F4I0X -MH4F4MZ%)E+=B4;FX8E&YN*F_)KFIOR:Y%\)+N1?"2[EN,3FY;C$YN8DNWKB) -M+MZXP"%>N,`A7K@``````````#`(7C@P"%XX:/O=.&C[W3B%\3@YA?$X.6EN -M@3EI;H$YEB&=.98AG3F(&)TYB!B=.572DSE5TI,Y1+PX.42\.#GOH5TX[Z%= -M.">57;@GE5VX-SH!N39>]SF<)^XYG"?N.4-XR3E#>,DY?7V2.7U]DCG48$DYU&!).>0> -M`#GD'@`Y[&,2.>QC$CD;B5LY&XE;.>)2DCGB4I(Y/R;).3\FR3G@AN0YX(;D -M.;G`]CFYP/8Y6++V.5BR]CF`=`0Z@'0$.L..#3K#C@TZ]J<6.O:G%CHLGQ8Z -M+)\6.F*6%CIBEA8ZOFT-.KYM#3I]90TZ?64-.J'L$3JA[!$Z?I$?.GZ1'SK" -M-"TZPC0M.J0J+3JD*BTZW`,D.MP#)#KU,PTZ]3,-.D"6VCE`EMHY.AVV.3H= -MMCF$W:,YA-VC.>WMK#GM[:PYJ4G1.:E)T3F`Z0,Z@.D#.L:>&CK&GAHZ8E$Q -M.F)1,3H`ZCXZ`.H^.M&753K1EU4ZM"QC.K0L8SK<2G4ZW$IU.GL\=3I[/'4Z -M&(]G.AB/9SH:14PZ&D5,.DW^,#I-_C`Z*C$1.BHQ$3K5C`,ZU8P#.C;X_3DV -M^/TYW8X,.MV.##H%(!HZ!2`:.F`G(SI@)R,ZJRTL.JLM+#J-(RPZC2,L.FX9 -M+#IN&2PZEQTU.I<=-3J/IT(ZCZ=".NXO4#KN+U`Z73!9.ETP63J[+V(ZNR]B -M.CJH9CHZJ&8Z,"!K.C`@:SJ>EV\ZGI=O.H&);SJ!B6\ZX(1X.N"$>#K!\7,Z -MP?%S.KY69CJ^5F8Z<;5/.G&U3SK-%CDZS18Y.CS^)CH\_B8Z8O0F.F+T)CHK -MYR.DMB93I+8F4Z0M=7.D+75SIZRELI7.E`\7#I0/%PZ -M^"ME.O@K93ID'F4Z9!YE.G,,;CIS#&XZ5OYM.E;^;3K,ZG8ZS.IV.C'6?SHQ -MUG\Z!-V(.@3=B#IB48TZ8E&-.N(*BSKB"HLZLDB$.K)(A#J+FVTZBYMM.N2H -M4CKDJ%(ZZ"I%.N@J13HP'T4Z,!]%.EB#4CI8@U(Z0V!D.D-@9#HDM7HZ)+5Z -M.O',@3KQS($ZQSZ&.L<^ACIO.C_V -M7CKOZ%XZ[^A>.J_%9SJOQ6/SK]'C\Z0DTM.D)-+3J1T2@ZD=$H.G/'*#IS -MQR@Z]F%#.O9A0SK%B%DZQ8A9.F:->#IFC7@Z-.>".C3G@CHS%X4Z,Q>%.LB! -M;SK(@6\ZM+9=.K2V73K:?DP>.J)=.3JB73DZQ_A*.L?X -M2CIZ*%@Z>BA8.GJ$7#IZA%PZ*G=<.BIW7#I>TF`Z7M)@.@DM93H)+64Z0']3 -M.D!_4SKA:STZX6L].D2,'CI$C!XZ9)+V.622]CFISEU'JLF.@;7/#H&USPZA[]2.H>_4CJ^LE(ZOK)2.I=[ -M13J7>T4Z1;DF.D6Y)CK`EP,ZP)<#.DVZR3E-NLDY6!&5.5@1E3F/[F,YC^YC -M.=710#G5T4`Y7KD=.5ZY'3EY*@PY>2H,.3*F'3DRIATY4ZEC.5.I8SD45*8Y -M%%2F.94,TCF5#-(YPK_].<*__3GC-P,ZXS<#.O'A]#GQX?0Y$!O).1`;R3F! -M69TY@5F=.8TZ8SF-.F,YA3E`.84Y0#G-+4`YS2U`.18B0#D6(D`Y7A9`.5X6 -M0#E2E2XY4I4N.2*B"SDBH@LYG9D+.9V9"SE==2XY774N.=M,43G;3%$Y%B!T -M.18@=#D/P((Y#\"".1*X@CD2N((YNQE1.;L943EQY/,X<>3S.')5BSAR58LX -M[$R+-^Q,BS=G1(NW9T2+M^(["[CB.PNX7#.+MUPSB[?7*@NXURH+N%$BB[=1 -M(HNWS!F+-\P9BS?JF=`XZIG0."*-4#DBC5`Y/`"+.3P`BSFM5IPYK5:<.3'O -MBCDQ[XHY5J`M.5:@+3DFW@HX)MX*.*'5BKBAU8JX\.;RN/#F\K@&V/*X!MCR -MN!1KK;@4:ZVX4`U0N%`-4+@&JXJW!JN*MP``````````^YF*-_N9BC=UD8HW -M=9&*-_"("CCPB`HXH,!/.*#`3SC8LT\XV+-/.&!OBC=@;XHWVF8*N-IF"KA_ -MC4^X?XU/N$/KK+A#ZZRX[W-/N.]S3[C%1(JWQ42*MU]:3SA?6D\XA=KQ.(7: -M\3B/[XFW"><)N`GG";C&S&P&CEUDG`Y=9)P.;6= -MFCFUG9HY.&>).3AGB3D,#DXY#`Y..2U6B3@M5HDXJ$V)N*A-B;AKEBNY:Y8K -MN7\"7[E_`E^Y*1MPN2D;<+E`#'"Y0`QPN9.T3;F3M$VYV#T:N=@]&KD"$HFX -M`A*)N'T)"3A]"0DX]P`).?<`"3GK:(`YZVB`.>H-FCGJ#9HYRI*B.'W -M.SDI[#LY*>P[.3RC"#D\HP@YMIJ(.+::B#@QDHBW,9*(MZN)"+BKB0BX)H$( -MN":!"+@``````````!MPB#@;<(@XEF<(.99G"#FV@CLYMH([.46B?SE%HG\Y -MQE>9.<97F3F07[LYD%^[.4C?U#E(W]0Y%=_V.17?]CFCS_8YH\_V.?H][CGZ -M/>XYJ:;#.:FFPSF&DY`YAI.0.8P-.SF,#3LY50$(.54!"#DW]:)@Y'FB8.8#GCSF`YX\Y<]Z/.7/>CSD! -MYVPY`>=L.3KM6SDZ[5LYE?5*.97U2CG-Z$HYS>A*.5NK;#E;JVPY;1N8.6T; -MF#GKW+DYZ]RY.7!#PCEP0\(YR%.Q.)1;#GB46PY5J(Y -M.5:B.3G$=4HYQ'5*.28E;#DF)6PYI%:/.:16CSF$EZ@YA)>H.>@6CGGH%HYEO(X.9;R.#DVA5HY-H5:.9(3 -M?#F2$WPY6S67.5LUESF_]ZG.:2'GSFDAY\YK;...:VSCCGH^FHYZ/IJ -M.?_K:CG_ZVHY,36&.3$UACD+59\Y"U6?.;1QN#FT<;@YM\?`.;?'P#EWN\`Y -M=[O`.>7MKSGE[:\Y4L*6.5+"ECDD68XY)%F..>HC>SGJ(WLYT)E9.="963GY -M5R+2CE#O9XY0[V>.:]GKSFO9Z\YPP*G.<,"ISE*DX4Y2I.%.7;M)CEV[28Y7T-( -M.%]#2#BZ>86WNGF%MS1QA;@T<86X!AU(N`8=2+@J8`6X*F`%N*17!3BD5P4X -MYJ*F..:BICA`F"8Y0)@F.674>3EEU'DY2]:N.4O6KCF]<+\YO7"_.<:VQSG& -MML -MG;FFWIVY;#>-N6PWC;F;B&BYFXAHNH2X%WJ$N))Q!+F2<02YDYU&N9.=1KG^ -M-'BY_C1XN8(HG;F"*)VY0S*^N4,ROKGBKLZYXJ[.N:I=QKFJ7<:Y9T.EN6=# -MI;D:1$:Y&D1&N1HNI;@:+J6X7!R$-UPH,M+GJ#+2Y`(W,N0"-S+F> -M4<2YGE'$N4T7O+E-%[RY#-ZSN0S>L[G=I:NYW:6KN=X6D[G>%I.Y(Q9UN2,6 -M=;GSK0*Y\ZT"N6ZE`KANI0*XZ9P"..F<`CACE((X8Y2".-Z+`CC>BP(X6(." -MN%B#@KB(&2.YB!DCN7*9BKERF8JYURJKN=T1N;'M$;D;Y!&Y&^01NK -M,;E05!&Y4%01N?LE@;C[)8&X,*Q!.#"L03B.-Q$YCC<1.;K583FZU6$Y3>=Q -M.4WG<3GGMV$YY[=A.9$O(3F1+R$Y5>H`.57J`#E#&J$X0QJA.$K9@#A*V8`X -M```````````_R("W/\B`MZCOH+BH[Z"XSA+!N,X2P;BR,>&XLC'AN#[Y0+@^ -M^4"X``````````"NW\`XKM_`.)F,`#F9C``Y&:4@.1FE(#D`BQ`Y`(L0.6J! -M$#EJ@1`YA&H`.81J`#D]GU`Y/9]0.9.<8#F3G&`Y$EN0.1);D#GU3(@Y]4R( -M.>9'D#GF1Y`Y`5I0.0%:4#F5.B`YE3H@.>XOH#CN+Z`XO"Q`.+PL0#@````` -M`````,@,@+?(#("W9`9`N&0&0+B<^3^XG/D_N'#F_[=PYO^W9=7_-V75_S=: -MQ/\X6L3_.!JX;SD:N&\Y6X9?SGN&7\Y.3=/ -M.3DW3SEJ:P\Y:FL/.4!0GSA`4)\X4B`_.%(@/SBXQ/XWN,3^-ZVS?CBMLWXX -M3L[>.$[.WCC_&A\Y_QH?.7+(3CERR$XYDJ%>.9*A7CF0>&XYD'AN.95H;CF5 -M:&XY%!"'.100ASEA^(XY8?B...5P;7CG$Z&TYQ.AM.:;'ACFFQX8YL*N..;"KCCF*M88YBK6& -M.;[X33F^^$TYX&_]..!O_3C57OTWU5[]-\I-_;?*3?VWC^T]N(_M/;BT*WVW -MM"M]MZD:_3>I&OTW:VC=.&MHW3A<&QXY7!L>.9A*73F82ETY\U&&.?-1ACEH -M^YTY:/N=.0B\K3D(O*TYAGJ].89ZO3DLB;4Y+(FU.U^CPXM?H\..?G>SCGYWLX)>&\."7AO#@73=PX%TW<./Q0'3G\4!TY -M1"]<.40O7#GNI84Y[J6%.?I4E3GZ5)4Y82:=.6$FG3G!98TYP66-.85.>SF% -M3GLYR]5;.C@DM7HXT5`L.=%0+#E1QY0Y4<>4 -M.9.UPSF3M<,YRGO+.>E#G7GI0Y8JX[.6*N.SF:H;LX -MFJ&[.-*4.SC2E#LX``````````"M^7FWK?EYMP``````````LF$[.+)A.SB- -MQODXC<;Y."H::CDJ&FHY$J&K.1*AJSG_8-HY_V#:.3OJZ3D[ZNDY+$/:.2Q# -MVCDV/;,Y-CVS.4%/>3E!3WDYQ5HK.<5:*SG@X;HXX.&Z."`<>3@@''DX4,@Z -M.%#(.CB(NSHXB+LZ.,"N.CC`KCHX]==X./77>#A2?)LX4GR;.-^U^#C?M?@X -M[05*.>T%2CDBTXLY(M.+.>/9JCGCV:HYU1C".=48PCE_2+HY?TBZ.;VVJCF] -MMJHY*&63.2AEDSDQV(,Y,=B#.7X<>#E^''@Y#D;^U@Y&_M8.7!P -M*CEP<"HY2-W8.$C=V#B-W)HXC=R:.*[(.3BNR#DX/\>:.#_'FC@HE/>9.)'GF3A$+G8W1"YV-SH==KZE=3EGT)$Y9]"1.>(BN#GB(K@YW1C/.=T8SSE\"L\Y?`K/.8K\MSF* -M_+1YUCCD>=8XC`SU-XP,]3>AO#>XH;PWN"A- -MUK@H3=:XY`<9N>0'&;G#22BYPTDHN2&G";DAIPFY3*;TMTRF]+?Q;[RKSG+,LXYRS+..6HDSCEJ),XY"(ZO.0B.KSEP6HDY -M<%J).7@6-SEX%CTM_.WJ:;SMZFF\[<``````````&^C-CAOHS8X&`75.!@% -MU3A^8O,X?F+S.$7GU#A%Y]0X3G"V.$YPMCA>+W,X7B]S.%,>-^5#EG0T4Y9T-%.0NS%SD+LQ[+_(WNR_R-[`>[K\#CNZ_`X*J2T."JDM#@G -M?I8X)WZ6.,VX<#C-N'`XPJ=P.,*G<#@*<;0X"G&T.*V%\#BMA?`X+U`E.2]0 -M)3GK4$,YZU!#.>D]-#GI/30Y"2TE.0DM)3EW,/`X=S#P.)$7M#B1%[0XE0S2 -M.)4,TCA6_A4Y5OX5.8?M8#F'[6`Y+FJ..2YJCCEBWI4Y8MZ5.;O3E3F[TY4Y -MDLZ&.9+.ACF'D'XYAY!^.0N&;SD+AF\Y4&Q^.5!L?CGU8V\Y]6-O.;M=8#F[ -M76`Y9W$S.6=Q,SF%?A4YA7X5.LYG#F\+IPYO"Z<.8TCG#F-(YPY71B<.5T8G#DN -M#9PY+@V<.9*XA3F2N(4YW*AM.=RH;3G7Y$\YU^1/.5JN7CE:KEXY%TU\.1=- -M?#GN7I0Y[EZ4.1^4JCD?E*HY"5RY.0E@_M -M.7H/[3D?3P4Z'T\%.E\4%#I?%!0Z?MF -M.I.&#SJ3A@\ZR<0`.LG$`#K#&_HYPQOZ.:<)^CFG"?HY<9WR.7&=\CFYV.,Y -MN=CC.2,6U3DC%M4Y+Z[-.2^NS3D_]]0Y/_?4.:^6XSFOEN,YT,<'.M#'!SKG -M%1HZYQ4:.E1A+#I482PZQU0W.L=4-SH^G#XZ/IP^.H4X0CJ%.$(Z0]1%.D/4 -M13I+'$(Z2QQ".HB[.CJ(NSHZQ[(O.L>R+SH*6ATZ"EH=.O8#"SKV`PLZ$0$` -M.A$!`#I^[_\Y?N__.>$]!SKA/0)*.FGB2CKH -M$5(ZZ!%2.DC?7#I(WUPZ73!9.ETP63J3XU$ZD^-1.K;Y1CJV^48Z4:\_.E&O -M/SJ8`SPZF`,\.K_U.SJ_]3LZTTHX.M-*.#H_/3@Z/STX.G^]*3I_O2DZM!0F -M.K04)CIR;"(Z&3IB?P?Q.4WG\3E1)`HYB_K;.8OZVSG6H<8YUJ'&.=MC -MN#G;8[@Y`VV_.0-MOSD9=<8Y&77&.3!FQCDP9L8YWT&_.=]!OSD#X*(Y`^"B -M.2?+XY6D^P.5I/L#EE&YLY91N;.:'JA3FAZH4Y('IA.2!Z -M83GS.T4Y\SM%.8@7-SF(%S13F5(E,YE2)3.9L24SF;$E,Y)>`V -M.27@-CG%H0PYQ:$,.`XI\3$.*?$Q#BJZ?PXJNG\./^.*#G_CB@Y -M>+AN.7BX;CG<88PYW&&,.9);DSF26Y,YCTR,.8],C#F]=;CGO -M76XY74=@.5U'8#DC+#8Y(RPV.:$9&CFA&1HYM1'$.+41Q#AF`B@X9@(H."?R -M7[%L"SD"Y/HX`N3Z.(7RWCB%\MXX2P7#.$L%PSA3 -M'*.#/I)C@SZ28X.7M>-SE[ -M7C-R197C.!E(7C@.-]XX#C?>.,+7"CG"UPHY:ZX8 -M.6NN&#ETP@HY=,(*.88TPCB&-,(X8FDF.&)I)CC-T-TWS=#=-\*_W3?"O]TW -M,XV*.#.-BCA!-J8X03:F.'DIICAY*:8XL1RF.+$.9DOGCG)P[(YR<.R.536JSE4UJLYG&V).9QMB3EP -M%$XY; -M6EHWD$E:MY!)6K=DJB.X9*HCN)R=([B!GUBX@9]8N-EJHKC9:J*X&8WSN!F- -M\[AA;-BX86S8N%9;V+A66]BX2TI8N$M*6+A`.=@W0#G8-R$9!SDA&0X$(Z\N!".O+CWHP:Y]Z,&N0P/%+D, -M#Q2Y5`,4N50#%+F>.=>XGCG7N&Y>(;AN7B&XB!=7.(@75SA-Y_$X3>?Q.&VG -M+CEMIRXYVP<\.=L'/#GR^#LY\O@[.7,Y!CES.08Y2+'6.$BQUC@N^*`X+OB@ -M.#*/5C@RCU8X)W[6-R=^UC<<;=:W'&W6MXOYA;B+^86X!TO6N`=+UKC\.=:X -M_#G6N+6>H+BUGJ"X[9$@N.V1(+C;!M8WVP;6-UQXH#A<>*`X7J'P.%ZA\#A5 -MI`4Y5:0%.09[\#@&>_`X84XY!X@..0> -M(#AZ;56W>FU5MV]<5;AO7%6X'T^%N!]/A;AX1(6X>$2%N$\IU;=/*=6W,](? -M.#/2'S@Y!]4X.0?5.*.X'SFCN!\Y+?HL.2WZ+#D2GQ\Y$I\?.0[#U#@.P]0X -M`[+4.`.RU#C9#+HXV0RZ./)KGSCR:Y\X3L^$.$[/A#C8;50XV&U4.,U<5#?- -M7%0WPDO4M\)+U+<)+)^X"2R?N'>DN;AWI+FX>1*?N'D2G[BQ!1^XL04?N(SV -M4[>,]E.W@>53.('E4SA9WYXX6=^>.-D[[CC9.^XX?$\$.7Q/!#GK?A$YZWX1 -M.3BL'CDXK!XY<)\>.7"?'CDU;M,X-6[3."M=4S@K75,X($Q3MR!,4[?M!(2X -M[02$N,G$N+C)Q+BXP%*>N,!2GKCU!].W]0?3M^KVTC?J]M(WW^52.-_E4C@% -MQ8,X!<6#.,G#4CC)PU(XOK+2-[ZRTC>TH5(WM*%2-P``````````GG]2-YY_ -M4C>3;M(WDV[2-R;&'3@FQATX7KD=.%ZY'3AS.U(X-.9VXC3F=N`>1T;@'D=&X_'_1N/Q_T;@30;>X$T&WN&T&G;AM -M!IVXI/DH4".87$T#B%Q-`XFX:<.)N&G#B%98(XA66" -M.-]:@CC?6H(X.%"".#A0@CB118(XD46".+-&'#BS1APX.4W0MSE-T+-\*#GC?"@YY&.U..1CM3B,/,\WC#S/-X$K3[B!*T^XV5.; -MN-E3F[AK"<^X:PG/N%49M;A5&;6X:PJUN&L*M;B"^[2X@ONTN$#%SKA`QZFC@2YH`X$N:`.-\K3CC?*TXXU!I.-]0:3C<``````````(]Z -M&KB/>AJXM.=-N+3G3;C_8)JX_V":N%)^Y[A2?N>XN"(GN;@B)[D7[S.Y%^\S -MN2[@,[DNX#.Y_T@-N?](#;EH<,VX:'#-N%U?3;A=7TVXOOH9N+[Z&;A'/4VW -M1SU-MSTLS;<]+,VW,AO-MS(;S;>=QQFXG<<9N-6Z&;C5NAFX$>C,MQ'HS+<& -MU\RW!M?,MWOW?[A[]W^X4QZSN%,>L[C@S/^XX,S_N)*W_[B2M_^X"A+FN`H2 -MYKB45)FXE%29N+M?3+B[7TRX!#L9N`0[&;@\+AFX/"X9N)HL3+B:+$RXCQM, -MN(\;3+@F#7^X)@U_N-CW?KC8]WZX4^Z8N%/NF+ADU\NX9-?+N/"W_KCPM_ZX -M^OD^N?KY/KF(7EBYB%Y8N=2^<;G4OG&Y=/)DN73R9+GM2R6Y[4LEN17TL;@5 -M]+&X#D]+MPY/2[>";A@X@FX8.+IA&#BZ81@X[1M+-^T;2S?B"DNWX@I+M]CY -M2KC8^4JXFBZ8N)HNF+BZ,N2XNC+DN'7!)+EUP22Y4@H^N5(*/KFBI$JYHJ1* -MN9>32KF7DTJY$HHDN1**)+FQO^.XL;_CN'9@2KAV8$JX:T_*MVM/RK=A/DJW -M83Y*MP"B%[@`HA>X.)67N#B5E[C8R;"XV,FPN#7ZR;@U^LFX!:RPN`6LL+@8 -M8A>X&&(7N!7'R;<5Q\FW"K9)MPJV2;?_I$FW_Z1)M_23R;?TD\FW+R(7N"\B -M%[C?<^/DFWOCY)MT?B%KA'XA:X?]66 -MN'_5EK@JZJ^X*NJON$';K[A!VZ^X)J^6N":OEKB=#GNXG0Y[N$_Y>KA/^7JX -M`N1ZN`+D>K@&?):X!GR6N,B!K[C(@:^X=6*6N'5BEKBM51:XK546N.5(%CCE -M2!8X+%KA."Q:X3@Y-R\Y.33@``````````-S5E;C;E'A"ZY1X0NN?,Y>;CS.7FXIB1Y.*8D>3B+5RXY -MBU#D+^G@Y->2(.37DB#EPSW@Y<,]X.0-K4SD#:U,YLR\5.;,O%3DY -MV<8X.=G&."[(1C@NR$8X``````````"2_)2XDOR4N*]GW[BO9]^XPGH(N<)Z -M"+FV#_BXM@_XN.UAQKCM8<:XJKP4N*J\%+C8/T8WV#]&-X"Z=SB`NGSDY_7LY.14E4CD5)5(Y -M"5PY.0E<.3G9/!0YV3P4.6N5Q3AKE<4X>>5V.'GE=CB`%A0X@!84.$MB13A+ -M8D4X\/R3./#\DS@\Z-TX/.C=.&V0!SEMD`6K.%-[]3A3>_4X4((?.5""'SF*_#1#GM2A\Y[4H?.8+[]#B"^_0X7>O#.%WKPSA2 -MVL,X4MK#.!Y0JS@>4*LX+6PSPSB*;]LXBF_;.%Y7-LXJ:;S.*FF\SAK>!3F/I\(XCZ?"../P$3CC\!$X>H5"MWJ%0K>+$7.XBQ%SN#W\ -M!3D(7@4YFRG:.)LIVCBLGZDXK)^I.(,\=QN$WG<;@`TG&X`-)QN,)C0;?"8T&W9:=Q.&6G<3@FW00Y -M)MT$.9<=-3F7'34YEQ]!.9ES\Y[J0/.>ZD#SDFF(\X)IB/.'UD/S=] -M9#\WCD!+&(Y`2QB.=MD/CG;9#XY&:0:.1FD&CD4L@XY -M%+(..3#"`CDPP@(YW*CM.-RH[3BE#[XXI0^^.)K^/3B:_CTXC^T]-X_M/3>$ -MW#VWA-P]MWK+/3=ZRSTWTTL..--+#C@X]*4X./2E.+U8`CF]6`(YV:XQ.=FN -M,3D,)54Y#"55.=J4>#G:E'@Y@12(.8$4B#F-QIDYC<:9.46BGSE%HI\YXY.? -M.>.3GSD"S(TY`LR-.:4.>#FE#G@YS+Q(.=$@Y7G1(.N5F#GKE9@Y$HB8.1*(F#F+5YXYBU>> -M.?(EI#GR):0Y2?.I.4GSJ3D?"*0Y'PBD.3;YHSDV^:,YI0^>.:4/GCDE)Y@Y -M)2>8.>/RG3GC\ITYD+VC.9"]HSF:_91[CF7D>XYG]GB -M.9_9XCD_4]$Y/U/1.1+0OSD2T+\YCA^T.8X?M#DL<:@Y+'&H.8G^LSF)_K,Y -M8O/0.6+ST#F6??DYEGWY.2L`$3HK`!$ZBG$?.HIQ'SJB+B4ZHBXE.MHY(CK: -M.2(Z0V`<.D-@'#JKHA,ZJZ(3.DBP$#I(L!`Z;;X-.FV^#3IAL0TZ8;$-.E6D -M#3I5I`TZ29<-.DF7#3H\B@TZ/(H-.II#$SJ:0Q,ZZ/L8.NC[&#K2E2$ZTI4A -M.B(N*CHB+BHZM>(O.K7B+SI7M#(Z5[0R.CG"+SHYPB\Z4Y,R.E.3,CK0@C(Z -MT((R.AU3-3H=4S4Z;@,[.FX#.SK7$3@ZUQ$X.LD@-3K)(#4Z2+$F.DBQ)CKJ -M(QLZZB,;.AQ7%3H<5Q4Z4&H2.E!J$CK'^!HZQ_@:.N,@+#KC("PZ\R-`.O,C -M0#HJ`53H6"WDZT3IS -M.M$Z;)3HU02LZ-4$K.L%//#K!3SPZ&EM-.AI;33HE/6$Z)3UA.OFT:3KY -MM&DZCP1U.H\$=3K6GWHZUI]Z.@8=@#H&'8`Z4GV!.E)]@3HT<8$Z-'&!.B?R -M?SHG\G\ZZE)W.NI2=SK(W6LZR-UK.@%"8SH!0F,ZR7Y=.LE^73H$:ETZ!&I= -M.C]573H_55TZ],)E.O3"93ICK64Z8ZUE.G%M:#IQ;6@Z/X)E.C^"93JM;&4Z -MK6QE.DB"8CI(@F(Z&A9H.AH6:#KSIL3H`Z;$Z`.DY"@#I.0H`Z`YM].@.;?3J^#W4ZO@]U.A.&;#H3AFPZDLYF -M.I+.9CK51UXZU4=>.KF26#JYDE@ZZ0Y0.ND.4#HT7$HZ-%Q*.HZJ1#J.JD0Z -MZI=$.NJ71#K,CE(ZS(Y2.I.T73J3M%TZO4)Q.KU"<3IU8WPZ=6-\.@;!@SH& -MP8,Z@K2#.H*T@SIQ08(Z<4&".I@#?#J8`WPZ>U)V.GM2=CJ^;G,ZOFYS.GZ_ -M;3I^OVTZ3Q%H.D\1:#JNF%\ZKIA?.FE65#II5E0Z2TM&.DM+1CKWHD`Z]Z)` -M.IB00#J8D$`Z94A#.F5(0SI]DTLZ?9-+.J&F5CJAIE8ZX21<.N$D7#HN:V0Z -M+FMD.N*O;#KBKVPZD;MW.I&[=SI^-'TZ?C1].D[D?SI.Y'\Z/SQZ.C\\>CJV -M/FPZMCYL.E>U6#I7M5@ZJ;U*.JF]2CKN`3HZ[@$Z.J0I-SJD*3K(R.GJR,CHKH3(Z*Z$R.IK0+SJ:T"\ZD0`M.I$`+3K*[RPZRN\L -M.@3?+#H$WRPZ/.N_H'CH:HA8Z -M&J(6.KL9$3J[&1$Z!4\..@5/#CHJ=A8Z*G86.L97(3K&5R$Z#?,N.@WS+CJ; -M%#"@LZG@H+.K2)!3JTB04ZV@D`.MH)`#HC%O4Y(Q;U.6^,[SEOC.\YRP/J -M.\YD5WO.6DF^CEI)OHYD'8".I!V`CK;V`X#SJK%A4ZJQ85.K*]%SJR -MO1\B[.?E!ICGY0:8YF'B;.9AX -MFSD_#98Y/PV6.5;^E3E6_I4Y;.^5.6SOE3F#X)4Y@^"5.9K1E3F:T94Y[1N; -M.>T;FSDP9:`Y,&6@.;M=L#F[7;`YN:K%.;FJQ3D\1=`Y/$70.3$TX#DQ-.`Y -MTQW@.=,=X#EO7-4Y;US5.2V=RCDMG+HY]GBZ.?$2M3GQ -M$K4Y<;6?.7&UGSDT7(HY-%R*.5''5#E1QU0Y`N,4.0+C%#F4W^DXE-_I.";( -MZ3@FR.DX1K84.4:V%#D*^UXY"OM>.7.8E#ESF)0Y[*NY.>RKN3E?S\XY7\_. -M.8_NXSF/[N,YJ-?C.:C7XSG5=-XYU73>.>8PR3GF,,DY7::N.5VFKCG3UHXY -MT]:..<:'4SG&AU,Y5`,4.50#%#DK7=,X*UW3.!0GOC@4)[XXX&_]..!O_3@U -MY"@Y->0H.2>573DGE5TY+R-].2\C?3E8RH,Y6,J#.0>]@SD'O8,YMZ^#.;>O -M@SGNO'PY[KQ\.5ZC?#E>HWPYS8E\.TR4CDNF3(Y+IDR.:@&*#FH!B@Y]W0R.?=T,CDEX3PY)>$\.6K(43EJR%$Y -M1"]<.40O7#G/G5$YSYU1.7L.1SE[#DC#E6<7DY -M5G%Y.+&&SE\V[HX?-NZ.&0')CAD!R8XA?'X-X7Q^#>B -M7D\XHEY/.%1)SSA42<\XGJX&.9ZN!CDNLB4Y+K(E.37[+SDU^R\Y&>DO.1GI -M+SD-?R4Y#7\E.>+$+SGBQ"\Y^%PE.?A<)3GM2R4Y[4LE.8:3$#F&DQ`YP[[W -M.,.^]SC_7LXX_U[..+JHN3BZJ+DXC96Y.(V5N3B"6/1.$X,P`N.3,`+CD`%F$Y`!9A -M.>NL?SGKK'\YK.6$.:SEA#G3UX0YT]>$.1TF=3D=)G4YUJ!@.=:@8#D(ZT$Y -M".M!.2K4#CDJU`XY-_7+.#?URSCJWTLXZM]+.```````````BMW+WHK=G -MYB*W9^8BMPM`]#<+0/0W4L2B.%+$HCBJ,00YJC$$.6#,+#E@S"PY?35+.7TU -M2SDWF&DY-YAI.9O-?3F;S7TY7O^(.5[_B#G-%9,YS163.3L8F#D[&)@YDN6- -M.9+EC3G8)G,YV"9S.1&+2CD1BTHYUK@-.=:X#3DGVO(X)]KR."E+RC@I2\HX -M1?VU.$7]M3@9ZK4X&>JU..W6M3CMUK4XP<.U.,'#M3C&0/(XQD#R.'EO(3EY -M;R$Y\P.5U'L#FY-+`YN32P.3@) -MH3DX":$Y*-F,.2C9C#FT6G$YM%IQ.?\84SG_&%,Y4/9(.5#V2#D#X4@Y`^%( -M.>754CGEU5(YA[]2.8>_4CDVL5PY-K%<.<6@9CG%H&8Y,HYP.3*.<#FB='`Y -MHG1P.4979CE&5V8Y##Q<.0P\7#E)(4@Y22%(.%'.;#!;SFPP6\YZG*D#D#LJ0Y`[*D.:6:KCFEFJXY;XNI.6^+ -MJ3DVA9HY-H6:.:R&ACFLAH8YOPYO.;\.;SEK"ULY:PM;.2L`43DK`%$YS.E0 -M.Q5HY'L5:.1"]4#D0O5`YLJ90.;*F4#E4D%`Y5)!0.=!49#G05&0Y -M"15X.0D5>#F`Z(4Y@.B%.1_:A3D?VH4Y*=>`.2G7@#E2VF,Y4MIC.=/!8SG3 -MP6,Y6\-9.5O#63F[=6TYNW5M.2M<;3DK7&TYVF:*.=IFBCGQ5XHY\5>*.;58 -MA3FU6(4YB5J`.8E:@#E:W&PY6MQL.5?E8CE7Y6(YU\QB.=?,8CD&V5@Y!ME8 -M.5;G3CE6YTXYQ_=$.(+,3F1HD0Y -MD:)$.=L(8CG;"&(Y])5U.?25=3D.37\Y#DU_.;)@=3FR8'4Y0M=7.4+75SG` -M(D0YP")$.7,-1#ES#40YC<1-.8W$33G>1&$YWD1A.:;V:CFF]FHY%=UJ.17= -M:CF%PVHYA<-J.>#B8#G@XF`Y6#Y-.5@^33D&8T,Y!F-#.9P133F<$4TY$+Y6 -M.1"^5CEB:&`Y8FA@.3*/5CDRCU8Y([A,.2.X3#DUXT(Y->-".6D0.3EI$#DY -MOC\O.;X_+SF2+"\YDBPO.689+SEF&2\Y_4PE.?U,)3F+RA$YB\H1.9&Z$3F1 -MNA$YEZH1.9>J$3F)N2XYB;DN.?]U53G_=54Y9.Z".63N@CG+D8PYRY&,.0\S -MECD/,Y8YYW*,.>=RC#G@M((YX+2".>N67CGKEEXY]]%4.??15#G"8T$YPF-! -M.2*D-SDBI#,)E3GC"94YVI6>.=J5GCFP'Z@YL!^H.0P-J#D,#:@YV<>4.=G' -ME#EBW6\Y8MUO.;2@+#FTH"PYL[F_.+.YOSA14!DX45`9.$<_F3='/YDW/"X9 -M-SPN&3"(YLG@B.6@J4CEH*E(Y -M^$1X.?A$>#F!*X\Y@2N/.;;@DSFVX),YC0N/.8T+CSDQUG7 -M.$.FESA#II:SSC, -MZI8XS.J6.#*0/#@RD#PXMLB6-[;(EC>!$^(W@1/B-_'YX3?Q^>$WEI46.):5 -M%CB+A)8WBX26-X!S%C>`#N(28SKB$F,ZX0/6H -MN$#UJ+@4XJBX%.*HN(^0.[B/D#NX3_K@MT_ZX+ -M$\,;GA/#&YGWLZN9][.KF2N<.\ -M"[G#O`NY2=O,N$G;S+B(ZY2XB.N4N+M'7[B[1U^XN,F4WKA)EQVY -M29<=N7250KETE4*Y%G]"N19_0KG7Y"^YU^0ON?L-%+G[#12Y!GOPN`9[\+C8 -MX=VXV.'=N.Q,R[CL3,NX1+RXN$2\N+CVIKBX]J:XN*B1N+BHD;BX6WRXN%M\ -MN+@-9[BX#6>XN'F=[[AYG>^XR)DY5*P('+GL"!RY -MZ'``N>AP`+FT%=RXM!72N`V7DKC"UJ2XPM:DN'.OV[AS -MK]NX[&,2N>QC$KGV,4FY]C%)N1K/=KD:SW:YOPV)N;\-B;G#CHVYPXZ-NQ[?[E//W:Y3S]VN8TB=KF-(G:Y(NELN2+I;+D(Z76Y".EU -MN3>78[DWEV.Y,S!(N3,P2+F2SRRYDL\LN55U$;E5=1&Y@F_^N()O_KBO4?ZX -MKU'^N!$N"+D1+@BY3UYE]>9N9?7F;E+2YZY2TN>N6"SF;E@LYFY3(Z'N4R. -MA[E@XW.Y8.-SN8RP6+F,L%BYL9!/N;&03[DR>$^Y,GA/N05H8;D%:&&Y?%!J -MN7Q0:KG+-&JYRS1JN2L76+DK%UBYM/U.N;3]3KFSZ#.YL^@SN2O1/+DKT3RY -MS;H\NY&&*WN4AJ -MI;E(:J6YQ?Z.N<7^CKDJ,7&Y*C%QN3Q=3;D\74VY2&L[N4AK.[E`:3*Y0&DR -MN?)3,KGR4S*YM1%$N;411+F^XDRYON),N0R97KD,F5ZY;C%YN6XQ>;FVX8FY -MMN&)N8,GE[F#)Y>YB_B?N8OXG[EI5J2Y:5:DN3/2G[DSTI^Y#D^;N0Y/F[EJ -M/)NY:CR;N<8IF[G&*9NY0F*HN4)BJ+G=NZRYW;NLN1BGK+D8IZRY_DN?N?Y+ -MG[D5])&Y%?21N>6/=[GECW>YHAA4N:(85+E[5$*Y>U1"N4812[E&$4NY&Y]< -MN1N?7+FM*&ZYK2ANN?RM?[G\K7^Y@/^,N8#_C+E=O96Y7;V5N>]&I[GO1J>Y -M>/^ON7C_K[G?M;BYW[6XN=0$O;G4!+VY[>V\N>WMO+E"JJ^Y0JJONGAA[GIX8>Y_C*,N?XRC+GDP(>YY,"'N6&P -MA[EAL(>YX#^#N>`_@[GF+X.YYB^#N=I^A[G:?H>YB8F4N8F)E+D'D:&Y!Y&A -MN:?RLKFG\K*YT=RRN='Y0".WN6E5KKEI5:ZY@"ZAN8`NH;D@P)RY -M(,":;&Y(UJDN2-:I+G;39>YVTV7N>N6CKGKEHZYNC.* -MN;HSBKFO(HJYKR**N1?!A;D7P86YD&"!N9!@@;F64(&YEE"!N9"/A;F0CX6Y -MY1N.N>4;CKG\\YJY_/.:N3P6K+D\%JRY9^>XN6?GN+G+'+VYRQR]N5P%O;E< -M!;VYLZ*XN;.BN+G-B[BYS8NXN;TJM+F]*K2YOLJON;[*K[F?V:*YG]FBN;+K -ME;FRZY6YZ'"`N>AP@+GA]E6YX?95N8BD,[F(I#.Y*HXSN2J.,[EECT2Y98]$ -MN4ZW=[E.MW>Y8B21N6(DD;G)9Z:YR6>FN5,>L[E3'K.Y,4NWN3%+M[F6\;*Y -MEO&RN0R9KKD,F:ZY2[ZAN4N^H;G8)YFYV">9N?-2C+GS4HRY?P)_N7\"?[E] -M966Y?65ENYQ120N<44 -MD+E9QHNY6<:+N38]@[DV/8.Y:W]DN6M_9+E=`DNY70)+N;.+,;FSBS&Y"`(I -MN0@"*;GW7C&Y]UXQN<2Y.;G$N3FYI_)2N:?R4KD;MF.Y&[9CN4QU=+E,=72Y -M'IB"N1Z8@KET\XJY=/.*N:A,D[FH3).Y!#J3N00ZD[D-\XZY#?..N8TC?+F- -M(WRY_P)2N?\"4KFDAQ^YI(X1VVGN/E7I[CY5Z>XBORWN(K\M[A1#.JX40SJ -MN'5B%KEU8A:Y^@]`N?H/0+DL6F&Y+%IAN3Q$>KD\1'JY2"1ZN4@D>KE?66FY -M7UEIN;F26+FYDEBYI"DWN:0I-[G,=@VYS'8-N<;ZMKC&^K:X"D,FN`I#)K@` -M`````````&\8)CAO&"8XR&IH.,AJ:#C3[24XT^TE.#BM!+X&&+7N%H)%;E:"16YX')+;D";B6Y`FXEN=;/%+G6SQ2Y75[G -MN%U>Y[A2-\:X4C?&N%&6E+A1EI2X,@1&N#($1KC!\8.WP?&#M[;@`S>VX`,W -M@;?%-X&WQ3=)KB0X2:XD.):M`SB6K0,XBYR#-XN<@S>`BP.W@(L#MW5Z@[=U -M>H.W:FF#MVII@[>/!,6WCP3%M__JQ+?_ZL2W2C:#MTHV@[<_)8.W/R6#MS04 -M@[BP*WWHL"M]-Z`KC3>@*XR&D"N,AI`K@S1P*WJ#8"-Z@V`C=K.,,W:SC#-Y(4`CB2%`(X:80B.&F$(CB[ -MZT(XN^M".*`=DCB@'9(X=`J2.'0*DCA']Y$X1_>1.`[Q8C@.\6(XF`0B.)@$ -M(C@\C($W/(R!-P``````````)FJ!MR9J@;<``````````!!(`3<02`$WB-+! -M-XC2P3?XN,$W^+C!-VB?P3=HG\$WUX7!-]>%P3=';,$W1VS!-[=2P3>W4L$W -M]@0A./8$(3B7'T$XEQ]!.+(Q83BR,6$XWQ-A.-\383@,]F`X#/9@.#G88#@Y -MV&`XU'>0.-1WD#C7;Z`XUV^@.)=CL#B78[`X*$RP."A,L#CN+Z`X[B^@.$@E -M8#A()6`X4P4@.%,%(#APYO\W<.;_-UK$_S=:Q/\WL[D_.+.Y/S@O@'\X+X!_ -M..ZDCSCNI(\X!#Q_.`0\?SAR4S\X1_C:7D?XV@F]^-X)O?C=D\!XX9/`>..QE7CCL95XXR<6>.,G%GCC3 -M2\XXTTO..!;%_3@6Q?TXL*L..;"K#CF+A!8YBX06.6&:!CEAF@8YC^V].(_M -MO3C_TSTX_],].```````````#P8>N`\&'KA.ASVX3H<]N'3;';ATVQVX/7!\ -MMSUP?+<```````````TAO3<-(;TW_`G\-_P)_#?M[3PX[>T\.!=-7#@735PX -MD0"M.)$`K3A76C@JNSLX*KL[.)JA -MNS>:H;LWN`KZMK@*^K9E,1RX93$3A60ZLX5D.K.&IJZ3AJ:NDX]=?X./77^#A.Y@LY -M3N8+.2+3"SDBTPLY];\+.?6_"SFERN@XI -MZ?W'8/WM\6(N;?%B+FW-6^YMS5O -MN;<``````````,;Z]K;&^O:V``````````";MG8WF[9V-],<&CC3'!HX<')V -M.'!R=C@^5ZDX/E>I.,\_J3C//ZDXG<>9.)W'F3C6+%" -M^W2W;-ETMVS9=+=6M_2V5K?TM@``````````*W-T-RMS=#?0/+ISM[_(\[>_R/.WJ:;SMZFF\[>4A'.WE(1S -MMP``````````:$!S-VA`^5K8WOE:V-SW\0 -MZ_&WD.OQMQQ7-;@<5S6X9:=QN&6G<;C]VH>X_=J'N#EC<;@Y8W&X)$%QN"1! -M<;@L^U*X+/M2N/G\<+CY_'"XA[]2N(>_4KB:BC2XFHHTN`IQM+<*<;2WHG3P -MMJ)T\+:,4G`WC%)P-UDDM#=9)+0W80YP-V$.<#<``````````#;*[[N'%NWKAY9A@IN688 -M*;G231JYTDT:N7O?$KE[WQ*YYXKYN.>*^;A0#]RX4`_I,FJW``````````#>Y3H<]N4Z'/;F]_4NYO?U+N>DG4[GI -M)U.Y!0E3N04)4[E$I$NY1*1+N3QS+KD\W.>_GMS;@$+@VX!"X.%""N#A0@K@K+I^X*RZ? -MN'7PV+AU\-BXS43GN,U$Y[B'`P*YAP,"N6`H";E@*`FY#((7N0R"%[F2H1ZY -MDJ$>NN;1R'KGB]0^YXO4/N5)]`;E2?0&Y*W/TN"MS]+CU -M[^6X]>_EN,Y#`;G.0P&Y7HL/N5Z+#[E>HA:Y7J(6N<-@#[G#8`^Y'?<`N1WW -M`+ES(^6X^N;@'OKFXYNG' -MN.;IQ[BDAKFXI(:YN$"NQ[A`KL>X04^YN$%/N;BEL=6XI;'5N#/\ON7C_+[FOVRBYK]LHN=NI$[G;J1.Y<84%N7&%!;E^UORX -M?M;\N":P_+@FL/RX)H+NN":"[KAH6."X:%C@N(@OQ+B(+\2X+@^HN"X/J+A9 -M]XNX6?>+N`OBB[@+XHNXO.?"N*7)PKBER<*XAI/0 -MN(:3T+AMJ+2X;:BTN-K%F+C:Q9BXNH\FN+J/)K@J=J:W*G:FM\W0W;;-T-VV -MMZ[=MK>NW;:BC%VWHHQ=MYAB"KB88@JX2DT*N$I-"KC)W"6XR=PEN,)C0;C" -M8T&X[T5!N.]%0;@4^`FX%/@)N(AV);B(=B6X^%REM_AH@DXD(V).)"-B3@J=<`X*G7`.(G1VSB)T=LX?/3-.'STS3C<8K(XW&*R -M.,'9ECC!V98X,DE;.#))6S@YPC\X.<(_.&:D/SAFI#\X3S]V.$\_=CCW&'8X -M]QAV.#RCB#@\HX@X[HV(..Z-B#A*'I8X2AZ6.&2JHSADJJ,X"'7,.`AUS#A: -M]-DX6O39.(P,]3B,#/4XG$`!.9Q``3E`Q0XY0,4..:)%'#FB11PY8%8W.6!6 -M-SE:E4LY6I5+.1Z69CD>EF8Y!3EM.04Y;3FP368YL$UF.8D52SF)%4LYY^4O -M.>?E+SGP^PTY\/L-.6A`\SAH0/,XG9CE.)V8Y3B%.0`YA3D`.7BB#3EXH@TY -M*`<;.2@'&SF69R@YEF0%C3F/2I`YCTJ0.2J.DSDJCI,YRL*,.)\Y);&B.26QHCD-EZ(Y#9>B.=_-I3G?S:4Y`U2L.0-4K#G@ -M)[8YX">V.3FIO#DYJ;PY<2C#.7$HPSD%"<,Y!0G#.9GIPCF9Z<(Y_7R_.?U\ -MOSEQ$;PY<1&\.1;SNSD6\[LY4B"_.5(@OSE?MKLY7[:[.7U-N#E]3;@YK9NQ -M.:V;L3G_ZZHY_^NJ.4W0JCE-T*HY`?VM.0']K3E?N+Z?TY8,K@.6#*X#DVX=8Y-N'6.2X\ -MT#DN/-`YL-G,.;#9S#D#^,\Y`_C/.445TSE%%=,YI_+2.:?RTCE0#M8Y4`[6 -M.>@HV3GH*-DY<$+<.7!"W#F5E^(YE9?B.0J?\CD*G_(YEE`!.I90`3IRL0.5Y%U#E>1=0Y9>O0.67KT#G'R-`YQ\C0.:T1USFM$=]X$.GO>!#H%E0DZ!94).F87"SIF%PLZ,34& -M.C$U!CHK1OPY*T;\.4.4[.WCF@!>4YH`7E.=$ZZSG1.NLY')OT.1R;]#F"R_HY@LOZ.?D2`CKY -M$@(ZQB@%.L8H!3KVIP8Z]J<&.ITF"#J=)@@ZIU`#.J=0`SJ4^/PYE/C\.1,J -M\#D3*O`Y1HCF.4:(YCFJZ=PYJNG<.0*=V3D"G=DY&Y_<.1N?W#DCH-\Y(Z#? -M.8Q4W#F,5-PY1"_<.40OW#FS+M\YLR[?.1(MXCD2+>(Y!D[H.09.Z#D4L_0Y -M%+/T.4KQ_3E*\?TY(`4".B`%`CH&[P$Z!N\!.FY(`#IN2``ZU`/W.=0#]SG5 -MN?,YU;GS.2$RZCDA,NHY5.OF.53KYCF@2MHYH$K:.<3ITSG$Z=,YQVW*.<=M -MRCEO+L4Y>N'>.7KAWCE$B]@Y1(O8.?UEV#G]9=@Y -MDU?;.9-7VSEN7N$Y;E[A.5M-Y#E;3>0Y\B7D.?(EY#F)_N,YB?[C.>S"X#GL -MPN`Y7XC=.5^(W3DJ=>`Y*G7@.:X[W3FN.]TY0P/:.4,#VCG3J-`YTZC0.951 -MQSF54<L3G$7+5*TCGE2M(Y,DG;.3))VSE-1.0Y -M343D.?HFYSGZ)N>20.9?7F3F7UYDY-CG8/7HYJ@*`.:H"@#E7Y8(Y5^6".?/& -MA3GSQH4Y_*Z%.?RNA3G_CH@Y_XZ(.?%MBSGQ;8LYO4*1.;U"D3FD*)$YI"B1 -M.;,8CCFS&(XY@12(.8$4B#G`47@YP%%X.0.R5#D#LE0Y7-5".5S50CD:_S`Y -M&O\P.73$-CETQ#8YZ6M".>EK0CD=#TXY'0]..?'+4SGQRU,YF:53.9FE4SE& -MGTTY1I]-.?YY33G^>4TY!)E!.02903D5F3LY%9D[.?]V.SG_=CLYFY\O.9N? -M+SD0IBDY$*8I.::N(SFFKB,YTY`C.=.0(SG*'R\YRA\O.7VJ.CE]JCHY[C!& -M.>XP1CGJWTLYZM]+.5X60#E>%D`Y%5$T.151-#D/D"@Y#Y`H.5P$%SE+U -M#SGB]0\Y*AH*.2H:"CF9``HYF0`*.1,H!#D3*`0Y7HL/.5Z+#SEFZAHY9NH: -M.>\`+#GO`"PY8%8W.6!6-SFH>C$YJ'HQ.1*A*SD2H2LY&Q(@.1L2(#EHAQ0Y -M:(<4.?<`"3GW``DY&#,#.1@S`SFTSOHXM,[Z.-:?^CC6G_HXA:SC.(6LXS@" -MXNXX`N+N.$:U[CA&M>XX&Z`".1N@`CEU-`@Y=30(.4(>&3E"'ADY?P$9.7\! -M&3EECAXY98X>.?O'&#G[QQ@YL@,3.;(#$SD`Z!(Y`.@2.;1Q&#FT<1@Y1_D= -M.4?Y'3G\(2DY_"$I.3ND+CD[I"XY-H,N.3:#+CG[@1TY^X$=.2?%%SDGQ1&!!CGJ:?8XZFGV.-()ZSC2 -M">LXXW[4..-^U#AY_+TX>?R].&NMLCAKK;(X"[2].`NTO3AHML@X:+;(.&?] -MZ3AG_>DXIPH`.:<*`#EOHA`Y;Z(0.H1<..PUA#CL-80X+!0YWBP4.=(''SG2!Q\Y3EDO.4Y9 -M+SGRL#0Y\K`T.2,5+SDC%2\YW`,D.=P#)#G)"0XYR0D..7Z\'#D*GAPY"IX<.2VT$3DMM!$Y)FH!.29J`3D&3>(X!DWB.(C2 -MP3B(TL$X1R>L.$LX\6WK.'WP]3A]\/4X-A#K.#80ZSB\+,`X -MO"S`.%,%H#A3!:`XG8-J.)V#:C@Y!U4X.0=5.)[<5#B>W%0X!#Q_.`0\?SC0 -M)(HXT"2*.)I%GSB:19\XWEVT.-Y=M#B<;CTXAGH]."Y4/3@N5#TXUBT].-8M/3A2"%(X4@A2.+?=43BWW5$X -MS;H\.,VZ/#B!B%$X@8A1.'L]>SA[/7LX>.:<.'CFG#C)K;$XR:VQ.)1LQCB4 -M;,8X&D3&.!I$QCBA&\8XH1O&.-"QICC0L:8XP[V1.,.]D3AWI'DX=Z1Y.%9Q -M>3A6<7DX!WED.`=Y9#@]B4\X/8E/.*)>3SBB7D\XGWLZ.)][.CAK"4\X:PE/ -M.)0^>#B4/G@X6;&0.%FQD#B0CJ\XD(ZO.',-Q#AS#<0XC96Y.(V5N3CK(:\X -MZR&O.&-GFCAC9YHXHD9B.*)&8CCXB4TX^(E-.-35.#C4U3@X>Z\X.'NO.#@G -M"DTX)PI-.(S?3#B,WTPX;RUA.&\M83B%\X0XA?.$.,Q'F3C,1YDX:SHCA'LZ(XRMES -M.,K94*LX'E"K.,&5YSC! -ME>`YY\W:\6?-FO%GS8````````` -M`$"!GS9`@9\V]#;'-_0VQS>W)#,XMR0S.)\WBSB?-XLXZ?B>..GXGCBNL;(X -MKK&R.%6+LCA5B[(XTWNH.--[J#B<5Z@XG%>H.'U.GCA]3IXX]/&Q./3QL3CF -MC,4XYHS%.$MBQ3A+8L4X35N[.$U;NSA2?J`DX<%H).'!:"3AS#<0W(.'ZGB#B^RZ4XOLNE.*8DN3BF)+DX -M+/RX."S\N#@97Z4X&5^E.&`2B#A@$H@X!7U\.`5]?#C1W6@XT=UH.+"J:#BP -MJF@XD'=H.)!W:#AFZ50X9NE4./L,+CC[#"XX'Y0:.!^4&C@.J^9C>,WF8WG<<9-YW'&3>(I9DVB*69 -M-G*#&3=R@QDW"A)F-PH29C<8C[\W&(^_-]=`+#C70"PX#9AX.`V8>#AU2X\X -M=4N/.!*@A3@2H(4X2-]D.$C?9#C%O4)@W``````````"$ -M#!BWA`P8MZ7?8[>EWV.WA:QCMX6L8[=#II>W0Z:7MRV$E[W&&*7MQAB -ME[<"0!>W`D`7M^P=%[?L'1>W``````````#!V9:VP=F6MJRWEK:LMY:VEI46 -MMY:5%K>`F&W52^6ME4OEK8_#98V/PV6-BKK%3)NXV5.;N-E3F[BV -M#I*XM@Z2N-?-B+C7S8BXXJV(N.*MB+AUJ)&X=:B1N/CGM;CXY[6X;Q;:N&\6 -MVKC<,_ZXW#/^N!Z7#+D>EPRY^H45N?J%%;FSZ1FYL^D9N7S%&;E\Q1FY1:$9 -MN46A&;FS=1"YLW40N0#1"[D`T0NYK5;\N*U6_+@B&.&X(ACAN(GGO+B)Y[RX -M9,&JN&3!JKCJF*JXZIBJN,59Q;C%6<6X>?KQN'GZ\;@'O1.Y![T3N0/W*;D# -M]RFY8JX[N6*N.[EQ;T2Y<6]$N3TL3;D]+$VY`G!1N0)P4;FEL56YI;%5N>(+ -M4;GB"U&Y0&A,N4!H3+F]Y3ZYO>4^N0[9-;D.V36YXO,CN>+S([EYS".Y>@GN=7H)[D^5B.Y/E8CN>\`++GO`"RY:&\GN6AO)[FQ>S2Y -ML7LTN050-+D%4#2Y2QQ*N4L<2KDQ>ENY,7I;N>.4=;GCE'6Y`C^!N0(_@;G: -M3X.YVD^#N=^@?;G?H'VY3*9TN4RF=+E1]6*Y4?5BN60%6KED!5JYNQE1N;L9 -M4;F:YE"YFN90N8P,5;F,#%6Y7XA=N5^(7;D-4EVY#5)=N7O'9;E[QV6Y!X]E -MN0>/9;D2_FVY$OYMN2L6(BYH7B(N=<3 -MA+G7$X2YN"![N;@@>[D!GV6Y`9]EN71G5+ET9U2YW79'N=UV1[G-1$>YS41' -MN0Y/2[D.3TNY*T1@N2M$8+ES]'"Y<_1PN<@CA[G((X>YU8V1N=6-D;F1NY>Y -MD;N7N2ZPF;DNL)FYNZ.;N;NCF[E2?)NY4GR;N>E4F[GI5)NY@"V;N8`MF[E_ -MU9:Y?]66N=IGD+G:9Y"Y9_V)N6?]B;DGEH.Y)Y:#N8>0?KF'D'ZYC4]^N8U/ -M?KF4#GZYE`Y^N?(/@[GR#X.Y;@*%N6X"A;E;!XFY6P>)N2`=C[D@'8^Y(T*7 -MN2-"E[G\4)VY_%"=N:)>)D[GG -MB9.YM8>9N;6'F;G,=IVYS':=N<98G[G&6)^Y.R^?N3LOG[F(&:.YB!FCN5#X -MI+E0^*2Y!]:FN0?6IKE;JJ:Y6ZJFN>5VI+GE=J2Y/SV@N3\]H+FZ!9RYN@6< -MN5;0E[E6T)>YO**5N;RBE;E3>Y6Y4WN5N1I=F;D:79FYKSB;N:\XF[DU$YVY -M-1.=N:GLGKFI[)ZYA,>BN83'HKD]H*:Y/:"FN3IXK+DZ>*RY!DVRN09-LKE* -M'K:Y2AZVN6WNK[EM[J^Y!L&KN0;!J[E(F*.Y2)BCNN2KJG[DJZI^YJ*FEN:BII;E$=:>Y1'6GN<\_J;G//ZFY -MN3*\GKD/D9ZY#Y&>N2=/HKDG -M3Z*Y\R*BN?,BHKF_]J&YO_:AN8;7G[F&UY^Y7KF=N5ZYG;E?N)>Y7[B7N6B= -ME;EHG96Y@X.3N8.#D[FN:I&YKFJ1N;U"D;F]0I&YS!J1NPN;2@K+FTH*RY2+&FN4BQIKD$KZ*Y!*^BN5&8H+E1F*"YKH*>N:Z"GKG8 -M/J"YV#Z@N1L2H+D;$J"Y7^6?N5_EG[D9ZYNY&>N;N?3RE[GT\I>Y/!>2N3P7 -MDKGB(XZYXB..N?#[C;GP^XVY&KB/N1JXC[DTN2J\H;DJO*&Y`A*IN0(2J;ETPJJY=,*JN:]1KKFO4:ZY -MAV&JN8=AJKGPM:*Y\+6BN1Y2E[D>4I>Y';"/N1VPC[F/[XFYC^^)N=@.AKG8 -M#H:Y8@R$N6(,A+DHPH6Y*,*%N<^;A;G/FX6Y=W6%N7=UA;D?3X6Y'T^%N22U -MBKDDM8JY"S^.N0L_CKF8=Y6YF'>5N0C4FKD(U)JY12V@N44MH+GO_I^Y[_Z? -MN9K0G[F:T)^YBLR=N8K,G;F+R9NYB\F;N4>Y -MC)J7N4>;E;E'FY6Y>R6.N7LECKGQLX:Y\;.&N9GI@KF9Z8*YQ4)^N<5"?KF" -MS("Y@LR`N6]&A+EO1H2YD(V)N9"-B;FR`HVYL@*-N?A#DKGX0Y*Y3[25N4^T -ME;F$(IFYA"*9N>S!FKGLP9JY9RR>N6KE+G85HVYV%:-N5;0A[E6T(>YW*>'N=RGA[GYMH6Y^;:%N>E6A[GI5H>Y -MR/6(NZ.CKFY'8FYN1V)N31Q -MA;DT<86YW`6`N=P%@+GVO7^Y]KU_N6SP>[EL\'NYEE"!N990@;D`Z(*Y`.B" -MN:4\AKFE/(:YHQ.&N:,3AKE1(HNY42*+N6FTC+EIM(RY;T6.N6]%CKDB7HRY -M(EZ,N>5WBKGE=XJY=:B!N76H@;G#,'6YPS!UN2$99[DA&6>Y]7MY:&]GN?HF9[GZ)F>Y97EC -MN65Y8[G9:5RYV6E5;F17E6YE?5*N97U2KGB\T.YXO-#N1NV0[D;MD.Y -M5'A#N51X0[G:W#^YVMP_N=_F.+G?YCBY)O4QN2;U,;FP!RNYL`YW)[GN -M=R>YK$(GN:Q")[FY9"JYN60JN:6$+;FEA"VY0DTMN4)-+;FE;2:YI6TFN4J2 -M'[E*DA^Y+@TN/2KMKCTJ[:X0-R5N$#SOON'L[ -M[[B'8`2YAV`$N=2G"KG4IPJYD+@.PU2X#L-4N)/[+KB3^RZXJ$T)N*A-";CN8."W[F#@ -MMZ%(KK>A2*ZWLR^5M[,OE;>?3WBWGT]XMVCZ=[=H^G>W49:4MU&6E+>7V<6W -ME]G%M\;Z]K?&^O:W!%(@N`12(+AR:RRX/I\*WCZ?"M_@6JK?X%JJW4MNIMU+;J;>LGZFWK)^IM^*6P;?BEL&WMU+! -MM[=2P;>Z[*BWNNRHM_G\<+?Y_'"W-89`MS6&0+<*0D"V"D)`M@`````````` -M```````````FF`^W)I@/MUTQ/[==,3^WY3&/M^4QC[?%_HZWQ?Z.MT"8IK=` -MF*:WA)B.MX28CK?T(*:W]""FMUF8O;=9F+VW.:GLMSFI[+<#5.RW`U3LMU)E -MU+=29=2WM_:DM[?VI+>!0SRW@4,\MU;_N[96_[NV*KL[-BJ[.S;_=KLV_W:[ -M-M0R.S;4,CLV``````````!]JCJV?:HZMKW,"[>]S`NWL*IHM["J:+?[W;FW -M^]VYMT0`Z+=$`.BW/``+N#P`"[@'>/ZW!WC^MTP:_K=,&OZWVF:*M]IFBK?X -M1#BW^$0XMP``````````H;PWMJ&\-[9V>#>V=G@WMDLT-[=+-#>W&Q*@MQL2 -MH+=S@;M;='F[6W.*R> -MMSBLGK?Q$K6W\1*UMQ2;A[<4FX>W0*UAMT"M8;=O1C2W;T8TMT,"-+=#`C2W -M&+XSMQB^,[=RFX:WX -M4/6W6J[>MUJNWK=!E?2W097TMXXU!;B.-06X@"T;N(`M&[@A`R:X(0,FN$#= -M.[A`W3NXTI0[N-*4.[AD3#NX9$P[N`<##[@'`P^XJ\\#N*O/`[C0:L6WT&K% -MMR`>Q;<@'L6WK!29MZP4F;<&V9BW!MF8M[=J+K>W:BZW+K!9MRZP6;?X6EFW -M^%I9M_Q1P[?\4<.WR*X7N,BN%[BT+&.XM"QCN$:=@;A&G8&X:Y>1N&N7D;CF -M79&XYEV1N#C$B[@XQ(NX>>5VN'GE=KC?$V&XWQ-AN%!32[A04TNX70)+N%T" -M2[AD!D"X9`9`N"YL*K@N;"JX`N,4N`+C%+@?"0JX'PD*N()O_K>";_ZW6)H) -MN%B:";AK]!.X:_03N"K3*+@JTRBX[Q[B'CI*XAXZ2N%4`HKA5`**XJRVLN*LMK+CM'+&X[1RQN#6ZXH'ENN&L)3[AK"4^XE@@ZN)8(.KA9:B^X66HON#5O -M.;@U;SFXC&M#N(QK0[B8&D.XF!I#N"<*3;@G"DVX;RUAN&\M8;BM/W6XK3]U -MN$VZB;A-NHFX^\>8N/O'F+CAR*>XXH;@5(Z:X%2.FN!C6H+@8UJ"X[9&@N.V1H+A3 -M2YNX4TN;N/T(EKC]"):XZN.GX -MGKADJJ.X9*JCN!=DH[@79*.X9RR>N&CKBZ&YVXNAN=N`BEIK@(I::XT"6PN-`EL+@3GKFX$YZYN'@MOKAX+;ZX -M8]J]N&/:O;AD8\*X9&/"N"X.PK@N#L*XRY#&N,N0QKC"8\&XPF/!N/LZO+C[ -M.KRX*Z*HN"NBJ+BSZ9FXL^F9N*JC@;BJHX&X)FJ!N"9J@;BA,(&XH3"!N'5+ -MC[AU2X^X@5F=N(%9G;A+';"X2QVPN*4/OKBE#[ZX]#;'N/0VQ[B=F:S[B,/,^XC#S/N/S#P+C\P\"XL`FWN+`)M[B/J*BX -MCZBHN&X!G[AN`9^XZ@V:N.H-FKB2<)ZXDG">N/C.HKCXSJ*XSLVKN,[-J[B- -M(;"XC2&PN`IQM+@*<;2XIW^ON*=_K[BS+J^XLRZON,#=KKC`W:ZXC%2\N(Q4 -MO+C&*,6XQBC%N-1@R;C48,FX&`/)N!@#R;B3AK^XDX:_N$^%L;A/A;&X]ANH -MN/8;J+@A,IJX(3*:N--BE;C38I6XAQR5N(<X&^6WN`K\Q+@*_,2X,0;2N#$&TKB[C=JXNXW:N%DEVKA9 -M)=JX0&C,N$!HS+A_2+JX?TBZN"I?G[@J7Y^XD_J(N)/ZB+BM*&ZXK2ANN/89 -M7+CV&5RX!WEDN`=Y9+B2SVRXDL]LN-I/@[C:3X.XB8F4N(F)E+@NLJ6X+K*E -MN,?)MKC'R;:X5'C#N%1XP[CY;]2X^6_4N%9;V+A66]BXTO#7N-+PU[A=2,:X -M74C&N/*PM+CRL+2XDRJCN),JH[@E;(VX)6R-N.J7A+CJEX2XX56$N.%5A+A% -MHG^X1:)_N',B?[AS(G^XOR5VN+\E=KC1(GZXT2)^N`"C?;@`HWVXLP&'N+,! -MA[AT\XJX=/.*N(A(E[B(2)>X@"V;N(`MF[@W#I^X-PZ?N*KJHKBJZJ*X4&R> -MN%!LGK@X\IFX./*9N&G*G;AIRIVX=GF=N'9YG;C"!9FXP@69N%&6E+A1EI2X -M(RN0N",KD+B`BX.X@(N#N#1:?K@T6GZX*@.#N"H#@[C^OH*X_KZ"N("BBKB` -MHHJX$EJ*N!):BK@0`H:X$`*&N+U!>[B]07NXPGEBN,)Y8K@7\%FX%_!9N"N+ -M8;@KBV&XE"=QN)0G<;A%7(2X15R$N$T7C+A-%XRX6IJXCWJ:N)@SEKB8,Y:X(O^-N"+_C;B3XX&X -MD^.!N)>I:[B7J6NXW\Y+N-_.2[BSZ#.XL^@SN$SJ([A,ZB.XXL8;N.+&&[A; -M-R.X6SN,!2'KBZ[0ZXNNT.N$,;![A#&P>X -MDLX&N)+.!KCA@0:XX8$&N*@>%;BH'A6X9*HCN&2J([A+D3FX2Y$YN*)>3[BB -M7D^X&79LN!EV;+@P37.X,$USN,,;>KC#&WJX+89CN"V&8[C-7%2XS5Q4N!7V -M/;@5]CVX3H<]N$Z'/;BJTC6XJM(UN"=H-;@G:#6XH_TTN*/]-+@US#NX-/\ON'C_+[C4 -MID2XU*9$N.TR4KCM,E*X'+-1N!RS4;CQ0$.X\4!#N.;J+;CFZBVX:ZX8N&NN -M&+A*9!&X2F01N-$*$;C1"A&X&'D>N!AY'KA4UBNX5-8KN-_]/[C?_3^XJEU& -MN*I=1KCQM$RX\;1,N"`U3+@@-4RX3[5+N$^U2[AQ;T2X<6]$N!@R/;@8,CVX -MOC\ON+X_+[@JI1JX*J4:N"J.$[@JCA.X\,X%N/#.!;@V)@RX-B8,N/=T$KCW -M=!*X*E\?N"I?'[@&V#*X!M@RN&\`.;AO`#FX4B`_N%(@/[C7$3BXUQ$XN#4I -M/K@U*3ZXIZT]N*>M/;@\N$.X/+A#N$VZ2;A-NDFX.39)N#DV2;C).$*XR3A" -MN,7.-+C%SC2XS'4GN,QU)[A*P1.X2L$3N*J.!KBJC@:X1:+_MT6B_[=!./*W -M03CRMUJ6\;=:EO&WYD7DM^9%Y+=^!M>W?@;7MR#8R;<@V,FWQN35M\;DU;?8 -M;.ZWV&SNMV.K";ACJPFXR@8X+O@@N"[X -M(+B\7!JXO%P:N&>A#;AGH0VX0QL'N$,;![BDG0"XI)T`N!51]+<54?2W,DG; -MMS))V[=D8\*W9&/"MUV]M;==O;6WC#VUMXP]M;>ZO;2WNKVTM^D]M+?I/;2W -ML[F_M[.YO[=S),NW73<^W:SC#MVLXP[<&P:NW!L&K -MM[9KE+>V:Y2WA:QCMX6L8[=U-`BW=30(M_$2M;;Q$K6V\V>'MO-GA[:S`8>V -MLP&'MNUYL[;M>;.V,34&MS$U!K=HMDBW:+9(M[Z&D+>^AI"W'&Z\MQQNO+=/ -M$>BW3Q'HMUIG_;=:9_VWJ\\#N*O/`[AAW_NW8=_[MZQOS[>L;\^W47^-MU%_ -MC;=NZA>W;NH7M]X5K;7>%:VUB(TL-HB-+#;E`X$VY0.!-MM\JS7;?*LU```` -M``````!%HG^V1:)_ML/5_K;#U?ZV@5LIMX%;*;=:(FBW6B)HMR;&G;P"-&WL`C1MR?SQ;;MR8DAKW8%8WMV!6-[<<;R*W'&\BM\7F -M(;?%YB&WH3(-MZ$R#;?;\#2WV_`TMWI7-+=Z5S2W(*AOMR"H;[=R58NW36+&WDUBQM[H;G;>Z&YVW9).KB8BW -MJXF(MXO*D;>+RI&WW-9[M]S6>[=/^7JW3_EZMXS>9K>,WF:W-CYYMS8^>;?E -MO86WY;V%M]#?M+?0W[2W>K/0MWJST+<#R/6W`\CUM_)5_K?R5?ZW:FD#N&II -M`[CNU.FW[M3IMR<*S;X@"T;N(`M&[AU8A:X=6(6N!3)%;@4R16XT0H1N-$*$;BS=1"X -MLW40N/#\$[CP_!.XVZL?N-NK'[A>+2>X7BTGN$..*KA#CBJXH^8MN*/F+;B, -M/36XC#TUN&F#/+AI@SRXL[D_N+.Y/[B^X4:XON%&N+<,1KBW#$:X[T5!N.]% -M0;BLASRXK(<\N#P6++@\%BRXTY`CN-.0([@L_1ZX+/T>N'U.'KA]3AZXSI\= -MN,Z?';C[F"2X^Y@DN!V!*[@=@2NX-5@RN#58,KAHY3RX:.4\N.T;2[CM&TNX -M73!9N%TP6;A::&.X6FACN,:&;;C&AFVXRMESN,K9<[AL96NX;&5KN,987[C& -M6%^XH,!/N*#`3[A=ZT.X7>M#N*HO.+BJ+SBX&+XSN!B^,[@+52^X"U4ON$>$ -M+KA'A"ZX`B@JN`(H*KB!6RFX@5LIN/24+[CTE"^X%3PYN!4\.;@:1$:X&D1& -MN#V@5K@]H%:XQ-%FN,319K@#:W.X`VMSN,-X?+C#>'RX>SU[N'L]>[A-H7:X -M3:%VN`.Q;K@#L6ZX97ECN&5Y8[A66UBX5EM8N-A63;C85DVXZ6M"N.EK0KC@ -MX3JXX.$ZN`;O.;@&[SFX^SH\N/LZ/+B%\T2XA?-$N'V23;A]DDVXYA=6N.87 -M5KAQ^E2XXA[]2N(>_4K@.PU2X#L-4N%:A4[A6H5.XC+!8 -MN(RP6+BWKEVXMZY=N,JK9;C*JV6X[G=GN.YW9[@@-&:X(#1FN"GM8;@I[6&X -MGJU@N)ZM8+C+>%FXRWA9N&1+5;AD2U6X@R91N(,F4;@G"DVX)PI-N+<,1KBW -M#$:XJ05"N*D%0K@@!SZX(`<^N+[*/[B^RC^X9EY$N&9>1+B:B4ZXFHE.N*?R -M4KBG\E*XJ4I7N*E*5[@5.U.X%3M3N$?W4;A']U&X?/1-N'ST3;CQM$RX\;1, -MN.V^2+CMODBX$>A,N!'H3+A$I$NX1*1+N'9@2KAV8$JXJ1Q)N*D<2;C;V$>X -MV]A'N`Z51K@.E4:XYNE'N.;I1[@Y-DFX.39)N.;I1[CFZ4>XM1%$N+411+@* -M0D"X"D)`N(SW.;B,]SFX+CTVN"X]-K@H!C6X*`8UN$2\.+A$O#BX[T5!N.]% -M0;AOT42X;]%$N.1+2+CD2TBXR4])N,E/2;BCB$6XHXA%N`/*0;@#RD&XZ!,^ -MN.@3/KC-NCRXS;H\N"$"0+@A`D"XE@@ZN)8(.KA[KSBX>Z\XN&!6-[A@5C>X -M1?TUN$7]-;@JI#2X*J0TN+>!-;BW@36X620TN%DD-+C[QC*X^\8RN)YI,;B> -M:3&XH^8MN*/F+;C32BBXTTHHN#[Z)K@^^B:X?<(GN'W")[@W@BBX-X(HN+I) -M*[BZ22NX)O0KN";T*[A#CBJX0XXJN%(A);A2(26XK\0AN*_$(;B2 -MN'D2'[AY$A^X7KD=N%ZY';A]3AZX?4X>N`W%(+@-Q2"X*E\?N"I?'[A'^1VX -M1_D=N&23'+ADDQRXE%09N)14&;CT\A>X]/(7N%.1%KA3D1:XCV,3N(]C$[A0 -M/A"X4#X0N%B:";A8F@FX3L\$N$[/!+AGT`&X9]`!N%Y&^K=>1OJW>SW[MWL] -M^[?JQOBWZL;XMU@F`+A8)@"X%(0`N!2$`+C3>@*XTWH"N(=@!+B'8`2X,34& -MN#$U!KC/^`>XS_@'N&.K";ACJPFXL<`)N+'`";B`10BX@$4(N'>[`+AWNP"X -M!Z?RMP>G\K?J).&WZB3AM]_ETK??Y=*W_^K$M__JQ+>^RK^WOLJ_MSSGM[<\ -MY[>W7;VUMUV]M;>^5K:WOE:VMXQ4O+>,5+RW.1_"MSD?PKW8>#AMV'@X;9NHZW"?Z0MPG^D+>$]9"WA/60MT[]DK=._9*W`N.4MP+CE+>A -MII:WH::6M\PPDK?,,)*W1\N/MT?+C[<";XNW`F^+MP(2B;<"$HFW`K6&MP*U -MAK)W[4E9 -MM^U)6;?_7DZW_UY.MT_[1K=/^T:WPAU&M\(=1KM[H;';>Z -M&QVW\/P3M_#\$[>',1"WAS$0MZ_<#K>OW`ZW/``+MSP`"[=#>`FW0W@)MZ01 -M"K>D$0JWVF8*M]IF"K>+5@BWBU8(MX<#`K>'`P*WJ:;SMJFF\[:T%=RVM!7< -MMFN5Q;9KE<6VB(VLMHB-K+;BQINVXL:;MMH@C[;:((^V,36&MC$UAK:DG8"V -MI)V`MN?G>[;GYWNV+PQVMB\,=K;C3GJVXTYZMI3X?+:4^'RV)FJ!MB9J@;8+ -M^GBV"_IXMK]4:K:_5&JV8;)3MF&R4[929CJV4F8ZMJ]0&+:O4!BV*@,#MBH# -M`[:7P=BUE\'8M;.YO[6SN;^UB(VLM8B-K+5<89FU7&&9M=-Z@K73>H*U"A)F -MM0H29K6SN3^UL[D_M<7F(;7%YB&U-C[YM#8^^;0N;*JT+FRJM.*N+K3BKBZT -(```````````` +M4DE&1G0]`P!7059%9FUT(!`````!``(`1*P``!"Q`@`$`!``9&%T85`]`P`` +M`````````````````````0```/[_```"````_O\```$```````$``0#___W_ +M```#````_O\!``$`__\```(`___]_P$``P```/W_```#````_O\```(```#^ +M_P```@```/[_`0`"````_____P`````!``$`_O___P,``@#]__[_`P`!`/W_ +M```#````_O\```$```````$```#^_P```P`!`/W__O\#``,`_?_\_P,`!0#^ +M__O_`@`%`/[__/\!``,```#^_P$``0#__P```0```/[_`0`$`/___/\```0` +M`0#\_P``!`#___W_`@`#`/[__?\"``,```#^____`0`!`````````/__```# +M``(`_?_\_P,`!0#]__S_`@`#````__\``````0```/[_`0`"`/___O\!``,` +M___^_P$``0``````_____P(``0#]_P``!````/S_`0`#`/[___\!`/__```" +M``$`_?_^_P0``@#\____`P#___W_`P`#`/S__?\#``(`_O___P$`__\```(` +M___]_P$``P#___[_`````````0```/[_```!`/__```"`/___/\!``0`___\ +M____`P`"`/[__?\```,```#]____`0`!`/___O\```(```#\____`P`!`/__ +M_?_]_P(`!`#^__O_`0`"`/[_`0`"`/S__?\"``$`__\`````_?_^_P,``P#] +M__O_`@`#`/S___\#`/[__?\"``$`_?___P(`___]_P```@```/[___\````` +M_____P$`___^_P$```#__P``___^_P```0#^____`0#^_P```@#]__S_`@`# +M`/W__?\!````________`````/____\`````__________\!````_O\````` +M_O\```$`_O___P(`_O_]_P(``0#]____`@#^__[_`P#___S_`0`"`/W__?\# +M``(`_/_^_P(```#^_P````#^_P```@#^__W_`0`"`/___O______`0`"`/[_ +M_/\```0```#\____`@`!`/W__O\"``$`_O___P(```#__P$`___]_P```P`! +M`/W__O\#``(`_?___P,```#\_P$`!0```/S___\$``(`_?___P(``0#__P`` +M`P`!`/W_```$````_O\!``$``0`!```````!``$`__\!``(````"``$`_O\! +M``0``0#\_P``!@`"`/W___\$``0`_O_^_P,`!``!`/[_```$``(`_O\!``4` +M``#^_P0``P#__P```@`#``(````!``0``0#^_P,`!0`!`````@`"``(``@`` +M``$`!``#`````0`%``(`_O\"``4``P``````!``%````__\%``4`_O\```<` +M!`#^_P$`!0`$``$````"``0`!``"`````@`%``,````!``0`!0`#`````0`% +M``0``0`#``,````$``@``0#\_P0`"0`#`/[_`@`'``,````$``0``@`"``0` +M`P`!``0`!0`#``$``0`%``4``P`!``$`!@`%``$``P`$``(``P`%``,``0`$ +M``4``@`"``4`!``!``(`!@`%`````@`'``0````#``8``P`"``4``P`!``4` +M!@`"``$`!``&``0``@`"``,`!``%``4``0````8`!P`!``$`!0`$``,`!``# +M``(`!``%``,``P`$``,``@`#``4`!``!``,`!@`$``$``P`%``(``P`&``(` +M```%``8``@`"``0``P`$``0``@`$``4``0`"``8``P````4`!P`"````!``& +M``0``@`!``,`!P`&``$````$``8`!0`"``$``P`%``8``P`!``,`!``$``0` +M`P`!``,`!@`$``(``@`$``4``@`!``4`!@`!``$`!0`$``,`!``#``(``P`% +M``0``0`"``4`!``#``4``P````0`!@`"``(`!0`%``,``@`#``8`!0````(` +M!P`&``(``0`%``8``P`#``4`!``#``8`!``!``0`!@`%``,``@`%``<`!0`" +M``,`!0`%``0`!``%``0``P`&``8``@`#``8`!0`%``0``@`&``@``P`"``8` +M!@`$``8`!0`!``4`"@`%``$`!0`'``4`!0`%``4`!@`%``0`!@`'``0``P`' +M``@`!@`$``,`!@`*``8``0`%``L`!P`"``4`"``'``<`!@`$``8`"@`&``,` +M!P`)``<`!P`'``4`!P`*``8`!0`)``@`!``(``P`!@`#``D`"P`(``8`!@`( +M``H`"0`'``8`"``+``H`!0`%``P`#``%``8`#``+``4`!P`.``L`!``'``X` +M#``%``8`#0`-``<`"``,``L`"0`*``H`"@`+``L`"@`)``H`#@`,``<`"@`/ +M``L`"``,``T`"@`*``T`#@`*``D`#@`/``D`"@`.``P`#``.``P`"P`-``T` +M#``-``P`#``.``X`#``,``X`#0`,``X`$``.``H`#``0`!$`#0`)``T`$P`0 +M``H`#0`3``\`"@`.`!(`#P`-``T`#P`1`!``#@`-``\`$0`/``T`#P`2``\` +M#0`0`!$`#P`.``\`$0`1``X`#0`2`!,`#@`.`!$`$0`0`!``$``/`!$`$@`/ +M`!``$P`1``T`$``6`!$`#``2`!4`$``/`!,`$@`/`!$`$P`3`!$`$``2`!0` +M$@`.`!(`%@`0``\`%0`5``\`$``5`!,`$0`3`!(`$@`3`!(`$@`3`!0`%``1 +M`!$`%0`6`!$`$``5`!0`$0`4`!8`$P`0`!,`%P`4`!$`$P`5`!0`$P`4`!0` +M%0`4`!``%``9`!0`#P`3`!@`%0`2`!,`%``6`!4`$0`2`!@`%@`/`!,`&0`5 +M`!``$P`7`!0`$P`4`!,`%``5`!0`$P`3`!0`%0`5`!,`$P`5`!,`%``7`!(` +M$``7`!<`$``3`!<`$@`3`!@`%``0`!0`%@`3`!,`%0`4`!,`%0`5`!(`$@`5 +M`!4`$P`3`!,`%``6`!0`$0`3`!8`$P`3`!8`$@`0`!0`%@`4`!(`$@`3`!8` +M%0`0`!(`%0`3`!,`%``3`!(`$@`4`!0`$0`2`!0`%``2`!(`%``2`!$`$@`2 +M`!0`$P`0`!$`%0`3``X`$0`6`!(`#0`2`!8`$0`/`!$`$@`1`!(`$0`.`!,` +M%0`-``X`%0`2``T`$0`3``X`#P`3`!$`#P`0`!``$``0`!``#P`.`!``$@`0 +M``X`#P`0`!``#P`.``T`#P`3`!``"0`-`!4`$``)``X`$0`.``X`#P`,``T` +M$``.``P`#@`0``X`"P`-`!``#P`+``P`#0`-``\`#0`*``P`#@`-``P`#0`+ +M``H`#0`.``L`"0`,``X`"0`)``\`#``&``L`$``*``8`"P`-``L`"0`(``H` +M#``*``<`"``,``P`"0`&``<`#``-``<`!``)``P`"``'``D`"``'``H`"@`' +M``8`!P`(``H`"0`#``0`#0`*``$`!``,``H``P`#``D`"0`$``4`"0`&``,` +M!P`(``0`!0`(``4``P`(``@``@`#``D`!P`"``,`!P`(``,````'``H``@#_ +M_P<`"``"``,`!@`#``,`!P`&``$``0`%``8``P`"``,`!``$``0``P`"``(` +M!``$``(``@`#``(``@`$``,````"``0``P`!`````@`$``(````"``0``0`` +M``(``@```````P`#`/__```#``$`__\"``,`___^_P(``P`!`/____\!``(` +M`P`!`/W_```#``$``````/__`0`%``$`_/\!``0```#^_P```P`"`/____\! +M``,``0#^____`P`#`/W___\%````_/\"``0`_O___P0```#^_P,``@#]_P$` +M!0#___[_!``"`/[_`@`%`/___?\$``4`___^_P(``@````(``P```/__`P`# +M`/__```#````_O\#``0`_____P(``0````(```#^_P,`!`#^__W_`P`$```` +M_O___P(`!0```/O_`0`&``$`_?\```,``0````$``0`!``$``@`!`````0`! +M``(``@`!``$````#``,````!``(``@`"``,``P````$``P`#``(``0`#``4` +M`@````,`!0`"``$`!``$``(``P`#``0`!``!``(`!0`%``,``@`#``,`!``$ +M``,`!``#``,`!``$``0``@`"``0`!0`%``,``0`#``<``P#__P4`"``"```` +M!0`&``(``@`%``4``@`#``4`!``$``0``P`#``0`!``$``4``P`!``0`!P`$ +M``$``P`&``4``@`#``4`!``#``4`!0`"``0`!P`%``,`!``%``0`!@`&``$` +M`P`(``8``@`#``8`!@`%``,`!``(``8``0`#``D`!@````8`"@`#``$`!P`( +M``,``P`&``4`!@`'``4`!0`$``4`"``&``,``P`'``H`!P`"``(`"0`*``,` +M`@`'``D`!P`%``,`!0`*``<``@`%``@`!@`%``8`!@`&``4`!``'``D`!``" +M``<`"0`'``,``P`)``@``0`#``L`!P````<`"P`"``(`"P`(``$`!``(``@` +M!@`$``4`"``&``,`!P`(``0`!``&``8`!@`&``4`!``&``@`!@`$``4`!P`% +M``0`"``'``(``P`)``<``0`%``H`!@`"``0`!P`(``4``@`&``D`!0`$``<` +M!0`#``<`!P`$``4`!@`%``8`!P`#``,`"``'``,``P`&``8`!0`&``0`!``' +M``8``P`%``@`!``!``<`"0`!``$`"0`(``$``P`)``8``@`%``8`!``$``8` +M!@`$``0`!@`'``4``@`$``@`!0`"``4`!P`&``0`!``&``8`!0`$``0`!0`' +M``8``0`$``L`!@````8`"@`$``,`"0`&``(`"``*``0``@`'``H`!P`$``,` +M!P`,``<````%``P`"0`#``4`"@`(``4`!@`(``D`!@`&``D`"``&``8`"``( +M``@`"0`'``8`"``*``<`!@`*``@`!0`(``L`"0`%``8`"@`,``@``P`'``\` +M"P`#``@`#@`(``4`"P`,``8`!P`,``L`!P`(``L`"@`(``L`#``(``<`#``, +M``D`"0`*``L`"P`*``H`"@`+``L`"0`)``P`#``*``H`#``+``H`#``+``D` +M"@`-``X`"0`(``X`#P`)``@`#@`.``D`"P`.``P`"@`+``T`#@`*``@`#@`1 +M``H`"``.``T`"@`.``X`"0`+`!$`#@`*``L`#0`/``P`"@`.``\`"@`+`!$` +M#``(``\`$``*``P`$``-``L`#@`.``T`#0`,``T`$``/``H`"P`1``\`"@`, +M`!``#P`-``T`#``.`!``#``)``X`$@`,``H`#P`.``P`#@`/``T`"@`,`!`` +M$``,``D`#``0``\`"P`)``\`$@`+``D`#@`/``T`"P`+``X`$``-``H`#0`/ +M``T`"P`-``\`"P`,`!$`#@`*``X`$0`+``L`$@`/``D`#``2``\`"P`-``X` +M#P`/``T`#0`.``X`#@`.``T`#@`/``X`#@`.``T`#@`/``T`#``/`!``#@`- +M``T`#P`1``X`"@`,`!$`$0`-``L`#0`1`!``#``,``X`$``0``P`"P`1`!$` +M"@`,`!,`#P`*``X`$0`/``T`#``-`!$`$0`+``L`$0`1``T`"P`,`!``$0`- +M``H`#@`3``\`"0`-`!(`#@`+`!``$0`,``T`$0`/``P`#``.`!$`$``,``T` +M$0`/``P`#P`0``X`#@`/``X`#@`0`!``#P`-``T`$0`2``X`"P`.`!,`$0`, +M``T`$@`1``P`#@`2``\`#0`/`!``#P`/`!$`$``-``X`$0`1``X`#@`1`!`` +M#P`1`!``#0`/`!,`$``-``\`$0`1``\`#P`0`!$`$0`0`!``$``0`!$`$0`/ +M`!``%0`1``L`$0`6`!``#0`2`!0`$0`1`!$`$@`3`!``$``5`!4`#P`/`!<` +M%0`.`!$`%P`3``X`%``7`!$`$``4`!0`$P`5`!(`$@`7`!0`$``5`!<`$P`3 +M`!8`%``3`!8`%P`4`!,`%0`7`!8`%0`6`!8`%0`7`!<`%@`7`!8`%0`8`!D` +M%@`5`!<`&``8`!<`%@`8`!D`%0`6`!L`&0`4`!<`&P`8`!4`&``;`!<`%0`: +M`!H`%P`8`!H`%P`7`!L`&0`6`!@`&P`:`!<`&0`;`!@`%@`;`!P`%P`8`!X` +M&P`6`!H`'@`:`!<`&@`=`!L`&P`;`!D`&P`?`!L`&``=`!X`&@`;`!T`&@`: +M`!\`'P`:`!D`'P`A`!L`&``=`"$`'P`;`!L`'P`A`!T`'``?`!X`'0`@`"`` +M'@`?`"``'@`@`"(`'P`=`"$`(P`?`!\`(@`A`"$`(0`A`"$`(P`B`"``(@`D +M`",`(@`B`",`(P`B`"(`)``D`",`(P`C`",`)0`D`"(`)``E`",`(P`E`"4` +M(P`C`"4`)@`C`",`)0`E`"4`)``C`"4`)P`E`"(`)0`G`"4`)``D`"8`)P`E +M`"0`)0`E`"4`)@`E`"0`)@`F`"4`)@`E`"0`)@`G`"4`)``E`"4`)P`F`"$` +M)``I`"4`(0`E`"@`)0`D`"4`)``D`"8`)P`D`",`)@`G`"0`)``H`"<`(P`C +M`"8`*``F`"0`)0`F`"4`)@`H`"0`(P`H`"@`)``D`"<`)P`E`"0`)``G`"D` +M)@`B`"0`*@`H`"$`(P`G`"4`)``G`"0`(0`F`"@`(P`B`"8`)P`B`"$`)P`G +M`"``(0`G`"8`(@`C`"0`(P`C`"0`(@`A`"(`(P`C`"(`(@`C`"$`(0`C`"(` +M(``A`",`(0`@`",`(0`?`"$`(0`@`"$`(0`?`"``(@`?`!X`(0`@`!T`'P`A +M`!\`'0`?`"``'0`<`"$`(0`;`!H`'P`A`!T`&0`<`"``(``;`!H`'0`>`!T` +M'``<`!L`&@`>`!X`&0`9`!X`'``8`!L`'0`9`!D`&P`9`!@`'``;`!<`&``; +M`!H`&``8`!<`%P`<`!H`%``6`!L`&0`5`!8`&``7`!<`%P`7`!<`%``5`!L` +M&``1`!0`&@`7`!(`%``7`!0`%``5`!0`$@`4`!4`$0`1`!4`$P`0`!(`%@`3 +M``X`$0`5`!$`#P`2`!$`#@`3`!,`"P`-`!4`$@`*``T`%0`0``D`#@`3``X` +M"P`/``\`#``.``T`"P`-``X`"0`*``X`#``(``H`#``+``D`"``*``P`!P`' +M``L`"@`&``8`"0`)``8`!@`&``4`!0`)``D``@`"``D`"``#``0`!0`"``4` +M"``$`/__`@`%``0``P`!`````P`#``$````!`````0`"`/___O\!````_?_^ +M_P(`___Z__W_`P#^__C__?\!`/S_^__\__O__?_^__G_^?____W_]O_Y__W_ +M_/_Z__C_]__[__[_^/_T__K__/_Y__?_]__X__K_^O_V__;_^?_W__?_^/_U +M__7_^?_X__3_]/_X__;_\__U__?_]?_Q__/_^/_U__'_\__W__/_\?_T__+_ +M\?_T__/_\?_R__/_\?_Q__#_\/_S__'_[?_R__/_[/_L__3_\__L_^S_\?_Q +M_^W_Z__N_^__[?_L_^[_[?_L_^W_[?_J_^K_[O_M_^C_Z__M_^G_Z/_M_^S_ +MY__H_^S_ZO_H_^G_Z?_I_^K_Z/_H_^K_ZO_G_^;_ZO_K_^;_Y?_I_^K_YO_F +M_^G_Z/_E_^?_Z/_F_^7_Y__G_^7_YO_H_^;_Y/_E_^;_Y?_F_^?_Y?_A_^7_ +MZO_D_]__X__I_^C_XO_A_^3_Y__F_^/_X__D_^7_Y?_C_^/_Y/_D_^3_Y/_C +M_^/_Y/_D_^+_X__E_^/_X?_B_^3_Y/_A_^'_X__C_^#_XO_E_^#_WO_D_^3_ +MWO_>_^/_X?_?_^#_X/_?_]__X/_@_][_W?_>_^'_X/_=_][_W__=_]W_WO_> +M_]W_W?_=_][_W__>_]K_V__@_][_V?_;_][_WO_;_]K_V__=_]S_V?_=_][_ +MV?_;_][_V__8_]K_W?_=_]O_V/_:_][_V__8_]O_W?_:_]G_V__;_]O_VO_9 +M_]O_W/_;_]K_VO_<_]S_VO_9_]W_W?_7_]K_X/_<_]7_VO_@_]G_UO_<_]W_ +MVO_9_]O_V__:_]O_VO_9_]K_W/_<_]G_V?_;_]O_VO_:_]O_V?_8_]S_W/_7 +M_]C_WO_=_]?_V/_=_]S_V/_9_]W_W?_9_]G_W?_<_]C_VO_=_]O_VO_:_]O_ +MW/_9_]G_W?_>_]C_V/_>_]S_V?_;_]S_V__;_]S_VO_:_]S_W/_;_]K_V__= +M_]S_VO_;_]S_V__<_][_VO_7_]W_X/_;_]G_W/_<_]S_WO_<_]G_W/_@_]W_ +MV?_;_]__X/_;_]C_WO_C_]W_U__<_^'_W?_;_][_WO_=_^#_X/_;_]K_X?_B +M_]O_VO_A_^+_WO_=_][_W__A_]__W/_?_^+_W__=_^#_XO_>_][_XO_@_]S_ +MWO_C_^+_W?_<_^'_X__>_][_XO_@_]W_X/_B_]__WO_@_^'_W__>_^'_XO_? +M_][_X?_A_^'_XO_@_][_X/_D_^/_WO_>_^/_Y/_@_^#_X__C_^#_X?_D_^+_ +MX/_B_^3_XO_@_^7_Y?_@_^'_Y?_D_^+_X__C_^+_Y?_D_^#_XO_E_^3_X__B +M_^+_Y/_D_^+_Y/_D_^#_Y/_G_^#_WO_E_^;_X/_?_^7_Y/_?_^+_YO_C_]__ +MX?_D_^+_XO_B_^#_X?_C_^/_XO_@_^#_X__C_^#_X?_C_^'_X/_B_^/_XO_B +M_^#_WO_D_^3_W/_>_^;_Y/_=_]__Y/_A_]__X?_A_^'_X?_B_^+_X/_@_^#_ +MXO_B_]__X/_B_^+_X?_?_]__X?_C_^#_W?_B_^3_W__>_^+_X__?_]__X__A +M_^#_XO_?_]__Y/_C_]W_W__E_^+_WO_A_^/_X/_?_^+_XO_@_^#_X?_A_^'_ +MX/_?_]__X/_B_^'_W?_?_^3_XO_=_][_X__B_][_W__C_^/_W?_>_^7_XO_> +M_^#_XO_A_^'_XO_>_]__X__A_]__X/_D_^/_WO_?_^+_X__A_][_X/_D_^/_ +MW__?_^/_Y/_?_]__Y/_D_]__WO_C_^3_X?_?_^#_X__A_]__XO_B_^#_X?_C +M_^'_W__C_^/_WO_@_^/_X?_?_^'_XO_?_^'_X__@_^'_XO_@_^#_X?_C_^+_ +MX?_A_^'_XO_A_^+_X__@_^#_Y/_D_^#_X/_B_^+_XO_B_^+_X?_A_^/_X__A +M_^'_Y/_C_]__X/_D_^3_X/_?_^/_Y?_B_^#_XO_D_^+_X?_B_^'_X?_C_^/_ +MX/_@_^7_Y/_>_^#_YO_D_][_X/_E_^+_W__@_^+_Y?_A_][_Y/_D_][_W__E +M_^/_WO_@_^3_Y/_?_][_Y/_E_^'_W__A_^+_XO_B_^#_X?_D_^+_W__B_^7_ +MX/_>_^/_X__A_^3_Y/_?_^'_YO_C_][_XO_F_^/_X?_C_^3_X__B_^/_Y/_D +M_^'_XO_G_^3_W__C_^?_X__A_^3_Y/_D_^3_XO_B_^/_X__C_^3_Y/_C_^3_ +MY/_C_^/_X__C_^7_Y?_B_^'_Y?_H_^3_WO_B_^G_YO_?_^'_Z/_F_^#_X__H +M_^3_W__E_^C_X?_B_^G_Y?_>_^3_Z?_C_^#_Y?_H_^/_X?_F_^?_XO_B_^?_ +MY__C_^7_YO_D_^;_Y__C_^3_Z/_E_^/_Z?_H_^3_YO_H_^?_Y__G_^;_Z/_I +M_^7_Z?_L_^?_Y?_I_^O_ZO_H_^G_Z__J_^G_[/_M_^K_Y__L__#_[O_H_^K_ +M\?_O_^O_[?_N_^__[O_N_^__\O_P_^[_\?_S__'_\/_P__/_]?_Q_^__]?_V +M__'_\/_S__7_]?_R__'_]?_X__3_\?_T__C_]O_S__3_^/_W__3_]O_Y__?_ +M]O_X__K_^/_W__C_^O_Z__K_^__Z__C_^__^__W_^/_Y_P``___X__O_`0#_ +M__K__O\"````_O_^____`0```/____\!``(```````,``P`!`````P`$``0` +M`@````0`!P`#``(`!0`%``,`!0`&``4`!0`'``8`!0`'``@`!0`&``D`"``' +M``@`"``)``D`"0`)``L`"0`(``L`"P`)``H`#``-``L`"P`,``X`#``,``X` +M#0`,``X`$``.``H`#0`2`!$`#``-`!$`$@`0``\`#P`0`!(`%``1``X`$0`5 +M`!$`#@`3`!<`$P`0`!(`%``3`!,`$P`5`!8`%``3`!8`%P`4`!,`%P`8`!8` +M%``6`!H`&``4`!<`&0`7`!<`&0`7`!8`&@`:`!8`&``:`!@`%P`9`!D`&``9 +M`!H`&0`9`!H`&P`8`!@`'0`<`!8`&``?`!X`%P`9`!X`'``9`!H`'0`=`!D` +M&@`>`!T`&0`;`!\`'``:`!X`'@`<`!P`'0`=`!X`'@`<`!P`'0`>`!\`'0`< +M`!X`'P`<`!P`'P`=`!L`'P`@`!T`'0`?`!X`'@`?`!T`'``?`"$`'0`;`!\` +M(``>`!X`(``?`!P`'P`B`!X`'``@`"$`'0`>`"$`'P`=`!X`'P`?`"$`'P`< +M`"``(0`?`!X`'P`?`!X`(@`B`!P`'@`D`"``&@`@`"0`'@`<`"(`(P`=`!T` +M(0`@`!\`(0`A`!X`'P`C`"``'``A`"0`'0`<`"0`(P`:`!P`)@`D`!L`'0`D +M`"(`'0`>`"$`(0`@`!\`'P`A`",`'P`:`"``)``>`!T`(@`A`!T`(``A`!\` +M(0`?`!P`(@`E`!T`&P`B`",`'P`>`!\`(``B`"(`'P`>`"(`)``@`!T`(0`E +M`"$`'0`B`"4`'P`=`"$`(P`A`!\`'P`B`"4`(``;`"$`)@`B`!X`'P`B`",` +M(@`@`!\`(@`E`"(`'@`@`"4`(P`>`"``)0`E`"``'P`D`"4`(``?`",`(P`@ +M`"(`)@`C`!X`(@`E`"``'0`B`"4`(``<`"(`)P`B`!P`'P`E`"(`'P`A`"$` +M(0`@`"``(P`C`"``'@`A`"0`(0`>`"``(P`A`!T`(0`F`"$`'``A`"0`(``@ +M`"(`(``?`"(`(P`@`!X`'@`A`"0`'@`<`"(`(@`=`!X`(0`?`!T`'P`=`!X` +M(0`=`!H`'P`A`!T`&P`=`!\`'P`=`!L`'``>`!X`'0`;`!L`'@`>`!L`'``; +M`!D`'@`?`!@`&0`>`!L`&@`?`!H`%0`=`"``&``7`!T`'0`8`!<`&P`=`!@` +M%@`:`!T`&@`5`!@`'0`9`!4`%P`:`!D`&0`8`!4`%P`;`!<`%``6`!D`&``5 +M`!0`&``9`!0`$@`6`!@`%0`2`!4`%@`4`!0`%0`5`!4`$P`2`!4`%@`2`!`` +M$P`5`!,`$@`/`!$`%``2``\`#P`1`!(`$0`0``T`#P`3`!``"0`,`!(`#P`) +M``P`$``-``D`"P`-``P`"0`(``L`#0`*``<`!P`*``H`"``&``<`"0`)``4` +M!``'``@`!@`%``4`!@`$``4`!P`$``$`!``&``0``@`"``$``P`$``$`__\" +M``,``0````$``0````````#__P````#^__W_`0`!`/S__?\!`/___/_]_P`` +M___[__S_`0`!`/O_^O____W__/_\__K_^__^__W_^?_Y__W_^__X__G__/_\ +M__G_^/_Z__K_^?_Y__K_^/_V__G__/_X__3_]O_Y__C_^/_V__/_]O_[__G_ +M]/_T__;_]__Y__;_\O_U__G_]O_T__7_]/_U__C_]O_S__3_]O_W__;_\__T +M__?_]__T__3_]__V__7_]__W__;_]O_V__?_^/_V__/_]?_Z__C_\__U__K_ +M]__S__C_^?_U__;_]__U__?_^?_V__7_^?_Z__?_]/_V__K_^O_V__;_^/_Z +M__O_]__T__G_^__Y__G_^?_X__K__/_Z__;_^?_^__S_]__Y__[__/_Y__O_ +M_/_\__O__/_^__W_^__\__W__/_\__[__?_\____``#]__[_______[__?__ +M_P(```#\__[_`@`!`/W__?\!``$`_O_]_P$``@#_____```!``$`__\```(` +M`0#__P```@`!`/__`@`$````_O\"``0``0````(``@`!``(``P````$`!0`# +M`````@`$``,``@`#``(``@`%``4``0`"``<`!@````,`"``%``$`!@`*``0` +M```'``L`!0`!``@`"P`$``(`"0`*``<`!P`(``@`"@`*``8`!0`,``P`!@`' +M``P`"P`'``D`#0`*``@`"@`-``P`!P`(``T`#0`)``@`#``,``D`"P`,``D` +M"@`.``P`"``+``T`"P`+``P`"P`+``P`#``+``P`#0`,``L`#``-``P`"P`- +M``X`#``+``X`#P`,``L`$``1``T`#0`0``\`#0`-`!$`$@`.``T`$``2`!(` +M#P`/`!$`$P`1`!``$@`2`!$`$P`3`!$`$@`5`!,`$0`4`!8`$@`2`!<`%0`/ +M`!,`&``5`!(`%``6`!,`$P`9`!8`$``4`!D`%0`3`!4`%@`6`!4`$P`4`!<` +M%@`4`!4`%``5`!<`%0`5`!<`%``3`!@`&``2`!0`&``5`!0`%@`5`!0`%P`8 +M`!,`$P`7`!<`%0`5`!8`%@`7`!@`%``3`!D`&0`4`!4`&``8`!8`%@`7`!@` +M%P`5`!<`&@`8`!0`%@`;`!@`%0`7`!D`&0`8`!<`&``9`!D`&``8`!@`%P`7 +M`!H`&P`7`!4`&0`;`!8`%0`9`!D`%P`7`!@`&``8`!D`%P`6`!D`&@`5`!0` +M&@`:`!0`%0`7`!<`&``8`!8`%0`7`!D`%@`3`!0`&``8`!4`$P`4`!8`%@`5 +M`!4`%``3`!8`&``3`!$`%@`7`!(`$@`8`!8`$0`4`!4`$P`2`!0`$P`1`!0` +M%0`2`!$`$@`3`!(`$P`3`!``$0`5`!0`$``0`!(`%``5`!,`$``0`!,`$P`3 +M`!$`#@`2`!8`$@`.``\`$P`3`!``#P`1`!$`$``0``\`#@`2`!$`#``-`!`` +M#@`,``T`#P`,``L`#P`/``P`#``-``P`"@`,``P`"@`,``P`"``+``X`"0`' +M``P`"P`'``H`#``(``@`#``+``@`"0`)``@`!P`(``@`!P`&``@`"0`'``8` +M!P`(``@`!@`%``@`"0`%``4`!@`%``4`!@`%``4`!``#``8`!P`!````"``* +M``(`__\%``@`!``"``(`!``%``0``P`"``,`!0`$``(``0`$``0``0`"``,` +M`@`#``,``@`!``(``0````$``@```````0```/[___\"``$`_?_]_P$``P#^ +M__K__?\"````^__[_______[__O____^__K_^_____W_^O_\__W_^__[__S_ +M_?_Z__G__/_\__K_^__Z__C_^/_[__O_^/_X__K_]__W__O_^?_S__7_^__Z +M__3_]/_V__C_^/_T__/_]__V__3_]/_T__7_]?_T__;_]__T__/_]O_W__3_ +M\__U__7_]/_T__;_]?_Q__3_^/_T__+_]?_T__+_]O_V__'_\O_V__7_\__T +M__3_\__T__7_]/_S__3_]?_T__7_]?_T__7_]/_S__?_]__S__3_]__T__/_ +M^/_W__'_]/_Y__7_\O_U__?_]/_S__?_]O_T__7_]?_U__3_]/_W__C_]/_P +M__;__/_T_^[_]O_[__7_\O_V__7_\__X__G_\__T__G_^/_U__?_^/_W__C_ +M^/_V__;_^/_[__G_]__Y__S_^O_[__O_^/_Z__[__/_[__S__?_]_____?_\ +M____``#^_______^_P$`!`#___S_`0`'``(`_?\!``0``@`!``$`!``$``,` +M`P`$``4`!0`#``(`!0`(``4``P`&``D`!@`$``<`"``%``8`"@`*``8`!P`* +M``D`!P`*``H`!P`*``X`"0`'``P`#P`*``@`#0`1``T`"0`-`!(`#0`+`!`` +M$@`.``T`$0`4`!``#@`2`!4`$0`0`!0`%@`3`!,`%``5`!4`%@`6`!8`%@`9 +M`!@`%P`8`!D`&``8`!@`&0`;`!P`&``8`!T`'``8`!L`'0`;`!D`'``?`!T` +M&@`<`"``'P`;`!P`'@`@`!\`'0`?`"$`'@`=`"``(P`@`!X`(0`B`"$`(0`@ +M`"(`(@`B`"(`(@`C`",`(P`B`"$`)``F`"4`(0`E`"D`)``C`"D`)P`D`"D` +M+``E`"0`*P`K`"4`*0`M`"D`*``L`"H`*``L`"\`*@`G`"P`,@`N`"@`*@`Q +M`#``+``L`"\`,0`Q`"X`+P`Q`#$`,``S`#(`+@`R`#<`,0`L`#(`.0`S`"\` +M-``V`#,`,P`T`#4`-0`U`#0`-@`W`#0`,P`W`#<`-``U`#8`-0`V`#<`-@`T +M`#0`-P`Y`#4`,@`W`#H`-P`V`#4`-0`Y`#H`-0`T`#D`.P`V`#8`.@`Z`#<` +M.``\`#D`-0`Y`#P`.@`W`#@`.@`Y`#H`/``Y`#D`/``\`#H`.@`Z`#P`/``[ +M`#L`.P`\`#T`/``Z`#H`/@`]`#L`/``]`#T`/``\`#\`/P`Z`#H`00!``#H` +M/`!"`#X`.@!``$$`.P`\`$(`00`Z`#X`1``_`#H`/P!$`$``/0!``$``/0`^ +M`$``/P`^`$``0``]`#\`0@!``#P`/0!#`$(`/0`]`$``0``^`#X`/P!``$$` +M/P`^`$$`00`]`#X`0P!!`#T`00!$`#\`/@!"`$(`/P`_`$$`0``_`$``0`!` +M`$$`00`_`$``00!!`$(`/P`]`$``0P!!`#\`00!!`$$`00!``$$`0P!"`$`` +M0@!$`$(`00!"`$0`0@!``$,`1P!%`$``0@!'`$4`0@!"`$,`10!&`$,`0`!$ +M`$@`0P`^`$0`20!$`$$`0P!%`$4`1`!#`$0`10!$`$4`1@!#`$,`1@!&`$,` +M1`!&`$,`0P!'`$8`0`!"`$H`1P!``$,`1P!%`$,`0P!#`$8`10!!`$,`1P!& +M`$(`00!$`$8`0P!!`$4`10!!`$,`1@!#`$(`1`!#`$(`0P!"`$,`1`!!`$`` +M0P!$`$,`00!``$(`1`!#`$$`0@!#`$$`0@!#`$$`00!#`$,`0`!!`$0`0@!! +M`$``/P!!`$(`0@!!`#X`/P!"`$(`0``_`#\`/P!!`$(`/``\`$,`0@`Y`#L` +M0P!``#H`/0!!`$``/``\`#\`/@`\`#T`/0`\`#L`/0`]`#L`.P`\`#L`.@`] +M`#T`-P`Y`#X`.P`X`#H`.P`Z`#<`.``Z`#D`-@`W`#D`-@`V`#H`-@`S`#<` +M.``S`#4`.``T`#,`-@`V`#,`,@`U`#4`-``S`#,`-``T`#(`,0`S`#4`,@`P +M`#(`,P`Q`#$`,@`R`#``,``S`#,`+``M`#0`,0`L`#``,0`M`"P`,``P`"T` +M+``M`"\`+P`K`"L`+``M`"X`+``J`"T`+@`I`"D`+@`N`"L`)P`H`"P`+@`H +M`",`*@`P`"H`)``G`"T`*@`E`"8`*@`I`"4`)0`J`"<`(P`D`"8`)``D`"4` +M(P`B`"8`)``@`"(`(P`B`"(`(0`B`",`(P`@`!\`(@`B`"``'P`?`"$`(@`@ +M`!X`(0`B`!\`(``A`!\`'P`@`!X`'@`C`"$`'``>`"$`(``>`!\`'@`=`"(` +M(0`<`!L`'P`A`!T`'``>`!X`'@`<`!P`'0`=`!P`'``=`!X`&P`9`!L`'@`< +M`!@`&P`>`!D`&0`:`!@`&``;`!D`%@`8`!H`&``8`!@`%@`7`!D`%P`6`!8` +M%P`8`!<`%``5`!<`%P`4`!(`%0`9`!4`#P`2`!D`$P`.`!,`%0`3`!(`$0`2 +M`!(`$0`0`!(`#P`-`!$`$0`-``X`#P`.``\`$``,``L`#P`0``L`"@`+``P` +M#0`-``D`!P`-``\`!P`'``P`#``'``@`"0`&``@`"P`'``0`!P`+``8``P`% +M``4`!0`'``8``@`"``<`!P`#``,`!``%``8`!0`$``4`!0`%``8`!@`$``0` +M!@`%``0`!``$``0`!0`&``4`!``#``0`!P`&``(``@`&``<`!``!``,`!``# +M``(``P`%``,`__\"``8`!`#__P``!``$``$````"``,``P`#```````#``,` +M``````$``0`"``$`_?___P,``0#^_P````#__P$``0#]__W_`@`"`/W__O\! +M`/___O\```$`_____P$``0```/[__?\!``,`_?_[_P$`!`#___O___\#```` +M_/___P(`_O_]_P````#_____```!````_?\```(`_?_^_P,```#]_P$``0#] +M__[_`0#___[___\`````_?_^_P(`_O_Z_P``!`#\__K_``#___W____^__W_ +M______[_______W__O____[_______W___\"`/__^____P(`___]____```` +M`/____\```$``0```/____\```(```#^_P```@`"``(```#__P(``P#__P(` +M!0`!`````P`"``(`!0`#````!0`&``(``@`%``0``P`%``8`!0`#``,`!P`& +M``,`!0`(``8``P`$``8`!P`&``,`!``(``<``@`#``@`"0`'``,``P`+``P` +M!0`"``@`#0`'``(`!P`+``D`!P`'``<`"``*``H`!P`&``<`"@`*``0`!0`+ +M``H`!@`&``D`"``'``@`!P`'``@`"0`*``<`"``+``D`!@`'``L`"0`&``D` +M#``(``0`"P`.``8`!``,``X`!@`&``X`"P`&``H`#0`)``<`"@`,``H`!@`' +M``X`#0`&``@`#0`*``<`"@`+``D`!@`'``L`"P`'``8`"P`,``8`!@`)``@` +M!@`'``<`!@`%``8`!0`%``4``P`#``4`!``#``,`!``"``(`!``"`/__`0`# +M``(`_O_^_P```0```/[__/_^_P``_O_Y__O_``#___C_^/_\__S_^?_Y__C_ +M^?_W__7_]__Y__3_\__W__?_\/_R__?_]/_M_^__\__R_^[_[__O_^__[O_L +M_^K_[O_M_^C_Z?_N_^K_YO_H_^G_Y/_F_^G_YO_C_^;_Y/_B_^/_X__@_^'_ +MX/_@_^/_XO_;_]O_X/_B_]S_V/_=_^+_VO_5_]S_WO_7_]C_V__9_]?_V/_6 +M_];_UO_5_]3_U?_4_]7_T__1_]+_T__0_]'_TO_/_\S_TO_2_\O_R/_-_]#_ +MS?_&_\G_SO_,_\7_QO_._\S_PO_$_\[_S?^__\#_S/_+_[[_OO_)_\K_O?^] +M_\C_Q_^Z_[O_Q/_$_[O_N?^__\/_N_^X_[O_O/^]_[S_M?^W_[__O?^P_[+_ +MPO_`_Z?_J/_&_\;_G?^=_\W_S?^3_Y/_RO_*_X__C_\Z`#H`5O]6_QP`&P`& +M_PC_)@(D`NO\[OR4]9#U]0'X`>;]XOT_`T4#IP>A!P']!OU__WO_70%@`7_^ +M??Z"_H3^;@)K`E3[5_OW^?3YJ0>M!XD&A08R]C7V`/K^^:[^K_X-!0P%S0W- +M#;;_M_]A\6'Q;_MO^\D'R`>T`;4![OSL_+W\P/SH`N8"I@&G`80#@P.3_I/^ +M_?3^])'_D/\+"@P*+/XJ_I#]D_TL^RG[C_F2^5T/6@^`_8/]9?!C\-S]W?W5 +M"]4+\PSQ#&[[<_MFZ5_IE/V<_9L5DQ7P#?4-P?/`\Y3HD^A=!U\'_@G\"3<# +M-P,3^Q3[3.]+[Z0#I0-S$W(3#OL/^RST*O2+]X_WRO_$_R\3-!,H!R4'"/H) +M^KGRN_*\_[C_/A%"$5W_6O^#\(3P:/UI_?T"^@*)#HX.E`".`/_H!.E^^7OY +MT0;1!M@)V@DF_R/_W?+A\L0%P`4P"3()[?WL_>7_YO^"]X+W7`E<"2@))@D, +M]@[VN?^X_TO]2_W:_-S\60E5"=?ZVOHN^2SYQ/O&^VSY:_D:!QD'V__<_Q'_ +M#_]>^V'[;P-M`U$4410T_#;\B.N#ZU,$6P39!]`'%PL?"X<`@`!YZW_K+00H +M!.,&Y@:J_:K]#_4,]87VB?8L"B@*/05`!4OV2_;^__O_%/L8^Z8#H@,-#Q$/ +MI/RA_&4!9@%S^W+[\0#T`-T)V0G?^^+[NOJX^B[^+OYR_W3_20I&"IWSH//L +M[.CLD026!"\%*P6=#IX.I?JG^LKPQO`_"4,)3@=,!_'^\/[1_M3^#OD*^=@) +MW`E4"U$+^_[]_AKY&?EO\6[Q9`9G!H4$@023^)?X"``$`)KUG/6C_J3^X0_? +M#[_YP?F"^G_Z8P%E`0($`02X"KH*]_KT^DWZ3_IR`W$#M@6U!8(*A@K,]L;V +MV +M"+L(CPJ,"K@`N0`U`C0"5?16]$<'10>:"YT+]O7S]37W-_>\_+S\)@8D!B4- +M*`V2\8_QWN_@[RX$+@3/"LT*5@=8!P'Q`/'V_?;]5`=4!^\`\`"S![$'C?V0 +M_23Y(?ES"W0+5@!6`#S_/O^A_Y[_Z>_L[TP!20%@!V$'ZP7L!3_[/OMH\6CQ +MM@2W!,H)R0GT^O3Z6_M<^Y_ZGOH9#!H,(`X?#K3RM?)0_$[\[`+O`E4%4@6- +M!)`$//4X]9O^G_ZY![8'3?U._5G^6?XU]S;W8`!=`(`)A0F9^)/X,`$T`08` +M!@`Z^3CY/@A`",<%Q@4?_Q__$?T1_2!(X$ +MO`#``.+VW?;P_O7^W@C;"$H"2P)M!6T%0OI`^J'XI/@@!AP&G?ZC_N`&V09D +M_&O\X_+=\H4%B04]!3L%X?CB^%3[4_M0_5+]R@O("R8$)P1N]FWV!@`'`)O] +MF_U%"T4+=`9T!G[T??1"`4,!^@CZ"(W^C/[2_-3\*_8I]E?_6/\@!2`%%0,5 +M`P\##0/W[OONSOO*^XP*CPK=_]S_:@%I`>7\YOP%_`7\61!9$.P$ZP1<^5[Y +M/?L[^YO]G/T^#3\-W@';`0?U"_6B^9WY5_U>_=4&S`9?`&@`Y/;<]HX`E``" +M_/_[-`0T!!L''`1`9`!8!%B$2?W)/?Q]/7T6/]4_S/^-OZF`*,`@!)X$4PA4".`%X047]!3TVOK= +M^F0'8@=4!54%JORJ_/7M].V]_[[_`0O_"A3^%_X:^A?Z_?(`\ZD"IP+^$/X0 +M1/]$_Y7\E?S/_-#\ZOWI_1H.&P[(`<8!+?LP^_,"\`+L_NW^S?_._Q']#OU' +M^DSZ)?P@_$G[3/OI">@)(@$B`2KS*O//`]`#W__=_U7_6/\:!A<&3OQ0_+T# +MO0/Z"OD*%OX7_@WZ#/HC^"+XF@B<"!0*%`KZ]?CU$/L3^WO[=_NR_[7_QP;& +M!BGZ*/K2]]7WS`''`9X$I03R">D)HOFJ^0;W`/>Q`[8#/04Y!;H-O0T&_@+^ +M4?)6\E3_3O]-!%,$<05M!3+^-/[I\.GP/P8]!DL(30BG^:?YB?R(_*+XH_AP +M`'``M`:T!IT$G`0<_QW_4?E/^=D"W0+J!^<'$OL2^W,$=01R_V__L/FS^<`) +MO@F+_(O\7/5=]90`E`"*!8D%-P8V!L#XP_@;]1CU>P1\!`<""0(S`BX"6_MA +M^S_Z.?JE#:H-H@:>!FWYT"[@*@ +M#*,,,_XP_IOTG/0-`PT#CPJ-"H,"A@+T]//T%?L4^Y\,H0S8_M7^3_I2^E+X +M4/AS]W7WR@K("B\$,00*^@CZ<`!Q`+#^L/Y^`7X!Q0/&`V7\8_R*`XP#-0`Q +M`$($1P2-"(D(&?L=^[SXM_A>^6+Y.04V!0<*"0JK]:OU2_5*]=H#V@-E`6G]ZOT]"CL*+_XQ +M_EW[7/NL^*SX$OH2^ND%Z@4>`QP#7/]>_^WY[/F5_I3^+@LQ"]L!UP$W_3K] +M-`,R`T7]1_W0"<\)0P="![3TMO3E]N+V`/\#_[$'L`<%``4`QO7&]0W_#/\3 +M`!0`:/YH_ND$Z@1;^%CXG0"A`$`+/`ON`O("M0"S`$3^0_[9_]S_!`$!`4<` +M20!S!',$:/UF_3OX/O@X!#0$#/T/_1'\$/PB_"'\9?MH^]@)U`DV`C@"9_9F +M]E[_8/\6!10%;PEP"98$E021]I'V"`$*`5@(5@B5`I4"R_;+]L?UR?4T"#`( +M#083!@?]__QV_G_^U?7-]87ZC/K^!O<&;@)S`LX!S0$A_1_]AOZ*_O8'\0== +M!&`$*P`K`)'WD/?_^O_Z1!!$$.$&X09]]'[T)_`>,!X0%W_'G\VP7:!0\+[`NJ`JX"F`:(! +MOP3"!%<#5@-X^7?YA?N(^Q3_$/^#`H<"+P$J`7KU@/7;^=;YKP*T`D$#/`/^ +M!P$(3`)*`K;YN/DL_RO_8P%C`5\)8`F.`XL#J/:L]@(`__\["#P(YP'I`;7V +MLO:`](+TY0'E`<`$OP1#_43]`_T"_;3ZM?I?`UX#JP6L!2;\)?R^`;X!"P@+ +M"+0#M`-N_6[]AON&^[`#L0/O`>P!Y?WH_7\#?`.U^[C[]/CT^-S_V/\@_2;] +MW?W6_3S^1/[$_+S\/`5$!<$&N0;U`/P`VOC5^/7[^/O:"]@+B`J*"N3]XOWG +M^.CX0_Q"_)\&H`;8`=0![`)SYF?GI_.W\Q`S`#/$$ +M]03,^LGZ1?]&_Q``$`"5_I/^.OX__K__N?\"_P?_'?P9_,P#SP.,`HH"F_J= +M^@#]_?S@_>+]=P-Y`Y$(C@A:_UK_+/LM^W@`=@`B!R<'C02(!-OVW?:V_;3] +M9PAJ"/H!^`&"^H3Z!_D#^<'_QO^D!I\&\/_U_W_]?/V#_H/^+OXP_H8"@P*, +M_H_^YO[D_FL#;0/P`^X#9@)G`I@`F`!H`&@`F/V9_23Y(OF*!8P%"`@'"-OY +MV_DH_2G]1`%!`24`*0"3_Y#_"/P*_"C])OV9!)H$-08U!BO_+/]?]UWW]P#Y +M`/0(\0@+``\`$_X0_H@`B@""`X$#6P):`K_\P?P9_QC_+0$M`9K_F__J`N@" +MB_V,_>OY[/EE_V+_J_^O_V`"7`(3`Q8#,P`R`+C^M_X!_P3_!P8"!D$#1@,> +M^!OX\0#S``0+!0O&`\(#;/MP^WGZ=OI?_V'_OP&_`40"0P+6_M;^(?0!T_[3OMW]WCWB?^)_Z\!K@'J`NL";P!O`/3\]/QK_FO^+P@O")X) +MG0D;_!W\#/L*^^@(Z@A1!4\%__X!_R#_'?\M^C+Z2/Q"_%,!60$8`A,"$?\4 +M__K[^?L<_AW^B0"'`)L`G0`=`QL#_O\!``$`_O]>!F`&ZP?I!QP"'@)(^4?Y +M:?]I_X@(B`CF_N7^O/J_^KO_[/]/_D[^Q?_&_[G] +MN?T$_`/\6P->`S\$.02O^[?[-/LL^TS[4_LA`!P`+P@Q",0!Q`$,_`O\W0#? +M`!L)&`F#!X4'9_IF^K;YM_DP!C`&B06(!7G_>O]A_6#]?/U]_?[]_OVR_K+^ +M80)?`C/]-OVU_+'\_`0!!10`$``Z_#O\]P7X!<$"OP(J_BO^"@0*!+D$N01$ +M_D/^8_QF_/$$[00@`R,#AON$^_#_\/];`5X!T?S._,/_Q/_+_X!L@*Q`N_[[_O[ +M_?O](@(B`L?_QO^(_(O\6`)4`CL$/P3%_<+]5OY7_@+_`O^V^[7[-00W!%T( +M7`CG_^;_%OH7^@(!`0%4"%8(:O]H_W;Z>/JR`:\!]P'[`6[_:O]I`6P!&/\7 +M__+]\/W%_LG^D`&+`6\`0%Y`3,!,0$V_CK^N/NR^W4`?`#:_]3_8OQF_-<`U@"5_I3^:/II +M^D4$100M"RT+F`&8`?;W]_>9_Y?_4`A3"!H!%P$^_T'_,P,P`^7_Z/_"`+\` +M+P(Q`LSYR_E\^'WX`@$!`>`!X`$5^Q7[^@#Z`"0')0?T_/3\&_P8_#<&.P8> +M!!H$./\]_YX"F0+S`O<"=P!S`-#_U/_M`ND"+P`R`/+[\/MQ_G+^V/W7_5[[ +M7_N?_Y[_&P(=`C;]-/U:_EO^EP:5!LD%S`57^E7ZP1_!`OY!/G6^]_[UP/.`R?^+_Z`_'G\KP&R +M`9/]E/T:_A?^3011!';^B_J/^./XX_B(((0C-`L\" +MX/O?^ZL#JP-6!%8$@ON"^\+[POO+`,P`!@`&`)K_F?\U`34!/?\]_^3]Y/VU +M`[<#Y?[B_C7X-_B:`9D!T0G2"4L#2P/._,W\K`*L`J8&J`;H_>7]=_E[^6\! +M;`'[`?P!#@`-`!<"&`)F_V3_E?N9^W``;`#C`N8",OPP_$W^3?ZS!;,%./\Y +M_[K[NOMR!W$'*@4K!6_Z;OI]_WW_@`:!!FX`;0#%^\7[;/]M_V$`7P#B`.0` +M:P5J!20!)`&*^(OXL_ZR_E@$6`0M_2[]GON<^Y<$F02V!;8%1/Y#_HS_C?]& +M`T0#VOW<_?3\\_S-`LX"$`(.`HD`BP"(`X<#'`(=`B+\(/S5_=?]%@04!/O^ +M_/Y\^GSZT`+0`KX!O@'C^N+ZC`*.`G8'T! +MZ@#K`/SZ_/K4_]/_BP6-!?C[]OLO^C'ZW@3`-[`]G[U?M7]EGV10)&`MT&VP;L_._\S?[)_JH'K0?^!/P$+?XO_LD` +MQP"B`:,!C_V/_5L!6@')`\L#R_S(_&C_:_]2!$X$[?CR^*;UHO45`A@".00W +M!,7ZQ?JN_[#_\`?N!\D`RP"W_;7]8@1D!*\"K0*O_K'^S0'+`9,!E0$)``<` +MQ`#&`+``K@!R^W3[U_O6^UL#6P,3`!0`P_;`]CG\/?S,!OL'_`G\ +MH@&@`=\!X0%L`&D`,@0V!!$"#0+C^>?Y=OYS_IX&GP:K`*P`VOW9_5X%7@4H +M`RD#.?LW^]G^W/Y'`D4"`OH#^E;\5OP1!1`%*O\K_TC\1_SR!/0$R@#(`*SX +MK?AD`6(!70=A![W^M_XD_"S\^P3S!-X!XP&C_*'\A@.&`Q<#&`,.^P[[HOZA +M_BH"*P+=_-S\NOZ[_G4#=0-__G[^./LY^T@%1P6A!*($GO>>]SSZ//IL!FL& +M@P6#!;3^M/X0`!$`5@)6`BO_*?^._I+^_P'Y`;S]POVX_;3]MP2X!((`A`"C +M^Z#[[`+P`D\"3`+W]_;WU_S;_`T(!PAB`&H`%_@0^%<"6@)K!6H%?/Y[_G$! +M=`$Q`RX#D/V2_:_^K?ZW`K@"-O\V__3]]/WR!/($?0)^`A?Y$_FZ_L'^`07X +M!('[B?MK^&;X]`'V`;\%P`4U`3(!N?^[_U0`5``?_1[]*``I`%,%407'`,H` +MZ/WF_?P#_0-O`V\#ROS(_/W]`/Y,`DD"./P]_/'XZ_B0!)0$9@1D!%7Y5?D+ +M_0W];01K!',`=`"K`*L`DP.1`^+]Y?U;_5G]Y`;E!H,$@P0T^S+[A@**`B@& +M)`9N^W#[3OI-^KD!N@&$_H/^:_IL^IH`EP#N`_(#-?XR_G#^0`9(!90!A`,`#Q0/S_^[_:OUN +M_98"E@)I`&4`]/KX^F__;O\3`A("0_M&^]W\V?R2!)4$R?_(_P?Y"/DO`BX" +MG0>>!V/^8O[1_M/^.@8X!J\#L`-;`%L`*`,H`W/^=/[5^=/Y6P)=`E<%5`4Y +M^CWZ`?S^^P<&"`96_5?]E_>3]U$"5@*#!'\$R/S+_)D`EP!Q!G(&4P)2`H$` +M@@"[`KL"]?[T_I#^DO[9!-<$9@%H`23[(OM3_U3_E0.4`XC]B?VR^K/ZL@*P +M`E4#5P.9^9;Y'OPA_/0%\P5A`V`#3O]/_Y`"D`*G`J8"7P!A`/L"^0("`0(! +MKOFQ^4S_1_\:"!\(A0"!`#?[.?O]`?T!B/^&_^7XZ/C___S_JP6M!1S^&O[A +M_./\@@5_!:D"K0*W_;3]L`*Q`I4`E@`3_!#\D`.3`Z0&H@8[_3W]UOO2^Z\# +MM@-M`F0"`/P)_$K_0O]^`8,!H?R@_#'^,/X/`Q$#I0&B`<[^T/[J`>H!!@(% +M`H/\A/Q)_TG_F`67!<7^QOXZ_#G\A0:%!C@%.04?_!W\E?Z9_M8!T`%-^U7[ +M]/WK_48&309<`%<`6_I?^GX"?`*3`I0"R?O(^Y\`H``@`QX#FOR<_)#^COY$ +M!T8',P(Q`G'[<_OU`?(!+P0R!!?^%?[;`-P`7P)?`K?ZMOJE_*7\9P5H!6P" +M;`*6^I3ZT/[4_D,#/`,R_3K](OP<_,("Q@)+`DD"@_^"_W@#>P,D!"$$=OYY +M_A7_$__0`M`"7?U?_4#]/OT)!PH'-P(V`BWX+OC>_][_&`08!)_YGOFF^J?Z +M`@4"!=8`U@!K^VO[;@-M`P@%"@67_I7^$P(6`C,#+P,@_2/]U@#5`+P%O`5X +M_7C]N_F[^1\$'P1K!6L%D?J1^F/Z9/KD`.(`>OU\_6'\8/PD`B0"60):`NO_ +MZO^@`I\"D0.3`Y_^GOZ(_XC_'P0@!(0`@@",_8[];@1L!`@$"@0L^BOZ_/K[ +M^G`"OYG/Z=_N'\W_S4 +M^];[]P'U`:8#J`.1_Y#_1@!'`!(##P,%_PG_\/[M_B`%(@6T`K0"+?TK_5P" +M7@+3!=$%J/VK_4_Y3/E;_UW_[O_L_Z_ZLOK)_L7^)0(I`J3\H?R/_I'^[P3O +M!*T`JP!H_6K])00D!*8#IP,5_!7\D0&0`:`'H0$!C@"2`"[^+?Z+ +M!(H$FP6_L,&PP;N_^__\/KP +M^DX#3`._`L$"QOG%^:G\JOP]`CT"*?\H_WG_>?^%!(4$ZO_J_X;\B/P1`PT# +M-`$Y`7;[`1W!(O\C/P1_Q#_I0*G`H7\A/R^^[[[G`&;`4[_3__Q_/'\<@-Q +M`X4!AP%8^E;Z>/]Y_\H#R@/H_>;]^?W]_5T%6`7R`O<"R/W%_;,"LP(J!"L$ +M=OUU_3_^/_[H`.H`(/P>_!7\%?Q*`DH"G@&=`5O\7?QJ_FG^*`(H`F[^;OX$ +M_0/]Q`'%`88!AP$$``(`%047!:<%I@4(_@?^C_V1_;D"MP+__0'^"OL(^V\# +M<0//`^F#ZS`'(`:K_K_]I^F3Z[O[R_LD` +MQP!2_E+^4P13!,D%R06T_+7\U?_3_U8'5P<"``(`B_J+^G4`Y0`G!B8&Z/_H_^/[X_OS`O8"X@/=`Y7\ +MFOP]_SC_T@/5`Z;]I_VD_:']"P0-!"+^(/[O^/'X1_Y&_K__O__A_.+\9P!D +M`,,#R`/J_N7^V_W>_74# +M`CT`/0`:`!L`S@/-`Q@`&0`"^@'Z;_]O_QP$'035_=7]W/O:^\X!T0'I`>4! +MFOR>_!K^%_X>`2`!5?U5_63]8OV?`:,!J?ZB_N+^Z_YR!6D%P`'(`;7[L/O9 +M`-P```3]`_[[`/S:^]C[PP3'!)\!FP&*^XS[:0%H`04"!0*`^H'Z#?P,_,W_ +MS?_H_>G]N?^X_^4#Y0,__T'_F_R8_'`$/_P_?;] +MEP.1`R\$,@0'_0;]3/U,_4\$4`09`1@!K/RK_%4#5P/0`\X#9_UI_8'_?__L +M`.\`Q?K!^LOZS_HK`"@`COZ0_BK]*OUT`W(#6P)>`I'ZC_I:_UO_B@6)!_QY_-0`U``U_3;]+OLL +M^X'_@_\I`"@`?_Y__OH`^@`K`RL#7_Y>_BS\+?S5`M0"-P0W!)/_E/]H`F8" +M(00D!&G^9?XF_BK^LP&P`?G\^_RZ^+GX&``8`$H"2@*<^YS[@/Z`_AL#&P-( +M_4G]W/S:_#4#.`/G`>,!;?]Q_T8#0@-M`W$#2_Y'_M$!UP%)!4$%FOR@_(SY +MB/D%`04!KP*U`@3^__U+`D\"7099!L_^T_Y/ +M_DS^4@55!1H`%@`3^A?ZA_^#_VX"<@+G_>3]*OXK_K@!N`%>_EW^C?R/_-8` +MU`!6_U?_J/RI_`T""@+K`^\#*0`F`)T!G0'V!/H$G?^7_^7YZ_F?`)L``04" +M!9+^D_XR_3#]Z`'K`8,`@0#"_`2(!G/R;_+X"O`+M`O$"^OOV^V7^9_Y$!$0$=`!S``G^"_[. +M`LL"Q__(_T'[0_M)_T7_"P`0`.;\X?Q[`'X`'@0>!&4`9`!A_F+^K`*L`ND!^P#]`!0"$P)+_TS_VOS:_)<`E`"F`:L!Q?O! +M^Z_\L_RH`Z4#9@%F`;#]LOUF`64!%P$7`=3\U?R5_Y+_B0.-`T;_0_\R_C/^ +MF@2;!!D#%P.Q_K/^>P)Z`OX`_``I^BWZZ_WF_3@#/0/S_N_^-?TW_?4"]`+5 +M`=8!`_P#_-C^U_X4`10!>?UY_;;_MO\F!"<$[@'O`;X`N@"2`Y8#$@(/`C/\ +M,_P7_AK^Y`/@`^#_Y?\7_!+\.@`^`#\!/`%6_E?^BO^+_____?]+_$[\C/V* +M_8P#BP-6`5@!%?X3_D,$1@1M!&H$D/Z2_F0!80$)!`T$U_S2_,+ZR/K@`MP" +MR@/+`W+]_V'_T?O/^TC_2?]/ +M`U$#^@#W`-S^W?Z5`)8`&0`6`!C^&_YN`6X!/P,\`P$`!@"O`:H!NP2]!*#_ +MH/\M^RS[%/\6_WD`>`!L^VO[/_U!_=<#U0/0`-$`Q/W$_?X`_@`A_R'_W/S< +M_)\!GP%<`EP"Z/WH_>4`Y0#.!^^'[-`$Q`3H%/`4J +M`"D`U_[8_HH#B0-,`DT"5_Q7_'S]>_V3`90!-_\V_WC^>?Z7`I8"_P```>'] +MXOW9_]7_O_[$_JW[J/M^_X+_=P-W`YC^E/ZW_;S]^@3U!`@##0-X_'3\4@!5 +M`%@"5@+[_/S\1/Y%_DT#20-\`8$!V?[6_C$!,P'M_^S_A/R$_%+_4/]5_UK_ +MUOK1^I3^E_Y/!4X%LP*R`A7^%O[V__;_80%@`1S^'?[Y_OG^J@*I`DP`3`"Z +M_[O_F0.7`_0!^`%$_3_]W/W?_3__/_^6_)/\?YY_KG\NOQK_VS_T_W1_7W\?OSV +M`/4`1P%(`=/\TOQ#_D3^.`$W`?'^\OYH_FC^S@',`:\`L0"O_J[^H`2@!%(& +M5`;R_N_^&OX=_O4!\@%>_F+^7?M8^UK_7_],`$@`G/V?_='_T/\-`0P!F?R: +M_'O]>OWM`.\`V_W:_9G^F/ZH!:D%3@5.!5;_5O])`$H`6`-6`X/_A?^._(S\ +MV/_;_PL`!@!G_6W]:_]F_[``M0#I_>7].?T[_1C_%O^W_;K]K?VK_:P"K0+% +M`L0"3?Y._B@"*`+_!O\&*P$K`='\T?P2`!(`$``/`/K\_/S,_\K_Q`'&`<;] +MQ?U`_C_^Y`#E`)3\E/PK^RG[S@#2`'``:P#"_4!B?Z'_CD` +M/`#'!,0$$0$3`>K\ZOQU`'0`R@#*`#+^-/XZ`38!Q0+*`O+^[_Z?_I_^V@'; +M`5'_4/^Q^[+[:?YI_L#_P/\F_27]5_]8_\`"P`+,`,H`&_\?_WH`=P`0`!$` +M^OWY_1@`&0`"`@$"UO[9_G[_>O]?!&($O0&[`?'[\OME_6;]K/^J_Y']D?UN +M_F_^/@(]`LW_SO^\_;S]E0&4`9L`FP"4_)7\__[^_KH`NP!$_D3^V0#8`)H$ +MFP24`)0`POS"_#4!-`$S`C4"T_S0_#/]-OT0``\`Z_[J_F__^IOZO/R^_-S_VO_A_.+\5OU8_=8"TP)/`E$"U?[4 +M_K\`O@!G`FD"S_[._JO]J_U%`40!+0$P`04``0#>`>(!=0!Q`&?\:OQE_6/] +M'/\?_P[\"?S:^^#[Q@"_`,L`T@!\_7;]V0#=`%\#70,F_R;_@?Z"_LD!R`%0 +M`%``2_],_[4"M`*6`90&0_)#\0/M$^[3^K_XZ_CS^%_P8_`S_"?_^``,!A/Y__E/_5O^A`9\! +M#O\0_RC^)?["`<4!QP'%`5/_5/]3`E("201)!`;_!?\:_1W]'``9``#^`OX< +M^QO[_/W[_1__(O\%_@+^OP#``/P!_@&2_8_]X_SF_#H!-P%V`'@`Z_[J_M\" +MX`+X`_<#X?_B_[#_L/_N`>X!%_\6_V#[8?MT_7/]Z?_K_[#^K_XS_S/_J_^L +M_Z;]I/V._I#^_Y__&_T9_7?^>?ZJ`*H`R?['_E/_5O^P`:P!X_WH_0G[!/O. +M_]+_A@&#`1'_%/^S`;`!>01\!*8`H@`D_BG^_0#X`'0`>``Q_2[]>?Y[_B@` +M)P!'_T?_M0"V`$D!1P%<_6#]+_PJ_$@`3``"`0`!_OS^_&'^8?[@`N("K`&I +M`3@`.P!1`DX"PP#%`!#]#OVI_JS^^`#T`$G_3O_;_];_50)9`J?_I/_H_>G] +MV@#:`-_^WOZL^J[Z0?X__JH"JP)4`%0`J/^F_[`"M`+T`/``;OUP_:C_J/_G +M`.4`G_ZB_CP`.0`]`D`"*0`F`,[_S_\4`A4"L/^N_^'[X_L'_@7^G`"=`,'^ +MP?ZW_K;^*P$L`6``8`"+_XK_5@%8`:0`H0`/_1+].OXW_B\",P*!`7P!>P"! +M`.$"VP*;`9\!MOVT_?/^]/Y)`$H`0?T__23])OTB`2`!!0$%`5;_5__*`,D` +M>?]\_XW\B?Q'_TG_#@(,`E__8/_<_][__`/X`[T"P0(I_R;_K0"O`(P!C`'< +M_=G]/OU#_=7_S_^;_Z/_9/]=_R`EX"5/]4_S\"/@(&!`D$ +MM0"Q`,;_RO\^`3H!H!Z@$,_PW_ +M2O](_R4`)P!I_&C\(_LE^TC_1/\S`#<`P/Z]_D$!0@%A`V,#70%8`5@`7@!$ +M`3X!U/_9_Z;^H_XZ`3L!40)2`BT`*P#6`-<`,@$S`2K])_U@^V3[P_[!_I__ +MGO]`_43]__[Y_@H"$`(Y`30!X0#D`-H!V0$W_SC_%_X6_KT!O@%\`GD"9_]K +M_Q8`$P!Z`GT"2P!(`*+^I?ZN_ZK_3OU4_2/['/N^_L7^/0$X`;/_M?]C`&0` +M#P(,`@T`$0"Y_K;^S`#-`"$`(@`<_AK^]`#V`/H#^0-L`6T!-?\T_T(`0P!@ +M_V#_WOS<_*W]L?V'_X+_W?[A_O#_[O]X`7D!>?]Y_]3^T_Z#`8,!)P`G`(_\ +MD/Q#_T/_<`-O`W0!?]X_P;[!ON]_+[\^P'Z +M`18!%0'1_M3^ZP#H`/D!_`$*``<`P__%_]#_S_]1_E+^-0`T`#D#.0-Y`7L! +M)/\B_[H`O`!S`'$`1OU'_4[]3OTN_R____W^_;_]P/U1`4\!`@($`J'_H?\B +M`"``%0$9`?C^\_["_L;^0&P`*X`J0*L`BX"+`)"_D+^-/XV_FG_ +M9__"_,+\0/Q"_!(`#P`D`28!P__$_X4`@P`I`"H`_?W]_:3_H_],`DX"&@`9 +M`"3_(_\,`PX#'00:!,,`R``]_SG_7O]>_]/]UOVP_:O]:O]P_XC^@_YZ_7[] +M/``X`(0!AP$Q_S#_X?[@_B<`*@`B_Q__`O\$_[P"N@*R`[,#\0#R`&`!7P'$ +M`L0"LO^R_Q_]'?T^_D/^0OX]_KK]O?T(``8`V`#9`';^=OXC_R/_O0"^`-_] +MW/U$_4C]W`'9`2`#(`-R`'4`4P%.`8H#CP,5`A$"^O_\_T[_3O^B_:#]A_V* +M_3L`.`#]_P``JOVH_?K^^OX"`0,!Y_[F_J[]KOWF_^?_T/_._UC^6_[3`=$! +M1`5$!9T"G@+P_^W_E`"9`$L`1P#)_LO^V_[;_GO^>OXE_B7^`@$#`0P""P+- +M_<[]:_QI_-3_UO]O_V[_*?TJ_30`,P!T`W0#J`&H`?H`^P"]`KL")0$H`2#_ +M'?\```(`//\\_\']OOT7`!T`IP&@`1;_'/^)_H;^+0`N`'G^>/Z&_(C\/O\[ +M_^$`XP`4_Q;_2P!&`,0#R0/_`OH"(0`E`)C_EO\)_PK_D?Z/_JT`L`""`8`! +MM/ZU_@S_"_\*`@H"%0`7`$G\1OQH_6O];_]M_\G^R?X*``L`)P(F`N4`Y0#V +M__?_N`&V`<@`R@!=_EO^HO^D_[L`N0`K_RW_+``K`&\";P);`%P`-_XV_J+_ +MH_\?_Q__@?R`_"/^)?X1`1`!9`!C`!P`'@##`<`!]0#X``O_"O^F_Z7_E/^6 +M_P[^"O[:_][_:@-G`Q0"%@(E_R3_UO_5_Q@`&P`Y_C3^,?XX_AO_$_]!_DG^ +ML?^J_ZX"LP+S`/``I_VJ_6W_:__1`-``6_Y=_J3^HOYX`7P!_0#Z``L`"P`E +M`B8"'0(;`NW^[_YQ_G'^4?]0_T'^0_[M_NK^^P#]`!,`$`!`_T7__`#X`/O_ +M_?^1_)#\OOV^_3`!+P$E`"@`#_\,_R@"*0*&`X<#[`#I`$S_3O\I_RG_K_ZN +M_JW_KO\(`0_1W].?\Z_VW^:_Y]_H#^ +MV`'5`38".0(%``(`)`$F`9,"DP(Y`#@`^?[\_G__>?^9_J'^E/^,_Y$"F`(9 +M`10!(/TC_5_^7?YR`',`6/U8_<#[OOM__X/_=`%N`90`F@".`8D!-P([`I,` +MCP`2`!8`?P!Z``O_$?]U_V__2@)/`G\!?`%A_F+^8_]B_Z0`I0!^_7[]"_P, +M_)'^C_[R_O/^__W__>L`ZP`4`Q4#W@#:`.#_Y?\K`2P`! +M``0`F@&7`6T";@*3_Y7_??YX_@@!$`&)`8$!,0`V`(T!C`$>`AT"K/^M_\S^ +MS?ZG_Z/_O/W"_1S\%OPJ_B[^7O]>_Y;^DOXE`"H`!0(``C@`/`"`_W[_%0$7 +M`3$`+@#0_M/^C@&,`3(#,P.+`(H`]__W_Z_5\`8@"%`8$!]_[Z_N_^[OZ=`9T!>P![`.;]YOVJ_ZG_'P(A`HT!BP&T +M`;4!-P(W`A$`$`!0_E#^+?\O_YS^F/[:_-_\-/XQ_O+_\O\;_Q[_MO^P_[_QW_ +M>OYZ_F'^8/XT`#8`ZO_H_ZW^K_ZI`*<`EP&8`?S]^_V/_)'\/``Y`$,"1P)` +M`3T!NP&\`7\"@`([`3D!R0#*`*$`H@`Q_C#^5_U7_>;_YO^.`(X`%?\6_P<` +M!@!V`'8`[?WN_<']P/U^`'\`\__Q_W'^<_XA`2`!AP.)`WD"=0*Z`;\!KP&I +M`=S_X?\:_Q?___\!`-W^W/X!_@'^T`#0`.D!Z0%]_GW^D?V1_>S_Z_](_TK_ +M*_TJ_1O_&_\*`@H"01X!-@!VP%V_W+_)!TH'8SQB?$([`GLN0ZY#KOZN_I/]T_W&`\8#S@&.`:T +M[+3L\P'S`;4!M`&G]:GU1!%#$:,.HPYO^F_Z^?KY^A[['OL._`_\-@(T`MD+ +MV@L3"Q,+Z/3I].+TX?0P!"\$$0(3`I,'D0?;_M[^>?-V\[W_P/_#`[\#KP*T +M`C3W+_=&^$SX[17F%7P.A`[Z\O+RM?>[]X#Z>_H!!`8$YP_D#[+_L_^D^:/Y +M/?P^_&H":0+U`_<#JO*G\FWW;_>."HX*0`I`"E\`7@"]_;W]>_A\^(_YCOFR +M#K0.=@YS#B3U)?6I]ZKW)`PB#(,`A@"A^9WY$@(6`FO\9_R8`YT#`P+]``-T`A0:$!D@$2@2S_['_ +M,@$T`4H#20/@]N#VF/N9^]P-V0T]`T(#F_J7^OP&_@:L]:WUDO&.\9$0E1"" +M!(`$E/J5^IL#G`-B_6#]>OU\_>3XX_AW`74`)8`S07- +M!>H,Z@Q\^GWZSOC,^`T!#@'G`^8#KORQ_$3V0/9_!8,%"`D%"7WT?O2>^9[Y +M_@3^!*'[H/L5"Q@+L@FN"0OT$/2)_X3_\07U!=/_T/\[_3W]&P4;!70(@'*^,7X+OHR^M__ +MW/]#`D4"COZ._BG_*?\B`2`!4_I5^M/^T?Y/"E$*Y@/F`TH`1P!'!4P%O?VX +M_?GX_/CO^N[Z/`4\!8$&@@9^^7[YJ@6H!58$600*\`?P5/E7^0`!_@`2_Q+_ +M*`HJ"AT+&@O*_LW^E/J2^A+[$OL1_Q/_XG\R?SP@7!!7X%?P68`Y@# +M.0@Y",7YQ?F;^)KXY`3E!%,`5`"P`*T`'P(B`JD%I@7G`>D!E?26],<`Q`"B +M!:0%T/G.^3<'.0<[!CL&^.WV[;SZP/JC!YP'??J$^K7]L/W=">`)60U9#:D! +MJ`'_^/[X1_I)^NGWYO<%`PD#C`N)"V$"8P+3_=+]NP*[`AS_'?_^[_WO8_5C +M];$)LPDQ!"X$N_V^_:4,HPS`_<#]2?!+\*$(G@@<"!X(//X\_H,"@@*``8(! +MT__0_PGW"_<1^Q'[$@<2!Y8`EP!1`$X`WP3A!.GUY_42\Q7S$P,0`ZT"L`)2 +M`TX#:`YK#BX*+0J_\[_S-?,U\S8&-@8D_B/^Y_SH_)D)F0E6!58%20!*`/?W +M]/?Y]?OUAOV%_:W^L/X/"`L(/0E`"9KWE_]FD`;``_!CP&>`-Z`Z\"K@+H +M_^C_8?5A]5+[4OO3!]('Y_OJ^]#TS/24`Y<#)P?IU +M^LOXS_C9^-?XW/3=])T#G0.B$Y\3?@&#`;KZM?IE"&H(+P$L`7#T_F#^2@9&!G'^=OZ_ +M]KOV=!\`!P@&8\97QLO:U]KP+N0NV!+H$^OGV^:H) +MK@D\!3@%@_2&]#/X,OBR^+'XM?VX_=L$V01M!&X$;01K!(+ZA?KS^^_[4017 +M!-7\SOS,`M,"W0W6#<3]ROW3]-#TN?^[_X;^A/Z.^I#Z4@-/`[X'P@>G^:3Y +M%/45];+_L_^%^H+ZC?N1^[\)NPD@#2(-"0<*!^3ZX?K.^-/XC?R&_)C\G_SW +M!_$'`=T!I@*H`F`! +M70%<^E_ZP/^]_V@+:@LW`#<`R/G'^=L%W057`U0#MO6Y]3#X+?B2`94!*_XI +M_C/^-?XJ""@((/LA^W3Q<_$0!Q$'=`-U`\?VQ?;["?T)>0YV#M?^V_[D]^#W +M0_Q(_&D`9`!,^5#YJ?^E__P+``SB_=[]IOBK^#$!+`'A].7TJ_6G]9P)H`FC +M":$)S?_-__8!^`&L`J@"N_7`];;ZLOK5"]@+#0$+`87ZAOI+!4H%.O\\_[GT +MMO05^AGZ(`86])?TI/2D],0!Q`&_`;\!D?N2^^<%Y`5^#H(.*_PH_&'S8O-=`%T` +M:_YK_IC\E_SS"/4(ZP?H!ZOXKOB$]X+W_P8!!__^^OY,\E/RG`25!,$&QP82 +M_`_\Z@/I`R3^*/[X]?/UV@3=!/L,^PS\`?H!>/=\]QS_%_]A!F8&`O;\]?;V +M_/:?"9H)]][WYO_D_P<0 +M"Q"6")((S/C/^$,$000M`BT"P/'!\8W_C?_$"<0)Q?[$_M3^UO[W`/,`<_QZ +M_![Y%OE2_%G\?`)W`E/]5OUJ`FD"\PKT"CW[._L?]R'W20=(!T4'10?Z^_O[ +MQ/[#_B@&*0;8^];[1_9)]FP#:@.*_XS_8?EA^6,)80E]!GX&;_=O]Y?YF/GB +M_.#\]O_W_T`#0`.M"*P(4PA6"%3Z4/HZ_3W]1`%"`8SQC?%Z^WG[]@_X#V0# +M8P/>^=WY<`-R`_0`\0"J]*[T\?KN^J`*H0I\`WT#=/UR_08%"06O^JSZM/:U +M]D$&0P;X!_0'5?U;_1#["?M@`F4"`?W^_.;SZ?/"!,`$3@I/"G_\??P+"0X) +M?PA\"%7M6>VP]*ST/0H_"K<&MP86_17]8@%D`=`&SP8'^@7Z#_82]@@`!0!] +M^X#['?X;_N\+\`L!!0$%F/F7^4@`20!Z!7@%]?KX^GG[=_NC#*0,E0*4`O7S +M]_-M!&H$80%E`<+SO?.^`<(!O`:[!D;\1?Q8^EKZ4`%.`=X"WP*$^83YR`#( +M`"0-(PV;`IX"1@)"`BL%+P4^\SOS"?8+]HT)C`GM!.T$WOG=^3H"/0(3!Q`' +MVO/=\P+Q__`&!`@$W@'<`;,`M@"-#8H-209-!D/X/_AG^VG[4011!'X"?0+/ +M_-#\UP;7!D$%/P7S\O?R'/T8_;8#N0,/]0OU>`!^`,`*N@HL^S'[Q_;#]JD! +MJP$M!"T$%OT6_;$%L`7#$<41JO^G_S'S-/-__WW_E/^4_Q?[&/LG`B<"?05] +M!7'_6\97Q^0?X!UT*7@HZ_CG^E`:4!ID$FP2\^;GY +M5OY:_N,#WP--`$\`P?G`^8>]Z'W +M'`T;#9X(G@@%^@?Z(/X=_A(!%0&[^+GX_?C^^*,)I`G&!\0'G/B?^&L!9P$$ +M`0@!*_,I\U?^5_[V!?@%E/^2_XL%C`5:"EH*[@'M`0/W!?=/^T[[D0.1`W7[ +M<_MS`'8`X0O@"[;ZM?J!](3T&P46!20")P*,]8WUQ_S#_$$*1PJV`[`#B_R/ +M_)H$F`2R_K/^C?N-^^X+[0L?!2$%X_7A]9?]FOVW`K,"+_LT^X_XBOC6!-H$ +M@0=^!W[Y@?F2`(\`'`4?!1KQ%O'!^<3Y&!`7$"<')@>&_XC_Z0;F!C4"-P)6 +M]%7T2/A)^%8&508"``(`G_V@_9<(E@A#_D3^N/6W]<(`PP##`,(`3/I,^G\" +M?P)-#4X-BP"*`(#R@?)V!'0$<0IT"A'[#OOB`^4#;P9M!@[U#O6S^+3X[@+M +M`N#_X?\2_1']_P3_!'H)>@D,_`W\4/E/^0@!"`%1^5#Y:/YK_FL.:0Z-!8X% +MC/J,^F8!8P&``H4"#O8*]N3WYO?T"?0)F`.7`S+Y,OGI!^H'3@1,!+?VNO88 +M_Q;_:P-L`^G_Z/_B`>0!E@23!'7^>/Y9]5CU(P(B`KL*O0I+^DGZ,/TR_&^H?Z]_CV^`T%#P7\`?D!8OUF_9H(E@C##<4-LOBS^*?PI?">`I\"=P)W +M`NO\ZOS0!=$%A@2'!-C\UOP!_`/\`OX`_O_Z`/LJ^RK[?@M^"Z\+L`NI^:?Y +MX`#C`'T%>05P]W3W$/\-__X)``HK`2L!0/H^^A_^(OYI`&4`JOBN^&'\7OQH +M"6H)SP#.`,KZROIG`V@#C/^*_W/[=?OE`^,#S@C1"#\$.P1I`FX"D02*!`/X +M"_AZ\G3R[07P!<4%Q`52^%'X*0(K`@P&"P84^Q3[6/A8^-_]WOU1`E0"60!4 +M`'$'=P=%##\,T?O6^V?[9/NX![D'X/W@_3OZ._HY!3@%/0)``N'YW?E(_4W] +MBP&%`3WY0OD%^P'[`0L$"ZT"K`(B]R+WG0.=`W@$=P2/^I#ZV@#;`(,-@0W1 +M"-,(F/F6^6C\:/Q._U'_LO2N]%K_7_\T"2X);/QQ_,3_P/\N!C(&6OE7^9OU +MG/4,`0L!>0A["#8#,P,S`S<#O@BY"!G['?OM]NOV6P=+WY/=@`E\"TP?4!X/_@_\'`@8">`)X`F?\:/SN_>W]5@=7!]\( +MW@@D^R3[,_LT^Q4$$P0]^4#Y4OA.^'0&>`84`Q`#Z?OM^T(!/P'C`>4!E?B3 +M^.?ZZ?I\"GH*(P_E8`5P"N!JX&T/[._E?X6OC&`,,`L/VR_4+Y0OD7!!8$5@=6!PH` +M"@#!_,#\[_[R_JW\JOSG^^K[P0F^"?<)^`G-^LWZ40%2`>X$[033]=3U*?@H +M^#D%.07.!09Y!B3^(_[@]>'UK`2L!*P%JP78 +M^=OY/@,Z`[H)O@DT_C'^Y_;H]MW]WOW"`[\#L?VU_?D!]0'0!]0'-_HT^JSZ +MKOJ9!)4$1_I,^I'YC/EE"6L)/@DY"2;^*?[`_[[_Q03&!*_ZKOKZ]_OW30=- +M!U@$6`0L^BOZQ0'&`=(!T`$<^"#X?_IZ^O`#]`/-`LD"1?U)_?T$^00A!"0$ +M5_A4^/D!_`$5"1,)@OZ#_C,#,0-\!WT'ROC*^-CSVO,B_Q__J@2M!#C],_WV +M_OO^'@@;"$[]4/W4]=3UJ@"H`.(`XP"\_[S_X`K@"@@+"@OI_.;\5_I:^E8# +M4P,2_A7^7OA=^#@%.`45!!4$"?@)^&;_9O\=`1T!R_?-]Z[]JOWB".4([P3M +M!,'YP_E)`$@`P0C""'/\H_J?^N_6^]G!XL#B0-H^VK[4OY0_F4%9P6N_:[]9OQD_%P)7@GO`^P#!O@) +M^&W\;/P<_QO_)?LF^V8`9@`/"@X*F@2;!._[[_LL`BH"%/\8_WCU=/7G`.H` +M'`D:"5@"60*-`8P!;P%Q`6#[7_LA^"'XU/_5_^4%X@45_1K](0`=`,@*R@H[ +M_CO^)O`!X`)WYG?GF`^4#5P97!L+\POPN_##\BP*)`AW]'OT' +M]P7W?`.``UT&5P9J^W'[O@&V`<\$U@1!^#[XG?J<^ID&G`84!P\'4P-9`U,& +M3@;@!>,%K?BK^._V\O;B`=X!LOVW_=3]S?TZ"4`)OP&\`8WVCO9__G[^2P1- +M!+OZM_K3^]C[&@L5"WH'?0=Z^WK[[0'K`;H#O0,>_1K]N`*\`D`%/@4[_#O\ +M$_D3^4(`00#1`=(!I_JH^@(!``$B""4(&_T6_4G]3_WI`^(#`!\`(`4@!;?[MOL@]B+V%@$2`?H"_P*M_ZG_M@6Z!60" +M7P)$^4GYW_S;_!L#'@.$_H3^I/RB_'8(>`CV!O0&I/FE^04`!@`"!0$%B_N+ +M^QH`&0#S!O4&Q?W#_Q!Y,(E`C%_<;]&_P8_(("AP*.^XG[\_;V]A8&%@9U +M"7,)0O]$_TT"3`+J`^H#]?KW^GSZ>OK%`L8"R@/)`V3\9/Q'_TC_E025!,W[ +MS/OS_/7\.`8T!N7_Z?\;_1G]V`/9`\0`PP"W^[C[X@'A`?<&^`8"`0,!3@!* +M`*P&L`;T_?']%/07](/]@/TN`S(#S/[%_AD!(0&;!98%/P%!`4;[1?N$_X/_ +M```"`.7ZY?I)!4<%B0N+"U8`5`!__X#_@@.#`Q;\$_RH^:WY9`%?`?8"^0)% +M_$/\U/W6_=8#U0/.^\_[,_HP^LL'T`>:!90%LONX^_<`\P"W!+@$B?V+_:_] +MJ_U]!X$'J@6G!8'^A/Z?`9P!+OPQ_&3S8//N_?+];`9I!A``$P!<`%@`)`8I +M!G4!;@'_]P?XZ?SB_-T%X@6W`+4`?@)]`A@*&PIS_V__>/A\^"4!(@'#`<0! +MEON7^Y3^D?YC`V@#D/Z*_G_\A?Q\`G8"$0`5`-C]UOWH!ND&:@1J!*#YG_D) +M_@O^205&!0D`#`!L_&K\,04Q!2<&*@:V^K#ZK?RU_'0`;``Z^4#Y/_\\__\( +M_@A_`H,"MOZQ_OT#`01U`7,!!?D$^<'[P_ME!&0$80%@`6<`:`"B!J(&=/YS +M_B/W)/=G`68!>01Y!-[[X/N<_IG^DPB6",0"P`*R^;CYB@&%`8@$B@3*_?Y[_B[_*_]="V`+2P=(!^#[X_N!_7_]IO^G_SCY./F3^I'Z0`5$!6H# +M9@-+_$[\G@"<`$8`2`"F^Z3[E0&6`<`%P`4/`0X!/YE`&2 +M`8$&A`:<`9D!XP#E`)X$G038_-C\+/@M^,8$Q`3<"-\(0``^`&@`:`"O`K`" +MO?R[_$+Y1?E>_ES^TP'4`<[_SO^2`Y$#'P0?!.GYZOG&_,7\M06W!:+_G__B +M_>;]K@6G!94#G@-$_#S\E_Z<_L\"S0)/_D_^Y_[I_F0&8`9)`$T`5_A3^.#_ +MX_^C`Z$#V?W:_>3_Y?\%!@,&-`(V`G/\P!Z`%@%607+_,K\ +M;?AN^*D"J0(L`RL#D?V2_=$!T@$>`QP#M/NV^X7[@OLH`RP#Q@'$`6__;O_B +M!>0%0`(^`DKY3/F*_XG_T@3/!$G_3_\!`/K_3P96!C`"*P+;]]WWH?J@^J,! +MI`'#__U<`EX"6?U9_;[YN_E,`U`#'`0:!-/[TOL4_A?^90)C`G[\??RC^J;Z +MV036!`P&#P9;_5C]A0&(`0(%_P3(_,K\EOZ6_J`$GP26`)<`I?^E_U\#7@,' +M_PC_6_=:]ZG\JOP0!1`%VO[9_LG\R_P5!A(&9`)G`@GZ!_H$_P7_T`+0`G/_ +M<_^,`HL"9`AE"*8!I@'A^=_Y2/]-_R8`'P`3^QO[`@'Z`&\$=03%_L+^4_Y4 +M_F4`90"L_*O\I/NE^Z($H@2%!X0'!?T&_53]4_T3!14%S/[*_O'[\OLE!"4$ +MS0/,`ZC^JOYY_W?_KOZO_LKYR?F@_:']"P<+!Q0#$@,N_3'],P(P`EL`70"K +M^:OY-?XS_FT#<`,3`1`!GP&A`=L%V@6Y_[K_!?<#]PK_#/^/!8X%;OUN_=O^ +MW?XK!B<&]@#Z`('\?OS._]#_6P!9`*_]LOT(`00!'00B!,#\N_S9^MSZZP+J +M`J``GP"$_(C\<@-M`VD$;00A_1[]^OW\_>\"[P+5_M3^W/S=_*X&K0:2!I,& +MQOO%^V7]9OU$`$,`6/I9^H;[A_NG`J,"V@+?`N+_WO]5`5@!5@!4`"/Z)/I% +M_D3^.P8\!I4!E0'.`,T`70=>!U`!4`$_^3_Y-OXU_MD"VP(*_@?^+/TP_7\" +M?`*:_9O]A_B'^'\`?P`9`Q@#YO[H_ML"V@+U!/4$2OY*_I'\D/SW`OD"9`-C +M`ZK_JO^R!+($V@3:!%3Y5/G#^<3YSO_+_Y7ZF?H!_/[[.P8]!I($D01M^VW[ +M:_UL_4T#3`.;_IS^K/VJ_?X%_P6A!*($Z?_H_U4#5@/F`.0`$/H1^KG]N/T* +M`PT#X_W@_4#Z0OH=`1H!?0"``%SZ6_JH`:@!M06T!6[^_"'\EOB2^'O^?_X^_CO^"/\) +M_[0&M09]!7L%_?O_^S7\-/RF`J4"P?[#_CO\.?PX!3D%<@5S!?G]]OV6_YK_ +M40!-`"+[)?N6_97]G`2;!(`!@@%_^GWZ`_\&_YX#F@,T_C?^@0!_`'@%>06K +M_ZS_P?V^_;$`M``]_#O\,_HU^BT#*P/R!_,'N/^X_[K\NOQ,`DX"XOW?_?OX +M_/@B`2,!$04/!?X``@'$`K\"B`2-!&O]9OU9^5[Y/0`X`%(!5P%!_#S\"@$0 +M`9(#C0,J_2[]T/[-_D\"4`(?_B#^5OY5_J0$I01#`T(#YOOE^QO_'?\?!AT& +M7@!@``K]"?UP`VX#M0"Y`$KZ1OK7_-K\H/Z?_KO\N_S>`-\`I0:C!D4"1P(U +M_3/]R@#-`!O_&?]4^U3[]P+X`C@'-P>;`9L!]@#X`"0#(0,)_0O]'?<=]\#] +MOOU#`T<#]/WP_7S_?O\J!"D$5OU6_6[[;_MI`VD#A0.$`RG_*O^5`9,!7P-B +M`X7^@OZP_K3^301(!#$!-@$O_BK^'@,C`PG_!?^`]H+V<_MS^TX#3`/M`/$` +MGO^:_X\%D@4Y`S4#D_J8^O7]\/TO`C0")/X@_ED"6P)-"$P((P(D`M/\TOR2 +M_Y3_4O]._Q#[%/O:_=?]F@*=`J'^H/[%_L3^101&!`3_`__O^N_Z5P-8`U0% +M4P4]_3[]3_U/_=D#U@-L`6\!O_V\_3X#0@-2`TX#%_X:_M``S0#9_]S_G?B< +M^,S[R_LO!#`$<0-P`][_W__F`N<"/`(Y`HOZC_I#_#[\LP&W`5_^7?YE`68! +M9PEG"10#$P,;^AOZ\_WU_1P"&0+)_X%[P5-_D[^P_W"_8``@0#(^\?[3OM. +M^]\!X`%7`58!F`"8`(@%B04X`C8"*_LN^Q#_#?^5!)@$&P`8`(S]C_WN`^P# +MG`*<`@K\#/P]`#D`<@)W`I?\D_P-_A#^60)7`JW^K?Y9^UG["@`*``<#"`-' +M`$8`!0,%`]$%T06$_H/^D/R3_`8"`@)>_V+_Q?W!_=L$W03#!<0%X?W?_5#\ +M4_R;_YC_^OK[^K+YL_FN`ZT#8P5E!4O_2/]``40!^@+V`K_]P_V"_7_]60-: +M`V\#<`/&_\3_=0)W`M8!U0$M^RW[#/\,_X8$A@0!_@'^W_O?^S@!-P$S_S7_ +MY_KE^O+_]?^W!;,%'P(C`D\!2@'[!``%D_Z0_@WY#_FZ`+@`TP34!,\`S@!9 +M`5L!DP.2`^_^[_YO^V_[=/YS_@[]$?WK^^C[.`4Z!;L&N@;M_.W\O_W`_?P# +M^@.>`*$`'?T:_7$!=`$G`R0#R?[,_G(`<``O`S$#OOR[_.[\\OQ$!4`%/0)` +M`HG[A_M__H#^R0#)`$S^3/[(`,<`V03;!/8`\P!#_T;_WP/=`W'^<_YI]V?W +M>/]Z_Q8&$P8]`4`!"``'`!D$&02N`*X`7?I=^H+^@OYP`G$"ZO[I_K8"M@*: +M!IP&&?X5_@+[!OL:`18!>`!\`&7]8_WO`/``3P),`C;].?UW_G;^Q@/'`S?_ +M-__$_`5@! +ME`:8!AL`&0!R_G'^Y`/H`__^^_ZC^:?Y7?]8_T,!1P&>_)O\N/Z[_CD#-P/N +M_N[^UOS7_)<#E0._`\(#*P`H`',$=03>`]X#[_SM_&S^;_[9`M8"A/^&_V'] +M8/T9`QD#40)2`D/Y0?DN^S#[0P%!`2W]+OV*_HO^[@;L!O($\P01_Q/_H`"< +M`'`%`_T#__@']`=4!UP'-^<&Y@:#_H3^ +M\?OP^Y`"D0)S`',`9?QD_)D!F@%@!%\$`0`#`+H`MP#D`^<#^/SU_'+Y=OG= +M`MD"8`5D!3C_,_]H_V[_<@!L`-#\U?R4_9']\P'T`0\`#P"9_IG^OP6^!<@$ +MR@0Q^B_ZK_RP_.8$YP3D`>`!+``R`#$$*@0=`20!(OL=^QW^(/[Z`?@!C?V. +M_4'^0/Z:!)P$C0"+`(C[B?O^__[_`@$!`/]_@/]`]<#UP/% +M_L;^V`#7`$H"2@)J_&K\`OP#_*X#K0,^`S\#W_W=_;$`LP"]`+P`S?O-^_O_ +M_/]#!$`$._\__SG^-OY4`U4#O@#``"?Z(_I[_W[_K0:M!F$!7P%I_FO^I@*F +M`LS_R_]A_&'\F`"9`$T!2P%J_FW^`6#^6OX'_@S^-P(S`AG_'/\(_`;\60!;`"\!+0'B^^/[K_ZN_B\& +M,0;X`O<"-?\U_P\#$`-/`$T`LOJT^CD`-P#_LV`3(! +MY`+G`MOZV?I,^D[ZOP*\`A(#%P.(_X'_+@0V!.`#V0.Y^[W[@OV!_?8!]`&/ +M_93]-_XQ_@L&$`:-!(H$2?Q)_%_\8?RA_Y__[?SN_'K^>_ZB`Z`#8P!E``7_ +M!/\Y!#@$J0"L`+3YL/DA_R7_I`6B!7[_?O]Q^W+[6@%8`2O^&_('\"0$/`?H#]@/'_D#ZP-@`5P!4/M5^XO]AOVV_[O_UOS1_+#_ +MM?^&!($$!``*`';^;_[[!`,%"@,!`ZG\L?R;`)4`WP/D`X[]BOV<^Y_[Q@#" +M``W_$?]A_%_\"`,)`Z$#H0,>^QS[U_O:^Q0!$@%N_F[^&/X:_DL$2`3^!`$% +M)P$D`4\"4`+W`/@`I_FF^5;\6/S(!,0$&`$<`83\@?Q?`6$!+`$L`3;[-?N; +M_9S]'0(<`O']\OV+_8K]H02C!&("7P+8^]K[E0"3`"`%(@7D`.0`:/]H_X4` +MA`#Z_/O\>_QY_%<"6P)1`4T![/OP^SL!-P$D!2<%_^+_:`)C`LX"T@*G^Z3[0?M$^Y8"E0(K`"D`!?L( +M^UL"5P*S!K8&9O]F_VO]:/W!`<4!+@`I`%;^6OZ*`H<"P">_IG^'P,D`],`S@#]_`#]`@(" +M`BT#*@.#^H?Z?/IZ^I`"D`(D`"0`V_O:^^@`Z0#B`>(!_OS]_"W_+O]G`F<" +MQ_['_@8`!0`7!Q<'Y@/G`Y[\GOQ=_UW_=@!U`,[[SOL;_QS_C@..`R3^)/[` +M^[_[W`'=`7\`?0!N^7'Y=OYT_OD%^@5=`5T!;OYL_E8"6@)>`%H`.?X\_L8" +MQ0)W`G8"2/U+_<'_OO\6`Q@#>_QZ_!'Z$?K4`=0!;`)N`H#]??V!`(0`HP&@ +M`1?\&?S^_?W]/P1`!),`D0"__,+\E0.3`^4$Y01;_%S\B_R)_,X!T0&Z_[?_ +M>?][_[L"O`(N_RK_`OP'_&8!8@%6`E<"2_M,^T;]1?T8!1@%A0&&`6G\:/PU +M`#4`&0`9`+K\N_R;`9D!LP2U!"3^(_X&_0;]\`/P`XX!CP']^OSZD/^0_]0# +MU0/U__/_B/^,_W0`;P#>^^/[9_QB_`P#$`.6`94!X_OA^UH`7`!&!$4$-/TS +M_0O\#OQ@`UP#6P)?`D?_0_]>`V(#9P)C`G_[@_N/_8S],`,S`S__//^*_(S\ +MWP'>`<$`P@`\^SS[-/XS_JD`J0#4_=;]$`$-`9D%G044``\``/P$_(4"A`*( +M`X8#T/S4_+?_LO_2!-<$1_]"_X7\B?RM`*H`0OY$_K#ZK_IY`'D`>P-\`V'] +M7_T6_1?]P`+!`GD`=@`2_AC^$@,*`V`":`(R_BO^!P(-`GL#=P/Z^_W[V/O7 +M^^@#Y@/H`>H!N?NY^[S_O?]1`4\!.?L[^U/]3_WU`OD"I_^G_V7^8?YT!'H$ +M80-;`VO\;OPM_B[^3@)*`CW_0__P_^K_X@/F`QG_%_\5_!;\X@'B`>D`Z`!9 +M^EKZMOZW_ED%506*_X__./LU^Q$!$0'.`=`!0?X^_AT"'P(X`S@#$_T2_>7] +MYOV=`IL"UO[8_C7\-/PV`S<#ZP3I!%_^8OZI_J7^7`!A`*[[JOO4_=?]Q03" +M!,P`SP`<_!G\5P):`OL#^0-Z^WO[3/M+^Z("HP)@`5X!COZ1_G\"?`(<`1\! +M(?T?_>D!Z@%8!%8$?OV!_='\S_Q$`T8#]0#T`,/[PONO_[#_;@%N`;?]M_W] +M`/X`+P,N`\+[P_O;^MGZ6`-;`Q$##@,._0_]9`%E`;(&L0;\`/T`JOVI_9$` +MD``3_A3^$_T4_1T#&P.$`H8";?QK_-/^U/X<`AP"??Q]_$'[0/LB`B,"U0'4 +M`7W^?OX7`Q@#D@./`T#]0_WZ__C_#P80!AD!&0$6_!7\Y0#F`,8!Q@$/_`[\ +M(/XA_DD"2`*M_J[^??Y\_N,!XP%'_4C]0_I#^IP!G`'[!/H$50!6`.(!X0&G +M!:H%Z/_E_W?\>?RX`;8!E@"8`#S\._RG`:@!W038$YP1]_WK_H?ZE_GT$>00G`BL"*?PF +M_$S_3?\"`@(":OUJ_>C^Z/X-!`P$]?[W_JOZJ?J2`),`-P$X`4/[0/MX_GO^ +MH06@!:\#K@/^```!V`+7`F__;__^^__[&@$9`24")0+\_/W\2`!'`'\%@`6O +M_Z[_O?J^^GG_=_]]`($`K?RG_&``9@"W`[,#-_XX_J?^I_Y"!4,%^0'W`;K\ +MO/QJ`6H!4P-0`S#]-/TC_2']G0&>`1P`&P"#`(,`[`3M!&$`7P`3^ACZ"?\" +M_Q$"%P)*_$7\M?VZ_<$%O041!!4$=OYQ_GH`?@#@_][_!?L(^Y[_FO\"!04% +MDO^0_\;]Q_U`!$`$'P,>`\?\Q_R'_HG^5`%2`5C^6/YM_V[_90)C`NC]Z_T5 +M_1+];01O!!0#$@-O^W'[-_XU_CP#/@.(_H;^R_S,_!<#%P,\`SL##@`0`*<# +MI0/5`M<"8/M>^X;\A_S>`=X!)?\E_Z#]H?W?`MT"D0*2`I?]E_T(_PG_[O_L +M_TO[3/N5_I7^)@8F!F$"80+`_<#].@,Y`S0$-@1H_6;]&_X<_K$"L0([_SO_ +M-_TV_93Y^`'Y`8("@0(I_BC^_P`"`4D" +M10*:_)_\J/RC_!P!(`&`_GS^9OUI_?H#^`-]!(`$.O\V_YT`H@#:`M,"R?[/ +M_K#^K?YO`G`"G?Z>_GS[>_L@`A\"J0.J`\[\SOP:_AC^?_Z/\`_/_[!?T%_4X# +M4`/F`>(!T/W5_50!3P$L`C$"K?RI_)/]E?V6`I8"M0&S`1P!(`&$`W\#YP#M +M`&K]9/W,`-(`U0'/`6G\;OQ@_5[]*`,G`XD`BP#-_,S\S@#.`"D"*0*Y_KC^ +M0@!#`!X"'@*)_8K]'OT;_2P#+P.7`I4"=OYW_J,!H@%W`WD#U_[4_L_]T_VZ +M_[7_P/W$_<7^POYW!'D$!`,$`QW]'/VW_[C_X0+@`D#]/_VB^Z7[EP&4`1H" +M'`*&_X7_Y@'F`;`!L0$Z_3C]5?]7_^(#X`,0`!,``?W]_*T!L@&K`J4"K_ZV +M_N#_VO_)`P%?_F?^1/\\_WH!@@$,_@7^ +MP?S'_`D"`P*>`J,"Z?WF_4#_0O_,`LH"O__!_Q#^#OYX`7H!8P%A`3(`-`"\ +M`KH"G0&?`:7\H_P(_@G^%@(6`DW_3O]3_5']F@&<`>P!Z@$'_@C^(O\C_VL` +M:0![_7O]Y?[G_M@#U0-_`8,!(_T?_8\`D0!Z`W@#B/^,__[^^?X#`@<"I`"A +M`/S^_?X]`#\`??YY_L;\ROS?`-P`^`+Y`EK^6_Y4_5/]\@#R`%C_6?_Q_._\ +MSP#2`/X"_`*J`*H`M0&U`0\#$`/L_NO^&_T;_3P!/@&=`9H!./X[_F__;/_) +M`,P`'/X;_B7^)?X!``$`3/Y*_D3^2/X)`@8"'`$=`2_],?WV__'_#P06!$D! +M0@&\_\#_M`*T`OD`]P`]_4#]*/\F_S@`.0!>_5W]T/[0_HP"C0+0_\__7?Q? +M_/7^\O[2_]3_[_WM_80`B`#^`OD"@@"'`*,`GP!+`TP#4@!4`,C\QOQC`&,` +M@`*!`EO^6OX5_A?^!0$#`8_^D/[G_.;\IP"G`/0`]0"=_9S],/\Q_^P!ZP%> +M_U__9/YB_MH!W`$H`B<"B0")`,(!P@$]`#T`7OQ?_%O^6?[8`=L!>_]W_RC^ +M*_[O`>X!DP&2`?7\^/RM_:K]$@`3`+S^O/[\__O_$@,5`[D`M@`!_@+^D0"1 +M`%$!40$7_A?^T?[1_JD!J0$/`!``(_\B_QH!&@%]_W[_#OX+_M0!V0$_`CH" +M__P$_0?]`OUD`6D!$@`-`)C]G/WE`.(`$`(1`@[_#_^._XS_;`!N`#+],/WE +M_>;]+0,L`P0#!0-F_V;_T0#1`/D!^0$T_C3^<_UR_;;_N?]U_G#^IOZM_H0" +M?0+#`,<`"OP*_'S^>OXX`CL"_/[Y_DK]3/V;`9D!7@)A`M3_T?\X`3H!&P(9 +M`K;_N?]9`%4`Q0')`1O^%_Z9^YS[Y_[F_A\`'P`Z_CK^*P`J`,D!S`&8_I3^ +M`_X'_E``3`"C_J?^%?X1_D8#2@,_!3L%#0$2`?/_[O^-`9$!RO['_IW\G_P0 +M_Q#_:_]J_PO^"_XL`"T`HP"B`$']0OWX_??]?P&``>O_Z/_J_>W],P$S`1\" +M'`*._Y/_J`&B`?<#_`-;`%@`K/ZN_A(!#P'D_NC^ZOKE^L3]R?U=`5D!7?]? +M_S__/?^4`98!(/\?_[/\LOR6_YG_C0"(`.K^\/[<`=@!O`2]!'`!<0$O_RS_ +M_0`"`3?_,O]K_&_\E/^1_TT!30'(_\!B_Z+_GK_>?_E_^?_(OX@_E(`5`!#`D$"RO[,_A7^%/[E`>4! +MW0#>`/'\[_RB_J7^OP"]`/7]]/T;_A[^F`&3`48`30`P_RG_$@,9`Z("FP+] +M_`']8/U?_5P!6P$<`"``WO[8_@P"$@))`D0"AO^)_^[_[O_1_L_^#OL0^S_] +M/?U"`D,"F`":`$/^/_ZD`:0&#_H'^D_Z6_ID! +ME@'5_];_?OY^_G\"?@*^`L$"M?ZR_BH`+`#X`O8"%?\7_T;\1OPP_R__Z_[K +M_I3\E/PO`#``"@,*`\3_P_\!_P'_5P%6`2G_*__6_-7\$@`1``4"!P)W`'4` +M+@(O`F8#90.__L#^_?S\_&D`:P!7_U7_?OQ__-'_T/]R`G,"7/];_Z/^I/[X +M`/<`*_\L_YO]FOW8`-H`%P$2`13^&_X"`/O_J@*O`FP`:@";_YO_O@&^`1$` +M$@"4_9+]G?^?__'_\/]M_6S]-P`Z`+H#MP/N_^__$?T3__][_[K\N_R!`'\`T0'3`9;^EO[2_]'_ +MCP*1`LK_Q__^_0+^5P!4`./^Y?Y6_%3\P__%_SD!.`'R_/3\$_X0_H,#A@,U +M`C("MOZY_MT`VP"Y`;L!;/YI_A?_&?^J`:D!V/_8_\W_SO_C`N("0P!"`*G[ +MJ_MO_6W]+?\O_\;\Q/P;_AW^0@)``BT!+P&>_YS_)`(E`AL!&P%L_6W]_O_\ +M_SH#/`-B`&``%/\5__0!]0'A`-X`HOVE_<7^Q/XI_RG_>?QY_-#]T/U%`$0` +M[_WP_0_^$/[D`N("QP++`F3_7O_0`-4`"`(&`HO^C/Z=_IW^0`(_`MD`V``+ +M_P___`'X`>8`Z`!?^V#[^/OS^VO_<__V_>_]P?W&_>(!WP')`P!2OY._E#_3?\-`PX#GP"@`!3^$OX;`1P!+P$P`7/],!Y`$3`A$"L/ZT_@4``0!:`5P!@_V!_9O\G?SY__C_D?^1_RW^+OY, +M`4D!VP'?`?G]]?W!_L3^TP'1`83_AO_U_?+]#`$/`0,!`0&H_JG^,@`T`"H! +M)0&<_J#^E/^3_S`"+P+D_NC^4?Q,_!D`'``P`2X!+_XQ_JK_J?\#`@4"__[[ +M_J+]IOU)`$8`^/[[_@#]_?QI`6L!Q0/#`WG_?/^S_J_^C0&2`'^W?X9`!H`,`(P`H'_@?]P_7']#P$- +M`?]:OUK_3L`/0`)``8`XO[E_L$!O0&9`IP" +M7O]=_\'_PO\-`0L!3/]/_^;^XO[!`L4"`@```"8`)@![_GO^,`@Q"._M[NU< +M^%WXH@NB"W'W;_S&[!?K%>M,$U`3OP&[`=+XU?@"%0`5^!3X%.7\YOS]T/S0F`:9 +M!KDMN2WF^^3[4^=6YZ/_G__.!=0%$>$+X6P&<`9K'FD>0P-$`T4!10%1Y5'E +M<_ES^=(7TA>I#JD._`'\`6GW:?<:\QKSR.?(Y\GYROG`$K\2?B!^(*;^I_Z7 +MU935BP>0!XP!(,\@OR.>H[ZC3[,OOJ^NOZJ!6H%5X2 +M7A)-X$S@,>HSZ@$-_PS@$.,0.0,V`[H&NP:P$K`2O_F_^4KD2N0!]P+W`AL` +M&UX)80F^[;OM)OPH_#WI.^G7^-GXHQFC&:@/IP\-!P\'JONF^YCVF_9MY&SD +MY`SE#%XL7"QP^7/Y].OQZ];RU_+V`O<"R^_)[_+\]/P"+``LK?ZN_K#EL.4- +M[PWOI12E%)P7VY=[RWO)?&5T9T!+3 +M$J7TH_0HW2G=B?B*^%`031#2`]4#_@O\"PX)$`G/!QM+_TS_&>48Y>GIZNE* +M$$D0VAO;&]L&VP8;]1OU&/P7_%CS6O.<]YGW?0>!!RD()0CR'O4>^?#V\!C. +M&\Y!##\,Y2KF*NC\YOQ`X4+A2!=&%QX'(`>OW*_<%/L2^QH8'!B-'(P<5>E5 +MZ6KI:ND$``4``_P!_%`04Q!2!U$',`PM#.(`Y@#K\>CQ@O*$\J?^I_X0)@XF +M1?Y'_K?AMN$/^Q#[NOZX_NOX[_C2`,T`R2;-)OH!]P$SUC;6X_CB^.`,WPSI +M&NH:(1`A$#;W-O?T\_7S'?D;^1?[&/OW\OCR0P]"#^T1[A&_[+[L8NECZ?S] +M^OUQ(7,AF`R7#'OM>^WB!^,',`$N`6WI;^G9_MC^D1N1&VP#:P/\^/_X8/M= +M^YCAFN&(_X;_?1E_&=8&U`;;_=W]+`8J!FK_:_],Z4OIL/^S_[(=K1T_$T,3 +M[=OJVTC?2M_^$_X3[OWM_2`!(`$_&C\:Z07J!6/J8NIW\GCR/0@\"*X)K0EB +M(&4@HONA^Z'8GM@M_C/^80=9!VX!=@$:^!7XV0O;"QT%'04*WPG?)_LI^VH? +M:!_P'?(=9_!E\!?Q&?%0$4X1VO7<];GRN/*4_93]LPRR#)P-G0T8ZACJ[.GL +MZ8L"BP+?(-\@.P8Y!KOFP.;*$,40:PEM"?/F].94]E+V#R(2(K@8MAC*VLC: +M&^8@YA`#"@-6"%T(K`>E!SD)/@F=$YH3D.^2[P;M!>U\!7L%5PU9#5H46!3L +M\N[RY][EWM[ZW_KG'.8<*O@K^"7L)>R*)HDF]@3W!$W53=4<^1KY@"B#*.@7 +MYA?U[O?N4_51]58#6`/2_<_]>.E\Z;\`NP"F%*L4,OLM^P_W$O!BX+/[MOM4)E(F_`;\!L_HT.A1YE#F +M-`4V!4`%/@6+_HO^L1*Q$D@,2`PK]BWV:.=FYTP&3@:7%I0640E5"6D!9`$( +MWP[?D/.*\Q`2%A+O_NK^>P%_`2H0)A"4!Y@'Y=[@WH7WB?X7_Q3_ +M-?DV^:T4K12'$(806_1=])GRE_*.`I`"!`4"!:`%H05@%%\4BP.,`T/H0^@^ +MX3WA'`<_38:.1H@_1S]=N]Z[QX'&@?J"^T+S^K-Z@#R`?)]('T@`!G_ +M&#KS//-IZ&?HU0G5"3P&/@;7Y=3E:@IM"LT3S!.*\XGS8.QB[*KXJ/A@#F$. +MX!#B$#\..PXH]2SUG_><]YL+G`MZ\7SQ]0/P`]T:XAHS!B\&L=JSVAK<&]Q< +M&UD;9A%I$9?SE?/^_?[]$A`4$(+^@/[=Y=WE"!8)%H(A@2&$^X3[B>F+Z73Q +M_ +M]W+Y`_4*]`J_$KT2 +M*`(M`DOC1N-S_';\JPBJ",<`Q@!J"&T(F@27!*3NI>Z;YIOF>@IY"H87B1?$ +M!K\&-`$[`9WXEOC%]?%\\5D$5@01"Q(+ZO3L]"?\(_P:&QX;ON^[[[_:P=I-"4P)"2,*(Y3_DO]& +M[D?NG@>?!SCW-O?O^_+[<0AN".@&Z`;/#M(.>_)W\GWH@>C,^,GXYQ+H$OH- +M^@V'[X?O-?\T_[\*P@K'`,,`#_(2\KT'O`?3&],;_>_^[^OGZN=E!60%S`?. +M!^/ZX?K._-'\"P8'!E/\5_PY^C3ZTOS7_!``#0!2$U03?1![$'#T=#YR00'1"&%8P5`_W__&3\9_PI +M""<((OO2Y$;D17`]<#T[V3?97^%KXBO6&]=/QU_&A"YT+O1&_$33W +M-?<:]!CT[0KO"B@`)@!%^T?[H`R>#.(&Y`:)^XC[Y>[E[O;_]_\.`PP#&O4= +M]1`*#`H'!PP'6/92]OOV`?=8#5,-4055!1+Y#_GI%NL6$_\2_QOM&^VT_+3\ +M,``Q`!4$%`0P_C#^GO^?_UOQ6?''\\KS3PA,"&L,;`R/$8\1T`31!&7Y8ODX +M\CSRR_W'_4T5414H_R7_H/*@\A@#&0--]TSW.>L\Z^P%Z`7F&N@:*OLJ^U/I +M4>D[!T`'*PPD#+H!P0'K!>8%\P;V!JS\J_QC\F+RXOGE^2'['?M^`X,#APV! +M#=_ZY?J?]9OUT0?2!]<(UP@.\P_SL?VP_=H8VQA]`'L`8>!BX+G\NOR&&(08 +MD_F6^5+S3O-L#6\-@P""`$SI3.G]__W_Q!K%&O`,[PQK`6P!L?"P\`'R`?*X +M![D':@)J`AS^&_Z(^XG[G`*;`KGVNO;PZ^_K[P_O#UD=6AU,_TK_>N1\Y!@( +M%PC-%,P4H^^E[Y0"D@()#PH/H/R?_$?P2?#$\L/R-P,V`^\&\09Q!&T$-O<\ +M]__]^OV.#9(-D_Z/_J_ZL_I"!ST'+Q`U$+K[M?N1XY;C+`,H`W`^P]+'T +M0``^`/X1`1*P^:[Y=^-WXW\$@`1;&%D8P0+#`E(`40")^XG[0O=#]^W^[/[V +M!_8',`4P!?[V__8:_AC^8O=F][CQLO&W#[P/DAN0&_W\_/Q`ZT3KBP.%`ZX% +MLP7?]]SW;`5N!5$-3PTV^#GXO?&Y\0X%$06._XS_MO>X]TH$2026!Y4'J?:J +M]I7\E/SC#N4.Q?_$_]L"V0+C".<(J/NC^VCT;?13^%#X3`5,!70`=@#@!=T% +M!`4'!8'U@/6$]8/U1O]'_WL->@W]__W__P,!!)4(DPATZG3JQ?K'^H`8?1C; +M!MT&^^[Z[H?[AOO'`\+0#MP/L%>@5P@7%!3'W+_>S]+7TH@*A`O,$ +M]`0G`28!U@/6`V/U9/4L^BOZUP77!2CY*OF<_)C\:1)N$KL%M@5#Y4?E80)> +M`B@;*QM\^7KY?O9_]B`.'PZ`"8$)'NT<[?CL^^RJ!J<&"P4.!1+Y#_GN_/'\ +M[@3J!&0!:`%]`7D!ZP3O!&@'9@>?"Y\+$_L3^ZKIJND@_B#^E1B5&,#^P/Z9 +M[)CLW@/A`V$!70%0ZE/JD/:-]@T@$"`)$`<0_>S_[`C\!?R1"90)"P@("//\ +M]?RH_*C\N/NX^WOX>O@]_C[^6?=8]UD)6PG&$L429OIE^FON;NZ-`(@`T177 +M%3'Y*_EQ\G;R]Q3T%(4%AP5LYVKGD?B1^%,.5`X5^A7ZK_.N\R0+)0O@!.`$ +M<_QR_`T##P,X!C8&E`N5"T`"/P)*]TSWXO'@\0CX"_C,"\@+Z`'K`9?UE?6+ +M`8T!'0,<`WSP?/#$^\/[RA[,'D0'0@>6[YCO70);`JX$K@1W_GG^A0*"`@$% +M!07)]L7V?O"`\!S['/L1`@\"=05Y!6X(:PB!!8(%!_@'^)[]G?W_"0$*`_\" +M_R[Y+OG@!N`&_?_^_XOOB^_X`_8#Q`?'!T7X0_B#`(,`/@A!"/?[\_NZ\[SS +M(0PB#"(,'PRV^KGZW`/;`Q,"$@(;\1WQA.V![80(A@B#"H(*L/:Q]K,!M`%. +M`TP#'_PA_#D#-@-?#V,/%0@1"/SR`//G^>3Y7_YA_I;[E?OG"^8+O`>^!]CM +MU>W"[L;N0`<[!^(`YP`"]?[T/A-!$_41\Q&"\8/Q)O@E^&(-9`T6#!0,;/1M +M](KVB?:F!*<$C/F+^=3WU_=-`4DI\2KQDA&1$?H'^@=H\&GPEOF4^:8'J`=C +M`&(`U_K7^I(*D@K[#OL.F/J8^C#O,._2^=/Y(P0A!`@."@X.`PP#YNKHZIC] +MF/T>"QP+B_:.]O'W[?=3%5@5+0TI#33G-N>0^I#ZI!>A%\$%QP4S\2WQ7OUC +M_ZP[I_SH/-4$%00D@B3"&OK:.N+^([XU`S0#(#WA?>9 +M_)7\915H%:(+H`M.]T_WZ/'H\>C]Y_W<`=X!6`16!+4"M@).]T[WWOG=^9O_ +MG?^3`Y`#_`$``B0)(0F"!X0'7.];[QW\'/R.$9$1U0'2`:OXKOB&`H0";_AN +M^!SN(.X[`#8`'POT'!`8$9OAG^*X%K@5" +M&$`8%P(<`NSXYOB(`(P`P/N^^\+VPO9T!'4$`@X##DCP1O"IZ*OHBPF'":NYM[QV/%\$X(3W`C6"'KY +M?_F&#((,CPZ3#A'U#?42[A;N(@@>"(H/C0^$]H'V2_A.^"/_(/^0^9/YY0#C +M`,D$R`0__D+^^0'U`74+>0ND^:'Y./@Y^"01)1'+!,H$TO#1\"C\*OQ$"4() +M2_E-^6CN:.YI"68)IA*J$N[XZOB:]IWVC`B,"%$$3P3&^L?ZX@;B!L\(SPA9 +M]UGWP?C"^$+Z/_K=^^+[T0K,"@`%!`6E\Z/SR_/,\[@*MPHW"C@*)?PD_%T, +M7PQ6"%0(=_)Y\B_V*_:?"Z4+SP;)!M'TU?1._DS^P_O$^SWV//9W`GD"]@?S +M!V,!90$D_23]L@6Q!9L`G0`"^O_Y`0D$"2T**PJ=]Y_WUOO4^S`',0?=]MOV +MS?/1\W,(;PA1!E0&!/@!^"X!,`'3`](#POK#^K8$M`05"Q<+F0&7`3ST/O0P +M_##\X`7>!`BD#Z,/)_LI +M^T?Y0_FX"[T+\P7O!4SS3_->!%P$L1*R$DKU2?6.\9'QJP*F`LO]TOW+]\/W +M)?TM_:(&G`;9`ML"(P,D`VX`;`#@_.+\4PM2"ZL)JPF\^+OX8O5E]=\%VP5+ +M`4X!7.Y;[LX#SP-I$6<1,?4T]:_IK.G0!]('DQ62%3__/_^L^ZW[(PDB"3X` +M/@!*]$ST//\W_S$%.`62_HO^$/L6^TGV1/;V]_GWX`O?"R/)Z\JH&I@9N#G,.2O-&\X_MDNUL!FH&\P/T`R?S)_-6_57] +MZPSM#)H%JA"G$`#^`?X,_`W\*08F +M!LG\S/Q<^%KX-`(U`D@(20AQ]F_V9?5G]<\(S`C2`=4!Z?GH^3P&.P8?!R$' +M?/EY^64`:``P"B\*(/T@_7?\=_S$",0(>OUY_6SO;N^J^ZG[=`-T`^KYZ?D< +M_A[^^`SU#&D#;0/O]^OW%047!<\(SPA.`TP#10-*`Z+\F_P5]1SU`_W]_!T& +M(@8Q_2[]9/IG^B4"(0(^^T+[RO+'\LT#SP,A%B`63`1-!`KT"?2!!8(%W@?< +M!V3W9_?\__C_X`7D!3OY./EM]W#W6_U8_;,`M@!@!%P$@`>#!VG\:OR4^9'Y +M^`;[!D0$0@1E^67YX/SB_*4/H@^W`;L!+NHKZIL"G`(5#!4,MO2U]$#V0O;9 +M#=<-'@4@!>?SX_/B!.<$9PMC"^$$XP3._\[_)O@D^-_WXO>G_:7]^P/]`Z#^ +MG?X:_AW^^P;Z!NS\[/S/\-#P]P/T`W<5>A5I_VC_]_'X\08(!`@="QX+Z_CK +M^$#^0/[Q!O$&KOJN^A3Q$_%1^E3ZY@7B!8\$DP2I`Z4#H@"F`%/^4/Y\!7X% +MN`*X`E/Y4OGN`/``GPN<"YO[GOLF\B7RI`6C!4L(30C*]O(;^QK[Q`?'![W_N?]5^ECZC@2,!-$$T@3N +M^^_[-`(Q`KP(P`@&`P(#)P8J!I$"CP*8\9KQC/F)^48'2@>N_*K\A?6']3D$ +M.@1)"$<(F?F;^2$"'@*G$JH2\`7N!<_STO-9^5;Y]`'U`>W][?WZ`/L``@(` +M`LOZSOIN_FG^-`$Z`7;Z0%W`48`!\`0.X[[A/Z%OK]#/H,3@-3`S7]+OU2"%D()P4A!28!*P$, +M_PG_=O]W__@#]P,R!#,$A/^$__?R]_*(^H?Z`PH%"AG_%?]V\WOS>P-W`W<, +M>0ST]?7U._TV_3(8.!BW![('X?'E\>/YX/G#`L,"*?XL_JW\J/S/`-4`K0"G +M`,[_T_]4`%``9P!J`%0%4@7="-X("_\+_TWU3?7M`>P!ZPSM#.GXY_A-]4[U +M_PG^"00!!0'0\-'PC_R,_/L)_@EA`EX".?\]_X@(A0AH!VH'0@$_`?K]_?U= +M_EW^_?_[_WT!@0&V_+#\F_*A\E_\6_S?"^(+,P$Q`1KW&_<%!04%ZP7I!8'W +MA?<\`#8`^A(!$U@*4@K%]LGV5OQ4_&X!;0&Q^K/Z]/WQ_;@!NP'S^O+Z2?E' +M^68%:P6^`;8!*_\T_S@,,`RF`JT")O4@]>7^Z?Z*"XD+=`%T`;OXN_@T!3,% +M[0/M`T#W0?<7^1CYS@/+`[4!N`&I_:;]+`8P!EX"60+V_?O]?09X!L0`R`"5 +M^9/Y*`0I!*X"K`*A\J3R-ODR^?0+^`O:!M<&Z?GK^5G_6/]M!6X%)/\C_SG\ +M.?P["#P(/`H["MKZW/J^^;SY.?TZ_0?^!OXC!2,%T?_3_ZOSJ/,Z_#[\S`G' +M"9G_G/]G_F?^^@_X#Z,)IPD8]1/U_O@"^=P)V@DN`BT"+O0P]$O^2O[+!_Y&P`C`,X%R`5T_GG^./.]XT`C@!8 +M!5<%*_,K\Z_RK_)>"5\)E@B4""/X)?B8`98!Z`CJ"(_^COX&_P;_D@:1!MX# +MX`.Q^[#[)?DF^6#Z7_JY_KG^A`:%!KH%N`5/^5+YH_J?^I8(F@AK!&D$J_RK +M_,4&Q@8'"`4(U/76]7?V=O:#YWOEP^W/[V_[8_H'_@O^*`XP# +M(0<!5X%M@"W`&H":0(@_2#]COR._`P` +M"P#)]\OWC_B.^$((00C(!\H'P/6^]<[YS_EI#&L,:0=E!P7\"OP.!0D%E@J9 +M"@#Z`/HR]3'UUP39!,0%P07!^L3ZN?JW^D[]3_V^_K_^901C!(<#B`,E_R;_ +M3@-,`V4':`>S_:_]6?E=^?@&]`8$!0@%T/7-]73Z=OHM""P(P__#_S7V-?:/ +M!9`%L0FP"?[^`/_7_M3^)@,I`YX"G`*M_Z__$@(0`CO_/?^"^H#Z^`$+`@L]"SP+]OWW_5`!3@$U`S@#M/FQ +M^0+Y!?GY__7_.P`_`/C]]?WV_??]:?MJ^_3\\OP>!R`'S`G*"4\!4`'&`,4` +MS0G/">P!Z0&[]K_V<0)M`E4$5P0O]2_U4?=1]QH$&@33`=0!9/QA_'\"@P)] +M!7D%M0.Y`QT#&@-B`&,`XO[B_GD#>`-^!8`%#_L-^V/V9/9I`6@!HP"F`$?U +M0O5:`5X!1@]$#V0`90!)]TGW[0;M!FH-:`V8_YO_6_Q9_.H`[``C^R#[M?BY +M^./]WOV'`8T!3`!'`%D`7`#3_-'\Y/OE^\\*T`J?"YT+8/QB_+/^L?Y'"D@* +M6P%<`57T4_3B_^7_0P8_!M+WUOO +M!*\$PP'$`2'_'_]=_&#\'`,8`U`$5`3P^.WX-_HY^M`%SP5E`&8`J?:I]LH" +MR@(-#`P,?_Z`_H3VA/9CZY_IN]V_WWO_; +M_VP#<`.R`JX"701A!-_^W/YM_6[]_`G]">P(Z0C`^<3YQOK#^AX%'P4=_Q[_ +M=O9U]OO_^?^2!I<&*_DE^4?U3O5!!#L$)`4G!!!X$GPRA#)K_EO_G]^OW\`+O`J$"G@(D^2GY +M!O\!_UX&809Q_G'^;/II^CD`/0"(`(4`#@$0`>H"Z`)X`'D`R@#*`.\%[P5- +M!$T$YOCE^"/[)/LI!B@&5OU8_;WRN_)I_VG_Q0?&!X'\@/R4_I7^2@M)"Q@% +M&06J^JCZU_[;_BD$(P1<`V(#>P!U`,K\T/R$^X#[E?V7_9__G/^F^ZK[]OOR +M^ZH$KP3S`.\`>/EY^84%A@5U$7,1JP&N`=_UW/6&`H@"F0*8`FOW;/=H^F;Z +M>`-[`T@!1`%F^VK[O_^]_X`"?P(]`D`"9@)B`IS_H/_=_]K_-`4U!0X$#P3; +M^-GXZ_ON^X@&A0:#^X7[+O(M\AP`'0!P!W`')/TC_>O^[/[^#/X,_@C]"!_[ +M(OM:_5;].0,\`ZW_J_\K_"W\_/KZ^FKY;?D0_0S]E0*9`O+][_WX_?G]GP:@ +M!M8!U0$<^QS[105%!;`.KPXZ`SP#D/B.^!\`(`#P`/``;/9J]OCW^_=K`6@! +M\OWU_4/Z0OJ6`I8"6`57!<8#QP-"!D$&Q0''`7#\;OS"`L4"R0+$`DCY3/D8 +M_!7\0`5#!4P`20`(]@OV'/T9_2,%)@6;_IG^VOW;_2H'*0?=!MX&+/XL_K0` +MM`"J`:H!)?PD_/7^]_Y__'W\[/7N]?W\^ORK!ZT'30--`V3^9/Z6!I4&S03. +M!$CZ1_I<_EW^QPC("!<#$P.I]Z_WN_RU_'P!@0%.^TK[_?L!_++_K?]7_5W] +M%?\/_Y`#E`/^`OP"2@1*!(\)D0D>`QL#?OB"^/O^]OXB!"8$5OE2^>#UY?6; +M`Y<#MP6Z!97XD_A%_$/\/09!!KT!N@'J_>S]S`+,`F4$8P2$`(4`-0`U`'O_ +M>O]2_U7_]0'R`6']8OT!]@#V\?OS^WH'>@>/`HX")/PC_*\&L09*"$@(7/E? +M^9'[CONS"+0(U`'3`;KVO/:F^J/Z8`%C`9T`F@`'_PK_^/_U_W'_=/\I`24! +M-`$X`<3_P?]\`W\#*0DG"?@#^`.5^);XEO^4_YD%G`4)]P?W^?/Z\[`"KP*1 +M`Y,#/O@Z^(?^C?Z2"8L)H`.H`\/^O/X[`T`#]@3R!`H`#0`2_A'^B@"+`*T` +MJP`]`CX"$/\0_QWU'?7Y^/KXW`39!)``E``'^@/ZP`3$!)D)E@E\_7W]RO[) +M_I4)F`EM`VD#5_A;^(7Z@?KR`/0`./\W_V[_?YV_E,&50:O!*\$&`(7`NX$[P2E`*0`6/Q8_*[_K_]+_4K]-_HX^D,` +M0@!1`E("KOBM^//Y\_F8!IH&!P4#!1/^&?YI`F,"*@@O"!@!W`.?ZZOJ[][CW:O]K_Y8# +MEP.R^;'Y[_GO^?,(]0A'"4,)E_V;_9S_F?_"!\0'(@$B`27\(_P8`QH#X`+? +M`M/ZU/HM^"SX//H]^E?]5?U*`DT"MP&T`3;]./U1`D\"B@F,"2L#*P-Y_7C] +MT`;0!A$%$07#]<+ULOBV^)X%F`6#`(H`"_<$]P__%/^4!)$$B?Z,_C[^._ZG +M`JD"\`/O`_D!^0'+`_][SW*@$M`97_D__5^=;YSO_/_V@#9@/1`-,`^P+X`KX$P@2% +M`8`!CP&5`<0$O@2F_JK^'_H=^EH!6P%S`7,!TOC1^"?\)_Q5`U8#/_T]_0OZ +M#_JN`ZH#P@?%!\X`RP"J_ZW__@+Z`HC_C/^X_K;^<@%S`1G_&?_=^]S[-OXU +M_B;^*?[Q^^[[]@/Z`^0&X`;`_,+\K/NK^]L%W`5D!F0&IORF_';^=?X"!`($ +M&OP<_([WBOH)+`(P`L#^OOYW`G<"O@#``'$`;@#/`-$`,/TP_9?[EOLP_C'^,?PP +M_/WZ_?I9!%H$6`98!C_\/?PY_#S\^@;W!O8%^04*_`G\:?]G_^H$[@22^XW[ +MP/;%]@4!`0$7!!L$C/V'_;;]O/V;`94!W`'A`=P"V0+2`]0#"``&`$P`3@#0 +M`L\"._P\_#GX./A-_T[_U0'3`9GYFOEY^WK[OP6^!;_-_\:`5E!:<) +MJ@E6`E4"N/ZX_B`!(`%._D[^3OQ-_$7^1_[1^\_[+OHQ^EW]6OU@_F+^]_WU +M_6\$<00$"0,)V/_9_U[]7/W2!M0&R@7'!;#^M/Y-_TG_9P!J`,CYQ?G^]__W +MO/Z^_G;_`_4&^P;4_\__,_TU_28#)@.W +M_K;^,?@S^(_^C?[X`_D#/OP^_#_[/OMY`WH#[P'O`3/^,?[_`@,#N@:U!HL" +MCP*I_Z;_H_^E_PS\#/QE_&3\\`#Q`.W\[/R5^);X.@`Z`+$#L0-C_F+^^P+\ +M`CT*/0K<`=P!Z/GH^<(!P@&N!:T%V_[<_FG\:/QW_GG^!?T$_0?[!OM#_D3^ +MC@"-`*$`H@!$`D0"$0`1`(G_B/_W!?H%R`;"!I/^F_[I_.+\\P+V`D;^1_Z] +M]KKV1/Y'_LT$RP3^_O[^Y_OH^T(#0`/E!.@$"OX'_E,`50`<$Z`1!^S[[XOGH^:;_G_]Q_7;]GOJ;^M8`UP!P`W(# +ME@&3`1L"'0(J`"@`&P`=`$4&0P8\!S\'C_^+_T3]2/WE`>`!"_X1_BCV(O9/ +M^U3[DP*/`K_]P?W5^]3[7P-@`\D$QP0(``H`@0*``I`%D`68`9D!O?^Z_ZG_ +MK/]3_E'^SO_0_Z\!K0'M_>W]TOG2^0K^#/["`+\`,OTT_5T!6P$>""`(E0*5 +M`NK[Z?M]`7X!(P4B!6K^:_XB_2+]W?[=_DW[3/M@_6']-@,U`[,"M`+<_]O_ +MW0+>`J0"H@(W_#G\Y`#B`(0&A@9^_WS_&_L<^Z,`I`#P`.X`4_E4^8S\C/P[ +M`CH";/YN_F3^8_X6`Q4#K@2O!$P#2@/W!/L$>@1V!-#^U/ZZ_K;^8_YF_A/Y +M$?EF^FGZG`*8`J\!M`$6^A+ZKOZP_A0%$P6;`)L`"``)``D&"`;#!,4$FOZ8 +M_K__O_^K`ZT#L`"M`)[\H?P[^SK[LOFP^63\:/P!`OT!%0(9`G@!=`%)!DP& +M[@3K!,7\R/P^`#L`D@:6!AW^&/[<]^'W/_\[_]H!VP'&^\C[X_S@_.L"[@)` +M`3T!G?ZA_A\"&@*-`Y,#?P-X`_X$!`6N`JL":_YL_B7_)/\N_C#^>/9T]E7Y +M6_E=`U<#W/_?_]KZV?H%`@8"D0B1"+T"O0)E`&0`1@='!]0#TP.*^XO[X_SB +M_%("5`)8`%8`W_OA^Z+\G_SP^O+Z/OP^_$_=#_47]!P$$`?C[^_L]_3O])@4H!7X"?`+> +M_>#]'P$=`:0!I0%*`$H`V`'8`7_F/^1`0_!%`#4P.(_X?_X`+A`B4$)`1Q_W/_ +M%``1`!$%%`53`%$`K_>Q]S#[+OM<_E_^JONE^^O^\?[*`\0#>@*``I$`BP#( +M`\P#70);`K'^LOXF`B8"G@&<`6_\/QY_%O]6OWK_.O\B_R* +M_'+_=/^&_X7_/_P__#/_,_\2!Q('=01U!+C^N/ZU`K4"90-D`^3\YOR[_;G] +MDP25!'$!;P%F^6?YN_J[^O;]]?T?_2+]GOV:_3P`/@""`8,!Y0+B`I4%F@6N +M`ZD#;P%Q`6\$<`2$`8(!3?E/^=7[U/N?`9\!R/O)^T'Y/_F#`H4"W0/;`^WZ +M[_J%_(3\PP3"!/("]0(@`!P`_`/_`X@$AP2__KW^?/V!_6C_8O\__43]9OYC +M_G[^?_ZS^K7ZF_Z7_DP&4`;'!,0$Y?[F_@0"!@*2`H\"6_M>^]K\U_SG`ND" +MN`"W`$SZ3?IL_&S\IP&F`6O_;/^4_Y+_BP*.`C$"+P("`@,"`@,!`X$"@@(8 +M`18!'P(C`L_^ROZ=^*+X(?L=^S;^./Y!^S_[O/N_^S4#,@.:!9T%'0`:```" +M`0*'!H@&GP*<`H\`E`!7`U(#6`!;`$?Z1?K+_,W\4?]/_XW[C_LO_2[]I?^C +M_PG\#?Q2_4[]V@7=!7('<0?E`>,!.P,]`XH$B@3;_=K]:/QI_#T!/`$U_C7^ +M%OD7^>G\Z/Q"`$,`,O\Q_T$"0@+$!,0$(0`A`$/_0?^D`Z@#.P(W`O[^`O\! +M`?T`^P'^`1#\#_P/^A#Z/_\^_X/^@_XX_#C\V0#9`/H$_`3U`O$"Z@'O`2T$ +M*`1Y`GP",P$R`9P`G`!"_$'\Y?KG^M/]T?WY_OO^'_T=_;?_N/\S`S,#^_W[ +M_?C\]_Q9!5P%]P;R!J\`M`"7`)4`[0/J`\W^T_Y._$C\HP"G`-;^U?XQ^R_[ +M?OR`_+S^O/XN`"X`LP.S`ZX$K@1+_TK_&@`<`*H$J`0T`#<`)/PA_$8!1P'7 +M`M<"Q?K%^ES[7/M<`UP#Q0#$`(?\B/R"`8(!#`4-!=(`T``Q_S3_QP+"`F0" +M:@+5_]'_=O]Y_[O]N?V;^YO[4?U1_5S^7OX+_@C^O0'``9\$G`3\__[_ZO[I +M_F,&909[!G@&/O]!_\K^Q_Z%`8_][_QT!'`'V`O<"VP39!+0"N`(!`OT!Z03L!!_\0`QP#7_=7]*OPK_(T`C`````$`H/N>^RO^+OX1`0T! +M5`!8`.@"Y0(*!@P&1P)%`@__$?_U`O0"_P+_`F_]P-[`W<#=P,"``(`9P!F`/D! +M^@%L_6O]J?FK^4W^2_Y-`$\`U?S2_/W^`/]W`W4#_0#_`"G^)_XW`SD#'08; +M!EH!7`&1_Y#_1_]'___\`/VU_;+]6?]=_T?^1/Z%_8?]2@!*``D!!P&,`(X` +M_@3\!#`&,@8,``P`.OTX_?`!\P&T`;$!,?HR^KS[O?N;`)D`;?QN_*_ZL/K: +M`-@``P0$!"@(&!0(% +MX0#E`";^(_X'``H`P0"^`&7^:/X"_?_\I_VH_>?\Z?S*_<;]P`#&`(8!?P$7 +M`AT">@-U`Q\"(P*[_[C_U0#8`!L!%P&N_+/\7_Q9_)L`H@!]_W;_(?PF_*?^ +MI/YF`F@":`!H`$G_2/]&`T8#^@+\`N__ZO^[`<(!"@,$`T``1`#__?[]1?Q! +M_';Y??FG^Z#[T`#6`&__:_]8_EK^00,_`V4$9P3@`-\`2`))`B,&(P8-`0L! +M=OMW^]__X/\H`B<"=OUX_;3[L?O(_4"YP+C +M`^$#W@+A`@<#!`.$`X<#;P%M`2G]*?U*_$S\?Y +MU/W4_=C^U?Z\^\#[V_W7_:$`I0`I_R;_!?\'_Y0"D0)7`UL#M`&P`?<"^@+B +M`N$"V@#8`'P'_`/L`@?R$_$/Z0/KE_.G\?OQY_'KZ@/K"_KS^%`,8`R4# +M)`-_`W\#]@3W!)T#FP.,_X[_0O]!_Z@`J``+_PS_]?WR_<7]ROW]_/;\NOS! +M_(7_?__$`,@`MOZT_DP`3`#``L$"N`*W`H(#@P,?!!X$;P%P`7G\>/Q\^W[[ +M__S]_`7]!?T7_AG^3?]*__C_^__Y`?`:4!<_YL_K_]Q/TV +M_S/_%?T6_;+[LONY_KC^Q0#)`*C_H?\^`$<`?@)T`FP!=0%/`$@`Y0'J`>L! +MZ`$[`#X`X__?_SP`/P#J_N?^-OTY_33],_V"_(#\5?U9_5D!50%:`UT#%`,3 +M`]("T`*Q`K0"%`(2`A(!%`'G_^3_FOV=_7#\;?QT_'?\^?SW_%7_5?^+`(P` +MQ/_#_[#^L/[Q__/_%`,1`V$#9`/L`N@"(0,E`T8"0P(>`"``1?Y%_C#^+_[" +M_,+\`/L!^^W[Z_N#_H?^2P%'`?_)_\[0#K`!D#'`-@`5L!L@"V +M`($"?P)Z`GP"F_^:_X_^CO[M_>W]X/OB^T'[/?MT_7K])`$=`?0"^P(0`@L" +M2@%-`:`"G0*%`X@#O@&]`6H`:@!,`$T`C/Z)_KO\O_S)_,7\!?X*_M_]V?V- +M^Y3[6?Q1_*@`L``U`RX#%0,;`Y<#D@-V!7H%3P1,!/D`^P!W_W;_O_Z__BC] +M*?W3^M+ZC?N.^R/^(OY__H#^4/Y/_H7_AO^2`I$"<@-U`S\!.@%A`68!P0*\ +M`OD!_@$L`"D`2P!-`/7_\?]?_&3\V_K6^LK\SOQU_W/_M@"V`"X`+@`=`1T! +MB`*(`O4"]`(W`CH";P!L`%?_6?_"_<#]W?S>_`W^#?Z@_Z+_XO_?_W_^@?[X +M_O;^VP#>`*\!K0&N`:X!C@&/`9$"D0+/``5L!!/\&_Q+^$/Z?_9_]$OX3_GG^>/Y*_TS_Y?_C_T__3_^! +M`((`5`)3`G@">0(B`2$!2@!+`),`D@#Q__'_)_\H_Q[^'/YR_73]%?X4_OG] +M^/U&_D?^JO^K_T\!3`'/`=0!I`&=`2$$)P2>!)H$C0"0`+_^OO[^__W_%?\7 +M_P7\`?S+_,_\(/X?_E+\4/PN_3+]T`#)`+@"P`+/`/[E_N;^?/U\_04``@#( +M`\T#3`-(`T4!2`&U`;,!L@*Q`H4`B`!;_EG^?_^`_U[_7O]7_57],/TR_?;^ +M]OY7`%4`)@`J`!T`&`!8`5T!?0-X`PH##@.A_Y[_0?]#_XD`B0`#_P'_+_TR +M_0+^__W^_@#_-OTT_>C]ZOU;`EH">0-Z`Q@"%P)"`D$"C0*.`I$!D@%:`5D! +MN@"Z`%K]6OU%_$;\2_U(_8;\B_R[_;3]C`"3`,@`PP`I_RO_2`%(`>P$ZP1% +M`T<#;@%J`<("Q@+=`=L!@?Z!_O;]^/TK_RG_DOR2_&O[;?N&_H/^O?_`__3_ +M\O_-`?YU_C7_.O\J_B3^&_XA +M_C7_+_]M_G3^*_\D_\`"Q0)=`UH#$0(3`HL"B0+J`>P!*``F`#4`-@"!`((` +M8_U?_0"#`X,#G@">`)[_GO^A`Z(#DP.1`_G^_/Y) +M_D7^^_X`_YK]E_W$_<7]M_^V_Q\`'P`(``@`'@$A`:L`I@"&`(L`?@-X`[," +MN0*5_Y'_J`&K`D!N`&Y`63]8_V__L#^_0'\`>;\Z/S?^=SY5O]9_Q0!$@$&_P?_ +M^@+Y`KH&NP8D`B0"]_[U_O@-= +M`5\!F/^6_P$$`P0L`RL#"OX)_E``40"^`KT"^/WY_8+\@?S5`-8`G?^<_Z3[ +MI?L,_@O^)_\H_\O\ROP```(``P0"!"`"(`(>`AX"!P8'!I\#GP,B_B/^O?^] +M_[W_O/_Z^?KYO_J^^JH`K``F_R;_C_N.^_[__?^+`HT"9/]B_RL!+0%*!$D$ +MW`'<`=$`T@`L`RL#:0%I`4_^3_ZG_ZC_?_U__:SXJ_BB_*/\`@(!`N+^XOX0 +M_A'^O`2[!(@$B`2B_J/^@0&``4$$0@05_A3^^OOY^Z,`I@#)_\;_3/Q/_!K_ +M&/]L`&P`:?UJ_;/_L_\%`P4#2@!)`&L`;@`7!1(%UP+=`KO]M?VH`*P`'0$< +M`8`Z0"1`(X``?T$_=#^S/YO_W+_`/L`^\O\R?S!`<4!LO^M_[/^N/[X +M`_,#$P07!-[_W/]1`E,"'`0:!'K]>?TH^RK[8/]@_S[^/?ZW^[G[%``0`'(" +M=0+F_N7^QP#'`&L$;`1T`'0`Q?[#_J8"J`(,`0H!SOS0_('_@?^'`(4`!?L( +M^_G[]?M8`5P!,O\O_VK];/TQ`R\#]@3X!/L`^0`Z`SP#MP6U!?[^`?_7^]+[ +MW__D_^W]Z?WH^>OYV/W7_>3_X_]P^W+[YOWD_;D$N@1M`FT"HO^B_X,$@P0C +M!2,%Y/_D_P`!``&G`Z@#W/W:_;+ZL_J$_H3^(OTB_8GZBOKO_N[^_@#]`.'] +MX_T6`10!^`7Z!;\!OP%S_W#_-00Y!(<"A`)^_'_\S/[,_OP!^P'*_,S\=?MT +M^S0!,P%N`'``\/SL_$``1`!(`D<"\/_N_ZX!L@&7`Y$#+O\T_RK^)OZ6`9@! +MCOZ-_G3[=?M#`$(`0`%!`>7[Y/N-_H[^X07A!8L"BP(=_AO^N0*]`C`#+`." +M_83]HOZB_@4"`@+=_>+]F_N6^V7_:?][_GC^J/RJ_/<`]@#_`?X!IOZI_M(! +MSP$M!C`&/@$\`9C^F/[F`^<#D0&/`7[Z@?JC_:#]:P%N`='[SOO!^L3ZM0&Q +M`4$!10%F_6/]?P&"`?@#]`-Z`'\`8@%<`?4#^P.8_Y3_]/SV_-L`V@!<_US_ +M@?N`^S[_0?_M`.H`%_P9_!3^$OZC!*0$!0('`IK^EOY:`UX#>@-U`[S]P?T9 +M_Q?_#P(.`A[](/UM^FOZ]_[Y_C;_,__T_/C\6P%6`7@#?@/-_\C_^J/ZL?RM_%\!8@%,_DK^0?U"_1D"&`+Q`?,!:/]G +M_[L"N@)9`UP#.O\T_U`!5P&#`WT#.OU`_8SZA_J&_X?_'?X>_@#Y_OCO_?+] +M&P,:`\;^Q/XQ_S/_;@9M!B4%)05H_VG_,@$Q`0$"`@(<_1O]S?W._;$`KP#? +M_.+\MONS^[G_N_]S_G+^)OPF_)(!D@'L`^P#Q/[$_L0`Q0"I!J<&,@(T`LW\ +MR_Q@`&(`4`!0`.KYZ?FX^[G[&@$9`6G^:OZV_;;]&`,9`]L"V`*#_X;_\`'N +M`<$"P@)S_G7^C?^(_WL"@0+*_<3]8/MD^^[_[/_X_OG^AOJ&^J;^I?[F`N@" +MJOZF_I[_I/^P!JH&WP3C!*+_H?^S`;(!K@&P`;3[LOLO_#'\&0`6`)C\G/S] +M^OGZ*0`K`($`@P"G_:/]K@&Q`14$$P01_Q+_"``(`)<%EP4B`B(""?X(_O4! +M]@%"`4(!D?J1^N;[YOMB`&$`P_S$_-3[U/MF`F<"]P+U`FO_;/]G`F@"O`.Z +M`TO^3?XU_C7^5`)1`K;^NOX._`O\?P%_`?'][P+M`H``@@#6^]3[+?\P_X(` +M@`#N^^_[E?R4_`(``P"L_:S]E/V4_40#1`,<`QL#%0$7`:P%J@72!=,%?_Z` +M_D[^2_Y9`EP"U?S3_%?X5_C0_M/^)0$A`3/\-?R7_I?^ZP/J`Y\`H`!V_G;^ +M@0.``XT#CP.\_[O_X0'@`5P"70+!_<']7/Y;_LL`S0`2_!#\%OH8^F4`8@!( +M`4P!]?WQ_>H"[0(O!RT'/`$^`5G^5_X3`Q8#*P$H`2_[,/NA_:']00!"`,C\ +MQ?R[_<#]T`'+`P%X +M`:G^K?X4^A'Z>/YZ_GL!>0'&_<;](``C``L&!@;<`^(#3@%(`7,$>`0?`QL# +M:/UK_>']WOW(_LS^>?IU^LC[S/L9`10!PO[(_HC\@_PD`BD"H0.;`^C^[OYH +M`60!W07?!90!DP$!_P'_`0,!`]P!W@&H_*3\8?UF_6#^6OX-^Q/[&OT6_>0! +MY@$'``<`JO^I_]@$V03(`\<#7OY@_@8!!`%9`UD#Z/SJ_,?[P_LL`C("Z@#D +M`-W[XOM(_T3_9`)G`AW^'/Y6_E7^,P,U`[ +M^UW_7_^V`K("D/Z5_KC^LOY^`X0#90%@`6#^9/[V`?,!)@(G`OK]_/T7_Q7_ +MIP"H`'S\?/QB_%_\80%G`;K_M/\-_1+]J`*D`ND$Z@3V__G_U0#0`,T$T@1Z +M_W7_^?K]^G#_;?^\_\#_SOK*^A/]%?W$`,(`[?WP_1_^'/Y@`V0#,P(O`J3_ +MIO_8`]D#DP21!)O_G/^*`(L`_`+Y`M'\U?R.^8OY3OY/_C'^,?[@^>#Y4?Y1 +M_L4"Q@(D`"$`!P`*`$(%005>`UX#+/TN_>WYZ?E#`$8`/08\!OS^_/[`!L(& +M>@)W`A;J&>H#_0#]%`P5#)[TH/1D_F'^I!*H$K,`KP`C[B7N*P$K`:D.IPY$ +M_DG^I/N=^UG_8?_[`/,`LPFX"?4`]`!M\&SP'OL?^R03)!-)!$@$S>'-X>SP +M[?!K%FH6^1;Y%@7Z!OIA\%_PI?VG_2`+'@N4$981W/3:]$+;1-NE`*,`/B9` +M)D$"/P)HUFK6]/'R\>4:YQK7'=4=.PD]"1+G$.=)XTOC;15K%4TH3RC0\,[P +MLM2TU*;YI?FP$+`0\P3S!#_\0/QW_G;^)/PE_"$#(`/6$]83Z`CH",;UR/7> +M^]O[3_]3_WGV=/:;`Z`#+@DI"5OK7^N9Z9?IB!&($4T<3AQ,^DOZ).HCZM(! +MU0'Z%?85_Q,"%'#\;OP1W1/=(N@@Z$8:21JQ'ZP?%>8;YKW4M]3@!N8&'"$7 +M(6L0;A"\_+O\6/!8\)C]F?UR%G$6_@S^#%KJ6NKDY^3G3P10!*L'J@?*^U!X@:@QJ/")4([O#H\(KYD/FA!)L$-0HY"@<(!0A. +M[T_O1]I&VNKYZ_G3(M$B,PPT#'SD?N3M[NGNK16Q%:@=I1U9!UH'^>_Y[V/H +M9.@$"`((4Q]6'W/Y;_F7U)K4,_0S]"8;(QLJ##`,*_@B^`GX$_AP_F?^F@RA +M#'@3L%70!?`%[]6_UY`7P!=O5T]=OMW.VM#ZP/ +MAR*((GK^>/Z1ZI3JO/VZ_0L/#0_8#M8.R/O*^^'BW^*UZ;7IX1+D$EL75Q8)'1$>$5'Z4?KW]_;W.P$]`:S_JO^*!XL'5!!4$"0#(P,F\RCS;_EL +M^;8!N0'/_LS^S071!4C^0_Y4Y5GEH?B>^,4BQ2+F$>@1KN:JYJWPLO#%$,,0 +MXQ/C$TP"2P($[`7L0NA!Z`4&!P9E'V,?`0$"`;?8M]A=\5[QGA^<'T0711?( +M],CTL_.R\YG]G?VK!:0%`Q(+$GP!=`&ZZ,#H6O56]4,+10M3`E,"VOC:^-\% +MWP46`A8";/9M]E,%3P69$I\2GOV8_2GI+^F%^H'ZW0S=#(<+B`LS`3$!I^NJ +MZW;M<^U^%8`5`2(!(G7U<_7JW.WA%]$:`0G!#Y[O[NZ^;FYL(/Q0_,'LL>L3 +M[1._Z;WI0N9#YK<'M@(( +MXA7N$^X[&3P99Q)G$HSPBO#^Z`'IF_:8]F$%8P4\#SL/C@6/!>/LX>Q;^5[Y +M(A<>%U`,5`P>^QS[=`!T`(C^B/[B\^/S5P!6`"$,(@R2^)'XX.O@Z\/VQ/97 +M!%8$;0MN"TL+2PN?_I_^[_/N\UD,6PS1'\X?'/H@^KK:M]HP^#'X$142%;<% +MM`4W\#GP[>GNZ=WVV/:7&9\90B`Z(.ST\?2GXJ;B#0D+"9D>FQY8`E<"E>V7 +M[2GW)_>Z^KOZLOJQ^N<#YP/I^^K[_O#^\(@$AP2M$ZX3&`<6!]L$W02^";T) +M#/@-^*'RH/(P"C$*/%X\:7CI^.B\*#PGP6B!8<5 +M@Q40!Q('T_#3\"K]*OVK%ZP7(P\@#QSS'_,W]C3V&@`>`-+WSO?A_./\-08T +M!DGY2?E<\EWR%P05!/D*^PIY!G@&R0G)";7]MOWW[_;O-@8U!D,;1ANR_:_] +M4MM4V_+N\>YL#FT.$`D/"3OX//A&\T3S<_MV^Y07D!=[(H$B7/E4^3K>0MXH +M`R(#R1W,'0'_`/\DY23E=>]V[QO^&?[@!N,&>PIV"B'])_VS\*[P<0)T`G$4 +M:;X);@*@TP#>09WAGH^NWZ +MC^N,Z^+TX_10!U`']1;V%F8*9`KLZ.[HN_"Z\+P7NQ=C$F42+?(K\FSO;>]Q +M_7']JOVJ_1+^$/X@!R,'3_],_];XVOBF!Z$'S@K4"MG_T__>`>(!-P`V`#'O +M,.^*^XO[#!8-%F\!;`$%XPGC0?8\]G\6A!;5$-`07_ED^8+O?^_O]>_U@0V$ +M#;X;N1O/_=3]I=RAW%'W5/>S'+`OL[>R,`(D`;Q%S$0+^_OVR[[7O8_QB_%SY?09[!H@/B@\Q +M^B_Z-_(Y\A,&$@:V#+8,)P,H`]3\TOS=\]_SO_6^]940E1"H$*D0@NE_Z2WD +M,N3M"^@+?!V`'>X#[`,\\#OP3?=0]P(&_P6J$ZP39`=C!^OCZN.6Y9?E01!# +M$)<7DA<*]0_US?'(\6`&908T"3`)M@FY"2P(*0C?]^+WQ/'#\4<$1@0H"2H) +M3/A(^(;XB_BK_J;^8/1E].#XW/C2$]43&`X6#HKOBN\D^R7['1@<&"D0*A!( +M^4CY+_`M\'/Q=_&%`H`":Q)P$J?XH_CHV^G;8OAD^$XB2R+7%=D5Z?'H\;GS +MN?,T!C8&)@TD#2@-*`UF^V?[_.?ZY[_WPOF +M!U(#40-0"5()^PSY##7^-OX_]C[V-P0X!/P&^P8*^@SZ=/9Q]@+S!?-V\W/S +MG0J@"D0701<3^1;Y*NHHZCD..@YL'VP?[P3O!%GO6.\(\`OPG/^7_SL10!%V +M"',('N0@Y"/@(>!6#%@,.QTY'(1FPV:#1_X'_A7[UCO3.U+[?;\]OR\%+X4`0?^!K?DNN0F]23U +M,2`S(.T8ZQ@#^`;XXO'>\<<`RP`Y"C4*]`;X!BSV*?9?XV+C$O4/]704=A3X +M"?8);?%P\4G[1_N5#98-F0>7!X<'B0=Q"G$*YO?F]]CTUO0*!@P&/@0]!`OV +M#/8-]POWWOG?^;KVN_;5"-0(.Q<\%U?]5?W"ZL3JD06/!3<<.1RO!JT&`O`% +M\*?QH_'0^=7YR`K""K`-MPW\[_3O/N9%Y@D-!`T"(08A\_[P_@GJ"^H$_@+^ +MUPS:#+<(M0B"`8(!.O0[].#KW>MZ`8`!;Q%I$7O]?_UV]73U!@4&!9`$D03- +M_LO^3@Q/#+T*O@HT\S/SZO3K]"T'+`>-!8T%C_>0]PKU"O7D^>/YBP&-`7$1 +M;A'$"<@)'^P:[&GS;_.$&'T81Q=.%QCS$O,1[!;L[OOJ^T$'0P=-#DT.S_W. +M_9#GDN?+]LGV*A@J&`80"1!O\&KP*?8N]M\(VP@M!C`&Z`#F`+__P?]S]G#V +MK_2S]/T'^@?1"=()L?JQ^O3^\_Y+`TT#X?O@^ZP#JP/,#\X/K_RL_/?H^^@& +M_P+_%Q,:$XP!B@'M[N[N[_3O]-0"U`)O#V\/]A+U$F3W9??AX^#C+@4P!0$@ +M_Q\5`Q8#>^=[YT'U0/5M!&\$T@7/!94$F029^)7X!/0'].((X0AM%6P59_]I +M_YOPF?`[`CP"GPB@"!S\&?S@_./\SO[,_D;R1O+$]L?V:@YE#B$+)@LB^QW[ +M:/UM_>/_WO_J`_`#JQ"E$-,)V`GBZM[J=^IZZ@H-"`T%#P$'P0@1"#^G_Z1OA)^*T&K`:Z_[G_$OT4 +M__AE$F<2.POIY^J/HI.A._T[_615:%1[[&OO=X.+@UO+2\AH-'@U$$$(0A`B$".7ZYOI4 +M\U+SZ0KL"H0;@1MJ`6T!@.M^ZS_Y0/G`!+\$H_FD^0;X!?AU_7;]BO>)]UK] +M7/W]#/L,\`GQ"6W^;/Z9!)H$=`AS"*O^K?[H!.8$H`2A!$GL2>S]ZOSJ.@@\ +M"&@/9@_%]L;VD/"0\((!@0%&#DD.'!,8$Z$%I`4&[P3O:O-J\SP3/Q./$HD2 +M[.KTZO#EY^5J`70!6PA1"%P!9`%;`54!U/S:_+S\M_PV$#H0.Q`X$.KWZO>* +M]XWWN`:U!CP`/@`8]!CT0/P\_%?]7OVJ\J/RY?_L_[\1N1%O!G,&V??6]^S^ +M[_XX!C8&0`E""40.00X<^Q_[Z4[93M@@V"#6$181'+\,OPO.B]Z+T%N@57$5P1$`0+!)#\D_P-^`SXVOO9 +M^\L-S@U3#E`.C?2.]'KO>N]A!6$%R@;*!HS\C/S'`<@!(0(>`JCWK/>T_;#] +MW@SA#+`$KP1^]WWWT_S5_!X!'`%*_TO_P0/``VO_;/_\[OON8/QA_*X9K1D( +M#`@,)NXF[@/T`_0Q"C(*F@Z7#H`%A`7I].3T[.CRZ.#]V_W'%LD6F`69!?WJ +M^>JX][[W\1#L$*H)K0E:_%G\H/Z@_C_]/_V%`(<`,PHN"G`!>`$_\#7PU??? +M]V4&70:?_Z3_??]Z_^8'Z`<'_@;^8/=A]PL*"`K#$\<3`?S^^RGN*NX7^QC[ +MM`*Q`I($E@2^`KH"F?*<\O#N[NX$#@0.:!QJ'"3\(?RNZ['K<0)P`J(1H!$? +M""((.OLW^UWS7O.*\XSSL`2M!*<*J@JG]*/T:^YN[OX'_0>G$J@2(@(A`K'_ +ML?^-!(T$/_Q!_/7_\_\#"@0*\/WO_;?NM^XF]R?W2P%+`2X"+@*,"8L)M`2T +M!%#S4?,X^S?[>Q5]%?$0[A#]\O_RYO'G\7T#>@/2!=8%V/_2__CT__21[(SL +M6_U?_>P8Z!B9#9L-Q>[#[LSXS_@2%!`4[1'O$<;^POX$]0GU1?-!\SOX/O@8 +M!A8&V@#;`('O@.^-^8[Y\POR"X$&@@:4`)(`1@I("NX$[018^5CY.0]#,0-!#J#>D-2OI- +M^OGT]?1H]&OT]?OR^UT)7PF_`[\#9_!E\//W]_I8ZK'WM/=%"T(+905I!0;Y`_ED]&7TRO7+]>8( +MXPCH%.P4T/[._EWN7>Y0`U$#'!0;%*0"I`(>]!_T5_=7]ZO[JOLY!#H$7@I< +M"J#_I/_6\M+R>?U]_=X)V@DM`2\!2?]*_\<$Q02>^Z#[_/;Z]NP'[0=P"W$+ +M)/4B]KU[O7Z`_@#8@MC +M"U@"5P(``@`"&?\:_P'T`/35_-;\C0V,#;'_L?\RZS3K@OI_^@(1!A'F#>(- +M5`!5`/[V`/>L]ZGW&0<7]?^:$YH'X?/@I#R\/W`/6`]OTX/1E +M^V'[40)4`E,%405@#&$,E0.4`TGW2O>8`ID"KPRM##7_-?]E\V?S=_ES^0SY +M$_FY][+W$`@5"*$,G0S]^@#[^O;Y]CX(/0C;#=T-'`88!M4`VP#=]-CT[?/P +M\U8)4PD-#`\,NO"Y\,/IQ>GQ!>\%T!'0$6<":0(5^A+ZV/W;_5H&6`;`$,`0 +M80MC"^WPZ?`AZR7KO02[!+\(P0C"],#T%_07]&D!:0'G`^D#70=;!QP-'@U$ +M`D$"3OI2^G,%;P5S"7<)@_Z`_@OZ#/H9^AKZH/*>\AKY'/E?"UX+U@/6`QSQ +M'O$&^P+[[JZ^JA`J("%0T4#:_YL?F2\Y#SEP&8 +M`1L'&P>N"*X(_`KZ"GH!?P';^-7X2P-/`U`'3P=<^5KY9O=J]S+^+OZ=^J#Z +M`XWKB^L"]@7V_`[Y#MP+W@OF^^7[F?R8_/,#]0,Z!C<&?PB#")G^ +MEO[S[_7O0?D^^:8+J`M"!$$$J?.K\R[\+/QM!FX&<@%P`7H%?04O"2P)7?Q@ +M_/7V\O:J`ZL#BP:-!KC]MOVQ_K'^Z/SJ_,SWR/?-`]$#.PXZ#KG]N?U$\$/P +MQ@/'`_P1^A%P`W(#2O=*]R+X(OA#_$'\X`7C!;8)L@G>]^/WBN^%[U('5@KRZ/(%`0SYI`^A#_P+_PM$^D'ZA_R*_(0%@06F +M`*@`C/V-_>P`Z0#_]P+X+?0J]'D">P)Y!G@&1?U'_6W_:O]6!5@%*0$H`38& +M-@:1#Y(/T0#1``SP"O!&^TC[T0G/"=;_V?_9\MCR)_8F]BS^+/[T"/0(F0V: +M#0#^`?ZB\J#R:`9I!LL6R19D`V<#0?)`\D7Y1/E*_TO_1_]%_XT`D`#U^O3Z +M[?3L])8"EP)!#S\/KP.Q`P+\`OQM!6P%-0W];7U1`1$!-(+U`L^`3L! +M?_^`_RT&,`;-`L<":`%O`9`'B0?W_?S]L.ZN[L;YQODG"2D)QP##`$?V2_8C +M^B#Z3@%1`9@)E@EF$&<03`-,`\GSR//M`>T!IA&G$6D`:`#SZ_3KN_2Z]!@" +M&`+T`?4!3@%-`>+[XON=]Y[WJ06H!>@2Z!(>!Q\'?_E^^?,`]`!$!$,$Z/SI +M_"[_+/^@`*0`7O98]FKU(%V0G3"3L'0` +M`9(&D@;4!=,%Z@+M`O'V[/:J]+#TU0?.!Q@.'@Y?^%OXJ?"J\!$#%`.T"Z\+ +MP@3&!,,!P`%._T__P?O#^X\#BP,'!@L&9/=B][WTO?3$`<8!<@)O`M'\U/R, +M!(D$Q@;*!HS[B/LV_SO_J@RE#+X'P@?:^M;Z)?LJ^QG_$__>_N7^&P,4`]G] +MWOU1[T_O6/A7^/0.]@Y!"D$*3O=+]Q[](_U8#%(,V0K>"KD$N`2P_*_\JO*K +M\BCY)_DR"3()Z@+L`HSQBO%;^ESZ\PGR"70$=`2*_XS_:P1I!*,!I`&N_JW^ +M90=F!P,%`P62]I/VY?CB^&4!:0$8_1+]WOSF_(H$@P1W_GS^5O=2]WP(?@@] +M%3P5N`2Z!`?V!?8$_`3\DP&4`6L`:@!`_T+_G/6:]6GQ:O&S!+,$N`^W#Q;^ +M&/ZH]:7U[@;R!L8.PPX'!0D%(P`@`!/]%?W'^9 +M!IP&00`\``O\$?Q(\T+S8_=I]Z$(G`AE`V<#/O$_\<+YP/E"#T0/\PWQ#28# +M)P/_`O\"HOZB_N3YY?DG`B4"K`.N`UGV5_9W]7GU7@%<`1H`'`"F_:7]C`:+ +M!KD#NP,1^A#ZIP.E`Z(.IPY-`D@"(?$#WP/8$]H3!P,%`P7T!_3V`?0!,PPV#/\"^P(T_#C\'_H;^A+W%O=< +M`%D`1@E("3#]+OT+]0SU``,!`W`*;PH_`$``G_^>_S,%,P6:_9O]I/RD_+\' +MO@@,.!!`$T/;.]FCW:O>@ +M!I\&PP;#!@/_`_^0`Y$#,P(Q`M7VU_:K_:K]_`G\"63_9?_X\_;SVOS=_-X$ +MV@3Q`_8#`@;[!:,`K`"Y]K+VF0"<`!0.%`Y<`%D`.O`_\`7]`OV`"H`*@@*# +M`J/[HOLF^R?[@/F`^4D"2`*<-R`'&`0;T"O3V`?$!I@RK#!S_&/\&^`GX +M6OY7_DO\3OQL^FCZZ@3N!"X%+`6'^H?ZH?^B_[,(L0@D`R8#5@!5`/0#]`.6 +M^I?Z8?1?]%@$6@1J"6@)5O=9]]?SU/.2`I,"'08[]_OPP/"`R<')P?_^`#YH?>@]T;\1OQB_F+^9PAF +M"+P+O0NO_:_]6_I;^E,'4@_=^]_8(\@@Y!3P%[__N_Y("D0*,`(\`[P3K!*@.JPZ7`I8"(/`?\`GZ +M"_HQ"2\)2_Y-_B+T(?2!^X'[+?TM_>#\W_S?!N(&0P=`!Q/]%?UV`G0"*`TI +M#:4$I025_);\-`$R`3;[-_M?\E_R'_X?_J4&I0;<]]WWC?.+\Y@%F@5I#&<, +MTP75!8<#A@,R_C+^C_J0^JX&K`::"IT*;?9I]F/O:.\,`@<"4P=7!YSZFOH_ +M^3[YZ__O_QD"$P)F"6P)7PQ:#`']!?W5]M/VZ07H!8('A`?;]]GWZO;M]IK] +MF/T?^1WY:/QM_"L))0E2!5@%E?N1^V@#:0/""<0)50%2``K[XQ/C!_KS^L/ZS_ISVF_8V_S;_Y`CD"*W] +MKOUA^&#X'P8?!D\*4`H.`@T"`P$$`>/^XOYF^6?YA`*#`F8(9P@O]B_V!^X& +M[MP"W0+^#/X,"P`+`(W[C/N7`)D`<0!N`-L$WP0C"2`)2/Q)_*WTK?0T!#0$ +M?0E]"=/YT_D.^`_XJ0*G`N+_Y?],_DG^%`<7!VD!90%T]GCVR@#(`+X)O@E5 +M_U7_0?M"^^``W@!2^U7[2OI&^CH)/`E3"%0(!_<&]]OZW/KQ"N\*109&!M_Z +MWOHE_"C\#_P,_!C]&OVG!Z8'10-%`^WQ[?$H^2GY#PX.#FL(;`C1^-#XS/W, +M_2L$+`2V`+8`SP//`\4#Q`-S]W/W9OAG^*\&K@:E`J<"E/>2]X+_@O\1!1$% +MROW*_:("H0*/"I,*2_Y&_GKU?/50`4\!R@7+!?_\__RW_+C\.OXV_@CZ#/IJ +M`F<"Y0SH#/C_]?_.]-#THP2B!-8-UPWQ_N_^Y_?I]UW]6_W2_-3\-0`U`-,% +MT07;^=WYD_&3\30$,@3>#^$/&`$4`5C[7/N5!9,%<`-R`P+^_OW,`]$#-@`Q +M`"CT+?2O^JSZ<`5O!7C]>_T=^AOZVP/=`W<"=0+R_O+^V@G;";`)L`FN]Z[W +M"/@(^,8'Q0?V`_<#&?@:^,GYR/DW^S;[D/N3^T,'/@?X"/X(,_@O^/SZ_?KW +M#_@/X0O>"Q[Y(?E+^DGZL`"Q`(#\@?S2_=#](O\B__[T`/5G]V3W\0CU",T( +MR@A'_4C]!P,'`Z<)I@EP`'(`QO_%_U4'50=2_5+]`?,`\VG]:_V2`I$"`_D# +M^3/Z-/JP`*T`G?VB_;`#J@-U#WH/3P1+!)WUH/5<`ET"A`V!#3'_,_]G]6;U +MA_J'^G_Z@?JW_+3\X07C!87_A?\J]2GU,00Q!-41UA'(`L<"\_?S]ZL!K0%= +M`UH#G_RB_-O_V?_H_>C]H_.D\_WZ_?IW"'8(``(!`N7[X_M$!48%105%!?/] +M\OVJ!JP&K`FI";SWO_?P\^[S]P+X`N(!X@%X]GCVOOF^^8O^B_[<_MS^%@D5 +M" +M$!4$$00!]@3VDOZ0_D@"2@*7^Y3[9?]H_\P`R`"V][OW<_UO_1X+(0M3`U$# +M!?D$^=`!]K[V_NH +M_:?]2P%,`8;]A/UC`V8#20A%",OYS_G=]-KT``<"!S8)-`E?]V+W>?9W]OH! +M^@%6`E<"8`-?`[T'O@<=_A[^EOB2^`\'$P=]"7P)3OA,^&[W<_?B`MH"]_[_ +M_M3[T/N4!)0$``$#`6WX:/BW`KP"!@H#"N']XOW.^L[Z\P/R`UH`6P"L_*S\ +MC@:-!A(#$P/9\MCRP/G`^<0*Q@I%!$,$:OIK^N0`XP`Q`S$#I0"H`!@&%`9G +M`6H!;O)K\MOYW?FK"JP*;@%L`4OT3/2?_I_^V0?8!UH#7`/[!/@$3051!0WZ +M"OKW^_G[*`DF"9$"DP+-]9]Q;Q%_'P`^X# +MJPRL#%7^5?XG_"?\#@8.!B$$(01^`G\"#P8,!F/\:/P<]1;UEP.=`\X(R0C. +M]M+V7/-8\QT"(0(T`S(#./\W_WH'?`>X!K8&H/RB_(8#A0-."TX+%/X5_B+V +M'_9,_E'^0/PZ_*GVKO9H`64!_03_!'SY>_F@_I_^)0XG#K4'L@=8^US[D@". +M`*T#KP.?_Z#_1P1$!'T!@@%Q\6KQ__0&]1`("PB_`\(#M_6V]6/^8OY:"5P) +ML`6O!!=P%A@"&`"KR+/*L^:CYK`2O!&G_9_]N +M`&\`M`BT".\`[P"&^H;Z2`='!^$)X@GL^^S[0/L_^UP5Y!>,`Y``.`PT#[OGO +M^07P!/#`_<']Z`CH""?[)?L.]A#V4@51!1P('@@X`#8`Q0/&`TH$2`3=^^'[ +MC@&*`4L*3@H-_@S^R/3']&_^`=L#W0/4^=/YX/?B]TH&1P9*!$T$G_2< +M]);YE_F?!J$&$P(0`J__LO]Z!W@'-P(W`G#ZA!*0$&_@8^$?\2/R?`I\"4OY2_J,!I`%A!6`% +M^?GX^;7XM_@."@P*'0H="H#XA/A!^3KY:0-P`^W^Z/X8_!G\[P#S`%;\3_RM +M^[3[OPJY"H0+B`L+^@GZ6OI:^@(&!`:I`*<`._HZ^MG_W?\U_3#]*_`U\#)`,B`T((1@@G_B'^H?>G]]<$TP3J!>H%R_3/]'3T;O0\`4$!&`$4 +M`9[^H/X!`P$#@/Y__M;[U_O^"?P)I`VF#>W\Z_SF^>GYF`65!?P`_@#9]M?V +M7/M=^_[[_OL+]PKW30%0`7D*=0HW`#H`NORX_.D'Z@<[!CP&GOV<_4P"30+* +M`@%+OXK_D'U1/4P_R__(P4C!2X!+@$-!0X%]`7R!6[\ +MOQ[_/;]3`!+ +M`.(+Y`N5!I,&^_G\^=0`U`#:!]H'(?T?_<+YQOF.`(H`X?KE^D;W0O<;`QT# +ME@25!$'Y0OE6_57]Q@?'!W@#=P.Q`;(!20=&!R?_+/]Q]FOV-`$Y`1L&&`;L +M]^[W'/8;]AL#'`.N`ZL#V/_;_R$$'P2*`(P`P/J_^E$%404+"@H*>_E]^97T +MDO0$`P@#)00A!.OZ[_I4_E#^]0#X`$#[/_N'`88!\@GT"3,`,@"9^ICZ:`5J +M!4@%1@7T^O7Z%/X5_L@`QP`^]S[W&OH9^D@'2@>@`IT"S?C1^)X`FP#2!M,& +MUP#6`*D!JP&:`Y<#D_J7^E[[6ONO![('3@).`MCTUO2(_(O\UP;3!C8`.@!I +M_6C]H@&@`7#]=/WL_>?][@CS")4$D03P]?/U6_M9^QH&&P8N_R[_-/LT^R,# +M)`/^`/T`@?N!^T8$1P36!M4&7?M>^R[\+OSF!.0$A_^+_[WZN/IJ`6X!"_\) +M_R?X*/A@`E\"P`G!"5O]6?W=^.#X6P19!&$%807-_L[^7`%:`1K_'?^J]Z;W +M(P`G`#8(,PAZ^WW[J?6F]3<$.@09"!4(:_YP_L/_P/_<`MP"V_S=_)G_E?]B +M!F8&V_S8_/CT^_2Y_[;_#000!,G[Q?L"_P?_Y`7?!5__8__9_M?^"PD,"6\$ +M;P0$^03Y3OY._I<#EP,J_"K\./LY^Q__'?_N^/#X$/H/^FP(:P@P!,P$T`?'T\?0-^@SZ +M$0@4"/0#[P-+_5#]N@.V`]<#VP,O_BS^RP/,`X@%B`7Z^/KX&O<:]VL!:P'U +M_O?^"/H$^E@"7`)O`VL#DON6^Z0"H0*%"X@+O@"Z`'/Y=_G<`MH"?`-[`\[Z +MT?K;^]?[9/QG_`'W`?=8_E7^[`CP"!\`&P`$^0;Y'08>!@H,"`S2`=,!:O]I +M_\?AW^,?ZR?HI"2<)KP:P!CS^._X``P(#(`0>!,S\SORP_Z[_A@*' +M`L_WS_?@]>#U=0%T`5P!7P'&^L#Z-`$[`9P%EP4F_RC_#@,.`[T*O`J)`(D` +M_/;]]A+_$?^``H$")_LF^Z'\HOS"_\+_D%T`C5"`+\_/O`^L;Z<@5N!7`"<@+I^>CY +MMORU_#_]0OU,^DCZ4P)8`D4&/P:$^XK[#OL)^VP(;PC%!L0&[/KL^MW\W?S7 +M`-<`SOS/_%4`4P"7!9H%9?U@_6CY;?FA`YX#)`0F!#?[-_N%_H/^.`,[`^;\ +MXOR?_:/]T@7/!=7_V/]S]W'WR/_(_W$%<@48_A7^U?W:_0H#!0-B_V;_#``* +M`"$((`B``8(!A/6#]2S\+/ROK@`.4`7@!8`.W_\__2!LL& +MP@+(`E'X3/B(_HW^^@?V!]O_W?^]^;[YF@"6`!4`&@!Y^W7[E`&6`0P##`/K +M^>OYH_RB_'0'=@?&`\,#V_W>_>H"Z0*K`:L!9/ID^JG^J?X4`A0"(?@A^(7W +MA__QW_COJ.^D/Z1?J9`Y4#Z`'L`0#[_?H\`CT"X0CB"%T!6P%N +M_G#^C0*+`G/^<_Y`_$'\*0,I`Q,`$P"^];SUK?NP^_`%[`43`!@`I/N?^VL" +M;P+H`^4#HP*D`G<(>`C>!=P%'_DB^9/ZD/HU`S<#6/Q7_*STK/0M_"_\90%C +M`0W_#?\U!#8$@0=_!U/_5_^#_W__C`B."(0$@P2&^X?[U_[7_BD`)P"1^93Y +M]OSS_"4"*0(`^OSY2?=+]VD#:`-.!T\'M`"S`!L#'0/E!N(&H0&D`5,!40$Q +M`S,#)_DE^;/SM?/2_]#_!P4*!>7[XOMF_&G\!`0!!&P";@+]`?T!BP:+!@4! +M!`$I^BKZZP'K`=P%VP6=^Z#[1OE"^1``$P#,_V![`&KP:R_++\\_[T_E,$4@34_]7_D_Z1_H`!@P%U_'+\ +MO/J_^F,#80/<`]T#$?L2^YO\F/S#`L<"A?^!_VK_;O^&!8,%R0'+`9K[F/NL +M`:T!RP3,!)?[EONO^;#Y4@!/`)C_G/\S_R[_]`3[!(<"@`+0^];[,P(N`B0( +M*`BH_J3^:/AM^!_^&?Z<_Z+_G/V8_3`!,@%\`'P`QOK$^ET`7P#9!]@'_@`` +M`?S[^ON``X$#F068!8[_CO\7`!H`#P`+`)KXG_CK^>;YU@'8`?S^_?Y#^D+Z +M1@%'`8!-`0T!&8&:0;._"(4! +M@P'_]/_TC/Z-_G+YL"ZP(M^"[XOO:\]@/_!?^@`YX#NO^\_YS_F_^L`*P` +M1P-'`\P*RPJW"K@*T/[/_ESU7?6B_*+\O/^Z_S'Y-/E6^53Y(OLB^]CXVOA# +M"$$(A!*%$KT"O@+7^=7Y9`5F!6X%;04&^P;[`/@`^-[WWO<1^A'Z20-)`YH' +MF@<2_!#\3_Y3_O<&\0:4!9L%T`#)`"8`*P#,_LG^HONC^X'_@?];`EH"B/V* +M_=KXV/@%_@7^9P)I`GC_=O_R`/(`:P9N!N#_VO]A^FGZ-@0O!!@%'`7Z_/C\ +M"_\,_RK]*?V`_X'_&0<8!P0$!01(]TCW?O1]]#0`-`"6`98!9OQF_)D`FP#/ +M!,L$R@+/`OX*^`JS![D'&/P2_!O[(?NZ^[;[Y/OF^][YWOG>^-OX$O\6_P@" +M!0*\!+\$-PHT"KT#OP/Q_N_^!`,&`U<`5@#!^<'YQ?K%^K']L?W7_-;\F?B< +M^.L!YP&.!)$$.0`X`!0($P@#"08)V/S4_&W^+Y +MX/ED_V;_=_]V_W__?O\2"!8(%P42!_AW^I@.F`YP`G0"G_*;\XOOC +M^]_^W/Y8_EW^_0#Y`/,"]0(X_SC_?P%^`>$%X@7Q`/$``OP!_+7XMOC=_=[] +M5?]3_UW]7_V2`9`!B@*+`@D!"0%)"4H)R@?(!XCYBOGG^N;ZJORI_+K\N_RB +M_:+]JOVJ_:_\L/P;`1D!@PF#";X(OPB\_;K]%_P;_.7_X/^R_K?^RO[&_G[[ +M@/M[]WOW7/Y;_OX$_P2T!;,%=01W!"(!(`%M`VX#P`._`[T`O@"Q_+'\P?C` +M^#?T./0.^PW[C@*0`AH`&``,_PS_2@1,!-P'V@<@""((P@G`"9G_FO_`]<#U +MZ?GI^?3^]/Z-^HSZ?_B!^,/]P?U^`($`I`2@!$D+30N:`I4"EOJ<^@("_0%M +M!7$%3_Y+_G[Y@ODV^C+ZJ/^L__(![P&#`(4`^0'X`0W\#OPD_R3_>@=X!YD! +MG`'\^?KY%OX7_A0"%`*M`*P`Z/_J_T/-5^%7XF?F9^5D"6`(B!B4&M?ZQ_F<`;`!P"VL+W`K?"J@" +MJ`(D_B+^N/B\^$SV1_;*_,W\B?Z)_H;YA/E-^5#Y'`0:!#H).0G6!MD&^@3W +M!)$!E`$8_17]P_[&_O4!\@&S_+?\L/BL^"+Z)?H#`P$#UP'9`8W^BO[S`?@! +M\OWL_?3]^OU1!TH'70%B`;_XO?B``($`LP2S!#H`.`!=`&``+@(J`KK^OO[: +M^MCZF`"8`+(!LP%,]TOWWOO>^PD""@(C`2(!\0'Q`;0"M0*.`8T!A`2$!$X$ +M3@0B`2,!#/H*^L?VR?;G`.8`XO[@_D;W3/>G_J#^9@9K!EP%6`52!E8&PP3` +M!+/]M?WB^^'[4@)0`G7_>?\W]#3TWO7@]5H!60%-`TP#@@6$!9$#CP,=_B#^ +MU@/4`_P)_0G;`MH"V??9]^7XY_AD_&#\I/ZJ_D[]1_U4_5O]7OY7_HP`D@`I +M!R0'1@E*"_LE_2C]VP#8`*G]JOV@`*$` +MFP6;!?W_^O_M_/'\1P%$`3T$/P10_D_^H/>A]]7_TO^L`+``<_MQ^QX#'P-V +M`W0#N/VZ_6,#80/M!?`%QOS#_'S[??MO_F_^=OQW_+_ZOOI]`7T!0`5`!3'_ +M,/]U`GD"O0:X!H'_A?^[^[?[9?]H_P_\#OQU]G;VO/J[^NP"Z@+,`]$#)@$A +M`1T%(05!!S\'X`'@`<4!Q@&Y`+D`1_A%^+[WPO>J_Z;_=OMY^_#W[O="`4,! +MW`';`6(`9`#T!O$&A`N'"^4"X@+9_-S\'@$;`7'_=/^C]Z#W7?A?^#K[.OO- +M^LSZC`*,`OD%^P5$`T(#/P0_!+X#P`-M`FD"C_V5_>?YX?GY_?S]Q/O#^U3[ +M4_OO`O(",@(P`G'^`!_`$\%2@7=_.#\Z_OJ^V[_;_^S`K$" +M!00'!,8"Q`("`@,"^?W[_6$`7@!/!E$&'P`>`'_V?_:F]J?V"_L)^UO]7OUH +M`64!/0)``L\"S`(9!1L%RPO+"T4(0P@M_##\_/KY^@'\`_Q<]ESVJ_>J]SG_ +M.O\2_!+\U_W5_3D).PD#"@,*C`.+`R`"(0+V`?4!1/]%_V/\8OP&_`?\J?JG +M^BWZ+_IP_6_]?/]]__+]\?VK`ZP#=09T!F8"9P*``H`"-00T!$@#20-1_%'\ +M`_H#^G7^=/[G_.G\1_E$^=T`WP#,`LT"B0"&`/D$_`3G`.0`]?SW_*(`HP`H +M`"<`5_U6_*_HG^601;!"O\ +M*_S:^MCZ+@`P`.@&Y@;/!]$'\@#R`-[\W/S1_-+\_O_^_SX#/@.9_)K\J?6G +M]2/_)?]\!7L%]`'T`;4!M@%Y`7P)F@*;`E/U4?7%^,?X +MA@&$`7'^D`[@!N^VK[8_YE_I'_D/]Y_'K\S?S,_(4"A@(=!AL&J0&K`>/^XOY7!5<% +MB0*)`I']DOT0_0[]4?I4^GS[=_O'`,T`+`4F!:0"J@+L_N;^C0&3`=H"U0)* +M_TW_MO^U_SK\./SZ]O[VH@&?`=P)W@G*`<@!V_S<_.+_XO]'`T8#20%*`?/] +M\_V0^I#Z+_LN^Z\`L`!F!60%3P12!$3_0O]8_EG^S?_-_R8$)03#!,0$P_W! +M_9/WE_?H^^3[P0+%`B0!(`'+^L[ZA/N"^R8$)P2%!X4'8P9C!J@%IP7E_N?^ +M^_WX_2P"+P(K_"C\2_9-]O#X\/@;^QK[D?^3_Q`("PC4!MD&M0*S`LX#SP-\ +M"'P(&@$8`6WV;?9%^TG[J_ZF_A7[&?O&_,/\7P!@`.L![`%:!%H$M0.S`W@" +M>P(3`0\!R0#-`(,#@@/'_<;]]_GX^1X`'0"'_8?]L?JS^I,`D0#B`>,!5OU6 +M_=D!V`%/"5`)UP36!`+\!/QI_F?^Y@'H`1[]&_VP_[/_S`#*`"S]+OVV_[7_ +MPP+"`DW_4/][_'C\;OUP_:+]H?W._LW^0`)"`I@%EP67`Y<#DP&3`4$&0`:` +M!($$S_O.^W/Y=OF0^XS[Z?CM^%GY5?G'`O9O_FW^G`.?`^?_Y/^6 +M_YG_-@,T`V4%905B!60%:@%G`5G_7?\,_0G]U/W5_4T`3@`M_2K];_MS^Z[_ +MJO\5`1H!``/Z`B('*0?P`.@`:OQQ_)P`E@"J`Z\#,_XQ_A;X%OAF^V?[I/^A +M_\\!T@%,!DD&Y03H!$(`00"U`K4"#P,.`VW_;O]#_D'^Y_GJ^5+Y4?F0_Y#_ +M,`0O!$0"10+)_,C\=`!U`*4&I08J!"D$L_ZT_F3_8__3_M7^B?V%_;$`M0`# +M`@$"6_Y<_C7\-/R(`8D!:0-G`[(!M@%1`DP";/YP_K3[LOMI`FD"M`2U!#W[ +M//NA^J+Z'00]`+@` +M,0!![<&O0;]`O<"YO[K_F<#9`-.!%`$Z/GE^0'U +M!/4S^3'Y%0`6`,T!S@$#`@`"#000!`0#`0.``X(#6PA;"`$$``1@^6'Y0OI! +M^F#_8/\;`!L`?_^`_Y?\EOP#^P/[K0"N`'\'?0?J`NP"O/R[_-X`W@!B!&,$ +M@@&!`4(!0@'!`L$"8/UA_2W\*ORF`*H`R0'%`?7]^/W+^\G[:OYL_@$!_@!> +M`F("K@"K`&3^9OX:_QC_(P4F!8T'B`<-`1,!G_^;_],!U`$*_0S](_H@^FG] +M:OU1_%+\Q?G"^>\`\P`/"`P(E`65!0P"#0)O!6T%G`2=!.S^[?X,_0G]^_@` +M^9#XC/AD_F;^6P!:``7^!?[K`.P`X07?!8$&A@9_`W<#N0+``HP#AP,*_@[^ +MH/R=_&__@%VOW:_8OVBO8G_2C]7@!=`&?\9_S@_>+]V`#5`(H!C0&S`[`#NP6^!>#_ +MW?\V^CGZ6/U6_;(#L@/5`M8"Z/[F_C[_0?_<`-D`1@))`EX!6P&;_9S]V_O< +M^]#^SO[;`=\!%P`3`"4`)P#T`O,"IP&H`1?^%OZ(`8D!=01U!/[]_/V1_)3\ +MS0#*`"``(P#'_<7]7O]?_^3]X_V9_9K]2P)*`@4$!P3-`\H#"@0-!%D#5P.T +M_K3^1OU(_>W_ZO^S^[;[0O=`]QG]&OW=`]P#!0,&`ZH"J0)%!48%R@3*!&D` +M9P#[_?[]:?]F_]GZV_H9^A?ZZ@#M`+X!NP'R`/0`%004!'T"?0*\_KS^HP*C +M`BX#+@/5_-7\!_P'_``"_P%5_U;_$_D4^6+_7_^R`[8#2O]%_\K_SO\5!10% +MQ@7%!0$#!`,&`0(!)/XF_F7[9?OD^^/[7_YA_JS\JOR4_)7\U0/4`P(&!`:1 +M`XX#KP.S`Q$"#`(N^S/[G/N:^Z\!K@%)_DO^I_JD^@0!!P&&!H0&_0'^`?7_ +M]/_'`<@!<_]R__?]]OTA_R/_!O\$_\S_T/\>`A@"Y/_J_W#^:_[F`.H`"`$% +M`9S^GOY$_4/]HP"E``\##0/I`.D`Y`#F`&4!80'[_0'^9?U?_>8"ZP+5`](# +M7@!?`#3^-/Y>_US_!``'`,[^S/Z__,'\?_A]^';\=OR:!9H%%`85!D`#/P.F +M!:<%?`1[!$T`30!I`&H`./TW_8KVB_;0]=#UB?V(_T%_`7V!_M_^T7Z0_IB^V+[ +M2P!*`(,"A0(Y`C<"C@20!!X$'02]_[W_>_]\_TL`2@#I_>G]3_U1_:0"H0+, +M`LX"=_YW_HW^C?YX`7@!Q__'_WCZ=_IG_&C\!P$'`3,!,P'+`)]X[ZB_JC`*4` +M$`0/!-T"W`(Q!#($TP?3!]D'V@=C!&($%O\5_\KVR_8(]0?U!/T&_<4`PP#G +M^^?[NON\^\H$Q@2F!ZL'&P07!)0"EP*H_Z7_2?Y,_DP!2@%7`5D!E?R4_,3\ +MPOS\_@#_6_Y7_O/_]_\L`BD"RO_+_SG].?TE`B4">05Y!9<`EP"4_)3\.O\[ +M_W(`<``\_3[]J?ZG_OH!_`$'`04!G0"?`"0#(P/Z`/H`;/QL_!G^&?X!_P'_ +M&OT:_5(`5`!?!%H$0P%)`6(!7`%M!7,%:_]F_QKY'?GG_.;\[?_L__+[]?MA +M_%W\7P-D`](%S@7,`\\#QP+&`O4!]`%._E'^I/V@_=K^WOZR_:_],_TU_?G] +M^/VN_J[^6_];_U<#60-Z!'4$I/ZJ_NS^Y_[5!=D%U@/3`Q+\%?PP_"S\A/V( +M_5;\4_SS_O3^,`$Q`:K_I_]S`'<`%P84!N$&X0;&`L@"/_X\_D/Z1?I%^D3Z +M'/X>_FO^:/Y0^U/[^/[U_@P�:M![`'/`,X`XX`D`"``'\`FOV<_5G\5_RX +M_KG^>OYY_C;\-OS&_L?^V@+:`G`";@([`C\"!@$!`:'^I?Z5`)$`IP.L`R'_ +M'/_/^M3Z-?\O_[K_O_\/_`S\:?YJ_J`#H`/Z`?D!Q`#&`/@#]0/_`@(#5O]3 +M_S3^-O[3_=3]LORP_.C]Z/TM`"\`8`!=`$T`4`"D`:(!`9(!D0&#`8,!4@93!HH(B`BG`JL"\/WJ +M_1+^&/XL^R?[=/EY^;3[K_LD_"?\_]\__?_]O_W!/<$2`9)!I,$DP3. +M`\P#`@($`J']H?VQ_:_]&_X>_MCXUOCR^/'XL_^V_Y(`CP!2_E/^C`.,`QL' +M&P +M!+T'O`>%`8(!3?Q3_/7^[_XW_SS_#OL*^]_\XOS"`,``-_XZ_F/^7_[;`]\# +MIP2B!*``I@"?`)D`_P`$`9/_D/]R_G/^M?RV_+_ZO/KG^^O[A@"!`!X!(@&P +M_Z[_*P,L`P8'!@>(`X8#;/]N_P@`!@#U_/C\L?BM^,G[S/MX_W?_%/T4_=K^ +MV_Z7!I4&7P9A!J,!H@'/`-``Y__F_]_]X/UR_G+^1_U%_=OYW_F*_87]1P-+ +M`]\!W@$$_P+_E@&8`:@"IP*B_Z/_00!!`*("H0+K_^K_,?PS_#[_/_\#`@$" +MA?Z&_I;\EOSU_O/^B@"/`&\!:@&3`I4"-O\X_S+\+/R"_XO_I@&=`23_*O]) +M`$8`L@.R`VT!;@%=`5P!:P1K!+__O_^V^+;XW?G=^7W^?/X3_A7^K/VI_?\` +M`0&6!)8$+P8O!LP%RP71`M0"G_Z:_@_]%?WT^^_[ROK-^B3\(_S&_L7^*O\L +M_Y/_D?_.!,\$6P=;!V@"9P)/`%``;0-L`TL!3`$F_"7\]?OV^Y_[GON'^HCZ +MTOS1_&K_;/\.``H`2@-/`T@'0P?2!=<%EP24!&`$801Z_WK_=_EV^6SZ;OH= +M_!O\@_J%^O/\\/P6`QH#Q`3`!(L!CP'A`=X!N0.Y`[T`O0"<_9W]\?[Q_C8` +M-P"__[S_6@%=`40!0@&(_HG^=/]U__[_^__+_,_\3OU,_2<")P*%`(0`^OW\ +M_2<#)`/P!?,%:0!G`,']POW^`/T`O0"]`/#^\/[S_O/^&/X:_L;\P?S]_03^ +MXO_:_PP`%`"E`)X`HP*H`DL$1P3Z!/X$=05S!>4!Y`'H^NKZ&_H8^H[\D/Q1 +M^E/ZQ?C!^-+^U?[0!-`$/`4W!2$'*P=D"%D(/@-&`^']W?V"_8+]7/Q@_'+[ +M;?MS_'?\^/OT^R3^*/ZF`Z,#=`1W!(D`A@#0`-,`8@1?!+L"O0+@_N#^&O\9 +M_WO_>_]I_&K\`OP!_$G_2O_[__K_)O\E_SL!/@%R!&\$&@0/\@_1[]>/][_XH"A@(1`Q0#%004!%T#70-2_U/_ +MD?V0_='^T/YA_&3\9_ID^F'^8_[[`?H!Z@+I`K4%N042"`T(9`-H`]#\S?R^ +M_,#\IOZE_C;[./NX^+7X>OU\_10"$P(2!!$$,@4U!:,#H`-9`5P!#`()`I`! +MD0%W_77]`?T%_;+^KOX@_23]0/T\_9@!F@&A`:$!%_X6_K$`L0"#!84%ZP3H +M!.4!Z0$]`#D`O?Z__AO]&OU=_&#\]OOR^]_\X_R)_X;__0+^`MT$W02L!*T$ +MK@.M`SH`.@##_<3]-?\R_U?_6_^8^Y7[ROS,_/0"\@(!!`,$QP#%`(4`AP`' +M`08!1_Y'_IG]F_VO_JO^B?Z-_O<`]`#0`]$#G@*@`N0!X`'$`L<"MO^U_Z3\ +MH_S-_L_^#P`-`"G]*OU9_%G\/0$^`04#`@,2`!8`5P!3`!8#&0,T`S,#/@(] +M`E4"5P+7_]7_@_R$_&C\:/SR_/'\^OK]^B;\(?S8`-T`D`.-`SL'/`=M"FX* +M3`5*!='^TOX"``(`WU!_L&^QG_&/_!`,,`SP/-`Z<%J062 +M!)$$A`2%!$<$1`1\`8`!,?XN_@7]"?V,_(?\@OV&_9C_E?^6_IG^2_Q*_.#^ +MWOZ=!*$$#@0)!$4`3`!"`CH"F`.?`Q4`#P#Q_O;^K/^H_[[\POQ\_'?\[0#R +M`*H"I0*%`8H!_P#\`&T`;0!O_W#_6@!9`*K_K/_#_,'\O?Z__C,$,00:!!P$ +M!`$#`:0!I`%-`4X!//TZ_>/ZYOHQ_2[]Q?[&_@#^`?XW`34!Z@7L!9H%F`6' +M`X@#I@*F`EP`70#W_O;^NOZY_AW[(/N/^8OY^_W__<$`O@"/_I#^/@`_`'(% +M<07U!/4$&@,9`\$$PP1/`TX#7/Y<_F?]:/T&_@3^(OLE^VKY9_D9_!S\!0$" +M`4T$3P2&!84%ZP3L!)(#D@.;`YL#'`(;`@W\#OQ]^'SX4_Q5_`'___YG_FG^ +M9@%C`:<$JP1E`F`"^0#^`!<$$P3.`M`""_T+_8O\B?S-_\__@0"``'X`?P#U +M__3_!_X'_@P`#0!L`VH#@0&%`:C^H_Z\_\+_1P%"`=3_V/]K_VC_J`"J`'G_ +M=_]+_D[^50)3`CX%/P64`9,!SO[._CL`/`"/`(X``_X$_M+\T?R+_8O]-?XW +M_BX!4@!2`)#\C_R2^I3ZG?R9 +M_`C^#_Z9_I'^;@)U`EH%5@4@`R$#H@*B`@P%#06-`HP"8/QA_#3[,_N'_HC^ +MP?[`_B;]*/T3_Q#_9`%G`58!5`$:`QL#W0/<`R$!(@$:`1L!7P)<`CP`0`"% +M_H#^8O]F_^?]YOU,_$O\7@!@`*$#H`,P`"X`Y_[J_L<"Q0(9`AL"I_ZE_EC_ +M6?^*`(H`M_^W_RT`+@#8`M0"<`-U`S`!+0&O_['_#_\.__O]^OWP_?+]R_W( +M_97\F?P$``$`D@64!0T%"P7U`?^C/^,?YA`6(!@0&!`6(#8@,C!2$%,P4W!4T%205A`V4#$_\.__O\`/UQ +M_6O]G/NC^SWY-_EJ^V_[40!-`/(!]0%C`V`#80=C!Y,'DP=K`VL#-`$T`5X` +M7@#:_-G\$?H1^L?[R?MU_G/^VO_<_W@!=0$P`C0"'P(:`E,$6`21`XT#@_Z& +M_IG^EOZW`;H!!P`$`)#]D_V`_W[_`0$``87_B/]\_WC_B@&.`4P!20'P__'_ +M6@%9`1T"(`+M`.H`+0`O`*K_J/_-_\__'`$;`=D!VP$=`!D`F?Z=_NK_Z?\9 +M`!D`3OU-_0W^#O[S`/(`FO^=_Y@`E`#S!?8%TP;0!L4"R0)<`5D!3`%.`5#^ +M3_X*^PK[X/G@^5[Z7OJ'_8C]:P)J`O8#]P.>`YT#E`65!;4%L@5M`G("F`"2 +M``$`!P`K_2;]M/JW^AG]&/TA`"``Z_[M_A/^$?Y(`DD"]`3U!-<#U`-:`U\# +M4P)-`B<`+`"+_X?_@_Z&_M_[WOL!_`#\A/Z$_O[___\"`@$"6`5:!2H%)P5Y +M`7L!B@&*`0L""0*=_:#]Y/KA^G']=/T+``D`;@!O`!T!&P&;`IT"?P-_`RD" +M)P)(`$P`Q?[`_A/^%OY)_TG_4/]-_U#_5?\P`BL"8@-F`[$`K0#R__;_$0(- +M`N,`YP`6_A+^8O]F_XP!B`&+_X__??YZ_A<`&`"@_Z#_#``-`$L"20(L`BX" +M20%)`14"$P(.`1$!C?Z+_FC^9_ZV_KK^7OU9_3C^//YB`V$#)04C!=4!V`&^ +M`;L!Q`/'`[``KP"\^[G[J/NN^Q#]"OU%_4K]U__5_XX#C`/S`_<#<@-N`VP$ +M;@1T`G4"A_^%_S'_,O\+_0S]W/K8^IG^G?X7`A8"F?^8_RO_+?]Z`W@#U`35 +M!.,!Y`'.`,T`B`&'`7W_@/___?O]W_[E_LO]QOW.^]#[FOV;_4X!2@&#`X@# +MUP33!#P%/P4=!!L$B0**`FD!:`%*_4O]?_A]^*'ZI/J&_H+^0?Y%_OK_]_^2 +M!)0$_03]!!(#$`,P`S$#9`)D`C/_,_]E_6?]EOZ4_IW_G/\]`#\`L`"M`(+_ +MB/_,_\7_80%G`3L`-@#Z_?W]?/]\_R0"(@+2`=4!F`"6`(8!A0'_`0("I?^B +M_Y#_D_]6`50!\?_Q_T_^3_[2_]/_=P!V`##_,O_2_M#^`O\#_R8`)0#7`M@" +M_@/]`YH"G`*]`KL"X@/C`QP`&P`E^R7["OL-^YW[F/OR^O?Z2/Y#_O@#_`,# +M!@`&H`6C!;]]/WS_1K_ +M'/^\`[H#J`6J!6$#7P/W`OH"M`.Q`V(!9`$#_P+_Y/_D_X[^COZ$^H3ZE?N5 +M^VS_:_\A`",`Z0#G`'4#=0-E!&<$Q03"!!\$(02``(``$?T/_>O\[_S7_=+] +M\/ST_!+^$/[Z`?H!M`*W`B4!(`&_`L0":`-D`PO_#__#_+_\UO_:_RP"*0(. +M`1$!70%:`=P"WP)Q`6P!_?\$`.G^X_Y*_$[\5?Q3_&C_9__<_^#_\?_K_R4# +M*P-0!$L$5`)7`FH":0*]`[T#70%>`4'^0/X7_Q?_B_^*___\`/TR_#/\//X[ +M_E'_4?\%``8`10)!`C4$/`3J!.,$P`3%!%T#6@,8`!D`OOV^_7?\=_Q`^D#Z +M!/L#^S8`.0#3`LX"*`(M`A,%$`5-!TX'A0.&`T?_1?\V_CC^5_U4_0+\!OSC +M_.#\QO[&_B0`)P"%`H`"X0/G`XP"AP+9`=L!:P)K`I,`DP!7_U?_M@"V`'8` +M=0"F_:C]7OU=_8D`B`"S`+4`M_ZU_B0!)0$.!`\$1P)$`@P-E`F@"M0&P`?\!!`(V`3,!6/]9 +M_Q3]%/W)^\G['OT?_2D`)P`,`@X"KP*M`H4$A@0=!AX&;0-K`X3_A?^N_J_^ +M&?T6_3SZ/_KE^N3ZP?[`_@P!#@'5`=,!5`14!`8&"0:4!)($FP*;`KP`O0!O +M_FW^=/YW_F'^7_Y6_%C\'?T:_<__T__J_^;_DO^5_Q8"%0(%!`0$K@*O`DD" +M2@)4`U,#-P$X`;7]M/W4_=3][_[P_D[^3?Z%_H?^9@!C`!L"'0*V`K4"A0*& +M`K4`LP#H_NS^HO^=_^W_\?]N_FO^W?_>_QL#'`/4`=,!)@`F`-4!U@$"`0`! +M_OP!_:_\K/RS_[7_90!D`'$`<0`T`C8"5@-3`S4#.`-L`VH#OP'``1K^&_XS +M_3#]JOVN_:/\G_ST_/C\G?^9_Z,`I@"N`*L`C@.2`SD&-`;#`\@#/`$X`3\# +M00.>`IT"3OU._13[%/ML^V[[\OKO^D'\0_SC_^+_4`)/`OL#_@-O!FT&;P9O +M!K8#M@/#`<,!3_]/_]KZW/JL^JGZU_W8_;_]P?UB_5[]1`%)`?D$]03J`^L# +MW`'>`48"0P+V`?@!X__B_UO_6_\__S__V/W9_3K^.?YU_W7_5O]6_^C_Y_\E +M`24`Y0"6`I4"!@()`B?_(O^E_ZO_&`(2`G,`=P!Y_GC^7@!=`"(! +M)`'&_\3_G?^?_[7_L_^,_HW^(?XB_A+_$/_B_^3_8@%A`3,#,@-?`V$#J`*H +M`D0#00/A`>4!!/T`_3O[/OO2_<_]2?Y._N/\W?Q*_T[_&P,:`WX#>P,L`S(# +MZ0/D`S("-`(:_QG_`/\"_Z_^J_ZN_+/\"OT&_63^9OX$_P3_9`%C`<0#Q0.O +M`JX"\`'R`>D#Y@.``X0#(?\=_\3\Q_RG_:3]C/R/_-#[SON]_K[^1@%&`80! +M@@&H`JP"O02X!!`$%01D`5\!>_^`_U[^6OZ%_8?]0_Y#_HC^AOX&_@C^_`#\ +M`/(#\`,4`A<",``M`"`!(0%V`'D`)OX@_HC^CO[T__#_2_],_S;_.?]``3L! +MS`'0`10!$0%I`6P!7`%8`?@`_0#<`-@`=_]X_X;]B/T=_AK^U/_6_V'_8?\+ +M_PG_@0&%`24#(0-1`50!W`#:``8"!0*R_[;_`_W__%[^8?XX`#8`H/^?_T(` +M1@`,`@@"?@*!`DL"20(I`2H!?/Y[_FG];/U1_TW_#/\/_V']8/W0_\__Y`+G +M`CX".@+&`,`X`#Q_O/^ +M/_\^_^K_ZO_^_O[^>_]\_Y4`DP`(``H`(``>`%L!70$,``L`@/V`_1W_'?_1 +M`=$!"P$,`<4`PP"6`Y@#=01T!*X!K@%>_U__^_WZ_?S[_/OR^O/Z8/Q?_"+^ +M(_[[__K_]`+U`G4$=`2+!(L$1@5&!=8#UP/1_\__9/YG_MC^U/Z,_(_\G?J: +M^FS\;_SK_NC^5_]:_^D`Y0"2`Y4#``3^`]0#U0,"!`($9`)B`J[_L?^N_:O] +MO?N_^S7[-/L+_0S]UO[4_H7_A_^T`;(!HP6E!84%A`76`=8!Y0#E`,\`SP"L +M_:S]V/O8^[#]L/VC_J3^O?Z[_L(`Q`":`I@"J@&L`8<`A0!.`%``:O]H_Q8` +M&`"A`:`!90!D`$G_2_]^`7L!]`'W`=;^U/[F_>C]D?^._W;_>O^4_H_^^O_^ +M_^8`Y``Z_SO_BO^*_^8!Y@'B`>`!EP";`#D!,P&H`:X!!`$``78`>`"._H[^ +M-?PT_-?\U_SK_NO^IOZG_DG_2/]J`VP#,P4Q!4\#4`/3`M("'P(A`N#]W_TD +M^R3[(?PA_./\X_Q)_4C]3_]3_TL"1@+=`^`#C`2*!,(#PP/]`/T`G/^=__?_ +M]O\S_C/^"_P*_+W]O_V1_X[_Y/[H_F'_7?^8`9H!?P&``8(`?@"H`JT"G@.9 +M`T<`2P!W_G7^'_\@_S'^+_YE_6C]K_ZM_H[_C_\+`0L!N@.X`[D#O0/F`.(` +MT?_3__'_\?]/_4[]O?N_^Q/^$?YO_V__'/\=_P<"!P(Y!3H%7@-;`V``8P`T +M`#$`Z?_K_P/^!/ZJ_:?]F?Z<_N?^Y/Z:`)P`7P)>`L0`Q0`R_S/_F0"6`&(` +M9@#%_L#^#P`4``$!_@`;_Q[_1?]"_Q@"&0)'`4_G3]<_WX_/?\C?N1^T'\._Q._U/_I`"A`'L!?`%Q`W(#+P,L`ZX! +ML@%4`E$"S0+/`M7_U/\C_2+]"/X)_B/^(_X>_![\GOR>_&__;__2`-$`2P), +M`EH$6@0O!"X$G`*>`DP!2@$V_S?_S_S._&W\;_S$_,+\Q?S&_)G_F/]D`V8# +MY`+B`LP`S@#H`>8!_P$``C+_,?]"_D3^;_]L_^;_ZO]$`$$`*0$J`7\`?@#N +M_N_^T?[1_N/^XO[\_?W]H?Z@_D8`1P`7`!<`-P$U`0P$#@17`U8#3P!0`',` +M<@"'`8@![_[N_N+\XOQ*_DS^.OXV_GK\?_PE_B'^00!#`)#_C__)`,D`U`/5 +M`Y@$E00(!`P$4@-.`TD`30#?_-S\B/R*_"?\)/S@^>/Y6/M6^Q$!%`%_`WL# +M/0-"`RL%)057]DOZ6_OH`]0"] +M`L$"D0&-`<(!Q@$/`PX#1P%%`=_^X?Y+_TK_!?\%_\C\R?P9_1?];_]Q_]O^ +MV?X-_@_^H0&>`48$203W`O4".0(Y`@8""0(!`/S_0OY&_J3]H_T-_`S\OON_ +M^\S^S?XE`2$!+@$T`?\"^0*(!(X$N0&S`7S_@?\4`!``+_XR_D3[0_O<_-W\ +M-P`U`,4`QP`0`0X!D0*4`G`";0+Z`/P`Y@#E`"0`)0#X_??]%_X8_OK^^?X: +M_AK^@OZ$_G<`=``O`#(`N?^V_TP"3P)3`T\#@P"'`*/_H?]I`6@!S?_1__S\ +M]?P9_B'^:O]C_^?^[/XT`#(`#P(.`IT!H`$``?T`S0#/`#S_.O_P_?+]P_[" +M_AC_&/]G_FC^A0"$``@#"`.&`8?W=_M_^ +MY_[E_F3_9O]X`7/YT_W?_,O\O_U\!80'W`O<"T`#/`&``80"( +M`H4">`%]`7O^=_[^_@'_1P!%`#G^.?X1_A/^$`$.`;T`O@"6_I;^H_^B_W4` +M=P#V_O/^,_\V_[0`L0#*`,P`J`&G`8<#AP-&`D8"L/^P_T``0`"\_[O_+/PO +M_-C[T_M&_DO^-?XQ_H;^B/X2`A$"T0/2`YX"G@+?`MT"I`.G`QH!%@$@_B3^ +MX?W?_1;]%_T'_`;\1_U(_7O^>?[^_@'_&`(5`L4$R025`Y`#D`*5`F,#7P-/ +M`5$!)OTF_9/\DOP3_17]`_L!^Z[[K_OX__C_R@'*`=8!U@'/`\\#N02X!"T# +M+P/5`=,!YO_H__G\^?QR_'#\6?Y;_B+^(/Y<_5_]U/_1_WH!?0%``#T`S`#/ +M`'D"=@*L`*X`)O\E_P8!!P&"`8`!%/\7_ZO^J?X^`#T`Y/_F_Z3_H?^+`)`` +MDO^-_WW^@?X*``4`]?_Z_Q;^$_Y9_UO_'@$<`20`)0"-`(T`)P,G`T$"0@+@ +M_MW^+/\O_PX`"P!E_6C]/_P^_*C^I_[6_];_9`!D`(L"C`(+`PH#I`&F`7@! +M=0$&`0@!!_X'_H3\@OSY_?W]B_V&_H"Z0+:`=H!_Q]_&+\7_PC`"4`]@'U`=X`WP`_ +M`C\"_@/^`XX!C0$)_PO_\?[N_G']=?U1_$W\)OXJ_FK_9_\A_R+_SP#/`)4" +ME@)!`3\!B`"*`)8!DP'+_\__A?V!_57_6?]%`$``S?W2_3#^+?[5`-4`@P"% +M`(3_@/\>`2,!E@&3`9C_FO^E_Z/_EP"8`+_^O_Z;_9K]^O[]_MC^U?[9_MO^ +MI`&A`2@"+`+Y__3_:P!Q`-0!S@'3_MC^;/QH_/K^_?Y,`$H`\?[Q_N4`YP!4 +M`U(#*P$K`4O_3/_;_]K_2OY,_C;\,_P\_3_]@_Y^_GS^@O[A`-T`N0.[`\\" +MSP+E`>,!+0,P`XD!A@&O_;/]M/VP_3W^/_Z^^[[[=?MT^\7^R/[&_\+_,/\R +M_\H!R`$W!#H$/@,\`ZD"J@*I`J<"V/_:_UC]5_TK_2K]M?NX^^[ZZ_K:_=W] +M8P!?`'T`@0"N`JD"?`6"!;(#K0-4`%<`\?_P_^?^YO[.^\_[VOO9^R_^,?ZM +M_JO^@O^#_Z(!H0':`=L!W`#:`#P!/P$/`0P!A/^&_^W_[/_X`/<`//\__S;^ +M,O[]_P$`=/]P_WK]?OWR_N_^5P!9`-;^U/X,_PW_D`&1`5$!3P')_\K_L`"P +M`%0!4P$J`"P`=`!R`/T`_0!'_TG_N?ZV_FC_:O^Q_;#]&OP:_.W][OV3_Y/_ +M?O]]_^,!XP$`%L`'/\A_]C^TOXI_"[\I_NE^US^ +M7?XT_C'^\/ST_/S_]_\I`RX#:`)E`E0"50+V`_4#F`*;`J7_G_\?_R;_9?Y? +M_O+[]_ME_&'\F/Z:_M?^UOXX`#H`#`,*`W,"=`*0`(\`B0&*`1(!$@%H_6C] +M%/T3_1L`'0```/W_%_\;_\8!P@&7`IH"`P`!`'S_??_G_^C_*/XE_J/]I_VQ +M_ZW_,0`T`!``$`#``;T!.P)``B``&@#8_]W_CP",`$3^1_Y&_4/]X__E_VL` +M:0":_IS^YO_E_Q$"$@+G`.8`,P`T`/L!^@&G`:_)K\RO_-_^\![0&3`98!O@*Z`JD#K`,<`1L!N_ZZ_J?^JO[` +M_;S]"OT-_1[_'?]$`$,``?\#_YX`G`!-`TX#J0&I`00``P"2`94!#`$(`3G^ +M//Z0_HW^;?]P_U3]4_W2_-'\J?^J_]X`W`"/`)$`9`)C`C4#-0.(`8D!_0#[ +M`%P`7@!)_4;]!?P)_#K^-_[\_O[^I_ZG_BP!*0%>`V$#L0&P`:(`H@"G`:@! +MS__._SG]./U8_EK^A_^%_Z'^H_Z3_Y+_8P%C`;\`OP`6`!<`MP"U`);_F/\< +M_AK^BO^+_V0`90!,_TK_A@")`%X"6@+(`,H``ET"!0,$`_0!]0%5`E4"^`'W +M`3?^.O[E^^+[N/RZ_$O\2?RW^[C[!O\&_TX"3P(Q`B\"7`->`V@%9@4+`PP# +M+?\M_VS^;/Z._8[]OON^^W[\??P@_B'^>_Y[_D@`2`!&`T8#*@,J`[L!N@&* +M`HP"^P'Y`=7^U_XV_C;^`/_^_L+\Q?RH^Z3[H_ZI_AD`$@#T_OK^;@!K`"@# +M*`.L`J\"[@'I`6<":P)L`&H`"?X+_L3^POZF_J?^&OT9_9S^G?ZP`*\`P!L`&@`IP*N`O,!Z@&G_[#_ +MR?_"_P?_"_]Z_'C\7/Q>_%G^5?Y\_H+^6?]2_T4"2P(!`_T"TP'5`10#$P/+ +M`LP"0O]!_^;]YOT/_Q'_Y/SA_"O[+ON*_8G]XO_@_X__DO]V`7,!J0.L`W,# +M<0-_`H`"R`/'`R#^(?Z>]9WU@O^#_R/^(OZD`J0":`)I`BST*_1[`WL#2`%* +M`;+[KOL%!PD'[07L!?<$]01>_&+\R?[$_E$%505&^47YD/B.^-#\T_SG`^4# +MNP*[`AS]'OT/`PH#L?^W__,'[P<_!$$$P/:_]D;\1_RN_*S\%008!%K^5_[A +M_^3_ZP3H!$O\3?Q4`50!R?O'^S$"-0(2_0S]ROK0^M@%U`4*`@T"(@D?"9CX +MF_CZ^O;ZYOWK_2D`)0"_#<(-]/'Q\7C]>OTK`BL"`P,!`VH(;@B6\Y#S,0,W +M`PL`!P#T"/8(Z`+G`D/P0_".!(X$H/^@_P\(#PA!^D+Z4O51]:("H@(F^B?Z +M-PTU#4W^4?Y&_4#]:P5R!1L!%0'3!]<',O,P\[_[O_N'_(C\(P$A`4@)2@G4 +M^]+[/P-!`YSXFOAI!6L%N@BX",OZS/IW_'?\PBJ_J;^7P-C`]SWV/>["[X+F@68!8+\@_P;`!H`X_#D\%P#6P/R_O3^ +MA06#!<8`Q@"V];;U3@M0"R'^'OZ7!9H%OOZ[_C;W-_>_`<$!__K\^I8)F`F; +M^IOZ)/PA_`X%$P5$`C\",0DU">;RX_*G^:GYYOWD_8$$@@3,"LT*RO;(]CG_ +M._^;`)D`@@F#"7,!=`%N]&WTF@":`'#_;_:']C?^& +M_R$.*`YM^F;Z>_Z`_D<"10(K`BL"6`=9!W'Z;_I4`E<";_5K];C]O?U<`U<# +M./<]]UH#5@,_^T#[Q`K&"H$'?P=2`50!Y0/D`]3OTN]&`TD#,0$P`6;_9?]Q +M_'/\LO*P\L8&R`9N`FT"M`BR"+KZOOK0]\WWLPZU#NL$Z00%!`8$#_`.\%CX +M6_@2"@X*@P"&`!,`$0`?\"'P&?\6_W,(=PB^!KH&\P+W`KKTM_0I!RP'GP6< +M!0\!$0%?]U[W_/3\]`<("`@=`1P!Y0;E!E7\5?P;^!OX/P)``KS_N?^S!+8$ +M2/=']V8!90'P`?,!B_^'_X<)BPE9^U;[I`*F`EK\6/P!`@("&0,;`X+S?_/O +M_O'^EO:4]ML%W`5\"GT*L_^R_[+_LO^=^)[X\0WO#?X!`0)U^7+Y:OMM^S;W +M,_\K$`LP!.!4X%\P#S`)T#G0,'^P;[,`PR#&@%904`_P3_ +M=_MR^R'T)O21_XS_SO[3_FH'9P>,^HWZ'OH>^O<&]P85`14!P`?`!W#Y#\WOR*!(L$V@C;"%L#6@.E]*3T=P)Z`EW] +M6/VG!ZX'"@`#`/[S`_24`I$"P?[#_KH)N0DW_#C\./DW^80$A`1'`$D`O0JZ +M"D/Y1OF+^(GX5OI6^G?]>OW\"?@)O?G!^0D"!0*>_Z+_^P/W`U(*5PIU^W'[ +M`O\$_W;V=O8U`C$"4`17!(;X?_CD^NKZR/G#^3P+/PMW!G8&9@)D`L[\T?P5 +M]Q/W80EB"80`A0`;_!C\#?40]2?])?WH"ND*2/Y(_B(!(@$J^BOZG`2:!/H) +M^PEI`6D!+/\K_POQ#/'U`/4`Z/WG_=O[W/M,`TL#>/IW^B@'*P?P`>P!>0=\ +M!SP".P*L^*SXE@27!&7Z8OK]`0$")OHB^K[WPOR]V?\9_RR +M_K#^P0_$#]H(V`A3]%+TEOF9^4/Z0/H;"1P).?\[_Z?VHO;+^]#[D@&0`68. +M9`[U_/G\3?I)^J_\LOS9`]D#T@C/"#/Y-OGD_.#\>O:`]K,#K0.9"IT*S?S, +M_&X`:P"3^ICZB@2&!!W^'OZ?^Z#[3P!/`!#V#O9_!8(%S0+*`J`$H@3,_LS^ +M=_EU^1X*(0J%`($`#`41!8[XB?BW\;OQ^P#X`-[_W_](!DH&2_=']X+^A_[W +M"?,)C@60!7\&?P;<]-OTZO[K_E("4@)^`7T!0/Y"_J+MG^W__0+^C@.,`_,' +M\P<#`P4#4?Q._+\&P0:/`8\!XP;A!I3[F/L+]@7VJORQ_+S]M?TH!2X%)_8B +M]B_\,_SR`>X!=`-Y`VH,9`PI`"X`-/\Q_X_XD/@7`A<"C`2,!._W\/>E_:+] +M#O@2^',&;P8,!A`&0/T]_5C]6?V0^(_X^`S[##8#,@-_^8+Y=?ER^4'Y0_E7 +M"E<*C@&.`:^./X7@58!4D"3P(\_C?^F_Z?_D;U0O4X`CT"&`,2`WH$@`1A_US_)OPI +M_,<'QP>1`(X`\@+V`C;Z,OI4^E?ZMP2V!%,"4@*B!J0&+_8M]I;ZE_I>!%X$ +MY0'D`:P#K0,E]R;W+P`M`+$#LP-Y!78%_`3^!++XLOAW`7D%5@Q4#+K^O?[6_M/^&/P: +M_%@'5P?[!?L%6OU<_9'XCO@J]2OU#`<-!U0"4P++_,S\0_M#^TH`2`#I#NP. +MTP'0`4C^2_[S^/#X.OX]_M`)S@EY_GK^N?VX_8KXBO@$`@,"]03X!$O\2?QQ +M`'$`"_L,^\@%Q`4;!2$%D`"+`'0`>``N^2OY!04'!<<`Q0#Z_OW^9/]@_[3Z +MN/K)_\?_0?Y"_H,'@@<#`00!5?M3^R4!*0&1`8T!<0=S!Q3]%/VG_*7\1/]& +M_U(#4@,*"`D(W?;=]N/UY?4I_";\5P=9!ZT&K08]^SK[KP"T``H!!@&."I`* +M#`0,!%;X4_BE^ZK[DOV,_4X(50C5^\_[BO>.]R7_(O_4`=8!R@G)"?[__O^U +M_[?_T_O/^X'_A?\^"#P(4/Q0_`+_`_]C_&/\S0'+`6\#P!PP""P(S_C7^%_<5]\8"QP+Z`?L!G@&<`?#]\OV9^)?X4@54 +M!4\$3@05`Q8#0/H^^F+[9/L&!P4'9@)F`B$!(@$'^07Y>_U\_2T%+`52!E0& +MQP+$`G?U>_6&_H'^<@)W`K\!(@_5_]X/W@_8$"@`+N!.X$N?J\^@#]^OQI_'#\.0DR +M"3,'.`=!_$#\T/_._UG\7/P'`P,#JOZN_B+\'OQP_77]T/S,_%\(80@2`Q$# +M0/]`_RW]+OW&`,4`QP;(!B[_+/^W_[K_./@T^%_[9/MA`UP#M@&Z`4<"1`+$ +M^\;[N@.Y`X\%D`7#`L("'P`@`+OXNOB7`)@`T@'1`:T#K@.U^K7ZI?6D]3X# +M0`.'!(0$,@@T"'7^=?X<^QK[SP+2`B,#(@,8!18%;?9P]H7Z@OH0!1,%/`4Z +M!!5L%FOV>_:#^G?ZE +M`*4`1@%(`1P(&0B,`H\"5@%4`9'ZD?H-_1#])P$C`;3[N/L._0K]`_L&^Q8% +M%`45!Q8')P0G!!,!$P$T^C3Z0@9"!H\%C066_IK^.O0):`5`+G^P?[+_\/_D?R8_.0(W@C-!M(&;@)J`L/^Q_Y%^4+Y;0%N`>D` +MZ@`I`28!F/F<^>OYYOE1!%<$"@($`L<#S`.%_H'^9P%H`6,%9`7O`>\!2@%( +M`2?X*OAL_&G\<`!S`.P`Z0";`)T`T/S._(`!@P&?`)P`"08+!OP#^P-A_&#\ +M_?T`_I?\D_RY`;T!%_X4_D7^1_Z(`88!]`#U`%$(40@5`A4"S?S-_+;XM_@K +M_BC^609`ZS^K?YJ\VCS1OE(^9K]F?WO`O`"00%``=;^UO[M!.T$ +MD@21!"0(*`AG`&(`N/J\^A+^#OZZ_KS^30%.`5[Y7?FG^JCZ8O]@_[<"N0(_ +M!ST'L_ZU_HO]BOUH`&D`^@3Z!*0#H@,E_"?\;/YK_K?]M_W*_\S_X_W?_>;[ +MZOOL_NG^80!C``$(``AM!&T$!/\%_Z/]H?U8_5O]SP#,`!G^&_YY_W?_3?U0 +M_3S_./\I!"T$P`&\`3$!-0%D^V'[RO[+_E8!5@%G_V;_70!?``S["OMX`7@! +MJP:M!A$&#@:R_[3_SO?.]X#^??YH_V[_MP"Q`+O]O?V5^I?Z20)&`B$$)`1Q +M!6X%4_Q4_`#[`?M7!58%W@3?!/@!^`$$^@'Z!OL+^P#_^_X\`$``6@%8`4/[ +M0_N%_X7_E@.6`XP%C`6``X$#:?UG_:,`I0"$_X+_L_^U_T3\0_Q5^%7XQ_S( +M_)(`D@!5!E,&Q0/'`SD".`*U`+4`@/Z"_NT!Z0%._E+^+?TJ_8K[C?L.``H` +M'P4D!3S^-_Z@_:7]OOVY_4`"1`(E!",$U@#6`-C^V/Y'^DCZ80)?`N,#Y@/H +M_N7^\?SS_#7\-/PV!#4$A0&'`9+_D?_Q_O+^F/Z7_FH%:@64`94!I?VB_>WX +M\OC2^\S[4@-7`\$!OP'3`M("P_[%_N8`Y`"[`[P#%``5`)+^C_Y8^EWZ^__U +M_S`"-@),`$8`3P!6`.O]X_W*`-,`40%(`40#2@.[_KG^[OGM^<`Y0!@`F,"Y/SB_*/\H_P0_Q+_?@)[`M$"TP(Y +M_#G\SOW+_7O]?_U)`D8"S03/!/?_]?]<`%\`\O[O_E<#60,C`"$`,/LQ^PG] +M"OV8_)?\T@+3`D4"1`(M`"P`;?YP_A0!$0$""00)H@&B`?K\^/RQ^K3ZS?O+ +M^\$`P@!S_G+^LO^R_[C]N?T_`CX"0@=#!\X!S0&#_87]T_O/^Y8#G`.6!(\$ +M>_^`_W#\_$@!1@%Z_G_^N/JR^HO\DORH`:(!*P8O!C8"-`(V`3`(6`!@`\?ON^[(!M0$1`@X"A`*'`IW^F_ZB^:3Y_OW[_>$`XP`-!`X$ +M:OYG_NW\\?PD`A\"]@'[`70"<`+@_>7]5O]1_Y,!E@':`M@"%@09!!#[#?M. +M^E+Z]O[P_D("2`)W`',`TOO5^X?_AO]W`78!?01^!'H#>@."_X+_S?_,_S\` +M0@#W`?,!$/P3_-WYV_F[_;S]R`#'`(T$CP3\`?D!!P`*`+;^M/Y0`5`!9@-G +M`WW_>__C_N;^?_U]_<3_Q/^W_[C_(?X?_NS^[OXI_2?]+@,P`\($P01L`6P! +MZ?[J_LW]ROVQ`;8!L?^K_V4`:@"C_J#^C/N-^SX`/@"W`;@!&`,5`]+^UOX- +M_PG_50-7`W\`@`"<_YG_YOKJ^B?\(_P!`@0",`0N!`T##0/4_-;\X_[@_BH! +M+0&T`;(!OP"_`!#\$?R._HS^1P%)`3P#.P,(_PG_H/J@^FT`:@#*`\\#IP*B +M`F']9OVS_+#\+^YOX]_C?^*_TQ_8;\@ORL`:X!Z/_I_R0`(0!&`$D`=@%S`18$ +M&`3C`>,!20!)`-'YS_F9^YS[K0&J`9``D@`=_QW_O?R\_!(!$P'B`N("E`&2 +M`2`!)`&__KO^O0+!`LP#R`,X`#H`9OIG^J[XK/AE_F;^#P`0`+4`L@`4_QC_ +M@`!]`"`$(`2R`[,#<@)S`H;]@_W?_>+]"@`'`$3_1_^(_8;]\/OQ^^[_[/]R +M`78!I@.A`[P!P@%>_%?\&/X?_HO_A?]M`7(!DO^._QP`(`!2`DT"3O]3_R,! +M(`$9_QK_I/RE_`C]!?W4_]C_=P-S`[/^M_YD_E_^^/[\_G@`=@#Q`_$#Y0#F +M`,[_S/\#_@7^?@!\``0"!@+`_;[]Q_S)_.;[Y?NC_Z/_N@"[`'S^>_Y?_E_^ +M;P!P`*@'IP>F!J8&3@%/`4C]1_U7^U?[(?TB_?7[\_M@_&+\>/QW_!,`%`"@ +M!9X%601G_Y_\0_Q'_D`"/`)_\H/S2_=+] +MZ?_H_TP"3@(7`A0"U?[9_KO_M__^_0'^]/_T_S(`,`"._)'\)_PD_'+^<_[D +M!.8$EP24!%X!8`$C`"0`Q?_!_P4!"P$O_BG^(_PF_$7[1?N\_[O_0@1#!!T` +M'0`A_2#]*/TI_;D!N`$Q!#,$=0)R`B@`*@`,_@S^M0&T`2T`+P"@^YW[D_J5 +M^F3]8_U#`D,"K@"O`/`!\`$H`B<":`%H`4,$1`3W`?8!.OX[_D'Y0/F^^K[Z +M+_XQ_LK]Q_VF`:D!T0+-`A\$(P3<`MH"%@$6`64`9@!.^T[[./TV_8[_D/]C +M`&(`?P"``,[]SOWB_N'^GO^>_[,"M`)!`D`"K?ZO_G;_P#<`]X#3`%,`3D`-P`R_C3^0_Y!_DC_2/_J +M_>W]X/_=_[?^N?[L`.H`#P,0`Y``CP#-_L_^/_T^_;P`NP"0`9(!5P%5`0P! +M#0$._0_]5OU3_5#^5/XE`",`5?]4_VC_;/^K!*0$3P16!/D!]0'!_L+^3/Q- +M_"3\(_Q>_5W]_?X`_S#\+/QI_6S]HP*B`G\&?P8P!C`&H@&B`6(`8@`G_R?_ +MCOZ/_B[\+/Q*^4WYEOR1_.,`ZP!S`VD#<`!Z`.S^Y/Y1`%4`?@%^`4@$1P1. +M`4T!L_ZV_I_^G/[E_^;__?\``,WZQ_J`_(;\!`#__WD!?0%*`D<"`0$$`9X! +MF@$7`!L`K@&J`;X`P0!X_'?\%OT6_2+](OU3_U+_7`!>`/H!^`%:`UL#Y__H +M_][_V__V_OG^H/V@_5']3?V#_HG^.`(S`AL#'0-F`V<#P/^]_RW],/UL_VG_ +MQ?_*_R7^'OY=^V3[T_W-_=8`VP!F`6(!!`((`GL`=P`1`10!\`+O`H<#A0.7 +M_IO^??IZ^D3^1?Z%`(4`R/_&_U+\5OPD_!_\9/]I_TD!10'H`^L#-0(S`A$" +M$0*G`Z<#=@)X`H?^A?[$^,7X%?H5^GC\=_P$_P7_DP&3`5P`6P!G`F@"D0.2 +M`QT$&@3#`<8!2?Y&_N[^\/[$_,3\(/T@_5_^7O[,_`!L`X/_@_YP!GP&K`:8!8@!I`"_]*/T(_0W]2P!(`&S_;?]+ +M_DW^6?]5_U0"6@(:`A("EO^=_RG^)?Z<_)_\1/]!_V0!9@$6`10!-O\W_SK_ +M._]P`FX"S0'0`5X`6P`&_@?^Q?S&_#G^-_[I_NS^1/]!_Y3]EOUY`7D!(04? +M!=,"U@+R`.\`=/UV_0;]!_VC_9_](O\H_^S_Y?]&_4S]_/_Z__L!^@%W`7H! +M00`]`!L`'@`M`BP"E`"4`*0`I0"E_J3^7/M=^^C\YOR]_K_^GP">`([_C__, +M_\K__J3]I_T2_`[\0_Y%_M+_ +MU/]9`E0"H02G!,T"R`+)_LW^E_R3_.+_Y?\[`#@`O?W!_5W]6OTL_RW_F@&: +M`>$`X`!!`4(!DO^3_ZO_J/^(`XL#J`&G`4K]2?TU^S?[2/]&_[$!L0%J_VS_ +M$?\/_^[]\/TL`"H`50)6`JX!K0$D`"<`0?\]_T4"20(Q`"T`!OT*_?3\\/Q# +M_47]*`$H`:8"I0(*`@T"R?_#_T#_1O_"`+X`H/ZB_I?^F/YN_VK_*@`N`,S_ +MRO^R_[3_W@'<`?;_]_\J_RG_G@"?`(,`@P`1_Q+_EOV3_7+^=/ZK_JS^7P%< +M`=(#U@.Z`;8!*/\J_U3_4__E`>8!S__._[O]O?U9_E?^C/Z-_A4`%``+_PS_ +M8?UA_;W]O/V&`H<""P8+!L`"OP+"`,0`V__8_^O_[?\'_P;_[OSO_)O\F_P* +M_`G\6P!<`$,"0`)Q_W7_<0!O`"8#)0.6!)D$/P$Z`1W_(__V_?']'/P?_*K^ +MJ/[)_\O_G?^=_^S^ZOX9`!P`1@)$`GL`>P"Z`+P`)P$E`2,!)`&2`9(!10!% +M`&;^9?XF_"C\?_U]_:__K_^I`*P`&P$6`=C_W?\A`!T`UP#:`$$"0`+)`,@` +M2_Y,_DH`20!W`7D!6@!8`*_^K_Y%_T?_K_^K_^3_Z?]+`4/PV_CC^1O]"_Z__M/^$_W__$@$6`60!8P$I`R@#U035!)P!G@%U_G'^ +M)?TK_+]&@`9`!L! +M&@%)`4L!$0`.`&H`;0#'`,8``0(``N/_Y/]D^V3[4?Y0_CD".P("`P`#=@!W +M`&?^9OY%_T?_[/[J_F@`:0#\__W__/[Y_H8!B0'B`^$#L`*P`G7]=?U1_5'] +MJ?^I_SO_._^R_K+^]/WT_9#_C_\C`24![P+M`L,#Q0-P`6X!P@#$`/K_^?]` +M_D#^O/R^_+O]N/UU_WC_\__Q_S8"-@*C`:4!IO^C_TK_3/^Y_[G_P@#!`",! +M(P$-`@X"PO_`_^;]Z?V^_[O_+0`N`-G^VOX,_@O^6`%9`7`";@(U`3_\S_R_\& +M``D`[`#G`$#_1O]<_U?_"0$,`7H!>0'U__3_#/\._YL`F0":_YW_0_\__\[_ +MTO_Q_NW^&/\;_Q\!'@$_`T`#V`#7`#C^./Z/_H[^2/]*_Q,`$@#``,$`LP&Q +M`1$`$@`%``8`E`"2`%;]6?V0_(W\8?YC_KX!O0%V`W<#]@+T`M@!V@&;_YK_ +MC@".``8`!@!!_4']_/O\^ZS\K/Q\_WO_&P`<`.L!ZP&"`H$"2@%,`4`#/0.: +M`IP"`O\!__C[^?O`_;[]Z`#K`//_\?]>_UW_A?Z(_F?^8_Z1_Y7_&P$9`9\! +MGP$P`#``GP*?`NX#[P.7`)4`6OU=_3C\,_Q2_5C]-_XT_GG_>?^5_Y?_XO_> +M_S\"1`)E`V$#F0*=`E(`3@"Q_[7_ZO[E_D3]2OV<_I;^./\^_[S_MO_/`-0` +MVP'7`3T`/P"<_9S](_XC_OC]^/UC_V+_M`*U`K$#L0.'`8?QY_,7\Q/Q:_5O]@'-`_*W_K?^>`I\" +M%0,4`XX"C@+X`?@!7?]=_]S^W?[W_O7^[OWO_=C]U_W"_\7_\@#M`(+_A_^; +M_Y;_B/^-_P$`_/]2`E<"_P+Y`C4!.P'V_O'^H/^D_YK_F/_T_?3]0OY"_C?^ +M./Z'_H;^/P!``*T"JP+3`=4!F`"7``4#!@/R`?$!A/Z$_AW\'/S0^]/[GOV; +M_7C_>O_P`NX"9P-I`_@!]@'[`?T!;@%M`;__O_\)_0K]FOV9_8S^B_[H_NS^ +M(@`>``;_"?]O_VW_6P%;`2@#*@-I`F@"=?]T_V[_;O_8_]K_/@`[``L`#@"+ +M_HG^\/WP_4O_3/\[`3D!)_\I_Z_^K?X``@,"8P-?`T4"20*T_[#_Z_WN_;+\ +ML/SF_>C]AP"%`"D`*@!A`&$`;@)L`E8#6P-*`$0`;_YT_IO_EO^2_I?^U_[3 +M_K'_MO_._\?_`0`)`.$`V0`^`D0"3`%*`<8`Q0"`_X/_`?W\_#S]0?T\_SG_ +M(0$A`3P`/P`!`?P`T`+6`KD!M`'_``$!8/]@_['^L/X-``X`*0$J`;K_N/^5 +M^YC[0/P[_`\`$P"+`8D!G`"?`"@!)0'9`]P#D@.-`Y<"G`(X`#4`J_RM_"W] +M+?W&_L7^S_W/_9_[H?OA_=_]C@*0`E,$4`1?!&,$"P,'`VT!<0':_];_\/[S +M_FK]:/W@^^'[9_YF_O/_\__+_\W_@P"``+@`NP"@`)T`%P$9`2P#+`-Z`G@" +M&0`<`#S_.?^S_K;^L_ZP_I'^E/[>_]S_3/]._XG^AOZ0`90!\P'N`=S_X?\_ +M_SS_&@$9`=_]__A?Z%_J``GP!``D("#0`*`#+]-OU._DK^F@&=`3P!.@'9`-H` +M$@$2`7X`?0"E`*<`XO_@_ZW]K_VP_*[\^__]_WT"?`)2`5(!1@!'`$@`1P"G +M`:_Y[_K?RM_![]'_W8_M?^]`#U`/(![P%.`5(! +MV0#7`&8!90&G`ZD#Y@'D`;+]L_T"_0/]!?X#_B/^)/X5_A7^JO^J_PX!#@%( +M`T<#O`2^!#$!+P$V_C?^"_\,_\D`QP"N_[#__?W[_2G_*?^@_Z+_G?^<_WD` +M>0`J`2H!X0#@`((!A`&N`JT"4P!3`'7^=/[-_M#^!?\`_RO_,O]8_U+_1@!( +M`)[_G_^*_XC_P`'"`90"DP+B`>(!QP#'``L`"P"Q_K+^GOZ;_CK_/O]A_E[^ +M90!F`,`"P0(B`B`"S?_._Q#^$?Z8_I?^`_\#_S0`-0`:`1@!0`!#`-0`T0"K +M`JT"M0*T`JW_KO]*_TG_G`">`"7_(O\-_A#^SOW-_2S^*OY;_U__L@&O`18# +M%@,+`0X!/@`[`)P!G`'H`>L!UP#1`$(`20"E_Z#_Y?WG_37^-?[I_>C]]?SV +M_!7_%O^*`H8"FP2@!``$_`.@`J,"XP#B`#3_,_\&_P;_%?X7_JG\IORF_*K\ +MB?^$_R8!*0&B`*(`<0)P`DD#2P-Q`FT"B@&.`4``/@"?_J'^H_VA_;3_M/_> +M`.``S/_)_TS_3_]H_V?_0O]!_S3_-?]Q`7$!,0(O`AD!'`$!`O\!F@&9`=;_ +MVO]U_G'^0?Y#_G;_=?_7`-@`J0&H`>/_Y/^[_KK^0`!!`*L!J@%B`&,`I/ZC +M_@`````#`00!8`%=`?4!^0'``+P`&P`@`$4!/P'5`-D`T/S._-[ZWOH:_AS^ +M>P%Y`9@"F0+4`M,"G@.>`Z\"L0(M`2D!9`!J`,?]OOT9_2/](O\:_^7_Z?_G +M_N;^C_Z/_E$`4``E`2@!K`&G`2\"-0(;`A8"B@&-`9X`G`#!`,0`\__P_W3_ +M=_]F_V/_1?U&_57]5_V:_Y?_R0#,`/L`^0"(`HD"7`1;!*("HP+Z__C_V?W= +M_='\S?PF_BG^NP"W`,T!T0'Q_^[_R@#,``P##`,_`3T!T_[6_M;^TOX>`"(` +MHP"@`'X`@0"2_Y#_)O\F_RL!*P%A`F$"_P`!`9+^C_YW_GK^(``<`#<`/`#@ +M`-P`?0%_`5X!7@%Z`7@!U`#7`-K^V?ZV_;3][/_P_Z8!H0%Y`7T!.@$X`54` +M5@#\_OO^Y_WH_8#_?O_C`.8`+``H`((!(P(G`G\` +M>P`$``<`T`#.`'[_?_\R_3+]HORB_);_E?\V`C@"``/^`HX#D`-1`DX">0!\ +M`*[_J__S_O;^COV,_4+^0_XB`2$!B@&+`00!!`%R`'$`F?^9_[C_N?^``'\` +M0P%$`3@`.``O`"T`I@&I`4,!0`'<`-\`0P%!`7$`<@`O_B[^5?Y6_I/_DO_# +M_L3^;_]O_VP!:P&*`HL""P()`J``HP"I_Z;_[_[Q_I@`E@#.`M`"NP&Z`8/_ +MA/^2_Y#_^/_Z_PS^"?X;_1_]1/Y`_HL`C@!0`U`#DP.1`^,!Y0'Q`.\`T`'1 +M`:(!H@%4_E7^*_PI_.7\Z/RL_J?^Y/_J_W0!;@$V`CL"?@)Z`O`#\P-9`E<" +MLOZT_@O^"?ZU_[;_>0!Z`#[_//]L_V__B_^'_SS^0?ZQ_JO^=@!]`(0!?`'. +M`=8!1@,^`[D"P0)\`'0`:`!O`'O_=?^__<+]Y/WD_5?_5O\```(`,O\O_^/_ +MYO^5`9$!>P*``A<"$@(7`!P`9_YC_KG^O/[H`.4`^@#]`"H`*`""`8(!_`'_ +M`5D`5`"T_;K]A/Q^_)3]F?W\__K_8`)@`DX"3@(8`1D!FP&9`:H"K@*:`)4` +M/OY"_AK_&/\._P__//X[_JC^J?[R_O'^7O]@_]T`VP"B`J0"^@'W`;L`O@#8 +M`-<`VP#;`*4`I@"$`(,`*P`K``#^`/[8_-?\Y/WF_?#][_U;_US_S@'+`3,# +M-P/$`[\#'@,D`P\!"@'J_>W]=_UW_?'^[?X`_P;_8_Y>_OK^_O[T`/$`0@%# +M`8P!BP'X`?H!.@`X`)3_E?\B`"(`9/]D__O]^OVD_J7^#`$,`9H!F0&``((` +M@_^`_R'_)/\X_S;_CP"0`/H!^`'-`-$`$0`,`%4`6@!R_VW_;_YS_I/^D/Z# +M_X;_XP#A`#<".`)'`44![?[O_GS^>_Z@_Z+_+0`K`"'_(?^5_Y7_B@"+`*`` +MG@#\`?\!\@'O`24`)@#<_]S_6`!7`*#^HOX]_#S\3_U/_;;_M?]E`6 +M_TW^3/Z]_[W_"@(+`I<"E@*/`9$!.@`W`'(`=`#P_^__0/]"_[3_LO]1_U+_ +M_/[[_DK_3/]._TS_,/\Q_ZD`J0`C`B("10%'`?L`^0#L`.T`6?]8_\#]POUC +M_F'^(P$F`_QS_F_^:_RW_+O_N_>_]5_]5_YO_F_NC^8O]?_U7_6/\0_PS_P__'_V'_7_\M_R[_K0"M`#<"-P+A`>`!.@`Y +M`$?_2__N_NO^_/[^_C0`,P`O`2X!$@`3`*W_K?\T`34!=P!T`*K^K_["_KW^ +MF/^;__W__?_K_^C_#``0`/+_[__K`.T``P,!`\4"Q@+>_]__G/V9_>3]Z?VP +M_:G]>OV`_0[_"?]A`&8`6`)5`F<#:`,.`@P"<@!U`"4`(@#.`-$`$0`.`(;^ +MB/[@_>#]I?VE_>']X/WJ_NS^@0!_`/$`\@#O`?`!&@,7`ZT!L@&@`)L`NP"^ +M`,__SO\B_B+^F?V:_0_^#OZY_;K]`?X`_HX`CP!.`TT#O0._`\D"Q@(5`1@! +ME?Z2_@?^"OYT_G'^L/VS_??]]/TT`#<`Z@'G`28!*0'V__/_\O_S_P,!!0&7 +M`94!`(P`C0!8_U?_4_]6_\O_Q_^O`+(`;P)M`@(" +M`@*.`)``JP"I`(T`CP!$_D'^A/R'_'+];_WH_NO^,@`P```!`0%M`6P!]@'W +M`4X"3`*K`JX"\P#Q`!#^$OXJ_2C]O/R]_";])?T1_Q+_^0#Z`"4"(P+[`OX" +M^P+W`LX`T0`(_PC_S/[*_B/_)O^+_XC_K?^O_[;_M?^T_K7^6OY:_AX`'`"S +M`+8`1@!#`!$!%`'"`;\!%0$6`7``<@`&``(`1O]+_PK_!?]G_VK_A?^%_Z[^ +MK?[<_MS^T@#3`.P!Z@&J`:P!K0"L``C_"/\;_AO^#_\/_]G_V?_U__7_B@&* +M`:8"I@(``@`"-0`U`,+]POW._,W\G_VA_9#_CO^!`(,`[O_K_R`!(P$O`RT# +M!0,%`Q3\X?Q<_&#\MORQ_!#_ +M%O^.`8D![`+P`K<#M`-5`U8#G0&>`7K_>/_`_L+^S/[,_A'^#_ZF_:G]@OY^ +M_IC_G/]-`4H!P0+$`GT!>@$2`!,`7P!?`!@`&`"Q_K+^(/X?_@,``P#S`?(! +MF0&:`04`!0!H_FC^#_X/_D__3__)`,D`;P!O`#0`-`#;`=H!HP&F`?W_^O_< +M_M[^^?[X_FL`:@#/`-(`,@`O`#G_.__X_O?^[?_L__C_^_\Q_R[_,/\R_QP` +M'`!J`&<`Z0#N`&$"7`);`F`"P`&[`0$!!0'N_NK^M?RZ_!O\%OQ,_5#]]O[T +M_M``SP`9`QX#:@-A`UH!90&,`((`Y`#L`-S_U__H_NG^VO[<_L[^R_XQ_S3_ +MIO^C_X__D?^6_Y7_R0#)`.4!Y0%L`6P!#@`.`++_L?_%`,@`^@#U`!@`'0#. +M_LO^=_UX_6?^:/ZC_Z'_W__A_P@!!P&J`JH"5`-5`Z8!HP%-_U'_KOVK_2W] +M+_V7_I?^80!>`/P`_P"0`(X`60%:`?`!\0$J`"@`5_]9_W__?O\0_P__-/\V +M_Y'_C__"_\3_'@`>`#(!+P$2`A<"/0$W`9W_H__R_N[^__X!_T__3O]P`'$` +M&@$8`70`>``_`#H`W__D_P__"O\C_R?_&0`7`*8`J``M`2H!.@$]`3\`/`"U +M_K?^6?Y:_N__[/_<`-\`?0!Y``8!"P$1`0T!G@"A`,0`P0`;`!T`?/Y[_M[] +MX/T4_Q'_A?^(_T;_0_\Z`#T`0P)!`J`#H0-<`EP"3`!+`#O^/?X]_3K]H/ZD +M_F'_7?]5_UG_>P!W`*P!KP&Y`;D!P`'``=C_V/_1_M+^?_]]_Y/_E?]._DS^ +MKOZO_FD`:@!!`3X!S`#1`,3_O_^I_ZW_:0!F`+0!MP&B`IX".0$^`;[_NO]> +M_U__,?XR_N[\[/QA_63]\O[N_F\``` +MW`!;_U__&OX5_E7]6/WW_/G\*_XF_GP`@0!U`7$!?0%_`<,!PP$3`1(!90!F +M`&,`80`1`!0`[__M_Q,`%``A`"$`^/[V_E?]6/U(_DG^<0!P`%4!5@%#`4(! +M70%<`>P`[@!-`$L`8P!F`(7_@__$_L/^A?^)_S``*P#A_^7_W/[;_A#_#_^( +M`(H`^0#W`)8`EP">_YW_`/\"_^#_W?\9`1P!1@%$`1T!'@%4`50!D0"0`/[^ +M__Z\_;S]%?T5_>[][OVM_ZW__P#^`"P!+@&R`+``(0$C`:$!G@'3`-8`DP"2 +M`)D`F`!:_US_=/YQ_G_^@?Y5_E?^F/Z4_IW_H?^2`(X`N`"Z`,``P0#^`/T` +M[P#P`,X`S`#B`.,`8@!A`'[^@/X]_3S]POW#_;'^KOYA`&8`EP&0`8L!DP'9 +M`=,!?`%_`0D`"0!0_DW^!?X)_EW_6__X__?_9?]G__G^]_Y@_V'_/@`_`%4! +M4P%K`6L!X__F_[__NO^$`(L`IO^?_WO^?_Z^_KW^(``?`,D`S``'``,`5/]7 +M_SW_//_1_]#_#`$-`1(!$@',_\O_.?\\_S?_,O^5_IK^A?Z!_I/_EO_R`/$` +MU0'5`9`!D`%"`$(`/_]`_[_^O/Z=_J+^Y/[>_E7_6_](`$0`5P!9`.#_W_^_ +M`+\`+`$M`5H`6@"<_YO_T/[1_D+^0?Z+_HW^:?]G_]S_W/^"`(0`Q0'"`>@! +M[`$^`#H`;OYP_N+^X_X5`!,`NO^\_TW_2__@_N+^R_[*_J7_I/_^_P``_?_\ +M_[8`MP#K`>H!#0(,`BH`+0`Y_C;^N?V\_4K^1_["_L3^!?\#_RK_+?_S__#_ +MS@'1`6X":P*.`9$!2@%'`9X`H0#E_N+^7?U@_9W\F_S\_/W\AOZ'_G,`;P## +M`<@!A0&"`=(`T@#4`-<`7@!9`/7_^?]K`&H`5@!5`#S_/O]G_F7^2OY+_D;^ +M1OZ=_IS^7_]A_VD`9@!%`4D!!`'_`'\`A`#V__+_W?_A_XL`A@#+_\__G?Z: +M_H;^A_[T_O?^\?_K_[L`P0"*`(0`X__H__/_\?^I_ZG_2?Y*_NK]Z/V!_X+_ +MLP&U`4@"1`)#`4@!1@!!`.O^[OY:_EG^J?ZJ_B#^'O[]_0#^AO^!_^8`[``# +M`?\`Q0#'`$(!00'G`>C_Z?]E_F/^=OUX_:G]J/V1_I#^V?[:_A#_ +M$/_P__#_A`"$`.\`[@#>`=X!5@)8`E`"3`*3`9D!E?^/_X+]AOT,_`O\\OOP +M^SO]/OT(_P?_B0&'`1$#%0.$`G\"D@&6`6,!8@&B`*$`!?\&_QS^&_Z__<#] +M]?WT_>W^[OZ8_Y;_'0`@`-0`T@"R`;,!E0&4`8K_B_^"_H+^0#0`-$`DO^1_V#^8/Y>_F#^D_Z0 +M_I/^E?[;_MO^]__V_TT!3@%.`DT""0,)`VH":@)=`%\`>_YX_G#]`&(`3@!*`%X`8P#;_]7_ +M$/\5_[/^L?Z?_I[^>/]\_UT`5P!J`'``\@#N`.D`ZP`)``@`U/_4_QT`'@!2 +M`%$`/@`_``<`!0"+_XS_M_ZX_F[^;?[E_N;^U?_4_R`!(`%E`F8"'@(=`N__ +M\/^P_J_^D_Z5_G#^;OZV_K?^1?]%_X4`@P"[`<`!VP'5`3(!.`$O`"D`A_^, +M_VO_9_^+_H_^2/U$_:;]JOW$_[__O0'"`9L"EP(8`AH"`P$#`6P`:P!4_U?_ +M\?WN_:K]JOVW_KG^`0#^_T0`2`"C_Z#_E/^5_Q0`%0`0`0X!)`(D`L8!R`'7 +M`-4`:`!J`&7_9/^\_;O]`?T"_:O]K/V8_I?^9?]F_V(`80"6`94!?@*``J0" +MI`)H`F8")0$H`1#_#?^M_:[]GOR?_,'\OORU_KK^L@"L`*4!K`&?`9``XP"Z`+D`?@!_`"``'@`E_R?_6_Y9_GS^?OXB +M_R'_C_^/_^__[_\H`2@!KP*O`NP"[0*#`8$!```#`.+^W_X!_@/^3_Y._O'^ +M\OYK_VK_$``1`#<`-@`J`"H`50!7``,!``'<`=\![0'J`2T!+P%K`&L`;O]M +M_SC^./ZN_:_]/OX\_E3_6/]O`&H`G`"?`)\`G@",`8T!.P([`L@!QP&"`(,` +M1?]$_[S^O?YL_FS^._XY_MO^W_[\__?_0`%$`0$"_P'%`,8`,?\Q_W?_=?^% +M`(@`P`"^`(P`C@"'`(4`>P!\`!T`'`!?_V'_L/ZO_KC^N/YM_V[_/P`]`-C_ +MV?\\_SW_+``J`#H!/`&M`:P!>@%[`=L`VP!Y`'<`"P`-`%;_5?_C_N3^]O[V +M_AW_'/_H_NC^AOZ(_K_^N_X&``L`@0%\`6\"=`(L`R@#A0*&`L,`Q0`#_P#_ +M7_UB_0+]`?WR_?#]+O\Q_^[_[/]O`'``7`%<`1\"'P*C`:,!S`#+`)<`F``G +M`"8`+/\M_TG^2?[A_>+]N_ZX_EL`7@#E`.(`0@!%`/C_]O]1`%0`^0#U`/\` +M`@&A`)\`M@"V`*0`I@#=_]O_?_Z!_FW]:OU8_EK^40!1``@!"`'#`,(`C0". +M`&X`;0`Q`#(`^__[_[C_N/_>_]S_?@""`+H`M0!*`$\`B/^%_YW_GO]%`$8` +MK/^J_]S^WO[F_N3^*?\K_\3_P_^[`+T`JP&I`?L!^P%C`60!*``G`.G^ZOYC +M_F3^O_Z]_FW_;O^W_[;_%``5`)0`E``\`#P`Q/_#_PT`$`"A`)P`9`%J`60! +M7@'Z____^O[W_@#_`?\V_S?_]/[P_JK^L/Y>_U?_?P"%``,!``$<`1L!)`$G +M`4,!/P%)`4T!30!+`!O^&O[C_.;\\_WP_9W_G__$`,,`3`%,`9L!F@&=`:$! +MB`""`!S_(_]Q_FK^@?Z&_IC_E?\S`#4`M_^W_XK_B/\?`"$`GP"=`,0`Q@#& +M`,4`;P!P`!$`#P#`_\+_*_\I__/^]/XR_S/_H_^B_Z+_HO\U_S7_J?^H_VP` +M;@"9`)@`#0$-`58!5@&;`)P`9/]A_U/^5O[W_??]S?[,_DH`2P`Q`3`!_0#] +M`&L`;0`J`"@`C?^._V3^9/XZ_CK^3?]-_U``4`"V`+0`<0!U`!,`$`"G`*D` +M^`#V`/W__O^B_J'^\/WQ_8;^AOYJ_VK_X/_A_W@`=0`\`3\!G0&:`>@`[0!. +M_T?_'/XD_LC^P?[E_^K_NO^W_SK_._\/_Q#_!?\#_S[_0/^P_ZW_BP"/`(8! +MA`$"`@("P@'"`3H`.0"=_J#^,_XP_OK]^_V$_8;]Z?WD_>7^[/[/_\C_Q`#( +M`,0!P@%2`E0"/`(Z`@4!!@$D_R3_NOVY_6[]G]G_Z@_DC_1_\-``\` +M\0#O`.0!Y@$=`AH"^0#]`-C_U/]A_V3_C/Z+_L_]S?TX_CO^]O[T_B?_*/\D +M_R/_0?]!_^;_Y?]3`54!=@)U`DP"3`+?`-\`8/]@_V+^8_X6_13]2OQ,_&;] +M9?U9_UG_%`$5`;H!N0$;`1L!I0"E`-P`W`"?`)\`=_]W_X7^A?Z;_IK^'O\? +M_VK_:?]^_W[_W__@_RT`*P`P`#,`J?^F_V#^8OY`_C_^R__*_^4`Z``+`0_MC] +MU_TJ_BK^JO^K_RD!*`&1`9(!VP';`<8!Q0%Q`'$`AOZ'_E#]4/TL_2K]4?Y5 +M_O7_\/]L`'``$@`1`#4`-`#5`-8`Z`#H``8`!0"U_[?_,``M`,O_SO_8_M3^ +M"OX1_@'^^?TN_S7_A@"``(X`D@`K`"H`@0"!`!8!%P'6`-,`Y__K_PS_"/^* +M_HW^%/X2_K[]P/T8_A7^3O]2_X$!>P'G`NT"5`)0`A0!%P'(_\7_/_Y"_F+] +M7OV8_9W]A_Z"_I[_H_]F`&(`J`"K`(\`C`"'`(H`JP"H`!\`(@`B_R#_]/[U +M_DK_2?_X_OC^]/[U_MC_U_^4`)4`KP"M`/3_]O\5_Q7_,_\Q__G__/^#`'\` +M"P`/`%3_4O]P_W#_9?]F_\+^P/[%_L?^WO_=_Q0!%0&!`7\!F@"=`-[^VOX[ +M_C_^V_[8_D;_1_]@_V'_\/_N_PH!#`&&`84!U@#6`",`(P"(_XC_ZO[J_I[^ +MGO[:_=K]+OTN_3K^.OY'`$<`H0&@`2("(P)#`D,"F`&8`4X`3@#]_OW^7OY> +M_G/^<_ZR_K+^XO[B_J+^H_YA_F#^%/\5__S_^_^^`,``T@'/`78">0+O`>P! +MC`"/`/+^\/[P_?']P/V__>W][OT6_A3^:_YO_J[_J/]7`5\!!0+\`:,!JP&4 +M`8\!(`$B`8'_@O_N_>O]&?T<_8G]AOTZ_SW_I0"B`+$`M0`I`"0`&0`=`%8` +M4P#+_\S_4/]1_^7_Y?^/`(T`;@!P`-?_UO\2_Q+_Y_[H_KG_M_\D`"8`@_^" +M_P7_!?\H_RG_=_]T_Y/_E_^I_Z7_*``K`)4`E`"H`*@`;P!N`.W_[_\*``@` +MC@"0````_O^3_I7^Q?W$_8?]AOVE_:?]MOZT_J@`J0!M`FX")@,D`W8">`+B +M`.``'/\>_P'^_OV8_9S]+_TK_5[]8_W;_M;^```#`'$`;P`0`1$!V0'9`40" +M10*D`:$!!``(`&G^9?[M_?#]9_YF_I[^G?Y?_F#^O?Z\_KG_NO\7`!<`[__P +M_U\`7``8`1L!V@'7`;4`XP`!``8`:?]A_S'_.?]M_V?_HO^E_S3_-/_Z_OC^R__-_Y,`D`"< +M`)\`3@!,`/C_^O]S_W+_IOZF_CS^._[(_LG^O?^]_YT`G@#*`,D`$0`1`*?_ +MI_^H_ZC_2/](_SG_.?_G_^;_/@!!`+?_L__N_O'^>/YW_MG^V/[H_^O_N0"T +M`,``Q@"8`),`S`#/`)``CP`B_R'_,_XW_G/^;OZ4_I;^E?Z5_J_^L/Y5_U7_ +MT@#1`&0"8@)Y`GT"#0$)`7C_??^:_I7^Y/WG_5O]6?W:_=S]B_^)__8`^`!^ +M`7T!^0#Y``4`!`#\____5P!3`-/_U_\!__[^M/ZU_L?^QO["_L3^Z_[I_JK_ +MJ_^C`*0`,P$P`4\!4@'-`,P`Y/_C_Z;_J?^@_YS_W_[C_E;^4_Z"_H3^K_ZN +M_@__#O\8`!L`=`%P`3<".P+A`=X!IP"H`!G_&?\/_@[^)OXH_H7^@_[L_N[^ +M'``9`"L!+0$)`0D!1@!&`-[_WO_N_^S_/P!"`",`(`!,_U'_!_\`_ZW_M/]L +M`&<`/@!"`+3_LO_C_^3__?_[_UO_7O_Z_OG^/_\__QL`'`!+`4D!?@&``5,` +M40!N_W'_M/^Q_R8`*``*``H`^__X_SH`/P`+``8`%O\:_U'^3OXW_CG^"?\( +M_[8`N`"V`;(!HP&H`:H!I0&@`:,!U0#5`,S_R?\$_PC_B?Z&_B#^(O[W_?;] +MB_Z+_L+_PO]8`5@!=P)W`DH"2P)K`6H!T@#2`/[____:_MC^H?ZC_D+_0?^: +M_YK_;?]N_PC_!_\=_QW_*``H`)0!E@%3`D\"W`'@`5P!60$0`1(!UO_6_QC^ +M&/Z5_9/]7_YB_E'_3__L_^W_*0`I`(T`C0"J`:H!8`)@`JL!JP%/`$X`@O^% +M_VW_:O\?_R#_$?\3_]W_V/_'`,X`)`$=`:H`L`!/_TO_8OYE_@O_!_\'``P` +MC`"'`!0!&0&X`;0!G`&>`=\`W0`M`#``Y/_A_Z?_J?]O_VW_&O\<_Z/^H?[3 +M_M;^]?_P_Z4`J@#X`/0`G0&@`>X![0%'`48!6P!<`/C_^/\2`!$`X/_A_QO_ +M&_]#_D+^^_W]_>'^W_Y#`$0`00%!`2<")P(G`R<#VP+<`A,!$`$N_S+_"_X' +M_@_^$_[H_N3^7O]A_WC_=O\"``,`'`$;`8P!C0$%`00!P`#!``0!`P&T`+0` +MZ/_J_U__7/\^_T'_U?_2_Y\`H@!@`%X`@/^!_V[_;O\'``8`=`!W`*\`JP`5 +M`1D!90%A`;\`P@"Y_[C_Z?[I_K/^M/Z%_X/_K0"O`/T`^P#C`.4`Y0#C`'0` +M=@"Y_[?_@_^%_]G_UO\@`"0`0P`_`$P`3P!6`%4`FP":`/@`^@##`,``I?^H +M_R+_(/]%_T?_(?\>_SW_/_]I`&D`M@&U`2(")`*$`8$!30!/`&[_;/]N_W+_ +MX?_<_\W_TO^%_X'_S__1_S``+P#&_\?_=/]Q_P\`%0`6`1`!Q0'*`7X!>0$_ +M`$$`?_^`_YW_G/^D_Z7_-O\V_R?_)/_D_^G_M`"O`.P`[P`-`0X!30%*`4$! +M1`'*`,<`>/]Y_]/]U?V+_8G]R?[*_A``#P`1`1(!&0(9`HP"BP+'`?Y[_K_^O?X@_R'_]?_U_R,!(@'1`=0!KP&J`?P``@$E`!\` +MX?_F_U8`4@!K`&X`UO_5_X__CO]'_TC_D_Z3_GC^=_Z"_X7_\P#N`/@!_`%% +M`D("XP'E`1L!&@%O`&\`OO^__X_^CO[4_=7]2_Y(_O/^]_Y4_U#_/P!#`+L! +MN0'/`L\"T`+1`IX!G0'I_^G_\O[S_OW^^_XI_RW_#?\)_U'_4_\7`!8`.``Y +M`*W_K/^>_Z#_.P`X`"P!+@'2`=(!6@%8`4@`2P#T__+_(@`B`-G_VO]T_W+_ +MG/^?__O_^?_<_]W_I/^B_ZK_K?_9_];_6P!>`,T`RP!D`&0`[?_O_WL`>``@ +M`2,!.0$V`1,!%@&V`+,`LO^W_V;^8/Z;_:']Z/WC_2S_+___`/X`.@(Z`F4" +M9@(7`A<"G0&<`5T`7@#V_O3^E_Z:_M#^SO[1_M/^S_[+_B+_)_\6`!(`7@%B +M`3,"+@+6`=H!_`#X`*X`L@!Y`'<`C?^-_^;^YOY&_T;_K?^M_WS_>_\N_S'_ +M"O\&_[?_NO]3`5,!8`)=`NP!\0$3`0X!C0"0`-3_T?^B_J?^3OY(_A#_%O\@ +M`!P`RP#,`-L`W`!D`&(`;@!P`.``X`":`)D`UO_6_ZS_K/_9_]K_V?_8__+_ +M\_]J`&<`U@#:`-H`UP!E`&<`D?^0_PW_#/^'_XK_80!>`)$`DP"[`+H`!0$% +M`:,`I`"__[W_;_]R_P4``0"T`+D`\@#L`(4`B@"J_Z;_+O\Q_V/_8O^+_XO_ +M>?]X_S<`.`!V`7`/4`]P!(`4@!X`#> +M`)__H?_%_L/^_YK_VS_L_^Q__7_]__)`,8`?0&``<@`N`2X!I@&D`38!.`'8_]C_\?[Q_OC^]_X"_P+_1_](_S\`/@`K`2T!'P$< +M`:D`K`!?`%T``P`$`,'_P/^D_Z7_2?])_P__#_^J_ZG_C@"/`-``SP#R`/0` +M4P%/`>,`Z`"B_Y[_GOZ@_G/^=/X(_P7_#@`1`+<`M`!Z`'X`5P!2`*D`L`"/ +M`(@`\/_U_P,`__^"`(4`5P!5`(S_CO_=_MS^F/Z8_D;_1?^'`(@```'_`+8` +MN`#;`-D```$!`3``+P`H_RG_ZO[H_OS^__[Q_NW^(?\F_Y'_C/\\`#\`D0&0 +M`8P"C`+Y`?D!B`")`&O_:?]/_E'^5OU5_7K]>_W,_LO^.@`Z`#D!.`&/`9$! +M0@%!`=$`T0#!`,$`/@`]`%'_4O_W_O;^$?\2_\'^P/Z7_IG^6O]6_VD`;P`! +M`?H`'@$E`<@`PP`T`#<`,@`O`$H`3@"L_ZC_WO[B_N7^XOX"_P/_F_Z<_M+^ +MT?X.``X`3P%/`?@!^0&[`;H!@`""`%C_5/\J_R[_,?\N_]K^W?X<_QG_"``* +M`$@`1P#(_\?_O]V/[2_NC_ +M[O^7`)$`4`%5`3$"+@(/`A`"#0$-`5H`60#`_\#_D/Z2_F[]:OT@_27]S?W( +M_5+_5_\M`2L!XP'7`=X!=`%N`3(`-P`C_R'_&_\9_TG_3/\+_PG_ +MR/[(_I_^HO[O_NK^#@`2`#X!/`%:`5L!%@$5`2L!+0&\`+D`FO^=_P3_`?\\ +M_S[_@_^#_Z;_IO_%_\7_L_^R_^S_[?^T`+0`\@#R`$H`2@#8_]C_U?_5_W3_ +M=?];_UG_)@`H``D!"`%1`5$!`P$$`0P`"@"\_K_^4?Y/_L+^POXD_R3_R?_* +M_PH!"0&[`;T!7`%9`?@`^0#B`.,`CP"-``P`#0!H_VG_=_YT_AS^(?[1_LO^ +MD/^4_]O_VO^5`)0`E@&8`:8!I`'M`.T`CP"2`*``G`"7`)H`6`!6`(3_A/]9 +M_EO^(OX@_MK^VOY]_W__%``0`$_R?_)?_9_]S_K`"H`*D`K@"Z`+0`&`$?`>D`X@`3`!@`>O]W +M_R__,/]/_U#_]__W_VH`9P`&``H`T/_-_S4`-@!``$(`%@`2`)@`G0`Q`2P! +MO@#!`-;_U?\N_R[_Y?[F_DO_2?\X`#L`LP"P`+T`OP#Y`/<`X@#D`.?_YO\D +M_R7_6_]9_[S_O?^Z_[K_R/_)_^[_[/]4`%8`,P$P`8T!D`&[`+D`VO_<_YK_ +MEO\;_R'_=OYO_N/^Z?XZ`#8`.0$[`80!@P$<`1P!+``M`(?_AO^X_[G_J/^G +M_S7_-O]M_VS_!P`)`.K_Y__+_\W_2P!+`/L`^@`Z`3P!)`$B`6L`:P!=_U__ +M^_[X_AG_'?_\_OC^"O\-_][_W/]G`&@`:`!G`*P`K0`X`3@!4P%2`?D`^@`I +M`"@`UO[8_O3]\OTT_C;^V/[5_FS__[<`MP!M +M`6X!>@%X`;D`N@"+_XS_._\Y_WO_??]5_U3_2O])_^+_Y?\#````5/]7__3^ +M\/XD_RG_?_]Z_^7_Z?\I`"<`W/_<_Z7_IO\-``T`4P!1`/+_]?\.``L`30!/ +M`*/_H_^U_K/^BOZ-_L/^P/X<_Q[_M?^T_^__\/_%_\/_#``.`+4`LP"O`+(` +M@@!_`+\`P0"&`(0`(O\C_^/]Y/V-_8S]Y/WC_>+^Y/XD`",`KP"O`.(`X@!' +M`4@!#@$+`0,`"`!I_V3_C/^/_V7_9/^X_KC^>?YY_KC^N/Y(_TG_-``R`+,` +MM@!1`$T`(0`D`%0`4P#I_^K_4/]/_X#_@?_5_]/_C/^-_P;_!O_,_LS^QO[& +M_F[_;_^>`)T`(`$?``/K__/\,``L`\O_Q_Z;_I_]._TW_J/ZI_G3^=/Y?_U__ +M6P!:`-$`T@!T`7,!RP'-`>8`XP!:_U__:?YC_OC]_?TT_C'^"O\+_\?_R?\@ +M`!T`FP"?`$`!.P'\``$!A@"#`)D`F@!*`$H`,/\O_U_^8?Y4_E+^C/Z._D/_ +M0?\<`!T`7@!>`'8`=0#E`.<`!0$$`7,`OYD_V3_B`")`-X`W``/`1`!P`&_`>8!Z`%!`3X!)P`J`+3^L?Y@_6+] +M3?U-_?+]\?V5_I7^KO^O_U4!4@$*`@\"BP&&`?\`!`'G`.(`=`!W`/C_]O]K +M_VW_?_Y]_@G^#/Z&_H+^!O\)_V;_9O]B`%\`7@%D`6(!6P&R`+@`.0`U`.?_ +MZ?^J_ZK_IO^E_WK_>_\*_PK_.O\X_^'_Y/\+``@`(0`C`*@`J`"F`*0`R__- +M_R7_)?\1_P__8/]D_S0`+P`(`0T!&0$5`9@`G``\`#<`C?^1_X7^A/YM_FS^ +M/O]!_ZW_J/_)_\S_/0`\`*<`J`#U`/4`4@%0`1L!'@%%`$$`A_^,_S;_,O^+ +M_HW^(OXB_N#^W_X```$`7@!=`&X`;P"&`(8`=P!W`+@`MP`5`14!I@"G`-K_ +MVO^,_XS_"_\*_S'^,OX<_AO^./\X_V$`8P`&`0$!6`%@`2\!)P&:`)X`9`!C +M`!$`$0`F_R;_N/ZZ_@3_`/_Y_OW^Y_[D_J'_HO^V`+<`/P$]`3L!/@'9`-4` +M!0`)`&G_9O]__X'_>/]W_SG_.?^=_YW__/_]_Y7_D_]>_V#_]__W_Y,`D0#+ +M`,\`O`"W`!,`%P!)_TC_^_[Z_@[_$?\0_PS_F_^=_[P`O``L`2L!G@"@`%8` +M50!.`$T`\O_S_XW_C/\H_RG_E_Z7_I;^E_Y=_UO_#@`/`'``<0`F`20!I`&F +M`>$`X`!\_WO_SO[0_K[^O?X%_P;_R?_(_U,`4P!)`$H`>P!Y`*(`I0`-``L` +M@_^#_['_L__!_[W_+O\R_]/^T?X/_Q#_E?^5_V,`80`K`2X!-`$S`<4`Q`"6 +M`)@`^?_U_^C^[?ZX_K7^$?\2_PO_"O_L_NW^1/]#_]S_WO^0`(T`<0%S`OUZ_1S^&OXX_SO_Y?_B_W<`>@#U`/(`#@$0 +M`1P!'`$%`0,!6`!;`)'_CO\M_R[_T_[5_CC^,_Y'_D[^8?]:_SP`0@""`'X` +MF0":`'(`<@`_`$``C@",`*(`I@`H`"(`S?_4_[G_L?\*_Q/_3_Y'_HO^D/Z- +M_XS_20!)`+@`MP#9`-L`5@!3`.'_Y/_]__W_T?_._W?_>__7_]/_*0`M`,#_ +MO?]I_VO_IO^C_^S_[__L_^K_T?_4_Y+_CO\W_SG_@?^!_R$`(`!]`'\`W@#< +M`&@!:0'U`/0`N?^[_^3^XOZ:_IO^@_Z$_M/^T/X^_T+_@O]___/_]/^[`+L` +M-`$T`3P!.P&,`8X!L@&P`90`E0`#_P/_)/XC_L/]Q/W;_=O]G?Z<_EC_6O\# +M``$`#@$/`>8!Y0'2`=,!:P%K`3P!.P&,`(T`#_\._]?]U_V7_9C]$/X/_C'_ +M,?^%`(8`(@$A`4\!40%O`6P!V`#:`-S_V_]\_WW_K_^O_Z/_HO]5_U7_4?]2 +M_WC_=O^Y_[S_5`!0`*H`K@!D`&$`/0`_`$0`0@##_\3_9_]G__O__/^,`(L` +M<@!P`!T`(`#3_]'_AO^(_X7_A/_+_\K_W?_>_\?_Q_\!````"P`.`)+_C?^Y +M_[__O0"W`$H!3@$R`3`!R@#+``$``0`__S__]/[S_M/^T_[-_L[^7?]=_T(` +M0@"*`(D`:`!I`-$`T`!:`5P!.`$U`;D`O``!`/[_"_\._[3^L?[[_O[^*_\H +M_X3_AO]I`&@`]@#W`+@`MP!A`&$`DP"3`,,`Q`"=`)P`3`!,`+#_L/_I_NG^ +MP/["_A;_$_]3_U7_%P`6`"X!+@%[`7T!"0$'`94`E0!.`%``[O_K_X__D_]> +M_UG_$O\7_R;_(O_#_\;_6`!6`*H`J0!1`50!HP&@`;@`N@"2_Y#_^_[[_LS^ +MSOX;_QC_S?_0_TX`2P"/`)(`W0#<`/@`^`"'`(<`.``X`(T`C`![`'X`J_^H +M_QS_'?\._P__/O\[_]K_WO^0`(P`O0"_`,``P0#6`-,`9`!I`,+_O/^A_Z;_ +M&P`8`"X`,`#J_^C_Y/_F_P$```!"`$$`R@#-`-L`U@!(`$P`[/_K_YO_FO_J +M_NO^R?[(_J?_J/_"`,``3P%1`7(!<0$L`2P!AP")`/O_]_^Y_[W_3?])_P+_ +M!_]R_VW_P__'_[7_L_]"`$(`.@$\`9T!F@%=`6`!T@#/``$`!0!"_S[_!/\' +M_QG_%_\U_S;_J?^I_UT`70!S`',`1@!&`,P`RP!Q`7(!B`&'`3`"<` +M)`!C`&<`DP"0`+(`M`"P`*\`)``D`*/_I/^,_XO_4_]4_S[_/?_K_^O_@0"" +M`%P`6@`"``4`[__M______\_`#\`H`"@`,T`SP"J`*<`A@"(`$D`2`"&_X;_ +M*O\L_W;_#]P?Z^_E[_8/_'_\7_;`!M`/@`^`!@`6`!K`&M`6`! +M70&A`*4`"0`&`&+_8_^$_H3^'?X>_LC^Q?[&_\G_6P!9`+(`L@#H`.H`T`#- +M`,P`S@#<`-T`80!>`,[_T/^F_Z;_,?\P_YS^G_[<_MC^O__!_W0`=`#%`,8` +MT0#.`'<`?`#Y__'_UO_?__[_]__Y__W_1P!%`*4`I0`T`#0`C_^0_X/_@O^C +M_Z7_F_^9_XO_B_^%_X;_BO^(_\O_T/]-`$<`F@"@`-8`T``T`3@!!@$%`?'_ +M\/\5_Q?_$/\._RS_+?]K_VO_O/^[_\#_PO^\_[G_```$`#(`+0`O`#,`B0"' +M``X!$`'8`-4`Y__K_TC_0O\6_QW_$/\)_TK_4/^V_['_T/_4_R4`(P"6`)4` +MG@"@`'@`=@":`)P`?P!^`,K_R?_C_N;^HOZ>_M;^V_Y@_UO_)@`J`+``KP#" +M`,``O@#"`(\`B0#;_^'_I?^B__+_\O_:_]O_:O]J_RK_*/\R_S3_@_^!_Q`` +M$@"P`+``W`#;`,0`Q0"9`)<`Y/_F_P#___[]_O[^??]]_Y+_D?^6_Y7_W?_? +M_S``+P".`(\`[0#L`-T`W`!P`',``0#^_WS_?O^X_K?^>/YX_D__4?]5`%(` +MOP#!`-``SP"7`)@`!P`&`+__P?^;_YC_;O]Q_Z;_I/\B`",`/``\`/____\* +M``H``9H!D@&3`8D`B0"?_Y__%_\8_\7^Q?Z]_KK^`?\%_V#_7/_G_^O_ +MF0"5`!D!'`%%`4,!@P&%`9H!F0'1`-``D_^4__'^\_[-_LG^W_[D_E[_6?_` +M_\/_X__C_RH`*`!Z`'P`@@"!`*4`I@`R`3`!A0&'`><`Y0#I_^O_7/]<_P7_ +M`__]_O_^@!\`,$`OP"D`*8`*``G +M`+3_M/^8_YC_KO^M_PD`"P!F`&4`5`!5`#$`+P`7`!@`IO^G_V7_8_\*``X` +MXP#>`#D!/0$R`3`!\`#Q`&,`80#0_]/_@_^`_T[_4?\L_RK_9?]E_[K_NO^S +M_[3_"@`)``P!#0'$`<(!Q0''`7D!=P'0`-,`_?_Z_US_7O_G_N3^H?ZF_M#^ +MR_YL_W'_Z?_D_RP`,`#L`.H`\P'T`4L"3`+F`>(!`@$'`;C_M?^C_J/^4/Y3 +M_E7^4?[1_M/^!@`&`",!(@%9`5L!*`$F`2`!(@$0`0X!G`"=`#``,`#1_\__ +M5/]8_TK_1O^>_Z'_S__-_R(`(@"R`+0`LP"P`"4`*`#A_^#_`@`!`%H`70"F +M`*$`TP#9`,``N@!<`&(`&``3`,S_S_^;_YK_Z__J_VP`;@`R`#``M/^U_YC_ +MF/^__[__$``1`)0`D0#T`/@`$0$-`0H!#0'%`,0`'0`<`(O_C/^S_[/_W__? +M_WS_?/]6_U3_P?_$_T8`0P#1`-4`5`%0`4(!1`'9`-<`8P!G`+;_L_\)_PK_ +M!_\'_\/_PO]1`%,`80!@`'``;@"'`(P`DP"-`+D`OP#N`.@`J`"L`&\`;@`@ +M`"``9O]F_]S^W?XF_R/_PO_'_SL`-@"-`)$`\0#N`!8!&0$1`0\!!@$&`:<` +MJ`#\__K_E?^8_RS_*?^S_K;^"/\%_S<`.P!!`3P!HP&I`9D!DP$!`04!,``N +M`&[_;_\7_Q?_#_\0_WO_>/\S`#4`A`"$`%X`7@"I`*D`(`$@`1X!'0'/`-$` +M8`!>`,/_Q?]D_V/_6_]9_V;_:O_`_[S_90!G`-T`W0"J`*D`3`!-`&X`;@"7 +M`)8`>P!\`%T`70`0``\`G/^=_Y+_DO_3_]+_#P`1`),`D``S`34!*0$H`5T` +M7@"9_YC_5?]6_UW_7/^B_Z+_2`!)`,<`Q@#_```!*0$G`>P`[P!6`%0`"``( +M`-S_W?]#_T'_S_[1_@7_!/^__\#_EP"6`&(!8P'3`=(!L`&O`24!*`%W`'0` +MA_^)_P?_!_]3_U#_B?^/_U__5_]>_V7_M/^O_R``)`#$`,``;`%P`;L!M@&K +M`;(!4P%,`78`?`!@_UO_S_[2_@#___X;_QS_'O\=_Y7_EO]"`$``W`#>`(`! +M?P&Q`;$!9`%F`>,`WP`P`#4`0?\[_ZK^L/[^_OK^RO_,_TL`2P!S`'``B@"/ +M`'4`;P!A`&<`D@".`)``D@"2`)``J`"K`%D`5@"<_Y__2_](_Y3_EO_M_^W_ +M'``;`%``4`!C`&,`1P!(`#4`-`!1`%(`20!'`&@`:@"V`+8`@P""`!H`&@`Z +M`#P`@@!_`%<`6@#W__;_=/]S__K^_/[*_L?^&_\>_^+_X/_G`.D`#@(-`I$" +MCP+O`?(!U`#1`/'_]?\E_R#_@_Z&_F;^9OZ5_I/^(?\E_PD``P#2`-@`6`%3 +M`<\!TP$:`A@"DP&3`50`50!?_U[___[__M/^U?[]_OK^<_]V_\?_PO\>`"0` +MGP":`-D`W0#^`/L`9@%G`8(!@@'7`-8`Q__)_Q7_%/_5_M;^[O[L_G__@/\_ +M`#\`OP#``/X`_@#Q`/``9P!G`!<`&``X`#<`,P`T`/G_^?_K_^G_$0`4`$0` +M0@!U`'<`H@"?`'L`?@`K`"@`V?_<_X7_A/\[_SG_N_^__[``K``)`0L!Y`#C +M`+(`M`!3`$\`\/_U_^;_X?_X__S_(@`A`%``3@!2`%4`Z__H_X__D__J_^;_ +M?@""`+H`M0#&`,P`V`#2`)(`EP!/`$L`#``/`*3_H_]W_W;_EO^8_Z?_I?^M +M_ZW_+``O`"4!(0&\`;X!N@&Z`4D!2`%L`&X`9_]E_^K^Z_[A_N#^&_\<_^+_ +MX/^[`+\`[0#H`,(`QP#6`-(`[@#P`,0`PP!X`'@`+``M`,7_Q/]O_W#_=O]T +M_[7_M_\E`"0`XP#D`%T!7`$(`0@!K@"P`(H`A@!/`%,`[?_J_Y;_F/]5_U7_ +M0/\^_X3_AO\0``X`N`"Z`(X!C`%(`DL"`P+^`<\`U0#%_\#_(O\F_\[^ROX, +M_P__M/^S_TH`2@#9`-H`*@$G`?H`_@"B`)\`EP"8`*(`H@`W`#8`Q/_'_];_ +MTO\<`!X`7P!?`+<`MP#7`-@`D`".`$X`3P#^__[_H/^A_\;_Q/]N`'``YP#D +M`-,`U@"=`)L`?`!]`%8`5P!.`$H`>@!^`'\`?`!A`&(`-``W`+S_M_]!_T;_ +MH_^>_V@`:@#F`.@`-`$P`6\!=`%E`6`!`P$%`8,`A0`#`/[_=_]]_S7_,/\H +M_RK_%?\5_W;_=O^@`)\`K@&P`?D!]@'6`=@!7P%>`:``H0#M_^S_D?^2_VC_ +M9_^._Y#_U/_1_^3_Y_^P_ZW_WO_A_Z4`HP!.`4\!E@&6`;$!L`%0`5(!H`"> +M`.[_[_]0_T__[O[O_B7_)O^B_Z#_[O_O_T``0`#R`/``IP&L`<\!R0&$`8D! +M`@'_`",`)`!F_V?_,/\N_TK_3/^]_[O_E0"7`/<`]`"9`)T`,``K`!8`'0`G +M`!X`/0!%`*,`G@`!`0,!%`$6`?D`]`"Z`,``40!+`"(`)P`R`"\`T__4_U/_ +M4_],_TO_G_^@_Q(`$`"F`*H`+`$F`6T!?]\_Q,`#@!:`&``70!8`$H`3@`^`#L`+P`P`'$` +M<0#,`,P`^P#[`!4!%`'-`,\`"``%`'3_=_^G_Z;_^__Z_S$`,@!P`&\`G`"= +M`'X`?@!)`$H`*@`G`!P`'@`N`"T`<`!R`'<`=0`I`"H`.P`X`*,`IP"]`+H` +M=@!X`"4`)`#)_\G_BO^)_Y3_EO_>_]O_7P!C``D!!0%8`5L!\0#O`#,`,@#A +M_^3_W__=_]/_U/_N_^[_%@`3``4`"0#Y__?_`0`"`/____],`$P`X@#A`!T! +M'P''`,4`7P!?`"\`,0#U__/_G?^=_XG_B_^5_Y'_IO^J__K_^/]K`&H`P0## +M`#8!-0%@`6$!Q`##`+/_M/_X_O;^P?[$_@__#/^[_[__L`"L`&$!8P&4`9,! +M20%*`88`A@#._\W_F?^;_X__C/\T_S;_(O\A_WK_>?_?_^+_4P!1`,<`QP`7 +M`1H`Z@!R`',`T?_/_Y3_E__A_]W_ZO_N_Z+_G_^'_XG_M/^S_^#_ +MXO]"`#X`N`"]`.\`Z@#F`.H`B`"&`-;_U_\Y_SG_5/]2_]O_W?]0`$\`?P!_ +M`)X`GP!P`&\`"0`*`,K_R?^H_ZK_M?^R_P@`"@!*`$H`/0`^`%H`60"M`*P` +MLP"T`$D`20"__[__4_]4_P#__OX._Q#_E?^3_T@`20#R`/(`;P%P`2P!*@%F +M`&@`^?_V_^C_[/_+_\?_J/^K_Z3_H?^/_Y/_7O]:_TC_2_]^_WO_\/_S_Z\` +MK@!6`54!3P%1`>\`[0"T`+4`7`!<`+;_M?\K_RS_SO[._J+^H?[>_M[^=/]T +M_RT`+@`+`0H!S`'-`=D!UP$&`00!\`/3_\O\Q_S+_B/Z'_J+^H_Z4_Y3_=0!T +M`!`!$@&``7X!EP&8`1\!'@%X`'D`S__._T#_0__\_O?^^_X`_QC_$_]?_V/_ +M-P`V`#,!,0&!`84!5@%1`0,!!@%\`'P`YO_F_XO_B_]L_VO_`-``UP!V`&\`_?\!`,#_O__D_^/_80!C`*(`H0!H`&<`*P`N`"(`'0#Q +M__;_VO_5__;_^_\U`#$`;`!O`)T`F@"@`*(`D`"/`)@`F0"0`(\``P`$`$K_ +M2?\?_R'_=O]S_^O_[O^E`*(`3P%3`80!?P$G`2P!@@!]`*3_J/\Q_R__8/]@ +M_]+_T_\0``\`-P`X`'\`?P"C`*$`EP";`)L`EP"?`*(`<@!Q`#$`+P#`_\3_ +M5_]4_V/_9/_7_]?_,@`P`%4`6`"?`)P`VP#>`/@`]P#L`.H`T0#5`'8`<`#F +M_^S_2_]'_ZC^J_YV_G3^0_]$_W<`=@!0`5`!N`&Y`;P!N@$J`2P!.@`Y`&W_ +M;?\(_PG_*/\E_YC_F_\*``@`.@`\`&<`90#!`,(`W@#=`((`A``O`"T`[?_O +M_[C_M?^N_[/_Y__B_R4`*0![`'@`C0"/`#,`,@"J_ZK_BO^)_^'_X_\Z`#<` +M?0!_`+8`M0"=`)T`-0`W`.G_Y?^]_\#_T?_0_S0`-`!O`'``*P`J`+3_L_^# +M_XC_@_]\_W__AO_#_[S_20!/`,<`PP#_``(!%@$4`>\`\0"J`*@`4P!5`)S_ +MF?^N_K#^0OY#_I/^D?XY_SS_$P`/`.<`ZP!]`7@!?P&$`1,!#P%E`&D`W/_9 +M_\;_Q__D_^3_I/^D_SG_./\2_Q/_'_\?_TO_3/_-_\O_?@!_`!\!'@%Q`7(! +M4@%3`;D`N``+``H`E?^7_S7_,O^@_J/^>OYY_N+^XOZ?_Y__60!8`!\!(`&+ +M`8L!:0%I`$!Y0%N +M`6L!MP"[`/O_^/\8_QC__]W_4P!6`/L`]@!1`5`($`?`#[____1_]%_];^UO[M +M_N_^9O]D_^/_Y?]O`&T`X@#C`!$!$0'8`-@`D`"0`&0`8P!H`&D`:@!I`"$` +M(0!R_W3_]?[Q_M?^V_X5_Q+_EO^7_W@`>@!1`4T!HP&G`64!80&Z`+T`$@`0 +M`)O_G?]P_V[_3O]/_T[_3O^J_ZG_,0`R`'``;P";`)P`P`#``*X`K`!#`$@` +MTO_+_W__A?^D_Y__%P`:`'(`<@!R`'(`,P`Q``@`"@#?_]W_R?_+_PT`#0"* +M`(@`V@#<`,``O@`[`#T`F_^9_U+_5?]^_WK_NO^__P0`_O]D`&H`NP"W`+(` +MM`!A`&``(@`C`/G_^/_0_]'_K_^N_Z/_I?_6_]+_9P!L`/D`]``!`04!H@"@ +M`"L`*P"/_Y'_!_\$_^C^ZOYQ_W#_00!!`-<`V``%`04!P@#``&(`9``F`"0` +M^__]_\__SO_<_]S_`0`!``(``0#C_^7_[/_J_R0`)@!L`&H`FP"<`'4`=0`I +M`"@`^/_[__3_[__)_]#_K/^D_[#_M__7_]+_Z?_L_RP`*P"E`*4`)`$D`6`! +M8`$=`1X!0``]`"S_,?^0_HK^<_YY_M?^T?ZA_Z;_O`"Y`'H!>P&(`8@!(P$B +M`9,`E``2`!,`OO^[_W+_=O\W_S/_2_]/_[;_L?\#``D`0``[`'8`>0"G`*8` +MC`"+`$,`1``3`!(`'``=`%T`7`"'`(D`1@!#`+G_N_]G_V;_2/]'_TK_3?^G +M_Z/_60!?`/4`[@`%`0L!N`"S`#H`/@#J_^C_W/_<_P(``@`3`!0`&@`9`"X` +M+P`0``\`O/^]_Y;_E/^M_[#_TO_/_^O_[O\/``T`5`!5`,<`QP`@`1\!%P$9 +M`9,`D`#T__?_`,X`R``7`1P!'P$<`9D`F0#G_^C_A?^#_X+_A/^= +M_YW_Q/_#_P8`!@!1`%$`L0"Q`.4`Y@#"`,$`70!=``X`#@#1_]+_D?^0_Z;_ +MJ?\I`",`H0"H`-$`R@"/`)0`$0`1`*W_J?^D_ZG_Z/_E_RP`+`!T`'8`N`"T +M`)(`E@`8`!8`S__0__?_]_\[`#H`@`"``)\`H0"%`($`60!>`"T`*0#D_^?_ +MF/^6_Z3_I?_E_^7_%0`6`$H`1P"I`*T`#0$)`0D!#0&L`*H`^?_Y_U+_4O\N +M_R__B?^'_R``(@#<`-L`AP&'`8T!CP'9`-4`Y__J_SK_.?_X_OC^(O\C_Z3_ +MH_\@`"``H`"A`!4!%`$[`3L!`@$#`\`\`!1`%`` +M@?^!__'^\O[J_NC^____P(`?0!Z +M`-T`WP#E`.0`@`!^`!,`&``7`!$`30!3`'X`>0"(`(L`9`!B`"``(@#@_][_ +MH_^E_UO_6?]A_V'_QO_(_S@`-@"'`(D`"0$'`8(!@@&+`8X!_`#W`!``%@`8 +M_Q+_<_YX_GW^>?[R_O;^N_^V_YL`H0!6`5`!6@%?`?H`]@"O`+(`BP"(`&(` +M90`Q`"X`V/_;_W/__E__6_\G`"@`PP#&`!X!&@$7`1H! +MS0#+`%X`7@#,_\[_3/]*_R#_(?\\_SO_6?]:_Z3_H_\^`#X`M`"U`*P`J@!0 +M`%(`X?_A_XW_C/^/_X__S?_._P0``0`L`#(`2`!!`.W_]/]N_V?_/O]#_Z3_ +MH?\@`"(`A0"$`+``L`""`((`$@`1`*O_K?]*_TG_%_\7_U[_7?_C_^3_-0`U +M`%4`50"'`(<`F0"9`%``3P#,_\__2?]%_^/^Y_X"__[^@/^$_R$`'0"B`*8` +M$P$1`?X`_``U`#D`:_]G_Q'_%/\>_Q[_>?]W_^W_[O\Q`#(`20!&`#(`-P`` +M`/O_J?^M_Z;_H__>_]__YO_G_[S_O/^T_[/_U__7__+_\?\7`!D`+``K`!H` +M'``;`!<`(``C`/?_]?_"_\/_MO^X_Z7_HO\T_S;_W?[;_O[^`?^4_Y'_6P!? +M`"@!(P%W`7H!*@$I`7H`?`"'_X3_@OZ%_B+^'_Z._I#^6O];__[_^_^-`(\` +MX`#A`.4`X@"?`*,`.@`W`*O_K/]$_T3_+_\N_Q[_'_\I_RK_DO^0_QD`&P!' +M`$0`(@`E``@`!P#X__C_$``1`%,`40!R`'4`1P!#`/S_`0"(_X/_V/[=_G[^ +M>?[B_N;^HO^@_SD`.@"@`*``P0#!`(T`BP`O`#(`Z__J_Y[_G/^2_Y;_Q?_` +M_^+_Y?^^_[[_M_^U_]__X/_1_]+_GO^<_VK_:_]8_UG_@_^!__W__O]\`'X` +MP`"[`-T`Y`"S`*P`\O_Y_R3_'?_._M7^!?_^_EG_8/_&_\#_%0`:`!T`&0`5 +M`!<`&``8`"``'@`R`#4`G`"9`+<`N@!)`$8`J?^K_S[_//_H_NK^N/ZV_M/^ +MUOY(_T3_V__?_Z4`H@!*`4L!?`%]`6@!9@'^`/\``@`#`,_^S?X2_A7^&_X7 +M_J/^I_Z3_Y#_?@"``/L`^P`1`0\!T0#4`$8`0P"__\+_M/^Q_^#_X__3_\__ +MH_^F_Z7_I/^H_ZG_KO^L_]C_V__]__C_#P`5`#D`-`!,`$\`(``?``0``P`< +M`!X`#@`-`*S_K/]N_V[_?_]__[K_NO\.``\`8@!A`&(`8@`H`"H`Y/_A_W[_ +M@O]3_T__L/^S_WT`>P#H`.H`Y0#D`(X`C@#L_^S_/?\\_\_^TO["_K_^_OX! +M_ZG_IO]@`&(`MP"V`-H`VP`-`0L!^P#_`'0`;@#'_\W_4_]._PW_$?\C_R#_ +M@O^$_];_U?\8`!@`0@!#`"\`+P#2_]#_R__/_SP`-@"=`*0`L@"K`(@`C@`2 +M``P`>?]^_QK_%_\0_Q'_4?]1__W_^_^S`+8`[P#L`+$`M0!Y`'0`,``U`+?_ +MLO]3_U?_,?\O_T'_0?^3_Y7_'@`<`($`@0##`,0`Z@#H`+``L@#^__W_:?]I +M_V'_8?^;_YO_W__@_S``+@!$`$4`'@`>`/'_\?_9_]K_SO_-_Q@`&`".`(X` +MG0"=`#,`,P#,_\[_DO^._VC_;?^'_X+_U/_7_S8`-0"'`(<`Q@#'`*<`I0!( +M`$H`%0`2`-?_W/]M_VC_'O\B_UW_6?_G_^O_=0!R`.X`\`#Y`/D`C0"*`.G_ +M[_]6_U#_Y/[G_A#_#__H_^C_M`"V``0!`@$!`0$!N@"[`"T`+`"B_Z/_7O]= +M_T;_1O]F_VC_Q/_!__K__O\6`!$`6P!@`+L`N`"U`+4`7P!@`",`(P#M_^S_ +MQ?_(_\/_OO_&_\K_L_^P_[G_N__'_\C_OO^Z_^?_[?^4`(P`)P$O`4`!.@'6 +M`-H`,@`O`%?_6?^U_K/^F_Z>_N/^X/Z/_Y'_=0!T``?\[_S?_8?]F_ZO_J/\B`"(`J`"K`,\`R@".`),`&0`5`(O_C/\0_Q+_ +M!?\$_S#_+O]&_TG_D_^._RD`,`":`)4`I@"H`(4`A``X`#@`L/^R_U;_4_\Z +M_SS_6?]9_[C_M_\R`#0`/P`[`-/_U_^(_X;_?/]]_Y#_D/_7_];_1@!&`'T` +M?@!5`%0`\/_R_U__6__>_N3^\_[K_E?_7_^T_Z[_!0`(`'P`?`"X`+<`B@"+ +M`#X`/0#5_]7_6?];__G^]O[I_NW^`/_[_FG_;_\B`!P`C`"0`&$`7P`*``P` +MPO_!_W#__P__#?]E_VC_X__?_Q@`'``X`#8`0``_`!H`'0#/ +M_\S_J_^L_YC_F/^1_Y'_I/^D_Y3_D_\B_R/_X_[B_B+_(O^B_Z3_+``H`+L` +MP``5`1`!S@#1`"X`+@!F_V3_L?ZT_F_^:_Z^_L/^)O\@_W'_=__P_^K_9`!I +M`(<`A`!G`&D`2@!(``,`!0"M_ZO_>_]^_VG_9/]A_VC_I?^>_]#_U?^>_YO_ +M2?]*_T3_1?]V_W7_O?^]_SP`/`"K`*P`L@"O`$X`4P"^_[K_"/\+_[O^N?X" +M_P/_?_]^_\__T?\D`"(`6@!;`$``0`#N_^W_PO_$_ZK_J/^;_YS_N/^W_\S_ +MS?_,_\O_]O_X_R,`(0#J_^O_8O]A_Q7_%O_\_OO^%O\7_YG_F?]4`%,`[0#N +M`"H!*0'Y`/H`10!$`'K_>_\=_QW_$?\/___^`_\C_Q[_9_]L_Y/_CO^E_ZK_ +M\/_K_SX`0P";`)<`W0#?`-H`V@!?`%X`YO_F_Y+_E/\T_S'_T/[2_M;^U?XC +M_R3_A?^#_Q(`%0"T`+``"P$.`1(!$0&[`+H`Y/_F_P+___ZA_J/^X?[@_E7_ +M5O_P__#_E0"4`,P`RP"+`(T`(@`@`+C_O/^$_W__J_^O_^[_Z__V__;_^/_[ +M_Q\`'``7`!@`W/_=_[?_M/^K_Z__G/^9_ZG_JO_@_^'_'P`=`&H`;@"N`*H` +MA`"&`/W__/^J_ZO_I/^D_[C_N/_A_^#_-0`V`$8`1``#``8`K/^I_V'_8_]5 +M_U3_Q/_%_VL`:`"\`,$`R@#$`*H`L`!:`%8`QO_(_V__;_]7_U7_;/]O_Z;_ +MH__X__O_-``Q`'@`>P#!`+\`O@"_`$<`1@#=_]W_H_^C_X?_B/^-_XW_YO_E +M_S$`,@!%`$,`-``V`/?_]O^Z_[S_[/_J_V,`9`"Q`+``IP"G`($`@@`A`"$` +MC/^+_RG_*O]!_S__D?^3_R,`(P"B`*(`TP#2`+H`NP"B`*$`50!6`,'_PO]/ +M_TW_-/\U_UO_6_^Z_[K_7P!>`/@`^@!#`4`!+0$Q`:4`H0#'_\K_/O\]_SC_ +M-O]R_W?_P?^Z_R<`+@!V`'$`7`!>`#``,``A`"(`*0`H`%P`6P"4`)8`A0"! +M`#4`.P`2``\``P`!`-3_V/_'_\3_ZO_J_Q``$P`;`!@`.``Y`$4`1@!,`$L` +M4@!2`#$`,@#=_]O_K_^R__G_]_];`%P`IP"F`-\`WP#1`-,`4P!1`*/_I/\L +M_RS_#O\,_W3_>/].`$D`Y`#J`"(!'`$,`1$!S@#)`"X`,P"A_YW_7_]B_V+_ +M8/]^_W[_SO_0_R<`)0!U`'4`T0#2``0!`P'$`,4`7P!?`"8`)0`.``X`[?_O +M__O_^/\)``P`\?_O_Z[_KO]W_WG_:?]G_[__P/^'`(4`,P$U`6P!;`%*`4H! +MZ@#I`"L`*P!V_W;_(?\B_S/_,O]W_W?_Z/_I_T@`1@!Z`'P`NP"Z`.0`Y`"^ +M`+X`7P!@`"@`)P`%``8`X/_>_^[_\/\A`"``/0`^`#$`,``(``<`P__%_[#_ +MKO\4`!<`H0"=`.8`Z@#]`/H`W`#>`&D`:`#3_]/_B_^,_YC_EO_?_^+_0``^ +M`&L`;``]`#T`$0`/``D`"@`#``0``P`"`$P`30"P`*\`X@#B`/8`]0#?`.(` +MFP"7`!X`(P"C_YW_"/\-_[?^M/X-_P__WO_=_ZT`K0!<`5L!N@&]`8,!@`'$ +M`,8`^__Z_V3_9?\S_S+_=/]U_][_WO\;`!D`.@`_`'L`=`"!`(@`=0!P`'H` +M?`"3`)0`=@!T`$(`0@`'``D`V?_5_[K_P/_6_]#_RO_/_\7_P/_U__G_:@!H +M`+D`NP#P`.X`!`$%`<\`S@`\`#X`L?^O_TO_3/]-_TS_P__#_U\`8`"=`)T` +MAP"&`%H`6P`*``H`LO^O_ZC_KO\)``,`9`!I`+0`L`#,`,\`H@"?`%L`7@`M +M`"L`[?_N_X__CO]R_W3_HO^?_^/_YO\J`"<`AP"+`+L`MP"9`)L`20!)`.;_ +MY/^?_Z3_S/_$_S,`/0")`'\`?@"'`&<`80`*``L`EO^9_VC_9?^G_ZC_'0`> +M`)P`F0#>`.(`O0"Z`%0`5@`"````O/^]_V?_:/]C_V'_LO^V_Q``"@!5`%L` +MK0"H`-D`W0#(`,4`>0!Z`/C_]_]O_W'_0_]"_Y#_D/_[__G_20!+`)$`D`"< +M`)X`5P!5``$``@#A_]__]O_X_R,`(@!0`%$`*``G`-#_T/^H_ZC_IO^F_\/_ +MP_\$``4`B@"(`-0`U@#7`-4`GP"A`$H`1P#8_]S_C_^+_U7_6/\N_RS_3O]0 +M_]W_VO]P`',`T`#.`/\```'E`.4`70!<`+7_MO])_TK_1?]#_Z[_K_]0`$\` +MLP"T`*8`I@!C`&,`"0`*`)__G/]A_V3_FO^8__7_]?]#`$4`;0!K`'``<0!4 +M`%,`/0`_`"D`)0#8_]W_I/^@_ZS_K?_8_]K__O_Z_SL`/P""`'\`A0"'`%,` +M40`/`!$`W__<_][_XO\G`"(`2P!0`"8`(@#=_^#_EO^4_U;_5O]/_T__P/_` +M_W$`?]&_T/_@/^!_Q<`%P"]`+T`-0$T`34!-P&_`+P`'``A`*W_ +MJ/]5_UC_0?\__XK_BO_Y__S_2@!'`&T`;P"-`(L`B`")`&D`:0!#`$,`_/_\ +M_\S_S?_P_^[_-@`X`&L`:@!J`&H`7`!>``4```"!_XG_/?\U_U#_5__._\;_ +M;`!T`/,`[`#[``(!S@#(`((`A0`P`"X`TO_5_\/_O__1_]7_V/_5_[C_N?^M +M_ZW_M_^W_^K_ZO](`$@`BP"+`)X`G0"T`+8`M0"S`'8`>``:`!<`R?_-_W__ +M?/\W_SG_)_\F_W/__][_G?^>_W?_<_]N_W3_TO_-_T<`2@"3 +M`)$`B`"(`&0`90`<`!L`P/_#_Z3_G__=_^+_00`\`'4`>@!T`'$`&``;`++_ +MK/][_X3_A_]]_Z'_JO_O_^G_2@!,`&T`;P!:`%8`,``T``L`"`#F_^G_PO^_ +M_ZO_KO^9_Y?_PO_"_QP`'0!B`&(`<`!O`&,`90`R`"X`R?_-_WW_>_]X_WK_ +MO/^X_P$`!@`Z`#4`%0`:`-#_S?^B_Z+_K/^N_\[_R_\5`!H`?0!X`)X`H0!A +M`&``\/_P_X?_A_]%_T?_1_]$_V___N#^./\V_];_U_^)`(H`WP#=`+8`N`!;`%H`Z__K +M_WC_>?]'_T7_:/]K_[+_K__*_\W_S/_)_[?_N/^M_Z__S?_*__[_`0`9`!8` +M%``6`!\`'@`!``(`T/_._Z/_I_^J_Z7_I/^H_YC_EO^9_YK_L/^O_^C_Z_\T +M`"X`1@!-`!@`$P#&_\?_AO^)_UC_4O]3_UO_M_^O_RH`,`!F`&(`2`!+`-7_ +MU/]7_U7_!_\,_RO_)/]S_WK_Y?_@_V,`9@"P`*\`F`"7`$,`1`#L_^S_C_^1 +M_S;_,__X_OG^Y?[D_@K_#?][_W?_]?_Z_T$`/`!=`&``7@!<`"T`+@#G_^;_ +MT__6__'_[?___P(`[/_J_Y3_E?\:_QG_S?[._MW^W/XU_S?_M?^S_U<`5@#' +M`,H`R@#&`&4`:@#[__;_C_^3_TO_2?\F_R;_)/\D_SC_.?^._XW_\/_R_RX` +M*@!%`$H`4P!-`!H`(0#*_\3_?_^#_WS_>?^>_Z#_R/_'_]#_T?^?_Y[_8/]A +M_U?_5O]Y_WK_OO^^_RX`+@"5`)4`F`"7`$``0@#+_\G_;/]O_U#_3?]L_VW_ +MG_^?_[[_O__:_]?_X/_D_[W_NO^1_Y'_D/^3_[?_L__)_\S_]__V_S$`,0!V +M`'8`H`"@`(D`B0`1`!,`9?]B_^'^X_Z<_IK^M_ZY_D'_0/\>`"``U@#3``T! +M#P'B`.$`8@!B`-C_V?^`_X#_4_]1_S[_0?]<_UK_C?^-_[/_M/_(_\;_```" +M`$0`10!L`&D`8@!D`$$`00`3`!$`[/_O_]7_T_^K_ZO_;O]Q_US_5O]9_U[_ +M=_]U_\7_Q/]"`$4`M`"P`,(`Q0!]`'P`^?_X_X'_@O]5_U7_?_]^_\3_QO\@ +M`!X`:0!J`%L`6P`&``4`J?^J_XW_C?^1_Y'_N/^X_^3_X_\(``H`+P`M`$X` +M4`!+`$H`'P`=`/'_]?_3_]#_IO^I_YO_F/_)_\O_*0`G`&4`9P!M`&P`)@`F +M`,#_P/]]_WW_?_]__['_L?\)``@`9P!I`(``?@`W`#D`S/_)_X;_BO^!_W[_ +MN?^\_QL`%P!D`&<`C0"*`)(`E@!D`&(``````*__K_]__W[_8?]B_UC_6/^0 +M_Y#__?_]_W4`=0"^`+T`O`"]`&,`8@#Y__O_J_^I_WW_?_^1_X[_XO_E_S\` +M/@!2`%$`+``N````_?_M__#_!0`"`"T`,0!!`#P`)P`L`/__^_^Y_[O_?^1_Y?_X__=_SP`0`!^`'L`@`"" +M`%T`7``>`!X`V?_:_\G_QO_J_^[_$``,`!@`'``O`"L`1@!*`%$`3@!#`$4` +M%P`7`-__W/^M_[+_L/^K_\C_S?\%``(`4P!3`(L`BP!P`'$`(@`@`.3_Z/_? +M_]K__/\!`$4`0`!J`&X`8`!=`"0`)P#,_\O_>_]X_V/_9_^U_[+_(``B`'(` +M`!Z`*(`GP"3`)0`+0`O`+G_M?]^_X+_K?^J__G_^_]'`$8`=`!U +M`%H`6``-``X`O/^]_X'_@/]]_WW_PO_#_R(`(`!@`&,`@`!\`)X`H@">`)L` +M90!G`!``$`"W_[7_;/]N_U?_5?]\_X#_PO^]_Q<`'`!E`&``:@!N`"X`+0#Z +M__G__?_^_R@`)P!?`&``@P"#`&4`9``*``L`H?^A_T/_0O\D_R7_9O]F__3_ +M\_]B`&4`IP"C`,0`Q@"Z`+H`>`!X`"``(0"^_[S_9O]F_SG_.O]#_T+_>O]\ +M_]/_T?]7`%<`J@"J`*4`I0!M`&X`0``^`!P`'@`9`!<`&0`<`!(`#P#9_]O_ +MG/^9_T[_4?\P_R[_:/]I__/_\_]W`'8`L0"R`+<`M0!\`'X`*P`K`-__WO_` +M_\'_M_^V_];_U_______!0`$`/S__O_^__S_^__\_]7_U/^:_YO_C_^/_ZK_ +MJO_]__S_80!C`+T`NP#"`,,`B@")``$``P!@_UW_^_X`_R3_'O^'_XW_#@`( +M`&P`<@"M`*<`D0"7`%@`4@`+`!``S__,_ZO_K/^E_Z7_EO^5_X#_@O^<_YK_ +MX/_A_Q,`$0`U`#D`3`!)`$X`3P!#`$(`0@!#`#0`-``;`!P`\O_P_[7_M?]7 +M_UC_'O\>_T;_1?^W_[G_,P`P`)$`E`"Q`*X`=P!Z`!4`$0"Y_[__A/]__Y'_ +ME/_=_]O_%0`6`!P`'``%``4`^O_Z_^+_XO_(_\C_R/_(_\[_S?_:_]S_]/_R +M_PD`"@`4`!,`)``E`"$`(0#G_^C_G/^9_WW_@/^I_Z;_Z__P_S\`.@!P`',` +M7`!9``X`$@"S_[#_9?]H_VC_8_^[_\#_+0`J`%H`7`!1`%``)P`E`-7_V?^8 +M_Y/_=/]Z_X__B?^R_[?_]O_R_R@`*@`Y`#H`1@!#`$L`4``A`!L`Q?_+_X7_ +M?_]K_W'_BO^$_\[_T_\F`",`5@!7`$L`2P`6`!4`M?^U_VS_;O]W_W7_MO^X +M_^__Z_\!``8`#@`)`/;__/_J_^7_\?_S_Q@`&``Y`#@`4`!3`"\`*@#/_]3_ +M<_]O_TW_4/]&_T7_5O]4_X3_A__A_][_*0`L`'D`=@"I`*L`FP":`%H`6@#L +M_^W_9_]F__?^^/[V_O7^9/]E__#_[O]@`&,`E@"4`'(`_QS_H_^D_S,`-`"9`)@` +MHP"C`&@`:0`=`!L`U/_7_VW_:O\9_QO_"O\'_QK_(/]@_UG_L/^V_P,`_O\= +M`"``#``,`-7_T_]]_W__7O]>_Y7_D__I_^S_$@`/`!L`'0#T__3_G/^:_T[_ +M4/\Y_SC_5?]5_X[_C__:_]?_ZO_P_]/_RO^O_[G_P_^Y_[O_Q?_!_[C_QO_. +M_\G_PO^H_ZW_CO^+_W__@?]L_VO_??]^_Y;_E?^?_Y__M_^W_P<`!P!2`%,` +M:`!F`#<`.@#<_]C_/_]#_[_^N_Z-_I'^P_Z__DS_4/\*``<`C0"/`)T`G0!J +M`&<`'0`B`,/_OO]X_WS_9?]C_W'__US_2O]+ +M_W7_=?_F_^7_0P!$`$T`30`)``@`N_^]_V?_9/\Q_S3_4?]._YG_G?_H_^3_ +M&0`;`"8`)@#[__G_U?_9_]/_S__!_\+_GO^@_Z+_GO^W_[S_T/_,_^?_Z?\' +M``8`!``$`.'_X?^V_[?_B?^(_W/_<_^M_ZW_!``#`"0`)@`3`!(`Z/_H_ZS_ +MK/]]_WS_C_^1_]C_U_\D`"4`60!8`%,`4P`!``$`F_^<_WW_>_]Z_W[_G/^7 +M_]?_VO\C`"(`1P!&`$(`1``N`"P`[O_O_ZO_J_]Q_W'_5/]3_TO_3?^O_ZW_ +M10!'`+H`N0#B`.,`P0"_`$T`4`"H_Z;_-O\V_Q/_%?]&_T/_K_^Q_R$`(0!* +M`$@`/``_`#D`-@`S`#4`'@`=`!@`&0`?`!X``P`$`-C_U_^V_[?_JO^K_[/_ +MK__H_^W_'``7`"4`*0`^`#P`9`!E`'0``%T`>0![`*,`H@#<`-P`[`#L`*`` +MH``L`"P`UO_5_Y__H?^D_Z+_```!`'8`=P#%`,(`T@#5`*@`IP!%`$0`__\" +M`/[_^O\*``T`'0`;`$$`0@!S`'0`=@!S`'@`?`""`'X`=0!Z`%\`60!*`$\` +M,@`N`"(`)0!%`$0`>`!X`&X`;0`Y`#L`%0`3`.;_Z/_;_]G_$``1`'X`?P#/ +M`,X`^@#[`-,`T@!E`&4`\O_T_]?_U?_I_^K_%@`6`&@`9@"P`+0`NP"V`(T` +MD@!B`%X`+0`O`/___O_Z__O__/_Z_P$``P!#`$(`FP":`,H`S0#%`,$`IP"J +M`&(`8``*``L`X/_@__S_^_\T`#8`?@!\`+0`M0"2`)(`3@!-`",`)0`>`!L` +M%P`;`#L`-@!B`&<`8P!>`#\`0P`M`"P`)P`G`$0`0P"'`(D`P0"]`*X`M`"9 +M`)0`?`!_`#\`/0#Z__S_VO_7_]'_U?_0_\S__O\!`%4`4P"S`+4`#`$*`3@! +M.0'R`/,`6`!6`,[_T?^!_WW_8_]G_[+_K_\]`#X`L@"S`.<`Y0#D`.<`J@"G +M`&(`8@!*`$L`3`!,`#0`,P`A`",`*``E`!L`'0#^__W_"@`*`"(`(@`J`"H` +M00!!`%\`7P!T`'0`H0"B`-T`V0#:`.``D@",`#@`/@#V__'_M?^Y_[C_M/_\ +M_P``4`!+`'(`>`![`'4`30!2`!0`$``:`!T`5`!1`(,`A@"H`*0`O@##`*`` +MG0!0`%$`#P`.`//_\__B_^/_]__W_QH`&@`R`#(`50!4`)P`G`"^`,``J`"F +M`'<`>0!,`$D`^__^_]K_V/_O__'_)0`C`%0`5`!S`'0`:0!H`#$`,@`L`"P` +M90!B`)\`HP#*`,@`[`#L`+<`N``_`#X`N_^\_VK_:?]2_U/_E/^3_PL`#0!T +M`'(`Q0#%`!,!$P$P`3`!^P#\`*L`JP!6`%0`Y?_G_Y3_DO^-_X[_PO_$_QX` +M&0!]`(0`M0"N`(<`C`!3`%``/@`_`"\`+@`\`#\`=P!T`)T`GP"+`(@`3@!2 +M`!\`'`#S__;__O_\_SD`.`!=`&``70!:`&0`9@!E`&4`2@!'`#T`0@!9`%0` +M6P!>`%8`50!3`%,`1`!#`#$`-``_`#L`-@`Y``P`"P#B_^'_]?_W_R$`'P!< +M`%X`Q0##``X!$`$``?X`I0"G`"4`)`"=_YW_8O]C_Y?_EO_L_^W_/``[`(H` +MBP"X`+<`F`":`&T`:@!.`%$`1`!!`"4`*``I`"8`&@`=`!4`$@`N`#$`6@!7 +M`$\`4@`[`#@`+0`O`",`(P`1`!``,@`T`&L`9P!X`'P`;`!J`"\`,`#/_\__ +MD_^2_[K_N_\-``P`7@!?`*T`K`#5`-8`H`"@`$L`20`"``4`XO_?_];_V?_W +M__7_$``1``@`"``9`!@`-@`V`#8`-P`E`"4`+P`N`!\`(``$``(`__\!`"`` +M'@`^`$``6P!9`%\`8``M`"T`[?_L_^C_Z?\&``<`'P`<`$4`2`!@`%T`*``L +M`-C_U/^5_YG_BO^&_Z[_L?\6`!4`=`!T`(<`A@!_`((`8@!>`!P`(0#<_]?_ +MQO_*_]K_U__=_]___O_]_R<`)@`Y`#P`0@`^`#P`/P#Q__#_D_^1_V7_:?^# +M_W[_NO^^_RX`*P"H`*H`W0#=`*X`K@!6`%,`U/_8_X#_>_]L_W/_H?^;_[7_ +MNO_=_]?_`0`(`!(`"P`#``H`*0`C`$<`2P!*`$D`-@`U``T`#P#3_\__I/^H +M_ZS_JO^M_Z[_IO^E_\'_PO_W__;_&``:`$P`2`"'`(H`CP"/`%<`5@#[__W_ +MC?^*_R[_,/\\_SO_D_^4_^7_Y/\F`"<`5`!2`#\`0@#X__7_UO_8_]W_W/_Z +M__G_(``B`$(`0``D`"<`^O_V_]W_X?^[_[?_BO^._X+_?_^C_Z7_MO^V_^'_ +MX/\F`"8`70!>`&D`9P!,`%```P#__Y/_EO]O_VS_B/^+_[S_NO_S__3_+P`N +M`#$`,P#Q_^[_M?^X_Z__K?_"_\+_!0`'`$H`1P!1`%,`&P`<`.K_Y_^G_ZO_ +M7O]:_TC_2_]O_VW_E/^5_\O_S/\=`!L`:P!M`)8`E`"M`*P`=@!Z``8``@"/ +M_Y+_6_]9_SC_.?]*_TG_B?^*_\7_Q/_._\__S/_,_]__W__W__;_.``Y`($` +M@0"%`(4`0@!"`.O_[/^4_Y'_+O\R_R/_'_]C_V?_NO^X__[__?\U`#<`/P`] +M`"``(@`&``4`XO_B_YW_G?]T_W3_AO^'_ZK_J/_4_];_(``?`$\`3P`R`#,` +M[/_K_Y;_EO]6_U?_6_]9_[G_O?\;`!8`10!)`%<`5``K`"T`P__"_V?_:/]2 +M_U'_8_]B_XC_B__)_\;_[__Q__W__/\:`!H`-``T`"``(0#[__K_ZO_J_\G_ +MRO^6_Y3_E?^7_ZO_J_^T_[/_K_^O_ZK_J_^2_Y'_F_^=_^7_X?\N`#(`4@!. +M`%(`5P`L`"<`N/^[_T[_3/\Q_S/_5?]3_Z/_I?\+``@`0@!&`#<`,P`2`!8` +M]/_P_[G_O/^7_Y7_GO^@_[3_LO^E_Z;_M_^W_\W_S?_<_]S_YO_F_^S_Z__+ +M_\W_J/^F_\+_Q/_P_^[_!@`(`!\`'``B`"8`X/_=_X#_@_]2_TW_3_]5_Y/_ +MC?_U__O_2@!%`#T`00`-``H`R?_)_W?_>/]!_T#_9/]G_ZW_JO_I_^G_'``= +M`$``0``\`#P`(0`A``,`!`#`_[[_;?]O_U?_5?]G_VK_@_^`_[__P?\0``\` +M'@`>`/3_]/_`_\'_EO^3_X?_C/_(_\+_#P`4`"X`*P`F`"@`$0`0`-7_U?^8 +M_YC_E/^5_[?_M__*_\C_R__._[S_NO^7_Y?_?_^"_ZS_I?_&_\__[?_E_Q@` +M'P!&`$``-``X`!X`&P`&``H`V__7_X[_D?]:_U?_-?\Y_TK_1O^F_ZK_-@`S +M`(8`A@"<`)\`>@!V``0`!P!N_VS_'O\>_R/_)O]F_V'_O__%_R,`'``V`#T` +M/``W`#0`-P`G`"<`_O_]__?_^/_V__3_U/_7_[3_LO^O_['_L/^O_ZS_JO^S +M_[;_L_^Q_Z;_I__/_]#_(@`?`%D`70!Y`'0`?`"!`$D`1`#/_]3_@?]^_V3_ +M9?^%_X3_Q__(_Q0`$P`C`",`!0`'`/7_\O_:_]W_R__)_^O_Z_\H`"@`/0`_ +M`"\`+0`:`!P`]O_T_\[_S__`_[[_M/^Y_Z'_F_^N_[3_[O_J_R(`(P!/`$\` +MA0"%`(8`A@!"`$(`\/_Q_[7_L_^9_YO_KO^L_^G_[/_\__G_X?_C_]'_T/_% +M_\3_PO_$______]S`'$`O`#``-,`S@"D`*<`0`!``,C_QO]V_WK_6?]4_T?_ +M3/]V_W'_U?_:_S4`,`!S`'<`M@"T`,P`S`",`(T`+``I`,G_SO]\_WC_>_]] +M_\G_R/\?`!\`3P!0`&P`;`!B`%\`&@`?`/'_[/_^_P(`'``:`"X`+0`T`#<` +M&P`9`.3_Y/_4_]7_Y/_C__/_]/\7`!8`20!)`%T`7P!(`$0`/@!$`$<`00`T +M`#@`*P`I`!P`'0`"``$`]/_V_Q\`'@`U`#0`+``N`!@`%0#[__[_NO^Z_Z'_ +MGO_2_];_/``W`)T`H@#T`/(`[0#L`)(`DP`F`"4`UO_7_Y?_F/^0_X[_S__0 +M_Q,`$P`Q`"\`10!+`&,`6P!6`%X`70!6`$L`4``N`"P`!``%`!,`$0`B`"4` +M+@`I`#T`1`!8`%(`,``T``(```#W__?_$P`4`$``/P"'`(@`GP">`&<`:``; +M`!H`W/_=_ZG_J/^H_ZC__?_]_UX`70"2`)0`G`";`(P`C`!3`%0`+``J`!<` +M&@`&``,`Z__N_P0``0`@`",`,@`P`%``4@!]`'L`>@!\`$\`3``C`"8`_?_[ +M_^__\?\4`!$`/0!``#\`/``N`#``)@`G`!,`#P`(``T`30!)`)8`F`"V`+8` +MH@"@`&8`:@`$``$`NO^\_[;_M/_3_]3_^__[_T4`10!Y`'L`>0!T`%<`70!7 +M`%(`-``V`!``$@`#`````P`%`!0`$P!6`%8`FP":`*8`J@"&`(``1`!+`.S_ +MYO^'_XG_@/^"_\G_Q?\D`"@`A@"#`+4`MP";`)H`6@!:`"P`+0`-``H`Z/_M +M_P,`_?\:`!\`'0`:``T`#@`E`"4`0P!"`%L`7`!W`'<`=@!U`$@`2@`H`"0` +M$@`7`.__ZO_0_]7_UO_3_]7_U/_'_\S_\?_K_T$`1@"=`)D`XP#F``(!`0'` +M`,$`2`!&`.#_X?^7_Y?_<_]T_Y__G?_W__C_*P`K`#<`-@`^`$$`-0`R`#<` +M-P!)`$T`;0!E`%T`9P!=`%4`6P!?`#D`.0`0``T``0`%`/O_^/_4_]7_RO_+ +M_^K_Y_\5`!H`9@!A`*(`I@"I`*4`:`!K`"4`)`#J_^K_L_^U_\[_RO\:`!X` +M5@!4`&$`8`!(`$T`(@`;`.K_\/_U__+_'P`?`$$`0P!M`&L`BP"+`'D`>@!" +M`$(`&@`9`/W__O_)_\C_J_^J_Z?_J__$_[__!``(`&X`:P"Y`+L`Q`##`+4` +MM0!_`'\`'@`?`-[_W/_<_]__\?_N__G_^O___P``\__Q_]S_W_____S_/P!# +M`((`?`"E`*T`R`#``(X`E``E`"$`R__._Z;_I?^<_YO_Q?_'_PT`"0!'`$T` +M?@!Y`+$`M`"]`+P`D`"/`%@`60`:`!H`R/_(_YO_G/^\_[G_!0`(`$P`20"! +M`(0`?@!\`#P`/0`&``8`^/_X__[__/\S`#8`A@""`+0`N0"<`)<`9`!I`#8` +M,`#]_P,`Z/_D_^__\/_M_^__[O_J_PT`$0`O`"P`.0`\`%L`6`!Y`'P`<@!O +M`$\`4@!#`$$`2`!)`$\`3@!@`&$`4@!3`!D`%0#>_^7_U?_-_]'_V/\$`/__ +M7`!=`*<`J0"=`)L`:0!K`"4`(P#>_]__S__-__3_^/\M`"D`40!5`(``>P"% +M`(D`9`!B`$P`3@!*`$<`.``[`!4`$0`%``L`!@#___W_!``G`"$`0`!$`#H` +M.``M`"T`)@`H`"8`(P`F`"@`70!<`)P`G0">`)X`=`!Q`",`*`#1_\O_G_^E +M_\[_RO\2`!0`7P!>`*4`I@"Z`+D`?0!^`"@`*`#\__O_U/_4_\G_R__@_][_ +M"``(`"L`+@!J`&4`G`"B`*0`G@!_`(,`80!?`!D`&P#;_]G_T__5_P<`!0`U +M`#<`5@!4`%P`70`S`#,`!``$``(``P`9`!<`-@`W`&<`9P""`((`8@!B`"$` +M(0`0``\`!P`(`!\`'P!'`$<`7P!>`%$`4@`\`#L`,0`R`!``#P`!``$`&``9 +M`"X`+0`H`"@`.P`[`%\`7P!P`'``:P!L`%,`40`"``,`MO^W_Z'_GO_&_\O_ +M#P`+`(H`B@#Q`/8`"P$$`;H`P`!9`%8`[?_M_Z'_H_^5_Y/_NO^[_]3_T_\# +M``0`+0`M`$$`/P!&`$@`<`!O`(P`C0"``'\`;@!N`%X`7@!"`$(`+0`O`!\` +M'`#X__O_P?^^_ZW_K_^W_[?_U?_3_R$`)0"0`(P`P`##`*T`JP!M`&X`'``; +M`,__T/_-_\W_\/_O_Q4`%P`X`#0`3P!4`$$`.P`+`!,`$P`+`"$`)P`P`"L` +M-@`Z`#X`/``M`"\`'0`:`!P`'@`0``\`[__P_^G_Z/_I_^O_Z?_D_P``!P!8 +M`%$`B@"/`)@`E@!Z`'H`/@`^`.W_[?_3_]/_Y?_E____```;`!@`+0`P``H` +M"`##_\/_I?^G_\C_Q?\"``4`60!8`*<`I0"[`+\`H0"<`&X`=``E`!\`OO_$ +M_Y'_C/]\_X#_@/]]_Y__H?\&``0`:P!M`+0`L@#"`,0`G0":`"D`+@#7_]#_ +MG_^F_YO_E?^Y_[W_!P`&`#(`,0`E`"<`#@`,``H`#``1``\`*P`L`%8`5@!< +M`%P`.@`Z`!H`&@#G_^?_NO^Z_[#_L/_7_]?_Y__F_^[_\?\,``@`'P`C`"T` +M*0`U`#@`,0`O``H`"P#M_^W_]O_V______\<`!P`3@!.`&0`8P`C`"8`UO_1 +M_XK_C_]P_V[_BO^(_^;_ZO]``#L`:0!N`'P`>`!7`%L`!P`#`-'_U/_0_\[_ +MW?_>_^;_YO\#``0`%P`4`!X`(@`I`"8`,``Q``L`"P#K_^K_U?_7_\#_OO^Y +M_[K_Y?_D_QL`'@`I`"8`$``1`.__[__)_\G_O__`__[__O\^`#T`:P!K`'@` +M>0!5`%,`[O_Q_Y+_CO]V_WK_A_^#_ZO_KO_J_^G_'0`<`"H`+``T`#``,P`X +M`!8`$P#Z__S_[?_K_]K_W/_!_[__U/_7_PT`"@`J`"P`/``[`"\`+P#__P`` +MT/_._\?_RO_8_]7_W__A_____?\%``<`V?_7_ZC_J_^J_Z;_VO_>_Q<`%`!D +M`&4`BP"+`&\`;P`N`"X`[/_L_Y[_G?]X_WG_D?^1_[S_N__6_]C__/_Z_RX` +M,``\`#H`-0`V`!\`'@#F_^G_MO^S_ZG_J__*_\C_\O_S_S<`-P!L`&T`3P!- +M`/W__O^N_Z__@_^`_W[_@O_$_\#_$@`7`$D`0P!4`%H`4@!,`!4`&@#>_]S_ +MT/_0_]/_T__"_\+_MO^V_[[_P/_*_\;_X__G_Q8`$@`Q`#4`.@`W`$0`10`Z +M`#H`(0`@``@`"@`*``@`Y?_F_Z/_HO]N_V__6?]9_VO_:O_`_\'_,@`R`(,` +M@0";`)\`D@"-`#0`-P#5_]3_GO^?_ZC_I_^N_Z__SO_,_^K_Z__M_^[_Z__I +M__/_]?_\__O_]?_U_P$``@`-``L`]/_V_^K_Z?_V__?__/_[_]O_W/_#_\+_ +MH_^D_Y3_E/^]_[O_!P`+`$X`2@!Q`'0`>`!W`#<`-0#"_\;_?O]Z_W#_<_^( +M_X?_N/^X__'_\O_\__K_[/_O_^#_W?_3_]7_S__.__#_[_\=`"$`+@`I`"8` +M*@`S`#``*0`J``(``@#2_]3_FO^5_U'_6/\__SG_7?]A_Y__G?_P__'_4`!/ +M`'0`=@!1`$X`&@`>`/3_[__5_]K_T/_,_]K_W?_)_\C_I/^C_XG_BO^"_X'_ +MB/^)_\[_SO\D`"0`60!9`$T`3``R`#0`^O_W_[#_L_^$_X+__\;_SO^^_[C_O__#_[G_M_^S_[+_S/_/__K_]O_S__C_ +MY?_@_\S_S_^V_[7_L_^R_]K_W?\%``$`"@`/``P`!@#?_^7_G_^9_W/_>?^< +M_Y?_V/_<_Q4`$@`\`#X`/0`[`/[_``#%_\/_E?^7_WK_>?]\_WO_IO^H_\;_ +MP__%_\?_XO_A_P$``@`1``\``P`'``0`_O_;_^'_PO^\_[S_PO_2_\W_T__8 +M_^'_V__9_][_JO^H_WS_>_]]_X#_KO^K_]S_W?\6`!@`(P`@`/;_^/^P_Z__ +MA?^%_V;_9O]^_X#_UO_2_QL`(``X`#(`+@`T`!X`&0#B_^;_I/^A_W3_=?]* +M_TO_,_\Q_T__4?^*_XG_R/_(_Q(`$@!0`%``0@!"`/_____$_\7_GO^<_Y'_ +MD_^G_Z;_S/_,_\7_Q?^T_[3_F_^;_WW_?_]W_W/_I_^J_^#_W__N_^[_Y/_F +M_]/_S_^L_Z__DO^/_Y__H_^U_[/_OO^^_]/_TO_3_]7_O_^\_ZW_LO_`_[K_ +ML?^U_Y3_D_^,_XS_EO^6_ZC_J/_:_]G_!P`)``<`!0#9_]O_FO^7_T7_2?\+ +M_P;_*?\N_X[_B?_H_^W_,P`O`%0`5@`P`"\`Z__M_[#_K?]__X/_8/]:_UO_ +M8O]P_VO_8O]E_W'_/]\_VG_9_^:_YO_V__9__K__O\+``4` +MXO_H_Y3_D?]7_U?_2O]+_UO_6_^2_Y'_V/_9__C_]__B_^+_S/_-_[S_O/^D +M_Z/_E?^5_YO_G/^<_YO_FO^:_['_L__+_\?_RO_/_]W_V?_4_];_K_^M_X+_ +MA/^1_Y#_LO^S_]#_S__M_^W_Y__H_Z[_K?]P_W'_5/]4_U?_5?^#_X?_Y/_A +M_R$`(@`>`!\`___\_]'_T_^<_YW_??][_X;_B/^;_YG_J_^L_\G_R/_A_^+_ +MW?_=_^'_X?_D_^3_NO^Z_X/_@?]H_VS_;_]K_X3_B/^^_[K_\O_U_P(```#W +M__C_[O_N_]7_U?_/_\W_Z?_M_P<``P#<_]__IO^D_V[_;_\]_SS_-_\Y_W/_ +M_^&_X?_H_^A +M_\/_Q?_U__3_'@`?`!L`&@#P_^__P?_#_X/_@O];_US_@_^"_]#_T/\&``4` +M'0`A`!P`%@#>_^3_JO^E_Y/_E_^P_ZW_R?_+_P4``P`B`"0`$``1`.S_Z/_? +M_^/_T/_-_[;_M_^N_Z__K?^L_YS_G?^C_Z'_O/^__];_TO_I_^W_#@`,`!$` +M$`#[__W_"``'`"@`*``K`"P`&P`:`/3_]/^E_Z?_5O]3_S7_-_]2_U/_E/^0 +M__W_`P!B`%L`<@!X`%``3``J`"T`]?_Q_\+_QO^P_ZW_M_^Y_ZS_K/^P_ZW_ +MO__$_\S_R/_7_]O_`P#^_Q``%``)``8`_O\!`!0`$@`-``\`"0`&`/;_^O_- +M_\C_@O^'_V3_8/]B_V;_C_^+_^G_[/]<`%H`@0""`&D`:0`R`#(`[?_K_Z;_ +MJ?^9_Y?_L?^Q_\O_S?_B_]__[__Q_][_WO^__[[_QO_'_]#_S__&_\?_S?_- +M_^G_Z?\'``8`&0`;`#X`.P`Y`#T`#@`)`-3_VO^B_YS_;_]T_W__?/_"_\+_ +M__\``!,`$P`=`!T`!0`%`-S_W/_._\W_X/_@_^W_[O_P_^__[O_P_\3_PO^4 +M_Y7_D?^/_[[_P?_J_^C_&P`<`%(`4@!6`%4`.@`]`!@`%`#?_^/_H/^<_VO_ +M;O];_UK_2_],_W3_=/_;_]K_.0`Z`&\`;0!V`'D`5P!4`/[_``"[_[O_JO^H +M_['_L__7_];_"@`)``P`#@#>_]S_N?^[_ZW_J_^D_Z;_M_^V_^+_XO_]__W_ +M!``#``H`#0`2``\`^O_\_____?_Y__O_X/_?_\3_Q/_5_]7_\/_P__W__O\( +M``<``@`!`,O_SO^G_Z3_H?^D_[#_K?_D_^7_)P`G`$\`4``D`",`]?_U_\?_ +MQ_^N_Z[_L/^R_^O_Y_\7`!L`*0`F`"L`+@`3`!``Y/_F_]3_TO_9_]S_U?_4 +M_\/_P?_4_];_Z__I__+_]?\/``P`'``>`!``#@#Q__/_X__A_]?_V?_9_]?_ +M#@`0`#$`,``C`",`_/_\_]O_V_^V_[?_N/^W_^K_Z_\J`"@`10!'`$D`1P`A +M`",`R__)_Y7_E_^6_Y7_K/^K_\W_S_\-``P`1P!&`%L`7@!I`&8`80!C`#(` +M,0#S__/_O_^^_XW_D/]Y_W?_K?^M_P$``0`S`#,`2@!+`$<`10`:`!T`Z?_E +M_]W_X?_L_^G_`````!<`&@`?`!H`[__U_]/_S?_=_^#_]__W_Q0`$@`X`#L` +M3P!-`"\`+@`,``\`\O_N_\[_T__&_\'_S/_/_];_U?_,_\S_Z?_J_Q<`%P`P +M`"T`,@`W`#@`,P`3`!@`[/_H_^G_Z_\#``$`(``C`$T`2P!2`%(`$P`4`+3_ +MLO]W_WK_8O]@_W+_<__"_\'_'0`>`%L`60!R`'<`>0!R`%$`6``I`",`%P`: +M`/G_^?_'_\7_LO^V_[K_M?^]_\+_SO_*__/_]?\"``,`_O_Z_P(`"0`6``X` +M&@`@`#X`.P!4`%0`,0`T`.G_Y/^H_ZW_A?^!_WS_?_^X_[;_%``6`%0`4@!R +M`'4`;P!K`#(`-@#T__+_T__4_\G_R?^R_['_K_^P_\+_PO_-_\S_V/_;_PH` +M!0`<`"$`)@`C`"H`*P`A`"$`"0`)``@`"``E`"4`'@`=`/;_^/_3_]#_J?^N +M_Y+_C/^?_Z3_Y?_B_Q@`&@`]`#P`2`!'`!@`&P#@_]S_T/_3_^;_YO____O_ +M%P`>`#4`+0`=`"0`\__N_]'_U/^__[W_L_^T_\[_S__W__;_"0`(`"<`*@!9 +M`%0`8`!F`$$`/0`+``P`Q__(_WG_>/]=_US_A?^(_\[_R_\F`"D`?0!Z`(X` +MD`!A`&``,P`S`!$`$@#Q__#_X/_@_^S_[/_?_]__P?_#_[G_MO^V_[C_QO_% +M__3_]/\O`#$`/``Y`#@`.@!#`$(`-0`W`!X`&@`$``D``0#\_]?_V__1_]#_ +MX?_?__C__/\:`!4`0`!%`#\`.P#\____RO_(_Z__L/^O_Z[_U?_6_R0`(P!8 +M`%H`7@!;`$D`3``?`!T`[/_L_]W_X/\!`/S_!@`+``X`"0`4`!@`%``1`/G_ +M^__T__/_^?_[_^K_YO_@_^3_[?_I_^[_\?\"``(`,P`Q`%$`4@`Z`#L`%@`5 +M`/S__/_<_]W_XO_?_P<`#``_`#L`/@!``#(`,@```/[_K_^R_Y?_D_^S_[C_ +MY/_@_P\`$0!!`$``50!5`#<`.0`;`!D`!@`'`.W_[/_=_][_Z?_I_^?_Z/_J +M_^C_$0`1`#@`.0`\`#P`(@`B``T`#0#3_]+_K/^M_[+_L?_4_]7_\O_Q_Q8` +M&``F`"0`"``)`/'_\/\-``\`(P`@`#0`-P!!`#X`*@`M`.C_YO^E_Z?_BO^& +M_X3_B/^N_ZW__/_[_S$`,P!!`#\`30!,`$D`3``=`!L`Y/_E_\'_P?^6_Y3_ +M>O]\_YC_EO_)_\S_#0`+`$4`1@!H`&@`.@`W`/'_]__1_\S_M_^Z_[[_OO_F +M_^/_"@`.````_?_>_]__N_^\_Y?_E?^2_Y/_P?_!__+_\O\#``,`&P`;`"@` +M)P`A`"0`#P`*`!$`%@#Y__;_U__8_[O_O/^M_ZG_E?^:_ZO_I__*_\[_R?_& +M_[O_N__"_\/_U?_5_^__[O\>`"``4@!/`$@`3``:`!<`UO_7_X#_@/]-_TW_ +M:/]I_Z3_HO_4_];_!@`%`"<`)P`6`!@`\__O_]__XO_'_\;_K_^N_Z#_HO^= +M_YO_B?^*_YW_G?_*_\K_YO_F_^G_Z?_[__K_^__\__#_\?_\__K_"@`,`/__ +M_/_3_]?_H?^=_T__4O\8_Q?_,/\O_WS_?O_2_\__'P`A`%@`6`!'`$<`#0`, +M`-K_W/^V_[/_E?^8_Z;_I/^O_Z__JO^L_Z7_I/^\_[S_OO^^_Z__KO^O_[#_ +MK/^L_YS_F_^E_ZC_T/_*_^[_]?\0``D`&0`?`/+_[?^D_ZC_>_]X_W+_=?^! +M_W[_HO^D_]?_UO_E_^7_T/_1_\G_R/^]_[__O_^\_]O_WO_X__7_WO_A_[3_ +MLO^1_Y'__^#_R/_& +M_X__D/]I_VG_9?]D_VS_;_^6_Y'_N?^^_\[_R?^\_\'_TO_._]+_U/_!_\'_ +MK_^M_ZK_KO^0_XS_>O]\_X7_A?^8_Y?_F_^=_Z+_H/^3_Y7_;/]J_U[_7_^# +M_X/_K_^N_]#_TO_S__'_ZO_K_[+_LO^`_X#_?]8_UO_7/]9_XK_C?^[ +M_[C_VO_;__;_]O_V__;_VO_:_\C_RO_`_[S_D_^7_V[_:O]D_VC_;O]L_WS_ +M?/^M_Z[_W/_:_]O_W__)_\7_J/^K_X;_A/^`_X'_N/^W__3_]?\(``@`"0`* +M`.K_Y_^;_Y__8?]<_U#_5?]D_V'_@O^"_[?_N?_<_]C_U?_;_^/_WO_G_^O_ +MW__:_[G_O/^U_[3_H/^B_X[_C/^1_Y/_M?^Q_[K_O__+_\?_R/_+_[G_MO^G +M_ZK_RO_(__K__/\*``@`"``*`.[_Z_^:_YW_2/]'_RK_*?\W_SK_____^/_]_P4`!0#__P\`$@`"``(`Y__D_[O_P/^@ +M_YS_>?]\_YW_FO_:_]W_%0`2`"$`)0`N`"D`^O_^_[?_M/^-_X__E?^5_ZK_ +MI__._]3_^O_S_^W_\__5_\__R?_._\G_Q__`_\'_T/_/_^?_Y__F_^?_X?_@ +M_^K_Z__K_^K_XO_B_]__X?_4_]'_JO^K_ZC_J/_#_\+_WO_A_^S_Y__V__O_ +MX__>_Z;_K/^'_X#_AO^-_ZG_I/_J_^S_+P`P`$<`10`C`"0`^O_\_]/_S?^; +M_Z/_F_^4_['_MO_&_\/_S/_,_]G_V__8_];_S?_/_]+_T/_/_]#_M_^W_ZS_ +MK/_%_\;_X/_>_P```0`M`"P`.@`\`!D`%P#F_^C_O/^Y_Y/_E?^2_Y'_N/^X +M_\G_RO_!_[__N?^]_[W_M_^R_[C_V/_2_PX`%``[`#<`.P`^`"P`*0#T__;_ +MLO^Q_Y'_DO^8_YC_F_^:_[3_M?_J_^G_"``)`"$`(``P`#$`+@`M``,`!`#% +M_\/_E_^:_VW_:?]V_WO_O/^X_P0`!@`J`"H`0@`_`#$`-@`$````U?_8_]__ +MWO_O_^W__/___PD`!P#S__7_Q/_#_ZG_J/^N_Z__K_^N_\7_Q__N_^W_"P`+ +M`!``#P`>`!\`+0`L`!X`'P`4`!0`^O_Z_]?_UO^S_[7_Q/_!_]S_W__H_^;_ +M^/_Z__O_^/_9_]W_Q/_!_]'_T__X__;_'P`@`$\`3@!-`$\`%P`5`-/_U?^N +M_ZW_C?^,_Y+_E/_'_\7__?___Q@`%P`T`#0`1`!$`#L`/``S`#$`)@`H`/[_ +M_?_$_\+_M/^X_ZS_J/^L_Z__O_^]_^'_X?_E_^;_Y__G__[__O\A`!\`10!( +M`'8`<@"!`(8`1@!"`/3_]O^L_ZO_=/]U_V;_9/^=_Z#_YO_B_QD`'@`X`#,` +M/@!"`!X`&P`#``4`___^__#_\/_;_]O_X__D__3_\__Y__C_"P`,`"0`)``> +M`!\`_?_\_^?_YO_)_\O_P?_`_^3_X_\2`!4`'``8``\`$P#Y__C_V/_4_ZG_ +ML/_(_\'_Y__M_P\`#``G`"8`)P`K`!$`#0#O__/_`0#__PD`!P`(``P`$@`0 +M``X`#@#B_^/_P?^__Z[_K_^H_ZK_J/^D_\3_R/_A_][__/_^_RX`+0!J`&H` +M<0!Q`%D`6@`L`"H`W?_A_Y#_C/][_W[_E?^2_\?_RO\%``,`+P`Q`"4`(P#[ +M__S_Z__K_^'_XO_<_]G_\?_T_P\`#@`*``D`]O_Y__;_\O_M__'_[/_H__?_ +M_?\%`/S_ZO_T__7_[/___P8``P```/S_^O_Z__[_Y__D_[__P?^T_[3_S__, +M_^W_\?\M`"H`4`!3`$D`1P`)``H`X?_?_[K_O/^P_Z__S/_-_PL`"P`H`"<` +M*P`L`"<`)@`%``8`[O_L_^G_Z__T__+_X__E_]__W__L_^K_[?_N_^[_[O\& +M``8`$@`3``<`!0#\__W_```!````__\6`!<`.P`Y`#8`.``7`!4`\__V_]C_ +MU/^Z_[[_S?_*______\C`"8`*P`G`!H`'0#U__/_R__+_]#_TO_]__K_%@`: +M`$8`0@!A`&,`70!=`#D`-P`8`!L`_O_\_]#_TO_`_[W_N_^]_\G_R/_M_^__ +M/``Y`&4`:`!J`&<`5`!7`"$`(`#B_^+_NO^X_\S_T/_T__'_&0`;`$(`00!# +M`$,`*0`I`"(`(@`N`"X`+0`N`#,`,0`W`#D`%@`3`.#_X_^]_[W_NO^W_[[_ +MPO_G_^/_&P`?`#H`-P!2`%,`=P!V`'X`@0!I`&8`3`!.`"``'@#@_^+_M?^U +M_[;_M?_6_];_^/_Z_RX`*@`M`#,`%0`0`/3_]__Y__G_`@#^_R0`*0!?`%L` +MP!'`$<`!@`&`+'_L?^&_X?_F?^7 +M_ZW_K__=_]K_$``4`$``/0!*`$P`50!3`%P`7@!$`$(`%P`:`/G_]?_._]'_ +MO?^Z_\S_T/_Q_^W_]__Z__S_^?\!``4`^O_U__3_^?\4`!``-0`V`#,`-@`I +M`"4`^O_\_[S_O/^>_YS_L_^W_^+_WO\/`!$`0@!!`%0`50`S`#(`!P`(`/#_ +M[__:_]O_U?_4_^+_X__J_^G_X__D__3_\_\!``(`]/_T_^+_X?_8_]K_Q?_# +M_[W_OO_=_]W_'@`=`$H`3`!O`&T`90!F`"$`(0#3_]+_G?^@_XW_B?^'_XK_ +MM/^R_]__X/_G_^?_Z/_J__+_[O_S__;_!0`#`",`)``K`"T`&@`6`/__`@#V +M__3_UO_8_\3_P__,_\S_QO_'_\3_PO_._]#_Z/_G_____O\/`!,`'P`9`.[_ +M]/^^_[K_G/^?_Z'_GO^R_[3_]?_T_S@`.`!(`$H`,P`Q``H`"P#6_]3_KO^Q +M_[C_L__#_\K_T/_)_]7_V__F_^'_U__;_];_TO_D_^K_^O_S__'_]__X__3_ +M]__X_^O_[O_U__#_```%`/C_]/_5_]C_R__(_\#_PO_"_\'_YO_H_QP`&0`M +M`#``'0`9`/+_]_^W_[+_@?^&_X[_B?^Y_[W_Z?_G_QH`&@`Y`#L`*P`G`/__ +M`P#L_^K_V__:_\O_S__9_]3_W?_@_^/_X__K_^C_!``)``H`!@#S__7_Z/_G +M_\W_S/^P_['_L?^S_]W_V_\$``4`(P`B`"T`+0`5`!<`Y/_B_]3_U?_<_]S_ +MY?_D__;_^?\+``@`]O_W_]'_TO_%_\+_R__/_^3_X/\,``\`+P`N`"8`)@`+ +M``L`[__O_\W_R_^C_Z;_HO^@_[+_L_^\_[S_Z/_G_Q\`(`!5`%4`<`!O`'4` +M=@!$`$(`Y/_G_Z#_G_]W_W7__[[_O__$_\3_]O_V_QD`&0`R`#(`-@`V`!P` +M'`#G_^?_Q?_%_\3_PO_!_\;_V?_2_^W_\_\*``<`&0`8`#T`00!3`$X`10!) +M`"\`+0`5`!8`Z?_I_\?_QO_*_\O_U/_4_]7_U?_8_]G_S__._\S_S/_?_]__ +M)0`E`%P`7@"!`'T`B@"/`&,`7@`&``D`NO^Z_Y'_C_^/_Y+_K_^L_^__\?\8 +M`!@`,0`P`$,`1`!+`$H`-``T`!4`%P`(``8`X/_A_\S_RO_1_]3_\__O_P@` +M#0`J`"4`*0`L`!$`#P#Q__/_]O_U_P8`!0`5`!8`0P!!`$4`2``J`"D`^O_Y +M_]O_W/_,_\G_T?_6____^_\,``\`%``1``0`!P`-``@`]?_\_P@````C`"P` +M/``U`#P`/P`^`#T`-@`V`!X`'P`1`!$`_?_]_]C_UO^W_[K_P/^]_]G_W/\` +M`/__0P!!`&T`<0!H`&(`.@!!``P`!@#;_^#_S__*__+_^/\E`!T`,P`\`$H` +M00`^`$8`'@`9``,`!@`#````"``*``$``0`'``4`_/\``/;_\?_V__K_#P`, +M`!(`%0`7`!,`)``J`#,`*@`L`#<`3`!"`%8`7@!/`$H`)@`G`/7_]__#_\#_ +MJO^M_\K_R?\%``,`,@`U`%<`4P!8`%T`,``L`/K__?_G_^7_\/_P__K__/\B +M`"``-P`X`#4`-0`V`#4`.0`[`"\`+0`<`!X`%@`4``,`!`#I_^C_X/_@_^__ +M\?\``/W_"P`.`!L`&``-`!``!@`#`!(`%``U`#0`2`!)`%H`6@!9`%@`+0`N +M`.W_[/_0_]+_S/_(_^'_Y?\2`!``.0`Z`#8`-0`C`",`#P`0`/;_]?_K_^S_ +M_O_]_Q\`(``E`"0`-0`W`$(`/@`Y`#\`+@`G`!D`'P#Y__7_R__-_\3_Q/_? +M_]W_```#`$$`/@!R`',`>P!]`%``30`<`"``Z/_D_\G_RO_-_\__\O_O__G_ +M_/\%``,`#0`.``\`#P`6`!4`-``U`&4`9`!H`&@`8P!D`$8`10`3`!4`Z/_F +M_]3_T__%_\C_NO^W_\S_S__Q_^[_$``2`#X`/`!L`&\`?0!Z`&(`8P`S`#,` +M`0```-C_V__=_]G_`0`%`"(`'@`M`#``,P`S`!@`%@#R__7_[__K_P,`!P`= +M`!L`,@`R`#P`/0`G`"4`"``*`/K_^/_W__C_[__P_P0``0`4`!@`)0`@`"@` +M+`!&`$4`5`!3`$4`2``W`#(`#0`3`.3_WO_2_]?_Y__E__O_^O\'``L`&``2 +M``,`!P#D_^/_W?_<__W___\Q`#``:0!G`)8`F@"0`(P`7`!>`"4`)0#H_^?_ +MLO^S_Z+_HO^M_ZW_PO_!_]3_UO\(``4`-@`Z`%<`4@!N`'4`;`!D`#P`0P`; +M`!8`_O\``/3_]?_R__'_`@`"``D`"`#R__/_Y?_E_^__[_\$``0`*P`J`%8` +M5P!7`%<`-0`T``4`!@#<_]K_M_^Z_\;_P__S__?_)``>`#,`.@!*`$,`-P`^ +M`"H`)0`7`!@`$``3`/O_]O_M__3__/_T__W_!0`-``8`'@`D`"T`*``.`!(` +MZ__J_]'_T/_%_\;_W?_;_Q@`&@!&`$8`5P!6`$\`4``O`"X`^O_[_]O_V__G +M_^;_]?_V_P(``P`/``T`!@`(`/7_]/_V__7_`0`#``@`!P`0``X`%@`;`!L` +M%0`#``@`!P`#``H`#@`#````]?_W__+_\/_D_^7_\__S_QP`'`!$`$,`3P!2 +M`$@`0@`=`"0`W__9_Z7_J?^E_Z/_O__`_^O_ZO\<`!X`,``O`"D`)P`0`!0` +M%0`0``(`!P`)``4`$P`7`!D`%`#__P0`_O_Y__O_``#[__?__/_^__[__O_S +M__'_X?_E_^S_Z/_U__C__/_Y_P4`"``'``4`ZO_L_]7_T__<_]S__/_^_R\` +M+0!<`%X`<@!P`$<`2``+``L`S__/_YO_FO^-_X__J_^I_]K_V__S__/_#P`. +M`!D`&P`=`!P`&0`8`!\`(0`,``H`__\!`/C_]_\"``$`_O\"``T`"``6`!H` +M!````-O_W__$_\'_M?^X_\7_PO_N__#_'0`<`"8`*``D`"``#``1`/7_[__= +M_^/_]__T_PP`#0`7`!<`"@`)`/G_^O_8_]?_SO_0_^+_W__X__S_%0`2`"D` +M*@`T`#0`'@`=``@`"0#X__C_W?_=_[W_O?^P_[#_L?^P_\C_RO\$``(`4`!1 +M`'``<`!W`'<`6`!8`!H`&0#1_]/_N/^U_[G_O?_1_\[_ZO_J__?_^O_S_^[_ +MY__L__3_\?\*``L`&@`:`#D`.``_`$``*P`L``@`!`#S__?_W__=_]/_U/_3 +M_]/_V__:_^/_X_\```$`+0`M`$0`0@!(`$T`1@`_``\`%0#=_]G_M?^W_\'_ +MP?_5_]3__O___Q\`'@`6`!<``P`#`/3_\__T__3__?_^_R@`)P!)`$L`50!2 +M`#H`/0`C`!\`^/_]_]O_UO_&_\O_PO^^_['_M/^\_[G_V__=__O_^O\=`!\` +M3`!)`%(`5@!*`$0`*0`P`"``&@`+`!``#@`*``L`#0#Q__#_P__%_Z?_I/^< +M_Z#_J?^D_^;_ZO\P`"X`7P!@`&$`80!(`$<`$0`3`-K_UO^]_\/_PO^]_\?_ +MS/_G_^+_!@`)`!P`'``I`"8`,``V`#,`+``$``H`Y/_A_[W_O?^H_ZC_L_^R +M_]K_W/_\__K_`P`%``H`"`#X__G_YO_F_^C_Z/\,``L`)@`H`#$`+P`E`"@` +M`0#^_\__T/^Z_[O_OO^\_\7_R/_@_]W_\__U__3_\O_D_^?_X__?_^K_[O_K +M_^C_]O_W__S__O\!`/S__O\$`!0`#P`4`!<``@```.S_[O_(_\7_GO^B_Y'_ +MC?^Q_[3_XO_@_Q$`$@`S`#,`*``G`/G_^__5_]+_M?^W_[?_M__"_\+_Z/_I +M__O_]__H_^W_ZO_E_^#_Y?_B_]__ZO_K__3_\__O__#_X?_A_^'_X/_;_]S_ +MT?_0_\__T?_0_\W_NO^^_[7_L/^__\/_X/_=__W_```=`!L`%P`:`/'_[/^\ +M_\#_HO^?_Y3_E_^Q_['_[O_M_QL`&P`C`",`#@`-`.G_[?^\_[C_G/^@_YS_ +ME_^5_YG_H?^>_['_M?_+_\?_V/_;__G_]O\2`!0`$@`2````_O_G_^K_U__4 +M_\#_P__+_\C_Q/_'_[?_L_^=_Z+_EO^1_XK_C?^>_Y[_T/_._P```P`2``X` +M"0`,`.7_Y/^\_[S_H/^A_['_L/_(_\?_W__A__G_]__T__;_V__:_[[_OO^Y +M_[C_IO^H_ZG_IO^I_ZW_N?^V_\3_Q?_B_^/_^?_W__G_^O_C_^7_RO_&_Z'_ +MI?^/_XS_IO^F_]?_VO\&``(`(0`C`!\`'P#W__7_P/_%_ZS_I?^6_YW_G/^6 +M_ZG_KO^X_[;_J_^J_Z?_JO^Y_[7_T__7__?_]/\?`"$`*@`J`",`(``(``P` +M\O_O_\;_R?^D_Z'_CO^/_W?_=O]L_V[_A_^&_[[_OO_Z__K_+0`M`$(`0P`E +M`"0`\O_R_\/_Q?^G_Z/_E_^>_[O_M/_/_]3_W/_8_\G_S/_*_\C_PO_%_]'_ +MSO_M_^__`P`!``0`!0#Z__O_Z__J_\G_R?^V_[;_LO^Q_ZW_KO^E_Z;_K_^L +M_\#_P__@_]W__?\``!L`&0`4`!0`^?_Z_]W_V__(_\S_PO^]_];_V?_U__3_ +M]?_S_]G_WO^W_[+_B?^-_W__?/^7_Y?_R?_,__W_^O\D`"8`-@`U`"H`*0`$ +M``4`\/_Q_];_TO^Q_[?_I_^A_Z#_H_^H_ZC_S/_)__/_^/\5`!$`%0`8`!`` +M#`#K_^__S/_)_[O_O?_+_\K_UO_6_]C_V/_:_]O_R__)_\?_R/_>_^#_"P`' +M`"T`,`!(`$<`1`!#`!0`%@#2_]'_H_^B_X[_D/^._XS_J/^J_\S_RO_B_^7_ +M_O_Y_Q(`%P`>`!H`%0`9`!,`$``!``,`YO_D_]S_W?_N_^[__?_]_PT`#0`( +M``@`Z/_G_[K_O/^A_Y__HO^C_[O_NO_D_^;_&@`8`",`)``6`!8``P`"`//_ +M]?_M_^S_^O_Z_PD`"``%``@`^__W_^K_[__?_]K_T?_4_]?_U__F_^3_X/_D +M_^G_X__P__?_!@#__PX`%0`>`!<`#@`3`.W_ZO_5_];_R__,_]C_UO_]__[_ +M,0`P`$<`2``Z`#D`#P`1`.K_Y__!_\3_Q?_"_]__X?_W__;_#0`.`!D`&0`7 +M`!4`"``*``T`#``6`!<`$P`2``D`"@`$``(`]__Y__;_]O\)``@`$P`4`!H` +M&0`8`!D`'@`<`!(`%@`>`!D`*@`O`#D`-``>`",```#\_]7_V/^X_[;_PO_# +M__?_]_\R`#$`9P!H`(0`@P!T`'4`1`!$`!8`%0#^____[__N__/_]/\$``,` +M#@`0`!T`&@`K`"\`/``Y`#H`.@`O`#$`*0`F``L`#P`$````#0`0`"T`*@`[ +M`#\`20!%`#\`0@`B`!\`%``7`!H`&0`S`#$`0@!%`&,`8`!4`%<`-0`S``X` +M#0#Z__S__?_Z_P@`#@`G`"$`+P`R`"D`)P`G`"<`(``C`"@`)0`U`#8`4P!4 +M`&$`70!;`&$`8P!>`%X`8`!-`$T`.@`Z`!0`$P#C_^;_O?^Y_[K_O/_6_]?_ +M$``.`%P`7@"5`),`H`"A`(D`B0!E`&4`0`!!`"H`*``I`"P`*``C`!<`'@`. +M``8`^?\"`/?_[__]_P(`*P`H`$L`30!A`&``9P!I`&@`9`!/`%,`0P!``"X` +M+P`5`!8`_?_]_P$`__\$``<`*P`F`%4`6@"*`(<`C`"/`'0`<0!&`$<`%0`5 +M`/;_]?\```(`&0`7`#``,P!$`$``0@!&`"X`*P`C`"0`+P`O`$<`1@!7`%D` +M9P!F`&D`:0!=`%P`4`!1`$@`1P`X`#H`)P`E`!T`'@`,``P`!``$`!(`$``S +M`#@`5`!-`%H`80!D`%\`3P!1`$4`10!%`$4`6P!;`%L`6P!>`%\`2`!%`!4` +M&0#S__#_Z/_J_PT`#0`Y`#<`=`!V`)<`E0"1`),`=P!V`$\`3P`A`"(``P`` +M`/'_]O_X__/_]__Z_Q0`$P!``$$`:0!I`(D`AP"0`)(`>P!Y`%$`50`U`#$` +M)``F`"``'P`L`"T`.0`Y`"X`+``.`!$``P#_____!0`;`!4`0P!'`&8`8P!S +M`'8`9P!E`%(`5``[`#D`*0`I`#0`-@!$`$$`1`!(`$0`00`_`$``,@`R`"P` +M*@`F`"@`'0`>``\`#0`,``T`&0`9`#``+@!-`%$`>0!S`'0`>P!E`&``.@`^ +M`"(`'P`0`!``(``A`$,`0P!4`%4`6P!9`$0`1@`H`"8`!@`'``@`"``8`!H` +M+@`I`#<`/0!&`$$`.@`]`#D`.`!``#\`2`!*`$P`2@!2`%0`4`!-`$8`20`_ +M`#P`1@!)`$,`0``N`#``&``8``0``@#V__G_$@`/`#T`/@!H`&D`?@!]`'X` +M?@!5`%4`(0`@``$``P`(``8`(0`B`$@`1P!E`&<`=`!R`&,`9`!9`%D`0P!" +M`"P`+P`C`"``%@`9``H`!P`#``8`&0`7`#0`-`!.`%(`9@!@`&<`;0!9`%0` +M2@!-`$D`2`!%`$0`1P!)`$(`00`M`"P`"0`*`/7_]?\#``(`(P`E`%$`3P!\ +M`'X`@P"``&<`;`!&`$``&P`@``,````$``8`'0`<`"X`+@`U`#0`0P!%`$@` +M1P!+`$L`4`!0`$L`2P!!`$$`-``U`#4`-``]`#P`0@!%`%4`40!+`$\`)``B +M`/W__/_@_^/_Y?_A_P(`!0`_`#X`;P!O`(<`B`"%`(4`<0!N`%``5`!"`#\` +M10!(`#\`/0`O`"\`&@`;``$```#Q__+_]/_R_PP`#@`G`"8`1`!'`&0`7@!K +M`'(`=0!N`'0`>@!N`&P`40!0`"``(`#__P$`Y?_A__'_]O\>`!H`3P!0`'$` +M<@!\`'L`:@!J`#P`/``9`!@`#P`0`!4`%@`A`!\`)@`H`"X`*@`?`"0`+@`J +M`#H`/0!5`%,`:P!K`'L`?0!X`'4`50!8`$<`0P`H`"P`&0`7`/__``#U__3_ +MZ?_I__C_^/\9`!D`2`!)`&T`;`"$`(4`@`!^`%D`6P`W`#8`+@`N`#0`-0!. +M`$P`60!=`%P`5P`U`#D`$@`.`/+_]O_L_^G__/___RT`*0!,`%``;`!H`'(` +M=0!]`'P`<@!R`&``8`!*`$D`(P`E``4`!`#[__S_!0`#`"(`(P!(`$@`;`!M +M`&\`;@!=`%X`4`!.`$(`10!"`#\`20!+`$<`1@`W`#@`&0`8``0`!@#^__O_ +M%``7`$<`10![`'P`CP"0`)$`CP!X`'H`4@!0`#(`,P`D`"4`&P`8`!L`(``G +M`"$`,0`V`$$`/0!/`%$`8`!A`%\`6P!``$8`,@`L`!P`(0`D`"$`/0`]`&`` +M80!O`&\`;@!O`%H`6``U`#8`(``?`"8`*`!!`#\`3P!1`%<`50!*`$P`,0`N +M`!D`'``:`!@`'@`?`#0`-0!'`$0`5`!7`%(`4`!/`%$`60!6`%,`5P!/`$H` +M/P!$`#@`-``J`"T`.P`Z`$H`20!:`%P`5@!2`#\`1``;`!@`]/_T_^[_\/\- +M``H`-@`Y`&4`9`!]`'P`>`!Z`%P`6@!$`$4`,@`R`"T`+0`V`#<`.P`Y`#`` +M,@`B`"``(``A`!\`(``E`"0`*@`K`"P`*0`A`"4`(0`=`"``)0`M`"D`.@`Z +M`$4`2`!,`$@`-@`Y`#4`-``]`#P`2`!+`%P`6`!2`%0`-0`V`/[__/_2_]3_ +MNO^X_\;_Q__Z__G_/``]`&L`:P!Z`'H`=P!V`%P`70`^`#P`'P`C`!$`#@#Z +M__O_]O_T__O__O\(``4`'0`A`#D`-0!%`$<`.``X`"H`*``5`!D`%P`4`!L` +M'0`P`"\`,P`R`",`)@`/``P`]/_W_^W_Z?_Y__W_*``E`$4`1P!1`%$`3`!( +M`"L`,@`:`!(`!P`/`!``"0`-`!(`%@`3``X`#P`#``0`\__Q__C_^O\!```` +M"0`*`!(`$0`>`!\`*``H`#H`.0!-`$X`5P!6`$8`1P`E`"4`]?_T_\3_Q?^V +M_[3_R/_+__K_]O\:`!\`00`[`#H`0``N`"@`%0`;`!,`#0`)`!``&``/``\` +M%P`'``(`ZO_M_^;_Y?_K_^K__?_^_Q8`%@`Q`#$`.P`[`#P`.P`X`#D`)@`F +M`!$`$`#J_^O_S?_._Z/_H/^9_YS_L_^Q_^W_[_\L`"H`8`!B`'4`_^+_WO_=_^O_ +MZ?\```0`&``5`"T`+0`R`#,`'0`=`/K_^O_<_]S_S/_,_]O_VO_Z__S_(``? +M`"$`(0`+``H`Y?_G_\7_PO^O_[/_P/^\_]C_V__^__S_'0`=`#,`-0`Z`#D` +M/@`]`#P`/@`P`"T`"0`,`.K_Z/_(_\K_O_^\_[S_P/_;_]C_[__P______\* +M``H`#0`.`!,`$@`I`"D`.0`Z`$``/@`B`"4`!0`"`-O_W/_(_\C_U/_4__C_ +M^/\@`"$`/P`]`$(`0P`M`"T`#@`-`/C_^O_P_^[_Z?_I_^O_[?_X__7__/_^ +M_PX`#0`B`"(`,``Q`#<`-@`T`#4`)@`D`!(`%0`4`!$`(@`D`#,`,@`S`#,` +M*``H``$```#?_^'_S?_*_]G_W/_]__K_)``G`$L`20!/`$\`00!"`#0`,@`N +M`#(`+@`I`#$`-0`S`#``(P`F`!$`#@#]_P``]O_S__?_^?\'``<`%P`6`"`` +M(0`P`#``1@!$`%@`6@!A`&$`5@!5`#<`.````/[_V?_=_\?_PO_4_]G_`P#] +M_S@`/P!B`%P`:0!N`&$`7@!2`%(`.@`]`#8`,``B`"H`&P`4`/G__O_I_^;_ +MUO_7_][_W?_Z__S_)0`B`$,`10!8`%D`90!A`&4`:P!>`%8`3P!8`#P`-``4 +M`!H`^/_T_^3_YO_B_^+_`P`"`"<`)P!'`$<`1`!$`#0`-``4`!,``0`#``4` +M`P`A`",`1@!#`%8`6`!;`%L`0P!$`",`(0`/`!$`"``&``D`#``.``P`%@`5 +M`!X`(``B`"(`,@`Q`#0`-@`Q`"T`'0`A``8`!`#M_^[_YO_F_____O\>`!\` +M.P`Z`$8`2`!%`$(`,0`U`"L`)@`E`"H`+0`I`"P`+P`?`!T`_O___]3_U/^Y +M_[C_P__%_][_V_\-`!``.@`W`%,`5@!9`%4`2@!/`#\`.@`B`"<`%0`/`/W_ +M`@#L_^G_W/_?_^W_Z__[__S_%@`4`!L`'0`A`"``!@`(`/G_]O_O__'_^/_V +M_Q``$P`G`"4`,0`R`!H`&0`$``0`[__Q__7_\O_]_P``&0`8`"(`(@`<`!P` +M"@`)`/#_\?_A_^'_X?_A__'_\/_]____!@`#``H`#0`9`!8`%@`8`!L`&P`0 +M`!``_O_\_^'_Y/_6_]/_V/_:_^__[_\:`!D`-@`W`#@`-@`=`"``_?_Y_]O_ +MX/_3_\[_W?_@__?_]O\"``(`!P`(`/[__/_J_^S_[/_J__#_\O\$``,`!@`% +M``H`#0`)``4````$``<`!``(``D`#@`/``<`!0#[__[_Z__H_]K_W/_;_]K_ +MY/_E_^/_XO_<_][_UO_3_]+_UO_B_]W_!``(`"T`+`!+`$H`2@!-`#``*@#P +M__;_P_^__Z;_J?^N_ZS_QO_&_^?_Y_\"``,`$``.``L`#@`,``D`_/____/_ +M\/_B_^3_T__2_\;_QO_+_\W_ZO_H_P$``0`9`!L`'@`:`!(`%P```/W_]/_T +M__#_\__X__+_]O_]__#_Z__._]#_K?^N_Z/_G_^N_[3_T__-_P``!@`M`"8` +M-@`]`#0`+P`D`"<`#0`,``4`!`#Z__S_\__Q_][_X/_1_\[_QO_)_\K_R?_0 +M_\[_V__>_^;_X__H_^K_\?_P_P(``P`=`!L`-@`Y`$@`10`U`#8`"@`*`-[_ +MWO^Z_[K_LO^R_\3_Q/_D_^3__?_\_PD`"P`*``<``0`%``(`_O\.`!(`'0`: +M`!L`'0`-``P`^?_Y_^#_X/_-_\[_T?_0_]O_W/_M_^O_!``&`!<`%0`L`#`` +M00`[`$@`3@`^`#H`#@`/`-W_WO^M_ZS_F?^:_Z?_I__4_]+_"@`-`#$`+@!! +M`$4`-0`Q`!H`'0`/``P`!P`+``H`!@`"``4```#^_^W_[O_H_^C_X__C__/_ +M\O_]__W_"``*``X`"P`!``,```#__P0``P`,``\`"@`'``4`!@#Q__/_ZO_F +M_^'_YO_V__'_"0`-`!X`'``C`"0`$P`3`/?_]__M_^S_\?_S_PH`!P`:`!X` +M*``C`!(`%P#R_^__SO_0_[G_MO^\_[[_UO_6__S__/\9`!D`+P`N`$,`0P!+ +M`$P`10!%`#$`,``2`!0`Z?_F_\?_R?^[_[K_O__`_]S_W?_[__C_"@`.``L` +M!@#^_P,``@#__PD`"@`>`!X`,P`S`#H`.@`H`"@`"0`)`.__[O_:_]S_Z?_F +M__G__?\/``L`#P`2``\`#``!``0`]__U__+_\__R__'_]O_W__7_]?\#``,` +M#P`.`"4`)P!``#T`10!)`#L`.``8`!H`_/_Z_^+_X__A_^'_\__T_PP`"@`6 +M`!D`%P`2``,`"@#Z__3_^O___Q(`#@`F`"<`+P`Q`"X`+``;`!T`"0`'`/S_ +M_O\&``0`$@`4`"$`(``M`"P`)0`G`"$`(``>`!T`&``;`!0`$0`%``8`^?_Y +M__7_]/_Y__O_&``7`#,`-`!*`$@`2P!,`#(`,0`,``X`]?_T__;_]O\2`!(` +M+@`N`$P`3`!,`$P`/``\`!H`&0````,`]?_Q_^[_\_\"`/O_`@`(``X`#``> +M`!P`+0`S`$<`/@!``$D`10`_`"@`*@`2`!,`_O_[__K__O\$``$`%``6`!T` +M&P`:`!P`$P`1`!D`'``H`"4`-P`Y`$(`0@`X`#<`&0`:`.S_[/_/_\[_P/_# +M_][_VO\&``H`-P`T`$X`4`!:`%D`6`!7`$0`1P`W`#0`'``?``P`"`#J_^W_ +MW?_;_\__TO_;_]?_[/_P_P0````+``\`#0`*``X`#P`9`!D`*P`K`$8`1@!5 +M`%8`4`!.`"T`+@#_____U/_5_\'_P/_,_\S_Y?_D__O__?\&``4`"``)``0` +M`0#^_P$`"P`*`!4`%@`C`"(`(P`C`!<`%P`+``T```#\__W_`P#X__#_Z/_Q +M_^;_W__?_^/_[/_J____```A`"$`*P`K`"H`*0`'``@`X/_A_[?_M/^Q_[3_ +MP?^__^+_X____P$`'``8`!P`'P`A`!\`&@`<`!X`'0`8`!D`#@`+`.__\__, +M_\G_J_^L_Z3_I?^K_ZC_OO_!_]W_V__Y__K_#@`-`!\`(0`L`"D`*@`M`"`` +M'0`!``(`T__5_[#_KO^<_YW_L_^R_]#_T/_Y__O_#0`,`!$`$0#R__+_W__> +M_\S_SO_1_]#_Y/_D__G_^?_X__?_\__U_^'_X/_9_]G_S/_,_]'_T/_3_]7_ +MU__7_][_W/_F_^C_^?_X_PD`"@`8`!<`"P`,`.[_[/_-_\__M?^U_[/_L?^Z +M_[W_T/_-_]S_WO_;_]K_VO_:_]7_UO_I_^C_```!`!X`'@`E`",`$``4`/7_ +M\/_)_\__L_^M_ZC_K/^W_[;_RO_)_]G_V__E_^/_Z?_I_^G_Z__R__#_[O_N +M_^7_Y__7_]7_TO_2_\[_T/_C_^'_^?_Z_PP`#0`3``\`_?\"`.C_Y/_*_\W_ +MQ?_#_]#_T?_<_]S_X?_@_]G_V?_(_\G_O/^[_[[_P?_:_];_^?_[_Q8`%0`@ +M`"$`&``8`/_____L_^O_W/_>_\W_RO_'_\K_QO_#_\?_R__3_\__X/_C__;_ +M]/_Y__K_\O_R_]S_V_^\_[[_M/^R_\/_Q/_C_^/_"``'`!\`(0`?`!T`"0`) +M`.?_Z?_5_]+_S?_0_]+_S__:_]W_U/_2_\O_S?_!_[W_Q?_*_\[_RO_A_^3_ +M\__Q__G_^?_V__?_\?_P__#_\/_O__#_]__U__+_]?_G_^/_U?_8_]+_TO_4 +M_]+_V?_;_^'_W__;_]S_R__+_[O_N_^Y_[C_R?_*__+_\/\9`!P`,P`P`"8` +M*``%``0`VO_9_[/_M?^A_Z#_JO^J_[W_OO_8_]7_Z__O_P(`__\+``T`%P`5 +M`!0`%@```/W_WO_B_\+_OO^O_['_JO^J_[W_N__/_]/_X__>_^?_Z__J_^C_ +M[?_M__S__O\6`!0`)P`G`!\`(@`(``,`V?_>_[;_L_^:_YO_HO^B_[?_MO_5 +M_];_[?_M__;_]?_V__C_^?_U_P``!0`$`````0`"`/C_^O_O_^O_Y/_I_^+_ +MWO_I_^O_[__N_^G_Z?_E_^;_T/_/_]#_TO_=_]K_]O_Y_PT`"@`1`!,`!0`& +M`.S_ZO_1_]+_RO_(_];_V?_N_^S_"0`+`!(`$``*``H````!`/[__?\!``,` +M"0`'`!``$0`*``H`^?_W_]__XO_0_\[_P/_!_\;_QO_,_\K_V__=_^[_[O\4 +M`!,`/``]`&$`7@!L`&\`70!<`"X`+P#O_^W_N/^Z_Y[_F_^D_Z?_QO_$_^[_ +M[_\'``@`%P`6`"``'@`A`"0`*P`H`"L`,``R`"X`(``@``T`$`#X__3_[__S +M__#_[?\"``0`!``#``H`"0`$``8`#``)``@`#``9`!0`%P`<`!P`%P`(``T` +M_?_Y__+_\__Z__O_$``0`"L`*0`K`#``)P`?``D`$0#X__/_\O_T_P(``0`= +M`!X`-P`T`#D`/@`O`"L`$0`2`/C_^/_K_^O_Z__J_^?_Z?_X__7_```#`!0` +M$@`D`"4`-``U`#\`.P`S`#<`(0`?``0`!`#N__#_\/_N_P```0`0``\`&@`; +M`!4`%``%``<`^/_V__;_]_\'``8`&@`<`"T`+``A`"(`%``1`/#_\__K_^K_ +MZ__L__S__/\5`!0`*``H`#``,``M`"\`+0`I`"``)``7`!,``P`'`.[_Z__7 +M_]C_U?_5_^/_X_\``/__&@`=`"L`)@`C`"D`%P`2`/[_`0```/__"``'`!@` +M&@`F`"4`'``;``@`"@#\__K_\__T__K_^O\(``<`#P`2``\`"@#U__K_Y/_@ +M_]'_U/_6_]7_Z?_H_P```0`4`!0`(P`C`#,`-``Y`#8`-0`Y`"T`*``2`!<` +M[?_J_\O_S?^W_[7_O?^^_]G_V/_Z__S_$P`0``P`$``$`/__Z/_O_^K_XO_M +M__/_#@`*`"0`)P`O`"X`*0`I`!(`$0#X__O_Z__F_]G_W__5_]#_Q__+_\O_ +MR/_/_]/_XO_<__C___\5``\`'``?`!D`&0`*``@`]/_V_^K_Z/_L_^W_ZO_K +M_^G_YO_:_][_V?_4_\[_T__C_^#__/_]_QT`'0`H`"<`'0`>`/C_^/_-_\S_ +MKO^P_ZO_J/^S_[;_SO_+_^?_ZO_\__K_!P`)`!$`#P`7`!@`%@`6``P`#`#V +M__;_W/_<_\/_PO_$_\C_Q__"_]#_U/_9_]7_U?_7_]+_T__3_]'_Y?_G__W_ +M^_\7`!@`(``@``T`#0#L_^O_PO_$_[3_L?^Q_[3_R/_&_^#_XO_Y__C_^O_Y +M__K_^O_O__#_Z__L_^[_[?_I_^G_X__A_]+_UO_1_\W_S/_1_]3_SO_4_]G_ +MV?_5_]#_T__+_\G_SO_0_^7_XO\```0`*0`E`"X`,0`A`!\`^__\_]'_T?^O +M_Z__GO^>_Y__GO^N_[#_M_^U_\7_QO_._\__Y__D_P(`!0`C`"``,P`W`"H` +M)@`4`!@`]/_P_\W_T/^Y_[?_K/^M_ZW_K?^P_Z__O__!_\__SO_N_^W_"0`* +M`!H`&@`1`!$`\__R_]3_UO^W_[7_L_^U_\K_R/_M_^[_#0`,`!,`%0`*``<` +M[/_P_]?_T__0_]3_T/_-_]G_VO_:_]K_V__<_]7_T__3_]7_V?_7_^'_XO_F +M_^?_Z__I_^;_Y__J_^G_\O_R_P<`"0`/``T`$0`1`/W____H_^3_R/_-_\;_ +MPO_'_\K_V?_7_^/_Y/_C_^+_U?_6_\__S__5_]7_[/_K_P4`!P`=`!L`(P`D +M`!$`$@#Z__C_VO_;_\__T/_,_\G_T/_5_]C_T__9_]W_YO_A_^[_\__]__K_ +M```!``(`!`#V__'_W?_C_]?_T?_2_];_X?_A__?_]?\```,`_/_Y_^W_\/_C +M_^#_YO_H_^__[O\'``@`$``0``T`#`#T__3_V/_:_\S_R/_'_\W_Y?_>__3_ +M^_\*``0`#``1``X`"0#__P0`_O_Z_^[_\O_T__#_Y/_H_^S_Y__N__/_"@`& +M`",`)P`\`#@`-P`[`!X`&P#\__W_TO_2_[__P/_%_\/_W/_@__W_]_\)``\` +M%P`2``X`$0`8`!@`(``>`#,`-@`W`#0`,``Q`"``(0`&``,`\__X_^S_Z/_S +M__7_^?_X_P``___\__[__?_\_P,``P`0`!$`'0`;`",`)0`B`"``(@`C`!H` +M&@`F`"8`+P`O`#H`.P`U`#0`(0`A`/S__/_C_^+_V?_;_^__[O\2`!$`+P`R +M`$D`1``^`$(`*@`I`!,`$0`"``4`___]_P0`!``.`!``%``3`"<`)0`R`#8` +M2@!&`$L`3@!-`$L`,0`S`!L`&`#U__G_\/_L_^O_[O_Z__C_"``)``@`!P`) +M``H`$``/`"8`*`!%`$$`8`!E`'@`=`!J`&P`2`!(`!\`'0#Q__3_Y?_C_^/_ +MY/_V__3_`@`%`!(`#@`?`"0`,@`O`#T`/0!+`$T`3@!*`$$`1@`Q`"\`'``< +M`!$`$0`3`!(`%P`9`!X`'0`3`!,`#P`0``X`"P`5`!D`+P`L`$8`2`!5`%0` +M30!,`#(`-``1``\`__\"`/G_]?\,``\`%``2`!\`(``;`!P`&@`7`!``$P`= +M`!L`+``L`$``0@!-`$H`20!-`$(`/0`M`#$`(0`>``L`#0#R__/_X/_=_]/_ +MUO_?_]O_]/_X_RH`*`!/`$\`:P!M`&<`8P!%`$H`'0`8`/G__?_O_^O_\/_U +M_P,`_O\.`!(`&0`6`!0`%@`;`!H`'P`A`"P`*``G`"P`(0`=``D`#`#T__/_ +MZ__J_^__[__V__G_#P`+`!8`&@`C`!\`(@`E`"T`*@`R`#8`.0`V`"T`+0`3 +M`!8`\__N_]7_VO_3_]'_W__=__+_]?\-``L`#0`.``<`"`#S__'_\__S__C_ +M^?\7`!8`+``M`#L`.P`T`#,`(P`C``P`#@#U__+_X__F_]K_U__2_]7_SO_, +M_]/_U/_B_^+_]O_U_Q(`$@`:`!P`&0`5``0`"`#___W_]?_T____`@`'``,` +M#@`1````_O_I_^O_T?_/_\3_Q?_+_\O_Y?_C__7_^?\$````^?_\__#_[O_< +M_]W_U?_5_]K_V__D_^+_Z__L__/_\O_S__;_]__U_______Z__K_]__W_]G_ +MV?_)_\K_M?^S_[/_M?_'_\;_W__?__/_\__U__7_[/_L_]G_VO_1_\__T__5 +M_]O_VO_?_]__U__9_\S_R?^\_[[_N/^W_\C_R?_@_^#_]__W_P,``@#T__3_ +MW?_>_\+_P?^L_Z[_HO^@_YS_G/^C_Z3_K?^J_[G_O?_3_]'_Z/_I__G_^?_X +M__7_X__G_\7_P?^D_ZG_HO^>_ZK_K/_%_\3_U?_5_]__W__,_\W_M_^U_Y[_ +MH?^B_Y[_I?^I_[[_N__(_\K_R__)_\C_R__)_\3_Q/_+_\G_P?_$_\S_Q?^_ +M_[K_N_^R_[3_M_^V_\#_O__-_]#_V/_4_\[_T/_`_\+_L?^L_ZC_K?^E_Z/_ +ML/^P_Z[_K_^O_Z[_HO^B_Z'_HO^M_ZW_S?_,__7_]O\3`!(`%0`6``$``0#7 +M_]?_L/^P_XG_B/]__X'_?O]]_X7_A?^3_Y/_HO^C_[[_O/_7_]G_]?_T_P(` +M`@#___[_\O_T_]__W/_*_\[_Q/_`_[S_OO^V_[7_I/^F_YC_EO^-_X__F_^8 +M_[?_N__C_]__^/_\_P0`___J__'_U/_,_ZW_M?^H_Z'_J?^N_[W_NO_*_\O_ +MU__6_]'_T__9_]?_U/_6_]S_V__0_\__S__1_\3_PO^]_[[_N?^Z_\?_Q?_, +M_\[_TO_0_\G_S/^[_[?_J?^M_[?_L__#_\;_ZO_I__O_^O\'``H```#\_^'_ +MY?_4_]#_O?_`_\'_P/^^_[[_NO^[_['_K_^I_ZO_L?^O_\;_R/_>_]W__/_^ +M_PX`"P`(``L`^O_X_][_W__+_\O_N?^Y_[#_L?^I_Z?_I?^I_[S_MO_2_]G_ +M^__V_Q0`%@`?`!X`#0`.`.3_Y/^X_[C_F_^:_Y?_E_^J_ZO_RO_(_]O_WO_F +M_^+_YO_L_^?_X/_C_^C_\/_M__7_]__Z__G_\/_Q_^7_X__4_]C_TO_-_\K_ +MSO_+_\G_O_^__[7_MO^X_[?_N?^Z_]#_T/_F_^7_^?_Z__S_^__R__/_Z/_H +M_]W_W/_J_^O_\/_P__O_^__K_^K_UO_7_[[_O?^E_Z?_JO^H_[C_N?_/_\[_ +MY/_E_^O_Z__G_^?_X__B_^'_X__H_^;_Z__L_^7_YO_K_^C_W?_A_]__V__A +M_^7_YO_C_^;_Y__9_]C_QO_)_[C_L_^P_[;_Q/^^_]#_U?_A_][_XO_B_]W_ +MW__2_]#_TO_3_]__X/_Y__7_`@`(``P`!@#N__/_UO_2_[#_L_^J_ZC_H_^E +M_[?_MO_)_\G_U__6_^#_XO_H_^;_YO_I_^;_X__:_]O_Q__'_[7_M/^J_ZS_ +MN/^V_]#_T__Q_^[_`P`#``4`"`#N_^G_Q__/_[+_J?^?_Z;_I_^C_['_LO^X +M_[C_M_^V_[O_O/^^_[[_S?_-_][_W?_P__+_]?_Q_^+_Z/_-_\?_M_^\_ZG_ +MIO^L_Z[_KO^M_[7_MO^X_[?_P/^__\+_Q?_/_\S_TO_3_]7_UO_(_\;_N/^Z +M_Z?_I_^K_ZG_L/^R_\K_R/_2_]3_S?_-_[W_O/^F_Z?_F_^:_YW_G?^Z_[S_ +MV/_5_^O_[O_P_^[_W__@_\?_Q_^Q_['_GO^=_X__DO^'_X/_A/^'_XK_B/^; +M_YS_M?^V_]K_U__J_^[_]O_Q_^;_Z__5_]/_QO_&_[W_O?^X_[C_JO^I_Y?_ +MF?^!_X'__Y__@_^!_WC_>?]X_W?_B_^._YG_E?^>_Z+_FO^6_YC_ +MF_^;_YG_J?^K_[?_M?_%_\?_RO_(_\W_T/_0_\S_PO_'_[W_N?^C_Z7_B_^+ +M_V7_9/]:_UK_5_]8_W__?O^F_ZC_VO_7_^?_ZO_I_^?_T/_0_[3_M_^A_YS_ +MDO^7_Y7_DO^1_Y'_C?^._XS_B_^2_Y/_HO^A_[O_O/_._\O_S__3_\O_R?^X +M_[G_IO^E_Y;_EO^5_Y7_D_^4_Y7_E?^8_Y?_F/^9_ZC_IO^Z_[W_U?_2_]C_ +MV__3_]'_OO^^_Z;_IO^6_Y?_F_^:_Z__L/^__[W_R/_*_[O_NO^F_Z;_E?^5 +M_X__D/^;_YG_H_^F_[O_N/_!_\+_O__!_\+_OO^Z_[[_PO^__\#_P?_`_\'_ +MN/^V_Z__L?^P_Z__N/^W_[__P?_#_\'_N?^[_ZC_J/^;_YK_E/^4_Z7_IO^\ +M_[O_TO_3_]O_V__3_]'_P/_#_[?_M/^X_[G_P/_"_]G_U?_A_^;_YO_A_]+_ +MU/_!_\+_M?^S_ZC_J_^K_Z?_I?^I_ZC_I/^I_ZW_P?^^_\W_S?_I_^O_]/_R +M__G_^O_F_^?_V?_5_\?_S/_+_\C_T?_2_^#_W__7_]C_U/_4_[__O__#_\+_ +MOO^__]'_T/_=_]__X/_>_]7_U?_&_\?_P/^__\C_R?_D_^3_`@`!`!@`&0`= +M`!T`&@`:````_O_F_^K_S__+_[S_P/^M_ZG_J/^J_Z[_K?_$_\;_[?_I_Q`` +M%0`M`"D`+0`O`!@`%P`"``(`XO_C_^/_XO_F_^C_^__W__S_``#\__K_[O_N +M_^/_Y?_D_^+_\?_R__S_^_\,``T`$0`0``X`$``+``@`!``(``P`"``'``H` +M!@`$`/S__?_O_^__]O_U__S__O\7`!0`'``?`"D`)@`D`"4`'``=`!8`%0`7 +M`!@`'@`=`!P`'0`4`!(`]__[_^7_X?_7_]S_[__I_P4`"@`Q`"X`20!*`%@` +M60!)`$<`/``^`"4`)``@`!\`$P`5``H`"0`&``8`^?_Z_P<`!@`1`!$`(@`C +M`#<`-P`_`#T`/``_`#8`,@`P`#(`+P`Q`#4`,@`O`#(`*0`E``\`$@`(``<` +M#``,`"(`(P!&`$0`:P!M`'L`>P!S`'$`5`!6`"X`+``2`!0``0`!``@`!@`+ +M``T`&``7`"T`+``[`#X`4P!0`&4`9@!R`'0`>`!S`&0`:@!=`%<`2@!/`$@` +M10!*`$P`2`!%`#@`.P`I`"<`%@`7`!$`$0`=`!L`-P`Z`%H`60!O`&T`=@!Y +M`'H`=@!K`&\`<0!O`'``<0!P`'``8@!A`%$`40`R`#,`'@`=`!D`&@`H`"<` +M0P!#`%<`6`!Q`'```!S`&\`:P!Q`%X`5P!&`$X`.@`R`"P`,P`Y +M`#0`4P!4`'4`=P"2`(\`E`"8`((`?@!>`%\`/@`_`#$`,``V`#<`0``_`%,` +M4P!?`&``7`!<`%\`7@!B`&,`;P!M`'@`>P"``'T`>0!\`&\`;`!D`&<`90!B +M`%H`7@!D`%\`5@!:`%D`5P!+`$L`4`!2`%P`6P!O`&X`?`!^`'X`>P!N`',` +M80!<`%8`60!:`%@`:0!K`'H`>@"'`(4`?0!]`&L`;0!?`%T`4P!6`&``7`!I +M`&P`=0!S`',`=0!O`&T`:0!J`&(`8@!D`&4`:0!F`&<`:P!G`&(`7`!B`&0` +M7P!E`&D`=`!Q`'T`?P!\`'L`@"$`((`>`!Y +M`&4`9`!)`$L`.@`W`#P`/P!4`%$`;`!O`(4`@P"'`(@`A@"%`'(`@!Y`'L`@`!^`'P`?0!U`'0`50!6`$$`00`J +M`"@`(P`E`#H`.0!>`%X`A@"(`*0`H`"F`*H`H@"?`(L`C`"#`(0`=P!U`&P` +M;@!@`&``4`!-`#@`.P`Q`"\`-0`W`%$`4`!I`&H`A@"#`(@`BP"(`(<`>@!Z +M`'(`<@!L`&P`90!E`&8`9@!C`&(`60!;`&4`80!C`&D`>`!R`',`=P!O`&X` +M6P!:`$@`20!!`$(`3`!)`%L`70!N`&\`>0!V`&T`<0!:`%8`2`!*`$0`0P!/ +M`%$`:`!E`'L`?@"#`($`?@!_`'4`=`!G`&@`60!8`$P`3@!#`$``,0`T`"L` +M*``M`"\`/@`^`%8`50!N`&\`=@!T`'8`>`!L`&L`:@!J`&0`90!I`&@`9@!F +M`%L`6P`\`#P`)0`E`!(`$P`:`!H`-0`T`%D`60!T`'8`@@!_`'X`@@!R`&X` +M60!;`$H`2P`^`#P`,``Q`"4`)0`@`!\`(0`C`#0`,@!.`$\`:0!H`'8`>`!Y +M`'<`<0!T`&$`70!4`%<`2`!&`#\`00`L`"H`&@`<``X`#``+``P`)0`E`$8` +M1@!J`&H`>0!Z`'T`>@!I`&P`40!0`$4`10`_`$$`3`!(`$L`3P!/`$L`/P!# +M`#(`,``L`"L`)0`H`"H`)0`H`"X`.``T`#P`/@!1`%``9`!C`'8`>0!^`'H` +M;`!Q`%<`4@`O`#,`(``>`!\`'P`T`#4`2@!(`%\`8@!H`&4`5P!:`%$`3@`] +M`#\`0``_`#X`/@`]`#T`.0`[`#(`+P`V`#@`10!$`%@`6`!R`'0`A0"#`(8` +MA@!X`'H`9P!D`%0`5P!#`$$`.P`\`"L`*@`B`",`(``?`"T`+0!$`$4`90!D +M`'P`?`"*`(H`?@!^`&L`;`!9`%<`40!4`%D`50!E`&H`;0!H`&0`9P!5`%4` +M2`!'`#@`.0!``$``30!*`%<`70!F`&``9@!J`&@`9@!L`&T`;P!N`'8`=P!P +M`&\`:P!M`&<`90!E`&8`9P!F`'``<@!X`'<`<0!P`&``8@!2`$\`0P!'`%,` +M40!I`&@`@0"$`)(`C0")`(X`?P!\`&4`:`!<`%@`60!=`&H`90!O`'0`?P!\ +M`'L`?`!Z`'H`?@!]`'T`?0!_`(``>`!X`'0`P!^`(4`@P!X`'@`;`!L`&``80!F`&4`;P!P`'T`?`"&`(8`A0"%`'X`?@!W +M`'@`@`"``(T`C`"7`)<`E0"5`($`@P!@`%\`10!$`#0`-0`[`#D`20!.`'(` +M;`"%`(L`G0"7`*$`I0"D`*,`G0"=`(T`C`!V`'D`6P!6`$$`1P!'`$$`2P!0 +M`&P`:0!_`'\`D`"2`(<`A0!P`'$`7@!>`%(`40!5`%<`90!C`&P`;@!T`'(` +M:0!K`&L`:0!F`&D`<@!M`'D`?P"&`($`@@"$`',``"L`+@`Z`#<`0`!#`$,`0`!&`$@`40!/`%8`5P!9`%D`0P!$`"X`+0`& +M``8`]?_T__/_]?\0``T`,0`U`%8`4@!D`&<`9`!B`$T`30`Z`#L`)0`E`!4` +M%``,``P``@`$``0```#^_P,`%0`1`"``(@`U`#0`.0`Z`#<`-@`H`"D`(``? +M`"``(``B`"0`*@`G`"D`+0`?`!H`#0`1`/___?___P``!@`&`!<`%0`7`!H` +M&P`8``@`"P`'``8``P`!``H`#0`:`!@`(P`D`!T`'0`-``T`_?_\_^S_[O_T +M__+_^/_Y_P@`"0`5`!(`&0`<`",`(``<`"``&P`8``D`"P#U__/_T?_1_[G_ +MN_^R_[#_Q/_&_^?_YO\2`!$`*@`L`#,`,@`I`"D`%``5`/___/_T__C_\__Q +M_^W_[?_C_^3_U/_2_\7_R/_"_[__R/_+_]K_U__B_^3_[__P__3_\?_S__?_ +M^/_T__G__/\"````^/_Z__'_[O_;_][_U__5_]#_T?_B_^+_Y/_C_^3_X__4 +M_]?_Q__#_[+_MO^V_[3_QO_%_]S_WO_Q_^[_^/_[__+_\/_I_^O_Y/_B_^'_ +MXO_B_^+_XO_A_]O_WO_0_\S_O?_"_[W_M_^[_\'_Q__#_\W_S__0_\__SO_/ +M_]+_T/_5_]C_X?_>_][_X?_B_]__T?_4_\G_QO^^_\#_QO_&_]S_V__F_^?_ +M]?_U_]__W?_(_\O_LO^P_Z?_I_^M_[#_P/^\_];_V?_D_^+_XO_D_]O_V/_/ +M_]/_Q__$_\+_Q/_!_[__OO_`_\7_PO_)_\[_X/_;_^G_Z__S__3_\/_M_]7_ +MVO^\_[?_H?^D_YO_F?^C_Z3_O/^\_\W_S/_6_]G_WO_9_]G_W?_<_]G_W/_? +M_^/_X?_>_]__S__._[C_M_^G_ZO_I?^A_['_L__*_\K_X/_>_^W_\/_P_^[_ +M[/_M_];_U?_-_\__P/^^_[+_L_^E_Z;_H_^@_Z3_J?^^_[G_T__7__+_[O_X +M__S_^O_V_^C_[?_=_]C_T/_4_]G_U?_=_]__VO_<_]#_S/^R_[;_HO^=_Y#_ +ME/^>_YW_L/^O_\O_S?_G_^3_\__V__[__/_[__S___\``/C_]?_J_^__U__1 +M_[W_P_^O_ZG_JO^P_[?_L?^Y_\#_R__$_\C_S?_-_\K_R__-_]W_W?_Q__#_ +M!0`%``D`"@#]__O_WO_A_\C_QO^U_[?_M_^U_\7_QO_9_]C_Z__L_^W_[?_K +M_^S_[/_I_^;_Z__H_^'_V__B_]+_S/^^_\+_N?^X_[S_N__*_\O_X?_A__C_ +M]O\%``<`!``$``<`!0#]_P$``0#\_^[_\O_?_]S_O?_`_ZO_I_^8_YW_JO^F +M_\'_Q/_J_^?_#0`0`!<`%``0`!0`_O_Z_^K_[?_@_]__Y/_C_^+_Y/_I_^;_ +MX__I_^G_X?_=_^7_X__;_^#_Y__B_]W_V__?_]O_V?_B_^+_[?_M_P8`!@`/ +M`!``$@`1`/[____D_^3_S__-_\#_P__+_\G_X/_@__3_]_\%``$`!P`)``(` +M`@`%``,`!0`(``H`!P`!``0`]/_Q_^#_X__*_\;_P/_%_\G_Q/_:_][_\/_M +M_P```0`)``L`$``,`!$`%0`7`!,`$``5``<``@#T__C_X__@_]?_V?_7_]?_ +M[/_L__K_^/\$``<``0#^_^[_\?_B_^#_V__<_^G_Z/_W__G_#P`+`!(`%P`4 +M`!$`!P`'`/__`P`$`/O__O\(``P`!0`$``@`___^__+_\?_M_^[_YO_E_^;_ +MYO_F_^7_Y?_J_^G_XO_N__3_!0#__Q8`&P`L`"L`+@`L`!X`(0`(``,`ZO_Q +M_^C_X?_D_^K_^?_U_P@`"@`)``@`!``$`/+_\O_G_^G_Y__E_^O_[/_U__3_ +M^O_[_____O\&``@`"P`(`!0`%P`>`!T`(``?`!8`%P`$``0`^__Y_^W_\/_R +M__#_]__X__K_^/_V__G_\?_M_^[_\O_S__#_`0`#`!,`$0`9`!P`&@`6``D` +M#0`&``,`__\"`!0`$0`?`"(`,0`M`"0`*0`<`!<`]O_Z_^/_X/_6_]C_W?_= +M_^W_Z__^_P$`&@`5`"4`*@`]`#H`/P!!`$(`0@`L`"H`$0`2`/'_\?_;_]O_ +MV?_:_^__[?\+``T`)0`D`"\`,``N`"T`(P`C`!L`&P`?`!\`%@`8`"``'0`/ +M`!(`!@`#`/'_\O_I_^O_]/_P__[_`@`9`!8`(0`C`"\`+0`M`"\`,@`O`#(` +M-0`M`"L`)@`H`!8`$P`"``0`\__R__'_\?_W__C_!``#``T`#0`/`!``#0`+ +M``T`#P`9`!<`*``J`#@`-@!"`$0`.``W`",`(P`$``4`^/_V_^[_\/_^__W_ +M"@`*`!,`%``5`!0`%``4``\`$``2`!$`$0`1`!8`%@`0`!$`!P`%``(`!0`! +M`/[_"``*``\`#@`8`!D`%``4`!<`%@`7`!@`'@`=`"<`*0`R`#``*``J`!$` +M#0#I_^[_R?_%_[G_O/_$_\'_X__E_PL`"@`K`"P`1`!#`$D`20!%`$4`.``Y +M`"H`*0`4`!0`^?_Z_^/_X?_1_]3_T?_-_]O_WO_P_^_______P@`"0`-``L` +M#``-`!``$0`8`!8`(0`C`"$`'P`6`!@`!0`%`/3_\O_O__#_\O_S_P``_?_^ +M_P4`!0#[__'_^__M_^7_X?_F__/_\?______%0`6`!<`%@`9`!D`!``$`/[_ +M_?_M_^__[?_K_^S_[?_P__#_^/_V__W_```0``X`'P`?`"(`(P`@`!X``P`% +M`.C_Z/_1_]#_S/_+_]K_W/_O_^W_!P`*`!@`%@`2`!$`"0`,``$`_?_^_P(` +M`@````P`#``$``4```#___/_\__N_^__[__N__7_]?_]__[_`@```/__`0#\ +M__K_^O_]_____/___P$`_?_\__7_]?_J_^K_Z/_J__+_[O_]_P(`%P`3`"$` +M(@`A`",`$@`/`/[_``#Q__#_[?_L__7_^/_[__G__/_\__3_]/_Q__#_Z/_I +M__+_\_\``/[_#``-`!$`$``.`!``"``%``,`!P`&````"``.``X`"P`*``H` +M#``-``0``P`'``@`!0`%``D`"0#___W_[__S_^#_W/_6_]G_XO_@__W__?\? +M`"$`.P`X`$0`1P`^`#L`)@`H``X`#0`&``<``P`"``P`#0`+``D`#0`/``4` +M!``!``,`___\_P```0#^__[_!0`$``4`!P`1``\`'@`@`#L`.@!.`$X`5`!3 +M`$@`2@`Q`#``#P`0`/___O_S__3_]O_T_P```P`'``4`$``1`!``#P`C`",` +M,0`R`$H`20!/`%$`30!+`#8`-P`=`!P`!P`(``(````%``D`'P`;`"H`+``\ +M`#P`0``^`$4`2`!$`$(`0@!"`#0`-0`C`"(`$@`2``<`"``0``\`&0`:`#,` +M,@!"`$,`2@!(`$``0P`Z`#8`-0`Z`#X`.@!*`$T`50!2`$T`3P!``#X`)``G +M`!H`%P`*``P`'``9`"4`*@`\`#<`/P!#`$P`2`!+`$\`5`!1`%(`5@!0`$L` +M0`!%`#0`+P`G`"P`*0`D`"T`,@`_`#L`10!(`$8`1``_`#\`-0`W`#D`-@!" +M`$4`50!2`%D`6P!5`%8`0@`^`"<`+``7`!``"P`3`!\`&``H`"X`0P`]`$H` +M3P!:`%8`8`!C`&L`:0!M`&T`:`!J`%<`5``\`$``(``=``@`"``!``,`"@`' +M`!,`%@`C`"(`-P`V`$@`2@!?`%X`=@!U`'\`@0!\`'D`8@!F`$``/0`:`!T` +M"0`%``@`#``>`!H`,P`V`$0`0P!&`$8`/0`]`#0`-0`K`"D`+``N`"X`+0`Y +M`#D`.P`[`#X`/P!&`$0`2`!,`$P`2`!%`$8`-0`V`",`(``6`!H`&``6`"(` +M(P`N`"T`.P`[`#@`.``I`"@`&``;``\`"P`1`!8`(``;`"X`,0`S`#$`-P`Y +M`#$`+P`S`#0`,``P`#H`.0`Z`#L`-@`U`!\`'P`*``H`^O_[__+_\/_X__G_ +M`@`#`!``#@`6`!@`)P`F`"H`*0`V`#D`.0`V`#@`.@`D`"(`#``/`/G_]O_Q +M__/_]__V_PL`"@`1`!4`&0`3``L`$``"`/[_]__Z__?_]O\'``<`#@`.`!0` +M$P`,``T``0`!`/;_]?_R__7_]O_S_P```@`!````"0`)``0`!``#``,`_?\` +M``$`_/_U__K_[__J_^#_X__>_]__X__@__7_]_\'``<`$``.``@`"P#V__/_ +MVO_<_\O_RO_._\__WO_=_^__[_\#``0`!P`%``,`!@```/[_]__W__/_\__J +M_^K_X?_A_]'_T__!_[[_O__`_\+_P?_3_]7_W__=_^K_[/_L_^K_[O_P__'_ +M[__U__?_^O_X__O__/_Q__'_X?_A_]+_T?_(_\O_T/_,_]/_U?_8_]G_U__4 +M_\+_QO^\_[C_M?^Y_\C_Q?_<_][_``#]_PH`#@`4`!``_/\``/+_[O_0_]+_ +MR?_*_[K_N/^Z_[O_N?^X_\/_Q/_6_]7_Y?_F__?_]O_V__?_[__N_]#_T?_` +M_[__L_^T_[W_N__*_\W_Y?_A_^G_[O_S_^__Z__M_^K_Z/_C_^7_[?_K_^?_ +MZ/_B_^3_SO_*_[C_O/^I_Z?_J/^E_ZW_M?_'_[[_T/_7_^O_Y__P__'__O_^ +M_P$``@`*``@``````.__\?_;_]G_PO_%_[S_N/^X_[G_Q?_&_\K_R?_2_]7_ +MU/_0_]'_U/_>_]K_Y?_J_P(`__\)``H`!P`'`/?_]__;_]O_R/_'_[[_P?_& +M_\+_SO_2_][_W/_D_^/_Y__J_^C_Y?_O__#_\/_Q__K_^?_S__+_[/_N_^+_ +MX/_;_]W_W?_;_];_V/_:_]C_TO_5_]/_S__._]+_X?_=__G__O\;`!8`*@`N +M`"L`)P`0`!0`[?_J_\K_S?^X_[3_L/^S_[__O?_1_]+_WO_@__/_[__Z__[_ +M"@`'``T`#P`.``T`!@`%`/'_\__F_^7_V__<_][_W/_G_^G_]/_R__G__/_[ +M__?_\O_V__;_\__S__7_^?_X__C_^?_O_^W_X?_C_]K_V?_:_]K_Y?_F_P`` +M_O\4`!4`'@`?`!L`&@`)``D`_O_^__?_]__Z__K_```!``0``P`#``,`^__\ +M__+_\/_G_^K_ZO_H_^G_Z?_I_^G_\__S__C_^?\1`!$`*``F`#\`00!%`$,` +M.@`\`!\`'@`!``$`Z__K_^'_X/_N__#_]O_T_P(``P`"``$``@`#``(``0`* +M``L`'``;`"8`)P`T`#0`,P`R`"L`+0`@`!T`&@`<`!<`%P`8`!<`$0`3`!$` +M$``+``H`#0`.`!<`%@`>`!\`(@`B`"``(0`9`!<`#``.`!$`#P`9`!L`,P`Q +M`#L`/0!'`$4`/0`_`"P`*P`E`"0`&0`;`!T`'``B`"(`)0`F`"$`'P`;`!X` +M&0`5`!\`)0`P`"@`.P!#`$0`/@!!`$4`.``U`"P`+@`E`"0`(``A`"``(``D +M`"(`(0`C`"H`*0`W`#D`2@!'`%<`60!:`%@`3`!.`#$`+P`6`!D`"0`%``H` +M#@`?`!L`-0`Y`$P`20!*`$P`1@!$`#D`/``U`#(`,P`V`$``/0`^`$$`20!& +M`$4`2`!)`$8`1`!'`$8`10!%`$,`-``W`"X`*P`<`!\`(P`A`"H`*@!``$`` +M3@!0`%0`40!/`%$`1P!&`#L`.@`Y`#T`/P`[`$4`10!!`$4`/@`Y`#@`/0`V +M`#(`00!#`$\`3@!=`%X`6P!;`%``4``V`#8`'P`>``T`#@`0``\`&``9`"L` +M*P`^`#T`3@!1`&8`8@!O`'(`>P!Y`&T`;P!A`%\`/``^`"8`)0`,``P`#0`/ +M`!0`$0`>`"``)@`E`"@`*``F`"@`,0`M`#X`0P!:`%8`;`!N`',`_^/_Z/_C_^S_\/_S__#_]O_W__S__?\-``P`)``E +M`#8`-0!``$$`-P`U`"<`*@`-``L`^O_Z_^C_ZO_D_^'_Y?_H_^/_X/_I_^S_ +M\__P_P0`"``9`!0`(0`F`"@`)``?`"(`%``2``T`#@`*``D`#0`.`!(`$@`. +M``T`!P`(`/O_^O_Y__G__/___P\`"P`;`!X`'P`>`!H`&``#``@`^O_V_^[_ +M\/_V__7_!``$`!(`%``@`!P`(0`F`"8`(0`E`"D`)@`D`"0`)0`<`!L`$@`4 +M``L`"``!``4`!@`!``0`"0`,``@``0`%`/[_^O_R__7_^__X_P\`$0`N`"X` +M4`!/`%H`7`!>`%H`00!%`"4`(P`(``<`\O_V__#_ZO_G_^W_]__R__G__/\/ +M``T`'P`@`#4`-0!#`$,`1`!$`#T`/0`J`"D`%P`9`!(`#P`)``T`$@`.``T` +M#P`1`!$`$0`0`!8`%P`E`"0`+``M`#8`-0`M`"X`(P`B``L`#````/___O\! +M``P`"``=`"``*``F`"H`*@`?`"$`$P`2``T`#0`+``P`&``5`!\`(@`G`"8` +M)0`E`"8`)0`A`",`(0`?`!8`&``-``P`_?_[__7_^/_U__/_!``%`!,`$P`H +M`"<`+@`N`"0`)@`9`!8`"@`,``H`"0`2`!(`'``=`"8`)0`?`!\`%P`7``D` +M"0#__P```P`"``4`!0`.``X`"@`*``@`"``"``(`!0`%``8`!0`,``X`#0`+ +M``X`#P`/``\`$P`3`!H`&@`A`"``)@`I`"``&P`)``\`^?_S_^/_Z/_E_^'_ +MZO_N__C_]?\!``(`!``$``,``P```````P`$``\`#0`8`!L`'``8`!0`&``* +M``<`^?_\__O_]__T__C_`0#]____!``%``$`_?____;_]/_N__#_ZO_I_^7_ +MYO_D_^/_W__@_^C_Y__U__;_#P`/`"4`)``X`#D`+@`M`!T`'@#T__3_U?_5 +M_\3_P__`_\+_U/_0_]S_X?_O_^K_[O_R__/_\O_X__;_]?_X_P(`___]____ +M`@`#`/C_]?_T__?_\__Q__+_\__Q__'_Z?_G_]O_WO_3_]'_SO_0_]/_T/_B +M_^/_\/_Q__[__?_\__S_\O_U_^__ZO_G_^O_Y__F_^W_ZO_=_^3_X/_7_\7_ +MS?_(_\/_R?_+_]S_W?_U__+_!@`'``H`"P`!``$`[O_N_]K_VO_*_\C_O?_! +M_[C_M?^W_[G_OO^\_\S_S?_=_][_\O_Q_P$``@`%``(`]?_Y_^?_X__8_]O_ +MT/_0_]C_U?_4_]C_V?_6_\W_SO_$_\/_N_^]_\7_P__/_]+_[?_J__+_]?_\ +M__C_Z?_M_][_V__+_\[_Q?_#_\/_P__$_\7_SO_-_]7_U?_@_^'_\/_N__7_ +M]__]__W_\__P_]W_XO_._\C_M_^\_[;_M/^]_[W_Q?_%_]'_T?_1_\__S__3 +M_\__R__8_]O_ZO_H__W__/\&``D`!0`"`/;_^/_B_^+_U?_2_\7_R?_#_\#_ +MO/^__[O_N/^Q_[/_L_^Q_[S_O__._\K_YO_K__S_]_\#``<`"P`)``,``@#Z +M__W_[__K_]O_X/_)_\3_L_^W_Z7_H/^D_ZO_O/^U_]3_V?_U__'_`P`%``$` +M`@#P_^W_V?_<_\G_QO_&_\C_SO_._]W_W?_H_^?_[/_N__/_\/_N__+_\/_M +M_^O_[?_D_^+_U__9_\[_S?_)_\K_S?_,_];_U__D_^/_Z/_H_^7_YO_A_][_ +MVO_@_^7_W__K_^___?_Z__K__/_Z__G_Z/_I_^'_X/_7_]C_W__?_^?_Y?_M +M_^__Z?_H_^/_X__8_]G_V__9_^'_X__L_^O_^O_Z__O_^_\``/__]?_X__7_ +M\O_S__3_[__P__/_\?_E_^C_Z__H_^3_YO_S__+_^O_Z_P<`"``$``0`___] +M_^S_\/_H_^3_XO_D__#_\/_\__S_!@`%``(`!`#Z__C_\/_Q_^W_[__Y__3_ +M`0`'`!4`#P`7`!T`&@`4``D`#P`&````^_\``/C_]O_V__7_\?_S__3_\O_Z +M__O_`P`$`!4`$P`7`!D`&@`8``P`#@`!````^?_Z_P,``0`6`!@`+``K`#T` +M/@`X`#@`,0`O`!D`&@`'``@`_?_\__;_]O_W__?_^?_X__?_^?\$``0`$@`/ +M`"@`+`!$`$``30!1`%8`4P!'`$@`.0`Z`"D`)P`;`!P`%``4``\`#@`(``H` +M!@`%``\`#@`9`!H`+@`M`#D`.@!"`$$`-@`Y`#,`+P`D`"@`*@`F`#,`-0!% +M`$4`40!1`$P`30`^`#P`+``N`!\`'``=`"``(@`@`"\`,``[`#H`00!"`$8` +M10!%`$4`0@!"`$``00`X`#8`+0`P`"H`)P`I`"L`/0`\`$P`30!D`&,`9P!H +M`&@`:`!6`%0`10!)`#@`,P`S`#8`-@`V`#<`-@`T`#8`+@`L`#``+P`U`#<` +M3@!-`&``8@!V`'0`?`!\`'0`=0!I`&<`4P!6`$T`2P`\`#T`.P`Y`#``,0`Q +M`#(`-P`W`$8`1@!;`%H`;`!K`'(`=`!S`'(`90!F`%D`60!3`%(`3P!.`$\` +M4@!-`$D`1`!*`$``.@`^`$,`30!)`&$`8P!W`'@`BP")`(<`B0![`'D`8P!D +M`$P`3`!$`$0`0``_`$``0@!)`$8`2@!,`%,`4P!<`%H`8P!F`&X`:P!Q`',` +M;P!O`&\`;0!L`&X`<@!R`'H`>0!X`'H`<@!P`%L`6P!"`$0`,0`O`"L`+``X +M`#D`50!3`&T`;0"``((`A`""`(,`@P!U`'D`>`!P`&8`;P!I`&(`6`!=`%<` +M5`!2`%,`6`!7`&,`9`!N`&X`=@!V`'(`<@!H`&<`7@!?`%,`4P!4`%0`4`!/ +M`%8`6`!5`%(`6`!<`&0`8`!P`'(`A0"$`)$`DP"4`)$`?P""`&@`90!,`$\` +M/0`[`#P`/@!'`$4`40!3`%\`70!=`&``7`!:`%P`7`!<`%T`:0!H`&H`:@!U +M`'<`<0!M`'8`>@!N`&L`=0!W`&D`:`!A`&$`40!0`#X`0``\`#H`.@`]`%(` +M3@!=`%\`;P!N`&D`:P!G`&4`60!;`%L`6`!:`%T`:P!I`&X`<`!Q`&\`90!G +M`%<`5`!-`%$`2`!$`$T`4`!.`$P`6`!7`%``50!=`%8`5`!:`%\`6@!>`&(` +M7P!=`%H`6@!/`$\`20!*`$X`3`!3`%8`8`!=`&$`8@!;`%P`4P!2`$@`1P!( +M`$L`30!)`%4`60!=`%H`6`!9`%,`4P!&`$8`1@!&`$8`10!0`%$`4P!4`%(` +M3P!&`$D`/@`\`#8`-P`Z`#H`1P!'`%4`50!A`&$`9P!H`&P`:@!G`&D`9`!C +M`%``4``_`$$`)P`D`!8`&0`1``T`'``@`#@`-@!7`%@`;`!K`'$`<@!D`&,` +M4P!3`$``0@`_`#L`.0`^`$0`0`!#`$0`0@!#`$``/@`V`#@`/0`]`#L`.``\ +M`$(`/0`U`#$`.``T`"\`,0`T`#P`.@`^`$$`3`!(`#\`0P!'`$,`.0`[`$`` +M0`!$`$4`3P!,`$T`40!)`$4`.P`^`"\`+@`J`"D`*``J`#$`,``P`"X`+``P +M`"(`'@`;`!X`'``;`"X`+0!$`$0`5P!9`&L`:`!H`&L`7@!=`%$`3P`Y`#L` +M*0`I`!<`%``%``P`!0#]____!0`5`!$`*``H`$(`10!/`$T`5@!7`$\`3@!( +M`$<`0P!%`$$`0`!$`$0`.P`\`"X`+@`;`!H`!P`'``<`"``/``X`)0`H`#\` +M.P!*`$T`5`!1`$H`30!"`$``,@`S`"L`*@`@`"$`&@`9`!4`%@`<`!P`)P`F +M`#H`.P!)`$D`3@!-`$<`20`]`#L`+``N`"@`)0`A`"4`+@`J`"H`+@`L`"D` +M'P`@`!L`&P`;`!L`)``D`#,`,P`_`#\`0P!#`$``00`Z`#D`,@`R`#,`-``X +M`#<`.0`Y`#8`.``L`"D`(@`E`!P`&@`;`!L`(``B`"H`*``O`#$`.0`W`#D` +M.P!#`$$`00!$`$8`0@`U`#H`)@`A``\`%``*``4`$``4`"<`(P!``$0`6@!8 +M`%X`7P!5`%0`/0`]`",`)``5`!,`"0`-``X`"0`+`!``%P`4`!X`'@`M`"\` +M.0`W`$8`1P!-`$T`2P!+`$4`1``W`#H`-``P`#``-``S`"\`*@`N`",`(``3 +M`!0`#0`-``X`#0`6`!D`*@`E`#,`.0`^`#@`-@`[`#8`,@`O`#(`/``Z`$(` +M1`!,`$L`1P!&`#H`/``J`"@`'0`?`!<`%0`4`!4`'``<`!X`'0`?`"$`(P`A +M`"D`*@`S`#(`.@`[`#X`/@`[`#L`.@`Y`#(`,P`Z`#@`,P`V`$(`0``X`#@` +M+P`Q`"$`'0`-`!(`$0`-`!<`&@`F`"0`.0`Y`#L`/``V`#4`+0`O`",`(0`A +M`",`+``I`#,`-0`\`#H`.@`^`#@`-0`Q`#(`+``K`"<`)P`>`"``&P`9`!8` +M%P`9`!@`(P`D`#(`,0`]`#X`0P!!`#0`.``H`",`$``5``\`"@`.`!$`(@`A +M`#``,@`_`#T`/``]`#P`.@`P`#,`,P`P`"P`,``I`"0`'``A``\`"P`*``P` +M!``$`!$`$0`@`!\`+P`Q`#X`/``]`#T`-@`Y`#$`+``C`"D`(0`<`!8`&0`. +M``P`"0`,``D`!P`7`!<`)0`G`#L`.``]`$$`.@`W`"$`(0`-``\`___^_P$` +M__\$``<`%``1`!<`&0`8`!@`$@`/``T`$``+``L`&``6`!H`'0`J`"<`*0`K +M`"L`*P`D`",`'P`@`!(`$@`$``,`[O_P_]W_VO_4_];_V/_8_^O_ZO\"``,` +M&@`9`",`(P`E`"8`(@`@`!D`'``=`!D`$@`6`!$`#@#^____Z?_J_];_T__( +M_\O_S/_*_]C_V?_L_^S__/_\_P@`!@`'``L`#0`)``@`"P`%``0`___]_^__ +M\__G_^3_VO_<_]O_V?_?_^'_[/_J__+_]/_T__/_[O_N_^3_Y?_D_^+_W__B +M_^?_X__>_^/_W/_7_\S_T/_(_\3_QO_*_]K_U__N_^__!0`&``X`#``&``@` +M]O_U_]__WO_-_\__PO_!_[__OO^\_[[_P/^^_\;_R/_0_\W_V__=_^/_X__H +M_^?_X__D_]S_W/_6_]3_V/_<_^'_W/_H_^S_Z__J_^'_X/_-_\[_N/^W_ZK_ +MJ_^K_ZK_MO^W_\O_R?_6_]K_Y?_@_^'_Y?_E_^/_WO_?_]S_W/_5_]/_R/_+ +M_[W_N_^Q_[+_KO^M_[K_N__%_\/_UO_9_^#_W?_D_^?_Y__E_]W_W?_;_]O_ +MT/_2_\G_QO^Y_[S_JO^H_Z/_I/^J_ZK_O/^\_]3_TO_B_^7_ZO_I_^'_W__2 +M_];_R?_%_\+_Q/_,_\S_T?_0_]G_V?_4_]7_S?_-_\7_Q/_`_\#_NO^[_[K_ +MN/^W_[K_N_^Z_[[_O?_#_\7_TO_0_]C_V/_B_^3_W?_<_]7_UO_,_\K_R__, +M_\__SO_:_]W_VO_7_]C_V/_!_\3_M_^R_Z7_K/^L_Z7_M/^Y_\?_Q/_1_]/_ +MV?_8_];_U__8_];_UO_9_]K_UO_9_][_W?_9_]/_U?_4_]+_S/_-_\[_S__0 +M_\__S?_._\K_Q_^[_[__N/^T_[K_OO_!_[__T__1_]?_W?_E_][_V?_@_]W_ +MU__5_]G_X/_>_^3_Y?_H_^C_X?_A_]?_U?_%_\K_P_^[_[O_Q/_,_\3_T/_6 +M_]S_V/_7_]C_U/_5_\O_RO_+_\S_SO_-_]7_U?_?_]__Y?_E__'_\O_X__?_ +M^__]__W_^O_L_^W_UO_8_[S_N/^F_ZO_J?^E_[C_NO_5_]7_\/_O_P0`!0`' +M``<``@`!`/+_]?_L_^?_X/_E_^+_W__<_]W_V?_9_];_UO_8_];_W?_A_^S_ +MY__T__G__O_Y__K____W__/_\__V__/_\?_S__/_\/_R_^C_Y__@_]__V/_9 +M_]S_V__F_^?_]?_U_P(``0`)``H`"0`'`````P`&``,``P`&`!,`$``-`!`` +M#P`,`/;_^?_J_^G_V/_6_]C_V__@_][_\__T_P,``P`3`!,`'@`=`!\`(0`G +M`"0`(``B`!P`&P`.`!```@```/?_^/_X__?___\```@`"``2`!$`$0`3`!`` +M#@`&``@`"@`(``X`#P`<`!L`(``B`"(`'P`2`!4`"P`(`/O__?\$``0`#``* +M`"0`*``S`"X`.P`_`#H`.``S`#,`)0`G`!P`&@`*``H`^O_\__'_\/_P__#_ +M]__W_Q``$``H`"<`/@!!`$X`2P!'`$D`10!#`#@`.0`T`#,`,``P`"<`*0`? +M`!P`#0`/``4`!`#]__O_!P`+`!4`$0`E`"D`-``R`#<`-@`W`#D`,@`P`#<` +M.``Z`#P`1`!!`$(`10`[`#D`-``S`"$`(P`@`!\`%@`7`!@`%P`6`!<`'``9 +M`"$`)@`R`"P`00!'`%$`3`!-`%$`1P!$`"P`+@`;`!D`"P`-`!(`$``B`"4` +M-P`S`$4`20!-`$H`0P!$`#X`/@`N`"X`+@`M`",`)P`G`"``'@`E`"``&0`> +M`"0`*@`G`#(`,P`Z`#H`.P`Y`#0`-P`Q`"X`+0`P`"\`+``T`#8`-0`U`#4` +M,P`G`"H`)``A`!T`'P`H`"<`,``P`#H`.@`Y`#D`,0`Q`",`(P`;`!L`&``8 +M`"$`(@`J`"@`,P`U`#(`,``O`#$`*@`J`"\`+0`K`"X`-0`Q`"\`,P`M`"H` +M*``J`"(`(@`C`!\`'@`D`"$`&P`8`!X`%``/`!$`$P`0``\`'0`>`"@`*``R +M`#``*P`O`"<`(P`7`!H`%P`4`!@`&0`F`"<`,``Q`#8`-``L`"X`'0`:``H` +M#`#^__[_^__Z__G_^_\$``$`"0`,`!0`$0`;`!T`)``C`"L`*@`L`"\`*@`F +M`!T`(0`7`!0`$@`3`!@`&0`A`!\`(``A`!H`&P`(``8`]O_X_^C_Y__I_^G_ +M\/_P_P0`!0`4`!(`'``>`"4`)0`J`"<`+``R`#4`+P`M`#$`(P`A``\`#P#W +M__?_X__E_^+_WO_B_^?_]/_O_P(`!@`1``X`&0`;`"(`(0`F`"<`)0`D`!T` +M'@`/``X`^__]__3_\O_O__'____\_PX`$0`?`!T`(0`B`!D`&@`-``H`_?\! +M`/W_^?_[__[_!P`%``L`#``*``L`"``&`/O__/_______O_^_P8`!0`*``P` +M$@`/`!(`%0`<`!P`'0`:`"``)``:`!8`#``0`/__^__J_^W_ZO_G_^K_[__[ +M__;_"``,`!4`$``0`!0`$0`0``X`#@`1`!$`%@`6`!L`&@`8`!H`#``+`/__ +M___P__#_[O_P__;_\O_^_P,`#``'`!$`%@`5`!``%0`:`!8`$0`1`!4`#``+ +M````_?_Q__7_YO_C_^#_XO_G_^?_^?_W_P$``P`1`!``%``4`!@`&@`<`!@` +M&P`?`"$`'@`4`!8`"``'`.O_[/_3_]+_PO_#_\3_P__6_]?_[__N_PD`"@`9 +M`!H`(``=`!P`'P`4`!$`#@`0``(``0#\__W_\?_O_^O_[O_J_^;_[?_Q__#_ +M[?_Q__/_[/_J_^+_Y?_;_]C_V?_<_^C_Y?_X__O_"@`(`!,`%0`+``@````" +M`.__\/_L_^G_Y?_H_^C_Y?_I_^W_[/_G_^;_ZO_D_^'_Z/_I_^?_Z?_Q_^[_ +MZ__M_^7_Y/_=_]W_UO_6_]O_W/_?_][_[?_M__3_]/_W__C_^__[__K_^/_[ +M__[_^?_U_^[_]/_C_]S_RO_0_[__NO^X_[W_QO_!_]7_V/_O_^[_^__Z__S_ +M_O_T__/_Z__J_^#_XO_E_^7_Y__D_^?_Z__I_^;_Y/_F_]W_WO_=_]O_V__; +M_]S_W/_9_]K_V/_7_]?_V/_>_]W_XO_A_^K_[?_R_^[_Z?_M_^3_X?_8_]K_ +MUO_5_]O_W/_H_^7_[O_S__C_\O_I_^__Y/_?_]#_U/_5_]'_TO_5_^+_X?_F +M_^;_Z__M_^O_YO_D_^K_X__>_]__X__?_][_V__8_]7_V__5_\W_T?_9_]__ +MV?_E_^G_\__P__3_]O_Q_^__[/_O_^/_X/_C_^;_X?_=_][_XO_:_]?_S/_. +M_\7_Q/^\_[W_R__*_]+_TO_H_^C_Z?_I_^[_[__D_^/_XO_B_]W_W?_E_^7_ +MZ?_I__#_\/_G_^?_WO_?_]'_T/_!_\'_M_^X_[+_L/^P_[/_M_^S_[S_P?_5 +M_]#_YO_K__[_^O\"``0`_O_]_^3_YO_-_\K_MO^Y_Z__K?^U_[7_PO_$_\__ +MS/_4_]7_T/_2_\__RO_'_\[_R__#_\C_S__(_\+_P?_&_[S_N?^\_[W_OO^^ +M_\K_R?_1_]/_V__:_]K_V?_8_]K_S__._\[_SO_$_\7_P__!_[/_MO^J_Z?_ +MG/^?_Z#_G/^A_Z7_M_^U_\/_P__,_\W_T?_/_\G_R__,_\O_RO_*_]+_T__1 +M_]#_T/_0_[__P/^P_Z__G/^<_Y'_D_^2_X[_FO^?_Z;_H?^V_[G_OO^]_\G_ +MR/_/_]+_U?_1_\__TO_"_\'_M_^U_Z#_I?^A_YO_F?^?_ZG_I/^L_Z[_L/^O +M_ZW_K_^J_Z?_JO^N_[+_KO^__\#_R/_*_\K_Q_^^_\'_JO^H_Y7_EO^(_XC_ +MB/^(_X[_CO^?_Y__K/^K_[G_O/_%_\'_R?_,_\S_S/_&_\/_MO^[_Z3_G_^0 +M_Y3_A/^!_X;_B/^-_XS_G?^=_Z/_H_^I_ZG_IO^F_ZO_J_^K_ZK_L?^R_[3_ +ML_^P_[#_J?^J_YG_F/^8_Y?_D?^5_Z#_F_^E_ZG_KO^J_ZC_J_^=_YS_E/^5 +M_XK_B?^+_XK_C/^._YC_E?^8_YO_H?^>_Y[_H/^B_Z'_H_^D_Z3_H?^:_Y[_ +ME_^2_X__E?^8_Y+_F_^@_Z[_J?^J_Z__K?^I_Y'_E/^!_W[_9O]I_V?_9/]K +M_VW_@O^"_Y7_E/^B_Z/_K?^M_Z__K/^L_[#_K?^K_Z?_I_^<_Y[_D_^/_W__ +M@O]Y_WC__]Z_WG_@?^`_WO_??^$_X+_@_^$_X__C_^9_YC_H/^A +M_Z#_H/^9_YC_C/^-_X3_@_^!_X+_C/^+_XO_C/^1_Y#_A?^&_WW_?/]M_VW_ +M;/]L_W'_?]\_WW_>O]Z_WO_=/]U_WC_=O]U_WC_AO^$_Y#_ +MD/^8_YG_E_^6_XS_C/]T_W;_;O]J_V+_9O]Q_V[_??]^_XW_CO^6_Y3_E?^6 +M_X[_C?^)_XO_?O]]_X#_?_]X_WG_>/]W_W3_=?]X_WC_>?]Y_X7_A/^$_X7_ +MA/^#_WS_?/]R_W/_=/]S_WO_?/^'_X;_FO^:_YG_FO^=_YS_B?^*_X+_@?]X +M_WC_O]T_W/_=?]V_W7_=/]S_W/_??]]_XC_B?^4_Y3_GO^>_YC_ +MEO^/_Y+_@_^`_W?_>?]Z_WK_?/]Z_X'_@_^,_XS_DO^0_YC_F_^A_Y[_H?^C +M_Y[_G_^._XO_>O]]_V7_8O]?_V+_8O]A_W[_?/^0_Y+_J/^F_[#_L_^P_Z__ +MK?^J_Z+_IO^H_Z3_H/^D_Z/_H/^6_YG_C/^(_W[_@_]W_W+_=O]Z_WC_=O^# +M_X3_CO^-_YO_G/^I_ZC_N/^Z_\?_Q/_,_\__Q__#_[;_NO^A_Y__E/^5_XS_ +MBO^0_Y+_E/^3_YC_FO^<_YG_F/^:_YW_G/^B_Z/_L?^R_[__O?_(_\G_R/_' +M_[S_O?^W_[?_J?^J_ZW_J_^H_ZK_L_^Q_[+_M/^T_[+_N/^Y_[S_O?_#_\+_ +MR__+_\;_Q__#_\'_M?^W_Z__KO^R_[+_L/^Q_[W_O/^\_[S_P__#_\'_P?_* +M_\K_TO_3_^7_X__I_^K_ZO_K_]K_V/_#_\7_KO^L_Z'_HO^C_Z3_L_^R_\7_ +MQ?_:_]O_X__B_^W_[O_J_^G_Z/_I_]__WO_6_]C_RO_(_\7_QO_%_\3_S?_- +M_]O_W/_H_^C_ZO_J_^?_YO_:_]O_U__6_]/_U?_A_]__Y?_G_^W_Z__I_^K_ +MX/_@_]G_VO_2_]'_UO_6_]O_V__C_^/_Y/_D_^?_Z/_J_^G_[O_O__O_^?_] +M_P```0#]__S_`0#P_^O_Y/_H_]?_U?_5_]7_TO_3_];_U?_6_];_W?_>_^K_ +MZ/_X__K_#@`/`!4`$@`3`!<`"@`%`/?_^__J_^?_XO_F_^K_YO_N__+_^O_U +M__+_]O_R__#_X?_C_^;_Y/_<_]W_[/_K__+_]/_]__O_`P`%``4``@`+``T` +M"P`,`!,`#P`'``T`#``$`/7__?_[__;_]O_X__G_^O_]__O_^__[__#_\__M +M_^K_Y__I__#_\/\%``(`$P`7`"<`)0`D`"0`'P`?`!$`$0`&``8``0`!`/[_ +M__\'``0`__\"``4``P````(`!@`%``H`"0`-``X`#@`.``H`"@`$``4``P`" +M``0``P`+``\`%@`0`!D`'P`?`!L`%P`8`!@`&0`7`!4`&@`<`!P`'``6`!,` +M"``,`/__^__T__C_^/_V_P4`!0`3`!0`(``?`"0`)0`?`!\`&``6``X`$0`2 +M`!``$P`5`!T`&P`=`!X`(0`@`!H`&@`6`!D`%0`0``@`#@`*``,`^?____[_ +M^O\```(`$P`2`"8`)P`U`#4`-0`U`"X`+@`2`!$`!``&`/+_\/_V__G__/_Y +M_PD`"P`5`!0`'0`<`"(`)``H`"8`)@`I`"X`*@`B`"4`(``<``8`"P#[__C_ +M\/_Q__#_\/_U__3__O___P<`!P`/``X`&P`=`"0`(@`Q`#(`,@`S`#(`+P`B +M`"4`$``/``0``@#W__O__O_Y__O___\$``,`___]__S_``#___K_`@`&`!$` +M$``A`!\`*P`O`#$`+@`I`"L`'@`<`!``$0`+``L`!0`&``L`"@`&``<`#``) +M``L`$``5``\`%``9`!D`%@`7`!<`"P`.``D`!0#__P(`"P`*`!(`$``@`",` +M(P`B`",`(@`7`!D`%0`3`!$`$@`:`!L`(P`A`"H`*@`B`",`&0`:``8`!`#Z +M__S_\/_L__'_]O_[__C_`@`#`!4`%0`>`!X`,@`Q`#L`/@!&`$(`/@!#`"X` +M*0`7`!P`!0#___?__?_Y__/_^?\```4`_?\$``P`$@`+`!(`%0`>`!X`*``G +M`#0`-0`R`#,`+0`J`!8`&0`/``T``@`#``H`"0`.`!``'``:`!\`(0`?`!X` +M&0`:`!4`$@`0`!8`%0`-`!(`&P`:`!,`&``<`!P`&@`?`"$`(@`?`!X`(@`: +M`!8`#0`0``4``P`#``4`#``)`!L`(``Q`"H`-0`]`#L`-``G`"L`'``;``D` +M"0`+``L`!0`&`!,`$0`1`!$`%0`7`!0`$0`7`!L`'0`9`!\`(0`D`"(`(@`D +M`!X`'@`@`"``'0`=`"8`(P`B`"@`*@`E`!X`(0`=`!P`%``3`!8`&``8`!@` +M(``=`!L`(``=`!8`"@`1`!``"@`(``T`&P`7`"<`*0`S`#,`.P`X`"\`-``O +M`"D`(``G`"4`'@`>`"0`(@`=`!L`'@`4`!0`$0`0``H`"@`/`!``%``1`!H` +M'@`?`!P`(@`D`"0`(P`L`"P`*P`J`#,`-@`L`"<`)``J`"``&P`5`!@`'``< +M`"0`(0`M`#``+P`N`"@`*0`4`!(`!``'`/S_]__]_P,`#@`)`!T`(@`P`"L` +M.P`^`$(`0``_`$``/0`_`#@`-0`L`"T`'P`@`!(`$0`*``H`"@`,`!$`#0`7 +M`!T`&@`4`!T`(0`5`!(`%@`9`!P`&@`I`"D`-0`V`#P`/``^`#T`-``U`"@` +M)P`A`"$`&0`<`!P`%@`4`!H`%``0``D`#``+``D`#0`-`!P`'0`H`"<`-P`Y +M`#P`.@`X`#D`+@`N`"$`(0`6`!8`#``+``D`"P`+``@`"@`.`!L`%@`D`"D` +M.P`V`$$`10!"`$``-``U`!\`'@`-``X``P`"``$``P`.``P`%P`8`"$`(``? +M`"``(``?`"$`(P`F`"(`*P`P`#8`,``P`#8`,0`M`"8`*``<`!P`&@`8`!`` +M$@`3`!,`#@`,``\`%``4``X`'``A`"H`)@`O`#$`,0`R`"H`*``;`!T`%0`3 +M``P`#@`4`!$`&@`>`",`'@`D`"D`)``@`!T`'@`=`!\`)``A`"0`)@`K`"H` +M(@`B`!H`&P`-``P`!``$`/[___\#``(`"``(``X`#0`3`!8`&0`5`",`*``O +M`"@`,@`Y`#4`+P`J`"\`(P`?``X`$0`(``8`_?_]__C_^?_T__3_Z__J_^O_ +M[?_O_^O_^O___Q0`$0`K`"P`/P`^`#\`/P`S`#0`&@`9``0`!@#V__+_ZO_N +M_^__[/_Q__/_]__V_P$``0`#``,`#@`/``H`"0`*``H``P`#`/K_^?_U__G_ +M_?_X_P0`!P`2`!``$@`4`!(`$0`(``@`^O_X__?_^__S_^__^?_^__[_^/_\ +M_P``^/_V__3_]O_Q__#_]O_V__K_^O\!``(``0#___S____]__C_]?_Z__O_ +M^/_^_P``!0`#``,`!0```/[_^O_\__?_]/_P__/_\/_N_^C_Z__G_^3_WO_@ +M_^?_Y?_I_^K__/_[_P0`!0`-``X``P`"`/K_^O_G_^;_X?_B_^#_X/_L_^S_ +M]/_T__;_]O_Y__?_[__T_^[_Z/_F_^K_Z/_F_^G_Z?_J_^O_YO_E_^;_Z/_E +M_^'_Y?_K_^G_X__F_^G_XO_B_^3_XO_=_^'_ZO_G_^W_[?_Y__K_^/_W__3_ +M]?_C_^3_VO_7_\[_T?_6_]3_W/_<_^;_Z/_G_^7_Y__G_]G_VO_8_]?_T/_0 +M_]3_U?_:_]C_W/_?_^/_X/_E_^;_Z__K__+_\__X__;_]/_V_^__[O_=_]S_ +MSO_0_\7_PO^\_\#_P/^]_\#_P?_&_\;_RO_)_]'_U/_A_]W_\/_S__O_^?\" +M``,`]O_V_^G_Z/_4_]7_Q__&_\3_Q?_'_\;_S?_._]+_T?_-_\[_R?_(_\7_ +MQ?_"_\/_R__+_]+_T?_=_][_X?_?_^?_ZO_O_^S_Z__M_^G_Z/_=_]S_S/_/ +M_\#_N_^T_[O_O/^T_\+_R?_<_]7_X?_H_^W_Z/_=_^#_U?_3_\G_R/_"_\?_ +MS/_$_\7_S?_2_\S_S__2_]3_U/_9_];_W__C_^7_XO_K_^W_Y/_B_]O_W/_, +M_\S_O?^^_[W_N_^Q_[/_N_^Z_[C_M__"_\7_R?_%_]+_U__D_]__Z__O__'_ +M[?_F_^S_W?_5_\?_S__$_[S_NO_"_\;_P/_#_\;_Q__&_\/_PO_!_\+_PO_$ +M_]'_S/_6_]S_Y__A_^;_ZO_D_^/_UO_6_\K_R__$_\+_O/^]_[S_O/^__\'_ +MP_^__\G_SO_8_]/_W__B_^K_Z__J_^;_X/_E_]3_S__)_\W_PO_!_\?_Q?_( +M_\S_S__+_\G_S?_&_\/_O/^^_[__O?_'_\K_V__8_^K_[/_W__?_^__Y__'_ +M]/_K_^?_U__;_]3_T?_"_\3_P/^__[?_N/^Y_[?_Q/_'_]'_SO_B_^3_[__P +M__+_[__Q__3_Y?_D_^+_X?_<_]__W__;_]W_XO_?_]G_U__=_]G_U/_:_]W_ +MX/_@_^S_ZO_L_^[_Z?_G_]K_W?_/_\O_Q?_(_\C_Q__6_];_X__C__'_\O_] +M__K_^__^__S_^__X__?_\/_R_^W_[/_D_^3_W?_=_]W_W?_<_]S_YO_F_^?_ +MZ/_K_^K_Y?_F_]W_V__7_]K_W__;_^G_[?____S_"@`,`!(`$0`/`!```0`` +M`/;_]O_L_^W_ZO_I_^K_Z__C_^3_Z/_F_^/_Y/_H_^?_[?_N__7_]?_^____ +M`0#^_P0`!@`$``,``@`"``8`!P`(``4`!P`,``4```#W__K_]/_T__/_\/_T +M__G_`0#\_P(`!P`&``(`_O\"`/K_]?_U__K_^__W_P0`!P`-``L`$@`2``P` +M#0`(``<`________```#``(`!0`&``L`"0`$``8`!0`$``(``@`$``4`"0`' +M``D`"P`,``L`"@`*``D`"0`,``L`"0`+``P`"P`(``@```#___;_]__Q__'_ +M^?_X_P8`"``>`!L`+``M`#$`,@`L`"H`&0`;``8`!`#V__C_[__L__3_]__P +M_^[_^O_[__O_^O\(``L`$0`-`!8`&@`<`!D`%P`8`!``$``-``T`"0`+`!`` +M#0`3`!4`$P`1``\`$@`'``0`_O\!`/K_]O_Z__[_`0#]_P4`"@`*``4`!0`) +M``4``@`!``,`"``&``\`$0`6`!0`&``:`!``#@`)``H``0`!``4``P`$``@` +M$P`-``X`%``6`!,`"0`)``8`!P#Z__C_]__Z__'_[__M_^[_[?_M__;_]/\$ +M``<`(``>`"X`+P`[`#H`-``W`"H`)@`0`!4`___Z__'_\__O__'_\?_M__+_ +M^/_T_^[_]O_Y__G_^/\#``0`$P`3`!X`'0`I`"H`*0`H`",`)0`>`!L`$0`6 +M``T`!P#\_P(`^O_T_^O_\?_R_^W_\__X_P<``P`2`!0`'``;`"``(0`7`!8` +M#P`0``4`!``#``,`"``)``D`"0`-``T``P`!``0`!P#___S_!P`*``T`"P`8 +M`!@`&@`;`!4`%``2`!$`!P`+``@``@`$``H`!@````0`"0`"`/__`@`$``0` +M`@`'``@`$0`1`!8`%@`:`!H`%P`6`!8`&``7`!0`%0`9`!P`&``5`!@`"@`) +M`/___O_O__'_[__M__?_^/\*``H`'0`<`"0`)P`I`"4`&@`=`!0`$P`"``(` +M_O____G_^/_]__W_`P`%``P`"@`8`!@`(``B`"H`)P`F`"@`'``<``X`#0#_ +M_P``]?_V__C_]/_Y__[_!0````8`"P`+``<`!0`)``P`"``0`!,`&P`:`"8` +M)``A`"4`(P`>`!$`%@`0``P`!P`*``H`"``$``4`!P`%`/W_`0#]__G_\__U +M__K_^?\!``(`"P`+`!0`%``;`!H`(``@`"0`)0`I`"@`)0`F`"(`(0`3`!0` +M"``'`/C_^/_W__G_^__W_P(`!P`/``L`#@`1`!``#@`%``8`!P`&``D`"0`3 +M`!4`'P`=`"D`*0`G`"<`(P`C`!D`&0`3`!0`#@`+``L`#P`*``<`!0`'``8` +M!0`'``8`#0`/`!(`$0`7`!@`%@`4`!(`$P`2`!,`%0`2`!P`(``I`"8`+@`N +M`"@`*P`@`!P`#P`2``D`!P`&``@`#@`,`!4`%P`@`!X`(@`D`",`(0`>`!\` +M'``<`!P`&P`:`!L`&``7`!(`$P`2`!(`$@`0`!D`&P`E`",`*@`L`#$`,``M +M`"T`)P`G`"(`(``7`!P`'``6``X`%``0``H`!``)``<``P`-`!``&0`7`"L` +M+0`S`#(`-``T`"L`*@`;`!P`#@`/``D`"``-``T`%0`4`!T`'P`F`",`(0`D +M`"(`(``<`!P`'``>`!P`&@`8`!D`$P`4`!D`%0`0`!@`(@`:`!P`(0`@`!X` +M&0`8``P`#P`(``8`"P`+`!4`%0`G`"@`-0`T`#T`/0`^`#X`+P`O`",`(P`5 +M`!8`#0`,``@`"``"``(`_O____S_^O___P(`$0`-`!P`(0`U`#``-P`\`$4` +M/P`Y`#\`.``U`"H`*@`@`"(`%0`0``<`#P`!`/C_]O_^__W_]_\(``P`%``3 +M`"8`)0`G`"D`*P`I`"8`)P`C`"(`(P`E`"@`)0`G`"H`*``E`!T`'P`7`!4` +M"P`.`!(`#0`2`!@`(0`=`"8`)P`D`"4`)@`D`!H`'``5`!4`%@`3``L`$``6 +M`!$`#@`2`!@`%@`A`"$`*P`K`#@`.``V`#8`-``V`"8`)``8`!D`$``/`!$` +M$0`3`!0`'``<`!L`&@`9`!L`$``/`!$`#P`)``T`&0`5`",`)0`L`"T`-@`S +M`#$`-``R`#``+``M`"0`)``>`!T`$``2``L`"0`!``(`!``$``D`"0`8`!@` +M)P`G`"T`+``O`#$`+``J`"4`)@`B`",`'P`;`!4`'``3``L`!P`/``8````' +M``L`%``1`"``(@`R`#(`.0`X`"\`,0`E`"(`$P`7``X`"0`'``L`"@`'``L` +M#0`2`!(`%``3`!<`%P`7`!@`'0`<`!@`&``:`!L`$P`2`!4`%P`9`!8`'@`B +M`"L`)0`F`"T`*``B`!0`&``&``0`_?_]__;_^/____S_!P`)`!``$``3`!$` +M#0`1`!``"P`0`!0`%P`5`!L`'``>`!T`'``=`!D`%@`2`!<`#P`*``H`#@`' +M``0`!0`'`/K_^/_X__O_]O_S__K__/\&``4`"@`*``X`#@`*``P`#@`+``L` +M#0`0``X`%@`7`!L`'0`:`!<`$0`4``(`___X__G_\__U__C_\__X__W__?_[ +M__S_^__S__;__/_X__3_]O\``````P`$``P`"P`-``T`"P`*``@`"@`#``(` +M`@`#`/___O_Y__G_\__S__'_\?_Q__#_]O_X__O_^?_\__[_^/_V_^S_[?_F +M_^3_W?_@_^S_ZO_Y__K_$``0`!L`&0`:`!T`%P`5``8`!P#W__?_[/_K_^'_ +MXO_<_]S_V__:_]S_WO_G_^3_\?_T__[_^_\&``D`"0`'``@`"0#Y__C_]__X +M__'_\?_T__+_\?_U__#_[/_H_^O_Y/_C_^/_X?_E_^C_\O_N__/_^?_^__?_ +M\?_X__7_[O_F_^O_Z__H_^C_ZO_N_^W_Z__K_^;_Y__>_]W_U__6_]3_U__< +M_]C_Y/_I__3_\/_Y__K__?_^_____O_]____^/_T_^O_\/_;_]?_S?_0_\'_ +MP/_"_\#_RO_-_]W_V__O__#_^/_X__W__?_U__3_\/_R_^K_Y__K_^__Z__G +M_^S_[O_A_^'_X/_@_]7_U/_5_]?_V/_4_]S_X?_B_][_Z/_K_^7_X__H_^K_ +MYO_D_^;_Y__C_^/_W/_;_]?_VO_5_]#_U__=_^/_W?_O__3__/_X__O__?_V +M__;_Z/_G_]S_W?_4_]3_SO_._]#_T/_,_\O_S__0_\O_R__2_]/_VO_8_^?_ +MZ?_V__7_`0```/S__O____S_[/_P_^O_Z/_@_^+_W/_9_]?_V?_._\__S__, +M_\7_R/_1_\[_U/_7_]__WO_F_^;_YO_E_^W_[?_N_^___/_]_P(````!``,` +M^O_W_^C_ZO_3_]3_R/_&_\#_P?_'_\C_V?_6_^/_Z/_V__'_\O_U__C_]__P +M__#_[?_M_^+_X__@_][_V/_<_][_V/_<_^+_Z?_D__#_]/_U__3_^O_X_^[_ +M\/_K_^K_W?_=_]W_WO_?_][_YO_F_^?_Z/_H_^7_W__D_^;_X?_B_^;_[?_K +M__'_\?_W__?_[__Q_^S_Z/_A_^?_Y__@_^;_[?_S_^S_[O_T__3_\?_N__#_ +M[/_I_^?_Z__H_^/_X__I_^;_X?_C_^7_X?_B_^?_Y/_I_^S_\__R__?_]O_T +M__7_[O_M_^O_Z__F_^C_[O_K__+_]?_^__K__/\``/S_^/_N__'_Y?_C_][_ +MW__B_^+_Y?_E__'_\?_O_^W_\/_T_^__Z__K_^[_\O_Q__+_\?_V__C_^?_V +M__7_^/____W_^O_[_P4`!0```/[_^/_\_^W_Z?_B_^3_V?_8_]__X/_F_^;_ +M[O_O__G_]O_U__G_]O_R__#_\__T__/_]__V_P,`!0`#``(`"@`*``<`!@`! +M``(`_?_\__7_]__S__#_ZO_N_^7_X/_7_]S_V__7_]W_W__J_^K_^?_Y_P@` +M"``/`!``&``5`!0`&``7`!0`#@`0``<`!P#Y__C_Y?_E_]?_V?_4_]#_V__? +M_^W_Z__]__[_#``,``X`#``)``L```#^__K__/_X__?__/_\__W__O\#``$` +M`````/__`0#]__O___\"`/O_^/_[__W_]?_R__3_^?_\__;_^O\!``<``@`' +M``D`"0`'`/W____S__/_[/_K_^S_[O_X__7_```!``L`#0`/``P`#0`/``@` +M"``'``0`__\$``(`_?_X__S_]/_R_^S_[/_C_^7_Z/_E_^W_[__W__;_`0`" +M``8`!@`+``H`!P`'``@`"0`#``$`_?\``/C_]?_L_^W_Y/_F_^;_XO_G_^S_ +M^O_U__[_`0`'``<`!0`#`/__`@#\__C_\__W__?_]/_R__3_\O_P_^G_Z__E +M_^3_W/_<_^#_X/_J_^G_]__Y_P0`!``0``X`#@`0`!``#0`"``8`^O_W_^S_ +M[O_>_]W_U?_4_\W_S__._\S_V?_<_^K_Y?_U__O_`@#\__O_`0#Z__7_]/_V +M__3_]?_Z__C__?\```(`_O_X__K_[?_L_]S_W__7_]/_S?_1_];_TO_6_]?_ +MV__<_^'_X/_H_^G_\/_P__;_]O_]__O_]/_X__C_\O_C_^G_X__@_]G_VO_9 +M_]K_X/_>_^#_X/_=_^#_X?_=_]7_VO_C_][_V__>_^3_XO_:_]W_UO_5_\[_ +MS/_*_\O_T__4_][_W/_N__/__/_T__?__O_U__'_Y/_E_]C_VO_0_\S_Q__* +M_\7_Q?_-_\O_Q__*_]?_U/_3_]7_X?_@_^'_XO_D_^+_W?_?_]K_V/_7_]G_ +MW?_<_^/_X__H_^C_Z/_G_]__X?_3_]+_R__+_\7_Q?_*_\G_T?_3_];_U?_: +M_]O_UO_5_]/_U/_5_]+_TO_8_]__V/_8_][_X?_=_]K_V__:_]S_VO_7_]?_ +MV?_9_]C_U__7_]3_U?_2_\__SO_4_]C_T/_6_]__X__:_^#_Y__?_]O_V/_9 +M_]'_TO_4_]/_U__7_]C_V?_@_][_V?_;_]?_U__5_]/_U?_7_][_W?_J_^G_ +MZO_M__'_[O_G_^C_X?_@_]C_VO_6_]3_T__5_];_U/_4_]3_TO_3_]?_U__5 +M_]3_W?_@_^'_W/_A_^7_YO_C_^7_YO_J_^W_]?_O__G____^__K_^O_[_^;_ +MY__5_]/_P/_"_[O_NO^\_[W_S__-_]W_W__L_^K_]?_W__K_^?_^__W_^/_[ +M__K_]?_J_^__ZO_H_]S_VO_:_][_VO_6_^'_X__D_^7_[O_J_^3_ZO_H_^+_ +MXO_F_]__WO_G_^7_XO_F_^O_Y__E_^C_Y/_C_^3_X__I_^K_\?_P__G_^O_] +M__W_^O_Y__#_\?_E_^7_X__B_]W_W?_D_^7_X?_@_^+_Y?_?_]S_U__8_]O_ +MVO_;_]O_YO_I_^W_Z?_P__7__?_W__K__O\$``,`_O_]__G_^__L_^G_V/_< +M_\O_Q__`_\3_P?^\_\W_T?_:_]G_[?_L_^S_[O_W__7_[?_M_^__\?_O_^W_ +MZO_K_^W_[O_D_^#_VO_?_]'_SO_,_\W_S?_,_]'_TO_:_]G_W/_>_^C_Y?_H +M_^K_[O_N__#_[O_K_^W_X__B_]?_U__&_\C_Q?_`_[[_P__/_\O_UO_:_^?_ +MY/_G_^?_ZO_L_^?_Y?_D_^;_Y/_C_^/_XO_<_][_V?_7_\C_RO_'_\;_P/_! +M_\K_R?_,_\S_V/_8_]K_V?_<_]__V__7_]G_WO_<_];_V__@_][_V__;_]W_ +MW/_:_];_V?_:_];_V/_<_]O_V/_4_]?_T/_+_\+_R?_+_\/_Q/_+_]K_UO_> +M_^#_Z?_G_^3_YO_>_]O_T?_5_\O_R/_/_]#_T?_1_^/_X?_A_^;_\/_I_^7_ +M[/_K_^7_W__C_]G_V/_-_\W_Q/_#_[W_O_^]_[K_P?_%_]3_S__<_^+_[?_H +M_^W_\/_N_^W_Z__J_^?_Z?_G_^7_YO_H_^#_WO_6_]G_S__*_\/_R/_'_\/_ +MQO_*_]+_T/_8_]?_W/_>_]O_V?_;_]W_W/_<_^3_X?_E_^K_\/_J_^W_\__O +M_^G_Y__L_^+_W__>_]__VO_:_]7_U/_-_]#_SO_)_\;_S/_3_\W_VO_>_^+_ +MX?_L_^O_Y?_G_^;_Y/_=_]W_W__@_^+_XO_I_^C_Z?_J_^C_Z/_9_]C_T/_1 +M_\G_R?_)_\C_SO_0_]7_T__:_]S_W__<_][_X__?_]G_W/_A_]K_UO_6_]C_ +MS__/_\O_R__-_\S_T/_0_]?_U__:_]O_W__?_]?_U?_3_]7_RO_(_\?_R__/ +M_\K_T?_6_]O_UO_2_];_U/_2_\;_QO_"_\/_PO_!_\'_PO_,_\O_R/_)_]#_ +MS__-_\[_TO_1_];_U__9_]G_W/_<_]3_U/_+_\K_PO_#_[G_N?^Z_[G_M_^X +M_[G_NO^__[S_OO_#_\K_P__,_]'_U__5_]3_UO_1_\__Q?_&_[K_N?^O_Z__ +MKO^O_[;_MO_)_\C_T?_2_]K_V/_1_]/_S?_+_[[_P?^U_[/_LO^Q_ZG_J_^M +M_ZO_JO^L_ZW_K/^S_[+_N?^[_\+_P/_(_\G_R/_(_\C_R/_#_\3_R?_(_\C_ +MQ__(_\O_PO^]_[+_N?^K_Z/_E/^=_YC_D/^6_YK_H_^B_['_L/^V_[C_P?_` +M_\3_P__)_\O_S?_,_\W_SO_._\W_Q/_$_[C_M_^F_ZG_H?^>_YO_GO^=_YO_ +MHO^B_Z;_I?^M_[#_M?^R_[S_O__#_\#_Q?_'_\3_P_^Z_[O_MO^U_ZG_J_^J +M_Z?_J/^L_[+_K?^Q_[;_M?^R_Z[_L/^N_ZW_K_^M_ZW_LO^]_[;_L_^[_[K_ +MLO^I_[#_I?^?_Y[_HO^?_YW_J/^I_Z__KO^Y_[K_OO^\_\/_QO_$_\'_PO_$ +M_[K_N?^Q_['_IO^H_YW_F/^4_YG_G?^:_Z7_I?^P_[3_N_^U_[G_OO^W_[3_ +MK_^O_ZG_J_^M_ZO_L/^Q_[7_MO^T_[+_L?^S_ZS_JO^K_ZO_I?^G_ZS_J_^K +M_ZO_K/^L_ZO_K/^L_ZC_K/^R_[3_KO^Q_[?_MO^Q_[3_M_^L_ZG_I/^G_Z#_ +MG_^B_Z+_J/^H_ZG_J/^N_['_L_^O_[;_N?^Z_[K_Q/_!_\/_R/_$_[[_M/^Z +M_Z;_H?^7_YO_D/^-_Y7_EO^:_YS_K/^H_['_MO^\_[;_MO^]_[W_M_^Y_[S_ +MN/^X_[S_N?^Q_[;_N/^S_ZO_K_^U_['_K?^Q_[/_K_^G_ZO_IO^D_YO_FO^: +M_YS_F/^6_Z3_IO^M_ZW_N_^Z_[O_N_^\_[W_N/^W_[C_NO^X_[;_NO^[_[S_ +MO/^V_[7_K_^R_ZC_I/^=_Z'_G_^<_YW_GO^C_Z3_IO^C_ZG_KO^N_Z?_L?^Y +M_[S_M/^]_\7_Q/^\_[K_P/^Z_[;_K_^R_Z[_K?^O_Z__L/^P_[3_M/^L_ZW_ +ML/^N_Z3_J/^M_Z?_J/^N_[7_L?^V_[C_M_^W_['_L/^S_[/_K?^N_[C_MO^Y +M_[S_Q?_"_\7_R/_"_\#_O?^]_[7_M_^U_['_K/^Q_[/_KO^J_Z__MO^R_Z[_ +MLO^W_[+_M_^\_[W_N/^Z_[[_N/^W_[;_M?^S_[7_NO^W_\+_Q?_,_\K_TO_3 +M_\S_R_^__\#_M/^S_Z?_J?^G_Z3_J/^K_['_K?^X_[W_Q__"_\C_S/_0_\W_ +MT/_2_]#_SO_(_\S_QO^__[/_N_^U_Z[_K/^R_[?_L_^[_[W_RO_*_\S_RO_3 +M_]?_U?_0_]3_VO_8_]+_U/_9_]'_SO_'_\C_NO^[_[;_M/^N_[#_O/^[_\+_ +MP/_._]+_U__3_]7_V?_=_]K_V/_9_]__W__C_^'_X__F_^/_XO_<_]S_U?_5 +M_\O_R?_%_\?_P/_`_\3_P__#_\7_S?_*_]'_T__>_]W_Z__K__'_\__V__3_ +M\O_S_^7_YO_?_]O_U/_9_]G_U?_=_^#_WO_=_^;_YO_@_][_W?_@_]K_V?_> +M_][_X__B_^?_Z?_J_^;_Y?_K_^C_XO_A_^;_ZO_E_^+_Z/_Q_^O_ZO_O__'_ +M[?_T__?_]O_U__O_^__Z__K_]?_U_^K_Z?_=_]__U/_3_]#_SO_4_]C_X/_: +M_^?_[O_Y__3_^?_\_P(````"``(`!0`'``D`!@`!``0`^O_Y_^__[?_<_^'_ +MX?_9_]/_W?_G_]W_Y__N__/_\/_U__7_^O_[__O_^O\#``(`_/\```(`_?_Z +M__[__?_Y__;_^O_[__G_^O_[_P``___\__O_^/_Z_^[_[O_P_^[_ZO_N__C_ +M\?___P<`#@`'``\`%``-``H``0`#`/G_^/_T__/_\?_S__3_\?_X__O_^?_X +M_P4`!``&``<`#@`-``\`$0`0``X`"0`*``$``0#[__G_]?_Y__K_]__]__[_ +M`0````$``P`"````_O___P(``@`#``(`!P`)``X`#``*``L`"``(``,`!``# +M``(`!0`%``H`"0`-`!``#@`+``<`"P`!`/W_^/_[__?_]?_X__G_^__[__W_ +M_/___P(`!@`!``8`#``/``D`$@`7`!,`#P`1`!0`#``*``H`"P`$``0`"P`) +M``8`"0`+``D`!0`%`/[_``#\__C_]O_[_P``_?\'``8`$0`4`!4`$P`5`!8` +M$0`0``L`#``/``T`"P`/`!8`$@`2`!0`&``8`!0`$@`3`!4`#0`.``\`#0`& +M``@```#^__O__/_W__C____]_PD`#``3`!``'P`B`"(`(``D`"0`&P`=`!P` +M&@`8`!D`&0`:`!P`&0`3`!<`$P`/``<`"@`&``4``P`"``D`"P`+``@`$``2 +M`!0`%``7`!0`&P`A`"<`'P`F`"\`-@`K`"8`,0`O`"8`&P`C`!P`%@`/`!(` +M#0`+``H`#``-``P`$``0`!<`&0`D`"``+@`R`#D`-P`]`#X`.``W`"P`+@`? +M`!L`$``7`!8`#@`5`!L`)@`C`"L`+0`R`#(`-@`T`#4`-0`S`#8`-@`S`#`` +M-``Q`"P`*P`N`"0`(P`H`"D`)@`F`"P`*P`Q`#``+0`Q`#,`+@`O`#0`.0`T +M`#P`/P!)`$@`2`!*`$L`1P!``$0`.P`W`#(`-0`Q`#``-``T`#<`-@`X`#L` +M/@`Y`#@`/@!``#H`00!&`$<`0P!)`$T`3`!)`$H`2P!$`$0`0@!``#\`0@!` +M`$``2@!&`$4`2@!.`$D`2@!.`%$`4`!3`%(`5`!5`%,`4@!,`$X`10!#`#L` +M/0`Y`#@`/@`^`$``00!+`$D`3@!1`%8`4P!3`%4`6@!:`%X`7`!E`&<`:@!I +M`&<`9@!9`%P`3P!,`#X`/P`W`#<`-0`V`#D`-@`\`$$`1P!``%,`6@!@`%L` +M;`!O`'``;P!Q`'``:`!J`&``7@!3`%4`3`!+`$T`30!.`%``5@!3`%0`5P!2 +M`%``4`!0`$D`2P!1`$\`4@!3`%D`6`!A`&(`7P!=`&$`9`!D`&$`80!D`&<` +M8P!A`&8`90!?`%@`7P!9`%,`3P!3`%8`5`!8`%D`70!<`%L`6P!@`&``6P!< +M`%T`70!?`%X`7@!>`&,`8P!>`%\`7@!=`%\`8`!?`%X`:`!J`&\`;0!R`',` +M<0!P`&@`:`!<`%X`5`!2`%(`4P!2`%$`7@!<`%X`8@!I`&8`:`!I`&<`:`!K +M`&<`9P!K`&<`90!C`&0`80!@`%\`8`!@`%X`8@!E`&D`9@!J`&T`:0!G`&`` +M8`!>`%\`60!8`%T`7@!B`&(`90!D`&8`90!@`&0`8`!;`%D`70!;`%D`80!? +M`%\`9`!J`&8`9P!H`&,`9`!B`&``8`!B`&$`8`!B`&,`8`!?`%\`7@!=`&`` +M70!9`%X`8@!@`%X`80!@`%P`7P!6`%,`4P!5`%$`3P!4`%<`60!6`&``8@!B +M`&$`9@!G`&,`8@!H`&@`9P!H`&L`:@!E`&8`8`!@`%@`5@!&`$D`1P!$`#P` +M/P!(`$8`2P!,`%@`5P!@`&``8@!C`&8`9@!H`&<`9P!H`&D`9P!A`&0`8`!= +M`%<`6P!9`%4`4P!5`%0`5`!0`$X`20!,`$4`0P!``$``0@!%`$\`20!7`%\` +M:@!A`&L``!H`&P`@`!P`%@`9`"`` +M'``5`!,`&``8`!4`"P`,``\`#P`+``L`"P`*`!``$0`.``T`$@`3`!``$``0 +M``\`%@`8`!L`&``B`"0`(0`A`"(`(0`;`!T`%@`5`!@`%P`4`!8`'@`<`!T` +M(``?`!P`&@`<`!D`&``3`!,`$``2``X`#``/``\`$0`2`!4`$P`9`!P`)0`B +M`"<`*@`U`#$`*@`N`"X`*P`>`"$`&P`8`!4`%0`0`!,`%@`2`!$`%0`8`!8` +M&``7`!<`&@`?`!T`(@`B`"H`*P`J`"D`*0`J`"8`)0`A`",`(P`?`!P`(0`E +M`!\`'``B`",`'0`9`!\`'P`9`!D`'@`@`!L`(P`G`"8`)``I`"D`*0`J`"D` +M*0`K`"D`)0`H`"8`(@`<`"$`&``3`!4`&@`3``X`&@`>`"0`(@`O`#``-P`W +M`#D`.``V`#8`*P`L`"4`)0`@`!\`&0`;`!D`%@`3`!8`&``6`!8`%P`=`!P` +M(@`D`"D`)0`K`#$`,``I`"@`+P`L`"8`)0`K`"L`)0`D`"D`)P`D`!X`'P`; +M`!T`&0`5`!@`'``?`!T`(P`B`"0`*``H`",`(0`E`"(`'P`<`!T`'P`@`"(` +M(0`A`"(`)0`B`"``)``E`"$`(P`G`"H`)@`I`"P`*@`G`"@`+``B`!T`'``B +M`!H`%0`5`!@`%P`5`!4`%0`6`!D`&@`7`!H`'``G`"0`)0`I`#0`,``M`#$` +M,P`Q`"H`*0`E`"<`)``C`!T`'0`@`"$`&0`8`!<`%@`,``\`$``-``L`#0`7 +M`!4`&@`<`"@`)P`N`"T`,0`R`"\`,``N`"L`)@`K`"4`'@`7`!T`%0`1``H` +M#0`.``T`#P`/`!8`%0`>`"``(P`@`"$`)``=`!T`'0`9`!4`&P`=`!8`%P`> +M`"``'``=`!X`'@`>`!L`&P`=`!P`&P`>`!\`'``=`!X`&0`:`!D`%P`3`!0` +M%0`5`!4`%0`:`!H`'@`=`!T`'@`=`!P`&P`<`!@`&``:`!D`&``9`!D`&0`: +M`!D`%P`8`!P`&P`:`!H`'P`B`",`'@`>`"0`'@`8`!0`&``1``\`$``1`!`` +M$0`9`!<`&0`:`!P`'``9`!D`%P`7`!8`%@`5`!0`%P`8`!<`&``;`!<`%@`< +M`!T`&``9`!L`'``=`!X`&@`7`!X`&@`3``\`%``1``T`#``0`!0`$``2`!8` +M%P`4`!4`%@`2`!,`$P`0`!$`%``7`!8`&@`8`!T`(0`@`!L`&0`>`!H`%@`3 +M`!8`%P`4`!,`%@`4`!(`$@`4``\`#0`-``\`#@`+``T`$``1``\`$0`1`!(` +M%0`1``T`$@`5`!8`%``9`!H`(@`A`!\`(@`D`"``&@`=`!@`%P`1`!$`#``- +M``T`"@`"``4`!@`#`/O___\&``(`!``&`!8`%``:`!X`*@`E`"<`+``F`"$` +M(``D`!8`%``3`!,`"@`+``D`"0`&``4`!``&``D`!0`&``P`#@`)`!``%``3 +M``X`#P`4`!(`#@`,``\`$@`1`!4`%``5`!<`&@`8`!(`$@`-``\`!P`%``,` +M!``#``0`"0`&``<`"@`-``P`#@`,``L`#P`/``H`"``.``T`!P`%``D`!@`$ +M`/__``#]__[_`0#^_P,`!P`-``@`#P`5`!(`#0`/`!,`#0`*``D`"@`%``4` +M`@`"`/O__/_\__K_\O_T__G_]__V__G_`P````8`"``.``T`#@`-``<`"@`% +M``(`_O\``/[__?_[__O__/_]__G_^/_Y__K_]O_T__C__/_[__;__/\!``,` +M_____P(``0#___W____]__O_]O_W__?_^/_R__#_[__R_^__Z__K_^__]?_R +M__G__/\!`/[_"``*``,``@`#``0`^O_Y__3_]/_O_^__[/_M_^S_[/_J_^G_ +MY__H_^?_Y?_E_^C_[__L_^[_\/_Y__C_^/_X__O_^________?_\_P```0#Z +M__O_^__Y_^[_\/_K_^C_WO_@_^'_XO_A_]__Y__H_^S_Z__P__'_]?_V__?_ +M]/_Y__S__/_Y__O__O_[__C_]?_Y__/_[O_I_^W_Z__I_^O_ZO_O__+_]O_R +M__+_]__W__+_Z__O_^W_ZO_I_^O_Z/_G_^S_[?_L_^O_\O_T__;_]/_Z__K_ +M```!``$``0`!``$`^__Z__+_]/_O_^S_ZO_N_^[_ZO_O__+_\__Q__3_]O_U +M__+_[O_Q__/_\O_Q__'_]__V__G_^__\__G__/____W__?____O_^_\!``$` +M^O_X_____?_X__+_]O_W__+_[/_P__?_]?_S__3_^O_[__W_^?_W__K__O_] +M__O_^_\```$``@````,`!0#___[_``#___C_^O_\__K_^O_]_P``_?\```(` +M`0```/_____Z__O____]__K__/\#``,``@````(`!0`$``$`__\``/_____] +M__[__O_]____``#^__W__O_^_P```0`!`/__!0`(``<`!0`)``H`!0`$``4` +M!`#\_P````#\__W_```!`/__`0`!`/K__/_]__S_]/_T__C_]__X__K_`@`` +M``<`"0`-``T`$@`0``\`$@`1``X`"0`+``<`!0#[__W_^/_X__+_\/_M__#_ +M\__O__7_^/\!`/__!``&``T`"P`)``L`#``)``@`"@`&``8`"@`*``8`!``$ +M``<``@#]__S_`P#^__C___\#``(`__\!``,``P`!`/W_``#[__C___\``/O_ +M^_\$``4`!P`&``<`"``+``@`!0`(``@`!P`'``@`"``'``<`!P`&``8`!0`% +M``(``P`'``4````!``<`!P#]__S__O____;_]O_V__7_^/_Z__[__/\(``@` +M#@`/`!4`%0`5`!0`$0`4``X`"0`&``H``0#______O_U__C_]?_R__/_]?_V +M__3_^O_]_P0````'``H`#0`,``X`#0`-``\`#0`+``T`#0`)``H`!P`&``(` +M`@#[__S__?_\__C_]__^_P$``P#^_P8`#0`/``<`"P`3``T`!0`+`!(`"0`$ +M``H`#@`%``,`!@`%`/[_```!`````0`!``$``P`+``D`#``,`!``$@`2``\` +M#@`0``\`#P`.``T`"P`.``P`!@`$``L`!@``````!0`#````!``$``@`"0`+ +M``H`"P`-``T`"P`+``L`#0`.`!$`#P`5`!<`%P`6`!8`%@`2`!,`#``*``@` +M"@`&``0`!``%``D`"0`)``D`#@`-``T`$``3``X`$0`6`!0`$``2`!0`#@`/ +M``X`"P`)``P`$``-`!(`%``8`!<`'@`@`!\`'``=`!\`%P`6`!,`%``-``T` +M#0`-``H`"0`+``P`"0`*``T`#``/``\`%@`6`!P`&P`>`"$`)0`B`!P`'P`? +M`!L`&``<`!<`%``8`!H`%0`4`!0`%0`3`!(`#0`.`!``$``.``T`$``1`!(` +M$@`3`!$`$@`6`!@`%``8`!L`(P`@`"(`(P`F`"<`)0`C`!L`'@`;`!@`#P`1 +M`!,`$0`-`!``$P`0`!$`%``1`!``%``3`!(`%``7`!4`&0`:`!L`'0`@`!P` +M'``?`!T`'0`?`!T`&@`=`!X`&P`9`!H`%``6`!4`$@`*``T`$@`0`!``$``5 +M`!8`&P`9`!8`&``:`!L`&``3`!,`&0`9`!,`%@`:`!@`&0`;`!<`$P`7`!<` +M$@`0`!0`%``3`!0`%0`9`!8`%0`9`!H`%0`3`!@`%P`4`!D`&@`7`!<`&@`9 +M`!,`%0`4`!(`#``.``T`"P`,``X`$0`/`!,`%0`7`!4`&0`<`!P`%P`;`"$` +M(``:`!P`(@`>`!H`%``6`!,`$``'``L`"P`'``<`#``2``T`$P`5`!H`'``< +M`!@`%P`;`!H`%@`3`!4`$@`4`!,`$``-``\`%``1``P`#P`8`!8`%@`7`!T` +M'0`=`!P`&0`;`!0`$@`0`!(`#``*``T`#P`,``L`$``0``T`#P`0``T`"P`. +M``\`#``1`!0`%``1`!P`'P`;`!D`(``@`!P`'``<`!X`&P`7`!8`'``6``\` +M"P`0``D`!@`!``,``@`#``<``P`)``T`%P`3`!<`&P`B`"``(0`C`"@`)0`B +M`"0`)@`D`!L`'0`8`!<`#0`/``H`!P`"``0`!@`&``L`"@`.``\`%0`5`!4` +M%``6`!@`&``6`!@`&@`<`!L`&P`:`!H`'0`7`!(`$@`7`!$`#P`0``X`$``5 +M`!0`#@`2`!<`%``1`!0`%P`6`!$`%``;`!@`$@`3`!@`%``0``T`#P`.``X` +M#0`-`!,`$P`8`!<`'``<`!X`(``?`!P`&0`;`!@`&``6`!,`$0`6`!8`$``. +M`!0`$@`.``T`#@`/`!``$0`.``\`%``6`!(`%0`7`!0`%``5`!,`$P`5`!<` +M%@`7`!D`&``7`!<`%P`4`!(`#@`1``P`"P`,``T`#@`,``X`$``3`!$`#@`1 +M`!(`#P`3`!4`$P`1`!<`&``2`!(`$0`0``D`"@`'``<`!``"``0`"``+``8` +M#0`0`!8`%@`7`!4`%P`9`!,`$P`3`!``"P`/``X`"@````0`!@`"`/K__O\$ +M`````P`%``<`!@`-``X`"0`(``X`#P`&``4`#0`-``@`!P`/`!$`$@`/``\` +M%``3``X`"``,``H`!@`#``8``P`"`````0```/___/_\____``#___[_!``& +M``H`!P`,``\`#P`,`!$`%``,``D`#@`2``P`!P`(``T`#0`(````!0`%```` +M^/_]__W_^/_Z_____?_Y_P(`!0`!`/__`@`#``$``0`#``(`!@`)``L`!@`) +M``X`"``$``4`"0`!`/[__/_]__S__/_Y__G_^O_[__O_^?_W__K__/_Y__7_ +M]__Y__G_^?_X__O__/_X__?_^__\__C_]__Y__K__?_\__K__/\!`/__^?_[ +M__C_]/_R__C_\?_L__+_]O_R_^__]?_W__/_\O_Q__+_[?_L_^K_ZO_J_^O_ +MZ__K__#_[__Q__+_]__T__+_]__\__;_\__Y__O_]O_O__+_[__N_^?_YO_A +M_^+_X__B_]__X?_H_^?_[?_N_^__[?_S__7_\?_O__#_\__P_^[_[/_M_^S_ +MZ__E_^;_X__B_]__X/_>_][_YO_E_^;_Y__P__#_[__O__#_\/_M_^S_Z__K +M_^?_Z/_E_^?_Z/_D_^#_X__C_^#_W/_?_][_W?_;_]K_XO_C_^#_X/_G_^;_ +MY__J_^K_Y?_M__+_\?_M__+_]?_S__#_Z__O_^;_X?_>_^/_UO_1_]?_W/_7 +M_]/_W/_>_^'_X?_F_^3_Z/_J_^O_ZO_M_^[_[__M_^W_\/_O_^W_Y__G_^7_ +MYO_A_^#_X/_@_^'_X__@_][_XO_B_^'_XO_?_][_XO_B_^#_X?_F_^3_Y/_G +M_^7_X?_A_^;_X?_<_][_X__C_][_X?_E_^G_Y__H_^C_Z/_J_^C_Y?_D_^;_ +MX?_B_^+_WO_;_^'_W?_8_]3_UO_3_]3_T__0_]+_UO_=_]O_W?_<_^;_Z/_E +M_^/_Z?_J_^;_YO_E_^7_X__B_]W_W__<_]K_T__4_]/_TO_-_\[_TO_2_]+_ +MT?_8_]G_U?_5_]K_V/_3_];_V?_7_]C_V/_<_][_X/_=_^#_XO_:_]G_VO_; +M_]/_T?_/_]+_S/_(_\O_SO_)_\C_S__/_\W_SO_1_\[_U?_9_]7_T?_5_]O_ +MU?_._\S_TO_*_\3_P?_'_\;_PO_"_\3_R__*_\3_QO_0_\O_P__*_\[_Q__$ +M_\G_R/_%_\C_R?_#_\+_P?_$_\#_O/^Z_[W_P/^]_[W_P/_!_[__PO_$_[W_ +MN__!_\/_M_^U_[W_OO^Z_[K_O?^\_[__P?^__[W_O__`_\#_OO^\_[[_O_^^ +M_[O_O?^Y_[;_M/^T_ZS_L/^P_ZK_JO^Q_[3_K?^P_[3_M_^V_[/_M/^V_[/_ +ML/^T_[7_LO^U_[?_N/^V_[;_MO^U_[;_L?^Q_Z__KO^L_ZW_J_^J_ZG_J?^K +M_ZS_IO^E_ZG_J?^F_Z?_JO^I_ZW_KO^N_ZW_K_^P_Z__K?^K_Z[_KO^L_Z[_ +MKO^O_['_MO^R_[#_L_^O_[#_KO^I_Z3_JO^H_Z3_I?^F_Z3_IO^G_Z/_I?^H +M_Z3_HO^H_ZO_JO^F_ZW_L?^S_['_M?^T_[+_M?^W_[+_K_^T_[7_LO^O_['_ +ML_^Q_ZS_K?^L_ZS_I?^C_Z7_J/^D_Z+_I?^F_ZG_J?^L_ZK_L/^S_[3_L?^V +M_[G_O_^\_[W_O__"_\+_O/^Y_[/_N?^R_ZO_I?^J_ZK_J/^G_Z;_K?^P_[3_ +MLO^X_[C_NO^[_\#_OO^[_[W_PO_"_[S_N_^[_[S_MO^V_[7_M/^T_[;_N?^V +M_[K_O?_`_[W_O__"_\+_P?_`_[__O?^__\3_P/^]_\+_R?_$_\'_QO_%_\'_ +MP/_#_\#_O?^__\'_PO_`_\+_Q/_%_\3_QO_'_\G_QO_)_\S_S?_,_]+_T?_/ +M_]+_U?_0_\O_T/_+_\C_R/_)_\/_P__)_\C_Q?_&_\O_S/_+_\C_S__1_\W_ +MS?_5_]/_TO_6_]?_U/_5_]3_S__2_\[_S/_+_\W_S?_+_\__T/_3_]+_UO_7 +M_]3_U/_7_]?_T__3_]3_T__3_]3_TO_1_]#_TO_._\W_S/_*_\C_R__,_\G_ +MR__._]/_TO_6_]7_V/_:_][_VO_:_^#_Y/_=_]S_X__?_]K_V/_:_]+_TO_+ +M_\K_QO_'_\7_Q?_*_\K_SO_,_]+_UO_9_]3_V/_<_]W_W/_>_]O_W?_C_^3_ +MW/_;_^/_W__8_]?_V__3_]/_U__4_\W_TO_9_]/_S/_1_]+_SO_._]'_S?_, +M_]3_U?_5_]/_V__>_][_V?_;_^+_XO_;_]C_WO_>_]K_U__8_]C_V?_7_]7_ +MTO_4_]/_TO_0_]'_U?_3_];_V/_<_]G_V/_;_]G_U__6_]?_TO_2_]+_T?_6 +M_];_U/_5_]__W/_7_]O_X?_=_]S_W__=_]S_WO_=_]G_V?_7_]G_U?_1_\__ +MU/_2_\W_TO_6_]C_U?_9_]K_W/_=_]S_VO_8_]O_V__7_]7_V/_:_]C_V/_: +M_]S_V__:_]O_V__9_]?_V?_=_]K_U__;_][_V__8_]K_VO_8_]?_V/_5_]3_ +MU__8_];_UO_:_]C_VO_<_]G_V/_9_]K_V?_8_]G_V?_:_]K_W?_=_]G_VO_A +M_]__U/_7_][_VO_6_]K_WO_<_]W_WO_?_][_V__;_]W_W/_8_]K_V?_8_]S_ +MW/_9_]G_W?_<_]?_V/_8_]C_UO_7_]O_V?_:_]O_X__C_^+_XO_G_^G_Y__D +M_^?_Z/_D_^3_Y?_E_^'_X?_=_][_W/_;_]7_U?_:_]O_V/_6_][_X/_A_^#_ +MY/_D_^?_Z/_E_^3_Z__K_^C_Z?_Q_^__[?_O_^__[O_N_^__Z/_G_^;_YO_= +M_][_WO_=_]O_W/_=_]S_W__?_^#_XO_J_^;_Z?_N__?_\O_R__;_^/_U__+_ +M]/_O_^[_Z?_J_^K_Z?_E_^7_Z?_I_^C_Z?_K_^G_Z__M_^[_[?_O_^[_\/_S +M__+_[?_O__3_[__J_^K_\/_L_^?_Z__N__'_[O_T__;_]__W__G_^?_U__7_ +M^?_W__'_]/_V__/_\?_S__'_\O_Q_^[_[?_P_^__Z__N__/_\O_N__'_\__U +M__;_]__R__;__?_Z__3__/\``/___?\"``$``0`#`/S_^__X__G_\__S__/_ +M\?_P__'_]/_T__?_]__V__?__O_]__W__?\!````!``&``(``0`%``4`___^ +M__[_``#\__C_]O_]_P``^/_X__[_`@#]____`P`#``$`!0`&``<`!@`*``H` +M"0`*``H`"0`&``<``0```/[__O_\__[_^O_X_P````#\__W_!@`$``$`!``, +M``D`!@`)`!,`#P`-`!``$0`/``H`#``)``<``0`"`````````/___/___P8` +M`0#]_P(`!P`$``4`!@`(``@`"P`+``P`"P`+``\`#@`'``@`$``-``8`"@`/ +M``X`#0`0``T`#@`3``T`"``(``T`!P`!``(`"0`'``$`!P`+``P`"0`-`!`` +M$0`-``T`$P`3``L`$0`8`!(`#0`4`!@`$0`.`!0`%@`/``X`%@`6`!(`$P`9 +M`!@`$@`3`!,`%``.``L`"@`.``P`"``)``T`#P`.`!,`$0`4`!8`&``6`!D` +M&P`=`!T`'0`<`"$`(@`:`!@`'0`>`!$`$0`4`!4`#P`-`!``$@`5`!(`$0`5 +M`!@`%0`4`!4`%P`6`!4`%@`:`!H`&@`9`!L`'0`=`!H`'0`@`!X`&P`>`"$` +M'@`;`!\`(P`>`!@`&``?`!D`$P`3`!8`%@`5`!@`&``9`!D`'@`?`!T`&P`@ +M`"$`'0`<`"``(0`?`!\`(0`?`"$`)``@`!T`'0`@`!T`&P`9`!H`'0`<`!P` +M'``<`!X`'P`;`!L`(0`B`!P`'@`B`"0`(@`C`",`(@`C`!\`'P`<`!P`&0`9 +M`!@`&``<`!L`&0`;`",`(@`>`!X`(P`C`"(`(P`C`"``)0`H`"0`(P`D`"0` +M'P`@`"``'@`9`!H`&@`;`!H`&0`;`!L`'``<`!P`'``<`!L`&@`=`"(`'@`> +M`"(`)P`D`"0`)@`G`"4`(@`E`"4`(@`>`"``'@`>`!T`&@`6`!L`&@`5`!(` +M%@`7`!4`%P`6`!P`'@`>`!P`(@`D`!\`'@`?`!\`&@`:`!<`&``:`!<`%``9 +M`!\`&@`9`!L`'0`@`"$`&@`:`",`(@`:`!D`'``7`!D`%0`0``L`$0`.``H` +M#``+``X`$@`4``\`%P`<`!H`%@`:`!T`'0`;`!L`&P`<`!X`'@`;`!4`&``9 +M`!8`#@`/``T`#@`)``@`"``)``L`"0`)``L`#@`+``P`$``2``\`$P`4`!8` +M%P`:`!@`&P`<`!D`&@`5`!$`$``5``H`!P`+``P`!0`%``8`!@`&``0``@`% +M``@`!@`*``H`"P`.`!(`#0`0`!,`#0`-``\`#0`&``H`"``$``8`!P`%``8` +M!P`&``4`!0`%``8`!``#``8`!@`%``<`"@`'``<`"0`)``<`!``&``(``@`# +M``(`_O___P$`___]____````````__\!``$`!``$``<`"``*``D`"@`)``8` +M"``#``$`_?____[__/_[__S__?_]_P````#^__[_`0`!``$``0#_____`P`% +M``(`_?___P4``@#[__O_`@```/O__O\``/____\$``(``@`&``4```````4` +M`0#]____`@```/W__?\!``,`___\_____O_\__W__?_\__S__?_^_P``___] +M__[_`@#___O___\"`/[___\"``8`!@`)``8`"0`-``@`!0`%``<`___^__W_ +M_/_X__K_^O_W__;__/_]__7_^?\``/__^O\"``0`!0`&``D`!P`+``T`"``& +M``@`"@`&``0`!0`'``<`!0`$``8`"``&``$`!``$````_?\!````_?___P`` +M`0`"``0``P`&``8`!P`(``L`"0`*``P`#P`.`!``#P`.`!``$``.``@`"@`) +M``<``P`$``4`!``!``,`!``"``(``P`$``,``P`%``D`!@`'``L`#P`+``P` +M#P`.``T`#``-``L`"@`)``D`"@`+``@`"``*``D`!@`'``<`!P`&``4`!P`* +M``@`!``-`!``"@`(``P`#@`)``<`!@`(``@`!@`%``8`"``(``H`"@`+``H` +M#0`.``P`#``.``\`#P`-``\`$0`-``H`"0`.``L`!P`$``8`"``(``<`!``( +M``P`"P`(``H`#``(``@`"0`'``8`"0`+``8`!P`-``X`"@`+``T`#``,``P` +M"@`%``@`#0`)``$`!P`,``4`__\&``@``0`!``@`"``"``<`#``,``@`"P`- +M``@`"``(``<`__\!``,``0#\__W______P``__\```(`!``"``8`"``*``@` +M"P`,``X`#@`*``@`!0`*``,`_/_Y____]__U__G_]__S__C____X__?___\$ +M`/[__?\!``<`!0`#``,`"0`*``4`!``)``H``0````(``@#[__S__?_[__?_ +M^O_Y__7_\__W__3_\?_S__7_]O_U__K_^O_^_P```@#__P$``P`#``(`_?_] +M____`0#Z__G__?_[__?_^__Z__;_]O_Y__7_]/_W__7_]O_Y__G_]__Y__K_ +M^?_Y__C_]O_W__K_]__T__C_^O_Z__C_^?_[__O__/_[__C_^O_\__S_^O_] +M__[__?_^_____O_\__O_^?_Z__;_]__W__/_\/_V__?_\O_S__3_]/_V__C_ +M]?_V__G__/_Z__W___\#```````"``,``@#[__O__/_]__3_]/_X__;_\?_S +M__C_]?_R__;_^/_V__G_^O_\__K__O___P````#^____`0#___G_^__[__G_ +M^?_[__K_^/_]_____O_\__W_``#___O_^O_]__S_^__\__S__?_^_P``_O__ +M_P```0`!`/___O\```,``0#]__[_`@```/W_^__\__O__/_\__O_^__[_P$` +M`@`#````!0`*``L`!0`$``H`"``"``$`!@`%``$````#``$`___^_____O_] +M__O__?\``/W__?\```0``@`%``4``@`$``D`!0#__P0`"@`%``(`!@`,``D` +M!@`'``L`"P`&``4`!@`(``(````#``0`_?_]_P0``P#Y__K_`P`$`/W_^O\# +M``@`!P`"``D`"P`*``L`#0`+``H`"P`*``H`"0`'``8`"0`+``D`!@`&``8` +M!P`$``(````#``(`__\"``0``@`!``@`"0`#``(`"@`+``4`!``)``H`"0`( +M``@`"0`*``D`"``)``<`!P`(``<`!@`(``H`"``(``@`"0`+``0``@`$``8` +M___]____``#_____```!``,``@`&``8`!@`&``D`"P`+``<`"0`.``X`"0`) +M``X`"@`%``0`"``!`/__`````/__`0```/S_`@`&````_O\$``0``0`"``8` +M!0`$``0`"@`+``<`!@`-``P`!0`(``P`"``#``<`"``%``,`!0`%``0````` +M``````#^_P``!`#__P$`"``-``4`!P`0`!(`"@`)``T`"@`)``,``P`#``0` +M`0#_____`0`!`/____\"``4``@`'``@`"@`+`!``#@`,``X`#@`.``H`"0`' +M``<`!``$``$``0`"``0``0#__P(``P`"``$``@`#``4`!0`'``8`"``*``X` +M"P`*``T`#0`*``@`"@`*``D`!@`'``D`"``'``<`!``#``0`!@`!`````P`" +M``$`!0`)``(``@`)``L`!@`$``<`!P`&``8`!P`'``0`!0`)``<`!``'``D` +M!P`&``8`!@`(``@`"``)``H`"0`+``P`!P`&``<`!P````$``0`!````____ +M_P``!0`"`/[_`P`*``0`__\&``T`!@`%``H`#P`+``H`#@`0``L`!P`.``P` +M`P````H`!@#^__S_`0```/W_^?_Z__S__?_[__K_```!``0``@`)``L`"P`* +M``X`#P`+``D`"P`-``4``P`%``8``0`"``(```#__P(``0#\__S_`@`#`/W_ +M_?\"``4``@`"``,`!0`%``(``@`#``,``0`!``0`!``&``4`!0`'``<`!0`& +M``D``P#__P0`!P#___W_`P`%`/___?\#``4`___\_P(`!0`!`/__!``%``4` +M!0`&``0``@`&``4`___]_P0``0#[_P```P````$`!P`$``0`!P`)``4`!``( +M``<`!0`#``0`!0`$``(``P`#``(``P`#```````%``4``0`#``D`!@`#``4` +M"``'``(``@`%``8``P`!``(`!@`+``4`!``*``T`"``(``L`"``'``D`"0`& +M``<`"``(``@`!P`&``8`!0`%``,`!``%``0``P`$``<`!@`(``@`"``(``D` +M"0`(``@`"0`)``L`"P`+``P`#0`+``L`#@`,``@`"0`.``L`!P`(``P`#``( +M``@`"P`*``@`"``*``4``P`(``L`!P`$``H`#``*``D`#0`,``L`#@`/``L` +M#P`3`!(`#@`3`!<`%@`3`!,`$P`1`!,`#0`+``H`"P`'``<`!@`$``<`"@`' +M``4`"P`,``L`"@`2`!,`$P`2`!@`&0`8`!D`&0`6`!<`&@`2`!``$@`4``\` +M#P`1``X`#@`2`!$`#0`*``\`$``,``H`#0`2``\`#@`1`!8`$P`4`!<`&0`6 +M`!8`&@`;`!8`%P`;`!<`%0`9`!D`$``2`!8`$@`,`!``$@`1`!$`$``3`!4` +M&0`6`!<`&0`9`!L`&0`4`!4`&@`8`!4`%``3`!(`&``6``T`#P`8`!<`$0`6 +M`!<`%@`:`!\`&0`;`"``'@`;`!L`'``7`!@`%0`3`!0`%0`2`!$`$P`5`!8` +M%``2`!,`&``8`!0`$P`<`!X`%P`5`"``(0`6`!8`'@`>`!<`%@`8`!H`&``6 +M`!<`&``7`!<`%@`6`!8`%``1`!8`&@`3`!(`&@`>`!8`&``>`!P`&``9`!P` +M&0`7`!0`%0`5`!8`$P`1`!4`%@`2`!,`%@`3`!$`%@`8`!,`%P`:`!H`&@`< +M`!H`&@`=`!H`%P`8`!D`$P`3`!0`%0`1``\`#P`3`!``"@`+`!$`#P`)``P` +M$0`2``X`$0`6`!H`%``4`!H`'``6`!8`&@`7`!8`%0`5`!0`$P`.`!``$``. +M``@`"0`)``D`!P`'``D`"0`,``T`#P`-`!$`$P`2`!``$``4`!0`#P`-`!,` +M%0`-``H`$P`2``L`#``/``H`"P`/``H`"0`0``\`"0`*``X`"P`)``<`!P`% +M``8`!@`&``8`!0`)``H`#``+``L`#0`0``X`"P`.``\`"@`*`!``#@`'``@` +M#P`+``8`!P`*``8`!0`(``8``@`'``L`!0`#``@`"``$``0`!@`!``(`!P`% +M`````P`+``<`!0`(``L`"0`(``D`"0`)``<`"``'``4`!@`)``0````#``<` +M`0#^__[_`````/__```!``,``@`#``0`!@`%``,``P`$``4``P`"``,``P`# +M``0`!@`$``,`!0`#``(`!0`%``$``@`#``,``P```/__!0`%`/W__/\&``0` +M^____P0`!``"``(``P`&``<`!0`"``(`!0`$``$`_?\!``0````!``0``@`! +M``4`!`````(`!``"``$``P`$``,`!0`$``8`!P`$``8`!P`$``,`!0`"``(` +M`P```/__!````/W_``#___W_`0```/K___\%``0``0`'``<`#``,``@`"@`. +M``L`!``'``@`!0`!``,``0`!`/[__O____[_^/_Z_P``_?_Y__S_``#^_P,` +M!0`#``$`"``)``4`!``%``<`!0`#``(`!``&``,``0`$``8`!`#__P```P`" +M`/S__O\"`/[_^_\!``(`_/_]_P$``0#^____```!``(`!0`$``,`!``(``4` +M`P`%``,``P`"``,``0````$````$``4``P`"``<`"0`$``,`!0`&``$`_O__ +M_P,```#\__K__?_^__[__/_Y__[_`0```/__!0`$``4`"``,``@`"0`,``D` +M"0`(``8``0`$``,`___]_P$`_O_\__S__O_\__G_^__]__W__/_^_P``!``! +M``,`!0`'``8`!``%``<`!P`!``$`!@`$`/__`@`"````__\!`/W_^__^_P`` +M_/_Y__W_`0`"`/[__O\#``@``P`!``0`!0`$``,``P`"``,``P`"``$``0#_ +M_P``_?_\____``#^__W_```!``0``P`"``0`"``%``0`!P`#````!0`'```` +M```%``0````!``(``@`!`/[_^_\`````^__\_______^_P```0`!`/__`@`% +M``<``P`!``4`"``$``$`!@`'``,``0`#``$```#]__W__?_]__[_``#___S_ +M`@`$``$``0`#```````%``(`_/___P0`___^_P0``@#^_P$`!``!`/__`0`% +M``0``@`$``D`!P`#``4`!P`&``(``@``````______S_^__]_____?_[__W_ +M```!`/S__O\#``$`_?\#``4``@`!``4`!P`$``$``@`&``4`__\```8``P#^ +M_P(`!P`!`/W_`@`$`/S__/_]__O_^O_\__G_^?_^__W__?___P(`__\#``0` +M`P`%``8``P`&``D`!@`#``4`!P`$``0``@```/[_`0#^__O__/_^__[__O_\ +M__O___\``/K_^?______^__]_P``_?___P,``P#^_P(`!@`!`/__`P`%```` +M_?___P(```#^__S__O\``/W_^/_[__W_^O_Y__O_^__\____^__[_P```@#^ +M__[______P$```#]__[_``#___[_`0`!`/O__?\``/[_^__\__S_^O_]_P`` +M_O_[__[_`0#___W_______W___\``/[__?_^__[__O_\__O__O____;_]__^ +M__S_]?_V__K_^O_]__W__?_]_P,``@````$``0```/[_`0`"`/[_^?_]_P$` +M_?_V__G__O_\__;_^/_]__S_^O_Y____`0#___S___\"````_O_\__[____] +M__S__/_^__[_________`0```/[_`````/W__O\"`/___/\!``(`_O_^_P`` +M______[__/_\_P``_O_Z__[_``#^__[__O_]__[____]__S__?_^_P$```#_ +M_P``!@`&``4`!``'``@`!0`%``8``P#__P4`!`#^__S_`0#\__C_^?_Z__;_ +M]__[__K_^?_[_P,```````,`"P`(``0`"``-``@``P`(``8``P`#``4``0`` +M````___^____^O_[__[__/_[__W____]_P```@`!`/__!``&``,``0`&``<` +M`P`$``D`!@`$``<`!@`%``4`!`````(`!``!`````@`"``(`!``#``0`!@`# +M````!@`)``$`__\$``8``@`!``0`!``"``,`!0`#````!``$````!``'``0` +M`P`'``8`!@`'``4`!0`%``4``P`#``0``P`!``,`!0`#`/[_```%``(`_?__ +M_P$``0#__P```0````,``P`#``,``P`"``,`!@`$``$``P`&``8``P`#``0` +M`@`#``(``0#^_______^__W___\!`/[_```"``4`!``"``(`!0`(``,`_O\# +M``8``0`!``(```#]_P````#^__W__?___P$``@#__P,`!@`'``4`!``&``8` +M!0`#``$``0`%``(`_?\```4`___\_P,``P#]____`0#^__[_`0```/[___\` +M``$``0`"```````$``0`__\```8`!P`!``,`!P`)``@``P`"``@`"P#___O_ +M`P`&`/S_^_\`````_O_^__W__O\"````_/_^_P8`!`````$`"0`*``4``P`* +M``L`!P`&``<`"``%``0`!0`'``0``0`$``8`!``#``$```````,``0#^____ +M```#``,``P`"``(`!0`*``8`!``&``H`"P`)``<`"@`,``D`!P`*``P`!P`% +M``8`"``&``4`!0`$``0`!P`)``4`!0`)``@`!0`&``@`!@`%``<`"``)``D` +M"@`'``H`#@`*``<`"@`.``H`!@`*``L`"0`)``L`"P`(``H`#@`+``@`"0`+ +M``L`"``)``L`"@`*``H`"0`*``D`!P`&``D`"@`'``@`"P`.``L`#0`/`!(` +M$0`/`!``%``4``X`#0`2`!(`#0`-``T`#P`.``L`"@`,``L`"@`,``P`#``, +M`!$`$0`1`!(`%@`5`!,`%``6`!0`$0`2`!,`%``2`!``$``3`!4`$@`.`!`` +M%0`4`!``$``5`!4`%``4`!8`%@`6`!8`%P`6`!0`%P`9`!0`%0`:`!@`%``8 +M`!H`$P`4`!@`%@`1`!(`$P`3`!0`%``2`!(`&@`:`!<`&``?`!T`'@`?`!X` +M'@`@`"``'0`=`!T`'@`<`!D`%P`:`!8`$P`3`!8`%P`6`!4`%0`<`!L`&P`: +M`!T`(``?`!X`(``@`!\`'@`B`",`'P`>`"``(@`>`!P`'0`=`!D`&P`@`!T` +M%P`:`"$`(``:`!@`'P`B`!L`&``>`"``&P`:`!\`(``=`!P`(``A`!T`&P`? +M`!\`'``?`!\`'``?`"(`(``<`"``(@`?`"``(``>`!T`(``>`!H`&@`>`!X` +M&@`7`!L`&P`8`!@`&@`7`!<`'P`=`!D`'``C`!\`'@`B`"(`(``@`"``(``A +M`!\`'``>`"(`'0`;`!X`'0`9`!P`'0`8`!0`&P`;`!8`&``9`!@`&``=`!P` +M&0`<`!X`'``>`!\`'P`=`"$`(@`>`!X`(``@`!H`&@`<`!P`%0`5`!D`&0`5 +M`!0`&0`:`!@`%P`9`!L`&P`9`!H`&P`9`!H`'``:`!@`&@`:`!@`%P`8`!8` +M%@`6`!<`%P`5`!<`&0`9`!4`&``<`!D`&``7`!<`&0`9`!4`%``7`!@`%0`6 +M`!@`%@`3`!8`&@`6`!``%0`;`!8`$0`5`!D`%@`2`!0`$P`4`!,`$``1`!4` +M%``/`!0`&``5`!0`&0`8`!0`%P`9`!0`$``5`!0`$0`2`!(`#P`2`!,`#P`- +M`!``#P`.``\`#P`/``\`$``1`!$`$``0`!$`$@`1`!$`$0`1`!$`$0`3`!4` +M$@`0`!,`%@`2``T`$0`2`!$`#P`-``P`#@`/``X`#0`-``P`#P`1``L`"@`0 +M``\`"@`-`!$`#P`-`!``#P`0`!,`$``-``\`$@`0``T`#@`/`!``$``,``X` +M$@`/``D`#0`0``H`!@`,``P`"0`)``D`"``*``P`"0`&``D`#0`+``<`"``. +M``T`"@`*``\`#P`,``\`$``+``P`$0`,``<`#0`1``H`"0`+``L`"0`)``8` +M!@`%``4`!``#``,`!@`&``(`"``+``D`"``/``T`"P`/`!$`#0`,``\`#@`+ +M``H`#0`)``8`!@`)``0``@`$``0``@`$``8``P`%``@`"@`'``8`"``*``D` +M!P`'``D`"0`&``<`"@`)``<`!@`&``D`"``$``,`!@`'``8`!@`&``0`!@`* +M``<`!``&``D`!P`$``8`"``(``8`!0`)``L`!P`$``<`"0`%``0``@`#``4` +M!``"``,`!``#``8`!0`#``8`"@`&``0`"``,``D`!0`'``T`"@`"``8`"0`% +M````!``#````__\!``0``P`#``,`!0`%``8`!@`#``0`"``'``0`!``&``8` +M!0`$``,`!@`&``(``@`&``0`__\!``8`!P`#``$`!0`+``<``0`"``@`"0`! +M``$`"``'``$``@`&``4``@`"``(``@````(``P#___[_!``'``$``P`%``@` +M"``&``<`"P`)``8`"``)``@`!@`%``4`!P`%``(````$``,`___\_P$``P#^ +M____`@`#``(`!0`%``0`!0`)``@`!0`'``L`!P`'``L`"@`(``H`"0`&``H` +M"``#``,`!P`%``(``@`$``0``P`!``(`!0`$``$``@`'``8``P`%``P`"0`' +M``D`#0`,``H`"P`)``@`"@`+``8`!0`(``<`!``'``@`!0`%``<`"0`)``8` +M`P`(``T`!P`"``<`#``&``,`"0`(``(`!0`)``8`!``'``L`"0`)``D`"P`, +M``L`"@`)``D`"0`*``8`!0`)``D`!``%``D`!P`%``<`!P`&``<`!P`'``<` +M!P`'``H`"@`'``8`"``+``@``P`&``P`"``"``8`"P`*``8`!P`*``H`"``' +M``@`!P`'``D`"``$``8`"@`)``,``P`)``D`!``$``4`!0`'``@`!@`%``<` +M"``*``D`!@`%``@`"@`$``,`"``(``4`!@`*``<`!0`(``@`!@`(``D`!0`% +M``@`!P`%``8`!P`&``@`"``%``<`"``%``0`"``'``,`!``(``@``P`"``<` +M"``%`````@`)``@``P`"``D`"P`'``4`"@`,``@`!P`)``D`"``(``8`!@`' +M``8`!``'``<`!``#``,`!``&``(`__\$``<``@`!``8`!0`%``<`!P`%``L` +M"P`&``<`"P`,``L`"@`*``D`"0`+``<`!``#``<``P```````@`!`/__```" +M``0``@`"``0`!@`&``<`!0`(``D`"@`*``<`"``+``H`!0`%``<`!@`"``,` +M`P`#``(``P`$``(``P`%``4``@`#``4``P`#``(``0`#``4``@#__P(`!0`$ +M``$`__\"``4``P`"``,``P`#``8`!0#__P(`"0`$`/O_`0`'``(`_/___P0` +M`P```/__```"``$`___^_P``_O_\__W__O_\__S_``#___W__O\"``(`____ +M_P,``P`!``$``P`!````!0`!`/O__O\$`/S_]__\_P``^?_V__O__?_\__K_ +M^__^_P``_?_]_______^_________P$``0#\__S_`P```/G_^/____W_^/_X +M__K__/_[__K__/_]__O__/____[_^?_\_P$`_?_[_____O_Y__W_`@#\__;_ +M_?_]__?_^/_\__?_]/_X__S_^?_U__G__?_\__C_^O_]__[__/_[__W_`0#_ +M__G__/\"`/W_]O_[__W_^O_W__C_]O_X__K_]O_U__G_^O_V__?_^__Z__?_ +M]__Y__K_^?_X__C__?_]__K_^O_]__S__/_]__K_^?_\__W_^?_Y__O_^?_X +M__G_]__W__;_]O_V__C_]__T__G_^O_W__?_^O_[__G_^/_Z__O_^?_W__?_ +M^?_[__O_]__V__[__O_W__?__?_[__;_^__\__?_^/_\__K_]__W__C_]__W +M__3_]/_V__?_\__Q__?_^O_V__/_^__\__C_^?_\__K_^?_[__O_^O_Y__G_ +M^O_[__C_]O_W__K_]__T__;_^/_U__/_]__Z__7_\O_W__O_]__Q__/_^?_W +M__/_]/_W__G_]O_V__G__?_Y__;_^__\__C_^/_Z__C_]__Y__G_]O_V__7_ +M]?_W__?_\O_Q__?_^?_Q__#_^?_X__/_]/_Z__C_]/_X__O_]__S__?__?_Y +M__+_]O_\__C_]?_X__C_]O_U__?_]?_T__3_]?_U__/_]O_X__?_]/_U__C_ +M]__V__?_]__V__;_^?_X__7_]O_X__?_]O_W__/_]/_X__7_\/_T__G_]/_R +M__;_^/_W__?_]O_W__C_^/_W__?_^/_Y__?_]O_Z__O_]?_R__G_^O_T__'_ +M]?_W__7_\__S__7_]O_T__+_]?_X__7_\__V__;_^/_Y__C_]O_[__W_^O_X +M__G_^__[__G_]/_V__C_]O_R__3_]__V__/_\__X__?_]/_U__C_^/_W__?_ +M^?_Y__G_^?_Z__C_]__[__G_]/_U__K_^?_V__?_]__Z__W_^O_V__O__O_Z +M__C_^O_]__O_]__V__O__/_V__3_^?_Z__C_]?_U__C_^/_W__C_^/_W__S_ +M_?_[__K__O_]__S__O_]__W__?_]__[__?_^__[__/_\__S__/_Z__S_^O_W +M__C_^__X__7_^/_Z__K_^?_[__O_^__\__[__O____W_```$``0`_O___P4` +M`@#^__[____[__W__O_Z__?__/\``/K_]O_\_P(`_?_X__S_`@#___W___\! +M`````@`!`/[_`0`%````_?\#``(`_?___P(`_O_\_P$``@#___[__O\```(` +M`0#]__S_`0`#``(```````(``P`!``$``P#^__W_`P`#`/O__/\#``$`_/_^ +M_P,``@#^____!``#`/____\#``(``0`$``,````"``4``P#_____`0`!``(` +M___^_P`````!``(```#]_P$`!0```/[_`@`"`/__```#``(`__\```0`!``` +M`/__`P`$``$````#``4``@````,`!``#``(``@`"``$``P`"```````"``,` +M__\```4``P````(``P`!``,``@#]__[_!``"`/[___\!``(``0````$`!``# +M``$``P`&``,``P`%``4`!``%``8``0`!``(``0#__P````#]__S_`@`#`/O_ +M^_\#``0`_O\```,``0`!``,``@`"``(``P`$``0``P`%``4``@`#``4``P`! +M``0`!`````$`!0`#`/[___\$``$`_?_]_P$```#\__[_`@```/W_`P`#``$` +M!``$````!``)``0`__\$``@`!0`"``,`!0`#``$````#````_?___P$`___^ +M_________P```0```/[___\"````__\!``(``0`"``,``P`#``(``0`"``,` +M`@`!`````0`!````__\`````_____P$``0#^__[_`0`"`/W__?\#``0`___^ +M_P(``@#_____``#^____`@#___S_```$`/___?\#``(`_O\"``,`_?_^_P,` +M`0#]____``#__P$``0#_______\``/___?_]__[_______W__?_________^ +M__W__?\```$`_?_]_P(``@#___[_`@`#`/____\"````_/___P$`_O_Y__S_ +M`@`!`/O_^?___P$`___^__S__/\"``,`_O_\_P$``P#___W___\!`/W__/_\ +M__W__?_Z__C__?\``/O_^________O\"`/___/\!``,`___^_P$`___]____ +M``#\__K_^_____O_^/_Z__O_^?_Y__S__/_[__O__/_]__S_^O_\__[____] +M__[_`````/__``#_____`````/__^__\__[__O_X__;__?_^__;_]__]__O_ +M]__X__O_^__\__S_^__[_P$``0#\__O_`@`#`/S__/\``/__^__\__[__O_[ +M__O__?_\__O__/_[__G_^?_]__[_^?_X__W_``#\__K__/_[__S____[__C_ +M_/_^__S_^__[__S__?_]__S__O_^__O__/____[__?_\__O_______O_^__^ +M__[__/_Z__O__/_[__C_^O_[__G_^O_[__K_^O_\__O_^__]__W__/____[_ +M_/_^_P``_O_]_P``___[__O__O_\__K_^O_[__K_^__]__G_^?____[_^/_[ +M_P``_/_Y__W____]__S__/_]_____O_^__[__/_^_P``_O_[__S_``#___S_ +M_/___P``^O_Z_P$```#Y__K_`0#___K__?\!`/_______P```0```/[___\! +M`/___O_^____`0#___K__?\%``$`^?_]_P,``0#_____`0`"``0``P`"``(` +M!``%``,``0`!``0`!``!`/__`@`%``$`_O\#``4```````4``P#__P,`!0`" +M``$`!``%``0``P`"``0`!P`$``,`!0`(``<`!0`%``@`"0`'``8`!P`'``8` +M!P`%``0`!``#``,`!@`%`````@`)``@``@`#``8`!P`'``<`!0`'``@`"0`) +M``@`"0`)``@`"P`,``8`!``+``P`!``&``P`"``$``D`"@`%``8`"@`'``4` +M"0`)``8`"``+``<`!P`,``T`"0`(``D`#``.``D`!@`,``X`"P`*``P`#0`, +M``L`#0`/``L`"``.``\`"``+``\`"@`)``\`#P`(``@`#@`,``D`"@`*``P` +M#0`+``L`#P`.``P`#@`1``X`#@`0``\`#P`0`!``$``0``X`#P`1``\`"P`, +M``\`$``+``H`#P`0``L`"@`0`!``"P`+``\`$``-``P`#@`/`!$`#P`/`!$` +M$P`1`!``$@`2`!``#P`0``\`#P`.``T`#0`/``T`"P`.``\`#``,`!$`#P`, +M``\`$P`2`!``#@`1`!4`$P`-``T`%``1``L`#0`0``T`#``/``\`#``-`!(` +M#P`+``\`%``0``L`$``5`!$`#P`/`!$`$P`1`!``#@`/`!``$``.``T`#@`. +M``P`#@`0``X`#``,``\`$@`-``D`#P`2``T`#``3`!$`#``/`!,`$@`.``X` +M$0`2``\`#0`/`!$`#@`,``\`$``+``L`#P`/``H`"@`/``\`#``+``X`#P`/ +M``X`#P`0``X`#0`.`!``#@`+``P`$``0``P`"P`/`!(`#@`+``T`$0`1``L` +M#``0``X`#0`/``X`"P`-`!$`#``)``P`#@`+``H`"P`+``H`#0`.``D`"@`/ +M``T`"0`-`!$`#``*`!``$0`.``T`#P`.``T`$0`-``D`#``/``D`!P`+``L` +M!P`(``D`"@`*``<`!@`+``X`!P`&``T`#P`)``D`#0`-``T`#@`+``D`#0`. +M``H`"``*``H`"@`*``D`!P`)``H`"``(``@`!@`(``D`!P`'``@`!P`(``H` +M!P`'``H`"0`'``@`"@`)``<`"``*``L`"``'``L`"@`&``8`"@`(``,`!``) +M``<``P`%``@`!P`&``<`!0`%``D`"``#``8`"P`%``(`"``)``,``P`(``<` +M`P`%``8`!0`&``4`!``'``D`!``#``D`"0`#``0`"``%``,`!0`'``4``P`$ +M``4`!@`%``0`!``%``<`!0`#``0`!@`%``,`!``%``8`!@`#``(`!0`'``0` +M`@`$``4`!0`%``8`!0`$``8`"``&``,`!``&``8`!``%``4``P`$``8``P`" +M``4`!``"``4`!0`"``0`!0`$``0`!``$``<`!P`"``$`"``)``$``0`(``@` +M`P`#``8`!0`%``4`!@`(``0``@`&``<``P`#``8`!0`#``0`!0`$``0`!``" +M``,`!0`%``$````$``4`!``"``(`!``%``4`!``"``4`"``&``,`!0`'``<` +M!@`$``0`!@`'``,``0`#``4``0````,``@#__P(`!0`!````!0`&``$``0`% +M``<`!``"``0`"``'``,`!0`'``0`!0`(``0``0`%``8````#``<``0#]_P0` +M!@```/[_`@`#`````0`$``(`__\$``<`!``!``0`!P`'``0``P`%``8`!``" +M``0`!``$``,``0`#``4``0#^_P(`!0`!````!``$``(``@`%``0``P`$``0` +M!``"``(``P`"`````@`$``(````"``0``@````(`!@`#`/__!``(``,``0`% +M``0``0`$``8```````0`!``!``$``0#__P,`!@```/[_`@`$``$``0`#```` +M`0`$``,````"``0``P`#``,``@`$``4``@`!``,``P`!``(``P`!`````@`$ +M``(`__\"``0``0`!``(``@`"``(``@`$``4``@`!``0`!``!``(``@`!``(` +M`P`!`````@`#``$````"``0``P#__P``!@`&`/__`0`&``0``P`&``,`_O\$ +M``@``@#]_P$`!0`!`/W_`@`%`/___/\$``8`_O_\_P(`!0```/__!``&``(` +M`0`$``<`!0`"``,`!0`%``,``P`"``(``P`"`/__`0`#``$`_?___P,``@#^ +M__[_`P`#``$````"``0``P`!``,`!0`"``$`!0`#`/__`@`'``0`_O\!``<` +M!P`"`/[_`0`&``4```#]_P(`!0`!`/__`@`!`/__`0`#``(`_____P(`!0`# +M`/__```%``4``P`!``$`!``$``(``0`"``(``0`#``,`__\!``0``P`!`/__ +M```#``(```````$````!``(```#__P```0`!````__\!``(`_____P0`!``! +M`````P`$``$````"``,``0#__P```@#_____``#^_P```P#^__S_`P`!`/K_ +M```%`/W__/\$``$`_/\!``(`_O___P$```#_______\!``(`___]_P$`!0#_ +M__K_`0`$`/[__/\``/[__O\!`/S_^_\#````^/___P4`_O_Z____`0#___[_ +M_?_^_P(```#[__W_`0`!`/[__/_]_____O_]__W__?_]_______\__W____] +M__W__O_]__W_______W__/_]_P``___\__S__?_^__O__/_]__K_^?_]____ +M^?_W__[__O_Y__K__?_]__O__/_]__W__?_]__W__?_^__[__/_[__S__/_[ +M__G_^__\__K_]__Y__S_^__W__;__/_^__?_]?_]____]O_U____``#Y__?_ +M^__]__S_^__Z__G_^O_]__S_^/_Y__W_^__W__G__/_W__7_^O_[__;_]?_Y +M__O_^?_V__?_^__]__C_]?_Z__W_^?_X__K_^/_X__O_^O_V__?_^O_Z__G_ +M^/_W__G_^__Y__?_^/_\__O_]O_X__S_^O_U__?_^O_X__7_]O_W__C_^/_V +M__7_]__[__G_]?_W__K_^?_Z__G_]__Z__W_^O_W__G_^__Z__?_]__X__C_ +M]O_W__C_]O_T__?_^?_U__/_^/_Z__3_\__Y__O_]?_S__C__/_Y__3_]__\ +M__G_]?_V__K_^O_U__7_^?_Z__;_]?_V__?_]__U__/_]?_X__?_]/_T__?_ +M^/_X__;_]?_W__K_^/_T__;_^/_W__?_]O_U__;_]__V__3_]O_X__7_\O_V +M__O_]O_Q__7_^?_X__;_]/_T__C_^O_V__7_]__W__;_]?_W__C_]/_R__?_ +M^O_V__+_\__V__?_]?_R__3_^/_W__3_]?_W__C_]O_V__?_]__W__?_]O_U +M__C_^/_U__7_]__W__;_]O_V__3_]?_W__;_]/_U__C_]__T__7_^/_W__7_ +M]?_U__?_^?_V__'_]?_\__;_\?_X__O_]?_S__C_^O_W__7_]/_X__S_]O_R +M__?_^O_V__3_]__W__/_]?_X__7_\O_T__C_]__U__7_]O_W__;_]?_V__?_ +M]O_T__?_^?_V__/_]__Z__7_]?_[__C_\__V__O_^?_S__7_^?_W__3_]__Y +M__/_\O_Z__?_\?_T__C_]O_U__?_]?_W__G_]O_U__C_^O_X__;_]__Y__K_ +M]O_T__G_^O_W__;_]__W__C_^/_T__3_^?_W__3_]__X__3_]/_Y__?_\__W +M__K_]?_S__C_^O_W__;_]?_W__O_^O_U__7_^O_[__?_]?_W__G_^/_U__;_ +M^/_X__;_]/_U__G_^/_T__;_^/_V__7_^/_X__7_]__Z__G_]O_V__K_^/_T +M__?_^O_X__7_]__Z__?_]?_X__G_]O_U__G_^/_U__C_^/_W__?_]O_W__O_ +M^O_T__;__?_Z__/_]O_[__C_]O_X__G_^/_U__;_^?_X__;_]__X__;_^?_[ +M__;_]?_[__S_^?_V__C__?_[__3_]?_]__W_]O_T__G__?_Z__;_]O_Y__O_ +M^O_X__C_^?_Y__G_^O_X__C_^__[__K_^__X__?__?_^__?_]?_\_P``^O_V +M__G____^__;_^/____W_]__X__[__/_X__O__?_[__K__/_]__O_^O_[__O_ +M_/_]__O_^O_\__S_^__\__O_^O_]__W_^?_[__[__/_\_____/_\_______^ +M__W__?_^_____O_[__K__O____O_^?_^__W_^/_]_P``^/_W_P$```#Y__S_ +M`0#^__W__O_______O_^_P``___]_P````#\__S_`````/W__/_]__[____] +M__O__?\``/W_^O_^_P``_O_\__[_``#_____``#]__W_`@`"`/W__O\"```` +M_?\```$`_O_^_P````#\__[_`P#___O__O\"````_O_^__[_`0`"`/W__/\! +M``(`_O_]____`0```/_____^_P```@#___S__O\"``$`______[_```#``$` +M_O_]_P$``P#^__W_`0`"`/W__/\#``,`_/_\_P(``@#\__W_`@`!`/W__?\! +M``(`___^_P````````$``0#_____`0```````0#^__[_`P`!`/K__O\%`/[_ +M^O\```(`___^_P``_____P$```#__P```0```/[___\!`/___O\```$`_O_] +M_P$``0#^__[_`````/[_```"`/[__/\"``4`_?_Z_P(`!`#]__S_`@```/W_ +M```!`/W__/\"``(`_?_]____`0#___W__O___P``___]__[______P``___^ +M____`0```/W___\"````_?___P(```#^_____O___P(`_O_Y____`@#]__O_ +M______S__?___P``_O_]_______^_P````#[__S_!``"`/K__?\"````_O__ +M__[__?\```$`_?_\____`0#]__O__O____[__O_[__O_`0```/K_^_\"``$` +M^__\_P$``0#]__S___\``/___O_^__[___\``/S__/\`````_/_\__[__?__ +M____^O_]_P``_/_\_P``_O_Z__W_``#^__W__?_]_____O_[__W_`0#___G_ +M_/\"`/___/_]__S__/\`````^__[_P```0#]__O__?____[__/_\__[__O_] +M__S__?_^__S__/____[_^O_\_P$`_?_Y____`@#]__G__O\"`/[__/_^__[_ +M_?______^O_[_P``_O_Z__S_``#]__K__?_^__W__?_]__S__O____S_^___ +M_P$`_/_Z_P```0#[__K_______O__/\``/[_^O_^_P$`^__Y____`0#\__C_ +M^_\!``$`^O_Y_P$``0#Z__S_`0#]__O_______W__?_]__S__O____O__/__ +M__S__/_^__S_^O_^_P$`^__[_P````#^__W__?_^_______^__[__O_]__[_ +M_O_]__S__/_^_____/_Z__W_``#\__K__O_^__W__O_]__S_`````/O__?\` +M`/___?_\__[_`0#___K__?\"`/[_^__^_P``_?_[__[_``#^__O__/______ +M_?_]__[__O_]__[__O_\__W_______[__?_]____``#]__S_______S__?\` +M`/[__/___P$`_O_\_____O_]_P$`_O_Y__W_`P#___G__?\``/[__?_\__[_ +M___]__W__O\``/[_^__^_P(`___\__[_````````_O_[__[_`P```/S__?__ +M_P``___]__S__O____[____^__O__?\!`/__^__^_P$`_?_]_P$``@#]__K_ +M```$`/__^O_]_P0``@#Z__O_`P`"`/G_^_\#````^O_^_P$`_O_]__[_____ +M__[__O_______O___P``_O_\____`@#^__O__O\"````_/_]_P```0#^__S_ +M__\``/[___\``/W__?\!````_/_]_P$`_O_\_P$``0#[__O_!``#`/G_^_\$ +M``,`^O_Z_P,``@#[__S_`0`!`/S_^_\!``$`_?_]____`0#___W__O\````` +M_/_^_P,```#[__W_`P```/K__?\#````^O_]_P,```#Z__[_`@#]__S_`0`! +M`/O__/\#````^__^_P$`___]_P```0#]__W_`@`"`/O__/\$``$`^__^_P(` +M``#^__[__O\```$`_?_\_P```0#^__W___\``/___O______`````/[__?__ +M_P(```#\____`P```/O__O\#``$`_?_]_P```0```/___O_^_P```0#^__W_ +M`0`!`/W__O\"````^__]_P,``0#\__[_`0```/__``#^__[_`@`!`/[__O__ +M_P(``@#^__W_`@`"`/W___\"`/___O\"````_/\```(`_?_]_P$```#]____ +M``#__P$```#]____`P```/W_`0`#````_O___P$``0#^__[_`@`#`/[__/\` +M``(`___^_________P$``0#]__W_`0`"`/___O\`````__\`````__\```$` +M___^_P```0```/[__O\!``,`_O_\_P````#__P$```#\____!`#___O_```" +M`/____\``/____\!````_?___P(``0#\__S_!``!`/K__O\#````_/\````` +M_?\!``(`_?_\_P(``P#]__W_`0`!`/___O_________________^__[_```! +M`/[__/___P$`___^_________P``___^_______^_P````#^____``#___[_ +M______[___\``/___?_^_P```0```/S__/\!``(`_?_\_P```0#^__W__O\` +M````_?_]____`````/W__/_^_P$``0#\__S_```!`/___?_^____`````/S_ +M_?\#``$`^O_\_P,``0#[__S_`0```/W__O_^__[____^__[__O_^__[___\` +M`/W__?\!``$`_/_\_P(```#[__[_`@#___O___\#`/[_^?_^_P4`___W__[_ +M!0#^__C__?\#````^O_[_P$``P#]__K___\"`/[__/___P``_O_^________ +M__[__O_^__W___\``/[__?_^_P``___^__W__?\!````^O_\_P(``0#[__S_ +M`0```/W__?_^_____O_^_P``_O_]__________[__?___P``___]__W___\` +M`/___?_]_P````#\__S_```!`/___/_\_P$``P#\__C___\%````^O_]_P`` +M___^__[__O_^_P````#]__[_``#^__S___\!`/[__/\```(`_/_[_P$``0#] +M__W_``#___S__O\!````_/_\_P$``@#\__O_`````/[_______[_______W_ +M_O\``/___?_^_P``_________?_]_P```@#^__K___\#`/___/_^______\! +M`/__^____P,`_O_[____`0#___[__O___P$`___[____`P#^__K___\$`/__ +M^_\```$`_?_^_P$`___\____`P```/S__?\!``(`_O_\__[_`0`!`/[__?__ +M_P$`___\____`@#___W_________``#___[___\``/__`````/S___\#`/__ +M_O\!`/___/\```,`_O_\_P```0#_____``#___[___\``/____\``/[__O\! +M``$`___^____````````_O___P$`___^_P```0#___[_```!`/___O\````` +M__\``/___O\!``$`_O___P``_O\```(`_?_^_P,```#]_P$``0#\____!``` +M`/S___\#``$`_?_]_P$``@```/____\`````_____P$```#^_P`````!``(` +M_/_\_P4`!`#Z__S_!0`#`/S__?\"``,`_O_\_P(`!`#___W___\!``(```#] +M____`@`"`/___?\```,```#]____`P`"`/[__?___P,``P#]__S_`@`$`/[_ +F_O\#`/___?\"``,`_O_]_P(``@#_____`````````0```/__```` ` end diff --git a/sound/windsound/windsound.c b/sound/windsound/windsound.c index 01e05b7e08..0dc486f183 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 } @@ -301,7 +302,7 @@ maybe_preinsert_directory(int32_t findsound_approach, char *exedir, char *buf, s int largest_basename = 35; /* findsound_approach = sff_havdir_append_rest means a directory name will be - * inserted into the begining of buf and the remaining parts of the + * inserted into the beginning of buf and the remaining parts of the * resource/file name will be appended by * get_sound_effect_filename(seid, buf, sizeof buf, findsound_approach) * when it sees the sff_havedir_append_rest indicator. diff --git a/src/.gitattributes b/src/.gitattributes index 1e3746cded..a618fdc82c 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 bf12c8d60c..dda3abdaca 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -28,4 +28,6 @@ objutil/* objlua/* objpdc/* bundle/* - +GNUmakefile +GNUmakefile.depend +.*.c diff --git a/src/allmain.c b/src/allmain.c index 5c30b70501..8480d67da3 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 allmain.c $NHDT-Date: 1652831519 2022/05/17 23:51:59 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.185 $ */ +/* NetHack 3.7 allmain.c $NHDT-Date: 1726894914 2024/09/21 05:01:54 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.261 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -6,33 +6,43 @@ /* various code that was replicated in *main.c */ #include "hack.h" -#include #ifndef NO_SIGNAL #include #endif -static void moveloop_preamble(boolean); -static void u_calc_moveamt(int); -static void maybe_do_tutorial(void); +staticfn void moveloop_preamble(boolean); +staticfn void u_calc_moveamt(int); +staticfn void maybe_do_tutorial(void); #ifdef POSITIONBAR -static void do_positionbar(void); +staticfn void do_positionbar(void); #endif -static void regen_pw(int); -static void regen_hp(int); -static void interrupt_multi(const char *); -static void debug_fields(const char *); +staticfn void regen_pw(int); +staticfn void regen_hp(int); +staticfn void interrupt_multi(const char *); +staticfn void debug_fields(const char *); #ifndef NODUMPENUMS -static void dump_enums(void); +staticfn void dump_enums(void); #endif #ifdef EXTRAINFO_FN static long prev_dgl_extrainfo = 0; #endif +#ifdef CRASHREPORT +#define USED_FOR_CRASHREPORT +#else +#define USED_FOR_CRASHREPORT UNUSED +#endif + +/*ARGSUSED*/ void -early_init(void) +early_init(int argc USED_FOR_CRASHREPORT, char *argv[] USED_FOR_CRASHREPORT) { +#ifdef CRASHREPORT + /* Do this as early as possible, but let ports do other things first. */ + crashreport_init(argc, argv); +#endif decl_globals_init(); objects_globals_init(); monst_globals_init(); @@ -40,7 +50,7 @@ early_init(void) runtime_info_init(); } -static void +staticfn void moveloop_preamble(boolean resuming) { /* if a save file created in normal mode is now being restored in @@ -73,19 +83,21 @@ moveloop_preamble(boolean resuming) } if (!resuming) { /* new game */ - gc.context.rndencode = rnd(9000); + program_state.beyond_savefile_load = 1; /* for TTY_PERM_INVENT */ + svc.context.rndencode = rnd(9000); set_wear((struct obj *) 0); /* for side-effects of starting gear */ reset_justpicked(gi.invent); (void) pickup(1); /* autopickup at initial location */ /* only matters if someday a character is able to start with clairvoyance (wizard with cornuthaum perhaps?); without this, first "random" occurrence would always kick in on turn 1 */ - gc.context.seer_turn = (long) rnd(30); + svc.context.seer_turn = (long) rnd(30); /* give hero initial movement points; new game only--for restore, pending movement points were included in the save file */ u.umovement = NORMAL_SPEED; + initrack(); } - gc.context.botlx = TRUE; /* for STATUS_HILITES */ + disp.botlx = TRUE; /* for STATUS_HILITES */ if (resuming) { /* restoring old game */ read_engr_at(u.ux, u.uy); /* subset of pickup() */ fix_shop_damage(); @@ -96,12 +108,11 @@ moveloop_preamble(boolean resuming) gd.defer_see_monsters = FALSE; see_monsters(); } - initrack(); u.uz0.dlevel = u.uz.dlevel; - gc.context.move = 0; + svc.context.move = 0; - gp.program_state.in_moveloop = 1; + program_state.in_moveloop = 1; #ifdef WHEREIS_FILE touch_whereis(); @@ -113,7 +124,7 @@ moveloop_preamble(boolean resuming) update_inventory(); } -static void +staticfn void u_calc_moveamt(int wtcap) { int moveamt = 0; @@ -176,7 +187,7 @@ moveloop_core(void) boolean monscanmove = FALSE; #ifdef SAFERHANGUP - if (gp.program_state.done_hup) + if (program_state.done_hup) end_of_input(); #endif get_nh_event(); @@ -184,26 +195,28 @@ moveloop_core(void) do_positionbar(); #endif - if (gc.context.bypasses) + dobjsfree(); + + if (svc.context.bypasses) clear_bypasses(); if (iflags.sanity_check || iflags.debug_fuzzer) sanity_check(); - if (gc.context.move) { + if (svc.context.move) { /* actual time passed */ u.umovement -= NORMAL_SPEED; do { /* hero can't move this turn loop */ mvl_wtcap = encumber_msg(); - gc.context.mon_moving = TRUE; + svc.context.mon_moving = TRUE; do { monscanmove = movemon(); if (u.umovement >= NORMAL_SPEED) break; /* it's now your turn */ } while (monscanmove); - gc.context.mon_moving = FALSE; + svc.context.mon_moving = FALSE; if (!monscanmove && u.umovement < NORMAL_SPEED) { /* both hero and monsters are out of steam this round */ @@ -248,7 +261,7 @@ moveloop_core(void) u_calc_moveamt(mvl_wtcap); settrack(); - gm.moves++; + svm.moves++; /* * Never allow 'moves' to grow big enough to wrap. * We don't care what the maximum possible 'long int' @@ -257,17 +270,17 @@ moveloop_core(void) * When imposing the limit, use a mystic decimal value * instead of a magic binary one such as 0x7fffffffL. */ - if (gm.moves >= 1000000000L) { + if (svm.moves >= 1000000000L) { display_nhwindow(WIN_MESSAGE, TRUE); urgent_pline("The dungeon capitulates."); done(ESCAPED); } /* 'moves' is misnamed; it represents turns; hero_seq is a value that is distinct every time the hero moves */ - gh.hero_seq = gm.moves << 3; + gh.hero_seq = svm.moves << 3; - if (flags.time && !gc.context.run) - iflags.time_botl = TRUE; /* 'moves' just changed */ + if (flags.time && !svc.context.run) + disp.time_botl = TRUE; /* 'moves' just changed */ /********************************/ /* once-per-turn things go here */ @@ -287,8 +300,9 @@ moveloop_core(void) u.uconduct.conflicting++; #ifdef EXTRAINFO_FN - if ((prev_dgl_extrainfo == 0) || (prev_dgl_extrainfo < (gm.moves + 250))) { - prev_dgl_extrainfo = gm.moves; + if ((prev_dgl_extrainfo == 0) + || (prev_dgl_extrainfo < (svm.moves + 250))) { + prev_dgl_extrainfo = svm.moves; mk_dgl_extrainfo(); } #endif @@ -318,7 +332,7 @@ moveloop_core(void) You("wither away completely!"); } losehp(loss, "withered away", NO_KILLER_PREFIX); - gc.context.botl = TRUE; + disp.botl = TRUE; interrupt_multi("You are slowly withering away."); } @@ -327,8 +341,8 @@ moveloop_core(void) /* moving around while encumbered is hard work */ if (mvl_wtcap > MOD_ENCUMBER && u.umoved) { - if (!(mvl_wtcap < EXT_ENCUMBER ? gm.moves % 30 - : gm.moves % 10)) { + if (!(mvl_wtcap < EXT_ENCUMBER ? svm.moves % 30 + : svm.moves % 10)) { overexert_hp(); } } @@ -355,7 +369,7 @@ moveloop_core(void) mvl_change = 0; if (Polymorph && !rn2(100)) mvl_change = 1; - else if (u.ulycn >= LOW_PM && !Upolyd + else if (ismnum(u.ulycn) && !Upolyd && !rn2(80 - (20 * night()))) mvl_change = 2; if (mvl_change && !Unchanging) { @@ -385,7 +399,8 @@ moveloop_core(void) nauseating_loc_effects(); - if (!gl.level.flags.noautosearch && Searching && gm.multi >= 0) + if (Searching && !svl.level.flags.noautosearch + && gm.multi >= 0) (void) dosearch0(1); if (Warning) warnreveal(); @@ -414,7 +429,7 @@ moveloop_core(void) /* vision will be updated as bubbles move */ if (Is_waterlevel(&u.uz) || Is_airlevel(&u.uz)) movebubbles(); - else if (gl.level.flags.fumaroles) + else if (svl.level.flags.fumaroles) fumaroles(); /* when immobile, count is in turns */ @@ -447,13 +462,13 @@ moveloop_core(void) if (iflags.hilite_delta) status_eval_next_unhilite(); #endif - if (gm.moves >= gc.context.seer_turn) { + if (svm.moves >= svc.context.seer_turn) { if ((u.uhave.amulet || Clairvoyant) && !In_endgame(&u.uz) && !BClairvoyant) do_vicinity_map((struct obj *) 0); /* we maintain this counter even when clairvoyance isn't taking place; on average, go again 30 turns from now */ - gc.context.seer_turn = gm.moves + (long) rn1(31, 15); /*15..45*/ + svc.context.seer_turn = svm.moves + (long) rn1(31, 15); /*15..45*/ /* [it used to be that on every 15th turn, there was a 50% chance of farsight, so it could happen as often as every 15 turns or theoretically never happen at all; but when @@ -474,6 +489,7 @@ moveloop_core(void) else if (u.uburied) under_ground(0); + see_nearby_monsters(); } /* actual time passed */ /****************************************/ @@ -483,7 +499,7 @@ moveloop_core(void) clear_splitobjs(); find_ac(); /* passive_gold_detect(); */ /* too buggy right now */ - if (!gc.context.mv || Blind) { + if (!svc.context.mv || Blind) { /* redo monsters if hallu or wearing a helm of telepathy */ if (Hallucination) { /* update screen randomly */ see_monsters(); @@ -491,23 +507,28 @@ moveloop_core(void) see_traps(); if (u.uswallow) swallowed(0); - } else if (Unblind_telepat) { - see_monsters(); - } else if (Warning || Warn_of_mon) + } else if (Unblind_telepat || Warning || Warn_of_mon + /* this is needed for the case where you saw a monster + due to being next to it while it's in a gas cloud + and then you moved away; it should no longer be seen + when that happens, even if it hasn't moved */ + || any_visible_region()) { /* TODO: optimize this */ see_monsters(); - + } if (gv.vision_full_recalc) vision_recalc(0); /* vision! */ } - if (gc.context.botl || gc.context.botlx) { + if (disp.botl || disp.botlx) { bot(); curs_on_u(); - } else if (iflags.time_botl) { + } else if (disp.time_botl) { timebot(); curs_on_u(); } - gc.context.move = 1; + m_everyturn_effect(&gy.youmonst); + + svc.context.move = 1; if (gm.multi >= 0 && go.occupation) { #if defined(MICRO) || defined(WIN32CON) @@ -552,23 +573,23 @@ moveloop_core(void) runmode_delay_output(); if (!gm.multi) { /* lookaround may clear multi */ - gc.context.move = 0; + svc.context.move = 0; return; } - if (gc.context.mv) { + if (svc.context.mv) { if (gm.multi < COLNO && !--gm.multi) end_running(TRUE); domove(); } else { --gm.multi; nhassert(gc.command_count != 0); - rhack(gc.command_line); + rhack(gc.cmd_key); } } else if (gm.multi == 0) { #ifdef MAIL ckmailstatus(); #endif - rhack((char *) 0); + rhack(0); } if (u.utotype) /* change dungeon level */ deferred_goto(); /* after rhack() */ @@ -576,11 +597,11 @@ moveloop_core(void) if (gv.vision_full_recalc) vision_recalc(0); /* vision! */ /* when running in non-tport mode, this gets done through domove() */ - if ((!gc.context.run || flags.runmode == RUN_TPORT) - && (gm.multi && (!gc.context.travel ? !(gm.multi % 7) - : !(gm.moves % 7L)))) { - if (flags.time && gc.context.run) - gc.context.botl = TRUE; + if ((!svc.context.run || flags.runmode == RUN_TPORT) + && (gm.multi && (!svc.context.travel ? !(gm.multi % 7) + : !(svm.moves % 7L)))) { + if (flags.time && svc.context.run) + disp.botl = TRUE; /* [should this be flush_screen() instead?] */ display_nhwindow(WIN_MAP, FALSE); } @@ -588,11 +609,12 @@ moveloop_core(void) if (gl.luacore && nhcb_counts[NHCB_END_TURN]) { lua_getglobal(gl.luacore, "nh_callback_run"); lua_pushstring(gl.luacore, nhcb_name[NHCB_END_TURN]); - nhl_pcall(gl.luacore, 1, 0); + nhl_pcall_handle(gl.luacore, 1, 0, "moveloop_core", NHLpa_panic); + lua_settop(gl.luacore, 0); } } -static void +staticfn void maybe_do_tutorial(void) { s_level *sp = find_level("tut-1"); @@ -603,7 +625,8 @@ maybe_do_tutorial(void) if (ask_do_tutorial()) { assign_level(&u.ucamefrom, &u.uz); iflags.nofollowers = TRUE; - schedule_goto(&sp->dlevel, UTOTYPE_NONE, (char *) 0, (char *) 0); + schedule_goto(&sp->dlevel, UTOTYPE_NONE, + "Entering the tutorial.", (char *) 0); deferred_goto(); vision_recalc(0); docrt(); @@ -624,7 +647,7 @@ moveloop(boolean resuming) } } -static void +staticfn void regen_pw(int wtcap) { if ((u.uen < u.uenmax) && (wtcap < MOD_ENCUMBER)) { @@ -648,7 +671,7 @@ regen_pw(int wtcap) if (u.uen > u.uenmax) u.uen = u.uenmax; - gc.context.botl = TRUE; + disp.botl = TRUE; if (u.uen == u.uenmax) interrupt_multi("You feel full of energy."); } @@ -657,7 +680,7 @@ regen_pw(int wtcap) #define U_CAN_REGEN() (Regeneration || (Sleepy && u.usleep)) /* maybe recover some lost health (or lose some when an eel out of water) */ -static void +staticfn void regen_hp(int wtcap) { int heal = 0; @@ -674,14 +697,14 @@ regen_hp(int wtcap) /* eel out of water loses hp, similar to monster eels; as hp gets lower, rate of further loss slows down */ if (u.mh > 1 && !Regeneration && rn2(u.mh) > rn2(8) - && (!Half_physical_damage || !(gm.moves % 2L))) + && (!Half_physical_damage || !(svm.moves % 2L))) heal = -1; } else if (u.mh < u.mhmax) { - if (U_CAN_REGEN() || (encumbrance_ok && !(gm.moves % 20L))) + if (U_CAN_REGEN() || (encumbrance_ok && !(svm.moves % 20L))) heal = 1; } if (heal && !(Withering && heal > 0)) { - gc.context.botl = TRUE; + disp.botl = TRUE; u.mh += heal / (fiend_adversity(PM_DISPATER) ? 2 : 1); reached_full = (u.mh == u.mhmax); } @@ -693,29 +716,15 @@ regen_hp(int wtcap) once u.mh reached u.mhmax; that may have been convenient for the player, but it didn't make sense for gameplay...] */ if (u.uhp < u.uhpmax && (encumbrance_ok || U_CAN_REGEN())) { - if (u.ulevel > 9) { - if (!(gm.moves % 3L)) { - int Con = (int) ACURR(A_CON); - - if (Con <= 12) { - heal = 1; - } else { - heal = rnd(Con); - if (heal > u.ulevel - 9) - heal = u.ulevel - 9; - } - } - } else { /* u.ulevel <= 9 */ - if (!(gm.moves % (long) ((MAXULEV + 12) / (u.ulevel + 2) + 1))) - heal = 1; - } - if (U_CAN_REGEN() && !heal) - heal = 1; + heal = (u.ulevel + (int)ACURR(A_CON)) > rn2(100); + + if (U_CAN_REGEN()) + heal += 1; if (Sleepy && u.usleep) heal++; if (heal && !(Withering && heal > 0)) { - gc.context.botl = TRUE; + disp.botl = TRUE; u.uhp += heal / (fiend_adversity(PM_DISPATER) ? 2 : 1); if (u.uhp > u.uhpmax) u.uhp = u.uhpmax; @@ -738,7 +747,7 @@ stop_occupation(void) if (!maybe_finished_meal(TRUE)) You("stop %s.", go.occtxt); go.occupation = (int (*)(void)) 0; - gc.context.botl = TRUE; /* in case u.uhs changed */ + disp.botl = TRUE; /* in case u.uhs changed */ nomul(0); } else if (gm.multi >= 0) { nomul(0); @@ -760,6 +769,13 @@ init_sound_disp_gamewindows(void) SoundAchievement(0, sa2_newgame_nosplash, 0); } +#ifdef CHANGE_COLOR + /* init_nhwindows() has already been called, so before + creating the windows, check to see if there are any + palette entries to alter */ + change_palette(); +#endif + WIN_MESSAGE = create_nhwindow(NHW_MESSAGE); if (VIA_WINDOWPORT()) { status_initialize(0); @@ -768,6 +784,9 @@ init_sound_disp_gamewindows(void) } WIN_MAP = create_nhwindow(NHW_MAP); WIN_INVEN = create_nhwindow(NHW_MENU); + if (WIN_INVEN != WIN_ERR) + adjust_menu_promptstyle(WIN_INVEN, &iflags.menu_headings); + #ifdef TTY_PERM_INVENT if (WINDOWPORT(tty) && WIN_INVEN != WIN_ERR) { menu_behavior = MENU_BEHAVE_PERMINV; @@ -797,6 +816,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 @@ -804,18 +827,20 @@ newgame(void) { int i; - gc.context.botlx = TRUE; - gc.context.ident = 1; - gc.context.warnlevel = 1; - gc.context.next_attrib_check = 600L; /* arbitrary first setting */ - gc.context.tribute.enabled = TRUE; /* turn on 3.6 tributes */ - gc.context.tribute.tributesz = sizeof(struct tribute_info); + /* make sure welcome messages are given before noticing monsters */ + notice_mon_off(); + disp.botlx = TRUE; + 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 */ + svc.context.tribute.tributesz = sizeof(struct tribute_info); /* Extra entropy added to sysopt.serverseed */ /* sysopt.serverseed += rn2(8000000); */ for (i = LOW_PM; i < NUMMONS; i++) - gm.mvitals[i].mvflags = mons[i].geno & G_NOCORPSE; + svm.mvitals[i].mvflags = mons[i].geno & G_NOCORPSE; init_objects(); /* must be before u_init() */ @@ -832,7 +857,7 @@ newgame(void) u_init(); - l_nhcore_init(); /* create a Lua state that lasts until the end of the game */ + l_nhcore_init(); /* create a Lua state that lasts until end of game */ reset_glyphmap(gm_newgame); #ifndef NO_SIGNAL (void) signal(SIGINT, (SIG_RET_TYPE) done1); @@ -877,7 +902,7 @@ newgame(void) if (flags.legacy) { flush_screen(1); - com_pager("legacy"); + com_pager(u.uroleplay.pauper ? "pauper_legacy" : "legacy"); } urealtime.realtime = 0L; @@ -885,14 +910,19 @@ newgame(void) #ifdef INSURANCE save_currentstate(); #endif - gp.program_state.something_worth_saving++; /* useful data now exists */ + program_state.something_worth_saving++; /* useful data now exists */ /* Success! */ welcome(TRUE); + notice_mon_on(); /* now we can notice monsters */ + if (a11y.glyph_updates) + (void) dolookaround(); + else + notice_all_mons(TRUE); return; } -/* show "welcome [back] to nethack" message at program startup */ +/* show "welcome [back] to NetHack" message at program startup */ void welcome(boolean new_game) /* false => restoring an old game */ { @@ -921,15 +951,16 @@ welcome(boolean new_game) /* false => restoring an old game */ Sprintf(eos(buf), " %s", align_str(u.ualignbase[A_ORIGINAL])); if (!gu.urole.name.f && (new_game - ? (gu.urole.allow & ROLE_GENDMASK) == (ROLE_MALE | ROLE_FEMALE) - : currentgend != flags.initgend)) + ? (gu.urole.allow & ROLE_GENDMASK) == (ROLE_MALE | ROLE_FEMALE) + : currentgend != flags.initgend)) Sprintf(eos(buf), " %s", genders[currentgend].adj); Sprintf(eos(buf), " %s %s", gu.urace.adj, - (currentgend && gu.urole.name.f) ? gu.urole.name.f : gu.urole.name.m); + (currentgend && gu.urole.name.f) ? gu.urole.name.f + : gu.urole.name.m); pline(new_game ? "%s %s, welcome to xNetHack! You are a%s." : "%s %s, the%s, welcome back to xNetHack!", - Hello((struct monst *) 0), gp.plname, buf); + Hello((struct monst *) 0), svp.plname, buf); if (Hallucination) pline("xNetHack is filmed in front of an undead studio audience."); @@ -937,34 +968,46 @@ welcome(boolean new_game) /* false => restoring an old game */ if (new_game) { /* guarantee that 'major' event category is never empty */ livelog_printf(LL_ACHIEVE, "%s the%s entered the dungeon", - gp.plname, buf); + svp.plname, buf); } else { /* if restoring in Gehennom, give same hot/smoky message as when first entering it */ hellish_smoke_mesg(); + /* remind player of the level annotation, like in goto_level() */ + print_level_annotation(); } } #ifdef POSITIONBAR -static void +staticfn void do_positionbar(void) { + /* FIXME: this will break if any coordinate is too big for (char); + the sys/msdos/vid*.c code uses (unsigned char) which is less + vulnerable but not guaranteed to be able to hold coordxy values; + also, there doesn't appear to be any need for this to be static, + nor to contain pairs of (> or <) and x; it could just be a full + line of spaces and > or < characters with update_positionbar() + revised to reconstruct the x values for non-space characters */ static char pbar[COLNO]; char *p; - stairway *stway = gs.stairs; + stairway *stway; + coordxy x, y; + int glyph, symbol; p = pbar; /* TODO: use the same method as getpos() so objects don't cover stairs */ - while (stway) { - int x = stway->sx; - int y = stway->sy; - int glyph = glyph_to_cmap(gl.level.locations[x][y].glyph); - - if (is_cmap_stairs(glyph)) { + /* FIXME: traversing 'stairs' list ignores mimics that pose as stairs */ + for (stway = gs.stairs; stway; stway = stway->next) { + x = stway->sx; + y = stway->sy; + glyph = levl[x][y].glyph; + symbol = glyph_to_cmap(glyph); + + if (is_cmap_stairs(symbol)) { *p++ = (stway->up ? '<' : '>'); - *p++ = stway->sx; + *p++ = (char) x; } - stway = stway->next; } /* hero location */ @@ -979,12 +1022,12 @@ do_positionbar(void) } #endif -static void +staticfn void interrupt_multi(const char *msg) { - if (gm.multi > 0 && !gc.context.travel && !gc.context.run) { + if (gm.multi > 0 && !svc.context.travel && !svc.context.run) { nomul(0); - if (Verbose(0,interrupt_multi) && msg) + if (flags.verbose && msg) Norep("%s", msg); } } @@ -1005,16 +1048,17 @@ interrupt_multi(const char *msg) static const struct early_opt earlyopts[] = { { ARG_DEBUG, "debug", 5, TRUE }, { ARG_VERSION, "version", 4, TRUE }, - { ARG_SHOWPATHS, "showpaths", 9, FALSE }, + { ARG_SHOWPATHS, "showpaths", 8, FALSE }, #ifndef NODUMPENUMS { ARG_DUMPENUMS, "dumpenums", 9, FALSE }, #endif -#ifdef ENHANCED_SYMBOLS { ARG_DUMPGLYPHIDS, "dumpglyphids", 12, FALSE }, -#endif #ifdef WIN32 { ARG_WINDOWS, "windows", 4, TRUE }, #endif +#if defined(CRASHREPORT) + { ARG_BIDSHOW, "bidshow", 7, FALSE }, +#endif }; #ifdef WIN32 @@ -1036,11 +1080,12 @@ argcheck(int argc, char *argv[], enum earlyarg e_arg) const char *dashdash = ""; for (idx = 0; idx < SIZE(earlyopts); idx++) { - if (earlyopts[idx].e == e_arg) + if (earlyopts[idx].e == e_arg){ break; + } } if (idx >= SIZE(earlyopts) || argc < 1) - return FALSE; + return 0; for (i = 0; i < argc; ++i) { if (argv[i][0] != '-') @@ -1075,13 +1120,25 @@ argcheck(int argc, char *argv[], enum earlyarg e_arg) if (extended_opt) { extended_opt++; + /* Deprecated in favor of "copy" - remove no later + than next major version */ if (match_optname(extended_opt, "paste", 5, FALSE)) { insert_into_pastebuf = TRUE; - } else { - raw_printf( - "-%sversion can only be extended with -%sversion:paste.\n", + } else if (match_optname(extended_opt, "copy", 4, FALSE)) { + insert_into_pastebuf = TRUE; + } else if (match_optname(extended_opt, "dump", 4, FALSE)) { + /* version number plus enabled features and sanity + values that the program compares against the same + thing recorded in save and bones files to check + whether they're being used compatibly */ + dump_version_info(); + return 2; /* done */ + } else if (!match_optname(extended_opt, "show", 4, FALSE)) { + raw_printf("-%sversion can only be extended with" + " -%sversion:copy or :dump or :show.\n", dashdash, dashdash); - return TRUE; + /* exit after we've reported bad command line argument */ + return 2; } } early_version_info(insert_into_pastebuf); @@ -1094,10 +1151,13 @@ argcheck(int argc, char *argv[], enum earlyarg e_arg) dump_enums(); return 2; #endif -#ifdef ENHANCED_SYMBOLS case ARG_DUMPGLYPHIDS: dump_glyphids(); return 2; +#ifdef CRASHREPORT + case ARG_BIDSHOW: + crashreport_bidshow(); + return 2; #endif #ifdef WIN32 case ARG_WINDOWS: @@ -1105,13 +1165,14 @@ argcheck(int argc, char *argv[], enum earlyarg e_arg) extended_opt++; return windows_early_options(extended_opt); } + FALLTHROUGH; /*FALLTHRU*/ #endif default: break; } }; - return FALSE; + return 0; } /* @@ -1126,7 +1187,7 @@ argcheck(int argc, char *argv[], enum earlyarg e_arg) * optimization so that display output * can be debugged without buffering. */ -static void +staticfn void debug_fields(const char *opts) { char *op; @@ -1190,34 +1251,102 @@ timet_delta(time_t etim, time_t stim) /* end and start times */ return (long) difftime(etim, stim); } -#if !defined(NODUMPENUMS) || defined(ENHANCED_SYMBOLS) +#if !defined(NODUMPENUMS) /* monsdump[] and objdump[] are also used in utf8map.c */ + #define DUMP_ENUMS struct enum_dump monsdump[] = { #include "monsters.h" { NUMMONS, "NUMMONS" }, + { NON_PM, "NON_PM" }, + { LOW_PM, "LOW_PM" }, + { HIGH_PM, "HIGH_PM" }, + { SPECIAL_PM, "SPECIAL_PM" } }; struct enum_dump objdump[] = { #include "objects.h" { NUM_OBJECTS, "NUM_OBJECTS" }, }; + +#define DUMP_ENUMS_PCHAR +static struct enum_dump defsym_cmap_dump[] = { +#include "defsym.h" + { MAXPCHARS, "MAXPCHARS" }, +}; +#undef DUMP_ENUMS_PCHAR + +#define DUMP_ENUMS_MONSYMS +static struct enum_dump defsym_mon_syms_dump[] = { +#include "defsym.h" + { MAXMCLASSES, "MAXMCLASSES" }, +}; +#undef DUMP_ENUMS_MONSYMS + +#define DUMP_ENUMS_MONSYMS_DEFCHAR +static struct enum_dump defsym_mon_defchars_dump[] = { +#include "defsym.h" +}; +#undef DUMP_ENUMS_MONSYMS_DEFCHAR + +#define DUMP_ENUMS_OBJCLASS_DEFCHARS +static struct enum_dump objclass_defchars_dump[] = { +#include "defsym.h" +}; +#undef DUMP_ENUMS_OBJCLASS_DEFCHARS + +#define DUMP_ENUMS_OBJCLASS_CLASSES +static struct enum_dump objclass_classes_dump[] = { +#include "defsym.h" + { MAXOCLASSES, "MAXOCLASSES" }, +}; +#undef DUMP_ENUMS_OBJCLASS_CLASSES + +#define DUMP_ENUMS_OBJCLASS_SYMS +static struct enum_dump objclass_syms_dump[] = { +#include "defsym.h" +}; +#undef DUMP_ENUMS_OBJCLASS_SYMS + +#define DUMP_ARTI_ENUM +static struct enum_dump arti_enum_dump[] = { +#include "artilist.h" + { AFTER_LAST_ARTIFACT, "AFTER_LAST_ARTIFACT" } +}; +#undef DUMP_ARTI_ENUM + #undef DUMP_ENUMS + #ifndef NODUMPENUMS -static void + +staticfn void dump_enums(void) { enum enum_dumps { monsters_enum, objects_enum, objects_misc_enum, + defsym_cmap_enum, + defsym_mon_syms_enum, + defsym_mon_defchars_enum, + objclass_defchars_enum, + objclass_classes_enum, + objclass_syms_enum, + arti_enum, NUM_ENUM_DUMPS }; static const char *const titles[NUM_ENUM_DUMPS] = { - "monnums", "objects_nums" , "misc_object_nums" + "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[] = { + dump_om(LAST_GENERIC), + dump_om(OBJCLASS_HACK), + dump_om(FIRST_OBJECT), dump_om(FIRST_AMULET), dump_om(LAST_AMULET), dump_om(FIRST_SPELL), @@ -1233,22 +1362,53 @@ dump_enums(void) }; #undef dump_om static const struct enum_dump *const ed[NUM_ENUM_DUMPS] = { - monsdump, objdump, omdump + monsdump, objdump, omdump, + defsym_cmap_dump, defsym_mon_syms_dump, + defsym_mon_defchars_dump, + objclass_defchars_dump, + objclass_classes_dump, + objclass_syms_dump, + arti_enum_dump, + }; + static const char *const pfx[NUM_ENUM_DUMPS] = { + "PM_", "", "", "", "", "", "", "", "", "" }; - static const char *const pfx[NUM_ENUM_DUMPS] = { "PM_", "", "" }; - static int szd[NUM_ENUM_DUMPS] = { - SIZE(monsdump), SIZE(objdump), SIZE(omdump) + /* 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), }; 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) { - nmprefix = (j == szd[i] - 1) ? "" : pfx[i]; /* "" or "PM_" */ + int unprefixed_count = (i == monsters_enum) ? 4 : 1; + nmprefix = (j >= szd[i] - unprefixed_count) + ? "" : pfx[i]; /* "" or "PM_" */ nmwidth = 27 - (int) strlen(nmprefix); /* 27 or 24 */ - raw_printf(" %s%*s = %3d,", - nmprefix, -nmwidth, ed[i][j].nm, ed[i][j].val); + if (dumpflgs[i] > 0) { + Snprintf(comment, sizeof comment, + " /* '%c' */", + (ed[i][j].val >= 32 && ed[i][j].val <= 126) + ? ed[i][j].val : ' '); + } else { + comment[0] = '\0'; + } + raw_printf(" %s%*s = %3d,%s", + nmprefix, -nmwidth, + ed[i][j].nm, ed[i][j].val, + comment); } raw_print("};"); raw_print(""); @@ -1257,13 +1417,11 @@ dump_enums(void) } #endif /* NODUMPENUMS */ -#ifdef ENHANCED_SYMBOLS void dump_glyphids(void) { dump_all_glyphids(stdout); } -#endif /* ENHANCED_SYMBOLS */ -#endif /* !NODUMPENUMS || ENHANCED_SYMBOLS */ +#endif /* !NODUMPENUMS */ /*allmain.c*/ diff --git a/src/alloc.c b/src/alloc.c index ef39244715..72faa160bf 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -1,9 +1,8 @@ -/* NetHack 3.7 alloc.c $NHDT-Date: 1596498147 2020/08/03 23:42:27 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.18 $ */ +/* 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. */ -/* to get the malloc() prototype from system.h */ #define ALLOC_C /* comment line for pre-compiled headers */ /* since this file is also used in auxiliary programs, don't include all the function declarations for all of nethack */ @@ -14,10 +13,11 @@ #include "nhlua.h" #endif -/*#define FITSint(x) FITSint_(x, __func__, (int) __LINE__)*/ -extern int FITSint_(LUA_INTEGER, const char *, int); -/*#define FITSuint(x) FITSuint_(x, __func__, (int) __LINE__)*/ -extern unsigned FITSuint_(unsigned long long, const char *, int); + +/*#define FITSint(x) FITSint_(x, __func__, __LINE__)*/ +extern int FITSint_(LUA_INTEGER, const char *, int) NONNULLARG2; +/*#define FITSuint(x) FITSuint_(x, __func__, __LINE__)*/ +extern unsigned FITSuint_(unsigned long long, const char *, int) NONNULLARG2; char *fmt_ptr(const genericptr) NONNULL; @@ -26,25 +26,56 @@ char *fmt_ptr(const genericptr) NONNULL; #undef re_alloc #undef free extern void free(genericptr_t); -static void heapmon_init(void); +staticfn void heapmon_init(void); static FILE *heaplog = 0; static boolean tried_heaplog = FALSE; #endif +/* + * For historical reasons, nethack's alloc() returns 'long *' rather + * than 'void *' or 'char *'. + * + * Some static analysis complains if it can't deduce that the number + * of bytes being allocated is a multiple of 'sizeof (long)'. It + * recognizes that the following manipulation overcomes that via + * rounding the requested length up to the next long. NetHack doesn't + * make a lot of tiny allocations, so this shouldn't waste much memory + * regardless of whether malloc() does something similar. NetHack + * isn't expected to call alloc(0), but if that happens treat it as + * alloc(sizeof (long)) instead. + */ +#define ForceAlignedLength(LTH) \ + do { \ + if (!(LTH) || (LTH) % sizeof (long) != 0) \ + (LTH) += sizeof (long) - (LTH) % sizeof (long); \ + } while (0) + +#ifndef MONITOR_HEAP long *alloc(unsigned int) NONNULL; long *re_alloc(long *, unsigned int) NONNULL; +#else + /* for #if MONITOR_HEAP, alloc() might return Null but only nhalloc() + should be calling it; nhalloc() never returns Null */ +long *alloc(unsigned int); +long *re_alloc(long *, unsigned int); +long *nhalloc(unsigned int, const char *, int) NONNULL; +long *nhrealloc(long *, unsigned int, const char *, int) NONNULL; +#endif ATTRNORETURN extern void panic(const char *, ...) PRINTF_F(1, 2) NORETURN; long * alloc(unsigned int lth) { - register genericptr_t ptr; + genericptr_t ptr; + ForceAlignedLength(lth); ptr = malloc(lth); #ifndef MONITOR_HEAP if (!ptr) panic("Memory allocation failure; cannot get %u bytes", lth); +#else + /* for #if MONITOR_HEAP, failure is handled in nhalloc() */ #endif return (long *) ptr; } @@ -53,11 +84,16 @@ alloc(unsigned int lth) long * re_alloc(long *oldptr, unsigned int newlth) { - long *newptr = (long *) realloc((genericptr_t) oldptr, (size_t) newlth); + long *newptr; + + ForceAlignedLength(newlth); + newptr = (long *) realloc((genericptr_t) oldptr, (size_t) newlth); #ifndef MONITOR_HEAP /* "extend to": assume it won't ever fail if asked to shrink */ if (newlth && !newptr) panic("Memory allocation failure; cannot extend to %u bytes", newlth); +#else + /* for #if MONITOR_HEAP, failure is handled in nhrealloc() */ #endif return newptr; } @@ -102,7 +138,7 @@ fmt_ptr(const genericptr ptr) /* If ${NH_HEAPLOG} is defined and we can create a file by that name, then we'll log the allocation and release information to that file. */ -static void +staticfn void heapmon_init(void) { char *logname = getenv("NH_HEAPLOG"); @@ -185,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 @@ -197,23 +237,28 @@ 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); } -/* similar for reasonable size strings, but return the length of the input as well */ +/* similar for reasonable size strings, but return length of input as well */ char * 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); } + /* cast to int or panic on overflow; use via macro */ int FITSint_(LUA_INTEGER i, const char *file, int line) diff --git a/src/apply.c b/src/apply.c index e623f1765a..8e763adcbb 100644 --- a/src/apply.c +++ b/src/apply.c @@ -1,63 +1,85 @@ -/* NetHack 3.7 apply.c $NHDT-Date: 1655631557 2022/06/19 09:39:17 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.381 $ */ +/* NetHack 3.7 apply.c $NHDT-Date: 1737275719 2025/01/19 00:35:19 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.464 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" -static int use_camera(struct obj *); -static int use_towel(struct obj *); -static boolean its_dead(coordxy, coordxy, int *, struct obj *); -static int use_stethoscope(struct obj *); -static void use_whistle(struct obj *); -static void use_magic_whistle(struct obj *); -static void magic_whistled(struct obj *); -static int use_leash(struct obj *); -static boolean mleashed_next2u(struct monst *); -static int use_mirror(struct obj *); -static void use_bell(struct obj **); -static void use_candelabrum(struct obj *); -static void use_candle(struct obj **); -static void use_lamp(struct obj *); -static void light_cocktail(struct obj **); -static int use_silver_on_withering(struct obj *); -static int rub_ok_core(struct obj *, boolean); -static int rub_ok(struct obj *); -static void display_jump_positions(int); -static void use_tinning_kit(struct obj *); -static int use_figurine(struct obj **); -static int grease_ok(struct obj *); -static int use_grease(struct obj *); -static void use_trap(struct obj *); -static int touchstone_ok(struct obj *); -static int use_stone(struct obj *); -static int set_trap(void); /* occupation callback */ -static void display_polearm_positions(int); -static int use_cream_pie(struct obj *); -static int jelly_ok(struct obj *); -static int use_royal_jelly(struct obj **); -static int grapple_range(void); -static boolean can_grapple_location(coordxy, coordxy); -static int use_grapple(struct obj *); -static int do_break_wand(struct obj *); -static int apply_ok(struct obj *); -static int flip_through_book(struct obj *); -static boolean figurine_location_checks(struct obj *, coord *, boolean); -static boolean check_jump(genericptr_t, coordxy, coordxy); -static boolean is_valid_jump_pos(coordxy, coordxy, int, boolean); -static boolean get_valid_jump_position(coordxy, coordxy); -static boolean get_valid_polearm_position(coordxy, coordxy); -static boolean find_poleable_mon(coord *, int, int); +staticfn int use_camera(struct obj *); +staticfn int use_towel(struct obj *); +staticfn boolean its_dead(coordxy, coordxy, int *, struct obj *); +staticfn int use_stethoscope(struct obj *); +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 **); +staticfn void use_candelabrum(struct obj *); +staticfn void use_candle(struct obj **); +staticfn void use_lamp(struct obj *); +staticfn void light_cocktail(struct obj **); +staticfn int use_silver_on_withering(struct obj *); +staticfn int rub_ok_core(struct obj *, boolean); +staticfn int rub_ok(struct obj *); +staticfn void display_jump_positions(boolean); +staticfn void use_tinning_kit(struct obj *); +staticfn int use_figurine(struct obj **); +staticfn int grease_ok(struct obj *); +staticfn int use_grease(struct obj *); +staticfn void use_trap(struct obj *); +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 int use_cream_pie(struct obj *); +staticfn int jelly_ok(struct obj *); +staticfn int use_royal_jelly(struct obj **); +staticfn int grapple_range(void); +staticfn boolean can_grapple_location(coordxy, coordxy); +staticfn void display_grapple_positions(boolean); +staticfn int use_grapple(struct obj *); +staticfn void discard_broken_wand(void); +staticfn void broken_wand_explode(struct obj *, int, int); +staticfn void maybe_dunk_boulders(coordxy, coordxy); +staticfn int do_break_wand(struct obj *); +staticfn int apply_ok(struct obj *); +staticfn int flip_through_book(struct obj *); +staticfn int flip_coin(struct obj *); +staticfn boolean figurine_location_checks(struct obj *, coord *, boolean); +staticfn boolean check_jump(genericptr_t, coordxy, coordxy); +staticfn boolean is_valid_jump_pos(coordxy, coordxy, int, boolean); +staticfn boolean get_valid_jump_position(coordxy, coordxy); +staticfn boolean get_valid_polearm_position(coordxy, coordxy); +staticfn boolean find_poleable_mon(coord *, int, int); static const char - Nothing_seems_to_happen[] = "Nothing seems to happen.", no_elbow_room[] = "don't have enough elbow-room to maneuver."; -static int -use_camera(struct obj *obj) +void +do_blinding_ray(struct obj *obj) { - struct monst *mtmp; + struct monst *mtmp = bhit(u.dx, u.dy, COLNO, FLASHED_LIGHT, + (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) { + (void) flash_hits_mon(mtmp, obj); + if (obj->otyp == EXPENSIVE_CAMERA) + see_monster_closeup(mtmp); + } + /* normally bhit() would do this but for FLASHED_LIGHT we want it + to be deferred until after flash_hits_mon() */ + transient_light_cleanup(); +} + +staticfn int +use_camera(struct obj *obj) +{ if (Underwater) { pline("Using your camera underwater would void the warranty."); return ECMD_OK; @@ -82,20 +104,12 @@ use_camera(struct obj *obj) } else if (!u.dx && !u.dy) { (void) zapyourself(obj, TRUE); } else { - mtmp = bhit(u.dx, u.dy, COLNO, FLASHED_LIGHT, - (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) - (void) flash_hits_mon(mtmp, obj); - /* normally bhit() would do this but for FLASHED_LIGHT we want it - to be deferred until after flash_hits_mon() */ - transient_light_cleanup(); + do_blinding_ray(obj); } return ECMD_TIME; } -static int +staticfn int use_towel(struct obj *obj) { boolean drying_feedback = (obj == uwep); @@ -181,7 +195,7 @@ use_towel(struct obj *obj) } /* maybe give a stethoscope message based on floor objects */ -static boolean +staticfn boolean its_dead(coordxy rx, coordxy ry, int *resp, struct obj *stethoscope) { char buf[BUFSZ]; @@ -192,7 +206,7 @@ its_dead(coordxy rx, coordxy ry, int *resp, struct obj *stethoscope) /* Set only one of corpse, statue, and egg based on which is the topmost. */ corpse = statue = egg = NULL; - for (otmp = gl.level.objects[rx][ry]; otmp; otmp = otmp->nexthere) { + for (otmp = svl.level.objects[rx][ry]; otmp; otmp = otmp->nexthere) { /* can't reach corpse on floor */ if (otmp->otyp == CORPSE && floor) { corpse = otmp; @@ -232,8 +246,7 @@ its_dead(coordxy rx, coordxy ry, int *resp, struct obj *stethoscope) /* (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]; @@ -327,7 +340,7 @@ static const char hollow_str[] = "a hollow sound. This must be a secret %s!"; not take any time; however, unless it did, the stethoscope would be almost useless. As a compromise, one use per turn is free, another uses up the turn; this makes curse status have a tangible effect. */ -static int +staticfn int use_stethoscope(struct obj *obj) { struct monst *mtmp; @@ -350,8 +363,8 @@ use_stethoscope(struct obj *obj) if (!getdir((char *) 0)) return ECMD_CANCEL; - res = (gh.hero_seq == gc.context.stethoscope_seq) ? ECMD_TIME : ECMD_OK; - gc.context.stethoscope_seq = gh.hero_seq; + res = (gh.hero_seq == svc.context.stethoscope_seq) ? ECMD_TIME : ECMD_OK; + svc.context.stethoscope_seq = gh.hero_seq; gb.bhitpos.x = u.ux, gb.bhitpos.y = u.uy; /* tentative, reset below */ gn.notonhead = u.uswallow; @@ -448,7 +461,7 @@ use_stethoscope(struct obj *obj) pline("%s %s %s really %s.", use_plural ? "Those" : "That", what, use_plural ? "are" : "is", mnm); - } else if (Verbose(0, use_stethoscope) && !canspotmon(mtmp)) { + } else if (flags.verbose && !canspotmon(mtmp)) { There("is %s there.", mnm); } @@ -466,6 +479,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: @@ -484,7 +498,7 @@ use_stethoscope(struct obj *obj) static const char whistle_str[] = "produce a %s whistling sound.", alt_whistle_str[] = "produce a %s, sharp vibration."; -static void +staticfn void use_whistle(struct obj *obj) { if (!can_blow(&gy.youmonst)) { @@ -497,13 +511,13 @@ use_whistle(struct obj *obj) else You(whistle_str, obj->cursed ? "shrill" : "high"); Soundeffect(se_shrill_whistle, 50); - wake_nearby(); + wake_nearby(TRUE); if (obj->cursed) vault_summon_gd(); } } -static void +staticfn void use_magic_whistle(struct obj *obj) { if (!can_blow(&gy.youmonst)) { @@ -514,7 +528,7 @@ use_magic_whistle(struct obj *obj) else You("produce a %shigh-%s.", Underwater ? "very " : "", Deaf ? "frequency vibration" : "pitched humming noise"); - wake_nearby(); + wake_nearby(TRUE); } else { /* it's magic! it works underwater too (at a higher pitch) */ You(Deaf ? alt_whistle_str : whistle_str, @@ -527,7 +541,7 @@ use_magic_whistle(struct obj *obj) } /* 'obj' is assumed to be a magic whistle */ -static void +staticfn void magic_whistled(struct obj *obj) { struct monst *mtmp, *nextmon; @@ -570,7 +584,10 @@ magic_whistled(struct obj *obj) mnexto(mtmp, !already_discovered ? RLOC_MSG : RLOC_NONE); if (mtmp->mx != omx || mtmp->my != omy) { - mtmp->mundetected = 0; /* reveal non-mimic hider iff it moved */ + if (mtmp->mundetected) { /* reveal non-mimic hider that moved */ + mtmp->mundetected = 0; + newsym(mtmp->mx, mtmp->my); + } /* * FIXME: * All relocated monsters should change positions essentially @@ -716,26 +733,27 @@ number_leashed(void) void o_unleash(struct obj *otmp) { - register struct monst *mtmp; + struct monst *mtmp; for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) if (mtmp->m_id == (unsigned) otmp->leashmon) { mtmp->mleashed = 0; - otmp->leashmon = 0; - update_inventory(); break; } + otmp->leashmon = 0; + update_inventory(); } /* mtmp is about to die, or become untame */ void m_unleash(struct monst *mtmp, boolean feedback) { - register struct obj *otmp; + struct obj *otmp; if (feedback) { if (canseemon(mtmp)) - pline("%s pulls free of %s leash!", Monnam(mtmp), mhis(mtmp)); + pline_mon(mtmp, "%s pulls free of %s leash!", + Monnam(mtmp), mhis(mtmp)); else Your("leash falls slack."); } @@ -750,8 +768,8 @@ m_unleash(struct monst *mtmp, boolean feedback) void unleash_all(void) { - register struct obj *otmp; - register struct monst *mtmp; + struct obj *otmp; + struct monst *mtmp; for (otmp = gi.invent; otmp; otmp = otmp->nobj) if (otmp->otyp == LEASH) @@ -770,13 +788,11 @@ leashable(struct monst *mtmp) && (!nolimbs(mtmp->data) || has_head(mtmp->data))); } -/* ARGSUSED */ -static int +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 @@ -803,8 +819,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; @@ -820,16 +836,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"); @@ -847,7 +867,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; } @@ -876,7 +896,6 @@ use_leash(struct obj *obj) spotmon ? y_monnam(mtmp) : l_monnam(mtmp)); } } - return ECMD_TIME; } /* assuming mtmp->mleashed has been checked */ @@ -891,13 +910,13 @@ get_mleash(struct monst *mtmp) return otmp; } -static boolean +staticfn boolean mleashed_next2u(struct monst *mtmp) { if (mtmp->mleashed) { - if (!next2u(mtmp->mx, mtmp->my)) + if (!m_next2u(mtmp)) mnexto(mtmp, RLOC_NOMSG); - if (!next2u(mtmp->mx, mtmp->my)) { + if (!m_next2u(mtmp)) { struct obj *otmp = get_mleash(mtmp); if (!otmp) { @@ -934,8 +953,8 @@ next_to_u(void) void check_leash(coordxy x, coordxy y) { - register struct obj *otmp; - register struct monst *mtmp; + struct obj *otmp; + struct monst *mtmp; for (otmp = gi.invent; otmp; otmp = otmp->nobj) { if (otmp->otyp != LEASH || otmp->leashmon == 0) @@ -965,7 +984,8 @@ check_leash(coordxy x, coordxy y) if (!DEADMONSTER(mtmp)) u.uconduct.killer = save_pacifism; } else { - pline("%s is choked by the leash!", Monnam(mtmp)); + pline_mon(mtmp, "%s is choked by the leash!", + Monnam(mtmp)); /* tameness eventually drops to 1 here (never 0) */ if (mtmp->mtame && rn2(mtmp->mtame)) mtmp->mtame--; @@ -1002,7 +1022,7 @@ beautiful(void) const char *res; int cha = ACURR(A_CHA); - /* don't bother complaining about the sexism; nethack is not real life */ + /* don't bother complaining about the sexism; NetHack is not real life */ res = ((cha >= 25) ? "sublime" /* 25 is the maximum possible */ : (cha >= 19) ? "splendorous" /* note: not "splendiferous" */ : (cha >= 16) ? ((poly_gender() == 1) ? "beautiful" : "handsome") @@ -1017,7 +1037,7 @@ beautiful(void) static const char look_str[] = "look %s."; -static int +staticfn int use_mirror(struct obj *obj) { const char *mirror, *uvisage; @@ -1036,7 +1056,7 @@ use_mirror(struct obj *obj) if (!Blind) pline_The("%s fogs up and doesn't reflect!", mirror); else - pline("%s", Nothing_seems_to_happen); + pline("%s", nothing_seems_to_happen); return ECMD_TIME; } if (!u.dx && !u.dy && !u.dz) { @@ -1199,10 +1219,10 @@ use_mirror(struct obj *obj) #undef SEENMON } -static void +staticfn void use_bell(struct obj **optr) { - register struct obj *obj = *optr; + struct obj *obj = *optr; struct monst *mtmp; boolean wakem = FALSE, learno = FALSE, ordinary = (obj->otyp != BELL_OF_OPENING || !obj->spe), @@ -1224,9 +1244,9 @@ use_bell(struct obj **optr) } else if (ordinary) { if (obj->cursed && !rn2(4) /* note: once any of them are gone, we stop all of them */ - && !(gm.mvitals[PM_WOOD_NYMPH].mvflags & G_GONE) - && !(gm.mvitals[PM_WATER_NYMPH].mvflags & G_GONE) - && !(gm.mvitals[PM_MOUNTAIN_NYMPH].mvflags & G_GONE) + && !(svm.mvitals[PM_WOOD_NYMPH].mvflags & G_GONE) + && !(svm.mvitals[PM_WATER_NYMPH].mvflags & G_GONE) + && !(svm.mvitals[PM_MOUNTAIN_NYMPH].mvflags & G_GONE) && (mtmp = makemon(mkclass(S_NYMPH, 0), u.ux, u.uy, NO_MINVENT | MM_NOMSG)) != 0) { You("summon %s!", a_monnam(mtmp)); @@ -1270,7 +1290,7 @@ use_bell(struct obj **optr) } else if (invoking) { pline("%s an unsettling shrill sound...", Tobjnam(obj, "issue")); - obj->age = gm.moves; + obj->age = svm.moves; learno = TRUE; wakem = TRUE; @@ -1313,10 +1333,10 @@ use_bell(struct obj **optr) obj->known = 1; } if (wakem) - wake_nearby(); + wake_nearby(TRUE); } -static void +staticfn void use_candelabrum(struct obj *obj) { const char *s = (obj->spe != 1) ? "candles" : "candle"; @@ -1384,11 +1404,11 @@ use_candelabrum(struct obj *obj) begin_burn(obj, FALSE); } -static void +staticfn void use_candle(struct obj **optr) { - register struct obj *obj = *optr; - register struct obj *otmp; + struct obj *obj = *optr; + struct obj *otmp; const char *s = (obj->quan != 1) ? "candles" : "candle"; char qbuf[QBUFSZ], qsfx[QBUFSZ], *q; boolean was_lamplit; @@ -1444,7 +1464,8 @@ use_candle(struct obj **optr) else if (!otmp->lamplit && was_lamplit) pline("%s out.", (obj->quan > 1L) ? "They go" : "It goes"); if (obj->unpaid) { - struct monst *shkp VOICEONLY = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE)); + struct monst *shkp VOICEONLY + = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE)); SetVoice(shkp, 0, 80, 0); verbalize("You %s %s, you bought %s!", @@ -1548,6 +1569,8 @@ splash_lit(struct obj *obj) && ((!is_flyer(mtmp->data) && !is_floater(mtmp->data)) || Is_waterlevel(&u.uz))); snuff = FALSE; + if (useeit) + set_msg_xy(x, y); } if (useeit || uhearit) @@ -1593,15 +1616,19 @@ catch_lit(struct obj *obj) && obj->cursed && !rn2(2)) return FALSE; - if (obj->where == OBJ_INVENT || cansee(x, y)) + if (obj->where == OBJ_INVENT || cansee(x, y)) { + if (obj->where == OBJ_FLOOR && cansee(x, y)) + set_msg_xy(x, y); pline("%s %s %s", Yname2(obj), /* "catches light!" or "feels warm." */ otense(obj, Blind ? "feel" : "catch"), Blind ? "warm." : "light!"); + } if (obj->otyp == POT_OIL) makeknown(obj->otyp); if (carried(obj) && obj->unpaid && costly_spot(u.ux, u.uy)) { - struct monst *shkp VOICEONLY = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE)); + struct monst *shkp VOICEONLY + = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE)); /* if it catches while you have it, then it's your tough luck */ check_unpaid(obj); @@ -1618,7 +1645,7 @@ catch_lit(struct obj *obj) } /* light a lamp or candle */ -static void +staticfn void use_lamp(struct obj *obj) { char buf[BUFSZ]; @@ -1654,7 +1681,7 @@ use_lamp(struct obj *obj) if (!Blind) Your("lantern is out of power."); else - pline("%s", Nothing_seems_to_happen); + pline("%s", nothing_seems_to_happen); } else { pline("This %s has no oil.", xname(obj)); } @@ -1669,7 +1696,7 @@ use_lamp(struct obj *obj) pline("%s for a moment, then %s.", Tobjnam(obj, "flicker"), otense(obj, "die")); } else { - pline("%s", Nothing_seems_to_happen); + pline("%s", nothing_seems_to_happen); } } else { if (lamp) { /* lamp or lantern */ @@ -1681,7 +1708,8 @@ use_lamp(struct obj *obj) if (obj->unpaid && costly_spot(u.ux, u.uy) && obj->age == 20L * (long) objects[obj->otyp].oc_cost) { const char *ithem = (obj->quan > 1L) ? "them" : "it"; - struct monst *shkp VOICEONLY = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE)); + struct monst *shkp VOICEONLY + = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE)); SetVoice(shkp, 0, 80, 0); verbalize("You burn %s, you bought %s!", ithem, ithem); @@ -1692,7 +1720,7 @@ use_lamp(struct obj *obj) } } -static void +staticfn void light_cocktail(struct obj **optr) { struct obj *obj = *optr; /* obj is a potion of oil */ @@ -1710,10 +1738,14 @@ light_cocktail(struct obj **optr) /* * Free & add to re-merge potion. This will average the * age of the potions. Not exactly the best solution, - * but its easy. + * but its easy. Don't do that unless obj is not worn (uwep, + * uswapwep, or uquiver) because if wielded and other oil is + * quivered a "null obj after quiver merge" panic will occur. */ - freeinv(obj); - *optr = addinv(obj); + if (!obj->owornmask) { + freeinv(obj); + *optr = addinv(obj); + } return; } else if (Underwater) { There("is not enough oxygen to sustain a fire."); @@ -1728,7 +1760,8 @@ light_cocktail(struct obj **optr) Blind ? "" : " It gives off a dim light."); if (obj->unpaid && costly_spot(u.ux, u.uy)) { - struct monst *shkp VOICEONLY = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE)); + struct monst *shkp VOICEONLY = shop_keeper(*in_rooms(u.ux, u.uy, + SHOPBASE)); /* Normally, we shouldn't both partially and fully charge * for an item, but (Yendorian Fuel) Taxes are inevitable... @@ -1753,7 +1786,7 @@ light_cocktail(struct obj **optr) } /* Use a silver item to cure withering. */ -static int +staticfn int use_silver_on_withering(struct obj *obj) { uchar new_oeroded2 = obj->oeroded2; @@ -1853,7 +1886,7 @@ use_silver_on_withering(struct obj *obj) /* getobj callback for object to be rubbed - not selecting a secondary object to rub on a gray stone or rub jelly on */ -static int +staticfn int rub_ok_core(struct obj *obj, boolean check_withering) { if (!obj) @@ -1877,7 +1910,7 @@ rub_ok_core(struct obj *obj, boolean check_withering) return GETOBJ_EXCLUDE; } -static int +staticfn int rub_ok(struct obj *obj) { return rub_ok_core(obj, TRUE); @@ -1976,7 +2009,7 @@ enum jump_trajectory { }; /* callback routine for walk_path() */ -static boolean +staticfn boolean check_jump(genericptr arg, coordxy x, coordxy y) { int traj = *(int *) arg; @@ -2007,7 +2040,7 @@ check_jump(genericptr arg, coordxy x, coordxy y) return TRUE; } -static boolean +staticfn boolean is_valid_jump_pos(coordxy x, coordxy y, int magic, boolean showmsg) { if (!magic && !(HJumping & ~INTRINSIC) && !EJumping && distu(x, y) != 5) { @@ -2033,7 +2066,7 @@ is_valid_jump_pos(coordxy x, coordxy y, int magic, boolean showmsg) coord uc, tc; struct rm *lev = &levl[u.ux][u.uy]; /* we want to categorize trajectory for use in determining - passage through doorways: horizonal, vertical, or diagonal; + passage through doorways: horizontal, vertical, or diagonal; since knight's jump and other irregular directions are possible, we flatten those out to simplify door checks */ int diag, traj; @@ -2073,7 +2106,7 @@ is_valid_jump_pos(coordxy x, coordxy y, int magic, boolean showmsg) return TRUE; } -static boolean +staticfn boolean get_valid_jump_position(coordxy x, coordxy y) { return (isok(x, y) @@ -2081,22 +2114,23 @@ get_valid_jump_position(coordxy x, coordxy y) && is_valid_jump_pos(x, y, gj.jumping_is_magic, FALSE)); } -static void -display_jump_positions(int state) +staticfn void +display_jump_positions(boolean on_off) { - if (state == 0) { - tmp_at(DISP_BEAM, cmap_to_glyph(S_goodpos)); - } else if (state == 1) { - coordxy x, y, dx, dy; + coordxy x, y, dx, dy; + if (on_off) { + /* on */ + 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; - if (get_valid_jump_position(x, y)) + x = dx + u.ux; + y = dy + u.uy; + if (get_valid_jump_position(x, y) && !u_at(x, y)) tmp_at(x, y); } } else { + /* off */ tmp_at(DISP_END, 0); } } @@ -2181,21 +2215,25 @@ jump(int magic) /* 0=Physical, otherwise skill level */ return ECMD_CANCEL; /* user pressed ESC */ if (!is_valid_jump_pos(cc.x, cc.y, magic, TRUE)) { return ECMD_FAIL; + } else if (u.usteed && u_at(cc.x, cc.y)) { + pline("%s isn't capable of jumping in place.", YMonnam(u.usteed)); + return ECMD_FAIL; } else { coord uc; + long side; int range, temp; + boolean wastrapped = FALSE; - if (u.utrap) + if (u.utrap) { + wastrapped = TRUE; switch (u.utraptype) { - case TT_BEARTRAP: { - long side = rn2(3) ? LEFT_SIDE : RIGHT_SIDE; - + case TT_BEARTRAP: + side = rn2(3) ? LEFT_SIDE : RIGHT_SIDE; You("rip yourself free of the bear trap! Ouch!"); losehp(Maybe_Half_Phys(rnd(10)), "jumping out of a bear trap", KILLED_BY); set_wounded_legs(side, rn1(1000, 500)); break; - } case TT_PIT: You("leap from the pit!"); break; @@ -2205,8 +2243,8 @@ jump(int magic) /* 0=Physical, otherwise skill level */ break; case TT_LAVA: You("pull yourself above the %s!", hliquid("lava")); - reset_utrap(TRUE); - return ECMD_TIME; + cc.x = u.ux, cc.y = u.uy; /* take u_at() 'if' below */ + break; case TT_BURIEDBALL: case TT_INFLOOR: You("strain your %s, but you're still %s.", @@ -2217,7 +2255,34 @@ jump(int magic) /* 0=Physical, otherwise skill level */ set_wounded_legs(LEFT_SIDE, rn1(10, 11)); set_wounded_legs(RIGHT_SIDE, rn1(10, 11)); return ECMD_TIME; + default: + impossible("Jumping out of strange trap (%d)?", u.utraptype); + break; + } + /* if we reach here, hero is no longer trapped */ + reset_utrap(TRUE); + } + /* jumping on hero's same spot doesn't use walk_path() and isn't + allowed when riding (handled above) */ + if (u_at(cc.x, cc.y)) { + struct trap *t; + + /* escaping from a trap takes precedence over jumping in place */ + if (wastrapped) { + morehungry(rnd(10)); + return ECMD_TIME; + } + /* jumping in place on a trap will trigger it */ + if ((t = t_at(cc.x, cc.y)) != 0) { + You("jump up and %s back down.", !Flying ? "come" : "fly"); + dotrap(t, FORCETRAP | TOOKPLUNGE); + return ECMD_TIME; } + /* jumping in place takes no time and doesn't exercise anything */ + You("%s.", Hallucination ? "hop up and down a bit" + : "decide not to jump after all"); + return ECMD_OK; + } /* * Check the path from uc to cc, calling hurtle_step at each @@ -2259,7 +2324,7 @@ tinnable(struct obj *corpse) return 1; } -static void +staticfn void use_tinning_kit(struct obj *obj) { struct obj *corpse, *can; @@ -2382,7 +2447,7 @@ use_unicorn_horn(struct obj **optr, boolean passive) break; case 6: if (Deaf) /* make_deaf() won't give feedback when already deaf */ - pline("%s", Nothing_seems_to_happen); + pline("%s", nothing_seems_to_happen); make_deaf((HDeaf & TIMEOUT) + lcount, TRUE); break; } @@ -2469,11 +2534,10 @@ use_unicorn_horn(struct obj **optr, boolean passive) } if (did_prop) - gc.context.botl = TRUE; + disp.botl = TRUE; else if (!passive) - pline("%s", Nothing_seems_to_happen); + pline("%s", nothing_seems_to_happen); - gc.context.botl = did_prop; #undef PROP_COUNT #undef prop_trouble #undef TimedTrouble @@ -2497,7 +2561,7 @@ fig_transform(anything *arg, long timeout) impossible("null figurine in fig_transform()"); return; } - silent = (timeout != gm.moves); /* happened while away */ + silent = (timeout != svm.moves); /* happened while away */ okay_spot = get_obj_location(figurine, &cc.x, &cc.y, 0); if (figurine->where == OBJ_INVENT || figurine->where == OBJ_MINVENT) okay_spot = enexto(&cc, cc.x, cc.y, &mons[figurine->corpsenm]); @@ -2512,7 +2576,7 @@ fig_transform(anything *arg, long timeout) mtmp = make_familiar(figurine, cc.x, cc.y, TRUE); if (mtmp) { char and_vanish[BUFSZ]; - struct obj *mshelter = gl.level.objects[mtmp->mx][mtmp->my]; + struct obj *mshelter = svl.level.objects[mtmp->mx][mtmp->my]; /* [m_monnam() yields accurate mon type, overriding hallucination] */ Sprintf(monnambuf, "%s", an(m_monnam(mtmp))); @@ -2548,6 +2612,7 @@ fig_transform(anything *arg, long timeout) case OBJ_FLOOR: if (cansee_spot && !silent) { + set_msg_xy(cc.x, cc.y); if (suppress_see) pline("%s suddenly vanishes!", an(xname(figurine))); else @@ -2597,7 +2662,7 @@ fig_transform(anything *arg, long timeout) newsym(cc.x, cc.y); } -static boolean +staticfn boolean figurine_location_checks(struct obj *obj, coord *cc, boolean quietly) { coordxy x, y; @@ -2614,7 +2679,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!", @@ -2630,10 +2695,10 @@ figurine_location_checks(struct obj *obj, coord *cc, boolean quietly) return TRUE; } -static int +staticfn int use_figurine(struct obj **optr) { - register struct obj *obj = *optr; + struct obj *obj = *optr; coordxy x, y; coord cc; @@ -2643,7 +2708,7 @@ use_figurine(struct obj **optr) return ECMD_OK; } if (!getdir((char *) 0)) { - gc.context.move = gm.multi = 0; + svc.context.move = gm.multi = 0; return ECMD_CANCEL; } x = u.ux + u.dx; @@ -2671,7 +2736,7 @@ use_figurine(struct obj **optr) } /* getobj callback for object to apply grease to */ -static int +staticfn int grease_ok(struct obj *obj) { if (!obj) @@ -2688,7 +2753,7 @@ grease_ok(struct obj *obj) return GETOBJ_SUGGEST; } -static int +staticfn int use_grease(struct obj *obj) { struct obj *otmp; @@ -2719,7 +2784,7 @@ use_grease(struct obj *obj) consume_obj_charge(obj, TRUE); oldglib = (int) (Glib & TIMEOUT); - if (otmp != &cg.zeroobj) { + if (otmp != &hands_obj) { You("cover %s with a thick layer of grease.", yname(otmp)); otmp->greased = 1; if (obj->cursed && !nohands(gy.youmonst.data)) { @@ -2790,7 +2855,7 @@ thiefstone_ok(struct obj *obj) } /* getobj callback for object to rub on a known touchstone */ -static int +staticfn int touchstone_ok(struct obj *obj) { if (!obj) @@ -2813,7 +2878,7 @@ touchstone_ok(struct obj *obj) /* touchstones - by Ken Arnold * also thiefstones */ -static int +staticfn int use_stone(struct obj *tstone) { static const char scritch[] = "\"scritch, scritch\""; @@ -3029,7 +3094,7 @@ reset_trapset(void) } /* Place a landmine/bear trap. Helge Hafting */ -static void +staticfn void use_trap(struct obj *otmp) { int ttyp, tmp; @@ -3055,7 +3120,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)) @@ -3118,12 +3183,13 @@ use_trap(struct obj *otmp) } } You("begin setting %s%s.", shk_your(buf, otmp), trapname(ttyp, FALSE)); + use_unpaid_trapobj(otmp, u.ux, u.uy); set_occupation(set_trap, occutext, 0); return; } /* occupation routine called each turn while arming a beartrap or landmine */ -static int +staticfn int set_trap(void) { struct obj *otmp = gt.trapinfo.tobj; @@ -3263,8 +3329,11 @@ use_whip(struct obj *obj) /* Have a shot at snaring something on the floor. A flyer can reach the floor so could just pick an item up, but allow snagging by whip too. */ - otmp = gl.level.objects[u.ux][u.uy]; - if (otmp && otmp->otyp == CORPSE && otmp->corpsenm == PM_HORSE) { + otmp = svl.level.objects[u.ux][u.uy]; + if (otmp && otmp->otyp == CORPSE + && (otmp->corpsenm == PM_HORSE + || otmp->corpsenm == little_to_big(PM_HORSE) /* warhorse */ + || otmp->corpsenm == big_to_little(PM_HORSE))) { /* pony */ pline("Why beat a dead horse?"); return ECMD_TIME; } @@ -3417,7 +3486,7 @@ use_whip(struct obj *obj) hitvalu = 8 + otmp->spe; hitu = thitu(hitvalu, dmgval(otmp, &gy.youmonst), - &otmp, (char *)0); + &otmp, (char *) 0); if (hitu) { pline_The("%s hits you as you try to snatch it!", the(onambuf)); @@ -3481,7 +3550,7 @@ use_whip(struct obj *obj) } /* regardless of mtmp's weapon or hero's proficiency */ wakeup(mtmp, TRUE, TRUE); - } else if (IS_ROCK(levl[rx][ry].typ)) { + } else if (IS_OBSTRUCTED(levl[rx][ry].typ)) { pline("Your bullwhip slaps against the %s.", explain_terrain(rx, ry)); } else { snapped_in_air = TRUE; @@ -3492,7 +3561,7 @@ use_whip(struct obj *obj) You("crack the whip!"); else pline("CRACK!"); - wake_nearby(); + wake_nearby(FALSE); for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (!DEADMONSTER(mtmp) && is_animal(mtmp->data) && distu(mtmp->mx, mtmp->my) <= 8 @@ -3519,7 +3588,7 @@ static const char (glyph_is_monster(G) || glyph_is_invisible(G) || glyph_is_statue(G)) /* find pos of monster in range, if only one monster */ -static boolean +staticfn boolean find_poleable_mon(coord *pos, int min_range, int max_range) { struct monst *mtmp; @@ -3557,7 +3626,7 @@ find_poleable_mon(coord *pos, int min_range, int max_range) return TRUE; } -static boolean +staticfn boolean get_valid_polearm_position(coordxy x, coordxy y) { int glyph; @@ -3570,16 +3639,16 @@ get_valid_polearm_position(coordxy x, coordxy y) && glyph_is_poleable(glyph)))); } -static void -display_polearm_positions(int state) +staticfn void +display_polearm_positions(boolean on_off) { - if (state == 0) { - tmp_at(DISP_BEAM, cmap_to_glyph(S_goodpos)); - } else if (state == 1) { - coordxy x, y, dx, dy; + coordxy x, y, dx, dy; - for (dx = -4; dx <= 4; dx++) - for (dy = -4; dy <= 4; dy++) { + if (on_off) { + /* on */ + tmp_at(DISP_BEAM, cmap_to_glyph(S_goodpos)); + for (dx = -3; dx <= 3; dx++) + for (dy = -3; dy <= 3; dy++) { x = dx + (int) u.ux; y = dy + (int) u.uy; if (get_valid_polearm_position(x, y)) { @@ -3587,19 +3656,79 @@ display_polearm_positions(int state) } } } else { + /* off */ tmp_at(DISP_END, 0); } } +/* + * 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; +} + /* 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 = gc.context.polearm.hitmon; + struct monst *hitm = svc.context.polearm.hitmon; /* Are you allowed to use the pole? */ if (u.uswallow) { @@ -3616,32 +3745,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) @@ -3680,19 +3784,19 @@ use_pole(struct obj *obj, boolean autohit) return ECMD_FAIL; } - gc.context.polearm.hitmon = (struct monst *) 0; + svc.context.polearm.hitmon = (struct monst *) 0; /* Attack the monster there */ gb.bhitpos = cc; if ((mtmp = m_at(gb.bhitpos.x, gb.bhitpos.y)) != (struct monst *) 0) { if (attack_checks(mtmp, uwep)) /* can attack proceed? */ /* no, abort the attack attempt; result depends on res: 1 => polearm became wielded, 0 => already wielded; - gc.context.move: 1 => discovered hidden monster at target spot, + svc.context.move: 1 => discovered hidden monster at target spot, 0 => answered 'n' to "Really attack?" prompt */ - return res | (gc.context.move ? ECMD_TIME : ECMD_OK); + return res | (svc.context.move ? ECMD_TIME : ECMD_OK); if (overexertion()) return ECMD_TIME; /* burn nutrition; maybe pass out */ - gc.context.polearm.hitmon = mtmp; + svc.context.polearm.hitmon = mtmp; check_caitiff(mtmp); gn.notonhead = (gb.bhitpos.x != mtmp->mx || gb.bhitpos.y != mtmp->my); (void) thitmonst(mtmp, uwep); @@ -3741,7 +3845,7 @@ use_pole(struct obj *obj, boolean autohit) #undef glyph_is_poleable -static int +staticfn int use_cream_pie(struct obj *obj) { boolean wasblind = Blind; @@ -3780,7 +3884,7 @@ use_cream_pie(struct obj *obj) } /* getobj callback for object to rub royal jelly on */ -static int +staticfn int jelly_ok(struct obj *obj) { if (obj && obj->otyp == EGG) @@ -3789,15 +3893,15 @@ jelly_ok(struct obj *obj) return GETOBJ_EXCLUDE; } -static int +staticfn int use_royal_jelly(struct obj **optr) { int oldcorpsenm; unsigned was_timed; - struct obj *obj = *optr; - struct obj *eobj; + struct obj *eobj, *obj = *optr; + boolean splitit = (obj->quan > 1L); - if (obj->quan > 1L) + if (splitit) obj = splitobj(obj, 1L); /* remove from inventory so that it won't be offered as a choice to rub on itself */ @@ -3806,8 +3910,13 @@ use_royal_jelly(struct obj **optr) /* right now you can rub one royal jelly on an entire stack of eggs */ eobj = getobj("rub the royal jelly on", jelly_ok, GETOBJ_PROMPT); if (!eobj) { - addinv(obj); /* put the unused lump back; if it came from - * a split, it should merge back */ + if (splitit) { + (void) unsplitobj(obj); + update_inventory(); /* freeinv() updated perminv w/ obj omitted */ + } else { + /* this lump was already separate; pervent merge */ + addinv_nomerge(obj); /* put unused lump back; updates perminv */ + } return ECMD_CANCEL; } @@ -3825,7 +3934,7 @@ use_royal_jelly(struct obj **optr) if (eobj->timed || eobj->corpsenm != oldcorpsenm) pline("The %s %s feebly.", xname(eobj), otense(eobj, "quiver")); else - pline("%s", Nothing_seems_to_happen); + pline("%s", nothing_seems_to_happen); kill_egg(eobj); goto useup_jelly; } @@ -3844,7 +3953,7 @@ use_royal_jelly(struct obj **optr) || eobj->corpsenm != oldcorpsenm) pline("The %s %s briefly.", xname(eobj), otense(eobj, "quiver")); else - pline("%s", Nothing_seems_to_happen); + pline("%s", nothing_seems_to_happen); useup_jelly: /* not useup() because we've already done freeinv() */ @@ -3854,7 +3963,7 @@ use_royal_jelly(struct obj **optr) return ECMD_TIME; } -static int +staticfn int grapple_range(void) { int typ = uwep_skill_type(); @@ -3869,13 +3978,35 @@ grapple_range(void) return max_range; } -static boolean +staticfn boolean can_grapple_location(coordxy x, coordxy y) { return (isok(x, y) && cansee(x, y) && distu(x, y) <= grapple_range()); } -static int +staticfn void +display_grapple_positions(boolean on_off) +{ + coordxy x, y, dx, dy; + + if (on_off) { + /* on */ + tmp_at(DISP_BEAM, cmap_to_glyph(S_goodpos)); + for (dx = -3; dx <= 3; dx++) + for (dy = -3; dy <= 3; dy++) { + x = dx + (int) u.ux; + y = dy + (int) u.uy; + if (can_grapple_location(x, y) && !u_at(x, y)) { + tmp_at(x, y); + } + } + } else { + /* off */ + tmp_at(DISP_END, 0); + } +} + +staticfn int use_grapple(struct obj *obj) { int res = ECMD_OK, typ, tohit; @@ -3890,6 +4021,7 @@ use_grapple(struct obj *obj) return ECMD_OK; } if (obj != uwep) { + /* "cast": grappling hook evolved from slash'em's fishing pole */ if (wield_tool(obj, "cast")) { cmdq_add_ec(CQ_CANNED, doapply); cmdq_add_key(CQ_CANNED, obj->invlet); @@ -3903,7 +4035,7 @@ use_grapple(struct obj *obj) pline(where_to_hit); cc.x = u.ux; cc.y = u.uy; - getpos_sethilite(NULL, can_grapple_location); + getpos_sethilite(display_grapple_positions, can_grapple_location); if (getpos(&cc, TRUE, "the spot to hit") < 0) /* ESC; uses turn iff grapnel became wielded */ return (res | ECMD_CANCEL); @@ -3928,10 +4060,10 @@ use_grapple(struct obj *obj) anything any; char buf[BUFSZ]; menu_item *selected; - int clr = 0; + int clr = NO_COLOR; any = cg.zeroany; /* set all bits to zero */ - any.a_int = 1; /* use index+1 (cant use 0) as identifier */ + any.a_int = 1; /* use index+1 (can't use 0) as identifier */ start_menu(tmpwin, MENU_BEHAVE_STANDARD); any.a_int++; Sprintf(buf, "an object on the %s", surface(cc.x, cc.y)); @@ -3964,7 +4096,7 @@ use_grapple(struct obj *obj) /* FIXME -- untrap needs to deal with non-adjacent traps */ break; case 1: /* Object */ - if ((otmp = gl.level.objects[cc.x][cc.y]) != 0) { + if ((otmp = svl.level.objects[cc.x][cc.y]) != 0) { You("snag an object from the %s!", surface(cc.x, cc.y)); (void) pickup_object(otmp, 1L, FALSE); /* If pickup fails, leave it alone */ @@ -3997,6 +4129,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)) @@ -4020,20 +4153,51 @@ use_grapple(struct obj *obj) return ECMD_TIME; } +staticfn void +discard_broken_wand(void) +{ + struct obj *obj; + + obj = gc.current_wand; /* [see dozap() and destroy_items()] */ + gc.current_wand = 0; + if (obj) + delobj(obj); + nomul(0); +} + +staticfn void +broken_wand_explode(struct obj *obj, int dmg, int expltype) +{ + explode(u.ux, u.uy, -(obj->otyp), dmg, WAND_CLASS, expltype); + makeknown(obj->otyp); /* explode describes the effect */ + discard_broken_wand(); +} + +/* if x,y has lava or water, dunk any boulders at that location into it */ +staticfn 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 */ -static int +staticfn int do_break_wand(struct obj *obj) { #define BY_OBJECT ((struct monst *) 0) static const char nothing_else_happens[] = "But nothing else happens..."; - register int i; + int i; coordxy x, y; - register struct monst *mon; + struct monst *mon; int dmg, damage; boolean affects_objects; boolean shop_damage = FALSE; boolean fillmsg = FALSE; - int expltype = EXPL_MAGICAL; char confirm[QBUFSZ], buf[BUFSZ]; boolean is_fragile = (objdescr_is(obj, "balsa") || objdescr_is(obj, "glass")); @@ -4071,21 +4235,23 @@ do_break_wand(struct obj *obj) } gc.current_wand = obj; /* destroy_items might reset this */ - freeinv(obj); /* hide it from destroy_items instead... */ - setnotworn(obj); /* so we need to do this ourselves */ + freeinv(obj); /* hide it from destroy_items instead... */ + setnotworn(obj); /* so we need to do this ourselves */ /* If you know the wand you're breaking is a wand of nothing, * it should say something different. * OK to skip other wand breaking code since even wresting won't have * any effect. */ if(obj->otyp == WAN_NOTHING && objects[WAN_NOTHING].oc_name_known) { - pline("Predictably, nothing happens."); - goto discard_broken_wand; + pline("Predictably, nothing happens."); + discard_broken_wand(); + return ECMD_TIME; } if (!zappable(obj)) { pline(nothing_else_happens); - goto discard_broken_wand; + discard_broken_wand(); + return ECMD_TIME; } /* successful call to zappable() consumes a charge; put it back */ obj->spe++; @@ -4108,8 +4274,10 @@ do_break_wand(struct obj *obj) release_hold(); if (obj->dknown) makeknown(WAN_OPENING); - goto discard_broken_wand; + discard_broken_wand(); + return ECMD_TIME; } + FALLTHROUGH; /*FALLTHRU*/ case WAN_WISHING: case WAN_NOTHING: @@ -4118,29 +4286,27 @@ do_break_wand(struct obj *obj) case WAN_ENLIGHTENMENT: case WAN_SECRET_DOOR_DETECTION: pline(nothing_else_happens); - goto discard_broken_wand; + discard_broken_wand(); + return ECMD_TIME; case WAN_DEATH: case WAN_LIGHTNING: - dmg *= 4; - goto wanexpl; + broken_wand_explode(obj, dmg * 4, EXPL_MAGICAL); + return ECMD_TIME; case WAN_FIRE: - expltype = EXPL_FIERY; - /*FALLTHRU*/ + broken_wand_explode(obj, dmg * 2, EXPL_FIERY); + return ECMD_TIME; case WAN_COLD: - if (expltype == EXPL_MAGICAL) - expltype = EXPL_FROSTY; - dmg *= 2; - /*FALLTHRU*/ + broken_wand_explode(obj, dmg * 2, EXPL_FROSTY); + return ECMD_TIME; case WAN_MAGIC_MISSILE: - wanexpl: - explode(u.ux, u.uy, -(obj->otyp), dmg, WAND_CLASS, expltype); - makeknown(obj->otyp); /* explode describes the effect */ - goto discard_broken_wand; + broken_wand_explode(obj, dmg, EXPL_MAGICAL); + return ECMD_TIME; case WAN_STRIKING: /* we want this before the explosion instead of at the very end */ 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: @@ -4172,8 +4338,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, FALSE, x, y)) { + 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 */ @@ -4201,6 +4368,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 */ @@ -4218,11 +4388,11 @@ do_break_wand(struct obj *obj) */ if ((mon = m_at(x, y)) != 0) { (void) bhitm(mon, obj); - /* if (gc.context.botl) bot(); */ + /* if (disp.botl) bot(); */ } - if (affects_objects && gl.level.objects[x][y]) { + if (affects_objects && svl.level.objects[x][y]) { (void) bhitpile(obj, bhito, x, y, 0); - if (gc.context.botl) + if (disp.botl) bot(); /* potion effects */ } } else { @@ -4238,9 +4408,9 @@ do_break_wand(struct obj *obj) * of obj->bypass in the zap code to accomplish that last case * since it's also used by retouch_equipment() for polyself.) */ - if (affects_objects && gl.level.objects[x][y]) { + if (affects_objects && svl.level.objects[x][y]) { (void) bhitpile(obj, bhito, x, y, 0); - if (gc.context.botl) + if (disp.botl) bot(); /* potion effects */ } damage = zapyourself(obj, FALSE); @@ -4248,7 +4418,7 @@ do_break_wand(struct obj *obj) Sprintf(buf, "killed %sself by breaking a wand", uhim()); losehp(Maybe_Half_Phys(damage), buf, NO_KILLER_PREFIX); } - if (gc.context.botl) + if (disp.botl) bot(); /* blindness */ } } @@ -4264,19 +4434,14 @@ do_break_wand(struct obj *obj) if (obj->otyp == WAN_LIGHT) litroom(TRUE, obj); /* only needs to be done once */ - discard_broken_wand: - obj = gc.current_wand; /* [see dozap() and destroy_item()] */ - gc.current_wand = 0; - if (obj) - delobj(obj); - nomul(0); + discard_broken_wand(); return ECMD_TIME; #undef BY_OBJECT } /* getobj callback for object to apply - this is more complex than most other * callbacks because there are a lot of appliables */ -static int +staticfn int apply_ok(struct obj *obj) { if (!obj) @@ -4288,6 +4453,11 @@ apply_ok(struct obj *obj) || obj->oclass == SPBOOK_CLASS) return GETOBJ_SUGGEST; + /* applying coins to flip them is a minor easter egg, so do not suggest + coin application to the player */ + if (obj->oclass == COIN_CLASS) + return GETOBJ_DOWNPLAY; + /* certain weapons */ if (obj->oclass == WEAPON_CLASS && (is_pick(obj) || is_axe(obj) || is_pole(obj) @@ -4314,6 +4484,9 @@ apply_ok(struct obj *obj) if (obj->otyp == ROCK) return GETOBJ_DOWNPLAY; + if (obj->otyp == BANANA && Hallucination) + return GETOBJ_DOWNPLAY; + if (is_graystone(obj)) { /* The only case where we don't suggest a gray stone is if we KNOW it * isn't a touchstone or thiefstone. */ @@ -4341,7 +4514,7 @@ int doapply(void) { struct obj *obj; - register int res = ECMD_TIME; + int res = ECMD_TIME; if (nohands(gy.youmonst.data)) { You("aren't able to use or apply tools in your current form."); @@ -4379,6 +4552,9 @@ doapply(void) if (obj->oclass == SPBOOK_CLASS) return flip_through_book(obj); + if (obj->oclass == COIN_CLASS) + return flip_coin(obj); + switch (obj->otyp) { case BLINDFOLD: case LENSES: @@ -4537,6 +4713,13 @@ doapply(void) case ROCK: res = use_stone(obj); break; + case BANANA: + if (Hallucination) { + pline("It rings! ... But no-one answers."); + break; + } + FALLTHROUGH; + /*FALLTHRU*/ default: /* Pole-weapons can strike at a distance */ if (is_pole(obj)) { @@ -4602,7 +4785,7 @@ unfixable_trouble_count(boolean is_horn) return unfixable_trbl; } -static int +staticfn int flip_through_book(struct obj *obj) { if (Underwater) { @@ -4656,4 +4839,36 @@ flip_through_book(struct obj *obj) return ECMD_TIME; } +staticfn int +flip_coin(struct obj *obj) +{ + struct obj *otmp = obj; + boolean lose_coin = FALSE; + + You("flip %s.", an(singular(obj, xname))); + if (Underwater) { + pline("It tumbles away."); + lose_coin = TRUE; + } else if (Glib || Fumbling + || (ACURR(A_DEX) < 10 && !rn2(ACURR(A_DEX)))) { + pline("It slips between your %s.", fingers_or_gloves(FALSE)); + lose_coin = TRUE; + } + + if (lose_coin) { + if (otmp->quan > 1L) + otmp = splitobj(otmp, 1L); + dropx(otmp); + return ECMD_TIME; + } + if (Hallucination) { + pline(rn2(100) ? "Wow, a double header!" + /* edge case */ + : "The coin miraculously lands on its edge!"); + } else { + pline("It comes up %s.", rn2(2) ? "heads" : "tails"); + } + return ECMD_TIME; +} + /*apply.c*/ diff --git a/src/artifact.c b/src/artifact.c index ca8d2bc9f4..ffd46ade22 100644 --- a/src/artifact.c +++ b/src/artifact.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 artifact.c $NHDT-Date: 1654717838 2022/06/08 19:50:38 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.190 $ */ +/* NetHack 3.7 artifact.c $NHDT-Date: 1715889721 2024/05/16 20:02:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.236 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -14,20 +14,27 @@ * the contents, just the total size. */ -#define get_artifact(o) \ - (((o) && (o)->oartifact) ? &artilist[(int) (o)->oartifact] : 0) - -static boolean bane_applies(const struct artifact *, struct monst *); -static int spec_applies(const struct artifact *, struct monst *); -static int invoke_ok(struct obj *); -static int arti_invoke(struct obj *); -static boolean Mb_hit(struct monst * magr, struct monst *mdef, +staticfn struct artifact *get_artifact(struct obj *) NONNULL; + +/* #define get_artifact(o) \ + (((o) && ((o)->artifact > 0 && (o)->artifact < AFTER_LAST_ARTIFACT)) \ + ? &artilist[(int) (o)->oartifact] \ + : &artilist[ART_NONARTIFACT]) */ + +staticfn boolean bane_applies(const struct artifact *, struct monst *) + NONNULLARG12; +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 arti_invoke(struct obj *); +staticfn boolean Mb_hit(struct monst * magr, struct monst *mdef, struct obj *, int *, int, boolean, char *); -static unsigned long abil_to_spfx(long *); -static uchar abil_to_adtyp(long *); -static int glow_strength(int); -static boolean untouchable(struct obj *, boolean); -static int count_surround_traps(coordxy, coordxy); +staticfn unsigned long abil_to_spfx(long *) NONNULLARG1; +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); /* The amount added to the victim's total hit points to insure that the victim will be killed even after damage bonus/penalty adjustments. @@ -65,11 +72,10 @@ static xint16 artidisco[NROFARTIFACTS]; */ static const struct arti_info zero_artiexist = {0}; /* all bits zero */ -static void hack_artifacts(void); -static boolean attacks(int, struct obj *); +staticfn void hack_artifacts(void); /* handle some special cases; must be called after u_init() */ -static void +staticfn void hack_artifacts(void) { struct artifact *art; @@ -136,13 +142,17 @@ 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 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; @@ -150,6 +160,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 */ @@ -159,6 +170,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 @@ -180,17 +193,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 @@ -209,8 +238,19 @@ mk_artifact( a = &artilist[m]; /* make an appropriate object if necessary, then christen it */ - if (by_align) - otmp = mksobj((int) a->otyp, TRUE, FALSE); + otmp = mksobj((int) a->otyp, TRUE, FALSE); + + if (adjust_spe) { + int new_spe; + + /* Adjust otmp->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; + } if (otmp) { /* prevent erosion from generating */ @@ -244,8 +284,8 @@ artifact_name( short *otyp_p, /* secondary output */ boolean fuzzy) /* whether to allow extra or omitted spaces or dashes */ { - register const struct artifact *a; - register const char *aname; + const struct artifact *a; + const char *aname; if (!strncmpi(name, "the ", 4)) name += 4; @@ -268,7 +308,7 @@ artifact_name( boolean exist_artifact(int otyp, const char *name) { - register const struct artifact *a; + const struct artifact *a; struct arti_info *arex; if (otyp && *name) @@ -287,7 +327,7 @@ artifact_exists( boolean mod, /* True: exists, False: being un-created */ unsigned flgs) /* ONAME_xyz flags; not relevant if !mod */ { - register const struct artifact *a; + const struct artifact *a; if (otmp && *name) for (a = artilist + 1; a->otyp; a++) @@ -430,7 +470,8 @@ spec_ability(struct obj *otmp, unsigned long abil) { const struct artifact *arti = get_artifact(otmp); - return (boolean) (arti && (arti->spfx & abil) != 0L); + return (boolean) (arti != &artilist[ART_NONARTIFACT] + && (arti->spfx & abil) != 0L); } /* used so that callers don't need to known about SPFX_ codes */ @@ -450,7 +491,7 @@ arti_reflects(struct obj *obj) { const struct artifact *arti = get_artifact(obj); - if (arti) { + if (arti != &artilist[ART_NONARTIFACT]) { /* while being worn */ if ((obj->owornmask & ~W_ART) && (arti->spfx & SPFX_REFLECT)) return TRUE; @@ -473,7 +514,8 @@ shade_glare(struct obj *obj) return TRUE; /* non-silver artifacts with bonus against undead also are effective */ arti = get_artifact(obj); - if (arti && (arti->spfx & SPFX_DFLAG2) && arti->mtype == M2_UNDEAD) + if (arti != &artilist[ART_NONARTIFACT] && (arti->spfx & SPFX_DFLAG2) + && arti->mtype == M2_UNDEAD) return TRUE; /* [if there was anything with special bonus against noncorporeals, it would be effective too] */ @@ -485,7 +527,7 @@ shade_glare(struct obj *obj) boolean restrict_name(struct obj *otmp, const char *name) { - register const struct artifact *a; + const struct artifact *a; const char *aname, *odesc, *other; boolean sametype[NUM_OBJECTS]; int i, lo, hi, otyp = otmp->otyp, ocls = objects[otyp].oc_class; @@ -505,7 +547,7 @@ restrict_name(struct obj *otmp, const char *name) if (!objects[otyp].oc_name_known && (odesc = OBJ_DESCR(objects[otyp])) != 0) { obj_shuffle_range(otyp, &lo, &hi); - for (i = gb.bases[ocls]; i < NUM_OBJECTS; i++) { + for (i = svb.bases[ocls]; i < NUM_OBJECTS; i++) { if (objects[i].oc_class != ocls) break; if (!objects[i].oc_name_known @@ -533,12 +575,12 @@ restrict_name(struct obj *otmp, const char *name) return FALSE; } -static boolean +boolean attacks(int adtyp, struct obj *otmp) { const struct artifact *weap; - if ((weap = get_artifact(otmp)) != 0) + if ((weap = get_artifact(otmp)) != &artilist[ART_NONARTIFACT]) return (boolean) (weap->attk.adtyp == adtyp); return FALSE; } @@ -546,11 +588,11 @@ attacks(int adtyp, struct obj *otmp) boolean defends(int adtyp, struct obj *otmp) { - struct artifact *weap; + const struct artifact *weap; if (!otmp) return FALSE; - if ((weap = get_artifact(otmp)) != 0) + if ((weap = get_artifact(otmp)) != &artilist[ART_NONARTIFACT]) return (boolean) (weap->defn.adtyp == adtyp); if (Is_dragon_armor(otmp)) { /* convert mail to scales to simplify testing */ @@ -596,7 +638,7 @@ defends_when_carried(int adtyp, struct obj *otmp) { const struct artifact *weap; - if ((weap = get_artifact(otmp)) != 0) + if ((weap = get_artifact(otmp)) != &artilist[ART_NONARTIFACT]) return (boolean) (weap->cary.adtyp == adtyp); return FALSE; } @@ -610,7 +652,7 @@ protects(struct obj *otmp, boolean being_worn) if (being_worn && objects[otmp->otyp].oc_oprop == PROTECTION) return TRUE; arti = get_artifact(otmp); - if (!arti) + if (arti == &artilist[ART_NONARTIFACT]) return FALSE; return (boolean) ((arti->cspfx & SPFX_PROTECT) != 0 || (being_worn && (arti->spfx & SPFX_PROTECT) != 0)); @@ -621,15 +663,18 @@ 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; - register const struct artifact *art, *oart = get_artifact(otmp); - register struct obj *obj; - register uchar dtyp; - register long spfx; + const struct artifact *art, *oart = get_artifact(otmp); + struct obj *obj; + uchar dtyp; + long spfx; - if (!oart) + if (oart == &artilist[ART_NONARTIFACT]) return; /* effects from the defn field */ @@ -656,7 +701,8 @@ set_artifact_intrinsic(struct obj *otmp, boolean on, long wp_mask) for (obj = gi.invent; obj; obj = obj->nobj) { if (obj != otmp && obj->oartifact) { art = get_artifact(obj); - if (art && art->cary.adtyp == dtyp) { + if (art != &artilist[ART_NONARTIFACT] + && art->cary.adtyp == dtyp) { mask = (long *) 0; break; } @@ -677,7 +723,7 @@ set_artifact_intrinsic(struct obj *otmp, boolean on, long wp_mask) for (obj = gi.invent; obj; obj = obj->nobj) if (obj != otmp && obj->oartifact) { art = get_artifact(obj); - if (art) + if (art != &artilist[ART_NONARTIFACT]) spfx &= ~art->cspfx; } } @@ -700,7 +746,7 @@ set_artifact_intrinsic(struct obj *otmp, boolean on, long wp_mask) pline_The("world no longer makes any sense to you!"); } (void) make_hallucinated((long) !on, - gp.program_state.restoring ? FALSE : TRUE, + program_state.restoring ? FALSE : TRUE, wp_mask); } if (spfx & SPFX_ESP) { @@ -708,6 +754,7 @@ set_artifact_intrinsic(struct obj *otmp, boolean on, long wp_mask) ETelepat |= wp_mask; else ETelepat &= ~wp_mask; + recalc_telepat_range(); see_monsters(); } if (spfx & SPFX_STLTH) { @@ -742,18 +789,18 @@ set_artifact_intrinsic(struct obj *otmp, boolean on, long wp_mask) if (on) { EWarn_of_mon |= wp_mask; if (is_mlet) { - gc.context.warntype.obj_mlet = type; + svc.context.warntype.obj_mlet = type; } else { - gc.context.warntype.obj |= type; + svc.context.warntype.obj |= type; } } else { EWarn_of_mon &= ~wp_mask; if (is_mlet) { - gc.context.warntype.obj_mlet = 0; + svc.context.warntype.obj_mlet = 0; } else { - gc.context.warntype.obj &= ~type; + svc.context.warntype.obj &= ~type; } } see_monsters(); @@ -809,6 +856,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 @@ -826,11 +880,11 @@ static boolean touch_blasted; /* for retouch_object() */ int touch_artifact(struct obj *obj, struct monst *mon) { - register const struct artifact *oart = get_artifact(obj); + const struct artifact *oart = get_artifact(obj); boolean badclass, badalign, self_willed, yours; touch_blasted = FALSE; - if (!oart) + if (oart == &artilist[ART_NONARTIFACT]) return 1; yours = (mon == &gy.youmonst); @@ -899,9 +953,9 @@ touch_artifact(struct obj *obj, struct monst *mon) boolean arti_immune(struct obj *obj, int dtyp) { - register const struct artifact *weap = get_artifact(obj); + const struct artifact *weap = get_artifact(obj); - if (!weap) + if (weap == &artilist[ART_NONARTIFACT]) return FALSE; if (dtyp == AD_PHYS) return FALSE; /* nothing is immune to phys dmg */ @@ -910,12 +964,13 @@ arti_immune(struct obj *obj, int dtyp) || weap->cary.adtyp == dtyp); } -static boolean +staticfn boolean bane_applies(const struct artifact *oart, struct monst *mon) { struct artifact atmp; - if (oart && (oart->spfx & SPFX_DBONUS) != 0) { + if (oart != &artilist[ART_NONARTIFACT] + && (oart->spfx & SPFX_DBONUS) != 0) { atmp = *oart; atmp.spfx &= SPFX_DBONUS; /* clear other spfx fields */ if (spec_applies(&atmp, mon)) @@ -925,7 +980,7 @@ bane_applies(const struct artifact *oart, struct monst *mon) } /* decide whether an artifact's special attacks apply against mtmp */ -static int +staticfn int spec_applies(const struct artifact *weap, struct monst *mtmp) { struct permonst *ptr; @@ -947,7 +1002,7 @@ spec_applies(const struct artifact *weap, struct monst *mtmp) return ((ptr->mflags2 & weap->mtype) || (yours && ((!Upolyd && (gu.urace.selfmask & weap->mtype)) - || ((weap->mtype & M2_WERE) && u.ulycn >= LOW_PM)))); + || ((weap->mtype & M2_WERE) && ismnum(u.ulycn))))); } else if (weap->spfx & SPFX_DALIGN) { return yours ? (u.ualign.type != weap->alignment) : (ptr->maligntyp == A_NONE @@ -986,7 +1041,7 @@ spec_m2(struct obj *otmp) { const struct artifact *artifact = get_artifact(otmp); - if (artifact) + if (artifact != &artilist[ART_NONARTIFACT]) return artifact->mtype; return 0L; } @@ -1000,7 +1055,8 @@ spec_abon(struct obj *otmp, struct monst *mon) /* no need for an extra check for `NO_ATTK' because this will always return 0 for any artifact which has that attribute */ - if (weap && weap->attk.damn && spec_applies(weap, mon)) + if (weap != &artilist[ART_NONARTIFACT] + && weap->attk.damn && spec_applies(weap, mon)) return rnd((int) weap->attk.damn); return 0; } @@ -1009,9 +1065,10 @@ spec_abon(struct obj *otmp, struct monst *mon) int spec_dbon(struct obj *otmp, struct monst *mon, int tmp) { - register const struct artifact *weap = get_artifact(otmp); + const struct artifact *weap = get_artifact(otmp); - if (!weap || (weap->attk.adtyp == AD_PHYS /* check for `NO_ATTK' */ + if ((weap == &artilist[ART_NONARTIFACT]) + || (weap->attk.adtyp == AD_PHYS /* check for `NO_ATTK' */ && weap->attk.damn == 0 && weap->attk.damd == 0)) gs.spec_dbon_applies = FALSE; else if (is_art(otmp, ART_GRIMTOOTH)) @@ -1062,7 +1119,8 @@ undiscovered_artifact(xint16 m) /* display a list of discovered artifacts; return their count */ int -disp_artifact_discoveries(winid tmpwin) /* supplied by dodiscover() */ +disp_artifact_discoveries( + winid tmpwin) /* supplied by dodiscover(); type is NHW_TEXT */ { int i, m, otyp; const char *algnstr; @@ -1075,7 +1133,7 @@ disp_artifact_discoveries(winid tmpwin) /* supplied by dodiscover() */ continue; /* for WIN_ERR, we just count */ if (i == 0) - putstr(tmpwin, iflags.menu_headings, "Artifacts"); + putstr(tmpwin, iflags.menu_headings.attr, "Artifacts"); m = artidisco[i]; otyp = artilist[m].otyp; algnstr = align_str(artilist[m].alignment); @@ -1096,7 +1154,8 @@ dump_artifact_info(winid tmpwin) int m; char buf[BUFSZ], buf2[BUFSZ]; - putstr(tmpwin, iflags.menu_headings, "Artifacts"); + /* not a menu, but header uses same bold or whatever attribute as such */ + putstr(tmpwin, iflags.menu_headings.attr, "Artifacts"); for (m = 1; m <= NROFARTIFACTS; ++m) { Snprintf(buf2, sizeof buf2, "[%s%s%s%s%s%s%s%s%s]", /* 9 bits overall */ @@ -1161,7 +1220,7 @@ static const char *const mb_verb[2][NUM_MB_INDICES] = { }; /* called when someone is being hit by Magicbane */ -static boolean +staticfn boolean Mb_hit(struct monst *magr, /* attacker */ struct monst *mdef, /* defender */ struct obj *mb, /* Magicbane */ @@ -1170,7 +1229,7 @@ Mb_hit(struct monst *magr, /* attacker */ boolean vis, /* whether the action can be seen */ char *hittee) /* target's name: "you" or mon_nam(mdef) */ { - struct permonst *old_uasmon; + struct permonst *old_mdat; const char *verb; boolean youattack = (magr == &gy.youmonst), youdefend = (mdef == &gy.youmonst), @@ -1229,7 +1288,7 @@ Mb_hit(struct monst *magr, /* attacker */ /* now perform special effects */ switch (attack_indx) { case MB_INDEX_CANCEL: - old_uasmon = gy.youmonst.data; + old_mdat = youdefend ? gy.youmonst.data : mdef->data; /* No mdef->mcan check: even a cancelled monster can be polymorphed * into a golem, and the "cancel" effect acts as if some magical * energy remains in spellcasting defenders to be absorbed later. @@ -1239,16 +1298,20 @@ Mb_hit(struct monst *magr, /* attacker */ } else { do_stun = FALSE; if (youdefend) { - if (gy.youmonst.data != old_uasmon) + if (gy.youmonst.data != old_mdat) *dmgptr = 0; /* rehumanized, so no more damage */ if (u.uenmax > 0) { u.uenmax--; if (u.uen > 0) u.uen--; - gc.context.botl = TRUE; + disp.botl = TRUE; You("lose magical energy!"); } } else { + /* canceled shapeshifter/vamp may have changed forms, so + update its name if necessary */ + if (mdef->data != old_mdat) + Strcpy(hittee, mon_nam(mdef)); if (mdef->data == &mons[PM_CLAY_GOLEM]) mdef->mhp = 1; /* cancelled clay golems will die */ if (youattack && attacktype(mdef->data, AT_MAGC)) { @@ -1256,7 +1319,7 @@ Mb_hit(struct monst *magr, /* attacker */ if (u.uenmax > u.uenpeak) u.uenpeak = u.uenmax; u.uen++; - gc.context.botl = TRUE; + disp.botl = TRUE; You("absorb magical energy!"); } } @@ -1327,7 +1390,7 @@ Mb_hit(struct monst *magr, /* attacker */ shieldeff(youdefend ? u.ux : mdef->mx, youdefend ? u.uy : mdef->my); } - if ((do_stun || do_confuse) && Verbose(0,Mb_hit)) { + if ((do_stun || do_confuse) && flags.verbose) { char buf[BUFSZ]; buf[0] = '\0'; @@ -1584,7 +1647,7 @@ artifact_hit( return ARTIFACTHIT_GAVEMSG; } *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER; - pline(behead_msg[rn2(SIZE(behead_msg))], wepdesc, + pline(ROLL_FROM(behead_msg), wepdesc, mon_nam(mdef)); if (Hallucination && !flags.female) pline("Good job Henry, but that wasn't Anne."); @@ -1612,7 +1675,7 @@ artifact_hit( return ARTIFACTHIT_GAVEMSG; } *dmgptr = 2 * (Upolyd ? u.mh : u.uhp) + FATAL_DAMAGE_MODIFIER; - pline(behead_msg[rn2(SIZE(behead_msg))], wepdesc, "you"); + pline(ROLL_FROM(behead_msg), wepdesc, "you"); otmp->dknown = TRUE; /* Should amulets fall off? */ return ARTIFACTHIT_INSTAKILLMSG | ARTIFACTHIT_GAVEMSG; @@ -1720,9 +1783,8 @@ artifact_hit( if (youattack) { healup(drain, 0, FALSE, FALSE); } else { - magr->mhp += drain; - if (magr->mhp > magr->mhpmax) - magr->mhp = magr->mhpmax; + assert(magr != 0); + healmon(magr, drain, 0); } } return retval; @@ -1748,9 +1810,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 ARTIFACTHIT_GAVEMSG; } @@ -1761,7 +1821,7 @@ artifact_hit( RESTORE_WARNING_FORMAT_NONLITERAL /* getobj callback for object to be invoked */ -static int +staticfn int invoke_ok(struct obj *obj) { if (!obj) @@ -1796,15 +1856,24 @@ doinvoke(void) return arti_invoke(obj); } -static int +staticfn void +nothing_special(struct obj *obj) +{ + if (carried(obj)) + You_feel("a surge of power, but nothing seems to happen."); +} + +staticfn int arti_invoke(struct obj *obj) { - register const struct artifact *oart = get_artifact(obj); + const struct artifact *oart; + if (!obj) { impossible("arti_invoke without obj"); return ECMD_OK; } - if (!oart || !oart->inv_prop) { + oart = get_artifact(obj); + if (oart == &artilist[ART_NONARTIFACT] || !oart->inv_prop) { if (obj->otyp == CRYSTAL_BALL) use_crystal_ball(&obj); else @@ -1814,7 +1883,7 @@ arti_invoke(struct obj *obj) if (oart->inv_prop > LAST_PROP) { /* It's a special power, not "just" a property */ - if (obj->age > gm.moves) { + if (obj->age > svm.moves) { /* the artifact is tired :-) */ You_feel("that %s %s ignoring you.", the(xname(obj)), otense(obj, "are")); @@ -1824,7 +1893,7 @@ arti_invoke(struct obj *obj) return ECMD_TIME; } } - obj->age = gm.moves + rnz(100); + obj->age = svm.moves + rnz(100); switch (oart->inv_prop) { case TAMING: { @@ -1853,8 +1922,10 @@ arti_invoke(struct obj *obj) due to PermaBlind or eyeless polymorph; vary the message in that situation */ && (HBlinded & ~TIMEOUT) != 0L) ? "slightly " : ""); - else - goto nothing_special; + else { + nothing_special(obj); + return ECMD_TIME; + } if (healamt > 0) { if (Upolyd) u.mh += healamt; @@ -1875,7 +1946,7 @@ arti_invoke(struct obj *obj) set_itimeout(&HBlinded, 1L); make_blinded(creamed, FALSE); } - gc.context.botl = TRUE; + disp.botl = TRUE; break; } case ENERGY_BOOST: { @@ -1887,10 +1958,12 @@ arti_invoke(struct obj *obj) epboost = u.uenmax - u.uen; if (epboost) { u.uen += epboost; - gc.context.botl = TRUE; + disp.botl = TRUE; You_feel("re-energized."); - } else - goto nothing_special; + } else { + nothing_special(obj); + return ECMD_TIME; + } break; } case UNTRAP: { @@ -1923,18 +1996,20 @@ arti_invoke(struct obj *obj) d_level newlev; winid tmpwin = create_nhwindow(NHW_MENU); anything any; - int clr = 0; + int clr = NO_COLOR; any = cg.zeroany; /* set all bits to zero */ start_menu(tmpwin, MENU_BEHAVE_STANDARD); - /* use index+1 (cant use 0) as identifier */ - for (i = num_ok_dungeons = 0; i < gn.n_dgns; i++) { - if (!gd.dungeons[i].dunlev_ureached) + /* 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, - gd.dungeons[i].dname, MENU_ITEMFLAGS_NONE); + svd.dungeons[i].dname, MENU_ITEMFLAGS_NONE); num_ok_dungeons++; last_ok_dungeon = i; } @@ -1947,7 +2022,8 @@ arti_invoke(struct obj *obj) n = select_menu(tmpwin, PICK_ONE, &selected); if (n <= 0) { destroy_nhwindow(tmpwin); - goto nothing_special; + nothing_special(obj); + return ECMD_TIME; } i = selected[0].item.a_int - 1; free((genericptr_t) selected); @@ -1962,10 +2038,10 @@ arti_invoke(struct obj *obj) * The closest level is either the entry or dunlev_ureached. */ newlev.dnum = i; - if (gd.dungeons[i].depth_start >= depth(&u.uz)) - newlev.dlevel = gd.dungeons[i].entry_lev; + if (svd.dungeons[i].depth_start >= depth(&u.uz)) + newlev.dlevel = svd.dungeons[i].entry_lev; else - newlev.dlevel = gd.dungeons[i].dunlev_ureached; + 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()) { @@ -1985,8 +2061,10 @@ arti_invoke(struct obj *obj) case CREATE_AMMO: { struct obj *otmp = mksobj(ARROW, TRUE, FALSE); - if (!otmp) - goto nothing_special; + if (!otmp) { + nothing_special(obj); + return ECMD_TIME; + } otmp->blessed = obj->blessed; otmp->cursed = obj->cursed; otmp->bknown = obj->bknown; @@ -2026,7 +2104,7 @@ arti_invoke(struct obj *obj) if (mtmp->data->msound == MS_NEMESIS) continue; - if (In_quest(&u.uz) && !gq.quest_status.killed_nemesis) + if (In_quest(&u.uz) && !svq.quest_status.killed_nemesis) chance += 10; if (is_dprince(mtmp->data)) chance += 2; @@ -2049,13 +2127,49 @@ arti_invoke(struct obj *obj) } if (nvanished) { - pline("%she demon%s disappear%s in a cloud of brimstone!", - nstayed ? (nvanished > nstayed ? "Most of t" : "Some of t") : "T", - nvanished > 1 ? "s" : "", - nvanished > 1 ? "" : "s"); + 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 LIGHTNING_BOLT: { struct obj* pseudo = mksobj(WAN_LIGHTNING, FALSE, FALSE); pseudo->blessed = pseudo->cursed = 0; @@ -2111,20 +2225,23 @@ arti_invoke(struct obj *obj) } else { You("gaze into the polished surface..."); - gc.context.crystal.ball = obj; - gc.context.crystal.o_id = obj->o_id; - gc.context.crystal.looktime = rn1(5, 6); /* same as ball */ + svc.context.crystal.ball = obj; + svc.context.crystal.o_id = obj->o_id; + svc.context.crystal.looktime = rn1(5, 6); /* same as ball */ set_occupation(look_in_crystal_ball, "gazing", 0); } break; } + default: + impossible("Unknown invoke power %d.", oart->inv_prop); + break; } } else { long eprop = (u.uprops[oart->inv_prop].extrinsic ^= W_ARTI), iprop = u.uprops[oart->inv_prop].intrinsic; boolean on = (eprop & W_ARTI) != 0; /* true if prop just set */ - if (on && obj->age > gm.moves) { + if (on && obj->age > svm.moves) { /* the artifact is tired :-) */ u.uprops[oart->inv_prop].extrinsic ^= W_ARTI; You_feel("that %s %s ignoring you.", the(xname(obj)), @@ -2137,14 +2254,12 @@ arti_invoke(struct obj *obj) } else if (!on) { /* when turning off property, determine downtime */ /* arbitrary for now until we can tune this -dlc */ - obj->age = gm.moves + rnz(100); + obj->age = svm.moves + rnz(100); } if ((eprop & ~W_ARTI) || iprop) { - nothing_special: /* you had the property from some other source too */ - if (carried(obj)) - You_feel("a surge of power, but nothing seems to happen."); + nothing_special(obj); return ECMD_TIME; } switch (oart->inv_prop) { @@ -2172,8 +2287,10 @@ arti_invoke(struct obj *obj) break; /* Formerly used for Orb of Detection, now unused. case INVIS: - if (BInvis || Blind) - goto nothing_special; + if (BInvis || Blind) { + nothing_special(obj); + return ECMD_TIME; + } newsym(u.ux, u.uy); if (on) Your("body takes on a %s transparency...", @@ -2198,7 +2315,8 @@ finesse_ahriman(struct obj *obj) /* if we aren't levitating or this isn't an artifact which confers levitation via #invoke then freeinv() won't toggle levitation */ - if (!Levitation || (oart = get_artifact(obj)) == 0 + if (!Levitation + || (oart = get_artifact(obj)) == &artilist[ART_NONARTIFACT] || oart->inv_prop != LEVITATION || !(ELevitation & W_ARTI)) return FALSE; @@ -2226,7 +2344,7 @@ artifact_light(struct obj *obj) && (obj->owornmask & (W_ARM | W_ARMC)) != 0L) return TRUE; - return (boolean) (get_artifact(obj) + return (boolean) ((get_artifact(obj) != &artilist[ART_NONARTIFACT]) && (is_art(obj, ART_SUNSWORD) || is_art(obj, ART_SOL_VALTIVA))); } @@ -2240,7 +2358,7 @@ arti_speak(struct obj *obj) char buf[BUFSZ]; /* Is this a speaking artifact? */ - if (!oart || !(oart->spfx & SPFX_SPEAK)) + if (oart == &artilist[ART_NONARTIFACT] || !(oart->spfx & SPFX_SPEAK)) return ECMD_OK; /* nothing happened */ line = getrumor(bcsign(obj), buf, TRUE); @@ -2257,7 +2375,8 @@ artifact_has_invprop(struct obj *otmp, uchar inv_prop) { const struct artifact *arti = get_artifact(otmp); - return (boolean) (arti && (arti->inv_prop == inv_prop)); + return (boolean) ((arti != &artilist[ART_NONARTIFACT]) + && (arti->inv_prop == inv_prop)); } /* Return the price sold to the hero of a given artifact or unique item */ @@ -2272,7 +2391,7 @@ arti_cost(struct obj *otmp) return (100L * (long) objects[otmp->otyp].oc_cost); } -static uchar +staticfn uchar abil_to_adtyp(long *abil) { struct abil2adtyp_tag { @@ -2296,7 +2415,7 @@ abil_to_adtyp(long *abil) return 0; } -static unsigned long +staticfn unsigned long abil_to_spfx(long *abil) { static const struct abil2spfx_tag { @@ -2348,11 +2467,11 @@ what_gives(long *abil) for (obj = gi.invent; obj; obj = obj->nobj) { if (obj->oartifact - && (abil != &EWarn_of_mon || gc.context.warntype.obj - || gc.context.warntype.obj_mlet)) { + && (abil != &EWarn_of_mon || svc.context.warntype.obj + || svc.context.warntype.obj_mlet)) { const struct artifact *art = get_artifact(obj); - if (art) { + if (art != &artilist[ART_NONARTIFACT]) { if (dtyp) { if (art->cary.adtyp == dtyp /* carried */ || (art->defn.adtyp == dtyp /* defends while worn */ @@ -2367,6 +2486,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)) @@ -2391,7 +2514,7 @@ static const char *const glow_verbs[] = { }; /* relative strength that Sting is glowing (0..3), to select verb */ -static int +staticfn int glow_strength(int count) { /* glow strength should also be proportional to proximity and @@ -2416,7 +2539,9 @@ glow_verb(int count, /* 0 means blind rather than no applicable creatures */ /* use for warning "glow" for Sting, Orcrist, and Grimtooth */ void -Sting_effects(int orc_count) /* new count (warn_obj_cnt is old count); -1 is a flag value */ +Sting_effects( + int orc_count) /* new count (warn_obj_cnt is old count); + * -1 is a flag value */ { if (uwep && uwep->oartifact && artilist[(int) uwep->oartifact].acolor != NO_COLOR) { @@ -2554,24 +2679,29 @@ retouch_object(struct obj **objp, /* might be destroyed or unintentionally dropp return 0; } -/* an item which is worn/wielded or an artifact which conveys - something via being carried or which has an #invoke effect - currently in operation undergoes a touch test; if it fails, - it will be unworn/unwielded and revoked but not dropped */ -static boolean -untouchable(struct obj *obj, boolean drop_untouchable) +/* hero has changed form or alignment; an item which is worn/wielded + or an artifact which conveys something via being carried or which + has an #invoke effect currently in operation undergoes a touch test; + if it fails, it will be unworn/unwielded and maybe dropped */ +staticfn boolean +untouchable( + struct obj *obj, /* object to test; in invent or is steed's saddle */ + boolean drop_untouchable) /* whether to drop it if it can't be touched */ { struct artifact *art; boolean beingworn, carryeffect, invoked; long wearmask = ~(W_QUIVER | (u.twoweap ? 0L : W_SWAPWEP) | W_BALL); - beingworn = ((obj->owornmask & wearmask) != 0L - /* some items in use don't have any wornmask setting */ - || (obj->oclass == TOOL_CLASS - && (obj->lamplit || (obj->otyp == LEASH && obj->leashmon) - || (Is_container(obj) && Has_contents(obj))))); + beingworn = (obj /* never Null; this pacifies static analysis when + * the get_artifact() macro tests 'obj' for Null */ + && ((obj->owornmask & wearmask) != 0L + /* some items in use don't have any wornmask setting */ + || (obj->oclass == TOOL_CLASS + && (obj->lamplit + || (obj->otyp == LEASH && obj->leashmon) + || (Is_container(obj) && Has_contents(obj)))))); - if ((art = get_artifact(obj)) != 0) { + if ((art = get_artifact(obj)) != &artilist[ART_NONARTIFACT]) { carryeffect = (art->cary.adtyp || art->cspfx); invoked = (art->inv_prop > 0 && art->inv_prop <= LAST_PROP && (u.uprops[art->inv_prop].extrinsic & W_ARTI) != 0L); @@ -2597,7 +2727,8 @@ untouchable(struct obj *obj, boolean drop_untouchable) /* check all items currently in use (mostly worn) for touchability */ void -retouch_equipment(int dropflag) /* 0==don't drop, 1==drop all, 2==drop weapon */ +retouch_equipment( + int dropflag) /* 0==don't drop, 1==drop all, 2==drop weapon */ { static int nesting = 0; /* recursion control */ struct obj *obj; @@ -2663,12 +2794,11 @@ retouch_equipment(int dropflag) /* 0==don't drop, 1==drop all, 2==drop weapon */ clear_bypasses(); /* reset upon final exit */ } -static int +staticfn int count_surround_traps(coordxy x, coordxy y) { struct rm *levp; - struct obj *otmp; - struct trap *ttmp; + struct obj *o; coordxy dx, dy; int glyph, ret = 0; @@ -2684,7 +2814,7 @@ count_surround_traps(coordxy x, coordxy y) glyph = glyph_at(dx, dy); if (glyph_is_trap(glyph)) continue; - if ((ttmp = t_at(dx, dy)) != 0) { + if (t_at(dx, dy)) { ++ret; continue; } @@ -2693,8 +2823,8 @@ count_surround_traps(coordxy x, coordxy y) ++ret; continue; } - for (otmp = gl.level.objects[dx][dy]; otmp; otmp = otmp->nexthere) - if (Is_container(otmp) && otmp->otrapped) { + for (o = svl.level.objects[dx][dy]; o; o = o->nexthere) + if (Is_container(o) && o->otrapped) { ++ret; /* we're counting locations, so just */ break; /* count the first one in a pile */ } @@ -2762,7 +2892,37 @@ has_magic_key(struct monst *mon) /* if null, hero assumed */ return (struct obj *) 0; } -/* ditto but for the Apple of Discord - only for hero */ +/* #define is_art(o,art) ((o) && (o)->oartifact == (art)) */ + +boolean +is_art(struct obj *obj, int art) +{ + if (obj && obj->oartifact == art) + return TRUE; + return FALSE; +} + +/* #define get_artifact(o) \ + (((o) && ((o)->artifact > 0 && (o)->artifact < AFTER_LAST_ARTIFACT)) \ + ? &artilist[(int) (o)->oartifact] \ + : &artilist[ART_NONARTIFACT]) */ + +staticfn struct artifact * +get_artifact(struct obj *obj) +{ + if (obj) { + int artidx = (int) obj->oartifact; + + /* skip 0, 1st artifact at 1 */ + /* SIZE(artilist) would include the terminator, + so use AFTER_LAST_ARTIFACT instead */ + if (artidx > 0 && artidx < AFTER_LAST_ARTIFACT) + return &artilist[artidx]; + } + return &artilist[ART_NONARTIFACT]; +} + +/* like has_magic_key but for the Apple of Discord - only for hero */ struct obj * uhave_magic_apple(void) { diff --git a/src/attrib.c b/src/attrib.c index 414e4f6c43..e8ae8b3a69 100644 --- a/src/attrib.c +++ b/src/attrib.c @@ -1,11 +1,10 @@ -/* NetHack 3.7 attrib.c $NHDT-Date: 1651908297 2022/05/07 07:24:57 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.86 $ */ +/* NetHack 3.7 attrib.c $NHDT-Date: 1726168587 2024/09/12 19:16:27 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.129 $ */ /* 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 @@ -15,7 +14,7 @@ static const char "agile", "tough", "charismatic" }, *const minusattr[] = { "weaker", "stupider", "more foolish", "clumsier", "more fragile", "more repulsive" }; -/* also used by enlightenment for non-abbreviated status info */ +/* also used by enlightenment in insight.c for non-abbreviated status info */ const char *const attrname[] = { "strength", "intelligence", "wisdom", "dexterity", "constitution", "charisma" }; @@ -104,11 +103,13 @@ static const struct innate { hum_abil[] = { { 0, 0, 0, 0 } }; -static void exerper(void); -static void postadjabil(long *); -static const struct innate *role_abil(int); -static const struct innate *check_innate_abil(long *, long); -static int innately(long *); +staticfn void exerper(void); +staticfn int rnd_attr(void); +staticfn int init_attr_role_redist(int, boolean); +staticfn void postadjabil(long *); +staticfn const struct innate *role_abil(int); +staticfn const struct innate *check_innate_abil(long *, long); +staticfn int innately(long *); /* adjust an attribute; return an adjattrib_return value */ boolean @@ -172,7 +173,7 @@ adjattrib( } if (ACURR(ndx) == old_acurr) { if (ABASE(ndx) == old_abase && AMAX(ndx) == old_amax) { - if (msgflg == AA_YESMSG && Verbose(0, adjattrib)) { + if (msgflg == AA_YESMSG && flags.verbose) { attrstr = staticattr[ndx]; pline("You're %s as %s as you can get.", abonflg ? "currently" : "already", attrstr); @@ -181,7 +182,7 @@ adjattrib( } else { /* current stayed the same but base value changed, or base is at minimum and reduction caused max to drop */ - if (msgflg != AA_NOMSG && Verbose(4, adjattrib2)) { + if (msgflg != AA_NOMSG && flags.verbose) { Your("innate %s has %s.", attrname[ndx], (incr > 0) ? "improved" : "declined"); } @@ -189,10 +190,10 @@ adjattrib( } } - gc.context.botl = TRUE; + disp.botl = TRUE; if (msgflg != AA_NOMSG) You_feel("%s%s!", (incr > 1 || incr < -1) ? "much " : "", attrstr); - if (gp.program_state.in_moveloop && (ndx == A_STR || ndx == A_CON)) + if (program_state.in_moveloop && (ndx == A_STR || ndx == A_CON)) (void) encumber_msg(); return AA_CURRCHNG; } @@ -243,19 +244,19 @@ losestr(int num, const char *knam, schar k_format) losehp(dmg, knam, k_format); if (Upolyd) { - /* if still polymorhed, reduce you-as-monst maxHP; never below 1 */ - u.mhmax -= min(dmg, u.mhmax - 1); + /* when still poly'd, reduce you-as-monst maxHP; never below 1 */ + setuhpmax(max(u.mhmax - dmg, 1), FALSE); /* acts as setmhmax() */ } else if (!waspolyd) { /* not polymorphed now and didn't rehumanize when taking damage; - reduce max HP, but not below below uhpmin */ + reduce max HP, but not below uhpmin */ if (u.uhpmax > uhpmin) - setuhpmax(max(u.uhpmax - dmg, uhpmin)); + setuhpmax(max(u.uhpmax - dmg, uhpmin), FALSE); } - gc.context.botl = TRUE; + disp.botl = TRUE; } #if 0 /* only possible if uhpmax was already less than uhpmin */ if (!Upolyd && u.uhpmax < uhpmin) { - setuhpmax(min(olduhpmax, uhpmin)); + setuhpmax(min(olduhpmax, uhpmin), FALSE); if (!Drain_resistance) losexp(NULL); /* won't be fatal when no 'drainer' is supplied */ } @@ -343,7 +344,7 @@ poisoned( /* suppress killer prefix if it already has one */ i = name_to_mon(pkiller, (int *) 0); - if (i >= LOW_PM && (mons[i].geno & G_UNIQ)) { + if (ismnum(i) && (mons[i].geno & G_UNIQ)) { kprefix = KILLED_BY; if (!type_is_pname(&mons[i])) pkiller = the(pkiller); @@ -371,8 +372,8 @@ poisoned( } if (u.uhp < 1) { - gk.killer.format = kprefix; - Strcpy(gk.killer.name, pkiller); + svk.killer.format = kprefix; + Strcpy(svk.killer.name, pkiller); /* "Poisoned by a poisoned ___" is redundant */ done(strstri(pkiller, "poison") ? DIED : POISONING); } @@ -389,14 +390,16 @@ change_luck(schar n) u.uluck = LUCKMAX; } +/* decide whether there are more blessed luckstones (plus luck-conferring + artifacts) than cursed ones; optionally combine uncursed with blessed */ int -stone_luck(boolean parameter) /* So I can't think up of a good name. So sue me. --KAA */ +stone_luck(boolean include_uncursed) { - register struct obj *otmp; - register long bonchance = 0; + struct obj *otmp; + long bonchance = 0; /* Your quest leader dying casts a pall over your fortunes... */ - if (gq.quest_status.killed_leader) { + if (svq.quest_status.killed_leader) { return -1; } @@ -407,9 +410,7 @@ stone_luck(boolean parameter) /* So I can't think up of a good name. So sue me. if (confers_luck(otmp)) { if (otmp->cursed) bonchance -= otmp->quan; - else if (otmp->blessed) - bonchance += otmp->quan; - else if (parameter) + else if (otmp->blessed || include_uncursed) bonchance += otmp->quan; } @@ -453,13 +454,13 @@ restore_attrib(void) if (ATEMP(i) != equilibrium && ATIME(i) != 0) { if (!(--(ATIME(i)))) { /* countdown for change */ ATEMP(i) += (ATEMP(i) > 0) ? -1 : 1; - gc.context.botl = TRUE; + disp.botl = TRUE; if (ATEMP(i)) /* reset timer */ ATIME(i) = 100 / ACURR(A_CON); } } } - if (gc.context.botl) + if (disp.botl) (void) encumber_msg(); } @@ -493,14 +494,14 @@ exercise(int i, boolean inc_or_dec) : "Con", (inc_or_dec) ? "inc" : "dec", AEXE(i)); } - if (gm.moves > 0 && (i == A_STR || i == A_CON)) + if (svm.moves > 0 && (i == A_STR || i == A_CON)) (void) encumber_msg(); } -static void +staticfn void exerper(void) { - if (!(gm.moves % 10)) { + if (!(svm.moves % 10)) { /* Hunger Checks */ int hs = (u.uhunger > 1000) ? SATIATED : (u.uhunger > 150) ? NOT_HUNGRY @@ -547,7 +548,7 @@ exerper(void) } /* status checks */ - if (!(gm.moves % 5)) { + if (!(svm.moves % 5)) { debugpline0("exerper: Status checks"); if ((HClairvoyant & (INTRINSIC | TIMEOUT)) && !BClairvoyant) exercise(A_WIS, TRUE); @@ -582,11 +583,11 @@ exerchk(void) /* Check out the periodic accumulations */ exerper(); - if (gm.moves >= gc.context.next_attrib_check) { + if (svm.moves >= svc.context.next_attrib_check) { debugpline1("exerchk: ready to test. multi = %ld.", gm.multi); } /* Are we ready for a test? */ - if (gm.moves >= gc.context.next_attrib_check && !gm.multi) { + if (svm.moves >= svc.context.next_attrib_check && !gm.multi) { debugpline0("exerchk: testing."); /* * Law of diminishing returns (Part II): @@ -650,63 +651,74 @@ exerchk(void) platform-dependent rounding/truncation for negative vals */ AEXE(i) = (abs(ax) / 2) * mod_val; } - gc.context.next_attrib_check += rn1(200, 800); + svc.context.next_attrib_check += rn1(200, 800); debugpline1("exerchk: next check at %ld.", - gc.context.next_attrib_check); + svc.context.next_attrib_check); } } -/* allocate hero's initial characteristics */ -void -init_attr(int np) +/* return random hero attribute (by role's attr distribution). + returns A_MAX if failed. */ +staticfn int +rnd_attr(void) { - register int i, x, tryct; - - for (i = 0; i < A_MAX; i++) { - ABASE(i) = AMAX(i) = gu.urole.attrbase[i]; - ATEMP(i) = ATIME(i) = 0; - np -= gu.urole.attrbase[i]; - } + int i, x = rn2(100); /* 3.7: the x -= ... calculation used to have an off by 1 error that resulted in the values being biased toward Str and away from Cha */ - tryct = 0; - while (np > 0 && tryct < 100) { - x = rn2(100); - for (i = 0; i < A_MAX; ++i) - if ((x -= gu.urole.attrdist[i]) < 0) - break; + for (i = 0; i < A_MAX; ++i) + if ((x -= gu.urole.attrdist[i]) < 0) + break; + return i; +} + +/* add or subtract np points from random attributes, + adjusting the base and maximum values of the attributes. + if subtracting, np must be negative. + returns the left over points. */ +staticfn int +init_attr_role_redist(int np, boolean addition) +{ + int tryct = 0; + int adj = addition ? 1 : -1; + + while ((addition ? (np > 0) : (np < 0)) && tryct < 100) { + int i = rnd_attr(); + if (i >= A_MAX || ABASE(i) >= ATTRMAX(i)) { tryct++; continue; } tryct = 0; - ABASE(i)++; - AMAX(i)++; - np--; + ABASE(i) += adj; + AMAX(i) += adj; + np -= adj; } + return np; +} - tryct = 0; - while (np < 0 && tryct < 100) { /* for redistribution */ - x = rn2(100); - for (i = 0; i < A_MAX; ++i) - if ((x -= gu.urole.attrdist[i]) < 0) - break; - if (i >= A_MAX || ABASE(i) <= ATTRMIN(i)) { - tryct++; - continue; - } - tryct = 0; - ABASE(i)--; - AMAX(i)--; - np++; +/* allocate hero's initial characteristics */ +void +init_attr(int np) +{ + int i; + + for (i = 0; i < A_MAX; i++) { + ABASE(i) = AMAX(i) = gu.urole.attrbase[i]; + ATEMP(i) = ATIME(i) = 0; + np -= gu.urole.attrbase[i]; } + + /* distribute leftover points */ + np = init_attr_role_redist(np, TRUE); + /* if we went over, remove points */ + np = init_attr_role_redist(np, FALSE); } void redist_attr(void) { - register int i, tmp; + int i, tmp; for (i = 0; i < A_MAX; i++) { if (i == A_INT || i == A_WIS) @@ -726,7 +738,23 @@ redist_attr(void) /* (void) encumber_msg(); -- caller needs to do this */ } -static +/* apply minor variation to attributes */ +void +vary_init_attr(void) +{ + int i; + + for (i = 0; i < A_MAX; i++) + if (!rn2(20)) { + int xd = rn2(7) - 2; /* biased variation */ + + (void) adjattrib(i, xd, AA_NOMSG); + if (ABASE(i) < AMAX(i)) + AMAX(i) = ABASE(i); + } +} + +staticfn void postadjabil(long *ability) { @@ -736,7 +764,7 @@ postadjabil(long *ability) see_monsters(); } -static const struct innate * +staticfn const struct innate * role_abil(int r) { const struct { @@ -765,7 +793,7 @@ role_abil(int r) return roleabils[i].abil; } -static const struct innate * +staticfn const struct innate * check_innate_abil(long *ability, long frommask) { const struct innate *abil = 0; @@ -811,7 +839,7 @@ check_innate_abil(long *ability, long frommask) #define FROM_LYCN 6 /* check whether particular ability has been obtained via innate attribute */ -static int +staticfn int innately(long *ability) { const struct innate *iptr; @@ -833,7 +861,7 @@ is_innate(int propidx) int innateness; /* innately() would report FROM_FORM for this; caller wants specificity */ - if (propidx == DRAIN_RES && u.ulycn >= LOW_PM) + if (propidx == DRAIN_RES && ismnum(u.ulycn)) return FROM_LYCN; if (propidx == FAST && Very_fast) return FROM_NONE; /* can't become very fast innately */ @@ -844,7 +872,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; } @@ -852,7 +881,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]; @@ -896,7 +926,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" @@ -915,7 +945,7 @@ from_what(int propidx) /* special cases can have negative values */ else if (propidx == BLINDED && u.ucreamed && BlindedTimeout == (long) u.ucreamed && !EBlinded && !(HBlinded & ~TIMEOUT)) - Sprintf(buf, "due to goop coverting your %s", + Sprintf(buf, "due to goop covering your %s", body_part(FACE)); /* remove some verbosity and/or redundancy */ @@ -956,7 +986,7 @@ RESTORE_WARNING_FORMAT_NONLITERAL void adjabil(int oldlevel, int newlevel) { - register const struct innate *abil, *rabil; + const struct innate *abil, *rabil; long prevabil, mask = FROMEXPER; abil = role_abil(Role_switch); @@ -1024,7 +1054,9 @@ adjabil(int oldlevel, int newlevel) } } -/* calculate hp max increase for new level */ +/* called when gaining a level (before u.ulevel gets incremented); + also called with u.ulevel==0 during hero initialization or for + re-init if hero turns into a "new man/woman/elf/&c" */ int newhp(void) { @@ -1037,7 +1069,7 @@ newhp(void) hp += rnd(gu.urole.hpadv.inrnd); if (gu.urace.hpadv.inrnd > 0) hp += rnd(gu.urace.hpadv.inrnd); - if (gm.moves <= 1L) { /* initial hero; skip for polyself to new man */ + if (svm.moves == 0) { /* initial hero; skip for polyself to new man */ /* Initialize alignment stuff */ u.ualign.type = aligns[flags.initalign].value; u.ualign.record = gu.urole.initrecord; @@ -1100,76 +1132,122 @@ minuhpmax(int altmin) return max(u.ulevel, altmin); } -/* update u.uhpmax and values of other things that depend upon it */ +/* update u.uhpmax or u.mhmax and values of other things that depend upon + whichever of them is relevant */ void -setuhpmax(int newmax) +setuhpmax(int newmax, boolean even_when_polyd) { - if (newmax != u.uhpmax) { - u.uhpmax = newmax; - if (u.uhpmax > u.uhppeak) - u.uhppeak = u.uhpmax; - gc.context.botl = TRUE; + if (!Upolyd || even_when_polyd) { + if (newmax != u.uhpmax) { + u.uhpmax = newmax; + if (u.uhpmax > u.uhppeak) + u.uhppeak = u.uhpmax; + disp.botl = TRUE; + } + if (u.uhp > u.uhpmax) + u.uhp = u.uhpmax, disp.botl = TRUE; + } else { /* Upolyd */ + if (newmax != u.mhmax) { + u.mhmax = newmax; + disp.botl = TRUE; + } + if (u.mh > u.mhmax) + u.mh = u.mhmax, disp.botl = TRUE; } - if (u.uhp > u.uhpmax) - u.uhp = u.uhpmax, gc.context.botl = TRUE; } -schar -acurr(int x) +/* called after setuhpmax() when damage is pending; + if uhpmax (or mhmax) has been reduced, it might have caused uhp (or mh) + to be reduced too; if so, recalculate pending loss to account for that */ +int +adjuhploss( + int loss, /* pending hp loss */ + int olduhp) /* does double duty as oldmh when Upolyd */ { - register int tmp = (u.abon.a[x] + u.atemp.a[x] + u.acurr.a[x]); + if (!Upolyd) { + if (u.uhp < olduhp) + loss -= (olduhp - u.uhp); + } else { + if (u.mh < olduhp) + loss -= (olduhp - u.mh); + } + return max(loss, 1); +} - if (x == A_STR) { - if (tmp >= 125 || (uarmg && uarmg->otyp == GAUNTLETS_OF_POWER)) - return (schar) 125; +/* return the current effective value of a specific characteristic + (the 'a' in 'acurr()' comes from outdated use of "attribute" for the + six Str/Dex/&c characteristics; likewise for u.abon, u.atemp, u.acurr) */ +schar +acurr(int chridx) +{ + int tmp, result = 0; /* 'result' will always be reset to positive value */ + + assert(chridx >= 0 && chridx < A_MAX); + tmp = u.abon.a[chridx] + u.atemp.a[chridx] + u.acurr.a[chridx]; + + /* for Strength: 3 <= result <= 125; + for all others: 3 <= result <= 25 */ + if (chridx == A_STR) { + /* strength value is encoded: 3..18 normal, 19..118 for 18/xx (with + 1 <= xx <= 100), and 119..125 for other characteristics' 19..25; + STR18(x) yields 18 + x (intended for 0 <= x <= 100; not used here); + STR19(y) yields 100 + y (intended for 19 <= y <= 25) */ + if (tmp >= STR19(25) || (uarmg && uarmg->otyp == GAUNTLETS_OF_POWER)) + result = STR19(25); /* 125 */ else -#ifdef WIN32_BUG - return (x = ((tmp <= 3) ? 3 : tmp)); -#else - return (schar) ((tmp <= 3) ? 3 : tmp); -#endif - } else if (x == A_CHA) { - if (tmp < 18 - && (gy.youmonst.data->mlet == S_NYMPH - || u.umonnum == PM_AMOROUS_DEMON)) - return (schar) 18; - } else if (x == A_CON) { + /* need non-zero here to avoid 'if(result==0)' below because + that doesn't deal with Str encoding; the cap of 25 applied + there would limit Str to 18/07 [18 + 7] */ + result = max(tmp, 3); + } else if (chridx == A_CHA) { + if (tmp < 18 && (gy.youmonst.data->mlet == S_NYMPH + || u.umonnum == PM_AMOROUS_DEMON)) + result = 18; + } else if (chridx == A_CON) { if (u_wield_art(ART_OGRESMASHER)) - return (schar) 25; - } else if (x == A_INT || x == A_WIS) { - /* yes, this may raise int/wis if player is sufficiently - * stupid. there are lower levels of cognition than "dunce". - */ + result = 25; + } else if (chridx == A_INT || chridx == A_WIS) { + /* Yes, this may raise Int and/or Wis if hero is sufficiently + stupid. There are lower levels of cognition than "dunce". */ if (uarmh && uarmh->otyp == DUNCE_CAP) - return (schar) 6; + result = 6; + } else if (chridx == A_DEX) { + ; /* there aren't any special cases for dexterity */ } -#ifdef WIN32_BUG - return (x = ((tmp >= 25) ? 25 : (tmp <= 3) ? 3 : tmp)); -#else - return (schar) ((tmp >= 25) ? 25 : (tmp <= 3) ? 3 : tmp); -#endif + + if (result == 0) /* none of the special cases applied */ + result = (tmp >= 25) ? 25 : (tmp <= 3) ? 3 : tmp; + + return (schar) result; } -/* condense clumsy ACURR(A_STR) value into value that fits into game formulas - */ +/* condense clumsy ACURR(A_STR) value into value that fits into formulas */ schar acurrstr(void) { - register int str = ACURR(A_STR); - - if (str <= 18) - return (schar) str; - if (str <= 121) - return (schar) (19 + str / 50); /* map to 19..21 */ - else - return (schar) (min(str, 125) - 100); /* 22..25 */ + int str = ACURR(A_STR), /* 3..125 after massaging by acurr() */ + result; /* 3..25 */ + + if (str <= STR18(0)) /* <= 18; max(,3) here is redundant */ + result = max(str, 3); /* 3..18 */ + else if (str <= STR19(21)) /* <= 121 */ + /* this converts + 18/01..18/31 into 19, + 18/32..18/81 into 20, + 18/82..18/100 and 19..21 into 21 */ + result = 19 + str / 50; /* map to 19..21 */ + else /* convert 122..125; min(,125) here is redundant */ + result = min(str, 125) - 100; /* 22..25 */ + + return (schar) result; } /* when wearing (or taking off) an unID'd item, this routine is used to distinguish between observable +0 result and no-visible-effect due to an attribute not being able to exceed maximum or minimum */ boolean -extremeattr(int attrindx) /* does attrindx's value match its max or min? */ +extremeattr( + int attrindx) /* does attrindx's value match its max or min? */ { /* Fixed_abil and racial MINATTR/MAXATTR aren't relevant here */ int lolimit = 3, hilimit = 25, curval = ACURR(attrindx); @@ -1203,8 +1281,14 @@ adjalign(int n) int newalign = u.ualign.record + n; if (n < 0) { + unsigned newabuse = u.ualign.abuse - n; + if (newalign < u.ualign.record) u.ualign.record = newalign; + if (newabuse > u.ualign.abuse) { + u.ualign.abuse = newabuse; + adj_erinys(newabuse); + } } else if (newalign > u.ualign.record) { u.ualign.record = newalign; if (u.ualign.record > ALIGNLIM) @@ -1214,16 +1298,17 @@ adjalign(int n) /* change hero's alignment type, possibly losing use of artifacts */ void -uchangealign(int newalign, - int reason) /* 0==conversion, 1==helm-of-OA on, 2==helm-of-OA off */ +uchangealign( + int newalign, + int reason) /* A_CG_CONVERT, A_CG_HELM_ON, or A_CG_HELM_OFF */ { aligntyp oldalign = u.ualign.type; u.ublessed = 0; /* lose divine protection */ /* You/Your/pline message with call flush_screen(), triggering bot(), so the actual data change needs to come before the message */ - gc.context.botl = TRUE; /* status line needs updating */ - if (reason == 0) { + disp.botl = TRUE; /* status line needs updating */ + if (reason == A_CG_CONVERT) { /* conversion via altar */ livelog_printf(LL_ALIGNMENT, "permanently converted to %s", aligns[1 - newalign].adj); @@ -1235,18 +1320,21 @@ uchangealign(int newalign, (u.ualign.type != oldalign) ? "sudden " : ""); } else { /* putting on or taking off a helm of opposite alignment */ - if (reason == 1) { + u.ualign.type = (aligntyp) newalign; + if (reason == A_CG_HELM_ON) { + adjalign(-7); /* for abuse -- record will be cleared shortly */ + Your("mind oscillates %s.", Hallucination ? "wildly" : "briefly"); + make_confused(rn1(2, 3), FALSE); + if (Is_astralevel(&u.uz) || ((unsigned) rn2(50) < u.ualign.abuse)) + summon_furies(Is_astralevel(&u.uz) ? 0 : 1); /* don't livelog taking it back off */ livelog_printf(LL_ALIGNMENT, "used a helm to turn %s", aligns[1 - newalign].adj); - } - u.ualign.type = (aligntyp) newalign; - if (reason == 1) - Your("mind oscillates %s.", Hallucination ? "wildly" : "briefly"); - else if (reason == 2) + } else if (reason == A_CG_HELM_OFF) { Your("mind is %s.", Hallucination ? "much of a muchness" : "back in sync with your body"); + } } if (u.ualign.type != oldalign) { u.ualign.record = 0; /* slate is wiped clean */ diff --git a/src/ball.c b/src/ball.c index 909a666519..b000f9c322 100644 --- a/src/ball.c +++ b/src/ball.c @@ -8,11 +8,11 @@ #include "hack.h" -static int bc_order(void); -static void litter(void); -static void placebc_core(void); -static void unplacebc_core(void); -static boolean check_restriction(int); +staticfn int bc_order(void); +staticfn void litter(void); +staticfn void placebc_core(void); +staticfn void unplacebc_core(void); +staticfn boolean check_restriction(int); static int bcrestriction = 0; #ifdef BREADCRUMBS @@ -48,7 +48,7 @@ ballfall(void) { boolean gets_hit; - if (uball && carried(uball) && welded(uball)) + if (!uball || (uball && carried(uball) && welded(uball))) return; gets_hit = (((uball->ox != u.ux) || (uball->oy != u.uy)) @@ -62,11 +62,11 @@ ballfall(void) if (is_brittle(uarmh) && break_glass_obj(uarmh)) { ; } - else if (is_hard(uarmh)) { + else if (hard_helmet(uarmh)) { Your("helmet only slightly protects you."); if (dmg > 2) dmg -= 2; - } else if (Verbose(0, ballfall)) + } else if (flags.verbose) pline("%s does not protect you.", Yname2(uarmh)); } losehp(Maybe_Half_Phys(dmg), "crunched in the head by an iron ball", @@ -124,7 +124,7 @@ ballfall(void) * * Should not be called while swallowed except on waterlevel. */ -static void +staticfn void placebc_core(void) { if (!uchain || !uball) { @@ -151,7 +151,7 @@ placebc_core(void) bcrestriction = 0; } -static void +staticfn void unplacebc_core(void) { if (u.uswallow) { @@ -184,7 +184,7 @@ unplacebc_core(void) u.bc_felt = 0; /* feel nothing */ } -static boolean +staticfn boolean check_restriction(int restriction) { boolean ret = FALSE; @@ -358,7 +358,7 @@ Lift_covet_and_placebc(int pin, char *funcnm, int linenum) * Return the stacking of the hero's ball & chain. This assumes that the * hero is being punished. */ -static int +staticfn int bc_order(void) { struct obj *obj; @@ -367,7 +367,7 @@ bc_order(void) || u.uswallow) return BCPOS_DIFFER; - for (obj = gl.level.objects[uball->ox][uball->oy]; obj; + for (obj = svl.level.objects[uball->ox][uball->oy]; obj; obj = obj->nexthere) { if (obj == uchain) return BCPOS_CHAIN; @@ -617,7 +617,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) \ && door_is_closed(&levl[x][y]))) /* @@ -642,7 +642,7 @@ drag_ball(coordxy x, coordxy y, int *bc_control, already_in_rock = FALSE; switch (dist2(x, y, uball->ox, uball->oy)) { - /* two spaces diagonal from ball, move chain inbetween */ + /* two spaces diagonal from ball, move chain in-between */ case 8: *chainx = (uball->ox + x) / 2; *chainy = (uball->oy + y) / 2; @@ -751,7 +751,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 */ @@ -878,8 +879,6 @@ drag_ball(coordxy x, coordxy y, int *bc_control, return TRUE; } -DISABLE_WARNING_FORMAT_NONLITERAL - /* * drop_ball() * @@ -900,7 +899,7 @@ drop_ball(coordxy x, coordxy y) } if (x != u.ux || y != u.uy) { - static const char *const pullmsg = "The ball pulls you out of the %s!"; + static const char pullmsg[] = "The ball pulls you out of the "; struct trap *t; long side; @@ -908,20 +907,20 @@ drop_ball(coordxy x, coordxy y) && u.utraptype != TT_INFLOOR && u.utraptype != TT_BURIEDBALL) { switch (u.utraptype) { case TT_PIT: - pline(pullmsg, "pit"); + pline("%s%s!", pullmsg, "pit"); break; case TT_WEB: - pline(pullmsg, "web"); + pline("%s%s!", pullmsg, "web"); Soundeffect(se_destroy_web, 30); pline_The("web is destroyed!"); deltrap(t_at(u.ux, u.uy)); break; case TT_LAVA: - pline(pullmsg, hliquid("lava")); + pline("%s%s!", pullmsg, hliquid("lava")); break; case TT_BEARTRAP: side = rn2(3) ? LEFT_SIDE : RIGHT_SIDE; - pline(pullmsg, "bear trap"); + pline("%s%s!", pullmsg, "bear trap"); set_wounded_legs(side, rn1(1000, 500)); if (!u.usteed) { Your("%s %s is severely damaged.", @@ -950,7 +949,7 @@ drop_ball(coordxy x, coordxy y) u.ux = x - u.dx; u.uy = y - u.dy; } - gv.vision_full_recalc = 1; /* hero has moved, recalculate vision later */ + gv.vision_full_recalc = 1; /* hero has moved, recalc vision later */ if (Blind) { /* drop glyph under the chain */ @@ -971,10 +970,8 @@ drop_ball(coordxy x, coordxy y) } } -RESTORE_WARNING_FORMAT_NONLITERAL - /* ball&chain cause hero to randomly lose stuff from inventory */ -static void +staticfn void litter(void) { struct obj *otmp, *nextobj = 0; diff --git a/src/bones.c b/src/bones.c index 4bfde135fc..d1d6076fc5 100644 --- a/src/bones.c +++ b/src/bones.c @@ -1,18 +1,19 @@ -/* NetHack 3.7 bones.c $NHDT-Date: 1654931350 2022/06/11 07:09:10 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.119 $ */ +/* NetHack 3.7 bones.c $NHDT-Date: 1701500709 2023/12/02 07:05:09 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.129 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985,1993. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" -static boolean no_bones_level(d_level *); -static void goodfruit(int); -static void resetobjs(struct obj *, boolean); -static void give_to_nearby_mon(struct obj *, coordxy, coordxy); -static boolean fixuporacle(struct monst *); -static void remove_mon_from_bones(struct monst *); +staticfn boolean no_bones_level(d_level *); +staticfn void goodfruit(int); +staticfn void resetobjs(struct obj *, boolean); +staticfn void give_to_nearby_mon(struct obj *, coordxy, coordxy) NONNULLARG1; +staticfn boolean fixuporacle(struct monst *) NONNULLARG1; +staticfn void remove_mon_from_bones(struct monst *) NONNULLARG1; +staticfn void set_ghostly_objlist(struct obj *objchain); -static boolean +staticfn boolean no_bones_level(d_level *lev) { s_level *sptr; @@ -21,7 +22,7 @@ no_bones_level(d_level *lev) assign_level(lev, &gs.save_dlevel); return (boolean) (((sptr = Is_special(lev)) != 0 && !sptr->boneid) - || !gd.dungeons[lev->dnum].boneid + || !svd.dungeons[lev->dnum].boneid /* no bones on the last or multiway branch levels in any dungeon (level 1 isn't multiway) */ || Is_botlevel(lev) @@ -35,7 +36,7 @@ no_bones_level(d_level *lev) * ID is positive instead of negative). This way, when we later save the * chain of fruit types, we know to only save the types that exist. */ -static void +staticfn void goodfruit(int id) { struct fruit *f = fruit_from_indx(-id); @@ -44,7 +45,7 @@ goodfruit(int id) f->fid = id; } -static void +staticfn void resetobjs(struct obj *ochain, boolean restore) { struct obj *otmp, *nobj; @@ -98,7 +99,7 @@ resetobjs(struct obj *ochain, boolean restore) result depends upon hero's location */ && inside_shop(ox, oy) && *(p = in_rooms(ox, oy, SHOPBASE)) - && tended_shop(&gr.rooms[*p - ROOMOFFSET])); + && tended_shop(&svr.rooms[*p - ROOMOFFSET])); } if (otmp->otyp == THIEFSTONE && thiefstone_ledger_valid(otmp)) { @@ -116,9 +117,10 @@ 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->was_thrown = 0; + otmp->how_lost = LOST_NONE; /* strip user-supplied names */ /* Statue and some corpse names are left intact, @@ -150,7 +152,7 @@ resetobjs(struct obj *ochain, boolean restore) otmp->spe = 0; /* not "laid by you" in next game */ } else if (otmp->otyp == TIN) { /* make tins of unique monster's meat be empty */ - if (otmp->corpsenm >= LOW_PM + if (ismnum(otmp->corpsenm) && unique_corpstat(&mons[otmp->corpsenm])) otmp->corpsenm = NON_PM; } else if (otmp->otyp == CORPSE || otmp->otyp == STATUE) { @@ -259,7 +261,7 @@ sanitize_name(char *namebuf) /* Give object to a random object-liking monster on or adjacent to x,y but skipping hero's location. If no such monster, place object on floor at x,y. */ -static void +staticfn void give_to_nearby_mon(struct obj *otmp, coordxy x, coordxy y) { struct monst *mtmp; @@ -344,7 +346,7 @@ drop_upon_death( /* possibly restore oracle's room and/or put her back inside it; returns False if she's on the wrong level and should be removed, True otherwise */ -static boolean +staticfn boolean fixuporacle(struct monst *oracle) { coord cc; @@ -359,7 +361,7 @@ fixuporacle(struct monst *oracle) oracle->mpeaceful = 1; /* for behavior toward next character */ o_ridx = levl[oracle->mx][oracle->my].roomno - ROOMOFFSET; - if (o_ridx >= 0 && gr.rooms[o_ridx].rtype == DELPHI) + if (o_ridx >= 0 && svr.rooms[o_ridx].rtype == DELPHI) return TRUE; /* no fixup needed */ /* @@ -370,14 +372,14 @@ fixuporacle(struct monst *oracle) */ /* find original delphi chamber; should always succeed */ - for (ridx = 0; ridx < SIZE(gr.rooms); ++ridx) - if (gr.rooms[ridx].orig_rtype == DELPHI) + for (ridx = 0; ridx < SIZE(svr.rooms); ++ridx) + if (svr.rooms[ridx].orig_rtype == DELPHI) break; - if (o_ridx != ridx && ridx < SIZE(gr.rooms)) { - /* room found and she's not not in it, so try to move her there */ - cc.x = (gr.rooms[ridx].lx + gr.rooms[ridx].hx) / 2; - cc.y = (gr.rooms[ridx].ly + gr.rooms[ridx].hy) / 2; + if (o_ridx != ridx && ridx < SIZE(svr.rooms)) { + /* room found and she's not in it, so try to move her there */ + cc.x = (svr.rooms[ridx].lx + svr.rooms[ridx].hx) / 2; + cc.y = (svr.rooms[ridx].ly + svr.rooms[ridx].hy) / 2; if (enexto(&cc, cc.x, cc.y, oracle->data)) { rloc_to(oracle, cc.x, cc.y); o_ridx = levl[oracle->mx][oracle->my].roomno - ROOMOFFSET; @@ -387,7 +389,7 @@ fixuporacle(struct monst *oracle) same as used to happen before this fixup was introduced] */ } if (ridx == o_ridx) /* if she's in her room, mark it as such */ - gr.rooms[ridx].rtype = DELPHI; + svr.rooms[ridx].rtype = DELPHI; return TRUE; /* keep oracle in new bones file */ } @@ -396,7 +398,7 @@ fixuporacle(struct monst *oracle) boolean can_make_bones(void) { - register struct trap *ttmp; + struct trap *ttmp; if (!flags.bones || Polyinit_mode) return FALSE; @@ -410,7 +412,7 @@ can_make_bones(void) if (is_open_air(u.ux, u.uy)) return FALSE; /* possessions would all fall to another level; rest of this level probably isn't very interesting as bones */ - if (gl.level.flags.visited_after_event) + if (svl.level.flags.visited_after_event) return FALSE; /* the level has probably been transformed in some way that someone else shouldn't get when seeing it for the first time */ @@ -428,17 +430,16 @@ can_make_bones(void) return TRUE; } -/* monster removed before saving a bones file, +/* monster might need to be removed before saving a bones file, in case these characters are not in their home bases */ -static void +staticfn void remove_mon_from_bones(struct monst *mtmp) { struct permonst *mptr = mtmp->data; if (mtmp->iswiz || mptr == &mons[PM_MEDUSA] || mptr->msound == MS_NEMESIS || mptr->msound == MS_LEADER - || mptr == &mons[PM_VLAD_THE_IMPALER] - || mtmp->cham == PM_VLAD_THE_IMPALER /* in case he's vampshifted */ + || is_Vlad(mtmp) /* mptr == &mons[VLAD_THE_IMPALER] || cham == VLAD */ || (mptr == &mons[PM_ORACLE] && !fixuporacle(mtmp))) mongone(mtmp); } @@ -491,23 +492,48 @@ 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) f->fid = -f->fid; + set_ghostly_objlist(gi.invent); /* dispose of your possessions, usually cursed */ - if (u.ugrave_arise == (NON_PM - 1)) { + if (ismnum(u.ugrave_arise)) { + /* give your possessions to the monster you become */ + gi.in_mklev = TRUE; /* use as-is */ + mtmp = makemon(&mons[u.ugrave_arise], u.ux, u.uy, NO_MINVENT); + gi.in_mklev = FALSE; + if (!mtmp) { /* arise-type might have been genocided */ + drop_upon_death((struct monst *) 0, (struct obj *) 0, u.ux, u.uy); + u.ugrave_arise = NON_PM; /* in case caller cares */ + return; + } + give_u_to_m_resistances(mtmp); + mtmp = christen_monst(mtmp, svp.plname); + newsym(u.ux, u.uy); + /* ["Your body rises from the dead as an ..." used + to be given here, but it has been moved to done() so that + it gets delivered even when savebones() isn't called] */ + drop_upon_death(mtmp, (struct obj *) 0, u.ux, u.uy); + /* 'mtmp' now has hero's inventory; if 'mtmp' is a mummy, give it + a wrapping unless already carrying one */ + if (mtmp->data->mlet == S_MUMMY && !m_carrying(mtmp, MUMMY_WRAPPING)) + (void) mongets(mtmp, MUMMY_WRAPPING); + m_dowear(mtmp, TRUE); + } else if (u.ugrave_arise == LEAVESTATUE) { struct obj *otmp; /* embed your possessions in your statue */ - otmp = mk_named_object(STATUE, &mons[u.umonnum], u.ux, u.uy, gp.plname); + otmp = mk_named_object(STATUE, &mons[u.umonnum], u.ux, u.uy, + svp.plname); drop_upon_death((struct monst *) 0, otmp, u.ux, u.uy); if (!otmp) return; /* couldn't make statue */ mtmp = (struct monst *) 0; - } else if (u.ugrave_arise < LOW_PM) { + } else { /* u.ugrave_arise < LEAVESTATUE */ /* drop everything */ drop_upon_death((struct monst *) 0, (struct obj *) 0, u.ux, u.uy); /* trick makemon() into allowing monster creation @@ -518,30 +544,9 @@ savebones(int how, time_t when, struct obj *corpse) gi.in_mklev = FALSE; if (!mtmp) return; - mtmp = christen_monst(mtmp, gp.plname); + mtmp = christen_monst(mtmp, svp.plname); if (corpse) (void) obj_attach_mid(corpse, mtmp->m_id); - } else { - /* give your possessions to the monster you become */ - gi.in_mklev = TRUE; /* use as-is */ - mtmp = makemon(&mons[u.ugrave_arise], u.ux, u.uy, NO_MINVENT); - gi.in_mklev = FALSE; - if (!mtmp) { /* arise-type might have been genocided */ - drop_upon_death((struct monst *) 0, (struct obj *) 0, u.ux, u.uy); - u.ugrave_arise = NON_PM; /* in case caller cares */ - return; - } - mtmp = christen_monst(mtmp, gp.plname); - newsym(u.ux, u.uy); - /* ["Your body rises from the dead as an ..." used - to be given here, but it has been moved to done() so that - it gets delivered even when savebones() isn't called] */ - drop_upon_death(mtmp, (struct obj *) 0, u.ux, u.uy); - /* 'mtmp' now has hero's inventory; if 'mtmp' is a mummy, give it - a wrapping unless already carrying one */ - if (mtmp->data->mlet == S_MUMMY && !m_carrying(mtmp, MUMMY_WRAPPING)) - (void) mongets(mtmp, MUMMY_WRAPPING); - m_dowear(mtmp, TRUE); } if (mtmp) { mtmp->m_lev = (u.ulevel ? u.ulevel : 1); @@ -572,18 +577,23 @@ savebones(int how, time_t when, struct obj *corpse) EBONES(mtmp)->crowned = u.uevent.uhand_of_elbereth; } for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + set_ghostly_objlist(mtmp->minvent); resetobjs(mtmp->minvent, FALSE); /* do not zero out m_ids for bones levels any more */ mtmp->mlstmv = 0L; if (mtmp->mtame) mtmp->mtame = mtmp->mpeaceful = 0; + /* observations about the current hero won't apply to future game */ + mtmp->seen_resistance = M_SEEN_NOTHING; } for (ttmp = gf.ftrap; ttmp; ttmp = ttmp->ntrap) { ttmp->madeby_u = 0; ttmp->tseen = unhideable_trap(ttmp->ttyp); } + set_ghostly_objlist(fobj); resetobjs(fobj, FALSE); - resetobjs(gl.level.buriedobjlist, FALSE); + set_ghostly_objlist(svl.level.buriedobjlist); + resetobjs(svl.level.buriedobjlist, FALSE); /* Hero is no longer on the map. */ u.ux0 = u.ux, u.uy0 = u.uy; @@ -595,7 +605,7 @@ savebones(int how, time_t when, struct obj *corpse) levl[x][y].seenv = 0; levl[x][y].waslit = 0; levl[x][y].glyph = GLYPH_UNEXPLORED; - gl.lastseentyp[x][y] = 0; + svl.lastseentyp[x][y] = 0; } /* Attach bones info to the current level before saving. */ @@ -607,7 +617,7 @@ savebones(int how, time_t when, struct obj *corpse) gender and alignment reflect final values rather than what the character started out as, same as topten and logfile entries */ Sprintf(newbones->who, "%s-%.3s-%.3s-%.3s-%.3s", - gp.plname, gu.urole.filecode, + svp.plname, gu.urole.filecode, gu.urace.filecode, genders[flags.female].filecode, aligns[1 - u.ualign.type].filecode); formatkiller(newbones->how, sizeof newbones->how, how, TRUE); @@ -617,13 +627,13 @@ savebones(int how, time_t when, struct obj *corpse) newbones->bonesknown = FALSE; /* if current character died on a bones level, the cemetery list will have multiple entries, most recent (this dead hero) first */ - newbones->next = gl.level.bonesinfo; - gl.level.bonesinfo = newbones; + newbones->next = svl.level.bonesinfo; + svl.level.bonesinfo = newbones; /* flag these bones if they are being created in wizard mode; they might already be flagged as such, even when we're playing in normal mode, if this level came from a previous bones file */ if (wizard) - gl.level.flags.wizard_bones = 1; + svl.level.flags.wizard_bones = 1; nhfp = create_bonesfile(&u.uz, &bonesid, whynot); if (!nhfp) { @@ -637,7 +647,7 @@ savebones(int how, time_t when, struct obj *corpse) } c = (char) (strlen(bonesid) + 1); - nhfp->mode = WRITING | FREEING; + nhfp->mode = WRITING; store_version(nhfp); store_savefileinfo(nhfp); if (nhfp->structlevel) { @@ -659,7 +669,8 @@ getbones(void) { int ok; NHFILE *nhfp = (NHFILE *) 0; - char c = 0, *bonesid, oldbonesid[40]; /* was [10]; more should be safer */ + char c = 0, *bonesid, + oldbonesid[40] = { 0 }; /* was [10]; more should be safer */ if (discover) /* save bones files for real games */ return 0; @@ -683,7 +694,7 @@ getbones(void) return 0; } - if (validate(nhfp, gb.bones) != 0) { + if (validate(nhfp, gb.bones, FALSE) != 0) { if (!wizard) pline("Discarding unusable bones; no need to panic..."); ok = FALSE; @@ -701,8 +712,18 @@ getbones(void) string and wasn't recorded in the file */ mread(nhfp->fd, (genericptr_t) &c, sizeof c); /* length including terminating '\0' */ - mread(nhfp->fd, (genericptr_t) oldbonesid, - (unsigned) c); /* DD.nn or Qrrr.n for role rrr */ + if ((unsigned) c <= sizeof oldbonesid) { + mread(nhfp->fd, (genericptr_t) oldbonesid, + (unsigned) c); /* DD.nn or Qrrr.n for role rrr */ + } else { + if (wizard) + debugpline2("Abandoning bones , %u > %u.", + (unsigned) c, (unsigned) sizeof oldbonesid); + close_nhfile(nhfp); + compress_bonesfile(); + /* ToDo: maybe unlink these problematic bones? */ + return 0; + } } if (strcmp(bonesid, oldbonesid) != 0) { char errbuf[BUFSZ]; @@ -715,7 +736,7 @@ getbones(void) } trickery(errbuf); } else { - register struct monst *mtmp; + struct monst *mtmp; getlev(nhfp, 0, 0); @@ -740,7 +761,7 @@ getbones(void) resetobjs(mtmp->minvent, TRUE); } resetobjs(fobj, TRUE); - resetobjs(gl.level.buriedobjlist, TRUE); + resetobjs(svl.level.buriedobjlist, TRUE); fix_shop_damage(); } } @@ -793,7 +814,7 @@ bones_include_name(const char *name) Strcat(buf, "-"); len = strlen(buf); - for (bp = gl.level.bonesinfo; bp; bp = bp->next) { + for (bp = svl.level.bonesinfo; bp; bp = bp->next) { if (!strncmp(bp->who, buf, len)) return TRUE; } @@ -801,4 +822,39 @@ bones_include_name(const char *name) return FALSE; } +/* set the ghostly bit in a list of objects */ +staticfn void +set_ghostly_objlist(struct obj *objchain) +{ + while (objchain) { + objchain->ghostly = 1; + objchain = objchain->nobj; + } +} + +/* This is called when a marked object from a bones file is picked-up. + Some could result in a message, and the obj->ghostly flag is always + cleared. obj->ghostly has no other usage at this time. */ +void +fix_ghostly_obj(struct obj *obj) +{ + if (!obj->ghostly) + return; + switch(obj->otyp) { + /* asymmetrical weapons */ + case BOW: + case ELVEN_BOW: + case ORCISH_BOW: + case YUMI: + case BOOMERANG: + You("make adjustments to %s to suit your %s hand.", + the(xname(obj)), + URIGHTY ? "right" : "left"); + break; + default: + break; + } + obj->ghostly = 0; +} + /*bones.c*/ diff --git a/src/botl.c b/src/botl.c index 2caf208430..15e438814f 100644 --- a/src/botl.c +++ b/src/botl.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 botl.c $NHDT-Date: 1646171622 2022/03/01 21:53:42 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.209 $ */ +/* NetHack 3.7 botl.c $NHDT-Date: 1720397739 2024/07/08 00:15:39 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.264 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -10,25 +10,21 @@ extern const char *const hu_stat[]; /* defined in eat.c */ +/* also used in insight.c */ const char *const enc_stat[] = { "", "Burdened", "Stressed", "Strained", "Overtaxed", "Overloaded" }; -static const char *rank(void); -static void bot_via_windowport(void); -static void stat_update_time(void); -#ifdef STATUS_HILITES -static unsigned long query_conditions(void); -static boolean status_hilite_remove(int); -static boolean status_hilite_menu_fld(int); -static void status_hilites_viewall(void); -#endif +staticfn const char *rank(void); +staticfn void bot_via_windowport(void); +staticfn void stat_update_time(void); +staticfn char *get_strength_str(void); /* limit of the player's name in the status window */ #define BOTL_NSIZ 16 -static char * +staticfn char * get_strength_str(void) { static char buf[32]; @@ -59,13 +55,13 @@ char * do_statusline1(void) { static char newbot1[BUFSZ]; - register char *nb; - register int i, j; + char *nb; + int i, j; if (suppress_map_output()) return strcpy(newbot1, ""); - Strcpy(newbot1, gp.plname); + Strcpy(newbot1, svp.plname); if ('a' <= newbot1[0] && newbot1[0] <= 'z') newbot1[0] += 'A' - 'a'; newbot1[BOTL_NSIZ] = '\0'; @@ -83,8 +79,9 @@ do_statusline1(void) k++; } Strcpy(nb = eos(nb), mbot); - } else + } else { Strcpy(nb = eos(nb), rank()); + } Sprintf(nb = eos(nb), " "); i = gm.mrank_sz + 15; @@ -97,9 +94,9 @@ do_statusline1(void) ACURR(A_DEX), ACURR(A_CON), ACURR(A_INT), ACURR(A_WIS), ACURR(A_CHA)); Sprintf(nb = eos(nb), "%s", - (u.ualign.type == A_CHAOTIC) - ? " Chaotic" - : (u.ualign.type == A_NEUTRAL) ? " Neutral" : " Lawful"); + (u.ualign.type == A_CHAOTIC) ? " Chaotic" + : (u.ualign.type == A_NEUTRAL) ? " Neutral" + : " Lawful"); #ifdef SCORE_ON_BOTL if (flags.showscore) Sprintf(nb = eos(nb), " S:%ld", botl_score()); @@ -114,9 +111,10 @@ do_statusline2(void) /* dungeon location (and gold), hero health (HP, PW, AC), experience (HD if poly'd, else Exp level and maybe Exp points), time (in moves), varying number of status conditions */ - dloc[QBUFSZ], hlth[QBUFSZ], expr[QBUFSZ], tmmv[QBUFSZ], cond[QBUFSZ]; - register char *nb; - size_t dln, dx, hln, xln, tln, cln; + dloc[QBUFSZ], hlth[QBUFSZ], expr[QBUFSZ], + tmmv[QBUFSZ], cond[QBUFSZ], vers[QBUFSZ]; + char *nb; + size_t dln, dx, hln, xln, tln, cln, vrn; int hp, hpmax, cap; long money; @@ -160,12 +158,12 @@ do_statusline2(void) else if (flags.showexp) Sprintf(expr, "Xp:%d/%-1ld", u.ulevel, u.uexp); else - Sprintf(expr, "Exp:%d", u.ulevel); + Sprintf(expr, "Xp:%d", u.ulevel); xln = strlen(expr); /* time/move counter */ if (flags.time) - Sprintf(tmmv, "T:%ld", gm.moves); + Sprintf(tmmv, "T:%ld", svm.moves); else tmmv[0] = '\0'; tln = strlen(tmmv); @@ -217,6 +215,13 @@ do_statusline2(void) Strcpy(nb = eos(nb), " Ride"); cln = strlen(cond); + /* version on status line, with leading space */ + if (flags.showvers) + (void) status_version(vers, sizeof vers, TRUE); + else + vers[0] = '\0'; + vrn = strlen(vers); + /* * Put the pieces together. If they all fit, keep the traditional * sequence. Otherwise, move least important parts to the end in @@ -229,22 +234,24 @@ do_statusline2(void) * wider displays can still show wider status than the map if the * interface supports that. */ - if ((dln - dx) + 1 + hln + 1 + xln + 1 + tln + 1 + cln <= COLNO) { - Snprintf(newbot2, sizeof(newbot2), "%s %s %s %s %s", dloc, hlth, expr, - tmmv, cond); + if ((dln - dx) + 1 + hln + 1 + xln + 1 + tln + 1 + cln + vrn <= COLNO) { + Snprintf(newbot2, sizeof newbot2, "%s %s %s %s %s%s", dloc, hlth, + expr, tmmv, cond, vers); } else { - if (dln + 1 + hln + 1 + xln + 1 + tln + 1 + cln + 1 > MAXCO) { + if (dln + 1 + hln + 1 + xln + 1 + tln + 1 + cln + vrn > MAXCO) { panic("bot2: second status line exceeds MAXCO (%u > %d)", - (unsigned)(dln + 1 + hln + 1 + xln + 1 + tln + 1 + cln + 1), MAXCO); + (unsigned) (dln + 1 + hln + 1 + xln + 1 + tln + 1 + cln + + vrn), + MAXCO); } else if ((dln - dx) + 1 + hln + 1 + xln + 1 + cln <= COLNO) { - Snprintf(newbot2, sizeof(newbot2), "%s %s %s %s %s", dloc, hlth, - expr, cond, tmmv); + Snprintf(newbot2, sizeof newbot2, "%s %s %s %s %s%s", dloc, hlth, + expr, cond, tmmv, vers); } else if ((dln - dx) + 1 + hln + 1 + cln <= COLNO) { - Snprintf(newbot2, sizeof(newbot2), "%s %s %s %s %s", dloc, hlth, - cond, expr, tmmv); + Snprintf(newbot2, sizeof newbot2, "%s %s %s %s %s%s", dloc, hlth, + cond, expr, tmmv, vers); } else { - Snprintf(newbot2, sizeof(newbot2), "%s %s %s %s %s", hlth, cond, - dloc, expr, tmmv); + Snprintf(newbot2, sizeof newbot2, "%s %s %s %s %s%s", hlth, cond, + dloc, expr, tmmv, vers); } /* only two or three consecutive spaces available to squeeze out */ mungspaces(newbot2); @@ -255,6 +262,8 @@ do_statusline2(void) void bot(void) { + if (gb.bot_disabled) + return; /* dosave() flags completion by setting u.uhp to -1; suppress_map_output() covers program_state.restoring and is used for status as well as map */ if (u.uhp != -1 && gy.youmonst.data @@ -268,15 +277,17 @@ bot(void) putmixed(WIN_STATUS, 0, do_statusline2()); } } - gc.context.botl = gc.context.botlx = iflags.time_botl = FALSE; + disp.botl = disp.botlx = disp.time_botl = FALSE; } /* special purpose status update: move counter ('time' status) only */ void timebot(void) { - /* we're called when iflags.time_botl is set and general gc.context.botl - is clear; iflags.time_botl gets set whenever gm.moves changes value + if (gb.bot_disabled) + return; + /* we're called when disp.time_botl is set and general disp.botl + is clear; disp.time_botl gets set whenever svm.moves changes value so there's no benefit in tracking previous value to decide whether to skip update; suppress_map_output() handles program_state.restoring and program_state.done_hup (tty hangup => no further output at all) @@ -289,7 +300,7 @@ timebot(void) bot(); } } - iflags.time_botl = FALSE; + disp.time_botl = FALSE; } /* convert experience level (1..30) to rank index (0..8) */ @@ -330,8 +341,8 @@ rank_to_xlev(int rank) const char * rank_of(int lev, short monnum, boolean female) { - register const struct Role *role; - register int i; + const struct Role *role; + int i; /* Find the role */ for (role = roles; role->name.m; role++) @@ -356,16 +367,19 @@ rank_of(int lev, short monnum, boolean female) return "Player"; } -static const char * +staticfn const char * rank(void) { return rank_of(u.ulevel, Role_switch, flags.female); } int -title_to_mon(const char *str, int *rank_indx, int *title_length) +title_to_mon( + const char *str, + int *rank_indx, + int *title_length) { - register int i, j; + int i, j; /* Loop through each of the roles */ for (i = 0; roles[i].name.m; i++) { @@ -397,8 +411,9 @@ title_to_mon(const char *str, int *rank_indx, int *title_length) void max_rank_sz(void) { - register int i; + int i; size_t r, maxr = 0; + for (i = 0; i < 9; i++) { if (gu.urole.rank[i].m && (r = strlen(gu.urole.rank[i].m)) > maxr) maxr = r; @@ -414,17 +429,20 @@ long botl_score(void) { long deepest = deepest_lev_reached(FALSE); - long utotal; + long umoney, depthbonus; /* hidden_gold(False): only gold in containers whose contents are known */ - utotal = money_cnt(gi.invent) + hidden_gold(FALSE); - if ((utotal -= u.umoney0) < 0L) - utotal = 0L; - utotal += u.urexp + (50 * (deepest - 1)) - + (deepest > 30 ? 10000 : deepest > 20 ? 1000 * (deepest - 20) : 0); - if (utotal < u.urexp) - utotal = LONG_MAX; /* wrap around */ - return utotal; + umoney = money_cnt(gi.invent) + hidden_gold(FALSE); + /* 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; + /* neither umoney nor depthbonus can grow unusually big (gold due to + weight); u.urexp might */ + return nowrap_add(u.urexp, umoney + depthbonus); } #endif /* SCORE_ON_BOTL */ @@ -439,7 +457,7 @@ describe_level( int ret = 1; if (Is_knox(&u.uz)) { - Sprintf(buf, "%s", gd.dungeons[u.uz.dnum].dname); + Sprintf(buf, "%s", svd.dungeons[u.uz.dnum].dname); addbranch = FALSE; } else if (In_quest(&u.uz)) { Sprintf(buf, "Home %d", dunlev(&u.uz)); @@ -452,13 +470,14 @@ describe_level( } else { /* ports with more room may expand this one */ if (!addbranch) - Sprintf(buf, "Dlvl:%-2d", depth(&u.uz)); + Sprintf(buf, "%s:%-2d", /* "Dlvl:n" (grep fodder) */ + In_tutorial(&u.uz) ? "Tutorial" : "Dlvl", depth(&u.uz)); else Sprintf(buf, "level %d", depth(&u.uz)); ret = 0; } if (addbranch) { - Sprintf(eos(buf), ", %s", gd.dungeons[u.uz.dnum].dname); + Sprintf(eos(buf), ", %s", svd.dungeons[u.uz.dnum].dname); (void) strsubst(buf, "The ", "the "); } if (addspace) @@ -475,48 +494,53 @@ describe_level( #ifdef STATUS_HILITES #endif /* STATUS_HILITES */ -static boolean eval_notify_windowport_field(int, boolean *, int); -static void evaluate_and_notify_windowport(boolean *, int); -static void init_blstats(void); -static int compare_blstats(struct istat_s *, struct istat_s *); -static char *anything_to_s(char *, anything *, int); -static int percentage(struct istat_s *, struct istat_s *); -static int exp_percentage(void); -static int QSORTCALLBACK cond_cmp(const genericptr, const genericptr); -static int QSORTCALLBACK menualpha_cmp(const genericptr, const genericptr); +staticfn boolean eval_notify_windowport_field(int, boolean *, int); +staticfn void evaluate_and_notify_windowport(boolean *, int); +staticfn void init_blstats(void); +staticfn int compare_blstats(struct istat_s *, struct istat_s *); +staticfn char *anything_to_s(char *, anything *, int); +staticfn int percentage(struct istat_s *, struct istat_s *); +staticfn int exp_percentage(void); +staticfn int QSORTCALLBACK cond_cmp(const genericptr, const genericptr); +staticfn int QSORTCALLBACK menualpha_cmp(const genericptr, const genericptr); #ifdef STATUS_HILITES -static void s_to_anything(anything *, char *, int); -static enum statusfields fldname_to_bl_indx(const char *); -static boolean hilite_reset_needed(struct istat_s *, long); -static boolean noneoftheabove(const char *); -static struct hilite_s *get_hilite(int, int, genericptr_t, int, int, int *); -static void split_clridx(int, int *, int *); -static boolean is_ltgt_percentnumber(const char *); -static boolean has_ltgt_percentnumber(const char *); -static int splitsubfields(char *, char ***, int); -static boolean is_fld_arrayvalues(const char *, const char *const *, int, int, - int *); -static int query_arrayvalue(const char *, const char *const *, int, int); -static void status_hilite_add_threshold(int, struct hilite_s *); -static boolean parse_status_hl2(char (*)[QBUFSZ], boolean); -static char *conditionbitmask2str(unsigned long); -static unsigned long match_str2conditionbitmask(const char *); -static unsigned long str2conditionbitmask(char *); -static boolean parse_condition(char (*)[QBUFSZ], int); -static char *hlattr2attrname(int, char *, size_t); -static void status_hilite_linestr_add(int, struct hilite_s *, unsigned long, - const char *); -static void status_hilite_linestr_done(void); -static int status_hilite_linestr_countfield(int); -static void status_hilite_linestr_gather_conditions(void); -static void status_hilite_linestr_gather(void); -static char *status_hilite2str(struct hilite_s *); -static int status_hilite_menu_choose_field(void); -static int status_hilite_menu_choose_behavior(int); -static int status_hilite_menu_choose_updownboth(int, const char *, boolean, - boolean); -static boolean status_hilite_menu_add(int); +staticfn void s_to_anything(anything *, char *, int); +staticfn enum statusfields fldname_to_bl_indx(const char *); +staticfn boolean hilite_reset_needed(struct istat_s *, long); +staticfn boolean noneoftheabove(const char *); +staticfn struct hilite_s *get_hilite(int, int, genericptr_t, int, int, int *); +staticfn void split_clridx(int, int *, int *); +staticfn boolean is_ltgt_percentnumber(const char *); +staticfn boolean has_ltgt_percentnumber(const char *); +staticfn int splitsubfields(char *, char ***, int); +staticfn boolean is_fld_arrayvalues(const char *, const char *const *, + int, int, int *); +staticfn int query_arrayvalue(const char *, const char *const *, int, int); +staticfn void status_hilite_add_threshold(int, struct hilite_s *); +staticfn boolean parse_status_hl2(char (*)[QBUFSZ], boolean); +staticfn unsigned long query_conditions(void); +staticfn char *conditionbitmask2str(unsigned long); +staticfn unsigned long match_str2conditionbitmask(const char *); +staticfn unsigned long str2conditionbitmask(char *); +staticfn boolean parse_condition(char (*)[QBUFSZ], int); +staticfn char *hlattr2attrname(int, char *, size_t); +staticfn void status_hilite_linestr_add(int, struct hilite_s *, unsigned long, + const char *); +staticfn void status_hilite_linestr_done(void); +staticfn int status_hilite_linestr_countfield(int); +staticfn void status_hilite_linestr_gather_conditions(void); +staticfn void status_hilite_linestr_gather(void); +staticfn char *status_hilite2str(struct hilite_s *); +staticfn int status_hilite_menu_choose_field(void); +staticfn int status_hilite_menu_choose_behavior(int); +staticfn int status_hilite_menu_choose_updownboth(int, const char *, boolean, + boolean); +staticfn boolean status_hilite_menu_add(int); +staticfn boolean status_hilite_remove(int); +staticfn boolean status_hilite_menu_fld(int); +staticfn void status_hilites_viewall(void); + #define has_hilite(i) (gb.blstats[0][(i)].thresholds) /* TH_UPDOWN encompasses specific 'up' and 'down' also general 'changed' */ #define Is_Temp_Hilite(rule) ((rule) && (rule)->behavior == BL_TH_UPDOWN) @@ -529,12 +553,12 @@ static boolean status_hilite_menu_add(int); #define INIT_BLSTAT(name, fmtstr, anytyp, wid, fld) \ { name, fmtstr, 0L, FALSE, FALSE, 0, anytyp, \ - { (genericptr_t) 0 }, (char *) 0, \ - wid, -1, fld INIT_THRESH } + { (genericptr_t) 0 }, { (genericptr_t) 0 }, (char *) 0, \ + wid, -1, fld INIT_THRESH } #define INIT_BLSTATP(name, fmtstr, anytyp, wid, maxfld, fld) \ { name, fmtstr, 0L, FALSE, TRUE, 0, anytyp, \ - { (genericptr_t) 0 }, (char *) 0, \ - wid, maxfld, fld INIT_THRESH } + { (genericptr_t) 0 }, { (genericptr_t) 0 }, (char *) 0, \ + wid, maxfld, fld INIT_THRESH } /* If entries are added to this, botl.h will require updating too. 'max' value of BL_EXP gets special handling since the percentage @@ -563,7 +587,12 @@ static struct istat_s initblstats[MAXBLSTATS] = { INIT_BLSTAT("hitpoints-max", "(%s)", ANY_INT, 10, BL_HPMAX), INIT_BLSTAT("dungeon-level", "%s", ANY_STR, MAXVALWIDTH, BL_LEVELDESC), INIT_BLSTATP("experience", "/%s", ANY_LONG, 20, BL_EXP, BL_EXP), - INIT_BLSTAT("condition", "%s", ANY_MASK32, 0, BL_CONDITION) + INIT_BLSTAT("condition", "%s", ANY_MASK32, 0, BL_CONDITION), + /* optional; once set it doesn't change unless 'showvers' option is + toggled or player modifies the 'versinfo' option; + available mostly for screenshots or someone looking over shoulder; + blstat[][BL_VERS] is actually an int copy of flags.versinfo (0...7) */ + INIT_BLSTAT("version", " %s", ANY_STR, MAXVALWIDTH, BL_VERS), }; #undef INIT_BLSTATP @@ -572,7 +601,7 @@ static struct istat_s initblstats[MAXBLSTATS] = { #ifdef STATUS_HILITES -const struct condmap condition_aliases[] = { +static const struct condmap condition_aliases[] = { { "strangled", BL_MASK_STRNGL }, { "all", BL_MASK_BAREH | BL_MASK_BLIND | BL_MASK_BUSY | BL_MASK_CONF | BL_MASK_DEAF | BL_MASK_ELF_IRON @@ -603,6 +632,7 @@ const struct condmap condition_aliases[] = { #endif /* STATUS_HILITES */ +/* condition names and their abbreviations are used by windowport code */ const struct conditions_t conditions[] = { /* ranking, mask, identifier, txt1, txt2, txt3 */ { 20, BL_MASK_BAREH, bl_bareh, { "Bare", "Bar", "Bh" } }, @@ -639,7 +669,8 @@ const struct conditions_t conditions[] = { }; struct condtests_t condtests[CONDITION_COUNT] = { - /* id, useropt, opt_in or out, enabled, configchoice, testresult */ + /* id, useropt, opt_in or out, enabled, configchoice, testresult; + default value for enabled is !opt_in but can get changed via options */ { bl_bareh, "barehanded", opt_in, FALSE, FALSE, FALSE }, { bl_blind, "blind", opt_out, TRUE, FALSE, FALSE }, { bl_busy, "busy", opt_in, FALSE, FALSE, FALSE }, @@ -680,7 +711,7 @@ static boolean cache_avail[3] = { FALSE, FALSE, FALSE }; static boolean cache_reslt[3] = { FALSE, FALSE, FALSE }; static const char *cache_nomovemsg = NULL, *cache_multi_reason = NULL; -#define cond_cache_prepA() \ +#define cond_cache_prepA() \ do { \ boolean clear_cache = FALSE, refresh_cache = FALSE; \ \ @@ -718,12 +749,12 @@ do { \ * without STATUS_HILITES. */ -static void +staticfn void bot_via_windowport(void) { char buf[BUFSZ]; const char *titl; - register char *nb; + char *nb; int i, idx, cap; long money; @@ -746,7 +777,7 @@ bot_via_windowport(void) /* * Player name and title. */ - Strcpy(nb = buf, gp.plname); + Strcpy(nb = buf, svp.plname); nb[0] = highc(nb[0]); titl = !Upolyd ? rank() : pmname(&mons[u.umonnum], Ugender); i = (int) (strlen(buf) + sizeof " the " + strlen(titl) - sizeof ""); @@ -795,10 +826,12 @@ bot_via_windowport(void) /* Hit points */ i = Upolyd ? u.mh : u.uhp; - if (i < 0) + if (i < 0) /* gameover sets u.uhp to -1 */ i = 0; + gb.blstats[idx][BL_HP].rawval.a_int = i; gb.blstats[idx][BL_HP].a.a_int = min(i, 9999); i = Upolyd ? u.mhmax : u.uhpmax; + gb.blstats[idx][BL_HPMAX].rawval.a_int = i; gb.blstats[idx][BL_HPMAX].a.a_int = min(i, 9999); /* Dungeon level. */ @@ -808,6 +841,7 @@ bot_via_windowport(void) /* Gold */ if ((money = money_cnt(gi.invent)) < 0L) money = 0L; /* ought to issue impossible() and then discard gold */ + gb.blstats[idx][BL_GOLD].rawval.a_long = money; gb.blstats[idx][BL_GOLD].a.a_long = min(money, 999999L); /* * The tty port needs to display the current symbol for gold @@ -831,7 +865,9 @@ bot_via_windowport(void) gv.valset[BL_GOLD] = TRUE; /* indicate val already set */ /* Power (magical energy) */ + gb.blstats[idx][BL_ENE].rawval.a_int = u.uen; gb.blstats[idx][BL_ENE].a.a_int = min(u.uen, 9999); + gb.blstats[idx][BL_ENEMAX].rawval.a_int = u.uenmax; gb.blstats[idx][BL_ENEMAX].a.a_int = min(u.uenmax, 9999); /* Armor class */ @@ -845,7 +881,7 @@ bot_via_windowport(void) gb.blstats[idx][BL_EXP].a.a_long = u.uexp; /* Time (moves) */ - gb.blstats[idx][BL_TIME].a.a_long = gm.moves; + gb.blstats[idx][BL_TIME].a.a_long = svm.moves; /* Hunger */ /* note: u.uhs is unsigned, and 3.6.1's STATUS_HILITE defined @@ -864,20 +900,40 @@ bot_via_windowport(void) (cap > UNENCUMBERED) ? enc_stat[cap] : ""); gv.valset[BL_CAP] = TRUE; + /* Version; unchanging unless player toggles 'showvers' option or + modifies 'versinfo' option; toggling showvers off will clear it */ + if (gb.blstats[idx][BL_VERS].a.a_int != (int) flags.versinfo) { + gb.blstats[idx][BL_VERS].a.a_int = (int) flags.versinfo; + gv.valset[BL_VERS] = FALSE; + } + if (!gv.valset[BL_VERS]) { + (void) status_version(gb.blstats[idx][BL_VERS].val, + gb.blstats[idx][BL_VERS].valwidth, FALSE); + gv.valset[BL_VERS] = TRUE; + } + /* Conditions */ gb.blstats[idx][BL_CONDITION].a.a_ulong = 0L; - /* avoid anything that does string comparisons in here because this - is called *extremely* often, for every screen update and the same - string comparisons would be repeated, thus contributing toward - performance degradation. If it is essential that string comparisons - are needed for a particular condition, consider adding a caching - mechanism to limit the string comparisons to the first occurrence - for that cache lifetime. There is caching of that nature done for - unconsc (1) and parlyz (2) because the suggested way of being able - to distinguish unconsc, parlyz, sleeping, and busy involves multiple - string comparisons. */ + /* + * Avoid anything that does string comparisons in here because this + * is called *extremely* often, for every screen update and the same + * string comparisons would be repeated, thus contributing toward + * performance degradation. If it is essential that string comparisons + * are needed for a particular condition, consider adding a caching + * mechanism to limit the string comparisons to the first occurrence + * for that cache lifetime. There is caching of that nature done for + * unconsc (1) and parlyz (2) because the suggested way of being able + * to distinguish unconsc, parlyz, sleeping, and busy involves multiple + * string comparisons. + * + * [Rebuttal: it's called a lot for Windows and MS-DOS because their + * sample run-time configuration file enables 'time' (move counter). + * The optimization to bypass full status update when only 'time' + * has changed (via timebot(), only effective for VIA_WINDOWPORT() + * configurations) should ameliorate that.] + */ #define test_if_enabled(c) if (condtests[(c)].enabled) condtests[(c)].test @@ -970,28 +1026,32 @@ bot_via_windowport(void) condtests[bl_sleeping].test = condtests[bl_busy].test = FALSE; } -#define cond_bitset(c) \ - gb.blstats[idx][BL_CONDITION].a.a_ulong |= conditions[(c)].mask; +#define cond_setbit(c) \ + gb.blstats[idx][BL_CONDITION].a.a_ulong |= conditions[(c)].mask for (i = 0; i < CONDITION_COUNT; ++i) { if (condtests[i].enabled /* && i != bl_holding */ /* uncomment to suppress UHold */ && condtests[i].test) - cond_bitset(i); + cond_setbit(i); } +#undef cond_bitset + evaluate_and_notify_windowport(gv.valset, idx); +#undef test_if_enabled } +#undef cond_cache_prepA /* update just the status lines' 'time' field */ -static void +staticfn void stat_update_time(void) { int idx = gn.now_or_before_idx; /* no 0/1 toggle */ int fld = BL_TIME; /* Time (moves) */ - gb.blstats[idx][fld].a.a_long = gm.moves; + gb.blstats[idx][fld].a.a_long = svm.moves; gv.valset[fld] = FALSE; eval_notify_windowport_field(fld, gv.valset, idx); @@ -1027,13 +1087,12 @@ condopt(int idx, boolean *addr, boolean negated) condtests[idx].enabled = negated ? FALSE : TRUE; condtests[idx].choice = condtests[idx].enabled; /* avoid lingering false positives if test is no longer run */ - if (!condtests[idx].enabled) - condtests[idx].test = FALSE; + condtests[idx].test = FALSE; } } /* qsort callback routine for sorting the condition index */ -static int QSORTCALLBACK +staticfn int QSORTCALLBACK cond_cmp(const genericptr vptr1, const genericptr vptr2) { int indx1 = *(int *) vptr1, indx2 = *(int *) vptr2, @@ -1046,7 +1105,7 @@ cond_cmp(const genericptr vptr1, const genericptr vptr2) } /* qsort callback routine for alphabetical sorting of index */ -static int QSORTCALLBACK +staticfn int QSORTCALLBACK menualpha_cmp(const genericptr vptr1, const genericptr vptr2) { int indx1 = *(int *) vptr1, indx2 = *(int *) vptr2; @@ -1074,10 +1133,14 @@ parse_cond_option(boolean negated, char *opts) return 1; /* !0 indicates error */ } -void +/* display a menu of all available status condition options and let player + toggled them on or off; returns True iff any changes are made */ +boolean cond_menu(void) { - static const char *const menutitle[2] = { "alphabetically", "by ranking"}; + static const char *const menutitle[2] = { + "alphabetically", "by ranking" + }; int i, res, idx = 0; int sequence[CONDITION_COUNT]; winid tmpwin; @@ -1085,7 +1148,8 @@ cond_menu(void) menu_item *picks = (menu_item *) 0; char mbuf[QBUFSZ]; boolean showmenu = TRUE; - int clr = 0; + int clr = NO_COLOR; + boolean changed = FALSE; do { for (i = 0; i < CONDITION_COUNT; ++i) { @@ -1107,16 +1171,14 @@ cond_menu(void) clr, mbuf, MENU_ITEMFLAGS_SKIPINVERT); any = cg.zeroany; Sprintf(mbuf, "sorted %s", menutitle[gc.condmenu_sortorder]); - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - iflags.menu_headings, clr, mbuf, MENU_ITEMFLAGS_NONE); + add_menu_heading(tmpwin, mbuf); for (i = 0; i < SIZE(condtests); i++) { idx = sequence[i]; Sprintf(mbuf, "cond_%-14s", condtests[idx].useroption); any = cg.zeroany; any.a_int = idx + 2; /* avoid zero and the sort change pick */ condtests[idx].choice = FALSE; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, mbuf, + add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, mbuf, condtests[idx].enabled ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE); } @@ -1147,14 +1209,16 @@ cond_menu(void) for (i = 0; i < CONDITION_COUNT; ++i) if (condtests[i].enabled != condtests[i].choice) { condtests[i].enabled = condtests[i].choice; - gc.context.botl = TRUE; + condtests[idx].test = FALSE; + disp.botl = changed = TRUE; } } - return; + return changed; } /* called by all_options_conds() to get value for next cond_xyz option - so that #saveoptions can collect it and write the set into new RC file */ + so that #saveoptions can collect it and write the set into new RC file. + returns zero-length string if the option is the default value. */ boolean opt_next_cond(int indx, char *outbuf) { @@ -1180,13 +1244,19 @@ opt_next_cond(int indx, char *outbuf) * wasn't used to choose their preferred order. */ - Sprintf(outbuf, "%scond_%s", condtests[indx].enabled ? "" : "!", - condtests[indx].useroption); + if ((condtests[indx].opt == opt_in && condtests[indx].enabled) + || (condtests[indx].opt == opt_out && !condtests[indx].enabled)) { + Sprintf(outbuf, "%scond_%s", condtests[indx].enabled ? "" : "!", + condtests[indx].useroption); + } return TRUE; } -static boolean -eval_notify_windowport_field(int fld, boolean *valsetlist, int idx) +staticfn boolean +eval_notify_windowport_field( + int fld, + boolean *valsetlist, + int idx) { static int oldrndencode = 0; static nhsym oldgoldsym = 0; @@ -1226,28 +1296,28 @@ eval_notify_windowport_field(int fld, boolean *valsetlist, int idx) || (fld == BL_HP && iflags.wc2_hitpointbar)) { fldmax = curr->idxmax; pc = (fldmax == BL_EXP) ? exp_percentage() - : (fldmax >= 0 && fldmax < MAXBLSTATS) - ? percentage(curr, &gb.blstats[idx][fldmax]) - : 0; /* bullet proofing; can't get here */ + : (fldmax >= 0 && fldmax < MAXBLSTATS) + ? percentage(curr, &gb.blstats[idx][fldmax]) + : 0; /* bullet proofing; can't get here */ if (pc != prev->percent_value) - chg = 1; + chg = (pc < prev->percent_value) ? -1 : 1; curr->percent_value = pc; } else { pc = 0; } /* Temporary? hack: moveloop()'s prolog for a new game sets - * gc.context.rndencode after the status window has been init'd, + * svc.context.rndencode after the status window has been init'd, * so $:0 has already been encoded and cached by the window * port. Without this hack, gold's \G sequence won't be * recognized and ends up being displayed as-is for 'gu.update_all'. * - * Also, even if gc.context.rndencode hasn't changed and the + * Also, even if svc.context.rndencode hasn't changed and the * gold amount itself hasn't changed, the glyph portion of the * encoding may have changed if a new symset was put into effect. * * \GXXXXNNNN:25 - * XXXX = the gc.context.rndencode portion + * XXXX = the svc.context.rndencode portion * NNNN = the glyph portion * 25 = the gold amount * @@ -1255,10 +1325,10 @@ eval_notify_windowport_field(int fld, boolean *valsetlist, int idx) * not to honor an initial highlight, so force 'gu.update_all = TRUE'. */ if (fld == BL_GOLD - && (gc.context.rndencode != oldrndencode + && (svc.context.rndencode != oldrndencode || gs.showsyms[COIN_CLASS + SYM_OFF_O] != oldgoldsym)) { gu.update_all = TRUE; /* chg = 2; */ - oldrndencode = gc.context.rndencode; + oldrndencode = svc.context.rndencode; oldgoldsym = gs.showsyms[COIN_CLASS + SYM_OFF_O]; } @@ -1307,24 +1377,28 @@ eval_notify_windowport_field(int fld, boolean *valsetlist, int idx) return updated; } -static void -evaluate_and_notify_windowport(boolean *valsetlist, int idx) +staticfn void +evaluate_and_notify_windowport( + boolean *valsetlist, + int idx) { - int i, updated = 0, notpresent UNUSED = 0; + int i, fld, updated = 0; /* * Now pass the changed values to window port. */ for (i = 0; i < MAXBLSTATS; i++) { - if (((i == BL_SCORE) && !flags.showscore) - || ((i == BL_EXP) && !flags.showexp) - || ((i == BL_TIME) && !flags.time) - || ((i == BL_HD) && !Upolyd) - || ((i == BL_XP || i == BL_EXP) && Upolyd)) { - notpresent++; + fld = initblstats[i].fld; + if (((fld == BL_SCORE) && !flags.showscore) + || ((fld == BL_EXP) && !flags.showexp) + || ((fld == BL_TIME) && !flags.time) + || ((fld == BL_HD) && !Upolyd) + || ((fld == BL_XP || i == BL_EXP) && Upolyd) + || ((fld == BL_VERS) && !flags.showvers) + ) { continue; } - if (eval_notify_windowport_field(i, valsetlist, idx)) + if (eval_notify_windowport_field(fld, valsetlist, idx)) updated++; } /* @@ -1336,9 +1410,9 @@ evaluate_and_notify_windowport(boolean *valsetlist, int idx) * fields that have changed since the previous update. * * In both of those situations, we need to force updates to - * all of the fields when gc.context.botlx is set. The tty port in + * all of the fields when disp.botlx is set. The tty port in * particular has a problem if that isn't done, since the core sets - * gc.context.botlx when a menu or text display obliterates the status + * disp.botlx when a menu or text display obliterates the status * line. * * For those situations, to trigger the full update of every field @@ -1350,22 +1424,21 @@ evaluate_and_notify_windowport(boolean *valsetlist, int idx) * the display, call status_update() with BL_FLUSH. * */ - if (gc.context.botlx && (windowprocs.wincap2 & WC2_RESET_STATUS) != 0L) + if (disp.botlx && (windowprocs.wincap2 & WC2_RESET_STATUS) != 0L) status_update(BL_RESET, (genericptr_t) 0, 0, 0, NO_COLOR, (unsigned long *) 0); - else if ((updated || gc.context.botlx) + else if ((updated || disp.botlx) && (windowprocs.wincap2 & WC2_FLUSH_STATUS) != 0L) status_update(BL_FLUSH, (genericptr_t) 0, 0, 0, NO_COLOR, (unsigned long *) 0); - gc.context.botl = gc.context.botlx = iflags.time_botl = FALSE; + disp.botl = disp.botlx = disp.time_botl = FALSE; gu.update_all = FALSE; } void status_initialize( - boolean reassessment) /* TRUE: just recheck fields without other - * initialization */ + boolean reassessment) /* True: just recheck fields without other init */ { enum statusfields fld; boolean fldenabl; @@ -1388,7 +1461,8 @@ status_initialize( : (fld == BL_EXP) ? (boolean) (flags.showexp && !Upolyd) : (fld == BL_XP) ? (boolean) !Upolyd : (fld == BL_HD) ? (boolean) Upolyd - : TRUE; + : (fld == BL_VERS) ? flags.showvers + : TRUE; fieldname = initblstats[i].fldname; fieldfmt = (fld == BL_TITLE && iflags.wc2_hitpointbar) ? "%-30.30s" @@ -1396,7 +1470,7 @@ status_initialize( status_enablefield(fld, fieldname, fieldfmt, fldenabl); } gu.update_all = TRUE; - gc.context.botlx = TRUE; + disp.botlx = TRUE; } void @@ -1411,9 +1485,11 @@ status_finish(void) /* free memory that we alloc'd now */ for (i = 0; i < MAXBLSTATS; ++i) { if (gb.blstats[0][i].val) - free((genericptr_t) gb.blstats[0][i].val), gb.blstats[0][i].val = 0; + free((genericptr_t) gb.blstats[0][i].val), + gb.blstats[0][i].val = (char *) NULL; if (gb.blstats[1][i].val) - free((genericptr_t) gb.blstats[1][i].val), gb.blstats[1][i].val = 0; + free((genericptr_t) gb.blstats[1][i].val), + gb.blstats[1][i].val = (char *) NULL; #ifdef STATUS_HILITES /* pointer to an entry in thresholds list; Null it out since that list is about to go away */ @@ -1425,13 +1501,15 @@ status_finish(void) next = temp->next; free((genericptr_t) temp); } - gb.blstats[0][i].thresholds = gb.blstats[1][i].thresholds = 0; + gb.blstats[0][i].thresholds + = gb.blstats[1][i].thresholds + = (struct hilite_s *) NULL; } #endif /* STATUS_HILITES */ } } -static void +staticfn void init_blstats(void) { static boolean initalready = FALSE; @@ -1450,7 +1528,8 @@ init_blstats(void) gb.blstats[i][j] = initblstats[j]; gb.blstats[i][j].a = cg.zeroany; if (gb.blstats[i][j].valwidth) { - gb.blstats[i][j].val = (char *) alloc(gb.blstats[i][j].valwidth); + gb.blstats[i][j].val + = (char *) alloc(gb.blstats[i][j].valwidth); gb.blstats[i][j].val[0] = '\0'; } else gb.blstats[i][j].val = (char *) 0; @@ -1480,10 +1559,12 @@ init_blstats(void) * - for strings, 0 = stayed the same, 1 = changed * */ -static int -compare_blstats(struct istat_s *bl1, struct istat_s*bl2) +staticfn int +compare_blstats(struct istat_s *bl1, struct istat_s *bl2) { - int anytype, result = 0; + anything *a1, *a2; + boolean use_rawval; + int anytype, fld, result = 0; if (!bl1 || !bl2) { panic("compare_blstat: bad istat pointer %s, %s", @@ -1492,59 +1573,58 @@ compare_blstats(struct istat_s *bl1, struct istat_s*bl2) anytype = bl1->anytype; if ((!bl1->a.a_void || !bl2->a.a_void) - && (anytype == ANY_IPTR || anytype == ANY_UPTR || anytype == ANY_LPTR - || anytype == ANY_ULPTR)) { + && (anytype == ANY_IPTR || anytype == ANY_UPTR + || anytype == ANY_LPTR || anytype == ANY_ULPTR)) { panic("compare_blstat: invalid pointer %s, %s", fmt_ptr((genericptr_t) bl1->a.a_void), fmt_ptr((genericptr_t) bl2->a.a_void)); } + fld = bl1->fld; + use_rawval = (fld == BL_HP || fld == BL_HPMAX + || fld == BL_ENE || fld == BL_ENEMAX + || fld == BL_GOLD); + a1 = use_rawval ? &bl1->rawval : &bl1->a; + a2 = use_rawval ? &bl2->rawval : &bl2->a; + switch (anytype) { case ANY_INT: - result = (bl1->a.a_int < bl2->a.a_int) - ? 1 - : (bl1->a.a_int > bl2->a.a_int) ? -1 : 0; + result = (a1->a_int < a2->a_int) ? 1 + : (a1->a_int > a2->a_int) ? -1 : 0; break; case ANY_IPTR: - result = (*bl1->a.a_iptr < *bl2->a.a_iptr) - ? 1 - : (*bl1->a.a_iptr > *bl2->a.a_iptr) ? -1 : 0; + result = (*a1->a_iptr < *a2->a_iptr) ? 1 + : (*a1->a_iptr > *a2->a_iptr) ? -1 : 0; break; case ANY_LONG: - result = (bl1->a.a_long < bl2->a.a_long) - ? 1 - : (bl1->a.a_long > bl2->a.a_long) ? -1 : 0; + result = (a1->a_long < a2->a_long) ? 1 + : (a1->a_long > a2->a_long) ? -1 : 0; break; case ANY_LPTR: - result = (*bl1->a.a_lptr < *bl2->a.a_lptr) - ? 1 - : (*bl1->a.a_lptr > *bl2->a.a_lptr) ? -1 : 0; + result = (*a1->a_lptr < *a2->a_lptr) ? 1 + : (*a1->a_lptr > *a2->a_lptr) ? -1 : 0; break; case ANY_UINT: - result = (bl1->a.a_uint < bl2->a.a_uint) - ? 1 - : (bl1->a.a_uint > bl2->a.a_uint) ? -1 : 0; + result = (a1->a_uint < a2->a_uint) ? 1 + : (a1->a_uint > a2->a_uint) ? -1 : 0; break; case ANY_UPTR: - result = (*bl1->a.a_uptr < *bl2->a.a_uptr) - ? 1 - : (*bl1->a.a_uptr > *bl2->a.a_uptr) ? -1 : 0; + result = (*a1->a_uptr < *a2->a_uptr) ? 1 + : (*a1->a_uptr > *a2->a_uptr) ? -1 : 0; break; case ANY_ULONG: - result = (bl1->a.a_ulong < bl2->a.a_ulong) - ? 1 - : (bl1->a.a_ulong > bl2->a.a_ulong) ? -1 : 0; + result = (a1->a_ulong < a2->a_ulong) ? 1 + : (a1->a_ulong > a2->a_ulong) ? -1 : 0; break; case ANY_ULPTR: - result = (*bl1->a.a_ulptr < *bl2->a.a_ulptr) - ? 1 - : (*bl1->a.a_ulptr > *bl2->a.a_ulptr) ? -1 : 0; + result = (*a1->a_ulptr < *a2->a_ulptr) ? 1 + : (*a1->a_ulptr > *a2->a_ulptr) ? -1 : 0; break; case ANY_STR: result = sgn(strcmp(bl1->val, bl2->val)); break; case ANY_MASK32: - result = (bl1->a.a_ulong != bl2->a.a_ulong); + result = (a1->a_ulong != a2->a_ulong); break; default: result = 1; @@ -1552,7 +1632,7 @@ compare_blstats(struct istat_s *bl1, struct istat_s*bl2) return result; } -static char * +staticfn char * anything_to_s(char *buf, anything *a, int anytype) { if (!buf) @@ -1596,7 +1676,7 @@ anything_to_s(char *buf, anything *a, int anytype) } #ifdef STATUS_HILITES -static void +staticfn void s_to_anything(anything *a, char *buf, int anytype) { if (!buf || !a) @@ -1642,15 +1722,18 @@ s_to_anything(anything *a, char *buf, int anytype) } #endif /* STATUS_HILITES */ -static int +/* integer percentage is 100 * bl->a / maxbl->a */ +staticfn int percentage(struct istat_s *bl, struct istat_s *maxbl) { int result = 0; int anytype; - int ival; + int ival, mval; long lval; unsigned uval; unsigned long ulval; + int fld; + boolean use_rawval; if (!bl || !maxbl) { impossible("percentage: bad istat pointer %s, %s", @@ -1658,13 +1741,19 @@ percentage(struct istat_s *bl, struct istat_s *maxbl) return 0; } + fld = bl->fld; + use_rawval = (fld == BL_HP || fld == BL_ENE); ival = 0, lval = 0L, uval = 0U, ulval = 0UL; anytype = bl->anytype; if (maxbl->a.a_void) { switch (anytype) { case ANY_INT: - ival = bl->a.a_int; - result = ((100 * ival) / maxbl->a.a_int); + /* HP and energy are int so this is the only case that cares + about 'rawval'; for them, we use that rather than their + potentially truncated (to 9999) display value */ + ival = use_rawval ? bl->rawval.a_int : bl->a.a_int; + mval = use_rawval ? maxbl->rawval.a_int : maxbl->a.a_int; + result = ((100 * ival) / mval); break; case ANY_LONG: lval = bl->a.a_long; @@ -1709,7 +1798,7 @@ percentage(struct istat_s *bl, struct istat_s *maxbl) /* percentage for both xp (level) and exp (points) is the percentage for (curr_exp - this_level_start) in (next_level_start - this_level_start) */ -static int +staticfn int exp_percentage(void) { int res = 0; @@ -1736,6 +1825,7 @@ exp_percentage(void) curval.a = maxval.a = cg.zeroany; curval.a.a_long = exp_val; maxval.a.a_long = nxt_exp_val; + curval.fld = maxval.fld = BL_EXP; /* (neither BL_HP nor BL_ENE) */ /* maximum delta between levels is 10000000; calculation of 100 * (10000000 - N) / 10000000 fits within 32-bit long */ res = percentage(&curval, &maxval); @@ -1758,7 +1848,7 @@ exp_percent_changing(void) struct istat_s *curr; /* if status update is already requested, skip this processing */ - if (!gc.context.botl) { + if (!disp.botl) { /* * Status update is warranted iff percent integer changes and the new * percentage results in a different highlighting rule being selected. @@ -1777,7 +1867,7 @@ exp_percent_changing(void) rule = get_hilite(gn.now_or_before_idx, BL_XP, (genericptr_t) &a, 0, pc, &color_dummy); if (rule != curr->hilite_rule) - return TRUE; /* caller should set 'gc.context.botl' to True */ + return TRUE; /* caller should set 'disp.botl' to True */ #endif } } @@ -1824,13 +1914,26 @@ bl_idx_to_fldname(int idx) return (const char *) 0; } +/* used when rendering hitpointbar; inoutbuf[] has been padded with + trailing spaces; replace pairs of spaces with pairs of space+dash */ +void +repad_with_dashes(char *inoutbuf) +{ + char *p = eos(inoutbuf); + + while (p >= inoutbuf + 2 && p[-1] == ' ' && p[-2] == ' ') { + p[-1] = '-'; + p -= 2; + } +} + #ifdef STATUS_HILITES /****************************************************************************/ /* Core status hiliting support */ /****************************************************************************/ -static struct fieldid_t { +static const struct fieldid_t { const char *fieldname; enum statusfields fldid; } fieldids_alias[] = { @@ -1864,7 +1967,7 @@ static const char threshold_value[] = "hilite_status threshold ", /* field name to bottom line index */ -static enum statusfields +staticfn enum statusfields fldname_to_bl_indx(const char *name) { int i, nmatches = 0, fld = 0; @@ -1876,7 +1979,6 @@ fldname_to_bl_indx(const char *name) fld = initblstats[i].fld; nmatches++; } - if (!nmatches) { /* check aliases */ for (i = 0; fieldids_alias[i].fieldname; i++) @@ -1886,7 +1988,6 @@ fldname_to_bl_indx(const char *name) nmatches++; } } - if (!nmatches) { /* check partial matches to canonical names */ int len = (int) strlen(name); @@ -1902,11 +2003,11 @@ fldname_to_bl_indx(const char *name) return (nmatches == 1) ? fld : BL_FLUSH; } -static boolean -hilite_reset_needed(struct istat_s *bl_p, - long augmented_time) /* no longer augmented; it once - * encoded fractional amounts for - * multiple moves within same turn */ +staticfn boolean +hilite_reset_needed( + struct istat_s *bl_p, + long augmented_time) /* no longer augmented; it once encoded fractional + * amounts for multiple moves within same turn */ { /* * This 'multi' handling may need some tuning... @@ -1931,27 +2032,27 @@ status_eval_next_unhilite(void) struct istat_s *curr; long next_unhilite, this_unhilite; - gb.bl_hilite_moves = gm.moves; /* simplified; at one point we used to try - * to encode fractional amounts for multiple - * moves within same turn */ + gb.bl_hilite_moves = svm.moves; /* simplified; at one point we used to + * 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) { - curr = &gb.blstats[0][i]; /* blstats[0][*].time == blstats[1][*].time */ + curr = &gb.blstats[0][i]; /* blstats[0][*].time==blstats[1][*].time */ if (curr->chg) { struct istat_s *prev = &gb.blstats[1][i]; if (Is_Temp_Hilite(curr->hilite_rule)) - curr->time = prev->time = (gb.bl_hilite_moves - + iflags.hilite_delta); + curr->time = (gb.bl_hilite_moves + iflags.hilite_delta); else - curr->time = prev->time = 0L; + curr->time = 0L; + prev->time = curr->time; curr->chg = prev->chg = FALSE; - gc.context.botl = TRUE; + disp.botl = TRUE; } - if (gc.context.botl) + if (disp.botl) continue; /* just process other gb.blstats[][].time and .chg */ this_unhilite = curr->time; @@ -1960,7 +2061,7 @@ status_eval_next_unhilite(void) && hilite_reset_needed(curr, this_unhilite + 1L)) { next_unhilite = this_unhilite; if (next_unhilite < gb.bl_hilite_moves) - gc.context.botl = TRUE; + disp.botl = TRUE; } } } @@ -1976,12 +2077,12 @@ reset_status_hilites(void) gb.blstats[0][i].time = gb.blstats[1][i].time = 0L; gu.update_all = TRUE; } - gc.context.botlx = TRUE; + disp.botlx = TRUE; } /* test whether the text from a title rule matches the string for title-while-polymorphed in the 'textmatch' menu */ -static boolean +staticfn boolean noneoftheabove(const char *hl_text) { if (fuzzymatch(hl_text, "none of the above", "\" -_", TRUE) @@ -2009,9 +2110,12 @@ noneoftheabove(const char *hl_text) * Get back: * pointer to rule that applies; Null if no rule does. */ -static struct hilite_s * -get_hilite(int idx, int fldidx, genericptr_t vp, int chg, int pc, - int *colorptr) +staticfn struct hilite_s * +get_hilite( + int idx, int fldidx, + genericptr_t vp, + int chg, int pc, + int *colorptr) { struct hilite_s *hl, *rule = 0; anything *value = (anything *) vp; @@ -2031,11 +2135,20 @@ get_hilite(int idx, int fldidx, genericptr_t vp, int chg, int pc, ancient configurations; we don't need LONG_MIN */ long max_lval = -LONG_MAX, min_lval = LONG_MAX; boolean exactmatch = FALSE, updown = FALSE, changed = FALSE, - perc_or_abs = FALSE; + perc_or_abs = FALSE, crit_hp = FALSE; /* min_/max_ are used to track best fit */ for (hl = gb.blstats[0][fldidx].thresholds; hl; hl = hl->next) { dt = initblstats[fldidx].anytype; /* only needed for 'absolute' */ + /* for HP, if we already have a critical-hp rule then we ignore + other HP rules unless we hit another critical-hp one (last + one found wins); critical-hp takes precedence over temporary + HP highlights, otherwise a hero with regeneration and an up + or changed rule for HP would always show that up or changed + highlight even when within the critical-hp threshold because + the value will go up by at least one on every move */ + if (crit_hp && hl->behavior != BL_TH_CRITICALHP) + continue; /* if we've already matched a temporary highlight, it takes precedence over all persistent ones; we still process updown rules to get the last one which qualifies */ @@ -2172,7 +2285,7 @@ get_hilite(int idx, int fldidx, genericptr_t vp, int chg, int pc, txtstr = gb.blstats[idx][fldidx].val; if (fldidx == BL_TITLE) /* " the ", skip past " the " */ - txtstr += (strlen(gp.plname) + sizeof " the " - sizeof ""); + txtstr += strlen(svp.plname) + sizeof " the " - sizeof ""; if (hl->rel == TXT_VALUE && hl->textmatch[0]) { if (fuzzymatch(hl->textmatch, txtstr, "\" -_", TRUE)) { rule = hl; @@ -2188,6 +2301,13 @@ get_hilite(int idx, int fldidx, genericptr_t vp, int chg, int pc, case BL_TH_ALWAYS_HILITE: rule = hl; break; + case BL_TH_CRITICALHP: + if (fldidx == BL_HP && critically_low_hp(FALSE)) { + rule = hl; + crit_hp = TRUE; + updown = changed = perc_or_abs = FALSE; + } + break; case BL_TH_NONE: break; default: @@ -2199,7 +2319,10 @@ get_hilite(int idx, int fldidx, genericptr_t vp, int chg, int pc, return rule; } -static void +#undef has_hilite +#undef Is_Temp_Hilite + +staticfn void split_clridx(int idx, int *coloridx, int *attrib) { if (coloridx) @@ -2267,13 +2390,15 @@ parse_status_hl1(char *op, boolean from_configfile) } if (badopt) return FALSE; - else if (!iflags.hilite_delta) + /* make sure highlighting is On; use short duration for temp highlights */ + if (!iflags.hilite_delta) iflags.hilite_delta = 3L; return TRUE; +#undef MAX_THRESH } /* is str in the format of "[<>]?=?[-+]?[0-9]+%?" regex */ -static boolean +staticfn boolean is_ltgt_percentnumber(const char *str) { const char *s = str; @@ -2294,7 +2419,7 @@ is_ltgt_percentnumber(const char *str) } /* does str only contain "<>=-+0-9%" chars */ -static boolean +staticfn boolean has_ltgt_percentnumber(const char *str) { const char *s = str; @@ -2310,10 +2435,10 @@ 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 */ -#define MAX_SUBFIELDS 16 -static int +staticfn int splitsubfields(char *str, char ***sfarr, int maxsf) { +#define MAX_SUBFIELDS 16 static char *subfields[MAX_SUBFIELDS]; char *st = (char *) 0; int sf = 0; @@ -2349,10 +2474,10 @@ splitsubfields(char *str, char ***sfarr, int maxsf) } *sfarr = subfields; return sf; -} #undef MAX_SUBFIELDS +} -static boolean +staticfn boolean is_fld_arrayvalues( const char *str, const char *const *arr, @@ -2369,7 +2494,7 @@ is_fld_arrayvalues( return FALSE; } -static int +staticfn int query_arrayvalue( const char *querystr, const char *const *arr, @@ -2380,12 +2505,14 @@ query_arrayvalue( anything any; menu_item *picks = (menu_item *) 0; int adj = (arrmin > 0) ? 1 : arrmax; - int clr = 0; + int clr = NO_COLOR; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); for (i = arrmin; i < arrmax; i++) { + if (!arr[i]) /* the array of hunger status values has a gap ...*/ + continue; /*... set to Null between Satiated and Hungry */ any = cg.zeroany; any.a_int = i + adj; add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, @@ -2404,7 +2531,7 @@ query_arrayvalue( return ret; } -static void +staticfn void status_hilite_add_threshold(int fld, struct hilite_s *hilite) { struct hilite_s *new_hilite, *old_hilite; @@ -2434,24 +2561,25 @@ status_hilite_add_threshold(int fld, struct hilite_s *hilite) gb.blstats[1][fld].thresholds = gb.blstats[0][fld].thresholds; } - -static boolean +staticfn boolean parse_status_hl2(char (*s)[QBUFSZ], boolean from_configfile) { + static const char *const aligntxt[] = { "chaotic", "neutral", "lawful" }; + /* hu_stat[] from eat.c has trailing spaces which foul up comparisons; + for the "not hungry" case, there's no text hence no way to highlight */ + static const char *const hutxt[] = { + "Satiated", "", "Hungry", "Weak", "Fainting", "Fainted", "Starved" + }; char *tmp, *how; int sidx = 0, i = -1, dt = -1; int coloridx = -1, successes = 0; int disp_attrib = 0; boolean percent, changed, numeric, down, up, - grt, lt, gte, le, eq, txtval, always; + grt, lt, gte, le, eq, txtval, always, criticalhp; const char *txt; enum statusfields fld = BL_FLUSH; struct hilite_s hilite; char tmpbuf[BUFSZ]; - static const char *const aligntxt[] = { "chaotic", "neutral", "lawful" }; - /* hu_stat[] from eat.c has trailing spaces which foul up comparisons */ - static const char *const hutxt[] = { "Satiated", "", "Hungry", "Weak", - "Fainting", "Fainted", "Starved" }; /* Examples: 3.6.1: @@ -2491,9 +2619,10 @@ parse_status_hl2(char (*s)[QBUFSZ], boolean from_configfile) int sf = 0; /* subfield count */ int kidx; - txt = (const char *)0; + txt = (const char *) 0; percent = numeric = always = FALSE; down = up = changed = FALSE; + criticalhp = FALSE; grt = gte = eq = le = lt = txtval = FALSE; #if 0 /* threshold value - return on empty string */ @@ -2538,6 +2667,8 @@ parse_status_hl2(char (*s)[QBUFSZ], boolean from_configfile) txtval = TRUE; } else if (!strcmpi(s[sidx], "changed")) { changed = TRUE; + } else if (fld == BL_HP && !strcmpi(s[sidx], "criticalhp")) { + criticalhp = TRUE; } else if (is_ltgt_percentnumber(s[sidx])) { const char *op; @@ -2577,7 +2708,7 @@ parse_status_hl2(char (*s)[QBUFSZ], boolean from_configfile) is_out_of_range); return FALSE; } else if (dt == ANY_LONG - && (hilite.value.a_long < (grt ? -1L : lt ? 1L : 0L))) { + && hilite.value.a_long < (grt ? -1L : lt ? 1L : 0L)) { config_error_add("%s'%s%ld'%s", threshold_value, op, hilite.value.a_long, is_out_of_range); return FALSE; @@ -2661,20 +2792,22 @@ parse_status_hl2(char (*s)[QBUFSZ], boolean from_configfile) for (i = 0; i < sf; ++i) { int a = match_str2attr(subfields[i], FALSE); - if (a == ATR_DIM) + if (a == ATR_BOLD) + disp_attrib |= HL_BOLD; + else if (a == ATR_DIM) disp_attrib |= HL_DIM; - else if (a == ATR_BLINK) - disp_attrib |= HL_BLINK; + else if (a == ATR_ITALIC) + disp_attrib |= HL_ITALIC; else if (a == ATR_ULINE) disp_attrib |= HL_ULINE; + else if (a == ATR_BLINK) + disp_attrib |= HL_BLINK; else if (a == ATR_INVERSE) disp_attrib |= HL_INVERSE; - else if (a == ATR_BOLD) - disp_attrib |= HL_BOLD; else if (a == ATR_NONE) disp_attrib = HL_NONE; else { - int c = match_str2clr(subfields[i]); + int c = match_str2clr(subfields[i], FALSE); if (c >= CLR_MAX || coloridx != -1) { config_error_add("bad color '%d %d'", c, coloridx); @@ -2701,7 +2834,9 @@ parse_status_hl2(char (*s)[QBUFSZ], boolean from_configfile) hilite.behavior = BL_TH_TEXTMATCH; else if (hilite.value.a_void) hilite.behavior = BL_TH_VAL_ABSOLUTE; - else + else if (criticalhp) + hilite.behavior = BL_TH_CRITICALHP; + else hilite.behavior = BL_TH_NONE; hilite.anytype = dt; @@ -2720,11 +2855,8 @@ parse_status_hl2(char (*s)[QBUFSZ], boolean from_configfile) return (successes > 0); } -#endif /* STATUS_HILITES */ -#ifdef STATUS_HILITES - -static unsigned long +staticfn unsigned long query_conditions(void) { int i,res; @@ -2732,7 +2864,7 @@ query_conditions(void) winid tmpwin; anything any; menu_item *picks = (menu_item *) 0; - int clr = 0; + int clr = NO_COLOR; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -2756,7 +2888,7 @@ query_conditions(void) return ret; } -static char * +staticfn char * conditionbitmask2str(unsigned long ul) { static char buf[BUFSZ]; @@ -2786,7 +2918,7 @@ conditionbitmask2str(unsigned long ul) return buf; } -static unsigned long +staticfn unsigned long match_str2conditionbitmask(const char *str) { int i, nmatches = 0; @@ -2824,7 +2956,7 @@ match_str2conditionbitmask(const char *str) return mask; } -static unsigned long +staticfn unsigned long str2conditionbitmask(char *str) { unsigned long conditions_bitmask = 0UL; @@ -2848,7 +2980,7 @@ str2conditionbitmask(char *str) return conditions_bitmask; } -static boolean +staticfn boolean parse_condition(char (*s)[QBUFSZ], int sidx) { int i; @@ -2895,7 +3027,7 @@ parse_condition(char (*s)[QBUFSZ], int sidx) * bitmasks indexed by the color chosen * (0 to (CLR_MAX - 1)) * and/or attributes chosen - * (HL_ATTCLR_DIM to (BL_ATTCLR_MAX - 1)) + * (HL_ATTCLR_NONE to (BL_ATTCLR_MAX - 1)) * We still have to parse the colors and attributes out. */ @@ -2921,36 +3053,33 @@ parse_condition(char (*s)[QBUFSZ], int sidx) * We have the following additional array offsets to * use for storing the attributes beyond the end of * the color indexes, all of which are less than CLR_MAX. - * HL_ATTCLR_DIM = CLR_MAX - * HL_ATTCLR_BLINK = CLR_MAX + 1 - * HL_ATTCLR_ULINE = CLR_MAX + 2 - * HL_ATTCLR_INVERSE = CLR_MAX + 3 - * HL_ATTCLR_BOLD = CLR_MAX + 4 - * HL_ATTCLR_MAX = CLR_MAX + 5 (this is past array boundary) * */ for (i = 0; i < sf; ++i) { int a = match_str2attr(subfields[i], FALSE); - if (a == ATR_DIM) + if (a == ATR_BOLD) + gc.cond_hilites[HL_ATTCLR_BOLD] |= conditions_bitmask; + else if (a == ATR_DIM) gc.cond_hilites[HL_ATTCLR_DIM] |= conditions_bitmask; - else if (a == ATR_BLINK) - gc.cond_hilites[HL_ATTCLR_BLINK] |= conditions_bitmask; + else if (a == ATR_ITALIC) + gc.cond_hilites[HL_ATTCLR_ITALIC] |= conditions_bitmask; else if (a == ATR_ULINE) gc.cond_hilites[HL_ATTCLR_ULINE] |= conditions_bitmask; + else if (a == ATR_BLINK) + gc.cond_hilites[HL_ATTCLR_BLINK] |= conditions_bitmask; else if (a == ATR_INVERSE) gc.cond_hilites[HL_ATTCLR_INVERSE] |= conditions_bitmask; - else if (a == ATR_BOLD) - gc.cond_hilites[HL_ATTCLR_BOLD] |= conditions_bitmask; else if (a == ATR_NONE) { + gc.cond_hilites[HL_ATTCLR_BOLD] &= ~conditions_bitmask; gc.cond_hilites[HL_ATTCLR_DIM] &= ~conditions_bitmask; - gc.cond_hilites[HL_ATTCLR_BLINK] &= ~conditions_bitmask; + gc.cond_hilites[HL_ATTCLR_ITALIC] &= ~conditions_bitmask; gc.cond_hilites[HL_ATTCLR_ULINE] &= ~conditions_bitmask; + gc.cond_hilites[HL_ATTCLR_BLINK] &= ~conditions_bitmask; gc.cond_hilites[HL_ATTCLR_INVERSE] &= ~conditions_bitmask; - gc.cond_hilites[HL_ATTCLR_BOLD] &= ~conditions_bitmask; } else { - int k = match_str2clr(subfields[i]); + int k = match_str2clr(subfields[i], FALSE); if (k >= CLR_MAX) { config_error_add("bad color %d", k); @@ -2987,7 +3116,7 @@ clear_status_hilites(void) } } -static char * +staticfn char * hlattr2attrname(int attrib, char *buf, size_t bufsz) { if (attrib && buf) { @@ -3003,14 +3132,16 @@ hlattr2attrname(int attrib, char *buf, size_t bufsz) if (attrib & HL_BOLD) Strcat(attbuf, first++ ? "+bold" : "bold"); - if (attrib & HL_INVERSE) - Strcat(attbuf, first++ ? "+inverse" : "inverse"); + if (attrib & HL_DIM) + Strcat(attbuf, first++ ? "+dim" : "dim"); + if (attrib & HL_ITALIC) + Strcat(attbuf, first++ ? "+italic" : "italic"); if (attrib & HL_ULINE) Strcat(attbuf, first++ ? "+underline" : "underline"); if (attrib & HL_BLINK) Strcat(attbuf, first++ ? "+blink" : "blink"); - if (attrib & HL_DIM) - Strcat(attbuf, first++ ? "+dim" : "dim"); + if (attrib & HL_INVERSE) + Strcat(attbuf, first++ ? "+inverse" : "inverse"); k = strlen(attbuf); if (k < (size_t)(bufsz - 1)) @@ -3020,7 +3151,6 @@ hlattr2attrname(int attrib, char *buf, size_t bufsz) return (char *) 0; } - struct _status_hilite_line_str { int id; int fld; @@ -3030,12 +3160,16 @@ struct _status_hilite_line_str { struct _status_hilite_line_str *next; }; +/* these don't need to be in 'struct g' */ static struct _status_hilite_line_str *status_hilite_str = 0; static int status_hilite_str_id = 0; -static void -status_hilite_linestr_add(int fld, struct hilite_s *hl, - unsigned long mask, const char *str) +staticfn void +status_hilite_linestr_add( + int fld, + struct hilite_s *hl, + unsigned long mask, + const char *str) { struct _status_hilite_line_str *tmp, *nxt; @@ -3061,7 +3195,7 @@ status_hilite_linestr_add(int fld, struct hilite_s *hl, } } -static void +staticfn void status_hilite_linestr_done(void) { struct _status_hilite_line_str *nxt, *tmp = status_hilite_str; @@ -3075,7 +3209,7 @@ status_hilite_linestr_done(void) status_hilite_str_id = 0; } -static int +staticfn int status_hilite_linestr_countfield(int fld) { struct _status_hilite_line_str *tmp; @@ -3101,7 +3235,7 @@ count_status_hilites(void) return count; } -static void +staticfn void status_hilite_linestr_gather_conditions(void) { int i; @@ -3123,14 +3257,16 @@ status_hilite_linestr_gather_conditions(void) clr = j; break; } - if (gc.cond_hilites[HL_ATTCLR_DIM] & conditions[i].mask) - atr |= HL_DIM; if (gc.cond_hilites[HL_ATTCLR_BOLD] & conditions[i].mask) atr |= HL_BOLD; - if (gc.cond_hilites[HL_ATTCLR_BLINK] & conditions[i].mask) - atr |= HL_BLINK; + if (gc.cond_hilites[HL_ATTCLR_DIM] & conditions[i].mask) + atr |= HL_DIM; + if (gc.cond_hilites[HL_ATTCLR_ITALIC] & conditions[i].mask) + atr |= HL_ITALIC; if (gc.cond_hilites[HL_ATTCLR_ULINE] & conditions[i].mask) atr |= HL_ULINE; + if (gc.cond_hilites[HL_ATTCLR_BLINK] & conditions[i].mask) + atr |= HL_BLINK; if (gc.cond_hilites[HL_ATTCLR_INVERSE] & conditions[i].mask) atr |= HL_INVERSE; if (atr != HL_NONE) @@ -3181,7 +3317,7 @@ status_hilite_linestr_gather_conditions(void) } } -static void +staticfn void status_hilite_linestr_gather(void) { int i; @@ -3201,11 +3337,11 @@ status_hilite_linestr_gather(void) } -static char * +staticfn char * status_hilite2str(struct hilite_s *hl) { static char buf[BUFSZ]; - int clr = 0, attr = 0; + int clr = NO_COLOR, attr = ATR_NONE; char behavebuf[BUFSZ]; char clrbuf[BUFSZ]; char attrbuf[BUFSZ]; @@ -3262,6 +3398,9 @@ status_hilite2str(struct hilite_s *hl) case BL_TH_ALWAYS_HILITE: Sprintf(behavebuf, "always"); break; + case BL_TH_CRITICALHP: + Sprintf(behavebuf, "criticalhp"); + break; case BL_TH_NONE: break; default: @@ -3280,14 +3419,14 @@ status_hilite2str(struct hilite_s *hl) return buf; } -static int +staticfn int status_hilite_menu_choose_field(void) { winid tmpwin; int i, res, fld = BL_FLUSH; anything any; menu_item *picks = (menu_item *) 0; - int clr = 0; + int clr = NO_COLOR; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -3315,7 +3454,7 @@ status_hilite_menu_choose_field(void) return fld; } -static int +staticfn int status_hilite_menu_choose_behavior(int fld) { winid tmpwin; @@ -3325,7 +3464,7 @@ status_hilite_menu_choose_behavior(int fld) char buf[BUFSZ]; int at; int onlybeh = BL_TH_NONE, nopts = 0; - int clr = 0; + int clr = NO_COLOR; if (fld < 0 || fld >= MAXBLSTATS) return BL_TH_NONE; @@ -3352,7 +3491,7 @@ status_hilite_menu_choose_behavior(int fld) nopts++; } - if (fld != BL_CONDITION) { + if (fld != BL_CONDITION && fld != BL_VERS) { any = cg.zeroany; any.a_int = onlybeh = BL_TH_UPDOWN; Sprintf(buf, "%s value changes", initblstats[fld].fldname); @@ -3378,6 +3517,16 @@ status_hilite_menu_choose_behavior(int fld) nopts++; } + if (fld == BL_HP) { + any = cg.zeroany; + any.a_int = onlybeh = BL_TH_CRITICALHP; + Sprintf(buf, "Highlight critically low %s", + initblstats[fld].fldname); + add_menu(tmpwin, &nul_glyphinfo, &any, 'C', 0, ATR_NONE, + clr, buf, MENU_ITEMFLAGS_NONE); + nopts++; + } + if (initblstats[fld].anytype == ANY_STR || fld == BL_CAP || fld == BL_HUNGER) { any = cg.zeroany; @@ -3398,8 +3547,9 @@ status_hilite_menu_choose_behavior(int fld) beh = BL_TH_NONE; else if (res == -1) /* menu cancelled */ beh = (BL_TH_NONE - 1); - } else if (onlybeh != BL_TH_NONE) + } else if (onlybeh != BL_TH_NONE) { beh = onlybeh; + } destroy_nhwindow(tmpwin); if (res > 0) { beh = picks->item.a_int; @@ -3408,16 +3558,18 @@ status_hilite_menu_choose_behavior(int fld) return beh; } -static int -status_hilite_menu_choose_updownboth(int fld, const char *str, - boolean ltok, boolean gtok) +staticfn int +status_hilite_menu_choose_updownboth( + int fld, + const char *str, + boolean ltok, boolean gtok) { int res, ret = NO_LTEQGT; winid tmpwin; char buf[BUFSZ]; anything any; menu_item *picks = (menu_item *) 0; - int clr = 0; + int clr = NO_COLOR; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -3469,8 +3621,8 @@ status_hilite_menu_choose_updownboth(int fld, const char *str, Sprintf(buf, "Value goes up"); any = cg.zeroany; any.a_int = 10 + GT_VALUE; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, buf, MENU_ITEMFLAGS_NONE); + add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, + buf, MENU_ITEMFLAGS_NONE); } Sprintf(buf, "Select field %s value:", initblstats[fld].fldname); end_menu(tmpwin, buf); @@ -3485,7 +3637,7 @@ status_hilite_menu_choose_updownboth(int fld, const char *str, return ret; } -static boolean +staticfn boolean status_hilite_menu_add(int origfld) { int fld; @@ -3679,7 +3831,7 @@ status_hilite_menu_add(int origfld) if (initblstats[fld].anytype != ANY_STR) { boolean ltok = (fld != BL_TIME), gtok = TRUE; - lt_gt_eq = status_hilite_menu_choose_updownboth(fld, (char *)0, + lt_gt_eq = status_hilite_menu_choose_updownboth(fld, (char *) 0, ltok, gtok); if (lt_gt_eq == NO_LTEQGT) goto choose_behavior; @@ -3825,14 +3977,14 @@ status_hilite_menu_add(int origfld) } choose_color: - clr = query_color(colorqry); + clr = query_color(colorqry, NO_COLOR); if (clr == -1) { if (behavior != BL_TH_ALWAYS_HILITE) goto choose_value; else goto choose_behavior; } - atr = query_attr(attrqry); + atr = query_attr(attrqry, ATR_NONE); if (atr == -1) goto choose_color; @@ -3841,22 +3993,25 @@ status_hilite_menu_add(int origfld) char attrbuf[BUFSZ]; char *tmpattr; + if (atr & HL_BOLD) + gc.cond_hilites[HL_ATTCLR_BOLD] |= cond; if (atr & HL_DIM) gc.cond_hilites[HL_ATTCLR_DIM] |= cond; - if (atr & HL_BLINK) - gc.cond_hilites[HL_ATTCLR_BLINK] |= cond; + if (atr & HL_ITALIC) + gc.cond_hilites[HL_ATTCLR_ITALIC] |= cond; if (atr & HL_ULINE) gc.cond_hilites[HL_ATTCLR_ULINE] |= cond; + if (atr & HL_BLINK) + gc.cond_hilites[HL_ATTCLR_BLINK] |= cond; if (atr & HL_INVERSE) gc.cond_hilites[HL_ATTCLR_INVERSE] |= cond; - if (atr & HL_BOLD) - gc.cond_hilites[HL_ATTCLR_BOLD] |= cond; if (atr == HL_NONE) { + gc.cond_hilites[HL_ATTCLR_BOLD] &= ~cond; gc.cond_hilites[HL_ATTCLR_DIM] &= ~cond; - gc.cond_hilites[HL_ATTCLR_BLINK] &= ~cond; + gc.cond_hilites[HL_ATTCLR_ITALIC] &= ~cond; gc.cond_hilites[HL_ATTCLR_ULINE] &= ~cond; + gc.cond_hilites[HL_ATTCLR_BLINK] &= ~cond; gc.cond_hilites[HL_ATTCLR_INVERSE] &= ~cond; - gc.cond_hilites[HL_ATTCLR_BOLD] &= ~cond; } gc.cond_hilites[clr] |= cond; (void) strNsubst(strcpy(clrbuf, clr2colorname(clr)), " ", "-", 0); @@ -3892,7 +4047,7 @@ status_hilite_menu_add(int origfld) return TRUE; } -static boolean +staticfn boolean status_hilite_remove(int id) { struct _status_hilite_line_str *hlstr = status_hilite_str; @@ -3909,10 +4064,11 @@ status_hilite_remove(int id) for (i = 0; i < CLR_MAX; i++) gc.cond_hilites[i] &= ~hlstr->mask; - gc.cond_hilites[HL_ATTCLR_DIM] &= ~hlstr->mask; gc.cond_hilites[HL_ATTCLR_BOLD] &= ~hlstr->mask; - gc.cond_hilites[HL_ATTCLR_BLINK] &= ~hlstr->mask; + gc.cond_hilites[HL_ATTCLR_DIM] &= ~hlstr->mask; + gc.cond_hilites[HL_ATTCLR_ITALIC] &= ~hlstr->mask; gc.cond_hilites[HL_ATTCLR_ULINE] &= ~hlstr->mask; + gc.cond_hilites[HL_ATTCLR_BLINK] &= ~hlstr->mask; gc.cond_hilites[HL_ATTCLR_INVERSE] &= ~hlstr->mask; return TRUE; } else { @@ -3943,7 +4099,7 @@ status_hilite_remove(int id) return FALSE; } -static boolean +staticfn boolean status_hilite_menu_fld(int fld) { winid tmpwin; @@ -3954,7 +4110,7 @@ status_hilite_menu_fld(int fld) struct _status_hilite_line_str *hlstr; char buf[BUFSZ]; boolean acted; - int clr = 0; + int clr = NO_COLOR; if (!count) { if (status_hilite_menu_add(fld)) { @@ -3980,23 +4136,18 @@ status_hilite_menu_fld(int fld) hlstr = hlstr->next; } } else { - any = cg.zeroany; Sprintf(buf, "No current hilites for %s", initblstats[fld].fldname); - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, buf, - MENU_ITEMFLAGS_NONE); + add_menu_str(tmpwin, buf); } /* separator line */ - any = cg.zeroany; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, "", - MENU_ITEMFLAGS_NONE); + add_menu_str(tmpwin, ""); if (count) { any = cg.zeroany; any.a_int = -1; - add_menu(tmpwin, &nul_glyphinfo, &any, 'X', 0, ATR_NONE, - clr, "Remove selected hilites", - MENU_ITEMFLAGS_NONE); + add_menu(tmpwin, &nul_glyphinfo, &any, 'X', 0, ATR_NONE, clr, + "Remove selected hilites", MENU_ITEMFLAGS_NONE); } #ifndef SCORE_ON_BOTL @@ -4047,7 +4198,7 @@ status_hilite_menu_fld(int fld) return acted; } -static void +staticfn void status_hilites_viewall(void) { winid datawin; @@ -4093,12 +4244,12 @@ boolean status_hilite_menu(void) { winid tmpwin; - int i, res; + int i, fld, res; menu_item *picks = (menu_item *) 0; anything any; boolean redo; int countall; - int clr = 0; + int clr = NO_COLOR; shlmenu_redo: redo = FALSE; @@ -4115,25 +4266,25 @@ status_hilite_menu(void) clr, "View all hilites in config format", MENU_ITEMFLAGS_NONE); - any = cg.zeroany; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "", MENU_ITEMFLAGS_NONE); + add_menu_str(tmpwin, ""); } for (i = 0; i < MAXBLSTATS; i++) { - int count = status_hilite_linestr_countfield(i); + int count; char buf[BUFSZ]; + fld = initblstats[i].fld; + count = status_hilite_linestr_countfield(fld); #ifndef SCORE_ON_BOTL /* config file might contain rules for highlighting 'score' even when SCORE_ON_BOTL is disabled; if so, 'O' command menus will show them and allow deletions but not additions, otherwise, it won't show 'score' at all */ - if (initblstats[i].fld == BL_SCORE && !count) + if (fld == BL_SCORE && !count) continue; #endif any = cg.zeroany; - any.a_int = i + 1; + any.a_int = fld + 1; Sprintf(buf, "%-18s", initblstats[i].fldname); if (count) Sprintf(eos(buf), " (%d defined)", count); @@ -4143,11 +4294,11 @@ status_hilite_menu(void) end_menu(tmpwin, "Status hilites:"); if ((res = select_menu(tmpwin, PICK_ONE, &picks)) > 0) { - i = picks->item.a_int - 1; - if (i < 0) { + fld = picks->item.a_int - 1; + if (fld < 0) { status_hilites_viewall(); } else { - if (status_hilite_menu_fld(i)) + if (status_hilite_menu_fld(fld)) reset_status_hilites(); } free((genericptr_t) picks), picks = (menu_item *) 0; diff --git a/src/calendar.c b/src/calendar.c new file mode 100644 index 0000000000..78846b258a --- /dev/null +++ b/src/calendar.c @@ -0,0 +1,553 @@ +/* NetHack 3.7 calendar.c $NHDT-Date: 1706213796 2024/01/25 20:16:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.116 $ */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/*-Copyright (c) Michael Allison, 2007. */ +/* Copyright (c) Robert Patrick Rankin, 1991 */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +/* + * Time routines + * + * The time is used for: + * - seed for rand() + * - year on tombstone and yyyymmdd in record file + * - phase of the moon (various monsters react to NEW_MOON or FULL_MOON) + * - night and midnight (the undead are dangerous at midnight) + * - determination of what files are "very old" + */ + +/* TIME_type: type of the argument to time(); we actually use &(time_t); + you might need to define either or both of these to 'long *' in *conf.h */ +#ifndef TIME_type +#define TIME_type time_t * +#endif +#ifndef LOCALTIME_type +#define LOCALTIME_type time_t * +#endif + +staticfn struct tm *getlt(void); +static int weekday(void); +static int days_since_epoch(int); + +time_t +getnow(void) +{ + time_t datetime = 0; + + (void) time((TIME_type) &datetime); + return datetime; +} + +staticfn struct tm * +getlt(void) +{ + time_t date = getnow(); + + return localtime((LOCALTIME_type) &date); +} + +int +getyear(void) +{ + return (1900 + getlt()->tm_year); +} + + +long +yyyymmdd(time_t date) +{ + long datenum; + struct tm *lt; + + if (date == 0) + lt = getlt(); + else + lt = localtime((LOCALTIME_type) &date); + + /* just in case somebody's localtime supplies (year % 100) + rather than the expected (year - 1900) */ + if (lt->tm_year < 70) + datenum = (long) lt->tm_year + 2000L; + else + datenum = (long) lt->tm_year + 1900L; + /* yyyy --> yyyymm */ + datenum = datenum * 100L + (long) (lt->tm_mon + 1); + /* yyyymm --> yyyymmdd */ + datenum = datenum * 100L + (long) lt->tm_mday; + return datenum; +} + +long +hhmmss(time_t date) +{ + long timenum; + struct tm *lt; + + if (date == 0) + lt = getlt(); + else + lt = localtime((LOCALTIME_type) &date); + + timenum = lt->tm_hour * 10000L + lt->tm_min * 100L + lt->tm_sec; + return timenum; +} + +char * +yyyymmddhhmmss(time_t date) +{ + long datenum; + static char datestr[15]; + struct tm *lt; + + if (date == 0) + lt = getlt(); + else + lt = localtime((LOCALTIME_type) &date); + + /* just in case somebody's localtime supplies (year % 100) + rather than the expected (year - 1900) */ + if (lt->tm_year < 70) + datenum = (long) lt->tm_year + 2000L; + else + datenum = (long) lt->tm_year + 1900L; + Snprintf(datestr, sizeof datestr, "%04ld%02d%02d%02d%02d%02d", + datenum, lt->tm_mon + 1, + lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec); + //debugpline1("yyyymmddhhmmss() produced date string %s", datestr); + return datestr; +} + +time_t +time_from_yyyymmddhhmmss(char *buf) +{ + int k; + time_t timeresult = (time_t) 0; + struct tm t, *lt; + char *d, *p, y[5], mo[3], md[3], h[3], mi[3], s[3]; + + if (buf && strlen(buf) == 14) { + d = buf; + p = y; /* year */ + for (k = 0; k < 4; ++k) + *p++ = *d++; + *p = '\0'; + p = mo; /* month */ + for (k = 0; k < 2; ++k) + *p++ = *d++; + *p = '\0'; + p = md; /* day */ + for (k = 0; k < 2; ++k) + *p++ = *d++; + *p = '\0'; + p = h; /* hour */ + for (k = 0; k < 2; ++k) + *p++ = *d++; + *p = '\0'; + p = mi; /* minutes */ + for (k = 0; k < 2; ++k) + *p++ = *d++; + *p = '\0'; + p = s; /* seconds */ + for (k = 0; k < 2; ++k) + *p++ = *d++; + *p = '\0'; + lt = getlt(); + if (lt) { + t = *lt; + t.tm_year = atoi(y) - 1900; + t.tm_mon = atoi(mo) - 1; + t.tm_mday = atoi(md); + t.tm_hour = atoi(h); + t.tm_min = atoi(mi); + t.tm_sec = atoi(s); + timeresult = mktime(&t); + } + if (timeresult == (time_t) -1) + ; +#if 0 +TODO: set_debugpline1, debugpline1 -> function pointer + debugpline1("time_from_yyyymmddhhmmss(%s) would have returned -1", + buf ? buf : ""); +#endif + else + return timeresult; + } + return (time_t) 0; +} + +/* + * moon period = 29.53058 days ~= 30, year = 365.2422 days + * days moon phase advances on first day of year compared to preceding year + * = 365.2422 - 12*29.53058 ~= 11 + * years in Metonic cycle (time until same phases fall on the same days of + * the month) = 18.6 ~= 19 + * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30 + * (29 as initial condition) + * current phase in days = first day phase + days elapsed in year + * 6 moons ~= 177 days + * 177 ~= 8 reported phases * 22 + * + 11/22 for rounding + */ +int +phase_of_the_moon(void) /* 0-7, with 0: new, 4: full */ +{ + struct tm *lt = getlt(); + int epact, diy, goldn; + + diy = lt->tm_yday; + goldn = (lt->tm_year % 19) + 1; + epact = (11 * goldn + 18) % 30; + if ((epact == 25 && goldn > 11) || epact == 24) + epact++; + + return ((((((diy + epact) * 6) + 11) % 177) / 22) & 7); +} + +boolean +friday_13th(void) +{ + struct tm *lt = getlt(); + + /* tm_wday (day of week; 0==Sunday) == 5 => Friday */ + return (boolean) (lt->tm_wday == 5 && lt->tm_mday == 13); +} + +int +night(void) +{ + int hour = getlt()->tm_hour; + + return (hour < 6 || hour > 21); +} + +int +midnight(void) +{ + return (getlt()->tm_hour == 0); +} + +/* Returns current day of week (0==Sunday through 6==Saturday) */ +static int +weekday(void) +{ + struct tm *lt = getlt(); + return lt->tm_wday; +} + +/* Return the number of days since 01/01/0000 on the Gregorian calendar, + * inclusive of the start date but not the current date. + * Argument should be a yyyymmdd int. */ +static int +days_since_epoch(int ymd) +{ + const int year = ymd / 10000; + const int month = (ymd % 10000) / 100; + const int date = ymd % 100; + int monthlen[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 }; + int i; + + /* insert Feb 29 if this is a leap year */ + if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) { + monthlen[1] += 1; + } + + /* baseline: 365 days per year */ + int days = 365 * year; + + /* Gregorian leap years. + * These will ignore that year 0 was a leap year, but that shouldn't matter + * if all you're doing with this function is subtracting two epoch dates + * from each other. + * This does year - 1 so as NOT to count the current year, which the end of + * February may or may not have happened in yet. */ + /* 1 per year divisible by 4. */ + days += (year - 1) / 4; + /* Minus a leap year for each year divisible by 100. */ + days -= (year - 1) / 100; + /* Plus a leap year for each year divisible by 400. */ + days += (year - 1) / 400; + /* Plus the partial amount of days this year. */ + for (i = 0; i < (month - 1); ++i) { + days += monthlen[i]; + } + /* Plus the amount of days so far this month. */ + days += date; + return days; +} + +/* Return a bitmask of holidays it is today. + * This uses a bitmask rather than an enum so that it can extend to "seasons" + * which extend over multiple days and possibly intersect with different + * holidays (for instance, Hanukkah can overlap with Christmas). */ +int +current_holidays(void) +{ + const int ymd = yyyymmdd((time_t) 0); + const int year = ymd / 10000; + const int month = (ymd % 10000) / 100; /* 1..12 */ + const int date = ymd % 100; /* 1..31 */ + const int today_epoch = days_since_epoch(ymd); + const int hour = getlt()->tm_hour; + int retmask = 0; + + /* These cache the value of the last time this function did a full holiday + * computation, to avoid recomputing it all over again unless needed. */ + static int cached_ymd = 0, cached_hour = 0, cached_retmask = 0; + boolean recompute = FALSE; + /* recompute if: + * 1. first ever call (no need to check cached_hour == -1 because cached_ymd + * of 0 is always less than ymd in that case) + * 2. date has changed + * 3. this was last called before 6 pm and it is now after 6 pm (this + * changes the Jewish day which can begin or end a holiday, see below) + */ + if (cached_ymd < ymd || (cached_hour < 18 && hour >= 18)) { + recompute = TRUE; + } + cached_ymd = ymd; + cached_hour = hour; + if (!recompute) { + return cached_retmask; + } + + /* Simple holidays observed yearly on the Gregorian calendar. */ + if (month == 1 && date == 1) { + retmask |= HOLIDAY_NEW_YEARS; + } + if (month == 2 && date == 2) { + retmask |= HOLIDAY_GROUNDHOG_DAY; + } + if (month == 2 && date == 14) { + retmask |= HOLIDAY_VALENTINES_DAY; + } + if (month == 3 && date == 14) { + retmask |= HOLIDAY_PI_DAY; + } + if (month == 4 && date == 1) { + retmask |= HOLIDAY_APRIL_FOOLS; + } + if (month == 7 && date == 1) { + retmask |= HOLIDAY_CANADA_DAY; + } + if (month == 10 && date == 31) { + retmask |= HOLIDAY_HALLOWEEN; + } + if (month == 11 && date >= 1 && date <= 2) { + retmask |= HOLIDAY_LOS_MUERTOS; + } + if (month == 11 && date >= 22 && date <= 28 && weekday() == 4) { + retmask |= HOLIDAY_THANKSGIVING; + } + if (month == 12 && date >= 24 && date <= 25) { + /* counts Christmas Eve too */ + retmask |= HOLIDAY_CHRISTMAS; + } + + /* Now for the tough stuff. */ + { + /* Modified form of Gauss's algorithm to compute the date of Easter, also + * known as the "Computus" algorithm. Relies on a not-inconsiderable amount + * of voodoo magic. */ + int a = year % 19; + int b = year / 100; + int c = year % 100; + int d = b / 4; + int e = b % 4; + int f = (b + 8) / 25; + int g = (b - f + 1) / 3; + int h = ((19 * a) + b - d - g + 15) % 30; + int ii = c / 4; /* note: 'i' declared already in this function */ + int k = c % 4; + int L = (32 + (2 * e) + (2 * ii) - h - k) % 7; + int m = (a + (11 * h) + (22 * L)) / 451; + int eastermonth = (h + L - (7 * m) + 114) / 31; + int easterday = (h + L - (7 * m) + 114) % 31 + 1; + if (month == eastermonth && date == easterday) { + retmask |= HOLIDAY_EASTER; + } + /* Mardi Gras is based on Easter, 47 days before it */ + { + int easterymd = year * 10000 + eastermonth * 100 + easterday; + if (days_since_epoch(easterymd) - today_epoch == 47) { + retmask |= HOLIDAY_MARDI_GRAS; + } + } + } + + { + /* The Islamic calendar begins on 16 Jul 622 in the Julian calendar (19 + * July in the Gregorian calendar). This is only one of many rule-based + * reckonings for it. + * The (lunar) year contains 12 months, in a 30 29 30 29... pattern, + * except in leap years where the final month is 30 days. Leap years + * repeat every 30-year cycle, on years 2, 5, 7, 10, 13, 16, 18, 21, 24, + * 26, 29. This closely but imperfectly approximates the lunar period. + * TODO: in year 3000 or so, check if it's drifted off by a day or two, + * and fix it if it has. + * Begin by calculating a delta number of days since the start of the + * Islamic calendar. + * Every 30-year period contains 10631 days, so we can immediately take + * that delta modulo 10631 to ignore however many 30-year periods that + * is. + * This algorithm is not going to pinpoint Ramadan or other holidays + * exactly, particularly since they depend on manual observation. But it + * should get pretty close. + */ + int lunar_leap[30] = { 0,1,0,0,1,0,1,0,0,1,0,0,1,0,0, + 1,0,1,0,0,1,0,0,1,0,1,0,0,1,0 }; + + /* There are 166 days in the partial year 622. Start with that. */ + int date_delta = today_epoch - days_since_epoch(6220719); + int cycyear; + int month_ctr; + + /* Now cut off as many 30-year periods as possible. */ + date_delta = date_delta % 10631; + /* Then cut off year by year until we reach the current lunar year. */ + for (cycyear = 0; cycyear < 30; ++cycyear) { + int this_year_len = 354 + lunar_leap[cycyear]; + if (date_delta < this_year_len) { + break; + /* cycyear stays in scope so we can tell below if it is + * currently a leap year and need to adjust the last month + * accordingly */ + } + date_delta -= this_year_len; + } + if (date_delta < 0 || cycyear == 30) { + impossible("holiday: bad math finding lunar year"); + date_delta = 0; + } + /* Then using whatever is remaining, find the month and date of the + * current day. */ + int islam_month = 0, islam_date = 0; + for (month_ctr = 0; month_ctr < 12; ++month_ctr) { + int month_len = (month_ctr % 2 == 1) ? 29 : 30; + if (month_ctr == 11) + month_len += lunar_leap[cycyear]; + if (date_delta < month_len) { + islam_month = month_ctr + 1; /* convert back to human-readable */ + islam_date = date_delta + 1; + break; + } + date_delta -= month_len; + } + if (date_delta < 0 || month_ctr >= 12) { + impossible("holiday: bad math finding lunar month/date"); + } + if (islam_month == 9) { + retmask |= HOLIDAY_RAMADAN; + } + if (islam_month == 10 && islam_date == 1) { + retmask |= HOLIDAY_EID_AL_FITR; + } + } + + { + /* The Hebrew calendar is even more complicated than the Islamic + * calendar, but the real problem with it is that it depends on + * obtaining the precise instant of the new moon. This is difficult to + * do because the lunar synodic period fluctuates, and getting a new + * moon wrong by even an hour can throw off the date of the new year by + * up to two days. In lieu of piling a bunch of orbital mechanics + * calculations on top of the Hebrew calendar to make it work for + * arbitrary dates, this will just use dates for the Hebrew new year + * obtained from hebcal.com. + */ + const int heb_new_year[30] = { + 20200919, 20210907, 20220926, 20230916, 20241003, /* 2020-2024 */ + 20250923, 20260912, 20271002, 20280921, 20290910, /* 2025-2029 */ + 20300928, 20310918, 20320906, 20330924, 20340914, /* 2030-2034 */ + 20351004, 20360922, 20370910, 20380930, 20390919, /* 2035-2039 */ + 20400908, 20410926, 20420915, 20431005, 20440922, /* 2040-2044 */ + 20450912, 20461001, 20470921, 20480908, 20490927, /* 2045-2049 */ + }; + + if (year > 2048) { + pline("This game is still being played after 2048? Cool."); + impossible("no data for Hebrew calendar in year %d", year); + } + else if (year < 2020) { + pline("Time travel to the past? Or fix your system clock."); + impossible("no data for Hebrew calendar in year %d", year); + } + else { + int tmp_epoch_today = today_epoch; + /* The Gregorian day begins at midnight, but the Hebrew day begins + * at sunset. Assume sunset is at 6 PM; if it's after that, advance + * the day by 1. */ + if (hour >= 18) { + tmp_epoch_today += 1; + } + /* ymd is no longer safe to use in this computation */ + + /* advance index in heb_new_year until it points at the current + * Hebrew year */ + int index = 0; + int epoch_last_newyear, epoch_next_newyear; + do { + epoch_last_newyear = days_since_epoch(heb_new_year[index]); + epoch_next_newyear = days_since_epoch(heb_new_year[index + 1]); + index++; + } while (epoch_next_newyear <= tmp_epoch_today); + + int heb_year_length = epoch_next_newyear - epoch_last_newyear; + + /* The leap year inserts a 30-day month in the middle of the year, + * represented by a 0 here. */ + int heb_month_len[13] = { 30,29,30,29,30,0,29,30,29,30,29,30,29 }; + if (heb_year_length >= 383 && heb_year_length <= 385) { + heb_month_len[5] = 30; /* insert Adar I */ + } + else if (heb_year_length < 353 || heb_year_length > 355) { + /* if not a leap year, year must be 353 to 355 days */ + impossible("illegal Hebrew year length %d", heb_year_length); + heb_year_length = 354; /* try for graceful fallback */ + } + if (heb_year_length % 10 == 3) { /* short year */ + heb_month_len[2] -= 1; /* deduct 1 day from Kislev */ + } + else if (heb_year_length % 10 == 5) { /* full year */ + heb_month_len[1] += 1; /* add 1 day to Cheshvan */ + } + int hebrew_month = 0, hebrew_date = 0; + int date_delta = tmp_epoch_today - epoch_last_newyear; + int month_ctr; + for (month_ctr = 0; month_ctr < 13; ++month_ctr) { + if (date_delta < heb_month_len[month_ctr]) { + hebrew_month = month_ctr + 1; + hebrew_date = date_delta + 1; + break; + } + date_delta -= heb_month_len[month_ctr]; + } + if (date_delta < 0 || month_ctr == 13) { + impossible("holiday: bad math finding hebrew month/date"); + } + if (hebrew_month == 1 && hebrew_date >= 1 && hebrew_date <= 2) { + retmask |= HOLIDAY_ROSH_HASHANAH; + } + if (hebrew_month == 1 && hebrew_date == 10) { + retmask |= HOLIDAY_YOM_KIPPUR; + } + /* There are a number of different ways to observe Passover; this + * tracks the first two days but not the rest of its week. */ + if (hebrew_month == 8 && hebrew_date >= 15 && hebrew_date <= 16) { + retmask |= HOLIDAY_PASSOVER; + } + /* Judging the end date of Hanukkah depends on whether Kislev was + * reduced in length or not. */ + if ((hebrew_month == 3 && hebrew_date >= 25) + || (hebrew_month == 4 + && (hebrew_date <= (heb_month_len[2] == 30 ? 2 : 3)))) { + retmask |= HOLIDAY_HANUKKAH; + } + } + } + cached_retmask = retmask; /* for next time */ + return retmask; +} + +/* calendar.c */ + diff --git a/src/cmd.c b/src/cmd.c index 534fedaeec..b386c49e53 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 cmd.c $NHDT-Date: 1684791777 2023/05/22 21:42:57 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.677 $ */ +/* 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. */ @@ -15,10 +15,6 @@ #define NR_OF_EOFS 20 #endif #endif -#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) -static int wiz_display_macros(void); -static int wiz_mon_diff(void); -#endif #ifdef DUMB /* stuff commented out in extern.h, but needed here */ extern int doapply(void); /**/ @@ -98,101 +94,71 @@ extern int dozap(void); /**/ extern int doorganize(void); /**/ #endif /* DUMB */ -static const char *ecname_from_fn(int (*)(void)); -static int dosuspend_core(void); -static int dosh_core(void); -static int doholidays(void); -static int doherecmdmenu(void); -static int dotherecmdmenu(void); -static int doprev_message(void); -static int timed_occupation(void); -static boolean can_do_extcmd(const struct ext_func_tab *); -static int doshout(void); -static int dotravel(void); -static int dotravel_target(void); -static int doclicklook(void); -static int domouseaction(void); -static int doterrain(void); -static int wiz_wish(void); -static int wiz_identify(void); -static int wiz_map(void); -static int wiz_makemap(void); -static int wiz_genesis(void); -static int wiz_where(void); -static int wiz_detect(void); -static int wiz_panic(void); -static int wiz_fuzzer(void); -static int wiz_polyself(void); -static int wiz_kill(void); -static int wiz_load_lua(void); -static int wiz_level_tele(void); -static int wiz_level_change(void); -static int wiz_flip_level(void); -static int wiz_show_seenv(void); -static int wiz_show_vision(void); -static int wiz_smell(void); -static int wiz_intrinsic(void); -static int wiz_show_wmodes(void); -static int wiz_show_stats(void); -static int wiz_rumor_check(void); -static int wiz_migrate_mons(void); - -static void makemap_unmakemon(struct monst *, boolean); -static void makemap_remove_mons(void); -static void wiz_map_levltyp(void); -static void wiz_levltyp_legend(void); +staticfn int dosuspend_core(void); +staticfn int dosh_core(void); +staticfn int doholidays(void); +staticfn int doherecmdmenu(void); +staticfn int dotherecmdmenu(void); +staticfn int doprev_message(void); +staticfn int timed_occupation(void); +staticfn boolean can_do_extcmd(const struct ext_func_tab *); +staticfn int doshout(void); +staticfn int dotravel(void); +staticfn int dotravel_target(void); +staticfn int doclicklook(void); +staticfn boolean yn_menuable_resp(const char *); +staticfn void yn_func_menu_opt(winid, char, const char *, char); +staticfn boolean yn_function_menu(const char *, const char *, char, char *); +staticfn int domouseaction(void); +staticfn int doterrain(void); +staticfn boolean u_have_seen_whole_selection(struct selectionvar *); +staticfn boolean u_have_seen_bounds_selection(struct selectionvar *); +staticfn boolean u_can_see_whole_selection(struct selectionvar *); +staticfn int dolookaround_floodfill_findroom(coordxy, coordxy); +staticfn void lookaround_known_room(coordxy, coordxy); + #if defined(__BORLANDC__) && !defined(_WIN32) extern void show_borlandc_stats(winid); #endif -static int size_monst(struct monst *, boolean); -static int size_obj(struct obj *); -static void count_obj(struct obj *, long *, long *, boolean, boolean); -static void obj_chain(winid, const char *, struct obj *, boolean, long *, - long *); -static void mon_invent_chain(winid, const char *, struct monst *, long *, - long *); -static void mon_chain(winid, const char *, struct monst *, boolean, long *, - long *); -static void contained_stats(winid, const char *, long *, long *); -static void misc_stats(winid, long *, long *); -static void you_sanity_check(void); -static boolean accept_menu_prefix(const struct ext_func_tab *); -static void reset_cmd_vars(boolean); - -static void mcmd_addmenu(winid, int, const char *); -static int there_cmd_menu_self(winid, coordxy, coordxy, int *); -static int there_cmd_menu_next2u(winid, coordxy, coordxy, int, int *); -static int there_cmd_menu_far(winid, coordxy, coordxy, int); -static int there_cmd_menu_common(winid, coordxy, coordxy, int, int *); -static void act_on_act(int, coordxy, coordxy); -static char there_cmd_menu(coordxy, coordxy, int); -static char here_cmd_menu(void); - -static char readchar_core(coordxy *, coordxy *, int *); -static char *parse(void); -static void show_direction_keys(winid, char, boolean); -static boolean help_dir(char, uchar, const char *); -static int QSORTCALLBACK migrsort_cmp(const genericptr, const genericptr); -static void list_migrating_mons(d_level *); - -static void handler_rebind_keys_add(boolean); -static boolean bind_key_fn(uchar, int (*)(void)); -static void commands_init(void); -static boolean keylist_func_has_key(const struct ext_func_tab *, boolean *); -static int keylist_putcmds(winid, boolean, int, int, boolean *); -static const char *spkey_name(int); - -static int (*timed_occ_fn)(void); -static char *doc_extcmd_flagstr(winid, const struct ext_func_tab *); +staticfn boolean accept_menu_prefix(const struct ext_func_tab *); +staticfn void reset_cmd_vars(boolean); + +staticfn void mcmd_addmenu(winid, int, const char *); +staticfn int there_cmd_menu_self(winid, coordxy, coordxy, int *); +staticfn int there_cmd_menu_next2u(winid, coordxy, coordxy, int, int *); +staticfn int there_cmd_menu_far(winid, coordxy, coordxy, int); +staticfn int there_cmd_menu_common(winid, coordxy, coordxy, int, int *); +staticfn void act_on_act(int, coordxy, coordxy); +staticfn char there_cmd_menu(coordxy, coordxy, int); +staticfn char here_cmd_menu(void); + +staticfn char readchar_core(coordxy *, coordxy *, int *); +staticfn int parse(void); +staticfn void show_direction_keys(winid, char, boolean); +staticfn boolean help_dir(char, uchar, const char *); + +staticfn void handler_rebind_keys_add(boolean); +staticfn boolean bind_key_fn(uchar, int (*)(void)); +staticfn void commands_init(void); +staticfn boolean keylist_func_has_key(const struct ext_func_tab *, boolean *); +staticfn int keylist_putcmds(winid, boolean, int, int, boolean *); +staticfn const char *spkey_name(int); + +staticfn int (*timed_occ_fn)(void); +staticfn char *doc_extcmd_flagstr(winid, const struct ext_func_tab *); +staticfn int dummyfunction(void); static const char *readchar_queue = ""; -/* for rejecting attempts to use wizard mode commands */ -static const char unavailcmd[] = "Unavailable command '%s'."; + +/* for rejecting attempts to use wizard mode commands + * Also used in wizcmds.c */ +const char unavailcmd[] = "Unavailable command '%s'."; + /* for rejecting #if !SHELL, !SUSPEND */ static const char cmdnotavail[] = "'%s' command not available."; /* the #prevmsg command */ -static int +staticfn int doprev_message(void) { (void) nh_doprev_message(); @@ -200,7 +166,7 @@ doprev_message(void) } /* Count down by decrementing multi */ -static int +staticfn int timed_occupation(void) { (*timed_occ_fn)(); @@ -251,18 +217,30 @@ set_occupation(int (*fn)(void), const char *txt, cmdcount_nht xtime) void cmdq_print(int q) { - struct _cmd_queue *cq = gc.command_queue[q]; char buf[QBUFSZ]; + struct _cmd_queue *cq = gc.command_queue[q]; pline("CQ:%i", q); while (cq) { switch (cq->typ) { - case CMDQ_KEY: pline("(key:%s)", key2txt(cq->key, buf)); break; - case CMDQ_EXTCMD: pline("(extcmd:#%s)", cq->ec_entry->ef_txt); break; - case CMDQ_DIR: pline("(dir:%i,%i,%i)", cq->dirx, cq->diry, cq->dirz); break; - case CMDQ_USER_INPUT: pline1("(userinput)"); break; - case CMDQ_INT: pline("(int:%i)", cq->intval); break; - default: pline("(ERROR:%i)",cq->typ); break; + case CMDQ_KEY: + pline("(key:%s)", key2txt(cq->key, buf)); + break; + case CMDQ_EXTCMD: + pline("(extcmd:#%s)", cq->ec_entry->ef_txt); + break; + case CMDQ_DIR: + pline("(dir:%i,%i,%i)", cq->dirx, cq->diry, cq->dirz); + break; + case CMDQ_USER_INPUT: + pline("(userinput)"); + break; + case CMDQ_INT: + pline("(int:%i)", cq->intval); + break; + default: + pline("(ERROR:%i)",cq->typ); + break; } cq = cq->next; } @@ -464,7 +442,7 @@ cmdq_clear(int q) char pgetchar(void) /* courtesy of aeb@cwi.nl */ { - register int ch = '\0'; + int ch = '\0'; if (iflags.debug_fuzzer) return randomkey(); @@ -479,7 +457,7 @@ extcmd_initiator(void) return gc.Cmd.extcmd_char; } -static boolean +staticfn boolean can_do_extcmd(const struct ext_func_tab *extcmd) { int ecflags = extcmd->flags; @@ -488,9 +466,12 @@ can_do_extcmd(const struct ext_func_tab *extcmd) lua_getglobal(gl.luacore, "nh_callback_run"); lua_pushstring(gl.luacore, nhcb_name[NHCB_CMD_BEFORE]); lua_pushstring(gl.luacore, extcmd->ef_txt); - nhl_pcall(gl.luacore, 2, 1); - if (!lua_toboolean(gl.luacore, -1)) + nhl_pcall_handle(gl.luacore, 2, 1, "can_do_extcmd", NHLpa_panic); + if (!lua_toboolean(gl.luacore, -1)) { + lua_settop(gl.luacore, 0); return FALSE; + } + lua_settop(gl.luacore, 0); } if (!wizard && (ecflags & WIZMODECMD)) { @@ -537,26 +518,22 @@ doextcmd(void) } /* format extended command flags for display */ -static char * +staticfn char * doc_extcmd_flagstr( winid menuwin, const struct ext_func_tab *efp) /* if Null, add a footnote to the menu */ { static char Abuf[10]; /* 5 would suffice: {'[','m','A',']','\0'} */ - int clr = 0; /* note: tag shown for menu prefix is 'm' even if m-prefix action has been bound to some other key */ if (!efp) { char qbuf[QBUFSZ]; - anything any = cg.zeroany; - add_menu(menuwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, - "[A] Command autocompletes", MENU_ITEMFLAGS_NONE); + add_menu_str(menuwin, "[A] Command autocompletes"); Sprintf(qbuf, "[m] Command accepts '%s' prefix", visctrl(cmd_from_func(do_reqmenu))); - add_menu(menuwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, qbuf, - MENU_ITEMFLAGS_NONE); + add_menu_str(menuwin, qbuf); return (char *) 0; } else { boolean mprefix = accept_menu_prefix(efp), @@ -582,7 +559,7 @@ doc_extcmd_flagstr( int doextlist(void) { - register const struct ext_func_tab *efp = (struct ext_func_tab *) 0; + const struct ext_func_tab *efp = (struct ext_func_tab *) 0; char buf[BUFSZ], searchbuf[BUFSZ], descbuf[BUFSZ], promptbuf[QBUFSZ]; const char *cmd_desc; winid menuwin; @@ -593,7 +570,7 @@ doextlist(void) boolean redisplay = TRUE, search = FALSE; static const char *const headings[] = { "Extended commands", "Debugging Extended Commands" }; - int clr = 0; + int clr = NO_COLOR; searchbuf[0] = '\0'; menuwin = create_nhwindow(NHW_MENU); @@ -602,11 +579,8 @@ doextlist(void) redisplay = FALSE; any = cg.zeroany; start_menu(menuwin, MENU_BEHAVE_STANDARD); - add_menu(menuwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, - "Extended Commands List", - MENU_ITEMFLAGS_NONE); - add_menu(menuwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, - "", MENU_ITEMFLAGS_NONE); + add_menu_str(menuwin, "Extended Commands List"); + add_menu_str(menuwin, ""); Sprintf(buf, "Switch to %s commands that don't autocomplete", menumode ? "including" : "excluding"); @@ -644,9 +618,7 @@ doextlist(void) : "Switch to showing all alphabetically, including debugging commands", MENU_ITEMFLAGS_NONE); } - any = cg.zeroany; - add_menu(menuwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, - "", MENU_ITEMFLAGS_NONE); + add_menu_str(menuwin, ""); menushown[0] = menushown[1] = 0; n = 0; for (pass = 0; pass <= 1; ++pass) { @@ -670,9 +642,9 @@ doextlist(void) continue; if (!onelist && pass != wizc) continue; - /* command descripton might get modified on the fly */ + /* command description might get modified on the fly */ cmd_desc = efp->ef_desc; - /* suppress part of the descripton for #genocided if it + /* suppress part of the description for #genocided if it doesn't apply during the current game */ if (!wizard && !discover && (efp->flags & GENERALCMD) != 0 /* minor optimization */ @@ -697,26 +669,21 @@ doextlist(void) results menu. */ if (!menushown[pass]) { Strcpy(buf, headings[pass]); - add_menu(menuwin, &nul_glyphinfo, &any, 0, 0, - iflags.menu_headings, clr, buf, - MENU_ITEMFLAGS_NONE); + add_menu_heading(menuwin, buf); menushown[pass] = 1; } /* longest ef_txt at present is "wizrumorcheck" (13 chars); 2nd field will be " " or " [A]" or " [m]" or "[mA]" */ Sprintf(buf, " %-14s %4s %s", efp->ef_txt, doc_extcmd_flagstr(menuwin, efp), cmd_desc); - add_menu(menuwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, buf, MENU_ITEMFLAGS_NONE); + add_menu_str(menuwin, buf); ++n; } if (n) - add_menu(menuwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "", MENU_ITEMFLAGS_NONE); + add_menu_str(menuwin, ""); } if (*searchbuf && !n) - add_menu(menuwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "no matches", MENU_ITEMFLAGS_NONE); + add_menu_str(menuwin, "no matches"); else (void) doc_extcmd_flagstr(menuwin, (struct ext_func_tab *) 0); @@ -794,7 +761,7 @@ extcmd_via_menu(void) int accelerator, prevaccelerator; int matchlevel = 0; boolean wastoolong, one_per_line; - int clr = 0; + int clr = NO_COLOR; ret = 0; cbuf[0] = '\0'; @@ -818,7 +785,7 @@ extcmd_via_menu(void) "Exceeded %d extended commands in doextcmd() menu; 'extmenu' disabled.", MAX_EXT_CMD); #endif /* NH_DEVEL_STATUS != NH_STATUS_RELEASED */ - iflags.extmenu = 0; + iflags.extmenu = FALSE; return -1; } } @@ -840,7 +807,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]; @@ -925,7 +892,8 @@ domonability(void) char c = '\0'; if (might_hide && webmaker(uptr)) { - c = yn_function("Hide [h] or spin a web [s]?", "hsq", 'q', TRUE); + c = yn_function("Hide [h] or spin a web [s]?", + hidespinchars, 'q', TRUE); if (c == 'q' || c == '\033') return ECMD_OK; } @@ -949,8 +917,13 @@ domonability(void) if (IS_FOUNTAIN(levl[u.ux][u.uy].typ)) { if (split_mon(&gy.youmonst, (struct monst *) 0)) dryup(u.ux, u.uy, TRUE); - } else + } else if (is_pool(u.ux, u.uy)) { + /* is_pool: might be wearing water walking boots or amulet of + magical breathing */ + (void) split_mon(&gy.youmonst, (struct monst *) 0); + } else { There("is no fountain here."); + } } else if (is_unicorn(uptr)) { use_unicorn_horn((struct obj **) 0, FALSE); return ECMD_TIME; @@ -965,6 +938,9 @@ domonability(void) } else if (attacktype(uptr, AT_EXPL)) { You("explode!"); return doselfexplode(); + } else if (u.usteed && can_breathe(u.usteed->data)) { + (void) pet_ranged_attk(u.usteed, TRUE); + return ECMD_TIME; } else if (Upolyd) { pline("Any special ability you may have is purely reflexive."); } else { @@ -983,10 +959,7 @@ enter_explore_mode(void) } else { const char *oldmode = !wizard ? "normal game" : "debug mode"; -#ifdef SYSCF -#if defined(UNIX) - if (!sysopt.explorers || !sysopt.explorers[0] - || !check_user_string(sysopt.explorers)) { + if (!authorize_explore_mode()) { if (!wizard) { You("cannot access explore mode."); return ECMD_OK; @@ -996,8 +969,6 @@ enter_explore_mode(void) /* keep going */ } } -#endif -#endif pline("Beware! From explore mode there will be no return to %s,", oldmode); if (paranoid_query(ParanoidQuit, @@ -1014,124 +985,6 @@ enter_explore_mode(void) return ECMD_OK; } -/* #wizwish command - wish for something */ -static int -wiz_wish(void) /* Unlimited wishes for debug mode by Paul Polderman */ -{ - if (wizard) { - boolean save_verbose = flags.verbose; - - flags.verbose = FALSE; - makewish(); - flags.verbose = save_verbose; - (void) encumber_msg(); - } else - pline(unavailcmd, ecname_from_fn(wiz_wish)); - return ECMD_OK; -} - -/* #wizidentify command - reveal and optionally identify hero's inventory */ -static int -wiz_identify(void) -{ - if (wizard) { - iflags.override_ID = (int) cmd_from_func(wiz_identify); - /* command remapping might leave #wizidentify as the only way - to invoke us, in which case cmd_from_func() will yield NUL; - it won't matter to display_inventory()/display_pickinv() - if ^I invokes some other command--what matters is that - display_pickinv() and xname() see override_ID as nonzero */ - if (!iflags.override_ID) - iflags.override_ID = C('I'); - (void) display_inventory((char *) 0, FALSE); - iflags.override_ID = 0; - } else - pline(unavailcmd, ecname_from_fn(wiz_identify)); - return ECMD_OK; -} - -/* used when wiz_makemap() gets rid of monsters for the old incarnation of - a level before creating a new incarnation of it */ -static void -makemap_unmakemon(struct monst *mtmp, boolean migratory) -{ - int ndx = monsndx(mtmp->data); - - /* uncreate any unique monster so that it is eligible to be remade - on the new incarnation of the level; ignores DEADMONSTER() [why?] */ - if (mtmp->data->geno & G_UNIQ) - gm.mvitals[ndx].mvflags &= ~G_EXTINCT; - if (gm.mvitals[ndx].born) - gm.mvitals[ndx].born--; - - /* vault is going away; get rid of guard who might be in play or - be parked at <0,0>; for the latter, might already be flagged as - dead but is being kept around because of the 'isgd' flag */ - if (mtmp->isgd) { - mtmp->isgd = 0; /* after this, fall through to mongone() */ - } else if (DEADMONSTER(mtmp)) { - return; /* already set to be discarded */ - } else if (mtmp->isshk && on_level(&u.uz, &ESHK(mtmp)->shoplevel)) { - setpaid(mtmp); - } - if (migratory) { - /* caller has removed 'mtmp' from migrating_mons; put it onto fmon - so that dmonsfree() bookkeeping for number of dead or removed - monsters won't get out of sync; it is not on the map but - mongone() -> m_detach() -> mon_leaving_level() copes with that */ - mtmp->mstate |= MON_OFFMAP; - mtmp->mstate &= ~(MON_MIGRATING | MON_LIMBO); - mtmp->nmon = fmon; - fmon = mtmp; - } - mongone(mtmp); -} - -/* get rid of the all the monsters on--or intimately involved with--current - level; used when #wizmakemap destroys the level before replacing it */ -static void -makemap_remove_mons(void) -{ - struct monst *mtmp, **mprev; - - /* keep steed and other adjacent pets after releasing them - from traps, stopping eating, &c as if hero were ascending */ - keepdogs(TRUE, FALSE); /* (pets-only; normally we'd be using 'FALSE') */ - /* get rid of all the monsters that didn't make it to 'mydogs' */ - for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { - /* if already dead, dmonsfree(below) will get rid of it */ - if (DEADMONSTER(mtmp)) - continue; - makemap_unmakemon(mtmp, FALSE); - } - /* some monsters retain details of this level in mon->mextra; that - data becomes invalid when the level is replaced by a new one; - get rid of them now if migrating or already arrived elsewhere; - [when on their 'home' level, the previous loop got rid of them; - if they aren't actually migrating but have been placed on some - 'away' level, such monsters are treated like the Wizard: kept - on migrating monsters list, scheduled to migrate back to their - present location instead of being saved with whatever level they - happen to be on; see keepdogs() and keep_mon_accessible(dog.c)] */ - for (mprev = &gm.migrating_mons; (mtmp = *mprev) != 0; ) { - if (mtmp->mextra - && ((mtmp->isshk && on_level(&u.uz, &ESHK(mtmp)->shoplevel)) - || (mtmp->ispriest && on_level(&u.uz, &EPRI(mtmp)->shrlevel)) - || (mtmp->isgd && on_level(&u.uz, &EGD(mtmp)->gdlevel)))) { - *mprev = mtmp->nmon; - makemap_unmakemon(mtmp, TRUE); - } else { - mprev = &mtmp->nmon; - } - } - /* release dead and 'unmade' monsters */ - dmonsfree(); - if (fmon) { - impossible("makemap_remove_mons: 'fmon' did not get emptied?"); - } - return; -} - void makemap_prepost(boolean pre) { @@ -1150,11 +1003,11 @@ makemap_prepost(boolean pre) if (Is_mineend_level(&u.uz)) { if (remove_achievement(ACH_MINE_PRIZE)) pline(Unachieve, "Mine's-end"); - gc.context.achieveo.mines_prize_oid = 0; + svc.context.achieveo.mines_prize_oid = 0; } else if (Is_sokoend_level(&u.uz)) { if (remove_achievement(ACH_SOKO_PRIZE)) pline(Unachieve, "Soko-prize"); - gc.context.achieveo.soko_prize_oid = 0; + svc.context.achieveo.soko_prize_oid = 0; } } if (Punished) { @@ -1164,22 +1017,23 @@ makemap_prepost(boolean pre) /* reset lock picking unless it's for a carried container */ maybe_reset_pick((struct obj *) 0); /* reset interrupted digging if it was taking place on this level */ - if (on_level(&gc.context.digging.level, &u.uz)) - (void) memset((genericptr_t) &gc.context.digging, 0, + if (on_level(&svc.context.digging.level, &u.uz)) + (void) memset((genericptr_t) &svc.context.digging, 0, sizeof (struct dig_info)); /* reset cached targets */ iflags.travelcc.x = iflags.travelcc.y = 0; /* travel destination */ - gc.context.polearm.hitmon = (struct monst *) 0; /* polearm target */ + svc.context.polearm.hitmon = (struct monst *) 0; /* polearm target */ /* escape from trap */ reset_utrap(FALSE); check_special_room(TRUE); /* room exit */ - (void) memset((genericptr_t)&gd.dndest, 0, sizeof (dest_area)); - (void) memset((genericptr_t)&gu.updest, 0, sizeof (dest_area)); + (void) memset((genericptr_t) &svd.dndest, 0, sizeof (dest_area)); + (void) memset((genericptr_t) &svu.updest, 0, sizeof (dest_area)); u.ustuck = (struct monst *) 0; u.uswallow = u.uswldtim = 0; set_uinwater(0); /* u.uinwater = 0 */ u.uundetected = 0; /* not hidden, even if means are available */ dmonsfree(); /* purge dead monsters from 'fmon' */ + dobjsfree(); /* discard current level; "saving" is used to release dynamic data */ zero_nhfile(&tmpnhfp); /* also sets fd to -1 as desired */ @@ -1214,1060 +1068,315 @@ makemap_prepost(boolean pre) } } -/* #wizmakemap - discard current dungeon level and replace with a new one */ -static int -wiz_makemap(void) +/* temporary? hack, since level type codes aren't the same as screen + symbols and only the latter have easily accessible descriptions. + Also used by wizcmds.c */ +const char *levltyp[MAX_TYPE + 2] = { + "stone", "vertical wall", "horizontal wall", "top-left corner wall", + "top-right corner wall", "bottom-left corner wall", + "bottom-right corner wall", "cross wall", "tee-up wall", "tee-down wall", + "tee-left wall", "tee-right wall", "drawbridge wall", "tree", + "secret door", "secret corridor", "pool", "moat", "water", + "drawbridge up", "lava pool", "lava wall", "iron bars", "door", + "corridor", "room", "stairs", "ladder", "fountain", "throne", "sink", + "grave", "altar", "ice", "grass", "magic platform", "drawbridge down", + "air", "cloud", + /* not a real terrain type, but used for undiggable stone + by wiz_map_levltyp() */ + "unreachable/undiggable", + /* padding in case the number of entries above is odd */ + "" +}; + +const char * +levltyp_to_name(int typ) { - if (wizard) { - makemap_prepost(TRUE); - /* create a new level; various things like bestowing a guardian - angel on Astral or setting off alarm on Ft.Ludios are handled - by goto_level(do.c) so won't occur for replacement levels */ - mklev(); - makemap_prepost(FALSE); - } else { - pline(unavailcmd, ecname_from_fn(wiz_makemap)); - } - return ECMD_OK; + if (typ >= 0 && typ < MAX_TYPE) + return levltyp[typ]; + return NULL; } -/* the #wizmap command - reveal the level map - and any traps or engravings on it */ -static int -wiz_map(void) +/* #terrain command -- show known map, inspired by crawl's '|' command */ +staticfn int +doterrain(void) { - if (wizard) { - struct trap *t; - struct engr *ep; - long save_Hconf = HConfusion, save_Hhallu = HHallucination; + winid men; + menu_item *sel; + anything any; + int n; + int which; + int clr = NO_COLOR; - HConfusion = HHallucination = 0L; - for (t = gf.ftrap; t != 0; t = t->ntrap) { - t->tseen = 1; - map_trap(t, TRUE); - } - for (ep = head_engr; ep != 0; ep = ep->nxt_engr) { - map_engraving(ep, TRUE); + /* this used to be done each time vision was recalculated, so would + always be up to date (hopefully); now we do it on demand instead */ + recalc_mapseen(); + + /* + * normal play: choose between known map without mons, obj, and traps + * (to see underlying terrain only), or + * known map without mons and objs (to see traps under mons and objs), or + * known map without mons (to see objects under monsters); + * explore mode: normal choices plus full map (w/o mons, objs, traps); + * wizard mode: normal and explore choices plus + * a dump of the internal levl[][].typ codes w/ level flags, or + * a legend for the levl[][].typ codes dump + */ + men = create_nhwindow(NHW_MENU); + start_menu(men, MENU_BEHAVE_STANDARD); + any = cg.zeroany; + any.a_int = 1; + add_menu(men, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, + "known map without monsters, objects, and traps", + MENU_ITEMFLAGS_SELECTED); + any.a_int = 2; + add_menu(men, &nul_glyphinfo, &any, 0, 0, ATR_NONE, + clr, "known map without monsters and objects", + MENU_ITEMFLAGS_NONE); + any.a_int = 3; + add_menu(men, &nul_glyphinfo, &any, 0, 0, ATR_NONE, + clr, "known map without monsters", + MENU_ITEMFLAGS_NONE); + any.a_int = 7; + add_menu(men, &nul_glyphinfo, &any, 0, 0, ATR_NONE, + clr, "known terrain showing spaces you have visited", + MENU_ITEMFLAGS_NONE); + if (discover || wizard) { + any.a_int = 4; + add_menu(men, &nul_glyphinfo, &any, 0, 0, ATR_NONE, + clr, "full map without monsters, objects, and traps", + MENU_ITEMFLAGS_NONE); + if (wizard) { + any.a_int = 5; + add_menu(men, &nul_glyphinfo, &any, 0, 0, ATR_NONE, + clr, "internal levl[][].typ codes in base-36", + MENU_ITEMFLAGS_NONE); + any.a_int = 6; + add_menu(men, &nul_glyphinfo, &any, 0, 0, ATR_NONE, + clr, "legend of base-36 levl[][].typ codes", + MENU_ITEMFLAGS_NONE); } - do_mapping(); - HConfusion = save_Hconf; - HHallucination = save_Hhallu; - } else - pline(unavailcmd, ecname_from_fn(wiz_map)); - return ECMD_OK; -} + } + end_menu(men, "View which?"); -/* #wizgenesis - generate monster(s); a count prefix will be honored */ -static int -wiz_genesis(void) -{ - if (wizard) { - boolean mongen_saved = iflags.debug_mongen; + n = select_menu(men, PICK_ONE, &sel); + destroy_nhwindow(men); + /* + * n < 0: player used ESC to cancel; + * n == 0: preselected entry was explicitly chosen and got toggled off; + * n == 1: preselected entry was implicitly chosen via |; + * n == 2: another entry was explicitly chosen, so skip preselected one. + */ + which = (n < 0) ? -1 : (n == 0) ? 1 : sel[0].item.a_int; + if (n > 1 && which == 1) + which = sel[1].item.a_int; + if (n > 0) + free((genericptr_t) sel); - iflags.debug_mongen = FALSE; - (void) create_particular(); - iflags.debug_mongen = mongen_saved; - } else - pline(unavailcmd, ecname_from_fn(wiz_genesis)); - return ECMD_OK; + switch (which) { + case 1: /* known map */ + reveal_terrain(TER_MAP); + break; + case 2: /* known map with known traps */ + reveal_terrain(TER_MAP | TER_TRP); + break; + case 3: /* known map with known traps and objects */ + reveal_terrain(TER_MAP | TER_TRP | TER_OBJ); + break; + case 4: /* full map */ + reveal_terrain(TER_MAP | TER_FULL); + break; + case 5: /* map internals */ + wiz_map_levltyp(); + break; + case 6: /* internal details */ + wiz_levltyp_legend(); + break; + case 7: /* full map with visited spaces */ + reveal_terrain(TER_MAP | TER_VISIT); + default: + break; + } + return ECMD_OK; /* no time elapses */ } -/* #wizwhere command - display dungeon layout */ -static int -wiz_where(void) +/* has hero seen all locations in selection? */ +staticfn boolean +u_have_seen_whole_selection(struct selectionvar *sel) { - if (wizard) - (void) print_dungeon(FALSE, (schar *) 0, (xint16 *) 0); - else - pline(unavailcmd, ecname_from_fn(wiz_where)); - return ECMD_OK; -} + coordxy x, y; + NhRect rect = cg.zeroNhRect; -/* the #wizdetect command - detect secret doors, traps, hidden monsters */ -static int -wiz_detect(void) -{ - if (wizard) - (void) findit(); - else - pline(unavailcmd, ecname_from_fn(wiz_detect)); - return ECMD_OK; + selection_getbounds(sel, &rect); + + for (x = rect.lx; x <= rect.hx; x++) + for (y = rect.ly; y <= rect.hy; y++) + if (isok(x,y) && selection_getpoint(x, y, sel) + && glyph_at(x, y) == GLYPH_UNEXPLORED) + return FALSE; + + return TRUE; } -/* the #wizkill command - pick targets and reduce them to 0HP; - by default, the hero is credited/blamed; use 'm' prefix to avoid that */ -static int -wiz_kill(void) +/* has hero seen all location of the rectangular outline in the selection */ +staticfn boolean +u_have_seen_bounds_selection(struct selectionvar *sel) { - struct monst *mtmp; - coord cc; - int ans; - char c, qbuf[QBUFSZ]; - const char *prompt = "Pick first monster to slay"; - boolean save_verbose = flags.verbose, - save_autodescribe = iflags.autodescribe; - d_level uarehere = u.uz; - - cc.x = u.ux, cc.y = u.uy; - for (;;) { - pline("%s:", prompt); - prompt = "Next monster"; - - flags.verbose = FALSE; - iflags.autodescribe = TRUE; - ans = getpos(&cc, TRUE, "a monster"); - flags.verbose = save_verbose; - iflags.autodescribe = save_autodescribe; - if (ans < 0 || cc.x < 1) - break; + coordxy x, y; + NhRect rect = cg.zeroNhRect; - mtmp = 0; - if (u_at(cc.x, cc.y)) { - if (u.usteed) { - Sprintf(qbuf, "Kill %.110s?", mon_nam(u.usteed)); - if ((c = ynq(qbuf)) == 'q') - break; - if (c == 'y') - mtmp = u.usteed; - } - if (!mtmp) { - Sprintf(qbuf, "%s?", Role_if(PM_SAMURAI) ? "Perform seppuku" - : "Commit suicide"); - if (paranoid_query(TRUE, qbuf)) { - Sprintf(gk.killer.name, "%s own player", uhis()); - gk.killer.format = KILLED_BY; - done(DIED); - } - break; - } - } else if (u.uswallow) { - mtmp = next2u(cc.x, cc.y) ? u.ustuck : 0; - } else { - mtmp = m_at(cc.x, cc.y); - } + selection_getbounds(sel, &rect); - /* whether there's an unseen monster here or not, player will know - that there's no monster here after the kill or failed attempt; - let hero know too */ - (void) unmap_invisible(cc.x, cc.y); - - if (mtmp) { - /* we don't require that the monster be seen or sensed so - we issue our own message in order to name it in case it - isn't; note that if it triggers other kills, those might - be referred to as "it" */ - int tame = !!mtmp->mtame, - seen = (canspotmon(mtmp) || (u.uswallow && mtmp == u.ustuck)), - flgs = (SUPPRESS_IT | SUPPRESS_HALLUCINATION - | ((tame && has_mgivenname(mtmp)) ? SUPPRESS_SADDLE - : 0)), - articl = tame ? ARTICLE_YOUR : seen ? ARTICLE_THE : ARTICLE_A; - const char *adjs = tame ? (!seen ? "poor, unseen" : "poor") - : (!seen ? "unseen" : (const char *) 0); - char *Mn = x_monnam(mtmp, articl, adjs, flgs, FALSE); - - if (!iflags.menu_requested) { - /* normal case: hero is credited/blamed */ - You("%s %s!", nonliving(mtmp->data) ? "destroy" : "kill", Mn); - xkilled(mtmp, XKILL_NOMSG); - } else { /* 'm'-prefix */ - /* we know that monsters aren't moving because player has - just issued this #wizkill command, but if 'mtmp' is a - gas spore whose explosion kills any other monsters we - need to have the mon_moving flag be True in order to - avoid blaming or crediting hero for their deaths */ - gc.context.mon_moving = TRUE; - pline("%s is %s.", upstart(Mn), - nonliving(mtmp->data) ? "destroyed" : "killed"); - /* Null second arg suppresses the usual message */ - monkilled(mtmp, (char *) 0, AD_PHYS); - gc.context.mon_moving = FALSE; - } - /* end targetting loop if an engulfer dropped hero onto a level- - changing trap */ - if (u.utotype || !on_level(&u.uz, &uarehere)) - break; - } else { - There("is no monster there."); - break; - } + for (x = rect.lx; x <= rect.hx; x++) { + y = rect.ly; + if (isok(x,y) && selection_getpoint(x, y, sel) + && glyph_at(x, y) == GLYPH_UNEXPLORED) + return FALSE; + y = rect.hy; + if (isok(x,y) && selection_getpoint(x, y, sel) + && glyph_at(x, y) == GLYPH_UNEXPLORED) + return FALSE; + } + for (y = rect.ly; y <= rect.hy; y++) { + x = rect.lx; + if (isok(x,y) && selection_getpoint(x, y, sel) + && glyph_at(x, y) == GLYPH_UNEXPLORED) + return FALSE; + x = rect.hx; + if (isok(x,y) && selection_getpoint(x, y, sel) + && glyph_at(x, y) == GLYPH_UNEXPLORED) + return FALSE; } - /* distinction between ECMD_CANCEL and ECMD_OK is unimportant here */ - return ECMD_OK; /* no time elapses */ -} - -/* the #wizloadlua command - load an arbitrary lua file */ -static int -wiz_load_lua(void) -{ - if (wizard) { - char buf[BUFSZ]; - nhl_sandbox_info sbi = {NHL_SB_SAFE | NHL_SB_DEBUGGING, 0, 0, 0}; - buf[0] = '\0'; - getlin("Load which lua file?", buf); - if (buf[0] == '\033' || buf[0] == '\0') - return ECMD_CANCEL; - if (!strchr(buf, '.')) - strcat(buf, ".lua"); - (void) load_lua(buf, &sbi); - } else - pline(unavailcmd, ecname_from_fn(wiz_load_lua)); - return ECMD_OK; + return TRUE; } -/* the #wizloaddes command - load a special level lua file */ -static int -wiz_load_splua(void) +/* can hero currently see all locations in the selection */ +staticfn boolean +u_can_see_whole_selection(struct selectionvar *sel) { - if (wizard) { - char buf[BUFSZ]; + coordxy x, y; + NhRect rect = cg.zeroNhRect; - buf[0] = '\0'; - getlin("Load which des lua file?", buf); - if (buf[0] == '\033' || buf[0] == '\0') - return ECMD_CANCEL; - if (!strchr(buf, '.')) - strcat(buf, ".lua"); + selection_getbounds(sel, &rect); - lspo_reset_level(NULL); - (void) load_special(buf); - lspo_finalize_level(NULL); + for (x = rect.lx; x <= rect.hx; x++) + for (y = rect.ly; y <= rect.hy; y++) + if (isok(x,y) && selection_getpoint(x, y, sel) && !cansee(x, y)) + return FALSE; - } else - pline(unavailcmd, ecname_from_fn(wiz_load_splua)); - return ECMD_OK; + return TRUE; } -/* the #wizlevelport command - level teleport */ -static int -wiz_level_tele(void) +/* selection_floofill callback to get all locations in a room */ +staticfn int +dolookaround_floodfill_findroom(coordxy x, coordxy y) { - if (wizard) - level_tele(); - else - pline(unavailcmd, ecname_from_fn(wiz_level_tele)); - return ECMD_OK; + schar typ = levl[x][y].typ; + + if (IS_STWALL(typ) || IS_DOOR(typ) || IS_TREE(typ) + || IS_WATERWALL(typ) || typ == LAVAWALL || typ == IRONBARS + || typ == SCORR || typ == SDOOR || typ == DRAWBRIDGE_UP) + return FALSE; + return TRUE; } -/* #wizfliplevel - transpose the current level */ -static int -wiz_flip_level(void) +/* describe the room at x,y */ +staticfn void +lookaround_known_room(coordxy x, coordxy y) { - static const char choices[] = "0123", - prmpt[] = "Flip 0=randomly, 1=vertically, 2=horizonally, 3=both:"; - - /* - * Does not handle - * levregions, - * monster mtrack, - * migrating monsters aimed at returning to specific coordinates - * on this level - * as flipping is normally done only during level creation. - */ - if (wizard) { - char c = yn_function(prmpt, choices, '\0', TRUE); - - if (c && strchr(choices, c)) { - c -= '0'; + struct selectionvar *sel = selection_new(); + int rmno = u.urooms[0] - ROOMOFFSET; + char qbuf[QBUFSZ]; - if (!c) - flip_level_rnd(3, TRUE); - else - flip_level((int) c, TRUE); + set_selection_floodfillchk(dolookaround_floodfill_findroom); + selection_floodfill(sel, x, y, TRUE); - docrt(); - } else { - pline("%s", Never_mind); - } + if (!u_at(x, y)) + set_msg_xy(x, y); + + if (u_have_seen_whole_selection(sel)) { + boolean u_in = (boolean) selection_getpoint(x, y, sel); + + You("%s %s %s.", + u_at(x, y) && u_in && u_can_see_whole_selection(sel) ? "are in" + : (u_at(x, y)) ? "remember this as" : "remember that as", + an(selection_size_description(sel, qbuf)), + rmno >= 0 ? "room" : "area"); + } else if (u_have_seen_bounds_selection(sel)) { + You("guess %s to be %s %s.", + u_at(x, y) ? "this" : "that", + an(selection_size_description(sel, qbuf)), + rmno >= 0 ? "room" : "area"); + } else { + You("can't guess the size of %s area.", + u_at(x, y) ? "this" : "that"); } - return ECMD_OK; + selection_free(sel, TRUE); } -/* #levelchange command - adjust hero's experience level */ -static int -wiz_level_change(void) +/* #lookaround - describe what the hero can see, in text */ +int +dolookaround(void) { - char buf[BUFSZ] = DUMMY; - int newlevel = 0; - int ret; - - getlin("To what experience level do you want to be set?", buf); - (void) mungspaces(buf); - if (buf[0] == '\033' || buf[0] == '\0') - ret = 0; - else - ret = sscanf(buf, "%d", &newlevel); - - if (ret != 1) { - pline1(Never_mind); - return ECMD_OK; - } - if (newlevel == u.ulevel) { - You("are already that experienced."); - } else if (newlevel < u.ulevel) { - if (u.ulevel == 1) { - You("are already as inexperienced as you can get."); - return ECMD_OK; + coordxy x, y; + int tmp_getloc_filter = iflags.getloc_filter; + boolean tmp_accessiblemsg = a11y.accessiblemsg; + boolean corr_next2u = FALSE; + + a11y.accessiblemsg = TRUE; + if (levl[u.ux][u.uy].typ == CORR) { + /* In a corridor, mention corridors next to you. */ + corr_next2u = TRUE; + /* TODO: if we know, describe where the corridor goes, + perhaps by describing the rooms? */ + } else if (IS_DOOR(levl[u.ux][u.uy].typ)) { + /* In a doorway, describe the rooms next to you */ + int i; + + for (i = DIR_W; i < N_DIRS; i += 2) { + x = u.ux + xdir[i]; + y = u.uy + ydir[i]; + if (isok(x, y) && IS_ROOM(levl[x][y].typ)) + lookaround_known_room(x, y); } - if (newlevel < 1) - newlevel = 1; - while (u.ulevel > newlevel) - losexp("#levelchange"); + corr_next2u = TRUE; } else { - if (u.ulevel >= MAXULEV) { - You("are already as experienced as you can get."); - return ECMD_OK; - } - if (newlevel > MAXULEV) - newlevel = MAXULEV; - while (u.ulevel < newlevel) - pluslvl(FALSE); + lookaround_known_room(u.ux, u.uy); } - u.ulevelmax = u.ulevel; - return ECMD_OK; -} - -DISABLE_WARNING_CONDEXPR_IS_CONSTANT - -/* #wiztelekinesis */ -static int -wiz_telekinesis(void) -{ - int ans = 0; - coord cc; - struct monst *mtmp = (struct monst *) 0; - - cc.x = u.ux; - cc.y = u.uy; - - pline("Pick a monster to hurtle."); - do { - ans = getpos(&cc, TRUE, "a monster"); - if (ans < 0 || cc.x < 1) - return ECMD_CANCEL; - if ((((mtmp = m_at(cc.x, cc.y)) != 0) && canspotmon(mtmp)) - || u_at(cc.x, cc.y)) { - if (!getdir("which direction?")) - return ECMD_CANCEL; + /* TODO: maybe describe stuff outside the current room differently? */ - if (mtmp) { - mhurtle(mtmp, u.dx, u.dy, 6); - if (!DEADMONSTER(mtmp) && canspotmon(mtmp)) { - cc.x = mtmp->mx; - cc.y = mtmp->my; - } - } else { - hurtle(u.dx, u.dy, 6, FALSE); - cc.x = u.ux, cc.y = u.uy; + iflags.getloc_filter = GFILTER_VIEW; + for (y = 0; y < ROWNO; y++) + for (x = 1; x < COLNO; x++) { + int glyph, mapsym; + boolean iscorr = (corr_next2u + && (glyph = glyph_at(x, y)) >= 0 + && glyph_is_cmap(glyph) + && ((mapsym = glyph_to_cmap(glyph)) == S_corr + || mapsym == S_litcorr)); + + if (!u_at(x, y) + && (gather_locs_interesting(x, y, GLOC_INTERESTING) + /* note: GLOC_INTERESTING catches S_engrcorr */ + || iscorr)) { + char buf[BUFSZ]; + coord cc; + int sym = 0; + const char *firstmatch = 0; + + cc.x = x, cc.y = y; + do_screen_description(cc, TRUE, sym, buf, &firstmatch, NULL); + pline_xy(x, y, "%s.", firstmatch); } } - } while (u.utotype == UTOTYPE_NONE); - return ECMD_OK; -} - -RESTORE_WARNING_CONDEXPR_IS_CONSTANT + iflags.getloc_filter = tmp_getloc_filter; + a11y.accessiblemsg = tmp_accessiblemsg; -/* #panic command - test program's panic handling */ -static int -wiz_panic(void) -{ - if (iflags.debug_fuzzer) { - u.uhp = u.uhpmax = 1000; - u.uen = u.uenmax = 1000; - return ECMD_OK; - } - if (paranoid_query(TRUE, - "Do you want to call panic() and end your game?")) - panic("Crash test."); return ECMD_OK; } -/* #debugfuzzer command - fuzztest the program */ -static int -wiz_fuzzer(void) -{ - if (flags.suppress_alert < FEATURE_NOTICE_VER(3,7,0)) { - 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 */ - return ECMD_OK; -} - -/* #polyself command - change hero's form */ -static int -wiz_polyself(void) -{ - polyself(POLY_CONTROLLED); - return ECMD_OK; -} - -/* #seenv command */ -static int -wiz_show_seenv(void) -{ - winid win; - coordxy x, y, startx, stopx, curx; - int v; - char row[COLNO + 1]; - - win = create_nhwindow(NHW_TEXT); - /* - * Each seenv description takes up 2 characters, so center - * the seenv display around the hero. - */ - startx = max(1, u.ux - (COLNO / 4)); - stopx = min(startx + (COLNO / 2), COLNO); - /* can't have a line exactly 80 chars long */ - if (stopx - startx == COLNO / 2) - startx++; - - for (y = 0; y < ROWNO; y++) { - for (x = startx, curx = 0; x < stopx; x++, curx += 2) { - if (u_at(x, y)) { - row[curx] = row[curx + 1] = '@'; - } else { - v = levl[x][y].seenv & 0xff; - if (v == 0) - row[curx] = row[curx + 1] = ' '; - else - Sprintf(&row[curx], "%02x", v); - } - } - /* remove trailing spaces */ - for (x = curx - 1; x >= 0; x--) - if (row[x] != ' ') - break; - row[x + 1] = '\0'; - - putstr(win, 0, row); - } - display_nhwindow(win, TRUE); - destroy_nhwindow(win); - return ECMD_OK; -} - -/* #vision command */ -static int -wiz_show_vision(void) -{ - winid win; - coordxy x, y; - int v; - char row[COLNO + 1]; - - win = create_nhwindow(NHW_TEXT); - Sprintf(row, "Flags: 0x%x could see, 0x%x in sight, 0x%x temp lit", - COULD_SEE, IN_SIGHT, TEMP_LIT); - putstr(win, 0, row); - putstr(win, 0, ""); - for (y = 0; y < ROWNO; y++) { - for (x = 1; x < COLNO; x++) { - if (u_at(x, y)) { - row[x] = '@'; - } else { - v = gv.viz_array[y][x]; /* data access should be hidden */ - row[x] = (v == 0) ? ' ' : ('0' + v); - } - } - /* remove trailing spaces */ - for (x = COLNO - 1; x >= 1; x--) - if (row[x] != ' ') - break; - row[x + 1] = '\0'; - - putstr(win, 0, &row[1]); - } - display_nhwindow(win, TRUE); - destroy_nhwindow(win); - return ECMD_OK; -} - -/* #wmode command */ -static int -wiz_show_wmodes(void) -{ - winid win; - coordxy x, y; - char row[COLNO + 1]; - struct rm *lev; - boolean istty = WINDOWPORT(tty); - - win = create_nhwindow(NHW_TEXT); - if (istty) - putstr(win, 0, ""); /* tty only: blank top line */ - for (y = 0; y < ROWNO; y++) { - for (x = 0; x < COLNO; x++) { - lev = &levl[x][y]; - if (u_at(x, y)) - row[x] = '@'; - else if (IS_WALL(lev->typ) || lev->typ == SDOOR) - row[x] = '0' + (lev->wall_info & WM_MASK); - else if (lev->typ == CORR) - row[x] = '#'; - else if (IS_ROOM(lev->typ) || IS_DOOR(lev->typ)) - row[x] = '.'; - else - row[x] = 'x'; - } - row[COLNO] = '\0'; - /* map column 0, levl[0][], is off the left edge of the screen */ - putstr(win, 0, &row[1]); - } - display_nhwindow(win, TRUE); - destroy_nhwindow(win); - return ECMD_OK; -} - -/* wizard mode variant of #terrain; internal levl[][].typ values in base-36 */ -static void -wiz_map_levltyp(void) -{ - winid win; - coordxy x, y; - int terrain; - char row[COLNO + 1]; - boolean istty = !strcmp(windowprocs.name, "tty"); - - win = create_nhwindow(NHW_TEXT); - /* map row 0, levl[][0], is drawn on the second line of tty screen */ - if (istty) - putstr(win, 0, ""); /* tty only: blank top line */ - for (y = 0; y < ROWNO; y++) { - /* map column 0, levl[0][], is off the left edge of the screen; - it should always have terrain type "undiggable stone" */ - for (x = 1; x < COLNO; x++) { - terrain = levl[x][y].typ; - /* assumes there aren't more than 10+26+26 terrain types */ - row[x - 1] = (char) ((terrain == STONE && !may_dig(x, y)) - ? '*' - : (terrain < 10) - ? '0' + terrain - : (terrain < 36) - ? 'a' + terrain - 10 - : 'A' + terrain - 36); - } - x--; - if (levl[0][y].typ != STONE || may_dig(0, y)) - row[x++] = '!'; - row[x] = '\0'; - putstr(win, 0, row); - } - - { - char dsc[COLBUFSZ]; - s_level *slev = Is_special(&u.uz); - - Sprintf(dsc, "D:%d,L:%d", u.uz.dnum, u.uz.dlevel); - /* [dungeon branch features currently omitted] */ - /* special level features */ - if (slev) { - Sprintf(eos(dsc), " \"%s\"", slev->proto); - /* special level flags (note: dungeon.def doesn't set `maze' - or `hell' for any specific levels so those never show up) */ - if (slev->flags.maze_like) - Strcat(dsc, " mazelike"); - if (slev->flags.hellish) - Strcat(dsc, " hellish"); - if (slev->flags.town) - Strcat(dsc, " town"); - /* alignment currently omitted to save space */ - } - /* level features */ - if (gl.level.flags.nfountains) - Sprintf(eos(dsc), " %c:%d", defsyms[S_fountain].sym, - (int) gl.level.flags.nfountains); - if (gl.level.flags.nsinks) - Sprintf(eos(dsc), " %c:%d", defsyms[S_sink].sym, - (int) gl.level.flags.nsinks); - if (gl.level.flags.has_vault) - Strcat(dsc, " vault"); - if (gl.level.flags.has_shop) - Strcat(dsc, " shop"); - if (gl.level.flags.has_temple) - Strcat(dsc, " temple"); - if (gl.level.flags.has_court) - Strcat(dsc, " throne"); - if (gl.level.flags.has_zoo) - Strcat(dsc, " zoo"); - if (gl.level.flags.has_morgue) - Strcat(dsc, " morgue"); - if (gl.level.flags.has_barracks) - Strcat(dsc, " barracks"); - if (gl.level.flags.has_beehive) - Strcat(dsc, " hive"); - if (gl.level.flags.has_swamp) - Strcat(dsc, " swamp"); - /* level flags */ - if (gl.level.flags.noteleport) - Strcat(dsc, " noTport"); - if (gl.level.flags.hardfloor) - Strcat(dsc, " noDig"); - if (gl.level.flags.nommap == MAPPABLE_NEVER) - Strcat(dsc, " noMMap"); - else if (gl.level.flags.nommap == MAPPABLE_BOSSBLOCKED) - Strcat(dsc, " noMMapBoss"); - if (!gl.level.flags.hero_memory) - Strcat(dsc, " noMem"); - if (gl.level.flags.shortsighted) - Strcat(dsc, " shortsight"); - if (gl.level.flags.graveyard) - Strcat(dsc, " graveyard"); - if (gl.level.flags.is_maze_lev) - Strcat(dsc, " maze"); - if (gl.level.flags.is_cavernous_lev) - Strcat(dsc, " cave"); - if (gl.level.flags.arboreal) - Strcat(dsc, " tree"); - if (Sokoban) - Strcat(dsc, " sokoban-rules"); - /* non-flag info; probably should include dungeon branching - checks (extra stairs and magic portals) here */ - if (Invocation_lev(&u.uz)) - Strcat(dsc, " invoke"); - if (On_W_tower_level(&u.uz)) - Strcat(dsc, " tower"); - /* append a branch identifier for completeness' sake */ - if (u.uz.dnum == 0) - Strcat(dsc, " dungeon"); - else if (u.uz.dnum == mines_dnum) - Strcat(dsc, " mines"); - else if (In_sokoban(&u.uz)) - Strcat(dsc, " sokoban"); - else if (u.uz.dnum == quest_dnum) - Strcat(dsc, " quest"); - else if (Is_knox(&u.uz)) - Strcat(dsc, " ludios"); - else if (u.uz.dnum == gehennom_dnum) - Strcat(dsc, " gehennom"); - else if (u.uz.dnum == tower_dnum) - Strcat(dsc, " vlad"); - else if (In_endgame(&u.uz)) - Strcat(dsc, " endgame"); - else { - /* somebody's added a dungeon branch we're not expecting */ - const char *brname = gd.dungeons[u.uz.dnum].dname; - - if (!brname || !*brname) - brname = "unknown"; - if (!strncmpi(brname, "the ", 4)) - brname += 4; - Sprintf(eos(dsc), " %s", brname); - } - /* limit the line length to map width */ - if (strlen(dsc) >= COLNO) - dsc[COLNO - 1] = '\0'; /* truncate */ - putstr(win, 0, dsc); - } - - display_nhwindow(win, TRUE); - destroy_nhwindow(win); - return; -} - -/* temporary? hack, since level type codes aren't the same as screen - symbols and only the latter have easily accessible descriptions */ -const char *levltyp[MAX_TYPE + 2] = { - "stone", "vertical wall", "horizontal wall", "top-left corner wall", - "top-right corner wall", "bottom-left corner wall", - "bottom-right corner wall", "cross wall", "tee-up wall", "tee-down wall", - "tee-left wall", "tee-right wall", "drawbridge wall", "tree", - "secret door", "secret corridor", "pool", "moat", "water", - "drawbridge up", "lava pool", "lava wall", "iron bars", "door", - "corridor", "room", "stairs", "ladder", "fountain", "throne", "sink", - "grave", "altar", "ice", "grass", "magic platform", "drawbridge down", - "air", "cloud", - /* not a real terrain type, but used for undiggable stone - by wiz_map_levltyp() */ - "unreachable/undiggable", - /* padding in case the number of entries above is odd */ - "" -}; - -const char * -levltyp_to_name(int typ) -{ - if (typ >= 0 && typ < MAX_TYPE) - return levltyp[typ]; - return NULL; -} - -DISABLE_WARNING_FORMAT_NONLITERAL - -/* explanation of base-36 output from wiz_map_levltyp() */ -static void -wiz_levltyp_legend(void) -{ - winid win; - int i, j, last, c; - const char *dsc, *fmt; - char buf[BUFSZ]; - - win = create_nhwindow(NHW_TEXT); - putstr(win, 0, "#terrain encodings:"); - putstr(win, 0, ""); - fmt = " %c - %-28s"; /* TODO: include tab-separated variant for win32 */ - *buf = '\0'; - /* output in pairs, left hand column holds [0],[1],...,[N/2-1] - and right hand column holds [N/2],[N/2+1],...,[N-1]; - N ('last') will always be even, and may or may not include - the empty string entry to pad out the final pair, depending - upon how many other entries are present in levltyp[] */ - last = SIZE(levltyp) & ~1; - for (i = 0; i < last / 2; ++i) - for (j = i; j < last; j += last / 2) { - dsc = levltyp[j]; - c = !*dsc ? ' ' - : !strncmp(dsc, "unreachable", 11) ? '*' - /* same int-to-char conversion as wiz_map_levltyp() */ - : (j < 10) ? '0' + j - : (j < 36) ? 'a' + j - 10 - : 'A' + j - 36; - Sprintf(eos(buf), fmt, c, dsc); - if (j > i) { - putstr(win, 0, buf); - *buf = '\0'; - } - } - display_nhwindow(win, TRUE); - destroy_nhwindow(win); - return; -} - -RESTORE_WARNING_FORMAT_NONLITERAL - -DISABLE_WARNING_CONDEXPR_IS_CONSTANT - -/* #wizsmell command - test usmellmon(). */ -static int -wiz_smell(void) -{ - struct monst *mtmp; /* monster being smelled */ - struct permonst *mptr; - int ans, glyph; - coord cc; /* screen pos to sniff */ - boolean is_you; - - cc.x = u.ux; - cc.y = u.uy; - if (!olfaction(gy.youmonst.data)) { - You("are incapable of detecting odors in your present form."); - return ECMD_OK; - } - - You("can move the cursor to a monster that you want to smell."); - do { - pline("Pick a monster to smell."); - ans = getpos(&cc, TRUE, "a monster"); - if (ans < 0 || cc.x < 0) { - return ECMD_CANCEL; /* done */ - } - is_you = FALSE; - if (u_at(cc.x, cc.y)) { - if (u.usteed) { - mptr = u.usteed->data; - } else { - mptr = gy.youmonst.data; - is_you = TRUE; - } - } else if ((mtmp = m_at(cc.x, cc.y)) != (struct monst *) 0) { - mptr = mtmp->data; - } else { - mptr = (struct permonst *) 0; - } - /* Buglet: mapping or unmapping "remembered, unseen monster" should - cause time to elapse; since we're in wizmode, don't bother */ - glyph = glyph_at(cc.x, cc.y); - /* Is it a monster? */ - if (mptr) { - if (is_you) - You("surreptitiously sniff under your %s.", body_part(ARM)); - if (!usmellmon(mptr)) - pline("%s to not give off any smell.", - is_you ? "You seem" : "That monster seems"); - if (!glyph_is_monster(glyph)) - map_invisible(cc.x, cc.y); - } else { - You("don't smell any monster there."); - if (glyph_is_invisible(glyph)) - unmap_invisible(cc.x, cc.y); - } - } while (TRUE); - return ECMD_OK; -} - -RESTORE_WARNING_CONDEXPR_IS_CONSTANT - -DISABLE_WARNING_FORMAT_NONLITERAL - -#define DEFAULT_TIMEOUT_INCR 30 - -/* #wizinstrinsic command to set some intrinsics for testing */ -static int -wiz_intrinsic(void) -{ - if (wizard) { - static const char wizintrinsic[] = "#wizintrinsic"; - static const char fmt[] = "You are%s %s."; - winid win; - anything any; - char buf[BUFSZ]; - int i, j, n, amt, typ, p = 0; - long oldtimeout, newtimeout; - const char *propname; - menu_item *pick_list = (menu_item *) 0; - int clr = 0; - - any = cg.zeroany; - win = create_nhwindow(NHW_MENU); - start_menu(win, MENU_BEHAVE_STANDARD); - if (iflags.cmdassist) { - /* start menu with a subtitle */ - Sprintf(buf, - "[Precede any selection with a count to increment by other than %d.]", - DEFAULT_TIMEOUT_INCR); - any.a_int = 0; - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, buf, - MENU_ITEMFLAGS_NONE); - } - for (i = 0; (propname = property_by_index(i, &p)) != 0; ++i) { - if (p == HALLUC_RES) { - /* Grayswandir vs hallucination; ought to be redone to - use u.uprops[HALLUC].blocked instead of being treated - as a separate property; letting in be manually toggled - even only in wizard mode would be asking for trouble... */ - continue; - } - if (p == FIRE_RES) { - /* FIRE_RES and properties beyond it (in the propertynames[] - ordering, not their numerical PROP values), can only be - set to timed values here so show a separator */ - any.a_int = 0; - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, "--", MENU_ITEMFLAGS_NONE); - } - any.a_int = i + 1; /* +1: avoid 0 */ - oldtimeout = u.uprops[p].intrinsic & TIMEOUT; - if (oldtimeout) - Sprintf(buf, "%-27s [%li]", propname, oldtimeout); - else - Sprintf(buf, "%s", propname); - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, buf, - MENU_ITEMFLAGS_NONE); - } - end_menu(win, "Which intrinsics?"); - n = select_menu(win, PICK_ANY, &pick_list); - destroy_nhwindow(win); - - for (j = 0; j < n; ++j) { - i = pick_list[j].item.a_int - 1; /* -1: reverse +1 above */ - propname = property_by_index(i, &p); - oldtimeout = u.uprops[p].intrinsic & TIMEOUT; - amt = (pick_list[j].count == -1L) ? DEFAULT_TIMEOUT_INCR - : (int) pick_list[j].count; - if (amt <= 0) /* paranoia */ - continue; - newtimeout = oldtimeout + (long) amt; - - switch (p) { - case SICK: - case SLIMED: - case STONED: - if (oldtimeout > 0L && newtimeout > oldtimeout) - newtimeout = oldtimeout; - break; - } - - switch (p) { - case BLINDED: - make_blinded(newtimeout, TRUE); - break; -#if 0 /* make_confused() only gives feedback when confusion is - * ending so use the 'default' case for it instead */ - case CONFUSION: - make_confused(newtimeout, TRUE); - break; -#endif /*0*/ - case DEAF: - make_deaf(newtimeout, TRUE); - break; - case HALLUC: - make_hallucinated(newtimeout, TRUE, 0L); - break; - case SICK: - typ = !rn2(2) ? SICK_VOMITABLE : SICK_NONVOMITABLE; - make_sick(newtimeout, wizintrinsic, TRUE, typ); - break; - case SLIMED: - Sprintf(buf, fmt, - !Slimed ? "" : " still", "turning into slime"); - make_slimed(newtimeout, buf); - break; - case STONED: - Sprintf(buf, fmt, - !Stoned ? "" : " still", "turning into stone"); - make_stoned(newtimeout, buf, KILLED_BY, wizintrinsic); - break; - case STUNNED: - make_stunned(newtimeout, TRUE); - break; - case WITHERING: - make_withering(newtimeout, TRUE); - break; - case VOMITING: - Sprintf(buf, fmt, !Vomiting ? "" : " still", "vomiting"); - make_vomiting(newtimeout, FALSE); - pline1(buf); - break; - case WARN_OF_MON: - if (!Warn_of_mon) { - gc.context.warntype.speciesidx = PM_GRID_BUG; - gc.context.warntype.species - = &mons[gc.context.warntype.speciesidx]; - } - goto def_feedback; - case GLIB: - /* slippery fingers might need a persistent inventory update - so needs more than simple incr_itimeout() but we want - the pline() issued with that */ - make_glib((int) newtimeout); - /*FALLTHRU*/ - default: - def_feedback: - if (p != GLIB) - incr_itimeout(&u.uprops[p].intrinsic, amt); - gc.context.botl = 1; /* have pline() do a status update */ - pline("Timeout for %s %s %d.", propname, - oldtimeout ? "increased by" : "set to", amt); - break; - } - /* this has to be after incr_itimeout() */ - if (p == LEVITATION || p == FLYING) - float_vs_flight(); - else if (p == PROT_FROM_SHAPE_CHANGERS) - rescham(); - } - if (n >= 1) - free((genericptr_t) pick_list); - docrt(); - } else - pline(unavailcmd, ecname_from_fn(wiz_intrinsic)); - return ECMD_OK; -} - -RESTORE_WARNING_FORMAT_NONLITERAL - -/* #wizrumorcheck command - verify each rumor access */ -static int -wiz_rumor_check(void) -{ - rumor_check(); - return ECMD_OK; -} - -/* #terrain command -- show known map, inspired by crawl's '|' command */ -static int -doterrain(void) -{ - winid men; - menu_item *sel; - anything any; - int n; - int which; - int clr = 0; - - /* - * normal play: choose between known map without mons, obj, and traps - * (to see underlying terrain only), or - * known map without mons and objs (to see traps under mons and objs), or - * known map without mons (to see objects under monsters); - * explore mode: normal choices plus full map (w/o mons, objs, traps); - * wizard mode: normal and explore choices plus - * a dump of the internal levl[][].typ codes w/ level flags, or - * a legend for the levl[][].typ codes dump - */ - men = create_nhwindow(NHW_MENU); - start_menu(men, MENU_BEHAVE_STANDARD); - any = cg.zeroany; - any.a_int = 1; - add_menu(men, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, - "known map without monsters, objects, and traps", - MENU_ITEMFLAGS_SELECTED); - any.a_int = 2; - add_menu(men, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "known map without monsters and objects", - MENU_ITEMFLAGS_NONE); - any.a_int = 3; - add_menu(men, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "known map without monsters", - MENU_ITEMFLAGS_NONE); - any.a_int = 7; - add_menu(men, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "known terrain showing spaces you have visited", - MENU_ITEMFLAGS_NONE); - if (discover || wizard) { - any.a_int = 4; - add_menu(men, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "full map without monsters, objects, and traps", - MENU_ITEMFLAGS_NONE); - if (wizard) { - any.a_int = 5; - add_menu(men, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "internal levl[][].typ codes in base-36", - MENU_ITEMFLAGS_NONE); - any.a_int = 6; - add_menu(men, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "legend of base-36 levl[][].typ codes", - MENU_ITEMFLAGS_NONE); - } - } - end_menu(men, "View which?"); - - n = select_menu(men, PICK_ONE, &sel); - destroy_nhwindow(men); - /* - * n < 0: player used ESC to cancel; - * n == 0: preselected entry was explicitly chosen and got toggled off; - * n == 1: preselected entry was implicitly chosen via |; - * n == 2: another entry was explicitly chosen, so skip preselected one. - */ - which = (n < 0) ? -1 : (n == 0) ? 1 : sel[0].item.a_int; - if (n > 1 && which == 1) - which = sel[1].item.a_int; - if (n > 0) - free((genericptr_t) sel); - - switch (which) { - case 1: /* known map */ - reveal_terrain(0, TER_MAP); - break; - case 2: /* known map with known traps */ - reveal_terrain(0, TER_MAP | TER_TRP); - break; - case 3: /* known map with known traps and objects */ - reveal_terrain(0, TER_MAP | TER_TRP | TER_OBJ); - break; - case 4: /* full map */ - reveal_terrain(1, TER_MAP); - break; - case 5: /* map internals */ - wiz_map_levltyp(); - break; - case 6: /* internal details */ - wiz_levltyp_legend(); - break; - case 7: /* full map with visited spaces */ - reveal_terrain(0, TER_MAP | TER_VISIT); - default: - break; - } - return ECMD_OK; /* no time elapses */ -} - void set_move_cmd(int dir, int run) { @@ -2276,10 +1385,10 @@ set_move_cmd(int dir, int run) u.dy = ydir[dir]; /* #reqmenu -prefix disables autopickup during movement */ if (iflags.menu_requested) - gc.context.nopick = 1; - gc.context.travel = gc.context.travel1 = 0; + svc.context.nopick = 1; + svc.context.travel = svc.context.travel1 = 0; if (!gd.domove_attempting && !u.dz) { - gc.context.run = run; + svc.context.run = run; gd.domove_attempting |= (!run ? DOMOVE_WALK : DOMOVE_RUSH); } } @@ -2476,12 +1585,12 @@ do_rush(void) { if ((gd.domove_attempting & DOMOVE_RUSH)) { Norep("Double rush prefix, canceled."); - gc.context.run = 0; + svc.context.run = 0; gd.domove_attempting = 0; return ECMD_CANCEL; } - gc.context.run = 2; + svc.context.run = 2; gd.domove_attempting |= DOMOVE_RUSH; return ECMD_OK; } @@ -2492,12 +1601,12 @@ do_run(void) { if ((gd.domove_attempting & DOMOVE_RUSH)) { Norep("Double run prefix, canceled."); - gc.context.run = 0; + svc.context.run = 0; gd.domove_attempting = 0; return ECMD_CANCEL; } - gc.context.run = 3; + svc.context.run = 3; gd.domove_attempting |= DOMOVE_RUSH; return ECMD_OK; } @@ -2506,14 +1615,14 @@ do_run(void) int do_fight(void) { - if (gc.context.forcefight) { + if (svc.context.forcefight) { Norep("Double fight prefix, canceled."); - gc.context.forcefight = 0; + svc.context.forcefight = 0; gd.domove_attempting = 0; return ECMD_CANCEL; } - gc.context.forcefight = 1; + svc.context.forcefight = 1; gd.domove_attempting |= DOMOVE_WALK; return ECMD_OK; } @@ -2533,12 +1642,12 @@ do_repeat(void) } repeat_copy = cmdq_copy(CQ_REPEAT); gi.in_doagain = TRUE; - rhack((char *) 0); /* read and execute command */ + rhack(0); /* read and execute command */ gi.in_doagain = FALSE; cmdq_clear(CQ_REPEAT); gc.command_queue[CQ_REPEAT] = repeat_copy; iflags.menu_requested = FALSE; - if (gc.context.move) + if (svc.context.move) res = ECMD_TIME; } return res; @@ -2565,6 +1674,10 @@ struct ext_func_tab extcmdlist[] = { doattributes, IFBURIED, NULL }, { '@', "autopickup", "toggle the 'autopickup' option on/off", dotogglepickup, IFBURIED, NULL }, +#ifdef CRASHREPORT + { '\0', "bugreport", "file a bug report", + dobugreport, GENERALCMD | NOFUZZERCMD, NULL }, +#endif { 'C', "call", "name a monster, specific object, or type of object", docallcmd, IFBURIED, NULL }, { 'Z', "cast", "zap (cast) a spell", @@ -2584,7 +1697,7 @@ struct ext_func_tab extcmdlist[] = { { '>', "down", "go down a staircase", /* allows 'm' prefix (for move without autopickup) but not the g/G/F movement modifiers; not flagged as MOVEMENTCMD because - that would would suppress it from dokeylist output */ + that would suppress it from dokeylist output */ dodown, CMD_M_PREFIX, NULL }, { 'd', "drop", "drop an item", dodrop, 0, NULL }, @@ -2640,10 +1753,12 @@ struct ext_func_tab extcmdlist[] = { wiz_light_sources, IFBURIED | AUTOCOMPLETE | WIZMODECMD, NULL }, { ':', "look", "look at what is here", dolook, IFBURIED, NULL }, + { '\0', "lookaround", "describe what you can see", + dolookaround, IFBURIED | GENERALCMD, NULL }, { M('l'), "loot", "loot a box on the floor", doloot, AUTOCOMPLETE | CMD_M_PREFIX, NULL }, { '\0', "migratemons", -#ifdef DEBUG_MIGRATING_MONSTERS +#ifdef DEBUG_MIGRATING_MONS "show migrating monsters and migrate N random ones", #else "show migrating monsters", @@ -2674,7 +1789,7 @@ struct ext_func_tab extcmdlist[] = { { '\0', "panic", "test panic routine (fatal to game)", wiz_panic, IFBURIED | AUTOCOMPLETE | WIZMODECMD, NULL }, { 'p', "pay", "pay your shopping bill", - dopay, 0, NULL }, + dopay, CMD_M_PREFIX, NULL }, { '|', "perminv", "scroll persistent inventory display", doperminv, IFBURIED | GENERALCMD | NOFUZZERCMD, NULL }, { ',', "pickup", "pick up things at the current location", @@ -2689,7 +1804,7 @@ struct ext_func_tab extcmdlist[] = { doputon, 0, NULL }, { 'q', "quaff", "quaff (drink) something", dodrink, CMD_M_PREFIX, NULL }, - { '\0', "quit", "exit without saving current game", + { '\0', "quit", "exit without saving current game", done2, IFBURIED | AUTOCOMPLETE | GENERALCMD | NOFUZZERCMD, NULL }, { 'Q', "quiver", "select ammunition for quiver", @@ -2727,18 +1842,19 @@ struct ext_func_tab extcmdlist[] = { { 's', "search", "search for traps and secret doors", dosearch, IFBURIED | CMD_M_PREFIX, "searching" }, { '*', "seeall", "show all equipment in use", - doprinuse, IFBURIED, NULL }, + doprinuse, IFBURIED | CMD_M_PREFIX, NULL }, { AMULET_SYM, "seeamulet", "show the amulet currently worn", - dopramulet, IFBURIED, NULL }, + dopramulet, IFBURIED | CMD_M_PREFIX, NULL }, { ARMOR_SYM, "seearmor", "show the armor currently worn", - doprarm, IFBURIED, NULL }, + doprarm, IFBURIED | CMD_M_PREFIX, NULL }, { RING_SYM, "seerings", "show the ring(s) currently worn", - doprring, IFBURIED, NULL }, + doprring, IFBURIED | CMD_M_PREFIX, NULL }, { TOOL_SYM, "seetools", "show the tools currently in use", - doprtool, IFBURIED, NULL }, + doprtool, IFBURIED | CMD_M_PREFIX, NULL }, { WEAPON_SYM, "seeweapon", "show the weapon currently wielded", - doprwep, IFBURIED, NULL }, - { '!', "shell", "leave game to enter a sub-shell ('exit' to come back)", + doprwep, IFBURIED | CMD_M_PREFIX, NULL }, + { '!', "shell", + "leave game to enter a sub-shell ('exit' to come back)", dosh_core, (IFBURIED | GENERALCMD | NOFUZZERCMD #ifndef SHELL | CMD_NOT_AVAILABLE @@ -2748,7 +1864,7 @@ struct ext_func_tab extcmdlist[] = { doshout, AUTOCOMPLETE, NULL }, /* $ is like ),=,&c but is not included with *, so not called "seegold" */ { GOLD_SYM, "showgold", "show gold, possibly shop credit or debt", - doprgold, IFBURIED, NULL }, + doprgold, IFBURIED | CMD_M_PREFIX, NULL }, { SPBOOK_SYM, "showspells", "list and reorder known spells", dovspell, IFBURIED, NULL }, { '^', "showtrap", "describe an adjacent, discovered trap", @@ -2828,6 +1944,8 @@ struct ext_func_tab extcmdlist[] = { #endif { '\0', "wizcast", "cast any spell", dowizcast, IFBURIED | WIZMODECMD, NULL }, + { '\0', "wizcustom", "show customized glyphs", + wiz_custom, IFBURIED | WIZMODECMD | NOFUZZERCMD, NULL }, { C('e'), "wizdetect", "reveal hidden things within a small radius", wiz_detect, IFBURIED | WIZMODECMD, NULL }, #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) @@ -2877,56 +1995,56 @@ struct ext_func_tab extcmdlist[] = { dozap, 0, NULL }, /* movement commands will be bound by reset_commands() */ /* move or attack; accept m/g/G/F prefixes */ - { '\0', "movewest", "move west (screen left)", - do_move_west, MOVEMENTCMD | CMD_MOVE_PREFIXES, NULL }, - { '\0', "movenorthwest", "move northwest (screen upper left)", - do_move_northwest, MOVEMENTCMD | CMD_MOVE_PREFIXES, NULL }, - { '\0', "movenorth", "move north (screen up)", - do_move_north, MOVEMENTCMD | CMD_MOVE_PREFIXES, NULL }, - { '\0', "movenortheast", "move northeast (screen upper right)", - do_move_northeast, MOVEMENTCMD | CMD_MOVE_PREFIXES, NULL }, - { '\0', "moveeast", "move east (screen right)", - do_move_east, MOVEMENTCMD | CMD_MOVE_PREFIXES, NULL }, - { '\0', "movesoutheast", "move southeast (screen lower right)", - do_move_southeast, MOVEMENTCMD | CMD_MOVE_PREFIXES, NULL }, - { '\0', "movesouth", "move south (screen down)", - do_move_south, MOVEMENTCMD | CMD_MOVE_PREFIXES, NULL }, - { '\0', "movesouthwest", "move southwest (screen lower left)", - do_move_southwest, MOVEMENTCMD | CMD_MOVE_PREFIXES, NULL }, + { '\0', "movewest", "move west (screen left)", + do_move_west, MOVEMENTCMD | CMD_MOVE_PREFIXES, NULL }, + { '\0', "movenorthwest", "move northwest (screen upper left)", + do_move_northwest, MOVEMENTCMD | CMD_MOVE_PREFIXES, NULL }, + { '\0', "movenorth", "move north (screen up)", + do_move_north, MOVEMENTCMD | CMD_MOVE_PREFIXES, NULL }, + { '\0', "movenortheast", "move northeast (screen upper right)", + do_move_northeast, MOVEMENTCMD | CMD_MOVE_PREFIXES, NULL }, + { '\0', "moveeast", "move east (screen right)", + do_move_east, MOVEMENTCMD | CMD_MOVE_PREFIXES, NULL }, + { '\0', "movesoutheast", "move southeast (screen lower right)", + do_move_southeast, MOVEMENTCMD | CMD_MOVE_PREFIXES, NULL }, + { '\0', "movesouth", "move south (screen down)", + do_move_south, MOVEMENTCMD | CMD_MOVE_PREFIXES, NULL }, + { '\0', "movesouthwest", "move southwest (screen lower left)", + do_move_southwest, MOVEMENTCMD | CMD_MOVE_PREFIXES, NULL }, /* rush; accept m prefix but not g/G/F */ - { '\0', "rushwest", "rush west (screen left)", - do_rush_west, MOVEMENTCMD | CMD_M_PREFIX, NULL }, - { '\0', "rushnorthwest", "rush northwest (screen upper left)", - do_rush_northwest, MOVEMENTCMD | CMD_M_PREFIX, NULL }, - { '\0', "rushnorth", "rush north (screen up)", - do_rush_north, MOVEMENTCMD | CMD_M_PREFIX, NULL }, - { '\0', "rushnortheast", "rush northeast (screen upper right)", - do_rush_northeast, MOVEMENTCMD | CMD_M_PREFIX, NULL }, - { '\0', "rusheast", "rush east (screen right)", - do_rush_east, MOVEMENTCMD | CMD_M_PREFIX, NULL }, - { '\0', "rushsoutheast", "rush southeast (screen lower right)", - do_rush_southeast, MOVEMENTCMD | CMD_M_PREFIX, NULL }, - { '\0', "rushsouth", "rush south (screen down)", - do_rush_south, MOVEMENTCMD | CMD_M_PREFIX, NULL }, - { '\0', "rushsouthwest", "rush southwest (screen lower left)", - do_rush_southwest, MOVEMENTCMD | CMD_M_PREFIX, NULL }, + { '\0', "rushwest", "rush west (screen left)", + do_rush_west, MOVEMENTCMD | CMD_M_PREFIX, NULL }, + { '\0', "rushnorthwest", "rush northwest (screen upper left)", + do_rush_northwest, MOVEMENTCMD | CMD_M_PREFIX, NULL }, + { '\0', "rushnorth", "rush north (screen up)", + do_rush_north, MOVEMENTCMD | CMD_M_PREFIX, NULL }, + { '\0', "rushnortheast", "rush northeast (screen upper right)", + do_rush_northeast, MOVEMENTCMD | CMD_M_PREFIX, NULL }, + { '\0', "rusheast", "rush east (screen right)", + do_rush_east, MOVEMENTCMD | CMD_M_PREFIX, NULL }, + { '\0', "rushsoutheast", "rush southeast (screen lower right)", + do_rush_southeast, MOVEMENTCMD | CMD_M_PREFIX, NULL }, + { '\0', "rushsouth", "rush south (screen down)", + do_rush_south, MOVEMENTCMD | CMD_M_PREFIX, NULL }, + { '\0', "rushsouthwest", "rush southwest (screen lower left)", + do_rush_southwest, MOVEMENTCMD | CMD_M_PREFIX, NULL }, /* run; accept m prefix but not g/G/F */ - { '\0', "runwest", "run west (screen left)", - do_run_west, MOVEMENTCMD | CMD_M_PREFIX, NULL }, - { '\0', "runnorthwest", "run northwest (screen upper left)", - do_run_northwest, MOVEMENTCMD | CMD_M_PREFIX, NULL }, - { '\0', "runnorth", "run north (screen up)", - do_run_north, MOVEMENTCMD | CMD_M_PREFIX, NULL }, - { '\0', "runnortheast", "run northeast (screen upper right)", - do_run_northeast, MOVEMENTCMD | CMD_M_PREFIX, NULL }, - { '\0', "runeast", "run east (screen right)", - do_run_east, MOVEMENTCMD | CMD_M_PREFIX, NULL }, - { '\0', "runsoutheast", "run southeast (screen lower right)", - do_run_southeast, MOVEMENTCMD | CMD_M_PREFIX, NULL }, - { '\0', "runsouth", "run south (screen down)", - do_run_south, MOVEMENTCMD | CMD_M_PREFIX, NULL }, - { '\0', "runsouthwest", "run southwest (screen lower left)", - do_run_southwest, MOVEMENTCMD | CMD_M_PREFIX, NULL }, + { '\0', "runwest", "run west (screen left)", + do_run_west, MOVEMENTCMD | CMD_M_PREFIX, NULL }, + { '\0', "runnorthwest", "run northwest (screen upper left)", + do_run_northwest, MOVEMENTCMD | CMD_M_PREFIX, NULL }, + { '\0', "runnorth", "run north (screen up)", + do_run_north, MOVEMENTCMD | CMD_M_PREFIX, NULL }, + { '\0', "runnortheast", "run northeast (screen upper right)", + do_run_northeast, MOVEMENTCMD | CMD_M_PREFIX, NULL }, + { '\0', "runeast", "run east (screen right)", + do_run_east, MOVEMENTCMD | CMD_M_PREFIX, NULL }, + { '\0', "runsoutheast", "run southeast (screen lower right)", + do_run_southeast, MOVEMENTCMD | CMD_M_PREFIX, NULL }, + { '\0', "runsouth", "run south (screen down)", + do_run_south, MOVEMENTCMD | CMD_M_PREFIX, NULL }, + { '\0', "runsouthwest", "run southwest (screen lower left)", + do_run_southwest, MOVEMENTCMD | CMD_M_PREFIX, NULL }, /* internal commands: only used by game core, not available for user */ { '\0', "clicklook", NULL, doclicklook, INTERNALCMD | MOUSECMD, NULL }, @@ -2965,7 +2083,7 @@ static const struct { { 0, (const char *) 0, FALSE } }; -int extcmdlist_length = SIZE(extcmdlist) - 1; +static int extcmdlist_length = SIZE(extcmdlist) - 1; /* get entry i in the extended commands list. for windowport use. */ struct ext_func_tab * @@ -2984,7 +2102,8 @@ count_bind_keys(void) int i; for (i = 0; i < extcmdlist_length; i++) - if (extcmdlist[i].key && gc.Cmd.commands[extcmdlist[i].key] != &extcmdlist[i]) + if (extcmdlist[i].key + && gc.Cmd.commands[extcmdlist[i].key] != &extcmdlist[i]) nbinds++; return nbinds; } @@ -3021,7 +2140,7 @@ get_changed_key_binds(strbuf_t *sbuf) } /* interactive key binding */ -static void +staticfn void handler_rebind_keys_add(boolean keyfirst) { struct ext_func_tab *ec; @@ -3032,6 +2151,7 @@ handler_rebind_keys_add(boolean keyfirst) char buf[BUFSZ]; char buf2[QBUFSZ]; uchar key = '\0'; + int clr = NO_COLOR; if (keyfirst) { pline("Bind which key? "); @@ -3053,19 +2173,16 @@ handler_rebind_keys_add(boolean keyfirst) Sprintf(buf, "Key '%s' is not bound to anything.", key2txt(key, buf2)); } - add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, 0, buf, - MENU_ITEMFLAGS_NONE); - add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, 0, "", - MENU_ITEMFLAGS_NONE); + add_menu_str(win, buf); + add_menu_str(win, ""); } any.a_int = -1; - add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, 0, "nothing: unbind the key", + add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, clr, + "nothing: unbind the key", MENU_ITEMFLAGS_NONE); - any.a_int = 0; - add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, 0, "", - MENU_ITEMFLAGS_NONE); + add_menu_str(win, ""); for (i = 0; i < extcmdlist_length; i++) { ec = &extcmdlist[i]; @@ -3075,7 +2192,7 @@ handler_rebind_keys_add(boolean keyfirst) any.a_int = (i + 1); Sprintf(buf, "%s: %s", ec->ef_txt, ec->ef_desc); - add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, 0, buf, + add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, clr, buf, MENU_ITEMFLAGS_NONE); } if (key) @@ -3100,7 +2217,7 @@ handler_rebind_keys_add(boolean keyfirst) ec = &extcmdlist[i-1]; cmdstr = ec->ef_txt; } -bindit: + bindit: if (!key) { pline("Bind which key? "); key = pgetchar(); @@ -3116,7 +2233,8 @@ handler_rebind_keys_add(boolean keyfirst) pline("Changed key '%s' from \"%s\" to \"%s\".", key2txt(key, buf2), prevec->ef_txt, cmdstr); } else if (!prevec) { - pline("Bound key '%s' to \"%s\".", key2txt(key, buf2), cmdstr); + pline("Bound key '%s' to \"%s\".", + key2txt(key, buf2), cmdstr); } } else { pline("Key binding failed?!"); @@ -3131,23 +2249,23 @@ handler_rebind_keys(void) anything any; int i, npick; menu_item *picks = (menu_item *) 0; + int clr = NO_COLOR; -redo_rebind: - + redo_rebind: win = create_nhwindow(NHW_MENU); start_menu(win, MENU_BEHAVE_STANDARD); any = cg.zeroany; any.a_int = 1; - add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, 0, "bind key to a command", - MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, clr, + "bind key to a command", MENU_ITEMFLAGS_NONE); any.a_int = 2; - add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, 0, "bind command to a key", - MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, clr, + "bind command to a key", MENU_ITEMFLAGS_NONE); if (count_bind_keys()) { any.a_int = 3; - add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, 0, "view changed key binds", - MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, '\0', 0, ATR_NONE, clr, + "view changed key binds", MENU_ITEMFLAGS_NONE); } end_menu(win, "Do what?"); npick = select_menu(win, PICK_ONE, &picks); @@ -3156,13 +2274,80 @@ handler_rebind_keys(void) i = picks->item.a_int; free((genericptr_t) picks); - if (i == 1 || i == 2) { - handler_rebind_keys_add((i == 1)); - } else if (i == 3) { - get_changed_key_binds(NULL); + if (i == 1 || i == 2) { + handler_rebind_keys_add((i == 1)); + } else if (i == 3) { + get_changed_key_binds(NULL); + } + goto redo_rebind; + } +} + +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); + } } - goto redo_rebind; } + + destroy_nhwindow(win); } /* find extended command entries matching findstr. @@ -3274,7 +2459,8 @@ bind_mousebtn(int btn, const char *command) struct ext_func_tab *extcmd; if (btn < 1 || btn > NUM_MOUSE_BUTTONS) { - config_error_add("Wrong mouse button, valid are 1-%i", NUM_MOUSE_BUTTONS); + config_error_add("Wrong mouse button, valid are 1-%i", + NUM_MOUSE_BUTTONS); return FALSE; } btn--; @@ -3337,7 +2523,7 @@ bind_key(uchar key, const char *command) } /* bind key by ext cmd function */ -static boolean +staticfn boolean bind_key_fn(uchar key, int (*fn)(void)) { struct ext_func_tab *extcmd; @@ -3355,7 +2541,7 @@ bind_key_fn(uchar key, int (*fn)(void)) } /* initialize all keyboard commands */ -static void +staticfn void commands_init(void) { struct ext_func_tab *extcmd; @@ -3390,7 +2576,7 @@ commands_init(void) #endif } -static boolean +staticfn boolean keylist_func_has_key(const struct ext_func_tab *extcmd, boolean *skip_keys_used) /* boolean keys_used[256] */ { @@ -3406,7 +2592,7 @@ keylist_func_has_key(const struct ext_func_tab *extcmd, return FALSE; } -static int +staticfn int keylist_putcmds(winid datawin, boolean docount, int incl_flags, int excl_flags, boolean *keys_used) /* boolean keys_used[256] */ @@ -3613,872 +2799,144 @@ dokeylist(void) #undef IGNORECMD } -const struct ext_func_tab * -ext_func_tab_from_func(int (*fn)(void)) -{ - const struct ext_func_tab *extcmd; - - for (extcmd = extcmdlist; extcmd->ef_txt; ++extcmd) - if (extcmd->ef_funct == fn) - return extcmd; - - return NULL; -} - -/* returns the key bound to a movement command for given DIR_ and MV_ mode */ -char -cmd_from_dir(int dir, int mode) -{ - return cmd_from_func(move_funcs[dir][mode]); -} - -/* return the key bound to extended command */ -char -cmd_from_func(int (*fn)(void)) -{ - int i; - char ret = '\0'; - - /* skip NUL; allowing it would wreak havoc */ - for (i = 1; i < 256; ++i) { - /* skip space; we'll use it below as last resort if no other - keystroke invokes space's command */ - if (i == ' ') - continue; - /* skip digits if number_pad is Off; also skip '-' unless it has - been bound to something other than what number_pad assigns */ - if (((i >= '0' && i <= '9') || (i == '-' && fn == do_fight)) - && !gc.Cmd.num_pad) - continue; - - if (gc.Cmd.commands[i] && gc.Cmd.commands[i]->ef_funct == fn) { - if (i >= ' ' && i <= '~') - return (char) i; - else { - ret = (char) i; - } - } - } - if (gc.Cmd.commands[' '] && gc.Cmd.commands[' ']->ef_funct == fn) - return ' '; - return ret; -} - -/* return visual interpretation of the key bound to extended command, - or the ext cmd name if not bound to any key. */ -char * -cmd_from_ecname(const char *ecname) -{ - static char cmdnamebuf[QBUFSZ]; - const struct ext_func_tab *extcmd; - - for (extcmd = extcmdlist; extcmd->ef_txt; ++extcmd) - if (!strcmp(extcmd->ef_txt, ecname)) { - char key = cmd_from_func(extcmd->ef_funct); - - if (key) - Sprintf(cmdnamebuf, "%s", visctrl(key)); - else - Sprintf(cmdnamebuf, "#%s", ecname); - return cmdnamebuf; - } - - cmdnamebuf[0] = '\0'; - return cmdnamebuf; -} - -static const char * -ecname_from_fn(int (*fn)(void)) -{ - const struct ext_func_tab *extcmd, *cmdptr = 0; - - for (extcmd = extcmdlist; extcmd->ef_txt; ++extcmd) - if (extcmd->ef_funct == fn) { - cmdptr = extcmd; - return cmdptr->ef_txt; - } - return (char *) 0; -} - -/* return extended command name (without leading '#') for command (*fn)() */ -const char * -cmdname_from_func( - int (*fn)(void), /* function whose command name is wanted */ - char outbuf[], /* place to store the result */ - boolean fullname) /* False: just enough to disambiguate */ -{ - const struct ext_func_tab *extcmd, *cmdptr = 0; - const char *res = 0; - - for (extcmd = extcmdlist; extcmd->ef_txt; ++extcmd) - if (extcmd->ef_funct == fn) { - cmdptr = extcmd; - res = cmdptr->ef_txt; - break; - } - - if (!res) { - /* make sure output buffer doesn't contain junk or stale data; - return Null below */ - outbuf[0] = '\0'; - } else if (fullname) { - /* easy; the entire command name */ - res = strcpy(outbuf, res); - } else { - const struct ext_func_tab *matchcmd = extcmdlist; - unsigned len = 0, maxlen = Strlen(res); - - /* find the shortest leading substring which is unambiguous */ - do { - if (++len >= maxlen) - break; - for (extcmd = matchcmd; extcmd->ef_txt; ++extcmd) { - if (extcmd == cmdptr) - continue; - if ((extcmd->flags & CMD_NOT_AVAILABLE) != 0 - || ((extcmd->flags & WIZMODECMD) != 0 && !wizard)) - continue; - if (!strncmp(res, extcmd->ef_txt, len)) { - matchcmd = extcmd; - break; - } - } - } while (extcmd->ef_txt); - copynchars(outbuf, res, len); - /* [note: for Qt, this debugpline writes a couple dozen lines to - stdout during menu setup when message window isn't ready yet] */ - debugpline2("shortened %s: \"%s\"", res, outbuf); - res = outbuf; - } - return res; -} - -/* - * wizard mode sanity_check code - */ - -static const char template[] = "%-27s %4ld %6ld"; -static const char stats_hdr[] = " count bytes"; -static const char stats_sep[] = "--------------------------- ----- -------"; - -static int -size_obj(struct obj *otmp) -{ - int sz = (int) sizeof (struct obj); - - if (otmp->oextra) { - sz += (int) sizeof (struct oextra); - if (ONAME(otmp)) - sz += (int) strlen(ONAME(otmp)) + 1; - if (OMONST(otmp)) - sz += size_monst(OMONST(otmp), FALSE); - if (OMAILCMD(otmp)) - sz += (int) strlen(OMAILCMD(otmp)) + 1; - /* sz += (int) sizeof (unsigned); -- now part of oextra itself */ - } - return sz; -} - -static void -count_obj(struct obj *chain, long *total_count, long *total_size, - boolean top, boolean recurse) -{ - long count, size; - struct obj *obj; - - for (count = size = 0, obj = chain; obj; obj = obj->nobj) { - if (top) { - count++; - size += size_obj(obj); - } - if (recurse && obj->cobj) - count_obj(obj->cobj, total_count, total_size, TRUE, TRUE); - } - *total_count += count; - *total_size += size; -} - -DISABLE_WARNING_FORMAT_NONLITERAL /* RESTORE_WARNING follows show_wiz_stats */ - -static void -obj_chain( - winid win, - const char *src, - struct obj *chain, - boolean force, - long *total_count, long *total_size) -{ - char buf[BUFSZ]; - long count = 0L, size = 0L; - - count_obj(chain, &count, &size, TRUE, FALSE); - - if (count || size || force) { - *total_count += count; - *total_size += size; - Sprintf(buf, template, src, count, size); - putstr(win, 0, buf); - } -} - -static void -mon_invent_chain( - winid win, - const char *src, - struct monst *chain, - long *total_count, long *total_size) -{ - char buf[BUFSZ]; - long count = 0, size = 0; - struct monst *mon; - - for (mon = chain; mon; mon = mon->nmon) - count_obj(mon->minvent, &count, &size, TRUE, FALSE); - - if (count || size) { - *total_count += count; - *total_size += size; - Sprintf(buf, template, src, count, size); - putstr(win, 0, buf); - } -} - -static void -contained_stats( - winid win, - const char *src, - long *total_count, long *total_size) -{ - char buf[BUFSZ]; - long count = 0, size = 0; - struct monst *mon; - - count_obj(gi.invent, &count, &size, FALSE, TRUE); - count_obj(fobj, &count, &size, FALSE, TRUE); - count_obj(gl.level.buriedobjlist, &count, &size, FALSE, TRUE); - count_obj(gm.migrating_objs, &count, &size, FALSE, TRUE); - /* DEADMONSTER check not required in this loop since they have no - * inventory */ - for (mon = fmon; mon; mon = mon->nmon) - count_obj(mon->minvent, &count, &size, FALSE, TRUE); - for (mon = gm.migrating_mons; mon; mon = mon->nmon) - count_obj(mon->minvent, &count, &size, FALSE, TRUE); - - if (count || size) { - *total_count += count; - *total_size += size; - Sprintf(buf, template, src, count, size); - putstr(win, 0, buf); - } -} - -static int -size_monst(struct monst *mtmp, boolean incl_wsegs) -{ - int sz = (int) sizeof (struct monst); - - if (mtmp->wormno && incl_wsegs) - sz += size_wseg(mtmp); - - if (mtmp->mextra) { - sz += (int) sizeof (struct mextra); - if (MGIVENNAME(mtmp)) - sz += (int) strlen(MGIVENNAME(mtmp)) + 1; - if (EGD(mtmp)) - sz += (int) sizeof (struct egd); - if (EPRI(mtmp)) - sz += (int) sizeof (struct epri); - if (ESHK(mtmp)) - sz += (int) sizeof (struct eshk); - if (EMIN(mtmp)) - 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; -} - -static void -mon_chain( - winid win, - const char *src, - struct monst *chain, - boolean force, - long *total_count, long *total_size) -{ - char buf[BUFSZ]; - long count, size; - struct monst *mon; - /* mon->wormno means something different for migrating_mons and mydogs */ - boolean incl_wsegs = !strcmpi(src, "fmon"); - - count = size = 0L; - for (mon = chain; mon; mon = mon->nmon) { - count++; - size += size_monst(mon, incl_wsegs); - } - if (count || size || force) { - *total_count += count; - *total_size += size; - Sprintf(buf, template, src, count, size); - putstr(win, 0, buf); - } -} - -static void -misc_stats( - winid win, - long *total_count, long *total_size) -{ - char buf[BUFSZ], hdrbuf[QBUFSZ]; - long count, size; - int idx; - struct trap *tt; - struct damage *sd; /* shop damage */ - struct kinfo *k; /* delayed killer */ - struct cemetery *bi; /* bones info */ - - /* traps and engravings are output unconditionally; - * others only if nonzero - */ - count = size = 0L; - for (tt = gf.ftrap; tt; tt = tt->ntrap) { - ++count; - size += (long) sizeof *tt; - } - *total_count += count; - *total_size += size; - Sprintf(hdrbuf, "traps, size %ld", (long) sizeof (struct trap)); - Sprintf(buf, template, hdrbuf, count, size); - putstr(win, 0, buf); - - count = size = 0L; - engr_stats("engravings, size %ld+text", hdrbuf, &count, &size); - *total_count += count; - *total_size += size; - Sprintf(buf, template, hdrbuf, count, size); - putstr(win, 0, buf); - - count = size = 0L; - light_stats("light sources, size %ld", hdrbuf, &count, &size); - if (count || size) { - *total_count += count; - *total_size += size; - Sprintf(buf, template, hdrbuf, count, size); - putstr(win, 0, buf); - } - - count = size = 0L; - timer_stats("timers, size %ld", hdrbuf, &count, &size); - if (count || size) { - *total_count += count; - *total_size += size; - Sprintf(buf, template, hdrbuf, count, size); - putstr(win, 0, buf); - } - - count = size = 0L; - for (sd = gl.level.damagelist; sd; sd = sd->next) { - ++count; - size += (long) sizeof *sd; - } - if (count || size) { - *total_count += count; - *total_size += size; - Sprintf(hdrbuf, "shop damage, size %ld", - (long) sizeof (struct damage)); - Sprintf(buf, template, hdrbuf, count, size); - putstr(win, 0, buf); - } - - count = size = 0L; - region_stats("regions, size %ld+%ld*rect+N", hdrbuf, &count, &size); - if (count || size) { - *total_count += count; - *total_size += size; - Sprintf(buf, template, hdrbuf, count, size); - putstr(win, 0, buf); - } - - count = size = 0L; - for (k = gk.killer.next; k; k = k->next) { - ++count; - size += (long) sizeof *k; - } - if (count || size) { - *total_count += count; - *total_size += size; - Sprintf(hdrbuf, "delayed killer%s, size %ld", - plur(count), (long) sizeof (struct kinfo)); - Sprintf(buf, template, hdrbuf, count, size); - putstr(win, 0, buf); - } - - count = size = 0L; - for (bi = gl.level.bonesinfo; bi; bi = bi->next) { - ++count; - size += (long) sizeof *bi; - } - if (count || size) { - *total_count += count; - *total_size += size; - Sprintf(hdrbuf, "bones history, size %ld", - (long) sizeof (struct cemetery)); - Sprintf(buf, template, hdrbuf, count, size); - putstr(win, 0, buf); - } - - count = size = 0L; - for (idx = 0; idx < NUM_OBJECTS; ++idx) - if (objects[idx].oc_uname) { - ++count; - size += (long) (strlen(objects[idx].oc_uname) + 1); - } - if (count || size) { - *total_count += count; - *total_size += size; - Strcpy(hdrbuf, "object type names, text"); - Sprintf(buf, template, hdrbuf, count, size); - putstr(win, 0, buf); - } -} - -/* the #stats command - * Display memory usage of all monsters and objects on the level. - */ -static int -wiz_show_stats(void) -{ - char buf[BUFSZ]; - winid win; - long total_obj_size, total_obj_count, - total_mon_size, total_mon_count, - total_ovr_size, total_ovr_count, - total_misc_size, total_misc_count; - - win = create_nhwindow(NHW_TEXT); - putstr(win, 0, "Current memory statistics:"); - - total_obj_count = total_obj_size = 0L; - putstr(win, 0, stats_hdr); - Sprintf(buf, " Objects, base size %ld", (long) sizeof (struct obj)); - putstr(win, 0, buf); - obj_chain(win, "invent", gi.invent, TRUE, - &total_obj_count, &total_obj_size); - obj_chain(win, "fobj", fobj, TRUE, &total_obj_count, &total_obj_size); - obj_chain(win, "buried", gl.level.buriedobjlist, FALSE, - &total_obj_count, &total_obj_size); - obj_chain(win, "migrating obj", gm.migrating_objs, FALSE, - &total_obj_count, &total_obj_size); - obj_chain(win, "billobjs", gb.billobjs, FALSE, - &total_obj_count, &total_obj_size); - mon_invent_chain(win, "minvent", fmon, &total_obj_count, &total_obj_size); - mon_invent_chain(win, "migrating minvent", gm.migrating_mons, - &total_obj_count, &total_obj_size); - contained_stats(win, "contained", &total_obj_count, &total_obj_size); - putstr(win, 0, stats_sep); - Sprintf(buf, template, " Obj total", total_obj_count, total_obj_size); - putstr(win, 0, buf); - - total_mon_count = total_mon_size = 0L; - putstr(win, 0, ""); - Sprintf(buf, " Monsters, base size %ld", (long) sizeof (struct monst)); - putstr(win, 0, buf); - mon_chain(win, "fmon", fmon, TRUE, &total_mon_count, &total_mon_size); - mon_chain(win, "migrating", gm.migrating_mons, FALSE, - &total_mon_count, &total_mon_size); - /* 'gm.mydogs' is only valid during level change or end of game disclosure, - but conceivably we've been called from within debugger at such time */ - if (gm.mydogs) /* monsters accompanying hero */ - mon_chain(win, "mydogs", gm.mydogs, FALSE, - &total_mon_count, &total_mon_size); - putstr(win, 0, stats_sep); - Sprintf(buf, template, " Mon total", total_mon_count, total_mon_size); - putstr(win, 0, buf); - - total_ovr_count = total_ovr_size = 0L; - putstr(win, 0, ""); - putstr(win, 0, " Overview"); - overview_stats(win, template, &total_ovr_count, &total_ovr_size); - putstr(win, 0, stats_sep); - Sprintf(buf, template, " Over total", total_ovr_count, total_ovr_size); - putstr(win, 0, buf); - - total_misc_count = total_misc_size = 0L; - putstr(win, 0, ""); - putstr(win, 0, " Miscellaneous"); - misc_stats(win, &total_misc_count, &total_misc_size); - putstr(win, 0, stats_sep); - Sprintf(buf, template, " Misc total", total_misc_count, total_misc_size); - putstr(win, 0, buf); - - putstr(win, 0, ""); - putstr(win, 0, stats_sep); - Sprintf(buf, template, " Grand total", - (total_obj_count + total_mon_count - + total_ovr_count + total_misc_count), - (total_obj_size + total_mon_size - + total_ovr_size + total_misc_size)); - putstr(win, 0, buf); +const struct ext_func_tab * +ext_func_tab_from_func(int (*fn)(void)) +{ + const struct ext_func_tab *extcmd; -#if defined(__BORLANDC__) && !defined(_WIN32) - show_borlandc_stats(win); -#endif + for (extcmd = extcmdlist; extcmd->ef_txt; ++extcmd) + if (extcmd->ef_funct == fn) + return extcmd; - display_nhwindow(win, FALSE); - destroy_nhwindow(win); - return ECMD_OK; + return NULL; } -RESTORE_WARNING_FORMAT_NONLITERAL +/* returns the key bound to a movement command for given DIR_ and MV_ mode */ +char +cmd_from_dir(int dir, int mode) +{ + return cmd_from_func(move_funcs[dir][mode]); +} -#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) -/* the #wizdispmacros command - * Verify that some display macros are returning sane values */ -static int -wiz_display_macros(void) +/* return the key bound to extended command */ +char +cmd_from_func(int (*fn)(void)) { - static const char display_issues[] = "Display macro issues:"; - char buf[BUFSZ]; - winid win; - int glyph, test, trouble = 0, no_glyph = NO_GLYPH, max_glyph = MAX_GLYPH; + int i; + char ret = '\0'; - win = create_nhwindow(NHW_TEXT); + /* skip NUL; allowing it would wreak havoc */ + for (i = 1; i < 256; ++i) { + /* skip space; we'll use it below as last resort if no other + keystroke invokes space's command */ + if (i == ' ') + continue; + /* skip digits if number_pad is Off; also skip '-' unless it has + been bound to something other than what number_pad assigns */ + if (((i >= '0' && i <= '9') || (i == '-' && fn == do_fight)) + && !gc.Cmd.num_pad) + continue; - for (glyph = 0; glyph < MAX_GLYPH; ++glyph) { - /* glyph_is_cmap / glyph_to_cmap() */ - if (glyph_is_cmap(glyph)) { - test = glyph_to_cmap(glyph); - /* check for MAX_GLYPH return */ - if (test == no_glyph) { - if (!trouble++) - putstr(win, 0, display_issues); - Sprintf(buf, "glyph_is_cmap() / glyph_to_cmap(glyph=%d)" - " sync failure, returned NO_GLYPH (%d)", - glyph, test); - putstr(win, 0, buf); - } - if (glyph_is_cmap_zap(glyph) - && !(test >= S_vbeam && test <= S_rslant)) { - if (!trouble++) - putstr(win, 0, display_issues); - Sprintf(buf, - "glyph_is_cmap_zap(glyph=%d) returned non-zap cmap %d", - glyph, test); - putstr(win, 0, buf); - } - /* check against defsyms array subscripts */ - if (test < 0 || test >= SIZE(defsyms)) { - if (!trouble++) - putstr(win, 0, display_issues); - Sprintf(buf, "glyph_to_cmap(glyph=%d) returns %d" - " exceeds defsyms[%d] bounds (MAX_GLYPH = %d)", - glyph, test, SIZE(defsyms), max_glyph); - putstr(win, 0, buf); - } - } - /* glyph_is_monster / glyph_to_mon */ - if (glyph_is_monster(glyph)) { - test = glyph_to_mon(glyph); - /* check against mons array subscripts */ - if (test < 0 || test >= NUMMONS) { - if (!trouble++) - putstr(win, 0, display_issues); - Sprintf(buf, "glyph_to_mon(glyph=%d) returns %d" - " exceeds mons[%d] bounds", - glyph, test, NUMMONS); - putstr(win, 0, buf); - } - } - /* glyph_is_object / glyph_to_obj */ - if (glyph_is_object(glyph)) { - test = glyph_to_obj(glyph); - /* check against objects array subscripts */ - if (test < 0 || test > NUM_OBJECTS) { - if (!trouble++) - putstr(win, 0, display_issues); - Sprintf(buf, "glyph_to_obj(glyph=%d) returns %d" - " exceeds objects[%d] bounds", - glyph, test, NUM_OBJECTS); - putstr(win, 0, buf); + if (gc.Cmd.commands[i] && gc.Cmd.commands[i]->ef_funct == fn) { + if (i >= ' ' && i <= '~') + return (char) i; + else { + ret = (char) i; } } } - if (!trouble) - putstr(win, 0, "No display macro issues detected."); - display_nhwindow(win, FALSE); - destroy_nhwindow(win); - return ECMD_OK; + if (gc.Cmd.commands[' '] && gc.Cmd.commands[' ']->ef_funct == fn) + return ' '; + return ret; } -#endif /* (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) */ -#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) -/* the #wizmondiff command */ -static int -wiz_mon_diff(void) +/* return visual interpretation of the key bound to extended command, + or the ext cmd name if not bound to any key. */ +char * +cmd_from_ecname(const char *ecname) { - static const char window_title[] = "Review of monster difficulty ratings" - " [index:level]:"; - char buf[BUFSZ]; - winid win; - int mhardcoded = 0, mcalculated = 0, trouble = 0, cnt = 0, mdiff = 0; - int mlev; - struct permonst *ptr; + static char cmdnamebuf[QBUFSZ]; + const struct ext_func_tab *extcmd; - /* - * Possible extension: choose between showing discrepancies, - * showing all monsters, or monsters within a particular class. - */ + for (extcmd = extcmdlist; extcmd->ef_txt; ++extcmd) + if (!strcmp(extcmd->ef_txt, ecname)) { + char key = cmd_from_func(extcmd->ef_funct); - win = create_nhwindow(NHW_TEXT); - for (ptr = &mons[0]; ptr->mlet; ptr++, cnt++) { - mcalculated = mstrength(ptr); - mhardcoded = (int) ptr->difficulty; - mdiff = mhardcoded - mcalculated; - if (mdiff) { - if (!trouble++) - putstr(win, 0, window_title); - mlev = (int) ptr->mlevel; - if (mlev > 50) /* hack for named demons */ - mlev = 50; - Snprintf(buf, sizeof buf, - "%-18s [%3d:%2d]: calculated: %2d, hardcoded: %2d (%+d)", - ptr->pmnames[NEUTRAL], cnt, mlev, - mcalculated, mhardcoded, mdiff); - putstr(win, 0, buf); + if (key) + Sprintf(cmdnamebuf, "%s", visctrl(key)); + else + Sprintf(cmdnamebuf, "#%s", ecname); + return cmdnamebuf; } - } - if (!trouble) - putstr(win, 0, "No monster difficulty discrepencies were detected."); - display_nhwindow(win, FALSE); - destroy_nhwindow(win); - return ECMD_OK; -} -#endif /* (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) */ - -static void -you_sanity_check(void) -{ - struct monst *mtmp; - - if (u.uswallow && !u.ustuck) { - /* this probably ought to be panic() */ - impossible("sanity_check: swallowed by nothing?"); - display_nhwindow(WIN_MESSAGE, TRUE); - /* try to recover from whatever the problem is */ - u.uswallow = 0; - u.uswldtim = 0; - docrt(); - } - if ((mtmp = m_at(u.ux, u.uy)) != 0) { - /* u.usteed isn't on the map */ - if (u.ustuck != mtmp) - impossible("sanity_check: you over monster"); - } - (void) check_invent_gold("invent"); + cmdnamebuf[0] = '\0'; + return cmdnamebuf; } -void -sanity_check(void) -{ - if (iflags.sanity_no_check) { - /* in case a recurring sanity_check warning occurs, we mustn't - re-trigger it when ^P is used, otherwise msg_window:Single - and msg_window:Combination will always repeat the most recent - instance, never able to go back to any earlier messages */ - iflags.sanity_no_check = FALSE; - return; - } - you_sanity_check(); - obj_sanity_check(); - timer_sanity_check(); - mon_sanity_check(); - light_sources_sanity_check(); - bc_sanity_check(); - trap_sanity_check(); - engraving_sanity_check(); -} - -/* qsort() comparison routine for use in list_migrating_mons() */ -static int QSORTCALLBACK -migrsort_cmp(const genericptr vptr1, const genericptr vptr2) -{ - const struct monst *m1 = *(const struct monst **) vptr1, - *m2 = *(const struct monst **) vptr2; - int d1 = (int) m1->mux, l1 = (int) m1->muy, - d2 = (int) m2->mux, l2 = (int) m2->muy; - - /* if different branches, sort by dungeon number */ - if (d1 != d2) - return d1 - d2; - /* within same branch, sort by level number */ - if (l1 != l2) - return l1 - l2; - /* same destination level: use a tie-breaker to force stable sort; - monst->m_id is unsigned so we need more than just simple subtraction */ - return (m1->m_id < m2->m_id) ? -1 : (m1->m_id > m2->m_id); -} - -/* called by #migratemons; displays count of migrating monsters, optionally - displays them as well */ -static void -list_migrating_mons( - d_level *nextlevl) /* default destination for wiz_migrate_mons() */ +const char * +ecname_from_fn(int (*fn)(void)) { - winid win = WIN_ERR; - boolean showit = FALSE; - unsigned n; - int xyloc; - coordxy x, y; - char c, prmpt[10], xtra[10], buf[BUFSZ]; - struct monst *mtmp, **marray; - int here = 0, nxtlv = 0, other = 0; - - for (mtmp = gm.migrating_mons; mtmp; mtmp = mtmp->nmon) { - if (mtmp->mux == u.uz.dnum && mtmp->muy == u.uz.dlevel) - ++here; - else if (mtmp->mux == nextlevl->dnum && mtmp->muy == nextlevl->dlevel) - ++nxtlv; - else - ++other; - } - if (here + nxtlv + other == 0) { - pline("No monsters currently migrating."); - } else { - pline( - "%d mon%s pending for current level, %d for next level, %d for others.", - here, plur(here), nxtlv, other); - prmpt[0] = xtra[0] = '\0'; - (void) strkitten(here ? prmpt : xtra, 'c'); - (void) strkitten(nxtlv ? prmpt : xtra, 'n'); - (void) strkitten(other ? prmpt : xtra, 'o'); - Strcat(prmpt, "a q"); - if (*xtra) - Sprintf(eos(prmpt), "%c%s", '\033', xtra); - c = yn_function("List which?", prmpt, 'q', TRUE); - n = (c == 'c') ? here - : (c == 'n') ? nxtlv - : (c == 'o') ? other - : (c == 'a') ? here + nxtlv + other - : 0; - if (n > 0) { - win = create_nhwindow(NHW_TEXT); - switch (c) { - case 'c': - case 'n': - case 'o': - Sprintf(buf, "Monster%s migrating to %s:", plur(n), - (c == 'c') ? "current level" - : (c == 'n') ? "next level" - : "'other' levels"); - break; - default: - Strcpy(buf, "All migrating monsters:"); - break; - } - putstr(win, 0, buf); - putstr(win, 0, ""); - /* collect the migrating monsters into an array; for 'o' and 'a' - where multiple destination levels might be present, sort by - the destination; 'c' and 'n' don't need to be sorted but we - do that anyway to get the same tie-breaker as 'o' and 'a' */ - marray = (struct monst **) alloc((n + 1) * sizeof *marray); - n = 0; - for (mtmp = gm.migrating_mons; mtmp; mtmp = mtmp->nmon) { - if (c == 'a') - showit = TRUE; - else if (mtmp->mux == u.uz.dnum && mtmp->muy == u.uz.dlevel) - showit = (c == 'c'); - else if (mtmp->mux == nextlevl->dnum - && mtmp->muy == nextlevl->dlevel) - showit = (c == 'n'); - else - showit = (c == 'o'); + const struct ext_func_tab *extcmd, *cmdptr = 0; - if (showit) - marray[n++] = mtmp; - } - marray[n] = (struct monst *) 0; /* mark end for traversal loop */ - if (n > 1) - qsort((genericptr_t) marray, (size_t) n, sizeof *marray, - migrsort_cmp); /* sort elements [0] through [n-1] */ - for (n = 0; (mtmp = marray[n]) != 0; ++n) { - Sprintf(buf, " %s", minimal_monnam(mtmp, FALSE)); - /* minimal_monnam() appends map coordinates; strip that */ - (void) strsubst(buf, " <0,0>", ""); - if (has_mgivenname(mtmp)) /* if mtmp is named, include that */ - Sprintf(eos(buf), " named %s", MGIVENNAME(mtmp)); - if (c == 'o' || c == 'a') - Sprintf(eos(buf), " to %d:%d", mtmp->mux, mtmp->muy); - xyloc = mtmp->mtrack[0].x; /* (for legibility) */ - if (xyloc == MIGR_EXACT_XY) { - x = mtmp->mtrack[1].x; - y = mtmp->mtrack[1].y; - Sprintf(eos(buf), " at <%d,%d>", (int) x, (int) y); - } - putstr(win, 0, buf); - } - free((genericptr_t) marray); - display_nhwindow(win, FALSE); - destroy_nhwindow(win); - } else if (c != 'q') { - pline("None."); + for (extcmd = extcmdlist; extcmd->ef_txt; ++extcmd) + if (extcmd->ef_funct == fn) { + cmdptr = extcmd; + return cmdptr->ef_txt; } - - } + return (char *) 0; } -/* #migratemons command */ -static int -wiz_migrate_mons(void) +/* return extended command name (without leading '#') for command (*fn)() */ +const char * +cmdname_from_func( + int (*fn)(void), /* function whose command name is wanted */ + char outbuf[], /* place to store the result */ + boolean fullname) /* False: just enough to disambiguate */ { -#ifdef DEBUG_MIGRATING_MONS - int mcount; - char inbuf[BUFSZ]; - struct permonst *ptr; - struct monst *mtmp; -#endif - d_level tolevel; - - if (Is_stronghold(&u.uz)) - assign_level(&tolevel, &valley_level); - else if (!Is_botlevel(&u.uz)) - get_level(&tolevel, depth(&u.uz) + 1); - else - tolevel.dnum = 0, tolevel.dlevel = 0; + const struct ext_func_tab *extcmd, *cmdptr = 0; + const char *res = 0; - list_migrating_mons(&tolevel); + for (extcmd = extcmdlist; extcmd->ef_txt; ++extcmd) + if (extcmd->ef_funct == fn) { + cmdptr = extcmd; + res = cmdptr->ef_txt; + break; + } -#ifdef DEBUG_MIGRATING_MONS - inbuf[0] = '\033', inbuf[1] = '\0'; - if (tolevel.dnum || tolevel.dlevel) - getlin("How many random monsters to migrate to next level? [0]", - inbuf); - else - pline("Can't get there from here."); - if (*inbuf == '\033') - return ECMD_OK; + if (!res) { + /* make sure output buffer doesn't contain junk or stale data; + return Null below */ + outbuf[0] = '\0'; + } else if (fullname) { + /* easy; the entire command name */ + res = strcpy(outbuf, res); + } else { + const struct ext_func_tab *matchcmd = extcmdlist; + unsigned len = 0, maxlen = Strlen(res); - mcount = atoi(inbuf); - 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 (mtmp) - migrate_to_level(mtmp, ledger_no(&tolevel), MIGR_RANDOM, - (coord *) 0); - mcount--; - } -#endif /* DEBUG_MIGRATING_MONS */ - return ECMD_OK; + /* find the shortest leading substring which is unambiguous */ + do { + if (++len >= maxlen) + break; + for (extcmd = matchcmd; extcmd->ef_txt; ++extcmd) { + if (extcmd == cmdptr) + continue; + if ((extcmd->flags & CMD_NOT_AVAILABLE) != 0 + || ((extcmd->flags & WIZMODECMD) != 0 && !wizard)) + continue; + if (!strncmp(res, extcmd->ef_txt, len)) { + matchcmd = extcmd; + break; + } + } + } while (extcmd->ef_txt); + copynchars(outbuf, res, len); + /* [note: for Qt, this debugpline writes a couple dozen lines to + stdout during menu setup when message window isn't ready yet] */ + debugpline2("shortened %s: \"%s\"", res, outbuf); + res = outbuf; + } + return res; } static struct { @@ -4531,7 +2989,7 @@ bind_specialkey(uchar key, const char *command) return FALSE; } -static const char * +staticfn const char * spkey_name(int nhkf) { const char *name = 0; @@ -4571,7 +3029,7 @@ void parseautocomplete(char *autocomplete, boolean condition) { struct ext_func_tab *efp; - register char *autoc; + char *autoc; /* break off first autocomplete from the rest; parse the rest */ if ((autoc = strchr(autocomplete, ',')) != 0 @@ -4598,6 +3056,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)) { + if ((efp->flags & AUTOCOMP_ADJ)) + efp->flags &= ~AUTOCOMP_ADJ; + else + efp->flags |= AUTOCOMP_ADJ; + } if (condition) efp->flags |= AUTOCOMPLETE; else @@ -4612,6 +3076,36 @@ parseautocomplete(char *autocomplete, boolean condition) wait_synch(); } +/* add changed autocompletions to the string buffer in config file format */ +void +all_options_autocomplete(strbuf_t *sbuf) +{ + struct ext_func_tab *efp; + char buf[BUFSZ]; + + for (efp = extcmdlist; efp->ef_txt; efp++) + if ((efp->flags & AUTOCOMP_ADJ) != 0) { + Sprintf(buf, "AUTOCOMPLETE=%s%s\n", + (efp->flags & AUTOCOMPLETE) ? "" : "!", + efp->ef_txt); + strbuf_append(sbuf, buf); + } +} + +/* 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) @@ -4713,13 +3207,13 @@ reset_commands(boolean initial) /* phone_layout has been toggled */ for (i = 0; i < 3; i++) { c = '1' + i; /* 1,2,3 <-> 7,8,9 */ - cmdtmp = gc.Cmd.commands[c]; /* tmp = [1] */ + cmdtmp = gc.Cmd.commands[c]; /* tmp = [1] */ gc.Cmd.commands[c] = gc.Cmd.commands[c + 6]; /* [1] = [7] */ - gc.Cmd.commands[c + 6] = cmdtmp; /* [7] = tmp */ + gc.Cmd.commands[c + 6] = cmdtmp; /* [7] = tmp */ c = (M('1') & 0xff) + i; /* M-1,M-2,M-3 <-> M-7,M-8,M-9 */ - cmdtmp = gc.Cmd.commands[c]; /* tmp = [M-1] */ - gc.Cmd.commands[c] = gc.Cmd.commands[c + 6]; /* [M-1] = [M-7] */ - gc.Cmd.commands[c + 6] = cmdtmp; /* [M-7] = tmp */ + cmdtmp = gc.Cmd.commands[c]; /* tmp = [M-1] */ + gc.Cmd.commands[c] = gc.Cmd.commands[c + 6]; /* [M-1]=[M-7] */ + gc.Cmd.commands[c + 6] = cmdtmp; /* [M-7] = tmp */ } } } /*?initial*/ @@ -4799,7 +3293,7 @@ update_rest_on_space(void) /* commands which accept 'm' prefix to request menu operation or other alternate behavior; it's also overloaded for move-without-autopickup; there is no overlap between the two groups of commands */ -static boolean +staticfn boolean accept_menu_prefix(const struct ext_func_tab *ec) { return (ec && ((ec->flags & CMD_M_PREFIX) != 0)); @@ -4898,16 +3392,16 @@ rnd_extcmd_idx(void) return rn2(extcmdlist_length + 1) - 1; } -static void +staticfn void reset_cmd_vars(boolean reset_cmdq) { - gc.context.run = 0; - gc.context.nopick = gc.context.forcefight = FALSE; - gc.context.move = gc.context.mv = FALSE; + svc.context.run = 0; + svc.context.nopick = svc.context.forcefight = FALSE; + svc.context.move = svc.context.mv = FALSE; gd.domove_attempting = 0; gm.multi = 0; iflags.menu_requested = FALSE; - gc.context.travel = gc.context.travel1 = 0; + svc.context.travel = svc.context.travel1 = 0; if (gt.travelmap) { selection_free(gt.travelmap, TRUE); gt.travelmap = NULL; @@ -4919,19 +3413,19 @@ reset_cmd_vars(boolean reset_cmdq) } void -rhack(char *cmd) +rhack(int key) { - char queuedkeystroke[2]; - boolean bad_command, firsttime = (cmd == 0); + boolean bad_command, firsttime = (key == 0); struct _cmd_queue cq, *cmdq = NULL; const struct ext_func_tab *cmdq_ec = 0, *prefix_seen = 0; boolean was_m_prefix = FALSE; + int (*func)(void) = dummyfunction; iflags.menu_requested = FALSE; - gc.context.nopick = 0; + svc.context.nopick = 0; got_prefix_input: #ifdef SAFERHANGUP - if (gp.program_state.done_hup) + if (program_state.done_hup) end_of_input(); #endif if ((cmdq = cmdq_pop()) != 0) { @@ -4940,55 +3434,57 @@ rhack(char *cmd) free(cmdq); if (cq.typ == CMDQ_EXTCMD && (cmdq_ec = cq.ec_entry) != 0) goto do_cmdq_extcmd; - cmd = queuedkeystroke; /* already handled a queued command (goto do_cmdq_extcmd); if something other than a key is queued, we'll drop down to the !*cmd handling which clears out the command-queue */ - cmd[0] = (cq.typ == CMDQ_KEY) ? cq.key : '\0'; - cmd[1] = '\0'; + key = (cq.typ == CMDQ_KEY) ? cq.key : 0; } else if (firsttime) { - cmd = parse(); + key = parse(); #ifdef FUZZER_LOG { char buf[BUFSZ]; - Sprintf(buf, "rhack() cmd: '%s'", visctrl(*cmd)); + Sprintf(buf, "rhack() key: '%d'", key); fuz_log(buf); } #endif /* FUZZER_LOG */ /* parse() pushed a cmd but didn't return any key */ - if (!*cmd && cmdq_peek(CQ_CANNED)) + if (!key && cmdq_peek(CQ_CANNED)) goto got_prefix_input; } /* if there's no command, there's nothing to do except reset */ - if (!cmd || !*cmd || *cmd == (char) 0377 - || *cmd == gc.Cmd.spkeys[NHKF_ESC]) { - if (*cmd == gc.Cmd.spkeys[NHKF_ESC]) { + if (!key || key == (char) 0377 + || key == gc.Cmd.spkeys[NHKF_ESC]) { + if (key == gc.Cmd.spkeys[NHKF_ESC]) { + /* don't perform next sanity check if player typed ESC for + the current command, similar to handling for CMD_INSANE + flag below (^P and ^R) */ + iflags.sanity_no_check = iflags.sanity_check; /* a clever player might try to press Escape to escape from a * monster... */ if (u.ustuck && !sticks(gy.youmonst.data)) { pline("You cannot escape from %s!", mon_nam(u.ustuck)); } } - if (!cmd || *cmd != gc.Cmd.spkeys[NHKF_ESC]) + else nhbell(); reset_cmd_vars(TRUE); return; } /* handle most movement commands */ - gc.context.travel = gc.context.travel1 = 0; + svc.context.travel = svc.context.travel1 = 0; { - register const struct ext_func_tab *tlist; - int res, (*func)(void); + const struct ext_func_tab *tlist; + int res; do_cmdq_extcmd: if (cmdq_ec) tlist = cmdq_ec; else - tlist = gc.Cmd.commands[*cmd & 0xff]; + tlist = gc.Cmd.commands[key & 0xff]; - /* current - use *cmd to directly index cmdlist array */ + /* current - use key to directly index cmdlist array */ if (tlist != 0) { if (!can_do_extcmd(tlist)) { /* can_do_extcmd() already gave a message */ @@ -5013,17 +3509,17 @@ rhack(char *cmd) pline("The %s command does not accept '%s' prefix.", tlist->ef_txt, which); } else { - uchar key = tlist->key; - boolean up = (key == '<' || tlist->ef_funct == doup), - down = (key == '>' || tlist->ef_funct == dodown); + uchar ch = tlist->key; + boolean up = (ch == '<' || tlist->ef_funct == doup), + down = (ch == '>' || tlist->ef_funct == dodown); pline( "The '%s' prefix should be followed by a movement command%s.", - which, (up || down) ? " other than up or down" : ""); + which, + (up || down) ? " other than up or down" : ""); } 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() */ @@ -5078,7 +3574,6 @@ rhack(char *cmd) return; } prefix_seen = tlist; - bad_command = FALSE; cmdq_ec = NULL; if (func == do_reqmenu) was_m_prefix = TRUE; @@ -5087,18 +3582,18 @@ rhack(char *cmd) && gd.domove_attempting) { /* not a movement command, but a move prefix earlier? */ ; /* just do nothing */ - } else if (((gd.domove_attempting & (DOMOVE_RUSH | DOMOVE_WALK)) - != 0L) - && !gc.context.travel && !dxdy_moveok()) { + } else if (((gd.domove_attempting + & (DOMOVE_RUSH | DOMOVE_WALK)) != 0L) + && !svc.context.travel && !dxdy_moveok()) { /* trying to move diagonally as a grid bug */ You_cant("get there from here..."); reset_cmd_vars(TRUE); return; } else if ((gd.domove_attempting & DOMOVE_WALK) != 0L) { if (gm.multi) - gc.context.mv = TRUE; + svc.context.mv = TRUE; domove(); - gc.context.forcefight = 0; + svc.context.forcefight = 0; iflags.menu_requested = FALSE; return; } else if ((gd.domove_attempting & DOMOVE_RUSH) != 0L) { @@ -5107,13 +3602,12 @@ rhack(char *cmd) gm.multi = max(COLNO, ROWNO); u.last_str_turn = 0; } - gc.context.mv = TRUE; + svc.context.mv = TRUE; domove(); iflags.menu_requested = FALSE; 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 @@ -5129,8 +3623,14 @@ rhack(char *cmd) } /* reset_cmd_vars() sets context.move to False so we might need to change it [back] to True */ - if ((res & ECMD_TIME) != 0) - gc.context.move = TRUE; + if ((res & ECMD_TIME) != 0) { + svc.context.move = TRUE; + if (func != dokick) { + /* hero did something else than kicking a location; + reset the location, so pets don't avoid it */ + gk.kickedloc.x = 0, gk.kickedloc.y = 0; + } + } return; } /* if we reach here, cmd wasn't found in cmdlist[] */ @@ -5138,36 +3638,22 @@ rhack(char *cmd) } if (bad_command) { - char expcmd[20]; /* we expect 'cmd' to point to 1 or 2 chars */ - char c, c1 = cmd[1]; - - expcmd[0] = '\0'; - while ((c = *cmd++) != '\0') - Strcat(expcmd, visctrl(c)); /* add 1..4 chars plus terminator */ -#if 1 - nhUse(c1); -#else - /* note: since prefix keys became actual commnads, we can no longer - get here with 'prefix_seen' set so this never calls help_dir() - anymore */ - if (!prefix_seen - || !help_dir(c1, prefix_seen->key, "Invalid direction key!")) -#endif - Norep("Unknown command '%s'.", expcmd); + 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 */ - gc.context.move = FALSE; + svc.context.move = FALSE; gm.multi = 0; return; } /* convert an x,y pair into a direction code */ -coordxy +int xytod(coordxy x, coordxy y) { - register int dd; + int dd; for (dd = 0; dd < N_DIRS; dd++) if (x == xdir[dd] && y == ydir[dd]) @@ -5191,8 +3677,8 @@ movecmd(char sym, int mode) { int d = DIR_ERR; - if (gc.Cmd.commands[(uchar)sym]) { - int (*fnc)(void) = gc.Cmd.commands[(uchar)sym]->ef_funct; + if (gc.Cmd.commands[(uchar) sym]) { + int (*fnc)(void) = gc.Cmd.commands[(uchar) sym]->ef_funct; if (mode == MV_ANY) { for (d = N_DIRS_Z - 1; d > DIR_ERR; d--) @@ -5285,7 +3771,8 @@ getdir(const char *s) if (!cmdq->dirz) { dirsym = gc.Cmd.dirchars[xytod(cmdq->dirx, cmdq->diry)]; } else { - dirsym = gc.Cmd.dirchars[(cmdq->dirz > 0) ? DIR_DOWN : DIR_UP]; + dirsym = gc.Cmd.dirchars[(cmdq->dirz > 0) ? DIR_DOWN + : DIR_UP]; } } else if (cmdq->typ == CMDQ_KEY) { dirsym = cmdq->key; @@ -5299,7 +3786,7 @@ getdir(const char *s) } retry: - gp.program_state.input_state = getdirInp; + program_state.input_state = getdirInp; if (gi.in_doagain || *readchar_queue) dirsym = readchar(); else @@ -5394,7 +3881,8 @@ getdir(const char *s) break; } } - iflags.getdir_click = mod; + if (iflags.getdir_click) + iflags.getdir_click = mod; return (pos >= 0); } else if (!(is_mov = movecmd(dirsym, MV_ANY)) && !u.dz) { boolean did_help = FALSE, help_requested; @@ -5405,7 +3893,7 @@ getdir(const char *s) did_help = help_dir((s && *s == '^') ? dirsym : '\0', gc.Cmd.spkeys[NHKF_ESC], help_requested ? (const char *) 0 - : "Invalid direction key!"); + : "Invalid direction key!"); if (help_requested) goto retry; } @@ -5422,7 +3910,7 @@ getdir(const char *s) return 1; } -static void +staticfn void show_direction_keys( winid win, /* should specify a window which is using a fixed-width font */ char centerchar, /* '.' or '@' or ' ' */ @@ -5471,7 +3959,7 @@ show_direction_keys( /* explain choices if player has asked for getdir() help or has given an invalid direction after a prefix key ('F', 'g', 'm', &c), which might be bogus but could be up, down, or self when not applicable */ -static boolean +staticfn boolean help_dir( char sym, uchar spkey, /* actual key; either prefix or ESC */ @@ -5604,11 +4092,13 @@ void confdir(boolean force_impairment) { if (force_impairment || u_maybe_impaired()) { - int x = NODIAG(u.umonnum) ? (int) dirs_ord[rn2(4)] : rn2(N_DIRS); + int kmax = NODIAG(u.umonnum) ? (N_DIRS / 2) : N_DIRS, + k = (int) dirs_ord[rn2(kmax)]; - u.dx = xdir[x]; - u.dy = ydir[x]; + u.dx = xdir[k]; + u.dy = ydir[k]; } + return; } const char * @@ -5625,14 +4115,14 @@ directionname(int dir) } int -isok(register coordxy x, register coordxy y) +isok(coordxy x, coordxy y) { /* x corresponds to curx, so x==1 is the first column. Ach. %% */ return x >= 1 && x <= COLNO - 1 && y >= 0 && y <= ROWNO - 1; } /* #herecmdmenu command */ -static int +staticfn int doherecmdmenu(void) { char ch = here_cmd_menu(); @@ -5641,7 +4131,7 @@ doherecmdmenu(void) } /* #therecmdmenu command, a way to test there_cmd_menu without mouse */ -static int +staticfn int dotherecmdmenu(void) { char ch; @@ -5657,6 +4147,7 @@ dotherecmdmenu(void) else ch = there_cmd_menu(x, y, iflags.getdir_click); gc.clicklook_cc.x = gc.clicklook_cc.y = -1; + iflags.getdir_click = 0; return (ch && ch != '\033') ? ECMD_TIME : ECMD_OK; } @@ -5718,11 +4209,11 @@ enum menucmd { MCMD_TRAVEL, }; -static void +staticfn void mcmd_addmenu(winid win, int act, const char *txt) { anything any; - int clr = 0; + int clr = NO_COLOR; /* TODO: fixed letters for the menu entries? */ any = cg.zeroany; @@ -5732,7 +4223,7 @@ mcmd_addmenu(winid win, int act, const char *txt) } /* command menu entries when targeting self */ -static int +staticfn int there_cmd_menu_self(winid win, coordxy x, coordxy y, int *act UNUSED) { int K = 0; @@ -5782,7 +4273,7 @@ there_cmd_menu_self(winid win, coordxy x, coordxy y, int *act UNUSED) #endif if (OBJ_AT(x, y)) { - struct obj *otmp = gl.level.objects[x][y]; + struct obj *otmp = svl.level.objects[x][y]; Sprintf(buf, "Pick up %s", otmp->nexthere ? "items" : doname(otmp)); mcmd_addmenu(win, MCMD_PICKUP, buf), ++K; @@ -5821,7 +4312,7 @@ there_cmd_menu_self(winid win, coordxy x, coordxy y, int *act UNUSED) } /* add entries to there_cmd_menu, when x,y is next to hero */ -static int +staticfn int there_cmd_menu_next2u( winid win, coordxy x, coordxy y, @@ -5920,7 +4411,7 @@ there_cmd_menu_next2u( return K; } -static int +staticfn int there_cmd_menu_far(winid win, coordxy x, coordxy y, int mod) { int K = 0; @@ -5935,7 +4426,7 @@ there_cmd_menu_far(winid win, coordxy x, coordxy y, int mod) return K; } -static int +staticfn int there_cmd_menu_common( winid win, coordxy x, coordxy y, @@ -5954,7 +4445,7 @@ there_cmd_menu_common( } /* queue up command(s) to perform #therecmdmenu action */ -static void +staticfn void act_on_act( int act, /* action */ coordxy dx, coordxy dy) /* delta to adjacent spot (farther sometimes) */ @@ -6139,7 +4630,7 @@ act_on_act( /* offer choice of actions to perform at adjacent location ; a few choices can be farther away */ -static char +staticfn char there_cmd_menu(coordxy x, coordxy y, int mod) { winid win; @@ -6195,7 +4686,7 @@ there_cmd_menu(coordxy x, coordxy y, int mod) return ch; } -static char +staticfn char here_cmd_menu(void) { there_cmd_menu(u.ux, u.uy, CLICK_1); @@ -6212,7 +4703,7 @@ click_to_cmd(coordxy x, coordxy y, int mod) cmdq_add_ec(CQ_CANNED, gc.Cmd.mousebtn[mod-1]->ef_funct); } -static int +staticfn int domouseaction(void) { coordxy x, y; @@ -6316,7 +4807,7 @@ get_count( unsigned gc_flags) /* control flags: GC_SAVEHIST, GC_ECHOFIRST */ { char qbuf[QBUFSZ]; - int key, save_input_state = gp.program_state.input_state; + int key, save_input_state = program_state.input_state; long cnt = 0L, first = inkey ? (long) (inkey - '0') : 0L; boolean backspaced = FALSE, showzero = TRUE, /* should "Count: 123" go into message history? */ @@ -6338,12 +4829,15 @@ get_count( } else { /* if readchar() has already been called in this loop, it will have reset input_state; put that back to its previous value */ - gp.program_state.input_state = save_input_state; + program_state.input_state = save_input_state; key = readchar(); } if (digit(key)) { - cnt = 10L * cnt + (long) (key - '0'); + long dgt = (long) (key - '0'); + + /* cnt = (10 * cnt) + (key - '0'); */ + cnt = AppendLongDigit(cnt, dgt); if (cnt < 0L) cnt = 0L; else if (maxcount > 0L && cnt > maxcount) @@ -6387,25 +4881,35 @@ get_count( return key; } - -static char * +/* main command input routine when not repeating and not executing canned + commands; input comes via get_count() which collects repeat count if one + is present and returns next non-digit to us */ +staticfn int parse(void) { - register int foo; + int foo; iflags.in_parse = TRUE; gc.command_count = 0; - gc.context.move = TRUE; /* assume next command will take game time */ + svc.context.move = TRUE; /* assume next command will take game time */ flush_screen(1); /* Flush screen buffer. Put the cursor on the hero. */ /* affects readchar() behavior for ESC iff 'altmeta' option is On; - reset to 0 by readchar() */ - gp.program_state.input_state = commandInp; + is always reset to otherInp by readchar() */ + program_state.input_state = commandInp; + if (!gc.Cmd.num_pad || (foo = readchar()) == gc.Cmd.spkeys[NHKF_COUNT]) { + /* if 'num_pad' is On then readchar() has just reset input_state; + set it back to commandInp, so that get_count() supports 'altmeta'; + otherwise "nESC" becomes "nESC" (with + not read from keyboard yet) rather than intended count + and meta keystroke "nM-" */ + program_state.input_state = commandInp; + foo = get_count((char *) 0, '\0', LARGEST_INT, &gc.command_count, GC_NOFLAGS); - gl.last_command_count = gc.command_count; } + gl.last_command_count = gc.command_count; if (foo == gc.Cmd.spkeys[NHKF_ESC]) { /* esc cancels count (TH) */ clear_nhwindow(WIN_MESSAGE); @@ -6429,12 +4933,11 @@ parse(void) if (gm.multi) gm.multi--; - gc.command_line[0] = foo; - gc.command_line[1] = '\0'; + gc.cmd_key = foo; clear_nhwindow(WIN_MESSAGE); iflags.in_parse = FALSE; - return gc.command_line; + return gc.cmd_key; } #ifdef HANGUPHANDLING @@ -6447,20 +4950,20 @@ hangup( int sig_unused UNUSED) /* called as signal() handler, so sent * at least one arg */ { - if (gp.program_state.exiting) - gp.program_state.in_moveloop = 0; + if (program_state.exiting) + program_state.in_moveloop = 0; nhwindows_hangup(); #ifdef SAFERHANGUP - /* When using SAFERHANGUP, the done_hup flag it tested in rhack + /* When using SAFERHANGUP, the done_hup flag is tested in rhack and a couple of other places; actual hangup handling occurs then. This is 'safer' because it disallows certain cheats and also protects against losing objects in the process of being thrown, but also potentially riskier because the disconnected program must continue running longer before attempting a hangup save. */ - gp.program_state.done_hup++; + program_state.done_hup++; /* defer hangup iff game appears to be in progress */ - if (gp.program_state.in_moveloop - && gp.program_state.something_worth_saving) + if (program_state.in_moveloop + && program_state.something_worth_saving) return; #endif /* SAFERHANGUP */ end_of_input(); @@ -6471,16 +4974,16 @@ end_of_input(void) { #ifdef NOSAVEONHANGUP #ifdef INSURANCE - if (flags.ins_chkpt && gp.program_state.something_worth_saving) + if (flags.ins_chkpt && program_state.something_worth_saving) program_state.preserve_locks = 1; /* keep files for recovery */ #endif - gp.program_state.something_worth_saving = 0; /* don't save */ + program_state.something_worth_saving = 0; /* don't save */ #endif #ifndef SAFERHANGUP - if (!gp.program_state.done_hup++) + if (!program_state.done_hup++) #endif - if (gp.program_state.something_worth_saving) + if (program_state.something_worth_saving) (void) dosave0(); if (soundprocs.sound_exit_nhsound) (*soundprocs.sound_exit_nhsound)("end_of_input"); @@ -6493,10 +4996,10 @@ end_of_input(void) } #endif /* HANGUPHANDLING */ -static char +staticfn char readchar_core(coordxy *x, coordxy *y, int *mod) { - register int sym; + int sym; if (iflags.debug_fuzzer) { sym = randomkey(); @@ -6511,7 +5014,7 @@ readchar_core(coordxy *x, coordxy *y, int *mod) #ifdef NR_OF_EOFS if (sym == EOF) { - register int cnt = NR_OF_EOFS; + int cnt = NR_OF_EOFS; /* * Some SYSV systems seem to return EOFs for various reasons * (?like when one hits break or for interrupted systemcalls?), @@ -6531,7 +5034,7 @@ readchar_core(coordxy *x, coordxy *y, int *mod) sym = '\033'; #ifdef ALTMETA } else if (sym == '\033' && iflags.altmeta - && gp.program_state.input_state != otherInp) { + && program_state.input_state != otherInp) { /* iflags.altmeta: treat two character ``ESC c'' as single `M-c' but only when we're called by parse() [possibly via get_count()] or getpos() [to support Alt+digit] or getdir() [for arrow keys @@ -6551,7 +5054,7 @@ readchar_core(coordxy *x, coordxy *y, int *mod) readchar_done: /* next readchar() will be for an ordinary char unless parse() sets this back to non-zero */ - gp.program_state.input_state = otherInp; + program_state.input_state = otherInp; return (char) sym; } @@ -6573,13 +5076,13 @@ readchar_poskey(coordxy *x, coordxy *y, int *mod) { char ch; - gp.program_state.input_state = getposInp; + program_state.input_state = getposInp; ch = readchar_core(x, y, mod); return ch; } /* '_' command, #travel, via keyboard rather than mouse click */ -static int +staticfn int dotravel(void) { coord cc; @@ -6628,7 +5131,7 @@ dotravel(void) } /* #retravel, travel to iflags.travelcc, which must be set */ -static int +staticfn int dotravel_target(void) { if (!isok(iflags.travelcc.x, iflags.travelcc.y)) { @@ -6645,34 +5148,107 @@ dotravel_target(void) iflags.getloc_travelmode = FALSE; - gc.context.travel = 1; - gc.context.travel1 = 1; - gc.context.run = 8; - gc.context.nopick = 1; + svc.context.travel = 1; + svc.context.travel1 = 1; + svc.context.run = 8; + svc.context.nopick = 1; gd.domove_attempting |= DOMOVE_RUSH; if (!gm.multi) gm.multi = max(COLNO, ROWNO); u.last_str_turn = 0; - gc.context.mv = TRUE; + svc.context.mv = TRUE; domove(); return ECMD_TIME; } /* mouse click look command */ -static int +staticfn int doclicklook(void) { if (!isok(gc.clicklook_cc.x, gc.clicklook_cc.y)) return ECMD_OK; - gc.context.move = FALSE; + svc.context.move = FALSE; auto_describe(gc.clicklook_cc.x, gc.clicklook_cc.y); return ECMD_OK; } +/* can we use menu entries to respond to a query? */ +staticfn boolean +yn_menuable_resp(const char *resp) +{ + return iflags.query_menu && iflags.window_inited + && (resp == ynchars || resp == ynqchars || resp == ynaqchars + || resp == rightleftchars || resp == hidespinchars); +} + +staticfn void +yn_func_menu_opt(winid win, char key, const char *text, char def) +{ + anything any; + + any = cg.zeroany; + any.a_char = key; + add_menu(win, &nul_glyphinfo, &any, key, 0, + ATR_NONE, NO_COLOR, text, + (def == key) ? MENU_ITEMFLAGS_SELECTED + : MENU_ITEMFLAGS_NONE); + +} + +/* use a menu to ask a specific response to a query. + returns TRUE if the menu was shown to the user. + puts the response char into res. */ +staticfn boolean +yn_function_menu( + const char *query, + const char *resp, + char def, + char *res) +{ + if (yn_menuable_resp(resp)) { + winid win = create_nhwindow(NHW_MENU); + menu_item *sel; + int n; + char keybuf[QBUFSZ]; + + start_menu(win, MENU_BEHAVE_STANDARD); + if (resp == rightleftchars) { + yn_func_menu_opt(win, 'r', "Right", def); + yn_func_menu_opt(win, 'l', "Left", def); + } else if (resp == hidespinchars) { + yn_func_menu_opt(win, 'h', "Hide", def); + yn_func_menu_opt(win, 's', "Spin a web", def); + } else { + yn_func_menu_opt(win, 'y', "Yes", def); + yn_func_menu_opt(win, 'n', "No", def); + } + if (resp == ynaqchars) + yn_func_menu_opt(win, 'a', "All", def); + if (resp == ynqchars || resp == ynaqchars || resp == hidespinchars) + yn_func_menu_opt(win, 'q', "Quit", def); + end_menu(win, query); + n = select_menu(win, PICK_ONE, &sel); + destroy_nhwindow(win); + if (n > 0) { + *res = sel[0].item.a_char; + /* two were selected? use the one that wasn't the default */ + if (n > 1 && *res == def) + *res = sel[1].item.a_char; + free((genericptr_t) sel); + } else { + *res = def; + } + pline("%s %s", query, key2txt(*res, keybuf)); + clear_nhwindow(WIN_MESSAGE); + return TRUE; + } + return FALSE; +} + /* * Parameter validator for generic yes/no function to prevent * the core from sending too long a prompt string to the @@ -6687,7 +5263,7 @@ yn_function( { char res = '\033', qbuf[QBUFSZ]; struct _cmd_queue cq, *cmdq; -#if defined(DUMPLOG) || defined(DUMPHTML) +#ifdef DUMPLOG_CORE unsigned idx = gs.saved_pline_index; /* buffer to hold query+space+formatted_single_char_response */ char dumplog_buf[QBUFSZ + 1 + 15]; /* [QBUFSZ+1+7] should suffice */ @@ -6724,12 +5300,14 @@ yn_function( gp.pline_flags &= ~PLINE_SPEECH; } #endif - res = (*windowprocs.win_yn_function)(query, resp, def); + 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 defined(DUMPLOG) || defined(DUMPHTML) +#ifdef DUMPLOG_CORE if (idx == gs.saved_pline_index) { /* when idx is still the same as gs.saved_pline_index, the interface didn't put the prompt into gs.saved_plines[]; we put a simplified @@ -6739,24 +5317,45 @@ yn_function( dumplogmsg(dumplog_buf); } #endif + /* should not happen but cq.key has been observed to not obey 'resp'; + do this after dumplog has recorded the potentially bad value */ + 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)); + res = altres; + } /* in case we're called via getdir() which sets input_state */ - gp.program_state.input_state = otherInp; + program_state.input_state = otherInp; return res; } -/* for paranoid_confirm:quit,die,attack prompting */ -boolean -paranoid_query(boolean be_paranoid, const char *prompt) +/* for paranoid_confirm:quit,die,attack,&c prompting; allows yes, n|no, + or q|quit; result is one of 'y' or 'n' or 'q'; ESC yields 'q' */ +char +paranoid_ynq( + boolean be_paranoid, + const char *prompt, + boolean accept_q) { - boolean confirmed_ok; + char c = 'n'; /* default result */ /* when paranoid, player must respond with "yes" rather than just 'y' to give the go-ahead for this query; default is "no" unless the ParanoidConfirm flag is set in which case there's no default */ if (be_paranoid) { char pbuf[BUFSZ], qbuf[QBUFSZ], ans[BUFSZ]; - const char *promptprefix = "", - *responsetype = ParanoidConfirm ? "[yes|no]" : "[yes|n] (n)"; + const char *promptprefix = "", /* empty for first iteration */ + *responsetype = ParanoidConfirm ? (accept_q ? "[yes|no|quit]" + : "[yes|no]") + /* default of 'n' is shown for + * the !ParanoidConfirm cases */ + : (accept_q ? "[yes|n|q] (n)" + : "[yes|n] (n)"); int k, trylimit = 6; /* 1 normal, 5 more with "Yes or No:" prefix */ copynchars(pbuf, prompt, BUFSZ - 1); @@ -6773,24 +5372,43 @@ paranoid_query(boolean be_paranoid, const char *prompt) Strcpy(pbuf + (QBUFSZ - 1) - k - 4, "...?"); /* -4: "...?" */ } - Snprintf(qbuf, sizeof(qbuf), "%s%s %s", promptprefix, pbuf, + Snprintf(qbuf, sizeof qbuf, "%s%s %s", promptprefix, pbuf, responsetype); *ans = '\0'; getlin(qbuf, ans); (void) mungspaces(ans); - confirmed_ok = !strcmpi(ans, "yes"); - if (confirmed_ok || *ans == '\033') + if (!strcmpi(ans, "yes")) { + c = 'y'; break; + } + if (!strcmpi(ans, "quit") || *ans == '\033') { + c = 'q'; + break; + } + /* we don't bother adding "or \"Quit\"" for the accept_q case */ promptprefix = "\"Yes\" or \"No\": "; + /* for empty input, return value c will already be 'n' */ } while (ParanoidConfirm && strcmpi(ans, "no") && --trylimit); + } else if (accept_q) { + c = ynq(prompt); /* 'y', 'n', or 'q' */ } else { - confirmed_ok = (y_n(prompt) == 'y'); + c = y_n(prompt); /* 'y' or 'n' */ } - return confirmed_ok; + if (c != 'y' && (c != 'q' || !accept_q)) + c = 'n'; + return c; +} + +/* for paranoid_confirm:quit,die,attack,&c prompting; allows yes or n|no; + result is True for yes; n|no and ESC yield False */ +boolean +paranoid_query(boolean be_paranoid, const char *prompt) +{ + return (paranoid_ynq(be_paranoid, prompt, FALSE) == 'y'); } /* ^Z command, #suspend */ -static int +staticfn int dosuspend_core(void) { #ifdef SUSPEND @@ -6810,7 +5428,7 @@ dosuspend_core(void) } /* '!' command, #shell */ -static int +staticfn int dosh_core(void) { #ifdef SHELL @@ -6827,8 +5445,14 @@ dosh_core(void) return ECMD_OK; } +staticfn int +dummyfunction(void) +{ + return ECMD_CANCEL; +} + /* #holidays extended command, show current holidays */ -static int +staticfn int doholidays(void) { int holidays = current_holidays(); @@ -6882,7 +5506,7 @@ doholidays(void) } /* #shout extended command, shout a string to livelog/chronicle */ -static int +staticfn int doshout(void) { char input[BUFSZ], qbuf[QBUFSZ]; @@ -6919,7 +5543,7 @@ doshout(void) } else { You("raise your voice and shout: \"%s\"", buf); - wake_nearby(); + wake_nearby(TRUE); } /* The main reason for this command: putting this string into the livelog * and chronicle. */ diff --git a/src/coloratt.c b/src/coloratt.c new file mode 100644 index 0000000000..436e9274ef --- /dev/null +++ b/src/coloratt.c @@ -0,0 +1,1157 @@ +/* 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" + +struct color_names { + const char *name; + int color; +}; + +static const struct color_names colornames[] = { + { "black", CLR_BLACK }, + { "red", CLR_RED }, + { "green", CLR_GREEN }, + { "brown", CLR_BROWN }, + { "blue", CLR_BLUE }, + { "magenta", CLR_MAGENTA }, + { "cyan", CLR_CYAN }, + { "gray", CLR_GRAY }, + { "orange", CLR_ORANGE }, + { "light green", CLR_BRIGHT_GREEN }, + { "yellow", CLR_YELLOW }, + { "light blue", CLR_BRIGHT_BLUE }, + { "light magenta", CLR_BRIGHT_MAGENTA }, + { "light cyan", CLR_BRIGHT_CYAN }, + { "white", CLR_WHITE }, + { "no color", NO_COLOR }, + { (const char *) 0, CLR_BLACK }, /* everything after this is an alias */ + { "transparent", NO_COLOR }, + { "purple", CLR_MAGENTA }, + { "light purple", CLR_BRIGHT_MAGENTA }, + { "bright purple", CLR_BRIGHT_MAGENTA }, + { "grey", CLR_GRAY }, + { "bright red", CLR_ORANGE }, + { "bright green", CLR_BRIGHT_GREEN }, + { "bright blue", CLR_BRIGHT_BLUE }, + { "bright magenta", CLR_BRIGHT_MAGENTA }, + { "bright cyan", CLR_BRIGHT_CYAN } +}; + +struct attr_names { + const char *name; + int attr; +}; + +static const struct attr_names attrnames[] = { + { "none", ATR_NONE }, + { "bold", ATR_BOLD }, + { "dim", ATR_DIM }, + { "italic", ATR_ITALIC }, + { "underline", ATR_ULINE }, + { "blink", ATR_BLINK }, + { "inverse", ATR_INVERSE }, + { (const char *) 0, ATR_NONE }, /* everything after this is an alias */ + { "normal", ATR_NONE }, + { "uline", ATR_ULINE }, + { "reverse", ATR_INVERSE }, +}; + +/* { colortyp, tableindex, rgbindx, name, hexval, r, g, b }, */ + +static struct nethack_color colortable[] = { + { nh_color, 0, 0, "black", "", 0, 0, 0 }, + { nh_color, 1, 0, "red", "", 255, 0, 0 }, + { nh_color, 2, 0, "green", "", 34, 139, 34 }, + { nh_color, 3, 0, "brown", "", 165, 42, 42 }, + { nh_color, 4, 0, "blue", "", 0, 0, 255 }, + { nh_color, 5, 0, "magenta", "", 255, 0, 255 }, + { nh_color, 6, 0, "cyan", "", 0, 255, 255 }, + { nh_color, 7, 0, "gray", "", 128, 128, 128 }, + { no_color, 8, 0, "nocolor", "", 0, 0, 0 }, + { nh_color, 9, 0, "orange", "", 255, 165, 0 }, + { nh_color, 10, 0, "bright-green", "", 0, 128, 0 }, + { nh_color, 11, 0, "yellow", "", 255, 255, 0 }, + { nh_color, 12, 0, "bright-blue", "", 173, 216, 230 }, + { nh_color, 13, 0, "bright-magenta", "", 147, 112, 219 }, + { nh_color, 14, 0, "light-cyan", "", 224, 255, 255 }, + { nh_color, 15, 0, "white", "", 255, 255, 255 }, + { rgb_color, 16, 0, "maroon", "#800000", 128, 0, 0 }, + { rgb_color, 17, 1, "dark-red", "#8B0000", 139, 0, 0 }, + { rgb_color, 18, 2, "brown", "#A52A2A", 165, 42, 42 }, + { rgb_color, 19, 3, "firebrick", "#B22222", 178, 34, 34 }, + { rgb_color, 20, 4, "crimson", "#DC143C", 220, 20, 60 }, + { rgb_color, 21, 5, "red", "#FF0000", 255, 0, 0 }, + { rgb_color, 22, 6, "tomato", "#FF6347", 255, 99, 71 }, + { rgb_color, 23, 7, "coral", "#FF7F50", 255, 127, 80 }, + { rgb_color, 24, 8, "indian-red", "#CD5C5C", 205, 92, 92 }, + { rgb_color, 25, 9, "light-coral", "#F08080", 240, 128, 128 }, + { rgb_color, 26, 10, "dark-salmon", "#E9967A", 233, 150, 122 }, + { rgb_color, 27, 11, "salmon", "#FA8072", 250, 128, 114 }, + { rgb_color, 28, 12, "light-salmon", "#FFA07A", 255, 160, 122 }, + { rgb_color, 29, 13, "orange-red", "#FF4500", 255, 69, 0 }, + { rgb_color, 30, 14, "dark-orange", "#FF8C00", 255, 140, 0 }, + { rgb_color, 31, 15, "orange", "#FFA500", 255, 165, 0 }, + { rgb_color, 32, 16, "gold", "#FFD700", 255, 215, 0 }, + { rgb_color, 33, 17, "dark-golden-rod", "#B8860B", 184, 134, 11 }, + { rgb_color, 34, 18, "golden-rod", "#DAA520", 218, 165, 32 }, + { rgb_color, 35, 19, "pale-golden-rod", "#EEE8AA", 238, 232, 170 }, + { rgb_color, 36, 20, "dark-khaki", "#BDB76B", 189, 183, 107 }, + { rgb_color, 37, 21, "khaki", "#F0E68C", 240, 230, 140 }, + { rgb_color, 38, 22, "olive", "#808000", 128, 128, 0 }, + { rgb_color, 39, 23, "yellow", "#FFFF00", 255, 255, 0 }, + { rgb_color, 40, 24, "yellow-green", "#9ACD32", 154, 205, 50 }, + { rgb_color, 41, 25, "dark-olive-green", "#556B2F", 85, 107, 47 }, + { rgb_color, 42, 26, "olive-drab", "#6B8E23", 107, 142, 35 }, + { rgb_color, 43, 27, "lawn-green", "#7CFC00", 124, 252, 0 }, + { rgb_color, 44, 28, "chart-reuse", "#7FFF00", 127, 255, 0 }, + { rgb_color, 45, 29, "green-yellow", "#ADFF2F", 173, 255, 47 }, + { rgb_color, 46, 30, "dark-green", "#006400", 0, 100, 0 }, + { rgb_color, 47, 31, "green", "#008000", 0, 128, 0 }, + { rgb_color, 48, 32, "forest-green", "#228B22", 34, 139, 34 }, + { rgb_color, 49, 33, "lime", "#00FF00", 0, 255, 0 }, + { rgb_color, 50, 34, "lime-green", "#32CD32", 50, 205, 50 }, + { rgb_color, 51, 35, "light-green", "#90EE90", 144, 238, 144 }, + { rgb_color, 52, 36, "pale-green", "#98FB98", 152, 251, 152 }, + { rgb_color, 53, 37, "dark-sea-green", "#8FBC8F", 143, 188, 143 }, + { rgb_color, 54, 38, "medium-spring-green", "#00FA9A", 0, 250, 154 }, + { rgb_color, 55, 39, "spring-green", "#00FF7F", 0, 255, 127 }, + { rgb_color, 56, 40, "sea-green", "#2E8B57", 46, 139, 87 }, + { rgb_color, 57, 41, "medium-aqua-marine", "#66CDAA", 102, 205, 170 }, + { rgb_color, 58, 42, "medium-sea-green", "#3CB371", 60, 179, 113 }, + { rgb_color, 59, 43, "light-sea-green", "#20B2AA", 32, 178, 170 }, + { rgb_color, 60, 44, "dark-slate-gray", "#2F4F4F", 47, 79, 79 }, + { rgb_color, 61, 45, "teal", "#008080", 0, 128, 128 }, + { rgb_color, 62, 46, "dark-cyan", "#008B8B", 0, 139, 139 }, + { rgb_color, 63, 47, "aqua", "#00FFFF", 0, 255, 255 }, + { rgb_color, 64, 48, "cyan", "#00FFFF", 0, 255, 255 }, + { rgb_color, 65, 49, "light-cyan", "#E0FFFF", 224, 255, 255 }, + { rgb_color, 66, 50, "dark-turquoise", "#00CED1", 0, 206, 209 }, + { rgb_color, 67, 51, "turquoise", "#40E0D0", 64, 224, 208 }, + { rgb_color, 68, 52, "medium-turquoise", "#48D1CC", 72, 209, 204 }, + { rgb_color, 69, 53, "pale-turquoise", "#AFEEEE", 175, 238, 238 }, + { rgb_color, 70, 54, "aqua-marine", "#7FFFD4", 127, 255, 212 }, + { rgb_color, 71, 55, "powder-blue", "#B0E0E6", 176, 224, 230 }, + { rgb_color, 72, 56, "cadet-blue", "#5F9EA0", 95, 158, 160 }, + { rgb_color, 73, 57, "steel-blue", "#4682B4", 70, 130, 180 }, + { rgb_color, 74, 58, "corn-flower-blue", "#6495ED", 100, 149, 237 }, + { rgb_color, 75, 59, "deep-sky-blue", "#00BFFF", 0, 191, 255 }, + { rgb_color, 76, 60, "dodger-blue", "#1E90FF", 30, 144, 255 }, + { rgb_color, 77, 61, "light-blue", "#ADD8E6", 173, 216, 230 }, + { rgb_color, 78, 62, "sky-blue", "#87CEEB", 135, 206, 235 }, + { rgb_color, 79, 63, "light-sky-blue", "#87CEFA", 135, 206, 250 }, + { rgb_color, 80, 64, "midnight-blue", "#191970", 25, 25, 112 }, + { rgb_color, 81, 65, "navy", "#000080", 0, 0, 128 }, + { rgb_color, 82, 66, "dark-blue", "#00008B", 0, 0, 139 }, + { rgb_color, 83, 67, "medium-blue", "#0000CD", 0, 0, 205 }, + { rgb_color, 84, 68, "blue", "#0000FF", 0, 0, 255 }, + { rgb_color, 85, 69, "royal-blue", "#4169E1", 65, 105, 225 }, + { rgb_color, 86, 70, "blue-violet", "#8A2BE2", 138, 43, 226 }, + { rgb_color, 87, 71, "indigo", "#4B0082", 75, 0, 130 }, + { rgb_color, 88, 72, "dark-slate-blue", "#483D8B", 72, 61, 139 }, + { rgb_color, 89, 73, "slate-blue", "#6A5ACD", 106, 90, 205 }, + { rgb_color, 90, 74, "medium-slate-blue", "#7B68EE", 123, 104, 238 }, + { rgb_color, 91, 75, "medium-purple", "#9370DB", 147, 112, 219 }, + { rgb_color, 92, 76, "dark-magenta", "#8B008B", 139, 0, 139 }, + { rgb_color, 93, 77, "dark-violet", "#9400D3", 148, 0, 211 }, + { rgb_color, 94, 78, "dark-orchid", "#9932CC", 153, 50, 204 }, + { rgb_color, 95, 79, "medium-orchid", "#BA55D3", 186, 85, 211 }, + { rgb_color, 96, 80, "purple", "#800080", 128, 0, 128 }, + { rgb_color, 97, 81, "thistle", "#D8BFD8", 216, 191, 216 }, + { rgb_color, 98, 82, "plum", "#DDA0DD", 221, 160, 221 }, + { rgb_color, 99, 83, "violet", "#EE82EE", 238, 130, 238 }, + { rgb_color, 100, 84, "magenta", "#FF00FF", 255, 0, 255 }, + { rgb_color, 101, 85, "orchid", "#DA70D6", 218, 112, 214 }, + { rgb_color, 102, 86, "medium-violet-red", "#C71585", 199, 21, 133 }, + { rgb_color, 103, 87, "pale-violet-red", "#DB7093", 219, 112, 147 }, + { rgb_color, 104, 88, "deep-pink", "#FF1493", 255, 20, 147 }, + { rgb_color, 105, 89, "hot-pink", "#FF69B4", 255, 105, 180 }, + { rgb_color, 106, 90, "light-pink", "#FFB6C1", 255, 182, 193 }, + { rgb_color, 107, 91, "pink", "#FFC0CB", 255, 192, 203 }, + { rgb_color, 108, 92, "antique-white", "#FAEBD7", 250, 235, 215 }, + { rgb_color, 109, 93, "beige", "#F5F5DC", 245, 245, 220 }, + { rgb_color, 110, 94, "bisque", "#FFE4C4", 255, 228, 196 }, + { rgb_color, 111, 95, "blanched-almond", "#FFEBCD", 255, 235, 205 }, + { rgb_color, 112, 96, "wheat", "#F5DEB3", 245, 222, 179 }, + { rgb_color, 113, 97, "corn-silk", "#FFF8DC", 255, 248, 220 }, + { rgb_color, 114, 98, "lemon-chiffon", "#FFFACD", 255, 250, 205 }, + { rgb_color, 115, 99, "light-golden-rod-yellow", "#FAFAD2", 250, 250, 210 }, + { rgb_color, 116, 100, "light-yellow", "#FFFFE0", 255, 255, 224 }, + { rgb_color, 117, 101, "saddle-brown", "#8B4513", 139, 69, 19 }, + { rgb_color, 118, 102, "sienna", "#A0522D", 160, 82, 45 }, + { rgb_color, 119, 103, "chocolate", "#D2691E", 210, 105, 30 }, + { rgb_color, 120, 104, "peru", "#CD853F", 205, 133, 63 }, + { rgb_color, 121, 105, "sandy-brown", "#F4A460", 244, 164, 96 }, + { rgb_color, 122, 106, "burly-wood", "#DEB887", 222, 184, 135 }, + { rgb_color, 123, 107, "tan", "#D2B48C", 210, 180, 140 }, + { rgb_color, 124, 108, "rosy-brown", "#BC8F8F", 188, 143, 143 }, + { rgb_color, 125, 109, "moccasin", "#FFE4B5", 255, 228, 181 }, + { rgb_color, 126, 110, "navajo-white", "#FFDEAD", 255, 222, 173 }, + { rgb_color, 127, 111, "peach-puff", "#FFDAB9", 255, 218, 185 }, + { rgb_color, 128, 112, "misty-rose", "#FFE4E1", 255, 228, 225 }, + { rgb_color, 129, 113, "lavender-blush", "#FFF0F5", 255, 240, 245 }, + { rgb_color, 130, 114, "linen", "#FAF0E6", 250, 240, 230 }, + { rgb_color, 131, 115, "old-lace", "#FDF5E6", 253, 245, 230 }, + { rgb_color, 132, 116, "papaya-whip", "#FFEFD5", 255, 239, 213 }, + { rgb_color, 133, 117, "sea-shell", "#FFF5EE", 255, 245, 238 }, + { rgb_color, 134, 118, "mint-cream", "#F5FFFA", 245, 255, 250 }, + { rgb_color, 135, 119, "slate-gray", "#708090", 112, 128, 144 }, + { rgb_color, 136, 120, "light-slate-gray", "#778899", 119, 136, 153 }, + { rgb_color, 137, 121, "light-steel-blue", "#B0C4DE", 176, 196, 222 }, + { rgb_color, 138, 122, "lavender", "#E6E6FA", 230, 230, 250 }, + { rgb_color, 139, 123, "floral-white", "#FFFAF0", 255, 250, 240 }, + { rgb_color, 140, 124, "alice-blue", "#F0F8FF", 240, 248, 255 }, + { rgb_color, 141, 125, "ghost-white", "#F8F8FF", 248, 248, 255 }, + { rgb_color, 142, 126, "honeydew", "#F0FFF0", 240, 255, 240 }, + { rgb_color, 143, 127, "ivory", "#FFFFF0", 255, 255, 240 }, + { rgb_color, 144, 128, "azure", "#F0FFFF", 240, 255, 255 }, + { rgb_color, 145, 129, "snow", "#FFFAFA", 255, 250, 250 }, + { rgb_color, 146, 130, "black", "#000000", 0, 0, 0 }, + { rgb_color, 147, 131, "dim-gray", "#696969", 105, 105, 105 }, + { rgb_color, 148, 132, "gray", "#808080", 128, 128, 128 }, + { rgb_color, 149, 133, "dark-gray", "#A9A9A9", 169, 169, 169 }, + { rgb_color, 150, 134, "silver", "#C0C0C0", 192, 192, 192 }, + { rgb_color, 151, 135, "light-gray", "#D3D3D3", 211, 211, 211 }, + { rgb_color, 152, 136, "gainsboro", "#DCDCDC", 220, 220, 220 }, + { rgb_color, 153, 137, "white-smoke", "#F5F5F5", 245, 245, 245 }, + { rgb_color, 154, 138, "white", "#FFFFFF", 255, 255, 255 }, +}; + +#ifdef CHANGE_COLOR +staticfn int32 alt_color_spec(const char *cp); +#endif + +int32 +colortable_to_int32(struct nethack_color *cte) +{ + int32 clr = NO_COLOR | NH_BASIC_COLOR; + + if (cte->colortyp == rgb_color) + clr = (cte->r << 16) | (cte->g << 8) | cte->b; + else if (cte->colortyp == nh_color) + clr = cte->tableindex | NH_BASIC_COLOR; + return clr; +} + +char * +color_attr_to_str(color_attr *ca) +{ + static char buf[BUFSZ]; + + Sprintf(buf, "%s&%s", + clr2colorname(ca->color), + attr2attrname(ca->attr)); + return buf; +} + +/* parse string like "color&attr" into color_attr */ +boolean +color_attr_parse_str(color_attr *ca, char *str) +{ + char buf[BUFSZ]; + char *amp = NULL; + int tmp, c = NO_COLOR, a = ATR_NONE; + + (void) strncpy(buf, str, sizeof buf - 1); + buf[sizeof buf - 1] = '\0'; + + if ((amp = strchr(buf, '&')) != 0) + *amp = '\0'; + + if (amp) { + amp++; + c = match_str2clr(buf, FALSE); + a = match_str2attr(amp, TRUE); + /* FIXME: match_str2clr & match_str2attr give config_error_add(), + so this is useless */ + if (c >= CLR_MAX && a == -1) { + /* try other way around */ + c = match_str2clr(amp, FALSE); + a = match_str2attr(buf, TRUE); + } + if (c >= CLR_MAX || a == -1) + return FALSE; + } else { + /* one param only */ + tmp = match_str2attr(buf, FALSE); + if (tmp == -1) { + tmp = match_str2clr(buf, FALSE); + if (tmp >= CLR_MAX) + return FALSE; + c = tmp; + } else { + a = tmp; + } + } + ca->attr = a; + ca->color = c; + return TRUE; +} + +boolean +query_color_attr(color_attr *ca, const char *prompt) +{ + int c, a; + + c = query_color(prompt, ca->color); + if (c == -1) + return FALSE; + a = query_attr(prompt, ca->attr); + if (a == -1) + return FALSE; + ca->color = c; + ca->attr = a; + return TRUE; +} + +const char * +attr2attrname(int attr) +{ + int i; + + for (i = 0; i < SIZE(attrnames); i++) + if (attrnames[i].attr == attr) + return attrnames[i].name; + return (char *) 0; +} + +/* + * Color support functions and data for "color" + * + * Used by: optfn_() + * + */ + +const char * +clr2colorname(int clr) +{ + int i; + + for (i = 0; i < SIZE(colornames); i++) + if (colornames[i].name && colornames[i].color == clr) + return colornames[i].name; + return (char *) 0; +} + +int +match_str2clr(char *str, boolean suppress_msg) +{ + int i, c = CLR_MAX; + + /* allow "lightblue", "light blue", and "light-blue" to match "light blue" + (also junk like "_l i-gh_t---b l u e" but we won't worry about that); + also copes with trailing space; caller has removed any leading space */ + for (i = 0; i < SIZE(colornames); i++) + if (colornames[i].name + && fuzzymatch(str, colornames[i].name, " -_", TRUE)) { + c = colornames[i].color; + break; + } + if (i == SIZE(colornames) && digit(*str)) + c = atoi(str); + + if (c < 0 || c >= CLR_MAX) { + if (!suppress_msg) + config_error_add("Unknown color '%.60s'", str); + c = CLR_MAX; /* "none of the above" */ + } + return c; +} + +int +match_str2attr(const char *str, boolean complain) +{ + int i, a = -1; + + for (i = 0; i < SIZE(attrnames); i++) + if (attrnames[i].name + && fuzzymatch(str, attrnames[i].name, " -_", TRUE)) { + a = attrnames[i].attr; + break; + } + + if (a == -1 && complain) + config_error_add("Unknown text attribute '%.50s'", str); + + return a; +} + +/* ask about highlighting attribute; for menu headers and menu + coloring patterns, only one attribute at a time is allowed; + for status highlighting, multiple attributes are allowed [overkill; + life would be much simpler if that were restricted to one also...] */ +int +query_attr(const char *prompt, int dflt_attr) +{ + winid tmpwin; + anything any; + int i, pick_cnt; + menu_item *picks = (menu_item *) 0; + boolean allow_many = (prompt && !strncmpi(prompt, "Choose", 6)); + int clr = NO_COLOR; + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin, MENU_BEHAVE_STANDARD); + any = cg.zeroany; + for (i = 0; i < SIZE(attrnames); i++) { + if (!attrnames[i].name) + break; + any.a_int = i + 1; + add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, + attrnames[i].attr, clr, attrnames[i].name, + (attrnames[i].attr == dflt_attr) ? MENU_ITEMFLAGS_SELECTED + : MENU_ITEMFLAGS_NONE); + } + end_menu(tmpwin, (prompt && *prompt) ? prompt : "Pick an attribute"); + pick_cnt = select_menu(tmpwin, allow_many ? PICK_ANY : PICK_ONE, &picks); + destroy_nhwindow(tmpwin); + if (pick_cnt > 0) { + int j, k = 0; + + if (allow_many) { + /* PICK_ANY, with one preselected entry (ATR_NONE) which + should be excluded if any other choices were picked */ + for (i = 0; i < pick_cnt; ++i) { + j = picks[i].item.a_int - 1; + if (attrnames[j].attr != ATR_NONE || pick_cnt == 1) { + switch (attrnames[j].attr) { + case ATR_NONE: + k = HL_NONE; + break; + case ATR_BOLD: + k |= HL_BOLD; + break; + case ATR_DIM: + k |= HL_DIM; + break; + case ATR_ITALIC: + k |= HL_ITALIC; + break; + case ATR_ULINE: + k |= HL_ULINE; + break; + case ATR_BLINK: + k |= HL_BLINK; + break; + case ATR_INVERSE: + k |= HL_INVERSE; + break; + } + } + } + } else { + /* PICK_ONE, but might get 0 or 2 due to preselected entry */ + j = picks[0].item.a_int - 1; + /* pick_cnt==2: explicitly picked something other than the + preselected entry */ + if (pick_cnt == 2 && attrnames[j].attr == dflt_attr) + j = picks[1].item.a_int - 1; + k = attrnames[j].attr; + } + free((genericptr_t) picks); + return k; + } else if (pick_cnt == 0 && !allow_many) { + /* PICK_ONE, preselected entry explicitly chosen */ + return dflt_attr; + } + /* either ESC to explicitly cancel (pick_cnt==-1) or + PICK_ANY with preselected entry toggled off and nothing chosen */ + return -1; +} + +int +query_color(const char *prompt, int dflt_color) +{ + winid tmpwin; + anything any; + int i, pick_cnt; + menu_item *picks = (menu_item *) 0; + + /* replace user patterns with color name ones and force 'menucolors' On */ + basic_menu_colors(TRUE); + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin, MENU_BEHAVE_STANDARD); + any = cg.zeroany; + for (i = 0; i < SIZE(colornames); i++) { + if (!colornames[i].name) + break; + any.a_int = i + 1; + add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, + ATR_NONE, NO_COLOR, colornames[i].name, + (colornames[i].color == dflt_color) ? MENU_ITEMFLAGS_SELECTED + : MENU_ITEMFLAGS_NONE); + } + end_menu(tmpwin, (prompt && *prompt) ? prompt : "Pick a color"); + pick_cnt = select_menu(tmpwin, PICK_ONE, &picks); + destroy_nhwindow(tmpwin); + + /* remove temporary color name patterns and restore user-specified ones; + reset 'menucolors' option to its previous value */ + basic_menu_colors(FALSE); + + if (pick_cnt > 0) { + i = colornames[picks[0].item.a_int - 1].color; + /* pick_cnt==2: explicitly picked something other than the + preselected entry */ + if (pick_cnt == 2 && i == NO_COLOR) + i = colornames[picks[1].item.a_int - 1].color; + free((genericptr_t) picks); + return i; + } else if (pick_cnt == 0) { + /* pick_cnt==0: explicitly picking preselected entry toggled it off */ + return dflt_color; + } + return -1; +} + +DISABLE_WARNING_FORMAT_NONLITERAL + +extern const char regex_id[]; /* from sys/share/regex.{c,cpp} */ + +/* set up a menu for picking a color, one that shows each name in its color; + overrides player's MENUCOLORS with a set of "blue"=blue, "red"=red, and + so forth; suppresses color for black and white because one of those will + likely be invisible due to matching the background; the alternate set of + MENUCOLORS is kept around for potential re-use */ +void +basic_menu_colors( + boolean load_colors) /* True: temporarily replace menu color entries with + * a fake set of menu colors which match their names; + * False: restore user-specified colorings */ +{ + if (load_colors) { + /* replace normal menu colors with a set specifically for colors */ + gs.save_menucolors = iflags.use_menu_color; + gs.save_colorings = gm.menu_colorings; + + iflags.use_menu_color = TRUE; + if (gc.color_colorings) { + /* use the alternate colorings which were set up previously */ + gm.menu_colorings = gc.color_colorings; + } else { + /* create the alternate colorings once */ + char cnm[QBUFSZ]; + int i, c; + boolean pmatchregex = !strcmpi(regex_id, "pmatchregex"); + const char *patternfmt = pmatchregex ? "*%s" : "%s"; + + /* menu_colorings pointer has been saved; clear it in order + to add the alternate entries as if from scratch */ + gm.menu_colorings = (struct menucoloring *) 0; + + /* this orders the patterns last-in/first-out; that means + that the "light " variations come before the basic + "" ones, which is exactly what we want (so that the + shorter basic names won't get false matches as substrings + of the longer ones) */ + for (i = 0; i < SIZE(colornames); ++i) { + if (!colornames[i].name) /* first alias entry has no name */ + break; + c = colornames[i].color; + if (c == CLR_BLACK || c == CLR_WHITE || c == NO_COLOR) + continue; /* skip these */ + Sprintf(cnm, patternfmt, colornames[i].name); + add_menu_coloring_parsed(cnm, c, ATR_NONE); + } + + /* right now, menu_colorings contains the alternate color list; + remember that list for future pick-a-color instances and + also keep it as is for this instance */ + gc.color_colorings = gm.menu_colorings; + } + } else { + /* restore normal user-specified menu colors */ + iflags.use_menu_color = gs.save_menucolors; + gm.menu_colorings = gs.save_colorings; + } +} + +RESTORE_WARNING_FORMAT_NONLITERAL + +boolean +add_menu_coloring_parsed(const char *str, int c, int a) +{ + static const char re_error[] = "Menucolor regex error"; + struct menucoloring *tmp; + + if (!str) + return FALSE; + tmp = (struct menucoloring *) alloc(sizeof *tmp); + tmp->match = regex_init(); + /* test_regex_pattern() has already validated this regexp but parsing + it again could conceivably run out of memory */ + if (!regex_compile(str, tmp->match)) { + char errbuf[BUFSZ]; + char *re_error_desc = regex_error_desc(tmp->match, errbuf); + + /* free first in case reason for regcomp failure was out-of-memory */ + regex_free(tmp->match); + free((genericptr_t) tmp); + config_error_add("%s: %s", re_error, re_error_desc); + return FALSE; + } + tmp->next = gm.menu_colorings; + tmp->origstr = dupstr(str); + tmp->color = c; + tmp->attr = a; + gm.menu_colorings = tmp; + iflags.use_menu_color = TRUE; + return TRUE; +} + +/* parse '"regex_string"=color&attr' and add it to menucoloring */ +boolean +add_menu_coloring(char *tmpstr) /* never Null but could be empty */ +{ + int c = NO_COLOR, a = ATR_NONE; + char *tmps, *cs, *amp; + char str[BUFSZ]; + + (void) strncpy(str, tmpstr, sizeof str - 1); + str[sizeof str - 1] = '\0'; + + if ((cs = strchr(str, '=')) == 0) { + config_error_add("Malformed MENUCOLOR"); + return FALSE; + } + + tmps = cs + 1; /* advance past '=' */ + mungspaces(tmps); + if ((amp = strchr(tmps, '&')) != 0) + *amp = '\0'; + + c = match_str2clr(tmps, FALSE); + if (c >= CLR_MAX) + return FALSE; + + if (amp) { + tmps = amp + 1; /* advance past '&' */ + a = match_str2attr(tmps, TRUE); + if (a == -1) + return FALSE; + } + + /* the regexp portion here has not been condensed by mungspaces() */ + *cs = '\0'; + tmps = str; + if (*tmps == '"' || *tmps == '\'') { + cs--; + while (isspace((uchar) *cs)) + cs--; + if (*cs == *tmps) { + *cs = '\0'; + tmps++; + } + } + return add_menu_coloring_parsed(tmps, c, a); +} + +/* release all menu color patterns */ +void +free_menu_coloring(void) +{ + /* either menu_colorings or color_colorings or both might need to + be freed or already be Null; do-loop will iterate at most twice */ + do { + struct menucoloring *tmp, *tmp2; + + for (tmp = gm.menu_colorings; tmp; tmp = tmp2) { + tmp2 = tmp->next; + regex_free(tmp->match); + free((genericptr_t) tmp->origstr); + free((genericptr_t) tmp); + } + gm.menu_colorings = gc.color_colorings; + gc.color_colorings = (struct menucoloring *) 0; + } while (gm.menu_colorings); +} + +/* release a specific menu color pattern; not used for color_colorings */ +void +free_one_menu_coloring(int idx) /* 0 .. */ +{ + struct menucoloring *tmp = gm.menu_colorings; + struct menucoloring *prev = NULL; + + while (tmp) { + if (idx == 0) { + struct menucoloring *next = tmp->next; + + regex_free(tmp->match); + free((genericptr_t) tmp->origstr); + free((genericptr_t) tmp); + if (prev) + prev->next = next; + else + gm.menu_colorings = next; + return; + } + idx--; + prev = tmp; + tmp = tmp->next; + } +} + +int +count_menucolors(void) +{ + struct menucoloring *tmp; + int count = 0; + + for (tmp = gm.menu_colorings; tmp; tmp = tmp->next) + count++; + return count; +} + +/* returns -1 on no-match. + * buf is NONNULLARG1 + */ +int32 +check_enhanced_colors(char *buf) +{ + char xtra = '\0'; /* used to catch trailing junk after "#rrggbb" */ + unsigned r, g, b; + int32 retcolor = -1, color; + + if ((color = match_str2clr(buf, TRUE)) != CLR_MAX) { + retcolor = color | NH_BASIC_COLOR; + } else if (sscanf(buf, "#%02x%02x%02x%c", &r, &g, &b, &xtra) >= 3) { + retcolor = !xtra ? (int32) ((r << 16) | (g << 8) | b) : -1; + } else { + /* altbuf: allow user's "grey" to match colortable[]'s "gray"; + * fuzzymatch(): ignore spaces, hyphens, and underscores so that + * space or underscore in user-supplied name will match hyphen + * [note: caller splits text at spaces so we won't see any here] + */ + char *altbuf = NULL, *grey = strstri(buf, "grey"); + ptrdiff_t greyoffset = grey ? (grey - buf) : -1; + + if (greyoffset >= 0) { + altbuf = dupstr(buf); + /* use direct copy because strsubst() is case-sensitive */ + /*(void) strncpy(&altbuf[greyoffset], "gray", 4);*/ + (void) memcpy(altbuf + greyoffset, "gray", 4); + } + for (color = 0; color < SIZE(colortable); ++color) { + if (fuzzymatch(buf, colortable[color].name, " -_", TRUE) + || (altbuf && fuzzymatch(altbuf, colortable[color].name, + " -_", TRUE))) { + retcolor = colortable_to_int32(&colortable[color]); + break; + } + } + if (altbuf) + free(altbuf); + } + return retcolor; +} + +/* return the canonical name of a particular color */ +const char * +wc_color_name(int32 colorindx) +{ + static char hexcolor[sizeof "#rrggbb"]; /* includes room for '\0' */ + const char *result = "no-color"; + + if (colorindx >= 0) { + int32 basicindx = colorindx & ~NH_BASIC_COLOR; + + /* if colorindx has NH_BASIC_COLOR bit set, basicindx won't, + so differing implies a basic color */ + if (basicindx != colorindx) { + assert(basicindx < 16); + result = colortable[basicindx].name; + } else { + int indx; + long r = (colorindx >> 16) & 0x0000ff, /* shift rrXXXX to rr */ + g = (colorindx >> 8) & 0x0000ff, /* shift XXggXX to gg */ + b = colorindx & 0x0000ff; /* mask XXXXbb to bb */ + + Snprintf(hexcolor, sizeof hexcolor, "#%02x%02x%02x", + (uint8) r, (uint8) g, (uint8) b); + result = hexcolor; + /* override hex value if this is a named color */ + for (indx = 16; indx < SIZE(colortable); ++indx) + if (colortable[indx].r == r + && colortable[indx].g == g + && colortable[indx].b == b) { + result = colortable[indx].name; + break; + } + } + } + return result; +} + +/* hexdd[] is defined in decl.c */ +boolean +onlyhexdigits(const char *buf) +{ + const char *dp = buf; + + for (dp = buf; *dp; ++dp) { + if (!(strchr(hexdd, *dp) || *dp == '-')) + return FALSE; + } + return TRUE; +} + +int32_t +rgbstr_to_int32(const char *rgbstr) +{ + int r, g, b, milestone = 0; + char *cp, *c_r, *c_g, *c_b; + int32_t rgb = 0; + char buf[BUFSZ]; + boolean dash = FALSE; + + + Snprintf(buf, sizeof buf, "%s", + rgbstr ? rgbstr : ""); + + if (*buf && onlyhexdigits(buf)) { + c_g = c_b = (char *) 0; + c_r = cp = buf; + while (*cp) { + if (digit(*cp) || *cp == '-') { + if (*cp == '-') { + *cp = '\0'; + milestone++; + dash = TRUE; + } + cp++; + if (dash) { + if (milestone < 2) + c_g = cp; + else + c_b = cp; + dash = FALSE; + } + } else { + return -1L; + } + } + /* sanity checks */ + if (c_r && c_g && c_b + && (strlen(c_r) > 0 && strlen(c_r) < 4) + && (strlen(c_g) > 0 && strlen(c_g) < 4) + && (strlen(c_b) > 0 && strlen(c_b) < 4)) { + r = atoi(c_r); + g = atoi(c_g); + b = atoi(c_b); + rgb = (r << 16) | (g << 8) | (b << 0); + return rgb; + } + } else if (*buf) { + /* perhaps an enhanced color name was used instead of rgb value? */ + if ((rgb = check_enhanced_colors(buf)) != -1) { + return rgb; + } + } + return -1; +} + +int +set_map_customcolor(glyph_map *gmap, uint32 nhcolor) +{ + glyph_map *tmpgm = gmap; + uint32 closecolor = 0; + uint16 clridx = 0; + + if (!tmpgm) + return 0; + + gmap->customcolor = nhcolor; + if (closest_color(nhcolor, &closecolor, &clridx)) + gmap->color256idx = clridx; + else + gmap->color256idx = 0; + return 1; +} + +static struct { + int index; + uint32 value; +} color_256_definitions[] = { + /* color values are from unnethack */ + { 16, 0x000000 }, { 17, 0x00005f }, { 18, 0x000087 }, + { 19, 0x0000af }, { 20, 0x0000d7 }, { 21, 0x0000ff }, + { 22, 0x005f00 }, { 23, 0x005f5f }, { 24, 0x005f87 }, + { 25, 0x005faf }, { 26, 0x005fd7 }, { 27, 0x005fff }, + { 28, 0x008700 }, { 29, 0x00875f }, { 30, 0x008787 }, + { 31, 0x0087af }, { 32, 0x0087d7 }, { 33, 0x0087ff }, + { 34, 0x00af00 }, { 35, 0x00af5f }, { 36, 0x00af87 }, + { 37, 0x00afaf }, { 38, 0x00afd7 }, { 39, 0x00afff }, + { 40, 0x00d700 }, { 41, 0x00d75f }, { 42, 0x00d787 }, + { 43, 0x00d7af }, { 44, 0x00d7d7 }, { 45, 0x00d7ff }, + { 46, 0x00ff00 }, { 47, 0x00ff5f }, { 48, 0x00ff87 }, + { 49, 0x00ffaf }, { 50, 0x00ffd7 }, { 51, 0x00ffff }, + { 52, 0x5f0000 }, { 53, 0x5f005f }, { 54, 0x5f0087 }, + { 55, 0x5f00af }, { 56, 0x5f00d7 }, { 57, 0x5f00ff }, + { 58, 0x5f5f00 }, { 59, 0x5f5f5f }, { 60, 0x5f5f87 }, + { 61, 0x5f5faf }, { 62, 0x5f5fd7 }, { 63, 0x5f5fff }, + { 64, 0x5f8700 }, { 65, 0x5f875f }, { 66, 0x5f8787 }, + { 67, 0x5f87af }, { 68, 0x5f87d7 }, { 69, 0x5f87ff }, + { 70, 0x5faf00 }, { 71, 0x5faf5f }, { 72, 0x5faf87 }, + { 73, 0x5fafaf }, { 74, 0x5fafd7 }, { 75, 0x5fafff }, + { 76, 0x5fd700 }, { 77, 0x5fd75f }, { 78, 0x5fd787 }, + { 79, 0x5fd7af }, { 80, 0x5fd7d7 }, { 81, 0x5fd7ff }, + { 82, 0x5fff00 }, { 83, 0x5fff5f }, { 84, 0x5fff87 }, + { 85, 0x5fffaf }, { 86, 0x5fffd7 }, { 87, 0x5fffff }, + { 88, 0x870000 }, { 89, 0x87005f }, { 90, 0x870087 }, + { 91, 0x8700af }, { 92, 0x8700d7 }, { 93, 0x8700ff }, + { 94, 0x875f00 }, { 95, 0x875f5f }, { 96, 0x875f87 }, + { 97, 0x875faf }, { 98, 0x875fd7 }, { 99, 0x875fff }, + { 100, 0x878700 }, { 101, 0x87875f }, { 102, 0x878787 }, + { 103, 0x8787af }, { 104, 0x8787d7 }, { 105, 0x8787ff }, + { 106, 0x87af00 }, { 107, 0x87af5f }, { 108, 0x87af87 }, + { 109, 0x87afaf }, { 110, 0x87afd7 }, { 111, 0x87afff }, + { 112, 0x87d700 }, { 113, 0x87d75f }, { 114, 0x87d787 }, + { 115, 0x87d7af }, { 116, 0x87d7d7 }, { 117, 0x87d7ff }, + { 118, 0x87ff00 }, { 119, 0x87ff5f }, { 120, 0x87ff87 }, + { 121, 0x87ffaf }, { 122, 0x87ffd7 }, { 123, 0x87ffff }, + { 124, 0xaf0000 }, { 125, 0xaf005f }, { 126, 0xaf0087 }, + { 127, 0xaf00af }, { 128, 0xaf00d7 }, { 129, 0xaf00ff }, + { 130, 0xaf5f00 }, { 131, 0xaf5f5f }, { 132, 0xaf5f87 }, + { 133, 0xaf5faf }, { 134, 0xaf5fd7 }, { 135, 0xaf5fff }, + { 136, 0xaf8700 }, { 137, 0xaf875f }, { 138, 0xaf8787 }, + { 139, 0xaf87af }, { 140, 0xaf87d7 }, { 141, 0xaf87ff }, + { 142, 0xafaf00 }, { 143, 0xafaf5f }, { 144, 0xafaf87 }, + { 145, 0xafafaf }, { 146, 0xafafd7 }, { 147, 0xafafff }, + { 148, 0xafd700 }, { 149, 0xafd75f }, { 150, 0xafd787 }, + { 151, 0xafd7af }, { 152, 0xafd7d7 }, { 153, 0xafd7ff }, + { 154, 0xafff00 }, { 155, 0xafff5f }, { 156, 0xafff87 }, + { 157, 0xafffaf }, { 158, 0xafffd7 }, { 159, 0xafffff }, + { 160, 0xd70000 }, { 161, 0xd7005f }, { 162, 0xd70087 }, + { 163, 0xd700af }, { 164, 0xd700d7 }, { 165, 0xd700ff }, + { 166, 0xd75f00 }, { 167, 0xd75f5f }, { 168, 0xd75f87 }, + { 169, 0xd75faf }, { 170, 0xd75fd7 }, { 171, 0xd75fff }, + { 172, 0xd78700 }, { 173, 0xd7875f }, { 174, 0xd78787 }, + { 175, 0xd787af }, { 176, 0xd787d7 }, { 177, 0xd787ff }, + { 178, 0xd7af00 }, { 179, 0xd7af5f }, { 180, 0xd7af87 }, + { 181, 0xd7afaf }, { 182, 0xd7afd7 }, { 183, 0xd7afff }, + { 184, 0xd7d700 }, { 185, 0xd7d75f }, { 186, 0xd7d787 }, + { 187, 0xd7d7af }, { 188, 0xd7d7d7 }, { 189, 0xd7d7ff }, + { 190, 0xd7ff00 }, { 191, 0xd7ff5f }, { 192, 0xd7ff87 }, + { 193, 0xd7ffaf }, { 194, 0xd7ffd7 }, { 195, 0xd7ffff }, + { 196, 0xff0000 }, { 197, 0xff005f }, { 198, 0xff0087 }, + { 199, 0xff00af }, { 200, 0xff00d7 }, { 201, 0xff00ff }, + { 202, 0xff5f00 }, { 203, 0xff5f5f }, { 204, 0xff5f87 }, + { 205, 0xff5faf }, { 206, 0xff5fd7 }, { 207, 0xff5fff }, + { 208, 0xff8700 }, { 209, 0xff875f }, { 210, 0xff8787 }, + { 211, 0xff87af }, { 212, 0xff87d7 }, { 213, 0xff87ff }, + { 214, 0xffaf00 }, { 215, 0xffaf5f }, { 216, 0xffaf87 }, + { 217, 0xffafaf }, { 218, 0xffafd7 }, { 219, 0xffafff }, + { 220, 0xffd700 }, { 221, 0xffd75f }, { 222, 0xffd787 }, + { 223, 0xffd7af }, { 224, 0xffd7d7 }, { 225, 0xffd7ff }, + { 226, 0xffff00 }, { 227, 0xffff5f }, { 228, 0xffff87 }, + { 229, 0xffffaf }, { 230, 0xffffd7 }, { 231, 0xffffff }, + { 232, 0x080808 }, { 233, 0x121212 }, { 234, 0x1c1c1c }, + { 235, 0x262626 }, { 236, 0x303030 }, { 237, 0x3a3a3a }, + { 238, 0x444444 }, { 239, 0x4e4e4e }, { 240, 0x585858 }, + { 241, 0x626262 }, { 242, 0x6c6c6c }, { 243, 0x767676 }, + { 244, 0x808080 }, { 245, 0x8a8a8a }, { 246, 0x949494 }, + { 247, 0x9e9e9e }, { 248, 0xa8a8a8 }, { 249, 0xb2b2b2 }, + { 250, 0xbcbcbc }, { 251, 0xc6c6c6 }, { 252, 0xd0d0d0 }, + { 253, 0xdadada }, { 254, 0xe4e4e4 }, { 255, 0xeeeeee }, +}; + +/** Calculate the color distance between two colors. + * + * Algorithm taken from UnNetHack which took it from + * https://www.compuphase.com/cmetric.htm + **/ + +int +color_distance(uint32_t rgb1, uint32_t rgb2) +{ + int r1 = (rgb1 >> 16) & 0xFF; + int g1 = (rgb1 >> 8) & 0xFF; + int b1 = (rgb1) & 0xFF; + int r2 = (rgb2 >> 16) & 0xFF; + int g2 = (rgb2 >> 8) & 0xFF; + int b2 = (rgb2) & 0xFF; + + int rmean = (r1 + r2) / 2; + int r = r1 - r2; + int g = g1 - g2; + int b = b1 - b2; + return ((((512 + rmean) * r * r) >> 8) + 4 * g * g + + (((767 - rmean) * b * b) >> 8)); +} + +boolean +closest_color(uint32 lcolor, uint32 *closecolor, uint16 *clridx) +{ + int i, color_index = -1, similar = INT_MAX, current; + boolean retbool = FALSE; + + for (i = 0; i < SIZE(color_256_definitions); i++) { + /* look for an exact match */ + if (lcolor == color_256_definitions[i].value) { + color_index = i; + break; + } + /* find a close color match */ + current = color_distance(lcolor, color_256_definitions[i].value); + if (current < similar) { + color_index = i; + similar = current; + } + } + if (closecolor && clridx && color_index >= 0) { + *closecolor = color_256_definitions[color_index].value; + *clridx = color_256_definitions[color_index].index; + retbool = TRUE; + } + return retbool; +} + +uint32 +get_nhcolor_from_256_index(int idx) +{ + uint32 retcolor = NO_COLOR | NH_BASIC_COLOR; + + if (IndexOk(idx, color_256_definitions)) + retcolor = color_256_definitions[idx].value; + return retcolor; +} + +#ifdef CHANGE_COLOR + +int +count_alt_palette(void) +{ + int clr, clrcount = 0; + + for (clr = 0; clr < CLR_MAX; ++clr) { + if (ga.altpalette[clr] != 0U) + clrcount++; + } + return clrcount; +} + +int +alternative_palette(char *op) +{ + char buf[BUFSZ], *c_colorid, *c_colorval, *cp; + int reslt = 0, coloridx = CLR_MAX; + long rgb = 0L; + boolean slash = FALSE; + + if (!op) + return 0; + + Snprintf(buf, sizeof buf, "%s", op); + c_colorval = (char *) 0; + c_colorid = cp = buf; + while (*cp) { + if (*cp == ':' || *cp == '/') { + if (*cp == '/') { + slash = TRUE; + *cp = '\0'; + } + } + cp++; + if (slash) { + c_colorval = cp; + slash = FALSE; + } + } + /* some sanity checks */ + if (c_colorid && *c_colorid == ' ') + c_colorid++; + if (c_colorval && *c_colorval == ' ') + c_colorval++; + if (c_colorid) + coloridx = match_str2clr(c_colorid, TRUE); + + if (c_colorval && coloridx >= 0 && coloridx < CLR_MAX) { + rgb = rgbstr_to_int32(c_colorval); + if (rgb == -1) { + rgb = alt_color_spec(c_colorval); + } + if (rgb != -1) { + ga.altpalette[coloridx] = (uint32) rgb | NH_ALTPALETTE; + /* use COLORVAL(ga.altpalette[coloridx]) to get + the actual rgb value out of ga.altpalette[] */ + reslt = 1; + } + } + return reslt; +} + +void +change_palette(void) +{ + int clridx; + + for (clridx = 0; clridx < CLR_MAX; ++clridx) { + if (ga.altpalette[clridx] != 0) { + long rgb = (long) COLORVAL(ga.altpalette[clridx]); + (*windowprocs.win_change_color)(clridx, rgb, 0); + } + } +} + +staticfn int32 +alt_color_spec(const char *str) +{ + static NEARDATA const char oct[] = "01234567", dec[] = "0123456789"; + /* hexdd[] is defined in decl.c */ + + const char *dp, *cp = str; + int32 cval = -1; + int dcount, dlimit = 6; + boolean hexescape = FALSE, octescape = FALSE; + + dcount = 0; /* for decimal, octal, hexadecimal cases */ + hexescape = + (*cp == '\\' && cp[1] && (cp[1] == 'x' || cp[1] == 'X') && cp[2]); + if (!hexescape) { + octescape = + (*cp == '\\' && cp[1] && (cp[1] == 'o' || cp[1] == 'O') && cp[2]); + } + + if (hexescape || octescape) { + cval = 0; + cp += 2; + if (octescape) + dlimit = 8; + } else if (*cp == '#' && cp[1]) { + hexescape = TRUE; + cval = 0; + cp += 1; + } else if (cp[1]) { + cval = 0; + dlimit = 8; + } else if (!cp[1]) { + if (strchr(dec, *cp) != 0) { + /* simple val, or nothing left for \ to escape */ + cval = (*cp - '0'); + } + dlimit = 1; + cp++; + } + + while (*cp) { + if (!hexescape && !octescape && strchr(dec, *cp)) { + cval = (cval * 10) + (*cp - '0'); + } else if (octescape && strchr(oct, *cp)) { + cval = (cval * 8) + (*cp - '0'); + } else if (hexescape && (dp = strchr(hexdd, *cp)) != 0) { + cval = (cval * 16) + ((int) (dp - hexdd) / 2); + } + ++cp; + if (++dcount > dlimit) { + cval = -1; + break; + } + } + return cval; +} +#endif /* CHANGE_COLOR */ + +/*coloratt.c*/ diff --git a/src/date.c b/src/date.c index e75a31550d..f85712652e 100644 --- a/src/date.c +++ b/src/date.c @@ -3,20 +3,21 @@ /* NetHack may be freely redistributed. See license for details. */ #include "config.h" +#include "hacklib.h" +#ifdef Snprintf +#undef Snprintf +#endif +#define Snprintf(str, size, ...) \ + nh_snprintf(__func__, __LINE__, str, size, __VA_ARGS__) /* these are in extern.h but we don't include hack.h */ -void populate_nomakedefs(struct version_info *); +void populate_nomakedefs(struct version_info *) NONNULLARG1; void free_nomakedefs(void); -#define Snprintf(str, size, ...) \ - nh_snprintf(__func__, __LINE__, str, size, __VA_ARGS__) -extern void nh_snprintf(const char *func, int line, char *str, size_t size, - const char *fmt, ...); extern unsigned long md_ignored_features(void); extern char *mdlib_version_string(char *, const char *); extern char *version_id_string(char *, size_t, const char *); extern char *bannerc_string(char *, size_t, const char *); -extern int case_insensitive_comp(const char *, const char *); /* nomakedefs_populated: flag for whether 'nomakedefs' should be freed */ static int nomakedefs_populated = 0; @@ -28,6 +29,7 @@ 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 */ "1.0.0-0", "xNetHack Version 1.0.0-0 - last build Tue Jul 28 13:18:57 1987.", 0x01010000UL, @@ -124,6 +126,9 @@ populate_nomakedefs(struct version_info *version) #ifdef NETHACK_GIT_BRANCH nomakedefs.git_branch = dupstr(NETHACK_GIT_BRANCH); #endif +#ifdef NETHACK_GIT_PREFIX + nomakedefs.git_prefix = dupstr(NETHACK_GIT_PREFIX); +#endif nomakedefs_populated = 1; return; @@ -160,6 +165,11 @@ free_nomakedefs(void) free((genericptr_t) nomakedefs.git_branch), nomakedefs.git_branch = 0; #endif +#ifdef NETHACK_GIT_PREFIX + if (nomakedefs.git_prefix) + free((genericptr_t) nomakedefs.git_prefix), + nomakedefs.git_prefix = 0; +#endif /* values are Null now; dynamic vs static doesn't really matter anymore */ nomakedefs_populated = 0; diff --git a/src/dbridge.c b/src/dbridge.c index ecd8d4bcb2..9082a85ce1 100644 --- a/src/dbridge.c +++ b/src/dbridge.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 dbridge.c $NHDT-Date: 1596498153 2020/08/03 23:42:33 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.47 $ */ +/* NetHack 3.7 dbridge.c $NHDT-Date: 1702349063 2023/12/12 02:44:23 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.62 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -19,21 +19,21 @@ #include "hack.h" -static void get_wall_for_db(coordxy *, coordxy *); -static struct entity *e_at(coordxy, coordxy); -static void m_to_e(struct monst *, coordxy, coordxy, struct entity *); -static void u_to_e(struct entity *); -static void set_entity(coordxy, coordxy, struct entity *); -static const char *e_nam(struct entity *); -static const char *E_phrase(struct entity *, const char *); -static boolean e_survives_at(struct entity *, coordxy, coordxy); -static void e_died(struct entity *, int, int); -static boolean automiss(struct entity *); -static boolean e_missed(struct entity *, boolean); -static boolean e_jumps(struct entity *); -static void do_entity(struct entity *); -static boolean cerberus_blocked(void); -static void nokiller(void); +staticfn void get_wall_for_db(coordxy *, coordxy *); +staticfn struct entity *e_at(coordxy, coordxy); +staticfn void m_to_e(struct monst *, coordxy, coordxy, struct entity *); +staticfn void u_to_e(struct entity *); +staticfn void set_entity(coordxy, coordxy, struct entity *); +staticfn const char *e_nam(struct entity *); +staticfn const char *E_phrase(struct entity *, const char *); +staticfn boolean e_survives_at(struct entity *, coordxy, coordxy); +staticfn void e_died(struct entity *, int, int); +staticfn boolean automiss(struct entity *); +staticfn boolean e_missed(struct entity *, boolean); +staticfn boolean e_jumps(struct entity *); +staticfn void do_entity(struct entity *); +staticfn boolean cerberus_blocked(void); +staticfn void nokiller(void); boolean is_waterwall(coordxy x, coordxy y) @@ -144,28 +144,30 @@ db_under_typ(int mask) * We want to know whether a wall (or a door) is the portcullis (passageway) * of an eventual drawbridge. * - * Return value: the direction of the drawbridge. + * Return value: the direction of the drawbridge, or -1 if not valid */ - int is_drawbridge_wall(coordxy x, coordxy y) { struct rm *lev; + if (!isok(x, y)) + return -1; + lev = &levl[x][y]; if (lev->typ != DOOR && lev->typ != DBWALL) return -1; - if (IS_DRAWBRIDGE(levl[x + 1][y].typ) + if (isok(x + 1, y) && IS_DRAWBRIDGE(levl[x + 1][y].typ) && (levl[x + 1][y].drawbridgemask & DB_DIR) == DB_WEST) return DB_WEST; - if (IS_DRAWBRIDGE(levl[x - 1][y].typ) + if (isok(x - 1, y) && IS_DRAWBRIDGE(levl[x - 1][y].typ) && (levl[x - 1][y].drawbridgemask & DB_DIR) == DB_EAST) return DB_EAST; - if (IS_DRAWBRIDGE(levl[x][y - 1].typ) + if (isok(x, y - 1) && IS_DRAWBRIDGE(levl[x][y - 1].typ) && (levl[x][y - 1].drawbridgemask & DB_DIR) == DB_SOUTH) return DB_SOUTH; - if (IS_DRAWBRIDGE(levl[x][y + 1].typ) + if (isok(x, y + 1) && IS_DRAWBRIDGE(levl[x][y + 1].typ) && (levl[x][y + 1].drawbridgemask & DB_DIR) == DB_NORTH) return DB_NORTH; @@ -218,7 +220,7 @@ find_drawbridge(coordxy *x, coordxy *y) /* * Find the drawbridge wall associated with a drawbridge. */ -static void +staticfn void get_wall_for_db(coordxy *x, coordxy *y) { switch (levl[*x][*y].drawbridgemask & DB_DIR) { @@ -266,6 +268,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; @@ -292,14 +295,15 @@ create_drawbridge(coordxy x, coordxy y, int dir, boolean flag) return TRUE; } -static struct entity * +staticfn struct entity * e_at(coordxy x, coordxy y) { int entitycnt; for (entitycnt = 0; entitycnt < ENTITIES; entitycnt++) - if ((go.occupants[entitycnt].edata) && (go.occupants[entitycnt].ex == x) - && (go.occupants[entitycnt].ey == y)) + if (go.occupants[entitycnt].edata + && go.occupants[entitycnt].ex == x + && go.occupants[entitycnt].ey == y) break; debugpline1("entitycnt = %d", entitycnt); #ifdef D_DEBUG @@ -309,7 +313,7 @@ e_at(coordxy x, coordxy y) : &(go.occupants[entitycnt]); } -static void +staticfn void m_to_e(struct monst *mtmp, coordxy x, coordxy y, struct entity *etmp) { etmp->emon = mtmp; @@ -326,7 +330,7 @@ m_to_e(struct monst *mtmp, coordxy x, coordxy y, struct entity *etmp) } } -static void +staticfn void u_to_e(struct entity *etmp) { etmp->emon = &gy.youmonst; @@ -335,7 +339,7 @@ u_to_e(struct entity *etmp) etmp->edata = gy.youmonst.data; } -static void +staticfn void set_entity( coordxy x, coordxy y, /* location of span or portcullis */ struct entity *etmp) /* pointer to occupants[0] or occupants[1] */ @@ -356,7 +360,7 @@ set_entity( /* #define e_strg(etmp, func) (is_u(etmp) ? (char *) 0 : func(etmp->emon)) */ -static const char * +staticfn const char * e_nam(struct entity *etmp) { return is_u(etmp) ? "you" : mon_nam(etmp->emon); @@ -366,7 +370,7 @@ e_nam(struct entity *etmp) * Generates capitalized entity name, makes 2nd -> 3rd person conversion on * verb, where necessary. */ -static const char * +staticfn const char * E_phrase(struct entity *etmp, const char *verb) { static char wholebuf[80]; @@ -385,7 +389,7 @@ E_phrase(struct entity *etmp, const char *verb) /* * Simple-minded "can it be here?" routine */ -static boolean +staticfn boolean e_survives_at(struct entity *etmp, coordxy x, coordxy y) { if (noncorporeal(etmp->edata)) @@ -406,23 +410,23 @@ e_survives_at(struct entity *etmp, coordxy x, coordxy y) return TRUE; } -static void +staticfn void e_died(struct entity *etmp, int xkill_flags, int how) { if (is_u(etmp)) { if (how == DROWNING) { - gk.killer.name[0] = 0; /* drown() sets its own killer */ + svk.killer.name[0] = 0; /* drown() sets its own killer */ (void) drown(); } else if (how == BURNING) { - gk.killer.name[0] = 0; /* lava_effects() sets own killer */ + svk.killer.name[0] = 0; /* lava_effects() sets own killer */ (void) lava_effects(); } else { coord xy; /* use more specific killer if specified */ - if (!gk.killer.name[0]) { - gk.killer.format = KILLED_BY_AN; - Strcpy(gk.killer.name, "falling drawbridge"); + if (!svk.killer.name[0]) { + svk.killer.format = KILLED_BY_AN; + Strcpy(svk.killer.name, "falling drawbridge"); } done(how); /* So, you didn't die */ @@ -442,12 +446,12 @@ e_died(struct entity *etmp, int xkill_flags, int how) } else { int entitycnt; - gk.killer.name[0] = 0; + svk.killer.name[0] = 0; /* fake "digested to death" damage-type suppresses corpse */ #define mk_message(dest) (((dest & XKILL_NOMSG) != 0) ? (char *) 0 : "") #define mk_corpse(dest) (((dest & XKILL_NOCORPSE) != 0) ? AD_DGST : AD_PHYS) /* if monsters are moving, one of them caused the destruction */ - if (gc.context.mon_moving) + if (svc.context.mon_moving) monkilled(etmp->emon, mk_message(xkill_flags), mk_corpse(xkill_flags)); else /* you caused it */ @@ -468,7 +472,7 @@ e_died(struct entity *etmp, int xkill_flags, int how) /* * These are never directly affected by a bridge or portcullis. */ -static boolean +staticfn boolean automiss(struct entity *etmp) { return (boolean) ((is_u(etmp) ? Passes_walls : passes_walls(etmp->edata)) @@ -478,7 +482,7 @@ automiss(struct entity *etmp) /* * Does falling drawbridge or portcullis miss etmp? */ -static boolean +staticfn boolean e_missed(struct entity *etmp, boolean chunks) { int misses; @@ -513,7 +517,7 @@ e_missed(struct entity *etmp, boolean chunks) /* * Can etmp jump from death? */ -static boolean +staticfn boolean e_jumps(struct entity *etmp) { int tmp = 4; /* out of 10 */ @@ -536,7 +540,7 @@ e_jumps(struct entity *etmp) return (tmp >= rnd(10)) ? TRUE : FALSE; } -static void +staticfn void do_entity(struct entity *etmp) { coordxy newx, newy, oldx, oldy; @@ -580,8 +584,8 @@ do_entity(struct entity *etmp) } else { if (crm->typ == DRAWBRIDGE_DOWN) { if (is_u(etmp)) { - gk.killer.format = NO_KILLER_PREFIX; - Strcpy(gk.killer.name, + svk.killer.format = NO_KILLER_PREFIX; + Strcpy(svk.killer.name, "crushed to death underneath a drawbridge"); } pline("%s crushed underneath the drawbridge.", @@ -700,8 +704,8 @@ do_entity(struct entity *etmp) E_phrase(etmp, "disappear")); } if (!e_survives_at(etmp, etmp->ex, etmp->ey)) { - gk.killer.format = KILLED_BY_AN; - Strcpy(gk.killer.name, "closing drawbridge"); + svk.killer.format = KILLED_BY_AN; + Strcpy(svk.killer.name, "closing drawbridge"); e_died(etmp, XKILL_NOMSG, CRUSHING); return; } @@ -732,8 +736,8 @@ do_entity(struct entity *etmp) pline("%s into the %s.", E_phrase(etmp, "fall"), lava ? hliquid("lava") : "moat"); } - gk.killer.format = NO_KILLER_PREFIX; - Strcpy(gk.killer.name, "fell from a drawbridge"); + svk.killer.format = NO_KILLER_PREFIX; + Strcpy(svk.killer.name, "fell from a drawbridge"); e_died(etmp, /* CRUSHING is arbitrary */ XKILL_NOCORPSE | (e_inview ? XKILL_GIVEMSG : XKILL_NOMSG), is_pool(etmp->ex, etmp->ey) ? DROWNING @@ -744,11 +748,11 @@ do_entity(struct entity *etmp) } /* clear stale reason for death and both 'entities' before returning */ -static void +staticfn void nokiller(void) { - gk.killer.name[0] = '\0'; - gk.killer.format = 0; + svk.killer.name[0] = '\0'; + svk.killer.format = 0; m_to_e((struct monst *) 0, 0, 0, &go.occupants[0]); m_to_e((struct monst *) 0, 0, 0, &go.occupants[1]); } @@ -759,7 +763,7 @@ nokiller(void) void close_drawbridge(coordxy x, coordxy y) { - register struct rm *lev1, *lev2; + struct rm *lev1, *lev2; struct trap *t; coordxy x2, y2; @@ -826,7 +830,7 @@ close_drawbridge(coordxy x, coordxy y) boolean open_drawbridge(coordxy x, coordxy y) { - register struct rm *lev1, *lev2; + struct rm *lev1, *lev2; struct trap *t; coordxy x2, y2; @@ -882,7 +886,7 @@ open_drawbridge(coordxy x, coordxy y) void destroy_drawbridge(coordxy x, coordxy y) { - register struct rm *lev1, *lev2; + struct rm *lev1, *lev2; struct trap *t; struct obj *otmp; coordxy x2, y2; @@ -971,8 +975,8 @@ destroy_drawbridge(coordxy x, coordxy y) if (e_inview) pline("%s blown apart by flying debris.", E_phrase(etmp2, "are")); - gk.killer.format = KILLED_BY_AN; - Strcpy(gk.killer.name, "exploding drawbridge"); + svk.killer.format = KILLED_BY_AN; + Strcpy(svk.killer.name, "exploding drawbridge"); e_died(etmp2, XKILL_NOCORPSE | (e_inview ? XKILL_GIVEMSG : XKILL_NOMSG), CRUSHING); /*no corpse*/ @@ -1004,8 +1008,8 @@ destroy_drawbridge(coordxy x, coordxy y) debugpline1("%s from shrapnel", E_phrase(etmp1, "die")); } } - gk.killer.format = KILLED_BY_AN; - Strcpy(gk.killer.name, "collapsing drawbridge"); + svk.killer.format = KILLED_BY_AN; + Strcpy(svk.killer.name, "collapsing drawbridge"); e_died(etmp1, XKILL_NOCORPSE | (e_inview ? XKILL_GIVEMSG : XKILL_NOMSG), CRUSHING); /*no corpse*/ @@ -1014,10 +1018,12 @@ destroy_drawbridge(coordxy x, coordxy y) } } nokiller(); + if (Is_stronghold(&u.uz)) + u.uevent.uheard_tune = 3; /* bridge is gone so tune is now useless */ } /* The drawbridge at the Gate of Hell will not open while Cerberus guards it. */ -static boolean +staticfn boolean cerberus_blocked(void) { if (Is_hellgate(&u.uz)) { diff --git a/src/decl.c b/src/decl.c index 7fb67b7b1f..55da6fb24c 100644 --- a/src/decl.c +++ b/src/decl.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 decl.c $NHDT-Date: 1661896581 2022/08/30 21:56:21 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.266 $ */ +/* 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. */ @@ -38,6 +38,7 @@ const char *c_obj_colors[] = { const struct c_common_strings c_common_strings = { "Nothing happens.", + "Nothing seems to happen.", "That's enough tries!", "That is a silly thing to %s.", "shudder for a moment.", @@ -50,7 +51,7 @@ const struct c_common_strings c_common_strings = { "mon", "you" } }; -const struct savefile_info default_sfinfo = { +static const struct savefile_info default_sfinfo = { #ifdef NHSTDC 0x00000000UL #else @@ -92,17 +93,21 @@ const char *fqn_prefix_names[PREFIX_COUNT] = { }; #endif +/* used by coloratt.c, options.c, utf8map.c, windows.c */ +const char hexdd[33] = "00112233445566778899aAbBcCdDeEfF"; + /* x/y/z deltas for the 10 movement directions (8 compass pts, 2 down/up) */ const schar xdir[N_DIRS_Z] = { -1, -1, 0, 1, 1, 1, 0, -1, 0, 0 }; const schar ydir[N_DIRS_Z] = { 0, -1, -1, -1, 0, 1, 1, 1, 0, 0 }; const schar zdir[N_DIRS_Z] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, -1 }; -/* redordered directions, cardinals first */ +/* reordered directions, cardinals first */ const schar dirs_ord[N_DIRS] = { DIR_W, DIR_N, DIR_E, DIR_S, DIR_NW, DIR_NE, DIR_SE, DIR_SW }; NEARDATA boolean has_strong_rngseed = FALSE; struct engr *head_engr; NEARDATA struct instance_flags iflags; +NEARDATA struct accessibility_data a11y; /* NOTE: the order of these words exactly corresponds to the order of oc_material values #define'd in objclass.h. */ const char *materialnm[] = { "mysterious", "liquid", "wax", "organic", @@ -133,6 +138,8 @@ const char ynchars[] = "yn"; const char ynqchars[] = "ynq"; const char ynaqchars[] = "ynaq"; const char ynNaqchars[] = "yn#aq"; +const char rightleftchars[] = "rl"; +const char hidespinchars[] = "hsq"; NEARDATA long yn_number = 0L; #ifdef PANICTRACE @@ -141,7 +148,7 @@ const char *ARGV0; #define IVMAGIC 0xdeadbeef -const struct Role urole_init_data = { +static const struct Role urole_init_data = { { "Undefined", 0 }, { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, @@ -159,7 +166,7 @@ const struct Role urole_init_data = { 10 }; -const struct Race urace_init_data = { +static const struct Race urace_init_data = { "something", "undefined", "something", @@ -180,8 +187,9 @@ const struct Race urace_init_data = { { 1, 0, 2, 0, 2, 0 } /* Energy */ }; +struct display_hints disp = { 0 }; -const struct instance_globals_a g_init_a = { +static const struct instance_globals_a g_init_a = { /* artifact.c */ /* decl.c */ UNDEFINED_PTR, /* afternmv */ @@ -196,7 +204,14 @@ const struct instance_globals_a g_init_a = { /* mon.c */ UNDEFINED_PTR, /* animal_list */ UNDEFINED_VALUE, /* animal_list_count */ +#ifdef CHANGE_COLOR + /* options.c */ + { 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, + 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U }, /* altpalette[CLR_MAX] */ +#endif /* pickup.c */ + 0, /* A_first_hint */ + 0, /* A_second_hint */ UNDEFINED_VALUE, /* abort_looting */ /* shk.c */ FALSE, /* auto_credit */ @@ -209,9 +224,9 @@ const struct instance_globals_a g_init_a = { IVMAGIC /* a_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_b g_init_b = { +static const struct instance_globals_b g_init_b = { /* botl.c */ - { { { NULL, NULL, 0L, FALSE, FALSE, 0, 0U, { 0 }, NULL, 0, 0, 0 + { { { NULL, NULL, 0L, FALSE, FALSE, 0, 0U, { 0 }, { 0 }, NULL, 0, 0, 0 #ifdef STATUS_HILITES , UNDEFINED_PTR, UNDEFINED_PTR #endif @@ -222,28 +237,24 @@ const struct instance_globals_b g_init_b = { 0L, /* bl_hilite_moves */ #endif /* decl.c */ - DUMMY, /* bases */ { 0, 0 }, /* bhitpos */ UNDEFINED_PTR, /* billobjs */ - /* dungeon.c */ - UNDEFINED_PTR, /* branches */ /* files.c */ BONESINIT, /* bones */ /* hack.c */ 0U, /* bldrpush_oid - last boulder pushed */ 0L, /* bldrpushtime - turn message was given about pushing that boulder */ - /* mkmaze.c */ - UNDEFINED_PTR, /* bbubbles */ /* pickup.c */ FALSE, /* bucx_filter */ /* zap.c */ NULL, /* buzzer -- monst that zapped/cast/breathed to initiate buzz() */ + FALSE, /* bot_disabled */ TRUE, /* havestate*/ IVMAGIC /* b_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_c g_init_c = { +static const struct instance_globals_c g_init_c = { UNDEFINED_VALUES, /* command_queue */ /* botl.c */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -254,15 +265,18 @@ const struct instance_globals_c g_init_c = { { 0, 0 }, /* clicklook_cc */ /* decl.c */ UNDEFINED_VALUES, /* chosen_windowtype */ - UNDEFINED_VALUES, /* command_line */ + 0, /* cmd_key */ 0L, /* command_count */ UNDEFINED_PTR, /* current_wand */ #ifdef DEF_PAGER NULL, /* catmore */ #endif - DUMMY, /* context */ /* dog.c */ DUMMY, /* catname */ + /* end.c */ + NULL, /* crash_email */ + NULL, /* crash_name */ + -1, /* crash_urlmax */ /* symbols.c */ 0, /* currentgraphics */ /* files.c */ @@ -291,18 +305,13 @@ const struct instance_globals_c g_init_c = { IVMAGIC /* c_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_d g_init_d = { +static const struct instance_globals_d g_init_d = { /* decl.c */ 0, /* doorindex */ 0L, /* done_money */ 0L, /* domove_attempting */ 0L, /* domove_succeeded */ - { { UNDEFINED_VALUES } }, /* dungeons */ - { 0, 0, 0, 0, 0, 0, 0, 0 }, /* dndest */ FALSE, /* defer_see_monsters */ - { DUMMY }, /* dungeon_topology */ - 0, /* doors_alloc */ - NULL, /* doors */ /* dig.c */ UNDEFINED_VALUE, /* did_dig_msg */ /* do.c */ @@ -315,18 +324,22 @@ const struct instance_globals_d g_init_d = { 0L, /* done_seq */ /* mon.c */ FALSE, /* disintegested */ - /* o_init.c */ - DUMMY, /* disco */ /* objname.c */ 0, /* distantname */ + /* pickup.c */ + FALSE, /* decor_fumble_override */ + FALSE, /* decor_levitate_override */ + /* windows.c */ #ifdef DUMPHTML FALSE, /* dumping_list */ #endif + FALSE, /* deferred_showpaths */ + NULL, /* deferred_showpaths_dir */ TRUE, /* havestate*/ IVMAGIC /* d_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_e g_init_e = { +static const struct instance_globals_e g_init_e = { /* cmd.c */ WIN_ERR, /* en_win */ FALSE, /* en_via_menu */ @@ -341,7 +354,7 @@ const struct instance_globals_e g_init_e = { IVMAGIC /* e_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_f g_init_f = { +static const struct instance_globals_f g_init_f = { /* decl.c */ UNDEFINED_PTR, /* ftrap */ { NULL }, /* fqn_prefix */ @@ -360,13 +373,14 @@ const struct instance_globals_f g_init_f = { IVMAGIC /* f_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_g g_init_g = { +static const struct instance_globals_g g_init_g = { /* display.c */ { { { 0 } } }, /* gbuf */ UNDEFINED_VALUES, /* gbuf_start */ UNDEFINED_VALUES, /* gbug_stop */ /* do_name.c */ + 0, 0, /* getposx, getposy */ UNDEFINED_PTR, /* gloc_filter_map */ UNDEFINED_VALUE, /* gloc_filter_floodfill_match_glyph */ /* dog.c */ @@ -379,6 +393,11 @@ const struct instance_globals_g g_init_g = { { UNDEFINED_VALUES }, /* gems */ /* invent.c */ 0L, /* glyph_reset_timestamp */ + /* nhlua.c */ + FALSE, /* gmst_stored */ + 0L, /* gmst_moves */ + NULL, /* gmst_invent */ + NULL, NULL, NULL, /* gmst_ubak, gmst_disco, gmst_mvitals */ /* pline.c */ UNDEFINED_PTR, /* gamelog */ /* region.c */ @@ -391,10 +410,9 @@ const struct instance_globals_g g_init_g = { IVMAGIC /* g_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_h g_init_h = { +static const struct instance_globals_h g_init_h = { /* decl.c */ NULL, /* hname */ - 0, /* hackpid */ #if defined(MICRO) || defined(WIN32) UNDEFINED_VALUES, /* hackdir */ #endif /* MICRO || WIN32 */ @@ -411,10 +429,9 @@ const struct instance_globals_h g_init_h = { IVMAGIC /* h_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_i g_init_i = { +static const struct instance_globals_i g_init_i = { /* decl.c */ 0, /* in_doagain */ - { 0, 0 } , /* inv_pos */ FALSE, /* in_mklev */ FALSE, /* in_steed_dismounting */ UNDEFINED_PTR, /* invent */ @@ -424,38 +441,38 @@ const struct instance_globals_i g_init_i = { NULL, /* invbuf */ 0U, /* invbufsize */ 0, /* in_sync_perminvent */ + /* mon.c */ + NULL, /* itermonarr */ /* restore.c */ UNDEFINED_PTR, /* id_map */ /* sp_lev.c */ FALSE, /* in_mk_themerooms */ + TRUE, /* havestate*/ IVMAGIC /* i_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_j g_init_j = { +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 */ }; -const struct instance_globals_k g_init_k = { +static const struct instance_globals_k g_init_k = { + { 0, 0 }, /* kickedloc */ /* decl.c */ UNDEFINED_PTR, /* kickedobj */ - DUMMY, /* killer */ /* read.c */ UNDEFINED_VALUE, /* known */ TRUE, /* havestate*/ IVMAGIC /* k_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_l g_init_l = { +static const struct instance_globals_l g_init_l = { /* cmd.c */ UNDEFINED_VALUE, /* last_command_count */ - /* dbridge.c */ - { { 0 } }, /* lastseentyp */ - { UNDEFINED_VALUES }, /* level_info */ - { { { UNDEFINED_VALUES } } }, /* level */ + /* decl.c */ #if defined(UNIX) || defined(VMS) 0, /* locknum */ #endif @@ -471,6 +488,8 @@ const struct instance_globals_l g_init_l = { UNDEFINED_PTR, /* light_base */ /* mklev.c */ { UNDEFINED_PTR }, /* luathemes[] */ + /* mon.c */ + 0U, /* last_hider */ /* nhlan.c */ #ifdef MAX_LAN_USERNAME UNDEFINED_VALUES, /* lusername */ @@ -479,6 +498,8 @@ const struct instance_globals_l g_init_l = { /* nhlua.c */ UNDEFINED_VALUE, /* luacore */ DUMMY, /* lua_warnbuf[] */ + 0, /* loglua */ + 0, /* lua_sid */ /* options.c */ FALSE, /* loot_reset_justpicked */ /* save.c */ @@ -498,7 +519,7 @@ const struct instance_globals_l g_init_l = { IVMAGIC /* l_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_m g_init_m = { +static const struct instance_globals_m g_init_m = { /* apply.c */ 0, /* mkot_trap_warn_count */ /* botl.c */ @@ -512,18 +533,14 @@ const struct instance_globals_m g_init_m = { { 0, 0, STRANGE_OBJECT, FALSE }, /* m_shot */ FALSE, /* mrg_to_wielded */ UNDEFINED_PTR, /* menu_colorings */ - 1L, /* moves; misnamed turn counter */ UNDEFINED_PTR, /* migrating_objs */ /* display.c */ DUMMY, /* monstercolors */ /* dog.c */ UNDEFINED_PTR, /* mydogs */ UNDEFINED_PTR, /* migrating_mons */ - { UNDEFINED_VALUES }, /* mvitals */ /* dokick.c */ UNDEFINED_PTR, /* maploc */ - /* dungeon.c */ - UNDEFINED_PTR, /* mapseenchn */ /* mhitu.c */ UNDEFINED_VALUE, /* mhitu_dieroll */ /* mklev.c */ @@ -547,21 +564,20 @@ const struct instance_globals_m g_init_m = { UNDEFINED_VALUES, /* mapped_menu_op */ /* region.c */ 0, /* max_regions */ + /* trap.c */ + FALSE, /* mentioned_water */ TRUE, /* havestate*/ IVMAGIC /* m_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_n g_init_n = { +static const struct instance_globals_n g_init_n = { /* botl.c */ 0, /* now_or_before_idx */ /* decl.c */ NULL, /* nomovemsg */ - 0, /* nroom */ 0, /* nsubroom */ /* dokick.c */ UNDEFINED_VALUES, /* nowhere */ - /* dungeon.c */ - 0, /* n_dgns */ /* files.c */ 0, /* nesting */ 0, /* no_sound_notified */ @@ -576,8 +592,6 @@ const struct instance_globals_n g_init_n = { FALSE, /* notonhead */ /* questpgr.c */ UNDEFINED_VALUES, /* nambuf */ - /* region.c */ - 0, /* n_regions */ /* restore.c */ 0, /* n_ids_mapped */ /* sp_lev.c */ @@ -591,7 +605,8 @@ const struct instance_globals_n g_init_n = { IVMAGIC /* n_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_o g_init_o = { +static const struct instance_globals_o g_init_o = { + NULL, /* objs_deleted */ /* dbridge.c */ { { 0 } }, /* occupants */ /* decl.c */ @@ -602,7 +617,6 @@ const struct instance_globals_o g_init_o = { NULL, /* occtxt */ /* symbols.c */ DUMMY, /* ov_primary_syms */ - DUMMY, /* ov_rogue_syms */ /* invent.c */ UNDEFINED_VALUES, /* only (coord) */ /* o_init.c */ @@ -613,6 +627,11 @@ const struct instance_globals_o g_init_o = { FALSE, /* opt_from_file */ FALSE, /* opt_need_redraw */ FALSE, /* opt_need_glyph_reset */ + FALSE, /* opt_need_promptstyle */ + FALSE, /* opt_reset_customcolors */ + FALSE, /* opt_reset_customsymbols */ + FALSE, /* opt_update_basic_palette */ + FALSE, /* opt_symset_changed */ /* pickup.c */ 0, /* oldcap */ /* restore.c */ @@ -620,7 +639,6 @@ const struct instance_globals_o g_init_o = { 0L, /* omoves */ /* rumors.c */ 0, /* oracle_flag */ - 0U, /* oracle_cnt */ UNDEFINED_PTR, /* oracle_loc */ /* uhitm.c */ FALSE, /* override_confirmation */ @@ -630,18 +648,14 @@ const struct instance_globals_o g_init_o = { IVMAGIC /* o_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_p g_init_p = { +static const struct instance_globals_p g_init_p = { /* apply.c */ -1, /* polearm_range_min */ -1, /* polearm_range_max */ /* decl.c */ - DUMMY, /* plname */ 0, /* plnamelen */ - DUMMY, /* pl_character */ '\0', /* pl_race */ - DUMMY, /* pl_fruit */ UNDEFINED_PTR, /* plinemsg_types */ - UNDEFINED_VALUES, /* program_state */ /* dog.c */ 0, /* petname_used */ UNDEFINED_VALUE, /* preferred_pet */ @@ -651,6 +665,7 @@ const struct instance_globals_p g_init_p = { 0, /* perm_invent_toggling_direction */ /* pickup.c */ FALSE, /* picked_filter */ + 0, /* pickup_encumbrance */ /* pline.c */ 0U, /* pline_flags */ UNDEFINED_VALUES, /* prevmsg */ @@ -669,20 +684,12 @@ const struct instance_globals_p g_init_p = { IVMAGIC /* p_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_q g_init_q = { - /* quest.c */ - DUMMY, /* quest_status */ +static const struct instance_globals_q g_init_q = { TRUE, /* havestate*/ IVMAGIC /* q_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_r g_init_r = { - /* decl.c */ - { DUMMY }, /* rooms */ - /* symbols.c */ - DUMMY, /* rogue_syms */ - /* extralev.c */ - { { UNDEFINED_VALUES } }, /* r */ +static const struct instance_globals_r g_init_r = { /* mkmaze.c */ FALSE, /* ransacked */ /* region.c */ @@ -692,30 +699,26 @@ const struct instance_globals_r g_init_r = { /* role.c */ UNDEFINED_VALUES, /* role_pa */ UNDEFINED_VALUE, /* role_post_attrib */ - { UNDEFINED_VALUES }, /* rfilter */ + { { 0 }, 0 }, /* rfilter */ /* shk.c */ UNDEFINED_VALUES, /* repo */ TRUE, /* havestate*/ IVMAGIC /* r_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_s g_init_s = { +static const struct instance_globals_s g_init_s = { /* artifact.c */ 0, /* spec_dbon_applies */ /* decl.c */ - UNDEFINED_PTR, /* sp_levchn */ UNDEFINED_PTR, /* stairs */ DUMMY, /* smeq */ FALSE, /* stoned */ - { DUMMY }, /* spl_book */ UNDEFINED_PTR, /* subrooms */ /* do.c */ { 0, 0 }, /* save_dlevel */ /* symbols.c */ { DUMMY }, /* symset */ -#ifdef ENHANCED_SYMBOLS - { { 0 } }, /* symset_customizations */ -#endif + { { { 0 } }, { { 0 } } }, /* symset_customizations */ DUMMY, /* showsyms */ /* files.c */ 0, /* symset_count */ @@ -736,9 +739,10 @@ const struct instance_globals_s g_init_s = { (struct menucoloring *) 0, /* save_colorings */ FALSE, /* simple_options_help */ /* pickup.c */ + FALSE, /* sellobj_first */ FALSE, /* shop_filter */ /* pline.c */ -#ifdef DUMPLOG +#ifdef DUMPLOG_CORE 0U, /* saved_pline_index */ { NULL }, /* saved_plines */ #endif @@ -759,11 +763,10 @@ const struct instance_globals_s g_init_s = { IVMAGIC /* s_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_t g_init_t = { +static const struct instance_globals_t g_init_t = { /* apply.c */ UNDEFINED_VALUES, /* trapinfo */ /* decl.c */ - DUMMY, /* tune */ 0, /* tbx */ 0, /* tby */ UNDEFINED_VALUES, /* toplines */ @@ -788,31 +791,30 @@ const struct instance_globals_t g_init_t = { FALSE, /* themeroom_failed */ /* timeout.c */ UNDEFINED_PTR, /* timer_base */ - 1UL, /* timer_id */ /* topten.c */ WIN_ERR, /* toptenwin */ + /* uhitm.c */ + 0, /* twohits */ + /**/ TRUE, /* havestate*/ IVMAGIC /* t_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_u g_init_u = { +static const struct instance_globals_u g_init_u = { /* botl.c */ FALSE, /* update_all */ /* decl.c */ - { 0, 0, 0, 0, 0, 0, 0, 0 }, /* updest */ FALSE, /* unweapon */ /* role.c */ - { UNDEFINED_VALUES }, /* urole */ - UNDEFINED_VALUES, /* urace */ + UNDEFINED_ROLE, /* urole */ + UNDEFINED_RACE, /* urace */ /* save.c */ - 0U, /* ustuck_id */ - 0U, /* usteed_id */ { 0, 0 }, /* uz_save */ TRUE, /* havestate*/ IVMAGIC /* u_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_v g_init_v = { +static const struct instance_globals_v g_init_v = { /* botl.c */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* valset */ @@ -838,10 +840,12 @@ const struct instance_globals_v g_init_v = { IVMAGIC /* v_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_w g_init_w = { +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 */ @@ -855,19 +859,17 @@ const struct instance_globals_w g_init_w = { { { UNDEFINED_VALUES }, UNDEFINED_VALUES, 0, 0, 0, 0 }, /* wizpuzzle */ /* new */ { wdmode_traditional, NO_COLOR }, /* wsettings */ - TRUE, /* havestate*/ - IVMAGIC /* w_magic used to validate that structure layout has been preserved */ + IVMAGIC /* w_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_x g_init_x = { +static const struct instance_globals_x g_init_x = { /* decl.c */ (COLNO - 1) & ~1, /* x_maze_max */ /* lock.c */ UNDEFINED_VALUES, /* xlock */ - /* mkmaze.c */ - UNDEFINED_VALUE, /* xmin */ - UNDEFINED_VALUE, /* xmax */ + /* objnam.c */ + NULL, /* xnamep */ /* sp_lev.c */ UNDEFINED_VALUE, /* xstart */ UNDEFINED_VALUE, /* xsize */ @@ -875,13 +877,10 @@ const struct instance_globals_x g_init_x = { IVMAGIC /* x_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_y g_init_y = { +static const struct instance_globals_y g_init_y = { /* decl.c */ (ROWNO - 1) & ~1, /* y_maze_max */ DUMMY, /* youmonst */ - /* mkmaze.c */ - UNDEFINED_VALUE, /* ymin */ - UNDEFINED_VALUE, /* ymax */ /* pline.c */ NULL, /* you_buf */ 0, /* you_buf_siz */ @@ -892,7 +891,7 @@ const struct instance_globals_y g_init_y = { IVMAGIC /* y_magic to validate that structure layout has been preserved */ }; -const struct instance_globals_z g_init_z = { +static const struct instance_globals_z g_init_z = { /* mon.c */ FALSE, /* zombify */ /* muse.c */ @@ -901,6 +900,137 @@ const struct instance_globals_z g_init_z = { IVMAGIC /* z_magic to validate that structure layout has been preserved */ }; +static const struct instance_globals_saved_b init_svb = { + /* dungeon.c */ + UNDEFINED_PTR, /* branches */ + /* mkmaze.c */ + UNDEFINED_PTR, /* bbubbles */ + DUMMY /* bases */ +}; + +static const struct instance_globals_saved_c init_svc = { + /* decl.c */ + DUMMY, /* context */ +}; + +static const struct instance_globals_saved_d init_svd = { + /* dungeon.c */ + { { {0},{0},{0},{0}, 0, {0}, 0, 0, 0, 0, 0 } }, /* dungeons */ + { {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, + {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, + {0}, {0}, {0}, {0}, {0}, + 0, 0, 0, 0, 0, 0, 0, + {0}, {0}, {0}, + {0}, {0}, {0} }, /* dungeon_topology */ + /* decl.c */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* dndest */ + NULL, /* doors */ + 0, /* doors_alloc */ + /* o_init.c */ + DUMMY, /* disco */ +}; + +static const struct instance_globals_saved_e init_sve = { + /* decl.c */ + NULL /* exclusion_zones */ +}; + +static const struct instance_globals_saved_h init_svh = { + /* decl.c */ + 0 /* hackpid */ +}; + +static const struct instance_globals_saved_i init_svi = { + /* decl.c */ + { 0, 0 } /* inv_pos */ +}; + +static const struct instance_globals_saved_k init_svk = { + /* decl.c */ + DUMMY /* killer */ +}; + +static const struct instance_globals_saved_l init_svl = { + /* decl.c */ + { { 0 } }, /* lastseentyp */ + { { { UNDEFINED_VALUES } }, /* level.locations */ + { { UNDEFINED_PTR } }, /* level.objects */ + { { UNDEFINED_PTR } }, /* level.monsters */ + NULL, NULL, NULL, NULL, NULL, {0} }, /* level */ + { UNDEFINED_VALUES } /* level_info */ +}; + +static const struct instance_globals_saved_m init_svm = { + /* dungeon.c */ + UNDEFINED_PTR, /* mapseenchn */ + /* decl.c */ + 0L, /* moves; misnamed turn counter */ + { UNDEFINED_VALUES } /* mvitals */ +}; + +static const struct instance_globals_saved_n init_svn = { + /* dungeon.c */ + 0, /* n_dgns */ + /* mkroom.c */ + 0, /* nroom */ + /* region.c */ + 0 /* n_regions */ +}; + +static const struct instance_globals_saved_o init_svo = { + /* rumors.c */ + 0U /* oracle_cnt */ +}; + +static const struct instance_globals_saved_p init_svp = { + /* decl.c */ + DUMMY, /* plname */ + DUMMY, /* pl_character */ + DUMMY, /* pl_fruit */ +}; + +static const struct instance_globals_saved_q init_svq = { + /* quest.c */ + DUMMY /* quest_status */ +}; + +static const struct instance_globals_saved_r init_svr = { + /* mkroom.c */ + { DUMMY }, /* rooms */ +}; + +static const struct instance_globals_saved_s init_svs = { + /* decl.c */ + { DUMMY }, /* spl_book */ + UNDEFINED_PTR /* sp_levchn */ +}; + +static const struct instance_globals_saved_t init_svt = { + /* decl.c */ + DUMMY, /* tune */ + /* timeout.c */ + 1UL, /* timer_id */ +}; + +static const struct instance_globals_saved_u init_svu = { + /* decl.c */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* updest */ +}; + +static const struct instance_globals_saved_x init_svx = { + /* mkmaze.c */ + UNDEFINED_VALUE, /* xmin */ + UNDEFINED_VALUE /* xmax */ +}; + +static const struct instance_globals_saved_y init_svy = { + /* mkmaze.c */ + UNDEFINED_VALUE, /* ymin */ + UNDEFINED_VALUE /* ymax */ +}; + +static const struct sinfo init_program_state = { 0 }; + #if 0 struct instance_globals g; #endif /* 0 */ @@ -931,11 +1061,32 @@ struct instance_globals_w gw; struct instance_globals_x gx; struct instance_globals_y gy; struct instance_globals_z gz; +struct instance_globals_saved_b svb; +struct instance_globals_saved_c svc; +struct instance_globals_saved_d svd; +struct instance_globals_saved_e sve; +struct instance_globals_saved_h svh; +struct instance_globals_saved_i svi; +struct instance_globals_saved_k svk; +struct instance_globals_saved_l svl; +struct instance_globals_saved_m svm; +struct instance_globals_saved_n svn; +struct instance_globals_saved_o svo; +struct instance_globals_saved_p svp; +struct instance_globals_saved_q svq; +struct instance_globals_saved_r svr; +struct instance_globals_saved_s svs; +struct instance_globals_saved_t svt; +struct instance_globals_saved_u svu; +struct instance_globals_saved_x svx; +struct instance_globals_saved_y svy; +struct sinfo program_state; const struct const_globals cg = { DUMMY, /* zeroobj */ DUMMY, /* zeromonst */ DUMMY, /* zeroany */ + DUMMY, /* zeroNhRect */ }; #ifdef FUZZER_LOG @@ -993,6 +1144,26 @@ decl_globals_init(void) gx = g_init_x; gy = g_init_y; gz = g_init_z; + svb = init_svb; + svc = init_svc; + svd = init_svd; + sve = init_sve; + svh = init_svh; + svi = init_svi; + svk = init_svk; + svl = init_svl; + svm = init_svm; + svn = init_svn; + svo = init_svo; + svp = init_svp; + svq = init_svq; + svr = init_svr; + svs = init_svs; + svt = init_svt; + svu = init_svu; + svx = init_svx; + svy = init_svy; + program_state = init_program_state; gv.valuables[0].list = gg.gems; gv.valuables[0].size = SIZE(gg.gems); @@ -1035,10 +1206,12 @@ decl_globals_init(void) sfrestinfo = default_sfinfo; sfsaveinfo = default_sfinfo; - gs.subrooms = &gr.rooms[MAXNROFROOMS + 1]; + gs.subrooms = &svr.rooms[MAXNROFROOMS + 1]; ZERO(flags); ZERO(iflags); + ZERO(a11y); + ZERO(disp); ZERO(u); ZERO(ubirthday); ZERO(urealtime); @@ -1058,15 +1231,14 @@ decl_globals_init(void) } } -#ifndef NO_VERBOSE_GRANULARITY -long verbosity_suppressions[vb_elements] = { 0L, 0L, 0L, 0L, 0L, }; -#endif +/* fields in 'hands_obj' don't matter, just its distinct address */ +struct obj hands_obj = DUMMY; -/* gcc 12.2's static analyzer thinks that some fields of gc.context.victual +/* gcc 12.2's static analyzer thinks that some fields of svc.context.victual are uninitialized when compiling 'bite(eat.c)' but that's impossible; it is defined at global scope so guaranteed to be given implicit initialization for fields that aren't explicitly initialized (all of - 'context'); having bite() pass &gc.context.victual to this no-op + 'context'); having bite() pass &svc.context.victual to this no-op eliminates the analyzer's very verbose complaint */ void sa_victual( diff --git a/src/detect.c b/src/detect.c index 596c864936..46cba37347 100644 --- a/src/detect.c +++ b/src/detect.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 detect.c $NHDT-Date: 1613721262 2021/02/19 07:54:22 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.131 $ */ +/* NetHack 3.7 detect.c $NHDT-Date: 1721684299 2024/07/22 21:38:19 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.180 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -11,23 +11,35 @@ #include "hack.h" #include "artifact.h" -static boolean unconstrain_map(void); -static void reconstrain_map(void); -static void map_redisplay(void); -static void browse_map(int, const char *); -static void map_monst(struct monst *, boolean); -static void do_dknown_of(struct obj *); -static boolean check_map_spot(coordxy, coordxy, char, unsigned); -static boolean clear_stale_map(char, unsigned); -static void sense_trap(struct trap *, coordxy, coordxy, int); -static int detect_obj_traps(struct obj *, boolean, int); -static void display_trap_map(struct trap *, int); -static int furniture_detect(void); -static void show_map_spot(coordxy, coordxy); -static void findone(coordxy, coordxy, genericptr_t); -static void openone(coordxy, coordxy, genericptr_t); -static int mfind0(struct monst *, boolean); -static int reveal_terrain_getglyph(coordxy, coordxy, int, unsigned, int, int); +#ifndef FOUND_FLASH_COUNT +/* for screen alert shown to player when secret door detection or ^E + finds stuff; to use tmp_at() instead of flash_glyph_at(), define as 0; + extra code for tmp_at() will be included and the flash_glyph_at() + calls will execute but won't do anything */ +#define FOUND_FLASH_COUNT 6 +#endif + +struct found_things; + +staticfn boolean unconstrain_map(void); +staticfn void reconstrain_map(void); +staticfn void map_redisplay(void); +staticfn void browse_map(unsigned, const char *); +staticfn void map_monst(struct monst *, boolean); +staticfn void do_dknown_of(struct obj *); +staticfn boolean check_map_spot(coordxy, coordxy, char, unsigned); +staticfn boolean clear_stale_map(char, unsigned); +staticfn void sense_trap(struct trap *, coordxy, coordxy, int); +staticfn int detect_obj_traps(struct obj *, boolean, int, + struct found_things *) NO_NNARGS; +staticfn void display_trap_map(int); +staticfn int furniture_detect(void); +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 int reveal_terrain_getglyph(coordxy, coordxy, unsigned, int, + unsigned); /* dummytrap: used when detecting traps finds a door or chest trap; the couple of fields that matter are always re-initialized during use so @@ -37,6 +49,7 @@ static struct trap dummytrap; /* data for enhanced feedback from findone() */ struct found_things { + coord ft_cc; /* for passing extra info to detect_obj_traps() */ uchar num_sdoors; uchar num_scorrs; uchar num_traps; @@ -52,7 +65,7 @@ struct found_things { /* bring hero out from underwater or underground or being engulfed; return True iff any change occurred */ -static boolean +staticfn boolean unconstrain_map(void) { boolean res = u.uinwater || u.uburied || u.uswallow; @@ -67,7 +80,7 @@ unconstrain_map(void) } /* put hero back underwater or underground or engulfed */ -static void +staticfn void reconstrain_map(void) { /* if was in water and taken out, put back; bypass set_uinwater() */ @@ -76,7 +89,7 @@ reconstrain_map(void) u.uswallow = iflags.save_uswallow, iflags.save_uswallow = 0; } -static void +staticfn void map_redisplay(void) { reconstrain_map(); @@ -88,8 +101,8 @@ map_redisplay(void) } /* use getpos()'s 'autodescribe' to view whatever is currently shown on map */ -static void -browse_map(int ter_typ, const char *ter_explain) +staticfn void +browse_map(unsigned ter_typ, const char *ter_explain) { coord dummy_pos; /* don't care whether player actually picks a spot */ boolean save_autodescribe; @@ -104,17 +117,17 @@ browse_map(int ter_typ, const char *ter_explain) } /* extracted from monster_detection() so can be shared by do_vicinity_map() */ -static void +staticfn void map_monst(struct monst *mtmp, boolean showtail) { - if (def_monsyms[(int) mtmp->data->mlet].sym == ' ') - show_glyph(mtmp->mx, mtmp->my, - detected_mon_to_glyph(mtmp, newsym_rn2)); - else - show_glyph(mtmp->mx, mtmp->my, mtmp->mtame - ? pet_to_glyph(mtmp, newsym_rn2) - : mtmp->mpeaceful ? peaceful_to_glyph(mtmp, newsym_rn2) - : mon_to_glyph(mtmp, newsym_rn2)); + int glyph = (monsym(mtmp->data) == ' ') + ? detected_mon_to_glyph(mtmp, newsym_rn2) + : mtmp->mtame + ? pet_to_glyph(mtmp, newsym_rn2) + : mtmp->mpeaceful ? peaceful_to_glyph(mtmp, newsym_rn2) + : mon_to_glyph(mtmp, newsym_rn2); + + show_glyph(mtmp->mx, mtmp->my, glyph); if (showtail && mtmp->data == &mons[PM_LONG_WORM]) detect_wsegs(mtmp, 0); @@ -186,7 +199,7 @@ trapped_door_at(int ttyp, coordxy x, coordxy y) struct obj * o_in(struct obj *obj, char oclass) { - register struct obj *otmp; + struct obj *otmp; struct obj *temp; if (obj->oclass == oclass) @@ -214,7 +227,7 @@ o_in(struct obj *obj, char oclass) struct obj * o_material(struct obj *obj, unsigned material) { - register struct obj *otmp; + struct obj *otmp; struct obj *temp; if (obj->material == material) @@ -231,7 +244,7 @@ o_material(struct obj *obj, unsigned material) return (struct obj *) 0; } -static void +staticfn void do_dknown_of(struct obj *obj) { struct obj *otmp; @@ -244,24 +257,25 @@ do_dknown_of(struct obj *obj) } /* Check whether the location has an outdated object displayed on it. */ -static boolean +staticfn boolean check_map_spot(coordxy x, coordxy y, char oclass, unsigned material) { int glyph; - register struct obj *otmp; - register struct monst *mtmp; + struct obj *otmp; + struct monst *mtmp; glyph = glyph_at(x, y); if (glyph_is_object(glyph)) { /* there's some object shown here */ if (oclass == ALL_CLASSES) { - return !(gl.level.objects[x][y] /* stale if nothing here */ + return !(svl.level.objects[x][y] /* stale if nothing here */ || ((mtmp = m_at(x, y)) != 0 && mtmp->minvent)); } else { if (material && objects[glyph_to_obj(glyph)].oc_material == material) { /* object shown here is of interest because material matches */ - for (otmp = gl.level.objects[x][y]; otmp; otmp = otmp->nexthere) + for (otmp = svl.level.objects[x][y]; otmp; + otmp = otmp->nexthere) if (o_material(otmp, GOLD)) return FALSE; /* didn't find it; perhaps a monster is carrying it */ @@ -275,7 +289,8 @@ check_map_spot(coordxy x, coordxy y, char oclass, unsigned material) } if (oclass && objects[glyph_to_obj(glyph)].oc_class == oclass) { /* obj shown here is of interest because its class matches */ - for (otmp = gl.level.objects[x][y]; otmp; otmp = otmp->nexthere) + for (otmp = svl.level.objects[x][y]; otmp; + otmp = otmp->nexthere) if (o_in(otmp, oclass)) return FALSE; /* didn't find it; perhaps a monster is carrying it */ @@ -298,10 +313,10 @@ check_map_spot(coordxy x, coordxy y, char oclass, unsigned material) * reappear after the detection has completed. Return true if noticeable * change occurs. */ -static boolean +staticfn boolean clear_stale_map(char oclass, unsigned material) { - register coordxy zx, zy; + coordxy zx, zy; boolean change_made = FALSE; for (zx = 1; zx < COLNO; zx++) @@ -318,14 +333,14 @@ clear_stale_map(char oclass, unsigned material) int gold_detect(struct obj *sobj, boolean passive) { - register struct obj *obj; - register struct monst *mtmp; + struct obj *obj; + struct monst *mtmp; struct obj gold, *temp = 0; boolean stale, ugold = FALSE, steedgold = FALSE; int ter_typ = TER_DETECT | TER_OBJ; gk.known = stale = clear_stale_map(COIN_CLASS, - (unsigned) (sobj->blessed ? GOLD : 0)); + (unsigned) (sobj->blessed ? GOLD : 0)); /* look for gold carried by monsters (might be in a container) */ for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { @@ -369,26 +384,24 @@ gold_detect(struct obj *sobj, boolean passive) if (!gk.known) { /* no gold found on floor or monster's inventory. adjust message if you have gold in your inventory */ - if (sobj && !passive) { - char buf[BUFSZ]; + char buf[BUFSZ]; - if (gy.youmonst.data == &mons[PM_GOLD_GOLEM]) - Sprintf(buf, "You feel like a million %s!", currency(2L)); - else if (money_cnt(gi.invent) || hidden_gold(TRUE)) - Strcpy(buf, - "You feel worried about your future financial situation."); - else if (steedgold) - Sprintf(buf, "You feel interested in %s financial situation.", - s_suffix(x_monnam(u.usteed, - u.usteed->mtame ? ARTICLE_YOUR - : ARTICLE_THE, - (char *) 0, - SUPPRESS_SADDLE, FALSE))); - else - Strcpy(buf, "You feel materially poor."); + if (gy.youmonst.data == &mons[PM_GOLD_GOLEM]) + Sprintf(buf, "You feel like a million %s!", currency(2L)); + else if (money_cnt(gi.invent) || hidden_gold(TRUE)) + Strcpy(buf, + "You feel worried about your future financial situation."); + else if (steedgold) + Sprintf(buf, "You feel interested in %s financial situation.", + s_suffix(x_monnam(u.usteed, + u.usteed->mtame ? ARTICLE_YOUR + : ARTICLE_THE, + (char *) 0, + SUPPRESS_SADDLE, FALSE))); + else + Strcpy(buf, "You feel materially poor."); - strange_feeling(sobj, buf); - } + strange_feeling(sobj, buf); return 1; } /* only under me - no separate display required */ @@ -493,9 +506,9 @@ gold_detect(struct obj *sobj, boolean passive) int food_detect(struct obj *sobj) { - register struct obj *obj; - register struct monst *mtmp; - register int ct = 0, ctu = 0; + struct obj *obj; + struct monst *mtmp; + int ct = 0, ctu = 0; boolean confused = (Confusion || (sobj && sobj->cursed)), stale; char oclass = confused ? POTION_CLASS : FOOD_CLASS; const char *what = confused ? something : "food"; @@ -618,15 +631,15 @@ int object_detect(struct obj *detector, /* object doing the detecting */ int class) /* an object class, 0 for all */ { - register coordxy x, y; + coordxy x, y; char stuff[BUFSZ]; int is_cursed = (detector && detector->cursed); int do_dknown = (detector && (detector->oclass == POTION_CLASS || detector->oclass == SPBOOK_CLASS) && detector->blessed); int ct = 0, ctu = 0; - register struct obj *obj, *otmp = (struct obj *) 0; - register struct monst *mtmp; + struct obj *obj, *otmp = (struct obj *) 0; + struct monst *mtmp; int sym, boulder = 0, ter_typ = TER_DETECT | TER_OBJ; if (class < 0 || class >= MAXOCLASSES) { @@ -666,7 +679,7 @@ object_detect(struct obj *detector, /* object doing the detecting */ do_dknown_of(obj); } - for (obj = gl.level.buriedobjlist; obj; obj = obj->nobj) { + for (obj = svl.level.buriedobjlist; obj; obj = obj->nobj) { if (!class || o_in(obj, class)) { if (u_at(obj->ox, obj->oy)) ctu++; @@ -715,7 +728,7 @@ object_detect(struct obj *detector, /* object doing the detecting */ /* * Map all buried objects first. */ - for (obj = gl.level.buriedobjlist; obj; obj = obj->nobj) + for (obj = svl.level.buriedobjlist; obj; obj = obj->nobj) if (!class || (otmp = o_in(obj, class)) != 0) { if (class) { if (otmp != obj) { @@ -736,7 +749,7 @@ object_detect(struct obj *detector, /* object doing the detecting */ */ for (x = 1; x < COLNO; x++) for (y = 0; y < ROWNO; y++) - for (obj = gl.level.objects[x][y]; obj; obj = obj->nexthere) + for (obj = svl.level.objects[x][y]; obj; obj = obj->nexthere) if ((!class && !boulder) || (otmp = o_in(obj, class)) != 0 || (otmp = o_in(obj, boulder)) != 0) { if (class || boulder) { @@ -814,7 +827,7 @@ int monster_detect(struct obj *otmp, /* detecting object (if any) */ int mclass) /* monster class, 0 for all */ { - register struct monst *mtmp; + struct monst *mtmp; int mcnt = 0; /* Note: This used to just check fmon for a non-zero value @@ -877,7 +890,7 @@ monster_detect(struct obj *otmp, /* detecting object (if any) */ return 0; } -static void +staticfn void sense_trap(struct trap *trap, coordxy x, coordxy y, int src_cursed) { if (Hallucination || src_cursed) { @@ -919,15 +932,16 @@ sense_trap(struct trap *trap, coordxy x, coordxy y, int src_cursed) /* check a list of objects for chest traps; return 1 if found at , 2 if found at some other spot, 3 if both, 0 otherwise; optionally update the map to show where such traps were found */ -static int +staticfn int detect_obj_traps( struct obj *objlist, boolean show_them, - int how) /* 1 for misleading map feedback */ + int how, /* 1 for misleading map feedback */ + struct found_things *ft) /* being called by findone() when non-Null */ { struct obj *otmp; coordxy x, y; - int result = OTRAP_NONE; + int trapglyph, result = OTRAP_NONE; /* * TODO? Display locations of unarmed land mine and beartrap objects. @@ -935,49 +949,68 @@ detect_obj_traps( */ dummytrap.ttyp = TRAPPED_CHEST; + trapglyph = ft ? trap_to_glyph(&dummytrap) : GLYPH_NOTHING; for (otmp = objlist; otmp; otmp = otmp->nobj) { - if (Is_box(otmp) && otmp->otrapped - && get_obj_location(otmp, &x, &y, BURIED_TOO | CONTAINED_TOO)) { + x = y = 0; /* lint suppression */ + if ((Is_box(otmp) && otmp->otrapped) || Has_contents(otmp)) { + /* !get_obj_location and !isok should both be impossible here */ + if (!get_obj_location(otmp, &x, &y, BURIED_TOO | CONTAINED_TOO) + || !isok(x, y) + || (ft && (x != ft->ft_cc.x || y != ft->ft_cc.y))) + 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); + } if (show_them) { dummytrap.tx = x, dummytrap.ty = y; sense_trap(&dummytrap, x, y, how); } + if (ft) { + foundone(x, y, trapglyph); + ft->num_traps++; + } } if (Has_contents(otmp)) - result |= detect_obj_traps(otmp->cobj, show_them, how); + result |= detect_obj_traps(otmp->cobj, show_them, how, ft); } return result; } -static void -display_trap_map(struct trap *ttmp, int cursed_src) +staticfn void +display_trap_map(int cursed_src) { struct monst *mon; - int door, glyph, ter_typ = TER_DETECT | ( cursed_src ? TER_OBJ : TER_TRP ); + struct trap *ttmp; + int door, glyph, ter_typ = TER_DETECT | (cursed_src ? TER_OBJ : TER_TRP); coord cc; cls(); (void) unconstrain_map(); - /* show chest traps first, so that subsequent floor trap display - will override if both types are present at the same location */ - (void) detect_obj_traps(fobj, TRUE, cursed_src); - (void) detect_obj_traps(gl.level.buriedobjlist, TRUE, cursed_src); + /* show chest traps first, first buried chests then floor chests, so + that subsequent floor trap display will override if both types are + present at the same location */ + (void) detect_obj_traps(svl.level.buriedobjlist, TRUE, cursed_src, NULL); + (void) detect_obj_traps(fobj, TRUE, cursed_src, NULL); for (mon = fmon; mon; mon = mon->nmon) { if (DEADMONSTER(mon) || (mon->isgd && !mon->mx)) continue; - (void) detect_obj_traps(mon->minvent, TRUE, cursed_src); + (void) detect_obj_traps(mon->minvent, TRUE, cursed_src, NULL); } - (void) detect_obj_traps(gi.invent, TRUE, cursed_src); + (void) detect_obj_traps(gi.invent, TRUE, cursed_src, NULL); for (ttmp = gf.ftrap; ttmp; ttmp = ttmp->ntrap) sense_trap(ttmp, 0, 0, cursed_src); dummytrap.ttyp = TRAPPED_DOOR; for (door = 0; door < gd.doorindex; door++) { - cc = gd.doors[door]; - if (levl[cc.x][cc.y].typ == SDOOR) /* see above */ + cc = svd.doors[door]; + if (levl[cc.x][cc.y].typ == SDOOR) /* can't be trapped; see above */ continue; if (door_is_trapped(&levl[cc.x][cc.y])) { dummytrap.tx = cc.x, dummytrap.ty = cc.y; @@ -1004,10 +1037,10 @@ display_trap_map(struct trap *ttmp, int cursed_src) * returns 0 if something was detected */ int -trap_detect(struct obj *sobj) /* null if crystal ball, - *scroll if gold detection scroll */ +trap_detect( + struct obj *sobj) /* Null if crystal ball, scroll if gold detection */ { - register struct trap *ttmp; + struct trap *ttmp; struct monst *mon; int door, tr; int cursed_src = sobj && sobj->cursed; @@ -1020,43 +1053,44 @@ trap_detect(struct obj *sobj) /* null if crystal ball, /* floor/ceiling traps */ for (ttmp = gf.ftrap; ttmp; ttmp = ttmp->ntrap) { if (ttmp->tx != u.ux || ttmp->ty != u.uy) { - display_trap_map(ttmp, cursed_src); + display_trap_map(cursed_src); return 0; - } else - found = TRUE; + } + found = TRUE; } /* chest traps (might be buried or carried) */ - if ((tr = detect_obj_traps(fobj, FALSE, 0)) != OTRAP_NONE) { + if ((tr = detect_obj_traps(fobj, FALSE, 0, NULL)) != OTRAP_NONE) { if (tr & OTRAP_THERE) { - display_trap_map(ttmp, cursed_src); + display_trap_map(cursed_src); return 0; - } else - found = TRUE; + } + found = TRUE; } - if ((tr = detect_obj_traps(gl.level.buriedobjlist, FALSE, 0)) + if ((tr = detect_obj_traps(svl.level.buriedobjlist, FALSE, 0, NULL)) != OTRAP_NONE) { if (tr & OTRAP_THERE) { - display_trap_map(ttmp, cursed_src); + display_trap_map(cursed_src); return 0; - } else - found = TRUE; + } + found = TRUE; } for (mon = fmon; mon; mon = mon->nmon) { if (DEADMONSTER(mon) || (mon->isgd && !mon->mx)) continue; - if ((tr = detect_obj_traps(mon->minvent, FALSE, 0)) != OTRAP_NONE) { + if ((tr = detect_obj_traps(mon->minvent, FALSE, 0, NULL)) + != OTRAP_NONE) { if (tr & OTRAP_THERE) { - display_trap_map(ttmp, cursed_src); + display_trap_map(cursed_src); return 0; - } else - found = TRUE; + } + found = TRUE; } } - if (detect_obj_traps(gi.invent, FALSE, 0) != OTRAP_NONE) + if (detect_obj_traps(gi.invent, FALSE, 0, NULL) != OTRAP_NONE) found = TRUE; /* door traps */ for (door = 0; door < gd.doorindex; door++) { - cc = gd.doors[door]; + cc = svd.doors[door]; /* levl[][].doormask and .wall_info both overlay levl[][].flags; the bit in doormask for D_TRAPPED is also a bit in wall_info; secret doors use wall_info so can't be marked as trapped */ @@ -1064,10 +1098,10 @@ trap_detect(struct obj *sobj) /* null if crystal ball, continue; if (door_is_trapped(&levl[cc.x][cc.y])) { if (cc.x != u.ux || cc.y != u.uy) { - display_trap_map(ttmp, cursed_src); + display_trap_map(cursed_src); return 0; - } else - found = TRUE; + } + found = TRUE; } } if (!found) { @@ -1082,7 +1116,7 @@ trap_detect(struct obj *sobj) /* null if crystal ball, return 0; } -static int +staticfn int furniture_detect(void) { struct monst *mon; @@ -1132,7 +1166,7 @@ furniture_detect(void) int look_in_crystal_ball(void) { - struct obj* ball = gc.context.crystal.ball; + struct obj* ball = svc.context.crystal.ball; /* Itlachiayaque also uses this routine, but is not a crystal ball... */ boolean actualball = (ball->otyp == CRYSTAL_BALL); @@ -1141,11 +1175,11 @@ look_in_crystal_ball(void) return 0; } - if (--gc.context.crystal.looktime > 0) + if (--svc.context.crystal.looktime > 0) return 1; /* still looking */ - gc.context.crystal.ball = (struct obj *) 0; - gc.context.crystal.o_id = 0; + svc.context.crystal.ball = (struct obj *) 0; + svc.context.crystal.o_id = 0; /* was ball stolen somehow? */ if (!carried(ball)) @@ -1163,13 +1197,13 @@ look_in_crystal_ball(void) int class; int ret = -1; - if (Verbose(0, use_crystal_ball1)) + if (flags.verbose) You("may look for an object, monster, or special map symbol."); do { /* read a single character */ ch = yn_function("What do you look for?", (char *) 0, '\0', TRUE); if (strchr(quitchars, ch)) { - if (Verbose(0, use_crystal_ball2)) + if (flags.verbose) pline1(Never_mind); return 0; } @@ -1183,8 +1217,8 @@ look_in_crystal_ball(void) * for help in using the crystal ball. */ - /* checking furnture before objects allows '_' to find altars - (along with other furniture) instead of finding iron chains */ + /* checking furniture before objects allows '_' to find altars + (along with other furniture) instead of finding iron chains */ if (def_char_is_furniture(ch) >= 0) { ret = furniture_detect(); } else if ((class = def_char_to_objclass(ch)) != MAXOCLASSES) { @@ -1312,22 +1346,23 @@ use_crystal_ball(struct obj **optr) * uncharged, it will still take several turns of peering into it to get a * null result. */ You("gaze into %s...", the(xname(obj))); - gc.context.crystal.ball = obj; - gc.context.crystal.o_id = obj->o_id; - gc.context.crystal.looktime = rn1(5, 6); + svc.context.crystal.ball = obj; + svc.context.crystal.o_id = obj->o_id; + svc.context.crystal.looktime = rn1(5, 6); set_occupation(look_in_crystal_ball, "gazing", 0); return; } -static void -show_map_spot(coordxy x, coordxy y) +/* used by magic mapping, clairvoyance, and wand of probing */ +void +show_map_spot(coordxy x, coordxy y, boolean cnf) { struct rm *lev; struct trap *t; struct engr *ep; int oldglyph; - if (Confusion && rn2(7)) + if (cnf && rn2(7)) return; lev = &levl[x][y]; @@ -1347,7 +1382,7 @@ show_map_spot(coordxy x, coordxy y) * opposite to how normal vision behaves. */ oldglyph = glyph_at(x, y); - if (gl.level.flags.hero_memory) { + if (svl.level.flags.hero_memory) { magic_map_background(x, y, 0); newsym(x, y); /* show it, if not blocked */ } else { @@ -1356,28 +1391,31 @@ show_map_spot(coordxy x, coordxy y) 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); - if (gl.level.flags.hero_memory) + if (svl.level.flags.hero_memory) lev->glyph = oldglyph; } } + /* possibly update #overview */ + if (!cnf && lev->roomno >= ROOMOFFSET) + room_discovered(lev->roomno - ROOMOFFSET); } void do_mapping(void) { - register int zx, zy; + int zx, zy; boolean unconstrained; unconstrained = unconstrain_map(); for (zx = 1; zx < COLNO; zx++) for (zy = 0; zy < ROWNO; zy++) - show_map_spot(zx, zy); + show_map_spot(zx, zy, Confusion); - if (!gl.level.flags.hero_memory || unconstrained) { + if (!svl.level.flags.hero_memory || unconstrained) { flush_screen(1); /* flush temp screen */ /* browse_map() instead of display_nhwindow(WIN_MAP, TRUE) */ browse_map(TER_DETECT | TER_MAP | TER_TRP | TER_OBJ, @@ -1393,9 +1431,10 @@ do_mapping(void) /* clairvoyance */ void -do_vicinity_map(struct obj *sobj) /* scroll--actually fake spellbook--object */ +do_vicinity_map( + struct obj *sobj) /* scroll--actually fake spellbook--object */ { - register int zx, zy; + int zx, zy; struct monst *mtmp; struct obj *otmp; long save_EDetect_mons; @@ -1438,7 +1477,7 @@ do_vicinity_map(struct obj *sobj) /* scroll--actually fake spellbook--object */ /* if hero is engulfed, show engulfer at */ save_viz_uyux = gv.viz_array[u.uy][u.ux]; if (u.uswallow) - gv.viz_array[u.uy][u.ux] |= IN_SIGHT; /* are reversed to [y][x] */ + gv.viz_array[u.uy][u.ux] |= IN_SIGHT; /* are reversed, [y][x] */ save_EDetect_mons = EDetect_monsters; /* for skilled spell, getpos() scanning of the map will display all monsters within range; otherwise, "unseen creature" will be shown */ @@ -1448,12 +1487,12 @@ do_vicinity_map(struct obj *sobj) /* scroll--actually fake spellbook--object */ for (zy = lo_y; zy <= hi_y; zy++) { oldglyph = glyph_at(zx, zy); /* this will remove 'remembered, unseen mon' (and objects) */ - show_map_spot(zx, zy); + show_map_spot(zx, zy, Confusion); /* if there are any objects here, see the top one */ if (OBJ_AT(zx, zy)) { /* not vobj_at(); this is not vision-based access; unlike object detection, we don't notice buried items */ - otmp = gl.level.objects[zx][zy]; + otmp = svl.level.objects[zx][zy]; if (extended) otmp->dknown = 1; map_object(otmp, TRUE); @@ -1470,7 +1509,7 @@ do_vicinity_map(struct obj *sobj) /* scroll--actually fake spellbook--object */ the map and we're not doing extended/blessed clairvoyance (hence must be swallowed or underwater), show "unseen creature" unless map already displayed a monster here */ - if ((unconstrained || !gl.level.flags.hero_memory) + if ((unconstrained || !svl.level.flags.hero_memory) && !extended && (zx != u.ux || zy != u.uy) && !glyph_is_monster(oldglyph)) map_invisible(zx, zy); @@ -1493,7 +1532,7 @@ do_vicinity_map(struct obj *sobj) /* scroll--actually fake spellbook--object */ if (random_farsight && flags.quick_farsight) mdetected = odetected = FALSE; - if (!gl.level.flags.hero_memory || unconstrained + if (!svl.level.flags.hero_memory || unconstrained || mdetected || odetected) { flush_screen(1); /* flush temp screen */ /* the getpos() prompt from browse_map() is only shown when @@ -1548,58 +1587,115 @@ cvt_sdoor_to_door(struct rm *lev) set_door_lock(lev, rn2(2)); } -/* find something at one location; it should find all somethings there +/* update the map for something which has just been found by wand of secret + door detection or wizard mode ^E; will be called multiple times during a + single operation if multiple things of interest are discovered */ +staticfn void +foundone(coordxy zx, coordxy zy, int glyph) +{ + if (glyph_is_cmap(glyph) || glyph_is_unexplored(glyph)) + levl[zx][zy].seenv = SVALL; + + { + seenV save_viz = gv.viz_array[zy][zx]; + + if (!Blind) + gv.viz_array[zy][zx] = COULD_SEE | IN_SIGHT; + newsym(zx, zy); + gv.viz_array[zy][zx] = save_viz; + } + +#if FOUND_FLASH_COUNT == 0 + /* + * This works [for non-monsters at present] but flash_glyph_at() + * seems preferrable because the tmp_at() variation requires that + * the player respond to --More-- at the end, the flash_glyph + * variation doesn't. + */ + tmp_at(DISP_CHANGE, glyph); + tmp_at(zx, zy); +#endif +} + +/* find something at one location; this should find all somethings there since it is used for magical detection rather than physical searching */ -static void +staticfn void findone(coordxy zx, coordxy zy, genericptr_t whatfound) { - struct trap *ttmp; - struct monst *mtmp; + struct rm *lev = &levl[zx][zy]; + struct trap *ttmp = t_at(zx, zy); + struct monst *mtmp = m_at(zx, zy); struct found_things *found_p = (struct found_things *) whatfound; - /* - * This used to use if/else-if/else-if/else/end-if but that only - * found the first hidden thing at the location. Two hidden things - * at the same spot is uncommon, but it's possible for an undetected - * monster to be hiding at the location of an unseen trap. - */ + if (mtmp && (DEADMONSTER(mtmp) || (mtmp->isgd && !mtmp->mx))) + mtmp = (struct monst *) NULL; + found_p->ft_cc.x = zx; /* needed by detect_obj_traps() */ + found_p->ft_cc.y = zy; + + if (lev->typ == SDOOR) { + nhsym sym = lev->horizontal ? S_hcdoor : S_vcdoor; - if (levl[zx][zy].typ == SDOOR) { - cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */ + 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); - newsym(zx, zy); + foundone(zx, zy, back_to_glyph(zx, zy)); found_p->num_sdoors++; - } else if (levl[zx][zy].typ == SCORR) { - levl[zx][zy].typ = CORR; + } else if (lev->typ == SCORR) { + flash_glyph_at(zx, zy, cmap_to_glyph(S_corr), FOUND_FLASH_COUNT); + lev->typ = CORR; unblock_point(zx, zy); magic_map_background(zx, zy, 0); - newsym(zx, zy); + foundone(zx, zy, cmap_to_glyph(S_corr)); found_p->num_scorrs++; } - if ((ttmp = t_at(zx, zy)) != 0 && !ttmp->tseen + if (ttmp && !ttmp->tseen /* [shouldn't successful 'find' reveal and activate statue traps?] */ && ttmp->ttyp != STATUE_TRAP) { + flash_glyph_at(zx, zy, trap_to_glyph(ttmp), FOUND_FLASH_COUNT); ttmp->tseen = 1; - newsym(zx, zy); + sense_trap(ttmp, zx, zy, 0); /* handles Hallucination */ + foundone(zx, zy, trap_to_glyph(ttmp)); found_p->num_traps++; } + if (closed_door(zx, zy) && (lev->doormask & D_TRAPPED) != 0) { + dummytrap.ttyp = TRAPPED_DOOR; + dummytrap.tx = zx, dummytrap.ty = zy; + flash_glyph_at(zx, zy, trap_to_glyph(&dummytrap), FOUND_FLASH_COUNT); + dummytrap.tseen = 1; + sense_trap(&dummytrap, zx, zy, 0); /* handles Hallucination */ + foundone(zx, zy, trap_to_glyph(&dummytrap)); + found_p->num_traps++; + } + /* trapped chests */ + (void) detect_obj_traps(svl.level.buriedobjlist, TRUE, 0, found_p); + (void) detect_obj_traps(fobj, TRUE, 0, found_p); + if (mtmp) + (void) detect_obj_traps(mtmp->minvent, TRUE, 0, found_p); + if (u_at(zx, zy)) + (void) detect_obj_traps(gi.invent, TRUE, 0, found_p); - if ((mtmp = m_at(zx, zy)) != 0 - /* brings hidden monster out of hiding even if already sensed */ - && (!canspotmon(mtmp) || mtmp->mundetected || M_AP_TYPE(mtmp))) { + if (mtmp && (!canspotmon(mtmp) || mtmp->mundetected || M_AP_TYPE(mtmp))) { if (M_AP_TYPE(mtmp)) { + flash_glyph_at(zx, zy, mon_to_glyph(mtmp, rn2_on_display_rng), + FOUND_FLASH_COUNT); seemimic(mtmp); + /*foundone(zx, zy, mon_to_glyph(mtmp, rn2_on_display_rng);*/ found_p->num_mons++; } else if (mtmp->mundetected && (is_hider(mtmp->data) || hides_under(mtmp->data) || mtmp->data->mlet == S_EEL)) { + flash_glyph_at(zx, zy, mon_to_glyph(mtmp, rn2_on_display_rng), + FOUND_FLASH_COUNT); mtmp->mundetected = 0; + /*foundone(zx, zy, mon_to_glyph(mtmp, rn2_on_display_rng);*/ newsym(zx, zy); found_p->num_mons++; } - if (!glyph_is_invisible(levl[zx][zy].glyph)) { + if (!glyph_is_invisible(lev->glyph)) { if (!canspotmon(mtmp)) { + flash_glyph_at(zx, zy, GLYPH_INVISIBLE, FOUND_FLASH_COUNT); map_invisible(zx, zy); found_p->num_invis++; } @@ -1607,19 +1703,21 @@ findone(coordxy zx, coordxy zy, genericptr_t whatfound) found_p->num_kept_invis++; } } else if (unmap_invisible(zx, zy)) { + /* flash the invisible monster glyph because it is already gone */ + flash_glyph_at(zx, zy, GLYPH_INVISIBLE, FOUND_FLASH_COUNT); found_p->num_cleared_invis++; } } -static void +staticfn void openone(coordxy zx, coordxy zy, genericptr_t num) { - register struct trap *ttmp; - register struct obj *otmp; + struct trap *ttmp; + struct obj *otmp; int *num_p = (int *) num; if (OBJ_AT(zx, zy)) { - for (otmp = gl.level.objects[zx][zy]; otmp; otmp = otmp->nexthere) { + for (otmp = svl.level.objects[zx][zy]; otmp; otmp = otmp->nexthere) { if (Is_box(otmp) && otmp->olocked) { otmp->olocked = 0; (*num_p)++; @@ -1681,9 +1779,22 @@ findit(void) char buf[BUFSZ]; struct found_things found; + /* + * findit() -> do_clear_area(findone) -> findone() -> foundone() + * is used to notify player where various things have been found. + * Changing FOUND_FLASH_COUNT to 0 will switch to tmp_at() to + * highlight all discoveries for the current operation, but requires + * player to respond to --More-- when done. Neither allows browsing + * the map via getpos() autodescribe (until after it has reverted to + * normal display, where found traps might be covered by objects). + */ + if (u.uswallow) return 0; +#if FOUND_FLASH_COUNT == 0 /* _COUNT > 0 doesn't need to init tmp_at() */ + tmp_at(DISP_ALL, GLYPH_NOTHING); +#endif (void) memset((genericptr_t) &found, 0, sizeof found); do_clear_area(u.ux, u.uy, BOLT_LIM, findone, (genericptr_t) &found); /* count that controls "reveal" punctuation; 0..4 */ @@ -1719,6 +1830,12 @@ findit(void) Strcat(buf, "a trap"); num += found.num_traps; } + +#if FOUND_FLASH_COUNT == 0 + int tmp_num; + tmp_num = num; /* sdoors, scorrs, and traps call tmp_at() */ +#endif + if (found.num_mons) { if (*buf) Strcat(buf, (k > 2) ? ", and " : " and "); @@ -1733,10 +1850,10 @@ findit(void) if (found.num_invis) { if (found.num_invis > 1) - Sprintf(buf, "%d%s invisible monsters", found.num_invis, + Sprintf(buf, "%d%s unseen monsters", found.num_invis, found.num_kept_invis ? " other" : ""); else - Sprintf(buf, "%s invisible monster", + Sprintf(buf, "%s unseen monster", found.num_kept_invis ? "another" : "an"); You("detect %s!", buf); num += found.num_invis; @@ -1751,6 +1868,16 @@ findit(void) } /* note: num_kept_invis is not included in the final result */ + if (!num) + You("don't find anything."); +#if FOUND_FLASH_COUNT == 0 + else if (tmp_num) { + flush_screen(1); + display_nhwindow(WIN_MAP, TRUE); + } + tmp_at(DISP_END, GLYPH_NOTHING); /* note: outside of 'if (tmp_num) { }' */ +#endif + return num; } @@ -1809,6 +1936,7 @@ find_trap(struct trap *trap) cleared = TRUE; } + set_msg_xy(trap->tx, trap->ty); You("find %s.", an(trapname(trap->ttyp, FALSE))); if (cleared) { @@ -1817,7 +1945,7 @@ find_trap(struct trap *trap) } } -static int +staticfn int mfind0(struct monst *mtmp, boolean via_warning) { coordxy x = mtmp->mx, y = mtmp->my; @@ -1837,6 +1965,7 @@ mfind0(struct monst *mtmp, boolean via_warning) || hides_under(mtmp->data) || mtmp->data->mlet == S_EEL)) { if (via_warning && found_something) { + set_msg_xy(x, y); Your("danger sense causes you to take a second %s.", Blind ? "to check nearby" : "look close by"); display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */ @@ -1856,8 +1985,10 @@ mfind0(struct monst *mtmp, boolean via_warning) exercise(A_WIS, TRUE); if (!canspotmon(mtmp)) { map_invisible(x, y); + set_msg_xy(x, y); You_feel("an unseen monster!"); } else if (!sensemon(mtmp)) { + set_msg_xy(x, y); You("find %s.", mtmp->mtame ? y_monnam(mtmp) : a_monnam(mtmp)); } return 1; @@ -1869,8 +2000,8 @@ int dosearch0(int aflag) /* intrinsic autosearch vs explicit searching */ { coordxy x, y; - register struct trap *trap; - register struct monst *mtmp; + struct trap *trap; + struct monst *mtmp; if (u.uswallow) { if (!aflag) @@ -1890,15 +2021,17 @@ dosearch0(int aflag) /* intrinsic autosearch vs explicit searching */ if (u_at(x, y)) continue; - if (Blind && !aflag) + if (!aflag && (Blind || visible_region_at(x, y))) feel_location(x, y); if (levl[x][y].typ == SDOOR) { if (rn2(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 */ + set_msg_xy(x, y); You("find a hidden door."); } else if (levl[x][y].typ == SCORR) { if (rn2(7 - fund)) @@ -1908,6 +2041,7 @@ dosearch0(int aflag) /* intrinsic autosearch vs explicit searching */ exercise(A_WIS, TRUE); nomul(0); feel_newsym(x, y); /* make sure it shows up */ + set_msg_xy(x, y); You("find a hidden passage."); } else { /* Be careful not to find anything in an SCORR or SDOOR */ @@ -1969,13 +2103,13 @@ warnreveal(void) } } -/* Pre-map the sokoban levels */ +/* Pre-map (the sokoban) levels */ void -sokoban_detect(void) +premap_detect(void) { - register coordxy x, y; - register struct trap *ttmp; - register struct obj *obj; + coordxy x, y; + struct trap *ttmp; + struct obj *obj; /* Map the background and boulders */ for (x = 1; x < COLNO; x++) @@ -1993,38 +2127,48 @@ sokoban_detect(void) for (ttmp = gf.ftrap; ttmp; ttmp = ttmp->ntrap) { ttmp->tseen = 1; map_trap(ttmp, 1); - /* set sokoban_rules when there is at least one pit or hole */ - if (ttmp->ttyp == PIT || ttmp->ttyp == HOLE) - Sokoban = 1; } } -static int -reveal_terrain_getglyph(coordxy x, coordxy y, int full, unsigned swallowed, - int default_glyph, int which_subset) +/* used to see under visible gas/cloud regions; caller must declare cmaptmp */ +#define glyph_is_gascloud(glyph) \ + (glyph_is_cmap(glyph) && ((cmaptmp = glyph_to_cmap(glyph)) == S_cloud \ + || cmaptmp == S_poisoncloud)) + +staticfn int +reveal_terrain_getglyph( + coordxy x, coordxy y, + unsigned swallowed, + int default_glyph, + unsigned which_subset) { + struct trap *t; + struct monst *mtmp; int glyph, levl_glyph; uchar seenv; - boolean keep_traps = (which_subset & TER_TRP) !=0, + boolean keep_traps = (which_subset & TER_TRP) != 0, keep_objs = (which_subset & TER_OBJ) != 0, keep_mons = (which_subset & TER_MON) != 0, - do_visited = (which_subset & TER_VISIT) != 0; - struct monst *mtmp; - struct trap *t; + do_visited = (which_subset & TER_VISIT) != 0, + full = (which_subset & TER_FULL) != 0; /* for 'full', show the actual terrain for the entire level, otherwise what the hero remembers for seen locations with monsters, objects, and/or traps removed as caller dictates */ - seenv = (full || gl.level.flags.hero_memory) + seenv = (full || svl.level.flags.hero_memory) ? levl[x][y].seenv : cansee(x, y) ? SVALL : 0; if (full) { levl[x][y].seenv = SVALL; glyph = back_to_glyph(x, y); levl[x][y].seenv = seenv; } else { - levl_glyph = gl.level.flags.hero_memory - ? levl[x][y].glyph - : seenv ? back_to_glyph(x, y): default_glyph; + int cmaptmp = 0; /* used by glyph_is_gascloud() macro */ + NhRegion *reg = visible_region_at(x, y); + boolean was_mon = FALSE; + + levl_glyph = svl.level.flags.hero_memory ? levl[x][y].glyph + : seenv ? back_to_glyph(x, y) + : default_glyph; /* glyph_at() returns the displayed glyph, which might be a monster. levl[][].glyph contains the remembered glyph, which will never be a monster (unless it is @@ -2036,24 +2180,45 @@ reveal_terrain_getglyph(coordxy x, coordxy y, int full, unsigned swallowed, /* +2 = use orange magic platform (+0 would be "default") */ glyph = cmap_to_glyph(S_magicplatform) + 2; - if (keep_mons && u_at(x, y) && swallowed) + if (keep_mons && u_at(x, y) && swallowed) { glyph = mon_to_glyph(u.ustuck, rn2_on_display_rng); - else if (((glyph_is_monster(glyph) - || glyph_is_warning(glyph)) && !keep_mons) - || glyph_is_swallow(glyph)) + } else if ((!keep_mons && (glyph_is_monster(glyph) + || glyph_is_warning(glyph))) + || glyph_is_swallow(glyph)) { glyph = levl_glyph; - if (((glyph_is_object(glyph) && !keep_objs) + was_mon = TRUE; + } + if (((!keep_objs && glyph_is_object(glyph)) || glyph_is_invisible(glyph)) && keep_traps && !covers_traps(x, y)) { if ((t = t_at(x, y)) != 0 && t->tseen) glyph = trap_to_glyph(t); } - if ((glyph_is_object(glyph) && !keep_objs) - || (glyph_is_trap(glyph) && !keep_traps) + if ((!keep_objs && glyph_is_object(glyph)) + /* we either show both traps and visible regions (trap if both + are present at the same spot) or neither traps nor regions */ + || (!keep_traps && (glyph_is_trap(glyph) + || (reg && glyph_is_gascloud(glyph)))) + || (reg && was_mon) || glyph_is_invisible(glyph)) { if (!seenv) { - glyph = default_glyph; - } else if (gl.lastseentyp[x][y] == levl[x][y].typ) { + /* it's possible to have a visible region shown at an + otherwise unexplored location (cast stinking cloud + through unexplored corridor into lit room, then approach + far enough to be adjacent to the cloud without having + seen the corridor underneath it) */ + glyph = !reg ? default_glyph : GLYPH_UNEXPLORED; + } else if (keep_traps && reg + && (glyph_is_gascloud(glyph) || was_mon)) { + t = t_at(x, y); + /* we need reg->glyph here when there's a monster shown + at a region spot; the region glyph isn't the remembered + background glyph or the current glyph */ + glyph = (t && t->tseen) ? trap_to_glyph(t) : reg->glyph; + /* FIXME? what about objects temporarily hidden by regions? + when objects are being shown, shouldn't showing them take + precedence over showing the region, just like traps? */ + } else if (svl.lastseentyp[x][y] == levl[x][y].typ) { glyph = back_to_glyph(x, y); } else { /* look for a mimic here posing as furniture; @@ -2077,8 +2242,8 @@ reveal_terrain_getglyph(coordxy x, coordxy y, int full, unsigned swallowed, * doormask==D_OPEN for an open door remembered as a wall. */ save_spot = levl[x][y]; - levl[x][y].typ = gl.lastseentyp[x][y]; - if (IS_WALL(levl[x][y].typ)) + levl[x][y].typ = svl.lastseentyp[x][y]; + if (IS_WALL(levl[x][y].typ) || levl[x][y].typ == SDOOR) xy_set_wall_state(x, y); /* levl[x][y].wall_info */ glyph = back_to_glyph(x, y); levl[x][y] = save_spot; @@ -2094,20 +2259,24 @@ reveal_terrain_getglyph(coordxy x, coordxy y, int full, unsigned swallowed, return glyph; } +#undef glyph_is_gascloud + #if defined(DUMPLOG) || defined(DUMPHTML) void dump_map(void) { + char buf[COLBUFSZ]; coordxy x, y; int glyph, skippedrows, lastnonblank; - int subset = TER_MAP | TER_TRP | TER_OBJ | TER_MON; - int default_glyph = cmap_to_glyph(gl.level.flags.arboreal ? S_tree - : S_stone); - char buf[COLBUFSZ]; boolean blankrow, toprow; + unsigned subset = TER_MAP | TER_TRP | TER_OBJ | TER_MON; + /* cmap_to_glyph() evaluates its argument multiple times, so pull the + tree vs stone conditional out of it */ + nhsym default_sym = svl.level.flags.arboreal ? S_tree : S_stone; + int default_glyph = cmap_to_glyph(default_sym); /* - * Squeeze out excess vertial space when dumping the map. + * Squeeze out excess vertical space when dumping the map. * If there are any blank map rows at the top, suppress them * (our caller has already printed a separator). If there is * more than one blank map row at the bottom, keep just one. @@ -2122,7 +2291,7 @@ dump_map(void) int ch; glyph_info glyphinfo; - glyph = reveal_terrain_getglyph(x, y, FALSE, u.uswallow, + glyph = reveal_terrain_getglyph(x, y, u.uswallow, default_glyph, subset); map_glyphinfo(x, y, glyph, 0, &glyphinfo); ch = glyphinfo.ttychar; @@ -2157,11 +2326,11 @@ dump_map(void) #endif /* DUMPLOG || DUMPHTML */ /* idea from crawl; show known portion of map without any monsters, - objects, or traps occluding the view of the underlying terrain */ + objects, or traps occluding the view of the underlying terrain; + in explore or wizard modes, can also display unexplored portion */ void reveal_terrain( - int full, /* wizard|explore modes allow player to request full map */ - int which_subset) /* if not full, whether to suppress objs and/or traps */ + unsigned which_subset) /* TER_TRP | TER_OBJ | TER_MON | TER_FULL */ { coordxy x, y; int glyph, default_glyph; @@ -2172,14 +2341,17 @@ reveal_terrain( keep_mons = (which_subset & TER_MON) != 0, /* not used */ do_visited = (which_subset & TER_VISIT) != 0; unsigned swallowed = u.uswallow; /* before unconstrain_map() */ + nhsym default_sym = svl.level.flags.arboreal ? S_tree : S_stone; + /* 'full' implies no-traps, no-objs, no-mons */ + boolean full = (which_subset & TER_FULL) != 0; /* show whole map */ if (unconstrain_map()) docrt(); - default_glyph = cmap_to_glyph(gl.level.flags.arboreal ? S_tree : S_stone); + default_glyph = cmap_to_glyph(default_sym); for (x = 1; x < COLNO; x++) for (y = 0; y < ROWNO; y++) { - glyph = reveal_terrain_getglyph(x,y, full, swallowed, + glyph = reveal_terrain_getglyph(x, y, swallowed, default_glyph, which_subset); show_glyph(x, y, glyph); } @@ -2229,4 +2401,6 @@ passive_gold_detect(void) } } +#undef FOUND_FLASH_COUNT + /*detect.c*/ diff --git a/src/dig.c b/src/dig.c index 9385b31824..94b063642f 100644 --- a/src/dig.c +++ b/src/dig.c @@ -1,19 +1,20 @@ -/* NetHack 3.7 dig.c $NHDT-Date: 1596498156 2020/08/03 23:42:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.142 $ */ +/* NetHack 3.7 dig.c $NHDT-Date: 1736530208 2025/01/10 09:30:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.225 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2012. */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" -static boolean rm_waslit(void); -static void mkcavepos(coordxy, coordxy, int, boolean, boolean); -static void mkcavearea(boolean); -static int dig(void); -static void dig_up_grave(coord *); -static boolean watchman_canseeu(struct monst *); -static int adj_pit_checks(coord *, char *); -static void pit_flow(struct trap *, schar); -static boolean furniture_handled(coordxy, coordxy, boolean); +staticfn boolean rm_waslit(void); +staticfn void mkcavepos(coordxy, coordxy, int, boolean, boolean); +staticfn void mkcavearea(boolean); +staticfn boolean pick_can_reach(struct obj *, coordxy, coordxy) NONNULLARG1; +staticfn int dig(void); +staticfn void dig_up_grave(coord *); +staticfn boolean watchman_canseeu(struct monst *) NONNULLARG1; +staticfn int adj_pit_checks(coord *, char *) NONNULLARG2; +staticfn void pit_flow(struct trap *, schar); +staticfn boolean furniture_handled(coordxy, coordxy, boolean); /* Indices returned by dig_typ() */ enum dig_types { @@ -25,10 +26,10 @@ enum dig_types { DIGTYP_TREE }; -static boolean +staticfn boolean rm_waslit(void) { - register coordxy x, y; + coordxy x, y; if (levl[u.ux][u.uy].typ == ROOM && levl[u.ux][u.uy].waslit) return TRUE; @@ -43,19 +44,19 @@ rm_waslit(void) * boulders in the name of a nice effect. Vision will get fixed up again * immediately after the effect is complete. */ -static void +staticfn void mkcavepos(coordxy x, coordxy y, int dist, boolean waslit, boolean rockit) { - register struct rm *lev; + struct rm *lev; if (!isok(x, y)) return; lev = &levl[x][y]; if (rockit) { - register struct monst *mtmp; + 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 */ @@ -83,14 +84,14 @@ mkcavepos(coordxy x, coordxy y, int dist, boolean waslit, boolean rockit) feel_newsym(x, y); } -static void +staticfn void mkcavearea(boolean rockit) { int dist; coordxy xmin = u.ux, xmax = u.ux; coordxy ymin = u.uy, ymax = u.uy; - register coordxy i; - register boolean waslit = rm_waslit(); + coordxy i; + boolean waslit = rm_waslit(); if (rockit) { Soundeffect(se_crashing_rock, 100); @@ -135,6 +136,34 @@ mkcavearea(boolean rockit) gv.vision_full_recalc = 1; /* everything changed */ } +/* called when attempting to break a statue or boulder with a pick */ +staticfn boolean +pick_can_reach(struct obj *pick, coordxy x, coordxy y) +{ + struct trap *t = t_at(x, y); + /* tseen: pit only affects item positioning when it is known */ + boolean target_in_pit = t && is_pit(t->ttyp) && t->tseen; + + /* if hero is in a pit, pick can only reach if the statue is too and + the two pits are conjoined or the statue isn't and pick is two-handed; + this applies to hero in pit trying to reach an adjcacent boulder too */ + if (u.utrap && u.utraptype == TT_PIT) { + if (target_in_pit) + return conjoined_pits(t, t_at(u.ux, u.uy), FALSE); + return bimanual(pick); + } + + /* when hero isn't in a pit, a mattock or flying hero w/ pick can reach + whether or not the statue is in a pit */ + if (bimanual(pick) || Flying) + return TRUE; + /* one-handed pick-axe can reach if statue isn't in a pit */ + if (!target_in_pit) + return TRUE; + + return FALSE; +} + /* When digging into location , what are you actually digging into? */ int dig_typ(struct obj *otmp, coordxy x, coordxy y) @@ -147,30 +176,22 @@ dig_typ(struct obj *otmp, coordxy x, coordxy y) if (!ispick && !is_axe(otmp)) return DIGTYP_UNDIGGABLE; - if (ispick && sobj_at(STATUE, x, y)) { - return DIGTYP_STATUE; - } - else if (ispick && sobj_at(BOULDER, x, y)) { - return DIGTYP_BOULDER; - } - else if (closed_door(x, y)) { - if (door_is_iron(&levl[x][y])) - return DIGTYP_UNDIGGABLE; - else - return DIGTYP_DOOR; - } - else if (IS_TREE(levl[x][y].typ)) { - if (ispick) - return DIGTYP_UNDIGGABLE; - else - return DIGTYP_TREE; - } - else if (ispick && IS_ROCK(levl[x][y].typ) - && (!gl.level.flags.arboreal || IS_WALL(levl[x][y].typ))) { - return DIGTYP_ROCK; - } - - 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) + ? (door_is_iron(&levl[x][y]) ? DIGTYP_UNDIGGABLE + : DIGTYP_DOOR) + : IS_TREE(levl[x][y].typ) + ? (ispick ? DIGTYP_UNDIGGABLE : DIGTYP_TREE) + : (ispick && IS_OBSTRUCTED(levl[x][y].typ) + && (!svl.level.flags.arboreal + || IS_WALL(levl[x][y].typ))) + ? DIGTYP_ROCK + : DIGTYP_UNDIGGABLE); } boolean @@ -185,85 +206,126 @@ is_digging(void) #define BY_YOU (&gy.youmonst) #define BY_OBJECT ((struct monst *) 0) -boolean -dig_check(struct monst *madeby, boolean verbose, coordxy x, coordxy y) +enum digcheck_result +dig_check(struct monst *madeby, coordxy x, coordxy y) { struct trap *ttmp = t_at(x, y); - const char *verb = - (madeby == BY_YOU && uwep && is_axe(uwep)) ? "chop" : "dig in"; if (On_stairs(x, y)) { stairway *stway = stairway_at(x, y); if (stway->isladder) { - if (verbose) - pline_The("ladder resists your effort."); - } else if (verbose) - pline_The("stairs are too hard to %s.", verb); - return FALSE; + return DIGCHECK_FAIL_ONLADDER; + } else { + return DIGCHECK_FAIL_ONSTAIRS; + } } else if (IS_THRONE(levl[x][y].typ) && madeby != BY_OBJECT) { - if (verbose) - pline_The("throne is too hard to break apart."); - return FALSE; + return DIGCHECK_FAIL_THRONE; } else if (IS_ALTAR(levl[x][y].typ) - && (madeby != BY_OBJECT || Is_astralevel(&u.uz) - || Is_sanctum(&u.uz))) { - if (verbose) - pline_The("altar is too hard to break apart."); - return FALSE; + && (madeby != BY_OBJECT + || (altarmask_at(x, y) & AM_SANCTUM) != 0)) { + return DIGCHECK_FAIL_ALTAR; } else if (IS_MAGIC_PLATFORM(levl[x][y].typ)) { - pline_The("magic platform resists your effort."); - return FALSE; + return DIGCHECK_FAIL_TOOHARD; } else if (Is_airlevel(&u.uz)) { - if (verbose) - You("cannot %s thin air.", verb); - return FALSE; + return DIGCHECK_FAIL_AIRLEVEL; } else if (Is_waterlevel(&u.uz)) { - if (verbose) - pline_The("%s splashes and subsides.", hliquid("water")); - return FALSE; - } else if ((IS_ROCK(levl[x][y].typ) && levl[x][y].typ != SDOOR - && (levl[x][y].wall_info & W_NONDIGGABLE) != 0) - || (ttmp - && (undestroyable_trap(ttmp->ttyp) - || (!Can_dig_down(&u.uz) && !levl[x][y].candig)))) { - if (verbose) - pline_The("%s here is too hard to %s.", surface(x, y), verb); - return FALSE; + return DIGCHECK_FAIL_WATERLEVEL; + } 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)) { + return DIGCHECK_FAIL_UNDESTROYABLETRAP; + } else if (!Can_dig_down(&u.uz) && !levl[x][y].candig) { + if (ttmp) { + if (!is_hole(ttmp->ttyp) && !is_pit(ttmp->ttyp)) + return DIGCHECK_PASSED_DESTROY_TRAP; + else + return DIGCHECK_FAIL_CANTDIG; + } else { + return DIGCHECK_PASSED_PITONLY; + } } else if (sobj_at(BOULDER, x, y)) { - if (verbose) - There("isn't enough room to %s here.", verb); - return FALSE; + return DIGCHECK_FAIL_BOULDER; } else if (madeby == BY_OBJECT /* the block against existing traps is mainly to prevent broken wands from turning holes into pits */ && (ttmp || is_pool_or_lava(x, y))) { /* digging by player handles pools separately */ - return FALSE; + return DIGCHECK_FAIL_OBJ_POOL_OR_TRAP; + } + return DIGCHECK_PASSED; +} + +void +digcheck_fail_message(enum digcheck_result digresult, struct monst *madeby, + coordxy x, coordxy y) +{ + const char *verb = + (madeby == BY_YOU && uwep && is_axe(uwep)) ? "chop" : "dig in"; + + if (digresult < DIGCHECK_FAILED) + return; + + switch (digresult) { + case DIGCHECK_FAIL_AIRLEVEL: + You("cannot %s thin air.", verb); + break; + case DIGCHECK_FAIL_ALTAR: + pline_The("altar is too hard to break apart."); + break; + case DIGCHECK_FAIL_BOULDER: + There("isn't enough room to %s here.", verb); + break; + case DIGCHECK_FAIL_ONLADDER: + pline_The("ladder resists your effort."); + break; + case DIGCHECK_FAIL_ONSTAIRS: + pline_The("stairs are too hard to %s.", verb); + break; + case DIGCHECK_FAIL_THRONE: + pline_The("throne is too hard to break apart."); + break; + case DIGCHECK_FAIL_CANTDIG: + case DIGCHECK_FAIL_TOOHARD: + case DIGCHECK_FAIL_UNDESTROYABLETRAP: + pline_The("%s here is too hard to %s.", surface(x, y), verb); + break; + case DIGCHECK_FAIL_WATERLEVEL: + pline_The("%s splashes and subsides.", hliquid("water")); + break; + case DIGCHECK_FAIL_OBJ_POOL_OR_TRAP: + case DIGCHECK_PASSED: + case DIGCHECK_PASSED_PITONLY: + case DIGCHECK_PASSED_DESTROY_TRAP: + break; } - return TRUE; } -static int +staticfn int dig(void) { struct rm *lev; - coordxy dpx = gc.context.digging.pos.x, dpy = gc.context.digging.pos.y; + coordxy dpx = svc.context.digging.pos.x, dpy = svc.context.digging.pos.y; boolean ispick = uwep && is_pick(uwep); const char *verb = (!uwep || is_pick(uwep)) ? "dig into" : "chop through"; + enum digcheck_result dcresult = DIGCHECK_PASSED; lev = &levl[dpx][dpy]; /* perhaps a nymph stole your pick-axe while you were busy digging */ /* or perhaps you teleported away */ if (u.uswallow || !uwep || (!ispick && !is_axe(uwep)) - || !on_level(&gc.context.digging.level, &u.uz) - || ((gc.context.digging.down ? (dpx != u.ux || dpy != u.uy) + || !on_level(&svc.context.digging.level, &u.uz) + || ((svc.context.digging.down ? (dpx != u.ux || dpy != u.uy) : !next2u(dpx, dpy)))) return 0; - if (gc.context.digging.down) { - if (!dig_check(BY_YOU, TRUE, u.ux, u.uy)) + if (svc.context.digging.down) { + dcresult = dig_check(BY_YOU, u.ux, u.uy); + if (dcresult >= DIGCHECK_FAILED) { + digcheck_fail_message(dcresult, BY_YOU, u.ux, u.uy); return 0; - } else { /* !gc.context.digging.down */ + } + } else { /* !svc.context.digging.down */ if (IS_TREE(lev->typ) && !may_dig(dpx, dpy) && dig_typ(uwep, dpx, dpy) == DIGTYP_TREE) { if (Hallucination) { @@ -275,7 +337,7 @@ dig(void) } 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); @@ -302,7 +364,7 @@ dig(void) Soundeffect(se_bang_weapon_side, 100); pline("Bang! You hit with the broad side of %s!", the(xname(uwep))); - wake_nearby(); + wake_nearby(FALSE); break; default: Your("swing misses its mark."); @@ -311,21 +373,22 @@ dig(void) return 0; } - gc.context.digging.effort += + svc.context.digging.effort += 10 + rn2(5) + abon() + uwep->spe - greatest_erosion(uwep) + u.udaminc; if (Race_if(PM_DWARF)) - gc.context.digging.effort *= 2; - if (gc.context.digging.down) { + svc.context.digging.effort *= 2; + if (svc.context.digging.down) { struct trap *ttmp = t_at(dpx, dpy); - if (gc.context.digging.effort > 250 || (ttmp && ttmp->ttyp == HOLE)) { + if (svc.context.digging.effort > 250 + || (ttmp && ttmp->ttyp == HOLE)) { (void) dighole(FALSE, FALSE, (coord *) 0); - (void) memset((genericptr_t) &gc.context.digging, 0, - sizeof gc.context.digging); + (void) memset((genericptr_t) &svc.context.digging, 0, + sizeof svc.context.digging); return 0; /* done with digging */ } - if (gc.context.digging.effort <= 50 + if (svc.context.digging.effort <= 50 || (ttmp && (ttmp->ttyp == TRAPDOOR || is_pit(ttmp->ttyp)))) { return 1; } else if (ttmp && (ttmp->ttyp == LANDMINE @@ -334,8 +397,8 @@ dig(void) hero should have used #untrap first */ dotrap(ttmp, FORCETRAP); /* restart completely from scratch if we resume digging */ - (void) memset((genericptr_t) &gc.context.digging, 0, - sizeof gc.context.digging); + (void) memset((genericptr_t) &svc.context.digging, 0, + sizeof svc.context.digging); return 0; } else if (ttmp && ttmp->ttyp == BEAR_TRAP && u.utrap) { if (rnl(7) > (Fumbling ? 1 : 4)) { @@ -362,7 +425,18 @@ dig(void) reset_utrap(TRUE); /* release from trap, maybe Lev or Fly */ } /* we haven't made any progress toward a pit yet */ - gc.context.digging.effort = 0; + svc.context.digging.effort = 0; + return 0; + } else if (ttmp && dcresult == DIGCHECK_PASSED_DESTROY_TRAP) { + const char *ttmpname = trapname(ttmp->ttyp, FALSE); + + if (ispick) + You("destroy %s with %s.", + ttmp->tseen ? the(ttmpname) : an(ttmpname), + yobjnam(uwep, (const char *) 0)); + deltrap(ttmp); + /* we haven't made any progress toward a pit yet */ + svc.context.digging.effort = 0; return 0; } @@ -373,13 +447,13 @@ dig(void) /* make pit at */ if (dighole(TRUE, FALSE, (coord *) 0)) { - gc.context.digging.level.dnum = 0; - gc.context.digging.level.dlevel = -1; + svc.context.digging.level.dnum = 0; + svc.context.digging.level.dlevel = -1; } return 0; } - if (gc.context.digging.effort > 100) { + if (svc.context.digging.effort > 100) { const char *digtxt, *dmgtxt = (const char *) 0; struct obj *obj; boolean shopedge = *in_rooms(dpx, dpy, SHOPBASE); @@ -432,9 +506,10 @@ dig(void) add_damage(dpx, dpy, SHOP_WALL_DMG); dmgtxt = "damage"; } - if (gl.level.flags.is_maze_lev) { + if (svl.level.flags.is_maze_lev) { lev->typ = ROOM, lev->flags = 0; - } else if (gl.level.flags.is_cavernous_lev && !in_town(dpx, dpy)) { + } else if (svl.level.flags.is_cavernous_lev + && !in_town(dpx, dpy)) { lev->typ = CORR, lev->flags = 0; } else { lev->typ = DOOR; @@ -454,13 +529,13 @@ dig(void) if (!does_block(dpx, dpy, &levl[dpx][dpy])) unblock_point(dpx, dpy); /* vision: can see through */ feel_newsym(dpx, dpy); - if (digtxt && !gc.context.digging.quiet) + if (digtxt && !svc.context.digging.quiet) pline1(digtxt); /* after newsym */ if (dmgtxt) pay_for_damage(dmgtxt, FALSE); if (Is_earthlevel(&u.uz) && !rn2(3)) { - register struct monst *mtmp; + struct monst *mtmp; switch (rn2(2)) { case 0: @@ -482,14 +557,15 @@ dig(void) add_damage(dpx, dpy, SHOP_DOOR_COST); dmgtxt = "break"; } + recalc_block_point(dpx, dpy); newsym(dpx, dpy); } } cleanup: - gc.context.digging.lastdigtime = gm.moves; - gc.context.digging.quiet = FALSE; - gc.context.digging.level.dnum = 0; - gc.context.digging.level.dlevel = -1; + svc.context.digging.lastdigtime = svm.moves; + svc.context.digging.quiet = FALSE; + svc.context.digging.level.dnum = 0; + svc.context.digging.level.dlevel = -1; return 0; } else { /* not enough effort has been spent yet */ static const char *const d_target[6] = { "", "rock", "statue", @@ -503,19 +579,19 @@ 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) { You("hit the %s with all your might.", d_target[dig_target]); - wake_nearby(); + wake_nearby(FALSE); gd.did_dig_msg = TRUE; } } return 1; } -static boolean +staticfn boolean furniture_handled(coordxy x, coordxy y, boolean madeby_u) { struct rm *lev = &levl[x][y]; @@ -546,7 +622,7 @@ holetime(void) { if (go.occupation != dig || !*u.ushops) return -1; - return ((250 - gc.context.digging.effort) / 20); + return ((250 - svc.context.digging.effort) / 20); } /* Return typ of liquid to fill a hole with, or ROOM, if no liquid nearby */ @@ -588,15 +664,16 @@ void digactualhole(coordxy x, coordxy y, struct monst *madeby, int ttyp) { struct obj *oldobjs, *newobjs; - register struct trap *ttmp; - char surface_type[BUFSZ]; + struct trap *ttmp; + const char *surface_type, *tname, *in_thru; + char furniture[BUFSZ]; struct rm *lev = &levl[x][y]; - boolean shopdoor; struct monst *mtmp = m_at(x, y); /* may be madeby */ - boolean madeby_u = (madeby == BY_YOU); - boolean madeby_obj = (madeby == BY_OBJECT); - boolean at_u = u_at(x, y); - boolean wont_fall = Levitation || Flying; + boolean madeby_u = (madeby == BY_YOU), madeby_obj = (madeby == BY_OBJECT), + /* BY_OBJECT means the hero broke a wand, so blame her for it */ + heros_fault = (madeby_u || madeby_obj); + boolean shopdoor, at_u = u_at(x, y), wont_fall = Levitation || Flying; + int old_typ, old_aligntyp = A_NONE; if (at_u && u.utrap) { if (u.utraptype == TT_BURIEDBALL) @@ -614,41 +691,67 @@ digactualhole(coordxy x, coordxy y, struct monst *madeby, int ttyp) ttyp = PIT; } - /* maketrap() might change it, also, in this situation, - surface() returns an inappropriate string for a grave */ - if (IS_GRAVE(lev->typ)) - Strcpy(surface_type, "grave"); - else - Strcpy(surface_type, surface(x, y)); + /* maketrap() might change terrain type but we deliver messages after + that, so prepare in advance */ + old_typ = lev->typ; + furniture[0] = '\0'; + if (IS_FURNITURE(lev->typ)) { + /* should mirror the word used by surface() for normal floor */ + surface_type = (IS_ROOM(lev->typ) && !Is_earthlevel(&u.uz) + ? "floor" : "ground"); + if (IS_ALTAR(lev->typ)) { + old_aligntyp = Amask2align(levl[x][y].altarmask & AM_MASK); + Strcpy(furniture, align_str(old_aligntyp)); + Strcat(furniture, " "); + } + Strcat(furniture, surface(x, y)); + } else { + surface_type = surface(x, y); + } shopdoor = IS_DOOR(lev->typ) && *in_rooms(x, y, SHOPBASE); - oldobjs = gl.level.objects[x][y]; + oldobjs = svl.level.objects[x][y]; + ttmp = maketrap(x, y, ttyp); if (!ttmp) return; - newobjs = gl.level.objects[x][y]; - ttmp->madeby_u = madeby_u; + newobjs = svl.level.objects[x][y]; + ttmp->madeby_u = heros_fault; ttmp->tseen = 0; if (cansee(x, y)) seetrap(ttmp); else if (madeby_u) feeltrap(ttmp); + tname = trapname(ttyp, TRUE); + in_thru = (ttyp == HOLE ? "through" : "in"); + if (madeby_u) { + if (x != u.ux || y != u.uy) + You("dig an adjacent %s.", tname); + else + You("dig %s %s the %s.", an(tname), in_thru, surface_type); + } else if (!madeby_obj && canseemon(madeby)) { + pline("%s digs %s %s the %s.", Monnam(madeby), an(tname), in_thru, + surface_type); + } else if (cansee(x, y) && flags.verbose) { + 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); + /* wrath should immediately follow altar destruction message */ + if (heros_fault && old_typ == ALTAR) + desecrate_altar(FALSE, old_aligntyp); + + /* now deal with actual post-trap creation effects */ if (ttyp == PIT) { - if (madeby_u) { - if (x != u.ux || y != u.uy) - You("dig an adjacent pit."); - else - You("dig a pit in the %s.", surface_type); - if (shopdoor) - pay_for_damage("ruin", FALSE); - else - add_damage(x, y, madeby_u ? SHOP_PIT_COST : 0L); - wake_nearby(); - } else if (!madeby_obj && canseemon(madeby)) { - pline("%s digs a pit in the %s.", Monnam(madeby), surface_type); - } else if (cansee(x, y) && Verbose(0, digactualhole1)) { - pline("A pit appears in the %s.", surface_type); - } + if (shopdoor && heros_fault) + pay_for_damage("ruin", FALSE); + else + add_damage(x, y, heros_fault ? SHOP_PIT_COST : 0L); + if (madeby_u) + wake_nearby(FALSE); /* in case we're digging down while encased in solid rock which is blocking levitation or flight */ switch_terrain(); @@ -673,15 +776,6 @@ digactualhole(coordxy x, coordxy y, struct monst *madeby, int ttyp) (void) mintrap(mtmp, NO_TRAP_FLAGS); } } else { /* was TRAPDOOR now a HOLE*/ - - if (madeby_u) - You("dig a hole through the %s.", surface_type); - else if (!madeby_obj && canseemon(madeby)) - pline("%s digs a hole through the %s.", Monnam(madeby), - surface_type); - else if (cansee(x, y) && Verbose(0, digactualhole2)) - pline("A hole appears in the %s.", surface_type); - if (at_u) { /* in case we're digging down while encased in solid rock which is blocking levitation or flight */ @@ -704,18 +798,15 @@ digactualhole(coordxy x, coordxy y, struct monst *madeby, int ttyp) impact_drop((struct obj *) 0, x, y, 0); if (oldobjs != newobjs) (void) pickup(1); - if (shopdoor && madeby_u) + if (shopdoor && heros_fault) pay_for_damage("ruin", FALSE); - } else { d_level newlevel; - if (*u.ushops && madeby_u) + if (*u.ushops && heros_fault) shopdig(1); /* shk might snatch pack */ - /* handle earlier damage, eg breaking wand of digging */ - else if (!madeby_u) + else /* handle any earlier hero-caused damage */ pay_for_damage("dig into", TRUE); - You("fall through..."); /* Earlier checks must ensure that the destination * level exists and is in the present dungeon. @@ -727,7 +818,7 @@ digactualhole(coordxy x, coordxy y, struct monst *madeby, int ttyp) spoteffects(FALSE); } } else { - if (shopdoor && madeby_u) + if (shopdoor && heros_fault) pay_for_damage("ruin", FALSE); if (newobjs) impact_drop((struct obj *) 0, x, y, 0); @@ -765,26 +856,40 @@ digactualhole(coordxy x, coordxy y, struct monst *madeby, int ttyp) DISABLE_WARNING_FORMAT_NONLITERAL /* - * Called from dighole(), but also from do_break_wand() - * in apply.c. + * Called from dighole(); also from do_break_wand() in apply.c + * and do_earthquake() in music.c. */ void -liquid_flow(coordxy x, coordxy y, schar typ, struct trap *ttmp, - const char *fillmsg) +liquid_flow( + coordxy x, coordxy y, + schar typ, + struct trap *ttmp, + const char *fillmsg) { struct obj *objchain; struct monst *mon; boolean u_spot = u_at(x, y); + /* caller should have changed levl[x][y].typ to POOL, MOAT, or LAVA */ + if (!is_pool_or_lava(x, y)) { + if (iflags.sanity_check) { + impossible("Insane liquid_flow(%d,%d,%s,%s).", x, y, + ttmp ? trapname(ttmp->ttyp, TRUE) : "no trap", + fillmsg ? fillmsg : "no mesg"); + } + return; + } + if (ttmp) - (void) delfloortrap(ttmp); + (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) pline(fillmsg, hliquid(typ == LAVAPOOL ? "lava" : "water")); /* handle object damage before hero damage; affects potential bones */ - if ((objchain = gl.level.objects[x][y]) != 0) { + if ((objchain = svl.level.objects[x][y]) != 0) { if (typ == LAVAPOOL) fire_damage_chain(objchain, TRUE, TRUE, x, y); else @@ -804,12 +909,13 @@ RESTORE_WARNING_FORMAT_NONLITERAL boolean dighole(boolean pit_only, boolean by_magic, coord *cc) { - register struct trap *ttmp; + struct trap *ttmp; struct rm *lev; struct obj *boulder_here; schar typ, old_typ; coordxy dig_x, dig_y; boolean nohole, retval = FALSE; + enum digcheck_result dig_check_result; if (!cc) { dig_x = u.ux; @@ -823,20 +929,26 @@ dighole(boolean pit_only, boolean by_magic, coord *cc) ttmp = t_at(dig_x, dig_y); lev = &levl[dig_x][dig_y]; - nohole = (!Can_dig_down(&u.uz) && !lev->candig); + dig_check_result = dig_check(BY_YOU, dig_x, dig_y); + /* nohole = (!Can_dig_down(&u.uz) && !lev->candig); */ + nohole = (dig_check_result == DIGCHECK_FAIL_CANTDIG + || dig_check_result == DIGCHECK_FAIL_TOOHARD); old_typ = lev->typ; if ((ttmp && (undestroyable_trap(ttmp->ttyp) || nohole)) || IS_MAGIC_PLATFORM(old_typ) - || (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" : ""); - + } else if (ttmp && is_magical_trap(ttmp->ttyp)) { + explode(dig_x, dig_y, 0, 20 + d(3, 6), TRAP_EXPLODE, EXPL_MAGICAL); + deltrap(ttmp); + newsym(dig_x, dig_y); } else if (is_pool_or_lava(dig_x, dig_y)) { pline_The("%s sloshes furiously for a moment, then subsides.", hliquid(is_lava(dig_x, dig_y) ? "lava" : "water")); - wake_nearby(); /* splashing */ + wake_nearby(FALSE); /* splashing */ } else if (old_typ == DRAWBRIDGE_DOWN || (is_drawbridge_wall(dig_x, dig_y) >= 0)) { @@ -866,7 +978,7 @@ dighole(boolean pit_only, boolean by_magic, coord *cc) */ Soundeffect(se_kadoom_boulder_falls_in, 60); pline("KADOOM! The boulder falls in!"); - wake_nearby(); + wake_nearby(FALSE); (void) delfloortrap(ttmp); } delobj(boulder_here); @@ -924,7 +1036,9 @@ dighole(boolean pit_only, boolean by_magic, coord *cc) } /* finally we get to make a hole */ - if (nohole || pit_only) + if (nohole || pit_only + || dig_check_result == DIGCHECK_PASSED_DESTROY_TRAP + || dig_check_result == DIGCHECK_PASSED_PITONLY) digactualhole(dig_x, dig_y, BY_YOU, PIT); else digactualhole(dig_x, dig_y, BY_YOU, HOLE); @@ -935,7 +1049,7 @@ dighole(boolean pit_only, boolean by_magic, coord *cc) return retval; } -static void +staticfn void dig_up_grave(coord *cc) { struct obj *otmp; @@ -1074,7 +1188,7 @@ int use_pick_axe2(struct obj *obj) { coordxy rx, ry; - register struct rm *lev; + struct rm *lev; struct trap *trap, *trap_with_u; int dig_target; boolean ispick = is_pick(obj); @@ -1099,7 +1213,7 @@ use_pick_axe2(struct obj *obj) You("hit yourself with %s.", yname(uwep)); Sprintf(buf, "%s own %s", uhis(), OBJ_NAME(objects[obj->otyp])); losehp(Maybe_Half_Phys(dam), buf, KILLED_BY); - gc.context.botl = 1; + disp.botl = TRUE; return ECMD_TIME; } else if (u.dz == 0) { confdir(FALSE); @@ -1115,6 +1229,8 @@ use_pick_axe2(struct obj *obj) return ECMD_TIME; dig_target = dig_typ(obj, rx, ry); if (dig_target == DIGTYP_UNDIGGABLE) { + struct obj *boulder; + /* ACCESSIBLE or POOL */ trap = t_at(rx, ry); if (trap && trap->ttyp == WEB) { @@ -1131,13 +1247,13 @@ use_pick_axe2(struct obj *obj) } else if (lev->typ == SDOOR && door_is_iron(lev)) { cvt_sdoor_to_door(lev); pline("Clang! You uncover a secret iron door!"); - wake_nearby(); + wake_nearby(FALSE); } else if (IS_DOOR(lev->typ) && door_is_iron(lev)) { pline("Clang! Your pick-axe bounces harmlessly off the door."); - wake_nearby(); + wake_nearby(FALSE); } else if (lev->typ == IRONBARS) { pline("Clang! Your pick-axe bounces harmlessly off the bars."); - wake_nearby(); + wake_nearby(FALSE); } else if (IS_WATERWALL(lev->typ)) { pline("Splash!"); } else if (lev->typ == LAVAWALL) { @@ -1145,19 +1261,30 @@ 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 (!ispick && (sobj_at(STATUE, rx, ry) - || sobj_at(BOULDER, rx, ry))) { - boolean vibrate = !rn2(3); - - pline("Sparks fly as you whack the %s.%s", - sobj_at(STATUE, rx, ry) ? "statue" : "boulder", - vibrate ? " The axe-handle vibrates violently!" : ""); - if (vibrate) - losehp(Maybe_Half_Phys(2), "axing a hard object", - KILLED_BY); - wake_nearby(); + } else if ((boulder = sobj_at(BOULDER, rx, ry)) != 0 + || sobj_at(STATUE, rx, ry)) { + /* if both boulders and statues are present, the topmost + boulder will be shown on the map so treat it as target */ + const char *what = boulder ? "boulder" : "statue"; + + if (!ispick) { + boolean vibrate = !rn2(3); + + pline("Sparks fly as you whack the %s.%s", what, + vibrate ? " The axe-handle vibrates violently!" + : ""); + if (vibrate) + losehp(Maybe_Half_Phys(2), "axing a hard object", + KILLED_BY); + wake_nearby(FALSE); + } else { + /* using a pick but dig_target is DIGTYPE_UNDIGGABLE + and there is at least one boulder or statue or both + present; pick_can_reach() returned false */ + You_cant("reach the %s.", what); + } } else if (u.utrap && u.utraptype == TT_PIT && trap && (trap_with_u = t_at(u.ux, u.uy)) && is_pit(trap->ttyp) @@ -1186,38 +1313,39 @@ use_pick_axe2(struct obj *obj) "cutting the tree" }; gd.did_dig_msg = FALSE; - gc.context.digging.quiet = FALSE; + svc.context.digging.quiet = FALSE; if (IS_DOOR(lev->typ) && predoortrapped(rx, ry, &gy.youmonst, ARM, D_BROKEN)) { /* doorstate was modified by trap, so don't dig: * do nothing else; trap will handle messages */ ; } - else if (gc.context.digging.pos.x != rx - || gc.context.digging.pos.y != ry - || !on_level(&gc.context.digging.level, &u.uz) - || gc.context.digging.down) { + else if (svc.context.digging.pos.x != rx + || svc.context.digging.pos.y != ry + || !on_level(&svc.context.digging.level, &u.uz) + || svc.context.digging.down) { if (flags.autodig && dig_target == DIGTYP_ROCK - && !gc.context.digging.down - && u_at(gc.context.digging.pos.x, gc.context.digging.pos.y) - && (gm.moves <= gc.context.digging.lastdigtime + 2 - && gm.moves >= gc.context.digging.lastdigtime)) { + && !svc.context.digging.down + && u_at(svc.context.digging.pos.x, + svc.context.digging.pos.y) + && (svm.moves <= svc.context.digging.lastdigtime + 2 + && svm.moves >= svc.context.digging.lastdigtime)) { /* avoid messages if repeated autodigging */ gd.did_dig_msg = TRUE; - gc.context.digging.quiet = TRUE; + svc.context.digging.quiet = TRUE; } - gc.context.digging.down = gc.context.digging.chew = FALSE; - gc.context.digging.warned = FALSE; - gc.context.digging.pos.x = rx; - gc.context.digging.pos.y = ry; - assign_level(&gc.context.digging.level, &u.uz); - gc.context.digging.effort = 0; - if (!gc.context.digging.quiet) + svc.context.digging.down = svc.context.digging.chew = FALSE; + svc.context.digging.warned = FALSE; + svc.context.digging.pos.x = rx; + svc.context.digging.pos.y = ry; + assign_level(&svc.context.digging.level, &u.uz); + svc.context.digging.effort = 0; + if (!svc.context.digging.quiet) You("start %s.", d_action[dig_target]); } else { - You("%s %s.", gc.context.digging.chew ? "begin" : "continue", + You("%s %s.", svc.context.digging.chew ? "begin" : "continue", d_action[dig_target]); - gc.context.digging.chew = FALSE; + svc.context.digging.chew = FALSE; } if (IS_DOOR(lev->typ)) { predoortrapped(rx, ry, &gy.youmonst, ARM, D_BROKEN); @@ -1248,16 +1376,17 @@ use_pick_axe2(struct obj *obj) surface(u.ux, u.uy)); u_wipe_engr(3); } else { - if (gc.context.digging.pos.x != u.ux || gc.context.digging.pos.y != u.uy - || !on_level(&gc.context.digging.level, &u.uz) - || !gc.context.digging.down) { - gc.context.digging.chew = FALSE; - gc.context.digging.down = TRUE; - gc.context.digging.warned = FALSE; - gc.context.digging.pos.x = u.ux; - gc.context.digging.pos.y = u.uy; - assign_level(&gc.context.digging.level, &u.uz); - gc.context.digging.effort = 0; + if (svc.context.digging.pos.x != u.ux + || svc.context.digging.pos.y != u.uy + || !on_level(&svc.context.digging.level, &u.uz) + || !svc.context.digging.down) { + svc.context.digging.chew = FALSE; + svc.context.digging.down = TRUE; + svc.context.digging.warned = FALSE; + svc.context.digging.pos.x = u.ux; + svc.context.digging.pos.y = u.uy; + assign_level(&svc.context.digging.level, &u.uz); + svc.context.digging.effort = 0; You("start %s downward.", verbing); if (*u.ushops) { shopdig(0); @@ -1271,7 +1400,7 @@ use_pick_axe2(struct obj *obj) return ECMD_TIME; } -static boolean +staticfn boolean watchman_canseeu(struct monst *mtmp) { if (is_watch(mtmp->data) && mtmp->mcansee && m_canseeu(mtmp) @@ -1299,7 +1428,7 @@ watch_dig(struct monst *mtmp, coordxy x, coordxy y, boolean zap) if (mtmp) { SetVoice(mtmp, 0, 80, 0); - if (zap || gc.context.digging.warned) { + if (zap || svc.context.digging.warned) { verbalize("Halt, vandal! You're under arrest!"); (void) angry_guards(!!Deaf); } else { @@ -1309,12 +1438,12 @@ 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"; verbalize("Hey, stop damaging that %s!", str); - gc.context.digging.warned = TRUE; + svc.context.digging.warned = TRUE; } if (is_digging()) stop_occupation(); @@ -1326,7 +1455,7 @@ watch_dig(struct monst *mtmp, coordxy x, coordxy y, boolean zap) boolean mdig_tunnel(struct monst *mtmp) { - register struct rm *here; + struct rm *here; int pile = rnd(12); boolean withpick = needspick(mtmp->data); @@ -1348,8 +1477,7 @@ mdig_tunnel(struct monst *mtmp) set_doorstate(here, D_BROKEN); unblock_point(mtmp->mx, mtmp->my); /* vision */ newsym(mtmp->mx, mtmp->my); - if (!Unaware && Verbose(0, mdig_tunnel1) && !rn2(3)) { - + if (!Unaware && flags.verbose && !rn2(3)) { /* not too often.. */ draft_message(TRUE); /* "You feel an unexpected draft." */ } @@ -1361,7 +1489,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; } @@ -1376,16 +1504,16 @@ mdig_tunnel(struct monst *mtmp) if (IS_WALL(here->typ)) { /* KMH -- Okay on arboreal levels (room walls are still stone) */ - if (Verbose(0, mdig_tunnel2) && !rn2(5)) { + if (flags.verbose && !rn2(5)) { Soundeffect(se_crashing_rock, 75); You_hear(Hallucination ? "a power chord!" : "crashing rock."); } if (*in_rooms(mtmp->mx, mtmp->my, SHOPBASE)) add_damage(mtmp->mx, mtmp->my, 0L); - if (gl.level.flags.is_maze_lev) { + if (svl.level.flags.is_maze_lev) { here->typ = ROOM, here->flags = 0; - } else if (gl.level.flags.is_cavernous_lev + } else if (svl.level.flags.is_cavernous_lev && !in_town(mtmp->mx, mtmp->my)) { here->typ = CORR, here->flags = 0; } else { @@ -1497,7 +1625,7 @@ zap_dig(void) if (u.dz) { if (!Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz) && !Underwater) { if ((u.dz < 0 || On_stairs(u.ux, u.uy)) - && !gl.level.flags.outdoors) { + && !svl.level.flags.outdoors) { int dmg; if (On_stairs(u.ux, u.uy)) { stairway *stway = stairway_at(u.ux, u.uy); @@ -1507,7 +1635,7 @@ zap_dig(void) } You("loosen a rock from the %s.", ceiling(u.ux, u.uy)); pline("It falls on your %s!", body_part(HEAD)); - dmg = rnd((uarmh && is_hard(uarmh)) ? 2 : 6); + dmg = rnd(hard_helmet(uarmh) ? 2 : 6); losehp(Maybe_Half_Phys(dmg), "falling rock", KILLED_BY_AN); otmp = mksobj(ROCK, FALSE, FALSE); if (otmp) { @@ -1525,7 +1653,7 @@ zap_dig(void) /* normal case: digging across the level */ shopdoor = shopwall = FALSE; - maze_dig = gl.level.flags.is_maze_lev && !Is_earthlevel(&u.uz); + maze_dig = svl.level.flags.is_maze_lev && !Is_earthlevel(&u.uz); zx = u.ux + u.dx; zy = u.uy + u.dy; if (u.utrap && u.utraptype == TT_PIT @@ -1549,6 +1677,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]; @@ -1601,7 +1730,7 @@ zap_dig(void) } } watch_dig((struct monst *) 0, zx, zy, TRUE); - unblock_point(zx, zy); /* vision */ + recalc_block_point(zx, zy); /* vision */ newsym(zx, zy); digdepth -= 2; if (maze_dig) @@ -1634,7 +1763,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) { @@ -1643,7 +1772,7 @@ zap_dig(void) shopwall = TRUE; } watch_dig((struct monst *) 0, zx, zy, TRUE); - if (gl.level.flags.is_cavernous_lev && !in_town(zx, zy)) { + if (svl.level.flags.is_cavernous_lev && !in_town(zx, zy)) { room->typ = CORR, room->flags = 0; } else { room->typ = DOOR; @@ -1653,7 +1782,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--; } @@ -1686,7 +1815,7 @@ zap_dig(void) * you're zapping a wand of digging laterally while * down in the pit. */ -static int +staticfn int adj_pit_checks(coord *cc, char *msg) { int ltyp; @@ -1767,7 +1896,7 @@ adj_pit_checks(coord *cc, char *msg) /* * Ensure that all conjoined pits fill up. */ -static void +staticfn void pit_flow(struct trap *trap, schar filltyp) { /* @@ -1819,7 +1948,7 @@ buried_ball(coord *cc) * criterium (within 2 steps of tethered hero's present location) * it will find an arbitrary one rather than the one which used * to be uball. Once 3.6.{0,1} save file compatibility is broken, - * we should add gc.context.buriedball_oid and then we can find the + * we should add svc.context.buriedball_oid and then we can find the * actual former uball, which might be extra heavy or christened * or not the one buried directly underneath the target spot. * @@ -1831,7 +1960,7 @@ buried_ball(coord *cc) of u.utraptype is no longer meaningful; if u.utrap is still set then u.utraptype needs to be for buried ball */ if (!u.utrap || u.utraptype == TT_BURIEDBALL) { - for (otmp = gl.level.buriedobjlist; otmp; otmp = otmp->nobj) { + for (otmp = svl.level.buriedobjlist; otmp; otmp = otmp->nobj) { if (otmp->otyp != HEAVY_IRON_BALL) continue; /* if found at the target spot, we're done */ @@ -1943,8 +2072,8 @@ bury_an_obj(struct obj *otmp, boolean *dealloced) obj_extract_self(otmp); under_ice = is_ice(otmp->ox, otmp->oy); - if (otmp->otyp == ROCK && !under_ice) { - /* merges into burying material */ + if ((otmp->otyp == ROCK && !under_ice) || otmp->otyp == BOULDER) { + /* merges into burying material; boulder removal is for #wizbury */ if (dealloced) *dealloced = TRUE; obfree(otmp, (struct obj *) 0); @@ -1956,7 +2085,7 @@ bury_an_obj(struct obj *otmp, boolean *dealloced) */ if (otmp->otyp == CORPSE) { ; /* should cancel timer if under_ice */ - } else if ((under_ice ? otmp->oclass == POTION_CLASS : is_organic(otmp)) + } else if ((under_ice ? (otmp->oclass == POTION_CLASS) : is_organic(otmp)) && !obj_resists(otmp, 5, 95)) { (void) start_timer((under_ice ? 0L : 250L) + (long) rnd(250), TIMER_OBJECT, ROT_ORGANIC, obj_to_any(otmp)); @@ -1984,11 +2113,11 @@ bury_objs(int x, int y) costly = ((shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) && costly_spot(x, y)); - if (gl.level.objects[x][y] != (struct obj *) 0) { + if (svl.level.objects[x][y] != (struct obj *) 0) { debugpline2("bury_objs: at <%d,%d>", x, y); } - for (otmp = gl.level.objects[x][y]; otmp; otmp = otmp2) { - if (costly && !gc.context.mon_moving) { + for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp2) { + if (costly && !svc.context.mon_moving) { loss += stolen_value(otmp, x, y, (boolean) shkp->mpeaceful, TRUE); if (otmp->oclass != COIN_CLASS) otmp->no_charge = 1; @@ -2007,7 +2136,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) { @@ -2018,7 +2148,7 @@ unearth_objs(int x, int y) cc.x = x; cc.y = y; bball = buried_ball(&cc); - for (otmp = gl.level.buriedobjlist; otmp; otmp = otmp2) { + for (otmp = svl.level.buriedobjlist; otmp; otmp = otmp2) { otmp2 = otmp->nobj; if (otmp->ox == x && otmp->oy == y) { if (bball && otmp == bball @@ -2039,7 +2169,7 @@ unearth_objs(int x, int y) /* * The organic material has rotted away while buried. As an expansion, - * we could add add partial damage. A damage count is kept in the object + * we could add partial damage. A damage count is kept in the object * and every time we are called we increment the count and reschedule another * timeout. Eventually the object rots away. * @@ -2080,7 +2210,7 @@ rot_corpse(anything *arg, long timeout) x = obj->ox; y = obj->oy; } else if (in_invent) { - if (Verbose(0, rot_corpse)) { + if (flags.verbose) { char *cname = corpse_xname(obj, (const char *) 0, CXN_NO_PFX); Your("%s%s %s away%c", obj == uwep ? "wielded " : "", cname, @@ -2188,7 +2318,7 @@ escape_tomb(void) surface(u.ux, u.uy)); good = (tunnels(gy.youmonst.data) && !needspick(gy.youmonst.data)) - ? dighole(TRUE, FALSE, (coord *)0) : TRUE; + ? dighole(TRUE, FALSE, (coord *) 0) : TRUE; if (good) unearth_you(); } @@ -2213,12 +2343,35 @@ struct obj *otmp; int wiz_debug_cmd_bury(void) { - int x, y; + struct obj *otmp; + int x, y, before = 0, after = 0, diff; for (x = u.ux - 1; x <= u.ux + 1; x++) - for (y = u.uy - 1; y <= u.uy + 1; y++) - if (isok(x, y)) - bury_objs(x, y); + for (y = u.uy - 1; y <= u.uy + 1; y++) { + if (!isok(x, y)) + continue; + for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp->nexthere) + ++before; + + bury_objs(x, y); + + for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp->nexthere) + ++after; + } + + diff = before - after; + if (before == 0) + /* there was nothing here */ + pline("No objects here or adjacent to bury."); + else if (diff == 0) + /* before and after will be the same if only unburiable objects are + present (The Amulet, invocation items, Rider corpses, uchain when + uball doesn't get buried: carried or floor beyond burial range) */ + pline("No objects buried."); + else + /* usual case; if uball got buried, uchain went away and won't be + counted as buried */ + pline("%d object%s buried.", diff, plur(diff)); return ECMD_OK; } #endif /* DEBUG */ @@ -2267,7 +2420,7 @@ create_pit_under(struct monst *mdef, struct monst *magr) return FALSE; } - if (Verbose(4, create_pit_under)) { + if (flags.verbose) { if (youattack) { pline("You stomp the ground!"); } @@ -2405,4 +2558,10 @@ create_pit_under(struct monst *mdef, struct monst *magr) return TRUE; } +#undef BY_YOU +#undef BY_OBJECT +/* for 'onefile' testing, leave STRIDENT defined so that the other instance + of it in pray.c will trigger a complaint if someone changes its value */ +/*#undef STRIDENT*/ + /*dig.c*/ diff --git a/src/display.c b/src/display.c index 12551469bf..0c4c11b145 100644 --- a/src/display.c +++ b/src/display.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 display.c $NHDT-Date: 1682758082 2023/04/29 08:48:02 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.216 $ */ +/* NetHack 3.7 display.c $NHDT-Date: 1723834773 2024/08/16 18:59:33 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.244 $ */ /* Copyright (c) Dean Luick, with acknowledgements to Kevin Darcy */ /* and Dave Cohrs, 1990. */ /* NetHack may be freely redistributed. See license for details. */ @@ -123,37 +123,36 @@ */ #include "hack.h" -static void show_mon_or_warn(coordxy, coordxy, int); -static void display_monster(coordxy, coordxy, struct monst *, int, boolean); -static int swallow_to_glyph(int, int); -static void display_warning(struct monst *); - -static int check_pos(coordxy, coordxy, int); -static void get_bkglyph_and_framecolor(coordxy x, coordxy y, int *, uint32 *); -static int tether_glyph(coordxy, coordxy); -static void mimic_light_blocking(struct monst *); -#ifdef UNBUFFERED_GLYPHINFO -static glyph_info *glyphinfo_at(coordxy, coordxy, int); -#endif +staticfn void show_mon_or_warn(coordxy, coordxy, int); +staticfn void display_monster(coordxy, coordxy, + struct monst *, int, boolean) NONNULLPTRS; +staticfn int swallow_to_glyph(int, int); +staticfn void display_warning(struct monst *) NONNULLARG1; +staticfn boolean mon_overrides_region(struct monst *, coordxy, coordxy); +staticfn int check_pos(coordxy, coordxy, int); +staticfn void get_bkglyph_and_framecolor(coordxy x, coordxy y, int *, + uint32 *); +staticfn int tether_glyph(coordxy, coordxy); +staticfn void mimic_light_blocking(struct monst *) NONNULLARG1; /*#define WA_VERBOSE*/ /* give (x,y) locations for all "bad" spots */ #ifdef WA_VERBOSE -static boolean more_than_one(coordxy, coordxy, coordxy, coordxy, coordxy); +staticfn boolean more_than_one(coordxy, coordxy, coordxy, coordxy, coordxy); #endif -static int set_twall(coordxy, coordxy, coordxy, coordxy, - coordxy, coordxy, coordxy, coordxy); -static int set_wall(coordxy, coordxy, int); -static int set_corn(coordxy, coordxy, coordxy, coordxy, - coordxy, coordxy, coordxy, coordxy); -static int set_crosswall(coordxy, coordxy); -static void set_seenv(struct rm *, coordxy, coordxy, coordxy, coordxy); -static void t_warn(struct rm *); -static int wall_angle(struct rm *); -static int magicplatform_to_glyph(coordxy, coordxy); -static boolean spot_shows_engravings(coordxy, coordxy); +staticfn int set_twall(coordxy, coordxy, coordxy, coordxy, + coordxy, coordxy, coordxy, coordxy); +staticfn int set_wall(coordxy, coordxy, int); +staticfn int set_corn(coordxy, coordxy, coordxy, coordxy, + coordxy, coordxy, coordxy, coordxy); +staticfn int set_crosswall(coordxy, coordxy); +staticfn void set_seenv(struct rm *, coordxy, coordxy, coordxy, coordxy); +staticfn void t_warn(struct rm *); +staticfn int wall_angle(struct rm *); +staticfn int magicplatform_to_glyph(coordxy, coordxy); +staticfn boolean spot_shows_engravings(coordxy, coordxy); -#define remember_topology(x, y) (gl.lastseentyp[x][y] = levl[x][y].typ) +#define _glyph_at(x, y) gg.gbuf[y][x].glyphinfo.glyph /* * See display.h for descriptions of tp_sensemon() through @@ -251,12 +250,13 @@ magic_map_background(coordxy x, coordxy y, int show) else if (lev->typ == CORR && glyph == cmap_to_glyph(S_litcorr)) glyph = cmap_to_glyph(S_corr); } - if (gl.level.flags.hero_memory) + if (svl.level.flags.hero_memory + && (glyph_is_unexplored(lev->glyph) || glyph_is_cmap(lev->glyph))) lev->glyph = glyph; if (show) show_glyph(x, y, glyph); - remember_topology(x, y); + update_lastseentyp(x, y); } /* @@ -278,11 +278,11 @@ magic_map_background(coordxy x, coordxy y, int show) * the hero can physically see the location. Update the screen if directed. */ void -map_background(register coordxy x, register coordxy y, register int show) +map_background(coordxy x, coordxy y, int show) { - register int glyph = back_to_glyph(x, y); + int glyph = back_to_glyph(x, y); - if (gl.level.flags.hero_memory) + if (svl.level.flags.hero_memory) levl[x][y].glyph = glyph; if (show) show_glyph(x, y, glyph); @@ -295,12 +295,12 @@ map_background(register coordxy x, register coordxy y, register int show) * hero can physically see the location. */ void -map_trap(register struct trap *trap, register int show) +map_trap(struct trap *trap, int show) { - register coordxy x = trap->tx, y = trap->ty; - register int glyph = trap_to_glyph(trap); + coordxy x = trap->tx, y = trap->ty; + int glyph = trap_to_glyph(trap); - if (gl.level.flags.hero_memory) + if (svl.level.flags.hero_memory) levl[x][y].glyph = glyph; if (show) show_glyph(x, y, glyph); @@ -312,22 +312,19 @@ map_trap(register struct trap *trap, register int show) * Map the engraving and print it out if directed. */ void -map_engraving(struct engr *ep, register int show) +map_engraving(struct engr *ep, int show) { coordxy x, y; int glyph; if (!ep) { impossible("map_engraving called with null engr"); - /* we could use engr_to_glyph(ENGRAVE, 0, 0, 0) to get a default - * engraving glyph to show, but if ep is null then we have no idea what - * x and y to show it at */ return; } x = ep->engr_x; y = ep->engr_y; - glyph = engr_to_glyph(ep->engr_type, x, y, ledger_no(&u.uz)); - if (gl.level.flags.hero_memory) + glyph = engraving_to_glyph(ep); + if (svl.level.flags.hero_memory) levl[x][y].glyph = glyph; if (show) show_glyph(x, y, glyph); @@ -342,10 +339,10 @@ map_engraving(struct engr *ep, register int show) * the claim here that the hero can see obj's .] */ void -map_object(register struct obj *obj, int show) +map_object(struct obj *obj, int show) { - register coordxy x = obj->ox, y = obj->oy; - register int glyph = obj_to_glyph(obj, newsym_rn2); + coordxy x = obj->ox, y = obj->oy; + int glyph = obj_to_glyph(obj, newsym_rn2); /* if this object is already displayed as a generic object, it might become a specific one now */ @@ -363,7 +360,7 @@ map_object(register struct obj *obj, int show) } } - if (gl.level.flags.hero_memory) { + if (svl.level.flags.hero_memory) { /* MRKR: While hallucinating, statues are seen as random monsters */ /* but remembered as random objects. */ @@ -387,10 +384,10 @@ map_object(register struct obj *obj, int show) * by newsym() if necessary. */ void -map_invisible(register coordxy x, register coordxy y) +map_invisible(coordxy x, coordxy y) { if (x != u.ux || y != u.uy) { /* don't display I at hero's location */ - if (gl.level.flags.hero_memory) + if (svl.level.flags.hero_memory) levl[x][y].glyph = GLYPH_INVISIBLE; show_glyph(x, y, GLYPH_INVISIBLE); } @@ -418,12 +415,12 @@ unmap_invisible(coordxy x, coordxy y) * invisible monster notation, we might have to call newsym(). */ void -unmap_object(register coordxy x, register coordxy y) +unmap_object(coordxy x, coordxy y) { - register struct trap *trap; + struct trap *trap; struct engr *ep; - if (!gl.level.flags.hero_memory) + if (!svl.level.flags.hero_memory) return; if ((trap = t_at(x, y)) != 0 && trap->tseen && !covers_traps(x, y)) { @@ -432,10 +429,13 @@ unmap_object(register coordxy x, register 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) @@ -455,24 +455,30 @@ unmap_object(register coordxy x, register coordxy y) * Internal to display.c, this is a #define for speed. */ #define _map_location(x, y, show) \ - { \ - register struct obj *obj; \ - register struct trap *trap; \ - struct engr *ep; \ - \ - 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); \ - \ - remember_topology(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 map_location(coordxy x, coordxy y, int show) @@ -481,7 +487,7 @@ map_location(coordxy x, coordxy y, int show) } /* display something on monster layer; may need to fixup object layer */ -static void +staticfn void show_mon_or_warn(coordxy x, coordxy y, int monglyph) { struct obj *o; @@ -513,7 +519,7 @@ show_mon_or_warn(coordxy x, coordxy y, int monglyph) * a worm tail. * */ -static void +staticfn void display_monster( coordxy x, coordxy y, /* display position */ struct monst *mon, /* monster to display */ @@ -527,16 +533,17 @@ display_monster( mgendercode = mon->female ? FEMALE : MALE; /* - * We must do the mimic check first. If the mimic is mimicing something, + * We must do the mimic check first. If the mimic is mimicking something, * and the location is in sight, we have to change the hero's memory * so that when the position is out of sight, the hero remembers what - * the mimic was mimicing. + * the mimic was mimicking. */ if (mon_mimic && (sightflags == PHYSICALLY_SEEN)) { switch (M_AP_TYPE(mon)) { 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)); @@ -558,7 +565,7 @@ display_monster( if (!sensed) { show_glyph(x, y, glyph); /* override real topology with mimic's fake one */ - gl.lastseentyp[x][y] = cmap_to_type(sym); + svl.lastseentyp[x][y] = cmap_to_type(sym); } break; } @@ -571,7 +578,7 @@ display_monster( obj.ox = x; obj.oy = y; obj.otyp = mon->mappearance; - /* might be mimicing a corpse or statue */ + /* might be mimicking a corpse or statue */ obj.corpsenm = has_mcorpsenm(mon) ? MCORPSENM(mon) : PM_TENGU; map_object(&obj, !sensed); break; @@ -586,7 +593,7 @@ display_monster( } /* switch M_AP_TYPE() */ } - /* If mimic is unsuccessfully mimicing something, display the monster. */ + /* If mimic is unsuccessfully mimicking something, display the monster. */ if (!mon_mimic || sensed) { int num; @@ -638,7 +645,7 @@ display_monster( * * Do not call for worm tails. */ -static void +staticfn void display_warning(struct monst *mon) { coordxy x = mon->mx, y = mon->my; @@ -670,19 +677,59 @@ warning_of(struct monst *mon) return wl; } +/* used by newsym() to decide whether to show a monster or a visible gas + cloud region when both are at the same spot; caller deals with region */ +staticfn boolean +mon_overrides_region( + struct monst *mon, /* might be Null */ + coordxy mx, coordxy my) /* won't match mon->mx,my if long worm's tail */ +{ + int r; + + /* this is redundant because newsym() doesn't call us when swallowed */ + if (u.uswallow && (!mon || mon != u.ustuck)) + return FALSE; + + if (mon) { + /* when not a worm tail, show mon if sensed rather than seen */ + if (mx == mon->mx && my == mon->my + && (sensemon(mon) || mon_warning(mon))) + return TRUE; + + /* even if worm tail; + check whether the spot is adjacent and 'mon' would be visible + there if the gas cloud wasn't interfering with normal vision; + _mon_visible() handles mon->mundetected; don't need to check + infravision when monster is adjacent */ + r = (u.xray_range > 1) ? u.xray_range : 1; + if (!Blind && _mon_visible(mon) + && M_AP_TYPE(mon) != M_AP_FURNITURE + && M_AP_TYPE(mon) != M_AP_OBJECT + && distu(mx, my) <= r * (r + 1)) + return TRUE; + } + + /* if not overriding region for current mon, propagate "remembered, + unseen monster" */ + return glyph_is_invisible(levl[mx][my].glyph) ? TRUE : FALSE; +} + +#ifdef HANGUPHANDLING +#define _suppress_map_output() \ + (gi.in_mklev || program_state.saving || program_state.restoring \ + || program_state.done_hup) +#else +#define _suppress_map_output() \ + (gi.in_mklev || program_state.saving || program_state.restoring) +#endif + /* map or status window might not be ready for output during level creation or game restoration (something like u.usteed which affects display of the hero and also a status condition might not be set up yet) */ boolean suppress_map_output(void) { - if (gi.in_mklev || gp.program_state.saving || gp.program_state.restoring) - return TRUE; -#ifdef HANGUPHANDLING - if (gp.program_state.done_hup) - return TRUE; -#endif - return FALSE; + return _suppress_map_output(); } /* @@ -715,10 +762,11 @@ feel_location(coordxy x, coordxy y) { struct rm *lev; struct obj *boulder; - register struct monst *mon; + struct monst *mon; + struct engr *ep; /* replicate safeguards used by newsym(); might not be required here */ - if (suppress_map_output()) + if (_suppress_map_output()) return; if (!isok(x, y)) @@ -758,7 +806,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) && door_is_closed(lev))) { map_background(x, y, 1); } else if ((boulder = sobj_at(BOULDER, x, y)) != 0) { @@ -823,6 +871,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) { @@ -840,14 +891,14 @@ feel_location(coordxy x, coordxy y) */ if (uchain && uchain->where == OBJ_FLOOR && uchain->ox == x && uchain->oy == y - && gl.level.objects[x][y] == uchain) + && svl.level.objects[x][y] == uchain) u.bc_felt |= BC_CHAIN; else u.bc_felt &= ~BC_CHAIN; /* do not feel the chain */ if (uball && uball->where == OBJ_FLOOR && uball->ox == x && uball->oy == y - && gl.level.objects[x][y] == uball) + && svl.level.objects[x][y] == uball) u.bc_felt |= BC_BALL; else u.bc_felt &= ~BC_BALL; /* do not feel the ball */ @@ -863,7 +914,7 @@ feel_location(coordxy x, coordxy y) show_glyph(x, y, lev->glyph = cmap_to_glyph(S_corr)); } /* draw monster on top if we can sense it */ - if ((x != u.ux || y != u.uy) && (mon = m_at(x, y)) != 0 && sensemon(mon)) + if (!u_at(x, y) && (mon = m_at(x, y)) != 0 && sensemon(mon)) display_monster(x, y, mon, (tp_sensemon(mon) || MATCH_WARN_OF_MON(mon)) ? PHYSICALLY_SEEN @@ -882,10 +933,11 @@ newsym(coordxy x, coordxy y) struct monst *mon; int see_it; boolean worm_tail; - register struct rm *lev = &(levl[x][y]); + 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()) + if (_suppress_map_output()) return; /* only permit updating the hero when swallowed */ @@ -921,18 +973,33 @@ 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, * pools and moats. - * However, sensed monsters take precedence over all regions. + * However, sensed monsters (via detection or telepathy or + * warning) take precedence over all regions. + * Adjacent monsters also take precedence if they would be + * seen when there's no gas region. + * + * FIXME: + * The adjacency checking [in mon_overrides_region()] works + * when the hero is outside the region and the monster is + * inside, and when they're both inside, but not when the + * hero is inside and monster outside (because 'reg' will be + * Null for mon's ). Checking whether hero is inside + * a region for every newsym() seems excessive. The hero is + * usually blind when in a gas cloud so the problem is less + * noticeable then it might otherwise be. */ - if (reg - && (ACCESSIBLE(lev->typ) - || (reg->visible && is_pool_or_lava(x, y))) - && (!mon || worm_tail || !sensemon(mon))) { - show_region(reg, x, y); - return; + if (reg && (ACCESSIBLE(lev->typ) + || (reg->visible && is_pool_or_lava(x, y)))) { + if (!mon_overrides_region(mon, x, y)) { + show_region(reg, x, y); + return; + } } if (u_at(x, y)) { @@ -945,6 +1012,8 @@ newsym(coordxy x, coordxy y) if (see_self) display_self(); } else { + boolean show = FALSE; + see_it = mon && (mon_visible(mon) || (!worm_tail && (tp_sensemon(mon) || MATCH_WARN_OF_MON(mon)))); @@ -957,17 +1026,18 @@ newsym(coordxy x, coordxy y) if (tt == BEAR_TRAP || is_pit(tt) || tt == WEB) trap->tseen = 1; } - _map_location(x, y, 0); /* map under the monster */ + _map_location(x, y, show); /* map under the monster */ /* also gets rid of any invisibility glyph */ display_monster(x, y, mon, see_it ? PHYSICALLY_SEEN : DETECTED, worm_tail); - } else if (mon && mon_warning(mon) && !is_worm_tail(mon)) { + } else if (mon && mon_warning(mon) && !worm_tail) { 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. */ @@ -981,7 +1051,7 @@ newsym(coordxy x, coordxy y) && ((see_it = (tp_sensemon(mon) || MATCH_WARN_OF_MON(mon) || (see_with_infrared(mon) && mon_visible(mon)))) != 0 - || Detect_monsters)) { + || (Detect_monsters && !is_worm_tail(mon)))) { /* Seen or sensed monsters are printed every time. This also gets rid of any invisibility glyph. */ display_monster(x, y, mon, see_it ? 0 : DETECTED, @@ -1035,7 +1105,7 @@ newsym(coordxy x, coordxy y) void shieldeff(coordxy x, coordxy y) { - register int i; + int i; if (!flags.sparkle) return; @@ -1049,7 +1119,7 @@ shieldeff(coordxy x, coordxy y) } } -static int +staticfn int tether_glyph(coordxy x, coordxy y) { int tdx, tdy; @@ -1076,6 +1146,8 @@ tether_glyph(coordxy x, coordxy y) * * DISP_BEAM - Display the given glyph at each location, but do not erase * any until the close call. + * DISP_ALL - Same as DISP_BEAM except glyph is shown at the specified + * spot even when that spot can't be seen. * DISP_TETHER - Display a tether glyph at each location, and the tethered * object at the farthest location, but do not erase any * until the return trip or close. @@ -1141,7 +1213,7 @@ tmp_at(coordxy x, coordxy y) case DISP_END: if (tglyph->style == DISP_BEAM || tglyph->style == DISP_ALL) { - register int i; + int i; /* Erase (reset) from source to end */ for (i = 0; i < tglyph->sidx; i++) @@ -1232,8 +1304,8 @@ flash_glyph_at(coordxy x, coordxy y, int tg, int rpt) rpt *= 2; /* two loop iterations per 'count' */ glyph[0] = tg; - glyph[1] = (gl.level.flags.hero_memory) ? levl[x][y].glyph - : back_to_glyph(x, y); + glyph[1] = (svl.level.flags.hero_memory) ? levl[x][y].glyph + : back_to_glyph(x, y); /* even iteration count (guaranteed) ends with glyph[1] showing; caller might want to override that, but no newsym() calls here in case caller has tinkered with location visibility */ @@ -1410,7 +1482,7 @@ under_ground(int mode) void see_monsters(void) { - register struct monst *mon; + struct monst *mon; int new_warn_obj_cnt = 0; if (gd.defer_see_monsters) @@ -1433,7 +1505,7 @@ see_monsters(void) if (mon->wormno) see_wsegs(mon); if (Warn_of_mon - && (gc.context.warntype.obj & mon->data->mflags2) != 0L) + && (svc.context.warntype.obj & mon->data->mflags2) != 0L) new_warn_obj_cnt++; } @@ -1451,7 +1523,7 @@ see_monsters(void) newsym(u.ux, u.uy); } -static void +staticfn void mimic_light_blocking(struct monst *mtmp) { if (mtmp->minvis && is_lightblocker_mappear(mtmp)) { @@ -1463,7 +1535,7 @@ mimic_light_blocking(struct monst *mtmp) } /* - * Block/unblock light depending on what a mimic is mimicing and if it's + * Block/unblock light depending on what a mimic is mimicking and if it's * invisible or not. Should be called only when the state of See_invisible * changes. */ @@ -1480,7 +1552,7 @@ set_mimic_blocking(void) void see_objects(void) { - register struct obj *obj; + struct obj *obj; for (obj = fobj; obj; obj = obj->nobj) if (vobj_at(obj->ox, obj->oy) == obj) @@ -1537,43 +1609,53 @@ see_traps(void) int glyph; for (trap = gf.ftrap; trap; trap = trap->ntrap) { - glyph = glyph_at(trap->tx, trap->ty); + glyph = _glyph_at(trap->tx, trap->ty); if (glyph_is_trap(glyph)) newsym(trap->tx, trap->ty); } } -/* glyph, ttychar, { glyphflags, { sym.color, sym.symidx }, - tileidx, u } */ +/* glyph, ttychar, framecolor, + { glyphflags, { NO_COLOR, sym.symidx }, + customcolor, color256idx, tileidx, u } */ static glyph_info no_ginfo = { - NO_GLYPH, ' ', NO_COLOR, { MG_BADXY, { NO_COLOR, 0 }, 0 + NO_GLYPH, ' ', NO_COLOR, + { MG_BADXY, { NO_COLOR, 0 }, + 0, + 0U, 0U #ifdef ENHANCED_SYMBOLS - , 0 + , 0 #endif } }; + #ifndef UNBUFFERED_GLYPHINFO +/* Note that the 'glyph' argument is not used in the expansion + * of this !UNBUFFERED_GLYPHINFO (default) variation, but is + * a requirement for the UNBUFFERED_GLYPHINFO variation */ #define Glyphinfo_at(x, y, glyph) \ (((x) < 0 || (y) < 0 || (x) >= COLNO || (y) >= ROWNO) ? &no_ginfo \ : &gg.gbuf[(y)][(x)].glyphinfo) #else static glyph_info ginfo; -#define Glyphinfo_at(x, y, glyph) glyphinfo_at(x, y, glyph) +staticfn glyph_info *glyphinfo_at(coordxy, coordxy, int); +#define Glyphinfo_at(x, y, glyph) glyphinfo_at((x), (y), (glyph)) #endif #ifdef TILES_IN_GLYPHMAP extern const glyph_info nul_glyphinfo; /* tile.c */ #else -/* glyph, ttychar, { glyphflags, { sym.color, sym.symidx }, +/* glyph, ttychar, framecolor, { glyphflags, { sym.symidx }, nhcolor, tileidx, 0} */ const glyph_info nul_glyphinfo = { NO_GLYPH, ' ', NO_COLOR, { /* glyph_map */ MG_UNEXPL, { NO_COLOR, SYM_UNEXPLORED + SYM_OFF_X }, - 0 + 0, + 0U, 0U #ifdef ENHANCED_SYMBOLS - , 0 + , 0 #endif } }; @@ -1583,9 +1665,11 @@ const glyph_info nul_glyphinfo = { extern glyph_map glyphmap[MAX_GLYPH]; /* from tile.c */ #else glyph_map glyphmap[MAX_GLYPH] = { - { 0U, { 0, 0}, 0 + { 0U, { NO_COLOR, 0 }, + 0, + 0U, 0U #ifdef ENHANCED_SYMBOLS - , 0 + , 0 #endif } }; @@ -1608,17 +1692,32 @@ doredraw(void) return ECMD_OK; } +/* the main refresh-the-screen routine */ void docrt(void) +{ + docrt_flags(docrtRecalc); +} + +/* docrt() with finer control */ +void +docrt_flags(int refresh_flags) { coordxy x, y; - register struct rm *lev; + struct rm *lev; + boolean maponly = (refresh_flags & docrtMapOnly) != 0, + redrawonly = (refresh_flags & docrtRefresh) != 0, + nocls = (refresh_flags & docrtNocls) != 0; - if (!u.ux || gp.program_state.in_docrt) + if (!u.ux || program_state.in_docrt) return; /* display isn't ready yet */ - gp.program_state.in_docrt = TRUE; + program_state.in_docrt = TRUE; + if (redrawonly) { + redraw_map(FALSE); + goto post_map; + } if (u.uswallow) { swallowed(1); goto post_map; @@ -1627,7 +1726,7 @@ docrt(void) under_water(1); goto post_map; } - if (u.uburied) { + if (u.uburied) { /* [not implemented] */ under_ground(1); goto post_map; } @@ -1640,7 +1739,8 @@ docrt(void) * + fills the physical screen with the symbol for rock * + clears the glyph buffer */ - cls(); + if (!nocls) + cls(); /* display memory */ for (x = 1; x < COLNO; x++) { @@ -1657,17 +1757,20 @@ docrt(void) post_map: - /* perm_invent */ - update_inventory(); - - gc.context.botlx = 1; /* force a redraw of the bottom line */ - gp.program_state.in_docrt = FALSE; + if (!maponly) { + /* perm_invent */ + update_inventory(); + /* status */ + disp.botlx = TRUE; /* force a redraw of the bottom lines */ + /* note: caller needs to call bot() to actually redraw status */ + } + program_state.in_docrt = FALSE; } /* for panning beyond a clipped region; resend the current map data to the interface rather than use docrt()'s regeneration of that data */ void -redraw_map(void) +redraw_map(boolean cursor_on_u) { coordxy x, y; int glyph; @@ -1691,12 +1794,13 @@ redraw_map(void) */ for (y = 0; y < ROWNO; ++y) for (x = 1; x < COLNO; ++x) { - glyph = glyph_at(x, y); /* not levl[x][y].glyph */ - get_bkglyph_and_framecolor(x, y, &bkglyphinfo.glyph, &bkglyphinfo.framecolor); + glyph = _glyph_at(x, y); /* not levl[x][y].glyph */ + get_bkglyph_and_framecolor(x, y, &bkglyphinfo.glyph, + &bkglyphinfo.framecolor); print_glyph(WIN_MAP, x, y, Glyphinfo_at(x, y, glyph), &bkglyphinfo); } - flush_screen(1); + flush_screen(cursor_on_u); #ifndef UNBUFFERED_GLYPHINFO nhUse(glyph); #endif @@ -1769,7 +1873,15 @@ show_glyph(coordxy x, coordxy y, int glyph) #ifndef UNBUFFERED_GLYPHINFO glyph_info glyphinfo; #endif + boolean show_glyph_change = FALSE; + int oldglyph; + /* don't process map glyphs when saving, restoring, or in_mklev */ + if (_suppress_map_output()) + return; + + //if (glyph == 3972 || glyph == 3988) + // __debugbreak(); /* * Check for bad positions and glyphs. */ @@ -1896,6 +2008,27 @@ show_glyph(coordxy x, coordxy y, int glyph) map_glyphinfo(x, y, glyph, 0, &glyphinfo); #endif + oldglyph = gg.gbuf[y][x].glyphinfo.glyph; + + if (a11y.glyph_updates && !a11y.mon_notices_blocked + && !program_state.in_docrt + && !program_state.in_getlev + && (oldglyph != glyph || gg.gbuf[y][x].gnew)) { + int c = glyph_to_cmap(glyph); + + if ((glyph_is_nothing(oldglyph) || glyph_is_unexplored(oldglyph) + || is_cmap_furniture(c)) + && !is_cmap_wall(c) && !is_cmap_room(c)) { + if ((a11y.mon_notices && glyph_is_monster(glyph)) + || (glyph_is_monster(oldglyph)) + || u_at(x, y)) { + ; /* nothing */ + } else { + show_glyph_change = TRUE; + } + } + } + if (gg.gbuf[y][x].glyphinfo.glyph != glyph #ifndef UNBUFFERED_GLYPHINFO /* flags might change (single object vs pile, monster tamed or pet @@ -1905,6 +2038,7 @@ show_glyph(coordxy x, coordxy y, int glyph) but that triggers full redraw so doesn't matter here); still, be thorough and check everything */ || gg.gbuf[y][x].glyphinfo.ttychar != glyphinfo.ttychar + || gg.gbuf[y][x].glyphinfo.gm.customcolor != glyphinfo.gm.customcolor || gg.gbuf[y][x].glyphinfo.gm.glyphflags != glyphinfo.gm.glyphflags || gg.gbuf[y][x].glyphinfo.gm.sym.color != glyphinfo.gm.sym.color || gg.gbuf[y][x].glyphinfo.gm.tileidx != glyphinfo.gm.tileidx @@ -1922,6 +2056,20 @@ show_glyph(coordxy x, coordxy y, int glyph) if (gg.gbuf_stop[y] < x) gg.gbuf_stop[y] = x; } + + if (show_glyph_change) { + char buf[BUFSZ]; + coord cc; + int sym = 0; + const char *firstmatch = 0; + boolean tmp_accessiblemsg = a11y.accessiblemsg; + + a11y.accessiblemsg = TRUE; + cc.x = x, cc.y = y; + do_screen_description(cc, TRUE, sym, buf, &firstmatch, NULL); + pline_xy(x, y, "%s.", firstmatch); + a11y.accessiblemsg = tmp_accessiblemsg; + } } /* @@ -1940,11 +2088,14 @@ show_glyph(coordxy x, coordxy y, int glyph) static gbuf_entry nul_gbuf = { 0, /* gnew */ - { GLYPH_UNEXPLORED, (unsigned) ' ', NO_COLOR, /* glyphinfo.glyph */ + { GLYPH_UNEXPLORED, (unsigned) ' ', NO_COLOR, + /* glyphinfo.glyph, glyphinfo.ttychar */ /* glyphinfo.gm */ - { MG_UNEXPL, { (unsigned) NO_COLOR, 0 }, 0 + { MG_UNEXPL, { NO_COLOR, 0 }, + 0, + 0U, 0U #ifdef ENHANCED_SYMBOLS - , 0 + , 0 #endif } } @@ -1956,7 +2107,7 @@ static gbuf_entry nul_gbuf = { void clear_glyph_buffer(void) { - register coordxy x, y; + coordxy x, y; gbuf_entry *gptr = &gg.gbuf[0][0]; glyph_info *giptr = #ifndef UNBUFFERED_GLYPHINFO @@ -1971,10 +2122,13 @@ clear_glyph_buffer(void) || giptr->gm.sym.color != nul_gbuf.glyphinfo.gm.sym.color || giptr->gm.glyphflags != nul_gbuf.glyphinfo.gm.glyphflags + || giptr->gm.customcolor + != nul_gbuf.glyphinfo.gm.customcolor || giptr->gm.tileidx != nul_gbuf.glyphinfo.gm.tileidx) #else - nul_gbuf.gnew = (giptr->glyphinfo.ttychar != ' ' + nul_gbuf.gnew = (giptr->ttychar != ' ' || giptr->gm.sym.color != NO_COLOR + || giptr->gm.customcolor != 0 || (giptr->gm.glyphflags & ~MG_UNEXPL) != 0) #endif ? 1 : 0; @@ -1993,9 +2147,9 @@ clear_glyph_buffer(void) void row_refresh(coordxy start, coordxy stop, coordxy y) { - register coordxy x; + coordxy x; int glyph; - register boolean force; + boolean force; gbuf_entry *gptr = &gg.gbuf[0][0]; glyph_info bkglyphinfo = nul_glyphinfo; glyph_info *giptr = @@ -2010,10 +2164,12 @@ row_refresh(coordxy start, coordxy stop, coordxy y) force = (giptr->ttychar != nul_gbuf.glyphinfo.ttychar || giptr->gm.sym.color != nul_gbuf.glyphinfo.gm.sym.color || giptr->gm.glyphflags != nul_gbuf.glyphinfo.gm.glyphflags + || giptr->gm.customcolor != nul_gbuf.glyphinfo.gm.customcolor || giptr->gm.tileidx != nul_gbuf.glyphinfo.gm.tileidx) #else force = (giptr->ttychar != ' ' || giptr->gm.sym.color != NO_COLOR + || giptr->gm.gm.customcolor != 0 || (giptr->gm.glyphflags & ~MG_UNEXPL) != 0) #endif ? 1 : 0; @@ -2039,7 +2195,7 @@ cls(void) return; in_cls = TRUE; display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */ - gc.context.botlx = 1; /* force update of botl window */ + disp.botlx = TRUE; /* force update of botl window */ clear_nhwindow(WIN_MAP); /* clear physical screen */ clear_glyph_buffer(); /* force gbuf[][].glyph to unexplored */ @@ -2057,12 +2213,12 @@ flush_screen(int cursor_on_u) */ static int flushing = 0; static int delay_flushing = 0; - register coordxy x, y; + coordxy x, y; glyph_info bkglyphinfo = nul_glyphinfo; int bkglyph; /* 3.7: don't update map, status, or perm_invent during save/restore */ - if (suppress_map_output()) + if (_suppress_map_output()) return; if (cursor_on_u == -1) @@ -2073,26 +2229,30 @@ flush_screen(int cursor_on_u) return; /* if already flushing then return */ flushing = 1; #ifdef HANGUPHANDLING - if (gp.program_state.done_hup) + if (program_state.done_hup) return; #endif /* get this done now, before we place the cursor on the hero */ - if (gc.context.botl || gc.context.botlx) + if (disp.botl || disp.botlx) bot(); - else if (iflags.time_botl) + else if (disp.time_botl) timebot(); for (y = 0; y < ROWNO; y++) { - register gbuf_entry *gptr = &gg.gbuf[y][x = gg.gbuf_start[y]]; + gbuf_entry *gptr = &gg.gbuf[y][x = gg.gbuf_start[y]]; for (; x <= gg.gbuf_stop[y]; gptr++, x++) { - get_bkglyph_and_framecolor(x, y, &bkglyph, &bkglyphinfo.framecolor); + get_bkglyph_and_framecolor(x, y, &bkglyph, + &bkglyphinfo.framecolor); if (gptr->gnew - || (gw.wsettings.map_frame_color != NO_COLOR && bkglyphinfo.framecolor != NO_COLOR)) { - map_glyphinfo(x, y, bkglyph, 0, &bkglyphinfo); /* won't touch framecolor */ + || (gw.wsettings.map_frame_color != NO_COLOR + && bkglyphinfo.framecolor != NO_COLOR)) { + /* map_glyphinfo() won't touch framecolor */ + map_glyphinfo(x, y, bkglyph, 0, &bkglyphinfo); print_glyph(WIN_MAP, x, y, - Glyphinfo_at(x, y, gptr->glyph), &bkglyphinfo); + Glyphinfo_at(x, y, gptr->glyphinfo.glyph), + &bkglyphinfo); gptr->gnew = 0; } } @@ -2133,6 +2293,15 @@ back_to_glyph(coordxy x, coordxy y) if (defsym == S_altar) { return altar_to_glyph(levl[x][y].altarmask); } + /* 7 engravings share 2 cmap entries */ + if (defsym == S_engroom || defsym == S_engrcorr) { + struct engr *ep = engr_at(x, y); + if (ep) + return engraving_to_glyph(ep); + else /* huh? */ + impossible("back_to_glyph: no engraving at %d, %d", x, y); + /* fall through to cmap_to_glyph case */ + } /* 7 magic platform colors share one cmap entry */ if (defsym == S_magicplatform) { return magicplatform_to_glyph(x, y); @@ -2150,7 +2319,7 @@ back_to_defsym(coordxy x, coordxy y) switch (ptr->typ) { case SCORR: case STONE: - idx = gl.level.flags.arboreal ? S_tree : S_stone; + idx = svl.level.flags.arboreal ? S_tree : S_stone; break; case ROOM: idx = S_room; @@ -2294,7 +2463,7 @@ back_to_defsym(coordxy x, coordxy y) * If you don't want a patchwork monster while hallucinating, decide on * a random monster in swallowed() and don't use what_mon() here. */ -static int +staticfn int swallow_to_glyph(int mnum, int loc) { int m_3 = what_mon(mnum, rn2_on_display_rng) << 3; @@ -2311,7 +2480,7 @@ swallow_to_glyph(int mnum, int loc) * * Change the given zap direction and beam type into a glyph. Each beam * type has four glyphs, one for each of the symbols below. The order of - * the zap symbols [0-3] as defined in rm.h are: + * the zap symbols [0-3] as defined in defsym.h are: * * | S_vbeam ( 0, 1) or ( 0,-1) * - S_hbeam ( 1, 0) or (-1, 0) @@ -2340,14 +2509,14 @@ glyph_at(coordxy x, coordxy y) { if (x < 0 || y < 0 || x >= COLNO || y >= ROWNO) return cmap_to_glyph(S_room); /* XXX */ - return gg.gbuf[y][x].glyphinfo.glyph; + return gg.gbuf[y][x].glyphinfo.glyph; /* _glyph_at(x,y) */ } #ifdef UNBUFFERED_GLYPHINFO glyph_info * glyphinfo_at(coordxy x, coordxy y, int glyph) { - map_glyphinfo(x, y, glyph, 0, &ginfo); + map_glyphinfo(x, y, glyph, 0, &ginfo); /* ginfo declared at file scope */ return &ginfo; } #endif @@ -2364,7 +2533,7 @@ glyphinfo_at(coordxy x, coordxy y, int glyph) * */ -static void +staticfn void get_bkglyph_and_framecolor( coordxy x, coordxy y, int *bkglyph, @@ -2378,7 +2547,7 @@ get_bkglyph_and_framecolor( switch (lev->typ) { case SCORR: case STONE: - idx = gl.level.flags.arboreal ? S_tree : S_stone; + idx = svl.level.flags.arboreal ? S_tree : S_stone; break; case ROOM: idx = S_room; @@ -2417,7 +2586,7 @@ get_bkglyph_and_framecolor( } if (!cansee(x, y) && (!lev->waslit || flags.dark_room)) { - /* Floor spaces are dark if unlit. Corridors are dark if unlit. */ + /* Floor spaces and corridors are dark if unlit. */ if (lev->typ == CORR && idx == S_litcorr) idx = S_corr; else if (idx == S_room) @@ -2482,7 +2651,6 @@ map_glyphinfo( * as well as to regular display.) */ if (is_you) { -#ifdef TEXTCOLOR if (!iflags.use_color || Upolyd || glyph != hero_glyph) { ; /* color tweak not needed (!use_color) or not wanted (poly'd or riding--which uses steed's color, not hero's) */ @@ -2492,7 +2660,6 @@ map_glyphinfo( by newsym()); we change the color to same as human hero */ glyphinfo->gm.sym.color = HI_DOMESTIC; } -#endif /* accessibility This unchanging display character for hero was requested by a blind player to enhance screen reader use. @@ -2520,7 +2687,6 @@ map_glyphinfo( * here. Likely the ones for object materials will be permanent, since * adding glyphs and tiles for every material variant of every object would * be an enormous amount of work. */ -#ifdef TEXTCOLOR /* isok is used because this is sometimes called with 0,0 */ if (iflags.use_color && isok(x, y)) { /* object or statue, which might be made of a non-default material or on @@ -2613,12 +2779,10 @@ map_glyphinfo( ? CLR_GRAY : CLR_WHITE; } } -#endif /* TEXTCOLOR */ glyphinfo->ttychar = gs.showsyms[glyphinfo->gm.sym.symidx]; glyphinfo->glyph = glyph; } -#ifdef TEXTCOLOR /* * This must be the same order as used for buzz() in zap.c. * The zap_color_ and altar_color_ enums are in decl.h. @@ -2637,8 +2801,8 @@ const int engravingcolors[] = { engraving_color_mark1, engraving_color_mark2, engraving_color_mark3, engraving_color_blood }; -const int magicplatformcolors[] = { - platform_color_default, platform_color_red, platform_color_orange, +const int magicplatformextracolors[] = { + platform_color_red, platform_color_orange, platform_color_yellow, platform_color_green, platform_color_blue, platform_color_violet, }; @@ -2655,9 +2819,6 @@ int wallcolors[sokoban_walls + 1] = { /* CLR_GRAY, CLR_BROWN, CLR_RED, CLR_GRAY, CLR_BRIGHT_BLUE, */ }; -#endif /* text color */ - -#ifdef TEXTCOLOR #define zap_color(n) color = iflags.use_color ? zapcolors[n] : NO_COLOR #define cmap_color(n) color = iflags.use_color ? defsyms[n].color : NO_COLOR #define obj_color(n) color = iflags.use_color ? objects[n].oc_color : NO_COLOR @@ -2675,30 +2836,10 @@ int wallcolors[sokoban_walls + 1] = { color = iflags.use_color ? explodecolors[n] : NO_COLOR #define wall_color(n) color = iflags.use_color ? wallcolors[n] : NO_COLOR #define altar_color(n) color = iflags.use_color ? altarcolors[n] : NO_COLOR -#define engraving_color(n) color = iflags.use_color ? engravingcolors[n] : NO_COLOR -#define magicplatform_color(n) \ - color = iflags.use_color ? magicplatformcolors[n] : NO_COLOR -#else /* no text color */ - -#define zap_color(n) -#define cmap_color(n) -#define obj_color(n) -#define mon_color(n) -#define invis_color(n) -#define pet_color(c) -#define warn_color(n) -#define explode_color(n) -#define wall_color(n) -#define altar_color(n) -#define engraving_color(n) -#define magicplatform_color(n) -#endif - -#if 0 -#define is_objpile(x, y) \ - (!Hallucination && gl.level.objects[(x)][(y)] \ - && gl.level.objects[(x)][(y)]->nexthere) -#endif +#define engraving_color(n) color = \ + iflags.use_color ? engravingcolors[n % max_engraving_glyphtypes] : NO_COLOR +#define magicplatform_extracolor(n) \ + color = iflags.use_color ? magicplatformextracolors[n] : NO_COLOR /* reset_glyphmap(trigger) @@ -2721,7 +2862,7 @@ void reset_glyphmap(enum glyphmap_change_triggers trigger) { int glyph; - register int offset; + int offset; int color = NO_COLOR; /* condense multiple tests in macro version down to single */ @@ -2783,6 +2924,21 @@ reset_glyphmap(enum glyphmap_change_triggers trigger) >= 0) { /* warn flash */ gmap->sym.symidx = offset + SYM_OFF_W; warn_color(offset); + } else if ((offset = (glyph - GLYPH_MAGICPLATFORM_OFF)) >= 0) { + gmap->sym.symidx = S_magicplatform; + magicplatform_extracolor(offset); + } else if ((offset = (glyph - GLYPH_ENGRAVING_OFF)) >= 0) { + if (offset >= max_engraving_glyphtypes) { + gmap->sym.symidx = S_engrcorr; + offset -= max_engraving_glyphtypes; + } + else { + gmap->sym.symidx = S_engroom; + } + engraving_color(offset); + } else if ((offset = (glyph - GLYPH_CMAP_D_OFF)) >= 0) { + gmap->sym.symidx = S_grass + offset + SYM_OFF_P; + cmap_color(S_grass + offset); } else if ((offset = (glyph - GLYPH_EXPLODE_FROSTY_OFF)) >= 0) { gmap->sym.symidx = S_expl_tl + offset + SYM_OFF_P; explode_color(expl_frosty); @@ -2817,62 +2973,59 @@ reset_glyphmap(enum glyphmap_change_triggers trigger) zap_color((offset >> 2)); } else if ((offset = (glyph - GLYPH_CMAP_B_OFF)) >= 0) { int cmap = S_grave + offset; + int sym = gs.showsyms[cmap + SYM_OFF_P]; gmap->sym.symidx = cmap + SYM_OFF_P; cmap_color(cmap); if (!iflags.use_color) { + unsigned spec_cmap = 0; + /* try to provide a visible difference between water and lava - if they use the same symbol and color is disabled */ - if (cmap == S_lava - && (gs.showsyms[gmap->sym.symidx] - == gs.showsyms[S_pool + SYM_OFF_P] - || gs.showsyms[gmap->sym.symidx] - == gs.showsyms[S_water + SYM_OFF_P])) { - gmap->glyphflags |= MG_BW_LAVA; - - /* similar for floor [what about empty doorway?] and ice */ - } else if (cmap == S_ice - && (gs.showsyms[gmap->sym.symidx] - == gs.showsyms[S_room + SYM_OFF_P] - || gs.showsyms[gmap->sym.symidx] - == gs.showsyms[S_darkroom - + SYM_OFF_P])) { - gmap->glyphflags |= MG_BW_ICE; - - /* and for fountain vs sink */ - } else if (cmap == S_sink - && (gs.showsyms[gmap->sym.symidx] - == gs.showsyms[S_fountain + SYM_OFF_P])) { - gmap->glyphflags |= MG_BW_SINK; + if they use the same symbol and color is disabled; + similar for floor and ice, for fountain vs sink, and for + corridor engravings (CMAP_A below) */ + switch (cmap) { + case S_lava: + case S_lavawall: + if (sym == gs.showsyms[S_pool + SYM_OFF_P] + || sym == gs.showsyms[S_water + SYM_OFF_P]) + spec_cmap = MG_BW_LAVA; + break; + case S_ice: + if (sym == gs.showsyms[S_room + SYM_OFF_P] + || sym == gs.showsyms[S_darkroom + SYM_OFF_P]) + spec_cmap = MG_BW_ICE; + break; + case S_sink: + if (sym == gs.showsyms[S_fountain + SYM_OFF_P]) + spec_cmap = MG_BW_SINK; + break; } + gmap->glyphflags |= spec_cmap; } - } else if ((offset = (glyph - GLYPH_MAGICPLATFORM_OFF)) >= 0) { - /* magical platform */ - gmap->sym.symidx = S_magicplatform + SYM_OFF_P; - magicplatform_color(offset); - } else if ((offset = (glyph - GLYPH_ENGRAVING_OFF)) >= 0) { - /* engraving of various types */ - gmap->sym.symidx = S_engraving + SYM_OFF_P; - engraving_color(offset); } else if ((offset = (glyph - GLYPH_ALTAR_OFF)) >= 0) { /* unaligned, chaotic, neutral, lawful, other altar */ gmap->sym.symidx = S_altar + SYM_OFF_P; altar_color(offset); } else if ((offset = (glyph - GLYPH_CMAP_A_OFF)) >= 0) { - int cmap = S_ndoor + offset; + int sym, cmap = S_ndoor + offset; + gmap->sym.symidx = cmap + SYM_OFF_P; cmap_color(cmap); + sym = gs.showsyms[gmap->sym.symidx]; /* * Some specialty color mappings not hardcoded in data init */ -#ifdef TEXTCOLOR /* provide a visible difference if normal and lit corridor use the same symbol */ - if ((cmap == S_litcorr) - && gs.showsyms[gmap->sym.symidx] - == gs.showsyms[S_corr + SYM_OFF_P]) { + if (cmap == S_litcorr + && sym == gs.showsyms[S_corr + SYM_OFF_P]) { color = CLR_WHITE; -#endif + /* likewise for corridor and engraving-in-corridor */ + } else if (cmap == S_engrcorr + && (sym == gs.showsyms[S_corr + SYM_OFF_P] + || sym == gs.showsyms[S_litcorr + SYM_OFF_P])) { + gmap->glyphflags |= MG_BW_ENGR; } } else if ((offset = (glyph - GLYPH_CMAP_SOKO_OFF)) >= 0) { gmap->sym.symidx = S_vwall + offset + SYM_OFF_P; @@ -2958,15 +3111,13 @@ reset_glyphmap(enum glyphmap_change_triggers trigger) if (gs.showsyms[pet_override] != ' ') gmap->sym.symidx = SYM_PET_OVERRIDE + SYM_OFF_X; } -#ifdef TEXTCOLOR /* Turn off color if no color defined. */ if (!has_color(color) || !iflags.use_color) -#endif color = NO_COLOR; gmap->sym.color = color; } - gg.glyph_reset_timestamp = gm.moves; + gg.glyph_reset_timestamp = svm.moves; } /* ------------------------------------------------------------------------ */ @@ -2974,8 +3125,8 @@ reset_glyphmap(enum glyphmap_change_triggers trigger) #ifdef WA_VERBOSE -static const char *type_to_name(int); -static void error4(coordxy, coordxy, int, int, int, int); +staticfn const char *type_to_name(int); +staticfn void error4(coordxy, coordxy, int, int, int, int); static int bad_count[MAX_TYPE]; /* count of positions flagged as bad */ static const char *const type_names[MAX_TYPE] = { @@ -2988,13 +3139,13 @@ static const char *const type_names[MAX_TYPE] = { "DRAWBRIDGE_DOWN", "AIR", "CLOUD" }; -static const char * +staticfn const char * type_to_name(int type) { return (type < 0 || type >= MAX_TYPE) ? "unknown" : type_names[type]; } -static void +staticfn void error4(coordxy x, coordxy y, int a, int b, int c, int dd) { pline("set_wall_state: %s @ (%d,%d) %s%s%s%s", @@ -3010,7 +3161,7 @@ error4(coordxy x, coordxy y, int a, int b, int c, int dd) * * Things that are ambiguous: lava */ -static int +staticfn int check_pos(coordxy x, coordxy y, int which) { int type; @@ -3018,7 +3169,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; } @@ -3026,7 +3178,7 @@ check_pos(coordxy x, coordxy y, int which) /* Return TRUE if more than one is non-zero. */ /*ARGSUSED*/ #ifdef WA_VERBOSE -static boolean +staticfn boolean more_than_one(coordxy x, coordxy y, coordxy a, coordxy b, coordxy c) { if ((a && (b | c)) || (b && (a | c)) || (c && (a | b))) { @@ -3041,14 +3193,16 @@ more_than_one(coordxy x, coordxy y, coordxy a, coordxy b, coordxy c) #endif /* Return the wall mode for a T wall. */ -static int +staticfn int set_twall( #ifdef WA_VERBOSE coordxy x0, coordxy y0, /* used #if WA_VERBOSE */ #else coordxy x0 UNUSED, coordxy y0 UNUSED, #endif - coordxy x1, coordxy y1, coordxy x2, coordxy y2, coordxy x3, coordxy y3) + coordxy x1, coordxy y1, + coordxy x2, coordxy y2, + coordxy x3, coordxy y3) { int wmode, is_1, is_2, is_3; @@ -3064,7 +3218,7 @@ set_twall( } /* Return wall mode for a horizontal or vertical wall. */ -static int +staticfn int set_wall(coordxy x, coordxy y, int horiz) { int wmode, is_1, is_2; @@ -3085,8 +3239,12 @@ set_wall(coordxy x, coordxy y, int horiz) } /* Return a wall mode for a corner wall. (x4,y4) is the "inner" position. */ -static int -set_corn(coordxy x1, coordxy y1, coordxy x2, coordxy y2, coordxy x3, coordxy y3, coordxy x4, coordxy y4) +staticfn int +set_corn( + coordxy x1, coordxy y1, + coordxy x2, coordxy y2, + coordxy x3, coordxy y3, + coordxy x4, coordxy y4) { coordxy wmode, is_1, is_2, is_3, is_4; @@ -3113,7 +3271,7 @@ set_corn(coordxy x1, coordxy y1, coordxy x2, coordxy y2, coordxy x3, coordxy y3, } /* Return mode for a crosswall. */ -static int +staticfn int set_crosswall(coordxy x, coordxy y) { coordxy wmode, is_1, is_2, is_3, is_4; @@ -3242,7 +3400,7 @@ const seenV seenv_matrix[3][3] = { #define sign(z) ((z) < 0 ? -1 : ((z) != 0)) /* Set the seen vector of lev as if seen from (x0,y0) to (x,y). */ -static void +staticfn void set_seenv( struct rm *lev, coordxy x0, coordxy y0, /* from */ @@ -3326,7 +3484,7 @@ static const int cross_matrix[4][6] = { }; /* Print out a T wall warning and all interesting info. */ -static void +staticfn void t_warn(struct rm *lev) { static const char warn_str[] = "wall_angle: %s: case %d: seenv = 0x%x"; @@ -3386,10 +3544,10 @@ t_warn(struct rm *lev) * draw diagrams. See rm.h for more details on the wall modes and * seen vector (SV). */ -static int +staticfn int wall_angle(struct rm *lev) { - register unsigned int seenv = lev->seenv & 0xff; + unsigned int seenv = lev->seenv & 0xff; const int *row; int col, idx; @@ -3442,22 +3600,6 @@ wall_angle(struct rm *lev) } break; case WM_T_BL: -#if 0 /* older method, fixed */ - if (only(seenv, SV4 | SV5)) { - col = T_tlcorn; - } else if ((seenv & (SV0 | SV1 | SV2)) - && only(seenv, SV0 | SV1 | SV2 | SV6 | SV7)) { - col = T_hwall; - } else if ((seenv & SV3) - || ((seenv & (SV0 | SV1 | SV2)) - && (seenv & (SV4 | SV5)))) { - col = T_tdwall; - } else { - if (seenv != SV6) - t_warn(lev); - col = T_stone; - } -#endif /* 0 */ if (only(seenv, SV4 | SV5)) col = T_tlcorn; else if ((seenv & (SV0 | SV1 | SV2 | SV7)) @@ -3469,22 +3611,6 @@ wall_angle(struct rm *lev) col = T_tdwall; break; case WM_T_BR: -#if 0 /* older method, fixed */ - if (only(seenv, SV5 | SV6)) { - col = T_trcorn; - } else if ((seenv & (SV0 | SV1 | SV2)) - && only(seenv, SV0 | SV1 | SV2 | SV3 | SV4)) { - col = T_hwall; - } else if ((seenv & SV7) - || ((seenv & (SV0 | SV1 | SV2)) - && (seenv & (SV5 | SV6)))) { - col = T_tdwall; - } else { - if (seenv != SV4) - t_warn(lev); - col = T_stone; - } -#endif /* 0 */ if (only(seenv, SV5 | SV6)) col = T_trcorn; else if ((seenv & (SV0 | SV1 | SV2 | SV3)) @@ -3508,6 +3634,7 @@ wall_angle(struct rm *lev) case SDOOR: if (lev->horizontal) goto horiz; + FALLTHROUGH; /*FALLTHRU*/ case VWALL: switch (lev->wall_info & WM_MASK) { @@ -3690,10 +3817,42 @@ wall_angle(struct rm *lev) return idx; } +/* Return the appropriate glyph for the engraving square at (x, y). + * This NEVER returns the "default" glyphs for S_engroom and S_engrcorr. Rather, + * it returns one of the 14 extra engraving glyphs based on the type (first 7 + * are for S_engroom, second 7 for S_engrcorr, so that glyph_to_cmap can + * distinguish between them). */ +int +engraving_to_glyph(struct engr *ep) +{ + int glyph = GLYPH_ENGRAVING_OFF; + coordxy x = ep->engr_x, y = ep->engr_y; + switch (ep->engr_type) { + case DUST: + glyph += engr_dust; + break; + case ENGRAVE: + glyph += engr_engrave; + break; + case BURN: + glyph += engr_burn; + break; + case BLOOD: + glyph += engr_blood; + break; + case MARK: + glyph += engr_mark1 + ((x + y) % 3); + break; + } + if (levl[x][y].typ == CORR) + glyph += max_engraving_glyphtypes; /* shunt it into the second 7 */ + return glyph; +} + /* Return the appropriate glyph for the magic platform square at (x, y). * Normally this will be its standard default appearance, but the Valkyrie quest * locate level uses its colored glyph variants. */ -static int +staticfn int magicplatform_to_glyph(coordxy x UNUSED, coordxy y) { if (Role_if(PM_VALKYRIE) && Is_qlocate(&u.uz)) { @@ -3703,7 +3862,7 @@ magicplatform_to_glyph(coordxy x UNUSED, coordxy y) * ROYGBV order */ return GLYPH_MAGICPLATFORM_OFF + platform_red + (y - 8); } - return GLYPH_MAGICPLATFORM_OFF + platform_default; + return cmap_to_glyph(S_magicplatform); } /* Should we render engravings on the terrain at x, y? For "interesting" types @@ -3711,13 +3870,12 @@ magicplatform_to_glyph(coordxy x UNUSED, coordxy y) * room and corridors, yes. * This used to live in engrave.h but is now a function because xNetHack permits * more types of terrain to show engravings. */ -static boolean +staticfn boolean spot_shows_engravings(coordxy x, coordxy y) { switch (levl[x][y].typ) { case ROOM: case CORR: - case SCORR: /* from vanilla but this doesn't really make sense */ case ICE: case GRASS: return TRUE; @@ -3748,13 +3906,19 @@ fn_cmap_to_glyph(int cmap) /* for 'onefile' processing where end of this file isn't necessarily the end of the source code seen by the compiler (there are lots of other macros defined above...) */ -#undef remember_topology +#undef _glyph_at #undef DETECTED #undef PHYSICALLY_SEEN #undef is_worm_tail +#undef _suppress_map_output #undef TMP_AT_MAX_GLYPHS #undef Glyphinfo_at #undef reset_glyph_bbox #undef GMAP_SET +#ifndef WA_VERBOSE +#undef more_than_one +#endif +#undef only +#undef set_corner /*display.c*/ diff --git a/src/dlb.c b/src/dlb.c index c6788d9a2c..d6551fe06c 100644 --- a/src/dlb.c +++ b/src/dlb.c @@ -59,20 +59,20 @@ extern unsigned FITSuint_(unsigned long long, const char *, int); */ #define MAX_LIBS 4 -static library dlb_libs[MAX_LIBS]; +staticfn library dlb_libs[MAX_LIBS]; -static boolean readlibdir(library * lp); -static boolean find_file(const char *name, library **lib, long *startp, +staticfn boolean readlibdir(library * lp); +staticfn boolean find_file(const char *name, library **lib, long *startp, long *sizep); -static boolean lib_dlb_init(void); -static void lib_dlb_cleanup(void); -static boolean lib_dlb_fopen(dlb *, const char *, const char *); -static int lib_dlb_fclose(dlb *); -static int lib_dlb_fread(char *, int, int, dlb *); -static int lib_dlb_fseek(dlb *, long, int); -static char *lib_dlb_fgets(char *, int, dlb *); -static int lib_dlb_fgetc(dlb *); -static long lib_dlb_ftell(dlb *); +staticfn boolean lib_dlb_init(void); +staticfn void lib_dlb_cleanup(void); +staticfn boolean lib_dlb_fopen(dlb *, const char *, const char *); +staticfn int lib_dlb_fclose(dlb *); +staticfn int lib_dlb_fread(char *, int, int, dlb *); +staticfn int lib_dlb_fseek(dlb *, long, int); +staticfn char *lib_dlb_fgets(char *, int, dlb *); +staticfn int lib_dlb_fgetc(dlb *); +staticfn long lib_dlb_ftell(dlb *); /* not static because shared with dlb_main.c */ boolean open_library(const char *lib_name, library *lp); @@ -123,7 +123,7 @@ extern char *eos(char *); * * Return TRUE on success, FALSE on failure. */ -static boolean +staticfn boolean readlibdir(library *lp) /* library pointer to fill in */ { int i; @@ -171,7 +171,7 @@ readlibdir(library *lp) /* library pointer to fill in */ * Look for the file in our directory structure. Return 1 if successful, * 0 if not found. Fill in the size and starting position. */ -static boolean +staticfn boolean find_file(const char *name, library **lib, long *startp, long *sizep) { int i, j; @@ -228,7 +228,7 @@ close_library(library *lp) * Open the library file once using stdio. Keep it open, but * keep track of the file position. */ -static boolean +staticfn boolean lib_dlb_init(void) { /* zero out array */ @@ -248,7 +248,7 @@ lib_dlb_init(void) return TRUE; } -static void +staticfn void lib_dlb_cleanup(void) { int i; @@ -269,7 +269,7 @@ build_dlb_filename(const char *lf) #endif /*ARGSUSED*/ -static boolean +staticfn boolean lib_dlb_fopen(dlb *dp, const char *name, const char *mode UNUSED) { long start, size; @@ -288,14 +288,14 @@ lib_dlb_fopen(dlb *dp, const char *name, const char *mode UNUSED) } /*ARGUSED*/ -static int +staticfn int lib_dlb_fclose(dlb *dp UNUSED) { /* nothing needs to be done */ return 0; } -static int +staticfn int lib_dlb_fread(char *buf, int size, int quan, dlb *dp) { long pos, nread, nbytes; @@ -317,10 +317,10 @@ lib_dlb_fread(char *buf, int size, int quan, dlb *dp) dp->mark += nbytes; dp->lib->fmark += nbytes; - return (int)nread; + return (int) nread; } -static int +staticfn int lib_dlb_fseek(dlb *dp, long pos, int whence) { long curpos; @@ -345,7 +345,7 @@ lib_dlb_fseek(dlb *dp, long pos, int whence) return 0; } -static char * +staticfn char * lib_dlb_fgets(char *buf, int len, dlb *dp) { int i; @@ -377,7 +377,7 @@ lib_dlb_fgets(char *buf, int len, dlb *dp) return buf; } -static int +staticfn int lib_dlb_fgetc(dlb *dp) { char c; @@ -387,13 +387,13 @@ lib_dlb_fgetc(dlb *dp) return (int) c; } -static long +staticfn long lib_dlb_ftell(dlb *dp) { return dp->mark; } -const dlb_procs_t lib_dlb_procs = { lib_dlb_init, lib_dlb_cleanup, +static const dlb_procs_t lib_dlb_procs = { lib_dlb_init, lib_dlb_cleanup, lib_dlb_fopen, lib_dlb_fclose, lib_dlb_fread, lib_dlb_fseek, lib_dlb_fgets, lib_dlb_fgetc, @@ -402,7 +402,7 @@ const dlb_procs_t lib_dlb_procs = { lib_dlb_init, lib_dlb_cleanup, #endif /* DLBLIB */ #ifdef DLBRSRC -const dlb_procs_t rsrc_dlb_procs = { rsrc_dlb_init, rsrc_dlb_cleanup, +static const dlb_procs_t rsrc_dlb_procs = { rsrc_dlb_init, rsrc_dlb_cleanup, rsrc_dlb_fopen, rsrc_dlb_fclose, rsrc_dlb_fread, rsrc_dlb_fseek, rsrc_dlb_fgets, rsrc_dlb_fgetc, diff --git a/src/do.c b/src/do.c index 0227f0ac6c..540cd6c7a7 100644 --- a/src/do.c +++ b/src/do.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 do.c $NHDT-Date: 1683832317 2023/05/11 19:11:57 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.352 $ */ +/* 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. */ @@ -7,22 +7,23 @@ #include "hack.h" -static boolean teleport_sink(void); -static void dosinkring(struct obj *); -static int drop(struct obj *); -static int menudrop_split(struct obj *, long); -static boolean engulfer_digests_food(struct obj *); -static boolean danger_uprops(void); -static int wipeoff(void); -static int menu_drop(int); -static boolean u_stuck_cannot_go(const char *); -static NHFILE *currentlevel_rewrite(void); -static void familiar_level_msg(void); -static void final_level(void); -static void do_level_updates(xint8); -static void temperature_change_msg(schar); - -/* static boolean badspot(coordxy,coordxy); */ +staticfn boolean teleport_sink(void); +staticfn void dosinkring(struct obj *); +staticfn int drop(struct obj *); +staticfn int menudrop_split(struct obj *, long); +staticfn boolean engulfer_digests_food(struct obj *); +staticfn boolean danger_uprops(void); +staticfn int wipeoff(void); +staticfn int menu_drop(int); +staticfn boolean u_stuck_cannot_go(const char *); +staticfn NHFILE *currentlevel_rewrite(void); +staticfn void familiar_level_msg(void); +staticfn void final_level(void); +staticfn void do_level_updates(xint8); +staticfn void temperature_change_msg(schar); +staticfn boolean better_not_try_to_drop_that(struct obj *); + + /* static boolean badspot(coordxy,coordxy); */ /* the #drop command: drop one inventory item */ int @@ -48,9 +49,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?"); @@ -75,6 +76,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 @@ -84,7 +86,8 @@ boulder_hits_pool( will be dead here; killing it again would yield impossible "dmonsfree: N removed doesn't match N+1 pending" when other monsters have finished their current turn */ - if ((mtmp = m_at(rx, ry)) != 0 && !DEADMONSTER(mtmp)) + if ((mtmp = m_at(rx, ry)) != 0 && !DEADMONSTER(mtmp) + && !m_in_air(mtmp)) mondied(mtmp); if (ttmp) @@ -100,7 +103,7 @@ boulder_hits_pool( Strcpy(whobuf, y_monnam(u.usteed)); pline("%s %s %s into the %s.", upstart(whobuf), vtense(whobuf, "push"), the(xname(otmp)), what); - if (Verbose(0, boulder_hits_pool1) && !Blind) + if (flags.verbose && !Blind) pline("Now you can cross it!"); /* no splashing in this case */ } @@ -136,9 +139,10 @@ boulder_hits_pool( dmg = d((Fire_resistance ? 1 : 3), 6); losehp(Maybe_Half_Phys(dmg), /* lava damage */ "molten lava", KILLED_BY); - } else if (!fills_up && Verbose(0, boulder_hits_pool2) - && (pushing ? !Blind : cansee(rx, ry))) + } else if (!fills_up && flags.verbose + && (pushing ? !Blind : cansee(rx, ry))) { pline("It sinks without a trace!"); + } } /* boulder is now gone */ @@ -170,7 +174,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; @@ -214,7 +221,7 @@ flooreffects(struct obj *obj, coordxy x, coordxy y, const char *verb) might have been thrown by a giant or launched by a rolling boulder trap triggered by a monster or dropped by a scroll of earth read by a monster */ - if (gc.context.mon_moving) { + if (svc.context.mon_moving) { /* normally we'd use ohitmon() but it can call drop_throw() which calls flooreffects() */ damage = dmgval(obj, mtmp); @@ -232,6 +239,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 { @@ -249,12 +257,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); @@ -265,10 +271,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); @@ -310,17 +319,57 @@ flooreffects(struct obj *obj, coordxy x, coordxy y, const char *verb) res = obj_aireffects(obj, cansee(x, y)); newsym(x, y); } else if (obj->globby) { + struct obj *globbyobj = obj; /* allow obj to be nonnull arg */ + /* Globby things like puddings might stick together */ - while (obj && (otmp = obj_nexto_xy(obj, x, y, TRUE)) != 0) { - pudding_merge_message(obj, otmp); + while (globbyobj + && (otmp = obj_nexto_xy(globbyobj, x, y, TRUE)) != 0) { + pudding_merge_message(globbyobj, otmp); /* intentionally not getting the melded object; obj_meld may set * obj to null. */ - (void) obj_meld(&obj, &otmp); + (void) obj_meld(&globbyobj, &otmp); } - res = (boolean) !obj; - } else if (gc.context.mon_moving && IS_ALTAR(levl[x][y].typ) + res = (boolean) !globbyobj; + } else if (svc.context.mon_moving && IS_ALTAR(levl[x][y].typ) && cansee(x,y)) { doaltarobj(obj); + } else if (obj->oclass == POTION_CLASS && svl.level.flags.temperature > 0 + && (levl[x][y].typ == ROOM || levl[x][y].typ == CORR)) { + /* Potions are sometimes destroyed when landing on very hot + ground. The basic odds are 50% for nonblessed potions and + 30% for blessed potions; if you have handled the object + (i.e. it is or was yours), these odds are adjusted by Luck + (each Luck point affects them by 2%). Artifact potions + would not be affected, if any existed. + + Oil is not affected because its boiling point (and flash + point) are higher than that of water. For example, whale + oil, one of the substances traditionally used in oil lamps, + can survive over 100 degrees Centigrade more heat than + water can.*/ + if (cansee(x,y)) { + /* unconditional "ground" is safe as this only runs for + room and corridor tiles */ + pline("%s up as %s the hot ground.", Tobjnam(obj, "heat"), + is_plural(obj) ? "they hit" : "it hits"); + } + + int survival_chance = obj->blessed ? 70 : 50; + if (obj->invlet) + survival_chance += Luck * 2; + if (obj->otyp == POT_OIL) + survival_chance = 100; + + if (!obj_resists(obj, survival_chance, 100)) { + if (cansee(x,y)) { + pline("%s from the heat!", + is_plural(obj) ? "They shatter" : "It shatters"); + } else { + You_hear("a shattering noise."); + } + breakobj(obj, x, y, FALSE, FALSE); + res = TRUE; + } } gb.bhitpos = save_bhitpos; @@ -336,7 +385,7 @@ doaltarobj(struct obj *obj) if (obj->oclass != COIN_CLASS) { /* KMH, conduct */ - if (!gc.context.mon_moving && !u.uconduct.gnostic++) + if (!svc.context.mon_moving && !u.uconduct.gnostic++) livelog_printf(LL_CONDUCT, "eschewed atheism, by dropping %s on an altar", doname(obj)); @@ -424,27 +473,26 @@ polymorph_sink(void) return; sinklooted = levl[u.ux][u.uy].looted != 0; - gl.level.flags.nsinks--; - levl[u.ux][u.uy].doormask = 0; /* levl[][].flags */ + /* svl.level.flags.nsinks--; // set_levltyp() will update this */ + levl[u.ux][u.uy].flags = 0; switch (rn2(4)) { default: case 0: sym = S_fountain; - levl[u.ux][u.uy].typ = FOUNTAIN; + set_levltyp(u.ux, u.uy, FOUNTAIN); /* updates level.flags.nfountains */ levl[u.ux][u.uy].blessedftn = 0; if (sinklooted) SET_FOUNTAIN_LOOTED(u.ux, u.uy); - gl.level.flags.nfountains++; break; case 1: sym = S_throne; - levl[u.ux][u.uy].typ = THRONE; + set_levltyp(u.ux, u.uy, THRONE); if (sinklooted) levl[u.ux][u.uy].looted = T_LOOTED; break; case 2: sym = S_altar; - levl[u.ux][u.uy].typ = ALTAR; + set_levltyp(u.ux, u.uy, ALTAR); /* 3.6.3: this used to pass 'rn2(A_LAWFUL + 2) - 1' to Align2amask() but that evaluates its argument more than once */ algn = rn2(3) - 1; /* -1 (A_Cha) or 0 (A_Neu) or +1 (A_Law) */ @@ -453,7 +501,7 @@ polymorph_sink(void) break; case 3: sym = S_room; - levl[u.ux][u.uy].typ = ROOM; + set_levltyp(u.ux, u.uy, ROOM); make_grave(u.ux, u.uy, (char *) 0); if (levl[u.ux][u.uy].typ == GRAVE) sym = S_grave; @@ -470,38 +518,45 @@ polymorph_sink(void) /* Teleports the sink at the player's position; return True if sink teleported. */ -static boolean +staticfn boolean teleport_sink(void) { coordxy cx, cy; - int cnt = 0; - struct trap *trp; - struct engr *eng; + unsigned alreadylooted; + int trycnt = 0; do { - cx = rnd(COLNO - 1); - cy = rn2(ROWNO); - trp = t_at(cx, cy); - eng = engr_at(cx, cy); - } while ((levl[cx][cy].typ != ROOM || trp || eng || cansee(cx, cy)) - && cnt++ < 200); - - if (levl[cx][cy].typ == ROOM && !trp && !eng) { - /* create sink at new position */ - levl[cx][cy].typ = SINK; - levl[cx][cy].looted = levl[u.ux][u.uy].looted; - newsym(cx, cy); - /* remove old sink */ - levl[u.ux][u.uy].typ = ROOM; - levl[u.ux][u.uy].looted = 0; - newsym(u.ux, u.uy); - return TRUE; - } +#if 0 /* this isn't incorrect but it is extremely unlikely that spots + * on the level's edge will be ROOM so picking such wastes tries */ + cx = rnd(COLNO - 1); /* 1..COLNO-1 */ + cy = rn2(ROWNO); /* 0..ROWNO-1 */ +#else /* use this instead */ + cx = 1 + rnd((COLNO - 1) - 2); /* 2..COLNO-2 */ + cy = 1 + rn2(ROWNO - 2); /* 1..ROWNO-2 */ +#endif + if (levl[cx][cy].typ == ROOM + && !t_at(cx, cy) && !engr_at(cx, cy) + && (!cansee(cx, cy) || distu(cx, cy) > 3 * 3)) { + /* this ends up having set_levltyp() count all sinks and + fountains on the level twice but that is not a problem */ + alreadylooted = levl[u.ux][u.uy].looted; + /* remove old sink */ + set_levltyp(u.ux, u.uy, ROOM); /* was SINK so updates nsinks */ + levl[u.ux][u.uy].looted = 0; + newsym(u.ux, u.uy); + /* create sink at new position */ + set_levltyp(cx, cy, SINK); /* now SINK so also updates nsinks */ + levl[cx][cy].looted = alreadylooted ? 1 : 0; + newsym(cx, cy); + return TRUE; + } + } while (++trycnt < 200); + return FALSE; } /* obj is a ring being dropped over a kitchen sink */ -static void +staticfn void dosinkring(struct obj *obj) { struct obj *otmp, *otmp2; @@ -562,7 +617,7 @@ dosinkring(struct obj *obj) break; case RIN_HUNGER: ideed = FALSE; - for (otmp = gl.level.objects[u.ux][u.uy]; otmp; otmp = otmp2) { + for (otmp = svl.level.objects[u.ux][u.uy]; otmp; otmp = otmp2) { otmp2 = otmp->nexthere; if (otmp != uball && otmp != uchain && !obj_resists(otmp, 1, 99)) { @@ -717,13 +772,15 @@ canletgo(struct obj *obj, const char *word) return TRUE; } -static int +staticfn int drop(struct obj *obj) { if (!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); @@ -740,7 +797,7 @@ drop(struct obj *obj) if (u.uswallow) { /* barrier between you and the floor */ - if (Verbose(0, drop1)) { + if (flags.verbose) { char *onam_p, *mnam_p, monbuf[BUFSZ]; mnam_p = mon_nam(u.ustuck); @@ -768,7 +825,7 @@ drop(struct obj *obj) if (levhack) ELevitation = W_ART; /* other than W_ARTI */ - if (Verbose(0, drop2)) + if (flags.verbose) You("drop %s.", doname(obj)); freeinv(obj); hitfloor(obj, TRUE); @@ -776,9 +833,10 @@ drop(struct obj *obj) float_down(I_SPECIAL | TIMEOUT, W_ARTI | W_ART); return ECMD_TIME; } - if (!IS_ALTAR(levl[u.ux][u.uy].typ) && Verbose(0, drop3)) + if (!IS_ALTAR(levl[u.ux][u.uy].typ) && flags.verbose) You("drop %s.", doname(obj)); } + obj->how_lost = LOST_DROPPED; dropx(obj); return ECMD_TIME; } @@ -833,9 +891,10 @@ dropz(struct obj *obj, boolean with_impact) place_object(obj, u.ux, u.uy); if (with_impact) container_impact_dmg(obj, u.ux, u.uy); + impact_disturbs_zombies(obj, with_impact); if (obj == uball) drop_ball(u.ux, u.uy); - else if (gl.level.flags.has_shop) + else if (svl.level.flags.has_shop) sellobj(obj, u.ux, u.uy); stackobj(obj); if (Blind && Levitation) @@ -865,7 +924,7 @@ obj_drops_at(struct obj *obj, int x, int y) /* when swallowed, move dropped object from OBJ_FREE to u.ustuck's inventory; for purple worm, immediately eat any corpse, glob, or special meat item from object polymorph; return True if object is used up, False otherwise */ -static boolean +staticfn boolean engulfer_digests_food(struct obj *obj) { /* animal swallower (purple worn) eats any @@ -880,7 +939,7 @@ engulfer_digests_food(struct obj *obj) if (obj->otyp == CORPSE) { could_petrify = touch_petrifies(&mons[obj->corpsenm]); - could_poly = polyfodder(obj); + could_poly = polyfood(obj); could_grow = (obj->corpsenm == PM_WRAITH); could_heal = (obj->corpsenm == PM_NURSE); } else if (obj->otyp == GLOB_OF_GREEN_SLIME) { @@ -897,7 +956,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); } @@ -930,7 +989,7 @@ obj_no_longer_held(struct obj *obj) */ if (!obj->oerodeproof || !rn2(10)) { /* if monsters aren't moving, assume player is responsible */ - if (!gc.context.mon_moving && !gp.program_state.gameover) + if (!svc.context.mon_moving && !program_state.gameover) costly_alteration(obj, COST_DEGRD); obj->otyp = WORM_TOOTH; obj->oerodeproof = 0; @@ -964,7 +1023,24 @@ doddrop(void) return result; } -static int /* check callers */ +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) { if (cnt && cnt < otmp->quan) { @@ -978,7 +1054,7 @@ menudrop_split(struct obj *otmp, long cnt) } /* Drop things from the hero's inventory, using a menu. */ -static int +staticfn int menu_drop(int retry) { int n, i, n_dropped = 0; @@ -997,8 +1073,13 @@ menu_drop(int retry) | BUC_BLESSED | BUC_CURSED | BUC_UNCURSED | BUC_UNKNOWN | JUSTPICKED | INCLUDE_VENOM), &pick_list, PICK_ANY); + /* when paranoid_confirm:A is set, 'A' by itself implies + 'A'+'a' which will be followed by a confirmation prompt; + when that option isn't set, 'A' by itself is rejected + by query_categorry() and result here will be n==0 */ if (!n) - goto drop_done; + goto drop_done; /* no non-autopick category filters specified */ + for (i = 0; i < n; i++) { if (pick_list[i].item.a_int == ALL_TYPES_SELECTED) { all_categories = TRUE; @@ -1023,7 +1104,7 @@ menu_drop(int retry) i = ggetobj("drop", drop, 0, TRUE, &ggoresults); if (i == -2) all_categories = TRUE; - if (ggoresults & ALL_FINISHED) { + if ((ggoresults & ALL_FINISHED) != 0) { n_dropped = i; goto drop_done; } @@ -1060,8 +1141,8 @@ menu_drop(int retry) /* drop the just picked item automatically, if only one stack */ otmp = find_justpicked(gi.invent); if (otmp) - n_dropped += ((menudrop_split(otmp, justpicked_quan) & ECMD_TIME) - != 0) ? 1 : 0; + n_dropped += ((menudrop_split(otmp, justpicked_quan) + & ECMD_TIME) != 0) ? 1 : 0; } else { /* should coordinate with perm invent, maybe not show worn items */ n = query_objlist("What would you like to drop?", &gi.invent, @@ -1072,7 +1153,7 @@ menu_drop(int retry) /* * picklist[] contains a set of pointers into inventory, but * as soon as something gets dropped, they might become stale - * (see the drop_everything code above for an explanation). + * (see the autopick code above for an explanation). * Just checking to see whether one is still in the gi.invent * chain is not sufficient validation since destroyed items * will be freed and items we've split here might have already @@ -1102,7 +1183,7 @@ menu_drop(int retry) return (n_dropped ? ECMD_TIME : ECMD_OK); } -static boolean +staticfn boolean u_stuck_cannot_go(const char *updn) { if (u.ustuck) { @@ -1127,9 +1208,8 @@ int dodown(void) { struct trap *trap = 0; - stairway *stway = stairway_at(u.ux, u.uy); - boolean stairs_down = (stway && !stway->up && !stway->isladder), - ladder_down = (stway && !stway->up && stway->isladder); + stairway *stway; + boolean stairs_down, ladder_down; set_move_cmd(DIR_DOWN, 0); @@ -1139,6 +1219,13 @@ dodown(void) if (stucksteed(TRUE)) { return ECMD_OK; } + + stairs_down = ladder_down = FALSE; + if ((stway = stairway_at(u.ux, u.uy)) != 0 && !stway->up) { + stairs_down = !stway->isladder; + ladder_down = !stairs_down; + } + /* Levitation might be blocked, but player can still use '>' to turn off controlled levitation */ if (HLevitation || ELevitation) { @@ -1150,8 +1237,8 @@ dodown(void) for (obj = gi.invent; obj; obj = obj->nobj) { if (obj->oartifact && artifact_has_invprop(obj, LEVITATION)) { - if (obj->age < gm.moves) - obj->age = gm.moves; + if (obj->age < svm.moves) + obj->age = svm.moves; obj->age += rnz(100); } } @@ -1166,16 +1253,18 @@ dodown(void) if (BLevitation) { ; /* weren't actually floating after all */ } else if (Blind) { + /* glyph_to_cmap() is a macro which expands its argument many + times; use this to do part of its work just once */ + int glyph_at_uxuy = levl[u.ux][u.uy].glyph; + /* Avoid alerting player to an unknown stair or ladder. * Changes the message for a covered, known staircase * too; staircase knowledge is not stored anywhere. */ if (stairs_down) - stairs_down = - (glyph_to_cmap(levl[u.ux][u.uy].glyph) == S_dnstair); + stairs_down = (glyph_to_cmap(glyph_at_uxuy) == S_dnstair); else if (ladder_down) - ladder_down = - (glyph_to_cmap(levl[u.ux][u.uy].glyph) == S_dnladder); + ladder_down = (glyph_to_cmap(glyph_at_uxuy) == S_dnladder); } if (Is_airlevel(&u.uz)) You("are floating in the %s.", surface(u.ux, u.uy)); @@ -1183,9 +1272,9 @@ dodown(void) You("are floating in %s.", is_pool(u.ux, u.uy) ? "the water" : "a bubble of air"); else - floating_above(stairs_down ? "stairs" : ladder_down - ? "ladder" - : surface(u.ux, u.uy)); + floating_above(stairs_down ? "stairs" + : ladder_down ? "ladder" + : surface(u.ux, u.uy)); return ECMD_OK; /* didn't move */ } @@ -1216,7 +1305,8 @@ dodown(void) return ECMD_TIME; } else if (!trap || !is_hole(trap->ttyp) || !Can_fall_thru(&u.uz) || !trap->tseen) { - if (flags.autodig && !gc.context.nopick && uwep && is_pick(uwep)) { + if (flags.autodig && !svc.context.nopick + && uwep && is_pick(uwep)) { return use_pick_axe2(uwep); } else { You_cant("go down here%s.", @@ -1331,7 +1421,7 @@ doup(void) } /* check that we can write out the current level */ -static NHFILE * +staticfn NHFILE * currentlevel_rewrite(void) { NHFILE *nhfp; @@ -1363,7 +1453,7 @@ save_currentstate(void) { NHFILE *nhfp; - gp.program_state.in_checkpoint++; + program_state.in_checkpoint++; if (flags.ins_chkpt) { /* write out just-attained level, with pets and everything */ nhfp = currentlevel_rewrite(); @@ -1378,13 +1468,13 @@ save_currentstate(void) /* write out non-level state */ savestateinlock(); - gp.program_state.in_checkpoint--; + program_state.in_checkpoint--; } #endif /* static boolean -badspot(register coordxy x, register coordxy y) +badspot(coordxy x, coordxy y) { return (boolean) ((levl[x][y].typ != ROOM && levl[x][y].typ != AIR @@ -1423,7 +1513,7 @@ u_collide_m(struct monst *mtmp) } } -static void +staticfn void familiar_level_msg(void) { static const char *const fam_msgs[4] = { @@ -1467,22 +1557,29 @@ goto_level( boolean cant_go_back, great_effort, up = (depth(newlevel) < depth(&u.uz)), newdungeon = (u.uz.dnum != newlevel->dnum), + leaving_tutorial = FALSE, familiar = FALSE, new = FALSE; /* made a new level? */ struct monst *mtmp; char whynot[BUFSZ]; - char *annotation; int dist = depth(newlevel) - depth(&u.uz); boolean do_fall_dmg = FALSE; - schar prev_temperature = gl.level.flags.temperature; + schar prev_temperature = svl.level.flags.temperature; if (dunlev(newlevel) > dunlevs_in_dungeon(newlevel)) newlevel->dlevel = dunlevs_in_dungeon(newlevel); - if (newdungeon && In_endgame(newlevel)) { /* 1st Endgame Level !!! */ - if (!u.uhave.amulet) - return; /* must have the Amulet */ - if (!wizard) { /* wizard ^V can bypass Earth level */ - assign_level(newlevel, &earth_level); /* (redundant) */ + if (newdungeon) { + if (In_endgame(newlevel)) { /* 1st Endgame Level !!! */ + if (!u.uhave.amulet) + return; /* must have the Amulet */ + if (!wizard) /* wizard ^V can bypass Earth level */ + assign_level(newlevel, &earth_level); /* (redundant) */ + } else if (In_tutorial(newlevel)) { + tutorial(TRUE); /* entering tutorial */ + } else if (In_tutorial(&u.uz)) { + tutorial(FALSE); /* leaving tutorial */ + up = FALSE; /* re-enter level 1 as if starting new game */ + leaving_tutorial = TRUE; } } new_ledger = ledger_no(newlevel); @@ -1503,7 +1600,8 @@ goto_level( if (gl.luacore && nhcb_counts[NHCB_LVL_LEAVE]) { lua_getglobal(gl.luacore, "nh_callback_run"); lua_pushstring(gl.luacore, nhcb_name[NHCB_LVL_LEAVE]); - nhl_pcall(gl.luacore, 1, 0); + nhl_pcall_handle(gl.luacore, 1, 0, "goto_level", NHLpa_panic); + lua_settop(gl.luacore, 0); } /* tethered movement makes level change while trapped feasible */ @@ -1521,7 +1619,7 @@ goto_level( maybe_reset_pick((struct obj *) 0); reset_trapset(); /* even if to-be-armed trap obj is accompanying hero */ iflags.travelcc.x = iflags.travelcc.y = 0; /* travel destination cache */ - gc.context.polearm.hitmon = (struct monst *) 0; /* polearm target */ + svc.context.polearm.hitmon = (struct monst *) 0; /* polearm target */ /* digging context is level-aware and can actually be resumed if hero returns to the previous level without any intervening dig */ @@ -1557,13 +1655,13 @@ goto_level( * for the level being left, to recover dynamic memory in use and * to avoid dangling timers and light sources. */ - cant_go_back = (newdungeon && In_endgame(newlevel)); + cant_go_back = ((newdungeon && In_endgame(newlevel)) || leaving_tutorial); if (!cant_go_back) { update_mlstmv(); /* current monsters are becoming inactive */ if (nhfp->structlevel) bufon(nhfp->fd); /* use buffered output */ } else { - free_luathemes(TRUE); + free_luathemes(leaving_tutorial ? tut_themes : most_themes); } save_mode = nhfp->mode; nhfp->mode = cant_go_back ? FREEING : (WRITING | FREEING); @@ -1573,10 +1671,12 @@ goto_level( if (cant_go_back) { /* discard unreachable levels; keep #0 */ for (l_idx = maxledgerno(); l_idx > 0; --l_idx) - delete_levelfile(l_idx); + if (!leaving_tutorial || ledger_to_dnum(l_idx) == tutorial_dnum) + delete_levelfile(l_idx); /* mark #overview data for all dungeon branches as uninteresting */ - for (l_idx = 0; l_idx < gn.n_dgns; ++l_idx) - remdun_mapseen(l_idx); + for (l_idx = 0; l_idx < svn.n_dgns; ++l_idx) + if (!leaving_tutorial || l_idx == tutorial_dnum) + remdun_mapseen(l_idx); /* get rid of mons & objs scheduled to migrate to discarded levels */ discard_migrations(); } @@ -1603,18 +1703,18 @@ goto_level( stairway_free_all(); /* set default level change destination areas */ /* the special level code may override these */ - (void) memset((genericptr_t) &gu.updest, 0, sizeof gu.updest); - (void) memset((genericptr_t) &gd.dndest, 0, sizeof gd.dndest); + (void) memset((genericptr_t) &svu.updest, 0, sizeof svu.updest); + (void) memset((genericptr_t) &svd.dndest, 0, sizeof svd.dndest); - if (!(gl.level_info[new_ledger].flags & LFILE_EXISTS)) { + if (!(svl.level_info[new_ledger].flags & LFILE_EXISTS)) { /* entering this level for first time; make it now */ - if (gl.level_info[new_ledger].flags & (VISITED)) { + if (svl.level_info[new_ledger].flags & (VISITED)) { impossible("goto_level: returning to discarded level?"); - gl.level_info[new_ledger].flags &= ~(VISITED); + svl.level_info[new_ledger].flags &= ~(VISITED); } mklev(); new = TRUE; /* made the level */ - familiar = bones_include_name(gp.plname); + familiar = bones_include_name(svp.plname); } else { /* returning to previously visited level; reload it */ nhfp = open_levelfile(new_ledger, whynot); @@ -1625,7 +1725,7 @@ goto_level( reseed_random(rn2); reseed_random(rn2_on_display_rng); minit(); /* ZEROCOMP */ - getlev(nhfp, gh.hackpid, new_ledger); + getlev(nhfp, svh.hackpid, new_ledger); close_nhfile(nhfp); oinit(); /* reassign level dependent obj probabilities */ } @@ -1638,8 +1738,7 @@ goto_level( if (portal && !In_endgame(&u.uz)) { /* find the portal on the new level */ - register struct trap *ttrap; - struct stairway *stway; + struct trap *ttrap; for (ttrap = gf.ftrap; ttrap; ttrap = ttrap->ntrap) if (ttrap->ttyp == MAGIC_PORTAL) @@ -1652,9 +1751,6 @@ goto_level( after already getting expelled once. The portal back doesn't exist anymore - see expulsion(). */ u_on_rndspot(0); - } else if ((stway = stairway_find_dir(TRUE)) != 0) { - /* returning from tutorial via portal */ - u_on_newpos(stway->sx, stway->sy); } else { if (!iflags.debug_fuzzer) impossible("goto_level: no corresponding portal!"); @@ -1677,7 +1773,7 @@ goto_level( /* you climb up the {stairs|ladder}; fly up the stairs; fly up along the ladder */ great_effort = (Punished && !Levitation); - if (Verbose(0, go_to_level1) || great_effort) + if (flags.verbose || great_effort) pline("%s %s up%s the %s.", great_effort ? "With great effort, you" : "You", u_locomotion("climb"), @@ -1695,7 +1791,7 @@ goto_level( if (!u.dz) { ; /* stayed on same level? (no transit effects) */ } else if (Flying) { - if (Verbose(0, go_to_level2)) + if (flags.verbose) You("fly down %s.", ga.at_ladder ? "along the ladder" : "the stairs"); } else if (near_capacity() > UNENCUMBERED @@ -1716,7 +1812,7 @@ goto_level( KILLED_BY); selftouch("Falling, you"); } else { /* ordinary descent */ - if (Verbose(0, go_to_level3)) + if (flags.verbose) You("%s.", ga.at_ladder ? "climb down the ladder" : "descend the stairs"); } @@ -1757,17 +1853,16 @@ goto_level( if ((mtmp = m_at(u.ux, u.uy)) != 0) u_collide_m(mtmp); - initrack(); - /* initial movement of bubbles just before vision_recalc */ if (Is_waterlevel(&u.uz) || Is_airlevel(&u.uz)) movebubbles(); - else if (gl.level.flags.fumaroles) + else if (svl.level.flags.fumaroles) fumaroles(); /* Reset the screen. */ vision_reset(); /* reset the blockages */ reset_glyphmap(gm_levelchange); + notice_mon_off(); /* not noticing monsters yet! */ docrt(); /* does a full vision recalc */ flush_screen(-1); @@ -1820,7 +1915,7 @@ goto_level( onquest(); /* might be reaching locate|goal level */ } else if (Is_knox(&u.uz)) { /* alarm stops working once Croesus has died */ - if (new || !gm.mvitals[PM_CROESUS].died) { + if (new || !svm.mvitals[PM_CROESUS].died) { You("have penetrated a high security area!"); Soundeffect(se_alarm, 100); pline("An alarm sounds!"); @@ -1862,7 +1957,7 @@ goto_level( /* main dungeon message from your quest leader */ if (!In_quest(&u.uz0) && at_dgn_entrance("The Quest") && !(u.uevent.qcompleted || u.uevent.qexpelled - || gq.quest_status.killed_leader)) { + || svq.quest_status.killed_leader)) { /* [TODO: copy of same TODO below; if an achievement for receiving quest call from leader gets added, that should come after logging new level entry] */ @@ -1892,12 +1987,19 @@ 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 */ #ifdef INSURANCE save_currentstate(); #endif + notice_mon_on(); + notice_all_mons(TRUE); /* Take deferred fall damage (from falling into open air) * Hero takes fall damage on arrival in the new level whereas monsters take @@ -1911,15 +2013,13 @@ goto_level( You("splatter upon hitting the ground."); else You("crash into the ground very hard."); - Sprintf(gk.killer.name, "fell to %s death", uhis()); - losehp(fall_damage, gk.killer.name, NO_KILLER_PREFIX); + Sprintf(svk.killer.name, "fell to %s death", uhis()); + losehp(fall_damage, svk.killer.name, NO_KILLER_PREFIX); make_stunned((HStun & TIMEOUT) + rn1(15, 15), TRUE); set_wounded_legs(BOTH_SIDES, 60 + d(10,10)); } - if ((annotation = get_annotation(&u.uz)) != 0) - You("remember this level as %s.", annotation); - + print_level_annotation(); /* give room entrance message, if any */ check_special_room(FALSE); /* deliver objects traveling with player */ @@ -1953,21 +2053,21 @@ goto_level( void hellish_smoke_mesg(void) { - if (gl.level.flags.temperature) + if (svl.level.flags.temperature) pline("It is %s here.", - gl.level.flags.temperature > 0 ? "hot" : "bitingly cold"); + svl.level.flags.temperature > 0 ? "hot" : "bitingly cold"); - if (In_hell(&u.uz) && gl.level.flags.temperature > 0) + if (In_hell(&u.uz) && svl.level.flags.temperature > 0) You("%s smoke...", olfaction(gy.youmonst.data) ? "smell" : "sense"); } /* give a message when the level temperature is different from previous */ -static void +staticfn void temperature_change_msg(schar prev_temperature) { - if (prev_temperature != gl.level.flags.temperature) { - if (gl.level.flags.temperature) + if (prev_temperature != svl.level.flags.temperature) { + if (svl.level.flags.temperature) hellish_smoke_mesg(); else if (prev_temperature > 0) pline_The("heat %s gone.", @@ -1982,14 +2082,15 @@ temperature_change_msg(schar prev_temperature) void maybe_lvltport_feedback(void) { - if (gd.dfr_post_msg && !strncmpi(gd.dfr_post_msg, "You materialize", 15)) { + if (gd.dfr_post_msg + && !strncmpi(gd.dfr_post_msg, "You materialize", 15)) { /* "You materialize on a different level." */ pline("%s", gd.dfr_post_msg); free((genericptr_t) gd.dfr_post_msg), gd.dfr_post_msg = 0; } } -static void +staticfn void final_level(void) { /* reset monster hostility relative to player */ @@ -2004,8 +2105,10 @@ final_level(void) /* change levels at the end of this turn, after monsters finish moving */ void -schedule_goto(d_level *tolev, int utotype_flags, - const char *pre_msg, const char *post_msg) +schedule_goto( + d_level *tolev, + int utotype_flags, + const char *pre_msg, const char *post_msg) { /* UTOTYPE_DEFERRED is used, so UTOTYPE_NONE can trigger deferred_goto() */ u.utotype = utotype_flags | UTOTYPE_DEFERRED; @@ -2080,6 +2183,8 @@ revive_corpse(struct obj *corpse, boolean moldy) chewed ? "bite-covered" : (const char *) 0, CXN_SINGULAR)); mcarry = (where == OBJ_MINVENT) ? corpse->ocarry : 0; + /* mcarry is NULL for (where == OBJ_BURIED and OBJ_CONTAINED) now */ + (void) get_obj_location(corpse, &corpsex, &corpsey, CONTAINED_TOO | BURIED_TOO); @@ -2087,9 +2192,9 @@ revive_corpse(struct obj *corpse, boolean moldy) struct monst *mtmp2; container = corpse->ocontainer; - mtmp2 = get_container_location(container, &container_where, (int *) 0); - /* container_where is the outermost container's location even if - * nested */ + mtmp2 = get_container_location(container, &container_where, + (int *) 0); + /* container_where is outermost container's location even if nested */ if (container_where == OBJ_MINVENT && mtmp2) mcarry = mtmp2; } @@ -2145,7 +2250,7 @@ revive_corpse(struct obj *corpse, boolean moldy) case OBJ_MINVENT: /* probably a nymph's */ if (cansee(mtmp->mx, mtmp->my)) { - if (canseemon(mcarry)) + if (mcarry && canseemon(mcarry)) pline("Startled, %s drops %s as it %s!", mon_nam(mcarry), (moldy ? "a corpse" : an(cname)), moldy ? "goes moldy" @@ -2185,7 +2290,8 @@ revive_corpse(struct obj *corpse, boolean moldy) struct trap *ttmp; ttmp = t_at(mtmp->mx, mtmp->my); - ttmp->tseen = TRUE; + if (ttmp) + ttmp->tseen = TRUE; pline("%s claws itself out of the ground!", canspotmon(mtmp) ? Amonnam(mtmp) : Something); newsym(mtmp->mx, mtmp->my); @@ -2193,8 +2299,10 @@ revive_corpse(struct obj *corpse, boolean moldy) Soundeffect(se_scratching, 50); You_hear("scratching noises."); } + fill_pit(mtmp->mx, mtmp->my); break; } + FALLTHROUGH; /*FALLTHRU*/ default: /* we should be able to handle the other cases... */ @@ -2245,7 +2353,7 @@ revive_mon(anything *arg, long timeout UNUSED) if (!obj_has_timer(body, ROT_CORPSE)) You_feel("%sless hassled.", is_rider(mptr) ? "much " : ""); action = ROT_CORPSE; - when = (long) d(5, 50) - (gm.moves - body->age); + when = (long) d(5, 50) - (svm.moves - body->age); if (when < 1L) when = 1L; } @@ -2266,7 +2374,7 @@ moldy_corpse(anything *arg, long timeout UNUSED) /* Acidic corpses and petrifying corpses only grow acidic fungi. */ if (acidic(&mons[oldtyp]) || touch_petrifies(&mons[oldtyp])) { - if (gm.mvitals[PM_GREEN_MOLD].mvflags & G_GONE) + if (svm.mvitals[PM_GREEN_MOLD].mvflags & G_GONE) newpm = NULL; else newpm = &mons[PM_GREEN_MOLD]; @@ -2294,11 +2402,11 @@ moldy_corpse(anything *arg, long timeout UNUSED) boolean no_eligible = (newpm == NULL); /* Don't grow mold on the corpse the player is eating. */ - boolean munching = (body == gc.context.victual.piece); + boolean munching = (body == svc.context.victual.piece); if (already_fungus || bad_spot || no_eligible || munching) { /* set to rot away normally */ - start_timer(250L - (gm.moves - peek_at_iced_corpse_age(body)), + start_timer(250L - (svm.moves - peek_at_iced_corpse_age(body)), TIMER_OBJECT, ROT_CORPSE, arg); return; } @@ -2340,7 +2448,7 @@ moldy_corpse(anything *arg, long timeout UNUSED) if (old_oname) ONAME(body) = old_oname; body->owt = weight(body); - start_timer(250L - (gm.moves - peek_at_iced_corpse_age(body)), + start_timer(250L - (svm.moves - peek_at_iced_corpse_age(body)), TIMER_OBJECT, ROT_CORPSE, arg); } } @@ -2352,7 +2460,7 @@ zombify_mon(anything *arg, long timeout) struct obj *body = arg->a_obj; int zmon = zombie_form(&mons[body->corpsenm]); - if (zmon != NON_PM && !(gm.mvitals[zmon].mvflags & G_GENOD)) { + if (zmon != NON_PM && !(svm.mvitals[zmon].mvflags & G_GENOD)) { if (has_omid(body)) free_omid(body); if (has_omonst(body)) @@ -2366,7 +2474,7 @@ zombify_mon(anything *arg, long timeout) } /* return TRUE if hero properties are dangerous to hero */ -static boolean +staticfn boolean danger_uprops(void) { return (Stoned || Slimed || Strangled || Sick); @@ -2408,7 +2516,7 @@ donull(void) return ECMD_TIME; /* Do nothing, but let other things happen */ } -static int +staticfn int wipeoff(void) { unsigned udelta = u.ucreamed; @@ -2481,7 +2589,7 @@ set_wounded_legs(long side, int timex) * You still call this function, but don't lose hp. * Caller is also responsible for adjusting messages. */ - gc.context.botl = 1; + disp.botl = TRUE; if (!Wounded_legs) ATEMP(A_DEX)--; @@ -2501,7 +2609,7 @@ heal_legs( int how) /* 0: ordinary, 1: dismounting steed, 2: limbs turn to stone */ { if (Wounded_legs) { - gc.context.botl = 1; + disp.botl = TRUE; if (ATEMP(A_DEX) < 0) ATEMP(A_DEX)++; @@ -2629,18 +2737,18 @@ restore_valk_locate(xint8 phases) /* You have just arrived on a level, which may undergo some changes to terrain * or other makeup depending on certain factors tracked in the * visited_after_event flag. */ -static void +staticfn void do_level_updates(xint8 phases) { /* Currently the only tracked event is the quest nemesis being killed (but * this does not have to be specific to the Quest). */ - if (!(gl.level.flags.visited_after_event & VISITED_AFTER_NEMDEAD) - && gq.quest_status.killed_nemesis) { + if (!(svl.level.flags.visited_after_event & VISITED_AFTER_NEMDEAD) + && svq.quest_status.killed_nemesis) { /* This is a bit hardcoded; once more things are added here it could be * refactored into a more general structure. */ restore_valk_locate(phases); if (phases & PHASE_SETFLAG) { - gl.level.flags.visited_after_event |= VISITED_AFTER_NEMDEAD; + svl.level.flags.visited_after_event |= VISITED_AFTER_NEMDEAD; } } } diff --git a/src/do_name.c b/src/do_name.c index 279799af95..3c2d4734cf 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -1,39 +1,22 @@ -/* NetHack 3.7 do_name.c $NHDT-Date: 1672605786 2023/01/01 20:43:06 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.280 $ */ +/* 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. */ #include "hack.h" -static char *nextmbuf(void); -static void getpos_getvalids_selection(struct selectionvar *, - boolean (*)(coordxy, coordxy)); -static void selection_force_newsyms(struct selectionvar *); -static void getpos_help_keyxhelp(winid, const char *, const char *, int); -static void getpos_help(boolean, const char *); -static int QSORTCALLBACK cmp_coord_distu(const void *, const void *); -static int gloc_filter_classify_glyph(int); -static int gloc_filter_floodfill_matcharea(coordxy, coordxy); -static void gloc_filter_floodfill(coordxy, coordxy); -static void gloc_filter_init(void); -static void gloc_filter_done(void); -static boolean gather_locs_interesting(coordxy, coordxy, int); -static void gather_locs(coord **, int *, int); -static void truncate_to_map(int *, int *, schar, schar); -static void getpos_refresh(int *); -static char *name_from_player(char *, const char *, const char *); -static void do_mgivenname(void); -static boolean alreadynamed(struct monst *, char *, char *); -static void do_oname(struct obj *); -static char *docall_xname(struct obj *); -static void namefloorobj(void); - -extern const char what_is_an_unknown_object[]; /* from pager.c */ +staticfn char *nextmbuf(void); +staticfn char *name_from_player(char *, const char *, const char *); +staticfn void do_mgivenname(void); +staticfn boolean alreadynamed(struct monst *, char *, char *) NONNULLPTRS; +staticfn void do_oname(struct obj *) NONNULLARG1; +staticfn char *docall_xname(struct obj *) NONNULLARG1; +staticfn void namefloorobj(void); #define NUMMBUF 5 /* manage a pool of BUFSZ buffers, so callers don't have to */ -static char * +staticfn char * nextmbuf(void) { static char NEARDATA bufs[NUMMBUF][BUFSZ]; @@ -43,1109 +26,6 @@ nextmbuf(void) return bufs[bufidx]; } -/* function for getpos() to highlight desired map locations. - * parameter value 0 = initialize, 1 = highlight, 2 = done - */ -static void (*getpos_hilitefunc)(int) = (void (*)(int)) 0; -static boolean - (*getpos_getvalid)(coordxy, coordxy) = (boolean (*)(coordxy, coordxy)) 0; - -void -getpos_sethilite( - void (*gp_hilitef)(int), - boolean (*gp_getvalidf)(coordxy, coordxy)) -{ - boolean was_valid = (getpos_getvalid != NULL); - struct selectionvar *sel = selection_new(); - - getpos_getvalids_selection(sel, getpos_getvalid); - getpos_hilitefunc = gp_hilitef; - getpos_getvalid = gp_getvalidf; - getpos_getvalids_selection(sel, getpos_getvalid); - gw.wsettings.map_frame_color = (getpos_getvalid != NULL) ? CLR_BLUE - : NO_COLOR; - if ((boolean) (getpos_getvalid != NULL) != was_valid) - selection_force_newsyms(sel); - selection_free(sel, TRUE); -} - -boolean -mapxy_valid(coordxy x, coordxy y) -{ - if (getpos_getvalid) - return (*getpos_getvalid)(x, y); - return FALSE; -} - -static void -getpos_getvalids_selection( - struct selectionvar *sel, - boolean (*validf)(coordxy, coordxy)) -{ - coordxy x, y; - - if (!sel || !validf) - return; - - for (x = 1; x < sel->wid; x++) - for (y = 0; y < sel->hei; y++) - if ((*validf)(x, y)) - selection_setpoint(x, y, sel, 1); -} - -static void -selection_force_newsyms(struct selectionvar *sel) -{ - coordxy x, y; - - for (x = 1; x < sel->wid; x++) - for (y = 0; y < sel->hei; y++) - if (selection_getpoint(x, y, sel)) - newsym_force(x, y); -} - -static const char *const gloc_descr[NUM_GLOCS][4] = { - { "any monsters", "monster", "next/previous monster", "monsters" }, - { "any items", "item", "next/previous object", "objects" }, - { "any doors", "door", "next/previous door or doorway", - "doors or doorways" }, - { "any unexplored areas", "unexplored area", "unexplored location", - "locations next to unexplored locations" }, - { "anything interesting", "interesting thing", "anything interesting", - "anything interesting" }, - { "any valid locations", "valid location", "valid location", - "valid locations" } -}; - -static const char *const gloc_filtertxt[NUM_GFILTER] = { - "", - " in view", - " in this area" -}; - -static void -getpos_help_keyxhelp( - winid tmpwin, - const char *k1, const char *k2, - int gloc) -{ - char sbuf[BUFSZ], fbuf[QBUFSZ]; - const char *move_cursor_to = "move the cursor to ", - *filtertxt = gloc_filtertxt[iflags.getloc_filter]; - - if (gloc == GLOC_EXPLORE) { - /* default of "move to unexplored location" is inaccurate - because the position will be one spot short of that */ - move_cursor_to = "move the cursor next to an "; - if (iflags.getloc_usemenu) - /* default is too wide for basic 80-column tty so shorten it - to avoid wrapping */ - filtertxt = strsubst(strcpy(fbuf, filtertxt), - "this area", "area"); - } - Sprintf(sbuf, "Use '%s'/'%s' to %s%s%s.", - k1, k2, - iflags.getloc_usemenu ? "get a menu of " : move_cursor_to, - gloc_descr[gloc][2 + iflags.getloc_usemenu], filtertxt); - putstr(tmpwin, 0, sbuf); -} - -DISABLE_WARNING_FORMAT_NONLITERAL - -/* the response for '?' help request in getpos() */ -static void -getpos_help(boolean force, const char *goal) -{ - static const char *const fastmovemode[2] = { "8 units at a time", - "skipping same glyphs" }; - char sbuf[BUFSZ]; - boolean doing_what_is; - winid tmpwin = create_nhwindow(NHW_MENU); - - Sprintf(sbuf, - "Use '%s', '%s', '%s', '%s' to move the cursor to %s.", /* hjkl */ - visctrl(cmd_from_func(do_move_west)), - visctrl(cmd_from_func(do_move_south)), - visctrl(cmd_from_func(do_move_north)), - visctrl(cmd_from_func(do_move_east)), goal); - putstr(tmpwin, 0, sbuf); - Sprintf(sbuf, - "Use '%s', '%s', '%s', '%s' to fast-move the cursor, %s.", - visctrl(cmd_from_func(do_run_west)), - visctrl(cmd_from_func(do_run_south)), - visctrl(cmd_from_func(do_run_north)), - visctrl(cmd_from_func(do_run_east)), - fastmovemode[iflags.getloc_moveskip]); - putstr(tmpwin, 0, sbuf); - Sprintf(sbuf, "(or prefix normal move with '%s' or '%s' to fast-move)", - visctrl(cmd_from_func(do_run)), - visctrl(cmd_from_func(do_rush))); - putstr(tmpwin, 0, sbuf); - putstr(tmpwin, 0, "Or enter a background symbol (ex. '<')."); - Sprintf(sbuf, "Use '%s' to move the cursor on yourself.", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_SELF])); - putstr(tmpwin, 0, sbuf); - if (!iflags.terrainmode || (iflags.terrainmode & TER_MON) != 0) { - getpos_help_keyxhelp(tmpwin, - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_MON_NEXT]), - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_MON_PREV]), - GLOC_MONS); - } - if (goal && !strcmp(goal, "a monster")) - goto skip_non_mons; - if (!iflags.terrainmode || (iflags.terrainmode & TER_OBJ) != 0) { - getpos_help_keyxhelp(tmpwin, - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_OBJ_NEXT]), - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_OBJ_PREV]), - GLOC_OBJS); - } - if (!iflags.terrainmode || (iflags.terrainmode & TER_MAP) != 0) { - /* these are primarily useful when choosing a travel - destination for the '_' command */ - getpos_help_keyxhelp(tmpwin, - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_DOOR_NEXT]), - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_DOOR_PREV]), - GLOC_DOOR); - getpos_help_keyxhelp(tmpwin, - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_UNEX_NEXT]), - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_UNEX_PREV]), - GLOC_EXPLORE); - getpos_help_keyxhelp(tmpwin, - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_INTERESTING_NEXT]), - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_INTERESTING_PREV]), - GLOC_INTERESTING); - } - Sprintf(sbuf, "Use '%s' to change fast-move mode to %s.", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_MOVESKIP]), - fastmovemode[!iflags.getloc_moveskip]); - putstr(tmpwin, 0, sbuf); - if (!iflags.terrainmode || (iflags.terrainmode & TER_DETECT) == 0) { - Sprintf(sbuf, "Use '%s' to toggle menu listing for possible targets.", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_MENU])); - putstr(tmpwin, 0, sbuf); - Sprintf(sbuf, - "Use '%s' to change the mode of limiting possible targets.", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_LIMITVIEW])); - putstr(tmpwin, 0, sbuf); - } - if (!iflags.terrainmode) { - char kbuf[BUFSZ]; - - if (getpos_getvalid) { - Sprintf(sbuf, "Use '%s' or '%s' to move to valid locations.", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_VALID_NEXT]), - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_VALID_PREV])); - putstr(tmpwin, 0, sbuf); - } - if (getpos_hilitefunc) { - Sprintf(sbuf, "Use '%s' to toggle marking of valid locations.", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_SHOWVALID])); - putstr(tmpwin, 0, sbuf); - } - Sprintf(sbuf, "Use '%s' to toggle automatic description.", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_AUTODESC])); - putstr(tmpwin, 0, sbuf); - if (iflags.cmdassist) { /* assisting the '/' command, I suppose... */ - Sprintf(sbuf, - (iflags.getpos_coords == GPCOORDS_NONE) - ? "(Set 'whatis_coord' option to include coordinates with '%s' text.)" - : "(Reset 'whatis_coord' option to omit coordinates from '%s' text.)", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_AUTODESC])); - } - skip_non_mons: - /* disgusting hack; the alternate selection characters work for any - getpos call, but only matter for dowhatis (and doquickwhatis, - also for dotherecmdmenu's simulated mouse) */ - doing_what_is = (goal == what_is_an_unknown_object); - if (doing_what_is) { - Sprintf(kbuf, "'%s' or '%s' or '%s' or '%s'", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK]), - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_Q]), - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_O]), - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_V])); - } else { - Sprintf(kbuf, "'%s'", visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK])); - } - Snprintf(sbuf, sizeof(sbuf), - "Type a %s when you are at the right place.", kbuf); - putstr(tmpwin, 0, sbuf); - if (doing_what_is) { - Sprintf(sbuf, - " '%s' describe current spot, show 'more info', move to another spot.", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_V])); - putstr(tmpwin, 0, sbuf); - Sprintf(sbuf, - " '%s' describe current spot,%s move to another spot;", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK]), - flags.help && !force ? " prompt if 'more info'," : ""); - putstr(tmpwin, 0, sbuf); - Sprintf(sbuf, - " '%s' describe current spot, move to another spot;", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_Q])); - putstr(tmpwin, 0, sbuf); - Sprintf(sbuf, - " '%s' describe current spot, stop looking at things;", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_O])); - putstr(tmpwin, 0, sbuf); - } - } - if (!force) - putstr(tmpwin, 0, "Type Space or Escape when you're done."); - putstr(tmpwin, 0, ""); - display_nhwindow(tmpwin, TRUE); - destroy_nhwindow(tmpwin); -} - -RESTORE_WARNING_FORMAT_NONLITERAL - -static int QSORTCALLBACK -cmp_coord_distu(const void *a, const void *b) -{ - const coord *c1 = a; - const coord *c2 = b; - int dx, dy, dist_1, dist_2; - - dx = u.ux - c1->x; - dy = u.uy - c1->y; - dist_1 = max(abs(dx), abs(dy)); - dx = u.ux - c2->x; - dy = u.uy - c2->y; - dist_2 = max(abs(dx), abs(dy)); - - if (dist_1 == dist_2) - return (c1->y != c2->y) ? (c1->y - c2->y) : (c1->x - c2->x); - - return dist_1 - dist_2; -} - -#define IS_UNEXPLORED_LOC(x,y) \ - (isok((x), (y)) \ - && glyph_is_unexplored(levl[(x)][(y)].glyph) \ - && !levl[(x)][(y)].seenv) - -#define GLOC_SAME_AREA(x,y) \ - (isok((x), (y)) \ - && (selection_getpoint((x),(y), gg.gloc_filter_map))) - -static int -gloc_filter_classify_glyph(int glyph) -{ - int c; - - if (!glyph_is_cmap(glyph)) - return 0; - - c = glyph_to_cmap(glyph); - - if (is_cmap_room(c) || is_cmap_furniture(c)) - return 1; - else if (is_cmap_wall(c) || c == S_tree) - return 2; - else if (is_cmap_corr(c)) - return 3; - else if (is_cmap_water(c)) - return 4; - else if (is_cmap_lava(c)) - return 5; - return 0; -} - -static int -gloc_filter_floodfill_matcharea(coordxy x, coordxy y) -{ - int glyph = back_to_glyph(x, y); - - if (!levl[x][y].seenv) - return FALSE; - - if (glyph == gg.gloc_filter_floodfill_match_glyph) - return TRUE; - - if (gloc_filter_classify_glyph(glyph) - == gloc_filter_classify_glyph(gg.gloc_filter_floodfill_match_glyph)) - return TRUE; - - return FALSE; -} - -static void -gloc_filter_floodfill(coordxy x, coordxy y) -{ - gg.gloc_filter_floodfill_match_glyph = back_to_glyph(x, y); - - set_selection_floodfillchk(gloc_filter_floodfill_matcharea); - selection_floodfill(gg.gloc_filter_map, x, y, FALSE); -} - -static void -gloc_filter_init(void) -{ - if (iflags.getloc_filter == GFILTER_AREA) { - if (!gg.gloc_filter_map) { - gg.gloc_filter_map = selection_new(); - } - /* special case: if we're in a doorway, try to figure out which - direction we're moving, and use that side of the doorway */ - if (IS_DOOR(levl[u.ux][u.uy].typ)) { - if (u.dx || u.dy) { - gloc_filter_floodfill(u.ux + u.dx, u.uy + u.dy); - } else { - /* TODO: maybe add both sides of the doorway? */ - } - } else { - gloc_filter_floodfill(u.ux, u.uy); - } - } -} - -static void -gloc_filter_done(void) -{ - if (gg.gloc_filter_map) { - selection_free(gg.gloc_filter_map, TRUE); - gg.gloc_filter_map = (struct selectionvar *) 0; - - } -} - -DISABLE_WARNING_UNREACHABLE_CODE - -static boolean -gather_locs_interesting(coordxy x, coordxy y, int gloc) -{ - int glyph, sym; - - if (iflags.getloc_filter == GFILTER_VIEW && !cansee(x, y)) - return FALSE; - if (iflags.getloc_filter == GFILTER_AREA && !GLOC_SAME_AREA(x, y) - && !GLOC_SAME_AREA(x - 1, y) && !GLOC_SAME_AREA(x, y - 1) - && !GLOC_SAME_AREA(x + 1, y) && !GLOC_SAME_AREA(x, y + 1)) - return FALSE; - - glyph = glyph_at(x, y); - sym = glyph_is_cmap(glyph) ? glyph_to_cmap(glyph) : -1; - switch (gloc) { - default: - case GLOC_MONS: - /* unlike '/M', this skips monsters revealed by - warning glyphs and remembered unseen ones */ - return (glyph_is_monster(glyph) - && glyph != monnum_to_glyph(PM_LONG_WORM_TAIL,MALE) - && glyph != monnum_to_glyph(PM_LONG_WORM_TAIL, FEMALE)); - case GLOC_OBJS: - return (glyph_is_object(glyph) - && glyph != objnum_to_glyph(BOULDER) - && glyph != objnum_to_glyph(ROCK)); - case GLOC_DOOR: - return (glyph_is_cmap(glyph) - && (is_cmap_door(sym) - || is_cmap_drawbridge(sym) - || sym == S_ndoor)); - case GLOC_EXPLORE: - return (glyph_is_cmap(glyph) - && !glyph_is_nothing(glyph_to_cmap(glyph)) - && (is_cmap_door(sym) - || is_cmap_drawbridge(sym) - || sym == S_ndoor - || is_cmap_room(sym) - || is_cmap_corr(sym)) - && (IS_UNEXPLORED_LOC(x + 1, y) - || IS_UNEXPLORED_LOC(x - 1, y) - || IS_UNEXPLORED_LOC(x, y + 1) - || IS_UNEXPLORED_LOC(x, y - 1))); - case GLOC_VALID: - if (getpos_getvalid) - return (*getpos_getvalid)(x, y); - /*FALLTHRU*/ - case GLOC_INTERESTING: - return (gather_locs_interesting(x, y, GLOC_DOOR) - || !((glyph_is_cmap(glyph) - && (is_cmap_wall(sym) - || sym == S_tree - || sym == S_bars - || sym == S_ice - || sym == S_air - || sym == S_cloud - || is_cmap_lava(sym) - || is_cmap_water(sym) - || sym == S_ndoor - || is_cmap_room(sym) - || is_cmap_corr(sym))) - || glyph_is_nothing(glyph) - || glyph_is_unexplored(glyph))); - } - /*NOTREACHED*/ - return FALSE; -} - -RESTORE_WARNINGS - -/* gather locations for monsters or objects shown on the map */ -static void -gather_locs(coord **arr_p, int *cnt_p, int gloc) -{ - int pass, idx; - coordxy x, y; - - /* - * We always include the hero's location even if there is no monster - * (invisible hero without see invisible) or object (usual case) - * displayed there. That way, the count will always be at least 1, - * and player has a visual indicator (cursor returns to hero's spot) - * highlighting when successive 'm's or 'o's have cycled all the way - * through all monsters or objects. - * - * Hero's spot will always sort to array[0] because it will always - * be the shortest distance (namely, 0 units) away from . - */ - - gloc_filter_init(); - - *cnt_p = idx = 0; - for (pass = 0; pass < 2; pass++) { - for (x = 1; x < COLNO; x++) - for (y = 0; y < ROWNO; y++) { - if (u_at(x, y) || gather_locs_interesting(x, y, gloc)) { - if (!pass) { - ++*cnt_p; - } else { - (*arr_p)[idx].x = x; - (*arr_p)[idx].y = y; - ++idx; - } - } - } - - if (!pass) /* end of first pass */ - *arr_p = (coord *) alloc(*cnt_p * sizeof (coord)); - else /* end of second pass */ - qsort(*arr_p, *cnt_p, sizeof (coord), cmp_coord_distu); - } /* pass */ - - gloc_filter_done(); -} - -char * -dxdy_to_dist_descr(coordxy dx, coordxy dy, boolean fulldir) -{ - static char buf[30]; - int dst; - - if (!dx && !dy) { - Sprintf(buf, "here"); - } else if ((dst = xytod(dx, dy)) != -1) { - /* explicit direction; 'one step' is implicit */ - Sprintf(buf, "%s", directionname(dst)); - } else { - static const char *const dirnames[4][2] = { - { "n", "north" }, - { "s", "south" }, - { "w", "west" }, - { "e", "east" } }; - buf[0] = '\0'; - /* 9999: protect buf[] against overflow caused by invalid values */ - if (dy) { - if (abs(dy) > 9999) - dy = sgn(dy) * 9999; - Sprintf(eos(buf), "%d%s%s", abs(dy), dirnames[(dy > 0)][fulldir], - dx ? "," : ""); - } - if (dx) { - if (abs(dx) > 9999) - dx = sgn(dx) * 9999; - Sprintf(eos(buf), "%d%s", abs(dx), - dirnames[2 + (dx > 0)][fulldir]); - } - } - return buf; -} - -DISABLE_WARNING_FORMAT_NONLITERAL - -/* coordinate formatting for 'whatis_coord' option */ -char * -coord_desc(coordxy x, coordxy y, char *outbuf, char cmode) -{ - static char screen_fmt[16]; /* [12] suffices: "[%02d,%02d]" */ - int dx, dy; - - outbuf[0] = '\0'; - switch (cmode) { - default: - break; - case GPCOORDS_COMFULL: - case GPCOORDS_COMPASS: - /* "east", "3s", "2n,4w" */ - dx = x - u.ux; - dy = y - u.uy; - Sprintf(outbuf, "(%s)", - dxdy_to_dist_descr(dx, dy, cmode == GPCOORDS_COMFULL)); - break; - case GPCOORDS_MAP: /* x,y */ - /* upper left corner of map is <1,0>; - with default COLNO,ROWNO lower right corner is <79,20> */ - Sprintf(outbuf, "<%d,%d>", x, y); - break; - case GPCOORDS_SCREEN: /* y+2,x */ - /* for normal map sizes, force a fixed-width formatting so that - /m, /M, /o, and /O output lines up cleanly; map sizes bigger - than Nx999 or 999xM will still work, but not line up like normal - when displayed in a column setting. - - The (100) is placed in brackets below to mark the [: "03"] as - explicit compile-time dead code for clang */ - if (!*screen_fmt) - Sprintf(screen_fmt, "[%%%sd,%%%sd]", - (ROWNO - 1 + 2 < (100)) ? "02" : "03", - (COLNO - 1 < (100)) ? "02" : "03"); - /* map line 0 is screen row 2; - map column 0 isn't used, map column 1 is screen column 1 */ - Sprintf(outbuf, screen_fmt, y + 2, x); - break; - } - return outbuf; -} - -RESTORE_WARNING_FORMAT_NONLITERAL - -void -auto_describe(coordxy cx, coordxy cy) -{ - coord cc; - int sym = 0; - char tmpbuf[BUFSZ]; - const char *firstmatch = "unknown"; - - cc.x = cx; - cc.y = cy; - if (do_screen_description(cc, TRUE, sym, tmpbuf, &firstmatch, - (struct permonst **) 0)) { - (void) coord_desc(cx, cy, tmpbuf, iflags.getpos_coords); - custompline((SUPPRESS_HISTORY | OVERRIDE_MSGTYPE | NO_CURS_ON_U), - "%s%s%s%s%s", firstmatch, *tmpbuf ? " " : "", tmpbuf, - (iflags.autodescribe - && getpos_getvalid && !(*getpos_getvalid)(cx, cy)) - ? " (invalid target)" : "", - (iflags.getloc_travelmode && !is_valid_travelpt(cx, cy)) - ? " (no travel path)" : ""); - curs(WIN_MAP, cx, cy); - flush_screen(0); - } -} - -boolean -getpos_menu(coord *ccp, int gloc) -{ - coord *garr = DUMMY; - int gcount = 0; - winid tmpwin; - anything any; - int i, pick_cnt; - menu_item *picks = (menu_item *) 0; - char tmpbuf[BUFSZ]; - int clr = 0; - - gather_locs(&garr, &gcount, gloc); - - if (gcount < 2) { /* gcount always includes the hero */ - free((genericptr_t) garr); - You("cannot %s %s.", - (iflags.getloc_filter == GFILTER_VIEW) ? "see" : "detect", - gloc_descr[gloc][0]); - return FALSE; - } - - tmpwin = create_nhwindow(NHW_MENU); - start_menu(tmpwin, MENU_BEHAVE_STANDARD); - any = cg.zeroany; - - /* gather_locs returns array[0] == you. skip it. */ - for (i = 1; i < gcount; i++) { - char fullbuf[BUFSZ]; - coord tmpcc; - const char *firstmatch = "unknown"; - int sym = 0; - - any.a_int = i + 1; - tmpcc.x = garr[i].x; - tmpcc.y = garr[i].y; - if (do_screen_description(tmpcc, TRUE, sym, tmpbuf, - &firstmatch, (struct permonst **)0)) { - (void) coord_desc(garr[i].x, garr[i].y, tmpbuf, - iflags.getpos_coords); - Snprintf(fullbuf, sizeof fullbuf, "%s%s%s", firstmatch, - (*tmpbuf ? " " : ""), tmpbuf); - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, fullbuf, MENU_ITEMFLAGS_NONE); - } - } - - Sprintf(tmpbuf, "Pick %s%s%s", - an(gloc_descr[gloc][1]), - gloc_filtertxt[iflags.getloc_filter], - iflags.getloc_travelmode ? " for travel destination" : ""); - end_menu(tmpwin, tmpbuf); - pick_cnt = select_menu(tmpwin, PICK_ONE, &picks); - destroy_nhwindow(tmpwin); - if (pick_cnt > 0) { - ccp->x = garr[picks->item.a_int - 1].x; - ccp->y = garr[picks->item.a_int - 1].y; - free((genericptr_t) picks); - } - free((genericptr_t) garr); - return (pick_cnt > 0); -} - -/* add dx,dy to cx,cy, truncating at map edges */ -static void -truncate_to_map(int *cx, int *cy, schar dx, schar dy) -{ - /* diagonal moves complicate this... */ - if (*cx + dx < 1) { - dy -= sgn(dy) * (1 - (*cx + dx)); - dx = 1 - *cx; /* so that (cx+dx == 1) */ - } else if (*cx + dx > COLNO - 1) { - dy += sgn(dy) * ((COLNO - 1) - (*cx + dx)); - dx = (COLNO - 1) - *cx; - } - if (*cy + dy < 0) { - dx -= sgn(dx) * (0 - (*cy + dy)); - dy = 0 - *cy; /* so that (cy+dy == 0) */ - } else if (*cy + dy > ROWNO - 1) { - dx += sgn(dx) * ((ROWNO - 1) - (*cy + dy)); - dy = (ROWNO - 1) - *cy; - } - *cx += dx; - *cy += dy; -} - -enum hilite_states { - Hilite_Inactive = 0, /* no highlighting of valid target spots */ - Hilite_Active = 1, /* '$' has just highlighted valid target spots */ - Hilite_Passive = 2, /* second '$' will unhighlight */ -}; - -/* called when ^R typed or for SHOWVALID if second '$' typed to explicitly - reverse previous '$' for highlighting valid target spots */ -static void -getpos_refresh(int *hilite_statep) -{ - if (*hilite_statep == Hilite_Active) - (*getpos_hilitefunc)(2); /* tmp_at(DISP_END) */ - docrt(); /* redraw everything */ - *hilite_statep = Hilite_Inactive; -} - -/* have the player use movement keystrokes to position the cursor at a - particular map location, then use one of [.,:;] to pick the spot */ -int -getpos(coord *ccp, boolean force, const char *goal) -{ - static struct { - int nhkf, ret; - } const pick_chars_def[] = { - { NHKF_GETPOS_PICK, LOOK_TRADITIONAL }, - { NHKF_GETPOS_PICK_Q, LOOK_QUICK }, - { NHKF_GETPOS_PICK_O, LOOK_ONCE }, - { NHKF_GETPOS_PICK_V, LOOK_VERBOSE } - }; - static const int mMoOdDxX_def[] = { - NHKF_GETPOS_MON_NEXT, - NHKF_GETPOS_MON_PREV, - NHKF_GETPOS_OBJ_NEXT, - NHKF_GETPOS_OBJ_PREV, - NHKF_GETPOS_DOOR_NEXT, - NHKF_GETPOS_DOOR_PREV, - NHKF_GETPOS_UNEX_NEXT, - NHKF_GETPOS_UNEX_PREV, - NHKF_GETPOS_INTERESTING_NEXT, - NHKF_GETPOS_INTERESTING_PREV, - NHKF_GETPOS_VALID_NEXT, - NHKF_GETPOS_VALID_PREV - }; - struct _cmd_queue cq, *cmdq; - const char *cp; - char pick_chars[6]; - char mMoOdDxX[13]; - int result = 0; - int cx, cy, i, c; - int sidx; - coordxy tx = u.ux, ty = u.uy, vx = 0, vy = 0; - boolean msg_given = TRUE; /* clear message window by default */ - boolean show_goal_msg = FALSE; - int hilite_state = Hilite_Inactive; - coord *garr[NUM_GLOCS] = DUMMY; - int gcount[NUM_GLOCS] = DUMMY; - int gidx[NUM_GLOCS] = DUMMY; - schar udx = u.dx, udy = u.dy, udz = u.dz; - int dx, dy; - boolean rushrun = FALSE; - - /* temporary? if we have a queued direction, return the adjacent spot - in that direction */ - if (!gi.in_doagain) { - if ((cmdq = cmdq_pop()) != 0) { - cq = *cmdq; - free((genericptr_t) cmdq); - if (cq.typ == CMDQ_DIR && !cq.dirz) { - ccp->x = u.ux + cq.dirx; - ccp->y = u.uy + cq.diry; - } else { - cmdq_clear(CQ_CANNED); - result = -1; - } - return result; - } - } - - for (i = 0; i < SIZE(pick_chars_def); i++) - pick_chars[i] = gc.Cmd.spkeys[pick_chars_def[i].nhkf]; - pick_chars[SIZE(pick_chars_def)] = '\0'; - - for (i = 0; i < SIZE(mMoOdDxX_def); i++) - mMoOdDxX[i] = gc.Cmd.spkeys[mMoOdDxX_def[i]]; - mMoOdDxX[SIZE(mMoOdDxX_def)] = '\0'; - - handle_tip(TIP_GETPOS); - - if (!goal) - goal = "desired location"; - if (Verbose(0, getpos1)) { - pline("(For instructions type a '%s')", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_HELP])); - msg_given = TRUE; - } - cx = ccp->x; - cy = ccp->y; -#ifdef CLIPPING - cliparound(cx, cy); -#endif - curs(WIN_MAP, cx, cy); - flush_screen(0); -#ifdef MAC - lock_mouse_cursor(TRUE); -#endif - lock_mouse_buttons(TRUE); - for (;;) { - if (show_goal_msg) { - pline("Move cursor to %s:", goal); - curs(WIN_MAP, cx, cy); - flush_screen(0); - show_goal_msg = FALSE; - } else if (iflags.autodescribe && !msg_given) { - auto_describe(cx, cy); - } - - rushrun = FALSE; - - if ((cmdq = cmdq_pop()) != 0) { - if (cmdq->typ == CMDQ_KEY) { - c = cmdq->key; - } else { - cmdq_clear(CQ_CANNED); - result = -1; - goto exitgetpos; - } - free(cmdq); - } else { - c = readchar_poskey(&tx, &ty, &sidx); - /* remember_getpos is normally False because reusing the - cursor positioning during ^A is almost never the right - thing to do, but caller could set it if that was needed */ - if (iflags.remember_getpos && !gi.in_doagain) - cmdq_add_key(CQ_REPEAT, c); - } - - /* update SHOWVALID if it is in use */ - if (hilite_state == Hilite_Active) { - /* 'valid spot' glyph gets reset to whatever it was covering */ - (*getpos_hilitefunc)(2); /* tmp_at(DISP_END) */ - hilite_state = Hilite_Passive; - curs(WIN_MAP, cx, cy); - flush_screen(0); - } - - if (iflags.autodescribe) - msg_given = FALSE; - - if (c == gc.Cmd.spkeys[NHKF_ESC]) { - cx = cy = -10; - msg_given = TRUE; /* force clear */ - result = -1; - break; - } - if (c == cmd_from_func(do_run) || c == cmd_from_func(do_rush)) { - c = readchar_poskey(&tx, &ty, &sidx); - rushrun = TRUE; - } - if (c == 0) { - if (!isok(tx, ty)) - continue; - /* a mouse click event, just assign and return */ - cx = tx; - cy = ty; - break; - } - if ((cp = strchr(pick_chars, c)) != 0) { - /* '.' => 0, ',' => 1, ';' => 2, ':' => 3 */ - result = pick_chars_def[(int) (cp - pick_chars)].ret; - break; - } else if (movecmd(c, MV_WALK)) { - if (rushrun) - goto do_rushrun; - dx = u.dx; - dy = u.dy; - truncate_to_map(&cx, &cy, dx, dy); - goto nxtc; - } else if (movecmd(c, MV_RUSH) || movecmd(c, MV_RUN)) { - do_rushrun: - if (iflags.getloc_moveskip) { - /* skip same glyphs */ - int glyph = glyph_at(cx, cy); - - dx = u.dx; - dy = u.dy; - while (isok(cx + dx, cy + dy) - && glyph == glyph_at(cx + dx, cy + dy) - && isok(cx + dx + u.dx, cy + dy + u.dy) - && glyph == glyph_at(cx + dx + u.dx, cy + dy + u.dy)) { - dx += u.dx; - dy += u.dy; - - } - } else { - dx = 8 * u.dx; - dy = 8 * u.dy; - } - truncate_to_map(&cx, &cy, dx, dy); - goto nxtc; - } - - if (c == gc.Cmd.spkeys[NHKF_GETPOS_HELP] || redraw_cmd(c)) { - /* '?' will redraw twice, first when removing popup text window - after showing the help text, then to reset highlighting */ - if (c == gc.Cmd.spkeys[NHKF_GETPOS_HELP]) - getpos_help(force, goal); - /* ^R: docrt(), hilite_state = Hilite_Inactive */ - getpos_refresh(&hilite_state); - /* update message window to reflect that we're still targeting */ - show_goal_msg = TRUE; - } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_SHOWVALID]) { - if (getpos_hilitefunc) { - if (hilite_state == Hilite_Inactive - || (hilite_state == Hilite_Passive - && (cx != vx || cy != vy))) { - /* toggling 'showvalid' on */ - (*getpos_hilitefunc)(0); /* tmp_at(DISP_start) */ - (*getpos_hilitefunc)(1); /* update appropriate spots */ - hilite_state = Hilite_Active; - vx = cx, vy = cy; - } else { - /* 'showvalid' was on, toggle it off: - docrt(), hilite_state = Hilite_Inactive */ - getpos_refresh(&hilite_state); - vx = vy = 0; - } - curs(WIN_MAP, cx, cy); - } - show_goal_msg = TRUE; /* we're still targeting */ - goto nxtc; - } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_AUTODESC]) { - iflags.autodescribe = !iflags.autodescribe; - pline("Automatic description %sis %s.", - Verbose(0, getpos2) ? "of features under cursor " : "", - iflags.autodescribe ? "on" : "off"); - if (!iflags.autodescribe) - show_goal_msg = TRUE; - msg_given = TRUE; - goto nxtc; - } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_LIMITVIEW]) { - static const char *const view_filters[NUM_GFILTER] = { - "Not limiting targets", - "Limiting targets to those in sight", - "Limiting targets to those in same area" - }; - - iflags.getloc_filter = (iflags.getloc_filter + 1) % NUM_GFILTER; - for (i = 0; i < NUM_GLOCS; i++) { - if (garr[i]) { - free((genericptr_t) garr[i]); - garr[i] = NULL; - } - gidx[i] = gcount[i] = 0; - } - pline("%s.", view_filters[iflags.getloc_filter]); - msg_given = TRUE; - goto nxtc; - } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_MENU]) { - iflags.getloc_usemenu = !iflags.getloc_usemenu; - pline("%s a menu to show possible targets%s.", - iflags.getloc_usemenu ? "Using" : "Not using", - iflags.getloc_usemenu - ? " for 'm|M', 'o|O', 'd|D', and 'x|X'" : ""); - msg_given = TRUE; - goto nxtc; - } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_SELF]) { - /* reset 'm&M', 'o&O', &c; otherwise, there's no way for player - to achieve that except by manually cycling through all spots */ - for (i = 0; i < NUM_GLOCS; i++) - gidx[i] = 0; - cx = u.ux; - cy = u.uy; - goto nxtc; - } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_MOVESKIP]) { - iflags.getloc_moveskip = !iflags.getloc_moveskip; - pline("%skipping over similar terrain when fastmoving the cursor.", - iflags.getloc_moveskip ? "S" : "Not s"); - msg_given = TRUE; - goto nxtc; - } else if ((cp = strchr(mMoOdDxX, c)) != 0) { /* 'm|M', 'o|O', &c */ - /* nearest or farthest monster or object or door or unexplored */ - int gtmp = (int) (cp - mMoOdDxX), /* 0..7 */ - gloc = gtmp >> 1; /* 0..3 */ - - if (iflags.getloc_usemenu) { - coord tmpcrd; - - if (getpos_menu(&tmpcrd, gloc)) { - cx = tmpcrd.x; - cy = tmpcrd.y; - } - goto nxtc; - } - - if (!garr[gloc]) { - gather_locs(&garr[gloc], &gcount[gloc], gloc); - gidx[gloc] = 0; /* garr[][0] is hero's spot */ - } - if (!(gtmp & 1)) { /* c=='m' || c=='o' || c=='d' || c=='x') */ - gidx[gloc] = (gidx[gloc] + 1) % gcount[gloc]; - } else { /* c=='M' || c=='O' || c=='D' || c=='X') */ - if (--gidx[gloc] < 0) - gidx[gloc] = gcount[gloc] - 1; - } - cx = garr[gloc][gidx[gloc]].x; - cy = garr[gloc][gidx[gloc]].y; - goto nxtc; - } else { - if (!strchr(quitchars, c)) { - char matching[MAXPCHARS]; - int pass, lo_x, lo_y, hi_x, hi_y, k = 0; - - (void) memset((genericptr_t) matching, 0, sizeof matching); - for (sidx = 0; sidx < MAXPCHARS; sidx++) { - /* don't even try to match some terrain: walls, room... */ - if (is_cmap_wall(sidx) || is_cmap_room(sidx) - || is_cmap_corr(sidx) || is_cmap_door(sidx) - || sidx == S_ndoor) - continue; - if (c == defsyms[sidx].sym - || c == (int) gs.showsyms[sidx] - /* have '^' match webs and vibrating square or any - other trap that uses something other than '^' */ - || (c == '^' && is_cmap_trap(sidx))) - matching[sidx] = (char) ++k; - } - if (k) { - for (pass = 0; pass <= 1; pass++) { - /* pass 0: just past current pos to lower right; - pass 1: upper left corner to current pos */ - lo_y = (pass == 0) ? cy : 0; - hi_y = (pass == 0) ? ROWNO - 1 : cy; - for (ty = lo_y; ty <= hi_y; ty++) { - lo_x = (pass == 0 && ty == lo_y) ? cx + 1 : 1; - hi_x = (pass == 1 && ty == hi_y) ? cx : COLNO - 1; - for (tx = lo_x; tx <= hi_x; tx++) { - /* first, look at what is currently visible - (might be monster) */ - k = glyph_at(tx, ty); - if (glyph_is_cmap(k) - && matching[glyph_to_cmap(k)]) - goto foundc; - /* next, try glyph that's remembered here - (might be trap or object) */ - if (gl.level.flags.hero_memory - /* !terrainmode: don't move to remembered - trap or object if not currently shown */ - && !iflags.terrainmode) { - k = levl[tx][ty].glyph; - if (glyph_is_cmap(k) - && matching[glyph_to_cmap(k)]) - goto foundc; - } - /* last, try actual terrain here (shouldn't - we be using gl.lastseentyp[][] instead?) */ - if (levl[tx][ty].seenv) { - k = back_to_glyph(tx, ty); - if (glyph_is_cmap(k) - && matching[glyph_to_cmap(k)]) - goto foundc; - } - continue; - foundc: - cx = tx, cy = ty; - if (msg_given) { - clear_nhwindow(WIN_MESSAGE); - msg_given = FALSE; - } - goto nxtc; - } /* column */ - } /* row */ - } /* pass */ - pline("Can't find dungeon feature '%c'.", c); - msg_given = TRUE; - goto nxtc; - } else { - char note[QBUFSZ]; - - if (!force) - Strcpy(note, "aborted"); - else /* hjkl */ - Sprintf(note, "use '%s', '%s', '%s', '%s' or '%s'", - visctrl(cmd_from_func(do_move_west)), - visctrl(cmd_from_func(do_move_south)), - visctrl(cmd_from_func(do_move_north)), - visctrl(cmd_from_func(do_move_east)), - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK])); - pline("Unknown direction: '%s' (%s).", visctrl((char) c), - note); - msg_given = TRUE; - } /* k => matching */ - } /* !quitchars */ - if (force) - goto nxtc; - pline("Done."); - msg_given = FALSE; /* suppress clear */ - cx = -1; - cy = 0; - result = 0; /* not -1 */ - break; - } - nxtc: - ; -#ifdef CLIPPING - cliparound(cx, cy); -#endif - curs(WIN_MAP, cx, cy); - flush_screen(0); - } - exitgetpos: -#ifdef MAC - lock_mouse_cursor(FALSE); -#endif - lock_mouse_buttons(FALSE); - if (msg_given) - clear_nhwindow(WIN_MESSAGE); - ccp->x = cx; - ccp->y = cy; - for (i = 0; i < NUM_GLOCS; i++) - if (garr[i]) - free((genericptr_t) garr[i]); - getpos_sethilite(NULL, NULL); - u.dx = udx, u.dy = udy, u.dz = udz; - return result; -} - /* allocate space for a monster's name; removes old name if there is one */ void new_mgivenname( @@ -1221,7 +101,7 @@ safe_oname(struct obj *obj) /* get a name for a monster or an object from player; truncate if longer than PL_PSIZ, then return it */ -static char * +staticfn char * name_from_player( char *outbuf, /* output buffer, assumed to be at least BUFSZ long; * anything longer than PL_PSIZ will be truncated */ @@ -1274,7 +154,7 @@ christen_monst(struct monst *mtmp, const char *name) /* check whether user-supplied name matches or nearly matches an unnameable monster's name, or is an attempt to delete the monster's name; if so, give alternate reject message for do_mgivenname() */ -static boolean +staticfn boolean alreadynamed(struct monst *mtmp, char *monnambuf, char *usrbuf) { char pronounbuf[10], *p; @@ -1315,13 +195,14 @@ alreadynamed(struct monst *mtmp, char *monnambuf, char *usrbuf) } /* allow player to assign a name to some chosen monster */ -static void +staticfn void do_mgivenname(void) { char buf[BUFSZ], monnambuf[BUFSZ], qbuf[QBUFSZ]; coord cc; int cx, cy; struct monst *mtmp = 0; + boolean do_swallow = FALSE; if (Hallucination) { You("would never recognize it anyway."); @@ -1339,18 +220,29 @@ do_mgivenname(void) mtmp = u.usteed; } else { pline("This %s creature is called %s and cannot be renamed.", - beautiful(), gp.plname); + beautiful(), svp.plname); return; } } else mtmp = m_at(cx, cy); - if (!mtmp + /* Allow you to name the monster that has swallowed you */ + if (!mtmp && u.uswallow) { + int glyph = glyph_at(cx, cy); + + if (glyph_is_swallow(glyph)) { + mtmp = u.ustuck; + do_swallow = TRUE; + } + } + + if (!do_swallow && (!mtmp || (!sensemon(mtmp) && (!(cansee(cx, cy) || see_with_infrared(mtmp)) || mtmp->mundetected || M_AP_TYPE(mtmp) == M_AP_FURNITURE || M_AP_TYPE(mtmp) == M_AP_OBJECT - || (mtmp->minvis && !See_invisible)))) { + || (mtmp->minvis && !See_invisible))))) { + pline("I see no monster there."); return; } @@ -1394,7 +286,7 @@ do_mgivenname(void) * used with extreme care. Applying a name to an object no longer * allocates a replacement object, so that old risk is gone. */ -static void +staticfn void do_oname(struct obj *obj) { char buf[BUFSZ], qbuf[QBUFSZ]; @@ -1440,6 +332,9 @@ do_oname(struct obj *obj) yname(obj)); return; } else if (obj->otyp == objtyp) { + /* artifact_name() always returns non-Null when it sets objtyp */ + assert(aname != 0); + /* artifact_name() found a match and restrict_name() didn't reject it; since 'obj' is the right type, naming will change it into an artifact so use canonical capitalization (Sting or Orcrist) */ @@ -1519,7 +414,7 @@ weapon_oname(struct obj *wpn) } /* if a name couldn't be found, fall through to default */ } - const char* name = wpn_names[rn2(SIZE(wpn_names))]; + const char* name = ROLL_FROM(wpn_names); if (strstri(name, "%s")) { Sprintf(buf, name, upwords(basename)); return oname(wpn, buf, ONAME_NO_FLAGS); @@ -1615,6 +510,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: @@ -1678,7 +574,7 @@ docallcmd(void) char ch = 0; /* if player wants a,b,c instead of i,o when looting, do that here too */ boolean abc = flags.lootabc; - int clr = 0; + int clr = NO_COLOR; if ((cmdq = cmdq_pop()) != 0) { cq = *cmdq; @@ -1773,7 +669,7 @@ docallcmd(void) } /* for use by safe_qbuf() */ -static char * +staticfn char * docall_xname(struct obj *obj) { struct obj otemp; @@ -1812,45 +708,44 @@ void docall(struct obj *obj) { char buf[BUFSZ], qbuf[QBUFSZ]; - char **str1; + char **uname_p; + boolean had_name = FALSE; if (!obj->dknown) - return; /* probably blind */ + return; /* probably blind; Blind || Hallucination for 'fromsink' */ flush_screen(1); /* buffered updates might matter to player's response */ if (obj->oclass == POTION_CLASS && obj->fromsink) - /* kludge, meaning it's sink water */ + /* fromsink: kludge, meaning it's sink water */ Sprintf(qbuf, "Call a stream of %s fluid:", OBJ_DESCR(objects[obj->otyp])); else (void) safe_qbuf(qbuf, "Call ", ":", obj, docall_xname, simpleonames, "thing"); /* pointer to old name */ - str1 = &(objects[obj->otyp].oc_uname); + uname_p = &(objects[obj->otyp].oc_uname); /* use getlin() to get a name string from the player */ - if (!name_from_player(buf, qbuf, *str1)) + if (!name_from_player(buf, qbuf, *uname_p)) return; /* clear old name */ - if (*str1) - free((genericptr_t) *str1); + if (*uname_p) { + had_name = TRUE; + free((genericptr_t) *uname_p), *uname_p = NULL; /* clear oc_uname */ + } /* strip leading and trailing spaces; uncalls item if all spaces */ (void) mungspaces(buf); if (!*buf) { - if (*str1) { /* had name, so possibly remove from disco[] */ - /* strip name first, for the update_inventory() call - from undiscover_object() */ - *str1 = (char *) 0; + if (had_name) /* possibly remove from disco[]; old *uname_p is gone */ undiscover_object(obj->otyp); - } } else { - *str1 = dupstr(buf); + *uname_p = dupstr(buf); discover_object(obj->otyp, FALSE, TRUE); /* possibly add to disco[] */ } } -static void +staticfn void namefloorobj(void) { coord cc; @@ -1927,7 +822,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 */ } } @@ -1946,8 +841,8 @@ static const char *const ghostnames[] = { const char * rndghostname(void) { - return rn2(7) ? ghostnames[rn2(SIZE(ghostnames))] - : (const char *) gp.plname; + return rn2(7) ? ROLL_FROM(ghostnames) + : (const char *) svp.plname; } /* @@ -1955,12 +850,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 * adj_monnam: the poor newt It the poor invisible orc the poor Fido * Some_Monnam: The newt Someone The invisible orc Fido * or Something @@ -1969,6 +864,7 @@ rndghostname(void) * a_monnam: a newt it an invisible orc Fido * m_monnam: newt xan orc Fido * y_monnam: your newt your xan your invisible orc Fido + * YMonnam: Your newt Your xan Your invisible orc Fido * noname_monnam(mon,article): * article newt art xan art invisible orc art dog */ @@ -1986,8 +882,14 @@ rndghostname(void) * suppress * * SUPPRESS_IT, SUPPRESS_INVISIBLE, SUPPRESS_HALLUCINATION, SUPPRESS_SADDLE. + * SUPPRESS_MAPPEARANCE: if monster is mimicking another monster (cloned + * Wizard or quickmimic pet), describe the real monster rather + * than its current form; * EXACT_NAME: combination of all the above * SUPPRESS_NAME: omit monster's assigned name (unless uniq w/ pname). + * AUGMENT_IT: not suppression but shares suppression bitmask; if result + * would have been "it", return "someone" if humanoid or + * "something" otherwise. * * Bug: if the monster is a priest or shopkeeper, not every one of these * options works, since those are special cases. @@ -2002,25 +904,39 @@ x_monnam( { char *buf = nextmbuf(); struct permonst *mdat = mtmp->data; - const char *pm_name = mon_pmname(mtmp); - boolean do_hallu, do_invis, do_it, do_saddle, do_name, augment_it; - boolean name_at_start, has_adjectives, insertbuf2; + const char *pm_name; + boolean do_hallu, do_invis, do_it, do_saddle, do_mappear, + do_exact, do_name, augment_it; + boolean name_at_start, has_adjectives, insertbuf2, + mappear_as_mon = (M_AP_TYPE(mtmp) == M_AP_MONSTER); char *bp, buf2[BUFSZ]; if (mtmp == &gy.youmonst) return strcpy(buf, "you"); /* ignore article, "invisible", &c */ - if (gp.program_state.gameover) + if (program_state.gameover) suppress |= SUPPRESS_HALLUCINATION; if (article == ARTICLE_YOUR && !mtmp->mtame) article = ARTICLE_THE; + if (u.uswallow && mtmp == u.ustuck) { + /* + * This monster has become important, for the moment anyway. + * As the hero's consumer, it is worthy of ARTICLE_THE. + * Also, suppress invisible as that particular characteristic + * is unimportant now and you can see its interior anyway. + */ + article = ARTICLE_THE; + suppress |= SUPPRESS_INVISIBLE; + } do_hallu = Hallucination && !(suppress & SUPPRESS_HALLUCINATION); do_invis = mtmp->minvis && !(suppress & SUPPRESS_INVISIBLE); do_it = !canspotmon(mtmp) && article != ARTICLE_YOUR - && !gp.program_state.gameover && mtmp != u.usteed + && !program_state.gameover && mtmp != u.usteed && !engulfing_u(mtmp) && !(suppress & SUPPRESS_IT); do_saddle = !(suppress & SUPPRESS_SADDLE); + do_mappear = mappear_as_mon && !(suppress & SUPPRESS_MAPPEARANCE); + do_exact = (suppress & EXACT_NAME) == EXACT_NAME; do_name = !(suppress & SUPPRESS_NAME) || type_is_pname(mdat); augment_it = (suppress & AUGMENT_IT) != 0; @@ -2029,14 +945,17 @@ x_monnam( /* unseen monsters, etc.; usually "it" but sometimes more specific; when hallucinating, the more specific values might be inverted */ if (do_it) { + /* !is_animal excludes all Y; !mindless excludes Z, M, \' */ + boolean s_one = humanoid(mdat) && !is_animal(mdat) && !mindless(mdat); + Strcpy(buf, !augment_it ? "it" - : (!do_hallu ? humanoid(mdat) : !rn2(2)) ? "someone" + : (!do_hallu ? s_one : !rn2(2)) ? "someone" : "something"); return buf; } /* priests and minions: don't even use this function */ - if (mtmp->ispriest || mtmp->isminion) { + if ((mtmp->ispriest || mtmp->isminion) && !do_mappear) { char *name; long save_prop = EHalluc_resistance; unsigned save_invis = mtmp->minvis; @@ -2046,28 +965,29 @@ x_monnam( EHalluc_resistance = 1L; if (!do_invis) mtmp->minvis = 0; - name = priestname(mtmp, article, buf2); + /* EXACT_NAME will force "of " on the Astral Plane */ + name = priestname(mtmp, article, do_exact, buf2); EHalluc_resistance = save_prop; mtmp->minvis = save_invis; if (article == ARTICLE_NONE && !strncmp(name, "the ", 4)) name += 4; return strcpy(buf, name); } -#if 0 /* [now handled by mon_pmname()] */ - /* an "aligned priest" not flagged as a priest or minion should be - "priest" or "priestess" (normally handled by priestname()) */ - if (mdat == &mons[PM_ALIGNED_CLERIC]) - pm_name = mtmp->female ? "priestess" : "priest"; - else if (mdat == &mons[PM_HIGH_CLERIC] && mtmp->female) - pm_name = "high priestess"; -#endif + + /* 'pm_name' is the base part of most names */ + if (do_mappear) { + /*assert(ismnum(mtmp->mappearance));*/ + pm_name = pmname(&mons[mtmp->mappearance], Mgender(mtmp)); + } else { + pm_name = mon_pmname(mtmp); + } /* Shopkeepers: use shopkeeper name. For normal shopkeepers, just * "Asidonhopo"; for unusual ones, "Asidonhopo the invisible * shopkeeper" or "Asidonhopo the blue dragon". If hallucinating, * none of this applies. */ - if (mtmp->isshk && !do_hallu) { + if (mtmp->isshk && !do_hallu && !do_mappear) { if (adjective && article == ARTICLE_THE) { /* pathological case: "the angry Asidonhopo the blue dragon" sounds silly */ @@ -2192,14 +1112,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); @@ -2219,19 +1140,19 @@ some_mon_nam(struct monst *mtmp) char * Monnam(struct monst *mtmp) { - register char *bp = mon_nam(mtmp); + char *bp = mon_nam(mtmp); *bp = highc(*bp); - return bp; + return bp; } char * noit_Monnam(struct monst *mtmp) { - register char *bp = noit_mon_nam(mtmp); + char *bp = noit_mon_nam(mtmp); *bp = highc(*bp); - return bp; + return bp; } char * @@ -2240,7 +1161,7 @@ Some_Monnam(struct monst *mtmp) char *bp = some_mon_nam(mtmp); *bp = highc(*bp); - return bp; + return bp; } /* return "a dog" rather than "Fido", honoring hallucination and visibility */ @@ -2274,6 +1195,16 @@ y_monnam(struct monst *mtmp) return x_monnam(mtmp, prefix, (char *) 0, suppression_flag, FALSE); } +/* y_monnam() for start of sentence */ +char * +YMonnam(struct monst *mtmp) +{ + char *bp = y_monnam(mtmp); + + *bp = highc(*bp); + return bp; +} + char * adj_monnam(struct monst *mtmp, const char *adj) { @@ -2287,7 +1218,7 @@ Adjmonnam(struct monst *mtmp, const char *adj) { char *bp = adj_monnam(mtmp, adj); *bp = highc(*bp); - return bp; + return bp; } char * @@ -2303,7 +1234,7 @@ Amonnam(struct monst *mtmp) char *bp = a_monnam(mtmp); *bp = highc(*bp); - return bp; + return bp; } /* used for monster ID by the '/', ';', and 'C' commands to block remote @@ -2318,7 +1249,7 @@ distant_monnam( unless you're adjacent (overridden for hallucination which does its own obfuscation) */ if (mon->data == &mons[PM_HIGH_CLERIC] && !Hallucination - && Is_astralevel(&u.uz) && !next2u(mon->mx, mon->my)) { + && Is_astralevel(&u.uz) && !m_next2u(mon)) { Strcpy(outbuf, article == ARTICLE_THE ? "the " : ""); Strcat(outbuf, mon->female ? "high priestess" : "high priest"); } else { @@ -2411,7 +1342,7 @@ minimal_monnam(struct monst *mon, boolean ckloc) fmt_ptr((genericptr_t) mon->data), fmt_ptr((genericptr_t) &mons[NUMMONS])); } else if (ckloc && ptr == &mons[PM_LONG_WORM] && mon->mx - && gl.level.monsters[mon->mx][mon->my] != mon) { + && svl.level.monsters[mon->mx][mon->my] != mon) { Sprintf(outbuf, "%s <%d,%d>", pmname(&mons[PM_LONG_WORM_TAIL], Mgender(mon)), mon->mx, mon->my); @@ -2455,7 +1386,7 @@ const char * mon_pmname(struct monst *mon) { /* for neuter, mon->data->pmnames[MALE] will be Null and use [NEUTRAL] */ - return pmname(mon->data, mon->female ? FEMALE : MALE); + return pmname(mon->data, Mgender(mon)); } /* mons[]->pmname for a corpse or statue or figurine */ @@ -2470,12 +1401,12 @@ obj_pmname(struct obj *obj) struct monst *m = OMONST(obj); /* obj->oextra->omonst->data is Null but ...->mnum is set */ - if (m->mnum >= LOW_PM) - return pmname(&mons[m->mnum], m->female ? FEMALE : MALE); + if (ismnum(m->mnum)) + return pmname(&mons[m->mnum], Mgender(m)); } #endif if ((obj->otyp == CORPSE || obj->otyp == STATUE || obj->otyp == FIGURINE) - && obj->corpsenm >= LOW_PM) { + && ismnum(obj->corpsenm)) { int cgend = (obj->spe & CORPSTAT_GENDER), mgend = ((cgend == CORPSTAT_MALE) ? MALE : (cgend == CORPSTAT_FEMALE) ? FEMALE @@ -2502,7 +1433,8 @@ obj_pmname(struct obj *obj) /* used by bogusmon(next) and also by init_CapMons(rumors.c); bogon_is_pname(below) checks a hard-coded subset of these rather than - use this list */ + use this list. + Also used in rumors.c */ const char bogon_codes[] = "-_+|="; /* see dat/bonusmon.txt */ /* fake monsters used to be in a hard-coded array, now in a data file @@ -2638,7 +1570,7 @@ const char * hliquid( const char *liquidpref) /* use as-is when not hallucintg (unless empty) */ { - boolean hallucinate = Hallucination && !gp.program_state.gameover; + boolean hallucinate = Hallucination && !program_state.gameover; if (hallucinate || !liquidpref || !*liquidpref) { int indx, count = SIZE(hliquids); @@ -2648,7 +1580,7 @@ hliquid( if (liquidpref && *liquidpref) ++count; indx = rn2_on_display_rng(count); - if (indx < SIZE(hliquids)) + if (IndexOk(indx, hliquids)) return hliquids[indx]; } return liquidpref; @@ -2712,7 +1644,7 @@ rndorcname(char *s) for (i = 0; i < iend; ++i) { vstart = 1 - vstart; /* 0 -> 1, 1 -> 0 */ Sprintf(eos(s), "%s%s", (i > 0 && !rn2(30)) ? "-" : "", - vstart ? v[rn2(SIZE(v))] : snd[rn2(SIZE(snd))]); + vstart ? ROLL_FROM(v) : ROLL_FROM(snd)); } } return s; @@ -2819,7 +1751,7 @@ lookup_novel(const char *lookname, int *idx) } } /* name not found; if novelidx is already set, override the name */ - if (idx && *idx >= 0 && *idx < SIZE(sir_Terry_novels)) + if (idx && IndexOk(*idx, sir_Terry_novels)) return sir_Terry_novels[*idx]; return (const char *) 0; @@ -2845,7 +1777,6 @@ const char* femdemonnames[] = { "Euryale", "Zorya", "Rhaenyra", "Bellatrix", "Rusalka", "Messaana", "Jadis", "Anzu", "Eve", "Bilquis", "Cyndane", "Vanessa", "Graendal" }; -#define rnd_name(list) list[rn2(SIZE(list))] /* Monster introduces themselves. If they're not currently named, give them a * random name from the specified list. */ @@ -2855,13 +1786,13 @@ mintroduce(struct monst *mtmp) if (!has_mgivenname(mtmp)) { const char* name; if (mtmp->data->mlet == S_NYMPH) { - name = rnd_name(nymphnames); + name = ROLL_FROM(nymphnames); } else if (is_demon(mtmp->data)) { if (mtmp->female) - name = rnd_name(femdemonnames); + name = ROLL_FROM(femdemonnames); else - name = rnd_name(maldemonnames); + name = ROLL_FROM(maldemonnames); } else { impossible("mintroduce: monster type %s has no defined names!", @@ -2925,8 +1856,8 @@ mon_wounds(struct monst *mon, "trashed", "smashed up", "crumpled", "scratched", "scraped", "cracked", "creased", "shredded", "shattered", "notched" }; - adverb = wound_adverbs[rn2(SIZE(wound_adverbs))]; - adjective = wound_adjectives[rn2(SIZE(wound_adjectives))]; + adverb = ROLL_FROM(wound_adverbs); + adjective = ROLL_FROM(wound_adjectives); } else if (mon->mhp * 6 <= mon->mhpmax) { /* <= 16.6% */ adverb = "critically"; @@ -2960,10 +1891,6 @@ print_mon_wounded(struct monst *mon, int pre_wound_hp) int mcurrhp; char *m_old_wounds, *m_curr_wounds; - if (!mon) { - impossible("print_mon_wounded: null mon!"); - return; - } if (!Role_if(PM_HEALER)) { return; /* optimization; redundant with healer check in mon_wounds */ } diff --git a/src/do_wear.c b/src/do_wear.c index d9534377fb..8c50e6cf28 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 do_wear.c $NHDT-Date: 1650875489 2022/04/25 08:31:29 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.156 $ */ +/* 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. */ @@ -20,36 +20,41 @@ static NEARDATA const long takeoff_order[] = { WORN_SHIRT, WORN_BOOTS, W_SWAPWEP, W_QUIVER, 0L }; -static void on_msg(struct obj *); -static void toggle_stealth(struct obj *, long, boolean); -static int Armor_on(void); +staticfn void on_msg(struct obj *); +staticfn void toggle_stealth(struct obj *, long, boolean); +staticfn int Armor_on(void); /* int Boots_on(void); -- moved to extern.h */ -static int Cloak_on(void); -static int Helmet_on(void); -static int Gloves_on(void); -static int Shield_on(void); -static int Shirt_on(void); -static void Amulet_on(void); -static void learnring(struct obj *, boolean); -static void adjust_attrib(struct obj *, int, int); -static void Ring_off_or_gone(struct obj *, boolean); -static int select_off(struct obj *); -static struct obj *do_takeoff(void); -static int take_off(void); -static int menu_remarm(int); -static void wornarm_destroyed(struct obj *); -static void count_worn_stuff(struct obj **, boolean); -static int armor_or_accessory_off(struct obj *); -static int accessory_or_armor_on(struct obj *); -static void already_wearing(const char *); -static void already_wearing2(const char *, const char *); -static int equip_ok(struct obj *, boolean, boolean); -static int puton_ok(struct obj *); -static int remove_ok(struct obj *); -static int wear_ok(struct obj *); -static int takeoff_ok(struct obj *); -static int wornmask_to_bodypart(int); -static void toggle_armor_light(struct obj *, boolean); +staticfn int Cloak_on(void); +staticfn int Helmet_on(void); +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(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); +staticfn int select_off(struct obj *); +staticfn struct obj *do_takeoff(void); +staticfn int take_off(void); +staticfn int menu_remarm(int); +staticfn void wornarm_destroyed(struct obj *); +staticfn void count_worn_stuff(struct obj **, boolean); +staticfn int armor_or_accessory_off(struct obj *); +staticfn int accessory_or_armor_on(struct obj *); +staticfn void already_wearing(const char *); +staticfn void already_wearing2(const char *, const char *); +staticfn int equip_ok(struct obj *, boolean, boolean); +staticfn int puton_ok(struct obj *); +staticfn int remove_ok(struct obj *); +staticfn int wear_ok(struct obj *); +staticfn int takeoff_ok(struct obj *); +staticfn int wornmask_to_bodypart(int); +staticfn void toggle_armor_light(struct obj *, boolean); +/* 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 * @@ -63,15 +68,24 @@ fingers_or_gloves(boolean check_gloves) void off_msg(struct obj *otmp) { - if (Verbose(0, off_msg)) + if (flags.verbose) You("were wearing %s.", doname(otmp)); } /* for items that involve no delay */ -static void +staticfn void on_msg(struct obj *otmp) { - if (Verbose(0, on_msg)) { + /* 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 might set obj->dknown and that affects the pname test */ @@ -86,15 +100,17 @@ on_msg(struct obj *otmp) } /* putting on or taking off an item which confers stealth; - give feedback and discover it iff stealth state is changing */ -static + give feedback and discover it iff stealth state is changing; + stealth is blocked by riding unless hero+steed fly (handled with + BStealth by mount and dismount routines) */ +staticfn void toggle_stealth( struct obj *obj, long oldprop, /* prop[].extrinsic, with obj->owornmask pre-stripped */ boolean on) { - if (on ? gi.initial_don : gc.context.takeoff.cancelled_don) + if (on ? gi.initial_don : svc.context.takeoff.cancelled_don) return; if (!oldprop /* extrinsic stealth from something else */ @@ -102,7 +118,7 @@ toggle_stealth( && !BStealth) { /* stealth blocked by something */ if (obj->otyp == RIN_STEALTH) learnring(obj, TRUE); - else + else /* discover elven cloak or elven boots */ makeknown(obj->otyp); if (on) { @@ -113,7 +129,13 @@ toggle_stealth( else You("walk very quietly."); } else { - You("sure are noisy."); + boolean riding = (u.usteed != NULL); + + You("%s%s are noisy.", riding ? "and " : "sure", + riding ? x_monnam(u.usteed, ARTICLE_YOUR, (char *) NULL, + (SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION), + FALSE) + : ""); } } } @@ -124,12 +146,13 @@ toggle_stealth( hero is able to see self (or sense monsters); for timed, 'obj' is Null and this is only called for the message */ void -toggle_displacement(struct obj *obj, - long oldprop, /* prop[].extrinsic, with obj->owornmask - stripped by caller */ - boolean on) +toggle_displacement( + struct obj *obj, + long oldprop, /* prop[].extrinsic, with obj->owornmask + stripped by caller */ + boolean on) { - if (on ? gi.initial_don : gc.context.takeoff.cancelled_don) + if (on ? gi.initial_don : svc.context.takeoff.cancelled_don) return; if (!oldprop /* extrinsic displacement from something else */ @@ -210,8 +233,22 @@ Boots_on(void) Your("%s feel longer.", makeplural(body_part(LEG))); 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; @@ -237,7 +274,7 @@ Boots_on(void) * so uarmf could be Null below; status line * gets updated during brief interval they're * worn so hero and player learn enchantment */ - gc.context.botl = 1; /* status hilites might mark AC changed */ + disp.botl = TRUE; /* status hilites might mark AC changed */ makeknown(uarmf->otyp); float_up(); if (Levitation) @@ -249,8 +286,11 @@ Boots_on(void) default: impossible(unknown_type, c_boots, uarmf->otyp); } - if (uarmf) /* could be Null here (levitation boots put on over a sink) */ + /* uarmf could be Null here (levitation boots put on over a sink) */ + if (uarmf && !uarmf->known) { uarmf->known = 1; /* boots' +/- evident because of status line AC */ + update_inventory(); + } return 0; } @@ -261,14 +301,14 @@ Boots_off(void) int otyp = otmp->otyp; long oldprop = u.uprops[objects[otyp].oc_oprop].extrinsic & ~WORN_BOOTS; - gc.context.takeoff.mask &= ~W_ARMF; + svc.context.takeoff.mask &= ~W_ARMF; /* For levitation, float_down() returns if Levitation, so we * must do a setworn() _before_ the levitation case. */ setworn((struct obj *) 0, W_ARMF); switch (otyp) { case SPEED_BOOTS: - if (!Very_fast && !gc.context.takeoff.cancelled_don) { + if (!Very_fast && !svc.context.takeoff.cancelled_don) { makeknown(otyp); You_feel("yourself slow down%s.", Fast ? " a bit" : ""); } @@ -277,7 +317,7 @@ Boots_off(void) /* check for lava since fireproofed boots make it viable */ if ((is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy)) && !Levitation && !Flying && grounded(gy.youmonst.data) - && !gc.context.takeoff.cancelled_don + && !svc.context.takeoff.cancelled_don /* avoid recursive call to lava_effects() */ && !iflags.in_lava_effects) { /* make boots known in case you survive the drowning */ @@ -294,7 +334,7 @@ Boots_off(void) break; case LEVITATION_BOOTS: if (!oldprop && !HLevitation && !(BLevitation & FROMOUTSIDE) - && !gc.context.takeoff.cancelled_don) { + && !svc.context.takeoff.cancelled_don) { /* lava_effects() sets in_lava_effects and calls Boots_off() so hero is already in midst of floating down */ if (!iflags.in_lava_effects) @@ -316,11 +356,11 @@ Boots_off(void) default: impossible(unknown_type, c_boots, otyp); } - gc.context.takeoff.cancelled_don = FALSE; + svc.context.takeoff.cancelled_don = FALSE; return 0; } -static int +staticfn int Cloak_on(void) { int otyp = uarmc->otyp; @@ -379,8 +419,10 @@ Cloak_on(void) default: impossible(unknown_type, c_cloak, uarmc->otyp); } - if (uarmc) /* no known instance of !uarmc here but play it safe */ + if (uarmc && !uarmc->known) { /* no known instance of !uarmc here */ uarmc->known = 1; /* cloak's +/- evident because of status line AC */ + update_inventory(); + } toggle_armor_light(uarmc, TRUE); return 0; } @@ -398,7 +440,7 @@ Cloak_off(void) otyp = GRAY_DRAGON_SCALES; } - gc.context.takeoff.mask &= ~W_ARMC; + svc.context.takeoff.mask &= ~W_ARMC; /* For mummy wrapping, taking it off first resets `Invisible'. */ setworn((struct obj *) 0, W_ARMC); switch (otyp) { @@ -445,13 +487,16 @@ Cloak_off(void) return 0; } -static int +staticfn int Helmet_on(void) { cursed_gear_welds(uarmh); switch (uarmh->otyp) { case FEDORA: + if (Role_if(PM_ARCHEOLOGIST)) + change_luck(1); + break; case HELMET: case DENTED_POT: case ELVEN_HELM: @@ -470,7 +515,7 @@ Helmet_on(void) but it takes trained arrogance to pull it off, and the actual enchantment of the hat is irrelevant */ ABON(A_CHA) += (Role_if(PM_WIZARD) ? 1 : -1); - gc.context.botl = 1; + disp.botl = TRUE; makeknown(uarmh->otyp); break; case HELM_OF_OPPOSITE_ALIGNMENT: @@ -482,8 +527,9 @@ Helmet_on(void) uchangealign((u.ualign.type != A_NEUTRAL) ? -u.ualign.type : (uarmh->o_id % 2) ? A_CHAOTIC : A_LAWFUL, - 1); + A_CG_HELM_ON); /* makeknown(HELM_OF_OPPOSITE_ALIGNMENT); -- below, after Tobjnam() */ + FALLTHROUGH; /*FALLTHRU*/ case DUNCE_CAP: if (uarmh && !uarmh->cursed) { @@ -502,7 +548,7 @@ Helmet_on(void) else if (uarmh->bknown) update_inventory(); /* keep bknown as-is; display the curse */ } - gc.context.botl = 1; /* reveal new alignment or INT & WIS */ + disp.botl = TRUE; /* reveal new alignment or INT & WIS */ if (Hallucination) { pline("My brain hurts!"); /* Monty Python's Flying Circus */ } else if (uarmh && uarmh->otyp == DUNCE_CAP) { @@ -520,18 +566,23 @@ Helmet_on(void) impossible(unknown_type, c_helmet, uarmh->otyp); } /* uarmh could be Null due to uchangealign() */ - if (uarmh) + if (uarmh && !uarmh->known) { uarmh->known = 1; /* helmet's +/- evident because of status line AC */ + update_inventory(); + } return 0; } int Helmet_off(void) { - gc.context.takeoff.mask &= ~W_ARMH; + svc.context.takeoff.mask &= ~W_ARMH; switch (uarmh->otyp) { case FEDORA: + if (Role_if(PM_ARCHEOLOGIST)) + change_luck(-1); + break; case HELMET: case DENTED_POT: case ELVEN_HELM: @@ -539,12 +590,12 @@ Helmet_off(void) case ORCISH_HELM: break; case DUNCE_CAP: - gc.context.botl = 1; + disp.botl = TRUE; break; case CORNUTHAUM: - if (!gc.context.takeoff.cancelled_don) { + if (!svc.context.takeoff.cancelled_don) { ABON(A_CHA) += (Role_if(PM_WIZARD) ? -1 : 1); - gc.context.botl = 1; + disp.botl = TRUE; } break; case HELM_OF_TELEPATHY: @@ -554,24 +605,35 @@ Helmet_off(void) see_monsters(); return 0; case HELM_OF_BRILLIANCE: - if (!gc.context.takeoff.cancelled_don) + if (!svc.context.takeoff.cancelled_don) adj_abon(uarmh, -uarmh->spe); break; case HELM_OF_OPPOSITE_ALIGNMENT: /* changing alignment can toggle off active artifact properties, including levitation; uarmh could get dropped or destroyed here */ - uchangealign(u.ualignbase[A_CURRENT], 2); + uchangealign(u.ualignbase[A_CURRENT], A_CG_HELM_OFF); break; default: impossible(unknown_type, c_helmet, uarmh->otyp); } setworn((struct obj *) 0, W_ARMH); - gc.context.takeoff.cancelled_don = FALSE; + svc.context.takeoff.cancelled_don = FALSE; return 0; } -static int +/* hard helms provide better protection against falling rocks */ +boolean +hard_helmet(struct obj *obj) +{ + if (!obj || !is_helmet(obj)) + return FALSE; + return (is_metallic(obj) || is_crackable(obj) + || (obj->material == WOOD) || (obj->material == BONE) + || (obj->material == MINERAL)) ? TRUE : FALSE; +} + +staticfn int Gloves_on(void) { long oldprop = @@ -588,7 +650,7 @@ Gloves_on(void) break; case GAUNTLETS_OF_POWER: makeknown(uarmg->otyp); - gc.context.botl = 1; /* taken care of in attrib.c */ + disp.botl = TRUE; /* taken care of in attrib.c */ break; case GAUNTLETS_OF_DEXTERITY: adj_abon(uarmg, uarmg->spe); @@ -596,8 +658,10 @@ Gloves_on(void) default: impossible(unknown_type, c_gloves, uarmg->otyp); } - if (uarmg) /* no known instance of !uarmg here but play it safe */ + if (!uarmg->known) { uarmg->known = 1; /* gloves' +/- evident because of status line AC */ + update_inventory(); + } return 0; } @@ -647,9 +711,9 @@ Gloves_off(void) struct obj *gloves = uarmg; /* needed after uarmg has been set to Null */ long oldprop = u.uprops[objects[uarmg->otyp].oc_oprop].extrinsic & ~WORN_GLOVES; - boolean on_purpose = !gc.context.mon_moving && !uarmg->in_use; + boolean on_purpose = !svc.context.mon_moving && !uarmg->in_use; - gc.context.takeoff.mask &= ~W_ARMG; + svc.context.takeoff.mask &= ~W_ARMG; switch (uarmg->otyp) { case GLOVES: @@ -660,17 +724,17 @@ Gloves_off(void) break; case GAUNTLETS_OF_POWER: makeknown(uarmg->otyp); - gc.context.botl = 1; /* taken care of in attrib.c */ + disp.botl = TRUE; /* taken care of in attrib.c */ break; case GAUNTLETS_OF_DEXTERITY: - if (!gc.context.takeoff.cancelled_don) + if (!svc.context.takeoff.cancelled_don) adj_abon(uarmg, -uarmg->spe); break; default: impossible(unknown_type, c_gloves, uarmg->otyp); } setworn((struct obj *) 0, W_ARMG); - gc.context.takeoff.cancelled_don = FALSE; + svc.context.takeoff.cancelled_don = FALSE; (void) encumber_msg(); /* immediate feedback for GoP */ /* usually can't remove gloves when they're slippery but it can @@ -701,12 +765,12 @@ Gloves_off(void) wielding_corpse(uswapwep, gloves, on_purpose); if (condtests[bl_bareh].enabled) - gc.context.botl = 1; + disp.botl = TRUE; return 0; } -static int +staticfn int Shield_on(void) { cursed_gear_welds(uarms); @@ -727,15 +791,17 @@ Shield_on(void) default: impossible(unknown_type, c_shield, uarms->otyp); } - if (uarms) /* no known instance of !uarms here but play it safe */ + if (!uarms->known) { uarms->known = 1; /* shield's +/- evident because of status line AC */ + update_inventory(); + } return 0; } int Shield_off(void) { - gc.context.takeoff.mask &= ~W_ARMS; + svc.context.takeoff.mask &= ~W_ARMS; /* no shield currently requires special handling when taken off, but we keep this uncommented in case somebody adds a new one which does */ @@ -756,7 +822,7 @@ Shield_off(void) return 0; } -static int +staticfn int Shirt_on(void) { cursed_gear_welds(uarmu); @@ -770,15 +836,17 @@ Shirt_on(void) default: impossible(unknown_type, c_shirt, uarmu->otyp); } - if (uarmu) /* no known instances of !uarmu here but play it safe */ + if (!uarmu->known) { uarmu->known = 1; /* shirt's +/- evident because of status line AC */ + update_inventory(); + } return 0; } int Shirt_off(void) { - gc.context.takeoff.mask &= ~W_ARMU; + svc.context.takeoff.mask &= ~W_ARMU; /* no shirt currently requires special handling when taken off, but we keep this uncommented in case somebody adds a new one which does */ @@ -824,7 +892,7 @@ dragon_armor_handling( EFast |= W_ARM; } else { EFast &= ~W_ARM; - if (!Very_fast && !gc.context.takeoff.cancelled_don) + if (!Very_fast && !svc.context.takeoff.cancelled_don) You("slow down."); } break; @@ -845,7 +913,7 @@ dragon_armor_handling( break; case GOLD_DRAGON_SCALES: (void) make_hallucinated((long) !puton, - gp.program_state.restoring ? FALSE : TRUE, + program_state.restoring ? FALSE : TRUE, W_ARM); break; case ORANGE_DRAGON_SCALES: @@ -879,16 +947,19 @@ dragon_armor_handling( } } -static int +staticfn int Armor_on(void) { cursed_gear_welds(uarm); if (!uarm) /* no known instances of !uarm here but play it safe */ return 0; - uarm->known = 1; /* suit's +/- evident because of status line AC */ + if (!uarm->known) { + uarm->known = 1; /* suit's +/- evident because of status line AC */ + update_inventory(); + } dragon_armor_handling(uarm, TRUE, TRUE); - /* gold DSM requires special handling since it emits light when worn; + /* gold DSM requires extra handling since it emits light when worn; do that after the special armor handling */ toggle_armor_light(uarm, TRUE); return 0; @@ -900,9 +971,9 @@ Armor_off(void) struct obj *otmp = uarm; boolean was_arti_light = otmp && otmp->lamplit && artifact_light(otmp); - gc.context.takeoff.mask &= ~W_ARM; + svc.context.takeoff.mask &= ~W_ARM; setworn((struct obj *) 0, W_ARM); - gc.context.takeoff.cancelled_don = FALSE; + svc.context.takeoff.cancelled_don = FALSE; /* taking off yellow dragon scales/mail might be fatal; arti_light comes from gold dragon scales/mail so they don't overlap, but @@ -926,9 +997,9 @@ Armor_gone(void) struct obj *otmp = uarm; boolean was_arti_light = otmp && otmp->lamplit && artifact_light(otmp); - gc.context.takeoff.mask &= ~W_ARM; + svc.context.takeoff.mask &= ~W_ARM; setnotworn(uarm); - gc.context.takeoff.cancelled_don = FALSE; + svc.context.takeoff.cancelled_don = FALSE; /* losing yellow dragon scales/mail might be fatal; arti_light comes from gold dragon scales/mail so they don't overlap, but @@ -944,17 +1015,14 @@ Armor_gone(void) return 0; } -static void -Amulet_on(void) +staticfn 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); /* avoid amulet of change's messages but NOT strangulation - print cursed * msg before strangulation */ @@ -967,9 +1035,26 @@ Amulet_on(void) case AMULET_OF_LIFE_SAVING: case AMULET_VERSUS_POISON: case AMULET_OF_REFLECTION: - case AMULET_OF_MAGICAL_BREATHING: case FAKE_AMULET_OF_YENDOR: break; + case AMULET_OF_MAGICAL_BREATHING: { + boolean was_in_poison_gas; + + /* amulet is already on; we need to check hero's gas-cloud status + when it was off */ + EMagical_breathing &= ~W_AMUL; + was_in_poison_gas = region_danger(); + EMagical_breathing |= W_AMUL; + if (was_in_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 */ + break; + } case AMULET_OF_UNCHANGING: if (Slimed) { pline("The slime stops progressing, but does not vanish."); @@ -977,51 +1062,63 @@ Amulet_on(void) } 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"); - gc.context.botl = 1; - 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; - gc.context.botl = TRUE; + 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; } case AMULET_OF_FLYING: - /* setworn() has already set extrinisic flying */ + /* setworn() has already set extrinsic flying */ float_vs_flight(); /* block flying if levitating */ if (Flying) { boolean already_flying; @@ -1034,7 +1131,9 @@ Amulet_on(void) if (!already_flying) { makeknown(AMULET_OF_FLYING); - gc.context.botl = TRUE; /* status: 'Fly' On */ + on_msg(uamul); + on_msg_done = TRUE; + disp.botl = TRUE; /* status: 'Fly' On */ You("are now in flight."); } } @@ -1046,19 +1145,28 @@ Amulet_on(void) case AMULET_OF_YENDOR: break; } + + if (!on_msg_done) + on_msg(uamul); } void Amulet_off(void) { - gc.context.takeoff.mask &= ~W_AMUL; + 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: @@ -1071,26 +1179,39 @@ Amulet_off(void) } 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; + } + 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; - gc.context.botl = TRUE; + disp.botl = TRUE; if (Breathless) Your("%s is no longer constricted!", body_part(NECK)); else You("can breathe more easily!"); + mkn = TRUE; } break; case AMULET_OF_RESTFUL_SLEEP: @@ -1098,20 +1219,25 @@ 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); - gc.context.botl = TRUE; /* status: 'Fly' Off */ + disp.botl = TRUE; /* status: 'Fly' Off */ You("%s.", (is_pool_or_lava(u.ux, u.uy) || is_open_air(u.ux, u.uy) || Is_waterlevel(&u.uz) || Is_airlevel(&u.uz)) ? "stop flying" : "land"); + mkn = TRUE; /* makeknown(AMULET_OF_FLYING) */ spoteffects(TRUE); } break; @@ -1122,17 +1248,22 @@ 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; } /* handle ring discovery; comparable to learnwand() */ -static void +staticfn void learnring(struct obj *ring, boolean observed) { int ringtype = ring->otyp; - /* if effect was observeable then we usually discover the type */ + /* if effect was observable then we usually discover the type */ if (observed) { /* if we already know the ring type which accomplishes this effect (assumes there is at most one type for each effect), @@ -1157,7 +1288,7 @@ learnring(struct obj *ring, boolean observed) } } -static void +staticfn void adjust_attrib(struct obj *obj, int which, int val) { int old_attrib; @@ -1173,11 +1304,11 @@ adjust_attrib(struct obj *obj, int which, int val) already discovered, both handled by learnring()] */ if (observable || !extremeattr(which)) learnring(obj, observable); - gc.context.botl = 1; + disp.botl = TRUE; } void -Ring_on(register struct obj *obj) +Ring_on(struct obj *obj) { long oldprop = u.uprops[objects[obj->otyp].oc_oprop].extrinsic; boolean observable; @@ -1214,7 +1345,9 @@ Ring_on(register 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_CARRYING: /* with inventory weights available, this is trivial to identify if it's @@ -1292,13 +1425,13 @@ Ring_on(register struct obj *obj) } } -static void -Ring_off_or_gone(register struct obj *obj, boolean gone) +staticfn void +Ring_off_or_gone(struct obj *obj, boolean gone) { long mask = (obj->owornmask & W_RING); boolean observable; - gc.context.takeoff.mask &= ~mask; + svc.context.takeoff.mask &= ~mask; if (!(u.uprops[objects[obj->otyp].oc_oprop].extrinsic & mask)) impossible("Strange... I didn't know you had that ring."); if (gone) @@ -1388,10 +1521,11 @@ Ring_off_or_gone(register 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; } } @@ -1414,8 +1548,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); @@ -1423,7 +1556,7 @@ Blindf_on(struct obj *otmp) if (Blind && !already_blind) { changed = TRUE; - if (Verbose(0, Blindf_on)) + if (flags.verbose) You_cant("see any more."); /* set ball&chain variables before the hero goes blind */ if (Punished) @@ -1461,7 +1594,7 @@ Blindf_off(struct obj *otmp) impossible("Blindf_off without eyewear?"); return; } - gc.context.takeoff.mask &= ~W_TOOL; + svc.context.takeoff.mask &= ~W_TOOL; setworn((struct obj *) 0, otmp->owornmask); if (!nooffmsg) off_msg(otmp); @@ -1494,8 +1627,8 @@ Blindf_off(struct obj *otmp) /* called in moveloop()'s prologue to set side-effects of worn start-up items; also used by poly_obj() when a worn item gets transformed */ void -set_wear(struct obj *obj) /* if null, do all worn items; - * otherwise just obj itself */ +set_wear( + struct obj *obj) /* if Null, do all worn items; otherwise just obj */ { gi.initial_don = !obj; @@ -1506,7 +1639,7 @@ set_wear(struct obj *obj) /* if null, do all worn items; 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(); @@ -1560,7 +1693,7 @@ donning(struct obj *otmp) boolean doffing(struct obj *otmp) { - long what = gc.context.takeoff.what; + long what = svc.context.takeoff.what; boolean result = FALSE; /* 'T' (or 'R' used for armor) sets ga.afternmv, 'A' sets takeoff.what */ @@ -1604,16 +1737,16 @@ cancel_doff(struct obj *obj, long slotmask) { /* Called by setworn() for old item in specified slot or by setnotworn() * for specified item. We don't want to call cancel_don() if we got - * here via _off() -> setworn((struct obj *)0) -> cancel_doff() + * here via _off() -> setworn((struct obj *) 0) -> cancel_doff() * because that would stop the 'A' command from continuing with next * selected item. So do_takeoff() sets a flag in takeoff.mask for us. * [For taking off an individual item with 'T'/'R'/'w-', it doesn't * matter whether cancel_don() gets called here--the item has already * been removed by now.] */ - if (!(gc.context.takeoff.mask & I_SPECIAL) && donning(obj)) + if (!(svc.context.takeoff.mask & I_SPECIAL) && donning(obj)) cancel_don(); /* applies to doffing too */ - gc.context.takeoff.mask &= ~slotmask; + svc.context.takeoff.mask &= ~slotmask; } /* despite their names, cancel_don() and cancel_doff() both apply to both @@ -1627,7 +1760,7 @@ cancel_don(void) * every item of the corresponding armor category takes 1 turn to wear, * but check all of them anyway */ - gc.context.takeoff.cancelled_don = (ga.afternmv == Cloak_on + svc.context.takeoff.cancelled_don = (ga.afternmv == Cloak_on || ga.afternmv == Armor_on || ga.afternmv == Shirt_on || ga.afternmv == Helmet_on @@ -1637,14 +1770,14 @@ cancel_don(void) ga.afternmv = (int (*)(void)) 0; gn.nomovemsg = (char *) 0; gm.multi = 0; - gc.context.takeoff.delay = 0; - gc.context.takeoff.what = 0L; + svc.context.takeoff.delay = 0; + svc.context.takeoff.what = 0L; } /* 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; @@ -1687,9 +1820,10 @@ stop_donning(struct obj *stolenobj) /* no message if stolenobj is already static NEARDATA int Narmorpieces, Naccessories; /* assign values to Narmorpieces and Naccessories */ -static void -count_worn_stuff(struct obj **which, /* caller wants this when count is 1 */ - boolean accessorizing) +staticfn void +count_worn_stuff( + struct obj **which, /* caller wants this when count is 1 */ + boolean accessorizing) { struct obj *otmp; @@ -1724,7 +1858,7 @@ count_worn_stuff(struct obj **which, /* caller wants this when count is 1 */ /* take off one piece or armor or one accessory; shared by dotakeoff('T') and doremring('R') */ -static int +staticfn int armor_or_accessory_off(struct obj *obj) { if (!(obj->owornmask & (W_ARMOR | W_ACCESSORY))) { @@ -1756,7 +1890,7 @@ armor_or_accessory_off(struct obj *obj) reset_remarm(); /* clear context.takeoff.mask and context.takeoff.what */ (void) select_off(obj); - if (!gc.context.takeoff.mask) + if (!svc.context.takeoff.mask) return ECMD_OK; /* none of armoroff()/Ring_/Amulet/Blindf_off() use context.takeoff.mask */ reset_remarm(); @@ -1773,12 +1907,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); } @@ -1904,7 +2038,8 @@ armoroff(struct obj *otmp) } if (what) { /* sizeof offdelaybuf == 60; increase it if this becomes longer */ - Sprintf(offdelaybuf, "You finish taking off your %s.", what); + Snprintf(offdelaybuf, sizeof offdelaybuf, + "You finish taking off your %s.", what); gn.nomovemsg = offdelaybuf; } } else { @@ -1940,17 +2075,17 @@ armoroff(struct obj *otmp) avoid "You were wearing ____ (being worn)." */ off_msg(otmp); } - gc.context.takeoff.mask = gc.context.takeoff.what = 0L; + svc.context.takeoff.mask = svc.context.takeoff.what = 0L; return 1; } -static void +staticfn void already_wearing(const char *cc) { You("are already wearing %s%c", cc, (cc == c_that_) ? '!' : '.'); } -static void +staticfn void already_wearing2(const char *cc1, const char *cc2) { You_cant("wear %s because you're wearing %s there already.", cc1, cc2); @@ -2046,12 +2181,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 @@ -2153,11 +2288,11 @@ will_touch_skin(long mask) return TRUE; } -static int +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_); @@ -2165,6 +2300,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 */ @@ -2180,7 +2316,7 @@ accessory_or_armor_on(struct obj *obj) You("are suddenly overcome with shame and change your mind."); u.ublessed = 0; /* lose your god's protection */ makeknown(obj->otyp); - gc.context.botl = 1; /* for AC after zeroing u.ublessed */ + disp.botl = TRUE; /* for AC after zeroing u.ublessed */ return ECMD_TIME; } } else { @@ -2217,7 +2353,7 @@ accessory_or_armor_on(struct obj *obj) Sprintf(qbuf, "Which %s%s, Right or Left?", humanoid(gy.youmonst.data) ? "ring-" : "", body_part(FINGER)); - answer = yn_function(qbuf, "rl", '\0', TRUE); + answer = yn_function(qbuf, rightleftchars, '\0', TRUE); switch (answer) { case '\0': case '\033': @@ -2248,7 +2384,9 @@ accessory_or_armor_on(struct obj *obj) } if (uwep) { res = !uwep->bknown; /* check this before calling welded() */ - if ((mask == RIGHT_RING || bimanual(uwep)) && welded(uwep)) { + if (((mask == RIGHT_RING && URIGHTY) + || (mask == LEFT_RING && ULEFTY) + || bimanual(uwep)) && welded(uwep)) { const char *hand = body_part(HAND); /* welded will set bknown */ @@ -2260,7 +2398,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; @@ -2319,6 +2457,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) @@ -2347,28 +2486,28 @@ accessory_or_armor_on(struct obj *obj) unmul(""); /* call afternmv, clear it+nomovemsg+multi_reason */ on_msg(obj); } - gc.context.takeoff.mask = gc.context.takeoff.what = 0L; + 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; } @@ -2415,7 +2554,7 @@ doputon(void) } /* Convert a wornmask to the body part that armor is worn on. */ -static int +staticfn int wornmask_to_bodypart(int wornmask) { if (wornmask & (W_ARM | W_ARMC | W_ARMU)) { @@ -2494,7 +2633,7 @@ find_ac(void) if (uac != u.uac) { u.uac = uac; - gc.context.botl = 1; + disp.botl = TRUE; #if 0 /* these could conceivably be achieved out of order (by being near threshold and putting on +N dragon scale mail from bones, for @@ -2514,14 +2653,23 @@ find_ac(void) void glibr(void) { - register struct obj *otmp; + struct obj *otmp; int xfl = 0; boolean leftfall, rightfall, wastwoweap = FALSE; const char *otherwep = 0, *thiswep, *which, *hand; + leftfall = (uleft && !uleft->cursed + && (!uwep || !(welded(uwep) && ULEFTY) + || !bimanual(uwep))); + rightfall = (uright && !uright->cursed + && (!uwep || !(welded(uwep) && URIGHTY) + || !bimanual(uwep))); +/* leftfall = (uleft && !uleft->cursed && (!uwep || !welded(uwep) || !bimanual(uwep))); rightfall = (uright && !uright->cursed && (!welded(uwep))); +*/ + if (!uarmg && (leftfall || rightfall) && !nolimbs(gy.youmonst.data)) { /* changed so cursed rings don't fall off, GAN 10/30/86 */ Your("%s off your %s.", @@ -2554,7 +2702,7 @@ glibr(void) if (otmp->quan > 1L) otherwep = makeplural(otherwep); hand = body_part(HAND); - which = "left "; + which = URIGHTY ? "left " : "right "; /* text for the off hand */ Your("%s %s%s from your %s%s.", otherwep, xfl ? "also " : "", otense(otmp, "slip"), which, hand); xfl++; @@ -2585,10 +2733,12 @@ glibr(void) } hand = body_part(HAND); which = ""; - if (bimanual(otmp)) + if (bimanual(otmp)) { hand = makeplural(hand); - else if (wastwoweap) - which = "right "; /* preceding msg was about left */ + } else if (wastwoweap) { + /* preceding msg was about non-dominant hand */ + which = URIGHTY ? "right " : "left "; + } pline("%s %s%s %s%s from your %s%s.", !strncmp(thiswep, "corpse", 6) ? "The" : "Your", otherwep ? "other " : "", thiswep, xfl ? "also " : "", @@ -2605,7 +2755,7 @@ glibr(void) struct obj * some_armor(struct monst *victim) { - register struct obj *otmph, *otmp; + struct obj *otmph, *otmp; otmph = (victim == &gy.youmonst) ? uarmc : which_armor(victim, W_ARMC); if (!otmph) @@ -2643,7 +2793,7 @@ stuck_ring(struct obj *ring, int otyp) if (nolimbs(gy.youmonst.data) && uamul && uamul->otyp == AMULET_OF_UNCHANGING && uamul->cursed) return uamul; - if (welded(uwep) && (ring == uright || bimanual(uwep))) + if (welded(uwep) && ((ring == RING_ON_PRIMARY) || bimanual(uwep))) return uwep; if (uarmg && uarmg->cursed) return uarmg; @@ -2667,9 +2817,9 @@ unchanger(void) return 0; } -static +staticfn int -select_off(register struct obj *otmp) +select_off(struct obj *otmp) { struct obj *why; char buf[BUFSZ]; @@ -2688,7 +2838,7 @@ select_off(register struct obj *otmp) } glibdummy = cg.zeroobj; why = 0; /* the item which prevents ring removal */ - if (welded(uwep) && (otmp == uright || bimanual(uwep))) { + if (welded(uwep) && ((otmp == RING_ON_PRIMARY) || bimanual(uwep))) { Sprintf(buf, "free a weapon %s", body_part(HAND)); why = uwep; } else if (uarmg && (uarmg->cursed || Glib)) { @@ -2715,6 +2865,8 @@ select_off(register struct obj *otmp) gloves_simple_name(uarmg)); return 0; } + if (better_not_take_that_off(otmp)) + return 0; } /* special boot checks */ if (otmp == uarmf) { @@ -2760,33 +2912,33 @@ select_off(register struct obj *otmp) } if (otmp == uarm) - gc.context.takeoff.mask |= WORN_ARMOR; + svc.context.takeoff.mask |= WORN_ARMOR; else if (otmp == uarmc) - gc.context.takeoff.mask |= WORN_CLOAK; + svc.context.takeoff.mask |= WORN_CLOAK; else if (otmp == uarmf) - gc.context.takeoff.mask |= WORN_BOOTS; + svc.context.takeoff.mask |= WORN_BOOTS; else if (otmp == uarmg) - gc.context.takeoff.mask |= WORN_GLOVES; + svc.context.takeoff.mask |= WORN_GLOVES; else if (otmp == uarmh) - gc.context.takeoff.mask |= WORN_HELMET; + svc.context.takeoff.mask |= WORN_HELMET; else if (otmp == uarms) - gc.context.takeoff.mask |= WORN_SHIELD; + svc.context.takeoff.mask |= WORN_SHIELD; else if (otmp == uarmu) - gc.context.takeoff.mask |= WORN_SHIRT; + svc.context.takeoff.mask |= WORN_SHIRT; else if (otmp == uleft) - gc.context.takeoff.mask |= LEFT_RING; + svc.context.takeoff.mask |= LEFT_RING; else if (otmp == uright) - gc.context.takeoff.mask |= RIGHT_RING; + svc.context.takeoff.mask |= RIGHT_RING; else if (otmp == uamul) - gc.context.takeoff.mask |= WORN_AMUL; + svc.context.takeoff.mask |= WORN_AMUL; else if (otmp == ublindf) - gc.context.takeoff.mask |= WORN_BLINDF; + svc.context.takeoff.mask |= WORN_BLINDF; else if (otmp == uwep) - gc.context.takeoff.mask |= W_WEP; + svc.context.takeoff.mask |= W_WEP; else if (otmp == uswapwep) - gc.context.takeoff.mask |= W_SWAPWEP; + svc.context.takeoff.mask |= W_SWAPWEP; else if (otmp == uquiver) - gc.context.takeoff.mask |= W_QUIVER; + svc.context.takeoff.mask |= W_QUIVER; else impossible("select_off: %s???", doname(otmp)); @@ -2794,14 +2946,14 @@ select_off(register struct obj *otmp) return 0; } -static struct obj * +staticfn struct obj * do_takeoff(void) { struct obj *otmp = (struct obj *) 0; boolean was_twoweap = u.twoweap; - struct takeoff_info *doff = &gc.context.takeoff; + struct takeoff_info *doff = &svc.context.takeoff; - gc.context.takeoff.mask |= I_SPECIAL; /* set flag for cancel_doff() */ + svc.context.takeoff.mask |= I_SPECIAL; /* set flag for cancel_doff() */ if (doff->what == W_WEP) { if (!cursed(uwep)) { setuwep((struct obj *) 0); @@ -2864,18 +3016,18 @@ do_takeoff(void) } else { impossible("do_takeoff: taking off %lx", doff->what); } - gc.context.takeoff.mask &= ~I_SPECIAL; /* clear cancel_doff() flag */ + svc.context.takeoff.mask &= ~I_SPECIAL; /* clear cancel_doff() flag */ return otmp; } /* occupation callback for 'A' */ -static int +staticfn int take_off(void) { - register int i; - register struct obj *otmp; - struct takeoff_info *doff = &gc.context.takeoff; + int i; + struct obj *otmp; + struct takeoff_info *doff = &svc.context.takeoff; if (doff->what) { if (doff->delay > 0) { @@ -2960,12 +3112,35 @@ 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) { - gc.context.takeoff.what = gc.context.takeoff.mask = 0L; - gc.context.takeoff.disrobing[0] = '\0'; + svc.context.takeoff.what = svc.context.takeoff.mask = 0L; + svc.context.takeoff.disrobing[0] = '\0'; } /* the #takeoffall command -- remove multiple worn items */ @@ -2974,9 +3149,9 @@ doddoremarm(void) { int result = 0; - if (gc.context.takeoff.what || gc.context.takeoff.mask) { - You("continue %s.", gc.context.takeoff.disrobing); - set_occupation(take_off, gc.context.takeoff.disrobing, 0); + if (svc.context.takeoff.what || svc.context.takeoff.mask) { + You("continue %s.", svc.context.takeoff.disrobing); + set_occupation(take_off, svc.context.takeoff.disrobing, 0); return ECMD_OK; } else if (!uwep && !uswapwep && !uquiver && !uamul && !ublindf && !uleft && !uright && !wearing_armor()) { @@ -2988,11 +3163,11 @@ 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 (gc.context.takeoff.mask) { - (void) strncpy(gc.context.takeoff.disrobing, - (((gc.context.takeoff.mask & ~W_WEAPONS) != 0) + if (svc.context.takeoff.mask) { + (void) strncpy(svc.context.takeoff.disrobing, + (((svc.context.takeoff.mask & ~W_WEAPONS) != 0) /* default activity for armor and/or accessories, possibly combined with weapons */ ? "disrobing" @@ -3032,12 +3207,12 @@ remarm_swapwep(void) * can't be unwielded even though things * don't work that way... */ reset_remarm(); - gc.context.takeoff.what = gc.context.takeoff.mask = W_SWAPWEP; + svc.context.takeoff.what = svc.context.takeoff.mask = W_SWAPWEP; (void) do_takeoff(); return (!uswapwep || uswapwep->bknown != oldbknown) ? ECMD_TIME : ECMD_OK; } -static int +staticfn int menu_remarm(int retry) { int n, i = 0; @@ -3091,10 +3266,10 @@ menu_remarm(int retry) /* take off the specific worn object and if it still exists after that, destroy it (taking off the item might already destroy it by dunking hero into lava) */ -static void +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 @@ -3123,46 +3298,53 @@ 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; } + } +} + +/* + * returns impacted armor with its in_use bit set, + * or Null. *resisted is updated to reflect whether + * it resisted or not */ +staticfn struct obj * +maybe_destroy_armor(struct obj *armor, struct obj *atmp, boolean *resisted) +{ + if ((armor != 0) && (!atmp || atmp == armor) + && ((*resisted = obj_resists(armor, 0, 90)) == FALSE)) { + armor->in_use = 1; + return armor; + } + return (struct obj *) 0; } /* hit by destroy armor scroll/black dragon breath/monster spell - * if choose is TRUE, let the player choose what to destroy. * Return 1 if something was destroyed, 0 if not. */ int -destroy_arm(register struct obj *atmp, boolean choose) +destroy_arm(struct obj *atmp) { - struct obj *otmp; - struct obj *ochoice; - boolean losing_gloves = FALSE; - if (choose) { - ochoice = getobj("destroy", takeoff_ok, GETOBJ_PROMPT); - /* prevent player from selecting non-worn armor */ - if (ochoice && (ochoice->owornmask & W_ARMOR)) { - atmp = ochoice; - } - } + struct obj *otmp = (struct obj *) 0; + boolean losing_gloves = FALSE, resisted = FALSE, + resistedc = FALSE, resistedsuit = FALSE; /* - * Note: if there were any artifact cloaks, the 90% chance of - * resistance here means that the cloak could survive and then - * the suit or shirt underneath could be destroyed. Likewise for - * artifact suit over a shirt. That would be a bug. Since there - * aren't any, we'll just look the other way. + * Note: if the cloak resisted, then the suit or shirt underneath + * wouldn't be impacted either. Likewise, if the suit resisted, the + * shirt underneath wouldn't be impacted. Since there are no artifact + * cloaks or suits right now, this is unlikely to come into effect, + * but it should behave appropriately if/when the situation changes. */ -#define DESTROY_ARM(o) \ - (((otmp = (o)) != 0 && (!atmp || atmp == otmp) \ - && !obj_resists(otmp, 0, 90)) ? (otmp->in_use = 1) : 0) - if (DESTROY_ARM(uarmc)) { + if ((otmp = maybe_destroy_armor(uarmc, atmp, &resistedc)) != 0) { urgent_pline("Your %s crumbles and turns to dust!", /* cloak/robe/apron/smock (ID'd apron)/wrapping */ cloak_simple_name(otmp)); - } else if (DESTROY_ARM(uarm)) { + } else if (!resistedc + && (otmp = maybe_destroy_armor(uarm, atmp, &resistedsuit)) != 0) { const char *suit = suit_simple_name(otmp); /* for gold DSM, we don't want Armor_gone() to report that it @@ -3173,25 +3355,24 @@ destroy_arm(register struct obj *atmp, boolean choose) /* suit might be "dragon scales" so vtense() is needed */ suit, vtense(suit, "turn"), vtense(suit, "fall"), surface(u.ux, u.uy)); - } else if (DESTROY_ARM(uarmu)) { + } else if (!resistedc && !resistedsuit + && (otmp = maybe_destroy_armor(uarmu, atmp, &resisted)) != 0) { urgent_pline("Your %s crumbles into tiny threads and falls apart!", shirt_simple_name(otmp)); /* always "shirt" */ - } else if (DESTROY_ARM(uarmh)) { + } else if ((otmp = maybe_destroy_armor(uarmh, atmp, &resisted)) != 0) { urgent_pline("Your %s turns to dust and is blown away!", helm_simple_name(otmp)); /* "helm" or "hat" */ - } else if (DESTROY_ARM(uarmg)) { + } else if ((otmp = maybe_destroy_armor(uarmg, atmp, &resisted)) != 0) { urgent_pline("Your %s vanish!", gloves_simple_name(otmp)); losing_gloves = TRUE; - } else if (DESTROY_ARM(uarmf)) { + } else if ((otmp = maybe_destroy_armor(uarmf, atmp, &resisted)) != 0) { urgent_pline("Your %s disintegrate!", boots_simple_name(otmp)); - } else if (DESTROY_ARM(uarms)) { + } else if ((otmp = maybe_destroy_armor(uarms, atmp, &resisted)) != 0) { urgent_pline("Your %s crumbles away!", shield_simple_name(otmp)); } else { return 0; /* could not destroy anything */ } -#undef DESTROY_ARM - /* cancel_don() if applicable, Cloak_off()/Armor_off()/&c, and useup() */ wornarm_destroyed(otmp); /* glove loss means wielded weapon will be touched */ @@ -3203,14 +3384,14 @@ destroy_arm(register struct obj *atmp, boolean choose) } void -adj_abon(register struct obj *otmp, register schar delta) +adj_abon(struct obj *otmp, schar delta) { if (uarmg && uarmg == otmp && otmp->otyp == GAUNTLETS_OF_DEXTERITY) { if (delta) { makeknown(uarmg->otyp); ABON(A_DEX) += (delta); } - gc.context.botl = 1; + disp.botl = TRUE; } if (uarmh && uarmh == otmp && otmp->otyp == HELM_OF_BRILLIANCE) { if (delta) { @@ -3218,7 +3399,7 @@ adj_abon(register struct obj *otmp, register schar delta) ABON(A_INT) += (delta); ABON(A_WIS) += (delta); } - gc.context.botl = 1; + disp.botl = TRUE; } } @@ -3305,7 +3486,7 @@ ringbon(short ring_typ) } /* not a getobj callback - unifies code among the other 4 getobj callbacks */ -static int +staticfn int equip_ok(struct obj *obj, boolean removing, boolean accessory) { boolean is_worn; @@ -3350,37 +3531,64 @@ equip_ok(struct obj *obj, boolean removing, boolean accessory) } /* getobj callback for P command */ -static int +staticfn int puton_ok(struct obj *obj) { return equip_ok(obj, FALSE, TRUE); } /* getobj callback for R command */ -static int +staticfn int remove_ok(struct obj *obj) { return equip_ok(obj, TRUE, TRUE); } /* getobj callback for W command */ -static int +staticfn int wear_ok(struct obj *obj) { return equip_ok(obj, FALSE, FALSE); } /* getobj callback for T command */ -static int +staticfn int 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; +} + /* hero is putting on or taking off obj, which may do something light-related * unifies code for cloak and body armor code paths since gold dragon scales are * worn in cloak slot and gold-scaled armor is worn in armor slot*/ -static void +staticfn void toggle_armor_light(struct obj *armor, boolean on) { if (on) { diff --git a/src/dog.c b/src/dog.c index d8f28116a9..94fedab724 100644 --- a/src/dog.c +++ b/src/dog.c @@ -1,14 +1,15 @@ -/* NetHack 3.7 dog.c $NHDT-Date: 1652689621 2022/05/16 08:27:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.121 $ */ +/* NetHack 3.7 dog.c $NHDT-Date: 1737287993 2025/01/19 03:59:53 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.178 $ */ /* 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" -static int pet_type(void); -static void set_mon_lastmove(struct monst *); -static int mon_leave(struct monst *); -static boolean keep_mon_accessible(struct monst *); +staticfn int pet_type(void); +staticfn struct permonst * pick_familiar_pm(struct obj *, boolean); +staticfn void set_mon_lastmove(struct monst *); +staticfn int mon_leave(struct monst *) NONNULLARG1; +staticfn boolean keep_mon_accessible(struct monst *); enum arrival { Before_you = 0, /* monsters kept on migrating_mons for accessibility; @@ -52,7 +53,7 @@ initedog(struct monst *mtmp) EDOG(mtmp)->dropdist = 10000; EDOG(mtmp)->apport = ACURR(A_CHA); EDOG(mtmp)->whistletime = 0; - EDOG(mtmp)->hungrytime = 1000 + gm.moves; + 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; @@ -62,7 +63,7 @@ initedog(struct monst *mtmp) u.uconduct.pets++; } -static int +staticfn int pet_type(void) { if (gu.urole.petnum != NON_PM) @@ -75,6 +76,37 @@ pet_type(void) return rn2(2) ? PM_KITTEN : PM_LITTLE_DOG; } +staticfn struct permonst * +pick_familiar_pm(struct obj *otmp, boolean quietly) +{ + struct permonst *pm = (struct permonst *) 0; + + if (otmp) { /* figurine; otherwise spell */ + int mndx = otmp->corpsenm; + + assert(ismnum(mndx)); + pm = &mons[mndx]; + /* activating a figurine provides one way to exceed the + maximum number of the target critter created--unless + it has a special limit (erinys, Nazgul) */ + if ((svm.mvitals[mndx].mvflags & G_EXTINCT) + && mbirth_limit(mndx) != MAXMONNO) { + if (!quietly) + /* have just been given "You + the figurine and it transforms." message */ + pline("... into a pile of dust."); + return (struct permonst *) 0; + } + } else if (!rn2(3)) { + pm = &mons[pet_type()]; + } else { + pm = rndmonst(); + if (!pm && !quietly) + There("seems to be nothing available for a familiar."); + } + return pm; +} + struct monst * make_familiar(struct obj *otmp, coordxy x, coordxy y, boolean quietly) { @@ -84,32 +116,10 @@ make_familiar(struct obj *otmp, coordxy x, coordxy y, boolean quietly) do { mmflags_nht mmflags; - int cgend, mndx; - - if (otmp) { /* figurine; otherwise spell */ - mndx = otmp->corpsenm; - pm = &mons[mndx]; - /* activating a figurine provides one way to exceed the - maximum number of the target critter created--unless - it has a special limit (erinys, Nazgul) */ - if ((gm.mvitals[mndx].mvflags & G_EXTINCT) - && mbirth_limit(mndx) != MAXMONNO) { - if (!quietly) - /* have just been given "You - the figurine and it transforms." message */ - pline("... into a pile of dust."); - break; /* mtmp is null */ - } - } else if (!rn2(3)) { - pm = &mons[pet_type()]; - } else { - pm = rndmonst(); - if (!pm) { - if (!quietly) - There("seems to be nothing available for a familiar."); - break; - } - } + int cgend; + + if (!(pm = pick_familiar_pm(otmp, quietly))) + break; mmflags = MM_EDOG | MM_IGNOREWATER | NO_MINVENT | MM_NOMSG; cgend = otmp ? (otmp->spe & CORPSTAT_GENDER) : 0; @@ -145,7 +155,6 @@ make_familiar(struct obj *otmp, coordxy x, coordxy y, boolean quietly) return (struct monst *) 0; initedog(mtmp); - u.uconduct.pets++; mtmp->msleeping = 0; if (otmp) { /* figurine; resulting monster might not become a pet */ chance = rn2(10); /* 0==tame, 1==peaceful, 2==hostile */ @@ -177,26 +186,23 @@ make_familiar(struct obj *otmp, coordxy x, coordxy y, boolean quietly) return mtmp; } +/* used exclusively for hero's starting pet */ struct monst * makedog(void) { - register struct monst *mtmp; - register struct obj *otmp; + struct monst *mtmp; + struct obj *otmp; const char *petname; int pettype; if (gp.preferred_pet == 'n') return ((struct monst *) 0); - u.uconduct.pets++; - pettype = pet_type(); - if (pettype == PM_LITTLE_DOG) - petname = gd.dogname; - else if (pettype == PM_PONY) - petname = gh.horsename; - else - petname = gc.catname; + petname = (pettype == PM_LITTLE_DOG) ? gd.dogname + : (pettype == PM_KITTEN) ? gc.catname + : (pettype == PM_PONY) ? gh.horsename + : ""; /* default pet names */ if (!*petname && pettype == PM_LITTLE_DOG) { @@ -214,13 +220,22 @@ makedog(void) mtmp = makemon(&mons[pettype], u.ux, u.uy, MM_EDOG); if (!mtmp) - return ((struct monst *) 0); /* pets were genocided */ - - gc.context.startingpet_mid = mtmp->m_id; - /* Horses already wear a saddle */ - if (pettype == PM_PONY && !!(otmp = mksobj(SADDLE, TRUE, FALSE))) { - otmp->dknown = otmp->bknown = otmp->rknown = 1; - put_saddle_on_mon(otmp, mtmp); + return ((struct monst *) 0); /* pets were genocided [how?] */ + + 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); + } + } + } else { + impossible("makedog() when startingpet_mid is already non-zero?"); } if (!gp.petname_used++ && *petname) @@ -230,10 +245,10 @@ makedog(void) return mtmp; } -static void +staticfn void set_mon_lastmove(struct monst *mtmp) { - mtmp->mlstmv = gm.moves; + mtmp->mlstmv = svm.moves; } /* record `last move time' for all monsters prior to level save so that @@ -250,7 +265,7 @@ static struct monst *failed_arrivals = 0; void losedogs(void) { - register struct monst *mtmp, **mprev; + struct monst *mtmp, **mprev; int dismissKops = 0, xyloc; #ifdef FUZZER_LOG @@ -408,6 +423,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 */ @@ -433,9 +454,9 @@ mon_arrive(struct monst *mtmp, int when) * specify its final destination. */ - if (mtmp->mlstmv < gm.moves - 1L) { + if (mtmp->mlstmv < svm.moves - 1L) { /* heal monster for time spent in limbo */ - long nmv = gm.moves - 1L - mtmp->mlstmv; + long nmv = svm.moves - 1L - mtmp->mlstmv; mon_catchup_elapsed_time(mtmp, nmv); @@ -490,8 +511,8 @@ mon_arrive(struct monst *mtmp, int when) that we know that the current endgame levels always build upwards and never have any exclusion subregion inside their TELEPORT_REGION settings. */ - xlocale = rn1(gu.updest.hx - gu.updest.lx + 1, gu.updest.lx); - ylocale = rn1(gu.updest.hy - gu.updest.ly + 1, gu.updest.ly); + xlocale = rn1(svu.updest.hx - svu.updest.lx + 1, svu.updest.lx); + ylocale = rn1(svu.updest.hy - svu.updest.ly + 1, svu.updest.ly); break; } /* find the arrival portal */ @@ -505,10 +526,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; @@ -530,7 +554,7 @@ mon_arrive(struct monst *mtmp, int when) coord c; /* somexy() handles irregular rooms */ - if (somexy(&gr.rooms[*r - ROOMOFFSET], &c)) + if (somexy(&svr.rooms[*r - ROOMOFFSET], &c)) xlocale = c.x, ylocale = c.y; else xlocale = ylocale = 0; @@ -623,10 +647,12 @@ mon_catchup_elapsed_time( mtmp->mstun = 0; /* might finish eating or be able to use special ability again */ - if (imv > mtmp->meating) - finish_meating(mtmp); - else - mtmp->meating -= imv; + if (mtmp->meating) { + if (imv > mtmp->meating) + finish_meating(mtmp); + else + mtmp->meating -= imv; + } if (imv > mtmp->mspec_used) mtmp->mspec_used = 0; else @@ -649,8 +675,8 @@ mon_catchup_elapsed_time( && (carnivorous(mtmp->data) || herbivorous(mtmp->data))) { struct edog *edog = EDOG(mtmp); - if ((gm.moves > edog->hungrytime + 500 && mtmp->mhp < 3) - || (gm.moves > edog->hungrytime + 750)) + if ((svm.moves > edog->hungrytime + 500 && mtmp->mhp < 3) + || (svm.moves > edog->hungrytime + 750)) mtmp->mtame = mtmp->mpeaceful = 0; } @@ -665,17 +691,14 @@ mon_catchup_elapsed_time( if (!mtmp->mwither) { 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); } /* bookkeeping when mtmp is about to leave the current level; common to keepdogs() and migrate_to_level() */ -static int +staticfn int mon_leave(struct monst *mtmp) { struct obj *obj; @@ -714,7 +737,7 @@ mon_leave(struct monst *mtmp) /* when hero leaves a level, some monsters should be placed on the migrating_mons list instead of being stashed inside the level's file */ -static boolean +staticfn boolean keep_mon_accessible(struct monst *mon) { /* the Wizard is kept accessible so that his harassment can fetch @@ -739,7 +762,7 @@ void keepdogs(boolean pets_only, /* true for ascension or final escape */ boolean stairs) { - register struct monst *mtmp, *mtmp2; + struct monst *mtmp, *mtmp2; for (mtmp = fmon; mtmp; mtmp = mtmp2) { mtmp2 = mtmp->nmon; @@ -753,7 +776,7 @@ keepdogs(boolean pets_only, /* true for ascension or final escape */ unlike level change for steed, don't bother trying to achieve a normal trap escape first */ mtmp->mtrapped = 0; - mtmp->meating = 0; + finish_meating(mtmp); mtmp->msleeping = 0; mtmp->mfrozen = 0; mtmp->mcanmove = 1; @@ -781,8 +804,8 @@ keepdogs(boolean pets_only, /* true for ascension or final escape */ 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)) @@ -826,7 +849,7 @@ keepdogs(boolean pets_only, /* true for ascension or final escape */ relmon(mtmp, &gm.mydogs); /* mtmp->mx,my retain current value */ mtmp->mx = mtmp->my = 0; /* mx==0 implies migrating */ mtmp->wormno = num_segs; - mtmp->mlstmv = gm.moves; + mtmp->mlstmv = svm.moves; } else if (keep_mon_accessible(mtmp)) { /* we want to be able to find the Wizard when his next resurrection chance comes up, but have him resume his @@ -878,7 +901,7 @@ migrate_to_level( destination codes */ xyflags = (depth(&new_lev) < depth(&u.uz)); /* 1 => up */ mtmp->wormno = num_segs; - mtmp->mlstmv = gm.moves; + mtmp->mlstmv = svm.moves; mtmp->mtrack[2].x = u.uz.dnum; /* migrating from this dungeon */ mtmp->mtrack[2].y = u.uz.dlevel; /* migrating from this dungeon level */ mtmp->mtrack[1].x = cc ? cc->x : mx; @@ -918,6 +941,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); } } @@ -940,6 +965,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 */ } } @@ -955,20 +986,26 @@ 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; switch (obj->oclass) { case FOOD_CLASS: fx = (obj->otyp == CORPSE || obj->otyp == TIN || obj->otyp == EGG) + /* corpsenm might be NON_PM (special tin, unhatchable egg) */ ? obj->corpsenm - : NUMMONS; /* valid mons[mndx] to pacify static analyzer */ - fptr = &mons[fx]; + : NON_PM; + /* mons[NUMMONS] is a valid array entry, though not a valid monster; + * predicate tests against it will fail */ + fptr = &mons[(ismnum(fx)) ? fx : NUMMONS]; if (obj->otyp == CORPSE && is_rider(fptr)) return TABU; if ((obj->otyp == CORPSE || obj->otyp == EGG) - && touch_petrifies(fptr) && !resists_ston(mon)) + && flesh_petrifies(fptr) /* c*ckatrice or Medusa */ + && !resists_ston(mon)) return POISON; if (obj->otyp == LUMP_OF_ROYAL_JELLY && mon->data == &mons[PM_KILLER_BEE]) { @@ -992,7 +1029,7 @@ dogfood(struct monst *mon, struct obj *obj) when starving; they never eat stone-to-flesh'd meat */ if (mptr == &mons[PM_GHOUL]) { if (obj->otyp == CORPSE) - return (peek_at_iced_corpse_age(obj) + 50L <= gm.moves + return (peek_at_iced_corpse_age(obj) + 50L <= svm.moves && !(fx == PM_LIZARD || fx == PM_LICHEN)) ? DOGFOOD : (starving && !vegan(fptr)) ? ACCFOOD : POISON; @@ -1009,9 +1046,11 @@ dogfood(struct monst *mon, struct obj *obj) case ENORMOUS_MEATBALL: return carni ? DOGFOOD : MANFOOD; case EGG: + if (obj->corpsenm == PM_PYROLISK && !likes_fire(mptr)) + return POISON; return carni ? CADAVER : MANFOOD; case CORPSE: - if ((peek_at_iced_corpse_age(obj) + 50L <= gm.moves + if ((peek_at_iced_corpse_age(obj) + 50L <= svm.moves && !(fx == PM_LIZARD || fx == PM_LICHEN) && mptr->mlet != S_FUNGUS) || (acidic(fptr) && !resists_acid(mon)) @@ -1019,7 +1058,7 @@ dogfood(struct monst *mon, struct obj *obj) return POISON; /* avoid polymorph unless starving or abused (in which case the pet will consider it for a chance to become more powerful) */ - else if (is_shapeshifter(fptr) && mon->mtame > 1 && !starving) + else if (polyfood(obj) && mon->mtame > 1 && !starving) return MANFOOD; else if (vegan(fptr)) return herbi ? (mon_wants_prop(fptr, mon) ? DOGFOOD : CADAVER) @@ -1083,24 +1122,52 @@ dogfood(struct monst *mon, struct obj *obj) && obj->oclass != BALL_CLASS && obj->oclass != CHAIN_CLASS) return APPORT; + FALLTHROUGH; + /*FALLTHRU*/ + case ROCK_CLASS: return UNDEF; } } /* - * With the separate mextra structure added in 3.6.x this always - * operates on the original mtmp. It now returns TRUE if the taming - * succeeded. + * tamedog() used to return the monster, which might have changed address + * if a new one was created in order to allocate the edog extension. + * With the separate mextra structure added in 3.6.x it always operates + * on the original mtmp. It now returns TRUE if the taming succeeded. */ boolean -tamedog(struct monst *mtmp, struct obj *obj, boolean pacify_only) +tamedog( + struct monst *mtmp, + struct obj *obj, /* food or scroll/spell */ + boolean pacify_only, + 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) + mtmp->mfrozen = (mtmp->mfrozen + 1) / 2; + /* end indefinite sleep; using distance==1 limits the waking to mtmp */ + if (mtmp->msleeping) + wake_nearto(mtmp->mx, mtmp->my, 1); /* [different from wakeup()] */ + /* The Wiz, Medusa and the quest nemeses aren't even made peaceful. */ if (mtmp->iswiz || mtmp->data == &mons[PM_MEDUSA] || (mtmp->data->mflags3 & M3_WANTSARTI)) return FALSE; /* worst case, at least it'll be peaceful. */ + if (givemsg && !mtmp->mpeaceful && canspotmon(mtmp)) { + pline_mon(mtmp, "%s seems %s.", Monnam(mtmp), + Hallucination ? "really chill" : "more amiable"); + givemsg = FALSE; /* don't give another message below */ + } mtmp->mpeaceful = 1; set_malign(mtmp); if (flags.moonphase == FULL_MOON && night() && rn2(6) && obj @@ -1126,14 +1193,15 @@ tamedog(struct monst *mtmp, struct obj *obj, boolean pacify_only) if (mtmp->mcanmove && !mtmp->mconf && !mtmp->meating && ((tasty = dogfood(mtmp, obj)) == DOGFOOD || (tasty <= ACCFOOD - && EDOG(mtmp)->hungrytime <= gm.moves))) { + && EDOG(mtmp)->hungrytime <= svm.moves))) { /* pet will "catch" and eat this thrown food */ if (canseemon(mtmp)) { boolean big_corpse = - (obj->otyp == CORPSE && obj->corpsenm >= LOW_PM + (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 */ @@ -1147,11 +1215,17 @@ tamedog(struct monst *mtmp, struct obj *obj, boolean pacify_only) 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 */ @@ -1170,9 +1244,9 @@ tamedog(struct monst *mtmp, struct obj *obj, boolean pacify_only) || (obj && dogfood(mtmp, obj) >= MANFOOD)) return FALSE; - if (mtmp->m_id == gq.quest_status.leader_m_id) + if (mtmp->m_id == svq.quest_status.leader_m_id) return FALSE; - if (mtmp->m_id == gq.quest_status.nemesis_m_id) + if (mtmp->m_id == svq.quest_status.nemesis_m_id) return FALSE; if (pacify_only) { @@ -1180,9 +1254,10 @@ tamedog(struct monst *mtmp, struct obj *obj, boolean pacify_only) } /* add the pet extension */ - newedog(mtmp); - initedog(mtmp); - u.uconduct.pets++; + if (!has_edog(mtmp)) { + newedog(mtmp); + initedog(mtmp); + } if (obj) { /* thrown food */ /* defer eating until the edog extension has been set up */ @@ -1193,7 +1268,13 @@ tamedog(struct monst *mtmp, struct obj *obj, boolean pacify_only) /* `obj' is now obsolete */ } + if (givemsg && canspotmon(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); @@ -1235,11 +1316,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 { @@ -1251,7 +1333,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; @@ -1266,8 +1348,8 @@ wary_dog(struct monst *mtmp, boolean was_dead) edog->killed_by_u = 0; edog->abuse = 0; edog->ogoal.x = edog->ogoal.y = -1; - if (was_dead || edog->hungrytime < gm.moves + 500L) - edog->hungrytime = gm.moves + 500L; + if (was_dead || edog->hungrytime < svm.moves + 500L) + edog->hungrytime = svm.moves + 500L; if (was_dead) { edog->droptime = 0L; edog->dropdist = 10000; @@ -1302,8 +1384,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 672c738e88..a44b25a132 100644 --- a/src/dogmove.c +++ b/src/dogmove.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 dogmove.c $NHDT-Date: 1646688063 2022/03/07 21:21:03 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.112 $ */ +/* NetHack 3.7 dogmove.c $NHDT-Date: 1725733007 2024/09/07 18:16:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.156 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -15,17 +15,16 @@ * away from getting hungry again. */ #define DOG_SATIATED 800 -static void dog_starve(struct monst *); -static boolean dog_hunger(struct monst *, struct edog *); -static int dog_invent(struct monst *, struct edog *, int); -static int dog_goal(struct monst *, struct edog *, int, int, int); -static struct monst *find_targ(struct monst *, int, int, int); -static int find_friends(struct monst *, struct monst *, int); -static struct monst *best_target(struct monst *); -static int pet_ranged_attk(struct monst *); -static long score_targ(struct monst *, struct monst *); -static boolean can_reach_location(struct monst *, coordxy, coordxy, coordxy, - coordxy); +staticfn void dog_starve(struct monst *); +staticfn boolean dog_hunger(struct monst *, struct edog *); +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 *, boolean); +staticfn long score_targ(struct monst *, struct monst *); +staticfn boolean can_reach_location(struct monst *, coordxy, coordxy, coordxy, + coordxy) NONNULLARG1; /* pick a carried item for pet to drop */ struct obj * @@ -81,6 +80,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)) { @@ -109,12 +109,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)) { @@ -140,14 +142,14 @@ static NEARDATA const char nofetch[] = { BALL_CLASS, CHAIN_CLASS, ROCK_CLASS, 0 }; -static void wantdoor(coordxy, coordxy, genericptr_t); +staticfn void wantdoor(coordxy, coordxy, genericptr_t); boolean cursed_object_at(coordxy x, coordxy y) { struct obj *otmp; - for (otmp = gl.level.objects[x][y]; otmp; otmp = otmp->nexthere) + for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp->nexthere) if (otmp->cursed) return TRUE; return FALSE; @@ -222,14 +224,14 @@ dog_eat(struct monst *mtmp, coordxy y, /* might be different from current */ boolean devour) { - register struct edog *edog = EDOG(mtmp); + struct edog *edog = EDOG(mtmp); int nutrit, res; long oprice; char objnambuf[BUFSZ], *obj_name; objnambuf[0] = '\0'; - if (edog->hungrytime < gm.moves) - edog->hungrytime = gm.moves; + if (edog->hungrytime < svm.moves) + edog->hungrytime = svm.moves; nutrit = dog_nutrition(mtmp, obj); if (devour) { @@ -285,9 +287,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); @@ -306,7 +308,7 @@ dog_eat(struct monst *mtmp, mtmp->mstun = 1; if (canseemon(mtmp)) { obj_name = distant_name(obj, doname); /* (see above) */ - if (Verbose(0, dog_eat)) + if (flags.verbose) pline("%s spits %s out in disgust!", Monnam(mtmp), obj_name); } @@ -314,15 +316,16 @@ dog_eat(struct monst *mtmp, /* It's a reward if it's DOGFOOD and the player dropped/threw it. We know the player had it if invlet is set. -dlc */ if (dogfood(mtmp, obj) == DOGFOOD && obj->invlet) - edog->apport += (int) (200L / ((long) edog->dropdist + gm.moves + edog->apport += (int) (200L / ((long) edog->dropdist + svm.moves - edog->droptime)); if (obj->unpaid) { /* edible item owned by shop has been thrown or kicked by hero and caught by tame or food-tameable monst */ - oprice = unpaid_cost(obj, TRUE); + oprice = unpaid_cost(obj, COST_CONTENTS); pline("That %s will cost you %ld %s.", objnambuf, oprice, currency(oprice)); - /* m_consume_obj->delobj->obfree will handle actual shop billing update */ + /* m_consume_obj() -> delobj() -> obfree() will handle the shop + billing update */ } m_consume_obj(mtmp, obj); } @@ -330,13 +333,13 @@ dog_eat(struct monst *mtmp, return (DEADMONSTER(mtmp)) ? 2 : 1; } -static void +staticfn void 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"); @@ -344,12 +347,12 @@ dog_starve(struct monst *mtmp) } /* hunger effects -- returns TRUE on starvation */ -static boolean +staticfn boolean dog_hunger(struct monst *mtmp, struct edog *edog) { - if (gm.moves > edog->hungrytime + DOG_WEAK) { + if (svm.moves > edog->hungrytime + DOG_WEAK) { if (!carnivorous(mtmp->data) && !herbivorous(mtmp->data)) { - edog->hungrytime = gm.moves + DOG_WEAK; + edog->hungrytime = svm.moves + DOG_WEAK; /* but not too high; it might polymorph */ } else if (!edog->mhpmax_penalty) { /* starving pets are limited in healing */ @@ -364,13 +367,13 @@ 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 You_feel("worried about %s.", y_monnam(mtmp)); stop_occupation(); - } else if (gm.moves > edog->hungrytime + DOG_STARVE + } else if (svm.moves > edog->hungrytime + DOG_STARVE || DEADMONSTER(mtmp)) { dog_starve(mtmp); return TRUE; @@ -382,7 +385,7 @@ dog_hunger(struct monst *mtmp, struct edog *edog) /* do something with object (drop, pick up, eat) at current position * returns 1 if object eaten (since that counts as dog's move), 2 if died */ -static int +staticfn int dog_invent(struct monst *mtmp, struct edog *edog, int udist) { coordxy omx, omy; @@ -406,10 +409,10 @@ dog_invent(struct monst *mtmp, struct edog *edog, int udist) if (edog->apport > 1) edog->apport--; edog->dropdist = udist; /* hpscdi!jon */ - edog->droptime = gm.moves; + edog->droptime = svm.moves; } } else { - if ((obj = gl.level.objects[omx][omy]) != 0 + if ((obj = svl.level.objects[omx][omy]) != 0 && !strchr(nofetch, obj->oclass) #ifdef MAIL_STRUCTURES && obj->otyp != SCR_MAIL @@ -423,7 +426,7 @@ dog_invent(struct monst *mtmp, struct edog *edog, int udist) /* starving pet is more aggressive about eating */ || (edog->mhpmax_penalty && edible == ACCFOOD)) && could_reach_item(mtmp, obj->ox, obj->oy) - && edog->hungrytime < gm.moves + DOG_SATIATED) + && edog->hungrytime < svm.moves + DOG_SATIATED) return dog_eat(mtmp, obj, omx, omy, FALSE); carryamt = can_carry(mtmp, obj); @@ -442,8 +445,8 @@ dog_invent(struct monst *mtmp, struct edog *edog, int udist) while otmp is still on floor */ char *otmpname = distant_name(otmp, doname); - if (Verbose(0, dog_invent)) - pline("%s picks up %s.", + if (flags.verbose) + pline_xy(omx, omy, "%s picks up %s.", Monnam(mtmp), otmpname); } obj_extract_self(otmp); @@ -465,15 +468,15 @@ dog_invent(struct monst *mtmp, struct edog *edog, int udist) /* set dog's goal -- gtyp, gx, gy; returns -1/0/1 (dog's desire to approach player) or -2 (abort move) */ -static int +staticfn int dog_goal( - register struct monst *mtmp, + struct monst *mtmp, struct edog *edog, int after, int udist, int whappr) { - register coordxy omx, omy; + coordxy omx, omy; boolean in_masters_sight, dog_has_minvent; - register struct obj *obj; + struct obj *obj; xint16 otyp; int appr; @@ -527,8 +530,8 @@ dog_goal( || !can_reach_location(mtmp, mtmp->mx, mtmp->my, nx, ny)) continue; if (otyp < MANFOOD - && (otyp < ACCFOOD || edog->hungrytime <= gm.moves) - && edog->hungrytime < gm.moves + DOG_SATIATED) { + && (otyp < ACCFOOD || edog->hungrytime <= svm.moves) + && edog->hungrytime < svm.moves + DOG_SATIATED) { if (otyp < gg.gtyp || DDIST(nx, ny) < DDIST(gg.gx, gg.gy)) { gg.gx = nx; @@ -553,7 +556,7 @@ dog_goal( /* follow player if appropriate */ if (gg.gtyp == UNDEF || (gg.gtyp != DOGFOOD && gg.gtyp != APPORT - && gm.moves < edog->hungrytime)) { + && svm.moves < edog->hungrytime)) { gg.gx = u.ux; gg.gy = u.uy; if (after && udist <= 4 && u_at(gg.gx, gg.gy)) @@ -597,7 +600,7 @@ dog_goal( #define FARAWAY (COLNO + 2) /* position outside screen */ if (u_at(gg.gx, gg.gy) && !in_masters_sight) { - register coord *cp; + coord *cp; cp = gettrack(omx, omy); if (cp) { @@ -634,9 +637,9 @@ dog_goal( #undef FARAWAY } -static struct monst * +staticfn struct monst * find_targ( - register struct monst *mtmp, + struct monst *mtmp, int dx, int dy, int maxdist) { @@ -671,14 +674,14 @@ find_targ( /* if a long worm, only accept the head as a target */ && targ->mx == curx && targ->my == cury) /* not tail */ break; - /* If the pet can't see it, it assumes it aint there */ + /* If the pet can't see it, it assumes it ain't there */ targ = 0; } } return targ; } -static int +staticfn int find_friends(struct monst *mtmp, struct monst *mtarg, int maxdist) { struct monst *pal; @@ -722,7 +725,7 @@ find_friends(struct monst *mtmp, struct monst *mtarg, int maxdist) return 0; } -static long +staticfn long score_targ(struct monst *mtmp, struct monst *mtarg) { long score = 0L; @@ -822,8 +825,8 @@ score_targ(struct monst *mtmp, struct monst *mtarg) return score; } -static struct monst * -best_target(struct monst *mtmp) /* Pet */ +staticfn struct monst * +best_target(struct monst *mtmp, boolean forced) /* Pet */ { int dx, dy; long bestscore = -40000L, currscore; @@ -866,15 +869,15 @@ 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; } /* Pet considers and maybe executes a ranged attack */ -static int -pet_ranged_attk(struct monst *mtmp) +int +pet_ranged_attk(struct monst *mtmp, boolean forced) { struct monst *mtarg; int hungry = 0; @@ -883,13 +886,13 @@ pet_ranged_attk(struct monst *mtmp) if (!mtmp->isminion) { struct edog *dog = EDOG(mtmp); - hungry = (gm.moves > (dog->hungrytime + DOG_HUNGRY)); + hungry = (svm.moves > (dog->hungrytime + DOG_HUNGRY)); } /* 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))) { @@ -949,7 +952,8 @@ pet_ranged_attk(struct monst *mtmp) */ if (mstatus != M_ATTK_MISS) return MMOVE_DONE; - } + } else if (forced) + (void) domonnoise(mtmp); return MMOVE_NOTHING; } @@ -972,7 +976,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; @@ -1018,12 +1022,12 @@ dog_move( if (edog) { j = dog_invent(mtmp, edog, udist); - if (j == 2) - return MMOVE_DIED; /* died */ + if (j == 2 || mon_offmap(mtmp)) + return DEADMONSTER(mtmp) ? MMOVE_DIED : MMOVE_DONE; else if (j == 1) goto newdogpos; /* eating something */ - whappr = (gm.moves - edog->whistletime < 5); + whappr = (svm.moves - edog->whistletime < 5); } else whappr = 0; @@ -1085,9 +1089,11 @@ 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; - register struct monst *mtmp2 = m_at(nx, ny); + struct monst *mtmp2 = m_at(nx, ny); /* weight the audacity of the pet to attack a differently-leveled * foe based on its fraction of max HP: * 100%: up to level + 2 @@ -1112,18 +1118,30 @@ dog_move( } 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 - && !grudge && !Conflict) - || (touch_petrifies(mtmp2->data) && !resists_ston(mtmp)) + || mtmp2->data->msound == MS_LEADER) + && mtmp2->mpeaceful && !grudge && !Conflict) || (attacktype(mtmp2->data, AT_BOOM) - && distu(mtmp2->mx, mtmp2->my) < 3)) + && distu(mtmp2->mx, mtmp2->my) < 3)) { + 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) @@ -1139,7 +1157,7 @@ dog_move( if ((mstatus & (M_ATTK_HIT | M_ATTK_DEF_DIED)) == M_ATTK_HIT && rn2(4) - && mtmp2->mlstmv != gm.moves + && mtmp2->mlstmv != svm.moves && !onscary(mtmp->mx, mtmp->my, mtmp2) /* monnear check needed: long worms hit on tail */ && monnear(mtmp2, mtmp->mx, mtmp->my)) { @@ -1154,7 +1172,7 @@ dog_move( if ((info[i] & ALLOW_MDISP) && MON_AT(nx, ny) && better_with_displacing && !undesirable_disp(mtmp, nx, ny)) { int mstatus; - register struct monst *mtmp2 = m_at(nx, ny); + struct monst *mtmp2 = m_at(nx, ny); mstatus = mdisplacem(mtmp, mtmp2, FALSE); /* displace monster */ if (mstatus & M_ATTK_DEF_DIED) @@ -1162,6 +1180,12 @@ dog_move( return MMOVE_NOTHING; } + /* avoid a location hero just kicked */ + if (m_avoid_kicked_loc(mtmp, nx, ny)) + continue; + if (m_avoid_soko_push_loc(mtmp, nx, ny)) + continue; + { /* Dog avoids harmful traps, but perhaps it has to pass one * in order to follow player. (Non-harmful traps do not @@ -1190,13 +1214,14 @@ dog_move( /* (minion isn't interested; `cursemsg' stays FALSE) */ if (edog) { boolean can_reach_food = could_reach_item(mtmp, nx, ny); - for (obj = gl.level.objects[nx][ny]; obj; obj = obj->nexthere) { + + for (obj = svl.level.objects[nx][ny]; obj; obj = obj->nexthere) { if (obj->cursed) { cursemsg[i] = TRUE; } else if (can_reach_food && (otyp = dogfood(mtmp, obj)) < MANFOOD - && (otyp < ACCFOOD || edog->hungrytime <= gm.moves) - && edog->hungrytime < gm.moves + DOG_SATIATED) { + && (otyp < ACCFOOD || edog->hungrytime <= svm.moves) + && edog->hungrytime < svm.moves + DOG_SATIATED) { /* Note: our dog likes the food so much that he * might eat it even when it conceals a cursed object */ nix = nx; @@ -1246,7 +1271,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: @@ -1255,8 +1280,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); @@ -1281,13 +1306,16 @@ dog_move( /* describe top item of pile, not necessarily cursed item itself; don't use glyph_at() here--it would return the pet but we want to know whether an object is remembered at this map location */ - struct obj *o = (!Hallucination && gl.level.flags.hero_memory + struct obj *o = (!Hallucination && svl.level.flags.hero_memory && glyph_is_object(levl[nix][niy].glyph)) ? vobj_at(nix, niy) : 0; const char *what = o ? distant_name(o, doname) : something; - pline("%s %s reluctantly over %s.", noit_Monnam(mtmp), - vtense((char *) 0, locomotion(mtmp->data, "step")), what); + 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", + what); } mon_track_add(mtmp, omx, omy); /* We have to know if the pet's going to do a combined eat and @@ -1355,8 +1383,11 @@ could_reach_item(struct monst *mon, coordxy nx, coordxy ny) * Since the maximum food distance is 5, this should never be more than 5 * calls deep. */ -static boolean -can_reach_location(struct monst *mon, coordxy mx, coordxy my, coordxy fx, coordxy fy) +staticfn boolean +can_reach_location( + struct monst *mon, + coordxy mx, coordxy my, + coordxy fx, coordxy fy) { int i, j; int dist; @@ -1373,7 +1404,7 @@ can_reach_location(struct monst *mon, coordxy mx, coordxy my, coordxy fx, coordx 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))) continue; if (IS_DOOR(levl[i][j].typ) && door_is_closed(&levl[i][j])) @@ -1388,7 +1419,7 @@ can_reach_location(struct monst *mon, coordxy mx, coordxy my, coordxy fx, coordx } /* do_clear_area client */ -static void +staticfn void wantdoor(coordxy x, coordxy y, genericptr_t distance) { int ndist, *dist_ptr = (int *) distance; @@ -1404,7 +1435,7 @@ static const struct qmchoices { int mndx; /* type of pet, 0 means any */ char mlet; /* symbol of pet, 0 means any */ unsigned mappearance; /* mimic this */ - uchar m_ap_type; /* what is the thing it is mimicing? */ + uchar m_ap_type; /* what is the thing it is mimicking? */ } qm[] = { /* Things that some pets might be thinking about at the time */ { PM_LITTLE_DOG, 0, PM_KITTEN, M_AP_MONSTER }, @@ -1422,7 +1453,7 @@ void finish_meating(struct monst *mtmp) { mtmp->meating = 0; - if (M_AP_TYPE(mtmp) && mtmp->mappearance && mtmp->data->mlet != S_MIMIC) { + if (M_AP_TYPE(mtmp) != M_AP_NOTHING && mtmp->data->mlet != S_MIMIC) { /* was eating a mimic and now appearance needs resetting */ mtmp->m_ap_type = M_AP_NOTHING; mtmp->mappearance = 0; @@ -1460,7 +1491,7 @@ quickmimic(struct monst *mtmp) if (trycnt == 0) idx = SIZE(qm) - 1; - Strcpy(buf, mon_nam(mtmp)); + Strcpy(buf, y_monnam(mtmp)); /* "your " or "the " or "Fang" */ spotted = canspotmon(mtmp); seeloc = cansee(mtmp->mx, mtmp->my); diff --git a/src/dokick.c b/src/dokick.c index 4f21fedd6c..0889a50cfb 100644 --- a/src/dokick.c +++ b/src/dokick.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 dokick.c $NHDT-Date: 1625963851 2021/07/11 00:37:31 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.167 $ */ +/* NetHack 3.7 dokick.c $NHDT-Date: 1712453347 2024/04/07 01:29:07 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.223 $ */ /* Copyright (c) Izchak Miller, Mike Stephenson, Steve Linhart, 1989. */ /* NetHack may be freely redistributed. See license for details. */ @@ -11,24 +11,26 @@ /* gk.kickedobj (decl.c) tracks a kicked object until placed or destroyed */ -static void kickdmg(struct monst *, boolean); -static boolean maybe_kick_monster(struct monst *, coordxy, coordxy); -static void kick_monster(struct monst *, coordxy, coordxy); -static int kick_object(coordxy, coordxy, char *); -static int really_kick_object(coordxy, coordxy); -static char *kickstr(char *, const char *); -static boolean watchman_thief_arrest(struct monst *); -static boolean watchman_door_damage(struct monst *, coordxy, coordxy); -static void kick_dumb(coordxy, coordxy); -static void kick_ouch(coordxy, coordxy, const char *); -static void kick_door(coordxy, coordxy, int); -static void otransit_msg(struct obj *, boolean, boolean, long); -static void drop_to(coord *, schar, coordxy, coordxy); +staticfn void kickdmg(struct monst *, boolean); +staticfn boolean maybe_kick_monster(struct monst *, coordxy, coordxy); +staticfn void kick_monster(struct monst *, coordxy, coordxy); +staticfn int kick_object(coordxy, coordxy, char *) NONNULLARG3; +staticfn int really_kick_object(coordxy, coordxy); +staticfn char *kickstr(char *, const char *) NONNULLPTRS; +staticfn boolean watchman_thief_arrest(struct monst *) NONNULLPTRS; +staticfn boolean watchman_door_damage(struct monst *, + coordxy, coordxy) NONNULLARG1; +staticfn void kick_dumb(coordxy, coordxy); +staticfn void kick_ouch(coordxy, coordxy, const char *) NONNULLARG3; +staticfn void kick_door(coordxy, coordxy, int); +staticfn int kick_nondoor(coordxy, coordxy, int); +staticfn void otransit_msg(struct obj *, boolean, boolean, long); +staticfn void drop_to(coord *, schar, coordxy, coordxy) NONNULLARG1; static const char kick_passes_thru[] = "kick passes harmlessly through"; /* kicking damage when not poly'd into a form with a kick attack */ -static void +staticfn void kickdmg(struct monst *mon, boolean clumsy) { int mdx, mdy; @@ -122,27 +124,27 @@ kickdmg(struct monst *mon, boolean clumsy) use_skill(kick_skill, 1); } -static boolean +staticfn boolean maybe_kick_monster(struct monst *mon, coordxy x, coordxy y) { if (mon) { - boolean save_forcefight = gc.context.forcefight; + boolean save_forcefight = svc.context.forcefight; gb.bhitpos.x = x; gb.bhitpos.y = y; if (!mon->mpeaceful || !canspotmon(mon)) - gc.context.forcefight = TRUE; /* attack even if invisible */ + svc.context.forcefight = TRUE; /* attack even if invisible */ /* kicking might be halted by discovery of hidden monster, by player declining to attack peaceful monster, or by passing out due to encumbrance */ if (attack_checks(mon, (struct obj *) 0) || overexertion()) mon = 0; /* don't kick after all */ - gc.context.forcefight = save_forcefight; + svc.context.forcefight = save_forcefight; } return (boolean) (mon != 0); } -static void +staticfn void kick_monster(struct monst *mon, coordxy x, coordxy y) { boolean clumsy = FALSE; @@ -296,7 +298,7 @@ kick_monster(struct monst *mon, coordxy x, coordxy y) * The gold object is *not* attached to the fobj chain! */ boolean -ghitm(register struct monst *mtmp, register struct obj *gold) +ghitm(struct monst *mtmp, struct obj *gold) { boolean msg_given = FALSE; @@ -311,14 +313,18 @@ ghitm(register struct monst *mtmp, register struct obj *gold) msg_given = TRUE; } } else { + unsigned was_sleeping = mtmp->msleeping; long umoney, value = gold->quan * objects[gold->otyp].oc_cost; + /* end indeterminate sleep (won't get here for temporary--counted--sleep + * since that uses mfrozen and mfrozen implies !mcanmove) */ wakeup(mtmp, FALSE, TRUE); if (!mtmp->isgd && !rn2(4)) /* not always pleasing */ setmangry(mtmp, TRUE); /* greedy monsters catch gold */ if (cansee(mtmp->mx, mtmp->my)) - pline("%s catches the gold.", Monnam(mtmp)); + pline("%s %scatches the gold.", Monnam(mtmp), + was_sleeping ? "awakens and " : ""); (void) mpickobj(mtmp, gold); gold = (struct obj *) 0; /* obj has been freed */ if (mtmp->isshk) { @@ -416,7 +422,7 @@ container_impact_dmg( struct monst *shkp; struct obj *otmp, *otmp2; long loss = 0L; - boolean costly, insider, frominv; + boolean costly, insider, frominv, wchange = FALSE; /* only consider normal containers */ if (!Is_container(obj) || !Has_contents(obj) || Is_mbag(obj)) @@ -445,7 +451,7 @@ container_impact_dmg( /* eggs laid by you. penalty is -1 per egg, max 5, * but it's always exactly 1 that breaks */ - if (otmp->otyp == EGG && otmp->spe && otmp->corpsenm >= LOW_PM) + if (otmp->otyp == EGG && otmp->spe && ismnum(otmp->corpsenm)) change_luck(-1); if (otmp->otyp == EGG) { Soundeffect(se_egg_cracking, 25); @@ -467,8 +473,11 @@ container_impact_dmg( } /* contents of this container are no longer known */ obj->cknown = 0; + wchange = TRUE; } } + if (wchange) + obj->owt = weight(obj); if (costly && loss) { if (!insider) { You("caused %ld %s worth of damage!", loss, currency(loss)); @@ -481,17 +490,18 @@ container_impact_dmg( } /* jacket around really_kick_object */ -static int +staticfn int kick_object(coordxy x, coordxy y, char *kickobjnam) { int res = 0; *kickobjnam = '\0'; /* if a pile, the "top" object gets kicked */ - gk.kickedobj = gl.level.objects[x][y]; + gk.kickedobj = svl.level.objects[x][y]; if (gk.kickedobj) { - /* kick object; if doing is fatal, done() will clean up gk.kickedobj */ - Strcpy(kickobjnam, killer_xname(gk.kickedobj)); /* matters iff res==0 */ + /* formatted object name matters iff res==0 */ + Strcpy(kickobjnam, killer_xname(gk.kickedobj)); + /* kick object; if fatal, done() will clean up kickedobj */ res = really_kick_object(x, y); gk.kickedobj = (struct obj *) 0; } @@ -499,7 +509,7 @@ kick_object(coordxy x, coordxy y, char *kickobjnam) } /* guts of kick_object */ -static int +staticfn int really_kick_object(coordxy x, coordxy y) { int range; @@ -548,9 +558,9 @@ really_kick_object(coordxy x, coordxy y) ; /* hero has been transformed but kick continues */ } else { /* normalize body shape here; foot, not body_part(FOOT) */ - Sprintf(gk.killer.name, "kicking %s barefoot", + Sprintf(svk.killer.name, "kicking %s barefoot", killer_xname(gk.kickedobj)); - instapetrify(gk.killer.name); + instapetrify(svk.killer.name); } } @@ -601,7 +611,8 @@ really_kick_object(coordxy x, coordxy y) range = 1; /* see if the object has a place to move into */ - if (!ZAP_POS(levl[x + u.dx][y + u.dy].typ) + if (!isok(x + u.dx, y + u.dy) + || !ZAP_POS(levl[x + u.dx][y + u.dy].typ) || closed_door(x + u.dx, y + u.dy)) range = 1; @@ -616,9 +627,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 @@ -643,6 +654,7 @@ really_kick_object(coordxy x, coordxy y) } if (!flooreffects(gk.kickedobj, u.ux, u.uy, "fall")) { place_object(gk.kickedobj, u.ux, u.uy); + impact_disturbs_zombies(gk.kickedobj, TRUE); stackobj(gk.kickedobj); newsym(u.ux, u.uy); } @@ -704,7 +716,7 @@ really_kick_object(coordxy x, coordxy y) if (!Deaf) pline1("Thwwpingg!"); - You("%s!", flyingcoinmsg[rn2(SIZE(flyingcoinmsg))]); + You("%s!", ROLL_FROM(flyingcoinmsg)); (void) scatter(x, y, rnd(3), VIS_EFFECTS | MAY_HIT, gk.kickedobj); newsym(x, y); @@ -786,13 +798,14 @@ really_kick_object(coordxy x, coordxy y) donate_gold(gtg, shkp, FALSE); } place_object(gk.kickedobj, gb.bhitpos.x, gb.bhitpos.y); + impact_disturbs_zombies(gk.kickedobj, TRUE); stackobj(gk.kickedobj); newsym(gk.kickedobj->ox, gk.kickedobj->oy); return 1; } /* cause of death if kicking kills kicker */ -static char * +staticfn char * kickstr(char *buf, const char *kickobjnam) { const char *what; @@ -807,7 +820,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"; @@ -832,7 +845,7 @@ kickstr(char *buf, const char *kickobjnam) return strcat(strcpy(buf, "kicking "), what); } -static boolean +staticfn boolean watchman_thief_arrest(struct monst *mtmp) { if (is_watch(mtmp->data) && couldsee(mtmp->mx, mtmp->my) @@ -844,7 +857,7 @@ watchman_thief_arrest(struct monst *mtmp) return FALSE; } -static boolean +staticfn boolean watchman_door_damage(struct monst *mtmp, coordxy x, coordxy y) { if (is_watch(mtmp->data) && mtmp->mpeaceful @@ -862,7 +875,7 @@ watchman_door_damage(struct monst *mtmp, coordxy x, coordxy y) return FALSE; } -static void +staticfn void kick_dumb(coordxy x, coordxy y) { exercise(A_DEX, FALSE); @@ -879,7 +892,7 @@ kick_dumb(coordxy x, coordxy y) hurtle(-u.dx, -u.dy, 1, TRUE); } -static void +staticfn void kick_ouch(coordxy x, coordxy y, const char *kickobjnam) { int dmg; @@ -895,7 +908,7 @@ kick_ouch(coordxy x, coordxy y, const char *kickobjnam) pline("Ouch! That hurts!"); exercise(A_DEX, FALSE); exercise(A_STR, FALSE); - wake_nearby(); + wake_nearby(FALSE); if (isok(x, y)) { if (Blind) feel_location(x, y); /* we know we hit it */ @@ -919,9 +932,11 @@ kick_ouch(coordxy x, coordxy y, const char *kickobjnam) hurtle(-u.dx, -u.dy, rn1(2, 4), TRUE); /* assume it's heavy */ } -static void +/* kick a door */ +staticfn void kick_door(coordxy x, coordxy y, int avrg_attrib) { + boolean doorbuster; /* all door states besides closed count as empty space */ if (!door_is_closed(gm.maploc)) { kick_dumb(x, y); @@ -935,12 +950,15 @@ kick_door(coordxy x, coordxy y, int avrg_attrib) } exercise(A_DEX, TRUE); + doorbuster = Upolyd && is_giant(gy.youmonst.data); /* door is known to be CLOSED or LOCKED */ - if (rnl(35) < avrg_attrib + (!martial() ? 0 : ACURR(A_DEX)) - && !door_is_iron(gm.maploc)) { + if (!door_is_iron(gm.maploc) + && ((rnl(35) < avrg_attrib + (!martial() ? 0 : ACURR(A_DEX))) + || doorbuster)) { boolean shopdoor = *in_rooms(x, y, SHOPBASE) ? TRUE : FALSE; + /* break the door */ - if (Verbose(0, dokick)) + if (flags.verbose) You("kick the door."); if (predoortrapped(x, y, &gy.youmonst, FOOT, D_BROKEN) < 2) { if (ACURR(A_STR) > 18 && !rn2(5) && !shopdoor) { @@ -984,7 +1002,310 @@ kick_door(coordxy x, coordxy y, int avrg_attrib) if (in_town(x, y)) (void) get_iter_mons_xy(watchman_door_damage, x, y); } - wake_nearby(); + wake_nearby(FALSE); +} + +/* kick non-door terrain */ +staticfn int +kick_nondoor(coordxy x, coordxy y, int avrg_attrib) +{ + if (gm.maploc->typ == SDOOR) { + if (!Levitation && rn2(30) < avrg_attrib) { + cvt_sdoor_to_door(gm.maploc); /* ->typ = DOOR */ + Soundeffect(se_crash_door, 40); + pline("Crash! %s a secret door!", + /* don't "kick open" when it's locked */ + (door_is_locked(gm.maploc) || door_is_iron(gm.maploc)) + ? "Your kick uncovers" + : "You kick open"); + exercise(A_DEX, TRUE); + predoortrapped(x, y, &gy.youmonst, FOOT, D_BROKEN); + if (!door_is_iron(gm.maploc) && !door_is_locked(gm.maploc)) { + /* assume doorstate is already D_CLOSED */ + postdoortrapped(x, y, &gy.youmonst, FOOT, D_ISOPEN); + set_doorstate(gm.maploc, D_ISOPEN); + } + feel_newsym(x, y); + if (doorstate(gm.maploc) == D_ISOPEN + || doorstate(gm.maploc) == D_NODOOR) + unblock_point(x, y); /* vision */ + } else { + /* Don't reveal whether secret door or secret corridor. */ + pline(Deaf ? "The wall gives way a little." + : "The wall responds with a hollow thump."); + } + wake_nearby(FALSE); + return ECMD_TIME; + } + if (gm.maploc->typ == SCORR) { + if (!Levitation && rn2(30) < avrg_attrib) { + Soundeffect(se_crash_door, 40); + pline("Crash! You kick open a secret passage!"); + exercise(A_DEX, TRUE); + gm.maploc->typ = CORR; + feel_newsym(x, y); /* we know it's gone */ + unblock_point(x, y); /* vision */ + return ECMD_TIME; + } else { + /* Don't reveal whether secret door or secret corridor. */ + pline(Deaf ? "The wall gives way a little." + : "The wall responds with a hollow thump."); + } + wake_nearby(FALSE); + return ECMD_TIME; + } + if (IS_THRONE(gm.maploc->typ)) { + int i; + if (Levitation) { + kick_dumb(x, y); + return ECMD_TIME; + } + if ((Luck < 0 || gm.maploc->looted) && !rn2(3)) { + gm.maploc->looted = 0; /* don't leave loose ends.. */ + gm.maploc->typ = ROOM; + (void) mkgold((long) rnd(200), x, y); + Soundeffect(se_crash_throne_destroyed, 60); + if (Blind) + pline("CRASH! You destroy it."); + else { + pline("CRASH! You destroy the throne."); + newsym(x, y); + } + exercise(A_DEX, TRUE); + wake_nearby(FALSE); + return ECMD_TIME; + } else if (Luck > 0 && !rn2(3) && !gm.maploc->looted) { + (void) mkgold((long) rn1(201, 300), x, y); + i = Luck + 1; + if (i > 6) + i = 6; + while (i--) + (void) mksobj_at( + rnd_class(DILITHIUM_CRYSTAL, LUCKSTONE - 1), x, y, + FALSE, TRUE); + if (Blind) + You("kick %s loose!", something); + else { + You("kick loose some ornamental coins and gems!"); + newsym(x, y); + } + /* prevent endless milking */ + gm.maploc->looted = T_LOOTED; + wake_nearby(FALSE); + return ECMD_TIME; + } else if (!rn2(4)) { + if (dunlev(&u.uz) < dunlevs_in_dungeon(&u.uz)) { + fall_through(FALSE, 0); + return ECMD_TIME; + } else { + kick_ouch(x, y, ""); + return ECMD_TIME; + } + } + kick_ouch(x, y, ""); + return ECMD_TIME; + } + if (IS_ALTAR(gm.maploc->typ)) { + if (Levitation) { + kick_dumb(x, y); + return ECMD_TIME; + } + You("kick %s.", (Blind ? something : "the altar")); + altar_wrath(x, y); + wake_nearby(FALSE); + if (!rn2(3)) { + kick_ouch(x, y, ""); + return ECMD_TIME; + } + exercise(A_DEX, TRUE); + return ECMD_TIME; + } + if (IS_FOUNTAIN(gm.maploc->typ)) { + if (Levitation) { + kick_dumb(x, y); + return ECMD_TIME; + } + You("kick %s.", (Blind ? something : "the fountain")); + wake_nearby(FALSE); + if (!rn2(3)) { + kick_ouch(x, y, ""); + return ECMD_TIME; + } + /* make metal boots rust */ + if (uarmf && rn2(3)) + if (water_damage(uarmf, "metal boots", TRUE) == ER_NOTHING) { + Your("boots get wet."); + /* could cause short-lived fumbling here */ + } + exercise(A_DEX, TRUE); + return ECMD_TIME; + } + if (IS_GRAVE(gm.maploc->typ)) { + if (Levitation) { + kick_dumb(x, y); + } else if (rn2(4)) { + /* minor injury */ + kick_ouch(x, y, ""); + } else if (!gm.maploc->disturbed && !rn2(2)) { + /* disturb the grave: summon a ghoul (once only), same as + when engraving */ + disturb_grave(x, y); + } else { + /* destroy the headstone, implicitly destroying any + not-yet-created contents (including zombie or mummy); + any already created contents will still be buried here */ + exercise(A_WIS, FALSE); + if (Role_if(PM_ARCHEOLOGIST) || Role_if(PM_SAMURAI) + || (u.ualign.type == A_LAWFUL && u.ualign.record > -10)) + adjalign(-sgn(u.ualign.type)); + gm.maploc->typ = ROOM; + gm.maploc->emptygrave = 0; /* clear 'flags' */ + gm.maploc->disturbed = 0; /* clear 'horizontal' */ + (void) mksobj_at(ROCK, x, y, TRUE, FALSE); + del_engr_at(x, y); + if (Blind) { + /* [feel this happen if Deaf?] */ + pline("Crack! %s broke!", Something); + } else { + pline_The("headstone topples over and breaks!"); + newsym(x, y); + } + } + wake_nearby(FALSE); + return ECMD_TIME; + } + if (gm.maploc->typ == IRONBARS) { + kick_ouch(x, y, ""); + return ECMD_TIME; + } + if (IS_TREE(gm.maploc->typ)) { + struct obj *treefruit; + + wake_nearby(FALSE); + /* nothing, fruit or trouble? 75:23.5:1.5% */ + if (rn2(3)) { + if (!rn2(6) && !(svm.mvitals[PM_KILLER_BEE].mvflags & G_GONE)) + You_hear("a low buzzing."); /* a warning */ + kick_ouch(x, y, ""); + return ECMD_TIME; + } + if (rn2(15) && !(gm.maploc->looted & TREE_LOOTED) + && (treefruit = rnd_treefruit_at(x, y))) { + long nfruit = 8L - rnl(7), nfall; + short frtype = treefruit->otyp; + + treefruit->quan = nfruit; + treefruit->owt = weight(treefruit); + if (is_plural(treefruit)) + pline("Some %s fall from the tree!", xname(treefruit)); + else + pline("%s falls from the tree!", An(xname(treefruit))); + nfall = scatter(x, y, 2, MAY_HIT, treefruit); + if (nfall != nfruit) { + /* scatter left some in the tree, but treefruit + * may not refer to the correct object */ + treefruit = mksobj(frtype, TRUE, FALSE); + treefruit->quan = nfruit - nfall; + pline("%ld %s got caught in the branches.", + nfruit - nfall, xname(treefruit)); + dealloc_obj(treefruit); + } + exercise(A_DEX, TRUE); + exercise(A_WIS, TRUE); /* discovered a new food source! */ + newsym(x, y); + gm.maploc->looted |= TREE_LOOTED; + return ECMD_TIME; + } else if (!(gm.maploc->looted & TREE_SWARM)) { + int cnt = rnl(4) + 2; + int made = 0; + coord mm; + + mm.x = x; + mm.y = y; + while (cnt--) { + if (enexto(&mm, mm.x, mm.y, &mons[PM_KILLER_BEE]) + && makemon(&mons[PM_KILLER_BEE], mm.x, mm.y, + MM_ANGRY|MM_NOMSG)) + made++; + } + if (made) + pline("You've attracted the tree's former occupants!"); + else + You("smell stale honey."); + gm.maploc->looted |= TREE_SWARM; + return ECMD_TIME; + } + kick_ouch(x, y, ""); + return ECMD_TIME; + } + if (IS_SINK(gm.maploc->typ)) { + if (Levitation) { + kick_dumb(x, y); + return ECMD_TIME; + } + if (rn2(3)) { + Soundeffect(se_klunk_pipe, 60); + if (!Deaf) + pline("Klunk! The pipes vibrate noisily."); + else + pline("Klunk!"); + exercise(A_DEX, TRUE); + wake_nearby(FALSE); + return ECMD_TIME; + } else if (!(gm.maploc->looted & S_LPUDDING) && !rn2(3) + && !(svm.mvitals[PM_BLACK_PUDDING].mvflags & G_GONE)) { + Soundeffect(se_gushing_sound, 100); + if (Blind) { + if (!Deaf) + You_hear("a gushing sound."); + } else { + pline("A %s ooze gushes up from the drain!", + hcolor(NH_BLACK)); + } + (void) makemon(&mons[PM_BLACK_PUDDING], x, y, MM_NOMSG); + exercise(A_DEX, TRUE); + newsym(x, y); + gm.maploc->looted |= S_LPUDDING; + wake_nearby(FALSE); + return ECMD_TIME; + } else if (!(gm.maploc->looted & S_LDWASHER) && !rn2(3) + && !(svm.mvitals[PM_AMOROUS_DEMON].mvflags & G_GONE)) { + /* can't resist... */ + int gend = poly_gender(); + long mmflag = MM_FEMALE; + if (((gend == 2 || flags.orientation == ORIENT_BISEXUAL) + && rn2(2)) + || (gend == 1 && flags.orientation == ORIENT_STRAIGHT) + || (gend == 0 && flags.orientation == ORIENT_GAY)) { + /* if neuter or bisexual, pick randomly + * if female and straight, or male and gay, make incubus */ + mmflag = MM_MALE; + } + pline("%s returns!", (Blind ? Something : "The dish washer")); + if (makemon(&mons[PM_AMOROUS_DEMON], x, y, mmflag)) + newsym(x, y); + gm.maploc->looted |= S_LDWASHER; + exercise(A_DEX, TRUE); + wake_nearby(FALSE); + return ECMD_TIME; + } else if (!rn2(3)) { + sink_backs_up(x, y); + return ECMD_TIME; + } + kick_ouch(x, y, ""); + return ECMD_TIME; + } + if (gm.maploc->typ == STAIRS || gm.maploc->typ == LADDER + || IS_STWALL(gm.maploc->typ)) { + if (!IS_STWALL(gm.maploc->typ) && gm.maploc->ladder == LA_DOWN) { + kick_dumb(x, y); + return ECMD_TIME; + } + kick_ouch(x, y, ""); + return ECMD_TIME; + } + kick_dumb(x, y); + return ECMD_TIME; } /* the #kick command */ @@ -994,9 +1315,8 @@ dokick(void) coordxy x, y; int avrg_attrib; int glyph, oldglyph = -1; - register struct monst *mtmp; + struct monst *mtmp; boolean no_kick = FALSE; - char buf[BUFSZ]; if (nolimbs(gy.youmonst.data) || slithy(gy.youmonst.data)) { You("have no legs to kick with."); @@ -1058,6 +1378,7 @@ dokick(void) x = u.ux + u.dx; y = u.uy + u.dy; + gk.kickedloc.x = x, gk.kickedloc.y = y; /* KMH -- Kicking boots always succeed */ if (uarmf && uarmf->otyp == KICKING_BOOTS) @@ -1075,6 +1396,7 @@ dokick(void) pline("%s burps loudly.", Monnam(u.ustuck)); break; } + FALLTHROUGH; /*FALLTHRU*/ default: Your("feeble kick has no effect."); @@ -1095,7 +1417,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."); @@ -1106,12 +1428,12 @@ dokick(void) mtmp = isok(x, y) ? m_at(x, y) : 0; /* might not kick monster if it is hidden and becomes revealed, if it is peaceful and player declines to attack, or if the - hero passes out due to encumbrance with low hp; gc.context.move + hero passes out due to encumbrance with low hp; svc.context.move will be 1 unless player declines to kick peaceful monster */ if (mtmp) { oldglyph = glyph_at(x, y); if (!maybe_kick_monster(mtmp, x, y)) - return (gc.context.move ? ECMD_TIME : ECMD_OK); + return (svc.context.move ? ECMD_TIME : ECMD_OK); } u_wipe_engr(2); @@ -1158,7 +1480,7 @@ dokick(void) map_invisible(x, y); } /* recoil if floating */ - if ((Is_airlevel(&u.uz) || Levitation) && gc.context.move) { + if ((Is_airlevel(&u.uz) || Levitation) && svc.context.move) { int range; range = @@ -1171,7 +1493,7 @@ dokick(void) range = 1; hurtle(-u.dx, -u.dy, range, TRUE); } - wake_nearby(); + wake_nearby(FALSE); return ECMD_TIME; } (void) unmap_invisible(x, y); @@ -1180,7 +1502,7 @@ dokick(void) You("splash some %s around.", hliquid(is_pool(x, y) ? "water" : "lava")); /* pretend the kick is fast enough for lava not to burn */ - wake_nearby(); + wake_nearby(FALSE); return ECMD_TIME; } @@ -1191,335 +1513,21 @@ dokick(void) if (kick_object(x, y, kickobjnam)) { if (Is_airlevel(&u.uz)) hurtle(-u.dx, -u.dy, 1, TRUE); /* assume it's light */ - wake_nearby(); + wake_nearby(FALSE); return ECMD_TIME; } kick_ouch(x, y, kickobjnam); return ECMD_TIME; } - if (!IS_DOOR(gm.maploc->typ)) { - if (gm.maploc->typ == SDOOR) { - if (!Levitation && rn2(30) < avrg_attrib) { - cvt_sdoor_to_door(gm.maploc); /* ->typ = DOOR */ - Soundeffect(se_crash_door, 40); - pline("Crash! %s a secret door!", - /* don't "kick open" when it's locked - unless it also happens to be trapped */ - (door_is_locked(gm.maploc) || door_is_iron(gm.maploc)) - ? "Your kick uncovers" - : "You kick open"); - exercise(A_DEX, TRUE); - predoortrapped(x, y, &gy.youmonst, FOOT, D_BROKEN); - if (!door_is_iron(gm.maploc) && !door_is_locked(gm.maploc)) { - /* assume doorstate is already D_CLOSED */ - postdoortrapped(x, y, &gy.youmonst, FOOT, D_ISOPEN); - set_doorstate(gm.maploc, D_ISOPEN); - } - feel_newsym(x, y); - if (doorstate(gm.maploc) == D_ISOPEN - || doorstate(gm.maploc) == D_NODOOR) - unblock_point(x, y); /* vision */ - } else { - /* Don't reveal whether secret door or secret corridor. */ - pline(Deaf ? "The wall gives way a little." - : "The wall responds with a hollow thump."); - } - wake_nearby(); - return ECMD_TIME; - } - if (gm.maploc->typ == SCORR) { - if (!Levitation && rn2(30) < avrg_attrib) { - Soundeffect(se_crash_door, 40); - pline("Crash! You kick open a secret passage!"); - exercise(A_DEX, TRUE); - gm.maploc->typ = CORR; - feel_newsym(x, y); /* we know it's gone */ - unblock_point(x, y); /* vision */ - } else { - /* Don't reveal whether secret door or secret corridor. */ - pline(Deaf ? "The wall gives way a little." - : "The wall responds with a hollow thump."); - } - wake_nearby(); - return ECMD_TIME; - } - if (IS_THRONE(gm.maploc->typ)) { - register int i; - if (Levitation) { - kick_dumb(x, y); - return ECMD_TIME; - } - if ((Luck < 0 || gm.maploc->looted) && !rn2(3)) { - gm.maploc->looted = 0; /* don't leave loose ends.. */ - gm.maploc->typ = ROOM; - (void) mkgold((long) rnd(200), x, y); - Soundeffect(se_crash_throne_destroyed, 60); - if (Blind) - pline("CRASH! You destroy it."); - else { - pline("CRASH! You destroy the throne."); - newsym(x, y); - } - exercise(A_DEX, TRUE); - wake_nearby(); - return ECMD_TIME; - } else if (Luck > 0 && !rn2(3) && !gm.maploc->looted) { - (void) mkgold((long) rn1(201, 300), x, y); - i = Luck + 1; - if (i > 6) - i = 6; - while (i--) - (void) mksobj_at( - rnd_class(DILITHIUM_CRYSTAL, LUCKSTONE - 1), x, y, - FALSE, TRUE); - if (Blind) - You("kick %s loose!", something); - else { - You("kick loose some ornamental coins and gems!"); - newsym(x, y); - } - /* prevent endless milking */ - gm.maploc->looted = T_LOOTED; - wake_nearby(); - return ECMD_TIME; - } else if (!rn2(4)) { - if (dunlev(&u.uz) < dunlevs_in_dungeon(&u.uz)) { - fall_through(FALSE, 0); - return ECMD_TIME; - } else { - kick_ouch(x, y, ""); - return ECMD_TIME; - } - } - kick_ouch(x, y, ""); - return ECMD_TIME; - } - if (IS_ALTAR(gm.maploc->typ)) { - if (Levitation) { - kick_dumb(x, y); - return ECMD_TIME; - } - You("kick %s.", (Blind ? something : "the altar")); - altar_wrath(x, y); - wake_nearby(); - if (!rn2(3)) { - kick_ouch(x, y, ""); - } - exercise(A_DEX, TRUE); - return ECMD_TIME; - } - if (IS_FOUNTAIN(gm.maploc->typ)) { - if (Levitation) { - kick_dumb(x, y); - return ECMD_TIME; - } - You("kick %s.", (Blind ? something : "the fountain")); - wake_nearby(); - if (!rn2(3)) { - kick_ouch(x, y, ""); - } - /* make metal boots rust */ - if (uarmf && rn2(3)) - if (water_damage(uarmf, "metal boots", TRUE) == ER_NOTHING) { - Your("boots get wet."); - /* could cause short-lived fumbling here */ - } - exercise(A_DEX, TRUE); - return ECMD_TIME; - } - if (IS_GRAVE(gm.maploc->typ)) { - if (Levitation) { - kick_dumb(x, y); - } else if (rn2(4)) { - /* minor injury */ - kick_ouch(x, y, ""); - } else if (!gm.maploc->disturbed && !rn2(2)) { - /* disturb the grave: summon a ghoul (once only), same as - when engraving */ - disturb_grave(x, y); - } else { - /* destroy the headstone, implicitly destroying any - not-yet-created contents (including zombie or mummy); - any already created contents will still be buried here */ - exercise(A_WIS, FALSE); - if (Role_if(PM_ARCHEOLOGIST) || Role_if(PM_SAMURAI) - || (u.ualign.type == A_LAWFUL && u.ualign.record > -10)) - adjalign(-sgn(u.ualign.type)); - - gm.maploc->typ = ROOM; - gm.maploc->emptygrave = 0; /* clear 'flags' */ - gm.maploc->disturbed = 0; /* clear 'horizontal' */ - (void) mksobj_at(ROCK, x, y, TRUE, FALSE); - del_engr_at(x, y); - if (Blind) { - /* [feel this happen if Deaf?] */ - pline("Crack! %s broke!", Something); - } else { - pline_The("headstone topples over and breaks!"); - newsym(x, y); - } - } - wake_nearby(); - return ECMD_TIME; - } - if (gm.maploc->typ == IRONBARS) { - kick_ouch(x, y, ""); - return ECMD_TIME; - } - if (IS_TREE(gm.maploc->typ)) { - struct obj *treefruit; - - wake_nearby(); - /* nothing, fruit or trouble? 75:23.5:1.5% */ - if (rn2(3)) { - if (!rn2(6) && !(gm.mvitals[PM_KILLER_BEE].mvflags & G_GONE)) - You_hear("a low buzzing."); /* a warning */ - kick_ouch(x, y, ""); - return ECMD_TIME; - } - if (rn2(15) && !(gm.maploc->looted & TREE_LOOTED) - && (treefruit = rnd_treefruit_at(x, y))) { - long nfruit = 8L - rnl(7), nfall; - short frtype = treefruit->otyp; - - treefruit->quan = nfruit; - treefruit->owt = weight(treefruit); - if (is_plural(treefruit)) - pline("Some %s fall from the tree!", xname(treefruit)); - else - pline("%s falls from the tree!", An(xname(treefruit))); - nfall = scatter(x, y, 2, MAY_HIT, treefruit); - if (nfall != nfruit) { - /* scatter left some in the tree, but treefruit - * may not refer to the correct object */ - treefruit = mksobj(frtype, TRUE, FALSE); - treefruit->quan = nfruit - nfall; - pline("%ld %s got caught in the branches.", - nfruit - nfall, xname(treefruit)); - dealloc_obj(treefruit); - } - exercise(A_DEX, TRUE); - exercise(A_WIS, TRUE); /* discovered a new food source! */ - newsym(x, y); - gm.maploc->looted |= TREE_LOOTED; - return ECMD_TIME; - } else if (!(gm.maploc->looted & TREE_SWARM)) { - int cnt = rnl(4) + 2; - int made = 0; - coord mm; - - mm.x = x; - mm.y = y; - while (cnt--) { - if (enexto(&mm, mm.x, mm.y, &mons[PM_KILLER_BEE]) - && makemon(&mons[PM_KILLER_BEE], mm.x, mm.y, - MM_ANGRY|MM_NOMSG)) - made++; - } - if (made) - pline("You've attracted the tree's former occupants!"); - else - You("smell stale honey."); - gm.maploc->looted |= TREE_SWARM; - return ECMD_TIME; - } - kick_ouch(x, y, ""); - return ECMD_TIME; - } - if (IS_SINK(gm.maploc->typ)) { - - if (Levitation) { - kick_dumb(x, y); - return ECMD_TIME; - } - if (rn2(3)) { - Soundeffect(se_klunk_pipe, 60); - if (!Deaf) - pline("Klunk! The pipes vibrate noisily."); - else - pline("Klunk!"); - exercise(A_DEX, TRUE); - wake_nearby(); - return ECMD_TIME; - } else if (!(gm.maploc->looted & S_LPUDDING) && !rn2(3) - && !(gm.mvitals[PM_BLACK_PUDDING].mvflags & G_GONE)) { - Soundeffect(se_gushing_sound, 100); - if (Blind) { - if (!Deaf) - You_hear("a gushing sound."); - } else { - pline("A %s ooze gushes up from the drain!", - hcolor(NH_BLACK)); - } - (void) makemon(&mons[PM_BLACK_PUDDING], x, y, MM_NOMSG); - exercise(A_DEX, TRUE); - newsym(x, y); - gm.maploc->looted |= S_LPUDDING; - wake_nearby(); - return ECMD_TIME; - } else if (!(gm.maploc->looted & S_LDWASHER) && !rn2(3) - && !(gm.mvitals[PM_AMOROUS_DEMON].mvflags & G_GONE)) { - /* can't resist... */ - int gend = poly_gender(); - long mmflag = MM_FEMALE; - if (((gend == 2 || flags.orientation == ORIENT_BISEXUAL) - && rn2(2)) - || (gend == 1 && flags.orientation == ORIENT_STRAIGHT) - || (gend == 0 && flags.orientation == ORIENT_GAY)) { - /* if neuter or bisexual, pick randomly - * if female and straight, or male and gay, make incubus */ - mmflag = MM_MALE; - } - pline("%s returns!", (Blind ? Something : "The dish washer")); - if (makemon(&mons[PM_AMOROUS_DEMON], x, y, mmflag)) - newsym(x, y); - gm.maploc->looted |= S_LDWASHER; - exercise(A_DEX, TRUE); - wake_nearby(); - return ECMD_TIME; - } else if (!rn2(3)) { - if (Blind && Deaf) - Sprintf(buf, " %s", body_part(FACE)); - else - buf[0] = '\0'; - pline("%s%s%s.", !Deaf ? "Flupp! " : "", - !Blind - ? "Muddy waste pops up from the drain" - : !Deaf - ? "You hear a sloshing sound" /* Deaf-aware */ - : "Something splashes you in the", buf); - struct obj * otmp = ring_from_sink(x, y); - if (otmp) { - if (!Blind) - You_see("a ring shining in its midst."); - exercise(A_DEX, TRUE); - exercise(A_WIS, TRUE); /* a discovery! */ - } - wake_nearby(); - return ECMD_TIME; - } - kick_ouch(x, y, ""); - return ECMD_TIME; - } - if (gm.maploc->typ == STAIRS || gm.maploc->typ == LADDER - || IS_STWALL(gm.maploc->typ)) { - if (!IS_STWALL(gm.maploc->typ) && gm.maploc->ladder == LA_DOWN) { - kick_dumb(x, y); - return ECMD_TIME; - } - kick_ouch(x, y, ""); - return ECMD_TIME; - } - kick_dumb(x, y); - return ECMD_TIME; - } - - kick_door(x, y, avrg_attrib); + if (IS_DOOR(gm.maploc->typ)) + kick_door(x, y, avrg_attrib); + else + return kick_nondoor(x, y, avrg_attrib); return ECMD_TIME; } -static void +staticfn void drop_to(coord *cc, schar loc, coordxy x, coordxy y) { stairway *stway = stairway_at(x, y); @@ -1535,6 +1543,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: @@ -1563,8 +1572,8 @@ impact_drop( xint16 dlev) /* if !0 send to dlev near player */ { schar toloc; - register struct obj *obj, *obj2; - register struct monst *shkp; + struct obj *obj, *obj2; + struct monst *shkp; long oct, dct, price, debit, robbed; boolean angry, costly, isrock; coord cc; @@ -1605,7 +1614,7 @@ impact_drop( isrock = (missile && missile->otyp == ROCK); oct = dct = 0L; - for (obj = gl.level.objects[x][y]; obj; obj = obj2) { + for (obj = svl.level.objects[x][y]; obj; obj = obj2) { obj2 = obj->nexthere; if (obj == missile) continue; @@ -1661,11 +1670,11 @@ impact_drop( You("removed %ld %s worth of goods!", price, currency(price)); if (cansee(shkp->mx, shkp->my)) { if (ESHK(shkp)->customer[0] == 0) - (void) strncpy(ESHK(shkp)->customer, gp.plname, PL_NSIZ); + (void) strncpy(ESHK(shkp)->customer, svp.plname, PL_NSIZ); if (angry) pline("%s is infuriated!", Shknam(shkp)); else - pline("\"%s, you are a thief!\"", gp.plname); + pline("\"%s, you are a thief!\"", svp.plname); } else You_hear("a scream, \"Thief!\""); hot_pursuit(shkp); @@ -1712,7 +1721,7 @@ ship_object(struct obj *otmp, coordxy x, coordxy y, boolean shop_floor_obj) unpaid = is_unpaid(otmp); if (OBJ_AT(x, y)) { - for (obj = gl.level.objects[x][y]; obj; obj = obj->nexthere) { + for (obj = svl.level.objects[x][y]; obj; obj = obj->nexthere) { if (obj == uchain) chainthere = TRUE; else if (obj != otmp) @@ -1773,7 +1782,7 @@ ship_object(struct obj *otmp, coordxy x, coordxy y, boolean shop_floor_obj) result = "crash"; } else { /* penalty for breaking eggs laid by you */ - if (otmp->otyp == EGG && otmp->spe && otmp->corpsenm >= LOW_PM) + if (otmp->otyp == EGG && otmp->spe && ismnum(otmp->corpsenm)) change_luck((schar) -min(otmp->quan, 5L)); result = "splat"; } @@ -1816,7 +1825,7 @@ ship_object(struct obj *otmp, coordxy x, coordxy y, boolean shop_floor_obj) void obj_delivery(boolean near_hero) { - register struct obj *otmp, *otmp2; + struct obj *otmp, *otmp2; int nx = 0, ny = 0; int where; boolean nobreak, noscatter; @@ -1850,6 +1859,7 @@ obj_delivery(boolean near_hero) switch (where) { case MIGR_LADDER_UP: isladder = TRUE; + FALLTHROUGH; /*FALLTHRU*/ case MIGR_STAIRS_UP: case MIGR_SSTAIRS: @@ -1877,7 +1887,7 @@ obj_delivery(boolean near_hero) struct obj* cobj; boolean found_container = FALSE; /* put into a container on this spot, if possible */ - cobj = gl.level.objects[nx][ny]; + cobj = svl.level.objects[nx][ny]; for (; cobj; cobj = cobj->nexthere) { if (Is_container(cobj)) { if (obj_is_burning(otmp)) @@ -1977,8 +1987,8 @@ deliver_obj_to_mon(struct monst *mtmp, int cnt, unsigned long deliverflags) } } -static void -otransit_msg(register struct obj *otmp, boolean nodrop, boolean chainthere, long num) +staticfn void +otransit_msg(struct obj *otmp, boolean nodrop, boolean chainthere, long num) { char *optr = 0, obuf[BUFSZ], xbuf[BUFSZ]; diff --git a/src/dothrow.c b/src/dothrow.c index 76c5837b03..d9578b95e1 100644 --- a/src/dothrow.c +++ b/src/dothrow.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 dothrow.c $NHDT-Date: 1683334246 2023/05/06 00:50:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.266 $ */ +/* 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. */ @@ -7,21 +7,23 @@ #include "hack.h" -static boolean valid_throw_target(void); -static int throw_obj(struct obj *, int); -static boolean ok_to_throw(int *); -static int throw_ok(struct obj *); -static void autoquiver(void); -static struct obj *find_launcher(struct obj *); -static int gem_accept(struct monst *, struct obj *); -static boolean toss_up(struct obj *, boolean); -static void sho_obj_return_to_u(struct obj * obj); -static struct obj *return_throw_to_inv(struct obj *, long, boolean, +staticfn boolean valid_throw_target(void); +staticfn int throw_obj(struct obj *, int); +staticfn boolean ok_to_throw(int *); +staticfn int throw_ok(struct obj *); +staticfn void autoquiver(void); +staticfn struct obj *find_launcher(struct obj *); +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 struct obj *return_throw_to_inv(struct obj *, long, boolean, struct obj *); -static void tmiss(struct obj *, struct monst *, boolean); -static int throw_gold(struct obj *); -static void check_shop_obj(struct obj *, coordxy, coordxy, boolean); -static boolean mhurtle_step(genericptr_t, coordxy, coordxy); +staticfn void tmiss(struct obj *, struct monst *, boolean); +staticfn int throw_gold(struct obj *); +staticfn void check_shop_obj(struct obj *, coordxy, coordxy, boolean); +staticfn void breakmsg(struct obj *, boolean); +staticfn boolean mhurtle_step(genericptr_t, coordxy, coordxy); /* uwep might already be removed from inventory so test for W_WEP instead; for Valk+Mjollnir, caller needs to validate the strength requirement */ @@ -66,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 */ @@ -83,7 +86,7 @@ multishot_class_bonus( * something? * Assumes u.dx and u.dy have already been set in the direction of the intended * throw. */ -static boolean +staticfn boolean valid_throw_target(void) { int maxdist = 10; /* arbitrary */ @@ -94,8 +97,8 @@ valid_throw_target(void) for (i = 0; i < maxdist; ++i) { x += u.dx; y += u.dy; - if (!isok(x, y) || IS_ROCK(levl[x][y].typ) || closed_door(x, y) - /* terrain types that are not IS_ROCK but which a thrown item + if (!isok(x, y) || IS_OBSTRUCTED(levl[x][y].typ) || closed_door(x, y) + /* terrain types that are not IS_OBSTRUCTED but which a thrown item * wouldn't fly through - note that missiles can pass iron bars so * they are not checked here */ || levl[x][y].typ == DRAWBRIDGE_UP || IS_WATERWALL(levl[x][y].typ) @@ -112,7 +115,7 @@ valid_throw_target(void) } /* Throw the selected object, asking for direction */ -static int +staticfn int throw_obj(struct obj *obj, int shotlimit) { struct obj *otmp, *oldslot; @@ -121,7 +124,7 @@ throw_obj(struct obj *obj, int shotlimit) long wep_mask; boolean twoweap, weakmultishot, badthrow = FALSE; int res = ECMD_TIME; - struct obj_split save_osplit = gc.context.objsplit; + struct obj_split save_osplit = svc.context.objsplit; /* ask "in what direction?" */ if (!getdir((char *) 0)) { @@ -172,8 +175,9 @@ throw_obj(struct obj *obj, int shotlimit) /* throwing with one hand, but pluralize since the expression "with your bare hands" sounds better */ makeplural(body_part(HAND))); - Sprintf(gk.killer.name, "throwing %s bare-handed", killer_xname(obj)); - instapetrify(gk.killer.name); + Sprintf(svk.killer.name, "throwing %s bare-handed", + killer_xname(obj)); + instapetrify(svk.killer.name); } if (welded(obj)) { weldmsg(obj); @@ -205,6 +209,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) @@ -308,6 +313,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); @@ -349,14 +355,14 @@ throw_obj(struct obj *obj, int shotlimit) || obj->o_id == save_osplit.child_oid)) { /* futureproofing: objsplit will have been affected if partial stack was thrown; objects will have been split off stack to throw. */ - gc.context.objsplit = save_osplit; + svc.context.objsplit = save_osplit; (void) unsplitobj(obj); } return res; } /* common to dothrow() and dofire() */ -static boolean +staticfn boolean ok_to_throw(int *shotlimit_p) /* (see dothrow()) */ { *shotlimit_p = LIMIT_TO_RANGE_INT(0, LARGEST_INT, gc.command_count); @@ -376,7 +382,7 @@ ok_to_throw(int *shotlimit_p) /* (see dothrow()) */ } /* getobj callback for object to be thrown */ -static int +staticfn int throw_ok(struct obj *obj) { if (!obj) @@ -414,7 +420,7 @@ throw_ok(struct obj *obj) int dothrow(void) { - register struct obj *obj; + struct obj *obj; int shotlimit; /* @@ -440,7 +446,7 @@ dothrow(void) /* KMH -- Automatically fill quiver */ /* Suggested by Jeffrey Bay */ -static void +staticfn void autoquiver(void) { struct obj *otmp, *oammo = 0, *omissile = 0, *omisc = 0, *altammo = 0; @@ -505,7 +511,7 @@ autoquiver(void) /* look through hero inventory for launcher matching ammo, avoiding known cursed items. Returns NULL if no match. */ -static struct obj * +staticfn struct obj * find_launcher(struct obj *ammo) { struct obj *otmp, *oX; @@ -536,7 +542,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; /* @@ -566,6 +573,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) { @@ -614,9 +622,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; @@ -647,7 +658,7 @@ void endmultishot(boolean verbose) { if (gm.m_shot.i < gm.m_shot.n) { - if (verbose && !gc.context.mon_moving) { + if (verbose && !svc.context.mon_moving) { You("stop %s after the %d%s %s.", gm.m_shot.s ? "firing" : "throwing", gm.m_shot.i, ordin(gm.m_shot.i), @@ -671,6 +682,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); @@ -692,7 +704,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)) @@ -742,14 +754,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) { @@ -833,7 +846,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."); @@ -845,30 +859,26 @@ 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) - && doorstate(&levl[x][y]) == 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) && doorstate(&levl[x][y]) == D_ISOPEN, + 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."); dmg = rnd(2 + *range); if (Hate_material(IRON)) { @@ -878,43 +888,32 @@ hurtle_step(genericptr_t arg, coordxy x, coordxy y) else { pline("Ouch!"); } - 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)); - /* 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 @@ -948,9 +947,9 @@ hurtle_step(genericptr_t arg, coordxy x, coordxy y) if (touch_petrifies(mon->data) /* this is a bodily collision, so check for body armor */ && !uarmu && !uarm && !uarmc) { - Sprintf(gk.killer.name, "bumping into %s", + Sprintf(svk.killer.name, "bumping into %s", an(pmname(mon->data, NEUTRAL))); - instapetrify(gk.killer.name); + instapetrify(svk.killer.name); } if (touch_petrifies(gy.youmonst.data) && !which_armor(mon, W_ARMU | W_ARM | W_ARMC)) { @@ -991,7 +990,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 @@ -999,8 +998,7 @@ hurtle_step(genericptr_t arg, coordxy x, coordxy y) check_special_room(FALSE); if (is_pool(x, y) && !u.uinwater) { - if ((Is_waterlevel(&u.uz) && is_waterwall(x,y)) - || !(Levitation || Flying || Wwalking)) { + if (is_waterwall(x, y) || !(Levitation || Flying || Wwalking)) { /* couldn't move while hurtling; allow movement now so that drown() will give a chance to crawl out of pool and survive */ gm.multi = 0; @@ -1031,8 +1029,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 || ttmp->ttyp == COLD_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) @@ -1068,7 +1065,7 @@ will_hurtle(struct monst *mon, coordxy x, coordxy y) return goodpos(x, y, mon, MM_IGNOREWATER | MM_IGNORELAVA); } -static boolean +staticfn boolean mhurtle_step(genericptr_t arg, coordxy x, coordxy y) { struct monst *mon = (struct monst *) arg; @@ -1107,18 +1104,18 @@ mhurtle_step(genericptr_t arg, coordxy x, coordxy y) if ((mtmp = m_at(x, y)) != 0 && mtmp != mon) { if (canseemon(mon) || canseemon(mtmp)) pline("%s bumps into %s.", Monnam(mon), a_monnam(mtmp)); - wakeup(mon, !gc.context.mon_moving, FALSE); - wakeup(mtmp, !gc.context.mon_moving, FALSE); + wakeup(mon, !svc.context.mon_moving, FALSE); + wakeup(mtmp, !svc.context.mon_moving, FALSE); /* check whether 'mon' is turned to stone by touching 'mtmp' */ if (touch_petrifies(mtmp->data) && !which_armor(mon, W_ARMU | W_ARM | W_ARMC)) { - minstapetrify(mon, !gc.context.mon_moving); + minstapetrify(mon, !svc.context.mon_moving); newsym(mon->mx, mon->my); } /* and whether 'mtmp' is turned to stone by being touched by 'mon' */ if (touch_petrifies(mon->data) && !which_armor(mtmp, W_ARMU | W_ARM | W_ARMC)) { - minstapetrify(mtmp, !gc.context.mon_moving); + minstapetrify(mtmp, !svc.context.mon_moving); newsym(mtmp->mx, mtmp->my); } } else if (u_at(x, y)) { @@ -1134,12 +1131,13 @@ mhurtle_step(genericptr_t arg, coordxy x, coordxy y) } /* and whether hero is turned to stone by being touched by 'mon' */ if (touch_petrifies(mon->data) && !(uarmu || uarm || uarmc)) { - Snprintf(gk.killer.name, sizeof gk.killer.name, "being hit by %s", + Snprintf(svk.killer.name, sizeof svk.killer.name, + "being hit by %s", /* combine m_monnam() and noname_monnam(): "{your,a} hurtling cockatrice" w/o assigned name */ x_monnam(mon, mon->mtame ? ARTICLE_YOUR : ARTICLE_A, "hurtling", EXACT_NAME | SUPPRESS_NAME, FALSE)); - instapetrify(gk.killer.name); + instapetrify(svk.killer.name); newsym(u.ux, u.uy); } } @@ -1152,7 +1150,7 @@ mhurtle_step(genericptr_t arg, coordxy x, coordxy y) * throwing or kicking something. * * dx and dy should be the direction of the hurtle, not of the original - * kick or throw and be only. + * kick or throw. */ void hurtle(int dx, int dy, int range, boolean verbose) @@ -1173,14 +1171,11 @@ hurtle(int dx, int dy, int range, boolean verbose) return; } else if (u.utrap) { You("are anchored by the %s.", - u.utraptype == TT_WEB - ? "web" - : u.utraptype == TT_LAVA - ? hliquid("lava") - : u.utraptype == TT_INFLOOR - ? surface(u.ux, u.uy) - : u.utraptype == TT_BURIEDBALL ? "buried ball" - : "trap"); + (u.utraptype == TT_WEB) ? "web" + : (u.utraptype == TT_LAVA) ? hliquid("lava") + : (u.utraptype == TT_INFLOOR) ? surface(u.ux, u.uy) + : (u.utraptype == TT_BURIEDBALL) ? "buried ball" + : "trap"); nomul(0); return; } @@ -1196,7 +1191,8 @@ hurtle(int dx, int dy, int range, boolean verbose) gm.multi_reason = "moving through the air"; gn.nomovemsg = ""; /* it just happens */ if (verbose) - You("%s in the opposite direction.", range > 1 ? "hurtle" : "float"); + You("%s in the opposite direction.", + (range > 1) ? "hurtle" : "float"); /* if we're in the midst of shooting multiple projectiles, stop */ endmultishot(TRUE); uc.x = u.ux; @@ -1216,7 +1212,7 @@ mhurtle(struct monst *mon, int dx, int dy, int range) { coord mc, cc; - wakeup(mon, !gc.context.mon_moving, TRUE); + wakeup(mon, !svc.context.mon_moving, TRUE); /* At the very least, debilitate the monster */ mon->movement = 0; mon->mstun = 1; @@ -1262,7 +1258,7 @@ mhurtle(struct monst *mon, int dx, int dy, int range) return; } -static void +staticfn void check_shop_obj(struct obj *obj, coordxy x, coordxy y, boolean broken) { boolean costly_xy; @@ -1337,12 +1333,13 @@ harmless_missile(struct obj *obj) * * Returns FALSE if the object is gone. */ -static boolean +staticfn boolean toss_up(struct obj *obj, boolean hitsroof) { const char *action; int otyp = obj->otyp; boolean petrifier = ((otyp == EGG || otyp == CORPSE) + && ismnum(obj->corpsenm) && touch_petrifies(&mons[obj->corpsenm])); /* note: obj->quan == 1 */ @@ -1352,8 +1349,7 @@ toss_up(struct obj *obj, boolean hitsroof) if (breaktest(obj)) { pline("%s hits the %s.", Doname2(obj), ceiling(u.ux, u.uy)); breakmsg(obj, !Blind); - breakobj(obj, u.ux, u.uy, TRUE, TRUE); - return FALSE; + return breakobj(obj, u.ux, u.uy, TRUE, TRUE) ? FALSE : TRUE; } action = "hits"; } else { @@ -1382,8 +1378,9 @@ toss_up(struct obj *obj, boolean hitsroof) ? rnd(25) : 0; breakmsg(obj, !Blind); - breakobj(obj, u.ux, u.uy, TRUE, TRUE); - obj = 0; /* it's now gone */ + if (breakobj(obj, u.ux, u.uy, TRUE, TRUE)) + obj = 0; /* it's now gone */ + switch (otyp) { case EGG: if (petrifier && !Stone_resistance @@ -1395,6 +1392,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: @@ -1411,13 +1409,17 @@ toss_up(struct obj *obj, boolean hitsroof) default: break; } - return FALSE; + if (!obj) + return FALSE; + /* 'obj' still exists, so drop it and return True */ + hitfloor(obj, FALSE); + gt.thrownobj = 0; } else if (harmless_missile(obj)) { pline("It doesn't hurt."); hitfloor(obj, FALSE); gt.thrownobj = 0; } else { /* neither potion nor other breaking object */ - boolean less_damage = (uarmh && is_hard(uarmh) + boolean less_damage = (hard_helmet(uarmh) && !Hate_material(obj->material)), harmless = (stone_missile(obj) && passes_rocks(gy.youmonst.data)); @@ -1426,8 +1428,8 @@ toss_up(struct obj *obj, boolean hitsroof) if (obj->oartifact && !harmless) /* need a fake die roll here; rn1(18,2) avoids 1 and 20 */ - artimsg = artifact_hit((struct monst *) 0, &gy.youmonst, obj, &dmg, - rn1(18, 2)); + artimsg = artifact_hit((struct monst *) 0, &gy.youmonst, obj, + &dmg, rn1(18, 2)); if (!dmg) { /* probably wasn't a weapon; base damage on weight */ dmg = ((int) obj->owt + 99) / 100; @@ -1471,7 +1473,7 @@ toss_up(struct obj *obj, boolean hitsroof) } /* helmet definitely protects you when it blocks petrification */ } else if (!petrifier) { - if (Verbose(0, toss_up)) + if (flags.verbose) Your("%s does not protect you.", helm_simple_name(uarmh)); } /* stone missile against hero in xorn form would have been @@ -1481,8 +1483,9 @@ toss_up(struct obj *obj, boolean hitsroof) && !(poly_when_stoned(gy.youmonst.data) && polymon(PM_STONE_GOLEM, POLYMON_ALL_MSGS))) { petrify: - gk.killer.format = KILLED_BY; - Strcpy(gk.killer.name, "elementary physics"); /* what goes up... */ + svk.killer.format = KILLED_BY; + /* what goes up... */ + Strcpy(svk.killer.name, "elementary physics"); You("turn to stone."); if (obj) dropy(obj); /* bypass most of hitfloor() */ @@ -1521,7 +1524,7 @@ throwing_weapon(struct obj *obj) } /* the currently thrown object is returning to you (not for boomerangs) */ -static void +staticfn void sho_obj_return_to_u(struct obj *obj) { /* might already be our location (bounced off a wall) */ @@ -1539,6 +1542,14 @@ sho_obj_return_to_u(struct obj *obj) } } +staticfn void +throwit_return(boolean clear_thrownobj) +{ + iflags.returning_missile = (genericptr_t) 0; + if (clear_thrownobj) + gt.thrownobj = (struct obj *) 0; +} + /* throw an object, NB: obj may be consumed in the process */ void throwit(struct obj *obj, @@ -1547,9 +1558,9 @@ throwit(struct obj *obj, * wielded weapon returns */ struct obj *oldslot) /* for thrown-and-return used with !fixinv */ { - register struct monst *mon; + struct monst *mon; int range, urange; - boolean crossbowing, clear_thrownobj = FALSE, + boolean crossbowing, impaired = (Confusion || Stunned || Blind || Hallucination || Fumbling), tethered_weapon = (obj->otyp == AKLYS && (wep_mask & W_WEP) != 0); @@ -1592,7 +1603,7 @@ throwit(struct obj *obj, } gt.thrownobj = obj; - gt.thrownobj->was_thrown = 1; + gt.thrownobj->how_lost = LOST_THROWN; iflags.returning_missile = AutoReturn(obj, wep_mask) ? (genericptr_t) obj : (genericptr_t) 0; /* NOTE: No early returns after this point or returning_missile @@ -1627,8 +1638,8 @@ throwit(struct obj *obj, } else { hitfloor(obj, TRUE); } - clear_thrownobj = TRUE; - goto throwit_return; + throwit_return(TRUE); + return; } else if (obj->otyp == BOOMERANG && !Underwater) { if (Is_airlevel(&u.uz) || Levitation) @@ -1638,8 +1649,8 @@ throwit(struct obj *obj, if (mon == &gy.youmonst) { /* the thing was caught */ exercise(A_DEX, TRUE); obj = return_throw_to_inv(obj, wep_mask, twoweap, oldslot); - clear_thrownobj = TRUE; - goto throwit_return; + throwit_return(TRUE); + return; } } else { /* crossbow range is independent of strength */ @@ -1714,7 +1725,8 @@ throwit(struct obj *obj, we're about to return */ if (tethered_weapon) tmp_at(DISP_END, 0); - goto throwit_return; + throwit_return(FALSE); + return; } } @@ -1722,8 +1734,8 @@ throwit(struct obj *obj, boolean obj_gone; if (mon->isshk && obj->where == OBJ_MINVENT && obj->ocarry == mon) { - clear_thrownobj = TRUE; - goto throwit_return; /* alert shk caught it */ + throwit_return(TRUE); /* alert shk caught it */ + return; } (void) snuff_candle(obj); gn.notonhead = (gb.bhitpos.x != mon->mx || gb.bhitpos.y != mon->my); @@ -1747,11 +1759,12 @@ throwit(struct obj *obj, tmp_at(DISP_END, 0); } else if (u.uswallow && !iflags.returning_missile) { swallowit: - if (obj != uball) + if (obj != uball) { (void) mpickobj(u.ustuck, obj); /* clears 'gt.thrownobj' */ - else - clear_thrownobj = TRUE; - goto throwit_return; + throwit_return(FALSE); + } else + throwit_return(TRUE); + return; } else { /* Mjollnir must be wielded to be thrown--caller verifies this; aklys must be wielded as primary to return when thrown */ @@ -1813,13 +1826,13 @@ throwit(struct obj *obj, if (!ship_object(obj, u.ux, u.uy, FALSE)) dropy(obj); } - clear_thrownobj = TRUE; - goto throwit_return; + throwit_return(TRUE); + return; } else { if (tethered_weapon) tmp_at(DISP_END, 0); /* when this location is stepped on, the weapon will be - auto-picked up due to 'obj->was_thrown' of 1; + auto-picked up due to 'obj->how_lost' of LOST_THROWN; addinv() prevents thrown Mjollnir from being placed into the quiver slot, but an aklys will end up there if that slot is empty at the time; since hero will need to @@ -1842,19 +1855,23 @@ throwit(struct obj *obj, nh_delay_output(); tmp_at(DISP_END, 0); breakmsg(obj, cansee(gb.bhitpos.x, gb.bhitpos.y)); - breakobj(obj, gb.bhitpos.x, gb.bhitpos.y, TRUE, TRUE); - clear_thrownobj = TRUE; - goto throwit_return; + if (breakobj(obj, gb.bhitpos.x, gb.bhitpos.y, TRUE, TRUE)) { + throwit_return(TRUE); + return; + } } if (!Deaf && !Underwater) { /* Some sound effects when item lands in water or lava */ if (is_pool(gb.bhitpos.x, gb.bhitpos.y) - || (is_lava(gb.bhitpos.x, gb.bhitpos.y) && !is_flammable(obj))) + || (is_lava(gb.bhitpos.x, gb.bhitpos.y) + && !is_flammable(obj))) { + Soundeffect(se_splash, 50); pline((weight(obj) > 9) ? "Splash!" : "Plop!"); + } } if (flooreffects(obj, gb.bhitpos.x, gb.bhitpos.y, "fall")) { - clear_thrownobj = TRUE; - goto throwit_return; + throwit_return(TRUE); + return; } obj_no_longer_held(obj); if (mon && mon->isshk && is_pick(obj)) { @@ -1863,22 +1880,24 @@ throwit(struct obj *obj, if (*u.ushops || obj->unpaid) check_shop_obj(obj, gb.bhitpos.x, gb.bhitpos.y, FALSE); (void) mpickobj(mon, obj); /* may merge and free obj */ - clear_thrownobj = TRUE; - goto throwit_return; + throwit_return(TRUE); + return; } (void) snuff_candle(obj); if (!mon && ship_object(obj, gb.bhitpos.x, gb.bhitpos.y, FALSE)) { - clear_thrownobj = TRUE; - goto throwit_return; + throwit_return(TRUE); + return; } gt.thrownobj = (struct obj *) 0; place_object(obj, gb.bhitpos.x, gb.bhitpos.y); /* container contents might break; do so before turning ownership of gt.thrownobj over to shk (container_impact_dmg handles item already owned by shop) */ - if (!IS_SOFT(levl[gb.bhitpos.x][gb.bhitpos.y].typ)) + if (!IS_SOFT(levl[gb.bhitpos.x][gb.bhitpos.y].typ)) { /* is spot where you initiated throw, not gb.bhitpos */ container_impact_dmg(obj, u.ux, u.uy); + impact_disturbs_zombies(obj, TRUE); + } /* charge for items thrown out of shop; shk takes possession for items thrown into one */ if ((*u.ushops || obj->unpaid) && obj != uball) @@ -1893,17 +1912,14 @@ throwit(struct obj *obj, gv.vision_full_recalc = 1; } - throwit_return: - iflags.returning_missile = (genericptr_t) 0; - if (clear_thrownobj) - gt.thrownobj = (struct obj *) 0; + throwit_return(FALSE); return; } /* handle a throw-and-return missile coming back into inventory; makes sure that if it was wielded, it will be re-wielded; if it was split off of a stack (boomerang), don't let it merge with a different compatible stack */ -static struct obj * +staticfn struct obj * return_throw_to_inv( struct obj *obj, /* object to add to invent */ long wep_mask, /* its owornmask before it was removed from invent */ @@ -1914,8 +1930,8 @@ return_throw_to_inv( /* if 'obj' is from a stack split, we can put it back by undoing split so there's no chance of merging with some other compatible stack */ - if (obj->o_id == gc.context.objsplit.parent_oid - || obj->o_id == gc.context.objsplit.child_oid) { + if (obj->o_id == svc.context.objsplit.parent_oid + || obj->o_id == svc.context.objsplit.child_oid) { obj->nobj = gi.invent; gi.invent = obj; obj->where = OBJ_INVENT; @@ -1932,9 +1948,9 @@ return_throw_to_inv( /* if 'obj' wasn't from a stack split or if it wouldn't merge back (maybe new erosion damage?) then it needs to be added to invent; don't merge with any other stack even if there is a compatible one - (others with similar erosion?) */ + (others with similar erosion?); can't use addinv_nomerge() here */ if (!otmp) { - obj->nomerge = 1; + obj->nomerge = 1; /* redundant unless 'oldslot' somehow went away */ obj = addinv_before(obj, oldslot); obj->nomerge = 0; @@ -2001,7 +2017,7 @@ omon_adj(struct monst *mon, struct obj *obj, boolean mon_notices) } /* thrown object misses target monster */ -static void +staticfn void tmiss(struct obj *obj, struct monst *mon, boolean maybe_wakeup) { const char *missile = mshot_xname(obj); @@ -2020,9 +2036,68 @@ tmiss(struct obj *obj, struct monst *mon, boolean maybe_wakeup) return; } -#define quest_arti_hits_leader(obj, mon) \ - (obj->oartifact && is_quest_artifact(obj) \ - && mon->m_id == gq.quest_status.leader_m_id) +#define special_obj_hits_leader(obj, mon) \ + ((is_quest_artifact(obj) || objects[obj->otyp].oc_unique \ + || (obj->otyp == FAKE_AMULET_OF_YENDOR && !obj->known)) \ + && mon->m_id == svq.quest_status.leader_m_id) + +/* whether or not object should be destroyed when it hits its target */ +boolean +should_mulch_missile(struct obj *obj) +{ + boolean broken; + int chance; + schar speadjust; + + /* only ammo (excluding magic stones) or missiles will break */ + if (!obj || !(is_ammo(obj) || is_missile(obj)) + || obj->otyp == BOOMERANG + || objects[obj->otyp].oc_magic) + return FALSE; + + /* The previous version of this logic made sure early-game characters + * couldn't use their ranged ammo much or it'd all break. That isn't what we + * want. So instead, we use a combination of skill and max skill to + * determine the base chance of breakage, then use item enchantment and + * bless/curse for the item's saving throw, as it were. + * Erosion used to be treated here as a penalty to spe, but no longer has + * any effect. + * There is also no longer any effect from Luck. + */ + if (!svc.context.mon_moving) { /* player's ammo */ + chance = (P_SKILL(weapon_type(obj)) + * P_MAX_SKILL(weapon_type(obj)) * 2); + if (chance < 2) { + /* must be restricted; even currently unskilled with ability to + * advance to basic gives 1 * 2 * 2 = 4 */ + chance = 2; /* base 50% chance of breakage */ + } + } + else { /* monster's missile */ + /* assume a monster employing missile attacks is, at minimum, "able to + * get to basic skill" with that missile. It would be possible to put in + * edge cases for certain monster-object combinations that would be + * considered "high-skill" here, but it's probably not that important. + */ + chance = 4; + } + speadjust = obj->spe + (obj->blessed * 2) - (obj->cursed * 2); + if (!rn2(chance)) { + /* Object will break unless positive ench or blessing saves it. */ + broken = (speadjust <= 0) || !rn2(1 + speadjust); + } else { + /* Object survives unless negative ench or curse dooms it. */ + broken = (speadjust < 0) && rn2(1 - speadjust); + } + + /* Flint and hard gems don't break easily */ + if (((obj->oclass == GEM_CLASS && objects[obj->otyp].oc_tough) + || obj->otyp == FLINT) + && !rn2(2)) + broken = FALSE; + + return broken; +} /* * Object thrown by player arrives at monster's location. @@ -2035,8 +2110,8 @@ thitmonst( struct monst *mon, struct obj *obj) /* gt.thrownobj or gk.kickedobj or uwep */ { - register int tmp; /* Base chance to hit */ - register int disttmp; /* distance modifier */ + int tmp; /* Base chance to hit */ + int disttmp; /* distance modifier */ int otyp = obj->otyp, hmode; boolean guaranteed_hit = engulfing_u(mon); int dieroll; @@ -2124,29 +2199,48 @@ thitmonst( /* don't make game unwinnable if naive player throws artifact at leader... (kicked artifact is ok too; HMON_APPLIED could occur if quest artifact polearm or grapnel ever gets added) */ - if (hmode != HMON_APPLIED && quest_arti_hits_leader(obj, mon)) { + if (hmode != HMON_APPLIED && special_obj_hits_leader(obj, mon)) { /* AIS: changes to wakeup() means that it's now less inappropriate here than it used to be, but manual version works just as well */ mon->msleeping = 0; mon->mstrategy &= ~STRAT_WAITMASK; if (mon->mcanmove) { - pline("%s catches %s.", Monnam(mon), the(xname(obj))); - if (mon->mpeaceful) { + pline("%s catches %s.", Some_Monnam(mon), the(xname(obj))); + /* leader will keep tossed invocation item after you've done the + invocation and it's become unnecessary for completion.. */ + if ((u.uevent.invoked && objects[obj->otyp].oc_unique + && obj->otyp != AMULET_OF_YENDOR) + /* ...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 */ + if (mon->mpeaceful && !Deaf) { + /* just in case, identify the object so its name will + appear in the message */ + fully_identify_obj(obj); + verbalize("%s part in this is finished.", + s_suffix(The(xname(obj)))); + verbalize( + "We will guard it in case it is ever needed again, %s forbid.", + align_gname(u.ualignbase[A_ORIGINAL])); + } + if (*u.ushops || obj->unpaid) /* not very likely... */ + check_shop_obj(obj, mon->mx, mon->my, FALSE); + (void) mpickobj(mon, obj); + } else { + /* under normal circumstances, leader will say something and + then return the item to the hero */ boolean next2u = monnear(mon, u.ux, u.uy); leader_sees_qarti(obj); /* maybe acknowledge quest completion */ - pline("%s %s %s back to you.", Monnam(mon), + pline("%s %s %s back to you.", Some_Monnam(mon), (next2u ? "hands" : "tosses"), the(xname(obj))); if (!next2u) sho_obj_return_to_u(obj); obj = addinv(obj); /* back into your inventory */ + nhUse(obj); (void) encumber_msg(); - } else { - /* angry leader caught it and isn't returning it */ - if (*u.ushops || obj->unpaid) /* not very likely... */ - check_shop_obj(obj, mon->mx, mon->my, FALSE); - (void) mpickobj(mon, obj); } return 1; /* caller doesn't need to place it */ } @@ -2230,54 +2324,11 @@ thitmonst( /* projectiles other than magic stones sometimes disappear when thrown; projectiles aren't among the types of weapon that hmon() might have destroyed so obj is intact */ - if (objects[otyp].oc_skill < P_NONE - && objects[otyp].oc_skill > -P_BOOMERANG - && !objects[otyp].oc_magic) { - - /* The previous version of this logic made sure early-game - * characters couldn't use their ranged weapons much or they'd - * all break. That isn't what we want. So instead, we use a - * combination of skill and max skill to determine the base - * chance of breakage, then use item enchantment for the - * item's saving throw, as it were. - */ - boolean broken = FALSE; - int chance = (P_SKILL(weapon_type(obj)) - * P_MAX_SKILL(weapon_type(obj)) * 2); - if (chance < 2) { - /* must be restricted; even currently unskilled with ability - * to advance to basic gives 1 * 2 * 2 = 4 */ - chance = 2; /* base 50% chance of breakage */ - } - schar speadjust = obj->spe - + (obj->blessed * 2) - - (obj->cursed * 2); - if (!rn2(chance)) { - /* Object will break unless positive enchantment saves it. */ - broken = (speadjust <= 0) || !rn2(1 + speadjust); - } else { - /* Object survives unless negative enchantment dooms it. */ - broken = (speadjust < 0) && rn2(1 - speadjust); - } - /* Flint and hard gems get an additional chance because they - * don't break easily. */ - if (((obj->oclass == GEM_CLASS && objects[otyp].oc_tough) - || obj->otyp == FLINT) - && !rn2(2)) { - broken = FALSE; - } - - /* Flint and hard gems don't break easily */ - if (((obj->oclass == GEM_CLASS && objects[otyp].oc_tough) - || obj->otyp == FLINT) && !rn2(2)) - broken = 0; - - if (broken) { - if (*u.ushops || obj->unpaid) - check_shop_obj(obj, gb.bhitpos.x, gb.bhitpos.y, TRUE); - obfree(obj, (struct obj *) 0); - return 1; - } + if (should_mulch_missile(obj)) { + if (*u.ushops || obj->unpaid) + check_shop_obj(obj, gb.bhitpos.x, gb.bhitpos.y, TRUE); + obfree(obj, (struct obj *) 0); + return 1; } passive_obj(mon, obj, (struct attack *) 0); } else { @@ -2322,7 +2373,7 @@ thitmonst( } else if (befriend_with_obj(mon->data, obj) || (mon->mtame && dogfood(mon, obj) <= ACCFOOD)) { - if (tamedog(mon, obj, FALSE)) { + if (tamedog(mon, obj, FALSE, TRUE)) { return 1; /* obj is gone */ } else { tmiss(obj, mon, FALSE); @@ -2359,8 +2410,10 @@ thitmonst( return 0; } -static int -gem_accept(register struct monst *mon, register struct obj *obj) +#undef special_obj_hits_leader + +staticfn int +gem_accept(struct monst *mon, struct obj *obj) { static NEARDATA const char nogood[] = " is not interested in your junk.", @@ -2468,9 +2521,10 @@ gem_accept(register struct monst *mon, register struct obj *obj) * Return 0 if the object didn't break, 1 if the object broke. */ int -hero_breaks(struct obj *obj, - coordxy x, coordxy y, /* object location (ox, oy may not be right) */ - unsigned breakflags) +hero_breaks( + struct obj *obj, + coordxy x, coordxy y, /* object location (ox, oy may not be right) */ + unsigned breakflags) { /* from_invent: thrown or dropped by player; maybe on shop bill; by-hero is implicit so callers don't need to specify BRK_BY_HERO */ @@ -2485,8 +2539,7 @@ hero_breaks(struct obj *obj, return 0; breakmsg(obj, in_view); - breakobj(obj, x, y, TRUE, from_invent); - return 1; + return breakobj(obj, x, y, TRUE, from_invent); } /* @@ -2495,16 +2548,16 @@ hero_breaks(struct obj *obj, * Return 0 if the object doesn't break, 1 if the object broke. */ int -breaks(struct obj *obj, - coordxy x, coordxy y) /* object location (ox, oy may not be right) */ +breaks( + struct obj *obj, + coordxy x, coordxy y) /* object location (ox, oy may not be right) */ { boolean in_view = Blind ? FALSE : cansee(x, y); if (!breaktest(obj)) return 0; breakmsg(obj, in_view); - breakobj(obj, x, y, FALSE, FALSE); - return 1; + return breakobj(obj, x, y, FALSE, FALSE); } void @@ -2524,17 +2577,25 @@ release_camera_demon(struct obj *obj, coordxy x, coordxy y) } /* - * Unconditionally break an object. Assumes all resistance checks + * Break an object. Breakable armor goes through erosion steps; other + * items break unconditionally. Assumes all resistance checks * and break messages have been delivered prior to getting here. + * (No longer true; breakmsg() is silent for crackable armor and we + * call erode_obj() for it and that delivers a damaged-the-item message.) */ -void +int breakobj( struct obj *obj, - coordxy x, coordxy y, /* object location (ox, oy may not be right) */ - boolean hero_caused, /* is this the hero's fault? */ + coordxy x, coordxy y, /* object location (ox, oy may not be right) */ + boolean hero_caused, /* is this the hero's fault? */ boolean from_invent) { boolean fracture = FALSE; + boolean explosion = FALSE; + + if (is_crackable(obj)) /* if erodeproof, erode_obj() will say so */ + return (erode_obj(obj, armor_simple_name(obj), ERODE_CRACK, + EF_DESTROY | EF_VERBOSE) == ER_DESTROYED); switch (obj->oclass == POTION_CLASS ? POT_WATER : obj->otyp) { case MIRROR: @@ -2570,8 +2631,10 @@ breakobj( break; case EGG: /* breaking your own eggs is bad luck */ - if (hero_caused && obj->spe && obj->corpsenm >= LOW_PM) + if (hero_caused && obj->spe && ismnum(obj->corpsenm)) change_luck((schar) -min(obj->quan, 5L)); + if (obj->corpsenm == PM_PYROLISK) + explosion = TRUE; break; case BOULDER: case STATUE: @@ -2612,11 +2675,15 @@ breakobj( } if (!fracture) delobj(obj); + if (explosion) + explode(x, y, -11, d(3, 6), 0, EXPL_FIERY); + return 1; } /* - * Check to see if obj is going to break, but don't actually break it. - * Return 0 if the object isn't going to break, 1 if it is. + * Check to see if obj (which has just hit hard something at speed, e.g. + * thrown or dropped from height) is going to break, but don't actually + * break it. Return 0 if the object isn't going to break, 1 if it is. */ boolean breaktest(struct obj *obj) @@ -2624,15 +2691,16 @@ breaktest(struct obj *obj) int nonbreakchance = 1; /* chance for non-artifacts to resist */ /* this may need to be changed if actual glass armor gets added someday; - for now, it affects crystal plate mail and helm of brilliance */ - if (obj->oclass == ARMOR_CLASS) - nonbreakchance = 95; + for now, it affects crystal plate mail and helm of brilliance; + either of them will have to be cracked 4 times before breaking */ + if (obj->oclass == ARMOR_CLASS && objects[obj->otyp].oc_material == GLASS) + nonbreakchance = 90; if (obj_resists(obj, nonbreakchance, 99)) - return 0; + return FALSE; if (obj->material == GLASS && !obj->oerodeproof && !obj->oartifact && obj->oclass != GEM_CLASS) - return 1; + return TRUE; switch (obj->oclass == POTION_CLASS ? POT_WATER : obj->otyp) { case EXPENSIVE_CAMERA: case POT_WATER: /* really, all potions */ @@ -2641,9 +2709,9 @@ breaktest(struct obj *obj) case MELON: case ACID_VENOM: case BLINDING_VENOM: - return 1; + return TRUE; default: - return 0; + return FALSE; } } @@ -2652,19 +2720,22 @@ breakmsg(struct obj *obj, boolean in_view) { const char *to_pieces; + if (is_crackable(obj)) /* breakobj() will call erode_obj() for message */ + return; + to_pieces = ""; switch (obj->oclass == POTION_CLASS ? POT_WATER : obj->otyp) { default: /* glass or crystal wand */ if (obj->material != GLASS) impossible("breaking odd object?"); + FALLTHROUGH; /*FALLTHRU*/ - case CRYSTAL_PLATE_MAIL: - case HELM_OF_BRILLIANCE: 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) @@ -2701,7 +2772,7 @@ break_glass_obj(struct obj* obj) return FALSE; /* now we are definitely breaking it */ - boolean your_fault = !gc.context.mon_moving; + boolean your_fault = !svc.context.mon_moving; boolean ucarried = carried(obj); /* remove its worn flags */ @@ -2754,18 +2825,18 @@ break_glass_obj(struct obj* obj) return TRUE; } -static int +staticfn int throw_gold(struct obj *obj) { int range, odx, ody; - register struct monst *mon; + struct monst *mon; if (!u.dx && !u.dy && !u.dz) { You("cannot throw gold at yourself."); /* If we tried to throw part of a stack, force it to merge back together (same as in throw_obj). Essential for gold. */ - if (obj->o_id == gc.context.objsplit.parent_oid - || obj->o_id == gc.context.objsplit.child_oid) + if (obj->o_id == svc.context.objsplit.parent_oid + || obj->o_id == svc.context.objsplit.child_oid) (void) unsplitobj(obj); return ECMD_CANCEL; } @@ -2806,7 +2877,8 @@ throw_gold(struct obj *obj) /* see if the gold has a place to move into */ odx = u.ux + u.dx; ody = u.uy + u.dy; - if (!ZAP_POS(levl[odx][ody].typ) || closed_door(odx, ody)) { + if (!isok(odx, ody) + || !ZAP_POS(levl[odx][ody].typ) || closed_door(odx, ody)) { gb.bhitpos.x = u.ux; gb.bhitpos.y = u.uy; } else { @@ -2837,4 +2909,6 @@ throw_gold(struct obj *obj) return ECMD_TIME; } +#undef AutoReturn + /*dothrow.c*/ diff --git a/src/drawing.c b/src/drawing.c index 551880e41f..cadee41317 100644 --- a/src/drawing.c +++ b/src/drawing.c @@ -9,17 +9,12 @@ #include "wintype.h" #include "sym.h" -/* Relevant header information in rm.h, objclass.h, sym.h, defsym.h. */ - -#ifdef C -#undef C -#endif +extern const struct symparse loadsyms[]; +extern const struct class_sym def_oc_syms[MAXOCLASSES]; +extern const struct class_sym def_monsyms[MAXMCLASSES]; +extern const uchar def_r_oc_syms[MAXOCLASSES]; -#ifdef TEXTCOLOR -#define C(n) n -#else -#define C(n) -#endif +/* Relevant header information in rm.h, objclass.h, sym.h, defsym.h. */ /* Default object class symbols. See objclass.h. * {symbol, name, explain} @@ -43,17 +38,17 @@ const struct class_sym def_monsyms[MAXMCLASSES] = { const struct symdef def_warnsyms[WARNCOUNT] = { /* white warning */ - { '0', "unknown creature causing you worry", C(CLR_WHITE) }, + { '0', "unknown creature causing you worry", CLR_WHITE }, /* pink warning */ - { '1', "unknown creature causing you concern", C(CLR_RED) }, + { '1', "unknown creature causing you concern", CLR_RED }, /* red warning */ - { '2', "unknown creature causing you anxiety", C(CLR_RED) }, + { '2', "unknown creature causing you anxiety", CLR_RED }, /* ruby warning */ - { '3', "unknown creature causing you disquiet", C(CLR_RED) }, + { '3', "unknown creature causing you disquiet", CLR_RED }, /* purple warning */ - { '4', "unknown creature causing you alarm", C(CLR_MAGENTA) }, + { '4', "unknown creature causing you alarm", CLR_MAGENTA }, /* black warning */ - { '5', "unknown creature causing you dread", C(CLR_BRIGHT_MAGENTA) }, + { '5', "unknown creature causing you dread", CLR_BRIGHT_MAGENTA }, }; /* @@ -70,15 +65,9 @@ const struct symdef defsyms[MAXPCHARS + 1] = { #define PCHAR_DRAWING #include "defsym.h" #undef PCHAR_DRAWING - { 0, NULL -#ifdef TEXTCOLOR - , NO_COLOR -#endif - } + { 0, NULL, NO_COLOR } }; -#undef C - /* * Convert the given character to an object class. If the character is not * recognized, then MAXOCLASSES is returned. Used in detect.c, invent.c, @@ -97,7 +86,7 @@ def_char_to_objclass(char ch) /* * Convert a character into a monster class. This returns the _first_ - * match made. If there are are no matches, return MAXMCLASSES. + * match made. If there are no matches, return MAXMCLASSES. * Used in detect.c, options.c, read.c, sp_lev.c, and lev_main.c */ int diff --git a/src/dungeon.c b/src/dungeon.c index 22ca20ee84..9d48798977 100644 --- a/src/dungeon.c +++ b/src/dungeon.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 dungeon.c $NHDT-Date: 1612400967 2021/02/04 01:09:27 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.146 $ */ +/* 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. */ @@ -33,51 +33,60 @@ struct lchoice { }; #if 0 -static void Fread(genericptr_t, int, int, dlb *); +staticfn void Fread(genericptr_t, int, int, dlb *); #endif -static xint16 dname_to_dnum(const char *); -static int find_branch(const char *, struct proto_dungeon *); -static xint16 parent_dnum(const char *, struct proto_dungeon *); -static int level_range(xint16, int, int, int, struct proto_dungeon *, - int *); -static xint16 parent_dlevel(const char *, struct proto_dungeon *); -static int correct_branch_type(struct tmpbranch *); -static branch *add_branch(int, int, struct proto_dungeon *); -static void add_level(s_level *); -static void init_level(int, int, struct proto_dungeon *); -static int possible_places(int, boolean *, struct proto_dungeon *); -static xint16 pick_level(boolean *, int); -static boolean place_level(int, struct proto_dungeon *); -static int get_dgn_flags(lua_State *); -static int get_dgn_align(lua_State *); -static void init_dungeon_levels(lua_State *, struct proto_dungeon *, int); -static boolean unplaced_floater(struct dungeon *); -static boolean unreachable_level(d_level *, boolean); -static void tport_menu(winid, char *, struct lchoice *, d_level *, - boolean); -static const char *br_string(int); -static char chr_u_on_lvl(d_level *); -static void print_branch(winid, int, int, int, boolean, struct lchoice *); -static void query_annotation(d_level *); -static mapseen *load_mapseen(NHFILE *); -static void save_mapseen(NHFILE *, mapseen *); -static mapseen *find_mapseen(d_level *); -static mapseen *find_mapseen_by_str(const char *); -static void print_mapseen(winid, mapseen *, int, int, boolean); -static boolean interest_mapseen(mapseen *); -static void traverse_mapseenchn(int, winid, int, int, int *); -static const char *seen_string(xint16, const char *); -static const char *br_string2(branch *); -static const char *shop_string(int); -static char *tunesuffix(mapseen *, char *, size_t); +staticfn xint16 dname_to_dnum(const char *); +staticfn int find_branch(const char *, struct proto_dungeon *); +staticfn xint16 parent_dnum(const char *, struct proto_dungeon *); +staticfn int level_range(xint16, int, int, int, struct proto_dungeon *, + int *); +staticfn xint16 parent_dlevel(const char *, struct proto_dungeon *); +staticfn int correct_branch_type(struct tmpbranch *); +staticfn branch *add_branch(int, int, struct proto_dungeon *); +staticfn void add_level(s_level *); +staticfn void init_level(int, int, struct proto_dungeon *); +staticfn int possible_places(int, boolean *, struct proto_dungeon *); +staticfn xint16 pick_level(boolean *, int); +staticfn boolean place_level(int, struct proto_dungeon *); +staticfn int get_dgn_flags(lua_State *); +staticfn int get_dgn_align(lua_State *); +staticfn void init_dungeon_levels(lua_State *, struct proto_dungeon *, int); +staticfn void init_dungeon_branches(lua_State *, struct proto_dungeon *, int); +staticfn void init_dungeon_set_entry(struct proto_dungeon *, int); +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 boolean init_dungeon_dungeons(lua_State *, struct proto_dungeon *, + int); +staticfn boolean unplaced_floater(struct dungeon *); +staticfn boolean unreachable_level(d_level *, boolean); +staticfn void tport_menu(winid, char *, struct lchoice *, d_level *, boolean); +staticfn const char *br_string(int) NONNULL; +staticfn char chr_u_on_lvl(d_level *); +staticfn void print_branch(winid, int, int, int, boolean, struct lchoice *); +staticfn char *get_annotation(d_level *); +staticfn void query_annotation(d_level *); +staticfn mapseen *load_mapseen(NHFILE *); +staticfn void save_mapseen(NHFILE *, mapseen *); +staticfn mapseen *find_mapseen(d_level *); +staticfn mapseen *find_mapseen_by_str(const char *); +staticfn void print_mapseen(winid, mapseen *, int, int, boolean); +staticfn boolean interest_mapseen(mapseen *); +staticfn void count_feat_lastseentyp(mapseen *, coordxy, coordxy); +staticfn void traverse_mapseenchn(int, winid, int, int, int *); +staticfn const char *seen_string(xint16, const char *) NONNULL NONNULLARG2; +staticfn const char *br_string2(branch *) NONNULL NONNULLARG1; +staticfn const char *shop_string(int) NONNULL; +staticfn char *tunesuffix(mapseen *, char *, size_t) NONNULL NONNULLPTRS; #ifdef DEBUG -#define DD gd.dungeons[i] -static void dumpit(void); +staticfn void dumpit(void); -static void +staticfn void dumpit(void) { +#define DD svd.dungeons[i] int i; s_level *x; branch *br; @@ -85,7 +94,7 @@ dumpit(void) if (!explicitdebug(__FILE__)) return; - for (i = 0; i < gn.n_dgns; i++) { + for (i = 0; i < svn.n_dgns; i++) { fprintf(stderr, "\n#%d \"%s\" (%s):\n", i, DD.dname, DD.proto); fprintf(stderr, " num_dunlevs %d, dunlev_ureached %d\n", DD.num_dunlevs, DD.dunlev_ureached); @@ -97,7 +106,7 @@ dumpit(void) (void) getchar(); } fprintf(stderr, "\nSpecial levels:\n"); - for (x = gs.sp_levchn; x; x = x->next) { + for (x = svs.sp_levchn; x; x = x->next) { fprintf(stderr, "%s (%d): ", x->proto, x->rndlevs); fprintf(stderr, "on %d, %d; ", x->dlevel.dnum, x->dlevel.dlevel); fprintf(stderr, "flags:%s%s%s\n", @@ -107,7 +116,7 @@ dumpit(void) (void) getchar(); } fprintf(stderr, "\nBranches:\n"); - for (br = gb.branches; br; br = br->next) { + for (br = svb.branches; br; br = br->next) { fprintf(stderr, "%d: %s, end1 %d %d, end2 %d %d, %s\n", br->id, br->type == BR_STAIR ? "stair" @@ -124,12 +133,17 @@ dumpit(void) (void) getchar(); fprintf(stderr, "\nDone\n"); (void) getchar(); + return; +#undef DD } #endif /* Save the dungeon structures. */ void -save_dungeon(NHFILE *nhfp, boolean perform_write, boolean free_data) +save_dungeon( + NHFILE *nhfp, + boolean perform_write, + boolean free_data) { int count; branch *curr, *next; @@ -137,48 +151,48 @@ save_dungeon(NHFILE *nhfp, boolean perform_write, boolean free_data) if (perform_write) { if(nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &gn.n_dgns, sizeof gn.n_dgns); - bwrite(nhfp->fd, (genericptr_t) gd.dungeons, - sizeof(dungeon) * (unsigned) gn.n_dgns); - bwrite(nhfp->fd, (genericptr_t) &gd.dungeon_topology, - sizeof gd.dungeon_topology); - bwrite(nhfp->fd, (genericptr_t) gt.tune, sizeof tune); + 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); } - for (count = 0, curr = gb.branches; curr; curr = curr->next) + for (count = 0, curr = svb.branches; curr; curr = curr->next) count++; if (nhfp->structlevel) bwrite(nhfp->fd, (genericptr_t) &count, sizeof count); - for (curr = gb.branches; curr; curr = curr->next) { + for (curr = svb.branches; curr; curr = curr->next) { if (nhfp->structlevel) bwrite(nhfp->fd, (genericptr_t) curr, sizeof *curr); } count = maxledgerno(); if (nhfp->structlevel) { bwrite(nhfp->fd, (genericptr_t) &count, sizeof count); - bwrite(nhfp->fd, (genericptr_t) gl.level_info, + bwrite(nhfp->fd, (genericptr_t) svl.level_info, (unsigned) count * sizeof (struct linfo)); - bwrite(nhfp->fd, (genericptr_t) &gi.inv_pos, sizeof gi.inv_pos); + bwrite(nhfp->fd, (genericptr_t) &svi.inv_pos, sizeof svi.inv_pos); } - for (count = 0, curr_ms = gm.mapseenchn; curr_ms; + 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); - for (curr_ms = gm.mapseenchn; curr_ms; curr_ms = curr_ms->next) { + for (curr_ms = svm.mapseenchn; curr_ms; curr_ms = curr_ms->next) { save_mapseen(nhfp, curr_ms); } } if (free_data) { - for (curr = gb.branches; curr; curr = next) { + for (curr = svb.branches; curr; curr = next) { next = curr->next; free((genericptr_t) curr); } - gb.branches = 0; - for (curr_ms = gm.mapseenchn; curr_ms; curr_ms = next_ms) { + svb.branches = 0; + for (curr_ms = svm.mapseenchn; curr_ms; curr_ms = next_ms) { next_ms = curr_ms->next; if (curr_ms->custom) free((genericptr_t) curr_ms->custom); @@ -186,7 +200,7 @@ save_dungeon(NHFILE *nhfp, boolean perform_write, boolean free_data) savecemetery(nhfp, &curr_ms->final_resting_place); free((genericptr_t) curr_ms); } - gm.mapseenchn = 0; + svm.mapseenchn = 0; } } @@ -199,14 +213,14 @@ restore_dungeon(NHFILE *nhfp) mapseen *curr_ms, *last_ms; if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) &gn.n_dgns, sizeof gn.n_dgns); - mread(nhfp->fd, (genericptr_t) gd.dungeons, - sizeof (dungeon) * (unsigned) gn.n_dgns); - mread(nhfp->fd, (genericptr_t) &gd.dungeon_topology, - sizeof gd.dungeon_topology); - mread(nhfp->fd, (genericptr_t) gt.tune, sizeof tune); + 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); } - last = gb.branches = (branch *) 0; + last = svb.branches = (branch *) 0; if (nhfp->structlevel) mread(nhfp->fd, (genericptr_t) &count, sizeof count); @@ -219,7 +233,7 @@ restore_dungeon(NHFILE *nhfp) if (last) last->next = curr; else - gb.branches = curr; + svb.branches = curr; last = curr; } @@ -230,11 +244,11 @@ restore_dungeon(NHFILE *nhfp) panic("level information count larger (%d) than allocated size", count); if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) gl.level_info, + mread(nhfp->fd, (genericptr_t) svl.level_info, (unsigned) count * sizeof (struct linfo)); if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) &gi.inv_pos, sizeof gi.inv_pos); + mread(nhfp->fd, (genericptr_t) &svi.inv_pos, sizeof svi.inv_pos); mread(nhfp->fd, (genericptr_t) &count, sizeof count); } @@ -245,13 +259,13 @@ restore_dungeon(NHFILE *nhfp) if (last_ms) last_ms->next = curr_ms; else - gm.mapseenchn = curr_ms; + svm.mapseenchn = curr_ms; last_ms = curr_ms; } } #if 0 -static void +staticfn void Fread(genericptr_t ptr, int size, int nitems, dlb *stream) { int cnt; @@ -267,13 +281,13 @@ Fread(genericptr_t ptr, int size, int nitems, dlb *stream) DISABLE_WARNING_UNREACHABLE_CODE -static xint16 +staticfn xint16 dname_to_dnum(const char *s) { xint16 i; - for (i = 0; i < gn.n_dgns; i++) - if (!strcmp(gd.dungeons[i].dname, s)) + for (i = 0; i < svn.n_dgns; i++) + if (!strcmp(svd.dungeons[i].dname, s)) return i; panic("Couldn't resolve dungeon number for name \"%s\".", s); @@ -287,16 +301,17 @@ s_level * find_level(const char *s) { s_level *curr; - for (curr = gs.sp_levchn; curr; curr = curr->next) + for (curr = svs.sp_levchn; curr; curr = curr->next) if (!strcmpi(s, curr->proto)) break; return curr; } /* Find the branch that links the named dungeon. */ -static int -find_branch(const char *s, /* dungeon name */ - struct proto_dungeon *pd) +staticfn int +find_branch( + const char *s, /* dungeon name */ + struct proto_dungeon *pd) { int i; @@ -311,8 +326,8 @@ find_branch(const char *s, /* dungeon name */ branch *br; const char *dnam; - for (br = gb.branches; br; br = br->next) { - dnam = gd.dungeons[br->end2.dnum].dname; + for (br = svb.branches; br; br = br->next) { + dnam = svd.dungeons[br->end2.dnum].dname; if (!strcmpi(dnam, s) || (!strncmpi(dnam, "The ", 4) && !strcmpi(dnam + 4, s))) break; @@ -328,9 +343,10 @@ DISABLE_WARNING_UNREACHABLE_CODE * Find the "parent" by searching the prototype branch list for the branch * listing, then figuring out to which dungeon it belongs. */ -static xint16 -parent_dnum(const char *s, /* dungeon name */ - struct proto_dungeon *pd) +staticfn xint16 +parent_dnum( + const char *s, /* dungeon name */ + struct proto_dungeon *pd) { int i; xint16 pdnum; @@ -361,11 +377,14 @@ RESTORE_WARNING_UNREACHABLE_CODE * a negative random component means from the (adjusted) base to the * end of the dungeon. */ -static int -level_range(xint16 dgn, int base, int randc, int chain, - struct proto_dungeon *pd, int *adjusted_base) +staticfn int +level_range( + xint16 dgn, + int base, int randc, int chain, + struct proto_dungeon *pd, + int *adjusted_base) { - int lmax = gd.dungeons[dgn].num_dunlevs; + int lmax = svd.dungeons[dgn].num_dunlevs; if (chain >= 0) { /* relative to a special level */ s_level *levtmp = pd->final_lev[chain]; @@ -393,7 +412,7 @@ level_range(xint16 dgn, int base, int randc, int chain, return 1; } -static xint16 +staticfn xint16 parent_dlevel(const char *s, struct proto_dungeon *pd) { int i, j, num, base, dnum = parent_dnum(s, pd); @@ -409,7 +428,7 @@ parent_dlevel(const char *s, struct proto_dungeon *pd) do { if (++i >= num) i = 0; - for (curr = gb.branches; curr; curr = curr->next) + for (curr = svb.branches; curr; curr = curr->next) if ((curr->end1.dnum == dnum && curr->end1.dlevel == base + i) || (curr->end2.dnum == dnum && curr->end2.dlevel == base + i)) break; @@ -418,7 +437,7 @@ parent_dlevel(const char *s, struct proto_dungeon *pd) } /* Convert from the temporary branch type to the dungeon branch type. */ -static int +staticfn int correct_branch_type(struct tmpbranch *tbr) { switch (tbr->type) { @@ -448,7 +467,8 @@ insert_branch(branch *new_branch, boolean extract_first) long new_val, curr_val, prev_val; if (extract_first) { - for (prev = 0, curr = gb.branches; curr; prev = curr, curr = curr->next) + for (prev = 0, curr = svb.branches; curr; + prev = curr, curr = curr->next) if (curr == new_branch) break; @@ -457,12 +477,12 @@ insert_branch(branch *new_branch, boolean extract_first) if (prev) prev->next = curr->next; else - gb.branches = curr->next; + svb.branches = curr->next; } new_branch->next = (branch *) 0; /* Convert the branch into a unique number so we can sort them. */ -#define branch_val(bp) \ +#define branch_val(bp) \ ((((long) (bp)->end1.dnum * (MAXLEVEL + 1) + (long) (bp)->end1.dlevel) \ * (MAXDUNGEON + 1) * (MAXLEVEL + 1)) \ + ((long) (bp)->end2.dnum * (MAXLEVEL + 1) + (long) (bp)->end2.dlevel)) @@ -473,7 +493,7 @@ insert_branch(branch *new_branch, boolean extract_first) prev = (branch *) 0; prev_val = -1; new_val = branch_val(new_branch); - for (curr = gb.branches; curr; + for (curr = svb.branches; curr; prev_val = curr_val, prev = curr, curr = curr->next) { curr_val = branch_val(curr); if (prev_val < new_val && new_val <= curr_val) @@ -483,27 +503,31 @@ insert_branch(branch *new_branch, boolean extract_first) new_branch->next = curr; prev->next = new_branch; } else { - new_branch->next = gb.branches; - gb.branches = new_branch; + new_branch->next = svb.branches; + svb.branches = new_branch; } } +#undef branch_val + /* Add a dungeon branch to the branch list. */ -static branch * -add_branch(int dgn, int child_entry_level, struct proto_dungeon *pd) +staticfn branch * +add_branch( + int dgn, int child_entry_level, + struct proto_dungeon *pd) { static int branch_id = 0; int branch_num; branch *new_branch; - branch_num = find_branch(gd.dungeons[dgn].dname, pd); + branch_num = find_branch(svd.dungeons[dgn].dname, pd); new_branch = (branch *) alloc(sizeof(branch)); - (void) memset((genericptr_t)new_branch, 0, sizeof(branch)); + (void) memset((genericptr_t) new_branch, 0, sizeof(branch)); new_branch->next = (branch *) 0; new_branch->id = branch_id++; new_branch->type = correct_branch_type(&pd->tmpbranch[branch_num]); - new_branch->end1.dnum = parent_dnum(gd.dungeons[dgn].dname, pd); - new_branch->end1.dlevel = parent_dlevel(gd.dungeons[dgn].dname, pd); + new_branch->end1.dnum = parent_dnum(svd.dungeons[dgn].dname, pd); + new_branch->end1.dlevel = parent_dlevel(svd.dungeons[dgn].dname, pd); new_branch->end2.dnum = dgn; new_branch->end2.dlevel = child_entry_level; new_branch->end1_up = pd->tmpbranch[branch_num].up ? TRUE : FALSE; @@ -518,28 +542,28 @@ add_branch(int dgn, int child_entry_level, struct proto_dungeon *pd) * level that has a dungeon number less than the dungeon number of the * last entry. */ -static void +staticfn void add_level(s_level *new_lev) { s_level *prev, *curr; prev = (s_level *) 0; - for (curr = gs.sp_levchn; curr; curr = curr->next) { + for (curr = svs.sp_levchn; curr; curr = curr->next) { if (curr->dlevel.dnum == new_lev->dlevel.dnum && curr->dlevel.dlevel > new_lev->dlevel.dlevel) break; prev = curr; } if (!prev) { - new_lev->next = gs.sp_levchn; - gs.sp_levchn = new_lev; + new_lev->next = svs.sp_levchn; + svs.sp_levchn = new_lev; } else { new_lev->next = curr; prev->next = new_lev; } } -static void +staticfn void init_level(int dgn, int proto_index, struct proto_dungeon *pd) { s_level *new_level; @@ -551,7 +575,7 @@ init_level(int dgn, int proto_index, struct proto_dungeon *pd) pd->final_lev[proto_index] = new_level = (s_level *) alloc(sizeof(s_level)); - (void) memset((genericptr_t)new_level, 0, sizeof(s_level)); + (void) memset((genericptr_t) new_level, 0, sizeof(s_level)); /* load new level with data */ Strcpy(new_level->proto, tlevel->name); new_level->boneid = tlevel->boneschar; @@ -570,10 +594,11 @@ init_level(int dgn, int proto_index, struct proto_dungeon *pd) new_level->next = (s_level *) 0; } -static int -possible_places(int idx, /* prototype index */ - boolean *map, /* array MAXLEVEL+1 in length */ - struct proto_dungeon *pd) +staticfn int +possible_places( + int idx, /* prototype index */ + boolean *map, /* array MAXLEVEL+1 in length */ + struct proto_dungeon *pd) { int i, start, count; s_level *lev = pd->final_lev[idx]; @@ -603,9 +628,10 @@ possible_places(int idx, /* prototype index */ DISABLE_WARNING_UNREACHABLE_CODE /* Pick the nth TRUE entry in the given boolean array. */ -static xint16 -pick_level(boolean *map, /* an array MAXLEVEL+1 in size */ - int nth) +staticfn xint16 +pick_level( + boolean *map, /* an array MAXLEVEL+1 in size */ + int nth) { xint16 i; for (i = 1; i <= MAXLEVEL; i++) @@ -619,9 +645,9 @@ pick_level(boolean *map, /* an array MAXLEVEL+1 in size */ RESTORE_WARNING_UNREACHABLE_CODE #ifdef DDEBUG -static void indent(int); +staticfn void indent(int); -static void +staticfn void indent(int d) { while (d-- > 0) @@ -636,7 +662,7 @@ indent(int d) * all possible places have been tried. If all possible places have * been exhausted, return false. */ -static boolean +staticfn boolean place_level(int proto_index, struct proto_dungeon *pd) { boolean map[MAXLEVEL + 1]; /* valid levels are 1..MAXLEVEL inclusive */ @@ -712,16 +738,23 @@ static struct level_map { { X_START, &qstart_level }, { X_LOCATE, &qlocate_level }, { X_GOAL, &nemesis_level }, - { "", (d_level *) 0 } }; + { "", (d_level *) 0 } +}; + +#undef X_START +#undef X_LOCATE +#undef X_GOAL -static int +staticfn int get_dgn_flags(lua_State *L) { int dgn_flags = 0; static const char *const flagstrs[] = { "town", "hellish", "mazelike", "unconnected", NULL }; - static const int flagstrs2i[] = { TOWN, HELLISH, MAZELIKE, UNCONNECTED, 0 }; + static const int flagstrs2i[] = { + TOWN, HELLISH, MAZELIKE, UNCONNECTED, 0 + }; lua_getfield(L, -1, "flags"); if (lua_type(L, -1) == LUA_TTABLE) { @@ -749,7 +782,7 @@ get_dgn_flags(lua_State *L) return dgn_flags; } -static int +staticfn int get_dgn_align(lua_State *L) { static const char *const dgnaligns[] = { @@ -765,8 +798,11 @@ get_dgn_align(lua_State *L) return a; } -static void -init_dungeon_levels(lua_State *L, struct proto_dungeon *pd, int dngidx) +staticfn void +init_dungeon_levels( + lua_State *L, + struct proto_dungeon *pd, + int dngidx) { char *lvl_name, *lvl_bonetag, *lvl_chain; int lvl_base, lvl_range, lvl_nlevels, lvl_chance, @@ -832,17 +868,353 @@ init_dungeon_levels(lua_State *L, struct proto_dungeon *pd, int dngidx) panic("init_dungeon: too many special levels"); } +staticfn void +init_dungeon_branches( + lua_State *L, + struct proto_dungeon *pd, + int dngidx) +{ + static const char *const brdirstr[] = { "up", "down", 0 }; + static const int brdirstr2i[] = { TRUE, FALSE, FALSE }; + static const char *const brtypes[] = { + "stair", "portal", "no_down", "no_up", 0 + }; + static const int brtypes2i[] = { + TBR_STAIR, TBR_PORTAL, TBR_NO_DOWN, TBR_NO_UP, TBR_STAIR + }; + char *br_name, *br_chain; + int br_base, br_range, br_type, br_up; + struct tmpbranch *tmpb; + int bi, f, nbranches; + + lua_len(L, -1); + nbranches = (int) lua_tointeger(L, -1); + pd->tmpdungeon[dngidx].branches = nbranches; + lua_pop(L, 1); + for (f = 0; f < nbranches; f++) { + lua_pushinteger(L, f + 1); + lua_gettable(L, -2); + if (lua_type(L, -1) == LUA_TTABLE) { + br_name = get_table_str(L, "name"); + br_chain = get_table_str_opt(L, "chainlevel", NULL); + br_base = get_table_int(L, "base"); + br_range = get_table_int_opt(L, "range", 0); + br_type = brtypes2i[get_table_option(L, "branchtype", + "stair", brtypes)]; + br_up = brdirstr2i[get_table_option(L, "direction", + "down", brdirstr)]; + tmpb = &(pd->tmpbranch[pd->n_brs + f]); + + debugpline4("BRANCH[%i]:%s,(%i,%i)", + f, br_name, br_base, br_range); + tmpb->name = br_name; + tmpb->lev.base = br_base; + tmpb->lev.rand = br_range; + tmpb->type = br_type; + tmpb->up = br_up; + tmpb->chain = -1; + if (br_chain) { + debugpline1("CHAINBRANCH:%s", br_chain); + for (bi = 0; bi < pd->n_levs + f - 1; bi++) + if (!strcmp(pd->tmplevel[bi].name, br_chain)) { + tmpb->chain = bi; + break; + } + if (tmpb->chain == -1) + panic("Could not chain branch %s to level %s", + br_name, br_chain); + free(br_chain); + } + } else + panic("dungeon[%i].branches[%i] is not a hash", dngidx, f); + lua_pop(L, 1); + } + pd->n_brs += nbranches; + if (pd->n_brs > BRANCH_LIMIT) + panic("init_dungeon: too many branches"); +} + +staticfn void +init_dungeon_set_entry(struct proto_dungeon *pd, int dngidx) +{ + int dgn_entry = pd->tmpdungeon[dngidx].entry_lev; + /* + * Set the entry level for this dungeon. The entry value means: + * < 0 from bottom (-1 == bottom level) + * 0 default (top) + * > 0 actual level (1 = top) + * + * Note that the entry_lev field in the dungeon structure is + * redundant. It is used only here and in print_dungeon(). + */ + if (dgn_entry < 0) { + svd.dungeons[dngidx].entry_lev = + svd.dungeons[dngidx].num_dunlevs + dgn_entry + 1; + if (svd.dungeons[dngidx].entry_lev <= 0) + svd.dungeons[dngidx].entry_lev = 1; + } else if (dgn_entry > 0) { + svd.dungeons[dngidx].entry_lev = dgn_entry; + if (svd.dungeons[dngidx].entry_lev > svd.dungeons[dngidx].num_dunlevs) + svd.dungeons[dngidx].entry_lev = svd.dungeons[dngidx].num_dunlevs; + } else { /* default */ + svd.dungeons[dngidx].entry_lev = 1; /* defaults to top level */ + } +} + +staticfn void +init_dungeon_set_depth(struct proto_dungeon *pd, int dngidx) +{ + branch *br; + schar from_depth; + boolean from_up; + + br = add_branch(dngidx, svd.dungeons[dngidx].entry_lev, pd); + + /* Get the depth of the connecting end. */ + if (br->end1.dnum == dngidx) { + from_depth = depth(&br->end2); + from_up = !br->end1_up; + } else { + from_depth = depth(&br->end1); + from_up = br->end1_up; + } + + /* + * Calculate the depth of the top of the dungeon via + * its branch. First, the depth of the entry point: + * + * depth of branch from "parent" dungeon + * + -1 or 1 depending on an up or down stair or + * 0 if portal + * + * Followed by the depth of the top of the dungeon: + * + * - (entry depth - 1) + * + * We'll say that portals stay on the same depth. + */ + svd.dungeons[dngidx].depth_start = + from_depth + (br->type == BR_PORTAL ? 0 : (from_up ? -1 : 1)) + - (svd.dungeons[dngidx].entry_lev - 1); +} + +staticfn boolean +init_dungeon_dungeons( + lua_State *L, + struct proto_dungeon *pd, + int dngidx) +{ + char *dgn_name, *dgn_bonetag, *dgn_protoname, *dgn_fill; + char *dgn_themerms; + int dgn_base, dgn_range, dgn_align, dgn_entry, dgn_chance, dgn_flags; + + dgn_name = get_table_str(L, "name"); + /* TODO: accept single char or "none" for bonetag */ + dgn_bonetag = get_table_str_opt(L, "bonetag", emptystr); + dgn_protoname = get_table_str_opt(L, "protofile", emptystr); + dgn_base = get_table_int(L, "base"); + dgn_range = get_table_int_opt(L, "range", 0); + dgn_align = get_dgn_align(L); + dgn_entry = get_table_int_opt(L, "entry", 0); + dgn_chance = get_table_int_opt(L, "chance", 100); + dgn_flags = get_dgn_flags(L); + dgn_fill = get_table_str_opt(L, "lvlfill", emptystr); + dgn_themerms = get_table_str_opt(L, "themerooms", emptystr); + + debugpline4("DUNGEON[%i]: %s, base=(%i,%i)", + dngidx, dgn_name, dgn_base, dgn_range); + + if (!wizard && dgn_chance && (dgn_chance <= rn2(100))) { + debugpline1("IGNORING %s", dgn_name); + svn.n_dgns--; + free((genericptr_t) dgn_name); + free((genericptr_t) dgn_bonetag); + free((genericptr_t) dgn_protoname); + free((genericptr_t) dgn_fill); + free((genericptr_t) dgn_themerms); + return FALSE; + } + + /* levels begin */ + lua_getfield(L, -1, "levels"); + if (lua_type(L, -1) == LUA_TTABLE) { + init_dungeon_levels(L, pd, dngidx); + } else if (lua_type(L, -1) != LUA_TNIL) + panic("dungeon[%i].levels is not an array of hashes", dngidx); + lua_pop(L, 1); + /* levels end */ + + /* branches begin */ + lua_getfield(L, -1, "branches"); + if (lua_type(L, -1) == LUA_TTABLE) { + init_dungeon_branches(L, pd, dngidx); + } else if (lua_type(L, -1) != LUA_TNIL) + panic("dungeon[%i].branches is not an array of hashes", dngidx); + lua_pop(L, 1); + /* branches end */ + + pd->tmpdungeon[dngidx].name = dgn_name; + pd->tmpdungeon[dngidx].protoname = dgn_protoname; + pd->tmpdungeon[dngidx].boneschar = *dgn_bonetag ? *dgn_bonetag : 0; + pd->tmpdungeon[dngidx].lev.base = dgn_base; + pd->tmpdungeon[dngidx].lev.rand = dgn_range; + pd->tmpdungeon[dngidx].flags = dgn_flags; + pd->tmpdungeon[dngidx].align = dgn_align; + pd->tmpdungeon[dngidx].chance = dgn_chance; + pd->tmpdungeon[dngidx].entry_lev = dgn_entry; + + /* FIXME: these should have length checks */ + Strcpy(svd.dungeons[dngidx].fill_lvl, dgn_fill); + Strcpy(svd.dungeons[dngidx].dname, dgn_name); + Strcpy(svd.dungeons[dngidx].proto, dgn_protoname); + Strcpy(svd.dungeons[dngidx].themerms, dgn_themerms); + /* FIXME: accept "none", convert that to '\0' */ + svd.dungeons[dngidx].boneid = *dgn_bonetag ? *dgn_bonetag : 0; + free((genericptr) dgn_fill); + /* free((genericptr) dgn_protoname); -- stored in pd.tmpdungeon[] */ + free((genericptr) dgn_bonetag); + free((genericptr) dgn_themerms); + + if (dgn_range) + svd.dungeons[dngidx].num_dunlevs = (xint16) rn1(dgn_range, dgn_base); + else + svd.dungeons[dngidx].num_dunlevs = (xint16) dgn_base; + + if (!dngidx) { + svd.dungeons[dngidx].ledger_start = 0; + svd.dungeons[dngidx].depth_start = 1; + svd.dungeons[dngidx].dunlev_ureached = 1; + } else { + svd.dungeons[dngidx].ledger_start + = svd.dungeons[dngidx - 1].ledger_start + + svd.dungeons[dngidx - 1].num_dunlevs; + svd.dungeons[dngidx].dunlev_ureached = 0; + } + + svd.dungeons[dngidx].flags.hellish = !!(dgn_flags & HELLISH); + svd.dungeons[dngidx].flags.maze_like = !!(dgn_flags & MAZELIKE); + svd.dungeons[dngidx].flags.align = dgn_align; + svd.dungeons[dngidx].flags.unconnected = !!(dgn_flags & UNCONNECTED); + + init_dungeon_set_entry(pd, dngidx); + + if (svd.dungeons[dngidx].flags.unconnected) { + svd.dungeons[dngidx].depth_start = 1; + } else if (dngidx) { /* set depth */ + init_dungeon_set_depth(pd, dngidx); + } + + if (svd.dungeons[dngidx].num_dunlevs > MAXLEVEL) + svd.dungeons[dngidx].num_dunlevs = MAXLEVEL; + + return TRUE; +} + +/* initialize the Castle drawbridge tune */ +staticfn void +init_castle_tune(void) +{ + int i; + + for (i = 0; i < 5; i++) + svt.tune[i] = 'A' + rn2(7); + svt.tune[5] = 0; +} + +/* fix up the special level names and locations for quick access */ +staticfn void +fixup_level_locations(void) +{ + int i; + s_level *x; + struct level_map *lev_map; + + /* + * Find most of the special levels and dungeons so we can access their + * locations quickly. + */ + for (lev_map = level_map; lev_map->lev_name[0]; lev_map++) { + x = find_level(lev_map->lev_name); + if (x) { + assign_level(lev_map->lev_spec, &x->dlevel); + if (!strncmp(lev_map->lev_name, "x-", 2)) { + /* This is where the name substitution on the + * levels of the quest dungeon occur. + */ + Sprintf(x->proto, "%s%s", gu.urole.filecode, + &lev_map->lev_name[1]); + } else if (lev_map->lev_spec == &knox_level) { + branch *br; + /* + * Kludge to allow floating Knox entrance. We + * specify a floating entrance by the fact that its + * entrance (end1) has a bogus dnum, namely n_dgns. + */ + for (br = svb.branches; br; br = br->next) + if (on_level(&br->end2, &knox_level)) + break; + + if (br) { + br->end1.dnum = svn.n_dgns; + /* adjust the branch's position on the list */ + insert_branch(br, TRUE); + } + } + } + } + /* + * I hate hardwiring these names. :-( + */ + quest_dnum = dname_to_dnum("The Quest"); + sokoban_dnum = dname_to_dnum("Sokoban"); + mines_dnum = dname_to_dnum("The Gnomish Mines"); + tower_dnum = dname_to_dnum("Vlad's Tower"); + gehennom_dnum = dname_to_dnum("Gehennom"); + abyss_dnum = dname_to_dnum("The Abyss"); + tutorial_dnum = dname_to_dnum("The Tutorial"); + + /* one special fixup for dummy surface level */ + if ((x = find_level("dummy")) != 0) { + i = x->dlevel.dnum; + /* the code above puts earth one level above dungeon level #1, + making the dummy level overlay level 1; but the whole reason + for having the dummy level is to make earth have depth -1 + instead of 0, so adjust the start point to shift endgame up */ + if (dunlevs_in_dungeon(&x->dlevel) > 1 - svd.dungeons[i].depth_start) + svd.dungeons[i].depth_start -= 1; + /* TODO: strip "dummy" out all the way here, + so that it's hidden from '#wizwhere' feedback. */ + } +} + +staticfn void +free_proto_dungeon(struct proto_dungeon *pd) +{ + int i; + + for (i = 0; i < pd->n_brs; i++) { + free((genericptr_t) pd->tmpbranch[i].name); + } + for (i = 0; i < pd->n_levs; i++) { + free((genericptr_t) pd->tmplevel[i].name); + if (pd->tmplevel[i].chainlvl) + free((genericptr_t) pd->tmplevel[i].chainlvl); + } + for (i = 0; i < svn.n_dgns; i++) { + free((genericptr_t) pd->tmpdungeon[i].name); + free((genericptr_t) pd->tmpdungeon[i].protoname); + } +} + /* initialize the "dungeon" structs */ void init_dungeons(void) { lua_State *L; - register int i, cl = 0; - register s_level *x; + int i, cl = 0; struct proto_dungeon pd; - struct level_map *lev_map; int tidx; - nhl_sandbox_info sbi = {NHL_SB_SAFE, 0, 0, 0}; + nhl_sandbox_info sbi = {NHL_SB_SAFE, 1*1024*1024, 0, 1*1024*1024}; (void) memset(&pd, 0, sizeof (struct proto_dungeon)); pd.n_levs = pd.n_brs = 0; @@ -881,7 +1253,7 @@ init_dungeons(void) if (iflags.window_inited) clear_nhwindow(WIN_MAP); - gs.sp_levchn = (s_level *) 0; + svs.sp_levchn = (s_level *) 0; lua_settop(L, 0); @@ -890,7 +1262,7 @@ init_dungeons(void) panic("dungeon is not a lua table"); lua_len(L, -1); - gn.n_dgns = (int) lua_tointeger(L, -1); + svn.n_dgns = (int) lua_tointeger(L, -1); lua_pop(L, 1); pd.start = 0; @@ -902,7 +1274,7 @@ init_dungeons(void) * dungeon arrays. */ - if (gn.n_dgns >= MAXDUNGEON) + if (svn.n_dgns >= MAXDUNGEON) panic("init_dungeons: too many dungeons"); tidx = lua_gettop(L); @@ -910,327 +1282,50 @@ init_dungeons(void) lua_pushnil(L); /* first key */ i = 0; while (lua_next(L, tidx) != 0) { - char *dgn_name, *dgn_bonetag, *dgn_protoname, *dgn_fill; - char *dgn_themerms; - int dgn_base, dgn_range, dgn_align, dgn_entry, dgn_chance, dgn_flags; if (!lua_istable(L, -1)) panic("dungeon[%i] is not a lua table", i); - dgn_name = get_table_str(L, "name"); - dgn_bonetag = get_table_str_opt(L, "bonetag", emptystr); /* TODO: single char or "none" */ - dgn_protoname = get_table_str_opt(L, "protofile", emptystr); - dgn_base = get_table_int(L, "base"); - dgn_range = get_table_int_opt(L, "range", 0); - dgn_align = get_dgn_align(L); - dgn_entry = get_table_int_opt(L, "entry", 0); - dgn_chance = get_table_int_opt(L, "chance", 100); - dgn_flags = get_dgn_flags(L); - dgn_fill = get_table_str_opt(L, "lvlfill", emptystr); - dgn_themerms = get_table_str_opt(L, "themerooms", emptystr); - - debugpline4("DUNGEON[%i]: %s, base=(%i,%i)", - i, dgn_name, dgn_base, dgn_range); - - if (!wizard && dgn_chance && (dgn_chance <= rn2(100))) { - debugpline1("IGNORING %s", dgn_name); - gn.n_dgns--; - lua_pop(L, 1); /* pop the dungeon table */ - free((genericptr_t) dgn_name); - free((genericptr_t) dgn_bonetag); - free((genericptr_t) dgn_protoname); - free((genericptr_t) dgn_fill); - free((genericptr_t) dgn_themerms); - continue; - } - - /* levels begin */ - lua_getfield(L, -1, "levels"); - if (lua_type(L, -1) == LUA_TTABLE) { - init_dungeon_levels(L, &pd, i); - } else if (lua_type(L, -1) != LUA_TNIL) - panic("dungeon[%i].levels is not an array of hashes", i); - lua_pop(L, 1); - /* levels end */ - - /* branches begin */ - lua_getfield(L, -1, "branches"); - if (lua_type(L, -1) == LUA_TTABLE) { - static const char *const brdirstr[] = { "up", "down", 0 }; - static const int brdirstr2i[] = { TRUE, FALSE, FALSE }; - static const char *const brtypes[] = { - "stair", "portal", "no_down", "no_up", 0 - }; - static const int brtypes2i[] = { - TBR_STAIR, TBR_PORTAL, TBR_NO_DOWN, TBR_NO_UP, TBR_STAIR - }; - char *br_name, *br_chain; - int br_base, br_range, br_type, br_up; - struct tmpbranch *tmpb; - int bi, f, nbranches; - - lua_len(L, -1); - nbranches = (int) lua_tointeger(L, -1); - pd.tmpdungeon[i].branches = nbranches; - lua_pop(L, 1); - for (f = 0; f < nbranches; f++) { - lua_pushinteger(L, f + 1); - lua_gettable(L, -2); - if (lua_type(L, -1) == LUA_TTABLE) { - br_name = get_table_str(L, "name"); - br_chain = get_table_str_opt(L, "chainlevel", NULL); - br_base = get_table_int(L, "base"); - br_range = get_table_int_opt(L, "range", 0); - br_type = brtypes2i[get_table_option(L, "branchtype", - "stair", brtypes)]; - br_up = brdirstr2i[get_table_option(L, "direction", - "down", brdirstr)]; - tmpb = &pd.tmpbranch[pd.n_brs + f]; - - debugpline4("BRANCH[%i]:%s,(%i,%i)", - f, br_name, br_base, br_range); - tmpb->name = br_name; - tmpb->lev.base = br_base; - tmpb->lev.rand = br_range; - tmpb->type = br_type; - tmpb->up = br_up; - tmpb->chain = -1; - if (br_chain) { - debugpline1("CHAINBRANCH:%s", br_chain); - for (bi = 0; bi < pd.n_levs + f - 1; bi++) - if (!strcmp(pd.tmplevel[bi].name, br_chain)) { - tmpb->chain = bi; - break; - } - if (tmpb->chain == -1) - panic("Could not chain branch %s to level %s", - br_name, br_chain); - free(br_chain); - } - } else - panic("dungeon[%i].branches[%i] is not a hash", i, f); - lua_pop(L, 1); + if (init_dungeon_dungeons(L, &pd, i)) { + for (; cl < pd.n_levs; cl++) { + init_level(i, cl, &pd); } - pd.n_brs += nbranches; - if (pd.n_brs > BRANCH_LIMIT) - panic("init_dungeon: too many branches"); - } else if (lua_type(L, -1) != LUA_TNIL) - panic("dungeon[%i].branches is not an array of hashes", i); - lua_pop(L, 1); - /* branches end */ - - pd.tmpdungeon[i].name = dgn_name; - pd.tmpdungeon[i].protoname = dgn_protoname; - pd.tmpdungeon[i].boneschar = *dgn_bonetag ? *dgn_bonetag : 0; - pd.tmpdungeon[i].lev.base = dgn_base; - pd.tmpdungeon[i].lev.rand = dgn_range; - pd.tmpdungeon[i].flags = dgn_flags; - pd.tmpdungeon[i].align = dgn_align; - pd.tmpdungeon[i].chance = dgn_chance; - pd.tmpdungeon[i].entry_lev = dgn_entry; - - Strcpy(gd.dungeons[i].fill_lvl, dgn_fill); /* FIXME: fill_lvl len */ - Strcpy(gd.dungeons[i].dname, dgn_name); /* FIXME: dname length */ - Strcpy(gd.dungeons[i].proto, dgn_protoname); /* FIXME: proto length */ - Strcpy(gd.dungeons[i].themerms, dgn_themerms); /* FIXME: length */ - gd.dungeons[i].boneid = *dgn_bonetag ? *dgn_bonetag : 0; - free((genericptr) dgn_fill); - /* free((genericptr) dgn_protoname); -- stored in pd.tmpdungeon[] */ - free((genericptr) dgn_bonetag); - free((genericptr) dgn_themerms); - - if (dgn_range) - gd.dungeons[i].num_dunlevs = (xint16) rn1(dgn_range, dgn_base); - else - gd.dungeons[i].num_dunlevs = (xint16) dgn_base; - - if (!i) { - gd.dungeons[i].ledger_start = 0; - gd.dungeons[i].depth_start = 1; - gd.dungeons[i].dunlev_ureached = 1; - } else { - gd.dungeons[i].ledger_start = - gd.dungeons[i - 1].ledger_start + gd.dungeons[i - 1].num_dunlevs; - gd.dungeons[i].dunlev_ureached = 0; - } - - gd.dungeons[i].flags.hellish = !!(dgn_flags & HELLISH); - gd.dungeons[i].flags.maze_like = !!(dgn_flags & MAZELIKE); - gd.dungeons[i].flags.align = dgn_align; - gd.dungeons[i].flags.unconnected = !!(dgn_flags & UNCONNECTED); - - /* - * Set the entry level for this dungeon. The entry value means: - * < 0 from bottom (-1 == bottom level) - * 0 default (top) - * > 0 actual level (1 = top) - * - * Note that the entry_lev field in the dungeon structure is - * redundant. It is used only here and in print_dungeon(). - */ - if (dgn_entry < 0) { - gd.dungeons[i].entry_lev = - gd.dungeons[i].num_dunlevs + dgn_entry + 1; - if (gd.dungeons[i].entry_lev <= 0) - gd.dungeons[i].entry_lev = 1; - } else if (dgn_entry > 0) { - gd.dungeons[i].entry_lev = dgn_entry; - if (gd.dungeons[i].entry_lev > gd.dungeons[i].num_dunlevs) - gd.dungeons[i].entry_lev = gd.dungeons[i].num_dunlevs; - } else { /* default */ - gd.dungeons[i].entry_lev = 1; /* defaults to top level */ - } - - if (gd.dungeons[i].flags.unconnected) { - gd.dungeons[i].depth_start = 1; - } else if (i) { /* set depth */ - branch *br; - schar from_depth; - boolean from_up; - - br = add_branch(i, gd.dungeons[i].entry_lev, &pd); - - /* Get the depth of the connecting end. */ - if (br->end1.dnum == i) { - from_depth = depth(&br->end2); - from_up = !br->end1_up; - } else { - from_depth = depth(&br->end1); - from_up = br->end1_up; - } - /* - * Calculate the depth of the top of the dungeon via - * its branch. First, the depth of the entry point: - * - * depth of branch from "parent" dungeon - * + -1 or 1 depending on an up or down stair or - * 0 if portal - * - * Followed by the depth of the top of the dungeon: - * - * - (entry depth - 1) - * - * We'll say that portals stay on the same depth. + * Recursively place the generated levels for this dungeon. This + * routine will attempt all possible combinations before giving + * up. */ - gd.dungeons[i].depth_start = - from_depth + (br->type == BR_PORTAL ? 0 : (from_up ? -1 : 1)) - - (gd.dungeons[i].entry_lev - 1); - } - - if (gd.dungeons[i].num_dunlevs > MAXLEVEL) - gd.dungeons[i].num_dunlevs = MAXLEVEL; - - - for (; cl < pd.n_levs; cl++) { - init_level(i, cl, &pd); - } - - /* - * Recursively place the generated levels for this dungeon. This - * routine will attempt all possible combinations before giving - * up. - */ - if (!place_level(pd.start, &pd)) - panic("init_dungeon: couldn't place levels"); + if (!place_level(pd.start, &pd)) + panic("init_dungeon: couldn't place levels"); #ifdef DDEBUG - fprintf(stderr, "--- end of dungeon %d ---\n", i); - fflush(stderr); - getchar(); + fprintf(stderr, "--- end of dungeon %d ---\n", i); + fflush(stderr); + getchar(); #endif - - for (; pd.start < pd.n_levs; pd.start++) - if (pd.final_lev[pd.start]) - add_level(pd.final_lev[pd.start]); - /* levels handling end */ - + for (; pd.start < pd.n_levs; pd.start++) + if (pd.final_lev[pd.start]) + add_level(pd.final_lev[pd.start]); + /* levels handling end */ + i++; + } lua_pop(L, 1); /* pop the dungeon table */ - i++; } lua_pop(L, 1); /* get rid of the dungeon global */ debugpline2("init_dungeon lua DONE (n_levs=%i, n_brs=%i)", pd.n_levs, pd.n_brs); - for (i = 0; i < 5; i++) - gt.tune[i] = 'A' + rn2(7); - gt.tune[5] = 0; - - /* - * Find most of the special levels and dungeons so we can access their - * locations quickly. - */ - for (lev_map = level_map; lev_map->lev_name[0]; lev_map++) { - x = find_level(lev_map->lev_name); - if (x) { - assign_level(lev_map->lev_spec, &x->dlevel); - if (!strncmp(lev_map->lev_name, "x-", 2)) { - /* This is where the name substitution on the - * levels of the quest dungeon occur. - */ - Sprintf(x->proto, "%s%s", gu.urole.filecode, - &lev_map->lev_name[1]); - } else if (lev_map->lev_spec == &knox_level) { - branch *br; - /* - * Kludge to allow floating Knox entrance. We - * specify a floating entrance by the fact that its - * entrance (end1) has a bogus dnum, namely n_dgns. - */ - for (br = gb.branches; br; br = br->next) - if (on_level(&br->end2, &knox_level)) - break; - - if (br) - br->end1.dnum = gn.n_dgns; - /* adjust the branch's position on the list */ - insert_branch(br, TRUE); - } - } - } - /* - * I hate hardwiring these names. :-( - */ - quest_dnum = dname_to_dnum("The Quest"); - sokoban_dnum = dname_to_dnum("Sokoban"); - mines_dnum = dname_to_dnum("The Gnomish Mines"); - tower_dnum = dname_to_dnum("Vlad's Tower"); - gehennom_dnum = dname_to_dnum("Gehennom"); - abyss_dnum = dname_to_dnum("The Abyss"); - - /* one special fixup for dummy surface level */ - if ((x = find_level("dummy")) != 0) { - i = x->dlevel.dnum; - /* the code above puts earth one level above dungeon level #1, - making the dummy level overlay level 1; but the whole reason - for having the dummy level is to make earth have depth -1 - instead of 0, so adjust the start point to shift endgame up */ - if (dunlevs_in_dungeon(&x->dlevel) > 1 - gd.dungeons[i].depth_start) - gd.dungeons[i].depth_start -= 1; - /* TODO: strip "dummy" out all the way here, - so that it's hidden from '#wizwhere' feedback. */ - } - + init_castle_tune(); + fixup_level_locations(); nhl_done(L); - - for (i = 0; i < pd.n_brs; i++) { - free((genericptr_t) pd.tmpbranch[i].name); - } - for (i = 0; i < pd.n_levs; i++) { - free((genericptr_t) pd.tmplevel[i].name); - if (pd.tmplevel[i].chainlvl) - free((genericptr_t) pd.tmplevel[i].chainlvl); - } - for (i = 0; i < gn.n_dgns; i++) { - free((genericptr_t) pd.tmpdungeon[i].name); - free((genericptr_t) pd.tmpdungeon[i].protoname); - } - + free_proto_dungeon(&pd); #ifdef DEBUG dumpit(); #endif } +#undef DUNGEON_FILE + /* return the level number for lev in *this* dungeon */ xint16 dunlev(d_level *lev) @@ -1242,7 +1337,7 @@ dunlev(d_level *lev) xint16 dunlevs_in_dungeon(d_level *lev) { - return gd.dungeons[lev->dnum].num_dunlevs; + return svd.dungeons[lev->dnum].num_dunlevs; } /* return the lowest level explored in the game*/ @@ -1264,14 +1359,14 @@ deepest_lev_reached(boolean noquest) * calculation. _However_ the Quest is a difficult dungeon, so we * include it in the factor of difficulty calculations. */ - register int i; + int i; d_level tmp; xint16 ret = 0; - for (i = 0; i < gn.n_dgns; i++) { + for (i = 0; i < svn.n_dgns; i++) { if (noquest && i == quest_dnum) continue; - tmp.dlevel = gd.dungeons[i].dunlev_ureached; + tmp.dlevel = svd.dungeons[i].dunlev_ureached; if (tmp.dlevel == 0) continue; tmp.dnum = i; @@ -1286,12 +1381,12 @@ deepest_lev_reached(boolean noquest) xint16 ledger_no(d_level *lev) { - return (xint16) (lev->dlevel + gd.dungeons[lev->dnum].ledger_start); + return (xint16) (lev->dlevel + svd.dungeons[lev->dnum].ledger_start); } /* * The last level in the bookkeeping list of level is the bottom of the last - * dungeon in the gd.dungeons[] array. + * dungeon in the svd.dungeons[] array. * * Maxledgerno() -- which is the max number of levels in the bookkeeping * list, should not be confused with dunlevs_in_dungeon(lev) -- which @@ -1302,8 +1397,8 @@ ledger_no(d_level *lev) xint16 maxledgerno(void) { - return (xint16) (gd.dungeons[gn.n_dgns - 1].ledger_start - + gd.dungeons[gn.n_dgns - 1].num_dunlevs); + return (xint16) (svd.dungeons[svn.n_dgns - 1].ledger_start + + svd.dungeons[svn.n_dgns - 1].num_dunlevs); } DISABLE_WARNING_UNREACHABLE_CODE @@ -1315,10 +1410,10 @@ ledger_to_dnum(xint16 ledgerno) xint16 i; /* find i such that (i->base + 1) <= ledgerno <= (i->base + i->count) */ - for (i = 0; i < gn.n_dgns; i++) - if (gd.dungeons[i].ledger_start < ledgerno - && (ledgerno - <= gd.dungeons[i].ledger_start + gd.dungeons[i].num_dunlevs)) + for (i = 0; i < svn.n_dgns; i++) + if (svd.dungeons[i].ledger_start < ledgerno + && ledgerno <= (svd.dungeons[i].ledger_start + + svd.dungeons[i].num_dunlevs)) return i; panic("level number out of range [ledger_to_dnum(%d)]", (int) ledgerno); @@ -1333,7 +1428,7 @@ xint16 ledger_to_dlev(xint16 ledgerno) { return (xint16) (ledgerno - - gd.dungeons[ledger_to_dnum(ledgerno)].ledger_start); + - svd.dungeons[ledger_to_dnum(ledgerno)].ledger_start); } /* returns the depth of a level, in floors below the surface @@ -1341,7 +1436,7 @@ ledger_to_dlev(xint16 ledgerno) schar depth(d_level *lev) { - return (schar) (gd.dungeons[lev->dnum].depth_start + lev->dlevel - 1); + return (schar) (svd.dungeons[lev->dnum].depth_start + lev->dlevel - 1); } /* are "lev1" and "lev2" actually the same? */ @@ -1358,7 +1453,7 @@ Is_special(d_level *lev) { s_level *levtmp; - for (levtmp = gs.sp_levchn; levtmp; levtmp = levtmp->next) + for (levtmp = svs.sp_levchn; levtmp; levtmp = levtmp->next) if (on_level(lev, &levtmp->dlevel)) return levtmp; @@ -1374,7 +1469,7 @@ Is_branchlev(d_level *lev) { branch *curr; - for (curr = gb.branches; curr; curr = curr->next) { + for (curr = svb.branches; curr; curr = curr->next) { if (on_level(lev, &curr->end1) || on_level(lev, &curr->end2)) return curr; } @@ -1385,14 +1480,14 @@ Is_branchlev(d_level *lev) boolean builds_up(d_level *lev) { - dungeon *dptr = &gd.dungeons[lev->dnum]; + dungeon *dptr = &svd.dungeons[lev->dnum]; branch *br; if (dptr->num_dunlevs > 1) return (boolean) (dptr->entry_lev == dptr->num_dunlevs); - /* else, single-level branch; find the branch connection that connects this + /* else, single-level branch; find branch connection that connects this * dungeon from a parent dungeon and determine whether it builds up from * that */ - for (br = gb.branches; br; br = br->next) { + for (br = svb.branches; br; br = br->next) { if (on_level(lev, &br->end2)) { return br->end1_up; } @@ -1523,7 +1618,7 @@ u_on_newpos(coordxy x, coordxy y) stale values from previous level */ if (!on_level(&u.uz, &u.uz0)) u.ux0 = u.ux, u.uy0 = u.uy; - else if (!Blind && !Hallucination) + else if (!Blind && !Hallucination && !u.uswallow) /* still on same level; might have come close enough to generic object(s) to redisplay them as specific objects */ see_nearby_objects(); @@ -1539,195 +1634,32 @@ u_on_rndspot(int upflag) * Unspecified region (.lx == 0) defaults to entire level. */ if (upflag) - place_lregion(gu.updest.lx, gu.updest.ly, gu.updest.hx, gu.updest.hy, - gu.updest.nlx, gu.updest.nly, gu.updest.nhx, gu.updest.nhy, + place_lregion(svu.updest.lx, svu.updest.ly, + svu.updest.hx, svu.updest.hy, + svu.updest.nlx, svu.updest.nly, + svu.updest.nhx, svu.updest.nhy, LR_UPTELE, (d_level *) 0); else - place_lregion(gd.dndest.lx, gd.dndest.ly, gd.dndest.hx, gd.dndest.hy, - gd.dndest.nlx, gd.dndest.nly, gd.dndest.nhx, gd.dndest.nhy, + place_lregion(svd.dndest.lx, svd.dndest.ly, + svd.dndest.hx, svd.dndest.hy, + svd.dndest.nlx, svd.dndest.nly, + svd.dndest.nhx, svd.dndest.nhy, LR_DOWNTELE, (d_level *) 0); /* might have just left solid rock and unblocked levitation */ switch_terrain(); } -void -stairway_add(coordxy x, coordxy y, boolean up, boolean isladder, d_level *dest) -{ - stairway *tmp = (stairway *) alloc(sizeof (stairway)); - - (void) memset((genericptr_t) tmp, 0, sizeof (stairway)); - tmp->sx = x; - tmp->sy = y; - tmp->up = up; - tmp->isladder = isladder; - tmp->u_traversed = FALSE; - assign_level(&(tmp->tolev), dest); - tmp->next = gs.stairs; - gs.stairs = tmp; -} - -void -stairway_free_all(void) -{ - stairway *tmp = gs.stairs; - - while (tmp) { - stairway *tmp2 = tmp->next; - free(tmp); - tmp = tmp2; - } - gs.stairs = NULL; -} - -stairway * -stairway_at(coordxy x, coordxy y) -{ - stairway *tmp = gs.stairs; - - while (tmp && !(tmp->sx == x && tmp->sy == y)) - tmp = tmp->next; - return tmp; -} - -stairway * -stairway_find(d_level *fromdlev) -{ - stairway *tmp = gs.stairs; - - while (tmp) { - if (tmp->tolev.dnum == fromdlev->dnum - && tmp->tolev.dlevel == fromdlev->dlevel) - break; /* return */ - tmp = tmp->next; - } - return tmp; -} - -stairway * -stairway_find_from(d_level *fromdlev, boolean isladder) -{ - stairway *tmp = gs.stairs; - - while (tmp) { - if (tmp->tolev.dnum == fromdlev->dnum - && tmp->tolev.dlevel == fromdlev->dlevel - && tmp->isladder == isladder) - break; /* return */ - tmp = tmp->next; - } - return tmp; -} - -stairway * -stairway_find_dir(boolean up) -{ - stairway *tmp = gs.stairs; - - while (tmp && !(tmp->up == up)) - tmp = tmp->next; - return tmp; -} - -stairway * -stairway_find_type_dir(boolean isladder, boolean up) -{ - stairway *tmp = gs.stairs; - - while (tmp && !(tmp->isladder == isladder && tmp->up == up)) - tmp = tmp->next; - return tmp; -} - -stairway * -stairway_find_special_dir(boolean up) -{ - stairway *tmp = gs.stairs; - - while (tmp) { - if (tmp->tolev.dnum != u.uz.dnum && tmp->up != up) - return tmp; - tmp = tmp->next; - } - return tmp; -} - -/* place you on the special staircase */ -void -u_on_sstairs(int upflag) -{ - stairway *stway = stairway_find_special_dir(upflag); - - if (stway) - u_on_newpos(stway->sx, stway->sy); - else - u_on_rndspot(upflag); -} - -/* place you on upstairs (or special equivalent) */ -void -u_on_upstairs(void) -{ - stairway *stway = stairway_find_dir(TRUE); - - if (stway) - u_on_newpos(stway->sx, stway->sy); - else - u_on_sstairs(0); /* destination upstairs implies moving down */ -} - -/* place you on dnstairs (or special equivalent) */ -void -u_on_dnstairs(void) -{ - stairway *stway = stairway_find_dir(FALSE); - - if (stway) - u_on_newpos(stway->sx, stway->sy); - else - u_on_sstairs(1); /* destination dnstairs implies moving up */ -} - -boolean -On_stairs(coordxy x, coordxy y) -{ - return (stairway_at(x,y) != NULL); -} - -boolean -On_ladder(coordxy x, coordxy y) -{ - stairway *stway = stairway_at(x,y); - - return (boolean) (stway && stway->isladder); -} - -boolean -On_stairs_up(coordxy x, coordxy y) -{ - stairway *stway = stairway_at(x,y); - - return (boolean) (stway && stway->up); -} - -boolean -On_stairs_dn(coordxy x, coordxy y) -{ - stairway *stway = stairway_at(x,y); - - return (boolean) (stway && !stway->up); -} - boolean Is_botlevel(d_level *lev) { - return (boolean) (lev->dlevel == gd.dungeons[lev->dnum].num_dunlevs); + return (boolean) (lev->dlevel == svd.dungeons[lev->dnum].num_dunlevs); } boolean Can_dig_down(d_level *lev) { - return (boolean) (!gl.level.flags.hardfloor + return (boolean) (!svl.level.flags.hardfloor && !Is_botlevel(lev) && !Invocation_lev(lev)); } @@ -1755,7 +1687,7 @@ Can_rise_up(coordxy x UNUSED, coordxy y UNUSED, d_level *lev) stairway *stway = stairway_find_special_dir(FALSE); return (boolean) (lev->dlevel > 1 - || (gd.dungeons[lev->dnum].entry_lev == 1 + || (svd.dungeons[lev->dnum].entry_lev == 1 && ledger_no(lev) != 1 && stway && stway->up)); } @@ -1767,7 +1699,7 @@ has_ceiling(d_level *lev) above ground and don't have ceilings outside of their buildings but we don't presently check for that */ if ((In_endgame(lev) && !Is_earthlevel(lev)) - || gl.level.flags.outdoors) + || svl.level.flags.outdoors) return FALSE; return TRUE; } @@ -1884,10 +1816,10 @@ get_level(d_level *newlevel, int levnum) if (levnum <= 0) { /* can only currently happen in endgame */ levnum = u.uz.dlevel; - } else if (levnum > (gd.dungeons[dgn].depth_start - + gd.dungeons[dgn].num_dunlevs - 1)) { + } else if (levnum > (svd.dungeons[dgn].depth_start + + svd.dungeons[dgn].num_dunlevs - 1)) { /* beyond end of dungeon, jump to last level */ - levnum = gd.dungeons[dgn].num_dunlevs; + levnum = svd.dungeons[dgn].num_dunlevs; } else { /* The desired level is in this dungeon or a "higher" one. */ @@ -1895,7 +1827,7 @@ get_level(d_level *newlevel, int levnum) * Branch up the tree until we reach a dungeon that contains the * levnum. */ - if (levnum < gd.dungeons[dgn].depth_start) { + if (levnum < svd.dungeons[dgn].depth_start) { do { /* * Find the parent dungeon of this dungeon. @@ -1903,18 +1835,18 @@ get_level(d_level *newlevel, int levnum) * This assumes that end2 is always the "child" and it is * unique. */ - for (br = gb.branches; br; br = br->next) + for (br = svb.branches; br; br = br->next) if (br->end2.dnum == dgn) break; if (!br) panic("get_level: can't find parent dungeon"); dgn = br->end1.dnum; - } while (levnum < gd.dungeons[dgn].depth_start); + } while (levnum < svd.dungeons[dgn].depth_start); } /* We're within the same dungeon; calculate the level. */ - levnum = levnum - gd.dungeons[dgn].depth_start + 1; + levnum = levnum - svd.dungeons[dgn].depth_start + 1; } newlevel->dnum = dgn; @@ -1952,7 +1884,7 @@ dungeon_branch(const char *s) dnum = dname_to_dnum(s); /* Find the branch that connects to dungeon i's branch. */ - for (br = gb.branches; br; br = br->next) + for (br = svb.branches; br; br = br->next) if (br->end2.dnum == dnum) break; @@ -1999,7 +1931,7 @@ On_W_tower_level(d_level *lev) boolean In_hell(d_level *lev) { - return (boolean) (gd.dungeons[lev->dnum].flags.hellish); + return (boolean) (svd.dungeons[lev->dnum].flags.hellish); } /* sets *lev to be the gateway to Gehennom... */ @@ -2063,9 +1995,9 @@ induced_align(int pct) if (rn2(100) < pct) return lev->flags.align; - if (gd.dungeons[u.uz.dnum].flags.align) + if (svd.dungeons[u.uz.dnum].flags.align) if (rn2(100) < pct) - return gd.dungeons[u.uz.dnum].flags.align; + return svd.dungeons[u.uz.dnum].flags.align; al = rn2(3) - 1; return Align2amask(al); @@ -2075,7 +2007,7 @@ boolean Invocation_lev(d_level *lev) { return (boolean) (lev->dnum == sanctum_level.dnum - && lev->dlevel == gd.dungeons[lev->dnum].num_dunlevs - 1); + && lev->dlevel == svd.dungeons[lev->dnum].num_dunlevs - 1); } /* use instead of depth() wherever a degree of difficulty is made @@ -2098,7 +2030,7 @@ level_difficulty(void) they were easier; adjust for the extra effort involved in going down to the entrance and then up to the location */ if (builds_up(&u.uz)) - res += 2 * (gd.dungeons[u.uz.dnum].entry_lev - u.uz.dlevel + 1); + res += 2 * (svd.dungeons[u.uz.dnum].entry_lev - u.uz.dlevel + 1); /* * 'Proof' by example: suppose the entrance to sokoban is * on dungeon level 9, leading up to bottom sokoban level @@ -2116,6 +2048,9 @@ level_difficulty(void) * there is inconsequential compared to overall depth. */ } + /* ring of aggravate monster */ + if (EAggravate_monster) + res += 15; return res; } @@ -2176,7 +2111,7 @@ lev_by_name(const char *nam) /* either wizard mode or else seen and not forgotten; note: used to be '(flags & (FORGOTTEN|VISITED)) == VISITED' back when amnesia could cause levels to be forgotten */ - && (wizard || (gl.level_info[idx].flags & (VISITED)) == VISITED)) { + && (wizard || (svl.level_info[idx].flags & VISITED) == VISITED)) { lev = depth(&dlev); } } else { /* not a specific level; try branch names */ @@ -2188,9 +2123,10 @@ lev_by_name(const char *nam) if (idx >= 0) { idxtoo = (idx >> 8) & 0x00FF; idx &= 0x00FF; - /* either wizard mode, or else _both_ sides of branch seen */ - if (wizard || (((gl.level_info[idx].flags & (VISITED)) == VISITED) - && ((gl.level_info[idxtoo].flags & (VISITED)) + /* either wizard mode, or else _both_ sides of branch seen; */ + /* (flags & VISITED)==VISITED: see comment about amnesia above */ + if (wizard || (((svl.level_info[idx].flags & VISITED) == VISITED) + && ((svl.level_info[idxtoo].flags & VISITED) == VISITED))) { if (ledger_to_dnum(idxtoo) == u.uz.dnum) idx = idxtoo; @@ -2206,22 +2142,22 @@ lev_by_name(const char *nam) #undef dlev_in_current_branch -static boolean +staticfn boolean unplaced_floater(struct dungeon *dptr) { branch *br; - int idx = (int) (dptr - gd.dungeons); + int idx = (int) (dptr - svd.dungeons); /* if other floating branches are added, this will need to change */ if (idx != knox_level.dnum) return FALSE; - for (br = gb.branches; br; br = br->next) - if (br->end1.dnum == gn.n_dgns && br->end2.dnum == idx) + for (br = svb.branches; br; br = br->next) + if (br->end1.dnum == svn.n_dgns && br->end2.dnum == idx) return TRUE; return FALSE; } -static boolean +staticfn boolean unreachable_level(d_level *lvl_p, boolean unplaced) { s_level *dummy; @@ -2235,19 +2171,23 @@ unreachable_level(d_level *lvl_p, boolean unplaced) return FALSE; } -static void -tport_menu(winid win, char *entry, struct lchoice *lchoices, - d_level *lvl_p, boolean unreachable) +staticfn void +tport_menu( + winid win, + char *entry, + struct lchoice *lchoices, + d_level *lvl_p, + boolean cannotreach) { char tmpbuf[BUFSZ]; anything any; - int clr = 0; + int clr = NO_COLOR; lchoices->lev[lchoices->idx] = lvl_p->dlevel; lchoices->dgn[lchoices->idx] = lvl_p->dnum; lchoices->playerlev[lchoices->idx] = depth(lvl_p); any = cg.zeroany; - if (unreachable) { + if (cannotreach) { /* not selectable, but still consumes next menuletter; prepend padding in place of missing menu selector */ Sprintf(tmpbuf, " %s", entry); @@ -2266,68 +2206,8 @@ tport_menu(winid win, char *entry, struct lchoice *lchoices, return; } -/* return True if 'sway' is a branch staircase and hero has used these stairs - to visit the branch */ -boolean -known_branch_stairs(stairway *sway) -{ - return (sway && sway->tolev.dnum != u.uz.dnum && sway->u_traversed); -} - -/* describe staircase 'sway' based on whether hero knows the destination */ -char * -stairs_description( - stairway *sway, /* stairs/ladder to describe */ - char *outbuf, /* result buffer */ - boolean stcase) /* True: "staircase" or "ladder", always singular; - * False: "stairs" or "ladder"; caller needs to deal - * with singular vs plural when forming a sentence */ -{ - d_level tolev; - const char *stairs, *updown; - - tolev = sway->tolev; - stairs = sway->isladder ? "ladder" : stcase ? "staircase" : "stairs"; - updown = sway->up ? "up" : "down"; - - if (!known_branch_stairs(sway)) { - /* ordinary stairs or branch stairs to not-yet-visited branch */ - Sprintf(outbuf, "%s %s", stairs, updown); - if (sway->u_traversed) { - boolean specialdepth = (tolev.dnum == quest_dnum - || single_level_branch(&tolev)); /* knox */ - int to_dlev = specialdepth ? dunlev(&tolev) : depth(&tolev); - - Sprintf(eos(outbuf), " to level %d", to_dlev); - } - } else if (u.uz.dnum == 0 && u.uz.dlevel == 1 && sway->up) { - /* stairs up from level one are a special case; they are marked - as having been traversed because the hero obviously started - the game by coming down them, but the remote side varies - depending on whether the Amulet is being carried */ - Sprintf(outbuf, "%s%s %s %s", - !u.uhave.amulet ? "" : "branch ", - stairs, updown, - !u.uhave.amulet ? "out of the dungeon" - /* minimize our expectations about what comes next */ - : (on_level(&tolev, &earth_level) - || on_level(&tolev, &air_level) - || on_level(&tolev, &fire_level) - || on_level(&tolev, &water_level)) - ? "to the Elemental Planes" - : "to the end game"); - } else { - /* known branch stairs; tacking on destination level is too verbose */ - Sprintf(outbuf, "branch %s %s to %s", - stairs, updown, gd.dungeons[tolev.dnum].dname); - /* dungeons[].dname is capitalized; undo that for "The " */ - (void) strsubst(outbuf, "The ", "the "); - } - return outbuf; -} - /* Convert a branch type to a string usable by print_dungeon(). */ -static const char * +staticfn const char * br_string(int type) { switch (type) { @@ -2343,28 +2223,30 @@ br_string(int type) return " (unknown)"; } -static char +staticfn char chr_u_on_lvl(d_level *dlev) { return u.uz.dnum == dlev->dnum && u.uz.dlevel == dlev->dlevel ? '*' : ' '; } /* Print all child branches between the lower and upper bounds. */ -static void -print_branch(winid win, int dnum, int lower_bound, int upper_bound, - boolean bymenu, struct lchoice *lchoices_p) +staticfn void +print_branch( + winid win, + int dnum, int lower_bound, int upper_bound, + boolean bymenu, struct lchoice *lchoices_p) { branch *br; char buf[BUFSZ]; /* This assumes that end1 is the "parent". */ - for (br = gb.branches; br; br = br->next) { + for (br = svb.branches; br; br = br->next) { if (br->end1.dnum == dnum && lower_bound < br->end1.dlevel && br->end1.dlevel <= upper_bound) { Sprintf(buf, "%c %s to %s: %d", bymenu ? chr_u_on_lvl(&br->end1) : ' ', br_string(br->type), - gd.dungeons[br->end2.dnum].dname, depth(&br->end1)); + svd.dungeons[br->end2.dnum].dname, depth(&br->end1)); if (bymenu) tport_menu(win, buf, lchoices_p, &br->end1, unreachable_level(&br->end1, FALSE)); @@ -2385,10 +2267,8 @@ print_dungeon(boolean bymenu, schar *rlev, xint16 *rdgn) s_level *slev; dungeon *dptr; branch *br; - anything any; struct lchoice lchoices; winid win = create_nhwindow(NHW_MENU); - int clr = 0; if (bymenu) { start_menu(win, MENU_BEHAVE_STANDARD); @@ -2396,7 +2276,7 @@ print_dungeon(boolean bymenu, schar *rlev, xint16 *rdgn) lchoices.menuletter = 'a'; } - for (i = 0, dptr = gd.dungeons; i < gn.n_dgns; i++, dptr++) { + for (i = 0, dptr = svd.dungeons; i < svn.n_dgns; i++, dptr++) { if (bymenu && In_endgame(&u.uz) && i != astral_level.dnum) continue; unplaced = unplaced_floater(dptr); @@ -2419,9 +2299,7 @@ print_dungeon(boolean bymenu, schar *rlev, xint16 *rdgn) dptr->depth_start + dptr->entry_lev - 1); } if (bymenu) { - any = cg.zeroany; - add_menu(win, &nul_glyphinfo, &any, 0, 0, - iflags.menu_headings, clr, buf, MENU_ITEMFLAGS_NONE); + add_menu_heading(win, buf); } else putstr(win, 0, buf); @@ -2429,7 +2307,7 @@ print_dungeon(boolean bymenu, schar *rlev, xint16 *rdgn) * Circle through the special levels to find levels that are in * this dungeon. */ - for (slev = gs.sp_levchn, last_level = 0; slev; slev = slev->next) { + for (slev = svs.sp_levchn, last_level = 0; slev; slev = slev->next) { if (slev->dlevel.dnum != i) continue; @@ -2441,7 +2319,7 @@ print_dungeon(boolean bymenu, schar *rlev, xint16 *rdgn) chr_u_on_lvl(&slev->dlevel), slev->proto, depth(&slev->dlevel)); if (Is_stronghold(&slev->dlevel)) - Sprintf(eos(buf), " (tune %s)", gt.tune); + Sprintf(eos(buf), " (tune %s)", svt.tune); if (bymenu) tport_menu(win, buf, &lchoices, &slev->dlevel, unreachable_level(&slev->dlevel, unplaced)); @@ -2475,15 +2353,15 @@ print_dungeon(boolean bymenu, schar *rlev, xint16 *rdgn) } /* Print out floating branches (if any). */ - for (first = TRUE, br = gb.branches; br; br = br->next) { - if (br->end1.dnum == gn.n_dgns) { + for (first = TRUE, br = svb.branches; br; br = br->next) { + if (br->end1.dnum == svn.n_dgns) { if (first) { putstr(win, 0, ""); putstr(win, 0, "Floating branches"); first = FALSE; } Sprintf(buf, " %s to %s", br_string(br->type), - gd.dungeons[br->end2.dnum].dname); + svd.dungeons[br->end2.dnum].dname); putstr(win, 0, buf); } } @@ -2492,7 +2370,7 @@ print_dungeon(boolean bymenu, schar *rlev, xint16 *rdgn) if (Invocation_lev(&u.uz)) { putstr(win, 0, ""); Sprintf(buf, "Invocation position @ (%d,%d), hero @ (%d,%d)", - gi.inv_pos.x, gi.inv_pos.y, u.ux, u.uy); + svi.inv_pos.x, svi.inv_pos.y, u.ux, u.uy); putstr(win, 0, buf); } else { struct trap *trap; @@ -2546,7 +2424,7 @@ recbranch_mapseen(d_level *source, d_level *dest) return; /* we only care about forward branches */ - for (br = gb.branches; br; br = br->next) { + for (br = svb.branches; br; br = br->next) { if (on_level(source, &br->end1) && on_level(dest, &br->end2)) break; if (on_level(source, &br->end2) && on_level(dest, &br->end1)) @@ -2567,7 +2445,7 @@ recbranch_mapseen(d_level *source, d_level *dest) } } -char * +staticfn char * get_annotation(d_level *lev) { mapseen *mptr; @@ -2577,9 +2455,19 @@ get_annotation(d_level *lev) return NULL; } +/* print the annotation for the current level, if it exists */ +void +print_level_annotation(void) +{ + const char *annotation; + + if ((annotation = get_annotation(&u.uz)) != 0) + You("remember this level as %s.", annotation); +} + /* ask user to annotate level lev. if lev is NULL, uses current level. */ -static void +staticfn void query_annotation(d_level *lev) { mapseen *mptr; @@ -2603,7 +2491,29 @@ query_annotation(d_level *lev) getlin(tmpbuf, nbuf); } else #endif - getlin("What do you want to call this dungeon level?", nbuf); + { + char qbuf[QBUFSZ], lbuf[QBUFSZ]; /* level description */ + + if (!lev || on_level(&u.uz, lev)) { + Strcpy(lbuf, "this dungeon level"); + } else { + int dflgs = (lev->dnum == u.uz.dnum) ? 0 : 2; + d_level save_uz = u.uz; + + u.uz = *lev; + (void) describe_level(lbuf, dflgs); + u.uz = save_uz; + + (void) strsubst(lbuf, "Dlvl:", "level "); + /* even though we've told describe_level() not to append + a trailing space (by not including '1' in dflgs), the + level number is formatted with %-2d so single digit + values will end up with one anyway; remove it */ + (void) trimspaces(lbuf); + } + Snprintf(qbuf, sizeof qbuf, "What do you want to call %s?", lbuf); + getlin(qbuf, nbuf); + } /* empty input or ESC means don't add or change annotation; space-only means discard current annotation without adding new one */ @@ -2629,29 +2539,92 @@ query_annotation(d_level *lev) int donamelevel(void) { - query_annotation((d_level *)0); + query_annotation((d_level *) 0); return ECMD_OK; } +/* exclusion zones */ +void +free_exclusions(void) +{ + struct exclusion_zone *ez = sve.exclusion_zones; + + while (ez) { + struct exclusion_zone *nxtez = ez->next; + + free(ez); + ez = nxtez; + } + sve.exclusion_zones = (struct exclusion_zone *) 0; +} + +void +save_exclusions(NHFILE *nhfp) +{ + struct exclusion_zone *ez; + int nez; + + 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); + } + } +} + +void +load_exclusions(NHFILE *nhfp) +{ + struct exclusion_zone *ez; + int nez = 0; + + if (nhfp->structlevel) + mread(nhfp->fd, (genericptr_t) &nez, sizeof nez); + + 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); + } + ez->next = sve.exclusion_zones; + sve.exclusion_zones = ez; + } +} + /* find the particular mapseen object in the chain; may return null */ -static mapseen * +staticfn mapseen * find_mapseen(d_level *lev) { mapseen *mptr; - for (mptr = gm.mapseenchn; mptr; mptr = mptr->next) + for (mptr = svm.mapseenchn; mptr; mptr = mptr->next) if (on_level(&(mptr->lev), lev)) break; return mptr; } -static mapseen * +staticfn mapseen * find_mapseen_by_str(const char *s) { mapseen *mptr; - for (mptr = gm.mapseenchn; mptr; mptr = mptr->next) + for (mptr = svm.mapseenchn; mptr; mptr = mptr->next) if (mptr->custom && !strcmpi(s, mptr->custom)) break; @@ -2662,11 +2635,11 @@ find_mapseen_by_str(const char *s) void rm_mapseen(int ledger_num) { - mapseen *mptr, *mprev = (mapseen *)0; + mapseen *mptr, *mprev = (mapseen *) 0; struct cemetery *bp, *bpnext; - for (mptr = gm.mapseenchn; mptr; mprev = mptr, mptr = mptr->next) - if (gd.dungeons[mptr->lev.dnum].ledger_start + mptr->lev.dlevel + for (mptr = svm.mapseenchn; mptr; mprev = mptr, mptr = mptr->next) + if (svd.dungeons[mptr->lev.dnum].ledger_start + mptr->lev.dlevel == ledger_num) break; @@ -2687,18 +2660,18 @@ rm_mapseen(int ledger_num) mprev->next = mptr->next; free(mptr); } else { - gm.mapseenchn = mptr->next; + svm.mapseenchn = mptr->next; free(mptr); } } -static void +staticfn void save_mapseen(NHFILE *nhfp, mapseen *mptr) { branch *curr; int brindx; - for (brindx = 0, curr = gb.branches; curr; curr = curr->next, ++brindx) + for (brindx = 0, curr = svb.branches; curr; curr = curr->next, ++brindx) if (curr == mptr->br) break; if (nhfp->structlevel) @@ -2708,7 +2681,8 @@ save_mapseen(NHFILE *nhfp, mapseen *mptr) 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); + bwrite(nhfp->fd, (genericptr_t) &mptr->custom_lth, + sizeof mptr->custom_lth); } if (mptr->custom_lth) { @@ -2722,7 +2696,7 @@ save_mapseen(NHFILE *nhfp, mapseen *mptr) savecemetery(nhfp, &mptr->final_resting_place); } -static mapseen * +staticfn mapseen * load_mapseen(NHFILE *nhfp) { int branchnum = 0, brindx; @@ -2733,7 +2707,7 @@ load_mapseen(NHFILE *nhfp) if (nhfp->structlevel) mread(nhfp->fd, (genericptr_t) &branchnum, sizeof branchnum); - for (brindx = 0, curr = gb.branches; curr; curr = curr->next, ++brindx) + for (brindx = 0, curr = svb.branches; curr; curr = curr->next, ++brindx) if (brindx == branchnum) break; load->br = curr; @@ -2742,7 +2716,8 @@ load_mapseen(NHFILE *nhfp) 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); + mread(nhfp->fd, (genericptr_t) &load->custom_lth, + sizeof load->custom_lth); } if (load->custom_lth) { @@ -2752,8 +2727,9 @@ load_mapseen(NHFILE *nhfp) mread(nhfp->fd, (genericptr_t) load->custom, load->custom_lth); } load->custom[load->custom_lth] = '\0'; - } else + } else { load->custom = 0; + } if (nhfp->structlevel) { mread(nhfp->fd, (genericptr_t) &load->msrooms, sizeof load->msrooms); } @@ -2773,10 +2749,10 @@ 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 = gm.mapseenchn; mptr; mptr = mptr->next) { + for (mptr = svm.mapseenchn; mptr; mptr = mptr->next) { ++ocount; osize += (long) sizeof *mptr; for (ce = mptr->final_resting_place; ce; ce = ce->next) { @@ -2811,7 +2787,7 @@ RESTORE_WARNING_FORMAT_NONLITERAL /* Remove all mapseen objects for a particular dnum. * Useful during quest expulsion to remove quest levels. - * [No longer deleted, just marked as unreachable. #overview will + * [No longer deleted, just marked as notreachable. #overview will * ignore such levels, end of game disclosure will include them.] */ void @@ -2819,11 +2795,11 @@ remdun_mapseen(int dnum) { mapseen *mptr, **mptraddr; - mptraddr = &gm.mapseenchn; + mptraddr = &svm.mapseenchn; while ((mptr = *mptraddr) != 0) { if (mptr->lev.dnum == dnum) { #if 1 /* use this... */ - mptr->flags.unreachable = 1; + mptr->flags.notreachable = 1; } #else /* old deletion code */ *mptraddr = mptr->next; @@ -2852,22 +2828,24 @@ init_mapseen(d_level *lev) explicitly initialize pointers to null */ init->next = 0, init->br = 0, init->custom = 0; init->final_resting_place = 0; - /* gl.lastseentyp[][] is reused for each level, so get rid of + /* svl.lastseentyp[][] is reused for each level, so get rid of previous level's data */ - (void) memset((genericptr_t) gl.lastseentyp, 0, sizeof gl.lastseentyp); + (void) memset((genericptr_t) svl.lastseentyp, 0, sizeof svl.lastseentyp); init->lev.dnum = lev->dnum; init->lev.dlevel = lev->dlevel; /* walk until we get to the place where we should insert init */ - for (mptr = gm.mapseenchn, prev = 0; mptr; prev = mptr, mptr = mptr->next) + for (mptr = svm.mapseenchn, prev = 0; mptr; + prev = mptr, mptr = mptr->next) { if (mptr->lev.dnum > init->lev.dnum || (mptr->lev.dnum == init->lev.dnum && mptr->lev.dlevel > init->lev.dlevel)) break; + } if (!prev) { - init->next = gm.mapseenchn; - gm.mapseenchn = init; + init->next = svm.mapseenchn; + svm.mapseenchn = init; } else { mptr = prev->next; prev->next = init; @@ -2875,19 +2853,28 @@ init_mapseen(d_level *lev) } } -#define INTEREST(feat) \ +#define OF_INTEREST(feat) \ ((feat).nfount || (feat).nsink || (feat).nthrone || (feat).naltar \ || (feat).ngrave || (feat).ntree || (feat).nshop || (feat).ntemple) /* || (feat).water || (feat).ice || (feat).lava */ /* returns true if this level has something interesting to print out */ -static boolean +staticfn boolean interest_mapseen(mapseen *mptr) { if (on_level(&u.uz, &mptr->lev)) return TRUE; - if (mptr->flags.unreachable) + if (mptr->flags.notreachable) return FALSE; + /* when in tutorial, show all tutorial levels visited whether interesting + or not and don't show any other levels; when outside tutorial, don't + show any tutorial levels even if they're considered interesting */ + if (In_tutorial(&u.uz)) { + return In_tutorial(&mptr->lev); + } else { + if (In_tutorial(&mptr->lev)) + return FALSE; + } /* level is of interest if it has an auto-generated annotation */ if (mptr->flags.oracle || mptr->flags.bigroom || mptr->flags.castle || mptr->flags.valley @@ -2904,18 +2891,166 @@ interest_mapseen(mapseen *mptr) return TRUE; /* when in the endgame, list all endgame levels visited, whether they have annotations or not, so that #overview doesn't become extremely - sparse once the rest of the dungeon has been flagged as unreachable */ + sparse once the rest of the dungeon has been flagged as notreachable */ if (In_endgame(&u.uz)) return (boolean) In_endgame(&mptr->lev); /* level is of interest if it has non-zero feature count or known bones or user annotation or known connection to another dungeon branch or is the furthest level reached in its branch */ - return (boolean) (INTEREST(mptr->feat) + return (boolean) (OF_INTEREST(mptr->feat) || (mptr->final_resting_place && (mptr->flags.knownbones || wizard)) || mptr->custom || mptr->br || (mptr->lev.dlevel - == gd.dungeons[mptr->lev.dnum].dunlev_ureached)); + == svd.dungeons[mptr->lev.dnum].dunlev_ureached)); +} + +/* update the lastseentyp at x,y */ +void +update_lastseentyp(coordxy x, coordxy y) +{ + struct monst *mtmp; + int ltyp = levl[x][y].typ; + + if (ltyp == DRAWBRIDGE_UP) + ltyp = db_under_typ(levl[x][y].drawbridgemask); + if ((mtmp = m_at(x, y)) != 0 + && M_AP_TYPE(mtmp) == M_AP_FURNITURE && canseemon(mtmp)) + ltyp = cmap_to_type(mtmp->mappearance); + svl.lastseentyp[x][y] = ltyp; +} + +/* for some cases where deferred update needs to be done immediately; + hide details from caller */ +int +update_mapseen_for(coordxy x, coordxy y) +{ + recalc_mapseen(); /* whole level */ + return svl.lastseentyp[x][y]; +} + +/* count mapseen feature from lastseentyp at x,y */ +staticfn void +count_feat_lastseentyp( + mapseen *mptr, /* remembered data for a level; update feat.X counts */ + coordxy x, coordxy y) +{ + int count; + unsigned atmp; + + switch (svl.lastseentyp[x][y]) { +#if 0 /* levels that have these tend of have a lot of them */ + /* + * FIXME? due to theme rooms, lots of levels have an increased + * chance of having these so automatic annotations for them may + * have become more worthwhile now. + */ + case ICE: + count = mptr->feat.ice + 1; + if (count <= 3) + mptr->feat.ice = count; + break; + case POOL: + case MOAT: + case WATER: + count = mptr->feat.water + 1; + if (count <= 3) + mptr->feat.water = count; + break; + case LAVAPOOL: + case LAVAWALL: + count = mptr->feat.lava + 1; + if (count <= 3) + mptr->feat.lava = count; + break; +#endif + case TREE: + count = mptr->feat.ntree + 1; + if (count <= 3) + mptr->feat.ntree = count; + break; + case FOUNTAIN: + count = mptr->feat.nfount + 1; + if (count <= 3) + mptr->feat.nfount = count; + break; + case THRONE: + count = mptr->feat.nthrone + 1; + if (count <= 3) + mptr->feat.nthrone = count; + break; + case SINK: + count = mptr->feat.nsink + 1; + if (count <= 3) + mptr->feat.nsink = count; + break; + case GRAVE: + count = mptr->feat.ngrave + 1; + if (count <= 3) + mptr->feat.ngrave = count; + break; + case ALTAR: + /* get the altarmask for this location; might be a mimic */ + atmp = altarmask_at(x, y); + /* convert to index: 0..3 */ + atmp = (Is_astralevel(&u.uz) && (levl[x][y].seenv & SVALL) != SVALL) + ? MSA_NONE + : Amask2msa(atmp); + if (!mptr->feat.naltar) + mptr->feat.msalign = atmp; + else if (mptr->feat.msalign != atmp) + mptr->feat.msalign = MSA_NONE; + count = mptr->feat.naltar + 1; + if (count <= 3) + mptr->feat.naltar = count; + break; + /* An automatic annotation is added to the Castle and + * to Fort Ludios once their structure's main entrance + * has been seen (in person or via magic mapping). + * For the Fort, that entrance is just a secret door + * which will be converted into a regular one when + * located (or destroyed). + * DOOR: possibly a lowered drawbridge's open portcullis; + * DBWALL: a raised drawbridge's "closed door"; + * DRAWBRIDGE_DOWN: the span provided by lowered bridge, + * with moat or other terrain hidden underneath; + * DRAWBRIDGE_UP: moat in front of a raised drawbridge, + * not recognizable as a bridge location unless/until + * the adjacent DBWALL has been seen. + */ + case DOOR: + if (Is_knox(&u.uz)) { + int ty, tx = x - 4; + + /* Throne is four columns to left, either directly in + * line or one row higher or lower, and doesn't have + * to have been seen yet. + * ......|}}}. + * ..\...S}... + * ..\...S}... + * ......|}}}. + * For 3.6.0 and earlier, it was always in direct line: + * both throne and door on the lower of the two rows. + */ + for (ty = y - 1; ty <= y + 1; ++ty) + if (isok(tx, ty) && IS_THRONE(levl[tx][ty].typ)) { + mptr->flags.ludios = 1; + break; + } + break; + } + if (is_drawbridge_wall(x, y) < 0) + break; + FALLTHROUGH; + /*FALLTHRU*/ + case DBWALL: + case DRAWBRIDGE_DOWN: + if (Is_stronghold(&u.uz)) + mptr->flags.castle = 1, mptr->flags.castletune = 1; + break; + default: + break; + } } /* recalculate mapseen for the current level */ @@ -2926,8 +3061,8 @@ recalc_mapseen(void) struct monst *mtmp; struct cemetery *bp, **bonesaddr; struct trap *t; - unsigned i, ridx, atmp; - int ltyp, count; + unsigned i, ridx; + int count; coordxy x, y; char uroom; @@ -2943,17 +3078,17 @@ recalc_mapseen(void) /* reset all features; mptr->feat.* = 0; */ (void) memset((genericptr_t) &mptr->feat, 0, sizeof mptr->feat); /* reset most flags; some level-specific ones are left as-is */ - if (mptr->flags.unreachable) { - mptr->flags.unreachable = 0; /* reached it; Eye of the Aethiopica? */ + if (mptr->flags.notreachable) { + mptr->flags.notreachable = 0; /* reached it; Eye of the Aethiopica? */ if (In_quest(&u.uz)) { - mapseen *mptrtmp = gm.mapseenchn; + mapseen *mptrtmp = svm.mapseenchn; - /* when quest was unreachable due to ejection and portal removal, + /* when quest was notreachable due to ejection and portal removal, getting back to it via arti-invoke should revive annotation data for all quest levels, not just the one we're on now */ do { if (mptrtmp->lev.dnum == mptr->lev.dnum) - mptrtmp->flags.unreachable = 0; + mptrtmp->flags.notreachable = 0; mptrtmp = mptrtmp->next; } while (mptrtmp); } @@ -2970,9 +3105,9 @@ recalc_mapseen(void) && u.uevent.qcalled && !(u.uevent.qcompleted || u.uevent.qexpelled - || gq.quest_status.killed_leader)); + || svq.quest_status.killed_leader)); mptr->flags.questing = (on_level(&u.uz, &qstart_level) - && gq.quest_status.got_quest); + && svq.quest_status.got_quest); /* flags.msanctum, .valley, and .vibrating_square handled below */ /* track rooms the hero is in */ @@ -2980,9 +3115,9 @@ recalc_mapseen(void) ridx = (unsigned) uroom - ROOMOFFSET; mptr->msrooms[ridx].seen = 1; mptr->msrooms[ridx].untended = - (gr.rooms[ridx].rtype >= SHOPBASE) + (svr.rooms[ridx].rtype >= SHOPBASE) ? (!(mtmp = shop_keeper(uroom)) || !inhishop(mtmp)) - : (gr.rooms[ridx].rtype == TEMPLE) + : (svr.rooms[ridx].rtype == TEMPLE) ? (!(mtmp = findpriest(uroom)) || !inhistemple(mtmp)) : 0; } @@ -2992,28 +3127,28 @@ recalc_mapseen(void) */ for (i = 0; i < SIZE(mptr->msrooms); ++i) { if (mptr->msrooms[i].seen) { - if (gr.rooms[i].rtype >= SHOPBASE) { + if (svr.rooms[i].rtype >= SHOPBASE) { if (mptr->msrooms[i].untended) mptr->feat.shoptype = SHOPBASE - 1; else if (!mptr->feat.nshop) - mptr->feat.shoptype = gr.rooms[i].rtype; - else if (mptr->feat.shoptype != (unsigned) gr.rooms[i].rtype) + mptr->feat.shoptype = svr.rooms[i].rtype; + else if (mptr->feat.shoptype != (unsigned) svr.rooms[i].rtype) mptr->feat.shoptype = 0; count = mptr->feat.nshop + 1; if (count <= 3) mptr->feat.nshop = count; - } else if (gr.rooms[i].rtype == TEMPLE) { + } else if (svr.rooms[i].rtype == TEMPLE) { /* altar and temple alignment handled below */ count = mptr->feat.ntemple + 1; if (count <= 3) mptr->feat.ntemple = count; - } else if (gr.rooms[i].orig_rtype == DELPHI) { + } else if (svr.rooms[i].orig_rtype == DELPHI) { mptr->flags.oracle = 1; } } } - /* Update gl.lastseentyp with typ if and only if it is in sight or the + /* Update svl.lastseentyp with typ if and only if it is in sight or the * hero can feel it on their current location (i.e. not levitating). * This *should* give the "last known typ" for each dungeon location. * (At the very least, it's a better assumption than determining what @@ -3024,133 +3159,19 @@ recalc_mapseen(void) * we could track "features" and then update them all here, and keep * track of when new features are created or destroyed, but this * seemed the most elegant, despite adding more data to struct rm. - * [3.6.0: we're using gl.lastseentyp[][] rather than level.locations + * [3.6.0: we're using svl.lastseentyp[][] rather than level.locations * to track the features seen.] * * Although no current windowing systems (can) do this, this would add * the ability to have non-dungeon glyphs float above the last known * dungeon glyph (i.e. items on fountains). */ + if (!Levitation) + update_lastseentyp(u.ux, u.uy); + for (x = 1; x < COLNO; x++) { for (y = 0; y < ROWNO; y++) { - if (cansee(x, y) || (u_at(x, y) && !Levitation)) { - ltyp = levl[x][y].typ; - if (ltyp == DRAWBRIDGE_UP) - ltyp = db_under_typ(levl[x][y].drawbridgemask); - if ((mtmp = m_at(x, y)) != 0 - && M_AP_TYPE(mtmp) == M_AP_FURNITURE && canseemon(mtmp)) - ltyp = cmap_to_type(mtmp->mappearance); - gl.lastseentyp[x][y] = ltyp; - } - - switch (gl.lastseentyp[x][y]) { -#if 0 - case ICE: - count = mptr->feat.ice + 1; - if (count <= 3) - mptr->feat.ice = count; - break; - case POOL: - case MOAT: - case WATER: - count = mptr->feat.water + 1; - if (count <= 3) - mptr->feat.water = count; - break; - case LAVAPOOL: - case LAVAWALL: - count = mptr->feat.lava + 1; - if (count <= 3) - mptr->feat.lava = count; - break; -#endif - case TREE: - count = mptr->feat.ntree + 1; - if (count <= 3) - mptr->feat.ntree = count; - break; - case FOUNTAIN: - count = mptr->feat.nfount + 1; - if (count <= 3) - mptr->feat.nfount = count; - break; - case THRONE: - count = mptr->feat.nthrone + 1; - if (count <= 3) - mptr->feat.nthrone = count; - break; - case SINK: - count = mptr->feat.nsink + 1; - if (count <= 3) - mptr->feat.nsink = count; - break; - case GRAVE: - count = mptr->feat.ngrave + 1; - if (count <= 3) - mptr->feat.ngrave = count; - break; - case ALTAR: - /* get the altarmask for this location; might be a mimic */ - atmp = altarmask_at(x, y); - /* convert to index: 0..3 */ - atmp = (Is_astralevel(&u.uz) - && (levl[x][y].seenv & SVALL) != SVALL) - ? MSA_NONE - : Amask2msa(atmp); - if (!mptr->feat.naltar) - mptr->feat.msalign = atmp; - else if (mptr->feat.msalign != atmp) - mptr->feat.msalign = MSA_NONE; - count = mptr->feat.naltar + 1; - if (count <= 3) - mptr->feat.naltar = count; - break; - /* An automatic annotation is added to the Castle and - * to Fort Ludios once their structure's main entrance - * has been seen (in person or via magic mapping). - * For the Fort, that entrance is just a secret door - * which will be converted into a regular one when - * located (or destroyed). - * DOOR: possibly a lowered drawbridge's open portcullis; - * DBWALL: a raised drawbridge's "closed door"; - * DRAWBRIDGE_DOWN: the span provided by lowered bridge, - * with moat or other terrain hidden underneath; - * DRAWBRIDGE_UP: moat in front of a raised drawbridge, - * not recognizable as a bridge location unless/until - * the adjacent DBWALL has been seen. - */ - case DOOR: - if (Is_knox(&u.uz)) { - int ty, tx = x - 4; - - /* Throne is four columns left, either directly in - * line or one row higher or lower, and doesn't have - * to have been seen yet. - * ......|}}}. - * ..\...S}... - * ..\...S}... - * ......|}}}. - * For 3.6.0 and earlier, it was always in direct line: - * both throne and door on the lower of the two rows. - */ - for (ty = y - 1; ty <= y + 1; ++ty) - if (isok(tx, ty) && IS_THRONE(levl[tx][ty].typ)) { - mptr->flags.ludios = 1; - break; - } - break; - } - if (is_drawbridge_wall(x, y) < 0) - break; - /*FALLTHRU*/ - case DBWALL: - case DRAWBRIDGE_DOWN: - if (Is_stronghold(&u.uz)) - mptr->flags.castle = 1, mptr->flags.castletune = 1; - break; - default: - break; - } + count_feat_lastseentyp(mptr, x, y); } } @@ -3194,11 +3215,11 @@ recalc_mapseen(void) || !oth_mptr->flags.msanctum); } - if (gl.level.bonesinfo && !mptr->final_resting_place) { + if (svl.level.bonesinfo && !mptr->final_resting_place) { /* clone the bonesinfo so we aren't dependent upon this level being in memory */ bonesaddr = &mptr->final_resting_place; - bp = gl.level.bonesinfo; + bp = svl.level.bonesinfo; do { *bonesaddr = (struct cemetery *) alloc(sizeof **bonesaddr); **bonesaddr = *bp; @@ -3211,14 +3232,15 @@ recalc_mapseen(void) guarantee of either a grave or a ghost, so we go by whether the current hero has seen the map location where each old one died */ for (bp = mptr->final_resting_place; bp; bp = bp->next) - if (gl.lastseentyp[bp->frpx][bp->frpy]) { + if (svl.lastseentyp[bp->frpx][bp->frpy]) { bp->bonesknown = TRUE; mptr->flags.knownbones = 1; } } /*ARGUSED*/ -/* valley and sanctum levels get automatic annotation once temple is entered */ +/* valley and sanctum levels get automatic annotation once their temple + is entered */ void mapseen_temple( struct monst *priest UNUSED) /* not used; might be useful someday */ @@ -3239,8 +3261,10 @@ room_discovered(int roomno) { mapseen *mptr = find_mapseen(&u.uz); - if (mptr) + if (mptr && !mptr->msrooms[roomno].seen) { mptr->msrooms[roomno].seen = 1; + recalc_mapseen(); + } } /* #overview command */ @@ -3276,9 +3300,9 @@ show_overview( if (In_endgame(&u.uz)) traverse_mapseenchn(1, win, why, reason, &lastdun); /* if game is over or we're not in the endgame yet, show the dungeon */ - if (why != 0 || !In_endgame(&u.uz)) + if (why > 0 || !In_endgame(&u.uz)) traverse_mapseenchn(0, win, why, reason, &lastdun); - end_menu(win, (char *)0); + end_menu(win, (char *) 0); n = select_menu(win, (why != -1) ? PICK_NONE : PICK_ONE, &selected); if (n > 0) { int ledger; @@ -3294,7 +3318,7 @@ show_overview( } /* display endgame levels or non-endgame levels, not both */ -static void +staticfn void traverse_mapseenchn( int viewendgame, /* 0: show endgame branch; 1: show other branches */ winid win, /* output window */ @@ -3305,7 +3329,7 @@ traverse_mapseenchn( mapseen *mptr; boolean showheader; - for (mptr = gm.mapseenchn; mptr; mptr = mptr->next) { + for (mptr = svm.mapseenchn; mptr; mptr = mptr->next) { if (viewendgame ^ In_endgame(&mptr->lev)) continue; @@ -3318,14 +3342,14 @@ traverse_mapseenchn( } } -static const char * +staticfn const char * seen_string(xint16 x, const char *obj) { /* players are computer scientists: 0, 1, 2, n */ switch (x) { case 0: return "no"; - /* an() returns too much. index is ok in this case */ + /* an() returns too much. index/strchr is ok in this case */ case 1: return strchr(vowels, *obj) ? "an" : "a"; case 2: @@ -3338,7 +3362,7 @@ seen_string(xint16 x, const char *obj) } /* better br_string */ -static const char * +staticfn const char * br_string2(branch *br) { /* Special case: quest portal says closed if kicked from quest */ @@ -3390,31 +3414,38 @@ endgamelevelname(char *outbuf, int indx) return outbuf; } -static const char * +/* short shop description */ +staticfn const char * shop_string(int rtype) { - const char *str = "shop"; /* catchall */ - if (rtype == SHOPBASE - 1) { + extern const struct shclass shtypes[]; /* defined in shknam.c */ + int shoptype = rtype - SHOPBASE; /* convert room type to shop type */ + const char *str = "shop?"; /* catchall */ + + if (shoptype < 0) { str = "untended shop"; - } - else if (rtype >= SHOPBASE) { - str = get_shtype(rtype)->noun_name; + } else if (shtypes[shoptype].annotation) { + str = shtypes[shoptype].annotation; + } else if (shtypes[shoptype].name) { + str = shtypes[shoptype].name; } return str; } /* if player knows about the mastermind tune, append it to Castle annotation; if drawbridge has been destroyed, flags.castletune will be zero */ -static char * -tunesuffix(mapseen *mptr, char *outbuf, - size_t bsz) /* sz of outbuf */ +staticfn char * +tunesuffix( + mapseen *mptr, + char *outbuf, + size_t bsz) /* size of outbuf */ { *outbuf = '\0'; if (mptr->flags.castletune && u.uevent.uheard_tune) { char tmp[BUFSZ]; if (u.uevent.uheard_tune == 2) - Sprintf(tmp, "notes \"%s\"", gt.tune); + Sprintf(tmp, "notes \"%s\"", svt.tune); else Strcpy(tmp, "5-note tune"); Snprintf(outbuf, bsz, " (play %s to open or close drawbridge)", tmp); @@ -3433,19 +3464,33 @@ tunesuffix(mapseen *mptr, char *outbuf, #endif #define COMMA (i++ > 0 ? ", " : PREFIX) /* "iterate" once; safe to use as ``if (cond) ADDTOBUF(); else whatever;'' */ -#define ADDNTOBUF(nam, var) \ +#define ADDTOBUF(nam, var) \ + do { \ + if (var) \ + Sprintf(eos(buf), "%s%s", COMMA, (nam)); \ + } while (0) +#define ADDNTOBUF(nam, var) \ do { \ if (var) \ Sprintf(eos(buf), "%s%s %s%s", COMMA, seen_string((var), (nam)), \ (nam), plur(var)); \ } while (0) -#define ADDTOBUF(nam, var) \ - do { \ - if (var) \ - Sprintf(eos(buf), "%s%s", COMMA, (nam)); \ +/* ADD2NTOBUF: for "M temples and N altars"; seen_string() is safe to use + multiple times within one expression; so is plur() */ +#define ADD2NTOBUF(nam, var, nam2, var2) \ + do { \ + if (var && var2) { \ + Sprintf(eos(buf), "%s%s %s%s and %s %s%s", COMMA, \ + seen_string((var), (nam)), (nam), plur(var), \ + seen_string((var2), (nam2)), (nam2), plur(var2)); \ + } else if (var) { \ + ADDNTOBUF(nam, var); \ + } else if (var2) { \ + ADDNTOBUF(nam2, var2); \ + } \ } while (0) -static void +staticfn void print_mapseen( winid win, mapseen *mptr, int final, /* -1: as menu; 0: not final; @@ -3466,26 +3511,24 @@ print_mapseen( if (dnum == quest_dnum || dnum == knox_level.dnum) depthstart = 1; else - depthstart = gd.dungeons[dnum].depth_start; + depthstart = svd.dungeons[dnum].depth_start; if (printdun) { - if (gd.dungeons[dnum].dunlev_ureached == gd.dungeons[dnum].entry_lev + if (svd.dungeons[dnum].dunlev_ureached == svd.dungeons[dnum].entry_lev /* suppress the negative numbers in the endgame */ || In_endgame(&mptr->lev)) - Sprintf(buf, "%s:", gd.dungeons[dnum].dname); + Sprintf(buf, "%s:", svd.dungeons[dnum].dname); else if (builds_up(&mptr->lev)) Sprintf(buf, "%s: levels %d up to %d", - gd.dungeons[dnum].dname, - depthstart + gd.dungeons[dnum].entry_lev - 1, - depthstart + gd.dungeons[dnum].dunlev_ureached - 1); + svd.dungeons[dnum].dname, + depthstart + svd.dungeons[dnum].entry_lev - 1, + depthstart + svd.dungeons[dnum].dunlev_ureached - 1); else Sprintf(buf, "%s: levels %d to %d", - gd.dungeons[dnum].dname, depthstart, - depthstart + gd.dungeons[dnum].dunlev_ureached - 1); - any = cg.zeroany; - add_menu(win, &nul_glyphinfo, &any, 0, 0, - !final ? iflags.menu_headings : ATR_SUBHEAD, NO_COLOR, - buf, MENU_ITEMFLAGS_NONE); + svd.dungeons[dnum].dname, depthstart, + depthstart + svd.dungeons[dnum].dunlev_ureached - 1); + + add_menu_heading(win, buf); } /* calculate level number */ @@ -3518,7 +3561,7 @@ print_mapseen( add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_PREFORM, NO_COLOR, buf, MENU_ITEMFLAGS_NONE); - if (INTEREST(mptr->feat)) { + if (OF_INTEREST(mptr->feat)) { buf[0] = 0; i = 0; /* interest counter */ @@ -3532,14 +3575,16 @@ print_mapseen( Sprintf(eos(buf), "%s%s", COMMA, an(shop_string(mptr->feat.shoptype))); } - if (mptr->feat.naltar > 0) { + if (mptr->feat.naltar > 0 || mptr->feat.ntemple > 0) { unsigned atmp; - /* Temples + non-temple altars get munged into just "altars" */ - if (mptr->feat.ntemple != mptr->feat.naltar) - ADDNTOBUF("altar", mptr->feat.naltar); - else - ADDNTOBUF("temple", mptr->feat.ntemple); + /* being aware of a temple doesn't guarantee being aware of its + altar (via entrance message when entering while blinded, or + possibly it being out of view in an irregularly shaped room); + FIXME: if all temples present have been desecrated, we ought + to say so */ + ADD2NTOBUF("temple", mptr->feat.ntemple, + "altar", mptr->feat.naltar); /* only print out altar's god if they are all to your god */ atmp = mptr->feat.msalign; /* 0, 1, 2, 3 */ @@ -3562,9 +3607,7 @@ print_mapseen( buf[i] = highc(buf[i]); /* capitalizing it makes it a sentence; terminate with '.' */ Strcat(buf, "."); - any = cg.zeroany; - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_PREFORM, NO_COLOR, - buf, MENU_ITEMFLAGS_NONE); + add_menu_str(win, buf); } /* we assume that these are mutually exclusive */ @@ -3578,7 +3621,7 @@ print_mapseen( Sprintf(buf, "%sA very big room.", PREFIX); } else if (on_level(&mptr->lev, &qstart_level)) { Sprintf(buf, "%sHome%s.", PREFIX, - mptr->flags.unreachable ? " (no way back...)" : ""); + mptr->flags.notreachable ? " (no way back...)" : ""); if (u.uevent.qcompleted) Sprintf(buf, "%sCompleted quest for %s.", PREFIX, ldrname()); else if (mptr->flags.questing) @@ -3599,22 +3642,18 @@ print_mapseen( Sprintf(buf, "%sMoloch's Sanctum.", PREFIX); } if (*buf) { - any = cg.zeroany; - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_PREFORM, NO_COLOR, - buf, MENU_ITEMFLAGS_NONE); + add_menu_str(win, buf); } - /* quest entrance is not mutually-exclusive with bigroom or rogue level */ + /* quest entrance is not mutually-exclusive with bigroom */ if (mptr->flags.quest_summons) { Sprintf(buf, "%sSummoned by %s.", PREFIX, ldrname()); - any = cg.zeroany; - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, NO_COLOR, - buf, MENU_ITEMFLAGS_NONE); + add_menu_str(win, buf); } /* print out branches */ if (mptr->br) { Sprintf(buf, "%s%s to %s", PREFIX, br_string2(mptr->br), - gd.dungeons[mptr->br->end2.dnum].dname); + svd.dungeons[mptr->br->end2.dnum].dname); /* Since mapseen objects are printed out in increasing order * of dlevel, clarify which level this branch is going to @@ -3623,9 +3662,7 @@ print_mapseen( if (mptr->br->end1_up && !In_endgame(&(mptr->br->end2))) Sprintf(eos(buf), ", level %d", depth(&(mptr->br->end2))); Strcat(buf, "."); - any = cg.zeroany; - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_PREFORM, NO_COLOR, - buf, MENU_ITEMFLAGS_NONE); + add_menu_str(win, buf); } /* maybe print out bones details */ @@ -3638,9 +3675,7 @@ print_mapseen( ++kncnt; if (kncnt) { Sprintf(buf, "%s%s", PREFIX, "Final resting place for"); - any = cg.zeroany; - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_PREFORM, NO_COLOR, - buf, MENU_ITEMFLAGS_NONE); + add_menu_str(win, buf); if (died_here) { /* disclosure occurs before bones creation, so listing dead hero here doesn't give away whether bones are produced */ @@ -3652,18 +3687,13 @@ print_mapseen( (void) strsubst(tmpbuf, " her ", " your "); Snprintf(buf, sizeof(buf), "%s%syou, %s%c", PREFIX, TAB, tmpbuf, --kncnt ? ',' : '.'); - any = cg.zeroany; - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_PREFORM, NO_COLOR, - buf, MENU_ITEMFLAGS_NONE); + add_menu_str(win, buf); } for (bp = mptr->final_resting_place; bp; bp = bp->next) { if (bp->bonesknown || wizard || final > 0) { Sprintf(buf, "%s%s%s, %s%c", PREFIX, TAB, bp->who, bp->how, --kncnt ? ',' : '.'); - any = cg.zeroany; - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_PREFORM, NO_COLOR, - buf, MENU_ITEMFLAGS_NONE); + add_menu_str(win, buf); } } } @@ -3700,5 +3730,8 @@ undiscovered_bones(void) return FALSE; } +#undef OF_INTEREST +#undef ADDNTOBUF +#undef ADDTOBUF /*dungeon.c*/ diff --git a/src/eat.c b/src/eat.c index 0e15218178..28d62f3ea3 100644 --- a/src/eat.c +++ b/src/eat.c @@ -1,63 +1,63 @@ -/* NetHack 3.7 eat.c $NHDT-Date: 1674294705 2023/01/21 09:51:45 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.301 $ */ +/* NetHack 3.7 eat.c $NHDT-Date: 1715177703 2024/05/08 14:15:03 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.334 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" -static int eatmdone(void); -static int eatfood(void); -static struct obj *costly_tin(int); -static int opentin(void); -static int unfaint(void); - -static const char *food_xname(struct obj *, boolean); -static void choke(struct obj *); -static void recalc_wt(void); -static struct obj *touchfood(struct obj *) NONNULL; -static void do_reset_eat(void); -static void maybe_extend_timed_resist(int); -static void done_eating(boolean); -static void cprefx(int); -static boolean temp_givit(int, struct permonst *); -static void givit(struct permonst *); -static void eye_of_newt_buzz(struct permonst *); -static void cpostfx(int); -static void consume_tin(const char *); -static void start_tin(struct obj *); -static int eatcorpse(struct obj *); -static void start_eating(struct obj *, boolean); -static void garlic_breath(struct monst *); -static void fprefx(struct obj *); -static void fpostfx(struct obj *); -static int bite(void); -static int edibility_prompts(struct obj *); -static int doeat_nonfood(struct obj *); -static int tinopen_ok(struct obj *); -static int rottenfood(struct obj *); -static void eatspecial(void); -static int bounded_increase(int, int, int); -static void accessory_has_effect(struct obj *); -static void eataccessory(struct obj *); -static const char *foodword(struct obj *); -static boolean maybe_cannibal(int, boolean); -static int eat_ok(struct obj *); -static int offer_ok(struct obj *); -static int tin_ok(struct obj *); +staticfn int eatmdone(void); +staticfn int eatfood(void); +staticfn struct obj *costly_tin(int); +staticfn int opentin(void); +staticfn int unfaint(void); + +staticfn const char *food_xname(struct obj *, boolean); +staticfn void choke(struct obj *); +staticfn void recalc_wt(void); +staticfn int adj_victual_nutrition(void); +staticfn struct obj *touchfood(struct obj *); +staticfn void do_reset_eat(void); +staticfn void done_eating(boolean); +staticfn void cprefx(int); +staticfn boolean temp_givit(int, struct permonst *); +staticfn void givit(struct permonst *); +staticfn void eye_of_newt_buzz(struct permonst *); +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 *); +staticfn void start_eating(struct obj *, boolean); +staticfn void garlic_breath(struct monst *); +staticfn boolean fprefx(struct obj *); +staticfn void fpostfx(struct obj *); +staticfn int bite(void); +staticfn int edibility_prompts(struct obj *); +staticfn int doeat_nonfood(struct obj *); +staticfn int tinopen_ok(struct obj *); +staticfn int rottenfood(struct obj *); +staticfn void eatspecial(void); +staticfn int bounded_increase(int, int, int); +staticfn void accessory_has_effect(struct obj *); +staticfn void eataccessory(struct obj *); +staticfn const char *foodword(struct obj *); +staticfn int tin_variety(struct obj *, boolean); +staticfn boolean maybe_cannibal(int, boolean); +staticfn int eat_ok(struct obj *); +staticfn int offer_ok(struct obj *); +staticfn int tin_ok(struct obj *); /* also used to see if you're allowed to eat cats and dogs */ #define CANNIBAL_ALLOWED() (Role_if(PM_CAVE_DWELLER) || Race_if(PM_ORC)) -/* monster types that cause hero to be turned into stone if eaten */ -#define flesh_petrifies(pm) (touch_petrifies(pm) || (pm) == &mons[PM_MEDUSA]) - /* Rider corpses are treated as non-rotting so that attempting to eat one will be sure to reach the stage of eating where that meal is fatal; acid blob corpses eventually rot away to nothing but before that happens they can be sacrificed regardless of age which implies that they never become rotten */ #define nonrotting_corpse(mnum) \ - ((mnum) == PM_LIZARD || (mnum) == PM_LICHEN || is_rider(&mons[mnum]) \ + ((mnum) == PM_LIZARD || (mnum) == PM_LICHEN \ + || is_rider(&mons[mnum]) \ || (mnum) == PM_ACID_BLOB) /* non-rotting non-corpses; unlike lizard corpses, these items will behave @@ -65,7 +65,8 @@ static int tin_ok(struct obj *); #define nonrotting_food(otyp) \ ((otyp) == LEMBAS_WAFER || (otyp) == CRAM_RATION) -/* see hunger states in hack.h - texts used on bottom line */ +/* see hunger states in hack.h - texts used on bottom line + Also used in botl.c and insight.c */ const char *const hu_stat[] = { "Satiated", " ", "Hungry ", "Weak ", "Fainting", "Fainted ", "Starved " @@ -87,7 +88,7 @@ static int getobj_else = 0; * polymorphed character. Not used for monster checks. */ boolean -is_edible(register struct obj *obj) +is_edible(struct obj *obj) { /* protect invocation tools but not Rider corpses (handled elsewhere)*/ /* if (obj->oclass != FOOD_CLASS && obj_resists(obj, 0, 0)) */ @@ -122,7 +123,7 @@ is_edible(register struct obj *obj) void init_uhunger(void) { - gc.context.botl = (u.uhs != NOT_HUNGRY || ATEMP(A_STR) < 0); + disp.botl = (u.uhs != NOT_HUNGRY || ATEMP(A_STR) < 0); u.uhunger = 900; u.uhs = NOT_HUNGRY; if (ATEMP(A_STR) < 0) { @@ -157,8 +158,8 @@ static const struct { { "", 0, 0, 0 } }; #define TTSZ SIZE(tintxts) -/* called after mimicing is over */ -static int +/* called after mimicking is over */ +staticfn int eatmdone(void) { /* release `eatmbuf' */ @@ -212,7 +213,7 @@ eatmupdate(void) } /* ``[the(] singular(food, xname) [)]'' */ -static const char * +staticfn const char * food_xname(struct obj *food, boolean the_pfx) { const char *result; @@ -240,7 +241,7 @@ food_xname(struct obj *food, boolean the_pfx) * * To a full belly all food is bad. (It.) */ -static void +staticfn void choke(struct obj *food) { /* only happens if you were satiated */ @@ -254,17 +255,17 @@ choke(struct obj *food) exercise(A_CON, FALSE); - if (Breathless || (!Strangled && !rn2(20))) { + if (Breathless || Hunger || (!Strangled && !rn2(20))) { /* choking by eating AoS doesn't involve stuffing yourself */ if (food && food->otyp == AMULET_OF_STRANGULATION) { You("choke, but recover your composure."); return; } You("stuff yourself and then vomit voluminously."); - morehungry(1000); /* you just got *very* sick! */ + morehungry(Hunger ? (u.uhunger - 60) : 1000); /* just got very sick! */ vomit(); } else { - gk.killer.format = KILLED_BY_AN; + svk.killer.format = KILLED_BY_AN; /* * Note all "killer"s below read "Choked on %s" on the * high score list & tombstone. So plan accordingly. @@ -272,25 +273,25 @@ choke(struct obj *food) if (food) { You("choke over your %s.", foodword(food)); if (food->oclass == COIN_CLASS) { - Strcpy(gk.killer.name, "very rich meal"); + Strcpy(svk.killer.name, "very rich meal"); } else { - gk.killer.format = KILLED_BY; - Strcpy(gk.killer.name, killer_xname(food)); + svk.killer.format = KILLED_BY; + Strcpy(svk.killer.name, killer_xname(food)); } } else { You("choke over it."); - Strcpy(gk.killer.name, "quick snack"); + Strcpy(svk.killer.name, "quick snack"); } You("die..."); done(CHOKING); } } -/* modify object wt. depending on time spent consuming it */ -static void +/* modify victual.piece->owt depending on time spent consuming it */ +staticfn void recalc_wt(void) { - struct obj *piece = gc.context.victual.piece; + struct obj *piece = svc.context.victual.piece; if (!piece) { impossible("recalc_wt without piece"); @@ -298,7 +299,7 @@ recalc_wt(void) } debugpline1("Old weight = %d", piece->owt); debugpline2("Used time = %d, Req'd time = %d", - gc.context.victual.usedtime, gc.context.victual.reqtime); + svc.context.victual.usedtime, svc.context.victual.reqtime); piece->owt = weight(piece); debugpline1("New weight = %d", piece->owt); } @@ -310,14 +311,16 @@ reset_eat(void) /* we only set a flag here - the actual reset process is done after * the round is spent eating. */ - if (gc.context.victual.eating && !gc.context.victual.doreset) { + if (svc.context.victual.eating && !svc.context.victual.doreset) { debugpline0("reset_eat..."); - gc.context.victual.doreset = 1; + svc.context.victual.doreset = 1; } return; } -/* base nutrition of a food-class object */ +/* base nutrition of a food-class object; this used to include a variation + of the code that is now in adj_victual_nutrition() and was moved due to + its affect on weight() */ unsigned obj_nutrition(struct obj *otmp) { @@ -325,24 +328,35 @@ obj_nutrition(struct obj *otmp) : otmp->globby ? otmp->owt : (unsigned) objects[otmp->otyp].oc_nutrition; - if (otmp->otyp == LEMBAS_WAFER) { + return nut; +} + +/* nutrition increment for next byte; this used to be factored into + victual.piece->oeaten but that produced weight change if hero + polymorphed to or from one of the races which has nutrition adjusted */ +staticfn int +adj_victual_nutrition(void) +{ + int otyp = svc.context.victual.piece->otyp; + /* note: adj_victual_nutrition() is only called when 'nmod' is negative */ + int nut = -svc.context.victual.nmod; /* convert 'nmod' to positive */ + + assert(nut > 0); + if (otyp == LEMBAS_WAFER) { if (maybe_polyd(is_elf(gy.youmonst.data), Race_if(PM_ELF))) - nut += nut / 4; /* 800 -> 1000 */ + nut += (nut + 2) / 4; /* 800 -> 1000 */ else if (maybe_polyd(is_orc(gy.youmonst.data), Race_if(PM_ORC))) - nut -= nut / 4; /* 800 -> 600 */ - /* prevent polymorph making a partly eaten wafer - become more nutritious than an untouched one */ - if (otmp->oeaten >= nut) - otmp->oeaten = (otmp->oeaten < objects[LEMBAS_WAFER].oc_nutrition) - ? (nut - 1) : nut; - } else if (otmp->otyp == CRAM_RATION) { + nut -= (nut + 2) / 4; /* 800 -> 600 */ + } else if (otyp == CRAM_RATION) { if (maybe_polyd(is_dwarf(gy.youmonst.data), Race_if(PM_DWARF))) - nut += nut / 6; /* 600 -> 700 */ + nut += (nut + 3) / 6; /* 600 -> 700 */ } + nut = max(nut, 1); return nut; } -static struct obj * +/* might destroy otmp if hero drops it */ +staticfn struct obj * touchfood(struct obj *otmp) { if (otmp->quan > 1L) { @@ -360,14 +374,14 @@ touchfood(struct obj *otmp) if (carried(otmp)) { freeinv(otmp); - if (inv_cnt(FALSE) >= 52) { + if (inv_cnt(FALSE) >= invlet_basic) { sellobj_state(SELL_DONTSELL); dropy(otmp); sellobj_state(SELL_NORMAL); + if (otmp->where == OBJ_DELETED) + otmp = (struct obj *) NULL; } else { - otmp->nomerge = 1; /* used to prevent merge */ - otmp = addinv(otmp); - otmp->nomerge = 0; + otmp = addinv_nomerge(otmp); } } return otmp; @@ -381,8 +395,8 @@ touchfood(struct obj *otmp) void food_disappears(struct obj *obj) { - if (obj == gc.context.victual.piece) - gc.context.victual = zero_victual; /* victual.piece = 0, .o_id = 0 */ + if (obj == svc.context.victual.piece) + svc.context.victual = zero_victual; /* victual.piece = 0, .o_id = 0 */ if (obj->timed) obj_stop_timers(obj); @@ -394,29 +408,34 @@ food_disappears(struct obj *obj) void food_substitution(struct obj *old_obj, struct obj *new_obj) { - if (old_obj == gc.context.victual.piece) { - gc.context.victual.piece = new_obj; - gc.context.victual.o_id = new_obj->o_id; + if (old_obj == svc.context.victual.piece) { + svc.context.victual.piece = new_obj; + svc.context.victual.o_id = new_obj->o_id; } - if (old_obj == gc.context.tin.tin) { - gc.context.tin.tin = new_obj; - gc.context.tin.o_id = new_obj->o_id; + if (old_obj == svc.context.tin.tin) { + svc.context.tin.tin = new_obj; + svc.context.tin.o_id = new_obj->o_id; } } -static void +staticfn void do_reset_eat(void) { debugpline0("do_reset_eat..."); - if (gc.context.victual.piece) { - gc.context.victual.o_id = 0; - gc.context.victual.piece = touchfood(gc.context.victual.piece); - gc.context.victual.o_id = gc.context.victual.piece->o_id; - recalc_wt(); - } - gc.context.victual.fullwarn - = gc.context.victual.eating - = gc.context.victual.doreset + if (svc.context.victual.piece) { + struct obj *otmp; + + svc.context.victual.o_id = 0; + otmp = touchfood(svc.context.victual.piece); + svc.context.victual.piece = otmp; + if (otmp) { + svc.context.victual.o_id = otmp->o_id; + recalc_wt(); + } + } + svc.context.victual.fullwarn + = svc.context.victual.eating + = svc.context.victual.doreset = 0; /* Do not set canchoke to FALSE; if we continue eating the same object * we need to know if canchoke was set when they started eating it the @@ -429,7 +448,7 @@ do_reset_eat(void) /* if 'prop' is only set because of a timed value (so not an intrinsic attribute or because of polymorph shape or worn or carried gear), return - its timeout, otherwise return 0 */ + its timeout, otherwise return 0; used by enlightenment */ long temp_resist(int prop) { @@ -449,8 +468,35 @@ temp_resist(int prop) return 0L; } +/* if temporary acid or stoning resistance is timing out while eating + something which that resistance is protecting against, caller will + extend resistance's duration so that it times out after meal finishes */ +boolean +eating_dangerous_corpse(int res) +{ + struct obj *food; + int mnum; + + if (go.occupation == eatfood + && (food = svc.context.victual.piece) != 0 + && food->otyp == CORPSE + && (mnum = food->corpsenm) >= LOW_PM + && (carried(food) || obj_here(food, u.ux, u.uy))) { + + if (res == ACID_RES && acidic(&mons[mnum])) + return TRUE; + /* flesh_petrifies() includes Medusa as well as touch_petrifies() */ + if (res == STONE_RES && flesh_petrifies(&mons[mnum])) + return TRUE; + } + return FALSE; +} + +#if 0 /* no longer used */ +staticfn void maybe_extend_timed_resist(int); + /* if temp resist against 'prop' is about to timeout, extend it slightly */ -static void +staticfn void maybe_extend_timed_resist(int prop) { long timeout = temp_resist(prop); @@ -466,12 +512,13 @@ maybe_extend_timed_resist(int prop) set_itimeout(&u.uprops[prop].intrinsic, 2L); } } +#endif /* called each move during eating process */ -static int +staticfn int eatfood(void) { - struct obj *food = gc.context.victual.piece; + struct obj *food = svc.context.victual.piece; if (food && !carried(food) && !obj_here(food, u.ux, u.uy)) food = 0; @@ -480,29 +527,10 @@ eatfood(void) do_reset_eat(); return 0; } - if (!gc.context.victual.eating) + if (!svc.context.victual.eating) return 0; - /* - * We don't want temporary acid resistance to timeout while eating - * an acidic corpse or temporary stoning resistance to do that while - * eating a cockatrice corpse. Protection is checked at the start - * of the meal and having it go away mid-meal with a message about - * increased vulnerability but no consequences is too obviously wrong, - * but also too nit-picky to deal with. - * - * (Tins aren't handled by eatfood() and wouldn't need this anyway - * because they're finished in one turn once they've been opened. - * Come to think of it, eggs are probably eaten in one turn too.) - */ - if ((food->otyp == CORPSE || food->otyp == EGG) - && food->corpsenm >= LOW_PM && acidic(&mons[food->corpsenm])) - maybe_extend_timed_resist(ACID_RES); - if ((food->otyp == CORPSE || food->otyp == EGG) - && food->corpsenm >= LOW_PM && touch_petrifies(&mons[food->corpsenm])) - maybe_extend_timed_resist(STONE_RES); - - if (++gc.context.victual.usedtime <= gc.context.victual.reqtime) { + if (++svc.context.victual.usedtime <= svc.context.victual.reqtime) { if (bite()) return 0; return 1; /* still busy */ @@ -512,10 +540,10 @@ eatfood(void) } } -static void +staticfn void done_eating(boolean message) { - struct obj *piece = gc.context.victual.piece; + struct obj *piece = svc.context.victual.piece; piece->in_use = TRUE; go.occupation = 0; /* do this early, so newuhs() knows we're done */ @@ -541,7 +569,7 @@ done_eating(boolean message) else useupf(piece, 1L); - gc.context.victual = zero_victual; /* victual.piece = 0, .o_id = 0 */ + svc.context.victual = zero_victual; /* victual.piece = 0, .o_id = 0 */ } void @@ -636,9 +664,9 @@ eat_brains( return M_ATTK_MISS; } else if (is_rider(pd)) { pline("Ingesting that is fatal."); - Sprintf(gk.killer.name, "unwisely ate the brain of %s", + Sprintf(svk.killer.name, "unwisely ate the brain of %s", pmname(pd, Mgender(mdef))); - gk.killer.format = NO_KILLER_PREFIX; + svk.killer.format = NO_KILLER_PREFIX; done(DIED); /* life-saving needed to reach here */ exercise(A_WIS, FALSE); @@ -650,7 +678,7 @@ eat_brains( ABASE(A_INT) += rnd(4); if (ABASE(A_INT) > AMAX(A_INT)) ABASE(A_INT) = AMAX(A_INT); - gc.context.botl = 1; + disp.botl = TRUE; } exercise(A_WIS, TRUE); *dmg_p += xtra_dmg; @@ -668,8 +696,8 @@ eat_brains( static NEARDATA const char brainlessness[] = "brainlessness"; if (Lifesaved) { - Strcpy(gk.killer.name, brainlessness); - gk.killer.format = KILLED_BY; + Strcpy(svk.killer.name, brainlessness); + svk.killer.format = KILLED_BY; done(DIED); /* amulet of life saving has now been used up */ pline("Unfortunately your brain is still gone."); @@ -679,8 +707,8 @@ eat_brains( } else { Your("last thought fades away."); } - Strcpy(gk.killer.name, brainlessness); - gk.killer.format = KILLED_BY; + Strcpy(svk.killer.name, brainlessness); + svk.killer.format = KILLED_BY; done(DIED); /* can only get here when in wizard or explore mode and user has explicitly chosen not to die; arbitrarily boost intelligence */ @@ -723,7 +751,7 @@ eat_brains( } /* eating a corpse or egg of one's own species is usually naughty */ -static boolean +staticfn boolean maybe_cannibal(int pm, boolean allowmsg) { static NEARDATA long ate_brains = 0L; @@ -732,9 +760,9 @@ maybe_cannibal(int pm, boolean allowmsg) /* when poly'd into a mind flayer, multiple tentacle hits in one turn cause multiple digestion checks to occur; avoid giving multiple luck penalties for the same attack */ - if (gm.moves == ate_brains) + if (svm.moves == ate_brains) return FALSE; - ate_brains = gm.moves; /* ate_anything, not just brains... */ + ate_brains = svm.moves; /* ate_anything, not just brains... */ if (!CANNIBAL_ALLOWED() /* non-cannibalistic heroes shouldn't eat own species ever @@ -743,7 +771,7 @@ maybe_cannibal(int pm, boolean allowmsg) about cannibalism--hero's innate traits aren't altered) */ && (your_race(fptr) || (Upolyd && same_race(gy.youmonst.data, fptr)) - || (u.ulycn >= LOW_PM && were_beastie(pm) == u.ulycn))) { + || (ismnum(u.ulycn) && were_beastie(pm) == u.ulycn))) { if (allowmsg) { if (Upolyd && your_race(fptr)) You("have a bad feeling deep inside."); @@ -756,21 +784,25 @@ maybe_cannibal(int pm, boolean allowmsg) return FALSE; } -static void -cprefx(register int pm) +staticfn void +cprefx(int pm) { (void) maybe_cannibal(pm, TRUE); if (flesh_petrifies(&mons[pm])) { if (!Stone_resistance && !(poly_when_stoned(gy.youmonst.data) && polymon(PM_STONE_GOLEM, POLYMON_ALL_MSGS))) { - Sprintf(gk.killer.name, "tasting %s meat", + /* 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]); - gk.killer.format = KILLED_BY; + svk.killer.format = KILLED_BY; You("turn to stone."); done(STONING); - if (gc.context.victual.piece) - gc.context.victual.eating = 0; + if (svc.context.victual.piece) + svc.context.victual.eating = 0; return; /* lifesaved */ } } @@ -797,9 +829,9 @@ cprefx(register int pm) case PM_PESTILENCE: case PM_FAMINE: { pline("Eating that is instantly fatal."); - Sprintf(gk.killer.name, "unwisely ate the body of %s", + Sprintf(svk.killer.name, "unwisely ate the body of %s", mons[pm].pmnames[NEUTRAL]); - gk.killer.format = NO_KILLER_PREFIX; + svk.killer.format = NO_KILLER_PREFIX; done(DIED); /* life-saving needed to reach here */ exercise(A_WIS, FALSE); @@ -807,11 +839,10 @@ cprefx(register int pm) 3.7: this used to assume that such tins were impossible but they can be wished for in wizard mode; they can't make it to normal play though because bones creation empties them */ - if (gc.context.victual.piece /* Null for tins */ - && gc.context.victual.piece->otyp == CORPSE /* paranoia */ - && revive_corpse(gc.context.victual.piece, FALSE)) { - gc.context.victual = zero_victual; /* victual.piece=0, .o_id=0 */ - } + if (svc.context.victual.piece /* Null for tins */ + && svc.context.victual.piece->otyp == CORPSE /* paranoia */ + && revive_corpse(svc.context.victual.piece, FALSE)) + svc.context.victual = zero_victual; /* victual.piece=0, .o_id=0 */ return; } case PM_BLACK_MOLD: { @@ -830,6 +861,7 @@ cprefx(register int pm) make_slimed(10L, (char *) 0); delayed_killer(SLIMED, KILLED_BY_AN, ""); } + FALLTHROUGH; /* Fall through */ default: if (acidic(&mons[pm]) && Stoned) @@ -993,7 +1025,7 @@ should_givit(int type, struct permonst *ptr) return (ptr->mlevel > rn2(chance)); } -static boolean +staticfn boolean temp_givit(int type, struct permonst *ptr) { int chance = (type == STONE_RES) ? 6 : (type == ACID_RES) ? 3 : 0; @@ -1004,7 +1036,7 @@ temp_givit(int type, struct permonst *ptr) /* givit() tries to give you an intrinsic based on the monster's level * and what type of intrinsic it is trying to give you. */ -static void +staticfn void givit(register struct permonst *ptr) { int type = corpse_intrinsic(ptr); @@ -1161,7 +1193,7 @@ corpse_intrinsic(struct permonst *ptr) DISABLE_WARNING_FORMAT_NONLITERAL -static void +staticfn void eye_of_newt_buzz(struct permonst *ptr) { if (rn2(3) || 3 * u.uen <= 2 * u.uenmax) { @@ -1178,7 +1210,7 @@ eye_of_newt_buzz(struct permonst *ptr) if (old_uen != u.uen) { You_feel("a %s buzz.", old_uenmax != u.uenmax ? "moderate" : "mild"); - gc.context.botl = 1; + disp.botl = TRUE; } } } @@ -1186,7 +1218,7 @@ eye_of_newt_buzz(struct permonst *ptr) DISABLE_WARNING_FORMAT_NONLITERAL /* called after completely consuming a corpse */ -static void +staticfn void cpostfx(int pm) { int tmp = 0; @@ -1224,7 +1256,7 @@ cpostfx(int pm) else u.uhp = u.uhpmax; make_blinded(0L, !u.ucreamed); - gc.context.botl = 1; + disp.botl = TRUE; check_intrinsics = TRUE; /* might also convey poison resistance */ break; case PM_STALKER: { @@ -1238,24 +1270,30 @@ cpostfx(int pm) } 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; if (gy.youmonst.data->mlet != S_MIMIC && !Unchanging) { char buf[BUFSZ]; + const char *tempshape = !Hallucination ? "a pile of gold" + : "an orange"; /* Long time debate on whether this should break polyselfless * conduct. In vanilla, it does, but in xNetHack it does not. The @@ -1268,8 +1306,7 @@ cpostfx(int pm) * is to abandon the benefits of turning into other monsters * (second HP bar, many powerful abilities, etc), and mimicking * an object provides none of these. */ - You_cant("resist the temptation to mimic %s.", - Hallucination ? "an orange" : "a pile of gold"); + You_cant("resist the temptation to mimic %s.", tempshape); /* A pile of gold can't ride. */ if (u.usteed) dismount_steed(DISMOUNT_FELL); @@ -1317,6 +1354,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"); @@ -1362,6 +1407,7 @@ cpostfx(int pm) } else { pline("For some reason, that tasted bland."); } + FALLTHROUGH; /*FALLTHRU*/ default: check_intrinsics = TRUE; @@ -1383,7 +1429,7 @@ cpostfx(int pm) givit(ptr); } /* check_intrinsics */ - if (catch_lycanthropy >= LOW_PM) { + if (ismnum(catch_lycanthropy)) { set_ulycn(catch_lycanthropy); retouch_equipment(2); } @@ -1405,18 +1451,18 @@ violated_vegetarian(void) return; } -/* common code to check and possibly charge for 1 gc.context.tin.tin, - * will split() gc.context.tin.tin if necessary */ -static struct obj * +/* common code to check and possibly charge for 1 svc.context.tin.tin, + * will split() svc.context.tin.tin if necessary */ +staticfn struct obj * costly_tin(int alter_type) /* COST_xxx */ { - struct obj *tin = gc.context.tin.tin; + struct obj *tin = svc.context.tin.tin; if (carried(tin) ? tin->unpaid : (costly_spot(tin->ox, tin->oy) && !tin->no_charge)) { if (tin->quan > 1L) { - tin = gc.context.tin.tin = splitobj(tin, 1L); - gc.context.tin.o_id = tin->o_id; + tin = svc.context.tin.tin = splitobj(tin, 1L); + svc.context.tin.o_id = tin->o_id; } costly_alteration(tin, alter_type); } @@ -1482,12 +1528,12 @@ tin_details(struct obj *obj, int mnum, char *buf) void set_tin_variety(struct obj *obj, int forcetype) { - register int r; + int r, mnum = obj->corpsenm; if (forcetype == SPINACH_TIN || (forcetype == HEALTHY_TIN - && (obj->corpsenm == NON_PM /* empty or already spinach */ - || !vegetarian(&mons[obj->corpsenm])))) { /* replace meat */ + && (mnum == NON_PM /* empty or already spinach */ + || !vegetarian(&mons[mnum])))) { /* replace meat */ obj->corpsenm = NON_PM; /* not based on any monster */ obj->spe = 1; /* spinach */ return; @@ -1501,7 +1547,7 @@ set_tin_variety(struct obj *obj, int forcetype) r = forcetype; } else { /* RANDOM_TIN */ r = rn2(TTSZ - 1); /* take your pick */ - if (r == ROTTEN_TIN && nonrotting_corpse(obj->corpsenm)) + if (r == ROTTEN_TIN && (ismnum(mnum) && nonrotting_corpse(mnum))) r = HOMEMADE_TIN; /* lizards don't rot */ } obj->spe = -(r + 1); /* offset by 1 to allow index 0 */ @@ -1509,9 +1555,9 @@ set_tin_variety(struct obj *obj, int forcetype) int tin_variety(struct obj *obj, - boolean disp) /* we're just displaying so leave things alone */ + boolean displ) /* we're just displaying so leave things alone */ { - register int r; + int r, mnum = obj->corpsenm; if (obj->spe == 1) { r = SPINACH_TIN; @@ -1520,35 +1566,52 @@ tin_variety(struct obj *obj, } else if (obj->spe < 0) { r = -(obj->spe); --r; /* get rid of the offset */ - } else + } else { r = rn2(TTSZ - 1); + } - if (!disp && r == HOMEMADE_TIN && !obj->blessed && !rn2(7)) + if (!displ && r == HOMEMADE_TIN && !obj->blessed && !rn2(7)) r = ROTTEN_TIN; /* some homemade tins go bad */ - if (r == ROTTEN_TIN && nonrotting_corpse(obj->corpsenm)) + if (r == ROTTEN_TIN && (ismnum(mnum) && nonrotting_corpse(mnum))) r = HOMEMADE_TIN; /* lizards don't rot */ return r; } -static void +/* 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; - struct obj *tin = gc.context.tin.tin; + 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))) { int lvl = level_difficulty(), dmg = rnd(5 + (lvl < 5 ? lvl : 2 + lvl / 2)); pline("KABOOM!! The tin was booby-trapped!"); - wake_nearby(); + wake_nearby(FALSE); losehp(Maybe_Half_Phys(dmg), "exploding tin", KILLED_BY_AN); exercise(A_STR, FALSE); make_stunned((HStun & TIMEOUT) + (long) dmg, TRUE); tin = costly_tin(COST_DSTROY); - goto use_up_tin; + use_up_tin(tin); + return; } pline1(mesg); /* "You succeed in opening the tin." */ @@ -1559,7 +1622,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 */ @@ -1581,49 +1647,65 @@ 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 (Verbose(0, consume_tin1)) - 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 */ - gc.context.victual = zero_victual; /* victual.piece = 0, .o_id = 0 */ + svc.context.victual = zero_victual; /* victual.piece = 0, .o_id = 0 */ You("consume %s %s.", tintxts[r].txt, mons[mnum].pmnames[NEUTRAL]); 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"); } if (!strcmp(tintxts[r].txt, "szechuan") && rn2(2)) { @@ -1642,11 +1724,12 @@ consume_tin(const char *mesg) tin->dknown = tin->known = 1; } - if (y_n("Eat it?") == 'n') { - if (Verbose(0, consume_tin2)) + 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; } /* @@ -1670,35 +1753,35 @@ 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); - gc.context.tin.tin = (struct obj *) 0; - gc.context.tin.o_id = 0; + if (tin) + use_up_tin(tin); + return; } /* called during each move whilst opening a tin */ -static int +staticfn int opentin(void) { /* perhaps it was stolen (although that should cause interruption) */ - if (!carried(gc.context.tin.tin) - && (!obj_here(gc.context.tin.tin, u.ux, u.uy) + if (!carried(svc.context.tin.tin) + && (!obj_here(svc.context.tin.tin, u.ux, u.uy) || !can_reach_floor(TRUE))) return 0; /* %% probably we should use tinoid */ - if (gc.context.tin.usedtime++ >= 50) { + if (svc.context.tin.usedtime++ >= 50) { You("give up your attempt to open the tin."); return 0; } - if (gc.context.tin.usedtime < gc.context.tin.reqtime) + if (svc.context.tin.usedtime < svc.context.tin.reqtime) return 1; /* still busy */ consume_tin("You succeed in opening the tin."); @@ -1706,11 +1789,11 @@ opentin(void) } /* called when starting to open a tin */ -static void +staticfn void start_tin(struct obj *otmp) { const char *mesg = 0; - register int tmp; + int tmp; if (metallivorous(gy.youmonst.data)) { mesg = "You bite right into the metal tin..."; @@ -1724,7 +1807,8 @@ start_tin(struct obj *otmp) access); 1 turn delay case is non-deterministic: getting interrupted and retrying might yield another 1 turn delay or might open immediately on 2nd (or 3rd, 4th, ...) try */ - tmp = (uwep && uwep->blessed && uwep->otyp == TIN_OPENER) ? 0 : rn2(2); + tmp = (uwep && uwep->blessed && uwep->otyp == TIN_OPENER) ? 0 + : rn2(2); if (!tmp) mesg = "The tin opens like magic!"; else @@ -1768,13 +1852,13 @@ start_tin(struct obj *otmp) tmp = rn1(1 + 500 / ((int) (ACURR(A_DEX) + ACURRSTR)), 10); } - gc.context.tin.tin = otmp; - gc.context.tin.o_id = otmp->o_id; + svc.context.tin.tin = otmp; + svc.context.tin.o_id = otmp->o_id; if (!tmp) { consume_tin(mesg); /* begin immediately */ } else { - gc.context.tin.reqtime = tmp; - gc.context.tin.usedtime = 0; + svc.context.tin.reqtime = tmp; + svc.context.tin.usedtime = 0; set_occupation(opentin, "opening the tin", 0); } return; @@ -1787,16 +1871,17 @@ Hear_again(void) /* Chance of deafness going away while fainted/sleeping/etc. */ if (!rn2(2)) { make_deaf(0L, FALSE); - gc.context.botl = TRUE; + disp.botl = TRUE; } return 0; } /* called on the "first bite" of rotten food */ -static int +staticfn int rottenfood(struct obj *obj) { - pline("Blecch! Rotten %s!", foodword(obj)); + pline("Blecch! %s %s!", + is_rottable(obj) ? "Rotten" : "Awful", foodword(obj)); if (!rn2(4)) { if (Hallucination) You_feel("rather trippy."); @@ -1823,7 +1908,7 @@ rottenfood(struct obj *obj) where = (u.usteed) ? "saddle" : surface(u.ux, u.uy); pline_The("world spins and %s %s.", what, where); incr_itimeout(&HDeaf, duration); - gc.context.botl = TRUE; + disp.botl = TRUE; nomul(-duration); gm.multi_reason = "unconscious from rotten food"; gn.nomovemsg = "You are conscious again."; @@ -1834,18 +1919,21 @@ rottenfood(struct obj *obj) } /* called when a corpse is selected as food */ -static int +staticfn int eatcorpse(struct obj *otmp) { int retcode = 0, tp = 0, mnum = otmp->corpsenm; long rotted = 0L; int ll_conduct = 0; - boolean stoneable = (flesh_petrifies(&mons[mnum]) && !Stone_resistance - && !poly_when_stoned(gy.youmonst.data)), + boolean stoneable, slimeable = (mnum == PM_GREEN_SLIME && !Slimed && !Unchanging && !slimeproof(gy.youmonst.data)), glob = otmp->globby ? TRUE : FALSE; + assert(ismnum(mnum)); + stoneable = (flesh_petrifies(&mons[mnum]) && !Stone_resistance + && !poly_when_stoned(gy.youmonst.data)); + /* KMH, conduct */ if (!vegan(&mons[mnum])) if (!u.uconduct.unvegan++) { @@ -1864,7 +1952,7 @@ eatcorpse(struct obj *otmp) if (!nonrotting_corpse(mnum)) { long age = peek_at_iced_corpse_age(otmp); - rotted = (gm.moves - age) / (10L + rn2(20)); + rotted = (svm.moves - age) / (10L + rn2(20)); if (otmp->cursed) rotted += 2L; else if (otmp->blessed) @@ -1923,7 +2011,7 @@ eatcorpse(struct obj *otmp) } /* delay is weight dependent */ - gc.context.victual.reqtime + svc.context.victual.reqtime = 3 + ((!glob ? mons[mnum].cwt : otmp->owt) >> 6); if (!tp && !nonrotting_corpse(mnum) @@ -1931,7 +2019,9 @@ eatcorpse(struct obj *otmp) || (fiend_adversity(PM_BAALZEBUB) && rnf(1,7)))) { if (rottenfood(otmp)) { otmp->orotten = TRUE; - (void) touchfood(otmp); + otmp = touchfood(otmp); + if (!otmp) + return 1; retcode = 1; } @@ -2042,7 +2132,7 @@ eatcorpse(struct obj *otmp) } /* called as you start to eat */ -static void +staticfn void start_eating(struct obj *otmp, boolean already_partly_eaten) { const char *old_nomovemsg, *save_nomovemsg; @@ -2053,17 +2143,17 @@ start_eating(struct obj *otmp, boolean already_partly_eaten) several such so we don't need to copy the first result before calling it a second time */ fmt_ptr((genericptr_t) otmp), - fmt_ptr((genericptr_t) gc.context.victual.piece)); - debugpline1("reqtime = %d", gc.context.victual.reqtime); + fmt_ptr((genericptr_t) svc.context.victual.piece)); + debugpline1("reqtime = %d", svc.context.victual.reqtime); debugpline1("(original reqtime = %d)", objects[otmp->otyp].oc_delay); - debugpline1("nmod = %d", gc.context.victual.nmod); + debugpline1("nmod = %d", svc.context.victual.nmod); debugpline1("oeaten = %d", otmp->oeaten); - gc.context.victual.fullwarn = gc.context.victual.doreset = 0; - gc.context.victual.eating = 1; + svc.context.victual.fullwarn = svc.context.victual.doreset = 0; + svc.context.victual.eating = 1; if (otmp->otyp == CORPSE || otmp->globby) { - cprefx(gc.context.victual.piece->corpsenm); - if (!gc.context.victual.piece || !gc.context.victual.eating) { + cprefx(svc.context.victual.piece->corpsenm); + if (!svc.context.victual.piece || !svc.context.victual.eating) { /* rider revived, or hero died and was lifesaved */ return; } @@ -2073,7 +2163,7 @@ start_eating(struct obj *otmp, boolean already_partly_eaten) if (bite()) { /* survived choking, finish off food that's nearly done; need this to handle cockatrice eggs, fortune cookies, etc */ - if (++gc.context.victual.usedtime >= gc.context.victual.reqtime) { + if (++svc.context.victual.usedtime >= svc.context.victual.reqtime) { /* don't want done_eating() to issue gn.nomovemsg if it is due to vomit() called by bite() */ save_nomovemsg = gn.nomovemsg; @@ -2086,9 +2176,9 @@ start_eating(struct obj *otmp, boolean already_partly_eaten) return; } - if (++gc.context.victual.usedtime >= gc.context.victual.reqtime) { + if (++svc.context.victual.usedtime >= svc.context.victual.reqtime) { /* print "finish eating" message if they just resumed -dlc */ - done_eating((gc.context.victual.reqtime > 1 + done_eating((svc.context.victual.reqtime > 1 || already_partly_eaten) ? TRUE : FALSE); return; } @@ -2101,11 +2191,11 @@ start_eating(struct obj *otmp, boolean already_partly_eaten) boolean eating_glob(struct obj *glob) { - return (go.occupation == eatfood && glob == gc.context.victual.piece); + return (go.occupation == eatfood && glob == svc.context.victual.piece); } /* scare nearby monster when hero eats garlic */ -static void +staticfn void garlic_breath(struct monst *mtmp) { if (olfaction(mtmp->data) && distu(mtmp->mx, mtmp->my) < 7) @@ -2117,11 +2207,29 @@ garlic_breath(struct monst *mtmp) * marked it 'partly eaten'. Used for non-rotten non-tin non-corpse food. * Messages should use present tense since multi-turn food won't be * finishing at the time they're issued. + * Returns FALSE if eating should not succeed for whatever reason. */ -static void +staticfn boolean fprefx(struct obj *otmp) { switch (otmp->otyp) { + case EGG: + if (otmp->corpsenm == PM_PYROLISK) { + if (carried(otmp)) + useup(otmp); + else + useupf(otmp, 1L); + explode(u.ux, u.uy, -11, d(3, 6), 0, EXPL_FIERY); + return FALSE; + } else if (stale_egg(otmp)) { + pline("Ugh. Rotten egg."); /* perhaps others like it */ + /* increasing existing nausea means that it will take longer + before eventual vomit, but also means that constitution + will be abused more times before illness completes */ + make_vomiting((Vomiting & TIMEOUT) + (long) d(10, 4), TRUE); + } else + goto give_feedback; + break; case FOOD_RATION: /* nutrition 800 */ /* 200+800 remains below 1000+1, the satiation threshold */ if (u.uhunger <= 200) @@ -2147,7 +2255,7 @@ fprefx(struct obj *otmp) /* not cannibalism, but we use similar criteria for deciding whether to be sickened by this meal */ if (rn2(2) && !CANNIBAL_ALLOWED()) - make_vomiting((long) rn1(gc.context.victual.reqtime, 14), + make_vomiting((long) rn1(svc.context.victual.reqtime, 14), FALSE); } break; @@ -2161,7 +2269,7 @@ fprefx(struct obj *otmp) } goto give_feedback; case CARROT: - wake_nearby(); + wake_nearby(FALSE); goto give_feedback; case MEATBALL: case MEAT_STICK: @@ -2170,14 +2278,15 @@ fprefx(struct obj *otmp) goto give_feedback; case CLOVE_OF_GARLIC: if (is_undead(gy.youmonst.data)) { - make_vomiting((long) rn1(gc.context.victual.reqtime, 5), FALSE); + make_vomiting((long) rn1(svc.context.victual.reqtime, 5), FALSE); break; } iter_mons(garlic_breath); + FALLTHROUGH; /*FALLTHRU*/ default: if (otmp->otyp == SLIME_MOLD && !otmp->cursed - && otmp->spe == gc.context.current_fruit) { + && otmp->spe == svc.context.current_fruit) { pline("My, this is a %s %s!", Hallucination ? "primo" : "yummy", singular(otmp, xname)); @@ -2210,12 +2319,6 @@ fprefx(struct obj *otmp) : "Yo' mama"); } #endif - } else if (otmp->otyp == EGG && stale_egg(otmp)) { - pline("Ugh. Rotten egg."); /* perhaps others like it */ - /* increasing existing nausea means that it will take longer - before eventual vomit, but also means that constitution - will be abused more times before illness completes */ - make_vomiting((Vomiting & TIMEOUT) + (long) d(10, 4), TRUE); } else { give_feedback: pline("This %s is %s", singular(otmp, xname), @@ -2229,10 +2332,11 @@ fprefx(struct obj *otmp) } break; /* default */ } /* switch */ + return TRUE; } /* increment a combat intrinsic with limits on its growth */ -static int +staticfn int bounded_increase(int old, int inc, int typ) { int absold, absinc, sgnold, sgninc; @@ -2269,14 +2373,14 @@ bounded_increase(int old, int inc, int typ) return old + inc; } -static void +staticfn void accessory_has_effect(struct obj *otmp) { pline("Magic spreads through your body as you digest the %s.", (otmp->oclass == RING_CLASS) ? "ring" : "amulet"); } -static void +staticfn void eataccessory(struct obj *otmp) { int typ = otmp->otyp; @@ -2376,7 +2480,7 @@ eataccessory(struct obj *otmp) (typ == RIN_PROTECTION) ? otmp->spe : 2, /* fixed amount for amulet */ typ); - gc.context.botl = 1; + disp.botl = TRUE; break; case RIN_FREE_ACTION: /* Give sleep resistance instead */ @@ -2392,7 +2496,7 @@ eataccessory(struct obj *otmp) change_sex(); You("are suddenly very %s!", flags.female ? "feminine" : "masculine"); - gc.context.botl = 1; + disp.botl = TRUE; break; case AMULET_OF_UNCHANGING: /* un-change: it's a pun */ @@ -2430,16 +2534,16 @@ eataccessory(struct obj *otmp) } /* called after eating non-food */ -static void +staticfn void eatspecial(void) { - struct obj *otmp = gc.context.victual.piece; + struct obj *otmp = svc.context.victual.piece; /* lesshungry wants an occupation to handle choke messages correctly */ set_occupation(eatfood, "eating non-food", 0); - lesshungry(gc.context.victual.nmod); + lesshungry(svc.context.victual.nmod); go.occupation = 0; - gc.context.victual = zero_victual; /* victual.piece = 0, .o_id = 0 */ + svc.context.victual = zero_victual; /* victual.piece = 0, .o_id = 0 */ if (otmp->oclass == COIN_CLASS) { if (carried(otmp)) @@ -2523,7 +2627,7 @@ static const char *const foodwords[] = { "plastic", "glass", "rich food", "stone" }; -static const char * +staticfn const char * foodword(struct obj *otmp) { if (otmp->oclass == FOOD_CLASS) @@ -2534,12 +2638,12 @@ foodword(struct obj *otmp) } /* called after consuming (non-corpse) food */ -static void +staticfn void fpostfx(struct obj *otmp) { switch (otmp->otyp) { case SPRIG_OF_WOLFSBANE: - if (u.ulycn >= LOW_PM || is_were(gy.youmonst.data)) + if (ismnum(u.ulycn) || is_were(gy.youmonst.data)) you_unwere(TRUE); break; case CARROT: @@ -2562,23 +2666,23 @@ fpostfx(struct obj *otmp) /* This stuff seems to be VERY healthy! */ gainstr(otmp, 1, TRUE); /* will -1 if cursed */ if (Upolyd) { - u.mh += otmp->cursed ? -rnd(20) : rnd(20), gc.context.botl = TRUE; + u.mh += otmp->cursed ? -rnd(20) : rnd(20), disp.botl = TRUE; if (u.mh > u.mhmax) { if (!rn2(17)) - u.mhmax++; + setuhpmax(u.mhmax + 1, FALSE); u.mh = u.mhmax; } else if (u.mh <= 0) { rehumanize(); } } else { - u.uhp += otmp->cursed ? -rnd(20) : rnd(20), gc.context.botl = TRUE; + u.uhp += otmp->cursed ? -rnd(20) : rnd(20), disp.botl = TRUE; if (u.uhp > u.uhpmax) { if (!rn2(17)) - setuhpmax(u.uhpmax + 1); + setuhpmax(u.uhpmax + 1, FALSE); u.uhp = u.uhpmax; } else if (u.uhp <= 0) { - gk.killer.format = KILLED_BY_AN; - Strcpy(gk.killer.name, "rotten lump of royal jelly"); + svk.killer.format = KILLED_BY_AN; + Strcpy(svk.killer.name, "rotten lump of royal jelly"); done(POISONING); } } @@ -2586,15 +2690,16 @@ fpostfx(struct obj *otmp) heal_legs(0); break; case EGG: - if (otmp->corpsenm >= LOW_PM + if (ismnum(otmp->corpsenm) && flesh_petrifies(&mons[otmp->corpsenm])) { if (!Stone_resistance && !(poly_when_stoned(gy.youmonst.data) && polymon(PM_STONE_GOLEM, POLYMON_ALL_MSGS))) { if (!Stoned) { - Sprintf(gk.killer.name, "%s egg", + Sprintf(svk.killer.name, "%s egg", mons[otmp->corpsenm].pmnames[NEUTRAL]); - make_stoned(5L, (char *) 0, KILLED_BY_AN, gk.killer.name); + make_stoned(5L, (char *) 0, KILLED_BY_AN, + svk.killer.name); } } /* note: no "tastes like chicken" message for eggs */ @@ -2655,9 +2760,9 @@ fpostfx(struct obj *otmp) /* intended for eating a spellbook while polymorphed, but not used; "leather" applied to appearance, not composition, and has been changed to "leathery" to reflect that */ -static boolean leather_cover(struct obj *); +staticfn boolean leather_cover(struct obj *); -static boolean +staticfn boolean leather_cover(struct obj *otmp) { const char *odesc = OBJ_DESCR(objects[otmp->otyp]); @@ -2675,7 +2780,7 @@ leather_cover(struct obj *otmp) * return 1 if the food was dangerous and you chose to stop. * return 2 if the food was dangerous and you chose to eat it anyway. */ -static int +staticfn int edibility_prompts(struct obj *otmp) { /* Blessed food detection grants hero a one-use @@ -2695,7 +2800,7 @@ edibility_prompts(struct obj *otmp) if (cadaver || otmp->otyp == EGG || otmp->otyp == TIN || otmp->otyp == GLOB_OF_GREEN_SLIME) { /* These checks must match those in eatcorpse() */ - stoneorslime = (mnum >= LOW_PM + stoneorslime = (ismnum(mnum) && flesh_petrifies(&mons[mnum]) && !Stone_resistance && !poly_when_stoned(gy.youmonst.data)); @@ -2708,7 +2813,7 @@ edibility_prompts(struct obj *otmp) /* worst case rather than random in this calculation to force prompt */ - rotted = (gm.moves - age) / (10L + 0 /* was rn2(20) */); + rotted = (svm.moves - age) / (10L + 0 /* was rn2(20) */); if (otmp->cursed) rotted += 2L; else if (otmp->blessed) @@ -2782,7 +2887,7 @@ edibility_prompts(struct obj *otmp) return 0; } -static int +staticfn int doeat_nonfood(struct obj *otmp) { int basenutrit; /* nutrition of full item */ @@ -2790,12 +2895,12 @@ doeat_nonfood(struct obj *otmp) boolean nodelicious = FALSE; int material; - gc.context.victual.reqtime = 1; - gc.context.victual.piece = otmp; - gc.context.victual.o_id = otmp->o_id; + svc.context.victual.reqtime = 1; + svc.context.victual.piece = otmp; + svc.context.victual.o_id = otmp->o_id; /* Don't split it, we don't need to if it's 1 move */ - gc.context.victual.usedtime = 0; - gc.context.victual.canchoke = (u.uhs == SATIATED); + svc.context.victual.usedtime = 0; + svc.context.victual.canchoke = (u.uhs == SATIATED); /* Note: gold weighs 1 pt. for each 1000 pieces (see pickup.c) so gold and non-gold is consistent. */ if (otmp->oclass == COIN_CLASS) @@ -2812,8 +2917,8 @@ doeat_nonfood(struct obj *otmp) nodelicious = TRUE; } #endif - gc.context.victual.nmod = basenutrit; - gc.context.victual.eating = 1; /* needed for lesshungry() */ + svc.context.victual.nmod = basenutrit; + svc.context.victual.eating = 1; /* needed for lesshungry() */ if (!u.uconduct.food++) { ll_conduct++; @@ -2825,15 +2930,15 @@ doeat_nonfood(struct obj *otmp) || material == DRAGON_HIDE || material == WAX) { if (!u.uconduct.unvegan++ && !ll_conduct) { livelog_printf(LL_CONDUCT, - "consumed animal products for the first time, by eating %s", - an(food_xname(otmp, FALSE))); + "consumed animal products for the first time, by eating %s", + an(food_xname(otmp, FALSE))); ll_conduct++; } if (material != WAX) { if (!u.uconduct.unvegetarian && !ll_conduct) livelog_printf(LL_CONDUCT, - "tasted meat by-products for the first time, by eating %s", - an(food_xname(otmp, FALSE))); + "tasted meat by-products for the first time, by eating %s", + an(food_xname(otmp, FALSE))); violated_vegetarian(); } } @@ -2892,8 +2997,8 @@ doeat(void) } } - /* from floorfood(), &zeroobj means iron bars at current spot */ - if (otmp == &cg.zeroobj) { + /* from floorfood(), &hands_obj means iron bars at current spot */ + if (otmp == &hands_obj) { /* hero in metallivore form is eating [diggable] iron bars at current location so skip the other assorted checks; operates as if digging rather than via the eat occupation */ @@ -2966,9 +3071,10 @@ doeat(void) if (otmp->oclass != FOOD_CLASS) return doeat_nonfood(otmp); - if (otmp == gc.context.victual.piece) { - boolean one_bite_left - = (gc.context.victual.usedtime + 1 >= gc.context.victual.reqtime); + + if (otmp == svc.context.victual.piece) { + boolean one_bite_left = (svc.context.victual.usedtime + 1 + >= svc.context.victual.reqtime); /* If they weren't able to choke, they don't suddenly become able to * choke just because they were interrupted. On the other hand, if @@ -2976,17 +3082,23 @@ doeat(void) * they shouldn't be able to choke now. */ if (u.uhs != SATIATED) - gc.context.victual.canchoke = 0; - gc.context.victual.o_id = 0; - gc.context.victual.piece = touchfood(otmp); - gc.context.victual.o_id = gc.context.victual.piece->o_id; + svc.context.victual.canchoke = 0; + svc.context.victual.o_id = 0; + otmp = touchfood(otmp); + if (otmp) { + svc.context.victual.piece = otmp; + svc.context.victual.o_id = otmp->o_id; + } else { + do_reset_eat(); + } /* if there's only one bite left, there sometimes won't be any "you finish eating" message when done; use different wording for resuming with one bite remaining instead of trying to determine whether or not "you finish" is going to be given */ You("%s your meal.", !one_bite_left ? "resume" : "consume the last bite of"); - start_eating(gc.context.victual.piece, FALSE); + if (otmp) + start_eating(otmp, FALSE); return ECMD_TIME; } @@ -3006,9 +3118,15 @@ doeat(void) } already_partly_eaten = otmp->oeaten ? TRUE : FALSE; - gc.context.victual.piece = otmp = touchfood(otmp); - gc.context.victual.o_id = gc.context.victual.piece->o_id; - gc.context.victual.usedtime = 0; + otmp = touchfood(otmp); + if (otmp) { + svc.context.victual.piece = otmp; + svc.context.victual.o_id = otmp->o_id; + svc.context.victual.usedtime = 0; + } else { + do_reset_eat(); + return ECMD_TIME; + } /* Now we need to calculate delay and nutritional info. * The base nutrition calculated here and in eatcorpse() accounts @@ -3020,7 +3138,7 @@ doeat(void) if (tmp == 2) { /* used up */ - gc.context.victual = zero_victual; /* victual.piece=0, .o_id=0 */ + svc.context.victual = zero_victual; /* victual.piece=0, .o_id=0 */ return ECMD_TIME; } else if (tmp) dont_start = TRUE; @@ -3057,7 +3175,7 @@ doeat(void) break; } - gc.context.victual.reqtime = objects[otmp->otyp].oc_delay; + svc.context.victual.reqtime = objects[otmp->otyp].oc_delay; if (otmp->otyp != FORTUNE_COOKIE && (otmp->cursed || (fiend_adversity(PM_JUIBLEX) && rnf(1,7))) && !nonrotting_food(otmp->otyp)) { @@ -3067,10 +3185,13 @@ doeat(void) } consume_oeaten(otmp, 1); /* oeaten >>= 1 */ } else if (!already_partly_eaten) { - fprefx(otmp); + if (!fprefx(otmp)) { + do_reset_eat(); + return ECMD_TIME; + } } else { You("%s %s.", - (gc.context.victual.reqtime == 1) ? "eat" : "begin eating", + (svc.context.victual.reqtime == 1) ? "eat" : "begin eating", doname(otmp)); } } @@ -3080,38 +3201,40 @@ doeat(void) debugpline3( "before rounddiv: victual.reqtime == %d, oeaten == %d, basenutrit == %d", - gc.context.victual.reqtime, otmp->oeaten, basenutrit); + svc.context.victual.reqtime, otmp->oeaten, basenutrit); - gc.context.victual.reqtime + svc.context.victual.reqtime = (basenutrit == 0) ? 0 - : rounddiv(gc.context.victual.reqtime * (long) otmp->oeaten, + : rounddiv(svc.context.victual.reqtime * (long) otmp->oeaten, basenutrit); debugpline1("after rounddiv: victual.reqtime == %d", - gc.context.victual.reqtime); + svc.context.victual.reqtime); /* * calculate the modulo value (nutrit. units per round eating) * note: this isn't exact - you actually lose a little nutrition due * to this method. * TODO: add in a "remainder" value to be given at the end of the meal. */ - if (gc.context.victual.reqtime == 0 || otmp->oeaten == 0) + if (svc.context.victual.reqtime == 0 || otmp->oeaten == 0) /* possible if most has been eaten before */ - gc.context.victual.nmod = 0; - else if ((int) otmp->oeaten >= gc.context.victual.reqtime) - gc.context.victual.nmod = -((int) otmp->oeaten - / gc.context.victual.reqtime); + svc.context.victual.nmod = 0; + else if ((int) otmp->oeaten >= svc.context.victual.reqtime) + svc.context.victual.nmod = -((int) otmp->oeaten + / svc.context.victual.reqtime); else - gc.context.victual.nmod = gc.context.victual.reqtime % otmp->oeaten; - gc.context.victual.canchoke = (u.uhs == SATIATED); + svc.context.victual.nmod = svc.context.victual.reqtime % otmp->oeaten; + svc.context.victual.canchoke = (u.uhs == SATIATED); if (!dont_start) start_eating(otmp, already_partly_eaten); + else + otmp->owt = weight(otmp); return ECMD_TIME; } /* getobj callback for object to be opened with a tin opener */ -static int +staticfn int tinopen_ok(struct obj *obj) { if (obj && obj->otyp == TIN) @@ -3156,29 +3279,29 @@ use_tin_opener(struct obj *obj) /* Take a single bite from a piece of food, checking for choking and * modifying usedtime. Returns 1 if they choked and survived, 0 otherwise. */ -static int +staticfn int bite(void) { /* hack to pacify static analyzer incorporated into gcc 12.2 */ - sa_victual(&gc.context.victual); + sa_victual(&svc.context.victual); - if (gc.context.victual.canchoke && u.uhunger >= 2000) { - choke(gc.context.victual.piece); + if (svc.context.victual.canchoke && u.uhunger >= 2000) { + choke(svc.context.victual.piece); return 1; } - if (gc.context.victual.doreset) { + if (svc.context.victual.doreset) { do_reset_eat(); return 0; } gf.force_save_hs = TRUE; - if (gc.context.victual.nmod < 0) { - lesshungry(-gc.context.victual.nmod); - consume_oeaten(gc.context.victual.piece, - gc.context.victual.nmod); /* -= -nmod */ - } else if (gc.context.victual.nmod > 0 - && (gc.context.victual.usedtime % gc.context.victual.nmod)) { + if (svc.context.victual.nmod < 0) { + lesshungry(adj_victual_nutrition(/*-svc.context.victual.nmod*/)); + consume_oeaten(svc.context.victual.piece, + svc.context.victual.nmod); /* -= -nmod */ + } else if (svc.context.victual.nmod > 0 + && (svc.context.victual.usedtime % svc.context.victual.nmod)) { lesshungry(1); - consume_oeaten(gc.context.victual.piece, -1); /* -= 1 */ + consume_oeaten(svc.context.victual.piece, -1); /* -= 1 */ } gf.force_save_hs = FALSE; recalc_wt(); @@ -3216,7 +3339,7 @@ gethungry(void) * Also causes melee-induced hunger to vary from turn-based hunger * instead of just replicating that. */ - accessorytime = rn2(20); /* rn2(20) replaces (int) (gm.moves % 20L) */ + accessorytime = rn2(20); /* rn2(20) replaces (int) (svm.moves % 20L) */ if (accessorytime % 2) { /* odd */ /* Regeneration uses up food, unless due to an artifact */ if ((HRegeneration & ~FROMFORM) @@ -3324,31 +3447,32 @@ lesshungry(int num) debugpline1("lesshungry(%d)", num); u.uhunger += (Hunger ? (num + 1) / 2 : num); if (u.uhunger >= 2000) { - if (!iseating || gc.context.victual.canchoke) { + if (!iseating || svc.context.victual.canchoke) { if (iseating) { - choke(gc.context.victual.piece); + choke(svc.context.victual.piece); reset_eat(); - } else - choke((go.occupation == opentin) ? gc.context.tin.tin : 0); - /* no reset_eat() */ + } else { + choke((go.occupation == opentin) ? svc.context.tin.tin : 0); + /* no reset_eat() */ + } } } else { /* Have lesshungry() report when you're nearly full so all eating * warns when you're about to choke. */ - if (u.uhunger >= 1500 - && (!gc.context.victual.eating - || (gc.context.victual.eating - && !gc.context.victual.fullwarn))) { + if (u.uhunger >= 1500 && !Hunger + && (!svc.context.victual.eating + || (svc.context.victual.eating + && !svc.context.victual.fullwarn))) { pline("You're having a hard time getting all of it down."); gn.nomovemsg = "You're finally finished."; - if (!gc.context.victual.eating) { + if (!svc.context.victual.eating) { gm.multi = -2; } else { - gc.context.victual.fullwarn = 1; - if (gc.context.victual.canchoke - && (gc.context.victual.reqtime - - gc.context.victual.usedtime) > 1) { + svc.context.victual.fullwarn = 1; + if (svc.context.victual.canchoke + && (svc.context.victual.reqtime + - svc.context.victual.usedtime) > 1) { /* food with one bite left will not survive a stop */ if (!paranoid_query(ParanoidEating, "Continue eating?")) { reset_eat(); @@ -3361,14 +3485,14 @@ lesshungry(int num) newuhs(FALSE); } -static int +staticfn int unfaint(void) { (void) Hear_again(); if (u.uhs > FAINTING) u.uhs = FAINTING; stop_occupation(); - gc.context.botl = 1; + disp.botl = TRUE; return 0; } @@ -3450,7 +3574,7 @@ newuhs(boolean incr) stop_occupation(); You("faint from lack of food."); incr_itimeout(&HDeaf, duration); - gc.context.botl = TRUE; + disp.botl = TRUE; nomul(-duration); gm.multi_reason = "fainted from lack of food"; gn.nomovemsg = "You regain consciousness."; @@ -3465,11 +3589,11 @@ newuhs(boolean incr) now uhunger becomes more negative at a slower rate */ } else if (u.uhunger < -(100 + 10 * (int) ACURR(A_CON))) { u.uhs = STARVED; - gc.context.botl = 1; + disp.botl = TRUE; bot(); You("die from starvation."); - gk.killer.format = KILLED_BY; - Strcpy(gk.killer.name, "starvation"); + svk.killer.format = KILLED_BY; + Strcpy(svk.killer.name, "starvation"); done(STARVING); /* if we return, we lifesaved, and that calls newuhs */ return; @@ -3529,7 +3653,7 @@ newuhs(boolean incr) end_running(TRUE); break; case NOT_HUNGRY: - if (Verbose(4, newuhs) && !incr) { + if (flags.verbose && !incr) { if (Hallucination) pline("The munchies have receded."); else @@ -3538,12 +3662,12 @@ newuhs(boolean incr) break; } u.uhs = newhs; - gc.context.botl = 1; + disp.botl = TRUE; bot(); if ((Upolyd ? u.mh : u.uhp) < 1) { You("die from hunger and exhaustion."); - gk.killer.format = KILLED_BY; - Strcpy(gk.killer.name, "exhaustion"); + svk.killer.format = KILLED_BY; + Strcpy(svk.killer.name, "exhaustion"); done(STARVING); return; } @@ -3551,7 +3675,7 @@ newuhs(boolean incr) } /* getobj callback for object to eat - effectively just wraps is_edible() */ -static int +staticfn int eat_ok(struct obj *obj) { /* 'getobj_else' will be non-zero if floor food is present and @@ -3573,7 +3697,7 @@ eat_ok(struct obj *obj) /* getobj callback for object to be offered (corpses and things that look like * the Amulet only */ -static int +staticfn int offer_ok(struct obj *obj) { if (!obj) @@ -3595,7 +3719,7 @@ offer_ok(struct obj *obj) } /* getobj callback for object to be tinned */ -static int +staticfn int tin_ok(struct obj *obj) { if (!obj) @@ -3618,7 +3742,7 @@ floorfood( const char *verb, int corpsecheck) /* 0, no check, 1, corpses, 2, tinnable corpses */ { - register struct obj *otmp; + struct obj *otmp; char qbuf[QBUFSZ]; char c; struct permonst *uptr = gy.youmonst.data; @@ -3682,16 +3806,16 @@ floorfood( pline("%s but you %s eat them.", qbuf, nodig ? "cannot" : "are too full to"); } else { - Strcat(qbuf, ((!gc.context.digging.chew - || gc.context.digging.pos.x != u.ux - || gc.context.digging.pos.y != u.uy - || !on_level(&gc.context.digging.level, &u.uz)) + Strcat(qbuf, (!svc.context.digging.chew + || !u_at(svc.context.digging.pos.x, + svc.context.digging.pos.y) + || !on_level(&svc.context.digging.level, &u.uz)) ? "; eat them?" - : "; resume eating them?")); + : "; resume eating them?"); c = yn_function(qbuf, ynqchars, 'n', TRUE); } if (c == 'y') - return (struct obj *) &cg.zeroobj; /* csst away 'const' */ + return &hands_obj; else if (c == 'q') return (struct obj *) 0; ++getobj_else; @@ -3712,7 +3836,7 @@ floorfood( } /* Is there some food (probably a heavy corpse) here on the ground? */ - for (otmp = gl.level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere) { + for (otmp = svl.level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere) { if (corpsecheck ? (otmp->otyp == CORPSE && (corpsecheck == 1 || tinnable(otmp))) @@ -3871,7 +3995,7 @@ consume_oeaten(struct obj *obj, int amt) * victual handling mechanism from scratch using a less complex * model. Alternatively, this routine could call done_eating() * or food_disappears() but its callers would need revisions to - * cope with gc.context.victual.piece unexpectedly going away. + * cope with svc.context.victual.piece unexpectedly going away. * * Multi-turn eating operates by setting the food's oeaten field * to its full nutritional value and then running a counter which @@ -3905,8 +4029,8 @@ consume_oeaten(struct obj *obj, int amt) /* mustn't let partly-eaten drop all the way to 0 or the item would become restored to untouched; set to no bites left */ if (obj->oeaten == 0) { - if (obj == gc.context.victual.piece) /* always true unless wishing */ - gc.context.victual.reqtime = gc.context.victual.usedtime; + if (obj == svc.context.victual.piece) /* always true unless wishing */ + svc.context.victual.reqtime = svc.context.victual.usedtime; obj->oeaten = 1; /* smallest possible positive value */ } } @@ -3918,10 +4042,10 @@ maybe_finished_meal(boolean stopping) { /* in case consume_oeaten() has decided that the food is all gone */ if (go.occupation == eatfood - && gc.context.victual.usedtime >= gc.context.victual.reqtime) { + && svc.context.victual.usedtime >= svc.context.victual.reqtime) { if (stopping) go.occupation = 0; /* for do_reset_eat */ - /* eatfood() calls done_eating() to use up gc.context.victual.piece */ + /* eatfood() calls done_eating() to use up svc.context.victual.piece */ (void) eatfood(); return TRUE; } @@ -3939,9 +4063,9 @@ cant_finish_meal(struct obj *corpse) * left for another bite. revive() needs continued access to the * corpse and will delete it when done. */ - if (go.occupation == eatfood && gc.context.victual.piece == corpse) { + if (go.occupation == eatfood && svc.context.victual.piece == corpse) { /* normally performed by done_eating() */ - gc.context.victual = zero_victual; /* victual.piece = 0, .o_id = 0 */ + svc.context.victual = zero_victual; /* victual.piece = 0, .o_id = 0 */ if (!corpse->oeaten) corpse->oeaten = 1; /* [see consume_oeaten()] */ @@ -3964,7 +4088,7 @@ Popeye(int threat) if (go.occupation != opentin) return FALSE; - otin = gc.context.tin.tin; + otin = svc.context.tin.tin; /* make sure hero still has access to tin */ if (!carried(otin) && (!obj_here(otin, u.ux, u.uy) || !can_reach_floor(TRUE))) @@ -3980,11 +4104,11 @@ Popeye(int threat) return (boolean) (mndx != NON_PM || otin->spe == 1); /* flesh from lizards and acidic critters stops petrification */ case STONED: - return (boolean) (mndx >= LOW_PM + return (boolean) (ismnum(mndx) && (mndx == PM_LIZARD || acidic(&mons[mndx]))); /* polymorph into a fiery monster */ case SLIMED: - return (boolean) polyfodder(otin); + return (boolean) polyfood(otin); /* no tins can cure these (yet?) */ case SICK: case VOMITING: diff --git a/src/end.c b/src/end.c index 70a3efa90f..8a023bc388 100644 --- a/src/end.c +++ b/src/end.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 end.c $NHDT-Date: 1674546299 2023/01/24 07:44:59 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.265 $ */ +/* NetHack 3.7 end.c $NHDT-Date: 1720397752 2024/07/08 00:15:52 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.315 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -9,274 +9,35 @@ #ifndef NO_SIGNAL #include #endif -#include #ifndef LONG_MAX #include #endif #include "dlb.h" - -/* add b to long a, convert wraparound to max value */ -#define nowrap_add(a, b) (a = ((a + b) < 0 ? LONG_MAX : (a + b))) - #ifndef NO_SIGNAL -static void done_intr(int); -#if defined(UNIX) || defined(VMS) || defined(__EMX__) -static void done_hangup(int); +staticfn void done_intr(int); +# if defined(UNIX) || defined(VMS) || defined(__EMX__) +staticfn void done_hangup(int); +# endif #endif -#endif -static boolean sentient_arise(int); -static void disclose(int, boolean); -static void get_valuables(struct obj *); -static void sort_valuables(struct valuable_data *, int); -static void artifact_score(struct obj *, boolean, winid); -static boolean fuzzer_savelife(int); -ATTRNORETURN static void really_done(int) NORETURN; -static void savelife(int); -static boolean should_query_disclose_option(int, char *); +staticfn boolean sentient_arise(int); +staticfn void disclose(int, boolean); +staticfn void get_valuables(struct obj *) NO_NNARGS; +staticfn void sort_valuables(struct valuable_data *, int); +staticfn void artifact_score(struct obj *, boolean, winid); +staticfn boolean fuzzer_savelife(int); +ATTRNORETURN staticfn void really_done(int) NORETURN; +staticfn void savelife(int); +staticfn boolean should_query_disclose_option(int, char *); #if defined(DUMPLOG) || defined(DUMPHTML) -static void dump_plines(void); -#endif -static void dump_everything(int, time_t); - -#if defined(__BEOS__) || defined(MICRO) || defined(OS2) || defined(WIN32) -ATTRNORETURN extern void nethack_exit(int) NORETURN; -#else -#define nethack_exit exit -#endif - -#define done_stopprint gp.program_state.stopprint - -#ifndef PANICTRACE -#define NH_abort NH_abort_ -#endif - -#ifdef AMIGA -#define NH_abort_() Abort(0) -#else -#ifdef SYSV -#define NH_abort_() (void) abort() -#else -#ifdef WIN32 -#define NH_abort_() win32_abort() -#else -#define NH_abort_() abort() -#endif -#endif /* !SYSV */ -#endif /* !AMIGA */ - -#ifdef PANICTRACE -#include -#ifdef PANICTRACE_LIBC -#include -#endif - -/* What do we try and in what order? Tradeoffs: - * libc: +no external programs required - * -requires newish libc/glibc - * -requires -rdynamic - * gdb: +gives more detailed information - * +works on more OS versions - * -requires -g, which may preclude -O on some compilers - */ -#ifdef SYSCF -#define SYSOPT_PANICTRACE_GDB sysopt.panictrace_gdb -#ifdef PANICTRACE_LIBC -#define SYSOPT_PANICTRACE_LIBC sysopt.panictrace_libc -#else -#define SYSOPT_PANICTRACE_LIBC 0 -#endif -#else -#define SYSOPT_PANICTRACE_GDB (nh_getenv("NETHACK_USE_GDB") == 0 ? 0 : 2) -#ifdef PANICTRACE_LIBC -#define SYSOPT_PANICTRACE_LIBC 1 -#else -#define SYSOPT_PANICTRACE_LIBC 0 -#endif +staticfn void dump_plines(void); #endif +staticfn void dump_everything(int, time_t); +staticfn void fixup_death(int); +staticfn int wordcount(char *); +staticfn void bel_copy1(char **, char *); -static void NH_abort(void); -#ifndef NO_SIGNAL -static void panictrace_handler(int); -#endif -static boolean NH_panictrace_libc(void); -static boolean NH_panictrace_gdb(void); - -#ifndef NO_SIGNAL -/* called as signal() handler, so sent at least one arg */ -/*ARGUSED*/ -void -panictrace_handler(int sig_unused UNUSED) -{ -#define SIG_MSG "\nSignal received.\n" - int f2; - -#ifdef CURSES_GRAPHICS - if (iflags.window_inited && WINDOWPORT(curses)) { - extern void curses_uncurse_terminal(void); /* wincurs.h */ - - /* it is risky calling this during a program-terminating signal, - but without it the subsequent backtrace is useless because - that ends up being scrawled all over the screen; call is - here rather than in NH_abort() because panic() calls both - exit_nhwindows(), which makes this same call under curses, - then NH_abort() and we don't want to call this twice */ - curses_uncurse_terminal(); - } -#endif - - f2 = (int) write(2, SIG_MSG, sizeof SIG_MSG - 1); - nhUse(f2); /* what could we do if write to fd#2 (stderr) fails */ - NH_abort(); /* ... and we're already in the process of quitting? */ -} - -void -panictrace_setsignals(boolean set) -{ -#define SETSIGNAL(sig) \ - (void) signal(sig, set ? (SIG_RET_TYPE) panictrace_handler : SIG_DFL); -#ifdef SIGILL - SETSIGNAL(SIGILL); -#endif -#ifdef SIGTRAP - SETSIGNAL(SIGTRAP); -#endif -#ifdef SIGIOT - SETSIGNAL(SIGIOT); -#endif -#ifdef SIGBUS - SETSIGNAL(SIGBUS); -#endif -#ifdef SIGFPE - SETSIGNAL(SIGFPE); -#endif -#ifdef SIGSEGV - SETSIGNAL(SIGSEGV); -#endif -#ifdef SIGSTKFLT - SETSIGNAL(SIGSTKFLT); -#endif -#ifdef SIGSYS - SETSIGNAL(SIGSYS); -#endif -#ifdef SIGEMT - SETSIGNAL(SIGEMT); -#endif -#undef SETSIGNAL -} -#endif /* NO_SIGNAL */ - -static void -NH_abort(void) -{ - int gdb_prio = SYSOPT_PANICTRACE_GDB; - int libc_prio = SYSOPT_PANICTRACE_LIBC; - static volatile boolean aborting = FALSE; - - /* don't execute this code recursively if a second abort is requested - while this routine or the code it calls is executing */ - if (aborting) - return; - aborting = TRUE; - -#ifndef VMS - if (gdb_prio == libc_prio && gdb_prio > 0) - gdb_prio++; - - if (gdb_prio > libc_prio) { - (void) (NH_panictrace_gdb() || (libc_prio && NH_panictrace_libc())); - } else { - (void) (NH_panictrace_libc() || (gdb_prio && NH_panictrace_gdb())); - } - -#else /* VMS */ - /* overload otherwise unused priority for debug mode: 1 = show - traceback and exit; 2 = show traceback and stay in debugger */ - /* if (wizard && gdb_prio == 1) gdb_prio = 2; */ - vms_traceback(gdb_prio); - nhUse(libc_prio); - -#endif /* ?VMS */ - -#ifndef NO_SIGNAL - panictrace_setsignals(FALSE); -#endif - NH_abort_(); -} - -static boolean -NH_panictrace_libc(void) -{ -#ifdef PANICTRACE_LIBC - void *bt[20]; - int count, x; - char **info, buf[BUFSZ]; - - raw_print(" Generating more information you may report:\n"); - count = backtrace(bt, SIZE(bt)); - info = backtrace_symbols(bt, count); - for (x = 0; x < count; x++) { - copynchars(buf, info[x], (int) sizeof buf - 1); - /* try to remove up to 16 blank spaces by removing 8 twice */ - (void) strsubst(buf, " ", ""); - (void) strsubst(buf, " ", ""); - raw_printf("[%02lu] %s", (unsigned long) x, buf); - } - /* free(info); -- Don't risk it. */ - return TRUE; -#else - return FALSE; -#endif /* !PANICTRACE_LIBC */ -} - -/* - * fooPATH file system path for foo - * fooVAR (possibly const) variable containing fooPATH - */ -#ifdef PANICTRACE_GDB -#ifdef SYSCF -#define GDBVAR sysopt.gdbpath -#define GREPVAR sysopt.greppath -#else /* SYSCF */ -#define GDBVAR GDBPATH -#define GREPVAR GREPPATH -#endif /* SYSCF */ -#endif /* PANICTRACE_GDB */ - -static boolean -NH_panictrace_gdb(void) -{ -#ifdef PANICTRACE_GDB - /* A (more) generic method to get a stack trace - invoke - * gdb on ourself. */ - const char *gdbpath = GDBVAR; - const char *greppath = GREPVAR; - char buf[BUFSZ]; - FILE *gdb; - - if (gdbpath == NULL || gdbpath[0] == 0) - return FALSE; - if (greppath == NULL || greppath[0] == 0) - return FALSE; - - sprintf(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); - sleep(4); /* ugly */ - pclose(gdb); - return TRUE; - } else { - return FALSE; - } -#else - return FALSE; -#endif /* !PANICTRACE_GDB */ -} -#endif /* PANICTRACE */ +#define done_stopprint program_state.stopprint /* * The order of these needs to match the macros in hack.h. @@ -310,6 +71,7 @@ done1(int sig_unused UNUSED) #ifndef NO_SIGNAL (void) signal(SIGINT, SIG_IGN); #endif + iflags.debug_fuzzer = fuzzer_off; if (flags.ignintr) { #ifndef NO_SIGNAL (void) signal(SIGINT, (SIG_RET_TYPE) done1); @@ -328,9 +90,18 @@ done1(int sig_unused UNUSED) int done2(void) { + boolean abandon_tutorial = FALSE; + if (iflags.debug_fuzzer) return 0; - if (!paranoid_query(ParanoidQuit, "Really quit? (This will end your game permanently!)")) { + + if (In_tutorial(&u.uz) + && y_n("Switch from the tutorial back to regular play?") == 'y') + abandon_tutorial = TRUE; + + if (abandon_tutorial + || !paranoid_query(ParanoidQuit, + "Really quit? (This will end your game permanently!)")) { #ifndef NO_SIGNAL (void) signal(SIGINT, (SIG_RET_TYPE) done1); #endif @@ -343,8 +114,13 @@ done2(void) u.uinvulnerable = FALSE; /* avoid ctrl-C bug -dlc */ u.usleep = 0; } + + if (abandon_tutorial) + schedule_goto(&u.ucamefrom, UTOTYPE_ATSTAIRS, + "Resuming regular play.", (char *) 0); return ECMD_OK; } + #if (defined(UNIX) || defined(VMS) || defined(LATTICE)) if (wizard) { int c; @@ -367,7 +143,7 @@ done2(void) (*soundprocs.sound_exit_nhsound)("done2"); exit_nhwindows((char *) 0); - NH_abort(); + NH_abort(NULL); } else if (c == 'q') done_stopprint++; } @@ -379,7 +155,7 @@ done2(void) #ifndef NO_SIGNAL /* called as signal() handler, so sent at least 1 arg */ /*ARGSUSED*/ -static void +staticfn void done_intr(int sig_unused UNUSED) { done_stopprint++; @@ -394,11 +170,11 @@ done_intr(int sig_unused UNUSED) #if defined(UNIX) || defined(VMS) || defined(__EMX__) /* signal() handler */ -static void +staticfn void done_hangup(int sig) { #ifdef HANGUPHANDLING - gp.program_state.done_hup++; + program_state.done_hup++; #endif sethanguphandler((void (*)(int)) SIG_IGN); done_intr(sig); @@ -415,7 +191,7 @@ done_hangup(int sig) * Returns true if and only if the player can continue playing. * Assumes u.ugrave_arise has already been set to the correct monster number. */ -static boolean +staticfn boolean sentient_arise(int how) { int mon_nm; @@ -425,7 +201,7 @@ sentient_arise(int how) } else { mon_nm = u.ugrave_arise; } - if (mon_nm < LOW_PM || (gm.mvitals[mon_nm].mvflags & G_GENOD)) { + if (mon_nm < LOW_PM || (svm.mvitals[mon_nm].mvflags & G_GENOD)) { return FALSE; } /* already got one shot... don't give third chances. @@ -484,26 +260,26 @@ format_monkiller(struct monst *mtmp) { char buf[BUFSZ]; struct permonst *mptr = mtmp->data, - *champtr = (mtmp->cham >= LOW_PM) ? &mons[mtmp->cham] - : mptr; + *champtr = ismnum(mtmp->cham) ? &mons[mtmp->cham] + : mptr; boolean distorted = (boolean) (Hallucination && canspotmon(mtmp)), mimicker = (M_AP_TYPE(mtmp) == M_AP_MONSTER), imitator = (mptr != champtr || mimicker); buf[0] = '\0'; - gk.killer.format = KILLED_BY_AN; + svk.killer.format = KILLED_BY_AN; /* "killed by the high priest of Crom" is okay, "killed by the high priest" alone isn't */ if ((mptr->geno & G_UNIQ) != 0 && !(imitator && !mimicker) && !(mptr == &mons[PM_HIGH_CLERIC] && !mtmp->ispriest)) { if (!type_is_pname(mptr)) Strcat(buf, "the "); - gk.killer.format = KILLED_BY; + svk.killer.format = KILLED_BY; } /* _the_ ghost of Dudley */ if (has_ebones(mtmp) && has_mgivenname(mtmp)) { Strcat(buf, "the "); - gk.killer.format = KILLED_BY; + svk.killer.format = KILLED_BY; } if (mtmp->minvis) @@ -550,7 +326,7 @@ format_monkiller(struct monst *mtmp) : mtmp->female ? "Ms. " : "Mr. "; Sprintf(eos(buf), "%s%s, the shopkeeper", honorific, shknm); - gk.killer.format = KILLED_BY; + svk.killer.format = KILLED_BY; } else if (mtmp->ispriest || mtmp->isminion) { /* m_monnam() suppresses "the" prefix plus "invisible", and it overrides the effect of Hallucination on priestname() */ @@ -564,7 +340,7 @@ format_monkiller(struct monst *mtmp) } } - Strcpy(gk.killer.name, buf); + Strcpy(svk.killer.name, buf); /* might need to fix up multi_reason if 'mtmp' caused the reason */ if (gm.multi_reason @@ -624,8 +400,8 @@ done_in_by(struct monst *mtmp, int how) u.ugrave_arise = PM_WRAITH; else if (mptr->mlet == S_MUMMY && gu.urace.mummynum != NON_PM) u.ugrave_arise = gu.urace.mummynum; - else if (zombie_maker(mtmp) && zombie_form(gy.youmonst.data) != NON_PM) - u.ugrave_arise = zombie_form(gy.youmonst.data); + else if (zombie_maker(mtmp) && gu.urace.zombienum != NON_PM) + u.ugrave_arise = gu.urace.zombienum; else if (mptr->mlet == S_VAMPIRE && Race_if(PM_HUMAN)) u.ugrave_arise = PM_VAMPIRE; else if (mptr == &mons[PM_GHOUL]) @@ -635,7 +411,7 @@ done_in_by(struct monst *mtmp, int how) /* this could happen if a high-end vampire kills the hero when ordinary vampires are genocided; ditto for wraiths */ if (u.ugrave_arise >= LOW_PM - && (gm.mvitals[u.ugrave_arise].mvflags & G_GENOD)) + && (svm.mvitals[u.ugrave_arise].mvflags & G_GENOD)) u.ugrave_arise = NON_PM; done(how); @@ -661,7 +437,7 @@ static const struct { /* clear away while-helpless when the cause of death caused that helplessness (ie, "petrified by while getting stoned") */ -static void +staticfn void fixup_death(int how) { int i; @@ -692,24 +468,26 @@ DISABLE_WARNING_FORMAT_NONLITERAL ATTRNORETURN void panic VA_DECL(const char *, str) { + char buf[BUFSZ]; VA_START(str); VA_INIT(str, char *); - if (gp.program_state.panicking++) - NH_abort(); /* avoid loops - this should never happen*/ + if (program_state.panicking++) + NH_abort(NULL); /* avoid loops - this should never happen*/ + gb.bot_disabled = TRUE; if (iflags.window_inited) { raw_print("\r\nOops..."); wait_synch(); /* make sure all pending output gets flushed */ if (soundprocs.sound_exit_nhsound) (*soundprocs.sound_exit_nhsound)("panic"); exit_nhwindows((char *) 0); - iflags.window_inited = 0; /* they're gone; force raw_print()ing */ + iflags.window_inited = FALSE; /* they're gone; force raw_print()ing */ } - raw_print(gp.program_state.gameover + raw_print(program_state.gameover ? "Postgame wrapup disrupted." - : !gp.program_state.something_worth_saving + : !program_state.something_worth_saving ? "Program initialization has failed." : "Suddenly, the dungeon collapses."); #ifndef MICRO @@ -717,14 +495,15 @@ panic VA_DECL(const char *, str) if (!wizard) raw_printf("Report the following error to \"%s\" or at \"%s\".", DEVTEAM_EMAIL, DEVTEAM_URL); - else if (gp.program_state.something_worth_saving) + else if (program_state.something_worth_saving) raw_print("\nError save file being written.\n"); #else /* !NOTIFY_NETHACK_BUGS */ if (!wizard) { - const char *maybe_rebuild = !gp.program_state.something_worth_saving + const char *maybe_rebuild = !program_state.something_worth_saving ? "." : "\nand it may be possible to rebuild."; +// XXX this may need an update if defined(CRASHREPORT) TBD if (sysopt.support) raw_printf("To report this error, %s%s", sysopt.support, maybe_rebuild); @@ -739,7 +518,7 @@ panic VA_DECL(const char *, str) /* XXX can we move this above the prints? Then we'd be able to * suppress "it may be possible to rebuild" based on dosave0() * or say it's NOT possible to rebuild. */ - if (gp.program_state.something_worth_saving && !iflags.debug_fuzzer) { + if (program_state.something_worth_saving && !iflags.debug_fuzzer) { set_error_savefile(); if (dosave0()) { /* os/win port specific recover instructions */ @@ -748,19 +527,19 @@ panic VA_DECL(const char *, str) } } #endif /* !MICRO */ - { - char buf[BUFSZ]; - (void) vsnprintf(buf, sizeof buf, str, VA_ARGS); - raw_print(buf); - paniclog("panic", buf); - } + (void) vsnprintf(buf, sizeof buf, str, VA_ARGS); + raw_print(buf); + paniclog("panic", buf); + #ifdef WIN32 interject(INTERJECT_PANIC); #endif #if defined(UNIX) || defined(VMS) || defined(LATTICE) || defined(WIN32) +# ifndef CRASHREPORT if (wizard) - NH_abort(); /* generate core dump */ +# endif + NH_abort(buf); /* generate core dump */ #endif VA_END(); really_done(PANICKED); @@ -768,7 +547,7 @@ panic VA_DECL(const char *, str) RESTORE_WARNING_FORMAT_NONLITERAL -static boolean +staticfn boolean should_query_disclose_option(int category, char *defquery) { int idx; @@ -810,7 +589,7 @@ should_query_disclose_option(int category, char *defquery) } #if defined(DUMPLOG) || defined(DUMPHTML) -static void +staticfn void dump_plines(void) { int i, j; @@ -830,10 +609,10 @@ dump_plines(void) } } } -#endif +#endif /* DUMPLOG */ /*ARGSUSED*/ -static void +staticfn void dump_everything( int how, /* ASCENDED, ESCAPED, QUIT, etc */ time_t when) /* date+time at end of game */ @@ -868,7 +647,7 @@ dump_everything( /* character name and basic role info */ Sprintf(pbuf, "%s, %s %s %s %s", - gp.plname, aligns[1 - u.ualign.type].adj, + svp.plname, aligns[1 - u.ualign.type].adj, genders[flags.female].adj, gu.urace.adj, (flags.female && gu.urole.name.f) ? gu.urole.name.f : gu.urole.name.m); @@ -917,7 +696,7 @@ dump_everything( #endif } -static void +staticfn void disclose(int how, boolean taken) { char c = '\0', defquery; @@ -1001,7 +780,7 @@ disclose(int how, boolean taken) } /* try to get the player back in a viable state after being killed */ -static void +staticfn void savelife(int how) { int uhpmin; @@ -1013,7 +792,7 @@ savelife(int how) u.ulevel = 1; uhpmin = minuhpmax(10); if (u.uhpmax < uhpmin) - setuhpmax(uhpmin); + setuhpmax(uhpmin, TRUE); u.uhp = min(u.uhpmax, givehp); if (Upolyd) /* Unchanging, or death which bypasses losing hit points */ u.mh = min(u.mhmax, givehp); @@ -1026,7 +805,7 @@ savelife(int how) make_sick(0L, (char *) 0, FALSE, SICK_ALL); } gn.nomovemsg = "You survived that attempt on your life."; - gc.context.move = 0; + svc.context.move = 0; gm.multi = -1; /* can't move again during the current turn */ /* in case being life-saved is immediately followed by being killed @@ -1038,10 +817,10 @@ savelife(int how) if (u.utrap && u.utraptype == TT_LAVA) reset_utrap(FALSE); - gc.context.botl = TRUE; + disp.botl = TRUE; u.ugrave_arise = NON_PM; curs_on_u(); - if (!gc.context.mon_moving) + if (!svc.context.mon_moving) endmultishot(FALSE); if (u.uswallow) { /* might drop hero onto a trap that kills her all over again */ @@ -1059,11 +838,11 @@ savelife(int how) * Get valuables from the given list. Revised code: the list always remains * intact. */ -static void +staticfn void get_valuables(struct obj *list) /* inventory or container contents */ { - register struct obj *obj; - register int i; + struct obj *obj; + int i; /* find amulets and gems, ignoring all artifacts */ for (obj = list; obj; obj = obj->nobj) @@ -1094,12 +873,12 @@ get_valuables(struct obj *list) /* inventory or container contents */ * Sort collected valuables, most frequent to least. We could just * as easily use qsort, but we don't care about efficiency here. */ -static void +staticfn void sort_valuables( struct valuable_data list[], int size) /* max value is less than 20 */ { - register int i, j; + int i, j; struct valuable_data ltmp; /* move greater quantities to the front of the list */ @@ -1122,11 +901,11 @@ sort_valuables( * odds_and_ends() was used for 3.6.0 and 3.6.1. * Schroedinger's Cat is handled differently as of 3.6.2. */ -static boolean odds_and_ends(struct obj *, int); +staticfn boolean odds_and_ends(struct obj *, int); #define CAT_CHECK 2 -static boolean +staticfn boolean odds_and_ends(struct obj *list, int what) { struct obj *otmp; @@ -1197,13 +976,13 @@ done_object_cleanup(void) a normal popup one; avoids "Bad fruit #n" when saving bones */ if (iflags.perm_invent) { iflags.perm_invent = FALSE; - update_inventory(); /* make interface notice the change */ + perm_invent_toggled(TRUE); /* make interface notice the change */ } return; } /* called twice; first to calculate total, then to list relevant items */ -static void +staticfn void artifact_score( struct obj *list, boolean counting, /* true => add up points; false => display them */ @@ -1220,7 +999,7 @@ artifact_score( value = arti_cost(otmp); /* zorkmid value */ points = value * 5 / 2; /* score value */ if (counting) { - nowrap_add(u.urexp, points); + u.urexp = nowrap_add(u.urexp, points); } else { discover_object(otmp->otyp, TRUE, FALSE); otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1; @@ -1240,23 +1019,14 @@ artifact_score( /* when dying while running the debug fuzzer, [almost] always keep going; True: forced survival; False: doomed unless wearing life-save amulet */ -static boolean +staticfn boolean fuzzer_savelife(int how) { /* * Some debugging code pulled out of done() to unclutter it. * 'done_seq' is maintained in done(). */ - if (!gp.program_state.panicking - && how != PANICKED && how != TRICKED - /* Guard against getting stuck in a loop if we die in one of - * the few ways where life-saving isn't effective (cited case - * was burning in lava when the level was too full to allow - * teleporting to safety). Skip the life-save attempt if we've - * died on the same move more than 100 times; give up instead. - * [Note: 100 deaths on the same move may seem excessive but it - * has been demonstrated that a limit of 20 was not enough.] */ - && (gd.done_seq++ < gh.hero_seq + 100L)) { + if (!program_state.panicking && how != PANICKED && how != TRICKED) { savelife(how); /* periodically restore characteristics plus lost experience @@ -1269,7 +1039,7 @@ fuzzer_savelife(int how) /* get rid of temporary potion with obfree() rather than useup() because it doesn't get entered into inventory */ - if (u.ulycn >= LOW_PM && !rn2(3)) { + if (ismnum(u.ulycn) && !rn2(3)) { potion = mksobj(POT_WATER, TRUE, FALSE); bless(potion); (void) peffects(potion); @@ -1284,7 +1054,7 @@ fuzzer_savelife(int how) ++remedies; } if (!rn2(3 + 3 * remedies)) { - /* confer temporary resistances for first 8 properities: + /* confer temporary resistances for first 8 properties: fire, cold, sleep, disint, shock, poison, acid, stone */ for (propidx = 1; propidx <= 8; ++propidx) { if (!u.uprops[propidx].intrinsic @@ -1301,8 +1071,23 @@ fuzzer_savelife(int how) } } /* clear stale cause of death info after life-saving */ - gk.killer.name[0] = '\0'; - gk.killer.format = 0; + svk.killer.name[0] = '\0'; + svk.killer.format = 0; + + /* + * Guard against getting stuck in a loop if we die in one of + * the few ways where life-saving isn't effective (cited case + * was burning in lava when the level was too full to allow + * teleporting to safety). Deal with it by recreating the level + * if we're in wizmode (always the case for debug_fuzzer unless + * player has used a debugger to fiddle with 'iflags' bits). + */ + if (gd.done_seq++ > gh.hero_seq + 100L) { + if (!wizard) + return FALSE; /* can't deal with it */ + cmdq_add_ec(CQ_CANNED, wiz_makemap); + } + return TRUE; } return FALSE; /* panic or too many consecutive deaths */ @@ -1315,27 +1100,27 @@ done(int how) boolean survive = FALSE; if (how == TRICKED) { - if (gk.killer.name[0]) { - paniclog("trickery", gk.killer.name); - gk.killer.name[0] = '\0'; + if (svk.killer.name[0]) { + paniclog("trickery", svk.killer.name); + svk.killer.name[0] = '\0'; } if (wizard) { You("are a very tricky wizard, it seems."); - gk.killer.format = KILLED_BY_AN; /* reset to 0 */ + svk.killer.format = KILLED_BY_AN; /* reset to 0 */ return; } } - if (gp.program_state.panicking + if (program_state.panicking #ifdef HANGUPHANDLING - || gp.program_state.done_hup + || program_state.done_hup #endif || (how == QUIT && done_stopprint)) { /* skip status update if panicking or disconnected or answer of 'q' to "Really quit?" */ - gc.context.botl = gc.context.botlx = iflags.time_botl = FALSE; + disp.botl = disp.botlx = disp.time_botl = FALSE; } else { /* otherwise force full status update */ - gc.context.botlx = TRUE; + disp.botlx = TRUE; bot(); } @@ -1351,13 +1136,13 @@ done(int how) return; } - if (how == ASCENDED || (!gk.killer.name[0] && how == GENOCIDED)) - gk.killer.format = NO_KILLER_PREFIX; + if (how == ASCENDED || (!svk.killer.name[0] && how == GENOCIDED)) + svk.killer.format = NO_KILLER_PREFIX; /* Avoid killed by "a" burning or "a" starvation */ - if (!gk.killer.name[0] && (how == STARVING || how == BURNING)) - gk.killer.format = KILLED_BY; - if (!gk.killer.name[0] || how >= PANICKED) - Strcpy(gk.killer.name, deaths[how]); + if (!svk.killer.name[0] && (how == STARVING || how == BURNING)) + svk.killer.format = KILLED_BY; + if (!svk.killer.name[0] || how >= PANICKED) + Strcpy(svk.killer.name, deaths[how]); if (how < PANICKED) { u.umortality++; @@ -1366,7 +1151,7 @@ done(int how) /* for deaths not triggered by loss of hit points, force current HP to zero */ u.uhp = u.mh = 0; - gc.context.botl = 1; + disp.botl = TRUE; } } if (Lifesaved && (how <= GENOCIDED) && !nonliving(gy.youmonst.data)) { @@ -1427,7 +1212,7 @@ done(int how) /* if hangup has occurred, the only possible answer to a paranoid query is 'no'; we want 'no' as the default for "Die?" but can't accept it more than once if there's no user supplying it */ - && !(gp.program_state.done_hup && gd.done_seq++ == gh.hero_seq) + && !(program_state.done_hup && gd.done_seq++ == gh.hero_seq) #endif && !paranoid_query(ParanoidDie, "Die?")) { pline("OK, so you don't %s.", (how == CHOKING) ? "choke" : "die"); @@ -1437,8 +1222,8 @@ done(int how) } if (survive) { - gk.killer.name[0] = '\0'; - gk.killer.format = KILLED_BY_AN; /* reset to 0 */ + svk.killer.name[0] = '\0'; + svk.killer.format = KILLED_BY_AN; /* reset to 0 */ return; } @@ -1447,7 +1232,7 @@ done(int how) } /* separated from done() in order to specify the __noreturn__ attribute */ -static void +staticfn void really_done(int how) { boolean taken; @@ -1462,19 +1247,19 @@ really_done(int how) /* * The game is now over... */ - gp.program_state.gameover = 1; + program_state.gameover = 1; /* in case of a subsequent panic(), there's no point trying to save */ - gp.program_state.something_worth_saving = 0; + program_state.something_worth_saving = 0; #ifdef HANGUPHANDLING - if (gp.program_state.done_hup) + if (program_state.done_hup) done_stopprint++; #endif /* render vision subsystem inoperative */ - iflags.vision_inited = 0; + iflags.vision_inited = FALSE; /* maybe use up active invent item(s), place thrown/kicked missile, deal with ball and chain possibly being temporarily off the map */ - if (!gp.program_state.panicking) + if (!program_state.panicking) done_object_cleanup(); /* in case we're panicking; normally cleared by done_object_cleanup() */ iflags.perm_invent = FALSE; @@ -1504,7 +1289,7 @@ really_done(int how) * On those rare occasions you get hosed immediately, go out * smiling... :-) -3. */ - if (gm.moves <= 1 && how < PANICKED && !done_stopprint) + if (svm.moves <= 1 && how < PANICKED && !done_stopprint) pline("Do not pass Go. Do not collect 200 %s.", currency(200L)); if (have_windows) @@ -1530,28 +1315,28 @@ really_done(int how) else if (how == BURNING || how == DISSOLVED) /* corpse burns up too */ u.ugrave_arise = (NON_PM - 2); /* leave no corpse */ else if (how == STONING) - u.ugrave_arise = (NON_PM - 1); /* statue instead of corpse */ + u.ugrave_arise = LEAVESTATUE; /* statue instead of corpse */ else if (how == TURNED_SLIME /* it's possible to turn into slime even though green slimes have been genocided: genocide could occur after hero is already infected or hero could eat a glob of one created before genocide; don't try to arise as one if they're gone */ - && !(gm.mvitals[PM_GREEN_SLIME].mvflags & G_GENOD)) { + && !(svm.mvitals[PM_GREEN_SLIME].mvflags & G_GENOD)) { if (Hallucination) pline("Next time, watch out for slime!"); u.ugrave_arise = PM_GREEN_SLIME; } if (how == QUIT) { - gk.killer.format = NO_KILLER_PREFIX; + svk.killer.format = NO_KILLER_PREFIX; if (u.uhp < 1) { how = DIED; u.umortality++; /* skipped above when how==QUIT */ - Strcpy(gk.killer.name, "quit while already on Charon's boat"); + Strcpy(svk.killer.name, "quit while already on Charon's boat"); } } if (how == ESCAPED || how == PANICKED) - gk.killer.format = NO_KILLER_PREFIX; + svk.killer.format = NO_KILLER_PREFIX; fixup_death(how); /* actually, fixup gm.multi_reason */ @@ -1571,14 +1356,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 */ @@ -1626,7 +1412,7 @@ really_done(int how) /* grave creation should be after disclosure so it doesn't have this grave in the current level's features for #overview */ if (bones_ok && u.ugrave_arise == NON_PM - && !(gm.mvitals[u.umonnum].mvflags & G_NOCORPSE)) { + && !(svm.mvitals[u.umonnum].mvflags & G_NOCORPSE)) { /* Base corpse on race when not poly'd since original u.umonnum is based on role, and all role monsters are human. */ int mnum = !Upolyd ? gu.urace.mnum : u.umonnum, @@ -1639,7 +1425,7 @@ really_done(int how) * withering is assumed to just wither the rest of the body without * any vitality to stop it) */ corpse = mk_named_object(CORPSE, &mons[mnum], u.ux, u.uy, - gp.plname); + svp.plname); } pbuf[0] = '\0'; if (y_n("Do you want to write your own epitaph?") == 'y') { @@ -1647,12 +1433,12 @@ really_done(int how) ebuf[0] = '\0'; /* don't show junk for EDIT_GETLIN */ getlin("Enter your epitaph:", ebuf); if (*ebuf) { - Sprintf(pbuf, "%s. ", gp.plname); + Sprintf(pbuf, "%s. ", svp.plname); (void) strncat(pbuf, ebuf, 100); } } if (!*pbuf) { /* didn't write own epitaph, or entered a blank line */ - Sprintf(pbuf, "%s, ", gp.plname); + Sprintf(pbuf, "%s, ", svp.plname); formatkiller(eos(pbuf), sizeof pbuf - Strlen(pbuf), how, TRUE); } make_grave(u.ux, u.uy, pbuf); @@ -1688,7 +1474,7 @@ really_done(int how) tmp += 50L * (long) (deepest - 1); if (deepest > 20) tmp += 1000L * (long) ((deepest > 30) ? 10 : deepest - 20); - nowrap_add(u.urexp, tmp); + u.urexp = nowrap_add(u.urexp, tmp); /* ascension gives a score bonus iff offering to original deity */ if (how == ASCENDED) { @@ -1698,15 +1484,15 @@ really_done(int how) tmp = (u.ualignbase[A_CURRENT] == u.ualignbase[A_ORIGINAL]) ? u.urexp : (u.urexp / 2L); - nowrap_add(u.urexp, tmp); + u.urexp = nowrap_add(u.urexp, tmp); } else { - Strcat(gk.killer.name, " (in dishonor)"); + Strcat(svk.killer.name, " (in dishonor)"); } } } - if (u.ugrave_arise >= LOW_PM && !done_stopprint) { + if (ismnum(u.ugrave_arise) && !done_stopprint) { /* give this feedback even if bones aren't going to be created, so that its presence or absence doesn't tip off the player to new bones or their lack; it might be a lie if makemon fails */ @@ -1744,7 +1530,7 @@ really_done(int how) if (WIN_INVEN != WIN_ERR) { destroy_nhwindow(WIN_INVEN), WIN_INVEN = WIN_ERR; /* precaution in case any late update_inventory() calls occur */ - iflags.perm_invent = 0; + iflags.perm_invent = FALSE; } display_nhwindow(WIN_MESSAGE, TRUE); destroy_nhwindow(WIN_MAP), WIN_MAP = WIN_ERR; @@ -1771,16 +1557,16 @@ really_done(int how) } #endif if (u.uhave.amulet) { - Strcat(gk.killer.name, " (with the Amulet)"); + Strcat(svk.killer.name, " (with the Amulet)"); } else if (how == ESCAPED) { if (Is_astralevel(&u.uz)) /* offered Amulet to wrong deity */ - Strcat(gk.killer.name, " (in celestial disgrace)"); + Strcat(svk.killer.name, " (in celestial disgrace)"); else if (carrying(FAKE_AMULET_OF_YENDOR)) - Strcat(gk.killer.name, " (with a fake Amulet)"); + Strcat(svk.killer.name, " (with a fake Amulet)"); /* don't bother counting to see whether it should be plural */ } - Sprintf(pbuf, "%s %s the %s...", Goodbye(), gp.plname, + Sprintf(pbuf, "%s %s the %s...", Goodbye(), svp.plname, (how != ASCENDED) ? (const char *) ((flags.female && gu.urole.name.f) ? gu.urole.name.f @@ -1802,8 +1588,8 @@ really_done(int how) if (how == ESCAPED || how == ASCENDED) { struct monst *mtmp; struct obj *otmp; - register struct val_list *val; - register int i; + struct val_list *val; + int i; for (val = gv.valuables; val->list; val++) for (i = 0; i < val->size; i++) { @@ -1817,7 +1603,7 @@ really_done(int how) if (val->list[i].count != 0L) { tmp = val->list[i].count * (long) objects[val->list[i].typ].oc_cost; - nowrap_add(u.urexp, tmp); + u.urexp = nowrap_add(u.urexp, tmp); } /* count the points for artifacts */ @@ -1830,7 +1616,7 @@ really_done(int how) while (mtmp) { Sprintf(eos(pbuf), " and %s", mon_nam(mtmp)); if (mtmp->mtame) - nowrap_add(u.urexp, mtmp->mhp); + u.urexp = nowrap_add(u.urexp, mtmp->mhp); mtmp = mtmp->nmon; } /* [it might be more robust to create a housecat and add it to @@ -1839,7 +1625,7 @@ really_done(int how) int mhp, m_lev = adj_lev(&mons[PM_HOUSECAT]); mhp = d(m_lev, 8); - nowrap_add(u.urexp, mhp); + u.urexp = nowrap_add(u.urexp, mhp); Strcat(eos(pbuf), " and Schroedinger's cat"); } dump_forward_putstr(endwin, 0, pbuf, done_stopprint); @@ -1903,7 +1689,7 @@ really_done(int how) (u.uz.dlevel < 0) ? "passed away" : ends[how]); } else { /* more conventional demise */ - const char *where = gd.dungeons[u.uz.dnum].dname; + const char *where = svd.dungeons[u.uz.dnum].dname; if (Is_astralevel(&u.uz)) where = "The Astral Plane"; @@ -1918,7 +1704,7 @@ really_done(int how) } Sprintf(pbuf, "and %ld piece%s of gold, after %ld move%s.", umoney, - plur(umoney), gm.moves, plur(gm.moves)); + plur(umoney), svm.moves, plur(svm.moves)); dump_forward_putstr(endwin, 0, pbuf, done_stopprint); Sprintf(pbuf, "You were level %d with a maximum of %d hit point%s when you %s.", @@ -1974,7 +1760,7 @@ container_contents( boolean all_containers, boolean reportempty) { - register struct obj *box, *obj; + struct obj *box, *obj; char buf[BUFSZ]; boolean cat, dumping = iflags.in_dumplog; @@ -2013,7 +1799,7 @@ container_contents( | (flags.sortpack ? SORTLOOT_PACK : 0)); sortedcobj = sortloot(&box->cobj, sortflags, FALSE, (boolean (*)(OBJ_P)) 0); - for (srtc = sortedcobj; ((obj = srtc->obj) != 0); ++srtc) { + for (srtc = sortedcobj; (obj = srtc->obj) != 0; ++srtc) { if (identified) { discover_object(obj->otyp, TRUE, FALSE); obj->known = obj->bknown = obj->dknown @@ -2050,7 +1836,7 @@ container_contents( ATTRNORETURN void nh_terminate(int status) { - gp.program_state.in_moveloop = 0; /* won't be returning to normal play */ + program_state.in_moveloop = 0; /* won't be returning to normal play */ l_nhcore_call(NHCORE_GAME_EXIT); #ifdef MAC @@ -2058,7 +1844,7 @@ nh_terminate(int status) #endif /* don't bother to try to release memory if we're in panic mode, to avoid trouble in case that happens to be due to memory problems */ - if (!gp.program_state.panicking) { + if (!program_state.panicking) { freedynamicdata(); dlb_cleanup(); l_nhcore_done(); @@ -2072,10 +1858,10 @@ nh_terminate(int status) */ /* don't call exit() if already executing within an exit handler; that would cancel any other pending user-mode handlers */ - if (gp.program_state.exiting) + if (program_state.exiting) return; #endif - gp.program_state.exiting = 1; + program_state.exiting = 1; nethack_exit(status); } @@ -2090,13 +1876,13 @@ delayed_killer(int id, int format, const char *killername) k = (struct kinfo *) alloc(sizeof (struct kinfo)); (void) memset((genericptr_t) k, 0, sizeof (struct kinfo)); k->id = id; - k->next = gk.killer.next; - gk.killer.next = k; + k->next = svk.killer.next; + svk.killer.next = k; } k->format = format; Strcpy(k->name, killername ? killername : ""); - gk.killer.name[0] = 0; + svk.killer.name[0] = 0; } struct kinfo * @@ -2104,7 +1890,7 @@ find_delayed_killer(int id) { struct kinfo *k; - for (k = gk.killer.next; k != (struct kinfo *) 0; k = k->next) { + for (k = svk.killer.next; k != (struct kinfo *) 0; k = k->next) { if (k->id == id) break; } @@ -2114,11 +1900,11 @@ find_delayed_killer(int id) void dealloc_killer(struct kinfo *kptr) { - struct kinfo *prev = &gk.killer, *k; + struct kinfo *prev = &svk.killer, *k; if (kptr == (struct kinfo *) 0) return; - for (k = gk.killer.next; k != (struct kinfo *) 0; k = k->next) { + for (k = svk.killer.next; k != (struct kinfo *) 0; k = k->next) { if (k == kptr) break; prev = k; @@ -2139,16 +1925,16 @@ save_killers(NHFILE *nhfp) struct kinfo *kptr; if (perform_bwrite(nhfp)) { - for (kptr = &gk.killer; kptr != (struct kinfo *) 0; kptr = kptr->next) { + for (kptr = &svk.killer; kptr; kptr = kptr->next) { if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t)kptr, sizeof(struct kinfo)); + bwrite(nhfp->fd, (genericptr_t) kptr, sizeof (struct kinfo)); } } if (release_data(nhfp)) { - while (gk.killer.next) { - kptr = gk.killer.next->next; - free((genericptr_t) gk.killer.next); - gk.killer.next = kptr; + while (svk.killer.next) { + kptr = svk.killer.next->next; + free((genericptr_t) svk.killer.next); + svk.killer.next = kptr; } } } @@ -2158,16 +1944,16 @@ restore_killers(NHFILE *nhfp) { struct kinfo *kptr; - for (kptr = &gk.killer; kptr != (struct kinfo *) 0; kptr = kptr->next) { + for (kptr = &svk.killer; kptr != (struct kinfo *) 0; kptr = kptr->next) { if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t)kptr, sizeof(struct kinfo)); + mread(nhfp->fd, (genericptr_t) kptr, sizeof(struct kinfo)); if (kptr->next) { kptr->next = (struct kinfo *) alloc(sizeof (struct kinfo)); } } } -static int +staticfn int wordcount(char *p) { int words = 0; @@ -2183,7 +1969,7 @@ wordcount(char *p) return words; } -static void +staticfn void bel_copy1(char **inp, char *out) { char *in = *inp; @@ -2236,4 +2022,91 @@ build_english_list(char *in) return out; } +/* What do we try to in what order? Tradeoffs: + * libc: +no external programs required + * -requires newish libc/glibc + * -requires -rdynamic + * gdb: +gives more detailed information + * +works on more OS versions + * -requires -g, which may preclude -O on some compilers + * + * And the UI: if sysopt.crashreporturl, and defined(CRASHREPORT) + * we gather the stacktrace (etc) and launch a browser to submit a bug report + * otherwise we just use stdout. Requires libc for now. + */ +# ifdef SYSCF +# define SYSOPT_PANICTRACE_GDB sysopt.panictrace_gdb +# ifdef PANICTRACE_LIBC +# define SYSOPT_PANICTRACE_LIBC sysopt.panictrace_libc +# else +# define SYSOPT_PANICTRACE_LIBC 0 +# endif +# else +# define SYSOPT_PANICTRACE_GDB (nh_getenv("NETHACK_USE_GDB") == 0 ? 0 : 2) +# ifdef PANICTRACE_LIBC +# define SYSOPT_PANICTRACE_LIBC 1 +# else +# define SYSOPT_PANICTRACE_LIBC 0 +# endif +# endif + +# ifdef CRASHREPORT +#define USED_FOR_CRASHREPORT +# else +#define USED_FOR_CRASHREPORT UNUSED +# endif + +void +NH_abort(char *why USED_FOR_CRASHREPORT) +{ +#ifdef PANICTRACE + int gdb_prio = SYSOPT_PANICTRACE_GDB; + int libc_prio = SYSOPT_PANICTRACE_LIBC; +#endif + static volatile boolean aborting = FALSE; + + /* don't execute this code recursively if a second abort is requested + while this routine or the code it calls is executing */ + if (aborting) + return; + aborting = TRUE; + +#ifdef PANICTRACE +#ifdef CRASHREPORT + if(!submit_web_report(1, "Panic", why)) +#endif + { +#ifndef VMS + if (gdb_prio == libc_prio && gdb_prio > 0) + gdb_prio++; + + if (gdb_prio > libc_prio) { + (void) (NH_panictrace_gdb() + || (libc_prio && NH_panictrace_libc())); + } else { + (void) (NH_panictrace_libc() + || (gdb_prio && NH_panictrace_gdb())); + } + +#else /* VMS */ + /* overload otherwise unused priority for debug mode: 1 = show + traceback and exit; 2 = show traceback and stay in debugger */ + /* if (wizard && gdb_prio == 1) gdb_prio = 2; */ + vms_traceback(gdb_prio); + nhUse(libc_prio); + +#endif /* ?VMS */ + } +#ifndef NO_SIGNAL + panictrace_setsignals(FALSE); +#endif +#endif /* PANICTRACE */ +#ifdef WIN32 + win32_abort(); +#else + abort(); +#endif +} +#undef USED_FOR_CRASHREPORT + /*end.c*/ diff --git a/src/engrave.c b/src/engrave.c index 14b1adaf81..7255ca6597 100644 --- a/src/engrave.c +++ b/src/engrave.c @@ -1,15 +1,53 @@ -/* NetHack 3.7 engrave.c $NHDT-Date: 1664616835 2022/10/01 09:33:55 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.131 $ */ +/* 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. */ #include "hack.h" -static int stylus_ok(struct obj *); -static boolean u_can_engrave(void); -static int engrave(void); -static void engraving_learn_wand(struct obj*); -static const char *blengr(void); +/* doengrave() data */ +struct _doengrave_ctx { + boolean dengr; /* TRUE if we wipe out the current engraving */ + boolean doblind; /* TRUE if engraving blinds the player */ + boolean preknown; /* TRUE if stylus identifies itself before */ + boolean postknown;/* TRUE if stylus identifies itself after */ + boolean eow; /* TRUE if we are overwriting oep */ + boolean jello; /* TRUE if we are engraving in slime */ + boolean ptext; /* TRUE if we must prompt for engrave text */ + boolean teleengr; /* TRUE if we move the old engraving */ + boolean zapwand; /* TRUE if we remove a wand charge */ + boolean disprefresh; /* TRUE if the display needs a refresh */ + boolean frosted; /* TRUE if engraving on ice */ + boolean adding; /* TRUE if adding to existing engraving */ + + int ret; /* doengrave return value */ + int type; /* Type of engraving made */ + int oetype; /* will be set to type of current engraving */ + + struct obj *otmp; /* Object selected with which to engrave */ + struct engr* oep; /* The current engraving */ + + char buf[BUFSZ]; /* Buffer for final/poly engraving text */ + char ebuf[BUFSZ]; /* Buffer for initial engraving text */ + char fbuf[BUFSZ]; /* Buffer for "your fingers" */ + char qbuf[QBUFSZ]; /* Buffer for query text */ + char post_engr_text[BUFSZ]; /* Text displayed after engraving prompt */ + char *writer; /* text of item used for writing */ + const char *everb; /* Present tense of engraving type */ + const char *eloc; /* Where the engraving is (ie dust/floor/...) */ + + size_t len; /* # of nonspace chars of new engraving text */ +}; + +staticfn int stylus_ok(struct obj *); +staticfn boolean u_can_engrave(void); +staticfn void doengrave_ctx_init(struct _doengrave_ctx *); +staticfn void doengrave_sfx_item_WAN(struct _doengrave_ctx *); +staticfn boolean doengrave_sfx_item(struct _doengrave_ctx *); +staticfn void doengrave_ctx_verb(struct _doengrave_ctx *); +staticfn int engrave(void); +staticfn void engraving_learn_wand(struct obj*); +staticfn const char *blengr(void); char * random_engraving(char *outbuf) @@ -212,7 +250,7 @@ sengr_at(const char *s, coordxy x, coordxy y, boolean strict) { struct engr *ep = engr_at(x, y); - if (ep && ep->engr_type != HEADSTONE && ep->engr_time <= gm.moves) { + if (ep && ep->engr_type != HEADSTONE && ep->engr_time <= svm.moves) { if (strict ? !strcmpi(ep->engr_txt[actual_text], s) : (strstri(ep->engr_txt[actual_text], s) != 0)) return ep; @@ -230,7 +268,7 @@ u_wipe_engr(int cnt) void wipe_engr_at(coordxy x, coordxy y, xint16 cnt, boolean magical) { - register struct engr *ep = engr_at(x, y); + struct engr *ep = engr_at(x, y); /* Headstones and some specially marked engravings are indelible */ if (ep && ep->engr_type != HEADSTONE && !ep->nowipeout) { @@ -249,6 +287,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) { @@ -256,7 +319,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]) { @@ -321,7 +384,8 @@ read_engr_at(coordxy x, coordxy y) strchr(".?!", *(eos(et) - 1)) ? "" : "."); Strcpy(ep->engr_txt[remembered_text], ep->engr_txt[actual_text]); ep->eread = 1; - if (gc.context.run > 0) + ep->erevealed = 1; + if (svc.context.run > 0) nomul(0); } } @@ -366,7 +430,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 */ @@ -390,7 +455,7 @@ freehand(void) } /* getobj callback for an object to engrave with */ -static int +staticfn int stylus_ok(struct obj *obj) { if (!obj) @@ -412,7 +477,7 @@ stylus_ok(struct obj *obj) } /* can hero engrave at all (at their location)? */ -static boolean +staticfn boolean u_can_engrave(void) { int levtyp = SURFACE_AT(u.ux, u.uy); @@ -457,137 +522,217 @@ u_can_engrave(void) return TRUE; } -/* Mohs' Hardness Scale: - * 1 - Talc 6 - Orthoclase - * 2 - Gypsum 7 - Quartz - * 3 - Calcite 8 - Topaz - * 4 - Fluorite 9 - Corundum - * 5 - Apatite 10 - Diamond - * - * Since granite is an igneous rock hardness ~ 7, anything >= 8 should - * probably be able to scratch the rock. - * Devaluation of less hard gems is not easily possible because obj struct - * does not contain individual oc_cost currently. 7/91 - * - * steel - 5-8.5 (usu. weapon) - * diamond - 10 * jade - 5-6 (nephrite) - * ruby - 9 (corundum) * turquoise - 5-6 - * sapphire - 9 (corundum) * opal - 5-6 - * topaz - 8 * glass - ~5.5 - * emerald - 7.5-8 (beryl) * dilithium - 4-5?? - * aquamarine - 7.5-8 (beryl) * iron - 4-5 - * garnet - 7.25 (var. 6.5-8) * fluorite - 4 - * agate - 7 (quartz) * brass - 3-4 - * amethyst - 7 (quartz) * gold - 2.5-3 - * jasper - 7 (quartz) * silver - 2.5-3 - * onyx - 7 (quartz) * copper - 2.5-3 - * moonstone - 6 (orthoclase) * amber - 2-2.5 - */ - -/* the #engrave command */ -int -doengrave(void) +/* initialize the doengrave data */ +staticfn void +doengrave_ctx_init(struct _doengrave_ctx *de) { - boolean dengr = FALSE; /* TRUE if we wipe out the current engraving */ - boolean doblind = FALSE; /* TRUE if engraving blinds the player */ - boolean preknown = FALSE; /* TRUE if we identify the stylus before */ - boolean postknown = FALSE; /* TRUE if we identify the stylus after */ - boolean eow = FALSE; /* TRUE if we are overwriting oep */ - boolean jello = FALSE; /* TRUE if we are engraving in slime */ - boolean ptext = TRUE; /* TRUE if we must prompt for engrave text */ - boolean teleengr = FALSE; /* TRUE if we move the old engraving */ - boolean zapwand = FALSE; /* TRUE if we remove a wand charge */ - boolean disprefresh = FALSE; /* TRUE if the display needs a refresh */ - - int type = DUST; /* Type of engraving made */ - int oetype = 0; /* will be set to type of current engraving */ - char buf[BUFSZ]; /* Buffer for final/poly engraving text */ - char ebuf[BUFSZ]; /* Buffer for initial engraving text */ - char fbuf[BUFSZ]; /* Buffer for "your fingers" */ - char qbuf[QBUFSZ]; /* Buffer for query text */ - char post_engr_text[BUFSZ]; /* Text displayed after engraving prompt */ - const char *everb; /* Present tense of engraving type */ - const char *eloc; /* Where the engraving is (ie dust/floor/...) */ - char *sp; /* Place holder for space count of engr text */ - size_t len; /* # of nonspace chars of new engraving text */ - struct engr *oep = engr_at(u.ux, u.uy); /* The current engraving */ - struct obj *otmp; /* Object selected with which to engrave */ - char *writer; - boolean frosted, adding; - - gm.multi = 0; /* moves consumed */ - gn.nomovemsg = (char *) 0; /* occupation end message */ - - buf[0] = (char) 0; - ebuf[0] = (char) 0; - post_engr_text[0] = (char) 0; - if (oep) - oetype = oep->engr_type; + de->dengr = FALSE; + de->doblind = FALSE; + de->preknown = FALSE; + de->postknown = FALSE; + de->eow = FALSE; + de->ptext = TRUE; + de->teleengr = FALSE; + de->zapwand = FALSE; + de->disprefresh = FALSE; + de->adding = FALSE; + + de->ret = ECMD_OK; + de->type = DUST; + de->oetype = 0; + + de->otmp = (struct obj *) 0; + de->oep = engr_at(u.ux, u.uy); + + de->buf[0] = (char) 0; + de->ebuf[0] = (char) 0; + de->fbuf[0] = (char) 0; + de->qbuf[0] = (char) 0; + de->post_engr_text[0] = (char) 0; + de->writer = (char *) 0; + + if (de->oep) + de->oetype = de->oep->engr_type; if (is_demon(gy.youmonst.data) || is_vampire(gy.youmonst.data)) - type = ENGR_BLOOD; - - /* Can the adventurer engrave at all? */ - if (!u_can_engrave()) - return ECMD_OK; - - jello = (u.uswallow && !(is_animal(u.ustuck->data) - || is_whirly(u.ustuck->data))); - - /* One may write with finger, or weapon, or wand, or..., or... - * Edited by GAN 10/20/86 so as not to change weapon wielded. - */ - - otmp = getobj("write with", stylus_ok, GETOBJ_PROMPT); - if (!otmp) /* otmp == cg.zeroobj if fingers */ - return ECMD_CANCEL; + de->type = ENGR_BLOOD; - if (otmp == &cg.zeroobj) { - Strcat(strcpy(fbuf, "your "), body_part(FINGERTIP)); - writer = fbuf; - } else { - writer = yname(otmp); - } - frosted = is_ice(u.ux, u.uy); - - /* There's no reason you should be able to write with a wand - * while both your hands are tied up. - */ - if (!freehand() && otmp != uwep && !otmp->owornmask) { - You("have no free %s to write with!", body_part(HAND)); - return ECMD_OK; - } + de->jello = (u.uswallow && !(is_animal(u.ustuck->data) + || is_whirly(u.ustuck->data))); + de->frosted = is_ice(u.ux, u.uy); +} - if (jello) { - You("tickle %s with %s.", mon_nam(u.ustuck), writer); - Your("message dissolves..."); - return ECMD_OK; - } - if (otmp->oclass != WAND_CLASS && !can_reach_floor(TRUE)) { - cant_reach_floor(u.ux, u.uy, FALSE, TRUE); - return ECMD_OK; - } - if (IS_ALTAR(levl[u.ux][u.uy].typ)) { - You("make a motion towards the altar with %s.", writer); - altar_wrath(u.ux, u.uy); - return ECMD_OK; - } - if (IS_GRAVE(levl[u.ux][u.uy].typ)) { - if (otmp == &cg.zeroobj) { /* using only finger */ - You("would only make a small smudge on the %s.", - surface(u.ux, u.uy)); - return ECMD_OK; - } else if (!levl[u.ux][u.uy].disturbed) { - /* disturb the grave: summon a ghoul, same as sometimes - happens when kicking; sets levl[ux][uy]->disturbed so - that it'll only happen once */ - disturb_grave(u.ux, u.uy); - return ECMD_TIME; +/* special engraving effects for WAND objects */ +staticfn void +doengrave_sfx_item_WAN(struct _doengrave_ctx *de) +{ + switch (de->otmp->otyp) { + /* DUST wands */ + default: + break; + /* NODIR wands */ + case WAN_LIGHT: + case WAN_SECRET_DOOR_DETECTION: + case WAN_CREATE_MONSTER: + case WAN_WISHING: + case WAN_ENLIGHTENMENT: + zapnodir(de->otmp); + /* pre/postknown not needed; these will make it known if + * applicable */ + break; + /* IMMEDIATE wands */ + /* If wand is "IMMEDIATE", remember to affect the + * previous engraving even if turning to dust. + */ + case WAN_STRIKING: + Strcpy(de->post_engr_text, + "The wand unsuccessfully fights your attempt to write!"); + de->postknown = TRUE; + break; + case WAN_SLOW_MONSTER: + if (!Blind) { + Sprintf(de->post_engr_text, "The bugs on the %s slow down!", + surface(u.ux, u.uy)); + de->postknown = TRUE; + } + break; + case WAN_SPEED_MONSTER: + if (!Blind) { + Sprintf(de->post_engr_text, "The bugs on the %s speed up!", + surface(u.ux, u.uy)); + de->postknown = TRUE; + } + break; + case WAN_POLYMORPH: + if (de->oep) { + if (!Blind) { + de->type = (xint16) 0; /* random */ + (void) random_engraving(de->buf); + de->preknown = TRUE; + } else { + /* keep the same type so that feels don't + change and only the text is altered, + but you won't know anyway because + you're a _blind writer_ */ + if (de->oetype) + de->type = de->oetype; + xcrypt(blengr(), de->buf); + } + de->dengr = TRUE; + } + break; + case WAN_NOTHING: + case WAN_UNDEAD_TURNING: + case WAN_OPENING: + case WAN_LOCKING: + case WAN_PROBING: + break; + /* RAY wands */ + case WAN_MAGIC_MISSILE: + de->ptext = TRUE; + if (!Blind) { + Sprintf(de->post_engr_text, + "The %s is riddled by bullet holes!", + surface(u.ux, u.uy)); + de->postknown = TRUE; + } + break; + /* can't tell sleep from death - Eric Backus */ + case WAN_SLEEP: + case WAN_DEATH: + if (!Blind) { + Sprintf(de->post_engr_text, "The bugs on the %s stop moving!", + surface(u.ux, u.uy)); } + break; + case WAN_COLD: + if (!Blind) { + Strcpy(de->post_engr_text, + "A few ice cubes drop from the wand."); + de->postknown = TRUE; + } + if (!de->oep || (de->oep->engr_type != BURN)) + break; + FALLTHROUGH; + /*FALLTHRU*/ + case WAN_CANCELLATION: + case WAN_MAKE_INVISIBLE: + if (de->oep && de->oep->engr_type != HEADSTONE) { + if (!Blind) + pline_The("engraving on the %s vanishes!", + surface(u.ux, u.uy)); + de->dengr = TRUE; + } + break; + case WAN_TELEPORTATION: + if (de->oep && de->oep->engr_type != HEADSTONE) { + if (!Blind) + pline_The("engraving on the %s vanishes!", + surface(u.ux, u.uy)); + de->teleengr = TRUE; + } + break; + /* type = ENGRAVE wands */ + case WAN_DIGGING: + de->ptext = TRUE; + de->type = ENGRAVE; + if (!objects[de->otmp->otyp].oc_name_known) { + if (flags.verbose) + pline("This %s is a wand of digging!", xname(de->otmp)); + de->preknown = TRUE; + } + Strcpy(de->post_engr_text, + (Blind && !Deaf) + ? "You hear drilling!" /* Deaf-aware */ + : Blind + ? "You feel tremors." + : IS_GRAVE(levl[u.ux][u.uy].typ) + ? "Chips fly out from the headstone." + : de->frosted + ? "Ice chips fly up from the ice surface!" + : (svl.level.locations[u.ux][u.uy].typ + == DRAWBRIDGE_DOWN) + ? "Splinters fly up from the bridge." + : "Gravel flies up from the floor."); + break; + /* type = BURN wands */ + case WAN_FIRE: + de->ptext = TRUE; + de->type = BURN; + if (!objects[de->otmp->otyp].oc_name_known) { + if (flags.verbose) + pline("This %s is a wand of fire!", xname(de->otmp)); + de->preknown = TRUE; + } + Strcpy(de->post_engr_text, Blind ? "You feel the wand heat up." + : "Flames fly from the wand."); + break; + case WAN_LIGHTNING: + de->ptext = TRUE; + de->type = BURN; + if (!objects[de->otmp->otyp].oc_name_known) { + if (flags.verbose) + pline("This %s is a wand of lightning!", xname(de->otmp)); + de->preknown = TRUE; + } + if (!Blind) { + Strcpy(de->post_engr_text, "Lightning arcs from the wand."); + de->doblind = TRUE; + } else { + Strcpy(de->post_engr_text, !Deaf + ? "You hear crackling!" /* Deaf-aware */ + : "Your hair stands up!"); + } + break; + /* type = MARK wands */ + /* type = ENGR_BLOOD wands */ } +} - /* SPFX for items */ - - switch (otmp->oclass) { +/* special engraving effects for all objects */ +staticfn boolean +doengrave_sfx_item(struct _doengrave_ctx *de) +{ + switch (de->otmp->oclass) { default: case AMULET_CLASS: case CHAIN_CLASS: @@ -598,30 +743,31 @@ doengrave(void) /* "diamond" rings and others should work */ case GEM_CLASS: /* diamonds & other hard gems should work */ - if (objects[otmp->otyp].oc_tough) { - type = ENGRAVE; + if (objects[de->otmp->otyp].oc_tough) { + de->type = ENGRAVE; break; } break; case ARMOR_CLASS: - if (is_boots(otmp)) { - type = DUST; + if (is_boots(de->otmp)) { + de->type = DUST; break; } + FALLTHROUGH; /*FALLTHRU*/ /* Objects too large to engrave with */ case BALL_CLASS: case ROCK_CLASS: You_cant("engrave with such a large object!"); - ptext = FALSE; + de->ptext = FALSE; break; /* Objects too silly to engrave with */ case FOOD_CLASS: case SCROLL_CLASS: case SPBOOK_CLASS: - pline("%s would get %s.", Yname2(otmp), - frosted ? "all frosty" : "too dirty"); - ptext = FALSE; + pline("%s would get %s.", Yname2(de->otmp), + de->frosted ? "all frosty" : "too dirty"); + de->ptext = FALSE; break; case RANDOM_CLASS: /* This should mean fingers */ break; @@ -634,196 +780,26 @@ doengrave(void) * therefore will know they are using a charge. */ case WAND_CLASS: - if (zappable(otmp)) { - check_unpaid(otmp); - if (otmp->cursed && !rn2(WAND_BACKFIRE_CHANCE)) { - wand_explode(otmp, 0); - return ECMD_TIME; + if (zappable(de->otmp)) { + check_unpaid(de->otmp); + if (de->otmp->cursed && !rn2(WAND_BACKFIRE_CHANCE)) { + wand_explode(de->otmp, 0); + de->ret = ECMD_TIME; + return FALSE; } - zapwand = TRUE; + de->zapwand = TRUE; if (!can_reach_floor(TRUE)) - ptext = FALSE; - - switch (otmp->otyp) { - /* DUST wands */ - default: - break; - /* NODIR wands */ - case WAN_LIGHT: - case WAN_CREATE_MONSTER: - case WAN_WISHING: - case WAN_ENLIGHTENMENT: - zapnodir(otmp); - /* pre/postknown not needed; these will make it known if - * applicable */ - break; - case WAN_SECRET_DOOR_DETECTION: - if (!Blind) { - Strcpy(post_engr_text, - "You find many hidden bugs on the floor."); - postknown = TRUE; - } - zapnodir(otmp); - break; - /* IMMEDIATE wands */ - /* If wand is "IMMEDIATE", remember to affect the - * previous engraving even if turning to dust. - */ - case WAN_STRIKING: - Strcpy(post_engr_text, - "The wand unsuccessfully fights your attempt to write!"); - postknown = TRUE; - break; - case WAN_SLOW_MONSTER: - if (!Blind) { - Sprintf(post_engr_text, "The bugs on the %s slow down!", - surface(u.ux, u.uy)); - postknown = TRUE; - } - break; - case WAN_SPEED_MONSTER: - if (!Blind) { - Sprintf(post_engr_text, "The bugs on the %s speed up!", - surface(u.ux, u.uy)); - postknown = TRUE; - } - break; - case WAN_POLYMORPH: - if (oep) { - if (!Blind) { - type = (xint16) 0; /* random */ - (void) random_engraving(buf); - preknown = TRUE; - } else { - /* keep the same type so that feels don't - change and only the text is altered, - but you won't know anyway because - you're a _blind writer_ */ - if (oetype) - type = oetype; - xcrypt(blengr(), buf); - } - dengr = TRUE; - } - break; - case WAN_PROBING: - Sprintf(post_engr_text, "You probe the bugs on the floor."); - postknown = TRUE; - break; - case WAN_NOTHING: - case WAN_UNDEAD_TURNING: - case WAN_OPENING: - case WAN_LOCKING: - break; - /* RAY wands */ - case WAN_MAGIC_MISSILE: - ptext = TRUE; - if (!Blind) { - Sprintf(post_engr_text, - "The %s is riddled by bullet holes!", - surface(u.ux, u.uy)); - postknown = TRUE; - } - break; - /* can't tell sleep from death - Eric Backus */ - case WAN_SLEEP: - case WAN_DEATH: - if (!Blind) { - Sprintf(post_engr_text, "The bugs on the %s stop moving!", - surface(u.ux, u.uy)); - } - break; - case WAN_COLD: - if (!Blind) { - Strcpy(post_engr_text, - "A few ice cubes drop from the wand."); - postknown = TRUE; - } - if (!oep || (oep->engr_type != BURN)) - break; - /*FALLTHRU*/ - case WAN_CANCELLATION: - case WAN_MAKE_INVISIBLE: - if (oep && oep->engr_type != HEADSTONE) { - if (!Blind) - pline_The("engraving on the %s vanishes!", - surface(u.ux, u.uy)); - dengr = TRUE; - } - break; - case WAN_TELEPORTATION: - if (oep && oep->engr_type != HEADSTONE) { - if (!Blind) - pline_The("engraving on the %s vanishes!", - surface(u.ux, u.uy)); - teleengr = TRUE; - } - break; - /* type = ENGRAVE wands */ - case WAN_DIGGING: - ptext = TRUE; - type = ENGRAVE; - if (!objects[otmp->otyp].oc_name_known) { - if (Verbose(1, doengrave1)) - pline("This %s is a wand of digging!", xname(otmp)); - preknown = TRUE; - } - Strcpy(post_engr_text, - (Blind && !Deaf) - ? "You hear drilling!" /* Deaf-aware */ - : Blind - ? "You feel tremors." - : IS_GRAVE(levl[u.ux][u.uy].typ) - ? "Chips fly out from the headstone." - : frosted - ? "Ice chips fly up from the ice surface!" - : (gl.level.locations[u.ux][u.uy].typ - == DRAWBRIDGE_DOWN) - ? "Splinters fly up from the bridge." - : "Gravel flies up from the floor."); - break; - /* type = BURN wands */ - case WAN_FIRE: - ptext = TRUE; - type = BURN; - if (!objects[otmp->otyp].oc_name_known) { - if (Verbose(1, doengrave2)) - pline("This %s is a wand of fire!", xname(otmp)); - preknown = TRUE; - } - Strcpy(post_engr_text, Blind ? "You feel the wand heat up." - : "Flames fly from the wand."); - break; - case WAN_LIGHTNING: - ptext = TRUE; - type = BURN; - if (!objects[otmp->otyp].oc_name_known) { - if (Verbose(1, doengrave3)) - pline("This %s is a wand of lightning!", xname(otmp)); - preknown = TRUE; - } - if (!Blind) { - Strcpy(post_engr_text, "Lightning arcs from the wand."); - doblind = TRUE; - } else { - Strcpy(post_engr_text, !Deaf - ? "You hear crackling!" /* Deaf-aware */ - : "Your hair stands up!"); - } - break; - - /* type = MARK wands */ - /* type = ENGR_BLOOD wands */ - } + de->ptext = FALSE; + doengrave_sfx_item_WAN(de); } else { /* end if zappable */ /* failing to wrest one last charge takes time */ - ptext = FALSE; /* use "early exit" below, return 1 */ + de->ptext = FALSE; /* use "early exit" below, return 1 */ /* give feedback here if we won't be getting the "can't reach floor" message below */ if (can_reach_floor(TRUE)) { /* cancelled wand turns to dust */ - if (otmp->spe < 0) - zapwand = TRUE; + if (de->otmp->spe < 0) + de->zapwand = TRUE; /* empty wand just doesn't write */ else pline_The("wand is too worn out to engrave."); @@ -832,50 +808,58 @@ doengrave(void) break; case WEAPON_CLASS: - if (is_art(otmp, ART_FIRE_BRAND)) { - type = BURN; /* doesn't dull weapon */ - } else if (is_blade(otmp)) { - if ((int) otmp->spe > -3) - type = ENGRAVE; + if (is_art(de->otmp, ART_FIRE_BRAND)) { + de->type = BURN; /* doesn't dull weapon */ + } else if (is_blade(de->otmp)) { + /* if non-blade or welded or too dull, engraving type stays set + to DUST; feedback for that is only given for bladed weapons */ + if (welded(de->otmp)) + pline("%s can only scratch the %s.", + Yname2(de->otmp), surface(u.ux, u.uy)); + else if ((int) de->otmp->spe <= -3) + pline("%s too dull for engraving.", + Yobjnam2(de->otmp, "are")); else - pline("%s too dull for engraving.", Yobjnam2(otmp, "are")); + de->type = ENGRAVE; } break; case TOOL_CLASS: - if (otmp == ublindf) { + if (de->otmp == ublindf) { pline( "That is a bit difficult to engrave with, don't you think?"); - return ECMD_OK; + de->ret = ECMD_FAIL; + return FALSE; } - switch (otmp->otyp) { + switch (de->otmp->otyp) { case MAGIC_MARKER: - if (otmp->spe <= 0) + if (de->otmp->spe <= 0) Your("marker has dried out."); else - type = MARK; + de->type = MARK; break; case TOWEL: /* Can't really engrave with a towel */ - ptext = FALSE; - if (oep) { - if (oep->engr_type == DUST - || oep->engr_type == ENGR_BLOOD - || oep->engr_type == MARK) { - if (is_wet_towel(otmp)) - dry_a_towel(otmp, -1, TRUE); + de->ptext = FALSE; + if (de->oep) { + if (de->oep->engr_type == DUST + || de->oep->engr_type == ENGR_BLOOD + || de->oep->engr_type == MARK) { + if (is_wet_towel(de->otmp)) + dry_a_towel(de->otmp, -1, TRUE); if (!Blind) You("wipe out the message here."); else - pline("%s %s.", Yobjnam2(otmp, "get"), - frosted ? "frosty" : "dusty"); - dengr = TRUE; + pline("%s %s.", Yobjnam2(de->otmp, "get"), + de->frosted ? "frosty" : "dusty"); + de->dengr = TRUE; } else { - pline("%s can't wipe out this engraving.", Yname2(otmp)); + pline("%s can't wipe out this engraving.", + Yname2(de->otmp)); } } else { - pline("%s %s.", Yobjnam2(otmp, "get"), - frosted ? "frosty" : "dusty"); + pline("%s %s.", Yobjnam2(de->otmp, "get"), + de->frosted ? "frosty" : "dusty"); } break; default: @@ -895,15 +879,153 @@ doengrave(void) break; } + return TRUE; +} + +/* which verb phrasing to use for engraving */ +staticfn void +doengrave_ctx_verb(struct _doengrave_ctx *de) +{ + switch (de->type) { + default: + de->everb = de->adding ? "add to the weird writing on" + : "write strangely on"; + break; + case DUST: + de->everb = de->adding ? "add to the writing in" : "write in"; + de->eloc = de->frosted ? "frost" : "dust"; + break; + case HEADSTONE: + de->everb = de->adding ? "add to the epitaph on" : "engrave on"; + break; + case ENGRAVE: + de->everb = de->adding ? "add to the engraving in" : "engrave in"; + break; + case BURN: + de->everb = de->adding ? (de->frosted ? "add to the text melted into" + : "add to the text burned into") + : (de->frosted ? "melt into" : "burn into"); + break; + case MARK: + de->everb = de->adding ? "add to the graffiti on" : "scribble on"; + break; + case ENGR_BLOOD: + de->everb = de->adding ? "add to the scrawl on" : "scrawl on"; + break; + } +} + +/* Mohs' Hardness Scale: + * 1 - Talc 6 - Orthoclase + * 2 - Gypsum 7 - Quartz + * 3 - Calcite 8 - Topaz + * 4 - Fluorite 9 - Corundum + * 5 - Apatite 10 - Diamond + * + * Since granite is an igneous rock hardness ~ 7, anything >= 8 should + * probably be able to scratch the rock. + * Devaluation of less hard gems is not easily possible because obj struct + * does not contain individual oc_cost currently. 7/91 + * + * steel - 5-8.5 (usu. weapon) + * diamond - 10 * jade - 5-6 (nephrite) + * ruby - 9 (corundum) * turquoise - 5-6 + * sapphire - 9 (corundum) * opal - 5-6 + * topaz - 8 * glass - ~5.5 + * emerald - 7.5-8 (beryl) * dilithium - 4-5?? + * aquamarine - 7.5-8 (beryl) * iron - 4-5 + * garnet - 7.25 (var. 6.5-8) * fluorite - 4 + * agate - 7 (quartz) * brass - 3-4 + * amethyst - 7 (quartz) * gold - 2.5-3 + * jasper - 7 (quartz) * silver - 2.5-3 + * onyx - 7 (quartz) * copper - 2.5-3 + * moonstone - 6 (orthoclase) * amber - 2-2.5 + */ + +/* the #engrave command */ +int +doengrave(void) +{ + char *sp; /* Place holder for space count of engr text */ + struct _doengrave_ctx *de; + int retval; + + /* Can the adventurer engrave at all? */ + if (!u_can_engrave()) + return ECMD_FAIL; + + de = (struct _doengrave_ctx *) alloc(sizeof (struct _doengrave_ctx)); + doengrave_ctx_init(de); + + gm.multi = 0; /* moves consumed */ + gn.nomovemsg = (char *) 0; /* occupation end message */ + + /* One may write with finger, or weapon, or wand, or..., or... + * Edited by GAN 10/20/86 so as not to change weapon wielded. + */ + + de->otmp = getobj("write with", stylus_ok, GETOBJ_PROMPT); + if (!de->otmp) {/* otmp == &hands_obj if fingers */ + de->ret = ECMD_CANCEL; + goto doengr_exit; + } + + if (de->otmp == &hands_obj) { + Strcat(strcpy(de->fbuf, "your "), body_part(FINGERTIP)); + de->writer = de->fbuf; + } else { + de->writer = yname(de->otmp); + } + + /* There's no reason you should be able to write with a wand + * while both your hands are tied up. + */ + if (!freehand() && de->otmp != uwep && !de->otmp->owornmask) { + You("have no free %s to write with!", body_part(HAND)); + goto doengr_exit; + } + + if (de->jello) { + You("tickle %s with %s.", mon_nam(u.ustuck), de->writer); + 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 (IS_ALTAR(levl[u.ux][u.uy].typ)) { + You("make a motion towards the altar with %s.", de->writer); + altar_wrath(u.ux, u.uy); + goto doengr_exit; + } + if (IS_GRAVE(levl[u.ux][u.uy].typ)) { + if (de->otmp == &hands_obj) { /* using only finger */ + You("would only make a small smudge on the %s.", + surface(u.ux, u.uy)); + goto doengr_exit; + } else if (!levl[u.ux][u.uy].disturbed) { + /* disturb the grave: summon a ghoul, same as sometimes + happens when kicking; sets levl[ux][uy]->disturbed so + that it'll only happen once */ + disturb_grave(u.ux, u.uy); + goto doengr_exit; + } + } + + /* SPFX for items */ + if (!doengrave_sfx_item(de)) + goto doengr_exit; + if (IS_GRAVE(levl[u.ux][u.uy].typ)) { - if (type == ENGRAVE || type == 0) { - type = HEADSTONE; + if (de->type == ENGRAVE || de->type == 0) { + de->type = HEADSTONE; } else { /* ensures the "cannot wipe out" case */ - type = DUST; - dengr = FALSE; - teleengr = FALSE; - buf[0] = '\0'; + de->type = DUST; + de->dengr = FALSE; + de->teleengr = FALSE; + de->buf[0] = '\0'; } } @@ -912,186 +1034,178 @@ doengrave(void) */ /* Identify stylus */ - if (preknown) { - engraving_learn_wand(otmp); + if (de->preknown) { + engraving_learn_wand(de->otmp); } - if (teleengr) { - rloc_engr(oep); - oep->eread = 0; - disprefresh = TRUE; - oep = (struct engr *) 0; + if (de->teleengr) { + rloc_engr(de->oep); + de->oep->eread = 0; + de->oep->erevealed = 0; + de->disprefresh = TRUE; + de->oep = (struct engr *) 0; } - if (dengr) { - del_engr(oep); - oep = (struct engr *) 0; - disprefresh = TRUE; + if (de->dengr) { + del_engr(de->oep); + de->oep = (struct engr *) 0; + de->disprefresh = TRUE; } /* Something has changed the engraving here */ - if (*buf) { + if (*de->buf) { struct engr *tmp_ep; - make_engr_at(u.ux, u.uy, buf, gm.moves, type); + make_engr_at(u.ux, u.uy, de->buf, svm.moves, de->type); tmp_ep = engr_at(u.ux, u.uy); if (!Blind) { if (tmp_ep != 0) { - pline_The("engraving now reads: \"%s\".", buf); + pline_The("engraving now reads: \"%s\".", de->buf); tmp_ep->eread = 1; - disprefresh = TRUE; + tmp_ep->erevealed = 1; + de->disprefresh = TRUE; } } - ptext = FALSE; + de->ptext = FALSE; } - if (zapwand && (otmp->spe < 0 - || (otmp->otyp == WAN_WISHING && otmp->spe == 0))) { - pline("%s %sturns to dust.", The(xname(otmp)), + if (de->zapwand + && (de->otmp->spe < 0 + /* x:0 wands of wishing shouldn't exist in xnethack anymore, but + * just in case... */ + || (de->otmp->otyp == WAN_WISHING && de->otmp->spe == 0))) { + pline("%s %sturns to dust.", The(xname(de->otmp)), Blind ? "" : "glows violently, then "); if (!IS_GRAVE(levl[u.ux][u.uy].typ)) You( "are not going to get anywhere trying to write in the %s with your dust.", - frosted ? "frost" : "dust"); - useup(otmp); - otmp = 0; /* wand is now gone */ - ptext = FALSE; + de->frosted ? "frost" : "dust"); + useup(de->otmp); + de->otmp = 0; /* wand is now gone */ + de->ptext = FALSE; } /* Early exit for some implements. */ - if (!ptext) { - if (otmp && otmp->oclass == WAND_CLASS && !can_reach_floor(TRUE)) + if (!de->ptext) { + if (de->otmp && de->otmp->oclass == WAND_CLASS + && !can_reach_floor(TRUE)) cant_reach_floor(u.ux, u.uy, FALSE, TRUE); - if (disprefresh) - newsym(u.ux, u.uy); - return ECMD_TIME; + de->ret = ECMD_TIME; + goto doengr_exit; } /* * Special effects should have deleted the current engraving (if * possible) by now. */ - if (oep) { + if (de->oep) { char c = 'n'; /* Give player the choice to add to engraving. */ - if (type == HEADSTONE) { + if (de->type == HEADSTONE) { /* no choice, only append */ c = 'y'; - } else if (type == oep->engr_type - && (!Blind || oep->engr_type == BURN - || oep->engr_type == ENGRAVE)) { + } else if (de->type == de->oep->engr_type + && (!Blind || de->oep->engr_type == BURN + || de->oep->engr_type == ENGRAVE)) { c = yn_function("Do you want to add to the current engraving?", ynqchars, 'y', TRUE); if (c == 'q') { pline1(Never_mind); - return ECMD_OK; + goto doengr_exit; } } if (c == 'n' || Blind) { - if (oep->engr_type == DUST - || oep->engr_type == ENGR_BLOOD - || oep->engr_type == MARK) { + if (de->oep->engr_type == DUST + || de->oep->engr_type == ENGR_BLOOD + || de->oep->engr_type == MARK) { if (!Blind) { You("wipe out the message that was %s here.", - (oep->engr_type == DUST) - ? (frosted + (de->oep->engr_type == DUST) + ? (de->frosted ? "written in the frost" : "written in the dust") - : (oep->engr_type == ENGR_BLOOD) + : (de->oep->engr_type == ENGR_BLOOD) ? "scrawled in blood" : "written"); - del_engr(oep); - oep = (struct engr *) 0; - disprefresh = TRUE; + del_engr(de->oep); + de->oep = (struct engr *) 0; + de->disprefresh = TRUE; } else { /* defer deletion until after we *know* we're engraving */ - eow = TRUE; + de->eow = TRUE; } - } else if (type == DUST || type == MARK || type == ENGR_BLOOD) { + } else if (de->type == DUST || de->type == MARK + || de->type == ENGR_BLOOD) { You("cannot wipe out the message that is %s the %s here.", - (oep->engr_type == BURN) - ? (frosted ? "melted into" : "burned into") + (de->oep->engr_type == BURN) + ? (de->frosted ? "melted into" : "burned into") : "engraved in", surface(u.ux, u.uy)); - return ECMD_TIME; - } else if (type != oep->engr_type || c == 'n') { + de->ret = ECMD_TIME; + goto doengr_exit; + } else if (de->type != de->oep->engr_type || c == 'n') { if (!Blind || can_reach_floor(TRUE)) You("will overwrite the current message."); - eow = TRUE; + de->eow = TRUE; } - } else if (oep && Strlen(oep->engr_txt[actual_text]) >= BUFSZ - 1) { + } else if (de->oep + && Strlen(de->oep->engr_txt[actual_text]) >= BUFSZ - 1) { There("is no room to add anything else here."); - return ECMD_TIME; + de->ret = ECMD_TIME; + goto doengr_exit; } } - eloc = surface(u.ux, u.uy); - adding = (oep && !eow); - switch (type) { - default: - everb = adding ? "add to the weird writing on" : "write strangely on"; - break; - case DUST: - everb = adding ? "add to the writing in" : "write in"; - eloc = frosted ? "frost" : "dust"; - break; - case HEADSTONE: - everb = adding ? "add to the epitaph on" : "engrave on"; - break; - case ENGRAVE: - everb = adding ? "add to the engraving in" : "engrave in"; - break; - case BURN: - everb = adding ? (frosted ? "add to the text melted into" - : "add to the text burned into") - : (frosted ? "melt into" : "burn into"); - break; - case MARK: - everb = adding ? "add to the graffiti on" : "scribble on"; - break; - case ENGR_BLOOD: - everb = adding ? "add to the scrawl on" : "scrawl on"; - break; - } + de->eloc = surface(u.ux, u.uy); + de->adding = (de->oep && !de->eow); + doengrave_ctx_verb(de); /* Tell adventurer what is going on */ - if (otmp != &cg.zeroobj) - You("%s the %s with %s.", everb, eloc, doname(otmp)); + if (de->otmp != &hands_obj) + You("%s the %s with %s%s.", de->everb, de->eloc, + /* since doname() yields "N items" when quantity is more than + one, match that by using "1 of" rather than "one of" when + informing the player that the stack will be split */ + (de->type == ENGRAVE && de->otmp->quan > 1L) ? "1 of " : "", + doname(de->otmp)); else - You("%s the %s with your %s.", everb, eloc, body_part(FINGERTIP)); + You("%s the %s with your %s.", + de->everb, de->eloc, body_part(FINGERTIP)); /* Prompt for engraving! */ - Sprintf(qbuf, "What do you want to %s the %s here?", everb, eloc); - getlin(qbuf, ebuf); + Sprintf(de->qbuf, "What do you want to %s the %s here?", + de->everb, de->eloc); + getlin(de->qbuf, de->ebuf); /* convert tabs to spaces and condense consecutive spaces to one */ - mungspaces(ebuf); + mungspaces(de->ebuf); /* Count the actual # of chars engraved not including spaces */ - len = strlen(ebuf); - for (sp = ebuf; *sp; sp++) + de->len = strlen(de->ebuf); + for (sp = de->ebuf; *sp; sp++) if (*sp == ' ') - len -= 1; + de->len -= 1; - if (len == 0 || strchr(ebuf, '\033')) { - if (zapwand) { + if (de->len == 0 || strchr(de->ebuf, '\033')) { + if (de->zapwand) { if (!Blind) - pline("%s, then %s.", Tobjnam(otmp, "glow"), - otense(otmp, "fade")); - return ECMD_TIME; + pline("%s, then %s.", Tobjnam(de->otmp, "glow"), + otense(de->otmp, "fade")); + de->ret = ECMD_TIME; + goto doengr_exit; } else { pline1(Never_mind); - return ECMD_OK; + goto doengr_exit; } } /* A single `x' is the traditional signature of an illiterate person */ - if (len != 1 || (!strchr(ebuf, 'x') && !strchr(ebuf, 'X'))) + if (de->len != 1 || (!strchr(de->ebuf, 'x') && !strchr(de->ebuf, 'X'))) if (!u.uconduct.literate++) livelog_printf(LL_CONDUCT, "became literate by engraving \"%s\"", - ebuf); + de->ebuf); /* Mix up engraving if surface or state of mind is unsound. Note: this won't add or remove any spaces. */ - for (sp = ebuf; *sp; sp++) { + for (sp = de->ebuf; *sp; sp++) { if (*sp == ' ') continue; - if (((type == DUST || type == ENGR_BLOOD) && !rn2(25)) + if (((de->type == DUST || de->type == ENGR_BLOOD) && !rn2(25)) || (Blind && !rn2(11)) || (Confusion && !rn2(7)) || (Stunned && !rn2(4)) || (Hallucination && !rn2(2))) *sp = ' ' + rnd(96 - 2); /* ASCII '!' thru '~' @@ -1099,28 +1213,28 @@ doengrave(void) } /* Previous engraving is overwritten */ - if (eow) { - del_engr(oep); - oep = (struct engr *) 0; - disprefresh = TRUE; + if (de->eow) { + del_engr(de->oep); + de->oep = (struct engr *) 0; + de->disprefresh = TRUE; } - Strcpy(gc.context.engraving.text, ebuf); - gc.context.engraving.nextc = gc.context.engraving.text; - gc.context.engraving.stylus = otmp; - gc.context.engraving.type = type; - gc.context.engraving.pos.x = u.ux; - gc.context.engraving.pos.y = u.uy; - gc.context.engraving.actionct = 0; + Strcpy(svc.context.engraving.text, de->ebuf); + svc.context.engraving.nextc = svc.context.engraving.text; + svc.context.engraving.stylus = de->otmp; + svc.context.engraving.type = de->type; + svc.context.engraving.pos.x = u.ux; + svc.context.engraving.pos.y = u.uy; + svc.context.engraving.actionct = 0; set_occupation(engrave, "engraving", 0); - if (post_engr_text[0]) { - pline("%s", post_engr_text); - if (postknown) { - engraving_learn_wand(otmp); + if (de->post_engr_text[0]) { + pline("%s", de->post_engr_text); + if (de->postknown) { + engraving_learn_wand(de->otmp); } } - if (doblind && !resists_blnd(&gy.youmonst)) { + if (de->doblind && !resists_blnd(&gy.youmonst)) { You("are blinded by the flash!"); make_blinded((long) rnd(50), FALSE); if (!Blind) @@ -1128,45 +1242,48 @@ doengrave(void) } /* Engraving will always take at least one action via being run as an - * occupation, so do not count this setup as taking time. */ - if (disprefresh) + occupation, so do not count this setup as taking time. */ + doengr_exit: + if (de->disprefresh) newsym(u.ux, u.uy); - return ECMD_OK; + retval = de->ret; + free(de); + return retval; } /* occupation callback for engraving some text */ -static int +staticfn int engrave(void) { struct engr *oep; char buf[BUFSZ]; /* holds the post-this-action engr text, including * anything already there */ const char *finishverb; /* "You finish [foo]." */ - struct obj * stylus; /* shorthand for gc.context.engraving.stylus */ - boolean firsttime = (gc.context.engraving.actionct == 0); + struct obj * stylus; /* shorthand for svc.context.engraving.stylus */ + boolean firsttime = (svc.context.engraving.actionct == 0); int rate = 10; /* # characters that can be engraved in this action */ boolean truncate = FALSE; - boolean neweng = (gc.context.engraving.actionct == 0); + boolean neweng = (svc.context.engraving.actionct == 0); - boolean carving = (gc.context.engraving.type == ENGRAVE - || gc.context.engraving.type == HEADSTONE); + boolean carving = (svc.context.engraving.type == ENGRAVE + || svc.context.engraving.type == HEADSTONE); boolean dulling_wep, marker; char *endc; /* points at character 1 beyond the last character to engrave * this action */ int i, space_left; - if (gc.context.engraving.pos.x != u.ux - || gc.context.engraving.pos.y != u.uy) { /* teleported? */ + if (svc.context.engraving.pos.x != u.ux + || svc.context.engraving.pos.y != u.uy) { /* teleported? */ You("are unable to continue engraving."); return 0; } /* Stylus might have been taken out of inventory and destroyed somehow. * Not safe to dereference stylus until after this. */ - if (gc.context.engraving.stylus == &cg.zeroobj) { /* bare finger */ + if (svc.context.engraving.stylus == &hands_obj) { /* bare finger */ stylus = (struct obj *) 0; } else { for (stylus = gi.invent; stylus; stylus = stylus->nobj) { - if (stylus == gc.context.engraving.stylus) + if (stylus == svc.context.engraving.stylus) break; } if (!stylus) { @@ -1178,14 +1295,14 @@ engrave(void) dulling_wep = (carving && stylus && stylus->oclass == WEAPON_CLASS && (stylus->otyp != ATHAME || stylus->cursed)); marker = (stylus && stylus->otyp == MAGIC_MARKER - && gc.context.engraving.type == MARK); + && svc.context.engraving.type == MARK); - gc.context.engraving.actionct++; + svc.context.engraving.actionct++; /* sanity checks */ if (dulling_wep && !is_blade(stylus)) { impossible("carving with non-bladed weapon"); - } else if (gc.context.engraving.type == MARK && !marker) { + } else if (svc.context.engraving.type == MARK && !marker) { impossible("making graffiti with non-marker stylus"); } @@ -1202,7 +1319,7 @@ engrave(void) /* Step 2: Compute last character that can be engraved this action. */ i = rate; - for (endc = gc.context.engraving.nextc; *endc && i > 0; endc++) { + for (endc = svc.context.engraving.nextc; *endc && i > 0; endc++) { if (*endc != ' ') { i--; } @@ -1210,6 +1327,22 @@ engrave(void) /* Step 3: affect stylus from engraving - it might wear out. */ if (dulling_wep) { + boolean splitstack = FALSE, dulled = FALSE; + + /* 'dulling_wep' guarantees that 'stylus' is a weapon which is + not welded to the hero's hand(s) */ + if (stylus->quan > 1L) { + if (firsttime) + pline("One of %s gets dull.", yname(stylus)); + stylus = svc.context.engraving.stylus = splitobj(stylus, 1L); + /* if stack is wielded or quivered, the split-off one isn't */ + stylus->owornmask = 0L; + splitstack = TRUE; + } else { + /* normal case: stylus->quan==1 */ + if (firsttime) + pline("%s gets dull.", Yname2(stylus)); + } /* Dull the weapon at a rate of -1 enchantment per 2 characters, * rounding down. * The number of characters obtainable given starting enchantment: @@ -1218,10 +1351,7 @@ engrave(void) * engrave "Elbereth" all at once. * However, you can engrave "Elb", then "ere", then "th", by taking * advantage of the rounding down. */ - if (firsttime) { - pline("%s dull.", Yobjnam2(stylus, "get")); - } - if (gc.context.engraving.actionct % 2 == 1) { /* 1st,3rd,... action */ + if (svc.context.engraving.actionct % 2 == 1) { /* 1st,3rd,... action */ /* deduct a point on 1st, 3rd, 5th, ... turns, unless this is the * last character being engraved (a rather convoluted way to round * down), but always deduct a point on the 1st turn to prevent @@ -1234,11 +1364,22 @@ engrave(void) impossible("<= -3 weapon valid for engraving"); } truncate = TRUE; - } else if (*endc || gc.context.engraving.actionct == 1) { + } else if (*endc || svc.context.engraving.actionct == 1) { stylus->spe -= 1; - update_inventory(); + dulled = TRUE; } } + if (splitstack) { + 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 */ + prinv((char *) NULL, stylus, 1L); + update_inventory(); + } } else if (marker) { int ink_cost = max(rate / 2, 1); /* Prevent infinite graffiti */ @@ -1256,7 +1397,7 @@ engrave(void) } } - switch (gc.context.engraving.type) { + switch (svc.context.engraving.type) { default: finishverb = "your weird engraving"; break; @@ -1287,9 +1428,9 @@ engrave(void) Strcpy(buf, oep->engr_txt[actual_text]); space_left = (int) (sizeof buf - strlen(buf) - 1U); - if (endc - gc.context.engraving.nextc > space_left) { + if (endc - svc.context.engraving.nextc > space_left) { You("run out of room to write."); - endc = gc.context.engraving.nextc + space_left; + endc = svc.context.engraving.nextc + space_left; truncate = TRUE; } @@ -1297,25 +1438,27 @@ engrave(void) * can't go any further. */ if (truncate && *endc != '\0') { *endc = '\0'; - You("are only able to write \"%s\".", gc.context.engraving.text); + You("are only able to write \"%s\".", svc.context.engraving.text); } else { /* input was not truncated; stylus may still have worn out on the last * character, though */ truncate = FALSE; } - (void) strncat(buf, gc.context.engraving.nextc, - min(space_left, endc - gc.context.engraving.nextc)); - make_engr_at(u.ux, u.uy, buf, gm.moves - gm.multi, - gc.context.engraving.type); + (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, + svc.context.engraving.type); oep = engr_at(u.ux, u.uy); - if (oep) + if (oep) { oep->eread = 1; + oep->erevealed = 1; + } if (*endc) { - gc.context.engraving.nextc = endc; + svc.context.engraving.nextc = endc; if (neweng) { - newsym(gc.context.engraving.pos.x, gc.context.engraving.pos.y); + newsym(svc.context.engraving.pos.x, svc.context.engraving.pos.y); } return 1; /* not yet finished this turn */ } else { /* finished engraving */ @@ -1329,17 +1472,17 @@ engrave(void) /* only print this if engraving took multiple actions */ You("finish %s.", finishverb); } - gc.context.engraving.text[0] = '\0'; - gc.context.engraving.nextc = (char *) 0; - gc.context.engraving.stylus = (struct obj *) 0; + svc.context.engraving.text[0] = '\0'; + svc.context.engraving.nextc = (char *) 0; + svc.context.engraving.stylus = (struct obj *) 0; } if (neweng) - newsym(gc.context.engraving.pos.x, gc.context.engraving.pos.y); + newsym(svc.context.engraving.pos.x, svc.context.engraving.pos.y); return 0; } /* Learn what a wand is by engraving with it. */ -static void +staticfn void engraving_learn_wand(struct obj *obj) { learnwand(obj); @@ -1361,6 +1504,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) { @@ -1399,9 +1558,9 @@ save_engravings(NHFILE *nhfp) if (ep->engr_alloc && ep->engr_txt[actual_text][0] && perform_bwrite(nhfp)) { if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t)&(ep->engr_alloc), + bwrite(nhfp->fd, (genericptr_t) &(ep->engr_alloc), sizeof ep->engr_alloc); - bwrite(nhfp->fd, (genericptr_t)ep, + bwrite(nhfp->fd, (genericptr_t) ep, sizeof (struct engr) + ep->engr_alloc); } } @@ -1410,7 +1569,8 @@ save_engravings(NHFILE *nhfp) } if (perform_bwrite(nhfp)) { if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t)&no_more_engr, sizeof no_more_engr); + bwrite(nhfp->fd, (genericptr_t) &no_more_engr, + sizeof no_more_engr); } if (release_data(nhfp)) head_engr = 0; @@ -1425,19 +1585,21 @@ rest_engravings(NHFILE *nhfp) head_engr = 0; while (1) { if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) <h, sizeof(unsigned)); + mread(nhfp->fd, (genericptr_t) <h, sizeof (unsigned)); if (lth == 0) return; ep = newengr(lth); if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) ep, sizeof(struct engr) + lth); + mread(nhfp->fd, (genericptr_t) ep, sizeof (struct engr) + lth); } 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[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; while (ep->engr_txt[actual_text][0] == ' ') ep->engr_txt[actual_text]++; while (ep->engr_txt[remembered_text][0] == ' ') @@ -1445,7 +1607,7 @@ rest_engravings(NHFILE *nhfp) /* mark as finished for bones levels -- no problem for * normal levels as the player must have finished engraving * to be able to move again */ - ep->engr_time = gm.moves; + ep->engr_time = svm.moves; } } @@ -1543,9 +1705,9 @@ disturb_grave(coordxy x, coordxy y) struct rm *lev = &levl[x][y]; if (!IS_GRAVE(lev->typ)) { - impossible("Disturing grave that isn't a grave? (%d)", lev->typ); + impossible("Disturbing grave that isn't a grave? (%d)", lev->typ); } else if (lev->disturbed) { - impossible("Disturing already disturbed grave?"); + impossible("Disturbing already disturbed grave?"); } else { You("disturb the undead!"); lev->disturbed = 1; @@ -1560,15 +1722,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] = { @@ -1592,10 +1757,10 @@ static const char blind_writing[][21] = { 0x69, 0x76, 0x6b, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, }; -static const char * +staticfn const char * blengr(void) { - return blind_writing[rn2(SIZE(blind_writing))]; + return ROLL_FROM(blind_writing); } /*engrave.c*/ diff --git a/src/exper.c b/src/exper.c index 0f4b2e3d55..7738ad542e 100644 --- a/src/exper.c +++ b/src/exper.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 exper.c $NHDT-Date: 1621380393 2021/05/18 23:26:33 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.46 $ */ +/* NetHack 3.7 exper.c $NHDT-Date: 1706133782 2024/01/24 22:03:02 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.62 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2007. */ /* NetHack may be freely redistributed. See license for details. */ @@ -109,9 +109,9 @@ newpw(void) /* return # of exp points for mtmp after nk killed */ int -experience(register struct monst *mtmp, register int nk) +experience(struct monst *mtmp, int nk) { - register struct permonst *ptr = mtmp->data; + struct permonst *ptr = mtmp->data; int i, tmp, tmp2; tmp = 1 + mtmp->m_lev * mtmp->m_lev; @@ -193,7 +193,7 @@ experience(register struct monst *mtmp, register int nk) } void -more_experienced(register int exper, register int rexp) +more_experienced(int exper, int rexp) { long oldexp = u.uexp, oldrexp = u.urexp, @@ -210,23 +210,23 @@ more_experienced(register int exper, register int rexp) if (newexp != oldexp) { u.uexp = newexp; if (flags.showexp) - gc.context.botl = TRUE; + disp.botl = TRUE; /* even when experience points aren't being shown, experience level might be highlighted with a percentage highlight rule and that percentage depends upon experience points */ - if (!gc.context.botl && exp_percent_changing()) - gc.context.botl = TRUE; + if (!disp.botl && exp_percent_changing()) + disp.botl = TRUE; } /* newrexp will always differ from oldrexp unless they're LONG_MAX */ if (newrexp != oldrexp) { u.urexp = newrexp; #ifdef SCORE_ON_BOTL if (flags.showscore) - gc.context.botl = TRUE; + disp.botl = TRUE; #endif } if (u.urexp >= (Role_if(PM_WIZARD) ? 1000 : 2000)) - flags.beginner = 0; + flags.beginner = FALSE; } /* e.g., hit by drain life attack */ @@ -249,37 +249,43 @@ losexp( in that situation */ if (u.ulevel > 1 || drainer) pline("%s level %d.", Goodbye(), u.ulevel); + if (u.ulevel > 1) { u.ulevel -= 1; /* remove intrinsic abilities */ adjabil(u.ulevel + 1, u.ulevel); livelog_printf(LL_MINORAC, "lost experience level %d", u.ulevel + 1); SoundAchievement(0, sa2_xpleveldown, 0); - } else { + } else { /* u.ulevel==1 */ if (drainer) { - gk.killer.format = KILLED_BY; - if (gk.killer.name != drainer) - Strcpy(gk.killer.name, drainer); + svk.killer.format = KILLED_BY; + if (svk.killer.name != drainer) + Strcpy(svk.killer.name, drainer); done(DIED); } /* no drainer or lifesaved */ + if (u.ulevel > 1) + /* can happen during debug fuzzing if fuzzer_savelife() uses + a blessed potion of restore ability to restore lost levels */ + return; u.uexp = 0; livelog_printf(LL_MINORAC, "lost all experience"); } + assert(u.ulevel >= 0 && u.ulevel < MAXULEV); /* valid array index */ olduhpmax = u.uhpmax; uhpmin = minuhpmax(10); /* same minimum as is used by life-saving */ num = (int) u.uhpinc[u.ulevel]; u.uhpmax -= num; if (u.uhpmax < uhpmin) - setuhpmax(uhpmin); + setuhpmax(uhpmin, TRUE); /* uhpmax might try to go up if it has previously been reduced by strength loss or by a fire trap or by an attack by Death which all use a different minimum than life-saving or experience loss; we don't allow it to go up because that contradicts assumptions elsewhere (such as healing wielder who drains with Stormbringer) */ if (u.uhpmax > olduhpmax) - setuhpmax(olduhpmax); + setuhpmax(olduhpmax, TRUE); u.uhp -= num; if (u.uhp < 1) @@ -306,13 +312,13 @@ losexp( u.mh -= num; if (u.mh <= 0) { /* in case we die here */ - Strcpy(gk.killer.name, "fragility"); - gk.killer.format = KILLED_BY; + Strcpy(svk.killer.name, "fragility"); + svk.killer.format = KILLED_BY; rehumanize(); } } - gc.context.botl = TRUE; + disp.botl = TRUE; } /* @@ -331,7 +337,8 @@ newexplevel(void) void pluslvl( boolean incr) /* True: incremental experience growth; - * False: potion of gain level or wraith corpse */ + * False: potion of gain level or wraith corpse + * or wizard mode #levelchange */ { int hpinc, eninc; @@ -342,12 +349,13 @@ pluslvl( in order to retain normal human/whatever increase for later) */ if (Upolyd) { hpinc = monhp_per_lvl(&gy.youmonst); - u.mhmax += hpinc; u.mh += hpinc; + setuhpmax(u.mhmax, FALSE); /* acts as setmhmax() when Upolyd */ } hpinc = newhp(); - setuhpmax(u.uhpmax + hpinc); u.uhp += hpinc; + setuhpmax(u.uhpmax + hpinc, TRUE); /* will lower u.uhp if it exceeds + * u.uhpmax */ /* increase spell power/energy points */ eninc = newpw(); @@ -391,7 +399,7 @@ pluslvl( if (u.ulevel > u.ulevelpeak) u.ulevelpeak = u.ulevel; } - gc.context.botl = TRUE; + disp.botl = TRUE; } /* compute a random amount of experience points suitable for the hero's diff --git a/src/explode.c b/src/explode.c index bd5514136c..3865ebae14 100644 --- a/src/explode.c +++ b/src/explode.c @@ -1,11 +1,11 @@ -/* 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. */ #include "hack.h" -static int explosionmask(struct monst *, uchar, char); -static void engulfer_explosion_msg(uchar, char); +staticfn int explosionmask(struct monst *, uchar, char) NONNULLARG1; +staticfn void engulfer_explosion_msg(uchar, char); /* Note: Arrays are column first, while the screen is row first */ static const int explosion[3][3] = { @@ -22,7 +22,7 @@ enum explode_action { }; /* check if shield effects are needed for location affected by explosion */ -static int +staticfn int explosionmask( struct monst *m, /* target monster (might be youmonst) */ uchar adtyp, /* damage type */ @@ -115,7 +115,7 @@ explosionmask( return res; } -static void +staticfn void engulfer_explosion_msg(uchar adtyp, char olet) { const char *adj = (char *) 0; @@ -301,9 +301,9 @@ explode( */ if (olet == MON_EXPLODE && !you_exploding) { - /* when explode() is called recursively, gk.killer.name might change so - we need to retain a copy of the current value for this explosion */ - str = strcpy(killr_buf, gk.killer.name); + /* when explode() is called recursively, svk.killer.name might change + so retain a copy of the current value for this explosion */ + str = strcpy(killr_buf, svk.killer.name); do_hallu = (Hallucination && (strstri(str, "'s explosion") || strstri(str, "s' explosion"))); @@ -473,15 +473,16 @@ explode( * with an explosion attack, leave them (and their gear) * unharmed, to avoid punishing them from using such * polyforms creatively */ - if (!gc.context.mon_moving && you_exploding) + if (!svc.context.mon_moving && you_exploding) uhurt = 0; } else if (inside_engulfer) { /* for inside_engulfer, only is affected */ continue; } + /* Affect the floor unless the player caused the explosion * from inside their engulfer. */ - if (!(u.uswallow && !gc.context.mon_moving)) + if (!(u.uswallow && !svc.context.mon_moving)) (void) zap_over_floor(xx, yy, type, &shopdamage, FALSE, exploding_wand_typ); @@ -519,19 +520,19 @@ explode( } if ((explmask[i][j] & EXPL_MON) != 0) { - /* damage from ring/wand explosion isn't itself - * electrical in nature, nor is damage from freezing potion - * really cold in nature, nor is damage from boiling potion - * or exploding oil; only burning items damage is the "same - * type" as the explosion. Because this is imperfect and - * marginal (burning items only deal 1 damage), ignore it - * for golemeffects(). */ + /* Damage from ring/wand explosion isn't itself + * electrical in nature, nor is damage from freezing + * potion really cold in nature, nor is damage from + * boiling potion or exploding oil; only burning items + * damage is the "same type" as the explosion. Because + * this is imperfect and marginal (burning items only + * deal 1 damage), ignore it for golemeffects(). */ golemeffects(mtmp, (int) adtyp, dam); - mtmp->mhp -= itemdmg; + mtmp->mhp -= itemdmg; /* item destruction dmg */ } else { - /* call resist with 0 and do damage manually so 1) we can + /* Call resist with 0 and do damage manually so 1) we can * get out the message before doing the damage, and 2) we - * can call mondied, not killed, if it's not your blast + * can call mondied, not killed, if it's not your blast. */ int mdam = dam; @@ -561,7 +562,7 @@ explode( && completelyburns(mtmp->data)) ? XKILL_NOCORPSE : 0); - if (!gc.context.mon_moving) { + if (!svc.context.mon_moving) { xkilled(mtmp, XKILL_GIVEMSG | xkflg); } else if (mdef && mtmp == mdef) { /* 'mdef' killed self trying to cure being turned @@ -583,7 +584,7 @@ explode( adtyp = AD_RBRE; /* no corpse */ monkilled(mtmp, "", (int) adtyp); } - } else if (!gc.context.mon_moving) { + } else if (!svc.context.mon_moving) { /* all affected monsters, even if mdef is set */ setmangry(mtmp, TRUE); } @@ -595,7 +596,7 @@ explode( if (uhurt) { /* give message for any monster-induced explosion or player-induced one other than scroll of fire */ - if (Verbose(1, explode) && (type < 0 || olet != SCROLL_CLASS)) { + if (flags.verbose && (type < 0 || olet != SCROLL_CLASS)) { if (do_hallu) { /* (see explanation above) */ do { Sprintf(hallu_buf, "%s explosion", @@ -633,37 +634,44 @@ explode( u.mh -= damu; else u.uhp -= damu; - gc.context.botl = 1; + disp.botl = TRUE; } /* You resisted the damage, lets not keep that to ourselves */ if (uhurt == 1) monstseesu_ad(adtyp); + else + monstunseesu_ad(adtyp); if (u.uhp <= 0 || (Upolyd && u.mh <= 0)) { if (olet == MON_EXPLODE) { if (generic) /* explosion was unseen; str=="explosion", */ - ; /* gk.killer.name=="gas spore's explosion". */ - else if (str != gk.killer.name && str != hallu_buf) - Strcpy(gk.killer.name, str); - gk.killer.format = KILLED_BY_AN; - } else if ((olet == BURNING_OIL && gc.context.mon_moving) + ; /* svk.killer.name=="gas spore's explosion". */ + else if (str != svk.killer.name && str != hallu_buf) + Strcpy(svk.killer.name, str); + svk.killer.format = KILLED_BY_AN; + } else if (olet == TRAP_EXPLODE) { + svk.killer.format = NO_KILLER_PREFIX; + Snprintf(svk.killer.name, sizeof svk.killer.name, + "caught %sself in a %s", uhim(), + str); + } else if ((olet == BURNING_OIL && svc.context.mon_moving) || olet == EXPLODING_DOOR) { - gk.killer.format = KILLED_BY_AN; - Snprintf(gk.killer.name, sizeof gk.killer.name, + svk.killer.format = KILLED_BY_AN; + Snprintf(svk.killer.name, sizeof svk.killer.name, "exploding %s", olet == BURNING_OIL ? "fire bomb" : "door"); } else if (type >= 0 && olet != SCROLL_CLASS) { - gk.killer.format = NO_KILLER_PREFIX; - Snprintf(gk.killer.name, sizeof gk.killer.name, + svk.killer.format = NO_KILLER_PREFIX; + Snprintf(svk.killer.name, sizeof svk.killer.name, "caught %sself in %s own %s", uhim(), uhis(), str); } else { - gk.killer.format = (!strcmpi(str, "tower of flame") + svk.killer.format = (!strcmpi(str, "tower of flame") || !strcmpi(str, "fireball")) ? KILLED_BY_AN : KILLED_BY; - Strcpy(gk.killer.name, str); + Strcpy(svk.killer.name, str); } if (Upolyd) { @@ -728,8 +736,8 @@ scatter(coordxy sx, coordxy sy, /* location of objects to scatter */ unsigned int scflags, struct obj *obj) /* only scatter this obj */ { - register struct obj *otmp; - register int tmp; + struct obj *otmp; + int tmp; int farthest = 0; uchar typ; long qtmp; @@ -750,7 +758,8 @@ scatter(coordxy sx, coordxy sy, /* location of objects to scatter */ if (shop_origin) credit_report(shkp, 0, TRUE); /* establish baseline, without msgs */ - while ((otmp = (individual_object ? obj : gl.level.objects[sx][sy])) != 0) { + while ((otmp = (individual_object ? obj + : svl.level.objects[sx][sy])) != 0) { if (otmp == uball || otmp == uchain) { boolean waschain = (otmp == uchain); @@ -811,7 +820,7 @@ scatter(coordxy sx, coordxy sy, /* location of objects to scatter */ } else if ((scflags & MAY_DESTROY) != 0 && (!rn2(10) || (otmp->material == GLASS || otmp->otyp == EGG))) { - if (breaks(otmp, (coordxy) sx, (coordxy) sy)) + if (breaks(otmp, sx, sy)) used_up = TRUE; } @@ -850,7 +859,10 @@ scatter(coordxy sx, coordxy sy, /* location of objects to scatter */ gt.thrownobj = stmp->obj; /* mainly in case it kills hero */ gb.bhitpos.x = stmp->ox + stmp->dx; gb.bhitpos.y = stmp->oy + stmp->dy; - typ = levl[gb.bhitpos.x][gb.bhitpos.y].typ; + if (isok(gb.bhitpos.x, gb.bhitpos.y)) + typ = levl[gb.bhitpos.x][gb.bhitpos.y].typ; + else + typ = STONE; if (!isok(gb.bhitpos.x, gb.bhitpos.y)) { gb.bhitpos.x -= stmp->dx; gb.bhitpos.y -= stmp->dy; @@ -924,7 +936,8 @@ scatter(coordxy sx, coordxy sy, /* location of objects to scatter */ retrieve the item and drop it back inside the shop, the owed charges will only be reduced at that point by the lesser shopkeeper buying-price. - The non-gold situation will likely get adjusted further. + The non-gold situation will likely get adjusted + further. */ if (stmp->obj->otyp == GOLD_PIECE) { addtobill(stmp->obj, FALSE, FALSE, TRUE); @@ -984,6 +997,7 @@ explode_oil(struct obj *obj, coordxy x, coordxy y) if (cansee(x, y)) { makeknown(obj->otyp); } + obj->how_lost = LOST_EXPLODING; splatter_burning_oil(x, y, diluted_oil); } @@ -1016,12 +1030,14 @@ adtyp_to_expltype(const int adtyp) } } -/* A monster explodes in a way that produces a real explosion (e.g. a sphere or - * gas spore, not a yellow light or similar). +/* A monster explodes in a way that produces a real explosion (e.g. a sphere + * or gas spore, not a yellow light or similar). * This is some common code between explmu() and explmm(). */ void -mon_explodes(struct monst *mon, struct attack *mattk) +mon_explodes( + struct monst *mon, + struct attack *mattk) { int dmg; int type; @@ -1039,8 +1055,9 @@ mon_explodes(struct monst *mon, struct attack *mattk) type = PHYS_EXPL_TYPE; } else if (mattk->adtyp >= AD_MAGM && mattk->adtyp <= AD_SPC2) { - /* The -1, +20, *-1 math is to set it up as a 'monster breath' type for - * the explosions (it isn't, but this is the closest analogue). */ + /* The -1, +20, *-1 math is to set it up as a 'monster breath' type + * for the explosions (it isn't, but this is the closest analogue). */ + /* FIXME: there are macros for kind of thing... */ type = -((mattk->adtyp - 1) + 20); } else { @@ -1049,17 +1066,17 @@ mon_explodes(struct monst *mon, struct attack *mattk) } /* Kill it now so it won't appear to be caught in its own explosion. - * Must check to see if already dead - which happens if this is called from - * an AT_BOOM attack upon death. */ + * Must check to see if already dead - which happens if this is called + * from an AT_BOOM attack upon death. */ if (!DEADMONSTER(mon)) { mondead(mon); } /* This might end up killing you, too; you never know... * also, it is used in explode() messages */ - Sprintf(gk.killer.name, "%s explosion", + Sprintf(svk.killer.name, "%s explosion", s_suffix(pmname(mon->data, Mgender(mon)))); - gk.killer.format = KILLED_BY_AN; + svk.killer.format = KILLED_BY_AN; explode(mon->mx, mon->my, type, dmg, MON_EXPLODE, adtyp_to_expltype(mattk->adtyp)); @@ -1078,7 +1095,7 @@ mon_explodes(struct monst *mon, struct attack *mattk) } /* reset killer */ - gk.killer.name[0] = '\0'; + svk.killer.name[0] = '\0'; } /* A monster explodes in a way that doesn't produce a "real" damage-causing @@ -1142,7 +1159,7 @@ mon_explodes_nodmg(struct monst *magr, struct attack *mattk) make_blinded((long) severity, FALSE); if (!Blind) Your1(vision_clears); - } else if (Verbose(1, explmu)) /* FIXME: not in explmu anymore */ + } else if (flags.verbose) You("get the impression it was not terribly bright."); } break; diff --git a/src/extralev.c b/src/extralev.c index af8a082bbd..11208f2af2 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. */ @@ -11,3 +11,6 @@ * which is frequently hardcoded along with all the other source files. So * I've brought back extralev.c even though it contains no code. */ +/* -Werror=pedantic will complain that this file is empty, so provide a dummy + * typedef to make the compiler happy */ +typedef int satisfy_iso_c_compilers; diff --git a/src/files.c b/src/files.c index 290930e907..2235be738d 100644 --- a/src/files.c +++ b/src/files.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 files.c $NHDT-Date: 1680625799 2023/04/04 16:29:59 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.373 $ */ +/* NetHack 3.7 files.c $NHDT-Date: 1737346561 2025/01/19 20:16:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.416 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -7,7 +7,6 @@ #include "hack.h" #include "dlb.h" -#include #ifdef TTY_GRAPHICS #include "wintty.h" /* more() */ @@ -52,7 +51,6 @@ const #if defined(UNIX) && defined(SELECTSAVED) #include #include -#include #endif #if defined(UNIX) || defined(VMS) || !defined(NO_SIGNAL) @@ -136,121 +134,122 @@ extern char *translate_path_variables(const char *, char *); extern char *sounddir; /* defined in sounds.c */ #endif -static NHFILE *new_nhfile(void); -static void free_nhfile(NHFILE *); +staticfn NHFILE *new_nhfile(void); +staticfn void free_nhfile(NHFILE *); #ifdef SELECTSAVED -static int QSORTCALLBACK strcmp_wrap(const void *, const void *); +staticfn int QSORTCALLBACK strcmp_wrap(const void *, const void *); #endif -static char *set_bonesfile_name(char *, d_level *); -static char *set_bonestemp_name(void); +staticfn char *set_bonesfile_name(char *, d_level *); +staticfn char *set_bonestemp_name(void); #ifdef COMPRESS -static void redirect(const char *, const char *, FILE *, boolean); +staticfn void redirect(const char *, const char *, FILE *, boolean); #endif #if defined(COMPRESS) || defined(ZLIB_COMP) -static void docompress_file(const char *, boolean); +staticfn void docompress_file(const char *, boolean); #endif #if defined(ZLIB_COMP) -static boolean make_compressed_name(const char *, char *); +staticfn boolean make_compressed_name(const char *, char *); #endif #ifndef USE_FCNTL -static char *make_lockname(const char *, char *); +staticfn char *make_lockname(const char *, char *); #endif -static void set_configfile_name(const char *); -static FILE *fopen_config_file(const char *, int); -static int get_uchars(char *, uchar *, boolean, int, const char *); +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 -static void adjust_prefix(char *, int); +staticfn void adjust_prefix(char *, int); #endif -static char *choose_random_part(char *, char); -static boolean config_error_nextline(const char *); -static void free_config_sections(void); -static char *is_config_section(char *); -static boolean handle_config_section(char *); +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 *); -static char *find_optparam(const char *); -static boolean cnf_line_OPTIONS(char *); -static boolean cnf_line_AUTOPICKUP_EXCEPTION(char *); -static boolean cnf_line_BINDINGS(char *); -static boolean cnf_line_AUTOCOMPLETE(char *); -static boolean cnf_line_MSGTYPE(char *); -static boolean cnf_line_HACKDIR(char *); -static boolean cnf_line_LEVELDIR(char *); -static boolean cnf_line_SAVEDIR(char *); -static boolean cnf_line_BONESDIR(char *); -static boolean cnf_line_DATADIR(char *); -static boolean cnf_line_SCOREDIR(char *); -static boolean cnf_line_LOCKDIR(char *); -static boolean cnf_line_CONFIGDIR(char *); -static boolean cnf_line_TROUBLEDIR(char *); -static boolean cnf_line_NAME(char *); -static boolean cnf_line_ROLE(char *); -static boolean cnf_line_dogname(char *); -static boolean cnf_line_catname(char *); -static boolean cnf_line_WIZARDS(char *); -static boolean cnf_line_SHELLERS(char *); -static boolean cnf_line_EXPLORERS(char *); -static boolean cnf_line_DEBUGFILES(char *); -static boolean cnf_line_DUMPLOGFILE(char *); -static boolean cnf_line_GENERICUSERS(char *); -static boolean cnf_line_BONES_POOLS(char *); -static boolean cnf_line_SUPPORT(char *); -static boolean cnf_line_RECOVER(char *); -static boolean cnf_line_CHECK_SAVE_UID(char *); -static boolean cnf_line_CHECK_PLNAME(char *); -static boolean cnf_line_SEDUCE(char *); -static boolean cnf_line_HIDEUSAGE(char *); -static boolean cnf_line_MAXPLAYERS(char *); -static boolean cnf_line_PERSMAX(char *); -static boolean cnf_line_PERS_IS_UID(char *); -static boolean cnf_line_ENTRYMAX(char *); -static boolean cnf_line_POINTSMIN(char *); -static boolean cnf_line_MAX_STATUENAME_RANK(char *); -static boolean cnf_line_LIVELOG(char *); -static boolean cnf_line_PANICTRACE_LIBC(char *); -static boolean cnf_line_PANICTRACE_GDB(char *); -static boolean cnf_line_GDBPATH(char *); -static boolean cnf_line_GREPPATH(char *); -static boolean cnf_line_SAVEFORMAT(char *); -static boolean cnf_line_BONESFORMAT(char *); -static boolean cnf_line_ACCESSIBILITY(char *); -static boolean cnf_line_PORTABLE_DEVICE_PATHS(char *); -static boolean cnf_line_BOULDER(char *); -static boolean cnf_line_MENUCOLOR(char *); -static boolean cnf_line_HILITE_STATUS(char *); -static boolean cnf_line_WARNINGS(char *); -static boolean cnf_line_ROGUESYMBOLS(char *); -static boolean cnf_line_SYMBOLS(char *); -static boolean cnf_line_WIZKIT(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_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_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 -static boolean cnf_line_SOUNDDIR(char *); -static boolean cnf_line_SOUND(char *); +staticfn boolean cnf_line_SOUNDDIR(char *); +staticfn boolean cnf_line_SOUND(char *); #endif -static boolean cnf_line_QT_TILEWIDTH(char *); -static boolean cnf_line_QT_TILEHEIGHT(char *); -static boolean cnf_line_QT_FONTSIZE(char *); -static boolean cnf_line_QT_COMPACT(char *); +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 *); /* xNetHack specific or otherwise non vanilla configs: */ -static boolean cnf_line_MONSTERCOLOR(char *); -static boolean cnf_line_DUMPHTMLFILE(char *); -static boolean cnf_line_SERVERSEED(char *); +staticfn boolean cnf_line_MONSTERCOLOR(char *); +staticfn boolean cnf_line_DUMPHTMLFILE(char *); +staticfn boolean cnf_line_SERVERSEED(char *); struct _cnf_parser_state; /* defined below (far below...) */ -static void cnf_parser_init(struct _cnf_parser_state *parser); -static void cnf_parser_done(struct _cnf_parser_state *parser); -static void parse_conf_buf(struct _cnf_parser_state *parser, +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)); -static boolean parse_conf_file(FILE *fp, boolean (*proc)(char *arg)); -static void parseformat(int *, char *); -static FILE *fopen_wizkit_file(void); -static void wizkit_addinv(struct obj *); +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); -static FILE *fopen_sym_file(void); - -#ifdef SELF_RECOVER -static boolean copy_bytes(int, int); -#endif -static NHFILE *viable_nhfile(NHFILE *); +void read_wizkit(void); /* in extern.h; why here too? */ +staticfn FILE *fopen_sym_file(void); +staticfn NHFILE *viable_nhfile(NHFILE *); /* return a file's name without its path and optionally trailing 'type' */ const char * @@ -304,7 +303,7 @@ nh_basename(const char *fname, boolean keep_suffix) * * Sample: * The following call: - * (void)fname_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", + * (void) fname_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", * '%', "This is a % test!", buf, 512); * results in this encoding: * "This%20is%20a%20%25%20test%21" @@ -334,7 +333,8 @@ fname_encode( (void) sprintf(op, "%c%02X", quotechar, *sp); op += 3; cnt += 3; - } else if ((strchr(legal, *sp) != 0) || (strchr(hexdigits, *sp) != 0)) { + } else if ((strchr(legal, *sp) != 0) + || (strchr(hexdigits, *sp) != 0)) { *op++ = *sp; *op = '\0'; cnt++; @@ -368,7 +368,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? */ @@ -510,38 +509,36 @@ fopen_datafile(const char *filename, const char *mode, int prefix) /* ---------- EXTERNAL FILE SUPPORT ----------- */ /* determine byte order */ -const int bei = 1; +static const int bei = 1; #define IS_BIGENDIAN() ( (*(char*)&bei) == 0 ) void zero_nhfile(NHFILE *nhfp) { - if (nhfp) { - nhfp->fd = -1; - nhfp->mode = COUNTING; - nhfp->structlevel = FALSE; - 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->eof = FALSE; - nhfp->fnidx = 0; - } -} - -static NHFILE * + nhfp->fd = -1; + nhfp->mode = COUNTING; + nhfp->structlevel = FALSE; + 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->eof = FALSE; + nhfp->fnidx = 0; +} + +staticfn NHFILE * new_nhfile(void) { - NHFILE *nhfp = (NHFILE *)alloc(sizeof(NHFILE)); + NHFILE *nhfp = (NHFILE *) alloc(sizeof(NHFILE)); zero_nhfile(nhfp); return nhfp; } -static void +staticfn void free_nhfile(NHFILE *nhfp) { if (nhfp) { @@ -553,12 +550,10 @@ free_nhfile(NHFILE *nhfp) void close_nhfile(NHFILE *nhfp) { - if (nhfp) { - if (nhfp->structlevel && nhfp->fd != -1) - (void) nhclose(nhfp->fd), nhfp->fd = -1; - zero_nhfile(nhfp); - free_nhfile(nhfp); - } + if (nhfp->structlevel && nhfp->fd != -1) + (void) nhclose(nhfp->fd), nhfp->fd = -1; + zero_nhfile(nhfp); + free_nhfile(nhfp); } void @@ -573,8 +568,7 @@ rewind_nhfile(NHFILE *nhfp) } } -static -NHFILE * +staticfn NHFILE * viable_nhfile(NHFILE *nhfp) { /* perform some sanity checks before returning @@ -596,7 +590,7 @@ viable_nhfile(NHFILE *nhfp) /* ---------- BEGIN LEVEL FILE HANDLING ----------- */ /* Construct a file name for a level-type file, which is of the form - * somethingl.level (with any old level stripped off). + * something.level (with any old level stripped off). * This assumes there is space on the end of 'file' to append * a two digit number. This is true for 'level' * but be careful if you use it for other things -dgk @@ -653,7 +647,7 @@ create_levelfile(int lev, char errbuf[]) #endif /* MICRO || WIN32 */ if (nhfp->fd >= 0) - gl.level_info[lev].flags |= LFILE_EXISTS; + svl.level_info[lev].flags |= LFILE_EXISTS; else if (errbuf) /* failure explanation */ Sprintf(errbuf, "Cannot create file \"%s\" for level %d (errno %d).", @@ -711,10 +705,10 @@ delete_levelfile(int lev) * Level 0 might be created by port specific code that doesn't * call create_levfile(), so always assume that it exists. */ - if (lev == 0 || (gl.level_info[lev].flags & LFILE_EXISTS)) { + if (lev == 0 || (svl.level_info[lev].flags & LFILE_EXISTS)) { set_levelfile_name(gl.lock, lev); (void) unlink(fqname(gl.lock, LEVELPREFIX, 0)); - gl.level_info[lev].flags &= ~LFILE_EXISTS; + svl.level_info[lev].flags &= ~LFILE_EXISTS; } } @@ -724,7 +718,7 @@ clearlocks(void) int x; #ifdef HANGUPHANDLING - if (gp.program_state.preserve_locks) + if (program_state.preserve_locks) return; #endif #ifndef NO_SIGNAL @@ -734,7 +728,7 @@ clearlocks(void) #endif #endif /* NO_SIGNAL */ /* can't access maxledgerno() before dungeons are created -dlc */ - for (x = (gn.n_dgns ? maxledgerno() : 0); x >= 0; x--) + for (x = (svn.n_dgns ? maxledgerno() : 0); x >= 0; x--) delete_levelfile(x); /* not all levels need be present */ #ifdef WHEREIS_FILE @@ -744,14 +738,10 @@ clearlocks(void) #if defined(SELECTSAVED) /* qsort comparison routine */ -static int QSORTCALLBACK +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 @@ -776,14 +766,15 @@ set_whereisfile(void) { char *p = (char *) strstr(whereis_file, "%n"); if (p) { - int new_whereis_len = strlen(whereis_file) + strlen(gp.plname) - 2; /* %n */ + int new_whereis_len = strlen(whereis_file) + + strlen(svp.plname) - 2; /* %n */ char *new_whereis_fn = (char *) alloc((unsigned)(new_whereis_len + 1)); char *q = new_whereis_fn; strncpy(q, whereis_file, p - whereis_file); q += p - whereis_file; - strncpy(q, gp.plname, strlen(gp.plname) + 1); + strncpy(q, svp.plname, strlen(svp.plname) + 1); regularize(q); - q[strlen(gp.plname)] = '\0'; + q[strlen(svp.plname)] = '\0'; q += strlen(q); p += 2; /* skip "%n" */ strncpy(q, p, strlen(p)); @@ -803,13 +794,13 @@ write_whereis(boolean playing) /* < True if game is running */ set_whereisfile(); Sprintf(whereis_work, "player=%s:depth=%d:dnum=%d:dname=%s:hp=%d:maxhp=%d:turns=%ld:score=%ld:role=%s:race=%s:gender=%s:align=%s:conduct=0x%lx:amulet=%d:ascended=%d:playing=%d\n", - gp.plname, + svp.plname, depth(&u.uz), u.uz.dnum, - gd.dungeons[u.uz.dnum].dname, + svd.dungeons[u.uz.dnum].dname, u.uhp, u.uhpmax, - gm.moves, + svm.moves, #ifdef SCORE_ON_BOTL botl_score(), #else @@ -825,7 +816,7 @@ write_whereis(boolean playing) /* < True if game is running */ 0L, #endif u.uhave.amulet ? 1 : 0, - u.uevent.ascended ? 2 : gp.program_state.gameover ? 1 : 0, + u.uevent.ascended ? 2 : program_state.gameover ? 1 : 0, playing); fp = fopen_datafile(whereis_file,"w",LEVELPREFIX); @@ -869,7 +860,7 @@ delete_whereis(void) /* set up "file" to be file name for retrieving bones, and return a * bonesid to be read/written in the bones file. */ -static char * +staticfn char * set_bonesfile_name(char *file, d_level *lev) { s_level *sptr; @@ -900,7 +891,7 @@ set_bonesfile_name(char *file, d_level *lev) dptr = eos(file); /* when this naming scheme was adopted, 'filecode' was one letter; 3.3.0 turned it into a three letter string for quest levels */ - Sprintf(dptr, "%c%s", gd.dungeons[lev->dnum].boneid, + Sprintf(dptr, "%c%s", svd.dungeons[lev->dnum].boneid, In_quest(lev) ? gu.urole.filecode : "0"); if ((sptr = Is_special(lev)) != 0) Sprintf(eos(dptr), ".%c", sptr->boneid); @@ -918,7 +909,7 @@ set_bonesfile_name(char *file, d_level *lev) * (we are not reading or writing level files while writing bones files, so * the same array may be used instead of copying.) */ -static char * +staticfn char * set_bonestemp_name(void) { char *tf; @@ -1062,7 +1053,7 @@ compress_bonesfile(void) /* ---------- BEGIN SAVE FILE HANDLING ----------- */ -/* set savefile name in OS-dependent manner from pre-existing gp.plname, +/* set savefile name in OS-dependent manner from pre-existing svp.plname, * avoiding troublesome characters */ void set_savefile_name(boolean regularize_it) @@ -1076,21 +1067,21 @@ set_savefile_name(boolean regularize_it) #endif #ifdef VMS - Sprintf(gs.SAVEF, "[.save]%d%s", getuid(), gp.plname); + Sprintf(gs.SAVEF, "[.save]%d%s", getuid(), svp.plname); regoffset = 7; indicator_spot = 1; postappend = ";1"; #endif #if defined(WIN32) if (regularize_it) { - static const char okchars[] = - "*ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-."; + static const char okchars[] + = "*ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-."; const char *legal = okchars; ++legal; /* skip '*' wildcard character */ - (void) fname_encode(legal, '%', gp.plname, tmp, sizeof tmp); + (void) fname_encode(legal, '%', svp.plname, tmp, sizeof tmp); } else { - Sprintf(tmp, "%s", gp.plname); + Sprintf(tmp, "%s", svp.plname); } if (strlen(tmp) < (SAVESIZE - 1)) Strcpy(gs.SAVEF, tmp); @@ -1100,7 +1091,7 @@ set_savefile_name(boolean regularize_it) regularize_it = FALSE; #endif #ifdef UNIX - Sprintf(gs.SAVEF, "save/%d%s", (int) getuid(), gp.plname); + Sprintf(gs.SAVEF, "save/%d%s", (int) getuid(), svp.plname); regoffset = 5; indicator_spot = 2; #endif @@ -1108,9 +1099,9 @@ set_savefile_name(boolean regularize_it) if (strlen(gs.SAVEP) < (SAVESIZE - 1)) Strcpy(gs.SAVEF, gs.SAVEP); if (strlen(gs.SAVEF) < (SAVESIZE - 1)) - (void) strncat(gs.SAVEF, gp.plname, (SAVESIZE - strlen(gs.SAVEF))); + (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 @@ -1121,11 +1112,11 @@ set_savefile_name(boolean regularize_it) { int i = strlen(gs.SAVEP); #ifdef AMIGA - /* gp.plname has to share space with gs.SAVEP and ".sav" */ - (void) strncat(gs.SAVEF, gp.plname, + /* svp.plname has to share space with gs.SAVEP and ".sav" */ + (void) strncat(gs.SAVEF, svp.plname, FILENAME - i - strlen(SAVE_EXTENSION)); #else - (void) strncat(gs.SAVEF, gp.plname, 8); + (void) strncat(gs.SAVEF, svp.plname, 8); #endif regoffset = i; } @@ -1167,7 +1158,8 @@ set_savefile_name(boolean regularize_it) } #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) if (overflow) - impossible("set_savefile_name() couldn't complete without overflow %d", + impossible("set_savefile_name() couldn't complete" + " without overflow %d", overflow); #endif } @@ -1219,8 +1211,9 @@ create_savefile(void) nhfp->fieldlevel = FALSE; nhfp->ftype = NHF_SAVEFILE; nhfp->mode = WRITING; - if (gp.program_state.in_self_recover || do_historical) { + 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; @@ -1273,8 +1266,9 @@ open_savefile(void) nhfp->fieldlevel = FALSE; nhfp->ftype = NHF_SAVEFILE; nhfp->mode = READING; - if (gp.program_state.in_self_recover || do_historical) { + 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; @@ -1316,7 +1310,7 @@ restore_saved_game(void) nh_uncompress(fq_save); if ((nhfp = open_savefile()) != 0) { - if (validate(nhfp, fq_save) != 0) { + if (validate(nhfp, fq_save, FALSE) != 0) { close_nhfile(nhfp); nhfp = (NHFILE *)0; if (y_n("Delete the save file?") == 'y') @@ -1326,11 +1320,59 @@ restore_saved_game(void) return nhfp; } +/* called if there is no save file for current character */ +int +check_panic_save(void) +{ + int result = 0; +#ifdef CHECK_PANIC_SAVE + FILE *cf; + const char *savef; + + set_error_savefile(); + savef = fqname(gs.SAVEF, SAVEPREFIX, 0); + + /* + * This duplicates part of docompress_file(). + * We don't want to start by uncompressing just to check for the + * file's existence and then have to recompress it. + */ + +#ifdef COMPRESS_EXTENSION + unsigned ln = (unsigned) (strlen(savef) + strlen(COMPRESS_EXTENSION)); + char *cfn = (char *) alloc(ln + 1); + + Strcpy(cfn, savef); + Strcat(cfn, COMPRESS_EXTENSION); + if ((cf = fopen(cfn, RDBMODE)) != NULL) { + (void) fclose(cf); + result = 1; + } + free((genericptr_t) cfn); +#endif /* COMPRESS_EXTENSION */ + + if (!result) { + /* maybe it has already been manually uncompressed */ + if ((cf = fopen(savef, RDBMODE)) != NULL) { + (void) fclose(cf); + result = 1; + } + } + + set_savefile_name(TRUE); /* reset to normal */ +#endif /* CHECK_PANIC_SAVE */ + return result; +} + #if defined(SELECTSAVED) + char * -plname_from_file(const char *filename) +plname_from_file( + const char *filename, + boolean without_wait_synch_per_file) { - NHFILE *nhfp = (NHFILE *) 0; + NHFILE *nhfp; + unsigned ln; char *result = 0; Strcpy(gs.SAVEF, filename); @@ -1346,68 +1388,46 @@ plname_from_file(const char *filename) #endif nh_uncompress(gs.SAVEF); if ((nhfp = open_savefile()) != 0) { - if (validate(nhfp, filename) == 0) { - char tplname[PL_NSIZ]; - - get_plname_from_file(nhfp, tplname); - result = dupstr(tplname); + if (validate(nhfp, filename, without_wait_synch_per_file) == 0) { + /* 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; + int i, count_failures = 0; - Strcpy(gp.plname, "*"); + Strcpy(svp.plname, "*"); set_savefile_name(FALSE); #if defined(ZLIB_COMP) Strcat(gs.SAVEF, COMPRESS_EXTENSION); @@ -1423,8 +1443,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 { @@ -1434,16 +1454,21 @@ 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]); + 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(gp.plname, r); + Strcpy(svp.plname, r); set_savefile_name(TRUE); fq_new_save = fqname(gs.SAVEF, SAVEPREFIX, 0); fq_old_save = fqname(files[i], SAVEPREFIX, 1); @@ -1451,16 +1476,19 @@ 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++; } } } free_saved_games(files); - + if (count_failures) + wait_synch(); } -#endif +#endif /* WIN32 */ #ifdef UNIX /* posixly correct version */ int myuid = getuid(); @@ -1479,7 +1507,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) @@ -1490,7 +1518,8 @@ get_saved_games(void) char *r; Sprintf(filename, "save/%d%s", uid, name); - r = plname_from_file(filename); + r = plname_from_file(filename, + ALLOW_WAITSYNCH_PERFILE); if (r) result[j++] = r; } @@ -1499,9 +1528,9 @@ get_saved_games(void) closedir(dir); } } -#endif +#endif /* UNIX */ #ifdef VMS - Strcpy(gp.plname, "*"); + Strcpy(svp.plname, "*"); set_savefile_name(FALSE); j = vms_get_saved_games(gs.SAVEF, &result); #endif /* VMS */ @@ -1509,23 +1538,26 @@ 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 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); } } @@ -1536,7 +1568,7 @@ free_saved_games(char **saved) #ifdef COMPRESS /* external compression */ -static void +staticfn void redirect( const char *filename, const char *mode, @@ -1564,7 +1596,7 @@ redirect( * * cf. child() in unixunix.c. */ -static void +staticfn void docompress_file(const char *filename, boolean uncomp) { char *cfn = 0; @@ -1740,6 +1772,7 @@ docompress_file(const char *filename, boolean uncomp) } free((genericptr_t) cfn); + return; } #endif /* COMPRESS : external compression */ @@ -1754,11 +1787,7 @@ docompress_file(const char *filename, boolean uncomp) void nh_compress(const char *filename UNUSED_if_not_COMPRESS) { -#if !defined(COMPRESS) && !defined(ZLIB_COMP) -#ifdef PRAGMA_UNUSED -#pragma unused(filename) -#endif -#else +#if defined(COMPRESS) || defined(ZLIB_COMP) docompress_file(filename, FALSE); #endif } @@ -1767,17 +1796,13 @@ nh_compress(const char *filename UNUSED_if_not_COMPRESS) void nh_uncompress(const char *filename UNUSED_if_not_COMPRESS) { -#if !defined(COMPRESS) && !defined(ZLIB_COMP) -#ifdef PRAGMA_UNUSED -#pragma unused(filename) -#endif -#else +#if defined(COMPRESS) || defined(ZLIB_COMP) docompress_file(filename, TRUE); #endif } #ifdef ZLIB_COMP /* RLC 09 Mar 1999: Support internal ZLIB */ -static boolean +staticfn boolean make_compressed_name(const char *filename, char *cfn) { #ifndef SHORT_FILENAMES @@ -1808,7 +1833,7 @@ make_compressed_name(const char *filename, char *cfn) #endif /* SHORT_FILENAMES */ } -static void +staticfn void docompress_file(const char *filename, boolean uncomp) { gzFile compressedfile; @@ -1932,6 +1957,8 @@ docompress_file(const char *filename, boolean uncomp) } #endif /* RLC 09 Mar 1999: End ZLIB patch */ +#undef UNUSED_if_not_COMPRESS + /* ---------- END FILE COMPRESSION HANDLING ----------- */ /* ---------- BEGIN FILE LOCKING HANDLING ----------- */ @@ -1940,18 +1967,27 @@ docompress_file(const char *filename, boolean uncomp) static int lockfd = -1; /* for lock_file() to pass to unlock_file() */ #endif #ifdef USE_FCNTL -struct flock sflock; /* for unlocking, same as above */ +static struct flock sflock; /* for unlocking, same as above */ #endif #if defined(HANGUPHANDLING) -#define HUP if (!gp.program_state.done_hup) +#define HUP if (!program_state.done_hup) #else #define HUP #endif + +#if defined(UNIX) || defined(VMS) || defined(AMIGA) || defined(WIN32) \ + || defined(MSDOS) +#define UNUSED_conditional /*empty*/ +#else +#define UNUSED_conditional UNUSED +#endif + + #ifndef USE_FCNTL -static char * -make_lockname(const char *filename, char *lockname) +staticfn char * +make_lockname(const char *filename UNUSED_conditional, char *lockname) { #if defined(UNIX) || defined(VMS) || defined(AMIGA) || defined(WIN32) \ || defined(MSDOS) @@ -1974,9 +2010,6 @@ make_lockname(const char *filename, char *lockname) #endif return lockname; #else /* !(UNIX || VMS || AMIGA || WIN32 || MSDOS) */ -#ifdef PRAGMA_UNUSED -#pragma unused(filename) -#endif lockname[0] = '\0'; return (char *) 0; #endif @@ -1985,12 +2018,9 @@ make_lockname(const char *filename, char *lockname) /* lock a file */ boolean -lock_file(const char *filename, int whichprefix, int retryct) +lock_file(const char *filename, int whichprefix, + int retryct UNUSED_conditional) { -#if defined(PRAGMA_UNUSED) && !(defined(UNIX) || defined(VMS)) \ - && !(defined(AMIGA) || defined(WIN32) || defined(MSDOS)) -#pragma unused(retryct) -#endif #ifndef USE_FCNTL char locknambuf[BUFSZ]; const char *lockname; @@ -2012,7 +2042,8 @@ lock_file(const char *filename, int whichprefix, int retryct) #ifdef USE_FCNTL lockfd = open(filename, O_RDWR); if (lockfd == -1) { - HUP raw_printf("Cannot open file %s. Is xNetHack installed correctly?", + HUP raw_printf("Cannot open file %s. " + " Is xNetHack installed correctly?", filename); gn.nesting--; return FALSE; @@ -2036,8 +2067,8 @@ lock_file(const char *filename, int whichprefix, int retryct) #ifdef USE_FCNTL if (retryct--) { - HUP raw_printf( - "Waiting for release of fcntl lock on %s. (%d retries left.)", + HUP raw_printf("Waiting for release of fcntl lock on %s. " + " (%d retries left.)", filename, retryct); sleep(1); } else { @@ -2053,7 +2084,8 @@ lock_file(const char *filename, int whichprefix, int retryct) switch (errnosv) { /* George Barbanis */ case EEXIST: if (retryct--) { - HUP raw_printf("Waiting for access to %s. (%d retries left).", + HUP raw_printf("Waiting for access to %s. " + " (%d retries left).", filename, retryct); #if defined(SYSV) || defined(ULTRIX) || defined(VMS) (void) @@ -2088,8 +2120,8 @@ lock_file(const char *filename, int whichprefix, int retryct) /* take a wild guess at the underlying cause */ HUP perror(lockname); HUP raw_printf("Cannot lock %s.", filename); - HUP raw_printf( - "(Perhaps you are running xNetHack from inside the distribution package?)"); + HUP raw_printf("(Perhaps you are running xNetHack from" + " inside the distribution package?)"); gn.nesting--; return FALSE; default: @@ -2189,11 +2221,13 @@ unlock_file(const char *filename) gn.nesting--; } +#undef UNUSED_conditional + /* ---------- END FILE LOCKING HANDLING ----------- */ /* ---------- BEGIN CONFIG FILE HANDLING ----------- */ -const char *default_configfile = +static const char *default_configfile = #ifdef UNIX ".xnethackrc"; #else @@ -2208,7 +2242,7 @@ const char *default_configfile = #endif #endif -/* used for messaging */ +/* used for messaging. Also used in options.c */ char configfile[BUFSZ]; #ifdef MSDOS @@ -2238,8 +2272,8 @@ do_write_config_file(void) wait_synch(); pline("Some settings are not saved!"); wait_synch(); - pline( - "All manual customization and comments are removed from the file!"); + pline("All manual customization and comments are removed" + " from the file!"); wait_synch(); } #define overwrite_prompt "Overwrite config file %.*s?" @@ -2261,7 +2295,7 @@ do_write_config_file(void) fclose(fp); strbuf_empty(&buf); if (wrote != len) - pline("An error occurred, wrote only partial data (%lu/%lu).", + pline("An error occurred, wrote only partial data (%zu/%zu).", wrote, len); } return ECMD_OK; @@ -2269,14 +2303,14 @@ do_write_config_file(void) /* remember the name of the file we're accessing; if may be used in option reject messages */ -static void +staticfn void set_configfile_name(const char *fname) { (void) strncpy(configfile, fname, sizeof configfile - 1); configfile[sizeof configfile - 1] = '\0'; } -static FILE * +staticfn FILE * fopen_config_file(const char *filename, int src) { FILE *fp; @@ -2417,7 +2451,7 @@ fopen_config_file(const char *filename, int src) * NOTE: zeros are inserted unless modlist is TRUE, in which case the list * location is unchanged. Callers must handle zeros if modlist is FALSE. */ -static int +staticfn int get_uchars(char *bufp, /* current pointer */ uchar *list, /* return list */ boolean modlist, /* TRUE: list is being modified in place */ @@ -2477,7 +2511,7 @@ get_uchars(char *bufp, /* current pointer */ } #ifdef NOCWD_ASSUMPTIONS -static void +staticfn void adjust_prefix(char *bufp, int prefixid) { char *ptr; @@ -2500,7 +2534,7 @@ adjust_prefix(char *bufp, int prefixid) #endif /* Choose at random one of the sep separated parts from str. Mangles str. */ -static char * +staticfn char * choose_random_part(char *str, char sep) { int nsep = 1; @@ -2538,7 +2572,7 @@ choose_random_part(char *str, char sep) return (char *) 0; } -static void +staticfn void free_config_sections(void) { if (gc.config_section_chosen) { @@ -2554,7 +2588,7 @@ free_config_sections(void) /* check for " [ anything-except-bracket-or-empty ] # arbitrary-comment" with spaces optional; returns pointer to "anything-except..." (with trailing " ] #..." stripped) if ok, otherwise Null */ -static char * +staticfn char * is_config_section( char *str) /* trailing spaces are stripped, ']' too iff result is good */ { @@ -2583,7 +2617,7 @@ is_config_section( return trimspaces(a); } -static boolean +staticfn boolean handle_config_section(char *buf) { char *sect = is_config_section(buf); @@ -2598,7 +2632,8 @@ handle_config_section(char *buf) } if (*sect) { /* got a section name */ gc.config_section_current = dupstr(sect); - debugpline1("set config section: '%s'", gc.config_section_current); + debugpline1("set config section: '%s'", + gc.config_section_current); } else { /* empty section name => end of sections */ free_config_sections(); debugpline0("unset config section"); @@ -2618,7 +2653,7 @@ handle_config_section(char *buf) #define match_varname(INP, NAM, LEN) match_optname(INP, NAM, LEN, TRUE) /* find the '=' or ':' */ -static char * +staticfn char * find_optparam(const char *buf) { char *bufp, *altp; @@ -2631,7 +2666,7 @@ find_optparam(const char *buf) return bufp; } -static boolean +staticfn boolean cnf_line_OPTIONS(char *origbuf) { char *bufp = find_optparam(origbuf); @@ -2640,33 +2675,33 @@ cnf_line_OPTIONS(char *origbuf) return parseoptions(bufp, TRUE, TRUE); } -static boolean +staticfn boolean cnf_line_AUTOPICKUP_EXCEPTION(char *bufp) { add_autopickup_exception(bufp); return TRUE; } -static boolean +staticfn boolean cnf_line_BINDINGS(char *bufp) { return parsebindings(bufp); } -static boolean +staticfn boolean cnf_line_AUTOCOMPLETE(char *bufp) { parseautocomplete(bufp, TRUE); return TRUE; } -static boolean +staticfn boolean cnf_line_MSGTYPE(char *bufp) { return msgtype_parse_add(bufp); } -static boolean +staticfn boolean cnf_line_HACKDIR(char *bufp) { #ifdef NOCWD_ASSUMPTIONS @@ -2681,7 +2716,7 @@ cnf_line_HACKDIR(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_LEVELDIR(char *bufp) { #ifdef NOCWD_ASSUMPTIONS @@ -2701,7 +2736,7 @@ cnf_line_LEVELDIR(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_SAVEDIR(char *bufp) { #ifdef NOCWD_ASSUMPTIONS @@ -2723,7 +2758,7 @@ cnf_line_SAVEDIR(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_BONESDIR(char *bufp) { #ifdef NOCWD_ASSUMPTIONS @@ -2734,7 +2769,7 @@ cnf_line_BONESDIR(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_DATADIR(char *bufp) { #ifdef NOCWD_ASSUMPTIONS @@ -2745,7 +2780,7 @@ cnf_line_DATADIR(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_SCOREDIR(char *bufp) { #ifdef NOCWD_ASSUMPTIONS @@ -2756,7 +2791,7 @@ cnf_line_SCOREDIR(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_LOCKDIR(char *bufp) { #ifdef NOCWD_ASSUMPTIONS @@ -2767,7 +2802,7 @@ cnf_line_LOCKDIR(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_CONFIGDIR(char *bufp) { #ifdef NOCWD_ASSUMPTIONS @@ -2778,7 +2813,7 @@ cnf_line_CONFIGDIR(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_TROUBLEDIR(char *bufp) { #ifdef NOCWD_ASSUMPTIONS @@ -2789,14 +2824,14 @@ cnf_line_TROUBLEDIR(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_NAME(char *bufp) { - (void) strncpy(gp.plname, bufp, PL_NSIZ - 1); + (void) strncpy(svp.plname, bufp, PL_NSIZ - 1); return TRUE; } -static boolean +staticfn boolean cnf_line_ROLE(char *bufp) { int len; @@ -2806,21 +2841,23 @@ cnf_line_ROLE(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_dogname(char *bufp) { (void) strncpy(gd.dogname, bufp, PL_PSIZ - 1); return TRUE; } -static boolean +staticfn boolean cnf_line_catname(char *bufp) { (void) strncpy(gc.catname, bufp, PL_PSIZ - 1); return TRUE; } -static boolean +#ifdef SYSCF + +staticfn boolean cnf_line_WIZARDS(char *bufp) { if (sysopt.wizards) @@ -2837,7 +2874,7 @@ cnf_line_WIZARDS(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_SHELLERS(char *bufp) { if (sysopt.shellers) @@ -2846,7 +2883,16 @@ cnf_line_SHELLERS(char *bufp) return TRUE; } -static boolean +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) @@ -2855,13 +2901,12 @@ cnf_line_EXPLORERS(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_DEBUGFILES(char *bufp) { - /* if showdebug() has already been called (perhaps we've added - some debugpline() calls to option processing) and has found - a value for getenv("DEBUGFILES"), don't override that */ - if (sysopt.env_dbgfl <= 0) { + /* 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); @@ -2869,7 +2914,7 @@ cnf_line_DEBUGFILES(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_DUMPLOGFILE(char *bufp) { #ifdef DUMPLOG @@ -2882,7 +2927,7 @@ cnf_line_DUMPLOGFILE(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_GENERICUSERS(char *bufp) { if (sysopt.genericusers) @@ -2891,7 +2936,7 @@ cnf_line_GENERICUSERS(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_BONES_POOLS(char *bufp) { /* max value of 10 guarantees (N % bones.pools) will be one digit @@ -2905,7 +2950,7 @@ cnf_line_BONES_POOLS(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_SUPPORT(char *bufp) { if (sysopt.support) @@ -2914,7 +2959,7 @@ cnf_line_SUPPORT(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_RECOVER(char *bufp) { if (sysopt.recover) @@ -2923,7 +2968,7 @@ cnf_line_RECOVER(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_CHECK_SAVE_UID(char *bufp) { int n = atoi(bufp); @@ -2932,7 +2977,7 @@ cnf_line_CHECK_SAVE_UID(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_CHECK_PLNAME(char *bufp) { int n = atoi(bufp); @@ -2941,7 +2986,7 @@ cnf_line_CHECK_PLNAME(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_SEDUCE(char *bufp) { int n = !!atoi(bufp); /* XXX this could be tighter */ @@ -2963,7 +3008,7 @@ cnf_line_SEDUCE(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_HIDEUSAGE(char *bufp) { int n = !!atoi(bufp); @@ -2972,7 +3017,7 @@ cnf_line_HIDEUSAGE(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_MAXPLAYERS(char *bufp) { int n = atoi(bufp); @@ -2986,7 +3031,7 @@ cnf_line_MAXPLAYERS(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_PERSMAX(char *bufp) { int n = atoi(bufp); @@ -2999,7 +3044,7 @@ cnf_line_PERSMAX(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_PERS_IS_UID(char *bufp) { int n = atoi(bufp); @@ -3012,7 +3057,7 @@ cnf_line_PERS_IS_UID(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_ENTRYMAX(char *bufp) { int n = atoi(bufp); @@ -3025,7 +3070,7 @@ cnf_line_ENTRYMAX(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_POINTSMIN(char *bufp) { int n = atoi(bufp); @@ -3038,21 +3083,21 @@ cnf_line_POINTSMIN(char *bufp) return TRUE; } -static boolean +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)"); + config_error_add("Illegal value in MAX_STATUENAME_RANK" + " (minimum is 1)"); n = 10; } sysopt.tt_oname_maxrank = n; return TRUE; } -static boolean +staticfn boolean cnf_line_LIVELOG(char *bufp) { /* using 0 for base accepts "dddd" as decimal provided that first 'd' @@ -3061,15 +3106,15 @@ cnf_line_LIVELOG(char *bufp) long L = strtol(bufp, NULL, 0); if (L < 0L || L > 0xffffffL) { - config_error_add( - "Illegal value for LIVELOG (must be between 0 and 0xFFFF)."); + config_error_add("Illegal value for LIVELOG" + " (must be between 0 and 0xFFFF)."); return 0; } sysopt.livelog = L; return TRUE; } -static boolean +staticfn boolean cnf_line_PANICTRACE_LIBC(char *bufp) { int n = atoi(bufp); @@ -3084,7 +3129,7 @@ cnf_line_PANICTRACE_LIBC(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_PANICTRACE_GDB(char *bufp) { int n = atoi(bufp); @@ -3099,7 +3144,7 @@ cnf_line_PANICTRACE_GDB(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_GDBPATH(char *bufp) { #if defined(PANICTRACE) && !defined(VMS) @@ -3114,7 +3159,7 @@ cnf_line_GDBPATH(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_GREPPATH(char *bufp) { #if defined(PANICTRACE) && !defined(VMS) @@ -3129,21 +3174,30 @@ cnf_line_GREPPATH(char *bufp) return TRUE; } -static boolean +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; } -static boolean +staticfn boolean cnf_line_BONESFORMAT(char *bufp) { parseformat(sysopt.bonesformat, bufp); return TRUE; } -static boolean +staticfn boolean cnf_line_ACCESSIBILITY(char *bufp) { int n = atoi(bufp); @@ -3156,15 +3210,15 @@ cnf_line_ACCESSIBILITY(char *bufp) return TRUE; } -static boolean +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,1)"); + config_error_add("Illegal value in PORTABLE_DEVICE_PATHS" + " (not 0 or 1)"); n = 0; } sysopt.portable_device_paths = n; @@ -3175,7 +3229,9 @@ cnf_line_PORTABLE_DEVICE_PATHS(char *bufp) return TRUE; } -static boolean +#endif /* SYSCF */ + +staticfn boolean cnf_line_BOULDER(char *bufp) { (void) get_uchars(bufp, &go.ov_primary_syms[SYM_BOULDER + SYM_OFF_X], @@ -3183,23 +3239,24 @@ cnf_line_BOULDER(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_MENUCOLOR(char *bufp) { return add_menu_coloring(bufp); } -static boolean +staticfn boolean cnf_line_HILITE_STATUS(char *bufp) { #ifdef STATUS_HILITES return parse_status_hl1(bufp, TRUE); #else + nhUse(bufp); return TRUE; #endif } -static boolean +staticfn boolean cnf_line_WARNINGS(char *bufp) { uchar translate[MAXPCHARS]; @@ -3209,7 +3266,7 @@ cnf_line_WARNINGS(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_ROGUESYMBOLS(char *bufp) { if (parsesymbols(bufp, ROGUESET)) { @@ -3220,7 +3277,7 @@ cnf_line_ROGUESYMBOLS(char *bufp) return FALSE; } -static boolean +staticfn boolean cnf_line_SYMBOLS(char *bufp) { if (parsesymbols(bufp, PRIMARYSET)) { @@ -3231,7 +3288,7 @@ cnf_line_SYMBOLS(char *bufp) return FALSE; } -static boolean +staticfn boolean cnf_line_WIZKIT(char *bufp) { (void) strncpy(gw.wizkit, bufp, WIZKIT_MAX - 1); @@ -3239,7 +3296,7 @@ cnf_line_WIZKIT(char *bufp) } #ifdef USER_SOUNDS -static boolean +staticfn boolean cnf_line_SOUNDDIR(char *bufp) { if (sounddir) @@ -3248,7 +3305,7 @@ cnf_line_SOUNDDIR(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_SOUND(char *bufp) { add_sound_mapping(bufp); @@ -3256,7 +3313,7 @@ cnf_line_SOUND(char *bufp) } #endif /*USER_SOUNDS*/ -static boolean +staticfn boolean cnf_line_QT_TILEWIDTH(char *bufp) { #ifdef QT_GRAPHICS @@ -3270,7 +3327,7 @@ cnf_line_QT_TILEWIDTH(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_QT_TILEHEIGHT(char *bufp) { #ifdef QT_GRAPHICS @@ -3284,7 +3341,7 @@ cnf_line_QT_TILEHEIGHT(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_QT_FONTSIZE(char *bufp) { #ifdef QT_GRAPHICS @@ -3298,7 +3355,7 @@ cnf_line_QT_FONTSIZE(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_QT_COMPACT(char *bufp) { #ifdef QT_GRAPHICS @@ -3313,13 +3370,13 @@ cnf_line_QT_COMPACT(char *bufp) /* xNetHack or otherwise non vanilla config line functions */ -static boolean +staticfn boolean cnf_line_MONSTERCOLOR(char *bufp) { return add_monstercolor(bufp); } -static boolean +staticfn boolean cnf_line_DUMPHTMLFILE(char *bufp) { #ifdef DUMPHTML @@ -3332,7 +3389,7 @@ cnf_line_DUMPHTMLFILE(char *bufp) return TRUE; } -static boolean +staticfn boolean cnf_line_SERVERSEED(char *bufp) { int n = atoi(bufp); @@ -3381,6 +3438,7 @@ static const struct match_config_line_stmt { #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), @@ -3402,6 +3460,7 @@ static const struct match_config_line_stmt { 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), @@ -3529,10 +3588,10 @@ config_error_init(boolean from_file, const char *sourcename, boolean secure) tmp->next = config_error_data; config_error_data = tmp; - gp.program_state.config_error_ready = TRUE; + program_state.config_error_ready = TRUE; } -static boolean +staticfn boolean config_error_nextline(const char *line) { struct _config_error_frame *ced = config_error_data; @@ -3592,10 +3651,10 @@ config_erradd(const char *buf) /* 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 = eos((char *) buf) - 1; /* eos(buf)-1 is valid; cast away const */ + punct = c_eos((char *) buf) - 1; /* eos(buf)-1 is valid */ punct = strchr(".!?", *punct) ? "" : "."; - if (!gp.program_state.config_error_ready) { + 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: " : "", @@ -3657,7 +3716,7 @@ config_error_done(void) } config_error_data = tmp->next; free(tmp); - gp.program_state.config_error_ready = (config_error_data != 0); + program_state.config_error_ready = (config_error_data != 0); return n; } @@ -3696,7 +3755,7 @@ struct _cnf_parser_state { }; /* Initialize config parser data */ -static void +staticfn void cnf_parser_init(struct _cnf_parser_state *parser) { parser->rv = TRUE; /* assume successful parse */ @@ -3711,7 +3770,7 @@ cnf_parser_init(struct _cnf_parser_state *parser) } /* caller has finished with 'parser' (except for 'rv' so leave that intact) */ -static void +staticfn void cnf_parser_done(struct _cnf_parser_state *parser) { parser->ep = 0; /* points into parser->inbuf, so becoming stale */ @@ -3727,7 +3786,7 @@ cnf_parser_done(struct _cnf_parser_state *parser) * * Continued lines are merged together with one space in between. */ -static void +staticfn void parse_conf_buf(struct _cnf_parser_state *p, boolean (*proc)(char *arg)) { p->cont = FALSE; @@ -3815,7 +3874,8 @@ parse_conf_buf(struct _cnf_parser_state *p, boolean (*proc)(char *arg)) char *bufp = find_optparam(p->buf); if (!bufp) { - config_error_add("Format is CHOOSE=section1,section2,..."); + config_error_add("Format is CHOOSE=section1" + ",section2,..."); p->rv = FALSE; free(p->buf), p->buf = (char *) 0; return; @@ -3877,7 +3937,7 @@ parse_conf_str(const char *str, boolean (*proc)(char *arg)) * * Read from file fp, calling parse_conf_buf for each line. */ -static boolean +staticfn boolean parse_conf_file(FILE *fp, boolean (*proc)(char *arg)) { struct _cnf_parser_state parser; @@ -3896,7 +3956,8 @@ parse_conf_file(FILE *fp, boolean (*proc)(char *arg)) return parser.rv; } -static void +#ifdef SYSCF +staticfn void parseformat(int *arr, char *str) { const char *legal[] = { "historical", "lendian", "ascii" }; @@ -3928,12 +3989,13 @@ parseformat(int *arr, char *str) } } } +#endif /* SYSCF */ /* ---------- END CONFIG FILE HANDLING ----------- */ /* ---------- BEGIN WIZKIT FILE HANDLING ----------- */ -static FILE * +staticfn FILE * fopen_wizkit_file(void) { FILE *fp; @@ -3995,8 +4057,8 @@ fopen_wizkit_file(void) else if (errno != ENOENT) { /* e.g., problems when setuid NetHack can't search home * directory restricted to user */ - raw_printf("Couldn't open default gw.wizkit file %s (%d).", tmp_wizkit, - errno); + raw_printf("Couldn't open default gw.wizkit file %s (%d).", + tmp_wizkit, errno); wait_synch(); } #endif @@ -4005,10 +4067,10 @@ fopen_wizkit_file(void) } /* add to hero's inventory if there's room, otherwise put item on floor */ -static void +staticfn void wizkit_addinv(struct obj *obj) { - if (!obj || obj == &cg.zeroobj) + if (!obj || obj == &hands_obj) return; /* subset of starting inventory pre-ID */ @@ -4016,7 +4078,7 @@ wizkit_addinv(struct obj *obj) if (Role_if(PM_CLERIC)) obj->bknown = 1; /* ok to bypass set_bknown() */ /* same criteria as lift_object()'s check for available inventory slot */ - if (obj->oclass != COIN_CLASS && inv_cnt(FALSE) >= 52 + if (obj->oclass != COIN_CLASS && inv_cnt(FALSE) >= invlet_basic && !merge_choice(gi.invent, obj)) { /* inventory overflow; can't just place & stack object since hero isn't in position yet, so schedule for arrival later */ @@ -4040,7 +4102,7 @@ proc_wizkit_line(char *buf) otmp = readobjnam(buf, (struct obj *) 0); if (otmp) { - if (otmp != &cg.zeroobj) + if (otmp != &hands_obj) wizkit_addinv(otmp); } else { /* .60 limits output line width to 79 chars */ @@ -4058,14 +4120,14 @@ read_wizkit(void) if (!wizard || !(fp = fopen_wizkit_file())) return; - gp.program_state.wizkit_wishing = 1; + program_state.wizkit_wishing = 1; config_error_init(TRUE, "WIZKIT", FALSE); parse_conf_file(fp, proc_wizkit_line); (void) fclose(fp); config_error_done(); - gp.program_state.wizkit_wishing = 0; + program_state.wizkit_wishing = 0; return; } @@ -4077,8 +4139,7 @@ read_wizkit(void) extern const char *const known_handling[]; /* symbols.c */ extern const char *const known_restrictions[]; /* symbols.c */ -static -FILE * +staticfn FILE * fopen_sym_file(void) { FILE *fp; @@ -4128,7 +4189,7 @@ read_sym_file(int which_set) clear_symsetentry(which_set, TRUE); config_error_done(); - /* If name was defined, it was invalid. Then we're loading fallback */ + /* If name was defined, it was invalid. Then we're loading fallback */ if (gs.symset[which_set].name) { gs.symset[which_set].explicitly = FALSE; return 0; @@ -4159,9 +4220,6 @@ read_sym_file(int which_set) void check_recordfile(const char *dir UNUSED_if_not_OS2_CODEVIEW) { -#if defined(PRAGMA_UNUSED) && !defined(OS2_CODEVIEW) -#pragma unused(dir) -#endif const char *fq_record; int fd; @@ -4171,8 +4229,8 @@ check_recordfile(const char *dir UNUSED_if_not_OS2_CODEVIEW) if (fd >= 0) { #ifdef VMS /* must be stream-lf to use UPDATE_RECORD_IN_PLACE */ if (!file_is_stmlf(fd)) { - raw_printf( - "Warning: scoreboard file '%s' is not in stream_lf format", + raw_printf("Warning: scoreboard file '%s'" + " is not in stream_lf format", fq_record); wait_synch(); } @@ -4264,6 +4322,8 @@ check_recordfile(const char *dir UNUSED_if_not_OS2_CODEVIEW) #endif /* MICRO || WIN32*/ } +#undef UNUSED_if_not_OS2_CODEVIEW + /* ---------- END SCOREBOARD CREATION ----------- */ /* ---------- BEGIN PANIC/IMPOSSIBLE/TESTING LOG ----------- */ @@ -4277,13 +4337,13 @@ paniclog( #ifdef PANICLOG FILE *lfile; - if (!gp.program_state.in_paniclog) { - gp.program_state.in_paniclog = 1; + if (!program_state.in_paniclog) { + program_state.in_paniclog = 1; lfile = fopen_datafile(PANICLOG, "a", TROUBLEPREFIX); if (lfile) { #ifdef PANICLOG_FMT2 (void) fprintf(lfile, "%ld %s: %s %s\n", - ubirthday, (gp.plname[0] ? gp.plname : "(none)"), + ubirthday, (svp.plname[0] ? svp.plname : "(none)"), type, reason); #else char buf[BUFSZ]; @@ -4298,7 +4358,7 @@ paniclog( #endif /* !PANICLOG_FMT2 */ (void) fclose(lfile); } - gp.program_state.in_paniclog = 0; + program_state.in_paniclog = 0; } #endif /* PANICLOG */ return; @@ -4335,12 +4395,12 @@ recover_savefile(void) { NHFILE *gnhfp, *lnhfp, *snhfp; int lev, savelev, hpid, pltmpsiz, filecmc; - xint16 levc; + 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 tmpplbuf[PL_NSIZ_PLUS]; const char *savewrite_failure = (const char *) 0; for (lev = 0; lev < 256; lev++) @@ -4362,16 +4422,18 @@ recover_savefile(void) } if (read(gnhfp->fd, (genericptr_t) &hpid, sizeof hpid) != sizeof hpid) { raw_printf("\n%s\n%s\n", - "Checkpoint data incompletely written or subsequently clobbered.", + "Checkpoint data incompletely written" + " or subsequently clobbered.", "Recovery impossible."); close_nhfile(gnhfp); return FALSE; } if (read(gnhfp->fd, (genericptr_t) &savelev, sizeof(savelev)) != sizeof(savelev)) { - raw_printf( - "\nCheckpointing was not in effect for %s -- recovery impossible.\n", - gl.lock); + raw_printf("\n%s %s %s\n", + "Checkpointing was not in effect for", + gl.lock, + "-- recovery impossible."); close_nhfile(gnhfp); return FALSE; } @@ -4385,8 +4447,9 @@ recover_savefile(void) != 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) - || (read(gnhfp->fd, (genericptr_t) &tmpplbuf, pltmpsiz) != pltmpsiz)) { + != 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); close_nhfile(gnhfp); return FALSE; @@ -4405,9 +4468,9 @@ recover_savefile(void) /* * Set a flag for the savefile routines to know the * circumstances and act accordingly: - * gp.program_state.in_self_recover + * program_state.in_self_recover */ - gp.program_state.in_self_recover = TRUE; + program_state.in_self_recover = TRUE; set_savefile_name(TRUE); snhfp = create_savefile(); if (!snhfp) { @@ -4474,14 +4537,14 @@ recover_savefile(void) processed[0] = 1; for (lev = 1; lev < 256; lev++) { - /* level numbers are kept in xint16s in save.c, so the + /* level numbers are kept in xint8's in save.c, so the * maximum level number (for the endlevel) must be < 256 */ if (lev != savelev) { lnhfp = open_levelfile(lev, (char *) 0); if (lnhfp) { /* any or all of these may not exist */ - levc = (xint16) lev; + levc = (xint8) lev; (void) write(snhfp->fd, (genericptr_t) &levc, sizeof(levc)); if (!copy_bytes(lnhfp->fd, snhfp->fd)) { close_nhfile(lnhfp); @@ -4515,31 +4578,16 @@ recover_savefile(void) close_nhfile(gnhfp); close_nhfile(snhfp); close_nhfile(lnhfp); - gp.program_state.in_self_recover = FALSE; + program_state.in_self_recover = FALSE; delete_savefile(); return FALSE; } - /* we don't clear gp.program_state.in_self_recover here, we + /* we don't clear program_state.in_self_recover here, we leave it as a flag to reload the structlevel savefile in the caller. The caller should then clear it. */ return TRUE; } -boolean -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) - return FALSE; - } while (nfrom == BUFSIZ); - return TRUE; -} - /* ---------- END INTERNAL RECOVER ----------- */ #endif /*SELF_RECOVER*/ @@ -4579,6 +4627,8 @@ assure_syscf_file(void) close(fd); return; } + if (gd.deferred_showpaths) + do_deferred_showpaths(1); /* does not return */ raw_printf("Unable to open SYSCF_FILE.\n"); exit(EXIT_FAILURE); } @@ -4586,6 +4636,33 @@ assure_syscf_file(void) #endif /* SYSCF_FILE */ #endif /* SYSCF */ +ATTRNORETURN void +do_deferred_showpaths(int code) +{ + gd.deferred_showpaths = FALSE; + reveal_paths(code); + + /* cleanup before heading to an exit */ + freedynamicdata(); + dlb_cleanup(); + l_nhcore_done(); + +#ifdef UNIX + after_opt_showpaths(gd.deferred_showpaths_dir); +#else +#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 +} + #ifdef DEBUG /* used by debugpline() to decide whether to issue a message * from a particular source file; caller passes __FILE__ and we check @@ -4599,23 +4676,14 @@ debugcore(const char *filename, boolean wildcards) { const char *debugfiles, *p; + /* debugpline() messages might disclose information that the player + doesn't normally get to see, so only display them in wizard mode */ + if (!wizard) + return FALSE; + if (!filename || !*filename) return FALSE; /* sanity precaution */ - if (sysopt.env_dbgfl == 0) { - /* check once for DEBUGFILES in the environment; - if found, it supersedes the sysconf value - [note: getenv() rather than nh_getenv() since a long value - is valid and doesn't pose any sort of overflow risk here] */ - if ((p = getenv("DEBUGFILES")) != 0) { - if (sysopt.debugfiles) - free((genericptr_t) sysopt.debugfiles); - sysopt.debugfiles = dupstr(p); - sysopt.env_dbgfl = 1; - } else - sysopt.env_dbgfl = -1; - } - debugfiles = sysopt.debugfiles; /* usual case: sysopt.debugfiles will be empty */ if (!debugfiles || !*debugfiles) @@ -4654,10 +4722,16 @@ debugcore(const char *filename, boolean wildcards) #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; @@ -4691,7 +4765,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 @@ -4703,6 +4778,11 @@ reveal_paths(void) filep = 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 */ @@ -4775,17 +4855,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) { @@ -4793,22 +4878,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(); - - /* 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); + 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); + } } } +#endif #endif /* personal configuration file */ @@ -4864,6 +4954,15 @@ reveal_paths(void) #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 ----------- */ @@ -4875,13 +4974,13 @@ reveal_paths(void) #define TITLESCOPE 2 #define PASSAGESCOPE 3 -#define MAXPASSAGES SIZE(gc.context.novel.pasg) /* 20 */ +#define MAXPASSAGES SIZE(svc.context.novel.pasg) /* 20 */ -static int choose_passage(int, unsigned); +staticfn int choose_passage(int, unsigned); /* choose a random passage that hasn't been chosen yet; once all have been chosen, reset the tracking to make all passages available again */ -static int +staticfn int choose_passage(int passagecnt, /* total of available passages */ unsigned oid) /* book.o_id, used to determine whether re-reading same book */ @@ -4893,32 +4992,33 @@ choose_passage(int passagecnt, /* total of available passages */ /* if a different book or we've used up all the passages already, reset in order to have all 'passagecnt' passages available */ - if (oid != gc.context.novel.id || gc.context.novel.count == 0) { + if (oid != svc.context.novel.id || svc.context.novel.count == 0) { int i, range = passagecnt, limit = MAXPASSAGES; - gc.context.novel.id = oid; + svc.context.novel.id = oid; if (range <= limit) { /* collect all of the N indices */ - gc.context.novel.count = passagecnt; + svc.context.novel.count = passagecnt; for (idx = 0; idx < MAXPASSAGES; idx++) - gc.context.novel.pasg[idx] = (xint16) ((idx < passagecnt) + svc.context.novel.pasg[idx] = (xint16) ((idx < passagecnt) ? idx + 1 : 0); } else { /* collect MAXPASSAGES of the N indices */ - gc.context.novel.count = MAXPASSAGES; + svc.context.novel.count = MAXPASSAGES; for (idx = i = 0; i < passagecnt; ++i, --range) if (range > 0 && rn2(range) < limit) { - gc.context.novel.pasg[idx++] = (xint16) (i + 1); + svc.context.novel.pasg[idx++] = (xint16) (i + 1); --limit; } } } - idx = rn2(gc.context.novel.count); - res = (int) gc.context.novel.pasg[idx]; + idx = rn2(svc.context.novel.count); + res = (int) svc.context.novel.pasg[idx]; /* move the last slot's passage index into the slot just used and reduce the number of passages available */ - gc.context.novel.pasg[idx] = gc.context.novel.pasg[--gc.context.novel.count]; + svc.context.novel.pasg[idx] + = svc.context.novel.pasg[--svc.context.novel.count]; return res; } @@ -5147,10 +5247,10 @@ livelog_add(long ll_type, const char *str) "gender=%s" LLOG_SEP "align=%s" LLOG_SEP "turns=%ld" LLOG_SEP "starttime=%ld" LLOG_SEP "curtime=%ld" LLOG_SEP "message=%s" LLOG_EOL, - (ll_type & sysopt.livelog), gp.plname, + (ll_type & sysopt.livelog), svp.plname, gu.urole.filecode, gu.urace.filecode, genders[gindx].filecode, aligns[aindx].filecode, - gm.moves, timet_to_seconds(ubirthday), + svm.moves, timet_to_seconds(ubirthday), timet_to_seconds(now), str); (void) fclose(livelogfile); unlock_file(LIVELOGFILE); diff --git a/src/fountain.c b/src/fountain.c index ef9fae0490..c6ba226692 100644 --- a/src/fountain.c +++ b/src/fountain.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 fountain.c $NHDT-Date: 1646870844 2022/03/10 00:07:24 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.78 $ */ +/* NetHack 3.7 fountain.c $NHDT-Date: 1699582923 2023/11/10 02:22:03 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.100 $ */ /* Copyright Scott R. Turner, srt@ucla, 10/27/86 */ /* NetHack may be freely redistributed. See license for details. */ @@ -6,12 +6,13 @@ #include "hack.h" -static void dowatersnakes(void); -static void dowaterdemon(void); -static void dowaternymph(void); -static void gush(coordxy, coordxy, genericptr_t); -static void dofindgem(void); -static boolean watchman_warn_fountain(struct monst *); +staticfn void dowatersnakes(void); +staticfn void dowaterdemon(void); +staticfn void dowaternymph(void); +staticfn void gush(coordxy, coordxy, genericptr_t) NONNULLARG3; +staticfn void dofindgem(void); +staticfn struct obj * find_sink_ring(coordxy, coordxy); +staticfn boolean watchman_warn_fountain(struct monst *) NONNULLARG1; DISABLE_WARNING_FORMAT_NONLITERAL @@ -34,13 +35,13 @@ floating_above(const char *what) RESTORE_WARNING_FORMAT_NONLITERAL /* Fountain of snakes! */ -static void +staticfn void dowatersnakes(void) { - register int num = rn1(5, 2); + int num = rn1(5, 2); struct monst *mtmp; - if (!(gm.mvitals[PM_WATER_MOCCASIN].mvflags & G_GONE)) { + if (!(svm.mvitals[PM_WATER_MOCCASIN].mvflags & G_GONE)) { if (!Blind) { pline("An endless stream of %s pours forth!", Hallucination ? makeplural(rndmonnam(NULL)) : "snakes"); @@ -60,12 +61,12 @@ dowatersnakes(void) } /* Water demon */ -static void +staticfn void dowaterdemon(void) { struct monst *mtmp; - if (!(gm.mvitals[PM_WATER_DEMON].mvflags & G_GONE)) { + if (!(svm.mvitals[PM_WATER_DEMON].mvflags & G_GONE)) { if ((mtmp = makemon(&mons[PM_WATER_DEMON], u.ux, u.uy, MM_NOMSG)) != 0) { if (!Blind) @@ -90,12 +91,12 @@ dowaterdemon(void) } /* Water Nymph */ -static void +staticfn void dowaternymph(void) { - register struct monst *mtmp; + struct monst *mtmp; - if (!(gm.mvitals[PM_WATER_NYMPH].mvflags & G_GONE) + if (!(svm.mvitals[PM_WATER_NYMPH].mvflags & G_GONE) && (mtmp = makemon(&mons[PM_WATER_NYMPH], u.ux, u.uy, MM_NOMSG)) != 0) { if (!Blind) @@ -130,11 +131,11 @@ dogushforth(int drinking) } } -static void +staticfn void gush(coordxy x, coordxy y, genericptr_t poolcnt) { - register struct monst *mtmp; - register struct trap *ttmp; + struct monst *mtmp; + struct trap *ttmp; if (((x + y) % 2) || u_at(x, y) || (rn2(1 + distmin(u.ux, u.uy, x, y))) || (levl[x][y].typ != ROOM) @@ -148,10 +149,11 @@ gush(coordxy x, coordxy y, genericptr_t poolcnt) pline("Water gushes forth from the overflowing fountain!"); /* Put a pool at x, y */ - levl[x][y].typ = POOL, levl[x][y].flags = 0; + set_levltyp(x, y, POOL); + levl[x][y].flags = 0; /* No kelp! */ del_engr_at(x, y); - water_damage_chain(gl.level.objects[x][y], TRUE, 0, TRUE); + water_damage_chain(svl.level.objects[x][y], TRUE, 0, TRUE); if ((mtmp = m_at(x, y)) != 0) (void) minliquid(mtmp); @@ -160,7 +162,7 @@ gush(coordxy x, coordxy y, genericptr_t poolcnt) } /* Find a gem in the sparkling waters. */ -static void +staticfn void dofindgem(void) { if (!Blind) @@ -174,7 +176,7 @@ dofindgem(void) exercise(A_WIS, TRUE); /* a discovery! */ } -static boolean +staticfn boolean watchman_warn_fountain(struct monst *mtmp) { if (is_watch(mtmp->data) && couldsee(mtmp->mx, mtmp->my) @@ -226,9 +228,9 @@ dryup(coordxy x, coordxy y, boolean isyou) pline_The("fountain dries up!"); } /* replace the fountain with ordinary floor */ - levl[x][y].typ = ROOM, levl[x][y].flags = 0; + set_levltyp(x, y, ROOM); /* updates level.flags.nfountains */ + levl[x][y].flags = 0; levl[x][y].blessedftn = 0; - gl.level.flags.nfountains--; /* The location is seen if the hero/monster is invisible or felt if the hero is blind. */ newsym(x, y); @@ -237,12 +239,13 @@ dryup(coordxy x, coordxy y, boolean isyou) } } +/* quaff from a fountain when standing on its location */ void drinkfountain(void) { /* What happens when you drink from a fountain? */ - register boolean mgkftn = (levl[u.ux][u.uy].blessedftn == 1); - register int fate = rnd(30); + boolean mgkftn = (levl[u.ux][u.uy].blessedftn == 1); + int fate = rnd(30); if (Levitation) { floating_above("fountain"); @@ -259,7 +262,7 @@ drinkfountain(void) if (++u.uenmax > u.uenpeak) u.uenpeak = u.uenmax; u.uen = u.uenmax; - gc.context.botl = 1; + disp.botl = 1; } else { int i, ii, littleluck = (u.uluck < 4); @@ -268,7 +271,6 @@ drinkfountain(void) for (ii = 0; ii < A_MAX; ii++) if (ABASE(ii) < AMAX(ii)) { ABASE(ii) = AMAX(ii); - gc.context.botl = 1; } /* gain ability, blessed if "natural" luck is high */ i = rn2(A_MAX); /* start at a random attribute */ @@ -280,6 +282,7 @@ drinkfountain(void) if (++i >= A_MAX) i = 0; } + disp.botl = TRUE; } display_nhwindow(WIN_MESSAGE, FALSE); pline("A wisp of vapor escapes the fountain..."); @@ -327,18 +330,20 @@ drinkfountain(void) dowaterdemon(); break; case 24: { /* Maybe curse some items */ - register 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; @@ -369,13 +374,14 @@ drinkfountain(void) dofindgem(); break; } + FALLTHROUGH; /*FALLTHRU*/ case 28: /* Water Nymph */ dowaternymph(); break; case 29: /* Scare */ { - register struct monst *mtmp; + struct monst *mtmp; pline("This %s gives you bad breath!", hliquid("water")); @@ -398,17 +404,20 @@ drinkfountain(void) dryup(u.ux, u.uy, TRUE); } +/* dip an object into a fountain when standing on its location */ void -dipfountain(register struct obj *obj) +dipfountain(struct obj *obj) { int er = ER_NOTHING; + boolean is_hands = (obj == &hands_obj); if (Levitation) { floating_above("fountain"); return; } - if (obj->otyp == LONG_SWORD && u.ulevel >= 5 && !rn2(6) + if (obj->otyp == LONG_SWORD && u.ulevel >= 5 + && !rn2(Role_if(PM_KNIGHT) ? 6 : 30) /* once upon a time it was possible to poly N daggers into N swords */ && obj->quan == 1L && !obj->oartifact && !exist_artifact(LONG_SWORD, artiname(ART_EXCALIBUR))) { @@ -416,7 +425,8 @@ dipfountain(register struct obj *obj) if (u.ualign.type != A_LAWFUL) { /* Ha! Trying to cheat her. */ - pline("A freezing mist rises from the %s and envelopes the sword.", + pline("A freezing mist rises from the %s" + " and envelopes the sword.", hliquid("water")); pline_The("fountain disappears!"); curse(obj); @@ -450,23 +460,25 @@ dipfountain(register struct obj *obj) artiname(ART_EXCALIBUR), lady); } update_inventory(); - levl[u.ux][u.uy].typ = ROOM, levl[u.ux][u.uy].flags = 0; + set_levltyp(u.ux, u.uy, ROOM); /* updates level.flags.nfountains */ + levl[u.ux][u.uy].flags = 0; newsym(u.ux, u.uy); - gl.level.flags.nfountains--; if (in_town(u.ux, u.uy)) (void) angry_guards(FALSE); return; + } else if (is_hands || obj == uarmg) { + er = wash_hands(); } else { er = water_damage(obj, NULL, TRUE); + } - if (er == ER_DESTROYED || (er != ER_NOTHING && !rn2(2))) { - return; /* no further effect */ - } + if (er == ER_DESTROYED || (er != ER_NOTHING && !rn2(2))) { + return; /* no further effect */ } switch (rnd(30)) { case 16: /* Curse the item */ - if (obj->oclass != COIN_CLASS && !obj->cursed) { + if (!is_hands && obj->oclass != COIN_CLASS && !obj->cursed) { curse(obj); } break; @@ -474,7 +486,7 @@ dipfountain(register struct obj *obj) case 18: case 19: case 20: /* Uncurse the item */ - if (obj->cursed) { + if (!is_hands && obj->cursed) { if (!Blind) pline_The("%s glows for a moment.", hliquid("water")); uncurse(obj); @@ -496,6 +508,7 @@ dipfountain(register struct obj *obj) dofindgem(); break; } + FALLTHROUGH; /*FALLTHRU*/ case 25: /* Water gushes forth */ dogushforth(FALSE); @@ -516,12 +529,14 @@ dipfountain(register 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 = @@ -532,6 +547,7 @@ dipfountain(register 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); @@ -557,26 +573,51 @@ dipfountain(register struct obj *obj) break; default: if (er == ER_NOTHING) - pline("Nothing seems to happen."); + pline1(nothing_seems_to_happen); break; } update_inventory(); dryup(u.ux, u.uy, TRUE); } +/* dipping '-' in fountain, pool, or sink */ +int +wash_hands(void) +{ + const char *hands = makeplural(body_part(HAND)); + int res = ER_NOTHING; + boolean was_glib = !!Glib; + + You("wash your %s%s in the %s.", uarmg ? "gloved " : "", hands, + hliquid("water")); + if (Glib) { + make_glib(0); + Your("%s are no longer slippery.", fingers_or_gloves(TRUE)); + } + if (uarmg) + res = water_damage(uarmg, (const char *) 0, TRUE); + /* not what ER_GREASED is for, but the checks in dipfountain just + compare the result to ER_DESTROYED and ER_NOTHING, so it works */ + if (was_glib && res == ER_NOTHING) + res = ER_GREASED; + return res; +} + +/* convert a sink into a fountain */ void breaksink(coordxy x, coordxy y) { if (cansee(x, y) || u_at(x, y)) pline_The("pipes break! Water spurts out!"); - gl.level.flags.nsinks--; - levl[x][y].typ = FOUNTAIN, levl[x][y].looted = 0; + /* updates level.flags.nsinks and level.flags.nfountains */ + set_levltyp(x, y, FOUNTAIN); + levl[x][y].looted = 0; levl[x][y].blessedftn = 0; SET_FOUNTAIN_LOOTED(x, y); - gl.level.flags.nfountains++; newsym(x, y); } +/* quaff from a sink while standing on its location */ void drinksink(void) { @@ -599,12 +640,14 @@ drinksink(void) if (Fire_resistance) { pline("It seems quite tasty."); monstseesu(M_SEEN_FIRE); - } else + } else { losehp(rnd(6), "sipping boiling water", KILLED_BY); + monstunseesu(M_SEEN_FIRE); + } /* boiling water burns considered fire damage */ break; case 3: - if (gm.mvitals[PM_SEWER_RAT].mvflags & G_GONE) + if (svm.mvitals[PM_SEWER_RAT].mvflags & G_GONE) pline_The("sink seems quite dirty."); else { mtmp = makemon(&mons[PM_SEWER_RAT], u.ux, u.uy, MM_NOMSG); @@ -615,13 +658,13 @@ drinksink(void) } break; case 4: - do { + for (;;) { otmp = mkobj(POTION_CLASS, FALSE); - if (otmp->otyp == POT_WATER) { - obfree(otmp, (struct obj *) 0); - otmp = (struct obj *) 0; - } - } while (!otmp); + if (otmp->otyp != POT_WATER) + break; + /* reject water and try again */ + obfree(otmp, (struct obj *) 0); + } otmp->cursed = otmp->blessed = 0; pline("Some %s liquid flows from the faucet.", Blind ? "odd" : hcolor(OBJ_DESCR(objects[otmp->otyp]))); @@ -645,7 +688,7 @@ drinksink(void) break; case 7: pline_The("%s moves as though of its own will!", hliquid("water")); - if ((gm.mvitals[PM_WATER_ELEMENTAL].mvflags & G_GONE) + if ((svm.mvitals[PM_WATER_ELEMENTAL].mvflags & G_GONE) || !makemon(&mons[PM_WATER_ELEMENTAL], u.ux, u.uy, MM_NOMSG)) pline("But it quiets down."); break; @@ -684,6 +727,7 @@ drinksink(void) pline("From the murky drain, a hand reaches up... --oops--"); break; } + FALLTHROUGH; /*FALLTHRU*/ default: You("take a sip of %s %s.", @@ -692,19 +736,29 @@ drinksink(void) } } +/* If there is a ring buried under a sink located at (sink_x, sink_y), return a + * pointer to it, but do nothing else. + * If multiple rings, return the first of them. */ +staticfn struct obj * +find_sink_ring(coordxy sink_x, coordxy sink_y) +{ + struct obj *otmp; + for (otmp = svl.level.buriedobjlist; otmp; otmp = otmp->nobj) { + if (otmp->ox == sink_x && otmp->oy == sink_y + && otmp->oclass == RING_CLASS) { + return otmp; + } + } + return (struct obj *) 0; +} + /* If there is a ring buried under a sink, pop it out and place it on the sink. * Return the ring that appeared or NULL if there was no ring there. * The caller should handle messages. */ struct obj * ring_from_sink(coordxy sink_x, coordxy sink_y) { - struct obj* otmp; - for (otmp = gl.level.buriedobjlist; otmp; otmp = otmp->nobj) { - if (otmp->ox == sink_x && otmp->oy == sink_y - && otmp->oclass == RING_CLASS) { - break; - } - } + struct obj* otmp = find_sink_ring(sink_x, sink_y); if (otmp) { obj_extract_self(otmp); place_object(otmp, sink_x, sink_y); @@ -713,4 +767,116 @@ ring_from_sink(coordxy sink_x, coordxy sink_y) return otmp; } +/* for #dip(potion.c) when standing on a sink */ +void +dipsink(struct obj *obj) +{ + boolean try_call = FALSE, + not_looted_yet = !!find_sink_ring(u.ux, u.uy), + is_hands = (obj == &hands_obj || (uarmg && obj == uarmg)); + + if (!rn2(not_looted_yet ? 25 : 15)) { + /* can't rely on using sink for unlimited scroll blanking; however, + since sink will be converted into a fountain, hero can dip again */ + breaksink(u.ux, u.uy); /* "The pipes break! Water spurts out!" */ + if (Glib && is_hands) + Your("%s are still slippery.", fingers_or_gloves(TRUE)); + return; + } else if (is_hands) { + (void) wash_hands(); + return; + } else if (obj->oclass != POTION_CLASS) { + You("hold %s under the tap.", the(xname(obj))); + if (water_damage(obj, (const char *) 0, TRUE) == ER_NOTHING) + pline1(nothing_seems_to_happen); + return; + } + + /* at this point the object must be a potion */ + You("pour %s%s down the drain.", (obj->quan > 1L ? "one of " : ""), + the(xname(obj))); + switch (obj->otyp) { + case POT_POLYMORPH: + polymorph_sink(); + makeknown(POT_POLYMORPH); + break; + case POT_OIL: + if (!Blind) { + pline("It leaves an oily film on the basin."); + makeknown(POT_OIL); + } else { + pline1(nothing_seems_to_happen); + } + break; + case POT_ACID: + /* acts like a drain cleaner product */ + try_call = TRUE; + if (!Blind) { + pline_The("drain seems less clogged."); + } else if (!Deaf) { + You_hear("a sucking sound."); + } else { + pline1(nothing_seems_to_happen); + try_call = FALSE; + } + break; + case POT_LEVITATION: + sink_backs_up(u.ux, u.uy); + try_call = TRUE; + break; + case POT_OBJECT_DETECTION: + if (not_looted_yet) { + You("sense a ring lost down the drain."); + makeknown(POT_OBJECT_DETECTION); + break; + } + FALLTHROUGH; + /* FALLTHRU */ + case POT_GAIN_LEVEL: + case POT_GAIN_ENERGY: + case POT_MONSTER_DETECTION: + case POT_FRUIT_JUICE: + case POT_WATER: + /* potions with no potionbreathe() effects, plus water. if effects + are added to potionbreathe these should go to that instead (except + for water). */ + pline1(nothing_seems_to_happen); + break; + default: + /* hero can feel the vapor on her skin, so no need to check Blind or + breathless for this message */ + pline("A wisp of vapor rises up..."); + /* NB: potionbreathe calls trycall or makeknown as appropriate */ + if (!breathless(gy.youmonst.data) || haseyes(gy.youmonst.data)) + potionbreathe(obj); + break; + } + if (try_call && obj->dknown) + trycall(obj); + useup(obj); +} + +/* find a ring in a sink */ +void +sink_backs_up(coordxy x, coordxy y) +{ + char buf[BUFSZ]; + + if (!Blind) + Strcpy(buf, "Muddy waste pops up from the drain"); + else if (!Deaf) + Strcpy(buf, "You hear a sloshing sound"); /* Deaf-aware */ + else + Sprintf(buf, "Something splashes you in the %s", body_part(FACE)); + pline("%s%s.", !Deaf ? "Flupp! " : "", buf); + + struct obj * otmp = ring_from_sink(x, y); + if (otmp) { + if (!Blind) + You_see("a ring shining in its midst."); + exercise(A_DEX, TRUE); + exercise(A_WIS, TRUE); /* a discovery! */ + } +} + /*fountain.c*/ diff --git a/src/getpos.c b/src/getpos.c new file mode 100644 index 0000000000..36e9d95ea2 --- /dev/null +++ b/src/getpos.c @@ -0,0 +1,1146 @@ +/* NetHack 3.7 getpos.c $NHDT-Date: 1723875487 2024/08/17 06:18:07 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.3 $ */ +/*-Copyright (c) Pasi Kallinen, 2023. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +extern const char what_is_a_location[]; /* from pager.c */ + +staticfn void getpos_toggle_hilite_state(void); +staticfn void getpos_getvalids_selection(struct selectionvar *, + boolean (*)(coordxy, coordxy)); +staticfn void getpos_help_keyxhelp(winid, const char *, const char *, int); +staticfn void getpos_help(boolean, const char *); +staticfn int QSORTCALLBACK cmp_coord_distu(const void *, const void *); +staticfn int gloc_filter_classify_glyph(int); +staticfn int gloc_filter_floodfill_matcharea(coordxy, coordxy); +staticfn void gloc_filter_floodfill(coordxy, coordxy); +staticfn void gloc_filter_init(void); +staticfn void gloc_filter_done(void); +staticfn void gather_locs(coord **, int *, int); +staticfn void truncate_to_map(coordxy *, coordxy *, schar, schar); +staticfn void getpos_refresh(void); +/* Callback function for getpos() to highlight desired map locations. + * Parameter TRUE: initialize and highlight, FALSE: done (remove highlights). + */ +staticfn void (*getpos_hilitefunc)(boolean) = (void (*)(boolean)) 0; +staticfn boolean (*getpos_getvalid)(coordxy, coordxy) + = (boolean (*)(coordxy, coordxy)) 0; +enum getposHiliteState { + HiliteNormalMap = 0, + HiliteGoodposSymbol = 1, + HiliteBackground = 2, +}; + +static enum getposHiliteState + getpos_hilite_state = HiliteNormalMap, + defaultHiliteState = HiliteNormalMap; + +void +getpos_sethilite( + void (*gp_hilitef)(boolean), + boolean (*gp_getvalidf)(coordxy, coordxy)) +{ + boolean (*old_getvalid)(coordxy, coordxy) = getpos_getvalid; + uint32 old_map_frame_color = gw.wsettings.map_frame_color; + struct selectionvar *sel = selection_new(); + + defaultHiliteState = iflags.bgcolors ? HiliteBackground : HiliteNormalMap; + if (gp_getvalidf != old_getvalid) + getpos_hilite_state = defaultHiliteState; + + getpos_getvalids_selection(sel, getpos_getvalid); + getpos_hilitefunc = gp_hilitef; + getpos_getvalid = gp_getvalidf; + getpos_getvalids_selection(sel, getpos_getvalid); + gw.wsettings.map_frame_color = (getpos_hilite_state == HiliteBackground) + ? HI_ZAP : NO_COLOR; + + if (getpos_getvalid != old_getvalid + || gw.wsettings.map_frame_color != old_map_frame_color) + selection_force_newsyms(sel); + selection_free(sel, TRUE); +} + +/* cycle 'getpos_hilite_state' to its next state; + when 'bgcolors' is Off, it will alternate between not showing valid + positions and showing them via temporary S_goodpos symbol; + when 'bgcolors' is On, there are three states and showing them via + setting background color becomes the default */ +staticfn void +getpos_toggle_hilite_state(void) +{ + /* getpos_hilitefunc isn't Null */ + if (getpos_hilite_state == HiliteGoodposSymbol) { + /* currently on, finish */ + (*getpos_hilitefunc)(FALSE); /* tmp_at(DISP_END) */ + } + + getpos_hilite_state = (getpos_hilite_state + 1) + % (iflags.bgcolors ? 3 : 2); + /* resetting the callback functions to their current values will draw + valid-spots with background color if that is the new state and turn + off that color if it was the previous state */ + getpos_sethilite(getpos_hilitefunc, getpos_getvalid); + + if (getpos_hilite_state == HiliteGoodposSymbol) { + /* now on, begin */ + (*getpos_hilitefunc)(TRUE); + } +} + +boolean +mapxy_valid(coordxy x, coordxy y) +{ + if (getpos_getvalid) + return (*getpos_getvalid)(x, y); + return FALSE; +} + +staticfn void +getpos_getvalids_selection( + struct selectionvar *sel, + boolean (*validf)(coordxy, coordxy)) +{ + coordxy x, y; + + if (!sel || !validf) + return; + + for (x = 1; x < sel->wid; x++) + for (y = 0; y < sel->hei; y++) + if ((*validf)(x, y)) + selection_setpoint(x, y, sel, 1); +} + +static const char *const gloc_descr[NUM_GLOCS][4] = { + { "any monsters", "monster", "next/previous monster", "monsters" }, + { "any items", "item", "next/previous object", "objects" }, + { "any doors", "door", "next/previous door or doorway", + "doors or doorways" }, + { "any unexplored areas", "unexplored area", "unexplored location", + "locations next to unexplored locations" }, + { "anything interesting", "interesting thing", "anything interesting", + "anything interesting" }, + { "any valid locations", "valid location", "valid location", + "valid locations" } +}; + +static const char *const gloc_filtertxt[NUM_GFILTER] = { + "", + " in view", + " in this area" +}; + +staticfn void +getpos_help_keyxhelp( + winid tmpwin, + const char *k1, const char *k2, + int gloc) +{ + char sbuf[BUFSZ], fbuf[QBUFSZ]; + const char *move_cursor_to = "move the cursor to ", + *filtertxt = gloc_filtertxt[iflags.getloc_filter]; + + if (gloc == GLOC_EXPLORE) { + /* default of "move to unexplored location" is inaccurate + because the position will be one spot short of that */ + move_cursor_to = "move the cursor next to an "; + if (iflags.getloc_usemenu) + /* default is too wide for basic 80-column tty so shorten it + to avoid wrapping */ + filtertxt = strsubst(strcpy(fbuf, filtertxt), + "this area", "area"); + } + Sprintf(sbuf, "Use '%s'/'%s' to %s%s%s.", + k1, k2, + iflags.getloc_usemenu ? "get a menu of " : move_cursor_to, + gloc_descr[gloc][2 + iflags.getloc_usemenu], filtertxt); + putstr(tmpwin, 0, sbuf); +} + +DISABLE_WARNING_FORMAT_NONLITERAL + +/* the response for '?' help request in getpos() */ +staticfn void +getpos_help(boolean force, const char *goal) +{ + static const char *const fastmovemode[2] = { "8 units at a time", + "skipping same glyphs" }; + char sbuf[BUFSZ]; + boolean doing_what_is; + winid tmpwin = create_nhwindow(NHW_MENU); + + Sprintf(sbuf, + "Use '%s', '%s', '%s', '%s' to move the cursor to %s.", /* hjkl */ + visctrl(cmd_from_func(do_move_west)), + visctrl(cmd_from_func(do_move_south)), + visctrl(cmd_from_func(do_move_north)), + visctrl(cmd_from_func(do_move_east)), goal); + putstr(tmpwin, 0, sbuf); + Sprintf(sbuf, + "Use '%s', '%s', '%s', '%s' to fast-move the cursor, %s.", + visctrl(cmd_from_func(do_run_west)), + visctrl(cmd_from_func(do_run_south)), + visctrl(cmd_from_func(do_run_north)), + visctrl(cmd_from_func(do_run_east)), + fastmovemode[iflags.getloc_moveskip]); + putstr(tmpwin, 0, sbuf); + Sprintf(sbuf, "(or prefix normal move with '%s' or '%s' to fast-move)", + visctrl(cmd_from_func(do_run)), + visctrl(cmd_from_func(do_rush))); + putstr(tmpwin, 0, sbuf); + putstr(tmpwin, 0, "Or enter a background symbol (ex. '<')."); + Sprintf(sbuf, "Use '%s' to move the cursor on yourself.", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_SELF])); + putstr(tmpwin, 0, sbuf); + if (!iflags.terrainmode || (iflags.terrainmode & TER_MON) != 0) { + getpos_help_keyxhelp(tmpwin, + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_MON_NEXT]), + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_MON_PREV]), + GLOC_MONS); + } + if (goal && !strcmp(goal, "a monster")) + goto skip_non_mons; + if (!iflags.terrainmode || (iflags.terrainmode & TER_OBJ) != 0) { + getpos_help_keyxhelp(tmpwin, + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_OBJ_NEXT]), + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_OBJ_PREV]), + GLOC_OBJS); + } + if (!iflags.terrainmode || (iflags.terrainmode & TER_MAP) != 0) { + /* these are primarily useful when choosing a travel + destination for the '_' command */ + getpos_help_keyxhelp(tmpwin, + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_DOOR_NEXT]), + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_DOOR_PREV]), + GLOC_DOOR); + getpos_help_keyxhelp(tmpwin, + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_UNEX_NEXT]), + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_UNEX_PREV]), + GLOC_EXPLORE); + getpos_help_keyxhelp(tmpwin, + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_INTERESTING_NEXT]), + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_INTERESTING_PREV]), + GLOC_INTERESTING); + } + Sprintf(sbuf, "Use '%s' to change fast-move mode to %s.", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_MOVESKIP]), + fastmovemode[!iflags.getloc_moveskip]); + putstr(tmpwin, 0, sbuf); + if (!iflags.terrainmode || (iflags.terrainmode & TER_DETECT) == 0) { + Sprintf(sbuf, "Use '%s' to toggle menu listing for possible targets.", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_MENU])); + putstr(tmpwin, 0, sbuf); + Sprintf(sbuf, + "Use '%s' to change the mode of limiting possible targets.", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_LIMITVIEW])); + putstr(tmpwin, 0, sbuf); + } + if (!iflags.terrainmode) { + char kbuf[BUFSZ]; + + if (getpos_getvalid) { + Sprintf(sbuf, "Use '%s' or '%s' to move to valid locations.", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_VALID_NEXT]), + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_VALID_PREV])); + putstr(tmpwin, 0, sbuf); + } + if (getpos_hilitefunc) { + Sprintf(sbuf, "Use '%s' to toggle marking of valid locations.", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_SHOWVALID])); + putstr(tmpwin, 0, sbuf); + } + Sprintf(sbuf, "Use '%s' to toggle automatic description.", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_AUTODESC])); + putstr(tmpwin, 0, sbuf); + if (iflags.cmdassist) { /* assisting the '/' command, I suppose... */ + Sprintf(sbuf, + (iflags.getpos_coords == GPCOORDS_NONE) + ? "(Set 'whatis_coord' option to include coordinates with '%s' text.)" + : "(Reset 'whatis_coord' option to omit coordinates from '%s' text.)", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_AUTODESC])); + } + skip_non_mons: + /* disgusting hack; the alternate selection characters work for any + getpos call, but only matter for dowhatis (and doquickwhatis, + also for dotherecmdmenu's simulated mouse) */ + doing_what_is = (goal == what_is_a_location); + if (doing_what_is) { + Sprintf(kbuf, "'%s' or '%s' or '%s' or '%s'", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK]), + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_Q]), + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_O]), + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_V])); + } else { + Sprintf(kbuf, "'%s'", visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK])); + } + Snprintf(sbuf, sizeof(sbuf), + "Type a %s when you are at the right place.", kbuf); + putstr(tmpwin, 0, sbuf); + if (doing_what_is) { + Sprintf(sbuf, + " '%s' describe current spot, show 'more info', move to another spot.", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_V])); + putstr(tmpwin, 0, sbuf); + Sprintf(sbuf, + " '%s' describe current spot,%s move to another spot;", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK]), + flags.help && !force ? " prompt if 'more info'," : ""); + putstr(tmpwin, 0, sbuf); + Sprintf(sbuf, + " '%s' describe current spot, move to another spot;", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_Q])); + putstr(tmpwin, 0, sbuf); + Sprintf(sbuf, + " '%s' describe current spot, stop looking at things;", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_O])); + putstr(tmpwin, 0, sbuf); + } + } + if (!force) + putstr(tmpwin, 0, "Type Space or Escape when you're done."); + putstr(tmpwin, 0, ""); + display_nhwindow(tmpwin, TRUE); + destroy_nhwindow(tmpwin); +} + +RESTORE_WARNING_FORMAT_NONLITERAL + +staticfn int QSORTCALLBACK +cmp_coord_distu(const void *a, const void *b) +{ + const coord *c1 = a; + const coord *c2 = b; + int dx, dy, dist_1, dist_2; + + dx = u.ux - c1->x; + dy = u.uy - c1->y; + dist_1 = max(abs(dx), abs(dy)); + dx = u.ux - c2->x; + dy = u.uy - c2->y; + dist_2 = max(abs(dx), abs(dy)); + + if (dist_1 == dist_2) + return (c1->y != c2->y) ? (c1->y - c2->y) : (c1->x - c2->x); + + return dist_1 - dist_2; +} + +#define IS_UNEXPLORED_LOC(x,y) \ + (isok((x), (y)) \ + && glyph_is_unexplored(levl[(x)][(y)].glyph) \ + && !levl[(x)][(y)].seenv) + +#define GLOC_SAME_AREA(x,y) \ + (isok((x), (y)) \ + && (selection_getpoint((x),(y), gg.gloc_filter_map))) + +staticfn int +gloc_filter_classify_glyph(int glyph) +{ + int c; + + if (!glyph_is_cmap(glyph)) + return 0; + + c = glyph_to_cmap(glyph); + + if (is_cmap_room(c) || is_cmap_furniture(c)) + return 1; + else if (is_cmap_wall(c) || c == S_tree) + return 2; + else if (is_cmap_corr(c)) + return 3; + else if (is_cmap_water(c)) + return 4; + else if (is_cmap_lava(c)) + return 5; + return 0; +} + +staticfn int +gloc_filter_floodfill_matcharea(coordxy x, coordxy y) +{ + int glyph = back_to_glyph(x, y); + + if (!levl[x][y].seenv) + return FALSE; + + if (glyph == gg.gloc_filter_floodfill_match_glyph) + return TRUE; + + if (gloc_filter_classify_glyph(glyph) + == gloc_filter_classify_glyph(gg.gloc_filter_floodfill_match_glyph)) + return TRUE; + + return FALSE; +} + +staticfn void +gloc_filter_floodfill(coordxy x, coordxy y) +{ + gg.gloc_filter_floodfill_match_glyph = back_to_glyph(x, y); + + set_selection_floodfillchk(gloc_filter_floodfill_matcharea); + selection_floodfill(gg.gloc_filter_map, x, y, FALSE); +} + +staticfn void +gloc_filter_init(void) +{ + if (iflags.getloc_filter == GFILTER_AREA) { + if (!gg.gloc_filter_map) { + gg.gloc_filter_map = selection_new(); + } + /* special case: if we're in a doorway, try to figure out which + direction we're moving, and use that side of the doorway */ + if (IS_DOOR(levl[u.ux][u.uy].typ)) { + if ((u.dx || u.dy) && isok(u.ux + u.dx, u.uy + u.dy)) { + gloc_filter_floodfill(u.ux + u.dx, u.uy + u.dy); + } else { + /* TODO: maybe add both sides of the doorway? */ + } + } else { + gloc_filter_floodfill(u.ux, u.uy); + } + } +} + +staticfn void +gloc_filter_done(void) +{ + if (gg.gloc_filter_map) { + selection_free(gg.gloc_filter_map, TRUE); + gg.gloc_filter_map = (struct selectionvar *) 0; + + } +} + +DISABLE_WARNING_UNREACHABLE_CODE + +boolean +gather_locs_interesting(coordxy x, coordxy y, int gloc) +{ + int glyph, sym; + + if (iflags.getloc_filter == GFILTER_VIEW && !cansee(x, y)) + return FALSE; + if (iflags.getloc_filter == GFILTER_AREA && !GLOC_SAME_AREA(x, y) + && !GLOC_SAME_AREA(x - 1, y) && !GLOC_SAME_AREA(x, y - 1) + && !GLOC_SAME_AREA(x + 1, y) && !GLOC_SAME_AREA(x, y + 1)) + return FALSE; + + glyph = glyph_at(x, y); + sym = glyph_is_cmap(glyph) ? glyph_to_cmap(glyph) : -1; + switch (gloc) { + default: + case GLOC_MONS: + /* unlike '/M', this skips monsters revealed by + warning glyphs and remembered unseen ones */ + return (glyph_is_monster(glyph) + && glyph != monnum_to_glyph(PM_LONG_WORM_TAIL,MALE) + && glyph != monnum_to_glyph(PM_LONG_WORM_TAIL, FEMALE)); + case GLOC_OBJS: + return (glyph_is_object(glyph) + && glyph != objnum_to_glyph(BOULDER) + && glyph != objnum_to_glyph(ROCK)); + case GLOC_DOOR: + return (glyph_is_cmap(glyph) + && (is_cmap_door(sym) + || is_cmap_drawbridge(sym) + || sym == S_ndoor)); + case GLOC_EXPLORE: + return (glyph_is_cmap(glyph) + && !glyph_is_nothing(glyph_to_cmap(glyph)) + && (is_cmap_door(sym) + || is_cmap_drawbridge(sym) + || sym == S_ndoor + || is_cmap_room(sym) + || is_cmap_corr(sym)) + && (IS_UNEXPLORED_LOC(x + 1, y) + || IS_UNEXPLORED_LOC(x - 1, y) + || IS_UNEXPLORED_LOC(x, y + 1) + || IS_UNEXPLORED_LOC(x, y - 1))); + case GLOC_VALID: + if (getpos_getvalid) + return (*getpos_getvalid)(x, y); + FALLTHROUGH; + /*FALLTHRU*/ + case GLOC_INTERESTING: + return (gather_locs_interesting(x, y, GLOC_DOOR) + || !((glyph_is_cmap(glyph) + && (is_cmap_wall(sym) + || sym == S_tree + || sym == S_bars + || sym == S_ice + || sym == S_air + || sym == S_cloud + || is_cmap_lava(sym) + || is_cmap_water(sym) + || sym == S_ndoor + || is_cmap_room(sym) + || is_cmap_corr(sym))) + || glyph_is_nothing(glyph) + || glyph_is_unexplored(glyph))); + } + /*NOTREACHED*/ + return FALSE; +} + +RESTORE_WARNINGS + +/* gather locations for monsters or objects shown on the map */ +staticfn void +gather_locs(coord **arr_p, int *cnt_p, int gloc) +{ + int pass, idx; + coordxy x, y; + + /* + * We always include the hero's location even if there is no monster + * (invisible hero without see invisible) or object (usual case) + * displayed there. That way, the count will always be at least 1, + * and player has a visual indicator (cursor returns to hero's spot) + * highlighting when successive 'm's or 'o's have cycled all the way + * through all monsters or objects. + * + * Hero's spot will always sort to array[0] because it will always + * be the shortest distance (namely, 0 units) away from . + */ + + gloc_filter_init(); + + *cnt_p = idx = 0; + for (pass = 0; pass < 2; pass++) { + for (x = 1; x < COLNO; x++) + for (y = 0; y < ROWNO; y++) { + if (u_at(x, y) || gather_locs_interesting(x, y, gloc)) { + if (!pass) { + ++*cnt_p; + } else { + (*arr_p)[idx].x = x; + (*arr_p)[idx].y = y; + ++idx; + } + } + } + + if (!pass) /* end of first pass */ + *arr_p = (coord *) alloc(*cnt_p * sizeof (coord)); + else /* end of second pass */ + qsort(*arr_p, *cnt_p, sizeof (coord), cmp_coord_distu); + } /* pass */ + + gloc_filter_done(); +} + +char * +dxdy_to_dist_descr(coordxy dx, coordxy dy, boolean fulldir) +{ + static char buf[30]; + int dst; + + if (!dx && !dy) { + Sprintf(buf, "here"); + } else if ((dst = xytod(dx, dy)) != -1) { + /* explicit direction; 'one step' is implicit */ + Sprintf(buf, "%s", directionname(dst)); + } else { + static const char *const dirnames[4][2] = { + { "n", "north" }, + { "s", "south" }, + { "w", "west" }, + { "e", "east" } }; + buf[0] = '\0'; + /* 9999: protect buf[] against overflow caused by invalid values */ + if (dy) { + if (abs(dy) > 9999) + dy = sgn(dy) * 9999; + Sprintf(eos(buf), "%d%s%s", abs(dy), dirnames[(dy > 0)][fulldir], + dx ? "," : ""); + } + if (dx) { + if (abs(dx) > 9999) + dx = sgn(dx) * 9999; + Sprintf(eos(buf), "%d%s", abs(dx), + dirnames[2 + (dx > 0)][fulldir]); + } + } + return buf; +} + +DISABLE_WARNING_FORMAT_NONLITERAL + +/* coordinate formatting for 'whatis_coord' option */ +char * +coord_desc(coordxy x, coordxy y, char *outbuf, char cmode) +{ + static char screen_fmt[16]; /* [12] suffices: "[%02d,%02d]" */ + int dx, dy; + + outbuf[0] = '\0'; + switch (cmode) { + default: + break; + case GPCOORDS_COMFULL: + case GPCOORDS_COMPASS: + /* "east", "3s", "2n,4w" */ + dx = x - u.ux; + dy = y - u.uy; + Sprintf(outbuf, "(%s)", + dxdy_to_dist_descr(dx, dy, cmode == GPCOORDS_COMFULL)); + break; + case GPCOORDS_MAP: /* x,y */ + /* upper left corner of map is <1,0>; + with default COLNO,ROWNO lower right corner is <79,20> */ + Sprintf(outbuf, "<%d,%d>", x, y); + break; + case GPCOORDS_SCREEN: /* y+2,x */ + /* for normal map sizes, force a fixed-width formatting so that + /m, /M, /o, and /O output lines up cleanly; map sizes bigger + than Nx999 or 999xM will still work, but not line up like normal + when displayed in a column setting. + + The (100) is placed in brackets below to mark the [: "03"] as + explicit compile-time dead code for clang */ + if (!*screen_fmt) + Sprintf(screen_fmt, "[%%%sd,%%%sd]", + (ROWNO - 1 + 2 < (100)) ? "02" : "03", + (COLNO - 1 < (100)) ? "02" : "03"); + /* map line 0 is screen row 2; + map column 0 isn't used, map column 1 is screen column 1 */ + Sprintf(outbuf, screen_fmt, y + 2, x); + break; + } + return outbuf; +} + +RESTORE_WARNING_FORMAT_NONLITERAL + +void +auto_describe(coordxy cx, coordxy cy) +{ + coord cc; + int sym = 0; + char tmpbuf[BUFSZ]; + const char *firstmatch = "unknown"; + + cc.x = cx; + cc.y = cy; + if (do_screen_description(cc, TRUE, sym, tmpbuf, &firstmatch, + (struct permonst **) 0)) { + (void) coord_desc(cx, cy, tmpbuf, iflags.getpos_coords); + custompline((SUPPRESS_HISTORY | OVERRIDE_MSGTYPE | NO_CURS_ON_U), + "%s%s%s%s%s", firstmatch, *tmpbuf ? " " : "", tmpbuf, + (iflags.autodescribe + && getpos_getvalid && !(*getpos_getvalid)(cx, cy)) + ? " (invalid target)" : "", + (iflags.getloc_travelmode && !is_valid_travelpt(cx, cy)) + ? " (no travel path)" : ""); + curs(WIN_MAP, cx, cy); + flush_screen(0); + } +} + +boolean +getpos_menu(coord *ccp, int gloc) +{ + coord *garr = DUMMY; + int gcount = 0; + winid tmpwin; + anything any; + int i, pick_cnt; + menu_item *picks = (menu_item *) 0; + char tmpbuf[BUFSZ]; + int clr = NO_COLOR; + + gather_locs(&garr, &gcount, gloc); + + if (gcount < 2) { /* gcount always includes the hero */ + free((genericptr_t) garr); + You("cannot %s %s.", + (iflags.getloc_filter == GFILTER_VIEW) ? "see" : "detect", + gloc_descr[gloc][0]); + return FALSE; + } + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin, MENU_BEHAVE_STANDARD); + any = cg.zeroany; + + /* gather_locs returns array[0] == you. skip it. */ + for (i = 1; i < gcount; i++) { + char fullbuf[BUFSZ]; + coord tmpcc; + const char *firstmatch = "unknown"; + int sym = 0; + + any.a_int = i + 1; + tmpcc.x = garr[i].x; + tmpcc.y = garr[i].y; + if (do_screen_description(tmpcc, TRUE, sym, tmpbuf, + &firstmatch, (struct permonst **)0)) { + (void) coord_desc(garr[i].x, garr[i].y, tmpbuf, + iflags.getpos_coords); + Snprintf(fullbuf, sizeof fullbuf, "%s%s%s", firstmatch, + (*tmpbuf ? " " : ""), tmpbuf); + add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, + ATR_NONE, clr, fullbuf, MENU_ITEMFLAGS_NONE); + } + } + + Sprintf(tmpbuf, "Pick %s%s%s", + an(gloc_descr[gloc][1]), + gloc_filtertxt[iflags.getloc_filter], + iflags.getloc_travelmode ? " for travel destination" : ""); + end_menu(tmpwin, tmpbuf); + pick_cnt = select_menu(tmpwin, PICK_ONE, &picks); + destroy_nhwindow(tmpwin); + if (pick_cnt > 0) { + ccp->x = garr[picks->item.a_int - 1].x; + ccp->y = garr[picks->item.a_int - 1].y; + free((genericptr_t) picks); + } + free((genericptr_t) garr); + return (pick_cnt > 0); +} + +/* add dx,dy to cx,cy, truncating at map edges */ +staticfn void +truncate_to_map(coordxy *cx, coordxy *cy, schar dx, schar dy) +{ + /* diagonal moves complicate this... */ + if (*cx + dx < 1) { + dy -= sgn(dy) * (1 - (*cx + dx)); + dx = 1 - *cx; /* so that (cx+dx == 1) */ + } else if (*cx + dx > COLNO - 1) { + dy += sgn(dy) * ((COLNO - 1) - (*cx + dx)); + dx = (COLNO - 1) - *cx; + } + if (*cy + dy < 0) { + dx -= sgn(dx) * (0 - (*cy + dy)); + dy = 0 - *cy; /* so that (cy+dy == 0) */ + } else if (*cy + dy > ROWNO - 1) { + dx += sgn(dx) * ((ROWNO - 1) - (*cy + dy)); + dy = (ROWNO - 1) - *cy; + } + *cx += dx; + *cy += dy; +} + +/* called when ^R typed; if '$' is being shown for valid spots, remove that; + if alternate background color is being shown for that, redraw it */ +staticfn void +getpos_refresh(void) +{ + if (getpos_hilitefunc && getpos_hilite_state == HiliteGoodposSymbol) { + (*getpos_hilitefunc)(FALSE); /* tmp_at(DISP_END) */ + getpos_hilite_state = defaultHiliteState; + } + + docrt_flags(docrtRefresh); + + if (getpos_hilitefunc && getpos_hilite_state == HiliteBackground) { + /* resetting to current values will draw valid-spots highlighting */ + getpos_sethilite(getpos_hilitefunc, getpos_getvalid); + } +} + +/* have the player use movement keystrokes to position the cursor at a + particular map location, then use one of [.,:;] to pick the spot */ +int +getpos(coord *ccp, boolean force, const char *goal) +{ + static struct { + int nhkf, ret; + } const pick_chars_def[] = { + { NHKF_GETPOS_PICK, LOOK_TRADITIONAL }, + { NHKF_GETPOS_PICK_Q, LOOK_QUICK }, + { NHKF_GETPOS_PICK_O, LOOK_ONCE }, + { NHKF_GETPOS_PICK_V, LOOK_VERBOSE } + }; + static const int mMoOdDxX_def[] = { + NHKF_GETPOS_MON_NEXT, + NHKF_GETPOS_MON_PREV, + NHKF_GETPOS_OBJ_NEXT, + NHKF_GETPOS_OBJ_PREV, + NHKF_GETPOS_DOOR_NEXT, + NHKF_GETPOS_DOOR_PREV, + NHKF_GETPOS_UNEX_NEXT, + NHKF_GETPOS_UNEX_PREV, + NHKF_GETPOS_INTERESTING_NEXT, + NHKF_GETPOS_INTERESTING_PREV, + NHKF_GETPOS_VALID_NEXT, + NHKF_GETPOS_VALID_PREV + }; + struct _cmd_queue cq, *cmdq; + const char *cp; + char pick_chars[6]; + char mMoOdDxX[13]; + int result = 0; + int i, c; + int sidx; + coordxy cx, cy; + coordxy tx = u.ux, ty = u.uy; + boolean msg_given = TRUE; /* clear message window by default */ + boolean show_goal_msg = FALSE; + coord *garr[NUM_GLOCS] = DUMMY; + int gcount[NUM_GLOCS] = DUMMY; + int gidx[NUM_GLOCS] = DUMMY; + schar udx = u.dx, udy = u.dy, udz = u.dz; + int dx, dy; + boolean rushrun = FALSE; + + /* temporary? if we have a queued direction, return the adjacent spot + in that direction */ + if (!gi.in_doagain) { + if ((cmdq = cmdq_pop()) != 0) { + cq = *cmdq; + free((genericptr_t) cmdq); + if (cq.typ == CMDQ_DIR && !cq.dirz) { + ccp->x = u.ux + cq.dirx; + ccp->y = u.uy + cq.diry; + } else { + cmdq_clear(CQ_CANNED); + result = -1; + } + return result; + } + } + + for (i = 0; i < SIZE(pick_chars_def); i++) + pick_chars[i] = gc.Cmd.spkeys[pick_chars_def[i].nhkf]; + pick_chars[SIZE(pick_chars_def)] = '\0'; + + for (i = 0; i < SIZE(mMoOdDxX_def); i++) + mMoOdDxX[i] = gc.Cmd.spkeys[mMoOdDxX_def[i]]; + mMoOdDxX[SIZE(mMoOdDxX_def)] = '\0'; + + if (handle_tip(TIP_GETPOS)) + show_goal_msg = TRUE; /* tip has overwritten prompt in mesg window */ + + if (!goal) + goal = "desired location"; + if (flags.verbose) { + pline("(For instructions type a '%s')", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_HELP])); + msg_given = TRUE; + } + cx = gg.getposx = ccp->x; + cy = gg.getposy = ccp->y; +#ifdef CLIPPING + cliparound(cx, cy); +#endif + curs(WIN_MAP, cx, cy); + flush_screen(0); +#ifdef MAC + lock_mouse_cursor(TRUE); +#endif + lock_mouse_buttons(TRUE); + for (;;) { + if (show_goal_msg) { + pline("Move cursor to %s:", goal); + curs(WIN_MAP, cx, cy); + flush_screen(0); + show_goal_msg = FALSE; + } else if (iflags.autodescribe && !msg_given) { + auto_describe(cx, cy); + } + + rushrun = FALSE; + + if ((cmdq = cmdq_pop()) != 0) { + if (cmdq->typ == CMDQ_KEY) { + c = cmdq->key; + } else { + cmdq_clear(CQ_CANNED); + result = -1; + goto exitgetpos; + } + free(cmdq); + } else { + c = readchar_poskey(&tx, &ty, &sidx); + /* remember_getpos is normally False because reusing the + cursor positioning during ^A is almost never the right + thing to do, but caller could set it if that was needed */ + if (iflags.remember_getpos && !gi.in_doagain) + cmdq_add_key(CQ_REPEAT, c); + } + + if (iflags.autodescribe) + msg_given = FALSE; + + if (c == gc.Cmd.spkeys[NHKF_ESC]) { + cx = cy = -10; + msg_given = TRUE; /* force clear */ + result = -1; + break; + } + if (c == cmd_from_func(do_run) || c == cmd_from_func(do_rush)) { + c = readchar_poskey(&tx, &ty, &sidx); + rushrun = TRUE; + } + if (c == 0) { + if (!isok(tx, ty)) + continue; + /* a mouse click event, just assign and return */ + cx = tx; + cy = ty; + break; + } + if ((cp = strchr(pick_chars, c)) != 0) { + /* '.' => 0, ',' => 1, ';' => 2, ':' => 3 */ + result = pick_chars_def[(int) (cp - pick_chars)].ret; + break; + } else if (movecmd(c, MV_WALK)) { + if (rushrun) + goto do_rushrun; + dx = u.dx; + dy = u.dy; + truncate_to_map(&cx, &cy, dx, dy); + goto nxtc; + } else if (movecmd(c, MV_RUSH) || movecmd(c, MV_RUN)) { + do_rushrun: + if (iflags.getloc_moveskip) { + /* skip same glyphs */ + int glyph = glyph_at(cx, cy); + + dx = u.dx; + dy = u.dy; + while (isok(cx + dx, cy + dy) + && glyph == glyph_at(cx + dx, cy + dy) + && isok(cx + dx + u.dx, cy + dy + u.dy) + && glyph == glyph_at(cx + dx + u.dx, cy + dy + u.dy)) { + dx += u.dx; + dy += u.dy; + + } + } else { + dx = 8 * u.dx; + dy = 8 * u.dy; + } + truncate_to_map(&cx, &cy, dx, dy); + goto nxtc; + } + + if (c == gc.Cmd.spkeys[NHKF_GETPOS_HELP] || redraw_cmd(c)) { + /* '?' will redraw twice, first when removing popup text window + after showing the help text, then to reset highlighting */ + if (c == gc.Cmd.spkeys[NHKF_GETPOS_HELP]) + getpos_help(force, goal); + /* ^R: docrt(), hilite_state = default */ + getpos_refresh(); + curs(WIN_MAP, cx, cy); + /* update message window to reflect that we're still targeting */ + show_goal_msg = TRUE; + } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_SHOWVALID]) { + if (getpos_hilitefunc) { + getpos_toggle_hilite_state(); + curs(WIN_MAP, cx, cy); + } + show_goal_msg = TRUE; /* we're still targeting */ + goto nxtc; + } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_AUTODESC]) { + iflags.autodescribe = !iflags.autodescribe; + pline("Automatic description %sis %s.", + flags.verbose ? "of features under cursor " : "", + iflags.autodescribe ? "on" : "off"); + if (!iflags.autodescribe) + show_goal_msg = TRUE; + msg_given = TRUE; + goto nxtc; + } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_LIMITVIEW]) { + static const char *const view_filters[NUM_GFILTER] = { + "Not limiting targets", + "Limiting targets to those in sight", + "Limiting targets to those in same area" + }; + + iflags.getloc_filter = (iflags.getloc_filter + 1) % NUM_GFILTER; + for (i = 0; i < NUM_GLOCS; i++) { + if (garr[i]) { + free((genericptr_t) garr[i]); + garr[i] = NULL; + } + gidx[i] = gcount[i] = 0; + } + pline("%s.", view_filters[iflags.getloc_filter]); + msg_given = TRUE; + goto nxtc; + } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_MENU]) { + iflags.getloc_usemenu = !iflags.getloc_usemenu; + pline("%s a menu to show possible targets%s.", + iflags.getloc_usemenu ? "Using" : "Not using", + iflags.getloc_usemenu + ? " for 'm|M', 'o|O', 'd|D', and 'x|X'" : ""); + msg_given = TRUE; + goto nxtc; + } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_SELF]) { + /* reset 'm&M', 'o&O', &c; otherwise, there's no way for player + to achieve that except by manually cycling through all spots */ + for (i = 0; i < NUM_GLOCS; i++) + gidx[i] = 0; + cx = u.ux; + cy = u.uy; + goto nxtc; + } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_MOVESKIP]) { + iflags.getloc_moveskip = !iflags.getloc_moveskip; + pline("%skipping over similar terrain when fastmoving the cursor.", + iflags.getloc_moveskip ? "S" : "Not s"); + msg_given = TRUE; + goto nxtc; + } else if ((cp = strchr(mMoOdDxX, c)) != 0) { /* 'm|M', 'o|O', &c */ + /* nearest or farthest monster or object or door or unexplored */ + int gtmp = (int) (cp - mMoOdDxX), /* 0..7 */ + gloc = gtmp >> 1; /* 0..3 */ + + if (iflags.getloc_usemenu) { + coord tmpcrd; + + if (getpos_menu(&tmpcrd, gloc)) { + cx = tmpcrd.x; + cy = tmpcrd.y; + } + goto nxtc; + } + + if (!garr[gloc]) { + gather_locs(&garr[gloc], &gcount[gloc], gloc); + gidx[gloc] = 0; /* garr[][0] is hero's spot */ + } + if (!(gtmp & 1)) { /* c=='m' || c=='o' || c=='d' || c=='x') */ + gidx[gloc] = (gidx[gloc] + 1) % gcount[gloc]; + } else { /* c=='M' || c=='O' || c=='D' || c=='X') */ + if (--gidx[gloc] < 0) + gidx[gloc] = gcount[gloc] - 1; + } + cx = garr[gloc][gidx[gloc]].x; + cy = garr[gloc][gidx[gloc]].y; + goto nxtc; + } else { + if (!strchr(quitchars, c)) { + char matching[MAXPCHARS]; + int pass, k = 0; + coordxy lo_x, lo_y, hi_x, hi_y; + + (void) memset((genericptr_t) matching, 0, sizeof matching); + for (sidx = 0; sidx < MAXPCHARS; sidx++) { + /* don't even try to match some terrain: walls, room... */ + if (is_cmap_wall(sidx) || is_cmap_room(sidx) + || is_cmap_corr(sidx) || is_cmap_door(sidx) + || sidx == S_ndoor) + continue; + if (c == defsyms[sidx].sym + || c == (int) gs.showsyms[sidx] + /* have '^' match webs and vibrating square or any + other trap that uses something other than '^' */ + || (c == '^' && is_cmap_trap(sidx)) + /* have room engraving character (default '`') + match corridor engravings (default '#') too */ + || (c == gs.showsyms[S_engroom] + && is_cmap_engraving(sidx))) + matching[sidx] = (char) ++k; + } + if (k) { + for (pass = 0; pass <= 1; pass++) { + /* pass 0: just past current pos to lower right; + pass 1: upper left corner to current pos */ + lo_y = (pass == 0) ? cy : 0; + hi_y = (pass == 0) ? ROWNO - 1 : cy; + for (ty = lo_y; ty <= hi_y; ty++) { + lo_x = (pass == 0 && ty == lo_y) ? cx + 1 : 1; + hi_x = (pass == 1 && ty == hi_y) ? cx : COLNO - 1; + for (tx = lo_x; tx <= hi_x; tx++) { + /* first, look at what is currently visible + (might be monster) */ + k = glyph_at(tx, ty); + if (glyph_is_cmap(k) + && matching[glyph_to_cmap(k)]) + goto foundc; + /* next, try glyph that's remembered here + (might be trap or object) */ + if (svl.level.flags.hero_memory + /* !terrainmode: don't move to remembered + trap or object if not currently shown */ + && !iflags.terrainmode) { + k = levl[tx][ty].glyph; + if (glyph_is_cmap(k) + && matching[glyph_to_cmap(k)]) + goto foundc; + } + /* last, try actual terrain here (shouldn't + we be using svl.lastseentyp[][] instead?) */ + if (levl[tx][ty].seenv) { + k = back_to_glyph(tx, ty); + if (glyph_is_cmap(k) + && matching[glyph_to_cmap(k)]) + goto foundc; + } + continue; + foundc: + cx = tx, cy = ty; + if (msg_given) { + clear_nhwindow(WIN_MESSAGE); + msg_given = FALSE; + } + goto nxtc; + } /* column */ + } /* row */ + } /* pass */ + pline("Can't find dungeon feature '%c'.", c); + msg_given = TRUE; + goto nxtc; + } else { + char note[QBUFSZ]; + + if (!force) + Strcpy(note, "aborted"); + else /* hjkl */ + Sprintf(note, "use '%s', '%s', '%s', '%s' or '%s'", + visctrl(cmd_from_func(do_move_west)), + visctrl(cmd_from_func(do_move_south)), + visctrl(cmd_from_func(do_move_north)), + visctrl(cmd_from_func(do_move_east)), + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK])); + pline("Unknown direction: '%s' (%s).", visctrl((char) c), + note); + msg_given = TRUE; + } /* k => matching */ + } /* !quitchars */ + if (force) + goto nxtc; + pline("Done."); + msg_given = FALSE; /* suppress clear */ + cx = -1; + cy = 0; + result = 0; /* not -1 */ + break; + } + nxtc: + gg.getposx = cx, gg.getposy = cy; +#ifdef CLIPPING + cliparound(cx, cy); +#endif + curs(WIN_MAP, cx, cy); + flush_screen(0); + } + exitgetpos: +#ifdef MAC + lock_mouse_cursor(FALSE); +#endif + lock_mouse_buttons(FALSE); + if (msg_given) + clear_nhwindow(WIN_MESSAGE); + ccp->x = cx; + ccp->y = cy; + gg.getposx = gg.getposy = 0; + for (i = 0; i < NUM_GLOCS; i++) + if (garr[i]) + free((genericptr_t) garr[i]); + getpos_sethilite(NULL, NULL); + u.dx = udx, u.dy = udy, u.dz = udz; + return result; +} + +/*getpos.c*/ diff --git a/src/glyphs.c b/src/glyphs.c new file mode 100644 index 0000000000..fb90aa04da --- /dev/null +++ b/src/glyphs.c @@ -0,0 +1,1325 @@ +/* NetHack 3.7 glyphs.c TODO: add NHDT branch/date/revision tags */ +/* Copyright (c) Michael Allison, 2021. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +extern const struct symparse loadsyms[]; +extern glyph_map glyphmap[MAX_GLYPH]; +extern struct enum_dump monsdump[]; +extern struct enum_dump objdump[]; + +#define Fprintf (void) fprintf + +enum reserved_activities { res_nothing, res_dump_glyphids, res_fill_cache }; +enum things_to_find { find_nothing, find_pm, find_oc, find_cmap, find_glyph }; +struct find_struct { + enum things_to_find findtype; + int val; + int loadsyms_offset; + int loadsyms_count; + int *extraval; + uint32 color; + const char *unicode_val; /* U+NNNN format */ + void (*callback)(int glyph, struct find_struct *); + enum reserved_activities restype; + genericptr_t reserved; +}; +static const struct find_struct zero_find = { 0 }; +struct glyphid_cache_t { + int glyphnum; + char *id; +}; +static struct glyphid_cache_t *glyphid_cache; +static unsigned glyphid_cache_lsize; +static size_t glyphid_cache_size; +static struct find_struct glyphcache_find, to_custom_symbol_find; +static const long nonzero_black = CLR_BLACK | NH_BASIC_COLOR; + +staticfn void init_glyph_cache(void); +staticfn void add_glyph_to_cache(int glyphnum, const char *id); +staticfn int find_glyph_in_cache(const char *id); +staticfn char *find_glyphid_in_cache_by_glyphnum(int glyphnum); +staticfn uint32 glyph_hash(const char *id); +staticfn void to_custom_symset_entry_callback(int glyph, + struct find_struct *findwhat); +staticfn int parse_id(const char *id, struct find_struct *findwhat); +staticfn int glyph_find_core(const char *id, struct find_struct *findwhat); +staticfn char *fix_glyphname(char *str); +staticfn void shuffle_customizations(void); +/* staticfn void purge_custom_entries(enum graphics_sets which_set); */ + +staticfn void +to_custom_symset_entry_callback( + int glyph, + struct find_struct *findwhat) +{ + int idx = gs.symset_which_set; +#ifdef ENHANCED_SYMBOLS + uint8 utf8str[6] = { 0, 0, 0, 0, 0, 0 }; + int uval = 0; +#endif + + if (findwhat->extraval) + *findwhat->extraval = glyph; + + assert(idx >= 0 && idx < NUM_GRAPHICS); +#ifdef ENHANCED_SYMBOLS + if (findwhat->unicode_val) + uval = unicode_val(findwhat->unicode_val); + if (uval && unicodeval_to_utf8str(uval, utf8str, sizeof utf8str)) { + /* presently the customizations are affiliated with a particular + * symset but if we don't have any symset context, ignore it for now + * in order to avoid a segfault. + * FIXME: + * One future idea might be to store the U+ entries under "UTF8" + * and apply those customizations to any current symset if it has + * a UTF8 handler. Similar approach for unaffiliated glyph/symbols + * non-UTF color customizations + */ + if (gs.symset[idx].name) { + add_custom_urep_entry(gs.symset[idx].name, glyph, uval, + utf8str, gs.symset_which_set); + } else { + static int glyphnag = 0; + + if (!glyphnag++) + config_error_add("Unimplemented customization feature," + " ignoring for now"); + } + } +#endif + if (findwhat->color) { + if (gs.symset[idx].name) { + add_custom_nhcolor_entry(gs.symset[idx].name, glyph, + findwhat->color, gs.symset_which_set); + } else { + static int colornag = 0; + + if (!colornag++) + config_error_add("Unimplemented customization feature," + " ignoring for now"); + } + } +} + +/* + * Return value: + * 1 = success + * 0 = failure + */ +int +glyphrep_to_custom_map_entries( + const char *op, + int *glyphptr) +{ + to_custom_symbol_find = zero_find; + char buf[BUFSZ], *c_glyphid, *c_unicode, *c_colorval, *cp; + int reslt = 0; + long rgb = 0L; + boolean slash = FALSE, colon = FALSE; + + 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; + c_glyphid = cp = buf; + while (*cp) { + if (*cp == ':' || *cp == '/') { + if (*cp == ':') { + colon = TRUE; + *cp = '\0'; + } + if (*cp == '/') { + slash = TRUE; + *cp = '\0'; + } + } + cp++; + if (colon) { + c_unicode = cp; + colon = FALSE; + } + if (slash) { + c_colorval = cp; + slash = FALSE; + } + } + /* some sanity checks */ + if (c_glyphid && *c_glyphid == ' ') + c_glyphid++; + if (c_colorval && *c_colorval == ' ') + c_colorval++; + if (c_unicode && *c_unicode == ' ') { + while (*c_unicode == ' ') { + c_unicode++; + } + } + if (c_unicode && !*c_unicode) + c_unicode = 0; + + if ((c_colorval && (rgb = rgbstr_to_int32(c_colorval)) != -1L) + || !c_colorval) { + /* if the color 0 is an actual color, as opposed to just "not set" + we set a marker bit outside the 24-bit range to indicate a + valid color value 0. That allows valid color 0, but allows a + simple checking for 0 to detect "not set". The window port that + implements the color switch, needs to either check that bit + or appropriately mask colors with 0xFFFFFF. */ + to_custom_symbol_find.color = (rgb == -1 || !c_colorval) ? 0L + : (rgb == 0L) ? nonzero_black + : rgb; + } + if (c_unicode) + to_custom_symbol_find.unicode_val = c_unicode; + to_custom_symbol_find.extraval = glyphptr; + to_custom_symbol_find.callback = to_custom_symset_entry_callback; + reslt = glyph_find_core(c_glyphid, &to_custom_symbol_find); + return reslt; +} + +staticfn char * +fix_glyphname(char *str) +{ + char *c; + + for (c = str; *c; c++) { + if (*c >= 'A' && *c <= 'Z') + *c += (char) ('a' - 'A'); + else if (*c >= '0' && *c <= '9') + ; + else if (*c < 'a' || *c > 'z') + *c = '_'; + } + return str; +} + +int +glyph_to_cmap(int glyph) +{ + if (glyph == GLYPH_CMAP_STONE_OFF) + return S_stone; + else if (glyph_is_cmap_main(glyph)) + return (glyph - GLYPH_CMAP_MAIN_OFF) + S_vwall; + else if (glyph_is_cmap_mines(glyph)) + return (glyph - GLYPH_CMAP_MINES_OFF) + S_vwall; + else if (glyph_is_cmap_gehennom(glyph)) + return (glyph - GLYPH_CMAP_GEH_OFF) + S_vwall; + else if (glyph_is_cmap_knox(glyph)) + return (glyph - GLYPH_CMAP_KNOX_OFF) + S_vwall; + else if (glyph_is_cmap_sokoban(glyph)) + return (glyph - GLYPH_CMAP_SOKO_OFF) + S_vwall; + else if (glyph_is_cmap_a(glyph)) + return (glyph - GLYPH_CMAP_A_OFF) + S_ndoor; + else if (glyph_is_cmap_altar(glyph)) + return S_altar; + else if (glyph_is_cmap_b(glyph)) + return (glyph - GLYPH_CMAP_B_OFF) + S_grave; + else if (glyph_is_cmap_c(glyph)) + return (glyph - GLYPH_CMAP_C_OFF) + S_digbeam; + else if (glyph_is_cmap_zap(glyph)) + return ((glyph - GLYPH_ZAP_OFF) % 4) + S_vbeam; + else if (glyph_is_swallow(glyph)) + return glyph_to_swallow(glyph) + S_sw_tl; + else if (glyph_is_explosion(glyph)) + return glyph_to_explosion(glyph) + S_expl_tl; + else if (glyph_is_cmap_d(glyph)) + return (glyph - GLYPH_CMAP_D_OFF) + S_grass; + else if (glyph_is_cmap_engraving(glyph)) { + /* this one depends on whether it's in the first or second half of + * engraving glyphs */ + if ((glyph - GLYPH_ENGRAVING_OFF) >= max_engraving_glyphtypes) + return S_engrcorr; + else + return S_engroom; + } + else if (glyph_is_cmap_magicplatform(glyph)) + return S_magicplatform; + else + return MAXPCHARS; /* MAXPCHARS is legal array index because + * of trailing fencepost entry */ +} + +staticfn int +glyph_find_core( + const char *id, + struct find_struct *findwhat) +{ + int glyph; + boolean do_callback, end_find = FALSE; + + if (parse_id(id, findwhat)) { + if (findwhat->findtype == find_glyph) { + (*findwhat->callback)(findwhat->val, findwhat); + } else { + for (glyph = 0; glyph < MAX_GLYPH; ++glyph) { + do_callback = FALSE; + switch (findwhat->findtype) { + case find_cmap: + if (glyph_to_cmap(glyph) == findwhat->val) + do_callback = TRUE; + break; + case find_pm: + if (glyph_is_monster(glyph) + && monsym(&mons[glyph_to_mon(glyph)]) + == findwhat->val) + do_callback = TRUE; + break; + case find_oc: + if (glyph_is_object(glyph) + && glyph_to_obj(glyph) == findwhat->val) + do_callback = TRUE; + break; + case find_glyph: + if (glyph == findwhat->val) { + do_callback = TRUE; + end_find = TRUE; + } + break; + case find_nothing: + default: + end_find = TRUE; + break; + } + if (do_callback) + (findwhat->callback)(glyph, findwhat); + if (end_find) + break; + } + } + return 1; + } + return 0; +} + +/* + When we start to process a config file or a symbol file, + that might have G_ entries, generating all 9000+ glyphid + for comparison repeatedly each time we encounter a G_ + entry to decipher, then comparing against them, is obviously + extremely performance-poor. + + Setting aside the "comparison" part for now (that has to be + done in some manner), we can likely do something about the + repeated "generation" of the names for parsing prior to the + actual comparison part by generating them once, ahead of the + bulk of the potential parsings. We can later free up + all the memory those names consumed once the bulk parsing is + over with. +*/ + + +void fill_glyphid_cache(void) +{ + int reslt = 0; + + if (!glyphid_cache) { + init_glyph_cache(); + } + if (glyphid_cache) { + glyphcache_find = zero_find; + glyphcache_find.findtype = find_nothing; + glyphcache_find.reserved = (genericptr_t) glyphid_cache; + glyphcache_find.restype = res_fill_cache; + reslt = parse_id((char *) 0, &glyphcache_find); + if (!reslt) { + free_glyphid_cache(); + glyphid_cache = (struct glyphid_cache_t *) 0; + } + } +} + +/* + * The glyph ID cache is a simple double-hash table. + * The cache size is a power of two, and two hashes are derived from the + * cache ID. The first is a location in the table, and the second is an + * offset. On any collision, the second hash is added to the first until + * a match or an empty bucket is found. + * The second hash is an odd number, which is necessary and sufficient + * to traverse the entire table. + */ + +staticfn void +init_glyph_cache(void) +{ + size_t glyph; + + /* Cache size of power of 2 not less than 2*MAX_GLYPH */ + glyphid_cache_lsize = 0; + glyphid_cache_size = 1; + while (glyphid_cache_size < 2*MAX_GLYPH) { + ++glyphid_cache_lsize; + glyphid_cache_size <<= 1; + } + + glyphid_cache = (struct glyphid_cache_t *) alloc( + glyphid_cache_size * sizeof (struct glyphid_cache_t)); + for (glyph = 0; glyph < glyphid_cache_size; ++glyph) { + glyphid_cache[glyph].glyphnum = 0; + glyphid_cache[glyph].id = (char *) 0; + } +} + +void free_glyphid_cache(void) +{ + size_t idx; + + if (!glyphid_cache) + return; + for (idx = 0; idx < glyphid_cache_size; ++idx) { + if (glyphid_cache[idx].id) { + free(glyphid_cache[idx].id); + glyphid_cache[idx].id = (char *) 0; + } + } + free(glyphid_cache); + glyphid_cache = (struct glyphid_cache_t *) 0; +} + +staticfn void +add_glyph_to_cache(int glyphnum, const char *id) +{ + uint32 hash = glyph_hash(id); + size_t hash1 = (size_t) (hash & (glyphid_cache_size - 1)); + size_t hash2 = (size_t) + (((hash >> glyphid_cache_lsize) & (glyphid_cache_size - 1)) | 1); + size_t i = hash1; + + do { + if (glyphid_cache[i].id == NULL) { + /* Empty bucket found */ + glyphid_cache[i].id = dupstr(id); + glyphid_cache[i].glyphnum = glyphnum; + return; + } + /* For speed, assume that no ID occurs twice */ + i = (i + hash2) & (glyphid_cache_size - 1); + } while (i != hash1); + /* This should never happen */ + panic("glyphid_cache full"); +} + +staticfn int +find_glyph_in_cache(const char *id) +{ + uint32 hash = glyph_hash(id); + size_t hash1 = (size_t) (hash & (glyphid_cache_size - 1)); + size_t hash2 = (size_t) + (((hash >> glyphid_cache_lsize) & (glyphid_cache_size - 1)) | 1); + size_t i = hash1; + + do { + if (glyphid_cache[i].id == NULL) { + /* Empty bucket found */ + return -1; + } + if (strcmpi(id, glyphid_cache[i].id) == 0) { + /* Match found */ + return glyphid_cache[i].glyphnum; + } + i = (i + hash2) & (glyphid_cache_size - 1); + } while (i != hash1); + return -1; +} + +staticfn char * +find_glyphid_in_cache_by_glyphnum(int glyphnum) +{ + size_t idx; + + if (!glyphid_cache) + return (char *) 0; + for (idx = 0; idx < glyphid_cache_size; ++idx) { + if (glyphid_cache[idx].glyphnum == glyphnum + && glyphid_cache[idx].id != 0) { + /* Match found */ + return glyphid_cache[idx].id; + } + } + return (char *) 0; +} + +staticfn uint32 +glyph_hash(const char *id) +{ + uint32 hash = 0; + size_t i; + + for (i = 0; id[i] != '\0'; ++i) { + char ch = id[i]; + if ('A' <= ch && ch <= 'Z') { + ch += 'a' - 'A'; + } + hash = (hash << 1) | (hash >> 31); + hash ^= ch; + } + return hash; +} + +boolean +glyphid_cache_status(void) +{ + return (glyphid_cache != 0); +} + +int +match_glyph(char *buf) +{ + char workbuf[BUFSZ]; + + /* buf contains a G_ glyph reference, not an S_ symbol. + There could be an R-G-B color attached too. + Let's get a copy to work with. */ + Snprintf(workbuf, sizeof workbuf, "%s", buf); /* get a copy */ + return glyphrep(workbuf); +} + +int +glyphrep(const char *op) +{ + int reslt = 0, glyph = NO_GLYPH; + + 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; + return 0; +} + +int +add_custom_nhcolor_entry( + const char *customization_name, + int glyphidx, + uint32 nhcolor, + enum graphics_sets which_set) +{ + struct symset_customization *gdc + = &gs.sym_customizations[which_set][custom_nhcolor]; + struct customization_detail *details, *newdetails = 0; +#if 0 + static uint32 closecolor = 0; + static int clridx = 0; +#endif + + if (!gdc->details) { + gdc->customization_name = dupstr(customization_name); + gdc->custtype = custom_nhcolor; + gdc->details = 0; + gdc->details_end = 0; + } + details = find_matching_customization(customization_name, + custom_nhcolor, which_set); + if (details) { + while (details) { + if (details->content.ccolor.glyphidx == glyphidx) { + details->content.ccolor.nhcolor = nhcolor; + return 1; + } + details = details->next; + } + } + /* create new details entry */ + newdetails = (struct customization_detail *) alloc(sizeof *newdetails); + newdetails->content.urep.glyphidx = glyphidx; + newdetails->content.ccolor.nhcolor = nhcolor; + newdetails->next = (struct customization_detail *) 0; + if (gdc->details == NULL) { + gdc->details = newdetails; + } else { + gdc->details_end->next = newdetails; + } + gdc->details_end = newdetails; + gdc->count++; + return 1; +} + +void +apply_customizations( + enum graphics_sets which_set, + enum do_customizations docustomize) +{ + glyph_map *gmap; + struct customization_detail *details; + struct symset_customization *sc; + boolean at_least_one = FALSE, + do_colors = ((docustomize & do_custom_colors) != 0), + do_symbols = ((docustomize & do_custom_symbols) != 0); + int custs; + + for (custs = 0; custs < (int) custom_count; ++custs) { + sc = &gs.sym_customizations[which_set][custs]; + if (sc->count && sc->details) { + at_least_one = TRUE; + /* These glyph customizations get applied to the glyphmap array, + not to symset entries */ + details = sc->details; + while (details) { +#ifdef ENHANCED_SYMBOLS + if (iflags.customsymbols && do_symbols) { + if (sc->custtype == custom_ureps) { + gmap = &glyphmap[details->content.urep.glyphidx]; + if (gs.symset[which_set].handling == H_UTF8) + (void) set_map_u(gmap, + details->content.urep.u.utf32ch, + details->content.urep.u.utf8str); + } + } +#endif + if (iflags.customcolors && do_colors) { + if (sc->custtype == custom_nhcolor) { + gmap = &glyphmap[details->content.ccolor.glyphidx]; + (void) set_map_customcolor(gmap, + details->content.ccolor.nhcolor); + } + } + details = details->next; + } + } + } + if (at_least_one) { + shuffle_customizations(); + } +} + +/* Shuffle the customizations to match shuffled object descriptions, + * so a red potion isn't displayed with a blue customization, and so on. + */ + +#if 0 +staticfn void +shuffle_customizations(void) +{ + static const int offsets[2] = { GLYPH_OBJ_OFF, GLYPH_OBJ_PILETOP_OFF }; + int j; + + for (j = 0; j < SIZE(offsets); j++) { + glyph_map *obj_glyphs = glyphmap + offsets[j]; + int i; + struct unicode_representation *tmp_u[NUM_OBJECTS]; + int duplicate[NUM_OBJECTS]; + + for (i = 0; i < NUM_OBJECTS; i++) { + duplicate[i] = -1; + tmp_u[i] = (struct unicode_representation *) 0; + } + for (i = 0; i < NUM_OBJECTS; i++) { + int idx = objects[i].oc_descr_idx; + + /* + * Shuffling gem appearances can cause the same oc_descr_idx to + * appear more than once. Detect this condition and ensure that + * each pointer points to a unique allocation. + */ + if (duplicate[idx] >= 0) { + /* Current structure already appears in tmp_u */ + struct unicode_representation *other = tmp_u[duplicate[idx]]; + + 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); + } + } else { + tmp_u[i] = obj_glyphs[idx].u; + if (obj_glyphs[idx].u != NULL) { + duplicate[idx] = i; + obj_glyphs[idx].u = NULL; + } + } + } + for (i = 0; i < NUM_OBJECTS; i++) { + /* Some glyphmaps may not have been transferred */ + if (obj_glyphs[i].u != NULL) { + free(obj_glyphs[i].u->utf8str); + free(obj_glyphs[i].u); + } + obj_glyphs[i].u = tmp_u[i]; + } + } +} + +#else +staticfn void +shuffle_customizations(void) +{ + static const int offsets[2] = { GLYPH_OBJ_OFF, GLYPH_OBJ_PILETOP_OFF }; + int j; + + for (j = 0; j < SIZE(offsets); j++) { + glyph_map *obj_glyphs = glyphmap + offsets[j]; + int i; +#ifdef ENHANCED_SYMBOLS + struct unicode_representation *tmp_u[NUM_OBJECTS]; +#endif + uint32 tmp_customcolor[NUM_OBJECTS]; + uint16 tmp_color256idx[NUM_OBJECTS]; + + int duplicate[NUM_OBJECTS]; + + for (i = 0; i < NUM_OBJECTS; i++) { + duplicate[i] = -1; +#ifdef ENHANCED_SYMBOLS + tmp_u[i] = (struct unicode_representation *) 0; +#endif + tmp_customcolor[i] = 0; + tmp_color256idx[i] = 0; + } + for (i = 0; i < NUM_OBJECTS; i++) { + int idx = objects[i].oc_descr_idx; + + /* + * Shuffling gem appearances can cause the same oc_descr_idx to + * appear more than once. Detect this condition and ensure that + * each pointer points to a unique allocation. + */ + if (duplicate[idx] >= 0) { +#ifdef ENHANCED_SYMBOLS + /* Current structure already appears in tmp_u */ + struct unicode_representation *other = tmp_u[duplicate[idx]]; +#endif + uint32 other_customcolor = tmp_customcolor[duplicate[idx]]; + uint16 other_color256idx = tmp_color256idx[duplicate[idx]]; + + tmp_customcolor[i] = other_customcolor; + tmp_color256idx[i] = other_color256idx; +#ifdef ENHANCED_SYMBOLS + 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 { + tmp_customcolor[i] = obj_glyphs[idx].customcolor; + tmp_color256idx[i] = obj_glyphs[idx].color256idx; +#ifdef ENHANCED_SYMBOLS + tmp_u[i] = obj_glyphs[idx].u; +#endif + if ( +#ifdef ENHANCED_SYMBOLS + obj_glyphs[idx].u != NULL || +#endif + obj_glyphs[idx].customcolor != 0) { + duplicate[idx] = i; +#ifdef ENHANCED_SYMBOLS + obj_glyphs[idx].u = NULL; +#endif + obj_glyphs[idx].customcolor = 0; + obj_glyphs[idx].color256idx = 0; + } + } + } + for (i = 0; i < NUM_OBJECTS; i++) { + /* Some glyphmaps may not have been transferred */ +#ifdef ENHANCED_SYMBOLS + if (obj_glyphs[i].u != NULL) { + free(obj_glyphs[i].u->utf8str); + free(obj_glyphs[i].u); + } + obj_glyphs[i].u = tmp_u[i]; +#endif + obj_glyphs[i].customcolor = tmp_customcolor[i]; + obj_glyphs[i].color256idx = tmp_color256idx[i]; + } + } +} +#endif + +struct customization_detail * +find_matching_customization( + const char *customization_name, + enum customization_types custtype, + enum graphics_sets which_set) +{ + struct symset_customization *gdc + = &gs.sym_customizations[which_set][custtype]; + + if ((gdc->custtype == custtype) && gdc->customization_name + && (strcmp(customization_name, gdc->customization_name) == 0)) + return gdc->details; + return (struct customization_detail *) 0; +} + +void +purge_all_custom_entries(void) +{ + int i; + + for (i = 0; i < NUM_GRAPHICS + 1; ++i) { + purge_custom_entries(i); + } +} + +void +purge_custom_entries(enum graphics_sets which_set) +{ + enum customization_types custtype; + struct symset_customization *gdc; + struct customization_detail *details, *next; + + for (custtype = custom_none; custtype < custom_count; ++custtype) { + gdc = &gs.sym_customizations[which_set][custtype]; + details = gdc->details; + while (details) { + next = details->next; + if (gdc->custtype == custom_ureps) { + if (details->content.urep.u.utf8str) + free(details->content.urep.u.utf8str); + details->content.urep.u.utf8str = (uint8 *) 0; + } else if (gdc->custtype == custom_symbols) { + details->content.sym.symparse = (struct symparse *) 0; + details->content.sym.val = 0; + } else if (gdc->custtype == custom_nhcolor) { + details->content.ccolor.nhcolor = 0; + details->content.ccolor.glyphidx = 0; + } + free(details); + details = next; + } + gdc->details = 0; + gdc->details_end = 0; + if (gdc->customization_name) { + free((genericptr_t) gdc->customization_name); + gdc->customization_name = 0; + } + gdc->count = 0; + } +} +void +dump_all_glyphids(FILE *fp) +{ + struct find_struct dump_glyphid_find = zero_find; + + dump_glyphid_find.findtype = find_nothing; + dump_glyphid_find.reserved = (genericptr_t) fp; + dump_glyphid_find.restype = res_dump_glyphids; + (void) parse_id((char *) 0, &dump_glyphid_find); +} + +void +wizcustom_glyphids(winid win) +{ + int glyphnum; + char *id; + + if (!glyphid_cache) + return; + for (glyphnum = 0; glyphnum < MAX_GLYPH; ++glyphnum) { + id = find_glyphid_in_cache_by_glyphnum(glyphnum); + if (id) { + wizcustom_callback(win, glyphnum, id); + } + } + } + +staticfn int +parse_id( + const char *id, + struct find_struct *findwhat) +{ + FILE *fp = (FILE *) 0; + int i = 0, j, mnum, glyph, + pm_offset = 0, oc_offset = 0, cmap_offset = 0, + pm_count = 0, oc_count = 0, cmap_count = 0; + boolean skip_base = FALSE, skip_this_one, dump_ids = FALSE, + filling_cache = FALSE, is_S = FALSE, is_G = FALSE; + char buf[4][QBUFSZ]; + + if (findwhat->findtype == find_nothing && findwhat->restype) { + if (findwhat->restype == res_dump_glyphids) { + if (findwhat->reserved) { + fp = (FILE *) findwhat->reserved; + dump_ids = TRUE; + } else { + return 0; + } + } + if (findwhat->restype == res_fill_cache) { + if (findwhat->reserved + && findwhat->reserved == (genericptr_t) glyphid_cache) { + filling_cache = TRUE; + } else { + return 0; + } + } + } + + is_G = (id && id[0] == 'G' && id[1] == '_'); + is_S = (id && id[0] == 'S' && id[1] == '_'); + + if ((is_G && !glyphid_cache) || filling_cache || dump_ids || is_S) { + while (loadsyms[i].range) { + if (!pm_offset && loadsyms[i].range == SYM_MON) + pm_offset = i; + if (!pm_count && pm_offset && loadsyms[i].range != SYM_MON) + pm_count = i - pm_offset; + if (!oc_offset && loadsyms[i].range == SYM_OC) + oc_offset = i; + if (!oc_count && oc_offset && loadsyms[i].range != SYM_OC) + oc_count = i - oc_offset; + if (!cmap_offset && loadsyms[i].range == SYM_PCHAR) + cmap_offset = i; + if (!cmap_count && cmap_offset && loadsyms[i].range != SYM_PCHAR) + cmap_count = i - cmap_offset; + i++; + } + } + if (is_G || filling_cache || dump_ids) { + if (!filling_cache && id && glyphid_cache) { + int val = find_glyph_in_cache(id); + if (val >= 0) { + findwhat->findtype = find_glyph; + findwhat->val = val; + findwhat->loadsyms_offset = 0; + return 1; + } else { + return 0; + } + } else { + const char *buf2, *buf3, *buf4; + + /* individual matching glyph entries */ + for (glyph = 0; glyph < MAX_GLYPH; ++glyph) { + skip_base = FALSE; + skip_this_one = FALSE; + buf[0][0] = buf[1][0] = buf[2][0] = buf[3][0] = '\0'; + if (glyph_is_monster(glyph)) { + /* buf2 will hold the distinguishing prefix */ + /* buf3 will hold the base name */ + buf2 = ""; + buf3 = monsdump[glyph_to_mon(glyph)].nm; + + if (glyph_is_normal_male_monster(glyph)) { + buf2 = "male_"; + } else if (glyph_is_normal_female_monster(glyph)) { + buf2 = "female_"; + } else if (glyph_is_ridden_male_monster(glyph)) { + buf2 = "ridden_male_"; + } else if (glyph_is_ridden_female_monster(glyph)) { + buf2 = "ridden_female_"; + } else if (glyph_is_detected_male_monster(glyph)) { + buf2 = "detected_male_"; + } else if (glyph_is_detected_female_monster(glyph)) { + buf2 = "detected_female_"; + } else if (glyph_is_male_pet(glyph)) { + buf2 = "pet_male_"; + } else if (glyph_is_female_pet(glyph)) { + buf2 = "pet_female_"; + } + Strcpy(buf[0], "G_"); + Strcat(buf[0], buf2); + Strcat(buf[0], buf3); + } else if (glyph_is_body(glyph)) { + /* buf2 will hold the distinguishing prefix */ + /* buf3 will hold the base name */ + buf2 = glyph_is_body_piletop(glyph) + ? "piletop_body_" + : "body_"; + buf3 = monsdump[glyph_to_body_corpsenm(glyph)].nm; + 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 = 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; + Strcpy(buf[0], "G_"); + Strcat(buf[0], buf2); + Strcat(buf[0], buf3); + } else if (glyph_is_object(glyph)) { + i = glyph_to_obj(glyph); + /* buf2 will hold the distinguishing prefix */ + /* buf3 will hold the base name */ + if (((i > SCR_STINKING_CLOUD) && (i < SCR_MAIL)) + || ((i > WAN_LIGHTNING) && (i < GOLD_PIECE))) + skip_this_one = TRUE; + if (!skip_this_one) { + if ((i >= WAN_LIGHT) && (i <= WAN_LIGHTNING)) + buf2 = "wand of "; + else if ((i >= SPE_DIG) && (i < SPE_BLANK_PAPER)) + buf2 = "spellbook of "; + else if ((i >= SCR_ENCHANT_ARMOR) + && (i <= SCR_STINKING_CLOUD)) + buf2 = "scroll of "; + else if ((i >= POT_GAIN_ABILITY) && (i <= POT_WATER)) + buf2 = (i == POT_WATER) ? "flask of n" + : "potion of "; + else if ((i >= RIN_ADORNMENT) + && (i <= RIN_PROTECTION_FROM_SHAPE_CHAN)) + 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" + : obj_descr[i].oc_name + ? obj_descr[i].oc_name + : obj_descr[i].oc_descr; + Strcpy(buf[0], "G_"); + if (glyph_is_normal_piletop_obj(glyph)) + Strcat(buf[0], "piletop_"); + Strcat(buf[0], buf2); + Strcat(buf[0], buf3); + } + } else if (glyph_is_cmap(glyph) || glyph_is_cmap_zap(glyph) + || glyph_is_swallow(glyph) + || glyph_is_explosion(glyph)) { + int cmap = -1; + + /* buf2 will hold the distinguishing prefix */ + /* buf3 will hold the base name */ + /* buf4 will hold the distinguishing suffix */ + buf2 = ""; + buf3 = ""; + buf4 = ""; + if (glyph == GLYPH_CMAP_OFF) { + cmap = S_stone; + buf3 = "stone substrate"; + skip_base = TRUE; + } else if (glyph_is_cmap_gehennom(glyph)) { + cmap = (glyph - GLYPH_CMAP_GEH_OFF) + S_vwall; + buf4 = "_gehennom"; + } else if (glyph_is_cmap_knox(glyph)) { + cmap = (glyph - GLYPH_CMAP_KNOX_OFF) + S_vwall; + buf4 = "_knox"; + } else if (glyph_is_cmap_main(glyph)) { + cmap = (glyph - GLYPH_CMAP_MAIN_OFF) + S_vwall; + buf4 = "_main"; + } else if (glyph_is_cmap_mines(glyph)) { + cmap = (glyph - GLYPH_CMAP_MINES_OFF) + S_vwall; + buf4 = "_mines"; + } else if (glyph_is_cmap_sokoban(glyph)) { + cmap = (glyph - GLYPH_CMAP_SOKO_OFF) + S_vwall; + buf4 = "_sokoban"; + } else if (glyph_is_cmap_a(glyph)) { + cmap = (glyph - GLYPH_CMAP_A_OFF) + S_ndoor; + } else if (glyph_is_cmap_altar(glyph)) { + static const char *const altar_text[] = { + "unaligned", "chaotic", "neutral", + "lawful", "other", + }; + + j = (glyph - GLYPH_ALTAR_OFF); + cmap = S_altar; + if (j != altar_other) { + Snprintf(buf[2], sizeof buf[2], "%s_", + altar_text[j]); + buf2 = buf[2]; + } else { + buf3 = "altar other"; + skip_base = TRUE; + } + } else if (glyph_is_cmap_b(glyph)) { + cmap = (glyph - GLYPH_CMAP_B_OFF) + S_grave; + } else if (glyph_is_cmap_zap(glyph)) { + static const char *const zap_texts[] = { + "missile", "fire", "frost", "sleep", + "death", "lightning", "poison gas", "acid" + }; + + j = (glyph - GLYPH_ZAP_OFF); + cmap = (j % 4) + S_vbeam; + Snprintf(buf[2], sizeof buf[2], "%s", + loadsyms[cmap + cmap_offset].name + 2); + Snprintf(buf[3], sizeof buf[3], "%s zap %s", + zap_texts[j / 4], fix_glyphname(buf[2])); + buf3 = buf[3]; + buf2 = ""; + skip_base = TRUE; + } else if (glyph_is_cmap_c(glyph)) { + cmap = (glyph - GLYPH_CMAP_C_OFF) + S_digbeam; + } else if (glyph_is_swallow(glyph)) { + static const char *const swallow_texts[] = { + "top left", "top center", "top right", + "middle left", "middle right", "bottom left", + "bottom center", "bottom right", + }; + + j = glyph - GLYPH_SWALLOW_OFF; + cmap = glyph_to_swallow(glyph); + mnum = j / ((S_sw_br - S_sw_tl) + 1); + Strcpy(buf[3], "swallow "); + Strcat(buf[3], monsdump[mnum].nm); + Strcat(buf[3], " "); + Strcat(buf[3], swallow_texts[cmap]); + buf3 = buf[3]; + skip_base = TRUE; + } else if (glyph_is_explosion(glyph)) { + static const char *const expl_type_texts[] = { + "dark", "noxious", "muddy", "wet", + "magical", "fiery", "frosty", + }; + static const char *const expl_texts[] = { + "tl", "tc", "tr", "ml", "mc", + "mr", "bl", "bc", "br", + }; + int expl; + + j = glyph - GLYPH_EXPLODE_OFF; + expl = j / ((S_expl_br - S_expl_tl) + 1); + cmap = glyph_to_explosion(glyph) + S_expl_tl; + i = cmap - S_expl_tl; + Snprintf(buf[2], sizeof buf[2], "%s ", + expl_type_texts[expl]); + buf2 = buf[2]; + Snprintf(buf[3], sizeof buf[3], "%s%s", "expl_", + expl_texts[i]); + buf3 = buf[3]; + skip_base = TRUE; + } + if (!skip_base) { + if (cmap >= 0 && cmap < MAXPCHARS) { + buf3 = loadsyms[cmap + cmap_offset].name + 2; + } + } + Strcpy(buf[0], "G_"); + Strcat(buf[0], buf2); + Strcat(buf[0], buf3); + Strcat(buf[0], buf4); + } else if (glyph_is_invisible(glyph)) { + Strcpy(buf[0], "G_invisible"); + } else if (glyph_is_nothing(glyph)) { + Strcpy(buf[0], "G_nothing"); + } else if (glyph_is_unexplored(glyph)) { + Strcpy(buf[0], "G_unexplored"); + } else if (glyph_is_warning(glyph)) { + j = glyph - GLYPH_WARNING_OFF; + Snprintf(buf[0], sizeof buf[0], "G_%s%d", "warning", j); + } + if (memchr(buf[0], '\0', sizeof buf[0]) == NULL) + panic("parse_id: buf[0] overflowed"); + if (!skip_this_one) { + fix_glyphname(buf[0]+2); + if (dump_ids) { + Fprintf(fp, "(%04d) %s\n", glyph, buf[0]); + } else if (filling_cache) { + add_glyph_to_cache(glyph, buf[0]); + } else if (id) { + if (!strcmpi(id, buf[0])) { + findwhat->findtype = find_glyph; + findwhat->val = glyph; + findwhat->loadsyms_offset = 0; + return 1; + } + } + } + } + } /* not glyphid_cache */ + } else if (is_S) { + /* cmap entries */ + for (i = 0; i < cmap_count; ++i) { + if (!strcmpi(loadsyms[i + cmap_offset].name + 2, id + 2)) { + findwhat->findtype = find_cmap; + findwhat->val = i; + findwhat->loadsyms_offset = i + cmap_offset; + return 1; + } + } + /* objclass entries */ + for (i = 0; i < oc_count; ++i) { + if (!strcmpi(loadsyms[i + oc_offset].name + 2, id + 2)) { + findwhat->findtype = find_oc; + findwhat->val = i; + findwhat->loadsyms_offset = i + oc_offset; + return 1; + } + } + /* permonst entries */ + for (i = 0; i <= pm_count; ++i) { + if (!strcmpi(loadsyms[i + pm_offset].name + 2, id + 2)) { + findwhat->findtype = find_pm; + findwhat->val = i + 1; /* starts at 1 */ + findwhat->loadsyms_offset = i + pm_offset; + return 1; + } + } + } + if (dump_ids || filling_cache) + return 1; + findwhat->findtype = find_nothing; + findwhat->val = 0; + findwhat->loadsyms_offset = 0; + return 0; +} + +/* extern glyph_map glyphmap[MAX_GLYPH]; */ + +void +clear_all_glyphmap_colors(void) +{ + int glyph; + + for (glyph = 0; glyph < MAX_GLYPH; ++glyph) { + if (glyphmap[glyph].customcolor) + glyphmap[glyph].customcolor = 0; + glyphmap[glyph].color256idx = 0; + } +} + +void reset_customcolors(void) +{ + clear_all_glyphmap_colors(); + apply_customizations(gc.currentgraphics, do_custom_colors); +} + +/* not used yet */ + +#if 0 +staticfn struct customization_detail *find_display_sym_customization( + const char *customization_name, const struct symparse *symparse, + enum graphics_sets which_set); +staticfn struct customization_detail *find_display_urep_customization( + const char *customization_name, int glyphidx, + enum graphics_sets which_set); + +struct customization_detail * +find_display_sym_customization( + const char *customization_name, + const struct symparse *symparse, + enum graphics_sets which_set) +{ + struct symset_customization *gdc; + struct customization_detail *symdetails; + + gdc = &gs.sym_customizations[which_set][custom_symbols]; + if ((gdc->custtype == custom_symbols) + && (strcmp(customization_name, gdc->customization_name) == 0)) { + symdetails = gdc->details; + while (symdetails) { + if (symdetails->content.sym.symparse == symparse) + return symdetails; + symdetails = symdetails->next; + } + } + return (struct customization_detail *) 0; +} + +struct customization_detail * +find_display_urep_customization( + const char *customization_name, + int glyphidx, + enum graphics_sets which_set) +{ + struct symset_customization *gdc = &gs.sym_customizations[which_set]; + struct customization_detail *urepdetails; + + if ((gdc->custtype == custom_reps) + || (strcmp(customization_name, gdc->customization_name) == 0)) { + urepdetails = gdc->details; + while (urepdetails) { + if (urepdetails->content.urep.glyphidx == glyphidx) + return urepdetails; + urepdetails = urepdetails->next; + } + } + return (struct customization_detail *) 0; +} +#endif /* 0 not used yet */ + +#ifdef TEST_GLYPHNAMES + +static struct { + int idx; + const char *nm1; + const char *nm2; +} cmapname[MAXPCHARS] = { +#define PCHAR_TILES +#include "defsym.h" +#undef PCHAR_TILES +}; + +void +test_glyphnames(void) +{ + int reslt; + + reslt = find_glyphs("G_potion_of_monster_detection"); + reslt = find_glyphs("G_piletop_body_chickatrice"); + reslt = find_glyphs("G_detected_male_homunculus"); + reslt = find_glyphs("S_pool"); + reslt = find_glyphs("S_dog"); + reslt = glyphs_to_unicode("S_dog", "U+130E6", 0L); +} + +staticfn void +just_find_callback(int glyph UNUSED, struct find_struct *findwhat UNUSED) +{ + return; +} + +staticfn int +find_glyphs(const char *id) +{ + struct find_struct find_only = zero_find; + + find_only.unicode_val = 0; + find_only.callback = just_find_callback; + return glyph_find_core(id, &find_only); +} + +staticfn void +to_unicode_callback(int glyph UNUSED, struct find_struct *findwhat) +{ + int uval; +#ifdef NO_PARSING_SYMSET + glyph_map *gm = &glyphmap[glyph]; +#endif + uint8 utf8str[6]; + + if (!findwhat->unicode_val) + return; + uval = unicode_val(findwhat->unicode_val); + if (unicodeval_to_utf8str(uval, utf8str, sizeof utf8str)) { +#ifdef NO_PARSING_SYMSET + set_map_u(gm, uval, utf8str, + (findwhat->color != 0L) ? findwhat->color : 0L); +#else + +#endif + } +} + +int +glyphs_to_unicode(const char *id, const char *unicode_val, long clr) +{ + struct find_struct to_unicode = zero_find; + + to_unicode.unicode_val = unicode_val; + to_unicode.callback = to_unicode_callback; + /* if the color 0 is an actual color, as opposed to just "not set" + we set a marker bit outside the 24-bit range to indicate a + valid color value 0. That allows valid color 0, but allows a + simple checking for 0 to detect "not set". The window port that + implements the color switch, needs to either check that bit + or appropriately mask colors with 0xFFFFFF. */ + to_unicode.color = (clr == -1) ? 0L : (clr == 0L) ? nonzero_black : clr; + return glyph_find_core(id, &to_unicode); +} + +#endif /* SOME TEST STUFF */ + +/* glyphs.c */ + + + diff --git a/src/hack.c b/src/hack.c index 335f374d24..8a6a7603fe 100644 --- a/src/hack.c +++ b/src/hack.c @@ -1,47 +1,58 @@ -/* NetHack 3.7 hack.c $NHDT-Date: 1655116515 2022/06/13 10:35:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.360 $ */ +/* 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. */ #include "hack.h" +#include "extern.h" /* #define DEBUG */ /* uncomment for debugging */ -static boolean could_move_onto_boulder(coordxy, coordxy); -static int moverock(void); -static void dosinkfall(void); -static boolean findtravelpath(int); -static boolean trapmove(coordxy, coordxy, struct trap *); -static void check_buried_zombies(coordxy, coordxy); -static schar u_simple_floortyp(coordxy, coordxy); -static boolean trap_move_danger(coordxy, coordxy); -static boolean swim_move_danger(coordxy, coordxy); -static boolean air_move_danger(coordxy, coordxy); -static boolean domove_bump_mon(struct monst *, int); -static boolean domove_attackmon_at(struct monst *, coordxy, coordxy, - boolean *); -static boolean domove_fight_ironbars(coordxy, coordxy); -static boolean domove_fight_web(coordxy, coordxy); -static boolean domove_swap_with_pet(struct monst *, coordxy, coordxy); -static boolean domove_fight_empty(coordxy, coordxy); -static boolean air_turbulence(void); -static void slippery_ice_fumbling(void); -static boolean impaired_movement(coordxy *, coordxy *); -static boolean avoid_moving_on_trap(coordxy, coordxy, boolean); -static boolean avoid_moving_on_liquid(coordxy, coordxy, boolean); -static boolean avoid_running_into_trap_or_liquid(coordxy, coordxy); -static boolean move_out_of_bounds(coordxy, coordxy); -static boolean carrying_too_much(void); -static boolean escape_from_sticky_mon(coordxy, coordxy); -static void domove_core(void); -static void maybe_smudge_engr(coordxy, coordxy, coordxy, coordxy); -static struct monst *monstinroom(struct permonst *, int); -static void move_update(boolean); -static int pickup_checks(void); -static void maybe_wail(void); -static boolean summon_thronerm_dlord(int); - -#define IS_SHOP(x) (gr.rooms[x].rtype >= SHOPBASE) +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); +staticfn void dosinkfall(void); +staticfn boolean findtravelpath(int); +staticfn boolean trapmove(coordxy, coordxy, struct trap *); +staticfn int QSORTCALLBACK notice_mons_cmp(const genericptr, + const genericptr) NONNULLPTRS; +staticfn schar u_simple_floortyp(coordxy, coordxy); +staticfn boolean swim_move_danger(coordxy, coordxy); +staticfn boolean air_move_danger(coordxy, coordxy); +staticfn boolean domove_bump_mon(struct monst *, int) NONNULLARG1; +staticfn boolean domove_attackmon_at(struct monst *, coordxy, coordxy, + boolean *) NONNULLPTRS; +staticfn boolean domove_fight_ironbars(coordxy, coordxy); +staticfn boolean domove_fight_web(coordxy, coordxy); +staticfn boolean domove_swap_with_pet(struct monst *, + coordxy, coordxy) NONNULLARG1; +staticfn boolean domove_fight_empty(coordxy, coordxy); +staticfn boolean air_turbulence(void); +staticfn void slippery_ice_fumbling(void); +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 move_out_of_bounds(coordxy, coordxy); +staticfn boolean carrying_too_much(void); +staticfn boolean escape_from_sticky_mon(coordxy, coordxy); +staticfn void domove_core(void); +staticfn void maybe_smudge_engr(coordxy, coordxy, coordxy, coordxy); +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 void maybe_wail(void); +staticfn boolean water_turbulence(coordxy *, coordxy *); +staticfn boolean summon_thronerm_dlord(int); + +#define IS_SHOP(x) (svr.rooms[x].rtype >= SHOPBASE) /* XXX: if more sources of water walking than just boots are added, cause_known(insight.c) should be externified and used for this */ @@ -93,12 +104,12 @@ obj_to_any(struct obj *obj) boolean revive_nasty(coordxy x, coordxy y, const char *msg) { - register struct obj *otmp, *otmp2; + struct obj *otmp, *otmp2; struct monst *mtmp; coord cc; boolean revived = FALSE; - for (otmp = gl.level.objects[x][y]; otmp; otmp = otmp2) { + for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp2) { otmp2 = otmp->nexthere; if (otmp->otyp == CORPSE && (is_rider(&mons[otmp->corpsenm]) @@ -129,7 +140,7 @@ revive_nasty(coordxy x, coordxy y, const char *msg) /* can hero move onto a spot containing one or more boulders? used for m

and travel and during boulder push failure */ -static boolean +staticfn boolean could_move_onto_boulder(coordxy sx, coordxy sy) { /* can if able to phaze through rock (must be poly'd, so not riding) */ @@ -141,8 +152,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; @@ -150,26 +161,204 @@ could_move_onto_boulder(coordxy sx, coordxy sy) return squeezeablylightinvent(); } -static int +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) +{ + const char *what; + + what = the(xname(otmp)); + if (u.usteed) + pline("%s tries to move %s, but cannot.", + YMonnam(u.usteed), what); + else + You("try to move %s, but in vain.", what); + if (Blind) + feel_location(sx, sy); +} + +staticfn int +cannot_push(struct obj *otmp, coordxy sx, coordxy sy) +{ + if (throws_rocks(gy.youmonst.data)) { + boolean + canpickup = (!Sokoban + /* similar exception as in can_lift(): + when poly'd into a giant, you can + pick up a boulder if you have a free + slot or into the overflow ('#') slot + unless already carrying at least one */ + && (inv_cnt(FALSE) < invlet_basic + || !carrying(BOULDER))), + willpickup = (canpickup + && (flags.pickup && !svc.context.nopick) + && autopick_testobj(otmp, TRUE)); + + if (u.usteed && P_SKILL(P_RIDING) < P_BASIC) { + You("aren't skilled enough to %s %s from %s.", + willpickup ? "pick up" : "push aside", + the(xname(otmp)), y_monnam(u.usteed)); + } else { + /* + * will pick up: you easily pick it up + * can but won't: you maneuver over it and could pick it up + * can't pick up: you maneuver over it (possibly followed + * by feedback from failed auto-pickup attempt) + */ + pline("However, you %s%s.", + willpickup ? "easily pick it up" + : "maneuver over it", + (canpickup && !willpickup) + ? " and could pick it up" + : ""); + /* similar to dropping everything and squeezing onto + a Sokoban boulder's spot, moving to same breaks the + Sokoban rules because on next step you could go + past it without pushing it to plug a pit or hole */ + sokoban_guilt(); + } + return 0; + } + + if (could_move_onto_boulder(sx, sy)) { + pline( + "However, you can squeeze yourself into a small opening."); + sokoban_guilt(); + return 0; + } else { + return -1; + } +} + +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) +{ + struct obj *otmp; + for (otmp = svl.level.objects[sx][sy]; otmp; otmp = otmp->nexthere) + if (otmp->otyp == BOULDER) + otmp->next_boulder = 0; /* resume normal xname() for this obj */ +} + +staticfn int moverock(void) { - register coordxy rx, ry, sx, sy; + coordxy sx, sy; + int ret; + + sx = u.ux + u.dx, sy = u.uy + u.dy; /* boulder starting position */ + ret = moverock_core(sx, sy); + moverock_done(sx, sy); + return ret; +} + +staticfn int +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; - int res = 0; - sx = u.ux + u.dx, sy = u.uy + u.dy; /* boulder starting position */ while ((otmp = sobj_at(BOULDER, sx, sy)) != 0) { if (Blind && glyph_to_obj(glyph_at(sx, sy)) != BOULDER) { pline("That feels like a boulder."); map_object(otmp, TRUE); nomul(0); - res = -1; - goto moverock_done; + return -1; } /* when otmp->next_boulder is 1, xname() will format it as @@ -182,7 +371,7 @@ moverock(void) firstboulder = FALSE; /* make sure that this boulder is visible as the top object */ - if (otmp != gl.level.objects[sx][sy]) + if (otmp != svl.level.objects[sx][sy]) movobj(otmp, sx, sy); rx = u.ux + 2 * u.dx; /* boulder destination position */ @@ -193,8 +382,9 @@ moverock(void) poly'd into a giant or squeezes under/beside it if small/light enough but is a no-op in other circumstances unless move attempt reveals an unseen boulder or lack of remembered, unseen monster */ - if (gc.context.nopick) { + if (svc.context.nopick) { int oldglyph = glyph_at(sx, sy); /* before feel_location() */ + int res; feel_location(sx, sy); /* same for all 3 if/else-if/else cases */ if (throws_rocks(gy.youmonst.data)) { @@ -216,10 +406,10 @@ moverock(void) /* use a move if hero learns something; see test_move() for how/why 'context.door_opened' is being dragged into this */ if (glyph_at(sx, sy) != oldglyph) - gc.context.door_opened = gc.context.move = TRUE; + svc.context.door_opened = svc.context.move = TRUE; res = -1; /* don't move to , so no soko guilt */ } - goto moverock_done; /* stop further push attempts */ + return res; } if (Levitation || Is_airlevel(&u.uz)) { /* FIXME? behavior in an air bubble on the water level should @@ -230,16 +420,15 @@ moverock(void) feel_location(sx, sy); You("don't have enough leverage to push %s.", the(xname(otmp))); /* Give them a chance to climb over it? */ - res = -1; - goto moverock_done; + return -1; } if (verysmall(gy.youmonst.data) && !u.usteed) { if (Blind) feel_location(sx, sy); pline("You're too small to push that %s.", xname(otmp)); - goto cannot_push; + 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)) { @@ -254,13 +443,12 @@ moverock(void) feel_location(sx, sy); pline("%s won't roll diagonally on this %s.", The(xname(otmp)), surface(sx, sy)); - goto cannot_push; + return cannot_push(otmp, sx, sy); } if (revive_nasty(rx, ry, "You sense movement on the other side.")) { - res = -1; - goto moverock_done; + return -1; } if (mtmp && !noncorporeal(mtmp->data) @@ -279,7 +467,7 @@ moverock(void) deliver_part1 = TRUE; map_invisible(rx, ry); } - if (Verbose(1, moverock)) { + if (flags.verbose) { char you_or_steed[BUFSZ]; Strcpy(you_or_steed, @@ -290,9 +478,20 @@ moverock(void) : upstart(you_or_steed), deliver_part1 ? "it" : the(xname(otmp))); } - goto cannot_push; + return cannot_push(otmp, sx, sy); + } + + if (closed_door(rx, ry)) { + cannot_push_msg(otmp, sx, sy); + return cannot_push(otmp, sx, sy); } + /* at this point the boulder should be able to move (though + potentially into something like a trap, pool, or lava) */ + + /* rumbling disturbs buried zombies */ + disturb_buried_zombies(sx, sy); + if (ttmp) { int newlev = 0; /* lint suppression */ d_level dest; @@ -324,8 +523,7 @@ moverock(void) fill_pit(u.ux, u.uy); if (cansee(rx, ry)) newsym(rx, ry); - res = sobj_at(BOULDER, sx, sy) ? -1 : 0; - goto moverock_done; + return sobj_at(BOULDER, sx, sy) ? -1 : 0; } break; case SPIKED_PIT: @@ -341,8 +539,7 @@ moverock(void) } if (mtmp && !Blind) newsym(rx, ry); - res = sobj_at(BOULDER, sx, sy) ? -1 : 0; - goto moverock_done; + return sobj_at(BOULDER, sx, sy) ? -1 : 0; case HOLE: case TRAPDOOR: Soundeffect(se_kerplunk_boulder_gone, 40); @@ -365,23 +562,20 @@ moverock(void) levl[rx][ry].candig = 1; if (cansee(rx, ry)) newsym(rx, ry); - res = sobj_at(BOULDER, sx, sy) ? -1 : 0; - goto moverock_done; + return sobj_at(BOULDER, sx, sy) ? -1 : 0; case LEVEL_TELEP: /* 20% chance of picking current level; 100% chance for 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!", - upstart(y_monnam(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); @@ -396,15 +590,12 @@ moverock(void) otmp->migrateflags = (long) MIGR_RANDOM; } seetrap(ttmp); - res = sobj_at(BOULDER, sx, sy) ? -1 : 0; - goto moverock_done; + return sobj_at(BOULDER, sx, sy) ? -1 : 0; default: break; /* boulder not affected by this trap */ } } - if (closed_door(rx, ry)) - goto nopushmsg; if (boulder_hits_pool(otmp, rx, ry, TRUE)) continue; @@ -416,135 +607,13 @@ moverock(void) 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 = gm.moves + 1L; - gb.bldrpush_oid = otmp->o_id; - } - givemesg = (gm.moves > gb.bldrpushtime + 2L - || gm.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.", - upstart(y_monnam(u.usteed)), what); - } - gb.bldrpushtime = gm.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 */ - if (costly && !costly_spot(rx, ry)) { - 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)) { - 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 { - nopushmsg: - what = the(xname(otmp)); - if (u.usteed) - pline("%s tries to move %s, but cannot.", - upstart(y_monnam(u.usteed)), what); - else - You("try to move %s, but in vain.", what); - if (Blind) - feel_location(sx, sy); - cannot_push: - if (throws_rocks(gy.youmonst.data)) { - boolean - canpickup = (!Sokoban - /* similar exception as in can_lift(): - when poly'd into a giant, you can - pick up a boulder if you have a free - slot or into the overflow ('#') slot - unless already carrying at least one */ - && (inv_cnt(FALSE) < 52 || !carrying(BOULDER))), - willpickup = (canpickup - && (flags.pickup && !gc.context.nopick) - && autopick_testobj(otmp, TRUE)); - - if (u.usteed && P_SKILL(P_RIDING) < P_BASIC) { - You("aren't skilled enough to %s %s from %s.", - willpickup ? "pick up" : "push aside", - the(xname(otmp)), y_monnam(u.usteed)); - } else { - /* - * will pick up: you easily pick it up - * can but won't: you maneuver over it and could pick it up - * can't pick up: you maneuver over it (possibly followed - * by feedback from failed auto-pickup attempt) - */ - pline("However, you %s%s.", - willpickup ? "easily pick it up" - : "maneuver over it", - (canpickup && !willpickup) - ? " and could pick it up" - : ""); - /* similar to dropping everything and squeezing onto - a Sokoban boulder's spot, moving to same breaks the - Sokoban rules because on next step you could go - past it without pushing it to plug a pit or hole */ - sokoban_guilt(); - break; - } - break; - } - - if (could_move_onto_boulder(sx, sy)) { - pline( - "However, you can squeeze yourself into a small opening."); - sokoban_guilt(); - break; - } else { - res = -1; - goto moverock_done; - } + cannot_push_msg(otmp, sx, sy); + return cannot_push(otmp, sx, sy); } } - res = 0; - - moverock_done: - for (otmp = gl.level.objects[sx][sy]; otmp; otmp = otmp->nexthere) - if (otmp->otyp == BOULDER) - otmp->next_boulder = 0; /* resume normal xname() for this obj */ - - return res; + return 0; } /* @@ -562,19 +631,17 @@ still_chewing(coordxy x, coordxy y) boolean nondig = (lev->wall_info & W_NONDIGGABLE); boolean u_cant_chew_metal = !metallivorous(gy.youmonst.data); - if (gc.context.digging.down) /* not continuing previous dig (w/ pick-axe) */ - (void) memset((genericptr_t) &gc.context.digging, 0, + if (svc.context.digging.down) /* not continuing prev dig (w/ pick-axe) */ + (void) memset((genericptr_t) &svc.context.digging, 0, sizeof (struct dig_info)); /* checks for terrain you can't chew through - * note: trees are covered in IS_ROCK, but that's really non-obvious so it's - * good to make it explicit * note: W_NONDIGGABLE and D_TRAPPED occupy same bit; thus, there can never * really be such thing as a nondiggable door. */ if (!boulder - && ((IS_ROCK(lev->typ) && nondig) - || (lev->typ == TREE && nondig) + && ((IS_OBSTRUCTED(lev->typ) && !may_dig(x, y)) || (IS_DOOR(lev->typ) && door_is_iron(lev) && u_cant_chew_metal) + /* may_dig() checks W_NONDIGGABLE but doesn't handle iron bars */ || (lev->typ == IRONBARS && (nondig || u_cant_chew_metal)))) { You("hurt your teeth on the %s.", (lev->typ == IRONBARS) @@ -592,19 +659,19 @@ still_chewing(coordxy x, coordxy y) You("are too full to eat the bars."); nomul(0); return 1; - } else if (!gc.context.digging.chew - || gc.context.digging.pos.x != x - || gc.context.digging.pos.y != y - || !on_level(&gc.context.digging.level, &u.uz)) { - gc.context.digging.down = FALSE; - gc.context.digging.chew = TRUE; - gc.context.digging.warned = FALSE; - gc.context.digging.pos.x = x; - gc.context.digging.pos.y = y; - assign_level(&gc.context.digging.level, &u.uz); + } else if (!svc.context.digging.chew + || svc.context.digging.pos.x != x + || svc.context.digging.pos.y != y + || !on_level(&svc.context.digging.level, &u.uz)) { + svc.context.digging.down = FALSE; + svc.context.digging.chew = TRUE; + svc.context.digging.warned = FALSE; + svc.context.digging.pos.x = x; + svc.context.digging.pos.y = y; + assign_level(&svc.context.digging.level, &u.uz); /* solid rock takes more work & time to dig through */ - gc.context.digging.effort = - (IS_ROCK(lev->typ) && !IS_TREE(lev->typ) ? 30 : 60) + u.udaminc; + svc.context.digging.effort = + (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" @@ -613,7 +680,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" @@ -622,20 +689,20 @@ still_chewing(coordxy x, coordxy y) predoortrapped(x, y, &gy.youmonst, FACE, D_BROKEN); watch_dig((struct monst *) 0, x, y, FALSE); return 1; - } else if ((gc.context.digging.effort += (30 + u.udaminc)) <= 100) { - if (Verbose(1, still_chewing)) + } else if ((svc.context.digging.effort += (30 + u.udaminc)) <= 100) { + if (flags.verbose) You("%s chewing on the %s.", - gc.context.digging.chew ? "continue" : "begin", + svc.context.digging.chew ? "continue" : "begin", boulder ? "boulder" : IS_TREE(lev->typ) ? "tree" - : IS_ROCK(lev->typ) + : IS_OBSTRUCTED(lev->typ) ? "rock" : (lev->typ == IRONBARS) ? "bars" : "door"); - gc.context.digging.chew = TRUE; + svc.context.digging.chew = TRUE; if (IS_DOOR(lev->typ)) predoortrapped(x, y, &gy.youmonst, FACE, D_BROKEN); watch_dig((struct monst *) 0, x, y, FALSE); @@ -648,7 +715,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); @@ -664,11 +731,11 @@ 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 */ - (void) memset((genericptr_t) &gc.context.digging, 0, + (void) memset((genericptr_t) &svc.context.digging, 0, sizeof (struct dig_info)); return 1; } @@ -679,9 +746,9 @@ still_chewing(coordxy x, coordxy y) dmgtxt = "damage"; } digtxt = "chew a hole in the wall."; - if (gl.level.flags.is_maze_lev) { + if (svl.level.flags.is_maze_lev) { lev->typ = ROOM; - } else if (gl.level.flags.is_cavernous_lev && !in_town(x, y)) { + } else if (svl.level.flags.is_cavernous_lev && !in_town(x, y)) { lev->typ = CORR; } else { lev->typ = DOOR; @@ -734,7 +801,7 @@ still_chewing(coordxy x, coordxy y) You1(digtxt); /* after newsym */ if (dmgtxt) pay_for_damage(dmgtxt, FALSE); - (void) memset((genericptr_t) &gc.context.digging, 0, + (void) memset((genericptr_t) &svc.context.digging, 0, sizeof (struct dig_info)); return 0; } @@ -750,7 +817,7 @@ movobj(struct obj *obj, coordxy ox, coordxy oy) newsym(ox, oy); } -static void +staticfn void dosinkfall(void) { static const char fell_on_sink[] = "fell onto a sink"; @@ -782,7 +849,7 @@ dosinkfall(void) losehp(Maybe_Half_Phys(dmg), fell_on_sink, NO_KILLER_PREFIX); exercise(A_DEX, FALSE); selftouch("Falling, you"); - for (obj = gl.level.objects[u.ux][u.uy]; obj; obj = obj->nexthere) + for (obj = svl.level.objects[u.ux][u.uy]; obj; obj = obj->nexthere) if (obj->oclass == WEAPON_CLASS || is_weptool(obj)) { You("fell on %s.", doname(obj)); losehp(Maybe_Half_Phys(rnd(3)), fell_on_sink, @@ -838,7 +905,7 @@ dosinkfall(void) /* intended to be called only on ROCKs or TREEs */ boolean -may_dig(register coordxy x, register coordxy y) +may_dig(coordxy x, coordxy y) { struct rm *lev = &levl[x][y]; @@ -847,7 +914,7 @@ may_dig(register coordxy x, register coordxy y) } boolean -may_passwall(register coordxy x, register coordxy y) +may_passwall(coordxy x, coordxy y) { return (boolean) (!(IS_STWALL(levl[x][y].typ) && (levl[x][y].wall_info & W_NONPASSWALL)) @@ -855,10 +922,10 @@ may_passwall(register coordxy x, register coordxy y) } boolean -bad_rock(struct permonst *mdat, register coordxy x, register coordxy y) +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)))); @@ -901,10 +968,10 @@ boolean invocation_pos(coordxy x, coordxy y) { return (boolean) (Invocation_lev(&u.uz) - && x == gi.inv_pos.x && y == gi.inv_pos.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( @@ -914,14 +981,20 @@ test_move( { coordxy x = ux + dx; coordxy y = uy + dy; - struct rm *tmpr = &levl[x][y]; + struct rm *tmpr; struct rm *ust; - gc.context.door_opened = FALSE; + 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)) { @@ -947,11 +1020,12 @@ test_move( You("cannot pass through the bars."); return FALSE; } - } else if (tunnels(gy.youmonst.data) && !needspick(gy.youmonst.data)) { + } else if (tunnels(gy.youmonst.data) + && !needspick(gy.youmonst.data)) { /* Eat the rock. */ if (mode == DO_MOVE && still_chewing(x, y)) return FALSE; - } else if (flags.autodig && !gc.context.run && !gc.context.nopick + } else if (flags.autodig && !svc.context.run && !svc.context.nopick && uwep && is_pick(uwep)) { /* MRKR: Automatic digging when wielding the appropriate tool */ if (mode == DO_MOVE) @@ -959,26 +1033,26 @@ test_move( return FALSE; } else { if (mode == DO_MOVE) { - if (is_db_wall(x, y)) + if (is_db_wall(x, y)) { pline("That drawbridge is up!"); - /* sokoban restriction stays even after puzzle is solved */ - else if (Passes_walls && !may_passwall(x, y) - && In_sokoban(&u.uz)) + } else if (Passes_walls && !may_passwall(x, y) + && In_sokoban(&u.uz)) { + /* soko restriction stays even after puzzle is solved */ pline_The("Sokoban walls resist your ability."); - else if (flags.mention_walls) { + } else if (flags.mention_walls) { char buf[BUFSZ]; - coord cc; - int sym = 0; - const char *firstmatch = 0; - - cc.x = x, cc.y = y; - do_screen_description(cc, TRUE, sym, buf, &firstmatch, - NULL); - if (!strcmp(firstmatch, "stone")) - Sprintf(buf, "solid stone"); + int glyph = back_to_glyph(x, y), + sym = glyph_is_cmap(glyph) ? glyph_to_cmap(glyph) + : -1; + + if (sym == S_stone) + Strcpy(buf, "solid stone"); + else if (sym >= 0) + Strcpy(buf, an(defsyms[sym].explanation)); else - Sprintf(buf, "%s", an(firstmatch)); - pline("It's %s.", buf); + Sprintf(buf, "impossible [background glyph=%d]", + glyph); + pline_dir(xytod(dx, dy), "It's %s.", buf); } } return FALSE; @@ -1016,13 +1090,23 @@ test_move( } else { if (mode == DO_MOVE) { if (amorphous(gy.youmonst.data)) - You( - "try to ooze under the door, but can't squeeze your possessions through."); - if (flags.autoopen && !gc.context.run + You("try to ooze under the door," + " but can't squeeze your possessions through."); + if (flags.autoopen && !svc.context.run && !Confusion && !Stunned && !Fumbling) { - gc.context.door_opened - = gc.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) { @@ -1038,7 +1122,7 @@ test_move( we haven't opened a door but we're going to return False and without having 'door_opened' set, 'move' would get reset by caller */ - gc.context.door_opened = gc.context.move = TRUE; + svc.context.door_opened = svc.context.move = TRUE; /* since we've just lied about successfully moving, we need to manually stop running */ nomul(0); @@ -1091,13 +1175,13 @@ test_move( } else if (dx && dy && worm_cross(ux, uy, x, y)) { /* consecutive long worm segments are at and */ if (mode == DO_MOVE) - pline("%s is in your way.", Monnam(m_at(ux, y))); + pline("%s is in your way.", YMonnam(m_at(ux, y))); return FALSE; } /* Pick travel path that does not require crossing a trap. * Avoid water and lava using the usual running rules. * (but not u.ux/u.uy because findtravelpath walks toward u.ux/u.uy) */ - if (gc.context.run == 8 && (mode != DO_MOVE) && !u_at(x, y)) { + if (svc.context.run == 8 && (mode != DO_MOVE) && !u_at(x, y)) { struct trap *t = t_at(x, y); if (t && t->tseen && t->ttyp != VIBRATING_SQUARE) @@ -1106,8 +1190,10 @@ test_move( /* FIXME: should be using lastseentyp[x][y] rather than seen vector */ if ((levl[x][y].seenv - && (is_pool_or_lava(x, y) || is_open_air(x, y))) /* known pool/lava */ - && ((IS_WATERWALL(levl[x][y].typ) || levl[x][y].typ == LAVAWALL) /* never enter wall of liquid */ + /* known pool/lava */ + && (is_pool_or_lava(x, y) || is_open_air(x, y))) + && ((IS_WATERWALL(levl[x][y].typ) /* never enter wall of liquid */ + || levl[x][y].typ == LAVAWALL) /* don't enter pool or lava (must be one of the two to get here) unless flying or levitating or have known water-walking for pool or known lava-walking and @@ -1136,10 +1222,10 @@ test_move( } if (sobj_at(BOULDER, x, y) && (Sokoban || !Passes_walls)) { - if (mode != TEST_TRAV && gc.context.run >= 2 + if (mode != TEST_TRAV && svc.context.run >= 2 && !(Blind || Hallucination) && !could_move_onto_boulder(x, y)) { if (mode == DO_MOVE && flags.mention_walls) - pline("A boulder blocks your path."); + pline_dir(xytod(dx,dy), "A boulder blocks your path."); return FALSE; } if (mode == DO_MOVE) { @@ -1148,7 +1234,7 @@ test_move( && !Sokoban) { if (still_chewing(x, y)) return FALSE; - } else if (gc.context.run || moverock() < 0) + } else if (svc.context.run || moverock() < 0) return FALSE; } else if (mode == TEST_TRAV) { struct obj *obj; @@ -1184,13 +1270,13 @@ test_move( * gt.travelmap keeps track of map locations we've moved through * this travel session. It will be cleared once the travel stops. */ -static boolean +staticfn boolean findtravelpath(int mode) { if (!gt.travelmap) gt.travelmap = selection_new(); /* if travel to adjacent, reachable location, use normal movement rules */ - if ((mode == TRAVP_TRAVEL || mode == TRAVP_VALID) && gc.context.travel1 + if ((mode == TRAVP_TRAVEL || mode == TRAVP_VALID) && svc.context.travel1 /* was '&& distmin(u.ux, u.uy, u.tx, u.ty) == 1' */ && next2u(u.tx, u.ty) /* one step away */ /* handle restricted diagonals */ @@ -1206,7 +1292,7 @@ findtravelpath(int mode) return TRUE; } if (mode == TRAVP_TRAVEL) - gc.context.run = 8; + svc.context.run = 8; } if (u.tx != u.ux || u.ty != u.uy) { coordxy travel[COLNO][ROWNO]; @@ -1319,22 +1405,24 @@ findtravelpath(int mode) || (!Blind && couldsee(nx, ny)))) { if (nx == ux && ny == uy) { if (mode == TRAVP_TRAVEL || mode == TRAVP_VALID) { - boolean visited = - selection_getpoint(x, y, gt.travelmap); + boolean visited + = selection_getpoint(x, y, gt.travelmap); u.dx = x - ux; u.dy = y - uy; if (mode == TRAVP_TRAVEL - && ((x == u.tx && y == u.ty) || visited)) { + && ((x == u.tx && y == u.ty) + || visited)) { nomul(0); /* reset run so domove run checks work */ - gc.context.run = 8; + svc.context.run = 8; if (visited) You("stop, unsure which way to go."); else iflags.travelcc.x = iflags.travelcc.y = 0; } - selection_setpoint(u.ux, u.uy, gt.travelmap, 1); + selection_setpoint(u.ux, u.uy, + gt.travelmap, 1); return TRUE; } } else if (!travel[nx][ny]) { @@ -1466,7 +1554,7 @@ is_valid_travelpt(coordxy x, coordxy y) /* try to escape being stuck in a trapped state by walking out of it; return true iff moving should continue to intended destination (all failures and most successful escapes leave hero at original spot) */ -static boolean +staticfn boolean trapmove( coordxy x, coordxy y, /* targeted destination, */ struct trap *desttrap) /* nonnull if another trap at */ @@ -1484,7 +1572,7 @@ trapmove( switch (u.utraptype) { case TT_BEARTRAP: - if (Verbose(1, trapmove1)) { + if (flags.verbose) { predicament = "caught in a bear trap"; if (u.usteed) Norep("%s is %s.", upstart(steedname), predicament); @@ -1519,7 +1607,7 @@ trapmove( return TRUE; /* escape trap and also move */ } if (--u.utrap) { - if (Verbose(1, trapmove2)) { + if (flags.verbose) { predicament = "stuck to the web"; if (u.usteed) Norep("%s is %s.", upstart(steedname), predicament); @@ -1534,7 +1622,7 @@ trapmove( } break; case TT_LAVA: - if (Verbose(1, trapmove3)) { + if (flags.verbose) { predicament = "stuck in the lava"; if (u.usteed) Norep("%s is %s.", upstart(steedname), predicament); @@ -1570,13 +1658,13 @@ trapmove( our next attempt to move out of tether range after this successful move would have its can't-do-that message suppressed by Norep */ - if (Verbose(1, trapmove4)) + if (flags.verbose) Norep("You move within the chain's reach."); return TRUE; } } if (--u.utrap) { - if (Verbose(1, trapmove5)) { + if (flags.verbose) { if (anchored) { predicament = "chained to the"; culprit = "buried ball"; @@ -1631,14 +1719,100 @@ u_rooted(void) return FALSE; } +void +notice_mon(struct monst *mtmp) +{ + if (a11y.mon_notices && !a11y.mon_notices_blocked) { + boolean spot = canspotmon(mtmp) + && !(is_hider(mtmp->data) + && (mtmp->mundetected + || M_AP_TYPE(mtmp) == M_AP_FURNITURE + || M_AP_TYPE(mtmp) == M_AP_OBJECT)); + + if (spot && !mtmp->mspotted && !DEADMONSTER(mtmp)) { + mtmp->mspotted = TRUE; + set_msg_xy(mtmp->mx, mtmp->my); + You("%s %s.", canseemon(mtmp) ? "see" : "notice", + x_monnam(mtmp, + mtmp->mtame ? ARTICLE_YOUR + : (!has_mgivenname(mtmp) + && !type_is_pname(mtmp->data)) ? ARTICLE_A + : ARTICLE_NONE, + (mtmp->mpeaceful && !mtmp->mtame) ? "peaceful" : 0, + has_mgivenname(mtmp) ? SUPPRESS_SADDLE : 0, FALSE)); + } else if (!spot) { + mtmp->mspotted = FALSE; + } + } +} + +staticfn int QSORTCALLBACK +notice_mons_cmp(const genericptr ptr1, const genericptr ptr2) +{ + const struct monst *m1 = *(const struct monst **) ptr1, + *m2 = *(const struct monst **) ptr2; + + return (distu(m1->mx, m1->my) - distu(m2->mx, m2->my)); +} + +void +notice_all_mons(boolean reset) +{ + if (a11y.mon_notices && !a11y.mon_notices_blocked) { + struct monst *mtmp; + struct monst **arr = NULL; + int j, i = 0, cnt = 0; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (canspotmon(mtmp)) + cnt++; + else if (reset) + mtmp->mspotted = FALSE; + + if (!cnt) + return; + + arr = (struct monst **) alloc(cnt * sizeof(struct monst *)); + + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (!canspotmon(mtmp)) + mtmp->mspotted = FALSE; + else if (!DEADMONSTER(mtmp) && i < cnt) + arr[i++] = mtmp; + } + + if (i) { + qsort((genericptr_t) arr, (size_t) i, sizeof *arr, + notice_mons_cmp); + + for (j = 0; j < i; j++) + notice_mon(arr[j]); + } + + free(arr); + } +} + +/* maybe disturb buried zombies when an object is dropped or thrown nearby */ +void +impact_disturbs_zombies(struct obj *obj, boolean violent) +{ + /* if object won't make a noticeable impact, let buried zombies rest */ + if (obj->owt < (violent ? 10U : 100U) || is_flimsy(obj)) + return; + + disturb_buried_zombies(obj->ox, obj->oy); +} + /* reduce zombification timeout of buried zombies around px, py */ -static void -check_buried_zombies(coordxy x, coordxy y) +void +disturb_buried_zombies(coordxy x, coordxy y) { struct obj *otmp; long t; - for (otmp = gl.level.buriedobjlist; otmp; otmp = otmp->nobj) { + for (otmp = svl.level.buriedobjlist; otmp; otmp = otmp->nobj) { if (otmp->otyp == CORPSE && otmp->timed && otmp->ox >= x - 1 && otmp->ox <= x + 1 && otmp->oy >= y - 1 && otmp->oy <= y + 1 @@ -1656,13 +1830,18 @@ 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 */ -static schar +staticfn schar u_simple_floortyp(coordxy x, coordxy y) { /* In xNetHack, u_in_air is a bad variable name because of open air terrain @@ -1686,90 +1865,45 @@ u_simple_floortyp(coordxy x, coordxy y) return ROOM; } -/* Is it dangerous for hero to move to x,y due to a trap the hero knows is - * there? */ -static boolean -trap_move_danger(coordxy x, coordxy y) -{ - struct trap *trap = t_at(x, y); - if ((Is_wizpuzzle_lev(&u.uz) && trap && trap->ttyp == SQKY_BOARD) - || (Is_telemaze_lev(&u.uz) && trap && trap->ttyp == TELEP_TRAP)) { - /* these are special case traps that the hero is supposed to use to - * trigger events in the puzzle or navigate the teleporter maze, so - * don't warn of it */ - return FALSE; - } - /* warn player before walking into known traps */ - if (trap && trap->tseen && (!gc.context.nopick || gc.context.run) - && !Stunned && !Confusion - && (immune_to_trap(&gy.youmonst, trap->ttyp) != TRAP_CLEARLY_IMMUNE - || Hallucination)) { - /* note on hallucination: all traps render as a random trap symbol and - * the hero can't tell what they are, so warn of every trap. */ - char qbuf[QBUFSZ]; - xint8 traptype = (Hallucination ? rnd(TRAPNUM - 1) : 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; - } - snprintf(qbuf, QBUFSZ, "Really %s %sto that %s?", - (Levitation || Flying) ? "move" - : locomotion(gy.youmonst.data, "step"), - (into ? "in" : "on"), - defsyms[trap_to_defsym(traptype)].explanation); - if (!paranoid_query(ParanoidTrap, qbuf)) { - nomul(0); - gc.context.move = 0; - return TRUE; - } - } - return FALSE; -} - -/* maybe show a helpful gameplay tip? */ -void +/* maybe show a helpful gameplay tip? returns True if tip gets shown */ +boolean handle_tip(int tip) { if (!flags.tips) - return; + return FALSE; - if (tip >= 0 && tip < NUM_TIPS && !gc.context.tips[tip]) { - gc.context.tips[tip] = TRUE; + if (tip >= 0 && tip < NUM_TIPS && !svc.context.tips[tip]) { + svc.context.tips[tip] = TRUE; + /* the "Tip:" prefix is a hint to use of OPTIONS=!tips to suppress */ switch (tip) { case TIP_ENHANCE: - pline("(Use the #enhance command to advance them.)"); + pline("(Tip: use the #enhance command to advance them.)"); break; case TIP_SWIM: - pline("(Use '%s' prefix to step in if you really want to.)", + pline("(Tip: use '%s' prefix to step in if you really want to.)", visctrl(cmd_from_func(do_reqmenu))); break; case TIP_UNTRAP_MON: - pline("(Perhaps #untrap would help?)"); + pline("(Tip: perhaps #untrap would help?)"); break; case TIP_GETPOS: l_nhcore_call(NHCORE_GETPOS_TIP); break; default: impossible("Unknown tip in handle_tip(%i)", tip); + break; } + return TRUE; } + return FALSE; } /* Is it dangerous for hero to move to x,y due to water or lava? */ -static boolean +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; @@ -1788,15 +1922,15 @@ swim_move_danger(coordxy x, coordxy y) continue to move over lava if already doing so */ || (is_lava(x, y) && !Known_lwalking && !is_lava(u.ux, u.uy)) || liquid_wall) { - if (gc.context.nopick) { + if (svc.context.nopick) { /* moving with m-prefix */ - gc.context.tips[TIP_SWIM] = TRUE; + svc.context.tips[TIP_SWIM] = TRUE; return FALSE; } else if (ParanoidSwim || liquid_wall) { You("avoid %s into the %s.", ing_suffix(u_locomotion("step")), waterbody_name(x, y)); - handle_tip(TIP_SWIM); + (void) handle_tip(TIP_SWIM); return TRUE; } } @@ -1805,15 +1939,15 @@ swim_move_danger(coordxy x, coordxy y) } /* Same as swim_move_danger but for stepping out into open air. */ -static boolean +staticfn boolean air_move_danger(coordxy x, coordxy y) { if (u_simple_floortyp(x, y) != u_simple_floortyp(u.ux, u.uy) && is_open_air(x, y) && !Stunned && !Confusion && levl[x][y].seenv) { - if (gc.context.nopick) { + if (svc.context.nopick) { /* moving with 'm' */ - gc.context.tips[TIP_SWIM] = TRUE; + svc.context.tips[TIP_SWIM] = TRUE; /* ParanoidSwim is close enough to do the same job here without * warranting a separate option for air */ if (ParanoidSwim && y_n("Really step off the cliff?") != 'y') { @@ -1832,7 +1966,7 @@ air_move_danger(coordxy x, coordxy y) } /* moving with 'm' prefix, bump into a monster? */ -static boolean +staticfn boolean domove_bump_mon(struct monst *mtmp, int glyph) { /* If they used a 'm' command, trying to move onto a monster @@ -1842,7 +1976,7 @@ domove_bump_mon(struct monst *mtmp, int glyph) * attack_check(), which still wastes a turn, but prints a * different message and makes the player remember the monster. */ - if (gc.context.nopick && !gc.context.travel + if (svc.context.nopick && !svc.context.travel && (canspotmon(mtmp) || glyph_is_invisible(glyph) || glyph_is_warning(glyph))) { if (M_AP_TYPE(mtmp) && !Protection_from_shape_changers @@ -1862,12 +1996,13 @@ domove_bump_mon(struct monst *mtmp, int glyph) returns TRUE if hero movement is used up. sets displaceu, if hero and monster could swap places instead. */ -static boolean +staticfn boolean domove_attackmon_at( struct monst *mtmp, coordxy x, coordxy y, boolean *displaceu) { + /* assert(mtmp != NULL) */ /* only attack if we know it's there * or if we used the 'F' command to fight blindly * or if it hides_under, in which case we call do_attack() to print @@ -1875,7 +2010,7 @@ domove_attackmon_at( * This is different from ceiling hiders, who aren't handled in * do_attack(). */ - if (gc.context.forcefight || !mtmp->mundetected || sensemon(mtmp) + if (svc.context.forcefight || !mtmp->mundetected || sensemon(mtmp) || ((hides_under(mtmp->data) || mtmp->data->mlet == S_EEL) && !is_safemon(mtmp))) { /* target monster might decide to switch places with you... */ @@ -1900,10 +2035,10 @@ domove_attackmon_at( } /* force-fight iron bars with your weapon? */ -static boolean +staticfn boolean domove_fight_ironbars(coordxy x, coordxy y) { - if (gc.context.forcefight && levl[x][y].typ == IRONBARS && uwep) { + if (svc.context.forcefight && levl[x][y].typ == IRONBARS && uwep) { struct obj *obj = uwep; unsigned breakflags = (BRK_BY_HERO | BRK_FROM_INV | BRK_MELEE); @@ -1911,7 +2046,7 @@ domove_fight_ironbars(coordxy x, coordxy y) if (obj->quan > 1L) obj = splitobj(obj, 1L); else - setuwep((struct obj *)0); + setuwep((struct obj *) 0); freeinv(obj); breakflags |= BRK_KNOWN2BREAK; } else { @@ -1925,29 +2060,78 @@ domove_fight_ironbars(coordxy x, coordxy y) } /* force-fight a spider web with your weapon */ -static boolean +staticfn boolean domove_fight_web(coordxy x, coordxy y) { struct trap *trap = t_at(x, y); - if (gc.context.forcefight && trap && trap->ttyp == WEB - && trap->tseen && uwep) { - if (u_wield_art(ART_STING)) { + if (svc.context.forcefight && trap && trap->ttyp == WEB && trap->tseen) { + int wtype = uwep_skill_type(), + /* minus_2: restricted or unskilled: -1, basic: 0, skilled: 1, + expert: 2, master: 3, grandmaster: 4 */ + wskill_minus_2 = max(P_SKILL(wtype), P_UNSKILLED) - 2, + /* higher value is worse for player; for weaponless, adjust the + chance to succeed rather than maybe make two tries */ + roll = rn2(uwep ? 20 : (45 - 5 * wskill_minus_2)); + + if (uwep && (u_wield_art(ART_STING) + || (uwep->oartifact && attacks(AD_FIRE, uwep)))) { /* guaranteed success */ - pline("%s cuts through the web!", - bare_artifactname(uwep)); - } else if (!is_blade(uwep)) { - You_cant("cut a web with %s!", an(xname(uwep))); + pline("%s %s through the web!", bare_artifactname(uwep), + u_wield_art(ART_STING) ? "cuts" : "burns"); + + /* is_blade() includes daggers (which are classified as PIERCE) + but doesn't include axes and slashing polearms */ + } else if (uwep && !is_blade(uwep) + && (!u.twoweap || !is_blade(uswapwep))) { + char *uwepstr = 0, *scndstr = 0, uwepbuf[BUFSZ], scndbuf[BUFSZ]; + boolean onewep; + + /* when dual wielding, second weapon will only be mentioned + if it has a different type description from primary */ + Strcpy(uwepbuf, weapon_descr(uwep)); + Strcpy(scndbuf, u.twoweap ? weapon_descr(uswapwep) : ""); + onewep = !*scndbuf || !strcmp(uwepbuf, scndbuf); + if (!strcmpi(uwepbuf, "armor") || !strcmpi(uwepbuf, "food") + || !strcmpi(uwepbuf, "venom")) { /* as-is */ + /* non-weapon item wielded, of a type where an() would + result in weird phrasing; dual wield not possible */ + uwepstr = uwepbuf; + } else if (uwep->quan == 1L /* singular */ + /* unless secondary is suppressed due to same type */ + && !(u.twoweap && onewep)) { + uwepstr = an(uwepbuf); + } else { /* plural */ + uwepstr = makeplural(uwepbuf); + } + if (!onewep) { + assert(uswapwep != NULL); + scndstr = (uswapwep->quan == 1L) ? an(scndbuf) + : makeplural(scndbuf); + } + You_cant("cut a web with %s%s%s!", uwepstr, + !onewep ? " or " : "", !onewep ? scndstr : ""); return TRUE; - } else if (rn2(20) > ACURR(A_STR) + uwep->spe) { + + /* weapon is ok; check whether hit is successful */ + } else if (roll > (acurrstr() - 2 /* 1..19 */ + /* for weaponless, 'roll' was adjusted above */ + + (uwep ? uwep->spe + wskill_minus_2 : 0))) { /* TODO: if failing to cut the web is going to be a thing, it should * really be an occupation. Not necessarily its own occupation, it * can probably slide into the digging code pretty well. */ - You("hack ineffectually at some of the strands."); + /* TODO: add failures, maybe make an occupation? */ + You("%s ineffectually at some of the strands.", + uwep ? "hack" : "thrash"); return TRUE; + + /* hit has succeeded */ } else { - You("cut through the web."); + You("%s through the web.", uwep ? "cut" : "punch"); + /* doesn't break "never hit with a wielded weapon" conduct */ + use_skill(wtype, 1); } + deltrap(trap); newsym(x, y); return TRUE; @@ -1956,8 +2140,10 @@ domove_fight_web(coordxy x, coordxy y) } /* maybe swap places with a pet? returns TRUE if swapped places */ -static boolean -domove_swap_with_pet(struct monst *mtmp, coordxy x, coordxy y) +staticfn boolean +domove_swap_with_pet( + struct monst *mtmp, + coordxy x, coordxy y) { struct trap *trap; /* if it turns out we can't actually move */ @@ -1973,42 +2159,51 @@ domove_swap_with_pet(struct monst *mtmp, coordxy x, coordxy y) seemimic(mtmp); u.ux = mtmp->mx, u.uy = mtmp->my; /* resume swapping positions */ - if (mtmp->mtrapped && (trap = t_at(mtmp->mx, mtmp->my)) != 0 - && is_pit(trap->ttyp) + trap = mtmp->mtrapped ? t_at(mtmp->mx, mtmp->my) : 0; + if (!trap) + mtmp->mtrapped = 0; + + if (mtmp->mtrapped && is_pit(trap->ttyp) && sobj_at(BOULDER, trap->tx, trap->ty)) { /* can't swap places with pet pinned in a pit by a boulder */ didnt_move = TRUE; } else if (u.ux0 != x && u.uy0 != y && NODIAG(mtmp->data - mons)) { /* can't swap places when pet can't move to your spot */ - You("stop. %s can't move diagonally.", upstart(y_monnam(mtmp))); + You("stop. %s can't move diagonally.", YMonnam(mtmp)); didnt_move = TRUE; } else if (u_with_boulder && !(verysmall(mtmp->data) && (!mtmp->minvent || curr_mon_load(mtmp) <= 600))) { /* can't swap places when pet won't fit there with the boulder */ You("stop. %s won't fit into the same spot that you're at.", - upstart(y_monnam(mtmp))); + YMonnam(mtmp)); didnt_move = TRUE; } else if (u.ux0 != x && u.uy0 != y && bad_rock(mtmp->data, x, u.uy0) && bad_rock(mtmp->data, u.ux0, y) && (bigmonst(mtmp->data) || (curr_mon_load(mtmp) > 600))) { /* can't swap places when pet won't fit thru the opening */ - You("stop. %s won't fit through.", upstart(y_monnam(mtmp))); + You("stop. %s won't fit through.", YMonnam(mtmp)); didnt_move = TRUE; } else if (mtmp->mpeaceful && mtmp->mtrapped) { /* all mtame are also mpeaceful, so this affects pets too */ - You("stop. %s can't move out of that trap.", - upstart(y_monnam(mtmp))); - handle_tip(TIP_UNTRAP_MON); + assert(trap != NULL); /* implied by mtrapped */ + const char *what = trapname(trap->ttyp, FALSE), *which = "that "; + char anbuf[10]; + + if (!trap->tseen) { + feeltrap(trap); /* show on map once mtmp is out of the way */ + which = just_an(anbuf, what); /* "a " or "an " */ + } + You("stop. %s can't move out of %s%s.", YMonnam(mtmp), which, what); + (void) handle_tip(TIP_UNTRAP_MON); didnt_move = TRUE; } else if (mtmp->mpeaceful && (!goodpos(u.ux0, u.uy0, mtmp, 0) || t_at(u.ux0, u.uy0) != NULL || mundisplaceable(mtmp))) { /* displacing peaceful into unsafe or trapped space, or trying to - * displace quest leader, Oracle, shopkeeper, or priest */ - You("stop. %s doesn't want to swap places.", - upstart(y_monnam(mtmp))); + displace quest leader, Oracle, shk, priest, or vault guard */ + You("stop. %s doesn't want to swap places.", YMonnam(mtmp)); didnt_move = TRUE; } else { mtmp->mtrapped = 0; @@ -2052,7 +2247,7 @@ domove_swap_with_pet(struct monst *mtmp, coordxy x, coordxy y) if (!u.uconduct.killer++) livelog_printf(LL_CONDUCT, "killed for the first time"); mndx = monsndx(mtmp->data); - tmp = experience(mtmp, (int) gm.mvitals[mndx].died); + tmp = experience(mtmp, (int) svm.mvitals[mndx].died); more_experienced(tmp, 0); newexplevel(); /* will decide if you go up */ } @@ -2076,7 +2271,7 @@ domove_swap_with_pet(struct monst *mtmp, coordxy x, coordxy y) } /* force-fight (x,y) which doesn't have anything to fight */ -static boolean +staticfn boolean domove_fight_empty(coordxy x, coordxy y) { static const char unknown_obstacle[] = "an unknown obstacle"; @@ -2101,9 +2296,10 @@ domove_fight_empty(coordxy x, coordxy y) * about this. */ /* specifying 'F' with no monster wastes a turn */ - if (gc.context.forcefight + if (svc.context.forcefight /* remembered an 'I' && didn't use a move command */ - || (glyph_is_invisible(glyph) && !m_at(x, y) && !gc.context.nopick)) { + || (glyph_is_invisible(glyph) && !m_at(x, y) + && !svc.context.nopick)) { struct obj *boulder = 0; boolean explo = (Upolyd && attacktype(gy.youmonst.data, AT_EXPL)), solid = (off_edge || (!accessible(x, y) @@ -2127,7 +2323,7 @@ domove_fight_empty(coordxy x, coordxy y) /* force fight at boulder/statue or wall/door while wielding pick: start digging to break the boulder or wall */ - if (gc.context.forcefight + if (svc.context.forcefight /* can we dig? */ && uwep && dig_typ(uwep, x, y) /* should we dig? */ @@ -2143,6 +2339,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)); @@ -2190,7 +2387,7 @@ domove_fight_empty(coordxy x, coordxy y) } /* does the plane of air disturb movement? */ -static boolean +staticfn boolean air_turbulence(void) { if (Is_airlevel(&u.uz) && rn2(4) && !Levitation && !Flying) { @@ -2213,7 +2410,7 @@ air_turbulence(void) } /* does water disturb the movement? */ -static boolean +staticfn boolean water_turbulence(coordxy *x, coordxy *y) { if (u.uinwater) { @@ -2244,15 +2441,16 @@ water_turbulence(coordxy *x, coordxy *y) return FALSE; } -static void +staticfn void slippery_ice_fumbling(void) { boolean on_ice = !Levitation && is_ice(u.ux, u.uy); + struct monst *iceskater = u.usteed ? u.usteed : &gy.youmonst; if (on_ice) { if ((uarmf && objdescr_is(uarmf, "snow boots")) - || resists_cold(&gy.youmonst) || Flying || !grounded(gy.youmonst.data) - || is_whirly(gy.youmonst.data)) { + || resists_cold(iceskater) || Flying || !grounded(iceskater->data) + || is_whirly(iceskater->data)) { on_ice = FALSE; } else if (!rn2(Cold_resistance ? 3 : 2)) { HFumbling |= FROMOUTSIDE; @@ -2271,11 +2469,11 @@ u_maybe_impaired(void) } /* change movement dir if impaired. return TRUE if can't move */ -static boolean +staticfn boolean impaired_movement(coordxy *x, coordxy *y) { if (u_maybe_impaired()) { - register int tries = 0; + int tries = 0; do { if (tries++ > 50) { @@ -2290,21 +2488,23 @@ impaired_movement(coordxy *x, coordxy *y) return FALSE; } -static boolean +staticfn boolean avoid_moving_on_trap(coordxy x, coordxy y, boolean msg) { struct trap *trap; if ((trap = t_at(x, y)) && trap->tseen) { - if (msg && flags.mention_walls) + if (msg && flags.mention_walls) { + set_msg_xy(x, y); You("stop in front of %s.", an(trapname(trap->ttyp, FALSE))); + } return TRUE; } return FALSE; } -static boolean +staticfn boolean avoid_moving_on_liquid( coordxy x, coordxy y, boolean msg) @@ -2315,8 +2515,8 @@ avoid_moving_on_liquid( if ((levl[x][y].typ == levl[u.ux][u.uy].typ /* or you are using shift-dir running and the transition isn't dangerous... */ - || (gc.context.run < 2 && (!is_lava(x, y) || in_air)) - || gc.context.travel) + || (svc.context.run < 2 && (!is_lava(x, y) || in_air)) + || svc.context.travel) /* and you know you won't fall in */ && (in_air || Known_lwalking || (is_pool(x, y) && Known_wwalking)) && !(IS_WATERWALL(levl[x][y].typ) || levl[x][y].typ == LAVAWALL)) { @@ -2324,9 +2524,11 @@ avoid_moving_on_liquid( polyforms are allowed to move over water */ return FALSE; /* liquid is safe to traverse */ } else if (is_pool_or_lava(x, y) && levl[x][y].seenv) { - if (msg && flags.mention_walls) + if (msg && flags.mention_walls) { + set_msg_xy(x, y); You("stop at the edge of the %s.", hliquid(is_pool(x,y) ? "water" : "lava")); + } return TRUE; } return FALSE; @@ -2334,29 +2536,29 @@ avoid_moving_on_liquid( /* when running/rushing, avoid stepping on a known trap or pool of liquid. returns TRUE if avoided. */ -static boolean +staticfn boolean avoid_running_into_trap_or_liquid(coordxy x, coordxy y) { - boolean would_stop = (gc.context.run >= 2); - if (!gc.context.run) + boolean would_stop = (svc.context.run >= 2); + if (!svc.context.run) return FALSE; if (avoid_moving_on_trap(x,y, would_stop) || (Blind && avoid_moving_on_liquid(x,y, would_stop))) { nomul(0); if (would_stop) - gc.context.move = 0; + svc.context.move = 0; return would_stop; } return FALSE; } /* trying to move out-of-bounds? */ -static boolean +staticfn boolean move_out_of_bounds(coordxy x, coordxy y) { if (!isok(x, y)) { - if (gc.context.forcefight) + if (svc.context.forcefight) return domove_fight_empty(x, y); if (flags.mention_walls) { @@ -2375,14 +2577,14 @@ move_out_of_bounds(coordxy x, coordxy y) directionname(xytod(dx, dy))); } nomul(0); - gc.context.move = 0; + svc.context.move = 0; return TRUE; } return FALSE; } /* carrying too much to be able to move? */ -static boolean +staticfn boolean carrying_too_much(void) { int wtcap; @@ -2405,13 +2607,13 @@ carrying_too_much(void) /* try to pull free from sticking monster, or you release a monster you're sticking to. returns TRUE if you lose your movement. */ -static boolean +staticfn boolean escape_from_sticky_mon(coordxy x, coordxy y) { if (u.ustuck && (x != u.ustuck->mx || y != u.ustuck->my)) { struct monst *mtmp; - if (!next2u(u.ustuck->mx, u.ustuck->my)) { + if (!m_next2u(u.ustuck)) { /* perhaps it fled (or was teleported or ... ) */ set_ustuck((struct monst *) 0); } else if (sticks(gy.youmonst.data)) { @@ -2420,7 +2622,7 @@ escape_from_sticky_mon(coordxy x, coordxy y) */ mtmp = u.ustuck; set_ustuck((struct monst *) 0); - You("release %s.", mon_nam(mtmp)); + You("release %s.", y_monnam(mtmp)); } else { /* If holder is asleep or paralyzed: * 37.5% chance of getting away, @@ -2438,7 +2640,7 @@ escape_from_sticky_mon(coordxy x, coordxy y) pull_free: mtmp = u.ustuck; set_ustuck((struct monst *) 0); - You("pull free from %s.", mon_nam(mtmp)); + You("pull free from %s.", y_monnam(mtmp)); break; case 3: if (!u.ustuck->mcanmove) { @@ -2446,11 +2648,12 @@ escape_from_sticky_mon(coordxy x, coordxy y) 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!", mon_nam(u.ustuck)); + You("cannot escape from %s!", y_monnam(u.ustuck)); nomul(0); return TRUE; } @@ -2467,29 +2670,34 @@ domove(void) gd.domove_succeeded = 0L; domove_core(); /* gd.domove_succeeded is available to make assessments now */ - if ((gd.domove_succeeded & (DOMOVE_RUSH | DOMOVE_WALK)) != 0) + if ((gd.domove_succeeded & (DOMOVE_RUSH | DOMOVE_WALK)) != 0) { maybe_smudge_engr(ux1, uy1, u.ux, u.uy); + maybe_adjust_hero_bubble(); + } gd.domove_attempting = 0L; + + gk.kickedloc.x = 0, gk.kickedloc.y = 0; } -static void +staticfn void domove_core(void) { - register struct monst *mtmp; - register struct rm *tmpr; + 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 */ + ballx = 0, bally = 0; /* ball&chain new positions */ int bc_control = 0; /* control for ball&chain */ boolean cause_delay = FALSE, /* dragging ball will skip a move */ displaceu = FALSE; /* involuntary swap */ - if (gc.context.travel) { + if (svc.context.travel) { if (!findtravelpath(TRAVP_TRAVEL)) (void) findtravelpath(TRAVP_GUESS); - gc.context.travel1 = 0; + svc.context.travel1 = 0; } if (carrying_too_much()) @@ -2530,13 +2738,13 @@ domove_core(void) /* Don't attack if you're running, and can see it */ /* It's fine to displace pets, though */ /* We should never get here if forcefight */ - if (gc.context.run && ((!Blind && mon_visible(mtmp) + if (svc.context.run && ((!Blind && mon_visible(mtmp) && ((M_AP_TYPE(mtmp) != M_AP_FURNITURE && M_AP_TYPE(mtmp) != M_AP_OBJECT) || Protection_from_shape_changers)) || sensemon(mtmp))) { nomul(0); - gc.context.move = 0; + svc.context.move = 0; return; } } @@ -2553,7 +2761,7 @@ domove_core(void) /* don't stop travel when displacing pets; if the displace fails for some reason, do_attack() in uhitm.c will stop travel rather than domove */ - if (!is_safemon(mtmp) || gc.context.forcefight) + if (!is_safemon(mtmp) || svc.context.forcefight) nomul(0); if (domove_bump_mon(mtmp, glyph)) @@ -2584,11 +2792,84 @@ domove_core(void) 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?", + u_locomotion("step"), + (reg_damg(newreg) > 0) ? "poison gas" : "vapor"); + if (!paranoid_query(ParanoidConfirm, upstart(qbuf))) { + 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) + /* these are special case traps that the hero is supposed to use to + * trigger events in the puzzle or navigate the teleporter maze, so + * don't warn of it */ + && !((Is_wizpuzzle_lev(&u.uz) && trap->ttyp == SQKY_BOARD) + || (Is_telemaze_lev(&u.uz) && trap->ttyp == TELEP_TRAP))) { + char qbuf[QBUFSZ]; + 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; + } + } + if (u.utrap) { boolean moved = trapmove(x, y, trap); if (!u.utrap) { - gc.context.botl = TRUE; + disp.botl = TRUE; reset_utrap(TRUE); /* might resume levitation or flight */ } /* might not have escaped, or did escape but remain in same spot */ @@ -2597,18 +2878,16 @@ domove_core(void) } if (!test_move(u.ux, u.uy, x - u.ux, y - u.uy, DO_MOVE)) { - if (!gc.context.door_opened) { - gc.context.move = 0; + if (!svc.context.door_opened) { + svc.context.move = 0; nomul(0); } return; } - /* Is it dangerous to swim in water or lava, move onto a known trap, or step - * off a cliff? */ - if (trap_move_danger(x, y) || swim_move_danger(x, y) - || air_move_danger(x, y)) { - gc.context.move = 0; + /* Is it dangerous to swim in water or lava or step off a cliff? */ + if (swim_move_danger(x, y) || air_move_danger(x, y)) { + svc.context.move = 0; nomul(0); return; } @@ -2624,9 +2903,14 @@ domove_core(void) return; mtmp = m_at(x, y); + /* mtmp can be null at this point */ + /* tentatively move the hero plus steed; leave CLIPPING til later */ u.ux += u.dx; u.uy += u.dy; + + m_postmove_effect(&gy.youmonst); + if (u.usteed) { u.usteed->mx = u.ux; u.usteed->my = u.uy; @@ -2634,62 +2918,68 @@ domove_core(void) exercise_steed(); /* train riding skill */ } - if (displaceu && mtmp) { - boolean noticed_it = (canspotmon(mtmp) || glyph_is_invisible(glyph) - || glyph_is_warning(glyph)); + if (mtmp) { + if (displaceu) { + boolean noticed_it = (canspotmon(mtmp) + || glyph_is_invisible(glyph) + || glyph_is_warning(glyph)); - remove_monster(u.ux, u.uy); - place_monster(mtmp, u.ux0, u.uy0); - newsym(u.ux, u.uy); - newsym(u.ux0, u.uy0); - /* monst still knows where hero is */ - mtmp->mux = u.ux, mtmp->muy = u.uy; - - pline("%s swaps places with you...", - !noticed_it ? Something : Monnam(mtmp)); - if (!canspotmon(mtmp)) - map_invisible(u.ux0, u.uy0); - /* monster chose to swap places; hero doesn't get any credit - or blame if something bad happens to it */ - gc.context.mon_moving = 1; - if (!minliquid(mtmp)) - (void) mintrap(mtmp, NO_TRAP_FLAGS); - gc.context.mon_moving = 0; + remove_monster(u.ux, u.uy); + place_monster(mtmp, u.ux0, u.uy0); + newsym(u.ux, u.uy); + newsym(u.ux0, u.uy0); + /* monst still knows where hero is */ + mtmp->mux = u.ux, mtmp->muy = u.uy; + + pline("%s swaps places with you...", + !noticed_it ? Something : YMonnam(mtmp)); + if (!canspotmon(mtmp)) + map_invisible(u.ux0, u.uy0); + /* monster chose to swap places; hero doesn't get any credit + or blame if something bad happens to it */ + svc.context.mon_moving = 1; + if (!minliquid(mtmp)) + (void) mintrap(mtmp, NO_TRAP_FLAGS); + svc.context.mon_moving = 0; - /* - * If safepet at destination then move the pet to the hero's - * previous location using the same conditions as in do_attack(). - * there are special extenuating circumstances: - * (1) if the pet dies then your god angers, - * (2) if the pet gets trapped then your god may disapprove. - * - * Ceiling-hiding pets are skipped by this section of code, to - * be caught by the normal falling-monster code. - */ - } else if (is_safemon(mtmp) - && !(is_hider(mtmp->data) && mtmp->mundetected)) { - if (!domove_swap_with_pet(mtmp, x, y)) { - u.ux = u.ux0, u.uy = u.uy0; /* didn't move after all */ - /* could skip this bit since we're about to call u_on_newpos() */ - if (u.usteed) - u.usteed->mx = u.ux, u.usteed->my = u.uy; + /* + * If safepet at destination then move the pet to the hero's + * previous location using the same conditions as in do_attack(). + * there are special extenuating circumstances: + * (1) if the pet dies then your god angers, + * (2) if the pet gets trapped then your god may disapprove. + * + * Ceiling-hiding pets are skipped by this section of code, to + * be caught by the normal falling-monster code. + */ + } else if (is_safemon(mtmp) + && !(is_hider(mtmp->data) && mtmp->mundetected)) { + if (!domove_swap_with_pet(mtmp, x, y)) { + u.ux = u.ux0, u.uy = u.uy0; /* didn't move after all */ + /* could skip this since we're about to call u_on_newpos() */ + if (u.usteed) + u.usteed->mx = u.ux, u.usteed->my = u.uy; + } } - } + } /* mtmp != NULL */ + /* tentative move above didn't handle CLIPPING, in case there was a monster in the way and the move attempt ended up being blocked; do a full re-position now, possibly back to where hero started */ u_on_newpos(u.ux, u.uy); reset_occupations(); - if (gc.context.run) { - if (gc.context.run < 8) - if (IS_DOOR(tmpr->typ) || IS_ROCK(tmpr->typ) + if (svc.context.run) { + if (svc.context.run < 8) + if (IS_DOOR(tmpr->typ) || IS_OBSTRUCTED(tmpr->typ) || IS_FURNITURE(tmpr->typ)) nomul(0); } - if (!Levitation && !Flying && !Stealth) - check_buried_zombies(u.ux, u.uy); + /* your tread on the ground may disturb the slumber of nearby zombies */ + if (!Levitation && !Flying && !Stealth + && gy.youmonst.data->cwt >= (WT_ELF / 2)) + disturb_buried_zombies(u.ux, u.uy); if (hides_under(gy.youmonst.data) || gy.youmonst.data->mlet == S_EEL || u.dx || u.dy) @@ -2740,15 +3030,15 @@ domove_core(void) void runmode_delay_output(void) { - if ((gc.context.run || gm.multi) && flags.runmode != RUN_TPORT) { + if ((svc.context.run || gm.multi) && flags.runmode != RUN_TPORT) { /* for tport mode, don't display anything until we've stopped; for normal (leap) mode, update display every 7th step (relative to turn counter; ought to be to start of running); for walk and crawl (visual debugging) modes, update the display after every step */ - if (flags.runmode != RUN_LEAP || !(gm.moves % 7L)) { + if (flags.runmode != RUN_LEAP || !(svm.moves % 7L)) { /* moveloop() suppresses time_botl when running */ - iflags.time_botl = flags.time; + disp.time_botl = flags.time; curs_on_u(); nh_delay_output(); if (flags.runmode == RUN_CRAWL) { @@ -2761,7 +3051,7 @@ runmode_delay_output(void) } } -static void +staticfn void maybe_smudge_engr(coordxy x1, coordxy y1, coordxy x2, coordxy y2) { struct engr *ep; @@ -2783,7 +3073,7 @@ overexert_hp(void) if (*hp > 1) { *hp -= 1; - gc.context.botl = TRUE; + disp.botl = TRUE; } else { You("pass out from exertion!"); exercise(A_CON, FALSE); @@ -2799,7 +3089,7 @@ overexertion(void) position, but is now called by do_attack() so that it doesn't execute if you decline to attack a peaceful monster */ gethungry(); - if ((gm.moves % 3L) != 0L && near_capacity() >= HVY_ENCUMBER) { + if ((svm.moves % 3L) != 0L && near_capacity() >= HVY_ENCUMBER) { overexert_hp(); } return (boolean) (gm.multi < 0); /* might have fainted (forced to sleep) */ @@ -2836,7 +3126,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; @@ -2868,7 +3158,7 @@ switch_terrain(void) You("start flying."); } if ((!!Levitation ^ was_levitating) || (!!Flying ^ was_flying)) - gc.context.botl = TRUE; /* update Lev/Fly status condition */ + disp.botl = TRUE; /* update Lev/Fly status condition */ } /* set or clear u.uinwater */ @@ -2892,12 +3182,11 @@ pooleffects( if (!is_pool(u.ux, u.uy)) { if (Is_waterlevel(&u.uz)) { You("pop into an air bubble."); + iflags.last_msg = PLNMSG_BACK_ON_GROUND; } else if (is_lava(u.ux, u.uy)) { You("leave the %s...", hliquid("water")); /* oops! */ } else { - You("are on solid %s again.", - is_ice(u.ux, u.uy) ? "ice" : "land"); - iflags.last_msg = PLNMSG_BACK_ON_GROUND; + back_on_ground(FALSE); } } else if (Is_waterlevel(&u.uz)) { still_inwater = TRUE; @@ -2978,8 +3267,8 @@ u_aireffects(void) if (destination.dnum == 0 && destination.dlevel == 0) { You("fall a few thousand feet to your death."); /* if there's no lower level, then... well, you're done for */ - Sprintf(gk.killer.name, "fell to %s death", uhis()); - gk.killer.format = NO_KILLER_PREFIX; + Sprintf(svk.killer.name, "fell to %s death", uhis()); + svk.killer.format = NO_KILLER_PREFIX; done(DIED); /* life-saved */ if (safe_teleds(TELEDS_ALLOW_DRAG | TELEDS_TELEPORT)) @@ -2987,8 +3276,8 @@ u_aireffects(void) else { pline("Unfortunately, there is still nowhere safe to land."); You("fall to your death again."); - Sprintf(gk.killer.name, "fell to %s death", uhis()); - gk.killer.format = NO_KILLER_PREFIX; + Sprintf(svk.killer.name, "fell to %s death", uhis()); + svk.killer.format = NO_KILLER_PREFIX; done(DIED); } } @@ -3157,10 +3446,10 @@ spoteffects(boolean pick) } /* returns first matching monster */ -static struct monst * +staticfn struct monst * monstinroom(struct permonst *mdat, int roomno) { - register struct monst *mtmp; + struct monst *mtmp; for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) @@ -3172,17 +3461,34 @@ monstinroom(struct permonst *mdat, int roomno) return (struct monst *) 0; } +/* check whether room contains a particular type of furniture */ +staticfn boolean +furniture_present(int furniture, int roomno) +{ + int x, y, lx, ly, hx, hy; + struct mkroom *sroom = &svr.rooms[roomno]; + + ly = sroom->ly, hy = sroom->hy; + lx = sroom->lx; hx = sroom->hx; + /* the inside_room() check handles irregularly shaped rooms */ + for (y = ly; y <= hy; ++y) + for (x = lx; x <= hx; ++x) + if (levl[x][y].typ == furniture && inside_room(sroom, x, y)) + return TRUE; + return FALSE; +} + char * -in_rooms(register coordxy x, register coordxy y, register int typewanted) +in_rooms(coordxy x, coordxy y, int typewanted) { static char buf[5]; char rno, *ptr = &buf[4]; int typefound, min_x, min_y, max_x, max_y_offset, step; - register struct rm *lev; + struct rm *lev; #define goodtype(rno) \ (!typewanted \ - || (typefound = gr.rooms[rno - ROOMOFFSET].rtype) == typewanted \ + || (typefound = svr.rooms[rno - ROOMOFFSET].rtype) == typewanted \ || (typewanted == SHOPBASE && typefound > SHOPBASE)) switch (rno = levl[x][y].roomno) { @@ -3241,18 +3547,17 @@ in_rooms(register coordxy x, register coordxy y, register int typewanted) boolean in_town(coordxy x, coordxy y) { - s_level *slev = Is_special(&u.uz); - register struct mkroom *sroom; + struct mkroom *sroom; boolean has_subrooms = FALSE; - if (!slev || !slev->flags.town) + if (!svl.level.flags.has_town) return FALSE; /* * See if (x,y) is in a room with subrooms, if so, assume it's the * town. If there are no subrooms, the whole level is in town. */ - for (sroom = &gr.rooms[0]; sroom->hx > 0; sroom++) { + for (sroom = &svr.rooms[0]; sroom->hx > 0; sroom++) { if (sroom->nsubrooms > 0) { has_subrooms = TRUE; if (inside_room(sroom, x, y)) @@ -3263,8 +3568,8 @@ in_town(coordxy x, coordxy y) return !has_subrooms; } -static void -move_update(register boolean newlev) +staticfn void +move_update(boolean newlev) { char c, *ptr1, *ptr2, *ptr3, *ptr4; @@ -3312,21 +3617,33 @@ check_special_room(boolean newlev) if (*u.ushops0) u_left_shop(u.ushops_left, newlev); - if (!*u.uentered && !*u.ushops_entered) /* implied by newlev */ - return; /* no entrance messages necessary */ - - if (!gc.context.achieveo.minetn_reached + /* + * Check for attaining 'entered Mine Town' achievement. + * Most of the Mine Town variations have the town in one large room + * containing a bunch of subrooms; we check for entering that large + * room. However, two of the variations cover the whole level rather + * than include a room with subrooms. We need to check for town entry + * before the possible early return for not having entered a room in + * case we have arrived in the town but have not entered any room. + * + * TODO: change the minetn variants which don't include any town + * boundary to have such. + */ + if (svl.level.flags.has_town && !svc.context.achieveo.minetn_reached && In_mines(&u.uz) && in_town(u.ux, u.uy)) { record_achievement(ACH_TOWN); - gc.context.achieveo.minetn_reached = TRUE; + svc.context.achieveo.minetn_reached = TRUE; } + if (!*u.uentered && !*u.ushops_entered) /* implied by newlev */ + return; /* no entrance messages necessary */ + /* Did we just enter a shop? */ if (*u.ushops_entered) u_entered_shop(u.ushops_entered); for (ptr = &u.uentered[0]; *ptr; ptr++) { - int roomno = *ptr - ROOMOFFSET, rt = gr.rooms[roomno].rtype; + int roomno = *ptr - ROOMOFFSET, rt = svr.rooms[roomno].rtype; boolean msg_given = TRUE; /* Regions on wizard3 are not special rooms, but may trigger doing @@ -3351,7 +3668,10 @@ check_special_room(boolean newlev) if (summon_thronerm_dlord(roomno)) break; else /* generic case */ - You("enter an opulent throne room!"); + You("enter an opulent%s room!", + /* the throne room in Sam quest home level lacks a + * throne */ + !furniture_present(THRONE, roomno) ? "" : " throne"); break; case LEPREHALL: You("enter a leprechaun hall!"); @@ -3388,10 +3708,10 @@ check_special_room(boolean newlev) if (oracle) { SetVoice(oracle, 0, 80, 0); if (!oracle->mpeaceful) - verbalize("You're in Delphi, %s.", gp.plname); + verbalize("You're in Delphi, %s.", svp.plname); else verbalize("%s, %s, welcome to Delphi!", - Hello((struct monst *) 0), gp.plname); + Hello((struct monst *) 0), svp.plname); } else msg_given = FALSE; break; @@ -3410,9 +3730,10 @@ check_special_room(boolean newlev) case TEMPLE: case SEMINARY: intemple(roomno + ROOMOFFSET); + FALLTHROUGH; /*FALLTHRU*/ default: - msg_given = (rt == TEMPLE || rt == SEMINARY); + msg_given = (rt == TEMPLE || rt == SEMINARY || rt >= SHOPBASE); rt = 0; break; } @@ -3420,30 +3741,30 @@ check_special_room(boolean newlev) room_discovered(roomno); if (rt != 0) { - gr.rooms[roomno].rtype = OROOM; + svr.rooms[roomno].rtype = OROOM; if (!search_special(rt)) { /* No more room of that type */ switch (rt) { case COURT: - gl.level.flags.has_court = 0; + svl.level.flags.has_court = 0; break; case SWAMP: - gl.level.flags.has_swamp = 0; + svl.level.flags.has_swamp = 0; break; case MORGUE: - gl.level.flags.has_morgue = 0; + svl.level.flags.has_morgue = 0; break; case ZOO: - gl.level.flags.has_zoo = 0; + svl.level.flags.has_zoo = 0; break; case BARRACKS: - gl.level.flags.has_barracks = 0; + svl.level.flags.has_barracks = 0; break; case TEMPLE: - gl.level.flags.has_temple = 0; + svl.level.flags.has_temple = 0; break; case BEEHIVE: - gl.level.flags.has_beehive = 0; + svl.level.flags.has_beehive = 0; break; } } @@ -3467,7 +3788,7 @@ check_special_room(boolean newlev) 0 = cannot pickup, no time taken -1 = do normal pickup -2 = loot the monster */ -static int +staticfn int pickup_checks(void) { struct trap *traphere; @@ -3506,7 +3827,7 @@ pickup_checks(void) } } if (!OBJ_AT(u.ux, u.uy)) { - register struct rm *lev = &levl[u.ux][u.uy]; + struct rm *lev = &levl[u.ux][u.uy]; if (IS_THRONE(lev->typ)) pline("It must weigh%s a ton!", lev->looted ? " almost" : ""); @@ -3578,7 +3899,7 @@ dopickup(void) void lookaround(void) { - register coordxy x, y; + coordxy x, y; coordxy i, x0 = 0, y0 = 0, m0 = 1, i0 = 9; int corrct = 0, noturn = 0; struct monst *mtmp; @@ -3591,7 +3912,7 @@ lookaround(void) return; } - if (Blind || gc.context.run == 0) + if (Blind || svc.context.run == 0) return; for (x = u.ux - 1; x <= u.ux + 1; x++) for (y = u.uy - 1; y <= u.uy + 1; y++) { @@ -3611,11 +3932,11 @@ lookaround(void) && mon_visible(mtmp)) { /* running movement and not a hostile monster */ /* OR it blocks our move direction and we're not traveling */ - if ((gc.context.run != 1 && !is_safemon(mtmp)) - || (infront && !gc.context.travel)) { + if ((svc.context.run != 1 && !is_safemon(mtmp)) + || (infront && !svc.context.travel)) { if (flags.mention_walls) - pline("%s blocks your path.", - upstart(a_monnam(mtmp))); + pline_xy(x, y, "%s blocks your path.", + upstart(a_monnam(mtmp))); goto stop; } } @@ -3628,15 +3949,16 @@ lookaround(void) continue; /* stop for traps, sometimes */ - if (avoid_moving_on_trap(x, y, (infront && gc.context.run > 1))) { - if (gc.context.run == 1) + if (avoid_moving_on_trap(x, y, + (infront && svc.context.run > 1))) { + if (svc.context.run == 1) goto bcorr; /* if you must */ if (infront) goto stop; } /* 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) || IS_GRASS(levl[x][y].typ) || levl[x][y].typ == ICE || IS_MAGIC_PLATFORM(levl[x][y].typ)) { continue; @@ -3645,20 +3967,22 @@ lookaround(void) /* ignore if diagonal */ if (x != u.ux && y != u.uy) continue; - if (gc.context.run != 1 && !gc.context.travel) { - if (flags.mention_walls) + if (svc.context.run != 1 && !svc.context.travel) { + if (flags.mention_walls) { + set_msg_xy(x, y); You("stop in front of the door."); + } goto stop; } - /* we're orthogonal to a closed door, consider it a corridor */ + /* orthogonal to a closed door, consider it a corridor */ goto bcorr; } else if (levl[x][y].typ == CORR) { /* corridor */ bcorr: if (levl[u.ux][u.uy].typ != ROOM) { /* running or traveling */ - if (gc.context.run == 1 || gc.context.run == 3 - || gc.context.run == 8) { + if (svc.context.run == 1 || svc.context.run == 3 + || svc.context.run == 8) { /* distance from x,y to location we're moving to */ i = dist2(x, y, u.ux + u.dx, u.uy + u.dy); /* ignore if not on or directly adjacent to it */ @@ -3686,9 +4010,9 @@ lookaround(void) goto stop; continue; } else { /* e.g. objects or trap or stairs */ - if (gc.context.run == 1) + if (svc.context.run == 1) goto bcorr; - if (gc.context.run == 8) + if (svc.context.run == 8) continue; if (mtmp) continue; /* d */ @@ -3701,12 +4025,12 @@ lookaround(void) return; } /* end for loops */ - if (corrct > 1 && gc.context.run == 2) { + if (corrct > 1 && svc.context.run == 2) { if (flags.mention_walls) pline_The("corridor widens here."); goto stop; } - if ((gc.context.run == 1 || gc.context.run == 3 || gc.context.run == 8) + if ((svc.context.run == 1 || svc.context.run == 3 || svc.context.run == 8) && !noturn && !m0 && i0 && (corrct == 1 || (corrct == 2 && i0 == 1))) { /* make sure that we do not turn too far */ @@ -3784,8 +4108,8 @@ crawl_destination(coordxy x, coordxy y) int monster_nearby(void) { - register coordxy x, y; - register struct monst *mtmp; + coordxy x, y; + struct monst *mtmp; /* Also see the similar check in dochugw() in monmove.c */ for (x = u.ux - 1; x <= u.ux + 1; x++) @@ -3810,13 +4134,13 @@ end_running(boolean and_travel) { /* moveloop() suppresses time_botl when context.run is non-zero; when running stops, update 'time' even if other botl status is unchanged */ - if (flags.time && gc.context.run) - iflags.time_botl = TRUE; - gc.context.run = 0; + if (flags.time && svc.context.run) + disp.time_botl = TRUE; + svc.context.run = 0; /* 'context.mv' isn't travel but callers who want to end travel all clear it too */ if (and_travel) - gc.context.travel = gc.context.travel1 = gc.context.mv = 0; + svc.context.travel = svc.context.travel1 = svc.context.mv = 0; if (gt.travelmap) { selection_free(gt.travelmap, TRUE); gt.travelmap = NULL; @@ -3831,7 +4155,7 @@ nomul(int nval) { if (gm.multi < nval) return; /* This is a bug fix by ab@unido */ - gc.context.botl |= (gm.multi >= 0); + disp.botl |= (gm.multi >= 0); u.uinvulnerable = FALSE; /* Kludge to avoid ctrl-C bug -dlc */ u.usleep = 0; gm.multi = nval; @@ -3845,7 +4169,7 @@ nomul(int nval) void unmul(const char *msg_override) { - gc.context.botl = TRUE; + disp.botl = TRUE; gm.multi = 0; /* caller will usually have done this already */ if (msg_override) gn.nomovemsg = msg_override; @@ -3857,6 +4181,7 @@ unmul(const char *msg_override) gn.nomovemsg = 0; u.usleep = 0; gm.multi_reason = NULL, gm.multireasonbuf[0] = '\0'; + if (ga.afternmv) { int (*f)(void) = ga.afternmv; @@ -3864,23 +4189,24 @@ unmul(const char *msg_override) encumbrance hack for levitation--see weight_cap()) */ ga.afternmv = (int (*)(void)) 0; (void) (*f)(); - /* for finishing Armor/Boots/&c_on() */ + /* for finishing Armor/Boots/&c_on(); total inventory weight might have + * changed as a result of the worn armor weight reduction, show + * encumbrance message now to avoid it being a turn too late */ (void) encumber_msg(); - update_inventory(); } } -static void +staticfn void maybe_wail(void) { static short powers[] = { TELEPORT, SEE_INVIS, POISON_RES, COLD_RES, SHOCK_RES, FIRE_RES, SLEEP_RES, DISINT_RES, TELEPORT_CONTROL, STEALTH, FAST, INVIS }; - if (gm.moves <= gw.wailmsg + 50) + if (svm.moves <= gw.wailmsg + 50) return; - gw.wailmsg = gm.moves; + gw.wailmsg = svm.moves; if (Role_if(PM_WIZARD) || Race_if(PM_ELF) || Role_if(PM_VALKYRIE)) { const char *who; int i, powercnt; @@ -3910,14 +4236,44 @@ maybe_wail(void) int saving_grace(int dmg) { - if (!u.usaving_grace && (u.uhp <= dmg) - && (u.uhp * 100 / u.uhpmax) > 90) { + if (dmg < 0) { + impossible("saving_grace check for negative damage? (%d)", dmg); + return 0; + } + + if (!u.usaving_grace && dmg >= u.uhp && (u.uhp * 100 / u.uhpmax) > 90) { + /* saving_grace doesn't have it's own livelog classification; + we might invent one, or perhaps use LL_LIFESAVE, but surviving + certain death (or preserving worn amulet of life saving) via + saving-grace feels like breaking a conduct; not sure how best + 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", + dmg, u.uhp, u.uhpmax); + + /* note: this could reduce dmg to 0 if u.uhpmax==1 */ dmg = u.uhp - 1; - u.usaving_grace = TRUE; /* used up */ + u.usaving_grace = 1; /* used up */ + end_running(TRUE); + if (u.usleep) + unmul("Suddenly you wake up!"); + if (is_fainted()) + reset_faint(); } return dmg; } +/* show a message how much damage you received */ +void +showdamage(int dmg) +{ + if (!iflags.showdamage || !dmg) + return; + + pline("[HP %i, %i left]", -dmg, Upolyd ? u.mh : u.uhp); +} + void losehp(int n, const char *knam, schar k_format) { @@ -3928,17 +4284,18 @@ losehp(int n, const char *knam, schar k_format) return; } #endif - gc.context.botl = TRUE; /* u.uhp or u.mh is changing */ + disp.botl = TRUE; /* u.uhp or u.mh is changing */ end_running(TRUE); if (Upolyd) { u.mh -= n; + showdamage(n); if (u.mhmax < u.mh) u.mhmax = u.mh; if (u.mh < 1) { /* copy this code here as well, in case we done() in rehumanize */ - gk.killer.format = k_format; - if (gk.killer.name != knam) /* the thing that killed you */ - Strcpy(gk.killer.name, knam ? knam : ""); + svk.killer.format = k_format; + if (svk.killer.name != knam) /* the thing that killed you */ + Strcpy(svk.killer.name, knam ? knam : ""); rehumanize(); } else if (n > 0 && u.mh * 10 < u.mhmax && Unchanging) { @@ -3949,12 +4306,13 @@ losehp(int n, const char *knam, schar k_format) n = saving_grace(n); u.uhp -= n; + showdamage(n); if (u.uhp > u.uhpmax) u.uhpmax = u.uhp; /* perhaps n was negative */ if (u.uhp < 1) { - gk.killer.format = k_format; - if (gk.killer.name != knam) /* the thing that killed you */ - Strcpy(gk.killer.name, knam ? knam : ""); + svk.killer.format = k_format; + if (svk.killer.name != knam) /* the thing that killed you */ + Strcpy(svk.killer.name, knam ? knam : ""); urgent_pline("You die..."); done(DIED); } else if (n > 0 && u.uhp * 10 < u.uhpmax) { @@ -4029,8 +4387,8 @@ weight_cap(void) int inv_weight(void) { - register struct obj *otmp = gi.invent; - register int wt = 0; + struct obj *otmp = gi.invent; + int wt = 0; while (otmp) { if (otmp->oclass == COIN_CLASS) @@ -4090,8 +4448,8 @@ check_capacity(const char *str) int inv_cnt(boolean incl_gold) { - register struct obj *otmp = gi.invent; - register int ct = 0; + struct obj *otmp = gi.invent; + int ct = 0; while (otmp) { if (incl_gold || otmp->invlet != GOLD_SYM) @@ -4177,6 +4535,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) @@ -4192,6 +4551,31 @@ spot_checks(coordxy x, coordxy y, schar old_typ) } } +/* calculate x/y, rounding as appropriate */ +int +rounddiv(long x, int y) +{ + int r, m; + int divsgn = 1; + + if (y == 0) + panic("division by zero in rounddiv"); + else if (y < 0) { + divsgn = -divsgn; + y = -y; + } + if (x < 0) { + divsgn = -divsgn; + x = -x; + } + r = (int) (x / y); + m = x % y; + if (2 * m >= y) + r++; + + return divsgn * r; +} + /* once per move, check for damage (or other effects) from being in a hostile * environment */ void @@ -4224,9 +4608,9 @@ environment_damages_u(void) /* We have just entered a throne room; check if we are in a demon lord lair, and * if so, spawn in that demon lord in the center of the throne room. - * argument 'roomno' is index into gr.rooms of the throne room we just entered. + * argument 'roomno' is index into svr.rooms of the throne room we just entered. * Return TRUE if a demon lord was spawned in or FALSE if not. */ -static boolean +staticfn boolean summon_thronerm_dlord(int roomno) { boolean lair; @@ -4236,16 +4620,16 @@ summon_thronerm_dlord(int roomno) return FALSE; if (boss_mndx != NON_PM - && (gm.mvitals[boss_mndx].born == 0) /* may have been summoned + && (svm.mvitals[boss_mndx].born == 0) /* may have been summoned elsewhere... */ && (is_archfiend(&mons[boss_mndx]))) { /* possible enhancement: scan for a throne in this room, and spawn the * demon lord on it if one exists. Currently, no demon lords have * thrones, though. */ - coordxy x = gr.rooms[roomno].lx + ((gr.rooms[roomno].hx - - gr.rooms[roomno].lx) / 2); - coordxy y = gr.rooms[roomno].ly + ((gr.rooms[roomno].hy - - gr.rooms[roomno].ly) / 2); + coordxy x = svr.rooms[roomno].lx + ((svr.rooms[roomno].hx - + svr.rooms[roomno].lx) / 2); + coordxy y = svr.rooms[roomno].ly + ((svr.rooms[roomno].hy - + svr.rooms[roomno].ly) / 2); struct monst *mtmp = makemon(&mons[boss_mndx], x, y, MM_NOMSG | MM_ADJACENTOK); if (!mtmp) { diff --git a/src/hacklib.c b/src/hacklib.c index fb18c614b2..f3a0c96b91 100644 --- a/src/hacklib.c +++ b/src/hacklib.c @@ -1,14 +1,14 @@ -/* NetHack 3.7 hacklib.c $NHDT-Date: 1596498172 2020/08/03 23:42:52 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.85 $ */ +/* NetHack 3.7 hacklib.c $NHDT-Date: 1706213796 2024/01/25 20:16:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.116 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2007. */ /* Copyright (c) Robert Patrick Rankin, 1991 */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" /* for config.h+extern.h */ + /*= Assorted 'small' utility routines. They're virtually independent of - NetHack, except that rounddiv may call panic(). setrandom calls one - of srandom(), srand48(), or srand() depending upon configuration. + NetHack. return type routine name argument type(s) boolean digit (char) @@ -24,7 +24,6 @@ char * strip_newline (char *) char * stripchars (char *, const char *, const char *) char * stripdigits (char *) - unsigned Strlen_ (const char *str, const char *, int) char * eos (char *) const char * c_eos (const char *) boolean str_start_is (const char *, const char *, boolean) @@ -46,51 +45,20 @@ const char * ordin (int) char * sitoa (int) int sgn (int) - int rounddiv (long, int) int distmin (int, int, int, int) int dist2 (int, int, int, int) boolean online2 (int, int) unsigned int coord_hash (int, int, int) unsigned int hash1 (int) int int_hash1 (int) - boolean pmatch (const char *, const char *) - boolean pmatchi (const char *, const char *) - boolean pmatchz (const char *, const char *) int strncmpi (const char *, const char *, int) char * strstri (const char *, const char *) boolean fuzzymatch (const char *, const char *, const char *, boolean) - void setrandom (void) - void init_random (fn) - void reseed_random (fn) - time_t getnow (void) - int getyear (void) - char * yymmdd (time_t) - long yyyymmdd (time_t) - long hhmmss (time_t) - char * yyyymmddhhmmss (time_t) - time_t time_from_yyyymmddhhmmss (char *) - int phase_of_the_moon (void) - boolean friday_13th (void) - int night (void) - int midnight (void) - int weekday (void) - int days_since_epoch (int) - boolean current_holidays (void) - void strbuf_init (strbuf *, const char *) - void strbuf_append (strbuf *, const char *) - void strbuf_reserve (strbuf *, int) - void strbuf_empty (strbuf *) - void strbuf_nl_to_crlf (strbuf_t *) int swapbits (int, int, int) - void shuffle_int_array (int *, int) void nh_snprintf (const char *, int, char *, size_t, const char *, ...) =*/ -static boolean pmatch_internal(const char *, const char *, boolean, - const char *); -static int weekday(void); -static int days_since_epoch(int); /* is 'c' a digit? */ boolean @@ -124,7 +92,7 @@ lowc(char c) char * lcase(char *s) { - register char *p; + char *p; for (p = s; *p; p++) if ('A' <= *p && *p <= 'Z') @@ -136,7 +104,7 @@ lcase(char *s) char * ucase(char *s) { - register char *p; + char *p; for (p = s; *p; p++) if ('a' <= *p && *p <= 'z') @@ -176,7 +144,7 @@ upwords(char *s) char * mungspaces(char *bp) { - register char c, *p, *p2; + char c, *p, *p2; boolean was_space = TRUE; for (p = p2 = bp; (c = *p) != '\0'; p++) { @@ -226,13 +194,14 @@ strip_newline(char *str) /* return the end of a string (pointing at '\0') */ char * -eos(register char *s) +eos(char *s) { while (*s) s++; /* s += strlen(s); */ return s; } +/* version of eos() which takes a const* arg and returns that result */ const char * c_eos(const char *s) { @@ -241,25 +210,18 @@ c_eos(const char *s) return s; } -/* like strlen(3) but returns unsigned and panics if string is unreasonably long */ -unsigned -Strlen_(const char *str, const char *file, int line){ - size_t len = strnlen(str, LARGEST_INT); - - if (len == LARGEST_INT) - panic("%s:%d string too long", file, line); - return (unsigned) len; -} - /* determine whether 'str' starts with 'chkstr', possibly ignoring case; * panics on huge strings */ boolean -str_start_is(const char *str, const char *chkstr, boolean caseblind) +str_start_is( + const char *str, + const char *chkstr, + boolean caseblind) { + char t1, t2; int n = LARGEST_INT; - while (n--) { - char t1, t2; + while (--n) { if (!*str) return (*chkstr == 0); /* chkstr >= str */ else if (!*chkstr) @@ -270,8 +232,10 @@ str_start_is(const char *str, const char *chkstr, boolean caseblind) if (t1 != t2) return FALSE; } +#if 0 if (n == 0) panic("string too long"); +#endif return TRUE; } @@ -438,9 +402,9 @@ ing_suffix(const char *s) char * xcrypt(const char *str, char *buf) { - register const char *p; - register char *q; - register int bitmask; + const char *p; + char *q; + int bitmask; for (bitmask = 1, p = str, q = buf; *p; q++) { *q = *p++; @@ -471,8 +435,8 @@ tabexpand( * will be truncated */ { char buf[BUFSZ + 10]; - register char *bp, *s = sbuf; - register int idx; + char *bp, *s = sbuf; + int idx; if (!*s) return sbuf; @@ -509,7 +473,7 @@ visctrl(char c) { static char visctrl_bufs[VISCTRL_NBUF][5]; static int nbuf = 0; - register int i = 0; + int i = 0; char *ccc = visctrl_bufs[nbuf]; nbuf = (nbuf + 1) % VISCTRL_NBUF; @@ -535,22 +499,23 @@ visctrl(char c) /* caller is responsible for ensuring that bp is a valid pointer to a BUFSZ buffer */ char * -stripchars(char *bp, const char *stuff_to_strip, const char *orig) +stripchars( + char *bp, + const char *stuff_to_strip, + const char *orig) { int i = 0; char *s = bp; - if (s) { - while (*orig && i < (BUFSZ - 1)) { - if (!strchr(stuff_to_strip, *orig)) { - *s++ = *orig; - i++; - } - orig++; + while (*orig && i < (BUFSZ - 1)) { + if (!strchr(stuff_to_strip, *orig)) { + *s++ = *orig; + i++; } - *s = '\0'; - } else - impossible("no output buf in stripchars"); + orig++; + } + *s = '\0'; + return bp; } @@ -568,21 +533,22 @@ stripdigits(char *s) return s; } -/* substitute a word or phrase in a string (in place) */ -/* caller is responsible for ensuring that bp points to big enough buffer */ +/* substitute a word or phrase in a string (in place); + caller is responsible for ensuring that bp points to big enough buffer */ char * -strsubst(char *bp, const char *orig, const char *replacement) +strsubst( + char *bp, + const char *orig, + const char *replacement) { char *found, buf[BUFSZ]; + /* [this could be replaced by strNsubst(bp, orig, replacement, 1)] */ - if (bp) { - /* [this could be replaced by strNsubst(bp, orig, replacement, 1)] */ - found = strstr(bp, orig); - if (found) { - Strcpy(buf, found + strlen(orig)); - Strcpy(found, replacement); - Strcat(bp, buf); - } + found = strstr(bp, orig); + if (found) { + Strcpy(buf, found + strlen(orig)); + Strcpy(found, replacement); + Strcat(bp, buf); } return bp; } @@ -601,7 +567,7 @@ strNsubst( const char *rp; unsigned len = (unsigned) strlen(orig); int ocount = 0, /* number of times 'orig' has been matched */ - rcount = 0; /* number of subsitutions made */ + rcount = 0; /* number of substitutions made */ for (bp = inoutbuf, op = workbuf; *bp && op < &workbuf[BUFSZ - 1]; ) { if ((!len || !strncmp(bp, orig, len)) && (++ocount == n || n == 0)) { @@ -661,7 +627,7 @@ findword( const char * ordin(int n) /* note: should be non-negative */ { - register int dd = n % 10; + int dd = n % 10; return (dd == 0 || dd > 3 || (n % 100) / 10 == 1) ? "th" : (dd == 1) ? "st" : (dd == 2) ? "nd" : "rd"; @@ -689,31 +655,6 @@ sgn(int n) return (n < 0) ? -1 : (n != 0); } -/* calculate x/y, rounding as appropriate */ -int -rounddiv(long x, int y) -{ - int r, m; - int divsgn = 1; - - if (y == 0) - panic("division by zero in rounddiv"); - else if (y < 0) { - divsgn = -divsgn; - y = -y; - } - if (x < 0) { - divsgn = -divsgn; - x = -x; - } - r = (int) (x / y); - m = x % y; - if (2 * m >= y) - r++; - - return divsgn * r; -} - /* distance between two points, in moves */ int distmin(coordxy x0, coordxy y0, coordxy x1, coordxy y1) @@ -772,127 +713,15 @@ online2(coordxy x0, coordxy y0, coordxy x1, coordxy y1) return (boolean) (!dy || !dx || dy == dx || dy == -dx); } -/* Deterministic hash of three coordinates (intended to be x, y, and z, but - * they don't actually have to be). In a lot of cases, z should probably also - * be ledger_no(&u.uz) so that the "z" is actually unique among levels; mere - * depth is not unique due to having levels in multiple branches at the same - * depth. - * Throws ubirthday and sysopt.serverseed into the hash so that the hash should - * be (practically) unique among the same coordinates in different games, so the - * player shouldn't be able to get the result out of the visible game state. - * (Note that sysopt.serverseed is the value of SERVERSEED plus a random number - * generated at game start). - */ -unsigned int -coord_hash(int x, int y, int z) -{ - const int magic_number = 0x45d9f3b; - /* use Cantor pairing to reduce (x,y) to a unique number */ - unsigned int a = ((x+y) * (x+y+1) / 2) + x + z + ubirthday - + sysopt.serverseed; - a = a * magic_number; - a = ((a >> 16) ^ a) * magic_number; - a = ((a >> 16) ^ a); - return a; -} - -/* Deterministic hash of a single number. Useful for hashes of non-coordinate - * numbers such as object or monster ids. */ -unsigned int -hash1(int x) -{ - /* wrap around coord_hash; ignore Cantor coordinate pairing */ - return coord_hash(0, 0, x); -} - -/* hash1(), but returns a positive int, for various use cases that convert it to - * int and which it would be unsafe to just use hash1 and possibly have that - * value converted to negative. */ -int -int_hash1(int x) -{ - unsigned int hash = hash1(x); - while (hash > INT_MAX) - hash /= 2; - return (int) hash; -} - -/* guts of pmatch(), pmatchi(), and pmatchz(); - match a string against a pattern */ -static boolean -pmatch_internal(const char *patrn, const char *strng, - boolean ci, /* True => case-insensitive, - False => case-sensitive */ - const char *sk) /* set of characters to skip */ -{ - char s, p; - /* - * Simple pattern matcher: '*' matches 0 or more characters, '?' matches - * any single character. Returns TRUE if 'strng' matches 'patrn'. - */ - pmatch_top: - if (!sk) { - s = *strng++; - p = *patrn++; /* get next chars and pre-advance */ - } else { - /* fuzzy match variant of pmatch; particular characters are ignored */ - do { - s = *strng++; - } while (strchr(sk, s)); - do { - p = *patrn++; - } while (strchr(sk, p)); - } - if (!p) /* end of pattern */ - return (boolean) (s == '\0'); /* matches iff end of string too */ - else if (p == '*') /* wildcard reached */ - return (boolean) ((!*patrn - || pmatch_internal(patrn, strng - 1, ci, sk)) - ? TRUE - : s ? pmatch_internal(patrn - 1, strng, ci, sk) - : FALSE); - else if ((ci ? lowc(p) != lowc(s) : p != s) /* check single character */ - && (p != '?' || !s)) /* & single-char wildcard */ - return FALSE; /* doesn't match */ - else /* return pmatch_internal(patrn, strng, ci, sk); */ - goto pmatch_top; /* optimize tail recursion */ -} - -/* case-sensitive wildcard match */ -boolean -pmatch(const char *patrn, const char *strng) -{ - return pmatch_internal(patrn, strng, FALSE, (const char *) 0); -} - -/* case-insensitive wildcard match */ -boolean -pmatchi(const char *patrn, const char *strng) -{ - return pmatch_internal(patrn, strng, TRUE, (const char *) 0); -} - -#if 0 -/* case-insensitive wildcard fuzzymatch; - NEVER WORKED AS INTENDED but fortunately isn't needed */ -boolean -pmatchz(const char *patrn, const char *strng) -{ - /* ignore spaces, tabs (just in case), dashes, and underscores */ - static const char fuzzychars[] = " \t-_"; - - return pmatch_internal(patrn, strng, TRUE, fuzzychars); -} -#endif - #ifndef STRNCMPI /* case insensitive counted string comparison */ /*{ aka strncasecmp }*/ int -strncmpi(register const char *s1, register const char *s2, - register int n) /*(should probably be size_t, which is unsigned)*/ +strncmpi( + const char *s1, const char *s2, + int n) /*(should probably be size_t, which is unsigned)*/ { - register char t1, t2; + char t1, t2; while (n--) { if (!*s2) @@ -913,8 +742,8 @@ strncmpi(register const char *s1, register const char *s2, char * strstri(const char *str, const char *sub) { - register const char *s1, *s2; - register int i, k; + const char *s1, *s2; + int i, k; #define TABSIZ 0x20 /* 0x40 would be case-sensitive */ char tstr[TABSIZ], tsub[TABSIZ]; /* nibble count tables */ #if 0 @@ -956,10 +785,12 @@ strstri(const char *str, const char *sub) /* compare two strings for equality, ignoring the presence of specified characters (typically whitespace) and possibly ignoring case */ boolean -fuzzymatch(const char *s1, const char *s2, const char *ignore_chars, - boolean caseblind) +fuzzymatch( + const char *s1, const char *s2, + const char *ignore_chars, + boolean caseblind) { - register char c1, c2; + char c1, c2; do { while ((c1 = *s1++) != '\0' && strchr(ignore_chars, c1) != 0) @@ -999,680 +830,6 @@ fuzzymatch(const char *s1, const char *s2, const char *ignore_chars, #define LOCALTIME_type time_t * #endif -static struct tm *getlt(void); - -/* Sets the seed for the random number generator */ -#ifdef USE_ISAAC64 - -static void -set_random(unsigned long seed, - int (*fn)(int)) -{ - init_isaac64(seed, fn); -} - -#else /* USE_ISAAC64 */ - -/*ARGSUSED*/ -static void -set_random(unsigned long seed, - int (*fn)(int) UNUSED) -{ - /* - * The types are different enough here that sweeping the different - * routine names into one via #defines is even more confusing. - */ -# ifdef RANDOM /* srandom() from sys/share/random.c */ - srandom((unsigned int) seed); -# else -# if defined(__APPLE__) || defined(BSD) || defined(LINUX) \ - || defined(ULTRIX) || defined(CYGWIN32) /* system srandom() */ -# if defined(BSD) && !defined(POSIX_TYPES) && defined(SUNOS4) - (void) -# endif - srandom((int) seed); -# else -# ifdef UNIX /* system srand48() */ - srand48((long) seed); -# else /* poor quality system routine */ - srand((int) seed); -# endif -# endif -# endif -} - -#endif /* USE_ISAAC64 */ - -/* An appropriate version of this must always be provided in - port-specific code somewhere. It returns a number suitable - as seed for the random number generator */ -extern unsigned long sys_random_seed(void); - -/* - * Initializes the random number generator. - * Only call once. - */ -void -init_random(int (*fn)(int)) -{ - set_random(sys_random_seed(), fn); -} - -/* Reshuffles the random number generator. */ -void -reseed_random(int (*fn)(int)) -{ - /* only reseed if we are certain that the seed generation is unguessable - * by the players. */ - if (has_strong_rngseed) - init_random(fn); -} - -time_t -getnow(void) -{ - time_t datetime = 0; - - (void) time((TIME_type) &datetime); - return datetime; -} - -static struct tm * -getlt(void) -{ - time_t date = getnow(); - - return localtime((LOCALTIME_type) &date); -} - -int -getyear(void) -{ - return (1900 + getlt()->tm_year); -} - -#if 0 -/* This routine is no longer used since in 20YY it yields "1YYmmdd". */ -char * -yymmdd(time_t date) -{ - static char datestr[10]; - struct tm *lt; - - if (date == 0) - lt = getlt(); - else - lt = localtime((LOCALTIME_type) &date); - - Sprintf(datestr, "%02d%02d%02d", - lt->tm_year, lt->tm_mon + 1, lt->tm_mday); - return datestr; -} -#endif - -long -yyyymmdd(time_t date) -{ - long datenum; - struct tm *lt; - - if (date == 0) - lt = getlt(); - else - lt = localtime((LOCALTIME_type) &date); - - /* just in case somebody's localtime supplies (year % 100) - rather than the expected (year - 1900) */ - if (lt->tm_year < 70) - datenum = (long) lt->tm_year + 2000L; - else - datenum = (long) lt->tm_year + 1900L; - /* yyyy --> yyyymm */ - datenum = datenum * 100L + (long) (lt->tm_mon + 1); - /* yyyymm --> yyyymmdd */ - datenum = datenum * 100L + (long) lt->tm_mday; - return datenum; -} - -long -hhmmss(time_t date) -{ - long timenum; - struct tm *lt; - - if (date == 0) - lt = getlt(); - else - lt = localtime((LOCALTIME_type) &date); - - timenum = lt->tm_hour * 10000L + lt->tm_min * 100L + lt->tm_sec; - return timenum; -} - -char * -yyyymmddhhmmss(time_t date) -{ - long datenum; - static char datestr[15]; - struct tm *lt; - - if (date == 0) - lt = getlt(); - else - lt = localtime((LOCALTIME_type) &date); - - /* just in case somebody's localtime supplies (year % 100) - rather than the expected (year - 1900) */ - if (lt->tm_year < 70) - datenum = (long) lt->tm_year + 2000L; - else - datenum = (long) lt->tm_year + 1900L; - Snprintf(datestr, sizeof datestr, "%04ld%02d%02d%02d%02d%02d", - datenum, lt->tm_mon + 1, - lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec); - debugpline1("yyyymmddhhmmss() produced date string %s", datestr); - return datestr; -} - -time_t -time_from_yyyymmddhhmmss(char *buf) -{ - int k; - time_t timeresult = (time_t) 0; - struct tm t, *lt; - char *d, *p, y[5], mo[3], md[3], h[3], mi[3], s[3]; - - if (buf && strlen(buf) == 14) { - d = buf; - p = y; /* year */ - for (k = 0; k < 4; ++k) - *p++ = *d++; - *p = '\0'; - p = mo; /* month */ - for (k = 0; k < 2; ++k) - *p++ = *d++; - *p = '\0'; - p = md; /* day */ - for (k = 0; k < 2; ++k) - *p++ = *d++; - *p = '\0'; - p = h; /* hour */ - for (k = 0; k < 2; ++k) - *p++ = *d++; - *p = '\0'; - p = mi; /* minutes */ - for (k = 0; k < 2; ++k) - *p++ = *d++; - *p = '\0'; - p = s; /* seconds */ - for (k = 0; k < 2; ++k) - *p++ = *d++; - *p = '\0'; - lt = getlt(); - if (lt) { - t = *lt; - t.tm_year = atoi(y) - 1900; - t.tm_mon = atoi(mo) - 1; - t.tm_mday = atoi(md); - t.tm_hour = atoi(h); - t.tm_min = atoi(mi); - t.tm_sec = atoi(s); - timeresult = mktime(&t); - } - if (timeresult == (time_t) -1) - debugpline1("time_from_yyyymmddhhmmss(%s) would have returned -1", - buf ? buf : ""); - else - return timeresult; - } - return (time_t) 0; -} - -/* - * moon period = 29.53058 days ~= 30, year = 365.2422 days - * days moon phase advances on first day of year compared to preceding year - * = 365.2422 - 12*29.53058 ~= 11 - * years in Metonic cycle (time until same phases fall on the same days of - * the month) = 18.6 ~= 19 - * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30 - * (29 as initial condition) - * current phase in days = first day phase + days elapsed in year - * 6 moons ~= 177 days - * 177 ~= 8 reported phases * 22 - * + 11/22 for rounding - */ -int -phase_of_the_moon(void) /* 0-7, with 0: new, 4: full */ -{ - register struct tm *lt = getlt(); - register int epact, diy, goldn; - - diy = lt->tm_yday; - goldn = (lt->tm_year % 19) + 1; - epact = (11 * goldn + 18) % 30; - if ((epact == 25 && goldn > 11) || epact == 24) - epact++; - - return ((((((diy + epact) * 6) + 11) % 177) / 22) & 7); -} - -boolean -friday_13th(void) -{ - register struct tm *lt = getlt(); - - /* tm_wday (day of week; 0==Sunday) == 5 => Friday */ - return (boolean) (lt->tm_wday == 5 && lt->tm_mday == 13); -} - -int -night(void) -{ - register int hour = getlt()->tm_hour; - - return (hour < 6 || hour > 21); -} - -int -midnight(void) -{ - return (getlt()->tm_hour == 0); -} - -/* Returns current day of week (0==Sunday through 6==Saturday) */ -static int -weekday(void) -{ - struct tm *lt = getlt(); - return lt->tm_wday; -} - -/* Return the number of days since 01/01/0000 on the Gregorian calendar, - * inclusive of the start date but not the current date. - * Argument should be a yyyymmdd int. */ -static int -days_since_epoch(int ymd) -{ - const int year = ymd / 10000; - const int month = (ymd % 10000) / 100; - const int date = ymd % 100; - int monthlen[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 }; - int i; - - /* insert Feb 29 if this is a leap year */ - if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) { - monthlen[1] += 1; - } - - /* baseline: 365 days per year */ - int days = 365 * year; - - /* Gregorian leap years. - * These will ignore that year 0 was a leap year, but that shouldn't matter - * if all you're doing with this function is subtracting two epoch dates - * from each other. - * This does year - 1 so as NOT to count the current year, which the end of - * February may or may not have happened in yet. */ - /* 1 per year divisible by 4. */ - days += (year - 1) / 4; - /* Minus a leap year for each year divisible by 100. */ - days -= (year - 1) / 100; - /* Plus a leap year for each year divisible by 400. */ - days += (year - 1) / 400; - /* Plus the partial amount of days this year. */ - for (i = 0; i < (month - 1); ++i) { - days += monthlen[i]; - } - /* Plus the amount of days so far this month. */ - days += date; - return days; -} - -/* Return a bitmask of holidays it is today. - * This uses a bitmask rather than an enum so that it can extend to "seasons" - * which extend over multiple days and possibly intersect with different - * holidays (for instance, Hanukkah can overlap with Christmas). */ -int -current_holidays(void) -{ - const int ymd = yyyymmdd((time_t) 0); - const int year = ymd / 10000; - const int month = (ymd % 10000) / 100; /* 1..12 */ - const int date = ymd % 100; /* 1..31 */ - const int today_epoch = days_since_epoch(ymd); - const int hour = getlt()->tm_hour; - int retmask = 0; - - /* These cache the value of the last time this function did a full holiday - * computation, to avoid recomputing it all over again unless needed. */ - static int cached_ymd = 0, cached_hour = 0, cached_retmask = 0; - boolean recompute = FALSE; - /* recompute if: - * 1. first ever call (no need to check cached_hour == -1 because cached_ymd - * of 0 is always less than ymd in that case) - * 2. date has changed - * 3. this was last called before 6 pm and it is now after 6 pm (this - * changes the Jewish day which can begin or end a holiday, see below) - */ - if (cached_ymd < ymd || (cached_hour < 18 && hour >= 18)) { - recompute = TRUE; - } - cached_ymd = ymd; - cached_hour = hour; - if (!recompute) { - return cached_retmask; - } - - /* Simple holidays observed yearly on the Gregorian calendar. */ - if (month == 1 && date == 1) { - retmask |= HOLIDAY_NEW_YEARS; - } - if (month == 2 && date == 2) { - retmask |= HOLIDAY_GROUNDHOG_DAY; - } - if (month == 2 && date == 14) { - retmask |= HOLIDAY_VALENTINES_DAY; - } - if (month == 3 && date == 14) { - retmask |= HOLIDAY_PI_DAY; - } - if (month == 4 && date == 1) { - retmask |= HOLIDAY_APRIL_FOOLS; - } - if (month == 7 && date == 1) { - retmask |= HOLIDAY_CANADA_DAY; - } - if (month == 10 && date == 31) { - retmask |= HOLIDAY_HALLOWEEN; - } - if (month == 11 && date >= 1 && date <= 2) { - retmask |= HOLIDAY_LOS_MUERTOS; - } - if (month == 11 && date >= 22 && date <= 28 && weekday() == 4) { - retmask |= HOLIDAY_THANKSGIVING; - } - if (month == 12 && date >= 24 && date <= 25) { - /* counts Christmas Eve too */ - retmask |= HOLIDAY_CHRISTMAS; - } - - /* Now for the tough stuff. */ - { - /* Modified form of Gauss's algorithm to compute the date of Easter, also - * known as the "Computus" algorithm. Relies on a not-inconsiderable amount - * of voodoo magic. */ - int a = year % 19; - int b = year / 100; - int c = year % 100; - int d = b / 4; - int e = b % 4; - int f = (b + 8) / 25; - int g = (b - f + 1) / 3; - int h = ((19 * a) + b - d - g + 15) % 30; - int ii = c / 4; /* note: 'i' declared already in this function */ - int k = c % 4; - int L = (32 + (2 * e) + (2 * ii) - h - k) % 7; - int m = (a + (11 * h) + (22 * L)) / 451; - int eastermonth = (h + L - (7 * m) + 114) / 31; - int easterday = (h + L - (7 * m) + 114) % 31 + 1; - if (month == eastermonth && date == easterday) { - retmask |= HOLIDAY_EASTER; - } - /* Mardi Gras is based on Easter, 47 days before it */ - { - int easterymd = year * 10000 + eastermonth * 100 + easterday; - if (days_since_epoch(easterymd) - today_epoch == 47) { - retmask |= HOLIDAY_MARDI_GRAS; - } - } - } - - { - /* The Islamic calendar begins on 16 Jul 622 in the Julian calendar (19 - * July in the Gregorian calendar). This is only one of many rule-based - * reckonings for it. - * The (lunar) year contains 12 months, in a 30 29 30 29... pattern, - * except in leap years where the final month is 30 days. Leap years - * repeat every 30-year cycle, on years 2, 5, 7, 10, 13, 16, 18, 21, 24, - * 26, 29. This closely but imperfectly approximates the lunar period. - * TODO: in year 3000 or so, check if it's drifted off by a day or two, - * and fix it if it has. - * Begin by calculating a delta number of days since the start of the - * Islamic calendar. - * Every 30-year period contains 10631 days, so we can immediately take - * that delta modulo 10631 to ignore however many 30-year periods that - * is. - * This algorithm is not going to pinpoint Ramadan or other holidays - * exactly, particularly since they depend on manual observation. But it - * should get pretty close. - */ - int lunar_leap[30] = { 0,1,0,0,1,0,1,0,0,1,0,0,1,0,0, - 1,0,1,0,0,1,0,0,1,0,1,0,0,1,0 }; - - /* There are 166 days in the partial year 622. Start with that. */ - int date_delta = today_epoch - days_since_epoch(6220719); - int cycyear; - int month_ctr; - - /* Now cut off as many 30-year periods as possible. */ - date_delta = date_delta % 10631; - /* Then cut off year by year until we reach the current lunar year. */ - for (cycyear = 0; cycyear < 30; ++cycyear) { - int this_year_len = 354 + lunar_leap[cycyear]; - if (date_delta < this_year_len) { - break; - /* cycyear stays in scope so we can tell below if it is - * currently a leap year and need to adjust the last month - * accordingly */ - } - date_delta -= this_year_len; - } - if (date_delta < 0 || cycyear == 30) { - impossible("holiday: bad math finding lunar year"); - date_delta = 0; - } - /* Then using whatever is remaining, find the month and date of the - * current day. */ - int islam_month = 0, islam_date = 0; - for (month_ctr = 0; month_ctr < 12; ++month_ctr) { - int month_len = (month_ctr % 2 == 1) ? 29 : 30; - if (month_ctr == 11) - month_len += lunar_leap[cycyear]; - if (date_delta < month_len) { - islam_month = month_ctr + 1; /* convert back to human-readable */ - islam_date = date_delta + 1; - break; - } - date_delta -= month_len; - } - if (date_delta < 0 || month_ctr >= 12) { - impossible("holiday: bad math finding lunar month/date"); - } - if (islam_month == 9) { - retmask |= HOLIDAY_RAMADAN; - } - if (islam_month == 10 && islam_date == 1) { - retmask |= HOLIDAY_EID_AL_FITR; - } - } - - { - /* The Hebrew calendar is even more complicated than the Islamic - * calendar, but the real problem with it is that it depends on - * obtaining the precise instant of the new moon. This is difficult to - * do because the lunar synodic period fluctuates, and getting a new - * moon wrong by even an hour can throw off the date of the new year by - * up to two days. In lieu of piling a bunch of orbital mechanics - * calculations on top of the Hebrew calendar to make it work for - * arbitrary dates, this will just use dates for the Hebrew new year - * obtained from hebcal.com. - */ - const int heb_new_year[30] = { - 20200919, 20210907, 20220926, 20230916, 20241003, /* 2020-2024 */ - 20250923, 20260912, 20271002, 20280921, 20290910, /* 2025-2029 */ - 20300928, 20310918, 20320906, 20330924, 20340914, /* 2030-2034 */ - 20351004, 20360922, 20370910, 20380930, 20390919, /* 2035-2039 */ - 20400908, 20410926, 20420915, 20431005, 20440922, /* 2040-2044 */ - 20450912, 20461001, 20470921, 20480908, 20490927, /* 2045-2049 */ - }; - - if (year > 2048) { - pline("This game is still being played after 2048? Cool."); - impossible("no data for Hebrew calendar in year %d", year); - } - else if (year < 2020) { - pline("Time travel to the past? Or fix your system clock."); - impossible("no data for Hebrew calendar in year %d", year); - } - else { - int tmp_epoch_today = today_epoch; - /* The Gregorian day begins at midnight, but the Hebrew day begins - * at sunset. Assume sunset is at 6 PM; if it's after that, advance - * the day by 1. */ - if (hour >= 18) { - tmp_epoch_today += 1; - } - /* ymd is no longer safe to use in this computation */ - - /* advance index in heb_new_year until it points at the current - * Hebrew year */ - int index = 0; - int epoch_last_newyear, epoch_next_newyear; - do { - epoch_last_newyear = days_since_epoch(heb_new_year[index]); - epoch_next_newyear = days_since_epoch(heb_new_year[index + 1]); - index++; - } while (epoch_next_newyear <= tmp_epoch_today); - - int heb_year_length = epoch_next_newyear - epoch_last_newyear; - - /* The leap year inserts a 30-day month in the middle of the year, - * represented by a 0 here. */ - int heb_month_len[13] = { 30,29,30,29,30,0,29,30,29,30,29,30,29 }; - if (heb_year_length >= 383 && heb_year_length <= 385) { - heb_month_len[5] = 30; /* insert Adar I */ - } - else if (heb_year_length < 353 || heb_year_length > 355) { - /* if not a leap year, year must be 353 to 355 days */ - impossible("illegal Hebrew year length %d", heb_year_length); - heb_year_length = 354; /* try for graceful fallback */ - } - if (heb_year_length % 10 == 3) { /* short year */ - heb_month_len[2] -= 1; /* deduct 1 day from Kislev */ - } - else if (heb_year_length % 10 == 5) { /* full year */ - heb_month_len[1] += 1; /* add 1 day to Cheshvan */ - } - int hebrew_month = 0, hebrew_date = 0; - int date_delta = tmp_epoch_today - epoch_last_newyear; - int month_ctr; - for (month_ctr = 0; month_ctr < 13; ++month_ctr) { - if (date_delta < heb_month_len[month_ctr]) { - hebrew_month = month_ctr + 1; - hebrew_date = date_delta + 1; - break; - } - date_delta -= heb_month_len[month_ctr]; - } - if (date_delta < 0 || month_ctr == 13) { - impossible("holiday: bad math finding hebrew month/date"); - } - if (hebrew_month == 1 && hebrew_date >= 1 && hebrew_date <= 2) { - retmask |= HOLIDAY_ROSH_HASHANAH; - } - if (hebrew_month == 1 && hebrew_date == 10) { - retmask |= HOLIDAY_YOM_KIPPUR; - } - /* There are a number of different ways to observe Passover; this - * tracks the first two days but not the rest of its week. */ - if (hebrew_month == 8 && hebrew_date >= 15 && hebrew_date <= 16) { - retmask |= HOLIDAY_PASSOVER; - } - /* Judging the end date of Hanukkah depends on whether Kislev was - * reduced in length or not. */ - if ((hebrew_month == 3 && hebrew_date >= 25) - || (hebrew_month == 4 - && (hebrew_date <= (heb_month_len[2] == 30 ? 2 : 3)))) { - retmask |= HOLIDAY_HANUKKAH; - } - } - } - cached_retmask = retmask; /* for next time */ - return retmask; -} - -/* strbuf_init() initializes strbuf state for use */ -void -strbuf_init(strbuf_t *strbuf) -{ - strbuf->str = NULL; - strbuf->len = 0; -} - -/* strbuf_append() appends given str to strbuf->str */ -void -strbuf_append(strbuf_t *strbuf, const char *str) -{ - int len = (int) strlen(str) + 1; - - strbuf_reserve(strbuf, - len + (strbuf->str ? (int) strlen(strbuf->str) : 0)); - Strcat(strbuf->str, str); -} - -/* strbuf_reserve() ensure strbuf->str has storage for len characters */ -void -strbuf_reserve(strbuf_t *strbuf, int len) -{ - if (strbuf->str == NULL) { - strbuf->str = strbuf->buf; - strbuf->str[0] = '\0'; - strbuf->len = (int) sizeof strbuf->buf; - } - - if (len > strbuf->len) { - char *oldbuf = strbuf->str; - - strbuf->len = len + (int) sizeof strbuf->buf; - strbuf->str = (char *) alloc(strbuf->len); - Strcpy(strbuf->str, oldbuf); - if (oldbuf != strbuf->buf) - free((genericptr_t) oldbuf); - } -} - -/* strbuf_empty() frees allocated memory and set strbuf to initial state */ -void -strbuf_empty(strbuf_t *strbuf) -{ - if (strbuf->str != NULL && strbuf->str != strbuf->buf) - free((genericptr_t) strbuf->str); - strbuf_init(strbuf); -} - -/* strbuf_nl_to_crlf() converts all occurences of \n to \r\n */ -void -strbuf_nl_to_crlf(strbuf_t *strbuf) -{ - if (strbuf->str) { - int len = (int) strlen(strbuf->str); - int count = 0; - char *cp = strbuf->str; - - while (*cp) - if (*cp++ == '\n') - count++; - if (count) { - strbuf_reserve(strbuf, len + count + 1); - for (cp = strbuf->str + len + count; count; --cp) - if ((*cp = cp[-count]) == '\n') { - *--cp = '\r'; - --count; - } - } - } -} - /* swapbits(val, bita, bitb) swaps bit a with bit b in val */ int swapbits(int val, int bita, int bitb) @@ -1682,21 +839,6 @@ swapbits(int val, int bita, int bitb) return (val ^ ((tmp << bita) | (tmp << bitb))); } -/* randomize the given list of numbers 0 <= i < count */ -void -shuffle_int_array(int *indices, int count) -{ - int i, iswap, temp; - - for (i = count - 1; i > 0; i--) { - if ((iswap = rn2(i + 1)) == i) - continue; - temp = indices[i]; - indices[i] = indices[iswap]; - indices[iswap] = temp; - } -} - DISABLE_WARNING_FORMAT_NONLITERAL /* @@ -1713,7 +855,7 @@ DISABLE_WARNING_FORMAT_NONLITERAL */ void nh_snprintf( - const char *func, int line, + const char *func UNUSED, int line UNUSED, char *str, size_t size, const char *fmt, ...) { @@ -1724,17 +866,19 @@ nh_snprintf( n = vsnprintf(str, size, fmt, ap); va_end(ap); if (n < 0 || (size_t) n >= size) { /* is there a problem? */ +#if 0 +TODO: add set_impossible(), impossible -> func pointer, + test funcpointer before call impossible("snprintf %s: func %s, file line %d", (n < 0) ? "format error" : "overflow", func, line); +#endif str[size - 1] = '\0'; /* make sure it is nul terminated */ } } RESTORE_WARNING_FORMAT_NONLITERAL -#ifdef ENHANCED_SYMBOLS - /* Unicode routines */ int @@ -1776,6 +920,41 @@ unicodeval_to_utf8str(int uval, uint8 *buffer, size_t bufsz) *b = '\0'; /* NUL terminate */ return 1; } -#endif /* ENHANCED_SYMBOLS */ + +int +case_insensitive_comp(const char *s1, const char *s2) +{ + uchar u1, u2; + + for (;; s1++, s2++) { + u1 = (uchar) *s1; + if (isupper(u1)) + u1 = (uchar) tolower(u1); + u2 = (uchar) *s2; + if (isupper(u2)) + u2 = (uchar) tolower(u2); + if (u1 == '\0' || u1 != u2) + break; + } + 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; +} /*hacklib.c*/ diff --git a/src/insight.c b/src/insight.c index b5c7d58639..31114d0530 100644 --- a/src/insight.c +++ b/src/insight.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 insight.c $NHDT-Date: 1683710630 2023/05/10 09:23:50 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.95 $ */ +/* 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. */ @@ -6,7 +6,7 @@ * Enlightenment and Conduct+Achievements and Vanquished+Extinct+Geno'd * and stethoscope/probing feedback. * - * Most code used to reside in cmd.c, presumeably because ^X was originally + * Most code used to reside in cmd.c, presumably because ^X was originally * a wizard mode command and the majority of those are in that file. * Some came from end.c where it is used during end of game disclosure. * And some came from priest.c that had once been in pline.c. @@ -14,28 +14,30 @@ #include "hack.h" -static void enlght_out_attr(int, const char *); -static void enlght_out(const char *); -static void enlght_line(const char *, const char *, const char *, - const char *); -static char *enlght_combatinc(const char *, int, int, char *); -static void enlght_halfdmg(int, int); -static boolean walking_on_water(void); -static boolean cause_known(int); -static char *attrval(int, int, char *); -static char *fmt_elapsed_time(char *, int); -static void background_enlightenment(int, int); -static void basics_enlightenment(int, int); -static void characteristics_enlightenment(int, int); -static void one_characteristic(int, int, int); -static void status_enlightenment(int, int); -static void weapon_insight(int); -static void attributes_enlightenment(int, int); -static void show_achievements(int); -static int QSORTCALLBACK vanqsort_cmp(const genericptr, const genericptr); -static int num_extinct(void); -static int num_gone(int, int *); -static char *size_str(int); +staticfn void enlght_out_attr(int, const char *); +staticfn void enlght_out(const char *); +staticfn void enlght_line(const char *, const char *, const char *, + const char *); +staticfn char *enlght_combatinc(const char *, int, int, char *); +staticfn void enlght_halfdmg(int, int); +staticfn boolean walking_on_water(void); +staticfn boolean cause_known(int); +staticfn char *attrval(int, int, char *); +staticfn char *fmt_elapsed_time(char *, int); +staticfn void background_enlightenment(int, int); +staticfn void basics_enlightenment(int, int); +staticfn void characteristics_enlightenment(int, int); +staticfn void one_characteristic(int, int, int); +staticfn void status_enlightenment(int, int); +staticfn void weapon_insight(int); +staticfn void attributes_enlightenment(int, int); +staticfn void show_achievements(int); +staticfn int QSORTCALLBACK vanqsort_cmp(const genericptr, const genericptr); +staticfn int num_extinct(void); +staticfn int num_gone(int, int *); +staticfn char *size_str(int); +staticfn void fiend_insight(boolean); +staticfn void item_resistance_message(int, const char *, int); extern const char *const hu_stat[]; /* hunger status from eat.c */ extern const char *const enc_stat[]; /* encumbrance status from botl.c */ @@ -106,45 +108,66 @@ static struct ll_achieve_msg achieve_msg [] = { #define you_are(attr, ps) enl_msg(You_, are, were, (attr), (ps)) #define you_have(attr, ps) enl_msg(You_, have, had, (attr), (ps)) #define you_can(attr, ps) enl_msg(You_, can, could, (attr), (ps)) -#define you_have_been(goodthing) enl_msg(You_, have_been, were, (goodthing), "") +#define you_have_been(goodthing) \ + enl_msg(You_, have_been, were, (goodthing), "") #define you_have_never(badthing) \ enl_msg(You_, have_never, never, (badthing), "") #define you_have_X(something) \ enl_msg(You_, have, (const char *) "", (something), "") -static void +staticfn void enlght_out_attr(int attr, const char *buf) { - int clr = 0; - if (ge.en_via_menu) { anything any; any = cg.zeroany; - add_menu(ge.en_win, &nul_glyphinfo, &any, 0, 0, attr, clr, buf, - MENU_ITEMFLAGS_NONE); + add_menu(ge.en_win, &nul_glyphinfo, &any, '\0', '\0', attr, NO_COLOR, + buf, MENU_ITEMFLAGS_NONE); } else putstr(ge.en_win, attr, buf); } -static void +staticfn void enlght_out(const char *buf) { enlght_out_attr(ATR_NONE, buf); } -static void -enlght_line(const char *start, const char *middle, const char *end, - const char *ps) +staticfn void +enlght_line( + const char *start, + const char *middle, + const char *end, + const char *ps) { +#ifndef NO_ENLGHT_CONTRACTIONS + static const struct contrctn { + const char *twowords, *contrctn; + } contra[] = { + { " are not ", " aren't " }, + { " were not ", " weren't " }, + { " have not ", " haven't " }, + { " had not ", " hadn't " }, + { " can not ", " can't " }, + { " could not ", " couldn't " }, + }; + int i; +#endif char buf[BUFSZ]; Sprintf(buf, " %s%s%s%s.", start, middle, end, ps); +#ifndef NO_ENLGHT_CONTRACTIONS + if (strstri(buf, " not ")) { /* TODO: switch to libc strstr() */ + for (i = 0; i < SIZE(contra); ++i) + (void) strsubst(buf, contra[i].twowords, contra[i].contrctn); + } +#endif enlght_out(buf); } /* format increased chance to hit or damage or defense (Protection) */ -static char * +staticfn char * enlght_combatinc(const char *inctyp, int incamt, int final, char *outbuf) { const char *modif, *bonus; @@ -181,7 +204,7 @@ enlght_combatinc(const char *inctyp, int incamt, int final, char *outbuf) } /* report half physical or half spell damage */ -static void +staticfn void enlght_halfdmg(int category, int final) { const char *category_name; @@ -204,7 +227,7 @@ enlght_halfdmg(int category, int final) } /* is hero actively using water walking capability on water (or lava)? */ -static boolean +staticfn boolean walking_on_water(void) { if (u.uinwater || Levitation || Flying) @@ -247,11 +270,11 @@ trap_predicament(char *outbuf, int final, boolean wizxtra) /* check whether hero is wearing something that player definitely knows confers the target property; item must have been seen and its type discovered but it doesn't necessarily have to be fully identified */ -static boolean +staticfn boolean cause_known( int propindx) /* index of a property which can be conveyed by worn item */ { - register struct obj *o; + struct obj *o; long mask = W_ARMOR | W_AMUL | W_RING | W_TOOL; /* simpler than from_what()/what_gives(); we don't attempt to @@ -267,7 +290,7 @@ cause_known( } /* format a characteristic value, accommodating Strength's strangeness */ -static char * +staticfn char * attrval( int attrindx, int attrvalue, @@ -294,7 +317,7 @@ attrval( (note: for a list of more than two entries, nethack usually includes the [style-wise] optional comma before "and" but in this instance it does not) */ -static char * +staticfn char * fmt_elapsed_time(char *outbuf, int final) { int fieldcnt; @@ -353,7 +376,7 @@ enlightenment( if (ge.en_via_menu) start_menu(ge.en_win, MENU_BEHAVE_STANDARD); - Strcpy(tmpbuf, gp.plname); + Strcpy(tmpbuf, svp.plname); *tmpbuf = highc(*tmpbuf); /* same adjustment as bottom line */ /* as in background_enlightenment, when poly'd we need to use the saved gender in u.mfemale rather than the current you-as-monster gender */ @@ -422,7 +445,7 @@ enlightenment( /*ARGSUSED*/ /* display role, race, alignment and such to en_win */ -static void +staticfn void background_enlightenment(int unused_mode UNUSED, int final) { const char *role_titl, *rank_titl; @@ -433,7 +456,7 @@ background_enlightenment(int unused_mode UNUSED, int final) to access hero's saved gender-as-human/elf/&c rather than current */ innategend = (Upolyd ? u.mfemale : flags.female) ? 1 : 0; role_titl = (innategend && gu.urole.name.f) ? gu.urole.name.f - : gu.urole.name.m; + : gu.urole.name.m; rank_titl = rank_of(u.ulevel, Role_switch, innategend); enlght_out(""); /* separator after title */ @@ -502,7 +525,7 @@ background_enlightenment(int unused_mode UNUSED, int final) way sooner (in other words, didn't start that way) */ ? (!final ? "now " : "belatedly ") /* atheist (ignored in very early game) */ - : (!u.uconduct.gnostic && gm.moves > 1000L) + : (!u.uconduct.gnostic && svm.moves > 1000L) ? "nominally " /* lastly, normal case */ : "", @@ -551,6 +574,13 @@ background_enlightenment(int unused_mode UNUSED, int final) Sprintf(buf, "%s", orientations[flags.orientation].technical); you_are(buf, ""); + /* "You are left-handed." won't work well if polymorphed into something + without hands; use "You are normally left-handed." in that situation */ + Sprintf(buf, "%s%s-handed", + !strcmp(body_part(HANDED), "handed") ? "" : "normally ", + URIGHTY ? "right" : "left"); + you_are(buf, ""); + /* As of 3.6.2: dungeon level, so that ^X really has all status info as claimed by the comment below; this reveals more information than the basic status display, but that's one of the purposes of ^X; @@ -565,13 +595,13 @@ background_enlightenment(int unused_mode UNUSED, int final) !strncmp(tmpbuf, "Plane", 5) ? "Elemental " : "", tmpbuf); } else if (Is_knox(&u.uz)) { /* this gives away the fact that the knox branch is only 1 level */ - Sprintf(buf, "on the %s level", gd.dungeons[u.uz.dnum].dname); + Sprintf(buf, "on the %s level", svd.dungeons[u.uz.dnum].dname); /* TODO? maybe phrase it differently when actually inside the fort, if we're able to determine that (not trivial) */ } else { char dgnbuf[QBUFSZ]; - Strcpy(dgnbuf, gd.dungeons[u.uz.dnum].dname); + Strcpy(dgnbuf, svd.dungeons[u.uz.dnum].dname); if (!strncmpi(dgnbuf, "The ", 4)) *dgnbuf = lowc(*dgnbuf); Sprintf(tmpbuf, "level %d", @@ -585,11 +615,12 @@ background_enlightenment(int unused_mode UNUSED, int final) you_are(buf, ""); /* this is shown even if the 'time' option is off */ - if (gm.moves == 1L) { + if (svm.moves == 1L) { you_have("just started your adventure", ""); } else { /* 'turns' grates on the nerves in this context... */ - Sprintf(buf, "the dungeon %ld turn%s ago", gm.moves, plur(gm.moves)); + Sprintf(buf, "the dungeon %ld turn%s ago", + svm.moves, plur(svm.moves)); /* same phrasing for current and final: "entered" is unconditional */ enlght_line(You_, "entered ", buf, ""); } @@ -677,7 +708,7 @@ background_enlightenment(int unused_mode UNUSED, int final) /* hit points, energy points, armor class -- essential information which doesn't fit very well in other categories */ /*ARGSUSED*/ -static void +staticfn void basics_enlightenment(int mode UNUSED, int final) { static char Power[] = "energy points (spell power)"; @@ -776,7 +807,7 @@ basics_enlightenment(int mode UNUSED, int final) } /* characteristics: expanded version of bottom line strength, dexterity, &c */ -static void +staticfn void characteristics_enlightenment(int mode, int final) { char buf[BUFSZ]; @@ -795,7 +826,7 @@ characteristics_enlightenment(int mode, int final) } /* display one attribute value for characteristics_enlightenment() */ -static void +staticfn void one_characteristic(int mode, int final, int attrindx) { extern const char *const attrname[]; /* attrib.c */ @@ -889,7 +920,7 @@ one_characteristic(int mode, int final, int attrindx) } /* status: selected obvious capabilities, assorted troubles */ -static void +staticfn void status_enlightenment(int mode, int final) { boolean magic = (mode & MAGICENLIGHTENMENT) ? TRUE : FALSE; @@ -899,7 +930,7 @@ status_enlightenment(int mode, int final) /* if hero dies while dismounting, u.usteed will still be set; we want to ignore steed in that situation */ && !(final == ENL_GAMEOVERDEAD - && !strcmp(gk.killer.name, "riding accident"))); + && !strcmp(svk.killer.name, "riding accident"))); const char *steedname = (!Riding ? (char *) 0 : x_monnam(u.usteed, u.usteed->mtame ? ARTICLE_YOUR : ARTICLE_THE, @@ -1017,7 +1048,7 @@ status_enlightenment(int mode, int final) (HBlinded & FROMROLEPLAY) != 0L ? "permanently" : (HBlinded & FROMFORM) ? "innately" : (HBlinded & FROMOUTSIDE) ? "indefinitely" - /* better phrasing desparately wanted... */ + /* better phrasing desperately wanted... */ : Blindfolded_only ? "deliberately" /* timed, possibly combined with blindfold */ : "temporarily"); @@ -1060,7 +1091,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); @@ -1231,7 +1263,7 @@ status_enlightenment(int mode, int final) } /* extracted from status_enlightenment() to reduce clutter there */ -static void +staticfn void weapon_insight(int final) { char buf[BUFSZ]; @@ -1432,7 +1464,7 @@ weapon_insight(int final) * note that if the debuffs are changed, these strings should change too * also note that there are a couple minor/situational debuffs that are not * mentioned here */ -static void +staticfn void fiend_insight(boolean final) { if (fiend_adversity(PM_JUIBLEX)) @@ -1456,13 +1488,33 @@ fiend_insight(boolean final) enl_msg("Demogorgon ", "is", "was", " sapping your metabolism", ""); } +staticfn void +item_resistance_message( + int adtyp, + const char *prot_message, + int final) +{ + int protection = adtyp_resistance_obj(&gy.youmonst, adtyp); + + if (protection) { + boolean somewhat = protection < 99; + + enl_msg("Your items ", + somewhat ? "are somewhat" : "are", + somewhat ? "were somewhat" : "were", + prot_message, item_what(adtyp)); + } +} + /* attributes: intrinsics and the like, other non-obvious capabilities */ -static void -attributes_enlightenment(int unused_mode UNUSED, int final) +staticfn void +attributes_enlightenment( + int unused_mode UNUSED, + int final) { static NEARDATA const char if_surroundings_permitted[] = " if surroundings permitted"; - int ltmp, armpro; + int ltmp, armpro, warnspecies; char buf[BUFSZ]; /*\ @@ -1496,24 +1548,19 @@ attributes_enlightenment(int unused_mode UNUSED, int final) you_are("magic-protected", from_what(ANTIMAGIC)); if (Fire_resistance) you_are("fire resistant", from_what(FIRE_RES)); - if (adtyp_resistance_obj(&gy.youmonst, AD_FIRE)) - enl_msg("Your items ", "are", "were", " protected from fire", ""); + item_resistance_message(AD_FIRE, " protected from fire", final); if (Cold_resistance) you_are("cold resistant", from_what(COLD_RES)); - if (adtyp_resistance_obj(&gy.youmonst, AD_COLD)) - enl_msg("Your items ", "are", "were", " protected from cold", ""); + item_resistance_message(AD_COLD, " protected from cold", final); if (Sleep_resistance) you_are("sleep resistant", from_what(SLEEP_RES)); if (Disint_resistance) you_are("disintegration resistant", from_what(DISINT_RES)); - if (adtyp_resistance_obj(&gy.youmonst, AD_DISN)) - enl_msg("Your items ", "are", "were", - " protected from disintegration", item_what(AD_DISN)); + item_resistance_message(AD_DISN, " protected from disintegration", final); if (Shock_resistance) you_are("shock resistant", from_what(SHOCK_RES)); - if (adtyp_resistance_obj(&gy.youmonst, AD_ELEC)) - enl_msg("Your items ", "are", "were", - " protected from electric shocks", item_what(AD_ELEC)); + item_resistance_message(AD_ELEC, " protected from electric shocks", + final); if (Poison_resistance) you_are("poison resistant", from_what(POISON_RES)); if (Acid_resistance) { @@ -1522,9 +1569,7 @@ attributes_enlightenment(int unused_mode UNUSED, int final) "acid resistant"); you_are(buf, from_what(ACID_RES)); } - if (adtyp_resistance_obj(&gy.youmonst, AD_ACID)) - enl_msg("Your items ", "are", "were", " protected from acid", - item_what(AD_ACID)); + item_resistance_message(AD_ACID, " protected from acid", final); if (Drain_resistance) you_are("level-drain resistant", from_what(DRAIN_RES)); if (Sick_resistance) @@ -1544,6 +1589,9 @@ attributes_enlightenment(int unused_mode UNUSED, int final) /*** 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)); @@ -1558,39 +1606,36 @@ attributes_enlightenment(int unused_mode UNUSED, int final) you_are("telepathic", from_what(TELEPAT)); if (Warning) you_are("warned", from_what(WARNING)); - if (Warn_of_mon && gc.context.warntype.obj) { + if (Warn_of_mon && svc.context.warntype.obj) { Sprintf(buf, "aware of the presence of %s", - (gc.context.warntype.obj & M2_ORC) ? "orcs" - : (gc.context.warntype.obj & M2_ELF) ? "elves" - : (gc.context.warntype.obj & M2_DEMON) ? "demons" : something); + (svc.context.warntype.obj & M2_ORC) ? "orcs" + : (svc.context.warntype.obj & M2_ELF) ? "elves" + : (svc.context.warntype.obj & M2_DEMON) ? "demons" + : something); you_are(buf, from_what(WARN_OF_MON)); } - if (Warn_of_mon && gc.context.warntype.obj_mlet) { + if (Warn_of_mon && svc.context.warntype.obj_mlet) { /* Like in pager.c, this will have weird results if anything is ever * added that warns of something strange like "eye or sphere". */ Sprintf(buf, "aware of the presence of %s", - makeplural(def_monsyms[gc.context.warntype.obj_mlet].explain)); + makeplural(def_monsyms[svc.context.warntype.obj_mlet].explain)); you_are(buf, from_what(WARN_OF_MON)); } - if (Warn_of_mon && gc.context.warntype.polyd) { + if (Warn_of_mon && svc.context.warntype.polyd) { Sprintf(buf, "aware of the presence of %s", - ((gc.context.warntype.polyd & (M2_HUMAN | M2_ELF)) - == (M2_HUMAN | M2_ELF)) - ? "humans and elves" - : (gc.context.warntype.polyd & M2_HUMAN) - ? "humans" - : (gc.context.warntype.polyd & M2_ELF) - ? "elves" - : (gc.context.warntype.polyd & M2_ORC) - ? "orcs" - : (gc.context.warntype.polyd & M2_DEMON) - ? "demons" - : "certain monsters"); + ((svc.context.warntype.polyd & (M2_HUMAN | M2_ELF)) + == (M2_HUMAN | M2_ELF)) ? "humans and elves" + : (svc.context.warntype.polyd & M2_HUMAN) ? "humans" + : (svc.context.warntype.polyd & M2_ELF) ? "elves" + : (svc.context.warntype.polyd & M2_ORC) ? "orcs" + : (svc.context.warntype.polyd & M2_DEMON) ? "demons" + : "certain monsters"); you_are(buf, ""); } - if (Warn_of_mon && gc.context.warntype.speciesidx >= LOW_PM) { + warnspecies = svc.context.warntype.speciesidx; + if (Warn_of_mon && ismnum(warnspecies)) { Sprintf(buf, "aware of the presence of %s", - makeplural(mons[gc.context.warntype.speciesidx].pmnames[NEUTRAL])); + makeplural(mons[warnspecies].pmnames[NEUTRAL])); you_are(buf, from_what(WARN_OF_MON)); } if (Undead_warning) @@ -1652,8 +1697,13 @@ attributes_enlightenment(int unused_mode UNUSED, int final) you_are("visible", from_what(-INVIS)); if (Displaced) you_are("displaced", from_what(DISPLACED)); - if (Stealth) + if (Stealth) { you_are("stealthy", from_what(STEALTH)); + } else if (BStealth && (HStealth || EStealth)) { + Sprintf(buf, " stealthy%s", + (BStealth == FROMOUTSIDE) ? " if not mounted" : ""); + enl_msg(You_, "would be", "would have been", buf, ""); + } if (Aggravate_monster) enl_msg("You aggravate", "", "d", " monsters", from_what(AGGRAVATE_MONSTER)); @@ -1808,7 +1858,7 @@ attributes_enlightenment(int unused_mode UNUSED, int final) /* blocked shape changes */ if (Polymorph) what = !final ? "polymorph" : "have polymorphed"; - else if (u.ulycn >= LOW_PM) + else if (ismnum(u.ulycn)) what = !final ? "change shape" : "have changed shape"; if (what) { Sprintf(buf, "would %s periodically", what); @@ -1843,7 +1893,7 @@ attributes_enlightenment(int unused_mode UNUSED, int final) } if (lays_eggs(gy.youmonst.data) && flags.female) /* Upolyd */ you_can("lay eggs", ""); - if (u.ulycn >= LOW_PM) { + if (ismnum(u.ulycn)) { /* "you are a werecreature [in beast form]" */ Strcpy(buf, an(pmname(&mons[u.ulycn], flags.female ? FEMALE : MALE))); @@ -1940,12 +1990,27 @@ attributes_enlightenment(int unused_mode UNUSED, int final) Sprintf(buf, "Fruit #%d ", f->fid); enl_msg(buf, "is ", "was ", f->fname, ""); } - enl_msg("The current fruit ", "is ", "was ", gp.pl_fruit, ""); + enl_msg("The current fruit ", "is ", "was ", svp.pl_fruit, ""); Sprintf(buf, "%d", flags.made_fruit); enl_msg("The made fruit flag ", "is ", "was ", buf, ""); } #endif + /* saving-grace: show during final disclosure, hide during normal play */ + if (final || wizard || discover) { + static const char *verbchoices[2][2] = { + { "might avoid", "have avoided" }, + { "could have avoided", "avoided" }, + }; + /* u.usaving_grace will always be 0 or 1; final is 0 (game in + progress), 1 (game over, survived), or 2 (game over, died) */ + const char *verb = verbchoices[!!final][u.usaving_grace]; + + /* 'verb' has already been set for present or past but enl_msg() + needs it twice, one for in progress, the other for game over */ + enl_msg(You_, verb, verb, " a one-shot death via saving-grace", ""); + } + if (Doomed) enl_msg("You ", "are under a curse of doom", "were doomed", "", ""); @@ -1977,6 +2042,8 @@ attributes_enlightenment(int unused_mode UNUSED, int final) switch (u.umortality) { case 0: impossible("dead without dying?"); + FALLTHROUGH; + /* FALLTHRU */ case 1: break; /* just "are dead" */ default: @@ -2005,7 +2072,7 @@ doattributes(void) } void -youhiding(boolean via_enlghtmt, /* englightment line vs topl message */ +youhiding(boolean via_enlghtmt, /* enlightenment line vs topl message */ int msgflag) /* for variant message phrasing */ { char *bp, buf[BUFSZ]; @@ -2031,7 +2098,7 @@ youhiding(boolean via_enlghtmt, /* englightment line vs topl message */ if (is_pool(u.ux, u.uy)) Sprintf(bp, " in the %s", waterbody_name(u.ux, u.uy)); } else if (hides_under(gy.youmonst.data)) { - struct obj *o = gl.level.objects[u.ux][u.uy]; + struct obj *o = svl.level.objects[u.ux][u.uy]; if (o) Sprintf(bp, " underneath %s", ansimpleoname(o)); @@ -2089,6 +2156,14 @@ show_conduct(int final) you_have_been("deaf from birth"); if (u.uroleplay.hallu) you_have_been("hallucinating for your entire life"); + /* note: we don't report "you are without possessions" unless the + game started with the pauper option set */ + if (u.uroleplay.pauper) + enl_msg(You_, gi.invent ? "started" : "are", "started out", + " without possessions", ""); + /* nudist is far more than a subset of possessionless, and a much + more impressive accomplishment, but showing "started out without + possessions" before "faithfully nudist" looks more logical */ if (u.uroleplay.nudist) you_have_been("faithfully nudist"); @@ -2245,7 +2320,7 @@ show_conduct(int final) * Achievements (see 'enum achievements' in you.h). */ -static void +staticfn void show_achievements( int final) /* 'final' is used "behind the curtain" by enl_foo() macros */ { @@ -2450,7 +2525,7 @@ record_achievement(schar achidx) /* avoid livelog for achievements recorded during final disclosure: nudist and blind-from-birth; also ascension which is suppressed by this gets logged separately in really_done() */ - if (gp.program_state.gameover) + if (program_state.gameover) return; if (absidx >= ACH_RNK1 && absidx <= ACH_RNK8) { @@ -2463,8 +2538,8 @@ record_achievement(schar achidx) || achidx == ACH_MINE_PRIZE) { /* need to supply extra information for these two */ short otyp = ((achidx == ACH_SOKO_PRIZE) - ? gc.context.achieveo.soko_prize_otyp - : gc.context.achieveo.mines_prize_otyp); + ? svc.context.achieveo.soko_prize_otyp + : svc.context.achieveo.mines_prize_otyp); /* note: OBJ_NAME() works here because both "bag of holding" and "amulet of reflection" are fully named in their objects[] entry @@ -2602,7 +2677,8 @@ show_gamelog(int final) * Vanquished monsters. */ -/* the two uppercase choices are implemented but suppressed from menu */ +/* the two uppercase choices are implemented but suppressed from menu. + also used in options.c */ const char *const vanqorders[NUM_VANQ_ORDER_MODES][3] = { { "t", "traditional: by monster level", "traditional: by monster level, by internal monster index" }, @@ -2622,7 +2698,7 @@ const char *const vanqorders[NUM_VANQ_ORDER_MODES][3] = { "by count, low to high, by internal index within tied count" }, }; -static int QSORTCALLBACK +staticfn int QSORTCALLBACK vanqsort_cmp( const genericptr vptr1, const genericptr vptr2) @@ -2653,11 +2729,12 @@ vanqsort_cmp( res = uniq2 - uniq1; break; } /* else both unique or neither unique */ + FALLTHROUGH; /*FALLTHRU*/ case VANQ_ALPHA_MIX: name1 = mons[indx1].pmnames[NEUTRAL]; name2 = mons[indx2].pmnames[NEUTRAL]; - res = strcmpi(name1, name2); /* caseblind alhpa, low to high */ + res = strcmpi(name1, name2); /* caseblind alpha, low to high */ break; case VANQ_MCLS_HTOL: case VANQ_MCLS_LTOH: @@ -2686,6 +2763,15 @@ vanqsort_cmp( } res = mcls1 - mcls2; /* class */ if (res == 0) { + /* Riders are in the same class as major demons, yielding res==0 + above when both mcls1 and mcls2 are either Riders or demons or + one of each; force Riders to be sorted before demons */ + res = is_rider(&mons[indx2]) - is_rider(&mons[indx1]); + /* res -1 => #1 is a Rider, #2 isn't; + 0 => both Riders or neither; + +1 => #2 is a Rider, #1 isn't */ + if (res) + break; mlev1 = mons[indx1].mlevel; mlev2 = mons[indx2].mlevel; res = mlev1 - mlev2; /* mlevel low to high */ @@ -2695,8 +2781,8 @@ vanqsort_cmp( break; case VANQ_COUNT_H_L: case VANQ_COUNT_L_H: - died1 = gm.mvitals[indx1].died; - died2 = gm.mvitals[indx2].died; + died1 = svm.mvitals[indx1].died; + died2 = svm.mvitals[indx2].died; res = died2 - died1; /* dead count high to low */ if (flags.vanq_sortmode == VANQ_COUNT_L_H) res = -res; /* dead count low to high */ @@ -2718,7 +2804,7 @@ set_vanq_order(boolean for_vanq) char buf[BUFSZ]; const char *desc; int i, n, choice, - clr = 0; + clr = NO_COLOR; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -2763,7 +2849,8 @@ set_vanq_order(boolean for_vanq) int dovanquished(void) { - list_vanquished(iflags.menu_requested ? 'a' : 'y', FALSE); + list_vanquished(iflags.menu_requested ? 'A' : 'y', FALSE); + iflags.menu_requested = FALSE; return ECMD_OK; } @@ -2771,30 +2858,44 @@ dovanquished(void) #define UniqCritterIndx(mndx) \ ((mons[mndx].geno & G_UNIQ) != 0 && mndx != PM_HIGH_CLERIC) -#define done_stopprint gp.program_state.stopprint +#define done_stopprint program_state.stopprint +/* used for #vanquished and end of game disclosure and end of game dumplog */ void list_vanquished(char defquery, boolean ask) { - register int i; + int i; int pfx, nkilled; unsigned ntypes, ni; long total_killed = 0L; winid klwin; short mindx[NUMMONS]; char c, buf[BUFSZ], buftoo[BUFSZ]; - boolean dumping; /* for DUMPLOG; doesn't need to be conditional */ - - dumping = (defquery == 'd'); - if (dumping) { + /* 'A' is only supplied by 'm #vanquished'; 'd' is only supplied by + dump_everything() when writing dumplog, so won't happen if built + without '#define DUMPLOG' but there's no need for conditionals here */ + boolean force_sort = (defquery == 'A'), + dumping = (defquery == 'd'); + + /* normally we don't ask about sort order for the vanquished list unless + it contains at least two entries; however, if player has used explicit + 'm #vanquished', choose order no matter what it contains so far */ + if (force_sort) { /* iflags.menu_requested via dovanquished() */ + /* choose value for vanq_sortmode via menu; ESC cancels choosing + sort order but continues with vanquishd monsters display */ + (void) set_vanq_order(TRUE); + } + if (dumping || force_sort) { + /* switch from 'A' or 'd' to 'y'; 'ask' is already False for the + cases that might supply 'A' or 'd' */ defquery = 'y'; - ask = FALSE; /* redundant; caller passes False with defquery=='d' */ + ask = FALSE; /* redundant */ } /* get totals first */ ntypes = 0; for (i = LOW_PM; i < NUMMONS; i++) { - if ((nkilled = (int) gm.mvitals[i].died) == 0) + if ((nkilled = (int) svm.mvitals[i].died) == 0) continue; mindx[ntypes++] = i; total_killed += (long) nkilled; @@ -2805,16 +2906,30 @@ list_vanquished(char defquery, boolean ask) */ if (ntypes != 0) { char mlet, prev_mlet = 0; /* used as small integer, not character */ - boolean class_header, uniq_header, was_uniq = FALSE; + boolean class_header, uniq_header, Rider, + was_uniq = FALSE, special_hdr = FALSE; + + if (ask) { + char allow_yn[10]; + + if (ntypes > 1) { + Strcpy(allow_yn, ynaqchars); + } else { + Strcpy(allow_yn, ynqchars); /* don't include 'a', but */ + Strcat(allow_yn, "\033a"); /* allow user to answer 'a' */ + if (defquery == 'a') /* potential default from 'disclose' */ + defquery = 'y'; + } + c = yn_function("Do you want an account of creatures vanquished?", + allow_yn, defquery, TRUE); + } else { + c = defquery; + } - c = ask ? yn_function( - "Do you want an account of creatures vanquished?", - ynaqchars, defquery, TRUE) - : defquery; if (c == 'q') done_stopprint++; if (c == 'y' || c == 'a') { - if (c == 'a' && ntypes > 1) { /* ask player to choose sort order */ + if (c == 'a' && ntypes > 1) { /* ask user to choose sort order */ /* choose value for vanq_sortmode via menu; ESC cancels list of vanquished monsters but does not set 'done_stopprint' */ if (set_vanq_order(TRUE) < 0) @@ -2833,11 +2948,21 @@ list_vanquished(char defquery, boolean ask) qsort((genericptr_t) mindx, ntypes, sizeof *mindx, vanqsort_cmp); for (ni = 0; ni < ntypes; ni++) { i = mindx[ni]; - nkilled = gm.mvitals[i].died; + nkilled = svm.mvitals[i].died; + Rider = is_rider(&mons[i]); mlet = mons[i].mlet; - if (class_header && mlet != prev_mlet) { - Strcpy(buf, def_monsyms[(int) mlet].explain); - putstr(klwin, ask ? 0 : iflags.menu_headings, + if (class_header + && (mlet != prev_mlet || (special_hdr && !Rider))) { + if (!Rider) { + Strcpy(buf, def_monsyms[(int) mlet].explain); + special_hdr = FALSE; + } else { + Strcpy(buf, "Rider"); + special_hdr = TRUE; + } + /* 'ask' implies final disclosure, where highlighting + of various header lines is suppressed */ + putstr(klwin, ask ? ATR_NONE : iflags.menu_headings.attr, upstart(buf)); prev_mlet = mlet; } @@ -2902,7 +3027,7 @@ list_vanquished(char defquery, boolean ask) * still in progress, so use present tense via pline(), or for dumplog * which needs putstr() and past tense. */ - } else if (!gp.program_state.gameover) { + } else if (!program_state.gameover) { /* #vanquished rather than final disclosure, so pline() is ok */ pline("No creatures have been vanquished."); #if defined(DUMPLOG) || defined(DUMPHTML) @@ -2919,7 +3044,7 @@ num_genocides(void) int i, n = 0; for (i = LOW_PM; i < NUMMONS; ++i) { - if (gm.mvitals[i].mvflags & G_GENOD) { + if (svm.mvitals[i].mvflags & G_GENOD) { ++n; if (UniqCritterIndx(i)) impossible("unique creature '%d: %s' genocided?", @@ -2930,7 +3055,7 @@ num_genocides(void) } /* return a count of the number of extinct species */ -static int +staticfn int num_extinct(void) { int i, n = 0; @@ -2938,14 +3063,14 @@ num_extinct(void) for (i = LOW_PM; i < NUMMONS; ++i) { if (UniqCritterIndx(i)) continue; - if ((gm.mvitals[i].mvflags & G_GONE) == G_EXTINCT) + if ((svm.mvitals[i].mvflags & G_GONE) == G_EXTINCT) ++n; } return n; } -/* collect both genocides and extintctions, skipping uniques */ -static int +/* collect both genocides and extinctions, skipping uniques */ +staticfn int num_gone(int mvflags, int *mindx) { uchar mflg = (uchar) mvflags; @@ -2959,7 +3084,7 @@ num_gone(int mvflags, int *mindx) if (UniqCritterIndx(i)) continue; - if ((gm.mvitals[i].mvflags & mflg) != 0) + if ((svm.mvitals[i].mvflags & mflg) != 0) mindx[n++] = i; } return n; @@ -2970,19 +3095,23 @@ num_gone(int mvflags, int *mindx) void list_genocided(char defquery, boolean ask) { - register int i, mndx; + int i, mndx; int ngenocided, nextinct, ngone, mvflags, mindx[NUMMONS]; char c; winid klwin; char buf[BUFSZ]; - boolean dumping; /* for DUMPLOG; doesn't need to be conditional */ - boolean both = (gp.program_state.gameover || wizard || discover); + boolean genoing, /* prompting for genocide or class genocide */ + dumping; /* for DUMPLOG; doesn't need to be conditional */ + boolean both = (program_state.gameover || wizard || discover); dumping = (defquery == 'd'); - if (dumping) + genoing = (defquery == 'g'); + if (dumping || genoing) defquery = 'y'; + if (genoing) + both = FALSE; /* genocides only, not extinctions */ - /* this goess through the whole monster list up to three times but will + /* this goes through the whole monster list up to three times but will happen rarely and is simpler than a more general single pass check; extinctions are only revealed during end of game disclosure or when running in wizard or explore mode */ @@ -3040,7 +3169,9 @@ list_genocided(char defquery, boolean ask) mlet = mons[mndx].mlet; if (class_header && mlet != prev_mlet) { Strcpy(buf, def_monsyms[(int) mlet].explain); - putstr(klwin, ask ? 0 : iflags.menu_headings, + /* 'ask' implies final disclosure, where highlighting + of various header lines is suppressed */ + putstr(klwin, ask ? ATR_NONE : iflags.menu_headings.attr, upstart(buf)); prev_mlet = mlet; } @@ -3055,7 +3186,7 @@ list_genocided(char defquery, boolean ask) * clear. During normal play, 'mndx' won't be in the * collected list unless that bit is set. */ - if ((gm.mvitals[mndx].mvflags & G_GONE) == G_EXTINCT) + if ((svm.mvitals[mndx].mvflags & G_GONE) == G_EXTINCT) Strcat(buf, " (extinct)"); putstr(klwin, 0, buf); } @@ -3075,12 +3206,12 @@ list_genocided(char defquery, boolean ask) } /* See the comment for similar code near the end of list_vanquished(). */ - } else if (!gp.program_state.gameover) { + } else if (!program_state.gameover) { /* #genocided rather than final disclosure, so pline() is ok and extinction has been ignored */ - pline("No creatures have been genocided."); + pline("No creatures have been genocided%s.", genoing ? " yet" : ""); #if defined (DUMPLOG) || defined (DUMPHTML) - } else if (dumping) { + } else if (dumping) { /* 'gameover' is True if we make it here */ putstr(0, 0, "No species were genocided or became extinct."); #endif } @@ -3108,18 +3239,18 @@ doborn(void) putstr(datawin, 0, "died born"); for (i = LOW_PM; i < NUMMONS; i++) - if (gm.mvitals[i].born || gm.mvitals[i].died - || (gm.mvitals[i].mvflags & G_GONE) != 0) { + if (svm.mvitals[i].born || svm.mvitals[i].died + || (svm.mvitals[i].mvflags & G_GONE) != 0) { Sprintf(buf, fmt, - gm.mvitals[i].died, gm.mvitals[i].born, - ((gm.mvitals[i].mvflags & G_GONE) == G_EXTINCT) ? 'E' - : ((gm.mvitals[i].mvflags & G_GONE) == G_GENOD) ? 'G' - : ((gm.mvitals[i].mvflags & G_GONE) != 0) ? 'X' + svm.mvitals[i].died, svm.mvitals[i].born, + ((svm.mvitals[i].mvflags & G_GONE) == G_EXTINCT) ? 'E' + : ((svm.mvitals[i].mvflags & G_GONE) == G_GENOD) ? 'G' + : ((svm.mvitals[i].mvflags & G_GONE) != 0) ? 'X' : ' ', mons[i].pmnames[NEUTRAL]); putstr(datawin, 0, buf); - nborn += gm.mvitals[i].born; - ndied += gm.mvitals[i].died; + nborn += svm.mvitals[i].born; + ndied += svm.mvitals[i].died; } putstr(datawin, 0, ""); @@ -3155,7 +3286,7 @@ align_str(aligntyp alignment) return "unknown"; } -static char * +staticfn char * size_str(int msize) { static char outbuf[40]; @@ -3249,7 +3380,7 @@ mstatusline(struct monst *mtmp) if (mtmp->data == &mons[PM_LONG_WORM]) { int segndx, nsegs = count_wsegs(mtmp); - /* the worm code internals don't consider the head of be one of + /* the worm code internals don't consider the head to be one of the worm's segments, but we count it as such when presenting worm feedback to the player */ if (!nsegs) { @@ -3261,7 +3392,7 @@ mstatusline(struct monst *mtmp) segndx, ordin(segndx), nsegs); } } - if (mtmp->cham >= LOW_PM && mtmp->data != &mons[mtmp->cham]) + if (ismnum(mtmp->cham) && mtmp->data != &mons[mtmp->cham]) /* don't reveal the innate form (chameleon, vampire, &c), just expose the fact that this current form isn't it */ Strcat(info, ", shapechanger"); @@ -3270,8 +3401,11 @@ mstatusline(struct monst *mtmp) Strcat(info, ", eating"); /* a stethoscope exposes mimic before getting here so this won't be relevant for it, but wand of probing doesn't */ - if (mtmp->mundetected || mtmp->m_ap_type) - mhidden_description(mtmp, TRUE, eos(info)); + if (mtmp->mundetected || mtmp->m_ap_type + || visible_region_at(gb.bhitpos.x, gb.bhitpos.y)) + mhidden_description(mtmp, + MHID_PREFIX | MHID_ARTICLE | MHID_ALTMON | MHID_REGION, + eos(info)); if (mtmp->mcan) Strcat(info, ", cancelled"); if (mtmp->mconf) @@ -3344,7 +3478,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.", @@ -3356,7 +3490,9 @@ mstatusline(struct monst *mtmp) void ustatusline(void) { + NhRegion *reg; char info[BUFSZ]; + size_t ln; info[0] = '\0'; if (Sick) { @@ -3411,17 +3547,31 @@ ustatusline(void) Strcat(info, Very_fast ? ", very fast" : ", fast"); if (u.uundetected) Strcat(info, ", concealed"); + else if (U_AP_TYPE != M_AP_NOTHING) + Strcat(info, ", disguised"); if (Invis) Strcat(info, ", invisible"); if (u.ustuck) { - if (sticks(gy.youmonst.data)) - Strcat(info, ", holding "); - else + if (u.uswallow) + Strcat(info, digests(u.ustuck->data) ? ", being digested by " + : ", engulfed by "); + else if (!sticks(gy.youmonst.data)) Strcat(info, ", held by "); - Strcat(info, mon_nam(u.ustuck)); - } - - pline("Status of %s (%s): Level %d HP %d(%d) AC %d%s.", gp.plname, + else + Strcat(info, ", holding "); + /* FIXME? a_monnam() uses x_monnam() which has a special case that + forces "the" instead of "a" when formatting u.ustuck while hero + is swallowed; we don't really want that here but it isn't worth + fiddling with just for self-probing while engulfed */ + Strcat(info, a_monnam(u.ustuck)); + } + if (!u.uswallow + && (reg = visible_region_at(u.ux, u.uy)) != 0 + && (ln = strlen(info)) < sizeof info) + Snprintf(eos(info), sizeof info - ln, ", in a cloud of %s", + reg_damg(reg) ? "poison gas" : "vapor"); + + pline("Status of %s (%s): Level %d HP %d(%d) AC %d%s.", svp.plname, piousness(FALSE, align_str(u.ualign.type)), Upolyd ? mons[u.umonnum].mlevel : u.ulevel, Upolyd ? u.mh : u.uhp, Upolyd ? u.mhmax : u.uhpmax, u.uac, info); diff --git a/src/invent.c b/src/invent.c index 48bc8221ce..cb1925f8ff 100644 --- a/src/invent.c +++ b/src/invent.c @@ -1,53 +1,58 @@ -/* NetHack 3.7 invent.c $NHDT-Date: 1672827802 2023/01/04 10:23:22 $ $NHDT-Branch: naming-overflow-fix $:$NHDT-Revision: 1.439 $ */ +/* 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. */ #include "hack.h" -#define NOINVSYM '#' +/* fake inventory letters, not 'a'..'z' or 'A'..'Z' */ +#define NOINVSYM '#' /* overflow because all 52 letters are in use */ #define CONTAINED_SYM '>' /* designator for inside a container */ -#define HANDS_SYM '-' - -static char *loot_xname(struct obj *); -static int invletter_value(char); -static int QSORTCALLBACK sortloot_cmp(const genericptr, const genericptr); -static void reorder_invent(void); -static struct obj *addinv_core0(struct obj *, struct obj *, boolean); -static void noarmor(boolean); -static void invdisp_nothing(const char *, const char *); -static boolean worn_wield_only(struct obj *); -static char *cinv_doname(struct obj *); -static char *cinv_ansimpleoname(struct obj *); -static boolean only_here(struct obj *); -static void compactify(char *); -static boolean taking_off(const char *); -static int ckvalidcat(struct obj *); -static int ckunpaid(struct obj *); -static char *safeq_xprname(struct obj *); -static char *safeq_shortxprname(struct obj *); -static char display_pickinv(const char *, const char *, const char *, - boolean, long *); -static char display_used_invlets(char); -static boolean this_type_only(struct obj *); -static void dounpaid(int, int, int); -static struct obj *find_unpaid(struct obj *, struct obj **); -static void menu_identify(int); -static boolean tool_being_used(struct obj *); -static int adjust_ok(struct obj *); -static int adjust_gold_ok(struct obj *); -static int doorganize_core(struct obj *); -static char obj_to_let(struct obj *); -static boolean item_naming_classification(struct obj *, char *, char *); -static int item_reading_classification(struct obj *, char *); -static void ia_addmenu(winid, int, char, const char *); -static void itemactions_pushkeys(struct obj *, int); -static void mime_action(const char *); +#define HANDS_SYM '-' /* hands|fingers|self depending on context */ + +staticfn void inuse_classify(Loot *, struct obj *); +staticfn char *loot_xname(struct obj *); +staticfn int invletter_value(char); +staticfn int QSORTCALLBACK sortloot_cmp(const genericptr, const genericptr); +staticfn void reorder_invent(void); +staticfn struct obj *addinv_core0(struct obj *, struct obj *, + boolean) NONNULLARG1; +staticfn void noarmor(boolean); +staticfn void invdisp_nothing(const char *, const char *); +staticfn boolean worn_wield_only(struct obj *); +staticfn char *cinv_doname(struct obj *); +staticfn char *cinv_ansimpleoname(struct obj *); +staticfn boolean only_here(struct obj *); +staticfn void compactify(char *); +staticfn boolean taking_off(const char *); +staticfn void mime_action(const char *); +staticfn char *getobj_hands_txt(const char *, char *); +staticfn int ckvalidcat(struct obj *); +staticfn int ckunpaid(struct obj *); +staticfn char *safeq_xprname(struct obj *); +staticfn char *safeq_shortxprname(struct obj *); +staticfn char display_pickinv(const char *, const char *, const char *, + boolean, boolean, long *); +staticfn char display_used_invlets(char); +staticfn boolean this_type_only(struct obj *); +staticfn void dounpaid(int, int, int); +staticfn struct obj *find_unpaid(struct obj *, struct obj **); +staticfn void menu_identify(int); +staticfn boolean tool_being_used(struct obj *); +staticfn int adjust_ok(struct obj *); +staticfn int adjust_gold_ok(struct obj *); +staticfn int doorganize_core(struct obj *); +staticfn char obj_to_let(struct obj *); +staticfn boolean item_naming_classification(struct obj *, char *, char *); +staticfn int item_reading_classification(struct obj *, char *); +staticfn void ia_addmenu(winid, int, char, const char *); +staticfn void itemactions_pushkeys(struct obj *, int); +staticfn int itemactions(struct obj *); +staticfn int dispinv_with_action(char *, boolean, const char *); /* enum and structs are defined in wintype.h */ -static win_request_info zerowri = { { 0L, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0 } }; static win_request_info wri_info; -static int done_setting_perminv_flags = 0; +static int perminv_flags = InvOptNone; static boolean in_perm_invent_toggled; /* wizards can wish for venom, which will become an invisible inventory @@ -61,7 +66,95 @@ static boolean in_perm_invent_toggled; */ static const char venom_inv[] = { VENOM_CLASS, 0 }; /* (constant) */ -/* sortloot() classification; called at most once [per sort] for each object */ +/* menu heading lines used instead of object classes when sorting by in-use; + pointers aren't const because dispinv_with_action() might temporarily + change "Accessories" to "Rings" or "Amulet", then back again */ +static const char *inuse_headers[] = { /* [4] shown first, [1] last */ + "", "Miscellaneous", "Worn Armor", + "Wielded/Readied Weapons", "Accessories", +}; + +/* sortloot() classification for in-use sort; + called at most once [per sort] for each object */ +staticfn void +inuse_classify(Loot *sort_item, struct obj *obj) +{ + long w_mask = (obj->owornmask & (W_ACCESSORY | W_WEAPONS | W_ARMOR)); + int rating = 0, altclass = 0; + +#define USE_RATING(test) \ + do { \ + /* 'rating' advances for each USE_RATING() call */ \ + ++rating; \ + if ((test) != 0) \ + goto assign_rating; \ + } while (0) + + /* + * In order of importance, least to most, somewhat arbitrarily. + * + * For instance, all accessories are grouped together even + * though they're usually less important than other stuff, so + * that they appear earlier within displayed list of used items. + * Amulet is rated as most important primarily because the + * default 'packorder' puts amulets first (possibly because one + * might be The Amulet). Non-wielded alternate weapon and + * quiver are grouped with primary weapon. Weapons are rated + * above armor because of default 'packorder'. + * + * These ratings don't match either subclasses or 'packorder'. + * + * USE_RATING() sets up 'rating' then jumps to 'assign_rating' + * if 'obj' warrants that. + */ + /* "Miscellaneous" */ + ++altclass; /* 1 */ + /* lamp and leash might be used doubly, as a tool and also wielded + or readied-in-quiver; these tests for used-as-tool only pass + when owornmask is 0 so that used-as-weapon takes precedence */ + USE_RATING(!w_mask && obj->otyp == LEASH && obj->leashmon); + USE_RATING(!w_mask && obj->oclass == TOOL_CLASS && obj->lamplit); + /* "Armor" */ + ++altclass; /* 2 */ + USE_RATING(w_mask & WORN_SHIRT); + USE_RATING(w_mask & WORN_BOOTS); + USE_RATING(w_mask & WORN_GLOVES); + USE_RATING(w_mask & WORN_HELMET); + USE_RATING(w_mask & WORN_SHIELD); + USE_RATING(w_mask & WORN_CLOAK); + USE_RATING(w_mask & WORN_ARMOR); + /* "Weapons" */ + ++altclass; /* 3 */ + /* could get more complicated: if uswapwep is just alternate weapon + rather than wielded secondary, swap order with quiver (unless + quiver is ammo for uswapwep without also being ammo for uwep) */ + USE_RATING(w_mask & W_QUIVER); + USE_RATING(w_mask & W_SWAPWEP); + USE_RATING(w_mask & W_WEP); + /* "Accessories" */ + ++altclass; /* 4 */ + USE_RATING(w_mask & WORN_BLINDF); + USE_RATING(w_mask & (ULEFTY ? RIGHT_RING : LEFT_RING)); /* off hand */ + USE_RATING(w_mask & (URIGHTY ? RIGHT_RING : LEFT_RING)); /* main hand */ + USE_RATING(w_mask & WORN_AMUL); + + /* if we get here, the USE_RATING() checks failed to find a match */ + rating = 0; + altclass = -1; /* 'orderclass' must end up non-zero */ + + assign_rating: + sort_item->inuse = rating; + sort_item->orderclass = altclass; /* used for alternate headings */ + + /* not applicable for in-use */ + sort_item->subclass = 0; + sort_item->disco = 0; + +#undef USE_RATING +} + +/* sortloot() classification; called at most once [per sort] for each object; + also called by '\' command if discoveries use sortloot order */ void loot_classify(Loot *sort_item, struct obj *obj) { @@ -94,7 +187,7 @@ loot_classify(Loot *sort_item, struct obj *obj) k = 1 + (int) (p - classorder); else k = 1 + (int) strlen(classorder) + (oclass != VENOM_CLASS); - sort_item->orderclass = (coordxy) k; + sort_item->orderclass = k; /* subclass designation; only a few classes have subclasses and the non-armor ones we use are fairly arbitrary */ switch (oclass) { @@ -210,17 +303,19 @@ loot_classify(Loot *sort_item, struct obj *obj) k = 1; /* any non-zero would do */ break; } - sort_item->subclass = (coordxy) k; + sort_item->subclass = k; /* discovery status */ k = !seen ? 1 /* unseen */ : (discovered || !OBJ_DESCR(objects[otyp])) ? 4 : (objects[otyp].oc_uname) ? 3 /* named (partially discovered) */ : 2; /* undiscovered */ - sort_item->disco = (coordxy) k; + sort_item->disco = k; + /* not applicable */ + sort_item->inuse = 0; } /* sortloot() formatting routine; for alphabetizing, not shown to user */ -static char * +staticfn char * loot_xname(struct obj *obj) { struct obj saveo; @@ -263,7 +358,7 @@ loot_xname(struct obj *obj) if (wizard) { /* flags.debug */ /* paranoia: before toggling off wizard mode, guard against a panic in xname() producing a normal mode panic save file */ - gp.program_state.something_worth_saving = 0; + program_state.something_worth_saving = 0; flags.debug = FALSE; } @@ -271,7 +366,7 @@ loot_xname(struct obj *obj) if (save_debug) { flags.debug = TRUE; - gp.program_state.something_worth_saving = 1; + program_state.something_worth_saving = 1; } /* restore the object */ obj->material = saveo.material; @@ -306,28 +401,47 @@ loot_xname(struct obj *obj) } /* '$'==1, 'a'-'z'==2..27, 'A'-'Z'==28..53, '#'==54, catchall 55 */ -static int +staticfn int invletter_value(char c) { return ('a' <= c && c <= 'z') ? (c - 'a' + 2) : ('A' <= c && c <= 'Z') ? (c - 'A' + 2 + 26) : (c == '$') ? 1 - : (c == '#') ? 1 + 52 + 1 - : 1 + 52 + 1 + 1; /* none of the above (shouldn't happen) */ + : (c == '#') ? 1 + invlet_basic + 1 + : 1 + invlet_basic + 1 + 1; /* none of the above + * (shouldn't happen) */ } /* qsort comparison routine for sortloot() */ -static int QSORTCALLBACK +staticfn int QSORTCALLBACK sortloot_cmp(const genericptr vptr1, const genericptr vptr2) { struct sortloot_item *sli1 = (struct sortloot_item *) vptr1, *sli2 = (struct sortloot_item *) vptr2; struct obj *obj1 = sli1->obj, *obj2 = sli2->obj; - char *nam1, *nam2; + char *nam1, *nam2, *tmpstr; const char *mat1, *mat2; int val1, val2, namcmp; + /* in-use takes precedence over all others */ + if ((gs.sortlootmode & SORTLOOT_INUSE) != 0) { + /* Classify each object at most once no matter how many + comparisons it is involved in. */ + if (!sli1->orderclass) + inuse_classify(sli1, obj1); + if (!sli2->orderclass) + inuse_classify(sli2, obj2); + + val1 = sli1->inuse; + val2 = sli2->inuse; + if (val1 != val2) + return val2 - val1; /* bigger value comes before smaller */ + /* neither item in use (or both are lit lamps/candles or both are + attached leashes; items using owornmask don't produce ties) */ + goto tiebreak; + } + /* order by object class unless we're doing by-invlet without sortpack */ if ((gs.sortlootmode & (SORTLOOT_PACK | SORTLOOT_INVLET)) != SORTLOOT_INVLET) { @@ -342,7 +456,7 @@ sortloot_cmp(const genericptr vptr1, const genericptr vptr2) val1 = sli1->orderclass; val2 = sli2->orderclass; if (val1 != val2) - return (int) (val1 - val2); + return val1 - val2; /* skip sub-classes when ordering by sortpack+invlet */ if ((gs.sortlootmode & SORTLOOT_INVLET) == 0) { @@ -387,11 +501,17 @@ sortloot_cmp(const genericptr vptr1, const genericptr vptr2) * comparisons it gets subjected to. */ nam1 = sli1->str; - if (!nam1) - nam1 = sli1->str = dupstr(loot_xname(obj1)); + if (!nam1) { + tmpstr = loot_xname(obj1); + nam1 = sli1->str = dupstr(tmpstr); + maybereleaseobuf(tmpstr); + } nam2 = sli2->str; - if (!nam2) - nam2 = sli2->str = dupstr(loot_xname(obj2)); + if (!nam2) { + tmpstr = loot_xname(obj2); + nam2 = sli2->str = dupstr(tmpstr); + maybereleaseobuf(tmpstr); + } if ((namcmp = strcmpi(nam1, nam2)) != 0) return namcmp; @@ -499,6 +619,7 @@ sortloot( boolean by_nexthere, /* T: traverse via obj->nexthere, F: via obj->nobj */ boolean (*filterfunc)(struct obj *)) /* optional filter */ { + static Loot zerosli; Loot *sliarray; struct obj *o; unsigned n, i; @@ -521,16 +642,14 @@ sortloot( && (!augment_filter || o->otyp != CORPSE || !touch_petrifies(&mons[o->corpsenm]))) continue; + sliarray[i] = zerosli; sliarray[i].obj = o, sliarray[i].indx = (int) i; - sliarray[i].str = (char *) 0; - sliarray[i].orderclass = sliarray[i].subclass = sliarray[i].disco = 0; ++i; } n = i; /* add a terminator so that we don't have to pass 'n' back to caller */ - sliarray[n].obj = (struct obj *) 0, sliarray[n].indx = -1; - sliarray[n].str = (char *) 0; - sliarray[n].orderclass = sliarray[n].subclass = sliarray[n].disco = 0; + sliarray[n] = zerosli; + sliarray[n].indx = -1; /* do the sort; if no sorting is requested, we'll just return a sortloot_item array reflecting the current ordering */ @@ -597,9 +716,9 @@ sortloot( void assigninvlet(struct obj *otmp) { - boolean inuse[52]; - register int i; - register struct obj *obj; + boolean inuse[invlet_basic]; + int i; + struct obj *obj; /* there should be at most one of these in inventory... */ if (otmp->oclass == COIN_CLASS) { @@ -607,7 +726,7 @@ assigninvlet(struct obj *otmp) return; } - for (i = 0; i < 52; i++) + for (i = 0; i < invlet_basic; i++) inuse[i] = FALSE; for (obj = gi.invent; obj; obj = obj->nobj) if (obj != otmp) { @@ -623,7 +742,7 @@ assigninvlet(struct obj *otmp) && (('a' <= i && i <= 'z') || ('A' <= i && i <= 'Z'))) return; for (i = gl.lastinvnr + 1; i != gl.lastinvnr; i++) { - if (i == 52) { + if (i == invlet_basic) { i = -1; continue; } @@ -639,7 +758,7 @@ assigninvlet(struct obj *otmp) #define inv_rank(o) ((o)->invlet ^ 040) /* sort the inventory; used by addinv() and doorganize() */ -static void +staticfn void reorder_invent(void) { struct obj *otmp, *prev, *next; @@ -679,8 +798,10 @@ struct obj * merge_choice(struct obj *objlist, struct obj *obj) { struct monst *shkp; - int save_nocharge; + unsigned save_nocharge; + if (!objlist) /* might be checking 'obj' against empty inventory */ + return (struct obj *) 0; if (obj->otyp == SCR_SCARE_MONSTER) /* punt on these */ return (struct obj *) 0; /* if this is an item on the shop floor, the attributes it will @@ -701,11 +822,12 @@ merge_choice(struct obj *objlist, struct obj *obj) else if (inhishop(shkp)) return (struct obj *) 0; } - while (objlist) { + do { + /*assert(objlist != NULL);*/ if (mergable(objlist, obj)) break; objlist = objlist->nobj; - } + } while (objlist); obj->no_charge = save_nocharge; return objlist; } @@ -714,7 +836,8 @@ merge_choice(struct obj *objlist, struct obj *obj) int merged(struct obj **potmp, struct obj **pobj) { - register struct obj *otmp = *potmp, *obj = *pobj; + struct obj *otmp = *potmp, *obj = *pobj; + boolean discovered = FALSE; if (mergable(otmp, obj)) { /* Approximate age: we do it this way because if we were to @@ -748,7 +871,7 @@ merged(struct obj **potmp, struct obj **pobj) otmp->owt = weight(otmp), otmp->bknown = 0; /* and puddings!!!1!!one! */ else if (!Is_pudding(otmp)) - otmp->owt += obj->owt; + otmp->owt = weight(otmp); if (!has_oname(otmp) && has_oname(obj)) otmp = *potmp = oname(otmp, ONAME(obj), ONAME_SKIP_INVUPD); obj_extract_self(obj); @@ -762,6 +885,27 @@ merged(struct obj **potmp, struct obj **pobj) if (obj->timed) obj_stop_timers(obj); /* follows lights */ + /* objects can be identified by comparing them (unless Blind, + but that is handled in mergable()); the object becomes + identified in a particular dimension if either object was + previously identified in that dimension, and if the + identification states don't match, one of them must have + previously been identified */ + if (obj->known != otmp->known) { + otmp->known = 1; + discovered = TRUE; + } + if (obj->rknown != otmp->rknown) { + otmp->rknown = 1; + if (otmp->oerodeproof) + discovered = TRUE; + } + if (obj->bknown != otmp->bknown) { + otmp->bknown = 1; + if (!Role_if(PM_CLERIC)) + discovered = TRUE; + } + /* fixup for `#adjust' merging wielded darts, daggers, &c */ if (obj->owornmask && carried(otmp)) { long wmask = otmp->owornmask | obj->owornmask; @@ -774,13 +918,13 @@ merged(struct obj **potmp, struct obj **pobj) (Prior to 3.3.0, it was not possible for the two stacks to be worn in different slots and `obj' didn't need to be unworn when merging.) */ - if (wmask & W_WEP) + if ((wmask & W_WEP) != 0L) { wmask = W_WEP; - else if (wmask & W_SWAPWEP) + } else if ((wmask & W_SWAPWEP) != 0L) { wmask = W_SWAPWEP; - else if (wmask & W_QUIVER) + } else if ((wmask & W_QUIVER) != 0L) { wmask = W_QUIVER; - else { + } else { impossible("merging strangely worn items (%lx)", wmask); wmask = otmp->owornmask; } @@ -819,6 +963,16 @@ merged(struct obj **potmp, struct obj **pobj) return 1; } + /* Print a message if item comparison discovers more + information about the items (with the exception of thrown + items, where this would be too spammy as such items get + unidentified by monsters very frequently). */ + if (discovered && otmp->where == OBJ_INVENT + && obj->how_lost != LOST_THROWN + && otmp->how_lost != LOST_THROWN) { + pline("You learn more about your items by comparing them."); + } + obfree(obj, otmp); /* free(obj), bill->otmp */ return 1; } @@ -834,13 +988,13 @@ merged(struct obj **potmp, struct obj **pobj) * addinv) or when an object in the hero's inventory has been polymorphed * in-place. * - * It may be valid to merge this code with with addinv_core2(). + * It may be valid to merge this code with addinv_core2(). */ void addinv_core1(struct obj *obj) { if (obj->oclass == COIN_CLASS) { - gc.context.botl = 1; + disp.botl = TRUE; } else if (obj->otyp == AMULET_OF_YENDOR) { /* save this first, then call start_fiend_harassment after recording the * achievement */ @@ -885,12 +1039,12 @@ addinv_core1(struct obj *obj) dumplog, originally just recorded in XLOGFILE */ if (is_mines_prize(obj)) { record_achievement(ACH_MINE_PRIZE); - gc.context.achieveo.mines_prize_oid = 0; /* done with luckstone o_id */ - obj->nomerge = 0; + svc.context.achieveo.mines_prize_oid = 0; /* done w/ luckstone o_id */ + obj->nomerge = 0; /* was set in create_object(sp_lev.c) */ } else if (is_soko_prize(obj)) { record_achievement(ACH_SOKO_PRIZE); - gc.context.achieveo.soko_prize_oid = 0; /* done with bag/amulet o_id */ - obj->nomerge = 0; + svc.context.achieveo.soko_prize_oid = 0; /* done w/ bag/amulet o_id */ + obj->nomerge = 0; /* (got set in sp_lev.c) */ } } @@ -917,7 +1071,7 @@ addinv_core2(struct obj *obj) * Add obj to the hero's inventory. Make sure the object is "free". * Adjust hero attributes as necessary. */ -static struct obj * +staticfn struct obj * addinv_core0(struct obj *obj, struct obj *other_obj, boolean update_perm_invent) { @@ -932,15 +1086,15 @@ addinv_core0(struct obj *obj, struct obj *other_obj, obj->no_charge = 0; /* should not be set in hero's invent */ if (Has_contents(obj)) picked_container(obj); /* clear no_charge */ - obj_was_thrown = obj->was_thrown; - obj->was_thrown = 0; /* not meaningful for invent */ + obj_was_thrown = (obj->how_lost == LOST_THROWN); + obj->how_lost = LOST_NONE; if (gl.loot_reset_justpicked) { gl.loot_reset_justpicked = FALSE; reset_justpicked(gi.invent); } - addinv_core1(obj); + addinv_core1(obj); /* handle most side effects of carrying obj */ /* for addinv_before(); if something has been removed and is now being reinserted, try to put it in the same place instead of merging or @@ -1001,7 +1155,7 @@ addinv_core0(struct obj *obj, struct obj *other_obj, setuqwep(obj); added: obj->pickup_prev = 1; - addinv_core2(obj); + addinv_core2(obj); /* handle extrinsics conferred by carrying obj */ carry_obj_effects(obj); /* carrying affects the obj */ if (update_perm_invent) update_inventory(); @@ -1015,13 +1169,28 @@ addinv(struct obj *obj) return addinv_core0(obj, (struct obj *) 0, TRUE); } -/* add obj to the hero's inventory by inserting in front of a specific item */ +/* add obj to the hero's inventory by inserting in front of a specific item; + used for throw-and-return in case '!fixinv' is in effect */ struct obj * addinv_before(struct obj *obj, struct obj *other_obj) { + /* if 'other_obj' is present this will implicitly be 'nomerge' */ return addinv_core0(obj, other_obj, TRUE); } +/* return value will always be 'obj' */ +struct obj * +addinv_nomerge(struct obj *obj) +{ + struct obj *result; + unsigned save_nomerge = obj->nomerge; + + obj->nomerge = 1; + result = addinv(obj); + obj->nomerge = save_nomerge; + return result; +} + /* * Some objects are affected by being carried. * Make those adjustments here. Called _after_ the object @@ -1074,7 +1243,7 @@ hold_another_object( dropy(obj); /* now put it back again :-) */ return obj; } else if (wasUpolyd && !Upolyd) { - /* loose your grip if you revert your form */ + /* lose your grip if you revert your form */ if (drop_fmt) pline(drop_fmt, drop_arg); obj_extract_self(obj); @@ -1090,7 +1259,14 @@ hold_another_object( } if (Fumbling) { obj->nomerge = 1; - /* dropping expects obj to be in invent */ + /* dropping expects obj to be in invent; since it's going to be + 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 { @@ -1110,8 +1286,8 @@ hold_another_object( drop_arg = strcpy(buf, drop_arg); obj = addinv_core0(obj, (struct obj *) 0, FALSE); - if (inv_cnt(FALSE) > 52 || (!undroppable(obj) - && near_capacity() > prev_encumbr)) { + if (inv_cnt(FALSE) > invlet_basic + || (!undroppable(obj) && near_capacity() > prev_encumbr)) { /* undo any merge which took place */ if (obj->quan > oquan) obj = splitobj(obj, oquan); @@ -1194,7 +1370,7 @@ void freeinv_core(struct obj *obj) { if (obj->oclass == COIN_CLASS) { - gc.context.botl = 1; + disp.botl = TRUE; return; } else if (obj->otyp == AMULET_OF_YENDOR) { if (!u.uhave.amulet) @@ -1223,10 +1399,15 @@ freeinv_core(struct obj *obj) if (confers_luck(obj)) { set_moreluck(); - gc.context.botl = 1; + disp.botl = TRUE; } 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 */ @@ -1245,7 +1426,7 @@ delallobj(coordxy x, coordxy y) { struct obj *otmp, *otmp2; - for (otmp = gl.level.objects[x][y]; otmp; otmp = otmp2) { + for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp2) { if (otmp == uball) unpunish(); /* after unpunish(), or might get deallocated chain */ @@ -1273,7 +1454,7 @@ delobj_core( boolean update_map; /* obj_resists(obj,0,0) protects the Amulet, the invocation tools, - and Rider corspes */ + and Rider corpses */ if (!force && obj_resists(obj, 0, 0)) { /* player might be doing something stupid, but we * can't guarantee that. assume special artifacts @@ -1296,9 +1477,9 @@ delobj_core( struct obj * sobj_at(int otyp, coordxy x, coordxy y) { - register struct obj *otmp; + struct obj *otmp; - for (otmp = gl.level.objects[x][y]; otmp; otmp = otmp->nexthere) + for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp->nexthere) if (otmp->otyp == otyp) break; @@ -1309,7 +1490,7 @@ sobj_at(int otyp, coordxy x, coordxy y) struct obj * nxtobj(struct obj *obj, int type, boolean by_nexthere) { - register struct obj *otmp; + struct obj *otmp; otmp = obj; /* start with the object after this one */ do { @@ -1325,7 +1506,7 @@ nxtobj(struct obj *obj, int type, boolean by_nexthere) struct obj * carrying(int type) { - register struct obj *otmp; + struct obj *otmp; /* this could be replaced by 'return m_carrying(&gy.youmonst, type);' */ for (otmp = gi.invent; otmp; otmp = otmp->nobj) @@ -1334,6 +1515,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 */ @@ -1371,14 +1564,15 @@ currency(long amount) { const char *res; - res = Hallucination ? currencies[rn2(SIZE(currencies))] : "zorkmid"; + res = Hallucination ? ROLL_FROM(currencies) : "zorkmid"; if (amount != 1L) res = makeplural(res); return res; } struct obj * -u_carried_gloves(void) { +u_carried_gloves(void) +{ struct obj *otmp, *gloves = (struct obj *) 0; if (uarmg) { @@ -1398,7 +1592,7 @@ u_carried_gloves(void) { struct obj * u_have_novel(void) { - register struct obj *otmp; + struct obj *otmp; for (otmp = gi.invent; otmp; otmp = otmp->nobj) if (otmp->otyp == SPE_NOVEL) @@ -1424,9 +1618,9 @@ o_on(unsigned int id, struct obj *objchn) boolean obj_here(struct obj *obj, coordxy x, coordxy y) { - register struct obj *otmp; + struct obj *otmp; - for (otmp = gl.level.objects[x][y]; otmp; otmp = otmp->nexthere) + for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp->nexthere) if (obj == otmp) return TRUE; return FALSE; @@ -1435,7 +1629,7 @@ obj_here(struct obj *obj, coordxy x, coordxy y) struct obj * g_at(coordxy x, coordxy y) { - register struct obj *obj = gl.level.objects[x][y]; + struct obj *obj = svl.level.objects[x][y]; while (obj) { if (obj->oclass == COIN_CLASS) @@ -1446,11 +1640,11 @@ g_at(coordxy x, coordxy y) } /* compact a string of inventory letters by dashing runs of letters */ -static void +staticfn void compactify(char *buf) { - register int i1 = 1, i2 = 1; - register char ilet, ilet1, ilet2; + int i1 = 1, i2 = 1; + char ilet, ilet1, ilet2; ilet2 = buf[0]; ilet1 = buf[1]; @@ -1490,7 +1684,7 @@ splittable(struct obj *obj) } /* match the prompt for either 'T' or 'R' command */ -static boolean +staticfn boolean taking_off(const char *action) { return !strcmp(action, "take off") || !strcmp(action, "remove") || @@ -1499,7 +1693,7 @@ taking_off(const char *action) !strcmp(action, "destroy"); } -static void +staticfn void mime_action(const char *word) { char buf[BUFSZ]; @@ -1546,17 +1740,38 @@ any_obj_or_hands_ok(struct obj *obj UNUSED) return GETOBJ_SUGGEST; } +/* return string describing your hands based on action. */ +staticfn char * +getobj_hands_txt(const char *action, char *qbuf) +{ + if (!strcmp(action, "grease")) { + Sprintf(qbuf, "your %s", fingers_or_gloves(FALSE)); + } else if (!strcmp(action, "write with")) { + Sprintf(qbuf, "your %s", body_part(FINGERTIP)); + } else if (!strcmp(action, "wield")) { + Sprintf(qbuf, "your %s %s%s", uarmg ? "gloved" : "bare", + makeplural(body_part(HAND)), + !uwep ? " (wielded)" : ""); + } else if (!strcmp(action, "ready")) { + Sprintf(qbuf, "empty quiver%s", + !uquiver ? " (nothing readied)" : ""); + } else { + Sprintf(qbuf, "your %s", makeplural(body_part(HAND))); + } + return qbuf; +} + /* * getobj returns: * struct obj *xxx: object to do something with. * (struct obj *) 0 error return: no object. - * &cg.zeroobj explicitly no object (as in w-). + * &hands_obj explicitly no object (as in w-). * The obj_ok callback should not have side effects (apart from * abnormal-behavior things like impossible calls); it can be called multiple * times on the same object during the execution of this function. * Callbacks' argument is either a valid object pointer or a null pointer, * which represents the validity of doing that action on HANDS_SYM. getobj - * won't call it with &cg.zeroobj, so its behavior can be undefined in that + * won't call it with &hands_obj, so its behavior can be undefined in that * case. */ struct obj * @@ -1565,12 +1780,12 @@ getobj( int (*obj_ok)(OBJ_P), /* callback to classify an object's suitability */ unsigned int ctrlflags) /* some control to fine-tune the behavior */ { - register struct obj *otmp; - register char ilet = 0; + struct obj *otmp; + char ilet = 0; char buf[BUFSZ], qbuf[QBUFSZ]; char lets[BUFSZ], altlets[BUFSZ]; - register int suggested = 0; - register char *bp = buf, *ap = altlets; + int suggested = 0; + char *bp = buf, *ap = altlets; boolean allowcnt = (ctrlflags & GETOBJ_ALLOWCNT), forceprompt = (ctrlflags & GETOBJ_PROMPT), allownone = FALSE; @@ -1602,7 +1817,7 @@ getobj( /* check whether the hands/self choice is suitable */ v = (*obj_ok)((struct obj *) 0); if (v == GETOBJ_SUGGEST || v == GETOBJ_DOWNPLAY) - otmp = (struct obj *) &cg.zeroobj; + otmp = &hands_obj; } else { /* there could be more than one match if key is '#'; take first one which passes the obj_ok callback */ @@ -1616,7 +1831,7 @@ getobj( } else if (cq.typ == CMDQ_INT) { /* getting a partial stack */ if (!cntgiven && allowcnt) { - cnt = cq.intval; + cnt = (long) cq.intval; cntgiven = TRUE; goto need_more_cq; /* now, get CMDQ_KEY */ } else { @@ -1625,11 +1840,11 @@ getobj( return NULL; } } - if (!otmp) /* didn't find what we were looking for, */ + if (!otmp) { /* didn't find what we were looking for, */ cmdq_clear(CQ_CANNED); /* so discard any other queued cmnds */ - else if (cntgiven) { + } else if (cntgiven) { /* if stack is smaller than count, drop the whole stack */ - if (cnt < 1 || otmp->quan <= cnt) + if (cnt < 1L || otmp->quan <= cnt) cntgiven = FALSE; goto split_otmp; } @@ -1646,9 +1861,10 @@ getobj( *bp++ = HANDS_SYM; *bp++ = ' '; /* put a space after the '-' in the prompt */ break; - case GETOBJ_DOWNPLAY: /* acceptable but not shown as likely chioce */ - case GETOBJ_EXCLUDE_INACCESS: /* nothing currently gives this for '-' but - * theoretically could if wearing gloves */ + case GETOBJ_DOWNPLAY: /* acceptable but not shown as likely choice */ + case GETOBJ_EXCLUDE_INACCESS: /* nothing currently gives this for '-' + * but theoretically could if wearing + * gloves */ case GETOBJ_EXCLUDE_SELECTABLE: /* ditto, I think... */ allownone = TRUE; *ap++ = HANDS_SYM; @@ -1724,7 +1940,7 @@ getobj( return (struct obj *) 0; } for (;;) { - cnt = 0; + cnt = 0L; cntgiven = FALSE; Sprintf(qbuf, "What do you want to %s?", word); if (gi.in_doagain) { @@ -1745,7 +1961,7 @@ getobj( ilet = yn_function(qbuf, (char *) 0, '\0', FALSE); } if (digit(ilet)) { - long tmpcnt = 0; + long tmpcnt = 0L; if (!allowcnt) { pline("No count allowed with this command."); @@ -1758,58 +1974,52 @@ getobj( } } if (strchr(quitchars, ilet)) { - if (Verbose(1, getobj1)) + if (flags.verbose) pline1(Never_mind); return (struct obj *) 0; } if (ilet == HANDS_SYM) { /* '-' */ if (!allownone) mime_action(word); - return (allownone ? (struct obj *) &cg.zeroobj : (struct obj *) 0); + return (allownone ? &hands_obj : (struct obj *) 0); } redo_menu: /* since gold is now kept in inventory, we need to do processing for select-from-invent before checking whether gold has been picked */ if (ilet == '?' || ilet == '*') { char *allowed_choices = (ilet == '?') ? lets : (char *) 0; - long ctmp = 0; + long ctmp = 0L; char menuquery[QBUFSZ]; - - menuquery[0] = qbuf[0] = '\0'; - if (iflags.force_invmenu) - Sprintf(menuquery, "What do you want to %s?", word); - if (!strcmp(word, "grease")) - Sprintf(qbuf, "your %s", fingers_or_gloves(FALSE)); - else if (!strcmp(word, "write with")) - Sprintf(qbuf, "your %s", body_part(FINGERTIP)); - else if (!strcmp(word, "wield")) - Sprintf(qbuf, "your %s %s%s", uarmg ? "gloved" : "bare", - makeplural(body_part(HAND)), - !uwep ? " (wielded)" : ""); - else if (!strcmp(word, "ready")) - Sprintf(qbuf, "empty quiver%s", - !uquiver ? " (nothing readied)" : ""); + char *handsbuf = (char *) 0; if (ilet == '?' && !*lets && *altlets) allowed_choices = altlets; - ilet = display_pickinv(allowed_choices, *qbuf ? qbuf : (char *) 0, - menuquery, - TRUE, allowcnt ? &ctmp : (long *) 0); + + menuquery[0] = qbuf[0] = '\0'; + if (iflags.force_invmenu) + Snprintf(menuquery, sizeof menuquery, + "What do you want to %s?", word); + if (!allowed_choices || *allowed_choices == HANDS_SYM + || *buf == HANDS_SYM) + handsbuf = getobj_hands_txt(word, qbuf); + ilet = display_pickinv(allowed_choices, handsbuf, + menuquery, allownone, TRUE, + allowcnt ? &ctmp : (long *) 0); if (!ilet) { if (oneloop) return (struct obj *) 0; continue; } if (ilet == HANDS_SYM) - return (struct obj *) &cg.zeroobj; /* cast away 'const' */ + return &hands_obj; if (ilet == '\033') { - if (Verbose(1, getobj2)) + if (flags.verbose) pline1(Never_mind); return (struct obj *) 0; } - if (ilet == '*') + if (ilet == '*' || ilet == '?') goto redo_menu; - if (allowcnt && ctmp >= 0) { + if (allowcnt && ctmp >= 0L) { cnt = ctmp; cntgiven = TRUE; } @@ -1834,10 +2044,10 @@ getobj( * gold pieces was allowed, and did interesting things to * your money supply. The LRS is the tax bureau from Larn. */ - if (cntgiven && cnt <= 0) { - if (cnt < 0) - pline_The( - "LRS would be very interested to know you have that much."); + if (cntgiven && cnt <= 0L) { + if (cnt < 0L) + pline_The("LRS would be very interested to know" + " you have that much."); return (struct obj *) 0; } } @@ -1862,9 +2072,9 @@ getobj( continue; } } - gc.context.botl = 1; /* May have changed the amount of money */ + disp.botl = TRUE; /* May have changed the amount of money */ if (otmp && !gi.in_doagain) { - if (cntgiven && cnt > 0) + if (cntgiven && cnt > 0L) cmdq_add_int(CQ_REPEAT, cnt); cmdq_add_key(CQ_REPEAT, ilet); } @@ -1876,7 +2086,7 @@ getobj( if (gi.in_doagain) return (struct obj *) 0; continue; - } else if (cnt < 0 || otmp->quan < cnt) { + } else if (cnt < 0L || otmp->quan < cnt) { You("don't have that many! You have only %ld.", otmp->quan); if (gi.in_doagain) return (struct obj *) 0; @@ -1888,9 +2098,9 @@ getobj( silly_thing(word, otmp); return (struct obj *) 0; } -split_otmp: + split_otmp: if (cntgiven) { - if (cnt == 0) + if (cnt == 0L) return (struct obj *) 0; if (cnt != otmp->quan) { /* don't split a stack of cursed loadstones */ @@ -1905,11 +2115,7 @@ DISABLE_WARNING_FORMAT_NONLITERAL void silly_thing(const char *word, -#ifdef OBSOLETE_HANDLING struct obj *otmp) -#else - struct obj *otmp UNUSED) -#endif { #ifdef OBSOLETE_HANDLING /* 'P','R' vs 'W','T' handling is obsolete */ @@ -1949,14 +2155,14 @@ silly_thing(const char *word, RESTORE_WARNING_FORMAT_NONLITERAL -static int +staticfn int ckvalidcat(struct obj *otmp) { /* use allow_category() from pickup.c */ return (int) allow_category(otmp); } -static int +staticfn int ckunpaid(struct obj *otmp) { return (otmp->unpaid || (Has_contents(otmp) && count_unpaid(otmp->cobj))); @@ -1977,6 +2183,15 @@ is_worn(struct obj *otmp) : FALSE; } +/* is 'obj' being used by the hero? worn, wielded, active lamp or leash; + not to be confused with obj->in_use, which finishes using up an item + (destroys it) if restoring a save file finds that bit set */ +boolean +is_inuse(struct obj *obj) +{ + return (carried(obj) && (is_worn(obj) || tool_being_used(obj))); +} + /* extra xprname() input that askchain() can't pass through safe_qbuf() */ static struct xprnctx { char let; @@ -1984,7 +2199,7 @@ static struct xprnctx { } safeq_xprn_ctx; /* safe_qbuf() -> short_oname() callback */ -static char * +staticfn char * safeq_xprname(struct obj *obj) { return xprname(obj, (char *) 0, safeq_xprn_ctx.let, safeq_xprn_ctx.dot, @@ -1992,7 +2207,7 @@ safeq_xprname(struct obj *obj) } /* alternate safe_qbuf() -> short_oname() callback */ -static char * +staticfn char * safeq_shortxprname(struct obj *obj) { return xprname(obj, ansimpleoname(obj), safeq_xprn_ctx.let, @@ -2038,7 +2253,8 @@ ggetobj(const char *word, int (*fn)(OBJ_P), int mx, ofilter = not_fully_identified; } - iletct = collect_obj_classes(ilets, gi.invent, FALSE, ofilter, &itemcount); + iletct = collect_obj_classes(ilets, gi.invent, FALSE, ofilter, + &itemcount); unpaid = count_unpaid(gi.invent); if (ident && !iletct) { @@ -2072,15 +2288,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 @@ -2291,6 +2509,7 @@ askchain( switch (sym) { case 'a': allflag = 1; + FALLTHROUGH; /*FALLTHRU*/ case 'y': tmp = (*fn)(otmp); @@ -2309,10 +2528,13 @@ askchain( cnt += tmp; if (--mx == 0) goto ret; + FALLTHROUGH; /*FALLTHRU*/ case 'n': if (nodot) dud++; + FALLTHROUGH; + /*FALLTHRU*/ default: break; case 'q': @@ -2361,7 +2583,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) @@ -2378,7 +2600,7 @@ identify(struct obj *otmp) } /* menu of unidentified objects; select and identify up to id_limit of them */ -static void +staticfn void menu_identify(int id_limit) { menu_item *pick_list; @@ -2472,6 +2694,7 @@ void learn_unseen_invent(void) { struct obj *otmp; + boolean invupdated = FALSE; if (Blind) return; /* sanity check */ @@ -2479,14 +2702,17 @@ learn_unseen_invent(void) for (otmp = gi.invent; otmp; otmp = otmp->nobj) { if (otmp->dknown && (otmp->bknown || !Role_if(PM_CLERIC))) continue; /* already seen */ - /* set dknown, perhaps bknown (for priest[ess]) */ - (void) xname(otmp); + invupdated = TRUE; + /* xname() will set dknown, perhaps bknown (for priest[ess]); + result from xname() is immediately released for re-use */ + maybereleaseobuf(xname(otmp)); /* * If object->eknown gets implemented (see learnwand(zap.c)), * handle deferred discovery here. */ } - update_inventory(); + if (invupdated) + update_inventory(); } /* persistent inventory window is maintained by interface code; @@ -2498,7 +2724,7 @@ update_inventory(void) { int save_suppress_price; - if (!gp.program_state.in_moveloop) /* not covered by suppress_map_output */ + if (!program_state.in_moveloop) /* not covered by suppress_map_output */ return; if (suppress_map_output()) /* despite name, used for perm_invent too */ return; @@ -2520,12 +2746,7 @@ update_inventory(void) */ save_suppress_price = iflags.suppress_price; iflags.suppress_price = 0; -#if defined(TTY_PERM_INVENT) - if (WINDOWPORT(tty)) - sync_perminvent(); - else -#endif - (*windowprocs.win_update_inventory)(0); + (*windowprocs.win_update_inventory)(0); iflags.suppress_price = save_suppress_price; } @@ -2577,7 +2798,7 @@ doperminv(void) } /* should of course only be called for things in invent */ -static char +staticfn char obj_to_let(struct obj *obj) { if (!flags.invlet_constant) { @@ -2606,7 +2827,7 @@ prinv(const char *prefix, struct obj *obj, long quan) " (%ld in total).", obj->quan); pline("%s%s%s%s", prefix, *prefix ? " " : "", xprname(obj, (char *) 0, obj_to_let(obj), !total_of, 0L, quan), - Verbose(3, prinv) ? totalbuf : ""); + flags.verbose ? totalbuf : ""); } DISABLE_WARNING_FORMAT_NONLITERAL @@ -2624,7 +2845,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 = 0; long save_owt = 0; @@ -2644,8 +2865,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 == '*') { @@ -2692,6 +2915,7 @@ enum item_action_actions { IA_DROP_OBJ, /* 'd' */ IA_EAT_OBJ, /* 'e' */ IA_ENGRAVE_OBJ, /* 'E' */ + IA_FIRE_OBJ, /* 'f' */ IA_ADJUST_OBJ, /* 'i' #adjust inventory letter */ IA_ADJUST_STACK, /* 'I' #adjust with count to split stack */ IA_LOOKUP_OBJ, /* 'l' look up in encyclopedia' */ @@ -2708,11 +2932,13 @@ enum item_action_actions { IA_WIELD_OBJ, IA_WEAR_OBJ, IA_SWAPWEAPON, + IA_TWOWEAPON, IA_ZAP_OBJ, + IA_WHATIS_OBJ, /* '/' specify inventory object */ }; /* construct text for the menu entries for IA_NAME_OBJ and IA_NAME_OTYP */ -static boolean +staticfn boolean item_naming_classification( struct obj *obj, char *onamebuf, @@ -2731,8 +2957,8 @@ item_naming_classification( Sprintf(onamebuf, "%s %s %s", (!has_oname(obj) || !*ONAME(obj)) ? Name : Rename, the_unique_obj(obj) ? "the" - : !is_plural(obj) ? "this" - : "these", + : !is_plural(obj) ? "this specific" + : "this stack of", /*"these",*/ simpleonames(obj)); } if (call_ok(obj) == GETOBJ_SUGGEST) { @@ -2752,7 +2978,7 @@ item_naming_classification( } /* construct text for the menu entries for IA_READ_OBJ */ -static int +staticfn int item_reading_classification(struct obj *obj, char *outbuf) { int otyp = obj->otyp, res = IA_READ_OBJ; @@ -2793,11 +3019,11 @@ item_reading_classification(struct obj *obj, char *outbuf) return res; } -static void +staticfn void ia_addmenu(winid win, int act, char let, const char *txt) { anything any; - int clr = 0; + int clr = NO_COLOR; any = cg.zeroany; any.a_int = act; @@ -2805,12 +3031,13 @@ ia_addmenu(winid win, int act, char let, const char *txt) ATR_NONE, clr, txt, MENU_ITEMFLAGS_NONE); } -static void +staticfn void itemactions_pushkeys(struct obj *otmp, int act) { switch (act) { default: impossible("Unknown item action"); + break; case IA_NONE: break; case IA_UNWIELD: @@ -2853,6 +3080,9 @@ itemactions_pushkeys(struct obj *otmp, int act) cmdq_add_ec(CQ_CANNED, doengrave); cmdq_add_key(CQ_CANNED, otmp->invlet); break; + case IA_FIRE_OBJ: + cmdq_add_ec(CQ_CANNED, dofire); + break; case IA_ADJUST_OBJ: cmdq_add_ec(CQ_CANNED, doorganize); /* #adjust */ cmdq_add_key(CQ_CANNED, otmp->invlet); @@ -2861,10 +3091,6 @@ itemactions_pushkeys(struct obj *otmp, int act) cmdq_add_ec(CQ_CANNED, adjust_split); /* #altadjust */ cmdq_add_key(CQ_CANNED, otmp->invlet); break; - case IA_LOOKUP_OBJ: - checkfile(simple_typename(otmp->otyp), NULL, TRUE, TRUE, NULL); - /* doesn't add any commands */ - break; case IA_SACRIFICE: cmdq_add_ec(CQ_CANNED, dosacrifice); cmdq_add_key(CQ_CANNED, otmp->invlet); @@ -2928,15 +3154,23 @@ itemactions_pushkeys(struct obj *otmp, int act) case IA_SWAPWEAPON: cmdq_add_ec(CQ_CANNED, doswapweapon); break; + case IA_TWOWEAPON: + cmdq_add_ec(CQ_CANNED, dotwoweapon); + break; case IA_ZAP_OBJ: cmdq_add_ec(CQ_CANNED, dozap); cmdq_add_key(CQ_CANNED, otmp->invlet); break; + case IA_WHATIS_OBJ: + cmdq_add_ec(CQ_CANNED, dowhatis); /* "/" command */ + cmdq_add_key(CQ_CANNED, 'i'); /* "i" == item from inventory */ + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; } } /* Show menu of possible actions hero could do with item otmp */ -static int +staticfn int itemactions(struct obj *otmp) { int n, act = IA_NONE; @@ -2969,12 +3203,16 @@ itemactions(struct obj *otmp) } /* a: apply */ - if (otmp->otyp == CREAM_PIE) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Hit yourself with this cream pie"); + if (otmp->oclass == COIN_CLASS) + ia_addmenu(win, IA_APPLY_OBJ, 'a', "Flip a coin"); + else if (otmp->otyp == CREAM_PIE) + ia_addmenu(win, IA_APPLY_OBJ, 'a', + "Hit yourself with this cream pie"); else if (otmp->otyp == BULLWHIP) ia_addmenu(win, IA_APPLY_OBJ, 'a', "Lash out with this whip"); else if (otmp->otyp == GRAPPLING_HOOK) - ia_addmenu(win, IA_APPLY_OBJ, 'a', "Grapple something with this hook"); + ia_addmenu(win, IA_APPLY_OBJ, 'a', + "Grapple something with this hook"); else if (otmp->otyp == BAG_OF_TRICKS && objects[otmp->otyp].oc_name_known) /* bag of tricks skips this unless discovered */ ia_addmenu(win, IA_APPLY_OBJ, 'a', "Reach into this bag"); @@ -3038,7 +3276,8 @@ itemactions(struct obj *otmp) ia_addmenu(win, IA_APPLY_OBJ, 'a', "Make this figurine transform"); else if (otmp->otyp == UNICORN_HORN) ia_addmenu(win, IA_APPLY_OBJ, 'a', "Use this unicorn horn"); - else if (otmp->otyp == HORN_OF_PLENTY && objects[otmp->otyp].oc_name_known) + else if (otmp->otyp == HORN_OF_PLENTY + && objects[otmp->otyp].oc_name_known) ia_addmenu(win, IA_APPLY_OBJ, 'a', "Blow into the horn of plenty"); else if (otmp->otyp >= FLUTE && otmp->otyp <= DRUM_OF_EARTHQUAKE) ia_addmenu(win, IA_APPLY_OBJ, 'a', "Play this musical instrument"); @@ -3060,27 +3299,54 @@ itemactions(struct obj *otmp) /* d: drop item, works on everything except worn items; those will always have a takeoff/remove choice so we don't have to worry about the menu maybe being empty when 'd' is suppressed */ - if (!already_worn) - ia_addmenu(win, IA_DROP_OBJ, 'd', "Drop this item"); + if (!already_worn) { + Sprintf(buf, "Drop this %s", (otmp->quan > 1L) ? "stack" : "item"); + ia_addmenu(win, IA_DROP_OBJ, 'd', buf); + } /* e: eat item */ - if (otmp->otyp == TIN && uwep && uwep->otyp == TIN_OPENER) - ia_addmenu(win, IA_EAT_OBJ, 'e', - "Open and eat this tin with your tin opener"); - else if (otmp->otyp == TIN) - ia_addmenu(win, IA_EAT_OBJ, 'e', "Open and eat this tin"); - else if (is_edible(otmp)) - ia_addmenu(win, IA_EAT_OBJ, 'e', "Eat this item"); + if (otmp->otyp == TIN) { + Sprintf(buf, "Open %s%s and eat the contents", + (otmp->quan > 1L) ? "one of these tins" : "this tin", + (otmp->otyp == TIN && uwep && uwep->otyp == TIN_OPENER) + ? " with your tin opener" : ""); + ia_addmenu(win, IA_EAT_OBJ, 'e', buf); + } else if (is_edible(otmp)) { + Sprintf(buf, "Eat %s", (otmp->quan > 1L) ? "one of these" : "this"); + ia_addmenu(win, IA_EAT_OBJ, 'e', buf); + } /* E: engrave with item */ - if (otmp->otyp == TOWEL) - ia_addmenu(win, IA_ENGRAVE_OBJ, 'E', "Wipe the floor with this towel"); - else if (otmp->otyp == MAGIC_MARKER) - ia_addmenu(win, IA_ENGRAVE_OBJ, 'E', "Scribble graffiti on the floor"); - else if (otmp->oclass == WEAPON_CLASS || otmp->oclass == WAND_CLASS - || otmp->oclass == GEM_CLASS || otmp->oclass == RING_CLASS) + if (otmp->otyp == TOWEL) { ia_addmenu(win, IA_ENGRAVE_OBJ, 'E', - "Write on the floor with this object"); + "Wipe the floor with this towel"); + } else if (otmp->otyp == MAGIC_MARKER) { + ia_addmenu(win, IA_ENGRAVE_OBJ, 'E', + "Scribble graffiti on the floor"); + } else if (otmp->oclass == WEAPON_CLASS || otmp->oclass == WAND_CLASS + || otmp->oclass == GEM_CLASS || otmp->oclass == RING_CLASS) { + Sprintf(buf, "%s on the %s with %s", + (is_blade(otmp) || otmp->oclass == WAND_CLASS + || ((otmp->oclass == GEM_CLASS || otmp->oclass == RING_CLASS) + && objects[otmp->otyp].oc_tough)) ? "Engrave" : "Write", + surface(u.ux, u.uy), + (otmp->quan > 1L) ? "one of these items" : "this item"); + ia_addmenu(win, IA_ENGRAVE_OBJ, 'E', buf); + } + + /* f: fire quivered ammo */ + if (otmp == uquiver) { + boolean shoot = ammo_and_launcher(otmp, uwep); + + /* FIXME: see the multi-shot FIXME about "one of" for 't: throw' */ + Sprintf(buf, "%s %s", shoot ? "Shoot" : "Throw", + (otmp->quan > 1L) ? "one of these" : "this"); + if (shoot) { + assert(uwep != NULL); + Sprintf(eos(buf), " with your wielded %s", simpleonames(uwep)); + } + ia_addmenu(win, IA_FIRE_OBJ, 'f', buf); + } /* i: #adjust inventory letter; gold can't be adjusted unless there is some in a slot other than '$' (which shouldn't be possible) */ @@ -3114,8 +3380,11 @@ itemactions(struct obj *otmp) /* FIXME: should also handle player owned container (so not flagged 'unpaid') holding shop owned items */ && (mtmp = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE))) != 0 - && inhishop(mtmp)) - ia_addmenu(win, IA_BUY_OBJ, 'p', "Buy this unpaid item"); + && inhishop(mtmp)) { + Sprintf(buf, "Buy this unpaid %s", + (otmp->quan > 1L) ? "stack" : "item"); + ia_addmenu(win, IA_BUY_OBJ, 'p', buf); + } /* P: put on accessory */ if (!already_worn) { @@ -3131,14 +3400,20 @@ itemactions(struct obj *otmp) } /* q: drink item */ - if (otmp->oclass == POTION_CLASS) - ia_addmenu(win, IA_QUAFF_OBJ, 'q', "Quaff this potion"); + if (otmp->oclass == POTION_CLASS) { + Sprintf(buf, "Quaff (drink) %s", + (otmp->quan > 1L) ? "one of these potions" : "this potion"); + ia_addmenu(win, IA_QUAFF_OBJ, 'q', buf); + } /* Q: quiver throwable item */ if ((otmp->oclass == GEM_CLASS || otmp->oclass == WEAPON_CLASS) - && otmp != uquiver) - ia_addmenu(win, IA_QUIVER_OBJ, 'Q', - "Quiver this item for easy throwing"); + && otmp != uquiver) { + Sprintf(buf, "Quiver this %s for easy %s with \'f\'ire", + (otmp->quan > 1L) ? "stack" : "item", + ammo_and_launcher(otmp, uwep) ? "shooting" : "throwing"); + ia_addmenu(win, IA_QUIVER_OBJ, 'Q', buf); + } /* r: read item */ if (item_reading_classification(otmp, buf) == IA_READ_OBJ) @@ -3156,7 +3431,7 @@ itemactions(struct obj *otmp) /* t: throw item */ if (!already_worn) { - const char *verb = ammo_and_launcher(otmp, uwep) ? "Shoot" : "Throw"; + boolean shoot = ammo_and_launcher(otmp, uwep); /* * FIXME: @@ -3166,10 +3441,17 @@ itemactions(struct obj *otmp) * volley count and that could randomly yield 1 here and 2..N * while throwing or vice versa. */ - Sprintf(buf, "%s %s", verb, + Sprintf(buf, "%s %s%s", shoot ? "Shoot" : "Throw", (otmp->quan == 1L) ? "this item" : (otmp->otyp == GOLD_PIECE) ? "them" - : "one of these"); + : "one of these", + /* if otmp is quivered, we've already listed + 'f - shoot|throw this item' as a choice; + if 't' is duplicating that, say so ('t' and 'f' + behavior differs for throwing a stack of gold) */ + (otmp == uquiver && (otmp->otyp != GOLD_PIECE + || otmp->quan == 1L)) + ? " (same as 'f')" : ""); ia_addmenu(win, IA_THROW_OBJ, 't', buf); } @@ -3192,17 +3474,28 @@ itemactions(struct obj *otmp) /* w: wield, hold in hands, works on everything but with different advice text; not mentioned for things that are already wielded */ - if (otmp == uwep) - ; - else if (otmp->oclass == WEAPON_CLASS || is_weptool(otmp) - || is_wet_towel(otmp)) - ia_addmenu(win, IA_WIELD_OBJ, 'w', "Wield this as your weapon"); - else if (otmp->otyp == TIN_OPENER) - ia_addmenu(win, IA_WIELD_OBJ, 'w', "Hold the tin opener to open tins"); - else if (!already_worn) - /* FIXME: there's no concept of "holding an item" that's any - different from having it in inventory; 'w' means wield as weapon */ - ia_addmenu(win, IA_WIELD_OBJ, 'w', "Hold this item in your hands"); + if (otmp == uwep || cantwield(gy.youmonst.data)) { + ; /* either already wielded or can't wield anything; skip 'w' */ + } else if (otmp->oclass == WEAPON_CLASS || is_weptool(otmp) + || is_wet_towel(otmp) || otmp->otyp == HEAVY_IRON_BALL) { + Sprintf(buf, "Wield this %s as your weapon", + (otmp->quan > 1L) ? "stack" : "item"); + ia_addmenu(win, IA_WIELD_OBJ, 'w', buf); + } else if (otmp->otyp == TIN_OPENER) { + ia_addmenu(win, IA_WIELD_OBJ, 'w', + "Wield the tin opener to easily open tins"); + } else if (!already_worn) { + /* originally this was using "hold this item in your hands" but + there's no concept of "holding an item", plus it unwields + whatever item you already have wielded so use "wield this item" */ + Sprintf(buf, "Wield this %s in your %s", + (otmp->quan > 1L) ? "stack" : "item", + /* only two-handed weapons and unicorn horns care about + pluralizing "hand" and they won't reach here, but plural + sounds better when poly'd into something with "claw" */ + makeplural(body_part(HAND))); + ia_addmenu(win, IA_WIELD_OBJ, 'w', buf); + } /* W: wear armor */ if (!already_worn) { @@ -3218,11 +3511,44 @@ itemactions(struct obj *otmp) ia_addmenu(win, IA_SWAPWEAPON, 'x', "Ready this as an alternate weapon"); else if (otmp == uswapwep) - ia_addmenu(win, IA_SWAPWEAPON, 'x', "Swap this with your main weapon"); + ia_addmenu(win, IA_SWAPWEAPON, 'x', + "Swap this with your main weapon"); + + /* this is based on TWOWEAPOK() in wield.c; we don't call can_two_weapon() + because it is very verbose; attempting to two-weapon might be rejected + but we screen out most reasons for rejection before offering it as a + choice */ +#define MAYBETWOWEAPON(obj) \ + ((((obj)->oclass == WEAPON_CLASS) \ + ? !(is_launcher(obj) || is_ammo(obj) || is_missile(obj)) \ + : is_weptool(obj)) \ + && !bimanual(obj)) + + /* X: Toggle two-weapon mode on or off */ + if ((otmp == uwep || otmp == uswapwep) + /* if already two-weaponing, no special checks needed to toggle off */ + && (u.twoweap + /* but if not, try to filter most "you can't do that" here */ + || (could_twoweap(gy.youmonst.data) && !uarms + && uwep && MAYBETWOWEAPON(uwep) + && uswapwep && MAYBETWOWEAPON(uswapwep)))) { + Sprintf(buf, "Toggle two-weapon combat %s", u.twoweap ? "off" : "on"); + ia_addmenu(win, IA_TWOWEAPON, 'X', buf); + } + +#undef MAYBETWOWEAPON /* z: Zap wand */ if (otmp->oclass == WAND_CLASS) - ia_addmenu(win, IA_ZAP_OBJ, 'z', "Zap this wand to release its magic"); + ia_addmenu(win, IA_ZAP_OBJ, 'z', + "Zap this wand to release its magic"); + + /* ?: Look up an item in the game's database */ + if (ia_checkfile(otmp)) { + Sprintf(buf, "Look up information about %s", + (otmp->quan > 1L) ? "these" : "this"); + ia_addmenu(win, IA_WHATIS_OBJ, '/', buf); + } Sprintf(buf, "Do what with %s?", the(cxname(otmp))); end_menu(win, buf); @@ -3242,22 +3568,58 @@ itemactions(struct obj *otmp) return ECMD_OK; } - -/* the #inventory command */ -int -ddoinv(void) +/* show some or all of inventory while allowing the picking of an item in + order to preform context-sensitive item action on it; always returns 'ok'; + invent subsets specified by the ')', '[', '(', '=', '"', or '*' commands + when they're invoked with the 'm' prefix (or without it for '*') */ +staticfn int +dispinv_with_action( + char *lets, /* list of invlet values to include */ + boolean use_inuse_ordering, /* affects sortloot() and header labels */ + const char *alt_label) /* alternate value for in-use "Accessories" */ { - struct obj *otmp; - char c = display_inventory((char *) 0, TRUE); + struct obj *otmp, *nextobj; + const char *save_accessories = 0; + char c, save_sortloot = 0; + unsigned len = lets ? (unsigned) strlen(lets) : 0U; + boolean menumode = (len != 1 || iflags.menu_requested) ? TRUE : FALSE, + save_force_invmenu = iflags.force_invmenu; + + if (use_inuse_ordering) { + save_accessories = inuse_headers[4]; + save_sortloot = flags.sortloot; + + flags.sortloot = 'i'; /* checked by display_pickinv() */ + if (alt_label) + inuse_headers[4] = alt_label; + } + iflags.force_invmenu = FALSE; + + c = display_inventory(lets, menumode); + + if (use_inuse_ordering) { + flags.sortloot = save_sortloot; + inuse_headers[4] = save_accessories; + } + 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; } +/* the #inventory command (not much left...) */ +int +ddoinv(void) +{ + return dispinv_with_action((char *) 0, FALSE, NULL); +} + /* * find_unpaid() * @@ -3267,7 +3629,7 @@ ddoinv(void) * next unpaid object is returned. This routine recursively follows * containers. */ -static struct obj * +staticfn struct obj * find_unpaid(struct obj *list, struct obj **last_found) { struct obj *obj; @@ -3303,11 +3665,12 @@ free_pickinv_cache(void) * Internal function used by display_inventory and getobj that can display * inventory and return a count as well as a letter. */ -static char +staticfn char display_pickinv( const char *lets, /* non-compacted list of invlet values */ const char *xtra_choice, /* non-object "bare hands" or "fingers" */ const char *query, /* optional; prompt string for menu */ + boolean allowxtra, /* hands are allowed (maybe alternate) choice */ boolean want_reply, /* True: select an item, False: just display */ long *out_cnt) /* optional; count player entered when selecting an item */ { @@ -3315,20 +3678,22 @@ display_pickinv( not_carrying_anything[] = "Not carrying anything", not_using_anything[] = "Not using any items", only_carrying_gold[] = "Only carrying gold"; - struct obj *otmp, wizid_fakeobj; + struct obj *otmp, wizid_fakeobj, inuse_fakeobj; char ilet, ret, *formattedobj; const char *invlet = flags.inv_order; - int n, classcount; + int n, classcount, inusecount = 0; winid win; /* windows being used */ anything any; menu_item *selected; unsigned sortflags; Loot *sortedinvent, *srtinv; + int8_t prevorderclass; + boolean (*filter)(struct obj *) = (boolean (*)(OBJ_P)) 0; boolean wizid = (wizard && iflags.override_ID), gotsomething = FALSE; - int clr = 0, menu_behavior = MENU_BEHAVE_STANDARD; - boolean show_gold = TRUE, sparse = FALSE, inuse_only = FALSE, - skipped_gold = FALSE, skipped_noninuse = FALSE, - doing_perm_invent = FALSE, save_flags_sortpack = flags.sortpack; + 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, + usextra = (xtra_choice && allowxtra); if (lets && !*lets) lets = 0; /* simplify tests: (lets) instead of (lets && *lets) */ @@ -3338,9 +3703,9 @@ display_pickinv( win = 0; /* passed to dump_putstr() which ignores it... */ } else #endif - if (lets || xtra_choice || wizid || want_reply + if (lets || usextra || wizid || want_reply #ifdef TTY_PERM_INVENT - || !gi.in_sync_perminvent + /*|| !gi.in_sync_perminvent*/ #endif || WIN_INVEN == WIN_ERR) { /* partial inventory in perm_invent setting; don't operate on @@ -3349,15 +3714,15 @@ display_pickinv( if (gc.cached_pickinv_win == WIN_ERR) gc.cached_pickinv_win = create_nhwindow(NHW_MENU); win = gc.cached_pickinv_win; + if (flags.sortloot == 'i') + inuse_only = TRUE; } else { win = WIN_INVEN; menu_behavior = MENU_BEHAVE_PERMINV; prepare_perminvent(win); show_gold = ((wri_info.fromcore.invmode & InvShowGold) != 0); - sparse = ((wri_info.fromcore.invmode & InvSparse) != 0); inuse_only = ((wri_info.fromcore.invmode & InvInUse) != 0); doing_perm_invent = TRUE; - nhUse(sparse); } /* * Exit early if no inventory -- but keep going if we are doing @@ -3370,17 +3735,18 @@ display_pickinv( * an issue if empty checks are done before hand and the call * to here is short circuited away. * - * 2: our count here is only to distinguish between 0 and 1 and - * more than 1; for the last one, we don't need a precise number. + * 2: our count here is only to distinguish between 0 or 1 or + * more than 1; for the last case, we don't need a precise number. * For perm_invent update we force 'more than 1'. */ - n = (iflags.perm_invent && !lets && !want_reply) ? 2 + n = (doing_perm_invent && !lets && !want_reply) ? 2 : lets ? (int) strlen(lets) : !gi.invent ? 0 : !gi.invent->nobj ? 1 : 2; /* for xtra_choice, there's another 'item' not included in initial 'n'; - for !lets (full gi.invent) and for override_ID (wizard mode identify), - skip message_menu handling of single item even if item count was 1 */ - if (xtra_choice || (n == 1 && (!lets || wizid))) + for !lets (full invent or inuse_only) and for override_ID (wizard + mode identify), skip message_menu handling of single item even if + item count was 1 */ + if (usextra || (n == 1 && (!lets || wizid))) ++n; if (n == 0) { @@ -3392,14 +3758,15 @@ display_pickinv( if (!flags.invlet_constant) reassign(); - if (n == 1 && !iflags.force_invmenu) { + if (n == 1 && !iflags.force_invmenu && !iflags.menu_requested) { /* when only one item of interest, use pline instead of menus; we actually use a fake message-line menu in order to allow the user to perform selection at the --More-- prompt for tty */ ret = '\0'; - if (xtra_choice) { + if (usextra) { /* xtra_choice is "bare hands" (wield), "fingertip" (Engrave), - "nothing" (ready Quiver), or "fingers" (apply grease) */ + "nothing" (prepare Quiver), "fingers" (apply grease), or + "hands" (default) */ ret = message_menu(HANDS_SYM, PICK_ONE, xprname((struct obj *) 0, xtra_choice, HANDS_SYM, TRUE, 0L, 0L)); /* '-' */ @@ -3421,17 +3788,47 @@ display_pickinv( sortflags = (flags.sortloot == 'f') ? SORTLOOT_LOOT : SORTLOOT_INVLET; if (flags.sortpack) sortflags |= SORTLOOT_PACK; + save_flags_sortpack = flags.sortpack; #ifdef TTY_PERM_INVENT if (doing_perm_invent && WINDOWPORT(tty)) { - sortflags = SORTLOOT_INVLET; - save_flags_sortpack = flags.sortpack; flags.sortpack = FALSE; + sortflags = SORTLOOT_INVLET; } -#else - nhUse(save_flags_sortpack); #endif - sortedinvent = sortloot(&gi.invent, sortflags, FALSE, - (boolean (*)(OBJ_P)) 0); + if (inuse_only) { + flags.sortpack = FALSE; + sortflags = SORTLOOT_INUSE; /* override */ + filter = is_inuse; + if (!uwep) { + /* + * inuse_only and not wielding anything: insert "bare hands" + * into primary weapon slot. Unlike adding an extra menu + * entry for 'xtra_choice' at top of menu, we need an object + * in invent for it to be sorted into desired position. + * It will need custom formatting below. + */ + inuse_fakeobj = cg.zeroobj; /* STRANGE_OBJECT, ILLOBJ_CLASS */ + inuse_fakeobj.invlet = HANDS_SYM; /* '-' */ + inuse_fakeobj.owornmask = W_WEP; /* inuse_classify needs this */ + inuse_fakeobj.where = OBJ_INVENT; /* is_inuse filter needs this */ + inuse_fakeobj.nobj = gi.invent; + gi.invent = &inuse_fakeobj; + } + } + + sortedinvent = sortloot(&gi.invent, sortflags, FALSE, filter); + /* inuse_only: if we inserted bare hands as a fake weapon, remove them; + although the fake object will no longer be in invent, sortedinvent + will still contain a pointer to it */ + if (gi.invent == &inuse_fakeobj) { + gi.invent = inuse_fakeobj.nobj; + inuse_fakeobj.nobj = (struct obj *) 0; + /* if inuse_fakeobj is the only thing present in sortedinvent, get + rid of it in order to produce "not using any items" */ + if (sortedinvent[0].obj == &inuse_fakeobj && !sortedinvent[1].obj) + sortedinvent[0].obj = (struct obj *) 0; + } + start_menu(win, menu_behavior); any = cg.zeroany; if (wizid) { @@ -3444,12 +3841,10 @@ display_pickinv( Sprintf(eos(prompt), " -- unidentified or partially identified item%s", plur(unid_cnt)); - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, prompt, - MENU_ITEMFLAGS_NONE); + add_menu_str(win, prompt); if (!unid_cnt) { - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, - "(all items are permanently identified already)", - MENU_ITEMFLAGS_NONE); + add_menu_str(win, + "(all items are permanently identified already)"); gotsomething = TRUE; } else { any.a_obj = &wizid_fakeobj; @@ -3467,12 +3862,10 @@ display_pickinv( ATR_NONE, clr, prompt, MENU_ITEMFLAGS_SKIPINVERT); gotsomething = TRUE; } - } else if (xtra_choice) { + } else if (usextra) { /* wizard override ID and xtra_choice are mutually exclusive */ if (flags.sortpack) - add_menu(win, &nul_glyphinfo, &any, 0, 0, - iflags.menu_headings, clr, - "Miscellaneous", MENU_ITEMFLAGS_NONE); + add_menu_heading(win, "Miscellaneous"); any.a_char = HANDS_SYM; /* '-' */ add_menu(win, &nul_glyphinfo, &any, HANDS_SYM, 0, ATR_NONE, clr, xtra_choice, MENU_ITEMFLAGS_NONE); @@ -3493,50 +3886,68 @@ display_pickinv( nextclass: classcount = 0; + prevorderclass = 0; for (srtinv = sortedinvent; (otmp = srtinv->obj) != 0; ++srtinv) { int tmpglyph; glyph_info tmpglyphinfo = nul_glyphinfo; + /* for showing a set of specific letters, skip ones not in the set */ if (lets && !strchr(lets, otmp->invlet)) continue; if (!flags.sortpack || otmp->oclass == *invlet) { if (wizid && !not_fully_identified(otmp)) continue; - if (doing_perm_invent) { - /* when showing equipment in use, gold shouldn't be excluded - just because !show_gold is set; it might be quivered; - tool_being_used() matches lit lamps/candles and active - leashes, neither of which set owornmask */ - if (inuse_only) { - if (!otmp->owornmask && !tool_being_used(otmp)) { - skipped_noninuse = TRUE; - continue; - } - } else if (otmp->invlet == GOLD_SYM && !show_gold) { + if (inuse_only) { + /* for inuse-only, start with an extra header */ + if (!inusecount++) + add_menu_heading(win, doing_perm_invent ? "In use" + : "Inventory in use"); + } else if (doing_perm_invent && !show_gold) { + /* don't skip gold if it is quivered, even for !show_gold */ + if (otmp->invlet == GOLD_SYM && !otmp->owornmask) { skipped_gold = TRUE; continue; } } - any = cg.zeroany; /* all bits zero */ - ilet = otmp->invlet; - if (flags.sortpack && !classcount) { - add_menu(win, &nul_glyphinfo, &any, 0, 0, - iflags.menu_headings, clr, - let_to_name(*invlet, FALSE, - (want_reply && iflags.menu_head_objsym)), - MENU_ITEMFLAGS_NONE); + /* maybe insert a class header */ + if (inuse_only ? (srtinv->orderclass != prevorderclass) + : (flags.sortpack && !classcount)) { + boolean withsym = (want_reply && iflags.menu_head_objsym); + const char *class_header = inuse_only + ? inuse_headers[(int) srtinv->orderclass] + : (const char *) let_to_name(*invlet, FALSE, withsym); + + add_menu_heading(win, class_header); classcount++; + prevorderclass = srtinv->orderclass; } + + ilet = otmp->invlet; + any = cg.zeroany; /* all bits zero */ if (wizid) any.a_obj = otmp; else any.a_char = ilet; - tmpglyph = obj_to_glyph(otmp, rn2_on_display_rng); - map_glyphinfo(0, 0, tmpglyph, 0U, &tmpglyphinfo); - formattedobj = doname(otmp); - add_menu(win, &tmpglyphinfo, &any, ilet, - wizid ? def_oc_syms[(int) otmp->oclass].sym : 0, - ATR_NONE, clr, formattedobj, MENU_ITEMFLAGS_NONE); + + if (otmp == &inuse_fakeobj) { + /* fake item to format as "bare|gloved hands" */ + char barehands[QBUFSZ]; + + /* like doname() below, makeplural() returns an obuf[] */ + formattedobj = makeplural(body_part(HAND)); + Sprintf(barehands, "%s %s (no weapon)", + uarmg ? "gloved" : "bare", formattedobj); + add_menu(win, &nul_glyphinfo, &any, ilet, 0, + ATR_NONE, clr, barehands, MENU_ITEMFLAGS_NONE); + } else { + /* normal inventory item */ + tmpglyph = obj_to_glyph(otmp, rn2_on_display_rng); + map_glyphinfo(0, 0, tmpglyph, 0U, &tmpglyphinfo); + formattedobj = doname(otmp); + add_menu(win, &tmpglyphinfo, &any, ilet, + wizid ? def_oc_syms[(int) otmp->oclass].sym : 0, + ATR_NONE, clr, formattedobj, MENU_ITEMFLAGS_NONE); + } /* doname() uses a static pool of obuf[] output buffers and we don't want inventory display to overwrite all of them, so when we've used one we release it for re-use */ @@ -3552,37 +3963,42 @@ display_pickinv( goto nextclass; } } - /* default for force_invmenu is a list of likely candidates; - add '*' for 'show all' as an extra choice unless list already - includes everything; won't work via keyboard if current menu - uses '*' as group accelerator for gems but might work via mouse */ - if (iflags.force_invmenu && lets && want_reply - && (int) strlen(lets) < inv_cnt(TRUE)) { + if (save_flags_sortpack != flags.sortpack) + flags.sortpack = save_flags_sortpack; + + /* default for force_invmenu is a menu listing likely candidates; + add '*' for 'list all' as an extra choice unless the menu already + includes everything; when reissuing the menu after player has + picked '*', add '?' for 'list likely candidates' to reverse that */ + if (iflags.force_invmenu && want_reply) { + const char *menutext = NULL; + any = cg.zeroany; - add_menu(win, &nul_glyphinfo, &any, 0, 0, - iflags.menu_headings, clr, "Special", MENU_ITEMFLAGS_NONE); - any.a_char = '*'; - add_menu(win, &nul_glyphinfo, &any, '*', 0, ATR_NONE, clr, - "(list everything)", MENU_ITEMFLAGS_NONE); - gotsomething = TRUE; + if ((allowxtra && !usextra) + || (lets && (int) strlen(lets) < inv_cnt(TRUE))) { + any.a_char = '*'; + menutext = "(list everything)"; + } else if (!lets) { + any.a_char = '?'; + menutext = "(list likely candidates)"; + } + if (menutext) { + add_menu_heading(win, "Special"); + add_menu(win, &nul_glyphinfo, &any, any.a_char, 0, ATR_NONE, clr, + menutext, MENU_ITEMFLAGS_NONE); + gotsomething = TRUE; /* menu isn't empty */ + } } unsortloot(&sortedinvent); /* for permanent inventory where nothing has been listed (because there isn't anything applicable to list; the n==0 case above gets skipped for perm_invent), put something into the menu */ - if (iflags.perm_invent && !lets && !gotsomething) { - any = cg.zeroany; - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, - (inuse_only && skipped_noninuse) ? not_using_anything - : (!show_gold && skipped_gold) ? only_carrying_gold - : not_carrying_anything, - MENU_ITEMFLAGS_NONE); + if (doing_perm_invent && !lets && !gotsomething) { + add_menu_str(win, inuse_only ? not_using_anything + : (!show_gold && skipped_gold) ? only_carrying_gold + : not_carrying_anything); want_reply = FALSE; } -#ifdef TTY_PERM_INVENT - if (doing_perm_invent && WINDOWPORT(tty)) - flags.sortpack = save_flags_sortpack; -#endif end_menu(win, (query && *query) ? query : (char *) 0); n = select_menu(win, @@ -3644,7 +4060,8 @@ display_inventory(const char *lets, boolean want_reply) for (otmp = gi.invent; otmp; otmp = otmp->nobj) if (otmp->invlet == cmdq->key && (!lets || !*lets - || strchr(lets, def_oc_syms[(int)otmp->oclass].sym))) { + || strchr(lets, + def_oc_syms[(int) otmp->oclass].sym))) { free(cmdq); return otmp->invlet; } @@ -3656,14 +4073,14 @@ display_inventory(const char *lets, boolean want_reply) return '\0'; } return display_pickinv(lets, (char *) 0, (char *) 0, - want_reply, (long *) 0); + FALSE, want_reply, (long *) 0); } /* * Show what is current using inventory letters. * */ -static char +staticfn char display_used_invlets(char avoidlet) { struct obj *otmp; @@ -3674,7 +4091,7 @@ display_used_invlets(char avoidlet) winid win; anything any; menu_item *selected; - int clr = 0; + int clr = NO_COLOR; if (gi.invent) { win = create_nhwindow(NHW_MENU); @@ -3689,17 +4106,16 @@ display_used_invlets(char avoidlet) if (!flags.sortpack || otmp->oclass == *invlet) { if (flags.sortpack && !classcount) { any = cg.zeroany; /* zero */ - add_menu(win, &nul_glyphinfo, &any, 0, 0, - iflags.menu_headings, clr, - let_to_name(*invlet, FALSE, FALSE), - MENU_ITEMFLAGS_NONE); + add_menu_heading(win, + let_to_name(*invlet, FALSE, FALSE)); classcount++; } any.a_char = ilet; tmpglyph = obj_to_glyph(otmp, rn2_on_display_rng); map_glyphinfo(0, 0, tmpglyph, 0U, &tmpglyphinfo); add_menu(win, &tmpglyphinfo, &any, ilet, 0, - ATR_NONE, clr, doname(otmp), MENU_ITEMFLAGS_NONE); + ATR_NONE, clr, doname(otmp), + MENU_ITEMFLAGS_NONE); } } if (flags.sortpack && *++invlet) @@ -3851,7 +4267,7 @@ count_contents( return count; } -static void +staticfn void dounpaid( int count, /* unpaid items in inventory */ int floorcount, /* unpaid items on floor (rare) */ @@ -3873,7 +4289,7 @@ dounpaid( } if (otmp && !contnr) { /* 1 item; use pline instead of popup menu */ - cost = unpaid_cost(otmp, FALSE); + cost = unpaid_cost(otmp, COST_NOCONTENTS); iflags.suppress_price++; /* suppress "(unpaid)" suffix */ pline1(xprname(otmp, distant_name(otmp, doname), carried(otmp) ? otmp->invlet : CONTAINED_SYM, @@ -3883,7 +4299,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(); @@ -3899,7 +4315,7 @@ dounpaid( classcount++; } - totcost += cost = unpaid_cost(otmp, FALSE); + totcost += cost = unpaid_cost(otmp, COST_NOCONTENTS); iflags.suppress_price++; /* suppress "(unpaid)" suffix */ putstr(win, 0, xprname(otmp, distant_name(otmp, doname), ilet, TRUE, cost, 0L)); @@ -3925,7 +4341,7 @@ dounpaid( marker = (struct obj *) 0; /* haven't found any */ while (find_unpaid(otmp->cobj, &marker)) { - totcost += cost = unpaid_cost(marker, FALSE); + totcost += cost = unpaid_cost(marker, COST_NOCONTENTS); contcost += cost; if (otmp->cknown) { iflags.suppress_price++; /* suppress "(unpaid)" sfx */ @@ -3989,7 +4405,8 @@ dounpaid( return; } -static boolean + +staticfn boolean this_type_only(struct obj *obj) { boolean res = (obj->oclass == gt.this_type); @@ -4048,7 +4465,7 @@ dotypeinv(void) title[0] = '\0'; u_carried = count_unpaid(gi.invent); u_floor = count_unpaid(fobj); - u_buried = count_unpaid(gl.level.buriedobjlist); + u_buried = count_unpaid(svl.level.buriedobjlist); any_unpaid = u_carried + u_floor + u_buried; tally_BUCX(gi.invent, FALSE, &bcnt, &ucnt, &ccnt, &xcnt, &ocnt, &jcnt); @@ -4269,7 +4686,7 @@ dfeature_at(coordxy x, coordxy y, char *buf) else if (is_lava(x, y)) cmap = S_lava; /* "molten lava" */ else if (is_ice(x, y)) - cmap = S_ice; /* "ice" */ + dfeature = ice_descr(x, y, altbuf), cmap = -1; /* "ice" */ else if (is_pool(x, y)) dfeature = "pool of water"; else if (IS_GRASS(ltyp)) @@ -4363,10 +4780,25 @@ look_here( } return (!!Blind ? ECMD_TIME : ECMD_OK); } - if (!skip_objects && (trap = t_at(u.ux, u.uy)) && trap->tseen) - There("is %s here.", an(trapname(trap->ttyp, FALSE))); + if (!skip_objects) { + NhRegion *reg; + char regbuf[QBUFSZ]; + + regbuf[0] = '\0'; + if ((reg = visible_region_at(u.ux, u.uy)) != 0) + Sprintf(regbuf, "a %s cloud", + reg_damg(reg) ? "poison gas" : "vapor"); + if ((trap = t_at(u.ux, u.uy)) != 0 && !trap->tseen) + trap = (struct trap *) NULL; + + if (reg || trap) + There("is %s%s%s here.", + reg ? regbuf : "", + (reg && trap) ? " and " : "", + trap ? an(trapname(trap->ttyp, FALSE)) : ""); + } - otmp = gl.level.objects[u.ux][u.uy]; + otmp = svl.level.objects[u.ux][u.uy]; dfeature = dfeature_at(u.ux, u.uy, fbuf2); if (dfeature && !strcmp(dfeature, "pool of water") && Underwater) dfeature = 0; @@ -4377,19 +4809,28 @@ look_here( if (dfeature && !strncmp(dfeature, "altar ", 6)) { /* don't say "altar" twice, dfeature has more info */ You("try to feel what is here."); + } else if (SURFACE_AT(u.ux, u.uy) == ICE) { + /* using describe_decor() to handle ice is simpler than + replicating it in the conditional message construction */ + if (!flags.mention_decor || iflags.prev_decor == ICE) + force_decor(FALSE); + /* plain "ice" if blind and levitating, otherwise "solid ice" &c; + "There is [thin ]ice here. You try to feel what is on it." */ + You("try to feel what is on it."); + skip_dfeature = TRUE; /* ice already described */ } else { - const char *where = (Blind && !can_reach_floor(TRUE)) - ? "lying beneath you" - : "lying here on the ", - *onwhat = (Blind && !can_reach_floor(TRUE)) - ? "" - : surface(u.ux, u.uy); + boolean cant_reach = !can_reach_floor(TRUE); + const char *surf = surface(u.ux, u.uy), + *where = cant_reach ? "lying beneath you" + : "lying here on the ", + *onwhat = cant_reach ? "" : surf; You("try to feel what is %s%s.", drift ? "floating here" : where, drift ? "" : onwhat); + + if (dfeature && !drift && !strcmp(dfeature, surf)) + skip_dfeature = TRUE; /* terrain feature already identified */ } - if (dfeature && !drift && !strcmp(dfeature, surface(u.ux, u.uy))) - dfeature = 0; /* ice already identified */ trap = t_at(u.ux, u.uy); if (!can_reach_floor(trap && is_pit(trap->ttyp))) { pline("But you can't reach it!"); @@ -4397,8 +4838,26 @@ look_here( } } - if (dfeature && !skip_dfeature) - Sprintf(fbuf, "There is %s here.", an(dfeature)); + if (dfeature && !skip_dfeature) { + const char *p; + int article = 1; /* 0 => none, 1 => a/an, 2 => the (not used here) */ + + /* "molten lava", "iron bars", and plain "ice" are handled as special + cases in an() but probably shouldn't be; don't rely on that */ + if (!strcmp(dfeature, "molten lava") + || !strcmp(dfeature, "iron bars") + || !strcmp(dfeature, "ice") + || !strncmp(dfeature, "frozen ", 7) /* ice while hallucinating */ + /* thawing ice ("solid ice", "thin ice", &c) */ + || ((p = strchr(dfeature, ' ')) != 0 && !strcmpi(p, " ice"))) + article = 0; + if (article == 1) + dfeature = an(dfeature); + + /* hardcoded "is" worked here because "iron bars" is actually + "set of iron bars"; use vtense() instead of relying on that */ + Sprintf(fbuf, "There %s %s here.", vtense(dfeature, "are"), dfeature); + } if (!otmp || is_lava(u.ux, u.uy) || (is_pool(u.ux, u.uy) && !Underwater)) { @@ -4529,7 +4988,8 @@ stackobj(struct obj *obj) { struct obj *otmp; - for (otmp = gl.level.objects[obj->ox][obj->oy]; otmp; otmp = otmp->nexthere) + for (otmp = svl.level.objects[obj->ox][obj->oy]; otmp; + otmp = otmp->nexthere) if (otmp != obj && merged(&obj, &otmp)) break; return; @@ -4538,8 +4998,8 @@ stackobj(struct obj *obj) /* returns TRUE if obj & otmp can be merged; used in invent.c and mkobj.c */ boolean mergable( - register struct obj *otmp, /* potential 'into' stack */ - register struct obj *obj) /* 'combine' stack */ + struct obj *otmp, /* potential 'into' stack */ + struct obj *obj) /* 'combine' stack */ { size_t objnamelth = 0, otmpnamelth = 0; @@ -4555,6 +5015,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 */ @@ -4570,7 +5032,8 @@ mergable( if (obj->unpaid != otmp->unpaid || obj->spe != otmp->spe || obj->no_charge != otmp->no_charge || obj->obroken != otmp->obroken - || obj->otrapped != otmp->otrapped || obj->lamplit != otmp->lamplit) + || obj->otrapped != otmp->otrapped || obj->lamplit != otmp->lamplit + || obj->how_lost != otmp->how_lost) return FALSE; if (obj->oclass == FOOD_CLASS @@ -4578,13 +5041,15 @@ mergable( return FALSE; if (obj->dknown != otmp->dknown - /* || (obj->bknown != otmp->bknown && !Role_if(PM_CLERIC)) */ + || (obj->bknown != otmp->bknown && !Role_if(PM_CLERIC) && + (Blind || Hallucination)) || obj->oeroded != otmp->oeroded || obj->oeroded2 != otmp->oeroded2 || obj->material != otmp->material || obj->greased != otmp->greased) return FALSE; if ((erosion_matters(obj) || destroyable_oclass(obj->oclass)) - && obj->oerodeproof != otmp->oerodeproof) + && (obj->oerodeproof != otmp->oerodeproof + || (obj->rknown != otmp->rknown && (Blind || Hallucination)))) return FALSE; if (obj->otyp == CORPSE || obj->otyp == EGG || obj->otyp == TIN) { @@ -4626,7 +5091,9 @@ mergable( if ((objnamelth != otmpnamelth && ((objnamelth && otmpnamelth) || obj->otyp == CORPSE)) || (objnamelth && otmpnamelth - && strncmp(ONAME(obj), ONAME(otmp), objnamelth))) + /* verify pointers before deref for static analyzer */ + && has_oname(obj) && has_oname(otmp) + && strncmp(ONAME(obj), ONAME(otmp), objnamelth))) return FALSE; /* if one has an attached mail command, other must have same command */ @@ -4647,6 +5114,9 @@ mergable( if (obj->oartifact != otmp->oartifact) return FALSE; + if (obj->known != otmp->known && (Blind || Hallucination)) + return FALSE; + return TRUE; } @@ -4662,7 +5132,7 @@ doprgold(void) person, but you have no such preternatural gold-sense. */ long hmoney = hidden_gold(FALSE); - if (Verbose(1, doprgold)) { + if (flags.verbose) { char buf[BUFSZ]; if (!umoney) { @@ -4686,6 +5156,14 @@ doprgold(void) You("have no money."); } shopper_financial_report(); + + if (umoney && iflags.menu_requested) { + char dollarsign[] = "$"; + + /* mustn't use TRUE or gold wouldn't show up unless it was quivered */ + (void) dispinv_with_action(dollarsign, FALSE, NULL); + } + return ECMD_OK; } @@ -4695,16 +5173,30 @@ doprwep(void) { if (!uwep) { You("are %s.", empty_handed()); - } else { + } else if (!iflags.menu_requested) { prinv((char *) 0, uwep, 0L); if (u.twoweap) prinv((char *) 0, uswapwep, 0L); + } else { + char lets[4]; /* 4: uwep, uswapwep, uquiver, terminator */ + int ct = 0; + + /* obj_to_let() will assign letters to all of invent if necessary + (for '!fixinv') so doesn't need to be repeated once called here */ + lets[ct++] = obj_to_let(uwep); + if (uswapwep) + lets[ct++] = uswapwep->invlet; + if (uquiver) + lets[ct++] = uquiver->invlet; + lets[ct] = '\0'; + + (void) dispinv_with_action(lets, TRUE, NULL); } return ECMD_OK; } /* caller is responsible for checking !wearing_armor() */ -static void +staticfn void noarmor(boolean report_uskin) { if (!uskin || !report_uskin) { @@ -4720,8 +5212,6 @@ noarmor(boolean report_uskin) int doprarm(void) { - char lets[8]; - int ct = 0; /* * Note: players sometimes get here by pressing a function key which * transmits ''ESC [ '' rather than by pressing '['; @@ -4731,22 +5221,30 @@ doprarm(void) if (!wearing_armor()) { noarmor(TRUE); } else { - if (uarmu) - lets[ct++] = obj_to_let(uarmu); + char lets[8]; /* 8: up to 7 pieces of armor plus terminator */ + int ct = 0; + + /* obj_to_let() will assign letters to all of invent if necessary + (for '!fixinv') so doesn't need to be repeated once called, but + each armor slot doesn't know whether any that precede have made + that call so just do it for each one; use SORTPACK_INUSE order */ if (uarm) lets[ct++] = obj_to_let(uarm); if (uarmc) lets[ct++] = obj_to_let(uarmc); - if (uarmh) - lets[ct++] = obj_to_let(uarmh); if (uarms) lets[ct++] = obj_to_let(uarms); + if (uarmh) + lets[ct++] = obj_to_let(uarmh); if (uarmg) lets[ct++] = obj_to_let(uarmg); if (uarmf) lets[ct++] = obj_to_let(uarmf); + if (uarmu) + lets[ct++] = obj_to_let(uarmu); lets[ct] = 0; - (void) display_inventory(lets, FALSE); + + (void) dispinv_with_action(lets, TRUE, NULL); } return ECMD_OK; } @@ -4758,15 +5256,32 @@ doprring(void) if (!uleft && !uright) { You("are not wearing any rings."); } else { - char lets[3]; + char lets[3]; /* 3: uright, uleft, terminator */ + boolean use_inuse_mode = FALSE; int ct = 0; - if (uleft) - lets[ct++] = obj_to_let(uleft); - if (uright) + /* if either ring is a meat ring, switch to use_inuse_mode in order + to label it/them as "Rings" rather than "Comestibles" */ + if (uright) { lets[ct++] = obj_to_let(uright); - lets[ct] = 0; - (void) display_inventory(lets, FALSE); + if (uright->oclass != RING_CLASS) + use_inuse_mode = TRUE; + } + if (uleft) { + lets[ct++] = obj_to_let(uleft); + if (uleft->oclass != RING_CLASS) + use_inuse_mode = TRUE; + } + lets[ct] = '\0'; + /* also switch to use_inuse_mode if there are two rings or player + used the 'm' prefix */ + if (ct > 1 || iflags.menu_requested) + use_inuse_mode = TRUE; + + (void) dispinv_with_action(lets, use_inuse_mode, + /* note; alternate label will be ignored + if 'use_inuse_mode' is False */ + (ct == 1) ? "Ring" : "Rings"); } return ECMD_OK; } @@ -4775,21 +5290,34 @@ doprring(void) int dopramulet(void) { - if (!uamul) + if (!uamul) { You("are not wearing an amulet."); - else - prinv((char *) 0, uamul, 0L); + } else { + char lets[2]; + + /* using display_inventory() instead of prinv() allows player + to use 'm "' to force and menu and be able to choose amulet + in order to perform a context-sensitive item action */ + lets[0] = obj_to_let(uamul), lets[1] = '\0'; + + (void) dispinv_with_action(lets, TRUE, "Amulet"); + } return ECMD_OK; } /* is 'obj' a tool that's in use? can't simply check obj->owornmask */ -static boolean +staticfn boolean tool_being_used(struct obj *obj) { + /* + * [Should this also include lit potions of oil? They're not tools + * but they are "in use" without being noticeable via obj->owornmask.] + */ if ((obj->owornmask & (W_TOOL | W_SADDLE)) != 0L) return TRUE; if (obj->oclass != TOOL_CLASS) return FALSE; + /* [don't actually need to check uwep here; caller catches it] */ return (boolean) (obj == uwep || obj->lamplit || (obj->otyp == LEASH && obj->leashmon)); } @@ -4800,7 +5328,7 @@ doprtool(void) { struct obj *otmp; int ct = 0; - char lets[52 + 1]; + char lets[invlet_basic + 1]; for (otmp = gi.invent; otmp; otmp = otmp->nobj) if (tool_being_used(otmp)) { @@ -4814,7 +5342,7 @@ doprtool(void) if (!ct) You("are not using any tools."); else - (void) display_inventory(lets, FALSE); + (void) dispinv_with_action(lets, TRUE, NULL); return ECMD_OK; } @@ -4825,21 +5353,18 @@ doprinuse(void) { struct obj *otmp; int ct = 0; - char lets[52 + 1]; + /* no longer need to collect letters; sortloot() takes care of it, but + still want to count far enough to know whether anything is in use */ for (otmp = gi.invent; otmp; otmp = otmp->nobj) - if (is_worn(otmp) || tool_being_used(otmp)) { - /* we could be carrying more than 52 items; theoretically they - might all be lit candles so avoid potential lets[] overflow */ - if (ct >= (int) sizeof lets - 1) - break; - lets[ct++] = obj_to_let(otmp); + if (is_inuse(otmp)) { + ++ct; + break; } - lets[ct] = '\0'; if (!ct) You("are not wearing or wielding anything."); else - (void) display_inventory(lets, FALSE); + (void) dispinv_with_action((char *) 0, TRUE, NULL); return ECMD_OK; } @@ -4858,7 +5383,7 @@ useupf(struct obj *obj, long numused) otmp = splitobj(obj, numused); else otmp = obj; - if (!gc.context.mon_moving && costly_spot(otmp->ox, otmp->oy)) { + if (!svc.context.mon_moving && costly_spot(otmp->ox, otmp->oy)) { if (strchr(u.urooms, *in_rooms(otmp->ox, otmp->oy, 0))) addtobill(otmp, FALSE, FALSE, FALSE); else @@ -4898,7 +5423,7 @@ let_to_name(char let, boolean unpaid, boolean showsym) else if ((pos = strchr(oth_symbols, let)) != 0) class_name = oth_names[pos - oth_symbols]; else - class_name = names[0]; + class_name = names[ILLOBJ_CLASS]; len = Strlen(class_name) + (unpaid ? sizeof "unpaid_" : sizeof "") + (oclass ? (Strlen(ocsymfmt) + invbuf_sympadding) : 0); @@ -5000,7 +5525,7 @@ check_invent_gold(const char *why) /* 'why' == caller in case of warning */ } /* normal getobj callback for item to #adjust; excludes gold */ -static int +staticfn int adjust_ok(struct obj *obj) { if (!obj || obj->oclass == COIN_CLASS) @@ -5010,7 +5535,7 @@ adjust_ok(struct obj *obj) } /* getobj callback for item to #adjust if gold is wonky; allows gold */ -static int +staticfn int adjust_gold_ok(struct obj *obj) { if (!obj) @@ -5151,7 +5676,7 @@ adjust_split(void) return doorganize_core(obj); } -static int +staticfn int doorganize_core(struct obj *obj) { struct obj *otmp, *splitting, *bumped; @@ -5159,8 +5684,8 @@ doorganize_core(struct obj *obj) char let; #define GOLD_INDX 0 #define GOLD_OFFSET 1 -#define OVRFLW_INDX (GOLD_OFFSET + 52) /* past gold and 2*26 letters */ - char lets[1 + 52 + 1 + 1]; /* room for '$a-zA-Z#\0' */ +#define OVRFLW_INDX (GOLD_OFFSET + invlet_basic) /* past gold+2*26 letters */ + char lets[1 + invlet_basic + 1 + 1]; /* room for '$a-zA-Z#\0' */ char qbuf[QBUFSZ]; char *objname, *otmpname; const char *adj_type; @@ -5193,7 +5718,7 @@ doorganize_core(struct obj *obj) lets[OVRFLW_INDX] = ' '; lets[sizeof lets - 1] = '\0'; /* for floating inv letters, truncate list after the first open slot */ - if (!flags.invlet_constant && (ix = inv_cnt(FALSE)) < 52) + if (!flags.invlet_constant && (ix = inv_cnt(FALSE)) < invlet_basic) lets[ix + (splitting ? 1 : 2)] = '\0'; /* blank out all the letters currently in use in the inventory @@ -5265,7 +5790,9 @@ doorganize_core(struct obj *obj) collect = (let == obj->invlet); /* change the inventory and print the resulting item */ - adj_type = collect ? "Collecting" : !splitting ? "Moving:" : "Splitting:"; + adj_type = collect ? "Collecting:" + : !splitting ? "Moving:" + : "Splitting:"; /* * don't use freeinv/addinv to avoid double-touching artifacts, @@ -5273,7 +5800,8 @@ doorganize_core(struct obj *obj) */ extract_nobj(obj, &gi.invent); - for (otmp = gi.invent; otmp;) { + for (otmp = gi.invent; otmp; ) { + otmpname = has_oname(otmp) ? ONAME(otmp) : (char *) 0; /* it's tempting to pull this outside the loop, but merged() could free ONAME(obj) [via obfree()] and replace it with ONAME(otmp) */ objname = has_oname(obj) ? ONAME(obj) : (char *) 0; @@ -5285,16 +5813,24 @@ doorganize_core(struct obj *obj) with compatible named ones; we only want that if it is the 'from' stack (obj) with a name and candidate (otmp) without one, not unnamed 'from' with named candidate. */ - otmpname = has_oname(otmp) ? ONAME(otmp) : (char *) 0; if ((!otmpname || (objname && !strcmp(objname, otmpname))) && merged(&otmp, &obj)) { - adj_type = "Merging:"; + /*adj_type = "Collecting:"; //already set to this*/ obj = otmp; otmp = otmp->nobj; extract_nobj(obj, &gi.invent); continue; /* otmp has already been updated */ } } else if (otmp->invlet == let) { + /* Merging: when from and to are compatible */ + if ((!otmpname || (objname && !strcmp(objname, otmpname))) + && merged(&otmp, &obj)) { + adj_type = "Merging:"; + obj = otmp; + otmp = otmp->nobj; + extract_nobj(obj, &gi.invent); + break; /* otmp has been updated and we're done merging */ + } /* Moving or splitting: don't merge extra compatible stacks. Found 'otmp' in destination slot; merge if compatible, otherwise bump whatever is there to an open slot. */ @@ -5319,7 +5855,7 @@ doorganize_core(struct obj *obj) adj_type = "Splitting and merging:"; obj = otmp; extract_nobj(obj, &gi.invent); - } else if (inv_cnt(FALSE) >= 52) { + } else if (inv_cnt(FALSE) >= invlet_basic) { (void) merged(&splitting, &obj); /* undo split */ /* "knapsack cannot accommodate any more items" */ Your("pack is too full."); @@ -5362,23 +5898,17 @@ doorganize_core(struct obj *obj) } /* common to display_minventory and display_cinventory */ -static void +staticfn void invdisp_nothing(const char *hdr, const char *txt) { winid win; - anything any; menu_item *selected; - int clr = 0; - any = cg.zeroany; win = create_nhwindow(NHW_MENU); start_menu(win, MENU_BEHAVE_STANDARD); - add_menu(win, &nul_glyphinfo, &any, 0, 0, iflags.menu_headings, clr, - hdr, MENU_ITEMFLAGS_NONE); - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, - "", MENU_ITEMFLAGS_NONE); - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, txt, - MENU_ITEMFLAGS_NONE); + add_menu_heading(win, hdr); + add_menu_str(win, ""); + add_menu_str(win, txt); end_menu(win, (char *) 0); if (select_menu(win, PICK_NONE, &selected) > 0) free((genericptr_t) selected); @@ -5387,7 +5917,7 @@ invdisp_nothing(const char *hdr, const char *txt) } /* query_objlist callback: return things that are worn or wielded */ -static boolean +staticfn boolean worn_wield_only(struct obj *obj) { #if 1 @@ -5469,7 +5999,7 @@ display_minventory( /* format a container name for cinventory_display(), inserting "trapped" if that's appropriate */ -static char * +staticfn char * cinv_doname(struct obj *obj) { char *result = doname(obj); @@ -5501,7 +6031,7 @@ cinv_doname(struct obj *obj) } /* used by safe_qbuf() if the full doname() result is too long */ -static char * +staticfn char * cinv_ansimpleoname(struct obj *obj) { char *result = ansimpleoname(obj); @@ -5554,15 +6084,16 @@ display_cinventory(struct obj *obj) return ret; } -static boolean + +staticfn boolean only_here(struct obj *obj) { return (obj->ox == go.only.x && obj->oy == go.only.y); } /* - * Display a list of buried items in inventory style. Return a non-zero - * value if there were items at that spot. + * Display a list of buried or underwater items in inventory style. + * Return a non-zero value if there were items at that spot. * * Currently, this is only used with a wand of probing zapped downwards. */ @@ -5570,11 +6101,43 @@ int display_binventory(coordxy x, coordxy y, boolean as_if_seen) { struct obj *obj; + char qbuf[QBUFSZ]; + const char *underwhat = "here"; menu_item *selected = 0; - int n; + int n, n2 = 0; + + /* if hero is levitating or flying over water or lava, list any items + below (the map won't be showing them); if hero is underwater, player + should use the normal look_here command instead of probing (caller + has already used bhitpile() which will have set dknown on all items) */ + if (is_pool_or_lava(x, y) && !Underwater + && (obj = svl.level.objects[x][y]) != 0) { + const char *real_liquid = is_pool(x, y) ? "water" : "lava", + *seen_liquid = hliquid(real_liquid); + + if (!obj->nexthere) { + boolean more_than_1 = is_plural(obj); + + There("%s %s under the %s here.", more_than_1 ? "are" : "is", + doname(obj), seen_liquid); + n2 = 1; + /* "pair of boots" is singular but "beneath it" sounds strange */ + if (pair_of(obj)) + more_than_1 = TRUE; + underwhat = more_than_1 ? "under them" : "beneath it"; + } else { + Sprintf(qbuf, "Things that are under the %s here:", seen_liquid); + if (query_objlist(qbuf, &svl.level.objects[x][y], BY_NEXTHERE, + &selected, PICK_NONE, allow_all) > 0) + free((genericptr_t) selected), selected = 0; + for (n2 = 0; obj; obj = obj->nexthere) + ++n2; + underwhat = "beneath them"; + } + } - /* count # of objects here */ - for (n = 0, obj = gl.level.buriedobjlist; obj; obj = obj->nobj) + /* count # of buried objects here */ + for (n = 0, obj = svl.level.buriedobjlist; obj; obj = obj->nobj) if (obj->ox == x && obj->oy == y) { if (as_if_seen) obj->dknown = 1; @@ -5584,36 +6147,29 @@ display_binventory(coordxy x, coordxy y, boolean as_if_seen) if (n) { go.only.x = x; go.only.y = y; - if (query_objlist("Things that are buried here:", - &gl.level.buriedobjlist, INVORDER_SORT, + /* "buried here", but vary if we've already shown underwater items */ + Sprintf(qbuf, "Things that are buried %s:", underwhat); + if (query_objlist(qbuf, &svl.level.buriedobjlist, INVORDER_SORT, &selected, PICK_NONE, only_here) > 0) free((genericptr_t) selected); go.only.x = go.only.y = 0; } - return n; + return n + n2; } void prepare_perminvent(winid window) { win_request_info *wri; + int invmode = (int) iflags.perminv_mode; - if (!done_setting_perminv_flags) { - /*TEMPORARY*/ - char *envtmp = !gp.program_state.gameover ? nh_getenv("TTYINV") : 0; - /* default for non-tty includes gold, for tty excludes gold; - if non-tty specifies any value, gold will be excluded unless - that value includes the show-gold bit (1) */ - int invmode = envtmp ? atoi(envtmp) - : !WINDOWPORT(tty) ? InvShowGold - : InvNormal; - + if (perminv_flags != invmode) { wri_info = zerowri; wri_info.fromcore.invmode = invmode; /* relay the mode settings to the window port */ wri = ctrl_nhwindow(window, set_mode, &wri_info); + perminv_flags = invmode; nhUse(wri); - done_setting_perminv_flags = 1; } } @@ -5630,8 +6186,7 @@ sync_perminvent(void) && gp.perm_invent_toggling_direction == toggling_on)) return; } - if (!done_setting_perminv_flags && WIN_INVEN != WIN_ERR) - prepare_perminvent(WIN_INVEN); + prepare_perminvent(WIN_INVEN); if ((!iflags.perm_invent && gc.core_invent_state)) { /* Odd - but this could be end-of-game disclosure @@ -5651,33 +6206,40 @@ sync_perminvent(void) * 1. iflags.perm_invent is on * AND * gc.core_invent_state is still zero. - * * OR - * * 2. iflags.perm_invent is off, but we're in the * midst of toggling it on. + * OR + * 3. iflags.perminv_mode has been changed via 'm O'. */ if ((iflags.perm_invent && !gc.core_invent_state) - || ((!iflags.perm_invent + || (!iflags.perm_invent && (in_perm_invent_toggled - && gp.perm_invent_toggling_direction == toggling_on)))) { - + && gp.perm_invent_toggling_direction == toggling_on))) { /* Send windowport a request to return the related settings to us */ if ((iflags.perm_invent && !gc.core_invent_state) || in_perm_invent_toggled) { - if ((wri = ctrl_nhwindow(WIN_INVEN, request_settings, &wri_info)) - != 0) { - if ((wri->tocore.tocore_flags & prohibited) != 0) { + 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 */ - set_option_mod_status("perm_invent", set_gameview); + if ((wri->tocore.tocore_flags & prohibited) != 0) { + set_option_mod_status("perm_invent", set_gameview); + set_option_mod_status("perminv_mode", set_gameview); + } iflags.perm_invent = FALSE; if (WIN_INVEN != WIN_ERR) destroy_nhwindow(WIN_INVEN), WIN_INVEN = WIN_ERR; - if (WINDOWPORT(tty) && iflags.perm_invent) - wport_id = "tty perm_invent"; - else - wport_id = "perm_invent"; + wport_id = WINDOWPORT(tty) ? "tty perm_invent" + : "perm_invent"; pline("%s could not be enabled.", wport_id); pline("%s needs a terminal that is at least %dx%d, yours " "is %dx%d.", @@ -5700,7 +6262,7 @@ sync_perminvent(void) WIN_INVEN = create_nhwindow(NHW_MENU); } - if (WIN_INVEN != WIN_ERR && gp.program_state.beyond_savefile_load) { + if (WIN_INVEN != WIN_ERR && program_state.beyond_savefile_load) { gi.in_sync_perminvent = 1; (void) display_inventory((char *) 0, FALSE); gi.in_sync_perminvent = 0; @@ -5718,6 +6280,8 @@ perm_invent_toggled(boolean negated) gc.core_invent_state = 0; } else { gp.perm_invent_toggling_direction = toggling_on; + if (iflags.perminv_mode == InvOptNone) + iflags.perminv_mode = InvOptOn; /* all inventory except gold */ sync_perminvent(); } gp.perm_invent_toggling_direction = toggling_not; diff --git a/src/isaac64.c b/src/isaac64.c index e95eb8131e..0fa0e7a8d8 100644 --- a/src/isaac64.c +++ b/src/isaac64.c @@ -30,6 +30,11 @@ #define inline /*empty*/ #endif +static inline uint32_t lower_bits(uint64_t); +static inline uint32_t upper_bits(uint64_t); +staticfn void isaac64_update(isaac64_ctx *); +staticfn void isaac64_mix(uint64_t[8]); + /* Extract ISAAC64_SZ_LOG bits (starting at bit 3). */ static inline uint32_t lower_bits(uint64_t x) { @@ -42,7 +47,7 @@ static inline uint32_t upper_bits(uint64_t y) return (y >> (ISAAC64_SZ_LOG+3)) & (ISAAC64_SZ-1); } -static void isaac64_update(isaac64_ctx *_ctx){ +staticfn void isaac64_update(isaac64_ctx *_ctx){ uint64_t *m; uint64_t *r; uint64_t a; @@ -95,7 +100,7 @@ static void isaac64_update(isaac64_ctx *_ctx){ _ctx->n=ISAAC64_SZ; } -static void isaac64_mix(uint64_t _x[8]){ +staticfn void isaac64_mix(uint64_t _x[8]){ static const unsigned char SHIFT[8]={9,9,23,15,14,20,17,14}; int i; for(i=0;i<8;i++){ diff --git a/src/light.c b/src/light.c index 907a6cbc70..751118d39a 100644 --- a/src/light.c +++ b/src/light.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 light.c $NHDT-Date: 1657918094 2022/07/15 20:48:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.57 $ */ +/* NetHack 3.7 light.c $NHDT-Date: 1726609514 2024/09/17 21:45:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.75 $ */ /* Copyright (c) Dean Luick, 1994 */ /* NetHack may be freely redistributed. See license for details. */ @@ -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,13 +38,17 @@ */ /* flags */ -#define LSF_SHOW 0x1 /* display the light source */ -#define LSF_NEEDS_FIXUP 0x2 /* need oid fixup */ - -static light_source *new_light_core(coordxy, coordxy, int, int, anything *); -static void discard_flashes(void); -static void write_ls(NHFILE *, light_source *); -static int maybe_write_ls(NHFILE *, int, boolean); +#define LSF_SHOW 0x1 /* display the light source */ +#define LSF_NEEDS_FIXUP 0x2 /* need oid fixup */ +#define LSF_IS_PROBLEMATIC 0x4 /* impossible situation encountered */ + +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[]; @@ -60,7 +64,7 @@ new_light_source(coordxy x, coordxy y, int range, int type, anything *id) } /* Create a new light source and return it. Only used within this file. */ -static light_source * +staticfn light_source * new_light_core(coordxy x, coordxy y, int range, int type, anything *id) { light_source *ls; @@ -88,14 +92,12 @@ new_light_core(coordxy x, coordxy y, int range, int type, anything *id) return ls; } -/* - * Delete a light source. This assumes only one light source is attached - * to an object at a time. - */ +/* Find and delete a light source. + Assumes at most one light source is attached to an object at a time. */ void del_light_source(int type, anything *id) { - light_source *curr, *prev; + light_source *curr; anything tmp_id; tmp_id = cg.zeroany; @@ -103,6 +105,10 @@ del_light_source(int type, anything *id) has only been partially restored during a level change (in particular: chameleon vs prot. from shape changers) */ switch (type) { + case LS_NONE: + impossible("del_light_source:type=none"); + tmp_id.a_uint = 0; + break; case LS_OBJECT: tmp_id.a_uint = id->a_obj ? id->a_obj->o_id : 0; break; @@ -114,23 +120,47 @@ del_light_source(int type, anything *id) break; } - for (prev = 0, curr = gl.light_base; curr; prev = curr, curr = curr->next) { + /* find the light source from its id */ + for (curr = gl.light_base; curr; curr = curr->next) { if (curr->type != type) continue; if (curr->id.a_obj - == ((curr->flags & LSF_NEEDS_FIXUP) ? tmp_id.a_obj : id->a_obj)) { + == ((curr->flags & LSF_NEEDS_FIXUP) ? tmp_id.a_obj : id->a_obj)) + break; + } + if (curr) { + delete_ls(curr); + } else { + impossible("del_light_source: not found type=%d, id=%s", type, + fmt_ptr((genericptr_t) id->a_obj)); + } +} + +/* remove a light source from the light_base list and free it */ +staticfn void +delete_ls(light_source *ls) +{ + light_source *curr, *prev; + + for (prev = 0, curr = gl.light_base; curr; + prev = curr, curr = curr->next) { + if (curr == ls) { if (prev) prev->next = curr->next; else gl.light_base = curr->next; - - free((genericptr_t) curr); - gv.vision_full_recalc = 1; - return; + break; } } - impossible("del_light_source: not found type=%d, id=%s", type, - fmt_ptr((genericptr_t) id->a_obj)); + if (curr) { + assert(curr == ls); + (void) memset((genericptr_t) ls, 0, sizeof(light_source)); + free((genericptr_t) ls); + gv.vision_full_recalc = 1; + } else { + impossible("delete_ls not found, ls=%s", fmt_ptr((genericptr_t) ls)); + } + return; } /* Mark locations that are temporarily lit via mobile light sources. */ @@ -239,6 +269,9 @@ show_transient_light(struct obj *obj, coordxy x, coordxy y) cameraflash = cg.zeroany; /* radius 0 will just light ; cameraflash.a_obj is Null */ ls = new_light_core(x, y, 0, LS_OBJECT, &cameraflash); + /* pacify static analysis; 'ls' is never Null for + new_light_core(,,0,LS_OBJECT,&zeroany) */ + assert(ls != NULL); } else { /* thrown or kicked object which is emitting light; validate its light source to obtain its radius (for monster sightings) */ @@ -248,12 +281,14 @@ show_transient_light(struct obj *obj, coordxy x, coordxy y) if (ls->id.a_obj == obj) break; } - } - if (!ls || (obj && obj->where != OBJ_FREE)) { - impossible("transient light %s %s is not %s?", - obj->lamplit ? "lit" : "unlit", xname(obj), - !ls ? "a light source" : "free"); - return; + assert(obj != NULL); /* necessary condition to get into this 'else' */ + if (!ls || obj->where != OBJ_FREE) { + impossible("transient light %s %s %s not %s?", + obj->lamplit ? "lit" : "unlit", + simpleonames(obj), otense(obj, "are"), + !ls ? "a light source" : "free"); + return; + } } if (obj) /* put lit candle or lamp temporarily on the map */ @@ -270,7 +305,7 @@ show_transient_light(struct obj *obj, coordxy x, coordxy y) if (DEADMONSTER(mon) || (mon->isgd && !mon->mx)) continue; /* light range is the radius of a circle and we're limiting - canseemon() to a square exclosing that circle, but setting + canseemon() to a square enclosing that circle, but setting mtemplit 'erroneously' for a seen monster is not a problem; it just flags monsters for another canseemon() check when 'obj' has reached its destination after missile traversal */ @@ -321,17 +356,15 @@ transient_light_cleanup(void) } /* camera flashes have Null object; caller wants to get rid of them now */ -static void +staticfn void discard_flashes(void) { light_source *ls, *nxt_ls; for (ls = gl.light_base; ls; ls = nxt_ls) { nxt_ls = ls->next; - if (ls->type != LS_OBJECT) - continue; - if (!ls->id.a_obj) - del_light_source(LS_OBJECT, &ls->id); + if (ls->type == LS_OBJECT && !ls->id.a_obj) + delete_ls(ls); } } @@ -343,7 +376,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) @@ -360,6 +393,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) @@ -407,6 +462,7 @@ save_light_sources(NHFILE *nhfp, int range) /* if global and not doing local, or vice versa, remove it */ if (is_global ^ (range == RANGE_LEVEL)) { *prev = curr->next; + (void) memset((genericptr_t) curr, 0, sizeof(light_source)); free((genericptr_t) curr); } else { prev = &(*prev)->next; @@ -464,27 +520,43 @@ relink_light_sources(boolean ghostly) unsigned nid; light_source *ls; + /* + * Caveat: + * There has been at least one instance during to-be-3.7 development + * where the light_base linked list ended up with a circular link. + * If that happens, then once all the traversed elements have their + * LSF_NEEDS_FIXUP flag cleared, the traversal attempt will run wild. + * + * The circular list instance was blamed on attempting to restore + * a save file which should have been invalidated by version/patch/ + * editlevel verification, but wasn't rejected because EDITLEVEL + * didn't get incremented when it should have been. Valid data should + * never produce the problem and it isn't possible in general to guard + * against code updates that neglect to set the verification info up + * to date. + */ + for (ls = gl.light_base; ls; ls = ls->next) { if (ls->flags & LSF_NEEDS_FIXUP) { if (ls->type == LS_OBJECT || ls->type == LS_MONSTER) { - if (ghostly) { - if (!lookup_id_mapping(ls->id.a_uint, &nid)) - impossible("relink_light_sources: no id mapping"); - } else - nid = ls->id.a_uint; + nid = ls->id.a_uint; + if (ghostly && !lookup_id_mapping(nid, &nid)) + panic("relink_light_sources: no id mapping"); + + which = '\0'; if (ls->type == LS_OBJECT) { - which = 'o'; - ls->id.a_obj = find_oid(nid); + if ((ls->id.a_obj = find_oid(nid)) == 0) + which = 'o'; } else { - which = 'm'; - ls->id.a_monst = find_mid(nid, FM_EVERYWHERE); + if ((ls->id.a_monst = find_mid(nid, FM_EVERYWHERE)) == 0) + which = 'm'; } - if (!ls->id.a_monst) - impossible("relink_light_sources: cant find %c_id %d", - which, nid); - } else - impossible("relink_light_sources: bad type (%d)", ls->type); - + if (which != '\0') + panic("relink_light_sources: can't find %c_id %u", + which, nid); + } else { + panic("relink_light_sources: bad type (%d)", ls->type); + } ls->flags &= ~LSF_NEEDS_FIXUP; } } @@ -495,7 +567,7 @@ relink_light_sources(boolean ghostly) * sources that would be written. If write_it is true, actually write * the light source out. */ -static int +staticfn int maybe_write_ls(NHFILE *nhfp, int range, boolean write_it) { int count = 0, is_global; @@ -542,7 +614,7 @@ light_sources_sanity_check(void) if (!ls->id.a_monst) panic("insane light source: no id!"); if (ls->type == LS_OBJECT) { - otmp = (struct obj *) ls->id.a_obj; + otmp = ls->id.a_obj; auint = otmp->o_id; if (find_oid(auint) != otmp) panic("insane light source: can't find obj #%u!", auint); @@ -558,7 +630,7 @@ light_sources_sanity_check(void) } /* Write a light source structure to disk. */ -static void +staticfn void write_ls(NHFILE *nhfp, light_source *ls) { anything arg_save; @@ -576,22 +648,55 @@ 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)); ls->id = arg_save; ls->flags &= ~LSF_NEEDS_FIXUP; + ls->flags &= ~LSF_IS_PROBLEMATIC; } } else { impossible("write_ls: bad type (%d)", ls->type); diff --git a/src/lock.c b/src/lock.c index 89acccf8c6..a47b278ce8 100644 --- a/src/lock.c +++ b/src/lock.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 lock.c $NHDT-Date: 1654464994 2022/06/05 21:36:34 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.114 $ */ +/* NetHack 3.7 lock.c $NHDT-Date: 1718745135 2024/06/18 21:12:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.137 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -6,13 +6,13 @@ #include "hack.h" /* occupation callbacks */ -static int picklock(void); -static int forcelock(void); +staticfn int picklock(void); +staticfn int forcelock(void); -static const char *lock_action(void); -static boolean obstructed(coordxy, coordxy, boolean); -static void chest_shatter_msg(struct obj *); -static boolean demogorgon_special_key(struct obj *key); +staticfn const char *lock_action(void); +staticfn boolean obstructed(coordxy, coordxy, boolean); +staticfn void chest_shatter_msg(struct obj *); +staticfn boolean demogorgon_special_key(struct obj *key) NONNULLARG1; boolean picking_lock(coordxy *x, coordxy *y) @@ -30,11 +30,12 @@ picking_lock(coordxy *x, coordxy *y) boolean picking_at(coordxy x, coordxy y) { - return (boolean) (go.occupation == picklock && gx.xlock.door == &levl[x][y]); + return (boolean) (go.occupation == picklock + && gx.xlock.door == &levl[x][y]); } /* produce an occupation string appropriate for the current activity */ -static const char * +staticfn const char * lock_action(void) { /* "unlocking"+2 == "locking" */ @@ -64,7 +65,7 @@ lock_action(void) } /* try to open/close a lock */ -static int +staticfn int picklock(void) { coordxy doorx = u.ux + u.dx, doory = u.uy + u.dy; @@ -163,10 +164,12 @@ picklock(void) : door_is_trapped(gx.xlock.door)) && 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; @@ -177,6 +180,7 @@ picklock(void) alreadyunlocked = !door_is_locked(gx.xlock.door); } else { gx.xlock.box->otrapped = 0; + gx.xlock.box->tknown = 0; what = (gx.xlock.box->otyp == CHEST) ? "chest" : "box"; alreadyunlocked = !gx.xlock.box->olocked; } @@ -251,7 +255,8 @@ breakchestlock(struct obj *box, boolean destroyit) if (!rn2(3) || otmp->oclass == POTION_CLASS) { chest_shatter_msg(otmp); if (costly) - loss += stolen_value(otmp, u.ux, u.uy, peaceful_shk, TRUE); + loss += stolen_value(otmp, u.ux, u.uy, peaceful_shk, + TRUE); if (otmp->quan == 1L) { obfree(otmp, (struct obj *) 0); continue; @@ -261,7 +266,7 @@ breakchestlock(struct obj *box, boolean destroyit) useup(otmp); } if (box->otyp == ICE_BOX && otmp->otyp == CORPSE) { - otmp->age = gm.moves - otmp->age; /* actual age */ + otmp->age = svm.moves - otmp->age; /* actual age */ start_corpse_timeout(otmp); } place_object(otmp, u.ux, u.uy); @@ -276,7 +281,7 @@ breakchestlock(struct obj *box, boolean destroyit) } /* try to force a locked chest */ -static int +staticfn int forcelock(void) { if ((gx.xlock.box->ox != u.ux) || (gx.xlock.box->oy != u.uy)) @@ -304,14 +309,14 @@ forcelock(void) return ((gx.xlock.usedtime = 0)); } } else /* blunt */ - wake_nearby(); /* due to hammering on the container */ + wake_nearby(FALSE); /* due to hammering on the container */ if (rn2(100) >= gx.xlock.chance) return 1; /* still busy */ You("succeed in forcing the lock."); exercise(gx.xlock.picktyp ? A_DEX : A_STR, TRUE); - /* breakchestlock() might destroy gx.xlock.box; if so, gx.xlock context will + /* breakchestlock() might destroy xlock.box; if so, xlock context will be cleared (delobj -> obfree -> maybe_reset_pick); but it might not, so explicitly clear that manually */ breakchestlock(gx.xlock.box, (boolean) (!gx.xlock.picktyp && !rn2(3))); @@ -511,7 +516,8 @@ pick_lock( count = 0; c = 'n'; /* in case there are no boxes here */ - for (otmp = gl.level.objects[cc.x][cc.y]; otmp; otmp = otmp->nexthere) { + for (otmp = svl.level.objects[cc.x][cc.y]; otmp; + otmp = otmp->nexthere) { /* autounlock on boxes: only the one that was just discovered to be locked; don't include any other boxes which might be here */ if (autounlock && otmp != container) @@ -575,7 +581,8 @@ pick_lock( You_cant("do that with %s.", an(simple_typename(picktyp))); return PICKLOCK_LEARNED_SOMETHING; - } else if (autounlock && !touch_artifact(pick, &gy.youmonst)) { + } else if (autounlock + && !touch_artifact(pick, &gy.youmonst)) { /* note: for !autounlock, apply already did touch check */ return PICKLOCK_DID_SOMETHING; } @@ -643,12 +650,12 @@ pick_lock( } if (!IS_DOOR(door->typ)) { int res = PICKLOCK_DID_NOTHING, oldglyph = door->glyph; - schar oldlastseentyp = gl.lastseentyp[cc.x][cc.y]; + schar oldlastseentyp = update_mapseen_for(cc.x, cc.y); /* this is probably only relevant when blind */ feel_location(cc.x, cc.y); if (door->glyph != oldglyph - || gl.lastseentyp[cc.x][cc.y] != oldlastseentyp) + || svl.lastseentyp[cc.x][cc.y] != oldlastseentyp) res = PICKLOCK_LEARNED_SOMETHING; if (is_drawbridge_wall(cc.x, cc.y) >= 0) @@ -729,7 +736,7 @@ pick_lock( } } } - gc.context.move = 0; + svc.context.move = 0; gx.xlock.chance = ch; gx.xlock.picktyp = picktyp; gx.xlock.pick = pick; @@ -759,8 +766,8 @@ RESTORE_WARNING_FORMAT_NONLITERAL int doforce(void) { - register struct obj *otmp; - register int c, picktyp; + struct obj *otmp; + int c, picktyp; char qbuf[QBUFSZ]; /* @@ -773,11 +780,14 @@ doforce(void) return ECMD_OK; } if (!u_have_forceable_weapon()) { - You_cant("force anything %s weapon.", + boolean use_plural = uwep && uwep->quan > 1; + + You_cant("force anything %s weapon%s.", !uwep ? "when not wielding a" - : (uwep->oclass != WEAPON_CLASS && !is_weptool(uwep)) - ? "without a proper" - : "with that"); + : (uwep->oclass != WEAPON_CLASS && !is_weptool(uwep)) + ? (use_plural ? "without proper" : "without a proper") + : (use_plural ? "with those" : "with that"), + use_plural ? "s" : ""); return ECMD_OK; } if (!can_reach_floor(TRUE)) { @@ -794,7 +804,7 @@ doforce(void) /* A lock is made only for the honest man, the thief will break it. */ gx.xlock.box = (struct obj *) 0; - for (otmp = gl.level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere) + for (otmp = svl.level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere) if (Is_box(otmp)) { if (otmp->obroken || !otmp->olocked) { /* force doname() to omit known "broken" or "unlocked" @@ -861,7 +871,7 @@ int doopen_indir(coordxy x, coordxy y) { coord cc; - register struct rm *door; + struct rm *door; boolean portcullis; const char *dirprompt; int res = ECMD_OK; @@ -911,11 +921,11 @@ doopen_indir(coordxy x, coordxy y) /* this used to be 'if (Blind)' but using a key skips that so we do too */ { int oldglyph = door->glyph; - schar oldlastseentyp = gl.lastseentyp[cc.x][cc.y]; + schar oldlastseentyp = update_mapseen_for(cc.x, cc.y); newsym(cc.x, cc.y); if (door->glyph != oldglyph - || gl.lastseentyp[cc.x][cc.y] != oldlastseentyp) + || svl.lastseentyp[cc.x][cc.y] != oldlastseentyp) res = ECMD_TIME; /* learned something */ } @@ -949,6 +959,7 @@ doopen_indir(coordxy x, coordxy y) default: impossible("doopen_indir: bad door state %d", door->doormask); } + set_msg_xy(cc.x, cc.y); pline("This door%s.", mesg); return res; } @@ -972,12 +983,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; + cmdq_add_dir(CQ_CANNED, + sgn(cc.x - u.ux), sgn(cc.y - u.uy), 0); + /* this was 'ECMD_TIME', but time shouldn't elapse until + the canned kick takes place */ + res = ECMD_OK; } } return res; @@ -985,6 +999,7 @@ doopen_indir(coordxy x, coordxy y) /* door is known to be CLOSED */ pline_The("door opens."); + set_msg_xy(cc.x, cc.y); if (postdoortrapped(cc.x, cc.y, &gy.youmonst, FINGER, D_ISOPEN) == 0) { set_doorstate(door, D_ISOPEN); feel_newsym(cc.x, cc.y); /* the hero knows she opened it */ @@ -994,7 +1009,7 @@ doopen_indir(coordxy x, coordxy y) return ECMD_TIME; } -static boolean +staticfn boolean obstructed(coordxy x, coordxy y, boolean quietly) { struct monst *mtmp = m_at(x, y); @@ -1028,8 +1043,8 @@ obstructed(coordxy x, coordxy y, boolean quietly) int doclose(void) { - register coordxy x, y; - register struct rm *door; + coordxy x, y; + struct rm *door; boolean portcullis; int res = ECMD_OK; @@ -1068,10 +1083,11 @@ doclose(void) portcullis = (is_drawbridge_wall(x, y) >= 0); if (Blind) { int oldglyph = door->glyph; - schar oldlastseentyp = gl.lastseentyp[x][y]; + schar oldlastseentyp = update_mapseen_for(x, y); feel_location(x, y); - if (door->glyph != oldglyph || gl.lastseentyp[x][y] != oldlastseentyp) + if (door->glyph != oldglyph + || svl.lastseentyp[x][y] != oldlastseentyp) res = ECMD_TIME; /* learned something */ } @@ -1145,8 +1161,8 @@ boxlock(struct obj *obj, struct obj *otmp) /* obj *is* a box */ break; case WAN_OPENING: case SPE_KNOCK: - if (obj->olocked) { /* unlock; couldn't be broken */ - pline("Klick!"); + if (obj->olocked) { /* unlock; isn't broken so doesn't need fixing */ + Soundeffect(se_klick, 50); pline("Klick!"); obj->olocked = 0; res = 1; @@ -1173,7 +1189,7 @@ boxlock(struct obj *obj, struct obj *otmp) /* obj *is* a box */ boolean doorlock(struct obj *otmp, struct monst *mon, coordxy x, coordxy y) { - register struct rm *door = &levl[x][y]; + struct rm *door = &levl[x][y]; boolean res = TRUE; const char *msg = (const char *) 0; const char *dustcloud = "A cloud of dust"; @@ -1271,7 +1287,7 @@ doorlock(struct obj *otmp, struct monst *mon, coordxy x, coordxy y) unblock_point(x, y); seeit = cansee(x, y); newsym(x, y); - if (Verbose(1, doorlock2)) { + if (flags.verbose) { if ((sawit || seeit) && !Unaware) { pline_The("door crashes open!"); } else if (!Deaf) { @@ -1303,7 +1319,7 @@ doorlock(struct obj *otmp, struct monst *mon, coordxy x, coordxy y) return res; } -static void +staticfn void chest_shatter_msg(struct obj *otmp) { const char *disposition; @@ -1359,11 +1375,11 @@ demogorgon_special_door(struct rm *door) { && door_is_iron(door)); } -/* Same for keys - for now, these are just any glass key. It could later be - * changed to "ornate glass key" or something, with its spe field set to +/* Same for keys - for now, these are just any bone key. It could later be + * changed to "ornate bone key" or something, with its spe field set to * visually distinguish from an ordinary key, and only generate with spe set on * Demogorgon's level. */ -static boolean +staticfn boolean demogorgon_special_key(struct obj *key) { if (key->otyp != SKELETON_KEY) { impossible("non key typ %d passed to demogorgon_special_key", diff --git a/src/mail.c b/src/mail.c index 92c8fe1cc1..e6a5c2935f 100644 --- a/src/mail.c +++ b/src/mail.c @@ -40,12 +40,12 @@ * random intervals. */ -static boolean md_start(coord *); -static boolean md_stop(coord *, coord *); -static boolean md_rush(struct monst *, int, int); -static void newmail(struct mail_info *); +staticfn boolean md_start(coord *); +staticfn boolean md_stop(coord *, coord *); +staticfn boolean md_rush(struct monst *, int, int); +staticfn void newmail(struct mail_info *); #if defined(SIMPLE_MAIL) || defined(SERVER_ADMIN_MSG) -static void read_simplemail(const char *mbox, boolean adminmsg); +staticfn void read_simplemail(const char *mbox, boolean adminmsg); #endif #if !defined(UNIX) && !defined(VMS) @@ -145,7 +145,7 @@ getmailstatus(void) * Pick coordinates for a starting position for the mail daemon. Called * from newmail() and newphone(). */ -static boolean +staticfn boolean md_start(coord *startp) { coord testcc; /* scratch coordinates */ @@ -170,7 +170,8 @@ md_start(coord *startp) * hero. */ while (stway) { - if (stway->tolev.dnum == u.uz.dnum && couldsee(stway->sx, stway->sy)) { + if (stway->tolev.dnum == u.uz.dnum + && couldsee(stway->sx, stway->sy)) { startp->x = stway->sx; startp->y = stway->sy; return TRUE; @@ -184,8 +185,8 @@ md_start(coord *startp) * position that could be seen. What we really ought to be doing is * finding a path from a stairwell... * - * The arrays gv.viz_rmin[] and gv.viz_rmax[] are set even when blind. These - * are the LOS limits for each row. + * The arrays gv.viz_rmin[] and gv.viz_rmax[] are set even when blind. + * These are the LOS limits for each row. */ lax = 0; /* be picky */ max_distance = -1; @@ -200,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)) { @@ -215,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)) { @@ -243,7 +244,7 @@ md_start(coord *startp) * enexto(). Use enexto() as a last resort because enexto() chooses * its point randomly, which is not what we want. */ -static boolean +staticfn boolean md_stop(coord *stopp, /* stopping position (we fill it in) */ coord *startp) /* starting position (read only) */ { @@ -273,7 +274,7 @@ md_stop(coord *stopp, /* stopping position (we fill it in) */ } /* Let the mail daemon have a larger vocabulary. */ -static NEARDATA const char *mail_text[] = { "Gangway!", "Look out!", +staticfn NEARDATA const char *mail_text[] = { "Gangway!", "Look out!", "Pardon me!" }; #define md_exclamations() (mail_text[rn2(3)]) @@ -283,12 +284,12 @@ static NEARDATA const char *mail_text[] = { "Gangway!", "Look out!", * FALSE if the md gets stuck in a position where there is a monster. Return * TRUE otherwise. */ -static boolean +staticfn boolean md_rush(struct monst *md, - register int tx, register int ty) /* destination of mail daemon */ + int tx, int ty) /* destination of mail daemon */ { struct monst *mon; /* displaced monster */ - register int dx, dy; /* direction counters */ + int dx, dy; /* direction counters */ int fx = md->mx, fy = md->my; /* current location */ int nfx = fx, nfy = fy, /* new location */ d1, d2; /* shortest distances */ @@ -387,7 +388,7 @@ md_rush(struct monst *md, /* Deliver a scroll of mail. */ /*ARGSUSED*/ -static void +staticfn void newmail(struct mail_info *info) { struct monst *md; @@ -406,7 +407,7 @@ newmail(struct mail_info *info) message_seen = TRUE; SetVoice(md, 0, 80, 0); - verbalize("%s, %s! %s.", Hello(md), gp.plname, info->display_txt); + verbalize("%s, %s! %s.", Hello(md), svp.plname, info->display_txt); if (info->message_typ) { struct obj *obj = mksobj(SCR_MAIL, FALSE, FALSE); @@ -416,7 +417,7 @@ newmail(struct mail_info *info) if (info->response_cmd) new_omailcmd(obj, info->response_cmd); - if (!next2u(md->mx, md->my)) { + if (!m_next2u(md)) { SetVoice(md, 0, 80, 0); verbalize("Catch!"); } @@ -447,7 +448,8 @@ ckmailstatus(void) return; if (mustgetmail < 0) { #if defined(AMIGA) || defined(MSDOS) || defined(TOS) - mustgetmail = (gm.moves < 2000) ? (100 + rn2(2000)) : (2000 + rn2(3000)); + mustgetmail = (svm.moves < 2000) ? (100 + rn2(2000)) + : (2000 + rn2(3000)); #endif return; } @@ -488,11 +490,13 @@ readmail(struct obj *otmp UNUSED) (suboptimal but works correctly); dollar sign and fractional zorkmids are inappropriate within nethack but are suitable for typical dysfunctional spam mail */ - "Buy a potion of gain level for only $19.99! Guaranteed to be blessed!", - /* DEVTEAM_URL will be substituted for 2nd "%s"; terminating punctuation - (formerly "!") has deliberately been omitted so that it can't be - mistaken for part of the URL (unfortunately that is still followed - by a closing quote--in the pline below, not the data here) */ + ("Buy a potion of gain level for only $19.99! " + " Guaranteed to be blessed!"), + /* DEVTEAM_URL will be substituted for 2nd "%s"; + terminating punctuation (formerly "!") has deliberately been + omitted so that it can't be mistaken for part of the URL + (unfortunately that is still followed by a closing quote--in + the pline below, not the data here) */ "%sInvitation: Visit the NetHack web site at %s%s" }; const char *const it_reads = "It reads: \""; @@ -533,12 +537,12 @@ ckmailstatus(void) if (!mailbox || u.uswallow || !flags.biff #ifdef MAILCKFREQ - || gm.moves < laststattime + MAILCKFREQ + || svm.moves < laststattime + MAILCKFREQ #endif ) return; - laststattime = gm.moves; + laststattime = svm.moves; if (stat(mailbox, &nmstat)) { #ifdef PERMANENT_MAILBOX pline("Cannot get status of MAIL=\"%s\" anymore.", mailbox); @@ -568,7 +572,7 @@ ckmailstatus(void) void read_simplemail(const char *mbox, boolean adminmsg) { - FILE* mb = fopen(mbox, "r"); + FILE *mb = fopen(mbox, "r"); char curline[128], *msg; boolean seen_one_already = FALSE; #ifdef SIMPLE_MAIL @@ -668,8 +672,8 @@ ck_server_admin_msg(void) static struct stat ost,nst; static long lastchk = 0; - if (gm.moves < lastchk + SERVER_ADMIN_MSG_CKFREQ) return; - lastchk = gm.moves; + if (svm.moves < lastchk + SERVER_ADMIN_MSG_CKFREQ) return; + lastchk = svm.moves; if (!stat(SERVER_ADMIN_MSG, &nst)) { if (nst.st_mtime > ost.st_mtime) @@ -684,7 +688,7 @@ void readmail(struct obj *otmp UNUSED) { #ifdef DEF_MAILREADER /* This implies that UNIX is defined */ - register const char *mr = 0; + const char *mr = 0; #endif /* DEF_MAILREADER */ #ifdef SIMPLE_MAIL read_simplemail(mailbox, FALSE); diff --git a/src/makemon.c b/src/makemon.c index 36e7d5fdfe..490b86bc8f 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -1,12 +1,10 @@ -/* NetHack 3.7 makemon.c $NHDT-Date: 1651886995 2022/05/07 01:29:55 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.204 $ */ +/* NetHack 3.7 makemon.c $NHDT-Date: 1720128166 2024/07/04 21:22:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.249 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ #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... */ @@ -14,16 +12,18 @@ (mptr->mlet == S_HUMAN && Role_if(role_pm) \ && (mptr->msound == MS_LEADER || mptr->msound == MS_NEMESIS)) -static boolean uncommon(int); -static int align_shift(struct permonst *); -static boolean mk_gen_ok(int, unsigned, unsigned); -static boolean wrong_elem_type(struct permonst *); -static void m_initgrp(struct monst *, coordxy, coordxy, int, mmflags_nht); -static void m_initthrow(struct monst *, int, int); -static void m_initweap(struct monst *); -static void m_initinv(struct monst *); -static xint8 hd_size(struct permonst *); -static boolean makemon_rnd_goodpos(struct monst *, mmflags_nht, coord *); +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 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); +staticfn void m_initweap(struct monst *); +staticfn void m_initinv(struct monst *); +staticfn xint8 hd_size(struct permonst *); +staticfn boolean makemon_rnd_goodpos(struct monst *, mmflags_nht, coord *); +staticfn void init_mextra(struct mextra *); #define m_initsgrp(mtmp, x, y, mmf) m_initgrp(mtmp, x, y, 3, mmf) #define m_initlgrp(mtmp, x, y, mmf) m_initgrp(mtmp, x, y, 10, mmf) @@ -31,7 +31,7 @@ static boolean makemon_rnd_goodpos(struct monst *, mmflags_nht, coord *); boolean is_home_elemental(struct permonst *ptr) { - if (ptr->mlet == S_ELEMENTAL) + if (ptr->mlet == S_ELEMENTAL) { switch (monsndx(ptr)) { case PM_AIR_ELEMENTAL: return Is_airlevel(&u.uz); @@ -41,14 +41,17 @@ is_home_elemental(struct permonst *ptr) return Is_earthlevel(&u.uz); case PM_WATER_ELEMENTAL: return Is_waterlevel(&u.uz); + default: + break; } + } return FALSE; } /* * Return true if the given monster cannot exist on this elemental level. */ -static boolean +staticfn boolean wrong_elem_type(struct permonst *ptr) { if (ptr->mlet == S_ELEMENTAL) { @@ -71,11 +74,14 @@ wrong_elem_type(struct permonst *ptr) } /* make a group just like mtmp */ -static void -m_initgrp(struct monst *mtmp, coordxy x, coordxy y, int n, mmflags_nht mmflags) +staticfn void +m_initgrp( + struct monst *mtmp, + coordxy x, coordxy y, int n, + mmflags_nht mmflags) { coord mm; - register int cnt = rnd(n); + int cnt = rnd(n); int diff = level_difficulty(); struct monst *mon; /* Tuning: cut down on swarming at low dungeon levels */ @@ -98,7 +104,7 @@ m_initgrp(struct monst *mtmp, coordxy x, coordxy y, int n, mmflags_nht mmflags) * 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; @@ -113,11 +119,10 @@ m_initgrp(struct monst *mtmp, coordxy x, coordxy y, int n, mmflags_nht mmflags) } } -static -void +staticfn void m_initthrow(struct monst *mtmp, int otyp, int oquan) { - register struct obj *otmp = mongets(mtmp, otyp); + struct obj *otmp = mongets(mtmp, otyp); if (!otmp) return; /* merged with something in inventory */ @@ -128,11 +133,11 @@ m_initthrow(struct monst *mtmp, int otyp, int oquan) otmp->opoisoned = TRUE; } -static void -m_initweap(register struct monst *mtmp) +staticfn void +m_initweap(struct monst *mtmp) { - register struct permonst *ptr = mtmp->data; - register int mm = monsndx(ptr); + struct permonst *ptr = mtmp->data; + int mm = monsndx(ptr); struct obj *otmp; int bias, w1, w2; @@ -161,7 +166,7 @@ m_initweap(register struct monst *mtmp) if (!rn2(3)) { /* lance and dwarvish mattock used to be in midst of the polearms but use different skills from polearms - and aren't appropriates choices for human soliders */ + and aren't appropriates choices for human soldiers */ do { w1 = rn1(BEC_DE_CORBIN - PARTISAN + 1, PARTISAN); } while (objects[w1].oc_skill != P_POLEARMS); @@ -314,23 +319,25 @@ m_initweap(register struct monst *mtmp) case S_ANGEL: if (humanoid(ptr)) { - /* create minion stuff; can't use mongets */ - otmp = mongets(mtmp, LONG_SWORD); + /* create minion stuff; bypass mongets */ + int typ = rn2(3) ? LONG_SWORD : 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) - 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; - /* vanilla has done away with this little "keep the initial - * enchantment if higher" bit, but we do init items given in - * mongets(), so it could happen that the sword naturally - * generates with more than +3. */ - otmp->spe = max(otmp->spe, rn2(4)); + /* 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 == MACE) + otmp->spe += 3; + (void) mpickobj(mtmp, otmp); otmp = mongets(mtmp, !rn2(4) || is_lord(ptr) ? SHIELD_OF_REFLECTION : LARGE_SHIELD); @@ -521,6 +528,7 @@ m_initweap(register struct monst *mtmp) */ if (!is_demon(ptr)) break; + FALLTHROUGH; /*FALLTHRU*/ default: /* @@ -584,12 +592,12 @@ mkmonmoney(struct monst *mtmp, long amount) } } -static void -m_initinv(register struct monst *mtmp) +staticfn void +m_initinv(struct monst *mtmp) { - register int cnt; - register struct obj *otmp; - register struct permonst *ptr = mtmp->data; + int cnt; + struct obj *otmp; + struct permonst *ptr = mtmp->data; /* * Soldiers get armour & rations - armour approximates their ac. @@ -598,7 +606,7 @@ m_initinv(register struct monst *mtmp) switch (ptr->mlet) { case S_HUMAN: if (is_mercenary(ptr)) { - register int mac; + int mac; switch (monsndx(ptr)) { case PM_GUARD: @@ -701,12 +709,15 @@ m_initinv(register 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); @@ -843,14 +854,16 @@ m_initinv(register struct monst *mtmp) /* Note: for long worms, always call cutworm (cutworm calls clone_mon) */ struct monst * -clone_mon(struct monst *mon, - coordxy x, coordxy y) /* clone's preferred location or 0 (near mon) */ +clone_mon( + struct monst *mon, + coordxy x, coordxy y) /* clone's preferred location or 0 (near mon) */ { coord mm; struct monst *m2; /* may be too weak or have been extinguished for population control */ - if (mon->mhp <= 1 || (gm.mvitals[monsndx(mon->data)].mvflags & G_EXTINCT)) + if (mon->mhp <= 1 + || (svm.mvitals[monsndx(mon->data)].mvflags & G_EXTINCT) != 0) return (struct monst *) 0; if (x == 0) { @@ -916,7 +929,7 @@ clone_mon(struct monst *mon, } /* not all clones caused by player are tame or peaceful */ - if (!gc.context.mon_moving && mon->mpeaceful) { + if (!svc.context.mon_moving && mon->mpeaceful) { if (mon->mtame) m2->mtame = rn2(max(2 + Luck, 2)) ? mon->mtame : 0; else if (mon->mpeaceful) @@ -930,6 +943,7 @@ clone_mon(struct monst *mon, int atyp; newemin(m2); + assert(has_emin(m2) && has_emin(mon)); *EMIN(m2) = *EMIN(mon); /* renegade when same alignment as hero but not peaceful or when peaceful while being different alignment from hero */ @@ -942,8 +956,9 @@ clone_mon(struct monst *mon, * must be made non-tame to get initialized properly. */ m2->mtame = 0; - if (tamedog(m2, (struct obj *) 0, FALSE)) { - *(EDOG(m2)) = *(EDOG(mon)); + if (tamedog(m2, (struct obj *) 0, FALSE, FALSE)) { + assert(has_edog(m2) && has_edog(mon)); + *EDOG(m2) = *EDOG(mon); } /* [TODO? some (most? all?) edog fields probably should be reinitialized rather that retain the 'parent's values] */ @@ -983,24 +998,24 @@ propagate(int mndx, boolean tally, boolean ghostly) boolean gone, result; int lim = mbirth_limit(mndx); - gone = (gm.mvitals[mndx].mvflags & G_GONE) != 0; /* geno'd|extinct */ - result = ((int) gm.mvitals[mndx].born < lim && !gone) ? TRUE : FALSE; + gone = (svm.mvitals[mndx].mvflags & G_GONE) != 0; /* geno'd|extinct */ + result = ((int) svm.mvitals[mndx].born < lim && !gone) ? TRUE : FALSE; /* if it's unique, don't ever make it again */ if ((mons[mndx].geno & G_UNIQ) != 0 && mndx != PM_HIGH_CLERIC && mndx != PM_JUIBLEX) - gm.mvitals[mndx].mvflags |= G_EXTINCT; + svm.mvitals[mndx].mvflags |= G_EXTINCT; - if (gm.mvitals[mndx].born < 255 && tally && (!ghostly || result)) - gm.mvitals[mndx].born++; - if ((int) gm.mvitals[mndx].born >= lim + if (svm.mvitals[mndx].born < 255 && tally && (!ghostly || result)) + svm.mvitals[mndx].born++; + if ((int) svm.mvitals[mndx].born >= lim && !(mons[mndx].geno & G_NOGEN) - && !(gm.mvitals[mndx].mvflags & G_EXTINCT)) { + && !(svm.mvitals[mndx].mvflags & G_EXTINCT)) { if (wizard) { debugpline1("Automatically extinguished %s.", makeplural(mons[mndx].pmnames[NEUTRAL])); } - gm.mvitals[mndx].mvflags |= G_EXTINCT; + svm.mvitals[mndx].mvflags |= G_EXTINCT; } return result; } @@ -1009,7 +1024,7 @@ propagate(int mndx, boolean tally, boolean ghostly) * calculated by rolling a die of this size for each level the monster has.) * It used to be 8 for all monsters, but it makes more sense for, say, a mumak * to be beefier than a killer bee of the same level. */ -static xint8 +staticfn xint8 hd_size(struct permonst * ptr) { switch(ptr->msize) { @@ -1111,7 +1126,7 @@ newmonhp(struct monst *mon, int mndx) static const struct mextra zeromextra = DUMMY; -static void +staticfn void init_mextra(struct mextra *mex) { *mex = zeromextra; @@ -1128,7 +1143,7 @@ newmextra(void) return mextra; } -static boolean +staticfn boolean makemon_rnd_goodpos( struct monst *mon, mmflags_nht gpflags, @@ -1138,6 +1153,7 @@ makemon_rnd_goodpos( coordxy nx, ny; boolean good; + gpflags |= GP_AVOID_MONPOS; do { nx = rn1(COLNO - 3, 2); ny = rn2(ROWNO); @@ -1204,7 +1220,7 @@ makemon( coordxy x, coordxy y, mmflags_nht mmflags) { - register struct monst *mtmp; + struct monst *mtmp; struct monst fakemon; coord cc; int mndx, mcham, ct, mitem; @@ -1215,12 +1231,12 @@ makemon( countbirth = ((mmflags & MM_NOCOUNTBIRTH) == 0), allowtail = ((mmflags & MM_NOTAIL) == 0); mmflags_nht gpflags = (((mmflags & MM_IGNOREWATER) ? MM_IGNOREWATER : 0) - | GP_CHECKSCARY); + | GP_CHECKSCARY | GP_AVOID_MONPOS); fakemon = cg.zeromonst; cc.x = cc.y = 0; - if (iflags.debug_mongen || (!gl.level.flags.rndmongen && !ptr)) + if (iflags.debug_mongen || (!svl.level.flags.rndmongen && !ptr)) return (struct monst *) 0; /* if caller wants random location, do it here */ @@ -1258,9 +1274,9 @@ makemon( mndx = monsndx(ptr); /* if you are to make a specific monster and it has already been genocided, return */ - if (gm.mvitals[mndx].mvflags & G_GENOD) + if (svm.mvitals[mndx].mvflags & G_GENOD) return (struct monst *) 0; - if (wizard && (gm.mvitals[mndx].mvflags & G_EXTINCT)) { + if (wizard && (svm.mvitals[mndx].mvflags & G_EXTINCT)) { debugpline1("Explicitly creating extinct monster %s.", mons[mndx].pmnames[NEUTRAL]); } @@ -1306,9 +1322,9 @@ makemon( mtmp->m_id = next_ident(); set_mon_data(mtmp, ptr); /* mtmp->data = ptr; */ if (ptr->msound == MS_LEADER && quest_info(MS_LEADER) == mndx) - gq.quest_status.leader_m_id = mtmp->m_id; + svq.quest_status.leader_m_id = mtmp->m_id; if (ptr->msound == MS_NEMESIS && quest_info(MS_NEMESIS) == mndx) - gq.quest_status.nemesis_m_id = mtmp->m_id; + svq.quest_status.nemesis_m_id = mtmp->m_id; mtmp->mnum = mndx; /* set up level and hit points */ @@ -1325,9 +1341,9 @@ makemon( but for ones which can be random, it has already been chosen (in role_init(), for possible use by the quest pager code) */ else if (ptr->msound == MS_LEADER && quest_info(MS_LEADER) == mndx) - mtmp->female = gq.quest_status.ldrgend; + mtmp->female = svq.quest_status.ldrgend; else if (ptr->msound == MS_NEMESIS && quest_info(MS_NEMESIS) == mndx) - mtmp->female = gq.quest_status.nemgend; + mtmp->female = svq.quest_status.nemgend; /* female used to be set randomly here even for neuters on the grounds that it was ignored, but after corpses were changed to @@ -1339,7 +1355,7 @@ makemon( mon_learns_traps(mtmp, PIT); mon_learns_traps(mtmp, HOLE); } - if (Is_stronghold(&u.uz) && !mindless(ptr)) /* know about the trap doors */ + if (Is_stronghold(&u.uz) && !mindless(ptr)) /* know about trap doors */ mon_learns_traps(mtmp, TRAPDOOR); /* quest leader and nemesis both know about all trap types */ if (ptr->msound == MS_LEADER || ptr->msound == MS_NEMESIS) @@ -1349,6 +1365,8 @@ makemon( mtmp->mcansee = mtmp->mcanmove = TRUE; mtmp->seen_resistance = M_SEEN_NOTHING; mtmp->mpeaceful = (mmflags & MM_ANGRY) ? FALSE : peace_minded(ptr); + if ((mmflags & MM_MINVIS) != 0) /* for ^G */ + mon_set_minvis(mtmp); /* call after place_monster() */ switch (ptr->mlet) { case S_MIMIC: @@ -1389,10 +1407,11 @@ makemon( break; case S_SPIDER: case S_SNAKE: - if (gi.in_mklev) + if (gi.in_mklev) { if (x && y) (void) mkobj_at(RANDOM_CLASS, x, y, TRUE); - (void) hideunder(mtmp); + (void) hideunder(mtmp); + } break; case S_LIGHT: case S_ELEMENTAL: @@ -1407,7 +1426,9 @@ makemon( } break; case S_EEL: - (void) hideunder(mtmp); + if (gi.in_mklev) { + (void) hideunder(mtmp); + } break; case S_LEPRECHAUN: mtmp->msleeping = 1; @@ -1433,7 +1454,7 @@ makemon( if ((ct = emits_light(mtmp->data)) > 0) new_light_source(mtmp->mx, mtmp->my, ct, LS_MONSTER, monst_to_any(mtmp)); - mitem = 0; /* extra inventory item for this monster */ + mitem = STRANGE_OBJECT; /* extra inventory item for this monster */ if (mndx == PM_VLAD_THE_IMPALER) mitem = CANDELABRUM_OF_INVOCATION; @@ -1453,8 +1474,8 @@ makemon( allow_minvent = FALSE; } else if (mndx == PM_WIZARD_OF_YENDOR) { mtmp->iswiz = TRUE; - gc.context.no_of_wizards++; - if (gc.context.no_of_wizards == 1 && Is_earthlevel(&u.uz)) + svc.context.no_of_wizards++; + if (svc.context.no_of_wizards == 1 && Is_earthlevel(&u.uz)) mitem = SPE_DIG; } else if (mndx == PM_GHOST && !(mmflags & MM_NONAME)) { mtmp = christen_monst(mtmp, rndghostname()); @@ -1463,7 +1484,7 @@ makemon( } else if (mndx == PM_PESTILENCE) { mitem = POT_SICKNESS; } - if (mitem && allow_minvent) + if (mitem != STRANGE_OBJECT && allow_minvent) (void) mongets(mtmp, mitem); if (gi.in_mklev) { @@ -1571,16 +1592,18 @@ makemon( exclaim = TRUE; } else if (canseemon(mtmp)) { /* mimic masquerading as furniture or object and not sensed */ - mhidden_description(mtmp, FALSE, mbuf); - what = upstart(strsubst(mbuf, ", mimicking ", "")); + mhidden_description(mtmp, MHID_ARTICLE | MHID_ALTMON, mbuf); + what = upstart(mbuf); } - if (what) + if (what) { + set_msg_xy(mtmp->mx, mtmp->my); Norep("%s%s appears%s%c", what, exclaim ? " suddenly" : "", next2u(x, y) ? " next to you" : (distu(x, y) <= (BOLT_LIM * BOLT_LIM)) ? " close by" : "", exclaim ? '!' : '.'); + } } /* if discernable and a threat, stop fiddling while Rome burns */ if (go.occupation) @@ -1615,10 +1638,11 @@ unmakemon( that just happened when creating this monster or the threshold had already been reached and further increments were suppressed; assume the latter */ - if (countbirth && gm.mvitals[mndx].born > 0 && gm.mvitals[mndx].born < 255) - gm.mvitals[mndx].born -= 1; + if (countbirth && svm.mvitals[mndx].born > 0 + && svm.mvitals[mndx].born < 255) + svm.mvitals[mndx].born -= 1; if ((mon->data->geno & G_UNIQ) != 0) - gm.mvitals[mndx].mvflags &= ~G_EXTINCT; + svm.mvitals[mndx].mvflags &= ~G_EXTINCT; mon->mhp = 0; /* let discard_minvent() know that mon isn't being kept */ /* uncreate any artifact that the monster was provided with; unlike @@ -1687,12 +1711,12 @@ create_critters(int cnt, return known; } -static boolean +staticfn boolean uncommon(int mndx) { if (mons[mndx].geno & (G_NOGEN | G_UNIQ)) return TRUE; - if (gm.mvitals[mndx].mvflags & G_GONE) + if (svm.mvitals[mndx].mvflags & G_GONE) return TRUE; if (Inhell) return (boolean) (mons[mndx].maligntyp > A_NEUTRAL); @@ -1705,18 +1729,18 @@ uncommon(int mndx) * comparing the dungeon alignment and monster alignment. * return an integer in the range of 0-5. */ -static int -align_shift(register struct permonst *ptr) +staticfn int +align_shift(struct permonst *ptr) { static NEARDATA long oldmoves = 0L; /* != 1, starting value of moves */ static NEARDATA s_level *lev; - register int alshift; + int alshift; - if (oldmoves != gm.moves) { + if (oldmoves != svm.moves) { lev = Is_special(&u.uz); - oldmoves = gm.moves; + oldmoves = svm.moves; } - switch ((lev) ? lev->flags.align : gd.dungeons[u.uz.dnum].flags.align) { + switch ((lev) ? lev->flags.align : svd.dungeons[u.uz.dnum].flags.align) { default: /* just in case */ case AM_NONE: alshift = 0; @@ -1734,6 +1758,17 @@ align_shift(register struct permonst *ptr) return alshift; } +/* return larger value if monster prefers the level temperature */ +staticfn int +temperature_shift(struct permonst *ptr) +{ + if (svl.level.flags.temperature + && pm_resistance(ptr, (svl.level.flags.temperature > 0) + ? MR_FIRE : MR_COLD)) + return 3; + return 0; +} + /* select a random monster type */ struct permonst * rndmonst(void) @@ -1745,8 +1780,8 @@ rndmonst(void) struct permonst * rndmonst_adj(int minadj, int maxadj) { - register struct permonst *ptr; - register int mndx; + struct permonst *ptr; + int mndx; int weight, totalweight, selected_mndx, zlevel, minmlev, maxmlev; boolean elemlevel; @@ -1792,6 +1827,7 @@ rndmonst_adj(int minadj, int maxadj) * potentially steal its spot. */ weight = (int) (ptr->geno & G_FREQ) + align_shift(ptr); + weight += temperature_shift(ptr); if (weight < 0 || weight > 127) { impossible("bad weight in rndmonst for mndx %d", mndx); weight = 0; @@ -1819,12 +1855,12 @@ rndmonst_adj(int minadj, int maxadj) } /* decide whether it's ok to generate a candidate monster by mkclass() */ -static boolean +staticfn boolean mk_gen_ok(int mndx, unsigned mvflagsmask, unsigned genomask) { struct permonst *ptr = &mons[mndx]; - if (gm.mvitals[mndx].mvflags & mvflagsmask) + if (svm.mvitals[mndx].mvflags & mvflagsmask) return FALSE; if (ptr->geno & genomask) return FALSE; @@ -1853,7 +1889,7 @@ struct permonst * mkclass_aligned(char class, int spc, /* special mons[].geno handling */ aligntyp atyp) { - register int first, last, num = 0; + int first, last, num = 0; int k, nums[SPECIAL_PM + 1]; /* +1: insurance for final return value */ int maxmlev, gehennom = Inhell != 0; unsigned mv_mask, gn_mask; @@ -1888,7 +1924,8 @@ 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; last++) { + for (last = first; last < SPECIAL_PM && mons[last].mlet == class; + last++) { if (atyp != A_NONE && sgn(mons[last].maligntyp) != sgn(atyp)) continue; /* traditionally mkclass() ignored hell-only and never-in-hell; @@ -1918,7 +1955,7 @@ mkclass_aligned(char class, int spc, /* special mons[].geno handling */ against picking the next demon resulted in incubus being picked nearly twice as often as succubus); we need the '+1' in case the entire set is too high - level (really low gl.level hero) */ + level (really low svl.level hero) */ nums[last] = k + 1 - (adj_lev(&mons[last]) > (u.ulevel * 2)); num += nums[last]; } @@ -1943,7 +1980,7 @@ mkclass_aligned(char class, int spc, /* special mons[].geno handling */ int mkclass_poly(int class) { - register int first, last, num = 0; + int first, last, num = 0; unsigned gmask; for (first = LOW_PM; first < SPECIAL_PM; first++) @@ -1974,7 +2011,7 @@ mkclass_poly(int class) /* adjust strength of monsters based on u.uz and u.ulevel */ int -adj_lev(register struct permonst *ptr) +adj_lev(struct permonst *ptr) { int tmp, tmp2; @@ -1982,7 +2019,7 @@ adj_lev(register struct permonst *ptr) /* does not depend on other strengths, but does get stronger * every time he is killed */ - tmp = ptr->mlevel + gm.mvitals[PM_WIZARD_OF_YENDOR].died; + tmp = ptr->mlevel + svm.mvitals[PM_WIZARD_OF_YENDOR].died; if (tmp > 49) tmp = 49; return tmp; @@ -2083,12 +2120,12 @@ grow_up(struct monst *mtmp, struct monst *victim) /* new form might force gender change */ fem = is_male(ptr) ? 0 : is_female(ptr) ? 1 : mtmp->female; - if (gm.mvitals[newtype].mvflags & G_GENOD) { /* allow G_EXTINCT */ + if (svm.mvitals[newtype].mvflags & G_GENOD) { /* allow G_EXTINCT */ if (canspotmon(mtmp)) pline("As %s grows up into %s, %s %s!", mon_nam(mtmp), an(pmname(ptr, Mgender(mtmp))), mhe(mtmp), nonliving(ptr) ? "expires" : "dies"); - set_mon_data(mtmp, ptr); /* keep gm.mvitals[] accurate */ + set_mon_data(mtmp, ptr); /* keep svm.mvitals[] accurate */ mondied(mtmp); return (struct permonst *) 0; } else if (canspotmon(mtmp)) { @@ -2108,11 +2145,11 @@ grow_up(struct monst *mtmp, struct monst *victim) slightly less sexist if prepared for it...) */ : (fem && !mtmp->female) ? "female " : "", pmname(ptr, fem)); - pline("%s %s %s.", upstart(y_monnam(mtmp)), - (fem != mtmp->female) ? "changes into" - : humanoid(ptr) ? "becomes" - : "grows up into", - an(buf)); + pline_mon(mtmp, "%s %s %s.", YMonnam(mtmp), + (fem != mtmp->female) ? "changes into" + : humanoid(ptr) ? "becomes" + : "grows up into", + an(buf)); } set_mon_data(mtmp, ptr); if (mtmp->cham == oldtype && is_shapeshifter(ptr)) @@ -2142,9 +2179,9 @@ grow_up(struct monst *mtmp, struct monst *victim) } struct obj * -mongets(register struct monst *mtmp, int otyp) +mongets(struct monst *mtmp, int otyp) { - register struct obj *otmp; + struct obj *otmp; if (!otyp) return (struct obj *) 0; @@ -2159,9 +2196,8 @@ mongets(register struct monst *mtmp, int otyp) otmp->cursed = FALSE; if (otmp->spe < 0) otmp->spe = 0; - otmp->oeroded = 0; - otmp->oeroded2 = 0; - otmp->oerodeproof = TRUE; + otmp->oerodeproof = 1; + otmp->oeroded = otmp->oeroded2 = 0; } else if (is_mplayer(mtmp->data) && is_sword(otmp)) { otmp->spe = (3 + rn2(4)); } @@ -2276,7 +2312,7 @@ golemhp(int type) * (Some "animal" types are co-aligned, but also hungry.) */ boolean -peace_minded(register struct permonst *ptr) +peace_minded(struct permonst *ptr) { aligntyp mal = ptr->maligntyp, ual = u.ualign.type; @@ -2288,6 +2324,8 @@ peace_minded(register struct permonst *ptr) return TRUE; if (ptr->msound == MS_NEMESIS) return FALSE; + if (ptr == &mons[PM_ERINYS]) + return !u.ualign.abuse; if (race_peaceful(ptr)) return TRUE; @@ -2399,7 +2437,7 @@ static const NEARDATA char syms[] = { }; void -set_mimic_sym(register struct monst *mtmp) +set_mimic_sym(struct monst *mtmp) { int typ, roomno, rt; unsigned appear, ap_type; @@ -2415,8 +2453,8 @@ set_mimic_sym(register struct monst *mtmp) /* only valid for INSIDE of room */ roomno = levl[mx][my].roomno - ROOMOFFSET; if (roomno >= 0) - rt = gi.in_mklev ? gr.rooms[roomno].rtype - : gr.rooms[roomno].orig_rtype; + rt = gi.in_mklev ? svr.rooms[roomno].rtype + : svr.rooms[roomno].orig_rtype; #ifdef SPECIALIZATION else if (IS_ROOM(typ)) rt = OROOM, roomno = 0; @@ -2426,7 +2464,7 @@ set_mimic_sym(register struct monst *mtmp) if (OBJ_AT(mx, my)) { ap_type = M_AP_OBJECT; - appear = gl.level.objects[mx][my]->otyp; + appear = svl.level.objects[mx][my]->otyp; } else if (IS_DOOR(typ) || IS_WALL(typ) || typ == SDOOR || typ == SCORR) { ap_type = M_AP_FURNITURE; /* @@ -2446,7 +2484,7 @@ set_mimic_sym(register struct monst *mtmp) appear = S_hcdoor; else appear = S_vcdoor; - } else if (gl.level.flags.is_maze_lev + } else if (svl.level.flags.is_maze_lev && !(In_mines(&u.uz) && in_town(u.ux, u.uy)) && !In_sokoban(&u.uz) && rn2(2)) { ap_type = M_AP_OBJECT; @@ -2504,7 +2542,7 @@ set_mimic_sym(register struct monst *mtmp) goto assign_sym; } } else { - s_sym = syms[rn2(SIZE(syms))]; + s_sym = ROLL_FROM(syms); assign_sym: if (s_sym == MAXOCLASSES) { static const int furnsyms[] = { @@ -2513,7 +2551,7 @@ set_mimic_sym(register struct monst *mtmp) }; ap_type = M_AP_FURNITURE; - appear = furnsyms[rn2(SIZE(furnsyms))]; + appear = ROLL_FROM(furnsyms); } else { ap_type = M_AP_OBJECT; if (s_sym == S_MIMIC_DEF) { @@ -2535,7 +2573,7 @@ set_mimic_sym(register struct monst *mtmp) && (appear == STATUE || appear == FIGURINE || appear == CORPSE || appear == EGG || appear == TIN)) { int mndx = rndmonnum(), - nocorpse_ndx = (gm.mvitals[mndx].mvflags & G_NOCORPSE) != 0; + nocorpse_ndx = (svm.mvitals[mndx].mvflags & G_NOCORPSE) != 0; if (appear == CORPSE && nocorpse_ndx) mndx = rn1(PM_WIZARD - PM_ARCHEOLOGIST + 1, PM_ARCHEOLOGIST); @@ -2547,7 +2585,7 @@ set_mimic_sym(register struct monst *mtmp) MCORPSENM(mtmp) = mndx; } else if (ap_type == M_AP_OBJECT && appear == SLIME_MOLD) { newmcorpsenm(mtmp); - MCORPSENM(mtmp) = gc.context.current_fruit; + MCORPSENM(mtmp) = svc.context.current_fruit; /* if no objects of this fruit type have been created yet, context.current_fruit is available for re-use when the player assigns a new fruit name; override that--having a mimic as the @@ -2606,10 +2644,21 @@ bagotricks( update_inventory(); /* for perm_invent */ } } else if (!tipping) { - pline1(!moncount ? nothing_happens : "Nothing seems to happen."); + pline1(!moncount ? nothing_happens : nothing_seems_to_happen); } } return moncount; } +/* create some or all remaining erinyes around the player */ +void +summon_furies(int limit) /* number to create, or 0 to create until extinct */ +{ + int i = 0; + while (mk_gen_ok(PM_ERINYS, G_GONE, 0U) && (i < limit || !limit)) { + makemon(&mons[PM_ERINYS], u.ux, u.uy, MM_ADJACENTOK | MM_NOWAIT); + i++; + } +} + /*makemon.c*/ diff --git a/src/mcastu.c b/src/mcastu.c index 006a974489..4e01c7a954 100644 --- a/src/mcastu.c +++ b/src/mcastu.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mcastu.c $NHDT-Date: 1596498177 2020/08/03 23:42:57 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.68 $ */ +/* NetHack 3.7 mcastu.c $NHDT-Date: 1726168598 2024/09/12 19:16:38 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.105 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -41,21 +41,21 @@ enum mcast_cleric_spells { CLC_GEYSER }; -static void cursetxt(struct monst *, boolean); -static int choose_magic_spell(int); -static int choose_clerical_spell(int); -static int m_cure_self(struct monst *, int); -static void cast_wizard_spell(struct monst *, int, int); -static void cast_cleric_spell(struct monst *, int, int); -static boolean has_special_spell_list(struct permonst *); -static int choose_special_spell(struct monst *); -static boolean is_undirected_spell(unsigned int, int); -static boolean spell_would_be_useless(struct monst *, unsigned int, int); -static boolean is_entombed(coordxy, coordxy); -static void sheer_cold(int *dmg); +staticfn void cursetxt(struct monst *, boolean); +staticfn int choose_magic_spell(int); +staticfn int choose_clerical_spell(int); +staticfn int m_cure_self(struct monst *, int); +staticfn void cast_wizard_spell(struct monst *, int, int); +staticfn void cast_cleric_spell(struct monst *, int, int); +staticfn boolean has_special_spell_list(struct permonst *); +staticfn int choose_special_spell(struct monst *); +staticfn boolean is_undirected_spell(unsigned int, int); +staticfn boolean spell_would_be_useless(struct monst *, unsigned int, int); +staticfn boolean is_entombed(coordxy, coordxy); +staticfn void sheer_cold(int *dmg); /* feedback when frustrated monster couldn't cast a spell */ -static void +staticfn void cursetxt(struct monst *mtmp, boolean undirected) { if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my)) { @@ -73,8 +73,8 @@ cursetxt(struct monst *mtmp, boolean undirected) else point_msg = "at you, then curses"; - pline("%s points %s.", Monnam(mtmp), point_msg); - } else if ((!(gm.moves % 4) || !rn2(4))) { + pline_mon(mtmp, "%s points %s.", Monnam(mtmp), point_msg); + } else if ((!(svm.moves % 4) || !rn2(4))) { if (!Deaf) Norep("You hear a mumbled curse."); /* Deaf-aware */ } @@ -82,7 +82,7 @@ cursetxt(struct monst *mtmp, boolean undirected) /* convert a level-based random selection into a specific mage spell; inappropriate choices will be screened out by spell_would_be_useless() */ -static int +staticfn int choose_magic_spell(int spellval) { /* for 3.4.3 and earlier, val greater than 22 selected default spell */ @@ -94,6 +94,7 @@ choose_magic_spell(int spellval) case 23: if (Antimagic || Hallucination) return MGC_PSI_BOLT; + FALLTHROUGH; /*FALLTHRU*/ case 22: case 21: @@ -135,7 +136,7 @@ choose_magic_spell(int spellval) } /* convert a level-based random selection into a specific cleric spell */ -static int +staticfn int choose_clerical_spell(int spellnum) { /* for 3.4.3 and earlier, num greater than 13 selected the default spell @@ -148,6 +149,7 @@ choose_clerical_spell(int spellnum) case 14: if (rn2(3)) return CLC_OPEN_WOUNDS; + FALLTHROUGH; /*FALLTHRU*/ case 13: return CLC_GEYSER; @@ -187,7 +189,7 @@ choose_clerical_spell(int spellnum) * CLC_OPEN_WOUNDS would instead cast MGC_PSI_BOLT, etc.) Refactoring would be * needed to make this work. */ -static boolean +staticfn boolean has_special_spell_list(struct permonst *mdat) { /* Add more monsters to this list as needed. */ @@ -197,13 +199,14 @@ has_special_spell_list(struct permonst *mdat) case PM_DEMOGORGON: case PM_ORCUS: return TRUE; + default: + return FALSE; } - return FALSE; } /* mdat is a spellcaster with a special spell list; return a spell from its list */ -static int +staticfn int choose_special_spell(struct monst *mtmp) { if (mtmp->data == &mons[PM_DISPATER]) { @@ -217,7 +220,7 @@ choose_special_spell(struct monst *mtmp) MGC_CURE_SELF, MGC_AGGRAVATION, MGC_HASTE_SELF, MGC_DISAPPEAR, MGC_ENTOMB, MGC_SUMMON_MONS, MGC_TPORT_AWAY }; - return dispater_list[rn2(SIZE(dispater_list))]; + return ROLL_FROM(dispater_list); } else if (mtmp->data == &mons[PM_ASMODEUS]) { if ((mtmp->mhp * 8 < mtmp->mhpmax) @@ -258,7 +261,7 @@ choose_special_spell(struct monst *mtmp) * lot of cases, but not for certain spells that would be too nasty * (such as destroying armor) */ do { - selection = orcus_list[rn2(SIZE(orcus_list))]; + selection = ROLL_FROM(orcus_list); } while (selection == MGC_DESTRY_ARMR && m_seenres(mtmp, M_SEEN_MAGR)); return selection; } @@ -273,8 +276,8 @@ choose_special_spell(struct monst *mtmp) */ int castmu( - register struct monst *mtmp, /* caster */ - register struct attack *mattk, /* caster's current attack */ + struct monst *mtmp, /* caster */ + struct attack *mattk, /* caster's current attack */ boolean thinks_it_foundyou, /* might be mistaken if displaced */ boolean foundyou) /* knows hero's precise location */ { @@ -327,50 +330,57 @@ castmu( } /* monster unable to cast spells? */ - if (mtmp->mcan || mtmp->mspec_used || !ml) { + if (mtmp->mcan || mtmp->mspec_used || !ml + || m_seenres(mtmp, cvt_adtyp_to_mseenres(mattk->adtyp))) { cursetxt(mtmp, is_undirected_spell(mattk->adtyp, spellnum)); return M_ATTK_MISS; } if (mattk->adtyp == AD_SPEL || mattk->adtyp == AD_CLRC) { - mtmp->mspec_used = 10 - mtmp->m_lev; - if (mtmp->mspec_used < 2) - mtmp->mspec_used = 2; + /* monst->m_lev is unsigned (uchar), monst->mspec_used is int */ + mtmp->mspec_used = (int) ((mtmp->m_lev < 8) ? (10 - mtmp->m_lev) : 2); } - /* monster can cast spells, but is casting a directed spell at the - wrong place? If so, give a message, and return. Do this *after* - penalizing mspec_used. */ + /* Monster can cast spells, but is casting a directed spell at the + * wrong place? If so, give a message, and return. + * Do this *after* penalizing mspec_used. + * + * FIXME? + * Shouldn't wall of lava have a case similar to wall of water? + * And should cold damage hit water or lava instead of missing + * even when the caster has targeted the wrong spot? Likewise + * for fire mis-aimed at ice. + */ if (!foundyou && thinks_it_foundyou && !is_undirected_spell(mattk->adtyp, spellnum)) { - pline("%s casts a spell at %s!", - canseemon(mtmp) ? Monnam(mtmp) : "Something", - is_waterwall(mtmp->mux,mtmp->muy) ? "empty water" - : "thin air"); + pline_mon(mtmp, "%s casts a spell at %s!", + canseemon(mtmp) ? Monnam(mtmp) : "Something", + is_waterwall(mtmp->mux, mtmp->muy) ? "empty water" + : "thin air"); return M_ATTK_MISS; } nomul(0); if (rn2(ml * 10) < (mtmp->mconf ? 100 : 20)) { /* fumbled attack */ Soundeffect(se_air_crackles, 60); - if (canseemon(mtmp) && !Deaf) + if (canseemon(mtmp) && !Deaf) { + set_msg_xy(mtmp->mx, mtmp->my); pline_The("air crackles around %s.", mon_nam(mtmp)); + } return M_ATTK_MISS; } if ((canspotmon(mtmp) || !is_undirected_spell(mattk->adtyp, spellnum)) /* dark speech has its own casting message */ && spellnum != MGC_DARK_SPEECH) { - pline("%s casts a spell%s!", - canspotmon(mtmp) ? Monnam(mtmp) : "Something", - is_undirected_spell(mattk->adtyp, spellnum) - ? "" - : (Invis && !perceives(mtmp->data) - && (mtmp->mux != u.ux || mtmp->muy != u.uy)) - ? " at a spot near you" - : (Displaced - && (mtmp->mux != u.ux || mtmp->muy != u.uy)) - ? " at your displaced image" - : " at you"); + pline_mon(mtmp, "%s casts a spell%s!", + canspotmon(mtmp) ? Monnam(mtmp) : "Something", + is_undirected_spell(mattk->adtyp, spellnum) ? "" + : (Invis && !perceives(mtmp->data) + && !u_at(mtmp->mux, mtmp->muy)) + ? " at a spot near you" + : (Displaced && !u_at(mtmp->mux, mtmp->muy)) + ? " at your displaced image" + : " at you"); } /* @@ -393,6 +403,10 @@ castmu( dmg = (dmg + 1) / 2; ret = M_ATTK_HIT; + /* + * FIXME: none of these hit the steed when hero is riding, nor do + * they inflict damage on carried items. + */ switch (mattk->adtyp) { case AD_FIRE: pline("You're enveloped in flames."); @@ -401,8 +415,12 @@ castmu( pline("But you resist the effects."); monstseesu(M_SEEN_FIRE); dmg = 0; + } else { + monstunseesu(M_SEEN_FIRE); } burn_away_slime(); + /* burn up flammable items on the floor, melt ice terrain */ + mon_spell_hits_spot(mtmp, AD_FIRE, u.ux, u.uy); break; case AD_COLD: sheer_cold(&dmg); @@ -415,36 +433,37 @@ castmu( pline("Some missiles bounce off!"); dmg = (dmg + 1) / 2; monstseesu(M_SEEN_MAGR); + } else { + monstunseesu(M_SEEN_MAGR); } if (Half_spell_damage) { /* stacks with Antimagic */ dmg = (dmg + 1) / 2; } + /* shower of magic missiles scuffs an engraving */ + mon_spell_hits_spot(mtmp, AD_MAGM, u.ux, u.uy); break; case AD_SPEL: /* wizard spell */ case AD_CLRC: /* clerical spell */ - { if (mattk->adtyp == AD_SPEL) cast_wizard_spell(mtmp, dmg, spellnum); else cast_cleric_spell(mtmp, dmg, spellnum); dmg = 0; /* done by the spell casting functions */ break; - } - } + } /* switch */ if (dmg) mdamageu(mtmp, dmg); return ret; } -static int +staticfn int 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; @@ -468,14 +487,22 @@ touch_of_death(struct monst *mtmp) u.mh = 0; rehumanize(); /* fatal iff Unchanging */ } else if (drain >= u.uhpmax) { - gk.killer.format = KILLED_BY; - Strcpy(gk.killer.name, kbuf); + svk.killer.format = KILLED_BY; + Strcpy(svk.killer.name, kbuf); done(DIED); } else { - u.uhpmax -= drain; + /* HP manipulation similar to poisoned(attrib.c) */ + int olduhp = u.uhp, + uhpmin = minuhpmax(3), + newuhpmax = u.uhpmax - drain; + + setuhpmax(max(newuhpmax, uhpmin), FALSE); + dmg = adjuhploss(dmg, olduhp); /* reduce pending damage if uhp has + * already been reduced due to drop + * in uhpmax */ losehp(dmg, kbuf, KILLED_BY); } - gk.killer.name[0] = '\0'; /* not killed if we get here... */ + svk.killer.name[0] = '\0'; /* not killed if we get here... */ } /* give a reason for death by some monster spells */ @@ -488,7 +515,7 @@ death_inflicted_by( Strcpy(outbuf, deathreason); if (mtmp) { struct permonst *mptr = mtmp->data, - *champtr = (mtmp->cham >= LOW_PM) ? &mons[mtmp->cham] : mptr; + *champtr = (ismnum(mtmp->cham)) ? &mons[mtmp->cham] : mptr; const char *realnm = pmname(champtr, Mgender(mtmp)), *fakenm = pmname(mptr, Mgender(mtmp)); @@ -518,14 +545,18 @@ death_inflicted_by( If you modify either of these, be sure to change is_undirected_spell() and spell_would_be_useless(). */ -static -void +staticfn void cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) { /* Orcus's casting often bypasses Antimagic or has some other effect on * spells */ const boolean orcus = (mtmp->data == &mons[PM_ORCUS]); + if (dmg < 0) { + impossible("monster cast wizard spell (%d) with negative dmg (%d)?", + spellnum, dmg); + return; + } if (dmg == 0 && !is_undirected_spell(AD_SPEL, spellnum)) { impossible("cast directed wizard spell (%d) with dmg=0?", spellnum); return; @@ -543,6 +574,7 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) } else { touch_of_death(mtmp); } + monstunseesu(M_SEEN_MAGR); } else { if (Antimagic) { shieldeff(u.ux, u.uy); @@ -553,7 +585,7 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) dmg = 0; break; case MGC_CLONE_WIZ: - if (mtmp->iswiz && gc.context.no_of_wizards == 1) { + if (mtmp->iswiz && svc.context.no_of_wizards == 1) { pline("Double Trouble..."); clonewiz(); dmg = 0; @@ -587,8 +619,12 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) shieldeff(u.ux, u.uy); monstseesu(M_SEEN_MAGR); pline("A field of force surrounds you!"); - } else if (!destroy_arm(some_armor(&gy.youmonst), FALSE)) { + } else if (!destroy_arm(some_armor(&gy.youmonst))) { Your("skin itches."); + } else { + /* monsters only realize you aren't magic-protected if armor is + actually destroyed */ + monstunseesu(M_SEEN_MAGR); } dmg = 0; break; @@ -609,14 +645,15 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) losestr(rnd(dmg), death_inflicted_by(kbuf, "strength loss", mtmp), KILLED_BY); - gk.killer.name[0] = '\0'; /* not killed if we get here... */ + svk.killer.name[0] = '\0'; /* not killed if we get here... */ + monstunseesu(M_SEEN_MAGR); } dmg = 0; break; 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)) @@ -638,6 +675,7 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) if (Half_spell_damage) dmg = (dmg + 1) / 2; make_stunned((HStun & TIMEOUT) + (long) dmg, FALSE); + monstunseesu(M_SEEN_MAGR); } dmg = 0; break; @@ -655,6 +693,8 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) shieldeff(u.ux, u.uy); monstseesu(M_SEEN_MAGR); dmg = (dmg + 1) / 2; + } else { + monstunseesu(M_SEEN_MAGR); } if (dmg <= 5) You("get a slight %sache.", body_part(HEAD)); @@ -693,7 +733,7 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) /* Only create actual walls where there is no monster or object * or trap in the way. */ if (!rn2(6) && levl[x][y].typ == ROOM && !m_at(x, y) - && !gl.level.objects[x][y] && !t_at(x, y)) { + && !svl.level.objects[x][y] && !t_at(x, y)) { levl[x][y].typ = rn2(2) ? HWALL : VWALL; levl[x][y].wall_info &= ~W_NONDIGGABLE; newsym(x, y); @@ -849,10 +889,16 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) DISABLE_WARNING_FORMAT_NONLITERAL -static void +staticfn void cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) { int orig_dmg = 0; + + if (dmg < 0) { + impossible("monster cast cleric spell (%d) with negative dmg (%d)?", + spellnum, dmg); + return; + } if (dmg == 0 && !is_undirected_spell(AD_CLRC, spellnum)) { impossible("cast directed cleric spell (%d) with dmg=0?", spellnum); return; @@ -869,12 +915,16 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) dmg = (dmg + 1) / 2; if (u.umonnum == PM_IRON_GOLEM) { You("rust!"); - Strcpy(gk.killer.name, "rusted away"); - gk.killer.format = NO_KILLER_PREFIX; + Strcpy(svk.killer.name, "rusted away"); + svk.killer.format = NO_KILLER_PREFIX; rehumanize(); dmg = 0; /* prevent further damage after rehumanization */ } erode_armor(&gy.youmonst, ERODE_RUST); +#if 0 /* since inventory items aren't affected, don't include this */ + /* make floor items wet */ + water_damage_chain(level.objects[u.ux][u.uy], TRUE); +#endif break; case CLC_FIRE_PILLAR: pline("A pillar of fire strikes all around you!"); @@ -883,14 +933,18 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) shieldeff(u.ux, u.uy); monstseesu(M_SEEN_FIRE); dmg = 0; + } else { + monstunseesu(M_SEEN_FIRE); } if (Half_spell_damage) dmg = (dmg + 1) / 2; burn_away_slime(); (void) burnarmor(&gy.youmonst); + /* item destruction dmg */ (void) destroy_items(&gy.youmonst, AD_FIRE, orig_dmg); ignite_items(gi.invent); - (void) burn_floor_objects(u.ux, u.uy, TRUE, FALSE); + /* burn up flammable items on the floor, melt ice terrain */ + mon_spell_hits_spot(mtmp, AD_FIRE, u.ux, u.uy); break; case CLC_LIGHTNING: { Soundeffect(se_bolt_of_lightning, 80); @@ -905,12 +959,21 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) monstseesu(M_SEEN_REFL); break; } + monstunseesu(M_SEEN_REFL); monstseesu(M_SEEN_ELEC); + } else { + monstunseesu(M_SEEN_ELEC | M_SEEN_REFL); } if (Half_spell_damage) dmg = (dmg + 1) / 2; (void) destroy_items(&gy.youmonst, AD_ELEC, orig_dmg); - (void) flashburn((long) rnd(100)); + /* lightning might destroy iron bars if hero is on such a spot; + reflection protects terrain here [execution won't get here due + to 'if (reflects) break' above] but hero resistance doesn't; + do this before maybe blinding the hero via flashburn() */ + mon_spell_hits_spot(mtmp, AD_ELEC, u.ux, u.uy); + /* blind hero; no effect if already blind */ + (void) flashburn((long) rnd(100), TRUE); break; } case CLC_CURSE_ITEMS: @@ -994,7 +1057,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; @@ -1021,7 +1084,12 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) if (Antimagic) { shieldeff(u.ux, u.uy); monstseesu(M_SEEN_MAGR); - dmg = 1; + dmg = 1; /* to produce nomul(-1), not actual damage */ + } else { + dmg = 4 + (int) mtmp->m_lev; + if (Half_spell_damage) + dmg = (dmg + 1) / 2; + monstunseesu(M_SEEN_MAGR); } dynamic_multi_reason(mtmp, "paralyzed", FALSE); make_paralyzed(dmg, TRUE, (const char *) 0); @@ -1043,6 +1111,7 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) You_feel("%s!", oldprop ? "trippier" : "trippy"); else You_feel("%sconfused!", oldprop ? "more " : ""); + monstunseesu(M_SEEN_MAGR); } dmg = 0; break; @@ -1054,6 +1123,8 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) shieldeff(u.ux, u.uy); monstseesu(M_SEEN_MAGR); dmg = (dmg + 1) / 2; + } else { + monstunseesu(M_SEEN_MAGR); } if (dmg <= 5) Your("skin itches badly for a moment."); @@ -1076,7 +1147,7 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) RESTORE_WARNING_FORMAT_NONLITERAL -static boolean +staticfn boolean is_undirected_spell(unsigned int adtyp, int spellnum) { if (adtyp == AD_SPEL) { @@ -1111,7 +1182,7 @@ is_undirected_spell(unsigned int adtyp, int spellnum) } /* Some spells are useless under some circumstances. */ -static boolean +staticfn boolean spell_would_be_useless(struct monst *mtmp, unsigned int adtyp, int spellnum) { /* Some spells don't require the player to really be there and can be cast @@ -1148,7 +1219,7 @@ spell_would_be_useless(struct monst *mtmp, unsigned int adtyp, int spellnum) if (!mcouldseeu && (spellnum == MGC_SUMMON_MONS || (!mtmp->iswiz && spellnum == MGC_CLONE_WIZ))) return TRUE; - if ((!mtmp->iswiz || gc.context.no_of_wizards > 1) + if ((!mtmp->iswiz || svc.context.no_of_wizards > 1) && spellnum == MGC_CLONE_WIZ) return TRUE; /* aggravation (global wakeup) when everyone is already active */ @@ -1205,7 +1276,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, @@ -1218,7 +1289,7 @@ buzzmu(struct monst *mtmp, struct attack *mattk) /* is (x,y) entombed (completely surrounded by boulders or nonwalkable spaces)? * note that (x,y) itself is not checked */ -static boolean +staticfn boolean is_entombed(coordxy x, coordxy y) { coordxy xx, yy; @@ -1238,7 +1309,7 @@ is_entombed(coordxy x, coordxy y) * The function expects *dmg to be the already rolled amount of damage the spell * will deliver by default. It may adjust *dmg in the process; the caller should * anticipate this. */ -static void +staticfn void sheer_cold(int *dmg) { pline("You're covered in frigid frost."); @@ -1248,6 +1319,9 @@ sheer_cold(int *dmg) monstseesu(M_SEEN_COLD); *dmg /= 4; } + else { + monstunseesu(M_SEEN_COLD); + } destroy_items(&gy.youmonst, AD_COLD, *dmg); } diff --git a/src/mdlib.c b/src/mdlib.c index 9811697813..0ef2023607 100644 --- a/src/mdlib.c +++ b/src/mdlib.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mdlib.c $NHDT-Date: 1655402414 2022/06/16 18:00:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.31 $ */ +/* NetHack 3.7 mdlib.c $NHDT-Date: 1701499945 2023/12/02 06:52:25 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.51 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Kenneth Lorber, Kensington, Maryland, 2015. */ /* Copyright (c) M. Stephenson, 1990, 1991. */ @@ -30,7 +30,8 @@ #include "context.h" #include "flag.h" #include "dlb.h" -#include +#include "hacklib.h" + /* version information */ #ifdef SHORT_FILENAMES #include "patchlev.h" @@ -58,18 +59,11 @@ static boolean date_via_env = FALSE; extern unsigned long md_ignored_features(void); -char *mdlib_version_string(char *, const char *); -char *version_id_string(char *, size_t, const char *); -char *bannerc_string(char *, size_t, const char *); -int case_insensitive_comp(const char *, const char *); +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; -static void make_version(void); -static char *eos(char *); -int mstrength(struct permonst *); - -#if 0 -static char *mdlib_strsubst(char *, const char *, const char *); -#endif +staticfn void make_version(void); #ifndef HAS_NO_MKSTEMP #ifdef _MSC_VER @@ -84,25 +78,32 @@ extern int GUILaunched; #endif /* these are in extern.h but we don't include hack.h */ +/* XXX move to new file mdlib.h? */ +extern void populate_nomakedefs(struct version_info *) NONNULLARG1; /*date.c*/ +extern void free_nomakedefs(void); /* date.c */ void runtime_info_init(void); -const char *do_runtime_info(int *); +const char *do_runtime_info(int *) NO_NNARGS; void release_runtime_info(void); -void populate_nomakedefs(struct version_info *); -extern void free_nomakedefs(void); /* date.c */ +char *mdlib_version_string(char *, const char *) NONNULL NONNULLPTRS; + +staticfn void build_options(void); +staticfn int count_and_validate_winopts(void); +staticfn void opt_out_words(char *, int *) NONNULLPTRS; +staticfn void build_savebones_compat_string(void); -void build_options(void); -static int count_and_validate_winopts(void); -static void opt_out_words(char *, int *); -static void build_savebones_compat_string(void); static int idxopttext, done_runtime_opt_init_once = 0; -#define MAXOPT 40 -static char *opttext[120] = { 0 }; -char optbuf[COLBUFSZ]; +#define MAXOPT 60 /* 3.7: currently 40 lines get inserted into opttext[] */ +static char *opttext[MAXOPT] = { 0 }; +#define STOREOPTTEXT(line) \ + ((void) ((idxopttext < MAXOPT) \ + ? (opttext[idxopttext++] = dupstr(line)) \ + : 0)) +static char optbuf[COLBUFSZ]; static struct version_info version; static const char opt_indent[] = " "; struct win_information { - const char *id, /* DEFAULT_WINDOW_SYS string */ + const char *id, /* windowtype value */ *name; /* description, often same as id */ boolean valid; }; @@ -110,7 +111,7 @@ struct win_information { static struct win_information window_opts[] = { #ifdef TTY_GRAPHICS { "tty", - /* testing 'TILES_IN_GLYPHMAP' here would bring confusion because it could + /* testing TILES_IN_GLYPHMAP here would bring confusion because it could apply to another interface such as X11, so check MSDOS explicitly instead; even checking TTY_TILES_ESCCODES would probably be confusing to most users (and it will already be listed separately @@ -162,7 +163,7 @@ static struct win_information window_opts[] = { }; #if !defined(MAKEDEFS_C) -static int count_and_validate_soundlibopts(void); +staticfn int count_and_validate_soundlibopts(void); struct soundlib_information { enum soundlib_ids id; @@ -254,10 +255,10 @@ md_ignored_features(void) ); } -static void +staticfn void make_version(void) { - register int i; + int i; /* * integer version number @@ -281,9 +282,7 @@ make_version(void) #endif /* objects (10..14) */ /* flag bits and/or other global variables (15..26) */ -#ifdef TEXTCOLOR - | (1L << 17) -#endif + /* color support always*/ | (1L << 17) #ifdef INSURANCE | (1L << 18) #endif @@ -307,11 +306,9 @@ make_version(void) for (i = 1; artifact_names[i]; i++) continue; version.entity_count = (unsigned long) (i - 1); - for (i = 1; objects[i].oc_class != ILLOBJ_CLASS; i++) - continue; + i = NUM_OBJECTS; version.entity_count = (version.entity_count << 12) | (unsigned long) i; - for (i = 0; mons[i].mlet; i++) - continue; + i = NUMMONS; version.entity_count = (version.entity_count << 12) | (unsigned long) i; /* * Value used for compiler (word size/field alignment/padding) check. @@ -344,29 +341,6 @@ mdlib_version_string(char *outbuf, const char *delim) extern void nh_snprintf(const char *func, int line, char *str, size_t size, const char *fmt, ...); -#ifdef MAKEDEFS_C -DISABLE_WARNING_FORMAT_NONLITERAL - -void -nh_snprintf(const char *func UNUSED, int line UNUSED, char *str, size_t size, - const char *fmt, ...) -{ - va_list ap; - int n; - - va_start(ap, fmt); - n = vsnprintf(str, size, fmt, ap); - va_end(ap); - - if (n < 0 || (size_t)n >= size) { /* is there a problem? */ - str[size-1] = 0; /* make sure it is nul terminated */ - } - -} - -RESTORE_WARNING_FORMAT_NONLITERAL -#endif /* MAKEDEFS_C */ - char * version_id_string(char *outbuf, size_t bufsz, const char *build_date) { @@ -392,9 +366,9 @@ version_id_string(char *outbuf, size_t bufsz, const char *build_date) Strcpy(&subbuf[1], PORT_SUB_ID); #endif - Snprintf(outbuf, bufsz, "%s xNetHack%s Version %s%s - last %s %s.", PORT_ID, - subbuf, mdlib_version_string(versbuf, "."), statusbuf, - date_via_env ? "revision" : "build", build_date); + Snprintf(outbuf, bufsz, "%s xNetHack%s Version %s%s - last %s %s.", + PORT_ID, subbuf, mdlib_version_string(versbuf, "."), statusbuf, + date_via_env ? "revision" : "build", build_date); return outbuf; } @@ -442,36 +416,9 @@ mkstemp(char *template) #endif /* HAS_NO_MKSTEMP */ #endif /* MAKEDEFS_C || FOR_RUNTIME */ -static char * -eos(char *str) -{ - while (*str) - str++; - return str; -} - -#if 0 -static char * -mdlib_strsubst(char *bp, const char *orig, const char *replacement) -{ - char *found, buf[BUFSZ]; - - if (bp) { - /* [this could be replaced by strNsubst(bp, orig, replacement, 1)] */ - found = strstr(bp, orig); - if (found) { - Strcpy(buf, found + strlen(orig)); - Strcpy(found, replacement); - Strcat(bp, buf); - } - } - return bp; -} -#endif - static char save_bones_compat_buf[BUFSZ]; -static void +staticfn void build_savebones_compat_string(void) { #ifdef VERSION_COMPATIBILITY @@ -503,9 +450,7 @@ static const char *const build_opts[] = { #ifdef ANSI_DEFAULT "ANSI default terminal", #endif -#ifdef TEXTCOLOR "color", -#endif #ifdef TTY_GRAPHICS #ifdef TTY_TILES_ESCCODES "console escape codes for tile hinting", @@ -536,9 +481,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", @@ -680,12 +622,18 @@ static const char *const build_opts[] = { #endif #ifdef SYSCF "system configuration at run-time", +#endif +#ifdef PANICTRACE + "show stack trace on error", +#endif +#ifdef CRASHREPORT + "launch browser to report issues", #endif save_bones_compat_buf, "and basic NetHack features" }; -static int +staticfn int count_and_validate_winopts(void) { int i, cnt = 0; @@ -696,6 +644,7 @@ count_and_validate_winopts(void) #ifdef WIN32 window_opts[i].valid = FALSE; if ((GUILaunched + && case_insensitive_comp(window_opts[i].id, "curses") != 0 && case_insensitive_comp(window_opts[i].id, "mswin") != 0) || (!GUILaunched && case_insensitive_comp(window_opts[i].id, "mswin") == 0)) @@ -709,7 +658,7 @@ count_and_validate_winopts(void) } #if !defined(MAKEDEFS_C) -static int +staticfn int count_and_validate_soundlibopts(void) { int i, cnt = 0; @@ -723,9 +672,10 @@ count_and_validate_soundlibopts(void) } #endif -static void -opt_out_words(char *str, /* input, but modified during processing */ - int *length_p) /* in/out */ +staticfn void +opt_out_words( + char *str, /* input, but modified during processing */ + int *length_p) /* in/out */ { char *word; @@ -739,9 +689,7 @@ opt_out_words(char *str, /* input, but modified during processing */ if (word) *word = '\0'; if (*length_p + (int) strlen(str) > COLNO - 5) { - opttext[idxopttext] = strdup(optbuf); - if (idxopttext < (MAXOPT - 1)) - idxopttext++; + STOREOPTTEXT(optbuf); Sprintf(optbuf, "%s", opt_indent), *length_p = (int) strlen(opt_indent); } else { @@ -752,7 +700,7 @@ opt_out_words(char *str, /* input, but modified during processing */ } } -void +staticfn void build_options(void) { char buf[COLBUFSZ]; @@ -766,9 +714,7 @@ build_options(void) #endif #endif build_savebones_compat_string(); - opttext[idxopttext] = strdup(optbuf); - if (idxopttext < (MAXOPT - 1)) - idxopttext++; + STOREOPTTEXT(optbuf); #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) #if (NH_DEVEL_STATUS == NH_STATUS_BETA) #define STATUS_ARG " [beta]" @@ -778,15 +724,11 @@ build_options(void) #else #define STATUS_ARG "" #endif /* NH_DEVEL_STATUS == NH_STATUS_RELEASED */ - Sprintf(optbuf, "%sNetHack version %d.%d%s\n", + Sprintf(optbuf, "%sxNetHack version %d.%d%s\n", opt_indent, VERSION_MAJOR, VERSION_MINOR, STATUS_ARG); - opttext[idxopttext] = strdup(optbuf); - if (idxopttext < (MAXOPT - 1)) - idxopttext++; + STOREOPTTEXT(optbuf); Sprintf(optbuf, "Options compiled into this edition:"); - opttext[idxopttext] = strdup(optbuf); - if (idxopttext < (MAXOPT - 1)) - idxopttext++; + STOREOPTTEXT(optbuf); optbuf[0] = '\0'; length = COLNO + 1; /* force 1st item onto new line */ for (i = 0; i < SIZE(build_opts); i++) { @@ -802,19 +744,13 @@ build_options(void) (i < SIZE(build_opts) - 1) ? "," : "."); opt_out_words(buf, &length); } - opttext[idxopttext] = strdup(optbuf); - if (idxopttext < (MAXOPT - 1)) - idxopttext++; + STOREOPTTEXT(optbuf); optbuf[0] = '\0'; winsyscnt = count_and_validate_winopts(); - opttext[idxopttext] = strdup(optbuf); - if (idxopttext < (MAXOPT - 1)) - idxopttext++; + STOREOPTTEXT(optbuf); Sprintf(optbuf, "Supported windowing system%s:", (winsyscnt > 1) ? "s" : ""); - opttext[idxopttext] = strdup(optbuf); - if (idxopttext < (MAXOPT - 1)) - idxopttext++; + STOREOPTTEXT(optbuf); optbuf[0] = '\0'; length = COLNO + 1; /* force 1st item onto new line */ @@ -846,19 +782,12 @@ build_options(void) #if !defined(MAKEDEFS_C) cnt = 0; - opttext[idxopttext] = dupstr(optbuf); - if (idxopttext < (MAXOPT - 1)) - idxopttext++; + STOREOPTTEXT(optbuf); optbuf[0] = '\0'; soundlibcnt = count_and_validate_soundlibopts(); - opttext[idxopttext] = dupstr(optbuf); - if (idxopttext < (MAXOPT - 1)) - idxopttext++; - Sprintf(optbuf, "Supported soundlib%s:", - (soundlibcnt > 1) ? "s" : ""); - opttext[idxopttext] = dupstr(optbuf); - if (idxopttext < (MAXOPT - 1)) - idxopttext++; + STOREOPTTEXT(optbuf); + Sprintf(optbuf, "Supported soundlib%s:", (soundlibcnt > 1) ? "s" : ""); + STOREOPTTEXT(optbuf); optbuf[0] = '\0'; length = COLNO + 1; /* force 1st item onto new line */ @@ -896,9 +825,7 @@ build_options(void) #endif #endif /* !MAKEDEFS_C */ - opttext[idxopttext] = dupstr(optbuf); - if (idxopttext < (MAXOPT - 1)) - idxopttext++; + STOREOPTTEXT(optbuf); optbuf[0] = '\0'; #if defined(MAKEDEFS_C) || defined(FOR_RUNTIME) @@ -909,7 +836,8 @@ build_options(void) /* 1 2 3 4 5 6 7 1234567890123456789012345678901234567890123456789012345678901234567890123456 */ - " \"Permission is hereby granted, free of charge, to any person obtaining", + (" \"Permission is hereby granted, free of charge," + " to any person obtaining"), " a copy of this software and associated documentation files (the ", " \"Software\"), to deal in the Software without restriction including", " without limitation the rights to use, copy, modify, merge, publish,", @@ -924,37 +852,17 @@ build_options(void) /* add lua copyright notice; ":TAG:" substitutions are deferred to caller */ for (i = 0; lua_info[i]; ++i) { - opttext[idxopttext] = strdup(lua_info[i]); - if (idxopttext < (MAXOPT - 1)) - idxopttext++; + STOREOPTTEXT(lua_info[i]); } } #endif /* MAKEDEFS_C || FOR_RUNTIME */ /* end with a blank line */ - opttext[idxopttext] = strdup(""); - if (idxopttext < (MAXOPT - 1)) - idxopttext++; + STOREOPTTEXT(""); return; } -int -case_insensitive_comp(const char *s1, const char *s2) -{ - uchar u1, u2; - - for (;; s1++, s2++) { - u1 = (uchar) *s1; - if (isupper(u1)) - u1 = (uchar) tolower(u1); - u2 = (uchar) *s2; - if (isupper(u2)) - u2 = (uchar) tolower(u2); - if (u1 == '\0' || u1 != u2) - break; - } - return u1 - u2; -} +#undef STOREOPTTEXT void runtime_info_init(void) @@ -978,7 +886,7 @@ do_runtime_info(int *rtcontext) if (!done_runtime_opt_init_once) runtime_info_init(); if (idxopttext && rtcontext) - if (*rtcontext >= 0 && *rtcontext < (MAXOPT - 1)) { + if (*rtcontext >= 0 && *rtcontext < MAXOPT) { retval = opttext[*rtcontext]; *rtcontext += 1; } diff --git a/src/mhitm.c b/src/mhitm.c index e9fed651b1..3e6a4d8c45 100644 --- a/src/mhitm.c +++ b/src/mhitm.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mhitm.c $NHDT-Date: 1627412283 2021/07/27 18:58:03 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.199 $ */ +/* 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. */ @@ -9,35 +9,35 @@ static const char brief_feeling[] = "have a %s feeling for a moment, then it passes."; -static void noises(struct monst *, struct attack *); -static void pre_mm_attack(struct monst *, struct monst *); -static void missmm(struct monst *, struct monst *, struct attack *); -static int hitmm(struct monst *, struct monst *, struct attack *, +staticfn void noises(struct monst *, struct attack *); +staticfn void pre_mm_attack(struct monst *, struct monst *); +staticfn void missmm(struct monst *, struct monst *, struct attack *); +staticfn int hitmm(struct monst *, struct monst *, struct attack *, struct obj *, int); -static int gazemm(struct monst *, struct monst *, struct attack *); -static int gulpmm(struct monst *, struct monst *, struct attack *); -static int explmm(struct monst *, struct monst *, struct attack *); -static int mdamagem(struct monst *, struct monst *, struct attack *, +staticfn int gazemm(struct monst *, struct monst *, struct attack *); +staticfn int gulpmm(struct monst *, struct monst *, struct attack *); +staticfn int explmm(struct monst *, struct monst *, struct attack *); +staticfn int mdamagem(struct monst *, struct monst *, struct attack *, struct obj *, int); -static void mswingsm(struct monst *, struct monst *, struct obj *); -static int passivemm(struct monst *, struct monst *, boolean, int, +staticfn void mswingsm(struct monst *, struct monst *, struct obj *); +staticfn int passivemm(struct monst *, struct monst *, boolean, int, struct obj *); -static void +staticfn void noises(struct monst *magr, struct attack *mattk) { boolean farq = (mdistu(magr) > 15); - if (!Deaf && (farq != gf.far_noise || gm.moves - gn.noisetime > 10)) { + if (!Deaf && (farq != gf.far_noise || svm.moves - gn.noisetime > 10)) { gf.far_noise = farq; - gn.noisetime = gm.moves; + gn.noisetime = svm.moves; You_hear("%s%s.", (mattk->aatyp == AT_EXPL) ? "an explosion" : "some noises", farq ? " in the distance" : ""); } } -static void +staticfn void pre_mm_attack(struct monst *magr, struct monst *mdef) { boolean showit = FALSE; @@ -72,8 +72,7 @@ pre_mm_attack(struct monst *magr, struct monst *mdef) } /* feedback for when a monster-vs-monster attack misses */ -static -void +staticfn void missmm( struct monst *magr, /* attacker */ struct monst *mdef, /* defender */ @@ -112,9 +111,9 @@ missmm( */ /* have monsters fight each other */ int -fightm(register struct monst *mtmp) +fightm(struct monst *mtmp) { - register struct monst *mon, *nmon; + struct monst *mon, *nmon; int result, has_u_swallowed; /* perhaps the monster will resist Conflict */ if (resist_conflict(mtmp)) @@ -241,7 +240,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)) @@ -304,8 +303,8 @@ mdisplacem( */ int mattackm( - register struct monst *magr, - register struct monst *mdef) + struct monst *magr, + struct monst *mdef) { int i, /* loop counter */ tmp, /* armor class difference */ @@ -338,7 +337,7 @@ mattackm( wakeup(mdef, FALSE, TRUE); } - /* undetect monsters become un-hidden if they are attacked */ + /* mundetected monsters become un-hidden if they are attacked */ if (mdef->mundetected) { mdef->mundetected = 0; newsym(mdef->mx, mdef->my); @@ -352,8 +351,15 @@ mattackm( if (!justone) montype = makeplural(montype); You("dream of %s.", montype); - } else - pline("Suddenly, you notice %s.", a_monnam(mdef)); + } else { + if (iflags.last_msg == PLNMSG_HIDE_UNDER + && mdef->m_id == gl.last_hider) + pline_mon(mdef, "%s emerges from hiding.", Monnam(mdef)); + else if (mdef->m_id == gl.last_hider) + You("notice %s.", mon_nam(mdef)); + else + pline("Suddenly, you notice %s.", a_monnam(mdef)); + } } } @@ -370,7 +376,7 @@ mattackm( * some cases, in which case this still counts as its move for the round * and it shouldn't move again. */ - magr->mlstmv = gm.moves; + magr->mlstmv = svm.moves; /* controls whether a mind flayer uses all of its tentacle-for-DRIN attacks; when fighting a headless monster, stop after the first @@ -415,6 +421,7 @@ mattackm( mswingsm(magr, mdef, mwep); tmp += hitval(mwep, mdef); } + FALLTHROUGH; /*FALLTHRU*/ case AT_CLAW: case AT_KICK: @@ -464,6 +471,8 @@ mattackm( pline("%s divides as %s hits it!", Monnam(mdef), mon_nam(magr)); (void) mintrap(mclone, NO_TRAP_FLAGS); + if (DEADMONSTER(magr)) + res[i] |= M_ATTK_AGR_DIED; } } } else @@ -493,7 +502,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); @@ -533,25 +542,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; @@ -559,6 +563,9 @@ mattackm( res[i] = M_ATTK_DEF_DIED; if (DEADMONSTER(magr)) res[i] |= M_ATTK_AGR_DIED; + } else { + strike = 0; + attk = 0; } break; @@ -578,7 +585,7 @@ mattackm( if (res[i] & M_ATTK_AGR_DIED) return res[i]; /* return if aggressor can no longer attack */ - if (helpless(magr)) + if ((res[i] & M_ATTK_AGR_DONE) || helpless(magr)) return res[i]; /* eg. defender was knocked into a level teleport trap */ if (mon_offmap(mdef)) @@ -642,7 +649,7 @@ failed_grab( } /* Returns the result of mdamagem(). */ -static int +staticfn int hitmm( struct monst *magr, struct monst *mdef, @@ -719,6 +726,7 @@ hitmm( Snprintf(buf, sizeof buf, "%s squeezes", magr_name); break; } + FALLTHROUGH; /*FALLTHRU*/ default: if (!weaponhit || !mwep || !mwep->oartifact) @@ -735,7 +743,7 @@ hitmm( } /* Returns the same values as mdamagem(). */ -static int +staticfn int gazemm(struct monst *magr, struct monst *mdef, struct attack *mattk) { char buf[BUFSZ]; @@ -785,7 +793,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; @@ -810,7 +818,9 @@ boolean engulf_target(struct monst *magr, struct monst *mdef) { struct rm *lev; - int dx, dy; + int ax, ay, dx, dy; + boolean uatk = (magr == &gy.youmonst), + udef = (mdef == &gy.youmonst); /* can't swallow something that's too big */ if (mdef->data->msize >= MZ_HUGE @@ -821,28 +831,32 @@ engulf_target(struct monst *magr, struct monst *mdef) if (mdef->mtrapped || magr->mtrapped) return FALSE; - /* (hypothetical) engulfers who can pass through walls aren't - limited by rock|trees|bars */ - if ((magr == &gy.youmonst) ? Passes_walls : passes_walls(magr->data)) - return TRUE; - - /* don't swallow something in a spot where attacker wouldn't - otherwise be able to move onto; we don't want to engulf - a wall-phaser and end up with a non-phaser inside a wall */ - dx = mdef->mx, dy = mdef->my; - if (mdef == &gy.youmonst) - dx = u.ux, dy = u.uy; + /* if attacker is phasing in solid rock and defender can't move there, + or vice versa, don't allow engulf to succeed; otherwise expelling + might not be able to place attacker and defender both back on map; + when defender is the hero, a sanity_check complaint about placing + the hero on top of a monster can occur */ + dx = (mdef == &gy.youmonst) ? u.ux : mdef->mx; + dy = (mdef == &gy.youmonst) ? u.uy : mdef->my; lev = &levl[dx][dy]; - if (IS_ROCK(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))) + if (!(udef ? Passes_walls : passes_walls(mdef->data)) + && (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; + ax = (magr == &gy.youmonst) ? u.ux : magr->mx; + ay = (magr == &gy.youmonst) ? u.uy : magr->my; + lev = &levl[ax][ay]; + if (!(uatk ? Passes_walls : passes_walls(magr->data)) + && (IS_OBSTRUCTED(lev->typ) || closed_door(ax, ay) || IS_TREE(lev->typ) + || (lev->typ == IRONBARS && !is_whirly(mdef->data)))) return FALSE; return TRUE; } /* Returns the same values as mattackm(). */ -static int +staticfn int gulpmm( struct monst *magr, struct monst *mdef, @@ -893,7 +907,7 @@ gulpmm( dx = mdef->mx; dy = mdef->my; /* - * Leave the defender in the monster chain at it's current position, + * Leave the defender in the monster chain at its current position, * but don't leave it on the screen. Move the aggressor to the * defender's position. */ @@ -961,7 +975,7 @@ gulpmm( return status; } -static int +staticfn int explmm(struct monst *magr, struct monst *mdef, struct attack *mattk) { int result; @@ -972,7 +986,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); @@ -1006,7 +1020,7 @@ explmm(struct monst *magr, struct monst *mdef, struct attack *mattk) /* * See comment at top of mattackm(), for return values. */ -static int +staticfn int mdamagem( struct monst *magr, struct monst *mdef, @@ -1040,7 +1054,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 */ @@ -1062,8 +1076,8 @@ mdamagem( if (mhitm_knockback(magr, mdef, mattk, &mhm.hitflags, (MON_WEP(magr) != 0)) - && ((mhm.hitflags & (M_ATTK_DEF_DIED|M_ATTK_HIT)) != 0 - || (mdef->mstate & (MON_DETACH|MON_MIGRATING|MON_LIMBO)) != 0)) + && ((mhm.hitflags & (M_ATTK_DEF_DIED | M_ATTK_HIT)) != 0 + || mon_offmap(mdef))) return mhm.hitflags; if (mhm.done) @@ -1101,22 +1115,24 @@ mdamagem( if (mattk->adtyp == AD_DGST) { /* various checks similar to dog_eat and meatobj. * after monkilled() to provide better message ordering */ - if (mdef->cham >= LOW_PM) { + if (ismnum(mdef->cham)) { (void) newcham(magr, (struct permonst *) 0, NC_SHOW_MSG); } else if (pd == &mons[PM_GREEN_SLIME] && !slimeproof(pa)) { (void) newcham(magr, &mons[PM_GREEN_SLIME], NC_SHOW_MSG); } else if (pd == &mons[PM_WRAITH]) { (void) grow_up(magr, (struct monst *) 0); /* don't grow up twice */ - return (M_ATTK_DEF_DIED | (!DEADMONSTER(magr) ? 0 : M_ATTK_AGR_DIED)); + 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); } /* caveat: above digestion handling doesn't keep `pa' up to date */ - return (M_ATTK_DEF_DIED | (grow_up(magr, mdef) ? 0 : M_ATTK_AGR_DIED)); + return (M_ATTK_DEF_DIED + | (grow_up(magr, mdef) ? 0 : M_ATTK_AGR_DIED)); } return (mhm.hitflags == M_ATTK_AGR_DIED) ? M_ATTK_AGR_DIED : M_ATTK_HIT; } @@ -1153,7 +1169,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... */ ; @@ -1177,7 +1193,7 @@ mon_poly(struct monst *magr, struct monst *mdef, int dmg) } else if (newcham(mdef, (struct permonst *) 0, NO_NC_FLAGS)) { if (gv.vis) { /* either seen or adjacent */ boolean was_seen = !!strcmpi("It", Before), - verbosely = Verbose(1, monpoly1) || !was_seen; + verbosely = flags.verbose || !was_seen; if (canspotmon(mdef)) pline("%s%s%s turns into %s.", Before, @@ -1197,7 +1213,7 @@ mon_poly(struct monst *magr, struct monst *mdef, int dmg) (void) rloc(magr, RLOC_MSG); } } else { - if (gv.vis && Verbose(1, monpoly2)) + if (gv.vis && flags.verbose) pline1(nothing_happens); } } @@ -1225,6 +1241,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); @@ -1248,7 +1270,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); } } @@ -1276,15 +1298,16 @@ rustm(struct monst *mdef, struct obj *obj) (void) erode_obj(obj, (char *) 0, dmgtyp, EF_GREASE | EF_VERBOSE); } -static void +staticfn void mswingsm( struct monst *magr, /* attacker */ struct monst *mdef, /* defender */ struct obj *otemp) /* attacker's weapon */ { - if (Verbose(1, mswingsm) && !Blind && mon_visible(magr)) { + if (flags.verbose && !Blind && mon_visible(magr)) { boolean bash = (is_pole(otemp) - && dist2(magr->mx, magr->my, mdef->mx, mdef->my) <= 2); + && (dist2(magr->mx, magr->my, mdef->mx, mdef->my) + <= 2)); pline("%s %s %s%s %s at %s.", Monnam(magr), mswings_verb(otemp, bash), (otemp->quan > 1L) ? "one of " : "", mhis(magr), xname(otemp), @@ -1296,7 +1319,7 @@ mswingsm( * Passive responses by defenders. Does not replicate responses already * handled above. Returns same values as mattackm. */ -static int +staticfn int passivemm( struct monst *magr, struct monst *mdef, @@ -1407,17 +1430,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; @@ -1425,7 +1446,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; @@ -1433,26 +1454,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; case AD_DISE: /* black mold */ /* monster illness not yet implemented */ @@ -1482,7 +1504,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 5696e87cfc..fdf05cd167 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mhitu.c $NHDT-Date: 1625838648 2021/07/09 13:50:48 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.246 $ */ +/* NetHack 3.7 mhitu.c $NHDT-Date: 1721844072 2024/07/24 18:01:12 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.318 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -8,18 +8,19 @@ static NEARDATA struct obj *mon_currwep = (struct obj *) 0; -static void missmu(struct monst *, boolean, struct attack *); -static void mswings(struct monst *, struct obj *, boolean); -static void wildmiss(struct monst *, struct attack *); -static void calc_mattacku_vars(struct monst *, boolean *, boolean *, +staticfn void missmu(struct monst *, boolean, struct attack *); +staticfn void mswings(struct monst *, struct obj *, boolean); +staticfn void wildmiss(struct monst *, struct attack *); +staticfn void calc_mattacku_vars(struct monst *, boolean *, boolean *, boolean *, boolean *); -static void summonmu(struct monst *, boolean); -static int hitmu(struct monst *, struct attack *); -static int gulpmu(struct monst *, struct attack *); -static int explmu(struct monst *, struct attack *, boolean); -static void mayberem(struct monst *, const char *, struct obj *, +staticfn void summonmu(struct monst *, boolean); +staticfn int hitmu(struct monst *, struct attack *); +staticfn int gulpmu(struct monst *, struct attack *); +staticfn int explmu(struct monst *, struct attack *, boolean); +staticfn void mayberem(struct monst *, const char *, struct obj *, const char *); -static int passiveum(struct permonst *, struct monst *, struct attack *); +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) @@ -144,8 +145,8 @@ barehitmsg(struct monst *magr) { "backhand", "combo", "caress", "noogie", "sucker punch", "pat", "tickle" }; - verb = Hallucination ? hmartialattk[rn2(SIZE(hmartialattk))] - : martialattk[rn2(SIZE(martialattk))]; + verb = Hallucination ? ROLL_FROM(hmartialattk) + : ROLL_FROM(martialattk); } if (magr->data == &mons[PM_QUANTUM_MECHANIC]) /* no simple way to conjugate this since vtense isn't smart enough to @@ -177,7 +178,7 @@ hitmsg(struct monst *mtmp, struct attack *mattk) if same gender, "engagingly" for nymph, normal msg for others. */ if ((compat = could_seduce(mtmp, &gy.youmonst, mattk)) != 0 && !mtmp->mcan && !mtmp->mspec_used) { - pline("%s %s you %s.", Monst_name, + pline_mon(mtmp, "%s %s you %s.", Monst_name, !Blind ? "smiles at" : !Deaf ? "talks to" : "touches", (compat == 2) ? "engagingly" : "seductively"); } else { @@ -233,14 +234,14 @@ hitmsg(struct monst *mtmp, struct attack *mattk) && gh.hitmsg_prev != NULL && mattk == gh.hitmsg_prev + 1 && mattk->aatyp == gh.hitmsg_prev->aatyp) ? " again" : ""; - pline("%s %s%s%s", Monst_name, verb, again, punct); + pline_mon(mtmp, "%s %s%s%s", Monst_name, verb, again, punct); } gh.hitmsg_mid = mtmp->m_id; gh.hitmsg_prev = mattk; } /* monster missed you */ -static void +staticfn void missmu(struct monst *mtmp, boolean nearmiss, struct attack *mattk) { gh.hitmsg_mid = 0; @@ -250,15 +251,16 @@ missmu(struct monst *mtmp, boolean nearmiss, struct attack *mattk) map_invisible(mtmp->mx, mtmp->my); if (could_seduce(mtmp, &gy.youmonst, mattk) && !mtmp->mcan) - pline("%s pretends to be friendly.", Monnam(mtmp)); + pline_mon(mtmp, "%s pretends to be friendly.", Monnam(mtmp)); else { const char *blocker = attack_blocker(&gy.youmonst); if (blocker && !rn2(5)) - pline("Your %s %s %s attack.", blocker, - rn2(3) ? "blocks" : "deflects", s_suffix(mon_nam(mtmp))); + pline_mon(mtmp, "Your %s %s %s attack.", + blocker, rn2(3) ? "blocks" : "deflects", + s_suffix(mon_nam(mtmp))); else - pline("%s %smisses!", Monnam(mtmp), - (nearmiss && Verbose(1, missmu)) ? "just " : ""); + pline_mon(mtmp, "%s %smisses!", Monnam(mtmp), + (nearmiss && flags.verbose) ? "just " : ""); } stop_occupation(); @@ -291,15 +293,17 @@ mswings_verb( } /* monster swings obj */ -static void +staticfn void mswings( struct monst *mtmp, /* attacker */ struct obj *otemp, /* attacker's weapon */ boolean bash) /* True: polearm used at too close range */ { - if (Verbose(1, mswings) && !Blind && mon_visible(mtmp)) { - pline("%s %s %s%s %s.", Monnam(mtmp), mswings_verb(otemp, bash), - (otemp->quan > 1L) ? "one of " : "", mhis(mtmp), xname(otemp)); + if (flags.verbose && !Blind && mon_visible(mtmp)) { + pline_mon(mtmp, "%s %s %s%s %s.", Monnam(mtmp), + mswings_verb(otemp, bash), + (otemp->quan > 1L) ? "one of " : "", + mhis(mtmp), xname(otemp)); } } @@ -331,17 +335,31 @@ u_slow_down(void) exercise(A_DEX, FALSE); } -/* monster attacked your displaced image */ -static void +/* monster attacked wrong location due to monster blindness, hero + invisibility, hero displacement, or hero being underwater */ +staticfn void wildmiss(struct monst *mtmp, struct attack *mattk) { int compat; - const char *Monst_name; /* Monnam(mtmp) */ + const char *Monst_name; /* Monnam(), deferred until after early returns */ + /* expected reasons for wildmiss() */ + boolean unotseen = (!mtmp->mcansee || (Invis && !perceives(mtmp->data))), + unotthere = (Displaced != 0), usubmerged = (Underwater != 0); + + /* the reasons for wildmiss end up getting checked twice so that the + impossible can be given, if warranted, before the early returns */ + if (!unotseen && !unotthere && !usubmerged) { + /* this used to be the 'else' case below */ + impossible("%s attacks you without knowing your location?", + Some_Monnam(mtmp)); + return; + } /* no map_invisible() -- no way to tell where _this_ is coming from */ - if (!Verbose(1, wildmiss)) + if (!flags.verbose) return; + /* no feedback if hero doesn't see the monster's spot */ if (!cansee(mtmp->mx, mtmp->my)) return; /* maybe it's attacking an image around the corner? */ @@ -350,7 +368,8 @@ wildmiss(struct monst *mtmp, struct attack *mattk) ? could_seduce(mtmp, &gy.youmonst, mattk) : 0); Monst_name = Monnam(mtmp); - if (!mtmp->mcansee || (Invis && !perceives(mtmp->data))) { + set_msg_xy(mtmp->mx, mtmp->my); + if (unotseen) { /* !mtmp->cansee || (Invis && !perceives(mtmp->data)) */ const char *swings = (mattk->aatyp == AT_BITE) ? "snaps" : (mattk->aatyp == AT_KICK) ? "kicks" : (mattk->aatyp == AT_STNG @@ -379,7 +398,7 @@ wildmiss(struct monst *mtmp, struct attack *mattk) break; } - } else if (Displaced) { + } else if (unotthere) { /* Displaced */ /* give 'displaced' message even if hero is Blind */ if (compat) pline("%s smiles %s at your %sdisplaced image...", Monst_name, @@ -392,7 +411,7 @@ wildmiss(struct monst *mtmp, struct attack *mattk) * image, since the displaced image is also invisible. */ Monst_name, Invis ? "invisible " : ""); - } else if (Underwater) { + } else if (usubmerged) { /* Underwater */ /* monsters may miss especially on water level where bubbles shake the player here and there */ if (compat) @@ -401,9 +420,9 @@ wildmiss(struct monst *mtmp, struct attack *mattk) pline("%s is fooled by water reflections and misses!", Monst_name); - } else - impossible("%s attacks you without knowing your location?", - Monst_name); + } else { + ; /*NOTREACHED*/ + } } void @@ -412,12 +431,12 @@ expels( struct permonst *mdat, /* if mtmp is polymorphed, mdat != mtmp->data */ boolean message) { - gc.context.botl = 1; + disp.botl = TRUE; if (message) { 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); @@ -453,12 +472,15 @@ expels( /* select a monster's next attack, possibly substituting for its usual one */ struct attack * -getmattk(struct monst *magr, struct monst *mdef, - int indx, int prev_result[], struct attack *alt_attk_buf) +getmattk( + struct monst *magr, struct monst *mdef, + int indx, int prev_result[], + struct attack *alt_attk_buf) { struct permonst *mptr = magr->data; struct attack *attk = &mptr->mattk[indx]; struct obj *weap = (magr == &gy.youmonst) ? uwep : MON_WEP(magr); + boolean udefend = mdef == &gy.youmonst; /* honor SEDUCE=0 */ if (!SYSOPT_SEDUCE) { @@ -489,7 +511,7 @@ getmattk(struct monst *magr, struct monst *mdef, attk->adtyp = AD_STUN; /* make drain-energy damage be somewhat in proportion to energy */ - } else if (attk->adtyp == AD_DREN && mdef == &gy.youmonst) { + } else if (attk->adtyp == AD_DREN && udefend) { int ulev = max(u.ulevel, 6); *alt_attk_buf = *attk; @@ -551,12 +573,36 @@ getmattk(struct monst *magr, struct monst *mdef, *alt_attk_buf = *attk; attk = alt_attk_buf; attk->adtyp = AD_PHYS; + + /* liches have a touch attack for cold damage and also a spell attack; + they won't use the spell for monster vs monster so become impotent + against cold resistant foes; change the touch damage from cold to + physical if target will resist */ + } else if (indx == 0 && attk->aatyp == AT_TUCH && attk->adtyp == AD_COLD + && (udefend ? Cold_resistance : resists_cold(mdef)) + /* don't substitute if target is immune to normal damage */ + && mdef->data != &mons[PM_SHADE]) { + *alt_attk_buf = *attk; + attk = alt_attk_buf; + attk->adtyp = AD_PHYS; + /* lessen new physical damage compared to old cold damage: + * before after + * lich: 1d10 1d6 + * demi: 3d4 2d4 + * master: 3d6 2d6 + * arch-: 5d6 3d6 + */ + attk->damn = (attk->damn + 1) / 2; + if (attk->damd == 10) + attk->damd = 6; + } + return attk; } /* calc some variables needed for mattacku() */ -static void +staticfn void calc_mattacku_vars( struct monst *mtmp, boolean *ranged, boolean *range2, @@ -584,7 +630,7 @@ calc_mattacku_vars( * take care of it... */ int -mattacku(register struct monst *mtmp) +mattacku(struct monst *mtmp) { struct attack *mattk, alt_attk; int i, j = 0, tmp, sum[NATTK]; @@ -626,15 +672,15 @@ mattacku(register struct monst *mtmp) /* Your steed won't attack you */ return 0; /* Orcs like to steal and eat horses and the like */ - if (!rn2(is_orc(mtmp->data) ? 2 : 4) && next2u(mtmp->mx, mtmp->my)) { + if (!rn2(is_orc(mtmp->data) ? 2 : 4) && m_next2u(mtmp)) { /* attack your steed instead; 'bhitpos' and 'notonhead' are - already set from tagetting hero */ + already set from targeting hero */ i = mattackm(mtmp, u.usteed); if ((i & M_ATTK_AGR_DIED) != 0) return 1; /* make sure steed is still alive and within range */ if ((i & M_ATTK_DEF_DIED) != 0 || !u.usteed - || !next2u(mtmp->mx, mtmp->my)) + || !m_next2u(mtmp)) return 0; /* Let your steed retaliate */ gb.bhitpos.x = mtmp->mx, gb.bhitpos.y = mtmp->my; @@ -669,7 +715,7 @@ mattacku(register 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; } @@ -702,7 +748,7 @@ mattacku(register struct monst *mtmp) * parallelism to work, we can't rephrase it, so we * zap the "laid by you" momentarily instead. */ - struct obj *obj = gl.level.objects[u.ux][u.uy]; + struct obj *obj = svl.level.objects[u.ux][u.uy]; if (obj || u.umonnum == PM_TRAPPER || (gy.youmonst.data->mlet == S_EEL @@ -722,14 +768,14 @@ mattacku(register struct monst *mtmp) pline( "Wait, %s! There's a hidden %s named %s there!", m_monnam(mtmp), - pmname(gy.youmonst.data, Ugender), gp.plname); + pmname(gy.youmonst.data, Ugender), svp.plname); else pline( "Wait, %s! There's a %s named %s hiding under %s!", m_monnam(mtmp), pmname(gy.youmonst.data, Ugender), - gp.plname, - doname(gl.level.objects[u.ux][u.uy])); + svp.plname, + doname(svl.level.objects[u.ux][u.uy])); if (obj) obj->spe = save_spe; } else @@ -751,7 +797,7 @@ mattacku(register struct monst *mtmp) pline("It gets stuck on you."); else /* see note about m_monnam() above */ pline("Wait, %s! That's a %s named %s!", m_monnam(mtmp), - pmname(gy.youmonst.data, Ugender), gp.plname); + pmname(gy.youmonst.data, Ugender), svp.plname); if (sticky) set_ustuck(mtmp); gy.youmonst.m_ap_type = M_AP_NOTHING; @@ -765,14 +811,15 @@ mattacku(register struct monst *mtmp) if (!canspotmon(mtmp)) map_invisible(mtmp->mx, mtmp->my); if (!youseeit) - pline("%s %s!", Something, (likes_gold(mtmp->data) - && gy.youmonst.mappearance == GOLD_PIECE) - ? "tries to pick you up" - : "disturbs you"); + pline("%s %s!", Something, + (likes_gold(mtmp->data) + && gy.youmonst.mappearance == GOLD_PIECE) + ? "tries to pick you up" + : "disturbs you"); else /* see note about m_monnam() above */ pline("Wait, %s! That %s is really %s named %s!", m_monnam(mtmp), mimic_obj_name(&gy.youmonst), - an(pmname(&mons[u.umonnum], Ugender)), gp.plname); + an(pmname(&mons[u.umonnum], Ugender)), svp.plname); if (gm.multi < 0) { /* this should always be the case */ char buf[BUFSZ]; @@ -808,14 +855,22 @@ mattacku(register struct monst *mtmp) also, were creature might change from human to animal or vice versa */ if (mtmp->cham == NON_PM && !mtmp->mcan && !range2 && (is_demon(mdat) || is_were(mdat))) { + boolean already_fleeing = mtmp->mflee != 0; + summonmu(mtmp, youseeit); + /* were-creature might have changed to beast form; if that has + caused it to become afraid (due to non-human reacting to scroll + of scare monster or engraved "Elbereth" which was being ignored + while in human form), don't continue this attack */ + if (mtmp->mflee && !already_fleeing) + return 0; mdat = mtmp->data; /* update cached value in case of were change */ } 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.", @@ -828,22 +883,27 @@ mattacku(register struct monst *mtmp) /* Unlike defensive stuff, don't let them use item _and_ attack. */ if (find_offensive(mtmp)) { - int foo = use_offensive(mtmp); + int offended = use_offensive(mtmp); - if (foo != 0) - return (foo == 1); + if (offended != 0) + return (offended == 1); } gs.skipdrin = FALSE; /* [see mattackm(mhitm.c)] */ firstfoundyou = foundyou; for (i = 0; i < NATTK; i++) { - /* recalc in case attack moved hero */ - calc_mattacku_vars(mtmp, &ranged, &range2, &foundyou, &youseeit); sum[i] = M_ATTK_MISS; - if (i > 0 && firstfoundyou /* previous attack might have moved hero */ - && (mtmp->mux != u.ux || mtmp->muy != u.uy)) - continue; /* fill in sum[] with 'miss' but skip other actions */ + if (i > 0) { + /* recalc in case prior attack moved hero; mtmp doesn't make + another attempt to guess your location but might have + accidentally knocked you to where it thought you were + [not sure whether that's actually possible] */ + calc_mattacku_vars(mtmp, &ranged, &range2, &foundyou, &youseeit); + /* if hero was found but isn't anymore, avoid wildmiss now */ + if (firstfoundyou && !foundyou) + continue; /* set sum[i] to 'miss' but skip other actions */ + } mon_currwep = (struct obj *) 0; mattk = getmattk(mtmp, &gy.youmonst, i, sum, &alt_attk); if ((u.uswallow && mattk->aatyp != AT_ENGL) @@ -914,10 +974,11 @@ mattacku(register 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); @@ -966,7 +1027,7 @@ mattacku(register struct monst *mtmp) mon_currwep = MON_WEP(mtmp); if (mon_currwep) { boolean bash = (is_pole(mon_currwep) - && next2u(mtmp->mx, mtmp->my)); + && m_next2u(mtmp)); hittmp = hitval(mon_currwep, &gy.youmonst); tmp += hittmp; @@ -996,11 +1057,11 @@ mattacku(register struct monst *mtmp) default: /* no attack */ break; } - if (gc.context.botl) + if (disp.botl) bot(); /* give player a chance of waking up before dying -kaa */ if (sum[i] == M_ATTK_HIT) { /* successful attack */ - if (u.usleep && u.usleep < gm.moves && !rn2(10)) { + if (u.usleep && u.usleep < svm.moves && !rn2(10)) { gm.multi = -1; gn.nomovemsg = "The combat suddenly awakens you."; } @@ -1015,7 +1076,7 @@ mattacku(register struct monst *mtmp) } /* monster summons help for its fight against hero */ -static void +staticfn void summonmu(struct monst *mtmp, boolean youseeit) { struct permonst *mdat = mtmp->data; @@ -1035,22 +1096,27 @@ summonmu(struct monst *mtmp, boolean youseeit) } if (is_were(mdat)) { + /* if hero has Protection_from_shape_changers, new_were() will work + in the critter-to-human direction but be a no-op the other way; + we repeat the criteria here for clarity */ if (is_human(mdat)) { /* maybe switch to animal form */ - if (!rn2(5 - (night() * 2))) + if (!Protection_from_shape_changers && !rn2(5 - (night() * 2))) new_were(mtmp); } else { /* maybe switch to back human form */ - if (!rn2(30)) + if (Protection_from_shape_changers || !rn2(30)) new_were(mtmp); } mdat = mtmp->data; /* form change invalidates cached value */ - if (!rn2(10)) { /* maybe summon compatible critters */ + /* maybe summon compatible critters; + not blocked by Protection_from_shape_changers */ + if (!rn2(10)) { int numseen, numhelp; char buf[BUFSZ], genericwere[BUFSZ]; 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) { @@ -1120,7 +1186,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", @@ -1191,7 +1257,8 @@ magic_negation(struct monst *mon) protection is too easy); it confers minimum mc 1 instead of 0 */ if ((is_you && ((HProtection && u.ublessed > 0) || u.uspellprot)) /* aligned priests and angels have innate intrinsic Protection */ - || (mon->data == &mons[PM_ALIGNED_CLERIC] || is_minion(mon->data))) + || (mon->data == &mons[PM_ALIGNED_CLERIC] + || is_minion(mon->data))) mc = 1; } return mc; @@ -1201,8 +1268,8 @@ magic_negation(struct monst *mon) * hitmu: monster hits you * returns MM_ flags */ -static int -hitmu(register struct monst *mtmp, register struct attack *mattk) +staticfn int +hitmu(struct monst *mtmp, struct attack *mattk) { struct permonst *mdat = mtmp->data; struct permonst *olduasmon = gy.youmonst.data; @@ -1234,7 +1301,7 @@ hitmu(register struct monst *mtmp, register struct attack *mattk) const char *what; char Amonbuf[BUFSZ]; - if ((obj = gl.level.objects[mtmp->mx][mtmp->my]) != 0) { + if ((obj = svl.level.objects[mtmp->mx][mtmp->my]) != 0) { if (Blind && !obj->dknown) what = something; else if (is_pool(mtmp->mx, mtmp->my) && !Underwater) @@ -1332,8 +1399,8 @@ hitmu(register struct monst *mtmp, register struct attack *mattk) else if (*hpmax_p > lowerlimit) *hpmax_p = lowerlimit; /* else unlikely... - * already at or below minimum threshold; do nothing */ - gc.context.botl = 1; + * already at or below minimum threshold, do nothing to hpmax */ + disp.botl = TRUE; } mdamageu(mtmp, mhm.damage); @@ -1366,13 +1433,13 @@ gulp_blnd_check(void) } /* monster swallows you, or damage if u.uswallow */ -static int +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; boolean mon_killed = FALSE; @@ -1478,8 +1545,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); + } } } @@ -1536,6 +1605,8 @@ gulpmu(struct monst *mtmp, struct attack *mattk) case AD_ACID: if (Acid_resistance) { You("are covered with a seemingly harmless goo."); + /* NB: the monst[un]seesu calls in gulpmu are no-ops since the + hero must be currently swallowed for the attack to hit... */ monstseesu(M_SEEN_ACID); tmp = 0; } else { @@ -1544,6 +1615,7 @@ gulpmu(struct monst *mtmp, struct attack *mattk) else You("are covered in slime! It burns!"); exercise(A_STR, FALSE); + monstunseesu(M_SEEN_ACID); } break; case AD_BLND: @@ -1571,6 +1643,8 @@ gulpmu(struct monst *mtmp, struct attack *mattk) monstseesu(M_SEEN_ELEC); ugolemeffects(AD_ELEC, tmp); tmp = 0; + } else { + monstunseesu(M_SEEN_ELEC); } } else tmp = 0; @@ -1583,8 +1657,10 @@ gulpmu(struct monst *mtmp, struct attack *mattk) monstseesu(M_SEEN_COLD); ugolemeffects(AD_COLD, tmp); tmp = 0; - } else + } else { You("are freezing to death!"); + monstunseesu(M_SEEN_COLD); + } } else tmp = 0; break; @@ -1609,9 +1685,11 @@ gulpmu(struct monst *mtmp, struct attack *mattk) monstseesu(M_SEEN_FIRE); ugolemeffects(AD_FIRE, tmp); tmp = 0; - } else + } else { You("are %s!", steamy ? "being boiled alive" : "burning to a crisp"); + monstunseesu(M_SEEN_FIRE); + } burn_away_slime(); } else tmp = 0; @@ -1680,7 +1758,7 @@ gulpmu(struct monst *mtmp, struct attack *mattk) You("get %s!", digests(mtmp->data) ? "regurgitated" : enfolds(mtmp->data) ? "released" : "expelled"); - if (Verbose(1, gulpmu) + if (flags.verbose && (digests(mtmp->data) && Slow_digestion)) pline("Obviously %s thinks you would give it indigestion.", mon_nam(mtmp)); @@ -1690,7 +1768,7 @@ gulpmu(struct monst *mtmp, struct attack *mattk) } /* monster explodes in your face */ -static int +staticfn int explmu(struct monst *mtmp, struct attack *mattk, boolean ufound) { boolean not_affected; @@ -1789,7 +1867,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"); @@ -1816,7 +1894,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); @@ -1832,8 +1910,8 @@ gazemu(struct monst *mtmp, struct attack *mattk) && polymon(PM_STONE_GOLEM, POLYMON_ALL_MSGS)) break; urgent_pline("You turn to stone..."); - gk.killer.format = KILLED_BY; - Strcpy(gk.killer.name, pmname(mtmp->data, Mgender(mtmp))); + svk.killer.format = KILLED_BY; + Strcpy(svk.killer.name, pmname(mtmp->data, Mgender(mtmp))); done(STONING); } break; @@ -1847,7 +1925,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); @@ -1864,7 +1943,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(); } @@ -1908,7 +1987,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); @@ -1916,8 +1996,12 @@ gazemu(struct monst *mtmp, struct attack *mattk) monstseesu(M_SEEN_FIRE); ugolemeffects(AD_FIRE, d(12, 6)); dmg = 0; + } else { + monstunseesu(M_SEEN_FIRE); } burn_away_slime(); + if (lev > rn2(20)) + (void) burnarmor(&gy.youmonst); if (lev > rn2(20)) { (void) destroy_items(&gy.youmonst, AD_FIRE, orig_dmg); ignite_items(gi.invent); @@ -1937,6 +2021,7 @@ gazemu(struct monst *mtmp, struct attack *mattk) fall_asleep(-rnd(10), TRUE); pline("%s gaze makes you very sleepy...", s_suffix(Monnam(mtmp))); + monstunseesu(M_SEEN_SLEEP); } } break; @@ -1963,7 +2048,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]); @@ -1975,9 +2060,18 @@ gazemu(struct monst *mtmp, struct attack *mattk) void mdamageu(struct monst *mtmp, int n) { - gc.context.botl = 1; + if (n < 0) { + impossible("mdamageu for negative damage? (%d)", n); + n = 0; + } + + disp.botl = TRUE; if (Upolyd) { u.mh -= n; + showdamage(n); + /* caller might have reduced mhmax before calling mdamageu() */ + if (u.mh > u.mhmax) + u.mh = u.mhmax; if (u.mh < 1) { /* in case rehumanize kills us for good */ format_monkiller(mtmp); @@ -1986,6 +2080,10 @@ mdamageu(struct monst *mtmp, int n) } else { n = saving_grace(n); u.uhp -= n; + showdamage(n); + /* caller might have reduced uhpmax before calling mdamageu() */ + if (u.uhp > u.uhpmax) + u.uhp = u.uhpmax; if (u.uhp < 1) done_in_by(mtmp, DIED); } @@ -1998,7 +2096,7 @@ mdamageu(struct monst *mtmp, int n) int could_seduce( struct monst *magr, struct monst *mdef, - struct attack *mattk) /* non-Null: current atk; Null: general capability */ + struct attack *mattk) /* non-Null: current atk; Null: genrl capability */ { struct permonst *pagr; boolean agrinvis, defperc; @@ -2066,12 +2164,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); @@ -2135,7 +2234,7 @@ doseduce(struct monst *mon) /* confirmation prompt when charisma is high bypassed if deaf */ if (!Deaf && rn2(20) < ACURR(A_CHA)) { (void) safe_qbuf(qbuf, "\"That ", - " looks pretty. Would you wear it for me?\"", + " looks pretty. Would you wear it for me?\"", ring, xname, simpleonames, "ring"); makeknown(RIN_ADORNMENT); SetVoice(mon, 0, 80, 0); @@ -2165,7 +2264,7 @@ doseduce(struct monst *mon) Ring_gone(uright); /* ring removal might cause loss of levitation which could drop hero onto trap that transports hero somewhere else */ - if (u.utotype || !next2u(mon->mx, mon->my)) + if (u.utotype || !m_next2u(mon)) return 1; setworn(ring, RIGHT_RING); } else if (uleft && uleft->otyp != RIN_ADORNMENT) { @@ -2173,7 +2272,7 @@ doseduce(struct monst *mon) pline("%s replaces %s with %s.", Who, yname(uleft), yname(ring)); Ring_gone(uleft); - if (u.utotype || !next2u(mon->mx, mon->my)) + if (u.utotype || !m_next2u(mon)) return 1; setworn(ring, LEFT_RING); } else @@ -2207,7 +2306,7 @@ doseduce(struct monst *mon) and changing location, so hero might not be adjacent to seducer any more (mayberem() has its own adjacency test so we don't need to check after each potential removal) */ - if (u.utotype || !next2u(mon->mx, mon->my)) + if (u.utotype || !m_next2u(mon)) return 1; if (uarm || uarmc) { @@ -2230,7 +2329,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)) @@ -2274,13 +2373,13 @@ doseduce(struct monst *mon) You("are down in the dumps."); (void) adjattrib(A_CON, -1, AA_NOMSG); exercise(A_CON, FALSE); - gc.context.botl = 1; + disp.botl = TRUE; break; case 2: Your("senses are dulled."); (void) adjattrib(A_WIS, -1, AA_NOMSG); exercise(A_WIS, FALSE); - gc.context.botl = 1; + disp.botl = TRUE; break; case 3: if (!resists_drli(&gy.youmonst) && !item_catches_drain(&gy.youmonst)) { @@ -2319,13 +2418,13 @@ doseduce(struct monst *mon) You_feel("good enough to do it again."); (void) adjattrib(A_CON, 1, AA_NOMSG); exercise(A_CON, TRUE); - gc.context.botl = 1; + disp.botl = TRUE; break; case 2: You("will always remember %s...", noit_mon_nam(mon)); (void) adjattrib(A_WIS, 1, AA_NOMSG); exercise(A_WIS, TRUE); - gc.context.botl = 1; + disp.botl = TRUE; break; case 3: pline("That was a very educational experience."); @@ -2338,7 +2437,7 @@ doseduce(struct monst *mon) if (Upolyd) u.mh = u.mhmax; exercise(A_STR, TRUE); - gc.context.botl = 1; + disp.botl = TRUE; break; } } @@ -2356,7 +2455,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); @@ -2376,10 +2476,10 @@ 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); - gc.context.botl = 1; + disp.botl = TRUE; } } if (!rn2(25) || (flags.orientation == ORIENT_BISEXUAL && !rn2(25))) @@ -2390,7 +2490,7 @@ doseduce(struct monst *mon) } /* 'mon' tries to remove a piece of hero's armor */ -static void +staticfn void mayberem(struct monst *mon, const char *seducer, /* only used for alternate message */ struct obj *obj, const char *str) @@ -2401,14 +2501,14 @@ mayberem(struct monst *mon, return; /* removal of a previous item might have sent the hero elsewhere (loss of levitation that leads to landing on a transport trap) */ - if (u.utotype || !next2u(mon->mx, mon->my)) + if (u.utotype || !m_next2u(mon)) return; /* being deaf overrides confirmation prompt for high charisma */ if (Deaf) { pline("%s takes off your %s.", seducer, str); } else if (rn2(20) < ACURR(A_CHA)) { - SetVoice(mon, 0, 80, 0); /* y_n a.k.a. yn_function is set up for this */ + SetVoice(mon, 0, 80, 0); /* y_n aka yn_function is set up for this */ Sprintf(qbuf, "\"Shall I remove your %s, %s?\"", str, (!rn2(2) ? "lover" : !rn2(2) ? "dear" : "sweetheart")); if (y_n(qbuf) == 'n') @@ -2436,6 +2536,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 @@ -2443,7 +2612,7 @@ mayberem(struct monst *mon, * to know whether hero reverted in order to decide whether passive * damage applies. */ -static int +staticfn int passiveum( struct permonst *olduasmon, struct monst *mtmp, @@ -2475,13 +2644,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 @@ -2490,7 +2659,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), @@ -2508,7 +2677,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)) @@ -2537,11 +2706,11 @@ passiveum( case AD_PHYS: if (oldu_mattk->aatyp == AT_BOOM) { You("explode!"); - Strcpy(gk.killer.name, "detonated"); - gk.killer.format = NO_KILLER_PREFIX; + Strcpy(svk.killer.name, "detonated"); + svk.killer.format = NO_KILLER_PREFIX; /* KMH, balance patch -- this is okay with unchanging */ rehumanize(); - goto assess_dmg; + return assess_dmg(mtmp, tmp); } break; case AD_PLYS: /* Floating eye */ @@ -2560,13 +2729,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; } @@ -2574,22 +2744,22 @@ 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)); - u.mh += tmp / 2; + 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; - if (u.mhmax > ((gy.youmonst.data->mlevel + 1) * 8)) + if (u.mhmax > (((int) gy.youmonst.data->mlevel + 1) * 8)) (void) split_mon(&gy.youmonst, mtmp); break; 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; @@ -2597,22 +2767,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; case AD_DISE: /* black mold */ /* monster illness not yet implemented */ @@ -2625,15 +2796,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 * @@ -2644,20 +2807,20 @@ cloneu(void) if (u.mh <= 1) return (struct monst *) 0; - if (gm.mvitals[mndx].mvflags & G_EXTINCT) + if (svm.mvitals[mndx].mvflags & G_EXTINCT) return (struct monst *) 0; mon = makemon(gy.youmonst.data, u.ux, u.uy, NO_MINVENT | MM_EDOG | MM_NOMSG); if (!mon) return NULL; mon->mcloned = 1; - mon = christen_monst(mon, gp.plname); + mon = christen_monst(mon, svp.plname); initedog(mon); mon->m_lev = gy.youmonst.data->mlevel; mon->mhpmax = u.mhmax; mon->mhp = u.mh / 2; u.mh -= mon->mhp; - gc.context.botl = 1; + disp.botl = TRUE; return mon; } @@ -2752,7 +2915,7 @@ piercer_hit(struct monst *magr, struct monst *mdef) * Give it some bonus damage. */ dmg += rnd(6); } - else if (is_hard(helm)) { + else if (hard_helmet(helm)) { pline("%s partially diverts the blow.", Yname2(helm)); dmg = (dmg + 1) / 2; } diff --git a/src/minion.c b/src/minion.c index 4e8d4fccf8..51db69aba1 100644 --- a/src/minion.c +++ b/src/minion.c @@ -92,6 +92,9 @@ msummon(struct monst *mon) dtype = (!rn2(50)) ? dprince(atyp) : (!rn2(20)) ? dlord(atyp) : ndemon(atyp); cnt = !rn2(4) ? rn1(3, 2) : rn1(2, 1); + } else if (ptr == &mons[PM_BONE_DEVIL]) { + dtype = PM_SKELETON; + cnt = 1; } else if (is_ndemon(ptr)) { dtype = (!rn2(20)) ? dlord(atyp) : (!rn2(6)) ? ndemon(atyp) : monsndx(ptr); @@ -107,7 +110,7 @@ msummon(struct monst *mon) if (!rn2(6)) { switch (atyp) { /* see summon_minion */ case A_NEUTRAL: - dtype = elementals[rn2(SIZE(elementals))]; + dtype = ROLL_FROM(elementals); break; case A_CHAOTIC: case A_NONE: @@ -124,7 +127,7 @@ msummon(struct monst *mon) /* put overrides for specific summoners here, but note they have to check * for dtype being NON_PM before using it */ if (ptr == &mons[PM_DISPATER] - && ((gm.mvitals[PM_PIT_FIEND].mvflags & G_GONE) == 0) + && ((svm.mvitals[PM_PIT_FIEND].mvflags & G_GONE) == 0) && (dtype == NON_PM || is_ndemon(&mons[dtype])) && !rn2(2)) { /* Dispater favors pit fiends, despite them being chaotic */ dtype = PM_PIT_FIEND; @@ -140,7 +143,7 @@ msummon(struct monst *mon) * If this daemon is unique and being re-summoned (the only way we * could get this far with an extinct dtype), try another. */ - if ((gm.mvitals[dtype].mvflags & G_GONE) != 0) { + if ((svm.mvitals[dtype].mvflags & G_GONE) != 0) { dtype = ndemon(atyp); if (dtype == NON_PM) return 0; @@ -204,7 +207,7 @@ msummon(struct monst *mon) void summon_minion(aligntyp alignment, boolean talk) { - register struct monst *mon; + struct monst *mon; int mnum; switch ((int) alignment) { @@ -212,7 +215,7 @@ summon_minion(aligntyp alignment, boolean talk) mnum = lminion(); break; case A_NEUTRAL: - mnum = elementals[rn2(SIZE(elementals))]; + mnum = ROLL_FROM(elementals); break; case A_CHAOTIC: case A_NONE: @@ -287,7 +290,7 @@ boss_entrance(struct monst* mtmp) return FALSE; } - if (gm.mvitals[mondx].died > 0) { + if (svm.mvitals[mondx].died > 0) { /* Never print entrance message if the player already killed it. */ return FALSE; } @@ -380,7 +383,7 @@ demon_value(struct obj *obj) /* returns 1 if it won't attack. */ int -demon_talk(register struct monst *mtmp) +demon_talk(struct monst *mtmp) { long cash, demand, offer = 0L; struct obj *otmp, *shiny = (struct obj *) 0; @@ -464,7 +467,7 @@ demon_talk(register struct monst *mtmp) } if (shiny->owornmask) { - remove_outer_gear(shiny); + remove_outer_gear(mtmp, shiny); remove_worn_item(shiny, TRUE); } items_given++; @@ -585,7 +588,7 @@ bribe(struct monst *mtmp) You("give %s %ld %s.", mon_nam(mtmp), offer, currency(offer)); } (void) money2mon(mtmp, offer); - gc.context.botl = 1; + disp.botl = TRUE; return offer; } @@ -596,7 +599,7 @@ dprince(aligntyp atyp) for (tryct = !In_endgame(&u.uz) ? 20 : 0; tryct > 0; --tryct) { pm = rn1(PM_DEMOGORGON + 1 - PM_ORCUS, PM_ORCUS); - if (!(gm.mvitals[pm].mvflags & G_GONE) + if (!(svm.mvitals[pm].mvflags & G_GONE) && (atyp == A_NONE || sgn(mons[pm].maligntyp) == sgn(atyp))) return pm; } @@ -615,7 +618,7 @@ dlord(aligntyp atyp) * G_EXTINCT set. Instead, check the born counter (which also works fine * for Yeenoghu or any other archfiend since the outcome is the same - * if they have already been generated, they can't be summoned. */ - if (!(gm.mvitals[pm].born) + if (!(svm.mvitals[pm].born) && (atyp == A_NONE || sgn(mons[pm].maligntyp) == sgn(atyp))) return pm; } @@ -626,7 +629,7 @@ dlord(aligntyp atyp) int llord(void) { - if (!(gm.mvitals[PM_ARCHON].mvflags & G_GONE)) + if (!(svm.mvitals[PM_ARCHON].mvflags & G_GONE)) return PM_ARCHON; return lminion(); /* approximate */ @@ -672,7 +675,8 @@ ndemon(aligntyp atyp) /* A_NONE is used for 'any alignment' */ /* guardian angel has been affected by conflict so is abandoning hero */ void -lose_guardian_angel(struct monst *mon) /* if null, angel hasn't been created yet */ +lose_guardian_angel( + struct monst *mon) /* if Null, angel hasn't been created yet */ { coord mm; int i; @@ -777,10 +781,10 @@ geryon_bonus(void) static int bonus = 0; struct monst *mtmp; - if (gm.moves == lastturncheck) + if (svm.moves == lastturncheck) return bonus; /* already calculated this turn */ - lastturncheck = gm.moves; + lastturncheck = svm.moves; bonus = 0; if (Is_geryon_level(&u.uz)) { for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { @@ -830,11 +834,11 @@ angry_geryon(void) * born = X, died = X (repeated resurrecting ending with a kill) */ if (!geryon && (lookup_fiend(PM_GERYON)->num_in_dgn == 0) - && (gm.mvitals[PM_GERYON].born == 0 - || gm.mvitals[PM_GERYON].born > gm.mvitals[PM_GERYON].died)) { + && (svm.mvitals[PM_GERYON].born == 0 + || svm.mvitals[PM_GERYON].born > svm.mvitals[PM_GERYON].died)) { /* Geryon hasn't generated yet OR has generated and been bribed * away, in which case he comes back mad; generate him now */ - boolean never_generated = (gm.mvitals[PM_GERYON].born == 0); + boolean never_generated = (svm.mvitals[PM_GERYON].born == 0); geryon = makemon(&mons[PM_GERYON], u.ux, u.uy, MM_NOMSG | MM_ADJACENTOK); if (!geryon) { @@ -882,7 +886,7 @@ init_archfiends(void) * given archfiend. */ struct fiend_info * lookup_fiend(int mndx) { - return &gc.context.archfiends[mndx - FIRST_ARCHFIEND]; + return &svc.context.archfiends[mndx - FIRST_ARCHFIEND]; } /* Should the given archfiend be giving the player a bad effect? @@ -907,7 +911,7 @@ fiend_adversity(int mndx) else if (fnd->num_in_dgn > 0) /* they are alive and kicking somewhere in the dungeon */ return TRUE; - else if (gm.mvitals[mndx].born == 0) + else if (svm.mvitals[mndx].born == 0) /* they were never encountered, thus not dealt with */ return TRUE; diff --git a/src/mklev.c b/src/mklev.c index c4cfdc578b..b1f39d309e 100644 --- a/src/mklev.c +++ b/src/mklev.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mklev.c $NHDT-Date: 1648066813 2022/03/23 20:20:13 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.121 $ */ +/* 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. */ @@ -9,34 +9,42 @@ /* croom->lx etc are schar (width <= int), so % arith ensures that */ /* conversion of result to int is reasonable */ -static int mkmonst_in_room(struct mkroom *); -static boolean generate_stairs_room_good(struct mkroom *, int); -static struct mkroom *generate_stairs_find_room(void); -static void generate_stairs(void); -static boolean find_okay_roompos(struct mkroom *, coord *); -static void makevtele(void); -void clear_level_structures(void); -static void fill_ordinary_room(struct mkroom *); -static void makelevel(void); -static boolean bydoor(coordxy, coordxy); -static void mktrap_victim(struct trap *); -static struct mkroom *find_branch_room(coord *); -static boolean cardinal_nextto_room(struct mkroom *, coordxy, coordxy); -static boolean place_niche(struct mkroom *, coordxy *, coordxy *, coordxy *); -static void makeniche(int); -static void make_niches(void); -static int QSORTCALLBACK mkroom_cmp(const genericptr, const genericptr); -static void dosdoor(coordxy, coordxy, struct mkroom *, int); -static void join(int, int, boolean); -static void alloc_doors(void); -static void do_room_or_subroom(struct mkroom *, +staticfn int mkmonst_in_room(struct mkroom *); +staticfn boolean generate_stairs_room_good(struct mkroom *, int); +staticfn struct mkroom *generate_stairs_find_room(void); +staticfn void generate_stairs(void); +staticfn void mkfount(struct mkroom *); +staticfn boolean find_okay_roompos(struct mkroom *, coord *) NONNULLARG12; +staticfn void mksink(struct mkroom *); +staticfn void mkaltar(struct mkroom *); +staticfn void mkgrave(struct mkroom *); +staticfn void mkinvpos(coordxy, coordxy, int); +staticfn int mkinvk_check_wall(coordxy x, coordxy y); +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 void makelevel(void); +staticfn boolean bydoor(coordxy, coordxy); +staticfn void mktrap_victim(struct trap *); +staticfn int traptype_rnd(unsigned); +staticfn int traptype_roguelvl(void); +staticfn struct mkroom *find_branch_room(coord *) NONNULLARG1; +staticfn struct mkroom *pos_to_room(coordxy, coordxy); +staticfn boolean cardinal_nextto_room(struct mkroom *, coordxy, coordxy); +staticfn boolean place_niche(struct mkroom *, int *, coordxy *, coordxy *); +staticfn void makeniche(int); +staticfn void make_niches(void); +staticfn int QSORTCALLBACK mkroom_cmp(const genericptr, const genericptr); +staticfn void dosdoor(coordxy, coordxy, struct mkroom *, int); +staticfn void join(int, int, boolean); +staticfn void alloc_doors(void); +staticfn void do_room_or_subroom(struct mkroom *, coordxy, coordxy, coordxy, coordxy, boolean, schar, boolean, boolean); -static void makerooms(void); -static boolean door_into_nonjoined(coordxy, coordxy); -static boolean finddpos(coord *, coordxy, coordxy, coordxy, coordxy); -static void mkinvpos(coordxy, coordxy, int); -static void mk_knox_portal(coordxy, coordxy); +staticfn void makerooms(void); +staticfn boolean door_into_nonjoined(coordxy, coordxy); +staticfn boolean finddpos(coord *, coordxy, coordxy, coordxy, coordxy); #define create_vault() create_room(-1, -1, 2, 2, -1, -1, VAULT, TRUE) #define init_vault() gv.vault_x = -1 @@ -46,10 +54,10 @@ static void mk_knox_portal(coordxy, coordxy); * qsort. * Args must be (const genericptr) so that qsort will always be happy. */ -static int QSORTCALLBACK +staticfn int QSORTCALLBACK mkroom_cmp(const genericptr vx, const genericptr vy) { - register const struct mkroom *x, *y; + const struct mkroom *x, *y; x = (const struct mkroom *) vx; y = (const struct mkroom *) vy; @@ -59,12 +67,12 @@ mkroom_cmp(const genericptr vx, const genericptr vy) } /* 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. + * 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. */ -static boolean +staticfn boolean door_into_nonjoined(coordxy x, coordxy y) { coordxy tx, ty, i; @@ -72,12 +80,12 @@ door_into_nonjoined(coordxy x, coordxy y) 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)) + if (!isok(tx, ty) || IS_OBSTRUCTED(levl[tx][ty].typ)) continue; /* Is this connecting to a room that doesn't want joining? */ if (levl[tx][ty].roomno >= ROOMOFFSET - && !gr.rooms[levl[tx][ty].roomno - ROOMOFFSET].needjoining) { + && !svr.rooms[levl[tx][ty].roomno - ROOMOFFSET].needjoining) { return TRUE; } } @@ -90,10 +98,13 @@ door_into_nonjoined(coordxy x, coordxy y) * If it can't find any valid places it'll just default to an * existing door. */ -static boolean -finddpos(coord *cc, coordxy xl, coordxy yl, coordxy xh, coordxy yh) +staticfn boolean +finddpos( + coord *cc, + coordxy xl, coordxy yl, + coordxy xh, coordxy yh) { - register coordxy x, y; + coordxy x, y; x = rn1(xh - xl + 1, xl); y = rn1(yh - yl + 1, yl); @@ -110,11 +121,12 @@ finddpos(coord *cc, coordxy xl, coordxy yl, coordxy xh, coordxy yh) 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. */ impossible("couldn't find an okdoor pos within (%d, %d, %d, %d)!", xl, yl, xh, yh); - x = xl; - y = yh; + cc->x = xl; + cc->y = yh; return FALSE; gotit: cc->x = x; @@ -128,13 +140,13 @@ void sort_rooms(void) { coordxy x, y; - unsigned i, ri[MAXNROFROOMS + 1], n = (unsigned) gn.nroom; + unsigned i, ri[MAXNROFROOMS + 1] = { 0U }, n = (unsigned) svn.nroom; - qsort((genericptr_t) gr.rooms, n, sizeof (struct mkroom), mkroom_cmp); + qsort((genericptr_t) svr.rooms, n, sizeof (struct mkroom), mkroom_cmp); /* Update the roomnos on the map */ for (i = 0; i < n; i++) - ri[gr.rooms[i].roomnoidx] = i; + ri[svr.rooms[i].roomnoidx] = i; for (x = 1; x < COLNO; x++) for (y = 0; y < ROWNO; y++) { @@ -159,8 +171,8 @@ sort_rooms(void) * skip this step. * is_room: Whether this room is a full room. FALSE if it's a subroom. * Only relevant to wallification and if special = FALSE. */ -static void -do_room_or_subroom(register struct mkroom *croom, +staticfn void +do_room_or_subroom(struct mkroom *croom, coordxy lowx, coordxy lowy, coordxy hix, coordxy hiy, boolean lit, schar rtype, boolean special, boolean is_room) { @@ -188,7 +200,7 @@ do_room_or_subroom(register struct mkroom *croom, } else croom->rlit = 0; - croom->roomnoidx = (croom - gr.rooms); + croom->roomnoidx = (croom - svr.rooms); croom->lx = lowx; croom->hx = hix; croom->ly = lowy; @@ -237,17 +249,17 @@ do_room_or_subroom(register struct mkroom *croom, * to TRUE. */ 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) { - register struct mkroom *croom; + struct mkroom *croom; - croom = &gr.rooms[gn.nroom]; + croom = &svr.rooms[svn.nroom]; do_room_or_subroom(croom, lowx, lowy, hix, hiy, lit, rtype, special, (boolean) TRUE); croom++; croom->hx = -1; - gn.nroom++; + svn.nroom++; } /* Adds a new subroom to the map as part of the given room. @@ -255,10 +267,12 @@ add_room(int lowx, int lowy, int hix, int hiy, * is_room hardcoded to FALSE. */ 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) { - register struct mkroom *croom; + struct mkroom *croom; croom = &gs.subrooms[gn.nsubroom]; do_room_or_subroom(croom, lowx, lowy, hix, hiy, lit, rtype, special, @@ -270,12 +284,19 @@ add_subroom(struct mkroom *proom, int lowx, int lowy, int hix, int hiy, } void -free_luathemes(boolean keependgame) /* F: done, T: discarding main dungeon */ +free_luathemes(enum lua_theme_group theme_group) { int i; - for (i = 0; i < gn.n_dgns; ++i) { - if (keependgame && i == astral_level.dnum) + /* + * Release which group(s)? + * tut_themes => leaving tutorial, free tutorial themes only; + * most_themes => entering endgame, free non-endgame themes; + * all_themes => end of game, free all themes. + */ + for (i = 0; i < svn.n_dgns; ++i) { + if ((theme_group == tut_themes && i != tutorial_dnum) + || (theme_group == most_themes && i == astral_level.dnum)) continue; if (gl.luathemes[i]) { nhl_done((lua_State *) gl.luathemes[i]); @@ -286,16 +307,16 @@ free_luathemes(boolean keependgame) /* F: done, T: discarding main dungeon */ /* Repeatedly create rooms and place them on the map until we can't create any * more. */ -static void +staticfn void makerooms(void) { boolean tried_vault = FALSE; int themeroom_tries = 0; char *fname; - nhl_sandbox_info sbi = {NHL_SB_SAFE, 0, 0, 0}; + nhl_sandbox_info sbi = {NHL_SB_SAFE, 1*1024*1024, 0, 1*1024*1024}; lua_State *themes = (lua_State *) gl.luathemes[u.uz.dnum]; - if (!themes && *(fname = gd.dungeons[u.uz.dnum].themerms)) { + if (!themes && *(fname = svd.dungeons[u.uz.dnum].themerms)) { if ((themes = nhl_init(&sbi)) != 0) { if (!nhl_loadlua(themes, fname)) { /* loading lua failed, don't use themed rooms */ @@ -309,7 +330,7 @@ makerooms(void) } } if (!themes) /* don't try again when making next level */ - *fname = '\0'; /* gd.dungeons[u.uz.dnum].themerms */ + *fname = '\0'; /* svd.dungeons[u.uz.dnum].themerms */ } if (themes) { @@ -317,40 +338,36 @@ makerooms(void) iflags.in_lua = gi.in_mk_themerooms = TRUE; gt.themeroom_failed = FALSE; lua_getglobal(themes, "pre_themerooms_generate"); - if ( nhl_pcall(themes, 0, 0)){ - impossible("Lua error: %s", lua_tostring(themes, -1)); - } + nhl_pcall_handle(themes, 0, 0, "makerooms-1", NHLpa_impossible); iflags.in_lua = gi.in_mk_themerooms = FALSE; } /* make rooms until satisfied */ /* rnd_rect() will returns 0 if no more rects are available... */ - while (gn.nroom < (MAXNROFROOMS-1) && rnd_rect()) { + while (svn.nroom < (MAXNROFROOMS - 1) && rnd_rect()) { /* If a certain number of rooms have already been created, and we have * not yet tried to make a vault, with 50% probability, try to create * one. */ - if (gn.nroom >= (MAXNROFROOMS / 6) && rn2(2) && !tried_vault) { + if (svn.nroom >= (MAXNROFROOMS / 6) && rn2(2) && !tried_vault) { tried_vault = TRUE; if (create_vault()) { /* This won't actually create the room and edit the terrain * with add_room. It'll just set the lx and ly of rooms[nroom] * to represent its location. */ - gv.vault_x = gr.rooms[gn.nroom].lx; - gv.vault_y = gr.rooms[gn.nroom].ly; - gr.rooms[gn.nroom].hx = -1; + gv.vault_x = svr.rooms[svn.nroom].lx; + gv.vault_y = svr.rooms[svn.nroom].ly; + svr.rooms[svn.nroom].hx = -1; } } else { if (themes) { iflags.in_lua = gi.in_mk_themerooms = TRUE; gt.themeroom_failed = FALSE; lua_getglobal(themes, "themerooms_generate"); - if (nhl_pcall(themes, 0, 0)) { - impossible("Lua error: %s", lua_tostring(themes, -1)); - } + nhl_pcall_handle(themes, 0, 0, "makerooms-2", NHLpa_panic); iflags.in_lua = gi.in_mk_themerooms = FALSE; if (gt.themeroom_failed && ((themeroom_tries++ > 10) - || (gn.nroom >= (MAXNROFROOMS / 6)))) + || (svn.nroom >= (MAXNROFROOMS / 6)))) break; } else { /* Try to create another random room. If it can't find anywhere for @@ -366,14 +383,8 @@ makerooms(void) iflags.in_lua = gi.in_mk_themerooms = TRUE; gt.themeroom_failed = FALSE; lua_getglobal(themes, "post_themerooms_generate"); - if ( nhl_pcall(themes, 0, 0)){ - impossible("Lua error: %s", lua_tostring(themes, -1)); - } + nhl_pcall_handle(themes, 0, 0, "makerooms-3", NHLpa_panic); iflags.in_lua = gi.in_mk_themerooms = FALSE; - - wallification(1, 0, COLNO - 1, ROWNO - 1); - free(gc.coder); - gc.coder = NULL; } } @@ -384,16 +395,16 @@ makerooms(void) * in which case it will use ROOM. * Afterwards, the smeq values of a and b will be set equal to each other. * Should this return boolean (success or failure)? */ -static void -join(register int a, register int b, boolean nxcor) +staticfn void +join(int a, int b, boolean nxcor) { coord cc, tt, org, dest; - register coordxy tx, ty, xx, yy; - register struct mkroom *croom, *troom; - register int dx, dy; + coordxy tx, ty, xx, yy; + struct mkroom *croom, *troom; + int dx, dy; - croom = &gr.rooms[a]; - troom = &gr.rooms[b]; + croom = &svr.rooms[a]; + troom = &svr.rooms[b]; if (!croom->needjoining || !troom->needjoining) return; @@ -475,7 +486,7 @@ join(register int a, register int b, boolean nxcor) dest.y = ty; if (!dig_corridor(&org, &dest, nxcor, - gl.level.flags.arboreal ? ROOM : CORR, STONE)) + svl.level.flags.arboreal ? ROOM : CORR, STONE)) return; /* We succeeded in digging the corridor. @@ -494,7 +505,7 @@ join(register int a, register int b, boolean nxcor) gs.smeq[a] = gs.smeq[b]; } -/* Generate corridors connecting all the rooms on the level. */ +/* create random corridors between rooms */ void makecorridors(void) { @@ -513,25 +524,23 @@ makecorridors(void) * * It's rather easy to see all the rooms joined in order from left to right * across the level if you know what you're looking for. */ - for (a = 0; a < gn.nroom - 1; a++) { + for (a = 0; a < svn.nroom - 1; a++) { join(a, a + 1, FALSE); if (!rn2(50)) break; /* allow some randomness */ } - /* Connect each room to the room two rooms after it in rooms, if and only * if they do not have the same smeq already. */ - for (a = 0; a < gn.nroom - 2; a++) + for (a = 0; a < svn.nroom - 2; a++) if (gs.smeq[a] != gs.smeq[a + 2]) join(a, a + 2, FALSE); - /* Connect any remaining rooms with different smeqs. * The "any" variable is an optimization; if on a given loop no different * smeqs were found from the current room, there's nothing more to be done. * */ - for (a = 0; any && a < gn.nroom; a++) { + for (a = 0; any && a < svn.nroom; a++) { any = FALSE; - for (b = 0; b < gn.nroom; b++) + for (b = 0; b < svn.nroom; b++) if (gs.smeq[a] != gs.smeq[b]) { join(a, b, FALSE); any = TRUE; @@ -542,31 +551,32 @@ makecorridors(void) /* Attempt to draw a few more corridors between rooms, but don't draw the * corridor if it starts on an already carved out corridor space. Possibly * also don't create the doors.*/ - if (gn.nroom > 2) - for (i = rn2(gn.nroom) + 4; i; i--) { - a = rn2(gn.nroom); - b = rn2(gn.nroom - 2); + if (svn.nroom > 2) + for (i = rn2(svn.nroom) + 4; i; i--) { + a = rn2(svn.nroom); + b = rn2(svn.nroom - 2); if (b >= a) b += 2; join(a, b, TRUE); } } -/* (re)allocate space for gd.doors array */ -static void +/* (re)allocate space for svd.doors array */ +staticfn void alloc_doors(void) { - if (!gd.doors || gd.doorindex >= gd.doors_alloc) { - int c = gd.doors_alloc + DOORINC; - coord *doortmp = (coord *) alloc(c * sizeof(coord)); - - (void) memset((genericptr_t) doortmp, 0, c * sizeof(coord)); - if (gd.doors) { - (void) memcpy(doortmp, gd.doors, gd.doors_alloc * sizeof(coord)); - free(gd.doors); + if (!svd.doors || gd.doorindex >= svd.doors_alloc) { + int c = svd.doors_alloc + DOORINC; + coord *doortmp = (coord *) alloc(c * sizeof (coord)); + + (void) memset((genericptr_t) doortmp, 0, c * sizeof (coord)); + if (svd.doors) { + (void) memcpy(doortmp, svd.doors, + svd.doors_alloc * sizeof (coord)); + free(svd.doors); } - gd.doors = doortmp; - gd.doors_alloc = c; + svd.doors = doortmp; + svd.doors_alloc = c; } } @@ -575,10 +585,10 @@ alloc_doors(void) * x and y are the coordinates of the door, and aroom is the room which is * getting the door. */ void -add_door(coordxy x, coordxy y, register struct mkroom *aroom) +add_door(coordxy x, coordxy y, struct mkroom *aroom) { - register struct mkroom *broom; - register int tmp; + struct mkroom *broom; + int tmp; int i; alloc_doors(); @@ -587,7 +597,7 @@ add_door(coordxy x, coordxy y, register struct mkroom *aroom) if (aroom->doorct) { for (i = 0; i < aroom->doorct; i++) { tmp = aroom->fdoor + i; - if (gd.doors[tmp].x == x && gd.doors[tmp].y == y) + if (svd.doors[tmp].x == x && svd.doors[tmp].y == y) return; } } @@ -602,13 +612,13 @@ add_door(coordxy x, coordxy y, register struct mkroom *aroom) /* If this room did already have doors, move all the other doors up in * position by 1. */ for (tmp = gd.doorindex; tmp > aroom->fdoor; tmp--) - gd.doors[tmp] = gd.doors[tmp - 1]; + svd.doors[tmp] = svd.doors[tmp - 1]; /* If this room was not the last room on the doors array, increment fdoor * for any rooms after it (because aroom's will be eating up another index) */ - for (i = 0; i < gn.nroom; i++) { - broom = &gr.rooms[i]; + for (i = 0; i < svn.nroom; i++) { + broom = &svr.rooms[i]; if (broom != aroom && broom->doorct && broom->fdoor >= aroom->fdoor) broom->fdoor++; } @@ -622,8 +632,8 @@ add_door(coordxy x, coordxy y, register struct mkroom *aroom) /* finally, increment doorindex because we have one more door now, and * aroom's first door becomes this one. */ gd.doorindex++; - gd.doors[aroom->fdoor].x = x; - gd.doors[aroom->fdoor].y = y; + svd.doors[aroom->fdoor].x = x; + svd.doors[aroom->fdoor].y = y; } /* Generate the door mask for a random door. Contains the random probabilities @@ -643,7 +653,7 @@ random_door_mask(int typ, boolean shdoor) } /* is it a locked door, closed, or a doorway? */ - if (!rn2(3) || typ == SDOOR || gl.level.flags.is_maze_lev) { + if (!rn2(3) || typ == SDOOR || svl.level.flags.is_maze_lev) { /* 1/3 of random doorways have a physical door, unless it's a maze level * in which case 100% of random doorways do, for some reason. */ if (rn2(5) || typ == SDOOR) { @@ -728,8 +738,8 @@ clear_nonsense_doortraps(coordxy x, coordxy y) * never generate trapped. (They can be locked, though, in which case the shop * becomes closed for inventory.) Secret doors always generate closed or locked. */ -static void -dosdoor(register coordxy x, register coordxy y, struct mkroom *aroom, int type) +staticfn void +dosdoor(coordxy x, coordxy y, struct mkroom *aroom, int type) { struct rm* lev = &levl[x][y]; @@ -743,9 +753,9 @@ dosdoor(register coordxy x, register coordxy y, struct mkroom *aroom, int type) struct monst *mtmp; if (level_difficulty() >= 9 && !rn2(7) && type != SDOOR - && !((gm.mvitals[PM_SMALL_MIMIC].mvflags & G_GONE) - && (gm.mvitals[PM_LARGE_MIMIC].mvflags & G_GONE) - && (gm.mvitals[PM_GIANT_MIMIC].mvflags & G_GONE))) { + && !((svm.mvitals[PM_SMALL_MIMIC].mvflags & G_GONE) + && (svm.mvitals[PM_LARGE_MIMIC].mvflags & G_GONE) + && (svm.mvitals[PM_GIANT_MIMIC].mvflags & G_GONE))) { /* make a mimic instead */ set_door_trap(lev, FALSE); set_doorstate(lev, D_NODOOR); @@ -766,10 +776,10 @@ dosdoor(register coordxy x, register coordxy y, struct mkroom *aroom, int type) */ /* is x,y location such that NEWS direction from it is inside aroom, excluding subrooms */ -static boolean +staticfn boolean cardinal_nextto_room(struct mkroom *aroom, coordxy x, coordxy y) { - int rmno = (int) ((aroom - gr.rooms) + ROOMOFFSET); + int rmno = (int) ((aroom - svr.rooms) + ROOMOFFSET); if (isok(x - 1, y) && !levl[x - 1][y].edge && (int) levl[x - 1][y].roomno == rmno) @@ -786,8 +796,11 @@ cardinal_nextto_room(struct mkroom *aroom, coordxy x, coordxy y) return FALSE; } -static boolean -place_niche(register struct mkroom *aroom, coordxy *dy, coordxy *xx, coordxy *yy) +staticfn boolean +place_niche( + struct mkroom *aroom, + int *dy, + coordxy *xx, coordxy *yy) { coord dd; @@ -798,11 +811,13 @@ place_niche(register struct mkroom *aroom, coordxy *dy, coordxy *xx, coordxy *yy * Look for a suitable spot on one of these walls to place a niche. */ if (rn2(2)) { *dy = 1; - if (!finddpos(&dd, aroom->lx, aroom->hy + 1, aroom->hx, aroom->hy + 1)) + if (!finddpos(&dd, aroom->lx, aroom->hy + 1, + aroom->hx, aroom->hy + 1)) return FALSE; } else { *dy = -1; - if (!finddpos(&dd, aroom->lx, aroom->ly - 1, aroom->hx, aroom->ly - 1)) + if (!finddpos(&dd, aroom->lx, aroom->ly - 1, + aroom->hx, aroom->ly - 1)) return FALSE; } *xx = dd.x; @@ -834,17 +849,17 @@ static NEARDATA const char *trap_engravings[TRAPNUM] = { /* Actually create a niche/closet, on a random room. Place a trap on it if * trap_type != NO_TRAP. */ -static void +staticfn void makeniche(int trap_type) { - register struct mkroom *aroom; + struct mkroom *aroom; struct rm *rm; - int vct = 8; /* number of attempts */ - coordxy dy, xx, yy; + int dy, vct = 8; /* number of attempts */ + coordxy xx, yy; struct trap *ttmp; while (vct--) { - aroom = &gr.rooms[rn2(gn.nroom)]; + aroom = &svr.rooms[rn2(svn.nroom)]; if (aroom->rtype != OROOM) /* don't place niches in special rooms */ continue; @@ -899,7 +914,7 @@ makeniche(int trap_type) * If an inaccessible niche is generated on a no-tele level, the * player shouldn't be able to get into it without some way of * getting back out... */ - if (!gl.level.flags.noteleport) + if (!svl.level.flags.noteleport) (void) mksobj_at(SCR_TELEPORTATION, xx, yy + dy, TRUE, FALSE); if (!rn2(3)) @@ -907,19 +922,19 @@ makeniche(int trap_type) } } /* mark as niche (for thiefstones) */ - rm->is_niche = TRUE; + rm->is_niche = 1; return; } } /* Try to create several random niches across an entire level. * Does NOT include the niche for a vault teleporter, if one exists. */ -static void +staticfn void make_niches(void) { /* This should really be nroom / 2... */ - int ct = rnd((gn.nroom >> 1) + 1), dep = depth(&u.uz); - boolean ltptr = (!gl.level.flags.noteleport && dep > 15), + int ct = rnd((svn.nroom >> 1) + 1), dep = depth(&u.uz); + boolean ltptr = (!svl.level.flags.noteleport && dep > 15), vamp = (dep > 5 && dep < 25); while (ct--) { @@ -942,7 +957,7 @@ make_niches(void) * go to a vault; this may become problematic if the player ever gains the * ability to make teleport traps... */ -static void +staticfn void makevtele(void) { makeniche(TELEP_TRAP); @@ -955,38 +970,38 @@ rand_roomtype(void) int u_depth = depth(&u.uz); /* minimum number of rooms needed to allow a random special room */ int room_threshold = Is_branchlev(&u.uz) ? 4 : 3; - if (gl.level.flags.has_vault) + if (svl.level.flags.has_vault) room_threshold++; if (!Inhell) { if (u_depth > 1 && u_depth < depth(&medusa_level) - && gn.nroom >= room_threshold && rn2(u_depth) < 3) { + && svn.nroom >= room_threshold && rn2(u_depth) < 3) { /* random shop */ return SHOPBASE; } else if (u_depth > 4 && !rn2(6)) return COURT; else if (u_depth > 5 && !rn2(8) - && !(gm.mvitals[PM_LEPRECHAUN].mvflags & G_GONE)) + && !(svm.mvitals[PM_LEPRECHAUN].mvflags & G_GONE)) return LEPREHALL; else if (u_depth > 6 && !rn2(7)) return ZOO; else if (u_depth > 8 && !rn2(5)) return TEMPLE; else if (u_depth > 9 && !rn2(5) - && !(gm.mvitals[PM_KILLER_BEE].mvflags & G_GONE)) + && !(svm.mvitals[PM_KILLER_BEE].mvflags & G_GONE)) return BEEHIVE; else if (u_depth > 11 && !rn2(6)) return MORGUE; else if (u_depth > 12 && !rn2(8) && antholemon()) return ANTHOLE; else if (u_depth > 14 && !rn2(4) - && !(gm.mvitals[PM_SOLDIER].mvflags & G_GONE)) + && !(svm.mvitals[PM_SOLDIER].mvflags & G_GONE)) return BARRACKS; else if (u_depth > 15 && !rn2(6)) return SWAMP; else if (u_depth > 16 && !rn2(8) - && !(gm.mvitals[PM_COCKATRICE].mvflags & G_GONE)) + && !(svm.mvitals[PM_COCKATRICE].mvflags & G_GONE)) return COCKNEST; else return OROOM; @@ -1014,6 +1029,24 @@ rand_roomtype(void) } } +/* count the tracked features (sinks, fountains) present on the level */ +void +count_level_features(void) +{ + coordxy x, y; + + svl.level.flags.nfountains = svl.level.flags.nsinks = 0; + for (y = 0; y < ROWNO; y++) + for (x = 1; x < COLNO; x++) { + int typ = levl[x][y].typ; + + if (typ == FOUNTAIN) + svl.level.flags.nfountains++; + else if (typ == SINK) + svl.level.flags.nsinks++; + } +} + /* clear out various globals that keep information on the current level. * some of this is only necessary for some types of levels (maze, normal, * special) but it's easier to put it all in one place than make sure @@ -1023,9 +1056,9 @@ void clear_level_structures(void) { static struct rm zerorm = { GLYPH_UNEXPLORED, - 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; coordxy x, y; - register struct rm *lev; + struct rm *lev; /* note: normally we'd start at x=1 because map column #0 isn't used (except for placing vault guard at <0,0> when removed from the map @@ -1035,53 +1068,63 @@ clear_level_structures(void) lev = &levl[x][0]; for (y = 0; y < ROWNO; y++) { *lev++ = zerorm; - gl.level.objects[x][y] = (struct obj *) 0; - gl.level.monsters[x][y] = (struct monst *) 0; + svl.level.objects[x][y] = (struct obj *) 0; + svl.level.monsters[x][y] = (struct monst *) 0; } } - gl.level.objlist = (struct obj *) 0; - gl.level.buriedobjlist = (struct obj *) 0; - gl.level.monlist = (struct monst *) 0; - gl.level.damagelist = (struct damage *) 0; - gl.level.bonesinfo = (struct cemetery *) 0; - - gl.level.flags.nfountains = 0; - gl.level.flags.nsinks = 0; - gl.level.flags.has_shop = 0; - gl.level.flags.has_vault = 0; - gl.level.flags.has_zoo = 0; - gl.level.flags.has_court = 0; - gl.level.flags.has_morgue = gl.level.flags.graveyard = 0; - gl.level.flags.has_beehive = 0; - gl.level.flags.has_barracks = 0; - gl.level.flags.has_temple = 0; - gl.level.flags.has_swamp = 0; - gl.level.flags.noteleport = 0; - gl.level.flags.hardfloor = 0; - gl.level.flags.nommap = MAPPABLE_ALWAYS; - gl.level.flags.hero_memory = 1; - gl.level.flags.shortsighted = 0; - gl.level.flags.sokoban_rules = 0; - gl.level.flags.is_maze_lev = 0; - gl.level.flags.is_cavernous_lev = 0; - gl.level.flags.arboreal = 0; - gl.level.flags.wizard_bones = 0; - gl.level.flags.outdoors = 0; - gl.level.flags.visited_after_event = 0; - - gn.nroom = 0; - gr.rooms[0].hx = -1; + svl.level.objlist = (struct obj *) 0; + svl.level.buriedobjlist = (struct obj *) 0; + svl.level.monlist = (struct monst *) 0; + svl.level.damagelist = (struct damage *) 0; + svl.level.bonesinfo = (struct cemetery *) 0; + + svl.level.flags.nfountains = 0; + svl.level.flags.nsinks = 0; + svl.level.flags.has_shop = 0; + svl.level.flags.has_vault = 0; + svl.level.flags.has_zoo = 0; + svl.level.flags.has_court = 0; + svl.level.flags.has_morgue = svl.level.flags.graveyard = 0; + svl.level.flags.has_beehive = 0; + svl.level.flags.has_barracks = 0; + svl.level.flags.has_temple = 0; + svl.level.flags.has_swamp = 0; + svl.level.flags.noteleport = 0; + svl.level.flags.hardfloor = 0; + svl.level.flags.nommap = MAPPABLE_ALWAYS; + svl.level.flags.hero_memory = 1; + svl.level.flags.shortsighted = 0; + svl.level.flags.sokoban_rules = 0; + svl.level.flags.is_maze_lev = 0; + svl.level.flags.is_cavernous_lev = 0; + svl.level.flags.arboreal = 0; + svl.level.flags.has_town = 0; + svl.level.flags.wizard_bones = 0; + svl.level.flags.temperature = In_hell(&u.uz) ? 1 : 0; + svl.level.flags.rndmongen = 1; + svl.level.flags.deathdrops = 1; + svl.level.flags.noautosearch = 0; + svl.level.flags.fumaroles = 0; + svl.level.flags.stormy = 0; + svl.level.flags.outdoors = 0; + svl.level.flags.visited_after_event = 0; + + svn.nroom = 0; + svr.rooms[0].hx = -1; gn.nsubroom = 0; gs.subrooms[0].hx = -1; gd.doorindex = 0; - gd.doors_alloc = 0; - free(gd.doors); - gd.doors = (coord *) 0; + if (svd.doors_alloc) { + free((genericptr_t) svd.doors); + svd.doors = (coord *) 0; + svd.doors_alloc = 0; + } init_rect(); init_vault(); stairway_free_all(); gm.made_branch = FALSE; clear_regions(); + free_exclusions(); reset_xystart_size(); if (gl.lev_message) { free(gl.lev_message); @@ -1089,15 +1132,24 @@ clear_level_structures(void) } } +#define ROOM_IS_FILLABLE(croom) \ + ((croom->rtype == OROOM || croom->rtype == THEMEROOM) \ + && croom->needfill == FILL_NORMAL) + /* Fill a "random" room (i.e. a typical non-special room in the Dungeons of - * Doom) with random monsters, objects, and dungeon features. - */ -static void -fill_ordinary_room(struct mkroom *croom) + Doom) with random monsters, objects, and dungeon features. + + If bonus_items is TRUE, there may be an additional special item + generated, depending on depth. */ +staticfn void +fill_ordinary_room( + struct mkroom *croom, + boolean bonus_items) { int trycnt = 0; coord pos; coordxy x, y; + boolean skip_chests = FALSE; if (croom->rtype != OROOM && croom->rtype != THEMEROOM) return; @@ -1106,7 +1158,13 @@ fill_ordinary_room(struct mkroom *croom) * that's specified to be unfilled to block an inner subroom that's * specified to be filled. */ for (x = 0; x < croom->nsubrooms; ++x) { - fill_ordinary_room(croom->sbrooms[x]); + struct mkroom *subroom = croom->sbrooms[x]; + + if (!subroom) { + impossible("fill_ordinary_room: Null subroom"); + return; + } + fill_ordinary_room(subroom, FALSE); } if (croom->needfill != FILL_NORMAL) @@ -1157,15 +1215,138 @@ fill_ordinary_room(struct mkroom *croom) (struct permonst *) 0, pos.x, pos.y, CORPSTAT_INIT); } + + /* + * bonus_items means that this is the room where the bonus item + * should be placed, if there is one; but there might not be a + * bonus item on any given level. + * + * Bonus items are currently as follows: + * a) on the Mines branch level, 100% chance of a fairly filling + * comestible; + * b) on other levels above the Oracle, 2/3 chance of a "supply + * chest" that contains an early-game survivability item + * (there are therefore more of these when Sokoban is deep, + * which is intentional as those games are harder). + * This mechanism could be expanded in the future to place + * near-guaranteed items on particular levels (but, it is possible + * that no room will be given a bonus item if there is no suitable + * room to place it in, so it should not be used for plot-critical + * items). + */ + if (bonus_items && somexyspace(croom, &pos)) { + branch *uz_branch = Is_branchlev(&u.uz); + + if (uz_branch && u.uz.dnum != mines_dnum + && (uz_branch->end1.dnum == mines_dnum + || uz_branch->end2.dnum == mines_dnum)) { + (void) mksobj_at((rn2(5) < 3) ? FOOD_RATION + : rn2(2) ? CRAM_RATION + : LEMBAS_WAFER, + pos.x, pos.y, TRUE, FALSE); + } else if (u.uz.dnum == oracle_level.dnum + && u.uz.dlevel < oracle_level.dlevel && rn2(3)) { + struct obj *otmp; + int otyp, tryct = 0; + boolean cursed; + /* reverse probabilities compared to non-supply chests; + these are twice as likely to be chests than large + boxes, rather than vice versa */ + struct obj *supply_chest = mksobj_at(rn2(3) ? CHEST : LARGE_BOX, + pos.x, pos.y, FALSE, FALSE); + + supply_chest->olocked = !!(rn2(6)); + + do { + static const int supply_items[] = { + POT_EXTRA_HEALING, + POT_SPEED, + POT_GAIN_ENERGY, + SCR_ENCHANT_WEAPON, + SCR_ENCHANT_ARMOR, + SCR_CONFUSE_MONSTER, + SCR_SCARE_MONSTER, + WAN_DIGGING, + SPE_HEALING, + }; + + /* 50% this is a potion of healing */ + otyp = rn2(2) ? POT_HEALING : ROLL_FROM(supply_items); + otmp = mksobj(otyp, TRUE, FALSE); + if (otyp == POT_HEALING && rn2(2)) { + otmp->quan = 2; + otmp->owt = weight(otmp); + } + cursed = otmp->cursed; + add_to_container(supply_chest, otmp); /* owt updated below */ + + ++tryct; + if (tryct == 50) { + impossible("couldn't generate supply chest item"); + break; + } + /* guarantee at least one noncursed item, with a small + probability of more; if we generate a cursed item, it's + added to the supply chest but we reroll for a noncursed + item and add that too */ + } while (cursed || !rn2(5)); + + /* maybe put a random item into the supply chest, biased + slightly towards low-level spellbooks; avoid tools + because chests don't fit into other chests */ + if (rn2(3)) { + static const int extra_classes[] = { + FOOD_CLASS, + WEAPON_CLASS, + ARMOR_CLASS, + GEM_CLASS, + SCROLL_CLASS, + POTION_CLASS, + RING_CLASS, + SPBOOK_no_NOVEL, + SPBOOK_no_NOVEL, + SPBOOK_no_NOVEL + }; + int oclass = ROLL_FROM(extra_classes); + + otmp = mkobj(oclass, FALSE); + if (oclass == SPBOOK_no_NOVEL) { + int pass, maxpass = (depth(&u.uz) > 2) ? 2 : 3; + + /* bias towards lower level by generating again + and taking the lower-level book; do that three + times if on level 1 or 2, twice when deeper */ + for (pass = 1; pass <= maxpass; ++pass) { + struct obj *otmp2 = mkobj(oclass, FALSE); + + if (objects[otmp->otyp].oc_level + <= objects[otmp2->otyp].oc_level) { + dealloc_obj(otmp2); + } else { + dealloc_obj(otmp); + otmp = otmp2; + } + } + } + add_to_container(supply_chest, otmp); /* owt updated below */ + } + + /* add_to_container() doesn't update the container's weight */ + supply_chest->owt = weight(supply_chest); + + skip_chests = TRUE; /* don't want a second chest in this room */ + } + } + /* put box/chest inside; * 40% chance for at least 1 box, regardless of number * of rooms; about 5 - 7.5% for 2 boxes, least likely * when few rooms; chance for 3 or more is negligible. */ - if (!rn2(gn.nroom * 5 / 2) && somexyspace(croom, &pos)) { - (void) mksobj_at((rn2(3)) ? LARGE_BOX : CHEST, + /*assert(svn.nroom > 0); // must be true because we're filling a room*/ + if (!skip_chests && !rn2(svn.nroom * 5 / 2) && somexyspace(croom, &pos)) + (void) mksobj_at(rn2(3) ? LARGE_BOX : CHEST, pos.x, pos.y, TRUE, FALSE); - } /* Maybe make some graffiti. * Chance decreases the lower you get in the dungeon. @@ -1185,12 +1366,11 @@ fill_ordinary_room(struct mkroom *croom) if (mesg) { do { - somexyspace(croom, &pos); + (void) somexyspace(croom, &pos); x = pos.x; y = pos.y; } while (levl[x][y].typ != ROOM && !rn2(40)); - if (!(IS_POOL(levl[x][y].typ) - || IS_FURNITURE(levl[x][y].typ))) + if (levl[x][y].typ == ROOM) make_engr_at(x, y, mesg, 0L, MARK); } } @@ -1204,36 +1384,62 @@ fill_ordinary_room(struct mkroom *croom) } } +staticfn void +themerooms_post_level_generate(void) +{ + lua_State *themes = (lua_State *) gl.luathemes[u.uz.dnum]; + + /* themes should already be loaded by makerooms(); + * if not, we don't run this either */ + if (!themes) + return; + + reset_xystart_size(); + iflags.in_lua = gi.in_mk_themerooms = TRUE; + gt.themeroom_failed = FALSE; + lua_getglobal(themes, "post_level_generate"); + nhl_pcall_handle(themes, 0, 0, "post_level_generate", NHLpa_panic); + iflags.in_lua = gi.in_mk_themerooms = FALSE; + + wallification(1, 0, COLNO - 1, ROWNO - 1); + free(gc.coder); + gc.coder = NULL; + lua_gc(themes, LUA_GCCOLLECT); +} + /* Full initialization of all level structures, map, objects, etc. * Handles any level - special levels will load that special level, Gehennom * will create mazes, and so on. * Called only from mklev(). */ -static void +staticfn void makelevel(void) { - register struct mkroom *croom; + struct mkroom *croom; branch *branchp; stairway *prevstairs; - register s_level *slev = Is_special(&u.uz); + s_level *slev; int i; /* this is apparently used to denote that a lot of program state is * uninitialized */ - 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(); /* full level reset */ + slev = Is_special(&u.uz); /* check for special levels */ if (slev) { /* a special level */ makemaz(slev->proto); - } else if (gd.dungeons[u.uz.dnum].proto[0]) { + } else if (svd.dungeons[u.uz.dnum].proto[0]) { /* named prototype file */ makemaz(""); - } else if (gd.dungeons[u.uz.dnum].fill_lvl[0]) { + } else if (svd.dungeons[u.uz.dnum].fill_lvl[0]) { /* various types of filler, e.g. "minefill" */ - makemaz(gd.dungeons[u.uz.dnum].fill_lvl); + makemaz(svd.dungeons[u.uz.dnum].fill_lvl); } else if (In_quest(&u.uz)) { /* quest filler */ char fillname[9]; @@ -1257,6 +1463,7 @@ makelevel(void) else { /* otherwise, fall through - it's a "regular" level. */ makerooms(); + assert(svn.nroom > 0); /* order rooms[] by x-coordinate */ sort_rooms(); @@ -1280,26 +1487,27 @@ makelevel(void) /* make sure vault can actually be placed */ if (check_room(&gv.vault_x, &w, &gv.vault_y, &h, TRUE)) { fill_vault: - add_room(gv.vault_x, gv.vault_y, gv.vault_x + w, gv.vault_y + h, + add_room(gv.vault_x, gv.vault_y, + gv.vault_x + w, gv.vault_y + h, TRUE, VAULT, FALSE); - gl.level.flags.has_vault = 1; - gr.rooms[gn.nroom - 1].needfill = FILL_NORMAL; - fill_special_room(&gr.rooms[gn.nroom - 1]); + svl.level.flags.has_vault = 1; + svr.rooms[svn.nroom - 1].needfill = FILL_NORMAL; + fill_special_room(&svr.rooms[svn.nroom - 1]); mk_knox_portal(gv.vault_x + w, gv.vault_y + h); /* Only put a vault teleporter with 1/3 chance; * a teleportation trap in a closet is a sure sign that a vault is * on the level, but a vault is not a sure sign of a vault * teleporter. */ - if (!gl.level.flags.noteleport && !rn2(3)) + if (!svl.level.flags.noteleport && !rn2(3)) makevtele(); } else if (rnd_rect() && create_vault()) { /* If we didn't create a vault already, try once more. */ - gv.vault_x = gr.rooms[gn.nroom].lx; - gv.vault_y = gr.rooms[gn.nroom].ly; + gv.vault_x = svr.rooms[svn.nroom].lx; + gv.vault_y = svr.rooms[svn.nroom].ly; if (check_room(&gv.vault_x, &w, &gv.vault_y, &h, TRUE)) goto fill_vault; else - gr.rooms[gn.nroom].hx = -1; + svr.rooms[svn.nroom].hx = -1; } } @@ -1327,21 +1535,44 @@ makelevel(void) if (u.uz.dnum == 0 && u.uz.dlevel == 1 && gs.stairs != prevstairs) gs.stairs->u_traversed = TRUE; + /* some levels have specially generated items in ordinary + rooms (intended to be indistinguishable from the normally + generated items); work out which room these will be placed in */ + int fillable_room_count = 0; + for (croom = svr.rooms; croom->hx > 0; croom++) { + if (ROOM_IS_FILLABLE(croom)) + fillable_room_count++; + } + /* choose a random fillable room to be the one that gets the + bonus items, if there are any; if there aren't any we don't + generate the bonus items (but levels with no fillable rooms + typically don't have any bonus items to generate anyway) */ + int bonus_item_room_countdown = fillable_room_count + ? rn2(fillable_room_count) : -1; + /* for each room: put things inside */ - for (croom = gr.rooms; croom->hx > 0; croom++) { - fill_ordinary_room(croom); + for (croom = svr.rooms; croom->hx > 0; croom++) { + boolean fillable = ROOM_IS_FILLABLE(croom); + + fill_ordinary_room(croom, + fillable && bonus_item_room_countdown == 0); + if (fillable) + --bonus_item_room_countdown; } } /* Fill all special rooms now, regardless of whether this is a special * level, proto level, or ordinary level. */ - for (i = 0; i < gn.nroom; ++i) { - fill_special_room(&gr.rooms[i]); + for (i = 0; i < svn.nroom; ++i) { + fill_special_room(&svr.rooms[i]); } + themerooms_post_level_generate(); + if (gl.luacore && nhcb_counts[NHCB_LVL_ENTER]) { lua_getglobal(gl.luacore, "nh_callback_run"); lua_pushstring(gl.luacore, nhcb_name[NHCB_LVL_ENTER]); - nhl_pcall(gl.luacore, 1, 0); + nhl_pcall_handle(gl.luacore, 1, 0, "makelevel", NHLpa_panic); + lua_settop(gl.luacore, 0); } } @@ -1384,7 +1615,7 @@ mineralize(int kelp_pool, int kelp_moat, int goldprob, int gemprob, almost all special levels are excluded */ if (!skip_lvl_checks && (In_hell(&u.uz) || In_V_tower(&u.uz) - || gl.level.flags.arboreal + || svl.level.flags.arboreal || ((sp = Is_special(&u.uz)) != 0 && !Is_oracle_level(&u.uz) && (!In_mines(&u.uz) || sp->flags.town)))) return; @@ -1470,16 +1701,16 @@ level_finalize_topology(void) mineralize(-1, -1, -1, -1, FALSE); gi.in_mklev = FALSE; /* avoid coordinates in future lua-loads for this level being thrown off - * because xstart and ystart aren't saved with the level and will be 0 after - * leaving and returning */ + * because xstart and ystart aren't saved with the level and will be 0 + * after leaving and returning */ gx.xstart = gy.ystart = 0; /* has_morgue gets cleared once morgue is entered; graveyard stays set (graveyard might already be set even when has_morgue is clear [see fixup_special()], so don't update it unconditionally) */ - if (gl.level.flags.has_morgue) - gl.level.flags.graveyard = 1; - if (!gl.level.flags.is_maze_lev) { - for (croom = &gr.rooms[0]; croom != &gr.rooms[gn.nroom]; croom++) + if (svl.level.flags.has_morgue) + svl.level.flags.graveyard = 1; + if (!svl.level.flags.is_maze_lev) { + for (croom = &svr.rooms[0]; croom != &svr.rooms[svn.nroom]; croom++) #ifdef SPECIALIZATION topologize(croom, FALSE); #else @@ -1487,10 +1718,10 @@ level_finalize_topology(void) #endif } set_wall_state(); - /* for many room types, gr.rooms[].rtype is zeroed once the room has been - entered; gr.rooms[].orig_rtype always retains original rtype value */ - for (ridx = 0; ridx < SIZE(gr.rooms); ridx++) - gr.rooms[ridx].orig_rtype = gr.rooms[ridx].rtype; + /* for many room types, svr.rooms[].rtype is zeroed once the room has been + entered; svr.rooms[].orig_rtype always retains original rtype value */ + for (ridx = 0; ridx < SIZE(svr.rooms); ridx++) + svr.rooms[ridx].orig_rtype = svr.rooms[ridx].rtype; } void @@ -1528,7 +1759,7 @@ topologize(struct mkroom *croom) #endif { coordxy x, y; - register int roomno = (int) ((croom - gr.rooms) + ROOMOFFSET); + int roomno = (int) ((croom - svr.rooms) + ROOMOFFSET); coordxy lowx = croom->lx, lowy = croom->ly; coordxy hix = croom->hx, hiy = croom->hy; #ifdef SPECIALIZATION @@ -1582,26 +1813,39 @@ topologize(struct mkroom *croom) } /* Find an unused room for a branch location. */ -static struct mkroom * +staticfn struct mkroom * find_branch_room(coord *mp) { struct mkroom *croom = 0; - if (gn.nroom == 0) { + if (svn.nroom == 0) { mazexy(mp); /* already verifies location */ } else { croom = generate_stairs_find_room(); - + assert(croom != NULL); /* Null iff nroom==0 which won't get here */ if (!somexyspace(croom, mp)) impossible("Can't place branch!"); } return croom; } +/* Find the room for (x,y). Return null if not in a room. */ +staticfn struct mkroom * +pos_to_room(coordxy x, coordxy y) +{ + int i; + struct mkroom *curr; + + for (curr = svr.rooms, i = 0; i < svn.nroom; curr++, i++) + if (inside_room(curr, x, y)) + return curr; + ; + return (struct mkroom *) 0; +} + /* Place a branch staircase or ladder for branch br at the coordinates (x,y). * If x is zero, pick the branch room and coordinates within it randomly. * If br is null, or the global made_branch is TRUE, do nothing. */ -/* If given a branch, randomly place a special stair or portal. */ void place_branch( branch *br, /* branch to place */ @@ -1660,10 +1904,10 @@ place_branch( /* Return TRUE if the given location is directly adjacent to a door or secret * door in any direction. */ -static boolean -bydoor(register coordxy x, register coordxy y) +staticfn boolean +bydoor(coordxy x, coordxy y) { - register int typ; + int typ; if (isok(x + 1, y)) { typ = levl[x + 1][y].typ; @@ -1698,10 +1942,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); } @@ -1743,7 +1987,7 @@ occupied(coordxy x, coordxy y) } /* generate a corpse and some items on top of a trap */ -static void +staticfn void mktrap_victim(struct trap *ttmp) { /* Object generated by the trap; initially NULL, stays NULL if @@ -1756,6 +2000,7 @@ mktrap_victim(struct trap *ttmp) coordxy x = ttmp->tx, y = ttmp->ty; int quan = rnd(4); /* amount of ammo to dump */ + assert(x > 0 && x < COLNO && y >= 0 && y < ROWNO); /* Not all trap types have special handling here; only the ones that kill in a specific way that's obvious after the fact. */ switch (kind) { @@ -1841,54 +2086,132 @@ mktrap_victim(struct trap *ttmp) victim_mnum = PM_GNOME; /* 10% chance of a candle too */ if (!rn2(10)) { - otmp = mksobj(rn2(4) ? TALLOW_CANDLE : WAX_CANDLE, - TRUE, FALSE); + otmp = mksobj(rn2(4) ? TALLOW_CANDLE : WAX_CANDLE, TRUE, FALSE); otmp->quan = 1; - otmp->blessed = 0; - otmp->cursed = 1; otmp->owt = weight(otmp); + curse(otmp); place_object(otmp, x, y); + if (!levl[x][y].lit) + begin_burn(otmp, FALSE); } break; default: - /* the most common race */ + /* human is the most common result */ victim_mnum = PM_HUMAN; break; } - otmp = mkcorpstat(CORPSE, NULL, &mons[victim_mnum], x, y, - CORPSTAT_INIT); - if (otmp) - otmp->age -= (TAINT_AGE + 1); /* died too long ago to eat */ + /* PM_HUMAN is a placeholder monster primarily used for zombie, mummy, + and vampire corpses; usually change it into a fake player monster + instead (always human); no role-specific equipment is provided */ + if (victim_mnum == PM_HUMAN && rn2(25)) + victim_mnum = rn1(PM_WIZARD - PM_ARCHEOLOGIST, PM_ARCHEOLOGIST); + otmp = mkcorpstat(CORPSE, NULL, &mons[victim_mnum], x, y, CORPSTAT_INIT); + otmp->age -= (TAINT_AGE + 1); /* died too long ago to safely eat */ } -/* Create a trap. - * If num is a valid trap index, create that specific trap. - * If tm is non-NULL, create the trap at tm's coordinates. Otherwise, if - * mazeflag is TRUE, choose a random maze position; if FALSE, assume that croom - * is non-NULL and pick a random location inside croom. - * - * If num is invalid as a trap index, it will create a random trap. In - * Gehennom, there is a 20% chance it will just pick fire trap. If various - * factors mean that the trap is unsuitable (usually because of difficulty), it - * will keep trying until it picks something valid. - * - * If a fallthru trap is created on a undiggable-floor level, it defaults to - * PIT. If a WEB is created, a giant spider is created on top of it, unless the - * MKTRAP_NOSPIDERONWEB flag is on in flags. - * Finally, if it is very early in the dungeon, and the trap is potentially - * lethal, create a minimal fake bones pile on the trap. - */ +/* pick a random trap type, return NO_TRAP if "too hard" */ +staticfn int +traptype_rnd(unsigned mktrapflags) +{ + unsigned lvl = level_difficulty(); + int kind = rnd(TRAPNUM - 1); + + switch (kind) { + /* these are controlled by the feature or object they guard, + not by the map so mustn't be created on it */ + case TRAPPED_DOOR: + case TRAPPED_CHEST: + kind = NO_TRAP; + break; + /* these can have a random location but can't be generated + randomly */ + case MAGIC_PORTAL: + case VIBRATING_SQUARE: + kind = NO_TRAP; + break; + case ROLLING_BOULDER_TRAP: + case SLP_GAS_TRAP: + if (lvl < 2) + kind = NO_TRAP; + break; + case LEVEL_TELEP: + if (lvl < 5 || svl.level.flags.noteleport + || single_level_branch(&u.uz)) + kind = NO_TRAP; + break; + case SPIKED_PIT: + if (lvl < 5) + kind = NO_TRAP; + break; + case LANDMINE: + if (lvl < 6) + kind = NO_TRAP; + break; + case WEB: + if (lvl < 7 && !(mktrapflags & MKTRAP_NOSPIDERONWEB)) + kind = NO_TRAP; + break; + case STATUE_TRAP: + case POLY_TRAP: + if (lvl < 8) + kind = NO_TRAP; + break; + case FIRE_TRAP: + if (!Inhell) + kind = NO_TRAP; + break; + case COLD_TRAP: + if (!In_cocytus(&u.uz)) + kind = NO_TRAP; + break; + case TELEP_TRAP: + if (svl.level.flags.noteleport) + kind = NO_TRAP; + break; + case HOLE: + /* make these much less often than other traps */ + if (rn2(7)) + kind = NO_TRAP; + break; + case RUST_TRAP: + case ROCKTRAP: + /* certain traps that rely on a ceiling to make sense */ + if (!has_ceiling(&u.uz)) + kind = NO_TRAP; + break; + } + return kind; +} + +/* mktrap(): select trap type and location, then use maketrap() to create it; + make it at location 'tm' when that isn't Null, otherwise in 'croom' + if mktrapflags doesn't have MKTRAP_MAZEFLAG set, else in maze corridor */ void mktrap( - int num, - int mktrapflags, - struct mkroom *croom, - coord *tm) + int num, /* if non-zero, specific type of trap to make */ + unsigned mktrapflags, /* MKTRAP_{SEEN,MAZEFLAG,NOSPIDERONWEB,NOVICTIM} */ + struct mkroom *croom, /* room to hold trap */ + coord *tm) /* specific location for trap */ { - register int kind; + static int mktrap_err = 0; /* move to struct g? */ struct trap *t; - unsigned lvl = level_difficulty(); coord m; + int kind; + unsigned lvl = level_difficulty(); + + if (!tm && !croom && !(mktrapflags & MKTRAP_MAZEFLAG)) { + /* complain when the combination of arguments will never set 'm' */ + if (!mktrap_err++) { + char errbuf[BUFSZ]; + + Snprintf(errbuf, sizeof errbuf, + "args (%d,%d,%s,%s) are invalid", + num, mktrapflags, "null room", "null location"); + paniclog("mktrap", errbuf); + } + return; + } + m.x = m.y = 0; /* no traps in pools */ if (tm && is_pool(tm->x, tm->y)) @@ -1904,71 +2227,7 @@ mktrap( kind = COLD_TRAP; } else { do { - kind = rnd(TRAPNUM - 1); - /* reject "too hard" traps */ - switch (kind) { - /* these are controlled by the feature or object they guard, - not by the map so mustn't be created on it */ - case TRAPPED_DOOR: - case TRAPPED_CHEST: - kind = NO_TRAP; - break; - /* these can have a random location but can't be generated - randomly */ - case MAGIC_PORTAL: - case VIBRATING_SQUARE: - kind = NO_TRAP; - break; - case ROLLING_BOULDER_TRAP: - case SLP_GAS_TRAP: - if (lvl < 2) - kind = NO_TRAP; - break; - case LEVEL_TELEP: - if (lvl < 5 || gl.level.flags.noteleport - || single_level_branch(&u.uz)) - kind = NO_TRAP; - break; - case SPIKED_PIT: - if (lvl < 5) - kind = NO_TRAP; - break; - case LANDMINE: - if (lvl < 6) - kind = NO_TRAP; - break; - case WEB: - if (lvl < 7 && !(mktrapflags & MKTRAP_NOSPIDERONWEB)) - kind = NO_TRAP; - break; - case STATUE_TRAP: - case POLY_TRAP: - if (lvl < 8) - kind = NO_TRAP; - break; - case FIRE_TRAP: - if (!Inhell || In_cocytus(&u.uz)) - kind = NO_TRAP; - break; - case COLD_TRAP: - if (!In_cocytus(&u.uz)) - kind = NO_TRAP; - break; - case TELEP_TRAP: - if (gl.level.flags.noteleport) - kind = NO_TRAP; - break; - case HOLE: - /* make these much less often than other traps */ - if (rn2(7)) - kind = NO_TRAP; - break; - case RUST_TRAP: - case ROCKTRAP: - /* certain traps that rely on a ceiling to make sense */ - if (!has_ceiling(&u.uz)) - kind = NO_TRAP; - } + kind = traptype_rnd(mktrapflags); } while (kind == NO_TRAP); } @@ -1978,16 +2237,16 @@ mktrap( if (tm) { m = *tm; } else { - register int tryct = 0; + int tryct = 0; boolean avoid_boulder = (is_pit(kind) || is_hole(kind)); /* Try up to 200 times to find a random coordinate for the trap. */ do { if (++tryct > 200) return; - if (mktrapflags & MKTRAP_MAZEFLAG) + if ((mktrapflags & MKTRAP_MAZEFLAG) != 0) mazexy(&m); - else if (!somexyspace(croom, &m)) + else if (croom && !somexyspace(croom, &m)) return; } while (occupied(m.x, m.y) || (avoid_boulder && sobj_at(BOULDER, m.x, m.y))); @@ -2026,9 +2285,11 @@ mktrap( nonlethal, even indirectly. We also exclude all of the later/fancier traps because they tend to have special considerations (e.g. webs, portals), often are indirectly - lethal, and tend not to generate on shallower levels anyway. - Finally, pits are excluded because it's weird to see an item - in a pit and yet not be able to identify that the pit is there. */ + lethal, and tend not to generate on shallower levels anyway + (exception: magic traps can generate on dlvl 1 and be + immediately lethal). Finally, pits are excluded because it's + weird to see an item in a pit and yet not be able to identify + that the pit is there. */ if (kind != NO_TRAP && !(mktrapflags & MKTRAP_NOVICTIM) && lvl <= (unsigned) rnd(4) && kind != SQKY_BOARD && kind != RUST_TRAP @@ -2037,7 +2298,7 @@ mktrap( which case tx,ty==launch.x,y; no boulder => no dead predecessor */ && !(kind == ROLLING_BOULDER_TRAP && t->launch.x == t->tx && t->launch.y == t->ty) - && !is_pit(kind) && kind < HOLE) { + && !is_pit(kind) && (kind < HOLE || kind == MAGIC_TRAP)) { mktrap_victim(t); } } @@ -2086,7 +2347,7 @@ mkstairs( } /* is room a good one to generate up or down stairs in? */ -static boolean +staticfn boolean generate_stairs_room_good(struct mkroom *croom, int phase) { /* @@ -2104,44 +2365,51 @@ generate_stairs_room_good(struct mkroom *croom, int phase) } /* find a good room to generate an up or down stairs in */ -static struct mkroom * +staticfn struct mkroom * generate_stairs_find_room(void) { struct mkroom *croom; int i, phase, ai; int *rmarr; - if (!gn.nroom) + if (!svn.nroom) return (struct mkroom *) 0; - rmarr = (int *) alloc(sizeof(int) * gn.nroom); + rmarr = (int *) alloc(sizeof(int) * svn.nroom); for (phase = 2; phase > -1; phase--) { ai = 0; - for (i = 0; i < gn.nroom; i++) - if (generate_stairs_room_good(&gr.rooms[i], phase)) + for (i = 0; i < svn.nroom; i++) + if (generate_stairs_room_good(&svr.rooms[i], phase)) rmarr[ai++] = i; if (ai > 0) { i = rmarr[rn2(ai)]; free(rmarr); - return &gr.rooms[i]; + return &svr.rooms[i]; } } free(rmarr); - croom = &gr.rooms[rn2(gn.nroom)]; + croom = &svr.rooms[rn2(svn.nroom)]; return croom; } /* construct stairs up and down within the same branch, up and down in different rooms if possible */ -static void +staticfn void generate_stairs(void) { - struct mkroom *croom = generate_stairs_find_room(); + /* generate_stairs_find_room() returns Null if nroom == 0, but that + should never happen for a rooms+corridors style level */ + static const char + gen_stairs_panic[] = "generate_stairs: failed to find a room! (%d)"; + struct mkroom *croom; coord pos; if (!Is_botlevel(&u.uz)) { + if ((croom = generate_stairs_find_room()) == NULL) + panic(gen_stairs_panic, svn.nroom); + if (!somexyspace(croom, &pos)) { pos.x = somex(croom); pos.y = somey(croom); @@ -2149,11 +2417,13 @@ generate_stairs(void) mkstairs(pos.x, pos.y, 0, croom, FALSE); /* down */ } - if (gn.nroom > 1) - croom = generate_stairs_find_room(); - /* now do the upstairs */ if (u.uz.dlevel != 1) { + /* if there is only 1 room and we found it above, this will find + it again */ + if ((croom = generate_stairs_find_room()) == NULL) + panic(gen_stairs_panic, svn.nroom); + if (!somexyspace(croom, &pos)) { pos.x = somex(croom); pos.y = somey(croom); @@ -2163,7 +2433,7 @@ generate_stairs(void) } /* Return number of monsters created. */ -int +staticfn int mkmonst_in_room(struct mkroom *croom) { int num_monst = 1; @@ -2199,10 +2469,10 @@ mkfount(struct mkroom *croom) if (!rn2(7)) levl[m.x][m.y].blessedftn = 1; - gl.level.flags.nfountains++; + svl.level.flags.nfountains++; } -static boolean +staticfn boolean find_okay_roompos(struct mkroom *croom, coord *crd) { int tryct = 0; @@ -2231,7 +2501,7 @@ bury_sink_ring(coordxy x, coordxy y) add_to_buried(ring); } -void +staticfn void mksink(struct mkroom *croom) { coord m; @@ -2246,7 +2516,7 @@ mksink(struct mkroom *croom) /* All sinks have a ring stuck in the pipes below */ bury_sink_ring(m.x, m.y); - gl.level.flags.nsinks++; + svl.level.flags.nsinks++; } void @@ -2270,12 +2540,12 @@ mkaltar(struct mkroom *croom) levl[m.x][m.y].altarmask = Align2amask(al); } -void +staticfn void mkgrave(struct mkroom *croom) { coord m; - register int tryct = 0; - register struct obj *otmp; + int tryct = 0; + struct obj *otmp; const char * inscription = (const char *) 0; if (croom->rtype != OROOM) @@ -2338,14 +2608,10 @@ mktree(struct mkroom *croom) set_levltyp(m.x, m.y, TREE); } -/* maze levels have slightly different constraints from normal levels */ -#define x_maze_min 2 -#define y_maze_min 2 - /* * Major level transmutation: add a set of stairs (to the Sanctum) after * an earthquake that leaves behind a new topology, centered at inv_pos. - * Assumes there are no rooms within the invocation area and that gi.inv_pos + * Assumes there are no rooms within the invocation area and that svi.inv_pos * is not too close to the edge of the map. Also assume the hero can see, * which is guaranteed for normal play due to the fact that sight is needed * to read the Book of the Dead. [That assumption is not valid; it is @@ -2355,19 +2621,50 @@ mktree(struct mkroom *croom) void mkinvokearea(void) { - int dist; - coordxy xmin = gi.inv_pos.x, xmax = gi.inv_pos.x, - ymin = gi.inv_pos.y, ymax = gi.inv_pos.y; - register coordxy i; + int dist, wallct; + coordxy xmin, xmax, ymin, ymax; + coordxy i; /* slightly odd if levitating, but not wrong */ pline_The("floor shakes violently under you!"); - /* - * TODO: - * Suppress this message if player has dug out all the walls - * that would otherwise be affected. - */ - pline_The("walls around you begin to bend and crumble!"); + /* decide whether to issue the crumbling walls message */ + { + xmin = xmax = svi.inv_pos.x; + ymin = ymax = svi.inv_pos.y; + wallct = mkinvk_check_wall(xmin, ymin); + /* this replicates the somewhat convoluted loop below, working + out from the stair position, except for stopping early when + walls are found */ + for (dist = 1; !wallct && dist < 7; ++dist) { + xmin--, xmax++; + /* top and bottom */ + if (dist != 3) { /* the area is wider that it is high */ + ymin--, ymax++; + for (i = xmin + 1; i < xmax; i++) { + if (mkinvk_check_wall(i, ymin)) + ++wallct; /* we could break after finding first wall + * but it isn't a significant optimization + * for code which only executes once */ + if (mkinvk_check_wall(i, ymax)) + ++wallct; + } + } + /* left and right */ + if (!wallct) { /* skip y loop if x loop found any walls */ + for (i = ymin; i <= ymax; i++) { + if (mkinvk_check_wall(xmin, i)) + ++wallct; + if (mkinvk_check_wall(xmax, i)) + ++wallct; + } + } + } + /* message won't appear if the maze 'walls' on this level are lava + or if all the walls within range have been dug away; when it does + appear, it will describe iron bars as "walls" (which is ok) */ + if (wallct) + pline_The("walls around you begin to bend and crumble!"); + } display_nhwindow(WIN_MESSAGE, TRUE); /* any trap hero is stuck in will be going away now */ @@ -2376,6 +2673,9 @@ mkinvokearea(void) buried_ball_to_punishment(); reset_utrap(FALSE); } + + xmin = xmax = svi.inv_pos.x; /* reset after the check for walls */ + ymin = ymax = svi.inv_pos.y; mkinvpos(xmin, ymin, 0); /* middle, before placing stairs */ for (dist = 1; dist < 7; dist++) { @@ -2411,21 +2711,30 @@ mkinvokearea(void) /* Change level topology. Boulders in the vicinity are eliminated. * Temporarily overrides vision in the name of a nice effect. */ -static void +staticfn void mkinvpos(coordxy x, coordxy y, int dist) { struct trap *ttmp; struct obj *otmp; boolean make_rocks; - register struct rm *lev = &levl[x][y]; + struct rm *lev = &levl[x][y]; struct monst *mon; + /* maze levels have slightly different constraints from normal levels; + these are also defined in mkmaze.c and may not be appropriate for + mazes with corridors wider than 1 or for cavernous levels */ +#define x_maze_min 2 +#define y_maze_min 2 /* clip at existing map borders if necessary */ - if (!within_bounded_area(x, y, x_maze_min + 1, y_maze_min + 1, - gx.x_maze_max - 1, gy.y_maze_max - 1)) { + if (!within_bounded_area(x, y, x_maze_min, y_maze_min, + gx.x_maze_max, gy.y_maze_max)) { /* outermost 2 columns and/or rows may be truncated due to edge */ - if (dist < (7 - 2)) - panic("mkinvpos: <%d,%d> (%d) off map edge!", x, y, dist); + if (dist < (7 - 2)) { /* panic() or impossible() */ + void (*errfunc)(const char *, ...) PRINTF_F(1, 2); + + errfunc = !isok(x, y) ? panic : impossible; + (*errfunc)("mkinvpos: <%d,%d> (%d) off map edge!", x, y, dist); + } return; } @@ -2493,10 +2802,27 @@ mkinvpos(coordxy x, coordxy y, int dist) } if (!does_block(x, y, lev)) - unblock_point(x, y); /* make sure vision knows this location is open */ + unblock_point(x, y); /* make sure vision knows location is open */ /* display new value of position; could have a monster/object on it */ newsym(x, y); +#undef x_maze_min +#undef y_maze_min +} + +/* reduces clutter in mkinvokearea() while avoiding potential static analyzer + confusion about using isok(x,y) to control access to levl[x][y] */ +staticfn int +mkinvk_check_wall(coordxy x, coordxy y) +{ + unsigned ltyp; + + if (!isok(x, y)) + return 0; + assert(x > 0 && x < COLNO); + assert(y >= 0 && y < ROWNO); + ltyp = levl[x][y].typ; + return (IS_STWALL(ltyp) || ltyp == IRONBARS) ? 1 : 0; } /* @@ -2507,7 +2833,7 @@ mkinvpos(coordxy x, coordxy y, int dist) * * Ludios will remain isolated until the branch is corrected by this function. */ -static void +staticfn void mk_knox_portal(coordxy x, coordxy y) { d_level *source; @@ -2515,6 +2841,9 @@ mk_knox_portal(coordxy x, coordxy y) schar u_depth; br = dungeon_branch("Fort Ludios"); + /* dungeon_branch() panics (so never returns) if result would be Null */ + assert(br != NULL); + if (on_level(&knox_level, &br->end1)) { source = &br->end2; } else { @@ -2525,7 +2854,7 @@ mk_knox_portal(coordxy x, coordxy y) } /* Already set. */ - if (source->dnum < gn.n_dgns) + if (source->dnum < svn.n_dgns) return; if (!(u.uz.dnum == oracle_level.dnum /* in main dungeon */ diff --git a/src/mkmap.c b/src/mkmap.c index 5e97a6752a..5ca96a693e 100644 --- a/src/mkmap.c +++ b/src/mkmap.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mkmap.c $NHDT-Date: 1596498181 2020/08/03 23:43:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.28 $ */ +/* NetHack 3.7 mkmap.c $NHDT-Date: 1717432093 2024/06/03 16:28:13 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.40 $ */ /* Copyright (c) J. C. Collet, M. Stephenson and D. Cohrs, 1992 */ /* NetHack may be freely redistributed. See license for details. */ @@ -8,48 +8,48 @@ #define HEIGHT (ROWNO - 1) #define WIDTH (COLNO - 2) -static void init_map(schar); -static void init_fill(schar, schar); -static schar get_map(int, int, schar); -static void pass_one(schar, schar); -static void pass_two(schar, schar); -static void pass_three(schar, schar); -static void join_map_cleanup(void); -static void join_map(schar, schar); -static void finish_map(schar, schar, boolean, boolean, boolean); -static void remove_room(unsigned); +staticfn void init_map(schar); +staticfn void init_fill(schar, 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); +staticfn void join_map_cleanup(void); +staticfn void join_map(schar, schar); +staticfn void finish_map(schar, schar, boolean, boolean, boolean); +staticfn void remove_room(unsigned); void mkmap(lev_init *); /* Unconditionally sets the whole map's terrain to bg_typ. */ -static void +staticfn void init_map(schar bg_typ) { - register 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; } } /* Randomly replaces 2/5 of squares on the level that have bg_typ terrain * with terrain of fg_typ. * Used to "seed" the random cavern-like generation. */ -static void +staticfn void init_fill(schar bg_typ, schar fg_typ) { - register 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++; } } @@ -58,15 +58,15 @@ init_fill(schar bg_typ, schar fg_typ) /* Return the terrain type at the given location, or bg_typ if out of bounds. * Used in determining the count of "live" neighbors in cavernous generation * (outside the map all counts as dead). */ -static schar -get_map(int col, int row, schar bg_typ) +staticfn schar +get_map(coordxy col, coordxy row, schar bg_typ) { if (col <= 0 || row < 0 || col > WIDTH || row >= HEIGHT) return bg_typ; return levl[col][row].typ; } -static const int dirs[16] = { +staticfn const int dirs[16] = { -1, -1 /**/, -1, 0 /**/, -1, 1 /**/, 0, -1 /**/, 0, 1 /**/, 1, -1 /**/, 1, 0 /**/, 1, 1 }; @@ -85,16 +85,16 @@ static const int dirs[16] = { * previous results. Not sure why it doesn't use new_locations like the other * pass_* functions do. */ -static void +staticfn void pass_one(schar bg_typ, schar fg_typ) { - register 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++; @@ -102,13 +102,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; @@ -124,27 +124,27 @@ pass_one(schar bg_typ, schar fg_typ) * * This time, it stores results in a temporary buffer, then copies them over * after it finishes. */ -static void +staticfn void pass_two(schar bg_typ, schar fg_typ) { - register 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); } /* Third pass at the cellular automaton: kill any live cells with fewer than 3 @@ -154,27 +154,27 @@ pass_two(schar bg_typ, schar fg_typ) * changes to levl until it's finished determining all the cell states. * * According to code below, this is used to tune map smoothing.*/ -static void +staticfn void pass_three(schar bg_typ, schar fg_typ) { - register 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); } /* @@ -185,14 +185,13 @@ pass_three(schar bg_typ, schar fg_typ) */ void flood_fill_rm( - int sx, - register int sy, - register int rmno, + coordxy sx, + coordxy sy, + int rmno, boolean lit, boolean anyroom) { - register int i; - int nx; + coordxy i, nx; schar fg_typ = levl[sx][sy].typ; /* back up to find leftmost uninitialized location */ @@ -213,7 +212,7 @@ flood_fill_rm( levl[i][sy].lit = lit; if (anyroom) { /* add walls to room as well */ - register 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) @@ -277,7 +276,7 @@ flood_fill_rm( } /* join_map uses temporary rooms; clean up after it */ -static void +staticfn void join_map_cleanup(void) { coordxy x, y; @@ -285,8 +284,8 @@ join_map_cleanup(void) for (x = 1; x < COLNO; x++) for (y = 0; y < ROWNO; y++) levl[x][y].roomno = NO_ROOM; - gn.nroom = gn.nsubroom = 0; - gr.rooms[gn.nroom].hx = gs.subrooms[gn.nsubroom].hx = -1; + svn.nroom = gn.nsubroom = 0; + svr.rooms[svn.nroom].hx = gs.subrooms[gn.nsubroom].hx = -1; } /* Connects all the discrete blobs of fg_typ on the level with "corridors" made @@ -296,29 +295,28 @@ join_map_cleanup(void) * already-connected rooms and some other room that isn't connected yet. * If any blob is of size 3 or less, it'll be removed instead of being * connected. */ -static void +staticfn void join_map(schar bg_typ, schar fg_typ) { - register struct mkroom *croom, *croom2; + struct mkroom *croom, *croom2; - register 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, gn.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); - gr.rooms[gn.nroom - 1].irregular = TRUE; - if (gn.nroom >= (MAXNROFROOMS * 2)) + svr.rooms[svn.nroom - 1].irregular = TRUE; + if (svn.nroom >= (MAXNROFROOMS * 2)) goto joinm; } else { /* @@ -328,7 +326,7 @@ join_map(schar bg_typ, schar fg_typ) for (sx = gm.min_rx; sx <= gm.max_rx; sx++) for (sy = gm.min_ry; sy <= gm.max_ry; sy++) if ((int) levl[sx][sy].roomno - == gn.nroom + ROOMOFFSET) { + == svn.nroom + ROOMOFFSET) { levl[sx][sy].typ = bg_typ; levl[sx][sy].roomno = NO_ROOM; } @@ -343,8 +341,8 @@ join_map(schar bg_typ, schar fg_typ) * so don't call sort_rooms(), which can screw up the roomno's * validity in the levl structure. */ - for (croom = &gr.rooms[0], croom2 = croom + 1; - croom2 < &gr.rooms[gn.nroom]; ) { + for (croom = &svr.rooms[0], croom2 = croom + 1; + croom2 < &svr.rooms[svn.nroom]; ) { /* pick random starting and end locations for "corridor" */ if (!somexy(croom, &sm) || !somexy(croom2, &em)) { /* ack! -- the level is going to be busted */ @@ -378,7 +376,7 @@ join_map(schar bg_typ, schar fg_typ) * than a moat. * Also automatically sets any lava terrain to be lit. */ -static void +staticfn void finish_map( schar fg_typ, schar bg_typ, @@ -386,30 +384,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 < gn.nroom; i++) - gr.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; } } @@ -427,13 +425,13 @@ 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; - for (i = gn.nroom - 1; i >= 0; --i) { - croom = &gr.rooms[i]; + for (i = svn.nroom - 1; i >= 0; --i) { + croom = &svr.rooms[i]; if (croom->hx < lx || croom->lx >= hx || croom->hy < ly || croom->ly >= hy) continue; /* no overlap */ @@ -452,33 +450,34 @@ remove_rooms(int lx, int ly, int hx, int hy) } /* - * Remove roomno from the rooms array, decrementing nroom. Also updates - * all level roomno values of affected higher numbered rooms. Assumes - * level structure contents corresponding to roomno have already been reset. + * Remove roomno from the rooms array, decrementing nroom. + * The last room is swapped with the being-removed room and locations + * within it have their roomno field updated. Other rooms are unaffected. + * Assumes level structure contents corresponding to roomno have already + * been reset. * Currently handles only the removal of rooms that have no subrooms. */ -static void +staticfn void remove_room(unsigned int roomno) { - struct mkroom *croom = &gr.rooms[roomno]; - struct mkroom *maxroom = &gr.rooms[--gn.nroom]; - int i, j; + struct mkroom *croom = &svr.rooms[roomno]; + struct mkroom *maxroom = &svr.rooms[--svn.nroom]; + coordxy x, y; unsigned oroomno; if (croom != maxroom) { /* since the order in the array only matters for making corridors, * copy the last room over the one being removed on the assumption * that corridors have already been dug. */ - (void) memcpy((genericptr_t) croom, (genericptr_t) maxroom, - sizeof(struct mkroom)); + *croom = *maxroom; /* since maxroom moved, update affected level roomno values */ - oroomno = gn.nroom + ROOMOFFSET; + 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; } } @@ -506,7 +505,7 @@ litstate_rnd(int litstate) * TRUE, regardless of what N_P3_ITER is. */ void -mkmap(lev_init* init_lev) +mkmap(lev_init *init_lev) { schar bg_typ = init_lev->bg, fg_typ = init_lev->fg; boolean smooth = init_lev->smoothed, join = init_lev->joined; @@ -537,8 +536,8 @@ mkmap(lev_init* init_lev) init_lev->icedpools); /* a walled, joined level is cavernous, not mazelike -dlc */ if (walled && join) { - gl.level.flags.is_maze_lev = FALSE; - gl.level.flags.is_cavernous_lev = TRUE; + svl.level.flags.is_maze_lev = FALSE; + svl.level.flags.is_cavernous_lev = TRUE; } free(gn.new_locations); } diff --git a/src/mkmaze.c b/src/mkmaze.c index 722efada66..0eb4e720fd 100644 --- a/src/mkmaze.c +++ b/src/mkmaze.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mkmaze.c $NHDT-Date: 1648064596 2022/03/23 19:43:16 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.133 $ */ +/* NetHack 3.7 mkmaze.c $NHDT-Date: 1737387068 2025/01/20 07:31:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.176 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Pasi Kallinen, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -6,23 +6,23 @@ #include "hack.h" #include "sp_lev.h" -static int iswall(coordxy, coordxy); -static int iswall_or_stone(coordxy, coordxy); -static boolean is_solid(coordxy, coordxy); -static int extend_spine(int[3][3], int, int, int); -static void wall_cleanup(coordxy, coordxy, coordxy, coordxy); -static boolean okay(coordxy, coordxy, coordxy); -static void maze0xy(coord *); -static boolean put_lregion_here(coordxy, coordxy, coordxy, coordxy, coordxy, +staticfn int iswall(coordxy, coordxy); +staticfn int iswall_or_stone(coordxy, coordxy); +staticfn boolean is_solid(coordxy, coordxy); +staticfn int extend_spine(int[3][3], int, int, int); +staticfn void wall_cleanup(coordxy, coordxy, coordxy, coordxy); +staticfn boolean okay(coordxy, coordxy, coordxy); +staticfn void maze0xy(coord *); +staticfn boolean put_lregion_here(coordxy, coordxy, coordxy, coordxy, coordxy, coordxy, xint16, boolean, d_level *); -static void setup_waterlevel(void); -static void unsetup_waterlevel(void); -static void check_ransacked(const char *); -static void migr_booty_item(int, const char *); -static void migrate_orc(struct monst *, unsigned long); -static void shiny_orc_stuff(struct monst *); -static void stolen_booty(void); -static void maze_remove_deadends(xint16); +staticfn void setup_waterlevel(void); +staticfn void unsetup_waterlevel(void); +staticfn void check_ransacked(const char *); +staticfn void migr_booty_item(int, const char *); +staticfn void migrate_orc(struct monst *, unsigned long); +staticfn void shiny_orc_stuff(struct monst *); +staticfn void stolen_booty(void); +staticfn void maze_remove_deadends(xint16); /* adjust a coordinate one step in the specified direction */ #define mz_move(X, Y, dir) \ @@ -40,7 +40,8 @@ static void maze_remove_deadends(xint16); * wall, door, secret door, or iron bars. * Despite the name, any door will return TRUE here, even if it's open, broken, * or missing. */ -static int +/* used to determine if wall spines can join this location */ +staticfn int iswall(coordxy x, coordxy y) { int type; @@ -49,12 +50,14 @@ iswall(coordxy x, coordxy y) return 0; type = levl[x][y].typ; return (IS_WALL(type) || IS_DOOR(type) + || type == LAVAWALL || type == WATER || type == SDOOR || type == IRONBARS); } /* Same as iswall(), except also returns TRUE if the terrain at the given * location is stone. */ -static int +/* used to determine if wall spines can join this location */ +staticfn int iswall_or_stone(coordxy x, coordxy y) { /* out of bounds = stone */ @@ -65,7 +68,7 @@ iswall_or_stone(coordxy x, coordxy y) } /* return TRUE if out of bounds, wall or rock */ -static boolean +staticfn boolean is_solid(coordxy x, coordxy y) { return (boolean) (!isok(x, y) || IS_STWALL(levl[x][y].typ)); @@ -73,24 +76,39 @@ is_solid(coordxy x, coordxy y) /* set map terrain type, handling lava lit, ice melt timers, etc */ boolean -set_levltyp(coordxy x, coordxy y, schar typ) +set_levltyp(coordxy x, coordxy y, schar newtyp) { - if (isok(x, y)) { - if ((typ < MAX_TYPE) && CAN_OVERWRITE_TERRAIN(levl[x][y].typ)) { - boolean was_ice = (levl[x][y].typ == ICE); - - levl[x][y].typ = typ; + if (isok(x, y) && newtyp >= STONE && newtyp < MAX_TYPE) { + if (CAN_OVERWRITE_TERRAIN(levl[x][y].typ)) { + schar oldtyp = levl[x][y].typ; + /* 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 + * the way newtyp will use them, clear them. + */ - if (typ == LAVAPOOL) + if (IS_LAVA(newtyp)) /* [what about IS_LAVA(oldtyp)=>.lit = 0?] */ levl[x][y].lit = 1; - - if (was_ice && typ != 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 */ + return TRUE; } #ifdef EXTRA_SANITY_CHECKS } else { - impossible("set_levltyp(%i,%i,%i) !isok", x, y, typ); + impossible("set_levltyp(%d,%d,%d)%s%s", + (int) x, (int) y, (int) newtyp, + !isok(x, y) ? " not isok()" : "", + (newtyp < STONE || newtyp >= MAX_TYPE) ? " bad type" : ""); #endif /*EXTRA_SANITY_CHECKS*/ } return FALSE; @@ -103,14 +121,17 @@ set_levltyp_lit(coordxy x, coordxy y, schar typ, schar lit) boolean ret = set_levltyp(x, y, typ); if (ret && isok(x, y)) { - /* LAVAPOOL handled in set_levltyp */ - if ((typ != LAVAPOOL) && (lit != SET_LIT_NOCHANGE)) { + if (lit != SET_LIT_NOCHANGE) { #ifdef EXTRA_SANITY_CHECKS if (lit < SET_LIT_NOCHANGE || lit > 1) - impossible("set_levltyp_lit(%i,%i,%i,%i)", x, y, typ, lit); + impossible("set_levltyp_lit(%d,%d,%d,%d)", + (int) x, (int) y, (int) typ, (int) lit); #endif /*EXTRA_SANITY_CHECKS*/ - if (lit == SET_LIT_RANDOM) + if (IS_LAVA(typ)) + lit = 1; + else if (lit == SET_LIT_RANDOM) lit = rn2(2); + levl[x][y].lit = lit; } } @@ -135,7 +156,7 @@ set_levltyp_lit(coordxy x, coordxy y, schar typ, schar lit) * W x W This would extend a spine from x down. * . W W */ -static int +staticfn int extend_spine(int locale[3][3], int wall_there, int dx, int dy) { int spine, nx, ny; @@ -168,7 +189,7 @@ extend_spine(int locale[3][3], int wall_there, int dx, int dy) /* Remove walls totally surrounded by stone. * Arguments specify the rectangle in which to do this. */ -static void +staticfn void wall_cleanup(coordxy x1, coordxy y1, coordxy x2, coordxy y2) { uchar type; @@ -271,7 +292,7 @@ wallification(coordxy x1, coordxy y1, coordxy x2, coordxy y2) * 3: Otherwise within the maze. * Used exclusively in walkfrom() to determine whether it should carve a path * to a new space. */ -static boolean +staticfn boolean okay(coordxy x, coordxy y, coordxy dir) { mz_move(x, y, dir); @@ -286,7 +307,7 @@ okay(coordxy x, coordxy y, coordxy dir) * The point is guaranteed to be on the maze grid: that is, it must have odd x * and y coordinates and have 3 <= x < x_maze_max and 3 <= y < y_maze_max */ /* find random starting point for maze generation */ -static void +staticfn void maze0xy(coord *cc) { cc->x = 3 + 2 * rn2((gx.x_maze_max >> 1) - 1); @@ -294,6 +315,24 @@ maze0xy(coord *cc) return; } +boolean +is_exclusion_zone(xint16 type, coordxy x, coordxy y) +{ + struct exclusion_zone *ez = sve.exclusion_zones; + + while (ez) { + if (((type == LR_DOWNTELE + && (ez->zonetype == LR_DOWNTELE || ez->zonetype == LR_TELE)) + || (type == LR_UPTELE + && (ez->zonetype == LR_UPTELE || ez->zonetype == LR_TELE)) + || type == ez->zonetype) + && within_bounded_area(x, y, ez->lx, ez->ly, ez->hx, ez->hy)) + return TRUE; + ez = ez->next; + } + return FALSE; +} + /* * Bad if: * pos is occupied OR @@ -310,7 +349,7 @@ bad_location( || within_bounded_area(x, y, nlx, nly, nhx, nhy) || is_open_air(x, y) || !((levl[x][y].typ == CORR - && gl.level.flags.is_maze_lev) + && svl.level.flags.is_maze_lev) || levl[x][y].typ == ROOM || levl[x][y].typ == GRASS || levl[x][y].typ == AIR)); @@ -335,7 +374,7 @@ place_lregion( * if there are rooms and this a branch, let place_branch choose * the branch location (to avoid putting branches in corridors). */ - if (rtype == LR_BRANCH && gn.nroom) { + if (rtype == LR_BRANCH && svn.nroom) { place_branch(Is_branchlev(&u.uz), 0, 0); return; } @@ -346,6 +385,16 @@ place_lregion( hy = ROWNO - 1; } + /* clamp the area to the map */ + if (lx < 1) + lx = 1; + if (hx > COLNO - 1) + hx = COLNO - 1; + if (ly < 0) + ly = 0; + if (hy > ROWNO - 1) + hy = ROWNO - 1; + /* first a probabilistic approach */ oneshot = (lx == hx && ly == hy); @@ -373,7 +422,7 @@ place_lregion( * oneshot basically means we're only trying this space, so don't tell the * caller to try somewhere else. */ -static boolean +staticfn boolean put_lregion_here( coordxy x, coordxy y, coordxy nlx, coordxy nly, coordxy nhx, coordxy nhy, @@ -383,7 +432,8 @@ put_lregion_here( { struct monst *mtmp; - if (bad_location(x, y, nlx, nly, nhx, nhy)) { + if (bad_location(x, y, nlx, nly, nhx, nhy) + || is_exclusion_zone(rtype, x, y)) { if (!oneshot) { return FALSE; /* caller should try again */ } else { @@ -397,7 +447,8 @@ put_lregion_here( mtmp->mtrapped = 0; deltrap_with_ammo(t, DELTRAP_DESTROY_AMMO); } - if (bad_location(x, y, nlx, nly, nhx, nhy)) + if (bad_location(x, y, nlx, nly, nhx, nhy) + || is_exclusion_zone(rtype, x, y)) return FALSE; } } @@ -435,13 +486,14 @@ void fixup_special(void) { lev_region *r = gl.lregions; + s_level *sp; struct d_level lev; int x, y; struct mkroom *croom; boolean added_branch = FALSE; if (Is_waterlevel(&u.uz) || Is_airlevel(&u.uz)) { - gl.level.flags.hero_memory = 0; + svl.level.flags.hero_memory = 0; /* water level is an odd beast - it has to be set up before calling place_lregions etc. */ setup_waterlevel(); @@ -458,10 +510,10 @@ fixup_special(void) lev = u.uz; lev.dlevel = atoi(r->rname.str); } else { - s_level *sp = find_level(r->rname.str); - + sp = find_level(r->rname.str); lev = sp->dlevel; } + FALLTHROUGH; /*FALLTHRU*/ case LR_UPSTAIR: @@ -477,24 +529,24 @@ fixup_special(void) case LR_DOWNTELE: /* save the region outlines for goto_level() */ if (r->rtype == LR_TELE || r->rtype == LR_UPTELE) { - gu.updest.lx = r->inarea.x1; - gu.updest.ly = r->inarea.y1; - gu.updest.hx = r->inarea.x2; - gu.updest.hy = r->inarea.y2; - gu.updest.nlx = r->delarea.x1; - gu.updest.nly = r->delarea.y1; - gu.updest.nhx = r->delarea.x2; - gu.updest.nhy = r->delarea.y2; + svu.updest.lx = r->inarea.x1; + svu.updest.ly = r->inarea.y1; + svu.updest.hx = r->inarea.x2; + svu.updest.hy = r->inarea.y2; + svu.updest.nlx = r->delarea.x1; + svu.updest.nly = r->delarea.y1; + svu.updest.nhx = r->delarea.x2; + svu.updest.nhy = r->delarea.y2; } if (r->rtype == LR_TELE || r->rtype == LR_DOWNTELE) { - gd.dndest.lx = r->inarea.x1; - gd.dndest.ly = r->inarea.y1; - gd.dndest.hx = r->inarea.x2; - gd.dndest.hy = r->inarea.y2; - gd.dndest.nlx = r->delarea.x1; - gd.dndest.nly = r->delarea.y1; - gd.dndest.nhx = r->delarea.x2; - gd.dndest.nhy = r->delarea.y2; + svd.dndest.lx = r->inarea.x1; + svd.dndest.ly = r->inarea.y1; + svd.dndest.hx = r->inarea.x2; + svd.dndest.hy = r->inarea.y2; + svd.dndest.nlx = r->delarea.x1; + svd.dndest.nly = r->delarea.y1; + svd.dndest.nhx = r->delarea.x2; + svd.dndest.nhy = r->delarea.y2; } /* place_lregion gets called from goto_level() */ break; @@ -514,7 +566,7 @@ fixup_special(void) struct obj *otmp; int tryct; - croom = &gr.rooms[0]; /* the first room defined on the medusa level */ + croom = &svr.rooms[0]; /* the first room defined on the medusa level */ for (tryct = rnd(4); tryct; tryct--) { x = somex(croom); y = somey(croom); @@ -555,12 +607,15 @@ fixup_special(void) gw.wizpuzzle.entered = FALSE; } + if ((sp = Is_special(&u.uz)) != 0 && sp->flags.town) /* Mine Town */ + svl.level.flags.has_town = 1; + if (gl.lregions) free((genericptr_t) gl.lregions), gl.lregions = 0; gn.num_lregions = 0; } -static void +staticfn void check_ransacked(const char *s) { /* this kludge only works as long as orctown is minetn-1 */ @@ -570,7 +625,7 @@ check_ransacked(const char *s) #define ORC_LEADER 1 static const char *const orcfruit[] = { "paddle cactus", "dwarven root" }; -static void +staticfn void migrate_orc(struct monst *mtmp, unsigned long mflags) { int nlev, max_depth, cur_depth; @@ -578,7 +633,7 @@ migrate_orc(struct monst *mtmp, unsigned long mflags) cur_depth = (int) depth(&u.uz); max_depth = dunlevs_in_dungeon(&u.uz) - + (gd.dungeons[u.uz.dnum].depth_start - 1); + + (svd.dungeons[u.uz.dnum].depth_start - 1); if (mflags == ORC_LEADER) { /* Note that the orc leader will take possession of any * remaining stuff not already delivered to other @@ -601,8 +656,8 @@ migrate_orc(struct monst *mtmp, unsigned long mflags) migrate_to_level(mtmp, ledger_no(&dest), MIGR_RANDOM, (coord *) 0); } -static void -shiny_orc_stuff(struct monst* mtmp) +staticfn void +shiny_orc_stuff(struct monst *mtmp) { int gemprob, goldprob, otyp; struct obj *otmp; @@ -633,8 +688,8 @@ shiny_orc_stuff(struct monst* mtmp) } } -static void -migr_booty_item(int otyp, const char* gang) +staticfn void +migr_booty_item(int otyp, const char *gang) { struct obj *otmp; @@ -644,7 +699,7 @@ migr_booty_item(int otyp, const char* gang) Strcpy(ONAME(otmp), gang); if (objects[otyp].oc_class == FOOD_CLASS) { if (otyp == SLIME_MOLD) - otmp->spe = fruitadd((char *) orcfruit[rn2(SIZE(orcfruit))], + otmp->spe = fruitadd((char *) ROLL_FROM(orcfruit), (struct fruit *) 0); otmp->quan += (long) rn2(3); otmp->owt = weight(otmp); @@ -652,7 +707,7 @@ migr_booty_item(int otyp, const char* gang) } } -static void +staticfn void stolen_booty(void) { char *gang, gang_name[BUFSZ]; @@ -680,7 +735,7 @@ stolen_booty(void) cnt = rnd(3); for (i = 0; i < cnt; ++i) migr_booty_item(SKELETON_KEY, gang); - otyp = rn2((GAUNTLETS_OF_DEXTERITY - GLOVES) + 1) + GLOVES; + otyp = rn1((GAUNTLETS_OF_DEXTERITY - GLOVES) + 1, GLOVES); migr_booty_item(otyp, gang); cnt = rnd(10); for (i = 0; i < cnt; ++i) { @@ -703,6 +758,7 @@ stolen_booty(void) if (mtmp) { mtmp = christen_monst(mtmp, upstart(gang)); mtmp->mpeaceful = 0; + set_malign(mtmp); shiny_orc_stuff(mtmp); migrate_orc(mtmp, ORC_LEADER); } @@ -766,7 +822,7 @@ maze_inbounds(coordxy x, coordxy y) * wall between them with whatever terrain typ is. * If it decides to remove the dead end in a direction that goes into a room, * create a door there instead. */ -static void +staticfn void maze_remove_deadends(xint16 typ) { char dirok[4]; @@ -805,7 +861,7 @@ maze_remove_deadends(xint16 typ) dir = dirok[rn2(idx)]; mz_move(dx, dy, dir); if (levl[dx][dy].roomno != NO_ROOM) { - dodoor(dx, dy, &gr.rooms[levl[dx][dy].roomno]); + dodoor(dx, dy, &svr.rooms[levl[dx][dy].roomno]); } else { levl[dx][dy].typ = typ; @@ -889,7 +945,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; @@ -922,22 +977,22 @@ makemaz(const char *s) "%s-%d", s, rnd((int) sp->rndlevs)); else Strcpy(protofile, s); - } else if (*(gd.dungeons[u.uz.dnum].proto)) { + } else if (*(svd.dungeons[u.uz.dnum].proto)) { if (dunlevs_in_dungeon(&u.uz) > 1) { if (sp && sp->rndlevs) Snprintf(protofile, sizeof protofile, - "%s%d-%d", gd.dungeons[u.uz.dnum].proto, + "%s%d-%d", svd.dungeons[u.uz.dnum].proto, dunlev(&u.uz), rnd((int) sp->rndlevs)); else Snprintf(protofile, sizeof protofile, - "%s%d", gd.dungeons[u.uz.dnum].proto, + "%s%d", svd.dungeons[u.uz.dnum].proto, dunlev(&u.uz)); } else if (sp && sp->rndlevs) { Snprintf(protofile, sizeof protofile, - "%s-%d", gd.dungeons[u.uz.dnum].proto, + "%s-%d", svd.dungeons[u.uz.dnum].proto, rnd((int) sp->rndlevs)); } else - Strcpy(protofile, gd.dungeons[u.uz.dnum].proto); + Strcpy(protofile, svd.dungeons[u.uz.dnum].proto); } else Strcpy(protofile, ""); @@ -945,6 +1000,7 @@ makemaz(const char *s) /* SPLEVTYPE format is "level-choice,level-choice"... */ if (wizard && *protofile && sp && sp->rndlevs) { char *ep = getenv("SPLEVTYPE"); /* not nh_getenv */ + if (ep) { /* strrchr always succeeds due to code in prior block */ int len = (int) ((strrchr(protofile, '-') - protofile) + 1); @@ -952,6 +1008,7 @@ makemaz(const char *s) while (ep && *ep) { if (!strncmp(ep, protofile, len)) { int pick = atoi(ep + len); + /* use choice only if valid */ if (pick > 0 && pick <= (int) sp->rndlevs) Sprintf(protofile + len, "%d", pick); @@ -988,7 +1045,7 @@ makemaz(const char *s) * attractive or randomized in any way, because it should never be * encountered. */ lvlfill_solid(ROOM, 1); - gl.level.flags.is_maze_lev = TRUE; + svl.level.flags.is_maze_lev = TRUE; mkstairs(20, 11, 1, (struct mkroom *) 0, FALSE); /* up */ if (Invocation_lev(&u.uz)) { @@ -1131,8 +1188,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); @@ -1140,8 +1197,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 */ @@ -1173,7 +1230,7 @@ get_level_extends( } } } - xmin -= (nonwall || !gl.level.flags.is_maze_lev) ? 2 : 1; + xmin -= (nonwall || !svl.level.flags.is_maze_lev) ? 2 : 1; if (xmin < 0) xmin = 0; @@ -1189,7 +1246,7 @@ get_level_extends( } } } - xmax += (nonwall || !gl.level.flags.is_maze_lev) ? 2 : 1; + xmax += (nonwall || !svl.level.flags.is_maze_lev) ? 2 : 1; if (xmax >= COLNO) xmax = COLNO - 1; @@ -1205,7 +1262,7 @@ get_level_extends( } } } - ymin -= (nonwall || !gl.level.flags.is_maze_lev) ? 2 : 1; + ymin -= (nonwall || !svl.level.flags.is_maze_lev) ? 2 : 1; found = nonwall = FALSE; for (ymax = ROWNO - 1; !found && ymax >= 0; ymax--) { @@ -1219,7 +1276,7 @@ get_level_extends( } } } - ymax += (nonwall || !gl.level.flags.is_maze_lev) ? 2 : 1; + ymax += (nonwall || !svl.level.flags.is_maze_lev) ? 2 : 1; *left = xmin; *right = xmax; @@ -1250,7 +1307,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; } @@ -1268,7 +1326,7 @@ mkportal(coordxy x, coordxy y, xint16 todnum, xint16 todlevel) return; } debugpline4("mkportal: at <%d,%d>, to %s, level %d", x, y, - gd.dungeons[todnum].dname, todlevel); + svd.dungeons[todnum].dname, todlevel); ttmp->dst.dnum = todnum; ttmp->dst.dlevel = todlevel; return; @@ -1277,6 +1335,8 @@ mkportal(coordxy x, coordxy y, xint16 todnum, xint16 todlevel) /* Selects d3+1 points on the level; create a large gas cloud on top of any of * these that are LAVAPOOL terrain. * Used only on the Plane of Fire. */ +/* augment the Plane of Fire; called from goto_level() when arriving and + moveloop_core() when on the level */ void fumaroles(void) { @@ -1288,7 +1348,7 @@ fumaroles(void) nmax++; sizemin += 5; } - if (gl.level.flags.temperature > 0) { + if (svl.level.flags.temperature > 0) { nmax++; sizemin += 5; } @@ -1318,22 +1378,28 @@ fumaroles(void) */ /* bubble movement boundaries */ -#define gbxmin (gx.xmin + 1) -#define gbymin (gy.ymin + 1) -#define gbxmax (gx.xmax - 1) -#define gbymax (gy.ymax - 1) +#define gbxmin (svx.xmin + 1) +#define gbymin (svy.ymin + 1) +#define gbxmax (svx.xmax - 1) +#define gbymax (svy.ymax - 1) + +/* the bubble hero is in */ +static struct bubble *hero_bubble = NULL; -static void set_wportal(void); -static void mk_bubble(coordxy, coordxy, int); -static void mv_bubble(struct bubble *, coordxy, coordxy, boolean); +staticfn void set_wportal(void); +staticfn void mk_bubble(coordxy, coordxy, int); +staticfn void mv_bubble(struct bubble *, coordxy, coordxy, boolean); +/* augment the Planes of Water (for bubbles) and Air (for clouds); called + from goto_level() when arriving and moveloop_core() when on the level */ void movebubbles(void) { - static const struct rm water_pos = { cmap_a_to_glyph(S_water), WATER, 0, 0, - 0, 0, 0, 0, 0, 0 }; - static const struct rm air_pos = { cmap_a_to_glyph(S_cloud), AIR, 0, 0, 0, - 1, 0, 0, 0, 0 }; + static const struct rm water_pos = { + cmap_b_to_glyph(S_water), WATER, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, air_pos = { + cmap_b_to_glyph(S_cloud), AIR, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 + }; static boolean up = FALSE; struct bubble *b; struct container *cons; @@ -1347,6 +1413,8 @@ movebubbles(void) vision_recalc(2); + hero_bubble = NULL; + if (Is_waterlevel(&u.uz)) { /* keep attached ball&chain separate from bubble objects */ if (Punished) @@ -1356,7 +1424,7 @@ movebubbles(void) * Pick up everything inside of a bubble then fill all bubble * locations. */ - for (b = up ? gb.bbubbles : ge.ebubbles; b; + for (b = up ? svb.bbubbles : ge.ebubbles; b; b = up ? b->next : b->prev) { if (b->cons) panic("movebubbles: cons != null"); @@ -1372,7 +1440,7 @@ movebubbles(void) if (OBJ_AT(x, y)) { struct obj *olist = (struct obj *) 0, *otmp; - while ((otmp = gl.level.objects[x][y]) != 0) { + while ((otmp = svl.level.objects[x][y]) != 0) { remove_object(otmp); otmp->ox = otmp->oy = 0; otmp->nexthere = olist; @@ -1417,6 +1485,7 @@ movebubbles(void) cons->next = b->cons; b->cons = cons; + hero_bubble = b; } if ((btrap = t_at(x, y)) != 0) { cons = (struct container *) alloc(sizeof *cons); @@ -1459,7 +1528,7 @@ movebubbles(void) * would eventually end up in the last bubble in the chain. */ up = !up; - for (b = up ? gb.bbubbles : ge.ebubbles; b; b = up ? b->next : b->prev) { + for (b = up ? svb.bbubbles : ge.ebubbles; b; b = up ? b->next : b->prev) { int rx = rn2(3), ry = rn2(3); mv_bubble(b, b->dx + 1 - (!b->dx ? rx : (rx ? 1 : 0)), @@ -1508,25 +1577,25 @@ water_friction(void) } void -save_waterlevel(NHFILE* nhfp) +save_waterlevel(NHFILE *nhfp) { struct bubble *b; - if (!gb.bbubbles) + if (!svb.bbubbles) return; if (perform_bwrite(nhfp)) { int n = 0; - for (b = gb.bbubbles; b; b = b->next) + 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) &gx.xmin, sizeof(int)); - bwrite(nhfp->fd, (genericptr_t) &gy.ymin, sizeof(int)); - bwrite(nhfp->fd, (genericptr_t) &gx.xmax, sizeof(int)); - bwrite(nhfp->fd, (genericptr_t) &gy.ymax, 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)); } - for (b = gb.bbubbles; b; b = b->next) { + for (b = svb.bbubbles; b; b = b->next) { if (nhfp->structlevel) bwrite(nhfp->fd, (genericptr_t) b, sizeof(struct bubble)); } @@ -1542,14 +1611,14 @@ restore_waterlevel(NHFILE *nhfp) struct bubble *b = (struct bubble *) 0, *btmp; int i, n = 0; - gb.bbubbles = (struct bubble *) 0; + svb.bbubbles = (struct bubble *) 0; set_wportal(); if (nhfp->structlevel) { mread(nhfp->fd,(genericptr_t) &n, sizeof (int)); - mread(nhfp->fd,(genericptr_t) &gx.xmin, sizeof (int)); - mread(nhfp->fd,(genericptr_t) &gy.ymin, sizeof (int)); - mread(nhfp->fd,(genericptr_t) &gx.xmax, sizeof (int)); - mread(nhfp->fd,(genericptr_t) &gy.ymax, 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)); } for (i = 0; i < n; i++) { btmp = b; @@ -1560,7 +1629,7 @@ restore_waterlevel(NHFILE *nhfp) btmp->next = b; b->prev = btmp; } else { - gb.bbubbles = b; + svb.bbubbles = b; b->prev = (struct bubble *) 0; } mv_bubble(b, 0, 0, TRUE); @@ -1570,7 +1639,7 @@ restore_waterlevel(NHFILE *nhfp) b->next = (struct bubble *) 0; } else { /* avoid "saving and reloading may fix this" */ - gp.program_state.something_worth_saving = 0; + program_state.something_worth_saving = 0; /* during restore, information about what level this is might not be available so we're wishy-washy about what we describe */ impossible("No %s to restore?", @@ -1579,13 +1648,13 @@ restore_waterlevel(NHFILE *nhfp) : (Is_airlevel(&u.uz) || Is_airlevel(&gu.uz_save)) ? "clouds" : "air bubbles or clouds"); - gp.program_state.something_worth_saving = 1; + program_state.something_worth_saving = 1; } } /* Set the global variable wportal to point to the magic portal on the current * level. */ -static void +staticfn void set_wportal(void) { /* there better be only one magic portal on water level... */ @@ -1595,7 +1664,7 @@ set_wportal(void) impossible("set_wportal(): no portal!"); } -static void +staticfn void setup_waterlevel(void) { int typ, glyph; @@ -1606,15 +1675,15 @@ setup_waterlevel(void) (int) u.uz.dnum, (int) u.uz.dlevel); /* ouch, hardcoded... (file scope statics and used in bxmin,bymax,&c) */ - gx.xmin = 3; - gy.ymin = 1; + svx.xmin = 3; + svy.ymin = 1; /* use separate statements so that compiler won't complain about min() comparing two constants; the alternative is to do this in the preprocessor: #if (20 > ROWNO-1) ymax=ROWNO-1 #else ymax=20 #endif */ - gx.xmax = 78; - gx.xmax = min(gx.xmax, (COLNO - 1) - 1); - gy.ymax = 20; - gy.ymax = min(gy.ymax, (ROWNO - 1)); + svx.xmax = 78; + svx.xmax = min(svx.xmax, (COLNO - 1) - 1); + svy.ymax = 20; + svy.ymax = min(svy.ymax, (ROWNO - 1)); /* entire level is remembered as one glyph and any unspecified portion should default to level's base element rather than to usual stone */ @@ -1643,20 +1712,20 @@ setup_waterlevel(void) mk_bubble(x, y, rn2(7)); } -static void +staticfn void unsetup_waterlevel(void) { struct bubble *b, *bb; /* free bubbles */ - for (b = gb.bbubbles; b; b = bb) { + for (b = svb.bbubbles; b; b = bb) { bb = b->next; free((genericptr_t) b); } - gb.bbubbles = ge.ebubbles = (struct bubble *) 0; + svb.bbubbles = ge.ebubbles = (struct bubble *) 0; } -static void +staticfn void mk_bubble(coordxy x, coordxy y, int n) { /* @@ -1699,8 +1768,8 @@ mk_bubble(coordxy x, coordxy y, int n) (void) memcpy((genericptr_t) b->bm, (genericptr_t) bmask[n], (bmask[n][1] + 2) * sizeof (b->bm[0])); b->cons = 0; - if (!gb.bbubbles) - gb.bbubbles = b; + if (!svb.bbubbles) + svb.bbubbles = b; if (ge.ebubbles) { ge.ebubbles->next = b; b->prev = ge.ebubbles; @@ -1711,6 +1780,22 @@ mk_bubble(coordxy x, coordxy y, int n) mv_bubble(b, 0, 0, TRUE); } +/* maybe change the movement direction of the bubble hero is in */ +void +maybe_adjust_hero_bubble(void) +{ + if (!Is_waterlevel(&u.uz)) + return; + + if (!u.dx && !u.dy) + return; + + if (hero_bubble && !rn2(2)) { + hero_bubble->dx = u.dx; + hero_bubble->dy = u.dy; + } +} + /* * The player, the portal and all other objects and monsters * float along with their associated bubbles. Bubbles may overlap @@ -1719,8 +1804,8 @@ mk_bubble(coordxy x, coordxy y, int n) * in the immediate neighborhood of one, he/she may get sucked inside. * This property also makes leaving a bubble slightly difficult. */ -static void -mv_bubble(struct bubble* b, coordxy dx, coordxy dy, boolean ini) +staticfn void +mv_bubble(struct bubble *b, coordxy dx, coordxy dy, boolean ini) { int i, j, colli = 0; coordxy x, y; @@ -1862,6 +1947,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; diff --git a/src/mkobj.c b/src/mkobj.c index ae7d632dcd..0385414362 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -1,35 +1,36 @@ -/* NetHack 3.7 mkobj.c $NHDT-Date: 1654881236 2022/06/10 17:13:56 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.237 $ */ +/* NetHack 3.7 mkobj.c $NHDT-Date: 1725138481 2024/08/31 21:08:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.304 $ */ /* 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" -static boolean may_generate_eroded(struct obj *); -static void mkobj_erosions(struct obj *); -static void mkbox_cnts(struct obj *); -static unsigned nextoid(struct obj *, struct obj *); -static int maybe_festive_fruit(void); -static void mksobj_init(struct obj *, boolean); -static int item_on_ice(struct obj *); -static void shrinking_glob_gone(struct obj *); -static void obj_timer_checks(struct obj *, coordxy, coordxy, int); -static void container_weight(struct obj *); -static struct obj *save_mtraits(struct obj *, struct monst *); -static void objlist_sanity(struct obj *, int, const char *); -static void shop_obj_sanity(struct obj *, const char *); -static void mon_obj_sanity(struct monst *, const char *); -static void insane_obj_bits(struct obj *, struct monst *); -static boolean nomerge_exception(struct obj *); -static const char *where_name(struct obj *); -static void insane_object(struct obj *, const char *, const char *, +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 int maybe_festive_fruit(void); +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); +staticfn void dealloc_obj_real(struct obj *); +staticfn struct obj *save_mtraits(struct obj *, struct monst *); +staticfn void objlist_sanity(struct obj *, int, const char *); +staticfn void shop_obj_sanity(struct obj *, const char *); +staticfn void mon_obj_sanity(struct monst *, const char *); +staticfn void insane_obj_bits(struct obj *, struct monst *); +staticfn boolean nomerge_exception(struct obj *); +staticfn const char *where_name(struct obj *); +staticfn void insane_object(struct obj *, const char *, const char *, struct monst *); -static void check_contained(struct obj *, const char *); -static void check_glob(struct obj *, const char *); -static void sanity_check_worn(struct obj *); -static void init_thiefstone(struct obj *); -static const struct icp* material_list(struct obj *); -static boolean invalid_obj_material(struct obj *, uchar); +staticfn void check_contained(struct obj *, const char *); +staticfn void check_glob(struct obj *, const char *); +staticfn void sanity_check_worn(struct obj *); +staticfn void init_oextra(struct oextra *); +staticfn void init_thiefstone(struct obj *); +staticfn const struct icp* material_list(struct obj *); +staticfn boolean invalid_obj_material(struct obj *, uchar); struct icp { int iprob; /* probability of an item type */ @@ -71,7 +72,7 @@ static const struct icp hellprobs[] = { { 20, WEAPON_CLASS }, static const struct oextra zerooextra = DUMMY; -static void +staticfn void init_oextra(struct oextra *oex) { *oex = zerooextra; @@ -168,11 +169,11 @@ free_omailcmd(struct obj *otmp) } /* can object be generated eroded? */ -static boolean +staticfn boolean may_generate_eroded(struct obj *otmp) { /* initial hero inventory */ - if (gm.moves <= 1 && !gi.in_mklev) + if (svm.moves <= 1 && !gi.in_mklev) return FALSE; /* already erodeproof or cannot be eroded */ if (otmp->oerodeproof || !erosion_matters(otmp) || !is_damageable(otmp)) @@ -187,7 +188,7 @@ may_generate_eroded(struct obj *otmp) } /* random chance of applying erosions/grease to object */ -static void +staticfn void mkobj_erosions(struct obj *otmp) { if (may_generate_eroded(otmp)) { @@ -197,7 +198,8 @@ mkobj_erosions(struct obj *otmp) if (!rn2(100)) { otmp->oerodeproof = 1; } else { - if (!rn2(80) && (is_flammable(otmp) || is_rustprone(otmp))) { + if (!rn2(80) && (is_flammable(otmp) || is_rustprone(otmp) + || is_crackable(otmp))) { do { otmp->oeroded++; } while (otmp->oeroded < 3 && !rn2(9)); @@ -215,6 +217,8 @@ mkobj_erosions(struct obj *otmp) } } +/* make a random object of class 'let' at a specific location; + 'let' might be random class; place_object() will validate */ struct obj * mkobj_at(char let, coordxy x, coordxy y, boolean artif) { @@ -225,10 +229,14 @@ mkobj_at(char let, coordxy x, coordxy y, boolean artif) return otmp; } -/* Similar to above, mksobj_at does not handle flooreffects. Use mksobj plus +/* make a specific object at a specific location + * Similar to above, mksobj_at does not handle flooreffects. Use mksobj plus * obj_drops_at to get flooreffects. */ struct obj * -mksobj_at(int otyp, coordxy x, coordxy y, boolean init, boolean artif) +mksobj_at( + int otyp, + coordxy x, coordxy y, + boolean init, boolean artif) { struct obj *otmp; @@ -237,12 +245,13 @@ mksobj_at(int otyp, coordxy x, coordxy y, boolean init, boolean artif) return otmp; } + +/* used for extra orctown loot */ struct obj * mksobj_migr_to_species( int otyp, - unsigned int mflags2, - boolean init, - boolean artif) + unsigned mflags2, + boolean init, boolean artif) { struct obj *otmp; @@ -273,28 +282,28 @@ mkobj(int oclass, boolean artif) if (oclass == SPBOOK_no_NOVEL) { /* Constant for when mkobj() should only produce an actual spellbook, not * some other sort of book like a novel. */ - i = rnd_class(gb.bases[SPBOOK_CLASS], SPE_BLANK_PAPER); + i = rnd_class(svb.bases[SPBOOK_CLASS], SPE_BLANK_PAPER); oclass = SPBOOK_CLASS; /* for sanity check below */ } else { prob = rnd(go.oclass_prob_totals[oclass]); - i = gb.bases[oclass]; + i = svb.bases[oclass]; while ((prob -= objects[i].oc_prob) > 0) ++i; } if (objects[i].oc_class != oclass || !OBJ_NAME(objects[i])) { impossible("probtype error, oclass=%d i=%d", (int) oclass, i); - i = gb.bases[oclass]; + i = svb.bases[oclass]; } return mksobj(i, TRUE, artif); } -static void +staticfn void mkbox_cnts(struct obj *box) { - register int n; - register struct obj *otmp; + int n; + struct obj *otmp; boolean is_bag = FALSE; box->cobj = (struct obj *) 0; @@ -312,10 +321,11 @@ mkbox_cnts(struct obj *box) case SACK: case OILSKIN_SACK: /* initial inventory: sack starts out empty */ - if (gm.moves <= 1 && !gi.in_mklev) { + if (svm.moves <= 1 && !gi.in_mklev) { n = 0; break; } + FALLTHROUGH; /*FALLTHRU*/ case BAG_OF_HOLDING: is_bag = TRUE; @@ -340,7 +350,7 @@ mkbox_cnts(struct obj *box) (void) stop_timer(SHRINK_GLOB, obj_to_any(otmp)); } } else { - register int tprob; + int tprob; const struct icp *iprobs = boxiprobs; if (is_bag && box->material == PAPER && !rn2(2) && n == 1) { @@ -381,6 +391,7 @@ mkbox_cnts(struct obj *box) } (void) add_to_container(box, otmp); } + /* caller will update box->owt */ } /* select a random, common monster type */ @@ -394,8 +405,8 @@ rndmonnum(void) int rndmonnum_adj(int minadj, int maxadj) { - register struct permonst *ptr; - register int i; + struct permonst *ptr; + int i; unsigned short excludeflags; /* Plan A: get a level-appropriate common monster */ @@ -426,6 +437,7 @@ copy_oextra(struct obj *obj2, struct obj *obj1) if (has_omonst(obj1)) { if (!OMONST(obj2)) newomonst(obj2); + assert(has_omonst(obj2)); (void) memcpy((genericptr_t) OMONST(obj2), (genericptr_t) OMONST(obj1), sizeof (struct monst)); OMONST(obj2)->mextra = (struct mextra *) 0; @@ -447,7 +459,7 @@ copy_oextra(struct obj *obj2, struct obj *obj1) } /* - * Split obj so that it gets size gets reduced by num. The quantity num is + * Split stack so that its size gets reduced by num. The quantity num is * put in the object structure delivered by this call. The returned object * has its wornmask cleared and is positioned just following the original * in the nobj chain (and nexthere chain when on the floor). @@ -473,8 +485,8 @@ splitobj(struct obj *obj, long num) otmp->lua_ref_cnt = 0; otmp->pickup_prev = 0; - gc.context.objsplit.parent_oid = obj->o_id; - gc.context.objsplit.child_oid = otmp->o_id; + svc.context.objsplit.parent_oid = obj->o_id; + svc.context.objsplit.child_oid = otmp->o_id; obj->nobj = otmp; /* Only set nexthere when on the floor; nexthere is also used as a back pointer to the container object when contained. @@ -504,7 +516,7 @@ splitobj(struct obj *obj, long num) unsigned next_ident(void) { - unsigned res = gc.context.ident; + unsigned res = svc.context.ident; /* +rnd(2): originally just +1; changed to rnd() to avoid potential exploit of player using #adjust to split an object stack in a manner @@ -514,25 +526,25 @@ next_ident(void) next object to be created was knowable and player could make a wish under controlled circumstances for an item that is affected by the low bits of its obj->o_id [particularly helm of opposite alignment] */ - gc.context.ident += rnd(2); /* ready for next new object or monster */ + svc.context.ident += rnd(2); /* ready for next new object or monster */ /* if ident has wrapped to 0, force it to be non-zero; if/when it ever wraps past 0 (unlikely, but possible on a configuration which uses 16-bit 'int'), just live with that and hope no o_id conflicts between objects or m_id conflicts between monsters arise */ - if (!gc.context.ident) - gc.context.ident = rnd(2); + if (!svc.context.ident) + svc.context.ident = rnd(2) + 1; /* id 1 is reserved */ return res; } /* when splitting a stack that has o_id-based shop prices, pick an o_id value for the new stack that will maintain the same price */ -static unsigned +staticfn unsigned nextoid(struct obj *oldobj, struct obj *newobj) { int olddif, newdif, trylimit = 256; /* limit of 4 suffices at present */ - unsigned oid = gc.context.ident - 1; /* loop increment will reverse -1 */ + unsigned oid = svc.context.ident - 1; /* loop increment will reverse -1 */ olddif = oid_price_adjustment(oldobj, oldobj->o_id); do { @@ -541,7 +553,7 @@ nextoid(struct obj *oldobj, struct obj *newobj) ++oid; newdif = oid_price_adjustment(newobj, oid); } while (newdif != olddif && --trylimit >= 0); - gc.context.ident = oid; /* update 'last ident used' */ + svc.context.ident = oid; /* update 'last ident used' */ (void) next_ident(); /* increment context.ident for next use */ return oid; /* caller will use this ident */ } @@ -582,17 +594,17 @@ unsplitobj(struct obj *obj) } /* first try the expected case; obj is split from another stack */ - if (obj->o_id == gc.context.objsplit.child_oid) { + if (obj->o_id == svc.context.objsplit.child_oid) { /* parent probably precedes child and will require list traversal */ ochild = obj; - target_oid = gc.context.objsplit.parent_oid; + target_oid = svc.context.objsplit.parent_oid; if (obj->nobj && obj->nobj->o_id == target_oid) oparent = obj->nobj; - } else if (obj->o_id == gc.context.objsplit.parent_oid) { + } else if (obj->o_id == svc.context.objsplit.parent_oid) { /* alternate scenario: another stack was split from obj; child probably follows parent and will be found here */ oparent = obj; - target_oid = gc.context.objsplit.child_oid; + target_oid = svc.context.objsplit.child_oid; if (obj->nobj && obj->nobj->o_id == target_oid) ochild = obj->nobj; } @@ -621,7 +633,7 @@ unsplitobj(struct obj *obj) void clear_splitobjs(void) { - gc.context.objsplit.parent_oid = gc.context.objsplit.child_oid = 0; + svc.context.objsplit.parent_oid = svc.context.objsplit.child_oid = 0; } /* @@ -630,7 +642,7 @@ clear_splitobjs(void) * the caller to provide a valid context for the swap. When done, obj will * still exist, but not on any chain. * - * Note: Don't use use obj_extract_self() -- we are doing an in-place swap, + * Note: Don't use obj_extract_self() -- we are doing an in-place swap, * not actually moving something. */ void @@ -669,7 +681,7 @@ replace_object(struct obj *obj, struct obj *otmp) obj->nobj = otmp; obj->nexthere = otmp; extract_nobj(obj, &fobj); - extract_nexthere(obj, &gl.level.objects[obj->ox][obj->oy]); + extract_nexthere(obj, &svl.level.objects[obj->ox][obj->oy]); break; default: panic("replace_object: obj position"); @@ -710,11 +722,11 @@ unknwn_contnr_contents(struct obj *obj) void bill_dummy_object(struct obj *otmp) { - register struct obj *dummy; + struct obj *dummy; long cost = 0L; if (otmp->unpaid) { - cost = unpaid_cost(otmp, FALSE); + cost = unpaid_cost(otmp, COST_SINGLEOBJ); subfrombill(otmp, shop_keeper(*u.ushops)); } dummy = newobj(); @@ -725,12 +737,12 @@ bill_dummy_object(struct obj *otmp) dummy->timed = 0; copy_oextra(dummy, otmp); if (has_omid(dummy)) - free_omid(dummy); /* only one association with m_id*/ + free_omid(dummy); /* only one association with m_id */ if (Is_candle(dummy)) dummy->lamplit = 0; dummy->owornmask = 0L; /* dummy object is not worn */ addtobill(dummy, FALSE, TRUE, TRUE); - if (cost) + if (cost && dummy->where != OBJ_DELETED) alter_cost(dummy, -cost); /* no_charge is only valid for some locations */ otmp->no_charge = (otmp->where == OBJ_FLOOR @@ -743,7 +755,8 @@ bill_dummy_object(struct obj *otmp) static const char *const alteration_verbs[] = { "cancel", "drain", "uncharge", "unbless", "uncurse", "disenchant", "degrade", "dilute", "erase", "burn", "neutralize", "destroy", "splatter", - "bite", "open", "break the lock on", "rust", "rot", "tarnish", "ferment" + "bite", "open", "break the lock on", "rust", "rot", "tarnish", "crack", + "ferment", }; /* possibly bill for an object which the player has just modified */ @@ -854,6 +867,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] */ @@ -866,7 +880,7 @@ unknow_object(struct obj *obj) * If it's not a holiday for which there are special fruits, return the current * fruit index. Caller should be able to set spe of a slime mold with this * return value without any conditions. */ -static int +staticfn int maybe_festive_fruit(void) { int holiday = current_holidays(); @@ -874,7 +888,7 @@ maybe_festive_fruit(void) int idx = 0; if (!gi.in_mklev) - return gc.context.current_fruit; + return svc.context.current_fruit; if (holiday & HOLIDAY_NEW_YEARS && Role_if(PM_SAMURAI)) { @@ -952,12 +966,11 @@ maybe_festive_fruit(void) return fruitadd(foodbuf, NULL); } /* no active holidays with fruits */ - return gc.context.current_fruit; + return svc.context.current_fruit; } -/* do some initialization to a newly created object. - object otyp must be set. */ -static void +/* do some initialization to newly created object; otyp must already be set */ +staticfn void mksobj_init(struct obj *otmp, boolean artif) { int mndx, tryct; @@ -979,7 +992,7 @@ mksobj_init(struct obj *otmp, boolean artif) otmp->opoisoned = 1; if (artif && !rn2(20 + (10 * nartifact_exist()))) - otmp = mk_artifact(otmp, (aligntyp) A_NONE); + otmp = mk_artifact(otmp, (aligntyp) A_NONE, 99, TRUE); /* check oartifact here because mk_artifact isn't guaranteed to * create an artifact */ @@ -996,11 +1009,11 @@ mksobj_init(struct obj *otmp, boolean artif) tryct = 50; do otmp->corpsenm = undead_to_corpse(rndmonnum()); - while ((gm.mvitals[otmp->corpsenm].mvflags & G_NOCORPSE) + while ((svm.mvitals[otmp->corpsenm].mvflags & G_NOCORPSE) && (--tryct > 0)); if (tryct == 0) { /* perhaps rndmonnum() only wants to make G_NOCORPSE - monsters on this gl.level; create an adventurer's + monsters on this svl.level; create an adventurer's corpse instead, then */ otmp->corpsenm = PM_HUMAN; } @@ -1027,7 +1040,7 @@ mksobj_init(struct obj *otmp, boolean artif) for (tryct = 200; tryct > 0; --tryct) { mndx = undead_to_corpse(rndmonnum()); if (mons[mndx].cnutrit - && !(gm.mvitals[mndx].mvflags & G_NOCORPSE)) { + && !(svm.mvitals[mndx].mvflags & G_NOCORPSE)) { otmp->corpsenm = mndx; set_tin_variety(otmp, RANDOM_TIN); break; @@ -1062,8 +1075,7 @@ mksobj_init(struct obj *otmp, boolean artif) need to perform any fix up and returns glob->owt as-is */ otmp->owt = objects[otmp->otyp].oc_weight; otmp->known = otmp->dknown = 1; - otmp->corpsenm = PM_GRAY_OOZE - + (otmp->otyp - GLOB_OF_GRAY_OOZE); + otmp->corpsenm = PM_GRAY_OOZE + (otmp->otyp - GLOB_OF_GRAY_OOZE); start_glob_timeout(otmp, 0L); } else { if (otmp->otyp != CORPSE && otmp->otyp != MEAT_RING @@ -1112,6 +1124,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: @@ -1158,7 +1172,7 @@ mksobj_init(struct obj *otmp, boolean artif) break; case AMULET_CLASS: if (otmp->otyp == AMULET_OF_YENDOR) - gc.context.made_amulet = TRUE; + svc.context.made_amulet = TRUE; if (rn2(10) && (otmp->otyp == AMULET_OF_STRANGULATION || otmp->otyp == AMULET_OF_CHANGE || otmp->otyp == AMULET_OF_RESTFUL_SLEEP)) { @@ -1195,10 +1209,10 @@ mksobj_init(struct obj *otmp, boolean artif) } else blessorcurse(otmp, 10); if (artif && !rn2(40 + (10 * nartifact_exist()))) - otmp = mk_artifact(otmp, (aligntyp) A_NONE); + otmp = mk_artifact(otmp, (aligntyp) A_NONE, 99, TRUE); /* simulate lacquered armor for samurai */ if (Role_if(PM_SAMURAI) && otmp->otyp == SPLINT_MAIL - && (gm.moves <= 1 || In_quest(&u.uz))) { + && (svm.moves <= 1 || In_quest(&u.uz))) { #ifdef UNIXPC /* optimizer bitfield bug */ otmp->oerodeproof = 1; @@ -1212,7 +1226,8 @@ mksobj_init(struct obj *otmp, boolean artif) if (otmp->otyp == WAN_WISHING) otmp->spe = 1; else - otmp->spe = rn1(5, (objects[otmp->otyp].oc_dir == NODIR) ? 11 : 4); + otmp->spe = rn1(5, + (objects[otmp->otyp].oc_dir == NODIR) ? 11 : 4); blessorcurse(otmp, 17); otmp->recharged = 0; /* used to control recharging */ break; @@ -1258,7 +1273,7 @@ mksobj_init(struct obj *otmp, boolean artif) otmp->corpsenm = rndmonnum(); if (!verysmall(&mons[otmp->corpsenm]) && rn2(level_difficulty() / 2 + 10) > 10) - (void) add_to_container(otmp, + (void) add_to_container(otmp, /* caller will update owt */ mkobj(SPBOOK_no_NOVEL, FALSE)); } /* boulder init'd below in the 'regardless of !init' code */ @@ -1286,7 +1301,7 @@ mksobj(int otyp, boolean init, boolean artif) otmp = newobj(); *otmp = cg.zeroobj; - otmp->age = gm.moves; + otmp->age = max(svm.moves, 1L); otmp->o_id = next_ident(); otmp->quan = 1L; otmp->oclass = let; @@ -1308,9 +1323,10 @@ mksobj(int otyp, boolean init, boolean artif) case CORPSE: if (otmp->corpsenm == NON_PM) { otmp->corpsenm = undead_to_corpse(rndmonnum()); - if (gm.mvitals[otmp->corpsenm].mvflags & (G_NOCORPSE | G_GONE)) + if (svm.mvitals[otmp->corpsenm].mvflags & (G_NOCORPSE | G_GONE)) otmp->corpsenm = gu.urole.mnum; } + FALLTHROUGH; /*FALLTHRU*/ case STATUE: case FIGURINE: @@ -1324,6 +1340,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: */ @@ -1337,6 +1354,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 */ @@ -1352,7 +1370,7 @@ 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); + otmp = mk_artifact(otmp, (aligntyp) A_NONE, 99, FALSE); otmp->owt = weight(otmp); return otmp; } @@ -1468,7 +1486,7 @@ start_corpse_timeout(struct obj *body) action = ROT_CORPSE; /* default action: rot away */ rot_adjust = gi.in_mklev ? 25 : 10; /* give some variation */ - age = gm.moves - body->age; + age = max(svm.moves, 1) - body->age; if (age > ROT_AGE) when = rot_adjust; else @@ -1527,7 +1545,7 @@ enum obj_on_ice { }; /* used by shrink_glob(); is 'item' or enclosing container on or under ice? */ -static int +staticfn int item_on_ice(struct obj *item) { struct obj *otmp; @@ -1612,9 +1630,9 @@ shrink_glob( /* * If shrinkage occurred while we were on another level, catch up now. */ - if (expire_time < gm.moves && globloc != BURIED_UNDER_ICE) { + if (expire_time < svm.moves && globloc != BURIED_UNDER_ICE) { /* number of units of weight to remove */ - long delta = (gm.moves - expire_time + 24L) / 25L, + long delta = (svm.moves - expire_time + 24L) / 25L, /* leftover amount to use for new timer */ moddelta = 25L - (delta % 25L); @@ -1653,7 +1671,7 @@ shrink_glob( */ if (eating_glob(obj) || globloc == BURIED_UNDER_ICE - || (globloc == SET_ON_ICE && (gm.moves % 3L) == 1L)) { + || (globloc == SET_ON_ICE && (svm.moves % 3L) == 1L)) { /* schedule next shrink attempt; for the being eaten case, the glob and its timer might be deleted before this kicks in */ start_glob_timeout(obj, 0L); @@ -1662,7 +1680,7 @@ shrink_glob( /* format "Your/Shk's/The [partly eaten] glob of " into globnambuf[] before shrinking the glob; Yname2() calls yname() - which calls xname() which ordinarly leaves "partly eaten" to + which calls xname() which ordinarily leaves "partly eaten" to doname() rather than inserting that itself; ask xname() to add that when appropriate */ iflags.partly_eaten_hack = TRUE; @@ -1757,7 +1775,7 @@ shrink_glob( } /* a glob has shrunk away to nothing; handle owornmask, then delete glob */ -static void +staticfn void shrinking_glob_gone(struct obj *obj) { xint16 owhere = obj->where; @@ -1994,7 +2012,7 @@ set_bknown( { if (obj->bknown != onoff) { obj->bknown = onoff; - if (obj->where == OBJ_INVENT && gm.moves > 1L) + if (obj->where == OBJ_INVENT && svm.moves > 1L) update_inventory(); } } @@ -2077,11 +2095,27 @@ weight(struct obj *obj) } if (Is_container(obj) || obj->otyp == STATUE) { struct obj *contents; - register int cwt = 0; - - if (obj->otyp == STATUE && obj->corpsenm >= LOW_PM) - wt = (int) obj->quan * ((int) mons[obj->corpsenm].cwt * 3 / 2); + int cwt; + + if (obj->otyp == STATUE && ismnum(obj->corpsenm)) { + int msize = (int) mons[obj->corpsenm].msize, /* 0..7 */ + minwt = (msize + msize + 1) * 100; + + /* default statue weight is 1.5 times corpse weight */ + wt = 3 * (int) mons[obj->corpsenm].cwt / 2; + /* some monsters that never leave a corpse when they die have + corpse weight defined as 0; statues resembling them need to + have non-zero weight; others are so tiny (killer bee) that + they weigh barely more than nothing or so insubstantial + (wraith) that they actually weigh nothing; statues of such + need more heft */ + if (wt < minwt) + wt = minwt; + /* this has no effect because statues don't stack */ + wt *= (int) obj->quan; + } + cwt = 0; /* contents weight */ for (contents = obj->cobj; contents; contents = contents->nobj) cwt += weight(contents); /* @@ -2105,7 +2139,7 @@ weight(struct obj *obj) return wt + cwt; } - if (obj->otyp == CORPSE && obj->corpsenm >= LOW_PM) { + if (obj->otyp == CORPSE && ismnum(obj->corpsenm)) { long long_wt = obj->quan * (long) mons[obj->corpsenm].cwt; wt = (long_wt > LARGEST_INT) ? LARGEST_INT : (int) long_wt; @@ -2182,7 +2216,7 @@ static const int treefruits[] = { struct obj * rnd_treefruit_at(coordxy x, coordxy y) { - return mksobj_at(treefruits[rn2(SIZE(treefruits))], x, y, TRUE, FALSE); + return mksobj_at(ROLL_FROM(treefruits), x, y, TRUE, FALSE); } /* create a stack of N gold pieces; never returns Null */ @@ -2206,6 +2240,34 @@ mkgold(long amount, coordxy x, coordxy y) return gold; } +/* potions of oil use their obj->age field differently from other potions + so changing potion type to or from oil needs to have that fixed up */ +void +fixup_oil( + struct obj *potion, /* potion that just had its otyp changed */ + struct obj *source) /* item used to create potion; might be Null */ +{ + if (potion->otyp == POT_OIL) { + if (source && source->otyp == POT_OIL) { + /* potion of oil being used to set potion's otyp to oil; + source might be partly used */ + potion->age = source->age; + } else { + /* non-oil is being turned into oil; change absolute age + (turn created) into relative age (amount remaining / + burn time available) */ + potion->age = MAX_OIL_IN_FLASK; + } + } else if (source && source->otyp == POT_OIL) { + /* potion is no longer oil, being turned into non-oil */ + if (potion->age == source->age) + potion->age = svm.moves; + /* when source is a partly used oil, mark potion as diluted */ + if (source->age < MAX_OIL_IN_FLASK) + potion->odiluted = 1; + } +} + /* return TRUE if the corpse has special timing; lizards and lichen don't rot, trolls and Riders and zombies auto-revive */ #define special_corpse(num) \ @@ -2269,8 +2331,9 @@ mkcorpstat( otmp->corpsenm = monsndx(ptr); otmp->owt = weight(otmp); - if (otmp->otyp == CORPSE && (gz.zombify || special_corpse(old_corpsenm) - || special_corpse(otmp->corpsenm))) { + if (otmp->otyp == CORPSE + && (gz.zombify || special_corpse(old_corpsenm) + || special_corpse(otmp->corpsenm))) { obj_stop_timers(otmp); if (mtmp && is_reviver(mtmp->data) && !is_rider(mtmp->data) && mtmp->mcan) { @@ -2318,7 +2381,7 @@ obj_attach_mid(struct obj *obj, unsigned int mid) return obj; } -static struct obj * +staticfn struct obj * save_mtraits(struct obj *obj, struct monst *mtmp) { if (mtmp->ispriest) @@ -2326,13 +2389,12 @@ save_mtraits(struct obj *obj, struct monst *mtmp) if (!has_omonst(obj)) newomonst(obj); if (has_omonst(obj)) { - int baselevel = mtmp->data->mlevel; + int baselevel = mtmp->data->mlevel; /* mtmp->data is valid ptr */ struct monst *mtmp2 = OMONST(obj); *mtmp2 = *mtmp; mtmp2->mextra = (struct mextra *) 0; - if (mtmp->data) - mtmp2->mnum = monsndx(mtmp->data); + mtmp2->mnum = monsndx(mtmp->data); /* invalidate pointers */ /* m_id is needed to know if this is a revived quest leader */ /* but m_id must be cleared when loading bones */ @@ -2465,10 +2527,10 @@ is_rottable(struct obj *otmp) void place_object(struct obj *otmp, coordxy x, coordxy y) { - register struct obj *otmp2; + struct obj *otmp2; #ifdef FUZZER_LOG fuzl_xyi("place_object", x,y, otmp->otyp); -#endif // TODO!!!!!!!!!! WRAPPING ALL FUZZER LOGS IN THIS +#endif if (!isok(x, y)) { /* validate location */ void (*func)(const char *, ...) PRINTF_F_PTR(1, 2); @@ -2488,7 +2550,8 @@ place_object(struct obj *otmp, coordxy x, coordxy y) panic("place_object: obj \"%s\" [%d] not free", safe_typename(otmp->otyp), otmp->where); - otmp2 = gl.level.objects[x][y]; + assert(x >= 0 && x < COLNO && y >= 0 && y < ROWNO); + otmp2 = svl.level.objects[x][y]; obj_no_longer_held(otmp); if (otmp->otyp == BOULDER) { @@ -2508,7 +2571,7 @@ place_object(struct obj *otmp, coordxy x, coordxy y) } else { /* put on top of current pile */ otmp->nexthere = otmp2; - gl.level.objects[x][y] = otmp; + svl.level.objects[x][y] = otmp; } /* set the object's new location */ @@ -2536,7 +2599,7 @@ recreate_pile_at(coordxy x, coordxy y) struct obj *otmp, *next_obj, *reversed = 0; /* remove all objects at , saving a reversed temporary list */ - for (otmp = gl.level.objects[x][y]; otmp; otmp = next_obj) { + for (otmp = svl.level.objects[x][y]; otmp; otmp = next_obj) { next_obj = otmp->nexthere; remove_object(otmp); /* obj_extract_self() for floor */ otmp->nobj = reversed; @@ -2561,12 +2624,12 @@ obj_ice_effects(coordxy x, coordxy y, boolean do_buried) { struct obj *otmp; - for (otmp = gl.level.objects[x][y]; otmp; otmp = otmp->nexthere) { + for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp->nexthere) { if (otmp->timed) obj_timer_checks(otmp, x, y, 0); } if (do_buried) { - for (otmp = gl.level.buriedobjlist; otmp; otmp = otmp->nobj) { + for (otmp = svl.level.buriedobjlist; otmp; otmp = otmp->nobj) { if (otmp->ox == x && otmp->oy == y) { if (otmp->timed) obj_timer_checks(otmp, x, y, 0); @@ -2589,17 +2652,17 @@ peek_at_iced_corpse_age(struct obj *otmp) if (otmp->otyp == CORPSE && otmp->on_ice) { /* Adjust the age; must be same as obj_timer_checks() for off ice*/ - age = gm.moves - otmp->age; + age = svm.moves - otmp->age; retval += age * (ROT_ICE_ADJUSTMENT - 1) / ROT_ICE_ADJUSTMENT; debugpline3( "The %s age has ice modifications: otmp->age = %ld, returning %ld.", s_suffix(doname(otmp)), otmp->age, retval); - debugpline1("Effective age of corpse: %ld.", gm.moves - retval); + debugpline1("Effective age of corpse: %ld.", svm.moves - retval); } return retval; } -static void +staticfn void obj_timer_checks( struct obj *otmp, coordxy x, coordxy y, @@ -2627,8 +2690,8 @@ obj_timer_checks( /* mark the corpse as being on ice */ otmp->on_ice = 1; - debugpline3("%s is now on ice at <%d,%d>.", The(xname(otmp)), x, - y); + debugpline3("%s is now on ice at <%d,%d>.", + The(xname(otmp)), x, y); /* Adjust the time remaining */ tleft *= ROT_ICE_ADJUSTMENT; restart_timer = TRUE; @@ -2637,8 +2700,8 @@ obj_timer_checks( later calculations behave as if it had been on ice during that time (longwinded way of saying this is the inverse of removing it from the ice and of peeking at its age). */ - age = gm.moves - otmp->age; - otmp->age = gm.moves - (age * ROT_ICE_ADJUSTMENT); + age = svm.moves - otmp->age; + otmp->age = svm.moves - (age * ROT_ICE_ADJUSTMENT); } /* Check for corpses coming off ice */ @@ -2663,7 +2726,7 @@ obj_timer_checks( tleft /= ROT_ICE_ADJUSTMENT; restart_timer = TRUE; /* Adjust the age */ - age = gm.moves - otmp->age; + age = svm.moves - otmp->age; otmp->age += age * (ROT_ICE_ADJUSTMENT - 1) / ROT_ICE_ADJUSTMENT; } } @@ -2683,11 +2746,10 @@ remove_object(struct obj *otmp) if (otmp->where != OBJ_FLOOR) panic("remove_object: obj not on floor"); - extract_nexthere(otmp, &gl.level.objects[x][y]); + 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); } @@ -2709,7 +2771,8 @@ discard_minvent(struct monst *mtmp, boolean uncreate_artifacts) /* * Free obj from whatever list it is on in preparation for deleting it - * or moving it elsewhere; obj->where will end up set to OBJ_FREE. + * or moving it elsewhere; obj->where will end up set to OBJ_FREE unless + * it is already OBJ_LUAFREE or OBJ_DELETED. * Doesn't handle unwearing of objects in hero's or monsters' inventories. * * Object positions: @@ -2722,6 +2785,7 @@ discard_minvent(struct monst *mtmp, boolean uncreate_artifacts) * OBJ_BURIED level.buriedobjs chain * OBJ_ONBILL on gb.billobjs chain * OBJ_LUAFREE obj is dealloc'd from core, but still used by lua + * OBJ_DELETED obj has been deleted from play but not yet deallocated * OBJ_INTRAP obj is in a trap as ammo (use extract_nobj instead) */ void @@ -2730,6 +2794,7 @@ obj_extract_self(struct obj *obj) switch (obj->where) { case OBJ_FREE: case OBJ_LUAFREE: + case OBJ_DELETED: break; case OBJ_FLOOR: remove_object(obj); @@ -2750,7 +2815,7 @@ obj_extract_self(struct obj *obj) extract_nobj(obj, &gm.migrating_objs); break; case OBJ_BURIED: - extract_nobj(obj, &gl.level.buriedobjlist); + extract_nobj(obj, &svl.level.buriedobjlist); break; case OBJ_ONBILL: extract_nobj(obj, &gb.billobjs); @@ -2845,6 +2910,10 @@ add_to_minv(struct monst *mon, struct obj *obj) /* * Add obj to container, make sure obj is "free". Returns (merged) obj. * The input obj may be deleted in the process. + * + * Caveat: this does not update the container's weight [possibly to + * prevent that from being recalculated repeatedly when adding multiple + * items]. */ struct obj * add_to_container(struct obj *container, struct obj *obj) @@ -2897,36 +2966,42 @@ add_to_buried(struct obj *obj) panic("add_to_buried: obj not free"); obj->where = OBJ_BURIED; - obj->nobj = gl.level.buriedobjlist; - gl.level.buriedobjlist = obj; + obj->nobj = svl.level.buriedobjlist; + svl.level.buriedobjlist = obj; } -/* Recalculate the weight of this container and all of _its_ containers. */ -static void -container_weight(struct obj *container) +/* recalculate weight of object, which doesn't have to be a container + itself; if it is contained, recursively handle _its_ container(s) */ +void +container_weight(struct obj *object) { - container->owt = weight(container); - if (container->where == OBJ_CONTAINED) - container_weight(container->ocontainer); - /* - else if (container->where == OBJ_INVENT) - recalculate load delay here ??? - */ + object->owt = weight(object); + if (object->where == OBJ_CONTAINED) + container_weight(object->ocontainer); } /* - * Deallocate the object. _All_ objects should be run through here for - * them to be deallocated. + * Mark object to be deallocated. _All_ objects should be run through here + * for them to be deallocated. */ void dealloc_obj(struct obj *obj) { - if (obj->where != OBJ_FREE && obj->where != OBJ_LUAFREE) - panic("dealloc_obj: obj not free"); + if (obj->where == OBJ_DELETED) { + impossible("dealloc_obj: obj already deleted (type=%d)", obj->otyp); + return; + } else if (obj->where != OBJ_FREE && obj->where != OBJ_LUAFREE) { + panic("dealloc_obj: obj not free (type=%d, where=%d)", + obj->otyp, obj->where); + } if (obj->nobj) panic("dealloc_obj with nobj"); if (obj->cobj) panic("dealloc_obj with cobj"); + if (obj == &hands_obj) { + impossible("dealloc_obj with hands_obj"); + return; + } /* free up any timers attached to the object */ if (obj->timed) @@ -2949,14 +3024,34 @@ 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() */ + if (obj->o_id == svc.context.objsplit.parent_oid + || obj->o_id == svc.context.objsplit.child_oid) + svc.context.objsplit.parent_oid = svc.context.objsplit.child_oid = 0; - if (obj->oextra) - dealloc_oextra(obj); if (obj->lua_ref_cnt) { /* obj is referenced from a lua script, let lua gc free it */ 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; +} + +/* actually deallocate the object */ +staticfn void +dealloc_obj_real(struct obj *obj) +{ + if (obj->oextra) + dealloc_oextra(obj); /* clear out of date information contained in the about-to-become stale memory so that potential used-after-freed bugs (should never @@ -2967,6 +3062,22 @@ dealloc_obj(struct obj *obj) free((genericptr_t) obj); } +/* free all the objects marked for deletion */ +void +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; + } +} + /* create an object from a horn of plenty; mirrors bagotricks(makemon.c) */ int hornoplenty( @@ -2991,10 +3102,14 @@ hornoplenty( consume_obj_charge(horn, !tipping); if (!rn2(13)) { obj = mkobj(POTION_CLASS, FALSE); - if (objects[obj->otyp].oc_magic) + if (objects[obj->otyp].oc_magic) { do { obj->otyp = rnd_class(POT_BOOZE, POT_WATER); } while (obj->otyp == POT_SICKNESS); + /* oil uses obj->age field differently from other potions */ + if (obj->otyp == POT_OIL) + fixup_oil(obj, (struct obj *) NULL); + } what = (obj->quan > 1L) ? "Some potions" : "A potion"; } else { obj = mkobj(FOOD_CLASS, FALSE); @@ -3081,7 +3196,7 @@ obj_sanity_check(void) for (y = 0; y < ROWNO; y++) { char at_fmt[BUFSZ]; - otop = gl.level.objects[x][y]; + otop = svl.level.objects[x][y]; prevo = 0; for (obj = otop; obj; prevo = obj, obj = prevo->nexthere) { /* should match ; <0,*> should always be empty */ @@ -3115,8 +3230,9 @@ obj_sanity_check(void) objlist_sanity(gi.invent, OBJ_INVENT, "invent sanity"); objlist_sanity(gm.migrating_objs, OBJ_MIGRATING, "migrating sanity"); - objlist_sanity(gl.level.buriedobjlist, OBJ_BURIED, "buried sanity"); + objlist_sanity(svl.level.buriedobjlist, OBJ_BURIED, "buried sanity"); objlist_sanity(gb.billobjs, OBJ_ONBILL, "bill sanity"); + objlist_sanity(go.objs_deleted, OBJ_DELETED, "deleted object sanity"); mon_obj_sanity(fmon, "minvent sanity"); mon_obj_sanity(gm.migrating_mons, "migrating minvent sanity"); @@ -3148,7 +3264,7 @@ obj_sanity_check(void) } /* sanity check for objects on specified list (fobj, &c) */ -static void +staticfn void objlist_sanity(struct obj *objlist, int wheretype, const char *mesg) { struct obj *obj; @@ -3156,6 +3272,13 @@ objlist_sanity(struct obj *objlist, int wheretype, const char *mesg) for (obj = objlist; obj; obj = obj->nobj) { if (obj->where != wheretype) insane_object(obj, ofmt0, mesg, (struct monst *) 0); + if (obj->where == OBJ_INVENT && obj->how_lost != LOST_NONE) { + char lostbuf[40]; + + /* %d: bitfield is unsigned but narrow, so promotes to int */ + Sprintf(lostbuf, "how_lost=%d obj in inventory!", obj->how_lost); + insane_object(obj, ofmt0, lostbuf, (struct monst *) 0); + } if (Has_contents(obj)) { if (wheretype == OBJ_ONBILL) /* containers on shop bill should always be empty */ @@ -3183,6 +3306,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) { @@ -3194,6 +3318,42 @@ objlist_sanity(struct obj *objlist, int wheretype, const char *mesg) break; } } + if (obj->otyp == LEASH && obj->leashmon) { + char buf[BUFSZ]; + struct monst *mtmp = find_mid(obj->leashmon, FM_FMON); + + if (obj->where == OBJ_INVENT) { + if (!mtmp) { /* found leash with phantom mon */ + Sprintf(buf, "leashmon=%u no monst,", + (unsigned) obj->leashmon); + insane_object(obj, ofmt0, buf, (struct monst *) 0); + } else if (!mtmp->mleashed) { /* found leashed mon + * not flagged as leashed */ + Sprintf(buf, "leashmon=%u %s not leashed,", + (unsigned) obj->leashmon, mon_pmname(mtmp)); + insane_object(obj, ofmt0, buf, (struct monst *) 0); + } + + /* have to explicitly exclude migrating_objs because the + obj->migr_species field overlays obj->corpsenm just like + obj->leashmon does, so obj->leashmon and consequently 'mtmp' + might be inaccurate for any leash found on migrating_objs */ + } else if (obj->where != OBJ_MIGRATING) { + struct monst *mtmp2 = (obj->where == OBJ_MINVENT) + ? obj->ocarry : (struct monst *) 0; + + if (mtmp) { /* found monst leashed by non-invent leash */ + Sprintf(buf, "leashmon:%u %s leashed by %s leash,", + (unsigned) obj->leashmon, + mon_pmname(mtmp), where_name(obj)); + insane_object(obj, ofmt0, buf, mtmp2); + } else { /* found non-invent leash with m_id of phantom mon */ + Sprintf(buf, "leashmon:%u no monst for %s leash,", + (unsigned) obj->leashmon, where_name(obj)); + insane_object(obj, ofmt0, buf, mtmp2); + } + } + } if (obj->globby) check_glob(obj, mesg); /* temporary flags that might have been set but which should @@ -3212,7 +3372,7 @@ objlist_sanity(struct obj *objlist, int wheretype, const char *mesg) /* check obj->unpaid and obj->no_charge for shop sanity; caller has verified that at least one of them is set */ -static void +staticfn void shop_obj_sanity(struct obj *obj, const char *mesg) { struct obj *otop; @@ -3282,7 +3442,7 @@ shop_obj_sanity(struct obj *obj, const char *mesg) } /* sanity check for objects carried by all monsters in specified list */ -static void +staticfn void mon_obj_sanity(struct monst *monlist, const char *mesg) { struct monst *mon; @@ -3315,15 +3475,21 @@ mon_obj_sanity(struct monst *monlist, const char *mesg) } } -static void +staticfn void insane_obj_bits(struct obj *obj, struct monst *mon) { - unsigned o_in_use = obj->in_use, o_bypass = obj->bypass, - /* having obj->nomerge be set might be intentional */ - o_nomerge = (obj->nomerge && !nomerge_exception(obj)), - /* next_boulder is only for object name formatting when - pushing boulders and should be reset by next sanity check */ - o_boulder = (obj->otyp == BOULDER && obj->next_boulder); + unsigned o_in_use, o_bypass, o_nomerge, o_boulder; + + if (obj->where == OBJ_DELETED) + return; /* skip bit checking for deleted objects */ + + o_in_use = obj->in_use; + o_bypass = obj->bypass; + /* having obj->nomerge be set might be intentional */ + o_nomerge = (obj->nomerge && !nomerge_exception(obj)); + /* next_boulder is only for object name formatting when pushing + boulders and should be reset by time of next sanity check */ + o_boulder = (obj->otyp == BOULDER && obj->next_boulder); if (o_in_use || o_bypass || o_nomerge || o_boulder) { char infobuf[QBUFSZ]; @@ -3338,7 +3504,7 @@ insane_obj_bits(struct obj *obj, struct monst *mon) } /* does 'obj' use the 'nomerge' flag persistently? */ -static boolean +staticfn boolean nomerge_exception(struct obj *obj) { /* special prize objects for achievement tracking are set 'nomerge' @@ -3353,10 +3519,10 @@ nomerge_exception(struct obj *obj) static const char *const obj_state_names[NOBJ_STATES] = { "free", "floor", "contained", "invent", "minvent", "migrating", "buried", "onbill", - "luafree" + "luafree", "deleted", "intrap" }; -static const char * +staticfn const char * where_name(struct obj *obj) { static char unknown[32]; /* big enough to handle rogue 64-bit int */ @@ -3374,7 +3540,7 @@ where_name(struct obj *obj) DISABLE_WARNING_FORMAT_NONLITERAL -static void +staticfn void insane_object( struct obj *obj, const char *fmt, @@ -3417,22 +3583,24 @@ init_dummyobj(struct obj *obj, short otyp, long oquan) /* obj->dknown = 0; */ /* suppress known except for amulets (needed for fakes & real AoY) */ obj->known = (obj->oclass == AMULET_CLASS) - ? obj->known + ? obj->known /* default is "on" for types which don't use it */ : !objects[otyp].oc_uses_known; obj->quan = oquan ? oquan : 1L; obj->corpsenm = NON_PM; /* suppress statue and figurine details */ + if (obj->otyp == LEASH) + obj->leashmon = 0; /* overloads corpsenm, avoid NON_PM */ if (obj->otyp == BOULDER) obj->next_boulder = 0; /* overloads corpsenm, avoid NON_PM */ /* but suppressing fruit details leads to "bad fruit #0" */ if (obj->otyp == SLIME_MOLD) - obj->spe = gc.context.current_fruit; + obj->spe = svc.context.current_fruit; } return obj; } /* obj sanity check: check objects inside container */ -static void +staticfn void check_contained(struct obj *container, const char *mesg) { struct obj *obj; @@ -3478,7 +3646,7 @@ check_contained(struct obj *container, const char *mesg) } /* called when 'obj->globby' is set so we don't recheck it here */ -static void +staticfn void check_glob(struct obj *obj, const char *mesg) { #define LOWEST_GLOB GLOB_OF_GRAY_OOZE @@ -3505,7 +3673,7 @@ check_glob(struct obj *obj, const char *mesg) } /* check an object in hero's or monster's inventory which has worn mask set */ -static void +staticfn void sanity_check_worn(struct obj *obj) { #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) @@ -3784,11 +3952,11 @@ obj_absorb(struct obj **obj1, struct obj **obj2) o2wt = otmp2->oeaten ? otmp2->oeaten : otmp2->owt; /* averaging the relative ages is less likely to overflow than averaging the absolute ages directly */ - agetmp = (((gm.moves - otmp1->age) * o1wt - + (gm.moves - otmp2->age) * o2wt) + agetmp = (((svm.moves - otmp1->age) * o1wt + + (svm.moves - otmp2->age) * o2wt) / (o1wt + o2wt)); /* convert relative age back to absolute age */ - otmp1->age = gm.moves - agetmp; + otmp1->age = svm.moves - agetmp; otmp1->owt += o2wt; if (otmp1->oeaten || otmp2->oeaten) otmp1->oeaten = o1wt + o2wt; @@ -3817,16 +3985,19 @@ obj_absorb(struct obj **obj1, struct obj **obj2) /* * Causes the heavier object to absorb the lighter object in * most cases, but if one object is OBJ_FREE and the other is - * on the floor, the floor object goes first. + * on the floor, the floor object goes first. Note that when + * a globby monster dies, its corpse (new glob) will be created + * on the floor; when a glob is dropped, thrown, or kicked it + * will be free at the time obj_meld() gets called. * - * wrapper for obj_absorb so that floor_effects works more - * cleanly (since we don't know which we want to stay around) + * Wrapper for obj_absorb() so that floor_effects works more + * cleanly (since we don't know which we want to stay around). */ struct obj * -obj_meld(struct obj** obj1, struct obj** obj2) +obj_meld(struct obj **obj1, struct obj **obj2) { struct obj *otmp1, *otmp2, *result = 0; - int ox, oy; + int ox, oy; /* coordinates for the glob that goes away */ if (obj1 && obj2) { otmp1 = *obj1; @@ -3856,8 +4027,14 @@ obj_meld(struct obj** obj1, struct obj** obj2) } /* callers really ought to take care of this; glob melding is a bookkeeping issue rather than a display one */ - if (ox && cansee(ox, oy)) - newsym(ox, oy); + if (ox) { + if (cansee(ox, oy)) + newsym(ox, oy); + /* and this; a hides-under monster might be hiding under + the glob that went away; if there's nothing else there + to hide under, force it out of hiding */ + maybe_unhide_at(ox, oy); + } } } else { impossible("obj_meld: not called with two actual objects"); @@ -3993,7 +4170,7 @@ choose_thiefstone_loc(coord *cc) /* Set up the data associated with a thiefstone. (This involves both selecting * its keyed location and recording that location in the stone.) */ -static void +staticfn void init_thiefstone(struct obj *stone) { curse(stone); /* always generated cursed */ @@ -4301,7 +4478,7 @@ init_obj_material(struct obj* obj) * are basically the same as the regular list but excluding one or two * materials. * This should be treated as subsidiary to valid_obj_material. */ -static boolean +staticfn boolean invalid_obj_material(struct obj *obj, uchar mat) { int oclass = obj->oclass; diff --git a/src/mkroom.c b/src/mkroom.c index 150d42b147..21cbe90d8f 100644 --- a/src/mkroom.c +++ b/src/mkroom.c @@ -17,33 +17,33 @@ #include "hack.h" -static boolean isbig(struct mkroom *); -static struct mkroom *pick_room(boolean); -static void mkshop(void), mkzoo(int), mkswamp(void); -static void mk_zoo_thronemon(coordxy, coordxy); -static struct mkroom *mktemple(void); -static void mkseminary(void); -static void mksubmerged(void); -static void mkstatuary(void); -static coord *shrine_pos(int); -static struct permonst *morguemon(void); -static struct permonst *squadmon(void); -static struct permonst *zoomon(void); -static struct permonst *demondenmon(void); -static struct permonst *abattoirmon(void); -static void save_room(NHFILE *, struct mkroom *); -static void rest_room(NHFILE *, struct mkroom *); -static boolean invalid_shop_shape(struct mkroom *sroom); +staticfn boolean isbig(struct mkroom *); +staticfn struct mkroom *pick_room(boolean); +staticfn void mkshop(void), mkzoo(int), mkswamp(void); +staticfn void mk_zoo_thronemon(coordxy, coordxy); +staticfn struct mkroom *mktemple(void); +staticfn void mkseminary(void); +staticfn void mksubmerged(void); +staticfn void mkstatuary(void); +staticfn coord *shrine_pos(int); +staticfn struct permonst *morguemon(void); +staticfn struct permonst *squadmon(void); +staticfn struct permonst *zoomon(void); +staticfn struct permonst *demondenmon(void); +staticfn struct permonst *abattoirmon(void); +staticfn void save_room(NHFILE *, struct mkroom *); +staticfn void rest_room(NHFILE *, struct mkroom *); +staticfn boolean invalid_shop_shape(struct mkroom *sroom); #define sq(x) ((x) * (x)) extern const struct shclass shtypes[]; /* defined in shknam.c */ /* Return TRUE if a room's rectangular floor area is larger than 20 */ -static boolean -isbig(struct mkroom* sroom) +staticfn boolean +isbig(struct mkroom *sroom) { - register int area = (sroom->hx - sroom->lx + 1) + int area = (sroom->hx - sroom->lx + 1) * (sroom->hy - sroom->ly + 1); return (boolean) (area > 20); @@ -113,10 +113,10 @@ do_mkroom(int roomtype) * Shops are always made lit. If a wand or a book shop whose area is larger than * 20 would have been created, it is instead replaced with a general store. */ -static void +staticfn void mkshop(void) { - register struct mkroom *sroom; + struct mkroom *sroom; int i = -1; char *ep = (char *) 0; /* (init == lint suppression) */ @@ -201,7 +201,7 @@ mkshop(void) } gottype: - for (sroom = &gr.rooms[0];; sroom++) { + for (sroom = &svr.rooms[0];; sroom++) { /* return from this loop: cannot find any eligible room to be a shop * continue: sroom is ineligible * break: sroom is eligible @@ -209,7 +209,7 @@ mkshop(void) if (sroom->hx < 0) /* could not find any suitable rooms */ return; - if (sroom - gr.rooms >= gn.nroom) { + if (sroom - svr.rooms >= svn.nroom) { impossible("rooms[] not closed by -1?"); return; } @@ -234,7 +234,7 @@ mkshop(void) } if (i < 0) { /* shoptype not yet determined */ - register int j; + int j; /* pick a shop type at random */ for (j = rnd(100), i = 0; (j -= shtypes[i].prob) > 0; i++) @@ -268,15 +268,16 @@ mkshop(void) * case it may allow it with 1/3 chance. * Rooms with exactly one door are heavily preferred; there is only a 1/5 * chance of selecting a room with more doors than that. */ -static struct mkroom * +/* pick an unused room, preferably with only one door */ +staticfn struct mkroom * pick_room(boolean strict) { - register struct mkroom *sroom; - register int i = gn.nroom; + struct mkroom *sroom; + int i = svn.nroom; - for (sroom = &gr.rooms[rn2(gn.nroom)]; i--; sroom++) { - if (sroom == &gr.rooms[gn.nroom]) - sroom = &gr.rooms[0]; + for (sroom = &svr.rooms[rn2(svn.nroom)]; i--; sroom++) { + if (sroom == &svr.rooms[svn.nroom]) + sroom = &svr.rooms[0]; if (sroom->hx < 0) return (struct mkroom *) 0; if (sroom->rtype != OROOM) @@ -294,10 +295,10 @@ pick_room(boolean strict) /* Try to find a suitable room for a zoo of the given type and, if one can be * found, set its room type and call fill_zoo to stock it. */ -static void +staticfn void mkzoo(int type) { - register struct mkroom *sroom; + struct mkroom *sroom; if ((sroom = pick_room(FALSE)) != 0) { sroom->rtype = type; @@ -309,7 +310,7 @@ mkzoo(int type) /* Create an appropriate "king" monster at the given location (assumed to be on * a throne). */ -static void +staticfn void mk_zoo_thronemon(coordxy x, coordxy y) { int i = rnd(level_difficulty()); @@ -343,13 +344,13 @@ mk_zoo_thronemon(coordxy x, coordxy y) * Currently, all of these involve placing a monster on every square of the * room, whereas objects may or may not be. */ void -fill_zoo(struct mkroom* sroom) +fill_zoo(struct mkroom *sroom) { - struct monst *mon = NULL; - register int sx, sy, i; + struct monst *mon; + int sx, sy, i; int sh, goldlim = 0, type = sroom->rtype; coordxy tx = 0, ty = 0; - int rmno = (int) ((sroom - gr.rooms) + ROOMOFFSET); + int rmno = (int) ((sroom - svr.rooms) + ROOMOFFSET); coord mm; /* Note: This doesn't check needfill; it assumes the caller has already @@ -358,7 +359,7 @@ fill_zoo(struct mkroom* sroom) switch (type) { case COURT: /* Did a special level hardcode the throne in a given spot? */ - if (gl.level.flags.is_maze_lev) { + if (svl.level.flags.is_maze_lev) { for (tx = sroom->lx; tx <= sroom->hx; tx++) for (ty = sroom->ly; ty <= sroom->hy; ty++) if (IS_THRONE(levl[tx][ty].typ)) @@ -370,7 +371,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; @@ -400,15 +401,18 @@ fill_zoo(struct mkroom* sroom) if (sroom->irregular) { if ((int) levl[sx][sy].roomno != rmno || levl[sx][sy].edge || (sroom->doorct - && distmin(sx, sy, gd.doors[sh].x, gd.doors[sh].y) <= 1)) + && (distmin(sx, sy, svd.doors[sh].x, svd.doors[sh].y) + <= 1))) continue; } else if (!SPACE_POS(levl[sx][sy].typ) || (sroom->doorct - && ((sx == sroom->lx && gd.doors[sh].x == sx - 1) - || (sx == sroom->hx && gd.doors[sh].x == sx + 1) - || (sy == sroom->ly && gd.doors[sh].y == sy - 1) + && ((sx == sroom->lx && svd.doors[sh].x == sx - 1) + || (sx == sroom->hx && svd.doors[sh].x + == sx + 1) + || (sy == sroom->ly && svd.doors[sh].y + == sy - 1) || (sy == sroom->hy - && gd.doors[sh].y == sy + 1)))) + && svd.doors[sh].y == sy + 1)))) continue; /* don't place monster on explicitly placed throne */ if (type == COURT && IS_THRONE(levl[sx][sy].typ)) @@ -475,7 +479,8 @@ fill_zoo(struct mkroom* sroom) case LEPREHALL: /* place floor gold */ if (sroom->doorct) { - int distval = dist2(sx, sy, gd.doors[sh].x, gd.doors[sh].y); + int distval = dist2(sx, sy, + svd.doors[sh].x, svd.doors[sh].y); i = sq(distval); } else i = goldlim; @@ -564,7 +569,7 @@ fill_zoo(struct mkroom* sroom) if (!rn2(2)) { const char* bloodstains[] = { "/", "-", "\\", ".", ",", ":" }; - make_engr_at(sx, sy, bloodstains[rn2(SIZE(bloodstains))], + make_engr_at(sx, sy, ROLL_FROM(bloodstains), 0, ENGR_BLOOD); } break; @@ -583,23 +588,23 @@ fill_zoo(struct mkroom* sroom) add_to_container(chest, gold); chest->owt = weight(chest); chest->spe = 2; /* so it can be found later */ - gl.level.flags.has_court = 1; + svl.level.flags.has_court = 1; break; } case BARRACKS: - gl.level.flags.has_barracks = 1; + svl.level.flags.has_barracks = 1; break; case ZOO: - gl.level.flags.has_zoo = 1; + svl.level.flags.has_zoo = 1; break; case MORGUE: - gl.level.flags.has_morgue = 1; + svl.level.flags.has_morgue = 1; break; case SWAMP: - gl.level.flags.has_swamp = 1; + svl.level.flags.has_swamp = 1; break; case BEEHIVE: - gl.level.flags.has_beehive = 1; + svl.level.flags.has_beehive = 1; break; } } @@ -626,15 +631,15 @@ mkundead( || !revive(otmp, FALSE))) (void) makemon(mdat, cc.x, cc.y, mm_flags); } - gl.level.flags.graveyard = TRUE; /* reduced chance for undead corpse */ + svl.level.flags.graveyard = TRUE; /* reduced chance for undead corpse */ } /* Return an appropriate undead monster type for generating in graveyards or * when raising the dead. */ -static struct permonst * +staticfn struct permonst * morguemon(void) { - register int i = rn2(100), hd = rn2(level_difficulty()); + int i = rn2(100), hd = rn2(level_difficulty()); if (hd > 10 && i < 10) { if (Inhell || In_endgame(&u.uz)) { @@ -679,9 +684,9 @@ antholemon(void) break; } /* try again if chosen type has been genocided or used up */ - } while (++trycnt < 3 && (gm.mvitals[mtyp].mvflags & G_GONE)); + } while (++trycnt < 3 && (svm.mvitals[mtyp].mvflags & G_GONE)); - return ((gm.mvitals[mtyp].mvflags & G_GONE) ? (struct permonst *) 0 + return ((svm.mvitals[mtyp].mvflags & G_GONE) ? (struct permonst *) 0 : &mons[mtyp]); } @@ -736,21 +741,21 @@ abattoirmon(void) * Swamps contain a checkerboard pattern of pools (except next to doors), * F-class monsters, and possibly one sea monster, apiece. */ -static void +staticfn void mkswamp(void) /* Michiel Huisjes & Fred de Wilde */ { - register struct mkroom *sroom; - register int i, eelct = 0; + struct mkroom *sroom; + int i, eelct = 0; coordxy sx, sy; int rmno; for (i = 0; i < 5; i++) { /* turn up to 5 rooms swampy */ - sroom = &gr.rooms[rn2(gn.nroom)]; + sroom = &svr.rooms[rn2(svn.nroom)]; if (sroom->hx < 0 || sroom->rtype != OROOM || has_upstairs(sroom) || has_dnstairs(sroom)) continue; - rmno = (int)(sroom - gr.rooms) + ROOMOFFSET; + rmno = (int)(sroom - svr.rooms) + ROOMOFFSET; /* satisfied; make a swamp */ sroom->rtype = SWAMP; @@ -781,7 +786,7 @@ mkswamp(void) /* Michiel Huisjes & Fred de Wilde */ levl[sx][sy].typ = TREE; } } - gl.level.flags.has_swamp = 1; + svl.level.flags.has_swamp = 1; } } @@ -789,12 +794,12 @@ mkswamp(void) /* Michiel Huisjes & Fred de Wilde */ * is to be a temple. It will be the exact center of the room, unless the center * isn't actually a square, in which case it'll be offset one space to the side. */ -static coord * +staticfn coord * shrine_pos(int roomno) { static coord buf; int delta; - struct mkroom *troom = &gr.rooms[roomno - ROOMOFFSET]; + struct mkroom *troom = &svr.rooms[roomno - ROOMOFFSET]; /* if width and height are odd, placement will be the exact center; if either or both are even, center point is a hypothetical spot @@ -815,12 +820,12 @@ shrine_pos(int roomno) /* Try and find a suitable room for a temple and if successful, create the * temple with its altar and attendant priest. */ -static struct mkroom * +staticfn struct mkroom * mktemple(void) { - register struct mkroom *sroom; + struct mkroom *sroom; coord *shrine_spot; - register struct rm *lev; + struct rm *lev; if (!(sroom = pick_room(TRUE))) return NULL; @@ -831,7 +836,7 @@ mktemple(void) * In temples, shrines are blessed altars * located in the center of the room */ - shrine_spot = shrine_pos((int) ((sroom - gr.rooms) + ROOMOFFSET)); + shrine_spot = shrine_pos((int) ((sroom - svr.rooms) + ROOMOFFSET)); lev = &levl[shrine_spot->x][shrine_spot->y]; lev->typ = ALTAR; if (!Inhell) { @@ -842,7 +847,7 @@ mktemple(void) } priestini(&u.uz, sroom, shrine_spot->x, shrine_spot->y, FALSE); lev->altarmask |= AM_SHRINE; - gl.level.flags.has_temple = 1; + svl.level.flags.has_temple = 1; return sroom; } @@ -860,7 +865,7 @@ mkseminary(void) return; /* get altar alignment, roaming priests should have the same */ - ss = shrine_pos((int) ((sroom - gr.rooms) + ROOMOFFSET)); + ss = shrine_pos((int) ((sroom - svr.rooms) + ROOMOFFSET)); if (levl[ss->x][ss->y].typ != ALTAR) { impossible("mkseminary: altar not present?"); return; @@ -942,7 +947,7 @@ mkstatuary(void) if (!nexttodoor(x, y)) { \ statue = mkcorpstat(STATUE, NULL, &mons[u.umonster], \ x, y, CORPSTAT_NONE); \ - oname(statue, gp.plname, ONAME_NO_FLAGS); \ + oname(statue, svp.plname, ONAME_NO_FLAGS); \ } /* pick the longer dimension to place statues */ @@ -969,8 +974,8 @@ mkstatuary(void) boolean nexttodoor(int sx, int sy) { - register int dx, dy; - register struct rm *lev; + int dx, dy; + struct rm *lev; for (dx = -1; dx <= 1; dx++) for (dy = -1; dy <= 1; dy++) { @@ -1000,14 +1005,14 @@ has_stairs(register struct mkroom *sroom, boolean up) /* Return a random x coordinate within the x limits of a room. */ int -somex(struct mkroom* croom) +somex(struct mkroom *croom) { return rn1(croom->hx - croom->lx + 1, croom->lx); } /* Return a random y coordinate within the y limits of a room. */ int -somey(struct mkroom* croom) +somey(struct mkroom *croom) { return rn1(croom->hy - croom->ly + 1, croom->ly); } @@ -1016,10 +1021,10 @@ somey(struct mkroom* croom) * of a room. */ boolean -inside_room(struct mkroom* croom, coordxy x, coordxy y) +inside_room(struct mkroom *croom, coordxy x, coordxy y) { if (croom->irregular) { - int i = (int) ((croom - gr.rooms) + ROOMOFFSET); + int i = (int) ((croom - svr.rooms) + ROOMOFFSET); return (!levl[x][y].edge && (int) levl[x][y].roomno == i); } @@ -1032,13 +1037,13 @@ inside_room(struct mkroom* croom, coordxy x, coordxy y) can return a non-accessible location, eg. inside a wall if a themed room is not irregular, but has some non-room terrain */ boolean -somexy(struct mkroom* croom,coord * c) +somexy(struct mkroom *croom, coord *c) { int try_cnt = 0; int i; if (croom->irregular) { - i = (int) ((croom - gr.rooms) + ROOMOFFSET); + i = (int) ((croom - svr.rooms) + ROOMOFFSET); while (try_cnt++ < 100) { c->x = somex(croom); @@ -1106,9 +1111,9 @@ somexyspace(struct mkroom* croom, coord *c) struct mkroom * search_special(schar type) { - register struct mkroom *croom; + struct mkroom *croom; - for (croom = &gr.rooms[0]; croom->hx >= 0; croom++) + for (croom = &svr.rooms[0]; croom->hx >= 0; croom++) if ((type == ANY_TYPE && croom->rtype != OROOM) || (type == ANY_SHOP && croom->rtype >= SHOPBASE) || croom->rtype == type) @@ -1157,7 +1162,7 @@ static const struct { /* Return an appropriate Yendorian Army monster type for generating in * barracks. They will generate with the percentage odds given above. */ -static struct permonst * +staticfn struct permonst * squadmon(void) { int sel_prob, i, cpro, mndx; @@ -1172,9 +1177,9 @@ squadmon(void) goto gotone; } } - mndx = squadprob[rn2(SIZE(squadprob))].pm; + mndx = ROLL_FROM(squadprob).pm; gotone: - if (!(gm.mvitals[mndx].mvflags & G_GONE)) + if (!(svm.mvitals[mndx].mvflags & G_GONE)) return &mons[mndx]; else return (struct permonst *) 0; @@ -1184,8 +1189,8 @@ squadmon(void) * save_room : A recursive function that saves a room and its subrooms * (if any). */ -static void -save_room(NHFILE* nhfp, struct mkroom* r) +staticfn void +save_room(NHFILE *nhfp, struct mkroom *r) { short i; @@ -1205,19 +1210,19 @@ save_room(NHFILE* nhfp, struct mkroom* r) * save_rooms : Save all the rooms on disk! */ void -save_rooms(NHFILE* nhfp) +save_rooms(NHFILE *nhfp) { short i; /* First, write the number of rooms */ if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &gn.nroom, sizeof(gn.nroom)); - for (i = 0; i < gn.nroom; i++) - save_room(nhfp, &gr.rooms[i]); + bwrite(nhfp->fd, (genericptr_t) &svn.nroom, sizeof(svn.nroom)); + for (i = 0; i < svn.nroom; i++) + save_room(nhfp, &svr.rooms[i]); } -static void -rest_room(NHFILE* nhfp, struct mkroom* r) +staticfn void +rest_room(NHFILE *nhfp, struct mkroom *r) { short i; @@ -1236,19 +1241,19 @@ rest_room(NHFILE* nhfp, struct mkroom* r) * the disk. */ void -rest_rooms(NHFILE* nhfp) +rest_rooms(NHFILE *nhfp) { short i; if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) &gn.nroom, sizeof(gn.nroom)); + mread(nhfp->fd, (genericptr_t) &svn.nroom, sizeof(svn.nroom)); gn.nsubroom = 0; - for (i = 0; i < gn.nroom; i++) { - rest_room(nhfp, &gr.rooms[i]); - gr.rooms[i].resident = (struct monst *) 0; + for (i = 0; i < svn.nroom; i++) { + rest_room(nhfp, &svr.rooms[i]); + svr.rooms[i].resident = (struct monst *) 0; } - gr.rooms[gn.nroom].hx = -1; /* restore ending flags */ + svr.rooms[svn.nroom].hx = -1; /* restore ending flags */ gs.subrooms[gn.nsubroom].hx = -1; } @@ -1378,29 +1383,29 @@ cmap_to_type(int sym) return typ; } -/* With the introduction of themed rooms, there are certain room shapes that may - * generate a door, the square just inside the door, and only one other ROOM - * square touching that one. E.g. +/* With the introduction of themed rooms, there are certain room shapes that + * may generate a door, the square just inside the door, and only one other + * ROOM square touching that one. E.g. * --- * ---.. * +.... * ---.. * --- - * This means that if the room becomes a shop, the shopkeeper will move between - * those two squares nearest the door without ever allowing the player to get - * past them. + * This means that if the room becomes a shop, the shopkeeper will move + * between those two squares nearest the door without ever allowing the + * player to get past them. * Before approving sroom as a shop, check for this circumstance, and if it * exists, don't consider it as valid for a shop. * * Note that the invalidity of the shape derives from the position of its door * already being chosen. It's quite possible that if the door were somewhere * else on the perimeter of this room, it would work fine as a shop.*/ -static boolean +staticfn boolean invalid_shop_shape(struct mkroom *sroom) { coordxy x, y; - coordxy doorx = gd.doors[sroom->fdoor].x; - coordxy doory = gd.doors[sroom->fdoor].y; + coordxy doorx = svd.doors[sroom->fdoor].x; + coordxy doory = svd.doors[sroom->fdoor].y; coordxy insidex = 0, insidey = 0, insidect = 0; /* First, identify squares inside the room and next to the door. */ diff --git a/src/mon.c b/src/mon.c index 330895a19d..e6b075dae6 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1,48 +1,47 @@ -/* NetHack 3.7 mon.c $NHDT-Date: 1683831104 2023/05/11 18:51:44 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.501 $ */ +/* NetHack 3.7 mon.c $NHDT-Date: 1722365546 2024/07/30 18:52:26 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.584 $ */ /* 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 - -static void sanity_check_single_mon(struct monst *, boolean, const char *); -static struct obj *make_corpse(struct monst *, unsigned); -static int minliquid_core(struct monst *); -static void m_calcdistress(struct monst *); -static boolean monlineu(struct monst *, int, int); -static long mm_2way_aggression(struct monst *, struct monst *); -static long mm_displacement(struct monst *, struct monst *); -static void mon_leaving_level(struct monst *); -static void m_detach(struct monst *, struct permonst *, boolean); -static void set_mon_min_mhpmax(struct monst *, int); -static void lifesaved_monster(struct monst *); -static void juiblex_transferral(void); -static boolean ok_to_obliterate(struct monst *); -static void nazgul_shriek(struct monst *); -static void qst_guardians_respond(void); -static void peacefuls_respond(struct monst *); -static void m_restartcham(struct monst *); -static boolean restrap(struct monst *); -static int pick_animal(void); -static int pickvampshape(struct monst *); -static boolean isspecmon(struct monst *); -static boolean validspecmon(struct monst *, int); -static struct permonst *accept_newcham_form(struct monst *, int); -static void kill_eggs(struct obj *); -static void pacify_guard(struct monst *); -#define LEVEL_SPECIFIC_NOCORPSE(mdat) \ - (!gl.level.flags.deathdrops \ - || (gl.level.flags.graveyard && is_undead(mdat) && rn2(3))) +staticfn void sanity_check_single_mon(struct monst *, boolean, const char *); +staticfn struct obj *make_corpse(struct monst *, unsigned); +staticfn int minliquid_core(struct monst *); +staticfn void m_calcdistress(struct monst *); +staticfn boolean monlineu(struct monst *, int, int); +staticfn long mm_2way_aggression(struct monst *, struct monst *); +staticfn long mm_aggression(struct monst *, struct monst *); +staticfn long mm_displacement(struct monst *, struct monst *); +staticfn void mon_leaving_level(struct monst *); +staticfn void m_detach(struct monst *, struct permonst *, boolean); +staticfn void set_mon_min_mhpmax(struct monst *, int); +staticfn void lifesaved_monster(struct monst *); +staticfn void juiblex_transferral(void); +staticfn boolean vamprises(struct monst *); +staticfn void logdeadmon(struct monst *, int); +staticfn boolean ok_to_obliterate(struct monst *); +staticfn void nazgul_shriek(struct monst *); +staticfn void qst_guardians_respond(void); +staticfn void peacefuls_respond(struct monst *); +staticfn void wake_nearto_core(coordxy, coordxy, int, boolean); +staticfn void m_restartcham(struct monst *); +staticfn boolean restrap(struct monst *); +staticfn int pick_animal(void); +staticfn int pickvampshape(struct monst *); +staticfn boolean isspecmon(struct monst *); +staticfn boolean validspecmon(struct monst *, int); +staticfn int wiz_force_cham_form(struct monst *); +staticfn struct permonst *accept_newcham_form(struct monst *, int); +staticfn void kill_eggs(struct obj *) NO_NNARGS; +staticfn void pacify_guard(struct monst *); + +extern const struct shclass shtypes[]; /* defined in shknam.c */ -/* A specific combination of x_monnam flags for livelogging. The livelog - * shouldn't show that you killed a hallucinatory monster and not what it - * actually is. */ -#define livelog_mon_nam(mtmp) \ - x_monnam(mtmp, ARTICLE_THE, (char *) 0, \ - (SUPPRESS_IT | SUPPRESS_HALLUCINATION), FALSE) +#define LEVEL_SPECIFIC_NOCORPSE(mdat) \ + (!svl.level.flags.deathdrops \ + || (svl.level.flags.graveyard && is_undead(mdat) && rn2(3))) #if 0 @@ -54,7 +53,7 @@ const char *warnings[] = { extern const int monstr[]; -static void +staticfn void sanity_check_single_mon( struct monst *mtmp, boolean chk_geno, @@ -63,7 +62,7 @@ sanity_check_single_mon( struct permonst *mptr = mtmp->data; coordxy mx = mtmp->mx, my = mtmp->my; - if (!mptr || mptr < &mons[LOW_PM] || mptr >= &mons[NUMMONS]) { + if (!mptr || mptr < &mons[LOW_PM] || mptr > &mons[HIGH_PM]) { /* most sanity checks issue warnings if they detect a problem, but this would be too extreme to keep going */ panic("illegal mon data %s; mnum=%d (%s)", @@ -77,39 +76,35 @@ sanity_check_single_mon( mtmp->mnum, mndx, msg); mtmp->mnum = mndx; } -#if 0 /* - * Gremlins don't obey the (mhpmax >= m_lev) rule so disable - * this check, at least for the time being. We could skip it - * when the cloned flag is set, but the original gremlin would - * still be an issue. - */ /* check before DEADMONSTER() because dead monsters should still have sane mhpmax */ if (mtmp->mhpmax < 1 + /* Gremlins don't obey the (mhpmax >= m_lev) rule so disable + * this check, at least for the time being. We could skip it + * when the cloned flag is set, but the original gremlin would + * still be an issue. || mtmp->mhpmax < (int) mtmp->m_lev + */ || mtmp->mhp > mtmp->mhpmax) - impossible( - "%s: level %d monster #%u [%s] has %d cur HP, %d max HP", - msg, (int) mtmp->m_lev, + impossible("%s: level %d %s #%u [%s] has %d cur HP, %d max HP", + msg, (int) mtmp->m_lev, mptr->pmnames[NEUTRAL], mtmp->m_id, fmt_ptr((genericptr_t) mtmp), mtmp->mhp, mtmp->mhpmax); -#endif if (DEADMONSTER(mtmp)) { #if 0 /* bad if not fmon list or if not vault guard */ if (strcmp(msg, "fmon") || !mtmp->isgd) impossible("dead monster on %s; %s at <%d,%d>", - msg, mons[mndx].pmnames[NEUTRAL], - mx, my); + msg, mptr->pmnames[NEUTRAL], mx, my); #endif return; } - if (chk_geno && (gm.mvitals[mndx].mvflags & G_GENOD) != 0) + if (chk_geno && (svm.mvitals[mndx].mvflags & G_GENOD) != 0) impossible("genocided %s in play (%s)", - pmname(&mons[mndx], Mgender(mtmp)), msg); + pmname(mptr, Mgender(mtmp)), msg); if (mtmp->mtame && !mtmp->mpeaceful) impossible("tame %s is not peaceful (%s)", - pmname(&mons[mndx], Mgender(mtmp)), msg); + pmname(mptr, Mgender(mtmp)), msg); } if (mtmp->isshk && !has_eshk(mtmp)) impossible("shk without eshk (%s)", msg); @@ -141,6 +136,14 @@ sanity_check_single_mon( } else if (!t_at(mx, my)) impossible("trapped without a trap (%s)", msg); } + /* monst->mfrozen is difficult to deal with--it's used for paralysis, + for temporary sleep, and for being busy (usually donning armor); + code that sets mfrozen needs to also clear mcanmove, otherwise the + helpless() test will be unreliable */ + if (mtmp->mfrozen && mtmp->mcanmove) + impossible("frozen monster [%s%s] is able to move (%s)", + mtmp->mtame ? "tame " : mtmp->mpeaceful ? "peaceful " : "", + pmname(mptr, Mgender(mtmp)), msg); /* monster is hiding? */ if (mtmp->mundetected) { @@ -154,16 +157,20 @@ sanity_check_single_mon( && !concealed_spot(mx, my)) impossible("mon hiding under nonexistent obj (%s)", msg); if (mptr->mlet == S_EEL - && !is_pool(mx, my) && !Is_waterlevel(&u.uz)) - impossible("eel hiding out of water (%s)", msg); + && !(is_pool(mx, my) && !Is_waterlevel(&u.uz))) + impossible("eel hiding %s (%s)", + !Is_waterlevel(&u.uz) ? "out of water" + : "on Plane of Water", msg); if (ceiling_hider(mptr) /* normally !accessible would be overridable with passes_walls, but not for hiding on the ceiling */ - && (!has_ceiling(&u.uz) || - !(levl[mx][my].typ == POOL - || levl[mx][my].typ == MOAT - || levl[mx][my].typ == LAVAPOOL - || accessible(mx, my)))) + && (!has_ceiling(&u.uz) + || !(levl[mx][my].typ == POOL + || levl[mx][my].typ == MOAT + || levl[mx][my].typ == WATER + || levl[mx][my].typ == LAVAPOOL + || levl[mx][my].typ == LAVAWALL + || accessible(mx, my)))) impossible("ceiling hider hiding %s (%s)", !has_ceiling(&u.uz) ? "without ceiling" : "in solid stone", @@ -206,6 +213,24 @@ sanity_check_single_mon( impossible("mimic%s concealed in inaccessible location: %s (%s)", is_mimic ? "" : "ker", typnam, msg); } +#endif + } + if (mtmp->mleashed) { + if (!get_mleash(mtmp)) + impossible("monst %u: leashed but no leash for %s", + mtmp->m_id, mon_pmname(mtmp)); + else if (!mtmp->mtame) + impossible("monst %u: leashed but not tame %s", + mtmp->m_id, mon_pmname(mtmp)); +#if 0 + /* after hero moves, leashed mon won't necessarily pass 'm_next2u()' + test; 90 is farthest observed distance with expert jumping spell + when very slow mon is already several steps away and hero jumps in + opposite direction (if hero teleports, leashed mon moves adjacent + immediately; knockback has shorter range than magical jumping) */ + else if (distu(mtmp->mx, mtmp->my) > 90) /*if (!m_next2u(mtmp))*/ + impossible("monst %u: leashed but not next to you (%d)", + mtmp->m_id, distu(mtmp->mx, mtmp->my)); #endif } } @@ -232,15 +257,15 @@ mon_sanity_check(void) if (x != u.ux || y != u.uy) impossible("steed (%s) claims to be at <%d,%d>?", fmt_ptr((genericptr_t) mtmp), x, y); - } else if (gl.level.monsters[x][y] != mtmp) { + } else if (svl.level.monsters[x][y] != mtmp) { impossible("mon (%s) at <%d,%d> is not there!", fmt_ptr((genericptr_t) mtmp), x, y); } else if (mtmp->wormno) { sanity_check_worm(mtmp); - /* TODO: figure out which other bits shouldn't be set for 'fmon' */ - } else if ((mtmp->mstate & (MON_DETACH | MON_MIGRATING | MON_LIMBO)) - != 0) { + /* some temp mstate bits can be expected for a mon on fmon, as part of + removing it, but DEADMONSTER check above should skip those. */ + } else if (mon_offmap(mtmp)) { impossible("floor mon (%s) with mstate set to 0x%08lx", fmt_ptr((genericptr_t) mtmp), mtmp->mstate); } @@ -248,7 +273,7 @@ mon_sanity_check(void) for (x = 1; x < COLNO; x++) for (y = 0; y < ROWNO; y++) - if ((mtmp = gl.level.monsters[x][y]) != 0) { + if ((mtmp = svl.level.monsters[x][y]) != 0) { for (m = fmon; m; m = m->nmon) if (m == mtmp) break; @@ -268,10 +293,10 @@ mon_sanity_check(void) for (mtmp = gm.migrating_mons; mtmp; mtmp = mtmp->nmon) { sanity_check_single_mon(mtmp, FALSE, "migr"); - /* TODO: figure out which other bits could or shouldn't be set - * when migrating */ - if ((mtmp->mstate & (MON_DETACH)) != 0 - || (mtmp->mstate & (MON_MIGRATING | MON_LIMBO)) == 0) + if ((mtmp->mstate + & ~(MON_MIGRATING | MON_LIMBO | MON_ENDGAME_MIGR | MON_OFFMAP)) + != 0L + || !(mtmp->mstate & MON_MIGRATING)) impossible("migrating mon (%s) with mstate set to 0x%08lx", fmt_ptr((genericptr_t) mtmp), mtmp->mstate); } @@ -283,7 +308,7 @@ mon_sanity_check(void) /* Does not check for actual poison gas at the location. */ /* Returns one of M_POISONGAS_foo */ int -m_poisongas_ok(struct monst* mtmp) +m_poisongas_ok(struct monst *mtmp) { int px, py; boolean is_you = (mtmp == &gy.youmonst); @@ -467,7 +492,7 @@ genus(int mndx, int mode) mndx = mode ? PM_VALKYRIE : PM_HUMAN; break; default: - if (mndx >= LOW_PM && mndx < NUMMONS) { + if (ismnum(mndx)) { struct permonst *ptr = &mons[mndx]; if (is_human(ptr)) @@ -496,7 +521,7 @@ pm_to_cham(int mndx) * As of 3.6.0 we just check M2_SHAPESHIFTER instead of having a * big switch statement with hardcoded shapeshifter types here. */ - if (mndx >= LOW_PM && is_shapeshifter(&mons[mndx])) + if (ismnum(mndx) && is_shapeshifter(&mons[mndx])) mcham = mndx; return mcham; } @@ -507,8 +532,8 @@ pm_to_cham(int mndx) || is_reviver((mon)->data) \ /* normally quest leader/nemesis will be unique, */ \ /* but he or she might have been polymorphed */ \ - || (mon)->m_id == gq.quest_status.leader_m_id \ - || (mon)->m_id == gq.quest_status.nemesis_m_id \ + || (mon)->m_id == svq.quest_status.leader_m_id \ + || (mon)->m_id == svq.quest_status.nemesis_m_id \ /* special cancellation handling for these */ \ || (dmgtype((mon)->data, AD_SEDU) || dmgtype((mon)->data, AD_SSEX))) @@ -517,15 +542,16 @@ pm_to_cham(int mndx) * G_NOCORPSE set in order to prevent wishing for one, finding tins of one, * etc.... */ -static struct obj * +staticfn struct obj * make_corpse(struct monst *mtmp, unsigned int corpseflags) { - register struct permonst *mdat = mtmp->data; + struct permonst *mdat = mtmp->data; int num; struct obj *obj = (struct obj *) 0; struct obj *otmp = (struct obj *) 0; coordxy x = mtmp->mx, y = mtmp->my; - int mndx = monsndx(mdat); +/* int mndx = monsndx(mdat); */ + enum monnums mndx = monsndx(mdat); unsigned corpstatflags = corpseflags; boolean burythem = ((corpstatflags & CORPSTAT_BURIED) != 0); @@ -536,6 +562,7 @@ make_corpse(struct monst *mtmp, unsigned int corpseflags) switch (mndx) { case PM_GRAY_DRAGON: + case PM_GOLD_DRAGON: case PM_SILVER_DRAGON: #if 0 /* DEFERRED */ case PM_SHIMMERING_DRAGON: @@ -562,7 +589,8 @@ make_corpse(struct monst *mtmp, unsigned int corpseflags) case PM_BLACK_UNICORN: if (mtmp->mrevived && rn2(2)) { if (canseemon(mtmp)) - pline("%s recently regrown horn crumbles to dust.", + pline_mon(mtmp, + "%s recently regrown horn crumbles to dust.", s_suffix(Monnam(mtmp))); } else { obj = mksobj(UNICORN_HORN, TRUE, FALSE); @@ -746,14 +774,14 @@ make_corpse(struct monst *mtmp, unsigned int corpseflags) case PM_TIGER: if (!mtmp->mrevived && !rn2(100)) { int otyp; - for (otyp = gb.bases[RING_CLASS]; otyp < gb.bases[RING_CLASS+1]; + for (otyp = svb.bases[RING_CLASS]; otyp < svb.bases[RING_CLASS+1]; ++otyp) { const char *s; if ((s = OBJ_DESCR(objects[otyp])) != 0 && !strcmp(s, "tiger eye")) break; } - if (otyp >= gb.bases[RING_CLASS + 1]) + if (otyp >= svb.bases[RING_CLASS + 1]) impossible("No tiger eye ring?"); else { obj = mksobj(otyp, TRUE, TRUE); @@ -761,9 +789,170 @@ make_corpse(struct monst *mtmp, unsigned int corpseflags) } } goto default_1; + case NON_PM: case LEAVESTATUE: case NUMMONS: /* never use as index */ + break; + +#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) + case PM_GIANT_ANT: case PM_KILLER_BEE: case PM_SOLDIER_ANT: + case PM_FIRE_ANT: case PM_GIANT_BEETLE: case PM_QUEEN_BEE: + case PM_GIANT_FLY: + + case PM_QUIVERING_BLOB: case PM_ACID_BLOB: case PM_GELATINOUS_CUBE: + case PM_CHICKATRICE: case PM_COCKATRICE: case PM_PYROLISK: + + case PM_JACKAL: case PM_FOX: case PM_COYOTE: case PM_WEREJACKAL: + case PM_LITTLE_DOG: case PM_DINGO: case PM_DOG: case PM_LARGE_DOG: + case PM_WOLF: case PM_WEREWOLF: case PM_WINTER_WOLF_CUB: + case PM_WARG: case PM_WINTER_WOLF: case PM_HELL_HOUND_PUP: + case PM_HELL_HOUND: case PM_CERBERUS: + + case PM_GAS_SPORE: case PM_FLOATING_EYE: case PM_FREEZING_SPHERE: + case PM_FLAMING_SPHERE: case PM_SHOCKING_SPHERE: + + case PM_KITTEN: case PM_HOUSECAT: case PM_JAGUAR: case PM_LYNX: + case PM_PANTHER: case PM_LARGE_CAT: + + case PM_DISPLACER_BEAST: case PM_GREMLIN: + case PM_GARGOYLE: case PM_WINGED_GARGOYLE: + + case PM_HOBBIT: case PM_DWARF: case PM_BUGBEAR: case PM_DWARF_LEADER: + case PM_DWARF_RULER: + case PM_MIND_FLAYER: case PM_MASTER_MIND_FLAYER: case PM_MANES: + case PM_HOMUNCULUS: case PM_IMP: case PM_LEMURE: case PM_QUASIT: + case PM_TENGU: case PM_BLUE_JELLY: case PM_SPOTTED_JELLY: + case PM_OCHRE_JELLY: case PM_KOBOLD: case PM_LARGE_KOBOLD: + case PM_KOBOLD_LEADER: case PM_KOBOLD_SHAMAN: case PM_LEPRECHAUN: + case PM_SMALL_MIMIC: case PM_LARGE_MIMIC: case PM_GIANT_MIMIC: + case PM_WOOD_NYMPH: case PM_WATER_NYMPH: case PM_MOUNTAIN_NYMPH: + case PM_GOBLIN: case PM_HOBGOBLIN: case PM_ORC: case PM_HILL_ORC: + case PM_MORDOR_ORC: case PM_URUK_HAI: case PM_ORC_SHAMAN: + case PM_ORC_CAPTAIN: + case PM_ROCK_PIERCER: case PM_IRON_PIERCER: case PM_GLASS_PIERCER: + case PM_ROTHE: case PM_MUMAK: case PM_LEOCROTTA: case PM_WUMPUS: + case PM_TITANOTHERE: case PM_BALUCHITHERIUM: case PM_MASTODON: + case PM_SEWER_RAT: case PM_GIANT_RAT: case PM_RABID_RAT: + case PM_WERERAT: + + case PM_ROCK_MOLE: case PM_WOODCHUCK: + case PM_CAVE_SPIDER: case PM_CENTIPEDE: case PM_GIANT_SPIDER: + case PM_SCORPION: + case PM_LURKER_ABOVE: case PM_TRAPPER: + case PM_PONY: case PM_HORSE: case PM_WARHORSE: + case PM_FOG_CLOUD: case PM_DUST_VORTEX: case PM_ICE_VORTEX: + case PM_ENERGY_VORTEX: case PM_STEAM_VORTEX: case PM_FIRE_VORTEX: + + case PM_BABY_LONG_WORM: case PM_BABY_PURPLE_WORM: + case PM_PURPLE_WORM: + + case PM_GRID_BUG: case PM_XAN: case PM_YELLOW_LIGHT: case PM_BLACK_LIGHT: + case PM_ZRUTY: case PM_COUATL: case PM_ALEAX: case PM_ANGEL: + case PM_KI_RIN: case PM_ARCHON: + + case PM_BAT: case PM_GIANT_BAT: case PM_RAVEN: case PM_VAMPIRE_BAT: + case PM_PHOENIX: + case PM_PLAINS_CENTAUR: case PM_FOREST_CENTAUR: case PM_MOUNTAIN_CENTAUR: + + case PM_BABY_GRAY_DRAGON: case PM_BABY_GOLD_DRAGON: + case PM_BABY_SILVER_DRAGON: case PM_BABY_RED_DRAGON: + case PM_BABY_WHITE_DRAGON: case PM_BABY_ORANGE_DRAGON: + case PM_BABY_BLACK_DRAGON: case PM_BABY_BLUE_DRAGON: + case PM_BABY_GREEN_DRAGON: case PM_BABY_YELLOW_DRAGON: + + case PM_STALKER: case PM_AIR_ELEMENTAL: case PM_FIRE_ELEMENTAL: + case PM_EARTH_ELEMENTAL: case PM_WATER_ELEMENTAL: + + case PM_LICHEN: case PM_BROWN_MOLD: case PM_YELLOW_MOLD: + case PM_GREEN_MOLD: case PM_RED_MOLD: case PM_SHRIEKER: + case PM_VIOLET_FUNGUS: case PM_BLACK_MOLD: + + case PM_GNOME: case PM_GNOME_LEADER: case PM_GNOMISH_WIZARD: + case PM_GNOME_RULER: + case PM_GIANT: case PM_STONE_GIANT: case PM_HILL_GIANT: + case PM_FIRE_GIANT: case PM_FROST_GIANT: case PM_ETTIN: + case PM_STORM_GIANT: case PM_TITAN: + + case PM_MINOTAUR: case PM_JABBERWOCK: case PM_KEYSTONE_KOP: + case PM_KOP_SERGEANT: case PM_KOP_LIEUTENANT: case PM_KOP_KAPTAIN: + case PM_LICH: case PM_DEMILICH: + case PM_MASTER_LICH: case PM_ARCH_LICH: + + case PM_RED_NAGA_HATCHLING: case PM_BLACK_NAGA_HATCHLING: + case PM_GOLDEN_NAGA_HATCHLING: case PM_GUARDIAN_NAGA_HATCHLING: + case PM_RED_NAGA: case PM_BLACK_NAGA: case PM_GOLDEN_NAGA: + case PM_GUARDIAN_NAGA: + + case PM_OGRE: case PM_OGRE_LEADER: case PM_OGRE_TYRANT: + + case PM_QUANTUM_MECHANIC: case PM_GENETIC_ENGINEER: + case PM_RUST_MONSTER: case PM_DISENCHANTER: + + case PM_GARTER_SNAKE: case PM_SNAKE: case PM_WATER_MOCCASIN: + case PM_PYTHON: case PM_PIT_VIPER: case PM_COBRA: + + case PM_TROLL: case PM_ICE_TROLL: case PM_ROCK_TROLL: case PM_WATER_TROLL: + case PM_OLOG_HAI: case PM_UMBER_HULK: + + case PM_VLAD_THE_IMPALER: + + case PM_BARROW_WIGHT: case PM_WRAITH: case PM_NAZGUL: + case PM_XORN: case PM_MONKEY: case PM_APE: case PM_OWLBEAR: + case PM_YETI: case PM_CARNIVOROUS_APE: case PM_SASQUATCH: + + case PM_GHOUL: + + case PM_STRAW_GOLEM: case PM_FLESH_GOLEM: + + case PM_HUMAN: case PM_HUMAN_WERERAT: case PM_HUMAN_WEREJACKAL: + case PM_HUMAN_WEREWOLF: case PM_ELF: case PM_WOODLAND_ELF: + case PM_GREEN_ELF: case PM_GREY_ELF: case PM_ELF_NOBLE: + case PM_ELVEN_MONARCH: + case PM_DOPPELGANGER: case PM_SHOPKEEPER: + case PM_GUARD: case PM_PRISONER: case PM_ORACLE: + case PM_ALIGNED_CLERIC: case PM_HIGH_CLERIC: + case PM_SOLDIER: case PM_SERGEANT: case PM_NURSE: + case PM_LIEUTENANT: case PM_CAPTAIN: case PM_WATCHMAN: + case PM_WATCH_CAPTAIN: + + case PM_MEDUSA: case PM_WIZARD_OF_YENDOR: case PM_CROESUS: + case PM_GHOST: case PM_SHADE: case PM_WATER_DEMON: + case PM_AMOROUS_DEMON: case PM_HORNED_DEVIL: + case PM_ERINYS: case PM_BARBED_DEVIL: case PM_MARILITH: case PM_VROCK: + case PM_HEZROU: case PM_BONE_DEVIL: case PM_ICE_DEVIL: case PM_NALFESHNEE: + case PM_PIT_FIEND: case PM_SANDESTIN: case PM_BALROG: case PM_JUIBLEX: + case PM_YEENOGHU: case PM_ORCUS: case PM_GERYON: case PM_DISPATER: + case PM_BAALZEBUB: case PM_ASMODEUS: case PM_DEMOGORGON: + case PM_DEATH: case PM_PESTILENCE: case PM_FAMINE: + case PM_MAIL_DAEMON: case PM_DJINNI: + + case PM_JELLYFISH: case PM_PIRANHA: case PM_SHARK: case PM_GIANT_EEL: + case PM_ELECTRIC_EEL: case PM_KRAKEN: + case PM_NEWT: case PM_GECKO: case PM_IGUANA: case PM_BABY_CROCODILE: + case PM_LIZARD: case PM_CHAMELEON: case PM_CROCODILE: + case PM_SALAMANDER: case PM_LONG_WORM_TAIL: + + case PM_ARCHEOLOGIST: case PM_BARBARIAN: case PM_CAVE_DWELLER: + case PM_HEALER: case PM_KNIGHT: case PM_MONK: case PM_CLERIC: + case PM_RANGER: case PM_ROGUE: case PM_SAMURAI: case PM_TOURIST: + case PM_VALKYRIE: case PM_WIZARD: + + case PM_LORD_CARNARVON: case PM_PELIAS: case PM_SHAMAN_KARNOV: + case PM_HIPPOCRATES: case PM_KING_ARTHUR: case PM_GRAND_MASTER: + case PM_ARCH_PRIEST: case PM_ORION: case PM_MASTER_OF_THIEVES: + case PM_LORD_SATO: case PM_TWOFLOWER: case PM_NORN: + case PM_NEFERET_THE_GREEN: case PM_SCHLIEMANN: + case PM_THOTH_AMON: case PM_TIAMAT: case PM_CYCLOPS: + case PM_IXOTH: case PM_MASTER_KAEN: case PM_NALZOK: + case PM_SCORPIUS: case PM_MASTER_ASSASSIN: case PM_ASHIKAGA_TAKAUJI: + case PM_LORD_SURTUR: case PM_ANARAXIS_THE_BLACK: case PM_STUDENT: + case PM_CHIEFTAIN: case PM_NEANDERTHAL: case PM_ATTENDANT: + 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: +#else default: +#endif default_1: - if (gm.mvitals[mndx].mvflags & G_NOCORPSE) { + if (svm.mvitals[mndx].mvflags & G_NOCORPSE) { return (struct obj *) 0; } else { corpstatflags |= CORPSTAT_INIT; @@ -787,7 +976,7 @@ make_corpse(struct monst *mtmp, unsigned int corpseflags) /* if polymorph or undead turning has killed this monster, prevent the same attack beam from hitting its corpse */ - if (gc.context.bypasses) + if (svc.context.bypasses) bypass_obj(obj); if (has_mgivenname(mtmp)) @@ -820,7 +1009,7 @@ make_corpse(struct monst *mtmp, unsigned int corpseflags) /* check mtmp and water/lava for compatibility, 0 (survived), 1 (died) */ int -minliquid(struct monst* mtmp) +minliquid(struct monst *mtmp) { int res; @@ -833,8 +1022,8 @@ minliquid(struct monst* mtmp) } /* guts of minliquid() */ -static int -minliquid_core(struct monst* mtmp) +staticfn int +minliquid_core(struct monst *mtmp) { boolean inpool, inlava, infountain, inair; boolean waterwall = is_waterwall(mtmp->mx,mtmp->my); @@ -868,7 +1057,7 @@ minliquid_core(struct monst* mtmp) int dam = d(2, 6); if (cansee(mtmp->mx, mtmp->my)) - pline("%s rusts.", Monnam(mtmp)); + pline_mon(mtmp, "%s rusts.", Monnam(mtmp)); mtmp->mhp -= dam; if (mtmp->mhpmax > dam) mtmp->mhpmax -= dam; @@ -899,7 +1088,7 @@ minliquid_core(struct monst* mtmp) struct attack *dummy = &mtmp->data->mattk[0]; const char *how = on_fire(mtmp->data, dummy); - pline("%s %s.", Monnam(mtmp), + pline_mon(mtmp, "%s %s.", Monnam(mtmp), !strcmp(how, "boiling") ? "boils away" : !strcmp(how, "melting") ? "melts away" : "burns to a crisp"); @@ -908,7 +1097,7 @@ minliquid_core(struct monst* mtmp) hero to create lava beneath a monster, so the !mon_moving case is not expected to happen (and we haven't made a player-against-monster variation of the message above) */ - if (gc.context.mon_moving) + if (svc.context.mon_moving) mondead(mtmp); /* no corpse */ else xkilled(mtmp, XKILL_NOMSG); @@ -916,10 +1105,12 @@ minliquid_core(struct monst* mtmp) mtmp->mhp -= 1; if (DEADMONSTER(mtmp)) { if (cansee(mtmp->mx, mtmp->my)) - pline("%s surrenders to the fire.", Monnam(mtmp)); + pline_mon(mtmp, "%s surrenders to the fire.", + Monnam(mtmp)); mondead(mtmp); /* no corpse */ } else if (cansee(mtmp->mx, mtmp->my)) - pline("%s burns slightly.", Monnam(mtmp)); + pline_mon(mtmp, "%s burns slightly.", + Monnam(mtmp)); } if (!DEADMONSTER(mtmp)) { (void) fire_damage_chain(mtmp->minvent, FALSE, FALSE, @@ -934,7 +1125,8 @@ minliquid_core(struct monst* mtmp) * water damage to dead monsters' inventory, but survivors need to * be handled here. Swimmers are able to protect their stuff... */ - if ((waterwall || !is_clinger(mtmp->data)) && !cant_drown(mtmp->data)) { + if ((waterwall || !is_clinger(mtmp->data)) + && !cant_drown(mtmp->data)) { /* like hero with teleport intrinsic or spell, teleport away if possible */ if (can_teleport(mtmp->data) && !tele_restrict(mtmp)) { @@ -942,8 +1134,8 @@ minliquid_core(struct monst* mtmp) return 0; } if (cansee(mtmp->mx, mtmp->my)) { - if (gc.context.mon_moving) - pline("%s drowns.", Monnam(mtmp)); + if (svc.context.mon_moving) + pline_mon(mtmp, "%s drowns.", Monnam(mtmp)); else /* hero used fire to melt ice that monster was on */ You("drown %s.", mon_nam(mtmp)); @@ -954,7 +1146,7 @@ minliquid_core(struct monst* mtmp) pline("%s sinks as %s rushes in and flushes you out.", Monnam(mtmp), hliquid("water")); } - if (gc.context.mon_moving) + if (svc.context.mon_moving) mondied(mtmp); /* ok to leave corpse despite water */ else xkilled(mtmp, XKILL_NOMSG); @@ -1001,7 +1193,7 @@ mcalcmove( else if (mon->mspeed == MFAST) mmove = (4 * mmove + 2) / 3; - if (mon == u.usteed && u.ugallop && gc.context.mv) { + if (mon == u.usteed && u.ugallop && svc.context.mv) { /* increase movement by a factor of 1.5; also increase variance of movement speed (if it's naturally 24, we don't want it to always become 36) */ @@ -1040,7 +1232,7 @@ mcalcdistress(void) iter_mons(m_calcdistress); } -static void +staticfn void m_calcdistress(struct monst *mtmp) { /* must check non-moving monsters once/turn in case they managed @@ -1091,10 +1283,8 @@ m_calcdistress(struct monst *mtmp) * level to die off. */ /* possibly polymorph shapechangers and lycanthropes */ - if (mtmp->cham >= LOW_PM) - decide_to_shapeshift(mtmp, (canspotmon(mtmp) - || engulfing_u(mtmp)) - ? SHIFT_MSG : 0); + if (ismnum(mtmp->cham)) + decide_to_shapeshift(mtmp); were_change(mtmp); /* gradually time out temporary problems */ @@ -1117,7 +1307,7 @@ movemon_singlemon(struct monst *mtmp) if (u.utotype #ifdef SAFERHANGUP /* or if the program has lost contact with the user */ - || gp.program_state.done_hup + || program_state.done_hup #endif ) { gs.somebody_can_move = FALSE; @@ -1132,9 +1322,9 @@ movemon_singlemon(struct monst *mtmp) mon->isgd flag so that dmonsfree() will get rid of mon) */ if (mtmp->isgd && !mtmp->mx && !(mtmp->mstate & MON_MIGRATING)) { /* parked at <0,0>; eventually isgd should get set to false */ - if (gm.moves > mtmp->mlstmv) { + if (svm.moves > mtmp->mlstmv) { (void) gd_move(mtmp); - mtmp->mlstmv = gm.moves; + mtmp->mlstmv = svm.moves; } return FALSE; } @@ -1145,6 +1335,8 @@ movemon_singlemon(struct monst *mtmp) if (mon_offmap(mtmp)) return FALSE; + m_everyturn_effect(mtmp); + /* Find a monster that we have not treated yet. */ if (mtmp->movement < NORMAL_SPEED) return FALSE; @@ -1155,7 +1347,7 @@ movemon_singlemon(struct monst *mtmp) vision_recalc(0); /* vision! */ /* reset obj bypasses before next monster moves */ - if (gc.context.bypasses) + if (svc.context.bypasses) clear_bypasses(); clear_splitobjs(); if (minliquid(mtmp)) @@ -1189,7 +1381,7 @@ movemon_singlemon(struct monst *mtmp) if (mtmp->mundetected) return FALSE; } else if (mtmp->data->mlet == S_EEL && !mtmp->mundetected - && (mtmp->mflee || !next2u(mtmp->mx, mtmp->my)) + && (mtmp->mflee || !m_next2u(mtmp)) && !canseemon(mtmp) && !rn2(4)) { /* some eels end up stuck in isolated pools, where they can't--or at least won't--move, so they never reach @@ -1236,7 +1428,7 @@ movemon(void) if (any_light_source()) gv.vision_full_recalc = 1; /* in case a mon moved w/ a light source */ /* reset obj bypasses after last monster has moved */ - if (gc.context.bypasses) + if (svc.context.bypasses) clear_bypasses(); clear_splitobjs(); /* remove dead monsters; dead vault guard will be left at <0,0> @@ -1285,14 +1477,13 @@ meatbox(struct monst *mon, struct obj *otmp) } } -#define mstoning(obj) \ - (ofood(obj) && (touch_petrifies(&mons[(obj)->corpsenm]) \ - || (obj)->corpsenm == PM_MEDUSA)) +#define mstoning(obj) \ + (ofood(obj) && ismnum(obj->corpsenm) \ + && flesh_petrifies(&mons[obj->corpsenm])) -/* monster consumes an object. - - monster may die, polymorph, grow up, heal, etc; meating is not changed. - object is extracted from any linked list and freed. */ +/* Monster mtmp consumes an object. + Monster may die, polymorph, grow up, heal, etc; meating is not changed. + Object is extracted from any linked list and freed. */ void m_consume_obj(struct monst *mtmp, struct obj *otmp) { @@ -1300,9 +1491,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); @@ -1320,7 +1509,7 @@ m_consume_obj(struct monst *mtmp, struct obj *otmp) || corpsenm == PM_LARGE_MIMIC || corpsenm == PM_GIANT_MIMIC)); slimer = (otmp->otyp == GLOB_OF_GREEN_SLIME); - poly = polyfodder(otmp); + poly = polyfood(otmp); grow = mlevelgain(otmp); heal = mhealup(otmp); eyes = (otmp->otyp == CARROT); @@ -1343,16 +1532,19 @@ m_consume_obj(struct monst *mtmp, struct obj *otmp) mon_to_stone(mtmp); } else if (!resists_ston(mtmp)) { if (vis) - pline("%s turns to stone!", Monnam(mtmp)); + pline_mon(mtmp, "%s turns to stone!", + Monnam(mtmp)); monstone(mtmp); } } if (heal) - mtmp->mhp = mtmp->mhpmax; + healmon(mtmp, mtmp->mhpmax, 0); if ((eyes || heal) && !mtmp->mcansee) mcureblindness(mtmp, canseemon(mtmp)); if (ispet && deadmimic) quickmimic(mtmp); + if (otmp->otyp == EGG && corpsenm == PM_PYROLISK) + explode(mtmp->mx, mtmp->my, -11, d(3, 6), 0, EXPL_FIERY); if (corpsenm != NON_PM) mon_givit(mtmp, &mons[corpsenm]); } @@ -1377,12 +1569,14 @@ meatmetal(struct monst *mtmp) return 0; /* Eats topmost metal object if it is there */ - for (otmp = gl.level.objects[mtmp->mx][mtmp->my]; otmp; + for (otmp = svl.level.objects[mtmp->mx][mtmp->my]; otmp; otmp = otmp->nexthere) { /* Don't eat indigestible/choking/inappropriate objects */ if (!can_eat_material(mtmp->data, otmp->material) || (otmp->otyp == AMULET_OF_STRANGULATION) - || (otmp->otyp == RIN_SLOW_DIGESTION)) + || (otmp->otyp == RIN_SLOW_DIGESTION) + || ((otmp->opoisoned || permapoisoned(otmp)) + && !resists_poison(mtmp))) continue; if (is_metallic(otmp) && !obj_resists(otmp, 5, 95) && touch_artifact(otmp, mtmp)) { @@ -1391,8 +1585,9 @@ meatmetal(struct monst *mtmp) /* call distant_name() for its side-effects even when !verbose so won't be printed */ otmpname = distant_name(otmp, doname); - if (Verbose(1, meatmetal1)) - pline("%s eats %s!", Monnam(mtmp), otmpname); + if (flags.verbose) + pline_mon(mtmp, "%s eats %s!", + Monnam(mtmp), otmpname); } /* The object's rustproofing is gone now */ otmp->oerodeproof = 0; @@ -1400,18 +1595,19 @@ meatmetal(struct monst *mtmp) if (vis) { /* (see above; format even if it won't be printed) */ otmpname = distant_name(otmp, doname); - if (Verbose(1, meatmetal2)) - pline("%s spits %s out in disgust!", + if (flags.verbose) + pline_mon(mtmp, "%s spits %s out in disgust!", Monnam(mtmp), otmpname); } } else { if (cansee(mtmp->mx, mtmp->my)) { /* (see above; format even if it won't be printed) */ otmpname = distant_name(otmp, doname); - if (Verbose(1, meatmetal3)) - pline("%s eats %s!", Monnam(mtmp), otmpname); + if (flags.verbose) + pline_mon(mtmp, "%s eats %s!", + Monnam(mtmp), otmpname); } else { - if (Verbose(1, meatmetal4)) { + if (flags.verbose) { Soundeffect(se_crunching_sound, 50); You_hear("a crunching sound."); } @@ -1466,7 +1662,7 @@ meatobj(struct monst* mtmp) /* for gelatinous cubes */ /* eat organic objects, including cloth and wood, if present; engulf others, except huge rocks and metal attached to player [despite comment at top, doesn't assume that eater is a g-cube] */ - for (otmp = gl.level.objects[mtmp->mx][mtmp->my]; otmp; otmp = otmp2) { + for (otmp = svl.level.objects[mtmp->mx][mtmp->my]; otmp; otmp = otmp2) { otmp2 = otmp->nexthere; /* avoid special items; once hero picks them up, they'll cease @@ -1510,6 +1706,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)) @@ -1534,15 +1731,16 @@ meatobj(struct monst* mtmp) /* for gelatinous cubes */ if (cansee(mtmp->mx, mtmp->my)) { /* (see above; distant_name() sometimes has side-effects */ otmpname = distant_name(otmp, doname); - if (Verbose(2, meatobj1)) - pline("%s eats %s!", Monnam(mtmp), otmpname); + if (flags.verbose) + pline_mon(mtmp, "%s eats %s!", + Monnam(mtmp), otmpname); /* give this one even if !verbose */ if (otmp->oclass == SCROLL_CLASS && objdescr_is(otmp, "YUM YUM")) pline("Yum%c", otmp->blessed ? '!' : '.'); } else { Soundeffect(se_slurping_sound, 30); - if (Verbose(2, meatobj2)) + if (flags.verbose) You_hear("a slurping sound."); } m_consume_obj(mtmp, otmp); @@ -1558,9 +1756,9 @@ meatobj(struct monst* mtmp) /* for gelatinous cubes */ } if (ecount > 0) { - if (cansee(mtmp->mx, mtmp->my) && Verbose(2, meatobj3) && buf[0]) + if (cansee(mtmp->mx, mtmp->my) && flags.verbose && buf[0]) pline1(buf); - else if (Verbose(2, meatobj4)) + else if (flags.verbose) You_hear("%s slurping sound%s.", (ecount == 1) ? "a" : "several", plur(ecount)); } @@ -1582,7 +1780,7 @@ meatrocks(struct monst *mtmp) return 0; /* Eats topmost rock or gem object if it is there */ - for (otmp = gl.level.objects[mtmp->mx][mtmp->my]; otmp; + for (otmp = svl.level.objects[mtmp->mx][mtmp->my]; otmp; otmp = otmp->nexthere) { if (otmp->oclass == ROCK_CLASS) /* boulders and statues are too big to be bothered with */ @@ -1593,10 +1791,10 @@ meatrocks(struct monst *mtmp) continue; if (cansee(mtmp->mx, mtmp->my)) { otmpname = distant_name(otmp, doname); - if (Verbose(1, meatmetal3)) + if (flags.verbose) pline("%s eats %s!", Monnam(mtmp), otmpname); } else { - if (Verbose(1, meatmetal4)) { + if (flags.verbose) { Soundeffect(se_crunching_sound, 50); You_hear("a crunching sound."); } @@ -1620,7 +1818,7 @@ meatrocks(struct monst *mtmp) #undef mstoning /* Monster eats a corpse off the ground. - * Return value is 0 = nothing eaten, 1 = ate a corpse, 2 = died */ + Return value is 0 = nothing eaten, 1 = ate a corpse, 2 = died. */ int meatcorpse( struct monst *mtmp) /* for purple worms and other voracious monsters */ @@ -1642,7 +1840,7 @@ meatcorpse( /* skip some corpses */ if (vegan(corpsepm) /* ignore veggy corpse even if omnivorous */ /* don't eat harmful corpses */ - || (touch_petrifies(corpsepm) && !resists_ston(mtmp))) + || (flesh_petrifies(corpsepm) && !resists_ston(mtmp))) continue; if (is_rider(corpsepm)) { boolean revived_it = revive_corpse(otmp, FALSE); @@ -1666,11 +1864,12 @@ meatcorpse( the result won't be printed */ char *otmpname = distant_name(otmp, doname); - if (Verbose(2, meatcorpse1)) - pline("%s eats %s!", Monnam(mtmp), otmpname); + if (flags.verbose) + pline_mon(mtmp, "%s eats %s!", + Monnam(mtmp), otmpname); } else { Soundeffect(se_masticating_sound, 50); - if (Verbose(2, meatcorpse2)) + if (flags.verbose) You_hear("a masticating sound."); } @@ -1717,32 +1916,26 @@ mon_give_prop(struct monst *mtmp, int prop) const char *msg = NULL; unsigned short intrinsic = 0; /* MR_* constant */ - /* Pets don't have all the fields that the hero does, so they can't get all - the same intrinsics. If it happens to choose strength gain or teleport - control or whatever, ignore it. */ + /* Pets don't have all the fields that the hero does, so they can't get + all the same intrinsics. If it happens to choose strength gain or + teleport control or whatever, ignore it. */ switch (prop) { case FIRE_RES: - intrinsic = MR_FIRE; msg = "%s shivers slightly."; break; case COLD_RES: - intrinsic = MR_COLD; msg = "%s looks quite warm."; break; case SLEEP_RES: - intrinsic = MR_SLEEP; msg = "%s looks wide awake."; break; case DISINT_RES: - intrinsic = MR_DISINT; msg = "%s looks very firm."; break; case SHOCK_RES: - intrinsic = MR_ELEC; msg = "%s crackles with static electricity."; break; case POISON_RES: - intrinsic = MR_POISON; msg = "%s looks healthy."; break; case TELEPAT: @@ -1755,6 +1948,7 @@ mon_give_prop(struct monst *mtmp, int prop) return; /* can't give it */ break; } + intrinsic = res_to_mr(prop); /* Don't give message if it already had this property intrinsically, but still do grant the intrinsic if it only had it from mresists. @@ -1768,7 +1962,7 @@ mon_give_prop(struct monst *mtmp, int prop) if (canseemon(mtmp) && msg) { DISABLE_WARNING_FORMAT_NONLITERAL - pline(msg, Monnam(mtmp)); + pline_mon(mtmp, msg, Monnam(mtmp)); RESTORE_WARNING_FORMAT_NONLITERAL } } @@ -1780,6 +1974,9 @@ mon_givit(struct monst *mtmp, struct permonst *ptr) int prop = corpse_intrinsic(ptr); boolean vis = canseemon(mtmp); + if (DEADMONSTER(mtmp)) + return; + if (ptr == &mons[PM_STALKER]) { /* * Invisible stalker isn't flagged as conferring invisibility @@ -1802,7 +1999,7 @@ mon_givit(struct monst *mtmp, struct permonst *ptr) Strcpy(mtmpbuf, Monnam(mtmp)); mon_set_minvis(mtmp); if (vis) - pline("%s %s.", mtmpbuf, + pline_mon(mtmp, "%s %s.", mtmpbuf, !canspotmon(mtmp) ? "vanishes" : mtmp->invis_blkd ? "seems to flicker" : "becomes invisible"); @@ -1821,9 +2018,9 @@ mon_givit(struct monst *mtmp, struct permonst *ptr) } void -mpickgold(register struct monst* mtmp) +mpickgold(struct monst *mtmp) { - register struct obj *gold; + struct obj *gold; int mat_idx; if ((gold = g_at(mtmp->mx, mtmp->my)) != 0) { @@ -1831,9 +2028,9 @@ mpickgold(register struct monst* mtmp) obj_extract_self(gold); add_to_minv(mtmp, gold); if (cansee(mtmp->mx, mtmp->my)) { - if (Verbose(2, mpickgold) && !mtmp->isgd) - pline("%s picks up some %s.", Monnam(mtmp), - mat_idx == GOLD ? "gold" : "money"); + if (flags.verbose && !mtmp->isgd) + pline_mon(mtmp, "%s picks up some %s.", Monnam(mtmp), + mat_idx == GOLD ? "gold" : "money"); newsym(mtmp->mx, mtmp->my); } } @@ -1844,7 +2041,7 @@ mpickgold(register struct monst* mtmp) boolean mpickstuff(struct monst *mtmp) { - register struct obj *otmp, *otmp2, *otmp3; + struct obj *otmp, *otmp2, *otmp3; int carryamt = 0; /* prevent shopkeepers from leaving the door of their shop */ @@ -1859,7 +2056,7 @@ mpickstuff(struct monst *mtmp) if (!could_reach_item(mtmp, mtmp->mx, mtmp->my)) return FALSE; - for (otmp = gl.level.objects[mtmp->mx][mtmp->my]; otmp; otmp = otmp2) { + for (otmp = svl.level.objects[mtmp->mx][mtmp->my]; otmp; otmp = otmp2) { otmp2 = otmp->nexthere; /* avoid special items; once hero picks them up, they'll cease @@ -1891,12 +2088,13 @@ mpickstuff(struct monst *mtmp) from floor and subsequent pickup by mtmp */ char *otmpname = distant_name(otmp, doname); - if (Verbose(2, mpickstuff)) - pline("%s picks up %s.", Monnam(mtmp), otmpname); + if (flags.verbose) + pline_mon(mtmp, "%s picks up %s.", + Monnam(mtmp), otmpname); } obj_extract_self(otmp3); /* remove from floor */ (void) mpickobj(mtmp, otmp3); /* may merge and free otmp3 */ - /* let them try and equip it on the next turn */ + /* let them try to equip it on the next turn */ check_gear_next_turn(mtmp); newsym(mtmp->mx, mtmp->my); return TRUE; /* pick only one object */ @@ -1906,7 +2104,7 @@ mpickstuff(struct monst *mtmp) } int -curr_mon_load(struct monst* mtmp) +curr_mon_load(struct monst *mtmp) { int curload = 0; struct obj *obj; @@ -1920,7 +2118,7 @@ curr_mon_load(struct monst* mtmp) } int -max_mon_load(struct monst* mtmp) +max_mon_load(struct monst *mtmp) { long maxload; @@ -1973,7 +2171,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. * @@ -1983,7 +2181,7 @@ can_touch_safely(struct monst *mtmp, struct obj *otmp) * likesgold handling m_move results in picking up the whole stack. */ int -can_carry(struct monst* mtmp, struct obj* otmp) +can_carry(struct monst *mtmp, struct obj *otmp) { int iquan, otyp = otmp->otyp, newload = otmp->owt; struct permonst *mdat = mtmp->data; @@ -2052,7 +2250,7 @@ can_carry(struct monst* mtmp, struct obj* otmp) } /* is in direct line with where 'mon' thinks hero is? */ -static boolean +staticfn boolean monlineu(struct monst *mon, int nx, int ny) { return online2(nx, ny, mon->mux, mon->muy); @@ -2060,7 +2258,7 @@ monlineu(struct monst *mon, int nx, int ny) /* return flags based on monster data, for mfndpos() */ long -mon_allowflags(struct monst* mtmp) +mon_allowflags(struct monst *mtmp) { long allowflags = 0L; boolean doorbuster = is_giant(mtmp->data); @@ -2097,7 +2295,11 @@ mon_allowflags(struct monst* mtmp) allowflags |= OPENDOOR; if (can_unlock(mtmp)) allowflags |= UNLOCKDOOR; - if (passes_bars(mtmp->data)) + if (passes_bars(mtmp->data) + /* restrict engulfer or holder who might try to pass iron bars while + carrying hero; accept small subset for poly'd hero passes_bars() */ + && (mtmp != u.ustuck || (unsolid(gy.youmonst.data) + || verysmall(gy.youmonst.data)))) allowflags |= ALLOW_BARS; #if 0 /* can't do this here; leave it for mfndpos() */ if (is_displacer(mtmp->data)) @@ -2139,7 +2341,7 @@ mfndpos( long flag) { struct permonst *mdat = mon->data; - register struct trap *ttmp; + struct trap *ttmp; coordxy x, y, nx, ny; int cnt = 0; uchar ntyp; @@ -2202,14 +2404,14 @@ 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) && (!(ntyp == SDOOR && door_is_iron(&levl[nx][ny])) || (metallivorous(mon->data) && rockok)))) 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))) @@ -2274,7 +2476,8 @@ mfndpos( /* Displacement also displaces the Elbereth/scare monster, * as long as you are visible. */ - if (Displaced && monseeu && mon->mux == nx && mon->muy == ny) { + if (Displaced && monseeu + && mon->mux == nx && mon->muy == ny) { dispx = u.ux; dispy = u.uy; } else { @@ -2325,7 +2528,8 @@ mfndpos( } /* Note: ALLOW_SANCT only prevents movement, not attack, into a temple. */ - if (gl.level.flags.has_temple && *in_rooms(nx, ny, TEMPLE) + if (svl.level.flags.has_temple + && *in_rooms(nx, ny, TEMPLE) && !*in_rooms(x, y, TEMPLE) && in_your_sanctuary((struct monst *) 0, nx, ny)) { if (!(flag & ALLOW_SANCT)) @@ -2370,12 +2574,15 @@ mfndpos( */ if ((ttmp = t_at(nx, ny)) != 0) { if (ttmp->ttyp >= TRAPNUM || ttmp->ttyp == 0) { - impossible( - "A monster looked at a very strange trap of type %d.", + impossible("A monster looked at a very strange trap" + " of type %d.", ttmp->ttyp); continue; } - if (immune_to_trap(mon, ttmp->ttyp)) { + /* fixed-destination teleport trap, was used by hero */ + if (fixed_tele_trap(ttmp) && hastrack(nx, ny)) + info[cnt] |= ALLOW_TRAPS; + else if (immune_to_trap(mon, ttmp->ttyp)) { if (!(flag & ALLOW_TRAPS)) { if (mon_knows_traps(mon, ttmp->ttyp)) continue; @@ -2398,7 +2605,7 @@ mfndpos( /* Part of mm_aggression that represents two-way aggression. To avoid having to code each case twice, this function contains those cases that ought to happen twice, and mm_aggression will call it twice. */ -static long +staticfn long mm_2way_aggression(struct monst *magr, struct monst *mdef) { struct permonst *ma = magr->data; @@ -2500,7 +2707,7 @@ mm_aggression( } /* Monster displacing another monster out of the way */ -static long +staticfn long mm_displacement( struct monst *magr, /* monster that is currently deciding where to move */ struct monst *mdef) /* another monster which is next to it */ @@ -2620,8 +2827,8 @@ replmon(struct monst *mtmp, struct monst *mtmp2) } mtmp->minvent = 0; /* before relmon(mtmp), because it could clear polearm.hitmon */ - if (gc.context.polearm.hitmon == mtmp) - gc.context.polearm.hitmon = mtmp2; + if (svc.context.polearm.hitmon == mtmp) + svc.context.polearm.hitmon = mtmp2; /* remove the old monster from the map and from `fmon' list */ relmon(mtmp, (struct monst **) 0); @@ -2690,7 +2897,7 @@ relmon( } void -copy_mextra(struct monst* mtmp2, struct monst* mtmp1) +copy_mextra(struct monst *mtmp2, struct monst *mtmp1) { if (!mtmp2 || !mtmp1 || !mtmp1->mextra) return; @@ -2704,26 +2911,31 @@ copy_mextra(struct monst* mtmp2, struct monst* mtmp1) if (EGD(mtmp1)) { if (!EGD(mtmp2)) newegd(mtmp2); + assert(has_egd(mtmp2)); *EGD(mtmp2) = *EGD(mtmp1); } if (EPRI(mtmp1)) { if (!EPRI(mtmp2)) newepri(mtmp2); + assert(has_epri(mtmp2)); *EPRI(mtmp2) = *EPRI(mtmp1); } if (ESHK(mtmp1)) { if (!ESHK(mtmp2)) neweshk(mtmp2); + assert(has_eshk(mtmp2)); *ESHK(mtmp2) = *ESHK(mtmp1); } if (EMIN(mtmp1)) { if (!EMIN(mtmp2)) newemin(mtmp2); + assert(has_emin(mtmp2)); *EMIN(mtmp2) = *EMIN(mtmp1); } if (EDOG(mtmp1)) { if (!EDOG(mtmp2)) newedog(mtmp2); + assert(has_edog(mtmp2)); *EDOG(mtmp2) = *EDOG(mtmp1); } if (EBONES(mtmp1)) { @@ -2736,7 +2948,7 @@ copy_mextra(struct monst* mtmp2, struct monst* mtmp1) } void -dealloc_mextra(struct monst* m) +dealloc_mextra(struct monst *m) { struct mextra *x = m->mextra; @@ -2782,18 +2994,18 @@ dealloc_monst(struct monst *mon) /* 'mon' is being removed from level due to migration [relmon from keepdogs or migrate_to_level] or due to death [m_detach from mondead or mongone] */ -static void +staticfn void mon_leaving_level(struct monst *mon) { coordxy mx = mon->mx, my = mon->my; - boolean onmap = (isok(mx, my) && gl.level.monsters[mx][my] == mon); + boolean onmap = (isok(mx, my) && svl.level.monsters[mx][my] == mon); /* to prevent an infinite relobj-flooreffects-hmon-killed loop */ mon->mtrapped = 0; unstuck(mon); /* mon is not swallowing or holding you nor held by you */ /* vault guard might be at <0,0> */ - if (onmap || mon == gl.level.monsters[0][0]) { + if (onmap || mon == svl.level.monsters[0][0]) { if (mon->wormno) remove_worm(mon); else @@ -2815,12 +3027,12 @@ mon_leaving_level(struct monst *mon) newsym(mx, my); } /* if mon is a remembered target, forget it since it isn't here anymore */ - if (mon == gc.context.polearm.hitmon) - gc.context.polearm.hitmon = (struct monst *) 0; + if (mon == svc.context.polearm.hitmon) + svc.context.polearm.hitmon = (struct monst *) 0; } /* 'mtmp' is going away; remove effects of mtmp from other data structures */ -static void +staticfn void m_detach( struct monst *mtmp, struct permonst *mptr, /* reflects mtmp->data _prior_ to mtmp's death */ @@ -2831,7 +3043,7 @@ m_detach( if (mtmp->mleashed) m_unleash(mtmp, FALSE); - if (mtmp->mx > 0 && emits_light(mptr)) + if (mx > 0 && emits_light(mptr)) del_light_source(LS_MONSTER, monst_to_any(mtmp)); /* @@ -2846,19 +3058,21 @@ m_detach( mon_leaving_level(mtmp); mtmp->mhp = 0; /* simplify some tests: force mhp to 0 */ + /* death handling for the Wizard needs to take place even if he is + leaving the dungeon alive rather than dying */ + if (mtmp->iswiz) + wizdeadorgone(); /* foodead() might give quest feedback for foo having died; skip that if we're called for mongone() rather than mondead(); saving bones or wizard mode genocide of "*" can result in special monsters going away without having been killed */ if (due_to_death) { - if (mtmp->iswiz) - wizdead(); if (mtmp->data->msound == MS_NEMESIS) { nemdead(); /* The Caveman and Priest quest texts describe the nemesis's body creating noxious fumes/gas when killed. */ if (stinky_nemesis(mtmp)) - create_gas_cloud(mx, my, 5, 8); + nemesis_stinks(mx, my); } if (mtmp->data->msound == MS_LEADER) leaddead(); @@ -2927,7 +3141,7 @@ faulty_lifesaver(struct obj* obj) /* give a life-saved monster a reasonable mhpmax value in case it has been the victim of excessive life draining */ -static void +staticfn void set_mon_min_mhpmax( struct monst *mon, int minimum_mhpmax) /* monster life-saving has traditionally used 10 */ @@ -2951,7 +3165,7 @@ set_mon_min_mhpmax( * to determine whether mon has an amulet of life saving that should activate * later, and we don't want it to just die as if the amulet weren't there. */ struct obj * -mlifesaver(struct monst* mon) +mlifesaver(struct monst *mon) { if (!nonliving(mon->data) || is_vampshifter(mon)) { struct obj *otmp = which_armor(mon, W_AMUL); @@ -2962,8 +3176,8 @@ mlifesaver(struct monst* mon) return (struct obj *) 0; } -static void -lifesaved_monster(struct monst* mtmp) +staticfn void +lifesaved_monster(struct monst *mtmp) { boolean surviver; struct obj *lifesave = mlifesaver(mtmp); @@ -3001,7 +3215,7 @@ lifesaved_monster(struct monst* mtmp) /* equip replacement amulet, if any, on next move */ check_gear_next_turn(mtmp); - surviver = !(gm.mvitals[monsndx(mtmp->data)].mvflags & G_GENOD); + surviver = !(svm.mvitals[monsndx(mtmp->data)].mvflags & G_GENOD); mtmp->mcanmove = 1; mtmp->mfrozen = 0; if (mtmp->mtame && !mtmp->isminion) { @@ -3020,10 +3234,8 @@ lifesaved_monster(struct monst* mtmp) } } -DISABLE_WARNING_FORMAT_NONLITERAL - /* transfer the juiblex "mind" to a clone */ -static void +staticfn void juiblex_transferral(void) { struct monst *mtmp; @@ -3036,6 +3248,201 @@ juiblex_transferral(void) } } +/* when a shape-shifted vampire is killed, it reverts to base form instead + 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)) { + char action[BUFSZ]; + /* alternate message phrasing for some monster types */ + boolean spec_mon = (nonliving(mtmp->data) + || noncorporeal(mtmp->data) + || amorphous(mtmp->data)), + spec_death = (gd.disintegested /* disintegrated/digested */ + || noncorporeal(mtmp->data) + || amorphous(mtmp->data)); + coordxy x = mtmp->mx, y = mtmp->my; + + /* 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%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 if + poly'd hero is hugging a vampire bat */ + if (mtmp == u.ustuck) { + if (u.uswallow) + expels(mtmp, mtmp->data, FALSE); + else + uunstick(); + } + + if (!newcham(mtmp, &mons[mndx], NO_NC_FLAGS)) + return !DEADMONSTER(mtmp); + mtmp->cham = (mtmp->data == &mons[mndx]) ? NON_PM : mndx; + + if (canspotmon(mtmp)) { + /* 3.6.0 used a_monnam(mtmp); that was weird if mtmp was + named: "Dracula suddenly transforms and rises as Dracula"; + 3.6.1 used mtmp->data->mname; that ignored hallucination */ + pline_mon(mtmp, "%s %s!", upstart(action), + x_monnam(mtmp, ARTICLE_A, (char *) 0, + (SUPPRESS_NAME | SUPPRESS_IT | SUPPRESS_INVISIBLE), + 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."; + struct rm *door = &levl[x][y]; + + set_msg_xy(x, y); /* You()/pline() will reset this */ + if (!cansee(x, y)) + You_hear(door_smashed); + else if (!canspotmon(mtmp)) + You_see(door_smashed); + else if (!Unaware) + pline_The("door is smashed."); + set_msg_xy(0, 0); /* in case none of the messages was delivered */ + + if (door_is_trapped(door) && getdoortrap(x, y) == FIRE_BLAST) { + doortrapped(x, y, mtmp, TORSO, D_BROKEN, 2); + } + else { + set_doorstate(door, D_BROKEN); + } + unblock_point(x, y); + if (DEADMONSTER(mtmp) && canspotmon(mtmp) && !Unaware) { + /* 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" */ + pline_mon(mtmp, "%s is destroyed!", Monnam(mtmp)); + } + } + newsym(x, y); + return TRUE; + } + return FALSE; +} + +/* specific combination of x_monnam flags for livelogging; show what was + actually killed even when unseen or hallucinated to be something else */ +#define livelog_mon_nam(mtmp) \ + x_monnam(mtmp, ARTICLE_THE, (char *) 0, EXACT_NAME, FALSE) + +/* when a mon has died, maybe record an achievement or issue livelog message; + moved into separate routine to unclutter mondead() */ +staticfn void +logdeadmon(struct monst *mtmp, int mndx) +{ + int howmany = svm.mvitals[mndx].died; + + if (mndx == PM_MEDUSA && howmany == 1) { + record_achievement(ACH_MEDU); /* also generates a livelog event */ + } else if ((unique_corpstat(mtmp->data) + && (mndx != PM_HIGH_CLERIC || !mtmp->mrevived)) + || (mtmp->isshk && !mtmp->mrevived)) { + char shkdetail[QBUFSZ]; + const char *mkilled; + boolean herodidit = !svc.context.mon_moving; + + /* + * livelog event; unique_corpstat() includes the Wizard and + * any High Priest even though they aren't actually unique. + * + * Shopkeeper kills are logged, but only the first time per + * shopkeeper, since their shared kill counter wouldn't work + * for this purpose (and it wouldn't account for polymorphed + * shopkeepers either). + */ + shkdetail[0] = '\0'; + if (mtmp->isshk) { + howmany = 1; + /* ", the proprietor" needs a trailing comma for + the alternate phrasing ", shkdetails, has been killed" + when hero isn't directly responsible */ + Snprintf(shkdetail, sizeof shkdetail, ", the %s %s%s", + shtypes[ESHK(mtmp)->shoptype - SHOPBASE].name, + /* in case shk name doesn't include Mr or Ms honorific */ + mtmp->female ? "proprietrix" : "proprietor", + herodidit ? "" : ","); + } else if (mndx == PM_HIGH_CLERIC) { + /* the high priest[ess] monster is not unique; we know that + this is the first death for this particular high priest + (because of the !mtmp->mrevived test above) */ + howmany = 1; + } + + /* killing a unique more than once doesn't get logged every time; + the Wizard and the Riders can be killed more than once + "naturally", others require deliberate player action such as + use of undead turning to revive a corpse or petrification plus + stone-to-flesh to create and revive a statue */ + if (howmany <= 3 || howmany == 5 || howmany == 10 || howmany == 25 + || (howmany % 50) == 0) { /* 50, 100, 150, 200, 250 */ + char xtra[BUFSZ]; /* space for " (Nth time)" when N > 1 */ + char wherebuf[100]; /* on what level? (for Wizard) */ + long llevent_type = LL_UMONST; + + /* the first kill of any unique monster is a major event; + all kills of the Wizard and the Riders are major when + they're logged but they still don't get logged every time */ + if (howmany == 1 || mtmp->iswiz || is_rider(mtmp->data)) + llevent_type |= LL_ACHIEVE; + + wherebuf[0] = '\0'; + if (mtmp->iswiz) { + char lvldesc[BUFSZ]; + /* special case to show dlvl */ + describe_level(lvldesc, 2); + Snprintf(wherebuf, BUFSZ, "; %s %s", + (In_endgame(&u.uz) ? "on the" + : (In_quest(&u.uz) + || Is_knox(&u.uz)) ? "in" : "on"), + lvldesc); + } + + xtra[0] = '\0'; + if (howmany > 1) /* "(2nd time)" or "(50th time)" */ + Sprintf(xtra, " (for the %d%s time%s)", howmany, ordin(howmany), + wherebuf); + + mkilled = nonliving(mtmp->data) ? "destroyed" : "killed"; + /* hero is responsible: "killed " */ + if (herodidit) + livelog_printf(llevent_type, "%s %s%s%s", + mkilled, + livelog_mon_nam(mtmp), shkdetail, xtra); + else /* trap, pet, conflict: " has been killed" */ + livelog_printf(llevent_type, "%s%s has been %s%s", + livelog_mon_nam(mtmp), shkdetail, + mkilled, xtra); + } + } +} + +/* monster 'mtmp' has died; maybe life-save, otherwise unshapeshift and + update vanquished stats and update map */ void mondead(struct monst *mtmp) { @@ -3052,74 +3459,17 @@ mondead(struct monst *mtmp) if (!DEADMONSTER(mtmp)) return; - if (is_vampshifter(mtmp)) { - /* this only happens if shapeshifted */ - mndx = mtmp->cham; - if (mndx >= LOW_PM && mndx != monsndx(mtmp->data) - && !(gm.mvitals[mndx].mvflags & G_GENOD)) { - coord new_xy; - char buf[BUFSZ]; - /* alternate message phrasing for some monster types */ - boolean spec_mon = (nonliving(mtmp->data) - || noncorporeal(mtmp->data) - || amorphous(mtmp->data)), - spec_death = (gd.disintegested /* disintegrated/digested */ - || noncorporeal(mtmp->data) - || amorphous(mtmp->data)); - coordxy x = mtmp->mx, y = mtmp->my; - - /* construct a format string before transformation; - will be capitalized when used, expects one %s arg */ - Sprintf(buf, "%s suddenly %s and rises as %%s!", - x_monnam(mtmp, ARTICLE_THE, - spec_mon ? (char *) 0 : "seemingly dead", - (SUPPRESS_INVISIBLE | SUPPRESS_IT), FALSE), - 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 */ - 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 (mtmp->data == &mons[mndx]) - mtmp->cham = NON_PM; - else - mtmp->cham = mndx; - if (canspotmon(mtmp)) { - /* 3.6.0 used a_monnam(mtmp); that was weird if mtmp was - named: "Dracula suddenly transforms and rises as Dracula"; - 3.6.1 used mtmp->data->mname; that ignored hallucination */ - pline(upstart(buf), - x_monnam(mtmp, ARTICLE_A, (char *) 0, - (SUPPRESS_NAME | SUPPRESS_IT - | SUPPRESS_INVISIBLE), FALSE)); - gv.vamp_rise_msg = TRUE; - } - newsym(x, y); - return; - } - } - if (mtmp->data == &mons[PM_STEAM_VORTEX]) { - create_gas_cloud(mtmp->mx, mtmp->my, rn2(10) + 5, 0); /* harmless */ - } + /* vampire in bat/fog/wolf form reverts to vampire instead of dying */ + if (is_vampshifter(mtmp) && vamprises(mtmp)) + return; if (be_sad) You("have a %s feeling for a moment, then it passes.", Hallucination ? "woebegone" : "sad"); + if (mtmp->data == &mons[PM_STEAM_VORTEX]) + create_gas_cloud(mtmp->mx, mtmp->my, rn2(10) + 5, 0); /* harmless */ + /* dead vault guard is actually kept at coordinate <0,0> until his temporary corridor to/from the vault has been removed; need to do this after life-saving and before m_detach() */ @@ -3128,7 +3478,7 @@ mondead(struct monst *mtmp) mptr = mtmp->data; /* save this for m_detach() */ /* restore chameleon, lycanthropes to true form at death */ - if (mtmp->cham >= LOW_PM) { + if (ismnum(mtmp->cham)) { set_mon_data(mtmp, &mons[mtmp->cham]); mtmp->cham = NON_PM; } else if (mtmp->data == &mons[PM_WEREJACKAL]) @@ -3142,7 +3492,7 @@ mondead(struct monst *mtmp) juiblex_transferral(); /* - * gm.mvitals[].died does double duty as total number of dead monsters + * svm.mvitals[].died does double duty as total number of dead monsters * and as experience factor for the player killing more monsters. * this means that a dragon dying by other means reduces the * experience the player gets for killing a dragon directly; this @@ -3152,18 +3502,18 @@ mondead(struct monst *mtmp) * for rings of conflict and such. */ mndx = monsndx(mtmp->data); - if (gm.mvitals[mndx].died < 255) - gm.mvitals[mndx].died++; + if (svm.mvitals[mndx].died < 255) + svm.mvitals[mndx].died++; /* if it's a (possibly polymorphed) quest leader, mark him as dead */ - if (mtmp->m_id == gq.quest_status.leader_m_id) - gq.quest_status.killed_leader = TRUE; - if (mtmp->m_id == gq.quest_status.nemesis_m_id) - gq.quest_status.killed_nemesis = TRUE; + if (mtmp->m_id == svq.quest_status.leader_m_id) + svq.quest_status.killed_leader = TRUE; + if (mtmp->m_id == svq.quest_status.nemesis_m_id) + svq.quest_status.killed_nemesis = TRUE; #ifdef MAIL_STRUCTURES /* if the mail daemon dies, no more mail delivery. -3. */ if (mndx == PM_MAIL_DAEMON) - gm.mvitals[mndx].mvflags |= G_GENOD; + svm.mvitals[mndx].mvflags |= G_GENOD; #endif if (mtmp->data->mlet == S_KOP) { @@ -3176,7 +3526,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; @@ -3184,74 +3535,19 @@ mondead(struct monst *mtmp) break; } } -#if 0 /* moved to m_detach() to kick in if mongone() happens */ - if (mtmp->iswiz) - wizdead(); - if (mtmp->data->msound == MS_NEMESIS) - nemdead(); -#endif - - if (mndx == PM_MEDUSA && gm.mvitals[mndx].died == 1) { - record_achievement(ACH_MEDU); /* also generates a livelog event */ - } else if (unique_corpstat(mtmp->data)) { - /* - * livelog event; unique_corpstat() includes the Wizard and - * any High Priest even though they aren't actually unique. - * - * It would be nice to include shopkeepers. Their names are - * unique within each game but unfortunately for this potential - * usage their kill count is lumped together in a group total. - */ - int howmany = gm.mvitals[mndx].died; - - /* killing a unique more than once doesn't get logged every time; - the Wizard and the Riders can be killed more than once - "naturally", others require deliberate player action such as - use of undead turning to revive a corpse or petrification plus - stone-to-flesh to create and revive a statue */ - if (howmany <= 3 || howmany == 5 || howmany == 10 || howmany == 25 - || (howmany % 50) == 0) { /* 50, 100, 150, 200, 250 */ - char xtra[BUFSZ], wherebuf[100]; - long llevent_type = LL_UMONST; - - /* first kill of any unique is a major event; all kills of the - Wizard and the Riders are major if they're logged but they - still don't get logged every time */ - if (howmany == 1 || mtmp->iswiz || is_rider(mtmp->data)) - llevent_type |= LL_ACHIEVE; - - wherebuf[0] = '\0'; - if (mtmp->iswiz) { - char lvldesc[BUFSZ]; - /* special case to show dlvl */ - describe_level(lvldesc, 2); - Snprintf(wherebuf, BUFSZ, "; %s %s", - (In_endgame(&u.uz) ? "on the" - : (In_quest(&u.uz) || Is_knox(&u.uz)) ? "in" - : "on"), - lvldesc); - } - - xtra[0] = '\0'; - if (howmany > 1) /* "(2nd time)" or "(50th time)" */ - Sprintf(xtra, " (for the %d%s time%s)", howmany, ordin(howmany), - wherebuf); - livelog_printf(llevent_type, "%s %s%s", - nonliving(mtmp->data) ? "destroyed" : "killed", - livelog_mon_nam(mtmp), xtra); - } - } + /* achievement and/or livelog */ + logdeadmon(mtmp, mndx); if (glyph_is_invisible(levl[mtmp->mx][mtmp->my].glyph)) unmap_object(mtmp->mx, mtmp->my); + /* remove 'mtmp' from play; it will stay on the fmon list until end of + current move, then dmonsfree() will get rid of it */ m_detach(mtmp, mptr, TRUE); return; } -RESTORE_WARNING_FORMAT_NONLITERAL - /* TRUE if corpse might be dropped, magr may die if mon was swallowed */ boolean corpse_chance( @@ -3264,7 +3560,8 @@ corpse_chance( if (mdat == &mons[PM_VLAD_THE_IMPALER] || mdat->mlet == S_LICH) { if (cansee(mon->mx, mon->my) && !was_swallowed) - pline("%s body crumbles into dust.", s_suffix(Monnam(mon))); + pline_mon(mon, "%s body crumbles into dust.", + s_suffix(Monnam(mon))); return FALSE; } @@ -3280,9 +3577,10 @@ corpse_chance( if (was_swallowed && magr) { if (magr == &gy.youmonst) { There("is an explosion in your %s!", body_part(STOMACH)); - Sprintf(gk.killer.name, "%s explosion", + Sprintf(svk.killer.name, "%s explosion", s_suffix(pmname(mdat, Mgender(mon)))); - losehp(Maybe_Half_Phys(tmp), gk.killer.name, KILLED_BY_AN); + losehp(Maybe_Half_Phys(tmp), svk.killer.name, + KILLED_BY_AN); } else { You_hear("an explosion."); magr->mhp -= tmp; @@ -3290,9 +3588,12 @@ corpse_chance( mondied(magr); if (DEADMONSTER(magr)) { /* maybe lifesaved */ if (canspotmon(magr)) - pline("%s rips open!", Monnam(magr)); + pline_mon(magr, "%s rips open!", + Monnam(magr)); } else if (canseemon(magr)) - pline("%s seems to have indigestion.", Monnam(magr)); + pline_mon(magr, + "%s seems to have indigestion.", + Monnam(magr)); } return FALSE; @@ -3318,7 +3619,7 @@ corpse_chance( /* drop (perhaps) a cadaver and remove monster */ void -mondied(register struct monst* mdef) +mondied(struct monst *mdef) { mondead(mdef); if (!DEADMONSTER(mdef)) @@ -3332,7 +3633,7 @@ mondied(register struct monst* mdef) /* monster disappears, not dies */ void -mongone(struct monst* mdef) +mongone(struct monst *mdef) { mdef->mhp = 0; /* can skip some inventory bookkeeping */ @@ -3352,7 +3653,7 @@ mongone(struct monst* mdef) /* drop a statue or rock and remove monster */ void -monstone(struct monst* mdef) +monstone(struct monst *mdef) { struct obj *otmp, *obj, *oldminvent; coordxy x = mdef->mx, y = mdef->my; @@ -3455,7 +3756,7 @@ monkilled( if (fltxt && (mdef->wormno ? worm_known(mdef) : cansee(mdef->mx, mdef->my))) - pline("%s is %s%s%s!", Monnam(mdef), + pline_mon(mdef, "%s is %s%s%s!", Monnam(mdef), nonliving(mptr) ? "destroyed" : "killed", *fltxt ? " by the " : "", fltxt); else @@ -3493,12 +3794,12 @@ void set_ustuck(struct monst *mtmp) { if (iflags.sanity_check || iflags.debug_fuzzer) { - if (mtmp && !next2u(mtmp->mx, mtmp->my)) + if (mtmp && !m_next2u(mtmp)) impossible("Sticking to %s at distu %d?", mon_nam(mtmp), mdistu(mtmp)); } - gc.context.botl = 1; + disp.botl = TRUE; u.ustuck = mtmp; if (!u.ustuck) { u.uswallow = 0; @@ -3538,7 +3839,7 @@ unstuck(struct monst *mtmp) } void -killed(struct monst* mtmp) +killed(struct monst *mtmp) { xkilled(mtmp, XKILL_GIVEMSG); } @@ -3547,7 +3848,7 @@ killed(struct monst* mtmp) void xkilled( struct monst *mtmp, - int xkill_flags) /* 1: suppress message, 2: suppress corpse, 4: pacifist */ + int xkill_flags) /* 1: suppress mesg, 2: suppress corpse, 4: pacifist */ { int tmp, mndx; coordxy x = mtmp->mx, y = mtmp->my; @@ -3643,7 +3944,7 @@ xkilled( } /* messing with Geryon's herds */ - if (Is_geryon_level(&u.uz) && !gc.context.mon_moving + if (Is_geryon_level(&u.uz) && !svc.context.mon_moving && mdat->mlet == S_QUADRUPED) { angry_geryon(); } @@ -3667,7 +3968,7 @@ xkilled( int otyp; /* illogical but traditional "treasure drop" */ - if (!rn2(6) && !(gm.mvitals[mndx].mvflags & G_NOCORPSE) + if (!rn2(6) && !(svm.mvitals[mndx].mvflags & G_NOCORPSE) /* no extra item from swallower or steed */ && (x != u.ux || y != u.uy) /* no extra item from kops--too easy to abuse */ @@ -3677,7 +3978,15 @@ xkilled( otmp = mkobj(RANDOM_CLASS, TRUE); /* don't create large objects from small monsters */ otyp = otmp->otyp; - if (mdat->msize < MZ_HUMAN && otyp != FIGURINE + if (otmp->oclass == FOOD_CLASS && !(mdat->mflags2 & M2_COLLECT) + && !otmp->oartifact) { + /* don't drop newly created permafood from kills, unless + the monster collects food; it creates too much nutrition + in the late game and encourages grinding in the early + game; oartifact check is paranoia and will be redundant + until an artifact comestible is added */ + delobj(otmp); + } else if (mdat->msize < MZ_HUMAN && otyp != FIGURINE /* oc_big is also oc_bimanual and oc_bulky */ && (otmp->owt > 30 || objects[otyp].oc_big)) { if (otmp->oartifact) /* un-create */ @@ -3719,10 +4028,18 @@ xkilled( newsym(x, y); cleanup: - /* punish bad behavior */ + /* + * Punish bad behavior. + */ if (is_human(mdat) && (!always_hostile(mdat) && mtmp->malign <= 0) + /* exclude role monsters */ && (mndx < PM_ARCHEOLOGIST || mndx > PM_WIZARD) + /* exclude plain "human", which isn't flagged as always hostile; + it is rare and most likely to occur as the result of resurrecting + a corpse or animating a statue and usually will be hostile */ + && mndx != PM_HUMAN + /* only applicable if hero is lawful or neutral */ && u.ualign.type != A_CHAOTIC) { HTelepat &= ~INTRINSIC; change_luck(-2); @@ -3738,19 +4055,19 @@ xkilled( } /* give experience points */ - tmp = experience(mtmp, (int) gm.mvitals[mndx].died); + tmp = experience(mtmp, (int) svm.mvitals[mndx].died); more_experienced(tmp, 0); newexplevel(); /* will decide if you go up */ /* adjust alignment points */ - if (mtmp->m_id == gq.quest_status.leader_m_id) { /* REAL BAD! */ + if (mtmp->m_id == svq.quest_status.leader_m_id) { /* REAL BAD! */ adjalign(-(u.ualign.record + (int) ALIGNLIM / 2)); u.ugangr += 7; /* instantly become "extremely" angry */ change_luck(-20); pline("That was %sa bad idea...", u.uevent.qcompleted ? "probably " : ""); - } else if (mtmp->m_id == gq.quest_status.nemesis_m_id) { /* Real good! */ - if (!gq.quest_status.killed_leader) + } else if (mtmp->m_id == svq.quest_status.nemesis_m_id) { /* Real good! */ + if (!svq.quest_status.killed_leader) adjalign((int) (ALIGNLIM / 4)); } else if (mdat->msound == MS_GUARDIAN) { /* Bad */ adjalign(-(int) (ALIGNLIM / 8)); @@ -3806,12 +4123,12 @@ xkilled( /* changes the monster into a stone monster of the same type this should only be called when poly_when_stoned() is true */ void -mon_to_stone(struct monst* mtmp) +mon_to_stone(struct monst *mtmp) { if (mtmp->data->mlet == S_GOLEM) { /* it's a golem, and not a stone golem */ if (canseemon(mtmp)) - pline("%s solidifies...", Monnam(mtmp)); + pline_mon(mtmp, "%s solidifies...", Monnam(mtmp)); if (newcham(mtmp, &mons[PM_STONE_GOLEM], NO_NC_FLAGS)) { if (canseemon(mtmp)) pline("Now it's %s.", an(pmname(mtmp->data, Mgender(mtmp)))); @@ -3832,7 +4149,7 @@ vamp_stone(struct monst *mtmp) /* this only happens if shapeshifted */ if (mndx >= LOW_PM && mndx != monsndx(mtmp->data) - && !(gm.mvitals[mndx].mvflags & G_GENOD)) { + && !(svm.mvitals[mndx].mvflags & G_GENOD)) { char buf[BUFSZ]; /* construct a format string before transformation */ @@ -3859,7 +4176,7 @@ vamp_stone(struct monst *mtmp) } } if (canspotmon(mtmp)) { - pline("%s!", buf); + pline_mon(mtmp, "%s!", buf); display_nhwindow(WIN_MESSAGE, FALSE); } (void) newcham(mtmp, &mons[mndx], NO_NC_FLAGS); @@ -3868,13 +4185,14 @@ vamp_stone(struct monst *mtmp) else mtmp->cham = mndx; if (canspotmon(mtmp)) { - pline("%s rises from the %s with renewed agility!", + pline_mon(mtmp, + "%s rises from the %s with renewed agility!", Amonnam(mtmp), surface(mtmp->mx, mtmp->my)); } newsym(mtmp->mx, mtmp->my); return FALSE; /* didn't petrify */ } - } else if (mtmp->cham >= LOW_PM + } else if (ismnum(mtmp->cham) && (mons[mtmp->cham].mresists & MR_STONE)) { /* sandestins are stoning-immune so if hit by stoning damage they revert to innate shape rather than become a statue */ @@ -3920,7 +4238,7 @@ migrate_mon( migrate_to_level(mtmp, target_lev, xyloc, (coord *) 0); } -static boolean +staticfn boolean ok_to_obliterate(struct monst *mtmp) { /* @@ -3943,10 +4261,10 @@ elemental_clog(struct monst *mon) if (In_endgame(&u.uz)) { m1 = m2 = m3 = m4 = m5 = zm = (struct monst *) 0; - if (!msgmv || (gm.moves - msgmv) > 200L) { + if (!msgmv || (svm.moves - msgmv) > 200L) { if (!msgmv || rn2(2)) You_feel("besieged."); - msgmv = gm.moves; + msgmv = svm.moves; } /* * m1 an elemental from another plane. @@ -4028,6 +4346,17 @@ mnexto(struct monst *mtmp, unsigned int rlocflags) deal_with_overcrowding(mtmp); return; } + /* wizard-mode player can choose destination by setting 'montelecontrol' + option; enexto()'s value for 'mm' will be the default; 'savemm' is + used to make sure player doesn't choose hero's location and then + answer 'y' to the 'override invalid spot' prompt */ + if (iflags.mon_telecontrol) { + coord savemm = mm; + + if (!control_mon_tele(mtmp, &mm, rlocflags, FALSE)) + mm = savemm; + } + rloc_to_flag(mtmp, mm.x, mm.y, rlocflags); /* if you can now spot the monster, try to do a special boss appearance @@ -4070,6 +4399,7 @@ maybe_mnexto(struct monst *mtmp) if (couldsee(mm.x, mm.y) /* don't move grid bugs diagonally */ && (diagok || mm.x == mtmp->mx || mm.y == mtmp->my)) { + /* [this doesn't honor the 'montelecontrol' option] */ rloc_to(mtmp, mm.x, mm.y); return; } @@ -4089,7 +4419,7 @@ maybe_mnexto(struct monst *mtmp) */ int mnearto( - register struct monst *mtmp, + struct monst *mtmp, coordxy x, coordxy y, boolean move_other, /* make sure mtmp gets to x, y! so move m_at(x, y) */ @@ -4104,11 +4434,9 @@ mnearto( return res; if (move_other && (othermon = m_at(x, y)) != 0) { - if (othermon->wormno) - remove_worm(othermon); - else - remove_monster(x, y); - + /* take othermon off the map; it might end up immediately returning + but for the moment it is leaving */ + mon_leaving_level(othermon); othermon->mx = othermon->my = 0; /* 'othermon' is not on the map */ othermon->mstate |= MON_OFFMAP; } @@ -4133,11 +4461,13 @@ mnearto( newx = mm.x; newy = mm.y; } + /* [this doesn't honor the 'montelecontrol' option] */ rloc_to_flag(mtmp, newx, newy, rlocflags); if (move_other && othermon) { res = 2; /* moving another monster out of the way */ - if (!mnearto(othermon, x, y, FALSE, rlocflags)) /* no 'move_other' this time */ + /* 'move_other'==FALSE this time; fail rather than recurse */ + if (!mnearto(othermon, x, y, FALSE, rlocflags)) deal_with_overcrowding(othermon); } @@ -4147,7 +4477,7 @@ mnearto( /* monster responds to player action; not the same as a passive attack; assumes reason for response has been tested, and response _must_ be made */ void -m_respond(struct monst* mtmp) +m_respond(struct monst *mtmp) { if (mtmp->data->msound == MS_SHRIEK) { if (!Deaf) { @@ -4177,7 +4507,7 @@ m_respond(struct monst* mtmp) wake_nearto(mtmp->mx, mtmp->my, 5 * 5); } if (mtmp->data == &mons[PM_MEDUSA]) { - register int i; + int i; for (i = 0; i < NATTK; i++) if (mtmp->data->mattk[i].aatyp == AT_GAZE) { @@ -4198,7 +4528,7 @@ m_respond(struct monst* mtmp) /* mtmp (a Nazgul) has a chance to shriek to negatively afflict the player. It * may also afflict other monsters. */ -static void +staticfn void nazgul_shriek(struct monst *mtmp) { boolean cansee = canseemon(mtmp); @@ -4271,7 +4601,7 @@ nazgul_shriek(struct monst *mtmp) } /* how quest guardians respond when you attack the quest leader */ -static void +staticfn void qst_guardians_respond(void) { struct monst *mon; @@ -4299,7 +4629,7 @@ qst_guardians_respond(void) } /* how other peacefuls react when you attack monster */ -static void +staticfn void peacefuls_respond(struct monst *mtmp) { struct monst *mon; @@ -4339,18 +4669,24 @@ peacefuls_respond(struct monst *mtmp) } } /* shopkeepers and temple priests might gasp in - surprise, but they won't become angry here */ - if (mon->isshk || mon->ispriest) { + surprise, but they won't become angry here; + quest leader will only get angry if hero attacks + own quest guardians */ + if (mon->isshk || mon->ispriest + || (mon->data == &mons[quest_info(MS_LEADER)] + && mtmp->data != &mons[gu.urole.guardnum])) { if (exclaimed) - pline("%s%s", buf, " then shrugs."); + pline_mon(mon, "%s%s", buf, " then shrugs."); continue; } - if (mon->data->mlevel < rn2(10)) { + if (mon->data->mlevel < rn2(10) + /* don't have quest guardians turn to flee */ + && (mon->data != &mons[gu.urole.guardnum])) { alreadyfleeing = (mon->mflee || mon->mfleetim); monflee(mon, rn2(50) + 25, TRUE, !exclaimed); if (exclaimed) { - if (Verbose(2, setmangry) && !alreadyfleeing) { + if (flags.verbose && !alreadyfleeing) { Strcat(buf, " and then turns to flee."); needpunct = FALSE; } @@ -4358,15 +4694,16 @@ peacefuls_respond(struct monst *mtmp) exclaimed = TRUE; /* got msg from monflee() */ } if (*buf) - pline("%s%s", buf, needpunct ? "." : ""); + pline_mon(mon, "%s%s", buf, needpunct ? "." : ""); if (mon->mtame) { ; /* mustn't set mpeaceful to 0 as below; * perhaps reduce tameness? */ } else { mon->mpeaceful = 0; + mon->mstrategy &= ~STRAT_WAITMASK; adjalign(-1); if (!exclaimed) - pline("%s gets angry!", Monnam(mon)); + pline_mon(mon, "%s gets angry!", Monnam(mon)); } } } else if (mon->data->mlet == mtmp->data->mlet @@ -4395,7 +4732,7 @@ peacefuls_respond(struct monst *mtmp) where mtmp was already hostile; it checks for situations where the player shouldn't be attacking and any ramifications /that/ might have. */ void -setmangry(struct monst* mtmp, boolean via_attack) +setmangry(struct monst *mtmp, boolean via_attack) { if (via_attack && sengr_at("Elbereth", u.ux, u.uy, TRUE) /* only hypocritical if monster is vulnerable to Elbereth (or @@ -4438,7 +4775,7 @@ setmangry(struct monst* mtmp, boolean via_attack) } if (humanoid(mtmp->data) || mtmp->isshk || mtmp->isgd) { if (couldsee(mtmp->mx, mtmp->my)) - pline("%s gets angry!", Monnam(mtmp)); + pline_mon(mtmp, "%s gets angry!", Monnam(mtmp)); } else { growl(mtmp); } @@ -4448,10 +4785,21 @@ setmangry(struct monst* mtmp, boolean via_attack) qst_guardians_respond(); /* make other peaceful monsters react */ - if (!gc.context.mon_moving) + if (!svc.context.mon_moving) peacefuls_respond(mtmp); } +/* Indicate via message that a monster has awoken. */ +void +wake_msg(struct monst *mtmp, boolean interesting) +{ + if (mtmp->msleeping && canseemon(mtmp)) { + pline_mon(mtmp, "%s wakes up%s%s", + Monnam(mtmp), interesting ? "!" : ".", + mtmp->data == &mons[PM_FLESH_GOLEM] ? " It's alive!" : ""); + } +} + /* wake up a monster, possibly making it angry in the process */ void wakeup(struct monst* mtmp, boolean via_attack, boolean reveal_hidden) @@ -4460,27 +4808,21 @@ wakeup(struct monst* mtmp, boolean via_attack, boolean reveal_hidden) if (DEADMONSTER(mtmp)) { return; } + wake_msg(mtmp, via_attack); mtmp->msleeping = 0; if (reveal_hidden) { if (M_AP_TYPE(mtmp) != M_AP_NOTHING) { /* mimics come out of hiding, but disguised Wizard doesn't - have to lose his disguise */ + have to lose his disguise */ if (M_AP_TYPE(mtmp) != M_AP_MONSTER) seemimic(mtmp); - } else if (gc.context.forcefight && !gc.context.mon_moving - && mtmp->mundetected) { + } else if (svc.context.forcefight && !svc.context.mon_moving + && mtmp->mundetected) { mtmp->mundetected = 0; newsym(mtmp->mx, mtmp->my); } finish_meating(mtmp); } - if (was_sleeping && canseemon(mtmp) - /* don't print message for still-disguised monster */ - && (M_AP_TYPE(mtmp) == M_AP_NOTHING - || M_AP_TYPE(mtmp) == M_AP_MONSTER)) { - pline("%s wakes up%s%s", Monnam(mtmp), via_attack ? "!" : ".", - mtmp->data == &mons[PM_FLESH_GOLEM] ? " It's alive!" : ""); - } if (via_attack) { boolean was_peaceful = mtmp->mpeaceful; @@ -4498,14 +4840,14 @@ wakeup(struct monst* mtmp, boolean via_attack, boolean reveal_hidden) /* Wake up nearby monsters without angering them. */ void -wake_nearby(void) +wake_nearby(boolean petcall) { - wake_nearto(u.ux, u.uy, u.ulevel * 20); + wake_nearto_core(u.ux, u.uy, u.ulevel * 20, petcall); } /* Wake up monsters near some particular location. */ -void -wake_nearto(coordxy x, coordxy y, int distance) +staticfn void +wake_nearto_core(coordxy x, coordxy y, int distance, boolean petcall) { struct monst *mtmp; @@ -4518,21 +4860,28 @@ wake_nearto(coordxy x, coordxy y, int distance) wakeup(mtmp, FALSE, FALSE); /* wake indeterminate sleep */ if (!(mtmp->data->geno & G_UNIQ)) mtmp->mstrategy &= ~STRAT_WAITMASK; /* wake 'meditation' */ - if (gc.context.mon_moving) + if (svc.context.mon_moving || !petcall) continue; if (mtmp->mtame) { if (!mtmp->isminion) - EDOG(mtmp)->whistletime = gm.moves; + EDOG(mtmp)->whistletime = svm.moves; /* Fix up a pet who is stuck "fleeing" its master */ mon_track_clear(mtmp); } } } + disturb_buried_zombies(x, y); +} + +void +wake_nearto(coordxy x, coordxy y, int distance) +{ + wake_nearto_core(x, y, distance, FALSE); } /* NOTE: we must check for mimicry before calling this routine */ void -seemimic(register struct monst* mtmp) +seemimic(struct monst *mtmp) { boolean is_blocker_appear = (is_lightblocker_mappear(mtmp)); @@ -4558,7 +4907,7 @@ normal_shape(struct monst *mon) { int mcham = (int) mon->cham; - if (mcham >= LOW_PM) { + if (ismnum(mcham)) { unsigned mcan = mon->mcan; (void) newcham(mon, &mons[mcham], NC_SHOW_MSG); @@ -4587,94 +4936,169 @@ normal_shape(struct monst *mon) } } -/* iterate all monsters on the level, even dead ones, calling func - for each monster. if func returns TRUE, stop iterating. - safe for list deletions and insertions, and guarantees - calling func once per monster in the list. */ +/* freed by freedynamicdata() when game ends; doesn't need to be struct g */ +static struct monst **itermonarr = NULL; +static unsigned itermonsiz = 0; /* size in 'monst *' pointers */ + +/* manage itermonarr; it used to be allocated and freed every time the + monster movement loop ran; now, keep it around most of the time */ +void +alloc_itermonarr(unsigned count) +{ + /* if count is 0 or bigger than itermonsiz or much smaller than + itermonsiz, release itermonarr (and reset itermonsiz to 0) */ + if (!count || count > itermonsiz || count + 40 < itermonsiz) { + if (itermonarr) + free((genericptr_t) itermonarr), itermonarr = NULL; + itermonsiz = 0; + } + /* when count is more than itermonsiz (including when that just + got reset to 0), allocate a new instance of itermonarr; + implies that count is greater than 0 */ + if (count > itermonsiz) { + /* overallocate to reduce free/alloc-again thrashing when the + number of monsters varies from turn to turn */ + itermonsiz = count + 20; + itermonarr = (struct monst **) alloc( + itermonsiz * sizeof (struct monst *)); + } +} + +/* Iterate all monsters on the level, even dead or off-map ones, calling + bfunc() for each monster. If bfunc() returns TRUE, stop iterating. + If the game ends during the call to bfunc(), then freedynamicdata() + will free 'itermonarr'. + + Safe for list deletions and insertions, and guarantees calling bfunc() + once per monster in fmon unless it returns TRUE (or game ends). */ void -iter_mons_safe(boolean (*func)(struct monst *)) +iter_mons_safe(boolean (*bfunc)(struct monst *)) { - struct monst **monarr; - int i = 0, nmons = 0; struct monst *mtmp; - boolean stopiter = FALSE; + unsigned i, nmons; - for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + for (nmons = 0, mtmp = fmon; mtmp; mtmp = mtmp->nmon) nmons++; - monarr = (struct monst **)alloc(nmons * sizeof(struct monst *)); - for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) - monarr[i++] = mtmp; + /* make sure itermonarr[] is big enough to hold nmons entries */ + alloc_itermonarr(nmons); - for (i = 0; i < nmons && !stopiter; i++) { - mtmp = monarr[i]; - if (func(mtmp)) - stopiter = TRUE; - } + if (nmons) { + for (i = 0, mtmp = fmon; mtmp; mtmp = mtmp->nmon) + itermonarr[i++] = mtmp; - free(monarr); + for (i = 0; i < nmons; i++) { + mtmp = itermonarr[i]; + if ((*bfunc)(mtmp)) + break; + } + } + return; } -/* iterate all living monsters on current level, calling func for each. */ +/* iterate all living monsters on current level, calling vfunc for each. */ void -iter_mons(void (*func)(struct monst *)) +iter_mons(void (*vfunc)(struct monst *)) { - struct monst *mtmp; + struct monst *mtmp, *mtmp2; - for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { - if (DEADMONSTER(mtmp)) + for (mtmp = fmon; mtmp; mtmp = mtmp2) { + mtmp2 = mtmp->nmon; + if (DEADMONSTER(mtmp) || mon_offmap(mtmp)) continue; - func(mtmp); + (*vfunc)(mtmp); } + return; } -/* iterate all living monsters on current level, calling func for each. - if func returns TRUE, stop and return that monster. */ +/* iterate all living monsters on current level, calling bfunc for each. + if bfunc returns TRUE, stop and return that monster. */ struct monst * -get_iter_mons(boolean (*func)(struct monst *)) +get_iter_mons(boolean (*bfunc)(struct monst *)) { - struct monst *mtmp; + struct monst *mtmp, *mtmp2; - for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { - if (DEADMONSTER(mtmp)) + for (mtmp = fmon; mtmp; mtmp = mtmp2) { + mtmp2 = mtmp->nmon; + if (DEADMONSTER(mtmp) || mon_offmap(mtmp)) continue; - if (func(mtmp)) - return mtmp; + if ((*bfunc)(mtmp)) + break; } - return (struct monst *) 0; + return mtmp; } -/* iterate all living monsters on current level, calling func for each, +/* iterate all living monsters on current level, calling bfunc for each, passing x,y to the function. - if func returns TRUE, stop and return that monster. */ + if bfunc returns TRUE, stop and return that monster. */ struct monst * -get_iter_mons_xy(boolean (*func)(struct monst *, coordxy, coordxy), - coordxy x, coordxy y) +get_iter_mons_xy( + boolean (*bfunc)(struct monst *, coordxy, coordxy), + coordxy x, coordxy y) { - struct monst *mtmp; + struct monst *mtmp, *mtmp2; - for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { - if (DEADMONSTER(mtmp)) + for (mtmp = fmon; mtmp; mtmp = mtmp2) { + mtmp2 = mtmp->nmon; + if (DEADMONSTER(mtmp) || mon_offmap(mtmp)) continue; - if (func(mtmp, x, y)) - return mtmp; + if ((*bfunc)(mtmp, x, y)) + break; + } + return mtmp; +} + + +/* 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; } - return (struct monst *) 0; } /* 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) { iter_mons(normal_shape); } -static void +staticfn void m_restartcham(struct monst *mtmp) { if (!mtmp->mcan) @@ -4710,7 +5134,7 @@ restore_cham(struct monst *mon) /* unwatched hiders may hide again; if so, returns True. * Only applies to is_hider monsters, *not* hides_under monsters. */ -static boolean +staticfn boolean restrap(struct monst *mtmp) { struct trap *t; @@ -4720,7 +5144,10 @@ restrap(struct monst *mtmp) /* can't hide while trapped except in pits */ || (mtmp->mtrapped && (t = t_at(mtmp->mx, mtmp->my)) != 0 && !is_pit(t->ttyp)) - || (sensemon(mtmp) && next2u(mtmp->mx, mtmp->my))) + /* can't hide on ceiling if there isn't one */ + || (ceiling_hider(mtmp->data) && !has_ceiling(&u.uz)) + /* won't hide when adjacent to hero */ + || (sensemon(mtmp) && m_next2u(mtmp))) return FALSE; if (mtmp->data->mlet == S_MIMIC) { @@ -4761,36 +5188,57 @@ maybe_unhide_at(coordxy x, coordxy y) } /* monster/hero tries to hide under something at the current location. - * Only applies to hides_under monsters, *not* is_hider monsters. */ + * Only applies to hides_under monsters, *not* is_hider monsters. + * if used by monster creation, should only happen during level + * creation, otherwise there will be message sequencing issues */ boolean hideunder(struct monst *mtmp) { struct trap *t; + const char *seenmon = (char *) 0, *seenobj = (char *) 0, + *locomo = (char *) 0; + int seeit = gi.in_mklev ? 0 : canseemon(mtmp); boolean oldundetctd, undetected = FALSE, is_u = (mtmp == &gy.youmonst); coordxy x = is_u ? u.ux : mtmp->mx, y = is_u ? u.uy : mtmp->my; if (mtmp == u.ustuck) { ; /* undetected==FALSE; can't hide if holding you or held by you */ - } else if (is_u ? (u.utrap && u.utraptype != TT_PIT) - : (mtmp->mtrapped - && (t = t_at(x, y)) != 0 && !is_pit(t->ttyp))) { - ; /* undetected==FALSE; can't hide while stuck in a non-pit trap */ + } else if ((is_u ? u.utrap : mtmp->mtrapped) + || ((t = t_at(x, y)) != 0 && !is_pit(t->ttyp))) { + ; /* undetected==FALSE; can't hide while trapped or on/in/under + any non-pit trap when not trapped */ } else if (mtmp->data->mlet == S_EEL) { - undetected = (is_pool(x, y) && !Is_waterlevel(&u.uz)); - } else if (hides_under(mtmp->data)) { + /* aquatic creatures only hide under water, not under objects; + they don't do so on the Plane of Water or when hero is also + under water unless some obstacle blocks line-of-sight */ + undetected = (is_pool(x, y) && !Is_waterlevel(&u.uz) + && (!Underwater || !couldsee(x, y))); + if (seeit) { + seenobj = "the water"; + locomo = "dive"; + } + } else if (hides_under(mtmp->data) + /* pets won't hide under a cursed item or an item of any BUC + state that shares a pile with one or more cursed items */ + && (!mtmp->mtame || !cursed_object_at(x, y)) + /* aquatic creatures don't reach here; other swimmers + shouldn't hide beneath underwater objects */ + && !is_pool_or_lava(x, y)) { int concealment = concealed_spot(x, y); if (concealment == CONCEALABLE_BY_TERRAIN) { undetected = TRUE; } else if (concealment == CONCEALABLE_BY_OBJECT) { - struct obj *otmp = gl.level.objects[x][y]; + struct obj *otmp = svl.level.objects[x][y]; + if (seeit) /*&& (!is_pool(x, y) || (Underwater && distu(x, y) <= 2))*/ + seenobj = ansimpleoname(otmp); /* most monsters won't hide under cockatrice corpse but they can hide under a pile containing more than just such corpses */ - while (otmp && otmp->otyp == CORPSE - && touch_petrifies(&mons[otmp->corpsenm])) - otmp = otmp->nexthere; - if (otmp != 0 || ((mtmp == &gy.youmonst) ? Stone_resistance - : resists_ston(mtmp))) + if (is_u ? !Stone_resistance : !resists_ston(mtmp)) + while (otmp && otmp->otyp == CORPSE + && touch_petrifies(&mons[otmp->corpsenm])) + otmp = otmp->nexthere; + if (otmp) undetected = TRUE; } } @@ -4798,9 +5246,26 @@ hideunder(struct monst *mtmp) if (is_u) { oldundetctd = u.uundetected != 0; u.uundetected = undetected ? 1 : 0; +#if 0 /* feedback handled via #monster */ + if (undetected && !oldundeteced && seenobj) + You("hide under %s.", seenobj); +#endif } else { + if (seeit) + seenmon = y_monnam(mtmp); oldundetctd = mtmp->mundetected != 0; mtmp->mundetected = undetected ? 1 : 0; + /* the "you see" message won't be shown for monster hiding during + level creation because 'seeit' will be 0 so 'seenmon' and 'seenobj' + will be Null */ + 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; + } } if (undetected != oldundetctd) newsym(x, y); @@ -4809,7 +5274,7 @@ hideunder(struct monst *mtmp) /* called when returning to a previously visited level */ void -hide_monst(struct monst* mon) +hide_monst(struct monst *mon) { boolean hider_under = hides_under(mon->data) || mon->data->mlet == S_EEL; @@ -4857,35 +5322,31 @@ mon_animal_list(boolean construct) } } -static int +staticfn int pick_animal(void) { int res; if (!ga.animal_list) mon_animal_list(TRUE); - + assert(ga.animal_list != 0); res = ga.animal_list[rn2(ga.animal_list_count)]; return res; } void -decide_to_shapeshift(struct monst *mon, int shiftflags) +decide_to_shapeshift(struct monst *mon) { struct permonst *ptr = 0; int mndx; unsigned was_female = mon->female; - boolean msg = FALSE, dochng = FALSE; - - if ((shiftflags & SHIFT_MSG) - || ((shiftflags & SHIFT_SEENMSG) && sensemon(mon))) - msg = TRUE; + boolean dochng = FALSE; if (!is_vampshifter(mon)) { - /* regular shapeshifter */ + /* regular shapeshifter; 'ptr' is Null */ if (!rn2(6)) dochng = TRUE; - } else { + } else if (!(mon->mstrategy & STRAT_WAITFORU)) { /* The vampire has to be in good health (mhp) to maintain * its shifted form. * @@ -4900,7 +5361,7 @@ decide_to_shapeshift(struct monst *mon, int shiftflags) */ if (mon->data->mlet != S_VAMPIRE) { if ((mon->mhp <= (mon->mhpmax + 5) / 6) && rn2(4) - && mon->cham >= LOW_PM) { + && ismnum(mon->cham)) { ptr = &mons[mon->cham]; dochng = TRUE; } else if (mon->data == &mons[PM_FOG_CLOUD] @@ -4912,7 +5373,7 @@ decide_to_shapeshift(struct monst *mon, int shiftflags) tame--and then switch back to vampire; they'll also switch to fog cloud if they encounter a closed door */ mndx = pickvampshape(mon); - if (mndx >= LOW_PM) { + if (ismnum(mndx)) { ptr = &mons[mndx]; dochng = (ptr != mon->data); } @@ -4933,16 +5394,19 @@ decide_to_shapeshift(struct monst *mon, int shiftflags) } } if (dochng) { - if (newcham(mon, ptr, msg ? NC_SHOW_MSG : 0) && is_vampshifter(mon)) { - /* for vampshift, override the 10% chance for sex change */ - ptr = mon->data; - if (!is_male(ptr) && !is_female(ptr) && !is_neuter(ptr)) - mon->female = was_female; + if (newcham(mon, ptr, NC_SHOW_MSG)) { + /* for vampshift, override the 10% chance for sex change + (by forcing original gender in case that occurred) */ + if (is_vampshifter(mon)) { + ptr = mon->data; + if (!is_male(ptr) && !is_female(ptr) && !is_neuter(ptr)) + mon->female = was_female; + } } } } -static int +staticfn int pickvampshape(struct monst *mon) { int mndx = mon->cham, wolfchance = 10; @@ -4953,12 +5417,14 @@ 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) && !is_open_air(mon->mx, mon->my)) { mndx = PM_WOLF; break; } + FALLTHROUGH; /*FALLTHRU*/ case PM_VAMPIRE: /* any vampire can become fog or bat */ mndx = !rn2(4) ? PM_FOG_CLOUD : PM_VAMPIRE_BAT; @@ -4968,7 +5434,7 @@ pickvampshape(struct monst *mon) /* return to base form if chosen poly target has been genocided or randomly if already in an alternate form (to prevent always switching back and forth between bat and fog) */ - if ((gm.mvitals[mndx].mvflags & G_GENOD) != 0 + if ((svm.mvitals[mndx].mvflags & G_GENOD) != 0 || (mon->data != &mons[mon->cham] && !rn2(4))) return mon->cham; @@ -4976,18 +5442,18 @@ pickvampshape(struct monst *mon) } /* nonshapechangers who warrant special polymorph handling */ -static boolean -isspecmon(struct monst* mon) +staticfn boolean +isspecmon(struct monst *mon) { return (mon->isshk || mon->ispriest || mon->isgd - || mon->m_id == gq.quest_status.leader_m_id); + || mon->m_id == svq.quest_status.leader_m_id); } /* restrict certain special monsters (shopkeepers, aligned priests, vault guards) to forms that allow them to behave sensibly (catching gold, speaking?) so that they don't need too much extra code */ -static boolean -validspecmon(struct monst* mon, int mndx) +staticfn boolean +validspecmon(struct monst *mon, int mndx) { if (mndx == NON_PM) return TRUE; /* caller wants random */ @@ -5022,7 +5488,7 @@ valid_vampshiftform(int base, int form) /* prevent wizard mode user from specifying invalid vampshifter shape when using monpolycontrol to assign a new form to a vampshifter */ boolean -validvamp(struct monst* mon, int* mndx_p, int monclass) +validvamp(struct monst *mon, int *mndx_p, int monclass) { /* simplify caller's usage */ if (!is_vampshifter(mon)) @@ -5033,7 +5499,7 @@ validvamp(struct monst* mon, int* mndx_p, int monclass) *mndx_p = PM_VLAD_THE_IMPALER; return TRUE; } - if (*mndx_p >= LOW_PM && is_shapeshifter(&mons[*mndx_p])) { + if (ismnum(*mndx_p) && is_shapeshifter(&mons[*mndx_p])) { /* player picked some type of shapeshifter; use mon's self (vampire or chameleon) */ *mndx_p = mon->cham; @@ -5062,6 +5528,7 @@ validvamp(struct monst* mon, int* mndx_p, int monclass) *mndx_p = PM_WOLF; break; } + FALLTHROUGH; /*FALLTHRU*/ default: *mndx_p = NON_PM; @@ -5070,8 +5537,87 @@ validvamp(struct monst* mon, int* mndx_p, int monclass) return (boolean) (*mndx_p != NON_PM); } +staticfn int +wiz_force_cham_form(struct monst *mon) +{ + char pprompt[BUFSZ], parttwo[QBUFSZ], buf[BUFSZ], prevbuf[BUFSZ]; + int monclass, len, tryct, mndx = NON_PM; + + /* construct prompt in pieces */ + Sprintf(pprompt, "Change %s", noit_mon_nam(mon)); + Sprintf(parttwo, " @ %s into what?", + coord_desc((int) mon->mx, (int) mon->my, buf, + (iflags.getpos_coords != GPCOORDS_NONE) + ? iflags.getpos_coords : GPCOORDS_MAP)); + /* combine the two parts, not exceeding QBUFSZ-1 in overall length; + if combined length is too long it has to be due to monster's + name so we'll chop enough of that off to fit the second part */ + if ((len = (int) strlen(pprompt) + (int) strlen(parttwo)) >= QBUFSZ) + /* strlen(parttwo) is less than QBUFSZ/2 so strlen(pprompt) is + more than QBUFSZ/2 and excess amount being truncated can't + exceed pprompt's length and back up to before &pprompt[0]) */ + *(eos(pprompt) - (len - (QBUFSZ - 1))) = '\0'; + Strcat(pprompt, parttwo); + + buf[0] = prevbuf[0] = '\0'; /* clear buffer for EDIT_GETLIN */ +#define TRYLIMIT 5 + tryct = TRYLIMIT; + do { + if (tryct == TRYLIMIT - 1) { /* first retry */ + /* change "into what?" to "into what kind of monster?" */ + if (strlen(pprompt) + sizeof " kind of monster" - 1 < QBUFSZ) + Strcpy(eos(pprompt) - 1, " kind of monster?"); + } +#undef TRYLIMIT + monclass = 0; + getlin(pprompt, buf); + mungspaces(buf); + /* for ESC, take form selected above (might be NON_PM) */ + if (*buf == '\033') + break; + /* for "*", use NON_PM to pick an arbitrary shape below */ + if (!strcmp(buf, "*") || !strcmpi(buf, "random")) { + mndx = NON_PM; + break; + } + mndx = name_to_mon(buf, (int *) 0); + if (mndx == NON_PM) { + /* didn't get a type, so check whether it's a class + (single letter or text match with def_monsyms[]) */ + monclass = name_to_monclass(buf, &mndx); + if (monclass && mndx == NON_PM) + mndx = mkclass_poly(monclass); + } + if (ismnum(mndx)) { + /* got a specific type of monster; use it if we can */ + if (validvamp(mon, &mndx, monclass)) + break; + /* can't; revert to random in case we exhaust tryct */ + mndx = NON_PM; + } + + pline("It can't become that."); +#ifdef EDIT_GETLIN + /* EDIT_GETLIN preloads the input buffer with the previous + response but we shouldn't just keep repeating that if player + leaves it unchanged; affects retry for empty input too */ + if (!strcmp(buf, prevbuf)) + Strcpy(buf, "random"); + Strcpy(prevbuf, buf); +#else + nhUse(prevbuf); +#endif + } while (--tryct > 0); + + if (!tryct) + pline1(thats_enough_tries); + if (is_vampshifter(mon) && !validvamp(mon, &mndx, monclass)) + mndx = pickvampshape(mon); /* don't resort to arbitrary */ + return mndx; +} + int -select_newcham_form(struct monst* mon) +select_newcham_form(struct monst *mon) { int mndx = NON_PM, tryct; @@ -5084,7 +5630,7 @@ select_newcham_form(struct monst* mon) if (!rn2(7)) { mndx = pick_nasty(mons[PM_JABBERWOCK].difficulty - 1); } else if (rn2(3)) { /* role monsters */ - mndx = rn1(PM_WIZARD - PM_ARCHEOLOGIST + 1, PM_ARCHEOLOGIST); + mndx = tt_doppel(mon); } else if (!rn2(3)) { /* quest guardians */ mndx = rn1(PM_APPRENTICE - PM_STUDENT + 1, PM_STUDENT); /* avoid own role's guardian */ @@ -5094,6 +5640,7 @@ select_newcham_form(struct monst* mon) tryct = 5; do { mndx = rn1(SPECIAL_PM - LOW_PM, LOW_PM); + /* assert(ismnum(mndx)); */ if (humanoid(&mons[mndx]) && polyok(&mons[mndx])) break; } while (--tryct > 0); @@ -5126,81 +5673,8 @@ select_newcham_form(struct monst* mon) } /* for debugging: allow control of polymorphed monster */ - if (wizard && iflags.mon_polycontrol) { - char pprompt[BUFSZ], parttwo[QBUFSZ], buf[BUFSZ], prevbuf[BUFSZ]; - int monclass, len; - - /* construct prompt in pieces */ - Sprintf(pprompt, "Change %s", noit_mon_nam(mon)); - Sprintf(parttwo, " @ %s into what?", - coord_desc((int) mon->mx, (int) mon->my, buf, - (iflags.getpos_coords != GPCOORDS_NONE) - ? iflags.getpos_coords : GPCOORDS_MAP)); - /* combine the two parts, not exceeding QBUFSZ-1 in overall length; - if combined length is too long it has to be due to monster's - name so we'll chop enough of that off to fit the second part */ - if ((len = (int) strlen(pprompt) + (int) strlen(parttwo)) >= QBUFSZ) - /* strlen(parttwo) is less than QBUFSZ/2 so strlen(pprompt) is - more than QBUFSZ/2 and excess amount being truncated can't - exceed pprompt's length and back up to before &pprompt[0]) */ - *(eos(pprompt) - (len - (QBUFSZ - 1))) = '\0'; - Strcat(pprompt, parttwo); - - buf[0] = prevbuf[0] = '\0'; /* clear buffer for EDIT_GETLIN */ -#define TRYLIMIT 5 - tryct = TRYLIMIT; - do { - if (tryct == TRYLIMIT - 1) { /* first retry */ - /* change "into what?" to "into what kind of monster?" */ - if (strlen(pprompt) + sizeof " kind of monster" - 1 < QBUFSZ) - Strcpy(eos(pprompt) - 1, " kind of monster?"); - } -#undef TRYLIMIT - monclass = 0; - getlin(pprompt, buf); - mungspaces(buf); - /* for ESC, take form selected above (might be NON_PM) */ - if (*buf == '\033') - break; - /* for "*", use NON_PM to pick an arbitrary shape below */ - if (!strcmp(buf, "*") || !strcmpi(buf, "random")) { - mndx = NON_PM; - break; - } - mndx = name_to_mon(buf, (int *) 0); - if (mndx == NON_PM) { - /* didn't get a type, so check whether it's a class - (single letter or text match with def_monsyms[]) */ - monclass = name_to_monclass(buf, &mndx); - if (monclass && mndx == NON_PM) - mndx = mkclass_poly(monclass); - } - if (mndx >= LOW_PM) { - /* got a specific type of monster; use it if we can */ - if (validvamp(mon, &mndx, monclass)) - break; - /* can't; revert to random in case we exhaust tryct */ - mndx = NON_PM; - } - - pline("It can't become that."); -#ifdef EDIT_GETLIN - /* EDIT_GETLIN preloads the input buffer with the previous - response but we shouldn't just keep repeating that if player - leaves it unchanged; affects retry for empty input too */ - if (!strcmp(buf, prevbuf)) - Strcpy(buf, "random"); - Strcpy(prevbuf, buf); -#else - nhUse(prevbuf); -#endif - } while (--tryct > 0); - - if (!tryct) - pline1(thats_enough_tries); - if (is_vampshifter(mon) && !validvamp(mon, &mndx, monclass)) - mndx = pickvampshape(mon); /* don't resort to arbitrary */ - } + if (wizard && iflags.mon_polycontrol) + mndx = wiz_force_cham_form(mon); /* if no form was specified above, pick one at random now */ if (mndx == NON_PM) { @@ -5226,7 +5700,7 @@ select_newcham_form(struct monst* mon) } /* this used to be inline within newcham() but monpolycontrol needs it too */ -static struct permonst * +staticfn struct permonst * accept_newcham_form(struct monst *mon, int mndx) { struct permonst *mdat; @@ -5234,7 +5708,7 @@ accept_newcham_form(struct monst *mon, int mndx) if (mndx == NON_PM) return 0; mdat = &mons[mndx]; - if ((gm.mvitals[mndx].mvflags & G_GENOD) != 0) + if ((svm.mvitals[mndx].mvflags & G_GENOD) != 0) return 0; if (is_placeholder(mdat)) return 0; @@ -5246,7 +5720,7 @@ accept_newcham_form(struct monst *mon, int mndx) /* shapeshifters are rejected by polyok() but allow a shapeshifter to take on its 'natural' form */ if (is_shapeshifter(mdat) - && mon->cham >= LOW_PM && mdat == &mons[mon->cham]) + && ismnum(mon->cham) && mdat == &mons[mon->cham]) return mdat; /* polyok() rules out M2_PNAME, M2_WERE, and all humans except Kops */ return polyok(mdat) ? mdat : 0; @@ -5263,8 +5737,11 @@ mgender_from_permonst( } else if (is_female(mdat)) { mtmp->female = TRUE; } else if (!is_neuter(mdat)) { - /* usually leave as-is; same chance to change as polymorphing hero */ - if (!rn2(10)) + /* usually leave as-is; same chance to change as polymorphing hero; + vampires use controlled shapechange (from their perspective, even + if it is random from the player's perspective) and don't undergo + gender change */ + if (!rn2(10) && !(is_vampire(mdat) || is_vampshifter(mtmp))) mtmp->female = !mtmp->female; } } @@ -5280,10 +5757,11 @@ newcham( { boolean polyspot = ((ncflags & NC_VIA_WAND_OR_SPELL) !=0), /* "The oldmon turns into a newmon!" */ - msg = ((ncflags & NC_SHOW_MSG) != 0); + msg = ((ncflags & NC_SHOW_MSG) != 0), + seenorsensed = canspotmon(mtmp); int hpn, hpd, mndx, tryct; struct permonst *olddata = mtmp->data; - char *p, oldname[BUFSZ], l_oldname[BUFSZ], newname[BUFSZ]; + char *p, oldname[BUFSZ], l_oldname[BUFSZ]; /* Riders are immune to polymorph and green slime (but apparent Rider might actually be a doppelganger) */ @@ -5291,7 +5769,7 @@ newcham( if (is_rider(olddata)) return 0; /* make Nazgul and erinyes immune too, to reduce chance of - anomalous extinction feedback during final disclsoure */ + anomalous extinction feedback during final disclosure */ if (mbirth_limit(monsndx(olddata)) < MAXMONNO) return 0; /* cancelled shapechangers become uncancelled prior @@ -5304,9 +5782,10 @@ newcham( } if (msg) { - /* like Monnam() but never mention saddle */ - Strcpy(oldname, x_monnam(mtmp, ARTICLE_THE, (char *) 0, - SUPPRESS_SADDLE, FALSE)); + Strcpy(oldname, + /* like YMonnam() but never mention saddle */ + x_monnam(mtmp, mtmp->mtame ? ARTICLE_YOUR : ARTICLE_THE, + (char *) 0, SUPPRESS_SADDLE, FALSE)); oldname[0] = highc(oldname[0]); } /* we need this one whether msg is true or not */ @@ -5325,7 +5804,7 @@ newcham( } while (mdat == 0 && --tryct > 0); if (!tryct) return 0; - } else if (gm.mvitals[monsndx(mdat)].mvflags & G_GENOD) + } else if (svm.mvitals[monsndx(mdat)].mvflags & G_GENOD) return 0; /* passed in mdat is genocided */ if (mdat == olddata) @@ -5447,17 +5926,20 @@ newcham( newsym(mtmp->mx, mtmp->my); if (msg) { - Strcpy(newname, noname_monnam(mtmp, ARTICLE_A)); - /* oldname was capitalized above; newname will be lower case */ - if (!strcmpi(newname, "it")) { /* can't see or sense it now */ - if (!!strcmpi(oldname, "it")) /* could see or sense it before */ - pline("%s disappears!", oldname); + /* oldname is capitalized and might be an assigned name */ + if (!canspotmon(mtmp)) { /* can't see or sense it now */ + if (seenorsensed) /* could see or sense it before */ + pline_mon(mtmp, "%s disappears!", oldname); (void) usmellmon(mdat); - } else { /* can see or sense it now */ - if (!strcmpi(oldname, "it")) /* couldn't see or sense it before */ - pline("%s appears!", upstart(newname)); - else - pline("%s turns into %s!", oldname, newname); + } else if (!seenorsensed) { /* couldn't see/sense before, can now */ + char *mnm = x_monnam(mtmp, mtmp->mtame ? ARTICLE_YOUR : ARTICLE_A, + (char *) 0, 0, FALSE); + + pline_mon(mtmp, "%s appears!", upstart(mnm)); + } else { /* saw/sensed it before, still see/sense it now */ + pline_mon(mtmp, "%s turns into %s!", oldname, + /* "a " even if it has a name assigned */ + noname_monnam(mtmp, ARTICLE_A)); } } @@ -5471,7 +5953,7 @@ newcham( mon_break_armor(mtmp, polyspot); if (!(mtmp->misc_worn_check & W_ARMG)) mselftouch(mtmp, "No longer petrify-resistant, ", - !gc.context.mon_moving); + !svc.context.mon_moving); check_gear_next_turn(mtmp); /* This ought to re-test can_carry() on each item in the inventory @@ -5481,7 +5963,7 @@ newcham( */ /* former giants can't continue carrying boulders */ if (mtmp->minvent && !throws_rocks(mdat)) { - register struct obj *otmp, *otmp2; + struct obj *otmp, *otmp2; /* DEADMONSTER(): it is possible for flooreffects() to kill mtmp; the rest of its inventory would be dropped making otmp2 stale */ @@ -5500,6 +5982,22 @@ newcham( } } } + if (mtmp == u.usteed) + poly_steed(mtmp, olddata); + + /* old form might not have been affected by Elbereth but perhaps the + new form is */ + if (svc.context.mon_moving) { + /* give 'mtmp' a new chance to pinpoint hero's location */ + if (!u_at(mtmp->mux, mtmp->muy)) + set_apparxy(mtmp); + /* if hero is on Elbereth or scare monster, mtmp in new form might + become scared */ + if (!mtmp->mpeaceful + && onscary(mtmp->mux, mtmp->muy, mtmp) + && monnear(mtmp, mtmp->mux, mtmp->muy)) + monflee(mtmp, rn1(9, 2), TRUE, TRUE); /* 2..10 turns */ + } return 1; } @@ -5568,14 +6066,15 @@ dead_species(int m_idx, boolean egg) * fortunately, none of them have eggs. Species extinction due to * overpopulation does not kill eggs. */ + /* assert(ismnum(m_idx)); */ alt_idx = egg ? big_to_little(m_idx) : m_idx; - return (boolean) ((gm.mvitals[m_idx].mvflags & G_GENOD) != 0 - || (gm.mvitals[alt_idx].mvflags & G_GENOD) != 0); + return (boolean) ((svm.mvitals[m_idx].mvflags & G_GENOD) != 0 + || (svm.mvitals[alt_idx].mvflags & G_GENOD) != 0); } /* kill off any eggs of genocided monsters */ -static void -kill_eggs(struct obj* obj_list) +staticfn void +kill_eggs(struct obj *obj_list) { struct obj *otmp; @@ -5627,10 +6126,10 @@ kill_genocided_monsters(void) if (DEADMONSTER(mtmp)) continue; mndx = monsndx(mtmp->data); - kill_cham = (mtmp->cham >= LOW_PM - && (gm.mvitals[mtmp->cham].mvflags & G_GENOD)); - if ((gm.mvitals[mndx].mvflags & G_GENOD) || kill_cham) { - if (mtmp->cham >= LOW_PM && !kill_cham) + kill_cham = (ismnum(mtmp->cham) + && (svm.mvitals[mtmp->cham].mvflags & G_GENOD)); + if ((svm.mvitals[mndx].mvflags & G_GENOD) || kill_cham) { + if (ismnum(mtmp->cham) && !kill_cham) (void) newcham(mtmp, (struct permonst *) 0, NC_SHOW_MSG); else mondead(mtmp); @@ -5642,11 +6141,11 @@ kill_genocided_monsters(void) kill_eggs(gi.invent); kill_eggs(fobj); kill_eggs(gm.migrating_objs); - kill_eggs(gl.level.buriedobjlist); + kill_eggs(svl.level.buriedobjlist); } void -golemeffects(register struct monst* mon, int damtype, int dam) +golemeffects(struct monst *mon, int damtype, int dam) { int heal = 0, slow = 0; @@ -5668,12 +6167,9 @@ golemeffects(register 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("%s seems healthier.", Monnam(mon)); + pline_mon(mon, "%s seems healthier.", Monnam(mon)); } } } @@ -5691,7 +6187,7 @@ angry_guards(boolean silent) if (is_watch(mtmp->data) && mtmp->mpeaceful) { ct++; if (canspotmon(mtmp) && mtmp->mcanmove) { - if (next2u(mtmp->mx, mtmp->my)) + if (m_next2u(mtmp)) nct++; else sct++; @@ -5715,7 +6211,7 @@ angry_guards(boolean silent) if (nct) { /* seen/sensed adjacent guard(s) */ Sprintf(buf, "guard%s", plur(nct)); pline_The("%s %s angry!", buf, vtense(buf, "get")); - } else if (sct) { /* seen/sensed non-adjcent guard(s) */ + } else if (sct) { /* seen/sensed non-adjacent guard(s) */ Sprintf(buf, "guard%s", plur(sct)); pline("%s %s %s approaching!", (sct == 1) ? "An angry" : "Angry", @@ -5731,7 +6227,7 @@ angry_guards(boolean silent) return FALSE; } -static void +staticfn void pacify_guard(struct monst *mtmp) { if (is_watch(mtmp->data)) @@ -5745,7 +6241,7 @@ pacify_guards(void) } void -mimic_hit_msg(struct monst* mtmp, short otyp) +mimic_hit_msg(struct monst *mtmp, short otyp) { short ap = mtmp->mappearance; @@ -5756,7 +6252,7 @@ mimic_hit_msg(struct monst* mtmp, short otyp) break; case M_AP_OBJECT: if (otyp == SPE_HEALING || otyp == SPE_EXTRA_HEALING) { - pline("%s seems a more vivid %s than before.", + pline_mon(mtmp, "%s seems a more vivid %s than before.", The(simple_typename(ap)), c_obj_colors[objects[ap].oc_color]); } @@ -5765,7 +6261,7 @@ mimic_hit_msg(struct monst* mtmp, short otyp) } boolean -usmellmon(struct permonst* mdat) +usmellmon(struct permonst *mdat) { int mndx; boolean nonspecific = FALSE; @@ -5889,6 +6385,110 @@ check_gear_next_turn(struct monst *mon) mon->misc_worn_check |= I_SPECIAL; } +/* make erinyes more dangerous based on your alignment abuse */ +void +adj_erinys(unsigned abuse) +{ + struct permonst *pm = &mons[PM_ERINYS]; + + if (abuse > 5L) { + pm->mflags1 |= M1_SEE_INVIS; + } + if (abuse > 10L) { + pm->mflags1 |= M1_AMPHIBIOUS; + } + if (abuse > 15L) { + pm->mflags1 |= M1_FLY; + } + if (abuse > 20L) { + /* more powerful attack */ + pm->mattk[0].damn = 3; + } + if (abuse > 25L) { + pm->mflags1 |= M1_REGEN; + } + if (abuse > 30L) { + pm->mflags1 |= M1_TPORT_CNTRL; + } + if (abuse > 35L) { + /* second attack */ + pm->mattk[1].aatyp = AT_WEAP; + pm->mattk[1].adtyp = AD_DRST; + pm->mattk[1].damn = 3; + pm->mattk[1].damd = 4; + } + if (abuse > 40L) { + pm->mflags1 |= M1_TPORT; + } + if (abuse > 50L) { + /* third (spellcasting) attack */ + pm->mattk[2].aatyp = AT_MAGC; + pm->mattk[2].adtyp = AD_SPEL; + pm->mattk[2].damn = 3; + pm->mattk[2].damd = 4; + } + + /* also adjust level and difficulty */ + pm->mlevel = min(7 + u.ualign.abuse, 50); + pm->difficulty = min(10 + (u.ualign.abuse / 3), 25); +} + +/* mark monster type as seen from close-up, + if we haven't seen it nearby before */ +void +see_monster_closeup(struct monst *mtmp) +{ + if (!svm.mvitals[monsndx(mtmp->data)].seen_close) { + svm.mvitals[monsndx(mtmp->data)].seen_close = TRUE; + if (Role_if(PM_TOURIST)) { + 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) +{ + coordxy x, y; + + for (x = u.ux - 1; x <= u.ux + 1; x++) + for (y = u.uy - 1; y <= u.uy + 1; y++) + if (isok(x, y) && MON_AT(x, y)) { + struct monst *mtmp = m_at(x, y); + + if (canspotmon(mtmp) && !mtmp->mundetected && !M_AP_TYPE(mtmp)) + svm.mvitals[monsndx(mtmp->data)].seen_close = TRUE; + } +} + +/* 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); +} + /* u_aireffects() equivalent for monsters. */ void mon_aireffects(struct monst *mtmp) @@ -5917,7 +6517,7 @@ mon_aireffects(struct monst *mtmp) /* No corpse is dropped here, because it would give a message about the * corpse falling away and disappearing which doesn't make any sense. * Assume it splatted on the floor into an unrecognizable state. */ - if (gc.context.mon_moving) + if (svc.context.mon_moving) mongone(mtmp); else { xkilled(mtmp, XKILL_NOMSG | XKILL_NOCORPSE); diff --git a/src/mondata.c b/src/mondata.c index cd818291c9..9bf1ab489c 100644 --- a/src/mondata.c +++ b/src/mondata.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mondata.c $NHDT-Date: 1672003297 2022/12/25 21:21:37 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.119 $ */ +/* NetHack 3.7 mondata.c $NHDT-Date: 1711620615 2024/03/28 10:10:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.132 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -39,7 +39,7 @@ set_mon_data(struct monst *mon, struct permonst *ptr) /* does monster-type have any attack for a specific type of damage? */ struct attack * -attacktype_fordmg(struct permonst* ptr, int atyp, int dtyp) +attacktype_fordmg(struct permonst *ptr, int atyp, int dtyp) { struct attack *a; @@ -51,14 +51,14 @@ attacktype_fordmg(struct permonst* ptr, int atyp, int dtyp) /* does monster-type have a particular type of attack */ boolean -attacktype(struct permonst* ptr, int atyp) +attacktype(struct permonst *ptr, int atyp) { return attacktype_fordmg(ptr, atyp, AD_ANY) ? TRUE : FALSE; } /* returns True if monster doesn't attack, False if it does */ boolean -noattacks(struct permonst* ptr) +noattacks(struct permonst *ptr) { int i; struct attack *mattk = ptr->mattk; @@ -81,7 +81,7 @@ poly_when_stoned(struct permonst *ptr) { /* non-stone golems turn into stone golems unless latter is genocided */ return (boolean) (is_golem(ptr) && ptr != &mons[PM_STONE_GOLEM] - && !(gm.mvitals[PM_STONE_GOLEM].mvflags & G_GENOD)); + && !(svm.mvitals[PM_STONE_GOLEM].mvflags & G_GENOD)); /* allow G_EXTINCT */ } @@ -138,7 +138,7 @@ resists_drli(struct monst *mon) /* True if monster is magic-missile (actually, general magic) resistant */ boolean -resists_magm(struct monst* mon) +resists_magm(struct monst *mon) { struct permonst *ptr = mon->data; boolean is_you = (mon == &gy.youmonst); @@ -193,14 +193,12 @@ resists_fire(struct monst* mon) return FALSE; } -/* True iff monster is resistant to light-induced blindness */ +/* True if monster is resistant to light-induced blindness */ boolean -resists_blnd(struct monst* mon) +resists_blnd(struct monst *mon) { struct permonst *ptr = mon->data; boolean is_you = (mon == &gy.youmonst); - long slotmask; - struct obj *o; if (is_you ? (Blind || Unaware) : (mon->mblinded || !mon->mcansee || !haseyes(ptr) @@ -212,21 +210,40 @@ resists_blnd(struct monst* mon) if (dmgtype_fromattack(ptr, AD_BLND, AT_EXPL) || dmgtype_fromattack(ptr, AD_BLND, AT_GAZE)) return TRUE; + /* 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 + or wielded magical equipment (used to decide whether to show sparkle + animation when resisting) */ +boolean +resists_blnd_by_arti(struct monst *mon) +{ + struct obj *o; + boolean is_you = (mon == &gy.youmonst); + o = is_you ? uwep : MON_WEP(mon); if (o && o->oartifact && defends(AD_BLND, o)) return TRUE; 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 == BLINDED) - || (o->oartifact && defends_when_carried(AD_BLND, o))) + if (defends_when_carried(AD_BLND, o)) return TRUE; +#if 0 /* omit this; the Eyes of the Overworld have no carry property and + * their worn property is magic resistance rather than blindness + * resistance; wearing them blocks blindness without actually + * preventing it, so don't classify them as providing resistance */ + if (is_you && is_art(uamul, ART_EYES_OF_THE_OVERWORLD)) + return TRUE; +#endif /* 0 */ return FALSE; } @@ -249,6 +266,10 @@ can_blnd( if (!haseyes(mdef->data)) return FALSE; + /* if monster has been permanently blinded, the deed is already done */ + if (!is_you && mon_perma_blind(mdef)) + return FALSE; + /* /corvus oculum corvi non eruit/ a saying expressed in Latin rather than a zoological observation: "a crow will not pluck out the eye of another crow" @@ -347,7 +368,7 @@ resists_light_halu(struct monst *mon) /* returns True if monster can attack at range */ boolean -ranged_attk(struct permonst* ptr) +ranged_attk(struct permonst *ptr) { int i; @@ -373,7 +394,7 @@ static boolean mstrength_ranged_attk(struct permonst *); an approximation of monster strength. It uses a similar method of determination as "experience()" to arrive at the strength. */ int -mstrength(struct permonst* ptr) +mstrength(struct permonst *ptr) { int i, tmp2, n, tmp = ptr->mlevel; @@ -406,7 +427,8 @@ mstrength(struct permonst* ptr) /* {freezing,flaming,shocking} spheres are fairly weak but can destroy equipment; {yellow,black} lights can't */ n += ((tmp3 == AD_COLD || tmp3 == AD_FIRE) ? 3 - : (tmp3 == AD_ELEC) ? 5 : 0); + : (tmp3 == AD_ELEC) ? 5 + : 0); } } @@ -438,11 +460,11 @@ mstrength(struct permonst* ptr) } /* returns True if monster can attack at range */ -static boolean -mstrength_ranged_attk(register struct permonst* ptr) +staticfn boolean +mstrength_ranged_attk(struct permonst *ptr) { - register int i, j; - register int atk_mask = (1 << AT_BREA) | (1 << AT_SPIT) | (1 << AT_GAZE); + int i, j; + int atk_mask = (1 << AT_BREA) | (1 << AT_SPIT) | (1 << AT_GAZE); for (i = 0; i < NATTK; i++) { if ((j = ptr->mattk[i].aatyp) >= AT_WEAP @@ -555,7 +577,7 @@ passes_bars(struct permonst *mptr) /* returns True if monster can blow (whistle, etc) */ boolean -can_blow(struct monst* mtmp) +can_blow(struct monst *mtmp) { if ((is_silent(mtmp->data) || mtmp->data->msound == MS_BUZZ) && (breathless(mtmp->data) || verysmall(mtmp->data) @@ -568,7 +590,7 @@ can_blow(struct monst* mtmp) /* for casting spells and reading scrolls while blind */ boolean -can_chant(struct monst* mtmp) +can_chant(struct monst *mtmp) { if ((mtmp == &gy.youmonst && Strangled) || is_silent(mtmp->data) || !has_head(mtmp->data) @@ -579,7 +601,7 @@ can_chant(struct monst* mtmp) /* True if mon is vulnerable to strangulation */ boolean -can_be_strangled(struct monst* mon) +can_be_strangled(struct monst *mon) { struct obj *mamul; boolean nonbreathing, nobrainer; @@ -611,17 +633,16 @@ can_be_strangled(struct monst* mon) /* returns True if monster can track well */ boolean -can_track(register struct permonst* ptr) +can_track(struct permonst *ptr) { if (u_wield_art(ART_EXCALIBUR)) return TRUE; - else - return (boolean) haseyes(ptr); + return (boolean) haseyes(ptr); } /* creature will slide out of armor */ boolean -sliparm(register struct permonst* ptr) +sliparm(struct permonst *ptr) { return (boolean) (is_whirly(ptr) || ptr->msize <= MZ_SMALL || noncorporeal(ptr) @@ -631,7 +652,7 @@ sliparm(register struct permonst* ptr) /* creature will break out of armor */ boolean -breakarm(register struct permonst* ptr) +breakarm(struct permonst *ptr) { if (sliparm(ptr)) return FALSE; @@ -645,7 +666,7 @@ breakarm(register struct permonst* ptr) /* creature sticks other creatures it hits */ boolean -sticks(register struct permonst* ptr) +sticks(struct permonst *ptr) { return (boolean) (dmgtype(ptr, AD_STCK) || (dmgtype(ptr, AD_WRAP) && !attacktype(ptr, AT_ENGL)) @@ -654,19 +675,22 @@ sticks(register struct permonst* ptr) /* some monster-types can't vomit */ boolean -cantvomit(struct permonst* ptr) +cantvomit(struct permonst *ptr) { - /* rats and mice are incapable of vomiting; + /* rats and mice are incapable of vomiting; likewise with horses; which other creatures have the same limitation? */ if (ptr->mlet == S_RODENT && ptr != &mons[PM_ROCK_MOLE] && ptr != &mons[PM_WOODCHUCK]) return TRUE; + if (ptr == &mons[PM_WARHORSE] || ptr == &mons[PM_HORSE] + || ptr == &mons[PM_PONY]) + return TRUE; return FALSE; } /* number of horns this type of monster has on its head */ int -num_horns(struct permonst* ptr) +num_horns(struct permonst *ptr) { switch (monsndx(ptr)) { case PM_HORNED_DEVIL: /* ? "more than one" */ @@ -688,7 +712,7 @@ num_horns(struct permonst* ptr) /* does monster-type deal out a particular type of damage from a particular type of attack? */ struct attack * -dmgtype_fromattack(struct permonst* ptr, int dtyp, int atyp) +dmgtype_fromattack(struct permonst *ptr, int dtyp, int atyp) { struct attack *a; @@ -700,7 +724,7 @@ dmgtype_fromattack(struct permonst* ptr, int dtyp, int atyp) /* does monster-type deal out a particular type of damage from any attack */ boolean -dmgtype(struct permonst* ptr, int dtyp) +dmgtype(struct permonst *ptr, int dtyp) { return dmgtype_fromattack(ptr, dtyp, AT_ANY) ? TRUE : FALSE; } @@ -708,7 +732,7 @@ dmgtype(struct permonst* ptr, int dtyp) /* returns the maximum damage a defender can do to the attacker via a passive defense */ int -max_passive_dmg(register struct monst* mdef, register struct monst* magr) +max_passive_dmg(struct monst *mdef, struct monst *magr) { int i, dmg, multi2 = 0; uchar adtyp; @@ -759,7 +783,7 @@ max_passive_dmg(register struct monst* mdef, register struct monst* magr) /* determine whether two monster types are from the same species */ boolean -same_race(struct permonst* pm1, struct permonst* pm2) +same_race(struct permonst *pm1, struct permonst *pm2) { char let1 = pm1->mlet, let2 = pm2->mlet; @@ -859,26 +883,6 @@ same_race(struct permonst* pm1, struct permonst* pm2) return FALSE; } -DISABLE_WARNING_UNREACHABLE_CODE - -/* return an index into the mons array */ -int -monsndx(struct permonst* ptr) -{ - register int i; - - i = (int) (ptr - &mons[0]); - if (i < LOW_PM || i >= NUMMONS) { - panic("monsndx - could not index monster (%s)", - fmt_ptr((genericptr_t) ptr)); - /*NOTREACHED*/ - return NON_PM; /* will not get here */ - } - return i; -} - -RESTORE_WARNING_UNREACHABLE_CODE - /* for handling alternate spellings */ struct alt_spl { const char *name; @@ -916,9 +920,9 @@ name_to_monplus( * This also permits plurals created by adding suffixes such as 's' * or 'es'. Other plurals must still be handled explicitly. */ - register int i; - register int mntmp = NON_PM; - register char *s, *str, *term; + int i; + int mntmp = NON_PM; + char *s, *str, *term; char buf[BUFSZ]; int len, mgend, matchgend = -1; size_t slen; @@ -973,6 +977,8 @@ name_to_monplus( to the rank title prefix (input has been singularized) */ { "master thief", PM_MASTER_OF_THIEVES, NEUTRAL }, { "master of assassin", PM_MASTER_ASSASSIN, NEUTRAL }, + { "master-lich", PM_MASTER_LICH, NEUTRAL }, /* cf arch-lich */ + { "masterlich", PM_MASTER_LICH, NEUTRAL }, /* cf demilich */ /* Outdated names */ { "invisible stalker", PM_STALKER, NEUTRAL }, { "high-elf", PM_ELVEN_MONARCH, NEUTRAL }, /* PM_HIGH_ELF is @@ -1026,7 +1032,7 @@ name_to_monplus( /* end of list */ { 0, NON_PM, NEUTRAL } }; - register const struct alt_spl *namep; + const struct alt_spl *namep; for (namep = names; namep->name; namep++) { len = (int) strlen(namep->name); @@ -1050,7 +1056,8 @@ name_to_monplus( continue; m_i_len = strlen(mons[i].pmnames[mgend]); - if (m_i_len > (size_t) len && !strncmpi(mons[i].pmnames[mgend], str, (int) m_i_len)) { + if (m_i_len > (size_t) len + && !strncmpi(mons[i].pmnames[mgend], str, (int) m_i_len)) { if (m_i_len == slen) { mntmp = i; len = (int) m_i_len; @@ -1183,7 +1190,7 @@ name_to_monclass(const char *in_str, int * mndx_p) /* returns 3 values (0=male, 1=female, 2=none) */ int -gender(register struct monst* mtmp) +gender(struct monst *mtmp) { if (is_neuter(mtmp->data)) return 2; @@ -1196,7 +1203,7 @@ gender(register struct monst* mtmp) This is the one we want to use when printing messages. */ int pronoun_gender( - register struct monst *mtmp, + struct monst *mtmp, unsigned pg_flags) /* flags&1: 'no it' unless neuter, * flags&2: random if hallucinating */ { @@ -1216,7 +1223,7 @@ pronoun_gender( /* used for nearby monsters when you go to another level */ boolean -levl_follower(struct monst* mtmp) +levl_follower(struct monst *mtmp) { if (mtmp == u.usteed) return TRUE; @@ -1314,7 +1321,7 @@ static const short grownups[][2] = { int little_to_big(int montype) { - register int i; + int i; for (i = 0; grownups[i][0] >= LOW_PM; i++) if (montype == grownups[i][0]) { @@ -1327,7 +1334,7 @@ little_to_big(int montype) int big_to_little(int montype) { - register int i; + int i; for (i = 0; grownups[i][0] >= LOW_PM; i++) if (montype == grownups[i][1]) { @@ -1372,45 +1379,49 @@ raceptr(struct monst *mtmp) { if (mtmp == &gy.youmonst && !Upolyd) return &mons[gu.urace.mnum]; - else - return mtmp->data; + return mtmp->data; } -static const char *const levitate[4] = { "float", "Float", "wobble", "Wobble" }; -static const char *const flys[4] = { "fly", "Fly", "flutter", "Flutter" }; -static const char *const flyl[4] = { "fly", "Fly", "stagger", "Stagger" }; -static const char *const slither[4] = { "slither", "Slither", "falter", "Falter" }; -static const char *const ooze[4] = { "ooze", "Ooze", "tremble", "Tremble" }; -static const char *const immobile[4] = { "wiggle", "Wiggle", "pulsate", "Pulsate" }; -static const char *const crawl[4] = { "crawl", "Crawl", "falter", "Falter" }; +typedef const char *const locoverbs[4]; +static locoverbs levitate = { "float", "Float", "wobble", "Wobble" }, + flys = { "fly", "Fly", "flutter", "Flutter" }, + flyl = { "fly", "Fly", "stagger", "Stagger" }, + slither = { "slither", "Slither", "falter", "Falter" }, + /* it would be useful to incorporate "swim" but we lack + * sufficient information to know whether water is involved + swim = { "swim", "Swim", "flop", "Flop" }, + */ + ooze = { "ooze", "Ooze", "tremble", "Tremble" }, + immobile = { "wiggle", "Wiggle", "pulsate", "Pulsate" }, + crawl = { "crawl", "Crawl", "falter", "Falter" }; const char * -locomotion(const struct permonst* ptr, const char* def) +locomotion(const struct permonst *ptr, const char *def) { - int capitalize = (*def == highc(*def)); - - return (is_floater(ptr) ? levitate[capitalize] - : (is_flyer(ptr) && ptr->msize <= MZ_SMALL) ? flys[capitalize] - : (is_flyer(ptr) && ptr->msize > MZ_SMALL) ? flyl[capitalize] - : slithy(ptr) ? slither[capitalize] - : amorphous(ptr) ? ooze[capitalize] - : !ptr->mmove ? immobile[capitalize] - : nolimbs(ptr) ? crawl[capitalize] + int locoindx = (*def != highc(*def)) ? 0 : 1; + + return (is_floater(ptr) ? levitate[locoindx] + : (is_flyer(ptr) && ptr->msize <= MZ_SMALL) ? flys[locoindx] + : (is_flyer(ptr) && ptr->msize > MZ_SMALL) ? flyl[locoindx] + : slithy(ptr) ? slither[locoindx] + : amorphous(ptr) ? ooze[locoindx] + : !ptr->mmove ? immobile[locoindx] + : nolimbs(ptr) ? crawl[locoindx] : def); } const char * -stagger(const struct permonst* ptr, const char* def) +stagger(const struct permonst *ptr, const char *def) { - int capitalize = 2 + (*def == highc(*def)); - - return (is_floater(ptr) ? levitate[capitalize] - : (is_flyer(ptr) && ptr->msize <= MZ_SMALL) ? flys[capitalize] - : (is_flyer(ptr) && ptr->msize > MZ_SMALL) ? flyl[capitalize] - : slithy(ptr) ? slither[capitalize] - : amorphous(ptr) ? ooze[capitalize] - : !ptr->mmove ? immobile[capitalize] - : nolimbs(ptr) ? crawl[capitalize] + int locoindx = (*def != highc(*def)) ? 2 : 3; + + return (is_floater(ptr) ? levitate[locoindx] + : (is_flyer(ptr) && ptr->msize <= MZ_SMALL) ? flys[locoindx] + : (is_flyer(ptr) && ptr->msize > MZ_SMALL) ? flyl[locoindx] + : slithy(ptr) ? slither[locoindx] + : amorphous(ptr) ? ooze[locoindx] + : !ptr->mmove ? immobile[locoindx] + : nolimbs(ptr) ? crawl[locoindx] : def); } @@ -1511,7 +1522,7 @@ msummon_environ(struct permonst *mptr, const char **cloud) * False if monster definitely does not have a sense of smell. * * Do not base this on presence of a head or nose, since many - * creatures sense smells other ways (feelers, forked-tongues, etc.) + * creatures sense smells other ways (feelers, forked-tongues, etc). * We're assuming all insects can smell at a distance too. */ boolean @@ -1588,8 +1599,9 @@ emits_light(struct permonst *ptr) case PM_FIRE_VORTEX: case PM_BABY_GOLD_DRAGON: return 1; + default: + return 0; } - return 0; } /* Return TRUE if the monster has flesh. */ @@ -1635,7 +1647,25 @@ cvt_adtyp_to_mseenres(uchar adtyp) } } -/* Monsters remember hero resisting effect M_SEEN_foo */ +/* Convert property resistance to M_SEEN_bar */ +unsigned long +cvt_prop_to_mseenres(uchar prop) +{ + switch (prop) { + case ANTIMAGIC: return M_SEEN_MAGR; + case FIRE_RES: return M_SEEN_FIRE; + case COLD_RES: return M_SEEN_COLD; + case SLEEP_RES: return M_SEEN_SLEEP; + case DISINT_RES: return M_SEEN_DISINT; + case POISON_RES: return M_SEEN_POISON; + case SHOCK_RES: return M_SEEN_ELEC; + case ACID_RES: return M_SEEN_ACID; + case REFLECTING: return M_SEEN_REFL; + default: return M_SEEN_NOTHING; + } +} + +/* Monsters in line of sight remember hero resisting effect M_SEEN_foo */ void monstseesu(unsigned long seenres) { @@ -1649,6 +1679,36 @@ monstseesu(unsigned long seenres) m_setseenres(mtmp, seenres); } +/* Monsters in line of sight forget hero resistance to M_SEEN_foo */ +void +monstunseesu(unsigned long seenres) +{ + struct monst *mtmp; + + if (seenres == M_SEEN_NOTHING || u.uswallow) + return; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (!DEADMONSTER(mtmp) && m_canseeu(mtmp)) + m_clearseenres(mtmp, seenres); +} + +/* give monster mtmp the same intrinsics hero has */ +void +give_u_to_m_resistances(struct monst *mtmp) +{ + int intr; + + /* convert the hero's current set of intrinsics to their monster + equivalents -- FIRE_RES to MR_FIRE, COLD_RES to MR_COLD, etc -- and + add each to the mintrinsics field for the given monster */ + for (intr = FIRE_RES; intr <= STONE_RES; intr++) { + if ((u.uprops[intr].intrinsic & INTRINSIC) != 0L) { + mtmp->mintrinsics |= (unsigned short) res_to_mr(intr); + } + } +} + /* Can monster resist conflict caused by hero? High-CHA heroes will be able to 'convince' monsters @@ -1656,7 +1716,7 @@ monstseesu(unsigned long seenres) for them much more easily than low-CHA ones. */ boolean -resist_conflict(struct monst* mtmp) +resist_conflict(struct monst *mtmp) { /* always a small chance at 19 */ int resist_chance = min(19, (ACURR(A_CHA) - mtmp->m_lev + u.ulevel)); @@ -1721,7 +1781,7 @@ get_atkdam_type(int adtyp) static const int rnd_breath_typ[] = { AD_MAGM, AD_FIRE, AD_COLD, AD_SLEE, AD_DISN, AD_ELEC, AD_DRST, AD_ACID }; - return rnd_breath_typ[rn2(SIZE(rnd_breath_typ))]; + return ROLL_FROM(rnd_breath_typ); } return adtyp; } diff --git a/src/monmove.c b/src/monmove.c index 3fe8eeadf8..e7ea8117ed 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 monmove.c $NHDT-Date: 1684621592 2023/05/20 22:26:32 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.218 $ */ +/* 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. */ @@ -7,24 +7,48 @@ #include "mfndpos.h" #include "artifact.h" -static void watch_on_duty(struct monst *); -static int disturb(struct monst *); -static void release_hero(struct monst *); -static void distfleeck(struct monst *, int *, int *, int *); -static int m_arrival(struct monst *); -static void mind_blast(struct monst *); -static boolean holds_up_web(coordxy, coordxy); -static int count_webbing_walls(coordxy, coordxy); -static boolean soko_allow_web(struct monst *); -static boolean m_search_items(struct monst *, coordxy *, coordxy *, schar *, - int *); -static boolean leppie_avoidance(struct monst *); -static void leppie_stash(struct monst *); -static boolean m_balks_at_approaching(struct monst *); -static boolean stuff_prevents_passage(struct monst *); -static int vamp_shift(struct monst *, struct permonst *, boolean); -static boolean special_juiblex_actions(struct monst *); -static boolean special_baalzebub_actions(struct monst *); +staticfn void msg_mon_movement(struct monst *, coordxy, coordxy) NONNULLARG1; +staticfn void watch_on_duty(struct monst *); +staticfn int disturb(struct monst *); +staticfn void release_hero(struct monst *); +staticfn void distfleeck(struct monst *, int *, int *, int *); +staticfn int m_arrival(struct monst *); +staticfn void mind_blast(struct monst *); +staticfn boolean holds_up_web(coordxy, coordxy); +staticfn int count_webbing_walls(coordxy, coordxy); +staticfn boolean soko_allow_web(struct monst *); +staticfn boolean m_search_items(struct monst *, coordxy *, coordxy *, int *, + int *) NONNULLPTRS; +staticfn int postmov(struct monst *, struct permonst *, coordxy, coordxy, int, + unsigned, boolean) NONNULLPTRS; +staticfn boolean can_hide_under_obj(struct obj *obj); +staticfn boolean leppie_avoidance(struct monst *); +staticfn void leppie_stash(struct monst *); +staticfn boolean m_balks_at_approaching(struct monst *); +staticfn boolean stuff_prevents_passage(struct monst *); +staticfn int vamp_shift(struct monst *, struct permonst *, boolean); +staticfn void maybe_spin_web(struct monst *); +staticfn boolean special_juiblex_actions(struct monst *); +staticfn boolean special_baalzebub_actions(struct monst *); + +/* a11y: give a message when monster moved */ +staticfn void +msg_mon_movement(struct monst *mtmp, coordxy omx, coordxy omy) +{ + if (a11y.mon_movement && canspotmon(mtmp) && mtmp->mspotted) { + coordxy nix = mtmp->mx, niy = mtmp->my; + boolean n2u = next2u(nix, niy), + close = !n2u && (distu(nix, niy) <= (BOLT_LIM * BOLT_LIM)), + closer = !n2u && (distu(nix, niy) <= distu(omx, omy)); + + pline_xy(nix, niy, "%s %s%s.", Monnam(mtmp), + vtense((char *) 0, locomotion(mtmp->data, "move")), + n2u ? " next to you" + : (close && closer) ? " closer" + : (close && !closer) ? " further away" + : " in the distance"); + } +} #ifdef FUZZER_LOG void @@ -94,14 +118,14 @@ monhaskey( } void -mon_yells(struct monst* mon, const char* shout) +mon_yells(struct monst *mon, const char *shout) { if (Deaf) { if (canspotmon(mon)) /* Sidenote on "A watchman angrily waves her arms!" * Female being called watchman is correct (career name). */ - pline("%s angrily %s %s %s!", + pline_mon(mon, "%s angrily %s %s %s!", Amonnam(mon), nolimbs(mon->data) ? "shakes" : "waves", mhis(mon), @@ -109,7 +133,7 @@ mon_yells(struct monst* mon, const char* shout) : makeplural(mbodypart(mon, ARM))); } else { if (canspotmon(mon)) { - pline("%s yells:", Amonnam(mon)); + pline_mon(mon, "%s yells:", Amonnam(mon)); } else { /* Soundeffect(se_someone_yells, 75); */ You_hear("someone yell:"); @@ -137,20 +161,34 @@ m_break_boulder(struct monst *mtmp, coordxy x, coordxy y) if (m_can_break_boulder(mtmp) && ((otmp = sobj_at(BOULDER, x, y)) != 0)) { if (!is_rider(mtmp->data)) { - if (!Deaf && (mdistu(mtmp) < 4*4)) + if (!Deaf && (mdistu(mtmp) < 4*4)) { + if (canspotmon(mtmp)) + set_msg_xy(mtmp->mx, mtmp->my); pline("%s mutters %s.", Monnam(mtmp), mtmp->ispriest ? "a prayer" : "an incantation"); + } mtmp->mspec_used += rn1(20, 10); } - if (cansee(x, y)) + if (cansee(x, y)) { + set_msg_xy(x, y); pline_The("boulder falls apart."); + } + + /* boulders pushed onto shop's boundary or free spot are cases where + an item not in hero's inventory can have its unpaid flag set; + if the boulder isn't already on the bill, don't charge for it */ + if (otmp->unpaid) { + /* remove original from bill + add cloned copy to used-up bill */ + bill_dummy_object(otmp); + } + /* fracturing keeps otmp, changing its otyp from BOULDER to ROCK */ fracture_rock(otmp); } } -static void -watch_on_duty(register struct monst* mtmp) +staticfn void +watch_on_duty(struct monst *mtmp) { coordxy x, y; @@ -170,8 +208,8 @@ watch_on_duty(register struct monst* mtmp) } } else if (is_digging()) { /* chewing, wand/spell of digging are checked elsewhere */ - watch_dig(mtmp, gc.context.digging.pos.x, gc.context.digging.pos.y, - FALSE); + watch_dig(mtmp, svc.context.digging.pos.x, + svc.context.digging.pos.y, FALSE); } } } @@ -179,7 +217,7 @@ watch_on_duty(register struct monst* mtmp) /* move a monster; if a threat to busy hero, stop doing whatever it is */ int dochugw( - register struct monst *mtmp, + struct monst *mtmp, boolean chug) /* True: monster is moving; * False: monster was just created or has teleported * so perform stop-what-you're-doing-if-close-enough- @@ -287,10 +325,9 @@ onscary(coordxy x, coordxy y, struct monst *mtmp) void mon_regen(struct monst *mon, boolean digest_meal) { - if (mon->mhp < mon->mhpmax - && (gm.moves % 20 == 0 || regenerates(mon->data)) + if ((svm.moves % 20 == 0 || regenerates(mon->data)) && !mon->mwither) - mon->mhp++; + healmon(mon, 1, 0); if (mon->data == &mons[PM_GERYON]) mon->mhp = min(mon->mhp + geryon_bonus(), mon->mhpmax); if (mon->mspec_used) @@ -308,8 +345,8 @@ mon_regen(struct monst *mon, boolean digest_meal) * Possibly awaken the given monster. Return a 1 if the monster has been * jolted awake. */ -static int -disturb(register struct monst *mtmp) +staticfn int +disturb(struct monst *mtmp) { /* * + Ettins are hard to surprise. @@ -321,7 +358,7 @@ disturb(register struct monst *mtmp) * not stealthy or (mon is an ettin and 9/10) AND * (mon is not a nymph, jabberwock, or leprechaun) or 1/50 AND * Aggravate or mon is (dog or human) or - * (1/7 and mon is not mimicing furniture or object) + * (1/7 and mon is not mimicking furniture or object) */ if (couldsee(mtmp->mx, mtmp->my) && mdistu(mtmp) <= 100 && (!Stealth || (mtmp->data == &mons[PM_ETTIN] && rn2(10))) @@ -342,7 +379,7 @@ disturb(register struct monst *mtmp) } /* ungrab/expel held/swallowed hero */ -static void +staticfn void release_hero(struct monst *mon) { if (mon == u.ustuck) { @@ -360,7 +397,7 @@ find_pmmonst(int pm) { struct monst *mtmp = 0; - if ((gm.mvitals[pm].mvflags & G_GENOD) == 0) + if ((svm.mvitals[pm].mvflags & G_GENOD) == 0) for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; @@ -375,7 +412,7 @@ find_pmmonst(int pm) will eat it if there is no queen bee on the level; return 1: mon died, 0: mon ate jelly and lived; -1: mon didn't eat jelly to use its move */ int -bee_eat_jelly(struct monst* mon, struct obj* obj) +bee_eat_jelly(struct monst *mon, struct obj *obj) { int m_delay; struct monst *mtmp = find_pmmonst(PM_QUEEN_BEE); @@ -386,7 +423,7 @@ bee_eat_jelly(struct monst* mon, struct obj* obj) if (obj->quan > 1L) obj = splitobj(obj, 1L); if (canseemon(mon)) - pline("%s eats %s.", Monnam(mon), an(xname(obj))); + pline_mon(mon, "%s eats %s.", Monnam(mon), an(xname(obj))); delobj(obj); if ((int) mon->m_lev < mons[PM_QUEEN_BEE].mlevel - 1) @@ -403,6 +440,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) \ @@ -452,11 +515,12 @@ monflee( sleep and temporary paralysis, so both conditions receive the same alternate message */ if (!mtmp->mcanmove || !mtmp->data->mmove) { - pline("%s seems to flinch.", Adjmonnam(mtmp, "immobile")); + pline_mon(mtmp, "%s seems to flinch.", + Adjmonnam(mtmp, "immobile")); } else if (flees_light(mtmp)) { if (Unaware) { /* tell the player even if the hero is unconscious */ - pline("%s is frightened.", Monnam(mtmp)); + pline_mon(mtmp, "%s is frightened.", Monnam(mtmp)); } else if (rn2(10) || Deaf) { /* via flees_light(), will always be either via uwep (Sunsword) or uarm (gold dragon scales/mail) or both; @@ -468,14 +532,14 @@ monflee( ? yname(uarm) : "[its imagination?]"; - pline("%s flees from the painful light of %s.", + pline_mon(mtmp, "%s flees from the painful light of %s.", Monnam(mtmp), lsrc); } else { SetVoice(mtmp, 0, 80, 0); verbalize("Bright light!"); } } else { - pline("%s turns to flee.", Monnam(mtmp)); + pline_mon(mtmp, "%s turns to flee.", Monnam(mtmp)); } } @@ -490,7 +554,7 @@ monflee( mon_track_clear(mtmp); } -static void +staticfn void distfleeck( struct monst *mtmp, int *inrange, int *nearby, int *scared) /* output */ @@ -539,8 +603,8 @@ distfleeck( /* perform a special one-time action for a monster; returns -1 if nothing special happened, 0 if monster uses up its turn, 1 if monster is killed */ -static int -m_arrival(struct monst* mon) +staticfn int +m_arrival(struct monst *mon) { mon->mstrategy &= ~STRAT_ARRIVE; /* always reset */ @@ -548,13 +612,13 @@ m_arrival(struct monst* mon) } /* a mind flayer unleashes a mind blast */ -static void -mind_blast(register struct monst* mtmp) +staticfn void +mind_blast(struct monst *mtmp) { struct monst *m2, *nmon = (struct monst *) 0; if (canseemon(mtmp)) - pline("%s concentrates.", Monnam(mtmp)); + pline_mon(mtmp, "%s concentrates.", Monnam(mtmp)); if (mdistu(mtmp) > BOLT_LIM * BOLT_LIM) { You("sense a faint wave of psychic energy."); return; @@ -613,15 +677,53 @@ mind_blast(register struct monst* mtmp) } } +/* called every turn for each living monster on the map, and the hero; + caller makes sure that we're not called for DEADMONSTER() */ +void +m_everyturn_effect(struct monst *mtmp) +{ + boolean is_u = (mtmp == &gy.youmonst) ? TRUE : FALSE; + coordxy x = is_u ? u.ux : mtmp->mx, + y = is_u ? u.uy : mtmp->my; + + if (mtmp->data == &mons[PM_FOG_CLOUD]) { + /* don't leave a vapor cloud if some other gas cloud is already + present, or when flowing under closed doors so that visibility + changes aren't mixed with messages about doing such */ + if (!closed_door(x, y) && !visible_region_at(x, y)) + create_gas_cloud(x, y, 1, 0); /* harmless vapor */ + } +} + +/* do whatever effects monster has after moving. + called for both monsters and polyed hero. + for hero, called after location changes, + to prevent spam messages for hero getting enveloped in a cloud. + for monsters, called before location changes, + because monsters don't have "previous location" field */ +void +m_postmove_effect(struct monst *mtmp) +{ + boolean is_u = (mtmp == &gy.youmonst) ? TRUE : FALSE; + coordxy x = is_u ? u.ux0 : mtmp->mx, + y = is_u ? u.uy0 : mtmp->my; + + /* Hezrous create clouds of stench. This does not cost a move. */ + if (mtmp->data == &mons[PM_HEZROU]) /* stench */ + create_gas_cloud(x, y, 1, 8); + else if (mtmp->data == &mons[PM_STEAM_VORTEX] && !mtmp->mcan) + create_gas_cloud(x, y, 1, 0); /* harmless vapor */ +} + /* returns 1 if monster died moving, 0 otherwise */ /* The whole dochugw/m_move/distfleeck/mfndpos section is serious spaghetti * code. --KAA */ int -dochug(register struct monst* mtmp) +dochug(struct monst *mtmp) { - register struct permonst *mdat; - register int status = MMOVE_NOTHING; + struct permonst *mdat; + int status = MMOVE_NOTHING; int inrange, nearby, scared, res; struct obj *otmp; boolean panicattk = FALSE; @@ -686,6 +788,10 @@ dochug(register struct monst* mtmp) return 0; /* dead or gone */ } + /* 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. */ @@ -731,6 +837,7 @@ dochug(register struct monst* mtmp) /* tactics -> mnexto -> deal_with_overcrowding */ if (mtmp->mstate) return 0; + set_apparxy(mtmp); } /* check distance and scariness of attacks */ @@ -764,6 +871,8 @@ dochug(register struct monst* mtmp) } else { mtmp->minvis = mtmp->perminvis = 0; /* Why? For the same reason in real demon talk */ + if (canseemon(mtmp)) + set_msg_xy(mtmp->mx, mtmp->my); pline("%s gets angry!", Amonnam(mtmp)); mtmp->mpeaceful = 0; set_malign(mtmp); @@ -779,6 +888,7 @@ dochug(register struct monst* mtmp) /* mind flayers can make psychic attacks! */ } else if (is_mind_flayer(mdat) && !rn2(20)) { mind_blast(mtmp); + set_apparxy(mtmp); distfleeck(mtmp, &inrange, &nearby, &scared); } /* ghosts prefer turning invisible instead of moving if they can */ @@ -908,6 +1018,10 @@ dochug(register 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 @@ -940,6 +1054,8 @@ dochug(register struct monst* mtmp) if (!status) status = m_move(mtmp, 0); + if (mon_offmap(mtmp)) + return 1; if (status != MMOVE_DIED) distfleeck(mtmp, &inrange, &nearby, &scared); /* recalc */ @@ -947,6 +1063,7 @@ dochug(register 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 */ @@ -961,14 +1078,17 @@ dochug(register struct monst* mtmp) break; case MMOVE_MOVED: /* monster moved */ /* if confused grabber has wandered off, let go */ - if (mtmp == u.ustuck && !next2u(mtmp->mx, mtmp->my)) + if (mtmp == u.ustuck && !m_next2u(mtmp)) unstuck(mtmp); + if (grounded(mdat)) + disturb_buried_zombies(mtmp->mx, mtmp->my); /* Maybe it stepped on a trap and fell asleep... */ if (helpless(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; @@ -1024,6 +1144,8 @@ mon_would_take_item(struct monst *mtmp, struct obj *otmp) if (otmp == uball || otmp == uchain) return FALSE; + if (mtmp->mtame && otmp->cursed) + return FALSE; /* note: will get overridden if mtmp will eat otmp */ if (is_unicorn(mtmp->data) && otmp->material != GEMSTONE) return FALSE; if (!mindless(mtmp->data) && !is_animal(mtmp->data) && pctload < 75 @@ -1058,18 +1180,25 @@ mon_would_take_item(struct monst *mtmp, struct obj *otmp) boolean mon_would_consume_item(struct monst *mtmp, struct obj *otmp) { + int ftyp; + if (otmp->otyp == CORPSE && !touch_petrifies(&mons[otmp->corpsenm]) && corpse_eater(mtmp->data)) return TRUE; + if (mtmp->mtame && has_edog(mtmp) /* has_edog(): not guardian angel */ + && (ftyp = dogfood(mtmp, otmp)) < MANFOOD + && (ftyp < ACCFOOD || EDOG(mtmp)->hungrytime <= svm.moves)) + return TRUE; + return FALSE; } boolean -itsstuck(register struct monst* mtmp) +itsstuck(struct monst *mtmp) { if (sticks(gy.youmonst.data) && mtmp == u.ustuck && !u.uswallow) { - pline("%s cannot escape from you!", Monnam(mtmp)); + pline_mon(mtmp, "%s cannot escape from you!", Monnam(mtmp)); return TRUE; } return FALSE; @@ -1094,7 +1223,7 @@ should_displace( int shortest_with_displacing = -1; int shortest_without_displacing = -1; int count_without_displacing = 0; - register int i, nx, ny; + int i, nx, ny; int ndist; for (i = 0; i < cnt; i++) { @@ -1153,7 +1282,7 @@ m_digweapon_check( } /* does leprechaun want to avoid the hero? */ -static boolean +staticfn boolean leppie_avoidance(struct monst *mtmp) { struct obj *lepgold, *ygold; @@ -1168,7 +1297,7 @@ leppie_avoidance(struct monst *mtmp) } /* unseen leprechaun with gold might stash it */ -static void +staticfn void leppie_stash(struct monst *mtmp) { struct obj *gold; @@ -1189,8 +1318,8 @@ leppie_stash(struct monst *mtmp) } /* does monster want to avoid you? */ -static boolean -m_balks_at_approaching(struct monst* mtmp) +staticfn boolean +m_balks_at_approaching(struct monst *mtmp) { /* peaceful, far away, or can't see you */ if (mtmp->mpeaceful @@ -1208,7 +1337,7 @@ m_balks_at_approaching(struct monst* mtmp) return TRUE; /* 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; @@ -1216,13 +1345,13 @@ m_balks_at_approaching(struct monst* mtmp) return FALSE; } -static boolean +staticfn boolean 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) @@ -1233,7 +1362,7 @@ holds_up_web(coordxy x, coordxy y) /* returns the number of walls in the four cardinal directions that could hold up a web */ -static int +staticfn int count_webbing_walls(coordxy x, coordxy y) { return (holds_up_web(x, y - 1) + holds_up_web(x + 1, y) @@ -1241,7 +1370,7 @@ count_webbing_walls(coordxy x, coordxy y) } /* reject webs which interfere with solving Sokoban */ -static boolean +staticfn boolean soko_allow_web(struct monst *mon) { stairway *stway; @@ -1258,7 +1387,7 @@ soko_allow_web(struct monst *mon) } /* monster might spin a web */ -static void +staticfn void maybe_spin_web(struct monst *mtmp) { if (webmaker(mtmp->data) @@ -1276,7 +1405,7 @@ maybe_spin_web(struct monst *mtmp) char mbuf[BUFSZ]; Strcpy(mbuf, canspotmon(mtmp) ? y_monnam(mtmp) : something); - pline("%s spins a web.", upstart(mbuf)); + pline_mon(mtmp, "%s spins a web.", upstart(mbuf)); trap->tseen = 1; } if (*in_rooms(mtmp->mx, mtmp->my, SHOPBASE)) @@ -1287,16 +1416,50 @@ maybe_spin_web(struct monst *mtmp) } } +/* monster avoids a location nx, ny, if hero kicked that location */ +boolean +m_avoid_kicked_loc(struct monst *mtmp, coordxy nx, coordxy ny) +{ + if ((mtmp->mpeaceful || mtmp->mtame) + && mtmp->mcansee + && !mtmp->mconf && !mtmp->mstun + && !Conflict + && isok(gk.kickedloc.x, gk.kickedloc.y) + && nx == gk.kickedloc.x && ny == gk.kickedloc.y + && next2u(nx, ny)) + return TRUE; + return FALSE; +} + +/* monster avoids a location nx, ny, if we're in sokoban, and + there's a boulder between the location and hero */ +boolean +m_avoid_soko_push_loc(struct monst *mtmp, coordxy nx, coordxy ny) +{ + if (Sokoban + && (mtmp->mpeaceful || mtmp->mtame) + && !mtmp->mconf && !mtmp->mstun + && !Conflict + && (dist2(nx, ny, u.ux, u.uy) == 4) + && sobj_at(BOULDER, nx + sgn(u.ux - nx), ny + sgn(u.uy - ny))) + return TRUE; + return FALSE; +} + /* max distmin() distance for monster to look for items */ #define SQSRCHRADIUS 5 /* monster looks for items it wants nearby */ -static boolean -m_search_items(struct monst *mtmp, coordxy *ggx, coordxy *ggy, schar *mmoved, int *appr) +staticfn boolean +m_search_items( + struct monst *mtmp, + coordxy *ggx, coordxy *ggy, + int *mmoved, + int *appr) { - register int minr = SQSRCHRADIUS; /* not too far away */ - register struct obj *otmp; - register coordxy xx, yy; + int minr = SQSRCHRADIUS; /* not too far away */ + struct obj *otmp; + coordxy xx, yy; coordxy hmx, hmy, lmx, lmy; struct trap *ttmp; coordxy omx = mtmp->mx, omy = mtmp->my; @@ -1366,7 +1529,7 @@ m_search_items(struct monst *mtmp, coordxy *ggx, coordxy *ggy, schar *mmoved, in costly = costly_spot(xx, yy); /* look through the items on this location */ - for (otmp = gl.level.objects[xx][yy]; + for (otmp = svl.level.objects[xx][yy]; otmp; otmp = otmp->nexthere) { /* monsters may pick rocks up, but won't go out of their way to grab them; this might hamper sling wielders, but it cuts @@ -1399,7 +1562,7 @@ m_search_items(struct monst *mtmp, coordxy *ggx, coordxy *ggy, schar *mmoved, in } } -finish_search: + finish_search: if (minr < SQSRCHRADIUS && *appr == -1) { if (distmin(omx, omy, mtmp->mux, mtmp->muy) <= 3) { *ggx = mtmp->mux; @@ -1412,30 +1575,207 @@ m_search_items(struct monst *mtmp, coordxy *ggx, coordxy *ggy, schar *mmoved, in #undef SQSRCHRADIUS -/* Handles the movement of a standard monster. */ -/* Return values: - * 0: did not move, but can still attack and do other stuff. - * 1: moved, possibly can attack. - * 2: monster died. +staticfn int +postmov( + struct monst *mtmp, + struct permonst *ptr, + coordxy omx, coordxy omy, + int mmoved, + unsigned seenflgs, + boolean can_tunnel) +{ + coordxy nix, niy; + int etmp, trapret; + + notice_mon(mtmp); + + if (mmoved == MMOVE_MOVED) { + nix = mtmp->mx, niy = mtmp->my; + /* sequencing issue: when monster movement decides that a + monster can move to a door location, it moves the monster + there before dealing with the door rather than after; + so a vampire/bat that is going to shift to fog cloud and + pass under the door is already there but transformation + into fog form--and its message, when in sight--has not + happened yet; we have to move monster back to previous + location before performing the vamp_shift() to make the + message happen at right time, then back to the door again + [if we did the shift sooner, before moving the monster, + we would need to duplicate it in dog_move()...] */ + if (is_vampshifter(mtmp) && !amorphous(mtmp->data) + && IS_DOOR(levl[nix][niy].typ) + && door_is_closed(&levl[nix][niy]) + && can_fog(mtmp)) { + /* note: remove_monster()+place_monster is not right for + long worms but they won't reach here */ + if (seenflgs) { + remove_monster(nix, niy); + place_monster(mtmp, omx, omy); + newsym(nix, niy), newsym(omx, omy); + } + 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); + place_monster(mtmp, nix, niy); + newsym(omx, omy), newsym(nix, niy); + } + } + + newsym(omx, omy); /* update the old position */ + trapret = mintrap(mtmp, NO_TRAP_FLAGS); + if (trapret == Trap_Killed_Mon || trapret == Trap_Moved_Mon) { + if (mtmp->mx) + newsym(mtmp->mx, mtmp->my); + return MMOVE_DIED; /* it died */ + } + ptr = mtmp->data; /* in case mintrap() caused polymorph */ + + /* aos: The only way a monster can move directly onto a closed door is + * by flowing under it. This if implicitly handles nodoor/open/broken + * cases as well. + * Monsters should be prevented from moving onto a + * demogorgon_special_door space before getting here. */ + if (IS_DOOR(levl[mtmp->mx][mtmp->my].typ) + && !passes_walls(ptr) /* doesn't need to open doors */ + && !can_tunnel) { /* taken care of below */ + struct rm *here = &levl[mtmp->mx][mtmp->my]; + + /* code for monster opening a door (and occupying the door's space + * as part of the same move) used to live here, but now happens as + * part of its own action in mon_open_door, before in m_move rather + * than in postmov. Thus, it's impossible for a monster to move onto + * a door that's still closed, unless it can physically exist in the + * same space as a closed door. */ + if (door_is_closed(here) && amorphous(ptr)) { + if (flags.verbose && canseemon(mtmp)) + pline("%s %s under the door.", Monnam(mtmp), + (ptr == &mons[PM_FOG_CLOUD] + || ptr->mlet == S_LIGHT) ? "flows" : "oozes"); + } else if (door_is_locked(here)) { + impossible("m_move: monster moving to locked door"); + } else if (door_is_closed(here)) { + impossible("m_move: monster moving to closed door"); + } + + + } else if (levl[mtmp->mx][mtmp->my].typ == IRONBARS) { + /* 3.6.2: was using may_dig() but that doesn't handle bars; + AD_RUST catches rust monsters but metallivorous() is + needed for xorns and rock moles */ + if (!(levl[mtmp->mx][mtmp->my].wall_info & W_NONDIGGABLE) + && (dmgtype(ptr, AD_RUST) || dmgtype(ptr, AD_CORR) + || metallivorous(ptr))) { + if (canseemon(mtmp)) + pline_mon(mtmp, "%s eats through the iron bars.", + Monnam(mtmp)); + dissolve_bars(mtmp->mx, mtmp->my); + return MMOVE_DONE; + } else if (flags.verbose && canseemon(mtmp)) + Norep("%s %s %s the iron bars.", Monnam(mtmp), + /* pluralization fakes verb conjugation */ + makeplural(locomotion(ptr, "pass")), + passes_walls(ptr) ? "through" : "between"); + } /* doors and bars */ + + /* possibly dig */ + if (can_tunnel && may_dig(mtmp->mx, mtmp->my) + && mdig_tunnel(mtmp)) + return MMOVE_DIED; /* mon died (position already updated) */ + + /* set also in domove(), hack.c */ + if (engulfing_u(mtmp) && (mtmp->mx != omx || mtmp->my != omy)) { + /* If the monster moved, then update */ + u.ux0 = u.ux; + u.uy0 = u.uy; + u_on_newpos(mtmp->mx, mtmp->my); + swallowed(0); + } else { + newsym(mtmp->mx, mtmp->my); + } + } /* mmoved==MMOVE_MOVED */ + + if (mmoved == MMOVE_MOVED || mmoved == MMOVE_DONE) { + if (OBJ_AT(mtmp->mx, mtmp->my) && mtmp->mcanmove) { + etmp = 0; /* default "nothing happened" from meat*() funcs */ + + /* Maybe a rock mole just ate some rock object */ + if (lithivorous(ptr)) { + etmp = meatrocks(mtmp); + if (etmp == 2) /* not currently possible */ + return MMOVE_DIED; + } + + /* Maybe a rock mole just ate some metal object */ + if (metallivorous(ptr)) { + if (meatmetal(mtmp) == 2) + return MMOVE_DIED; /* it died */ + } + + /* Maybe a cube ate just about anything */ + if (ptr == &mons[PM_GELATINOUS_CUBE]) { + if ((etmp = meatobj(mtmp)) >= 2) + return etmp; /* it died or got forced off the level */ + } + /* Maybe a purple worm ate a corpse */ + if (corpse_eater(ptr)) { + if ((etmp = meatcorpse(mtmp)) >= 2) + return etmp; /* it died or got forced off the level */ + } + + if (mpickstuff(mtmp)) + mmoved = MMOVE_DONE; + + if (mtmp->minvis) { + newsym(mtmp->mx, mtmp->my); + if (mtmp->wormno) + see_wsegs(mtmp); + } + } + + maybe_spin_web(mtmp); + + if (hides_under(ptr) || ptr->mlet == S_EEL) { + /* Always set--or reset--mundetected if it's already hidden + (just in case the object it was hiding under went away); + usually set mundetected unless monster can't move. */ + if (mtmp->mundetected || (!helpless(mtmp) && rn2(5))) + (void) hideunder(mtmp); + newsym(mtmp->mx, mtmp->my); + } + if (mtmp->isshk) { + after_shk_move(mtmp); + } + } + return mmoved; +} + +/* Handles the movement of a standard monster. + * Return values: + * 0: did not move, but can still attack and do other stuff; + * 1: moved, possibly can attack; + * 2: monster died; * 3: did not move, and can't do anything else either. */ int -m_move(register struct monst *mtmp, int after) +m_move(struct monst *mtmp, int after) { - int appr, etmp; + int appr; coordxy ggx, ggy, nix, niy; xint16 chcnt; - int chi; /* could be schar except for stupid Sun-2 compiler */ boolean can_tunnel = 0; boolean getitems = FALSE; boolean avoid = FALSE; boolean better_with_displacing = FALSE; - boolean sawmon = canspotmon(mtmp); /* before it moved */ + unsigned seenflgs; struct permonst *ptr; - schar mmoved = MMOVE_NOTHING; /* not strictly nec.: chi >= 0 will do */ + int chi, mmoved = MMOVE_NOTHING; /* not strictly nec.: chi >= 0 will do */ long info[9]; long flag; - int omx = mtmp->mx, omy = mtmp->my; + coordxy omx = mtmp->mx, omy = mtmp->my; if (mtmp->mtrapped) { int i = mintrap(mtmp, NO_TRAP_FLAGS); @@ -1456,6 +1796,9 @@ m_move(register struct monst *mtmp, int after) return MMOVE_DONE; /* still eating */ } + /* set up pre-move visibility flags */ + seenflgs = (canseemon(mtmp) ? 1 : 0) | (canspotmon(mtmp) ? 2 : 0); + /* Where does 'mtmp' think you are? Not necessary if m_move() called from this file, but needed for other calls of m_move(). */ set_apparxy(mtmp); /* set mtmp->mux, mtmp->muy */ @@ -1465,10 +1808,8 @@ m_move(register struct monst *mtmp, int after) goto not_special; /* my dog gets special treatment */ if (mtmp->mtame) { - mmoved = dog_move(mtmp, after); - if (mmoved >= 0) - goto postmov; - mmoved = 0; /* do regular m_move */ + return postmov(mtmp, ptr, omx, omy, dog_move(mtmp, after), + seenflgs, can_tunnel); } /* and the acquisitive monsters get special treatment */ @@ -1497,7 +1838,7 @@ m_move(register struct monst *mtmp, int after) mmoved = MMOVE_NOTHING; } if (!covetous_nonwarper(ptr)) - goto postmov; + return postmov(mtmp, ptr, omx, omy, mmoved, seenflgs, can_tunnel); } /* likewise for shopkeeper, guard, or priest */ @@ -1512,15 +1853,15 @@ m_move(register struct monst *mtmp, int after) case -1: mmoved = MMOVE_NOTHING; /* shk follow hero outside shop */ break; + default: + impossible("unknown shk/gd/pri_move return value (%d)", xm); + FALLTHROUGH; + /*FALLTHRU*/ case 0: - mmoved = MMOVE_NOTHING; - goto postmov; case 1: - mmoved = MMOVE_MOVED; - goto postmov; - default: impossible("unknown shk/gd/pri_move return value (%i)", xm); - mmoved = MMOVE_NOTHING; - goto postmov; + return postmov(mtmp, ptr, omx, omy, + (xm != 1) ? MMOVE_NOTHING : MMOVE_MOVED, + seenflgs, can_tunnel); } } @@ -1542,8 +1883,8 @@ m_move(register struct monst *mtmp, int after) (void) rloc(mtmp, RLOC_MSG); else mnexto(mtmp, RLOC_MSG); - mmoved = MMOVE_MOVED; - goto postmov; + return postmov(mtmp, ptr, omx, omy, MMOVE_MOVED, + seenflgs, can_tunnel); } not_special: if (u.uswallow && !mtmp->mflee && u.ustuck != mtmp) @@ -1584,7 +1925,7 @@ m_move(register struct monst *mtmp, int after) appr = -1; if (!should_see && can_track(ptr)) { - register coord *cp; + coord *cp; cp = gettrack(omx, omy); if (cp) { @@ -1597,7 +1938,8 @@ m_move(register struct monst *mtmp, int after) if (!mtmp->mpeaceful || !rn2(10)) { boolean in_line = (lined_up(mtmp) && (distmin(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) - <= (throws_rocks(gy.youmonst.data) ? 20 : ACURRSTR / 2 + 1))); + <= (throws_rocks(gy.youmonst.data) ? 20 + : (ACURRSTR / 2 + 1)))); if (appr != 1 || !in_line) { /* Monsters in combat won't pick stuff up, avoiding the @@ -1609,7 +1951,8 @@ m_move(register struct monst *mtmp, int after) } if (getitems && m_search_items(mtmp, &ggx, &ggy, &mmoved, &appr)) - goto postmov; + return postmov(mtmp, ptr, omx, omy, mmoved, + seenflgs, can_tunnel); /* don't tunnel if hostile and close enough to prefer a weapon */ if (can_tunnel && needspick(ptr) @@ -1628,7 +1971,7 @@ m_move(register 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; @@ -1638,7 +1981,7 @@ m_move(register struct monst *mtmp, int after) chi = -1; nidist = dist2(nix, niy, ggx, ggy); /* allow monsters be shortsighted on some levels for balance */ - if (!mtmp->mpeaceful && gl.level.flags.shortsighted + if (!mtmp->mpeaceful && svl.level.flags.shortsighted && nidist > (couldsee(nix, niy) ? 144 : 36) && appr == 1) appr = 0; if (is_unicorn(ptr) && noteleport_level(mtmp)) { @@ -1655,6 +1998,9 @@ m_move(register struct monst *mtmp, int after) nx = poss[i].x; ny = poss[i].y; + if (m_avoid_kicked_loc(mtmp, nx, ny)) + continue; + if (MON_AT(nx, ny) && (info[i] & ALLOW_MDISP) && !(info[i] & ALLOW_M) && !better_with_displacing) continue; @@ -1710,6 +2056,7 @@ m_move(register struct monst *mtmp, int after) * mfndpos) has no effect for normal attacks, though it lets a * confused monster attack you by accident. */ + assert(IndexOk(chi, info)); if (info[chi] & ALLOW_U) { nix = mtmp->mux; niy = mtmp->muy; @@ -1729,8 +2076,8 @@ m_move(register struct monst *mtmp, int after) || (nix == mtmp->mux && niy == mtmp->muy)) return m_move_aggress(mtmp, nix, niy); - /* hiding-under monsters will attack things from their hiding spot but - * are less likely to venture out */ + /* monsters hiding under things are less likely to venture out, even to + * attack */ if (hides_under(ptr) && concealed_spot(mtmp->mx, mtmp->my) && !concealed_spot(nix, niy) && rn2(10)) { return MMOVE_NOTHING; @@ -1758,10 +2105,13 @@ m_move(register struct monst *mtmp, int after) return MMOVE_DONE; } + m_postmove_effect(mtmp); + /* move a normal monster; for a long worm, remove_monster() and place_monster() only manipulate the head; they leave tail as-is */ remove_monster(omx, omy); place_monster(mtmp, nix, niy); + msg_mon_movement(mtmp, omx, omy); /* for a long worm, insert a new segment to reconnect the head with the tail; worm_move() keeps the end of the tail if worm is scheduled to grow, removes that for move-without-growing */ @@ -1781,163 +2131,7 @@ m_move(register struct monst *mtmp, int after) if (mtmp->wormno) worm_nomove(mtmp); } - postmov: - if (mmoved == MMOVE_MOVED || mmoved == MMOVE_DONE) { - - if (mmoved == MMOVE_MOVED) { - int trapret; - - /* normal monster move will already have , - but pet dog_move() with 'goto postmov' won't */ - nix = mtmp->mx, niy = mtmp->my; - /* sequencing issue: when monster movement decides that a - monster can move to a door location, it moves the monster - there before dealing with the door rather than after; - so a vampire/bat that is going to shift to fog cloud and - pass under the door is already there but transformation - into fog form--and its message, when in sight--has not - happened yet; we have to move monster back to previous - location before performing the vamp_shift() to make the - message happen at right time, then back to the door again - [if we did the shift above, before moving the monster, - we would need to duplicate it in dog_move()...] */ - if (is_vampshifter(mtmp) && !amorphous(mtmp->data) - && IS_DOOR(levl[nix][niy].typ) - && door_is_closed(&levl[nix][niy]) - && can_fog(mtmp)) { - if (sawmon) { - remove_monster(nix, niy); - place_monster(mtmp, omx, omy); - newsym(nix, niy), newsym(omx, omy); - } - if (vamp_shift(mtmp, &mons[PM_FOG_CLOUD], sawmon)) { - ptr = mtmp->data; /* update cached value */ - } - if (sawmon) { - remove_monster(omx, omy); - place_monster(mtmp, nix, niy); - newsym(omx, omy), newsym(nix, niy); - } - } - - newsym(omx, omy); /* update the old position */ - trapret = mintrap(mtmp, NO_TRAP_FLAGS); - if (trapret == Trap_Killed_Mon || trapret == Trap_Moved_Mon) { - if (mtmp->mx) - newsym(mtmp->mx, mtmp->my); - return MMOVE_DIED; /* it died */ - } - ptr = mtmp->data; /* in case mintrap() caused polymorph */ - - /* aos: The only way a monster can move directly onto a closed door - * is by flowing under it. This if implicitly handles - * nodoor/open/broken cases as well. - * Monsters should be prevented from moving onto a - * demogorgon_special_door space before getting here. */ - if (IS_DOOR(levl[mtmp->mx][mtmp->my].typ) - && !passes_walls(ptr) /* doesn't need to open doors */ - && !can_tunnel) { /* taken care of below */ - struct rm *here = &levl[mtmp->mx][mtmp->my]; - - if (door_is_closed(here) && amorphous(ptr)) { - if (Verbose(2, m_move1) && canseemon(mtmp)) - pline("%s %s under the door.", Monnam(mtmp), - (ptr == &mons[PM_FOG_CLOUD] - || ptr->mlet == S_LIGHT) ? "flows" : "oozes"); - } else if (door_is_locked(here)) { - impossible("m_move: monster moving to locked door"); - } else if (door_is_closed(here)) { - impossible("m_move: monster moving to closed door"); - } - } else if (levl[mtmp->mx][mtmp->my].typ == IRONBARS) { - /* 3.6.2: was using may_dig() but that doesn't handle bars; - AD_RUST catches rust monsters but metallivorous() is - needed for xorns and rock moles */ - if (!(levl[mtmp->mx][mtmp->my].wall_info & W_NONDIGGABLE) - && (dmgtype(ptr, AD_RUST) || dmgtype(ptr, AD_CORR) - || metallivorous(ptr))) { - if (canseemon(mtmp)) - pline("%s eats through the iron bars.", Monnam(mtmp)); - dissolve_bars(mtmp->mx, mtmp->my); - return MMOVE_DONE; - } else if (Verbose(2, m_move5) && canseemon(mtmp)) - Norep("%s %s %s the iron bars.", Monnam(mtmp), - /* pluralization fakes verb conjugation */ - makeplural(locomotion(ptr, "pass")), - passes_walls(ptr) ? "through" : "between"); - } - - /* possibly dig */ - if (can_tunnel && may_dig(mtmp->mx, mtmp->my) - && mdig_tunnel(mtmp)) - return MMOVE_DIED; /* mon died (position already updated) */ - - /* set also in domove(), hack.c */ - if (engulfing_u(mtmp) - && (mtmp->mx != omx || mtmp->my != omy)) { - /* If the monster moved, then update */ - u.ux0 = u.ux; - u.uy0 = u.uy; - u.ux = mtmp->mx; - u.uy = mtmp->my; - swallowed(0); - } else - newsym(mtmp->mx, mtmp->my); - } - if (OBJ_AT(mtmp->mx, mtmp->my) && mtmp->mcanmove) { - etmp = 0; /* default "nothing happened" from meat*() funcs */ - - /* Maybe a rock mole just ate some rock object */ - if (lithivorous(ptr)) { - etmp = meatrocks(mtmp); - if (etmp == 2) /* not currently possible */ - return MMOVE_DIED; - } - - /* Or maybe the rock mole just ate some metal object */ - if (etmp == 0 && metallivorous(ptr)) { - etmp = meatmetal(mtmp); - if (etmp == 2) - return MMOVE_DIED; /* it died */ - } - - /* Maybe a cube ate just about anything */ - if (ptr == &mons[PM_GELATINOUS_CUBE]) { - if ((etmp = meatobj(mtmp)) >= 2) - return etmp; /* it died or got forced off the level */ - } - /* Maybe a purple worm ate a corpse */ - if (corpse_eater(ptr)) { - if ((etmp = meatcorpse(mtmp)) >= 2) - return etmp; /* it died or got forced off the level */ - } - - if (mpickstuff(mtmp)) - mmoved = MMOVE_DONE; - - if (mtmp->minvis) { - newsym(mtmp->mx, mtmp->my); - if (mtmp->wormno) - see_wsegs(mtmp); - } - } - - maybe_spin_web(mtmp); - - if (hides_under(ptr) || ptr->mlet == S_EEL) { - /* Always set--or reset--mundetected if it's already hidden - (just in case the object it was hiding under went away); - usually set mundetected unless monster can't move. */ - if (mtmp->mundetected - || (!helpless(mtmp) && rn2(5))) - (void) hideunder(mtmp); - newsym(mtmp->mx, mtmp->my); - } - if (mtmp->isshk) { - after_shk_move(mtmp); - } - } - return mmoved; + return postmov(mtmp, ptr, omx, omy, mmoved, seenflgs, can_tunnel); } /* The part of m_move that deals with a monster attacking another monster (and @@ -1963,7 +2157,7 @@ m_move_aggress(struct monst *mtmp, coordxy x, coordxy y) mstatus = mattackm(mtmp, mtmp2); } - if (mstatus & M_ATTK_AGR_DIED) /* aggressor died */ + if ((mstatus & M_ATTK_AGR_DIED) || DEADMONSTER(mtmp)) /* aggressor died */ return MMOVE_DIED; if ((mstatus & (M_ATTK_HIT | M_ATTK_DEF_DIED)) == M_ATTK_HIT @@ -1991,9 +2185,18 @@ m_move_aggress(struct monst *mtmp, coordxy x, coordxy y) int concealed_spot(coordxy x, coordxy y) { + struct obj *otmp = svl.level.objects[x][y]; int cflags = NOT_CONCEALABLE_SPOT; - if (OBJ_AT(x, y)) + struct trap *t; + + /* can't hide in/on/under traps (except pits) even when there is an + object here */ + if ((t = t_at(x, y)) != 0 && !is_pit(t->ttyp)) + return NOT_CONCEALABLE_SPOT; + + if (otmp && can_hide_under_obj(otmp)) cflags |= CONCEALABLE_BY_OBJECT; + switch (levl[x][y].typ) { case GRASS: case SINK: @@ -2006,11 +2209,35 @@ concealed_spot(coordxy x, coordxy y) return cflags; } +/* returns TRUE if a mon can hide under the obj */ +staticfn boolean +can_hide_under_obj(struct obj *obj) +{ + if (!obj || obj->where != OBJ_FLOOR) + return FALSE; + /* can't hide under small amount of coins unless non-coins are also + present; we expect coins to be a single stack but don't assume that */ + if (obj->oclass == COIN_CLASS) { + long coinquan = 0L; + + do { + /* 10 coins is arbitrary amount considered enough to hide under */ + if ((coinquan += obj->quan) >= 10L) + break; /* fall through to other checks */ + obj = obj->nexthere; + if (!obj) + return FALSE; /* whole pile was less than 10 coins */ + } while (obj->oclass == COIN_CLASS); + } + return TRUE; /* can hide under the object */ +} + void dissolve_bars(coordxy x, coordxy y) { - levl[x][y].typ = (Is_special(&u.uz) || *in_rooms(x, y, 0)) ? ROOM : CORR; - levl[x][y].flags = 0; + levl[x][y].typ = (levl[x][y].edge == 1) ? DOOR + : (Is_special(&u.uz) || *in_rooms(x, y, 0)) ? ROOM : CORR; + set_doorstate(&levl[x][y], D_NODOOR); newsym(x, y); if (u_at(x, y)) switch_terrain(); @@ -2034,10 +2261,10 @@ accessible(coordxy x, coordxy y) /* decide where the monster thinks you are standing */ void -set_apparxy(register struct monst *mtmp) +set_apparxy(struct monst *mtmp) { boolean notseen, notthere, gotu; - int disp; + int displ; coordxy mx = mtmp->mux, my = mtmp->muy; long umoney = money_cnt(gi.invent); @@ -2059,17 +2286,17 @@ set_apparxy(register struct monst *mtmp) notthere = (Displaced && mtmp->data != &mons[PM_DISPLACER_BEAST]); /* add cases as required. eg. Displacement ... */ if (Underwater) { - disp = 1; + displ = 1; } else if (notseen) { /* Xorns can smell quantities of valuable metal like that in solid gold coins, treat as seen */ - disp = (mtmp->data == &mons[PM_XORN] && umoney) ? 0 : 1; + displ = (mtmp->data == &mons[PM_XORN] && umoney) ? 0 : 1; } else if (notthere) { - disp = couldsee(mx, my) ? 2 : 1; + displ = couldsee(mx, my) ? 2 : 1; } else { - disp = 0; + displ = 0; } - if (!disp) { + if (!displ) { mtmp->mux = u.ux; mtmp->muy = u.uy; return; @@ -2080,7 +2307,7 @@ set_apparxy(register struct monst *mtmp) gotu = notseen ? !rn2(3) : notthere ? !rn2(4) : FALSE; if (!gotu) { - register int try_cnt = 0; + int try_cnt = 0; do { if (++try_cnt > 200) { @@ -2088,10 +2315,10 @@ set_apparxy(register struct monst *mtmp) my = u.uy; break; /* punt */ } - mx = u.ux - disp + rn2(2 * disp + 1); - my = u.uy - disp + rn2(2 * disp + 1); + mx = u.ux - displ + rn2(2 * displ + 1); + my = u.uy - displ + rn2(2 * displ + 1); } while (!isok(mx, my) - || (disp != 2 && mx == mtmp->mx && my == mtmp->my) + || (displ != 2 && mx == mtmp->mx && my == mtmp->my) || ((mx != u.ux || my != u.uy) && !passes_walls(mtmp->data) && !(accessible(mx, my) || (closed_door(mx, my) @@ -2120,7 +2347,7 @@ undesirable_disp( coordxy x, coordxy y) /* spot 'mtmp' is considering moving to */ { - boolean is_pet = (mtmp && mtmp->mtame && !mtmp->isminion); + boolean is_pet = (mtmp->mtame && !mtmp->isminion); struct trap *trap = t_at(x, y); if (is_pet) { @@ -2156,8 +2383,8 @@ undesirable_disp( * Inventory prevents passage under door. * Used by can_ooze() and can_fog(). */ -static boolean -stuff_prevents_passage(struct monst* mtmp) +staticfn boolean +stuff_prevents_passage(struct monst *mtmp) { struct obj *chain, *obj; @@ -2194,7 +2421,7 @@ stuff_prevents_passage(struct monst* mtmp) } boolean -can_ooze(struct monst* mtmp) +can_ooze(struct monst *mtmp) { if (!amorphous(mtmp->data) || stuff_prevents_passage(mtmp)) return FALSE; @@ -2203,49 +2430,34 @@ can_ooze(struct monst* mtmp) /* monster can change form into a fog if necessary */ boolean -can_fog(struct monst* mtmp) +can_fog(struct monst *mtmp) { - if (!(gm.mvitals[PM_FOG_CLOUD].mvflags & G_GENOD) && is_vampshifter(mtmp) + if (!(svm.mvitals[PM_FOG_CLOUD].mvflags & G_GENOD) && is_vampshifter(mtmp) && !Protection_from_shape_changers && !stuff_prevents_passage(mtmp)) return TRUE; return FALSE; } -static int +/* this is called when a vampire turns into a fog cloud in order to move + under a closed door; if it was sensed via telepathy or seen via + infravision, its new fog cloud shape will disappear */ +staticfn int vamp_shift( struct monst *mon, struct permonst *ptr, boolean domsg) { int reslt = 0; - char oldmtype[BUFSZ]; - boolean sawmon = canseemon(mon); /* before shape change */ - - /* remember current monster type before shapechange */ - Strcpy(oldmtype, domsg ? noname_monnam(mon, ARTICLE_THE) : ""); if (mon->data == ptr) { /* already right shape */ reslt = 1; - domsg = FALSE; } else if (is_vampshifter(mon)) { - reslt = newcham(mon, ptr, NO_NC_FLAGS); - } - - if (reslt && domsg) { - /* might have seen vampire/bat/wolf with infravision then be - unable to see the same creature when it turns into a fog cloud */ - if (canspotmon(mon)) - You("%s %s where %s was.", - !canseemon(mon) ? "now detect" : "observe", - noname_monnam(mon, ARTICLE_A), oldmtype); - else - You("can no longer %s %s.", sawmon ? "see" : "sense", oldmtype); - /* this message is given when it turns into a fog cloud - in order to move under a closed door */ + reslt = newcham(mon, ptr, domsg ? NC_SHOW_MSG : NO_NC_FLAGS); + /* shape-change message is given when vampshifter turns into a + fog cloud in order to move under a closed door */ display_nhwindow(WIN_MESSAGE, FALSE); } - return reslt; } @@ -2297,7 +2509,7 @@ mon_open_door(struct monst * mtmp, coordxy x, coordxy y) if (doortrapped(x, y, mtmp, FINGER, -D_LOCKED, 2) == 0) { if (DEADMONSTER(mtmp)) return 2; - if (Verbose(2, mon_open_door1)) { + if (flags.verbose) { if (observeit) pline("%s unlocks a door.", Monnam(mtmp)); else if (!Deaf) { @@ -2306,15 +2518,16 @@ mon_open_door(struct monst * mtmp, coordxy x, coordxy y) } } set_door_lock(here, FALSE); + /* no vision updates needed; door is still closed */ } return TRUE; } else if (!door_is_locked(here) && can_open) { - /* tries to open, trigger openg traps */ + /* tries to open, trigger opening traps */ if (predoortrapped(x, y, mtmp, FINGER, D_ISOPEN) == 0) { if (DEADMONSTER(mtmp)) return 2; - if (Verbose(2, mon_open_door2)) { + if (flags.verbose) { if (observeit) pline("%s opens a door.", Monnam(mtmp)); else if (cansee(x, y)) @@ -2327,8 +2540,11 @@ mon_open_door(struct monst * mtmp, coordxy x, coordxy y) if (postdoortrapped(x, y, mtmp, FINGER, D_ISOPEN) == 0) { set_doorstate(here, D_ISOPEN); - unblock_point(x, y); + } + if (!door_is_closed(here)) { newsym(x,y); + unblock_point(x, y); + vision_recalc(0); } } return TRUE; @@ -2338,7 +2554,7 @@ mon_open_door(struct monst * mtmp, coordxy x, coordxy y) if (predoortrapped(x, y, mtmp, HAND, D_BROKEN) == 0) { if (DEADMONSTER(mtmp)) return 2; - if (Verbose(2, mon_open_door3)) { + if (flags.verbose) { if (observeit) pline("%s smashes down a door.", Monnam(mtmp)); @@ -2351,12 +2567,15 @@ mon_open_door(struct monst * mtmp, coordxy x, coordxy y) } if (postdoortrapped(x, y, mtmp, HAND, D_BROKEN) == 0) { set_doorstate(here, D_BROKEN); - unblock_point(x, y); /* vision */ - newsym(x,y); /* if it's a shop door, schedule repair */ if (*in_rooms(x, y, SHOPBASE)) add_damage(x, y, 0L); } + if (!door_is_closed(here)) { + newsym(x,y); + unblock_point(x, y); + vision_recalc(0); + } } return TRUE; } @@ -2368,6 +2587,7 @@ mon_open_door(struct monst * mtmp, coordxy x, coordxy y) return FALSE; } +/* previously a macro in rm.h; moved here to handle fuzzer logging */ void remove_monster(coordxy x, coordxy y) { @@ -2375,15 +2595,15 @@ remove_monster(coordxy x, coordxy y) fuzl_xy("remove_monster", x, y); #endif #ifdef EXTRA_SANITY_CHECKS - if (!gl.level.monsters[x][y]) + if (!svl.level.monsters[x][y]) impossible("no monster to remove"); #endif - gl.level.monsters[x][y] = (struct monst *) 0; + svl.level.monsters[x][y] = (struct monst *) 0; } /* once-per-move actions and effects for Juiblex; return true if this has used * up his move and false if he should continue with his normal actions */ -static boolean +staticfn boolean special_juiblex_actions(struct monst *juib) { struct monst *m2; @@ -2411,8 +2631,7 @@ special_juiblex_actions(struct monst *juib) } else if (rn2(2)) { const char classes[] = { S_BLOB, S_JELLY, S_PUDDING }; - m2 = makemon(mkclass(classes[rn2(SIZE(classes))], 0), - juib->mx, juib->my, + m2 = makemon(mkclass(ROLL_FROM(classes), 0), juib->mx, juib->my, MM_ADJACENTOK | MM_ANGRY | MM_NOMSG); pline("Some ooze rolls off %s and becomes %s.", mon_nam(juib), a_monnam(m2)); @@ -2422,7 +2641,7 @@ special_juiblex_actions(struct monst *juib) /* once-per-move actions and effects for Baalzebub; return true if this has used * up his move and false if he should continue with his normal actions */ -static boolean +staticfn boolean special_baalzebub_actions(struct monst *baalz) { coordxy x, y; diff --git a/src/monst.c b/src/monst.c index 289d6b0a2b..4b7f57fb1f 100644 --- a/src/monst.c +++ b/src/monst.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 monst.c $NHDT-Date: 1682205027 2023/04/22 23:10:27 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.97 $ */ +/* NetHack 3.7 monst.c $NHDT-Date: 1705092160 2024/01/12 20:42:40 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.100 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -8,59 +8,61 @@ #include "wintype.h" #include "sym.h" -#ifdef C -#undef C -#endif -#ifdef TEXTCOLOR #include "color.h" -#define C(color) color -#else -#define C(color) -#endif -#define NO_ATTK \ - { \ - 0, 0, 0, 0 \ - } +extern const struct attack c_sa_yes[NATTK]; +extern const struct attack c_sa_no[NATTK]; + +#define NO_ATTK { 0, 0, 0, 0 } +/* monster type with single name */ #define MON(nam, sym, lvl, gen, atk, siz, mr1, mr2, \ flg1, flg2, flg3, d, col, bn) \ - { \ - { (const char *) 0, (const char *) 0, nam }, \ - sym, lvl, gen, atk, siz, mr1, mr2, flg1, flg2, flg3, d, C(col) \ + { \ + nam, PM_##bn, \ + sym, lvl, gen, atk, siz, mr1, mr2, flg1, flg2, flg3, d, col \ } -#define MON3(namm, namf, namn, sym, lvl, gen, atk, siz, mr1, mr2, \ - flg1, flg2, flg3, d, col, bn) \ - { \ - { namm, namf, namn }, \ - sym, lvl, gen, atk, siz, mr1, mr2, flg1, flg2, flg3, d, C(col) \ - } /* LVL() and SIZ() collect several fields to cut down on number of args - * for MON() + * for MON(). Using more than 15 would fail to conform to the C Standard. + * ATTK() and A() are to avoid braces and commas within args to MON(). + * NAM() and NAMS() are used for both reasons. */ +#define NAM(name) { (const char *) 0, (const char *) 0, name } +#define NAMS(namm, namf, namn) { namm, namf, namn } #define LVL(lvl, mov, ac, mr, aln) lvl, mov, ac, mr, aln #define SIZ(wt, nut, snd, siz) wt, nut, snd, siz -/* ATTK() and A() are to avoid braces and commas within args to MON() */ -#define ATTK(at, ad, n, d) \ - { \ - at, ad, n, d \ - } -#define A(a1, a2, a3, a4, a5, a6) \ - { \ - a1, a2, a3, a4, a5, a6 \ - } +#define ATTK(at, ad, n, d) { at, ad, n, d } +#define A(a1, a2, a3, a4, a5, a6) { a1, a2, a3, a4, a5, a6 } -struct permonst mons_init[NUMMONS + 1] = { +static struct permonst mons_init[NUMMONS + 1] = { #include "monsters.h" /* - * array terminator + * Array terminator, added to the end of the entries in monsters.h. + * + * mons[NUMMONS] used to be all zero except "" instead of Null for + * the name field. Then the index field was added and the terminator + * uses NON_PM for that. Now, a few monster flags also get set. */ - MON("", 0, LVL(0, 0, 0, 0, 0), (0), +#undef MON +#define MON(nam, sym, lvl, gen, atk, siz, mr1, mr2, \ + flg1, flg2, flg3, d, col, bn) \ + { \ + nam, NON_PM, \ + sym, lvl, gen, atk, siz, mr1, mr2, flg1, flg2, flg3, d, col \ + } + MON(NAM(""), 0, + LVL(0, 0, 0, 0, 0), G_NOGEN | G_NOCORPSE, A(NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), - SIZ(0, 0, 0, 0), 0, 0, 0L, 0L, 0, 0, 0, 0) + SIZ(0, 0, 0, 0), 0, 0, + 0L, M2_NOPOLY, 0, + 0, 0, 0), }; +#undef MON +#undef NAM +#undef NAMS + void monst_globals_init(void); /* in hack.h but we're using config.h */ struct permonst mons[SIZE(mons_init)]; @@ -77,11 +79,7 @@ const struct attack c_sa_no[NATTK] = SEDUCTION_ATTACKS_NO; /* for 'onefile' processing where end of this file isn't necessarily the end of the source code seen by the compiler */ -#undef C -#define C(c) (0x1f & (c)) /* global.h */ #undef NO_ATTK -#undef MON -#undef MON3 #undef LVL #undef SIZ #undef ATTK diff --git a/src/mplayer.c b/src/mplayer.c index 30f54adcf1..e87ecde75c 100644 --- a/src/mplayer.c +++ b/src/mplayer.c @@ -4,9 +4,9 @@ #include "hack.h" -static const char *dev_name(void); -static void get_mplname(struct monst *, char *); -static void mk_mplayer_armor(struct monst *, short); +staticfn const char *dev_name(void); +staticfn void get_mplname(struct monst *, char *); +staticfn void mk_mplayer_armor(struct monst *, short); /* These are the names of those who * contributed to the development of NetHack 3.2/3.3/3.4/3.6. @@ -40,12 +40,12 @@ static const char *const developers[] = { }; /* return a randomly chosen developer name */ -static const char * +staticfn const char * dev_name(void) { - register int i, m = 0, n = SIZE(developers); - register struct monst *mtmp; - register boolean match; + int i, m = 0, n = SIZE(developers); + struct monst *mtmp; + boolean match; do { match = FALSE; @@ -68,8 +68,8 @@ dev_name(void) return (developers[i]); } -static void -get_mplname(register struct monst* mtmp, char *nam) +staticfn void +get_mplname(struct monst* mtmp, char *nam) { boolean fmlkind = is_female(mtmp->data); const char *devnam; @@ -91,8 +91,8 @@ get_mplname(register struct monst* mtmp, char *nam) (boolean) mtmp->female)); } -static void -mk_mplayer_armor(struct monst* mon, short typ) +staticfn void +mk_mplayer_armor(struct monst *mon, short typ) { struct obj *obj; @@ -268,11 +268,12 @@ mk_mplayer(struct permonst *ptr, coordxy x, coordxy y, boolean special) else if (!rn2(2)) otmp->greased = 1; 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)) otmp->quan += (long) rn2(is_spear(otmp) ? 4 : 8); + otmp->owt = weight(otmp); /* mplayers knew better than to overenchant Magicbane */ if (is_art(otmp, ART_MAGICBANE)) otmp->spe = rnd(4); @@ -328,7 +329,7 @@ mk_mplayer(struct permonst *ptr, coordxy x, coordxy y, boolean special) * fill up the overflow. */ void -create_mplayers(register int num, boolean special) +create_mplayers(int num, boolean special) { int pm, x, y; struct monst fakemon; @@ -357,7 +358,7 @@ create_mplayers(register int num, boolean special) } void -mplayer_talk(register struct monst* mtmp) +mplayer_talk(struct monst *mtmp) { static const char *same_class_msg[3] = { diff --git a/src/mthrowu.c b/src/mthrowu.c index 2879676152..ce2b68a8be 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -1,16 +1,17 @@ -/* 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. */ #include "hack.h" -static int monmulti(struct monst *, struct obj *, struct obj *); -static void monshoot(struct monst *, struct obj *, struct obj *); -static boolean ucatchgem(struct obj *, struct monst *); -static const char* breathwep_name(int); -static int drop_throw(struct obj *, boolean, coordxy, coordxy); -static int m_lined_up(struct monst *, struct monst *); +staticfn int monmulti(struct monst *, struct obj *, struct obj *); +staticfn void monshoot(struct monst *, struct obj *, struct obj *); +staticfn boolean ucatchgem(struct obj *, struct monst *); +staticfn 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; #define URETREATING(x, y) \ (distmin(u.ux, u.uy, x, y) > distmin(u.ux0, u.uy0, x, y)) @@ -25,34 +26,35 @@ static NEARDATA const char *breathwep[] = { }; /* hallucinatory ray types */ -const char *const hallublasts[] = { +static const char *const hallublasts[] = { "asteroids", "beads", "bubbles", "butterflies", "champagne", "chaos", - "coins", "cotton candy", "crumbs", "dark matter", "darkness", "dust specks", - "emoticons", "emotions", "entropy", "flowers", "foam", "fog", "gamma rays", - "gelatin", "gemstones", "ghosts", "glass shards", "glitter", "good vibes", - "gravel", "gravity", "gravy", "grawlixes", "holy light", "hornets", - "hot air", "hyphens", "hypnosis", "infrared", "insects", "laser beams", - "leaves", "lightening", "logic gates", "magma", "marbles", "mathematics", - "megabytes", "metal shavings", "metapatterns", "meteors", "mist", "mud", - "music", "nanites", "needles", "noise", "nostalgia", "oil", "paint", - "photons", "pixels", "plasma", "polarity", "powder", "powerups", - "prismatic light", "pure logic", "purple", "radio waves", "rainbows", - "rock music", "rocket fuel", "rope", "sadness", "salt", "sand", "scrolls", - "sinewaves", "sludge", "smileys", "snowflakes", "sparkles", "specularity", - "spirits", "spores", "stars", "steam", "tetrahedrons", "text", "the past", - "tinsel", "tornadoes", "toxic waste", "ultraviolet light", "viruses", - "water", "waveforms", "wind", "X-rays", "zorkmids" + "coins", "cotton candy", "crumbs", "dark matter", "darkness", "data", + "dust specks", "emoticons", "emotions", "entropy", "flowers", "foam", "fog", + "gamma rays", "gelatin", "gemstones", "ghosts", "glass shards", "glitter", + "good vibes", "gravel", "gravity", "gravy", "grawlixes", "holy light", + "hornets", "hot air", "hyphens", "hypnosis", "infrared", "insects", + "jargon", "laser beams", "leaves", "lightening", "logic gates", "magma", + "marbles", "mathematics", "megabytes", "metal shavings", "metapatterns", + "meteors", "mist", "mud", "music", "nanites", "needles", "noise", + "nostalgia", "oil", "paint", "photons", "pixels", "plasma", "polarity", + "powder", "powerups", "prismatic light", "pure logic", "purple", + "radio waves", "rainbows", "rock music", "rocket fuel", "rope", "sadness", + "salt", "sand", "scrolls", "sinewaves", "sludge", "smileys", "snowflakes", + "sparkles", "specularity", "spirits", "spores", "stars", "steam", + "tetrahedrons", "text", "the past", "tinsel", "tornadoes", "toxic waste", + "ultraviolet light", "viruses", "water", "waveforms", "wind", "X-rays", + "zorkmids" }; /* Return a random hallucinatory blast. */ const char * rnd_hallublast(void) { - return hallublasts[rn2(SIZE(hallublasts))]; + return ROLL_FROM(hallublasts); } boolean -m_has_launcher_and_ammo(struct monst* mtmp) +m_has_launcher_and_ammo(struct monst *mtmp) { struct obj *mwep = MON_WEP(mtmp); @@ -106,7 +108,7 @@ thitu( if (u.uac + tlev <= (dieroll = rnd(20))) { ++gm.mesg_given; - if (Blind || !Verbose(2, thitu1)) { + if (Blind || !flags.verbose) { pline("It misses."); } else if (u.uac + tlev <= dieroll - 2) { if (onm != onmbuf) @@ -116,7 +118,7 @@ thitu( You("are almost hit by %s.", onm); return 0; } else { - if (Blind || !Verbose(2, thitu2)) + if (Blind || !flags.verbose) You("are hit%s", exclam(dam)); else You("are hit by %s%s", onm, exclam(dam)); @@ -124,7 +126,8 @@ thitu( if (is_acid && Acid_resistance) { pline("It doesn't seem to hurt you."); monstseesu(M_SEEN_ACID); - } else if (stone_missile(obj) && passes_rocks(gy.youmonst.data)) { + } else if (obj && stone_missile(obj) + && passes_rocks(gy.youmonst.data)) { /* use 'named' as an approximation for "hitting from above"; we avoid "passes through you" for horizontal flight path because missile stops and that wording would suggest that @@ -147,8 +150,10 @@ thitu( else if (obj && obj->owt >= 400 && uarm && is_brittle(uarm)) { break_glass_obj(uarm); } - if (is_acid) + if (is_acid) { pline("It burns!"); + monstunseesu(M_SEEN_ACID); + } losehp(dam, knm, kprefix); /* acid damage */ exercise(A_STR, FALSE); } @@ -158,62 +163,57 @@ thitu( /* Be sure this corresponds with what happens to player-thrown objects in * dothrow.c (for consistency). --KAA - * Returns 0 if object still exists (not destroyed). + * Returns FALSE if object still exists (not destroyed). */ -static int +staticfn boolean drop_throw( - register struct obj *obj, + struct obj *obj, boolean ohit, coordxy x, coordxy y) { - int retvalu = 1; - int create; - struct monst *mtmp; - struct trap *t; + boolean broken; if (obj->otyp == CREAM_PIE || obj->oclass == VENOM_CLASS - || (ohit && obj->otyp == EGG)) - create = 0; - else if (ohit && (is_multigen(obj) || obj->otyp == ROCK)) - create = !rn2(3); - else - create = 1; - - if (create && !((mtmp = m_at(x, y)) != 0 && mtmp->mtrapped - && (t = t_at(x, y)) != 0 - && is_pit(t->ttyp))) { - int objgone = 0; - - if (!IS_SOFT(levl[x][y].typ) && breaktest(obj)) { - breakmsg(obj, cansee(x, y)); - breakobj(obj, x, y, FALSE, FALSE); - objgone = 1; - } - if (!objgone && down_gate(x, y) != -1) - objgone = ship_object(obj, x, y, FALSE); - if (!objgone) { - if (!flooreffects(obj, x, y, "fall")) { + || (ohit && obj->otyp == EGG)) { + broken = TRUE; + } else { + broken = (ohit && should_mulch_missile(obj)); + } + + /* glass and other fragile objects */ + if (!IS_SOFT(levl[x][y].typ) && breaktest(obj)) { + breakmsg(obj, cansee(x, y)); + breakobj(obj, x, y, FALSE, FALSE); + return TRUE; /* it's already deleted */ + } + if (broken) { + delobj(obj); + } else { + if (down_gate(x, y) != -1) + broken = ship_object(obj, x, y, FALSE); + if (!broken) { + struct monst *mtmp = m_at(x, y); + if (!(broken = flooreffects(obj, x, y, "fall"))) { place_object(obj, x, y); if (!mtmp && u_at(x, y)) mtmp = &gy.youmonst; if (mtmp && ohit) passive_obj(mtmp, obj, (struct attack *) 0); stackobj(obj); - retvalu = 0; } } - } else { - delobj(obj); } gt.thrownobj = 0; - return retvalu; + return broken; } /* calculate multishot volley count for mtmp throwing otmp (if not ammo) or shooting otmp with mwep (if otmp is ammo and mwep appropriate launcher) */ -static int -monmulti(struct monst* mtmp, struct obj* otmp, struct obj* mwep) +staticfn int +monmulti( + struct monst *mtmp, + struct obj *otmp, struct obj *mwep) { int multishot = 1; @@ -255,11 +255,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++; } @@ -271,8 +271,8 @@ monmulti(struct monst* mtmp, struct obj* otmp, struct obj* mwep) } /* mtmp throws otmp, or shoots otmp with mwep, at hero or at monster mtarg */ -static void -monshoot(struct monst* mtmp, struct obj* otmp, struct obj* mwep) +staticfn void +monshoot(struct monst *mtmp, struct obj *otmp, struct obj *mwep) { struct monst *mtarg = gm.mtarget; int dm = distmin(mtmp->mx, mtmp->my, @@ -300,6 +300,7 @@ monshoot(struct monst* mtmp, struct obj* otmp, struct obj* mwep) } gm.m_shot.s = ammo_and_launcher(otmp, mwep) ? TRUE : FALSE; Strcpy(trgbuf, mtarg ? some_mon_nam(mtarg) : ""); + set_msg_xy(mtmp->mx, mtmp->my); pline("%s %s %s%s%s!", Monnam(mtmp), gm.m_shot.s ? "shoots" : "throws", onm, mtarg ? " at " : "", trgbuf); @@ -329,20 +330,20 @@ monshoot(struct monst* mtmp, struct obj* otmp, struct obj* mwep) 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) */ -int +boolean ohitmon( struct monst *mtmp, /* accidental target, located at */ struct obj *otmp, /* missile; might be destroyed by drop_throw */ 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 (used for rolling_boulder_traps) */ - boolean verbose)/* give message(s) even when you can't see what happened */ + * unless it's gone (for rolling_boulder_traps) */ + boolean verbose)/* give messages even when you can't see what happened */ { int damage, tmp; - boolean vis; - int objgone = 1; + boolean vis, objgone; struct obj *mon_launcher = gm.marcher ? MON_WEP(gm.marcher) : NULL; + /* assert(otmp != NULL); */ gn.notonhead = (gb.bhitpos.x != mtmp->mx || gb.bhitpos.y != mtmp->my); vis = cansee(gb.bhitpos.x, gb.bhitpos.y); if (vis) @@ -448,7 +449,7 @@ ohitmon( (nonliving(mtmp->data) || is_vampshifter(mtmp) || !canspotmon(mtmp)) ? "destroyed" : "killed"); /* don't blame hero for unknown rolling boulder trap */ - if (!gc.context.mon_moving && (otmp->otyp != BOULDER + if (!svc.context.mon_moving && (otmp->otyp != BOULDER || range >= 0 || otmp->otrapped)) xkilled(mtmp, XKILL_NOMSG); else @@ -479,21 +480,21 @@ ohitmon( mtmp->mblinded = tmp; } - if (!DEADMONSTER(mtmp) && !gc.context.mon_moving) + if (!DEADMONSTER(mtmp) && !svc.context.mon_moving) setmangry(mtmp, TRUE); objgone = drop_throw(otmp, 1, gb.bhitpos.x, gb.bhitpos.y); if (!objgone && range == -1) { /* special case */ obj_extract_self(otmp); /* free it for motion again */ - return 0; + return FALSE; } - return 1; + return TRUE; } - return 0; + return FALSE; } /* hero catches gem thrown by mon iff poly'd into unicorn; might drop it */ -static boolean +staticfn boolean ucatchgem( struct obj *gem, /* caller has verified gem->oclass */ struct monst *mon) @@ -523,7 +524,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 */ \ @@ -586,7 +587,7 @@ m_throw( clear_dknown(singleobj); /* singleobj->dknown = 0; */ if ((singleobj->cursed || singleobj->greased) && (dx || dy) && !rn2(7)) { - if (canseemon(mon) && Verbose(2, m_throw)) { + if (canseemon(mon) && flags.verbose) { if (is_ammo(singleobj)) pline("%s misfires!", Monnam(mon)); else @@ -655,6 +656,7 @@ m_throw( hitu = 0; break; } + FALLTHROUGH; /*FALLTHRU*/ case CREAM_PIE: case BLINDING_VENOM: @@ -776,10 +778,10 @@ m_throw( /* Monster throws item at another monster */ int -thrwmm(struct monst* mtmp, struct monst* mtarg) +thrwmm(struct monst *mtmp, struct monst *mtarg) { struct obj *otmp, *mwep; - register coordxy x, y; + coordxy x, y; boolean ispole; /* Polearms won't be applied by monsters against other monsters */ @@ -823,7 +825,7 @@ thrwmm(struct monst* mtmp, struct monst* mtarg) /* monster spits substance at monster */ int -spitmm(struct monst* mtmp, struct attack* mattk, struct monst* mtarg) +spitmm(struct monst *mtmp, struct attack *mattk, struct monst *mtarg) { struct obj *otmp; @@ -851,6 +853,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); @@ -863,7 +866,7 @@ spitmm(struct monst* mtmp, struct attack* mattk, struct monst* mtarg) gm.mtarget = mtarg; m_throw(mtmp, mtmp->mx, mtmp->my, sgn(gt.tbx), sgn(gt.tby), distmin(mtmp->mx,mtmp->my,tx,ty), otmp); - gm.mtarget = (struct monst *)0; + gm.mtarget = (struct monst *) 0; nomul(0); /* If this is a pet, it'll get hungry. Minions and @@ -888,7 +891,7 @@ spitmm(struct monst* mtmp, struct attack* mattk, struct monst* mtarg) /* Return the name of a breath weapon. If the player is hallucinating, return * a silly name instead. * typ is AD_MAGM, AD_FIRE, etc */ -static const char * +staticfn const char * breathwep_name(int typ) { if (Hallucination) @@ -899,9 +902,10 @@ breathwep_name(int typ) /* monster breathes at monster (ranged) */ int -breamm(struct monst* mtmp, struct attack* mattk, struct monst* mtarg) +breamm(struct monst *mtmp, struct attack *mattk, struct monst *mtarg) { int typ = get_atkdam_type(mattk->adtyp); + boolean utarget = (mtarg == &gy.youmonst); if (m_lined_up(mtarg, mtmp)) { if (mtmp->mcan) { @@ -918,14 +922,12 @@ breamm(struct monst* mtmp, struct attack* mattk, struct monst* mtarg) /* if we've seen the actual resistance, don't bother, or if we're close by and they reflect, just jump the player */ - if (m_seenres(mtmp, cvt_adtyp_to_mseenres(typ)) - || (m_seenres(mtmp, M_SEEN_REFL) - && monnear(mtmp, mtmp->mux, mtmp->muy))) + if (utarget && (m_seenres(mtmp, cvt_adtyp_to_mseenres(typ)) + || m_seenres(mtmp, M_SEEN_REFL))) return M_ATTK_HIT; if (!mtmp->mspec_used && rn2(3)) { if (BZ_VALID_ADTYP(typ)) { - boolean utarget = (mtarg == &gy.youmonst); if (canseemon(mtmp)) pline("%s breathes %s!", Monnam(mtmp), breathwep_name(typ)); @@ -960,7 +962,7 @@ breamm(struct monst* mtmp, struct attack* mattk, struct monst* mtarg) /* remove an entire item from a monster's inventory; destroy that item */ void -m_useupall(struct monst* mon, struct obj* obj) +m_useupall(struct monst *mon, struct obj *obj) { extract_from_minvent(mon, obj, TRUE, FALSE); obfree(obj, (struct obj *) 0); @@ -968,7 +970,7 @@ m_useupall(struct monst* mon, struct obj* obj) /* remove one instance of an item from a monster's inventory */ void -m_useup(struct monst* mon, struct obj* obj) +m_useup(struct monst *mon, struct obj *obj) { if (obj->quan > 1L) { obj->quan--; @@ -978,10 +980,9 @@ m_useup(struct monst* mon, struct obj* obj) } } -/* monster attempts ranged weapon attack against player - * returns TRUE if it did something; FALSE if it decided not to attack */ +/* monster attempts ranged weapon attack against player */ boolean -thrwmu(struct monst* mtmp) +thrwmu(struct monst *mtmp) { struct obj *otmp, *mwep; coordxy x, y; @@ -1025,7 +1026,7 @@ thrwmu(struct monst* mtmp) if (canseemon(mtmp)) { onm = xname(otmp); - pline("%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)); @@ -1066,18 +1067,28 @@ thrwmu(struct monst* mtmp) /* monster spits substance at you */ int -spitmu(struct monst* mtmp, struct attack* mattk) +spitmu(struct monst *mtmp, struct attack *mattk) { return spitmm(mtmp, mattk, &gy.youmonst); } /* monster breathes at you (ranged) */ int -breamu(struct monst* mtmp, struct attack* mattk) +breamu(struct monst *mtmp, struct attack *mattk) { return breamm(mtmp, mattk, &gy.youmonst); } +/* return TRUE if terrain at x,y blocks linedup checks */ +staticfn boolean +blocking_terrain(coordxy x, coordxy 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; +} + /* Move from (ax,ay) to (bx,by), but only if distance is up to BOLT_LIM and only in straight line or diagonal, calling fnc for each step. Stops if fnc return TRUE, or if step was blocked by wall or closed door. @@ -1108,10 +1119,7 @@ linedup_callback( do { /* is guaranteed to eventually converge with */ bx += dx, by += dy; - if (!isok(bx, by)) - return FALSE; - if (IS_ROCK(levl[bx][by].typ) || closed_door(bx, by) - || is_waterwall(bx, by)) + if (blocking_terrain(bx, by)) return FALSE; if ((*fnc)(bx, by)) return TRUE; @@ -1122,10 +1130,10 @@ linedup_callback( boolean linedup( - register coordxy ax, - register coordxy ay, - register coordxy bx, - register coordxy by, + coordxy ax, + coordxy ay, + coordxy bx, + coordxy by, int boulderhandling) /* 0=block, 1=ignore, 2=conditionally block */ { int dx, dy, boulderspots; @@ -1154,8 +1162,7 @@ linedup( do { /* is guaranteed to eventually converge with */ bx += dx, by += dy; - if (IS_ROCK(levl[bx][by].typ) || closed_door(bx, by) - || is_waterwall(bx, by)) + if (blocking_terrain(bx, by)) return FALSE; if (sobj_at(BOULDER, bx, by)) ++boulderspots; @@ -1167,8 +1174,8 @@ linedup( return FALSE; } -static int -m_lined_up(struct monst* mtarg, struct monst* mtmp) +staticfn int +m_lined_up(struct monst *mtarg, struct monst *mtmp) { boolean utarget = (mtarg == &gy.youmonst); coordxy tx = utarget ? mtmp->mux : mtarg->mx; @@ -1190,7 +1197,7 @@ m_lined_up(struct monst* mtarg, struct monst* mtmp) /* is mtmp in position to use ranged attack on hero? */ boolean -lined_up(register struct monst* mtmp) +lined_up(struct monst *mtmp) { return m_lined_up(&gy.youmonst, mtmp) ? TRUE : FALSE; } @@ -1199,7 +1206,7 @@ lined_up(register struct monst* mtmp) struct obj * m_carrying(struct monst *mtmp, int type) { - register struct obj *otmp; + struct obj *otmp; for (otmp = (mtmp == &gy.youmonst) ? gi.invent : mtmp->minvent; otmp; otmp = otmp->nobj) diff --git a/src/muse.c b/src/muse.c index bed6f7632e..8d1f82efcd 100644 --- a/src/muse.c +++ b/src/muse.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 muse.c $NHDT-Date: 1654972707 2022/06/11 18:38:27 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.164 $ */ +/* 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. */ @@ -14,41 +14,47 @@ * are confused don't know not to read scrolls, etc.... */ -static int precheck(struct monst *, struct obj *); -static void mzapwand(struct monst *, struct obj *, boolean); -static void mplayhorn(struct monst *, struct obj *, boolean); -static void mreadmsg(struct monst *, struct obj *); -static void mquaffmsg(struct monst *, struct obj *); -static boolean m_use_healing(struct monst *); -static boolean m_sees_sleepy_soldier(struct monst *); -static boolean linedup_chk_corpse(coordxy, coordxy); -static void m_use_undead_turning(struct monst *, struct obj *); -static boolean hero_behind_chokepoint(struct monst *); -static boolean mon_has_friends(struct monst *); -static int mbhitm(struct monst *, struct obj *); -static void mbhit(struct monst *, int, int (*)(MONST_P, OBJ_P), +staticfn int precheck(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); +staticfn boolean m_next2m(struct monst *); +staticfn void reveal_trap(struct trap *, boolean); +staticfn int mon_escape(struct monst *, boolean); +staticfn boolean linedup_chk_corpse(coordxy, coordxy); +staticfn void m_use_undead_turning(struct monst *, struct obj *); +staticfn boolean hero_behind_chokepoint(struct monst *); +staticfn boolean mon_has_friends(struct monst *); +staticfn int mbhitm(struct monst *, struct obj *); +staticfn boolean fhito_loc(struct obj *obj, coordxy x, coordxy y, + int (*fhito)(OBJ_P, OBJ_P)); +staticfn void mbhit(struct monst *, int, int (*)(MONST_P, OBJ_P), int (*)(OBJ_P, OBJ_P), struct obj *); -static struct permonst *muse_newcham_mon(struct monst *); -static int mloot_container(struct monst *mon, struct obj *, boolean); -static void you_aggravate(struct monst *); +staticfn struct permonst *muse_newcham_mon(struct monst *); +staticfn int mloot_container(struct monst *mon, struct obj *, boolean); +staticfn void you_aggravate(struct monst *); #if 0 -static boolean necrophiliac(struct obj *, boolean); +staticfn boolean necrophiliac(struct obj *, boolean); #endif -static void mon_consume_unstone(struct monst *, struct obj *, boolean, +staticfn void mon_consume_unstone(struct monst *, struct obj *, boolean, boolean); -static boolean cures_stoning(struct monst *, struct obj *, boolean); -static boolean mcould_eat_tin(struct monst *); -static boolean muse_unslime(struct monst *, struct obj *, struct trap *, +staticfn boolean cures_stoning(struct monst *, struct obj *, boolean); +staticfn boolean mcould_eat_tin(struct monst *); +staticfn boolean muse_unslime(struct monst *, struct obj *, struct trap *, boolean); -static int cures_sliming(struct monst *, struct obj *); -static boolean green_mon(struct monst *); -static boolean wont_leave_level(struct monst *); +staticfn int cures_sliming(struct monst *, struct obj *); +staticfn boolean green_mon(struct monst *); +staticfn boolean wont_leave_level(struct monst *); /* Any preliminary checks which may result in the monster being unable to use * the item. Returns 0 if nothing happened, 2 if the monster can't do * anything (i.e. it teleported) and 1 if it's dead. */ -static int +staticfn int precheck(struct monst *mon, struct obj *obj) { boolean vis; @@ -63,8 +69,8 @@ precheck(struct monst *mon, struct obj *obj) struct monst *mtmp; if (objdescr_is(obj, "milky")) { - if (!(gm.mvitals[PM_GHOST].mvflags & G_GONE) - && !rn2(POTION_OCCUPANT_CHANCE(gm.mvitals[PM_GHOST].born))) { + if (!(svm.mvitals[PM_GHOST].mvflags & G_GONE) + && !rn2(POTION_OCCUPANT_CHANCE(svm.mvitals[PM_GHOST].born))) { if (!enexto(&cc, mon->mx, mon->my, &mons[PM_GHOST])) return 0; mquaffmsg(mon, obj); @@ -80,7 +86,8 @@ precheck(struct monst *mon, struct obj *obj) mon_nam(mon), Hallucination ? rndmonnam(NULL) : (const char *) "ghost"); - pline("%s is frightened to death, and unable to move.", + pline("%s is frightened to death," + " and unable to move.", Monnam(mon)); } paralyze_monst(mon, 3); @@ -89,8 +96,8 @@ precheck(struct monst *mon, struct obj *obj) } } if (objdescr_is(obj, "smoky") - && !(gm.mvitals[PM_DJINNI].mvflags & G_GONE) - && !rn2(POTION_OCCUPANT_CHANCE(gm.mvitals[PM_DJINNI].born))) { + && !(svm.mvitals[PM_DJINNI].mvflags & G_GONE) + && !rn2(POTION_OCCUPANT_CHANCE(svm.mvitals[PM_DJINNI].born))) { if (!enexto(&cc, mon->mx, mon->my, &mons[PM_DJINNI])) return 0; mquaffmsg(mon, obj); @@ -101,7 +108,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.... */ @@ -127,7 +134,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() */ @@ -153,7 +160,7 @@ precheck(struct monst *mon, struct obj *obj) /* when a monster zaps a wand give a message, deduct a charge, and if it isn't directly seen, remove hero's memory of the number of charges */ -static void +staticfn void mzapwand( struct monst *mtmp, struct obj *otmp, @@ -176,14 +183,14 @@ 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; } /* similar to mzapwand() but for magical horns (only instrument mons play) */ -static void +staticfn void mplayhorn( struct monst *mtmp, struct obj *otmp, @@ -226,11 +233,12 @@ mplayhorn( /* see or hear a monster reading a scroll; when scroll hasn't been seen, its label is revealed unless hero is deaf */ -static void +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 */ @@ -240,7 +248,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), @@ -256,11 +264,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", @@ -268,18 +280,20 @@ 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...", vismon ? mon_nam(mtmp) : mhe(mtmp)); } -static void +staticfn void 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(Hallucination ? "a steam locomotive passing by." @@ -321,7 +335,7 @@ mquaffmsg(struct monst *mtmp, struct obj *otmp) * that if you polymorph into one you teleport at will. */ -static boolean +staticfn boolean m_use_healing(struct monst *mtmp) { struct obj *obj; @@ -345,7 +359,7 @@ m_use_healing(struct monst *mtmp) } /* return TRUE if monster mtmp can see at least one sleeping soldier */ -static boolean +staticfn boolean m_sees_sleepy_soldier(struct monst *mtmp) { coordxy x = mtmp->mx, y = mtmp->my; @@ -368,6 +382,61 @@ m_sees_sleepy_soldier(struct monst *mtmp) return FALSE; } +staticfn void +m_tele( + struct monst *mtmp, /* monst that might be teleported */ + boolean vismon, /* can see it */ + boolean oseen, /* have seen the object that triggered this */ + int how) /* type of that object */ +{ + if (tele_restrict(mtmp)) { /* mysterious force... */ + if (vismon && how) /* mentions 'teleport' */ + makeknown(how); + /* monster learns that teleportation isn't useful here */ + if (noteleport_level(mtmp)) + mon_learns_traps(mtmp, TELEP_TRAP); + } else if ((mon_has_amulet(mtmp) || On_W_tower_level(&u.uz)) && !rn2(3)) { + if (vismon) + pline_mon(mtmp, "%s seems disoriented for a moment.", Monnam(mtmp)); + } else { + /* teleport monster 'mtmp' */ + if (how) { + /* teleporation has been triggered by an object */ + if (oseen) + makeknown(how); + (void) rloc(mtmp, RLOC_MSG); + } else { + /* monster is voluntarily entering a teleporation trap; use the + trap instead of rloc() in case it sends 'victim' to a vault */ + mtmp->mx = gt.trapx, mtmp->my = gt.trapy; + (void) mintrap(mtmp, FORCETRAP); + } + } +} + +/* return TRUE if monster mtmp has another monster next to it. + * Called from find_defensive() where it is limited to Is_knox() + * only, otherwise you could trap two monsters next to each other + * in a boulder fort, and they would be happy to stay in there. */ +staticfn boolean +m_next2m(struct monst *mtmp) +{ + coordxy x, y; + struct monst *m2; + + if (DEADMONSTER(mtmp) || mon_offmap(mtmp)) + return FALSE; + + for (x = mtmp->mx - 1; x <= mtmp->mx + 1; x++) + for (y = mtmp->my - 1; y <= mtmp->my + 1; y++) { + if (!isok(x,y)) + continue; + if ((m2 = m_at(x, y)) && m2 != mtmp) + return TRUE; + } + return FALSE; +} + /* Select a defensive item/action for a monster. Returns TRUE iff one is found. */ boolean @@ -388,6 +457,9 @@ find_defensive(struct monst *mtmp, boolean tryescape) return FALSE; if (!tryescape && dist2(x, y, mtmp->mux, mtmp->muy) > 25) return FALSE; + if (tryescape && Is_knox(&u.uz) + && !m_next2u(mtmp) && m_next2m(mtmp)) + return FALSE; if (u.uswallow && stuck) return FALSE; @@ -540,7 +612,7 @@ find_defensive(struct monst *mtmp, boolean tryescape) or some other monster is there */ if (u_at(xx, yy) || (xx != x && yy != y && !diag_ok) - || (gl.level.monsters[xx][yy] && !(xx == x && yy == y))) + || (svl.level.monsters[xx][yy] && !(xx == x && yy == y))) continue; /* skip if there's no trap or can't/won't move onto trap */ if ((t = t_at(xx, yy)) == 0 @@ -603,8 +675,7 @@ find_defensive(struct monst *mtmp, boolean tryescape) && !(levl[x][y].wall_info & W_NONDIGGABLE) && !(Is_botlevel(&u.uz) || In_endgame(&u.uz)) && !(is_ice(x, y) || is_pool(x, y) || is_lava(x, y)) - && !(mtmp->data == &mons[PM_VLAD_THE_IMPALER] - && In_V_tower(&u.uz))) { + && !(is_Vlad(mtmp) && In_V_tower(&u.uz))) { gm.m.defensive = obj; gm.m.has_defense = MUSE_WAN_DIGGING; } @@ -701,6 +772,49 @@ find_defensive(struct monst *mtmp, boolean tryescape) #undef nomore } +/* when a monster deliberately enters a trap, make sure the spot becomes + accessible (trap doors and teleporters inside niches are located at + secret corridor locations; convert such into normal corridor even if + hero doesn't see it happen) */ +staticfn void +reveal_trap(struct trap *t, boolean seeit) +{ + struct rm *lev = &levl[t->tx][t->ty]; + + if (lev->typ == SCORR) { + lev->typ = CORR, lev->flags = 0; /* set_levltyp(,,CORR) */ + unblock_point(t->tx, t->ty); + } + if (seeit) + seetrap(t); +} + +/* Monsters without the Amulet escape the dungeon and + * are gone for good when they leave up the up stairs. + * A monster with the Amulet would leave it behind + * (mongone -> mdrop_special_objs) but we force any + * monster who manages to acquire it or the invocation + * tools to stick around instead of letting it escape. + * Don't let the Wizard escape even when not carrying + * anything of interest unless there are more than 1 + * of him. + */ +staticfn int +mon_escape(struct monst *mtmp, boolean vismon) +{ + if (mon_has_special(mtmp) + || (mtmp->iswiz && svc.context.no_of_wizards < 2)) + return 0; + if (vismon) + pline_mon(mtmp, "%s escapes the dungeon!", Monnam(mtmp)); + mongone(mtmp); + if (is_archfiend(mtmp->data)) { + struct fiend_info *fiend = lookup_fiend(monsndx(mtmp->data)); + fiend->escaped = TRUE; + } + return 2; +} + /* Perform a defensive action for a monster. Must be called immediately * after find_defensive(). Return values are 0: did something, 1: died, * 2: did something and can't attack again (i.e. teleported). @@ -708,10 +822,11 @@ find_defensive(struct monst *mtmp, boolean tryescape) int use_defensive(struct monst *mtmp) { - int i, fleetim, how = 0; + static const char MissingDefensiveItem[] = "use_defensive: no %s"; + int i, fleetim; struct obj *otmp = gm.m.defensive; boolean vis, vismon, oseen; - const char *Mnam; + struct trap *t; stairway *stway; if ((i = precheck(mtmp, otmp)) != 0) @@ -730,9 +845,11 @@ use_defensive(struct monst *mtmp) switch (gm.m.has_defense) { case MUSE_UNICORN_HORN: + if (!otmp) + panic(MissingDefensiveItem, "unicorn horn"); 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)); } @@ -741,13 +858,15 @@ 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)); + 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!"); @@ -755,30 +874,17 @@ 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); mzapwand(mtmp, otmp, TRUE); - how = WAN_TELEPORTATION; - mon_tele: - if (tele_restrict(mtmp)) { /* mysterious force... */ - if (vismon && how) /* mentions 'teleport' */ - makeknown(how); - /* monster learns that teleportation isn't useful here */ - if (noteleport_level(mtmp)) - mon_learns_traps(mtmp, TELEP_TRAP); - return 2; - } - if (mon_has_amulet(mtmp) || On_W_tower_level(&u.uz)) { - if (vismon) - pline("%s seems disoriented for a moment.", Monnam(mtmp)); - return 2; - } - if (oseen && how) - makeknown(how); - (void) rloc(mtmp, RLOC_MSG); + 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; @@ -789,42 +895,55 @@ 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); - mreadmsg(mtmp, otmp); - m_useup(mtmp, otmp); /* otmp might be free'ed */ - how = SCR_TELEPORTATION; + /* we want to be able to access otmp after the teleport but it + might get destroyed if still in mtmp's inventory (maybe mtmp + lands in lava or on a fire trap) so take it out in advance */ + if (otmp->quan > 1L) + otmp = splitobj(otmp, 1L); + extract_from_minvent(mtmp, otmp, FALSE, FALSE); + /* 'last_msg' will be changed to PLNMSG_UNKNOWN if any messages + are issued by mreadmsg(), 'if (vismon) pline()', or m_tele() */ + iflags.last_msg = PLNMSG_enum; + mreadmsg(mtmp, otmp); /* sets otmp->dknown if !Blind or !Deaf */ if (obj_is_cursed || mtmp->mconf) { int nlev; d_level flev; + 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)); - return 2; - } - nlev = random_teleport_level(); - if (nlev == depth(&u.uz)) { + } else if (nlev == depth(&u.uz)) { if (vismon) - pline("%s shudders for a moment.", Monnam(mtmp)); - return 2; + pline_mon(mtmp, "%s shudders for a moment.", Monnam(mtmp)); + } else { + get_level(&flev, nlev); + migrate_to_level(mtmp, ledger_no(&flev), MIGR_RANDOM, + (coord *) 0); } - get_level(&flev, nlev); - migrate_to_level(mtmp, ledger_no(&flev), MIGR_RANDOM, - (coord *) 0); - if (oseen) - makeknown(SCR_TELEPORTATION); - } else - goto mon_tele; + } else { + m_tele(mtmp, vismon, oseen, SCR_TELEPORTATION); + } + /* m_tele() handles makeknown(); trycall() will be a no-op when + otmp->otyp is already discovered */ + if (otmp->dknown && iflags.last_msg != PLNMSG_enum) + trycall(otmp); + /* already removed from mtmp->minvent so not 'm_useup(mtmp, otmp)' */ + obfree(otmp, (struct obj *) 0); return 2; } - case MUSE_WAN_DIGGING: { - struct trap *ttmp; - + case MUSE_WAN_DIGGING: + if (!otmp) + panic(MissingDefensiveItem, "wand of digging"); m_flee(mtmp); mzapwand(mtmp, otmp, FALSE); if (oseen) @@ -840,7 +959,7 @@ use_defensive(struct monst *mtmp) /* can't dig further if there's already a pit (or other trap) here, or if pit creation fails for some reason */ if (t_at(mtmp->mx, mtmp->my) - || !(ttmp = maketrap(mtmp->mx, mtmp->my, PIT))) { + || !(t = maketrap(mtmp->mx, mtmp->my, PIT))) { if (vismon) { pline_The("%s here is too hard to dig in.", surface(mtmp->mx, mtmp->my)); @@ -849,32 +968,37 @@ use_defensive(struct monst *mtmp) } /* pit creation succeeded */ if (vis) { - seetrap(ttmp); - pline("%s has made a pit in the %s.", Monnam(mtmp), + seetrap(t); + 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; } - ttmp = maketrap(mtmp->mx, mtmp->my, HOLE); - if (!ttmp) + t = maketrap(mtmp->mx, mtmp->my, HOLE); + if (!t) return 2; - seetrap(ttmp); + 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); You_hear("%s crash through the %s.", something, surface(mtmp->mx, mtmp->my)); } + fill_pit(mtmp->mx, mtmp->my); /* we made sure that there is a level for mtmp to go to */ migrate_to_level(mtmp, ledger_no(&u.uz) + 1, MIGR_RANDOM, (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; @@ -888,6 +1012,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); @@ -897,6 +1023,8 @@ use_defensive(struct monst *mtmp) return 2; } case MUSE_SCR_CREATE_MONSTER: + if (!otmp) + panic(MissingDefensiveItem, "scroll of create monster"); /* FIXME: This logic should really be merged with the identical formula * in read.c. */ mreadmsg(mtmp, otmp); @@ -922,19 +1050,14 @@ use_defensive(struct monst *mtmp) if (Is_botlevel(&u.uz)) return 0; m_flee(mtmp); + t = t_at(gt.trapx, gt.trapy); if (vis) { - struct trap *t = t_at(gt.trapx, gt.trapy); - - Mnam = Monnam(mtmp); - pline("%s %s into a %s!", Mnam, + pline_mon(mtmp, "%s %s into a %s!", Monnam(mtmp), vtense(fakename[0], locomotion(mtmp->data, "jump")), - (t->ttyp == TRAPDOOR) ? "trap door" : "hole"); - if (levl[gt.trapx][gt.trapy].typ == SCORR) { - levl[gt.trapx][gt.trapy].typ = CORR; - unblock_point(gt.trapx, gt.trapy); - } - seetrap(t_at(gt.trapx, gt.trapy)); + trapname(t->ttyp, FALSE)); } + /* if trap was in a concealed niche, it's no longer concealed */ + reveal_trap(t, vis); /* don't use rloc_to() because worm tails must "move" */ remove_monster(mtmp->mx, mtmp->my); @@ -953,7 +1076,8 @@ use_defensive(struct monst *mtmp) if (!stway) return 0; if (ledger_no(&u.uz) == 1) - goto escape; /* impossible; level 1 upstairs are SSTAIRS */ + /* impossible; level 1 upstairs are SSTAIRS */ + return mon_escape(mtmp, vismon); if (vismon) pline("%s escapes upstairs!", Monnam(mtmp)); migrate_to_level(mtmp, ledger_no(&(stway->tolev)), MIGR_STAIRS_DOWN, @@ -965,7 +1089,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; @@ -975,7 +1099,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; @@ -985,7 +1109,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; @@ -995,31 +1119,10 @@ use_defensive(struct monst *mtmp) if (!stway) return 0; if (ledger_no(&u.uz) == 1) { - escape: - /* Monsters without the Amulet escape the dungeon and - * are gone for good when they leave up the up stairs. - * A monster with the Amulet would leave it behind - * (mongone -> mdrop_special_objs) but we force any - * monster who manages to acquire it or the invocation - * tools to stick around instead of letting it escape. - * Don't let the Wizard escape even when not carrying - * anything of interest unless there are more than 1 - * of him. - */ - if (mon_has_special(mtmp) - || (mtmp->iswiz && gc.context.no_of_wizards < 2)) - return 0; - if (vismon) - pline("%s escapes the dungeon!", Monnam(mtmp)); - mongone(mtmp); - if (is_archfiend(mtmp->data)) { - struct fiend_info *fiend = lookup_fiend(monsndx(mtmp->data)); - fiend->escaped = TRUE; - } - return 2; + 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 @@ -1030,63 +1133,71 @@ use_defensive(struct monst *mtmp) return 2; case MUSE_TELEPORT_TRAP: m_flee(mtmp); + t = t_at(gt.trapx, gt.trapy); if (vis) { - Mnam = Monnam(mtmp); - pline("%s %s onto a teleport trap!", Mnam, - vtense(fakename[0], locomotion(mtmp->data, "jump"))); - seetrap(t_at(gt.trapx, gt.trapy)); + pline_mon(mtmp, "%s %s onto a %s!", Monnam(mtmp), + vtense(fakename[0], locomotion(mtmp->data, "jump")), + trapname(t->ttyp, FALSE)); } + /* if trap was in a concealed niche, it's no longer concealed */ + reveal_trap(t, vis); /* don't use rloc_to() because worm tails must "move" */ remove_monster(mtmp->mx, mtmp->my); newsym(mtmp->mx, mtmp->my); /* update old location */ 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); - - goto mon_tele; + /* 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; @@ -1119,6 +1230,7 @@ rnd_defensive_item(struct monst *mtmp) goto try_again; if (!rn2(3)) return WAN_TELEPORTATION; + FALLTHROUGH; /*FALLTHRU*/ case 0: case 1: @@ -1127,6 +1239,7 @@ rnd_defensive_item(struct monst *mtmp) case 10: if (!rn2(3)) return WAN_CREATE_MONSTER; + FALLTHROUGH; /*FALLTHRU*/ case 2: return SCR_CREATE_MONSTER; @@ -1173,13 +1286,13 @@ rnd_defensive_item(struct monst *mtmp) /*#define MUSE_WAN_UNDEAD_TURNING 20*/ /* also a defensive item so don't * redefine; nonconsecutive value is ok */ -static boolean +staticfn boolean linedup_chk_corpse(coordxy x, coordxy y) { return (sobj_at(CORPSE, x, y) != 0); } -static void +staticfn void m_use_undead_turning(struct monst *mtmp, struct obj *obj) { coordxy ax = u.ux + sgn(mtmp->mux - mtmp->mx) * 3, @@ -1195,9 +1308,9 @@ m_use_undead_turning(struct monst *mtmp, struct obj *obj) we don't check whether hero is poly'd into an undead--the wand's turning effect is too weak to be a useful direct attack--only whether hero is carrying at least one corpse */ - if (carrying(CORPSE)) { + if (carrying(CORPSE) /* - * Hero is carrying one or more corpses but isn't wielding + * If hero is carrying one or more corpses but isn't wielding * a cockatrice corpse (unless being hit by one won't do * the monster much harm); otherwise we'd be using this wand * as a defensive item with higher priority. @@ -1213,19 +1326,17 @@ m_use_undead_turning(struct monst *mtmp, struct obj *obj) * dropped; player might not choose to spend a wand charge * on that when/if hero acquires this wand). */ - gm.m.offensive = obj; - gm.m.has_offense = MUSE_WAN_UNDEAD_TURNING; - } else if (linedup_callback(ax, ay, bx, by, linedup_chk_corpse)) { - /* There's a corpse on the ground in a direct line from the - * monster to the hero, and up to 3 steps beyond. - */ + || linedup_callback(ax, ay, bx, by, linedup_chk_corpse) + /* or there's a corpse on the ground in a direct line from the + monster to the hero, and up to 3 steps beyond. */ + ) { gm.m.offensive = obj; gm.m.has_offense = MUSE_WAN_UNDEAD_TURNING; } } /* from monster's point of view, is hero behind a chokepoint? */ -static boolean +staticfn boolean hero_behind_chokepoint(struct monst *mtmp) { coordxy dx = sgn(mtmp->mx - mtmp->mux); @@ -1252,7 +1363,7 @@ hero_behind_chokepoint(struct monst *mtmp) } /* hostile monster has another hostile next to it */ -static boolean +staticfn boolean mon_has_friends(struct monst *mtmp) { coordxy dx, dy; @@ -1281,10 +1392,8 @@ mon_has_friends(struct monst *mtmp) boolean find_offensive(struct monst *mtmp) { - register struct obj *obj; - boolean reflection_skip = m_seenres(mtmp, M_SEEN_REFL) != 0 - || monnear(mtmp, mtmp->mux, mtmp->muy); - struct obj *helmet = which_armor(mtmp, W_ARMH); + struct obj *obj, *mtmp_helmet; + boolean reflection_skip; gm.m.offensive = (struct obj *) 0; gm.m.has_offense = 0; @@ -1304,6 +1413,9 @@ find_offensive(struct monst *mtmp) return FALSE; #define nomore(x) if (gm.m.has_offense == x) continue; + reflection_skip = (m_seenres(mtmp, M_SEEN_REFL) != 0 + || monnear(mtmp, mtmp->mux, mtmp->muy)); + mtmp_helmet = which_armor(mtmp, W_ARMH); /* this picks the last viable item rather than prioritizing choices */ for (obj = mtmp->minvent; obj; obj = obj->nobj) { if (!reflection_skip) { @@ -1377,7 +1489,7 @@ find_offensive(struct monst *mtmp) /* do try to move hero to a more vulnerable spot */ && (onscary(u.ux, u.uy, mtmp) || (hero_behind_chokepoint(mtmp) && mon_has_friends(mtmp)) - || (stairway_at(u.ux, u.uy)))) { + || stairway_at(u.ux, u.uy))) { gm.m.offensive = obj; gm.m.has_offense = MUSE_WAN_TELEPORTATION; } @@ -1438,7 +1550,7 @@ find_offensive(struct monst *mtmp) */ nomore(MUSE_SCR_EARTH); if (obj->otyp == SCR_EARTH - && ((helmet && is_hard(helmet)) || mtmp->mconf + && (hard_helmet(mtmp_helmet) || mtmp->mconf || amorphous(mtmp->data) || passes_walls(mtmp->data) || noncorporeal(mtmp->data) || unsolid(mtmp->data) || !rn2(10)) @@ -1450,7 +1562,8 @@ find_offensive(struct monst *mtmp) } nomore(MUSE_CAMERA); if (obj->otyp == EXPENSIVE_CAMERA - && (!Blind || hates_light(gy.youmonst.data)) + && ((!Blind && !resists_blnd(&gy.youmonst)) + || hates_light(gy.youmonst.data)) && dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <= 2 && obj->spe > 0 && !rn2(6)) { gm.m.offensive = obj; @@ -1462,6 +1575,12 @@ find_offensive(struct monst *mtmp) && dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <= 2 && mtmp->mcansee && haseyes(mtmp->data) && !m_seenres(mtmp, M_SEEN_FIRE)) { + /* + * TODO? + * Could choose scroll if (where attacker thinks + * hero is located) is ice and attacker either isn't also + * on ice or is able to fly/float/swim. + */ gm.m.offensive = obj; gm.m.has_offense = MUSE_SCR_FIRE; } @@ -1471,11 +1590,12 @@ find_offensive(struct monst *mtmp) #undef nomore } -static int +staticfn int mbhitm(struct monst *mtmp, struct obj *otmp) { int tmp; - boolean reveal_invis = FALSE, hits_you = (mtmp == &gy.youmonst); + boolean reveal_invis = FALSE, learnit = FALSE, + hits_you = (mtmp == &gy.youmonst); if (!hits_you && otmp->otyp != WAN_UNDEAD_TURNING) { wakeup(mtmp, FALSE, TRUE); @@ -1485,34 +1605,42 @@ mbhitm(struct monst *mtmp, struct obj *otmp) reveal_invis = TRUE; if (hits_you) { if (Antimagic) { + monstseesu(M_SEEN_MAGR); /* monsters notice hero resisting */ shieldeff(u.ux, u.uy); Soundeffect(se_boing, 40); pline("Boing!"); + learnit = TRUE; } else if (rnd(20) < 10 + u.uac) { + monstunseesu(M_SEEN_MAGR); /* mons see hero not resisting */ pline_The("wand hits you!"); tmp = d(2, 12); if (Half_spell_damage) tmp = (tmp + 1) / 2; losehp(tmp, "wand", KILLED_BY_AN); - } else + learnit = TRUE; + } else { pline_The("wand misses you."); + } stop_occupation(); nomul(0); } else if (resists_magm(mtmp)) { shieldeff(mtmp->mx, mtmp->my); Soundeffect(se_boing, 40); pline("Boing!"); + learnit = TRUE; } else if (rnd(20) < 10 + find_mac(mtmp)) { tmp = d(2, 12); hit("wand", mtmp, exclam(tmp)); (void) resist(mtmp, otmp->oclass, tmp, TELL); - if (cansee(mtmp->mx, mtmp->my) && gz.zap_oseen) - makeknown(WAN_STRIKING); + learnit = TRUE; } else { miss("wand", mtmp); - if (cansee(mtmp->mx, mtmp->my) && gz.zap_oseen) - makeknown(WAN_STRIKING); } + /* need to see the wand being zapped and also the spot where the + target is hit; don't have to see the target itself though */ + if (learnit && gz.zap_oseen && (hits_you + || cansee(mtmp->mx, mtmp->my))) + makeknown(WAN_STRIKING); break; case WAN_TELEPORTATION: if (hits_you) { @@ -1523,7 +1651,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); } @@ -1532,9 +1660,7 @@ mbhitm(struct monst *mtmp, struct obj *otmp) case SPE_CANCELLATION: (void) cancel_monst(mtmp, otmp, FALSE, TRUE, FALSE); break; - case WAN_UNDEAD_TURNING: { - boolean learnit = FALSE; - + case WAN_UNDEAD_TURNING: if (hits_you) { unturn_you(); learnit = gz.zap_oseen; @@ -1548,7 +1674,7 @@ mbhitm(struct monst *mtmp, struct obj *otmp) /* context.bypasses=True: if resist() happens to be fatal, make_corpse() will set obj->bypass on the new corpse so that mbhito() will skip it instead of reviving it */ - gc.context.bypasses = TRUE; /* for make_corpse() */ + svc.context.bypasses = TRUE; /* for make_corpse() */ (void) resist(mtmp, WAND_CLASS, rnd(8), NOTELL); } if (wake) { @@ -1560,7 +1686,6 @@ mbhitm(struct monst *mtmp, struct obj *otmp) if (learnit) makeknown(WAN_UNDEAD_TURNING); break; - } default: break; } @@ -1571,23 +1696,45 @@ mbhitm(struct monst *mtmp, struct obj *otmp) return 0; } +/* hit all objects at x,y with fhito function */ +staticfn boolean +fhito_loc(struct obj *obj, + coordxy tx, coordxy ty, + int (*fhito)(OBJ_P, OBJ_P)) +{ + struct obj *otmp, *next_obj; + int hitanything = 0; + + if (!fhito || !OBJ_AT(tx, ty)) + return FALSE; + + for (otmp = svl.level.objects[tx][ty]; otmp; otmp = next_obj) { + next_obj = otmp->nexthere; + + if (otmp->where != OBJ_FLOOR || otmp->ox != tx || otmp->oy != ty) + continue; + hitanything += (*fhito)(otmp, obj); + } + + return hitanything ? TRUE : FALSE; +} + /* A modified bhit() for monsters. Based on bhit() in zap.c. Unlike * buzz(), bhit() doesn't take into account the possibility of a monster * zapping you, so we need a special function for it. (Unless someone wants * to merge the two functions...) */ -static void +staticfn void mbhit( - struct monst *mon, /* monster shooting the wand */ - register int range, /* direction and range */ - int (*fhitm)(MONST_P, OBJ_P), - int (*fhito)(OBJ_P, OBJ_P), /* fns called when mon/obj hit */ - struct obj *obj) /* 2nd arg to fhitm/fhito */ + struct monst *mon, /* monster shooting the wand */ + int range, /* direction and range */ + int (*fhitm)(MONST_P, OBJ_P), /* must be non-Null */ + int (*fhito)(OBJ_P, OBJ_P), /* fns called when mon/obj hit */ + struct obj *obj) /* 2nd arg to fhitm/fhito */ { - register struct monst *mtmp; - register struct obj *otmp; - register uchar typ; - int ddx, ddy; + struct monst *mtmp; + uchar ltyp; + int ddx, ddy, otyp = obj->otyp; gb.bhitpos.x = mon->mx; gb.bhitpos.y = mon->my; @@ -1595,7 +1742,7 @@ mbhit( ddy = sgn(mon->muy - mon->my); while (range-- > 0) { - coordxy x, y; + coordxy x, y, dbx, dby; gb.bhitpos.x += ddx; gb.bhitpos.y += ddy; @@ -1607,11 +1754,6 @@ mbhit( gb.bhitpos.y -= ddy; break; } - if (find_drawbridge(&x, &y)) - switch (obj->otyp) { - case WAN_STRIKING: - destroy_drawbridge(x, y); - } if (u_at(gb.bhitpos.x, gb.bhitpos.y)) { (*fhitm)(&gy.youmonst, obj); range -= 3; @@ -1621,23 +1763,21 @@ mbhit( (*fhitm)(mtmp, obj); range -= 3; } - /* modified by GAN to hit all objects */ - if (fhito) { - int hitanything = 0; - register struct obj *next_obj; - - for (otmp = gl.level.objects[gb.bhitpos.x][gb.bhitpos.y]; otmp; - otmp = next_obj) { - /* Fix for polymorph bug, Tim Wright */ - next_obj = otmp->nexthere; - hitanything += (*fhito)(otmp, obj); - } - if (hitanything) - range--; - } - typ = levl[gb.bhitpos.x][gb.bhitpos.y].typ; - if (IS_DOOR(typ) || typ == SDOOR) { - switch (obj->otyp) { + if (fhito_loc(obj, gb.bhitpos.x, gb.bhitpos.y, fhito)) + range--; + ltyp = levl[gb.bhitpos.x][gb.bhitpos.y].typ; + dbx = x, dby = y; + if (otyp == WAN_STRIKING + /* if levl[x][y].typ is DRAWBRIDGE_UP then the zap is passing + over the moat in front of a closed drawbridge and doesn't + hit any part of the bridge's mechanism (yet; it might be + about to hit the closed portcullis on the next iteration) */ + && ltyp != DRAWBRIDGE_UP && find_drawbridge(&dbx, &dby)) { + /* this might kill mon and destroy obj but they'll remain + accessible; (*fhitm)() and (*fhito)() use obj for zap type */ + destroy_drawbridge(dbx, dby); + } else if (IS_DOOR(ltyp) || ltyp == SDOOR) { + switch (otyp) { /* note: monsters don't use opening or locking magic at present, but keep these as placeholders */ case WAN_OPENING: @@ -1645,7 +1785,7 @@ mbhit( case WAN_STRIKING: if (doorlock(obj, mon, gb.bhitpos.x, gb.bhitpos.y)) { if (gz.zap_oseen) - makeknown(obj->otyp); + makeknown(otyp); /* if a shop door gets broken, add it to the shk's fix list (no cost to player) */ if (doorstate(&levl[gb.bhitpos.x][gb.bhitpos.y]) == D_BROKEN @@ -1655,8 +1795,8 @@ mbhit( break; } } - if (!ZAP_POS(typ) - || (IS_DOOR(typ) + if (!ZAP_POS(ltyp) + || (IS_DOOR(ltyp) && door_is_closed(&levl[gb.bhitpos.x][gb.bhitpos.y]))) { gb.bhitpos.x -= ddx; gb.bhitpos.y -= ddy; @@ -1679,7 +1819,7 @@ use_offensive(struct monst *mtmp) /* offensive potions are not drunk, they're thrown */ if (otmp->oclass != POTION_CLASS && (i = precheck(mtmp, otmp)) != 0) return i; - oseen = otmp && canseemon(mtmp); + oseen = canseemon(mtmp); /* total up the possible damage for just swinging */ for (i = 0; i < NATTK; i++) { @@ -1763,6 +1903,7 @@ use_offensive(struct monst *mtmp) mzapwand(mtmp, otmp, FALSE); gm.m_using = TRUE; mbhit(mtmp, rn1(8, 6), mbhitm, bhito, otmp); + /* note: 'otmp' might have been destroyed (drawbridge destruction) */ gm.m_using = FALSE; return 2; case MUSE_SCR_EARTH: { @@ -1798,7 +1939,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); @@ -1816,12 +1957,12 @@ use_offensive(struct monst *mtmp) if (Hallucination) { SetVoice(mtmp, 0, 80, 0); verbalize("Say cheese!"); - } else { + } else if (!Blind) { pline("%s takes a picture of you with %s!", Monnam(mtmp), an(xname(otmp))); } gm.m_using = TRUE; - if (!Blind) { + if (!Blind && !resists_blnd(&gy.youmonst)) { You("are blinded by the flash of light!"); make_blinded(BlindedTimeout + (long) rnd(1 + 50), FALSE); } @@ -1845,7 +1986,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); @@ -1895,7 +2036,8 @@ use_offensive(struct monst *mtmp) struct obj *minvptr; if (cansee(mtmp->mx, mtmp->my)) { otmp->dknown = 1; - pline("%s hurls %s!", Monnam(mtmp), singular(otmp, doname)); + pline_mon(mtmp, "%s hurls %s!", + Monnam(mtmp), singular(otmp, doname)); } if (isoil && !otmp->lamplit && (!mtmp->mconf || rn2(3))) { /* A monster throwing oil probably wants it to explode; assume they @@ -1944,12 +2086,14 @@ rnd_offensive_item(struct monst *mtmp) return WAN_DEATH; switch (rn2(9 - (difficulty < 4) + 4 * (difficulty > 6))) { case 0: { - struct obj *helmet = which_armor(mtmp, W_ARMH); + struct obj *mtmp_helmet = which_armor(mtmp, W_ARMH); - if ((helmet && is_hard(helmet)) || amorphous(pm) + 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: @@ -1993,7 +2137,7 @@ rnd_offensive_item(struct monst *mtmp) boolean find_misc(struct monst *mtmp) { - register struct obj *obj; + struct obj *obj; struct permonst *mdat = mtmp->data; coordxy x = mtmp->mx, y = mtmp->my; struct trap *t; @@ -2026,7 +2170,7 @@ find_misc(struct monst *mtmp) for (yy = y - 1; yy <= y + 1; yy++) if (isok(xx, yy) && !u_at(xx, yy) && (diag_ok || xx == x || yy == y) - && ((xx == x && yy == y) || !gl.level.monsters[xx][yy])) + && ((xx == x && yy == y) || !svl.level.monsters[xx][yy])) if ((t = t_at(xx, yy)) != 0 && (ignore_boulders || !sobj_at(BOULDER, xx, yy)) && !onscary(xx, yy, mtmp)) { @@ -2078,7 +2222,7 @@ find_misc(struct monst *mtmp) && uwep && !rn2(5) && obj == MON_WEP(mtmp) /* hero's location must be known and adjacent */ && u_at(mtmp->mux, mtmp->muy) - && next2u(mtmp->mx, mtmp->my) + && m_next2u(mtmp) /* don't bother if it can't work (this doesn't prevent cursed weapons from being targeted) */ && !u.uswallow @@ -2136,7 +2280,7 @@ find_misc(struct monst *mtmp) gm.m.has_misc = MUSE_BAG; } nomore(MUSE_ITLACHIAYAQUE); - if (obj->oartifact == ART_ITLACHIAYAQUE && obj->age <= gm.moves + if (obj->oartifact == ART_ITLACHIAYAQUE && obj->age <= svm.moves && !mtmp->mpeaceful && !mtmp->mblinded && m_canseeu(mtmp)) { gm.m.misc = obj; gm.m.has_misc = MUSE_ITLACHIAYAQUE; @@ -2148,7 +2292,7 @@ find_misc(struct monst *mtmp) /* type of monster to polymorph into; defaults to one suitable for the current level rather than the totally arbitrary choice of newcham() */ -static struct permonst * +staticfn struct permonst * muse_newcham_mon(struct monst *mon) { int pm = armor_to_dragon(mon); @@ -2159,7 +2303,7 @@ muse_newcham_mon(struct monst *mon) return rndmonst(); } -static int +staticfn int mloot_container( struct monst *mon, struct obj *container, @@ -2217,8 +2361,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 */ @@ -2242,7 +2388,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), @@ -2279,6 +2425,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; @@ -2293,10 +2440,12 @@ 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)) { - register int tolev = depth(&u.uz) - 1; + int tolev = depth(&u.uz) - 1; d_level tolevel; get_level(&tolevel, tolev); @@ -2304,8 +2453,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); @@ -2315,7 +2465,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); @@ -2323,7 +2473,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); @@ -2333,6 +2483,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 @@ -2360,10 +2512,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: @@ -2373,6 +2529,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); @@ -2380,10 +2538,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); @@ -2394,9 +2554,9 @@ use_misc(struct monst *mtmp) if (vis || vistrapspot) seetrap(t); if (vismon || vistrapspot) { - pline("%s deliberately %s onto a %s trap!", Some_Monnam(mtmp), + pline_mon(mtmp, "%s deliberately %s onto a %s!", Some_Monnam(mtmp), vtense(fakename[0], locomotion(mtmp->data, "jump")), - t->tseen ? "polymorph" : "hidden"); + t->tseen ? trapname(t->ttyp, FALSE) : "hidden trap"); /* note: if mtmp is unseen because it is invisible, its new shape will also be invisible and could produce "Its armor falls off" messages during the transformation; those make @@ -2408,12 +2568,15 @@ use_misc(struct monst *mtmp) remove_monster(mtmp->mx, mtmp->my); newsym(mtmp->mx, mtmp->my); place_monster(mtmp, gt.trapx, gt.trapy); + maybe_unhide_at(gt.trapx, gt.trapy); if (mtmp->wormno) worm_move(mtmp); newsym(gt.trapx, gt.trapy); (void) newcham(mtmp, muse_newcham_mon(mtmp), 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 */ @@ -2436,8 +2599,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; @@ -2464,17 +2627,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; } @@ -2484,7 +2647,7 @@ use_misc(struct monst *mtmp) return 0; case MUSE_ITLACHIAYAQUE: /* invoke timeout; allow quest nemesis to use it more often */ - otmp->age = gm.moves + (mtmp->data == &mons[PM_SCHLIEMANN] + otmp->age = svm.moves + (mtmp->data == &mons[PM_SCHLIEMANN] ? rn1(20,10) : rnz(100)); if (cansee(mtmp->mx, mtmp->my)) { pline("%s brandishes Itlachiayaque!", Monnam(mtmp)); @@ -2511,7 +2674,7 @@ use_misc(struct monst *mtmp) RESTORE_WARNINGS -static void +staticfn void you_aggravate(struct monst *mtmp) { pline("For some reason, %s presence is known to you.", @@ -2571,7 +2734,7 @@ rnd_misc_item(struct monst *mtmp) #if 0 /* check whether hero is carrying a corpse or contained petrifier corpse */ -static boolean +staticfn boolean necrophiliac(struct obj *objlist, boolean any_corpse) { while (objlist) { @@ -2672,7 +2835,7 @@ searches_for_item(struct monst *mon, struct obj *obj) return (boolean) (mcould_eat_tin(mon) && (!resists_ston(mon) && cures_stoning(mon, obj, TRUE))); - if (typ == EGG) + if (typ == EGG && ismnum(obj->corpsenm)) return (boolean) touch_petrifies(&mons[obj->corpsenm]); break; default: @@ -2780,7 +2943,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)); } } @@ -2807,7 +2970,7 @@ munstone(struct monst *mon, boolean by_you) return FALSE; } -static void +staticfn void mon_consume_unstone( struct monst *mon, struct obj *obj, @@ -2830,7 +2993,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"), @@ -2846,9 +3009,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 @@ -2864,29 +3027,29 @@ 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); - if (edog->hungrytime < gm.moves) - edog->hungrytime = gm.moves; + if (edog->hungrytime < svm.moves) + edog->hungrytime = svm.moves; edog->hungrytime += nutrit; mon->mconf = 0; } /* use up monster's next move */ mon->movement -= NORMAL_SPEED; - mon->mlstmv = gm.moves; + mon->mlstmv = svm.moves; } /* decide whether obj can cure petrification; also used when picking up */ -static boolean +staticfn boolean cures_stoning(struct monst *mon, struct obj *obj, boolean tinok) { if (obj->otyp == POT_ACID) @@ -2902,7 +3065,7 @@ cures_stoning(struct monst *mon, struct obj *obj, boolean tinok) || acidic(&mons[obj->corpsenm])); } -static boolean +staticfn boolean mcould_eat_tin(struct monst *mon) { struct obj *obj, *mwep; @@ -2997,7 +3160,7 @@ munslime(struct monst *mon, boolean by_you) } } if (t && t->ttyp == FIRE_TRAP) - return muse_unslime(mon, (struct obj *) &cg.zeroobj, t, by_you); + return muse_unslime(mon, &hands_obj, t, by_you); } /* MUSE */ @@ -3005,7 +3168,7 @@ munslime(struct monst *mon, boolean by_you) } /* mon uses an item--selected by caller--to burn away incipient slime */ -static boolean +staticfn boolean muse_unslime( struct monst *mon, struct obj *obj, @@ -3017,7 +3180,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); @@ -3046,7 +3209,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 */ @@ -3087,7 +3251,7 @@ muse_unslime( 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); @@ -3122,7 +3286,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 @@ -3130,23 +3294,23 @@ 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); } /* use up monster's next move */ mon->movement -= NORMAL_SPEED; - mon->mlstmv = gm.moves; + mon->mlstmv = svm.moves; return res; } /* decide whether obj can be used to cure green slime */ -static int +staticfn int cures_sliming(struct monst *mon, struct obj *obj) { /* scroll of fire */ @@ -3164,50 +3328,56 @@ cures_sliming(struct monst *mon, struct obj *obj) && obj->spe > 0); } -/* TRUE if monster appears to be green; for active TEXTCOLOR, we go by - the display color, otherwise we just pick things that seem plausibly - green (which doesn't necessarily match the TEXTCOLOR categorization) */ -static boolean +/* TRUE if monster appears to be green; we go by the display color. + The alternative was to just pick things that + seem plausibly green (which didn't necessarily match the categorization + by the color of the text). + iflags.use_color is not meant for game behavior decisions */ +staticfn boolean green_mon(struct monst *mon) { struct permonst *ptr = mon->data; if (Hallucination) return FALSE; -#ifdef TEXTCOLOR + return (ptr->mcolor == CLR_GREEN || ptr->mcolor == CLR_BRIGHT_GREEN); +#if 0 if (iflags.use_color) return (ptr->mcolor == CLR_GREEN || ptr->mcolor == CLR_BRIGHT_GREEN); -#endif - /* approximation */ - if (strstri(ptr->pmnames[NEUTRAL], "green") - || (ptr->pmnames[MALE] && strstri(ptr->pmnames[MALE], "green")) - || (ptr->pmnames[FEMALE] && strstri(ptr->pmnames[FEMALE], "green"))) - return TRUE; - switch (monsndx(ptr)) { - case PM_FOREST_CENTAUR: - case PM_GARTER_SNAKE: - case PM_GECKO: - case PM_GREMLIN: - case PM_HOMUNCULUS: - case PM_JUIBLEX: - case PM_LEPRECHAUN: - case PM_LICHEN: - case PM_LIZARD: - case PM_WOOD_NYMPH: - return TRUE; - default: - if (is_elf(ptr) && !is_prince(ptr) && !is_lord(ptr) - && ptr != &mons[PM_GREY_ELF]) + else { + /* approximation */ + if (strstri(ptr->pmnames[NEUTRAL], "green") + || (ptr->pmnames[MALE] && strstri(ptr->pmnames[MALE], "green")) + || (ptr->pmnames[FEMALE] + && strstri(ptr->pmnames[FEMALE], "green"))) return TRUE; - break; + switch (monsndx(ptr)) { + case PM_FOREST_CENTAUR: + case PM_GARTER_SNAKE: + case PM_GECKO: + case PM_GREMLIN: + case PM_HOMUNCULUS: + case PM_JUIBLEX: + case PM_LEPRECHAUN: + case PM_LICHEN: + case PM_LIZARD: + case PM_WOOD_NYMPH: + return TRUE; + default: + if (is_elf(ptr) && !is_prince(ptr) && !is_lord(ptr) + && ptr != &mons[PM_GREY_ELF]) + return TRUE; + break; + } } return FALSE; +#endif } /* mtmp, for whatever reason, does not want to leave the current level, even if * fleeing, so it should not consider MUSE_* options that would make it leave * the level. */ -static boolean +staticfn boolean wont_leave_level(struct monst *mtmp) { if (mtmp->data == &mons[PM_GERYON] && geryon_bonus() > 0) diff --git a/src/music.c b/src/music.c index 81993a4900..5e622a10c4 100644 --- a/src/music.c +++ b/src/music.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 music.c $NHDT-Date: 1646688067 2022/03/07 21:21:07 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.77 $ */ +/* 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. */ @@ -28,44 +28,51 @@ #include "hack.h" -static void awaken_monsters(int); -static void charm_snakes(int); -static void calm_nymphs(int); -static void charm_monsters(int); -static void do_earthquake(int); -static const char *generic_lvl_desc(void); -static int do_improvisation(struct obj *); -static char *improvised_notes(boolean *); - +staticfn void awaken_scare(struct monst *, boolean); +staticfn void awaken_monsters(int); +staticfn void charm_snakes(int); +staticfn void calm_nymphs(int); +staticfn void charm_monsters(int); +staticfn void do_pit(coordxy, coordxy, unsigned); +staticfn void do_earthquake(int); +staticfn const char *generic_lvl_desc(void); +staticfn int do_improvisation(struct obj *); +staticfn char *improvised_notes(boolean *); + +/* wake up monster, possibly scare it */ +staticfn void +awaken_scare(struct monst *mtmp, boolean scary) +{ + wakeup(mtmp, FALSE, FALSE); + mtmp->mcanmove = 1; + mtmp->mfrozen = 0; + /* may scare some monsters -- waiting monsters excluded */ + if (!unique_corpstat(mtmp->data) + && (mtmp->mstrategy & STRAT_WAITMASK) != 0) + mtmp->mstrategy &= ~STRAT_WAITMASK; + else if (scary + && !mindless(mtmp->data) + && !resist(mtmp, TOOL_CLASS, 0, NOTELL) + /* some monsters are immune */ + && onscary(0, 0, mtmp)) + monflee(mtmp, 0, FALSE, TRUE); +} /* * Wake every monster in range... */ -static void +staticfn void awaken_monsters(int distance) { - register struct monst *mtmp; - register int distm; + struct monst *mtmp; + int distm; for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; - if ((distm = mdistu(mtmp)) < distance) { - wakeup(mtmp, FALSE, FALSE); - mtmp->mcanmove = 1; - mtmp->mfrozen = 0; - /* may scare some monsters -- waiting monsters excluded */ - if (!unique_corpstat(mtmp->data) - && (mtmp->mstrategy & STRAT_WAITMASK) != 0) - mtmp->mstrategy &= ~STRAT_WAITMASK; - else if (distm < distance / 3 - && !mindless(mtmp->data) - && !resist(mtmp, TOOL_CLASS, 0, NOTELL) - /* some monsters are immune */ - && onscary(0, 0, mtmp)) - monflee(mtmp, 0, FALSE, TRUE); - } + if ((distm = mdistu(mtmp)) < distance) + awaken_scare(mtmp, (distm < distance / 3)); } } @@ -76,7 +83,7 @@ awaken_monsters(int distance) void put_monsters_to_sleep(struct monst *caster, int distance) { - register struct monst *mtmp; + struct monst *mtmp; for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) @@ -98,10 +105,10 @@ put_monsters_to_sleep(struct monst *caster, int distance) * Charm snakes in range. Note that the snakes are NOT tamed. */ -static void +staticfn void charm_snakes(int distance) { - register struct monst *mtmp; + struct monst *mtmp; int could_see_mon, was_peaceful; for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { @@ -132,10 +139,10 @@ charm_snakes(int distance) * Calm nymphs in range. */ -static void +staticfn void calm_nymphs(int distance) { - register struct monst *mtmp; + struct monst *mtmp; for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) @@ -158,11 +165,12 @@ calm_nymphs(int distance) void awaken_soldiers(struct monst* bugler /* monster that played instrument */) { - register struct monst *mtmp; + struct monst *mtmp; int distance, distm; /* distance of affected non-soldier monsters to bugler */ - distance = ((bugler == &gy.youmonst) ? u.ulevel : bugler->data->mlevel) * 30; + distance = ((bugler == &gy.youmonst) ? u.ulevel + : bugler->data->mlevel) * 30; for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) @@ -182,25 +190,13 @@ awaken_soldiers(struct monst* bugler /* monster that played instrument */) ? mdistu(mtmp) : dist2(bugler->mx, bugler->my, mtmp->mx, mtmp->my))) < distance) { - wakeup(mtmp, FALSE, FALSE); - mtmp->mcanmove = 1; - mtmp->mfrozen = 0; - /* may scare some monsters -- waiting monsters excluded */ - if (!unique_corpstat(mtmp->data) - && (mtmp->mstrategy & STRAT_WAITMASK) != 0) - mtmp->mstrategy &= ~STRAT_WAITMASK; - else if (distm < distance / 3 - && !mindless(mtmp->data) - && !resist(mtmp, TOOL_CLASS, 0, NOTELL) - /* some monsters are immune */ - && onscary(0, 0, mtmp)) - monflee(mtmp, 0, FALSE, TRUE); + awaken_scare(mtmp, (distm < distance / 3)); } } } /* Charm monsters in range. Note that they may resist the spell. */ -static void +staticfn void charm_monsters(int distance) { struct monst *mtmp, *mtmp2; @@ -219,25 +215,144 @@ charm_monsters(int distance) one; do that even if mtmp resists in order to behave the same as a non-cursed scroll of taming or spell of charm monster */ if (!resist(mtmp, TOOL_CLASS, 0, NOTELL) || mtmp->isshk) - (void) tamedog(mtmp, (struct obj *) 0, FALSE); + (void) tamedog(mtmp, (struct obj *) 0, FALSE, TRUE); + } + } +} + +/* Try to make a pit. */ +staticfn void +do_pit(coordxy x, coordxy y, unsigned tu_pit) +{ + struct monst *mtmp; + struct obj *otmp; + struct trap *chasm; + schar filltype; + + chasm = maketrap(x, y, PIT); + if (!chasm) + return; /* no pit if portal at that location */ + chasm->tseen = 1; + + mtmp = m_at(x, y); /* (redundant?) */ + if ((otmp = sobj_at(BOULDER, x, y)) != 0) { + if (cansee(x, y)) + pline("KADOOM! The boulder falls into a chasm%s!", + u_at(x, y) ? " below you" : ""); + if (mtmp) + mtmp->mtrapped = 0; + obj_extract_self(otmp); + (void) flooreffects(otmp, x, y, ""); + return; + } + + /* Let liquid flow into the newly created chasm. + Adjust corresponding code in apply.c for exploding + wand of digging if you alter this sequence. */ + filltype = fillholetyp(x, y, FALSE); + if (filltype != ROOM) { + set_levltyp(x, y, filltype); /* levl[x][y] = filltype; */ + liquid_flow(x, y, filltype, chasm, (char *) 0); + /* liquid_flow() deletes trap, might kill mtmp */ + if ((chasm = t_at(x, y)) == NULL) + return; + } + + /* We have to check whether monsters or hero falls into a + new pit.... Note: if we get here, chasm is non-Null. */ + if (mtmp) { + if (grounded(mtmp->data)) { + boolean m_already_trapped = mtmp->mtrapped; + + mtmp->mtrapped = 1; + if (!m_already_trapped) { /* suppress messages */ + if (cansee(x, y)) { + pline("%s falls into a chasm!", Monnam(mtmp)); + } else if (humanoid(mtmp->data)) { + Soundeffect(se_scream, 50); + You_hear("a scream!"); + } + } + /* Falling is okay for falling down + within a pit from jostling too */ + mselftouch(mtmp, "Falling, ", TRUE); + if (!DEADMONSTER(mtmp)) { + mtmp->mhp -= rnd(m_already_trapped ? 4 : 6); + if (DEADMONSTER(mtmp)) { + if (!cansee(x, y)) { + pline("It is destroyed!"); + } else { + You("destroy %s!", + mtmp->mtame + ? x_monnam(mtmp, ARTICLE_THE, "poor", + has_mgivenname(mtmp) + ? SUPPRESS_SADDLE : 0, + FALSE) + : mon_nam(mtmp)); + } + xkilled(mtmp, XKILL_NOMSG); + } + } + } + } else if (u_at(x, y)) { + if (u.utrap && u.utraptype == TT_BURIEDBALL) { + /* Note: the chain should break if a pit gets + created at the buried ball's location, which + is not necessarily here. But if we don't do + things this way, entering the new pit below + will override current trap anyway, but too + late to get Lev and Fly handling. */ + Your("chain breaks!"); + reset_utrap(TRUE); + } + if (Levitation || Flying || !grounded(gy.youmonst.data)) { + if (!tu_pit) { /* no pit here previously */ + pline("A chasm opens up under you!"); + You("don't fall in!"); + } + } else if (!tu_pit || !u.utrap || u.utraptype != TT_PIT) { + /* no pit here previously, or you were + not in it even if there was */ + You("fall into a chasm!"); + set_utrap(rn1(6, 2), TT_PIT); + losehp(Maybe_Half_Phys(rnd(6)), + "fell into a chasm", NO_KILLER_PREFIX); + selftouch("Falling, you"); + } else if (u.utrap && u.utraptype == TT_PIT) { + boolean keepfooting = + (!(Fumbling && rn2(5)) + && (!(rnl(Role_if(PM_ARCHEOLOGIST) ? 3 : 9)) + || ((ACURR(A_DEX) > 7) && rn2(5)))); + + You("are jostled around violently!"); + set_utrap(rn1(6, 2), TT_PIT); + losehp(Maybe_Half_Phys(rnd(keepfooting ? 2 : 4)), + "hurt in a chasm", NO_KILLER_PREFIX); + if (keepfooting) + exercise(A_DEX, TRUE); + else + selftouch((Upolyd && (slithy(gy.youmonst.data) + || nolimbs(gy.youmonst.data))) + ? "Shaken, you" + : "Falling down, you"); } + } else { + newsym(x, y); } } /* Generate earthquake :-) of desired force. * That is: create random chasms (pits). */ -static void +staticfn void do_earthquake(int force) { static const char into_a_chasm[] = " into a chasm"; - register coordxy x, y; + coordxy x, y; struct monst *mtmp; - struct obj *otmp; - struct trap *chasm, *trap_at_u = t_at(u.ux, u.uy); + struct trap *trap_at_u = t_at(u.ux, u.uy); int start_x, start_y, end_x, end_y, amsk; aligntyp algn; - schar filltype; unsigned tu_pit = 0; if (trap_at_u) @@ -299,11 +414,13 @@ do_earthquake(int force) case FOUNTAIN: /* make the fountain disappear */ if (cansee(x, y)) pline_The("fountain falls%s.", into_a_chasm); - goto do_pit; + do_pit(x, y, tu_pit); + break; case SINK: if (cansee(x, y)) pline_The("kitchen sink falls%s.", into_a_chasm); - goto do_pit; + do_pit(x, y, tu_pit); + break; case ALTAR: amsk = altarmask_at(x, y); /* always preserve the high altars */ @@ -313,152 +430,42 @@ do_earthquake(int force) if (cansee(x, y)) pline_The("%s altar falls%s.", align_str(algn), into_a_chasm); - goto do_pit; + desecrate_altar(FALSE, algn); + do_pit(x, y, tu_pit); + break; case GRAVE: if (cansee(x, y)) pline_The("headstone topples%s.", into_a_chasm); - goto do_pit; + do_pit(x, y, tu_pit); + break; case THRONE: if (cansee(x, y)) pline_The("throne falls%s.", into_a_chasm); - goto do_pit; + do_pit(x, y, tu_pit); + break; case SCORR: levl[x][y].typ = CORR; unblock_point(x, y); if (cansee(x, y)) pline("A secret corridor is revealed."); + FALLTHROUGH; /*FALLTHRU*/ case CORR: - case ROOM: /* Try to make a pit. */ - do_pit: - /* maketrap() won't replace furniture with a trap, - so remove the furniture first */ - if (levl[x][y].typ != CORR) { - if (levl[x][y].typ != DOOR) { - levl[x][y].typ = ROOM; - /* clear blessed fountain, disturbed grave */ - levl[x][y].horizontal = 0; - } - /* clear doormask, altarmask, looted throne */ - levl[x][y].flags = 0; /* same as 'doormask = D_NODOOR' */ - } - chasm = maketrap(x, y, PIT); - if (!chasm) - break; /* no pit if portal at that location */ - chasm->tseen = 1; - - /* Let liquid flow into the newly created chasm. - Adjust corresponding code in apply.c for exploding - wand of digging if you alter this sequence. */ - filltype = fillholetyp(x, y, FALSE); - if (filltype != ROOM) { - levl[x][y].typ = filltype; /* flags set via doormask */ - liquid_flow(x, y, filltype, chasm, (char *) 0); - } - - mtmp = m_at(x, y); /* (redundant?) */ - if ((otmp = sobj_at(BOULDER, x, y)) != 0) { - if (cansee(x, y)) - pline("KADOOM! The boulder falls into a chasm%s!", - u_at(x, y) ? " below you" : ""); - if (mtmp) - mtmp->mtrapped = 0; - obj_extract_self(otmp); - (void) flooreffects(otmp, x, y, ""); - break; /* from switch, not loop */ - } - - /* We have to check whether monsters or player - falls in a chasm... */ - if (mtmp) { - if (grounded(mtmp->data)) { - boolean m_already_trapped = mtmp->mtrapped; - - mtmp->mtrapped = 1; - if (!m_already_trapped) { /* suppress messages */ - if (cansee(x, y)) { - pline("%s falls into a chasm!", Monnam(mtmp)); - } else if (humanoid(mtmp->data)) { - Soundeffect(se_scream, 50); - You_hear("a scream!"); - } - } - /* Falling is okay for falling down - within a pit from jostling too */ - mselftouch(mtmp, "Falling, ", TRUE); - if (!DEADMONSTER(mtmp)) { - mtmp->mhp -= rnd(m_already_trapped ? 4 : 6); - if (DEADMONSTER(mtmp)) { - if (!cansee(x, y)) { - pline("It is destroyed!"); - } else { - You("destroy %s!", - mtmp->mtame - ? x_monnam(mtmp, ARTICLE_THE, "poor", - has_mgivenname(mtmp) - ? SUPPRESS_SADDLE : 0, - FALSE) - : mon_nam(mtmp)); - } - xkilled(mtmp, XKILL_NOMSG); - } - } - } - } else if (u_at(x, y)) { - if (u.utrap && u.utraptype == TT_BURIEDBALL) { - /* Note: the chain should break if a pit gets - created at the buried ball's location, which - is not necessarily here. But if we don't do - things this way, entering the new pit below - will override current trap anyway, but too - late to get Lev and Fly handling. */ - Your("chain breaks!"); - reset_utrap(TRUE); - } - if (Levitation || Flying || !grounded(gy.youmonst.data)) { - if (!tu_pit) { /* no pit here previously */ - pline("A chasm opens up under you!"); - You("don't fall in!"); - } - } else if (!tu_pit || !u.utrap || u.utraptype != TT_PIT) { - /* no pit here previously, or you were - not in it even if there was */ - You("fall into a chasm!"); - set_utrap(rn1(6, 2), TT_PIT); - losehp(Maybe_Half_Phys(rnd(6)), - "fell into a chasm", NO_KILLER_PREFIX); - selftouch("Falling, you"); - } else if (u.utrap && u.utraptype == TT_PIT) { - boolean keepfooting = - ((Fumbling && !rn2(5)) - || (!rnl(Role_if(PM_ARCHEOLOGIST) ? 3 : 9)) - || ((ACURR(A_DEX) > 7) && rn2(5))); - - You("are jostled around violently!"); - set_utrap(rn1(6, 2), TT_PIT); - losehp(Maybe_Half_Phys(rnd(keepfooting ? 2 : 4)), - "hurt in a chasm", NO_KILLER_PREFIX); - if (keepfooting) - exercise(A_DEX, TRUE); - else - selftouch((Upolyd && (slithy(gy.youmonst.data) - || nolimbs(gy.youmonst.data))) - ? "Shaken, you" - : "Falling down, you"); - } - } else { - newsym(x, y); - } + case ROOM: + do_pit(x, y, tu_pit); break; case SDOOR: 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 */ - if (doorstate(&levl[x][y]) == D_NODOOR) - goto do_pit; + if (doorstate(&levl[x][y]) == D_NODOOR) { + do_pit(x, y, tu_pit); + break; + } /* wasn't doorless, now it will be */ set_doorstate(&levl[x][y], D_NODOOR); unblock_point(x, y); @@ -472,7 +479,7 @@ do_earthquake(int force) } } -static const char * +staticfn const char * generic_lvl_desc(void) { if (Is_astralevel(&u.uz)) @@ -489,7 +496,7 @@ generic_lvl_desc(void) return "dungeon"; } -const char *beats[] = { +static const char *beats[] = { "stepper", "one drop", "slow two", "triple stroke roll", "double shuffle", "half-time shuffle", "second line", "train" }; @@ -497,19 +504,19 @@ const char *beats[] = { /* * The player is trying to extract something from his/her instrument. */ -static int -do_improvisation(struct obj* instr) +staticfn int +do_improvisation(struct obj *instr) { int damage, mode, do_spec = !(Stunned || Confusion); struct obj itmp; boolean mundane = FALSE, same_old_song = FALSE; static char my_goto_song[] = {'C', '\0'}, - *improvisation SOUNDLIBONLY = my_goto_song; + *improvisation = my_goto_song; itmp = *instr; itmp.oextra = (struct oextra *) 0; /* ok on this copy as instr maintains - the ptr to free at some point if - there is one */ + * the ptr to free at some point if + * there is one */ /* if won't yield special effect, make sound of mundane counterpart */ if (!do_spec || instr->spe <= 0) @@ -659,7 +666,8 @@ do_improvisation(struct obj* instr) if (!Deaf) pline("%s very attractive%s music.", - Tobjnam(instr, "produce"), same_old_song ? " and familiar" : ""); + Tobjnam(instr, "produce"), + same_old_song ? " and familiar" : ""); else You_feel("very soothing vibrations."); Hero_playnotes(obj_to_instr(&itmp), improvisation, 50); @@ -671,9 +679,11 @@ do_improvisation(struct obj* instr) do_spec &= (rn2(ACURR(A_DEX)) + u.ulevel > 25); if (!Deaf) pline("%s %s.", Yname2(instr), - (do_spec && same_old_song) ? "produces a familiar, lilting melody" - : (do_spec) ? "produces a lilting melody" - : (same_old_song) ? "twangs a familar tune" : "twangs"); + (do_spec && same_old_song) + ? "produces a familiar, lilting melody" + : (do_spec) ? "produces a lilting melody" + : (same_old_song) ? "twangs a familiar tune" + : "twangs"); else You_feel("soothing vibrations."); Hero_playnotes(obj_to_instr(&itmp), improvisation, 50); @@ -711,46 +721,48 @@ do_improvisation(struct obj* instr) /* TODO maybe: sound effects for these riffs */ You("%s %s.", rn2(2) ? "butcher" : rn2(2) ? "manage" : "pull off", - an(beats[rn2(SIZE(beats))])); + an(ROLL_FROM(beats))); Hero_playnotes(obj_to_instr(&itmp), improvisation, 50); } awaken_monsters(u.ulevel * (mundane ? 5 : 40)); - gc.context.botl = TRUE; + disp.botl = TRUE; break; default: impossible("What a weird instrument (%d)!", instr->otyp); return 0; } + nhUse(improvisation); return 2; /* That takes time */ } -static char * +staticfn char * improvised_notes(boolean *same_as_last_time) { - static const char notes[] = "ABCDEFG"; - /* target buffer has to be in gc.context, otherwise saving game + static const char notes[7] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G' }; + /* 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. */ /* You can change your tune, usually */ - if (!(Unchanging && gc.context.jingle[0] != '\0')) { - int i, notecount = rnd(SIZE(gc.context.jingle) - 1); /* 1 - 5 */ + if (!(Unchanging && svc.context.jingle[0] != '\0')) { + int i, notecount = rnd(SIZE(svc.context.jingle) - 1); /* 1 - 5 */ + for (i = 0; i < notecount; ++i) { - gc.context.jingle[i] = notes[rn2(SIZE(notes) - 1)]; /* -1 to exclude '\0' */ + svc.context.jingle[i] = ROLL_FROM(notes); } - gc.context.jingle[notecount] = '\0'; + svc.context.jingle[notecount] = '\0'; *same_as_last_time = FALSE; } else { *same_as_last_time = TRUE; } - return gc.context.jingle; + return svc.context.jingle; } /* * So you want music... */ int -do_play_instrument(struct obj* instr) +do_play_instrument(struct obj *instr) { char buf[BUFSZ] = DUMMY, c = 'y'; char *s; @@ -774,129 +786,130 @@ do_play_instrument(struct obj* instr) goto nevermind; } - if (c == 'n') { - if (u.uevent.uheard_tune == 2) - c = ynq("Play the passtune?"); - if (c == 'q') { - goto nevermind; - } else if (c == 'y') { - Strcpy(buf, gt.tune); - } else { - getlin("What tune are you playing? [5 notes, A-G]", buf); - (void) mungspaces(buf); - if (*buf == '\033') - goto nevermind; - - /* convert to uppercase and change any "H" to the expected "B" */ - for (s = buf; *s; s++) { - *s = highc(*s); - if (*s == 'H') - *s = 'B'; - } - } + if (c != 'n') + return do_improvisation(instr) ? ECMD_TIME : ECMD_OK; - You(!Deaf ? "extract a strange sound from %s!" - : "can feel %s emitting vibrations.", the(xname(instr))); - Hero_playnotes(obj_to_instr(instr), buf, 50); + if (u.uevent.uheard_tune == 2) + c = ynq("Play the passtune?"); + if (c == 'q') { + goto nevermind; + } else if (c == 'y') { + Strcpy(buf, svt.tune); + } else { + getlin("What tune are you playing? [5 notes, A-G]", buf); + (void) mungspaces(buf); + if (*buf == '\033') + goto nevermind; + /* convert to uppercase and change any "H" to the expected "B" */ + for (s = buf; *s; s++) { + *s = highc(*s); + if (*s == 'H') + *s = 'B'; + } + } - /* Check if there was the Stronghold drawbridge near - * and if the tune conforms to what we're waiting for. - */ - if (Is_stronghold(&u.uz)) { - exercise(A_WIS, TRUE); /* just for trying */ - if (!strcmp(buf, gt.tune)) { - /* Search for the drawbridge */ - for (y = u.uy - 2; y <= u.uy + 2; y++) - for (x = u.ux - 2; x <= u.ux + 2; x++) { - if (!isok(x, y)) - continue; - if (find_drawbridge(&x, &y)) { - /* tune now fully known */ - u.uevent.uheard_tune = 2; - record_achievement(ACH_TUNE); - if (levl[x][y].typ == DRAWBRIDGE_DOWN) { - if (!rn2(5)) { - /* Future improvement: if flags is ever - * expanded beyond 5 bits, could set a bit - * here to make the mechanism continue to be - * stuck until some condition is met, such - * as opening/closing magic used on it */ - pline("The mechanism seems to get jammed."); - pline("It won't close."); - return ECMD_TIME; - } - close_drawbridge(x, y); + You(!Deaf ? "extract a strange sound from %s!" + : "can feel %s emitting vibrations.", the(xname(instr))); + Hero_playnotes(obj_to_instr(instr), buf, 50); + + + /* Check if there was the Stronghold drawbridge near + * and if the tune conforms to what we're waiting for. + */ + if (Is_stronghold(&u.uz)) { + exercise(A_WIS, TRUE); /* just for trying */ + if (!strcmp(buf, svt.tune)) { + /* Search for the drawbridge */ + for (y = u.uy - 2; y <= u.uy + 2; y++) + for (x = u.ux - 2; x <= u.ux + 2; x++) { + if (!isok(x, y)) + continue; + if (find_drawbridge(&x, &y)) { + /* tune now fully known */ + u.uevent.uheard_tune = 2; + record_achievement(ACH_TUNE); + if (levl[x][y].typ == DRAWBRIDGE_DOWN) { + if (!rn2(5)) { + /* Future improvement: if flags is ever + * expanded beyond 5 bits, could set a bit + * here to make the mechanism continue to be + * stuck until some condition is met, such + * as opening/closing magic used on it */ + /* note: flags is now 6 bits. */ + pline("The mechanism seems to get jammed."); + pline("It won't close."); + return ECMD_TIME; } - else - open_drawbridge(x, y); - return ECMD_TIME; + close_drawbridge(x, y); } + else + open_drawbridge(x, y); + return ECMD_TIME; } - } else if (!Deaf) { - if (u.uevent.uheard_tune < 1) - u.uevent.uheard_tune = 1; - /* Okay, it wasn't the right tune, but perhaps - * we can give the player some hints like in the - * Mastermind game */ - ok = FALSE; - for (y = u.uy - 2; y <= u.uy + 2 && !ok; y++) - for (x = u.ux - 2; x <= u.ux + 2 && !ok; x++) - if (isok(x, y)) - if (IS_DRAWBRIDGE(levl[x][y].typ) - || is_drawbridge_wall(x, y) >= 0) - ok = TRUE; - if (ok) { /* There is a drawbridge near */ - int tumblers, gears; - boolean matched[5]; - - tumblers = gears = 0; - for (x = 0; x < 5; x++) - matched[x] = FALSE; - - for (x = 0; x < (int) strlen(buf); x++) - if (x < 5) { - if (buf[x] == gt.tune[x]) { - gears++; - matched[x] = TRUE; - } else { - for (y = 0; y < 5; y++) - if (!matched[y] && buf[x] == gt.tune[y] - && buf[y] != gt.tune[y]) { - tumblers++; - matched[y] = TRUE; - break; - } - } - } - if (tumblers) { - if (gears) { - Soundeffect(se_tumbler_click, 50); - Soundeffect(se_gear_turn, 50); - You_hear("%d tumbler%s click and %d gear%s turn.", - tumblers, plur(tumblers), gears, - plur(gears)); + } + } else if (!Deaf) { + if (u.uevent.uheard_tune < 1) + u.uevent.uheard_tune = 1; + /* Okay, it wasn't the right tune, but perhaps + * we can give the player some hints like in the + * Mastermind game */ + ok = FALSE; + for (y = u.uy - 2; y <= u.uy + 2 && !ok; y++) + for (x = u.ux - 2; x <= u.ux + 2 && !ok; x++) + if (isok(x, y)) + if (IS_DRAWBRIDGE(levl[x][y].typ) + || is_drawbridge_wall(x, y) >= 0) + ok = TRUE; + if (ok) { /* There is a drawbridge near */ + int tumblers, gears; + boolean matched[5]; + + tumblers = gears = 0; + for (x = 0; x < 5; x++) + matched[x] = FALSE; + + for (x = 0; x < (int) strlen(buf); x++) + if (x < 5) { + if (buf[x] == svt.tune[x]) { + gears++; + matched[x] = TRUE; } else { - Soundeffect(se_tumbler_click, 50); - You_hear("%d tumbler%s click.", tumblers, - plur(tumblers)); - } - } else if (gears) { - You_hear("%d gear%s turn.", gears, plur(gears)); - /* could only get `gears == 5' by playing five - correct notes followed by excess; otherwise, - tune would have matched above */ - if (gears == 5) { - u.uevent.uheard_tune = 2; - record_achievement(ACH_TUNE); + for (y = 0; y < 5; y++) + if (!matched[y] && buf[x] == svt.tune[y] + && buf[y] != svt.tune[y]) { + tumblers++; + matched[y] = TRUE; + break; + } } } + if (tumblers) { + if (gears) { + Soundeffect(se_tumbler_click, 50); + Soundeffect(se_gear_turn, 50); + You_hear("%d tumbler%s click and %d gear%s turn.", + tumblers, plur(tumblers), gears, + plur(gears)); + } else { + Soundeffect(se_tumbler_click, 50); + You_hear("%d tumbler%s click.", tumblers, + plur(tumblers)); + } + } else if (gears) { + You_hear("%d gear%s turn.", gears, plur(gears)); + /* could only get `gears == 5' by playing five + correct notes followed by excess; otherwise, + tune would have matched above */ + if (gears == 5) { + u.uevent.uheard_tune = 2; + record_achievement(ACH_TUNE); + } } } } - return ECMD_TIME; - } else - return do_improvisation(instr) ? ECMD_TIME : ECMD_OK; + } + return ECMD_TIME; nevermind: pline1(Never_mind); diff --git a/src/nhlobj.c b/src/nhlobj.c index ef83035289..6e97c3b87f 100644 --- a/src/nhlobj.c +++ b/src/nhlobj.c @@ -10,27 +10,28 @@ struct _lua_obj { struct obj *obj; }; -static struct _lua_obj *l_obj_check(lua_State *, int); -static int l_obj_add_to_container(lua_State *); -static int l_obj_gc(lua_State *); -static int l_obj_getcontents(lua_State *); -static int l_obj_isnull(lua_State *); -static int l_obj_new_readobjnam(lua_State *); -static int l_obj_nextobj(lua_State *); -static int l_obj_objects_to_table(lua_State *); -static int l_obj_placeobj(lua_State *); -static int l_obj_to_table(lua_State *); -static int l_obj_at(lua_State *); -static int l_obj_container(lua_State *); -static int l_obj_timer_has(lua_State *); -static int l_obj_timer_peek(lua_State *); -static int l_obj_timer_stop(lua_State *); -static int l_obj_timer_start(lua_State *); -static int l_obj_bury(lua_State *); +staticfn struct _lua_obj *l_obj_check(lua_State *, int); +staticfn int l_obj_add_to_container(lua_State *); +staticfn int l_obj_gc(lua_State *); +staticfn int l_obj_getcontents(lua_State *); +staticfn int l_obj_isnull(lua_State *); +staticfn int l_obj_new_readobjnam(lua_State *); +staticfn int l_obj_nextobj(lua_State *); +staticfn int l_obj_objects_to_table(lua_State *); +staticfn int l_obj_placeobj(lua_State *); +staticfn int l_obj_to_table(lua_State *); +staticfn int l_obj_at(lua_State *); +staticfn int l_obj_container(lua_State *); +staticfn int l_obj_timer_has(lua_State *); +staticfn int l_obj_timer_peek(lua_State *); +staticfn int l_obj_timer_stop(lua_State *); +staticfn int l_obj_timer_start(lua_State *); +staticfn int l_obj_bury(lua_State *); +staticfn struct _lua_obj *l_obj_push(lua_State *, struct obj *); #define lobj_is_ok(lo) ((lo) && (lo)->obj && (lo)->obj->where != OBJ_LUAFREE) -static struct _lua_obj * +staticfn struct _lua_obj * l_obj_check(lua_State *L, int indx) { struct _lua_obj *lo; @@ -42,7 +43,7 @@ l_obj_check(lua_State *L, int indx) return lo; } -static int +staticfn int l_obj_gc(lua_State *L) { struct obj *obj, *otmp; @@ -68,10 +69,11 @@ l_obj_gc(lua_State *L) return 0; } -static struct _lua_obj * +staticfn struct _lua_obj * l_obj_push(lua_State *L, struct obj *otmp) { - struct _lua_obj *lo = (struct _lua_obj *)lua_newuserdata(L, sizeof(struct _lua_obj)); + struct _lua_obj *lo + = (struct _lua_obj *) lua_newuserdata(L, sizeof (struct _lua_obj)); luaL_getmetatable(L, "obj"); lua_setmetatable(L, -2); @@ -91,7 +93,7 @@ nhl_push_obj(lua_State *L, struct obj *otmp) /* local o = obj.new("large chest"); local cobj = o:contents(); */ -static int +staticfn int l_obj_getcontents(lua_State *L) { struct _lua_obj *lo = l_obj_check(L, 1); @@ -106,9 +108,9 @@ l_obj_getcontents(lua_State *L) /* Puts object inside another object. */ /* local box = obj.new("large chest"); - box.addcontent(obj.new("rock")); + box:addcontent(obj.new("rock")); */ -static int +staticfn int l_obj_add_to_container(lua_State *L) { struct _lua_obj *lobox = l_obj_check(L, 1); @@ -126,9 +128,10 @@ l_obj_add_to_container(lua_State *L) /* was lo->obj merged? */ if (otmp != lo->obj) { - lo->obj->lua_ref_cnt += refs; lo->obj = otmp; + lo->obj->lua_ref_cnt += refs; } + lobox->obj->owt = weight(lobox->obj); return 0; } @@ -164,7 +167,7 @@ DISABLE_WARNING_UNREACHABLE_CODE /* local odata = obj.class(otbl.otyp); */ /* local odata = obj.class(obj.new("rock")); */ /* local odata = o:class(); */ -static int +staticfn int l_obj_objects_to_table(lua_State *L) { int argc = lua_gettop(L); @@ -188,6 +191,7 @@ l_obj_objects_to_table(lua_State *L) if (otyp == -1) { nhl_error(L, "l_obj_objects_to_table: Wrong args"); + /*NOTREACHED*/ return 0; } @@ -239,7 +243,7 @@ RESTORE_WARNING_UNREACHABLE_CODE object fields. local o = obj.new("rock"); local otbl = o:totable(); */ -static int +staticfn int l_obj_to_table(lua_State *L) { struct _lua_obj *lo = l_obj_check(L, 1); @@ -291,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 @@ -311,7 +316,7 @@ l_obj_to_table(lua_State *L) nhl_add_table_entry_int(L, "globby", obj->globby); nhl_add_table_entry_int(L, "greased", obj->greased); nhl_add_table_entry_int(L, "nomerge", obj->nomerge); - nhl_add_table_entry_int(L, "was_thrown", obj->was_thrown); + nhl_add_table_entry_int(L, "how_lost", obj->how_lost); nhl_add_table_entry_int(L, "in_use", obj->in_use); nhl_add_table_entry_int(L, "bypass", obj->bypass); nhl_add_table_entry_int(L, "cknown", obj->cknown); @@ -340,7 +345,7 @@ DISABLE_WARNING_UNREACHABLE_CODE /* create a new object via wishing routine */ /* local o = obj.new("rock"); */ -static int +staticfn int l_obj_new_readobjnam(lua_State *L) { int argc = lua_gettop(L); @@ -348,9 +353,11 @@ l_obj_new_readobjnam(lua_State *L) if (argc == 1) { char buf[BUFSZ]; struct obj *otmp; + Sprintf(buf, "%s", luaL_checkstring(L, 1)); lua_pop(L, 1); - otmp = readobjnam(buf, NULL); + if ((otmp = readobjnam(buf, NULL)) == &hands_obj) + otmp = NULL; (void) l_obj_push(L, otmp); return 1; } else @@ -361,7 +368,7 @@ l_obj_new_readobjnam(lua_State *L) /* Get the topmost object on the map at x,y */ /* local o = obj.at(x, y); */ -static int +staticfn int l_obj_at(lua_State *L) { int argc = lua_gettop(L); @@ -374,7 +381,7 @@ l_obj_at(lua_State *L) cvt_to_abscoord(&x, &y); lua_pop(L, 2); - (void) l_obj_push(L, gl.level.objects[x][y]); + (void) l_obj_push(L, svl.level.objects[x][y]); return 1; } else nhl_error(L, "l_obj_at: Wrong args"); @@ -385,7 +392,7 @@ l_obj_at(lua_State *L) /* Place an object on the map at (x,y). local o = obj.new("rock"); o:placeobj(u.ux, u.uy); */ -static int +staticfn int l_obj_placeobj(lua_State *L) { int argc = lua_gettop(L); @@ -417,7 +424,7 @@ RESTORE_WARNING_UNREACHABLE_CODE local o2 = o:next(true); local firstobj = obj.next(); */ -static int +staticfn int l_obj_nextobj(lua_State *L) { int argc = lua_gettop(L); @@ -441,7 +448,7 @@ l_obj_nextobj(lua_State *L) /* Get the container object is in */ /* local box = o:container(); */ -static int +staticfn int l_obj_container(lua_State *L) { struct _lua_obj *lo = l_obj_check(L, 1); @@ -455,7 +462,7 @@ l_obj_container(lua_State *L) /* Is the object a null? */ /* local badobj = o:isnull(); */ -static int +staticfn int l_obj_isnull(lua_State *L) { struct _lua_obj *lo = l_obj_check(L, 1); @@ -468,7 +475,7 @@ DISABLE_WARNING_UNREACHABLE_CODE /* does object have a timer of certain type? */ /* local hastimer = o:has_timer("rot-organic"); */ -static int +staticfn int l_obj_timer_has(lua_State *L) { int argc = lua_gettop(L); @@ -489,11 +496,10 @@ l_obj_timer_has(lua_State *L) return 0; } - /* peek at an object timer. return the turn when timer triggers. returns 0 if no such timer attached to the object. */ /* local timeout = o:peek_timer("hatch-egg"); */ -static int +staticfn int l_obj_timer_peek(lua_State *L) { int argc = lua_gettop(L); @@ -520,7 +526,7 @@ l_obj_timer_peek(lua_State *L) without a timer type parameter, stops all timers for the object. */ /* local timeout = o:stop_timer("rot-organic"); */ /* o:stop_timer(); */ -static int +staticfn int l_obj_timer_stop(lua_State *L) { int argc = lua_gettop(L); @@ -552,7 +558,7 @@ RESTORE_WARNING_UNREACHABLE_CODE /* start an object timer. */ /* o:start_timer("hatch-egg", 10); */ -static int +staticfn int l_obj_timer_start(lua_State *L) { int argc = lua_gettop(L); @@ -576,7 +582,7 @@ l_obj_timer_start(lua_State *L) false otherwise. */ /* local ogone = o:bury(); */ /* local ogone = o:bury(5,5); */ -static int +staticfn int l_obj_bury(lua_State *L) { int argc = lua_gettop(L); diff --git a/src/nhlsel.c b/src/nhlsel.c index 8694f7147e..2d0f92b4c0 100644 --- a/src/nhlsel.c +++ b/src/nhlsel.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 nhlua.c $NHDT-Date: 1582675449 2020/02/26 00:04:09 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.20 $ */ +/* NetHack 3.7 nhlua.c $NHDT-Date: 1704225560 2024/01/02 19:59:20 $ $NHDT-Branch: keni-luabits2 $:$NHDT-Revision: 1.61 $ */ /* Copyright (c) 2018 by Pasi Kallinen */ /* NetHack may be freely redistributed. See license for details. */ @@ -7,41 +7,41 @@ struct selectionvar *l_selection_check(lua_State *, int); -static struct selectionvar *l_selection_push_new(lua_State *); +staticfn struct selectionvar *l_selection_push_new(lua_State *); /* lua_CFunction prototypes */ -static int l_selection_new(lua_State *); -static int l_selection_clone(lua_State *); -static int l_selection_numpoints(lua_State *); -static int l_selection_getpoint(lua_State *); -static int l_selection_setpoint(lua_State *); -static int l_selection_filter_percent(lua_State *); -static int l_selection_rndcoord(lua_State *); -static int l_selection_room(lua_State *); -static int l_selection_getbounds(lua_State *); -static boolean params_sel_2coords(lua_State *, struct selectionvar **, +staticfn int l_selection_new(lua_State *); +staticfn int l_selection_clone(lua_State *); +staticfn int l_selection_numpoints(lua_State *); +staticfn int l_selection_getpoint(lua_State *); +staticfn int l_selection_setpoint(lua_State *); +staticfn int l_selection_filter_percent(lua_State *); +staticfn int l_selection_rndcoord(lua_State *); +staticfn int l_selection_room(lua_State *); +staticfn int l_selection_getbounds(lua_State *); +staticfn boolean params_sel_2coords(lua_State *, struct selectionvar **, coordxy *, coordxy *, coordxy *, coordxy *); -static int l_selection_line(lua_State *); -static int l_selection_randline(lua_State *); -static int l_selection_rect(lua_State *); -static int l_selection_fillrect(lua_State *); -static int l_selection_grow(lua_State *); -static int l_selection_filter_mapchar(lua_State *); -static int l_selection_match(lua_State *); -static int l_selection_flood(lua_State *); -static int l_selection_circle(lua_State *); -static int l_selection_ellipse(lua_State *); -static int l_selection_gradient(lua_State *); -static int l_selection_iterate(lua_State *); -static int l_selection_gc(lua_State *); -static int l_selection_not(lua_State *); -static int l_selection_and(lua_State *); -static int l_selection_or(lua_State *); -static int l_selection_xor(lua_State *); +staticfn int l_selection_line(lua_State *); +staticfn int l_selection_randline(lua_State *); +staticfn int l_selection_rect(lua_State *); +staticfn int l_selection_fillrect(lua_State *); +staticfn int l_selection_grow(lua_State *); +staticfn int l_selection_filter_mapchar(lua_State *); +staticfn int l_selection_match(lua_State *); +staticfn int l_selection_flood(lua_State *); +staticfn int l_selection_circle(lua_State *); +staticfn int l_selection_ellipse(lua_State *); +staticfn int l_selection_gradient(lua_State *); +staticfn int l_selection_iterate(lua_State *); +staticfn int l_selection_gc(lua_State *); +staticfn int l_selection_not(lua_State *); +staticfn int l_selection_and(lua_State *); +staticfn int l_selection_or(lua_State *); +staticfn int l_selection_xor(lua_State *); /* There doesn't seem to be a point in having a l_selection_add since it would * do the same thing as l_selection_or. The addition operator is mapped to * l_selection_or. */ -static int l_selection_sub(lua_State *); +staticfn int l_selection_sub(lua_State *); #if 0 /* the following do not appear to currently be used and because they are static, the OSX @@ -49,8 +49,8 @@ static int l_selection_sub(lua_State *); if ifdef'd out the prototype here and the function body below. */ -static int l_selection_ipairs(lua_State *); -static struct selectionvar *l_selection_to(lua_State *, int); +staticfn int l_selection_ipairs(lua_State *); +staticfn struct selectionvar *l_selection_to(lua_State *, int); #endif struct selectionvar * @@ -65,7 +65,7 @@ l_selection_check(lua_State *L, int index) return sel; } -static int +staticfn int l_selection_gc(lua_State *L) { struct selectionvar *sel = l_selection_check(L, 1); @@ -76,10 +76,11 @@ l_selection_gc(lua_State *L) } #if 0 -static struct selectionvar * +staticfn struct selectionvar * l_selection_to(lua_State *L, int index) { - struct selectionvar *sel = (struct selectionvar *)lua_touserdata(L, index); + struct selectionvar *sel + = (struct selectionvar *) lua_touserdata(L, index); if (!sel) nhl_error(L, "Selection error"); @@ -88,12 +89,12 @@ l_selection_to(lua_State *L, int index) #endif /* push a new selection into lua stack, return the selectionvar */ -static struct selectionvar * +staticfn struct selectionvar * l_selection_push_new(lua_State *L) { struct selectionvar *tmp = selection_new(); - struct selectionvar - *sel = (struct selectionvar *) lua_newuserdata(L, sizeof(struct selectionvar)); + struct selectionvar *sel + = (struct selectionvar *) lua_newuserdata(L, sizeof (struct selectionvar)); luaL_getmetatable(L, "selection"); lua_setmetatable(L, -2); @@ -109,8 +110,8 @@ l_selection_push_new(lua_State *L) void l_selection_push_copy(lua_State *L, struct selectionvar *tmp) { - struct selectionvar - *sel = (struct selectionvar *) lua_newuserdata(L, sizeof(struct selectionvar)); + struct selectionvar *sel + = (struct selectionvar *) lua_newuserdata(L, sizeof (struct selectionvar)); luaL_getmetatable(L, "selection"); lua_setmetatable(L, -2); @@ -121,7 +122,7 @@ l_selection_push_copy(lua_State *L, struct selectionvar *tmp) /* local sel = selection.new(); */ -static int +staticfn int l_selection_new(lua_State *L) { (void) l_selection_push_new(L); @@ -130,7 +131,7 @@ l_selection_new(lua_State *L) /* Replace the topmost selection in the stack with a clone of it. */ /* local sel = selection.clone(sel); */ -static int +staticfn int l_selection_clone(lua_State *L) { struct selectionvar *sel = l_selection_check(L, 1); @@ -152,8 +153,8 @@ DISABLE_WARNING_UNREACHABLE_CODE /* local sel = selection.set(); */ /* local sel = sel:set(); */ /* local sel = selection.set(sel); */ -/* TODO: allow setting multiple coordinates at once: set({x,y}, {x,y}, ...); */ -static int +/* TODO: allow setting multiple coords at once: set({x1,y1},{x2,y2},...); */ +staticfn int l_selection_setpoint(lua_State *L) { struct selectionvar *sel = (struct selectionvar *) 0; @@ -197,13 +198,13 @@ l_selection_setpoint(lua_State *L) } /* local numpoints = selection.numpoints(sel); */ -static int +staticfn int l_selection_numpoints(lua_State *L) { struct selectionvar *sel = l_selection_check(L, 1); coordxy x, y; int ret = 0; - NhRect rect; + NhRect rect = cg.zeroNhRect; selection_getbounds(sel, &rect); @@ -218,7 +219,7 @@ l_selection_numpoints(lua_State *L) } /* local value = selection.get(sel, x, y); */ -static int +staticfn int l_selection_getpoint(lua_State *L) { struct selectionvar *sel = l_selection_check(L, 1); @@ -240,7 +241,8 @@ l_selection_getpoint(lua_State *L) crd = SP_COORD_PACK_RANDOM(0); else crd = SP_COORD_PACK(x,y); - get_location_coord(&x, &y, ANY_LOC, gc.coder ? gc.coder->croom : NULL, crd); + get_location_coord(&x, &y, ANY_LOC, + gc.coder ? gc.coder->croom : NULL, crd); val = selection_getpoint(x, y, sel); lua_settop(L, 0); @@ -253,7 +255,7 @@ RESTORE_WARNING_UNREACHABLE_CODE /* local s = selection.negate(sel); */ /* local s = selection.negate(); */ /* local s = sel:negate(); */ -static int +staticfn int l_selection_not(lua_State *L) { int argc = lua_gettop(L); @@ -274,20 +276,22 @@ l_selection_not(lua_State *L) } /* local sel = selection.area(4,5, 40,10) & selection.rect(7,8, 60,14); */ -static int +staticfn int l_selection_and(lua_State *L) { - int x,y; + int x, y; struct selectionvar *sela = l_selection_check(L, 1); struct selectionvar *selb = l_selection_check(L, 2); struct selectionvar *selr = l_selection_push_new(L); - NhRect rect; + NhRect rect = cg.zeroNhRect; rect_bounds(sela->bounds, selb->bounds, &rect); for (x = rect.lx; x <= rect.hx; x++) for (y = rect.ly; y <= rect.hy; y++) { - int val = selection_getpoint(x, y, sela) & selection_getpoint(x, y, selb); + int val = (selection_getpoint(x, y, sela) + & selection_getpoint(x, y, selb)); + selection_setpoint(x, y, selr, val); } @@ -297,20 +301,22 @@ l_selection_and(lua_State *L) } /* local sel = selection.area(4,5, 40,10) | selection.rect(7,8, 60,14); */ -static int +staticfn int l_selection_or(lua_State *L) { int x,y; struct selectionvar *sela = l_selection_check(L, 1); struct selectionvar *selb = l_selection_check(L, 2); struct selectionvar *selr = l_selection_push_new(L); - NhRect rect; + NhRect rect = cg.zeroNhRect; rect_bounds(sela->bounds, selb->bounds, &rect); for (x = rect.lx; x <= rect.hx; x++) for (y = rect.ly; y <= rect.hy; y++) { - int val = selection_getpoint(x, y, sela) | selection_getpoint(x, y, selb); + int val = (selection_getpoint(x, y, sela) + | selection_getpoint(x, y, selb)); + selection_setpoint(x, y, selr, val); } selr->bounds = rect; @@ -321,24 +327,26 @@ l_selection_or(lua_State *L) } /* local sel = selection.area(4,5, 40,10) ~ selection.rect(7,8, 60,14); */ -static int +staticfn int l_selection_xor(lua_State *L) { int x,y; struct selectionvar *sela = l_selection_check(L, 1); struct selectionvar *selb = l_selection_check(L, 2); struct selectionvar *selr = l_selection_push_new(L); - NhRect rect; + NhRect rect = cg.zeroNhRect; rect_bounds(sela->bounds, selb->bounds, &rect); for (x = rect.lx; x <= rect.hx; x++) for (y = rect.ly; y <= rect.hy; y++) { - int val = selection_getpoint(x, y, sela) ^ selection_getpoint(x, y, selb); + int val = (selection_getpoint(x, y, sela) + ^ selection_getpoint(x, y, selb)); + selection_setpoint(x, y, selr, val); } - /* this may have created a smaller or irregular selection with bounds_dirty - * set to true - update its boundaries */ + /* this may have created a smaller or irregular selection with + * bounds_dirty set to true - update its boundaries */ selection_recalc_bounds(selr); lua_remove(L, 1); @@ -348,14 +356,14 @@ l_selection_xor(lua_State *L) /* local sel = selection.area(10,10, 20,20) - selection.area(14,14, 17,17) * - i.e. points that are in A but not in B */ -static int +staticfn int l_selection_sub(lua_State *L) { int x,y; struct selectionvar *sela = l_selection_check(L, 1); struct selectionvar *selb = l_selection_check(L, 2); struct selectionvar *selr = l_selection_push_new(L); - NhRect rect; + NhRect rect = cg.zeroNhRect; rect_bounds(sela->bounds, selb->bounds, &rect); @@ -366,8 +374,8 @@ l_selection_sub(lua_State *L) int val = (a_pt ^ b_pt) & a_pt; selection_setpoint(x, y, selr, val); } - /* this may have created a smaller or irregular selection with bounds_dirty - * set to true - update its boundaries */ + /* this may have created a smaller or irregular selection with + * bounds_dirty set to true - update its boundaries */ selection_recalc_bounds(selr); lua_remove(L, 1); @@ -376,7 +384,7 @@ l_selection_sub(lua_State *L) } /* local s = selection.percentage(sel, 50); */ -static int +staticfn int l_selection_filter_percent(lua_State *L) { int argc = lua_gettop(L); @@ -394,7 +402,7 @@ l_selection_filter_percent(lua_State *L) /* local pt = selection.rndcoord(sel); */ /* local pt = selection.rndcoord(sel, 1); */ -static int +staticfn int l_selection_rndcoord(lua_State *L) { struct selectionvar *sel = l_selection_check(L, 1); @@ -419,7 +427,7 @@ l_selection_rndcoord(lua_State *L) } /* local s = selection.room(); */ -static int +staticfn int l_selection_room(lua_State *L) { struct selectionvar *sel; @@ -429,7 +437,7 @@ l_selection_room(lua_State *L) if (argc == 1) { int i = luaL_checkinteger(L, -1); - croom = (i >= 0 && i < gn.nroom) ? &gr.rooms[i] : NULL; + croom = (i >= 0 && i < svn.nroom) ? &svr.rooms[i] : NULL; } sel = selection_from_mkroom(croom); @@ -441,11 +449,11 @@ l_selection_room(lua_State *L) } /* local rect = sel:bounds(); */ -static int +staticfn int l_selection_getbounds(lua_State *L) { struct selectionvar *sel = l_selection_check(L, 1); - NhRect rect; + NhRect rect = cg.zeroNhRect; selection_getbounds(sel, &rect); lua_settop(L, 0); @@ -463,7 +471,7 @@ l_selection_getbounds(lua_State *L) */ /* function(selection, x1,y1, x2,y2) */ /* selection:function(x1,y1, x2,y2) */ -static boolean +staticfn boolean params_sel_2coords(lua_State *L, struct selectionvar **sel, coordxy *x1, coordxy *y1, coordxy *x2, coordxy *y2) { @@ -496,7 +504,7 @@ params_sel_2coords(lua_State *L, struct selectionvar **sel, /* local s = selection.line(sel, x1,y1, x2,y2); */ /* local s = selection.line(x1,y1, x2,y2); */ /* s:line(x1,y1, x2,y2); */ -static int +staticfn int l_selection_line(lua_State *L) { struct selectionvar *sel = NULL; @@ -506,8 +514,10 @@ l_selection_line(lua_State *L) nhl_error(L, "selection.line: illegal arguments"); } - get_location_coord(&x1, &y1, ANY_LOC, gc.coder ? gc.coder->croom : NULL, SP_COORD_PACK(x1,y1)); - get_location_coord(&x2, &y2, ANY_LOC, gc.coder ? gc.coder->croom : NULL, SP_COORD_PACK(x2,y2)); + get_location_coord(&x1, &y1, ANY_LOC, gc.coder ? gc.coder->croom : NULL, + SP_COORD_PACK(x1, y1)); + get_location_coord(&x2, &y2, ANY_LOC, gc.coder ? gc.coder->croom : NULL, + SP_COORD_PACK(x2, y2)); (void) l_selection_clone(L); sel = l_selection_check(L, 2); @@ -516,7 +526,7 @@ l_selection_line(lua_State *L) } /* local s = selection.rect(sel, x1,y1, x2,y2); */ -static int +staticfn int l_selection_rect(lua_State *L) { struct selectionvar *sel = NULL; @@ -544,7 +554,7 @@ l_selection_rect(lua_State *L) /* local s = selection.fillrect(x1,y1, x2,y2); */ /* s:fillrect(x1,y1, x2,y2); */ /* selection.area(x1,y1, x2,y2); */ -static int +staticfn int l_selection_fillrect(lua_State *L) { struct selectionvar *sel = NULL; @@ -576,16 +586,16 @@ l_selection_fillrect(lua_State *L) /* local s = selection.randline(x1,y1, x2,y2, roughness); */ /* TODO: selection.randline(x1,y1, x2,y2, roughness); */ /* TODO: selection.randline({x1,y1}, {x2,y2}, roughness); */ -static int +staticfn int l_selection_randline(lua_State *L) { int argc = lua_gettop(L); - struct selectionvar *sel = (struct selectionvar *) 0; + struct selectionvar *sel; coordxy x1 = 0, y1 = 0, x2 = 0, y2 = 0; int roughness = 7; if (argc == 6) { - sel = l_selection_check(L, 1); + (void) l_selection_check(L, 1); x1 = (coordxy) luaL_checkinteger(L, 2); y1 = (coordxy) luaL_checkinteger(L, 3); x2 = (coordxy) luaL_checkinteger(L, 4); @@ -600,13 +610,13 @@ l_selection_randline(lua_State *L) roughness = (int) luaL_checkinteger(L, 5); lua_pop(L, 5); (void) l_selection_new(L); - sel = l_selection_check(L, 1); + (void) l_selection_check(L, 1); } - get_location_coord(&x1, &y1, ANY_LOC, - gc.coder ? gc.coder->croom : NULL, SP_COORD_PACK(x1, y1)); - get_location_coord(&x2, &y2, ANY_LOC, - gc.coder ? gc.coder->croom : NULL, SP_COORD_PACK(x2, y2)); + get_location_coord(&x1, &y1, ANY_LOC, gc.coder ? gc.coder->croom : NULL, + SP_COORD_PACK(x1, y1)); + get_location_coord(&x2, &y2, ANY_LOC, gc.coder ? gc.coder->croom : NULL, + SP_COORD_PACK(x2, y2)); (void) l_selection_clone(L); sel = l_selection_check(L, 2); @@ -616,13 +626,16 @@ l_selection_randline(lua_State *L) /* local s = selection.grow(sel); */ /* local s = selection.grow(sel, "north"); */ -static int +staticfn int l_selection_grow(lua_State *L) { + const char *const growdirs[] = { + "all", "random", "north", "west", "east", "south", NULL + }; + const int growdirs2i[] = { + W_ANY, W_RANDOM, W_NORTH, W_WEST, W_EAST, W_SOUTH, 0 + }; int argc = lua_gettop(L); - const char *const growdirs[] = { "all", "random", "north", "west", "east", "south", NULL }; - const int growdirs2i[] = { W_ANY, W_RANDOM, W_NORTH, W_WEST, W_EAST, W_SOUTH, 0 }; - struct selectionvar *sel = l_selection_check(L, 1); int dir = growdirs2i[luaL_checkoption(L, 2, "all", growdirs)]; @@ -637,7 +650,7 @@ l_selection_grow(lua_State *L) /* local s = selection.filter_mapchar(sel, mapchar, lit); */ -static int +staticfn int l_selection_filter_mapchar(lua_State *L) { int argc = lua_gettop(L); @@ -662,7 +675,7 @@ l_selection_filter_mapchar(lua_State *L) } /* local s = selection.match([[...]]); */ -static int +staticfn int l_selection_match(lua_State *L) { int argc = lua_gettop(L); @@ -702,7 +715,7 @@ l_selection_match(lua_State *L) /* local s = selection.floodfill(x,y); */ /* local s = selection.floodfill(x,y, diagonals); */ -static int +staticfn int l_selection_flood(lua_State *L) { int argc = lua_gettop(L); @@ -723,8 +736,8 @@ l_selection_flood(lua_State *L) /*NOTREACHED*/ } - get_location_coord(&x, &y, ANY_LOC, - gc.coder ? gc.coder->croom : NULL, SP_COORD_PACK(x, y)); + get_location_coord(&x, &y, ANY_LOC, gc.coder ? gc.coder->croom : NULL, + SP_COORD_PACK(x, y)); if (isok(x, y)) { set_floodfillchk_match_under(levl[x][y].typ); @@ -738,7 +751,7 @@ l_selection_flood(lua_State *L) /* local s = selection.circle(x, y, radius, filled); */ /* local s = selection.circle(sel, x, y, radius); */ /* local s = selection.circle(sel, x, y, radius, filled); */ -static int +staticfn int l_selection_circle(lua_State *L) { int argc = lua_gettop(L); @@ -773,8 +786,8 @@ l_selection_circle(lua_State *L) /*NOTREACHED*/ } - get_location_coord(&x, &y, ANY_LOC, - gc.coder ? gc.coder->croom : NULL, SP_COORD_PACK(x, y)); + get_location_coord(&x, &y, ANY_LOC, gc.coder ? gc.coder->croom : NULL, + SP_COORD_PACK(x, y)); selection_do_ellipse(sel, x, y, r, r, !filled); @@ -786,7 +799,7 @@ l_selection_circle(lua_State *L) /* local s = selection.ellipse(x, y, radius1, radius2, filled); */ /* local s = selection.ellipse(sel, x, y, radius1, radius2); */ /* local s = selection.ellipse(sel, x, y, radius1, radius2, filled); */ -static int +staticfn int l_selection_ellipse(lua_State *L) { int argc = lua_gettop(L); @@ -824,8 +837,8 @@ l_selection_ellipse(lua_State *L) /*NOTREACHED*/ } - get_location_coord(&x, &y, ANY_LOC, - gc.coder ? gc.coder->croom : NULL, SP_COORD_PACK(x, y)); + get_location_coord(&x, &y, ANY_LOC, gc.coder ? gc.coder->croom : NULL, + SP_COORD_PACK(x, y)); selection_do_ellipse(sel, x, y, r1, r2, !filled); @@ -838,7 +851,7 @@ l_selection_ellipse(lua_State *L) * non-obvious argument order. */ /* selection.gradient({ type = "radial", x = 3, y = 5, x2 = 10, y2 = 12, * mindist = 4, maxdist = 10, limited = false }); */ -static int +staticfn int l_selection_gradient(lua_State *L) { int argc = lua_gettop(L); @@ -847,8 +860,8 @@ l_selection_gradient(lua_State *L) * if they are set, the gradient is centered on a (x,y) to (x2,y2) line */ coordxy x = 0, y = 0, x2 = -1, y2 = -1; /* points are always added within mindist of the center; the chance for a - * point between mindist and maxdist to be added to the selection starts at - * 100% at mindist and decreases linearly to 0% at maxdist */ + * point between mindist and maxdist to be added to the selection starts + * at 100% at mindist and decreases linearly to 0% at maxdist */ coordxy mindist = 0, maxdist = 0; long type = 0; static const char *const gradtypes[] = { @@ -867,8 +880,8 @@ l_selection_gradient(lua_State *L) y2 = (coordxy) get_table_int_opt(L, "y2", -1); cvt_to_abscoord(&x, &y); cvt_to_abscoord(&x2, &y2); - /* maxdist is required because there's no obvious default value for it, - * whereas mindist has an obvious defalt of 0 */ + /* maxdist is required because there's no obvious default value for + * it, whereas mindist has an obvious default of 0 */ maxdist = get_table_int(L, "maxdist"); mindist = get_table_int_opt(L, "mindist", 0); @@ -896,15 +909,16 @@ l_selection_gradient(lua_State *L) /* sel:iterate(function(x,y) ... end); * The x, y coordinates passed to the function are map- or room-relative - * rather than absolute, unless there has been no previous map or room defined. + * rather than absolute, unless there has been no previous map or room + * defined. */ -static int +staticfn int l_selection_iterate(lua_State *L) { int argc = lua_gettop(L); struct selectionvar *sel = (struct selectionvar *) 0; int x, y; - NhRect rect; + NhRect rect = cg.zeroNhRect; if (argc == 2 && lua_type(L, 2) == LUA_TFUNCTION) { sel = l_selection_check(L, 1); @@ -917,9 +931,9 @@ l_selection_iterate(lua_State *L) lua_pushvalue(L, 2); lua_pushinteger(L, tmpx); lua_pushinteger(L, tmpy); - if (nhl_pcall(L, 2, 0)) { - impossible("Lua error: %s", lua_tostring(L, -1)); - /* abort the loops to prevent possible error cascade */ + if (nhl_pcall_handle(L, 2, 0, "l_selection_iterate", + NHLpa_impossible)) { + /* abort loops to prevent possible error cascade */ goto out; } } @@ -927,7 +941,7 @@ l_selection_iterate(lua_State *L) nhl_error(L, "wrong parameters"); /*NOTREACHED*/ } -out: + out: return 0; } diff --git a/src/nhlua.c b/src/nhlua.c index d87bcc6393..3b3a86c207 100644 --- a/src/nhlua.c +++ b/src/nhlua.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 nhlua.c $NHDT-Date: 1673740532 2023/01/14 23:55:32 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.95 $ */ +/* NetHack 3.7 nhlua.c $NHDT-Date: 1711034373 2024/03/21 15:19:33 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.141 $ */ /* Copyright (c) 2018 by Pasi Kallinen */ /* NetHack may be freely redistributed. See license for details. */ @@ -23,61 +23,73 @@ /* */ +struct e; + /* lua_CFunction prototypes */ #ifdef DUMPLOG -static int nhl_dump_fmtstr(lua_State *); +staticfn int nhl_dump_fmtstr(lua_State *); #endif /* DUMPLOG */ -static int nhl_dnum_name(lua_State *); -static int nhl_stairways(lua_State *); -static int nhl_pushkey(lua_State *); -static int nhl_doturn(lua_State *); -static int nhl_debug_flags(lua_State *); -static int nhl_timer_has_at(lua_State *); -static int nhl_timer_peek_at(lua_State *); -static int nhl_timer_stop_at(lua_State *); -static int nhl_timer_start_at(lua_State *); -static int nhl_get_cmd_key(lua_State *); -static int nhl_callback(lua_State *); -static int nhl_gamestate(lua_State *); -static int nhl_test(lua_State *); -static int nhl_getmap(lua_State *); -static char splev_typ2chr(schar); -static int nhl_gettrap(lua_State *); -static int nhl_deltrap(lua_State *); +staticfn int nhl_dnum_name(lua_State *); +staticfn int nhl_stairways(lua_State *); +staticfn int nhl_pushkey(lua_State *); +staticfn int nhl_doturn(lua_State *); +staticfn int nhl_debug_flags(lua_State *); +staticfn int nhl_timer_has_at(lua_State *); +staticfn int nhl_timer_peek_at(lua_State *); +staticfn int nhl_timer_stop_at(lua_State *); +staticfn int nhl_timer_start_at(lua_State *); +staticfn int nhl_get_cmd_key(lua_State *); +staticfn int nhl_callback(lua_State *); +staticfn int nhl_gamestate(lua_State *); +staticfn int nhl_test(lua_State *); +staticfn int nhl_getmap(lua_State *); +staticfn char splev_typ2chr(schar); +staticfn int nhl_gettrap(lua_State *); +staticfn int nhl_deltrap(lua_State *); #if 0 -static int nhl_setmap(lua_State *); +staticfn int nhl_setmap(lua_State *); #endif -static int nhl_impossible(lua_State *); -static int nhl_pline(lua_State *); -static int nhl_verbalize(lua_State *); -static int nhl_parse_config(lua_State *); -static int nhl_menu(lua_State *); -static int nhl_text(lua_State *); -static int nhl_getlin(lua_State *); -static int nhl_makeplural(lua_State *); -static int nhl_makesingular(lua_State *); -static int nhl_s_suffix(lua_State *); -static int nhl_ing_suffix(lua_State *); -static int nhl_an(lua_State *); -static int nhl_rn2(lua_State *); -static int nhl_random(lua_State *); -static int nhl_level_difficulty(lua_State *); -static void init_nhc_data(lua_State *); -static int nhl_push_anything(lua_State *, int, void *); -static int nhl_meta_u_index(lua_State *); -static int nhl_meta_u_newindex(lua_State *); -static int nhl_u_clear_inventory(lua_State *); -static int nhl_u_giveobj(lua_State *); -static void init_u_data(lua_State *); +staticfn int nhl_impossible(lua_State *); +staticfn int nhl_pline(lua_State *); +staticfn int nhl_verbalize(lua_State *); +staticfn int nhl_parse_config(lua_State *); +staticfn int nhl_menu(lua_State *); +staticfn int nhl_text(lua_State *); +staticfn int nhl_getlin(lua_State *); +staticfn int nhl_makeplural(lua_State *); +staticfn int nhl_makesingular(lua_State *); +staticfn int nhl_s_suffix(lua_State *); +staticfn int nhl_ing_suffix(lua_State *); +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 void init_nhc_data(lua_State *); +staticfn int nhl_push_anything(lua_State *, int, void *); +staticfn int nhl_meta_u_index(lua_State *); +staticfn int nhl_meta_u_newindex(lua_State *); +staticfn int nhl_u_clear_inventory(lua_State *); +staticfn int nhl_u_giveobj(lua_State *); +staticfn void init_u_data(lua_State *); #ifdef notyet -static int nhl_set_package_path(lua_State *, const char *); +staticfn int nhl_set_package_path(lua_State *, const char *); #endif -static int traceback_handler(lua_State *); +staticfn int traceback_handler(lua_State *); +staticfn uint32_t nhl_getmeminuse(lua_State *); #ifdef NHL_SANDBOX -static void nhlL_openlibs(lua_State *, uint32_t); +staticfn void nhlL_openlibs(lua_State *, uint32_t); #endif -static lua_State *nhlL_newstate (nhl_sandbox_info *); -static void end_luapat(void); +staticfn void *nhl_alloc(void *, void *, size_t, size_t); +staticfn lua_State *nhlL_newstate (nhl_sandbox_info *, const char *); +staticfn void end_luapat(void); +staticfn int nhl_get_config(lua_State *); +staticfn int nhl_variable(lua_State *); +staticfn void nhl_clearfromtable(lua_State *, int, int, struct e *); +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 *); static const char *const nhcore_call_names[NUM_NHCORE_CALLS] = { "start_new_game", @@ -85,6 +97,8 @@ static const char *const nhcore_call_names[NUM_NHCORE_CALLS] = { "moveloop_turn", "game_exit", "getpos_tip", + "enter_tutorial", + "leave_tutorial", }; static boolean nhcore_call_available[NUM_NHCORE_CALLS]; @@ -92,16 +106,22 @@ static boolean nhcore_call_available[NUM_NHCORE_CALLS]; * Note that we use it for both memory use tracking and instruction counting. */ typedef struct nhl_user_data { - uint32_t flags; /* from nhl_sandbox_info */ + lua_State *L; /* because the allocator needs it */ + uint32_t flags; /* from nhl_sandbox_info */ + + uint32_t memlimit; + + uint32_t steps; /* current counter */ + uint32_t osteps; /* original steps value */ + uint32_t perpcall; /* per pcall steps value */ - uint32_t inuse; - uint32_t memlimit; + /* stats */ + uint32_t statctr; /* stats step reload count */ + int sid; /* id number (per state) */ + const char *name; /* for stats logging (per pcall) */ - uint32_t steps; /* current counter */ - uint32_t osteps; /* original steps value */ - uint32_t perpcall; /* per pcall steps value */ #ifdef NHL_SANDBOX - jmp_buf jb; + jmp_buf jb; #endif } nhl_user_data; @@ -110,12 +130,9 @@ static lua_State *luapat; /* instance for file pattern matching */ void l_nhcore_init(void) { -#if 1 - nhl_sandbox_info sbi = {NHL_SB_SAFE, 0, 0, 0}; -#else - /* Sample sbi for getting resource usage information. */ - nhl_sandbox_info sbi = {NHL_SB_SAFE|NHL_SB_REPORT2, 10000000, 10000000, 0}; -#endif + nhl_sandbox_info sbi = { NHL_SB_SAFE, 1 * 1024 * 1024, 0, + 1 * 1024 * 1024 }; + if ((gl.luacore = nhl_init(&sbi)) != 0) { if (!nhl_loadlua(gl.luacore, "nhcore.lua")) { gl.luacore = (lua_State *) 0; @@ -144,8 +161,8 @@ l_nhcore_call(int callidx) { int ltyp; - if (callidx < 0 || callidx >= NUM_NHCORE_CALLS - || !gl.luacore || !nhcore_call_available[callidx]) + if (callidx < 0 || callidx >= NUM_NHCORE_CALLS || !gl.luacore + || !nhcore_call_available[callidx]) return; lua_getglobal(gl.luacore, "nhcore"); @@ -159,11 +176,7 @@ l_nhcore_call(int callidx) lua_getfield(gl.luacore, -1, nhcore_call_names[callidx]); ltyp = lua_type(gl.luacore, -1); if (ltyp == LUA_TFUNCTION) { - lua_remove(gl.luacore, -2); /* nhcore_call_names[callidx] */ - lua_remove(gl.luacore, -2); /* nhcore */ - if (nhl_pcall(gl.luacore, 0, 1)) { - impossible("Lua error: %s", lua_tostring(gl.luacore, -1)); - } + nhl_pcall_handle(gl.luacore, 0, 1, "l_nhcore_call", NHLpa_panic); } else { /*impossible("nhcore.%s is not a lua function", nhcore_call_names[callidx]);*/ @@ -177,7 +190,7 @@ ATTRNORETURN void nhl_error(lua_State *L, const char *msg) { lua_Debug ar; - char buf[BUFSZ*2]; + char buf[BUFSZ * 2]; lua_getstack(L, 1, &ar); lua_getinfo(L, "lS", &ar); @@ -237,7 +250,7 @@ get_table_mapchr_opt(lua_State *L, const char *name, schar defval) xint8 typ; ter = get_table_str_opt(L, name, emptystr); - if (name && ter && *ter) { + if (ter && *ter) { typ = (xint8) check_mapchr(ter); if (typ == INVALID_TYPE) nhl_error(L, "Erroneous map char"); @@ -251,10 +264,12 @@ get_table_mapchr_opt(lua_State *L, const char *name, schar defval) short nhl_get_timertype(lua_State *L, int idx) { - static const char *const timerstr[NUM_TIME_FUNCS+1] = { - "rot-organic", "rot-corpse", "revive-mon", "zombify-mon", - "burn-obj", "hatch-egg", "fig-transform", "melt-ice", "shrink-glob", - NULL + /* these are in the same order as enum timeout_types in timeout.h and + ttable timeout_funcs[] in timeout.c, although not spelled the same */ + static const char *const timerstr[NUM_TIME_FUNCS + 1] = { + "rot-organic", "rot-corpse", "revive-mon", "zombify-mon", + "burn-obj", "hatch-egg", "fig-transform", "shrink-glob", + "melt-ice", NULL }; short ret = luaL_checkoption(L, idx, NULL, timerstr); @@ -299,8 +314,8 @@ nhl_add_table_entry_bool(lua_State *L, const char *name, boolean value) } void -nhl_add_table_entry_region(lua_State *L, const char *name, - coordxy x1, coordxy y1, coordxy x2, coordxy y2) +nhl_add_table_entry_region(lua_State *L, const char *name, coordxy x1, + coordxy y1, coordxy x2, coordxy y2) { lua_pushstring(L, name); lua_newtable(L); @@ -313,7 +328,7 @@ nhl_add_table_entry_region(lua_State *L, const char *name, /* converting from special level "map character" to levl location type and back. order here is important. */ -const struct { +static const struct { char ch; schar typ; } char2typ[] = { @@ -376,7 +391,7 @@ check_mapchr(const char *s) return INVALID_TYPE; } -static char +staticfn char splev_typ2chr(schar typ) { int i; @@ -391,7 +406,7 @@ DISABLE_WARNING_UNREACHABLE_CODE /* local t = nh.gettrap(x,y); */ /* local t = nh.gettrap({ x = 10, y = 10 }); */ -static int +staticfn int nhl_gettrap(lua_State *L) { lua_Integer lx, ly; @@ -407,43 +422,45 @@ nhl_gettrap(lua_State *L) cvt_to_abscoord(&x, &y); if (isok(x, y)) { - struct trap *ttmp = t_at(x,y); - - if (ttmp) { - lua_newtable(L); - - nhl_add_table_entry_int(L, "tx", ttmp->tx); - nhl_add_table_entry_int(L, "ty", ttmp->ty); - nhl_add_table_entry_int(L, "ttyp", ttmp->ttyp); - nhl_add_table_entry_str(L, "ttyp_name", - 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); - switch (ttmp->ttyp) { - case SQKY_BOARD: - nhl_add_table_entry_int(L, "tnote", ttmp->tnote); - break; - case ROLLING_BOULDER_TRAP: - nhl_add_table_entry_int(L, "launchx", ttmp->launch.x); - nhl_add_table_entry_int(L, "launchy", ttmp->launch.y); - nhl_add_table_entry_int(L, "launch2x", ttmp->launch2.x); - nhl_add_table_entry_int(L, "launch2y", ttmp->launch2.y); - break; - case PIT: - case SPIKED_PIT: - nhl_add_table_entry_int(L, "conjoined", ttmp->conjoined); - break; - } - return 1; - } else - nhl_error(L, "No trap at location"); - } else - nhl_error(L, "Coordinates out of range"); + struct trap *ttmp = t_at(x, y); + + if (ttmp) { + lua_newtable(L); + + nhl_add_table_entry_int(L, "tx", ttmp->tx); + nhl_add_table_entry_int(L, "ty", ttmp->ty); + nhl_add_table_entry_int(L, "ttyp", ttmp->ttyp); + nhl_add_table_entry_str(L, "ttyp_name", + 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); + switch (ttmp->ttyp) { + case SQKY_BOARD: + nhl_add_table_entry_int(L, "tnote", ttmp->tnote); + break; + case ROLLING_BOULDER_TRAP: + nhl_add_table_entry_int(L, "launchx", ttmp->launch.x); + nhl_add_table_entry_int(L, "launchy", ttmp->launch.y); + nhl_add_table_entry_int(L, "launch2x", ttmp->launch2.x); + nhl_add_table_entry_int(L, "launch2y", ttmp->launch2.y); + break; + case PIT: + case SPIKED_PIT: + nhl_add_table_entry_int(L, "conjoined", ttmp->conjoined); + break; + } + return 1; + } else { + nhl_error(L, "No trap at location"); + } + } else { + nhl_error(L, "Coordinates out of range"); + } return 0; } /* nh.deltrap(x,y); nh.deltrap({ x = 10, y = 15 }); */ -static int +staticfn int nhl_deltrap(lua_State *L) { lua_Integer lx, ly; @@ -459,7 +476,7 @@ nhl_deltrap(lua_State *L) cvt_to_abscoord(&x, &y); if (isok(x, y)) { - struct trap *ttmp = t_at(x,y); + struct trap *ttmp = t_at(x, y); if (ttmp) deltrap(ttmp); @@ -473,10 +490,10 @@ RESTORE_WARNING_UNREACHABLE_CODE and set the x and y values. return TRUE if there are such params in the stack. Note that this does not adjust the values of x and y at all from what is - specified in the level file; so, it returns absolute coordinates rather than - map-relative coordinates. Callers of this function must decide if they want - to interpret the values as absolute or as map-relative, and adjust - accordingly. + specified in the level file; so, it returns absolute coordinates rather + than map-relative coordinates. Callers of this function must decide if + they want to interpret the values as absolute or as map-relative, and + adjust accordingly. */ boolean nhl_get_xy_params(lua_State *L, lua_Integer *x, lua_Integer *y) @@ -502,7 +519,7 @@ DISABLE_WARNING_UNREACHABLE_CODE /* local loc = nh.getmap(x,y); */ /* local loc = nh.getmap({ x = 10, y = 35 }); */ -static int +staticfn int nhl_getmap(lua_State *L) { lua_Integer lx, ly; @@ -517,79 +534,79 @@ nhl_getmap(lua_State *L) cvt_to_abscoord(&x, &y); if (isok(x, y)) { - char buf[BUFSZ]; - lua_newtable(L); + char buf[BUFSZ]; + lua_newtable(L); - /* FIXME: some should be boolean values */ - nhl_add_table_entry_int(L, "glyph", levl[x][y].glyph); - nhl_add_table_entry_int(L, "typ", levl[x][y].typ); - nhl_add_table_entry_str(L, "typ_name", - levltyp_to_name(levl[x][y].typ)); - Sprintf(buf, "%c", splev_typ2chr(levl[x][y].typ)); - nhl_add_table_entry_str(L, "mapchr", buf); - nhl_add_table_entry_int(L, "seenv", levl[x][y].seenv); - nhl_add_table_entry_bool(L, "horizontal", levl[x][y].horizontal); - nhl_add_table_entry_bool(L, "lit", levl[x][y].lit); - nhl_add_table_entry_bool(L, "waslit", levl[x][y].waslit); - nhl_add_table_entry_int(L, "roomno", levl[x][y].roomno); - nhl_add_table_entry_bool(L, "edge", levl[x][y].edge); - nhl_add_table_entry_bool(L, "candig", levl[x][y].candig); - - nhl_add_table_entry_bool(L, "has_trap", t_at(x,y) ? 1 : 0); - - /* TODO: FIXME: levl[x][y].flags */ - - lua_pushliteral(L, "flags"); - lua_newtable(L); + /* FIXME: some should be boolean values */ + nhl_add_table_entry_int(L, "glyph", levl[x][y].glyph); + nhl_add_table_entry_int(L, "typ", levl[x][y].typ); + nhl_add_table_entry_str(L, "typ_name", + levltyp_to_name(levl[x][y].typ)); + Sprintf(buf, "%c", splev_typ2chr(levl[x][y].typ)); + nhl_add_table_entry_str(L, "mapchr", buf); + nhl_add_table_entry_int(L, "seenv", levl[x][y].seenv); + nhl_add_table_entry_bool(L, "horizontal", levl[x][y].horizontal); + nhl_add_table_entry_bool(L, "lit", levl[x][y].lit); + nhl_add_table_entry_bool(L, "waslit", levl[x][y].waslit); + nhl_add_table_entry_int(L, "roomno", levl[x][y].roomno); + nhl_add_table_entry_bool(L, "edge", levl[x][y].edge); + nhl_add_table_entry_bool(L, "candig", levl[x][y].candig); + + nhl_add_table_entry_bool(L, "has_trap", t_at(x, y) ? 1 : 0); + + /* TODO: FIXME: levl[x][y].flags */ + + lua_pushliteral(L, "flags"); + lua_newtable(L); - if (IS_DOOR(levl[x][y].typ) || levl[x][y].typ == SDOOR) { - nhl_add_table_entry_bool(L, "nodoor", - doorstate(&levl[x][y]) == D_NODOOR); - nhl_add_table_entry_bool(L, "broken", - doorstate(&levl[x][y]) == D_BROKEN); - nhl_add_table_entry_bool(L, "isopen", - doorstate(&levl[x][y]) == D_ISOPEN); - nhl_add_table_entry_bool(L, "closed", - doorstate(&levl[x][y]) == D_CLOSED); - nhl_add_table_entry_bool(L, "locked", - door_is_locked(&levl[x][y])); - nhl_add_table_entry_bool(L, "trapped", - door_is_trapped(&levl[x][y])); - nhl_add_table_entry_bool(L, "iron", - door_is_iron(&levl[x][y])); - } else if (IS_ALTAR(levl[x][y].typ)) { - /* TODO: bits 0, 1, 2 */ - nhl_add_table_entry_bool(L, "shrine", - (levl[x][y].flags & AM_SHRINE)); - } else if (IS_THRONE(levl[x][y].typ)) { - nhl_add_table_entry_bool(L, "looted", - (levl[x][y].flags & T_LOOTED)); - } else if (levl[x][y].typ == TREE) { - nhl_add_table_entry_bool(L, "looted", - (levl[x][y].flags & TREE_LOOTED)); - nhl_add_table_entry_bool(L, "swarm", - (levl[x][y].flags & TREE_SWARM)); - } else if (IS_FOUNTAIN(levl[x][y].typ)) { - nhl_add_table_entry_bool(L, "looted", - (levl[x][y].flags & F_LOOTED)); - nhl_add_table_entry_bool(L, "warned", - (levl[x][y].flags & F_WARNED)); - } else if (IS_SINK(levl[x][y].typ)) { - nhl_add_table_entry_bool(L, "pudding", - (levl[x][y].flags & S_LPUDDING)); - nhl_add_table_entry_bool(L, "dishwasher", - (levl[x][y].flags & S_LDWASHER)); - /* vanilla NetHack allows "ring" to be read from this location; - * xNetHack just has the ring buried under the sink, and - * possibly multiple rings. For now, we don't bother searching - * through buriedobjlist to see if there's a ring at (x, y), and - * the lua table won't contain a 'ring' entry. */ - } - /* TODO: drawbridges, walls, ladders, room=>ICED_xxx */ + if (IS_DOOR(levl[x][y].typ) || levl[x][y].typ == SDOOR) { + nhl_add_table_entry_bool(L, "nodoor", + doorstate(&levl[x][y]) == D_NODOOR); + nhl_add_table_entry_bool(L, "broken", + doorstate(&levl[x][y]) == D_BROKEN); + nhl_add_table_entry_bool(L, "isopen", + doorstate(&levl[x][y]) == D_ISOPEN); + nhl_add_table_entry_bool(L, "closed", + doorstate(&levl[x][y]) == D_CLOSED); + nhl_add_table_entry_bool(L, "locked", + door_is_locked(&levl[x][y])); + nhl_add_table_entry_bool(L, "trapped", + door_is_trapped(&levl[x][y])); + nhl_add_table_entry_bool(L, "iron", + door_is_iron(&levl[x][y])); + } else if (IS_ALTAR(levl[x][y].typ)) { + /* TODO: bits 0, 1, 2 */ + nhl_add_table_entry_bool(L, "shrine", + (levl[x][y].flags & AM_SHRINE)); + } else if (IS_THRONE(levl[x][y].typ)) { + nhl_add_table_entry_bool(L, "looted", + (levl[x][y].flags & T_LOOTED)); + } else if (levl[x][y].typ == TREE) { + nhl_add_table_entry_bool(L, "looted", + (levl[x][y].flags & TREE_LOOTED)); + nhl_add_table_entry_bool(L, "swarm", + (levl[x][y].flags & TREE_SWARM)); + } else if (IS_FOUNTAIN(levl[x][y].typ)) { + nhl_add_table_entry_bool(L, "looted", + (levl[x][y].flags & F_LOOTED)); + nhl_add_table_entry_bool(L, "warned", + (levl[x][y].flags & F_WARNED)); + } else if (IS_SINK(levl[x][y].typ)) { + nhl_add_table_entry_bool(L, "pudding", + (levl[x][y].flags & S_LPUDDING)); + nhl_add_table_entry_bool(L, "dishwasher", + (levl[x][y].flags & S_LDWASHER)); + /* vanilla NetHack allows "ring" to be read from this location; + * xNetHack just has the ring buried under the sink, and + * possibly multiple rings. For now, we don't bother searching + * through buriedobjlist to see if there's a ring at (x, y), and + * the lua table won't contain a 'ring' entry. */ + } + /* TODO: drawbridges, walls, ladders, room=>ICED_xxx */ - lua_settable(L, -3); + lua_settable(L, -3); - return 1; + return 1; } else { /* TODO: return zerorm instead? */ nhl_error(L, "Coordinates out of range"); @@ -598,7 +615,7 @@ nhl_getmap(lua_State *L) } /* impossible("Error!") */ -static int +staticfn int nhl_impossible(lua_State *L) { int argc = lua_gettop(L); @@ -612,7 +629,7 @@ nhl_impossible(lua_State *L) /* pline("It hits!") */ /* pline("It hits!", true) */ -static int +staticfn int nhl_pline(lua_State *L) { int argc = lua_gettop(L); @@ -628,7 +645,7 @@ nhl_pline(lua_State *L) } /* verbalize("Fool!") */ -static int +staticfn int nhl_verbalize(lua_State *L) { int argc = lua_gettop(L); @@ -642,7 +659,7 @@ nhl_verbalize(lua_State *L) } /* parse_config("OPTIONS=!color") */ -static int +staticfn int nhl_parse_config(lua_State *L) { int argc = lua_gettop(L); @@ -656,7 +673,7 @@ nhl_parse_config(lua_State *L) } /* local windowtype = get_config("windowtype"); */ -static int +staticfn int nhl_get_config(lua_State *L) { int argc = lua_gettop(L); @@ -673,7 +690,7 @@ nhl_get_config(lua_State *L) /* str = getlin("What do you want to call this dungeon level?"); */ -static int +staticfn int nhl_getlin(lua_State *L) { int argc = lua_gettop(L); @@ -693,15 +710,16 @@ nhl_getlin(lua_State *L) } /* - selected = menu("prompt", default, pickX, { "a" = "option a", "b" = "option b", ...}) + selected = menu("prompt",default,pickX,{"a"="option a", "b"="option b",...}) pickX = 0,1,2, or "none", "one", "any" (PICK_X in code) selected = menu("prompt", default, pickX, - { {key:"a", text:"option a"}, {key:"b", text:"option b"}, ... } ) */ -static int + {{key:"a", text:"option a"},{key:"b", text:"option b"},... }) + */ +staticfn int nhl_menu(lua_State *L) { - static const char *const pickX[] = {"none", "one", "any"}; /* PICK_x */ + static const char *const pickX[] = { "none", "one", "any" }; /* PICK_x */ int argc = lua_gettop(L); const char *prompt; const char *defval = ""; @@ -709,7 +727,7 @@ nhl_menu(lua_State *L) winid tmpwin; anything any; menu_item *picks = (menu_item *) 0; - int clr = 0; + int clr = NO_COLOR; if (argc < 2 || argc > 4) { nhl_error(L, "Wrong args"); @@ -755,7 +773,8 @@ nhl_menu(lua_State *L) any.a_char = key[0]; add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, str, (*defval && *key && defval[0] == key[0]) - ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE); + ? MENU_ITEMFLAGS_SELECTED + : MENU_ITEMFLAGS_NONE); lua_pop(L, 1); /* removes 'value'; keeps 'key' for next iteration */ } @@ -768,8 +787,8 @@ nhl_menu(lua_State *L) char buf[2]; buf[0] = picks[0].item.a_char; - if (pick == PICK_ONE && pick_cnt > 1 - && *defval && defval[0] == picks[0].item.a_char) + if (pick == PICK_ONE && pick_cnt > 1 && *defval + && defval[0] == picks[0].item.a_char) buf[0] = picks[1].item.a_char; buf[1] = '\0'; @@ -786,7 +805,7 @@ nhl_menu(lua_State *L) } /* text("foo\nbar\nbaz") */ -static int +staticfn int nhl_text(lua_State *L) { int argc = lua_gettop(L); @@ -794,7 +813,6 @@ nhl_text(lua_State *L) if (argc > 0) { menu_item *picks = (menu_item *) 0; winid tmpwin; - anything any = cg.zeroany; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -817,8 +835,7 @@ nhl_text(lua_State *L) while ((ptr > str) && !(*ptr == ' ' || *ptr == '\n')) ptr--; *ptr = '\0'; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, 0, - str, MENU_ITEMFLAGS_NONE); + add_menu_str(tmpwin, str); str = ptr + 1; } while (*str && str <= lstr); lua_pop(L, 1); @@ -833,7 +850,7 @@ nhl_text(lua_State *L) } /* makeplural("zorkmid") */ -static int +staticfn int nhl_makeplural(lua_State *L) { int argc = lua_gettop(L); @@ -847,7 +864,7 @@ nhl_makeplural(lua_State *L) } /* makesingular("zorkmids") */ -static int +staticfn int nhl_makesingular(lua_State *L) { int argc = lua_gettop(L); @@ -861,7 +878,7 @@ nhl_makesingular(lua_State *L) } /* s_suffix("foo") */ -static int +staticfn int nhl_s_suffix(lua_State *L) { int argc = lua_gettop(L); @@ -875,7 +892,7 @@ nhl_s_suffix(lua_State *L) } /* ing_suffix("foo") */ -static int +staticfn int nhl_ing_suffix(lua_State *L) { int argc = lua_gettop(L); @@ -889,7 +906,7 @@ nhl_ing_suffix(lua_State *L) } /* an("foo") */ -static int +staticfn int nhl_an(lua_State *L) { int argc = lua_gettop(L); @@ -903,7 +920,7 @@ nhl_an(lua_State *L) } /* rn2(10) */ -static int +staticfn int nhl_rn2(lua_State *L) { int argc = lua_gettop(L); @@ -918,7 +935,7 @@ nhl_rn2(lua_State *L) /* random(10); -- is the same as rn2(10); */ /* random(5,8); -- same as 5 + rn2(8); */ -static int +staticfn int nhl_random(lua_State *L) { int argc = lua_gettop(L); @@ -926,7 +943,8 @@ nhl_random(lua_State *L) if (argc == 1) lua_pushinteger(L, rn2((int) luaL_checkinteger(L, 1))); else if (argc == 2) - lua_pushinteger(L, luaL_checkinteger(L, 1) + rn2((int) luaL_checkinteger(L, 2))); + lua_pushinteger(L, luaL_checkinteger(L, 1) + + rn2((int) luaL_checkinteger(L, 2))); else nhl_error(L, "Wrong args"); @@ -934,19 +952,39 @@ nhl_random(lua_State *L) } /* level_difficulty() */ -static int +staticfn int nhl_level_difficulty(lua_State *L) { int argc = lua_gettop(L); if (argc == 0) { lua_pushinteger(L, level_difficulty()); - } - else { + } else { nhl_error(L, "level_difficulty should not have any args"); } 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; +} + + RESTORE_WARNING_UNREACHABLE_CODE /* mon_difficulty("water troll") => mons[PM_WATER_TROLL].difficulty */ @@ -1043,7 +1081,7 @@ get_table_boolean(lua_State *L, const char *name) ret = lua_toboolean(L, -1); } else if (ltyp == LUA_TNUMBER) { ret = (int) luaL_checkinteger(L, -1); - if ( ret < 0 || ret > 1) + if (ret < 0 || ret > 1) ret = -1; } lua_pop(L, 1); @@ -1083,7 +1121,7 @@ get_table_option(lua_State *L, #ifdef DUMPLOG /* local fname = dump_fmtstr("/tmp/nethack.%n.%d.log"); */ -static int +staticfn int nhl_dump_fmtstr(lua_State *L) { int argc = lua_gettop(L); @@ -1098,7 +1136,7 @@ nhl_dump_fmtstr(lua_State *L) #endif /* DUMPLOG */ /* local dungeon_name = dnum_name(u.dnum); */ -static int +staticfn int nhl_dnum_name(lua_State *L) { int argc = lua_gettop(L); @@ -1106,8 +1144,8 @@ nhl_dnum_name(lua_State *L) if (argc == 1) { lua_Integer dnum = luaL_checkinteger(L, 1); - if (dnum >= 0 && dnum < gn.n_dgns) - lua_pushstring(L, gd.dungeons[dnum].dname); + if (dnum >= 0 && dnum < svn.n_dgns) + lua_pushstring(L, svd.dungeons[dnum].dname); else lua_pushstring(L, ""); } else @@ -1121,7 +1159,7 @@ DISABLE_WARNING_UNREACHABLE_CODE /* set or get variables which are saved and restored along with the game. nh.variable("test", 10); local ten = nh.variable("test"); */ -static int +staticfn int nhl_variable(lua_State *L) { int argc = lua_gettop(L); @@ -1129,7 +1167,7 @@ nhl_variable(lua_State *L) const char *key; if (!gl.luacore) { - nhl_error(L, "nh luacore not inited"); + panic("nh luacore not inited"); /*NOTREACHED*/ return 0; } @@ -1156,9 +1194,9 @@ nhl_variable(lua_State *L) else if (typ == LUA_TTABLE) { lua_getglobal(gl.luacore, "nh_get_variables_string"); lua_pushvalue(gl.luacore, -2); - nhl_pcall(gl.luacore, 1, 1); + nhl_pcall_handle(gl.luacore, 1, 1, "nhl_variable", NHLpa_panic); luaL_loadstring(L, lua_tostring(gl.luacore, -1)); - nhl_pcall(L, 0, 1); + nhl_pcall_handle(L, 0, 1, "nhl_variable-1", NHLpa_panic); } else nhl_error(L, "Cannot get variable of that type"); return 1; @@ -1185,9 +1223,9 @@ nhl_variable(lua_State *L) lua_getglobal(L, "nh_set_variables_string"); lua_pushstring(L, key); lua_pushvalue(L, -3); /* copy value to top */ - nhl_pcall(L, 2, 1); + nhl_pcall_handle(L, 2, 1, "nhl_variable-2", NHLpa_panic); luaL_loadstring(gl.luacore, lua_tostring(L, -1)); - nhl_pcall(gl.luacore, 0, 0); + nhl_pcall_handle(gl.luacore, 0, 0, "nhl_variable-3", NHLpa_panic); } else nhl_error(L, "Cannot set variable of that type"); return 0; @@ -1203,25 +1241,19 @@ get_nh_lua_variables(void) char *key = NULL; if (!gl.luacore) { - nhl_error(gl.luacore, "nh luacore not inited"); + panic("nh luacore not inited"); /*NOTREACHED*/ return key; } - - lua_getglobal(gl.luacore, "nh_lua_variables"); - if (!lua_istable(gl.luacore, -1)) { - impossible("nh_lua_variables is not a lua table"); - return key; - } - lua_getglobal(gl.luacore, "get_variables_string"); if (lua_type(gl.luacore, -1) == LUA_TFUNCTION) { - if (nhl_pcall(gl.luacore, 0, 1)) { - impossible("Lua error: %s", lua_tostring(gl.luacore, -1)); + if (nhl_pcall_handle(gl.luacore, 0, 1, "get_nh_lua_variables", + NHLpa_impossible)) { return key; } key = dupstr(lua_tostring(gl.luacore, -1)); } + lua_pop(gl.luacore, 1); return key; } @@ -1257,11 +1289,12 @@ restore_luadata(NHFILE *nhfp) if (!gl.luacore) l_nhcore_init(); luaL_loadstring(gl.luacore, lua_data); - nhl_pcall(gl.luacore, 0, 0); + free(lua_data); + nhl_pcall_handle(gl.luacore, 0, 0, "restore_luadata", NHLpa_panic); } /* local stairs = stairways(); */ -static int +staticfn int nhl_stairways(lua_State *L) { stairway *tmp = gs.stairs; @@ -1270,7 +1303,6 @@ nhl_stairways(lua_State *L) lua_newtable(L); while (tmp) { - lua_pushinteger(L, i); lua_newtable(L); @@ -1293,7 +1325,7 @@ nhl_stairways(lua_State *L) /* test( { x = 123, y = 456 } ); */ -static int +staticfn int nhl_test(lua_State *L) { coordxy x, y; @@ -1317,7 +1349,7 @@ nhl_test(lua_State *L) /* push a key into command queue */ /* nh.pushkey("i"); */ -static int +staticfn int nhl_pushkey(lua_State *L) { int argc = lua_gettop(L); @@ -1333,7 +1365,7 @@ nhl_pushkey(lua_State *L) /* do a turn of moveloop, or until gm.multi is done if param is true. */ /* nh.doturn(); nh.doturn(true); */ -static int +staticfn int nhl_doturn(lua_State *L) { int argc = lua_gettop(L); @@ -1353,7 +1385,7 @@ nhl_doturn(lua_State *L) /* nh.debug_flags({ mongen = false, hunger = false, overwrite_stairs = true }); */ -static int +staticfn int nhl_debug_flags(lua_State *L) { int val; @@ -1363,9 +1395,9 @@ nhl_debug_flags(lua_State *L) /* disable monster generation */ val = get_table_boolean_opt(L, "mongen", -1); if (val != -1) { - iflags.debug_mongen = !(boolean)val; /* value in lua is negated */ + iflags.debug_mongen = !(boolean) val; /* value in lua is negated */ if (iflags.debug_mongen) { - register struct monst *mtmp, *mtmp2; + struct monst *mtmp, *mtmp2; for (mtmp = fmon; mtmp; mtmp = mtmp2) { mtmp2 = mtmp->nmon; @@ -1379,13 +1411,13 @@ nhl_debug_flags(lua_State *L) /* prevent hunger */ val = get_table_boolean_opt(L, "hunger", -1); if (val != -1) { - iflags.debug_hunger = !(boolean)val; /* value in lua is negated */ + iflags.debug_hunger = !(boolean) val; /* value in lua is negated */ } /* allow overwriting stairs */ val = get_table_boolean_opt(L, "overwrite_stairs", -1); if (val != -1) { - iflags.debug_overwrite_stairs = (boolean)val; + iflags.debug_overwrite_stairs = (boolean) val; } return 0; @@ -1396,7 +1428,7 @@ DISABLE_WARNING_UNREACHABLE_CODE /* does location at x,y have timer? */ /* local has_melttimer = nh.has_timer_at(x,y, "melt-ice"); */ /* local has_melttimer = nh.has_timer_at({x=4,y=7}, "melt-ice"); */ -static int +staticfn int nhl_timer_has_at(lua_State *L) { boolean ret = FALSE; @@ -1427,7 +1459,7 @@ nhl_timer_has_at(lua_State *L) /* when does location at x,y timer trigger? */ /* local melttime = nh.peek_timer_at(x,y, "melt-ice"); */ /* local melttime = nh.peek_timer_at({x=5,y=6}, "melt-ice"); */ -static int +staticfn int nhl_timer_peek_at(lua_State *L) { long when = 0L; @@ -1455,7 +1487,7 @@ nhl_timer_peek_at(lua_State *L) /* stop timer at location x,y */ /* nh.stop_timer_at(x,y, "melt-ice"); */ /* nh.stop_timer_at({x=6,y=8}, "melt-ice"); */ -static int +staticfn int nhl_timer_stop_at(lua_State *L) { short timertype = nhl_get_timertype(L, -1); @@ -1480,7 +1512,7 @@ nhl_timer_stop_at(lua_State *L) /* start timer at location x,y */ /* nh.start_timer_at(x,y, "melt-ice", 10); */ -static int +staticfn int nhl_timer_start_at(lua_State *L) { short timertype = nhl_get_timertype(L, -2); @@ -1538,7 +1570,7 @@ nhl_is_wish_dlord(lua_State *L) /* returns the visual interpretation of the key bound to an extended command, or the ext cmd name if not bound to any key */ /* local helpkey = eckey("help"); */ -static int +staticfn int nhl_get_cmd_key(lua_State *L) { int argc = lua_gettop(L); @@ -1558,50 +1590,32 @@ nhl_get_cmd_key(lua_State *L) /* callback("level_enter", "function_name"); */ /* callback("level_enter", "function_name", true); */ /* level_enter, level_leave, cmd_before */ -static int +staticfn int nhl_callback(lua_State *L) { int argc = lua_gettop(L); int i; + boolean rm; + const char *fn, *cb; - if (argc == 2) { - const char *fn = luaL_checkstring(L, -1); - const char *cb = luaL_checkstring(L, -2); - - if (!gl.luacore) { - nhl_error(L, "nh luacore not inited"); - /*NOTREACHED*/ - return 0; - } - - for (i = 0; i < NUM_NHCB; i++) - if (!strcmp(cb, nhcb_name[i])) - break; - - if (i >= NUM_NHCB) - return 0; - - nhcb_counts[i]++; - - lua_getglobal(gl.luacore, "nh_callback_set"); - lua_pushstring(gl.luacore, cb); - lua_pushstring(gl.luacore, fn); - nhl_pcall(gl.luacore, 2, 0); - } else if (argc == 3) { - boolean rm = lua_toboolean(L, -1); - const char *fn = luaL_checkstring(L, -2); - const char *cb = luaL_checkstring(L, -3); - - if (!gl.luacore) { - nhl_error(L, "nh luacore not inited"); - /*NOTREACHED*/ - return 0; + if (!gl.luacore) { + panic("nh luacore not inited"); + /*NOTREACHED*/ + return 0; + } + if (argc == 2 || argc == 3) { + if (argc == 2) { + rm = FALSE; + fn = luaL_checkstring(L, -1); + cb = luaL_checkstring(L, -2); + } else { + rm = lua_toboolean(L, -1); + fn = luaL_checkstring(L, -2); + cb = luaL_checkstring(L, -3); } - for (i = 0; i < NUM_NHCB; i++) if (!strcmp(cb, nhcb_name[i])) break; - if (i >= NUM_NHCB) return 0; @@ -1616,60 +1630,92 @@ nhl_callback(lua_State *L) lua_getglobal(gl.luacore, rm ? "nh_callback_rm" : "nh_callback_set"); lua_pushstring(gl.luacore, cb); lua_pushstring(gl.luacore, fn); - nhl_pcall(gl.luacore, 2, 0); + nhl_pcall_handle(gl.luacore, 2, 0, "nhl_callback", NHLpa_panic); } - return 0; } /* store or restore game state */ -/* NOTE: doesn't work when saving/restoring the game */ -/* currently handles inventory and turns. */ +/* + * Currently handles + * turn counter, + * hero inventory and hunger, + * hero attributes and skills and conducts (all via 'struct u'), + * object discoveries, + * monster generation and vanquished statistics. + * NOTE: wouldn't work after restore if game has been saved, so the + * #save command ('S') is disabled during the tutorial. + */ /* gamestate(); -- save state */ /* gamestate(true); -- restore state */ -static int +staticfn int nhl_gamestate(lua_State *L) { + long wornmask; + struct obj *otmp; int argc = lua_gettop(L); - boolean reststate = argc > 0 ? lua_toboolean(L, -1) : FALSE; - static struct obj *invent = NULL; - static long moves = 0; - static boolean stored = FALSE; - static struct you ubak; + boolean reststate = (argc > 0) ? lua_toboolean(L, -1) : FALSE; + + debugpline4("gamestate: %d:%d (%c vs %c)", u.uz.dnum, u.uz.dlevel, + reststate ? 'T' : 'F', gg.gmst_stored ? 't' : 'f'); + + if (reststate && gg.gmst_stored) { + d_level cur_uz = u.uz, cur_uz0 = u.uz0; - if (reststate && stored) { /* restore game state */ - gm.moves = moves; + svm.moves = gg.gmst_moves; + pline("Resetting time to move #%ld.", svm.moves); + gg.gmst_moves = 0L; + gl.lastinvnr = 51; while (gi.invent) useupall(gi.invent); - while (invent) { - struct obj *otmp = invent; - long wornmask = otmp->owornmask; + while ((otmp = gg.gmst_invent) != NULL) { + wornmask = otmp->owornmask; otmp->owornmask = 0L; - extract_nobj(otmp, &invent); - addinv(otmp); + extract_nobj(otmp, &gg.gmst_invent); + addinv_nomerge(otmp); if (wornmask) setworn(otmp, wornmask); } - u = ubak; + assert(gg.gmst_ubak != NULL); + (void) memcpy((genericptr_t) &u, gg.gmst_ubak, sizeof u); + assert(gg.gmst_disco != NULL); + (void) memcpy((genericptr_t) &svd.disco, gg.gmst_disco, + sizeof svd.disco); + assert(gg.gmst_mvitals != NULL); + (void) memcpy((genericptr_t) &svm.mvitals, gg.gmst_mvitals, + sizeof svm.mvitals); + /* some restored state would confuse the level change in progress */ + u.uz = cur_uz, u.uz0 = cur_uz0; init_uhunger(); - stored = FALSE; - } else { + free_tutorial(); /* release gg.gmst_XYZ */ + gg.gmst_stored = FALSE; + } else if (!reststate && !gg.gmst_stored) { /* store game state */ - while (gi.invent) { - struct obj *otmp = gi.invent; - long wornmask = otmp->owornmask; + gg.gmst_moves = svm.moves; + while ((otmp = gi.invent) != NULL) { + wornmask = otmp->owornmask; setnotworn(otmp); freeinv(otmp); - otmp->nobj = invent; - otmp->owornmask = wornmask; - invent = otmp; + otmp->owornmask = wornmask; /* flag for later restore */ + otmp->nobj = gg.gmst_invent; + gg.gmst_invent = otmp; } - gl.lastinvnr = 51; - moves = gm.moves; - ubak = u; - stored = TRUE; + gl.lastinvnr = 51; /* next inv letter to try to use will be 'a' */ + gg.gmst_ubak = (genericptr_t) alloc(sizeof u); + (void) memcpy(gg.gmst_ubak, (genericptr_t) &u, sizeof u); + gg.gmst_disco = (genericptr_t) alloc(sizeof svd.disco); + (void) memcpy(gg.gmst_disco, (genericptr_t) &svd.disco, + sizeof svd.disco); + gg.gmst_mvitals = (genericptr_t) alloc(sizeof svm.mvitals); + (void) memcpy(gg.gmst_mvitals, (genericptr_t) &svm.mvitals, + sizeof svm.mvitals); + gg.gmst_stored = TRUE; + } else { + impossible("nhl_gamestate: inconsistent state (%s vs %s)", + reststate ? "restore" : "save", + gg.gmst_stored ? "already stored" : "not stored"); } update_inventory(); return 0; @@ -1677,56 +1723,99 @@ nhl_gamestate(lua_State *L) RESTORE_WARNING_UNREACHABLE_CODE +/* free dynamic date allocated when entering tutorial; + called when exiting tutorial normally or if player quits while in it */ +void +free_tutorial(void) +{ + struct obj *otmp; + + /* for normal tutorial exit, gmst_invent will already be Null */ + while ((otmp = gg.gmst_invent) != 0) { + /* set otmp->where = OBJ_FREE, otmp->nobj = NULL */ + extract_nobj(otmp, &gg.gmst_invent); + /* gmst_invent is a list of invent items sequestered when entering + the tutorial; for them, owornmask is used as a flag to re-wear + them when exiting tutorial, not that they are currently worn; + clear it to avoid a "deleting worn obj" complaint from obfree() */ + otmp->owornmask = 0L; + /* dealloc_obj() isn't enough (for containers, it assumes that + caller has already freed their contents) */ + obfree(otmp, (struct obj *) 0); + } + + if (gg.gmst_ubak) + free(gg.gmst_ubak), gg.gmst_ubak = NULL; + if (gg.gmst_disco) + free(gg.gmst_disco), gg.gmst_disco = NULL; + if (gg.gmst_mvitals) + free(gg.gmst_mvitals), gg.gmst_mvitals = NULL; +} + +/* called from gotolevel(do.c) */ +void +tutorial(boolean entering) +{ + l_nhcore_call(entering ? NHCORE_ENTER_TUTORIAL : NHCORE_LEAVE_TUTORIAL); + + if (!entering) { /* after leaving, can't go back */ + nhcore_call_available[NHCORE_ENTER_TUTORIAL] + = nhcore_call_available[NHCORE_LEAVE_TUTORIAL] + = FALSE; + } +} + static const struct luaL_Reg nhl_functions[] = { - {"test", nhl_test}, + { "test", nhl_test }, - {"getmap", nhl_getmap}, + { "getmap", nhl_getmap }, #if 0 - {"setmap", nhl_setmap}, + { "setmap", nhl_setmap }, #endif - {"gettrap", nhl_gettrap}, - {"deltrap", nhl_deltrap}, - - {"has_timer_at", nhl_timer_has_at}, - {"peek_timer_at", nhl_timer_peek_at}, - {"stop_timer_at", nhl_timer_stop_at}, - {"start_timer_at", nhl_timer_start_at}, - - {"abscoord", nhl_abs_coord}, - - {"impossible", nhl_impossible}, - {"pline", nhl_pline}, - {"verbalize", nhl_verbalize}, - {"menu", nhl_menu}, - {"text", nhl_text}, - {"getlin", nhl_getlin}, - {"eckey", nhl_get_cmd_key}, - {"callback", nhl_callback}, - {"gamestate", nhl_gamestate}, - - {"makeplural", nhl_makeplural}, - {"makesingular", nhl_makesingular}, - {"s_suffix", nhl_s_suffix}, - {"ing_suffix", nhl_ing_suffix}, - {"an", nhl_an}, - {"rn2", nhl_rn2}, - {"random", nhl_random}, - {"level_difficulty", nhl_level_difficulty}, - {"mon_difficulty", nhl_mon_difficulty}, - {"parse_config", nhl_parse_config}, - {"get_config", nhl_get_config}, - {"get_config_errors", l_get_config_errors}, + { "gettrap", nhl_gettrap }, + { "deltrap", nhl_deltrap }, + + { "has_timer_at", nhl_timer_has_at }, + { "peek_timer_at", nhl_timer_peek_at }, + { "stop_timer_at", nhl_timer_stop_at }, + { "start_timer_at", nhl_timer_start_at }, + + { "abscoord", nhl_abs_coord }, + + { "impossible", nhl_impossible }, + { "pline", nhl_pline }, + { "verbalize", nhl_verbalize }, + { "menu", nhl_menu }, + { "text", nhl_text }, + { "getlin", nhl_getlin }, + { "eckey", nhl_get_cmd_key }, + { "callback", nhl_callback }, + { "gamestate", nhl_gamestate }, + + { "makeplural", nhl_makeplural }, + { "makesingular", nhl_makesingular }, + { "s_suffix", nhl_s_suffix }, + { "ing_suffix", nhl_ing_suffix }, + { "an", nhl_an }, + { "rn2", nhl_rn2 }, + { "random", nhl_random }, + { "level_difficulty", nhl_level_difficulty }, + { "mon_difficulty", nhl_mon_difficulty }, + { "is_genocided", nhl_is_genocided }, + { "parse_config", nhl_parse_config }, + { "get_config", nhl_get_config }, + { "get_config_errors", l_get_config_errors }, #ifdef DUMPLOG - {"dump_fmtstr", nhl_dump_fmtstr}, + { "dump_fmtstr", nhl_dump_fmtstr }, #endif /* DUMPLOG */ - {"dnum_name", nhl_dnum_name}, - {"variable", nhl_variable}, - {"stairways", nhl_stairways}, - {"pushkey", nhl_pushkey}, - {"doturn", nhl_doturn}, - {"debug_flags", nhl_debug_flags}, - {"is_wish_dlord", nhl_is_wish_dlord}, - {NULL, NULL} + { "dnum_name", nhl_dnum_name }, + { "variable", nhl_variable }, + { "stairways", nhl_stairways }, + { "pushkey", nhl_pushkey }, + { "doturn", nhl_doturn }, + { "debug_flags", nhl_debug_flags }, + { "is_wish_dlord", nhl_is_wish_dlord }, + { NULL, NULL } }; static const struct { @@ -1736,15 +1825,15 @@ static const struct { { "COLNO", COLNO }, { "ROWNO", ROWNO }, #ifdef DLB - { "DLB", 1}, + { "DLB", 1 }, #else - { "DLB", 0}, + { "DLB", 0 }, #endif /* DLB */ { NULL, 0 }, }; /* register and init the constants table */ -static void +staticfn void init_nhc_data(lua_State *L) { int i; @@ -1760,19 +1849,22 @@ init_nhc_data(lua_State *L) lua_setglobal(L, "nhc"); } -static int +staticfn int nhl_push_anything(lua_State *L, int anytype, void *src) { anything any = cg.zeroany; switch (anytype) { - case ANY_INT: any.a_int = *(int *) src; + case ANY_INT: + any.a_int = *(int *) src; lua_pushinteger(L, any.a_int); break; - case ANY_UCHAR: any.a_uchar = *(uchar *) src; + case ANY_UCHAR: + any.a_uchar = *(uchar *) src; lua_pushinteger(L, any.a_uchar); break; - case ANY_SCHAR: any.a_schar = *(schar *) src; + case ANY_SCHAR: + any.a_schar = *(schar *) src; lua_pushinteger(L, any.a_schar); break; } @@ -1781,7 +1873,7 @@ nhl_push_anything(lua_State *L, int anytype, void *src) DISABLE_WARNING_UNREACHABLE_CODE -static int +staticfn int nhl_meta_u_index(lua_State *L) { static const struct { @@ -1807,7 +1899,7 @@ nhl_meta_u_index(lua_State *L) { "mhmax", &(u.mhmax), ANY_INT }, { "mtimedone", &(u.mtimedone), ANY_INT }, { "dlevel", &(u.uz.dlevel), ANY_SCHAR }, /* actually coordxy */ - { "dnum", &(u.uz.dnum), ANY_SCHAR }, /* actually coordxy */ + { "dnum", &(u.uz.dnum), ANY_SCHAR }, /* actually coordxy */ { "uluck", &(u.uluck), ANY_SCHAR }, { "uhp", &(u.uhp), ANY_INT }, { "uhpmax", &(u.uhpmax), ANY_INT }, @@ -1830,7 +1922,7 @@ nhl_meta_u_index(lua_State *L) lua_pushstring(L, gu.urole.name.m); return 1; } else if (!strcmp(tkey, "moves")) { - lua_pushinteger(L, gm.moves); + lua_pushinteger(L, svm.moves); return 1; } else if (!strcmp(tkey, "uhave_amulet")) { lua_pushinteger(L, u.uhave.amulet); @@ -1848,7 +1940,7 @@ nhl_meta_u_index(lua_State *L) return 0; } -static int +staticfn int nhl_meta_u_newindex(lua_State *L) { nhl_error(L, "Cannot set u table values"); @@ -1858,7 +1950,7 @@ nhl_meta_u_newindex(lua_State *L) RESTORE_WARNING_UNREACHABLE_CODE -static int +staticfn int nhl_u_clear_inventory(lua_State *L UNUSED) { while (gi.invent) @@ -1868,7 +1960,7 @@ nhl_u_clear_inventory(lua_State *L UNUSED) /* Put object into player's inventory */ /* u.giveobj(obj.new("rock")); */ -static int +staticfn int nhl_u_giveobj(lua_State *L) { return nhl_obj_u_giveobj(L); @@ -1880,7 +1972,7 @@ static const struct luaL_Reg nhl_u_functions[] = { { NULL, NULL } }; -static void +staticfn void init_u_data(lua_State *L) { lua_newtable(L); @@ -1895,7 +1987,7 @@ init_u_data(lua_State *L) } #ifdef notyet -static int +staticfn int nhl_set_package_path(lua_State *L, const char *path) { if (LUA_TTABLE != lua_getglobal(L, "package")) { @@ -1909,7 +2001,7 @@ nhl_set_package_path(lua_State *L, const char *path) } #endif -static int +staticfn int traceback_handler(lua_State *L) { luaL_traceback(L, L, lua_tostring(L, 1), 0); @@ -1917,48 +2009,88 @@ traceback_handler(lua_State *L) return 1; } -/* lua_pcall with our traceback handler and instruction step limiting. +staticfn uint32_t +nhl_getmeminuse(lua_State *L) +{ + return lua_gc(L, LUA_GCCOUNT) * 1024 + lua_gc(L, LUA_GCCOUNTB); +} + +/* lua_pcall with our traceback handler and memory and instruction step + * limiting. * On error, traceback will be on top of stack */ int -nhl_pcall(lua_State *L, int nargs, int nresults) +nhl_pcall(lua_State *L, int nargs, int nresults, const char *name) { struct nhl_user_data *nud; int rv; lua_pushcfunction(L, traceback_handler); lua_insert(L, 1); - (void)lua_getallocf(L, (void **)&nud); + (void) lua_getallocf(L, (void **) &nud); #ifdef NHL_SANDBOX + if (nud && name) { + nud->name = name; + } + /* NB: We don't need to deal with nud->memlimit - Lua handles that. */ if (nud && (nud->steps || nud->perpcall)) { - if (nud->perpcall) + if (nud->perpcall) { nud->steps = nud->perpcall; + nud->statctr = 0; + } if (setjmp(nud->jb)) { /* panic, because we don't know if the game state is corrupt */ - panic("time exceeded"); + /* XXX can we get a lua stack trace as well? */ + panic("Lua time exceeded %d:%s", nud->sid, + nud->name ? nud->name : "(unknown)"); } } #endif rv = lua_pcall(L, nargs, nresults, 1); + lua_remove(L, 1); /* remove handler */ #ifdef NHL_SANDBOX - if (nud && (nud->flags & (NHL_SB_REPORT | NHL_SB_REPORT2)) != 0 - && (nud->memlimit || nud->osteps || nud->perpcall)) { - if (nud->flags & NHL_SB_REPORT2) - lua_gc(L, LUA_GCCOLLECT); - pline("Lua context=%p RAM: %lu STEPS:%lu", (void *) L, - (unsigned long) nud->inuse, - (unsigned long) (nud->perpcall - ? (nud->perpcall - nud->steps) - : (nud->osteps - nud->steps))); + if (nud && nud->perpcall && gl.loglua) { + long ic = nud->statctr * NHL_SB_STEPSIZE; // an approximation + livelog_printf(LL_DEBUG, "LUASTATS PCAL %d:%s %ld", nud->sid, + nud->name, ic); + } + if (nud && nud->memlimit && gl.loglua) { + livelog_printf(LL_DEBUG, "LUASTATS PMEM %d:%s %lu", nud->sid, + nud->name, (long unsigned) nhl_getmeminuse(L)); } #endif + return rv; +} +int +nhl_pcall_handle(lua_State *L, int nargs, int nresults, const char *name, + NHL_pcall_action npa) +{ + int rv = nhl_pcall(L, nargs, nresults, name); + if (rv) { + nhl_user_data *nud; + (void) lua_getallocf(L, (void **) &nud); + /* XXX can we get a lua stack trace as well? */ + switch (npa) { + 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)", + lua_tostring(L, -1)); + /* Drop the error. If the caller cares, use nhl_pcall(). */ + lua_pop(L, 1); + } + } return rv; } -/* XXX impossible() should be swappable with pline or nothing via flag */ +/* XXX impossible() should be swappable with pline/nothing/panic via flag */ /* read lua code/data from a dlb module or an external file into a string buffer and feed that to lua */ boolean @@ -2005,7 +2137,8 @@ nhl_loadlua(lua_State *L, const char *fname) * in use, and fseek(SEEK_END) only yields an upper bound on * the actual amount of data in that situation.] */ - if ((cnt = dlb_fread(bufin, 1, min((int)buflen, LOADCHUNKSIZE), fh)) < 0L) + if ((cnt = dlb_fread(bufin, 1, min((int) buflen, LOADCHUNKSIZE), fh)) + < 0L) break; buflen -= cnt; /* set up for next iteration, if any */ if (cnt == 0L) { @@ -2049,13 +2182,12 @@ nhl_loadlua(lua_State *L, const char *fname) llret = luaL_loadbuffer(L, buf, strlen(buf), altfname); if (llret != LUA_OK) { - impossible("luaL_loadbuffer: Error loading %s: %s", - altfname, lua_tostring(L, -1)); + impossible("luaL_loadbuffer: Error loading %s: %s", altfname, + lua_tostring(L, -1)); ret = FALSE; goto give_up; } else { - if (nhl_pcall(L, 0, LUA_MULTRET)) { - impossible("Lua error: %s", lua_tostring(L, -1)); + if (nhl_pcall_handle(L, 0, LUA_MULTRET, fname, NHLpa_impossible)) { ret = FALSE; goto give_up; } @@ -2067,6 +2199,7 @@ nhl_loadlua(lua_State *L, const char *fname) if (buf) free((genericptr_t) buf); return ret; +#undef LOADCHUNKSIZE } DISABLE_WARNING_CONDEXPR_IS_CONSTANT @@ -2077,18 +2210,19 @@ nhl_init(nhl_sandbox_info *sbi) /* It would be nice to import EXPECTED from each build system. XXX */ /* And it would be nice to do it only once, but it's cheap. */ #ifndef NHL_VERSION_EXPECTED -#define NHL_VERSION_EXPECTED 50404 +#define NHL_VERSION_EXPECTED 50406 #endif #ifdef NHL_SANDBOX if (NHL_VERSION_EXPECTED != LUA_VERSION_RELEASE_NUM) { panic( - "sandbox doesn't know this Lua version: this=%d != expected=%d ", - LUA_VERSION_RELEASE_NUM, NHL_VERSION_EXPECTED); + "sandbox doesn't know this Lua version: this=%d != expected=%d ", + LUA_VERSION_RELEASE_NUM, NHL_VERSION_EXPECTED); } #endif - lua_State *L = nhlL_newstate(sbi); + lua_State *L = nhlL_newstate(sbi, "nhl_init"); + if(!L) return 0; iflags.in_lua = TRUE; /* Temporary for development XXX */ @@ -2142,8 +2276,24 @@ RESTORE_WARNING_CONDEXPR_IS_CONSTANT void nhl_done(lua_State *L) { - if (L) + if (L) { + nhl_user_data *nud = 0; + (void) lua_getallocf(L, (void **) &nud); + if (gl.loglua) { + if (nud && nud->osteps) { + long ic = nud->statctr * NHL_SB_STEPSIZE; // an approximation + livelog_printf(LL_DEBUG, "LUASTATS DONE %d:%s %ld", nud->sid, + nud->name, ic); + } + if (nud && nud->memlimit && !nud->perpcall) { + livelog_printf(LL_DEBUG, "LUASTATS DMEM %d:%s %lu", nud->sid, + nud->name, (long unsigned) nhl_getmeminuse(L)); + } + } lua_close(L); + if (nud) + nhl_alloc(NULL, nud, 0, 0); // free nud + } iflags.in_lua = FALSE; } @@ -2174,7 +2324,8 @@ DISABLE_WARNING_CONDEXPR_IS_CONSTANT const char * get_lua_version(void) { - nhl_sandbox_info sbi = {NHL_SB_VERSION, 0, 0, 0}; + nhl_sandbox_info sbi = { NHL_SB_VERSION, 1 * 1024 * 1024, 0, + 1 * 1024 * 1024 }; if (gl.lua_ver[0] == 0) { lua_State *L = nhl_init(&sbi); @@ -2189,7 +2340,7 @@ get_lua_version(void) get set up as a lua global */ lua_getglobal(L, "_RELEASE"); if (lua_isstring(L, -1)) - vs = lua_tolstring (L, -1, &len); + vs = lua_tolstring(L, -1, &len); #ifdef LUA_RELEASE else vs = LUA_RELEASE, len = strlen(vs); @@ -2197,7 +2348,7 @@ get_lua_version(void) if (!vs) { lua_getglobal(L, "_VERSION"); if (lua_isstring(L, -1)) - vs = lua_tolstring (L, -1, &len); + vs = lua_tolstring(L, -1, &len); #ifdef LUA_VERSION else vs = LUA_VERSION, len = strlen(vs); @@ -2226,6 +2377,26 @@ RESTORE_WARNING_CONDEXPR_IS_CONSTANT /*** *** SANDBOX / HARDENING CODE ***/ +/* + * Tracing the sandbox: + * 1. Make sure CHRONICLE and LIVELOG are defined in config.h. + * 2. Rebuild the game. + * 3. Run the game and do whatever you want to check with lua. + * 4. Find the logged information in livelog. + * + * Logging format in livelog: + * message=LUASTATS : + * where: + * TYPE + * DONE rough number of step taken during the life of the VM + * DMEM memory in use at destruction of VM + * PCAL rough number of steps during lua_pcall + * PMEM memory in use after lua_pcall returns + * SID a small integer identifying the Lua VM instance + * TAG a string from the nhl_luapcall call + * DATA memory: rough number of bytes in use by the VM + * steps: rough number of steps by the VM(see NHL_SB_STEPSIZE) + */ #ifdef NHL_SANDBOX enum ewhen {NEVER, IFFLAG, EOT}; @@ -2236,52 +2407,54 @@ struct e { /* NHL_BASE_BASE - safe things */ static struct e ct_base_base[] = { - {IFFLAG, "ipairs"}, - {IFFLAG, "next"}, - {IFFLAG, "pairs"}, - {IFFLAG, "pcall"}, - {IFFLAG, "select"}, - {IFFLAG, "tonumber"}, - {IFFLAG, "tostring"}, - {IFFLAG, "type"}, - {IFFLAG, "xpcall"}, - {EOT, NULL} + { IFFLAG, "ipairs" }, + { IFFLAG, "next" }, + { IFFLAG, "pairs" }, + { IFFLAG, "pcall" }, + { IFFLAG, "select" }, + { IFFLAG, "tonumber" }, + { IFFLAG, "tostring" }, + { IFFLAG, "type" }, + { IFFLAG, "xpcall" }, + { EOT, NULL } }; -/* NHL_BASE_ERROR - not really safe - might not want Lua to kill the process */ +/* NHL_BASE_ERROR - not really safe--might not want Lua to kill the process */ static struct e ct_base_error[] = { - {IFFLAG, "assert"}, /* ok, calls error */ - {IFFLAG, "error"}, /* ok, calls G->panic */ - {NEVER, "print"}, /*not ok - calls lua_writestring/lua_writeline -> stdout*/ - {NEVER, "warn"}, /*not ok - calls lua_writestringerror -> stderr*/ - {EOT, NULL} + { IFFLAG, "assert" }, /* ok, calls error */ + { IFFLAG, "error" }, /* ok, calls G->panic */ + { NEVER, "print" }, /* not ok - calls lua_writestring/lua_writeline + * which write to stdout */ + { NEVER, "warn" }, /* not ok - calls lua_writestringerror which writes + * to stderr */ + { EOT, NULL } }; /* NHL_BASE_META - metatable access */ static struct e ct_base_meta[] = { - {IFFLAG, "getmetatable"}, - {IFFLAG, "rawequal"}, - {IFFLAG, "rawget"}, - {IFFLAG, "rawlen"}, - {IFFLAG, "rawset"}, - {IFFLAG, "setmetatable"}, - {EOT, NULL} + { IFFLAG, "getmetatable" }, + { IFFLAG, "rawequal" }, + { IFFLAG, "rawget" }, + { IFFLAG, "rawlen" }, + { IFFLAG, "rawset" }, + { IFFLAG, "setmetatable" }, + { EOT, NULL } }; /* NHL_BASE_GC - questionable safety */ static struct e ct_base_iffy[] = { - {IFFLAG, "collectgarbage"}, - {EOT, NULL} + { IFFLAG, "collectgarbage" }, + { EOT, NULL } }; /* NHL_BASE_UNSAFE - include only if required */ /* TODO: if NHL_BASE_UNSAFE is ever used, we need to wrap lua_load with * something to forbid mode=="b" */ static struct e ct_base_unsafe[] = { - {IFFLAG, "dofile"}, - {IFFLAG, "loadfile"}, - {IFFLAG, "load"}, - {EOT, NULL} + { IFFLAG, "dofile" }, + { IFFLAG, "loadfile" }, + { IFFLAG, "load" }, + { EOT, NULL } }; /* no ct_co_ tables; all fns at same level of concern */ @@ -2293,54 +2466,54 @@ static struct e ct_base_unsafe[] = { /* possible ct_debug tables - likely to need changes */ static struct e ct_debug_debug[] = { - {NEVER, "debug"}, /* uses normal I/O so needs re-write */ - {IFFLAG, "getuservalue"}, - {NEVER, "gethook"}, /* see sethook */ - {IFFLAG, "getinfo"}, - {IFFLAG, "getlocal"}, - {IFFLAG, "getregistry"}, - {IFFLAG, "getmetatable"}, - {IFFLAG, "getupvalue"}, - {IFFLAG, "upvaluejoin"}, - {IFFLAG, "upvalueid"}, - {IFFLAG, "setuservalue"}, - {NEVER, "sethook"}, /* used for memory and step limits */ - {IFFLAG, "setlocal"}, - {IFFLAG, "setmetatable"}, - {IFFLAG, "setupvalue"}, - {IFFLAG, "setcstacklimit"}, - {EOT, NULL} + { NEVER, "debug" }, /* uses normal I/O so needs re-write */ + { IFFLAG, "getuservalue" }, + { NEVER, "gethook" }, /* see sethook */ + { IFFLAG, "getinfo" }, + { IFFLAG, "getlocal" }, + { IFFLAG, "getregistry" }, + { IFFLAG, "getmetatable" }, + { IFFLAG, "getupvalue" }, + { IFFLAG, "upvaluejoin" }, + { IFFLAG, "upvalueid" }, + { IFFLAG, "setuservalue" }, + { NEVER, "sethook" }, /* used for memory and step limits */ + { IFFLAG, "setlocal" }, + { IFFLAG, "setmetatable" }, + { IFFLAG, "setupvalue" }, + { IFFLAG, "setcstacklimit" }, + { EOT, NULL } }; static struct e ct_debug_safe[] = { - {IFFLAG, "traceback"}, - {EOT, NULL} + { IFFLAG, "traceback" }, + { EOT, NULL } }; /* possible ct_os_ tables */ static struct e ct_os_time[] = { - {IFFLAG, "clock"}, /* is this portable? XXX */ - {IFFLAG, "date"}, - {IFFLAG, "difftime"}, - {IFFLAG, "time"}, - {EOT, NULL} + { IFFLAG, "clock" }, /* is this portable? XXX */ + { IFFLAG, "date" }, + { IFFLAG, "difftime" }, + { IFFLAG, "time" }, + { EOT, NULL } }; static struct e ct_os_files[] = { - {NEVER, "execute"}, /* not portable */ - {NEVER, "exit"}, - {NEVER, "getenv"}, - {IFFLAG, "remove"}, - {IFFLAG, "rename"}, - {NEVER, "setlocale"}, - {NEVER, "tmpname"}, - {EOT, NULL} + { NEVER, "execute" }, /* not portable */ + { NEVER, "exit" }, + { NEVER, "getenv" }, + { IFFLAG, "remove" }, + { IFFLAG, "rename" }, + { NEVER, "setlocale" }, + { NEVER, "tmpname" }, + { EOT, NULL } }; #define DROPIF(flag, lib, ct) \ nhl_clearfromtable(L, !!(lflags & flag), lib, ct) -static void +staticfn void nhl_clearfromtable(lua_State *L, int flag, int tndx, struct e *todo) { while (todo->when != EOT) { @@ -2391,27 +2564,28 @@ return values from "call it": * We're going to do #3. */ #ifdef notyet -static boolean +staticfn boolean start_luapat(void) { int rv; -/* XXX set memory and step limits */ - nhl_sandbox_info sbi = {NHL_SB_STRING, 0, 0, 0}; + /* XXX set memory and step limits */ + nhl_sandbox_info sbi = { NHL_SB_STRING, 0, 0, 0 }; if ((luapat = nhl_init(&sbi)) == NULL) return FALSE; /* load a pattern matching function */ - rv = luaL_loadstring(luapat, - "function matches(s,p) return not not stringm.match(s,p) end"); + rv = luaL_loadstring( + luapat, + "function matches(s,p) return not not stringm.match(s,p) end"); if (rv != LUA_OK) { - panic("start_luapat: %d",rv); + panic("start_luapat: %d", rv); } return TRUE; } #endif -static void +staticfn void end_luapat(void) { if (luapat) { @@ -2421,7 +2595,7 @@ end_luapat(void) } #ifdef notyet -static int +staticfn int opencheckpat(lua_State *L, const char *ename, int param) { /* careful - we're using 2 different and unrelated Lua states */ @@ -2434,7 +2608,7 @@ opencheckpat(lua_State *L, const char *ename, int param) lua_pushstring(luapat, string); /* -0,+1 */ - (void)lua_getfield(L, -1, ename); /* pattern -0,+1 */ + (void) lua_getfield(L, -1, ename); /* pattern -0,+1 */ lua_pop(L, 1); /* -1,+0 */ string = lua_tolstring(L, -1, NULL); /* -0,+0 */ lua_pushstring(luapat, string); /* -0,+1 */ @@ -2460,13 +2634,15 @@ is pop sufficient? XXX or wrong - look at the balance */ #define HOOKTBLNAME "org.nethack.nethack.sb.fs" #ifdef notyet -static int (*io_open)(lua_State *) = NULL; /* XXX this may have to be in g TBD */ +static int (*io_open)(lua_State *) = NULL; /* XXX this may have to be in + * struct g (gi now) TBD */ #endif void nhl_pushhooked_open_table(lua_State *L) { int hot = lua_getfield(L, LUA_REGISTRYINDEX, HOOKTBLNAME); + if (hot == LUA_TNONE) { lua_newtable(L); lua_pushvalue(L, -1); @@ -2475,7 +2651,7 @@ nhl_pushhooked_open_table(lua_State *L) } #ifdef notyet -static int +staticfn int hooked_open(lua_State *L) { const char *mode; @@ -2539,11 +2715,11 @@ hooked_open(lua_State *L) return NHL_SBRV_DENY; /* default to "no" */ doopen: - lua_settop(L, params+1); + lua_settop(L, params + 1); return (*io_open)(L); } -static boolean +staticfn boolean hook_open(lua_State *L) { boolean rv = FALSE; @@ -2561,7 +2737,7 @@ hook_open(lua_State *L) * doesn't have to work, but POSIX says it does. So it * _should_ work everywhere but all we can do without messing * around inside Lua is to try to keep the compiler quiet. */ - io_open = (int (*)(lua_State *))lua_topointer(L, -1); + io_open = (int (*)(lua_State *)) lua_topointer(L, -1); lua_pushcfunction(L, hooked_open); lua_setfield(L, -1, "open"); rv = TRUE; @@ -2575,7 +2751,7 @@ hook_open(lua_State *L) DISABLE_WARNING_CONDEXPR_IS_CONSTANT #ifdef NHL_SANDBOX -static void +staticfn void nhlL_openlibs(lua_State *L, uint32_t lflags) { /* translate lflags from user-friendly to internal */ @@ -2698,41 +2874,30 @@ WRITEIO: needs changes to hooked open? RESTORE_WARNING_CONDEXPR_IS_CONSTANT -/* - * All we can do is approximate the amount of storage used. Every allocator - * has different overhead and uses that overhead differently. Since we're - * really just trying to prevent egregious use, we default to a minimum - * allocation size of 16 and if you know better about your allocator (and - * it's worth the processing time), it can be overridden. - */ -#ifndef NHL_ALLOC_ADJUST -#define NHL_ALLOC_ADJUST(d) d = (((d) + 15) & ~15) -#endif -static void * -nhl_alloc(void *ud, void *ptr, size_t osize, size_t nsize) +staticfn void * +nhl_alloc(void *ud, void *ptr, size_t osize UNUSED, size_t nsize) { nhl_user_data *nud = ud; - if (nud && nud->memlimit) { /* this state is size limited */ - uint32_t delta = !ptr ? nsize : nsize - osize; - - NHL_ALLOC_ADJUST(delta); - nud->inuse += delta; - if (nud->inuse > nud->memlimit) - return 0; - } - if (nsize == 0) { - free(ptr); + if (ptr != NULL) { + free(ptr); + } return NULL; } + /* Check nud->L because it will be NULL during state init. */ + if (nud && nud->L && nud->memlimit) { /* this state is size limited */ + if (nhl_getmeminuse(nud->L) > nud->memlimit) + return NULL; + } + return re_alloc(ptr, nsize); } DISABLE_WARNING_UNREACHABLE_CODE -static int +staticfn int nhl_panic(lua_State *L) { const char *msg = lua_tostring(L, -1); @@ -2748,7 +2913,7 @@ RESTORE_WARNING_UNREACHABLE_CODE /* called when lua issues a warning message; the text of the message is passed to us in pieces across multiple function calls */ -static void +staticfn void nhl_warn( void *userdata UNUSED, const char *msg_fragment, @@ -2769,7 +2934,7 @@ nhl_warn( } #ifdef NHL_SANDBOX -static void +staticfn void nhl_hookfn(lua_State *L, lua_Debug *ar UNUSED) { nhl_user_data *nud; @@ -2780,30 +2945,38 @@ nhl_hookfn(lua_State *L, lua_Debug *ar UNUSED) longjmp(nud->jb, 1); nud->steps -= NHL_SB_STEPSIZE; + nud->statctr++; } #endif -static lua_State * -nhlL_newstate(nhl_sandbox_info *sbi) +staticfn lua_State * +nhlL_newstate(nhl_sandbox_info *sbi, const char *name) { nhl_user_data *nud = 0; - if (sbi->memlimit || sbi->steps) { + if (sbi->memlimit || sbi->steps || sbi->perpcall) { nud = nhl_alloc(NULL, NULL, 0, sizeof (struct nhl_user_data)); if (!nud) return 0; + nud->L = NULL; nud->memlimit = sbi->memlimit; nud->perpcall = 0; /* set up below, if needed */ nud->steps = 0; nud->osteps = 0; nud->flags = sbi->flags; /* save reporting flags */ - uint32_t sz = sizeof (struct nhl_user_data); - NHL_ALLOC_ADJUST(sz); - nud->inuse = sz; + nud->statctr = 0; + + if (name) { + nud->name = name; + } + nud->sid = ++gl.lua_sid; } lua_State *L = lua_newstate(nhl_alloc, nud); + if (!L) + panic("NULL lua_newstate"); + if(nud) nud->L = L; lua_atpanic(L, nhl_panic); #if LUA_VERSION_NUM == 504 lua_setwarnf(L, nhl_warn, L); diff --git a/src/nhmd4.c b/src/nhmd4.c new file mode 100644 index 0000000000..02d9aa74e1 --- /dev/null +++ b/src/nhmd4.c @@ -0,0 +1,298 @@ +/* NetHack 3.7 nhmd4.c $NHDT-Date: 1708811387 2024/02/24 21:49:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.0 $ */ +/*-Copyright (c) Kenneth Lorber, Kensington, Maryland, 2024 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Usage is to try to match traceback data with the instance of the + * program which produced that, not for security related purposes. + * + * Derived from: + */ +/* + * MD4 (RFC-1320) message digest. + * Modified from MD5 code by Andrey Panin + * + * Written by Solar Designer in 2001, and placed in + * the public domain. There's absolutely no warranty. + * + * This differs from Colin Plumb's older public domain implementation in + * that no 32-bit integer data type is required, there's no compile-time + * endianness configuration, and the function prototypes match OpenSSL's. + * The primary goals are portability and ease of use. + * + * This implementation is meant to be fast, but not as fast as possible. + * Some known optimizations are not included to reduce source code size + * and avoid compile-time configuration. + */ +#include "hack.h" +#ifdef CRASHREPORT + +#include "nhmd4.h" + +staticfn const unsigned char *nhmd4_body(struct nhmd4_context *, + const unsigned char *, size_t); + +/* Avoid a conflict from a Lua header */ +#ifdef G +#undef G +#endif + +/* + * The basic MD4 functions. + */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) + +/* + * STEP: the MD4 transformation used for all four rounds. + * (Joining two expressions with the comma operator provides a sequence + * point. C89/C90 and later guarantee that the first will be fully complete + * before the second starts, making both assignments to 'a' be well defined.) + */ +#define STEP(f, a, b, c, d, x, s) \ + (((a) += f((b), (c), (d)) + (x)), \ + ((a) = ((a) << (s)) | ((a) >> (32 - (s))))) + +/* + * SET reads 4 input bytes in little-endian byte order and stores them + * in a properly aligned word in host byte order. + * + * The check for little-endian architectures which tolerate unaligned + * memory accesses is just an optimization. Nothing will break if it + * doesn't work. + */ +#if defined(__i386__) || defined(__x86_64__) +#define SET(n) (*(const quint32 *) &ptr[(n) * 4]) +#define GET(n) SET(n) +#else +#define SET(n) \ + (ctx->block[(n)] = \ + ((quint32) ptr[(n) * 4] \ + | ((quint32) ptr[(n) * 4 + 1] << 8) \ + | ((quint32) ptr[(n) * 4 + 2] << 16) \ + | ((quint32) ptr[(n) * 4 + 3] << 24))) +#define GET(n) (ctx->block[(n)]) +#endif + +/* + * This processes one or more 64-byte data blocks, but does NOT update + * the bit counters. There're no alignment requirements. + */ +staticfn const unsigned char * +nhmd4_body( + struct nhmd4_context *ctx, + const unsigned char *data, + size_t size) +{ + const unsigned char *ptr; + quint32 a, b, c, d; + quint32 saved_a, saved_b, saved_c, saved_d; + + ptr = data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + +/* Round 1 */ + STEP(F, a, b, c, d, SET( 0), 3); + STEP(F, d, a, b, c, SET( 1), 7); + STEP(F, c, d, a, b, SET( 2), 11); + STEP(F, b, c, d, a, SET( 3), 19); + + STEP(F, a, b, c, d, SET( 4), 3); + STEP(F, d, a, b, c, SET( 5), 7); + STEP(F, c, d, a, b, SET( 6), 11); + STEP(F, b, c, d, a, SET( 7), 19); + + STEP(F, a, b, c, d, SET( 8), 3); + STEP(F, d, a, b, c, SET( 9), 7); + STEP(F, c, d, a, b, SET(10), 11); + STEP(F, b, c, d, a, SET(11), 19); + + STEP(F, a, b, c, d, SET(12), 3); + STEP(F, d, a, b, c, SET(13), 7); + STEP(F, c, d, a, b, SET(14), 11); + STEP(F, b, c, d, a, SET(15), 19); +/* Round 2 */ + STEP(G, a, b, c, d, GET( 0) + 0x5A827999, 3); + STEP(G, d, a, b, c, GET( 4) + 0x5A827999, 5); + STEP(G, c, d, a, b, GET( 8) + 0x5A827999, 9); + STEP(G, b, c, d, a, GET(12) + 0x5A827999, 13); + + STEP(G, a, b, c, d, GET( 1) + 0x5A827999, 3); + STEP(G, d, a, b, c, GET( 5) + 0x5A827999, 5); + STEP(G, c, d, a, b, GET( 9) + 0x5A827999, 9); + STEP(G, b, c, d, a, GET(13) + 0x5A827999, 13); + + STEP(G, a, b, c, d, GET( 2) + 0x5A827999, 3); + STEP(G, d, a, b, c, GET( 6) + 0x5A827999, 5); + STEP(G, c, d, a, b, GET(10) + 0x5A827999, 9); + STEP(G, b, c, d, a, GET(14) + 0x5A827999, 13); + + STEP(G, a, b, c, d, GET( 3) + 0x5A827999, 3); + STEP(G, d, a, b, c, GET( 7) + 0x5A827999, 5); + STEP(G, c, d, a, b, GET(11) + 0x5A827999, 9); + STEP(G, b, c, d, a, GET(15) + 0x5A827999, 13); +/* Round 3 */ + STEP(H, a, b, c, d, GET( 0) + 0x6ED9EBA1, 3); + STEP(H, d, a, b, c, GET( 8) + 0x6ED9EBA1, 9); + STEP(H, c, d, a, b, GET( 4) + 0x6ED9EBA1, 11); + STEP(H, b, c, d, a, GET(12) + 0x6ED9EBA1, 15); + + STEP(H, a, b, c, d, GET( 2) + 0x6ED9EBA1, 3); + STEP(H, d, a, b, c, GET(10) + 0x6ED9EBA1, 9); + STEP(H, c, d, a, b, GET( 6) + 0x6ED9EBA1, 11); + STEP(H, b, c, d, a, GET(14) + 0x6ED9EBA1, 15); + + STEP(H, a, b, c, d, GET( 1) + 0x6ED9EBA1, 3); + STEP(H, d, a, b, c, GET( 9) + 0x6ED9EBA1, 9); + STEP(H, c, d, a, b, GET( 5) + 0x6ED9EBA1, 11); + STEP(H, b, c, d, a, GET(13) + 0x6ED9EBA1, 15); + + STEP(H, a, b, c, d, GET( 3) + 0x6ED9EBA1, 3); + STEP(H, d, a, b, c, GET(11) + 0x6ED9EBA1, 9); + STEP(H, c, d, a, b, GET( 7) + 0x6ED9EBA1, 11); + STEP(H, b, c, d, a, GET(15) + 0x6ED9EBA1, 15); + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while (size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; +} + +void +nhmd4_init( + struct nhmd4_context *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->lo = 0; + ctx->hi = 0; +} + +void +nhmd4_update( + struct nhmd4_context *ctx, + const unsigned char *data, + size_t size) +{ + /* @UNSAFE */ + quint32 saved_lo; + unsigned long used, free; + + saved_lo = ctx->lo; + if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) + ctx->hi++; + ctx->hi += (quint32)(size >> 29); + + used = saved_lo & 0x3f; + + if (used) { + free = 64 - used; + + if (size < free) { + memcpy(&ctx->buffer[used], data, size); + return; + } + + memcpy(&ctx->buffer[used], data, free); + data = (const unsigned char *) data + free; + size -= free; + nhmd4_body(ctx, ctx->buffer, 64); + } + + if (size >= 64) { + data = nhmd4_body(ctx, data, size & ~0x3fUL); + size &= 0x3fUL; + } + + memcpy(ctx->buffer, data, size); +} + +void +nhmd4_final( + struct nhmd4_context *ctx, + unsigned char result[NHMD4_RESULTLEN]) +{ + /* @UNSAFE */ + unsigned long used, free; + + used = ctx->lo & 0x3fUL; + + ctx->buffer[used++] = 0x80; + + free = 64 - used; + + if (free < 8) { + memset(&ctx->buffer[used], 0, free); + nhmd4_body(ctx, ctx->buffer, 64); + used = 0; + free = 64; + } + + memset(&ctx->buffer[used], 0, free - 8); + + ctx->lo <<= 3; + ctx->buffer[56] = ctx->lo; + ctx->buffer[57] = ctx->lo >> 8; + ctx->buffer[58] = ctx->lo >> 16; + ctx->buffer[59] = ctx->lo >> 24; + ctx->buffer[60] = ctx->hi; + ctx->buffer[61] = ctx->hi >> 8; + ctx->buffer[62] = ctx->hi >> 16; + ctx->buffer[63] = ctx->hi >> 24; + + nhmd4_body(ctx, ctx->buffer, 64); + + result[0] = ctx->a; + result[1] = ctx->a >> 8; + result[2] = ctx->a >> 16; + result[3] = ctx->a >> 24; + result[4] = ctx->b; + result[5] = ctx->b >> 8; + result[6] = ctx->b >> 16; + result[7] = ctx->b >> 24; + result[8] = ctx->c; + result[9] = ctx->c >> 8; + result[10] = ctx->c >> 16; + result[11] = ctx->c >> 24; + result[12] = ctx->d; + result[13] = ctx->d >> 8; + result[14] = ctx->d >> 16; + result[15] = ctx->d >> 24; + + memset(ctx, 0, sizeof *ctx); +} + +#undef F +#undef G +#undef H +#undef STEP +#undef SET +#undef GET + +#endif /* CRASHREPORT */ + +/*nhmd4.c*/ diff --git a/src/o_init.c b/src/o_init.c index b89a4cee73..a3b76bc427 100644 --- a/src/o_init.c +++ b/src/o_init.c @@ -1,22 +1,24 @@ -/* NetHack 3.7 o_init.c $NHDT-Date: 1672829455 2023/01/04 10:50:55 $ $NHDT-Branch: naming-overflow-fix $:$NHDT-Revision: 1.68 $ */ +/* NetHack 3.7 o_init.c $NHDT-Date: 1720391455 2024/07/07 22:30:55 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.87 $ */ /* 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" -static void setgemprobs(d_level *); -static void shuffle(int, int, boolean); -static void shuffle_all(void); -static int QSORTCALLBACK discovered_cmp(const genericptr, const genericptr); -static char *sortloot_descr(int, char *); -static char *disco_typename(int); -static void disco_append_typename(char *, int); -static char *oclass_to_name(char, char *); +staticfn void setgemprobs(d_level *); +staticfn void randomize_gem_colors(void); +staticfn void shuffle(int, int, boolean); +staticfn void shuffle_all(void); +staticfn int QSORTCALLBACK discovered_cmp(const genericptr, const genericptr); +staticfn char *sortloot_descr(int, char *); +staticfn char *disco_typename(int); +staticfn void disco_append_typename(char *, int); +staticfn void disco_output_sorted(winid, char **, int, boolean); +staticfn char *oclass_to_name(char, char *); #ifdef TILES_IN_GLYPHMAP extern glyph_map glyphmap[MAX_GLYPH]; -static void shuffle_tiles(void); +staticfn void shuffle_tiles(void); /* Shuffle tile assignments to match descriptions, so a red potion isn't * displayed with a blue tile and so on. @@ -27,7 +29,7 @@ static void shuffle_tiles(void); * is restored. So might as well do that the first time instead of writing * another routine. */ -static void +staticfn void shuffle_tiles(void) { int i; @@ -46,8 +48,8 @@ shuffle_tiles(void) } #endif /* TILES_IN_GLYPHMAP */ -static void -setgemprobs(d_level* dlev) +staticfn void +setgemprobs(d_level *dlev) { int j, first, lev, sum = 0; @@ -56,7 +58,7 @@ setgemprobs(d_level* dlev) : ledger_no(dlev); else lev = 0; - first = gb.bases[GEM_CLASS]; + first = svb.bases[GEM_CLASS]; for (j = 0; j < 9 - lev / 3; j++) objects[first + j].oc_prob = 0; @@ -71,13 +73,41 @@ setgemprobs(d_level* dlev) objects[j].oc_prob = (171 + j - first) / (LAST_REAL_GEM + 1 - first); /* recompute GEM_CLASS total oc_prob - including rocks/stones */ - for (j = gb.bases[GEM_CLASS]; j < gb.bases[GEM_CLASS + 1]; j++) + for (j = svb.bases[GEM_CLASS]; j < svb.bases[GEM_CLASS + 1]; j++) sum += objects[j].oc_prob; go.oclass_prob_totals[GEM_CLASS] = sum; } +/* some gems can have different colors */ +staticfn void +randomize_gem_colors(void) +{ +#define COPY_OBJ_DESCR(o_dst, o_src) \ + o_dst.oc_descr_idx = o_src.oc_descr_idx, o_dst.oc_color = o_src.oc_color + if (rn2(2)) { /* change turquoise from green to blue? */ + COPY_OBJ_DESCR(objects[TURQUOISE], objects[SAPPHIRE]); + } + if (rn2(2)) { /* change aquamarine from green to blue? */ + COPY_OBJ_DESCR(objects[AQUAMARINE], objects[SAPPHIRE]); + } + switch (rn2(4)) { /* change fluorite from violet? */ + case 0: + break; + case 1: /* blue */ + COPY_OBJ_DESCR(objects[FLUORITE], objects[SAPPHIRE]); + break; + case 2: /* white */ + COPY_OBJ_DESCR(objects[FLUORITE], objects[DIAMOND]); + break; + case 3: /* green */ + COPY_OBJ_DESCR(objects[FLUORITE], objects[EMERALD]); + break; + } +#undef COPY_OBJ_DESCR +} + /* shuffle descriptions on objects o_low to o_high */ -static void +staticfn void shuffle(int o_low, int o_high, boolean domaterial) { int i, j, num_to_shuffle; @@ -120,15 +150,9 @@ init_objects(void) { int i, first, last, prevoclass; char oclass; -#ifdef TEXTCOLOR -#define COPY_OBJ_DESCR(o_dst, o_src) \ - o_dst.oc_descr_idx = o_src.oc_descr_idx, o_dst.oc_color = o_src.oc_color -#else -#define COPY_OBJ_DESCR(o_dst, o_src) o_dst.oc_descr_idx = o_src.oc_descr_idx -#endif for (i = 0; i <= MAXOCLASSES; i++) { - gb.bases[i] = 0; + svb.bases[i] = 0; if (i > 0 && i < MAXOCLASSES && objects[i].oc_class != i) panic( "init_objects: class for generic object #%d doesn't match (%d)", @@ -156,30 +180,11 @@ init_objects(void) last = first + 1; while (last < NUM_OBJECTS && objects[last].oc_class == oclass) last++; - gb.bases[(int) oclass] = first; + svb.bases[(int) oclass] = first; if (oclass == GEM_CLASS) { setgemprobs((d_level *) 0); - - if (rn2(2)) { /* change turquoise from green to blue? */ - COPY_OBJ_DESCR(objects[TURQUOISE], objects[SAPPHIRE]); - } - if (rn2(2)) { /* change aquamarine from green to blue? */ - COPY_OBJ_DESCR(objects[AQUAMARINE], objects[SAPPHIRE]); - } - switch (rn2(4)) { /* change fluorite from violet? */ - case 0: - break; - case 1: /* blue */ - COPY_OBJ_DESCR(objects[FLUORITE], objects[SAPPHIRE]); - break; - case 2: /* white */ - COPY_OBJ_DESCR(objects[FLUORITE], objects[DIAMOND]); - break; - case 3: /* green */ - COPY_OBJ_DESCR(objects[FLUORITE], objects[EMERALD]); - break; - } + randomize_gem_colors(); } first = last; prevoclass = (int) oclass; @@ -187,14 +192,18 @@ init_objects(void) /* extra entry allows deriving the range of a class via bases[class] through bases[class+1]-1 for all classes (except for ILLOBJ_CLASS which is separated from WEAPON_CLASS - by generic objects) */ - gb.bases[MAXOCLASSES] = NUM_OBJECTS; + by generic objects); second extra entry is to prevent an + explained crash in doclassdisco(), where the code ended up + attempting to process non-existent class MAXOCLASSES; the + [MAXOCLASSES+1] element gives that non-class 0 objects + when traversing objects[] from bases[X] through bases[X+1]-1 */ + svb.bases[MAXOCLASSES] = svb.bases[MAXOCLASSES + 1] = NUM_OBJECTS; /* hypothetically someone might remove all objects of some class, or be adding a new class and not populated it yet, leaving gaps in bases[]; guarantee that there are no such gaps */ for (last = MAXOCLASSES - 1; last >= 0; --last) - if (!gb.bases[last]) - gb.bases[last] = gb.bases[last + 1]; + if (!svb.bases[last]) + svb.bases[last] = svb.bases[last + 1]; /* check objects[].oc_name_known */ for (i = MAXOCLASSES; i < NUM_OBJECTS; ++i) { @@ -224,7 +233,7 @@ init_objects(void) } /* Compute the total probability of each object class. - * Assumes gb.bases[] has already been set. */ + * Assumes svb.bases[] has already been set. */ void init_oclass_probs(void) { @@ -236,15 +245,15 @@ init_oclass_probs(void) /* note: for ILLOBJ_CLASS, bases[oclass+1]-1 isn't the last item in the class; but all the generic items have probability 0 so adding them to 'sum' has no impact */ - for (i = gb.bases[oclass]; i < gb.bases[oclass + 1]; ++i) { + for (i = svb.bases[oclass]; i < svb.bases[oclass + 1]; ++i) { sum += objects[i].oc_prob; } if (sum <= 0 && oclass != ILLOBJ_CLASS - && gb.bases[oclass] != gb.bases[oclass + 1]) { + && svb.bases[oclass] != svb.bases[oclass + 1]) { impossible("%s (%d) probability total for oclass %d", !sum ? "zero" : "negative", sum, oclass); /* gracefully fail by setting all members of this class to 1 */ - for (i = gb.bases[oclass]; i < gb.bases[oclass + 1]; ++i) { + for (i = svb.bases[oclass]; i < svb.bases[oclass + 1]; ++i) { objects[i].oc_prob = 1; sum++; } @@ -277,14 +286,14 @@ obj_shuffle_range( break; case POTION_CLASS: /* potion of water has the only fixed description */ - *lo_p = gb.bases[POTION_CLASS]; + *lo_p = svb.bases[POTION_CLASS]; *hi_p = POT_WATER - 1; break; case AMULET_CLASS: case SCROLL_CLASS: case SPBOOK_CLASS: /* exclude non-magic types and also unique ones */ - *lo_p = gb.bases[ocls]; + *lo_p = svb.bases[ocls]; for (i = *lo_p; objects[i].oc_class == ocls; i++) if (objects[i].oc_unique || !objects[i].oc_magic) break; @@ -294,8 +303,8 @@ obj_shuffle_range( case WAND_CLASS: case VENOM_CLASS: /* entire class */ - *lo_p = gb.bases[ocls]; - *hi_p = gb.bases[ocls + 1] - 1; + *lo_p = svb.bases[ocls]; + *hi_p = svb.bases[ocls + 1] - 1; break; } @@ -307,7 +316,7 @@ obj_shuffle_range( } /* randomize object descriptions */ -static void +staticfn void shuffle_all(void) { /* entire classes; obj_shuffle_range() handles their exceptions */ @@ -323,7 +332,8 @@ shuffle_all(void) /* do whole classes (amulets, &c) */ for (idx = 0; idx < SIZE(shuffle_classes); idx++) { - obj_shuffle_range(gb.bases[(int) shuffle_classes[idx]], &first, &last); + obj_shuffle_range(svb.bases[(int) shuffle_classes[idx]], + &first, &last); shuffle(first, last, TRUE); } /* do type ranges (helms, &c) */ @@ -360,16 +370,16 @@ oinit(void) } void -savenames(NHFILE* nhfp) +savenames(NHFILE *nhfp) { int i; unsigned int len; if (perform_bwrite(nhfp)) { if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t)gb.bases, sizeof gb.bases); - bwrite(nhfp->fd, (genericptr_t)gd.disco, sizeof gd.disco); - bwrite(nhfp->fd, (genericptr_t)objects, + 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); } } @@ -381,8 +391,8 @@ savenames(NHFILE* nhfp) if (perform_bwrite(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); + bwrite(nhfp->fd, (genericptr_t) &len, sizeof len); + bwrite(nhfp->fd, (genericptr_t) objects[i].oc_uname, len); } } if (release_data(nhfp)) { @@ -393,16 +403,16 @@ savenames(NHFILE* nhfp) } void -restnames(NHFILE* nhfp) +restnames(NHFILE *nhfp) { int i; unsigned int len = 0; if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t) gb.bases, sizeof gb.bases); - mread(nhfp->fd, (genericptr_t) gd.disco, sizeof gd.disco); + 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, - sizeof(struct objclass) * NUM_OBJECTS); + NUM_OBJECTS * sizeof (struct objclass)); } for (i = 0; i < NUM_OBJECTS; i++) { if (objects[i].oc_uname) { @@ -411,7 +421,7 @@ restnames(NHFILE* nhfp) } objects[i].oc_uname = (char *) alloc(len); if (nhfp->structlevel) { - mread(nhfp->fd, (genericptr_t)objects[i].oc_uname, len); + mread(nhfp->fd, (genericptr_t) objects[i].oc_uname, len); } } } @@ -435,10 +445,10 @@ discover_object( uname'd) or the next open slot; one or the other will be found before we reach the next class... */ - for (dindx = gb.bases[acls]; gd.disco[dindx] != 0; dindx++) - if (gd.disco[dindx] == oindx) + for (dindx = svb.bases[acls]; svd.disco[dindx] != 0; dindx++) + if (svd.disco[dindx] == oindx) break; - gd.disco[dindx] = oindx; + svd.disco[dindx] = oindx; /* if already known, we forced an item with a Japanese name into disco[] but don't want to exercise wisdom or update perminv */ @@ -451,7 +461,7 @@ discover_object( exercise(A_WIS, TRUE); } /* !in_moveloop => initial inventory, gameover => final disclosure */ - if (gp.program_state.in_moveloop && !gp.program_state.gameover) { + if (program_state.in_moveloop && !program_state.gameover) { if (objects[oindx].oc_class == GEM_CLASS) gem_learned(oindx); /* could affect price of unpaid gems */ update_inventory(); @@ -464,22 +474,22 @@ void undiscover_object(int oindx) { if (!objects[oindx].oc_name_known) { - register int dindx, acls = objects[oindx].oc_class; - register boolean found = FALSE; + int dindx, acls = objects[oindx].oc_class; + boolean found = FALSE; /* find the object; shift those behind it forward one slot */ - for (dindx = gb.bases[acls]; - dindx < NUM_OBJECTS && gd.disco[dindx] != 0 + for (dindx = svb.bases[acls]; + dindx < NUM_OBJECTS && svd.disco[dindx] != 0 && objects[dindx].oc_class == acls; dindx++) if (found) - gd.disco[dindx - 1] = gd.disco[dindx]; - else if (gd.disco[dindx] == oindx) + svd.disco[dindx - 1] = svd.disco[dindx]; + else if (svd.disco[dindx] == oindx) found = TRUE; /* clear last slot */ if (found) - gd.disco[dindx - 1] = 0; + svd.disco[dindx - 1] = 0; else impossible("named object not in disco"); @@ -511,7 +521,7 @@ static const short uniq_objs[] = { }; /* discoveries qsort comparison function */ -static int QSORTCALLBACK +staticfn int QSORTCALLBACK discovered_cmp(const genericptr v1, const genericptr v2) { const char *s1 = *(const char **) v1; @@ -525,7 +535,7 @@ discovered_cmp(const genericptr v1, const genericptr v2) return res; } -static char * +staticfn char * sortloot_descr(int otyp, char *outbuf) { Loot sl_cookie; @@ -540,7 +550,7 @@ sortloot_descr(int otyp, char *outbuf) o.corpsenm = NON_PM; /* suppress statue and figurine details */ /* but suppressing fruit details leads to "bad fruit #0" */ if (otyp == SLIME_MOLD) - o.spe = gc.context.current_fruit; + o.spe = svc.context.current_fruit; (void) memset((genericptr_t) &sl_cookie, 0, sizeof sl_cookie); sl_cookie.obj = (struct obj *) 0; @@ -557,8 +567,8 @@ sortloot_descr(int otyp, char *outbuf) #define DISCO_ALPHABYCLASS 2 /* alphabetized within each class */ #define DISCO_ALPHABETIZED 3 /* alphabetized across all classes */ /* also used in options.c (optfn_sortdiscoveries) */ -const char disco_order_let[] = "osca"; -const char *const disco_orders_descr[] = { +static const char disco_order_let[] = "osca"; +static const char *const disco_orders_descr[] = { "by order of discovery within each class", "sortloot order (by class with some sub-class groupings)", "alphabetical within each class", @@ -568,13 +578,13 @@ const char *const disco_orders_descr[] = { int choose_disco_sort( - int mode) /* 0 => 'O' cmd, 1 => full discoveries; 2 => class discoveries */ + int mode) /* 0 => 'O' cmd, 1 => full discoveries; 2 => class disco */ { winid tmpwin; menu_item *selected; anything any; int i, n, choice; - int clr = 0; + int clr = NO_COLOR; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -592,18 +602,13 @@ choose_disco_sort( /* called via 'm `' where full alphabetize doesn't make sense (only showing one class so can't span all classes) but the chosen sort will stick and also apply to '\' usage */ - any = cg.zeroany; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, - "", MENU_ITEMFLAGS_NONE); - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, - "Note: full alphabetical and alphabetical within class", - MENU_ITEMFLAGS_NONE); - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, - " are equivalent for single class discovery, but", - MENU_ITEMFLAGS_NONE); - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, - " will matter for future use of total discoveries.", - MENU_ITEMFLAGS_NONE); + add_menu_str(tmpwin, ""); + add_menu_str(tmpwin, + "Note: full alphabetical and alphabetical within class"); + add_menu_str(tmpwin, + " are equivalent for single class discovery, but"); + add_menu_str(tmpwin, + " will matter for future use of total discoveries."); } end_menu(tmpwin, "Ordering of discoveries"); @@ -621,7 +626,7 @@ choose_disco_sort( } /* augment obj_typename() with explanation of Japanese item names */ -static char * +staticfn char * disco_typename(int otyp) { char *result = obj_typename(otyp); @@ -653,7 +658,7 @@ disco_typename(int otyp) } /* append typename(dis) to buf[], possibly truncating in the process */ -static void +staticfn void disco_append_typename(char *buf, int dis) { unsigned len = (unsigned) strlen(buf); @@ -676,6 +681,27 @@ 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) +{ + char *p; + int j; + + qsort(sorted_lines, sorted_ct, sizeof (char *), discovered_cmp); + for (j = 0; j < sorted_ct; ++j) { + p = sorted_lines[j]; + if (lootsort) { + p[6] = p[0]; /* '*' or ' ' */ + p += 6; + } + putstr(tmpwin, 0, p); + free(sorted_lines[j]), sorted_lines[j] = 0; + } +} + /* the #known command - show discovered object types */ int dodiscovered(void) /* free after Robert Viduya */ @@ -684,7 +710,7 @@ dodiscovered(void) /* free after Robert Viduya */ char *s, *p, oclass, prev_class, classes[MAXOCLASSES], buf[BUFSZ], *sorted_lines[NUM_OBJECTS]; /* overkill */ - int i, j, dis, ct, uniq_ct, arti_ct, sorted_ct; + int i, dis, ct, uniq_ct, arti_ct, sorted_ct; long sortindx; // should be ptrdiff_t, but we don't require that exists boolean alphabetized, alphabyclass, lootsort; @@ -700,7 +726,7 @@ dodiscovered(void) /* free after Robert Viduya */ lootsort = (flags.discosort == 's'); sortindx = strchr(disco_order_let, flags.discosort) - disco_order_let; - tmpwin = create_nhwindow(NHW_MENU); + tmpwin = create_nhwindow(NHW_TEXT); Sprintf(buf, "Discoveries, %s", disco_orders_descr[sortindx]); putstr(tmpwin, 0, buf); putstr(tmpwin, 0, ""); @@ -711,7 +737,7 @@ dodiscovered(void) /* free after Robert Viduya */ for (i = dis = 0; i < SIZE(uniq_objs); i++) if (objects[uniq_objs[i]].oc_name_known) { if (!dis++) - putstr(tmpwin, iflags.menu_headings, "Unique items"); + putstr(tmpwin, iflags.menu_headings.attr, "Unique items"); ++uniq_ct; Sprintf(buf, " %s", OBJ_NAME(objects[uniq_objs[i]])); putstr(tmpwin, 0, buf); @@ -729,29 +755,20 @@ dodiscovered(void) /* free after Robert Viduya */ for (s = classes; *s; s++) { oclass = *s; prev_class = oclass + 1; /* forced different from oclass */ - for (i = gb.bases[(int) oclass]; + for (i = svb.bases[(int) oclass]; i < NUM_OBJECTS && objects[i].oc_class == oclass; i++) { - if ((dis = gd.disco[i]) != 0 && interesting_to_discover(dis)) { + if ((dis = svd.disco[i]) != 0 && interesting_to_discover(dis)) { ct++; if (oclass != prev_class) { if ((alphabyclass || lootsort) && sorted_ct) { /* output previous class */ - qsort(sorted_lines, sorted_ct, sizeof (char *), - discovered_cmp); - for (j = 0; j < sorted_ct; ++j) { - p = sorted_lines[j]; - if (lootsort) { - p[6] = p[0]; /* '*' or ' ' */ - p += 6; - } - putstr(tmpwin, 0, p); - free(sorted_lines[j]), sorted_lines[j] = 0; - } + disco_output_sorted(tmpwin, sorted_lines, sorted_ct, + lootsort); sorted_ct = 0; } if (!alphabetized || alphabyclass) { /* header for new class */ - putstr(tmpwin, iflags.menu_headings, + putstr(tmpwin, iflags.menu_headings.attr, let_to_name(oclass, FALSE, FALSE)); prev_class = oclass; } @@ -777,17 +794,8 @@ dodiscovered(void) /* free after Robert Viduya */ classes, we normally don't need a header; but it we showed any unique items or any artifacts then we do need one */ if ((uniq_ct || arti_ct) && alphabetized && !alphabyclass) - putstr(tmpwin, iflags.menu_headings, "Discovered items"); - qsort(sorted_lines, sorted_ct, sizeof (char *), discovered_cmp); - for (j = 0; j < sorted_ct; ++j) { - p = sorted_lines[j]; - if (lootsort) { - p[6] = p[0]; /* '*' or ' ' */ - p += 6; - } - putstr(tmpwin, 0, p); - free(sorted_lines[j]), sorted_lines[j] = 0; - } + putstr(tmpwin, iflags.menu_headings.attr, "Discovered items"); + disco_output_sorted(tmpwin, sorted_lines, sorted_ct, lootsort); } display_nhwindow(tmpwin, TRUE); } @@ -797,7 +805,7 @@ dodiscovered(void) /* free after Robert Viduya */ } /* lower case let_to_name() output, which differs from def_oc_syms[].name */ -static char * +staticfn char * oclass_to_name(char oclass, char *buf) { char *s; @@ -825,7 +833,7 @@ doclassdisco(void) *sorted_lines[NUM_OBJECTS]; /* overkill */ int i, ct, dis, xtras, sorted_ct; boolean traditional, alphabetized, lootsort; - int clr = 0; + int clr = NO_COLOR; if (!flags.discosort || !(p = strchr(disco_order_let, flags.discosort))) flags.discosort = 'o'; @@ -878,11 +886,11 @@ doclassdisco(void) for (s = allclasses; *s; ++s) { oclass = *s; c = def_oc_syms[(int) oclass].sym; - for (i = gb.bases[(int) oclass]; + for (i = svb.bases[(int) oclass]; i < NUM_OBJECTS && objects[i].oc_class == oclass; ++i) - if ((dis = gd.disco[i]) != 0 && interesting_to_discover(dis)) { + if ((dis = svd.disco[i]) != 0 && interesting_to_discover(dis)) { if (!strchr(discosyms, c)) { - Sprintf(eos(discosyms), "%c", c); + (void) strkitten(discosyms, c); if (!traditional) { any.a_int = c; add_menu(tmpwin, &nul_glyphinfo, &any, @@ -947,11 +955,11 @@ doclassdisco(void) /* * show discoveries for object class c */ - tmpwin = create_nhwindow(NHW_MENU); + tmpwin = create_nhwindow(NHW_TEXT); ct = 0; switch (c) { case 'u': - putstr(tmpwin, iflags.menu_headings, + putstr(tmpwin, iflags.menu_headings.attr, upstart(strcpy(buf, unique_items))); for (i = 0; i < SIZE(uniq_objs); i++) if (objects[uniq_objs[i]].oc_name_known) { @@ -979,14 +987,18 @@ doclassdisco(void) break; default: oclass = def_char_to_objclass(c); + /* this should never happen but has been observed via the fuzzer */ + if (oclass == MAXOCLASSES) + impossible("doclassdisco: invalid object class '%s'", visctrl(c)); Sprintf(buf, "Discovered %s in %s", let_to_name(oclass, FALSE, FALSE), (flags.discosort == 'o') ? "order of discovery" : (flags.discosort == 's') ? "'sortloot' order" : "alphabetical order"); putstr(tmpwin, 0, buf); /* skip iflags.menu_headings */ sorted_ct = 0; - for (i = gb.bases[(int) oclass]; i <= gb.bases[oclass + 1] - 1; ++i) { - if ((dis = gd.disco[i]) != 0 && interesting_to_discover(dis)) { + for (i = svb.bases[(int) oclass]; i <= svb.bases[oclass + 1] - 1; + ++i) { + if ((dis = svd.disco[i]) != 0 && interesting_to_discover(dis)) { ++ct; Strcpy(buf, objects[dis].oc_pre_discovered ? "* " : " "); if (lootsort) @@ -1025,13 +1037,13 @@ doclassdisco(void) void rename_disco(void) { - register int i, dis; + int i, dis; int ct = 0, mn = 0, sl; char *s, oclass, prev_class; winid tmpwin; anything any; menu_item *selected = 0; - int clr = 0; + int clr = NO_COLOR; any = cg.zeroany; tmpwin = create_nhwindow(NHW_MENU); @@ -1048,9 +1060,9 @@ rename_disco(void) for (s = flags.inv_order; *s; s++) { oclass = *s; prev_class = oclass + 1; /* forced different from oclass */ - for (i = gb.bases[(int) oclass]; + for (i = svb.bases[(int) oclass]; i < NUM_OBJECTS && objects[i].oc_class == oclass; i++) { - dis = gd.disco[i]; + dis = svd.disco[i]; if (!dis || !interesting_to_discover(dis)) continue; ct++; @@ -1060,10 +1072,8 @@ rename_disco(void) if (oclass != prev_class) { any.a_int = 0; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - iflags.menu_headings, clr, - let_to_name(oclass, FALSE, FALSE), - MENU_ITEMFLAGS_NONE); + add_menu_heading(tmpwin, + let_to_name(oclass, FALSE, FALSE)); prev_class = oclass; } any.a_int = dis; @@ -1101,4 +1111,16 @@ rename_disco(void) return; } +void +get_sortdisco(char *opts, boolean cnf) +{ + const char *p = strchr(disco_order_let, flags.discosort); + + if (!p) + flags.discosort = 'o', p = disco_order_let; + if (cnf) + Sprintf(opts, "%c", flags.discosort); + else + Strcpy(opts, disco_orders_descr[p - disco_order_let]); +} /*o_init.c*/ diff --git a/src/objects.c b/src/objects.c index a7ef8d52c8..45ceaeeaba 100644 --- a/src/objects.c +++ b/src/objects.c @@ -10,13 +10,13 @@ #include "color.h" #include "objclass.h" -NEARDATA struct objdescr obj_descr_init[NUM_OBJECTS + 1] = { +static struct objdescr obj_descr_init[NUM_OBJECTS + 1] = { #define OBJECTS_DESCR_INIT #include "objects.h" #undef OBJECTS_DESCR_INIT }; -NEARDATA struct objclass obj_init[NUM_OBJECTS + 1] = { +static struct objclass obj_init[NUM_OBJECTS + 1] = { #define OBJECTS_INIT #include "objects.h" #undef OBJECTS_INIT diff --git a/src/objnam.c b/src/objnam.c index 3b2e2a73bd..222e98d506 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 objnam.c $NHDT-Date: 1672829441 2023/01/04 10:50:41 $ $NHDT-Branch: naming-overflow-fix $:$NHDT-Revision: 1.386 $ */ +/* NetHack 3.7 objnam.c $NHDT-Date: 1732979463 2024/11/30 07:11:03 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.439 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -34,29 +34,31 @@ struct _readobjnam_data { char fruitbuf[BUFSZ]; }; -static char *strprepend(char *, const char *); -static char *nextobuf(void); -static void releaseobuf(char *); -static void xcalled(char *, int, const char *, const char *); -static char *xname_flags(struct obj *, unsigned); -static char *minimal_xname(struct obj *); -static void add_erosion_words(struct obj *, char *); -static char *doname_base(struct obj *obj, unsigned); -static boolean singplur_lookup(char *, char *, boolean, +staticfn char *strprepend(char *, const char *); +staticfn char *nextobuf(void); +staticfn void releaseobuf(char *); +staticfn void xcalled(char *, int, const char *, const char *); +staticfn char *xname_flags(struct obj *, unsigned); +staticfn char *minimal_xname(struct obj *); +staticfn void add_erosion_words(struct obj *, char *); +staticfn char *doname_base(struct obj *obj, unsigned); +staticfn boolean singplur_lookup(char *, char *, boolean, const char *const *); -static char *singplur_compound(char *); -static boolean ch_ksound(const char *basestr); -static boolean badman(const char *, boolean); -static boolean wishymatch(const char *, const char *, boolean); -static short rnd_otyp_by_wpnskill(schar); -static short rnd_otyp_by_namedesc(const char *, char, int); -static struct obj *wizterrainwish(struct _readobjnam_data *); -static void readobjnam_init(char *, struct _readobjnam_data *); -static int readobjnam_preparse(struct _readobjnam_data *); -static void readobjnam_parse_charges(struct _readobjnam_data *); -static int readobjnam_postparse1(struct _readobjnam_data *); -static int readobjnam_postparse2(struct _readobjnam_data *); -static int readobjnam_postparse3(struct _readobjnam_data *); +staticfn char *singplur_compound(char *); +staticfn boolean ch_ksound(const char *basestr); +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 *); +staticfn int readobjnam_preparse(struct _readobjnam_data *); +staticfn void readobjnam_parse_charges(struct _readobjnam_data *); +staticfn int readobjnam_postparse1(struct _readobjnam_data *); +staticfn int readobjnam_postparse2(struct _readobjnam_data *); +staticfn int readobjnam_postparse3(struct _readobjnam_data *); struct Jitem { int item; @@ -67,6 +69,32 @@ struct Jitem { #define BSTRNCMPI(base, ptr, str, num) \ ((ptr) < base || strncmpi((ptr), str, num)) #define Strcasecpy(dst, src) (void) strcasecpy(dst, src) +#define Strncat(dst, src, cnt) (void) strncat(dst, src, cnt) + +/* Concat(): append text to base, adjusted by delta, with bounds checking + via a pair of behind-the-scenes variables; delta is either 0 for normal + concatenation or 1 to replace the final character with something */ +#define Concat(base, delta, text) \ + do { \ + Strncat(base ## _eos - delta, text, base ## spaceleft + delta); \ + ConcUpdate(base); \ + } while (0) +#define ConcatF1(base, delta, fmt, arg1) \ + do { \ + Snprintf(base ## _eos - delta, base ## spaceleft + delta, \ + fmt, arg1); \ + ConcUpdate(base); \ + } while (0) +#define ConcatF2(base, delta, fmt, arg1, arg2) \ + do { \ + Snprintf(base ## _eos - delta, base ## spaceleft + delta, \ + fmt, arg1, arg2); \ + ConcUpdate(base); \ + } while (0) +#define ConcUpdate(base) \ + base ## _eos = eos(base), \ + /* convert signed ptrdiff_t to unsigned size_t */ \ + base ## spaceleft = (size_t) (base ## _end - base ## _eos) /* true for gems/rocks that should have " stone" appended to their names */ #define GemStone(typ) \ @@ -93,10 +121,10 @@ static const struct Jitem Japanese_items[] = { { 0, "" } }; -static char * -strprepend(char *s,const char * pref) +staticfn char * +strprepend(char *s, const char *pref) { - register int i = (int) strlen(pref); + int i = (int) strlen(pref); if (i > PREFIX) { impossible("PREFIX too short (for %d).", i); @@ -111,7 +139,7 @@ strprepend(char *s,const char * pref) static char NEARDATA obufs[NUMOBUF][BUFSZ]; static int obufidx = 0; -static char * +staticfn char * nextobuf(void) { obufidx = (obufidx + 1) % NUMOBUF; @@ -119,7 +147,7 @@ nextobuf(void) } /* put the most recently allocated buffer back if possible */ -static void +staticfn void releaseobuf(char *bufp) { /* caller may not know whether bufp is the most recently allocated @@ -296,11 +324,11 @@ safe_typename(int otyp) } boolean -obj_is_pname(struct obj* obj) +obj_is_pname(struct obj *obj) { if (!obj->oartifact || !has_oname(obj)) return FALSE; - if (!gp.program_state.gameover && !iflags.override_ID) { + if (!program_state.gameover && !iflags.override_ID) { if (not_fully_identified(obj)) return FALSE; } @@ -315,6 +343,7 @@ distant_name( char *(*func)(OBJ_P)) /* formatting routine (usually xname or doname) */ { char *str; + unsigned save_oid; coordxy ox = 0, oy = 0; /* * (r * r): square of the x or y distance; @@ -335,6 +364,19 @@ distant_name( int r = (u.xray_range > 2) ? u.xray_range : 2, neardist = (r * r) * 2 - r; /* same as r*r + r*(r-1) */ + /* setting o_id to 0 prevents xname() from adding T-shirt or apron + slogan, Hawaiian shirt motif, or candy wrapper label when called + with 'program_state.gameover' set; we want this suppression for + html-dump (not implemented in nethack) to prevent object-on-map + tooltips from including that extra text; also guards against a + potential change to minimal_xname() [indirectly used by attribute + disclosure] that propagates o_id rather than leave it 0, and + against a potential extra chance to browse the map with getpos() + during final disclosure (not currently implemented, nor planned) */ + save_oid = obj->o_id; + if (program_state.gameover) + obj->o_id = 0; + /* this maybe-nearby part used to be replicated in multiple callers */ if (get_obj_location(obj, &ox, &oy, 0) && cansee(ox, oy) && (obj->oartifact || distu(ox, oy) <= neardist)) { @@ -354,6 +396,9 @@ distant_name( str = (*func)(obj); --gd.distantname; } + + obj->o_id = save_oid; /* reset to normal */ + return str; } @@ -364,12 +409,12 @@ fruitname( boolean juice) /* whether or not to append " juice" to the name */ { char *buf = nextobuf(); - const char *fruit_nam = strstri(gp.pl_fruit, " of "); + const char *fruit_nam = strstri(svp.pl_fruit, " of "); if (fruit_nam) fruit_nam += 4; /* skip past " of " */ else - fruit_nam = gp.pl_fruit; /* use it as is */ + fruit_nam = svp.pl_fruit; /* use it as is */ Sprintf(buf, "%s%s", makesingular(fruit_nam), juice ? " juice" : ""); return buf; @@ -398,7 +443,7 @@ fruit_from_name( char *altfname; unsigned k; /* - * note: named fruits are case-senstive... + * note: named fruits are case-sensitive... */ if (highest_fid) @@ -503,7 +548,7 @@ reorder_fruit(boolean forward) } /* add " called " to end of buf, truncating if necessary */ -static void +staticfn void xcalled( char *buf, /* eos(obuf) or eos(&obuf[PREFIX]) */ int siz, /* BUFSZ or BUFSZ-PREFIX */ @@ -521,18 +566,19 @@ xcalled( } char * -xname(struct obj* obj) +xname(struct obj *obj) { return xname_flags(obj, CXN_NORMAL); } -static char * +staticfn char * xname_flags( - register struct obj *obj, + struct obj *obj, unsigned cxn_flags) /* bitmask of CXN_xxx values */ { - register char *buf; - char *obufp; + char *buf; + char *obufp, *buf_end, *buf_eos; + size_t bufspaceleft; int typ = obj->otyp; struct objclass *ocl = &objects[typ]; int nn = ocl->oc_name_known, omndx = obj->corpsenm; @@ -542,7 +588,14 @@ xname_flags( boolean pluralize = (obj->quan != 1L) && !(cxn_flags & CXN_SINGULAR); boolean known, dknown, bknown; - buf = nextobuf() + PREFIX; /* leave room for "17 -3 " */ + gx.xnamep = nextobuf(); + /* set up primary work buffer; the first 'PREFIX' bytes are set + aside for use by doname() */ + buf = gx.xnamep + PREFIX; /* leave room for "17 -3 " */ + buf_end = gx.xnamep + BUFSZ - 1; /* last byte within the obuf[] */ + buf[0] = '\0'; + ConcUpdate(buf); /* set buf_eos and bufspaceleft */ + if (Role_if(PM_SAMURAI)) { actualn = Japanese_item_name(typ, actualn); if (typ == HARP || typ == MAGIC_HARP) @@ -557,7 +610,6 @@ xname_flags( if (!dn) dn = actualn; - buf[0] = '\0'; /* * clean up known when it's tied to oc_name_known, eg after AD_DRIN * This is only required for unique objects since the article @@ -609,6 +661,12 @@ xname_flags( if (obj_is_pname(obj)) goto nameit; + + /* Some classes use strcpy(buf, something)+strcat(buf, otherthing). + In those cases, ConcUpdate() is needed in between if Concat() + will be used for the strcat() part. Other classes just use + strcpy(buf, something) and the ConcUpdate() can be deferred + until after the switch. */ switch (obj->oclass) { case AMULET_CLASS: if (obj->material != objects[typ].oc_material && dknown) { @@ -631,9 +689,12 @@ xname_flags( case WEAPON_CLASS: if (is_poisonable(obj) && obj->opoisoned) Strcpy(buf, "poisoned "); + FALLTHROUGH; /*FALLTHRU*/ case VENOM_CLASS: case TOOL_CLASS: + /* note: lenses or towel prefix would overwrite poisoned weapon + prefix if both were simultaneously possible, but they aren't */ if (typ == LENSES) Strcpy(buf, "pair of "); else if (is_wet_towel(obj)) @@ -653,15 +714,16 @@ xname_flags( xcalled(buf, BUFSZ - PREFIX, dn, un); else Strcat(buf, dn); + ConcUpdate(buf); if (typ == FIGURINE && omndx != NON_PM) { char anbuf[10]; /* [4] would be enough: 'a','n',' ','\0' */ const char *pm_name = obj_pmname(obj); - Sprintf(eos(buf), " of %s%s", just_an(anbuf, pm_name), pm_name); + ConcatF2(buf, 0, " of %s%s", just_an(anbuf, pm_name), pm_name); } else if (is_wet_towel(obj)) { if (wizard) - Sprintf(eos(buf), " (%d)", obj->spe); + ConcatF1(buf, 0, " (%d)", obj->spe); } break; case ARMOR_CLASS: @@ -669,16 +731,16 @@ xname_flags( if (typ >= FIRST_DRAGON_SCALES && typ <= LAST_DRAGON_SCALES) { Sprintf(buf, "set of %s", actualn); break; - } - if (is_boots(obj) || is_gloves(obj)) + } else if (is_boots(obj) || is_gloves(obj)) { Strcpy(buf, "pair of "); + } if ((obj->material != objects[typ].oc_material || force_material_name(typ)) && dknown) { - Strcat(buf, materialnm[obj->material]); - Strcat(buf, " "); + Concat(buf, 0, materialnm[obj->material]); + Concat(buf, 0, " "); } - if (!dknown) { + if (is_shield(obj) && !dknown) { if (obj->otyp >= ELVEN_SHIELD && obj->otyp <= ORCISH_SHIELD) { Strcat(buf, "shield"); break; @@ -687,13 +749,14 @@ xname_flags( break; } } + ConcUpdate(buf); if (nn) - Strcat(buf, actualn); + Concat(buf, 0, actualn); else if (un) xcalled(buf, BUFSZ - PREFIX, armor_simple_name(obj), un); else - Strcat(buf, dn); + Concat(buf, 0, dn); break; case FOOD_CLASS: if (obj->material != objects[typ].oc_material && dknown) { @@ -709,6 +772,9 @@ xname_flags( impossible("Bad fruit #%d?", obj->spe); Strcpy(buf, "fruit"); } else { + /* fruit name is limited in length to PL_FSIZ; converting + to/from singular/plural might increase the length a + little but not enough to pose a risk of overflowing buf */ Strcpy(buf, f->fname); if (pluralize) { /* ick: already pluralized fruit names are allowed--we @@ -733,18 +799,18 @@ xname_flags( appropriate and omitted by xname(); shrink_glob() wants it but uses Yname2() -> yname() -> xname() rather than doname() so we've added an external flag to request it */ - Strcat(buf, "partly eaten "); + Concat(buf, 0, "partly eaten "); } if (obj->globby) { /* 3.7 added "medium" to replace no-prefix */ - Sprintf(eos(buf), "%s %s", (obj->owt <= 100) ? "small" - : (obj->owt <= 300) ? "medium" - : (obj->owt <= 500) ? "large" - : "very large", - actualn); + ConcatF2(buf, 0, "%s %s", (obj->owt <= 100) ? "small" + : (obj->owt <= 300) ? "medium" + : (obj->owt <= 500) ? "large" + : "very large", + actualn); break; } - Strcat(buf, actualn); + Concat(buf, 0, actualn); if (typ == TIN && known) tin_details(obj, omndx, buf); break; @@ -757,25 +823,25 @@ xname_flags( char anbuf[10]; const char *statue_pmname = obj_pmname(obj); - Sprintf(buf, "%s%s %s of %s%s", - (Role_if(PM_ARCHEOLOGIST) - && (obj->spe & CORPSTAT_HISTORIC)) ? "historic " : "", - materialnm[obj->material], - actualn, - type_is_pname(&mons[omndx]) ? "" - : the_unique_pm(&mons[omndx]) ? "the " - : just_an(anbuf, statue_pmname), - statue_pmname); - } else { + Snprintf(buf, bufspaceleft, "%s%s %s of %s%s", + (Role_if(PM_ARCHEOLOGIST) + && (obj->spe & CORPSTAT_HISTORIC) != 0) ? "historic " + : "", + materialnm[obj->material], + actualn, + type_is_pname(&mons[omndx]) ? "" + : the_unique_pm(&mons[omndx]) ? "the " + : just_an(anbuf, statue_pmname), + statue_pmname); + } else if (typ == BOULDER && obj->next_boulder == 1) { /* sometimes caller wants "next boulder" rather than just "boulder" (when pushing against a pile of more than one); originally we just tested for non-0 but checking for 1 is more robust because the default value for that overloaded field (obj->corpsenm) is NON_PM (-1) rather than 0 */ - if (typ == BOULDER && obj->next_boulder == 1) - Strcat(strcpy(buf, "next "), actualn); - else - Strcpy(buf, actualn); + Strcat(strcpy(buf, "next "), actualn); /* "next boulder" */ + } else { + Strcpy(buf, actualn); /* "boulder" or "statue" */ } break; case BALL_CLASS: @@ -880,20 +946,48 @@ xname_flags( Strcat(buf, " stone"); } break; - } + } /* gem */ default: Sprintf(buf, "glorkum %d %d %d", obj->oclass, typ, obj->spe); impossible("xname_flags: %s", buf); break; - } + } /* switch */ + + /* check whether we've already gone out of bounds of the obuf[], prior + to pluralization and end-of-game shirt and apron text */ + buf_eos = eos(buf); + if (buf_eos > buf_end) { + /* PREFIX is bigger than 6 so there will always be room within the + obuf[] in front of buf to insert "buf[]="; strncpy(,,N) doesn't + add '\0' terminator unless fewer than N chars are copied, which + is what we want, but gcc complains about that so use memcpy() */ + paniclog("xname", (char *) memcpy(buf - 6, "buf[]=", 6)); + panic("xname: buffer overflow before appending name."); + /*NOTREACHED*/ + } + bufspaceleft = (size_t) (buf_end - buf_eos); + + /* if the name should be plural, do that now, after overflow check; + it could make buf[] become shorter */ if (pluralize) { - /* (see fruit name handling in case FOOD_CLASS above) */ - Strcpy(buf, obufp = makeplural(buf)); + obufp = makeplural(buf); + buf[0] = '\0'; /* replace the whole string */ + ConcUpdate(buf); /* reset buf_eos and bufspaceleft */ + Concat(buf, 0, obufp); releaseobuf(obufp); } - /* maybe give some extra information which isn't shown during play */ - if (gp.program_state.gameover) { + /* give some extra information when game is over; for end-of-game + attribute disclosure in wizard mode, ysimple_name() calls + minimal_xname() which passes us a dummy object with o_id==0; + tshirt_text(), apron_text(), and so forth base their result on + o_id and would give inconsistent information compared to what + just got shown for inventory disclosure; fortunately, we want to + avoid the 'with text' part of + "You were acid resistant because of your alchemy smock \ + with text \"Kiss the cook\"." + when disclosing attributes anyway */ + if (program_state.gameover && obj->o_id && bufspaceleft > 0) { const char *lbl; char tmpbuf[BUFSZ]; @@ -902,18 +996,18 @@ xname_flags( switch (obj->otyp) { case T_SHIRT: case ALCHEMY_SMOCK: - Sprintf(eos(buf), " with text \"%s\"", - (obj->otyp == T_SHIRT) ? tshirt_text(obj, tmpbuf) - : apron_text(obj, tmpbuf)); + ConcatF1(buf, 0, " with text \"%s\"", + (obj->otyp == T_SHIRT) ? tshirt_text(obj, tmpbuf) + : apron_text(obj, tmpbuf)); break; case CANDY_BAR: lbl = candy_wrapper_text(obj); if (*lbl) - Sprintf(eos(buf), " labeled \"%s\"", lbl); + ConcatF1(buf, 0, " labeled \"%s\"", lbl); break; case HAWAIIAN_SHIRT: - Sprintf(eos(buf), " with %s motif", - an(hawaiian_motif(obj, tmpbuf))); + ConcatF1(buf, 0, " with %s motif", + an(hawaiian_motif(obj, tmpbuf))); break; default: break; @@ -921,18 +1015,35 @@ xname_flags( } if (has_oname(obj) && dknown) { - Strcat(buf, " named "); + Concat(buf, 0, " named "); + + /* jump directly here if obj passes the has-personal-name test */ nameit: - obufp = eos(buf); - (void) strncat(buf, ONAME(obj), - BUFSZ - 1 - PREFIX - (unsigned) strlen(buf)); + /*assert(has_oname(obj));*/ + obufp = eos(buf); /* remember where the name will start */ + Concat(buf, 0, ONAME(obj)); /* downcase "The" in " named The ..." */ if (obj->oartifact && !strncmp(obufp, "The ", 4)) - *obufp = lowc(*obufp); /* = 't'; */ + *obufp = lowc(*obufp); /* change 'T' in "The " to 't' */ } if (!strncmpi(buf, "the ", 4)) buf += 4; + + buf_eos = eos(buf); /* pointer to '\0' terminator somewhere in obuf[] */ + if (buf_eos >= buf_end) { /* ('>' shouldn't be possible) */ + static int xname_full = 0; + + /* we want a record of something needing more buffer space than + anticipated; since we aren't panicking here, this could happen + repeatedly and we don't want to spam the paniclog file */ + if (!xname_full++) { + paniclog("xname", (char *) memcpy(buf - 6, "buf[]=", 6)); + /* 'PREFIX' ought to be 'PREFIX+4' if we stripped leading "the" */ + paniclog("xname", "used up entire obuf[PREFIX..BUFSX-1]"); + } + } + return buf; } @@ -942,7 +1053,7 @@ xname_flags( brown potion -- if oc_name_known not set potion of object detection -- if discovered */ -static char * +staticfn char * minimal_xname(struct obj *obj) { char *bufp; @@ -984,9 +1095,9 @@ minimal_xname(struct obj *obj) bareobj.material = force_material_name(obj->otyp) ? obj->material : objects[obj->otyp].oc_material; - /* bufp will be an obuf[] and a pointer into middle of that is viable */ bufp = distant_name(&bareobj, xname); - /* undo forced setting of bareobj.blessed for cleric (preist[ess]) */ + /* undo forced setting of bareobj.blessed for cleric (priest[ess]); + bufp is an obuf[] so a pointer into the middle of that is viable */ if (!strncmp(bufp, "uncursed ", 9)) bufp += 9; @@ -997,7 +1108,7 @@ minimal_xname(struct obj *obj) /* xname() output augmented for multishot missile feedback */ char * -mshot_xname(struct obj* obj) +mshot_xname(struct obj *obj) { char tmpbuf[BUFSZ]; char *onm = xname(obj); @@ -1013,7 +1124,7 @@ mshot_xname(struct obj* obj) /* used for naming "the unique_item" instead of "a unique_item" */ boolean -the_unique_obj(struct obj* obj) +the_unique_obj(struct obj *obj) { boolean known = (obj->known || iflags.override_ID); @@ -1028,7 +1139,7 @@ the_unique_obj(struct obj* obj) /* should monster type be prefixed with "the"? (mostly used for corpses) */ boolean -the_unique_pm(struct permonst* ptr) +the_unique_pm(struct permonst *ptr) { boolean uniq; @@ -1049,8 +1160,8 @@ the_unique_pm(struct permonst* ptr) return uniq; } -static void -add_erosion_words(struct obj* obj, char* prefix) +staticfn void +add_erosion_words(struct obj *obj, char *prefix) { boolean iscrys = (obj->otyp == CRYSKNIFE), skip_eroded = (iscrys || !is_damageable(obj)), @@ -1074,7 +1185,9 @@ add_erosion_words(struct obj* obj, char* prefix) Strcat(prefix, "thoroughly "); break; } - Strcat(prefix, is_rustprone(obj) ? "rusty " : "burnt "); + Strcat(prefix, is_rustprone(obj) ? "rusty " + : is_crackable(obj) ? "cracked " + : "burnt "); } if (obj->oeroded2 && !skip_eroded) { switch (obj->oeroded2) { @@ -1087,13 +1200,16 @@ add_erosion_words(struct obj* obj, char* prefix) } Strcat(prefix, is_corrodeable(obj) ? "corroded " : "rotted "); } + /* note: it is possible for an item to be both eroded and erodeproof + (cursed scroll of destroy armor read while confused erodeproofs an + item of armor without repairing existing erosion) */ if (rknown && obj->oerodeproof) { if (destroyable_oclass(obj->oclass)) Strcat(prefix, "indestructible "); else if (iscrys) Strcat(prefix, "fixed "); - else if (obj->material == GLASS) - Strcat(prefix, "shatterproof "); + else if (is_crackable(obj)) + Strcat(prefix, "tempered "); else if (is_rustprone(obj)) Strcat(prefix, "rustproof "); else if (is_corrodeable(obj)) @@ -1105,7 +1221,7 @@ add_erosion_words(struct obj* obj, char* prefix) /* used to prevent rust on items where rust makes no difference */ boolean -erosion_matters(struct obj* obj) +erosion_matters(struct obj *obj) { switch (obj->oclass) { case TOOL_CLASS: @@ -1129,16 +1245,18 @@ erosion_matters(struct obj* obj) #define DONAME_WITH_PRICE 1 #define DONAME_VAGUE_QUAN 2 +#define DONAME_FOR_MENU 4 /* [not used anywhere yet] */ /* core of doname() */ -static char * +staticfn char * doname_base( - struct obj* obj, /* object to format */ + struct obj *obj, /* object to format */ unsigned doname_flags) /* special case requests */ { boolean ispoisoned = FALSE, with_price = (doname_flags & DONAME_WITH_PRICE) != 0, - vague_quan = (doname_flags & DONAME_VAGUE_QUAN) != 0; + vague_quan = (doname_flags & DONAME_VAGUE_QUAN) != 0, + for_menu = (doname_flags & DONAME_FOR_MENU) != 0; boolean known, dknown, cknown, bknown, lknown, fake_arti, force_the; char prefix[PREFIX]; @@ -1147,7 +1265,20 @@ doname_base( * end (Strcat is used on the end) */ const char *aname = 0; int omndx = obj->corpsenm; - register char *bp = xname(obj); + char *bp; + char *bp_eos, *bp_end; + size_t bpspaceleft; + + /* 'bp' will be within an obuf[] rather than at the start of one, + usually (but not always) pointing at &obuf[PREFIX]; + gx.xnamep always points to the start of that buffer; + 'bp_eos' and 'bpspaceleft' are used and updated by Concat*() macros */ + bp = xname(obj); + bp_end = gx.xnamep + BUFSZ - 1; + bp_eos = eos(bp); + assert(bp_end >= bp_eos); /* ok provided xname() bounds checking works */ + /* size_t cast: convert signed ptrdiff_t to unsigned size_t */ + bpspaceleft = (size_t) (bp_end - bp_eos); if (iflags.override_ID) { known = dknown = cknown = bknown = lknown = TRUE; @@ -1166,7 +1297,7 @@ doname_base( */ /* must check opoisoned--someone can have a weirdly-named fruit */ if (!strncmp(bp, "poisoned ", 9) && obj->opoisoned) { - bp += 9; + bp += 9; /* doesn't affect bp_eos or bpspaceleft */ ispoisoned = TRUE; } @@ -1189,7 +1320,7 @@ doname_base( ; } else if (force_the || obj_is_pname(obj) || the_unique_obj(obj)) { if (!strncmpi(bp, "the ", 4)) - bp += 4; + bp += 4; /* doesn't affect bp_eos or bpspaceleft */ Strcpy(prefix, "the "); } else if (!fake_arti) { /* default prefix */ @@ -1246,6 +1377,9 @@ doname_base( Strcat(prefix, "uncursed "); } + /* "a large trapped box" would perhaps be more correct */ + 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 @@ -1261,85 +1395,114 @@ doname_base( if (obj->greased) Strcat(prefix, "greased "); - if (cknown && Has_contents(obj)) { + if (cknown && Has_contents(obj) && bpspaceleft > 0) { /* we count the number of separate stacks, which corresponds to the number of inventory slots needed to be able to take everything out if no merges occur */ long itemcount = count_contents(obj, FALSE, FALSE, TRUE, FALSE); - Sprintf(eos(bp), " containing %ld item%s", itemcount, - plur(itemcount)); + ConcatF2(bp, 0, " containing %ld item%s", itemcount, plur(itemcount)); } switch (is_weptool(obj) ? WEAPON_CLASS : obj->oclass) { case AMULET_CLASS: if (obj->owornmask & W_AMUL) - Strcat(bp, " (being worn)"); + Concat(bp, 0, " (being worn)"); break; case ARMOR_CLASS: if (obj->owornmask & W_ARMOR) { - Strcat(bp, (obj == uskin) ? " (embedded in your skin)" - /* in case of perm_invent update while Wear/Takeoff - is in progress; check doffing() before donning() - because donning() returns True for both cases */ - : doffing(obj) ? " (being doffed)" - : donning(obj) ? " (being donned)" - : " (being worn)"); - /* slippery fingers is an intrinsic condition of the hero - rather than extrinsic condition of objects, but gloves - are described as slippery when hero has slippery fingers */ - if (obj == uarmg && Glib) /* just appended "(something)", - * change to "(something; slippery)" */ - Strcpy(strrchr(bp, ')'), "; slippery)"); - else if (!Blind && obj->lamplit && artifact_light(obj)) - Sprintf(strrchr(bp, ')'), ", %s lit)", - arti_light_description(obj)); + Concat(bp, 0, + (obj == uskin) ? " (embedded in your skin)" + /* in case of perm_invent update while Wear/Takeoff + is in progress; check doffing() before donning() + because donning() returns True for both cases */ + : doffing(obj) ? " (being doffed)" + : donning(obj) ? " (being donned)" + : " (being worn)"); + /* we just added a parenthesized phrase, but the right paren + might be absent if the appended string got truncated */ + if (bp_eos[-1] == ')') { + /* slippery fingers is an intrinsic condition of the hero + rather than extrinsic condition of objects, but gloves + are described as slippery when hero has slippery fingers */ + if (obj == uarmg && Glib) /* just appended "(something)", + * replace paren, changing that + * to be "(something; slippery)" */ + Concat(bp, 1, "; slippery)"); + } + if (bp_eos[-1] == ')') { + /* there could be light-emitting artifact gloves someday, + so add 'lit' separately from 'slippery' rather than via + 'else if' after uarmg+Glib */ + if (!Blind && obj->lamplit && artifact_light(obj)) + ConcatF1(bp, 1, ", %s lit)", arti_light_description(obj)); + } } if (Is_dragon_scaled_armor(obj)) { - char scalebuf[30], *colorstr = dragon_scales_color(obj); - Sprintf(scalebuf, "%s-scaled ", colorstr); - Strcat(prefix, scalebuf); - releaseobuf(colorstr); /* don't consume an extra obuf */ + ConcatF1(bp, 0, "%s-scaled ", dragon_scales_color(obj)); } + FALLTHROUGH; /*FALLTHRU*/ case WEAPON_CLASS: if (ispoisoned) Strcat(prefix, "poisoned "); add_erosion_words(obj, prefix); if (known) { - Strcat(prefix, sitoa(obj->spe)); - Strcat(prefix, " "); + Sprintf(eos(prefix), "%+d ", obj->spe); /* sitoa(obj->spe)+" " */ } break; case TOOL_CLASS: if (obj->owornmask & (W_TOOL | W_SADDLE)) { /* blindfold */ - Strcat(bp, " (being worn)"); + Concat(bp, 0, " (being worn)"); break; } if (obj->otyp == LEASH && obj->leashmon != 0) { struct monst *mlsh = find_mid(obj->leashmon, FM_FMON); - if (!mlsh) { - impossible("leashed monster not on this level"); - obj->leashmon = 0; + if (mlsh && !DEADMONSTER(mlsh)) { + ConcatF1(bp, 0, " (attached to %s)", noit_mon_nam(mlsh)); } else { - Sprintf(eos(bp), " (attached to %s)", - noit_mon_nam(mlsh)); + if (mlsh) /*&& DEADMONSTER(mlsh)*/ + impossible("leashed %s #%u is dead", + mon_pmname(mlsh), (unsigned) obj->leashmon); + else + impossible("leashed monster #%u not found", + (unsigned) obj->leashmon); + obj->leashmon = 0; } break; } if (obj->otyp == CANDELABRUM_OF_INVOCATION) { - Sprintf(eos(bp), " (%d of 7 candle%s%s)", - obj->spe, plur(obj->spe), + char suffix[20]; /* longest value is "s attached" */ + + /* separately formatted suffix avoids need for ConcatF3() */ + Sprintf(suffix, "%s%s", plur(obj->spe), !obj->lamplit ? " attached" : ", lit"); + ConcatF2(bp, 0, " (%d of 7 candle%s)", obj->spe, suffix); break; } else if (obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP || obj->otyp == LANTERN || Is_candle(obj)) { - if (Is_candle(obj) - && obj->age < 20L * (long) objects[obj->otyp].oc_cost) - Strcat(prefix, "partly used "); + if (Is_candle(obj)) { + anything timer; + long full_burn_time = 20L * (long) objects[obj->otyp].oc_cost, + turns_left = obj->age; + + if (obj->lamplit) { + timer = cg.zeroany; + timer.a_obj = obj; + /* without this, wishing for "lit candle" yields + "partly used candle (lit)" because the time it can + burn gets adjusted when it becomes lit; matters for + the message as it gets added to invent and also if it + gets snuffed out immediately (where it will end up as + not partly used after all) */ + turns_left += peek_timer(BURN_OBJECT, &timer) - svm.moves; + } + if (turns_left < full_burn_time) + Strcat(prefix, "partly used "); + } if (obj->lamplit) - Strcat(bp, " (lit)"); + Concat(bp, 0, " (lit)"); break; } if (objects[obj->otyp].oc_charged) @@ -1349,29 +1512,26 @@ doname_base( add_erosion_words(obj, prefix); charges: if (known) - Sprintf(eos(bp), " (%d:%d)", (int) obj->recharged, obj->spe); + ConcatF2(bp, 0, " (%d:%d)", (int) obj->recharged, obj->spe); break; case POTION_CLASS: add_erosion_words(obj, prefix); if (obj->otyp == POT_OIL && obj->lamplit) - Strcat(bp, " (lit)"); + Concat(bp, 0, " (lit)"); if (obj->otyp == POT_FRUIT_JUICE && obj->corpsenm) - Strcat(bp, " (fermenting)"); + Concat(bp, 0, " (fermenting)"); break; case RING_CLASS: add_erosion_words(obj, prefix); - ring: + ring: /* normal rings reach here 'naturally'; meat ring jumps here */ if (obj->owornmask & W_RINGR) - Strcat(bp, " (on right "); + Concat(bp, 0, " (on right "); if (obj->owornmask & W_RINGL) - Strcat(bp, " (on left "); - if (obj->owornmask & W_RING) { - Strcat(bp, body_part(HAND)); - Strcat(bp, ")"); - } + Concat(bp, 0, " (on left "); + if (obj->owornmask & W_RING) /* either left or right */ + ConcatF1(bp, 0,"%s)", body_part(HAND)); if (known && objects[obj->otyp].oc_charged) { - Strcat(prefix, sitoa(obj->spe)); - Strcat(prefix, " "); + Sprintf(eos(prefix), "%+d ", obj->spe); /* sitoa(obj->spe)+" " */ } break; case FOOD_CLASS: @@ -1383,41 +1543,47 @@ doname_base( "corpse" is already in the buffer returned by xname() */ unsigned cxarg = (((obj->quan != 1L) ? 0 : CXN_ARTICLE) | CXN_NOCORPSE); - char *cxstr = corpse_xname(obj, prefix, cxarg); + char *cxstr, *save_xnamep; + /* corpse_xname() sets xnamep; callers other than doname_base() + itself shouldn't care about xnamep (pointer to start of + current obuf[]) but keep it accurate anyway */ + save_xnamep = gx.xnamep; + cxstr = corpse_xname(obj, prefix, cxarg); Sprintf(prefix, "%s ", cxstr); /* avoid having doname(corpse) consume an extra obuf */ releaseobuf(cxstr); + gx.xnamep = save_xnamep; } else if (obj->otyp == EGG) { #if 0 /* corpses don't tell if they're stale either */ if (known && stale_egg(obj)) Strcat(prefix, "stale "); #endif - if (omndx >= LOW_PM - && (known || (gm.mvitals[omndx].mvflags & MV_KNOWS_EGG))) { + if (ismnum(omndx) + && (known || (svm.mvitals[omndx].mvflags & MV_KNOWS_EGG))) { Strcat(prefix, mons[omndx].pmnames[NEUTRAL]); Strcat(prefix, " "); if (obj->spe == 1) - Strcat(bp, " (laid by you)"); + Concat(bp, 0, " (laid by you)"); } - } - if (obj->otyp == MEAT_RING) + } else if (obj->otyp == MEAT_RING) { goto ring; + } break; case GEM_CLASS: if (obj->otyp == THIEFSTONE && known) { if (!thiefstone_ledger_valid(obj)) { /* doesn't point to a level: assume cancelled */ - Strcat(eos(bp), " (inactive)"); + Concat(bp, 0, " (inactive)"); } else { d_level dlev; dlev.dnum = ledger_to_dnum(obj->keyed_ledger); dlev.dlevel = ledger_to_dlev(obj->keyed_ledger); - Sprintf(eos(bp), " (keyed to %s:%d)", - gd.dungeons[dlev.dnum].dname, depth(&dlev)); + ConcatF2(bp, 0, " (keyed to %s:%d)", + svd.dungeons[dlev.dnum].dname, depth(&dlev)); if (wizard) { - Sprintf(eos(bp), " at (%d,%d)", - keyed_x(obj), keyed_y(obj)); + ConcatF2(bp, 0, " at (%d,%d)", + keyed_x(obj), keyed_y(obj)); } } } @@ -1426,8 +1592,8 @@ doname_base( case CHAIN_CLASS: add_erosion_words(obj, prefix); if (obj->owornmask & (W_BALL | W_CHAIN)) - Sprintf(eos(bp), " (%s to you)", - (obj->owornmask & W_BALL) ? "chained" : "attached"); + ConcatF1(bp, 0, " (%s to you)", + (obj->owornmask & W_BALL) ? "chained" : "attached"); break; case SCROLL_CLASS: case SPBOOK_CLASS: @@ -1440,9 +1606,10 @@ doname_base( mgend = ((cgend == CORPSTAT_MALE) ? MALE : (cgend == CORPSTAT_FEMALE) ? FEMALE : NEUTRAL); - Sprintf(eos(bp), " (%s)", - cgend != CORPSTAT_RANDOM ? genders[mgend].adj - : "unspecified gender"); + + ConcatF1(bp, 0, " (%s)", + (cgend != CORPSTAT_RANDOM) ? genders[mgend].adj + : "unspecified gender"); } if ((obj->owornmask & W_WEP) && !gm.mrg_to_wielded) { @@ -1460,94 +1627,104 @@ doname_base( ? (is_ammo(obj) || is_missile(obj)) : !is_weptool(obj))) && !twoweap_primary) { - Strcat(bp, " (wielded)"); + Concat(bp, 0, " (wielded)"); } else { const char *hand_s = body_part(HAND); - - if (bimanual(obj)) - hand_s = makeplural(hand_s); - Sprintf(eos(bp), " (%s%s in %s%s)", - tethered ? "tethered " : "", /* aklys */ - /* avoid "tethered wielded in right hand" for twoweapon */ - (twoweap_primary && !tethered) ? "wielded" : "weapon", - twoweap_primary ? "right " : "", hand_s); - if (!Blind) { + char *obufp, handsbuf[40]; + + if (bimanual(obj)) { + hand_s = strcpy(handsbuf, obufp = makeplural(hand_s)); + releaseobuf(obufp); + } else { /* "right hand" or "left hand" */ + Sprintf(handsbuf, "%s %s", + URIGHTY ? "right" : "left", hand_s); + hand_s = handsbuf; + } + ConcatF2(bp, 0, " (%s %s)", + tethered ? "tethered to" + : twoweap_primary ? "wielded in" + : "weapon in", + hand_s); + if (!Blind && bpspaceleft && bp_eos[-1] == ')') { if (gw.warn_obj_cnt && obj == uwep && (EWarn_of_mon & W_WEP) != 0L) { /* we know bp[] ends with ')'; overwrite that */ if (strcmp(glow_color(obj->oartifact), "no color")) - Sprintf(eos(bp) - 1, ", %s %s)", - glow_verb(gw.warn_obj_cnt, TRUE), - glow_color(obj->oartifact)); + ConcatF2(bp, 1, ", %s %s)", + glow_verb(gw.warn_obj_cnt, TRUE), + glow_color(obj->oartifact)); } else if (obj->lamplit && artifact_light(obj)) /* as above, overwrite known closing paren */ - Sprintf(eos(bp) - 1, ", %s lit)", - arti_light_description(obj)); + ConcatF1(bp, 1, ", %s lit)", + arti_light_description(obj)); } } } if (obj->owornmask & W_SWAPWEP) { if (u.twoweap) - Sprintf(eos(bp), " (wielded in left %s)", body_part(HAND)); + ConcatF2(bp, 0, " (wielded in %s %s)", + URIGHTY ? "left" : "right", body_part(HAND)); else /* TODO: rephrase this when obj isn't a weapon or weptool */ - Sprintf(eos(bp), " (alternate weapon%s; not wielded)", - plur(obj->quan)); + ConcatF1(bp, 0, " (alternate weapon%s; not wielded)", + plur(obj->quan)); } if (obj->owornmask & W_QUIVER) { + int Qtyp; + switch (obj->oclass) { case WEAPON_CLASS: - if (is_ammo(obj)) { - if (objects[obj->otyp].oc_skill == -P_BOW) { - /* Ammo for a bow */ - Strcat(bp, " (in quiver)"); - break; - } else { - /* Ammo not for a bow */ - Strcat(bp, " (in quiver pouch)"); - break; - } - } else { - /* Weapons not considered ammo */ - Strcat(bp, " (at the ready)"); - break; - } - /* Small things and ammo not for a bow */ + Qtyp = !is_ammo(obj) ? 3 /* not ammo: "at the ready" */ + : (objects[obj->otyp].oc_skill != -P_BOW) ? 2 /* non-bow */ + : 1; /* ammo for a bow: "in quiver" */ + break; case RING_CLASS: case AMULET_CLASS: case WAND_CLASS: case COIN_CLASS: case GEM_CLASS: - Strcat(bp, " (in quiver pouch)"); + Qtyp = 2; /* small, non-bow: "in quiver pouch" */ break; default: /* odd things */ - Strcat(bp, " (at the ready)"); + Qtyp = 3; /* "at the ready" */ + break; } + ConcatF1(bp, 0, " (%s)", + (Qtyp == 1) ? "in quiver" + : (Qtyp == 2) ? "in quiver pouch" + : "at the ready"); } + /* treat 'restoring' like suppress_price because shopkeeper and bill might not be available yet while restore is in progress (objects won't normally be formatted during that time, but if 'perm_invent' is enabled then they might be [not any more...]) */ - if (iflags.suppress_price || gp.program_state.restoring) { + if (iflags.suppress_price || program_state.restoring) { ; /* don't attempt to obtain any shop pricing, even if 'with_price' */ } else if (is_unpaid(obj)) { /* in inventory or in container in invent */ - long quotedprice = unpaid_cost(obj, TRUE); + char pricebuf[40]; + long quotedprice = unpaid_cost(obj, COST_CONTENTS); - Sprintf(eos(bp), " (%s, %ld %s)", - obj->unpaid ? "unpaid" : "contents", - quotedprice, currency(quotedprice)); + /* separately formatted suffix avoids need for ConcatF3() */ + Sprintf(pricebuf, "%ld %s", quotedprice, currency(quotedprice)); + ConcatF2(bp, 0, " (%s, %s)", + obj->unpaid ? "unpaid" : "contents", pricebuf); } else if (with_price) { /* on floor or in container on floor */ int nochrg = 0; long price = get_cost_of_shop_item(obj, &nochrg); - if (price > 0L) - Sprintf(eos(bp), " (%s, %ld %s)", - nochrg ? "contents" : "for sale", - price, currency(price)); - else if (nochrg > 0) - Sprintf(eos(bp), " (no charge)"); + if (price > 0L) { + char pricebuf[40]; + + Sprintf(pricebuf, "%ld %s", price, currency(price)); + ConcatF2(bp, 0, " (%s, %s)", + nochrg ? "contents" : "for sale", pricebuf); + } else if (nochrg > 0) { + Concat(bp, 0, " (no charge)"); + } } + if (!strncmp(prefix, "a ", 2)) { /* save current prefix, without "a "; might be empty */ Strcpy(tmpbuf, prefix + 2); @@ -1559,29 +1736,77 @@ doname_base( /* show weight for items */ if (iflags.invweight && (obj->where == OBJ_INVENT || wizard)) { - Sprintf(eos(bp), " {%d}", obj->owt); + /* user has asked to see object weights */ + if (with_price && bp_eos[-1] == ')') + ConcatF1(bp, 1, ", %u aum)", obj->owt); + else + ConcatF1(bp, 0, " (%u aum)", obj->owt); + + /* ConcatF1(bp) updates bp_eos and bpspaceleft but we're done + with them now; add a fake use so compiler won't complain + about a variable assignment that won't be subsequently used */ + nhUse(bp_eos); + nhUse(bpspaceleft); } bp = strprepend(bp, prefix); + + /* + * Last gasp bounds check. + * + * If caller intends this to be for a menu entry, make sure that + * there is some room to combine with menu selector prefix without + * exceeding BUFSZ-1. + * + * offsetbp=4: width of menu entry selector text: "c - " for tty. + * For curses, that wastes a char since it only needs 3: "c) ". + * + * Reaching full BUFSZ-1 length can't happen unless both doname + * (BUFSZ-PREFIX) and strprepend (PREFIX) use up all available + * space or one of them overflows without being detected. + */ + if (strlen(bp) > BUFSZ - 1) { + paniclog("doname", bp); + /* ideally this will never happen; if xnamep is any obuf[] + other than the last, overflow here would be relatively + benign and we could probably keep going */ + panic("doname: long object description overflow."); + /*NOTREACHED*/ + } else { + static int doname_full = 0; + int offsetbp = for_menu ? 4 : 0; + + if (strlen(bp) + offsetbp >= BUFSZ - 1) { + /* for !offsetbp, we'll only get here if strlen(bp)==BUFSZ-1 */ + if (!doname_full++) { + paniclog("doname", bp); + Sprintf(tmpbuf, "long object description%s.", + offsetbp ? " truncated for menu use" : ""); + paniclog("doname", tmpbuf); + } + bp[BUFSZ - 1 - offsetbp] = '\0'; + } + } + return bp; } char * -doname(struct obj* obj) +doname(struct obj *obj) { return doname_base(obj, (unsigned) 0); } /* Name of object including price. */ char * -doname_with_price(struct obj* obj) +doname_with_price(struct obj *obj) { return doname_base(obj, DONAME_WITH_PRICE); } /* "some" instead of precise quantity if obj->dknown not set */ char * -doname_vague_quan(struct obj* obj) +doname_vague_quan(struct obj *obj) { /* Used by farlook. * If it hasn't been seen up close and quantity is more than one, @@ -1600,7 +1825,7 @@ doname_vague_quan(struct obj* obj) /* used from invent.c with query_objlist */ boolean -not_fully_identified(struct obj* otmp) +not_fully_identified(struct obj *otmp) { /* gold doesn't have any interesting attributes [yet?] */ if (otmp->oclass == COIN_CLASS) @@ -1626,6 +1851,11 @@ not_fully_identified(struct obj* otmp) else /* lack of `rknown' only matters for vulnerable objects */ return (boolean) (is_damageable(otmp) || destroyable_oclass(otmp->oclass) + /* is_crackable() is covered in is_damageable() but + * won't suffice here unless every item made of glass + * cracks before breaking, which light objects like + * glass wands won't; since those can be made + * indestructible, they are covered here */ || otmp->material == GLASS); } @@ -1637,8 +1867,7 @@ corpse_xname( const char *adjective, unsigned cxn_flags) /* bitmask of CXN_xxx values */ { - /* some callers [aobjnam()] rely on prefix area that xname() sets aside */ - char *nambuf = nextobuf() + PREFIX; + char *nambuf; int omndx = otmp->corpsenm; boolean ignore_quan = (cxn_flags & CXN_SINGULAR) != 0, /* suppress "the" from "the unique monster corpse" */ @@ -1653,6 +1882,10 @@ corpse_xname( glob = (otmp->otyp != CORPSE && otmp->globby); const char *mnam; + /* some callers [aobjnam()] rely on prefix area that xname() sets aside */ + gx.xnamep = nextobuf(); + nambuf = gx.xnamep + PREFIX; + if (glob) { mnam = OBJ_NAME(objects[otmp->otyp]); /* "glob of " */ } else if (omndx == NON_PM) { /* paranoia */ @@ -1729,7 +1962,7 @@ corpse_xname( /* xname doesn't include monster type for "corpse"; cxname does */ char * -cxname(struct obj* obj) +cxname(struct obj *obj) { if (obj->otyp == CORPSE) return corpse_xname(obj, (const char *) 0, CXN_NORMAL); @@ -1738,7 +1971,7 @@ cxname(struct obj* obj) /* like cxname, but ignores quantity */ char * -cxname_singular(struct obj* obj) +cxname_singular(struct obj *obj) { if (obj->otyp == CORPSE) return corpse_xname(obj, (const char *) 0, CXN_SINGULAR); @@ -1747,7 +1980,7 @@ cxname_singular(struct obj* obj) /* treat an object as fully ID'd when it might be used as reason for death */ char * -killer_xname(struct obj* obj) +killer_xname(struct obj *obj) { struct obj save_obj; unsigned save_ocknown; @@ -1895,7 +2128,7 @@ short_oname( * Used if only one of a collection of objects is named (e.g. in eat.c). */ const char * -singular(struct obj* otmp, char* (*func)(OBJ_P)) +singular(struct obj *otmp, char *(*func)(OBJ_P)) { long savequan; char *nam; @@ -1922,9 +2155,14 @@ just_an(char *outbuf, const char *str) if (!str[1] || str[1] == ' ') { /* single letter; might be used for named fruit or a musical note */ Strcpy(outbuf, strchr("aefhilmnosx", c0) ? "an " : "a "); - } else if (!strncmpi(str, "the ", 4) || !strcmpi(str, "molten lava") - || !strcmpi(str, "iron bars") || !strcmpi(str, "ice") - || !strcmpi(str, "grass")) { + } else if (!strncmpi(str, "the ", 4) + /* these probably shouldn't be handled here because doing so + impacts inventory when using them for named fruit */ + || !strcmpi(str, "molten lava") + || !strcmpi(str, "iron bars") + || !strcmpi(str, "ice") + || !strcmpi(str, "grass") + ) { ; /* no article */ } else { /* normal case is "an " or "a " */ @@ -1985,7 +2223,7 @@ An(const char *str) * Use type_is_pname() for monster names, not the(). the() is idempotent. */ char * -the(const char* str) +the(const char *str) { const char *aname; char *buf = nextobuf(); @@ -2012,7 +2250,7 @@ the(const char* str) insert_the = TRUE; } else { /* Probably a proper name, might not need an article */ - register char *tmp, *named, *called; + char *tmp, *named, *called; int l; /* some objects have capitalized adjectives in their names */ @@ -2075,7 +2313,7 @@ aobjnam(struct obj *otmp, const char *verb) /* combine yname and aobjnam eg "your count cxname(otmp)" */ char * -yobjnam(struct obj* obj, const char *verb) +yobjnam(struct obj *obj, const char *verb) { char *s = aobjnam(obj, verb); @@ -2093,9 +2331,9 @@ yobjnam(struct obj* obj, const char *verb) /* combine Yname2 and aobjnam eg "Your count cxname(otmp)" */ char * -Yobjnam2(struct obj* obj, const char *verb) +Yobjnam2(struct obj *obj, const char *verb) { - register char *s = yobjnam(obj, verb); + char *s = yobjnam(obj, verb); *s = highc(*s); return s; @@ -2103,7 +2341,7 @@ Yobjnam2(struct obj* obj, const char *verb) /* like aobjnam, but prepend "The", not count, and use xname */ char * -Tobjnam(struct obj* otmp, const char *verb) +Tobjnam(struct obj *otmp, const char *verb) { char *bp = The(xname(otmp)); @@ -2116,7 +2354,7 @@ Tobjnam(struct obj* otmp, const char *verb) /* capitalized variant of doname() */ char * -Doname2(struct obj* obj) +Doname2(struct obj *obj) { char *s = doname(obj); @@ -2124,32 +2362,55 @@ Doname2(struct obj* obj) return s; } -#if 0 /* stalled-out work in progress */ -/* Doname2() for itemized buying of 'obj' from a shop */ +/* doname() for itemized buying of 'obj' from a shop */ char * -payDoname(struct obj* obj) +paydoname(struct obj *obj) { static const char and_contents[] = " and its contents"; - char *p = doname(obj); + char *p; + unsigned save_cknown = obj->cknown; + boolean save_invweight = iflags.invweight; + + if (Has_contents(obj)) + obj->cknown = 0; + /* avoid showing item weights to unclutter billing's pay-menu a bit */ + iflags.invweight = FALSE; + /* suppress invent-style price; caller will add billing-style price */ + iflags.suppress_price++; + p = doname_base(obj, 0U); + iflags.suppress_price--; + iflags.invweight = save_invweight; + + if (Has_contents(obj)) { + /* buy_container() sets no_charge for a container that has just + been purchased so that when paydoname() is called by + shk_names_obj(), we'll provide "a/an " instead of + "your " */ + if (!obj->no_charge) { + if (!strncmp(p, "a ", 2)) + p += 2; + else if (!strncmp(p, "an ", 3)) + p += 3; + p = strprepend(p, obj->unpaid ? "an unpaid " : "your "); + } - if (Is_container(obj) && !obj->cknown) { - if (obj->unpaid) { - if ((int) strlen(p) + sizeof and_contents - 1 < BUFSZ - PREFIX) - Strcat(p, and_contents); - *p = highc(*p); - } else { - p = strprepend(p, "Contents of "); + if (!obj->cknown) { + if (obj->unpaid) { + if ((int) strlen(p) + sizeof and_contents - 1 + < BUFSZ - PREFIX) + Strcat(p, and_contents); + } else { + p = strprepend(p, "the contents of "); + } } - } else { - *p = highc(*p); } + obj->cknown = save_cknown; return p; } -#endif /*0*/ /* returns "[your ]xname(obj)" or "Foobar's xname(obj)" or "the xname(obj)" */ char * -yname(struct obj* obj) +yname(struct obj *obj) { char *s = cxname(obj); @@ -2168,7 +2429,7 @@ yname(struct obj* obj) /* capitalized variant of yname() */ char * -Yname2(struct obj* obj) +Yname2(struct obj *obj) { char *s = yname(obj); @@ -2181,7 +2442,7 @@ Yname2(struct obj* obj) * or "the minimal_xname(obj)" */ char * -ysimple_name(struct obj* obj) +ysimple_name(struct obj *obj) { char *outbuf = nextobuf(); char *s = shk_your(outbuf, obj); /* assert( s == outbuf ); */ @@ -2192,7 +2453,7 @@ ysimple_name(struct obj* obj) /* capitalized variant of ysimple_name() */ char * -Ysimple_name2(struct obj* obj) +Ysimple_name2(struct obj *obj) { char *s = ysimple_name(obj); @@ -2202,7 +2463,7 @@ Ysimple_name2(struct obj* obj) /* "scroll" or "scrolls" */ char * -simpleonames(struct obj* obj) +simpleonames(struct obj *obj) { char *obufp, *simpleoname = minimal_xname(obj); @@ -2220,7 +2481,7 @@ simpleonames(struct obj* obj) /* "a scroll" or "scrolls"; "a silver bell" or "the Bell of Opening" */ char * -ansimpleoname(struct obj* obj) +ansimpleoname(struct obj *obj) { char *obufp, *simpleoname = simpleonames(obj); int otyp = obj->otyp; @@ -2302,7 +2563,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; @@ -2334,7 +2595,7 @@ static const char *const special_subjs[] = { /* return form of the verb (input plural) for present tense 3rd person subj */ char * -vtense(const char* subj, const char* verb) +vtense(const char *subj, const char *verb) { char *buf = nextobuf(), *bspot; int len, ltmp; @@ -2438,6 +2699,7 @@ static const struct sing_plur one_off[] = { "children" }, /* (for wise guys who give their food funny names) */ { "cubus", "cubi" }, /* in-/suc-cubus */ { "culus", "culi" }, /* homunculus */ + { "Cyclops", "Cyclopes" }, { "djinni", "djinn" }, { "erinys", "erinyes" }, { "foot", "feet" }, @@ -2478,7 +2740,7 @@ static const char *const as_is[] = { }; /* singularize/pluralize decisions common to both makesingular & makeplural */ -static boolean +staticfn boolean singplur_lookup( char *basestr, char *endstring, /* base string, pointer to eos(string) */ boolean to_plural, /* true => makeplural, false => makesingular */ @@ -2553,7 +2815,7 @@ singplur_lookup( } /* searches for common compounds, ex. lump of royal jelly */ -static char * +staticfn char * singplur_compound(char *str) { /* if new entries are added, be sure to keep compound_start[] in sync */ @@ -2607,9 +2869,9 @@ singplur_compound(char *str) * 3.6.0: made case-insensitive. */ char * -makeplural(const char* oldstr) +makeplural(const char *oldstr) { - register char *spot; + char *spot; char lo_c, *str = nextobuf(); const char *excess = (char *) 0; int len, i; @@ -2815,9 +3077,9 @@ makeplural(const char* oldstr) * 3.6.0: made case-insensitive. */ char * -makesingular(const char* oldstr) +makesingular(const char *oldstr) { - register char *p, *bp; + char *p, *bp; const char *excess = 0; char *str = nextobuf(); @@ -2945,7 +3207,7 @@ makesingular(const char* oldstr) } -static boolean +staticfn boolean ch_ksound(const char *basestr) { /* these are some *ch words/suffixes that make a k-sound. They pluralize by @@ -2971,7 +3233,7 @@ ch_ksound(const char *basestr) return FALSE; } -static boolean +staticfn boolean badman( const char *basestr, boolean to_plural) /* True: makeplural, False: makesingular */ @@ -3020,7 +3282,7 @@ badman( } /* compare user string against object name string using fuzzy matching */ -static boolean +staticfn boolean wishymatch( const char *u_str, /* from user, so might be variant spelling */ const char *o_str, /* from objects[], so is in canonical form */ @@ -3180,6 +3442,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 */ @@ -3191,6 +3454,7 @@ static const struct alt_spellings { { "grapnel", GRAPPLING_HOOK }, { "grapple", GRAPPLING_HOOK }, { "protection from shape shifters", RIN_PROTECTION_FROM_SHAPE_CHAN }, + { "accuracy", RIN_INCREASE_ACCURACY }, /* if we ever add other sizes, move this to o_ranges[] with "bag" */ { "box", LARGE_BOX }, /* normally we wouldn't have to worry about unnecessary , but @@ -3204,13 +3468,13 @@ static const struct alt_spellings { { (const char *) 0, 0 }, }; -static short +staticfn short rnd_otyp_by_wpnskill(schar skill) { int i, n = 0; short otyp = STRANGE_OBJECT; - for (i = gb.bases[WEAPON_CLASS]; + for (i = svb.bases[WEAPON_CLASS]; i < NUM_OBJECTS && objects[i].oc_class == WEAPON_CLASS; i++) if (objects[i].oc_skill == skill) { n++; @@ -3218,7 +3482,7 @@ rnd_otyp_by_wpnskill(schar skill) } if (n > 0) { n = rn2(n); - for (i = gb.bases[WEAPON_CLASS]; + for (i = svb.bases[WEAPON_CLASS]; i < NUM_OBJECTS && objects[i].oc_class == WEAPON_CLASS; i++) if (objects[i].oc_skill == skill) if (--n < 0) @@ -3227,7 +3491,7 @@ rnd_otyp_by_wpnskill(schar skill) return otyp; } -static short +staticfn short rnd_otyp_by_namedesc( const char *name, char oclass, @@ -3236,7 +3500,7 @@ rnd_otyp_by_namedesc( { int i, n = 0; short validobjs[NUM_OBJECTS]; - register const char *zn, *of; + const char *zn, *of; boolean check_of; int lo, hi, minglob, maxglob, prob, maxprob = 0; @@ -3250,8 +3514,8 @@ rnd_otyp_by_namedesc( (void) memset((genericptr_t) validobjs, 0, sizeof validobjs); if (oclass) { - lo = gb.bases[(uchar) oclass]; - hi = gb.bases[(uchar) oclass + 1] - 1; + lo = svb.bases[(uchar) oclass]; + hi = svb.bases[(uchar) oclass + 1] - 1; } else { lo = MAXOCLASSES; /* STRANGE_OBJECT + 1; */ hi = NUM_OBJECTS - 1; @@ -3310,13 +3574,29 @@ 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 */ -static struct obj * +staticfn struct obj * wizterrainwish(struct _readobjnam_data *d) { struct rm *lev; - boolean madeterrain = FALSE, badterrain = FALSE, didblock; - int trap, oldtyp; + boolean madeterrain = FALSE, badterrain = FALSE, didblock, is_dbridge; + int trap; + unsigned oldtyp, ltyp; coordxy x = u.ux, y = u.uy; char *bp = d->bp, *p = d->p; @@ -3335,22 +3615,25 @@ wizterrainwish(struct _readobjnam_data *d) trap = t->ttyp; pline("%s%s.", An(tname), (trap != MAGIC_PORTAL) ? "" : " to nowhere"); - } else + } else { pline("Creation of %s failed.", an(tname)); - return (struct obj *) &cg.zeroobj; + } + return &hands_obj; } /* furniture and terrain (use at your own risk; can clobber stairs or place furniture on existing traps which shouldn't be allowed) */ 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; - gl.level.flags.nfountains++; + if (oldtyp != FOUNTAIN) + svl.level.flags.nfountains++; lev->looted = d->looted ? F_LOOTED : 0; /* overlays 'flags' */ - lev->blessedftn = !strncmpi(bp, "magic ", 6); + lev->blessedftn = d->blessed || !strncmpi(bp, "magic ", 6); pline("A %sfountain.", lev->blessedftn ? "magic " : ""); madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 6, "throne")) { @@ -3360,7 +3643,8 @@ wizterrainwish(struct _readobjnam_data *d) madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 4, "sink")) { lev->typ = SINK; - gl.level.flags.nsinks++; + if (oldtyp != SINK) + svl.level.flags.nsinks++; lev->looted = d->looted ? (S_LPUDDING | S_LDWASHER) : 0; /* wished-for sinks never have a buried ring regardless of d->looted */ pline("A sink."); @@ -3373,38 +3657,75 @@ wizterrainwish(struct _readobjnam_data *d) long save_prop; const char *new_water; - lev->typ = !BSTRCMPI(bp, p - 4, "pool") ? POOL - : !BSTRCMPI(bp, p - 4, "moat") ? MOAT - : WATER; - lev->flags = 0; + ltyp = !BSTRCMPI(bp, p - 4, "pool") ? POOL + : !BSTRCMPI(bp, p - 4, "moat") ? MOAT + : WATER; + if (!is_dbridge) { + lev->typ = ltyp; + lev->flags = 0; + } else { + /* drawbridgemask overloads flags */ + lev->drawbridgemask &= ~DB_UNDER; + lev->drawbridgemask |= DB_MOAT; + } del_engr_at(x, y); - save_prop = EHalluc_resistance; - EHalluc_resistance = 1; - new_water = waterbody_name(x, y); - EHalluc_resistance = save_prop; - pline("%s.", An(new_water)); - /* Must manually make kelp! */ - water_damage_chain(gl.level.objects[x][y], TRUE, 0, TRUE); + if (!is_dbridge) { + save_prop = EHalluc_resistance; + EHalluc_resistance = 1; + new_water = waterbody_name(x, y); + EHalluc_resistance = save_prop; + pline("%s.", An(new_water)); + /* Must manually make kelp! */ + } else { + dbterrainmesg("Moat", x, y); + } + water_damage_chain(svl.level.objects[x][y], TRUE, 0, TRUE); madeterrain = TRUE; /* also matches "molten lava" */ } else if (!BSTRCMPI(bp, p - 4, "lava") || !BSTRCMPI(bp, p - 12, "wall of lava")) { - lev->typ = !BSTRCMPI(bp, p - 12, "wall of lava") ? LAVAWALL : LAVAPOOL; - lev->flags = 0; + ltyp = !BSTRCMPI(bp, p - 12, "wall of lava") ? LAVAWALL : LAVAPOOL; + if (!is_dbridge) { + lev->typ = ltyp; + lev->flags = 0; + } else { + /* drawbridgemask overloads flags */ + lev->drawbridgemask &= ~DB_UNDER; + lev->drawbridgemask |= DB_LAVA; + } del_engr_at(x, y); - pline("A pool of molten lava."); - if (!(Levitation || Flying) || lev->typ == LAVAWALL) - pooleffects(FALSE); + if (!is_dbridge) { + pline("A %s of molten lava.", + (lev->typ == LAVAPOOL) ? "pool" : "wall"); + if (!(Levitation || Flying) || lev->typ == LAVAWALL) + pooleffects(FALSE); + } else { + dbterrainmesg("Lava", x, y); + } + fire_damage_chain(svl.level.objects[x][y], TRUE, TRUE, x, y); madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 3, "ice")) { - lev->typ = ICE; - lev->flags = 0; + if (!is_dbridge) { + lev->typ = ICE; + /* icedpool overloads flags; specifies what ice will melt into */ + lev->icedpool = (oldtyp == ROOM) ? ICED_POOL : ICED_MOAT; + } else { + /* drawbridgemask overloads flags */ + lev->drawbridgemask &= ~DB_UNDER; + lev->drawbridgemask |= DB_ICE; + } del_engr_at(x, y); if (!strncmpi(bp, "melting ", 8)) start_melt_ice_timeout(x, y, 0L); - pline("Ice."); + if (!is_dbridge) { + char icebuf[40]; + + pline("%s.", upstart(ice_descr(x, y, icebuf))); + } else { + dbterrainmesg("Ice", x, y); + } madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 5, "altar")) { aligntyp al; @@ -3438,6 +3759,7 @@ 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 - 5, "grass")) { @@ -3448,6 +3770,7 @@ wizterrainwish(struct _readobjnam_data *d) } 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 @@ -3458,6 +3781,7 @@ wizterrainwish(struct _readobjnam_data *d) lev->typ = CLOUD; lev->flags = 0; pline("A cloud."); + del_engr_at(x, y); madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 4, "door") || (d->doorless && !BSTRCMPI(bp, p - 7, "doorway"))) { @@ -3503,7 +3827,7 @@ wizterrainwish(struct _readobjnam_data *d) * interacting with an open door. This ideally should check to see * if the type of trap is like that. */ if (d->trapped == 2 /* 2: wish includes explicit "untrapped" */ - || secret /* secret doors can't trapped due to their use + || 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 */ || doorstate(lev) == D_NODOOR || doorstate(lev) == D_ISOPEN) @@ -3544,7 +3868,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)) @@ -3552,6 +3876,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."); @@ -3565,6 +3891,30 @@ wizterrainwish(struct _readobjnam_data *d) pline("Secret corridor requires corridor location."); badterrain = TRUE; } + } else if (!BSTRCMPI(bp, p - 4, "room") + || !BSTRCMPI(bp, p - 5, "floor") + || !BSTRCMPI(bp, p - 6, "ground")) { + if (oldtyp == ROOM + || (IS_FURNITURE(oldtyp) && CAN_OVERWRITE_TERRAIN(oldtyp)) + || oldtyp == ICE || is_pool_or_lava(x, y)) { + struct trap *t; + + lev->typ = ROOM; + pline("Room floor."); + if (IS_FURNITURE(oldtyp)) + count_level_features(); + if ((t = t_at(x, y)) != 0 && t->ttyp != MAGIC_PORTAL) + deltrap(t); + madeterrain = TRUE; + } else if (is_dbridge) { + lev->drawbridgemask &= ~DB_UNDER; + lev->drawbridgemask |= DB_FLOOR; + dbterrainmesg("Floor", x, y); + madeterrain = TRUE; + } else { + pline("Room|floor|ground not allowed here."); + badterrain = TRUE; + } } if (madeterrain) { @@ -3575,6 +3925,7 @@ wizterrainwish(struct _readobjnam_data *d) if (u.uinwater && !is_pool(u.ux, u.uy)) { set_uinwater(0); /* u.uinwater = 0; leave the water */ docrt(); + /* [block/unblock_point handled by docrt -> vision_recalc] */ } else { if (u.utrap && u.utraptype == TT_LAVA && !is_lava(u.ux, u.uy)) reset_utrap(FALSE); @@ -3588,14 +3939,11 @@ wizterrainwish(struct _readobjnam_data *d) } } - /* fixups for replaced terrain that aren't handled above; - for fountain placed on fountain or sink placed on sink, the - increment above gets canceled out by the decrement here; - otherwise if fountain or sink was replaced, there's one less */ - if (IS_FOUNTAIN(oldtyp)) - gl.level.flags.nfountains--; - else if (IS_SINK(oldtyp)) - gl.level.flags.nsinks--; + /* fixups for replaced terrain that aren't handled above */ + if (IS_FOUNTAIN(oldtyp) || IS_SINK(oldtyp)) + count_level_features(); /* update level.flags.nfountains,nsinks */ + if (!is_ice(x, y)) + spot_stop_timers(x, y, MELT_ICE_AWAY); /* horizontal is overlaid by fountain->blessedftn, grave->disturbed */ if (IS_FOUNTAIN(oldtyp) || IS_GRAVE(oldtyp) || IS_WALL(oldtyp) || oldtyp == IRONBARS @@ -3616,10 +3964,8 @@ wizterrainwish(struct _readobjnam_data *d) while in xorn form and replacing solid stone with furniture) */ switch_terrain(); } - if (madeterrain || badterrain) { - /* cast 'const' away; caller won't modify this */ - return (struct obj *) &cg.zeroobj; - } + if (madeterrain || badterrain) + return &hands_obj; return (struct obj *) 0; } @@ -3806,11 +4152,21 @@ not_actually_specifying_material(const char * const str, int material) return FALSE; } +/* message common to several wizterrainwish() results */ +staticfn void +dbterrainmesg( + const char *newtype, + coordxy x, coordxy y) +{ + pline("%s %s the drawbridge.", newtype, + (levl[x][y].typ == DRAWBRIDGE_UP) ? "in front of" : "under"); +} + #define TIN_UNDEFINED 0 #define TIN_EMPTY 1 #define TIN_SPINACH 2 -static void +staticfn void readobjnam_init(char *bp, struct _readobjnam_data *d) { d->otmp = (struct obj *) 0; @@ -3838,21 +4194,21 @@ readobjnam_init(char *bp, struct _readobjnam_data *d) d->bp = d->origbp = bp; d->p = (char *) 0; d->name = (const char *) 0; - d->ftype = gc.context.current_fruit; + d->ftype = svc.context.current_fruit; (void) memset(d->globbuf, '\0', sizeof d->globbuf); (void) memset(d->fruitbuf, '\0', sizeof d->fruitbuf); } /* return 1 if d->bp is empty or contains only various qualifiers like "blessed", "rustproof", and so on, or 0 if anything else is present */ -static int +staticfn int readobjnam_preparse(struct _readobjnam_data *d) { char *save_bp = 0; int more_l = 0, res = 1; for (;;) { - register int l; + int l; if (!d->bp || !*d->bp) break; @@ -3891,6 +4247,12 @@ readobjnam_preparse(struct _readobjnam_data *d) || !strncmpi(d->bp, "fixed ", l = 6) || !strncmpi(d->bp, "fireproof ", l = 10) || !strncmpi(d->bp, "rotproof ", l = 9) + || !strncmpi(d->bp, "tempered ", l = 9) + || !strncmpi(d->bp, "crackproof ", l = 11) + /* prior to upstream NetHack introducing "tempered" in 2024, + * xNetHack used "shatterproof" for the same purpose; allow + * this as an alias to support players used to wishing for + * it. */ || !strncmpi(d->bp, "shatterproof ", l = 13) || !strncmpi(d->bp, "indestructible ", l = 15)) { d->erodeproof = 1; @@ -3964,7 +4326,8 @@ readobjnam_preparse(struct _readobjnam_data *d) } else if (!strncmpi(d->bp, "rusty ", l = 6) || !strncmpi(d->bp, "rusted ", l = 7) || !strncmpi(d->bp, "burnt ", l = 6) - || !strncmpi(d->bp, "burned ", l = 7)) { + || !strncmpi(d->bp, "burned ", l = 7) + || !strncmpi(d->bp, "cracked ", l = 8)) { d->eroded = 1 + d->very; d->very = 0; } else if (!strncmpi(d->bp, "corroded ", l = 9) @@ -3997,7 +4360,7 @@ readobjnam_preparse(struct _readobjnam_data *d) and less than 15 (owt < 300) */ d->gsize = 2; } else if (!strncmpi(d->bp, "large ", l = 6)) { - /* "large" might be part of monster name (dog, cat, koboold, + /* "large" might be part of monster name (dog, cat, kobold, mimic) or object name (box, round shield) rather than prefix for glob size */ if (strncmpi(d->bp + l, "glob", 4) && !strstri(d->bp + l, " glob")) @@ -4079,7 +4442,7 @@ readobjnam_preparse(struct _readobjnam_data *d) return res; } -static void +staticfn void readobjnam_parse_charges(struct _readobjnam_data *d) { if (strlen(d->bp) > 1 && (d->p = strrchr(d->bp, '(')) != 0) { @@ -4141,7 +4504,7 @@ readobjnam_parse_charges(struct _readobjnam_data *d) d->rechrg = 7; /* recharge_limit */ } -static int +staticfn int readobjnam_postparse1(struct _readobjnam_data *d) { int i; @@ -4223,7 +4586,7 @@ readobjnam_postparse1(struct _readobjnam_data *d) * referred to as a "pair of". E.g. We should double if the player * types "pair of spears", but not if the player types "pair of * lenses". Luckily (?) all objects that are referred to as pairs - * -- boots, gloves, and lenses -- are also not mergable, so cnt is + * -- boots, gloves, and lenses -- are also not mergeable, so cnt is * ignored anyway. */ if (!strncmpi(d->bp, "pair of ", 8)) { @@ -4450,7 +4813,7 @@ readobjnam_postparse1(struct _readobjnam_data *d) d->otmp = mksobj(GOLD_PIECE, FALSE, FALSE); d->otmp->quan = (long) d->cnt; d->otmp->owt = weight(d->otmp); - gc.context.botl = 1; + disp.botl = TRUE; return 3; /*return otmp;*/ } @@ -4476,7 +4839,7 @@ readobjnam_postparse1(struct _readobjnam_data *d) && strncmpi(d->bp, "amulet of storms", 16) ) for (i = 0; i < (int) (sizeof wrpsym); i++) { - register int j = Strlen(wrp[i]); + int j = Strlen(wrp[i]); /* check for " [ of ] something" */ if (!strncmpi(d->bp, wrp[i], j)) { @@ -4559,7 +4922,7 @@ readobjnam_postparse1(struct _readobjnam_data *d) return 0; } -static int +staticfn int readobjnam_postparse2(struct _readobjnam_data *d) { int i; @@ -4590,7 +4953,7 @@ readobjnam_postparse2(struct _readobjnam_data *d) ; /* avoid false hit on "* glass" */ } else if (!BSTRCMPI(d->bp, d->p - 6, " glass") || !strcmpi(d->bp, "glass")) { - register char *s = d->bp; + char *s = d->bp; /* treat "broken glass" as a non-existent item; since "broken" is also a chest/box prefix it might have been stripped off above */ @@ -4629,15 +4992,15 @@ readobjnam_postparse2(struct _readobjnam_data *d) return 0; } -static int +staticfn int readobjnam_postparse3(struct _readobjnam_data *d) { int i; /* check real names of gems first */ if (!d->oclass && d->actualn) { - for (i = gb.bases[GEM_CLASS]; i <= LAST_REAL_GEM; i++) { - register const char *zn; + for (i = svb.bases[GEM_CLASS]; i <= LAST_REAL_GEM; i++) { + const char *zn; if ((zn = OBJ_NAME(objects[i])) != 0 && !strcmpi(d->actualn, zn)) { d->typ = i; @@ -4786,6 +5149,21 @@ readobjnam_postparse3(struct _readobjnam_data *d) } } + /* got a class, but not specific type; + check alternate spellings of items with matching classes */ + if (d->oclass && !d->typ) { + const struct alt_spellings *as = spellings; + + while (as->sp) { + if (objects[as->ob].oc_class == d->oclass + && wishymatch(d->bp, as->sp, TRUE)) { + d->typ = as->ob; + return 2; /*goto typfnd;*/ + } + as++; + } + } + return 0; } @@ -4794,7 +5172,7 @@ readobjnam_postparse3(struct _readobjnam_data *d) * Return something wished for. Specifying a null pointer for * the user request string results in a random object. Otherwise, * if asking explicitly for "nothing" (or "nil") return no_wish; - * if not an object return &cg.zeroobj; if an error (no matching object), + * if not an object return &hands_obj; if an error (no matching object), * return null. */ struct obj * @@ -4864,7 +5242,7 @@ readobjnam(char *bp, struct obj *no_wish) * Disallow such topology tweaks for WIZKIT startup wishes. */ wiztrap: - if (wizard && !gp.program_state.wizkit_wishing && !d.oclass) { + if (wizard && !program_state.wizkit_wishing && !d.oclass) { /* [inline code moved to separate routine to unclutter readobjnam] */ if ((d.otmp = wizterrainwish(&d)) != 0) return d.otmp; @@ -4976,7 +5354,7 @@ readobjnam(char *bp, struct obj *no_wish) if (rn1cnt > 6 - d.gsize) rn1cnt = 6 - d.gsize; if (d.cnt > rn1cnt - && (!wizard || gp.program_state.wizkit_wishing + && (!wizard || program_state.wizkit_wishing || y_n("Override glob weight limit?") != 'y')) d.cnt = rn1cnt; d.otmp->owt *= (unsigned) d.cnt; @@ -5051,7 +5429,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: @@ -5061,7 +5440,7 @@ readobjnam(char *bp, struct obj *no_wish) case STATUE: /* otmp->cobj already done in mksobj() */ case FIGURINE: case CORPSE: { - struct permonst *P = (d.mntmp >= LOW_PM) ? &mons[d.mntmp] : 0; + struct permonst *P = (ismnum(d.mntmp)) ? &mons[d.mntmp] : 0; d.otmp->spe = !P ? CORPSTAT_RANDOM /* if neuter, force neuter regardless of wish request */ @@ -5083,7 +5462,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: @@ -5095,13 +5475,14 @@ readobjnam(char *bp, struct obj *no_wish) d.otmp->spe = (rn2(10) ? -1 : 0); break; } + FALLTHROUGH; /*FALLTHRU*/ default: d.otmp->spe = d.spe; } /* set otmp->corpsenm or dragon scale [mail] */ - if (d.mntmp >= LOW_PM) { + if (ismnum(d.mntmp)) { int humanwere; if (d.mntmp == PM_LONG_WORM_TAIL) @@ -5110,7 +5491,7 @@ readobjnam(char *bp, struct obj *no_wish) corpses and tins, switch to their corresponding human form; for figurines, override the can't-be-human restriction instead */ if (d.typ != FIGURINE && is_were(&mons[d.mntmp]) - && (gm.mvitals[d.mntmp].mvflags & G_NOCORPSE) != 0 + && (svm.mvitals[d.mntmp].mvflags & G_NOCORPSE) != 0 && (humanwere = counter_were(d.mntmp)) != NON_PM) d.mntmp = humanwere; @@ -5119,14 +5500,14 @@ readobjnam(char *bp, struct obj *no_wish) if (dead_species(d.mntmp, FALSE)) { d.otmp->corpsenm = NON_PM; /* it's empty */ } else if ((!(mons[d.mntmp].geno & G_UNIQ) || wizard) - && !(gm.mvitals[d.mntmp].mvflags & G_NOCORPSE) + && !(svm.mvitals[d.mntmp].mvflags & G_NOCORPSE) && mons[d.mntmp].cnutrit != 0) { d.otmp->corpsenm = d.mntmp; } break; case CORPSE: if ((!(mons[d.mntmp].geno & G_UNIQ) || wizard) - && !(gm.mvitals[d.mntmp].mvflags & G_NOCORPSE)) { + && !(svm.mvitals[d.mntmp].mvflags & G_NOCORPSE)) { if (mons[d.mntmp].msound == MS_GUARDIAN) d.mntmp = genus(d.mntmp, 1); set_corpsenm(d.otmp, d.mntmp); @@ -5186,7 +5567,8 @@ readobjnam(char *bp, struct obj *no_wish) if (erosion_matters(d.otmp) || destroyable_oclass(d.otmp->oclass)) { /* wished-for item shouldn't be eroded unless specified */ d.otmp->oeroded = d.otmp->oeroded2 = 0; - if (d.eroded && (is_flammable(d.otmp) || is_rustprone(d.otmp))) + if (d.eroded && (is_flammable(d.otmp) || is_rustprone(d.otmp) + || is_crackable(d.otmp))) d.otmp->oeroded = d.eroded; if (d.eroded2 && (is_corrodeable(d.otmp) || is_rottable(d.otmp))) d.otmp->oeroded2 = d.eroded2; @@ -5303,7 +5685,7 @@ readobjnam(char *bp, struct obj *no_wish) || (d.otmp->oartifact && rn2(u.uconduct.wisharti))) && !wizard) { artifact_exists(d.otmp, safe_oname(d.otmp), FALSE, ONAME_NO_FLAGS); obfree(d.otmp, (struct obj *) 0); - d.otmp = (struct obj *) &cg.zeroobj; + d.otmp = &hands_obj; if (Hallucination) pline("Yeah, you wish!"); else @@ -5465,7 +5847,7 @@ helm_simple_name(struct obj *helmet) * fedora, cornuthaum, dunce cap -> hat * all other types of helmets -> helm */ - return (helmet && !is_hard(helmet)) ? "hat" : "helm"; + return !hard_helmet(helmet) ? "hat" : "helm"; } /* gloves vs gauntlets; depends upon discovery state */ @@ -5536,7 +5918,7 @@ shield_simple_name(struct obj *shield) return "shield"; } -/* for completness */ +/* for completeness */ const char * shirt_simple_name(struct obj *shirt UNUSED) { diff --git a/src/options.c b/src/options.c index 741c72e670..1e1c2e4270 100644 --- a/src/options.c +++ b/src/options.c @@ -1,22 +1,23 @@ -/* NetHack 3.7 options.c $NHDT-Date: 1661218575 2022/08/23 01:36:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.601 $ */ +/* NetHack 3.7 options.c $NHDT-Date: 1710792444 2024/03/18 20:07:24 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.723 $ */ /* 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 +#define COMPLAIN_ABOUT_PRAYCONFIRM /* whether the 'msg_window' option is used to control ^P behavior */ #if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS) @@ -49,6 +50,8 @@ NEARDATA struct instance_flags iflags; /* provide linkage */ * */ +#define OPTIONS_C + #define NHOPT_PROTO #include "optlist.h" #undef NHOPT_PROTO @@ -71,12 +74,12 @@ static struct allopt_t allopt_init[] = { }; #undef NHOPT_PARSE +#undef OPTIONS_C #define PILE_LIMIT_DFLT 5 #define rolestring(val, array, field) \ ((val >= 0) ? array[val].field : (val == ROLE_RANDOM) ? randomrole : none) - enum window_option_types { MESSAGE_OPTION = 1, STATUS_OPTION, @@ -93,7 +96,7 @@ enum requests { }; /* these aren't the same as set_xxx in optlist.h */ enum option_phases { - builtin_opt, /* compiled-in default value of an option */ + builtin_opt=1,/* compiled-in default value of an option */ syscf_opt, /* sysconf setting of an option, overrides builtin */ rc_file_opt, /* player's run-time config file setting, overrides syscf */ environ_opt, /* player's environment XNETHACKOPTIONS, overrides rc_file */ @@ -108,9 +111,9 @@ static struct allopt_t allopt[SIZE(allopt_init)]; /* use rest of file */ -extern char configfile[]; /* for messages */ +extern char configfile[]; /* for messages; files.c */ extern const struct symparse loadsyms[]; -#if defined(TOS) && defined(TEXTCOLOR) +#if defined(TOS) extern boolean colors_changed; /* in tos.c */ #endif #ifdef VIDEOSHADES @@ -122,8 +125,9 @@ static char empty_optstr[] = { '\0' }; static boolean duplicate, using_alias; static boolean give_opt_msg = TRUE; +enum { MAX_ROLEOPT = 4 }; /* 4: role,race,gend,algn */ static boolean opt_set_in_config[OPTCOUNT]; -static char *roleoptvals[4][num_opt_phases]; /* 4: role,race,gend,algn */ +static char *roleoptvals[MAX_ROLEOPT][num_opt_phases]; static NEARDATA const char *OptS_type[OptS_Advanced+1] = { "General", "Behavior", "Map", "Status", "Advanced" @@ -148,15 +152,18 @@ static const struct paranoia_opts { int synMinLen; const char *explain; /* for interactive menu */ } paranoia[] = { - /* there are some initial-letter conflicts: "a"ttack vs "a"ll, "attack" - takes precedence and "all" isn't present in the interactive menu, + /* there are some initial-letter conflicts: "a"ttack vs "A"utoall vs + "a"ll, "attack" takes precedence and "all" isn't present in the + interactive menu with "Autoall" capitalized there, and "d"ie vs "d"eath, synonyms for each other so doesn't matter; (also "p"ray vs "P"aranoia, "pray" takes precedence since "Paranoia" is just a synonym for "Confirm"); "b"ones vs "br"eak-wand, the latter requires at least two letters; "e"at vs "ex"plore, "cont"inue eating vs "C"onfirm; "wand"-break vs "Were"-change, - both require at least two letters during config processing and use - case-senstivity for 'O's interactive menu */ + both require at least two letters during config processing but use + one letter with case-sensitivity for 'm O's interactive menu; + if any entry or alias beginning with 'n' gets added, aside from "none", + the parsing to accept "nofoo" to mean "!foo" will need fixing */ { PARANOID_CONFIRM, "Confirm", 1, "Paranoia", 2, "for \"yes\" confirmations, require \"no\" to reject" }, { PARANOID_QUIT, "quit", 1, "explore", 2, @@ -173,14 +180,20 @@ static const struct paranoia_opts { "yes vs y to continue eating after first bite when satiated" }, { PARANOID_WERECHANGE, "Were-change", 2, (const char *) 0, 0, "yes vs y to change form when lycanthropy is controllable" }, + /* extra y/n questions rather than changing y/n to yes/n[o]; + they switch to yes/no if paranoid:confirm is also set */ { PARANOID_PRAY, "pray", 1, 0, 0, - "y to pray (supersedes old \"prayconfirm\" option)" }, + "y required to pray (supersedes old \"prayconfirm\" option)" }, + { PARANOID_TRAP, "trap", 1, "move-trap", 1, + "y required to enter known trap unless considered harmless" }, + { PARANOID_AUTOALL, "Autoall", 2, "autoselect-all", 2, + "y required to pick filter choice 'A' for menustyle:Full" }, + /* not a yes/n[o] vs y/n change nor a y/n addition */ + { PARANOID_SWIM, "swim", 1, 0, 0, + "'m' prefix necessary to deliberately walk into lava or water" }, { PARANOID_REMOVE, "Remove", 1, "Takeoff", 1, + /* normally when there is only 1 candidate it's chosen automatically */ "always pick from inventory for Remove and Takeoff" }, - { PARANOID_SWIM, "swim", 1, 0, 0, - "avoid walking into lava or water or off a cliff" }, - { PARANOID_TRAP, "trap", 1, "move-trap", 1, - "yes vs y to move onto a trap" }, { PARANOID_THROW, "Throw", 1, 0, 0, "y to throw ammo while not wielding a corresponding launcher" }, /* for config file parsing; interactive menu skips these */ @@ -227,6 +240,24 @@ static NEARDATA const char *runmodes[] = { static NEARDATA const char *sortltype[] = { "none", "loot", "full" }; +/* second column is an alias for the first; third is brief explanation; + entries 5 and 6 are 1|4 and 2|4 (tty only) */ +static NEARDATA const char *perminv_modes[][3] = { + /*0*/ { "none", "off", "no permanent inventory window" }, + /*1*/ { "all" , "on", "all inventory except for gold" }, + /*2*/ { "full", "gold", "full inventory including gold" }, + /*3*/ { NULL, NULL, NULL }, + /*4*/ { NULL, NULL, NULL }, +#ifdef TTY_PERM_INVENT + /*5*/ { "on+grid", "all+grid", "all except gold, plus unused letters" }, + /*6*/ { "gold+grid", "full+grid", "full inventory, plus unused letters" }, +#else + /*5*/ { NULL, NULL, NULL }, + /*6*/ { NULL, NULL, NULL }, +#endif + /*7*/ { NULL, NULL, NULL }, + /*8*/ { "in-use", "inuse-only", "subset: items currently in use" }, +}; /* * Default menu manipulation command accelerators. These may _not_ be: @@ -234,7 +265,7 @@ static NEARDATA const char *sortltype[] = { * + a number or '#' - reserved for counts * + an upper or lower case US ASCII letter - used for accelerators * + ESC - reserved for escaping the menu - * + NULL, CR or LF - reserved for commiting the selection(s). NULL + * + NULL, CR or LF - reserved for committing the selection(s). NULL * is kind of odd, but the tty's xwaitforspace() will return it if * someone hits a . * + a default object class symbol - used for object class accelerators @@ -286,93 +317,90 @@ static const menu_cmd_t default_menu_cmd_info[] = { { (char *) 0, '\0', (char *) 0 } }; -static void nmcpy(char *, const char *, int); -static void escapes(const char *, char *); -static void rejectoption(const char *); -static char *string_for_opt(char *, boolean); -static char *string_for_env_opt(const char *, char *, boolean); -static void bad_negation(const char *, boolean); -static int change_inv_order(char *); -static boolean warning_opts(char *, const char *); -static int feature_alert_opts(char *, const char *); -static boolean duplicate_opt_detection(int); -static void complain_about_duplicate(int); -static int length_without_val(const char *, int len); -static void determine_ambiguities(void); -static int check_misc_menu_command(char *, char *); -static int opt2roleopt(int); -static char *getoptstr(int, int); -static void saveoptstr(int, const char *); -static void unsaveoptstr(int, int); -static int shared_menu_optfn(int, int, boolean, char *, char *); -static int spcfn_misc_menu_cmd(int, int, boolean, char *, char *); - -static const char *attr2attrname(int); -static void basic_menu_colors(boolean); -static const char * msgtype2name(int); -static int query_msgtype(void); -static boolean msgtype_add(int, char *); -static void free_one_msgtype(int); -static int msgtype_count(void); -static boolean test_regex_pattern(const char *, const char *); -static boolean add_menu_coloring_parsed(const char *, int, int); -static void free_one_menu_coloring(int); -static int count_menucolors(void); -static boolean parse_role_opt(int, boolean, const char *, char *, char **); -static char *get_cnf_role_opt(int); -static unsigned int longest_option_name(int, int); -static int doset_simple_menu(void); -static void doset_add_menu(winid, const char *, const char *, int, int); -static int handle_add_list_remove(const char *, int); -static void all_options_conds(strbuf_t *); -static void all_options_menucolors(strbuf_t *); -static void all_options_msgtypes(strbuf_t *); -static void all_options_apes(strbuf_t *); -static void remove_autopickup_exception(struct autopickup_exception *); -static int count_apes(void); -static int count_cond(void); -static int count_monstercolors(void); -static void enhance_menu_text(char *, size_t, int, boolean *, - struct allopt_t *); - -static int handler_align_misc(int); -static int handler_autounlock(int); -static int handler_disclose(void); -static int handler_menu_headings(void); -static int handler_menustyle(void); -static int handler_msg_window(void); -static int handler_number_pad(void); -static int handler_paranoid_confirmation(void); -static int handler_pickup_burden(void); -static int handler_pickup_types(void); -static int handler_runmode(void); -static int handler_sortloot(void); -static int handler_symset(int); -static int handler_whatis_coord(void); -static int handler_whatis_filter(void); +static const char n_currently_set[] = "(%d currently set)"; + +staticfn void nmcpy(char *, const char *, int); +staticfn void escapes(const char *, char *); +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 int change_inv_order(char *); +staticfn boolean warning_opts(char *, const char *); +staticfn int feature_alert_opts(char *, const char *); +staticfn boolean duplicate_opt_detection(int); +staticfn void complain_about_duplicate(int); +staticfn int length_without_val(const char *, int len); +staticfn void determine_ambiguities(void); +staticfn int check_misc_menu_command(char *, char *); +staticfn int opt2roleopt(int); +staticfn char *getoptstr(int, int); +staticfn void saveoptstr(int, const char *); +staticfn void unsaveoptstr(int, int); +staticfn int petname_optfn(int, int, boolean, char *, char *); +staticfn int shared_menu_optfn(int, int, boolean, char *, char *); +staticfn int spcfn_misc_menu_cmd(int, int, boolean, char *, char *); + +staticfn const char * msgtype2name(int); +staticfn int query_msgtype(void); +staticfn boolean msgtype_add(int, char *); +staticfn void free_one_msgtype(int); +staticfn int msgtype_count(void); +staticfn boolean test_regex_pattern(const char *, const char *); +staticfn boolean parse_role_opt(int, boolean, const char *, char *, char **); +staticfn char *get_cnf_role_opt(int); +staticfn unsigned int longest_option_name(int, int); +staticfn int doset_simple_menu(void); +staticfn void doset_add_menu(winid, const char *, const char *, int, int); +staticfn int handle_add_list_remove(const char *, int); +staticfn void all_options_conds(strbuf_t *); +staticfn void all_options_menucolors(strbuf_t *); +staticfn void all_options_msgtypes(strbuf_t *); +staticfn void all_options_apes(strbuf_t *); +#ifdef CHANGE_COLOR +staticfn void all_options_palette(strbuf_t *); +#endif +staticfn void remove_autopickup_exception(struct autopickup_exception *); +staticfn int count_apes(void); +staticfn int count_cond(void); +staticfn int count_monstercolors(void); +staticfn void enhance_menu_text(char *, size_t, int, boolean *, + struct allopt_t *); +staticfn boolean can_set_perm_invent(void); +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_menustyle(void); +staticfn int handler_msg_window(void); +staticfn int handler_number_pad(void); +staticfn int handler_paranoid_confirmation(void); +staticfn int handler_perminv_mode(void); +staticfn int handler_pickup_burden(void); +staticfn int handler_pickup_types(void); +staticfn int handler_runmode(void); +staticfn int handler_petattr(void); +staticfn int handler_sortloot(void); +staticfn int handler_symset(int); +staticfn int handler_versinfo(void); +staticfn int handler_whatis_coord(void); +staticfn int handler_whatis_filter(void); /* next few are not allopt[] entries, so will only be called directly from doset, not from individual optfn's */ -static int handler_autopickup_exception(void); -static int handler_menu_colors(void); -static int handler_msgtype(void); -static int handler_monstercolor(void); -#ifndef NO_VERBOSE_GRANULARITY -static int handler_verbose(int optidx); -#endif -static int handler_windowborders(void); - -static boolean is_wc_option(const char *); -static boolean wc_supported(const char *); -static boolean is_wc2_option(const char *); -static boolean wc2_supported(const char *); -static void wc_set_font_name(int, char *); -static int wc_set_window_colors(char *); -static boolean illegal_menu_cmd_key(uchar); -static const char *term_for_boolean(int, boolean *); -#ifdef CURSES_GRAPHICS -extern int curses_read_attrs(const char *attrs); -extern char *curses_fmt_attrs(char *); -#endif +staticfn int handler_autopickup_exception(void); +staticfn int handler_menu_colors(void); +staticfn int handler_msgtype(void); +staticfn int handler_monstercolor(void); +staticfn int handler_windowborders(void); + +staticfn boolean is_wc_option(const char *); +staticfn boolean wc_supported(const char *); +staticfn boolean is_wc2_option(const char *); +staticfn boolean wc2_supported(const char *); +staticfn void wc_set_font_name(int, char *); +staticfn int wc_set_window_colors(char *); +staticfn boolean illegal_menu_cmd_key(uchar); +staticfn const char *term_for_boolean(int, boolean *); /* ask user if they want a tutorial, except if tutorial boolean option has been set in config - either on or off - in which case just obey that @@ -402,20 +430,17 @@ ask_do_tutorial(void) any = cg.zeroany; any.a_char = 'y'; add_menu(win, &nul_glyphinfo, &any, any.a_char, 0, - ATR_NONE, 0, "Yes, do a tutorial", MENU_ITEMFLAGS_NONE); + ATR_NONE, NO_COLOR, + "Yes, do a tutorial", MENU_ITEMFLAGS_NONE); any.a_char = 'n'; add_menu(win, &nul_glyphinfo, &any, any.a_char, 0, - ATR_NONE, 0, "No, just start play", MENU_ITEMFLAGS_NONE); + ATR_NONE, NO_COLOR, + "No, just start play", MENU_ITEMFLAGS_NONE); - any = cg.zeroany; - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, 0, "", MENU_ITEMFLAGS_NONE); - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, 0, buf, MENU_ITEMFLAGS_NONE); + add_menu_str(win, ""); + add_menu_str(win, buf); if (pass++) /* we'll get here after or */ - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, 0, "(Please choose 'y' or 'n'.)", - MENU_ITEMFLAGS_NONE); + add_menu_str(win, "(Please choose 'y' or 'n'.)"); end_menu(win, "Do you want a tutorial?"); @@ -441,7 +466,7 @@ ask_do_tutorial(void) */ boolean parseoptions( - register char *opts, + char *opts, boolean tinitial, boolean tfrom_file) { @@ -457,8 +482,19 @@ parseoptions( using_alias = FALSE; go.opt_initial = tinitial; go.opt_from_file = tfrom_file; - if ((op = strchr(opts, ',')) != 0) { + /* + * Process elements of comma-separated list in right to left order. + * When some options are set interactively--notably various compound + * options that issue a prompt for a value--they use parseoptions() + * to handle setting the new value. For those, 'tinitial' is False + * and if user tries to supply a comma-separated list, it will be + * treated as part of the current option, probably failing to parse. + */ + if (tinitial && (op = strchr(opts, ',')) != 0) { *op++ = 0; + /* current element remains pending while the rest of the line gets + handled recursively; if the rest of line contains any commas, + then the process will recurse deeper as it is processed */ if (!parseoptions(op, go.opt_initial, go.opt_from_file)) retval = FALSE; } @@ -517,7 +553,7 @@ parseoptions( * determine_ambiguities() * figured out exactly how many characters are required to * unambiguously differentiate one option from all others, and it - * placed that number into each option's alloption[n].minmatch. + * placed that number into each option's allopt[n].minmatch. * */ if (!got_match) @@ -557,7 +593,7 @@ parseoptions( } /* allow optfn's to test whether they were called from parseoptions() */ - gp.program_state.in_parseoptions++; + program_state.in_parseoptions++; if (got_match && matchidx >= 0) { duplicate = duplicate_opt_detection(matchidx); @@ -583,8 +619,8 @@ parseoptions( } } - if (gp.program_state.in_parseoptions > 0) - gp.program_state.in_parseoptions--; + if (program_state.in_parseoptions > 0) + program_state.in_parseoptions--; #if 0 /* This specialization shouldn't be needed any longer because each of @@ -614,12 +650,10 @@ parseoptions( if (pfx_match && optresult == optn_err) { char pfxbuf[BUFSZ], *pfxp; - if (opts) { - Snprintf(pfxbuf, sizeof pfxbuf, "%s", opts); - if ((pfxp = strchr(pfxbuf, ':')) != 0) - *pfxp = '\0'; - config_error_add("bad option suffix variation '%s'", pfxbuf); - } + Snprintf(pfxbuf, sizeof pfxbuf, "%s", opts); + if ((pfxp = strchr(pfxbuf, ':')) != 0) + *pfxp = '\0'; + config_error_add("bad option suffix variation '%s'", pfxbuf); return FALSE; } if (got_match && optresult == optn_err) @@ -632,7 +666,7 @@ parseoptions( return FALSE; } -static int +staticfn int check_misc_menu_command(char *opts, char *op UNUSED) { int i; @@ -653,7 +687,7 @@ static int roleopt2opt[4] = { }; /* role => 0, race => 1, gender => 2, alignment =>3 */ -static int +staticfn int opt2roleopt(int roleopt) { switch (roleopt) { @@ -672,7 +706,7 @@ opt2roleopt(int roleopt) } /* fetch saved option string for a particular option phase */ -static char * +staticfn char * getoptstr(int optidx, int ophase) { int roleoptindx = opt2roleopt(optidx); @@ -688,11 +722,15 @@ getoptstr(int optidx, int ophase) break; } } - return roleoptvals[roleoptindx][ophase]; + if ((roleoptindx >= 0 && roleoptindx < MAX_ROLEOPT + && ophase >= 0 && ophase < num_opt_phases)) + return roleoptvals[roleoptindx][ophase]; + panic("bad index roleoptvals[%d][%d]", roleoptindx, ophase); + /*NOTREACHED*/ } /* to track some unparsed option settings in case #saveoptions needs them */ -static void +staticfn void saveoptstr(int optidx, const char *optstr) { int phase = go.opt_phase, roleoptindx = opt2roleopt(optidx); @@ -710,7 +748,7 @@ saveoptstr(int optidx, const char *optstr) } /* discard specific saved option string */ -static void +staticfn void unsaveoptstr(int optidx, int ophase) { int roleoptindx = opt2roleopt(optidx); @@ -783,6 +821,36 @@ restoptvals(NHFILE *nhfp) #endif /* 0 */ +/* common to optfn_catname(), optfn_dogname(), optfn_horsename() */ +staticfn int +petname_optfn( + int optidx, int req, + boolean negated, + char *opts, char *op) +{ + char failsafe[PL_PSIZ + 1]; + char *petname = (optidx == opt_catname) ? gc.catname + : (optidx == opt_dogname) ? gd.dogname + : (optidx == opt_horsename) ? gh.horsename + : failsafe; + + if (req == do_init) { + ; + } else if (req == do_set) { + if (op == empty_optstr && !negated) + return optn_err; + if (negated || !strcmp(op, "none") || !strcmp(op, none)) + op = empty_optstr; + nmcpy(petname, op, PL_PSIZ); + sanitize_name(petname); + } else if (req == get_val || req == get_cnf_val) { + failsafe[0] = '\0'; + Sprintf(opts, "%s", *petname ? petname + : (req == get_cnf_val) ? "none" : none); + } + return optn_ok; +} + /* ********************************** * @@ -791,7 +859,7 @@ restoptvals(NHFILE *nhfp) ********************************** */ -static int +staticfn int optfn_alignment( int optidx, int req, @@ -817,8 +885,6 @@ optfn_alignment( return optn_ok; } if (req == get_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", rolestring(flags.initalign, aligns, adj)); return optn_ok; } @@ -831,7 +897,7 @@ optfn_alignment( } -static int +staticfn int optfn_align_message( int optidx, int req, boolean negated, char *opts, char *op) @@ -866,8 +932,6 @@ optfn_align_message( if (req == get_val || req == get_cnf_val) { int which; - if (!opts) - return optn_err; which = iflags.wc_align_message; Sprintf(opts, "%s", (which == ALIGN_TOP) ? "top" @@ -883,8 +947,10 @@ optfn_align_message( return optn_ok; } -static int -optfn_align_status(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_align_status( + int optidx, int req, boolean negated, + char *opts, char *op) { if (req == do_init) { return optn_ok; @@ -915,8 +981,6 @@ optfn_align_status(int optidx, int req, boolean negated, char *opts, char *op) if (req == get_val || req == get_cnf_val) { int which; - if (!opts) - return optn_err; which = iflags.wc_align_status; Sprintf(opts, "%s", (which == ALIGN_TOP) ? "top" @@ -932,7 +996,7 @@ optfn_align_status(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } -static int +staticfn int optfn_altkeyhandling( int optidx UNUSED, int req, @@ -957,8 +1021,6 @@ optfn_altkeyhandling( return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; #ifdef WIN32 Sprintf(opts, "%s", @@ -978,7 +1040,7 @@ optfn_altkeyhandling( return optn_ok; } -static int +staticfn int optfn_autounlock( int optidx, int req, @@ -1059,8 +1121,6 @@ optfn_autounlock( return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; if (!flags.autounlock) { Strcpy(opts, "none"); } else { @@ -1085,9 +1145,10 @@ optfn_autounlock( return optn_ok; } -static int -optfn_boulder(int optidx UNUSED, int req, boolean negated UNUSED, - char *opts, char *op UNUSED) +staticfn int +optfn_boulder( + int optidx UNUSED, int req, boolean negated UNUSED, + char *opts, char *op UNUSED) { #ifdef BACKWARD_COMPAT int clash = 0; @@ -1147,8 +1208,6 @@ optfn_boulder(int optidx UNUSED, int req, boolean negated UNUSED, #endif } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; #ifdef BACKWARD_COMPAT Sprintf(opts, "%c", @@ -1161,46 +1220,105 @@ optfn_boulder(int optidx UNUSED, int req, boolean negated UNUSED, return optn_ok; } -static int +staticfn int optfn_catname( - int optidx, int req, boolean negated UNUSED, + int optidx, int req, + boolean negated, + char *opts, char *op) +{ + return petname_optfn(optidx, req, negated, opts, op); +} + +#ifdef CRASHREPORT +staticfn int +optfn_crash_email( + int optidx UNUSED, int req, boolean negated UNUSED, char *opts, char *op) { if (req == do_init) { return optn_ok; } if (req == do_set) { - if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE)) + if ((op = string_for_opt(opts, FALSE)) != empty_optstr) { - nmcpy(gc.catname, op, PL_PSIZ); - } else { + gc.crash_email = dupstr(op); + } else return optn_err; - } - sanitize_name(gc.catname); return optn_ok; } - if (req == get_val) { + if (req == get_val || req == get_cnf_val) { if (!opts) return optn_err; - Sprintf(opts, "%s", gc.catname[0] ? gc.catname : none); + if (gc.crash_email) + Sprintf(opts, "%s", gc.crash_email); return optn_ok; } - if (req == get_cnf_val) { + return optn_ok; +} + +staticfn int +optfn_crash_name( + int optidx UNUSED, int req, boolean negated UNUSED, + char *opts, char *op) +{ + if (req == do_init) { + return optn_ok; + } + if (req == do_set) { + if ((op = string_for_opt(opts, FALSE)) + != empty_optstr) { + gc.crash_name = dupstr(op); + } else + return optn_err; + return optn_ok; + } + if (req == get_val || req == get_cnf_val) { if (!opts) return optn_err; - if (gc.catname[0]) - Sprintf(opts, "%s", gc.catname); - else - opts[0] = '\0'; + if (gc.crash_name) + Sprintf(opts, "%s", gc.crash_name); + return optn_ok; + } + return optn_ok; +} + +staticfn int +optfn_crash_urlmax( + int optidx UNUSED, int req, boolean negated UNUSED, + char *opts, char *op) +{ + if (req == do_init) { + return optn_ok; + } + if (req == do_set) { + if ((op = string_for_opt(opts, FALSE)) != empty_optstr) { + int temp = atoi(op); + + if (temp < 75){ + config_error_add("Invalid value %d for crash_urlmax. " + " Minimum value is 75.", temp); + return optn_err; + } + gc.crash_urlmax = temp; + } else + return optn_err; + return optn_ok; + } + if (req == get_val || req == get_cnf_val) { + if (!opts) + return optn_err; + Sprintf(opts, "%d", gc.crash_urlmax); return optn_ok; } return optn_ok; } +#endif /* CRASHREPORT */ #ifdef CURSES_GRAPHICS -static int -optfn_cursesgraphics(int optidx, int req, boolean negated, - char *opts, char *op UNUSED) +staticfn int +optfn_cursesgraphics( + int optidx, int req, boolean negated, + char *opts, char *op UNUSED) { #ifdef BACKWARD_COMPAT boolean badflag = FALSE; @@ -1239,8 +1357,6 @@ optfn_cursesgraphics(int optidx, int req, boolean negated, #endif } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; return optn_ok; } @@ -1248,9 +1364,10 @@ optfn_cursesgraphics(int optidx, int req, boolean negated, } #endif -static int -optfn_DECgraphics(int optidx, int req, boolean negated, - char *opts, char *op UNUSED) +staticfn int +optfn_DECgraphics( + int optidx, int req, boolean negated, + char *opts, char *op UNUSED) { #ifdef BACKWARD_COMPAT boolean badflag = FALSE; @@ -1289,16 +1406,16 @@ optfn_DECgraphics(int optidx, int req, boolean negated, #endif } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; return optn_ok; } return optn_ok; } -static int -optfn_disclose(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_disclose( + int optidx, int req, boolean negated, + char *opts, char *op) { int i, idx, prefix_val; unsigned num; @@ -1361,7 +1478,7 @@ optfn_disclose(int optidx, int req, boolean negated, char *opts, char *op) DISCLOSE_NO_WITHOUT_PROMPT, DISCLOSE_SPECIAL_WITHOUT_PROMPT, '\0' }; - register char c, *dop; + char c, *dop; c = lowc(*op); if (c == 'k') @@ -1400,9 +1517,6 @@ optfn_disclose(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; - opts[0] = '\0'; for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) { if (i) @@ -1418,34 +1532,19 @@ optfn_disclose(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } -static int -optfn_dogname(int optidx UNUSED, int req, boolean negated UNUSED, - char *opts, char *op) +staticfn int +optfn_dogname( + int optidx, int req, + boolean negated, + char *opts, char *op) { - if (req == do_init) { - return optn_ok; - } - if (req == do_set) { - if (op != empty_optstr) { - nmcpy(gd.dogname, op, PL_PSIZ); - } else { - return optn_err; - } - sanitize_name(gd.dogname); - return optn_ok; - } - if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; - Sprintf(opts, "%s", gd.dogname[0] ? gd.dogname : none); - return optn_ok; - } - return optn_ok; + return petname_optfn(optidx, req, negated, opts, op); } -static int -optfn_dungeon(int optidx UNUSED, int req, boolean negated UNUSED, - char *opts, char *op UNUSED) +staticfn int +optfn_dungeon( + int optidx UNUSED, int req, boolean negated UNUSED, + char *opts, char *op UNUSED) { if (req == do_init) { return optn_ok; @@ -1454,23 +1553,20 @@ optfn_dungeon(int optidx UNUSED, int req, boolean negated UNUSED, return optn_ok; } if (req == get_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", to_be_done); return optn_ok; } if (req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; return optn_ok; } return optn_ok; } -static int -optfn_effects(int optidx UNUSED, int req, boolean negated UNUSED, - char *opts, char *op UNUSED) +staticfn int +optfn_effects( + int optidx UNUSED, int req, boolean negated UNUSED, + char *opts, char *op UNUSED) { if (req == do_init) { return optn_ok; @@ -1479,42 +1575,44 @@ optfn_effects(int optidx UNUSED, int req, boolean negated UNUSED, return optn_ok; } if (req == get_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", to_be_done); return optn_ok; } if (req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; return optn_ok; } return optn_ok; } -static int -optfn_font_map(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_font_map( + int optidx, int req, boolean negated, + char *opts, char *op) { /* send them over to the prefix handling for font_ */ return pfxfn_font(optidx, req, negated, opts, op); } -static int -optfn_font_menu(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_font_menu( + int optidx, int req, boolean negated, + char *opts, char *op) { /* send them over to the prefix handling for font_ */ return pfxfn_font(optidx, req, negated, opts, op); } -static int -optfn_font_message(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_font_message( + int optidx, int req, boolean negated, + char *opts, char *op) { /* send them over to the prefix handling for font_ */ return pfxfn_font(optidx, req, negated, opts, op); } -static int +staticfn int optfn_font_size_map( int optidx, int req, boolean negated, char *opts, char *op) @@ -1523,7 +1621,7 @@ optfn_font_size_map( return pfxfn_font(optidx, req, negated, opts, op); } -static int +staticfn int optfn_font_size_menu( int optidx, int req, boolean negated, char *opts, char *op) @@ -1532,7 +1630,7 @@ optfn_font_size_menu( return pfxfn_font(optidx, req, negated, opts, op); } -static int +staticfn int optfn_font_size_message( int optidx, int req, boolean negated, char *opts, char *op) @@ -1541,7 +1639,7 @@ optfn_font_size_message( return pfxfn_font(optidx, req, negated, opts, op); } -static int +staticfn int optfn_font_size_status( int optidx, int req, boolean negated, char *opts, char *op) @@ -1550,7 +1648,7 @@ optfn_font_size_status( return pfxfn_font(optidx, req, negated, opts, op); } -static int +staticfn int optfn_font_size_text( int optidx, int req, boolean negated, char *opts, char *op) @@ -1559,23 +1657,28 @@ optfn_font_size_text( return pfxfn_font(optidx, req, negated, opts, op); } -static int -optfn_font_status(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_font_status( + int optidx, int req, boolean negated, + char *opts, char *op) { /* send them over to the prefix handling for font_ */ return pfxfn_font(optidx, req, negated, opts, op); } -static int -optfn_font_text(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_font_text( + int optidx, int req, boolean negated, + char *opts, char *op) { /* send them over to the prefix handling for font_ */ return pfxfn_font(optidx, req, negated, opts, op); } -static int -optfn_fruit(int optidx UNUSED, int req, boolean negated, - char *opts, char *op) +staticfn int +optfn_fruit( + int optidx UNUSED, int req, boolean negated, + char *opts, char *op) { struct fruit *forig = 0; @@ -1605,7 +1708,7 @@ optfn_fruit(int optidx UNUSED, int req, boolean negated, f = fruit_from_name(op, FALSE, &fnum); if (!f) { if (!flags.made_fruit) - forig = fruit_from_name(gp.pl_fruit, FALSE, (int *) 0); + forig = fruit_from_name(svp.pl_fruit, FALSE, (int *) 0); if (!forig && fnum >= 100) { config_error_add( @@ -1615,19 +1718,19 @@ optfn_fruit(int optidx UNUSED, int req, boolean negated, } } goodfruit: - nmcpy(gp.pl_fruit, op, PL_FSIZ); - sanitize_name(gp.pl_fruit); + nmcpy(svp.pl_fruit, op, PL_FSIZ); + sanitize_name(svp.pl_fruit); /* OBJ_NAME(objects[SLIME_MOLD]) won't work for this after initialization; it gets changed to generic "fruit" */ - if (!*gp.pl_fruit) - nmcpy(gp.pl_fruit, "slime mold", PL_FSIZ); + if (!*svp.pl_fruit) + nmcpy(svp.pl_fruit, "slime mold", PL_FSIZ); if (!go.opt_initial) { /* if 'forig' is nonNull, we replace it rather than add a new fruit; it can only be nonNull if no fruits have been created since the previous name was put in place */ - (void) fruitadd(gp.pl_fruit, forig); + (void) fruitadd(svp.pl_fruit, forig); if (give_opt_msg) - pline("Fruit is now \"%s\".", gp.pl_fruit); + pline("Fruit is now \"%s\".", svp.pl_fruit); } /* If initial, then initoptions is allowed to do it instead * of here (initoptions always has to do it even if there's @@ -1637,15 +1740,13 @@ optfn_fruit(int optidx UNUSED, int req, boolean negated, return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; - Sprintf(opts, "%s", gp.pl_fruit); + Sprintf(opts, "%s", svp.pl_fruit); return optn_ok; } return optn_ok; } -static int +staticfn int optfn_gender( int optidx, int req, @@ -1672,8 +1773,6 @@ optfn_gender( return optn_ok; } if (req == get_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", rolestring(flags.initgend, genders, adj)); return optn_ok; } @@ -1685,12 +1784,12 @@ optfn_gender( return optn_ok; } -static int -optfn_glyph(int optidx UNUSED, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_glyph( + int optidx UNUSED, int req, boolean negated, + char *opts, char *op) { -#ifdef ENHANCED_SYMBOLS int glyph; -#endif if (req == do_init) { return optn_ok; @@ -1707,28 +1806,22 @@ optfn_glyph(int optidx UNUSED, int req, boolean negated, char *opts, char *op) return optn_err; /* strip leading/trailing spaces, condense internal ones (3.6.2) */ mungspaces(op); -#ifdef ENHANCED_SYMBOLS if (!glyphrep_to_custom_map_entries(op, &glyph)) return optn_err; -#endif return optn_ok; } if (req == get_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", to_be_done); return optn_ok; } if (req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; return optn_ok; } return optn_ok; } -static int +staticfn int optfn_hilite_status( int optidx UNUSED, int req, @@ -1761,8 +1854,6 @@ optfn_hilite_status( #endif } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; #ifdef STATUS_HILITES if (req == get_val) @@ -1775,45 +1866,19 @@ optfn_hilite_status( return optn_ok; } -static int +staticfn int optfn_horsename( - int optidx, int req, boolean negated UNUSED, + int optidx, + int req, boolean negated, char *opts, char *op) { - if (req == do_init) { - return optn_ok; - } - if (req == do_set) { - if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE)) - != empty_optstr) { - nmcpy(gh.horsename, op, PL_PSIZ); - } else { - return optn_err; - } - sanitize_name(gh.horsename); - return optn_ok; - } - if (req == get_val) { - if (!opts) - return optn_err; - Sprintf(opts, "%s", gh.horsename[0] ? gh.horsename : none); - return optn_ok; - } - if (req == get_cnf_val) { - if (!opts) - return optn_err; - if (gh.horsename[0]) - Sprintf(opts, "%s", gh.horsename); - else - opts[0] = '\0'; - return optn_ok; - } - return optn_ok; + return petname_optfn(optidx, req, negated, opts, op); } -static int -optfn_IBMgraphics(int optidx, int req, boolean negated, - char *opts, char *op UNUSED) +staticfn int +optfn_IBMgraphics( + int optidx, int req, boolean negated, + char *opts, char *op UNUSED) { #ifdef BACKWARD_COMPAT const char *sym_name = allopt[optidx].name; @@ -1857,16 +1922,16 @@ optfn_IBMgraphics(int optidx, int req, boolean negated, #endif } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; return optn_ok; } return optn_ok; } -static int -optfn_map_mode(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_map_mode( + int optidx, int req, boolean negated, + char *opts, char *op) { int i; @@ -1923,8 +1988,6 @@ optfn_map_mode(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; i = iflags.wc_map_mode; Sprintf(opts, "%s", (i == MAP_MODE_TILES) ? "tiles" @@ -1947,8 +2010,8 @@ optfn_map_mode(int optidx, int req, boolean negated, char *opts, char *op) /* all the key assignment options for menu_* commands are identical but optlist.h treats them as distinct rather than sharing one */ -static int -shared_menu_optfn(int optidx UNUSED, int req, boolean negated UNUSED, +staticfn int +shared_menu_optfn(int optidx UNUSED, int req, boolean negated, char *opts, char *op) { if (req == do_init) { @@ -1962,137 +2025,156 @@ shared_menu_optfn(int optidx UNUSED, int req, boolean negated UNUSED, return spcfn_misc_menu_cmd(res, req, negated, opts, op); } if (req == get_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", to_be_done); return optn_ok; } if (req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; return optn_ok; } return optn_ok; } -static int -optfn_menu_deselect_all(int optidx, int req, boolean negated, - char *opts, char *op) +staticfn int +optfn_menu_deselect_all( + int optidx, int req, boolean negated, + char *opts, char *op) { return shared_menu_optfn(optidx, req, negated, opts, op); } -static int -optfn_menu_deselect_page(int optidx, int req, boolean negated, - char *opts, char *op) +staticfn int +optfn_menu_deselect_page( + int optidx, int req, boolean negated, + char *opts, char *op) { return shared_menu_optfn(optidx, req, negated, opts, op); } -static int -optfn_menu_first_page(int optidx, int req, boolean negated, - char *opts, char *op) +staticfn int +optfn_menu_first_page( + int optidx, int req, boolean negated, + char *opts, char *op) { return shared_menu_optfn(optidx, req, negated, opts, op); } -static int -optfn_menu_invert_all(int optidx, int req, boolean negated, - char *opts, char *op) +staticfn int +optfn_menu_invert_all( + int optidx, int req, boolean negated, + char *opts, char *op) { return shared_menu_optfn(optidx, req, negated, opts, op); } -static int -optfn_menu_invert_page(int optidx, int req, boolean negated, - char *opts, char *op) +staticfn int +optfn_menu_invert_page( + int optidx, int req, boolean negated, + char *opts, char *op) { return shared_menu_optfn(optidx, req, negated, opts, op); } -static int -optfn_menu_last_page(int optidx, int req, boolean negated, - char *opts, char *op) +staticfn int +optfn_menu_last_page( + int optidx, int req, boolean negated, + char *opts, char *op) { return shared_menu_optfn(optidx, req, negated, opts, op); } -static int -optfn_menu_next_page(int optidx , int req, boolean negated, - char *opts, char *op) +staticfn int +optfn_menu_next_page( + int optidx , int req, boolean negated, + char *opts, char *op) { return shared_menu_optfn(optidx, req, negated, opts, op); } -static int -optfn_menu_previous_page(int optidx, int req, boolean negated, - char *opts, char *op) +staticfn int +optfn_menu_previous_page( + int optidx, int req, boolean negated, + char *opts, char *op) { return shared_menu_optfn(optidx, req, negated, opts, op); } -static int -optfn_menu_search(int optidx, int req, boolean negated, - char *opts, char *op) +staticfn int +optfn_menu_search( + int optidx, int req, boolean negated, + char *opts, char *op) { return shared_menu_optfn(optidx, req, negated, opts, op); } -static int -optfn_menu_select_all(int optidx, int req, boolean negated, - char *opts, char *op) +staticfn int +optfn_menu_select_all( + int optidx, int req, boolean negated, + char *opts, char *op) { return shared_menu_optfn(optidx, req, negated, opts, op); } -static int -optfn_menu_select_page(int optidx, int req, boolean negated, - char *opts, char *op) +staticfn int +optfn_menu_select_page( + int optidx, int req, boolean negated, + char *opts, char *op) { return shared_menu_optfn(optidx, req, negated, opts, op); } -static int -optfn_menu_shift_left(int optidx, int req, boolean negated, - char *opts, char *op) +staticfn int +optfn_menu_shift_left( + int optidx, int req, boolean negated, + char *opts, char *op) { return shared_menu_optfn(optidx, req, negated, opts, op); } -static int -optfn_menu_shift_right(int optidx, int req, boolean negated, - char *opts, char *op) +staticfn int +optfn_menu_shift_right( + int optidx, int req, boolean negated, + char *opts, char *op) { return shared_menu_optfn(optidx, req, negated, opts, op); } /* end of shared key assignments for menu commands */ -static int -optfn_menu_headings(int optidx, int req, boolean negated UNUSED, - char *opts, char *op UNUSED) +staticfn int +optfn_menu_headings( + int optidx, + int req, boolean negated, + char *opts, char *op) { - int tmpattr; - if (req == do_init) { return optn_ok; } if (req == do_set) { - if ((opts = string_for_env_opt(allopt[optidx].name, opts, FALSE)) - == empty_optstr) { - return optn_err; + color_attr ca; + + if (op == empty_optstr) { + /* OPTIONS=menu_headings w/o value => no-color&inverse; + OPTIONS=!menu_headings => no-color&none */ + iflags.menu_headings.attr = negated ? ATR_NONE : ATR_INVERSE; + iflags.menu_headings.color = NO_COLOR; + return optn_ok; + } else if (negated) { /* 'op != empty_optstr' to get here */ + bad_negation(allopt[optidx].name, TRUE); + return optn_silenterr; } - tmpattr = match_str2attr(opts, TRUE); - if (tmpattr == -1) + if (!color_attr_parse_str(&ca, op)) return optn_err; - iflags.menu_headings = tmpattr; + iflags.menu_headings = ca; return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; - Sprintf(opts, "%s", attr2attrname(iflags.menu_headings)); + char ca_buf[BUFSZ]; + + Strcpy(ca_buf, color_attr_to_str(&iflags.menu_headings)); + /* change "no color" to "no-color" or "light blue" to "light-blue" */ + (void) strNsubst(ca_buf, " ", "-", 0); + Strcpy(opts, ca_buf); return optn_ok; } if (req == do_handler) { @@ -2101,9 +2183,11 @@ optfn_menu_headings(int optidx, int req, boolean negated UNUSED, return optn_ok; } -static int -optfn_menuinvertmode(int optidx, int req, boolean negated UNUSED, - char *opts, char *op) +staticfn int +optfn_menuinvertmode( + int optidx, int req, + boolean negated UNUSED, + char *opts, char *op) { if (req == do_init) { return optn_ok; @@ -2123,16 +2207,16 @@ optfn_menuinvertmode(int optidx, int req, boolean negated UNUSED, return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; Sprintf(opts, "%d", iflags.menuinvertmode); return optn_ok; } return optn_ok; } -static int -optfn_menustyle(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_menustyle( + int optidx, int req, boolean negated, + char *opts, char *op) { int tmp; boolean val_required; /* no initializer based on opts because this can be @@ -2178,8 +2262,6 @@ optfn_menustyle(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", menutype[(int) flags.menu_style][0]); return optn_ok; } @@ -2189,9 +2271,10 @@ optfn_menustyle(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } -static int -optfn_monsters(int optidx UNUSED, int req, boolean negated UNUSED, - char *opts, char *op UNUSED) +staticfn int +optfn_monsters( + int optidx UNUSED, int req, boolean negated UNUSED, + char *opts, char *op UNUSED) { if (req == do_init) { return optn_ok; @@ -2200,15 +2283,13 @@ optfn_monsters(int optidx UNUSED, int req, boolean negated UNUSED, return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; return optn_ok; } return optn_ok; } -static int +staticfn int optfn_mouse_support( int optidx, int req, boolean negated, char *opts, char *op) @@ -2257,24 +2338,21 @@ optfn_mouse_support( #undef MOUSEFIX2 int ms = iflags.wc_mouse_support; - if (!opts) - return optn_err; - if (ms >= 0 && ms <= 2) Sprintf(opts, "%s%s", mousemodes[ms][0], mousemodes[ms][1]); return optn_ok; } if (req == get_cnf_val) { - if (!opts) - return optn_err; Sprintf(opts, "%i", iflags.wc_mouse_support); return optn_ok; } return optn_ok; } -static int -optfn_msg_window(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_msg_window( + int optidx, int req, boolean negated, + char *opts, char *op) { int retval = optn_ok; #if PREV_MSGS @@ -2318,8 +2396,6 @@ optfn_msg_window(int optidx, int req, boolean negated, char *opts, char *op) return retval; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; #if PREV_MSGS tmp = iflags.prevmsg_window; @@ -2340,8 +2416,10 @@ optfn_msg_window(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } -static int -optfn_msghistory(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_msghistory( + int optidx, int req, boolean negated, + char *opts, char *op) { if (req == do_init) { return optn_ok; @@ -2358,16 +2436,16 @@ optfn_msghistory(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; Sprintf(opts, "%u", iflags.msg_history); return optn_ok; } return optn_ok; } -static int -optfn_name(int optidx, int req, boolean negated UNUSED, char *opts, char *op) +staticfn int +optfn_name( + int optidx, int req, boolean negated UNUSED, + char *opts, char *op) { if (req == do_init) { return optn_ok; @@ -2377,22 +2455,22 @@ optfn_name(int optidx, int req, boolean negated UNUSED, char *opts, char *op) if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE)) != empty_optstr) { - nmcpy(gp.plname, op, PL_NSIZ); + nmcpy(svp.plname, op, PL_NSIZ); } else return optn_err; return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; - Sprintf(opts, "%s", gp.plname); + Sprintf(opts, "%s", svp.plname); return optn_ok; } return optn_ok; } -static int -optfn_number_pad(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_number_pad( + int optidx, int req, boolean negated, + char *opts, char *op) { boolean compat; @@ -2450,8 +2528,6 @@ optfn_number_pad(int optidx, int req, boolean negated, char *opts, char *op) : (gc.Cmd.pcHack_compat ? 2 : 1)) : gc.Cmd.swap_yz ? 5 : 0; - if (!opts) - return optn_err; if (req == get_val) Strcpy(opts, numpadmodes[indx]); else { @@ -2465,9 +2541,10 @@ optfn_number_pad(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } -static int -optfn_objects(int optidx UNUSED, int req, boolean negated UNUSED, - char *opts, char *op UNUSED) +staticfn int +optfn_objects( + int optidx UNUSED, int req, boolean negated UNUSED, + char *opts, char *op UNUSED) { if (req == do_init) { return optn_ok; @@ -2476,14 +2553,10 @@ optfn_objects(int optidx UNUSED, int req, boolean negated UNUSED, return optn_ok; } if (req == get_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", to_be_done); return optn_ok; } if (req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; return optn_ok; } @@ -2520,9 +2593,10 @@ optfn_orientation(int optidx, int req, boolean negated, char *opts, char *op) } -static int -optfn_packorder(int optidx UNUSED, int req, boolean negated UNUSED, - char *opts, char *op) +staticfn int +optfn_packorder( + int optidx UNUSED, int req, boolean negated UNUSED, + char *opts, char *op) { if (req == do_init) { return optn_ok; @@ -2537,8 +2611,6 @@ optfn_packorder(int optidx UNUSED, int req, boolean negated UNUSED, if (req == get_val || req == get_cnf_val) { char ocl[MAXOCLASSES + 1]; - if (!opts) - return optn_err; oc_to_str(flags.inv_order, ocl); Sprintf(opts, "%s", ocl); return optn_ok; @@ -2547,57 +2619,78 @@ optfn_packorder(int optidx UNUSED, int req, boolean negated UNUSED, } #ifdef CHANGE_COLOR -static int + +DISABLE_WARNING_FORMAT_NONLITERAL + +staticfn int +optfn_palette( + int optidx UNUSED, int req, boolean negated UNUSED, + char *opts, char *op) +{ + if (req == do_init) { + return optn_ok; + } + if (req == do_set) { + if (op == empty_optstr) + return optn_err; + + if (match_optname(opts, "palette", 3, TRUE)) { + /* + * palette (adjust an RGB color in palette (color/R-G-B) + */ + if (!alternative_palette(op)) { + config_error_add("Error in palette parameter '%s'", op); + return optn_err; + } + if (!go.opt_initial) + go.opt_update_basic_palette = TRUE; + } + return optn_ok; + } + if (req == get_val || req == get_cnf_val) { + if (!opts) + return optn_err; + Sprintf(opts, n_currently_set, count_alt_palette()); + return optn_ok; + } + return optn_ok; +} + +#if 0 +/* old MAC OS9 code */ +staticfn int optfn_palette( int optidx UNUSED, int req, boolean negated UNUSED, char *opts, char *op) { -#ifndef WIN32 int cnt, tmp, reverse; char *pt = op; long rgb; -#endif + * Mac OS9 variant + * palette (00c/880/-fff is blue/yellow/reverse white) + */ if (req == do_init) { return optn_ok; } if (req == do_set) { if (op == empty_optstr) return optn_err; - /* - Non-WIN32 variant - palette (00c/880/-fff is blue/yellow/reverse white) - WIN32 variant - palette (adjust an RGB color in palette (color-R-G-B) - */ if (match_optname(opts, "palette", 3, TRUE) -#ifdef MAC - || match_optname(opts, "hicolor", 3, TRUE) -#endif - ) { + || match_optname(opts, "hicolor", 3, TRUE)) { int color_number, color_incr; -#ifndef WIN32 if (duplicate) complain_about_duplicate(optidx); -#endif -#ifdef MAC if (match_optname(opts, "hicolor", 3, TRUE)) { color_number = CLR_MAX + 4; /* HARDCODED inverse number */ color_incr = -1; - } else -#endif - { + } else { color_number = 0; color_incr = 1; } -#ifdef WIN32 - if (!alternative_palette(op)) { - config_error_add("Error in palette parameter '%s'", op); - return optn_err; - } -#else + /* ----------- Mac OS 9 code -------------------------*/ while (*pt && color_number >= 0) { cnt = 3; rgb = 0L; @@ -2609,21 +2702,15 @@ optfn_palette( } while (cnt-- > 0) { if (*pt && *pt != '/') { -#ifdef AMIGA - rgb <<= 4; -#else rgb <<= 8; -#endif tmp = *pt++; if (isalpha((uchar) tmp)) { tmp = (tmp + 9) & 0xf; /* Assumes ASCII... */ } else { tmp &= 0xf; /* Digits in ASCII too... */ } -#ifndef AMIGA /* Add an extra so we fill f -> ff and 0 -> 00 */ rgb += tmp << 4; -#endif rgb += tmp; } } @@ -2632,112 +2719,248 @@ optfn_palette( change_color(color_number, rgb, reverse); color_number += color_incr; } -#endif /* !WIN32 */ - if (!go.opt_initial) { - go.opt_need_redraw = TRUE; - } + + if (!go.opt_initial) + go.opt_update_basic_palette = TRUE; } return optn_ok; } if (req == get_val || req == get_cnf_val) { if (!opts) return optn_err; - opts[0] = '\0'; - Sprintf(opts, "%s", get_color_string()); + Sprintf(opts, n_currently_set, count_alt_palette()); return optn_ok; } return optn_ok; + } +#endif /* 0 */ + +RESTORE_WARNING_FORMAT_NONLITERAL + #endif /* CHANGE_COLOR */ -static int +/* for "paranoid_confirmation:foo" and alias "[!]prayconfirm" */ +staticfn int optfn_paranoid_confirmation( - int optidx, int req, boolean negated, + int optidx, int req, boolean opt_negated, char *opts, char *op) { + boolean fld_negated; int i; + /* + * Player can change required response for some prompts (quit, die, + * attack, save-bones, continue-eating, break-wand, Were-change to + * need to be "yes" instead of just 'y' keystroke to accept. + * + * For paranoid_confirm:Confirm, these prompts also need "no" + * instead of 'n' or or to reject. ( always + * works as a way to reject.) + * + * Player can add an extra prompt (pray, AutoAll) that isn't + * ordinarily there. (They ask for 'y' keystroke unless Confirm is + * also set, then they'll switch to "yes", "no".) + * + * Player can also change game's behavior. paranoid_confirm:swim + * can be used to prevent accidentally stepping into water or lava; + * player must use the 'm' movement prefix to do that intentionally. + * paranoid_confirm:Remove [with synonym parnoid_confirm:Takeoff] + * changes the 'R' and 'T' commands [which have differing criteria + * for "only one candidate item"] to prompt for inventory item to + * remove/takeoff when there is only one candidate, so allows player + * a chance to cancel at the pick-an-item prompt or menu. + */ if (req == do_init) { return optn_ok; } if (req == do_set) { - /* user can change required response for some prompts (quit, die, - hit), or add an extra prompt (pray, Remove) that isn't - ordinarily there */ - - if (strncmpi(opts, "prayconfirm", 4) != 0) { /* not prayconfirm */ - /* at present we don't complain about duplicates for this - option, but we do throw away the old settings whenever - we process a new one [clearing old flags is essential - for handling default paranoid_confirm:pray sanely] */ - flags.paranoia_bits = 0; /* clear all */ - if (negated) { - flags.paranoia_bits = 0; /* [now redundant...] */ - } else if (op != empty_optstr) { - char *pp, buf[BUFSZ]; - - strncpy(buf, op, sizeof buf - 1); - buf[sizeof buf - 1] = '\0'; - op = mungspaces(buf); - for (;;) { - /* We're looking to parse - "paranoid_confirm:whichone wheretwo whothree" - and "paranoid_confirm:" prefix has already - been stripped off by the time we get here */ - pp = strchr(op, ' '); - if (pp) - *pp = '\0'; - /* we aren't matching option names but match_optname() - does what we want once we've broken the space - delimited aggregate into separate tokens */ - for (i = 0; i < SIZE(paranoia); ++i) { - if (match_optname(op, paranoia[i].argname, - paranoia[i].argMinLen, FALSE) - || (paranoia[i].synonym - && match_optname(op, paranoia[i].synonym, - paranoia[i].synMinLen, - FALSE))) { - if (paranoia[i].flagmask) - flags.paranoia_bits |= paranoia[i].flagmask; - else /* 0 == "none", so clear all */ - flags.paranoia_bits = 0; - break; - } - } - if (i == SIZE(paranoia)) { - /* didn't match anything, so arg is bad; - any flags already set will stay set */ - config_error_add("Unknown %s parameter '%s'", - allopt[optidx].name, op); - return optn_err; + char prayconfirm[1 + sizeof "pray"]; + char *pp; + boolean plus_or_minus = FALSE; + + /* + * "prayconfirm" used to be a separate boolean option, + * now it is a synonym for paranoid_confirm:+pray and + * "!prayconfirm" has become one for paranoid_confirm:-pray. + */ + if (!strncmpi(opts, "prayconfirm", 4)) { + if (*op) { + /* presence of any value is treated as an error whether + complaining about the 'prayconfirm' deprecation or not; + this will erroneously reject "prayconfirm:true"; too + bad; back when prayconfirm was in active use, tacking on + an explicit value to a boolean option wasn't supported */ + config_error_add( + "deprecated %sprayconfirm option takes no parameters (found '%s')", + opt_negated ? "!" : "", op); + return optn_silenterr; + } +#ifdef COMPLAIN_ABOUT_PRAYCONFIRM + /* config file summary of complaints includes this in the count + of errors; we'd prefer that it be described as a warning but + that isn't supported [not important since this is considered + temporary until 'prayconfirm' gets removed altogether] */ + config_error_add( + "%sprayconfirm option is deprecated; switching to %s:%cpray", + opt_negated ? "!" : "", + allopt[optidx].name, + opt_negated ? '-' : '+'); + /* keep going */ +#endif + /* convert prayconfirm to paranoid_confirm:+pray and + !prayconfirm to paranoid_confirm:-pray */ + Sprintf(prayconfirm, "%cpray", opt_negated ? '-' : '+'); + op = prayconfirm; + /* possibly changing !prayconfirm to paranoid_confirm:-pray + which clears a paranoia bit but isn't a negated option */ + opt_negated = FALSE; + /* + * end of 'prayconfirm' processing + */ + + } else if (opt_negated) { + /* "!paranoid_confirm" w/o args is same as paranoid_confirm:none; + "!paranoid_confirm:anything" is disallowed */ + if (!*op) { + flags.paranoia_bits = 0; + return optn_ok; + } else { + config_error_add("!%s does not accept a value", + allopt[optidx].name); + return optn_silenterr; + } + } else if (!*op) { + /* "paranoid_confirm" without any arguments is disallowed */ + config_error_add("%s requires a value; use 'none' to cancel all", + allopt[optidx].name); + return optn_silenterr; + } + + /* + * Multiple settings for paranoid_confirmation are allowed. + * When a new instance is processed, the behavior depends on the + * first character of its value: + * + * paranoid_confirm:foo bar + * clears all confirmation bits (from previous settings, including + * default), then sets the bits for foo and bar; + * + * paranoid_confirm:+foo bar + * existing bits are kept, plus those for foo and bar are set; + * + * paranoid_confirm:-foo bar + * existing bits are kept except those for foo and bar get cleared; + * + * paranoid_confirm:+foo !bar + * combination of paranoid_confirm:+foo,paranoid_confirm:-bar; + * + * paranoid_confirm:-foo !bar + * the negation in '!bar' is ignored, treated as if '-foo bar'; + * + * !paranoid_confirm + * without a value is treated as paranoid_confirm:none and clears + * all bits; + * !paranoid_confirm:anything + * (including +anything_else or -anything_else) is disallowed; + * + * paranoid_confirm:+all is the same as paranoid_confirm:all; + * paranoid_confirm:-all is the same as paranoid_confirm:none; + * paranoid_confirm:+none and paranoid_confirm:-none are no-ops. + */ + (void) mungspaces(op); + if (*op != '+' && *op != '-') { + /* new value; first clear all old bits */ + flags.paranoia_bits = 0; + } else { + /* augmenting existing value; keep old bits */ + plus_or_minus = TRUE; /* only used for "+none" and "-none" */ + opt_negated = (*op == '-'); /* context is changed */ + if (*++op == ' ') /* skip '+' or '-', maybe whitespace */ + ++op; + } + + for (;;) { + fld_negated = (*op == '!'); + if (fld_negated) { + /* there shouldn't be a space after '!' because then + "! foo bar" looks like it might be intended to mean + "!foo !bar" but if there is one, skip it to prevent + a lookup attempt for "" which will fail and result in + an unhelpful error message; accepting the space is + simpler than another special case error message */ + if (*++op == ' ') /* skip '!', maybe whitespace */ + ++op; + } else { + /* accept "nofoo" to be same as "!foo", unless "no" is + followed by a space or 'foo' begins with "n" (to avoid + confusion for "none" */ + if (lowc(op[0]) == 'n' && lowc(op[1]) == 'o' + && lowc(op[2] != 'n' && lowc(op[2]) != '\0')) { + fld_negated = TRUE; + op += 2; /* skip "no"; we know next char isn't space */ + } + } + /* We're looking to parse + "paranoid_confirm:whichone wheretwo whothree" + and "paranoid_confirm:" prefix has already + been stripped off by the time we get here */ + pp = strchr(op, ' '); + if (pp) + *pp = '\0'; + /* we aren't matching option names but match_optname() + does what we want once we've broken the space + delimited aggregate into separate tokens */ + for (i = 0; i < SIZE(paranoia); ++i) { + if (match_optname(op, paranoia[i].argname, + paranoia[i].argMinLen, FALSE) + || (paranoia[i].synonym + && match_optname(op, paranoia[i].synonym, + paranoia[i].synMinLen, FALSE))) { + if (!paranoia[i].flagmask) { + /* flagmask==0 is "none", clear all bits + but "+none" and "-none" are no-ops */ + if (!plus_or_minus) + flags.paranoia_bits = 0; /* clear all */ + } else if (opt_negated || fld_negated) { + flags.paranoia_bits &= ~paranoia[i].flagmask; + } else { + flags.paranoia_bits |= paranoia[i].flagmask; } - /* move on to next token */ - if (pp) - op = pp + 1; - else - break; /* no next token */ - } /* for(;;) */ - } else - return optn_err; - return optn_ok; - } else { /* prayconfirm */ - if (negated) - flags.paranoia_bits &= ~PARANOID_PRAY; + break; + } + } + if (i == SIZE(paranoia)) { + /* didn't match anything, so arg is bad; + any flags already modified will stay modified */ + config_error_add("Unknown %s parameter '%s'", + allopt[optidx].name, op); + return optn_silenterr; + } + /* move on to next token */ + if (pp) + op = pp + 1; else - flags.paranoia_bits |= PARANOID_PRAY; - } + break; /* no next token */ + } /* for(;;) */ return optn_ok; } if (req == get_val || req == get_cnf_val) { - char tmpbuf[QBUFSZ]; + char tmpbuf[BUFSZ]; - if (!opts) - return optn_err; tmpbuf[0] = '\0'; - for (i = 0; paranoia[i].flagmask != 0; ++i) - if (flags.paranoia_bits & paranoia[i].flagmask) - Sprintf(eos(tmpbuf), " %s", paranoia[i].argname); - Strcpy(opts, tmpbuf[0] ? &tmpbuf[1] : "none"); + for (i = 0; paranoia[i].flagmask != 0; ++i) { + if ((flags.paranoia_bits & paranoia[i].flagmask) != 0 + /* hide paranoid_confirm:bones during play except for wizard + mode; keep it for any mode if rewriting the config file */ + && (paranoia[i].flagmask != PARANOID_BONES + || wizard || req == get_cnf_val)) + Snprintf(eos(tmpbuf), sizeof tmpbuf - strlen(tmpbuf), + " %s", paranoia[i].argname); + } + /* note: always leaves enough room for caller to tack on '\n' */ + opts[0] = '\0'; + (void) strncat(opts, tmpbuf[0] ? &tmpbuf[1] : "none", BUFSZ - 1); return optn_ok; } if (req == do_handler) { @@ -2746,8 +2969,102 @@ optfn_paranoid_confirmation( return optn_ok; } -static int -optfn_petattr(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_perminv_mode( + int optidx, int req, boolean negated, + char *opts, char *op) +{ + boolean old_perm_invent = iflags.perm_invent; + uchar old_perminv_mode = iflags.perminv_mode; + int retval = optn_ok; + + if (req == do_init) { +#if 0 + /* old, TEMPORARY method of controlling 'perm_invent'; + note: the bits used now have been changed, hence 'n << 1' */ + char *envtmp = nh_getenv("TTYINV") : 0; + int invmode = envtmp ? (atoi(envtmp) << 1) : 0; + + iflags.perminv_mode = (uchar) invmode; + iflags.perm_invent = iflags.perminv_mode != 0; +#endif + return optn_ok; + } else if (req == do_set) { + op = string_for_opt(opts, negated); + if (op != empty_optstr && negated) { /* reject "!perminv_mode=foo" */ + bad_negation(allopt[optidx].name, TRUE); + retval = optn_silenterr; + } else if (op != empty_optstr) { /* "perminv_mode=foo" */ + const char *pi0, *pi1; + int i; + unsigned ln = (unsigned) strlen(op); /* guaranteed > 0 */ + + for (i = 0; i < SIZE(perminv_modes); ++i) { + if (!(pi0 = perminv_modes[i][0])) + continue; + pi1 = perminv_modes[i][1]; + if (!strncmpi(op, pi0, ln) || !strncmpi(op, pi1, ln) + || op[0] == i + '0') { /* also accept '0'..'8' */ +#if 1 /*#ifdef TTY_PERM_INVENT*/ + if (strstri(pi0, "+grid") && !WINDOWPORT(tty)) { + i &= ~InvSparse; + config_error_add( + "%s: unavailable perm_invent mode '%s', using '%s'", + allopt[optidx].name, pi0, + perminv_modes[i][0]); + } +#endif + iflags.perminv_mode = (uchar) i; + iflags.perm_invent = TRUE; + break; + } + } + if (i == SIZE(perminv_modes)) { + config_error_add("Unknown %s parameter '%s'", + allopt[optidx].name, op); + iflags.perminv_mode = InvOptNone; + iflags.perm_invent = FALSE; + retval = optn_silenterr; + } + } else if (negated) { /* "!perminv_mode" */ + iflags.perminv_mode = InvOptNone; + iflags.perm_invent = FALSE; + } + if (!go.opt_initial) { + if (iflags.perminv_mode != old_perminv_mode + || iflags.perm_invent != old_perm_invent) + go.opt_need_redraw = TRUE; + } + } else if (req == do_handler) { + /* use a menu to choose new value for perminv_mode */ + retval = handler_perminv_mode(); + } else if (req == get_val) { + /* value shown when examining current option settings; enclosed + within square brackets for 'O', shown as-is when setting value */ + Sprintf(opts, "%s", perminv_modes[iflags.perminv_mode][2]); + if (iflags.perminv_mode != InvOptNone && !iflags.perm_invent + /* 'op' is Null when called by handler_perminv_mode() while + setting, non-Null when 'm O' shows current option values */ + && op) { + /* perminv_mode is set but isn't useful because perm_invent is + Off; say so after squeezing out enough for it to barely fit */ + if (iflags.perminv_mode == InvOptInUse) + (void) strsubst(opts, " currently", ""); + else + (void) strsubst(opts, " inventory", " invent"); + Strcat(opts, (((iflags.perminv_mode & InvSparse) != 0) ? " (Off)" + : " ('perm_invent' is Off)")); + } + } else if (req == get_cnf_val) { + Sprintf(opts, "%s", perminv_modes[iflags.perminv_mode][0]); + } + return retval; +} + +staticfn int +optfn_petattr( + int optidx, int req, boolean negated, + char *opts, char *op) { int retval = optn_ok; @@ -2762,8 +3079,8 @@ optfn_petattr(int optidx, int req, boolean negated, char *opts, char *op) bad_negation(allopt[optidx].name, TRUE); retval = optn_err; } else if (op != empty_optstr) { -#ifdef CURSES_GRAPHICS - int itmp = curses_read_attrs(op); +#if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS) + int itmp = match_str2attr(op, FALSE); if (itmp == -1) { config_error_add("Unknown %s parameter '%s'", @@ -2772,9 +3089,6 @@ optfn_petattr(int optidx, int req, boolean negated, char *opts, char *op) } else iflags.wc2_petattr = itmp; #else - /* non-curses windowports will not use this flag anyway - * but the above will not compile if we don't have curses. - * Just set it to a sensible default: */ iflags.wc2_petattr = ATR_INVERSE; #endif } else if (negated) { @@ -2788,14 +3102,9 @@ optfn_petattr(int optidx, int req, boolean negated, char *opts, char *op) return retval; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; - -#ifdef CURSES_GRAPHICS - if (WINDOWPORT(curses)) { - char tmpbuf[QBUFSZ]; - - Strcpy(opts, curses_fmt_attrs(tmpbuf)); +#if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS) + if (WINDOWPORT(tty) || WINDOWPORT(curses)) { + Strcpy(opts, attr2attrname(iflags.wc2_petattr)); } else #endif if (iflags.wc2_petattr != 0) @@ -2805,11 +3114,16 @@ optfn_petattr(int optidx, int req, boolean negated, char *opts, char *op) else Strcpy(opts, defopt); } + if (req == do_handler) { + return handler_petattr(); + } return optn_ok; } -static int -optfn_pettype(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_pettype( + int optidx, int req, boolean negated, + char *opts, char *op) { if (req == do_init) { return optn_ok; @@ -2848,8 +3162,6 @@ optfn_pettype(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } if (req == get_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", (gp.preferred_pet == 'c') ? "cat" : (gp.preferred_pet == 'd') ? "dog" : (gp.preferred_pet == 'h') ? "horse" @@ -2858,8 +3170,6 @@ optfn_pettype(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } if (req == get_cnf_val) { - if (!opts) - return optn_err; if (gp.preferred_pet) Sprintf(opts, "%c", gp.preferred_pet); else @@ -2869,7 +3179,7 @@ optfn_pettype(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } -static int +staticfn int optfn_pickup_burden( int optidx, int req, boolean negated UNUSED, char *opts, char *op) @@ -2912,8 +3222,6 @@ optfn_pickup_burden( return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", burdentype[flags.pickup_burden]); return optn_ok; } @@ -2923,8 +3231,10 @@ optfn_pickup_burden( return optn_ok; } -static int -optfn_pickup_types(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_pickup_types( + int optidx, int req, boolean negated, + char *opts, char *op) { char ocl[MAXOCLASSES + 1], tbuf[MAXOCLASSES + 1], qbuf[QBUFSZ], abuf[BUFSZ]; @@ -2968,14 +3278,14 @@ optfn_pickup_types(int optidx, int req, boolean negated, char *opts, char *op) op = tbuf; /* restore */ else if (abuf[0] == 'm') use_menu = TRUE; - /* note: abuf[0]=='a' is already handled via clearing the + /* note: abuf[0]=='a' is already handled via clearing the old value (above) as a default action */ } if (use_menu) { if (wizard && !strchr(ocl, VENOM_SYM)) strkitten(ocl, VENOM_SYM); - (void) choose_classes_menu("Autopickup what?", 1, TRUE, ocl, - tbuf); + (void) choose_classes_menu("Autopickup what?", + 1, TRUE, ocl, tbuf); op = tbuf; } } @@ -3007,8 +3317,6 @@ optfn_pickup_types(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; oc_to_str(flags.pickup_types, ocl); Sprintf(opts, "%s", ocl[0] ? ocl : "all"); return optn_ok; @@ -3019,8 +3327,10 @@ optfn_pickup_types(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } -static int -optfn_pile_limit(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_pile_limit( + int optidx, int req, boolean negated, + char *opts, char *op) { if (req == do_init) { return optn_ok; @@ -3045,15 +3355,13 @@ optfn_pile_limit(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; Sprintf(opts, "%d", flags.pile_limit); return optn_ok; } return optn_ok; } -static int +staticfn int optfn_player_selection( int optidx, int req, boolean negated, char *opts, char *op) @@ -3079,8 +3387,6 @@ optfn_player_selection( return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", iflags.wc_player_selection ? "prompts" : "dialog"); return optn_ok; @@ -3088,8 +3394,10 @@ optfn_player_selection( return optn_ok; } -static int -optfn_playmode(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_playmode( + int optidx, int req, boolean negated, + char *opts, char *op) { if (req == do_init) { return optn_ok; @@ -3116,8 +3424,6 @@ optfn_playmode(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; Strcpy(opts, wizard ? "debug" : discover ? "explore" : "normal"); return optn_ok; } @@ -3160,7 +3466,7 @@ optfn_polyinit(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } -static int +staticfn int optfn_race( int optidx, int req, @@ -3187,8 +3493,6 @@ optfn_race( return optn_ok; } if (req == get_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", rolestring(flags.initrace, races, noun)); return optn_ok; } @@ -3200,7 +3504,7 @@ optfn_race( return optn_ok; } -static int +staticfn int optfn_role( int optidx, int req, @@ -3221,14 +3525,12 @@ optfn_role( config_error_add("Unknown %s '%s'", allopt[optidx].name, op); return optn_err; } - nmcpy(gp.pl_character, op, PL_NSIZ); /* Backwards compat */ + nmcpy(svp.pl_character, op, PL_NSIZ); /* Backwards compat */ saveoptstr(optidx, rolestring(flags.initrole, roles, name.m)); } return optn_ok; } if (req == get_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", rolestring(flags.initrole, roles, name.m)); return optn_ok; } @@ -3240,8 +3542,10 @@ optfn_role( return optn_ok; } -static int -optfn_runmode(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_runmode( + int optidx, int req, boolean negated, + char *opts, char *op) { if (req == do_init) { return optn_ok; @@ -3264,14 +3568,13 @@ optfn_runmode(int optidx, int req, boolean negated, char *opts, char *op) return optn_err; } } else { - config_error_add("Value is mandatory for %s", allopt[optidx].name); + config_error_add("Value is mandatory for %s", + allopt[optidx].name); return optn_err; } return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", runmodes[flags.runmode]); return optn_ok; } @@ -3281,8 +3584,10 @@ optfn_runmode(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } -static int -optfn_scores(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_scores( + int optidx, int req, boolean negated, + char *opts, char *op) { if (req == do_init) { return optn_ok; @@ -3338,6 +3643,7 @@ optfn_scores(int optidx, int req, boolean negated, char *opts, char *op) allopt[optidx].name); return optn_silenterr; } + FALLTHROUGH; /*FALLTHRU*/ default: config_error_add("Unknown %s parameter '%s'", @@ -3356,8 +3662,6 @@ optfn_scores(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; *opts = '\0'; if (flags.end_top > 0) Sprintf(opts, "%d top", flags.end_top); @@ -3374,7 +3678,7 @@ optfn_scores(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } -static int +staticfn int optfn_scroll_amount( int optidx, int req, boolean negated, char *opts, char *op) @@ -3396,8 +3700,6 @@ optfn_scroll_amount( return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; if (iflags.wc_scroll_amount) Sprintf(opts, "%d", iflags.wc_scroll_amount); else @@ -3407,7 +3709,7 @@ optfn_scroll_amount( return optn_ok; } -static int +staticfn int optfn_scroll_margin( int optidx, int req, boolean negated, char *opts, char *op) @@ -3428,8 +3730,6 @@ optfn_scroll_margin( return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; if (iflags.wc_scroll_margin) Sprintf(opts, "%d", iflags.wc_scroll_margin); else @@ -3439,9 +3739,10 @@ optfn_scroll_margin( return optn_ok; } -static int -optfn_soundlib(int optidx, int req, boolean negated UNUSED, - char *opts, char *op) +staticfn int +optfn_soundlib( + int optidx, int req, boolean negated UNUSED, + char *opts, char *op) { char soundlibbuf[WINTYPELEN]; @@ -3459,16 +3760,17 @@ optfn_soundlib(int optidx, int req, boolean negated UNUSED, */ 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; return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; get_soundlib_name(soundlibbuf, WINTYPELEN); Sprintf(opts, "%s", soundlibbuf); return optn_ok; @@ -3476,7 +3778,7 @@ optfn_soundlib(int optidx, int req, boolean negated UNUSED, return optn_ok; } -static int +staticfn int optfn_sortdiscoveries( int optidx, int req, boolean negated, char *opts, char *op) @@ -3517,16 +3819,7 @@ optfn_sortdiscoveries( return optn_ok; } if (req == get_val || req == get_cnf_val) { - extern const char *const disco_orders_descr[]; /* o_init.c */ - extern const char disco_order_let[]; - const char *p = strchr(disco_order_let, flags.discosort); - - if (!p) - flags.discosort = 'o', p = disco_order_let; - if (req == get_cnf_val) - Sprintf(opts, "%c", flags.discosort); - else - Strcpy(opts, disco_orders_descr[p - disco_order_let]); + get_sortdisco(opts, (req == get_cnf_val) ? TRUE : FALSE); return optn_ok; } if (req == do_handler) { @@ -3536,10 +3829,9 @@ optfn_sortdiscoveries( return optn_ok; } -static int +staticfn int optfn_sortloot( - int optidx, int req, - boolean negated UNUSED, + int optidx, int req, boolean negated UNUSED, char *opts, char *op) { int i; @@ -3568,8 +3860,6 @@ optfn_sortloot( return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; for (i = 0; i < SIZE(sortltype); i++) if (flags.sortloot == sortltype[i][0]) { Strcpy(opts, sortltype[i]); @@ -3583,10 +3873,9 @@ optfn_sortloot( return optn_ok; } -static int +staticfn int optfn_sortvanquished( - int optidx, int req, - boolean negated, + int optidx, int req, boolean negated, char *opts, char *op) { extern const char *const vanqorders[][3]; /* insight.c */ @@ -3639,13 +3928,10 @@ optfn_sortvanquished( return optn_ok; } -static int +staticfn int optfn_statushilites( - int optidx UNUSED, - int req, - boolean negated, - char *opts, - char *op) + int optidx UNUSED, int req, boolean negated, + char *opts, char *op) { if (req == do_init) { return optn_ok; @@ -3675,8 +3961,6 @@ optfn_statushilites( #endif } if (req == get_val) { - if (!opts) - return optn_err; #ifdef STATUS_HILITES if (!iflags.hilite_delta) Strcpy(opts, "0 (off: don't highlight status fields)"); @@ -3690,16 +3974,18 @@ optfn_statushilites( } if (req == get_cnf_val) { #ifdef STATUS_HILITES - if (!opts) - return optn_err; Sprintf(opts, "%ld", iflags.hilite_delta); +#else + opts[0] = '\0'; #endif } return optn_ok; } -static int -optfn_statuslines(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_statuslines( + int optidx, int req, boolean negated, + char *opts, char *op) { int retval = optn_ok, itmp = 0; @@ -3730,8 +4016,6 @@ optfn_statuslines(int optidx, int req, boolean negated, char *opts, char *op) return retval; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; if (wc2_supported(allopt[optidx].name)) Strcpy(opts, (iflags.wc2_statuslines < 3) ? "2" : "3"); else @@ -3742,9 +4026,10 @@ optfn_statuslines(int optidx, int req, boolean negated, char *opts, char *op) } #ifdef WIN32CON -static int -optfn_subkeyvalue(int optidx UNUSED, int req, boolean negated UNUSED, - char *opts, char *op UNUSED) +staticfn int +optfn_subkeyvalue( + int optidx UNUSED, int req, boolean negated UNUSED, + char *opts, char *op UNUSED) { if (req == do_init) { return optn_ok; @@ -3758,8 +4043,6 @@ optfn_subkeyvalue(int optidx UNUSED, int req, boolean negated UNUSED, return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; return optn_ok; } @@ -3767,9 +4050,10 @@ optfn_subkeyvalue(int optidx UNUSED, int req, boolean negated UNUSED, } #endif /* WIN32CON */ -static int -optfn_suppress_alert(int optidx, int req, boolean negated, - char *opts, char *op) +staticfn int +optfn_suppress_alert( + int optidx, int req, boolean negated, + char *opts, char *op) { if (req == do_init) { return optn_ok; @@ -3783,8 +4067,6 @@ optfn_suppress_alert(int optidx, int req, boolean negated, return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; if (req == get_cnf_val && flags.suppress_alert == 0L) opts[0] = '\0'; else if (flags.suppress_alert == 0L) @@ -3800,19 +4082,19 @@ optfn_suppress_alert(int optidx, int req, boolean negated, extern const char *const known_handling[]; /* symbols.c */ extern const char *const known_restrictions[]; /* symbols.c */ -static int +staticfn int optfn_symset( - int optidx UNUSED, - int req, - boolean negated UNUSED, - char *opts, - char *op) + int optidx, int req, boolean negated UNUSED, + char *opts, char *op) { if (req == do_init) { return optn_ok; } 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); @@ -3830,17 +4112,16 @@ optfn_symset( load_symset("default", PRIMARYSET); } #endif - switch_symbols(gs.symset[PRIMARYSET].name != (char *) 0); - go.opt_need_redraw = go.opt_need_glyph_reset = TRUE; } + switch_symbols(gs.symset[PRIMARYSET].name != (char *) 0); + go.opt_need_redraw = go.opt_need_glyph_reset = TRUE; + go.opt_symset_changed = TRUE; } } else return optn_err; return optn_ok; } if (req == get_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", gs.symset[PRIMARYSET].name ? gs.symset[PRIMARYSET].name : "default"); @@ -3853,8 +4134,6 @@ optfn_symset( return optn_ok; } if (req == get_cnf_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", gs.symset[PRIMARYSET].name ? gs.symset[PRIMARYSET].name : "default"); @@ -3863,26 +4142,22 @@ optfn_symset( if (req == do_handler) { int reslt; - if (gs.symset[PRIMARYSET].handling == H_UTF8) { -#ifdef ENHANCED_SYMBOLS - if (!glyphid_cache_status()) - fill_glyphid_cache(); -#endif - } + if (!glyphid_cache_status()) + fill_glyphid_cache(); reslt = handler_symset(optidx); - if (gs.symset[PRIMARYSET].handling == H_UTF8) { -#ifdef ENHANCED_SYMBOLS - if (glyphid_cache_status()) - free_glyphid_cache(); -#endif - } + if (glyphid_cache_status()) + free_glyphid_cache(); + /* apply_customizations(gc.currentgraphics, + (do_custom_colors | do_custom_symbols)); */ return reslt; } return optn_ok; } -static int -optfn_term_cols(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_term_cols( + int optidx, int req, boolean negated, + char *opts, char *op) { int retval = optn_ok; long ltmp; @@ -3909,8 +4184,6 @@ optfn_term_cols(int optidx, int req, boolean negated, char *opts, char *op) return retval; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; if (iflags.wc2_term_cols) Sprintf(opts, "%d", iflags.wc2_term_cols); else if (req == get_cnf_val) @@ -3922,8 +4195,10 @@ optfn_term_cols(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } -static int -optfn_term_rows(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_term_rows( + int optidx, int req, boolean negated, + char *opts, char *op) { int retval = optn_ok; long ltmp; @@ -3950,8 +4225,6 @@ optfn_term_rows(int optidx, int req, boolean negated, char *opts, char *op) return retval; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; if (iflags.wc2_term_rows) Sprintf(opts, "%d", iflags.wc2_term_rows); else if (req == get_cnf_val) @@ -3963,9 +4236,10 @@ optfn_term_rows(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } -static int -optfn_tile_file(int optidx UNUSED, int req, boolean negated UNUSED, - char *opts, char *op) +staticfn int +optfn_tile_file( + int optidx UNUSED, int req, boolean negated UNUSED, + char *opts, char *op) { if (req == do_init) { return optn_ok; @@ -3981,15 +4255,11 @@ optfn_tile_file(int optidx UNUSED, int req, boolean negated UNUSED, return optn_ok; } if (req == get_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", iflags.wc_tile_file ? iflags.wc_tile_file : defopt); return optn_ok; } if (req == get_cnf_val) { - if (!opts) - return optn_err; if (iflags.wc_tile_file) Sprintf(opts, "%s", iflags.wc_tile_file); else @@ -3999,8 +4269,10 @@ optfn_tile_file(int optidx UNUSED, int req, boolean negated UNUSED, return optn_ok; } -static int -optfn_tile_height(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_tile_height( + int optidx, int req, boolean negated, + char *opts, char *op) { if (req == do_init) { return optn_ok; @@ -4018,8 +4290,6 @@ optfn_tile_height(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; if (iflags.wc_tile_height) Sprintf(opts, "%d", iflags.wc_tile_height); else if (req == get_cnf_val) @@ -4031,8 +4301,10 @@ optfn_tile_height(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } -static int -optfn_tile_width(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_tile_width( + int optidx, int req, boolean negated, + char *opts, char *op) { if (req == do_init) { return optn_ok; @@ -4050,8 +4322,6 @@ optfn_tile_width(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; if (iflags.wc_tile_width) Sprintf(opts, "%d", iflags.wc_tile_width); else if (req == get_cnf_val) @@ -4063,9 +4333,10 @@ optfn_tile_width(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } -static int -optfn_traps(int optidx UNUSED, int req, boolean negated UNUSED, - char *opts, char *op UNUSED) +staticfn int +optfn_traps( + int optidx UNUSED, int req, boolean negated UNUSED, + char *opts, char *op UNUSED) { if (req == do_init) { return optn_ok; @@ -4074,21 +4345,17 @@ optfn_traps(int optidx UNUSED, int req, boolean negated UNUSED, return optn_ok; } if (req == get_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", to_be_done); return optn_ok; } if (req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; return optn_ok; } return optn_ok; } -static int +staticfn int optfn_vary_msgcount( int optidx, int req, boolean negated, char *opts, char *op) @@ -4109,8 +4376,6 @@ optfn_vary_msgcount( return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; if (iflags.wc_vary_msgcount) Sprintf(opts, "%d", iflags.wc_vary_msgcount); else if (req == get_cnf_val) @@ -4122,10 +4387,76 @@ optfn_vary_msgcount( return optn_ok; } +staticfn int +optfn_versinfo( + int optidx, int req, boolean negated, + char *opts, char *op) +{ + const char *optname = allopt[optidx].name; + unsigned vi = flags.versinfo; + + if (req == do_init) { + return optn_ok; + } + if (req == do_set) { + /* versinfo: what to include when 'showvers' displays version + on status lines; bitmask with up to three bits: + (1) x.y.z number, (2) program name, (4) git branch if available. + If branch is requested but unavailable, status_version will + treat 4 as 1. + */ + boolean have_branch = (nomakedefs.git_branch + && *nomakedefs.git_branch); + int val, dflt = have_branch ? VI_BRANCH : VI_NUMBER; + + if (negated) { + bad_negation(allopt[optidx].name, TRUE); + return optn_silenterr; + } + op = string_for_opt(opts, FALSE); + if (op == empty_optstr) { + config_error_add("'%s' requires a value; defaulting to %d", + optname, dflt); + return optn_silenterr; + } + val = atoi(op); + if (!val || (val & ~7) != 0) { + config_error_add("'%s' must be one of 1, 2, 4, or" + " the sum of two or all three of those", + optname); + return optn_silenterr; + } + flags.versinfo = (unsigned) val; + } else if (req == do_handler) { + /* return handler_versinfo(); */ + (void) handler_versinfo(); + pline("'%s' %s %u.", optname, + (flags.versinfo == vi) ? "not changed, still" : "changed to", + flags.versinfo); + } else if (req == get_val) { + char vbuf[QBUFSZ]; + boolean g = (vi & VI_NAME) != 0, + b = (vi & VI_BRANCH) != 0, + n = (vi & VI_NUMBER) != 0; + + Sprintf(opts, "%u: %s%s%s%s%s (%.99s)", flags.versinfo, + g ? "name" : "", (b && g) ? "+" : "", b ? "branch" : "", + (n && (b || g)) ? "+" : "", n ? "number" : "", + status_version(vbuf, sizeof vbuf, FALSE)); + } else if (req == get_cnf_val) { + Sprintf(opts, "%u", flags.versinfo); + } + if (flags.versinfo != vi && !go.opt_initial) + go.opt_need_redraw = TRUE; /* context.botlx = TRUE ought to suffice + * but doesn't for X11 fancy status */ + return optn_ok; +} + #ifdef VIDEOSHADES -static int -optfn_videocolors(int optidx, int req, boolean negated UNUSED, - char *opts, char *op UNUSED) +staticfn int +optfn_videocolors( + int optidx, int req, boolean negated UNUSED, + char *opts, char *op UNUSED) { if (req == do_init) { return optn_ok; @@ -4145,8 +4476,6 @@ optfn_videocolors(int optidx, int req, boolean negated UNUSED, return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; Sprintf(opts, "%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d", ttycolors[CLR_RED], ttycolors[CLR_GREEN], ttycolors[CLR_BROWN], ttycolors[CLR_BLUE], @@ -4159,9 +4488,10 @@ optfn_videocolors(int optidx, int req, boolean negated UNUSED, return optn_ok; } -static int -optfn_videoshades(int optidx, int req, boolean negated UNUSED, - char *opts, char *op UNUSED) +staticfn int +optfn_videoshades( + int optidx, int req, boolean negated UNUSED, + char *opts, char *op UNUSED) { if (req == do_init) { return optn_ok; @@ -4181,8 +4511,6 @@ optfn_videoshades(int optidx, int req, boolean negated UNUSED, return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s-%s-%s", shade[0], shade[1], shade[2]); return optn_ok; } @@ -4191,9 +4519,10 @@ optfn_videoshades(int optidx, int req, boolean negated UNUSED, #endif /* VIDEOSHADES */ #ifdef MSDOS -static int -optfn_video_width(int optidx UNUSED, int req, boolean negated, - char *opts, char *op) +staticfn int +optfn_video_width( + int optidx UNUSED, int req, boolean negated, + char *opts, char *op) { if (req == do_init) { return optn_ok; @@ -4206,16 +4535,15 @@ optfn_video_width(int optidx UNUSED, int req, boolean negated, return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; } return optn_ok; } -static int -optfn_video_height(int optidx UNUSED, int req, boolean negated, - char *opts, char *op) +staticfn int +optfn_video_height( + int optidx UNUSED, int req, boolean negated, + char *opts, char *op) { if (req == do_init) { return optn_ok; @@ -4228,17 +4556,16 @@ optfn_video_height(int optidx UNUSED, int req, boolean negated, return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; } return optn_ok; } #ifdef NO_TERMS -static int -optfn_video(int optidx, int req, boolean negated UNUSED, - char *opts, char *op UNUSED) +staticfn int +optfn_video( + int optidx, int req, boolean negated UNUSED, + char *opts, char *op UNUSED) { if (req == do_init) { return optn_ok; @@ -4257,14 +4584,10 @@ optfn_video(int optidx, int req, boolean negated UNUSED, return optn_ok; } if (req == get_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", to_be_done); return optn_ok; } if (req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; return optn_ok; } @@ -4274,9 +4597,10 @@ optfn_video(int optidx, int req, boolean negated UNUSED, #endif /* NO_TERMS */ #endif /* MSDOS */ -static int -optfn_warnings(int optidx, int req, boolean negated UNUSED, - char *opts, char *op UNUSED) +staticfn int +optfn_warnings( + int optidx, int req, boolean negated UNUSED, + char *opts, char *op UNUSED) { int reslt; @@ -4288,16 +4612,16 @@ optfn_warnings(int optidx, int req, boolean negated UNUSED, return reslt ? optn_ok : optn_err; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; return optn_ok; } return optn_ok; } -static int -optfn_whatis_coord(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_whatis_coord( + int optidx, int req, boolean negated, + char *opts, char *op) { if (req == do_init) { return optn_ok; @@ -4325,8 +4649,6 @@ optfn_whatis_coord(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", (iflags.getpos_coords == GPCOORDS_MAP) ? "map" : (iflags.getpos_coords == GPCOORDS_COMPASS) ? "compass" @@ -4341,7 +4663,7 @@ optfn_whatis_coord(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } -static int +staticfn int optfn_whatis_filter( int optidx, int req, boolean negated, char *opts, char *op) @@ -4378,8 +4700,6 @@ optfn_whatis_filter( return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", (iflags.getloc_filter == GFILTER_VIEW) ? "view" : (iflags.getloc_filter == GFILTER_AREA) ? "area" @@ -4392,7 +4712,7 @@ optfn_whatis_filter( return optn_ok; } -static int +staticfn int optfn_windowborders( int optidx, int req, boolean negated, char *opts, char *op) @@ -4429,18 +4749,7 @@ optfn_windowborders( } return retval; } - if (req == get_cnf_val) { - if (!opts) - return optn_err; - Sprintf(opts, "%i", iflags.wc2_windowborders); - return optn_ok; - } - if (req == do_handler) { - return handler_windowborders(); - } if (req == get_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", (iflags.wc2_windowborders == 0) ? "0=off" : (iflags.wc2_windowborders == 1) ? "1=on" @@ -4452,12 +4761,22 @@ optfn_windowborders( : defopt); return optn_ok; } + if (req == get_cnf_val) { + Sprintf(opts, "%i", iflags.wc2_windowborders); + return optn_ok; + } + if (req == do_handler) { + return handler_windowborders(); + } return optn_ok; } #ifdef WINCHAIN -static int -optfn_windowchain(int optidx, int req, boolean negated UNUSED, char *opts, char *op) +staticfn int +optfn_windowchain( + int optidx, int req, + boolean negated UNUSED, + char *opts, char *op) { if (req == do_init) { return optn_ok; @@ -4474,8 +4793,6 @@ optfn_windowchain(int optidx, int req, boolean negated UNUSED, char *opts, char return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; return optn_ok; } @@ -4483,18 +4800,33 @@ optfn_windowchain(int optidx, int req, boolean negated UNUSED, char *opts, char } #endif -static int -optfn_windowcolors(int optidx, int req, boolean negated UNUSED, - char *opts, char *op) +/* Win GUI and curses */ +static const char *const wcnames[WC_COUNT] = { + "menu", "message", "status", "text" +}; +static const char *const wcshortnames[WC_COUNT] = { + "mnu", "msg", "sts", "txt" +}; +int wcolors_opt[WC_COUNT]; + +staticfn int +optfn_windowcolors( + int optidx, int req, + boolean negated UNUSED, + char *opts, char *op) { + int wccount; + if (req == do_init) { + for (wccount = 0; wccount < WC_COUNT; ++wccount) { + wcolors_opt[wccount] = 0; + } return optn_ok; } if (req == do_set) { /* WINCAP * setting window colors - * syntax: windowcolors=menu foregrnd/backgrnd text - * foregrnd/backgrnd + * syntax: windowcolors=menu foregrnd/backgrnd text foregrnd/backgrnd */ if ((op = string_for_opt(opts, FALSE)) != empty_optstr) { if (!wc_set_window_colors(op)) { @@ -4506,28 +4838,31 @@ optfn_windowcolors(int optidx, int req, boolean negated UNUSED, return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; - Sprintf( - opts, "%s/%s %s/%s %s/%s %s/%s", - iflags.wc_foregrnd_menu ? iflags.wc_foregrnd_menu : defbrief, - iflags.wc_backgrnd_menu ? iflags.wc_backgrnd_menu : defbrief, - iflags.wc_foregrnd_message ? iflags.wc_foregrnd_message - : defbrief, - iflags.wc_backgrnd_message ? iflags.wc_backgrnd_message - : defbrief, - iflags.wc_foregrnd_status ? iflags.wc_foregrnd_status : defbrief, - iflags.wc_backgrnd_status ? iflags.wc_backgrnd_status : defbrief, - iflags.wc_foregrnd_text ? iflags.wc_foregrnd_text : defbrief, - iflags.wc_backgrnd_text ? iflags.wc_backgrnd_text : defbrief); + const char *fg, *bg; + + /* TODO: wide 'get_val' may need to be wrapped in the menu display */ + opts[0] = '\0'; + for (wccount = 0; wccount < WC_COUNT; ++wccount) { + fg = iflags.wcolors[wccount].fg; + bg = iflags.wcolors[wccount].bg; + if (fg && (!*fg || !strcmp(fg, defbrief))) + fg = 0; + if (bg && (!*bg || !strcmp(bg, defbrief))) + bg = 0; + Sprintf(eos(opts), "%s%s %s/%s", !wccount ? "" : " ", + (fg || bg) ? wcnames[wccount] : wcshortnames[wccount], + fg ? fg : defbrief, bg ? bg : defbrief); + } return optn_ok; } return optn_ok; } -static int -optfn_windowtype(int optidx, int req, boolean negated UNUSED, - char *opts, char *op) +staticfn int +optfn_windowtype( + int optidx, int req, + boolean negated UNUSED, + char *opts, char *op) { if (req == do_init) { return optn_ok; @@ -4561,8 +4896,6 @@ optfn_windowtype(int optidx, int req, boolean negated UNUSED, return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; Sprintf(opts, "%s", windowprocs.name); return optn_ok; } @@ -4573,9 +4906,9 @@ optfn_windowtype(int optidx, int req, boolean negated UNUSED, * Prefix-handling functions */ -static int +staticfn int pfxfn_cond_( - int optidx, + int optidx UNUSED, int req, boolean negated, char *opts, @@ -4590,7 +4923,7 @@ pfxfn_cond_( switch (reslt) { case 0: - opt_set_in_config[optidx] = TRUE; + opt_set_in_config[pfx_cond_] = TRUE; break; case 3: config_error_add("Ambiguous condition option %s", opts); @@ -4608,19 +4941,17 @@ pfxfn_cond_( return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; return optn_ok; } - if (req == do_handler) { - cond_menu(); /* in botl.c */ + if (req == do_handler) { /* not used */ + (void) cond_menu(); /* in botl.c */ return optn_ok; } return optn_ok; } -static int +staticfn int pfxfn_font(int optidx, int req, boolean negated, char *opts, char *op) { int opttype = -1; @@ -4703,9 +5034,6 @@ pfxfn_font(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; - if (optidx == opt_font_map) { Sprintf(opts, "%s", iflags.wc_font_map ? iflags.wc_font_map : defopt); @@ -4764,79 +5092,9 @@ pfxfn_IBM_(int optidx UNUSED, int req, boolean negated UNUSED, return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; - opts[0] = '\0'; - return optn_ok; - } - return optn_ok; -} -#endif - -#ifndef NO_VERBOSE_GRANULARITY -int -pfxfn_verbose( - int optidx UNUSED, - int req, - boolean negated, - char *opts, - char *op) -{ - long ltmp = 0; - int reslt; - char *p; - boolean param_optional = FALSE; - - if (req == do_init) { - flags.verbose = allopt[optidx].opt_in_out; - return optn_ok; - } - if (req == do_set) { - if (opts) { - if (!strncmp(opts, "verbose", 7)) { - p = strchr("01234", *(opts + 7)); - if (p && *p == '\0') /* plain verbose, not verboseN */ - param_optional = TRUE; - if ((op = string_for_opt(opts, param_optional)) != empty_optstr) { - ltmp = atol(op); - int idx; - - if (p && (*p != '\0')) { - idx = *p - '0'; - if (idx >= 0 && idx < vb_elements) - verbosity_suppressions[idx] = ltmp; - return optn_ok; - } - } else { - if (param_optional) { - /* indicates plain verbose, not verboseN */ - flags.verbose = (negated ? 0 : 1); - return optn_ok; - } - } - } - } - return optn_err; - } - if (req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; return optn_ok; } - if (req == get_val) { - if (!opts) - return optn_err; - Sprintf(opts, "%s,%ld,%ld,%ld,%ld,%ld", flags.verbose ? "On" : "Off", - verbosity_suppressions[0], verbosity_suppressions[1], - verbosity_suppressions[2], verbosity_suppressions[3], - verbosity_suppressions[4]); - return optn_ok; - } - if (req == do_handler) { - reslt = handler_verbose(optidx); - return reslt; - } return optn_ok; } #endif @@ -4846,14 +5104,17 @@ pfxfn_verbose( * (Use optidx to reference the specific option) */ -static int -optfn_boolean(int optidx, int req, boolean negated, char *opts, char *op) +staticfn int +optfn_boolean( + int optidx, int req, boolean negated, + char *opts, char *op) { if (req == do_init) { return optn_ok; } if (req == do_set) { boolean nosexchange = FALSE; + int ln = 0; if (!allopt[optidx].addr) return optn_ok; /* silent retreat */ @@ -4868,8 +5129,6 @@ optfn_boolean(int optidx, int req, boolean negated, char *opts, char *op) op = string_for_opt(opts, TRUE); if (op != empty_optstr) { - int ln; - if (negated) { config_error_add( "Negated boolean '%s' should not have a parameter", @@ -4902,7 +5161,7 @@ optfn_boolean(int optidx, int req, boolean negated, char *opts, char *op) /* Before the change */ switch (optidx) { case opt_female: - if (!strncmpi(opts, "female", 3)) { + if (!strncmpi(opts, "female", max(ln, 3))) { if (!go.opt_initial && flags.female == negated) { nosexchange = TRUE; } else { @@ -4910,7 +5169,7 @@ optfn_boolean(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } } - if (!strncmpi(opts, "male", 3)) { + if (!strncmpi(opts, "male", max(ln, 3))) { if (!go.opt_initial && flags.female != negated) { nosexchange = TRUE; } else { @@ -4920,23 +5179,9 @@ optfn_boolean(int optidx, int req, boolean negated, char *opts, char *op) } break; case opt_perm_invent: -#ifdef TTY_PERM_INVENT - /* if attempting to enable perm_invent fails, say so and return - before "'perm_invent' option toggled on" would be given below; - perm_invent_toggled() and routines it calls don't check - iflags.perm_invent so it doesn't matter that 'SET IT HERE' - hasn't been executed yet */ - if (WINDOWPORT(tty) && !go.opt_initial && !negated) { - perm_invent_toggled(FALSE); - /* perm_invent_toggled() - -> sync_perminvent() - -> tty_create_nhwindow(NHW_PERMINVENT) - gives feedback for failure (terminal too small) */ - if (WIN_INVEN == WIN_ERR) - return optn_silenterr; - } -#endif - break; /* from opt_perm_invent */ + if (!negated && !go.opt_initial && !can_set_perm_invent()) + return optn_silenterr; + break; default: break; } @@ -4958,12 +5203,29 @@ optfn_boolean(int optidx, int req, boolean negated, char *opts, char *op) /* After the change */ switch (optidx) { + case opt_pauper: + /* pauper implies nudist */ + u.uroleplay.nudist = u.uroleplay.pauper; + break; case opt_ascii_map: iflags.wc_tiled_map = negated; break; case opt_tiled_map: iflags.wc_ascii_map = negated; break; + case opt_hilite_pet: +#if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS) + if (WINDOWPORT(tty) || WINDOWPORT(curses)) { + /* if we're enabling hilite_pet and petattr isn't set, + set it to Inverse; if we're disabling, leave petattr + alone so that re-enabling will get current value back + */ + if (iflags.hilite_pet && !iflags.wc2_petattr) + iflags.wc2_petattr = ATR_INVERSE; + } +#endif + go.opt_need_redraw = TRUE; + break; default: break; } @@ -4978,14 +5240,16 @@ optfn_boolean(int optidx, int req, boolean negated, char *opts, char *op) #ifdef SCORE_ON_BOTL case opt_showscore: #endif + case opt_showvers: case opt_showexp: if (VIA_WINDOWPORT()) status_initialize(REASSESS_ONLY); - gc.context.botl = TRUE; + disp.botl = TRUE; break; case opt_fixinv: case opt_sortpack: case opt_implicit_uncursed: + case opt_invweight: if (!flags.invlet_constant) reassign(); update_inventory(); @@ -5014,19 +5278,6 @@ optfn_boolean(int optidx, int req, boolean negated, char *opts, char *op) go.opt_need_redraw = TRUE; go.opt_need_glyph_reset = TRUE; break; - case opt_hilite_pet: -#ifdef CURSES_GRAPHICS - if (WINDOWPORT(curses)) { - /* if we're enabling hilite_pet and petattr isn't set, - set it to Inverse; if we're disabling, leave petattr - alone so that re-enabling will get current value back - */ - if (iflags.hilite_pet && !iflags.wc2_petattr) - iflags.wc2_petattr = curses_read_attrs("I"); - } -#endif - go.opt_need_redraw = TRUE; - break; case opt_hitpointbar: if (VIA_WINDOWPORT()) { /* [is reassessment really needed here?] */ @@ -5036,7 +5287,7 @@ optfn_boolean(int optidx, int req, boolean negated, char *opts, char *op) } else if (WINDOWPORT(Qt)) { /* Qt doesn't support HILITE_STATUS or FLUSH_STATUS so fails VIA_WINDOWPORT(), but it does support WC2_HITPOINTBAR */ - gc.context.botlx = TRUE; + disp.botlx = TRUE; #endif } break; @@ -5052,9 +5303,16 @@ optfn_boolean(int optidx, int req, boolean negated, char *opts, char *op) go.opt_need_redraw = TRUE; go.opt_need_glyph_reset = TRUE; break; + case opt_customcolors: + go.opt_reset_customcolors = TRUE; + break; + case opt_customsymbols: + go.opt_reset_customsymbols = TRUE; + break; case opt_menucolors: case opt_guicolor: update_inventory(); + go.opt_need_promptstyle = TRUE; break; case opt_mention_decor: iflags.prev_decor = STONE; @@ -5062,6 +5320,9 @@ optfn_boolean(int optidx, int req, boolean negated, char *opts, char *op) 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; } @@ -5076,15 +5337,13 @@ optfn_boolean(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; return optn_ok; } return optn_ok; } -static int +staticfn int spcfn_misc_menu_cmd(int midx, int req, boolean negated, char *opts, char *op) { if (req == do_init) { @@ -5104,8 +5363,6 @@ spcfn_misc_menu_cmd(int midx, int req, boolean negated, char *opts, char *op) return optn_ok; } if (req == get_val || req == get_cnf_val) { - if (!opts) - return optn_err; opts[0] = '\0'; return optn_ok; } @@ -5113,15 +5370,75 @@ spcfn_misc_menu_cmd(int midx, int req, boolean negated, char *opts, char *op) } -/* - ********************************** - * - * Special per-option handlers - * - ********************************** - */ +/* + ********************************** + * + * Special per-option handlers + * + ********************************** + */ + +/* test whether 'perm_invent' can be toggled On */ +staticfn boolean +can_set_perm_invent(void) +{ + /* + * Assumption: only called when iflags.perm_invent is False + * and is about to be changed to True. + */ + uchar old_perminv_mode = iflags.perminv_mode; + + if (!(windowprocs.wincap & WC_PERM_INVENT)) { +#ifdef TTY_GRAPHICS +#ifdef TTY_PERM_INVENT + /* check tty, not necessarily the active window port; + windows early startup can still be set to safeprocs */ + if (!check_tty_wincap(WC_PERM_INVENT)) +#endif +#endif + return FALSE; + } + + if (iflags.perminv_mode == InvOptNone) + iflags.perminv_mode = InvOptOn; + +#ifdef TTY_PERM_INVENT + if ((WINDOWPORT(tty) +#ifdef WIN32 + || WINDOWPORT(safestartup) +#endif + ) && !go.opt_initial) { + perm_invent_toggled(FALSE); + /* perm_invent_toggled() + -> sync_perminvent() + -> tty_create_nhwindow(NHW_PERMINVENT) + gives feedback for failure (terminal too small) */ + if (WIN_INVEN == WIN_ERR) { + iflags.perminv_mode = old_perminv_mode; + return FALSE; + } + } +#else + nhUse(old_perminv_mode); +#endif + 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 -static int +staticfn int handler_menustyle(void) { winid tmpwin; @@ -5130,7 +5447,7 @@ handler_menustyle(void) int i, n, old_menu_style = flags.menu_style; char buf[BUFSZ], sep = iflags.menu_tab_sep ? '\t' : ' '; menu_item *style_pick = (menu_item *) 0; - int clr = 0; + int clr = NO_COLOR; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -5143,9 +5460,7 @@ handler_menustyle(void) : MENU_ITEMFLAGS_NONE); /* second line is prefixed by spaces that "c - " would use */ Sprintf(buf, "%4s%-12.12s%c%.60s", "", "", sep, menutype[i][2]); - any.a_int = 0; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, buf, - MENU_ITEMFLAGS_NONE); + add_menu_str(tmpwin, buf); } end_menu(tmpwin, "Select menustyle:"); n = select_menu(tmpwin, PICK_ONE, &style_pick); @@ -5159,20 +5474,20 @@ handler_menustyle(void) } destroy_nhwindow(tmpwin); chngd = (flags.menu_style != old_menu_style); - if (chngd || Verbose(2, handler_menustyle)) + if (chngd || flags.verbose) pline("'menustyle' %s \"%s\".", chngd ? "changed to" : "is still", menutype[(int) flags.menu_style][0]); return optn_ok; } -static int +staticfn int handler_align_misc(int optidx) { winid tmpwin; anything any; menu_item *window_pick = (menu_item *) 0; char abuf[BUFSZ]; - int clr = 0; + int clr = NO_COLOR; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -5203,7 +5518,7 @@ handler_align_misc(int optidx) return optn_ok; } -static int +staticfn int handler_autounlock(int optidx) { winid tmpwin; @@ -5214,7 +5529,7 @@ handler_autounlock(int optidx) char buf[BUFSZ], sep = iflags.menu_tab_sep ? '\t' : ' '; menu_item *window_pick = (menu_item *) 0; int i, n, presel, res = optn_ok; - int clr = 0; + int clr = NO_COLOR; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -5246,7 +5561,7 @@ handler_autounlock(int optidx) } destroy_nhwindow(tmpwin); chngd = (flags.autounlock != oldflags); - if ((chngd || Verbose(2, handler_autounlock)) && give_opt_msg) { + if ((chngd || flags.verbose) && give_opt_msg) { optfn_autounlock(optidx, get_val, FALSE, buf, (char *) NULL); pline("'%s' %s '%s'.", optname, chngd ? "changed to" : "is still", buf); @@ -5254,7 +5569,7 @@ handler_autounlock(int optidx) return res; } -static int +staticfn int handler_disclose(void) { winid tmpwin; @@ -5271,7 +5586,7 @@ handler_disclose(void) int pick_cnt, pick_idx, opt_idx; char c; menu_item *disclosure_pick = (menu_item *) 0; - int clr = 0; + int clr = NO_COLOR; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -5359,28 +5674,29 @@ handler_disclose(void) return optn_ok; } -static int +staticfn int handler_menu_headings(void) { - int mhattr = query_attr("How to highlight menu headings:"); + boolean gotca = query_color_attr(&iflags.menu_headings, + "How to highlight menu headings:"); - if (mhattr != -1) { - iflags.menu_headings = mhattr; + if (gotca) { /* header highlighting affects persistent inventory display */ if (iflags.perm_invent) update_inventory(); } + adjust_menu_promptstyle(WIN_INVEN, &iflags.menu_headings); return optn_ok; } -static int +staticfn int handler_msg_window(void) { #if PREV_MSGS /* tty or curses */ winid tmpwin; anything any; boolean is_tty = WINDOWPORT(tty), is_curses = WINDOWPORT(curses); - int clr = 0; + int clr = NO_COLOR; if (is_tty || is_curses) { /* by Christian W. Cooper */ @@ -5398,16 +5714,16 @@ handler_msg_window(void) for (i = 0; i < SIZE(menutype); i++) { if (i < 2 && is_curses) continue; - Sprintf(buf, "%-12.12s%c%.60s", msgwind[i][0], sep, msgwind[i][1]); + Sprintf(buf, "%-12.12s%c%.60s", msgwind[i][0], sep, + msgwind[i][1]); any.a_char = c = *msgwind[i][0]; - add_menu(tmpwin, &nul_glyphinfo, &any, *buf, 0, ATR_NONE, clr, buf, + add_menu(tmpwin, &nul_glyphinfo, &any, *buf, 0, + ATR_NONE, clr, buf, (c == iflags.prevmsg_window) ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE); /* second line is prefixed by spaces that "c - " would use */ Sprintf(buf, "%4s%-12.12s%c%.60s", "", "", sep, msgwind[i][2]); - any.a_char = '\0'; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, buf, - MENU_ITEMFLAGS_NONE); + add_menu_str(tmpwin, buf); } end_menu(tmpwin, "Select message history display type:"); n = select_menu(tmpwin, PICK_ONE, &window_pick); @@ -5421,7 +5737,7 @@ handler_msg_window(void) } destroy_nhwindow(tmpwin); chngd = (iflags.prevmsg_window != old_prevmsg_window); - if (chngd || Verbose(2, handler_msg_window)) { + if (chngd || flags.verbose) { (void) optfn_msg_window(opt_msg_window, get_val, FALSE, buf, empty_optstr); pline("'msg_window' %.20s \"%.20s\".", @@ -5434,7 +5750,7 @@ handler_msg_window(void) return optn_ok; } -static int +staticfn int handler_number_pad(void) { winid tmpwin; @@ -5447,7 +5763,7 @@ handler_number_pad(void) "-1 (off, 'z' to move upper-left, 'y' to zap wands)" }; menu_item *mode_pick = (menu_item *) 0; - int clr = 0; + int clr = NO_COLOR; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -5494,14 +5810,16 @@ handler_number_pad(void) return optn_ok; } -static int +staticfn int handler_paranoid_confirmation(void) { winid tmpwin; anything any; int i; + char mkey, mbuf[QBUFSZ], ebuf[BUFSZ], cbuf[QBUFSZ]; + const char *explain, *cmdnm; menu_item *paranoia_picks = (menu_item *) 0; - int clr = 0; + int clr = NO_COLOR; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -5509,9 +5827,25 @@ handler_paranoid_confirmation(void) for (i = 0; paranoia[i].flagmask != 0; ++i) { if (paranoia[i].flagmask == PARANOID_BONES && !wizard) continue; + /* the 'swim' choice mentions the 'm' movement prefix in its + explanation; if that's been bound to something else or been + unbound altogether, substitute the replacement in the text */ + explain = paranoia[i].explain; + if (strstri(explain, "'m'") + && (mkey = cmd_from_func(do_reqmenu)) != 'm') { + if (mkey) { /* key for 'm' prefix */ + Sprintf(mbuf, "'%.9s'", visctrl(mkey)); /* .5 is enough */ + } else { /* extended command name for 'm' prefix */ + cmdnm = cmdname_from_func(do_reqmenu, cbuf, TRUE); + if (!cmdnm) + cmdnm = "reqmenu"; + Sprintf(mbuf, "'%s%.31s'", (*cmdnm != '#') ? "#" : "", cmdnm); + } + explain = strsubst(strcpy(ebuf, explain), "'m'", mbuf); + } any.a_int = paranoia[i].flagmask; add_menu(tmpwin, &nul_glyphinfo, &any, *paranoia[i].argname, - 0, ATR_NONE, clr, paranoia[i].explain, + 0, ATR_NONE, clr, explain, (flags.paranoia_bits & paranoia[i].flagmask) ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE); @@ -5534,7 +5868,82 @@ handler_paranoid_confirmation(void) return optn_ok; } -static int +staticfn int +handler_perminv_mode(void) +{ + winid tmpwin; + anything any; + char let, buf[BUFSZ], sepbuf[10]; + const char *pi0, *pi1; + menu_item *pi_pick = (menu_item *) 0; + boolean old_perm_invent = iflags.perm_invent; + int i, n, old_pi = (int) iflags.perminv_mode, new_pi = old_pi, + widest = !WINDOWPORT(tty) ? 8 : 11; /* "in-use__" or "full+grid__" */ + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin, MENU_BEHAVE_STANDARD); + any = cg.zeroany; + for (i = 0; i < SIZE(perminv_modes); ++i) { + if (!(pi0 = perminv_modes[i][0])) + continue; +#ifdef TTY_PERM_INVENT + if (strstri(pi0, "+grid") != 0 && !WINDOWPORT(tty)) + continue; +#endif + pi1 = perminv_modes[i][1]; + if (!iflags.menu_tab_sep) { + int numspaces = widest - (int) strlen(pi0); + + Sprintf(sepbuf, "%*s", max(numspaces, 1), " "); + } else { + Strcpy(sepbuf, "\t"); + } + Sprintf(buf, "%s%s%s", pi0, sepbuf, perminv_modes[i][2]); + let = ((i & (int) InvSparse) != 0) ? highc(pi1[0]) : pi0[0]; + any.a_int = i + 1; + add_menu(tmpwin, &nul_glyphinfo, &any, let, '0' + i, + ATR_NONE, NO_COLOR, + buf, (i == old_pi) ? MENU_ITEMFLAGS_SELECTED + : MENU_ITEMFLAGS_NONE); + } + end_menu(tmpwin, "Choose permanent inventory mode:"); + n = select_menu(tmpwin, PICK_ONE, &pi_pick); + destroy_nhwindow(tmpwin); + if (n > 0) { + new_pi = pi_pick[0].item.a_int - 1; + if (n > 1 && new_pi == old_pi) + new_pi = pi_pick[1].item.a_int - 1; + free((genericptr_t) pi_pick); + iflags.perminv_mode = new_pi; + } + if (n >= 0) { /* not ESC */ + buf[0] = '\0'; + (void) optfn_perminv_mode(opt_perm_invent, get_val, FALSE, buf, NULL); + pline("'perminv_mode' %s '%s' (%s).", + (new_pi != old_pi) ? "changed to" : "is still", + perminv_modes[new_pi][0], buf); + if (new_pi != InvOptNone && !old_perm_invent) + iflags.perm_invent = can_set_perm_invent(); + else if (new_pi == InvOptNone && old_perm_invent) + iflags.perm_invent = FALSE; + + if (new_pi != old_pi || iflags.perm_invent != old_perm_invent) { +#ifdef TTY_PERM_INVENT + /* FIXME: TTY_PERM_INVENT will blank WIN_INVEN when changing + perminv_mode while perm_invent is already on; to remedy that, + turn it off and then back on when already on */ + if (WINDOWPORT(tty) && iflags.perm_invent && old_perm_invent) { + perm_invent_toggled(TRUE); /*TEMP?*/ + perm_invent_toggled(FALSE); /*TEMP?*/ + } +#endif + go.opt_need_redraw = TRUE; + } + } + return optn_ok; +} + +staticfn int handler_pickup_burden(void) { winid tmpwin; @@ -5542,7 +5951,7 @@ handler_pickup_burden(void) int i; const char *burden_name, *burden_letters = "ubsntl"; menu_item *burden_pick = (menu_item *) 0; - int clr = 0; + int clr = NO_COLOR; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -5562,7 +5971,7 @@ handler_pickup_burden(void) return optn_ok; } -static int +staticfn int handler_pickup_types(void) { char buf[BUFSZ]; @@ -5572,7 +5981,7 @@ handler_pickup_types(void) return optn_ok; } -static int +staticfn int handler_runmode(void) { winid tmpwin; @@ -5580,7 +5989,7 @@ handler_runmode(void) int i; const char *mode_name; menu_item *mode_pick = (menu_item *) 0; - int clr = 0; + int clr = NO_COLOR; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -5600,7 +6009,22 @@ handler_runmode(void) return optn_ok; } -static int +staticfn int +handler_petattr(void) +{ + int tmp + = query_attr("Select pet highlight attribute", iflags.wc2_petattr); + + if (tmp != -1) { + iflags.wc2_petattr = tmp; + iflags.hilite_pet = (iflags.wc2_petattr != ATR_NONE); + if (!go.opt_initial) + go.opt_need_redraw = TRUE; + } + return optn_ok; +} + +staticfn int handler_sortloot(void) { winid tmpwin; @@ -5608,7 +6032,7 @@ handler_sortloot(void) int i, n; const char *sortl_name; menu_item *sortl_pick = (menu_item *) 0; - int clr = 0; + int clr = NO_COLOR; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -5639,7 +6063,7 @@ handler_sortloot(void) return optn_ok; } -static int +staticfn int handler_whatis_coord(void) { winid tmpwin; @@ -5648,7 +6072,7 @@ handler_whatis_coord(void) menu_item *window_pick = (menu_item *) 0; int pick_cnt; char gpc = iflags.getpos_coords; - int clr = 0; + int clr = NO_COLOR; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -5680,31 +6104,24 @@ handler_whatis_coord(void) 0, ATR_NONE, clr, "none (no coordinates displayed)", (gpc == GPCOORDS_NONE) ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE); - any.a_long = 0L; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, - "", MENU_ITEMFLAGS_NONE); + add_menu_str(tmpwin, ""); Sprintf(buf, "map: upper-left: <%d,%d>, lower-right: <%d,%d>%s", 1, 0, COLNO - 1, ROWNO - 1, - Verbose(2, handler_whatis_coord1) - ? "; column 0 unused, off left edge" : ""); - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, buf, MENU_ITEMFLAGS_NONE); + flags.verbose ? "; column 0 unused, off left edge" : ""); + add_menu_str(tmpwin, buf); if (strcmp(windowprocs.name, "tty")) /* only show for non-tty */ - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, - "screen: row is offset to accommodate tty interface's use of top line", - MENU_ITEMFLAGS_NONE); + add_menu_str(tmpwin, + "screen: row is offset to accommodate tty interface's use of top line"); #if COLNO == 80 -#define COL80ARG Verbose(2, handler_whatis_coord2) ? "; column 80 is not used" : "" +#define COL80ARG flags.verbose ? "; column 80 is not used" : "" #else #define COL80ARG "" #endif Sprintf(buf, "screen: upper-left: [%02d,%02d], lower-right: [%d,%d]%s", 0 + 2, 1, ROWNO - 1 + 2, COLNO - 1, COL80ARG); #undef COL80ARG - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, - buf, MENU_ITEMFLAGS_NONE); - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, - "", MENU_ITEMFLAGS_NONE); + add_menu_str(tmpwin, buf); + add_menu_str(tmpwin, ""); end_menu(tmpwin, "Select coordinate display when auto-describing a map position:"); if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &window_pick)) > 0) { @@ -5719,7 +6136,7 @@ handler_whatis_coord(void) return optn_ok; } -static int +staticfn int handler_whatis_filter(void) { winid tmpwin; @@ -5727,7 +6144,7 @@ handler_whatis_filter(void) menu_item *window_pick = (menu_item *) 0; int pick_cnt; char gfilt = iflags.getloc_filter; - int clr = 0; + int clr = NO_COLOR; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -5761,7 +6178,7 @@ handler_whatis_filter(void) return optn_ok; } -static int +staticfn int handler_symset(int optidx UNUSED) { int reslt; @@ -5771,7 +6188,7 @@ handler_symset(int optidx UNUSED) return reslt; } -static int +staticfn int handler_autopickup_exception(void) { winid tmpwin; @@ -5780,7 +6197,7 @@ handler_autopickup_exception(void) int opt_idx, numapes = 0; char apebuf[2 + BUFSZ]; /* so &apebuf[1] is BUFSZ long for getlin() */ struct autopickup_exception *ape; - int clr = 0; + int clr = NO_COLOR; ape_again: numapes = count_apes(); @@ -5814,10 +6231,8 @@ handler_autopickup_exception(void) if (numapes) { ape = ga.apelist; any = cg.zeroany; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - iflags.menu_headings, clr, - "Always pickup '<'; never pickup '>'", - MENU_ITEMFLAGS_NONE); + add_menu_heading(tmpwin, + "Always pickup '<'; never pickup '>'"); for (i = 0; i < numapes && ape; i++) { any.a_void = (opt_idx == 1) ? 0 : ape; /* length of pattern plus quotes (plus '<'/'>') is @@ -5849,7 +6264,7 @@ handler_autopickup_exception(void) return optn_ok; } -static int +staticfn int handler_menu_colors(void) { winid tmpwin; @@ -5857,7 +6272,7 @@ handler_menu_colors(void) char buf[BUFSZ]; int opt_idx, nmc, mcclr, mcattr; char mcbuf[BUFSZ]; - int clr = 0; + int clr = NO_COLOR; menucolors_again: nmc = count_menucolors(); @@ -5882,8 +6297,8 @@ handler_menu_colors(void) goto menucolors_done; if (*mcbuf && test_regex_pattern(mcbuf, "MENUCOLORS regex") - && (mcclr = query_color((char *) 0)) != -1 - && (mcattr = query_attr((char *) 0)) != -1 + && (mcclr = query_color((char *) 0, NO_COLOR)) != -1 + && (mcattr = query_attr((char *) 0, ATR_NONE)) != -1 && !add_menu_coloring_parsed(mcbuf, mcclr, mcattr)) { pline("Error adding the menu color."); wait_synch(); @@ -5944,7 +6359,7 @@ handler_menu_colors(void) return optn_ok; } -static int +staticfn int handler_msgtype(void) { winid tmpwin; @@ -5977,7 +6392,7 @@ handler_msgtype(void) const char *mtype; menu_item *pick_list = (menu_item *) 0; struct plinemsg_type *tmp = gp.plinemsg_types; - int clr = 0; + int clr = NO_COLOR; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -6015,7 +6430,7 @@ handler_msgtype(void) return optn_ok; } -static int +staticfn int handler_monstercolor(void) { winid tmpwin; @@ -6042,7 +6457,7 @@ handler_monstercolor(void) /* note: NO_COLOR is valid, despite not being default on any * monsters */ - color = query_color((const char *) 0); + color = query_color((const char *) 0, NO_COLOR); if (color < 0) { /* escaped out of the prompt */ break; } @@ -6105,77 +6520,54 @@ handler_monstercolor(void) return optn_ok; } -#ifndef NO_VERBOSE_GRANULARITY - -DISABLE_WARNING_FORMAT_NONLITERAL - -static int -handler_verbose(int optidx) +staticfn int +handler_versinfo(void) { winid tmpwin; anything any; - char buf[BUFSZ]; - int pick_cnt; - int i; - menu_item *picks = (menu_item *) 0; - static const char *const vbstrings[] = { - " verbose toggle (currently %s)", - " verbose_suppressor[%d] =%08X", - }; - char vbbuf[QBUFSZ]; - int clr = 0; + menu_item *vi_pick = (menu_item *) 0; + boolean have_branch = (nomakedefs.git_branch && *nomakedefs.git_branch); + int n, vi = (int) flags.versinfo; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); any = cg.zeroany; - Snprintf(vbbuf, sizeof vbbuf, vbstrings[0], flags.verbose ? "On" : "Off"); - any.a_int = 1; - add_menu(tmpwin, &nul_glyphinfo, &any, 'a', 0, ATR_NONE, clr, - vbbuf, MENU_ITEMFLAGS_NONE); - for (i = 0; i < vb_elements; i++) { - Snprintf(vbbuf, sizeof vbbuf, vbstrings[1], i, - verbosity_suppressions[i]); - any.a_int = i + 2; - add_menu(tmpwin, &nul_glyphinfo, &any, 'b' + i, 0, - ATR_NONE, clr, vbbuf, MENU_ITEMFLAGS_NONE); - } - end_menu(tmpwin, "Select verbosity choices:"); - - pick_cnt = select_menu(tmpwin, PICK_ANY, &picks); - destroy_nhwindow(tmpwin); - if (pick_cnt > 0) { - int j; - /* PICK_ANY, with one preselected entry (ATR_NONE) which - should be excluded if any other choices were picked */ - for (i = 0; i < pick_cnt; ++i) { - char abuf[BUFSZ]; - j = picks[i].item.a_int - 2; - if (j < 0) { - flags.verbose = !flags.verbose; - } else { - Sprintf(buf, - "Set verbose_suppressor[%d] (%ld) to what new decimal value ?", - j, verbosity_suppressions[j]); - abuf[0] = '\0'; - getlin(buf, abuf); - if (abuf[0] == '\033') - continue; - Sprintf(buf, "%s%d:", allopt[optidx].name, j); - (void) strncat(eos(buf), abuf, (sizeof buf - 1 - strlen(buf))); - /* pass the buck */ - (void) parseoptions(buf, TRUE, TRUE); - } - } - free((genericptr_t) picks), picks = (menu_item *) 0; + + any.a_int = n = VI_NUMBER; /* 1 */ + add_menu(tmpwin, &nul_glyphinfo, &any, 'n', n + '0', ATR_NONE, NO_COLOR, + "version number", + (vi & n) ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE); + any.a_int = n = VI_NAME; /* 2 */ + add_menu(tmpwin, &nul_glyphinfo, &any, 'g', n + '0', ATR_NONE, NO_COLOR, + "game name", + (vi & n) ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE); + any.a_int = n = VI_BRANCH; /* 4 */ + add_menu(tmpwin, &nul_glyphinfo, &any, 'b', n + '0', ATR_NONE, NO_COLOR, + (have_branch ? "development branch" +#if (NH_DEVEL_STATUS == NH_STATUS_RELEASED) + : "(not applicable)" +#else + : "(not available)" +#endif + ), (vi & n) ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE); + + end_menu(tmpwin, "Select version information flags:"); + n = select_menu(tmpwin, PICK_ANY, &vi_pick); + if (n > 0) { + int i, newval = 0; + + for (i = 0; i < n; ++i) + newval |= vi_pick[i].item.a_int; + newval &= 7; + if (newval) + flags.versinfo = (unsigned) newval; + free((genericptr_t) vi_pick); } + destroy_nhwindow(tmpwin); return optn_ok; } -RESTORE_WARNING_FORMAT_NONLITERAL - -#endif - -static int +staticfn int handler_windowborders(void) { winid tmpwin; @@ -6183,7 +6575,7 @@ handler_windowborders(void) int i; const char *mode_name; menu_item *mode_pick = (menu_item *) 0; - int clr = 0; + int clr = NO_COLOR; static const char *const windowborders_text[] = { "Off, never show borders", "On, always show borders", @@ -6199,7 +6591,7 @@ handler_windowborders(void) mode_name = windowborders_text[i]; any.a_int = i + 1; /* index 'i' matches the numeric setting for windowborders, - so allow corresponding digit as group accellerator */ + so allow corresponding digit as group accelerator */ add_menu(tmpwin, &nul_glyphinfo, &any, 'a' + i, '0' + i, ATR_NONE, clr, mode_name, MENU_ITEMFLAGS_NONE); } @@ -6220,7 +6612,7 @@ handler_windowborders(void) ********************************** */ -static char * +staticfn char * string_for_opt(char *opts, boolean val_optional) { char *colon, *equals; @@ -6238,7 +6630,7 @@ string_for_opt(char *opts, boolean val_optional) return colon; } -static char * +staticfn char * string_for_env_opt(const char *optname, char *opts, boolean val_optional) { if (!go.opt_initial) { @@ -6248,7 +6640,7 @@ string_for_env_opt(const char *optname, char *opts, boolean val_optional) return string_for_opt(opts, val_optional); } -static void +staticfn void bad_negation(const char *optname, boolean with_parameter) { config_error_add("The %s option may not %sbe negated.", optname, @@ -6258,7 +6650,7 @@ bad_negation(const char *optname, boolean with_parameter) /* go through all of the options and set the minmatch value based on what is needed for uniqueness of each individual option. Set a minimum of 3 characters. */ -static void +staticfn void determine_ambiguities(void) { int i, j, len, tmpneeded, needed[SIZE(allopt)]; @@ -6294,7 +6686,7 @@ determine_ambiguities(void) } } -static int +staticfn int length_without_val(const char *user_string, int len) { const char *p = strchr(user_string, ':'), @@ -6337,7 +6729,7 @@ reset_duplicate_opt_detection(void) allopt[k].dupdetected = 0; } -static boolean +staticfn boolean duplicate_opt_detection(int optidx) { if (go.opt_initial && go.opt_from_file) @@ -6345,7 +6737,7 @@ duplicate_opt_detection(int optidx) return FALSE; } -static void +staticfn void complain_about_duplicate(int optidx) { char buf[BUFSZ]; @@ -6367,7 +6759,7 @@ complain_about_duplicate(int optidx) return; } -static void +staticfn void rejectoption(const char *optname) { #ifdef MICRO @@ -6416,7 +6808,7 @@ nh_getenv(const char *ev) /* copy up to maxlen-1 characters; 'dest' must be able to hold maxlen; treat comma as alternate end of 'src' */ -static void +staticfn void nmcpy(char *dest, const char *src, int maxlen) { int count; @@ -6451,12 +6843,13 @@ nmcpy(char *dest, const char *src, int maxlen) * an appropriate digit will also fall through to \ and yield 'X' * or 'O', plus stop if the non-digit is end-of-string. */ -static void +staticfn void escapes(const char *cp, /* might be 'tp', updating in place */ char *tp) /* result is never longer than 'cp' */ { - static NEARDATA const char oct[] = "01234567", dec[] = "0123456789", - hex[] = "00112233445566778899aAbBcCdDeEfF"; + static NEARDATA const char oct[] = "01234567", dec[] = "0123456789"; + /* hexdd[] is defined in decl.c */ + const char *dp; int cval, meta, dcount; @@ -6488,11 +6881,11 @@ escapes(const char *cp, /* might be 'tp', updating in place */ cval = (cval * 8) + (*cp - '0'); } while (*++cp && strchr(oct, *cp) && ++dcount < 3); } else if ((cp[1] == 'x' || cp[1] == 'X') && cp[2] - && (dp = strchr(hex, cp[2])) != 0) { + && (dp = strchr(hexdd, cp[2])) != 0) { cp += 2; /* move past backslash and 'X' */ do { - cval = (cval * 16) + ((int) (dp - hex) / 2); - } while (*++cp && (dp = strchr(hex, *cp)) != 0 && ++dcount < 2); + cval = (cval * 16) + ((int) (dp - hexdd) / 2); + } while (*++cp && (dp = strchr(hexdd, *cp)) != 0 && ++dcount < 2); } else { /* C-style character escapes */ switch (*++cp) { case '\\': @@ -6638,8 +7031,15 @@ initoptions(void) { int i; - go.opt_phase = builtin_opt; - initoptions_init(); + /* + * Most places that call initoptions_init()/initoptions() would + * have the calls next to each other, so instead of adding + * initoptions_init() everywhere, just add it where it's needed in + * a non-adjacent place and call it here for all the other cases. + */ + if(go.opt_phase != builtin_opt) + initoptions_init(); + /* * Call each option function with an init flag and give it a chance * to make any preparations that it might require. We do this @@ -6672,6 +7072,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(); } @@ -6683,7 +7088,9 @@ initoptions_init(void) char *opts; #endif int i; + boolean have_branch = (nomakedefs.git_branch && *nomakedefs.git_branch); + go.opt_phase = builtin_opt; /* Did I need to move this here? */ memcpy(allopt, allopt_init, sizeof(allopt)); determine_ambiguities(); @@ -6696,20 +7103,24 @@ initoptions_init(void) config_error_init(FALSE, "command line", FALSE); choose_windows(gc.cmdline_windowsys); config_error_done(); - /* do not free gc.cmdline_windowsys yet unless it was rejected; - keeping it in that situation would complain about it twice */ - if (!windowprocs.name - || strcmpi(windowprocs.name, gc.cmdline_windowsys) != 0) { - free((genericptr_t) gc.cmdline_windowsys), + /* + * FIXME? This continues even if setting windowtype to player's + * specified value fails. It doesn't lock the windowtype in + * that situation though, so the game will use whatever is in + * RC/NETHACKOPTIONS or resort to DEFAULT_WINDOW_SYS. + */ + if (windowprocs.name + && !strcmpi(windowprocs.name, gc.cmdline_windowsys)) + /* ignore any windowtype:foo in RC file or NETHACKOPTIONS */ + iflags.windowtype_locked = TRUE; + /* shouldn't need cmdline_windowsys beyond here */ + free((genericptr_t) gc.cmdline_windowsys), gc.cmdline_windowsys = NULL; - } } -#ifdef ENHANCED_SYMBOLS /* make any symbol parsing quicker */ if (!glyphid_cache_status()) fill_glyphid_cache(); -#endif /* set up the command parsing */ reset_commands(TRUE); /* init */ @@ -6727,7 +7138,8 @@ initoptions_init(void) flags.end_own = FALSE; flags.end_top = 3; flags.end_around = 2; - flags.paranoia_bits = PARANOID_PRAY|PARANOID_SWIM; + flags.paranoia_bits = PARANOID_PRAY | PARANOID_SWIM | PARANOID_TRAP; + flags.versinfo = have_branch ? 4 : 1; flags.pile_limit = PILE_LIMIT_DFLT; /* 5 */ flags.runmode = RUN_LEAP; iflags.msg_history = 20; @@ -6740,7 +7152,8 @@ initoptions_init(void) #endif #endif - iflags.menu_headings = ATR_INVERSE; + iflags.menu_headings.attr = ATR_INVERSE; + iflags.menu_headings.color = NO_COLOR; iflags.getpos_coords = GPCOORDS_NONE; iflags.msg_is_alert = FALSE; @@ -6780,9 +7193,7 @@ initoptions_init(void) if (!gs.symset[PRIMARYSET].explicitly) load_symset("IBMGraphics", PRIMARYSET); switch_symbols(TRUE); -#ifdef TEXTCOLOR iflags.use_color = TRUE; -#endif } #endif /* UNIX && TTY_GRAPHICS */ #if defined(UNIX) || defined(VMS) @@ -6815,6 +7226,7 @@ initoptions_init(void) iflags.wc_align_status = ALIGN_BOTTOM; /* used by tty and curses */ iflags.wc2_statuslines = 2; + iflags.wc2_petattr = ATR_INVERSE; /* only used by curses */ iflags.wc2_windowborders = 2; /* 'Auto' */ @@ -6833,7 +7245,7 @@ initoptions_init(void) /* since this is done before init_objects(), do partial init here */ objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD; - nmcpy(gp.pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ); + nmcpy(svp.pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ); } /* @@ -6929,7 +7341,7 @@ initoptions_finish(void) free((genericptr_t) gc.cmdline_rcfile), gc.cmdline_rcfile = 0; /*[end of nethackrc handling]*/ - (void) fruitadd(gp.pl_fruit, (struct fruit *) 0); + (void) fruitadd(svp.pl_fruit, (struct fruit *) 0); /* * Remove "slime mold" from list of object names. This will * prevent it from being wished unless it's actually present @@ -6977,12 +7389,32 @@ initoptions_finish(void) && wc_supported("tiled_map")) iflags.wc_ascii_map = FALSE, iflags.wc_tiled_map = TRUE; -#ifdef ENHANCED_SYMBOLS + if (iflags.wc_tiled_map && !opt_set_in_config[opt_color]) + iflags.wc_color = TRUE; + if (iflags.wc_ascii_map && !iflags.wc_color + && !opt_set_in_config[opt_bgcolors]) + iflags.bgcolors = FALSE; + if (glyphid_cache_status()) free_glyphid_cache(); - apply_customizations_to_symset(gc.currentgraphics); -#endif + apply_customizations(gc.currentgraphics, + (do_custom_colors | do_custom_symbols)); go.opt_initial = FALSE; + + /* + * Do these after clearing the 'opt_initial' flag. + */ + + /* player's RC file might try to enable perm_invent before selecting + current interface, so the decision then would have been based on + default interface; re-check with the active interface now */ + if (iflags.perm_invent) { + /* can_set_perm_invent() expects to be called when perm_invent + is about to be toggled On, so start with it Off */ + iflags.perm_invent = FALSE; + if (can_set_perm_invent()) + iflags.perm_invent = TRUE; + } return; } @@ -7006,7 +7438,7 @@ initoptions_finish(void) * Used by: optfn_packorder() * */ -static int +staticfn int change_inv_order(char *op) { int oc_sym, num; @@ -7061,7 +7493,7 @@ change_inv_order(char *op) * */ -static boolean +staticfn boolean warning_opts(char *opts, const char *optype) { uchar translate[WARNCOUNT]; @@ -7098,7 +7530,7 @@ assign_warnings(uchar *graph_chars) * */ -static int +staticfn int feature_alert_opts(char *op, const char *optn) { char buf[BUFSZ]; @@ -7184,351 +7616,37 @@ parsebindings(char *bindings) return ret; } } - - /* read the key to be bound */ - key = txt2key(bindings); - if (!key) { - config_error_add("Unknown key binding key '%s'", bindings); - return FALSE; - } - - /* is it a special key? */ - if (bind_specialkey(key, bind)) - return ret; - - /* is it a menu command? */ - for (i = 0; default_menu_cmd_info[i].name; i++) { - if (!strcmp(default_menu_cmd_info[i].name, bind)) { - if (illegal_menu_cmd_key(key)) { - config_error_add("Bad menu key %s:%s", visctrl(key), bind); - return FALSE; - } else { - add_menu_cmd_alias((char) key, default_menu_cmd_info[i].cmd); - } - return ret; - } - } - - /* extended command? */ - if (!bind_key(key, bind)) { - config_error_add("Unknown key binding command '%s'", bind); - return FALSE; - } - return ret; -} - -/* - * Color support functions and data for "color" - * - * Used by: optfn_() - * - */ - -struct color_names { - const char *name; - int color; -}; - -static const struct color_names colornames[] = { - { "black", CLR_BLACK }, - { "red", CLR_RED }, - { "green", CLR_GREEN }, - { "brown", CLR_BROWN }, - { "blue", CLR_BLUE }, - { "magenta", CLR_MAGENTA }, - { "cyan", CLR_CYAN }, - { "gray", CLR_GRAY }, - { "orange", CLR_ORANGE }, - { "light green", CLR_BRIGHT_GREEN }, - { "yellow", CLR_YELLOW }, - { "light blue", CLR_BRIGHT_BLUE }, - { "light magenta", CLR_BRIGHT_MAGENTA }, - { "light cyan", CLR_BRIGHT_CYAN }, - { "white", CLR_WHITE }, - { "no color", NO_COLOR }, - { (const char *) 0, CLR_BLACK }, /* everything after this is an alias */ - { "transparent", NO_COLOR }, - { "purple", CLR_MAGENTA }, - { "light purple", CLR_BRIGHT_MAGENTA }, - { "bright purple", CLR_BRIGHT_MAGENTA }, - { "grey", CLR_GRAY }, - { "bright red", CLR_ORANGE }, - { "bright green", CLR_BRIGHT_GREEN }, - { "bright blue", CLR_BRIGHT_BLUE }, - { "bright magenta", CLR_BRIGHT_MAGENTA }, - { "bright cyan", CLR_BRIGHT_CYAN } -}; - -struct attr_names { - const char *name; - int attr; -}; - -static const struct attr_names attrnames[] = { - { "none", ATR_NONE }, - { "bold", ATR_BOLD }, - { "dim", ATR_DIM }, - { "italic", ATR_ITALIC }, - { "underline", ATR_ULINE }, - { "blink", ATR_BLINK }, - { "inverse", ATR_INVERSE }, - { (const char *) 0, ATR_NONE }, /* everything after this is an alias */ - { "normal", ATR_NONE }, - { "uline", ATR_ULINE }, - { "reverse", ATR_INVERSE }, -}; - -const char * -clr2colorname(int clr) -{ - int i; - - for (i = 0; i < SIZE(colornames); i++) - if (colornames[i].name && colornames[i].color == clr) - return colornames[i].name; - return (char *) 0; -} - -int -match_str2clr(char *str) -{ - int i, c = CLR_MAX; - - /* allow "lightblue", "light blue", and "light-blue" to match "light blue" - (also junk like "_l i-gh_t---b l u e" but we won't worry about that); - also copes with trailing space; caller has removed any leading space */ - for (i = 0; i < SIZE(colornames); i++) - if (colornames[i].name - && fuzzymatch(str, colornames[i].name, " -_", TRUE)) { - c = colornames[i].color; - break; - } - if (i == SIZE(colornames) && digit(*str)) - c = atoi(str); - - if (c < 0 || c >= CLR_MAX) { - config_error_add("Unknown color '%.60s'", str); - c = CLR_MAX; /* "none of the above" */ - } - return c; -} - -static const char * -attr2attrname(int attr) -{ - int i; - - for (i = 0; i < SIZE(attrnames); i++) - if (attrnames[i].attr == attr) - return attrnames[i].name; - return (char *) 0; -} - -int -match_str2attr(const char *str, boolean complain) -{ - int i, a = -1; - - for (i = 0; i < SIZE(attrnames); i++) - if (attrnames[i].name - && fuzzymatch(str, attrnames[i].name, " -_", TRUE)) { - a = attrnames[i].attr; - break; - } - - if (a == -1 && complain) - config_error_add("Unknown text attribute '%.50s'", str); - - return a; -} - -extern const char regex_id[]; /* from sys/share/regex.{c,cpp} */ - -DISABLE_WARNING_FORMAT_NONLITERAL - -/* True: temporarily replace menu color entries with a fake set of menu - colors, { "light blue"=light_blue, "blue"=blue, "red"=red, &c }, that - illustrates most colors for use when the pick-a-color menu is rendered; - suppresses black and white because one of those will likely be invisible - due to matching the background; False: restore user-specified colorings */ -static void -basic_menu_colors(boolean load_colors) -{ - if (load_colors) { - /* replace normal menu colors with a set specifically for colors */ - gs.save_menucolors = iflags.use_menu_color; - gs.save_colorings = gm.menu_colorings; - - iflags.use_menu_color = TRUE; - if (gc.color_colorings) { - /* use the alternate colorings which were set up previously */ - gm.menu_colorings = gc.color_colorings; - } else { - /* create the alternate colorings once */ - char cnm[QBUFSZ]; - int i, c; - boolean pmatchregex = !strcmpi(regex_id, "pmatchregex"); - const char *patternfmt = pmatchregex ? "*%s" : "%s"; - - /* menu_colorings pointer has been saved; clear it in order - to add the alternate entries as if from scratch */ - gm.menu_colorings = (struct menucoloring *) 0; - - /* this orders the patterns last-in/first-out; that means - that the "light " variations come before the basic - "" ones, which is exactly what we want */ - for (i = 0; i < SIZE(colornames); ++i) { - if (!colornames[i].name) /* first alias entry has no name */ - break; - c = colornames[i].color; - if (c == CLR_BLACK || c == CLR_WHITE || c == NO_COLOR) - continue; /* skip these */ - Sprintf(cnm, patternfmt, colornames[i].name); - add_menu_coloring_parsed(cnm, c, ATR_NONE); - } - - /* right now, menu_colorings contains the alternate color list; - remember that list for future pick-a-color instances and - also keep it as is for this instance */ - gc.color_colorings = gm.menu_colorings; - } - } else { - /* restore normal user-specified menu colors */ - iflags.use_menu_color = gs.save_menucolors; - gm.menu_colorings = gs.save_colorings; - } -} - -RESTORE_WARNING_FORMAT_NONLITERAL - -int -query_color(const char *prompt) -{ - winid tmpwin; - anything any; - int i, pick_cnt; - menu_item *picks = (menu_item *) 0; - int clr = 0; - - /* replace user patterns with color name ones and force 'menucolors' On */ - basic_menu_colors(TRUE); - - tmpwin = create_nhwindow(NHW_MENU); - start_menu(tmpwin, MENU_BEHAVE_STANDARD); - any = cg.zeroany; - for (i = 0; i < SIZE(colornames); i++) { - if (!colornames[i].name) - break; - any.a_int = i + 1; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, colornames[i].name, - (colornames[i].color == NO_COLOR) ? MENU_ITEMFLAGS_SELECTED - : MENU_ITEMFLAGS_NONE); - } - end_menu(tmpwin, (prompt && *prompt) ? prompt : "Pick a color"); - pick_cnt = select_menu(tmpwin, PICK_ONE, &picks); - destroy_nhwindow(tmpwin); - - /* remove temporary color name patterns and restore user-specified ones; - reset 'menucolors' option to its previous value */ - basic_menu_colors(FALSE); - - if (pick_cnt > 0) { - i = colornames[picks[0].item.a_int - 1].color; - /* pick_cnt==2: explicitly picked something other than the - preselected entry */ - if (pick_cnt == 2 && i == NO_COLOR) - i = colornames[picks[1].item.a_int - 1].color; - free((genericptr_t) picks); - return i; - } else if (pick_cnt == 0) { - /* pick_cnt==0: explicitly picking preselected entry toggled it off */ - return NO_COLOR; - } - return -1; -} - -/* ask about highlighting attribute; for menu headers and menu - coloring patterns, only one attribute at a time is allowed; - for status highlighting, multiple attributes are allowed [overkill; - life would be much simpler if that were restricted to one also...] */ -int -query_attr(const char *prompt) -{ - winid tmpwin; - anything any; - int i, pick_cnt; - menu_item *picks = (menu_item *) 0; - boolean allow_many = (prompt && !strncmpi(prompt, "Choose", 6)); - int default_attr = ATR_NONE; - int clr = 0; - - if (prompt && strstri(prompt, "menu headings")) - default_attr = iflags.menu_headings; - tmpwin = create_nhwindow(NHW_MENU); - start_menu(tmpwin, MENU_BEHAVE_STANDARD); - any = cg.zeroany; - for (i = 0; i < SIZE(attrnames); i++) { - if (!attrnames[i].name) - break; - any.a_int = i + 1; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - attrnames[i].attr, clr, attrnames[i].name, - (attrnames[i].attr == default_attr) ? MENU_ITEMFLAGS_SELECTED - : MENU_ITEMFLAGS_NONE); + + /* read the key to be bound */ + key = txt2key(bindings); + if (!key) { + config_error_add("Unknown key binding key '%s'", bindings); + return FALSE; } - end_menu(tmpwin, (prompt && *prompt) ? prompt : "Pick an attribute"); - pick_cnt = select_menu(tmpwin, allow_many ? PICK_ANY : PICK_ONE, &picks); - destroy_nhwindow(tmpwin); - if (pick_cnt > 0) { - int j, k = 0; - - if (allow_many) { - /* PICK_ANY, with one preselected entry (ATR_NONE) which - should be excluded if any other choices were picked */ - for (i = 0; i < pick_cnt; ++i) { - j = picks[i].item.a_int - 1; - if (attrnames[j].attr != ATR_NONE || pick_cnt == 1) { - switch (attrnames[j].attr) { - case ATR_DIM: - k |= HL_DIM; - break; - case ATR_BLINK: - k |= HL_BLINK; - break; - case ATR_ULINE: - k |= HL_ULINE; - break; - case ATR_INVERSE: - k |= HL_INVERSE; - break; - case ATR_BOLD: - k |= HL_BOLD; - break; - case ATR_NONE: - k = HL_NONE; - break; - } - } + + /* is it a special key? */ + if (bind_specialkey(key, bind)) + return ret; + + /* is it a menu command? */ + for (i = 0; default_menu_cmd_info[i].name; i++) { + if (!strcmp(default_menu_cmd_info[i].name, bind)) { + if (illegal_menu_cmd_key(key)) { + config_error_add("Bad menu key %s:%s", visctrl(key), bind); + return FALSE; + } else { + add_menu_cmd_alias((char) key, default_menu_cmd_info[i].cmd); } - } else { - /* PICK_ONE, but might get 0 or 2 due to preselected entry */ - j = picks[0].item.a_int - 1; - /* pick_cnt==2: explicitly picked something other than the - preselected entry */ - if (pick_cnt == 2 && attrnames[j].attr == default_attr) - j = picks[1].item.a_int - 1; - k = attrnames[j].attr; + return ret; } - free((genericptr_t) picks); - return k; - } else if (pick_cnt == 0 && !allow_many) { - /* PICK_ONE, preselected entry explicitly chosen */ - return default_attr; } - /* either ESC to explicitly cancel (pick_cnt==-1) or - PICK_ANY with preselected entry toggled off and nothing chosen */ - return -1; + + /* extended command? */ + if (!bind_key(key, bind)) { + config_error_add("Unknown key binding command '%s'", bind); + return FALSE; + } + return ret; } static const struct { @@ -7546,7 +7664,7 @@ static const struct { { "norep", MSGTYP_NOREP, "Do not repeat the message" } }; -static const char * +staticfn const char * msgtype2name(int typ) { int i; @@ -7557,14 +7675,14 @@ msgtype2name(int typ) return (char *) 0; } -static int +staticfn int query_msgtype(void) { winid tmpwin; anything any; int i, pick_cnt; menu_item *picks = (menu_item *) 0; - int clr = 0; + int clr = NO_COLOR; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -7587,7 +7705,7 @@ query_msgtype(void) return -1; } -static boolean +staticfn boolean msgtype_add(int typ, char *pattern) { static const char *const re_error = "MSGTYPE regex error"; @@ -7627,7 +7745,7 @@ msgtype_free(void) gp.plinemsg_types = (struct plinemsg_type *) 0; } -static void +staticfn void free_one_msgtype(int idx) /* 0 .. */ { struct plinemsg_type *tmp = gp.plinemsg_types; @@ -7686,7 +7804,7 @@ hide_unhide_msgtypes(boolean hide, int hide_mask) } } -static int +staticfn int msgtype_count(void) { int c = 0; @@ -7726,7 +7844,7 @@ msgtype_parse_add(char *str) /* parse 'str' as a regular expression to check whether it's valid; compiled regexp gets thrown away regardless of the outcome */ -static boolean +staticfn boolean test_regex_pattern(const char *str, const char *errmsg) { static const char def_errmsg[] = "NHregex error"; @@ -7759,156 +7877,6 @@ test_regex_pattern(const char *str, const char *errmsg) return retval; } -static boolean -add_menu_coloring_parsed(const char *str, int c, int a) -{ - static const char re_error[] = "Menucolor regex error"; - struct menucoloring *tmp; - - if (!str) - return FALSE; - tmp = (struct menucoloring *) alloc(sizeof *tmp); - tmp->match = regex_init(); - /* test_regex_pattern() has already validated this regexp but parsing - it again could conceivably run out of memory */ - if (!regex_compile(str, tmp->match)) { - char errbuf[BUFSZ]; - char *re_error_desc = regex_error_desc(tmp->match, errbuf); - - /* free first in case reason for regcomp failure was out-of-memory */ - regex_free(tmp->match); - free((genericptr_t) tmp); - config_error_add("%s: %s", re_error, re_error_desc); - return FALSE; - } - tmp->next = gm.menu_colorings; - tmp->origstr = dupstr(str); - tmp->color = c; - tmp->attr = a; - gm.menu_colorings = tmp; - iflags.use_menu_color = TRUE; - return TRUE; -} - -/* parse '"regex_string"=color&attr' and add it to menucoloring */ -boolean -add_menu_coloring(char *tmpstr) /* never Null but could be empty */ -{ - int c = NO_COLOR, a = ATR_NONE; - char *tmps, *cs, *amp; - char str[BUFSZ]; - - (void) strncpy(str, tmpstr, sizeof str - 1); - str[sizeof str - 1] = '\0'; - - if ((cs = strchr(str, '=')) == 0) { - config_error_add("Malformed MENUCOLOR"); - return FALSE; - } - - tmps = cs + 1; /* advance past '=' */ - mungspaces(tmps); - if ((amp = strchr(tmps, '&')) != 0) - *amp = '\0'; - - c = match_str2clr(tmps); - if (c >= CLR_MAX) - return FALSE; - - if (amp) { - tmps = amp + 1; /* advance past '&' */ - a = match_str2attr(tmps, TRUE); - if (a == -1) - return FALSE; - } - - /* the regexp portion here has not been condensed by mungspaces() */ - *cs = '\0'; - tmps = str; - if (*tmps == '"' || *tmps == '\'') { - cs--; - while (isspace((uchar) *cs)) - cs--; - if (*cs == *tmps) { - *cs = '\0'; - tmps++; - } - } - return add_menu_coloring_parsed(tmps, c, a); -} - -boolean -get_menu_coloring(const char *str, int *color, int *attr) -{ - struct menucoloring *tmpmc; - - if (iflags.use_menu_color) - for (tmpmc = gm.menu_colorings; tmpmc; tmpmc = tmpmc->next) - if (regex_match(str, tmpmc->match)) { - *color = tmpmc->color; - *attr = tmpmc->attr; - return TRUE; - } - return FALSE; -} - -/* release all menu color patterns */ -void -free_menu_coloring(void) -{ - /* either menu_colorings or color_colorings or both might need to - be freed or already be Null; do-loop will iterate at most twice */ - do { - struct menucoloring *tmp, *tmp2; - - for (tmp = gm.menu_colorings; tmp; tmp = tmp2) { - tmp2 = tmp->next; - regex_free(tmp->match); - free((genericptr_t) tmp->origstr); - free((genericptr_t) tmp); - } - gm.menu_colorings = gc.color_colorings; - gc.color_colorings = (struct menucoloring *) 0; - } while (gm.menu_colorings); -} - -/* release a specific menu color pattern; not used for color_colorings */ -static void -free_one_menu_coloring(int idx) /* 0 .. */ -{ - struct menucoloring *tmp = gm.menu_colorings; - struct menucoloring *prev = NULL; - - while (tmp) { - if (idx == 0) { - struct menucoloring *next = tmp->next; - - regex_free(tmp->match); - free((genericptr_t) tmp->origstr); - free((genericptr_t) tmp); - if (prev) - prev->next = next; - else - gm.menu_colorings = next; - return; - } - idx--; - prev = tmp; - tmp = tmp->next; - } -} - -static int -count_menucolors(void) -{ - struct menucoloring *tmp; - int count = 0; - - for (tmp = gm.menu_colorings; tmp; tmp = tmp->next) - count++; - return count; -} - /* Add a monster color specification. * The string is expected to be of the format "monster:color", either direct * from the config (we need to check syntax) or from the interactive option. */ @@ -7936,8 +7904,8 @@ add_monstercolor(char *str) return FALSE; } - color = match_str2clr(colorstr); /* if invalid color, prints error and - returns CLR_MAX */ + color = match_str2clr(colorstr, FALSE); /* if invalid color, prints error + and returns CLR_MAX */ if (color == CLR_MAX) { return FALSE; } @@ -7951,7 +7919,7 @@ add_monstercolor(char *str) } /* parse 'role' or 'race' or 'gender' or 'alignment' */ -static boolean +staticfn boolean parse_role_opt( int optidx, boolean negated, @@ -8067,7 +8035,7 @@ parse_role_opt( /* fetch a saved role|race|gender|alignment value suitable for writing into a new run-time config file */ -static char * +staticfn char * get_cnf_role_opt(int optidx) { int phase; @@ -8083,7 +8051,7 @@ get_cnf_role_opt(int optidx) } /* Check if character c is illegal as a menu command key */ -static boolean +staticfn boolean illegal_menu_cmd_key(uchar c) { if (c == 0 || c == '\r' || c == '\n' || c == '\033' || c == ' ' @@ -8219,11 +8187,11 @@ collect_menu_keys( int fruitadd(char *str, struct fruit *replace_fruit) { - register int i; - register struct fruit *f; + int i; + struct fruit *f; int highest_fruit_id = 0, globpfx; char buf[PL_FSIZ], altname[PL_FSIZ]; - boolean user_specified = (str == gp.pl_fruit); + boolean user_specified = (str == svp.pl_fruit); /* if not user-specified, then it's a fruit name for a fruit on * a bones level or from orctown raider's loot... */ @@ -8239,21 +8207,22 @@ fruitadd(char *str, struct fruit *replace_fruit) they already received it in their original game; str==pl_fruit but makesingular() creates a copy so we need to copy that back into pl_fruit */ - nmcpy(gp.pl_fruit, makesingular(str), PL_FSIZ); + nmcpy(svp.pl_fruit, makesingular(str), PL_FSIZ); /* disallow naming after other foods (since it'd be impossible * to tell the difference); globs might have a size prefix which * needs to be skipped in order to match the object type name */ - globpfx = (!strncmp(gp.pl_fruit, "small ", 6) - || !strncmp(gp.pl_fruit, "large ", 6)) ? 6 - : (!strncmp(gp.pl_fruit, "medium ", 7)) ? 7 - : (!strncmp(gp.pl_fruit, "very large ", 11)) ? 11 + globpfx = (!strncmp(svp.pl_fruit, "small ", 6) + || !strncmp(svp.pl_fruit, "large ", 6)) ? 6 + : (!strncmp(svp.pl_fruit, "medium ", 7)) ? 7 + : (!strncmp(svp.pl_fruit, "very large ", 11)) ? 11 : 0; - for (i = gb.bases[FOOD_CLASS]; objects[i].oc_class == FOOD_CLASS; i++) { - if (!strcmp(OBJ_NAME(objects[i]), gp.pl_fruit) - || (globpfx > 0 - && !strcmp(OBJ_NAME(objects[i]), &gp.pl_fruit[globpfx]))) { + for (i = svb.bases[FOOD_CLASS]; objects[i].oc_class == FOOD_CLASS; + i++) { + if (!strcmp(OBJ_NAME(objects[i]), svp.pl_fruit) + || (globpfx > 0 && !strcmp(OBJ_NAME(objects[i]), + &svp.pl_fruit[globpfx]))) { found = TRUE; break; } @@ -8261,7 +8230,7 @@ fruitadd(char *str, struct fruit *replace_fruit) if (!found) { char *c; - for (c = gp.pl_fruit; *c >= '0' && *c <= '9'; c++) + for (c = svp.pl_fruit; *c >= '0' && *c <= '9'; c++) continue; if (!*c || isspace((uchar) *c)) numeric = TRUE; @@ -8270,22 +8239,22 @@ fruitadd(char *str, struct fruit *replace_fruit) /* these checks for applying food attributes to actual items are case sensitive; "glob of foo" is caught by 'found' if 'foo' is a valid glob; when not valid, allow it as-is */ - || !strncmp(gp.pl_fruit, "cursed ", 7) - || !strncmp(gp.pl_fruit, "uncursed ", 9) - || !strncmp(gp.pl_fruit, "blessed ", 8) - || !strncmp(gp.pl_fruit, "partly eaten ", 13) - || (!strncmp(gp.pl_fruit, "tin of ", 7) - && (!strcmp(gp.pl_fruit + 7, "spinach") - || name_to_mon(gp.pl_fruit + 7, (int *) 0) >= LOW_PM)) - || !strcmp(gp.pl_fruit, "empty tin") - || (!strcmp(gp.pl_fruit, "glob") - || (globpfx > 0 && !strcmp("glob", &gp.pl_fruit[globpfx]))) - || ((str_end_is(gp.pl_fruit, " corpse") - || str_end_is(gp.pl_fruit, " egg")) - && name_to_mon(gp.pl_fruit, (int *) 0) >= LOW_PM)) { - Strcpy(buf, gp.pl_fruit); - Strcpy(gp.pl_fruit, "candied "); - nmcpy(gp.pl_fruit + 8, buf, PL_FSIZ - 8); + || !strncmp(svp.pl_fruit, "cursed ", 7) + || !strncmp(svp.pl_fruit, "uncursed ", 9) + || !strncmp(svp.pl_fruit, "blessed ", 8) + || !strncmp(svp.pl_fruit, "partly eaten ", 13) + || (!strncmp(svp.pl_fruit, "tin of ", 7) + && (!strcmp(svp.pl_fruit + 7, "spinach") + || ismnum(name_to_mon(svp.pl_fruit + 7, (int *) 0)))) + || !strcmp(svp.pl_fruit, "empty tin") + || (!strcmp(svp.pl_fruit, "glob") + || (globpfx > 0 && !strcmp("glob", &svp.pl_fruit[globpfx]))) + || ((str_end_is(svp.pl_fruit, " corpse") + || str_end_is(svp.pl_fruit, " egg")) + && ismnum(name_to_mon(svp.pl_fruit, (int *) 0)))) { + Strcpy(buf, svp.pl_fruit); + Strcpy(svp.pl_fruit, "candied "); + nmcpy(svp.pl_fruit + 8, buf, PL_FSIZ - 8); } *altname = '\0'; /* This flag indicates that a fruit has been made since the @@ -8300,7 +8269,7 @@ fruitadd(char *str, struct fruit *replace_fruit) /* replace_fruit is already part of the fruit chain; update it in place rather than looking it up again */ f = replace_fruit; - copynchars(f->fname, gp.pl_fruit, PL_FSIZ - 1); + copynchars(f->fname, svp.pl_fruit, PL_FSIZ - 1); goto nonew; } } else { @@ -8331,7 +8300,7 @@ fruitadd(char *str, struct fruit *replace_fruit) gf.ffruit = f; nonew: if (user_specified) - gc.context.current_fruit = f->fid; + svc.context.current_fruit = f->fid; return f->fid; } @@ -8344,18 +8313,9 @@ fruitadd(char *str, struct fruit *replace_fruit) ********************************** */ - -#if defined(MICRO) || defined(MAC) || defined(WIN32) -#define OPTIONS_HEADING "OPTIONS" -#else -#define OPTIONS_HEADING "XNETHACKOPTIONS" -#endif - -static const char n_currently_set[] = "(%d currently set)"; - DISABLE_WARNING_FORMAT_NONLITERAL /* RESTORE is after show_menucontrols() */ -static int +staticfn int optfn_o_autopickup_exceptions( int optidx UNUSED, int req, boolean negated UNUSED, char *opts, char *op UNUSED) @@ -8377,7 +8337,7 @@ optfn_o_autopickup_exceptions( return optn_ok; } -static int +staticfn int optfn_o_bind_keys( int optidx UNUSED, int req, boolean negated UNUSED, char *opts, char *op UNUSED) @@ -8399,7 +8359,29 @@ optfn_o_bind_keys( return optn_ok; } -static int +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) { @@ -8420,9 +8402,13 @@ optfn_o_menu_colors(int optidx UNUSED, int req, boolean negated UNUSED, return optn_ok; } -static int -optfn_o_message_types(int optidx UNUSED, int req, boolean negated UNUSED, - char *opts, char *op UNUSED) +staticfn int +optfn_o_message_types( + int optidx UNUSED, + int req, + boolean negated UNUSED, + char *opts, + char *op UNUSED) { if (req == do_init) { return optn_ok; @@ -8441,7 +8427,7 @@ optfn_o_message_types(int optidx UNUSED, int req, boolean negated UNUSED, return optn_ok; } -static int +staticfn int optfn_o_status_cond( int optidx UNUSED, int req, @@ -8465,7 +8451,8 @@ optfn_o_status_cond( ; /* handled inline by all_options_strbuf() via all_options_conds() */ } if (req == do_handler) { - cond_menu(); + if (cond_menu()) + opt_set_in_config[pfx_cond_] = TRUE; return optn_ok; } return optn_ok; @@ -8493,7 +8480,7 @@ optfn_o_monstercolor(int optidx UNUSED, int req, boolean negated UNUSED, } #ifdef STATUS_HILITES -static int +staticfn int optfn_o_status_hilites( int optidx UNUSED, int req, @@ -8555,7 +8542,7 @@ get_option_value(const char *optname, boolean cnfvalid) return (char *) 0; } -static unsigned int +staticfn unsigned int longest_option_name(int startpass, int endpass) { /* spin through the options to find the longest name */ @@ -8583,7 +8570,7 @@ longest_option_name(int startpass, int endpass) } /* guts of doset_simple(); called repeatedly until no choice is made */ -static int +staticfn int doset_simple_menu(void) { /* unlike doset()'s fmtstr, there is no leading %s for indentation */ @@ -8613,24 +8600,27 @@ doset_simple_menu(void) tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); + /* when showing 'help', also describe how to run full doset() */ + if (gs.simple_options_help) { + /* we could look up whether #optionsfull has been bound to a key + and show that, or whether #reqmenu and #options are both still + bound to keys and show those, but if meta keys are involved + the player might not know how to type them; keep this simple */ + Strcpy(buf, "Use command '#optionsfull'" + " to get the complete options list."); + add_menu_str(tmpwin, buf); + } any = cg.zeroany; any.a_int = -2 + 1; - add_menu(tmpwin, &nul_glyphinfo, &any, '?', 0, ATR_NONE, 0, + add_menu(tmpwin, &nul_glyphinfo, &any, '?', 0, ATR_NONE, NO_COLOR, gs.simple_options_help ? "hide help" : "show help", MENU_ITEMFLAGS_NONE); - any = cg.zeroany; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - 0, "", MENU_ITEMFLAGS_NONE); for (section = OptS_General; section < OptS_Advanced; section++) { any = cg.zeroany; - if (section != OptS_General) - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - 0, "", MENU_ITEMFLAGS_NONE); + add_menu_str(tmpwin, ""); Sprintf(buf, " %-30s ", OptS_type[section]); - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - iflags.menu_headings, 0, - buf /*OptS_type[section]*/, MENU_ITEMFLAGS_NONE); + add_menu_heading(tmpwin, buf); for (i = 0; (name = allopt[i].name) != 0; i++) { if (allopt[i].section != section) continue; @@ -8668,18 +8658,16 @@ doset_simple_menu(void) /* pickup_types is separated from autopickup due to the spelling of their names; emphasize what it means */ if (allopt[i].idx == opt_pickup_types - || allopt[i].idx == opt_pickup_thrown) + || allopt[i].idx == opt_pickup_thrown + || allopt[i].idx == opt_pickup_stolen + || allopt[i].idx == opt_dropped_nopick) Strcat(buf, " (for autopickup)"); add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, 0, buf, MENU_ITEMFLAGS_NONE); + ATR_NONE, NO_COLOR, buf, MENU_ITEMFLAGS_NONE); if (gs.simple_options_help && allopt[i].descr) { - any = cg.zeroany; Sprintf(buf, " %s", allopt[i].descr); - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, 0, buf, MENU_ITEMFLAGS_NONE); - any = cg.zeroany; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - 0, "", MENU_ITEMFLAGS_NONE); + add_menu_str(tmpwin, buf); + add_menu_str(tmpwin, ""); } } } @@ -8687,6 +8675,9 @@ doset_simple_menu(void) go.opt_need_redraw = FALSE; go.opt_need_glyph_reset = FALSE; + go.opt_reset_customcolors = FALSE; + go.opt_reset_customsymbols = FALSE; + go.opt_update_basic_palette = FALSE; pick_cnt = select_menu(tmpwin, PICK_ONE, &pick_list); /* note: without the complication of a preselected entry, a PICK_ONE menu returning pick_cnt > 0 implies exactly 1 */ @@ -8707,7 +8698,7 @@ doset_simple_menu(void) reslt = (*allopt[k].optfn)(allopt[k].idx, do_handler, FALSE, empty_optstr, empty_optstr); /* if player eventually saves options, include this one */ - if (reslt == optn_ok) + if (reslt == optn_ok && allopt[k].idx != pfx_cond_) opt_set_in_config[k] = TRUE; } else { Sprintf(buf, "Set %s to what?", allopt[k].name); @@ -8747,7 +8738,8 @@ doset_simple_menu(void) int doset_simple(void) { - int pickedone = 0; + int pickedone = 0, + opt_crt_flags = docrtNocls; if (iflags.menu_requested) { /* doset() checks for 'm' and calls doset_simple(); clear the @@ -8771,18 +8763,37 @@ doset_simple(void) if (go.opt_need_redraw) { check_gold_symbol(); reglyph_darkroom(); - docrt(); + if (go.opt_symset_changed) + opt_crt_flags &= ~docrtRefresh; + docrt_flags(opt_crt_flags); flush_screen(1); } - if (gc.context.botl || gc.context.botlx) { + if (go.opt_need_promptstyle) + adjust_menu_promptstyle(WIN_INVEN, &iflags.menu_headings); + if (go.opt_update_basic_palette) { +#ifdef CHANGE_COLOR + change_palette(); +#endif + go.opt_update_basic_palette = FALSE; + } + if (go.opt_reset_customcolors || go.opt_reset_customsymbols) { + if (go.opt_reset_customcolors) + reset_customcolors(); + if (go.opt_reset_customsymbols) + reset_customsymbols(); + docrt_flags(opt_crt_flags); + } + /* status may need updating if terminal is tall enough that + doset_simple menu doesn't cover up status or wide enough for + curses to honor player's choice of align_status:Right|Left */ + if (disp.botl || disp.botlx) bot(); - } } while (pickedone > 0); give_opt_msg = TRUE; return ECMD_OK; } -static const char * +staticfn const char * term_for_boolean(int idx, boolean *b) { int i, f_t = (*b) ? 1: 0; @@ -8799,6 +8810,8 @@ term_for_boolean(int idx, boolean *b) return boolean_term; } +#define HELP_IDX (SIZE(allopt)) + /* the #optionsfull command */ int doset(void) /* changing options via menu by Per Liboriussen */ @@ -8813,9 +8826,8 @@ doset(void) /* changing options via menu by Per Liboriussen */ anything any; menu_item *pick_list; int indexoffset, startpass, endpass; - boolean setinitial = FALSE, fromfile = FALSE, - gavehelp = FALSE, skiphelp = !iflags.cmdassist; - int clr = 0; + boolean gavehelp = FALSE, skiphelp = !iflags.cmdassist; + int clr = NO_COLOR; if (iflags.menu_requested) { /* doset_simple() checks for 'm' and calls doset(); clear the @@ -8838,18 +8850,17 @@ doset(void) /* changing options via menu by Per Liboriussen */ "For a brief explanation of how this works, type '?' to select", "the next menu choice, then press or .", NULL, /* actual '?' menu entry gets inserted here */ - "[To suppress this menu help, toggle off the 'cmdassist' option.]", + ("[To suppress this menu help," + " toggle off the 'cmdassist' option.]"), "", }; any = cg.zeroany; for (i = 0; i < SIZE(helptext); ++i) { if (helptext[i]) { Sprintf(buf, "%4s%.75s", "", helptext[i]); - any.a_int = 0; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, FALSE, clr, - buf, MENU_ITEMFLAGS_NONE); + add_menu_str(tmpwin, buf); } else { - any.a_int = '?' + 1; /* processing pick_list subtracts 1 */ + any.a_int = HELP_IDX + 1; /* handling pick_list subtracts 1 */ add_menu(tmpwin, &nul_glyphinfo, &any, '?', '?', ATR_NONE, clr, "view help for options menu", MENU_ITEMFLAGS_SKIPINVERT); @@ -8876,8 +8887,7 @@ doset(void) /* changing options via menu by Per Liboriussen */ indexoffset = 1; any = cg.zeroany; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, iflags.menu_headings, clr, - "Booleans (selecting will toggle value):", MENU_ITEMFLAGS_NONE); + add_menu_heading(tmpwin, "Booleans (selecting will toggle value):"); any.a_int = 0; /* first list any other non-modifiable booleans, then modifiable ones */ for (pass = 0; pass <= 1; pass++) @@ -8907,12 +8917,10 @@ doset(void) /* changing options via menu by Per Liboriussen */ add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, buf, MENU_ITEMFLAGS_NONE); } - any = cg.zeroany; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, iflags.menu_headings, clr, - "Compounds (selecting will prompt for new value):", - MENU_ITEMFLAGS_NONE); + + add_menu_str(tmpwin, ""); + add_menu_heading(tmpwin, + "Compounds (selecting will prompt for new value):"); for (pass = startpass; pass <= endpass; pass++) for (i = 0; (name = allopt[i].name) != 0; i++) { @@ -8928,12 +8936,8 @@ doset(void) /* changing options via menu by Per Liboriussen */ } } - any = cg.zeroany; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - iflags.menu_headings, clr, - "Other settings:", MENU_ITEMFLAGS_NONE); + add_menu_str(tmpwin, ""); + add_menu_heading(tmpwin, "Other settings:"); for (pass = startpass; pass <= endpass; pass++) for (i = 0; (name = allopt[i].name) != 0; i++) { @@ -8950,18 +8954,15 @@ doset(void) /* changing options via menu by Per Liboriussen */ } #ifdef PREFIXES_IN_USE - any = cg.zeroany; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - iflags.menu_headings, clr, - "Variable playground locations:", MENU_ITEMFLAGS_NONE); + add_menu_str(tmpwin, ""); + add_menu_heading(tmpwin, "Variable playground locations:"); for (i = 0; i < PREFIX_COUNT; i++) doset_add_menu(tmpwin, fqn_prefix_names[i], fmtstr_doset, -1, 0); #endif end_menu(tmpwin, "Set what options?"); go.opt_need_redraw = FALSE; go.opt_need_glyph_reset = FALSE; + if ((pick_cnt = select_menu(tmpwin, PICK_ANY, &pick_list)) > 0) { /* * Walk down the selection list and either invert the booleans @@ -8971,7 +8972,7 @@ doset(void) /* changing options via menu by Per Liboriussen */ */ for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) { opt_indx = pick_list[pick_idx].item.a_int - 1; - if (opt_indx == '?') { + if (opt_indx == HELP_IDX) { display_file(OPTMENUHELP, FALSE); gavehelp = TRUE; continue; /* just handled '?'; there might be more picks */ @@ -8983,10 +8984,10 @@ doset(void) /* changing options via menu by Per Liboriussen */ /* boolean option */ Sprintf(buf, "%s%s", *allopt[opt_indx].addr ? "!" : "", allopt[opt_indx].name); - (void) parseoptions(buf, setinitial, fromfile); + (void) parseoptions(buf, FALSE, FALSE); } else { /* compound option */ - int k = opt_indx, reslt UNUSED; + int k = opt_indx, reslt; if (allopt[k].has_handler && allopt[k].optfn) { reslt = (*allopt[k].optfn)(allopt[k].idx, do_handler, @@ -9007,7 +9008,7 @@ doset(void) /* changing options via menu by Per Liboriussen */ (void) strncat(eos(buf), abuf, (sizeof buf - 1 - strlen(buf))); /* pass the buck */ - (void) parseoptions(buf, setinitial, fromfile); + (void) parseoptions(buf, FALSE, FALSE); } } if (wc_supported(allopt[opt_indx].name) @@ -9030,19 +9031,37 @@ doset(void) /* changing options via menu by Per Liboriussen */ if (go.opt_need_glyph_reset) { reset_glyphmap(gm_optionchange); } - if (go.opt_need_redraw) { - check_gold_symbol(); - reglyph_darkroom(); + if (go.opt_reset_customcolors || go.opt_update_basic_palette + || go.opt_reset_customsymbols || go.opt_need_redraw) { + if (go.opt_update_basic_palette) { +#ifdef CHANGE_COLOR + change_palette(); +#endif + go.opt_update_basic_palette = FALSE; + } + if (go.opt_reset_customcolors) + reset_customcolors(); + if (go.opt_reset_customsymbols) + reset_customsymbols(); + if (go.opt_need_redraw) { + check_gold_symbol(); + reglyph_darkroom(); + } docrt(); } - if (gc.context.botl || gc.context.botlx) { + if (go.opt_need_promptstyle) { + adjust_menu_promptstyle(WIN_INVEN, &iflags.menu_headings); + } + if (disp.botl || disp.botlx) { bot(); } return ECMD_OK; } +#undef HELP_IDX + /* doset(#optionsfull command) menu entries for compound options */ -static void +staticfn void doset_add_menu( winid win, /* window to add to */ const char *option, /* option name */ @@ -9059,7 +9078,7 @@ doset_add_menu( #ifdef PREFIXES_IN_USE int j; #endif - int clr = 0; + int clr = NO_COLOR; buf2[0] = '\0'; /* per opt functs may not guarantee this, so do it */ any = cg.zeroany; @@ -9203,7 +9222,7 @@ show_menu_controls(winid win, boolean dolist) RESTORE_WARNING_FORMAT_NONLITERAL -static int +staticfn int count_cond(void) { int i, cnt = 0; @@ -9215,7 +9234,7 @@ count_cond(void) return cnt; } -static int +staticfn int count_apes(void) { int numapes = 0; @@ -9229,7 +9248,7 @@ count_apes(void) return numapes; } -static int +staticfn int count_monstercolors(void) { int i, numcolors = 0; @@ -9244,7 +9263,7 @@ count_monstercolors(void) DISABLE_WARNING_FORMAT_NONLITERAL /* common to msg-types, menu-colors, autopickup-exceptions, monster-colors */ -static int +staticfn int handle_add_list_remove(const char *optname, int numtotal) { winid tmpwin; @@ -9260,7 +9279,7 @@ handle_add_list_remove(const char *optname, int numtotal) { 'r', "remove existing %s" }, /* [2] */ { 'x', "exit this menu" }, /* [3] */ }; - int clr = 0; + int clr = NO_COLOR; opt_idx = 0; tmpwin = create_nhwindow(NHW_MENU); @@ -9363,7 +9382,7 @@ add_autopickup_exception(const char *mapping) return 1; } -static void +staticfn void remove_autopickup_exception(struct autopickup_exception *whichape) { struct autopickup_exception *ape, *freeape, *prev = 0; @@ -9403,7 +9422,7 @@ int sym_val(const char *strval) /* up to 4*BUFSZ-1 long; only first few chars matter */ { - char buf[QBUFSZ], tmp[QBUFSZ]; /* to hold trucated copy of 'strval' */ + char buf[QBUFSZ], tmp[QBUFSZ]; /* to hold truncated copy of 'strval' */ buf[0] = '\0'; if (!strval[0] || !strval[1]) { /* empty, or single character */ @@ -9458,7 +9477,8 @@ static const char *opt_intro[] = { #endif "or press \"O\" while playing and use the menu.", "", - "Boolean options (which can be negated by prefixing them with '!' or \"no\"):", + ("Boolean options (which can be negated by prefixing them" + " with '!' or \"no\"):"), (char *) 0 }; @@ -9480,7 +9500,7 @@ option_help(void) { char buf[BUFSZ], buf2[BUFSZ]; const char *optname; - register int i; + int i; winid datawin; datawin = create_nhwindow(NHW_TEXT); @@ -9565,11 +9585,16 @@ option_help(void) return; } -static void +/* gather all non-default cond_xyz options into one OPTIONS=cond_foo,!cond_bar + entry spread across multiple lines with backslash+newline if needed; + conditions with their default settings (cond_blind, !cond_glowhands, &c) + are excluded */ +staticfn void all_options_conds(strbuf_t *sbuf) { char buf[BUFSZ], nextcond[BUFSZ]; int idx = 0; + boolean gotone = FALSE; buf[0] = '\0'; while (opt_next_cond(idx, nextcond)) { @@ -9583,19 +9608,27 @@ all_options_conds(strbuf_t *sbuf) strbuf_append(sbuf, buf); /* indent continuation line */ Sprintf(buf, "%8s", " "); /* 8: strlen("OPTIONS=") */ - } else { + } else if (nextcond[0] && gotone) { Strcat(buf, ","); } - Strcat(buf, nextcond); + if (nextcond[0]) { + gotone = TRUE; + Strcat(buf, nextcond); + } ++idx; } - /* finish off final line */ - Strcat(buf, "\n"); - strbuf_append(sbuf, buf); + /* finish off final line; value might be empty if one or more cond_xyz + options were changed in such a manner that they're all back to their + default values--which will produce "OPTIONS=" with nothing after the + equals sign; only add to the output when there is more present */ + if (strcmp(buf, "OPTIONS=")) { + Strcat(buf, "\n"); + strbuf_append(sbuf, buf); + } } /* append menucolor lines to strbuf */ -static void +staticfn void all_options_menucolors(strbuf_t *sbuf) { int i = 0, ncolors = count_menucolors(); @@ -9628,7 +9661,7 @@ all_options_menucolors(strbuf_t *sbuf) free(arr); } -static void +staticfn void all_options_msgtypes(strbuf_t *sbuf) { struct plinemsg_type *tmp = gp.plinemsg_types; @@ -9643,7 +9676,7 @@ all_options_msgtypes(strbuf_t *sbuf) } } -static void +staticfn void all_options_apes(strbuf_t *sbuf) { struct autopickup_exception *tmp = ga.apelist; @@ -9657,6 +9690,26 @@ all_options_apes(strbuf_t *sbuf) } } +#ifdef CHANGE_COLOR +staticfn void +all_options_palette(strbuf_t *sbuf) +{ + int clr, n = count_alt_palette(); + char buf[BUFSZ]; + + if (!n) + return; + + for (clr = 0; clr < CLR_MAX; ++clr) { + if (ga.altpalette[clr] != 0U) { + Sprintf(buf, "OPTIONS=palette:%s/#%06x\n", + clr2colorname(clr), COLORVAL(ga.altpalette[clr])); + strbuf_append(sbuf, buf); + } + } +} +#endif /* CHANGE_COLOR */ + /* return strbuf of all options, to write to file */ void all_options_strbuf(strbuf_t *sbuf) @@ -9692,10 +9745,7 @@ all_options_strbuf(strbuf_t *sbuf) break; /* FIXME: get_option_value for: - menu_deselect_all &c menu control keys, - - mouse_support - - pettype - - term_cols, term_rows - - verbose */ + - term_cols, term_rows */ buf2 = get_option_value(name, TRUE); if (buf2) { Snprintf(tmp, sizeof tmp - 1, "OPTIONS=%s:%s", name, buf2); @@ -9709,15 +9759,21 @@ all_options_strbuf(strbuf_t *sbuf) } /* cond_xyz are closer to regular options than the other 'other opts' - so put them next */ - if (opt_set_in_config[opt_o_status_cond]) + so put them next; [pfx_cond_] will be set if any cond_Foo were + present when RC file was read in or if player made any changes via + status conditions menu; ignore opt_set_in_config[opt_o_status_cond] */ + if (opt_set_in_config[pfx_cond_]) all_options_conds(sbuf); +#ifdef CHANGE_COLOR + all_options_palette(sbuf); +#endif get_changed_key_binds(sbuf); savedsym_strbuf(sbuf); all_options_menucolors(sbuf); all_options_msgtypes(sbuf); all_options_apes(sbuf); + all_options_autocomplete(sbuf); #ifdef STATUS_HILITES all_options_statushilites(sbuf); #endif @@ -9765,135 +9821,13 @@ next_opt(winid datawin, const char *str) return; } -/* - * This is a somewhat generic menu for taking a list of NetHack style - * class choices and presenting them via a description - * rather than the traditional NetHack characters. - * (Benefits users whose first exposure to NetHack is via tiles). - * - * prompt - * The title at the top of the menu. - * - * category: 0 = monster class - * 1 = object class - * - * way - * FALSE = PICK_ONE, TRUE = PICK_ANY - * - * class_list - * a null terminated string containing the list of choices. - * - * class_selection - * a null terminated string containing the selected characters. - * - * Returns number selected. - */ -int -choose_classes_menu(const char *prompt, - int category, - boolean way, - char *class_list, - char *class_select) -{ - menu_item *pick_list = (menu_item *) 0; - winid win; - anything any; - char buf[BUFSZ]; - int i, n; - int ret; - int next_accelerator, accelerator; - int clr = 0; - - if (class_list == (char *) 0 || class_select == (char *) 0) - return 0; - accelerator = 0; - next_accelerator = 'a'; - any = cg.zeroany; - win = create_nhwindow(NHW_MENU); - start_menu(win, MENU_BEHAVE_STANDARD); - while (*class_list) { - const char *text; - boolean selected; - - text = (char *) 0; - selected = FALSE; - switch (category) { - case 0: - text = def_monsyms[def_char_to_monclass(*class_list)].explain; - accelerator = *class_list; - Sprintf(buf, "%s", text); - break; - case 1: - text = def_oc_syms[def_char_to_objclass(*class_list)].explain; - accelerator = next_accelerator; - Sprintf(buf, "%c %s", *class_list, text); - break; - default: - impossible("choose_classes_menu: invalid category %d", category); - } - if (way && *class_select) { /* Selections there already */ - if (strchr(class_select, *class_list)) { - selected = TRUE; - } - } - any.a_int = *class_list; - add_menu(win, &nul_glyphinfo, &any, accelerator, - category ? *class_list : 0, ATR_NONE, clr, buf, - selected ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE); - ++class_list; - if (category > 0) { - ++next_accelerator; - if (next_accelerator == ('z' + 1)) - next_accelerator = 'A'; - if (next_accelerator == ('Z' + 1)) - break; - } - } - if (category == 1 && next_accelerator <= 'z') { - /* for objects, add "A - ' ' all classes", after a separator */ - any = cg.zeroany; - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); - any.a_int = (int) ' '; - Sprintf(buf, "%c %s", (char) any.a_int, "all classes of objects"); - /* we won't preselect this even if the incoming list is empty; - having it selected means that it would have to be explicitly - de-selected in order to select anything else */ - add_menu(win, &nul_glyphinfo, &any, 'A', 0, - ATR_NONE, clr, buf, MENU_ITEMFLAGS_NONE); - } - end_menu(win, prompt); - n = select_menu(win, way ? PICK_ANY : PICK_ONE, &pick_list); - destroy_nhwindow(win); - if (n > 0) { - if (category == 1) { - /* for object classes, first check for 'all'; it means 'use - a blank list' rather than 'collect every possible choice' */ - for (i = 0; i < n; ++i) - if (pick_list[i].item.a_int == ' ') { - pick_list[0].item.a_int = ' '; - n = 1; /* return 1; also an implicit 'break;' */ - } - } - for (i = 0; i < n; ++i) - *class_select++ = (char) pick_list[i].item.a_int; - free((genericptr_t) pick_list); - ret = n; - } else if (n == -1) { - class_select = eos(class_select); - ret = -1; - } else - ret = 0; - *class_select = '\0'; - return ret; -} - static struct wc_Opt wc_options[] = { { "ascii_map", WC_ASCII_MAP }, { "color", WC_COLOR }, { "eight_bit_tty", WC_EIGHT_BIT_IN }, { "hilite_pet", WC_HILITE_PET }, { "perm_invent", WC_PERM_INVENT }, + { "perminv_mode", WC_PERM_INVENT }, /* shares WC_PERM_INVENT */ { "popup_dialog", WC_POPUP_DIALOG }, { "player_selection", WC_PLAYER_SELECTION }, { "preload_tiles", WC_PRELOAD_TILES }, @@ -9995,7 +9929,7 @@ set_wc_option_mod_status(unsigned long optmask, int status) } } -static boolean +staticfn boolean is_wc_option(const char *optnam) { int k = 0; @@ -10008,7 +9942,7 @@ is_wc_option(const char *optnam) return FALSE; } -static boolean +staticfn boolean wc_supported(const char *optnam) { int k; @@ -10049,7 +9983,7 @@ set_wc2_option_mod_status(unsigned long optmask, int status) } } -static boolean +staticfn boolean is_wc2_option(const char *optnam) { int k = 0; @@ -10062,7 +9996,7 @@ is_wc2_option(const char *optnam) return FALSE; } -static boolean +staticfn boolean wc2_supported(const char *optnam) { int k; @@ -10075,7 +10009,7 @@ wc2_supported(const char *optnam) return FALSE; } -static void +staticfn void wc_set_font_name(int opttype, char *fontname) { char **fn = (char **) 0; @@ -10109,28 +10043,28 @@ wc_set_font_name(int opttype, char *fontname) return; } -static int +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) { /* syntax: * menu white/black message green/yellow status white/blue text * white/black */ + int j; + int32 clr; char buf[BUFSZ]; char *wn, *tfg, *tbg, *newop; - static const char *const wnames[] = { - "menu", "message", "status", "text" - }; - static const char *const shortnames[] = { "mnu", "msg", "sts", "txt" }; - static char **fgp[] = { &iflags.wc_foregrnd_menu, - &iflags.wc_foregrnd_message, - &iflags.wc_foregrnd_status, - &iflags.wc_foregrnd_text }; - static char **bgp[] = { &iflags.wc_backgrnd_menu, - &iflags.wc_backgrnd_message, - &iflags.wc_backgrnd_status, - &iflags.wc_backgrnd_text }; Strcpy(buf, op); newop = mungspaces(buf); @@ -10180,26 +10114,53 @@ wc_set_window_colors(char *op) if (*newop) *newop++ = '\0'; - for (j = 0; j < 4; ++j) { - if (!strcmpi(wn, wnames[j]) || !strcmpi(wn, shortnames[j])) { + for (j = 0; j < WC_COUNT; ++j) { + if (!strcmpi(wn, wcnames[j]) || !strcmpi(wn, wcshortnames[j])) { if (!strstri(tfg, " ")) { if (*fgp[j]) free((genericptr_t) *fgp[j]); - *fgp[j] = dupstr(tfg); + clr = check_enhanced_colors(tfg); + *fgp[j] = dupstr((clr >= 0) ? wc_color_name(clr) : tfg); } if (!strstri(tbg, " ")) { if (*bgp[j]) free((genericptr_t) *bgp[j]); - *bgp[j] = dupstr(tbg); + clr = check_enhanced_colors(tbg); + *bgp[j] = dupstr((clr >= 0) ? wc_color_name(clr) : tbg); + } + if (wcolors_opt[j] != 0) { + config_error_add( + "windowcolors for %s windows specified multiple times", + wcnames[j]); } + wcolors_opt[j]++; break; } } + if (j == WC_COUNT) { + config_error_add("windowcolors for unrecognized window type: %s", + wn); + } } + options_set_window_colors_flag = 1; return 1; } -/* set up for wizard mode if player or save file has requested to it; +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 restoring a game which was saved in wizard mode */ @@ -10208,16 +10169,23 @@ set_playmode(void) { if (wizard) { if (authorize_wizard_mode()) - gp.plnamelen = (int) strlen(strcpy(gp.plname, "wizard")); + gp.plnamelen = (int) strlen(strcpy(svp.plname, "wizard")); else wizard = FALSE; /* not allowed or not available */ - /* force explore mode if we didn't make it into wizard mode */ + /* try explore mode if we didn't make it into wizard mode */ + /* if requesting wizard mode when restoring a normal game, this will + set iflags.deferred_X and prompt to activate explore mode after the + save file has already been deleted */ discover = !wizard; iflags.deferred_X = FALSE; } - /* don't need to do anything special for explore mode or normal play */ + if (discover && !authorize_explore_mode()) { + discover = iflags.deferred_X = FALSE; + } + /* don't need to do anything special for normal play */ } -void + +staticfn void enhance_menu_text( char *buf, size_t sz, @@ -10232,7 +10200,7 @@ enhance_menu_text( nowsz = strlen(buf) + 1; availsz = sz - nowsz; -#ifdef TTY_PERM_INVENT +#if 0 /*#ifdef TTY_PERM_INVENT*/ if (bool_p == &iflags.perm_invent && WINDOWPORT(tty)) { if (thisopt->setwhere == set_gameview) Snprintf(eos(buf), availsz, " *terminal size is too small"); @@ -10245,7 +10213,13 @@ enhance_menu_text( return; } +#undef CONFIG_SLOT #endif /* OPTION_LISTS_ONLY */ +#undef BACKWARD_COMPAT +#undef COMPLAIN_ABOUT_PRAYCONFIRM +#undef PREV_MSGS +#undef PILE_LIMIT_DFLT + /*options.c*/ diff --git a/src/pager.c b/src/pager.c index 16578aeaa5..0850e422bb 100644 --- a/src/pager.c +++ b/src/pager.c @@ -1,56 +1,70 @@ -/* NetHack 3.7 pager.c $NHDT-Date: 1655120486 2022/06/13 11:41:26 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.225 $ */ +/* 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. */ -/* This file contains the command routines dowhatis() and dohelp() and */ -/* a few other help related facilities */ +/* + * This file contains the command routines dowhatis() and dohelp() and + * a few other help related facilities such as data.base lookup. + */ #include "hack.h" #include "dlb.h" -static boolean is_swallow_sym(int); -static int append_str(char *, const char *); -static void trap_description(char *, int, coordxy, coordxy); -static void look_at_object(char *, coordxy, coordxy, int); -static void look_at_monster(char *, char *, struct monst *, coordxy, coordxy); -static struct permonst *lookat(coordxy, coordxy, char *, char *); -static void add_mon_info(winid, struct permonst *); -static void add_obj_info(winid, short); -static int add_cmap_descr(int, int, int, int, coord, +staticfn boolean is_swallow_sym(int); +staticfn int append_str(char *, const char *) NONNULLPTRS; +staticfn void trap_description(char *, int, coordxy, coordxy) NONNULLARG1; +staticfn void look_at_object(char *, coordxy, coordxy, int) NONNULLARG1; +staticfn void look_at_monster(char *, char *, struct monst *, + coordxy, coordxy) NONNULLARG13; +/* lookat() can return Null */ +staticfn struct permonst *lookat(coordxy, coordxy, char *, char *) NONNULLPTRS; +staticfn void add_mon_info(winid, struct permonst *); +staticfn void add_obj_info(winid, short); +staticfn int add_cmap_descr(int, int, int, int, coord, const char *, const char *, - boolean *, const char **, char *); -static void look_region_nearby(coordxy *, coordxy *, coordxy *, coordxy *, - boolean); -static void look_all(boolean, boolean); -static void look_traps(boolean); -static void do_supplemental_info(char *, struct permonst *, boolean); -static void whatdoes_help(void); -static void docontact(void); -static void dispfile_help(void); -static void dispfile_shelp(void); -static void dispfile_optionfile(void); -static void dispfile_optmenu(void); -static void dispfile_license(void); -static void dispfile_debughelp(void); -static void dispfile_usagehelp(void); -static void hmenu_doextversion(void); -static void hmenu_dohistory(void); -static void hmenu_dowhatis(void); -static void hmenu_dowhatdoes(void); -static void hmenu_doextlist(void); -static void domenucontrols(void); + boolean *, const char **, char *) NONNULLPTRS; +staticfn void look_region_nearby(coordxy *, coordxy *, coordxy *, coordxy *, + boolean) NONNULLPTRS; +staticfn void look_all(boolean, boolean); +staticfn void look_traps(boolean); +staticfn void look_engrs(boolean); +staticfn void do_supplemental_info(char *, struct permonst *, + boolean) NONNULLPTRS; +staticfn void whatdoes_help(void); +staticfn void docontact(void); +staticfn void dispfile_help(void); +staticfn void dispfile_shelp(void); +staticfn void dispfile_optionfile(void); +staticfn void dispfile_optmenu(void); +staticfn void dispfile_license(void); +staticfn void dispfile_debughelp(void); +staticfn void dispfile_usagehelp(void); +staticfn void hmenu_doextversion(void); +staticfn void hmenu_dohistory(void); +staticfn void hmenu_dowhatis(void); +staticfn void hmenu_dowhatdoes(void); +staticfn void hmenu_doextlist(void); +staticfn void domenucontrols(void); #ifdef PORT_HELP extern void port_help(void); #endif -static char *setopt_cmd(char *); -static boolean add_quoted_engraving(coordxy, coordxy, char *); +staticfn char *setopt_cmd(char *) NONNULL NONNULLARG1; +staticfn boolean add_quoted_engraving(coordxy, coordxy, char *, boolean) + NONNULLARG3; + +enum checkfileflags { + chkfilNone = 0, + chkfilUsrTyped = 1, + chkfilDontAsk = 2, + chkfilIaCheck = 4, +}; static const char invisexplain[] = "remembered, unseen, creature", altinvisexplain[] = "unseen creature"; /* for clairvoyance */ /* Returns "true" for characters that could represent a monster's stomach. */ -static boolean +staticfn boolean is_swallow_sym(int c) { int i; @@ -64,7 +78,7 @@ is_swallow_sym(int c) /* Append " or "+new_str to the end of buf if new_str doesn't already exist as a substring of buf. Return 1 if the string was appended, 0 otherwise. It is expected that buf is of size BUFSZ. */ -static int +staticfn int append_str(char *buf, const char *new_str) { static const char sep[] = " or "; @@ -102,11 +116,14 @@ self_lookat(char *outbuf) Sprintf(outbuf, "%s%s%s called %s", /* being blinded may hide invisibility from self */ (Invis && (senseself() || !Blind)) ? "invisible " : "", race, - pmname(&mons[u.umonnum], Ugender), gp.plname); + pmname(&mons[u.umonnum], Ugender), svp.plname); if (u.usteed) Sprintf(eos(outbuf), ", mounted on %s", y_monnam(u.usteed)); - if (u.uundetected || (Upolyd && U_AP_TYPE)) - mhidden_description(&gy.youmonst, FALSE, eos(outbuf)); + if (u.uundetected || (Upolyd && U_AP_TYPE) + || visible_region_at(u.ux, u.uy)) + mhidden_description(&gy.youmonst, + MHID_PREFIX | MHID_ARTICLE | MHID_REGION, + eos(outbuf)); if (Punished) Sprintf(eos(outbuf), ", chained to %s", uball ? ansimpleoname(uball) : "nothing?"); @@ -146,7 +163,7 @@ monhealthdescr(struct monst *mon, boolean addspace, char *outbuf) } /* copy a trap's description into outbuf[] */ -static void +staticfn void trap_description(char *outbuf, int tnum, coordxy x, coordxy y) { /* @@ -164,45 +181,65 @@ trap_description(char *outbuf, int tnum, coordxy x, coordxy y) } /* describe a hidden monster; used for look_at during extended monster - detection and for probing; also when looking at self */ + detection and for probing; also when looking at self and camera feedback */ void mhidden_description( - struct monst *mon, - boolean altmon, /* for probing: if mimicking a monster, say so */ - char *outbuf) + struct monst *mon, /* hidden monster to describe */ + unsigned mhid_flags, /* controls optional aspects of description */ + char *outbuf) /* output buffer */ { struct obj *otmp; + const char *what; + NhRegion *reg; + size_t buflen; + boolean incl_prefix = (mhid_flags & MHID_PREFIX) != 0, + incl_article = (mhid_flags & MHID_ARTICLE) != 0, + show_altmon = (mhid_flags & MHID_ALTMON) != 0, + force_region = (mhid_flags & MHID_REGION) != 0; boolean fakeobj, isyou = (mon == &gy.youmonst); coordxy x = isyou ? u.ux : mon->mx, y = isyou ? u.uy : mon->my; - int glyph = (gl.level.flags.hero_memory && !isyou) ? levl[x][y].glyph - : glyph_at(x, y); + int glyph = (svl.level.flags.hero_memory && !isyou) ? levl[x][y].glyph + : glyph_at(x, y); *outbuf = '\0'; if (M_AP_TYPE(mon) == M_AP_FURNITURE || M_AP_TYPE(mon) == M_AP_OBJECT) { - Strcpy(outbuf, ", mimicking "); + if (incl_prefix) + Strcpy(outbuf, ", mimicking "); if (M_AP_TYPE(mon) == M_AP_FURNITURE) { - Strcat(outbuf, an(defsyms[mon->mappearance].explanation)); + what = defsyms[mon->mappearance].explanation; + if (incl_article) + what = an(what); + Strcat(outbuf, what); } else if (M_AP_TYPE(mon) == M_AP_OBJECT /* remembered glyph, not glyph_at() which is 'mon' */ && glyph_is_object(glyph)) { objfrommap: otmp = (struct obj *) 0; fakeobj = object_from_map(glyph, x, y, &otmp); - Strcat(outbuf, (otmp && otmp->otyp != STRANGE_OBJECT) - ? ansimpleoname(otmp) - : an(obj_descr[STRANGE_OBJECT].oc_name)); - if (fakeobj) { + what = (otmp && otmp->otyp != STRANGE_OBJECT) + ? simpleonames(otmp) + : obj_descr[STRANGE_OBJECT].oc_name; + if (incl_article) + 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); } } else if (M_AP_TYPE(mon) == M_AP_MONSTER) { - if (altmon) - Sprintf(outbuf, ", masquerading as %s", - an(pmname(&mons[mon->mappearance], Mgender(mon)))); + if (show_altmon) { + if (incl_prefix) + Strcat(outbuf, ", masquerading as "); + what = pmname(&mons[mon->mappearance], Mgender(mon)); + if (incl_prefix) + what = an(what); + Strcat(outbuf, what); + } } else if (isyou ? u.uundetected : mon->mundetected) { Strcpy(outbuf, ", hiding"); if (hides_under(mon->data)) { @@ -226,21 +263,48 @@ mhidden_description( Strcat(outbuf, " in murky water"); } } + + /* FIXME: isn't right when looking at long worm tails */ + if ((reg = visible_region_at(x, y)) != 0 + && (buflen = strlen(outbuf)) < BUFSZ - 1) { + int r = (u.xray_range > 1) ? u.xray_range : 1; + + /* at present, hero must be next to the monster; being able to see + from the hero's spot to the monster's spot would be much better, + but a visible region marks all its spots as can't-be-seen, so + this monster's spot is !cansee and !couldsee [maybe we need an + additional vision bit for "hero's side of edge of gas cloud"?] */ + if (distu(x, y) <= r * (r + 1) || force_region) { + int rglyph = reg->glyph; + boolean poison_gas = (glyph_is_cmap(rglyph) + && glyph_to_cmap(rglyph) == S_poisoncloud); + + Snprintf(eos(outbuf), BUFSZ - buflen, ", in a cloud of %s", + poison_gas ? "poison gas" : "vapor"); + } + } } /* 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 */ if ((otmp = sobj_at(glyphotyp, x, y)) == 0) - for (otmp = gl.level.buriedobjlist; otmp; otmp = otmp->nobj) + for (otmp = svl.level.buriedobjlist; otmp; otmp = otmp->nobj) if (otmp->ox == x && otmp->oy == y && otmp->otyp == glyphotyp) break; @@ -255,16 +319,18 @@ 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; + /* 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 */ else if (otmp->otyp == SLIME_MOLD) - otmp->spe = gc.context.current_fruit; /* give it a type */ + otmp->spe = svc.context.current_fruit; /* give it a type */ if (mtmp && has_mcorpsenm(mtmp)) { /* mimic as corpse/statue */ if (otmp->otyp == SLIME_MOLD) - /* override gc.context.current_fruit to avoid + /* override svc.context.current_fruit to avoid look, use 'O' to make new named fruit, look again giving different results when current_fruit changes */ otmp->spe = MCORPSENM(mtmp); @@ -303,7 +369,7 @@ object_from_map(int glyph, coordxy x, coordxy y, struct obj **obj_p) return fakeobj; /* when True, caller needs to dealloc *obj_p */ } -static void +staticfn void look_at_object( char *buf, /* output buffer */ coordxy x, coordxy y, @@ -319,7 +385,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 */ @@ -339,10 +405,9 @@ look_at_object( return; } -static void +staticfn void look_at_monster( - char *buf, - char *monbuf, /* buf: output, monbuf: optional output */ + char *buf, char *monbuf, /* buf: output, monbuf: optional output */ struct monst *mtmp, coordxy x, coordxy y) { @@ -414,8 +479,9 @@ look_at_monster( /* we know the hero sees a monster at this location, but if it's shown due to persistent monster detection he might remember something else */ - if (mtmp->mundetected || M_AP_TYPE(mtmp)) - mhidden_description(mtmp, FALSE, eos(buf)); + if (mtmp->mundetected || M_AP_TYPE(mtmp) || visible_region_at(x, y)) + mhidden_description(mtmp, MHID_PREFIX | MHID_ARTICLE | MHID_REGION, + eos(buf)); if (monbuf) { unsigned how_seen = howmonseen(mtmp); @@ -464,8 +530,8 @@ look_at_monster( if (Hallucination) { Strcat(monbuf, "paranoid delusion"); } else { - unsigned long mW = (gc.context.warntype.obj - | gc.context.warntype.polyd), + unsigned long mW = (svc.context.warntype.obj + | svc.context.warntype.polyd), m2 = mtmp->data->mflags2; const char *whom = ((mW & M2_HUMAN & m2) ? "human" : (mW & M2_ELF & m2) ? "elf" @@ -474,7 +540,7 @@ look_at_monster( : pmname(mtmp->data, Mgender(mtmp))); - if (gc.context.warntype.obj_mlet == mtmp->data->mlet) { + if (svc.context.warntype.obj_mlet == mtmp->data->mlet) { /* Note: relying on .explain will be wonky if an * artifact is ever added that warns against some * monster class explained like "foo or bar" */ @@ -503,16 +569,12 @@ const char * waterbody_name(coordxy x, coordxy y) { static char pooltype[40]; - struct rm *lev; schar ltyp; - boolean hallucinate = Hallucination && !gp.program_state.gameover; + boolean hallucinate = Hallucination && !program_state.gameover; if (!isok(x, y)) return "drink"; /* should never happen */ - lev = &levl[x][y]; - ltyp = lev->typ; - if (ltyp == DRAWBRIDGE_UP) - ltyp = db_under_typ(lev->drawbridgemask); + ltyp = SURFACE_AT(x, y); if (ltyp == LAVAPOOL) { Snprintf(pooltype, sizeof pooltype, "molten %s", hliquid("lava")); @@ -556,11 +618,50 @@ waterbody_name(coordxy x, coordxy y) return "water"; /* don't hallucinate this as some other liquid */ } +char * +ice_descr(coordxy x, coordxy y, char *outbuf) +{ + static const char *const icetyp[] = { + "solid", /* 0: not melting */ + "sturdy", /* 1: more than 1000 turns left */ + "steady", /* 2: 101..1000 turns left */ + "unsteady", /* 3: 51..100 turns left */ + "thin", /* 4: 15..50 turns left */ + "slushy", /* 5: 1..14 turns left; matches Warning on ice */ + }; + /* same formula as is used in distant_name() for objects */ + int r = (u.xray_range > 2) ? u.xray_range : 2, + neardist = (r * r) * 2 - r; /* same as r*r + r*(r-1) */ + + iflags.ice_rating = -1; /* secondary output, for 'mention_decor' */ + if (SURFACE_AT(x, y) != ICE) { + Sprintf(outbuf, "[ice:%d?]", (int) levl[x][y].typ); + } else if ((distu(x, y) > neardist + || (!cansee(x, y) && (!u_at(x, y) || Levitation))) + && !gd.decor_levitate_override) { /* probe_decor(pickup.c) */ + Strcpy(outbuf, waterbody_name(x, y)); /* "ice" or "frozen " */ + } else { + long time_left = spot_time_left(x, y, MELT_ICE_AWAY); + + /* other, real ice thickness/strength terminology exists but seems + to be too unfamiliar for nethack's use */ + iflags.ice_rating = !time_left ? 0 /* solid */ + : (time_left > 1000L) ? 1 /* sturdy */ + : (time_left > 100L) ? 2 /* steady */ + : (time_left > 50L) ? 3 /* unsteady */ + : (time_left > 14L) ? 4 /* thin */ + : 5; /* slushy */ + Sprintf(outbuf, "%s %s", icetyp[(int) iflags.ice_rating], + waterbody_name(x, y)); + } + return outbuf; +} + /* * Return the name of the glyph found at (x,y). * If not hallucinating and the glyph is a monster, also monster data. */ -static struct permonst * +staticfn struct permonst * lookat(coordxy x, coordxy y, char *buf, char *monbuf) { struct monst *mtmp = (struct monst *) 0; @@ -610,11 +711,9 @@ lookat(coordxy x, coordxy y, char *buf, char *monbuf) } else if (u.uswallow) { /* when swallowed, we're only called for spots adjacent to hero, and blindness doesn't prevent hero from feeling what holds him */ - Sprintf(buf, "interior of %s", a_monnam(u.ustuck)); + Sprintf(buf, "interior of %s", mon_nam(u.ustuck)); pm = u.ustuck->data; } else if (glyph_is_monster(glyph)) { - gb.bhitpos.x = x; - gb.bhitpos.y = y; if ((mtmp = m_at(x, y)) != 0) { look_at_monster(buf, monbuf, mtmp, x, y); pm = mtmp->data; @@ -632,6 +731,8 @@ lookat(coordxy x, coordxy y, char *buf, char *monbuf) int warnindx = glyph_to_warning(glyph); Strcpy(buf, def_warnsyms[warnindx].explanation); + } else if (glyph_is_invisible(glyph)) { + Strcpy(buf, invisexplain); /* redundant; handled by caller */ } else if (glyph_is_nothing(glyph)) { Strcpy(buf, "dark part of a room"); } else if (glyph_is_unexplored(glyph)) { @@ -642,11 +743,7 @@ lookat(coordxy x, coordxy y, char *buf, char *monbuf) } else { Strcpy(buf, "unexplored area"); } - } else if (glyph_is_invisible(glyph)) { - /* already handled */ - } else if (!glyph_is_cmap(glyph)) { - Strcpy(buf, "unexplored area"); - } else { + } else if (glyph_is_cmap(glyph)) { int amsk; aligntyp algn; short symidx = glyph_to_cmap(glyph); @@ -688,8 +785,9 @@ lookat(coordxy x, coordxy y, char *buf, char *monbuf) Is_airlevel(&u.uz) ? "cloudy area" : "fog/vapor cloud"); break; case S_pool: - case S_water: + case S_water: /* was Plane of Water, now that or "wall of water" */ case S_lava: + case S_lavawall: case S_ice: /* for hallucination; otherwise defsyms[] would be fine */ Strcpy(buf, waterbody_name(x, y)); break; @@ -710,11 +808,14 @@ lookat(coordxy x, coordxy y, char *buf, char *monbuf) Strcpy(buf, "stone"); break; } + FALLTHROUGH; /*FALLTHRU*/ default: Strcpy(buf, defsyms[symidx].explanation); break; } + } else { /* not mon, obj, trap, or cmap */ + Strcpy(buf, "unexplored area"); } return (pm && !Hallucination) ? pm : (struct permonst *) 0; } @@ -799,7 +900,7 @@ static const char * damagetypes[] = { /* Add some information to an encyclopedia window which is printing information * about a monster. */ -static void +staticfn void add_mon_info(winid datawin, struct permonst * pm) { char buf[BUFSZ]; @@ -1069,7 +1170,7 @@ add_mon_info(winid datawin, struct permonst * pm) /* Add some information to an encyclopedia window which is printing information * about an object. */ -static void +staticfn void add_obj_info(winid datawin, short otyp) { struct objclass oc = objects[otyp]; @@ -1473,6 +1574,19 @@ add_obj_info(winid datawin, short otyp) } } +/* used to decide whether the context-sensitive inventory action menu for + item 'otmp' should include the "/ - look up this item" choice */ +boolean +ia_checkfile(struct obj *otmp) +{ + char itemnam[BUFSZ]; + + /* singular() of xname() of otmp is what "/i" looks up */ + Strcpy(itemnam, singular(otmp, xname)); + return checkfile(itemnam, (struct permonst *) 0, + chkfilIaCheck | chkfilDontAsk, (char *) 0); +} + /* * Look in the "data" file for more info. Called if the user typed in the * whole name (user_typed_name == TRUE), or we've found a possible match @@ -1482,23 +1596,32 @@ add_obj_info(winid datawin, short otyp) * must not be changed directly, e.g. via lcase(). We want to force * lcase() for data.base lookup so that we can have a clean key. * Therefore, we create a copy of inp _just_ for data.base lookup. + * + * Returns True if an entry is found, False otherwise. */ -void -checkfile(char *inp, struct permonst *pm, boolean user_typed_name, - boolean without_asking, char *supplemental_name) +staticfn boolean +checkfile( + char *inp, /* string to look up */ + struct permonst *pm, /* monster type to look up (overrides 'inp') */ + unsigned chkflags, + char *supplemental_name) { dlb *fp; char buf[BUFSZ], newstr[BUFSZ], givenname[BUFSZ]; char *ep, *dbase_str, *dbase_str_with_material; + boolean user_typed_name = (chkflags & chkfilUsrTyped) != 0, + without_asking = (chkflags & chkfilDontAsk) != 0, + ia_checking = (chkflags & chkfilIaCheck) != 0; unsigned long txt_offset = 0L; - winid datawin = WIN_ERR; short otyp, mat; boolean lookat_mon = (pm != (struct permonst *) 0); + winid datawin = WIN_ERR; + boolean res = FALSE; fp = dlb_fopen(DATAFILE, "r"); if (!fp) { pline("Cannot open 'data' file!"); - return; + return res; } /* If someone passed us garbage, prevent fault. */ if (!inp || strlen(inp) > (BUFSZ - 1)) { @@ -1651,6 +1774,12 @@ checkfile(char *inp, struct permonst *pm, boolean user_typed_name, if (alt && (ap = strstri(alt, " (")) != 0 && ap > alt) *ap = '\0'; + /* If the object's name matches the player-specified fruitname, + then "fruit" is the alternate description. We do this here so that + if the fruit name is an extant object, looking at the fruit yields + that object's description. */ + if (!alt && fruit_from_name(dbase_str, TRUE, (int *) 0)) + alt = strcpy(newstr, obj_descr[SLIME_MOLD].oc_name); /* * If the object is named, then the name is the alternate description; * otherwise, the result of makesingular() applied to the name is. @@ -1658,7 +1787,7 @@ checkfile(char *inp, struct permonst *pm, boolean user_typed_name, * user will usually be found under their name, rather than under * their object type, so looking for a singular form is pointless. */ - if (!alt) + else if (!alt) alt = makesingular(dbase_str); pass1found_in_file = FALSE; @@ -1782,7 +1911,8 @@ checkfile(char *inp, struct permonst *pm, boolean user_typed_name, if (!found_in_file && !pm && otyp == STRANGE_OBJECT) { if ((user_typed_name && pass == 0 && !pass1found_in_file) || yes_to_moreinfo) - pline("I don't have any information on those things."); + pline( + "You don't have any information on those things."); /* don't print anything otherwise; we don't want it to e.g. * print a database entry and then print the above message. */ @@ -1791,7 +1921,8 @@ checkfile(char *inp, struct permonst *pm, boolean user_typed_name, boolean do_obj_lookup = FALSE, do_mon_lookup = FALSE; if (pm) { do_mon_lookup = TRUE; - if (!lookat_mon && otyp != STRANGE_OBJECT) { + if (!lookat_mon && otyp != STRANGE_OBJECT + && !ia_checking) { /* found matches for both and player is NOT looking * at a monster; ask which they want to see */ /* TODO: this would ideally be better generalized so @@ -1807,6 +1938,10 @@ checkfile(char *inp, struct permonst *pm, boolean user_typed_name, else if (otyp != STRANGE_OBJECT) { do_obj_lookup = TRUE; } + res = TRUE; + if (ia_checking) + goto checkfile_done; + datawin = create_nhwindow(NHW_MENU); /* object lookup info */ @@ -1836,7 +1971,7 @@ checkfile(char *inp, struct permonst *pm, boolean user_typed_name, SEEK_SET) < 0) { pline("? Seek error on 'data' file!"); (void) dlb_fclose(fp); - return; + goto checkfile_done; } Snprintf(titlebuf, BUFSZ, @@ -1891,11 +2026,11 @@ checkfile(char *inp, struct permonst *pm, boolean user_typed_name, if (datawin != WIN_ERR) destroy_nhwindow(datawin); (void) dlb_fclose(fp); - return; + return res; } /* extracted from do_screen_description() */ -static int +staticfn int add_cmap_descr( int found, /* number of matching descriptions so far */ int idx, /* cmap index into defsyms[] */ @@ -1909,7 +2044,7 @@ add_cmap_descr( const char **firstmatch, /* output: pointer to 1st matching description */ char *out_str) /* input/output: current description gets appended */ { - char *mbuf = NULL; + char *mbuf = NULL, *p; int absidx = abs(idx); if (glyph == NO_GLYPH) { @@ -1925,7 +2060,7 @@ add_cmap_descr( if (absidx == S_pool) idx = S_pool; } else if (absidx == S_pool || idx == S_water - || idx == S_lava || idx == S_ice) { + || idx == S_lava || idx == S_lavawall || idx == S_ice) { /* replace some descriptions (x_str) with waterbody_name() */ schar save_ltyp = levl[cc.x][cc.y].typ; long save_prop = EHalluc_resistance; @@ -1944,25 +2079,39 @@ add_cmap_descr( it's not pool so must be one of water/lava/ice to get here */ levl[cc.x][cc.y].typ = (idx == S_water) ? WATER : (idx == S_lava) ? LAVAPOOL - : ICE; + : (idx == S_lavawall) ? LAVAWALL + : ICE; } EHalluc_resistance = 1; Strcpy(mbuf, waterbody_name(cc.x, cc.y)); EHalluc_resistance = save_prop; levl[cc.x][cc.y].typ = save_ltyp; - /* shorten the feedback for farlook/quicklook: "a pool or ..." */ + /* shorten the feedback for farlook/quicklook: "pool or ..." */ if (!strcmp(mbuf, "pool of water")) mbuf[4] = '\0'; else if (!strcmp(mbuf, "molten lava")) Strcpy(mbuf, "lava"); x_str = mbuf; + /* avoid "an ice" and so forth; "a pool", "a moat", and + "a wall of ..." are grammatically correct but make + "a pool or a moat or a wall of water" become too verbose */ article = !(!strncmp(x_str, "water", 5) + || !strncmp(x_str, "ice", 3) + || !strncmp(x_str, "pool", 4) + || !strncmp(x_str, "moat", 4) || !strncmp(x_str, "lava", 4) || !strncmp(x_str, "swamp", 5) || !strncmp(x_str, "molten", 6) || !strncmp(x_str, "shallow", 7) - || !strncmp(x_str, "limitless", 9)); + || !strncmp(x_str, "limitless", 9) + || !strncmp(x_str, "wall of lava", 12) + || !strncmp(x_str, "wall of water", 13) + /* ice while hallucinating */ + || !strncmp(x_str, "frozen", 6) + /* thawing ice ("solid ice", "thin ice", &c) */ + || ((p = strchr(x_str, ' ')) != 0 && !strcmpi(p, " ice")) + ); } if (!found) { @@ -1971,9 +2120,9 @@ add_cmap_descr( Sprintf(out_str, "%sa trap", prefix); *hit_trap = TRUE; } else { - Sprintf(out_str, "%s%s", prefix, - article == 2 ? the(x_str) - : article == 1 ? an(x_str) : x_str); + Sprintf(out_str, "%s%s", prefix, (article == 2) ? the(x_str) + : (article == 1) ? an(x_str) + : x_str); } *firstmatch = x_str; found = 1; @@ -1995,9 +2144,11 @@ add_cmap_descr( } int -do_screen_description(coord cc, boolean looked, int sym, char *out_str, - const char **firstmatch, - struct permonst **for_supplement) +do_screen_description( + coord cc, boolean looked, + int sym, char *out_str, + const char **firstmatch, + struct permonst **for_supplement) { static const char mon_interior[] = "the interior of a monster", unreconnoitered[] = "unreconnoitered"; @@ -2007,7 +2158,7 @@ do_screen_description(coord cc, boolean looked, int sym, char *out_str, skipped_venom = 0, found = 0; /* count of matching syms found */ boolean hit_trap, need_to_look = FALSE, submerged = (Underwater && !Is_waterlevel(&u.uz)), - hallucinate = (Hallucination && !gp.program_state.gameover); + hallucinate = (Hallucination && !program_state.gameover); const char *x_str; nhsym tmpsym; glyph_info glyphinfo = nul_glyphinfo; @@ -2071,6 +2222,7 @@ do_screen_description(coord cc, boolean looked, int sym, char *out_str, if (x_str == unreconnoitered) goto didlook; } + check_monsters: /* Check for monsters */ if (!iflags.terrainmode || (iflags.terrainmode & TER_MON) != 0) { @@ -2103,22 +2255,48 @@ do_screen_description(coord cc, boolean looked, int sym, char *out_str, /* Now check for objects */ if (!iflags.terrainmode || (iflags.terrainmode & TER_OBJ) != 0) { + const char *oc_ptr; + nhsym bouldersym; + + j = SYM_BOULDER + SYM_OFF_X; + bouldersym = go.ov_primary_syms[j]; + if (!bouldersym) + bouldersym = def_oc_syms[ROCK_CLASS].sym; + for (i = 1; i < MAXOCLASSES; i++) { - if (sym == (looked ? gs.showsyms[i + SYM_OFF_O] - : def_oc_syms[i].sym) - || (looked && i == ROCK_CLASS && glyph_is_statue(glyph))) { + if ((i != ROCK_CLASS) + ? (sym == (looked ? gs.showsyms[i + SYM_OFF_O] + : def_oc_syms[i].sym)) + /* ROCK_CLASS is complicated; statues are displayed as the + monster they depict rather than as S_rock; boulders might + be displayed as a custom symbol rather than as S_rock */ + : (glyph_is_statue(glyph) || sym == bouldersym)) { + oc_ptr = def_oc_syms[i].explain; + /* for added fun, engravings are shown with the same symbol + as S_rock which is why we want to shorten this */ + if (i == ROCK_CLASS && !strcmp(oc_ptr, "boulder or statue")) { + if (sym == bouldersym) + oc_ptr = "boulder"; /* discard "or statue" */ + else if (glyph_is_statue(glyph)) + oc_ptr = "statue"; /* discard "boulder or" */ + else if (looked) + continue; /* discard both */ + } need_to_look = TRUE; if (looked && i == VENOM_CLASS) { skipped_venom++; continue; } if (!found) { - Sprintf(out_str, "%s%s", - prefix, an(def_oc_syms[i].explain)); - *firstmatch = def_oc_syms[i].explain; + Sprintf(out_str, "%s%s", prefix, an(oc_ptr)); + /* note: if the value assigned to *firstmatch ever + becomes dynamically constructed, it will need to be + copied into a static buffer; as of now, all alternate + values are string literals and implicitly static */ + *firstmatch = oc_ptr; found++; } else { - found += append_str(out_str, an(def_oc_syms[i].explain)); + found += append_str(out_str, an(oc_ptr)); } } } @@ -2127,8 +2305,8 @@ do_screen_description(coord cc, boolean looked, int sym, char *out_str, if (sym == DEF_INVISIBLE) { /* for active clairvoyance, use alternate "unseen creature" */ boolean usealt = (EDetect_monsters & I_SPECIAL) != 0L; - const char *unseen_explain = usealt ? altinvisexplain - : Blind ? altinvisexplain : invisexplain; + const char *unseen_explain = (usealt || Blind) ? altinvisexplain + : invisexplain; if (!found) { Sprintf(out_str, "%s%s", prefix, an(unseen_explain)); @@ -2166,18 +2344,30 @@ do_screen_description(coord cc, boolean looked, int sym, char *out_str, for (hit_trap = FALSE, i = 0; i < MAXPCHARS; i++) { /* * Index hackery: we want - * "a pool or a moat or a wall of water or lava" + * "pool or moat or wall of water or lava or wall of lava" * rather than - * "a pool or a moat or lava or a wall of water" + * "pool or moat or lava or wall of lava or wall of water" * but S_lava comes before S_water so 'i' reaches it sooner. * Use 'alt_i' for the rest of the loop to behave as if their * places were swapped. + * This was much simpler when it just exchanged water and lava. + * Now it rotates water to the first of (lava, lavawall, water) + * lava to the middle of (lava, lavawall, water), and lavawall + * to last of (lava, lavawall, water); other values are used + * as-is. + * If S_water (and corresponding tile) were renumbered, this + * hackery could go away. */ - alt_i = ((i != S_water && i != S_lava) ? i /* as-is */ - : (S_water + S_lava) - i); /* swap water and lava */ + alt_i = (i == S_lava) ? S_water /* do water first (of these 3) */ + : (i == S_lavawall) ? S_lava /* process lava second */ + : (i == S_water) ? S_lavawall /* and wall of lava third */ + : i; /* other; handle in defsyms[] order */ x_str = defsyms[alt_i].explanation; - if (!*x_str) /* cmap includes beams, shield effects, swallow +*/ - continue; /*+ boundaries, and explosions; skip all of those */ + /* cmap includes beams, shield effects, swallow boundaries, and + explosions; skip all of those */ + if (!*x_str) + continue; + if (sym == (looked ? gs.showsyms[alt_i] : defsyms[alt_i].sym)) { int article; /* article==2 => "the", 1 => "an", 0 => (none) */ @@ -2193,11 +2383,6 @@ do_screen_description(coord cc, boolean looked, int sym, char *out_str, || strcmp(x_str, "land") == 0 || strcmp(x_str, "grass") == 0); - if (alt_i == S_engroom || alt_i == S_engrcorr) { - article = 1; - x_str = "engraving"; - need_to_look = TRUE; - } found = add_cmap_descr(found, alt_i, glyph, article, cc, x_str, prefix, &hit_trap, firstmatch, out_str); @@ -2214,7 +2399,11 @@ do_screen_description(coord cc, boolean looked, int sym, char *out_str, if (alt_i == S_altar || is_cmap_trap(alt_i) || (hallucinate && (alt_i == S_water /* S_pool already done */ - || alt_i == S_lava || alt_i == S_ice))) + || alt_i == S_lava + || alt_i == S_lavawall + || alt_i == S_ice)) + || alt_i == S_engroom || alt_i == S_engrcorr + || alt_i == S_grave) /* 'need_to_look' to report engraving */ need_to_look = TRUE; } } @@ -2252,11 +2441,12 @@ do_screen_description(coord cc, boolean looked, int sym, char *out_str, /* Finally, handle some optional overriding symbols */ for (j = SYM_OFF_X; j < SYM_MAX; ++j) { - if (j == (SYM_INVISIBLE + SYM_OFF_X)) + if (j == SYM_INVISIBLE + SYM_OFF_X || j == SYM_BOULDER + SYM_OFF_X) continue; /* already handled above */ tmpsym = go.ov_primary_syms[j]; if (tmpsym && sym == tmpsym) { switch (j) { +#if 0 case SYM_BOULDER + SYM_OFF_X: { static const char boulder[] = "boulder"; @@ -2269,6 +2459,7 @@ do_screen_description(coord cc, boolean looked, int sym, char *out_str, } break; } +#endif case SYM_PET_OVERRIDE + SYM_OFF_X: if (looked) { /* convert to symbol without override in effect */ @@ -2284,19 +2475,6 @@ do_screen_description(coord cc, boolean looked, int sym, char *out_str, } } } -#if 0 - /* handle optional boulder symbol as a special case */ - if (o_syms[SYM_BOULDER + SYM_OFF_X] - && sym == o_syms[SYM_BOULDER + SYM_OFF_X]) { - if (!found) { - *firstmatch = "boulder"; - Sprintf(out_str, "%s%s", prefix, an(*firstmatch)); - found++; - } else { - found += append_str(out_str, "boulder"); - } - } -#endif /* * If we are looking at the screen, follow multiple possibilities or @@ -2311,7 +2489,7 @@ do_screen_description(coord cc, boolean looked, int sym, char *out_str, didlook: if (looked) { - struct permonst *pm = (struct permonst *)0; + struct permonst *pm = (struct permonst *) 0; if (found > 1 || need_to_look) { char monbuf[BUFSZ]; @@ -2320,15 +2498,17 @@ do_screen_description(coord cc, boolean looked, int sym, char *out_str, pm = lookat(cc.x, cc.y, look_buf, monbuf); if (pm && for_supplement) *for_supplement = pm; + if (!strcmp(look_buf, "ice")) + (void) ice_descr(cc.x, cc.y, look_buf); if (look_buf[0] != '\0') *firstmatch = look_buf; if (*(*firstmatch)) { - if (strncmp(look_buf, "engraving", 9) != 0) { - Snprintf(temp_buf, sizeof temp_buf, " (%s)", *firstmatch); - (void) strncat(out_str, temp_buf, - BUFSZ - strlen(out_str) - 1); - } + Sprintf(temp_buf, " (%s", *firstmatch); + (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); found = 1; /* we have something to look up */ } if (monbuf[0]) { @@ -2342,27 +2522,48 @@ do_screen_description(coord cc, boolean looked, int sym, char *out_str, return found; } -static boolean -add_quoted_engraving(coordxy x, coordxy y, char *buf) +/* when farlook is reporting on an engraving, include its text */ +staticfn boolean +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); + boolean floorengr = !strcmp(buf, " (engraving"), + headstone = !strcmp(buf, " (grave"); - if (ep) { - if (ep->eread) - Snprintf(temp_buf, sizeof temp_buf, - " with remembered text: \"%s\"", - ep->engr_txt[remembered_text]); - else - Snprintf(temp_buf, sizeof temp_buf, " that you've never read"); - (void) strncat(buf, temp_buf, BUFSZ - strlen(buf) - 1); - return TRUE; - } - return FALSE; + /* + * If there is no engraving here, there's nothing to do; just return. + * + * When buf[] is " (engraving" or " (grave" then we're looking at an + * engraving and we'll add its text. Caller supplies the closing paren. + * + * If buf[] contains anything else, we're looking at something (monster + * or object) that happens to be on top of an engraving, so we won't + * append the engraving text. + */ + if (!ep) + return FALSE; + + if (!floorengr && !headstone && !force) + return FALSE; + + if (ep->eread) + Snprintf(temp_buf, sizeof temp_buf, " with %s: \"%s\"", + headstone ? "headstone reading" : "remembered text", + ep->engr_txt[remembered_text]); + else + Snprintf(temp_buf, sizeof temp_buf, " %s you haven't read", + headstone ? "whose headstone" : "that"); + + (void) strncat(buf, temp_buf, BUFSZ - strlen(buf) - 1); + return TRUE; } -/* also used by getpos hack in do_name.c */ -const char what_is_an_unknown_object[] = "an unknown object"; +/* also used by getpos hack in getpos.c */ +const char what_is_a_location[] = "a monster, object or location"; int do_look(int mode, coord *click_cc) @@ -2370,6 +2571,7 @@ do_look(int mode, coord *click_cc) boolean quick = (mode == 1); /* use cursor; don't search for "more info" */ boolean clicklook = (mode == 2); /* right mouse-click method */ char out_str[BUFSZ] = DUMMY; + struct _cmd_queue cq, *cmdq; const char *firstmatch = 0; struct permonst *pm = 0, *supplemental_pm = 0; int i = '\0', ans = 0; @@ -2378,11 +2580,21 @@ do_look(int mode, coord *click_cc) coord cc; /* screen pos of unknown glyph */ boolean save_verbose; /* saved value of flags.verbose */ boolean from_screen; /* question from the screen */ - int clr = 0; + int clr = NO_COLOR; cc.x = 0; cc.y = 0; + if ((cmdq = cmdq_pop()) != 0) { + cq = *cmdq; + free((genericptr_t) cmdq); + if (cq.typ == CMDQ_KEY) + i = cq.key; + else + cmdq_clear(CQ_CANNED); + goto dowhatiscmd; + } + if (!clicklook) { if (quick) { from_screen = TRUE; /* yes, we want to use the cursor */ @@ -2412,8 +2624,7 @@ do_look(int mode, coord *click_cc) MENU_ITEMFLAGS_NONE); if (!u.uswallow && !Hallucination) { any = cg.zeroany; - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "", MENU_ITEMFLAGS_NONE); + add_menu_str(win, ""); /* these options work sensibly for the swallowed case, but there's no reason for the player to use them then; objects work fine when hallucinating, but screen @@ -2425,25 +2636,34 @@ do_look(int mode, coord *click_cc) clr, "nearby monsters", MENU_ITEMFLAGS_NONE); any.a_char = 'M'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, 0, ATR_NONE, - clr, "all monsters shown on map", MENU_ITEMFLAGS_NONE); + flags.lootabc ? 0 : any.a_char, 0, ATR_NONE, clr, + "all monsters shown on map", MENU_ITEMFLAGS_NONE); any.a_char = 'o'; add_menu(win, &nul_glyphinfo, &any, flags.lootabc ? 0 : any.a_char, 0, ATR_NONE, clr, "nearby objects", MENU_ITEMFLAGS_NONE); any.a_char = 'O'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, 0, ATR_NONE, - clr, "all objects shown on map", MENU_ITEMFLAGS_NONE); - any.a_char = '^'; + flags.lootabc ? 0 : any.a_char, 0, ATR_NONE, clr, + "all objects shown on map", MENU_ITEMFLAGS_NONE); + any.a_char = 't'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, 0, ATR_NONE, + flags.lootabc ? 0 : any.a_char, '^', ATR_NONE, clr, "nearby traps", MENU_ITEMFLAGS_NONE); - any.a_char = '\"'; + any.a_char = 'T'; add_menu(win, &nul_glyphinfo, &any, - flags.lootabc ? 0 : any.a_char, 0, ATR_NONE, + flags.lootabc ? 0 : any.a_char, '\"', ATR_NONE, clr, "all seen or remembered traps", MENU_ITEMFLAGS_NONE); + any.a_char = 'e'; + add_menu(win, &nul_glyphinfo, &any, + flags.lootabc ? 0 : any.a_char, '`', ATR_NONE, + clr, "nearby engravings", MENU_ITEMFLAGS_NONE); + any.a_char = 'E'; + add_menu(win, &nul_glyphinfo, &any, + flags.lootabc ? 0 : any.a_char, '|', ATR_NONE, + clr, "all seen or remembered engravings", + MENU_ITEMFLAGS_NONE); } end_menu(win, "What do you want to look at:"); if (select_menu(win, PICK_ONE, &pick_list) > 0) { @@ -2453,6 +2673,7 @@ do_look(int mode, coord *click_cc) destroy_nhwindow(win); } + dowhatiscmd: switch (i) { default: case 'q': @@ -2475,12 +2696,12 @@ do_look(int mode, coord *click_cc) *out_str = '\0'; for (invobj = gi.invent; invobj; invobj = invobj->nobj) if (invobj->invlet == invlet) { - strcpy(out_str, singular(invobj, xname)); + Strcpy(out_str, singular(invobj, xname)); break; } if (*out_str) - checkfile(out_str, (struct permonst *) 0, TRUE, TRUE, - (char *) 0); + (void) checkfile(out_str, pm, chkfilUsrTyped | chkfilDontAsk, + (char *) 0); return ECMD_OK; } case '?': @@ -2494,7 +2715,8 @@ do_look(int mode, coord *click_cc) return ECMD_OK; if (out_str[1]) { /* user typed in a complete string */ - checkfile(out_str, pm, TRUE, TRUE, (char *) 0); + (void) checkfile(out_str, pm, chkfilUsrTyped | chkfilDontAsk, + (char *) 0); return ECMD_OK; } sym = out_str[0]; @@ -2511,12 +2733,18 @@ do_look(int mode, coord *click_cc) case 'O': look_all(FALSE, FALSE); /* list all objects */ return ECMD_OK; - case '^': + case 't': look_traps(TRUE); /* list nearby traps */ return ECMD_OK; - case '\"': + case 'T': look_traps(FALSE); /* list all traps (visible or remembered) */ return ECMD_OK; + case 'e': + look_engrs(TRUE); /* list nearby engravings */ + return ECMD_OK; + case 'E': + look_engrs(FALSE); /* list all engravings (visible|remembered) */ + return ECMD_OK; } } else { /* clicklook */ cc.x = click_cc->x; @@ -2539,13 +2767,13 @@ do_look(int mode, coord *click_cc) if (from_screen || clicklook) { if (from_screen) { - if (Verbose(2, dolook)) + if (flags.verbose) pline("Please move the cursor to %s.", - what_is_an_unknown_object); + what_is_a_location); else - pline("Pick an object."); + pline("Pick %s.", what_is_a_location); - ans = getpos(&cc, quick, what_is_an_unknown_object); + ans = getpos(&cc, quick, what_is_a_location); if (ans < 0 || cc.x < 0) break; /* done */ flags.verbose = FALSE; /* only print long question once */ @@ -2557,21 +2785,9 @@ do_look(int mode, coord *click_cc) /* Finally, print out our explanation. */ if (found) { - if (ans != LOOK_QUICK && ans != LOOK_ONCE - && (ans == LOOK_VERBOSE || (flags.help && !quick)) - && !clicklook - && !strncmp(firstmatch, "engraving", 9)) { - char engbuf[BUFSZ]; - - engbuf[0] = '\0'; - if (add_quoted_engraving(cc.x, cc.y, engbuf)) { - Snprintf(eos(out_str), BUFSZ - strlen(out_str) - 1, - "%s", engbuf); - } - } /* use putmixed() because there may be an encoded glyph present */ putmixed(WIN_MESSAGE, 0, out_str); -#if defined(DUMPLOG) || defined (DUMPHTML) +#ifdef DUMPLOG_CORE { char dmpbuf[BUFSZ]; @@ -2595,8 +2811,10 @@ do_look(int mode, coord *click_cc) supplemental_name[0] = '\0'; Strcpy(temp_buf, firstmatch); - checkfile(temp_buf, supplemental_pm, FALSE, - (boolean) (ans == LOOK_VERBOSE), supplemental_name); + (void) checkfile(temp_buf, pm, + (ans == LOOK_VERBOSE) ? chkfilDontAsk + : chkfilNone, + supplemental_name); if (supplemental_pm) do_supplemental_info(supplemental_name, supplemental_pm, (boolean) (ans == LOOK_VERBOSE)); @@ -2610,7 +2828,7 @@ do_look(int mode, coord *click_cc) return ECMD_OK; } -static void +staticfn void look_region_nearby( coordxy *lo_x, coordxy *lo_y, coordxy *hi_x, coordxy *hi_y, boolean nearby) @@ -2623,7 +2841,7 @@ look_region_nearby( DISABLE_WARNING_FORMAT_NONLITERAL /* RESTORE is after do_supplemental_info() */ -static void +staticfn void look_all( boolean nearby, /* True => within BOLTLIM, False => entire map */ boolean do_mons) /* True => monsters, False => objects */ @@ -2643,8 +2861,6 @@ look_all( if (glyph_is_monster(glyph)) { struct monst *mtmp; - gb.bhitpos.x = x; /* [is this actually necessary?] */ - gb.bhitpos.y = y; if (u_at(x, y) && canspotself()) { (void) self_lookat(lookbuf); ++count; @@ -2724,7 +2940,7 @@ look_all( } /* give a /M style display of discovered traps, even when they're covered */ -static void +staticfn void look_traps(boolean nearby) { winid win; @@ -2788,6 +3004,95 @@ look_traps(boolean nearby) destroy_nhwindow(win); } +/* display of discovered engravings including headstones, even when they're + covered provided they've been read */ +staticfn void +look_engrs(boolean nearby) +{ + winid win; + struct engr *e; + char lookbuf[BUFSZ], outbuf[BUFSZ]; + 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 + or vault wall repair); not sure what to do about that */ + e = engr_at(x, y); + if (!e) + continue; + 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) { + (void) strsubst(lookbuf, "(grave with ", ""); + (void) strsubst(lookbuf, "(grave whose ", ""); + } else { + (void) strsubst(lookbuf, "(engraving with ", ""); + (void) strsubst(lookbuf, "(engraving ", "engraving "); + } + + 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 { + /* 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; + } + if (*lookbuf) { /* (redundant) */ + char coordbuf[20], cmode; + + cmode = (iflags.getpos_coords != GPCOORDS_NONE) + ? iflags.getpos_coords : GPCOORDS_MAP; + if (count == 1) { + Sprintf(outbuf, "%sseen or remembered engravings%s:", + nearby ? "nearby " : "", + nearby ? "" : " on this level"); + putstr(win, 0, upstart(outbuf)); + /* hack alert! Qt watches a text window for any line + with 4 consecutive spaces and renders the window + in a fixed-width font it if finds at least one */ + putstr(win, 0, " "); /* separator */ + } + /* prefix: "coords C " where 'C' is engrvng|grave symbol */ + Sprintf(outbuf, (cmode == GPCOORDS_SCREEN) ? "%s " + : (cmode == GPCOORDS_MAP) ? "%8s " + : "%12s ", + coord_desc(x, y, coordbuf, cmode)); + Sprintf(eos(outbuf), "%s ", encglyph(glyph)); + /* guard against potential overflow */ + lookbuf[sizeof lookbuf - 1 - strlen(outbuf)] = '\0'; + Strcat(outbuf, lookbuf); + putmixed(win, 0, outbuf); + } + } + } + if (count) + display_nhwindow(win, TRUE); + else + pline("No engravings seen or remembered%s.", nearby ? " nearby" : ""); + destroy_nhwindow(win); +} + static const char *suptext1[] = { "%s is a member of a marauding horde of orcs", "rumored to have brutally attacked and plundered", @@ -2810,15 +3115,18 @@ static const char *suptext2[] = { (char *) 0, }; -static void -do_supplemental_info(char *name, struct permonst *pm, boolean without_asking) +staticfn void +do_supplemental_info( + char *name, + struct permonst *pm, + boolean without_asking) { const char **textp; winid datawin = WIN_ERR; char *entrytext = name, *bp = (char *) 0, *bp2 = (char *) 0; char question[QBUFSZ]; boolean yes_to_moreinfo = FALSE; - boolean is_marauder = (name && pm && is_orc(pm)); + boolean is_marauder = is_orc(pm); /* * Provide some info on some specific things @@ -2837,7 +3145,7 @@ do_supplemental_info(char *name, struct permonst *pm, boolean without_asking) Strcpy(question, "More info about \""); /* +2 => length of "\"?" */ copynchars(eos(question), entrytext, - (int) (sizeof question - 1 - (strlen(question) + 2))); + (int) (sizeof question - 1 - (strlen(question) + 2))); Strcat(question, "\"?"); if (y_n(question) == 'y') yes_to_moreinfo = TRUE; @@ -2893,7 +3201,7 @@ doquickwhatis(void) int doidtrap(void) { - register struct trap *trap; + struct trap *trap; int tt, glyph; coordxy x, y; @@ -2984,7 +3292,7 @@ doidtrap(void) rest_on_space, #if SHELL, #if SUSPEND) are booleans. */ -static void +staticfn void whatdoes_help(void) { dlb *fp; @@ -3019,9 +3327,9 @@ struct wd_stack_frame { Bitfield(else_seen, 1); }; -static boolean whatdoes_cond(char *, struct wd_stack_frame *, int *, int); +staticfn boolean whatdoes_cond(char *, struct wd_stack_frame *, int *, int); -static boolean +staticfn boolean whatdoes_cond(char *buf, struct wd_stack_frame *stack, int *depth, int lnum) { const char badstackfmt[] = "cmdhlp: too many &%c directives at line %d."; @@ -3281,7 +3589,7 @@ dowhatdoes(void) return ECMD_OK; } -static void +staticfn void docontact(void) { winid cwin = create_nhwindow(NHW_TEXT); @@ -3311,79 +3619,79 @@ docontact(void) destroy_nhwindow(cwin); } -static void +staticfn void dispfile_help(void) { display_file(HELP, TRUE); } -static void +staticfn void dispfile_shelp(void) { display_file(SHELP, TRUE); } -static void +staticfn void dispfile_optionfile(void) { display_file(OPTIONFILE, TRUE); } -static void +staticfn void dispfile_optmenu(void) { display_file(OPTMENUHELP, TRUE); } -static void +staticfn void dispfile_license(void) { display_file(LICENSE, TRUE); } -static void +staticfn void dispfile_debughelp(void) { display_file(DEBUGHELP, TRUE); } -static void +staticfn void dispfile_usagehelp(void) { display_file(USAGEHELP, TRUE); } -static void +staticfn void hmenu_doextversion(void) { (void) doextversion(); } -static void +staticfn void hmenu_dohistory(void) { (void) dohistory(); } -static void +staticfn void hmenu_dowhatis(void) { (void) dowhatis(); } -static void +staticfn void hmenu_dowhatdoes(void) { (void) dowhatdoes(); } -static void +staticfn void hmenu_doextlist(void) { (void) doextlist(); } -static void +staticfn void domenucontrols(void) { winid cwin = create_nhwindow(NHW_TEXT); @@ -3432,7 +3740,7 @@ dohelp(void) menu_item *selected; anything any; int sel; - int clr = 0; + int clr = NO_COLOR; any = cg.zeroany; /* zero all bits */ start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -3471,7 +3779,7 @@ RESTORE_WARNING_FORMAT_NONLITERAL normally 'O' but could be bound to something else, or not bound at all; with the implementation of a simple options subset, now need 'mO' to get the full options command; format it as 'm O' */ -static char * +staticfn char * setopt_cmd(char *outbuf) { char cmdbuf[QBUFSZ]; diff --git a/src/pickup.c b/src/pickup.c index 8dd47217cd..29aeadaa0d 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 pickup.c $NHDT-Date: 1654760203 2022/06/09 07:36:43 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.310 $ */ +/* NetHack 3.7 pickup.c $NHDT-Date: 1720074481 2024/07/04 06:28:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.374 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -11,47 +11,50 @@ #define CONTAINED_SYM '>' /* from invent.c */ -static void simple_look(struct obj *, boolean); -static boolean query_classes(char *, boolean *, boolean *, const char *, +staticfn void simple_look(struct obj *, boolean); +staticfn boolean query_classes(char *, boolean *, boolean *, const char *, struct obj *, boolean, int *); -static boolean fatal_corpse_mistake(struct obj *, boolean); -static boolean describe_decor(void); -static void check_here(boolean); -static boolean n_or_more(struct obj *); -static boolean all_but_uchain(struct obj *); +staticfn boolean fatal_corpse_mistake(struct obj *, boolean); +staticfn boolean describe_decor(void); +staticfn void check_here(boolean); +staticfn boolean n_or_more(struct obj *); +staticfn boolean all_but_uchain(struct obj *); #if 0 /* not used */ -static boolean allow_cat_no_uchain(struct obj *); +staticfn boolean allow_cat_no_uchain(struct obj *); #endif -static int autopick(struct obj *, int, menu_item **); -static int count_categories(struct obj *, int); -static int delta_cwt(struct obj *, struct obj *); -static long carry_count(struct obj *, struct obj *, long, boolean, int *, +staticfn int autopick(struct obj *, int, menu_item **); +staticfn int count_categories(struct obj *, int); +staticfn int delta_cwt(struct obj *, struct obj *); +staticfn long carry_count(struct obj *, struct obj *, long, boolean, int *, int *); -static int lift_object(struct obj *, struct obj *, long *, boolean); -static boolean mbag_explodes(struct obj *, int); -static boolean is_boh_item_gone(void); -static void do_boh_explosion(struct obj *, boolean); -static long boh_loss(struct obj *, boolean); -static int in_container(struct obj *); -static int out_container(struct obj *); -static long mbag_item_gone(boolean, struct obj *, boolean); -static int stash_ok(struct obj *); -static void explain_container_prompt(boolean); -static boolean transfer_container_available(void); -static boolean select_transfer_container(void); -static int traditional_loot(boolean); -static int menu_loot(int, boolean); -static int tip_ok(struct obj *); -static struct obj *tipcontainer_gettarget(struct obj *, boolean *); -static int tipcontainer_checks(struct obj *, struct obj *, boolean); -static char in_or_out_menu(const char *, struct obj *, boolean, boolean, +staticfn int lift_object(struct obj *, struct obj *, long *, boolean); +staticfn void pickup_prinv(struct obj *, long, const char *); +staticfn boolean mbag_explodes(struct obj *, int); +staticfn boolean is_boh_item_gone(void); +staticfn void do_boh_explosion(struct obj *, boolean); +staticfn long boh_loss(struct obj *, boolean); +staticfn int in_container(struct obj *); +staticfn int out_container(struct obj *); +staticfn long mbag_item_gone(boolean, struct obj *, boolean); +staticfn int stash_ok(struct obj *); +staticfn void explain_container_prompt(boolean); +staticfn boolean transfer_container_available(void); +staticfn boolean select_transfer_container(void); +staticfn int traditional_loot(boolean); +staticfn int menu_loot(int, boolean); +staticfn int tip_ok(struct obj *); +staticfn int choose_tip_container_menu(void); +staticfn struct obj *tipcontainer_gettarget(struct obj *, boolean *); +staticfn int tipcontainer_checks(struct obj *, struct obj *, boolean); +staticfn char in_or_out_menu(const char *, struct obj *, boolean, boolean, boolean, boolean); -static boolean able_to_loot(coordxy, coordxy, boolean); -static boolean reverse_loot(void); -static boolean mon_beside(coordxy, coordxy); -static int do_loot_cont(struct obj **, int, int); -static int doloot_core(void); -static void u_took_forbidden_object(struct obj *); +staticfn boolean able_to_loot(coordxy, coordxy, boolean); +staticfn boolean reverse_loot(void); +staticfn boolean mon_beside(coordxy, coordxy); +staticfn int do_loot_cont(struct obj **, int, int); +staticfn int doloot_core(void); +staticfn void tipcontainer(struct obj *); +staticfn void u_took_forbidden_object(struct obj *); /* define for query_objlist() and autopickup() */ #define FOLLOW(curr, flags) \ @@ -64,14 +67,15 @@ static void u_took_forbidden_object(struct obj *); #define Icebox (gc.current_container->otyp == ICE_BOX) static const char - moderateloadmsg[] = "You have a little trouble lifting", - nearloadmsg[] = "You have much trouble lifting", - overloadmsg[] = "You have extreme difficulty lifting"; + slightloadpfx[] = "You have a little trouble", + moderateloadpfx[] = "You have trouble", + nearloadpfx[] = "You have much trouble", + overloadpfx[] = "You have extreme difficulty"; /* BUG: this lets you look at cockatrice corpses while blind without touching them */ /* much simpler version of the look-here code; used by query_classes() */ -static void +staticfn void simple_look(struct obj *otmp, /* list of objects */ boolean here) /* flag for type of obj list linkage */ { @@ -100,8 +104,8 @@ int collect_obj_classes(char ilets[], struct obj *otmp, boolean here, boolean (*filter)(OBJ_P), int *itemcount) { - register int iletct = 0; - register char c; + int iletct = 0; + char c; *itemcount = 0; ilets[iletct] = '\0'; /* terminate ilets so that strchr() will work */ @@ -117,6 +121,8 @@ collect_obj_classes(char ilets[], struct obj *otmp, boolean here, } /* + * For menustyle:Traditional and menustyle:Combination. + * * Suppose some '?' and '!' objects are present, but '/' objects aren't: * "a" picks all items without further prompting; * "A" steps through all items, asking one by one; @@ -127,11 +133,22 @@ collect_obj_classes(char ilets[], struct obj *otmp, boolean here, * (bug fix: 3.1.0 thru 3.1.3 treated it as "a"); * "?/a" or "a?/" or "/a?",&c picks all '?' even though no '/' * (ie, treated as if it had just been "?a"). + * + * Note: the behavior and meaning of 'a' vs 'A' is effectively reversed + * when using menustyle:Full. For Traditional, the choice is based on + * ease of typing (using 'a' is much more common than 'A'); for Full, + * it was changed to enhance menu entry ordering ('A' stands out, but + * some players complain that it is too easy to choose accidentally). */ -static boolean -query_classes(char oclasses[], boolean *one_at_a_time, boolean *everything, - const char *action, struct obj *objs, boolean here, - int *menu_on_demand) +staticfn boolean +query_classes( + char oclasses[], /* selected classes */ + boolean *one_at_a_time, /* to tell caller that user picked 'A' */ + boolean *everything, /* to tell caller that user picked 'a' */ + const char *action, /* verb for what activity needs objects */ + struct obj *objs, /* invent or container->cobj or level.objects[x][y] */ + boolean here, /* True: traverse by obj->nexthere; False: by obj->nobj */ + int *menu_on_demand) /* to tell caller that user picked 'm' */ { char ilets[36], inbuf[BUFSZ] = DUMMY; /* FIXME: hardcoded ilets[] length */ int iletct, oclassct; @@ -247,12 +264,30 @@ query_classes(char oclasses[], boolean *one_at_a_time, boolean *everything, 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 */ -static boolean +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) @@ -281,10 +316,34 @@ rider_corpse_revival(struct obj *obj, boolean remotely) return TRUE; } +/* wand of probing zapped down; perhaps hero is levitating while blind */ +void +force_decor(boolean via_probing) +{ + /* we don't want describe_decor() to defer feedback if hero is fumbling + with 1 turn left until next slip_or_trip(), or for ice_descr() to + omit thawing details if hero is probing when levitating while blind + (those will be skipped for look_here() and farlook() or autodescribe); + we can't control that by temporarily tweaking properties because that + could become noticeable if status gets updated while decor feedback + is being delivered */ + gd.decor_fumble_override = TRUE; + gd.decor_levitate_override = via_probing; + /* force current terrain to be different from previous location, or + uninteresting if previous location was actually inside solid stone */ + iflags.prev_decor = STONE; + (void) describe_decor(); + gd.decor_fumble_override = gd.decor_levitate_override = FALSE; + svl.lastseentyp[u.ux][u.uy] = levl[u.ux][u.uy].typ; +} + void -deferred_decor(boolean setup) /* True: deferring, False: catching up */ +deferred_decor( + boolean setup) /* True: deferring, False: catching up */ { - if (setup) { + if (!flags.mention_decor) { + iflags.defer_decor = FALSE; + } else if (setup) { iflags.defer_decor = TRUE; } else { (void) describe_decor(); @@ -294,7 +353,7 @@ deferred_decor(boolean setup) /* True: deferring, False: catching up */ /* handle 'mention_decor' (when walking onto a dungeon feature such as stairs or altar, describe it even if it isn't covered up by an object) */ -static boolean +staticfn boolean describe_decor(void) { char outbuf[BUFSZ], fbuf[QBUFSZ]; @@ -302,21 +361,23 @@ describe_decor(void) const char *dfeature; int ltyp; - if ((HFumbling & TIMEOUT) == 1L && !iflags.defer_decor) { + if ((HFumbling & TIMEOUT) == 1L /* about to slip_or_trip */ + && !iflags.defer_decor + && !gd.decor_fumble_override) { /* probe_decor() */ /* - * Work around a message sequencing issue: avoid + * Work around a message sequencing issue if Fumbling's periodic + * timeout is about to kick in: avoid the combination * |You are back on floor. * |You trip over . or You flounder. * when the trip is being caused by moving on ice as hero - * steps off ice onto non-ice. + * steps off ice onto non-ice. Defer the back-on-floor part if + * that is about to happen. */ deferred_decor(TRUE); return FALSE; } - ltyp = levl[u.ux][u.uy].typ; - if (ltyp == DRAWBRIDGE_UP) /* surface for spot in front of closed db */ - ltyp = db_under_typ(levl[u.ux][u.uy].drawbridgemask); + ltyp = SURFACE_AT(u.ux, u.uy); dfeature = dfeature_at(u.ux, u.uy, fbuf); /* we don't mention "ordinary" doors but do mention broken ones (and @@ -327,46 +388,53 @@ describe_decor(void) if (doorhere || Underwater || (ltyp == ICE && IS_POOL(iflags.prev_decor))) /* pooleffects() */ dfeature = 0; + /* + * TODO: if on ice, report moving between thicker and thinner ice (based + * on ice_descr()'s classification) as if moving onto different terrain. + */ if (ltyp == iflags.prev_decor && !IS_FURNITURE(ltyp)) { res = FALSE; } else if (dfeature) { if (waterhere) dfeature = strcpy(fbuf, waterbody_name(u.ux, u.uy)); - if (strcmp(dfeature, "swamp")) + if (strcmp(dfeature, "swamp") && ltyp != ICE) dfeature = an(dfeature); - if (Verbose(2, describe_decor1)) { + if (flags.verbose) { Sprintf(outbuf, "There is %s here.", dfeature); } else { if (dfeature != fbuf) Strcpy(fbuf, dfeature); Sprintf(outbuf, "%s.", upstart(fbuf)); } - pline("%s", outbuf); + if (ltyp == ICE && flags.mention_decor) + Norep("%s", outbuf); + else + pline("%s", outbuf); } else if (!Underwater) { if (IS_POOL(iflags.prev_decor) - || iflags.prev_decor == LAVAPOOL + || IS_LAVA(iflags.prev_decor) || iflags.prev_decor == ICE) { - const char *ground = surface(u.ux, u.uy); - - if (iflags.last_msg != PLNMSG_BACK_ON_GROUND) - pline("%s %s %s.", - Verbose(2, describe_decor2) ? "You are back" : "Back", - (Levitation || Flying) ? "over" : "on", - ground); + if (iflags.last_msg != PLNMSG_BACK_ON_GROUND) { + back_on_ground(FALSE); + } } } - iflags.prev_decor = ltyp; + /* describe_decor() is normally called when moving onto a different + type of terrain, but it is also called by pickup() even when + mention_decor is Off if hero can't reach floor; only adapt the next + describe_decor() by what has just occurred in this one when it's On */ + iflags.prev_decor = flags.mention_decor ? ltyp : STONE; return res; } /* look at the objects at our location, unless there are too many of them */ -static void +staticfn void check_here(boolean picked_some) { - register struct obj *obj; - register int ct = 0; + struct obj *obj; + int ct = 0; unsigned lhflags = picked_some ? LOOKHERE_PICKED_SOME : LOOKHERE_NOFLAGS; if (flags.mention_decor) { @@ -375,14 +443,14 @@ check_here(boolean picked_some) } /* count the objects here */ - for (obj = gl.level.objects[u.ux][u.uy]; obj; obj = obj->nexthere) { + for (obj = svl.level.objects[u.ux][u.uy]; obj; obj = obj->nexthere) { if (obj != uchain) ct++; } /* If there are objects here, take a look. */ if (ct) { - if (gc.context.run) + if (svc.context.run) nomul(0); flush_screen(1); (void) look_here(ct, lhflags); @@ -392,7 +460,7 @@ check_here(boolean picked_some) } /* query_objlist callback: return TRUE if obj's count is >= reference value */ -static boolean +staticfn boolean n_or_more(struct obj *obj) { if (obj == uchain) @@ -441,7 +509,7 @@ add_valid_menu_class(int c) } /* query_objlist callback: return TRUE if not uchain */ -static boolean +staticfn boolean all_but_uchain(struct obj *obj) { return (boolean) (obj != uchain); @@ -458,6 +526,12 @@ allow_all(struct obj *obj UNUSED) boolean allow_category(struct obj *obj) { + /* If no filters are active, nothing will match unless + paranoid_confirm:A is set. */ + if (!gc.class_filter && !gs.shop_filter && !gb.bucx_filter + && !gp.picked_filter && !ParanoidAutoAll) + return FALSE; + /* For coins, if any class filter is specified, accept if coins * are included regardless of whether either unpaid or BUC-status * is also specified since player has explicitly requested coins. @@ -523,7 +597,7 @@ allow_category(struct obj *obj) #if 0 /* not used */ /* query_objlist callback: return TRUE if valid category (class), no uchain */ -static boolean +staticfn boolean allow_cat_no_uchain(struct obj *obj) { if (obj != uchain @@ -617,6 +691,9 @@ pickup(int what) /* should be a long */ return 0; } + /* used by pickup_object() for encumbrance feedback */ + gp.pickup_encumbrance = 0; + if (what < 0) /* pick N of something */ count = -what; else /* pick anything */ @@ -626,7 +703,7 @@ pickup(int what) /* should be a long */ struct trap *t; /* no auto-pick if no-pick move, nothing there, or in a pool */ - if (autopickup && (gc.context.nopick || !OBJ_AT(u.ux, u.uy) + if (autopickup && (svc.context.nopick || !OBJ_AT(u.ux, u.uy) || (is_pool(u.ux, u.uy) && !Underwater) || is_lava(u.ux, u.uy))) { if (flags.mention_decor) @@ -638,16 +715,17 @@ pickup(int what) /* should be a long */ t = t_at(u.ux, u.uy); if (!can_reach_floor(t && is_pit(t->ttyp))) { (void) describe_decor(); /* even when !flags.mention_decor */ - if ((gm.multi && !gc.context.run) || (autopickup && !flags.pickup) + if ((gm.multi && !svc.context.run) + || (autopickup && !flags.pickup) || (t && (uteetering_at_seen_pit(t) || uescaped_shaft(t)))) read_engr_at(u.ux, u.uy); return 0; } - /* multi && !gc.context.run means they are in the middle of some other - * action, or possibly paralyzed, sleeping, etc.... and they just - * teleported onto the object. They shouldn't pick it up. + /* multi && !svc.context.run means they are in the middle of some + * other action, or possibly paralyzed, sleeping, etc.... and they + * just teleported onto the object. They shouldn't pick it up. */ - if ((gm.multi && !gc.context.run) + if ((gm.multi && !svc.context.run) || (autopickup && !flags.pickup) || notake(gy.youmonst.data)) { check_here(FALSE); @@ -658,14 +736,14 @@ pickup(int what) /* should be a long */ } /* if there's anything here, stop running */ - if (OBJ_AT(u.ux, u.uy) && gc.context.run && gc.context.run != 8 - && !gc.context.nopick) + if (OBJ_AT(u.ux, u.uy) && svc.context.run && svc.context.run != 8 + && !svc.context.nopick) nomul(0); } add_valid_menu_class(0); /* reset */ if (!u.uswallow) { - objchain_p = &gl.level.objects[u.ux][u.uy]; + objchain_p = &svl.level.objects[u.ux][u.uy]; traverse_how = BY_NEXTHERE; } else { objchain_p = &u.ustuck->minvent; @@ -796,6 +874,7 @@ pickup(int what) /* should be a long */ lcount = (long) yn_number; if (lcount > obj->quan) lcount = obj->quan; + FALLTHROUGH; /*FALLTHRU*/ default: /* 'y' */ break; @@ -829,6 +908,7 @@ pickup(int what) /* should be a long */ check_here(n_picked > 0); } pickupdone: + gp.pickup_encumbrance = 0; add_valid_menu_class(0); /* reset */ return (n_tried > 0); } @@ -867,6 +947,16 @@ autopick_testobj(struct obj *otmp, boolean calc_costly) if (costly && !otmp->no_charge) return FALSE; + /* pickup_thrown/pickup_stolen/nopick_dropped override pickup_types and + exceptions */ + if ((flags.pickup_thrown && otmp->how_lost == LOST_THROWN) + || (flags.pickup_stolen && otmp->how_lost == LOST_STOLEN)) + 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)); @@ -875,9 +965,6 @@ autopick_testobj(struct obj *otmp, boolean calc_costly) if (ape) pickit = ape->grab; - /* pickup_thrown overrides pickup_types and exceptions */ - if (!pickit) - pickit = (flags.pickup_thrown && otmp->was_thrown); return pickit; } @@ -888,7 +975,7 @@ autopick_testobj(struct obj *otmp, boolean calc_costly) * picked is zero, the pickup list is left alone. The caller of this * function must free the pickup list. */ -static int +staticfn int autopick( struct obj *olist, /* the object list */ int follow, /* how to follow the object list */ @@ -960,7 +1047,7 @@ query_objlist(const char *qstr, /* query string */ unsigned sortflags; glyph_info tmpglyphinfo = nul_glyphinfo; Loot *sortedolist, *srtoli; - int clr = 0; + int clr = NO_COLOR; *pick_list = (menu_item *) 0; if (!olist && !engulfer) @@ -1010,8 +1097,7 @@ query_objlist(const char *qstr, /* query string */ if (gt.this_title) { /* dotypeinv() supplies gt.this_title to display as initial header; intentionally avoid the menu_headings highlight attribute here */ - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, gt.this_title, MENU_ITEMFLAGS_NONE); + add_menu_str(win, gt.this_title); } /* * Run through the list and add the objects to the menu. If @@ -1038,13 +1124,12 @@ query_objlist(const char *qstr, /* query string */ if ((*allow)(curr)) { /* if sorting, print type name (once only) */ if (sorted && !printed_type_name) { + boolean with_oc_sym = (how != PICK_NONE + && iflags.menu_head_objsym); + any = cg.zeroany; - add_menu(win, &nul_glyphinfo, &any, 0, 0, - iflags.menu_headings, clr, - let_to_name(*pack, FALSE, - ((how != PICK_NONE) - && iflags.menu_head_objsym)), - MENU_ITEMFLAGS_NONE); + add_menu_heading(win, + let_to_name(*pack, FALSE, with_oc_sym)); printed_type_name = TRUE; } @@ -1071,8 +1156,7 @@ query_objlist(const char *qstr, /* query string */ if (sorted && n > 1) { Sprintf(buf, "%s Creatures", digests(u.ustuck->data) ? "Swallowed" : "Engulfed"); - add_menu(win, &nul_glyphinfo, &any, 0, 0, iflags.menu_headings, - clr, buf, MENU_ITEMFLAGS_NONE); + add_menu_heading(win, buf); } fake_hero_object = cg.zeroobj; fake_hero_object.quan = 1L; /* not strictly necessary... */ @@ -1138,15 +1222,19 @@ query_objlist(const char *qstr, /* query string */ } /* + * For menustyle:Full. + * * allow menu-based category (class) selection (for Drop,take off etc.) * + * If ParanoidAutoAll, requires confirmation when 'A' has been picked. */ int -query_category(const char *qstr, /* query string */ - struct obj *olist, /* the list to pick from */ - int qflags, /* behavior modification flags */ - menu_item **pick_list, /* return list of items picked */ - int how) /* type of query */ +query_category( + const char *qstr, /* query string */ + struct obj *olist, /* the list to pick from */ + int qflags, /* behavior modification flags */ + menu_item **pick_list, /* return list of items picked */ + int how) /* type of query */ { int n; winid win; @@ -1157,45 +1245,54 @@ query_category(const char *qstr, /* query string */ char invlet; int ccount; boolean (*ofilter)(OBJ_P) = (boolean (*)(OBJ_P)) 0; - boolean do_unpaid = FALSE; - boolean do_blessed = FALSE, do_cursed = FALSE, do_uncursed = FALSE, - do_buc_unknown = FALSE; - int num_buc_types = 0; - unsigned itemflags = MENU_ITEMFLAGS_NONE; - int num_justpicked = 0; - int clr = 0; + boolean show_a, + do_unpaid = FALSE, do_usedup = FALSE, + do_blessed = FALSE, do_cursed = FALSE, + do_uncursed = FALSE, do_buc_unknown = FALSE, + do_worn = FALSE, verify_All = FALSE; + int num_buc_types = 0, num_justpicked = 0, clr = NO_COLOR; *pick_list = (menu_item *) 0; if (!olist) return 0; - if ((qflags & UNPAID_TYPES) && count_unpaid(olist)) + if ((qflags & UNPAID_TYPES) != 0 && count_unpaid(olist)) do_unpaid = TRUE; - if (qflags & WORN_TYPES) + /* caller only passes BILLED_TYPES when there are some used up items + on shop's bill */ + if ((qflags & BILLED_TYPES) != 0) + do_usedup = TRUE; + /* for the 'A' command to remove worn/wielded */ + if ((qflags & WORN_TYPES) != 0) { + do_worn = TRUE; ofilter = is_worn; - if ((qflags & BUC_BLESSED) && count_buc(olist, BUC_BLESSED, ofilter)) { + } + if ((qflags & BUC_BLESSED) != 0 + && count_buc(olist, BUC_BLESSED, ofilter)) { do_blessed = TRUE; num_buc_types++; } - if ((qflags & BUC_CURSED) && count_buc(olist, BUC_CURSED, ofilter)) { + if ((qflags & BUC_CURSED) != 0 + && count_buc(olist, BUC_CURSED, ofilter)) { do_cursed = TRUE; num_buc_types++; } - if ((qflags & BUC_UNCURSED) && count_buc(olist, BUC_UNCURSED, ofilter)) { + if ((qflags & BUC_UNCURSED) != 0 + && count_buc(olist, BUC_UNCURSED, ofilter)) { do_uncursed = TRUE; num_buc_types++; } - if ((qflags & BUC_UNKNOWN) && count_buc(olist, BUC_UNKNOWN, ofilter)) { + if ((qflags & BUC_UNKNOWN) != 0 + && count_buc(olist, BUC_UNKNOWN, ofilter)) { do_buc_unknown = TRUE; num_buc_types++; } - if (qflags & JUSTPICKED) { + if ((qflags & JUSTPICKED) != 0) { num_justpicked = count_justpicked(olist); } ccount = count_categories(olist, qflags); /* no point in actually showing a menu for a single category */ - if (ccount == 1 && !do_unpaid && num_buc_types <= 1 - && !(qflags & BILLED_TYPES)) { + if (ccount == 1 && !do_unpaid && !do_usedup && num_buc_types <= 1) { for (curr = olist; curr; curr = FOLLOW(curr, qflags)) { if (ofilter && !(*ofilter)(curr)) continue; @@ -1217,35 +1314,45 @@ query_category(const char *qstr, /* query string */ start_menu(win, MENU_BEHAVE_STANDARD); pack = strcpy(packbuf, flags.inv_order); - if (qflags & INCLUDE_VENOM) + if ((qflags & INCLUDE_VENOM) != 0) (void) strkitten(pack, VENOM_CLASS); /* venom is not in inv_order */ - if (qflags & CHOOSE_ALL) { + show_a = ((qflags & ALL_TYPES) != 0 && ccount > 1); + + if ((qflags & CHOOSE_ALL) != 0) { invlet = 'A'; any = cg.zeroany; any.a_int = 'A'; - itemflags = MENU_ITEMFLAGS_SKIPINVERT; add_menu(win, &nul_glyphinfo, &any, invlet, 0, ATR_NONE, clr, - (qflags & WORN_TYPES) ? "Auto-select every item being worn" - : "Auto-select every relevant item", - itemflags); - - any = cg.zeroany; - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); + /* note: menu_remarm() doesn't pass the CHOOSE_ALL flag, + so do_worn handling here is moot */ + do_worn ? "Auto-select every item being worn or wielded" + : "Auto-select every relevant item", + MENU_ITEMFLAGS_SKIPINVERT); + verify_All = (how == PICK_ANY) && ParanoidAutoAll; + if (!verify_All) { + if (!ga.A_first_hint++ || iflags.cmdassist) + add_menu_str(win, + " (ignored unless some other choices are also picked)"); + } else if (show_a) { + if (!ga.A_second_hint++ || iflags.cmdassist) + add_menu_str(win, + " (if no other choices are picked, 'a' is implied)"); + } + /* blank separator */ + add_menu_str(win, ""); } - if ((qflags & ALL_TYPES) && (ccount > 1)) { - invlet = 'a'; + invlet = 'a'; + if (show_a) { any = cg.zeroany; any.a_int = ALL_TYPES_SELECTED; - itemflags = MENU_ITEMFLAGS_SKIPINVERT; add_menu(win, &nul_glyphinfo, &any, invlet, 0, ATR_NONE, clr, - (qflags & WORN_TYPES) ? "All worn types" : "All types", - itemflags); - invlet = 'b'; - } else - invlet = 'a'; + do_worn ? "All worn and wielded types" : "All types", + MENU_ITEMFLAGS_SKIPINVERT); + ++invlet; /* invlet = 'b'; */ + } + do { collected_type_name = FALSE; for (curr = olist; curr; curr = FOLLOW(curr, qflags)) { @@ -1253,15 +1360,16 @@ query_category(const char *qstr, /* query string */ if (ofilter && !(*ofilter)(curr)) continue; if (!collected_type_name) { + int oclass = (int) curr->oclass; + any = cg.zeroany; - any.a_int = curr->oclass; - add_menu( - win, &nul_glyphinfo, &any, invlet++, - def_oc_syms[(int) objects[curr->otyp].oc_class].sym, - ATR_NONE, clr, let_to_name(*pack, FALSE, - (how != PICK_NONE) - && iflags.menu_head_objsym), - MENU_ITEMFLAGS_NONE); + any.a_int = oclass; + add_menu(win, &nul_glyphinfo, &any, invlet++, + (int) def_oc_syms[oclass].sym, ATR_NONE, clr, + let_to_name(*pack, FALSE, + (how != PICK_NONE + && iflags.menu_head_objsym)), + MENU_ITEMFLAGS_NONE); collected_type_name = TRUE; } } @@ -1274,11 +1382,10 @@ query_category(const char *qstr, /* query string */ } } while (*pack); - if (do_unpaid || (qflags & BILLED_TYPES) || do_blessed || do_cursed - || do_uncursed || do_buc_unknown) { - any = cg.zeroany; - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); + if (do_unpaid || do_usedup + || do_blessed || do_cursed || do_uncursed || do_buc_unknown + || num_justpicked) { + add_menu_str(win, ""); } /* unpaid items if there are any */ @@ -1287,48 +1394,48 @@ query_category(const char *qstr, /* query string */ any = cg.zeroany; any.a_int = 'u'; add_menu(win, &nul_glyphinfo, &any, invlet, 0, - ATR_NONE, clr, "Unpaid items", MENU_ITEMFLAGS_NONE); + ATR_NONE, clr, "Unpaid items", MENU_ITEMFLAGS_SKIPINVERT); } /* billed items: checked by caller, so always include if BILLED_TYPES */ - if (qflags & BILLED_TYPES) { + if (do_usedup) { invlet = 'x'; any = cg.zeroany; any.a_int = 'x'; add_menu(win, &nul_glyphinfo, &any, invlet, 0, ATR_NONE, clr, - "Unpaid items already used up", MENU_ITEMFLAGS_NONE); + "Unpaid items already used up", MENU_ITEMFLAGS_SKIPINVERT); } /* items with b/u/c/unknown if there are any; this cluster of menu entries is in alphabetical order, reversing the usual sequence of 'U' and 'C' in BUCX */ - itemflags = MENU_ITEMFLAGS_SKIPINVERT; if (do_blessed) { invlet = 'B'; any = cg.zeroany; any.a_int = 'B'; - add_menu(win, &nul_glyphinfo, &any, invlet, 0, ATR_NONE, - clr, "Items known to be Blessed", itemflags); + add_menu(win, &nul_glyphinfo, &any, invlet, 0, ATR_NONE, clr, + "Items known to be Blessed", MENU_ITEMFLAGS_SKIPINVERT); } if (do_cursed) { invlet = 'C'; any = cg.zeroany; any.a_int = 'C'; - add_menu(win, &nul_glyphinfo, &any, invlet, 0, ATR_NONE, - clr, "Items known to be Cursed", itemflags); + add_menu(win, &nul_glyphinfo, &any, invlet, 0, ATR_NONE, clr, + "Items known to be Cursed", MENU_ITEMFLAGS_SKIPINVERT); } if (do_uncursed) { invlet = 'U'; any = cg.zeroany; any.a_int = 'U'; - add_menu(win, &nul_glyphinfo, &any, invlet, 0, ATR_NONE, - clr, "Items known to be Uncursed", itemflags); + add_menu(win, &nul_glyphinfo, &any, invlet, 0, ATR_NONE, clr, + "Items known to be Uncursed", MENU_ITEMFLAGS_SKIPINVERT); } if (do_buc_unknown) { invlet = 'X'; any = cg.zeroany; any.a_int = 'X'; - add_menu(win, &nul_glyphinfo, &any, invlet, 0, ATR_NONE, - clr, "Items of unknown Bless/Curse status", itemflags); + add_menu(win, &nul_glyphinfo, &any, invlet, 0, ATR_NONE, clr, + "Items of unknown Bless/Curse status", + MENU_ITEMFLAGS_SKIPINVERT); } if (num_justpicked) { char tmpbuf[BUFSZ]; @@ -1337,38 +1444,91 @@ query_category(const char *qstr, /* query string */ Sprintf(tmpbuf, "Just picked up: %s", doname(find_justpicked(olist))); else - Sprintf(tmpbuf, "Items you just picked up"); + Strcpy(tmpbuf, "Items you just picked up"); invlet = 'P'; any = cg.zeroany; any.a_int = 'P'; - add_menu(win, &nul_glyphinfo, &any, invlet, 0, ATR_NONE, - clr, tmpbuf, itemflags); + add_menu(win, &nul_glyphinfo, &any, invlet, 0, ATR_NONE, clr, + tmpbuf, MENU_ITEMFLAGS_SKIPINVERT); } end_menu(win, qstr); n = select_menu(win, how, pick_list); + if (n > 0) { + assert(*pick_list != NULL); + } + + /* handle ParanoidAutoAll by confirming 'A' choice if present */ + if (n > 0 && verify_All) { + int i, j; + + for (i = 0; i < n; ++i) + if ((*pick_list)[i].item.a_int == 'A') { + /* ParanoidAutoAll is set (otherwise verify_All is false); + if ParanoidConfirm is also set, require "yes" rather than + just "y" to accept (and "no" rather than "n" to decline; + accepts "quit" and ESC without converting them to 'n') */ + switch (paranoid_ynq(ParanoidConfirm, + "Really autoselect All?", TRUE)) { + case 'y': + /* yes => honor Auto-select All */ + break; + case 'n': + /* no => remove 'A' from the list; if that would make + it empty then replace with 'a' */ + if (n > 1) { + for (j = i + 1; j < n; ++j) + (*pick_list)[j - 1] = (*pick_list)[j]; + --n; + break; /* from switch */ + } else if ((qflags & ALL_TYPES) != 0) { + /* 'A' was the only choice; convert it to 'a' and + then let the next menu offer a choice of all */ + (*pick_list)[0].item.a_int = ALL_TYPES_SELECTED; + /* assert( n == 1 ); */ + break; /* from switch */ + } + FALLTHROUGH; + /*FALLTHRU*/ + case 'q': + default: + /* quit | ESC => cancel, no Auto-select and no 2nd menu */ + n = 0; + free((genericptr_t) *pick_list), *pick_list = 0; + break; + } + break; /* from for => goto query_done; */ + } + } else if (n == 1 && !verify_All && (*pick_list)[0].item.a_int == 'A') { + /* without paranoid_confirm:A, choosing 'A' by itself is rejected */ + n = 0; + free((genericptr_t) *pick_list), *pick_list = 0; + /* the menu entry description is "Auto-select every relevant item" + [not sure whether issuing a message here is a good idea...] */ + pline("No relevant items selected."); + } query_done: destroy_nhwindow(win); - if (n < 0) - n = 0; /* caller's don't expect -1 */ + if (n < 0) /* closed menu with ESC */ + n = 0; /* callers don't expect -1 */ return n; } -static int +staticfn int count_categories(struct obj *olist, int qflags) { char *pack; boolean counted_category; int ccount = 0; struct obj *curr; + boolean do_worn = (qflags & WORN_TYPES) != 0; pack = flags.inv_order; do { counted_category = FALSE; for (curr = olist; curr; curr = FOLLOW(curr, qflags)) { if (curr->oclass == *pack) { - if ((qflags & WORN_TYPES) - && !(curr->owornmask & (W_ARMOR | W_ACCESSORY - | W_WEAPONS))) + if (do_worn && !(curr->owornmask + & (W_ARMOR | W_ACCESSORY | W_WEAPONS))) continue; if (!counted_category) { ccount++; @@ -1386,7 +1546,7 @@ count_categories(struct obj *olist, int qflags) * object is removed from it. Use before and after weight amounts rather * than trying to match the calculation used by weight() in mkobj.c. */ -static int +staticfn int delta_cwt(struct obj *container, struct obj *obj) { struct obj **prev; @@ -1412,7 +1572,7 @@ delta_cwt(struct obj *container, struct obj *obj) } /* could we carry `obj'? if not, could we carry some of it/them? */ -static long +staticfn long carry_count(struct obj *obj, /* object to pick up... */ struct obj *container, /* ...bag it is coming out of */ long count, @@ -1547,8 +1707,7 @@ carry_count(struct obj *obj, /* object to pick up... */ } /* determine whether character is able and player is willing to carry `obj' */ -static -int +staticfn int lift_object( struct obj *obj, /* object to pick up... */ struct obj *container, /* ...bag it's coming out of */ @@ -1566,13 +1725,16 @@ lift_object( and for boulder picked up by hero poly'd into a giant; override availability of open inventory slot iff not already carrying one */ if (obj->otyp == BOULDER && throws_rocks(gy.youmonst.data)) { - if (inv_cnt(FALSE) < 52 || !carrying(obj->otyp) + if (inv_cnt(FALSE) < invlet_basic || !carrying(obj->otyp) || merge_choice(gi.invent, obj)) return 1; /* lift regardless of current situation */ /* if we reach here, we're out of slots and already have at least - one of these, so treat this one more like a normal item */ + one of these, so treat this one more like a normal item + [this was using simpleonames(obj) for shortest description, but + that's suboptimal for loadstones because it omits user-assigned + type name which is something of interest for gray stones] */ You("are carrying too much stuff to pick up %s %s.", - (obj->quan == 1L) ? "another" : "more", simpleonames(obj)); + (obj->quan == 1L) ? "another" : "more", xname(obj)); return -1; } @@ -1583,7 +1745,8 @@ lift_object( } else if (obj->oclass != COIN_CLASS /* [exception for gold coins will have to change if silver/copper ones ever get implemented] */ - && inv_cnt(FALSE) >= 52 && !merge_choice(gi.invent, obj)) { + && inv_cnt(FALSE) >= invlet_basic + && !merge_choice(gi.invent, obj)) { /* if there is some gold here (and we haven't already skipped it), we aren't limited by the 52 item limit for it, but caller and "grandcaller" aren't prepared to skip stuff and then pickup @@ -1607,14 +1770,12 @@ lift_object( long savequan = obj->quan; obj->quan = *cnt_p; - Strcpy(qbuf, (next_encumbr > HVY_ENCUMBER) - ? overloadmsg - : (next_encumbr > MOD_ENCUMBER) - ? nearloadmsg - : moderateloadmsg); - if (container) - (void) strsubst(qbuf, "lifting", "removing"); - Strcat(qbuf, " "); + Sprintf(qbuf, "%s %s ", + (next_encumbr >= EXT_ENCUMBER) ? overloadpfx + : (next_encumbr >= HVY_ENCUMBER) ? nearloadpfx + : (next_encumbr >= MOD_ENCUMBER) ? moderateloadpfx + : slightloadpfx, + !container ? "lifting" : "removing"); (void) safe_qbuf(qbuf, qbuf, ". Continue?", obj, doname, ansimpleoname, something); obj->quan = savequan; @@ -1772,7 +1933,7 @@ thiefstone_teleport(struct obj* stone, struct obj* obj, boolean dobill) obj->oy = ky; if (Fits_in_container(obj)) { /* put into a container on this spot, if possible */ - for (cobj = gl.level.objects[obj->ox][obj->oy]; cobj; + for (cobj = svl.level.objects[obj->ox][obj->oy]; cobj; cobj = cobj->nexthere) { if (Is_container(cobj)) { if (obj_is_burning(obj)) @@ -1889,10 +2050,11 @@ thiefstone_tele_mon(struct obj* stone, struct monst* mon) int pickup_object( struct obj *obj, - long count, + long count, /* if non-zero, pick up a subset of this amount */ boolean telekinesis) /* not picking it up directly by hand */ { - int res, nearload; + unsigned save_how_lost; + int res; boolean forbidden = (obj->where == OBJ_FLOOR && Is_styxmarsh(&u.uz) && is_pool(obj->ox, obj->oy)); @@ -1950,22 +2112,33 @@ pickup_object( } } - if ((res = lift_object(obj, (struct obj *) 0, &count, telekinesis)) <= 0) + save_how_lost = obj->how_lost; + /* obj has either already passed autopick_testobj or we are explicitly + picking it off the floor, so override obj->how_lost; otherwise we + couldn't pick up a thrown, stolen, or dropped item that was split + off from a carried stack even while still carrying the rest of the + stack unless we have at least one free slot available */ + obj->how_lost &= ~LOSTOVERRIDEMASK; /* affects merge_choice() */ + res = lift_object(obj, (struct obj *) 0, &count, telekinesis); + obj->how_lost = save_how_lost; /* even when res > 0, + * in case we call splitobj() below */ + if (res <= 0) return res; - /* Whats left of the special case for gold :-) */ + /* What's left of the special case for gold :-) */ if (obj->oclass == COIN_CLASS) - gc.context.botl = 1; + disp.botl = TRUE; if (obj->quan != count) obj = splitobj(obj, count); + obj->how_lost &= ~LOSTOVERRIDEMASK; obj = pick_obj(obj); if (uwep && uwep == obj) gm.mrg_to_wielded = TRUE; - nearload = near_capacity(); - prinv(nearload == SLT_ENCUMBER ? moderateloadmsg : (char *) 0, obj, - count); + pickup_prinv(obj, count, "lifting"); + if (obj->ghostly) + fix_ghostly_obj(obj); gm.mrg_to_wielded = FALSE; if (forbidden) @@ -2022,6 +2195,36 @@ pick_obj(struct obj *otmp) return result; } +/* pickup_object()/out_container() helper; + print an added-to-invent message for current object, limiting feedback + about encumbrance to the first item which causes that to change */ +staticfn void +pickup_prinv( + struct obj *obj, + long count, + const char *verb) +{ + char pbuf[QBUFSZ]; + const char *prefix; + int nearload = near_capacity(); + + pbuf[0] = '\0'; + if (nearload == gp.pickup_encumbrance) { + prefix = (char *) 0; + } else { + prefix = (nearload >= EXT_ENCUMBER) ? overloadpfx + : (nearload >= HVY_ENCUMBER) ? nearloadpfx + : (nearload >= MOD_ENCUMBER) ? moderateloadpfx + : (nearload >= SLT_ENCUMBER) ? slightloadpfx + : (char *) 0; + gp.pickup_encumbrance = nearload; + } + if (prefix) + Sprintf(pbuf, "%s %s", prefix, verb); + + prinv(pbuf, obj, count); +} + /* * prints a message if encumbrance changed since the last check and * returns the new encumbrance value (from near_capacity()). @@ -2048,8 +2251,8 @@ encumber_msg(void) newcap == 4 ? "can barely" : "can't even"); break; } - update_inventory(); - gc.context.botl = 1; + update_inventory(); /* carry cap in perm_invent */ + disp.botl = TRUE; } else if (go.oldcap > newcap) { switch (newcap) { case 0: @@ -2066,8 +2269,8 @@ encumber_msg(void) stagger(gy.youmonst.data, "stagger")); break; } - update_inventory(); - gc.context.botl = 1; + update_inventory(); /* carry cap in perm_invent */ + disp.botl = TRUE; } go.oldcap = newcap; @@ -2081,7 +2284,7 @@ container_at(coordxy x, coordxy y, boolean countem) struct obj *cobj, *nobj; int container_count = 0; - for (cobj = gl.level.objects[x][y]; cobj; cobj = nobj) { + for (cobj = svl.level.objects[x][y]; cobj; cobj = nobj) { nobj = cobj->nexthere; if (Is_container(cobj)) { container_count++; @@ -2092,7 +2295,7 @@ container_at(coordxy x, coordxy y, boolean countem) return container_count; } -static boolean +staticfn boolean able_to_loot( coordxy x, coordxy y, boolean looting) /* loot vs tip */ @@ -2123,7 +2326,7 @@ able_to_loot( return TRUE; } -static boolean +staticfn boolean mon_beside(coordxy x, coordxy y) { int i, j; @@ -2139,7 +2342,7 @@ mon_beside(coordxy x, coordxy y) return FALSE; } -static int +staticfn int do_loot_cont( struct obj **cobjp, int cindex, /* index of this container (1..N)... */ @@ -2153,7 +2356,7 @@ do_loot_cont( int res = ECMD_OK; #if 0 - if (ccount < 2 && (gl.level.objects[cobj->ox][cobj->oy] == cobj)) + if (ccount < 2 && (svl.level.objects[cobj->ox][cobj->oy] == cobj)) pline("%s locked.", cobj->lknown ? "It is" : "Hmmm, it turns out to be"); else @@ -2181,7 +2384,7 @@ do_loot_cont( res = ECMD_TIME; /* attempting to untrap or unlock might trigger a trap which destroys 'cobj'; inform caller if that happens */ - for (otmp = gl.level.objects[ox][oy]; otmp; + for (otmp = svl.level.objects[ox][oy]; otmp; otmp = otmp->nexthere) if (otmp == cobj) break; @@ -2193,7 +2396,8 @@ do_loot_cont( && res != ECMD_TIME && ccount == 1 && u_have_forceable_weapon()) { /* single container, and we could #force it open... */ - cmdq_add_ec(CQ_CANNED, doforce); /* doforce asks for confirmation */ + /* note: doforce asks for confirmation */ + cmdq_add_ec(CQ_CANNED, doforce); ga.abort_looting = TRUE; } } @@ -2236,11 +2440,11 @@ doloot(void) } /* loot a container on the floor or loot saddle from mon. */ -static int +staticfn int doloot_core(void) { struct obj *cobj, *nobj; - register int c = -1; + int c = -1; int timepassed = 0; coord cc; boolean underfoot = TRUE; @@ -2249,7 +2453,7 @@ doloot_core(void) int prev_inquiry = 0; boolean mon_interact = FALSE; int num_conts = 0; - int clr = 0; + int clr = NO_COLOR; ga.abort_looting = FALSE; @@ -2307,12 +2511,12 @@ doloot_core(void) win = create_nhwindow(NHW_MENU); start_menu(win, MENU_BEHAVE_STANDARD); - for (cobj = gl.level.objects[cc.x][cc.y]; cobj; + for (cobj = svl.level.objects[cc.x][cc.y]; cobj; cobj = cobj->nexthere) if (Is_container(cobj)) { any.a_obj = cobj; - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, doname(cobj), MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, + doname(cobj), MENU_ITEMFLAGS_NONE); } end_menu(win, "Loot which containers?"); n = select_menu(win, PICK_ANY, &pick_list); @@ -2333,7 +2537,7 @@ doloot_core(void) if (n != 0) c = 'y'; } else { /* num_conts <= 1 */ - for (cobj = gl.level.objects[cc.x][cc.y]; cobj; cobj = nobj) { + for (cobj = svl.level.objects[cc.x][cc.y]; cobj; cobj = nobj) { nobj = cobj->nexthere; if (Is_container(cobj)) { @@ -2407,7 +2611,7 @@ doloot_core(void) } /* called when attempting to #loot while confused */ -static boolean +staticfn boolean reverse_loot(void) { struct obj *goldob = 0, *coffers, *otmp, boxdummy; @@ -2416,9 +2620,9 @@ reverse_loot(void) int n, x = u.ux, y = u.uy; if (!rn2(3)) { - /* n objects: 1/(n+1) chance per object plus 1/(n+1) to fall off end - */ - for (n = inv_cnt(TRUE), otmp = gi.invent; otmp; --n, otmp = otmp->nobj) + /* n objects: 1/(n+1) chance per object, 1/(n+1) to fall off end */ + for (n = inv_cnt(TRUE), otmp = gi.invent; otmp; + --n, otmp = otmp->nobj) if (!rn2(n + 1)) { prinv("You find old loot:", otmp, 0L); return TRUE; @@ -2437,21 +2641,24 @@ reverse_loot(void) if (!goldob) return FALSE; + /* gold might be quivered; dropping would un-wear it, but freeinv() + expects caller to do that; do so now */ + remove_worn_item(goldob, FALSE); + if (!IS_THRONE(levl[x][y].typ)) { dropx(goldob); /* the dropped gold might have fallen to lower level */ if (g_at(x, y)) pline("Ok, now there is loot here."); } else { - /* find original coffers chest if present, otherwise use nearest one */ + /* find original coffers chest if present, otherwise use nearest */ otmp = 0; for (coffers = fobj; coffers; coffers = coffers->nobj) if (coffers->otyp == CHEST) { if (coffers->spe == 2) break; /* a throne room chest */ - if (!otmp - || (distu(coffers->ox, coffers->oy) - < distu(otmp->ox, otmp->oy))) + if (!otmp || (distu(coffers->ox, coffers->oy) + < distu(otmp->ox, otmp->oy))) otmp = coffers; /* remember closest ordinary chest */ } if (!coffers) @@ -2602,7 +2809,7 @@ exchange_objects_with_mon(struct monst *mtmp, boolean taking) m_delay += 2; } if ((unwornmask & W_SADDLE) != 0L) { - if (Verbose(2, loot_mon)) + if (flags.verbose) You("take %s off of %s.", thesimpleoname(otmp), mon_nam(mtmp)); /* unstrapping a saddle takes additional time */ @@ -2716,7 +2923,7 @@ loot_mon(struct monst *mtmp, int *passed_info, boolean *mon_interact) * Decide whether an object being placed into a magic bag will cause * it to explode. If the object is a bag itself, check recursively. */ -static boolean +staticfn boolean mbag_explodes(struct obj *obj, int depthin) { /* these won't cause an explosion when they're empty */ @@ -2738,7 +2945,7 @@ mbag_explodes(struct obj *obj, int depthin) return FALSE; } -static boolean +staticfn boolean is_boh_item_gone(void) { return (boolean) (!rn2(13)); @@ -2746,7 +2953,7 @@ is_boh_item_gone(void) /* Scatter most of Bag of holding contents around. Some items will be destroyed with the same chance as looting a cursed bag. */ -static void +staticfn void do_boh_explosion(struct obj *boh, boolean on_floor) { struct obj *otmp, *nobj; @@ -2765,7 +2972,7 @@ do_boh_explosion(struct obj *boh, boolean on_floor) /* boh is about to be deleted so no need to reset its in_use flag here */ } -static long +staticfn long boh_loss(struct obj *container, boolean held) { /* sometimes toss objects if a cursed magic bag */ @@ -2786,7 +2993,7 @@ boh_loss(struct obj *container, boolean held) } /* Returns: -1 to stop, 1 item was inserted, 0 item was not inserted. */ -static int +staticfn int in_container(struct obj *obj) { boolean floor_container = !carried(gc.current_container); @@ -2866,16 +3073,18 @@ in_container(struct obj *obj) if (obj->oclass != COIN_CLASS) { /* sellobj() will take an unpaid item off the shop bill */ was_unpaid = obj->unpaid ? TRUE : FALSE; - /* don't sell when putting the item into your own container, - * but handle billing correctly */ - sellobj_state(gc.current_container->no_charge - ? SELL_DONTSELL : SELL_DELIBERATE); + if (gs.sellobj_first) { + /* don't sell when putting the item into your own container, + but handle billing correctly */ + sellobj_state(gc.current_container->no_charge + ? SELL_DONTSELL : SELL_DELIBERATE); + gs.sellobj_first = FALSE; + } sellobj(obj, u.ux, u.uy); - sellobj_state(SELL_NORMAL); } } if (Icebox && !age_is_relative(obj)) { - obj->age = gm.moves - obj->age; /* actual age */ + obj->age = svm.moves - obj->age; /* actual age */ /* stop any corpse timeouts when frozen */ if (obj->otyp == CORPSE) { if (obj->timed) { @@ -2907,9 +3116,9 @@ in_container(struct obj *obj) obfree(obj, (struct obj *) 0); /* if carried, shop goods will be flagged 'unpaid' and obfree() will handle bill issues, but if on floor, we need to put them on bill - before deleting them (non-shop items will be flagged 'no_charge') */ - if (floor_container - && costly_spot(gc.current_container->ox, gc.current_container->oy)) { + before deleting them (non-shop items will be flagged 'no_charge')*/ + if (floor_container && costly_spot(gc.current_container->ox, + gc.current_container->oy)) { struct obj save_no_charge; save_no_charge.no_charge = gc.current_container->no_charge; @@ -2964,13 +3173,13 @@ ck_bag(struct obj *obj) } /* Returns: -1 to stop, 1 item was removed, 0 item was not removed. */ -static int +staticfn int out_container(struct obj *obj) { - register struct obj *otmp; - boolean is_gold = (obj->oclass == COIN_CLASS); - int res, loadlev; + struct obj *otmp; + int res; long count; + boolean is_gold = (obj->oclass == COIN_CLASS); if (!gc.current_container) { impossible(" no gc.current_container?"); @@ -3030,12 +3239,7 @@ out_container(struct obj *obj) } otmp = addinv(obj); - loadlev = near_capacity(); - prinv(loadlev ? ((loadlev < MOD_ENCUMBER) - ? "You have a little trouble removing" - : "You have much trouble removing") - : (char *) 0, - otmp, count); + pickup_prinv(otmp, count, "removing"); if (is_gold) { bot(); /* update character's gold piece count immediately */ @@ -3048,7 +3252,7 @@ void removed_from_icebox(struct obj *obj) { if (!age_is_relative(obj)) { - obj->age = gm.moves - obj->age; /* actual age */ + obj->age = svm.moves - obj->age; /* actual age */ if (obj->otyp == CORPSE) { struct monst *m = get_mtraits(obj, FALSE); boolean iceT = m ? (m->data == &mons[PM_ICE_TROLL]) @@ -3066,7 +3270,7 @@ removed_from_icebox(struct obj *obj) } /* an object inside a cursed bag of holding is being destroyed */ -static long +staticfn long mbag_item_gone(boolean held, struct obj *item, boolean silent) { struct monst *shkp; @@ -3137,7 +3341,7 @@ observe_quantum_cat(struct obj *box, boolean makecat, boolean givemsg) /* set_corpsenm() will start the rot timer that was removed when makemon() created SchroedingersBox; start it from now rather than from when this special corpse got created */ - deadcat->age = gm.moves; + deadcat->age = svm.moves; set_corpsenm(deadcat, PM_HOUSECAT); deadcat = oname(deadcat, sc, ONAME_NO_FLAGS); } @@ -3160,7 +3364,7 @@ container_gone(int (*fn)(OBJ_P)) && !gc.current_container); } -static void +staticfn void explain_container_prompt(boolean more_containers) { static const char *const explaintext[] = { @@ -3207,7 +3411,7 @@ u_handsy(void) } /* getobj callback for object to be stashed into a container */ -static int +staticfn int stash_ok(struct obj *obj) { if (!obj) @@ -3238,6 +3442,7 @@ use_container( long loss; ga.abort_looting = FALSE; + gs.sellobj_first = TRUE; /* in_container() should call sellobj_state() */ emptymsg[0] = '\0'; if (!u_handsy()) @@ -3483,6 +3688,7 @@ use_container( update_inventory(); } + sellobj_state(SELL_NORMAL); /* in case in_container() set it */ *objp = gc.current_container; /* might have become null */ if (gc.current_container) gc.current_container = 0; /* avoid hanging on to stale pointer */ @@ -3493,10 +3699,11 @@ use_container( return used; } -static boolean +staticfn boolean transfer_container_available(void) { - struct obj *otmp, *objchns[2] = { gi.invent, gl.level.objects[u.ux][u.uy] }; + struct obj *otmp, *objchns[2] = { gi.invent, + svl.level.objects[u.ux][u.uy] }; struct trap *ttmp = t_at(u.ux, u.uy); int i; @@ -3528,7 +3735,7 @@ transfer_container_available(void) * return TRUE if one is successfully selected. * Returns FALSE if no valid container was selected and the caller should abort. */ -static boolean +staticfn boolean select_transfer_container(void) { char n; @@ -3537,7 +3744,7 @@ select_transfer_container(void) menu_item *pick_list; struct obj *otmp, *chosen = (struct obj *) 0; int i; - struct obj *objchns[2] = { gi.invent, gl.level.objects[u.ux][u.uy] }; + struct obj *objchns[2] = { gi.invent, svl.level.objects[u.ux][u.uy] }; struct trap *ttmp = t_at(u.ux, u.uy); boolean validcont = FALSE, known_locked = FALSE, unreachable = FALSE, cant_reach = !can_reach_floor(ttmp && is_pit(ttmp->ttyp)), @@ -3645,7 +3852,7 @@ select_transfer_container(void) } /* loot current_container (take things out or put things in), by prompting */ -static int +staticfn int traditional_loot(boolean put_in) { int (*actionfunc)(OBJ_P), (*checkfunc)(OBJ_P); @@ -3665,6 +3872,7 @@ traditional_loot(boolean put_in) objlist = &(gc.current_container->cobj); actionfunc = out_container; checkfunc = (int (*)(OBJ_P)) 0; + gp.pickup_encumbrance = 0; /* used to limit verbosity */ } if (query_classes(selection, &one_by_one, &allflag, action, *objlist, @@ -3679,7 +3887,7 @@ traditional_loot(boolean put_in) } /* loot current_container (take things out or put things in), using a menu */ -static int +staticfn int menu_loot(int retry, boolean put_in) { int n, i, n_looted = 0; @@ -3694,6 +3902,8 @@ menu_loot(int retry, boolean put_in) int mflags, res; long count = 0; + gp.pickup_encumbrance = 0; /* used by out_container(); no harm in + * zeroing it if about to use in_container() */ if (retry) { all_categories = (retry == -2); } else if (flags.menu_style == MENU_FULL) { @@ -3701,10 +3911,16 @@ menu_loot(int retry, boolean put_in) Sprintf(buf, "%s what type of objects?", action); mflags = (ALL_TYPES | UNPAID_TYPES | BUCX_TYPES | CHOOSE_ALL | JUSTPICKED ); - n = query_category(buf, put_in ? gi.invent : gc.current_container->cobj, + n = query_category(buf, + put_in ? gi.invent : gc.current_container->cobj, mflags, &pick_list, PICK_ANY); + /* when paranoid_confirm:A is set, 'A' by itself implies + 'A'+'a' which will be followed by a confirmation prompt; + when that option isn't set, 'A' by itself is rejected + by query_categorry() and result here will be n==0 */ if (!n) - return ECMD_OK; + return ECMD_OK; /* no non-autopick category filters specified */ + for (i = 0; i < n; i++) { if (pick_list[i].item.a_int == 'A') { loot_everything = autopick = TRUE; @@ -3751,7 +3967,8 @@ menu_loot(int retry, boolean put_in) n_looted += res; } } - } else if (put_in && loot_justpicked && count_justpicked(gi.invent) == 1) { + } else if (put_in && loot_justpicked + && count_justpicked(gi.invent) == 1) { otmp = find_justpicked(gi.invent); if (otmp) { n_looted = 1; @@ -3780,6 +3997,7 @@ menu_loot(int retry, boolean put_in) n_looted = n; for (i = 0; i < n; i++) { otmp = pick_list[i].item.a_obj; + assert(otmp != 0); count = pick_list[i].count; if (count > 0 && count < otmp->quan) { otmp = splitobj(otmp, count); @@ -3805,7 +4023,7 @@ menu_loot(int retry, boolean put_in) return n_looted ? ECMD_TIME : ECMD_OK; } -static char +staticfn char in_or_out_menu( const char *prompt, struct obj *obj, @@ -3822,7 +4040,7 @@ in_or_out_menu( char buf[BUFSZ]; int n; const char *menuselector = flags.lootabc ? abc_chars : lootchars; - int clr = 0; + int clr = NO_COLOR; any = cg.zeroany; win = create_nhwindow(NHW_MENU); @@ -3867,13 +4085,12 @@ in_or_out_menu( add_menu(win, &nul_glyphinfo, &any, menuselector[any.a_int], 0, ATR_NONE, clr, buf, MENU_ITEMFLAGS_NONE); } - any.a_int = 0; - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); + add_menu_str(win, ""); if (more_containers) { any.a_int = 8; /* 'n' */ add_menu(win, &nul_glyphinfo, &any, menuselector[any.a_int], 0, - ATR_NONE, clr, "loot next container", MENU_ITEMFLAGS_SELECTED); + ATR_NONE, clr, "loot next container", + MENU_ITEMFLAGS_SELECTED); } any.a_int = 9; /* 'q' */ Strcpy(buf, alreadyused ? "done" : "do nothing"); @@ -3896,7 +4113,7 @@ in_or_out_menu( } /* getobj callback for object to tip */ -static int +staticfn int tip_ok(struct obj *obj) { if (!obj || obj->oclass == COIN_CLASS) @@ -3915,6 +4132,67 @@ tip_ok(struct obj *obj) return GETOBJ_DOWNPLAY; } +/* show a menu of containers under hero, + and one extra entry for choosing an inventory. + returns ECMD_CANCEL if menu was canceled, + ECMD_TIME if a container was picked, + otherwise returns ECMD_OK. */ +staticfn int +choose_tip_container_menu(void) +{ + int n, i; + winid win; + anything any; + menu_item *pick_list = (menu_item *) 0; + struct obj dummyobj, *otmp; + int clr = NO_COLOR; + + any = cg.zeroany; + win = create_nhwindow(NHW_MENU); + start_menu(win, MENU_BEHAVE_STANDARD); + + for (otmp = svl.level.objects[u.ux][u.uy], i = 0; otmp; + otmp = otmp->nexthere) + if (Is_container(otmp)) { + ++i; + any.a_obj = otmp; + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, + clr, doname(otmp), MENU_ITEMFLAGS_NONE); + } + if (gi.invent) { + add_menu_str(win, ""); + any.a_obj = &dummyobj; + /* use 'i' for inventory unless there are so many + containers that it's already being used */ + i = (i <= 'i' - 'a' && !flags.lootabc) ? 'i' : 0; + add_menu(win, &nul_glyphinfo, &any, i, 0, ATR_NONE, + clr, "tip something being carried", + MENU_ITEMFLAGS_SELECTED); + } + end_menu(win, "Tip which container?"); + n = select_menu(win, PICK_ONE, &pick_list); + destroy_nhwindow(win); + /* + * Deal with quirk of preselected item in pick-one menu: + * n == 0 => picked preselected entry, toggling it off; + * n == 1 => accepted preselected choice via SPACE or RETURN; + * n == 2 => picked something other than preselected entry; + * n == -1 => cancelled via ESC; + */ + otmp = (n <= 0) ? (struct obj *) 0 : pick_list[0].item.a_obj; + if (n > 1 && otmp == &dummyobj) + otmp = pick_list[1].item.a_obj; + if (pick_list) + free((genericptr_t) pick_list); + if (otmp && otmp != &dummyobj) { + tipcontainer(otmp); + return ECMD_TIME; + } + if (n == -1) + return ECMD_CANCEL; + return ECMD_OK; +} + /* #tip command -- empty container contents onto floor */ int dotip(void) @@ -3948,66 +4226,17 @@ dotip(void) && (!iflags.menu_requested || (flags.menu_style == MENU_TRADITIONAL && boxes > 1))) { Sprintf(buf, "You can't tip %s while carrying so much.", - !Verbose(2, dotip) - ? "a container" : (boxes > 1) ? "one" : "it"); + !flags.verbose ? "a container" : (boxes > 1) ? "one" : "it"); if (!check_capacity(buf) && able_to_loot(cc.x, cc.y, FALSE)) { if (boxes > 1) { - /* use menu to pick a container to tip */ - int n, i; - winid win; - anything any; - menu_item *pick_list = (menu_item *) 0; - struct obj dummyobj, *otmp; - int clr = 0; - - any = cg.zeroany; - win = create_nhwindow(NHW_MENU); - start_menu(win, MENU_BEHAVE_STANDARD); - - for (cobj = gl.level.objects[cc.x][cc.y], i = 0; cobj; - cobj = cobj->nexthere) - if (Is_container(cobj)) { - ++i; - any.a_obj = cobj; - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, doname(cobj), MENU_ITEMFLAGS_NONE); - } - if (gi.invent) { - any = cg.zeroany; - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "", MENU_ITEMFLAGS_NONE); - any.a_obj = &dummyobj; - /* use 'i' for inventory unless there are so many - containers that it's already being used */ - i = (i <= 'i' - 'a' && !flags.lootabc) ? 'i' : 0; - add_menu(win, &nul_glyphinfo, &any, i, 0, ATR_NONE, - clr, "tip something being carried", - MENU_ITEMFLAGS_SELECTED); - } - end_menu(win, "Tip which container?"); - n = select_menu(win, PICK_ONE, &pick_list); - destroy_nhwindow(win); - /* - * Deal with quirk of preselected item in pick-one menu: - * n == 0 => picked preselected entry, toggling it off; - * n == 1 => accepted preselected choice via SPACE or RETURN; - * n == 2 => picked something other than preselected entry; - * n == -1 => cancelled via ESC; - */ - otmp = (n <= 0) ? (struct obj *) 0 : pick_list[0].item.a_obj; - if (n > 1 && otmp == &dummyobj) - otmp = pick_list[1].item.a_obj; - if (pick_list) - free((genericptr_t) pick_list); - if (otmp && otmp != &dummyobj) { - tipcontainer(otmp); - return ECMD_TIME; - } - if (n == -1) - return ECMD_OK; + int res; + /* pick one container via menu or ... */ + if ((res = choose_tip_container_menu()) != ECMD_OK) + return res; /* else pick-from-gi.invent below */ } else { - for (cobj = gl.level.objects[cc.x][cc.y]; cobj; cobj = nobj) { + for (cobj = svl.level.objects[cc.x][cc.y]; cobj; + cobj = nobj) { nobj = cobj->nexthere; if (!Is_container(cobj)) continue; @@ -4128,18 +4357,19 @@ tipcontainer(struct obj *box) /* or bag */ if (tipcontainer_checks(box, targetbox, FALSE) != TIPCHECK_OK) return; - if (targetbox && tipcontainer_checks(targetbox, NULL, TRUE) != TIPCHECK_OK) + if (targetbox + && tipcontainer_checks(targetbox, NULL, TRUE) != TIPCHECK_OK) return; dump_container(box, targetbox, DUMPCONT_NORMAL); } #if 0 -static int count_target_containers(struct obj *, struct obj *); +staticfn int count_target_containers(struct obj *, struct obj *); /* returns number of containers in object chain; does not recurse into containers; skips bags of tricks when they're known */ -static int +staticfn int count_target_containers( struct obj *olist, /* list of objects (invent) */ struct obj *excludo) /* particular object to exclude if found in list */ @@ -4161,7 +4391,7 @@ count_target_containers( /* ask user for a carried container into which they want box to be emptied; cancelled is TRUE if user cancelled the menu pick; hands aren't required when tipping to the floor but are when tipping into another container */ -static struct obj * +staticfn struct obj * tipcontainer_gettarget( struct obj *box, boolean *cancelled) @@ -4173,7 +4403,7 @@ tipcontainer_gettarget( menu_item *pick_list = (menu_item *) 0; struct obj dummyobj, *otmp; boolean hands_available = TRUE, exclude_it; - int clr = 0; + int clr = NO_COLOR; #if 0 /* [skip potential early return so that menu response is needed * regardless of whether other containers are being carried] */ @@ -4197,9 +4427,7 @@ tipcontainer_gettarget( add_menu(win, &nul_glyphinfo, &any, '-', 0, ATR_NONE, clr, /* [TODO? vary destination string depending on surface()] */ "on the floor", MENU_ITEMFLAGS_SELECTED); - any = cg.zeroany; - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, - "", MENU_ITEMFLAGS_NONE); + add_menu_str(win, ""); n_conts = 0; for (otmp = gi.invent; otmp; otmp = otmp->nobj) { @@ -4246,7 +4474,7 @@ tipcontainer_gettarget( /* Perform check on box if we can tip it. Returns one of TIPCHECK_foo values. If allowempty if TRUE, return TIPCHECK_OK instead of TIPCHECK_EMPTY. */ -static int +staticfn int tipcontainer_checks( struct obj *box, /* container player wants to tip */ struct obj *targetbox, /* destination (used here for horn of plenty) */ @@ -4322,7 +4550,7 @@ tipcontainer_checks( if (box->spe < old_spe) { if (bag && !totseen) - pline("Nothing seems to happen."); + pline1(nothing_seems_to_happen); /* check_unpaid wants to see a non-zero charge count */ box->spe = old_spe; check_unpaid_usage(box, TRUE); @@ -4478,6 +4706,7 @@ dump_container(struct obj* box, struct obj *targetbox, int msgflags) (void) add_to_container(targetbox, otmp); } } else if (highdrop) { + otmp->how_lost = LOST_DROPPED; /* might break or fall down stairs; handles altars itself */ hitfloor(otmp, TRUE); } else if (distant) { @@ -4496,6 +4725,7 @@ dump_container(struct obj* box, struct obj *targetbox, int msgflags) pline("%s%c", doname(otmp), nobj ? ',' : '.'); iflags.last_msg = PLNMSG_OBJNAM_ONLY; } + otmp->how_lost = LOST_DROPPED; dropy(otmp); if (iflags.last_msg != PLNMSG_OBJNAM_ONLY) terse = FALSE; /* terse formatting has been interrupted */ @@ -4519,7 +4749,7 @@ dump_container(struct obj* box, struct obj *targetbox, int msgflags) * triggering some response. * This is currently used only for taking an item from the dead in the Styx * marsh, but could be expanded to several other things. */ -static void +staticfn void u_took_forbidden_object(struct obj *offender) { coordxy x, y; @@ -4534,14 +4764,14 @@ u_took_forbidden_object(struct obj *offender) /* don't call this multiple times in a turn i.e. by picking up multiple * items */ - if (last_time_called == gm.moves) + if (last_time_called == svm.moves) return; - last_time_called = gm.moves; + last_time_called = svm.moves; for (x = 1; x < COLNO; x++) { for (y = 1; y < ROWNO; y++) { - for (otmp = gl.level.objects[x][y]; otmp; otmp = nobj) { + for (otmp = svl.level.objects[x][y]; otmp; otmp = nobj) { nobj = otmp->nobj; if (otmp->otyp == CORPSE) { switch (rn2(5)) { diff --git a/src/pline.c b/src/pline.c index 0e51721c2e..3a89c0d68d 100644 --- a/src/pline.c +++ b/src/pline.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 pline.c $NHDT-Date: 1646255375 2022/03/02 21:09:35 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.109 $ */ +/* NetHack 3.7 pline.c $NHDT-Date: 1719819280 2024/07/01 07:34:40 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.130 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -9,17 +9,13 @@ * config file parsing) with modest decoration; * result will then be truncated to BUFSZ-1 */ -static void putmesg(const char *); -static char *You_buf(int); -#if defined(MSGHANDLER) -static void execplinehandler(const char *); -#endif - +staticfn void putmesg(const char *); +staticfn char *You_buf(int); +staticfn void execplinehandler(const char *); #ifdef USER_SOUNDS extern void maybe_play_sound(const char *); #endif - -#if defined(DUMPLOG) || defined(DUMPHTML) +#ifdef DUMPLOG_CORE /* keep the most recent DUMPLOG_MSG_COUNT messages */ void @@ -65,7 +61,7 @@ dumplogfreemessages(void) #endif /* keeps windowprocs usage out of pline() */ -static void +staticfn void putmesg(const char *line) { int attr = ATR_NONE; @@ -80,7 +76,24 @@ putmesg(const char *line) SoundSpeak(line); } -static void vpline(const char *, va_list); +/* 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 @@ -94,7 +107,43 @@ pline(const char *line, ...) va_end(the_args); } -static void +void +pline_dir(int dir, const char *line, ...) +{ + va_list the_args; + + set_msg_dir(dir); + + va_start(the_args, line); + vpline(line, the_args); + va_end(the_args); +} + +void +pline_xy(coordxy x, coordxy y, const char *line, ...) +{ + va_list the_args; + + set_msg_xy(x, y); + + va_start(the_args, line); + vpline(line, the_args); + va_end(the_args); +} + +void +pline_mon(struct monst *mtmp, const char *line, ...) +{ + va_list the_args; + + set_msg_xy(mtmp->mx, mtmp->my); + + va_start(the_args, line); + vpline(line, the_args); + va_end(the_args); +} + +staticfn void vpline(const char *line, va_list the_args) { static int in_pline = 0; @@ -102,15 +151,37 @@ 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; #ifdef HANGUPHANDLING - if (gp.program_state.done_hup) + if (program_state.done_hup) return; #endif - if (gp.program_state.wizkit_wishing) + if (program_state.wizkit_wishing) + return; + + /* 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_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); + vpline(tmp, the_args); + free((genericptr_t) tmp); return; + } if (!strchr(line, '%')) { /* format does not specify any substitutions; use it as-is */ @@ -148,7 +219,7 @@ vpline(const char *line, va_list the_args) } msgtyp = MSGTYP_NORMAL; -#if defined(DUMPLOG) || defined(DUMPHTML) +#ifdef DUMPLOG_CORE /* We hook here early to have options-agnostic output. * Unfortunately, that means Norep() isn't honored (general issue) and * that short lines aren't combined into one longer one (tty behavior). @@ -189,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; @@ -265,7 +339,7 @@ Norep(const char *line, ...) va_end(the_args); } -static char * +staticfn char * You_buf(int siz) { if (siz > gy.you_buf_siz) { @@ -450,7 +524,7 @@ livelog_printf(long ll_type, const char *line, ...) (void) vsnprintf(gamelogbuf, sizeof gamelogbuf, line, the_args); va_end(the_args); - gamelog_add(ll_type, gm.moves, gamelogbuf); + gamelog_add(ll_type, svm.moves, gamelogbuf); strNsubst(gamelogbuf, "\t", "_", 0); livelog_add(ll_type, gamelogbuf); } @@ -473,7 +547,7 @@ livelog_printf( #endif /* !CHRONICLE */ -static void vraw_printf(const char *, va_list); +staticfn void vraw_printf(const char *, va_list); void raw_printf(const char *line, ...) @@ -483,13 +557,13 @@ raw_printf(const char *line, ...) va_start(the_args, line); vraw_printf(line, the_args); va_end(the_args); - if (!gp.program_state.beyond_savefile_load) + if (!program_state.beyond_savefile_load) ge.early_raw_messages++; } DISABLE_WARNING_FORMAT_NONLITERAL -static void +staticfn void vraw_printf(const char *line, va_list the_args) { char pbuf[BIGBUFSZ]; /* will be chopped down to BUFSZ-1 if longer */ @@ -505,10 +579,8 @@ 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 (!gp.program_state.beyond_savefile_load) + if (!program_state.beyond_savefile_load) ge.early_raw_messages++; } @@ -517,68 +589,80 @@ impossible(const char *s, ...) { va_list the_args; char pbuf[BIGBUFSZ]; /* will be chopped down to BUFSZ-1 if longer */ + char pbuf2[BUFSZ]; va_start(the_args, s); - if (gp.program_state.in_impossible) + if (program_state.in_impossible) panic("impossible called impossible"); - gp.program_state.in_impossible = 1; + program_state.in_impossible = 1; (void) vsnprintf(pbuf, sizeof pbuf, s, the_args); va_end(the_args); pbuf[BUFSZ - 1] = '\0'; /* sanity */ paniclog("impossible", pbuf); - if (iflags.debug_fuzzer) + if (iflags.debug_fuzzer == fuzzer_impossible_panic) panic("%s", pbuf); gp.pline_flags = URGENT_MESSAGE; pline("%s", pbuf); gp.pline_flags = 0; - /* reuse pbuf[] */ - Strcpy(pbuf, "Program in disorder!"); - if (gp.program_state.something_worth_saving) - Strcat(pbuf, " (Saving and reloading may fix this problem.)"); - pline("%s", pbuf); + + if (program_state.in_sanity_check) { + /* skip rest of multi-line feedback */ + program_state.in_impossible = 0; + return; + } + + Strcpy(pbuf2, "Program in disorder!"); + if (program_state.something_worth_saving) + Strcat(pbuf2, " (Saving and reloading may fix this problem.)"); + pline("%s", pbuf2); pline("Please report these messages to %s.", DEVTEAM_EMAIL); if (sysopt.support) { pline("Alternatively, contact local support: %s", sysopt.support); } - gp.program_state.in_impossible = 0; +#ifdef CRASHREPORT + if (sysopt.crashreporturl) { + boolean report = ('y' == yn_function("Report now?", ynchars, + 'n', FALSE)); + + raw_print(""); /* prove to the user the character was accepted */ + if (report) { + submit_web_report(1, "Impossible", pbuf); + } + } +#endif + + program_state.in_impossible = 0; } RESTORE_WARNING_FORMAT_NONLITERAL -#if defined(MSGHANDLER) static boolean use_pline_handler = TRUE; -static void +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; @@ -592,21 +676,23 @@ 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 sysytem. + use_pline_handler = FALSE; + nhUse(args); + nhUse(line); #endif } -#endif /* MSGHANDLER */ /* * varargs handling for files.c */ -static void vconfig_error_add(const char *, va_list); +staticfn void vconfig_error_add(const char *, va_list); DISABLE_WARNING_FORMAT_NONLITERAL @@ -620,7 +706,7 @@ config_error_add(const char *str, ...) va_end(the_args); } -static void +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; diff --git a/src/polyself.c b/src/polyself.c index de33b9e0c4..57205a2fb1 100644 --- a/src/polyself.c +++ b/src/polyself.c @@ -1,13 +1,13 @@ -/* NetHack 3.7 polyself.c $NHDT-Date: 1681429658 2023/04/13 23:47:38 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.197 $ */ +/* NetHack 3.7 polyself.c $NHDT-Date: 1703845752 2023/12/29 10:29:12 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.207 $ */ /* 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 @@ -21,13 +21,13 @@ #include "hack.h" -static void check_strangling(boolean, boolean); -static void polyman(const char *, const char *); -static void dropp(struct obj *); -static void break_armor(boolean); -static void drop_weapon(int, boolean); -static void newman(void); -static void polysense(void); +staticfn void check_strangling(boolean, boolean); +staticfn void polyman(const char *, const char *); +staticfn void dropp(struct obj *); +staticfn void break_armor(boolean); +staticfn void drop_weapon(int, boolean); +staticfn void newman(void); +staticfn void polysense(void); static const char no_longer_petrify_resistant[] = "No longer petrify-resistant, you"; @@ -40,6 +40,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; @@ -102,9 +103,16 @@ 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 - float_vs_flight(); /* maybe toggle (BFlying & I_SPECIAL) */ + /* whether the player is flying/floating depends on their steed, + which won't be known during the restore process: but BFlying + and BStealth should be set correctly already in that case, so + there's nothing to do */ + if (!program_state.restoring) + float_vs_flight(); /* maybe toggle (BFlying & I_SPECIAL) */ polysense(); #ifdef STATUS_HILITES @@ -132,11 +140,26 @@ float_vs_flight(void) BLevitation |= I_SPECIAL; else BLevitation &= ~I_SPECIAL; - gc.context.botl = TRUE; + + /* riding blocks stealth unless hero+steed fly, so a change in flying + might cause a change in stealth */ + steed_vs_stealth(); + + disp.botl = TRUE; +} + +/* riding blocks stealth unless hero+steed fly */ +void +steed_vs_stealth(void) +{ + if (u.usteed && !Flying && !Levitation) + BStealth |= FROMOUTSIDE; + else + BStealth &= ~FROMOUTSIDE; } /* for changing into form that's immune to strangulation */ -static void +staticfn void check_strangling(boolean on, boolean noisy) { /* on -- maybe resume strangling */ @@ -148,7 +171,7 @@ check_strangling(boolean on, boolean noisy) if (uamul && uamul->otyp == AMULET_OF_STRANGULATION && can_be_strangled(&gy.youmonst)) { Strangled = 6L; - gc.context.botl = TRUE; + disp.botl = TRUE; if (noisy) Your("%s %s your %s!", simpleonames(uamul), was_strangled ? "still constricts" : "begins constricting", @@ -160,7 +183,7 @@ check_strangling(boolean on, boolean noisy) } else { if (Strangled && !can_be_strangled(&gy.youmonst)) { Strangled = 0L; - gc.context.botl = TRUE; + disp.botl = TRUE; if (noisy) You("are no longer being strangled."); } @@ -170,7 +193,7 @@ check_strangling(boolean on, boolean noisy) DISABLE_WARNING_FORMAT_NONLITERAL /* make a (new) human out of the player */ -static void +staticfn void polyman(const char *fmt, const char *arg) { boolean sticking = (sticks(gy.youmonst.data) && u.ustuck && !u.uswallow), @@ -209,11 +232,11 @@ polyman(const char *fmt, const char *arg) struct kinfo *kptr = find_delayed_killer(POLYMORPH); if (kptr != (struct kinfo *) 0 && kptr->name[0]) { - gk.killer.format = kptr->format; - Strcpy(gk.killer.name, kptr->name); + svk.killer.format = kptr->format; + Strcpy(svk.killer.name, kptr->name); } else { - gk.killer.format = KILLED_BY; - Strcpy(gk.killer.name, "self-genocide"); + svk.killer.format = KILLED_BY; + Strcpy(svk.killer.name, "self-genocide"); } dealloc_killer(kptr); done(GENOCIDED); @@ -256,9 +279,9 @@ change_sex(void) u.mfemale = !u.mfemale; max_rank_sz(); /* [this appears to be superfluous] */ if ((Upolyd ? u.mfemale : flags.female) && gu.urole.name.f) - Strcpy(gp.pl_character, gu.urole.name.f); + Strcpy(svp.pl_character, gu.urole.name.f); else - Strcpy(gp.pl_character, gu.urole.name.m); + Strcpy(svp.pl_character, gu.urole.name.m); if (!Upolyd) { u.umonnum = u.umonster; } else if (u.umonnum == PM_AMOROUS_DEMON) { @@ -302,7 +325,7 @@ livelog_newform(boolean viapoly, int oldgend, int newgend) } } -static void +staticfn void newman(void) { const char *newform; @@ -364,7 +387,7 @@ newman(void) hpmax = u.ulevel; /* min of 1 HP per level */ /* retain same proportion for current HP; u.uhp * hpmax / u.uhpmax */ u.uhp = rounddiv((long) u.uhp * (long) hpmax, u.uhpmax); - u.uhpmax = hpmax; + setuhpmax(hpmax, TRUE); /* might reduce u.uhp */ /* * Do the same for spell power. */ @@ -394,8 +417,8 @@ newman(void) dead: /* we come directly here if experience level went to 0 or less */ urgent_pline( "Your new form doesn't seem healthy enough to survive."); - gk.killer.format = KILLED_BY_AN; - Strcpy(gk.killer.name, "unsuccessful polymorph"); + svk.killer.format = KILLED_BY_AN; + Strcpy(svk.killer.name, "unsuccessful polymorph"); done(DIED); /* must have been life-saved to get here */ newuhs(FALSE); @@ -426,7 +449,7 @@ newman(void) make_slimed(10L, (const char *) 0); } - gc.context.botl = 1; + disp.botl = TRUE; see_monsters(); (void) encumber_msg(); @@ -449,7 +472,7 @@ polyself(int psflags) * allow declining to become dragon, won't allow turning into * arbitrary monster */ draconian_only = ((psflags & POLY_DRAGON_ONLY) != 0), - iswere = (u.ulycn >= LOW_PM), + iswere = (ismnum(u.ulycn)), isvamp = (is_vampire(gy.youmonst.data) || is_vampshifter(&gy.youmonst)), controllable_poly = Polymorph_control && !(Stunned || Unaware); @@ -489,6 +512,7 @@ polyself(int psflags) if (controllable_poly || forcecontrol) { buf[0] = '\0'; tryct = 5; + do { mntmp = NON_PM; getlin("Become what kind of monster? [type the name]", buf); @@ -516,7 +540,28 @@ polyself(int psflags) mntmp = (draconian && class == S_DRAGON) ? armor_to_dragon(&gy.youmonst) : mkclass_poly(class); + + /* placeholder monsters are for corpses and all flagged + M2_NOPOLY but they are reasonable polymorph targets; + pick a suitable substitute (which might be geno'd) */ + } else if (is_placeholder(&mons[mntmp]) + /* when your own race, fall to !polyok() case */ + && !your_race(&mons[mntmp]) + /* same for generic human, even if hero isn't human */ + && mntmp != PM_HUMAN) { + /* far less general than mkclass() */ + if (mntmp == PM_ORC) + mntmp = rn2(3) ? PM_HILL_ORC : PM_MORDOR_ORC; + else if (mntmp == PM_ELF) + mntmp = rn2(3) ? PM_GREEN_ELF : PM_GREY_ELF; + else if (mntmp == PM_GIANT) + mntmp = rn2(3) ? PM_STONE_GIANT : PM_HILL_GIANT; + /* note: PM_DWARF and PM_GNOME are ordinary monsters and + no longer flagged no-poly so have no need for placeholder + handling; PM_HUMAN is a placeholder without a suitable + substitute so gets handled differently below */ } + if (mntmp < LOW_PM) { if (!class) pline("I've never heard of such monsters."); @@ -569,6 +614,7 @@ polyself(int psflags) } else break; } while (--tryct > 0); + if (!tryct) pline1(thats_enough_tries); /* allow skin merging, even when polymorph is controlled */ @@ -592,7 +638,7 @@ polyself(int psflags) return; } } - if (!(gm.mvitals[mntmp].mvflags & G_GENOD)) { + if (!(svm.mvitals[mntmp].mvflags & G_GENOD)) { struct obj **mergarm = (uarm && Is_dragon_scaled_armor(uarm)) ? &uarm : (uarmc && Is_dragon_scales(uarmc)) ? &uarmc @@ -633,7 +679,7 @@ polyself(int psflags) && !rn2(10)) ? PM_WOLF : !rn2(4) ? PM_FOG_CLOUD : PM_VAMPIRE_BAT; - if (gy.youmonst.cham >= LOW_PM + if (ismnum(gy.youmonst.cham) && !is_vampire(gy.youmonst.data) && !rn2(2)) mntmp = gy.youmonst.cham; } @@ -697,13 +743,14 @@ polymon(int mntmp, int msgflags) { char buf[BUFSZ], ustuckNam[BUFSZ]; boolean sticking = sticks(gy.youmonst.data) && u.ustuck && !u.uswallow, - was_blind = !!Blind, dochange = FALSE, was_expelled = FALSE; + was_blind = !!Blind, dochange = FALSE, was_expelled = FALSE, + was_hiding_under = u.uundetected && hides_under(gy.youmonst.data); int mlvl, newMaxStr; - if (gm.mvitals[mntmp].mvflags & G_GENOD) { /* allow G_EXTINCT */ + if (svm.mvitals[mntmp].mvflags & G_GENOD) { /* allow G_EXTINCT */ if (msgflags & POLYMON_TRANSFORM_MSG) You_feel("rather %s-ish.", - pmname(&mons[mntmp], Ugender)); + pmname(&mons[mntmp], flags.female ? FEMALE : MALE)); exercise(A_WIS, TRUE); return 0; } @@ -846,7 +893,10 @@ polymon(int mntmp, int msgflags) break_armor((msgflags & POLYMON_GEAR_MSG)); drop_weapon(1, (msgflags & POLYMON_GEAR_MSG)); find_ac(); /* (repeated below) */ - (void) hideunder(&gy.youmonst); + /* if hiding under something and can't hide anymore, unhide now; + but don't auto-hide when not already hiding-under */ + if (was_hiding_under) + (void) hideunder(&gy.youmonst); if (u.utrap && u.utraptype == TT_PIT) { set_utrap(rn1(6, 2), TT_PIT); /* time to escape resets */ @@ -977,7 +1027,7 @@ polymon(int mntmp, int msgflags) } check_strangling(TRUE, (msgflags & POLYMON_STATUS_MSG)); - gc.context.botl = 1; + disp.botl = TRUE; gv.vision_full_recalc = 1; see_monsters(); if (msgflags & POLYMON_ENCUMBER_MSG) @@ -993,7 +1043,7 @@ polymon(int mntmp, int msgflags) /* the explanation of '#monster' used to be shown sooner, but there are possible fatalities above and it isn't useful unless hero survives */ - if (Verbose(2, polymon) && (msgflags & POLYMON_INFO_MSG)) { + if (flags.verbose && (msgflags & POLYMON_INFO_MSG)) { static const char use_thec[] = "Use the command #%s to %s."; static const char monsterc[] = "monster"; struct permonst *uptr = gy.youmonst.data; @@ -1049,7 +1099,7 @@ uasmon_maxStr(void) struct permonst *ptr = &mons[mndx]; if (is_orc(ptr)) { - if (mndx != PM_URUK_HAI) + if (mndx != PM_URUK_HAI && mndx != PM_ORC_CAPTAIN) mndx = PM_ORC; } else if (is_elf(ptr)) { mndx = PM_ELF; @@ -1073,11 +1123,11 @@ uasmon_maxStr(void) hero poly'd into an orc the same; goblins, orc shamans, and orc zombies don't have strongmonst() attribute so won't get here; hobgoblins and orc mummies do get here and are limited to 18/50 - like normal orcs; however, Uruk-hai retain 18/100 strength; - hero gnomes are also limited to 18/50; hero elves are limited - to 18/00 regardless of whether they're strongmonst, but the two - strongmonst types (monarchs and nobles) have current strength - set to 18 [by polymon()], the others don't */ + like normal orcs; however, orc captains and Uruk-hai retain 18/100 + strength; hero gnomes are also limited to 18/50; hero elves are + limited to 18/00 regardless of whether they're strongmonst, but + the two strongmonst types (monarchs and nobles) have current + strength set to 18 [by polymon()], the others don't */ newMaxStr = R ? R->attrmax[A_STR] : live_H ? STR19(19) : STR18(100); } else { newMaxStr = R ? R->attrmax[A_STR] : 18; /* 18 is same as STR18(0) */ @@ -1086,7 +1136,7 @@ uasmon_maxStr(void) } /* dropx() jacket for break_armor() */ -static void +staticfn void dropp(struct obj *obj) { struct obj *otmp; @@ -1106,15 +1156,24 @@ 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; } } } -static void +staticfn void break_armor(boolean noisy) { - register struct obj *otmp; + struct obj *otmp; struct permonst *uptr = gy.youmonst.data; if (breakarm(uptr)) { @@ -1279,7 +1338,7 @@ break_armor(boolean noisy) /* rings stay worn even when no hands */ } -static void +staticfn void drop_weapon(int alone, boolean noisy) { struct obj *otmp; @@ -1354,10 +1413,10 @@ rehumanize(void) * xlogfile "while" field. */ /* in case we happen to get caught without killer set, avoid "killed by * a died" */ - if (gk.killer.name[0] == '\0') { + if (svk.killer.name[0] == '\0') { impossible("failed rehumanize with no killer set?"); - Strcpy(gk.killer.name, "killed weirdly"); - gk.killer.format = NO_KILLER_PREFIX; + Strcpy(svk.killer.name, "killed weirdly"); + svk.killer.format = NO_KILLER_PREFIX; } You("die..."); done(DIED); @@ -1382,14 +1441,14 @@ rehumanize(void) instead of u.mh while poly'd */ impossible("u.uhp was reduced while polyselfed?"); Your("old form was not healthy enough to survive."); - Sprintf(gk.killer.name, "reverting to unhealthy %s form", + Sprintf(svk.killer.name, "reverting to unhealthy %s form", gu.urace.adj); - gk.killer.format = KILLED_BY; + svk.killer.format = KILLED_BY; done(DIED); } nomul(0); - gc.context.botl = 1; + disp.botl = TRUE; gv.vision_full_recalc = 1; (void) encumber_msg(); if (was_flying && !Flying && u.usteed) @@ -1414,7 +1473,7 @@ dobreathe(void) return ECMD_OK; } u.uen -= 15; - gc.context.botl = 1; + disp.botl = TRUE; if (!getdir((char *) 0)) return ECMD_CANCEL; @@ -1448,6 +1507,7 @@ dospit(void) break; default: impossible("bad attack type in dospit"); + FALLTHROUGH; /*FALLTHRU*/ case AD_ACID: otmp = mksobj(ACID_VENOM, TRUE, FALSE); @@ -1614,7 +1674,7 @@ dosummon(void) return ECMD_OK; } u.uen -= 10; - gc.context.botl = 1; + disp.botl = TRUE; You("call upon your brethren for help!"); exercise(A_WIS, TRUE); @@ -1626,7 +1686,7 @@ dosummon(void) int dogaze(void) { - register struct monst *mtmp; + struct monst *mtmp; int looked = 0; char qbuf[QBUFSZ]; int i; @@ -1655,7 +1715,7 @@ dogaze(void) return ECMD_OK; } u.uen -= 15; - gc.context.botl = 1; + disp.botl = TRUE; for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) @@ -1767,8 +1827,8 @@ dogaze(void) l_monnam(mtmp)); /* as if gazing at a sleeping anything is fruitful... */ urgent_pline("You turn to stone..."); - gk.killer.format = KILLED_BY; - Strcpy(gk.killer.name, + svk.killer.format = KILLED_BY; + Strcpy(svk.killer.name, "deliberately meeting Medusa's gaze"); done(STONING); } @@ -1780,6 +1840,7 @@ dogaze(void) return ECMD_TIME; } +/* called by domonability() for #monster */ int dohide(void) { @@ -1796,15 +1857,14 @@ dohide(void) : !sticks(gy.youmonst.data) ? "being held" : (humanoid(u.ustuck->data) ? "holding someone" : "holding that creature")); - if (u.uundetected - || (ismimic && U_AP_TYPE != M_AP_NOTHING)) { + if (u.uundetected || (ismimic && U_AP_TYPE != M_AP_NOTHING)) { u.uundetected = 0; gy.youmonst.m_ap_type = M_AP_NOTHING; newsym(u.ux, u.uy); } return ECMD_OK; } - /* note: the eel and hides_under cases are hypothetical; + /* note: hero-as-eel handling is incomplete but unnecessary; such critters aren't offered the option of hiding via #monster */ if (gy.youmonst.data->mlet == S_EEL && !is_pool(u.ux, u.uy)) { if (IS_FOUNTAIN(levl[u.ux][u.uy].typ)) @@ -1816,7 +1876,7 @@ dohide(void) } if (hides_under(gy.youmonst.data)) { long ct = 0L; - struct obj *otmp, *otop = gl.level.objects[u.ux][u.uy]; + struct obj *otmp, *otop = svl.level.objects[u.ux][u.uy]; int concealflags = concealed_spot(u.ux, u.uy); if (concealflags == NOT_CONCEALABLE_SPOT) { @@ -1913,7 +1973,7 @@ domindblast(void) return ECMD_OK; } u.uen -= 10; - gc.context.botl = 1; + disp.botl = TRUE; You("concentrate."); pline("A wave of psychic energy pours out."); @@ -1998,72 +2058,67 @@ skinback(boolean noisy) const char * mbodypart(struct monst *mon, int part) { - if (part <= NO_PART) { - impossible("mbodypart: bad part %d", part); - return "strange body part"; - } - /* These arrays should otherwise be in the same order as the bodypart_types * defined in hack.h. */ static NEARDATA const char - *humanoid_parts[] = { "", "arm", "eye", "face", "finger", + *humanoid_parts[] = { "arm", "eye", "face", "finger", "fingertip", "foot", "hand", "handed", "head", "leg", "light headed", "neck", "spine", "toe", "hair", "blood", "lung", "nose", "stomach", "torso" }, - *jelly_parts[] = { "", "pseudopod", "dark spot", "front", + *jelly_parts[] = { "pseudopod", "dark spot", "front", "pseudopod extension", "pseudopod extremity", "pseudopod root", "grasp", "grasped", "cerebral area", "lower pseudopod", "viscous", "middle", "surface", "pseudopod extremity", "ripples", "juices", "surface", "sensor", "stomach", "central mass" }, - *animal_parts[] = { "", "forelimb", "eye", "face", - "foreclaw", "claw tip", "rear claw", - "foreclaw", "clawed", "head", - "rear limb", "light headed", "neck", - "spine", "rear claw tip", "fur", - "blood", "lung", "nose", - "stomach", "torso" }, - *bird_parts[] = { "", "wing", "eye", "face", "wing", + *animal_parts[] = { "forelimb", "eye", "face", + "foreclaw", "claw tip", "rear claw", + "foreclaw", "clawed", "head", + "rear limb", "light headed", "neck", + "spine", "rear claw tip", "fur", + "blood", "lung", "nose", + "stomach", "torso" }, + *bird_parts[] = { "wing", "eye", "face", "wing", "wing tip", "foot", "wing", "winged", "head", "leg", "light headed", "neck", "spine", "toe", "feathers", "blood", "lung", "bill", "stomach", "body" }, - *horse_parts[] = { "", "foreleg", "eye", "face", - "forehoof", "hoof tip", "rear hoof", - "forehoof", "hooved", "head", - "rear leg", "light headed", "neck", - "backbone", "rear hoof tip", "mane", - "blood", "lung", "nose", - "stomach", "torso" }, - *sphere_parts[] = { "", "appendage", "optic nerve", "body", "tentacle", + *horse_parts[] = { "foreleg", "eye", "face", + "forehoof", "hoof tip", "rear hoof", + "forehoof", "hooved", "head", + "rear leg", "light headed", "neck", + "backbone", "rear hoof tip", "mane", + "blood", "lung", "nose", + "stomach", "torso" }, + *sphere_parts[] = { "appendage", "optic nerve", "body", "tentacle", "tentacle tip", "lower appendage", "tentacle", "tentacled", "body", "lower tentacle", "rotational", "equator", "body", "lower tentacle tip", "cilia", "life force", "retina", "olfactory nerve", "interior", "body" }, - *fungus_parts[] = { "", "mycelium", "visual area", "front", - "hypha", "hypha", "root", - "strand", "stranded", "cap area", - "rhizome", "sporulated", "stalk", - "root", "rhizome tip", "spores", - "juices", "gill", "gill", - "interior", "body" }, - *vortex_parts[] = { "", "region", "eye", "front", + *fungus_parts[] = { "mycelium", "visual area", "front", + "hypha", "hypha", "root", + "strand", "stranded", "cap area", + "rhizome", "sporulated", "stalk", + "root", "rhizome tip", "spores", + "juices", "gill", "gill", + "interior", "body" }, + *vortex_parts[] = { "region", "eye", "front", "minor current", "minor current", "lower current", "swirl", "swirled", "central core", "lower current", "addled", "center", "currents", "edge", "currents", "life force", "center", "leading edge", "interior", "central core" }, - *snake_parts[] = { "", "vestigial limb", "eye", "face", "large scale", + *snake_parts[] = { "vestigial limb", "eye", "face", "large scale", "large scale tip", "rear region", "scale gap", "scale gapped", "head", "rear region", "light headed", "neck", "length", "rear scale", "scales", "blood", "lung", "forked tongue", "stomach", "midsection" }, - *worm_parts[] = { "", "anterior segment", "light sensitive cell", + *worm_parts[] = { "anterior segment", "light sensitive cell", "clitellum", "setae", "setae", "posterior segment", "segment", "segmented", "anterior segment", "posterior", "over stretched", "clitellum", @@ -2088,6 +2143,11 @@ mbodypart(struct monst *mon, int part) }; struct permonst *mptr = mon->data; + if (part <= NO_PART) { + impossible("mbodypart: bad part %d", part); + return "mystery part"; + } + /* some special cases */ if (mptr->mlet == S_DOG || mptr->mlet == S_FELINE || mptr->mlet == S_RODENT || mptr == &mons[PM_OWLBEAR]) { @@ -2212,7 +2272,7 @@ ugolemeffects(int damtype, int dam) u.mh += heal; if (u.mh > u.mhmax) u.mh = u.mhmax; - gc.context.botl = 1; + disp.botl = TRUE; pline("Strangely, you feel better than before."); exercise(A_STR, TRUE); } @@ -2246,14 +2306,14 @@ armor_to_dragon(struct monst *mon) } /* some species have awareness of other species */ -static void +staticfn void polysense(void) { short warnidx = NON_PM; - gc.context.warntype.speciesidx = NON_PM; - gc.context.warntype.species = 0; - gc.context.warntype.polyd = 0; + svc.context.warntype.speciesidx = NON_PM; + svc.context.warntype.species = 0; + svc.context.warntype.polyd = 0; HWarn_of_mon &= ~FROMRACE; switch (u.umonnum) { @@ -2263,13 +2323,13 @@ polysense(void) break; case PM_VAMPIRE: case PM_VAMPIRE_LEADER: - gc.context.warntype.polyd = M2_HUMAN | M2_ELF; + svc.context.warntype.polyd = M2_HUMAN | M2_ELF; HWarn_of_mon |= FROMRACE; return; } - if (warnidx >= LOW_PM) { - gc.context.warntype.speciesidx = warnidx; - gc.context.warntype.species = &mons[warnidx]; + if (ismnum(warnidx)) { + svc.context.warntype.speciesidx = warnidx; + svc.context.warntype.species = &mons[warnidx]; HWarn_of_mon |= FROMRACE; } } @@ -2278,8 +2338,8 @@ polysense(void) boolean ugenocided(void) { - return ((gm.mvitals[gu.urole.mnum].mvflags & G_GENOD) - || (gm.mvitals[gu.urace.mnum].mvflags & G_GENOD)); + return ((svm.mvitals[gu.urole.mnum].mvflags & G_GENOD) + || (svm.mvitals[gu.urace.mnum].mvflags & G_GENOD)); } /* how hero feels "inside" after self-genocide of role or race */ diff --git a/src/potion.c b/src/potion.c index fecdef964c..f65da4f398 100644 --- a/src/potion.c +++ b/src/potion.c @@ -1,47 +1,48 @@ -/* NetHack 3.7 potion.c $NHDT-Date: 1685135014 2023/05/26 21:03:34 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.238 $ */ +/* NetHack 3.7 potion.c $NHDT-Date: 1726356849 2024/09/14 23:34:09 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.270 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" -static long itimeout(long); -static long itimeout_incr(long, int); -static void ghost_from_bottle(void); -static int drink_ok(struct obj *); -static void peffect_restore_ability(struct obj *); -static void peffect_hallucination(struct obj *); -static void peffect_water(struct obj *); -static void peffect_booze(struct obj *); -static void peffect_enlightenment(struct obj *); -static void peffect_invisibility(struct obj *); -static void peffect_see_invisible(struct obj *); -static void peffect_paralysis(struct obj *); -static void peffect_sleeping(struct obj *); -static int peffect_monster_detection(struct obj *); -static int peffect_object_detection(struct obj *); -static void peffect_sickness(struct obj *); -static void peffect_confusion(struct obj *); -static void peffect_gain_ability(struct obj *); -static void peffect_speed(struct obj *); -static void peffect_blindness(struct obj *); -static void peffect_gain_level(struct obj *); -static void peffect_healing(struct obj *); -static void peffect_extra_healing(struct obj *); -static void peffect_full_healing(struct obj *); -static void peffect_levitation(struct obj *); -static void peffect_gain_energy(struct obj *); -static void peffect_oil(struct obj *); -static void peffect_acid(struct obj *); -static void peffect_polymorph(struct obj *); -static boolean H2Opotion_dip(struct obj *, struct obj *, boolean, +staticfn long itimeout(long); +staticfn long itimeout_incr(long, int); +staticfn void ghost_from_bottle(void); +staticfn int drink_ok(struct obj *); +staticfn void peffect_restore_ability(struct obj *); +staticfn void peffect_hallucination(struct obj *); +staticfn void peffect_water(struct obj *); +staticfn void peffect_booze(struct obj *); +staticfn void peffect_enlightenment(struct obj *); +staticfn void peffect_invisibility(struct obj *); +staticfn void peffect_see_invisible(struct obj *); +staticfn void peffect_paralysis(struct obj *); +staticfn void peffect_sleeping(struct obj *); +staticfn int peffect_monster_detection(struct obj *); +staticfn int peffect_object_detection(struct obj *); +staticfn void peffect_sickness(struct obj *); +staticfn void peffect_confusion(struct obj *); +staticfn void peffect_gain_ability(struct obj *); +staticfn void peffect_speed(struct obj *); +staticfn void peffect_blindness(struct obj *); +staticfn void peffect_gain_level(struct obj *); +staticfn void peffect_healing(struct obj *); +staticfn void peffect_extra_healing(struct obj *); +staticfn void peffect_full_healing(struct obj *); +staticfn void peffect_levitation(struct obj *); +staticfn void peffect_gain_energy(struct obj *); +staticfn void peffect_oil(struct obj *); +staticfn void peffect_acid(struct obj *); +staticfn void peffect_polymorph(struct obj *); +staticfn boolean H2Opotion_dip(struct obj *, struct obj *, boolean, const char *); -static short mixtype(struct obj *, struct obj *); -static int dip_ok(struct obj *); -static void hold_potion(struct obj *, const char *, const char *, +staticfn short mixtype(struct obj *, struct obj *); +staticfn int dip_ok(struct obj *); +staticfn int dip_hands_ok(struct obj *); +staticfn void hold_potion(struct obj *, const char *, const char *, const char *); -static void dipsink(struct obj *); -static int potion_dip(struct obj *obj, struct obj *potion); +staticfn void poof(struct obj *); +staticfn int potion_dip(struct obj *obj, struct obj *potion); /* used to indicate whether quaff or dip has skipped an opportunity to use a fountain or such, in order to vary the feedback if hero lacks @@ -50,7 +51,7 @@ static int potion_dip(struct obj *obj, struct obj *potion); static int drink_ok_extra = 0; /* force `val' to be within valid range for intrinsic timeout value */ -static long +staticfn long itimeout(long val) { if (val >= TIMEOUT) @@ -62,7 +63,7 @@ itimeout(long val) } /* increment `old' by `incr' and force result to be valid intrinsic timeout */ -static long +staticfn long itimeout_incr(long old, int incr) { return itimeout((old & TIMEOUT) + (long) incr); @@ -96,7 +97,7 @@ make_confused(long xtime, boolean talk) You_feel("less %s now.", Hallucination ? "trippy" : "confused"); } if ((xtime && !old) || (!xtime && old)) - gc.context.botl = TRUE; + disp.botl = TRUE; set_itimeout(&HConfusion, xtime); } @@ -123,7 +124,7 @@ make_stunned(long xtime, boolean talk) } } if ((!xtime && old) || (xtime && !old)) - gc.context.botl = TRUE; + disp.botl = TRUE; set_itimeout(&HStun, xtime); } @@ -176,7 +177,7 @@ make_sick(long xtime, set_itimeout(&Sick, fiend_adversity(PM_BAALZEBUB) ? (xtime + 1) / 2 : xtime); u.usick_type |= type; - gc.context.botl = TRUE; + disp.botl = TRUE; } else if (old && (type & u.usick_type)) { /* was sick, now not */ u.usick_type &= ~type; @@ -189,7 +190,7 @@ make_sick(long xtime, You_feel("cured. What a relief!"); Sick = 0L; /* set_itimeout(&Sick, 0L) */ } - gc.context.botl = TRUE; + disp.botl = TRUE; } kptr = find_delayed_killer(SICK); @@ -219,7 +220,7 @@ make_slimed(long xtime, const char *msg) #endif set_itimeout(&Slimed, xtime); if ((xtime != 0L) ^ (old != 0L)) { - gc.context.botl = TRUE; + disp.botl = TRUE; if (msg) pline("%s", msg); } @@ -246,7 +247,7 @@ make_stoned(long xtime, const char *msg, int killedby, const char *killername) #endif set_itimeout(&Stoned, xtime); if ((xtime != 0L) ^ (old != 0L)) { - gc.context.botl = TRUE; + disp.botl = TRUE; if (msg) pline("%s", msg); } @@ -265,7 +266,7 @@ make_vomiting(long xtime, boolean talk) talk = FALSE; set_itimeout(&Vomiting, xtime); - gc.context.botl = TRUE; + disp.botl = TRUE; if (!xtime && old) if (talk) You_feel("much less nauseated now."); @@ -355,7 +356,7 @@ toggle_blindness(void) boolean Stinging = (uwep && (EWarn_of_mon & W_WEP) != 0L); /* blindness has just been toggled */ - gc.context.botl = TRUE; /* status conditions need update */ + disp.botl = TRUE; /* status conditions need update */ gv.vision_full_recalc = 1; /* vision has changed */ /* this vision recalculation used to be deferred until moveloop(), but that made it possible for vision irregularities to occur @@ -448,7 +449,7 @@ make_hallucinated( (eg. Qt windowport's equipped items display) */ update_inventory(); - gc.context.botl = TRUE; + disp.botl = TRUE; if (talk) pline(message, verb); @@ -481,7 +482,7 @@ make_deaf(long xtime, boolean talk) set_itimeout(&HDeaf, xtime); can_hear_now = !Deaf; if ((xtime != 0L) ^ (old != 0L)) { - gc.context.botl = TRUE; + disp.botl = TRUE; if (talk) { if (old) { if (can_hear_now) @@ -542,7 +543,7 @@ make_paralyzed(int xtime, boolean talk, const char *explan) void make_glib(int xtime) { - gc.context.botl |= (!Glib ^ !!xtime); + disp.botl |= (!Glib ^ !!xtime); set_itimeout(&Glib, xtime); /* may change "(being worn)" to "(being worn; slippery)" or vice versa */ if (uarmg) @@ -561,7 +562,7 @@ make_withering(long xtime, boolean talk) set_itimeout(&HWithering, xtime); no_longer_withering = !Withering; if ((xtime != 0L) ^ (old != 0L)) { - gc.context.botl = TRUE; + disp.botl = TRUE; if (talk) { if (old) { if (no_longer_withering) { @@ -596,7 +597,7 @@ self_invis_message(void) : "can't see yourself"); } -static void +staticfn void ghost_from_bottle(void) { struct monst *mtmp = makemon(&mons[PM_GHOST], u.ux, u.uy, MM_NOMSG); @@ -616,7 +617,7 @@ ghost_from_bottle(void) /* getobj callback for object to drink from, which also does double duty as the callback for dipping into (both just allow potions). */ -static int +staticfn int drink_ok(struct obj *obj) { /* getobj()'s callback to test whether hands/self is a valid "item" to @@ -704,31 +705,41 @@ dodrink(void) return 0; } - /* quan > 1 used to be left to useup(), but we need to force - the current potion to be unworn, and don't want to do - that for the entire stack when starting with more than 1. - [Drinking a wielded potion of polymorph can trigger a shape - change which causes hero's weapon to be dropped. In 3.4.x, - that led to an "object lost" panic since subsequent useup() - was no longer dealing with an inventory item. Unwearing - the current potion is intended to keep it in inventory.] */ - if (otmp->quan > 1L) { - otmp = splitobj(otmp, 1L); - otmp->owornmask = 0L; /* rest of original stuck unaffected */ - } else if (otmp->owornmask) { - remove_worn_item(otmp, FALSE); + /* + * 3.6: quan > 1 used to be left to useup(), but we need to + * force the current potion to be unworn, and don't want to do + * that for the entire stack when starting with more than 1. + * [Drinking a wielded potion of polymorph can trigger a shape + * change which causes hero's weapon to be dropped. In 3.4.x, + * that led to an "object lost" panic since subsequent useup() + * was no longer dealing with an inventory item. Unwearing + * the current potion is intended to keep it in inventory.] + * + * 3.7: switch back to relying on useup() unless the object is + * actually worn. Otherwise drinking a stack of unpaid potions + * one by one in a shop makes each one a separate used-up item + * for 'Ix' invent display and for itemized shop billing instead + * of having a single stack with quantity greater than 1. + */ + if (otmp->owornmask) { + if (otmp->quan > 1L) { + otmp = splitobj(otmp, 1L); + otmp->owornmask = 0L; /* rest of original stack is unaffected */ + } else { + remove_worn_item(otmp, FALSE); + } } otmp->in_use = TRUE; /* you've opened the stopper */ if (objdescr_is(otmp, "milky") - && !(gm.mvitals[PM_GHOST].mvflags & G_GONE) - && !rn2(POTION_OCCUPANT_CHANCE(gm.mvitals[PM_GHOST].born))) { + && !(svm.mvitals[PM_GHOST].mvflags & G_GONE) + && !rn2(POTION_OCCUPANT_CHANCE(svm.mvitals[PM_GHOST].born))) { ghost_from_bottle(); useup(otmp); return ECMD_TIME; } else if (objdescr_is(otmp, "smoky") - && !(gm.mvitals[PM_DJINNI].mvflags & G_GONE) - && !rn2(POTION_OCCUPANT_CHANCE(gm.mvitals[PM_DJINNI].born))) { + && !(svm.mvitals[PM_DJINNI].mvflags & G_GONE) + && !rn2(POTION_OCCUPANT_CHANCE(svm.mvitals[PM_DJINNI].born))) { djinni_from_bottle(otmp); useup(otmp); return ECMD_TIME; @@ -764,7 +775,7 @@ dopotion(struct obj *otmp) /* potion or spell of restore ability; for spell, otmp is a temporary spellbook object that will be blessed if hero is skilled in healing */ -static void +staticfn void peffect_restore_ability(struct obj *otmp) { gp.potion_unkn++; @@ -789,7 +800,7 @@ peffect_restore_ability(struct obj *otmp) WEAK or worse, but that's handled via ATEMP(A_STR) now */ if (ABASE(i) < lim) { ABASE(i) = lim; - gc.context.botl = 1; + disp.botl = TRUE; /* only first found if not blessed */ if (!otmp->blessed) break; @@ -811,7 +822,7 @@ peffect_restore_ability(struct obj *otmp) } } -static void +staticfn void peffect_hallucination(struct obj *otmp) { if (Halluc_resistance) { @@ -832,7 +843,7 @@ peffect_hallucination(struct obj *otmp) } } -static void +staticfn void peffect_water(struct obj *otmp) { if (!otmp->blessed && !otmp->cursed) { @@ -847,7 +858,7 @@ peffect_water(struct obj *otmp) if (otmp->blessed) { pline("This burns like %s!", hliquid("acid")); exercise(A_CON, FALSE); - if (u.ulycn >= LOW_PM) { + if (ismnum(u.ulycn)) { Your("affinity to %s disappears!", makeplural(mons[u.ulycn].pmnames[NEUTRAL])); if (gy.youmonst.data == &mons[u.ulycn]) @@ -859,7 +870,7 @@ peffect_water(struct obj *otmp) } else if (otmp->cursed) { You_feel("quite proud of yourself."); healup(d(2, 6), 0, 0, 0); - if (u.ulycn >= LOW_PM && !Upolyd) + if (ismnum(u.ulycn) && !Upolyd) you_were(); exercise(A_CON, TRUE); } @@ -869,7 +880,7 @@ peffect_water(struct obj *otmp) make_sick(0L, (char *) 0, TRUE, SICK_ALL); exercise(A_WIS, TRUE); exercise(A_CON, TRUE); - if (u.ulycn >= LOW_PM) + if (ismnum(u.ulycn)) you_unwere(TRUE); /* "Purified" */ /* make_confused(0L, TRUE); */ } else { @@ -879,14 +890,14 @@ peffect_water(struct obj *otmp) KILLED_BY_AN); } else You_feel("full of dread."); - if (u.ulycn >= LOW_PM && !Upolyd) + if (ismnum(u.ulycn) && !Upolyd) you_were(); exercise(A_CON, FALSE); } } } -static void +staticfn void peffect_booze(struct obj *otmp) { gp.potion_unkn++; @@ -910,7 +921,7 @@ peffect_booze(struct obj *otmp) } } -static void +staticfn void peffect_enlightenment(struct obj *otmp) { if (otmp->cursed) { @@ -926,7 +937,7 @@ peffect_enlightenment(struct obj *otmp) } } -static void +staticfn void peffect_invisibility(struct obj *otmp) { boolean is_spell = (otmp->oclass == SPBOOK_CLASS); @@ -949,7 +960,7 @@ peffect_invisibility(struct obj *otmp) } } -static void +staticfn void peffect_see_invisible(struct obj *otmp) { int msg = Invisible && !Blind; @@ -985,14 +996,14 @@ peffect_see_invisible(struct obj *otmp) } } -static void +staticfn void peffect_paralysis(struct obj *otmp) { make_paralyzed(rn1(10, 25 - 12 * bcsign(otmp)), TRUE, "frozen by a potion"); } -static void +staticfn void peffect_sleeping(struct obj *otmp) { if (Sleep_resistance || Free_action) { @@ -1000,11 +1011,12 @@ peffect_sleeping(struct obj *otmp) You("yawn."); } else { You("suddenly fall asleep!"); + monstunseesu(M_SEEN_SLEEP); fall_asleep(-rn1(10, 25 - 12 * bcsign(otmp)), TRUE); } } -static int +staticfn int peffect_monster_detection(struct obj *otmp) { if (otmp->blessed) { @@ -1016,8 +1028,10 @@ peffect_monster_detection(struct obj *otmp) /* after a while, repeated uses become less effective */ if ((HDetect_monsters & TIMEOUT) >= 300L) i = 1; - else + else if (otmp->oclass == SPBOOK_CLASS) i = rn1(40, 21); + else /* potion */ + i = rn2(100) + 100; incr_itimeout(&HDetect_monsters, i); for (x = 1; x < COLNO; x++) { for (y = 0; y < ROWNO; y++) { @@ -1043,7 +1057,7 @@ peffect_monster_detection(struct obj *otmp) return 0; } -static int +staticfn int peffect_object_detection(struct obj *otmp) { if (object_detect(otmp, 0)) @@ -1052,7 +1066,7 @@ peffect_object_detection(struct obj *otmp) return 0; } -static void +staticfn void peffect_sickness(struct obj *otmp) { pline("Yecch! This stuff tastes like poison."); @@ -1079,7 +1093,7 @@ peffect_sickness(struct obj *otmp) if (!Fixed_abil) { poisontell(typ, FALSE); (void) adjattrib(typ, Poison_resistance ? -1 : -rn1(4, 3), - 1); + AA_YESMSG); } if (!Poison_resistance) { if (otmp->fromsink) @@ -1108,7 +1122,7 @@ peffect_sickness(struct obj *otmp) } } -static void +staticfn void peffect_confusion(struct obj *otmp) { if (!Confusion) { @@ -1124,7 +1138,7 @@ peffect_confusion(struct obj *otmp) FALSE); } -static void +staticfn void peffect_gain_ability(struct obj *otmp) { int i, ii; @@ -1247,7 +1261,7 @@ peffect_gain_ability(struct obj *otmp) } } -static void +staticfn void peffect_speed(struct obj *otmp) { boolean is_speed = (otmp->otyp == POT_SPEED); /* haste self */ @@ -1268,7 +1282,7 @@ peffect_speed(struct obj *otmp) } } -static void +staticfn void peffect_blindness(struct obj *otmp) { if (Blind || ((HBlinded || EBlinded) && BBlinded)) @@ -1278,7 +1292,7 @@ peffect_blindness(struct obj *otmp) (boolean) !Blind); } -static void +staticfn void peffect_gain_level(struct obj *otmp) { if (otmp->cursed) { @@ -1319,7 +1333,7 @@ peffect_gain_level(struct obj *otmp) u.uexp = rndexp(TRUE); } -static void +staticfn void peffect_healing(struct obj *otmp) { You_feel("better."); @@ -1328,7 +1342,7 @@ peffect_healing(struct obj *otmp) exercise(A_CON, TRUE); } -static void +staticfn void peffect_extra_healing(struct obj *otmp) { You_feel("much better."); @@ -1344,7 +1358,7 @@ peffect_extra_healing(struct obj *otmp) heal_legs(0); } -static void +staticfn void peffect_full_healing(struct obj *otmp) { You_feel("completely healed."); @@ -1365,7 +1379,7 @@ peffect_full_healing(struct obj *otmp) heal_legs(0); } -static void +staticfn void peffect_levitation(struct obj *otmp) { /* @@ -1401,7 +1415,7 @@ peffect_levitation(struct obj *otmp) resulted in incrementing 'nothing' */ gp.potion_nothing = 0; /* not nothing after all */ } else if (has_ceiling(&u.uz)) { - int dmg = rnd(!uarmh ? 10 : !is_hard(uarmh) ? 6 : 3); + int dmg = rnd(!uarmh ? 10 : !hard_helmet(uarmh) ? 6 : 3); You("hit your %s on the %s.", body_part(HEAD), ceiling(u.ux, u.uy)); @@ -1424,7 +1438,7 @@ peffect_levitation(struct obj *otmp) float_vs_flight(); } -static void +staticfn void peffect_gain_energy(struct obj *otmp) { int max_change, current_change; @@ -1461,11 +1475,11 @@ peffect_gain_energy(struct obj *otmp) u.uen = u.uenmax; else if (u.uen <= 0) u.uen = 0; - gc.context.botl = 1; + disp.botl = TRUE; exercise(A_WIS, TRUE); } -static void +staticfn void peffect_oil(struct obj *otmp) { boolean good_for_you = FALSE, vulnerable; @@ -1502,7 +1516,7 @@ peffect_oil(struct obj *otmp) exercise(A_WIS, good_for_you); } -static void +staticfn void peffect_acid(struct obj *otmp) { if (Acid_resistance) { @@ -1523,7 +1537,7 @@ peffect_acid(struct obj *otmp) gp.potion_unkn++; /* holy/unholy water can burn like acid too */ } -static void +staticfn void peffect_polymorph(struct obj *otmp) { You_feel("a little %s.", Hallucination ? "normal" : "strange"); @@ -1662,7 +1676,7 @@ healup(int nhp, int nxtra, boolean curesick, boolean cureblind) make_vomiting(0L, TRUE); make_sick(0L, (char *) 0, TRUE, SICK_ALL); } - gc.context.botl = 1; + disp.botl = TRUE; return; } @@ -1692,9 +1706,9 @@ strange_feeling(struct obj *obj, const char *txt) useup(obj); } -const char *bottlenames[] = { "bottle", "phial", "flagon", "carafe", +static const char *bottlenames[] = { "bottle", "phial", "flagon", "carafe", "flask", "jar", "vial" }; -const char *hbottlenames[] = { +static const char *hbottlenames[] = { "jug", "pitcher", "barrel", "tin", "bag", "box", "glass", "beaker", "tumbler", "vase", "flowerpot", "pan", "thingy", "mug", "teacup", "teapot", "keg", "bucket", "thermos", "amphora", "wineskin", "parcel", @@ -1705,13 +1719,13 @@ const char * bottlename(void) { if (Hallucination) - return hbottlenames[rn2(SIZE(hbottlenames))]; + return ROLL_FROM(hbottlenames); else - return bottlenames[rn2(SIZE(bottlenames))]; + return ROLL_FROM(bottlenames); } /* handle item dipped into water potion or steed saddle splashed by same */ -static boolean +staticfn boolean H2Opotion_dip( struct obj *potion, /* water */ struct obj *targobj, /* item being dipped into the water */ @@ -1753,8 +1767,12 @@ H2Opotion_dip( } else { /* dipping into uncursed water; carried() check skips steed saddle */ if (carried(targobj)) { + gm.mentioned_water = FALSE; /* water_damage() might set this */ if (water_damage(targobj, 0, TRUE) != ER_NOTHING) res = TRUE; + if (gm.mentioned_water) + makeknown(POT_WATER); + gm.mentioned_water = FALSE; } } if (func) { @@ -1955,23 +1973,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)); } @@ -1991,14 +2012,11 @@ potionhit(struct monst *mon, struct obj *obj, int how) break; } do_illness: - if ((mon->mhpmax > 3) && !resist(mon, POTION_CLASS, 0, NOTELL)) - mon->mhpmax /= 2; - if ((mon->mhp > 2) && !resist(mon, POTION_CLASS, 0, NOTELL)) + if (mon->mhp > 2) { mon->mhp /= 2; - if (mon->mhp > mon->mhpmax) - mon->mhp = mon->mhpmax; - if (canseemon(mon)) - pline("%s looks rather ill.", Monnam(mon)); + if (canseemon(mon)) + pline("%s looks rather ill.", Monnam(mon)); + } break; case POT_CONFUSION: case POT_BOOZE: @@ -2034,7 +2052,7 @@ potionhit(struct monst *mon, struct obj *obj, int how) mon_adjust_speed(mon, 1, obj); break; case POT_BLINDNESS: - if (haseyes(mon->data)) { + if (haseyes(mon->data) && !mon_perma_blind(mon)) { int btmp = 64 + rn2(32) + rn2(32) * !resist(mon, POTION_CLASS, 0, NOTELL); @@ -2061,9 +2079,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 */ @@ -2130,7 +2146,7 @@ potionhit(struct monst *mon, struct obj *obj, int how) when inside a tended shop */ if (!shkp) /* if shkp was killed, unpaid ought to cleared already */ obj->unpaid = 0; - else if (gc.context.mon_moving) /* obj thrown by monster */ + else if (svc.context.mon_moving) /* obj thrown by monster */ subfrombill(obj, shkp); else /* obj thrown by hero */ (void) stolen_value(obj, u.ux, u.uy, (boolean) shkp->mpeaceful, @@ -2143,7 +2159,6 @@ potionhit(struct monst *mon, struct obj *obj, int how) void potionbreathe(struct obj *obj) { - boolean cureblind = FALSE; boolean unambiguous = FALSE; /* if effect is unambiguous, call makeknown */ boolean breathe = !breathless(gy.youmonst.data); boolean cansmell = breathe && olfaction(gy.youmonst.data); @@ -2210,7 +2225,7 @@ potionbreathe(struct obj *obj) ABASE(i)++; /* only first found if not blessed */ isdone = !(obj->blessed); - gc.context.botl = 1; + disp.botl = TRUE; } if (++i >= A_MAX) i = 0; @@ -2234,38 +2249,17 @@ potionbreathe(struct obj *obj) unambiguous = TRUE; break; case POT_FULL_HEALING: - if (Upolyd) - u.mh += 10; - else - u.uhp += 10; - cureblind = TRUE; - /*FALLTHRU*/ + healup(10, 0, FALSE, TRUE); + exercise(A_CON, TRUE); + You_feel("a little better."); + break; case POT_EXTRA_HEALING: - if (Upolyd) - u.mh += 2; - else - u.uhp += 2; - if (!obj->cursed) - cureblind = TRUE; - /*FALLTHRU*/ + healup(2, 0, FALSE, !obj->cursed); + exercise(A_CON, TRUE); + You_feel("a little better."); + break; case POT_HEALING: - if (Upolyd) - u.mh++; - else - u.uhp++; - if (u.mh > u.mhmax) { - u.mh = u.mhmax; - } - if (u.uhp > u.uhpmax) { - u.uhp = u.uhpmax; - } - gc.context.botl = 1; - if (obj->blessed) - cureblind = TRUE; - if (cureblind) { - make_blinded(0L, !u.ucreamed); - make_deaf(0L, TRUE); - } + healup(1, 0, FALSE, obj->blessed); exercise(A_CON, TRUE); You_feel("a little better."); break; @@ -2282,7 +2276,7 @@ potionbreathe(struct obj *obj) else u.uhp -= 5; } - gc.context.botl = 1; + disp.botl = TRUE; exercise(A_CON, FALSE); } if (cansmell) { @@ -2378,8 +2372,9 @@ potionbreathe(struct obj *obj) break; case POT_WATER: if (u.umonnum == PM_GREMLIN) { + (void) split_mon(&gy.youmonst, (struct monst *) 0); unambiguous = TRUE; - } else if (u.ulycn >= LOW_PM) { + } else if (ismnum(u.ulycn)) { /* vapor from [un]holy water will trigger transformation but won't cure lycanthropy */ if (obj->blessed && gy.youmonst.data == &mons[u.ulycn]) { @@ -2480,7 +2475,7 @@ potionbreathe(struct obj *obj) } /* returns the potion type when o1 is dipped in o2 */ -static short +staticfn short mixtype(struct obj *o1, struct obj *o2) { int o1typ = o1->otyp, o2typ = o2->otyp; @@ -2500,6 +2495,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: @@ -2510,6 +2506,7 @@ mixtype(struct obj *o1, struct obj *o2) } else if (o1typ == POT_FULL_HEALING && o2typ == POT_GAIN_ABILITY) { return POT_GAIN_ENERGY; } + FALLTHROUGH; /*FALLTHRU*/ case UNICORN_HORN: switch (o2typ) { @@ -2573,11 +2570,14 @@ mixtype(struct obj *o1, struct obj *o2) /* getobj callback for object to be dipped (not the thing being dipped into, * that uses drink_ok) */ -static int +staticfn int dip_ok(struct obj *obj) { - /* dipping hands and gold isn't currently implemented */ - if (!obj || obj->oclass == COIN_CLASS) + if (!obj) + return GETOBJ_DOWNPLAY; + + /* dipping gold isn't currently implemented */ + if (obj->oclass == COIN_CLASS) return GETOBJ_EXCLUDE; if (inaccessible_equipment(obj, (const char *) 0, FALSE)) @@ -2586,12 +2586,24 @@ dip_ok(struct obj *obj) return GETOBJ_SUGGEST; } +/* getobj callback for object to be dipped when hero has slippery hands */ +staticfn int +dip_hands_ok(struct obj *obj) +{ + if (!obj && (Glib && can_reach_floor(FALSE))) + return GETOBJ_SUGGEST; + + return dip_ok(obj); +} + /* call hold_another_object() to deal with a transformed potion; its weight won't have changed but it might require an extra slot that isn't available or it might merge into some other carried stack */ -static void -hold_potion(struct obj *potobj, const char *drop_fmt, const char *drop_arg, - const char *hold_msg) +staticfn void +hold_potion( + struct obj *potobj, + const char *drop_fmt, const char *drop_arg, + const char *hold_msg) { int cap = near_capacity(), save_pickup_burden = flags.pickup_burden; @@ -2608,69 +2620,82 @@ hold_potion(struct obj *potobj, const char *drop_fmt, const char *drop_arg, return; } -/* #dip command - get item to dip, then get potion to dip it into */ +/* #dip command - get item to dip, then get potion to dip it into; + precede with 'm' to bypass fountain, pool, or sink at hero's spot */ int dodip(void) { static const char Dip_[] = "Dip "; struct obj *potion, *obj; - uchar here; char qbuf[QBUFSZ], obuf[QBUFSZ]; const char *shortestname; /* last resort obj name for prompt */ + uchar here = levl[u.ux][u.uy].typ; + boolean is_hands, at_pool = is_pool(u.ux, u.uy), + at_fountain = IS_FOUNTAIN(here), at_sink = IS_SINK(here), + at_here = (!iflags.menu_requested + && (at_pool || at_fountain || at_sink)); - if (!(obj = getobj("dip", dip_ok, GETOBJ_PROMPT))) + obj = getobj("dip", at_here ? dip_hands_ok : dip_ok, GETOBJ_PROMPT); + if (!obj) return ECMD_CANCEL; if (inaccessible_equipment(obj, "dip", FALSE)) return ECMD_OK; - shortestname = (is_plural(obj) || pair_of(obj)) ? "them" : "it"; - + is_hands = (obj == &hands_obj); + shortestname = (is_hands || is_plural(obj) || pair_of(obj)) ? "them" + : "it"; drink_ok_extra = 0; - /* preceding #dip with 'm' skips the possibility of dipping into - fountains and pools plus the prompting which those entail */ - if (!iflags.menu_requested) { - /* - * Bypass safe_qbuf() since it doesn't handle varying suffix without - * an awful lot of support work. Format the object once, even though - * the fountain and pool prompts offer a lot more room for it. - * 3.6.0 used thesimpleoname() unconditionally, which posed no risk - * of buffer overflow but drew bug reports because it omits user- - * supplied type name. - * getobj: "What do you want to dip into? [xyz or ?*] " - */ + /* + * Bypass safe_qbuf() since it doesn't handle varying suffix without + * an awful lot of support work. Format the object once, even though + * the fountain and pool prompts offer a lot more room for it. + * 3.6.0 used thesimpleoname() unconditionally, which posed no risk + * of buffer overflow but drew bug reports because it omits user- + * supplied type name. + * getobj: "What do you want to dip into? [xyz or ?*] " + */ + if (is_hands) { + Snprintf(obuf, sizeof obuf, "your %s", makeplural(body_part(HAND))); + } else { Strcpy(obuf, short_oname(obj, doname, thesimpleoname, - /* 128 - (24 + 54 + 1) leaves 49 for */ - QBUFSZ - sizeof "What do you want to dip \ + /* 128 - (24 + 54 + 1) leaves 49 for + */ + QBUFSZ - sizeof "What do you want to dip\ into? [abdeghjkmnpqstvwyzBCEFHIKLNOQRTUWXZ#-# or ?*] ")); + } - here = levl[u.ux][u.uy].typ; + /* preceding #dip with 'm' skips the possibility of dipping into pools, + fountains, and sinks plus the extra prompting which those entail */ + if (!iflags.menu_requested) { /* Is there a fountain to dip into here? */ if (!can_reach_floor(FALSE)) { ; /* can't dip something into fountain or pool if can't reach */ - } else if (IS_FOUNTAIN(here)) { + } else if (at_fountain) { Snprintf(qbuf, sizeof(qbuf), "%s%s into the fountain?", Dip_, - Verbose(3, dodip1) ? obuf : shortestname); + flags.verbose ? obuf : shortestname); /* "Dip into the fountain?" */ if (y_n(qbuf) == 'y') { - obj->pickup_prev = 0; + if (!is_hands) + obj->pickup_prev = 0; dipfountain(obj); return ECMD_TIME; } ++drink_ok_extra; - } else if (IS_SINK(here)) { + } else if (at_sink) { Snprintf(qbuf, sizeof(qbuf), "%s%s into the sink?", Dip_, - Verbose(4, dodip4) ? obuf : shortestname); - /* "Dip into the fountain?" */ + flags.verbose ? obuf : shortestname); if (y_n(qbuf) == 'y') { + if (!is_hands) + obj->pickup_prev = 0; dipsink(obj); - return 1; + return ECMD_TIME; } ++drink_ok_extra; - } else if (is_pool(u.ux, u.uy)) { + } else if (at_pool) { const char *pooltype = waterbody_name(u.ux, u.uy); Snprintf(qbuf, sizeof(qbuf), "%s%s into the %s?", Dip_, - Verbose(3, dodip2) ? obuf : shortestname, pooltype); + flags.verbose ? obuf : shortestname, pooltype); /* "Dip into the {pool, moat, &c}?" */ if (y_n(qbuf) == 'y') { if (Levitation) { @@ -2678,6 +2703,10 @@ dodip(void) } else if (u.usteed && !is_swimmer(u.usteed->data) && P_SKILL(P_RIDING) < P_BASIC) { rider_cant_reach(); /* not skilled enough to reach */ + } else if (is_hands || obj == uarmg) { + if (!is_hands) + obj->pickup_prev = 0; + (void) wash_hands(); } else { obj->pickup_prev = 0; if (obj->otyp == POT_ACID) @@ -2694,7 +2723,7 @@ dodip(void) /* "What do you want to dip into? [xyz or ?*] " */ Snprintf(qbuf, sizeof qbuf, "dip %s into", - Verbose(3, dodip3) ? obuf : shortestname); + flags.verbose ? obuf : shortestname); potion = getobj(qbuf, drink_ok, GETOBJ_NOFLAGS); if (!potion) return ECMD_CANCEL; @@ -2734,8 +2763,16 @@ dip_into(void) return potion_dip(obj, potion); } +staticfn void +poof(struct obj *potion) +{ + if (potion->dknown) + trycall(potion); + useup(potion); +} + /* called by dodip() or dip_into() after obj and potion have been chosen */ -static int +staticfn int potion_dip(struct obj *obj, struct obj *potion) { struct obj *singlepotion; @@ -2746,6 +2783,11 @@ potion_dip(struct obj *obj, struct obj *potion) pline("That is a potion bottle, not a Klein bottle!"); return ECMD_OK; } + if (obj == &hands_obj) { + You("can't fit your %s into the mouth of the bottle!", + body_part(HAND)); + return ECMD_OK; + } obj->pickup_prev = 0; /* no longer 'recently picked up' */ potion->in_use = TRUE; /* assume it will be used up */ @@ -2753,8 +2795,10 @@ potion_dip(struct obj *obj, struct obj *potion) boolean useeit = !Blind || (obj == ublindf && Blindfolded_only); const char *obj_glows = Yobjnam2(obj, "glow"); - if (H2Opotion_dip(potion, obj, useeit, obj_glows)) - goto poof; + if (H2Opotion_dip(potion, obj, useeit, obj_glows)) { + poof(potion); + return ECMD_TIME; + } } else if (obj->otyp == POT_POLYMORPH || potion->otyp == POT_POLYMORPH) { /* some objects can't be polymorphed */ if (obj_unpolyable(obj->otyp == POT_POLYMORPH ? potion : obj)) { @@ -2783,8 +2827,9 @@ potion_dip(struct obj *obj, struct obj *potion) prinv((char *) 0, obj, 0L); return ECMD_TIME; } else { - pline("Nothing seems to happen."); - goto poof; + pline1(nothing_seems_to_happen); + poof(potion); + return ECMD_TIME; } } potion->in_use = FALSE; /* didn't go poof */ @@ -2798,11 +2843,18 @@ potion_dip(struct obj *obj, struct obj *potion) magic = (mixture != STRANGE_OBJECT) ? objects[mixture].oc_magic : (objects[obj->otyp].oc_magic || objects[potion->otyp].oc_magic); Strcpy(qbuf, "The"); /* assume full stack */ - if (amt > (magic ? 3 : 7)) { - /* trying to dip multiple potions will usually affect only a + if (amt > (obj->odiluted ? 2 : magic ? 3 : 7)) { + /* Trying to dip multiple potions will usually affect only a subset; pick an amount between 3 and 8, inclusive, for magic - potion result, between 7 and N for non-magic */ - if (magic) + potion result, between 7 and N for non-magic. If diluted + potions are being dipped, only two are affected; this is a + balance fix to prevent cheap mass alchemy of the (very + common) potion of healing into the (very valuable) potion of + full healing, whilst permitting both healing->extra healing + and extra healing->full healing. */ + if (obj->odiluted) + amt = 2; + else if (magic) amt = rnd(min(amt, 8) - (3 - 1)) + (3 - 1); /* 1..6 + 2 */ else amt = rnd(amt - (7 - 1)) + (7 - 1); /* 1..(N-6) + 6 */ @@ -2820,7 +2872,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)) { + if (obj->cursed || obj->otyp == POT_ACID + || (obj->otyp == POT_OIL && obj->lamplit) || !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 @@ -2858,6 +2911,9 @@ potion_dip(struct obj *obj, struct obj *potion) case 4: otmp = mkobj(POTION_CLASS, FALSE); obj->otyp = otmp->otyp; + /* oil uses obj->age field differently from other potions */ + if (obj->otyp == POT_OIL || otmp->otyp == POT_OIL) + fixup_oil(obj, otmp); obfree(otmp, (struct obj *) 0); break; default: @@ -2902,7 +2958,8 @@ potion_dip(struct obj *obj, struct obj *potion) if (potion->otyp == POT_WATER && obj->otyp == TOWEL) { pline_The("towel soaks it up!"); /* wetting towel already done via water_damage() in H2Opotion_dip */ - goto poof; + poof(potion); + return ECMD_TIME; } if (is_poisonable(obj)) { @@ -2915,20 +2972,24 @@ potion_dip(struct obj *obj, struct obj *potion) Strcpy(buf, The(xname(potion))); pline("%s forms a coating on %s.", buf, the(xname(obj))); obj->opoisoned = TRUE; - goto poof; + poof(potion); + return ECMD_TIME; } 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; - goto poof; + poof(potion); + return ECMD_TIME; } } if (potion->otyp == POT_ACID) { - if (erode_obj(obj, 0, ERODE_CORRODE, EF_GREASE) != ER_NOTHING) - goto poof; + if (erode_obj(obj, 0, ERODE_CORRODE, EF_GREASE) != ER_NOTHING) { + poof(potion); + return ECMD_TIME; + } } if (potion->otyp == POT_OIL) { @@ -2943,8 +3004,8 @@ potion_dip(struct obj *obj, struct obj *potion) } else if (obj->oclass != WEAPON_CLASS && !is_weptool(obj)) { /* the following cases apply only to weapons */ goto more_dips; - /* Oil removes rust and corrosion, but doesn't unburn. - * Arrows, etc are classed as metallic due to arrowhead + /* Oil removes rust and corrosion, but doesn't unburn or repair + * cracks. Arrows, etc are classed as metallic due to arrowhead * material, but dipping in oil shouldn't repair them. */ } else if ((!is_rustprone(obj) && !is_corrodeable(obj)) @@ -2997,7 +3058,8 @@ potion_dip(struct obj *obj, struct obj *potion) set_keyed_loc(obj, u.ux, u.uy); pline("%s for an instant.", Tobjnam(obj, "quiver")); } - goto poof; + poof(potion); + return ECMD_TIME; } more_dips: @@ -3160,12 +3222,6 @@ potion_dip(struct obj *obj, struct obj *potion) pline("Interesting..."); return ECMD_TIME; - - poof: - if (potion->dknown) - trycall(potion); - useup(potion); - return ECMD_TIME; } void @@ -3232,32 +3288,6 @@ ferment(anything* arg, long timeout UNUSED) newsym(x, y); } -/* Dip an object into a sink. */ -void -dipsink(struct obj * obj) -{ - /* currently can't dip non-potions into sinks */ - if (obj->oclass != POTION_CLASS) { - pline("That wouldn't do anything."); - return; - } - You("pour %sthe %s down the drain.", (obj->quan != 1L ? "one of " : ""), - xname(obj)); - if (obj->otyp == POT_POLYMORPH) { - polymorph_sink(); - makeknown(POT_POLYMORPH); - } - else if (obj->otyp == POT_OIL) { - pline("It leaves an oily film on the basin."); - makeknown(POT_OIL); - } - else { - pline("A puff of vapor rises out."); - potionbreathe(obj); - } - useup(obj); -} - /* *monp grants a wish and then leaves the game */ void mongrantswish(struct monst **monp) @@ -3316,9 +3346,10 @@ djinni_from_bottle(struct obj *obj) * lamps */ if (u.uconduct.pets) { verbalize("Thank you for freeing me!"); - (void) tamedog(mtmp, (struct obj *) 0, FALSE); + (void) tamedog(mtmp, (struct obj *) 0, FALSE, FALSE); break; } + FALLTHROUGH; /* else FALLTHRU */ case 2: verbalize("You freed me!"); @@ -3356,16 +3387,27 @@ split_mon( : (const char *) s_suffix(mon_nam(mtmp))); if (mon == &gy.youmonst) { - mtmp2 = cloneu(); + if (u.mh > u.mhmax) /* sanity precaution */ + u.mh = u.mhmax; + mtmp2 = (u.mh > 1) ? cloneu() : (struct monst *) 0; if (mtmp2) { + /* mtmp2 has been created with mhpmax = u.mhmax, mhp = u.mh / 2, + and u.mh -= mtmp2->mhp; these reductions for both max hp + can't make either of them exceed corresponding current hp */ mtmp2->mhpmax = u.mhmax / 2; u.mhmax -= mtmp2->mhpmax; - gc.context.botl = 1; + disp.botl = TRUE; You("multiply%s!", reason); } } else { - mtmp2 = clone_mon(mon, 0, 0); + if (mon->mhp > mon->mhpmax) /* sanity precaution */ + mon->mhp = mon->mhpmax; + mtmp2 = (mon->mhp > 1) ? clone_mon(mon, 0, 0) : (struct monst *) 0; if (mtmp2) { + assert(mon->mhpmax >= mon->mhp); /* mon->mhpmax > 1 */ + /* mtmp2 has been created with mhpmax = mon->mhpmax, + mhp = mon->mhp / 2, and mon->mh -= mtmp2->mhp; + dividing max by 2 can't result in it exceeding current */ mtmp2->mhpmax = mon->mhpmax / 2; mon->mhpmax -= mtmp2->mhpmax; if (canspotmon(mon)) diff --git a/src/pray.c b/src/pray.c index b68295b4ae..486e1a631e 100644 --- a/src/pray.c +++ b/src/pray.c @@ -1,35 +1,41 @@ -/* NetHack 3.7 pray.c $NHDT-Date: 1684138081 2023/05/15 08:08:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.194 $ */ +/* NetHack 3.7 pray.c $NHDT-Date: 1727250729 2024/09/25 07:52:09 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.220 $ */ /* Copyright (c) Benson I. Margulies, Mike Stephenson, Steve Linhart, 1989. */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" -static int countfood(struct obj *); -static boolean insufficient_food(void); -static int prayer_done(void); -static struct obj *worst_cursed_item(void); -static int in_trouble(void); -static void fix_curse_trouble(struct obj *, const char *); -static void fix_worst_trouble(int); -static void angrygods(aligntyp); -static void at_your_feet(const char *); -static void gcrownu(void); -static void give_spell(void); -static void pleased(aligntyp); -static void godvoice(aligntyp, const char *); -static void god_zaps_you(aligntyp); -static void fry_by_god(aligntyp, boolean); -static void gods_angry(aligntyp); -static void gods_upset(aligntyp); -static void consume_offering(struct obj *); -static void offer_too_soon(aligntyp); -static void desecrate_high_altar(aligntyp); -static void offer_real_amulet(struct obj *, aligntyp); /* NORETURN */ -static void offer_different_alignment_altar(struct obj *, aligntyp); -static int bestow_artifact(void); -static boolean pray_revive(void); -static boolean water_prayer(boolean); -static boolean blocked_boulder(int, int); +staticfn int countfood(struct obj *); +staticfn boolean insufficient_food(void); +staticfn int prayer_done(void); +staticfn void maybe_turn_mon_iter(struct monst *); +staticfn struct obj *worst_cursed_item(void); +staticfn int in_trouble(void); +staticfn void fix_curse_trouble(struct obj *, const char *); +staticfn void fix_worst_trouble(int); +staticfn void angrygods(aligntyp); +staticfn void at_your_feet(const char *); +staticfn void gcrownu(void); +staticfn void give_spell(void); +staticfn void pleased(aligntyp); +staticfn void godvoice(aligntyp, const char *); +staticfn void god_zaps_you(aligntyp); +staticfn void fry_by_god(aligntyp, boolean); +staticfn void gods_angry(aligntyp); +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(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); /* simplify a few tests */ #define Cursed_obj(obj, typ) ((obj) && (obj)->otyp == (typ) && (obj)->cursed) @@ -108,13 +114,19 @@ static const char *hgodvoices[] = { #define on_altar() IS_ALTAR(levl[u.ux][u.uy].typ) #define on_shrine() ((levl[u.ux][u.uy].altarmask & AM_SHRINE) != 0) +/* used by turn undead iteration function; always reinitialized + before iterating that, so don't need to be globals */ +static int turn_undead_range; +static int turn_undead_msg_cnt; + /* critically low hit points if hp <= 5 or hp <= maxhp/N for some N */ boolean -critically_low_hp(boolean only_if_injured) /* determines whether maxhp <= 5 - matters */ +critically_low_hp( + boolean only_if_injured) /* determines whether maxhp <= 5 matters */ { - int divisor, hplim, curhp = Upolyd ? u.mh : u.uhp, - maxhp = Upolyd ? u.mhmax : u.uhpmax; + int divisor, hplim, + curhp = Upolyd ? u.mh : u.uhp, + maxhp = Upolyd ? u.mhmax : u.uhpmax; if (only_if_injured && !(curhp < maxhp)) return FALSE; @@ -167,7 +179,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; @@ -185,7 +197,7 @@ stuck_in_wall(void) * food, such as horns of plenty (arguably a horn of plenty could possibly just * produce near-useless water until it runs out of charges anyway). */ -static int +staticfn int countfood(struct obj* item) { struct obj* otmp; @@ -228,7 +240,7 @@ countfood(struct obj* item) /* Return True if you are not carrying enough food to get you out of being * Hungry (unless slow digesting in which you can survive a fair bit longer). */ -static boolean +staticfn boolean insufficient_food(void) { if (!u.uconduct.food) { @@ -264,7 +276,7 @@ insufficient_food(void) * 3.4.2: make an exception if polymorphed into a form which lacks * hands; that's a case where the ramifications override this doubt. */ -static int +staticfn int in_trouble(void) { struct obj *otmp; @@ -296,7 +308,7 @@ in_trouble(void) return TROUBLE_HIT; if (u.uhs >= WEAK && nofood) return TROUBLE_STARVING; - if (u.ulycn >= LOW_PM) + if (ismnum(u.ulycn)) return TROUBLE_LYCANTHROPE; if (near_capacity() >= EXT_ENCUMBER && AMAX(A_STR) - ABASE(A_STR) > 3) return TROUBLE_COLLAPSING; @@ -340,7 +352,7 @@ in_trouble(void) && (!u.uswallow || !attacktype_fordmg(u.ustuck->data, AT_ENGL, AD_BLND)))) return TROUBLE_BLIND; - /* deafness isn't it's own trouble; healing magic cures deafness + /* deafness isn't its own trouble; healing magic cures deafness when it cures blindness, so do the same with trouble repair */ if ((HDeaf & TIMEOUT) > 1L) return TROUBLE_BLIND; @@ -362,10 +374,10 @@ in_trouble(void) } /* select an item for TROUBLE_CURSED_ITEMS */ -static struct obj * +staticfn struct obj * worst_cursed_item(void) { - register struct obj *otmp; + struct obj *otmp; /* weapon takes precedence if it is interfering with taking off a ring or putting on a shield */ @@ -417,7 +429,7 @@ worst_cursed_item(void) return otmp; } -static void +staticfn void fix_curse_trouble(struct obj *otmp, const char *what) { if (!otmp) { @@ -441,10 +453,10 @@ fix_curse_trouble(struct obj *otmp, const char *what) update_inventory(); } -static void +staticfn void fix_worst_trouble(int trouble) { - int i; + int i, maxhp; struct obj *otmp = 0; const char *what = (const char *) 0; static NEARDATA const char leftglow[] = "Your left ring softly glows", @@ -464,22 +476,23 @@ fix_worst_trouble(int trouble) } You("can breathe again."); Strangled = 0; - gc.context.botl = 1; + disp.botl = TRUE; break; case TROUBLE_LAVA: /* teleport should always succeed, but if not, just untrap them */ if (!safe_teleds(TELEDS_NO_FLAGS)) reset_utrap(TRUE); - back_on_ground(DISSOLVED); /* DISSOLVED: pending cause of death - * if trouble didn't get cured */ + rescued_from_terrain(DISSOLVED); /* DISSOLVED: pending cause of death + * if trouble didn't get cured */ 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(); - gc.context.botl = 1; + disp.botl = TRUE; break; case TROUBLE_SICK: You_feel("better."); @@ -498,31 +511,31 @@ fix_worst_trouble(int trouble) boosted to be more than that */ You_feel("much better."); if (Upolyd) { - u.mhmax += rnd(5); - if (u.mhmax <= 5) - u.mhmax = 5 + 1; + maxhp = u.mhmax + rnd(5); + setuhpmax(max(maxhp, 5 + 1), FALSE); /* acts as setmhmax() */ u.mh = u.mhmax; } - if (u.uhpmax < u.ulevel * 5 + 11) - u.uhpmax += rnd(5); - if (u.uhpmax <= 5) - u.uhpmax = 5 + 1; - if (u.uhpmax > u.uhppeak) - u.uhppeak = u.uhpmax; - u.uhp = u.uhpmax; - gc.context.botl = 1; + maxhp = u.uhpmax; + if (maxhp < u.ulevel * 5 + 11) + maxhp += rnd(5); + /* True: update u.uhpmax even if currently poly'd */ + setuhpmax(max(maxhp, 5 + 1), TRUE); + u.uhp = u.uhpmax; /* setuhpmax() will do this when u.uhp is higher + * than u.uhpmax; prayer also does this if lower */ + disp.botl = TRUE; break; case TROUBLE_COLLAPSING: /* override Fixed_abil; uncurse that if feasible */ You_feel("%sstronger.", (AMAX(A_STR) - ABASE(A_STR) > 6) ? "much " : ""); ABASE(A_STR) = AMAX(A_STR); - gc.context.botl = 1; + disp.botl = TRUE; if (Fixed_abil) { if ((otmp = stuck_ring(uleft, RIN_SUSTAIN_ABILITY)) != 0) { if (otmp == uleft) what = leftglow; - } else if ((otmp = stuck_ring(uright, RIN_SUSTAIN_ABILITY)) != 0) { + } else if ((otmp = stuck_ring(uright, RIN_SUSTAIN_ABILITY)) + != 0) { if (otmp == uright) what = rightglow; } @@ -621,7 +634,7 @@ fix_worst_trouble(int trouble) for (i = 0; i < A_MAX; i++) { if (ABASE(i) < AMAX(i)) { ABASE(i) = AMAX(i); - gc.context.botl = 1; + disp.botl = TRUE; } } (void) encumber_msg(); @@ -681,7 +694,7 @@ fix_worst_trouble(int trouble) * bathroom walls, but who is foiled by bathrobes." --Bertrand Russell, 1943 * Divine wrath, dungeon walls, and armor follow the same principle. */ -static void +staticfn void god_zaps_you(aligntyp resp_god) { if (u.uswallow) { @@ -712,8 +725,11 @@ god_zaps_you(aligntyp resp_god) shieldeff(u.ux, u.uy); pline("It seems not to affect you."); monstseesu(M_SEEN_ELEC); - } else + monstunseesu(M_SEEN_REFL); + } else { fry_by_god(resp_god, FALSE); + monstunseesu(M_SEEN_REFL | M_SEEN_ELEC); + } } pline("%s is not deterred...", align_gname(resp_god)); @@ -733,17 +749,18 @@ god_zaps_you(aligntyp resp_god) */ if (uarms && !(EReflecting & W_ARMS) && !(EDisint_resistance & W_ARMS)) - (void) destroy_arm(uarms, FALSE); + (void) destroy_arm(uarms); if (uarmc && !(EReflecting & W_ARMC) && !(EDisint_resistance & W_ARMC)) - (void) destroy_arm(uarmc, FALSE); + (void) destroy_arm(uarmc); if (uarm && !(EReflecting & W_ARM) && !(EDisint_resistance & W_ARM) && !uarmc) - (void) destroy_arm(uarm, FALSE); + (void) destroy_arm(uarm); if (uarmu && !uarm && !uarmc) - (void) destroy_arm(uarmu, FALSE); + (void) destroy_arm(uarmu); if (!Disint_resistance) { fry_by_god(resp_god, TRUE); + monstunseesu(M_SEEN_DISINT); } else { You("bask in its %s glow for a minute...", NH_BLACK); godvoice(resp_god, "I believe it not!"); @@ -762,17 +779,17 @@ god_zaps_you(aligntyp resp_god) } } -static void +staticfn void fry_by_god(aligntyp resp_god, boolean via_disintegration) { You("%s!", !via_disintegration ? "fry to a crisp" : "disintegrate into a pile of dust"); - gk.killer.format = KILLED_BY; - Sprintf(gk.killer.name, "the wrath of %s", align_gname(resp_god)); + svk.killer.format = KILLED_BY; + Sprintf(svk.killer.name, "the wrath of %s", align_gname(resp_god)); done(DIED); } -static void +staticfn void angrygods(aligntyp resp_god) { int maxanger, new_ublesscnt; @@ -818,13 +835,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: @@ -853,7 +873,7 @@ angrygods(aligntyp resp_god) } /* helper to print "str appears at your feet", or appropriate */ -static void +staticfn void at_your_feet(const char *str) { if (Blind) @@ -870,7 +890,7 @@ at_your_feet(const char *str) } } -static void +staticfn void gcrownu(void) { struct obj *obj; @@ -924,7 +944,8 @@ gcrownu(void) case A_CHAOTIC: u.uevent.uhand_of_elbereth = 3; in_hand = u_wield_art(ART_STORMBRINGER); - already_exists = exist_artifact(RUNESWORD, artiname(ART_STORMBRINGER)); + already_exists = exist_artifact(RUNESWORD, + artiname(ART_STORMBRINGER)); what = (((already_exists && !in_hand) || class_gift != STRANGE_OBJECT) ? "take lives" : "steal souls"); @@ -1066,7 +1087,7 @@ gcrownu(void) return; } -static void +staticfn void give_spell(void) { struct obj *otmp; @@ -1091,7 +1112,7 @@ give_spell(void) || carrying(MAGIC_MARKER)) break; } - otmp->otyp = rnd_class(gb.bases[SPBOOK_CLASS], SPE_BLANK_PAPER); + otmp->otyp = rnd_class(svb.bases[SPBOOK_CLASS], SPE_BLANK_PAPER); } /* spellbook material depends on otyp, which we may have just * changed, so make sure the material matches the default. */ @@ -1140,7 +1161,7 @@ give_spell(void) return; } -static void +staticfn void pleased(aligntyp g_align) { /* don't use p_trouble, worst trouble may get fixed while praying */ @@ -1205,6 +1226,7 @@ pleased(aligntyp g_align) switch (min(action, 5)) { case 5: pat_on_head = 1; + FALLTHROUGH; /*FALLTHRU*/ case 4: do @@ -1213,9 +1235,12 @@ pleased(aligntyp g_align) break; case 3: + /* up to 10 troubles */ fix_worst_trouble(trouble); - case 2: - /* arbitrary number of tries */ + FALLTHROUGH; + /*FALLTHRU*/ + case 2: + /* up to 9 troubles */ while ((trouble = in_trouble()) > 0 && (++tryct < 10)) fix_worst_trouble(trouble); break; @@ -1223,6 +1248,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 */ } @@ -1292,7 +1318,7 @@ pleased(aligntyp g_align) if (u.uevent.uheard_tune < 1) { godvoice(g_align, (char *) 0); SetVoice((struct monst *) 0, 0, 80, voice_deity); - verbalize("Hark, %s!", (gy.youmonst.data->mlet == S_HUMAN) + verbalize("Hark, %s!", is_human(gy.youmonst.data) ? "mortal" : "creature"); SetVoice((struct monst *) 0, 0, 80, voice_deity); @@ -1303,12 +1329,13 @@ pleased(aligntyp g_align) } else if (u.uevent.uheard_tune < 2) { Soundeffect(se_divine_music, 50); You_hear("a divine music..."); - pline("It sounds like: \"%s\".", gt.tune); + pline("It sounds like: \"%s\".", svt.tune); u.uevent.uheard_tune++; record_achievement(ACH_TUNE); break; } } + FALLTHROUGH; /*FALLTHRU*/ case 2: if (!Blind) @@ -1330,7 +1357,7 @@ pleased(aligntyp g_align) u.mh = u.mhmax; if (ABASE(A_STR) < AMAX(A_STR)) { ABASE(A_STR) = AMAX(A_STR); - gc.context.botl = 1; /* before potential message */ + disp.botl = TRUE; /* before potential message */ (void) encumber_msg(); } if (u.uhunger < 900) @@ -1345,17 +1372,18 @@ pleased(aligntyp g_align) rather than issuing a pat-on-head */ u.ucreamed = 0; make_blinded(0L, TRUE); - gc.context.botl = 1; + disp.botl = TRUE; break; case 4: { - register 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)) { @@ -1409,6 +1437,7 @@ pleased(aligntyp g_align) gcrownu(); break; } + FALLTHROUGH; /*FALLTHRU*/ case 6: give_spell(); @@ -1433,8 +1462,8 @@ pleased(aligntyp g_align) of nutrition will be required. The increase gets throttled if it ever reaches 32K so that configurations using 16-bit ints are still viable. */ - if (gm.moves > 100000L) { - long incr = (gm.moves - 100000L) / 100L, + if (svm.moves > 100000L) { + long incr = (svm.moves - 100000L) / 100L, largest_ublesscnt_incr = (long) (LARGEST_INT - u.ublesscnt); if (incr > largest_ublesscnt_incr) @@ -1448,14 +1477,14 @@ pleased(aligntyp g_align) /* either blesses or curses water on the altar, * returns true if it found any water here. */ -static boolean +staticfn boolean water_prayer(boolean bless_water) { - register struct obj *otmp; - register long changed = 0; + struct obj *otmp; + long changed = 0; boolean other = FALSE, bc_known = !(Blind || Hallucination); - for (otmp = gl.level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere) { + for (otmp = svl.level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere) { /* turn water into (un)holy water */ if (otmp->otyp == POT_WATER && (bless_water ? !otmp->blessed : !otmp->cursed)) { @@ -1476,7 +1505,7 @@ water_prayer(boolean bless_water) return (boolean) (changed > 0L); } -static void +staticfn void godvoice(aligntyp g_align, const char *words) { const char *quot = ""; @@ -1487,19 +1516,18 @@ godvoice(aligntyp g_align, const char *words) words = ""; pline_The("voice of %s %s: %s%s%s", align_gname(g_align), - Hallucination ? hgodvoices[rn2(SIZE(hgodvoices))] - : godvoices[rn2(SIZE(godvoices))], + Hallucination ? ROLL_FROM(hgodvoices) : ROLL_FROM(godvoices), quot, words, quot); } -static void +staticfn void gods_angry(aligntyp g_align) { godvoice(g_align, "Thou hast angered me."); } /* The g_align god is upset with you. */ -static void +staticfn void gods_upset(aligntyp g_align) { if (g_align == u.ualign.type) @@ -1509,7 +1537,7 @@ gods_upset(aligntyp g_align) angrygods(g_align); } -static void +staticfn void consume_offering(struct obj *otmp) { if (Hallucination) @@ -1543,7 +1571,7 @@ consume_offering(struct obj *otmp) /* feedback when attempting to offer the Amulet on a "low altar" (not one of the high altars in the temples on the Astral Plane or Moloch's Sanctum) */ -static void +staticfn void offer_too_soon(aligntyp altaralign) { if (altaralign == A_NONE && Inhell) { @@ -1564,17 +1592,27 @@ offer_too_soon(aligntyp altaralign) : "ashamed"); } -static void -desecrate_high_altar(aligntyp altaralign) +void +desecrate_altar(boolean highaltar, aligntyp altaralign) { + char gvbuf[BUFSZ]; + /* * REAL BAD NEWS!!! High altars cannot be converted. Even an attempt - * gets the god who owns it truly pissed off. + * gets the god who owns it truly pissed off. The same effect for + * deliberately destroying a normal altar. */ + /* if you did this to your own altar, your god will hold a grudge... */ + if (altaralign == u.ualign.type) { + adjalign(-20); + u.ugangr += 5; + } You_feel("the air around you grow charged..."); - pline("Suddenly, you realize that %s has noticed you...", a_gname()); - godvoice(altaralign, - "So, mortal! You dare desecrate my High Temple!"); + pline("Suddenly, you realize that %s has noticed you...", + align_gname(altaralign)); + Sprintf(gvbuf, "So, mortal! You dare desecrate my %s!", + highaltar ? "High Temple" : "altar"); + godvoice(altaralign, gvbuf); /* Throw everything we have at the player */ god_zaps_you(altaralign); } @@ -1582,7 +1620,7 @@ desecrate_high_altar(aligntyp altaralign) /* offering the Amulet on a high altar (checked by caller) ends the game; we don't declare this 'NORETURN' because done() can return (if called with some reasons other than ASCENDED and ESCAPED) */ -static void +staticfn void offer_real_amulet(struct obj *otmp, aligntyp altaralign) { static NEARDATA const char @@ -1606,8 +1644,8 @@ offer_real_amulet(struct obj *otmp, aligntyp altaralign) /*[apparently shrug/snarl can be sensed without being seen]*/ pline("%s shrugs and retains dominion over %s,", Moloch, u_gname()); pline("then mercilessly snuffs out your life."); - Sprintf(gk.killer.name, "%s indifference", s_suffix(Moloch)); - gk.killer.format = KILLED_BY; + Sprintf(svk.killer.name, "%s indifference", s_suffix(Moloch)); + svk.killer.format = KILLED_BY; done(DIED); /* life-saved (or declined to die in wizard/explore mode) */ pline("%s snarls and tries again...", Moloch); @@ -1649,8 +1687,46 @@ 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, + boolean highaltar, + aligntyp altaralign) +{ + if (!highaltar && !otmp->known) { + offer_too_soon(altaralign); + return; + } + Soundeffect(se_thunderclap, 100); + You_hear("a nearby thunderclap."); + if (!otmp->known) { + You("realize you have made a %s.", + Hallucination ? "boo-boo" : "mistake"); + otmp->known = TRUE; + change_luck(-1); + } else { + /* don't you dare try to fool the gods */ + if (Deaf) + pline("Oh, no."); /* didn't hear thunderclap */ + change_luck(-3); + adjalign(-1); + u.ugangr += 3; + offer_negative_valued(highaltar, altaralign); + } +} + /* possibly convert an altar's alignment or the hero's alignment */ -static void +staticfn void offer_different_alignment_altar( struct obj *otmp, aligntyp altaralign) @@ -1664,7 +1740,7 @@ offer_different_alignment_altar( consume_offering(otmp); pline("%s accepts your allegiance.", a_gname()); - uchangealign(altaralign, 0); + uchangealign(altaralign, A_CG_CONVERT); /* Beware, Conversion is costly */ change_luck(-3); u.ublesscnt += 300; @@ -1718,16 +1794,101 @@ offer_different_alignment_altar( } } -static int -bestow_artifact(void) +staticfn void +sacrifice_your_race( + struct obj *otmp, + boolean highaltar, + aligntyp altaralign) +{ + int pm; + + if (is_demon(gy.youmonst.data)) { + You("find the idea very satisfying."); + exercise(A_WIS, TRUE); + } else if (u.ualign.type != A_CHAOTIC) { + pline("You'll regret this infamous offense!"); + exercise(A_WIS, FALSE); + } + + if (highaltar + && (altaralign != A_CHAOTIC || u.ualign.type != A_CHAOTIC)) { + desecrate_altar(highaltar, altaralign); + return; + } else if (altaralign != A_CHAOTIC && altaralign != A_NONE) { + /* curse the lawful/neutral altar */ + pline_The("altar is stained with %s blood.", gu.urace.adj); + levl[u.ux][u.uy].altarmask = AM_CHAOTIC; + newsym(u.ux, u.uy); /* in case Invisible to self */ + angry_priest(); + } else { + struct monst *dmon; + const char *demonless_msg; + + /* Human sacrifice on a chaotic or unaligned altar */ + /* is equivalent to demon summoning */ + if (altaralign == A_CHAOTIC && u.ualign.type != A_CHAOTIC) { + pline( + "The blood floods the altar, which vanishes in %s cloud!", + an(hcolor(NH_BLACK))); + levl[u.ux][u.uy].typ = ROOM; + levl[u.ux][u.uy].altarmask = 0; + newsym(u.ux, u.uy); + angry_priest(); + demonless_msg = "cloud dissipates"; + } else { + /* either you're chaotic or altar is Moloch's or both */ + pline_The("blood covers the altar!"); + change_luck(altaralign == A_NONE ? -2 : 2); + demonless_msg = "blood coagulates"; + } + if ((pm = dlord(altaralign)) != NON_PM + && (dmon = makemon(&mons[pm], u.ux, u.uy, MM_NOMSG)) + != 0) { + pline("Something's being summoned!"); + boss_entrance(dmon); + if (sgn(u.ualign.type) == sgn(dmon->data->maligntyp)) + dmon->mpeaceful = TRUE; + You("are terrified, and unable to move."); + nomul(-3); + gm.multi_reason = "being terrified of a demon"; + gn.nomovemsg = 0; + } else + pline_The("%s.", demonless_msg); + } + + if (u.ualign.type != A_CHAOTIC) { + adjalign(-5); + u.ugangr += 3; + (void) adjattrib(A_WIS, -1, AA_NOMSG); + if (!Inhell) + angrygods(u.ualign.type); + change_luck(-5); + } else + adjalign(5); + if (carried(otmp)) + useup(otmp); + else + useupf(otmp, 1L); +} + +staticfn int +bestow_artifact(uchar max_giftvalue) { - /* 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 && !Doomed && u.uluck >= 0 - && !rn2(10 + (2 * u.ugifts * u.ugifts))) { + boolean do_bestow = u.ulevel > 2 && u.uluck >= 0 && !Doomed; + 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 * u.ugifts)); + } + + if (do_bestow) { struct obj *otmp; - otmp = mk_artifact((struct obj *) 0, a_align(u.ux, u.uy)); + otmp = mk_artifact((struct obj *) 0, a_align(u.ux, u.uy), + max_giftvalue, TRUE); if (otmp) { char buf[BUFSZ]; @@ -1768,17 +1929,34 @@ 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) { - register struct obj *otmp; - int value = 0, pm; + struct obj *otmp; boolean highaltar; aligntyp altaralign = a_align(u.ux, u.uy); if (!on_altar() || u.uswallow) { - You("are not standing on an altar."); + You("are not %s an altar.", + (Levitation || Flying) ? "over" : "on"); + return ECMD_OK; + } else if (Confusion || Stunned || Hallucination) { + You("are too impaired to perform the rite."); return ECMD_OK; } highaltar = (levl[u.ux][u.uy].altarmask & AM_SANCTUM); @@ -1786,6 +1964,97 @@ dosacrifice(void) otmp = floorfood("sacrifice", 1); if (!otmp) return ECMD_OK; + if (otmp->otyp == AMULET_OF_YENDOR) { + if (!highaltar) { + offer_too_soon(altaralign); + return ECMD_TIME; + } else { + offer_real_amulet(otmp, altaralign); + /*NOTREACHED*/ + } + } /* real Amulet */ + + if (otmp->otyp == FAKE_AMULET_OF_YENDOR) { + offer_fake_amulet(otmp, highaltar, altaralign); + 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, AA_NOMSG); + 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 @@ -1797,294 +2066,150 @@ dosacrifice(void) */ #define MAXVALUE 24 /* Highest corpse value (besides Wiz) */ - if (otmp->otyp == CORPSE) { - register 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()); - - /* 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; - - if (otmp->corpsenm == PM_ACID_BLOB - || (gm.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)) { - if (is_demon(gy.youmonst.data)) { - You("find the idea very satisfying."); - exercise(A_WIS, TRUE); - } else if (u.ualign.type != A_CHAOTIC) { - pline("You'll regret this infamous offense!"); - exercise(A_WIS, FALSE); - } - - if (highaltar - && (altaralign != A_CHAOTIC || u.ualign.type != A_CHAOTIC)) { - desecrate_high_altar(altaralign); - return ECMD_TIME; - } else if (altaralign != A_CHAOTIC && altaralign != A_NONE) { - /* curse the lawful/neutral altar */ - pline_The("altar is stained with %s blood.", gu.urace.adj); - levl[u.ux][u.uy].altarmask = AM_CHAOTIC; - newsym(u.ux, u.uy); /* in case Invisible to self */ - angry_priest(); - if (!canspotself()) - /* with colored altars, regular newsym() doesn't cut it - - * it will see that the actual glyph is still the same, so - * the color won't be updated. This code must be added - * anywhere an altar mask could change. */ - newsym_force(u.ux, u.uy); - } else { - struct monst *dmon; - const char *demonless_msg; - - /* Human sacrifice on a chaotic or unaligned altar */ - /* is equivalent to demon summoning */ - if (altaralign == A_CHAOTIC && u.ualign.type != A_CHAOTIC) { - pline( - "The blood floods the altar, which vanishes in %s cloud!", - an(hcolor(NH_BLACK))); - levl[u.ux][u.uy].typ = ROOM; - levl[u.ux][u.uy].altarmask = 0; - newsym(u.ux, u.uy); - angry_priest(); - demonless_msg = "cloud dissipates"; - if (!canspotself()) - newsym_force(u.ux, u.uy); - } else { - /* either you're chaotic or altar is Moloch's or both */ - pline_The("blood covers the altar!"); - change_luck(altaralign == A_NONE ? -2 : 2); - demonless_msg = "blood coagulates"; - } - if ((pm = dlord(altaralign)) != NON_PM - && (dmon = makemon(&mons[pm], u.ux, u.uy, MM_NOMSG)) - != 0) { - pline("Something's being summoned!"); - boss_entrance(dmon); - if (sgn(u.ualign.type) == sgn(dmon->data->maligntyp)) - dmon->mpeaceful = TRUE; - You("are terrified, and unable to move."); - nomul(-3); - gm.multi_reason = "being terrified of a demon"; - gn.nomovemsg = 0; - } else - pline_The("%s.", demonless_msg); - } + /* 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()); - if (u.ualign.type != A_CHAOTIC) { - adjalign(-5); - u.ugangr += 3; - (void) adjattrib(A_WIS, -1, AA_NOMSG); - if (!Inhell) - angrygods(u.ualign.type); - change_luck(-5); - } else - adjalign(5); - if (carried(otmp)) - useup(otmp); - else - useupf(otmp, 1L); - 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, AA_NOMSG); - 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; - } - } - } /* corpse */ + /* you're handling this corpse, even if it was killed upon the altar + */ + feel_cockatrice(otmp, TRUE); + if (rider_corpse_revival(otmp, FALSE)) + return; - if (otmp->otyp == AMULET_OF_YENDOR) { - if (!highaltar) { - offer_too_soon(altaralign); - return ECMD_TIME; - } else { - offer_real_amulet(otmp, altaralign); - /*NOTREACHED*/ - } - } /* real Amulet */ + ptr = &mons[otmp->corpsenm]; - if (otmp->otyp == FAKE_AMULET_OF_YENDOR) { - if (!highaltar && !otmp->known) { - offer_too_soon(altaralign); - return ECMD_TIME; - } - Soundeffect(se_thunderclap, 100); - You_hear("a nearby thunderclap."); - if (!otmp->known) { - You("realize you have made a %s.", - Hallucination ? "boo-boo" : "mistake"); - otmp->known = TRUE; - change_luck(-1); - return ECMD_TIME; - } else { - /* don't you dare try to fool the gods */ - if (Deaf) - pline("Oh, no."); /* didn't hear thunderclap */ - change_luck(-3); - adjalign(-1); - u.ugangr += 3; - value = -3; - } - } /* fake Amulet */ + /* 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_high_altar(altaralign); - } else if (value < 0) { /* don't think the gods are gonna like this... */ - gods_upset(altaralign); - } else if (u.ualign.type != altaralign) { + desecrate_altar(highaltar, 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; - int saved_cnt = u.ublesscnt; - int saved_luck = u.uluck; + 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"); - 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 */ + 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; + 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 */ @@ -2116,9 +2241,9 @@ can_pray(boolean praying) /* false means no messages should be given */ if (gp.p_aligntyp == A_NONE) /* praying to Moloch */ gp.p_type = -2; - else if ((gp.p_trouble > 0) ? (u.ublesscnt > 200) /* big trouble */ - : (gp.p_trouble < 0) ? (u.ublesscnt > 100) /* minor difficulties */ - : (u.ublesscnt > 0)) /* not in trouble */ + else if ((gp.p_trouble > 0) ? (u.ublesscnt > 200) /* big trouble */ + : (gp.p_trouble < 0) ? (u.ublesscnt > 100) /* minor difficulty */ + : (u.ublesscnt > 0)) /* not in trouble */ gp.p_type = 0; /* too soon... */ else if ((int) Luck < 0 || u.ugangr || alignment < 0) gp.p_type = 1; /* too naughty... */ @@ -2141,12 +2266,12 @@ can_pray(boolean praying) /* false means no messages should be given */ } /* return TRUE if praying revived a pet corpse */ -static boolean +staticfn boolean pray_revive(void) { struct obj *otmp; - for (otmp = gl.level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere) + for (otmp = svl.level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere) if (otmp->otyp == CORPSE && has_omonst(otmp) && OMONST(otmp)->mtame && !OMONST(otmp)->isminion) break; @@ -2161,14 +2286,25 @@ pray_revive(void) int dopray(void) { + boolean ok; + /* * If ParanoidPray is set, confirm prayer to avoid accidental slips - * of Alt+p. - * YN(): don't save response in do-again buffer since if it is 'y', - * ^A would bypass confirmation, or if 'n', ^A would be pointless. + * of Alt+p. If ParanoidConfirm is also set, require "yes" rather + * than just "y" (will also require "no" to decline). */ - if (ParanoidPray && YN("Are you sure you want to pray?") != 'y') - return ECMD_OK; + if (ParanoidPray) { + ok = paranoid_query(ParanoidConfirm, + "Are you sure you want to pray?"); + + /* clear command recall buffer; otherwise ^A to repeat p(ray) would + do so without confirmation (if 'ok') or do nothing (if '!ok') */ + cmdq_clear(CQ_REPEAT); + cmdq_add_ec(CQ_REPEAT, dopray); + + if (!ok) /* declined the "are you sure?" confirmation */ + return ECMD_OK; + } if (!u.uconduct.gnostic++) /* breaking conduct should probably occur in can_pray() at @@ -2182,7 +2318,7 @@ dopray(void) if (!can_pray(TRUE)) return ECMD_OK; - u.ulastprayed = gm.moves; + u.ulastprayed = svm.moves; if (wizard && gp.p_type >= 0) { static const char forcesuccess[] = "Force the gods to be pleased?"; @@ -2191,7 +2327,16 @@ dopray(void) from the do-again buffer, so need to suppress this response too; otherwise subsequent ^A would use this answer for "are you sure?" and bypass confirmation */ - if ((ParanoidPray ? YN(forcesuccess) : y_n(forcesuccess)) == 'y') { + if (ParanoidPray) { + boolean save_doagain = gi.in_doagain; + + gi.in_doagain = FALSE; + ok = (YN(forcesuccess) == 'y'); + gi.in_doagain = save_doagain; + } else { + ok = (y_n(forcesuccess) == 'y'); + } + if (ok) { u.ublesscnt = 0; if (u.uluck < 0) u.uluck = 0; @@ -2217,7 +2362,7 @@ dopray(void) return ECMD_TIME; } -static int +staticfn int prayer_done(void) /* M. Stephenson (1.0.3b) */ { aligntyp alignment = gp.p_aligntyp; @@ -2227,7 +2372,7 @@ prayer_done(void) /* M. Stephenson (1.0.3b) */ /* praying at an unaligned altar, not necessarily in Gehennom */ You("%s diabolical laughter all around you...", !Deaf ? "hear" : "intuit"); - wake_nearby(); + wake_nearby(FALSE); adjalign(-2); exercise(A_WIS, FALSE); if (!Inhell) { @@ -2249,8 +2394,8 @@ prayer_done(void) /* M. Stephenson (1.0.3b) */ u.mh = 0; /* set killer things here because rehumanize will call done() if you * have unchanging and will impossible if killer is unset */ - Strcpy(gk.killer.name, residual); - gk.killer.format = KILLED_BY_AN; + Strcpy(svk.killer.name, residual); + svk.killer.format = KILLED_BY_AN; } rehumanize(); /* no Half_physical_damage adjustment here */ @@ -2298,14 +2443,75 @@ prayer_done(void) /* M. Stephenson (1.0.3b) */ return 1; } +/* iterable for undead turning by priest/knight */ +staticfn void +maybe_turn_mon_iter(struct monst *mtmp) +{ + /* 3.6.3: used to use cansee() here but the purpose is to prevent + #turn operating through walls, not to require that the hero be + able to see the target location */ + if (!couldsee(mtmp->mx, mtmp->my) + || mdistu(mtmp) > turn_undead_range) + return; + + if (!mtmp->mpeaceful + && (is_undead(mtmp->data) || is_vampshifter(mtmp) + || (is_demon(mtmp->data) && (u.ulevel > (MAXULEV / 2))))) { + wakeup(mtmp, FALSE, TRUE); + if (Confusion) { + if (!turn_undead_msg_cnt++) + pline("Unfortunately, your voice falters."); + mtmp->mflee = 0; + mtmp->mfrozen = 0; + mtmp->mcanmove = 1; + } else if (!resist(mtmp, '\0', 0, TELL)) { + int xlev = 6; + + switch (mtmp->data->mlet) { + /* this is intentional, lichs are tougher + than zombies. */ + case S_LICH: + xlev += 4; + 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)) { + if (u.ualign.type == A_CHAOTIC) { + mtmp->mpeaceful = 1; + set_malign(mtmp); + } else { /* damn them */ + killed(mtmp); + } + break; + } /* else flee */ + FALLTHROUGH; + /*FALLTHRU*/ + default: + monflee(mtmp, 0, FALSE, TRUE); + break; + } + } + } +} + /* #turn command */ int doturn(void) { /* Knights & Priest(esse)s only please */ - struct monst *mtmp, *mtmp2; const char *Gname; - int once, range, xlev; if (!Role_if(PM_CLERIC) && !Role_if(PM_KNIGHT)) { /* Try to use the "turn undead" spell. */ @@ -2351,65 +2557,11 @@ doturn(void) exercise(A_WIS, TRUE); /* note: does not perform unturn_dead() on victims' inventories */ - range = BOLT_LIM + (u.ulevel / 5); /* 8 to 14 */ - range *= range; - once = 0; - for (mtmp = fmon; mtmp; mtmp = mtmp2) { - mtmp2 = mtmp->nmon; - if (DEADMONSTER(mtmp)) - continue; - /* 3.6.3: used to use cansee() here but the purpose is to prevent - #turn operating through walls, not to require that the hero be - able to see the target location */ - if (!couldsee(mtmp->mx, mtmp->my) - || mdistu(mtmp) > range) - continue; - - if (!mtmp->mpeaceful - && (is_undead(mtmp->data) || is_vampshifter(mtmp) - || (is_demon(mtmp->data) && (u.ulevel > (MAXULEV / 2))))) { - wakeup(mtmp, FALSE, TRUE); - if (Confusion) { - if (!once++) - pline("Unfortunately, your voice falters."); - mtmp->mflee = 0; - mtmp->mfrozen = 0; - mtmp->mcanmove = 1; - } else if (!resist(mtmp, '\0', 0, TELL)) { - xlev = 6; - switch (mtmp->data->mlet) { - /* this is intentional, lichs are tougher - than zombies. */ - case S_LICH: - xlev += 4; - /*FALLTHRU*/ - case S_VAMPIRE: - xlev += 2; - /*FALLTHRU*/ - case S_WRAITH: - xlev += 2; - /*FALLTHRU*/ - case S_MUMMY: - xlev += 2; - /*FALLTHRU*/ - case S_ZOMBIE: - if (u.ulevel >= xlev && !resist(mtmp, '\0', 0, NOTELL)) { - if (u.ualign.type == A_CHAOTIC) { - mtmp->mpeaceful = 1; - set_malign(mtmp); - } else { /* damn them */ - killed(mtmp); - } - break; - } /* else flee */ - /*FALLTHRU*/ - default: - monflee(mtmp, 0, FALSE, TRUE); - break; - } - } - } - } + turn_undead_range = BOLT_LIM + (u.ulevel / 5); /* 8 to 14 */ + turn_undead_range *= turn_undead_range; + turn_undead_msg_cnt = 0; + + iter_mons(maybe_turn_mon_iter); /* * There is no detrimental effect on self for successful #turn @@ -2635,14 +2787,14 @@ altar_wrath(coordxy x, coordxy y) } /* assumes isok() at one space away, but not necessarily at two */ -static boolean +staticfn boolean blocked_boulder(int dx, int dy) { - register struct obj *otmp; + struct obj *otmp; int nx, ny; long count = 0L; - for (otmp = gl.level.objects[u.ux + dx][u.uy + dy]; otmp; + for (otmp = svl.level.objects[u.ux + dx][u.uy + dy]; otmp; otmp = otmp->nexthere) { if (otmp->otyp == BOULDER) count += otmp->quan; @@ -2660,6 +2812,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; @@ -2671,7 +2824,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 4b5e237109..ea5422d506 100644 --- a/src/priest.c +++ b/src/priest.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 priest.c $NHDT-Date: 1624322670 2021/06/22 00:44:30 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.70 $ */ +/* NetHack 3.7 priest.c $NHDT-Date: 1726862063 2024/09/20 19:54:23 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.103 $ */ /* Copyright (c) Izchak Miller, Steve Linhart, 1989. */ /* NetHack may be freely redistributed. See license for details. */ @@ -9,8 +9,8 @@ #define ALGN_SINNED (-4) /* worse than strayed (-1..-3) */ #define ALGN_PIOUS 14 /* better than fervent (9..13) */ -static boolean histemple_at(struct monst *, coordxy, coordxy); -static boolean has_shrine(struct monst *); +staticfn boolean histemple_at(struct monst *, coordxy, coordxy); +staticfn boolean has_shrine(struct monst *); void newepri(struct monst *mtmp) @@ -42,8 +42,8 @@ move_special(struct monst *mtmp, boolean in_his_shop, schar appr, boolean uondoor, boolean avoid, coordxy omx, coordxy omy, coordxy ggx, coordxy ggy) { - register coordxy nx, ny, nix, niy; - register schar i; + coordxy nx, ny, nix, niy; + schar i; schar chcnt, cnt; coord poss[9]; long info[9]; @@ -91,6 +91,7 @@ move_special(struct monst *mtmp, boolean in_his_shop, schar appr, } } } +#undef GDIST if (mtmp->ispriest && avoid && nix == omx && niy == omy && onlineu(omx, omy)) { /* might as well move closer as long it's going to stay @@ -145,15 +146,15 @@ move_special(struct monst *mtmp, boolean in_his_shop, schar appr, char temple_occupied(char *array) { - register char *ptr; + char *ptr; for (ptr = array; *ptr; ptr++) - if (gr.rooms[*ptr - ROOMOFFSET].rtype == TEMPLE) + if (svr.rooms[*ptr - ROOMOFFSET].rtype == TEMPLE) return *ptr; return '\0'; } -static boolean +staticfn boolean histemple_at(struct monst *priest, coordxy x, coordxy y) { return (boolean) (priest && priest->ispriest @@ -249,7 +250,7 @@ priestini( priest = makemon(prim, px, py, MM_EPRI); if (priest) { - EPRI(priest)->shroom = (schar) ((sroom - gr.rooms) + ROOMOFFSET); + EPRI(priest)->shroom = (schar) ((sroom - svr.rooms) + ROOMOFFSET); EPRI(priest)->shralign = Amask2align(levl[sx][sy].altarmask); EPRI(priest)->shrpos.x = sx; EPRI(priest)->shrpos.y = sy; @@ -307,6 +308,7 @@ char * priestname( struct monst *mon, int article, + boolean reveal_high_priest, char *pname) /* caller-supplied output buffer */ { boolean do_hallu = Hallucination, @@ -318,19 +320,23 @@ priestname( if (!mon->ispriest && !mon->isminion) /* should never happen... */ return strcpy(pname, what); /* caller must be confused */ + /* for high priest(ess), "high" (or "grand" for poohbah) will be inserted + [this was done near the end but we want 'what' to be updated sooner] */ + if (mon->ispriest || aligned_priest || high_priest) + what = do_hallu ? "poohbah" : mon->female ? "priestess" : "priest"; + *pname = '\0'; if (article != ARTICLE_NONE && (!do_hallu || !bogon_is_pname(whatcode))) { if (article == ARTICLE_YOUR || (article == ARTICLE_A && high_priest)) article = ARTICLE_THE; if (article == ARTICLE_THE) { - Strcat(pname, "the "); + Strcpy(pname, "the "); + } else if (!strcmp(what, "Angel")) { + /* bypass just_an(); it would yield "" due to treating capital A + as indicating a personal name */ + Strcpy(pname, "an "); } else { - char buf2[BUFSZ] = DUMMY; - - /* don't let "Angel of " fool an() into using "the " */ - Strcpy(buf2, pname); - *buf2 = lowc(*buf2); - (void) just_an(pname, buf2); + (void) just_an(pname, what); } } /* pname[] contains "" or {"a ","an ","the "} */ @@ -342,24 +348,14 @@ priestname( } if (mon->isminion && EMIN(mon)->renegade) { /* avoid "an renegade Angel" */ - if (!strcmp(pname, "an ")) /* will fail for "an invisible " */ + if (!strcmp(pname, "an ") && !mon->minvis) Strcpy(pname, "a "); Strcat(pname, "renegade "); } - if (mon->ispriest || aligned_priest) { /* high_priest implies ispriest */ - if (!aligned_priest && !high_priest) { - ; /* polymorphed priest; use ``what'' as is */ - } else { - if (high_priest) - Strcat(pname, "high "); - if (Hallucination) - what = "poohbah"; - else if (mon->female) - what = "priestess"; - else - what = "priest"; - } + if (mon->ispriest || aligned_priest) { + if (high_priest) + Strcat(pname, do_hallu ? "grand " : "high "); } else { if (mon->mtame && !strcmpi(what, "Angel")) Strcat(pname, "guardian "); @@ -367,8 +363,9 @@ priestname( Strcat(pname, what); /* same as distant_monnam(), more or less... */ - if (do_hallu || !high_priest || !Is_astralevel(&u.uz) - || next2u(mon->mx, mon->my) || gp.program_state.gameover) { + if (do_hallu || !high_priest || reveal_high_priest + || !Is_astralevel(&u.uz) + || m_next2u(mon) || program_state.gameover) { Strcat(pname, " of "); Strcat(pname, halu_gname(mon_aligntyp(mon))); } @@ -381,7 +378,7 @@ p_coaligned(struct monst *priest) return (boolean) (u.ualign.type == mon_aligntyp(priest)); } -static boolean +staticfn boolean has_shrine(struct monst *pri) { struct rm *lev; @@ -400,7 +397,7 @@ has_shrine(struct monst *pri) struct monst * findpriest(char roomno) { - register struct monst *mtmp; + struct monst *mtmp; for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) @@ -438,7 +435,7 @@ intemple(int roomno) sanctum = (priest->data == &mons[PM_HIGH_CLERIC] && (Is_sanctum(&u.uz) || In_endgame(&u.uz))); can_speak = !helpless(priest); - if (can_speak && !Deaf && gm.moves >= epri_p->intone_time) { + if (can_speak && !Deaf && svm.moves >= epri_p->intone_time) { unsigned save_priest = priest->ispriest; /* don't reveal the altar's owner upon temple entry in @@ -449,7 +446,7 @@ intemple(int roomno) pline("%s intones:", canseemon(priest) ? Monnam(priest) : "A nearby voice"); priest->ispriest = save_priest; - epri_p->intone_time = gm.moves + (long) d(10, 500); /* ~2505 */ + epri_p->intone_time = svm.moves + (long) d(10, 500); /* ~2505 */ /* make sure that we don't suppress entry message when we've just given its "priest intones" introduction */ epri_p->enter_time = 0L; @@ -467,7 +464,7 @@ intemple(int roomno) /* repeat visit, or attacked priest before entering */ msg1 = "You desecrate this place by your presence!"; } - } else if (gm.moves >= epri_p->enter_time) { + } else if (svm.moves >= epri_p->enter_time) { Sprintf(buf, "Pilgrim, you enter a %s place!", !shrined ? "desecrated" : "sacred"); msg1 = buf; @@ -477,7 +474,7 @@ intemple(int roomno) verbalize1(msg1); if (msg2) verbalize1(msg2); - epri_p->enter_time = gm.moves + (long) d(10, 100); /* ~505 */ + epri_p->enter_time = svm.moves + (long) d(10, 100); /* ~505 */ } if (!sanctum) { if (!shrined || !p_coaligned(priest) @@ -495,9 +492,9 @@ intemple(int roomno) /* give message if we haven't seen it recently or if alignment update has caused it to switch from forbidding to sense-of-peace or vice versa */ - if (gm.moves >= *this_time || *other_time >= *this_time) { + if (svm.moves >= *this_time || *other_time >= *this_time) { You(msg1, msg2); - *this_time = gm.moves + (long) d(10, 20); /* ~55 */ + *this_time = svm.moves + (long) d(10, 20); /* ~55 */ /* avoid being tricked by the RNG: switch might have just happened and previous random threshold could be larger */ if (*this_time <= *other_time) @@ -528,7 +525,7 @@ intemple(int roomno) if (!rn2(5) && (mtmp = makemon(&mons[PM_GHOST], u.ux, u.uy, MM_NOMSG)) != 0) { - int ngen = gm.mvitals[PM_GHOST].born; + int ngen = svm.mvitals[PM_GHOST].born; if (canspotmon(mtmp)) { pline("A%s ghost appears next to you%c", ngen < 5 ? "n enormous" : "", @@ -552,7 +549,7 @@ scary_ghost(struct monst* ghost) ghost->mpeaceful = 0; set_malign(ghost); if (gm.multi >= 0) { - if (Verbose(3, ghost_from_bottle)) /* not actually only from bottle */ + if (flags.verbose) /* not actually only from bottle */ You("are frightened to death, and unable to move."); nomul(-3); gm.multi_reason = "being terrified of a ghost"; @@ -690,9 +687,9 @@ priest_talk(struct monst *priest) SetVoice(priest, 0, 80, 0); verbalize("Thy selfless generosity is deeply appreciated."); if (money_cnt(gi.invent) < (offer * 2L) && coaligned) { - if (strayed && (gm.moves - u.ucleansed) > 5000L) { + if (strayed && (svm.moves - u.ucleansed) > 5000L) { u.ualign.record = 0; /* cleanse thee */ - u.ucleansed = gm.moves; + u.ucleansed = svm.moves; } else { adjalign(2); } @@ -705,8 +702,8 @@ struct monst * mk_roamer(struct permonst *ptr, aligntyp alignment, coordxy x, coordxy y, boolean peaceful) { - register struct monst *roamer; - register boolean coaligned = (u.ualign.type == alignment); + struct monst *roamer; + boolean coaligned = (u.ualign.type == alignment); #if 0 /* this was due to permonst's pxlth field which is now gone */ if (ptr != &mons[PM_ALIGNED_CLERIC] && ptr != &mons[PM_ANGEL]) @@ -753,8 +750,8 @@ in_your_sanctuary( struct monst *mon, /* if non-null, overrides */ coordxy x, coordxy y) { - register char roomno; - register struct monst *priest; + char roomno; + struct monst *priest; if (mon) { if (is_minion(mon->data) || is_rider(mon->data)) @@ -787,7 +784,7 @@ ghod_hitsu(struct monst *priest) ax = x = EPRI(priest)->shrpos.x; ay = y = EPRI(priest)->shrpos.y; - troom = &gr.rooms[roomno - ROOMOFFSET]; + troom = &svr.rooms[roomno - ROOMOFFSET]; if (u_at(x, y) || !linedup(u.ux, u.uy, x, y, 1)) { if (IS_DOOR(levl[u.ux][u.uy].typ)) { @@ -857,7 +854,7 @@ ghod_hitsu(struct monst *priest) void angry_priest(void) { - register struct monst *priest; + struct monst *priest; struct rm *lev; if ((priest = findpriest(temple_occupied(u.urooms))) != 0) { @@ -880,6 +877,7 @@ angry_priest(void) newemin(priest); priest->ispriest = 0; /* now a roaming minion */ priest->isminion = 1; + assert(has_emin(priest)); EMIN(priest)->min_align = eprip->shralign; EMIN(priest)->renegade = FALSE; /* discard priest's memory of his former shrine; @@ -918,4 +916,7 @@ restpriest(struct monst *mtmp, boolean ghostly) } } +#undef ALGN_SINNED +#undef ALGN_PIOUS + /*priest.c*/ diff --git a/src/quest.c b/src/quest.c index 9bf3f3d046..84414fc758 100644 --- a/src/quest.c +++ b/src/quest.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 quest.c $NHDT-Date: 1596498200 2020/08/03 23:43:20 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.29 $ */ +/* NetHack 3.7 quest.c $NHDT-Date: 1687036547 2023/06/17 21:15:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.38 $ */ /* Copyright 1991, M. Stephenson */ /* NetHack may be freely redistributed. See license for details. */ @@ -9,21 +9,21 @@ #include "quest.h" #define Not_firsttime (on_level(&u.uz0, &u.uz)) -#define Qstat(x) (gq.quest_status.x) - -static void on_start(void); -static void on_locate(void); -static void on_goal(void); -static boolean not_capable(void); -static int is_pure(boolean); -static void expulsion(boolean); -static boolean arti_fulfills_quest(void); -static void chat_with_leader(struct monst *); -static void chat_with_nemesis(void); -static void chat_with_guardian(void); -static void prisoner_speaks(struct monst *); - -static void +#define Qstat(x) (svq.quest_status.x) + +staticfn void on_start(void); +staticfn void on_locate(void); +staticfn void on_goal(void); +staticfn boolean not_capable(void); +staticfn int is_pure(boolean); +staticfn void expulsion(boolean); +staticfn boolean arti_fulfills_quest(void); +staticfn void chat_with_leader(struct monst *); +staticfn void chat_with_nemesis(void); +staticfn void chat_with_guardian(void); +staticfn void prisoner_speaks(struct monst *); + +staticfn void on_start(void) { if (!Qstat(first_start)) { @@ -37,7 +37,7 @@ on_start(void) } } -static void +staticfn void on_locate(void) { /* the locate messages are phrased in a manner such that they only @@ -59,7 +59,7 @@ on_locate(void) } } -static void +staticfn void on_goal(void) { if (Qstat(killed_nemesis)) { @@ -113,7 +113,7 @@ nemdead(void) /* this won't do anything unless on Valk locate level */ restore_valk_locate(PHASE_PHYSICAL | PHASE_DIALOGUE | PHASE_VISION); - gl.level.flags.visited_after_event |= VISITED_AFTER_NEMDEAD; + svl.level.flags.visited_after_event |= VISITED_AFTER_NEMDEAD; } } @@ -148,13 +148,13 @@ ok_to_quest(void) && is_pure(FALSE) > 0) || Qstat(killed_leader)); } -static boolean +staticfn boolean not_capable(void) { return (boolean) (u.ulevel < MIN_QUEST_LEVEL); } -static int +staticfn int is_pure(boolean talk) { int purity; @@ -187,7 +187,7 @@ is_pure(boolean talk) * This assumes that the hero is currently _in_ the quest dungeon and that * there is a single branch to and from it. */ -static void +staticfn void expulsion(boolean seal) { branch *br; @@ -223,7 +223,7 @@ expulsion(boolean seal) /* Does returning to the leader with the quest artifact complete the quest? * (Currently, if not, the completion condition is assumed to be killing the * nemesis.) */ -static boolean +staticfn boolean arti_fulfills_quest(void) { if (Role_if(PM_VALKYRIE)) { @@ -236,7 +236,9 @@ arti_fulfills_quest(void) artifact or you've just thrown it to/at him or her. If quest completion text hasn't been given yet, give it now. Otherwise give another message about the character keeping the artifact - and using the magic portal to return to the dungeon. */ + and using the magic portal to return to the dungeon. Also called + if hero throws or kicks an invocation item (probably the Bell) + at the leader. */ void leader_sees_qarti(struct obj *obj) /* quest artifact; possibly null if carrying Amulet or if finishing quest while talking @@ -244,11 +246,37 @@ leader_sees_qarti(struct obj *obj) /* quest artifact; possibly null if carrying { struct obj *otmp; - if (u.uhave.amulet) { /* unlikely but not impossible */ + if (obj && !is_quest_artifact(obj)) { + /* tossed an invocation item (or [fake] AoY) at the quest leader */ + if (Deaf) + return; /* optional (unlike quest completion) so skip if deaf */ + /* do ID first so that the message identifying the item will refer to + it by name (and so justify the ID we already gave...) */ + fully_identify_obj(obj); + /* update_inventory() is not necessary or helpful here because item + was thrown, so isn't currently in inventory anyway */ + if (obj->otyp == AMULET_OF_YENDOR) { + qt_pager("hasamulet"); + } else if (obj->otyp == FAKE_AMULET_OF_YENDOR) { + verbalize( + "Sorry to say, this is a mere imitation of the true Amulet of Yendor."); + } else { + verbalize("Ah, I see you've found %s.", the(xname(obj))); + } + return; + } + + if (u.uhave.amulet) { + /* has the amulet in inventory -- most likely the player has already + completed the quest and stopped in on her way back up, but it's not + impossible to have gotten the amulet before formally presenting the + quest artifact to the leader. */ qt_pager("hasamulet"); /* leader IDs the real amulet but ignores any fakes */ - if ((otmp = carrying(AMULET_OF_YENDOR)) != 0) + if ((otmp = carrying(AMULET_OF_YENDOR)) != (struct obj *) 0) { fully_identify_obj(otmp); + update_inventory(); + } } else { if (!arti_fulfills_quest()) { @@ -259,6 +287,7 @@ leader_sees_qarti(struct obj *obj) /* quest artifact; possibly null if carrying /* if nemesis is dead, continue to rest of this function and * complete the quest. */ } + /* normal quest completion; threw artifact or walked up carrying it */ qt_pager(!Qstat(got_thanks) ? "offeredit" : "offeredit2"); /* should have obtained bell during quest; if not, suggest returning for it now */ @@ -278,7 +307,7 @@ leader_sees_qarti(struct obj *obj) /* quest artifact; possibly null if carrying } } -static void +staticfn void chat_with_leader(struct monst *mtmp) { if (!mtmp->mpeaceful || Qstat(pissed_off)) @@ -402,7 +431,7 @@ leader_speaks(struct monst *mtmp) } } -static void +staticfn void chat_with_nemesis(void) { /* The nemesis will do most of the talking, but... */ @@ -439,7 +468,23 @@ nemesis_speaks(void) qt_pager("discourage"); } -static void +/* create cloud of stinking gas around dying nemesis */ +void +nemesis_stinks(coordxy mx, coordxy my) +{ + boolean save_mon_moving = svc.context.mon_moving; + + /* + * Some nemeses (determined by caller) release a cloud of noxious + * gas when they die. Don't make the hero be responsible for such + * a cloud even if hero has just killed nemesis. + */ + svc.context.mon_moving = TRUE; + create_gas_cloud(mx, my, 5, 8); + svc.context.mon_moving = save_mon_moving; +} + +staticfn void chat_with_guardian(void) { /* These guys/gals really don't have much to say... */ @@ -449,7 +494,7 @@ chat_with_guardian(void) qt_pager("guardtalk_before"); } -static void +staticfn void prisoner_speaks(struct monst *mtmp) { if (mtmp->data == &mons[PM_PRISONER] @@ -518,8 +563,10 @@ void quest_stat_check(struct monst *mtmp) { if (mtmp->data->msound == MS_NEMESIS) - Qstat(in_battle) = (!helpless(mtmp) - && monnear(mtmp, u.ux, u.uy)); + Qstat(in_battle) = (!helpless(mtmp) && monnear(mtmp, u.ux, u.uy)); } +#undef Not_firsttime +#undef Qstat + /*quest.c*/ diff --git a/src/questpgr.c b/src/questpgr.c index 04a104166d..6552cfa8df 100644 --- a/src/questpgr.c +++ b/src/questpgr.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 questpgr.c $NHDT-Date: 1655065145 2022/06/12 20:19:05 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.79 $ */ +/* NetHack 3.7 questpgr.c $NHDT-Date: 1704043695 2023/12/31 17:28:15 $ $NHDT-Branch: keni-luabits2 $:$NHDT-Revision: 1.87 $ */ /* Copyright 1991, M. Stephenson */ /* NetHack may be freely redistributed. See license for details. */ @@ -13,17 +13,18 @@ #include "wintty.h" #endif -static const char *intermed(void); -static struct obj *find_qarti(struct obj *); -static const char *neminame(void); -static const char *guardname(void); -static const char *homebase(void); -static void qtext_pronoun(char, char); -static void convert_arg(char); -static void deliver_by_pline(const char *); -static void deliver_by_window(const char *, int); -static boolean skip_pager(boolean); -static boolean com_pager_core(const char *, const char *, boolean, char **); +staticfn const char *intermed(void); +/* sometimes find_qarti(gi.invent), and gi.invent can be null */ +staticfn struct obj *find_qarti(struct obj *) NO_NNARGS; +staticfn const char *neminame(void); +staticfn const char *guardname(void); +staticfn const char *homebase(void); +staticfn void qtext_pronoun(char, char); +staticfn void convert_arg(char); +staticfn void deliver_by_pline(const char *); +staticfn void deliver_by_window(const char *, int); +staticfn boolean skip_pager(boolean); +staticfn boolean com_pager_core(const char *, const char *, boolean, char **); short quest_info(int typ) @@ -55,7 +56,7 @@ ldrname(void) } /* return your intermediate target string */ -static const char * +staticfn const char * intermed(void) { return gu.urole.intermed; @@ -67,7 +68,7 @@ is_quest_artifact(struct obj *otmp) return (boolean) (otmp->oartifact == gu.urole.questarti); } -static struct obj * +staticfn struct obj * find_qarti(struct obj *ochain) { struct obj *otmp, *qarti; @@ -112,13 +113,13 @@ find_quest_artifact(unsigned whichchains) qarti = find_qarti(gm.migrating_objs); } if (!qarti && (whichchains & (1 << OBJ_BURIED)) != 0) - qarti = find_qarti(gl.level.buriedobjlist); + qarti = find_qarti(svl.level.buriedobjlist); return qarti; } /* return your role nemesis' name */ -static const char * +staticfn const char * neminame(void) { int i = gu.urole.neminum; @@ -128,7 +129,7 @@ neminame(void) return gn.nambuf; } -static const char * +staticfn const char * guardname(void) /* return your role leader's guard monster name */ { int i = gu.urole.guardnum; @@ -136,7 +137,7 @@ guardname(void) /* return your role leader's guard monster name */ return mons[i].pmnames[NEUTRAL]; } -static const char * +staticfn const char * homebase(void) /* return your role leader's location */ { return gu.urole.homebase; @@ -170,8 +171,8 @@ stinky_nemesis(struct monst *mon) (void) com_pager_core(gu.urole.filecode, "killed_nemesis", FALSE, &mesg); #endif - /* this is somewhat fragile; it assumes that when both (noxious or - poisonous or toxic) and (gas or fumes) are present, the latter + /* this is somewhat fragile; it assumes that when both {noxious or + poisonous or toxic} and {gas or fumes} are present, the latter refers to the former rather than to something unrelated; it does make sure that fumes occurs after noxious rather than before */ if (mesg) { @@ -193,7 +194,7 @@ stinky_nemesis(struct monst *mon) /* replace deity, leader, nemesis, or artifact name with pronoun; overwrites cvt_buf[] */ -static void +staticfn void qtext_pronoun( char who, /* 'd' => deity, 'l' => leader, 'n' => nemesis, 'o' => arti */ char which) /* 'h'|'H'|'i'|'I'|'j'|'J' */ @@ -215,9 +216,9 @@ qtext_pronoun( : (lwhich == 'i') ? "them" : (lwhich == 'j') ? "their" : "?"; } else { - godgend = (who == 'd') ? gq.quest_status.godgend - : (who == 'l') ? gq.quest_status.ldrgend - : (who == 'n') ? gq.quest_status.nemgend + godgend = (who == 'd') ? svq.quest_status.godgend + : (who == 'l') ? svq.quest_status.ldrgend + : (who == 'n') ? svq.quest_status.nemgend : 2; /* default to neuter */ pnoun = (lwhich == 'h') ? genders[godgend].he : (lwhich == 'i') ? genders[godgend].him @@ -230,14 +231,14 @@ qtext_pronoun( return; } -static void +staticfn void convert_arg(char c) { - register const char *str; + const char *str; switch (c) { case 'p': - str = gp.plname; + str = svp.plname; break; case 'c': str = (flags.female && gu.urole.name.f) ? gu.urole.name.f @@ -310,7 +311,7 @@ convert_arg(char c) str = Blind ? "sense" : "see"; break; case 'Z': - str = gd.dungeons[0].dname; + str = svd.dungeons[0].dname; break; case '%': str = "%"; @@ -373,6 +374,7 @@ convert_line(const 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)); @@ -381,6 +383,7 @@ convert_line(const 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)); @@ -402,8 +405,9 @@ convert_line(const 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; @@ -415,7 +419,7 @@ convert_line(const char *in_line, char *out_line) return; } -static void +staticfn void deliver_by_pline(const char *str) { char in_line[BUFSZ], out_line[BUFSZ]; @@ -431,7 +435,7 @@ deliver_by_pline(const char *str) } } -static void +staticfn void deliver_by_window(const char *msg, int how) { char in_line[BUFSZ], out_line[BUFSZ]; @@ -451,16 +455,16 @@ deliver_by_window(const char *msg, int how) destroy_nhwindow(datawin); } -static boolean +staticfn boolean skip_pager(boolean common UNUSED) { /* WIZKIT: suppress plot feedback if starting with quest artifact */ - if (gp.program_state.wizkit_wishing) + if (program_state.wizkit_wishing) return TRUE; return FALSE; } -static boolean +staticfn boolean com_pager_core( const char *section, const char *msgid, @@ -475,7 +479,7 @@ com_pager_core( lua_State *L; char *text = NULL, *synopsis = NULL, *fallback_msgid = NULL; boolean res = FALSE; - nhl_sandbox_info sbi = {NHL_SB_SAFE, 0, 0, 0}; + nhl_sandbox_info sbi = {NHL_SB_SAFE, 1*1024*1024, 0, 1*1024*1024}; if (skip_pager(TRUE)) return FALSE; @@ -636,12 +640,12 @@ qt_montype(void) if (rn2(5)) { qpm = gu.urole.enemy1num; - if (qpm != NON_PM && rn2(5) && !(gm.mvitals[qpm].mvflags & G_GENOD)) + if (qpm != NON_PM && rn2(5) && !(svm.mvitals[qpm].mvflags & G_GENOD)) return &mons[qpm]; return mkclass(gu.urole.enemy1sym, 0); } qpm = gu.urole.enemy2num; - if (qpm != NON_PM && rn2(5) && !(gm.mvitals[qpm].mvflags & G_GENOD)) + if (qpm != NON_PM && rn2(5) && !(svm.mvitals[qpm].mvflags & G_GENOD)) return &mons[qpm]; return mkclass(gu.urole.enemy2sym, 0); } @@ -659,4 +663,6 @@ deliver_splev_message(void) } } +#undef QTEXT_FILE + /*questpgr.c*/ diff --git a/src/read.c b/src/read.c index beee8f0ca9..2de326e00f 100644 --- a/src/read.c +++ b/src/read.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 read.c $NHDT-Date: 1654931501 2022/06/11 07:11:41 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.257 $ */ +/* NetHack 3.7 read.c $NHDT-Date: 1715889745 2024/05/16 20:02:25 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.308 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -8,54 +8,55 @@ #define Your_Own_Role(mndx) ((mndx) == gu.urole.mnum) #define Your_Own_Race(mndx) ((mndx) == gu.urace.mnum) -static boolean learnscrolltyp(short); -static void cap_spe(struct obj *); -static char *erode_obj_text(struct obj *, char *); -static char *hawaiian_design(struct obj *, char *); -static char *tin_text(struct obj *, char *); -static int read_ok(struct obj *); -static void stripspe(struct obj *); -static void p_glow1(struct obj *); -static void p_glow2(struct obj *, const char *); -static void flood_space(coordxy, coordxy, genericptr); -static void unflood_space(coordxy, coordxy, genericptr); -static void forget(int); -static int maybe_tame(struct monst *, struct obj *); -static boolean can_center_cloud(coordxy, coordxy); -static void display_stinking_cloud_positions(int); -static void seffect_enchant_armor(struct obj **); -static void seffect_destroy_armor(struct obj **); -static void seffect_confuse_monster(struct obj **); -static void seffect_scare_monster(struct obj **); -static void seffect_remove_curse(struct obj **); -static void seffect_create_monster(struct obj **); -static void seffect_enchant_weapon(struct obj **); -static void seffect_taming(struct obj **); -static void seffect_genocide(struct obj **); -static void seffect_light(struct obj **); -static void seffect_charging(struct obj **); -static void seffect_amnesia(struct obj **); -static void seffect_fire(struct obj **); -static void seffect_earth(struct obj **); -static void seffect_punishment(struct obj **); -static void seffect_stinking_cloud(struct obj **); -static void seffect_water(struct obj **); -static void seffect_blank_paper(struct obj **); -static void seffect_teleportation(struct obj **); -static void seffect_gold_detection(struct obj **); -static void seffect_food_detection(struct obj **); -static void seffect_identify(struct obj **); -static void seffect_magic_mapping(struct obj **); +staticfn boolean learnscrolltyp(short); +staticfn void cap_spe(struct obj *); +staticfn char *erode_obj_text(struct obj *, char *); +staticfn char *hawaiian_design(struct obj *, char *); +staticfn char *tin_text(struct obj *, char *); +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 flood_space(coordxy, coordxy, genericptr); +staticfn void unflood_space(coordxy, coordxy, genericptr); +staticfn void forget(int); +staticfn int maybe_tame(struct monst *, struct obj *); +staticfn boolean can_center_cloud(coordxy, coordxy); +staticfn void display_stinking_cloud_positions(boolean); +staticfn void seffect_enchant_armor(struct obj **); +staticfn void seffect_destroy_armor(struct obj **); +staticfn void seffect_confuse_monster(struct obj **); +staticfn void seffect_scare_monster(struct obj **); +staticfn void seffect_remove_curse(struct obj **); +staticfn void seffect_create_monster(struct obj **); +staticfn void seffect_enchant_weapon(struct obj **); +staticfn void seffect_taming(struct obj **); +staticfn void seffect_genocide(struct obj **); +staticfn void seffect_light(struct obj **); +staticfn void seffect_charging(struct obj **); +staticfn void seffect_amnesia(struct obj **); +staticfn void seffect_fire(struct obj **); +staticfn void seffect_earth(struct obj **); +staticfn void seffect_punishment(struct obj **); +staticfn void seffect_stinking_cloud(struct obj **); +staticfn void seffect_water(struct obj **); +staticfn void seffect_blank_paper(struct obj **); +staticfn void seffect_teleportation(struct obj **); +staticfn void seffect_gold_detection(struct obj **); +staticfn void seffect_food_detection(struct obj **); +staticfn void seffect_identify(struct obj **); +staticfn void seffect_magic_mapping(struct obj **); #ifdef MAIL_STRUCTURES -static void seffect_mail(struct obj **); +staticfn void seffect_mail(struct obj **); #endif /* MAIL_STRUCTURES */ -static void set_lit(coordxy, coordxy, genericptr); -static void do_class_genocide(void); -static boolean create_particular_parse(char *, +staticfn void set_lit(coordxy, coordxy, genericptr); +staticfn void do_class_genocide(void); +staticfn boolean create_particular_parse(char *, + struct _create_particular_data *); +staticfn struct monst * create_particular_creation( struct _create_particular_data *); -static struct monst *create_particular_creation(struct _create_particular_data *); -static boolean +staticfn boolean learnscrolltyp(short scrolltyp) { if (!objects[scrolltyp].oc_name_known) { @@ -68,7 +69,7 @@ learnscrolltyp(short scrolltyp) /* also called from teleport.c for scroll of teleportation */ void -learnscroll(struct obj* sobj) +learnscroll(struct obj *sobj) { /* it's implied that sobj->dknown is set; we couldn't be reading this scroll otherwise */ @@ -77,8 +78,8 @@ learnscroll(struct obj* sobj) } /* max spe is +99, min is -99 */ -static void -cap_spe(struct obj* obj) +staticfn void +cap_spe(struct obj *obj) { if (obj) { if (abs(obj->spe) > SPE_LIM) @@ -86,8 +87,8 @@ cap_spe(struct obj* obj) } } -static char * -erode_obj_text(struct obj* otmp, char* buf) +staticfn char * +erode_obj_text(struct obj *otmp, char *buf) { int erosion = greatest_erosion(otmp); @@ -98,7 +99,7 @@ erode_obj_text(struct obj* otmp, char* buf) } char * -tshirt_text(struct obj* tshirt, char* buf) +tshirt_text(struct obj *tshirt, char *buf) { get_rnd_text(SHIRTFILE, buf, int_hash1(tshirt->o_id), (int (*)(int)) 0, MD_PAD_RUMORS); @@ -137,8 +138,8 @@ hawaiian_motif(struct obj* shirt, char* buf) return buf; } -static char * -hawaiian_design(struct obj* shirt, char* buf) +staticfn char * +hawaiian_design(struct obj *shirt, char *buf) { static const char *const hawaiian_bgs[] = { /* solid colors */ @@ -167,7 +168,7 @@ hawaiian_design(struct obj* shirt, char* buf) } char * -apron_text(struct obj* apron, char* buf) +apron_text(struct obj *apron, char *buf) { static const char *const apron_msgs[] = { "Kiss the cook", @@ -210,7 +211,7 @@ static const char *const candy_wrappers[] = { /* return the text of a candy bar's wrapper */ const char * -candy_wrapper_text(struct obj* obj) +candy_wrapper_text(struct obj *obj) { /* modulo operation is just bullet proofing; 'spe' is already in range */ return candy_wrappers[obj->spe % SIZE(candy_wrappers)]; @@ -218,7 +219,7 @@ candy_wrapper_text(struct obj* obj) /* assign a wrapper to a candy bar stack */ void -assign_candy_wrapper(struct obj* obj) +assign_candy_wrapper(struct obj *obj) { if (obj->otyp == CANDY_BAR) { /* skips candy_wrappers[0] */ @@ -230,7 +231,7 @@ assign_candy_wrapper(struct obj* obj) DISABLE_WARNING_FORMAT_NONLITERAL /* Get the text on a tin; return null for an unlabeled tin */ -static char * +staticfn char * tin_text(struct obj *tin, char* buf) { /* Keep these constants up to date with the strings in the array.*/ @@ -341,8 +342,8 @@ tin_text(struct obj *tin, char* buf) RESTORE_WARNING_FORMAT_NONLITERAL /* getobj callback for object to read */ -static int -read_ok(struct obj* obj) +staticfn int +read_ok(struct obj *obj) { if (!obj) return GETOBJ_EXCLUDE; @@ -360,7 +361,7 @@ int doread(void) { static const char find_any_braille[] = "feel any Braille writing."; - register struct obj *scroll; + struct obj *scroll; boolean confused, nodisappear; int otyp; @@ -393,7 +394,7 @@ doread(void) /* outrumor has its own blindness check */ if (otyp == FORTUNE_COOKIE) { - if (Verbose(3, doread1)) + if (flags.verbose) You("break up the cookie and throw away the pieces."); outrumor(bcsign(scroll), BY_COOKIE); if (!Blind) @@ -420,8 +421,7 @@ doread(void) return ECMD_OK; } if (otyp == HAWAIIAN_SHIRT) { - pline("%s features %s.", - Verbose(3, doread2) ? "The design" : "It", + pline("%s features %s.", flags.verbose ? "The design" : "It", hawaiian_design(scroll, buf)); return ECMD_TIME; } @@ -433,7 +433,7 @@ doread(void) mesg = (otyp == T_SHIRT) ? tshirt_text(scroll, buf) : apron_text(scroll, buf); endpunct = ""; - if (Verbose(3, doread3)) { + if (flags.verbose) { int ln = (int) strlen(mesg); /* we will be displaying a sentence; need ending punctuation */ @@ -497,7 +497,7 @@ doread(void) if (Blind) { You("feel the embossed numbers:"); } else { - if (Verbose(3, doread4)) + if (flags.verbose) pline("It reads:"); pline("\"%s\"", scroll->oartifact @@ -512,7 +512,7 @@ doread(void) ((int) scroll->o_id % 10), (!((int) scroll->o_id % 3)), (((int) scroll->o_id * 7) % 10), - (Verbose(3, doread5) || Blind) ? "." : ""); + (flags.verbose || Blind) ? "." : ""); if (!u.uconduct.literate++) livelog_printf(LL_CONDUCT, "became literate by reading a credit card"); @@ -522,18 +522,20 @@ 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) { You_cant(find_any_braille); return ECMD_OK; } - if (Verbose(3, doread6)) + if (flags.verbose) pline("It reads:"); Sprintf(buf, "%s", pmname(pm, NEUTRAL)); pline("\"Magic Marker(TM) %s Red Ink Marker Pen. Water Soluble.\"", @@ -546,7 +548,7 @@ doread(void) } else if (scroll->oclass == COIN_CLASS) { if (Blind) You("feel the embossed words:"); - else if (Verbose(3, doread7)) + else if (flags.verbose) You("read:"); pline("\"1 Zorkmid. 857 GUE. In Frobs We Trust.\""); if (!u.uconduct.literate++) @@ -733,8 +735,8 @@ doread(void) RESTORE_WARNING_FORMAT_NONLITERAL -static void -stripspe(register struct obj* obj) +staticfn void +stripspe(struct obj *obj) { if (obj->blessed || obj->spe <= 0) { pline1(nothing_happens); @@ -748,14 +750,14 @@ stripspe(register struct obj* obj) } } -static void -p_glow1(register struct obj* otmp) +staticfn void +p_glow1(struct obj *otmp) { pline("%s briefly.", Yobjnam2(otmp, Blind ? "vibrate" : "glow")); } -static void -p_glow2(register struct obj* otmp, register const char* color) +staticfn void +p_glow2(struct obj *otmp, const char *color) { pline("%s%s%s for a moment.", Yobjnam2(otmp, Blind ? "vibrate" : "glow"), Blind ? "" : " ", Blind ? "" : hcolor(color)); @@ -763,7 +765,7 @@ p_glow2(register struct obj* otmp, register const char* color) /* getobj callback for object to charge */ int -charge_ok(struct obj* obj) +charge_ok(struct obj *obj) { if (!obj) return GETOBJ_EXCLUDE; @@ -803,9 +805,9 @@ charge_ok(struct obj* obj) /* recharge an object; curse_bless is -1 if the recharging implement was cursed, +1 if blessed, 0 otherwise. */ void -recharge(struct obj* obj, int curse_bless) +recharge(struct obj *obj, int curse_bless) { - register int n; + int n; boolean is_cursed, is_blessed; is_cursed = curse_bless < 0; @@ -1044,7 +1046,7 @@ recharge(struct obj* obj, int curse_bless) * Other things are subject to flags: * howmuch & ALL_SPELLS = forget all spells */ -static void +staticfn void forget(int howmuch) { struct monst *mtmp; @@ -1068,7 +1070,7 @@ forget(int howmuch) } /* monster is hit by scroll of taming's effect */ -static int +staticfn int maybe_tame(struct monst *mtmp, struct obj *sobj) { int was_tame = mtmp->mtame; @@ -1082,8 +1084,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, FALSE); + + if ((!was_peaceful && mtmp->mpeaceful) || was_tame != mtmp->mtame) return 1; } return 0; @@ -1103,7 +1106,7 @@ valid_cloud_pos(coordxy x, coordxy y) /* Callback for getpos_sethilite, also used in determining whether a scroll * should have its regular effects, or not because it was out of range. */ -boolean +staticfn boolean can_center_cloud(coordxy x, coordxy y) { if (!valid_cloud_pos(x, y)) @@ -1111,29 +1114,35 @@ can_center_cloud(coordxy x, coordxy y) return (cansee(x, y) && distu(x, y) < 50); } -static void -display_stinking_cloud_positions(int state) +staticfn void +display_stinking_cloud_positions(boolean on_off) { - if (state == 0) { - tmp_at(DISP_BEAM, cmap_to_glyph(S_goodpos)); - } else if (state == 1) { - coordxy x, y, dx, dy; - int dist = 6; + coordxy x, y, dx, dy; + int dist = 6; + if (on_off) { + /* on */ + tmp_at(DISP_BEAM, cmap_to_glyph(S_goodpos)); for (dx = -dist; dx <= dist; dx++) for (dy = -dist; dy <= dist; dy++) { x = u.ux + dx; y = u.uy + dy; - if (can_center_cloud(x,y)) + /* hero's location is allowed but highlighting the hero's + spot makes map harder to read (if using '$' rather than + by changing background color) */ + if (u_at(x, y)) + continue; + if (can_center_cloud(x, y)) tmp_at(x, y); } } else { + /* off */ tmp_at(DISP_END, 0); } } /* Flood a space. This is a callback function. */ -static void +staticfn void flood_space(coordxy x, coordxy y, genericptr_t poolcnt) { struct monst *mtmp; @@ -1158,7 +1167,7 @@ flood_space(coordxy x, coordxy y, genericptr_t poolcnt) /* create pool */ levl[x][y].typ = POOL; del_engr_at(x, y); - water_damage_chain(gl.level.objects[x][y], TRUE, 0, TRUE); + water_damage_chain(svl.level.objects[x][y], TRUE, 0, TRUE); mtmp = m_at(x, y); if (mtmp) minliquid(mtmp); @@ -1167,7 +1176,7 @@ flood_space(coordxy x, coordxy y, genericptr_t poolcnt) } /* Unflood (dry up) a space. This is a callback function. */ -static void +staticfn void unflood_space(coordxy x, coordxy y, genericptr_t drycnt) { if ((levl[x][y].typ != POOL) && @@ -1182,11 +1191,11 @@ unflood_space(coordxy x, coordxy y, genericptr_t drycnt) (* (int*)drycnt)++; } -static void +staticfn void seffect_enchant_armor(struct obj **sobjp) { struct obj *sobj = *sobjp; - register schar s; + schar s; boolean special_armor; boolean same_color; boolean draconic = (uarmc && Is_dragon_scales(uarmc)); @@ -1398,12 +1407,11 @@ seffect_enchant_armor(struct obj **sobjp) Blind ? "again" : "unexpectedly"); } -static void +staticfn void seffect_destroy_armor(struct obj **sobjp) { struct obj *sobj = *sobjp; struct obj *otmp = some_armor(&gy.youmonst); - boolean sblessed = sobj->blessed; boolean scursed = sobj->cursed; boolean confused = (Confusion != 0); boolean old_erodeproof, new_erodeproof; @@ -1429,13 +1437,22 @@ seffect_destroy_armor(struct obj **sobjp) return; } if (!scursed || !otmp || !otmp->cursed) { - /* player is prompted to choose what to destroy only when the scroll is - * blessed and they are actually wearing armor */ - boolean gets_choice = (sblessed && otmp); + boolean gets_choice = (otmp && sobj && sobj->blessed + && count_worn_armor() > 1); + if (gets_choice) { - pline("This is a scroll of destroy armor."); + 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, gets_choice)) { + + if (!destroy_arm(otmp)) { strange_feeling(sobj, "Your skin itches."); *sobjp = 0; /* useup() in strange_feeling() */ exercise(A_STR, FALSE); @@ -1453,7 +1470,7 @@ seffect_destroy_armor(struct obj **sobjp) } } -static void +staticfn void seffect_confuse_monster(struct obj **sobjp) { struct obj *sobj = *sobjp; @@ -1461,6 +1478,7 @@ seffect_confuse_monster(struct obj **sobjp) scursed = sobj->cursed, confused = (Confusion != 0), altfeedback = (Blind || Invisible); + const char *const hands = makeplural(body_part(HAND)); if (gy.youmonst.data->mlet != S_HUMAN || scursed) { if (!HConfusion) @@ -1468,7 +1486,7 @@ seffect_confuse_monster(struct obj **sobjp) make_confused(HConfusion + rnd(100), FALSE); } else if (confused) { if (!sblessed) { - Your("%s begin to %s%s.", makeplural(body_part(HAND)), + Your("%s begin to %s%s.", hands, altfeedback ? "tingle" : "glow ", altfeedback ? "" : hcolor(NH_PURPLE)); make_confused(HConfusion + rnd(100), FALSE); @@ -1479,23 +1497,26 @@ seffect_confuse_monster(struct obj **sobjp) make_confused(0L, TRUE); } } else { - int incr = 0; + /* scroll vs spell */ + int incr = (sobj->oclass == SCROLL_CLASS) ? 3 : 0; if (!sblessed) { - Your("%s%s %s%s.", makeplural(body_part(HAND)), - altfeedback ? "" : " begin to glow", - altfeedback ? (const char *) "tingle" : hcolor(NH_RED), - u.umconf ? " even more" : ""); - incr = rnd(2); + if (altfeedback) + Your("%s tingle%s.", hands, u.umconf ? " even more" : ""); + else if (!u.umconf) + Your("%s begin to glow %s.", hands, hcolor(NH_RED)); + else + pline_The("%s glow of your %s intensifies.", hcolor(NH_RED), + hands); + incr += rnd(2); } else { if (altfeedback) - Your("%s tingle %s sharply.", makeplural(body_part(HAND)), + Your("%s tingle %s sharply.", hands, u.umconf ? "even more" : "very"); else - Your("%s glow %s brilliant %s.", - makeplural(body_part(HAND)), + Your("%s glow %s brilliant %s.", hands, u.umconf ? "an even more" : "a", hcolor(NH_RED)); - incr = rn1(8, 2); + incr += rn1(8, 2); } /* after a while, repeated uses become less effective */ if (u.umconf >= 40) @@ -1504,15 +1525,15 @@ seffect_confuse_monster(struct obj **sobjp) } } -static void +staticfn void seffect_scare_monster(struct obj **sobjp) { struct obj *sobj = *sobjp; int otyp = sobj->otyp; boolean scursed = sobj->cursed; boolean confused = (Confusion != 0); - register int ct = 0; - register struct monst *mtmp; + int ct = 0; + struct monst *mtmp; for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) @@ -1540,7 +1561,7 @@ seffect_scare_monster(struct obj **sobjp) } } -static void +staticfn void seffect_remove_curse(struct obj **sobjp) { struct obj *sobj = *sobjp; /* scroll or fake spellbook */ @@ -1548,7 +1569,7 @@ seffect_remove_curse(struct obj **sobjp) boolean sblessed = sobj->blessed; boolean scursed = sobj->cursed; boolean confused = (Confusion != 0); - register struct obj *obj, *nxto; + struct obj *obj, *nxto; long wornmask; You_feel(!Hallucination @@ -1602,6 +1623,8 @@ seffect_remove_curse(struct obj **sobjp) } } if (sblessed || wornmask || undroppable(obj) + /* this treats an in-use leash as a worn item but does not + do the same for lit lamp/candle [seems inconsistent] */ || (obj->otyp == LEASH && obj->leashmon)) { /* water price varies by curse/bless status */ boolean shop_h2o = (obj->unpaid && obj->otyp == POT_WATER); @@ -1657,7 +1680,7 @@ seffect_remove_curse(struct obj **sobjp) update_inventory(); } -static void +staticfn void seffect_create_monster(struct obj **sobjp) { struct obj *sobj = *sobjp; @@ -1676,7 +1699,7 @@ seffect_create_monster(struct obj **sobjp) */ } -static void +staticfn void seffect_enchant_weapon(struct obj **sobjp) { struct obj *sobj = *sobjp; @@ -1728,7 +1751,7 @@ seffect_enchant_weapon(struct obj **sobjp) cap_spe(uwep); } -static void +staticfn void seffect_taming(struct obj **sobjp) { struct obj *sobj = *sobjp; @@ -1771,7 +1794,7 @@ seffect_taming(struct obj **sobjp) } } -static void +staticfn void seffect_genocide(struct obj **sobjp) { struct obj *sobj = *sobjp; @@ -1790,7 +1813,7 @@ seffect_genocide(struct obj **sobjp) do_genocide((!scursed) | (2 * !!Confusion)); } -static void +staticfn void seffect_light(struct obj **sobjp) { struct obj *sobj = *sobjp; @@ -1809,7 +1832,7 @@ seffect_light(struct obj **sobjp) } else { int pm = scursed ? PM_BLACK_LIGHT : PM_YELLOW_LIGHT; - if ((gm.mvitals[pm].mvflags & G_GONE)) { + if ((svm.mvitals[pm].mvflags & G_GONE)) { pline("Tiny lights sparkle in the air momentarily."); } else { /* surround with cancelled tame lights which won't explode */ @@ -1820,13 +1843,14 @@ seffect_light(struct obj **sobjp) for (i = 0; i < numlights; ++i) { mon = makemon(&mons[pm], u.ux, u.uy, MM_EDOG | NO_MINVENT | MM_NOMSG); - initedog(mon); - u.uconduct.pets++; - mon->msleeping = 0; - mon->mcan = TRUE; - if (canspotmon(mon)) - sawlights = TRUE; - newsym(mon->mx, mon->my); + if (mon) { + initedog(mon); + mon->msleeping = 0; + mon->mcan = TRUE; + if (canspotmon(mon)) + sawlights = TRUE; + newsym(mon->mx, mon->my); + } } if (sawlights) { pline("Lights appear all around you!"); @@ -1836,7 +1860,7 @@ seffect_light(struct obj **sobjp) } } -static void +staticfn void seffect_charging(struct obj **sobjp) { struct obj *sobj = *sobjp; @@ -1860,7 +1884,7 @@ seffect_charging(struct obj **sobjp) else u.uen = u.uenmax; /* otherwise restore current to max */ } - gc.context.botl = 1; + disp.botl = TRUE; return; } /* known = TRUE; -- handled inline here */ @@ -1878,7 +1902,7 @@ seffect_charging(struct obj **sobjp) recharge(otmp, scursed ? -1 : sblessed ? 1 : 0); } -static void +staticfn void seffect_amnesia(struct obj **sobjp) { struct obj *sobj = *sobjp; @@ -1888,9 +1912,9 @@ seffect_amnesia(struct obj **sobjp) forget((!sblessed ? ALL_SPELLS : 0)); if (Hallucination) /* Ommmmmm! */ Your("mind releases itself from mundane concerns."); - else if (!strncmpi(gp.plname, "Maud", 4)) - pline( - "As your mind turns inward on itself, you forget everything else."); + else if (!strncmpi(svp.plname, "Maud", 4)) + pline("As your mind turns inward on itself," + " you forget everything else."); else if (rn2(2)) pline("Who was that Maud person anyway?"); else @@ -1898,7 +1922,7 @@ seffect_amnesia(struct obj **sobjp) exercise(A_WIS, FALSE); } -static void +staticfn void seffect_fire(struct obj **sobjp) { struct obj *sobj = *sobjp; @@ -1932,6 +1956,7 @@ seffect_fire(struct obj **sobjp) You_feel("a pleasant warmth in your %s.", makeplural(body_part(HAND))); } else { + monstunseesu(M_SEEN_FIRE); pline_The("scroll catches fire and you burn your %s.", makeplural(body_part(HAND))); losehp(1, "scroll of fire", KILLED_BY_AN); @@ -1966,7 +1991,7 @@ seffect_fire(struct obj **sobjp) #undef ZT_SPELL_O_FIRE } -static void +staticfn void seffect_earth(struct obj **sobjp) { struct obj *sobj = *sobjp; @@ -2015,7 +2040,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 += @@ -2031,7 +2056,7 @@ seffect_earth(struct obj **sobjp) } } -static void +staticfn void seffect_punishment(struct obj **sobjp) { struct obj *sobj = *sobjp; @@ -2046,7 +2071,7 @@ seffect_punishment(struct obj **sobjp) punish(sobj); } -static void +staticfn void seffect_stinking_cloud(struct obj **sobjp) { struct obj *sobj = *sobjp; @@ -2060,7 +2085,7 @@ seffect_stinking_cloud(struct obj **sobjp) do_stinking_cloud(sobj, already_known); } -static void +staticfn void seffect_water(struct obj **sobjp) { struct obj *sobj = *sobjp; @@ -2096,7 +2121,7 @@ seffect_water(struct obj **sobjp) } } -static void +staticfn void seffect_blank_paper(struct obj **sobjp UNUSED) { if (Blind) @@ -2109,7 +2134,7 @@ seffect_blank_paper(struct obj **sobjp UNUSED) gk.known = TRUE; } -static void +staticfn void seffect_teleportation(struct obj **sobjp) { struct obj *sobj = *sobjp; @@ -2129,7 +2154,7 @@ seffect_teleportation(struct obj **sobjp) } } -static void +staticfn void seffect_gold_detection(struct obj **sobjp) { struct obj *sobj = *sobjp; @@ -2148,7 +2173,7 @@ seffect_gold_detection(struct obj **sobjp) *sobjp = 0; /* failure: strange_feeling() -> useup() */ } -static void +staticfn void seffect_food_detection(struct obj **sobjp) { struct obj *sobj = *sobjp; @@ -2157,7 +2182,7 @@ seffect_food_detection(struct obj **sobjp) *sobjp = 0; /* nothing detected: strange_feeling -> useup */ } -static void +staticfn void seffect_identify(struct obj **sobjp) { struct obj *sobj = *sobjp; @@ -2206,7 +2231,7 @@ seffect_identify(struct obj **sobjp) } } -static void +staticfn void seffect_magic_mapping(struct obj **sobjp) { struct obj *sobj = *sobjp; @@ -2231,8 +2256,9 @@ 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]); + } /* do_mapping() already reveals secret passages */ } gk.known = TRUE; @@ -2248,7 +2274,9 @@ seffect_magic_mapping(struct obj **sobjp) cval = (scursed && !confused); if (cval) HConfusion = 1; /* to screw up map */ + notice_mon_off(); do_mapping(); + notice_mon_on(); if (cval) { HConfusion = 0; /* restore */ pline("Unfortunately, you can't grasp the details."); @@ -2256,7 +2284,7 @@ seffect_magic_mapping(struct obj **sobjp) } #ifdef MAIL_STRUCTURES -static void +staticfn void seffect_mail(struct obj **sobjp) { struct obj *sobj = *sobjp; @@ -2294,7 +2322,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; @@ -2395,7 +2424,11 @@ seffects(struct obj *sobj) /* sobj - scroll or fake spellbook for spell */ } void -drop_boulder_on_player(boolean confused, boolean helmet_protects, boolean byu, boolean skip_uswallow) +drop_boulder_on_player( + boolean confused, + boolean helmet_protects, + boolean byu, + boolean skip_uswallow) { int dmg; struct obj *otmp2; @@ -2419,7 +2452,7 @@ drop_boulder_on_player(boolean confused, boolean helmet_protects, boolean byu, b if (otmp2->owt >= 400 && is_brittle(uarmh) && break_glass_obj(uarmh)) { ; - } else if (is_hard(uarmh)) { + } else if (hard_helmet(uarmh)) { if (otmp2->owt >= 400) { if (dmg > 2) dmg -= 2; @@ -2430,7 +2463,7 @@ drop_boulder_on_player(boolean confused, boolean helmet_protects, boolean byu, b dmg = 2; pline("Fortunately, you are wearing a hard helmet."); } - } else if (Verbose(3, drop_boulder_on_player)) { + } else if (flags.verbose) { pline("%s does not protect you.", Yname2(uarmh)); } } @@ -2451,8 +2484,8 @@ drop_boulder_on_player(boolean confused, boolean helmet_protects, boolean byu, b boolean drop_boulder_on_monster(coordxy x, coordxy y, boolean confused, boolean byu) { - register struct obj *otmp2; - register struct monst *mtmp; + struct obj *otmp2; + struct monst *mtmp; /* Make the object(s) */ otmp2 = mksobj(confused ? ROCK : BOULDER, FALSE, FALSE); @@ -2482,7 +2515,7 @@ drop_boulder_on_monster(coordxy x, coordxy y, boolean confused, boolean byu) if (otmp2->owt >= 400 && is_brittle(helmet) && break_glass_obj(helmet)) { ; - } else if (is_hard(helmet)) { + } else if (hard_helmet(helmet)) { if (otmp2->owt >= 400) { if (mdmg > 2) mdmg -= 2; @@ -2594,7 +2627,7 @@ static struct litmon *gremlins = 0; /* * Low-level lit-field update routine. */ -static void +staticfn void set_lit(coordxy x, coordxy y, genericptr_t val) { struct monst *mtmp; @@ -2619,9 +2652,10 @@ 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)); char is_lit = 0; /* value is irrelevant but assign something anyway; its * address is used as a 'not null' flag for set_lit() */ int radius; @@ -2637,7 +2671,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); @@ -2668,7 +2703,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 */ @@ -2685,12 +2721,13 @@ litroom( pline("%s shines briefly.", Monnam(u.ustuck)); else pline("%s glistens.", Monnam(u.ustuck)); - } else if (!Blind) - pline("A lit field surrounds you!"); + } else if (!Blind) { + pline("A lit field %ssurrounds you!", no_op ? "briefly " : ""); + } } /* No-op when swallowed or in water */ - if (u.uswallow || Underwater || Is_waterlevel(&u.uz)) + if (no_op) return; /* * If we are darkening the room and the hero is punished but not @@ -2700,7 +2737,12 @@ litroom( if (Punished && !on && !Blind) move_bc(1, 0, uball->ox, uball->oy, uchain->ox, uchain->oy); - if(obj && obj->oclass == SCROLL_CLASS) { + if (is_art(obj, ART_SUNSWORD)) { + /* Sunsword's #invoke power directed up or down lights hero's spot + (do_clear_area() rejects radius 0 so call set_lit() directly) */ + set_lit(u.ux, u.uy, (genericptr_t) &is_lit); + } + else if (obj && obj->oclass == SCROLL_CLASS) { /* blessed scroll lights up entire level */ if(blessed_effect) { int x, y; @@ -2758,7 +2800,7 @@ litroom( return; } -static void +staticfn void do_class_genocide(void) { int i, j, immunecnt, gonecnt, goodcnt, class, feel_dead = 0; @@ -2767,15 +2809,18 @@ do_class_genocide(void) boolean gameover = FALSE; /* true iff killed self */ buf[0] = '\0'; /* for EDIT_GETLIN */ - for (j = 0;; j++) { + for (j = 0; ; j++) { if (j >= 5) { pline1(thats_enough_tries); return; } Strcpy(promptbuf, "What class of monsters do you want to genocide?"); - if (iflags.cmdassist && j > 0) - Strcat(promptbuf, - " [enter the symbol or name representing a class]"); + if (j > 0) + Snprintf(eos(promptbuf), sizeof promptbuf - strlen(promptbuf), + " [enter %s]", + iflags.cmdassist + ? "the symbol or name representing a class, or '?'" + : "'?' to see previous genocides"); getlin(promptbuf, buf); (void) mungspaces(buf); /* avoid 'that does not represent any monster' for empty input */ @@ -2795,6 +2840,13 @@ do_class_genocide(void) "declined to perform class genocide"); return; } + /* "?" runs #genocided to show existing genocides, then re-prompts; + accept "'?'" too because the prompt's hint shows it that way */ + if (!strcmp(buf, "?") || !strcmp(buf, "'?'")) { + list_genocided('g', FALSE); + --j; /* don't count this iteration as one of the tries */ + continue; + } class = name_to_monclass(buf, (int *) 0); if (class == 0 && (i = name_to_mon(buf, (int *) 0)) != NON_PM) @@ -2804,7 +2856,7 @@ do_class_genocide(void) if (mons[i].mlet == class) { if (!(mons[i].geno & G_GENO)) immunecnt++; - else if (gm.mvitals[i].mvflags & G_GENOD) + else if (svm.mvitals[i].mvflags & G_GENOD) gonecnt++; else goodcnt++; @@ -2817,7 +2869,7 @@ do_class_genocide(void) else if (immunecnt || class == S_invisible) You("aren't permitted to genocide such monsters."); else if (wizard && buf[0] == '*') { - register struct monst *mtmp, *mtmp2; + struct monst *mtmp, *mtmp2; gonecnt = 0; for (mtmp = fmon; mtmp; mtmp = mtmp2) { @@ -2845,7 +2897,7 @@ do_class_genocide(void) */ if (Your_Own_Role(i) || Your_Own_Race(i) || ((mons[i].geno & G_GENO) - && !(gm.mvitals[i].mvflags & G_GENOD))) { + && !(svm.mvitals[i].mvflags & G_GENOD))) { /* This check must be first since player monsters might * have G_GENOD or !G_GENO. */ @@ -2859,7 +2911,7 @@ do_class_genocide(void) def_monsyms[class].sym); } - gm.mvitals[i].mvflags |= (G_GENOD | G_NOCORPSE); + svm.mvitals[i].mvflags |= (G_GENOD | G_NOCORPSE); kill_genocided_monsters(); update_inventory(); /* eggs & tins */ pline("Wiped out all %s.", nam); @@ -2896,7 +2948,7 @@ do_class_genocide(void) make_slimed(0L, "The slime that covers you disappears!"); } - } else if (gm.mvitals[i].mvflags & G_GENOD) { + } else if (svm.mvitals[i].mvflags & G_GENOD) { if (!gameover) pline("%s are already nonexistent.", upstart(nam)); } else if (!gameover) { @@ -2928,8 +2980,8 @@ do_class_genocide(void) } } if (gameover || u.uhp == -1) { - gk.killer.format = KILLED_BY_AN; - Strcpy(gk.killer.name, "scroll of genocide"); + svk.killer.format = KILLED_BY_AN; + Strcpy(svk.killer.name, "scroll of genocide"); if (gameover) done(GENOCIDED); } @@ -2969,9 +3021,14 @@ do_genocide( pline1(thats_enough_tries); return; } - Strcpy(promptbuf, "What type of monster do you want to genocide?"); - if (iflags.cmdassist && i > 0) - Strcat(promptbuf, " [enter the name of a type of monster]"); + Strcpy(promptbuf, + "What type of monster do you want to genocide?"); + if (i > 0) + Snprintf(eos(promptbuf), sizeof promptbuf - strlen(promptbuf), + " [enter %s]", + iflags.cmdassist + ? "the name of a type of monster, or '?'" + : "'?' to see previous genocides"); getlin(promptbuf, buf); (void) mungspaces(buf); /* avoid 'such creatures do not exist' for empty input */ @@ -2993,9 +3050,15 @@ do_genocide( livelog_printf(LL_GENOCIDE, "declined to perform genocide"); return; } + /* "?" or "'?'" runs #genocided to show existing genocides */ + if (!strcmp(buf, "?") || !strcmp(buf, "'?'")) { + list_genocided('g', FALSE); + --i; /* don't count this iteration as one of the tries */ + continue; + } mndx = name_to_mon(buf, (int *) 0); - if (mndx == NON_PM || (gm.mvitals[mndx].mvflags & G_GENOD)) { + if (mndx == NON_PM || (svm.mvitals[mndx].mvflags & G_GENOD)) { pline("Such creatures %s exist in this world.", (mndx == NON_PM) ? "do not" : "no longer"); continue; @@ -3023,9 +3086,11 @@ do_genocide( * circumstances. Who's speaking? Divine pronouncements * aren't supposed to be hampered by deafness.... */ - if (Verbose(3, do_genocide)) - pline("A thunderous voice booms through the caverns:"); + if (flags.verbose) + pline("A thunderous voice booms" + " through the caverns:"); SetVoice((struct monst *) 0, 0, 80, voice_deity); + /* FIXME? shouldn't this override deafness? */ verbalize("No, mortal! That will not be done."); } continue; @@ -3041,7 +3106,8 @@ do_genocide( which = "all "; if (Hallucination) { if (Upolyd) - Strcpy(buf, pmname(gy.youmonst.data, flags.female ? FEMALE : MALE)); + Strcpy(buf, pmname(gy.youmonst.data, + flags.female ? FEMALE : MALE)); else { Strcpy(buf, (flags.female && gu.urole.name.f) ? gu.urole.name.f : gu.urole.name.m); @@ -3061,7 +3127,7 @@ do_genocide( livelog_printf(LL_GENOCIDE, "genocided %s", makeplural(buf)); /* setting no-corpse affects wishing and random tin generation */ - gm.mvitals[mndx].mvflags |= (G_GENOD | G_NOCORPSE); + svm.mvitals[mndx].mvflags |= (G_GENOD | G_NOCORPSE); pline("Wiped out %s%s.", which, (*which != 'a') ? buf : makeplural(buf)); @@ -3071,22 +3137,22 @@ do_genocide( if (killplayer) { u.uhp = -1; if (how & PLAYER) { - gk.killer.format = KILLED_BY; - Strcpy(gk.killer.name, "genocidal confusion"); + svk.killer.format = KILLED_BY; + Strcpy(svk.killer.name, "genocidal confusion"); } else if (how & ONTHRONE) { /* player selected while on a throne */ - gk.killer.format = KILLED_BY_AN; - Strcpy(gk.killer.name, "imperious order"); + svk.killer.format = KILLED_BY_AN; + Strcpy(svk.killer.name, "imperious order"); } else { /* selected player deliberately, not confused */ - gk.killer.format = KILLED_BY_AN; - Strcpy(gk.killer.name, "scroll of genocide"); + svk.killer.format = KILLED_BY_AN; + Strcpy(svk.killer.name, "scroll of genocide"); } /* Polymorphed characters will die as soon as they're rehumanized. */ /* KMH -- Unchanging prevents rehumanization */ if (Upolyd && ptr != gy.youmonst.data) { - delayed_killer(POLYMORPH, gk.killer.format, gk.killer.name); + delayed_killer(POLYMORPH, svk.killer.format, svk.killer.name); You_feel("%s inside.", udeadinside()); } else done(GENOCIDED); @@ -3099,12 +3165,12 @@ do_genocide( int cnt = 0, census = monster_census(FALSE); if (!(mons[mndx].geno & G_UNIQ) - && !(gm.mvitals[mndx].mvflags & (G_GENOD | G_EXTINCT))) + && !(svm.mvitals[mndx].mvflags & (G_GENOD | G_EXTINCT))) for (i = rn1(3, 4); i > 0; i--) { if (!makemon(ptr, u.ux, u.uy, NO_MINVENT | MM_NOMSG)) break; /* couldn't make one */ ++cnt; - if (gm.mvitals[mndx].mvflags & G_EXTINCT) + if (svm.mvitals[mndx].mvflags & G_EXTINCT) break; /* just made last one */ } if (cnt) { @@ -3119,17 +3185,21 @@ do_genocide( } void -punish(struct obj* sobj) +punish(struct obj *sobj) { + /* angrygods() calls this with NULL sobj arg */ struct obj *reuse_ball = (sobj && sobj->otyp == HEAVY_IRON_BALL) ? sobj : (struct obj *) 0; + /* analyzer doesn't know that the one caller that passes a NULL + * sobj (angrygods) checks !Punished first, so add a guard */ + int cursed_levy = (sobj && sobj->cursed) ? 1 : 0; /* KMH -- Punishment is still okay when you are riding */ if (!reuse_ball) You("are being punished for your misbehavior!"); if (Punished) { Your("iron ball gets heavier."); - uball->owt += IRON_BALL_W_INCR * (1 + sobj->cursed); + uball->owt += IRON_BALL_W_INCR * (1 + cursed_levy); return; } if (amorphous(gy.youmonst.data) || is_whirly(gy.youmonst.data) @@ -3187,8 +3257,8 @@ unpunish(void) } } -/* Prompt the player to create a stinking cloud and then create it if they give - * a location. */ +/* prompt the player to create a stinking cloud and then create it if they + give a location */ xint8 do_stinking_cloud(struct obj *sobj, boolean mention_stinking) { @@ -3259,7 +3329,7 @@ cant_revive( return FALSE; } -static boolean +staticfn boolean create_particular_parse( char *str, struct _create_particular_data *d) @@ -3352,11 +3422,11 @@ create_particular_parse( } else { /* no explicit gender term was specified */ d->fem = gender_name_var; } - if (d->which >= LOW_PM) + if (ismnum(d->which)) return TRUE; /* got one */ d->monclass = name_to_monclass(bufp, &d->which); - if (d->which >= LOW_PM) { + if (ismnum(d->which)) { d->monclass = MAXMCLASSES; /* matters below */ return TRUE; } else if (d->monclass == S_invisible) { /* not an actual monster class */ @@ -3374,7 +3444,7 @@ create_particular_parse( return FALSE; } -static struct monst * +staticfn struct monst * create_particular_creation( struct _create_particular_data *d) { @@ -3436,6 +3506,9 @@ create_particular_creation( } if (d->sleeping) mmflags |= MM_ASLEEP; + if (d->invisible) + mmflags |= MM_MINVIS; + mtmp = makemon(whichpm, u.ux, u.uy, mmflags); if (!mtmp) { /* quit trying if creation failed and is going to repeat */ @@ -3446,7 +3519,7 @@ create_particular_creation( } mx = mtmp->mx, my = mtmp->my; if (d->maketame) { - (void) tamedog(mtmp, (struct obj *) 0, FALSE); + (void) tamedog(mtmp, (struct obj *) 0, FALSE, FALSE); } else if (d->makepeaceful || d->makehostile) { mtmp->mtame = 0; /* sanity precaution */ mtmp->mpeaceful = d->makepeaceful ? 1 : 0; @@ -3461,34 +3534,19 @@ create_particular_creation( put_saddle_on_mon(otmp, mtmp); } - if (d->invisible) { - mon_set_minvis(mtmp); - if (does_block(mx, my, &levl[mx][my])) - block_point(mx, my); - else - unblock_point(mx, my); - } - if (d->hidden + if (d->hidden && ((is_hider(mtmp->data) && mtmp->data->mlet != S_MIMIC) || (hides_under(mtmp->data) && concealed_spot(mx, my)) || (mtmp->data->mlet == S_EEL && is_pool(mx, my)))) mtmp->mundetected = 1; if (d->sleeping) mtmp->msleeping = 1; - /* iff asking for 'hidden', show location of every created monster + /* 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 && !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); - } /* in case we got a doppelganger instead of what was asked for, make it start out looking like what was asked for */ if (mtmp->cham != NON_PM && firstchoice != NON_PM @@ -3633,11 +3691,11 @@ find_boss(boolean *lair) boolean can_magic_map(void) { - if (gl.level.flags.nommap == MAPPABLE_ALWAYS) + if (svl.level.flags.nommap == MAPPABLE_ALWAYS) return TRUE; - else if (gl.level.flags.nommap == MAPPABLE_NEVER) + else if (svl.level.flags.nommap == MAPPABLE_NEVER) return FALSE; - else if (gl.level.flags.nommap == MAPPABLE_BOSSBLOCKED) { + else if (svl.level.flags.nommap == MAPPABLE_BOSSBLOCKED) { /* Important to note that all this behavior only triggers on levels * where the designer set this flag. If it's regular always-mappable or * never-mappable, this doesn't come into play. */ @@ -3657,7 +3715,7 @@ can_magic_map(void) impossible("nommap set to BOSSBLOCKED on non-boss level"); return TRUE; } - if (gm.mvitals[boss_mndx].died == 0) + if (svm.mvitals[boss_mndx].died == 0) return FALSE; for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (mtmp->data == &mons[boss_mndx]) /* peaceful is irrelevant */ diff --git a/src/rect.c b/src/rect.c index eaa133f2ef..3af8f42996 100644 --- a/src/rect.c +++ b/src/rect.c @@ -6,7 +6,7 @@ int get_rect_ind(NhRect *); -static boolean intersect(NhRect *, NhRect *, NhRect *); +staticfn boolean intersect(NhRect *, NhRect *, NhRect *); /* * In this file, we will handle the various rectangle functions we @@ -52,17 +52,18 @@ free_rect(void) { if (rect) free(rect); + rect = 0; n_rects = rect_cnt = 0; } /* Find and return the index of one precise NhRect, or -1 if it doesn't exist * in the rect array. */ int -get_rect_ind(NhRect* r) +get_rect_ind(NhRect *r) { - register NhRect *rectp; - register int lx, ly, hx, hy; - register int i; + NhRect *rectp; + int lx, ly, hx, hy; + int i; lx = r->lx; ly = r->ly; @@ -79,11 +80,11 @@ get_rect_ind(NhRect* r) * the given rectangle, and return it, or NULL if no such rectangle exists. */ NhRect * -get_rect(NhRect* r) +get_rect(NhRect *r) { - register NhRect *rectp; - register int lx, ly, hx, hy; - register int i; + NhRect *rectp; + int lx, ly, hx, hy; + int i; lx = r->lx; ly = r->ly; @@ -108,8 +109,9 @@ rnd_rect(void) * If they don't intersect at all, return FALSE. * If they do, set r3 to be the intersection, and return TRUE. */ -static boolean -intersect(NhRect* r1, NhRect* r2, NhRect* r3) + +staticfn boolean +intersect(NhRect *r1, NhRect *r2, NhRect *r3) { if (r2->lx > r1->hx || r2->ly > r1->hy || r2->hx < r1->lx || r2->hy < r1->ly) @@ -140,7 +142,7 @@ rect_bounds(NhRect r1, NhRect r2, NhRect *r3) */ void -remove_rect(NhRect* r) +remove_rect(NhRect *r) { int ind; @@ -151,7 +153,7 @@ remove_rect(NhRect* r) /* Add the given rectangle to the rect array. */ void -add_rect(NhRect* r) +add_rect(NhRect *r) { if (rect_cnt >= n_rects) { impossible("n_rects may be too small."); @@ -175,7 +177,7 @@ add_rect(NhRect* r) * anywhere that isn't directly in line with r2. */ void -split_rects(NhRect* r1, NhRect* r2) +split_rects(NhRect *r1, NhRect *r2) { NhRect r, old_r; int i; diff --git a/src/region.c b/src/region.c index 7da307f51f..a9219156ae 100644 --- a/src/region.c +++ b/src/region.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 region.c $NHDT-Date: 1683832331 2023/05/11 19:12:11 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.81 $ */ +/* NetHack 3.7 region.c $NHDT-Date: 1727251269 2024/09/25 08:01:09 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.104 $ */ /* Copyright (c) 1996 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -37,8 +37,9 @@ boolean enter_force_field(genericptr,genericptr); NhRegion *create_force_field(coordxy,coordxy,int,long); #endif -static void reset_region_mids(NhRegion *); -static boolean is_hero_inside_gas_cloud(void); +staticfn void reset_region_mids(NhRegion *); +staticfn boolean is_hero_inside_gas_cloud(void); +staticfn void make_gas_cloud(NhRegion *, int, boolean) NONNULLARG1; static const callback_proc callbacks[] = { #define INSIDE_GAS_CLOUD 0 @@ -80,7 +81,7 @@ create_region(NhRect *rects, int nrect) NhRegion *reg; reg = (NhRegion *) alloc(sizeof(NhRegion)); - (void) memset((genericptr_t)reg, 0, sizeof(NhRegion)); + (void) memset((genericptr_t) reg, 0, sizeof(NhRegion)); /* Determines bounding box */ if (nrect > 0) { reg->bounding_box = rects[0]; @@ -161,6 +162,14 @@ add_mon_to_reg(NhRegion *reg, struct monst *mon) int i; unsigned *tmp_m; + /* if this is a long worm, it might already be present in the region; + only include it once no matter how segments the region contains */ + if (mon_in_region(reg, mon)) { + if (mon->data != &mons[PM_LONG_WORM]) + impossible("add_mon_to_reg: %s [#%u] already in region.", + m_monnam(mon), mon->m_id); + return; + } if (reg->max_monst <= reg->n_monst) { tmp_m = (unsigned *) alloc(sizeof (unsigned) * (reg->max_monst + MONST_INC)); @@ -181,7 +190,7 @@ add_mon_to_reg(NhRegion *reg, struct monst *mon) void remove_mon_from_reg(NhRegion *reg, struct monst *mon) { - register int i; + int i; for (i = 0; i < reg->n_monst; i++) if (reg->monsters[i] == mon->m_id) { @@ -274,7 +283,7 @@ add_region(NhRegion *reg) NhRegion **tmp_reg; int i, j; - if (gm.max_regions <= gn.n_regions) { + if (gm.max_regions <= svn.n_regions) { tmp_reg = gr.regions; gr.regions = (NhRegion **) alloc((gm.max_regions + 10) * sizeof (NhRegion *)); @@ -285,18 +294,32 @@ add_region(NhRegion *reg) } gm.max_regions += 10; } - gr.regions[gn.n_regions] = reg; - gn.n_regions++; + gr.regions[svn.n_regions] = reg; + svn.n_regions++; /* Check for monsters inside the region */ for (i = reg->bounding_box.lx; i <= reg->bounding_box.hx; i++) for (j = reg->bounding_box.ly; j <= reg->bounding_box.hy; j++) { - boolean is_inside = inside_region(reg, i, j); + struct monst *mtmp; + boolean is_inside = FALSE; /* Some regions can cross the level boundaries */ if (!isok(i, j)) continue; - if (is_inside && MON_AT(i, j)) - add_mon_to_reg(reg, gl.level.monsters[i][j]); + if (inside_region(reg, i, j)) { + is_inside = TRUE; + /* if there's a monster here, add it to the region */ + if ((mtmp = m_at(i, j)) != 0 +#if 0 + /* leave this bit (to exclude long worm tails) out; + assume that worms use "cutaneous respiration" (they + breath through their skin rather than nose/gills/&c) + so their tails are susceptible to poison gas */ + && mtmp->mx == i && mtmp->my == j +#endif + ) { + add_mon_to_reg(reg, mtmp); + } + } if (reg->visible) { if (is_inside) block_point(i, j); @@ -317,46 +340,60 @@ add_region(NhRegion *reg) void remove_region(NhRegion *reg) { - register int i, x, y; + int i, x, y; - for (i = 0; i < gn.n_regions; i++) + for (i = 0; i < svn.n_regions; i++) if (gr.regions[i] == reg) break; - if (i == gn.n_regions) + if (i == svn.n_regions) return; /* remove region before potential newsym() calls, but don't free it yet */ - if (--gn.n_regions != i) - gr.regions[i] = gr.regions[gn.n_regions]; - gr.regions[gn.n_regions] = (NhRegion *) 0; + if (--svn.n_regions != i) + gr.regions[i] = gr.regions[svn.n_regions]; + gr.regions[svn.n_regions] = (NhRegion *) 0; /* Update screen if necessary */ reg->ttl = -2L; /* for visible_region_at */ - if (reg->visible) - 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)) { - /*if (!sobj_at(BOULDER, x, y)) - unblock_point(x, y);*/ - if (cansee(x, y)) - newsym(x, y); - } - + 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)) { + if (pass == 1) { + if (!does_block(x, y, &levl[x][y])) + unblock_point(x, y); + } else { /* pass==2 */ + if (cansee(x, y)) + newsym(x, y); + } + } + } + u.uinwater = tmp_uinwater; + } free_region(reg); } /* - * Remove all regions and clear all related data (This must be down - * when changing level, for instance). + * Remove all regions and clear all related data. This must be done + * when changing level, for instance. */ void clear_regions(void) { - register int i; + int i; - for (i = 0; i < gn.n_regions; i++) + for (i = 0; i < svn.n_regions; i++) free_region(gr.regions[i]); - gn.n_regions = 0; + svn.n_regions = 0; if (gm.max_regions > 0) free((genericptr_t) gr.regions); gm.max_regions = 0; @@ -371,7 +408,7 @@ clear_regions(void) void run_regions(void) { - register int i, j, k; + int i, j, k; int f_indx; /* reset some messaging variables */ @@ -380,7 +417,7 @@ run_regions(void) /* End of life ? */ /* Do it backward because the array will be modified */ - for (i = gn.n_regions - 1; i >= 0; i--) { + for (i = svn.n_regions - 1; i >= 0; i--) { if (gr.regions[i]->ttl == 0L) { if ((f_indx = gr.regions[i]->expire_f) == NO_CALLBACK || (*callbacks[f_indx])(gr.regions[i], (genericptr_t) 0)) @@ -389,7 +426,7 @@ run_regions(void) } /* Process remaining regions */ - for (i = 0; i < gn.n_regions; i++) { + for (i = 0; i < svn.n_regions; i++) { /* Make the region age */ if (gr.regions[i]->ttl > 0L) gr.regions[i]->ttl--; @@ -415,12 +452,20 @@ run_regions(void) } } - if (gg.gas_cloud_diss_within) + if (gg.gas_cloud_diss_within) { pline_The("gas cloud around you dissipates."); - if (gg.gas_cloud_diss_seen) - You_see("%s dissipate.", - gg.gas_cloud_diss_seen == 1 - ? "a gas cloud" : "some gas clouds"); + /* normally won't see additional dissipation when within */ + /* FIXME? this assumes that additional dissipation is close by */ + if (u.xray_range <= 1) + gg.gas_cloud_diss_seen = 0; + gg.gas_cloud_diss_within = FALSE; + } + if (gg.gas_cloud_diss_seen) { + You_see("%s gas cloud%s dissipate.", + (gg.gas_cloud_diss_seen == 1) ? "a" : "some", + plur(gg.gas_cloud_diss_seen)); + gg.gas_cloud_diss_seen = 0; + } } /* @@ -432,7 +477,7 @@ in_out_region(coordxy x, coordxy y) int i, f_indx = 0; /* First check if hero can do the move */ - for (i = 0; i < gn.n_regions; i++) { + for (i = 0; i < svn.n_regions; i++) { if (gr.regions[i]->attach_2_u) continue; if (inside_region(gr.regions[i], x, y) @@ -446,7 +491,7 @@ in_out_region(coordxy x, coordxy y) } /* Callbacks for the regions hero does leave */ - for (i = 0; i < gn.n_regions; i++) { + for (i = 0; i < svn.n_regions; i++) { if (gr.regions[i]->attach_2_u) continue; if (hero_inside(gr.regions[i]) @@ -460,7 +505,7 @@ in_out_region(coordxy x, coordxy y) } /* Callbacks for the regions hero does enter */ - for (i = 0; i < gn.n_regions; i++) { + for (i = 0; i < svn.n_regions; i++) { if (gr.regions[i]->attach_2_u) continue; if (!hero_inside(gr.regions[i]) @@ -485,7 +530,7 @@ m_in_out_region(struct monst *mon, coordxy x, coordxy y) int i, f_indx = 0; /* First check if mon can do the move */ - for (i = 0; i < gn.n_regions; i++) { + for (i = 0; i < svn.n_regions; i++) { if (gr.regions[i]->attach_2_m == mon->m_id) continue; if (inside_region(gr.regions[i], x, y) @@ -499,7 +544,7 @@ m_in_out_region(struct monst *mon, coordxy x, coordxy y) } /* Callbacks for the regions mon does leave */ - for (i = 0; i < gn.n_regions; i++) { + for (i = 0; i < svn.n_regions; i++) { if (gr.regions[i]->attach_2_m == mon->m_id) continue; if (mon_in_region(gr.regions[i], mon) @@ -511,7 +556,7 @@ m_in_out_region(struct monst *mon, coordxy x, coordxy y) } /* Callbacks for the regions mon does enter */ - for (i = 0; i < gn.n_regions; i++) { + for (i = 0; i < svn.n_regions; i++) { if (gr.regions[i]->attach_2_m == mon->m_id) continue; if (!mon_in_region(gr.regions[i], mon) @@ -531,9 +576,9 @@ m_in_out_region(struct monst *mon, coordxy x, coordxy y) void update_player_regions(void) { - register int i; + int i; - for (i = 0; i < gn.n_regions; i++) + for (i = 0; i < svn.n_regions; i++) if (!gr.regions[i]->attach_2_u && inside_region(gr.regions[i], u.ux, u.uy)) set_hero_inside(gr.regions[i]); @@ -547,9 +592,9 @@ update_player_regions(void) void update_monster_region(struct monst *mon) { - register int i; + int i; - for (i = 0; i < gn.n_regions; i++) { + for (i = 0; i < svn.n_regions; i++) { if (inside_region(gr.regions[i], mon->mx, mon->my)) { if (!mon_in_region(gr.regions[i], mon)) add_mon_to_reg(gr.regions[i], mon); @@ -572,9 +617,9 @@ void replace_mon_regions(monold, monnew) struct monst *monold, *monnew; { - register int i; + int i; - for (i = 0; i < gn.n_regions; i++) + for (i = 0; i < svn.n_regions; i++) if (mon_in_region(gr.regions[i], monold)) { remove_mon_from_reg(gr.regions[i], monold); add_mon_to_reg(gr.regions[i], monnew); @@ -587,15 +632,79 @@ struct monst *monold, *monnew; void remove_mon_from_regions(struct monst *mon) { - register int i; + int i; - for (i = 0; i < gn.n_regions; i++) + for (i = 0; i < svn.n_regions; i++) if (mon_in_region(gr.regions[i], mon)) remove_mon_from_reg(gr.regions[i], mon); } #endif /*0*/ +/* per-turn damage inflicted by visible region; hides details from caller */ +int +reg_damg(NhRegion *reg) +{ + int damg = (!reg->visible || reg->ttl == -2L) ? 0 : reg->arg.a_int; + + return damg; +} + +/* check whether current level has any visible regions */ +boolean +any_visible_region(void) +{ + int i; + + for (i = 0; i < svn.n_regions; i++) { + if (!gr.regions[i]->visible || gr.regions[i]->ttl == -2L) + continue; + return TRUE; + } + return FALSE; +} + +/* for the wizard mode #timeout command */ +void +visible_region_summary(winid win) +{ + NhRegion *reg; + char buf[BUFSZ], typbuf[QBUFSZ]; + int i, damg, hdr_done = 0; + const char *fldsep = iflags.menu_tab_sep ? "\t" : " "; + + for (i = 0; i < svn.n_regions; i++) { + reg = gr.regions[i]; + if (!reg->visible || reg->ttl == -2L) + continue; + + if (!hdr_done++) { + putstr(win, 0, ""); + putstr(win, 0, "Visible regions"); + } + /* + * TODO? sort the regions by time-to-live or by bounding box. + */ + + /* we display relative time (turns left) rather than absolute + (the turn when region will go away); + since time-to-live has already been decremented, regions + which are due to timeout on the next turn have ttl==0; + adding 1 is intended to make the display be less confusing */ + Sprintf(buf, "%5ld", reg->ttl + 1L); + damg = reg->arg.a_int; + if (damg) + Sprintf(typbuf, "poison gas (%d)", damg); + else + Strcpy(typbuf, "vapor"); + Sprintf(eos(buf), "%s%-16s", fldsep, typbuf); + Sprintf(eos(buf), "%s@[%d,%d..%d,%d]", fldsep, + reg->bounding_box.lx, reg->bounding_box.ly, + reg->bounding_box.hx, reg->bounding_box.hy); + putstr(win, 0, buf); + } +} + /* * Check if a spot is under a visible region (eg: gas cloud). * Returns NULL if not, otherwise returns region. @@ -603,9 +712,9 @@ remove_mon_from_regions(struct monst *mon) NhRegion * visible_region_at(coordxy x, coordxy y) { - register int i; + int i; - for (i = 0; i < gn.n_regions; i++) { + for (i = 0; i < svn.n_regions; i++) { if (!gr.regions[i]->visible || gr.regions[i]->ttl == -2L) continue; if (inside_region(gr.regions[i], x, y)) @@ -634,18 +743,20 @@ save_regions(NHFILE *nhfp) goto skip_lots; if (nhfp->structlevel) { /* timestamp */ - bwrite(nhfp->fd, (genericptr_t) &gm.moves, sizeof (gm.moves)); - bwrite(nhfp->fd, (genericptr_t) &gn.n_regions, sizeof (gn.n_regions)); + bwrite(nhfp->fd, (genericptr_t) &svm.moves, sizeof svm.moves); + bwrite(nhfp->fd, (genericptr_t) &svn.n_regions, sizeof svn.n_regions); } - for (i = 0; i < gn.n_regions; i++) { + 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->bounding_box, + sizeof (NhRect)); bwrite(nhfp->fd, (genericptr_t) &r->nrects, sizeof (short)); } for (j = 0; j < r->nrects; j++) { if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &r->rects[j], sizeof (NhRect)); + bwrite(nhfp->fd, (genericptr_t) &r->rects[j], + sizeof (NhRect)); } if (nhfp->structlevel) bwrite(nhfp->fd, (genericptr_t) &r->attach_2_u, sizeof (boolean)); @@ -712,15 +823,15 @@ rest_regions(NHFILE *nhfp) if (ghostly) tmstamp = 0; else - tmstamp = (gm.moves - tmstamp); + tmstamp = (svm.moves - tmstamp); if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) &gn.n_regions, sizeof (gn.n_regions)); + mread(nhfp->fd, (genericptr_t) &svn.n_regions, sizeof svn.n_regions); - gm.max_regions = gn.n_regions; - if (gn.n_regions > 0) - gr.regions = (NhRegion **) alloc(sizeof (NhRegion *) * gn.n_regions); - for (i = 0; i < gn.n_regions; i++) { + 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)); @@ -802,7 +913,7 @@ rest_regions(NHFILE *nhfp) } /* 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 = gn.n_regions - 1; i >= 0; i--) { + for (i = svn.n_regions - 1; i >= 0; i--) { r = gr.regions[i]; if (r->ttl == 0L) remove_region(r); @@ -826,9 +937,9 @@ region_stats( /* other stats formats take one parameter; this takes two */ Sprintf(hdrbuf, hdrfmt, (long) sizeof (NhRegion), (long) sizeof (NhRect)); - *count = (long) gn.n_regions; /* might be 0 even though max_regions isn't */ + *count = (long) svn.n_regions; /* might be 0 even tho max_regions isn't */ *size = (long) gm.max_regions * (long) sizeof (NhRegion); - for (i = 0; i < gn.n_regions; ++i) { + for (i = 0; i < svn.n_regions; ++i) { rg = gr.regions[i]; *size += (long) rg->nrects * (long) sizeof (NhRect); if (rg->enter_msg) @@ -843,7 +954,7 @@ region_stats( RESTORE_WARNING_FORMAT_NONLITERAL /* update monster IDs for region being loaded from bones; `ghostly' implied */ -static void +staticfn void reset_region_mids(NhRegion *reg) { int i = 0, n = reg->n_monst; @@ -940,7 +1051,7 @@ create_force_field(coordxy x, coordxy y, int radius, long ttl) tmprect.hy--; } ff->ttl = ttl; - if (!gi.in_mklev && !gc.context.mon_moving) + if (!gi.in_mklev && !svc.context.mon_moving) set_heros_fault(ff); /* assume player has created it */ /* ff->can_enter_f = enter_force_field; */ /* ff->can_leave_f = enter_force_field; */ @@ -965,7 +1076,7 @@ boolean expire_gas_cloud(genericptr_t p1, genericptr_t p2 UNUSED) { NhRegion *reg; - int damage; + int damage, pass; coordxy x, y; reg = (NhRegion *) p1; @@ -980,16 +1091,24 @@ expire_gas_cloud(genericptr_t p1, genericptr_t p2 UNUSED) return FALSE; /* THEN return FALSE, means "still there" */ } - /* The cloud no longer blocks vision. */ - 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 (inside_region(reg, x, y)) { - if (!does_block(x, y, &levl[x][y])) - unblock_point(x, y); - if (u_at(x, y)) - gg.gas_cloud_diss_within = TRUE; - if (cansee(x, y)) - gg.gas_cloud_diss_seen++; + /* The cloud no longer blocks vision. cansee() checks shouldn't be made + until all blocked spots have been unblocked, so we need two passes */ + for (pass = 1; pass <= (Blind ? 1 : 2); ++pass) { + 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 (inside_region(reg, x, y)) { + if (pass == 1) { + if (!does_block(x, y, &levl[x][y])) + unblock_point(x, y); + } else { /* pass==2 */ + if (!u.uswallow) { + if (u_at(x, y)) + gg.gas_cloud_diss_within = TRUE; + else if (cansee(x, y)) + gg.gas_cloud_diss_seen++; + } + } + } } } } @@ -1003,6 +1122,7 @@ inside_gas_cloud(genericptr_t p1, genericptr_t p2) { NhRegion *reg = (NhRegion *) p1; struct monst *mtmp = (struct monst *) p2; + struct monst *umon = mtmp ? mtmp : &gy.youmonst; int dam = reg->arg.a_int; /* @@ -1010,6 +1130,10 @@ inside_gas_cloud(genericptr_t p1, genericptr_t p2) * start next to water and spread over it. */ + /* fog clouds maintain gas clouds, even poisonous ones */ + if (reg->ttl < 20 && umon && umon->data == &mons[PM_FOG_CLOUD]) + reg->ttl += 5; + if (dam < 1) return FALSE; /* if no damage then there's nothing to do here... */ @@ -1024,13 +1148,16 @@ inside_gas_cloud(genericptr_t p1, genericptr_t p2) pline("%s is burning your %s!", Something, makeplural(body_part(LUNG))); You("cough and spit blood!"); + wake_nearto(u.ux, u.uy, 2); dam = Maybe_Half_Phys(rnd(dam) + 5); if (Half_gas_damage) /* worn towel */ dam = (dam + 1) / 2; losehp(dam, "gas cloud", KILLED_BY_AN); + monstunseesu(M_SEEN_POISON); return FALSE; } else { You("cough!"); + wake_nearto(u.ux, u.uy, 2); monstseesu(M_SEEN_POISON); return FALSE; } @@ -1038,8 +1165,12 @@ inside_gas_cloud(genericptr_t p1, genericptr_t p2) mtmp = (struct monst *) p2; if (m_poisongas_ok(mtmp) != M_POISONGAS_OK) { - if (cansee(mtmp->mx, mtmp->my)) - pline("%s coughs!", Monnam(mtmp)); + if (!is_silent(mtmp->data)) { + if (cansee(mtmp->mx, mtmp->my) + || (distu(mtmp->mx, mtmp->my) < 8)) + pline("%s coughs!", Monnam(mtmp)); + wake_nearto(mtmp->mx, mtmp->my, 2); + } if (heros_fault(reg)) setmangry(mtmp, TRUE); if (haseyes(mtmp->data) && mtmp->mcansee) { @@ -1063,24 +1194,56 @@ inside_gas_cloud(genericptr_t p1, genericptr_t p2) return FALSE; /* Monster is still alive */ } -static boolean +staticfn boolean is_hero_inside_gas_cloud(void) { int i; - for (i = 0; i < gn.n_regions; i++) - if (hero_inside(gr.regions[i]) && gr.regions[i]->inside_f == INSIDE_GAS_CLOUD) + for (i = 0; i < svn.n_regions; i++) + if (hero_inside(gr.regions[i]) + && gr.regions[i]->inside_f == INSIDE_GAS_CLOUD) return TRUE; return FALSE; } +/* details of gas cloud creation which are common to create_gas_cloud() + and create_gas_cloud_selection() */ +staticfn void +make_gas_cloud( + NhRegion *cloud, + int damage, + boolean inside_cloud) +{ + if (!gi.in_mklev && !svc.context.mon_moving) + set_heros_fault(cloud); /* assume player has created it */ + cloud->inside_f = INSIDE_GAS_CLOUD; + cloud->expire_f = EXPIRE_GAS_CLOUD; + cloud->arg = cg.zeroany; + cloud->arg.a_int = damage; + cloud->visible = TRUE; + cloud->glyph = cmap_to_glyph(damage ? S_poisoncloud : S_cloud); + add_region(cloud); + + if (!gi.in_mklev && !inside_cloud && is_hero_inside_gas_cloud()) { + You("are enveloped in a cloud of %s!", + /* FIXME: "steam" is wrong if this cloud is just the trail of + a fog cloud's movement; changing to "vapor" would handle + that but seems a step backward when it really is steam */ + damage ? "noxious gas" : "steam"); + iflags.last_msg = PLNMSG_ENVELOPED_IN_GAS; + } +} + /* Create a gas cloud which starts at (x,y) and grows outward from it via * breadth-first search. * cloudsize is the number of squares the cloud will attempt to fill. * damage is how much it deals to afflicted creatures. */ #define MAX_CLOUD_SIZE 150 NhRegion * -create_gas_cloud(coordxy x, coordxy y, int cloudsize, int damage) +create_gas_cloud( + coordxy x, coordxy y, + int cloudsize, + int damage) { NhRegion *cloud; int i, j; @@ -1095,6 +1258,13 @@ create_gas_cloud(coordxy x, coordxy y, int cloudsize, int damage) int newidx = 1; /* initial spot is already taken */ boolean inside_cloud = is_hero_inside_gas_cloud(); + /* a single-point cloud on hero and it deals no damage. + probably a natural cause of being polyed. don't message about it */ + if (!svc.context.mon_moving && u_at(x, y) && cloudsize == 1 + && (!damage + || (damage && m_poisongas_ok(&gy.youmonst) == M_POISONGAS_OK))) + inside_cloud = TRUE; + if (cloudsize > MAX_CLOUD_SIZE) { impossible("create_gas_cloud: cloud too large (%d)!", cloudsize); cloudsize = MAX_CLOUD_SIZE; @@ -1114,13 +1284,13 @@ create_gas_cloud(coordxy x, coordxy y, int cloudsize, int damage) for (i = 4; i > 0; --i) { coordxy swapidx = rn2(i); coord tmp = dirs[swapidx]; - dirs[swapidx] = dirs[i-1]; - dirs[i-1] = tmp; + + dirs[swapidx] = dirs[i - 1]; + dirs[i - 1] = tmp; } int nvalid = 0; /* # of valid adjacent spots */ for (i = 0; i < 4; ++i) { - /* try all 4 directions */ - + /* try all 4 cardinal directions */ int dx = dirs[i].x, dy = dirs[i].y; boolean isunpicked = TRUE; @@ -1164,30 +1334,45 @@ create_gas_cloud(coordxy x, coordxy y, int cloudsize, int damage) /* If cloud was constrained in small space, give it more time to live. */ cloud->ttl = (cloud->ttl * cloudsize) / newidx; - if (!gi.in_mklev && !gc.context.mon_moving) - set_heros_fault(cloud); /* assume player has created it */ - cloud->inside_f = INSIDE_GAS_CLOUD; - cloud->expire_f = EXPIRE_GAS_CLOUD; - cloud->arg = cg.zeroany; - cloud->arg.a_int = damage; - cloud->visible = TRUE; - cloud->glyph = cmap_to_glyph(damage ? S_poisoncloud : S_cloud); - add_region(cloud); + make_gas_cloud(cloud, damage, inside_cloud); + return cloud; +} - if (!gi.in_mklev && !inside_cloud && is_hero_inside_gas_cloud()) - You("are enveloped in a cloud of %s!", - damage ? "noxious gas" : "steam"); +/* create a single gas cloud from selection */ +NhRegion * +create_gas_cloud_selection( + struct selectionvar *sel, + int damage) +{ + NhRegion *cloud; + NhRect tmprect; + coordxy x, y; + NhRect r = cg.zeroNhRect; + boolean inside_cloud = is_hero_inside_gas_cloud(); + + selection_getbounds(sel, &r); + cloud = create_region((NhRect *) 0, 0); + for (x = r.lx; x <= r.hx; x++) + for (y = r.ly; y <= r.hy; y++) + if (selection_getpoint(x, y, sel)) { + tmprect.lx = tmprect.hx = x; + tmprect.ly = tmprect.hy = y; + add_rect_to_reg(cloud, &tmprect); + } + + make_gas_cloud(cloud, damage, inside_cloud); return cloud; } + /* for checking troubles during prayer; is hero at risk? */ boolean region_danger(void) { int i, f_indx, n = 0; - for (i = 0; i < gn.n_regions; i++) { + for (i = 0; i < svn.n_regions; i++) { /* only care about regions that hero is in */ if (!hero_inside(gr.regions[i])) continue; @@ -1215,7 +1400,7 @@ region_safety(void) NhRegion *r = 0; int i, f_indx, n = 0; - for (i = 0; i < gn.n_regions; i++) { + for (i = 0; i < svn.n_regions; i++) { /* only care about regions that hero is in */ if (!hero_inside(gr.regions[i])) continue; @@ -1229,7 +1414,14 @@ region_safety(void) if (n > 1 || (n == 1 && !r)) { /* multiple overlapping cloud regions or non-expiring one */ - safe_teleds(TELEDS_NO_FLAGS); + (void) safe_teleds(TELEDS_NO_FLAGS); + /* maybe there's no safe place available; must get hero out of danger + or prayer's "fix all troubles" result will get stuck in a loop */ + if (region_danger()) { + set_itimeout(&HMagical_breathing, (long) (d(4, 4) + 4)); + /* not already Breathless or wouldn't be in region danger */ + You_feel("able to breathe."); + } } else if (r) { remove_region(r); } diff --git a/src/report.c b/src/report.c new file mode 100644 index 0000000000..6e82c66c3a --- /dev/null +++ b/src/report.c @@ -0,0 +1,642 @@ +/* NetHack 3.7 report.c $NHDT-Date: 1710525914 2024/03/15 18:05:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.7 $ */ +/* Copyright (c) Kenneth Lorber, Kensington, Maryland, 2024 */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +/* NB: CRASHREPORT implies PANICTRACE */ + +# ifndef NO_SIGNAL +#include +# endif +# ifndef LONG_MAX +#include +# endif +#include "dlb.h" +#include + +#include +# ifdef PANICTRACE_LIBC +#include +# endif + +#ifdef CRASHREPORT +# ifdef WIN32 +# define HASH_PRAGMA_START +# define HASH_PRAGMA_END +# else +# define HASH_PRAGMA_START \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +# define HASH_PRAGMA_END _Pragma("GCC diagnostic pop"); +# endif +# ifdef MACOS +#include +# define HASH_CONTEXTPTR(CTXP) \ + unsigned char tmp[CC_MD4_DIGEST_LENGTH]; \ + CC_MD4_CTX CTXP ## _; \ + CC_MD4_CTX *CTXP = &CTXP ## _ +# define HASH_INIT(ctxp) !CC_MD4_Init(ctxp) +# define HASH_UPDATE(ctx, ptr, len) !CC_MD4_Update(ctx, ptr, len) +# define HASH_FINISH(ctxp) !CC_MD4_Final(tmp, ctxp) +# define HASH_RESULT_SIZE(ctxp) CC_MD4_DIGEST_LENGTH +# define HASH_RESULT(ctx, inp) *inp = (unsigned char *) ctx +# define HASH_CLEANUP(ctxp) +# define HASH_OFLAGS O_RDONLY +# define HASH_BINFILE_DECL char *binfile = argv[0]; +# if (NH_DEVEL_STATUS == NH_STATUS_BETA) +# define HASH_BINFILE \ + if (!binfile || !*binfile) { \ + /* If this triggers, investigate CFBundleGetMainBundle */ \ + /* or CFBundleCopyExecutableURL. */ \ + raw_print( \ + "BETA warning: crashreport_init called without useful info"); \ + goto skip; \ + } +# else /* BETA */ +# define HASH_BINFILE() \ + if (!binfile || !*binfile) { \ + goto skip; \ + } +# endif /* BETA */ +# endif /* MACOS */ + +# ifdef __linux__ +#include "nhmd4.h" +/* v0 is just to suppress compiler warnings about unreachable code */ +# define HASH_CONTEXTPTR(CTXP) \ + volatile int v0 = 0; \ + unsigned char tmp[NHMD4_DIGEST_LENGTH]; \ + NHMD4_CTX CTXP ## _; \ + NHMD4_CTX *CTXP = &CTXP ## _ +# define HASH_INIT(ctxp) (nhmd4_init(ctxp), v0) +# define HASH_UPDATE(ctx, ptr, len) (nhmd4_update(ctx, ptr, len), v0) +# define HASH_FINISH(ctxp) (nhmd4_final(ctxp, tmp), v0) +# define HASH_RESULT_SIZE(ctxp) NHMD4_RESULTLEN +# define HASH_RESULT(ctx, inp) *inp = tmp +# define HASH_CLEANUP(ctxp) +# define HASH_OFLAGS O_RDONLY +# define HASH_BINFILE_DECL char binfile[PATH_MAX+1]; +# define HASH_BINFILE() \ + int len = readlink("/proc/self/exe", binfile, sizeof binfile - 1); \ + if (len > 0) { \ + binfile[len] = '\0'; \ + } else { \ + goto skip; \ + } +# endif // __linux__ + +# ifdef WIN32 +/* WIN32 takes too much code and is dependent on OS includes we can't + * pull in here, so we call out to code in sys/windows/windsys.c */ +# define HASH_CONTEXTPTR(CTXP) +# define HASH_INIT(ctxp) win32_cr_helper('i', ctxp, NULL, 0) +# define HASH_UPDATE(ctxp, ptr, len) win32_cr_helper('u', ctxp, ptr, len) +# define HASH_FINISH(ctxp) win32_cr_helper('f', ctxp, NULL, 0) +# define HASH_CLEANUP(ctxp) win32_cr_helper('c', ctxp, NULL, 0) +# define HASH_RESULT_SIZE(ctxp) win32_cr_helper('s', ctxp, NULL, 0) +# define HASH_RESULT(ctxp, inp) win32_cr_helper('r', ctxp, inp, 0) +# define HASH_OFLAGS _O_RDONLY | _O_BINARY +# define HASH_BINFILE_DECL char *binfile; +# define HASH_BINFILE() \ + if (win32_cr_helper('b', NULL, &binfile, 0)) { \ + goto skip; \ + } +# endif // WIN32 + +/* Binary ID - Use only as a hint to contact.html for recognizing our own + binaries. This is easily spoofed! */ +static char bid[40]; + +/* ARGSUSED */ +void +crashreport_init(int argc UNUSED, char *argv[] UNUSED) +{ + static int once = 0; + if (once++) /* NetHackW.exe calls us twice */ + return; + HASH_BINFILE_DECL; + HASH_PRAGMA_START + HASH_CONTEXTPTR(ctxp); + if (HASH_INIT(ctxp)) + goto skip; + HASH_BINFILE(); /* Does "goto skip" on error. */ + + int fd = open(binfile, HASH_OFLAGS, 0); + if (fd == -1) { +# ifdef BETA + raw_printf("open e=%s", strerror(errno)); +# endif + goto skip; + } + + int segsize; + unsigned char segment[4096]; + + while (0 < (segsize = read(fd, segment, sizeof segment))) { + if (HASH_UPDATE(ctxp, segment, segsize)) + goto skip; + } + if (segsize < 0) { + close(fd); + goto skip; + } + if (HASH_FINISH(ctxp)) + goto skip; + close(fd); + + static const char hexdigits[] = "0123456789abcdef"; + char *p = bid; + unsigned char *in; + HASH_RESULT(ctxp, &in); + uint8 cnt = (uint8) HASH_RESULT_SIZE(ctxp); + /* Just in case, make sure not to overflow the bid buffer. + Divide size by 2 because each octet in the hash uses two slots + in bid[] when formatted as a pair of hexadecimal digits. */ + if (cnt >= (uint8) sizeof bid / 2) + cnt = (uint8) sizeof bid / 2 - 1; + while (cnt) { + /* sprintf(p, "%02x", *in++), p += 2; */ + *p++ = hexdigits[(*in >> 4) & 0x0f]; + *p++ = hexdigits[*in++ & 0x0f]; + --cnt; + } + *p = '\0'; + HASH_CLEANUP(ctxp); + return; + + skip: + Strcpy(bid, "unknown"); + HASH_CLEANUP(ctxp); + HASH_PRAGMA_END +} + +#undef HASH_CONTEXTPTR +#undef HASH_INIT +#undef HASH_UPDATE +#undef HASH_FINISH +#undef HASH_CLEANUP +#undef HASH_RESULT +#undef HASH_RESULT_SIZE +#undef HASH_PRAGMA_START +#undef HASH_PRAGMA_END +#undef HASH_BINFILE_DECL +#undef HASH_BINFILE + +void +crashreport_bidshow(void) +{ +# if defined(WIN32) && !defined(WIN32CON) + if (0 == win32_cr_helper('D', ctxp, bid, 0)) +# endif + { + raw_print(bid); +# ifdef WIN32notyet + wait_synch(); +# endif + } +} + +/* Build a URL with a query string and try to launch a new browser window + * to report from panic() or impossible(). Requires libc support for + * the stacktrace. Uses memory on the stack to avoid memory allocation + * (on most platforms) (but libc can still do anything it wants). */ + +// No theoretial limit for URL length but reality is messy. +// This should work on all modern platforms. +# ifndef MAX_URL +# define MAX_URL 8192 +# endif +# ifndef SWR_FRAMES +# define SWR_FRAMES 20 +# endif + +// mark holds the initial eos; if we can't get the value in +// then we can remove the whole item if desired. For other +// semantics, caller can handle mark. +#define SWR_ADD(str) \ + utmp = strlen(str); \ + mark = uend; \ + if (utmp >= urem) \ + goto full; \ + memcpy(uend, str, utmp); \ + uend += utmp; urem -= utmp; \ + *uend = '\0'; + +// NB: on overflow this rolls us back to mark, so if we don't +// want to roll back to the last SWR_ADD, update mark before +// calling this macro. +#define SWR_ADD_URIcoded(str) \ + if (swr_add_uricoded(str, &uend, &urem, mark)) \ + goto full; + +/* On overflow, truncate to markp (but only if markp != NULL). */ +boolean +swr_add_uricoded( + const char *in, + char **out, + int *remaining, + char *markp) +{ + while (*in) { + if (isalnum(*in) || strchr("_-.~", *in)) { + **out = *in; + (*out)++; + (*remaining)--; + } else if (*in == ' ') { + **out = '+'; + (*out)++; + (*remaining)--; + } else { + if (*remaining <= 3) { + if (markp) + *out = markp, *remaining = 0; + **out = '\0'; + return TRUE; + } + int x = snprintf(*out, *remaining, "%%%02X", *in); + *out += x; + *remaining -= x; + } + in++; + if (!*remaining) { + if (markp) + *out = markp, *remaining = 0; + **out = '\0'; + return TRUE; + } + **out = '\0'; + } + return FALSE; /* normal return */ +} + +static char url[MAX_URL]; // XXX too bad this isn't allocated as needed +static int urem = MAX_URL; // adjusted for gc.crash_urlmax below +static char *uend = url; +static int utmp; // used inside macros +static char *mark; // holds previous terminator (generally) + +boolean +submit_web_report(int cos, const char *msg, const char *why) +{ + urem = (gc.crash_urlmax < 0 || gc.crash_urlmax > MAX_URL) + ? MAX_URL : min(MAX_URL,gc.crash_urlmax); + char temp[200]; + char temp2[200]; + int countpp = 0; /* pre and post traceback lines */ +// URL loaded for creating reports to the NetHack DevTeam +// CRASHREPORTURL=https://nethack.org/links/cr-37BETA.html + if (!sysopt.crashreporturl) + return FALSE; + SWR_ADD(sysopt.crashreporturl); + /* cos - operation, v - version */ + Snprintf(temp, sizeof 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 )); + SWR_ADD_URIcoded(temp); + } + + SWR_ADD("&gitver="); + SWR_ADD_URIcoded(getversionstring(temp2, sizeof temp2)); + + if (gc.crash_name) { + SWR_ADD("&name="); + SWR_ADD_URIcoded(gc.crash_name); + } + + if(gc.crash_email) { + SWR_ADD("&email="); + SWR_ADD_URIcoded(gc.crash_email); + } + // hardware: leave for user + // software: leave for user + // comments: leave for user + + SWR_ADD("&details="); + if (why) { + SWR_ADD_URIcoded(why); + SWR_ADD_URIcoded("\n"); + mark = uend; + countpp++; + } + + SWR_ADD_URIcoded("bid: "); + SWR_ADD_URIcoded(bid); + SWR_ADD_URIcoded("\n"); + mark = uend; + countpp++; + + int count = 0; + if (cos == 1) { +# ifdef WIN32 + count = win32_cr_gettrace(SWR_FRAMES, uend, MAX_URL - (uend - url)); + uend = eos(url); +# else + void *bt[SWR_FRAMES]; + int x; + char **info; + + count = backtrace(bt, SIZE(bt)); + info = backtrace_symbols(bt, count); + for (x = 0; x < count; x++) { + copynchars(temp, info[x], (int) sizeof temp - 1 - 1); /* \n\0 */ + /* try to remove up to 16 blank spaces by removing 8 twice */ + (void) strsubst(temp, " ", ""); + (void) strsubst(temp, " ", ""); + (void) strncat(temp, "\n", sizeof temp - 1); +# if 0 // __linux__ +// not needed for MacOS +// XXX is it actually needed for linux? TBD + Snprintf(temp2, sizeof temp2, "[%02lu]\n", (unsigned long) x); + uend--; // remove the \n we added above + SWR_ADD_URIcoded(temp2); +# endif // linux + SWR_ADD_URIcoded(temp); + mark = uend; + } +# endif // !WIN32 + } + +# ifdef DUMPLOG_CORE + // config.h turns this on, but make it easy to turn off if needed + if (cos == 1) { + int k; + SWR_ADD_URIcoded("Latest messages:\n"); + mark=uend; + countpp++; + for (k = 0; k < 5; k++) { + const char *line = get_saved_pline(k); + if (!line) + break; + SWR_ADD_URIcoded(line); + SWR_ADD_URIcoded("\n"); + countpp++; + mark = uend; + } + } +# endif /* DUMPLOG_CORE */ + + // detailrows: Guess since we can't know the + // width of the window. + SWR_ADD("&detailrows="); + Snprintf(temp, sizeof temp, "%d", min(count + countpp, 30)); + SWR_ADD_URIcoded(temp); + + full: + ; +//printf("URL=%ld '%s'\n",strlen(url),url); +# ifdef WIN32 + int *rv = win32_cr_shellexecute(url); +// XXX TESTING +printf("ShellExecute returned: %p\n",rv); // >32 is ok +# else /* !WIN32 */ + const char *xargv[] = { + CRASHREPORT, + url, + NULL + }; + int pid = fork(); + extern char **environ; + if (pid == 0) { + char err[100]; +# ifdef CRASHREPORT_EXEC_NOSTDERR + int devnull; + /* Keep the output clean - firefox spews useless errors on + * my system. */ + (void) close(2); + devnull = open("/dev/null", O_WRONLY); +# endif + + (void) execve(CRASHREPORT, (char * const *) xargv, environ); + Sprintf(err, "Can't start " CRASHREPORT ": %s", strerror(errno)); + raw_print(err); +# ifdef CRASHREPORT_EXEC_NOSTDERR + (void) close(devnull); +# endif + exit(1); + } else { + int status; + errno = 0; + (void) waitpid(pid, &status, 0); + if (status) { /* XXX check could be more precise */ +# ifdef BETA + /* Not useful at the moment. XXX */ + char err[100]; + + Sprintf(err, "pid=%d e=%d status=%0x", wpid, errno, status); + raw_print(err); +# endif + return FALSE; + } + } + /* free(info); -- Don't risk it. */ +# endif /* !WIN32 */ + return TRUE; +} + +int +dobugreport(void) +{ + if (!submit_web_report(2, NULL, "#bugreport command")) { + pline("Unable to send bug report. Please visit %s instead.", + (sysopt.crashreporturl && *sysopt.crashreporturl) + ? sysopt.crashreporturl + : "https://www.nethack.org" + ); + } + return ECMD_OK; +} + +#undef SWR_ADD +#undef SWR_ADD_URIcoded +#undef SWR_FRAMES +#undef SWR_HDR +#undef SWR_LINES + +#endif /* CRASHREPORT */ + +#ifdef PANICTRACE + +/*ARGSUSED*/ +boolean +NH_panictrace_libc(void) +{ +# if 0 /* XXX how did this get left here? */ + if (submit_web_report("Panic", why)) + return TRUE; +# endif + +# ifdef PANICTRACE_LIBC + void *bt[20]; + int count, x; + char **info, buf[BUFSZ]; + + raw_print(" Generating more information you may report:\n"); + count = backtrace(bt, SIZE(bt)); + info = backtrace_symbols(bt, count); + for (x = 0; x < count; x++) { + copynchars(buf, info[x], (int) sizeof buf - 1); + /* try to remove up to 16 blank spaces by removing 8 twice */ + (void) strsubst(buf, " ", ""); + (void) strsubst(buf, " ", ""); + raw_printf("[%02lu] %s", (unsigned long) x, buf); + } + /* free(info); -- Don't risk it. */ + return TRUE; +# else + return FALSE; +# endif /* !PANICTRACE_LIBC */ +} + +/* + * fooPATH file system path for foo + * fooVAR (possibly const) variable containing fooPATH + */ +# ifdef PANICTRACE_GDB +# ifdef SYSCF +# define GDBVAR sysopt.gdbpath +# define GREPVAR sysopt.greppath +# else /* SYSCF */ +# define GDBVAR GDBPATH +# define GREPVAR GREPPATH +# endif /* SYSCF */ +# endif /* PANICTRACE_GDB */ + +boolean +NH_panictrace_gdb(void) +{ +# ifdef PANICTRACE_GDB + /* A (more) generic method to get a stack trace - invoke + * gdb on ourself. */ + const char *gdbpath = GDBVAR; + const char *greppath = GREPVAR; + char buf[BUFSZ]; + FILE *gdb; + + if (gdbpath == NULL || gdbpath[0] == 0) + return FALSE; + if (greppath == NULL || greppath[0] == 0) + return FALSE; + + sprintf(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); + sleep(4); /* ugly */ + pclose(gdb); + return TRUE; + } else { + return FALSE; + } +# else + return FALSE; +# endif /* !PANICTRACE_GDB */ +} + +#ifdef DUMPLOG_CORE +#define USED_if_dumplog +#else +#define USED_if_dumplog UNUSED +#endif + +/* lineno==0 gives the most recent message (e.g. + "Do you want to call panic..." if called from #panic) */ +const char * +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; + + while (limit--) { + if (gs.saved_plines[p]) { /* valid line */ + if (lineno--) { + p = (p - 1 + DUMPLOG_MSG_COUNT) % DUMPLOG_MSG_COUNT; + } else { + return gs.saved_plines[p]; + } + } + } +#endif /* DUMPLOG_CORE */ + return NULL; +} + +#undef USED_if_dumplog + +# ifndef NO_SIGNAL +/* called as signal() handler, so sent at least one arg */ +/*ARGUSED*/ +void +panictrace_handler(int sig_unused UNUSED) +{ +#define SIG_MSG "\nSignal received.\n" + int f2; + +# ifdef CURSES_GRAPHICS + if (iflags.window_inited && WINDOWPORT(curses)) { + extern void curses_uncurse_terminal(void); /* wincurs.h */ + + /* it is risky calling this during a program-terminating signal, + but without it the subsequent backtrace is useless because + that ends up being scrawled all over the screen; call is + here rather than in NH_abort() because panic() calls both + exit_nhwindows(), which makes this same call under curses, + then NH_abort() and we don't want to call this twice */ + curses_uncurse_terminal(); + } +# endif + + f2 = (int) write(2, SIG_MSG, sizeof SIG_MSG - 1); + nhUse(f2); /* what could we do if write to fd#2 (stderr) fails */ + NH_abort(NULL); /* ... and we're already in the process of quitting? */ +} + +void +panictrace_setsignals(boolean set) +{ +#define SETSIGNAL(sig) \ + (void) signal(sig, set ? (SIG_RET_TYPE) panictrace_handler : SIG_DFL); +# ifdef SIGILL + SETSIGNAL(SIGILL); +# endif +# ifdef SIGTRAP + SETSIGNAL(SIGTRAP); +# endif +# ifdef SIGIOT + SETSIGNAL(SIGIOT); +# endif +# ifdef SIGBUS + SETSIGNAL(SIGBUS); +# endif +# ifdef SIGFPE + SETSIGNAL(SIGFPE); +# endif +# ifdef SIGSEGV + SETSIGNAL(SIGSEGV); +# endif +# ifdef SIGSTKFLT + SETSIGNAL(SIGSTKFLT); +# endif +# ifdef SIGSYS + SETSIGNAL(SIGSYS); +# endif +# ifdef SIGEMT + SETSIGNAL(SIGEMT); +# endif +#undef SETSIGNAL +} +# endif /* NO_SIGNAL */ +#endif /* PANICTRACE */ + +/*report.c*/ diff --git a/src/restore.c b/src/restore.c index 96facdfb7a..fef9144a77 100644 --- a/src/restore.c +++ b/src/restore.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 restore.c $NHDT-Date: 1649530943 2022/04/09 19:02:23 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.194 $ */ +/* 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. */ @@ -12,30 +12,30 @@ extern int dotrow; /* shared with save */ #endif #ifdef ZEROCOMP -static void zerocomp_minit(void); -static void zerocomp_mread(int, genericptr_t, unsigned int); -static int zerocomp_mgetc(void); +staticfn void zerocomp_minit(void); +staticfn void zerocomp_mread(int, genericptr_t, unsigned int); +staticfn int zerocomp_mgetc(void); #endif -static void find_lev_obj(void); -static void restlevchn(NHFILE *); -static void restdamage(NHFILE *); -static void restobj(NHFILE *, struct obj *); -static struct obj *restobjchn(NHFILE *, boolean); -static void restmon(NHFILE *, struct monst *); -static struct monst *restmonchn(NHFILE *); -static struct fruit *loadfruitchn(NHFILE *); -static void freefruitchn(struct fruit *); -static void ghostfruit(struct obj *); -static boolean restgamestate(NHFILE *, unsigned int *, unsigned int *); -static void restlevelstate(unsigned int, unsigned int); -static int restlevelfile(xint8); -static void rest_bubbles(NHFILE *); -static void restore_gamelog(NHFILE *); -static void restore_msghistory(NHFILE *); -static void reset_oattached_mids(boolean); -static void rest_levl(NHFILE *, boolean); -static void rest_stairs(NHFILE *); +staticfn void find_lev_obj(void); +staticfn void restlevchn(NHFILE *); +staticfn void restdamage(NHFILE *); +staticfn void restobj(NHFILE *, struct obj *); +staticfn struct obj *restobjchn(NHFILE *, boolean); +staticfn void restmon(NHFILE *, struct monst *); +staticfn struct monst *restmonchn(NHFILE *); +staticfn struct fruit *loadfruitchn(NHFILE *); +staticfn void freefruitchn(struct fruit *); +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 *); /* * Save a mapping of IDs from ghost levels to the current level. This @@ -50,8 +50,8 @@ struct bucket { } map[N_PER_BUCKET]; }; -static void clear_id_mapping(void); -static void add_id_mapping(unsigned, unsigned); +staticfn void clear_id_mapping(void); +staticfn void add_id_mapping(unsigned, unsigned); #ifdef AMII_GRAPHICS void amii_setpens(int); /* use colors from save file */ @@ -67,17 +67,17 @@ extern int amii_numcolors; in an implicit conversion; this macro does it explicitly */ #define Mread(fd,adr,siz) mread((fd), (genericptr_t) (adr), (unsigned) (siz)) -/* Recalculate gl.level.objects[x][y], since this info was not saved. */ -static void +/* Recalculate svl.level.objects[x][y], since this info was not saved. */ +staticfn void find_lev_obj(void) { - register struct obj *fobjtmp = (struct obj *) 0; - register struct obj *otmp; + struct obj *fobjtmp = (struct obj *) 0; + struct obj *otmp; int x, y; for (x = 0; x < COLNO; x++) for (y = 0; y < ROWNO; y++) - gl.level.objects[x][y] = (struct obj *) 0; + svl.level.objects[x][y] = (struct obj *) 0; /* * Reverse the entire fobj chain, which is necessary so that we can @@ -92,7 +92,7 @@ find_lev_obj(void) } /* fobj should now be empty */ - /* Set gl.level.objects (as well as reversing the chain back again) */ + /* Set svl.level.objects (as well as reversing the chain back again) */ while ((otmp = fobjtmp) != 0) { fobjtmp = otmp->nobj; place_object(otmp, otmp->ox, otmp->oy); @@ -113,7 +113,7 @@ find_lev_obj(void) void inven_inuse(boolean quietly) { - register struct obj *otmp, *otmp2; + struct obj *otmp, *otmp2; for (otmp = gi.invent; otmp; otmp = otmp2) { otmp2 = otmp->nobj; @@ -125,13 +125,13 @@ inven_inuse(boolean quietly) } } -static void +staticfn void restlevchn(NHFILE *nhfp) { int cnt = 0; s_level *tmplev, *x; - gs.sp_levchn = (s_level *) 0; + svs.sp_levchn = (s_level *) 0; if (nhfp->structlevel) Mread(nhfp->fd, &cnt, sizeof cnt); @@ -140,10 +140,10 @@ restlevchn(NHFILE *nhfp) if (nhfp->structlevel) Mread(nhfp->fd, tmplev, sizeof *tmplev); - if (!gs.sp_levchn) - gs.sp_levchn = tmplev; + if (!svs.sp_levchn) + svs.sp_levchn = tmplev; else { - for (x = gs.sp_levchn; x->next; x = x->next) + for (x = svs.sp_levchn; x->next; x = x->next) ; x->next = tmplev; } @@ -151,7 +151,7 @@ restlevchn(NHFILE *nhfp) } } -static void +staticfn void restdamage(NHFILE *nhfp) { unsigned int dmgcount = 0; @@ -171,15 +171,15 @@ restdamage(NHFILE *nhfp) Mread(nhfp->fd, tmp_dam, sizeof *tmp_dam); if (ghostly) - tmp_dam->when += (gm.moves - go.omoves); + tmp_dam->when += (svm.moves - go.omoves); - tmp_dam->next = gl.level.damagelist; - gl.level.damagelist = tmp_dam; + tmp_dam->next = svl.level.damagelist; + svl.level.damagelist = tmp_dam; } while (--counter > 0); } /* restore one object */ -static void +staticfn void restobj(NHFILE *nhfp, struct obj *otmp) { int buflen = 0; @@ -234,11 +234,11 @@ restobj(NHFILE *nhfp, struct obj *otmp) } } -static struct obj * +staticfn struct obj * restobjchn(NHFILE *nhfp, boolean frozen) { - register struct obj *otmp, *otmp2 = 0; - register struct obj *first = (struct obj *) 0; + struct obj *otmp, *otmp2 = 0; + struct obj *first = (struct obj *) 0; int buflen = 0; boolean ghostly = (nhfp->ftype == NHF_BONESFILE); @@ -250,6 +250,7 @@ restobjchn(NHFILE *nhfp, boolean frozen) break; otmp = newobj(); + assert(otmp != 0); restobj(nhfp, otmp); if (!first) first = otmp; @@ -269,7 +270,7 @@ restobjchn(NHFILE *nhfp, boolean frozen) * immediately after old player died. */ if (ghostly && !frozen && !age_is_relative(otmp)) - otmp->age = gm.moves - go.omoves + otmp->age; + otmp->age = svm.moves - go.omoves + otmp->age; /* get contents of a container or statue */ if (Has_contents(otmp)) { @@ -284,14 +285,14 @@ restobjchn(NHFILE *nhfp, boolean frozen) otmp->bypass = 0; if (!ghostly) { /* fix the pointers */ - if (otmp->o_id == gc.context.victual.o_id) - gc.context.victual.piece = otmp; - if (otmp->o_id == gc.context.tin.o_id) - gc.context.tin.tin = otmp; - if (otmp->o_id == gc.context.spbook.o_id) - gc.context.spbook.book = otmp; - if (otmp->o_id == gc.context.crystal.o_id) - gc.context.crystal.ball = otmp; + if (otmp->o_id == svc.context.victual.o_id) + svc.context.victual.piece = otmp; + if (otmp->o_id == svc.context.tin.o_id) + svc.context.tin.tin = otmp; + if (otmp->o_id == svc.context.spbook.o_id) + svc.context.spbook.book = otmp; + if (otmp->o_id == svc.context.crystal.o_id) + svc.context.crystal.ball = otmp; } otmp2 = otmp; } @@ -304,7 +305,7 @@ restobjchn(NHFILE *nhfp, boolean frozen) } /* restore one monster */ -static void +staticfn void restmon(NHFILE *nhfp, struct monst *mtmp) { int buflen = 0; @@ -382,11 +383,11 @@ restmon(NHFILE *nhfp, struct monst *mtmp) } /* mextra */ } -static struct monst * +staticfn struct monst * restmonchn(NHFILE *nhfp) { - register struct monst *mtmp, *mtmp2 = 0; - register struct monst *first = (struct monst *) 0; + struct monst *mtmp, *mtmp2 = 0; + struct monst *first = (struct monst *) 0; int offset, buflen = 0; boolean ghostly = (nhfp->ftype == NHF_BONESFILE); @@ -397,6 +398,7 @@ restmonchn(NHFILE *nhfp) break; mtmp = newmonst(); + assert(mtmp != 0); restmon(nhfp, mtmp); if (!first) first = mtmp; @@ -447,8 +449,8 @@ restmonchn(NHFILE *nhfp) restpriest(mtmp, ghostly); if (!ghostly) { - if (mtmp->m_id == gc.context.polearm.m_id) - gc.context.polearm.hitmon = mtmp; + if (mtmp->m_id == svc.context.polearm.m_id) + svc.context.polearm.hitmon = mtmp; } mtmp2 = mtmp; } @@ -459,10 +461,10 @@ restmonchn(NHFILE *nhfp) return first; } -static struct fruit * +staticfn struct fruit * loadfruitchn(NHFILE *nhfp) { - register struct fruit *flist, *fnext; + struct fruit *flist, *fnext; flist = 0; for (;;) { @@ -479,10 +481,10 @@ loadfruitchn(NHFILE *nhfp) return flist; } -static void -freefruitchn(register struct fruit *flist) +staticfn void +freefruitchn(struct fruit *flist) { - register struct fruit *fnext; + struct fruit *fnext; while (flist) { fnext = flist->nextf; @@ -491,10 +493,10 @@ freefruitchn(register struct fruit *flist) } } -static void -ghostfruit(register struct obj *otmp) +staticfn void +ghostfruit(struct obj *otmp) { - register struct fruit *oldf; + struct fruit *oldf; for (oldf = go.oldfruit; oldf; oldf = oldf->nextf) if (oldf->fid == otmp->spe) @@ -512,11 +514,8 @@ ghostfruit(register struct obj *otmp) #define SYSOPT_CHECK_SAVE_UID TRUE #endif -static -boolean -restgamestate( - NHFILE *nhfp, - unsigned *stuckid, unsigned *steedid) +staticfn boolean +restgamestate(NHFILE *nhfp) { struct flag newgameflags; struct context_info newgamecontext; /* all 0, but has some pointers */ @@ -524,7 +523,7 @@ restgamestate( struct obj *bc_obj; char timebuf[15]; unsigned long uid = 0; - boolean defer_perm_invent; + boolean defer_perm_invent, restoring_special; if (nhfp->structlevel) Mread(nhfp->fd, &uid, sizeof uid); @@ -538,13 +537,13 @@ restgamestate( return FALSE; } - newgamecontext = gc.context; /* copy statically init'd context */ + newgamecontext = svc.context; /* copy statically init'd context */ if (nhfp->structlevel) - Mread(nhfp->fd, &gc.context, sizeof gc.context); - gc.context.warntype.species = (gc.context.warntype.speciesidx >= LOW_PM) - ? &mons[gc.context.warntype.speciesidx] + Mread(nhfp->fd, &svc.context, sizeof svc.context); + svc.context.warntype.species = (ismnum(svc.context.warntype.speciesidx)) + ? &mons[svc.context.warntype.speciesidx] : (struct permonst *) 0; - /* gc.context.victual.piece, .tin.tin, .spellbook.book, and .polearm.hitmon + /* context.victual.piece, .tin.tin, .spellbook.book, and .polearm.hitmon are pointers which get set to Null during save and will be recovered via corresponding o_id or m_id while objs or mons are being restored */ @@ -560,7 +559,7 @@ restgamestate( of unpaid items before shopkeeper's bill is available is a no-no; named fruit names aren't accessible yet either [3.6.2: moved perm_invent from flags to iflags to keep it out of - save files; retaining the override here is simpler than trying to + save files; retaining the override here is simpler than trying to figure out where it really belongs now] */ defer_perm_invent = iflags.perm_invent; iflags.perm_invent = FALSE; @@ -569,11 +568,12 @@ restgamestate( in the discover case, we don't want to set that for a normal game until after the save file has been removed */ iflags.deferred_X = (newgameflags.explore && !discover); + restoring_special = (wizard || discover); if (newgameflags.debug) { /* authorized by startup code; wizard mode exists and is allowed */ wizard = TRUE, discover = iflags.deferred_X = FALSE; - } else if (wizard) { - /* specified by save file; check authorization now */ + } else if (restoring_special) { + /* specified by save file; check authorization now. */ set_playmode(); } role_init(); /* Reset the initial role, race, gender, and alignment */ @@ -584,6 +584,13 @@ restgamestate( Mread(nhfp->fd, &u, sizeof u); gy.youmonst.cham = u.mcham; + 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; + } + if (nhfp->structlevel) Mread(nhfp->fd, timebuf, 14); timebuf[14] = '\0'; @@ -616,7 +623,7 @@ restgamestate( iflags.deferred_X = FALSE; iflags.perm_invent = defer_perm_invent; flags = newgameflags; - gc.context = newgamecontext; + svc.context = newgamecontext; gy.youmonst = cg.zeromonst; return FALSE; } @@ -644,7 +651,7 @@ restgamestate( gm.migrating_mons = restmonchn(nhfp); if (nhfp->structlevel) { - Mread(nhfp->fd, &gm.mvitals[0], sizeof gm.mvitals); + Mread(nhfp->fd, &svm.mvitals[0], sizeof svm.mvitals); } /* @@ -672,25 +679,17 @@ restgamestate( restore_dungeon(nhfp); restlevchn(nhfp); if (nhfp->structlevel) { - Mread(nhfp->fd, &gm.moves, sizeof gm.moves); + Mread(nhfp->fd, &svm.moves, sizeof svm.moves); /* hero_seq isn't saved and restored because it can be recalculated */ - gh.hero_seq = gm.moves << 3; /* normally handled in moveloop() */ - Mread(nhfp->fd, &gq.quest_status, sizeof gq.quest_status); - Mread(nhfp->fd, gs.spl_book, (MAXSPELL + 1) * sizeof (struct spell)); + 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)); } restore_artifacts(nhfp); restore_oracles(nhfp); - if (u.ustuck) { - if (nhfp->structlevel) - Mread(nhfp->fd, stuckid, sizeof *stuckid); - } - if (u.usteed) { - if (nhfp->structlevel) - Mread(nhfp->fd, steedid, sizeof *steedid); - } if (nhfp->structlevel) { - Mread(nhfp->fd, gp.pl_character, sizeof gp.pl_character); - Mread(nhfp->fd, gp.pl_fruit, sizeof gp.pl_fruit); + Mread(nhfp->fd, svp.pl_character, sizeof svp.pl_character); + Mread(nhfp->fd, svp.pl_fruit, sizeof svp.pl_fruit); } freefruitchn(gf.ffruit); /* clean up fruit(s) made by initoptions() */ gf.ffruit = loadfruitchn(nhfp); @@ -703,6 +702,7 @@ restgamestate( /* must come after all mons & objs are restored */ relink_timers(FALSE); relink_light_sources(FALSE); + adj_erinys(u.ualign.abuse); #ifdef WHEREIS_FILE touch_whereis(); #endif @@ -712,34 +712,21 @@ restgamestate( } /* update game state pointers to those valid for the current level (so we - * don't dereference a wild u.ustuck when saving the game state, for instance) - */ -static void -restlevelstate(unsigned int stuckid, unsigned int steedid) + don't dereference a wild u.ustuck when saving game state, for instance) */ +staticfn void +restlevelstate(void) { - register struct monst *mtmp; - - if (stuckid) { - for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) - if (mtmp->m_id == stuckid) - break; - if (!mtmp) - panic("Cannot find the monster ustuck."); - set_ustuck(mtmp); - } - if (steedid) { - for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) - if (mtmp->m_id == steedid) - break; - if (!mtmp) - panic("Cannot find the monster usteed."); - u.usteed = mtmp; - remove_monster(mtmp->mx, mtmp->my); - } + /* + * Note: restoring steed and engulfer/holder/holdee is now handled + * in getlev() and there's nothing left for restlevelstate() to do. + */ + return; } +/* after getlev(), put current level into a level/lock file; + essential when splitting a save file into individual level files */ /*ARGSUSED*/ -static int +staticfn int restlevelfile(xint8 ltmp) { char whynot[BUFSZ]; @@ -747,8 +734,8 @@ restlevelfile(xint8 ltmp) nhfp = create_levelfile(ltmp, whynot); if (!nhfp) { - /* BUG: should suppress any attempt to write a panic - save file if file creation is now failing... */ + /* failed to create a new file; don't attempt to make a panic save */ + program_state.something_worth_saving = 0; panic("restlevelfile: %s", whynot); } bufon(nhfp->fd); @@ -761,16 +748,15 @@ restlevelfile(xint8 ltmp) int dorecover(NHFILE *nhfp) { - unsigned int stuckid = 0, steedid = 0; /* not a register */ xint8 ltmp = 0; int rtmp; /* suppress map display if some part of the code tries to update that */ - gp.program_state.restoring = REST_GSTATE; + program_state.restoring = REST_GSTATE; - get_plname_from_file(nhfp, gp.plname); + get_plname_from_file(nhfp, svp.plname, TRUE); getlev(nhfp, 0, (xint8) 0); - if (!restgamestate(nhfp, &stuckid, &steedid)) { + if (!restgamestate(nhfp)) { NHFILE tnhfp; display_nhwindow(WIN_MESSAGE, TRUE); @@ -778,14 +764,18 @@ dorecover(NHFILE *nhfp) tnhfp.mode = FREEING; tnhfp.fd = -1; savelev(&tnhfp, 0); /* discard current level */ - /* no need tfor close_nhfile(&tnhfp), which + /* no need for close_nhfile(&tnhfp), which is not really affiliated with an open file */ close_nhfile(nhfp); (void) delete_savefile(); - gp.program_state.restoring = 0; + u.usteed_mid = u.ustuck_mid = 0; + program_state.restoring = 0; return 0; } - restlevelstate(stuckid, steedid); + /* after restgamestate() -> restnames() so that 'bases[]' is populated */ + init_oclass_probs(); /* recompute go.oclass_prob_totals[] */ + + restlevelstate(); #ifdef INSURANCE savestateinlock(); #endif @@ -793,7 +783,7 @@ dorecover(NHFILE *nhfp) if (rtmp < 2) return rtmp; /* dorecover called recursively */ - gp.program_state.restoring = REST_LEVELS; + program_state.restoring = REST_LEVELS; /* these pointers won't be valid while we're processing the * other levels, but they'll be reset again by restlevelstate() @@ -817,7 +807,7 @@ dorecover(NHFILE *nhfp) #endif clear_nhwindow(WIN_MESSAGE); You("return to level %d in %s%s.", depth(&u.uz), - gd.dungeons[u.uz.dnum].dname, + svd.dungeons[u.uz.dnum].dname, flags.debug ? " while in debug mode" : flags.explore ? " while in explore mode" : ""); curs(WIN_MAP, 1, 1); @@ -851,19 +841,21 @@ dorecover(NHFILE *nhfp) } restoreinfo.mread_flags = 0; rewind_nhfile(nhfp); /* return to beginning of file */ - (void) validate(nhfp, (char *) 0); - get_plname_from_file(nhfp, gp.plname); + (void) validate(nhfp, (char *) 0, FALSE); + get_plname_from_file(nhfp, svp.plname, TRUE); + + /* not 0 nor REST_GSTATE nor REST_LEVELS */ + program_state.restoring = REST_CURRENT_LEVEL; getlev(nhfp, 0, (xint8) 0); close_nhfile(nhfp); - restlevelstate(stuckid, steedid); - gp.program_state.something_worth_saving = 1; /* useful data now exists */ + restlevelstate(); + program_state.something_worth_saving = 1; /* useful data now exists */ if (!wizard && !discover) (void) delete_savefile(); reset_glyphmap(gm_levelchange); max_rank_sz(); /* to recompute gm.mrank_sz (botl.c) */ - init_oclass_probs(); /* recompute go.oclass_prob_totals[] */ if ((uball && !uchain) || (uchain && !uball)) { impossible("restgamestate: lost ball & chain"); @@ -886,9 +878,9 @@ dorecover(NHFILE *nhfp) gv.vision_full_recalc = 1; /* recompute vision (not saved) */ run_timers(); /* expire all timers that have gone off while away */ - gp.program_state.restoring = 0; /* affects bot() so clear before docrt() */ + program_state.restoring = 0; /* affects bot() so clear before docrt() */ - if (ge.early_raw_messages && !gp.program_state.beyond_savefile_load) { + if (ge.early_raw_messages && !program_state.beyond_savefile_load) { /* * We're about to obliterate some potentially important * startup messages, so give the player a chance to see them. @@ -896,7 +888,8 @@ dorecover(NHFILE *nhfp) ge.early_raw_messages = 0; wait_synch(); } - gp.program_state.beyond_savefile_load = 1; + u.usteed_mid = u.ustuck_mid = 0; + program_state.beyond_savefile_load = 1; docrt(); clear_nhwindow(WIN_MESSAGE); @@ -907,7 +900,7 @@ dorecover(NHFILE *nhfp) return 1; } -static void +staticfn void rest_stairs(NHFILE *nhfp) { int buflen = 0; @@ -926,7 +919,7 @@ rest_stairs(NHFILE *nhfp) if (nhfp->structlevel) { Mread(nhfp->fd, &stway, sizeof stway); } - if (gp.program_state.restoring != REST_GSTATE + 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; @@ -962,7 +955,7 @@ restcemetery(NHFILE *nhfp, struct cemetery **cemeteryaddr) } /*ARGSUSED*/ -static void +staticfn void rest_levl( NHFILE *nhfp, #ifdef RLECOMP @@ -1012,15 +1005,15 @@ trickery(char *reason) pline("Strange, this map is not as I remember it."); pline("Somebody is trying some trickery here..."); pline("This game is void."); - Strcpy(gk.killer.name, reason ? reason : ""); + Strcpy(svk.killer.name, reason ? reason : ""); done(TRICKED); } void getlev(NHFILE *nhfp, int pid, xint8 lev) { - register struct trap *trap; - register struct monst *mtmp; + struct trap *trap; + struct monst *mtmp; long elapsed; branch *br; int hpid = 0; @@ -1031,6 +1024,8 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) short tlev; #endif + program_state.in_getlev = TRUE; + if (ghostly) clear_id_mapping(); @@ -1069,30 +1064,34 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) pline1(trickbuf); trickery(trickbuf); } - restcemetery(nhfp, &gl.level.bonesinfo); + restcemetery(nhfp, &svl.level.bonesinfo); rest_levl(nhfp, (boolean) ((sfrestinfo.sfi1 & SFI1_RLECOMP) == SFI1_RLECOMP)); if (nhfp->structlevel) { - Mread(nhfp->fd, gl.lastseentyp, sizeof gl.lastseentyp); + Mread(nhfp->fd, svl.lastseentyp, sizeof svl.lastseentyp); Mread(nhfp->fd, &go.omoves, sizeof go.omoves); } - elapsed = gm.moves - go.omoves; + elapsed = svm.moves - go.omoves; if (nhfp->structlevel) { rest_stairs(nhfp); - Mread(nhfp->fd, &gu.updest, sizeof gu.updest); - Mread(nhfp->fd, &gd.dndest, sizeof gd.dndest); - Mread(nhfp->fd, &gl.level.flags, sizeof gl.level.flags); - if (gd.doors) - free(gd.doors); - Mread(nhfp->fd, &gd.doors_alloc, sizeof gd.doors_alloc); - gd.doors = (coord *) alloc(gd.doors_alloc * sizeof (coord)); - Mread(nhfp->fd, gd.doors, gd.doors_alloc * sizeof (coord)); + 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)); + } } rest_rooms(nhfp); /* No joke :-) */ - if (gn.nroom) - gd.doorindex = gr.rooms[gn.nroom - 1].fdoor - + gr.rooms[gn.nroom - 1].doorct; + if (svn.nroom) + gd.doorindex = svr.rooms[svn.nroom - 1].fdoor + + svr.rooms[svn.nroom - 1].doorct; else gd.doorindex = 0; @@ -1109,7 +1108,7 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) if (nhfp->structlevel) Mread(nhfp->fd, trap, sizeof *trap); if (trap->tx != 0) { - if (gp.program_state.restoring != REST_GSTATE + if (program_state.restoring != REST_GSTATE && trap->dst.dnum == u.uz.dnum) { /* convert relative destination to absolute */ trap->dst.dlevel += u.uz.dlevel; @@ -1131,25 +1130,35 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) find_lev_obj(); /* restobjchn()'s `frozen' argument probably ought to be a callback routine so that we can check for objects being buried under ice */ - gl.level.buriedobjlist = restobjchn(nhfp, FALSE); + svl.level.buriedobjlist = restobjchn(nhfp, FALSE); gb.billobjs = restobjchn(nhfp, FALSE); rest_engravings(nhfp); /* reset level.monsters for new level */ for (x = 0; x < COLNO; x++) for (y = 0; y < ROWNO; y++) - gl.level.monsters[x][y] = (struct monst *) 0; + svl.level.monsters[x][y] = (struct monst *) 0; for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (mtmp->isshk) set_residency(mtmp, FALSE); - place_monster(mtmp, mtmp->mx, mtmp->my); - if (mtmp->wormno) - place_wsegs(mtmp, NULL); - if (hides_under(mtmp->data) && mtmp->mundetected) - (void) hideunder(mtmp); + if (mtmp->m_id == u.usteed_mid) { + /* steed is kept on fmon list but off the map */ + u.usteed = mtmp; + u.usteed_mid = 0; + } else { + if (mtmp->m_id == u.ustuck_mid) { + set_ustuck(mtmp); + u.ustuck_mid = 0; + } + place_monster(mtmp, mtmp->mx, mtmp->my); + if (mtmp->wormno) + place_wsegs(mtmp, NULL); + if (hides_under(mtmp->data) && mtmp->mundetected) + (void) hideunder(mtmp); + } /* regenerate monsters while on another level */ - if (!u.uz.dlevel) + if (!u.uz.dlevel || program_state.restoring == REST_LEVELS) continue; if (ghostly) { /* reset peaceful/malign relative to new character; @@ -1179,6 +1188,8 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) restdamage(nhfp); rest_regions(nhfp); rest_bubbles(nhfp); /* for water and air; empty marker on other levels */ + load_exclusions(nhfp); + rest_track(nhfp); if (ghostly) { stairway *stway = gs.stairs; @@ -1255,22 +1266,41 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) if (ghostly) clear_id_mapping(); + 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; + plbuf[0] = '\0'; if (nhfp->structlevel) { - (void) read(nhfp->fd, (genericptr_t) &pltmpsiz, sizeof(pltmpsiz)); + (void) read(nhfp->fd, (genericptr_t) &pltmpsiz, sizeof pltmpsiz); + /* pltmpsiz should now be PL_NSIZ_PLUS */ (void) read(nhfp->fd, (genericptr_t) plbuf, 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 */ -static void +staticfn void rest_bubbles(NHFILE *nhfp) { xint8 bbubbly; @@ -1286,7 +1316,7 @@ rest_bubbles(NHFILE *nhfp) restore_waterlevel(nhfp); } -static void +staticfn void restore_gamelog(NHFILE *nhfp) { int slen = 0; @@ -1309,7 +1339,7 @@ restore_gamelog(NHFILE *nhfp) } } -static void +staticfn void restore_msghistory(NHFILE *nhfp) { int msgsize = 0, msgcount = 0; @@ -1334,7 +1364,7 @@ restore_msghistory(NHFILE *nhfp) } /* Clear all structures for object and monster ID mapping. */ -static void +staticfn void clear_id_mapping(void) { struct bucket *curr; @@ -1347,7 +1377,7 @@ clear_id_mapping(void) } /* Add a mapping to the ID map. */ -static void +staticfn void add_id_mapping(unsigned int gid, unsigned int nid) { int idx; @@ -1397,7 +1427,7 @@ lookup_id_mapping(unsigned int gid, unsigned int *nidp) return FALSE; } -static void +staticfn void reset_oattached_mids(boolean ghostly) { struct obj *otmp; @@ -1422,19 +1452,21 @@ reset_oattached_mids(boolean ghostly) #ifdef SELECTSAVED /* put up a menu listing each character from this player's saved games; - returns 1: use gp.plname[], 0: new game, -1: quit */ + returns 1: use svp.plname[], 0: new game, -1: quit */ int restore_menu( - winid bannerwin) /* if not WIN_ERR, clear window and show copyright in menu */ + winid bannerwin) /* if not WIN_ERR, clear window + * and show copyright in 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 = 0; + int clr = NO_COLOR; - *gp.plname = '\0'; + *svp.plname = '\0'; saved = get_saved_games(); /* array of character names */ if (saved && *saved) { tmpwin = create_nhwindow(NHW_MENU); @@ -1445,32 +1477,47 @@ restore_menu( clear_nhwindow(bannerwin); /* COPYRIGHT_BANNER_[ABCD] */ for (k = 1; k <= 4; ++k) - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, copyright_banner_line(k), MENU_ITEMFLAGS_NONE); - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, "", - MENU_ITEMFLAGS_NONE); + add_menu_str(tmpwin, copyright_banner_line(k)); + 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; } - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "Select one of your saved games", MENU_ITEMFLAGS_NONE); 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) { ch = chosen_game->item.a_int; if (ch > 0) - Strcpy(gp.plname, saved[ch - 1]); + Strcpy(svp.plname, saved[ch - 1]); else if (ch < 0) ++ch; /* -1 -> 0 (new game), -2 -> -1 (quit) */ free((genericptr_t) chosen_game); @@ -1493,7 +1540,7 @@ restore_menu( #endif /* SELECTSAVED */ int -validate(NHFILE *nhfp, const char *name) +validate(NHFILE *nhfp, const char *name, boolean without_waitsynch_perfile) { readLenType rlen = 0; struct savefile_info sfi; @@ -1502,6 +1549,8 @@ validate(NHFILE *nhfp, const char *name) if (nhfp->structlevel) utdflags |= UTD_CHECKSIZES; + if (without_waitsynch_perfile) + utdflags |= UTD_WITHOUT_WAITSYNCH_PERFILE; if (!(reslt = uptodate(nhfp, name, utdflags))) return 1; diff --git a/src/rip.c b/src/rip.c index 8b109fb7cd..f20af98f74 100644 --- a/src/rip.c +++ b/src/rip.c @@ -20,7 +20,7 @@ #endif #ifdef TEXT_TOMBSTONE -static void center(int, char *); +staticfn void center(int, char *); #ifndef NH320_DEDICATION /* A normal tombstone for end of game display. */ @@ -59,7 +59,8 @@ static const char *const rip_txt[] = { " | | | Ascended |", " | 1001 | | |", " * | * * * | * * | * * * | *", - " _____)/\\|\\__//(\\/(/\\)/\\//\\/|_)________)/|\\\\_/_/(\\/(/\\)/\\/\\/|_)____", + (" _____)/\\|\\__//(\\/(/\\)/\\//\\/|_)___" + "_____)/|\\\\_/_/(\\/(/\\)/\\/\\/|_)____"), 0 }; #define STONE_LINE_CENT 19 /* char[] element of center of stone face */ @@ -71,10 +72,10 @@ static const char *const rip_txt[] = { #define DEATH_LINE 8 /* *char[] line # for death description */ #define YEAR_LINE 12 /* *char[] line # for year */ -static void +staticfn void center(int line, char *text) { - register char *ip, *op; + char *ip, *op; ip = text; op = &gr.rip[line][STONE_LINE_CENT - ((strlen(text) + 1) >> 1)]; while (*ip) @@ -84,10 +85,10 @@ center(int line, char *text) void genl_outrip(winid tmpwin, int how, time_t when) { - register char **dp; - register char *dpx; + char **dp; + char *dpx; char buf[BUFSZ]; - register int x; + int x; int line, year; long cash; @@ -97,7 +98,7 @@ genl_outrip(winid tmpwin, int how, time_t when) dp[x] = (char *) 0; /* Put name on stone */ - Sprintf(buf, "%.*s", (int) STONE_LINE_LEN, gp.plname); + Sprintf(buf, "%.*s", (int) STONE_LINE_LEN, svp.plname); center(NAME_LINE, buf); /* Put $ on stone */ diff --git a/src/rnd.c b/src/rnd.c index b6a038a427..30adcd4d69 100644 --- a/src/rnd.c +++ b/src/rnd.c @@ -7,7 +7,9 @@ #ifdef USE_ISAAC64 #include "isaac64.h" -static int whichrng(int (*fn)(int)); +staticfn int whichrng(int (*fn)(int)); +staticfn int RND(int); +staticfn void set_random(unsigned long, int (*)(int)); #if 0 static isaac64_ctx rng_state; @@ -26,7 +28,7 @@ static struct rnglist_t rnglist[] = { { rn2_on_display_rng, FALSE, { 0 } }, /* DISP */ }; -static int +staticfn int whichrng(int (*fn)(int)) { int i; @@ -55,7 +57,7 @@ init_isaac64(unsigned long seed, int (*fn)(int)) (int) sizeof seed); } -static int +staticfn int RND(int x) { return (isaac64_next_uint64(&rnglist[CORE].rng_state) % x); @@ -65,7 +67,7 @@ RND(int x) used in cases where the answer doesn't affect gameplay and we don't want to give users easy control over the main RNG sequence. */ int -rn2_on_display_rng(register int x) +rn2_on_display_rng(int x) { return (isaac64_next_uint64(&rnglist[DISP].rng_state) % x); } @@ -80,17 +82,17 @@ rn2_on_display_rng(register int x) #define RND(x) ((int) ((Rand() >> 3) % (x))) #endif int -rn2_on_display_rng(register int x) +rn2_on_display_rng(int x) { static unsigned seed = 1; seed *= 2739110765; - return (int)((seed >> 16) % (unsigned)x); + return (int) ((seed >> 16) % (unsigned) x); } #endif /* USE_ISAAC64 */ /* 0 <= rn2(x) < x */ int -rn2(register int x) +rn2(int x) { if (x <= 0) { impossible("rn2(%d) attempted, returning 0", x); @@ -103,9 +105,9 @@ rn2(register int x) /* 0 <= rnl(x) < x; sometimes subtracting Luck; good luck approaches 0, bad luck approaches (x-1) */ int -rnl(register int x) +rnl(int x) { - register int i, adjustment; + int i, adjustment; if (x <= 0) { impossible("rnl(%d) attempted, returning 0", x); @@ -144,7 +146,7 @@ rnl(register int x) /* 1 <= rnd(x) <= x */ int -rnd(register int x) +rnd(int x) { if (x <= 0) { impossible("rnd(%d) attempted, returning 1", x); @@ -155,16 +157,16 @@ rnd(register int x) } int -rnd_on_display_rng(register int x) +rnd_on_display_rng(int x) { return rn2_on_display_rng(x) + 1; } /* d(N,X) == NdX == dX+dX+...+dX N times; n <= d(n,x) <= (n*x) */ int -d(register int n, register int x) +d(int n, int x) { - register int tmp = n; + int tmp = n; if (x < 0 || n < 0 || (x == 0 && n != 0)) { impossible("d(%d,%d) attempted, returning 1", n, x); @@ -182,9 +184,9 @@ d(register int n, register int x) * etc. */ int -rne(register int x) +rne(int x) { - register int tmp; + int tmp; tmp = 1; while (tmp < 10 && !rn2(x)) @@ -196,8 +198,8 @@ rne(register int x) int rnz(int i) { - register long x = (long) i; - register long tmp = 1000L; + long x = (long) i; + long tmp = 1000L; tmp += rn2(1000); tmp *= rne(4); @@ -243,4 +245,130 @@ rnf(int numerator, int denominator) /**< @returns (rnf(n,d) < n/d) */ return rn2(denominator) < numerator; } +/* Sets the seed for the random number generator */ +#ifdef USE_ISAAC64 + +staticfn void +set_random(unsigned long seed, + int (*fn)(int)) +{ + init_isaac64(seed, fn); +} + +#else /* USE_ISAAC64 */ + +/*ARGSUSED*/ +staticfn void +set_random(unsigned long seed, + int (*fn)(int) UNUSED) +{ + /* + * The types are different enough here that sweeping the different + * routine names into one via #defines is even more confusing. + */ +# ifdef RANDOM /* srandom() from sys/share/random.c */ + srandom((unsigned int) seed); +# else +# if defined(__APPLE__) || defined(BSD) || defined(LINUX) \ + || defined(ULTRIX) || defined(CYGWIN32) /* system srandom() */ +# if defined(BSD) && !defined(POSIX_TYPES) && defined(SUNOS4) + (void) +# endif + srandom((int) seed); +# else +# ifdef UNIX /* system srand48() */ + srand48((long) seed); +# else /* poor quality system routine */ + srand((int) seed); +# endif +# endif +# endif +} +#endif /* USE_ISAAC64 */ + +/* An appropriate version of this must always be provided in + port-specific code somewhere. It returns a number suitable + as seed for the random number generator */ +extern unsigned long sys_random_seed(void); + +/* + * Initializes the random number generator. + * Only call once. + */ +void +init_random(int (*fn)(int)) +{ + set_random(sys_random_seed(), fn); +} + +/* Reshuffles the random number generator. */ +void +reseed_random(int (*fn)(int)) +{ + /* only reseed if we are certain that the seed generation is unguessable + * by the players. */ + if (has_strong_rngseed) + init_random(fn); +} + +/* randomize the given list of numbers 0 <= i < count */ +void +shuffle_int_array(int *indices, int count) +{ + int i, iswap, temp; + + for (i = count - 1; i > 0; i--) { + if ((iswap = rn2(i + 1)) == i) + continue; + temp = indices[i]; + indices[i] = indices[iswap]; + indices[iswap] = temp; + } +} + +/* Deterministic hash of three coordinates (intended to be x, y, and z, but + * they don't actually have to be). In a lot of cases, z should probably also + * be ledger_no(&u.uz) so that the "z" is actually unique among levels; mere + * depth is not unique due to having levels in multiple branches at the same + * depth. + * Throws ubirthday and sysopt.serverseed into the hash so that the hash should + * be (practically) unique among the same coordinates in different games, so the + * player shouldn't be able to get the result out of the visible game state. + * (Note that sysopt.serverseed is the value of SERVERSEED plus a random number + * generated at game start). + */ +unsigned int +coord_hash(int x, int y, int z) +{ + const int magic_number = 0x45d9f3b; + /* use Cantor pairing to reduce (x,y) to a unique number */ + unsigned int a = ((x+y) * (x+y+1) / 2) + x + z + ubirthday + + sysopt.serverseed; + a = a * magic_number; + a = ((a >> 16) ^ a) * magic_number; + a = ((a >> 16) ^ a); + return a; +} + +/* Deterministic hash of a single number. Useful for hashes of non-coordinate + * numbers such as object or monster ids. */ +unsigned int +hash1(int x) +{ + /* wrap around coord_hash; ignore Cantor coordinate pairing */ + return coord_hash(0, 0, x); +} + +/* hash1(), but returns a positive int, for various use cases that convert it to + * int and which it would be unsafe to just use hash1 and possibly have that + * value converted to negative. */ +int +int_hash1(int x) +{ + unsigned int hash = hash1(x); + while (hash > INT_MAX) + hash /= 2; + return (int) hash; +} + /*rnd.c*/ diff --git a/src/role.c b/src/role.c index 2462702908..60c015cbd7 100644 --- a/src/role.c +++ b/src/role.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 role.c $NHDT-Date: 1596498206 2020/08/03 23:43:26 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.71 $ */ +/* NetHack 3.7 role.c $NHDT-Date: 1711734229 2024/03/29 17:43:49 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.100 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985-1999. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -465,7 +465,7 @@ const struct Role roles[NUM_ROLES+1] = { 0, 50 }, /* Array terminator */ - { { 0, 0 } } + UNDEFINED_ROLE, }; /* Table of all races */ @@ -572,7 +572,7 @@ const struct Race races[NUM_RACES+1] = { { 1, 0, 1, 0, 1, 0 } /* Energy */ }, /* Array terminator */ - { 0, 0, 0, 0, { 0, 0 }, NON_PM } + UNDEFINED_RACE, }; /* Table of all genders */ @@ -599,10 +599,10 @@ const struct Align aligns[] = { { "evil", "unaligned", "Una", 0, A_NONE } }; -static int randrole_filtered(void); -static char *promptsep(char *, int); -static int role_gendercount(int); -static int race_alignmentcount(int); +staticfn int randrole_filtered(void); +staticfn char *promptsep(char *, int); +staticfn int role_gendercount(int); +staticfn int race_alignmentcount(int); /* used by str2XXX() */ static char NEARDATA randomstr[] = "random"; @@ -610,7 +610,7 @@ static char NEARDATA randomstr[] = "random"; boolean validrole(int rolenum) { - return (boolean) (rolenum >= 0 && rolenum < SIZE(roles) - 1); + return (boolean) (IndexOkT(rolenum, roles)); } int @@ -625,7 +625,7 @@ randrole(boolean for_display) return res; } -static int +staticfn int randrole_filtered(void) { int i, n = 0, set[SIZE(roles)]; @@ -676,7 +676,7 @@ boolean validrace(int rolenum, int racenum) { /* Assumes validrole */ - return (boolean) (racenum >= 0 && racenum < SIZE(races) - 1 + return (boolean) (IndexOkT(racenum, races) && (roles[rolenum].allow & races[racenum].allow & ROLE_RACEMASK)); } @@ -903,11 +903,11 @@ ok_role(int rolenum, int racenum, int gendnum, int alignnum) int i; short allow; - if (rolenum >= 0 && rolenum < SIZE(roles) - 1) { + if (IndexOkT(rolenum, roles)) { if (gr.rfilter.roles[rolenum]) return FALSE; allow = roles[rolenum].allow; - if (racenum >= 0 && racenum < SIZE(races) - 1 + if (IndexOkT(racenum, races) && !(allow & races[racenum].allow & ROLE_RACEMASK)) return FALSE; if (gendnum >= 0 && gendnum < ROLE_GENDERS @@ -923,7 +923,7 @@ ok_role(int rolenum, int racenum, int gendnum, int alignnum) if (gr.rfilter.roles[i]) continue; allow = roles[i].allow; - if (racenum >= 0 && racenum < SIZE(races) - 1 + if (IndexOkT(racenum, races) && !(allow & races[racenum].allow & ROLE_RACEMASK)) continue; if (gendnum >= 0 && gendnum < ROLE_GENDERS @@ -969,11 +969,11 @@ ok_race(int rolenum, int racenum, int gendnum, int alignnum) int i; short allow; - if (racenum >= 0 && racenum < SIZE(races) - 1) { + if (IndexOkT(racenum, races)) { if (gr.rfilter.mask & races[racenum].selfmask) return FALSE; allow = races[racenum].allow; - if (rolenum >= 0 && rolenum < SIZE(roles) - 1 + if (IndexOkT(rolenum, roles) && !(allow & roles[rolenum].allow & ROLE_RACEMASK)) return FALSE; if (gendnum >= 0 && gendnum < ROLE_GENDERS @@ -989,7 +989,7 @@ ok_race(int rolenum, int racenum, int gendnum, int alignnum) if (gr.rfilter.mask & races[i].selfmask) continue; allow = races[i].allow; - if (rolenum >= 0 && rolenum < SIZE(roles) - 1 + if (IndexOkT(rolenum, roles) && !(allow & roles[rolenum].allow & ROLE_RACEMASK)) continue; if (gendnum >= 0 && gendnum < ROLE_GENDERS @@ -1043,10 +1043,10 @@ ok_gend(int rolenum, int racenum, int gendnum, int alignnum UNUSED) if (gr.rfilter.mask & genders[gendnum].allow) return FALSE; allow = genders[gendnum].allow; - if (rolenum >= 0 && rolenum < SIZE(roles) - 1 + if (IndexOkT(rolenum, roles) && !(allow & roles[rolenum].allow & ROLE_GENDMASK)) return FALSE; - if (racenum >= 0 && racenum < SIZE(races) - 1 + if (IndexOkT(racenum, races) && !(allow & races[racenum].allow & ROLE_GENDMASK)) return FALSE; return TRUE; @@ -1056,10 +1056,10 @@ ok_gend(int rolenum, int racenum, int gendnum, int alignnum UNUSED) if (gr.rfilter.mask & genders[i].allow) continue; allow = genders[i].allow; - if (rolenum >= 0 && rolenum < SIZE(roles) - 1 + if (IndexOkT(rolenum, roles) && !(allow & roles[rolenum].allow & ROLE_GENDMASK)) continue; - if (racenum >= 0 && racenum < SIZE(races) - 1 + if (IndexOkT(racenum, races) && !(allow & races[racenum].allow & ROLE_GENDMASK)) continue; return TRUE; @@ -1108,10 +1108,10 @@ ok_align(int rolenum, int racenum, int gendnum UNUSED, int alignnum) if (gr.rfilter.mask & aligns[alignnum].allow) return FALSE; allow = aligns[alignnum].allow; - if (rolenum >= 0 && rolenum < SIZE(roles) - 1 + if (IndexOkT(rolenum, roles) && !(allow & roles[rolenum].allow & ROLE_ALIGNMASK)) return FALSE; - if (racenum >= 0 && racenum < SIZE(races) - 1 + if (IndexOkT(racenum, races) && !(allow & races[racenum].allow & ROLE_ALIGNMASK)) return FALSE; return TRUE; @@ -1121,10 +1121,10 @@ ok_align(int rolenum, int racenum, int gendnum UNUSED, int alignnum) if (gr.rfilter.mask & aligns[i].allow) continue; allow = aligns[i].allow; - if (rolenum >= 0 && rolenum < SIZE(roles) - 1 + if (IndexOkT(rolenum, roles) && !(allow & roles[rolenum].allow & ROLE_ALIGNMASK)) continue; - if (racenum >= 0 && racenum < SIZE(races) - 1 + if (IndexOkT(racenum, races) && !(allow & races[racenum].allow & ROLE_ALIGNMASK)) continue; return TRUE; @@ -1236,7 +1236,7 @@ gotrolefilter(void) if (gr.rfilter.mask) return TRUE; - for (i = 0; i < SIZE(roles); ++i) + for (i = 0; i < SIZE(roles) - 1; ++i) if (gr.rfilter.roles[i]) return TRUE; return FALSE; @@ -1252,25 +1252,25 @@ rolefilterstring(char *outbuf, int which) outbuf[0] = outbuf[1] = '\0'; switch (which) { case RS_ROLE: - for (i = 0; i < SIZE(roles); ++i) { + for (i = 0; i < SIZE(roles) - 1; ++i) { if (gr.rfilter.roles[i]) Sprintf(eos(outbuf), " !%.3s", roles[i].name.m); } break; case RS_RACE: - for (i = 0; i < SIZE(races); ++i) { + for (i = 0; i < SIZE(races) - 1; ++i) { if ((gr.rfilter.mask & races[i].selfmask) != 0) Sprintf(eos(outbuf), " !%s", races[i].noun); } break; case RS_GENDER: - for (i = 0; i < SIZE(genders); ++i) { + for (i = 0; i < SIZE(genders) - 1; ++i) { if ((gr.rfilter.mask & genders[i].allow) != 0) Sprintf(eos(outbuf), " !%s", genders[i].adj); } break; case RS_ALGNMNT: - for (i = 0; i < SIZE(aligns); ++i) { + for (i = 0; i < SIZE(aligns) - 1; ++i) { if ((gr.rfilter.mask & aligns[i].allow) != 0) Sprintf(eos(outbuf), " !%s", aligns[i].adj); } @@ -1292,9 +1292,10 @@ 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); ++i) + for (i = 0; i < SIZE(roles) - 1; ++i) gr.rfilter.roles[i] = FALSE; break; case RS_RACE: @@ -1309,7 +1310,7 @@ clearrolefilter(int which) } } -static char * +staticfn char * promptsep(char *buf, int num_post_attribs) { const char *conjuct = "and "; @@ -1324,7 +1325,7 @@ promptsep(char *buf, int num_post_attribs) return buf; } -static int +staticfn int role_gendercount(int rolenum) { int gendcount = 0; @@ -1340,7 +1341,7 @@ role_gendercount(int rolenum) return gendcount; } -static int +staticfn int race_alignmentcount(int racenum) { int aligncount = 0; @@ -1469,6 +1470,7 @@ root_plselection_prompt( /* || */ if (validrole(rolenum)) { + assert(IndexOkT(rolenum, roles)); if (donefirst) Strcat(buf, " "); if (gendnum != ROLE_NONE) { @@ -1598,31 +1600,32 @@ plnamesuffix(void) /* some generic user names will be ignored in favor of prompting */ if (sysopt.genericusers) { if (*sysopt.genericusers == '*') { - gp.plname[0] = '\0'; + svp.plname[0] = '\0'; } else { /* need to ignore appended '-role-race-gender-alignment'; 'plnamelen' is non-zero when dealing with plname[] value that contains a username with dash(es) in it and is usually 0 */ - i = ((eptr = strchr(gp.plname + gp.plnamelen, '-')) != 0) - ? (int) (eptr - gp.plname) - : (int) Strlen(gp.plname); + i = ((eptr = strchr(svp.plname + gp.plnamelen, '-')) != 0) + ? (int) (eptr - svp.plname) + : (int) Strlen(svp.plname); /* look for plname[] in the 'genericusers' space-separated list */ - if (findword(sysopt.genericusers, gp.plname, i, FALSE)) + if (findword(sysopt.genericusers, svp.plname, i, FALSE)) /* it's generic; remove it so that askname() will be called */ - gp.plname[0] = '\0'; + svp.plname[0] = '\0'; } - if (!gp.plname[0]) + if (!svp.plname[0]) gp.plnamelen = 0; } do { - if (!gp.plname[0]) { - askname(); /* fill gp.plname[] if necessary, or set defer_plname */ + if (!svp.plname[0]) { + askname(); /* fill svp.plname[] if necessary, or set + * defer_plname */ gp.plnamelen = 0; /* plname[] might have -role-race-&c attached */ } /* Look for tokens delimited by '-' */ - sptr = gp.plname + gp.plnamelen; + sptr = svp.plname + gp.plnamelen; if ((eptr = strchr(sptr, '-')) != (char *) 0) *eptr++ = '\0'; while (eptr) { @@ -1641,10 +1644,10 @@ plnamesuffix(void) else if ((i = str2align(sptr)) != ROLE_NONE) flags.initalign = i; } - } while (!gp.plname[0] && !iflags.defer_plname); + } while (!svp.plname[0] && !iflags.defer_plname); - /* commas in the gp.plname confuse the record file, convert to spaces */ - (void) strNsubst(gp.plname, ",", " ", 0); + /* commas in the svp.plname confuse the record file, convert to spaces */ + (void) strNsubst(svp.plname, ",", " ", 0); } /* show current settings for name, role, race, gender, and alignment @@ -1663,10 +1666,12 @@ role_selection_prolog(int which, winid where) gend = flags.initgend; a = flags.initalign; if (r >= 0) { + assert(IndexOkT(r, roles)); allowmask = roles[r].allow; if ((allowmask & ROLE_RACEMASK) == MH_HUMAN) c = 0; /* races[human] */ - else if (c >= 0 && !(allowmask & ROLE_RACEMASK & races[c].allow)) + else if (IndexOkT(c, races) + && !(allowmask & ROLE_RACEMASK & races[c].allow)) c = ROLE_RANDOM; if ((allowmask & ROLE_GENDMASK) == ROLE_MALE) gend = 0; /* role forces male (hypothetical) */ @@ -1677,16 +1682,17 @@ role_selection_prolog(int which, winid where) else if ((allowmask & ROLE_ALIGNMASK) == AM_NEUTRAL) a = 1; /* aligns[neutral] */ else if ((allowmask & ROLE_ALIGNMASK) == AM_CHAOTIC) - a = 2; /* alings[chaotic] */ + a = 2; /* aligns[chaotic] */ } if (c >= 0) { + assert(IndexOkT(c, races)); allowmask = races[c].allow; if ((allowmask & ROLE_ALIGNMASK) == AM_LAWFUL) a = 0; /* aligns[lawful] */ else if ((allowmask & ROLE_ALIGNMASK) == AM_NEUTRAL) a = 1; /* aligns[neutral] */ else if ((allowmask & ROLE_ALIGNMASK) == AM_CHAOTIC) - a = 2; /* alings[chaotic] */ + a = 2; /* aligns[chaotic] */ /* [c never forces gender] */ } /* [g and a don't constrain anything sufficiently @@ -1694,9 +1700,11 @@ role_selection_prolog(int which, winid where) Sprintf(buf, "%12s ", "name:"); Strcat(buf, (which == RS_NAME) ? choosing - : !*gp.plname ? not_yet : gp.plname); + : !*svp.plname ? not_yet : svp.plname); putstr(where, 0, buf); Sprintf(buf, "%12s ", "role:"); + assert(which == RS_ROLE || r == ROLE_NONE || r == ROLE_RANDOM + || IndexOkT(r, roles)); Strcat(buf, (which == RS_ROLE) ? choosing : (r == ROLE_NONE) ? not_yet : (r == ROLE_RANDOM) ? rand_choice @@ -1712,6 +1720,8 @@ role_selection_prolog(int which, winid where) } putstr(where, 0, buf); Sprintf(buf, "%12s ", "race:"); + assert(which == RS_RACE || c == ROLE_NONE || c == ROLE_RANDOM + || IndexOkT(c, races)); Strcat(buf, (which == RS_RACE) ? choosing : (c == ROLE_NONE) ? not_yet : (c == ROLE_RANDOM) ? rand_choice @@ -1746,7 +1756,7 @@ role_menu_extra(int which, winid where, boolean preselect) char buf[BUFSZ]; const char *what = 0, *constrainer = 0, *forcedvalue = 0; int f = 0, r, c, gend, a, i, allowmask; - int clr = 0; + int clr = NO_COLOR; r = flags.initrole; c = flags.initrace; @@ -1757,10 +1767,10 @@ role_menu_extra(int which, winid where, boolean preselect) case RS_ROLE: what = "role"; f = r; - for (i = 0; i < SIZE(roles); ++i) + for (i = 0; i < SIZE(roles) - 1; ++i) if (i != f && !gr.rfilter.roles[i]) break; - if (i == SIZE(roles)) { + if (i == SIZE(roles) - 1) { constrainer = "filter"; forcedvalue = "role"; } @@ -1776,8 +1786,8 @@ role_menu_extra(int which, winid where, boolean preselect) if (c >= 0) { constrainer = "role"; forcedvalue = races[c].noun; - } else if (f >= 0 - && (allowmask & ~gr.rfilter.mask) == races[f].selfmask) { + } else if (f >= 0 && ((allowmask & ~gr.rfilter.mask) + == races[f].selfmask)) { /* if there is only one race choice available due to user options disallowing others, race menu entry is disabled */ constrainer = "filter"; @@ -1798,8 +1808,8 @@ role_menu_extra(int which, winid where, boolean preselect) if (gend >= 0) { constrainer = "role"; forcedvalue = genders[gend].adj; - } else if (f >= 0 - && (allowmask & ~gr.rfilter.mask) == genders[f].allow) { + } else if (f >= 0 && ((allowmask & ~gr.rfilter.mask) + == genders[f].allow)) { /* if there is only one gender choice available due to user options disallowing other, gender menu entry is disabled */ constrainer = "filter"; @@ -1850,8 +1860,7 @@ role_menu_extra(int which, winid where, boolean preselect) any.a_int = 0; /* use four spaces of padding to fake a grayed out menu choice */ Sprintf(buf, "%4s%s forces %s", "", constrainer, forcedvalue); - add_menu(where, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, buf, - MENU_ITEMFLAGS_NONE); + add_menu_str(where, buf); } else if (what) { any.a_int = RS_menu_arg(which); Sprintf(buf, "Pick%s %s first", (f >= 0) ? " another" : "", what); @@ -1911,15 +1920,15 @@ role_init(void) /* Check for a valid role. Try flags.initrole first. */ if (!validrole(flags.initrole)) { /* Try the player letter second */ - if ((flags.initrole = str2role(gp.pl_character)) < 0) + if ((flags.initrole = str2role(svp.pl_character)) < 0) /* None specified; pick a random role */ flags.initrole = randrole_filtered(); } /* We now have a valid role index. Copy the role name back. */ /* This should become OBSOLETE */ - Strcpy(gp.pl_character, roles[flags.initrole].name.m); - gp.pl_character[PL_CSIZ - 1] = '\0'; + Strcpy(svp.pl_character, roles[flags.initrole].name.m); + svp.pl_character[PL_CSIZ - 1] = '\0'; /* Check for a valid race */ if (!validrace(flags.initrole, flags.initrace)) @@ -1954,7 +1963,7 @@ role_init(void) pm->maligntyp = alignmnt * 3; /* if gender is random, we choose it now instead of waiting until the leader monster is created */ - gq.quest_status.ldrgend = + svq.quest_status.ldrgend = is_neuter(pm) ? 2 : is_female(pm) ? 1 : is_male(pm) ? 0 : (rn2(100) < 50); @@ -1977,7 +1986,7 @@ role_init(void) pm->mflags3 |= M3_WANTSARTI | M3_WAITFORU; /* if gender is random, we choose it now instead of waiting until the nemesis monster is created */ - gq.quest_status.nemgend = is_neuter(pm) ? 2 : is_female(pm) ? 1 + svq.quest_status.nemgend = is_neuter(pm) ? 2 : is_female(pm) ? 1 : is_male(pm) ? 0 : (rn2(100) < 50); } @@ -2003,7 +2012,7 @@ role_init(void) gu.urole.cgod = roles[flags.pantheon].cgod; } /* 0 or 1; no gods are neuter, nor is gender randomized */ - gq.quest_status.godgend = !strcmpi(align_gtitle(alignmnt), "goddess"); + svq.quest_status.godgend = !strcmpi(align_gtitle(alignmnt), "goddess"); #if 0 /* @@ -2011,7 +2020,7 @@ role_init(void) * place where it actually matters for the hero is in set_uasmon() * and that can use mons[race] rather than mons[role] for this * particular property. Despite the comment, it is checked--where - * needed--via instrinsic 'Infravision' which set_uasmon() manages. + * needed--via intrinsic 'Infravision' which set_uasmon() manages. */ /* Fix up infravision */ if (mons[gu.urace.mnum].mflags3 & M3_INFRAVISION) { @@ -2035,7 +2044,7 @@ role_init(void) } const char * -Hello(struct monst* mtmp) +Hello(struct monst *mtmp) { switch (Role_switch) { case PM_KNIGHT: @@ -2079,13 +2088,14 @@ Goodbye(void) } /* if pmindex is any player race (not necessarily the hero's), - return a pointer to the races[] entry for it */ + return a pointer to the races[] entry for it; if pmindex is for some + other type of monster which isn't a player race, return Null */ const struct Race * character_race(short pmindex) { const struct Race *r; - for (r = races; r->mnum >= LOW_PM; ++r) + for (r = races; r->noun != NULL; ++r) if (r->mnum == pmindex) return r; return (const struct Race *) NULL; @@ -2105,16 +2115,16 @@ 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] */ -static boolean reset_role_filtering(void); -static winid plsel_startmenu(int, int); -static int maybe_skip_seps(int, int); -static void setup_rolemenu(winid, boolean, int, int, int); -static void setup_racemenu(winid, boolean, int, int, int); -static void setup_gendmenu(winid, boolean, int, int, int); -static void setup_algnmenu(winid, boolean, int, int, int); +staticfn boolean reset_role_filtering(void); +staticfn winid plsel_startmenu(int, int); +staticfn int maybe_skip_seps(int, int); +staticfn void setup_rolemenu(winid, boolean, int, int, int); +staticfn void setup_racemenu(winid, boolean, int, int, int); +staticfn void setup_gendmenu(winid, boolean, int, int, int); +staticfn void setup_algnmenu(winid, boolean, int, int, int); /* try to reduce clutter in the code below... */ #define ROLE flags.initrole @@ -2132,11 +2142,11 @@ genl_player_setup(int screenheight) boolean getconfirmation, picksomething; winid win = WIN_ERR; menu_item *selected = 0; - int clr = 0; + int clr = NO_COLOR; char pick4u = 'n'; int result = 0; /* assume failure (player chooses to 'quit') */ - gp.program_state.in_role_selection++; /* affects tty menu cleanup */ + program_state.in_role_selection++; /* affects tty menu cleanup */ /* Used to avoid "Is this ok?" if player has already specified all * four facets of role. * Note that rigid_role_checks might force any unspecified facets to @@ -2235,8 +2245,7 @@ genl_player_setup(int screenheight) role_menu_extra(ROLE_RANDOM, win, TRUE); any = cg.zeroany; /* separator, not a choice */ if (excess < 1 || excess > 2) - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); + add_menu_str(win, ""); role_menu_extra(RS_RACE, win, FALSE); role_menu_extra(RS_GENDER, win, FALSE); role_menu_extra(RS_ALGNMNT, win, FALSE); @@ -2330,8 +2339,7 @@ genl_player_setup(int screenheight) /* add miscellaneous menu entries */ role_menu_extra(ROLE_RANDOM, win, TRUE); any.a_int = 0; /* separator, not a choice */ - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); + add_menu_str(win, ""); role_menu_extra(RS_ROLE, win, FALSE); role_menu_extra(RS_GENDER, win, FALSE); role_menu_extra(RS_ALGNMNT, win, FALSE); @@ -2419,8 +2427,7 @@ genl_player_setup(int screenheight) /* add miscellaneous menu entries */ role_menu_extra(ROLE_RANDOM, win, TRUE); any.a_int = 0; /* separator, not a choice */ - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); + add_menu_str(win, ""); role_menu_extra(RS_ROLE, win, FALSE); role_menu_extra(RS_RACE, win, FALSE); role_menu_extra(RS_ALGNMNT, win, FALSE); @@ -2470,7 +2477,9 @@ genl_player_setup(int screenheight) } /* picking gender */ if (nextpick == RS_ALGNMNT) { - nextpick = (ROLE < 0) ? RS_ROLE : (RACE < 0) ? RS_RACE : RS_GENDER; + nextpick = (ROLE < 0) ? RS_ROLE + : (RACE < 0) ? RS_RACE + : RS_GENDER; /* Select an alignment, if necessary; force compatibility with role/race/gender. */ if (ALGN < 0 || !validalign(ROLE, RACE, ALGN)) { @@ -2504,8 +2513,7 @@ genl_player_setup(int screenheight) setup_algnmenu(win, TRUE, ROLE, RACE, GEND); role_menu_extra(ROLE_RANDOM, win, TRUE); any.a_int = 0; /* separator, not a choice */ - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); + add_menu_str(win, ""); role_menu_extra(RS_ROLE, win, FALSE); role_menu_extra(RS_RACE, win, FALSE); role_menu_extra(RS_GENDER, win, FALSE); @@ -2588,7 +2596,8 @@ genl_player_setup(int screenheight) if (iflags.renameallowed) { any.a_int = 3; add_menu(win, &nul_glyphinfo, &any, 'a', 0, ATR_NONE, - clr, "Not yet; choose another name", MENU_ITEMFLAGS_NONE); + clr, "Not yet; choose another name", + MENU_ITEMFLAGS_NONE); } any.a_int = -1; add_menu(win, &nul_glyphinfo, &any, 'q', 0, @@ -2617,10 +2626,12 @@ genl_player_setup(int screenheight) iflags.renameinprogress = TRUE; /* affects main() in unixmain.c */ /* plnamesuffix() can change any or all of ROLE, RACE, GEND, ALGN; we'll override that and honor only the name */ - saveROLE = ROLE, saveRACE = RACE, saveGEND = GEND, saveALGN = ALGN; - gp.plname[0] = '\0'; - plnamesuffix(); /* calls askname() when gp.plname[] is empty */ - ROLE = saveROLE, RACE = saveRACE, GEND = saveGEND, ALGN = saveALGN; + saveROLE = ROLE, saveRACE = RACE, + saveGEND = GEND, saveALGN = ALGN; + svp.plname[0] = '\0'; + plnamesuffix(); /* calls askname() when svp.plname[] is empty */ + ROLE = saveROLE, RACE = saveRACE, + GEND = saveGEND, ALGN = saveALGN; break; /* getconfirmation is still True */ } case 2: /* 'n' */ @@ -2641,44 +2652,35 @@ genl_player_setup(int screenheight) result = 1; setup_done: - gp.program_state.in_role_selection--; + program_state.in_role_selection--; return result; } -static boolean +staticfn boolean reset_role_filtering(void) { winid win; - anything any; - int i, n, clr = 0; + int i, n; char filterprompt[QBUFSZ]; menu_item *selected = 0; win = create_nhwindow(NHW_MENU); start_menu(win, MENU_BEHAVE_STANDARD); - any = cg.zeroany; /* no extra blank line preceding this entry; end_menu supplies one */ - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, - "Unacceptable roles", MENU_ITEMFLAGS_NONE); + add_menu_str(win, "Unacceptable roles"); setup_rolemenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE); - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "", MENU_ITEMFLAGS_NONE); - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "Unacceptable races", MENU_ITEMFLAGS_NONE); + add_menu_str(win, ""); + add_menu_str(win, "Unacceptable races"); setup_racemenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE); - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "", MENU_ITEMFLAGS_NONE); - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "Unacceptable genders", MENU_ITEMFLAGS_NONE); + add_menu_str(win, ""); + add_menu_str(win, "Unacceptable genders"); setup_gendmenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE); - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "", MENU_ITEMFLAGS_NONE); - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "Unacceptable alignments", MENU_ITEMFLAGS_NONE); + add_menu_str(win, ""); + add_menu_str(win, "Unacceptable alignments"); setup_algnmenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE); Sprintf(filterprompt, "Pick all that apply%s", @@ -2703,7 +2705,7 @@ reset_role_filtering(void) tty-only to tty+curses+? made the role selection menu require two pages on a traditional 24-line tty; that wasn't fair to tty, so squeeze out some blank separator lines from the menu if that will make it fit on one */ -static int +staticfn int maybe_skip_seps(int rows, int aspect) { int i, n = 0; @@ -2732,14 +2734,12 @@ maybe_skip_seps(int rows, int aspect) } /* start a menu; show role aspects specified so far as a header line */ -static winid +staticfn winid plsel_startmenu(int ttyrows, int aspect) { char qbuf[QBUFSZ]; winid win; - anything any; const char *rolename; - int clr = 0; /* whatever aspect was just chosen might force others (Orc => chaotic, Samurai => Human+lawful, Valkyrie => female) */ @@ -2748,7 +2748,7 @@ plsel_startmenu(int ttyrows, int aspect) rolename = (ROLE < 0) ? "" : (GEND == 1 && roles[ROLE].name.f) ? roles[ROLE].name.f : roles[ROLE].name.m; - if (!gp.plname[0] || ROLE < 0 || RACE < 0 || GEND < 0 || ALGN < 0) { + if (!svp.plname[0] || ROLE < 0 || RACE < 0 || GEND < 0 || ALGN < 0) { /* " " */ Sprintf(qbuf, "%.20s %.20s %.20s %.20s", rolename, @@ -2758,7 +2758,7 @@ plsel_startmenu(int ttyrows, int aspect) } else { /* " the " */ Sprintf(qbuf, "%.20s the %.20s %.20s %.20s %.20s", - gp.plname, + svp.plname, aligns[ALGN].adj, genders[GEND].adj, races[RACE].adj, @@ -2770,21 +2770,16 @@ plsel_startmenu(int ttyrows, int aspect) panic("could not create role selection window"); start_menu(win, MENU_BEHAVE_STANDARD); - any = cg.zeroany; - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, - qbuf, MENU_ITEMFLAGS_NONE); + add_menu_str(win, qbuf); /* show polyinit form, if any */ if (Polyinit_mode) { char pbuf[BUFSZ]; - any.a_int = 0; Snprintf(pbuf, sizeof(pbuf), "in a %s polyinit form", pmname(&mons[flags.polyinit_mnum], GEND)); - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, pbuf, - MENU_ITEMFLAGS_NONE); + add_menu_str(win, pbuf); } if (maybe_skip_seps(ttyrows, aspect) != 2) - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, - "", MENU_ITEMFLAGS_NONE); + add_menu_str(win, ""); return win; } @@ -2794,7 +2789,7 @@ plsel_startmenu(int ttyrows, int aspect) #undef ALGN /* add entries a-Archeologist, b-Barbarian, &c to menu being built in 'win' */ -static void +staticfn void setup_rolemenu( winid win, boolean filtering, /* True => exclude filtered roles; @@ -2805,7 +2800,7 @@ setup_rolemenu( int i; boolean role_ok; char thisch, lastch = '\0', rolenamebuf[50]; - int clr = 0; + int clr = NO_COLOR; any = cg.zeroany; /* zero out all bits */ for (i = 0; roles[i].name.m; i++) { @@ -2836,7 +2831,7 @@ setup_rolemenu( } } /* !filtering implies reset_role_filtering() where we want to - mark this role as preseleted if current filter excludes it */ + mark this role as preselected if current filter excludes it */ add_menu(win, &nul_glyphinfo, &any, thisch, 0, ATR_NONE, clr, an(rolenamebuf), (!filtering && !role_ok) @@ -2845,7 +2840,7 @@ setup_rolemenu( } } -static void +staticfn void setup_racemenu( winid win, boolean filtering, @@ -2855,7 +2850,7 @@ setup_racemenu( boolean race_ok; int i; char this_ch; - int clr = 0; + int clr = NO_COLOR; any = cg.zeroany; for (i = 0; races[i].noun; i++) { @@ -2883,7 +2878,7 @@ setup_racemenu( } } -static void +staticfn void setup_gendmenu( winid win, boolean filtering, @@ -2893,7 +2888,7 @@ setup_gendmenu( boolean gend_ok; int i; char this_ch; - int clr = 0; + int clr = NO_COLOR; any = cg.zeroany; for (i = 0; i < ROLE_GENDERS; i++) { @@ -2919,7 +2914,7 @@ setup_gendmenu( } } -static void +staticfn void setup_algnmenu( winid win, boolean filtering, @@ -2929,7 +2924,7 @@ setup_algnmenu( boolean algn_ok; int i; char this_ch; - int clr = 0; + int clr = NO_COLOR; any = cg.zeroany; for (i = 0; i < ROLE_ALIGNS; i++) { diff --git a/src/rumors.c b/src/rumors.c index 38bfef66fb..5048c962da 100644 --- a/src/rumors.c +++ b/src/rumors.c @@ -41,14 +41,14 @@ * and placed there by 'makedefs'. */ -static void unpadline(char *); -static void init_rumors(dlb *); -static char *get_rnd_line(dlb *, char *, unsigned, int (*)(int), +staticfn void unpadline(char *); +staticfn void init_rumors(dlb *); +staticfn char *get_rnd_line(dlb *, char *, unsigned, int (*)(int), long, long, unsigned); -static void init_oracles(dlb *); -static void others_check(const char *ftype, const char *, winid *); -static void couldnt_open_file(const char *); -static void init_CapMons(void); +staticfn void init_oracles(dlb *); +staticfn void others_check(const char *ftype, const char *, winid *); +staticfn void couldnt_open_file(const char *); +staticfn void init_CapMons(void); /* used by CapitalMon(); set up by init_CapMons(), released by free_CapMons(); there's no need for these to be put into 'struct instance_globals g' */ @@ -62,7 +62,7 @@ extern const char bogon_codes[]; /* from do_name.c */ /* makedefs pads short rumors, epitaphs, engravings, and hallucinatory monster names with trailing underscores; strip those off */ -static void +staticfn void unpadline(char *line) { char *p = eos(line); @@ -80,7 +80,7 @@ unpadline(char *line) DISABLE_WARNING_FORMAT_NONLITERAL -static void +staticfn void init_rumors(dlb *fp) { static const char rumors_header[] = "%d,%ld,%lx;%d,%ld,%lx;0,0,%lx\n"; @@ -170,7 +170,7 @@ getrumor( (void) dlb_fclose(rumors); if (count >= 50) impossible("Can't find non-cookie rumor?"); - else if (!gi.in_mklev) /* avoid exercizing wisdom for graffiti */ + else if (!gi.in_mklev) /* avoid exercising wisdom for graffiti */ exercise(A_WIS, (adjtruth > 0)); } else { couldnt_open_file(RUMORFILE); @@ -307,7 +307,7 @@ rumor_check(void) DISABLE_WARNING_FORMAT_NONLITERAL /* 3.7: augments rumors_check(); test 'engrave' or 'epitaph' or 'bogusmon' */ -static void +staticfn void others_check( const char *ftype, /* header: "{Engravings|Epitaphs|Bogus monsters}:" */ const char *fname, /* filename: {ENGRAVEFILE|EPITAPHFILE|BOGUSMONFILE} */ @@ -419,7 +419,7 @@ RESTORE_WARNING_FORMAT_NONLITERAL chosen; however, if padlength is 0, lines following long lines are more likely than average to be picked, and lines after short lines are less likely */ -static char * +staticfn char * get_rnd_line( dlb *fh, /* already opened file */ char *buf, /* output buffer */ @@ -448,7 +448,7 @@ get_rnd_line( return buf; /* 'rumors' is about 3/4 of the way to the limit on a 16-bit config for the whole, roughly 3/8 of the way for either half; all active - configuations these days are at least 32-bits anyway */ + configurations these days are at least 32-bits anyway */ nhassert(filechunksize <= INT_MAX); /* essential for rn2() */ /* @@ -598,6 +598,7 @@ outrumor( return; case BY_COOKIE: pline(fortune_msg); + FALLTHROUGH; /* FALLTHRU */ case BY_PAPER: pline("It reads:"); @@ -606,10 +607,10 @@ outrumor( pline1(line); } -static void +staticfn void init_oracles(dlb *fp) { - register int i; + int i; char line[BUFSZ]; int cnt = 0; @@ -617,7 +618,7 @@ init_oracles(dlb *fp) (void) dlb_fgets(line, sizeof line, fp); /* skip "don't edit" comment*/ (void) dlb_fgets(line, sizeof line, fp); if (sscanf(line, "%5d\n", &cnt) == 1 && cnt > 0) { - go.oracle_cnt = (unsigned) cnt; + svo.oracle_cnt = (unsigned) cnt; go.oracle_loc = (unsigned long *) alloc((unsigned) cnt * sizeof(long)); for (i = 0; i < cnt; i++) { (void) dlb_fgets(line, sizeof line, fp); @@ -632,19 +633,19 @@ save_oracles(NHFILE *nhfp) { if (perform_bwrite(nhfp)) { if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &go.oracle_cnt, - sizeof go.oracle_cnt); - if (go.oracle_cnt) { + 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, - go.oracle_cnt * sizeof (long)); + svo.oracle_cnt * sizeof (long)); } } } if (release_data(nhfp)) { - if (go.oracle_cnt) { + if (svo.oracle_cnt) { free((genericptr_t) go.oracle_loc); - go.oracle_loc = 0, go.oracle_cnt = 0, go.oracle_flg = 0; + go.oracle_loc = 0, svo.oracle_cnt = 0, go.oracle_flg = 0; } } } @@ -653,13 +654,13 @@ void restore_oracles(NHFILE *nhfp) { if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) &go.oracle_cnt, sizeof go.oracle_cnt); + mread(nhfp->fd, (genericptr_t) &svo.oracle_cnt, sizeof svo.oracle_cnt); - if (go.oracle_cnt) { - go.oracle_loc = (unsigned long *) alloc(go.oracle_cnt * sizeof(long)); + 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, - go.oracle_cnt * sizeof (long)); + svo.oracle_cnt * sizeof (long)); } go.oracle_flg = 1; /* no need to call init_oracles() */ } @@ -675,7 +676,7 @@ outoracle(boolean special, boolean delphi) /* early return if we couldn't open ORACLEFILE on previous attempt, or if all the oracularities are already exhausted */ - if (go.oracle_flg < 0 || (go.oracle_flg > 0 && go.oracle_cnt == 0)) + if (go.oracle_flg < 0 || (go.oracle_flg > 0 && svo.oracle_cnt == 0)) return; oracles = dlb_fopen(ORACLEFILE, "r"); @@ -684,17 +685,17 @@ outoracle(boolean special, boolean delphi) if (go.oracle_flg == 0) { /* if this is the first outoracle() */ init_oracles(oracles); go.oracle_flg = 1; - if (go.oracle_cnt == 0) + if (svo.oracle_cnt == 0) goto close_oracles; } /* oracle_loc[0] is the special oracle; oracle_loc[1..oracle_cnt-1] are normal ones */ - if (go.oracle_cnt <= 1 && !special) + if (svo.oracle_cnt <= 1 && !special) goto close_oracles; /*(shouldn't happen)*/ - oracle_idx = special ? 0 : rnd((int) go.oracle_cnt - 1); + oracle_idx = special ? 0 : rnd((int) svo.oracle_cnt - 1); (void) dlb_fseek(oracles, (long) go.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[--go.oracle_cnt]; + go.oracle_loc[oracle_idx] = go.oracle_loc[--svo.oracle_cnt]; tmpwin = create_nhwindow(NHW_TEXT); if (delphi) @@ -758,7 +759,7 @@ doconsult(struct monst *oracl) break; case 'n': if (umoney <= (long) minor_cost /* don't even ask */ - || (go.oracle_cnt == 1 || go.oracle_flg < 0)) + || (svo.oracle_cnt == 1 || go.oracle_flg < 0)) return ECMD_OK; Sprintf(qbuf, "\"Then dost thou desire a major one?\" (%d %s)", major_cost, currency((long) major_cost)); @@ -768,7 +769,7 @@ doconsult(struct monst *oracl) break; } money2mon(oracl, (long) u_pay); - gc.context.botl = 1; + disp.botl = TRUE; if (!u.uevent.major_oracle && !u.uevent.minor_oracle) record_achievement(ACH_ORCL); add_xpts = 0; /* first oracle of each type gives experience points */ @@ -795,19 +796,19 @@ doconsult(struct monst *oracl) return ECMD_TIME; } -static void +staticfn void couldnt_open_file(const char *filename) { - int save_something = gp.program_state.something_worth_saving; + int save_something = program_state.something_worth_saving; /* most likely the file is missing, so suppress impossible()'s "saving and restoring might fix this" (unless the fuzzer, which escalates impossible to panic, is running) */ if (!iflags.debug_fuzzer) - gp.program_state.something_worth_saving = 0; + program_state.something_worth_saving = 0; impossible("Can't open '%s' file.", filename); - gp.program_state.something_worth_saving = save_something; + program_state.something_worth_saving = save_something; } /* is 'word' a capitalized monster name that should be preceded by "the"? @@ -829,6 +830,7 @@ CapitalMon( if (!CapMons) init_CapMons(); + assert(CapMons != 0); wln = (unsigned) strlen(word); for (i = 0; i < CapMonSiz - 1; ++i) { @@ -853,7 +855,7 @@ CapitalMon( having a capitalized type name like Green-elf or Archon, plus unique monsters whose "name" is a title rather than a personal name, plus hallucinatory monster names that fall into either of those categories */ -static void +staticfn void init_CapMons(void) { unsigned pass; @@ -870,7 +872,7 @@ init_CapMons(void) unsigned mndx, mgend; /* the first CapMonstCnt entries come from mons[].pmnames[] and - the next CapBogonCnt entries from from the 'bogusmons' file; + the next CapBogonCnt entries from the 'bogusmons' file; there is an extra entry for Null at the end, but that is only useful to force non-zero array size in case both mons[] and bogusmons get modified to have no applicable monster names */ diff --git a/src/save.c b/src/save.c index 4f9c637185..88cfd1d412 100644 --- a/src/save.c +++ b/src/save.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 save.c $NHDT-Date: 1661240721 2022/08/23 07:45:21 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.195 $ */ +/* NetHack 3.7 save.c $NHDT-Date: 1706079844 2024/01/24 07:04:04 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.214 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2009. */ /* NetHack may be freely redistributed. See license for details. */ @@ -16,32 +16,32 @@ int dotcnt, dotrow; /* also used in restore */ #endif -static void savelevchn(NHFILE *); -static void savelevl(NHFILE *,boolean); -static void savedamage(NHFILE *); -static void save_bubbles(NHFILE *, xint8); -static void save_stairs(NHFILE *); -static void save_bc(NHFILE *); -static void saveobj(NHFILE *,struct obj *); -static void saveobjchn(NHFILE *,struct obj **); -static void savemon(NHFILE *,struct monst *); -static void savemonchn(NHFILE *,struct monst *); -static void savetrapchn(NHFILE *,struct trap *); -static void save_gamelog(NHFILE *); -static void savegamestate(NHFILE *); -static void savelev_core(NHFILE *, xint8); -static void save_msghistory(NHFILE *); +staticfn void savelevchn(NHFILE *); +staticfn void savelevl(NHFILE *,boolean); +staticfn void savedamage(NHFILE *); +staticfn void save_bubbles(NHFILE *, xint8); +staticfn void save_stairs(NHFILE *); +staticfn void save_bc(NHFILE *); +staticfn void saveobj(NHFILE *, struct obj *); +staticfn void saveobjchn(NHFILE *, struct obj **) NO_NNARGS; +staticfn void savemon(NHFILE *, struct monst *); +staticfn void savemonchn(NHFILE *, struct monst *) NO_NNARGS; +staticfn void savetrapchn(NHFILE *, struct trap *) NO_NNARGS; +staticfn void save_gamelog(NHFILE *); +staticfn void savegamestate(NHFILE *); +staticfn void savelev_core(NHFILE *, xint8); +staticfn void save_msghistory(NHFILE *); #ifdef ZEROCOMP -static void zerocomp_bufon(int); -static void zerocomp_bufoff(int); -static void zerocomp_bflush(int); -static void zerocomp_bwrite(int, genericptr_t, unsigned int); -static void zerocomp_bputc(int); +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 (!gp.program_state.done_hup) +#define HUP if (!program_state.done_hup) #else #define HUP #endif @@ -59,7 +59,7 @@ dosave(void) clear_nhwindow(WIN_MESSAGE); pline("Saving..."); #if defined(HANGUPHANDLING) - gp.program_state.done_hup = 0; + program_state.done_hup = 0; #endif if (dosave0()) { u.uhp = -1; /* universal game's over indicator */ @@ -90,7 +90,8 @@ dosave0(void) delete_whereis(); #endif - gp.program_state.saving++; /* inhibit status and perm_invent updates */ + program_state.saving++; /* inhibit status and perm_invent updates */ + notice_mon_off(); /* we may get here via hangup signal, in which case we want to fix up a few of things before saving so that they won't be restored in an improper state; these will be no-ops for normal save sequence */ @@ -107,7 +108,7 @@ dosave0(void) when punished, make sure ball and chain are placed too */ done_object_cleanup(); /* maybe force some items onto map */ - if (!gp.program_state.something_worth_saving || !gs.SAVEF[0]) + if (!program_state.something_worth_saving || !gs.SAVEF[0]) goto done; fq_save = fqname(gs.SAVEF, SAVEPREFIX, 1); /* level files take 0 */ @@ -165,8 +166,6 @@ dosave0(void) if (nhfp && nhfp->fplog) (void) fprintf(nhfp->fplog, "# post-validation\n"); store_plname_in_file(nhfp); - gu.ustuck_id = (u.ustuck ? u.ustuck->m_id : 0); - gu.usteed_id = (u.usteed ? u.usteed->m_id : 0); /* savelev() might save uball and uchain, releasing their memory if FREEING, so we need to check their status now; if hero is swallowed, uball and uchain will persist beyond saving map floor and inventory @@ -196,7 +195,7 @@ dosave0(void) for (ltmp = (xint8) 1; ltmp <= maxledgerno(); ltmp++) { if (ltmp == ledger_no(&gu.uz_save)) continue; - if (!(gl.level_info[ltmp].flags & LFILE_EXISTS)) + if (!(svl.level_info[ltmp].flags & LFILE_EXISTS)) continue; #ifdef MICRO curs(WIN_MAP, 1 + dotcnt++, dotrow); @@ -214,12 +213,12 @@ dosave0(void) HUP pline1(whynot); close_nhfile(nhfp); (void) delete_savefile(); - HUP Strcpy(gk.killer.name, whynot); + HUP Strcpy(svk.killer.name, whynot); HUP done(TRICKED); goto done; } minit(); /* ZEROCOMP */ - getlev(onhfp, gh.hackpid, ltmp); + getlev(onhfp, svh.hackpid, ltmp); close_nhfile(onhfp); if (nhfp->structlevel) bwrite(nhfp->fd, (genericptr_t) <mp, sizeof ltmp); /* lvl no. */ @@ -236,15 +235,16 @@ dosave0(void) delete_levelfile(0); nh_compress(fq_save); /* this should probably come sooner... */ - gp.program_state.something_worth_saving = 0; + program_state.something_worth_saving = 0; res = 1; done: - gp.program_state.saving--; + notice_mon_on(); + program_state.saving--; return res; } -static void +staticfn void save_gamelog(NHFILE *nhfp) { struct gamelog_line *tmp = gg.gamelog, *tmp2; @@ -277,16 +277,16 @@ save_gamelog(NHFILE *nhfp) gg.gamelog = NULL; } -static void +staticfn void savegamestate(NHFILE *nhfp) { unsigned long uid; - gp.program_state.saving++; /* caller should/did already set this... */ + 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) &gc.context, sizeof gc.context); + bwrite(nhfp->fd, (genericptr_t) &svc.context, sizeof svc.context); bwrite(nhfp->fd, (genericptr_t) &flags, sizeof flags); } urealtime.finish_time = getnow(); @@ -319,33 +319,23 @@ savegamestate(NHFILE *nhfp) if (release_data(nhfp)) gm.migrating_mons = (struct monst *) 0; if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) gm.mvitals, sizeof gm.mvitals); + bwrite(nhfp->fd, (genericptr_t) svm.mvitals, sizeof svm.mvitals); save_dungeon(nhfp, (boolean) !!perform_bwrite(nhfp), (boolean) !!release_data(nhfp)); savelevchn(nhfp); if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) &gm.moves, sizeof gm.moves); - bwrite(nhfp->fd, (genericptr_t) &gq.quest_status, - sizeof gq.quest_status); - bwrite(nhfp->fd, (genericptr_t) gs.spl_book, + 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)); } save_artifacts(nhfp); save_oracles(nhfp); - if (gu.ustuck_id) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &gu.ustuck_id, - sizeof gu.ustuck_id); - } - if (gu.usteed_id) { - if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &gu.usteed_id, - sizeof gu.usteed_id); - } if (nhfp->structlevel) { - bwrite(nhfp->fd, (genericptr_t) gp.pl_character, - sizeof gp.pl_character); - bwrite(nhfp->fd, (genericptr_t) gp.pl_fruit, sizeof gp.pl_fruit); + bwrite(nhfp->fd, (genericptr_t) svp.pl_character, + sizeof svp.pl_character); + bwrite(nhfp->fd, (genericptr_t) svp.pl_fruit, sizeof svp.pl_fruit); } savefruitchn(nhfp); savenames(nhfp); @@ -355,18 +345,18 @@ savegamestate(NHFILE *nhfp) bwrite(nhfp->fd, (genericptr_t) &gw.wizpuzzle, sizeof gw.wizpuzzle); if (nhfp->structlevel) bflush(nhfp->fd); - gp.program_state.saving--; + program_state.saving--; return; } /* potentially called from goto_level(do.c) as well as savestateinlock() */ boolean -tricked_fileremoved(NHFILE* nhfp, char* whynot) +tricked_fileremoved(NHFILE *nhfp, char *whynot) { if (!nhfp) { pline1(whynot); pline("Probably someone removed it."); - Strcpy(gk.killer.name, whynot); + Strcpy(svk.killer.name, whynot); done(TRICKED); return TRUE; } @@ -381,7 +371,7 @@ savestateinlock(void) char whynot[BUFSZ]; NHFILE *nhfp; - gp.program_state.saving++; /* inhibit status and perm_invent updates */ + program_state.saving++; /* inhibit status and perm_invent updates */ /* When checkpointing is on, the full state needs to be written * on each checkpoint. When checkpointing is off, only the pid * needs to be in the level.0 file, so it does not need to be @@ -402,15 +392,15 @@ savestateinlock(void) */ nhfp = open_levelfile(0, whynot); if (tricked_fileremoved(nhfp, whynot)) { - gp.program_state.saving--; + program_state.saving--; return; } if (nhfp->structlevel) (void) read(nhfp->fd, (genericptr_t) &hpid, sizeof hpid); - if (gh.hackpid != hpid) { + if (svh.hackpid != hpid) { Sprintf(whynot, "Level #0 pid (%d) doesn't match ours (%d)!", - hpid, gh.hackpid); + hpid, svh.hackpid); goto giveup; } close_nhfile(nhfp); @@ -419,31 +409,31 @@ savestateinlock(void) if (!nhfp) { pline1(whynot); giveup: - Strcpy(gk.killer.name, whynot); + Strcpy(svk.killer.name, whynot); /* done(TRICKED) will return when running in wizard mode; clear the display-update-suppression flag before rather than after so that screen updating behaves normally; game data shouldn't be inconsistent yet, unlike it would become midway through saving */ - gp.program_state.saving--; + program_state.saving--; done(TRICKED); return; } nhfp->mode = WRITING; if (nhfp->structlevel) - (void) write(nhfp->fd, (genericptr_t) &gh.hackpid, sizeof gh.hackpid); + (void) write(nhfp->fd, (genericptr_t) &svh.hackpid, + sizeof svh.hackpid); if (flags.ins_chkpt) { int currlev = ledger_no(&u.uz); if (nhfp->structlevel) - (void) write(nhfp->fd, (genericptr_t) &currlev, sizeof currlev); + (void) write(nhfp->fd, (genericptr_t) &currlev, + sizeof currlev); save_savefile_name(nhfp); store_version(nhfp); store_savefileinfo(nhfp); store_plname_in_file(nhfp); - gu.ustuck_id = (u.ustuck ? u.ustuck->m_id : 0); - gu.usteed_id = (u.usteed ? u.usteed->m_id : 0); /* if ball and/or chain aren't on floor or in invent, keep a copy of their pointers; not valid when on floor or in invent */ gl.looseball = BALL_IN_MON ? uball : 0; @@ -452,7 +442,7 @@ savestateinlock(void) } close_nhfile(nhfp); } - gp.program_state.saving--; + program_state.saving--; gh.havestate = flags.ins_chkpt; return; } @@ -469,7 +459,7 @@ savelev(NHFILE *nhfp, xint8 lev) but we'll be called during run-down */ if (set_uz_save && perform_bwrite(nhfp)) { if (u.uz.dnum == 0 && u.uz.dlevel == 0) { - gp.program_state.something_worth_saving = 0; + program_state.something_worth_saving = 0; panic("savelev: where are we?"); } gu.uz_save = u.uz; @@ -481,14 +471,14 @@ savelev(NHFILE *nhfp, xint8 lev) gu.uz_save.dnum = gu.uz_save.dlevel = 0; /* unset */ } -static void +staticfn void savelev_core(NHFILE *nhfp, xint8 lev) { #ifdef TOS short tlev; #endif - gp.program_state.saving++; /* even if current mode is FREEING */ + program_state.saving++; /* even if current mode is FREEING */ if (!nhfp) panic("Save on bad file!"); /* impossible */ @@ -519,9 +509,9 @@ savelev_core(NHFILE *nhfp, xint8 lev) dmonsfree(); if (lev >= 0 && lev <= maxledgerno()) - gl.level_info[lev].flags |= VISITED; + svl.level_info[lev].flags |= VISITED; if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) &gh.hackpid, sizeof gh.hackpid); + bwrite(nhfp->fd, (genericptr_t) &svh.hackpid, sizeof svh.hackpid); #ifdef TOS tlev = lev; tlev &= 0x00ff; @@ -539,20 +529,27 @@ savelev_core(NHFILE *nhfp, xint8 lev) the guessing that was needed in 3.4.3 and without having to interpret level data to find where to start; unfortunately it still needs to handle all the data compression schemes */ - savecemetery(nhfp, &gl.level.bonesinfo); + savecemetery(nhfp, &svl.level.bonesinfo); 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) gl.lastseentyp, sizeof gl.lastseentyp); - bwrite(nhfp->fd, (genericptr_t) &gm.moves, sizeof gm.moves); + 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) &gu.updest, sizeof (dest_area)); - bwrite(nhfp->fd, (genericptr_t) &gd.dndest, sizeof (dest_area)); - bwrite(nhfp->fd, (genericptr_t) &gl.level.flags, sizeof gl.level.flags); - bwrite(nhfp->fd, (genericptr_t) &gd.doors_alloc, sizeof gd.doors_alloc); - bwrite(nhfp->fd, (genericptr_t) gd.doors, gd.doors_alloc * sizeof (coord)); + 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)); } save_rooms(nhfp); /* no dynamic memory to reclaim */ @@ -566,29 +563,31 @@ savelev_core(NHFILE *nhfp, xint8 lev) save_worm(nhfp); /* save worm information */ savetrapchn(nhfp, gf.ftrap); saveobjchn(nhfp, &fobj); - saveobjchn(nhfp, &gl.level.buriedobjlist); + saveobjchn(nhfp, &svl.level.buriedobjlist); saveobjchn(nhfp, &gb.billobjs); save_engravings(nhfp); savedamage(nhfp); /* pending shop wall and/or floor repair */ save_regions(nhfp); save_bubbles(nhfp, lev); /* for water and air */ + save_exclusions(nhfp); + save_track(nhfp); if (nhfp->mode != FREEING) { if (nhfp->structlevel) bflush(nhfp->fd); } - gp.program_state.saving--; + program_state.saving--; if (release_data(nhfp)) { clear_level_structures(); gf.ftrap = 0; gb.billobjs = 0; - (void) memset(gr.rooms, 0, sizeof(gr.rooms)); + (void) memset(svr.rooms, 0, sizeof(svr.rooms)); } return; } -static void -savelevl(NHFILE* nhfp, boolean rlecomp) +staticfn void +savelevl(NHFILE *nhfp, boolean rlecomp) { #ifdef RLECOMP struct rm *prm, *rgrm; @@ -648,7 +647,7 @@ savelevl(NHFILE* nhfp, boolean rlecomp) } /* save Plane of Water's air bubbles and Plane of Air's clouds */ -static void +staticfn void save_bubbles(NHFILE *nhfp, xint8 lev) { xint8 bbubbly; @@ -671,7 +670,7 @@ save_bubbles(NHFILE *nhfp, xint8 lev) /* used when saving a level and also when saving dungeon overview data */ void -savecemetery(NHFILE* nhfp, struct cemetery** cemeteryaddr) +savecemetery(NHFILE *nhfp, struct cemetery **cemeteryaddr) { struct cemetery *thisbones, *nextbones; int flag; @@ -695,13 +694,13 @@ savecemetery(NHFILE* nhfp, struct cemetery** cemeteryaddr) *cemeteryaddr = 0; } -static void -savedamage(NHFILE* nhfp) +staticfn void +savedamage(NHFILE *nhfp) { - register struct damage *damageptr, *tmp_dam; + struct damage *damageptr, *tmp_dam; unsigned int xl = 0; - damageptr = gl.level.damagelist; + damageptr = svl.level.damagelist; for (tmp_dam = damageptr; tmp_dam; tmp_dam = tmp_dam->next) xl++; if (perform_bwrite(nhfp)) { @@ -719,18 +718,18 @@ savedamage(NHFILE* nhfp) free((genericptr_t) tmp_dam); } if (release_data(nhfp)) - gl.level.damagelist = 0; + svl.level.damagelist = 0; } -static void -save_stairs(NHFILE* nhfp) +staticfn void +save_stairs(NHFILE *nhfp) { stairway *stway = gs.stairs; int buflen = (int) sizeof *stway; while (stway) { if (perform_bwrite(nhfp)) { - boolean use_relative = (gp.program_state.restoring != REST_GSTATE + boolean use_relative = (program_state.restoring != REST_GSTATE && stway->tolev.dnum == u.uz.dnum); if (use_relative) { /* make dlevel relative to current level */ @@ -757,7 +756,7 @@ save_stairs(NHFILE* nhfp) /* if ball and/or chain are loose, make an object chain for it/them and save that separately from other objects */ -static void +staticfn void save_bc(NHFILE *nhfp) { struct obj *bc_objs = 0; @@ -787,7 +786,7 @@ save_bc(NHFILE *nhfp) /* save one object; caveat: this is only for perform_bwrite(); caller handles release_data() */ -static void +staticfn void saveobj(NHFILE *nhfp, struct obj *otmp) { int buflen, zerobuf = 0; @@ -831,10 +830,10 @@ saveobj(NHFILE *nhfp, struct obj *otmp) /* save an object chain; sets head of list to Null when done; handles release_data() for each object in the list */ -static void -saveobjchn(NHFILE* nhfp, struct obj** obj_p) +staticfn void +saveobjchn(NHFILE *nhfp, struct obj **obj_p) { - register struct obj *otmp = *obj_p; + struct obj *otmp = *obj_p; struct obj *otmp2; boolean is_invent = (otmp && otmp == gi.invent); int minusone = -1; @@ -853,21 +852,21 @@ saveobjchn(NHFILE* nhfp, struct obj** obj_p) * Always invalidate the pointer, but ensure that we have * the o_id in order to restore the pointer on reload. */ - if (otmp == gc.context.victual.piece) { - gc.context.victual.o_id = otmp->o_id; - gc.context.victual.piece = (struct obj *) 0; + if (otmp == svc.context.victual.piece) { + svc.context.victual.o_id = otmp->o_id; + svc.context.victual.piece = (struct obj *) 0; } - if (otmp == gc.context.tin.tin) { - gc.context.tin.o_id = otmp->o_id; - gc.context.tin.tin = (struct obj *) 0; + if (otmp == svc.context.tin.tin) { + svc.context.tin.o_id = otmp->o_id; + svc.context.tin.tin = (struct obj *) 0; } - if (otmp == gc.context.spbook.book) { - gc.context.spbook.o_id = otmp->o_id; - gc.context.spbook.book = (struct obj *) 0; + if (otmp == svc.context.spbook.book) { + svc.context.spbook.o_id = otmp->o_id; + svc.context.spbook.book = (struct obj *) 0; } - if (otmp == gc.context.crystal.ball) { - gc.context.crystal.o_id = otmp->o_id; - gc.context.crystal.ball = (struct obj *) 0; + if (otmp == svc.context.crystal.ball) { + svc.context.crystal.o_id = otmp->o_id; + svc.context.crystal.ball = (struct obj *) 0; } otmp->where = OBJ_FREE; /* set to free so dealloc will work */ otmp->nobj = NULL; /* nobj saved into otmp2 */ @@ -898,8 +897,8 @@ saveobjchn(NHFILE* nhfp, struct obj** obj_p) } } -static void -savemon(NHFILE* nhfp, struct monst* mtmp) +staticfn void +savemon(NHFILE *nhfp, struct monst *mtmp) { int buflen; @@ -968,10 +967,10 @@ savemon(NHFILE* nhfp, struct monst* mtmp) } } -static void -savemonchn(NHFILE* nhfp, register struct monst* mtmp) +staticfn void +savemonchn(NHFILE *nhfp, struct monst *mtmp) { - register struct monst *mtmp2; + struct monst *mtmp2; int minusone = -1; while (mtmp) { @@ -985,10 +984,14 @@ savemonchn(NHFILE* nhfp, register struct monst* mtmp) if (mtmp->minvent) saveobjchn(nhfp, &mtmp->minvent); if (release_data(nhfp)) { - if (mtmp == gc.context.polearm.hitmon) { - gc.context.polearm.m_id = mtmp->m_id; - gc.context.polearm.hitmon = NULL; + if (mtmp == svc.context.polearm.hitmon) { + svc.context.polearm.m_id = mtmp->m_id; + svc.context.polearm.hitmon = NULL; } + if (mtmp == u.ustuck) + u.ustuck_mid = u.ustuck->m_id; + if (mtmp == u.usteed) + u.usteed_mid = u.usteed->m_id; mtmp->nmon = NULL; /* nmon saved into mtmp2 */ dealloc_monst(mtmp); } @@ -1000,15 +1003,15 @@ savemonchn(NHFILE* nhfp, register struct monst* mtmp) } } -/* save traps; gf.ftrap is the only trap chain so the 2nd arg is superfluous */ -static void -savetrapchn(NHFILE* nhfp, register struct trap* trap) +/* save traps; gf.ftrap is the only trap chain so 2nd arg is superfluous */ +staticfn void +savetrapchn(NHFILE *nhfp, struct trap *trap) { static struct trap zerotrap; - register struct trap *trap2; + struct trap *trap2; while (trap) { - boolean use_relative = (gp.program_state.restoring != REST_GSTATE + boolean use_relative = (program_state.restoring != REST_GSTATE && trap->dst.dnum == u.uz.dnum); trap2 = trap->ntrap; if (use_relative) @@ -1039,10 +1042,10 @@ savetrapchn(NHFILE* nhfp, register struct trap* trap) * level routine marks nonexistent fruits by making the fid negative. */ void -savefruitchn(NHFILE* nhfp) +savefruitchn(NHFILE *nhfp) { static struct fruit zerofruit; - register struct fruit *f2, *f1; + struct fruit *f2, *f1; f1 = gf.ffruit; while (f1) { @@ -1063,21 +1066,19 @@ savefruitchn(NHFILE* nhfp) gf.ffruit = 0; } - - -static void -savelevchn(NHFILE* nhfp) +staticfn void +savelevchn(NHFILE *nhfp) { s_level *tmplev, *tmplev2; int cnt = 0; - for (tmplev = gs.sp_levchn; tmplev; tmplev = tmplev->next) + 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); } - for (tmplev = gs.sp_levchn; tmplev; tmplev = tmplev2) { + for (tmplev = svs.sp_levchn; tmplev; tmplev = tmplev2) { tmplev2 = tmplev->next; if (perform_bwrite(nhfp)) { if (nhfp->structlevel) @@ -1087,26 +1088,45 @@ savelevchn(NHFILE* nhfp) free((genericptr_t) tmplev); } if (release_data(nhfp)) - gs.sp_levchn = 0; + 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) +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; + + (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() */ bwrite(nhfp->fd, (genericptr_t) &plsiztmp, sizeof plsiztmp); - bwrite(nhfp->fd, (genericptr_t) gp.plname, plsiztmp); + bwrite(nhfp->fd, (genericptr_t) hero, plsiztmp); bufon(nhfp->fd); } return; } -static void -save_msghistory(NHFILE* nhfp) +staticfn void +save_msghistory(NHFILE *nhfp) { char *msg; int msgcount = 0, msglen; @@ -1138,7 +1158,7 @@ save_msghistory(NHFILE* nhfp) } void -store_savefileinfo(NHFILE* nhfp) +store_savefileinfo(NHFILE *nhfp) { /* sfcap (decl.c) describes the savefile feature capabilities * that are supported by this port/platform build. @@ -1172,11 +1192,13 @@ free_dungeons(void) tnhfp.mode = FREEING; savelevchn(&tnhfp); save_dungeon(&tnhfp, FALSE, TRUE); - free_luathemes(TRUE); + free_luathemes(all_themes); #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) @@ -1194,6 +1216,7 @@ freedynamicdata(void) msgtype_free(); savedsym_free(); 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) @@ -1210,6 +1233,7 @@ freedynamicdata(void) /* move-specific data */ dmonsfree(); /* release dead monsters */ + alloc_itermonarr(0U); /* a request of 0 releases existing allocation */ /* level-specific data */ done_object_cleanup(); /* maybe force some OBJ_FREE items onto map */ @@ -1221,6 +1245,8 @@ freedynamicdata(void) free_light_sources(RANGE_GLOBAL); freeobjchn(gi.invent); freeobjchn(gm.migrating_objs); + if (go.objs_deleted) + dobjsfree(); /* really free deleted objects */ freemonchn(gm.migrating_mons); freemonchn(gm.mydogs); /* ascension or dungeon escape */ /* freelevchn(); -- [folded into free_dungeons()] */ @@ -1235,6 +1261,7 @@ freedynamicdata(void) freeroleoptvals(); /* saveoptvals(&tnhfp) */ cmdq_clear(CQ_CANNED); cmdq_clear(CQ_REPEAT); + free_tutorial(); /* (only needed if quitting while in tutorial) */ /* some pointers in iflags */ if (iflags.wc_font_map) @@ -1258,13 +1285,21 @@ freedynamicdata(void) #ifdef USER_SOUNDS release_sound_mappings(); #endif +#ifdef DUMPLOG_CORE + dumplogfreemessages(); +#endif + discard_gamelog(); + release_runtime_info(); /* build-time options and version stuff */ #endif /* FREE_ALL_MEMORY */ if (VIA_WINDOWPORT()) status_finish(); -#if defined(DUMPLOG) || defined(DUMPHTML) - dumplogfreemessages(); -#endif + + if (options_set_window_colors_flag) + options_free_window_colors(); + + if (glyphid_cache_status()) + free_glyphid_cache(); /* last, because it frees data that might be used by panic() to provide feedback to the user; conceivably other freeing might trigger panic */ diff --git a/src/selvar.c b/src/selvar.c new file mode 100644 index 0000000000..d181c2790b --- /dev/null +++ b/src/selvar.c @@ -0,0 +1,810 @@ +/* NetHack 3.7 selvar.c $NHDT-Date: 1709677544 2024/03/05 22:25:44 $ $NHDT-Branch: keni-mdlib-followup $:$NHDT-Revision: 1.360 $ */ +/* Copyright (c) 2024 by Pasi Kallinen */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "selvar.h" +#include "sp_lev.h" + +staticfn boolean sel_flood_havepoint(coordxy, coordxy, coordxy *, coordxy *, + int); +staticfn long line_dist_coord(long, long, long, long, long, long); + +/* selection */ +struct selectionvar * +selection_new(void) +{ + struct selectionvar *tmps = (struct selectionvar *) alloc(sizeof *tmps); + + tmps->wid = COLNO; + tmps->hei = ROWNO; + tmps->bounds_dirty = FALSE; + tmps->bounds.lx = COLNO; + tmps->bounds.ly = ROWNO; + tmps->bounds.hx = tmps->bounds.hy = 0; + tmps->map = (char *) alloc((COLNO * ROWNO) + 1); + (void) memset(tmps->map, 1, (COLNO * ROWNO)); + tmps->map[(COLNO * ROWNO)] = '\0'; + + return tmps; +} + +void +selection_free(struct selectionvar *sel, boolean freesel) +{ + if (sel) { + if (sel->map) + free(sel->map); + sel->map = NULL; + if (freesel) + free((genericptr_t) sel); + else + (void) memset((genericptr_t) sel, 0, sizeof *sel); + } +} + +/* clear selection, setting all locations to value val */ +void +selection_clear(struct selectionvar *sel, int val) +{ + (void) memset(sel->map, 1 + val, (COLNO * ROWNO)); + if (val) { + sel->bounds.lx = 0; + sel->bounds.ly = 0; + sel->bounds.hx = COLNO - 1; + sel->bounds.hy = ROWNO - 1; + } else { + sel->bounds.lx = COLNO; + sel->bounds.ly = ROWNO; + sel->bounds.hx = sel->bounds.hy = 0; + } + sel->bounds_dirty = FALSE; +} + +struct selectionvar * +selection_clone(struct selectionvar *sel) +{ + struct selectionvar *tmps = (struct selectionvar *) alloc(sizeof *tmps); + + *tmps = *sel; + tmps->map = dupstr(sel->map); + + return tmps; +} + +/* get boundary rect of selection sel into b */ +void +selection_getbounds(struct selectionvar *sel, NhRect *b) +{ + if (!sel || !b) + return; + + selection_recalc_bounds(sel); + + if (sel->bounds.lx >= sel->wid) { + b->lx = 0; + b->ly = 0; + b->hx = COLNO - 1; + b->hy = ROWNO - 1; + } else { + b->lx = sel->bounds.lx; + b->ly = sel->bounds.ly; + b->hx = sel->bounds.hx; + b->hy = sel->bounds.hy; + } +} + +/* recalc the boundary of selection, if necessary */ +void +selection_recalc_bounds(struct selectionvar *sel) +{ + coordxy x, y; + NhRect r; + + if (!sel->bounds_dirty) + return; + + sel->bounds.lx = COLNO; + sel->bounds.ly = ROWNO; + sel->bounds.hx = sel->bounds.hy = 0; + + r.lx = r.ly = r.hx = r.hy = -1; + + /* left */ + for (x = 0; x < sel->wid; x++) { + for (y = 0; y < sel->hei; y++) { + if (selection_getpoint(x, y, sel)) { + r.lx = x; + break; + } + } + if (r.lx > -1) + break; + } + + if (r.lx > -1) { + /* right */ + for (x = sel->wid-1; x >= r.lx; x--) { + for (y = 0; y < sel->hei; y++) { + if (selection_getpoint(x, y, sel)) { + r.hx = x; + break; + } + } + if (r.hx > -1) + break; + } + + /* top */ + for (y = 0; y < sel->hei; y++) { + for (x = r.lx; x <= r.hx; x++) { + if (selection_getpoint(x, y, sel)) { + r.ly = y; + break; + } + } + if (r.ly > -1) + break; + } + + /* bottom */ + for (y = sel->hei-1; y >= r.ly; y--) { + for (x = r.lx; x <= r.hx; x++) { + if (selection_getpoint(x, y, sel)) { + r.hy = y; + break; + } + } + if (r.hy > -1) + break; + } + sel->bounds = r; + } + + sel->bounds_dirty = FALSE; +} + +coordxy +selection_getpoint( + coordxy x, coordxy y, + struct selectionvar *sel) +{ + if (!sel || !sel->map) + return 0; + if (x < 0 || y < 0 || x >= sel->wid || y >= sel->hei) + return 0; + + return (sel->map[sel->wid * y + x] - 1); +} + +void +selection_setpoint( + coordxy x, coordxy y, + struct selectionvar *sel, + int c) +{ + if (!sel || !sel->map) + return; + if (x < 0 || y < 0 || x >= sel->wid || y >= sel->hei) + return; + + if (c && !sel->bounds_dirty) { + if (sel->bounds.lx > x) sel->bounds.lx = x; + if (sel->bounds.ly > y) sel->bounds.ly = y; + if (sel->bounds.hx < x) sel->bounds.hx = x; + if (sel->bounds.hy < y) sel->bounds.hy = y; + } else { + sel->bounds_dirty = TRUE; + } + + sel->map[sel->wid * y + x] = (char) (c + 1); +} + +struct selectionvar * +selection_not(struct selectionvar *s) +{ + int x, y; + NhRect tmprect = cg.zeroNhRect; + + for (x = 0; x < s->wid; x++) + for (y = 0; y < s->hei; y++) + selection_setpoint(x, y, s, selection_getpoint(x, y, s) ? 0 : 1); + selection_getbounds(s, &tmprect); + return s; +} + +struct selectionvar * +selection_filter_percent( + struct selectionvar *ov, + int percent) +{ + int x, y; + struct selectionvar *ret; + NhRect rect = cg.zeroNhRect; + + if (!ov) + return NULL; + + ret = selection_new(); + + selection_getbounds(ov, &rect); + + for (x = rect.lx; x <= rect.hx; x++) + for (y = rect.ly; y <= rect.hy; y++) + if (selection_getpoint(x, y, ov) && (rn2(100) < percent)) + selection_setpoint(x, y, ret, 1); + + return ret; +} + +struct selectionvar * +selection_filter_mapchar(struct selectionvar *ov, xint16 typ, int lit) +{ + int x, y; + struct selectionvar *ret; + NhRect rect = cg.zeroNhRect; + + if (!ov) + return NULL; + + ret = selection_new(); + + selection_getbounds(ov, &rect); + + for (x = rect.lx; x <= rect.hx; x++) + for (y = rect.ly; y <= rect.hy; y++) + if (selection_getpoint(x, y, ov) + && match_maptyps(typ, levl[x][y].typ)) { + switch (lit) { + default: + case -2: + selection_setpoint(x, y, ret, 1); + break; + case -1: + selection_setpoint(x, y, ret, rn2(2)); + break; + case 0: + case 1: + if (levl[x][y].lit == (unsigned int) lit) + selection_setpoint(x, y, ret, 1); + break; + } + } + return ret; +} + +int +selection_rndcoord( + struct selectionvar *ov, + coordxy *x, coordxy *y, + boolean removeit) +{ + int idx = 0; + int c; + int dx, dy; + NhRect rect = cg.zeroNhRect; + + selection_getbounds(ov, &rect); + + for (dx = rect.lx; dx <= rect.hx; dx++) + for (dy = rect.ly; dy <= rect.hy; dy++) + if (selection_getpoint(dx, dy, ov)) + idx++; + + if (idx) { + c = rn2(idx); + for (dx = rect.lx; dx <= rect.hx; dx++) + for (dy = rect.ly; dy <= rect.hy; dy++) + if (selection_getpoint(dx, dy, ov)) { + if (!c) { + *x = dx; + *y = dy; + if (removeit) + selection_setpoint(dx, dy, ov, 0); + return 1; + } + c--; + } + } + *x = *y = -1; + return 0; +} + +void +selection_do_grow(struct selectionvar *ov, int dir) +{ + coordxy x, y; + struct selectionvar *tmp; + NhRect rect = cg.zeroNhRect; + + if (!ov) + return; + + tmp = selection_new(); + + if (dir == W_RANDOM) + dir = random_wdir(); + + selection_getbounds(ov, &rect); + + for (x = max(0, rect.lx-1); x <= min(COLNO-1, rect.hx+1); x++) + for (y = max(0, rect.ly-1); y <= min(ROWNO-1, rect.hy+1); y++) { + /* note: dir is a mask of multiple directions, but the only + way to specify diagonals is by including the two adjacent + orthogonal directions, which effectively specifies three- + way growth [WEST|NORTH => WEST plus WEST|NORTH plus NORTH] */ + if (((dir & W_WEST) && selection_getpoint(x + 1, y, ov)) + /* Note: extra parens around these operands are critical, due to + * == having precedence over & and |. */ + || (((dir & (W_WEST | W_NORTH)) == (W_WEST | W_NORTH)) + && selection_getpoint(x + 1, y + 1, ov)) + || ((dir & W_NORTH) && selection_getpoint(x, y + 1, ov)) + || (((dir & (W_NORTH | W_EAST)) == (W_NORTH | W_EAST)) + && selection_getpoint(x - 1, y + 1, ov)) + || ((dir & W_EAST) && selection_getpoint(x - 1, y, ov)) + || (((dir & (W_EAST | W_SOUTH)) == (W_EAST | W_SOUTH)) + && selection_getpoint(x - 1, y - 1, ov)) + || ((dir & W_SOUTH) && selection_getpoint(x, y - 1, ov)) + || (((dir & (W_SOUTH | W_WEST)) == (W_SOUTH | W_WEST)) + && selection_getpoint(x + 1, y - 1, ov))) { + selection_setpoint(x, y, tmp, 1); + } + } + + selection_getbounds(tmp, &rect); + + for (x = rect.lx; x <= rect.hx; x++) + for (y = rect.ly; y <= rect.hy; y++) + if (selection_getpoint(x, y, tmp)) + selection_setpoint(x, y, ov, 1); + + selection_free(tmp, TRUE); +} + +staticfn int (*selection_flood_check_func)(coordxy, coordxy); + +void +set_selection_floodfillchk(int (*f)(coordxy, coordxy)) +{ + selection_flood_check_func = f; +} + +/* check whethere is already in xs[],ys[] */ +staticfn boolean +sel_flood_havepoint( + coordxy x, coordxy y, + coordxy xs[], coordxy ys[], + int n) +{ + coordxy xx = x, yy = y; + + while (n > 0) { + --n; + if (xs[n] == xx && ys[n] == yy) + return TRUE; + } + return FALSE; +} + +void +selection_floodfill( + struct selectionvar *ov, + coordxy x, coordxy y, + boolean diagonals) +{ + struct selectionvar *tmp = selection_new(); +#define SEL_FLOOD_STACK (COLNO * ROWNO) +#define SEL_FLOOD(nx, ny) \ + do { \ + if (idx < SEL_FLOOD_STACK) { \ + dx[idx] = (nx); \ + dy[idx] = (ny); \ + idx++; \ + } else \ + panic(floodfill_stack_overrun); \ + } while (0) +#define SEL_FLOOD_CHKDIR(mx, my, sel) \ + do { \ + if (isok((mx), (my)) \ + && (*selection_flood_check_func)((mx), (my)) \ + && !selection_getpoint((mx), (my), (sel)) \ + && !sel_flood_havepoint((mx), (my), dx, dy, idx)) \ + SEL_FLOOD((mx), (my)); \ + } while (0) + static const char floodfill_stack_overrun[] = "floodfill stack overrun"; + int idx = 0; + coordxy dx[SEL_FLOOD_STACK]; + coordxy dy[SEL_FLOOD_STACK]; + + if (selection_flood_check_func == (int (*)(coordxy, coordxy)) 0) { + selection_free(tmp, TRUE); + return; + } + if (!isok(x, y)) { + return; + } + SEL_FLOOD(x, y); + do { + idx--; + x = dx[idx]; + y = dy[idx]; + if (isok(x, y)) { + selection_setpoint(x, y, ov, 1); + selection_setpoint(x, y, tmp, 1); + } + SEL_FLOOD_CHKDIR((x + 1), y, tmp); + SEL_FLOOD_CHKDIR((x - 1), y, tmp); + SEL_FLOOD_CHKDIR(x, (y + 1), tmp); + SEL_FLOOD_CHKDIR(x, (y - 1), tmp); + if (diagonals) { + SEL_FLOOD_CHKDIR((x + 1), (y + 1), tmp); + SEL_FLOOD_CHKDIR((x - 1), (y - 1), tmp); + SEL_FLOOD_CHKDIR((x - 1), (y + 1), tmp); + SEL_FLOOD_CHKDIR((x + 1), (y - 1), tmp); + } + } while (idx > 0); +#undef SEL_FLOOD +#undef SEL_FLOOD_STACK +#undef SEL_FLOOD_CHKDIR + selection_free(tmp, TRUE); +} + +/* McIlroy's Ellipse Algorithm */ +void +selection_do_ellipse( + struct selectionvar *ov, + int xc, int yc, + int a, int b, + int filled) +{ /* e(x,y) = b^2*x^2 + a^2*y^2 - a^2*b^2 */ + int x = 0, y = b; + long a2 = (long) a * a, b2 = (long) b * b; + long crit1 = -(a2 / 4 + a % 2 + b2); + long crit2 = -(b2 / 4 + b % 2 + a2); + long crit3 = -(b2 / 4 + b % 2); + long t = -a2 * y; /* e(x+1/2,y-1/2) - (a^2+b^2)/4 */ + long dxt = 2 * b2 * x, dyt = -2 * a2 * y; + long d2xt = 2 * b2, d2yt = 2 * a2; + long width = 1; + long i; + + if (!ov) + return; + + filled = !filled; + + if (!filled) { + while (y >= 0 && x <= a) { + selection_setpoint(xc + x, yc + y, ov, 1); + if (x != 0 || y != 0) + selection_setpoint(xc - x, yc - y, ov, 1); + if (x != 0 && y != 0) { + selection_setpoint(xc + x, yc - y, ov, 1); + selection_setpoint(xc - x, yc + y, ov, 1); + } + if (t + b2 * x <= crit1 /* e(x+1,y-1/2) <= 0 */ + || t + a2 * y <= crit3) { /* e(x+1/2,y) <= 0 */ + x++; + dxt += d2xt; + t += dxt; + } else if (t - a2 * y > crit2) { /* e(x+1/2,y-1) > 0 */ + y--; + dyt += d2yt; + t += dyt; + } else { + x++; + dxt += d2xt; + t += dxt; + y--; + dyt += d2yt; + t += dyt; + } + } + } else { + while (y >= 0 && x <= a) { + if (t + b2 * x <= crit1 /* e(x+1,y-1/2) <= 0 */ + || t + a2 * y <= crit3) { /* e(x+1/2,y) <= 0 */ + x++; + dxt += d2xt; + t += dxt; + width += 2; + } else if (t - a2 * y > crit2) { /* e(x+1/2,y-1) > 0 */ + for (i = 0; i < width; i++) + selection_setpoint(xc - x + i, yc - y, ov, 1); + if (y != 0) + for (i = 0; i < width; i++) + selection_setpoint(xc - x + i, yc + y, ov, 1); + y--; + dyt += d2yt; + t += dyt; + } else { + for (i = 0; i < width; i++) + selection_setpoint(xc - x + i, yc - y, ov, 1); + if (y != 0) + for (i = 0; i < width; i++) + selection_setpoint(xc - x + i, yc + y, ov, 1); + x++; + dxt += d2xt; + t += dxt; + y--; + dyt += d2yt; + t += dyt; + width += 2; + } + } + } +} + +/* square of distance from line segment (x1,y1, x2,y2) to point (x3,y3) */ +staticfn long +line_dist_coord(long x1, long y1, long x2, long y2, long x3, long y3) +{ + long px = x2 - x1; + long py = y2 - y1; + long s = px * px + py * py; + long x, y, dx, dy, distsq = 0; + float lu = 0; + + if (x1 == x2 && y1 == y2) + return dist2(x1, y1, x3, y3); + + lu = ((x3 - x1) * px + (y3 - y1) * py) / (float) s; + if (lu > 1) + lu = 1; + else if (lu < 0) + lu = 0; + + x = x1 + lu * px; + y = y1 + lu * py; + dx = x - x3; + dy = y - y3; + distsq = dx * dx + dy * dy; + + return distsq; +} + +/* guts of l_selection_gradient */ +void +selection_do_gradient( + struct selectionvar *ov, + long x, long y, + long x2,long y2, + long gtyp, + long mind, long maxd) +{ + long dx, dy, dofs; + + if (mind > maxd) { + long tmp = mind; + mind = maxd; + maxd = tmp; + } + + dofs = maxd * maxd - mind * mind; + if (dofs < 1) + dofs = 1; + + switch (gtyp) { + default: + impossible("Unrecognized gradient type! Defaulting to radial..."); + FALLTHROUGH; + /* FALLTHRU */ + case SEL_GRADIENT_RADIAL: { + for (dx = 0; dx < COLNO; dx++) + for (dy = 0; dy < ROWNO; dy++) { + long d0 = line_dist_coord(x, y, x2, y2, dx, dy); + + if (d0 <= mind * mind + || (d0 <= maxd * maxd && d0 - mind * mind < rn2(dofs))) + selection_setpoint(dx, dy, ov, 1); + } + break; + } + case SEL_GRADIENT_SQUARE: { + for (dx = 0; dx < COLNO; dx++) + for (dy = 0; dy < ROWNO; dy++) { + long d1 = line_dist_coord(x, y, x2, y2, x, dy); + long d2 = line_dist_coord(x, y, x2, y2, dx, y); + long d3 = line_dist_coord(x, y, x2, y2, x2, dy); + long d4 = line_dist_coord(x, y, x2, y2, dx, y2); + long d5 = line_dist_coord(x, y, x2, y2, dx, dy); + long d0 = min(d5, min(max(d1, d2), max(d3, d4))); + + if (d0 <= mind * mind + || (d0 <= maxd * maxd && d0 - mind * mind < rn2(dofs))) + selection_setpoint(dx, dy, ov, 1); + } + break; + } /*case*/ + } /*switch*/ +} + +/* bresenham line algo */ +void +selection_do_line( + coordxy x1, coordxy y1, + coordxy x2, coordxy y2, + struct selectionvar *ov) +{ + int d0, dx, dy, ai, bi, xi, yi; + + if (x1 < x2) { + xi = 1; + dx = x2 - x1; + } else { + xi = -1; + dx = x1 - x2; + } + if (y1 < y2) { + yi = 1; + dy = y2 - y1; + } else { + yi = -1; + dy = y1 - y2; + } + + selection_setpoint(x1, y1, ov, 1); + + if (!dx && !dy) { + /* single point - already all done */ + ; + } else if (dx > dy) { + ai = (dy - dx) * 2; + bi = dy * 2; + d0 = bi - dx; + do { + if (d0 >= 0) { + y1 += yi; + d0 += ai; + } else + d0 += bi; + x1 += xi; + selection_setpoint(x1, y1, ov, 1); + } while (x1 != x2); + } else { + ai = (dx - dy) * 2; + bi = dx * 2; + d0 = bi - dy; + do { + if (d0 >= 0) { + x1 += xi; + d0 += ai; + } else + d0 += bi; + y1 += yi; + selection_setpoint(x1, y1, ov, 1); + } while (y1 != y2); + } +} + +void +selection_do_randline( + coordxy x1, coordxy y1, + coordxy x2, coordxy y2, + schar rough, + schar rec, + struct selectionvar *ov) +{ + int mx, my; + int dx, dy; + + if (rec < 1 || (x2 == x1 && y2 == y1)) + return; + + if (rough > max(abs(x2 - x1), abs(y2 - y1))) + rough = max(abs(x2 - x1), abs(y2 - y1)); + + if (rough < 2) { + mx = ((x1 + x2) / 2); + my = ((y1 + y2) / 2); + } else { + do { + dx = rn2(rough) - (rough / 2); + dy = rn2(rough) - (rough / 2); + mx = ((x1 + x2) / 2) + dx; + my = ((y1 + y2) / 2) + dy; + } while ((mx > COLNO - 1 || mx < 0 || my < 0 || my > ROWNO - 1)); + } + + if (!selection_getpoint(mx, my, ov)) { + selection_setpoint(mx, my, ov, 1); + } + + rough = (rough * 2) / 3; + + rec--; + + selection_do_randline(x1, y1, mx, my, rough, rec, ov); + selection_do_randline(mx, my, x2, y2, rough, rec, ov); + + selection_setpoint(x2, y2, ov, 1); +} + +void +selection_iterate( + struct selectionvar *ov, + select_iter_func func, + genericptr_t arg) +{ + coordxy x, y; + NhRect rect = cg.zeroNhRect; + + if (!ov) + return; + + selection_getbounds(ov, &rect); + + for (x = rect.lx; x <= rect.hx; x++) + for (y = rect.ly; y <= rect.hy; y++) + if (isok(x,y) && selection_getpoint(x, y, ov)) + (*func)(x, y, arg); +} + +/* selection is not rectangular, or has holes in it */ +boolean +selection_is_irregular(struct selectionvar *sel) +{ + coordxy x, y; + NhRect rect = cg.zeroNhRect; + + selection_getbounds(sel, &rect); + + for (x = rect.lx; x <= rect.hx; x++) + for (y = rect.ly; y <= rect.hy; y++) + if (isok(x,y) && !selection_getpoint(x, y, sel)) + return TRUE; + + return FALSE; +} + +/* return a description of the selection size */ +char * +selection_size_description(struct selectionvar *sel, char *buf) +{ + NhRect rect = cg.zeroNhRect; + coordxy dx, dy; + + selection_getbounds(sel, &rect); + dx = rect.hx - rect.lx + 1; + dy = rect.hy - rect.ly + 1; + Sprintf(buf, "%s %i by %i", + selection_is_irregular(sel) ? "irregularly shaped" + : (dx == dy) ? "square" + : "rectangular", + dx, dy); + return buf; +} + +struct selectionvar * +selection_from_mkroom(struct mkroom *croom) +{ + struct selectionvar *sel = selection_new(); + coordxy x, y; + unsigned rmno; + + if (!croom && gc.coder && gc.coder->croom) + croom = gc.coder->croom; + if (!croom) + return sel; + + rmno = (unsigned)((croom - svr.rooms) + ROOMOFFSET); + for (y = croom->ly; y <= croom->hy; y++) + for (x = croom->lx; x <= croom->hx; x++) + if (isok(x, y) && !levl[x][y].edge + && levl[x][y].roomno == rmno) + selection_setpoint(x, y, sel, 1); + return sel; +} + +void +selection_force_newsyms(struct selectionvar *sel) +{ + coordxy x, y; + + for (x = 1; x < sel->wid; x++) + for (y = 0; y < sel->hei; y++) + if (selection_getpoint(x, y, sel)) + newsym_force(x, y); +} + +/*selvar.c*/ diff --git a/src/sfstruct.c b/src/sfstruct.c index 6c15b185f0..ae0b2c5fd8 100644 --- a/src/sfstruct.c +++ b/src/sfstruct.c @@ -10,7 +10,7 @@ * These were moved here from save.c and restore.c between 3.6.3 and 3.7.0. */ -static int getidx(int, int); +staticfn int getidx(int, int); #if defined(UNIX) || defined(WIN32) #define USE_BUFFERING @@ -46,7 +46,7 @@ static FILE *bw_FILE[MAXFD] = {0,0,0,0,0}; * Once buffered IO (stdio) has been enabled on the file * associated with a descriptor via fdopen(): * - * 1. If you use bufoff and bufon to try and toggle the + * 1. If you use bufoff and bufon to try to toggle the * use of write vs fwrite; the code just tracks which * routine is to be called through the tracking * variables and acts accordingly. @@ -66,7 +66,7 @@ static FILE *bw_FILE[MAXFD] = {0,0,0,0,0}; * happen. */ -static int +staticfn int getidx(int fd, int flg) { int i, retval = -1; @@ -194,7 +194,7 @@ bwrite(int fd, const genericptr_t loc, unsigned num) } if (failed) { #if defined(HANGUPHANDLING) - if (gp.program_state.done_hup) + if (program_state.done_hup) nh_terminate(EXIT_FAILURE); else #endif @@ -230,7 +230,7 @@ mread(int fd, genericptr_t buf, unsigned len) } else { pline("Read %d instead of %u bytes.", (int) rlen, len); display_nhwindow(WIN_MESSAGE, TRUE); /* flush before error() */ - if (gp.program_state.restoring) { + if (program_state.restoring) { (void) nhclose(fd); (void) delete_savefile(); error("Error restoring old game."); diff --git a/src/shk.c b/src/shk.c index 67f4f24140..b57b45a21d 100644 --- a/src/shk.c +++ b/src/shk.c @@ -1,23 +1,59 @@ -/* NetHack 3.7 shk.c $NHDT-Date: 1652299941 2022/05/11 20:12:21 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.232 $ */ +/* 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. */ #include "hack.h" -#define PAY_SOME 2 +/* + * FIXME: + * The normal shop messages are verbal. There are a lot of cases + * where an alternate message is given if the hero is deaf or shk + * is mute (when poly'd), but that is usually visual-based. It is + * possible for hero to pay for items while blind (only if adjacent + * to shk) and the alternate messages fail to account for that. + */ + #define PAY_BUY 1 #define PAY_CANT 0 /* too poor */ #define PAY_SKIP (-1) #define PAY_BROKE (-2) -static void makekops(coord *); -static void call_kops(struct monst *, boolean); -static void kops_gone(boolean); +enum billitem_status { + FullyUsedUp = 1, /* completely used up; obj->where==OBJ_ONBILL */ + PartlyUsedUp = 2, /* partly used up; obj->where==OBJ_INVENT or similar */ + PartlyIntact = 3, /* intact portion of partly used up item */ + FullyIntact = 4, /* normal unpaid item */ + 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 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; /* 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; + +staticfn void makekops(coord *); +staticfn void getcad(struct monst *, const char *, coordxy, coordxy, boolean, + boolean, boolean); +staticfn void call_kops(struct monst *, boolean); +staticfn void kops_gone(boolean); #define NOTANGRY(mon) ((mon)->mpeaceful) #define ANGRY(mon) (!NOTANGRY(mon)) -#define IS_SHOP(x) (gr.rooms[x].rtype >= SHOPBASE) +#define IS_SHOP(x) (svr.rooms[x].rtype >= SHOPBASE) #define muteshk(shkp) (helpless(shkp) || (shkp)->data->msound <= MS_ANIMAL) @@ -26,60 +62,75 @@ extern const struct shclass shtypes[]; /* defined in shknam.c */ static const char and_its_contents[] = " and its contents"; static const char the_contents_of[] = "the contents of "; -static void append_honorific(char *); -static long addupbill(struct monst *); -static void pacify_shk(struct monst *, boolean); -static struct bill_x *onbill(struct obj *, struct monst *, boolean); -static struct monst *next_shkp(struct monst *, boolean); -static long shop_debt(struct eshk *); -static char *shk_owns(char *, struct obj *); -static char *mon_owns(char *, struct obj *); -static void clear_unpaid_obj(struct monst *, struct obj *); -static void clear_unpaid(struct monst *, struct obj *); -static void clear_no_charge_obj(struct monst *, struct obj *); -static void clear_no_charge(struct monst *, struct obj *); -static long check_credit(long, struct monst *); -static void pay(long, struct monst *); -static long get_cost(struct obj *, struct monst *); -static long set_cost(struct obj *, struct monst *); -static const char *shk_embellish(struct obj *, long); -static long cost_per_charge(struct monst *, struct obj *, boolean); -static long cheapest_item(struct monst *); -static int dopayobj(struct monst *, struct bill_x *, struct obj **, int, - boolean); -static long stolen_container(struct obj *, struct monst *, long, boolean); -static long getprice(struct obj *, boolean); -static void shk_names_obj(struct monst *, struct obj *, const char *, long, +staticfn void append_honorific(char *); +staticfn long addupbill(struct monst *); +staticfn void pacify_shk(struct monst *, boolean); +staticfn struct bill_x *onbill(struct obj *, struct monst *, boolean); +staticfn struct monst *next_shkp(struct monst *, boolean); +staticfn long shop_debt(struct eshk *); +staticfn char *shk_owns(char *, struct obj *); +staticfn char *mon_owns(char *, struct obj *); +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 *); +staticfn long set_cost(struct obj *, struct monst *); +staticfn const char *shk_embellish(struct obj *, long); +staticfn long cost_per_charge(struct monst *, struct obj *, boolean); + +staticfn int QSORTCALLBACK sortbill_cmp(const genericptr, const genericptr) + NONNULLPTRS; +staticfn long cheapest_item(int, Bill *) NONNULLPTRS; +staticfn int make_itemized_bill(struct monst *shkp, Bill **ibill) NONNULLPTRS; +staticfn int menu_pick_pay_items(int, Bill *) NONNULLPTRS; +staticfn boolean pay_billed_items(struct monst *, int, Bill *, boolean, + boolean *) NONNULLPTRS; +staticfn void update_bill(int, int, Bill *, struct eshk *, struct bill_x *, + struct obj *) NONNULLPTRS; +staticfn int dopayobj(struct monst *, struct bill_x *, struct obj *, int, + boolean, boolean) NONNULLPTRS; +staticfn int buy_container(struct monst *, int, int, Bill *) NONNULLPTRS; +staticfn void reject_purchase(struct monst *, struct obj *, long) NONNULLPTRS; +staticfn boolean insufficient_funds(struct monst *, struct obj *, long) + NONNULLPTRS; +staticfn long stolen_container(struct obj *, struct monst *, long, boolean); +staticfn long corpsenm_price_adj(struct obj *); +staticfn long getprice(struct obj *, boolean); +staticfn void shk_names_obj(struct monst *, struct obj *, const char *, long, const char *); -static boolean inherits(struct monst *, int, int, boolean); -static void set_repo_loc(struct monst *); -static struct obj *bp_to_obj(struct bill_x *); -static long get_pricing_units(struct obj *); -static boolean angry_shk_exists(void); -static void home_shk(struct monst *, boolean); -static void rile_shk(struct monst *); -static void rouse_shk(struct monst *, boolean); -static boolean shk_impaired(struct monst *); -static boolean repairable_damage(struct damage *, struct monst *); -static struct damage *find_damage(struct monst *); -static void discard_damage_struct(struct damage *); -static void discard_damage_owned_by(struct monst *); -static void shk_fixes_damage(struct monst *); -static uint8 litter_getpos(uint8 *, coordxy, coordxy, struct monst *); -static void litter_scatter(uint8 *, coordxy, coordxy, struct monst *); -static void litter_newsyms(uint8 *, coordxy, coordxy); -static int repair_damage(struct monst *, struct damage *, boolean); -static void sub_one_frombill(struct obj *, struct monst *); -static void add_one_tobill(struct obj *, boolean, struct monst *); -static void dropped_container(struct obj *, struct monst *, boolean); -static void add_to_billobjs(struct obj *); -static void bill_box_content(struct obj *, boolean, boolean, +staticfn boolean inherits(struct monst *, int, int, boolean); +staticfn void set_repo_loc(struct monst *); +staticfn struct obj *bp_to_obj(struct bill_x *); +staticfn long get_pricing_units(struct obj *); +staticfn boolean angry_shk_exists(void); +staticfn void home_shk(struct monst *, boolean); +staticfn void rile_shk(struct monst *); +staticfn void rouse_shk(struct monst *, boolean); +staticfn boolean shk_impaired(struct monst *); +staticfn boolean repairable_damage(struct damage *, struct monst *); +staticfn struct damage *find_damage(struct monst *); +staticfn void discard_damage_struct(struct damage *); +staticfn void discard_damage_owned_by(struct monst *); +staticfn void shk_fixes_damage(struct monst *); +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 *) 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 *); +staticfn void bill_box_content(struct obj *, boolean, boolean, struct monst *); -static boolean rob_shop(struct monst *); -static void deserted_shop(char *); -static boolean special_stock(struct obj *, struct monst *, boolean); -static const char *cad(boolean); -static char *shk_plname_title(void); +staticfn boolean rob_shop(struct monst *); +staticfn void deserted_shop(char *); +staticfn boolean special_stock(struct obj *, struct monst *, boolean); +staticfn const char *cad(boolean); +staticfn char *shk_plname_title(void); /* invariants: obj->unpaid iff onbill(obj) [unless bp->useup] @@ -104,7 +155,7 @@ static const char *const angrytexts[] = { * if the monster kept the change. */ long -money2mon(struct monst* mon, long amount) +money2mon(struct monst *mon, long amount) { struct obj *ygold = findgold(gi.invent, TRUE); @@ -123,7 +174,7 @@ money2mon(struct monst* mon, long amount) remove_worn_item(ygold, FALSE); /* quiver */ freeinv(ygold); add_to_minv(mon, ygold); - gc.context.botl = 1; + disp.botl = TRUE; return amount; } @@ -133,7 +184,7 @@ money2mon(struct monst* mon, long amount) * the priest gives you money for an ale. */ void -money2u(struct monst* mon, long amount) +money2u(struct monst *mon, long amount) { struct obj *mongold = findgold(mon->minvent, TRUE); @@ -151,17 +202,18 @@ money2u(struct monst* mon, long amount) mongold = splitobj(mongold, amount); obj_extract_self(mongold); - if (!merge_choice(gi.invent, mongold) && inv_cnt(FALSE) >= 52) { + if (!merge_choice(gi.invent, mongold) + && inv_cnt(FALSE) >= invlet_basic) { You("have no room for the gold!"); dropy(mongold); } else { addinv(mongold); - gc.context.botl = 1; + disp.botl = TRUE; } } -static struct monst * -next_shkp(register struct monst *shkp, boolean withbill) +staticfn struct monst * +next_shkp(struct monst *shkp, boolean withbill) { for (; shkp; shkp = shkp->nmon) { if (DEADMONSTER(shkp)) @@ -184,7 +236,7 @@ void shkgone(struct monst *mtmp) { struct eshk *eshk = ESHK(mtmp); - struct mkroom *sroom = &gr.rooms[eshk->shoproom - ROOMOFFSET]; + struct mkroom *sroom = &svr.rooms[eshk->shoproom - ROOMOFFSET]; struct obj *otmp; char *p; int sx, sy; @@ -195,12 +247,12 @@ shkgone(struct monst *mtmp) discard_damage_owned_by(mtmp); sroom->resident = (struct monst *) 0; if (!search_special(ANY_SHOP)) - gl.level.flags.has_shop = 0; + svl.level.flags.has_shop = 0; /* items on shop floor revert to ordinary objects */ for (sx = sroom->lx; sx <= sroom->hx; sx++) for (sy = sroom->ly; sy <= sroom->hy; sy++) - for (otmp = gl.level.objects[sx][sy]; otmp; + for (otmp = svl.level.objects[sx][sy]; otmp; otmp = otmp->nexthere) otmp->no_charge = 0; @@ -221,14 +273,14 @@ void set_residency(struct monst *shkp, boolean zero_out) { if (on_level(&(ESHK(shkp)->shoplevel), &u.uz)) - gr.rooms[ESHK(shkp)->shoproom - ROOMOFFSET].resident = + svr.rooms[ESHK(shkp)->shoproom - ROOMOFFSET].resident = (zero_out) ? (struct monst *) 0 : shkp; } void replshk(struct monst *mtmp, struct monst *mtmp2) { - gr.rooms[ESHK(mtmp2)->shoproom - ROOMOFFSET].resident = mtmp2; + svr.rooms[ESHK(mtmp2)->shoproom - ROOMOFFSET].resident = mtmp2; if (inhishop(mtmp) && *u.ushops == ESHK(mtmp)->shoproom) { ESHK(mtmp2)->bill_p = &(ESHK(mtmp2)->bill[0]); } @@ -247,14 +299,14 @@ restshk(struct monst *shkp, boolean ghostly) /* savebones guarantees that non-homed shk's will be gone */ if (ghostly) { assign_level(&eshkp->shoplevel, &u.uz); - if (ANGRY(shkp) && strncmpi(eshkp->customer, gp.plname, PL_NSIZ)) + if (ANGRY(shkp) && strncmpi(eshkp->customer, svp.plname, PL_NSIZ)) pacify_shk(shkp, TRUE); } } } /* clear the unpaid bit on a single object and its contents */ -static void +staticfn void clear_unpaid_obj(struct monst *shkp, struct obj *otmp) { if (Has_contents(otmp)) @@ -264,7 +316,7 @@ clear_unpaid_obj(struct monst *shkp, struct obj *otmp) } /* clear the unpaid bit on all of the objects in the list */ -static void +staticfn void clear_unpaid(struct monst *shkp, struct obj *list) { while (list) { @@ -274,7 +326,7 @@ clear_unpaid(struct monst *shkp, struct obj *list) } /* clear the no_charge bit on a single object and its contents */ -static void +staticfn void clear_no_charge_obj( struct monst *shkp, /* if null, clear regardless of shop */ struct obj *otmp) @@ -315,14 +367,14 @@ clear_no_charge_obj( || !isok(x, y) || (rno = levl[x][y].roomno) < ROOMOFFSET || !IS_SHOP(rno - ROOMOFFSET) - || (rm_shkp = gr.rooms[rno - ROOMOFFSET].resident) == 0 + || (rm_shkp = svr.rooms[rno - ROOMOFFSET].resident) == 0 || rm_shkp == shkp) otmp->no_charge = 0; } } /* clear the no_charge bit on all of the objects in the list */ -static void +staticfn void clear_no_charge(struct monst *shkp, struct obj *list) { while (list) { @@ -333,17 +385,28 @@ 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(register struct monst *shkp) +setpaid(struct monst *shkp) { - register struct obj *obj; - register struct monst *mtmp; + struct obj *obj; + struct monst *mtmp; clear_unpaid(shkp, gi.invent); clear_unpaid(shkp, fobj); - if (gl.level.buriedobjlist) - clear_unpaid(shkp, gl.level.buriedobjlist); + if (svl.level.buriedobjlist) + clear_unpaid(shkp, svl.level.buriedobjlist); if (gt.thrownobj) clear_unpaid_obj(shkp, gt.thrownobj); if (gk.kickedobj) @@ -357,7 +420,7 @@ setpaid(register struct monst *shkp) /* clear obj->no_charge for all obj in shkp's shop */ clear_no_charge(shkp, fobj); - clear_no_charge(shkp, gl.level.buriedobjlist); + clear_no_charge(shkp, svl.level.buriedobjlist); while ((obj = gb.billobjs) != 0) { obj_extract_self(obj); @@ -371,7 +434,7 @@ setpaid(register struct monst *shkp) } } -static long +staticfn long addupbill(struct monst *shkp) { int ct = ESHK(shkp)->billct; @@ -385,7 +448,7 @@ addupbill(struct monst *shkp) return total; } -static void +staticfn void call_kops(struct monst *shkp, boolean nearshop) { /* Keystone Kops srt@ucla */ @@ -398,13 +461,13 @@ call_kops(struct monst *shkp, boolean nearshop) if (!Deaf) pline("An alarm sounds!"); - nokops = ((gm.mvitals[PM_KEYSTONE_KOP].mvflags & G_GONE) - && (gm.mvitals[PM_KOP_SERGEANT].mvflags & G_GONE) - && (gm.mvitals[PM_KOP_LIEUTENANT].mvflags & G_GONE) - && (gm.mvitals[PM_KOP_KAPTAIN].mvflags & G_GONE)); + nokops = ((svm.mvitals[PM_KEYSTONE_KOP].mvflags & G_GONE) + && (svm.mvitals[PM_KOP_SERGEANT].mvflags & G_GONE) + && (svm.mvitals[PM_KOP_LIEUTENANT].mvflags & G_GONE) + && (svm.mvitals[PM_KOP_KAPTAIN].mvflags & G_GONE)); if (!angry_guards(!!Deaf) && nokops) { - if (Verbose(3, call_kops1) && !Deaf) + if (flags.verbose && !Deaf) pline("But no one seems to respond to it."); return; } @@ -420,14 +483,14 @@ call_kops(struct monst *shkp, boolean nearshop) if (nearshop) { /* Create swarm around you, if you merely "stepped out" */ - if (Verbose(3, call_kops2)) + if (flags.verbose) pline_The("Keystone Kops appear!"); mm.x = u.ux; mm.y = u.uy; makekops(&mm); return; } - if (Verbose(3, call_kops3)) + if (flags.verbose) pline_The("Keystone Kops are after you!"); /* Create swarm near down staircase (hinders return to level) */ if (isok(sx, sy)) { @@ -488,7 +551,7 @@ u_left_shop(char *leavestring, boolean newlev) SetVoice(shkp, 0, 80, 0); verbalize(not_upset ? "%s! Please pay before leaving." : "%s! Don't you leave without paying!", - gp.plname); + svp.plname); } else { pline("%s %s that you need to pay before leaving%s", Shknam(shkp), @@ -562,8 +625,8 @@ remote_burglary(coordxy x, coordxy y) /* shop merchandise has been taken; pay for it with any credit available; return false if the debt is fully covered by credit, true otherwise */ -static boolean -rob_shop(struct monst* shkp) +staticfn boolean +rob_shop(struct monst *shkp) { struct eshk *eshkp; long total; @@ -598,11 +661,11 @@ rob_shop(struct monst* shkp) } /* give a message when entering an untended shop (caller has verified that) */ -static void -deserted_shop(/*const*/ char* enterstring) +staticfn void +deserted_shop(/*const*/ char *enterstring) { struct monst *mtmp; - struct mkroom *r = &gr.rooms[(int) *enterstring - ROOMOFFSET]; + struct mkroom *r = &svr.rooms[(int) *enterstring - ROOMOFFSET]; int x, y, m = 0, n = 0; for (x = r->lx; x <= r->hx; ++x) @@ -664,11 +727,11 @@ u_entered_shop(char *enterstring) eshkp->bill_p = &(eshkp->bill[0]); if ((!eshkp->visitct || *eshkp->customer) - && strncmpi(eshkp->customer, gp.plname, PL_NSIZ)) { + && strncmpi(eshkp->customer, svp.plname, PL_NSIZ)) { /* You seem to be new here */ eshkp->visitct = 0; eshkp->following = 0; - (void) strncpy(eshkp->customer, gp.plname, PL_NSIZ); + (void) strncpy(eshkp->customer, svp.plname, PL_NSIZ); pacify_shk(shkp, TRUE); } @@ -687,23 +750,23 @@ u_entered_shop(char *enterstring) return; } - rt = gr.rooms[*enterstring - ROOMOFFSET].rtype; + rt = svr.rooms[*enterstring - ROOMOFFSET].rtype; if (ANGRY(shkp)) { if (!Deaf && !muteshk(shkp)) { SetVoice(shkp, 0, 80, 0); - verbalize("So, %s, you dare return to %s %s?!", gp.plname, + verbalize("So, %s, you dare return to %s %s?!", svp.plname, s_suffix(shkname(shkp)), shtypes[rt - SHOPBASE].name); } else { pline("%s seems %s over your return to %s %s!", - Shknam(shkp), angrytexts[rn2(SIZE(angrytexts))], + Shknam(shkp), ROLL_FROM(angrytexts), noit_mhis(shkp), shtypes[rt - SHOPBASE].name); } } else if (eshkp->surcharge) { if (!Deaf && !muteshk(shkp)) { SetVoice(shkp, 0, 80, 0); verbalize("Back again, %s? I've got my %s on you.", - gp.plname, mbodypart(shkp, EYE)); + svp.plname, mbodypart(shkp, EYE)); } else { pline_The("atmosphere at %s %s seems unwelcoming.", s_suffix(shkname(shkp)), shtypes[rt - SHOPBASE].name); @@ -798,7 +861,7 @@ u_entered_shop(char *enterstring) /* called when removing a pick-axe or mattock from a container */ void -pick_pick(struct obj* obj) +pick_pick(struct obj *obj) { struct monst *shkp; @@ -810,7 +873,7 @@ pick_pick(struct obj* obj) /* if you bring a sack of N picks into a shop to sell, don't repeat this N times when they're taken out */ - if (gm.moves != pickmovetime) { + if (svm.moves != pickmovetime) { if (!Deaf && !muteshk(shkp)) { SetVoice(shkp, 0, 80, 0); verbalize("You sneaky %s! Get out of here with that pick!", @@ -822,19 +885,19 @@ pick_pick(struct obj* obj) : "is dismayed because of"); } } - pickmovetime = gm.moves; + pickmovetime = svm.moves; } } /* - Decide whether two unpaid items are mergable; caller is responsible for + Decide whether two unpaid items are mergeable; caller is responsible for making sure they're unpaid and the same type of object; we check the price quoted by the shopkeeper and also that they both belong to the same shk. */ boolean -same_price(struct obj* obj1, struct obj* obj2) +same_price(struct obj *obj1, struct obj *obj2) { - register struct monst *shkp1, *shkp2; + struct monst *shkp1, *shkp2; struct bill_x *bp1 = 0, *bp2 = 0; boolean are_mergable = FALSE; @@ -866,8 +929,8 @@ same_price(struct obj* obj1, struct obj* obj2) * turning the `$' command into a way to discover that the current * level is bones data which has a shk on the warpath. */ -static long -shop_debt(struct eshk* eshkp) +staticfn long +shop_debt(struct eshk *eshkp) { struct bill_x *bp; int ct; @@ -933,7 +996,7 @@ shop_keeper(char rmno) { struct monst *shkp; - shkp = (rmno >= ROOMOFFSET) ? gr.rooms[rmno - ROOMOFFSET].resident : 0; + shkp = (rmno >= ROOMOFFSET) ? svr.rooms[rmno - ROOMOFFSET].resident : 0; if (shkp) { if (has_eshk(shkp)) { if (ANGRY(shkp)) { @@ -946,13 +1009,13 @@ shop_keeper(char rmno) shkp->isshk ? "shopkeeper career change" : "shop resident not shopkeeper", (int) rmno, - (int) gr.rooms[rmno - ROOMOFFSET].rtype, + (int) svr.rooms[rmno - ROOMOFFSET].rtype, shkp->mnum, /* [real shopkeeper name is kept in ESHK, not MGIVENNAME] */ has_mgivenname(shkp) ? MGIVENNAME(shkp) : "anonymous"); /* not sure if this is appropriate, because it does nothing to - correct the underlying gr.rooms[].resident issue but... */ + correct the underlying svr.rooms[].resident issue but... */ return (struct monst *) 0; } } @@ -1002,7 +1065,7 @@ tended_shop(struct mkroom *sroom) return !mtmp ? FALSE : (boolean) inhishop(mtmp); } -static struct bill_x * +staticfn struct bill_x * onbill(struct obj *obj, struct monst *shkp, boolean silent) { if (shkp) { @@ -1034,7 +1097,7 @@ onshopbill(struct obj *obj, struct monst *shkp, boolean silent) /* check whether an object or any of its contents belongs to a shop */ boolean -is_unpaid(struct obj* obj) +is_unpaid(struct obj *obj) { return (boolean) (obj->unpaid || (Has_contents(obj) && count_unpaid(obj->cobj))); @@ -1042,9 +1105,9 @@ is_unpaid(struct obj* obj) /* Delete the contents of the given object. */ void -delete_contents(register struct obj* obj) +delete_contents(struct obj *obj) { - register struct obj *curr; + struct obj *curr; while ((curr = obj->cobj) != 0) { obj_extract_self(curr); @@ -1054,11 +1117,11 @@ delete_contents(register struct obj* obj) /* called with two args on merge */ void -obfree(register struct obj* obj, register struct obj* merge) +obfree(struct obj *obj, struct obj *merge) { - register struct bill_x *bp; - register struct bill_x *bpm; - register struct monst *shkp; + struct bill_x *bp; + struct bill_x *bpm; + struct monst *shkp; if (obj->otyp == LEASH && obj->leashmon) o_unleash(obj); @@ -1091,9 +1154,9 @@ obfree(register struct obj* obj, register struct obj* merge) if ((bp = onbill(obj, shkp, FALSE)) != 0) { if (!merge) { - bp->useup = 1; + bp->useup = TRUE; obj->unpaid = 0; /* only for doinvbill */ - /* for used up glob, put back origial weight in case it gets + /* for used up glob, put back original weight in case it gets formatted ('I x' or itemized billing) with 'wizweight' On */ if (obj->globby && !obj->owt && has_omid(obj)) obj->owt = OMID(obj); @@ -1112,19 +1175,12 @@ obfree(register struct obj* obj, register 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 @@ -1149,8 +1205,8 @@ obfree(register struct obj* obj, register struct obj* merge) dealloc_obj(obj); } -static long -check_credit(long tmp, register struct monst* shkp) +staticfn long +check_credit(long tmp, struct monst *shkp) { long credit = ESHK(shkp)->credit; @@ -1168,8 +1224,8 @@ check_credit(long tmp, register struct monst* shkp) return tmp; } -static void -pay(long tmp, register struct monst* shkp) +staticfn void +pay(long tmp, struct monst *shkp) { long robbed = ESHK(shkp)->robbed; long balance = ((tmp <= 0L) ? tmp : check_credit(tmp, shkp)); @@ -1178,7 +1234,7 @@ pay(long tmp, register struct monst* shkp) money2mon(shkp, balance); else if (balance < 0) money2u(shkp, -balance); - gc.context.botl = 1; + disp.botl = TRUE; if (robbed) { robbed -= tmp; if (robbed < 0) @@ -1188,13 +1244,13 @@ pay(long tmp, register struct monst* shkp) } /* return shkp to home position */ -static void +staticfn void home_shk(struct monst *shkp, boolean killkops) { coordxy x = ESHK(shkp)->shk.x, y = ESHK(shkp)->shk.y; (void) mnearto(shkp, x, y, TRUE, RLOC_NOMSG); - gl.level.flags.has_shop = 1; + svl.level.flags.has_shop = 1; if (killkops) { kops_gone(TRUE); pacify_guards(); @@ -1202,10 +1258,10 @@ home_shk(struct monst *shkp, boolean killkops) after_shk_move(shkp); } -static boolean +staticfn boolean angry_shk_exists(void) { - register struct monst *shkp; + struct monst *shkp; for (shkp = next_shkp(fmon, FALSE); shkp; shkp = next_shkp(shkp->nmon, FALSE)) @@ -1215,17 +1271,17 @@ angry_shk_exists(void) } /* remove previously applied surcharge from all billed items */ -static void +staticfn void pacify_shk(struct monst *shkp, boolean clear_surcharge) { NOTANGRY(shkp) = TRUE; /* make peaceful */ if (clear_surcharge && ESHK(shkp)->surcharge) { - register struct bill_x *bp = ESHK(shkp)->bill_p; - register int ct = ESHK(shkp)->billct; + struct bill_x *bp = ESHK(shkp)->bill_p; + int ct = ESHK(shkp)->billct; ESHK(shkp)->surcharge = FALSE; while (ct-- > 0) { - register long reduction = (bp->price + 3L) / 4L; + long reduction = (bp->price + 3L) / 4L; bp->price -= reduction; /* undo 33% increase */ bp++; } @@ -1233,14 +1289,14 @@ pacify_shk(struct monst *shkp, boolean clear_surcharge) } /* add aggravation surcharge to all billed items */ -static void +staticfn void rile_shk(struct monst *shkp) { NOTANGRY(shkp) = FALSE; /* make angry */ if (!ESHK(shkp)->surcharge) { - register long surcharge; - register struct bill_x *bp = ESHK(shkp)->bill_p; - register int ct = ESHK(shkp)->billct; + long surcharge; + struct bill_x *bp = ESHK(shkp)->bill_p; + int ct = ESHK(shkp)->billct; ESHK(shkp)->surcharge = TRUE; while (ct-- > 0) { @@ -1252,7 +1308,7 @@ rile_shk(struct monst *shkp) } /* wakeup and/or unparalyze shopkeeper */ -static void +staticfn void rouse_shk(struct monst *shkp, boolean verbosely) { if (helpless(shkp)) { @@ -1327,13 +1383,14 @@ hot_pursuit(struct monst *shkp) return; rile_shk(shkp); - (void) strncpy(ESHK(shkp)->customer, gp.plname, PL_NSIZ); + (void) strncpy(ESHK(shkp)->customer, svp.plname, PL_NSIZ); ESHK(shkp)->following = 1; /* shopkeeper networking: clear obj->no_charge for all obj on the 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 @@ -1366,33 +1423,265 @@ static const char no_money[] = "Moreover, you%s have no gold.", not_enough_money[] = "Besides, you don't have enough to interest %s."; +/* if one item is used-up and the other isn't, the used-up one comes first; + otherwise, if their costs differ, the more expensive one comes first; + if costs are the same, use internal index as tie-breaker for stable sort */ +staticfn int QSORTCALLBACK +sortbill_cmp(const genericptr vptr1, const genericptr vptr2) +{ + const struct sortbill_item *sbi1 = (struct sortbill_item *) vptr1, + *sbi2 = (struct sortbill_item *) vptr2; + long cost1 = sbi1->cost, cost2 = sbi2->cost; + int bidx1 = sbi1->bidx, bidx2 = sbi2->bidx, + /* sort such that FullyUsedUp and PartlyUsedUp come before + PartlyIntact, FullyIntact, KnownContainer, UndisclosedContainer */ + used1 = sbi1->usedup <= PartlyUsedUp, /* 0=>unpaid, 1=>used */ + used2 = sbi2->usedup <= PartlyUsedUp; + + if (used1 != used2) + return (used2 - used1); /* bigger comes before smaller here */ + if (cost1 != cost2) + return (cost2 - cost1); /* bigger comes before smaller here too */ + /* index into eshkp->bill_p[] isn't unique (an item that is partly + used and partly intact will have two ibill[] entries indexing same + bill_p[] element) but duplicates won't reach here (used1 vs used2) */ + return (bidx1 - bidx2); +} + /* delivers the cheapest item on the list */ -static long -cheapest_item(struct monst *shkp) +staticfn long +cheapest_item(int ibillct, Bill *ibill) { - register int ct = ESHK(shkp)->billct; - register struct bill_x *bp = ESHK(shkp)->bill_p; - register long gmin = (bp->price * bp->bquan); + int i; + long gmin = ibill[0].cost; - while (ct--) { - if (bp->price * bp->bquan < gmin) - gmin = bp->price * bp->bquan; - bp++; - } + /* + * 3.7: old version didn't determine cheapest item correctly if it + * was either the partly used or partly intact portion of a partially + * used stack. Rather than modify it to use bp_to_obj() in order to + * obtain quanities for every entry on eshkp->bill_p[], switch to + * ibill[] which has already split such items into separate entries. + */ + + for (i = 1; i < ibillct; ++i) + if (ibill[i].cost < gmin) + gmin = ibill[i].cost; return gmin; } + +/* for itemized purchasing, create an alternate shop bill that hides + container contents */ +staticfn int /* returns number of entries */ +make_itemized_bill( + struct monst *shkp, + Bill **ibill_p) /* output, augmented bill similar to a 'sortloot array' */ +{ + static Bill zerosbi; /* Null sortbill item */ + Bill *ibill; + struct bill_x *bp; + struct obj *otmp; + struct eshk *eshkp = ESHK(shkp); + int i, n, bidx, ebillct = eshkp->billct; + int8 used; + long quan, cost; + + /* this overallocates unless there happens to be a used-up portion + and an intact potion for every object on the bill; doing it this + way avoids the need to look up every object on the bill an extra + time; (the +1 for a terminator isn't actually needed) */ + n = 2 * ebillct + 1; + ibill = *ibill_p = (Bill *) alloc(n * sizeof *ibill); + for (i = 0; i < n; ++i) + ibill[i] = zerosbi; + + n = 0; /* number of entries in ibill[]; won't necessary match ebillct */ + for (i = 0; i < ebillct; ++i) { + bp = &eshkp->bill_p[i]; + /* find the object on the bill */ + otmp = bp_to_obj(bp); + if (!otmp) { + 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 + was first unpaid; otmp is on billobjs list where it can + only be seen via Ix and itemized billing while paying shk */ + otmp->quan = bp->bquan; + bp->useup = TRUE; /* (expected to be set already) */ + } else if (otmp->quan < bp->bquan) { + /* item is partly used up; we will create two entries in the + augmented bill: one for the used up part here, another for + the intact part (which might be inside a container if put in + after using part of a stack; used up part isn't) below */ + ibill[n].obj = otmp; + ibill[n].quan = bp->bquan - otmp->quan; + ibill[n].cost = bp->price * ibill[n].quan; + 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 */ + } + + if (otmp->where == OBJ_ONBILL) { + /* completely used up */ + quan = bp->bquan; + cost = bp->price * quan; + used = FullyUsedUp; + } else if (otmp->where == OBJ_CONTAINED || Has_contents(otmp)) { + int j; + struct obj *item = otmp; + boolean cknown = TRUE; /* assume container contents are known */ + + /* when it's in a container, put the container rather than the + specific object into ibill[]; find outermost container */ + while (otmp->where == OBJ_CONTAINED) { + otmp = otmp->ocontainer; + if (!otmp->cknown) + cknown = FALSE; + } + /* this container might already be in ibill[] if it is unpaid + itself or if it holds more than one unpaid item and another + besides this one has already been processed; only include + first instance */ + for (j = 0; j < n; ++j) + if (otmp == ibill[j].obj) + break; + if (j < n) { + /* when already on bill as FullyIntact, update; the cost + saved in ibill[j] is based on the container even if the + entry was initially created for an item of its contents */ + if (ibill[j].usedup == FullyIntact) + ibill[j].usedup = cknown ? KnownContainer + : UndisclosedContainer; + continue; /* 'i' loop */ + } + /* 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 */ + used = (otmp == item) ? FullyIntact + : cknown ? KnownContainer + : UndisclosedContainer; + } else { + /* ordinary unpaid; when partly used, these are values for the + intact portion; might be an empty shop-owned container */ + quan = otmp->quan; + cost = bp->price * quan; + used = (quan < bp->bquan) ? PartlyIntact : FullyIntact; + } + + ibill[n].obj = otmp; + ibill[n].quan = quan; + ibill[n].cost = cost; + 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 and + is excluded from the sort */ + if (n > 1) + qsort((genericptr_t) ibill, n, sizeof *ibill, sortbill_cmp); + return n; +} + +/* show items on your bill in a menu, and ask which to pay. + returns the number of entries selected. */ +staticfn int +menu_pick_pay_items( + int ibillct, /* number of entries in ibill[] */ + Bill *ibill) /* all used up items, if any, precede all intact items */ +{ + struct obj *otmp; + winid win; + anything any; + menu_item *pick_list = (menu_item *) 0; + char *p, buf[BUFSZ]; + long amt, largest_amt, save_quan; + int i, j, n, amt_width; + + any = cg.zeroany; + win = create_nhwindow(NHW_MENU); + start_menu(win, MENU_BEHAVE_STANDARD); + + /* we go through ibill[] twice, first time to control price formatting + during the second */ + largest_amt = 0L; + for (i = 0; i < ibillct; ++i) + if (ibill[i].cost > largest_amt) + largest_amt = ibill[i].cost; + Sprintf(buf, "%ld", largest_amt); + amt_width = (int) strlen(buf); + + /* show the "used up items" header if there are any used up items on + the bill, no matter whether there are also any intact items; + note: ibill[] has been sorted to hold used-up items first */ + if (ibill[0].usedup <= PartlyUsedUp) { + Sprintf(buf, "Used up item%s:", + (ibillct > 1 && ibill[1].usedup <= PartlyUsedUp) ? "s" : ""); + add_menu_heading(win, buf); + } + for (i = 0; i < ibillct; ++i) { + /* the "unpaid items" header is only shown if the "used up items" + one was shown before the first menu entry */ + if (i > 0 && ibill[i - 1].usedup <= PartlyUsedUp + && ibill[i].usedup >= PartlyIntact) { + Sprintf(buf, "Unpaid item%s:", (i < ibillct - 1) ? "s" : ""); + add_menu_heading(win, buf); + } + otmp = ibill[i].obj; + save_quan = otmp->quan; + otmp->quan = ibill[i].quan; /* in case it's partly used */ + p = paydoname(otmp); + otmp->quan = save_quan; + amt = ibill[i].cost; + /* this doesn't support hallucinatory currency because shopkeeper + isn't hallucinating; also, that would mess up the alignment */ + Snprintf(buf, sizeof buf, "%*ld Zm, %s", amt_width, amt, p); + any.a_int = i + 1; /* +1: avoid 0 */ + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, NO_COLOR, buf, + MENU_ITEMFLAGS_NONE); + } + + end_menu(win, "Pay for which items?"); + n = select_menu(win, PICK_ANY, &pick_list); + destroy_nhwindow(win); + + for (j = 0; j < n; ++j) { + /* + * FIXME: + * The menu will accept a subset count for each entry but buying + * doesn't have any support for that. + */ + i = pick_list[j].item.a_int - 1; /* -1: reverse +1 above */ + ibill[i].queuedpay = TRUE; + } + free(pick_list); + /* for ESC, return 0 instead of usual -1 */ + return max(n, 0); +} + /* the #pay command */ int dopay(void) { - register struct eshk *eshkp; - register struct monst *shkp; + struct eshk *eshkp; + struct monst *shkp; struct monst *nxtm, *resident; + Bill *ibill = (Bill *) NULL; long ltmp; long umoney; - int pass, tmp, sk = 0, seensk = 0; - boolean paid = FALSE, stashed_gold = (hidden_gold(TRUE) > 0L); + int sk = 0, seensk = 0, nexttosk = 0; + boolean paid = FALSE, stashed_gold = (hidden_gold(TRUE) > 0L), + pay_done; gm.multi = 0; @@ -1403,16 +1692,21 @@ dopay(void) for (shkp = next_shkp(fmon, FALSE); shkp; shkp = next_shkp(shkp->nmon, FALSE)) { sk++; - if (ANGRY(shkp) && next2u(shkp->mx, shkp->my)) + if (m_next2u(shkp)) { + /* next to an irate shopkeeper? prioritize that */ + if (nxtm && ANGRY(nxtm)) + continue; + nexttosk++; nxtm = shkp; + } if (canspotmon(shkp)) seensk++; if (inhishop(shkp) && (*u.ushops == ESHK(shkp)->shoproom)) resident = shkp; } - if (nxtm) { /* Player should always appease an */ - shkp = nxtm; /* irate shk standing next to them. */ + if (nxtm && nexttosk == 1) { + shkp = nxtm; goto proceed; } @@ -1439,7 +1733,8 @@ dopay(void) shkp = next_shkp(shkp->nmon, FALSE)) if (canspotmon(shkp)) break; - if (shkp != resident && !next2u(shkp->mx, shkp->my)) { + 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)); return ECMD_OK; @@ -1477,7 +1772,7 @@ dopay(void) pline("%s is not interested in your payment.", Monnam(mtmp)); return ECMD_OK; } - if (mtmp != resident && !next2u(mtmp->mx, mtmp->my)) { + if (mtmp != resident && !m_next2u(mtmp)) { pline("%s is too far to receive your payment.", Shknam(mtmp)); return ECMD_OK; } @@ -1504,9 +1799,9 @@ dopay(void) if (shkp != resident && NOTANGRY(shkp)) { umoney = money_cnt(gi.invent); - if (!ltmp) + if (!ltmp) { You("do not owe %s anything.", shkname(shkp)); - else if (!umoney) { + } else if (!umoney) { You("%shave no gold.", stashed_gold ? "seem to " : ""); if (stashed_gold) pline("But you have some gold stashed away."); @@ -1571,7 +1866,7 @@ dopay(void) : shkname(shkp), noit_mhim(shkp)); pay(1000L, shkp); - if (strncmp(eshkp->customer, gp.plname, PL_NSIZ) || rn2(3)) + if (strncmp(eshkp->customer, svp.plname, PL_NSIZ) || rn2(3)) make_happy_shk(shkp, FALSE); else pline("But %s is as angry as ever.", shkname(shkp)); @@ -1599,8 +1894,9 @@ dopay(void) else Strcat(sbuf, "for gold picked up and the use of merchandise."); - } else + } else { Strcat(sbuf, "for the use of merchandise."); + } pline1(sbuf); if (umoney + eshkp->credit < dtmp) { pline("But you don't%s have enough gold%s.", @@ -1618,7 +1914,7 @@ dopay(void) eshkp->debit = 0L; eshkp->loan = 0L; You("pay that debt."); - gc.context.botl = 1; + disp.botl = TRUE; } else { dtmp -= eshkp->credit; eshkp->credit = 0L; @@ -1627,91 +1923,23 @@ dopay(void) eshkp->loan = 0L; pline("That debt is partially offset by your credit."); You("pay the remainder."); - gc.context.botl = 1; + disp.botl = TRUE; } paid = TRUE; } } + /* now check items on bill */ + pay_done = TRUE; /* assume success */ if (eshkp->billct) { - register boolean itemize; - int iprompt; - - umoney = money_cnt(gi.invent); - if (!umoney && !eshkp->credit) { - You("%shave no gold or credit%s.", - stashed_gold ? "seem to " : "", paid ? " left" : ""); - return ECMD_OK; - } - if ((umoney + eshkp->credit) < cheapest_item(shkp)) { - You("don't have enough gold to buy%s the item%s you picked.", - eshkp->billct > 1 ? " any of" : "", plur(eshkp->billct)); - if (stashed_gold) - pline("Maybe you have some gold stashed away?"); - return ECMD_OK; - } + int ibillct = make_itemized_bill(shkp, &ibill); - /* this isn't quite right; it itemizes without asking if the - * single item on the bill is partly used up and partly unpaid */ - iprompt = (eshkp->billct > 1 ? ynq("Itemized billing?") : 'y'); - itemize = (iprompt == 'y'); - if (iprompt == 'q') - goto thanks; - - for (pass = 0; pass <= 1; pass++) { - tmp = 0; - while (tmp < eshkp->billct) { - struct obj *otmp; - register struct bill_x *bp = &(eshkp->bill_p[tmp]); - - /* find the object on one of the lists */ - if ((otmp = bp_to_obj(bp)) != 0) { - /* if completely used up, object quantity is stale; - restoring it to its original value here avoids - making the partly-used-up code more complicated */ - if (bp->useup) - otmp->quan = bp->bquan; - } else { - impossible("Shopkeeper administration out of order."); - setpaid(shkp); /* be nice to the player */ - return ECMD_TIME; - } - if (pass == bp->useup && otmp->quan == bp->bquan) { - /* pay for used-up items on first pass and others - * on second, so player will be stuck in the store - * less often; things which are partly used up - * are processed on both passes */ - tmp++; - } else { - switch (dopayobj(shkp, bp, &otmp, pass, itemize)) { - case PAY_CANT: - return ECMD_TIME; /*break*/ - case PAY_BROKE: - paid = TRUE; - goto thanks; /*break*/ - case PAY_SKIP: - tmp++; - continue; /*break*/ - case PAY_SOME: - paid = TRUE; - if (itemize) - bot(); - continue; /*break*/ - case PAY_BUY: - paid = TRUE; - break; - } - if (itemize) - bot(); - *bp = eshkp->bill_p[--eshkp->billct]; - } - } - } - thanks: - if (!itemize) - update_inventory(); /* Done in dopayobj() if itemize. */ + if (!pay_billed_items(shkp, ibillct, ibill, stashed_gold, &paid)) + pay_done = FALSE; /* skip thank you message */ } - if (!ANGRY(shkp) && paid) { + + /* {mute shk,deaf hero}-aware thank you message */ + if (pay_done && !ANGRY(shkp) && paid) { if (!Deaf && !muteshk(shkp)) { SetVoice(shkp, 0, 80, 0); verbalize("Thank you for shopping in %s %s%s", @@ -1725,7 +1953,192 @@ dopay(void) !eshkp->surcharge ? "!" : "."); } } - return ECMD_TIME; + + if (paid) + update_inventory(); + iflags.menu_requested = FALSE; /* reset */ + /* free the sortbill array used for itemized billing */ + if (ibill) { + free((genericptr_t) ibill), ibill = NULL; + nhUse(ibill); + } + return paid ? ECMD_TIME : ECMD_OK; +} + +/* for menustyle=Traditional, choose between paying for everything (by + declining to itemize), asking item-by-item (by accepting itemization), + or switch to selecting via menu (special 'm' answer at "Itemize? [ynq m]" + prompt); for other menustyles, always select via menu; + player can use 'm' prefix before 'p' command to invert those behaviors; + once the method is chosen, actually pay for the selected items, item by + item for as long as hero has enough credit+cash */ +staticfn boolean +pay_billed_items( + struct monst *shkp, + int ibillct, + Bill *ibill, + boolean stashed_gold, + boolean *paid_p) /* output */ +{ + struct bill_x *bp; + struct obj *otmp; + long umoney; + boolean itemize, more_than_one; + boolean queuedpay = FALSE, via_menu; + int buy, indx, bidx, pass, iprompt, ebillct; + struct eshk *eshkp = ESHK(shkp); + + umoney = money_cnt(gi.invent); + if (!umoney && !eshkp->credit) { + You("%shave no gold or credit%s.", + stashed_gold ? "seem to " : "", *paid_p ? " left" : ""); + return TRUE; + } + bp = eshkp->bill_p; + otmp = bp_to_obj(bp); + ebillct = eshkp->billct; + more_than_one = (ebillct > 1 || otmp->quan < bp->bquan + /* note: will only get here for a single item, so + we can deduce that it is ibill[0] */ + || ibill[0].usedup == UndisclosedContainer); + if ((umoney + eshkp->credit) < cheapest_item(ibillct, ibill)) { + You("don't have enough gold to buy%s the item%s %s.", + more_than_one ? " any of" : "", plur(more_than_one ? 2 : 1), + (ebillct > 1) ? "you've picked" : "on your bill"); + if (stashed_gold) + pline("Maybe you have some gold stashed away?"); + return TRUE; + } + + via_menu = (flags.menu_style != MENU_TRADITIONAL); + /* allow 'm p' to request a menu for menustyle:traditional; + for other styles, it will do the opposite; that doesn't make + a whole lot of sense for a 'request-menu' prefix, but otherwise + it would simply be redundant and there wouldn't be any way to + skip the menu when hero owes for multiple items */ + if (iflags.menu_requested) + via_menu = !via_menu; + /* this will loop for a second iteration iff not initially using a + menu and player answers 'm' at custom ynq prompt */ + do { + if (via_menu /*&& more_than_one*/ ) { + if (!menu_pick_pay_items(ibillct, ibill)) + return TRUE; + queuedpay = TRUE; + itemize = FALSE; + via_menu = FALSE; /* reset so that we don't loop */ + } else { + iprompt = !more_than_one ? 'y' + : yn_function("Itemized billing?", "ynq m", 'q', TRUE); + if (iprompt == 'q') + return TRUE; + itemize = (iprompt == 'y'); + via_menu = (iprompt == 'm'); + } + } while (via_menu); + + /* + * 3.7: this used to make two passes through eshkp->bill_p[], + * the first for used up items and the second for unpaid ones. + * Items which were partly used were processed on both passes. + * + * Now it makes one pass through ibill[], which has all used up + * items sorted to the beginning and unpaid ones sorted to the end. + * Partly used items have two entries for same base item, one in + * each section. + */ + for (indx = 0; indx < ibillct; ++indx) { + if (queuedpay && !ibill[indx].queuedpay) + continue; + + 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 */ + int boxbag_result = buy_container(shkp, indx, ibillct, ibill); + + if (boxbag_result == 0) { + buy = PAY_BUY; + } else { /* buy_container() failed... */ + if (boxbag_result == 2) /* ... but didn't explain why */ + verbalize("You need to remove any unpaid items from" + " that %s and buy them separately.", + simpleonames(otmp)); + 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) + update_bill(indx, ibillct, ibill, eshkp, bp, otmp); + } + switch (buy) { + case PAY_CANT: + return FALSE; + case PAY_BROKE: + *paid_p = TRUE; + return TRUE; + case PAY_SKIP: + continue; + /* case PAY_SOME: //no longer used */ + case PAY_BUY: + *paid_p = TRUE; + if (itemize || queuedpay) { + update_inventory(); + bot(); + } + break; + } + } + return TRUE; +} + +/* update shk's bill and augmented bill after an item has been purchased */ +staticfn void +update_bill( + int indx, /* index into ibill[]; -1 for unpaid contained item */ + int ibillct, + Bill *ibill, + struct eshk *eshkp, + struct bill_x *bp, + struct obj *paiditem) +{ + int j, newebillct; + + /* 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 (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; + for (j = 0; j < ibillct; ++j) + if (ibill[j].obj == paiditem && ibill[j].usedup == PartlyIntact) { + ibill[j].usedup = FullyIntact; + break; + } + } else { + /* if we get here, something was bought and needs to be removed + 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; /* clear before maybe deallocating */ + if (paiditem->where == OBJ_ONBILL) { + obj_extract_self(paiditem); + dealloc_obj(paiditem); + } + newebillct = eshkp->billct - 1; + *bp = eshkp->bill_p[newebillct]; + for (j = 0; j < ibillct; ++j) + if (ibill[j].bidx == newebillct) + ibill[j].bidx = (int) (bp - eshkp->bill_p); + eshkp->billct = newebillct; /* eshkp->billct - 1 */ + } + return; } /* return 2 if used-up portion paid @@ -1734,33 +2147,30 @@ dopay(void) * -1 if skip this object * -2 if no money/credit left */ -static int +staticfn int dopayobj( - register struct monst* shkp, - register struct bill_x* bp, - struct obj** obj_p, - int which /* 0 => used-up item, 1 => other (unpaid or lost) */, - boolean itemize) - + struct monst *shkp, + struct bill_x *bp, + struct obj *obj, + int which, /* 0 => used-up item, 1 => other (unpaid or lost) */ + boolean itemize, + boolean unseen) { - register struct obj *obj = *obj_p; long ltmp, quan, save_quan; - long umoney = money_cnt(gi.invent); int buy; - boolean stashed_gold = (hidden_gold(TRUE) > 0L), - consumed = (which == 0); + boolean consumed = (which == 0); - if (!obj->unpaid && !bp->useup) { + if (!obj->unpaid && !bp->useup + && !(Has_contents(obj) && unpaid_cost(obj, COST_CONTENTS))) { impossible("Paid object on bill??"); return PAY_BUY; } - if (itemize && umoney + ESHK(shkp)->credit == 0L) { - You("%shave no gold or credit left.", - stashed_gold ? "seem to " : ""); + if (itemize && insufficient_funds(shkp, obj, 0L)) { return PAY_BROKE; } /* we may need to temporarily adjust the object, if part of the - original quantity has been used up but part remains unpaid */ + original quantity has been used up but part remains unpaid; [note: + this predates 'ibill[]' and feels redundant but still works] */ save_quan = obj->quan; if (consumed) { /* either completely used up (simple), or split needed */ @@ -1771,81 +2181,234 @@ dopayobj( /* dealing with ordinary unpaid item */ quan = obj->quan; } + ltmp = bp->price * quan; + obj->quan = quan; /* to be used by doname() */ - obj->unpaid = 0; /* ditto */ iflags.suppress_price++; /* affects containers */ - ltmp = bp->price * quan; buy = PAY_BUY; /* flag; if changed then return early */ if (itemize) { char qbuf[BUFSZ], qsfx[BUFSZ]; + /* + * TODO: + * This should also accept 'a' and 'q' to end itemized paying: + * 'a' to buy the rest without asking, 'q' to just stop. + */ + Sprintf(qsfx, " for %ld %s. Pay?", ltmp, currency(ltmp)); (void) safe_qbuf(qbuf, (char *) 0, qsfx, obj, (quan == 1L) ? Doname2 : doname, ansimpleoname, (quan == 1L) ? "that" : "those"); if (y_n(qbuf) == 'n') { buy = PAY_SKIP; /* don't want to buy */ - } else if (quan < bp->bquan && !consumed) { /* partly used goods */ - obj->quan = bp->bquan - save_quan; /* used up amount */ - if (!Deaf && !muteshk(shkp)) { - SetVoice(shkp, 0, 80, 0); - verbalize("%s for the other %s before buying %s.", - ANGRY(shkp) ? "Pay" : "Please pay", - simpleonames(obj), /* short name suffices */ - save_quan > 1L ? "these" : "this one"); - } else { - pline("%s %s%s your bill for the other %s first.", - Shknam(shkp), - ANGRY(shkp) ? "angrily " : "", - nolimbs(shkp->data) ? "motions to" : "points out", - simpleonames(obj)); - } - buy = PAY_SKIP; /* shk won't sell */ } + } /* itemize */ + + if (quan < bp->bquan && !consumed) { /* partly used goods */ + /* shk won't sell the intact portion until the used up portion has + been paid for (once it has been, bp->bquan will match quan) */ + reject_purchase(shkp, obj, bp->bquan); + buy = PAY_SKIP; } - if (buy == PAY_BUY && umoney + ESHK(shkp)->credit < ltmp) { - You("don't%s have gold%s enough to pay for %s.", - stashed_gold ? " seem to" : "", - (ESHK(shkp)->credit > 0L) ? " or credit" : "", - thesimpleoname(obj)); + if (buy == PAY_BUY && insufficient_funds(shkp, obj, ltmp)) { buy = itemize ? PAY_SKIP : PAY_CANT; } - if (buy != PAY_BUY) { - /* restore unpaid object to original state */ - obj->quan = save_quan; - obj->unpaid = 1; - iflags.suppress_price--; - return buy; + if (buy == PAY_BUY) { + pay(ltmp, shkp); + if (!unseen) + shk_names_obj(shkp, obj, + consumed + ? "paid for %s at a cost of %ld gold piece%s.%s" + : "bought %s for %ld gold piece%s.%s", + ltmp, ""); } - pay(ltmp, shkp); - shk_names_obj(shkp, obj, - consumed ? "paid for %s at a cost of %ld gold piece%s.%s" - : "bought %s for %ld gold piece%s.%s", - ltmp, ""); + /* restore obj to original state */ obj->quan = save_quan; /* restore original count */ - /* quan => amount just bought, save_quan => remaining unpaid count */ + iflags.suppress_price--; - iflags.suppress_price--; /* before update_inventory() below */ - if (consumed) { - if (quan != bp->bquan) { - /* eliminate used-up portion; remainder is still unpaid */ - bp->bquan = obj->quan; - obj->unpaid = 1; - bp->useup = 0; - buy = PAY_SOME; - } else { /* completely used-up, so get rid of it */ - obj_extract_self(obj); - /* assert( obj == *obj_p ); */ - dealloc_obj(obj); - *obj_p = 0; /* destroy pointer to freed object */ + return buy; +} + +/* pay for the unpaid contents of a container without itemizing, + and for the container itself if it is unpaid too; + returns 0==successfully bought; 1==rejected, message given here; + 2=rejected, caller should issue message */ +staticfn int +buy_container( + struct monst *shkp, + int indx, + int ibillct, + Bill *ibill) +{ + unsigned boid, boids[BILLSZ]; + int i, j, buy, buycount = 0, boidsct = 0; + struct eshk *eshkp = ESHK(shkp); + int ebillct = eshkp->billct; + struct bill_x *bp; + struct obj *otmp, *otop, + *container = ibill[indx].obj; + unsigned unpaidcontainer = container->unpaid; + long totalcost = ibill[indx].cost; + boolean sightunseen = ibill[indx].usedup == UndisclosedContainer + /* give feedback just for container+contents rather + than for individiual contents even when those + contents are known */ + || ibill[indx].usedup == KnownContainer; + + /* check for no-gold first, then for not-enough-gold; feedback is + different for the two cases */ + if (insufficient_funds(shkp, container, 0L) + || insufficient_funds(shkp, container, totalcost)) + return 1; /* message given by insufficent_funds() */ + + /* check for partly intact portion of a not-yet-paid partly used item */ + for (i = 0; i < ebillct; ++i) { + bp = &eshkp->bill_p[i]; + otmp = bp_to_obj(bp); /* ibill[bidx].obj is the container */ + if (!otmp) { + impossible("Can't find contained item on shop bill (#%d).", + bp->bo_id); + return 2; /* failure; have caller give a generic message */ + } + if (otmp->where != OBJ_CONTAINED && !Has_contents(otmp)) + continue; + /* otmp is contained, but possibly inside a different container */ + for (otop = otmp; otop->where == OBJ_CONTAINED; + otop = otop->ocontainer) + continue; /* where==OBJ_CONTAINED loop */ + if (otop != container) + continue; /* 'i' loop */ + /* now check for partly intact portion of partly used item */ + if (otmp->quan < bp->bquan) { + reject_purchase(shkp, otmp, bp->bquan); + return 1; /* message given by reject_purchase() */ + } + /* record this for the second pass; unless it's the container--that + will be deferred until after the loop so that it will be last */ + if (bp->bo_id != container->o_id) + boids[boidsct++] = bp->bo_id; + } + if (unpaidcontainer) + boids[boidsct++] = container->o_id; + + /* now make the actual purchasing pass; we've collected a set of + o_id values in order to avoid traversing the shk's bill while it + undergoes updates */ + for (j = 0; j < boidsct; ++j) { + boid = boids[j]; + for (i = 0, bp = eshkp->bill_p; i < ebillct; ++i, ++bp) + if (bp->bo_id == boid) + break; + if (i == ebillct) { + impossible("Buying %s contents: item #%u disappeared from bill.", + simpleonames(container), boid); + return 2; } - } else if (itemize) { - update_inventory(); /* Done just once in dopay() if !itemize. */ + otmp = bp_to_obj(bp); + + buy = dopayobj(shkp, bp, otmp, 1, FALSE, sightunseen); + if (buy != PAY_BUY) { + impossible("Buying %s contents failed unexpectedly (#%u %d).", + simpleonames(container), otmp->o_id, buy); + 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) { + /* if the container was unpaid, the hero has just purchased it; + normally paydoname()--called by shk_names_obj()--would give + "contents of your " when it's hero-owned but we + want it to reflect container's state before purchase; + since paydoname() isn't called for no_charge items, we use + obj->no_charge as a hack to avoid that phrasing in favor of + "a/an and its contents"; temporarily set + obj->unpaid to reflect the before-purchase state too */ + if (unpaidcontainer) + container->unpaid = container->no_charge = 1; + shk_names_obj(shkp, container, + "bought %s for %ld gold piece%s.%s", + totalcost, ""); + container->unpaid = container->no_charge = 0; + } + + return buycount ? 0 : 2; /* we don't expect buycount to be 0 */ +} + +/* called if an item on shop bill is partly used up and partly intact and + player tries to buy the intact portion before paying for used up portion + (not actually very effective since player can just drop the unpaid + portion then pick it back up to have it get its own distinct bill entry; + the former partly used up portion becomes a fully used up separate item) */ +staticfn void +reject_purchase( + struct monst *shkp, + struct obj *obj, + long billed_quan) +{ + long intact_quan = obj->quan; + + assert(intact_quan < billed_quan); + /* temporarily change obj to refer to the used up portion */ + obj->quan = billed_quan - intact_quan; + if (!Deaf && !muteshk(shkp)) { + char which[BUFSZ]; + + if (obj->where == OBJ_CONTAINED) + Snprintf(which, sizeof which, "the one%s in %s", + plur(intact_quan), thesimpleoname(obj->ocontainer)); + else + Sprintf(which, "%s", (intact_quan > 1L) ? "these" : "this one"); + + SetVoice(shkp, 0, 80, 0); + verbalize("%s for the other %s before buying %s.", + ANGRY(shkp) ? "Pay" : "Please pay", + simpleonames(obj), /* short name suffices */ + which); + } else { + pline("%s %s%s your bill for the other %s first.", + Shknam(shkp), + ANGRY(shkp) ? "angrily " : "", + nolimbs(shkp->data) ? "motions to" : "points out", + simpleonames(obj)); } - return buy; + obj->quan = intact_quan; +} + +/* gold+credit checking+feedback common to dopayobj() and buy_container() */ +staticfn boolean +insufficient_funds( + struct monst *shkp, + struct obj *item, + long cost) /* 0: check for no-gold; >0: check for specified amount */ +{ + long stashed_gold; + long umoney = money_cnt(gi.invent), + ecredit = ESHK(shkp)->credit; + + /* dopayobj() checks for no-gold early and not-enough-gold later; + buy_container() checks for both early but uses separate calls to us */ + if (!cost && umoney + ecredit == 0L) { + stashed_gold = hidden_gold(TRUE); + You("%shave no gold or credit left.", + (stashed_gold > 0) ? "seem to " : ""); + return TRUE; + } + if (cost && umoney + ecredit < cost) { + stashed_gold = hidden_gold(TRUE); + You("don't%s have gold%s enough to pay for %s.", + (stashed_gold > 0L) ? " seem to" : "", + (ecredit > 0L) ? " or credit" : "", + paydoname(item)); + return TRUE; + } + return FALSE; } /* routine called after dying (or quitting) */ @@ -1941,7 +2504,7 @@ paybill( when this returns True, it should call set_repo_loc() before returning; when it returns False, it should not do such because that might have already been called for some shopkeeper */ -static boolean +staticfn boolean inherits( struct monst *shkp, int numsk, @@ -2003,7 +2566,7 @@ inherits( takes[0] = '\0'; if (helpless(shkp)) Strcat(takes, "wakes up and "); - if (!next2u(shkp->mx, shkp->my)) + if (!m_next2u(shkp)) Strcat(takes, "comes and "); Strcat(takes, "takes"); @@ -2013,18 +2576,18 @@ inherits( eshkp->robbed = 0L; if (umoney > 0L) { money2mon(shkp, umoney); - gc.context.botl = 1; + disp.botl = TRUE; } if (!silently) pline("%s %s all your possessions.", Shknam(shkp), takes); taken = TRUE; } else { money2mon(shkp, loss); - gc.context.botl = 1; + disp.botl = TRUE; if (!silently) pline("%s %s the %ld %s %sowed %s.", Shknam(shkp), takes, loss, currency(loss), - strncmp(eshkp->customer, gp.plname, PL_NSIZ) ? "" + strncmp(eshkp->customer, svp.plname, PL_NSIZ) ? "" : "you ", noit_mhim(shkp)); /* shopkeeper has now been paid in full */ @@ -2046,7 +2609,7 @@ inherits( return taken; } -static void +staticfn void set_repo_loc(struct monst *shkp) { coordxy ox, oy; @@ -2123,11 +2686,11 @@ finish_paybill(void) } /* find obj on one of the lists */ -static struct obj * -bp_to_obj(register struct bill_x *bp) +staticfn struct obj * +bp_to_obj(struct bill_x *bp) { - register struct obj *obj; - register unsigned int id = bp->bo_id; + struct obj *obj; + unsigned int id = bp->bo_id; if (bp->useup) obj = o_on(id, gb.billobjs); @@ -2153,7 +2716,7 @@ find_oid(unsigned int id) return obj; if ((obj = o_on(id, fobj)) != 0) return obj; - if ((obj = o_on(id, gl.level.buriedobjlist)) != 0) + if ((obj = o_on(id, svl.level.buriedobjlist)) != 0) return obj; if ((obj = o_on(id, gm.migrating_objs)) != 0) return obj; @@ -2241,8 +2804,8 @@ const int matprices[] = { 10 /* MINERAL */ }; -static long -get_pricing_units(struct obj* obj) +staticfn long +get_pricing_units(struct obj *obj) { long units = obj->quan; @@ -2260,7 +2823,7 @@ get_pricing_units(struct obj* obj) /* decide whether to apply a surcharge (or hypothetically, a discount) to obj if it had ID number 'oid'; returns 1: increase, 0: normal, -1: decrease */ int -oid_price_adjustment(struct obj* obj, unsigned int oid) +oid_price_adjustment(struct obj *obj, unsigned int oid) { int res = 0, otyp = obj->otyp; @@ -2272,11 +2835,18 @@ oid_price_adjustment(struct obj* obj, unsigned int oid) } /* calculate the value that the shk will charge for [one of] an object */ -static long +staticfn long get_cost( - register struct obj* obj, - register struct monst* shkp) /* if angry, impose a surcharge */ + struct obj *obj, + struct monst *shkp) /* if angry, impose a surcharge */ { + /* + * FIXME: + * If this obj is already on the shop's bill, use the price which + * has been set there. Otherwise, the amount could be different + * (if billed while undiscovered and now become discovered or + * hero's charisma and/or visible worn gear have changed). + */ long tmp = getprice(obj, FALSE), /* used to perform a single calculation even when multiple adjustments (unID'd, dunce/tourist, charisma) are made */ @@ -2392,7 +2962,7 @@ contained_cost( boolean usell, boolean unpaid_only) { - register struct obj *otmp, *top; + struct obj *otmp, *top; coordxy x, y; boolean on_floor, freespot; @@ -2440,8 +3010,8 @@ contained_gold( struct obj *obj, boolean even_if_unknown) /* T: all gold; F: limit to known contents */ { - register struct obj *otmp; - register long value = 0L; + struct obj *otmp; + long value = 0L; /* accumulate contained gold */ for (otmp = obj->cobj; otmp; otmp = otmp->nobj) @@ -2453,13 +3023,13 @@ contained_gold( return value; } -static void +staticfn void dropped_container( - register struct obj *obj, - register struct monst *shkp, - register boolean sale) + struct obj *obj, + struct monst *shkp, + boolean sale) { - register struct obj *otmp; + struct obj *otmp; /* the "top" container is treated in the calling fn */ for (otmp = obj->cobj; otmp; otmp = otmp->nobj) { @@ -2475,9 +3045,9 @@ dropped_container( } void -picked_container(register struct obj* obj) +picked_container(struct obj *obj) { - register struct obj *otmp; + struct obj *otmp; /* the "top" container is treated in the calling fn */ for (otmp = obj->cobj; otmp; otmp = otmp->nobj) { @@ -2492,7 +3062,7 @@ picked_container(register struct obj* obj) } } -static boolean +staticfn boolean special_stock( struct obj *obj, struct monst *shkp, @@ -2537,8 +3107,8 @@ special_stock( } /* calculate how much the shk will pay when buying [all of] an object */ -static long -set_cost(register struct obj* obj, register struct monst* shkp) +staticfn long +set_cost(struct obj *obj, struct monst *shkp) { long tmp, unit_price = getprice(obj, TRUE), multiplier = 1L, divisor = 1L; @@ -2614,7 +3184,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 */ @@ -2632,7 +3202,7 @@ gem_learned(int oindx) [if the new price drops for some reason, keep the old one in place] */ void alter_cost( - struct obj* obj, + struct obj *obj, long amt) /* if 0, use regular shop pricing, otherwise force amount; if negative, use abs(amt) even if it's less than old cost */ { @@ -2656,7 +3226,7 @@ alter_cost( long unpaid_cost( struct obj *unp_obj, /* known to be unpaid or contain unpaid */ - boolean include_contents) + uchar cost_type) /* COST_NOCONTENTS, COST_CONTENTS, or COST_SINGLEOBJ */ { struct bill_x *bp = (struct bill_x *) 0; struct monst *shkp = 0; @@ -2680,9 +3250,15 @@ unpaid_cost( #endif for (shop = u.ushops; *shop; shop++) { if ((shkp = shop_keeper(*shop)) != 0) { - if ((bp = onbill(unp_obj, shkp, TRUE))) - amt = unp_obj->quan * bp->price; - if (include_contents && Has_contents(unp_obj)) + if ((bp = onbill(unp_obj, shkp, TRUE))) { + amt = bp->price; + if (cost_type != COST_SINGLEOBJ) { + /* use quan rather than get_pricing_units -- glob weight + should already be factored into bp->price */ + amt *= unp_obj->quan; + } + } + if (cost_type == COST_CONTENTS && Has_contents(unp_obj)) amt = contained_cost(unp_obj, shkp, amt, FALSE, TRUE); if (bp || (!unp_obj->unpaid && amt)) break; @@ -2695,42 +3271,50 @@ unpaid_cost( return amt; } -static void -add_one_tobill(struct obj *obj, boolean dummy, struct monst *shkp) +/* add 'obj' to 'shkp's bill */ +staticfn void +add_one_tobill( + struct obj *obj, + boolean dummy, /* True: obj is used up so goes on bill differently */ + struct monst *shkp) { struct eshk *eshkp; struct bill_x *bp; int bct; + boolean unbilled = FALSE; - if (!billable(&shkp, obj, *u.ushops, TRUE)) - return; eshkp = ESHK(shkp); - - if (eshkp->billct == BILLSZ) { - You("got that for free!"); - /* - * FIXME: - * What happens when this is a dummy object? It won't be on any - * object list. - */ - return; - } - /* normally bill_p gets set up whenever you enter the shop, but obj might be going onto the bill because hero just snagged it with a grappling hook from outside without ever having been inside */ if (!eshkp->bill_p) - eshkp->bill_p = &(eshkp->bill[0]); + eshkp->bill_p = &eshkp->bill[0]; + + if (!billable(&shkp, obj, *u.ushops, TRUE)) { + /* shk doesn't want it */ + unbilled = TRUE; + } else if (eshkp->billct == BILLSZ) { + /* shk's bill is completely full */ + You("got that for free!"); + unbilled = TRUE; + } + /* if not on any list (probably from bill_dummy_object() which creates + a new OBJ_FREE object), don't leave unmanaged object hanging around */ + if (unbilled) { + if (obj->where == OBJ_FREE) + dealloc_obj(obj); /* change to obj->where==OBJ_DELETED */ + return; + } bct = eshkp->billct; - bp = &(eshkp->bill_p[bct]); + bp = &eshkp->bill_p[bct]; bp->bo_id = obj->o_id; bp->bquan = obj->quan; if (dummy) { /* a dummy object must be inserted into */ - bp->useup = 1; /* the gb.billobjs chain here. crucial for */ + bp->useup = TRUE; /* the gb.billobjs chain here. crucial for */ add_to_billobjs(obj); /* eating floorfood in shop. see eat.c */ } else - bp->useup = 0; + bp->useup = FALSE; bp->price = get_cost(obj, shkp); if (obj->globby) { /* for globs, the amt charged for quan 1 depends on owt */ @@ -2744,8 +3328,8 @@ add_one_tobill(struct obj *obj, boolean dummy, struct monst *shkp) obj->unpaid = 1; } -static void -add_to_billobjs(struct obj* obj) +staticfn void +add_to_billobjs(struct obj *obj) { if (obj->where != OBJ_FREE) panic("add_to_billobjs: obj not free"); @@ -2760,19 +3344,19 @@ add_to_billobjs(struct obj* obj) in_use by dodrink/dopotion but isn't being used up yet because it stays on the bill; only object sanity checking actually cares */ obj->in_use = 0; - /* ... same for bypass by destroy_item */ + /* ... same for bypass by destroy_items */ obj->bypass = 0; } /* recursive billing of objects within containers. */ -static void +staticfn void bill_box_content( - register struct obj *obj, - register boolean ininv, - register boolean dummy, - register struct monst *shkp) + struct obj *obj, + boolean ininv, + boolean dummy, + struct monst *shkp) { - register struct obj *otmp; + struct obj *otmp; if (SchroedingersBox(obj)) return; @@ -2791,7 +3375,7 @@ bill_box_content( DISABLE_WARNING_FORMAT_NONLITERAL /* shopkeeper tells you what you bought or sold, sometimes partly IDing it */ -static void +staticfn void shk_names_obj( struct monst *shkp, struct obj *obj, @@ -2814,7 +3398,7 @@ shk_names_obj( was_unknown |= !objects[obj->otyp].oc_name_known; makeknown(obj->otyp); } - obj_name = doname(obj); + obj_name = paydoname(obj); /* Use an alternate message when extra information is being provided */ if (was_unknown) { Sprintf(fmtbuf, "%%s; you %s", fmt); @@ -2977,7 +3561,7 @@ addtobill( } } -static void +staticfn void append_honorific(char *buf) { /* (chooses among [0]..[3] normally; [1]..[4] after the @@ -3023,51 +3607,44 @@ splitbill(struct obj *obj, struct obj *otmp) } bp->bquan -= otmp->quan; - if (ESHK(shkp)->billct == BILLSZ) + if (ESHK(shkp)->billct == BILLSZ) { otmp->unpaid = 0; - else { + } else { tmp = bp->price; bp = &(ESHK(shkp)->bill_p[ESHK(shkp)->billct]); bp->bo_id = otmp->o_id; bp->bquan = otmp->quan; - bp->useup = 0; + bp->useup = FALSE; bp->price = tmp; ESHK(shkp)->billct++; } } -static void -sub_one_frombill(register struct obj* obj, register struct monst* shkp) +staticfn void +sub_one_frombill(struct obj *obj, struct monst *shkp) { - register struct bill_x *bp; + struct bill_x *bp; + struct eshk *eshkp; if ((bp = onbill(obj, shkp, FALSE)) != 0) { - register struct obj *otmp; + struct obj *otmp; obj->unpaid = 0; if (bp->bquan > obj->quan) { otmp = newobj(); *otmp = *obj; otmp->oextra = (struct oextra *) 0; - bp->bo_id = otmp->o_id = next_ident(); /* gc.context.ident++ */ + bp->bo_id = otmp->o_id = next_ident(); /* svc.context.ident++ */ otmp->where = OBJ_FREE; otmp->quan = (bp->bquan -= obj->quan); otmp->owt = 0; /* superfluous */ - bp->useup = 1; + bp->useup = TRUE; 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"); @@ -3077,9 +3654,9 @@ sub_one_frombill(register struct obj* obj, register struct monst* shkp) /* recursive check of unpaid objects within nested containers. */ void -subfrombill(register struct obj* obj, register struct monst* shkp) +subfrombill(struct obj *obj, struct monst *shkp) { - register struct obj *otmp; + struct obj *otmp; sub_one_frombill(obj, shkp); @@ -3095,7 +3672,7 @@ subfrombill(register struct obj* obj, register struct monst* shkp) } } -static long +staticfn long stolen_container( struct obj *obj, struct monst *shkp, @@ -3115,6 +3692,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 @@ -3168,6 +3746,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); @@ -3244,7 +3823,7 @@ stolen_value( if (!silent) { if (canseemon(shkp)) { Norep("%s booms: \"%s, you are a thief!\"", - Shknam(shkp), gp.plname); + Shknam(shkp), svp.plname); } else if (!Deaf) { Norep("You hear a scream, \"Thief!\""); /* Deaf-aware */ } @@ -3312,8 +3891,8 @@ sellobj( struct obj *obj, coordxy x, coordxy y) { - register struct monst *shkp; - register struct eshk *eshkp; + struct monst *shkp; + struct eshk *eshkp; long ltmp = 0L, cltmp = 0L, gltmp = 0L, offer, shkmoney; boolean saleitem, cgold = FALSE, container = Has_contents(obj); boolean isgold = (obj->oclass == COIN_CLASS); @@ -3345,6 +3924,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)) { @@ -3365,21 +3959,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; @@ -3541,6 +4120,7 @@ sellobj( switch (gs.sell_response ? gs.sell_response : nyaq(qbuf)) { case 'q': gs.sell_response = 'n'; + FALLTHROUGH; /*FALLTHRU*/ case 'n': if (container) @@ -3551,6 +4131,7 @@ sellobj( break; case 'a': gs.sell_response = 'y'; + FALLTHROUGH; /*FALLTHRU*/ case 'y': if (container) @@ -3650,10 +4231,55 @@ doinvbill( return 0; } -static long -getprice(register struct obj* obj, boolean shk_buying) +/* adjust tin, egg, or corpse price based on monster data */ +staticfn long +corpsenm_price_adj(struct obj *obj) { - register long tmp = (long) objects[obj->otyp].oc_cost; + long val = 0L; + + if ((obj->otyp == TIN || obj->otyp == EGG || obj->otyp == CORPSE) + && ismnum(obj->corpsenm)) { + int i; + long tmp = 1L; + struct permonst *ptr = &mons[obj->corpsenm]; + struct { + int trinsic; + int cost; + } const icost[] = { + { FIRE_RES, 2 }, + { SLEEP_RES, 3 }, + { COLD_RES, 2 }, + { DISINT_RES, 5 }, + { SHOCK_RES, 4 }, + { POISON_RES, 2 }, + { ACID_RES, 1 }, + { STONE_RES, 3 }, + { TELEPORT, 2 }, + { TELEPORT_CONTROL, 3 }, + { TELEPAT, 5 } + }; + + for (i = 0; i < SIZE(icost); i++) + if (intrinsic_possible(icost[i].trinsic, ptr)) + tmp += icost[i].cost; + if (unique_corpstat(ptr)) + tmp += 50; + + + val = max(1, ((ptr->mlevel - 1) * 2)); + if (obj->otyp == CORPSE) + val += max(1, (ptr->cnutrit / 30)); + + val = val * tmp; + } + + return val; +} + +staticfn long +getprice(struct obj *obj, boolean shk_buying) +{ + long tmp = (long) objects[obj->otyp].oc_cost; if (obj->oartifact) { tmp = arti_cost(obj); @@ -3662,6 +4288,8 @@ getprice(register struct obj* obj, boolean shk_buying) } switch (obj->oclass) { case FOOD_CLASS: + tmp += corpsenm_price_adj(obj); + /* simpler hunger check, (2-4)*cost */ if (u.uhs >= HUNGRY && !shk_buying) tmp *= (long) u.uhs; @@ -3733,8 +4361,8 @@ shkcatch( void add_damage( - register coordxy x, - register coordxy y, + coordxy x, + coordxy y, long cost) { struct damage *tmp_dam; @@ -3751,29 +4379,29 @@ add_damage( if (!*shops) return; } - for (tmp_dam = gl.level.damagelist; tmp_dam; tmp_dam = tmp_dam->next) + for (tmp_dam = svl.level.damagelist; tmp_dam; tmp_dam = tmp_dam->next) if (tmp_dam->place.x == x && tmp_dam->place.y == y) { tmp_dam->cost += cost; - tmp_dam->when = gm.moves; /* needed by pay_for_damage() */ + tmp_dam->when = svm.moves; /* needed by pay_for_damage() */ return; } tmp_dam = (struct damage *) alloc((unsigned) sizeof *tmp_dam); (void) memset((genericptr_t) tmp_dam, 0, sizeof *tmp_dam); - tmp_dam->when = gm.moves; + tmp_dam->when = svm.moves; tmp_dam->place.x = x; tmp_dam->place.y = y; tmp_dam->cost = cost; tmp_dam->typ = levl[x][y].typ; tmp_dam->flags = levl[x][y].flags; - tmp_dam->next = gl.level.damagelist; - gl.level.damagelist = tmp_dam; - /* If player saw damage, display as a wall forever */ + tmp_dam->next = svl.level.damagelist; + svl.level.damagelist = tmp_dam; + /* If player saw damage, display walls post-repair as walls, not stone */ if (cansee(x, y)) levl[x][y].seenv = SVALL; } /* is shopkeeper impaired, so they cannot act? */ -static boolean +staticfn boolean shk_impaired(struct monst *shkp) { if (!shkp || !shkp->isshk || !inhishop(shkp)) @@ -3784,11 +4412,11 @@ shk_impaired(struct monst *shkp) } /* is damage dam repairable by shopkeeper shkp? */ -static boolean +staticfn boolean repairable_damage(struct damage *dam, struct monst *shkp) { coordxy x, y; - struct trap* ttmp; + struct trap *ttmp; struct monst *mtmp; if (!dam || shk_impaired(shkp)) @@ -3798,7 +4426,7 @@ repairable_damage(struct damage *dam, struct monst *shkp) y = dam->place.y; /* too soon to fix it? */ - if ((gm.moves - dam->when) < REPAIR_DELAY) + if ((svm.moves - dam->when) < REPAIR_DELAY) return FALSE; /* is it a wall? don't fix if anyone is in the way */ if (!IS_ROOM(dam->typ)) { @@ -3823,10 +4451,10 @@ repairable_damage(struct damage *dam, struct monst *shkp) } /* find any damage shopkeeper shkp could repair. returns NULL is none found */ -static struct damage * +staticfn struct damage * find_damage(struct monst *shkp) { - struct damage *dam = gl.level.damagelist; + struct damage *dam = svl.level.damagelist; if (shk_impaired(shkp)) return NULL; @@ -3841,16 +4469,16 @@ find_damage(struct monst *shkp) return NULL; } -static void +staticfn void discard_damage_struct(struct damage *dam) { if (!dam) return; - if (dam == gl.level.damagelist) { - gl.level.damagelist = dam->next; + if (dam == svl.level.damagelist) { + svl.level.damagelist = dam->next; } else { - struct damage *prev = gl.level.damagelist; + struct damage *prev = svl.level.damagelist; while (prev && prev->next != dam) prev = prev->next; @@ -3862,10 +4490,10 @@ discard_damage_struct(struct damage *dam) } /* discard all damage structs owned by shopkeeper */ -static void +staticfn void discard_damage_owned_by(struct monst *shkp) { - struct damage *dam = gl.level.damagelist, *dam2, *prevdam = NULL; + struct damage *dam = svl.level.damagelist, *dam2, *prevdam = NULL; while (dam) { coordxy x = dam->place.x, y = dam->place.y; @@ -3874,11 +4502,10 @@ discard_damage_owned_by(struct monst *shkp) dam2 = dam->next; if (prevdam) prevdam->next = dam2; - if (dam == gl.level.damagelist) - gl.level.damagelist = dam2; - (void) memset(dam, 0, sizeof(struct damage)); - free((genericptr_t) dam); - dam = dam2; + if (dam == svl.level.damagelist) + svl.level.damagelist = dam2; + (void) memset(dam, 0, sizeof *dam); + free((genericptr_t) dam), dam = (struct damage *) NULL; } else { prevdam = dam; dam2 = dam->next; @@ -3889,7 +4516,7 @@ discard_damage_owned_by(struct monst *shkp) } /* Shopkeeper tries to repair damage belonging to them */ -static void +staticfn void shk_fixes_damage(struct monst *shkp) { struct damage *dam = find_damage(shkp); @@ -3924,7 +4551,7 @@ shk_fixes_damage(struct monst *shkp) shop (possibly in the "free spot" or even in doorway or an adjacent wall gap), but if they are in a gap in a wall shared by two shops they might have started in the other shop */ -static uint8 +staticfn uint8 litter_getpos( uint8 *litter, /* array of 9 uint8's */ coordxy x, coordxy y, @@ -3935,7 +4562,7 @@ litter_getpos( (void) memset((genericptr_t) litter, 0, 9 * sizeof *litter); - if (gl.level.objects[x][y] && !IS_ROOM(levl[x][y].typ)) { + if (svl.level.objects[x][y] && !IS_ROOM(levl[x][y].typ)) { for (i = 0; i < 9; i++) { ix = x + horiz(i); iy = y + vert(i); @@ -3955,7 +4582,7 @@ litter_getpos( litter[] guarantees that items will end up inside shkp's shop, but if the wall being repaired is shared by two shops the items might have started in the other shop */ -static void +staticfn void litter_scatter( uint8 *litter, coordxy x, coordxy y, @@ -3988,7 +4615,7 @@ litter_scatter( unplacebc(); /* pick 'em up */ placebc(); /* put 'em down */ } - while ((otmp = gl.level.objects[x][y]) != 0) { + while ((otmp = svl.level.objects[x][y]) != 0) { /* Don't mess w/ boulders -- just merge into wall */ if (otmp->otyp == BOULDER || otmp->otyp == ROCK) { obj_extract_self(otmp); @@ -3997,7 +4624,7 @@ litter_scatter( int trylimit = 10; int i = rn2(9), ix, iy; - /* otmp must be moved otherwise gl.level.objects[x][y] will + /* otmp must be moved otherwise svl.level.objects[x][y] will never become Null and while-loop won't terminate */ do { i = (i + 1) % 9; @@ -4045,7 +4672,7 @@ litter_scatter( } } -static void +staticfn void litter_newsyms(uint8 *litter, coordxy x, coordxy y) { int i; @@ -4065,7 +4692,7 @@ litter_newsyms(uint8 *litter, coordxy x, coordxy y) * 0: repair postponed, 1: silent repair (no messages), 2: normal repair * 3: untrap */ -static int +staticfn int repair_damage( struct monst *shkp, struct damage *tmp_dam, @@ -4192,7 +4819,7 @@ fix_shop_damage(void) struct damage *damg, *nextdamg; /* if this level has no shop damage, there's nothing to do */ - if (!gl.level.damagelist) + if (!svl.level.damagelist) return; /* go through all shopkeepers on the level */ @@ -4204,7 +4831,7 @@ fix_shop_damage(void) /* go through all damage data trying to have this shopkeeper fix it; repair_damage() will only make repairs for damage matching shop controlled by specified shopkeeper */ - for (damg = gl.level.damagelist; damg; damg = nextdamg) { + for (damg = svl.level.damagelist; damg; damg = nextdamg) { nextdamg = damg->next; if (repair_damage(shkp, damg, TRUE)) discard_damage_struct(damg); @@ -4240,7 +4867,7 @@ shk_move(struct monst *shkp) return 0; } if (eshkp->following) { - if (strncmp(eshkp->customer, gp.plname, PL_NSIZ)) { + if (strncmp(eshkp->customer, svp.plname, PL_NSIZ)) { if (!Deaf && !muteshk(shkp)) { SetVoice(shkp, 0, 80, 0); verbalize("%s, %s! I was looking for %s.", Hello(shkp), @@ -4249,17 +4876,17 @@ shk_move(struct monst *shkp) eshkp->following = 0; return 0; } - if (gm.moves > gf.followmsg + 4) { + if (svm.moves > gf.followmsg + 4) { if (!Deaf && !muteshk(shkp)) { SetVoice(shkp, 0, 80, 0); verbalize("%s, %s! Didn't you forget to pay?", - Hello(shkp), gp.plname); + Hello(shkp), svp.plname); } else { pline("%s holds out %s upturned %s.", Shknam(shkp), noit_mhis(shkp), mbodypart(shkp, HAND)); } - gf.followmsg = gm.moves; + gf.followmsg = svm.moves; if (!rn2(9)) { pline("%s doesn't like customers who don't pay.", Shknam(shkp)); @@ -4320,6 +4947,7 @@ shk_move(struct monst *shkp) appr = gtx = gty = 0; } } +#undef GDIST } z = move_special(shkp, inhishop(shkp), appr, uondoor, avoid, omx, omy, @@ -4340,7 +4968,7 @@ after_shk_move(struct monst *shkp) /* reset bill_p, need to re-calc player's occupancy too */ eshkp->bill_p = &eshkp->bill[0]; /* only re-check occupancy if game hasn't just ended */ - if (!gp.program_state.gameover) + if (!program_state.gameover) check_special_room(FALSE); } } @@ -4399,7 +5027,7 @@ shopdig(int fall) } else if (!um_dist(shkp->mx, shkp->my, 5) && !helpless(shkp) && (ESHK(shkp)->billct || ESHK(shkp)->debit)) { - register struct obj *obj, *obj2; + struct obj *obj, *obj2; if (nolimbs(shkp->data)) { grabs = "knocks off"; @@ -4414,10 +5042,10 @@ shopdig(int fall) return; #endif } - if (!next2u(shkp->mx, shkp->my)) { + if (!m_next2u(shkp)) { mnexto(shkp, RLOC_MSG); /* for some reason the shopkeeper can't come next to you */ - if (!next2u(shkp->mx, shkp->my)) { + if (!m_next2u(shkp)) { if (lang == 2) pline("%s curses you in anger and frustration!", Shknam(shkp)); @@ -4447,8 +5075,8 @@ shopdig(int fall) } } -static void -makekops(coord* mm) +staticfn void +makekops(coord *mm) { static const short k_mndx[4] = { PM_KEYSTONE_KOP, PM_KOP_SERGEANT, PM_KOP_LIEUTENANT, PM_KOP_KAPTAIN }; @@ -4463,7 +5091,7 @@ makekops(coord* mm) if ((cnt = k_cnt[k]) == 0) break; mndx = k_mndx[k]; - if (gm.mvitals[mndx].mvflags & G_GONE) + if (svm.mvitals[mndx].mvflags & G_GONE) continue; while (cnt--) @@ -4472,16 +5100,50 @@ makekops(coord* mm) } } +staticfn void +getcad( + struct monst *shkp, const char *dmgstr, coordxy x, coordxy y, + boolean uinshp, boolean animal, boolean pursue) +{ + boolean dugwall = (!strcmp(dmgstr, "dig into") /* wand */ + || !strcmp(dmgstr, "damage")); /* pick-axe */ + + if (muteshk(shkp)) { + if (animal && !helpless(shkp)) + yelp(shkp); + } else if (pursue || uinshp || !um_dist(x, y, 1)) { + if (!Deaf) { + SetVoice(shkp, 0, 80, 0); + verbalize("How dare you %s my %s?", dmgstr, + dugwall ? "shop" : "door"); + } else { + pline("%s is %s that you decided to %s %s %s!", + Shknam(shkp), ROLL_FROM(angrytexts), + dmgstr, noit_mhis(shkp), dugwall ? "shop" : "door"); + } + } else { + if (!Deaf) { + pline("%s shouts:", Shknam(shkp)); + SetVoice(shkp, 0, 80, 0); + verbalize("Who dared %s my %s?", dmgstr, + dugwall ? "shop" : "door"); + } else { + pline("%s is %s that someone decided to %s %s %s!", + Shknam(shkp), ROLL_FROM(angrytexts), + dmgstr, noit_mhis(shkp), dugwall ? "shop" : "door"); + } + } + hot_pursuit(shkp); +} + void -pay_for_damage(const char* dmgstr, boolean cant_mollify) +pay_for_damage(const char *dmgstr, boolean cant_mollify) { - register struct monst *shkp = (struct monst *) 0; + struct monst *shkp = (struct monst *) 0; char shops_affected[5]; boolean uinshp = (*u.ushops != '\0'); char qbuf[80]; coordxy x, y; - boolean dugwall = (!strcmp(dmgstr, "dig into") /* wand */ - || !strcmp(dmgstr, "damage")); /* pick-axe */ boolean animal, pursue; struct damage *tmp_dam, *appear_here = 0; long cost_of_damage = 0L; @@ -4489,10 +5151,10 @@ pay_for_damage(const char* dmgstr, boolean cant_mollify) nearest_damage = nearest_shk; int picks = 0; - for (tmp_dam = gl.level.damagelist; tmp_dam; tmp_dam = tmp_dam->next) { + for (tmp_dam = svl.level.damagelist; tmp_dam; tmp_dam = tmp_dam->next) { char *shp; - if (tmp_dam->when != gm.moves || !tmp_dam->cost) + if (tmp_dam->when != svm.moves || !tmp_dam->cost) continue; cost_of_damage += tmp_dam->cost; Strcpy(shops_affected, @@ -4539,7 +5201,7 @@ pay_for_damage(const char* dmgstr, boolean cant_mollify) y = appear_here->place.y; /* not the best introduction to the shk... */ - (void) strncpy(ESHK(shkp)->customer, gp.plname, PL_NSIZ); + (void) strncpy(ESHK(shkp)->customer, svp.plname, PL_NSIZ); /* if the shk is already on the war path, be sure it's all out */ if (ANGRY(shkp) || ESHK(shkp)->following) { @@ -4552,7 +5214,8 @@ pay_for_damage(const char* dmgstr, boolean cant_mollify) if (!cansee(shkp->mx, shkp->my)) return; pursue = TRUE; - goto getcad; + getcad(shkp, dmgstr, x, y, uinshp, animal, pursue); + return; } if (uinshp) { @@ -4562,8 +5225,10 @@ pay_for_damage(const char* dmgstr, boolean cant_mollify) mnexto(shkp, RLOC_NOMSG); } pursue = um_dist(shkp->mx, shkp->my, 1); - if (pursue) - goto getcad; + if (pursue) { + getcad(shkp, dmgstr, x, y, uinshp, animal, pursue); + return; + } } else { /* * Make shkp show up at the door. Effect: If there is a monster @@ -4596,33 +5261,7 @@ pay_for_damage(const char* dmgstr, boolean cant_mollify) if ((um_dist(x, y, 1) && !uinshp) || cant_mollify || (money_cnt(gi.invent) + ESHK(shkp)->credit) < cost_of_damage || !rn2(50)) { - getcad: - if (muteshk(shkp)) { - if (animal && !helpless(shkp)) - yelp(shkp); - } else if (pursue || uinshp || !um_dist(x, y, 1)) { - if (!Deaf) { - SetVoice(shkp, 0, 80, 0); - verbalize("How dare you %s my %s?", dmgstr, - dugwall ? "shop" : "door"); - } else { - pline("%s is %s that you decided to %s %s %s!", - Shknam(shkp), angrytexts[rn2(SIZE(angrytexts))], - dmgstr, noit_mhis(shkp), dugwall ? "shop" : "door"); - } - } else { - if (!Deaf) { - pline("%s shouts:", Shknam(shkp)); - SetVoice(shkp, 0, 80, 0); - verbalize("Who dared %s my %s?", dmgstr, - dugwall ? "shop" : "door"); - } else { - pline("%s is %s that someone decided to %s %s %s!", - Shknam(shkp), angrytexts[rn2(SIZE(angrytexts))], - dmgstr, noit_mhis(shkp), dugwall ? "shop" : "door"); - } - } - hot_pursuit(shkp); + getcad(shkp, dmgstr, x, y, uinshp, animal, pursue); return; } @@ -4639,7 +5278,7 @@ pay_for_damage(const char* dmgstr, boolean cant_mollify) cost_of_damage = check_credit(cost_of_damage, shkp); if (cost_of_damage > 0L) { money2mon(shkp, cost_of_damage); - gc.context.botl = 1; + disp.botl = TRUE; } pline("Mollified, %s accepts your restitution.", shkname(shkp)); /* move shk back to his home loc */ @@ -4679,7 +5318,7 @@ costly_spot(coordxy x, coordxy y) struct monst *shkp; struct eshk *eshkp; - if (!gl.level.flags.has_shop) + if (!svl.level.flags.has_shop) return FALSE; shkp = shop_keeper(*in_rooms(x, y, SHOPBASE)); if (!shkp || !inhishop(shkp)) @@ -4710,16 +5349,16 @@ costly_adjacent( /* called by dotalk(sounds.c) when #chatting; returns obj if location contains shop goods and shopkeeper is willing & able to speak */ struct obj * -shop_object(register coordxy x, register coordxy y) +shop_object(coordxy x, coordxy y) { - register struct obj *otmp; - register struct monst *shkp; + struct obj *otmp; + struct monst *shkp; shkp = shop_keeper(*in_rooms(x, y, SHOPBASE)); if (!shkp || !inhishop(shkp)) return (struct obj *) 0; - for (otmp = gl.level.objects[x][y]; otmp; otmp = otmp->nexthere) + for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp->nexthere) if (otmp->oclass != COIN_CLASS) break; /* note: otmp might have ->no_charge set, but that's ok */ @@ -4791,11 +5430,11 @@ price_quote(struct obj *first_obj) destroy_nhwindow(tmpwin); } -static const char * -shk_embellish(register struct obj* itm, long cost) +staticfn const char * +shk_embellish(struct obj *itm, long cost) { if (!rn2(3)) { - register int o, choice = rn2(5); + int o, choice = rn2(5); if (choice == 0) choice = (cost < 100L ? 1 : cost < 500L ? 2 : 3); @@ -4832,7 +5471,7 @@ shk_embellish(register struct obj* itm, long cost) DISABLE_WARNING_FORMAT_NONLITERAL /* First 4 supplied by Ronen and Tamar, remainder by development team */ -const char *Izchak_speaks[] = { +static const char *Izchak_speaks[] = { "%s says: 'These shopping malls give me a headache.'", "%s says: 'Slow down. Think clearly.'", "%s says: 'You need to take things one at a time.'", @@ -4845,7 +5484,7 @@ const char *Izchak_speaks[] = { }; void -shk_chat(struct monst* shkp) +shk_chat(struct monst *shkp) { struct eshk *eshk; long shkmoney; @@ -4870,7 +5509,7 @@ shk_chat(struct monst* shkp) (!Deaf && !muteshk(shkp)) ? "mentions" : "indicates", noit_mhe(shkp), eshk->robbed ? "non-paying" : "rude"); } else if (eshk->following) { - if (strncmp(eshk->customer, gp.plname, PL_NSIZ)) { + if (strncmp(eshk->customer, svp.plname, PL_NSIZ)) { if (!Deaf && !muteshk(shkp)) { SetVoice(shkp, 0, 80, 0); verbalize("%s %s! I was looking for %s.", @@ -4881,14 +5520,14 @@ shk_chat(struct monst* shkp) if (!Deaf && !muteshk(shkp)) { SetVoice(shkp, 0, 80, 0); verbalize("%s %s! Didn't you forget to pay?", - Hello(shkp), gp.plname); + Hello(shkp), svp.plname); } else { pline("%s taps you on the %s.", Shknam(shkp), body_part(ARM)); } } } else if (eshk->billct) { - register long total = addupbill(shkp) + eshk->debit; + long total = addupbill(shkp) + eshk->debit; pline("%s %s that your bill comes to %ld %s.", Shknam(shkp), @@ -4920,7 +5559,7 @@ shk_chat(struct monst* shkp) (!Deaf && !muteshk(shkp)) ? "says" : "indicates"); } else if (is_izchak(shkp, FALSE)) { if (!Deaf && !muteshk(shkp)) - pline(Izchak_speaks[rn2(SIZE(Izchak_speaks))], shkname(shkp)); + pline(ROLL_FROM(Izchak_speaks), shkname(shkp)); } else { if (!Deaf && !muteshk(shkp)) pline("%s talks about the problem of shoplifters.", Shknam(shkp)); @@ -4929,7 +5568,7 @@ shk_chat(struct monst* shkp) RESTORE_WARNING_FORMAT_NONLITERAL -static void +staticfn void kops_gone(boolean silent) { int cnt = 0; @@ -4950,7 +5589,7 @@ kops_gone(boolean silent) plur(cnt), (cnt == 1) ? "es" : ""); } -static long +staticfn long cost_per_charge( struct monst *shkp, struct obj *otmp, @@ -5012,7 +5651,7 @@ DISABLE_WARNING_FORMAT_NONLITERAL * when an object is completely used. */ void -check_unpaid_usage(struct obj* otmp, boolean altusage) +check_unpaid_usage(struct obj *otmp, boolean altusage) { struct monst *shkp; const char *fmt, *arg1, *arg2; @@ -5115,10 +5754,10 @@ costly_gold( /* used in domove to block diagonal shop-exit */ /* x,y should always be a door */ boolean -block_door(register coordxy x, register coordxy y) +block_door(coordxy x, coordxy y) { - register int roomno = *in_rooms(x, y, SHOPBASE); - register struct monst *shkp; + int roomno = *in_rooms(x, y, SHOPBASE); + struct monst *shkp; if (roomno < 0 || !IS_SHOP(roomno)) return FALSE; @@ -5150,11 +5789,11 @@ block_door(register coordxy x, register coordxy y) /* used in domove to block diagonal shop-entry; u.ux, u.uy should always be a door */ boolean -block_entry(register coordxy x, register coordxy y) +block_entry(coordxy x, coordxy y) { - register coordxy sx, sy; - register int roomno; - register struct monst *shkp; + coordxy sx, sy; + int roomno; + struct monst *shkp; if (!(IS_DOOR(levl[u.ux][u.uy].typ) && doorstate(&levl[u.ux][u.uy]) == D_BROKEN)) @@ -5188,7 +5827,7 @@ block_entry(register coordxy x, register coordxy y) char * shk_your(char *buf, struct obj *obj) { - boolean chk_pm = obj->otyp == CORPSE && obj->corpsenm >= LOW_PM; + boolean chk_pm = obj->otyp == CORPSE && ismnum(obj->corpsenm); buf[0] = '\0'; if (chk_pm && type_is_pname(&mons[obj->corpsenm])) @@ -5208,7 +5847,7 @@ Shk_Your(char *buf, struct obj *obj) return buf; } -static char * +staticfn char * shk_owns(char *buf, struct obj *obj) { struct monst *shkp; @@ -5223,7 +5862,7 @@ shk_owns(char *buf, struct obj *obj) return (char *) 0; } -static char * +staticfn char * mon_owns(char *buf, struct obj *obj) { if (obj->where == OBJ_MINVENT) @@ -5231,7 +5870,7 @@ mon_owns(char *buf, struct obj *obj) return (char *) 0; } -static const char * +staticfn const char * cad( boolean altusage) /* used as a verbalized exclamation: \"Cad! ...\" */ { @@ -5300,7 +5939,7 @@ sasc_bug(struct obj *op, unsigned x) * 4. player_owned glob merging into player_owned glob */ void -globby_bill_fixup(struct obj* obj_absorber, struct obj* obj_absorbed) +globby_bill_fixup(struct obj *obj_absorber, struct obj *obj_absorbed) { int x = 0, y = 0; struct bill_x *bp, *bp_absorber = (struct bill_x *) 0; @@ -5345,16 +5984,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) { @@ -5432,9 +6062,26 @@ globby_bill_fixup(struct obj* obj_absorber, struct obj* obj_absorbed) return; } +/* Shopkeeper bills for use of a land mine or bear trap they own */ +void +use_unpaid_trapobj(struct obj *otmp, coordxy x, coordxy y) +{ + if (otmp->unpaid) { + if (!Deaf) { + struct monst *shkp = find_objowner(otmp, x, y); + + if (shkp && !muteshk(shkp)) { + SetVoice(shkp, 0, 80, 0); + verbalize("You set it, you buy it!"); + } + } + bill_dummy_object(otmp); + } +} + /* Return a string representing the player's name with any relevant titles * prepended to it; e.g. "Sir Robert" for a male knight. */ -static char* +staticfn char* shk_plname_title(void) { static char NEARDATA buf[BUFSZ]; @@ -5454,14 +6101,23 @@ shk_plname_title(void) if (title) { char *namstrt; - Sprintf(buf, "%s %s", title, gp.plname); + Sprintf(buf, "%s %s", title, svp.plname); namstrt = buf + strlen(title) + 1; *namstrt = highc(*namstrt); /* capitalize plname */ } else { - Strcpy(buf, gp.plname); /* don't capitalize */ + Strcpy(buf, svp.plname); /* don't capitalize */ } return buf; } +#undef PAY_BUY +#undef PAY_CANT +#undef PAY_SKIP +#undef PAY_BROKE +#undef NOTANGRY +#undef ANGRY +#undef IS_SHOP +#undef muteshk + /*shk.c*/ diff --git a/src/shknam.c b/src/shknam.c index 6fa1455984..e64a1a8af5 100644 --- a/src/shknam.c +++ b/src/shknam.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 shknam.c $NHDT-Date: 1596498209 2020/08/03 23:43:29 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.57 $ */ +/* 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. */ @@ -7,14 +7,14 @@ #include "hack.h" -static boolean stock_room_goodpos(struct mkroom *, int, int, int, int); -static boolean veggy_item(struct obj * obj, int); -static int shkveg(void); -static void mkveggy_at(int, int); -static void mkshobj_at(const struct shclass *, int, int, boolean); -static void nameshk(struct monst *, const char *const *); -static int good_shopdoor(struct mkroom *, coordxy *, coordxy *); -static int shkinit(const struct shclass *, struct mkroom *); +staticfn boolean stock_room_goodpos(struct mkroom *, int, int, int, int); +staticfn boolean veggy_item(struct obj * obj, int); +staticfn int shkveg(void); +staticfn void mkveggy_at(int, int); +staticfn void mkshobj_at(const struct shclass *, int, int, boolean); +staticfn void nameshk(struct monst *, const char *const *); +staticfn int good_shopdoor(struct mkroom *, coordxy *, coordxy *); +staticfn int shkinit(const struct shclass *, struct mkroom *); #define VEGETARIAN_CLASS (MAXOCLASSES + 1) @@ -193,7 +193,7 @@ static const char *const shkhealthfoods[] = { * have to lower some or all of the probability fields in old entries to * free up some percentage for the new type. * - * The placement type field is not yet used but will be in the near future. + * The placement type field is not yet used but might be someday. * * The iprobs array in each entry defines the probabilities for various kinds * of objects to be present in the given shop type. You can associate with @@ -201,10 +201,13 @@ 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. + * + * The second, usually shorter, store type name is used in automatically + * generated annotations for #overview. If Null, the first name gets used. */ const struct shclass shtypes[] = { - { "general store", - "general store", + { "general store", NULL, RANDOM_CLASS, 42, D_SHOP, @@ -215,8 +218,7 @@ const struct shclass shtypes[] = { { 0, 0 }, { 0, 0 } }, shkgeneral }, - { "used armor dealership", - "armor shop", + { "used armor dealership", "armor shop", ARMOR_CLASS, 14, D_SHOP, @@ -227,8 +229,7 @@ const struct shclass shtypes[] = { { 0, 0 }, { 0, 0 } }, shkarmors }, - { "second-hand bookstore", - "scroll shop", + { "second-hand bookstore", "scroll shop", SCROLL_CLASS, 10, D_SHOP, @@ -239,8 +240,7 @@ const struct shclass shtypes[] = { { 0, 0 }, { 0, 0 } }, shkbooks }, - { "liquor emporium", - "potion shop", + { "liquor emporium", "potion shop", POTION_CLASS, 10, D_SHOP, @@ -251,8 +251,7 @@ const struct shclass shtypes[] = { { 0, 0 }, { 0, 0 } }, shkliquors }, - { "antique weapons outlet", - "weapon shop", + { "antique weapons outlet", "weapon shop", WEAPON_CLASS, 5, D_SHOP, @@ -263,8 +262,7 @@ const struct shclass shtypes[] = { { 0, 0 }, { 0, 0 } }, shkweapons }, - { "delicatessen", - "delicatessen", + { "delicatessen", "food shop", FOOD_CLASS, 5, D_SHOP, @@ -275,8 +273,7 @@ const struct shclass shtypes[] = { { 3, -ICE_BOX }, { 0, 0 } }, shkfoods }, - { "jewelers", - "jewelry shop", + { "jewelers", "ring shop", RING_CLASS, 3, D_SHOP, @@ -287,8 +284,7 @@ const struct shclass shtypes[] = { { 0, 0 }, { 0, 0 } }, shkrings }, - { "quality apparel and accessories", - "wand shop", + { "quality apparel and accessories", "wand shop", WAND_CLASS, 3, D_SHOP, @@ -297,8 +293,7 @@ const struct shclass shtypes[] = { { 5, -ELVEN_CLOAK }, { 0, 0 } }, shkwands }, - { "hardware store", - "tool shop", + { "hardware store", "tool shop", TOOL_CLASS, 3, D_SHOP, @@ -309,8 +304,7 @@ const struct shclass shtypes[] = { { 0, 0 }, { 0, 0 } }, shktools }, - { "rare books", - "bookstore", + { "rare books", "bookstore", SPBOOK_CLASS, 3, D_SHOP, @@ -321,8 +315,7 @@ const struct shclass shtypes[] = { { 0, 0 }, { 0, 0 } }, shkbooks }, - { "health food store", - "health food store", + { "health food store", "vegetarian food shop", FOOD_CLASS, 2, D_SHOP, @@ -337,8 +330,7 @@ const struct shclass shtypes[] = { * probability of zero. They are only created via the special level * loader. */ - { "lighting store", - "lighting shop", + { "lighting store", "lighting shop", TOOL_CLASS, 0, D_SHOP, @@ -353,8 +345,7 @@ const struct shclass shtypes[] = { { 1, -SPE_LIGHT } }, shklight }, /* sentinel */ - { (char *) 0, - (char *) 0, + { (char *) 0, NULL, 0, 0, 0, @@ -368,7 +359,7 @@ const struct shclass shtypes[] = { void init_shop_selection() { - register int i, j, item_prob, shop_prob; + int i, j, item_prob, shop_prob; for (shop_prob = 0, i = 0; i < SIZE(shtypes); i++) { shop_prob += shtypes[i].prob; @@ -385,7 +376,7 @@ init_shop_selection() /* decide whether an object or object type is considered vegetarian; for types, items which might go either way are assumed to be veggy */ -static boolean +staticfn boolean veggy_item(struct obj* obj, int otyp /* used iff obj is null */) { int corpsenm; @@ -408,13 +399,12 @@ veggy_item(struct obj* obj, int otyp /* used iff obj is null */) if (otyp == TIN && corpsenm == NON_PM) /* implies obj is non-null */ return (boolean) (obj->spe == 1); /* 0 = empty, 1 = spinach */ if (otyp == TIN || otyp == CORPSE) - return (boolean) (corpsenm >= LOW_PM - && vegetarian(&mons[corpsenm])); + return (boolean) (ismnum(corpsenm) && vegetarian(&mons[corpsenm])); } return FALSE; } -static int +staticfn int shkveg(void) { int i, j, maxprob, prob; @@ -423,7 +413,7 @@ shkveg(void) j = maxprob = 0; ok[0] = 0; /* lint suppression */ - for (i = gb.bases[(int) oclass]; i < NUM_OBJECTS; ++i) { + for (i = svb.bases[(int) oclass]; i < NUM_OBJECTS; ++i) { if (objects[i].oc_class != oclass) break; @@ -449,7 +439,7 @@ shkveg(void) } /* make a random item for health food store */ -static void +staticfn void mkveggy_at(int sx, int sy) { struct obj *obj = mksobj_at(shkveg(), sx, sy, TRUE, TRUE); @@ -460,8 +450,8 @@ mkveggy_at(int sx, int sy) } /* make an object of the appropriate type for a shop square */ -static void -mkshobj_at(const struct shclass* shp, int sx, int sy, boolean mkspecl) +staticfn void +mkshobj_at(const struct shclass *shp, int sx, int sy, boolean mkspecl) { struct monst *mtmp; struct permonst *ptr; @@ -473,7 +463,7 @@ mkshobj_at(const struct shclass* shp, int sx, int sy, boolean mkspecl) struct obj *novel = mksobj_at(SPE_NOVEL, sx, sy, FALSE, FALSE); if (novel) - gc.context.tribute.bookstock = TRUE; + svc.context.tribute.bookstock = TRUE; return; } @@ -493,8 +483,8 @@ mkshobj_at(const struct shclass* shp, int sx, int sy, boolean mkspecl) } /* extract a shopkeeper name for the given shop type */ -static void -nameshk(struct monst* shk, const char* const* nlp) +staticfn void +nameshk(struct monst *shk, const char *const *nlp) { int i, trycnt; const char *shname = 0; @@ -545,6 +535,7 @@ nameshk(struct monst* shk, const char* const* nlp) for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp) || (mtmp == shk) || !mtmp->isshk) continue; + assert(has_eshk(mtmp)); if (strcmp(ESHK(mtmp)->shknam, shname)) continue; name_wanted = names_avail; /* try a random name */ @@ -559,7 +550,7 @@ nameshk(struct monst* shk, const char* const* nlp) } void -neweshk(struct monst* mtmp) +neweshk(struct monst *mtmp) { if (!mtmp->mextra) mtmp->mextra = newmextra(); @@ -570,7 +561,7 @@ neweshk(struct monst* mtmp) } void -free_eshk(struct monst* mtmp) +free_eshk(struct monst *mtmp) { if (mtmp->mextra && ESHK(mtmp)) { free((genericptr_t) ESHK(mtmp)); @@ -580,9 +571,9 @@ free_eshk(struct monst* mtmp) } /* find a door in room sroom which is good for shop entrance. - returns -1 if no good door found, or the gd.doors index + returns -1 if no good door found, or the svd.doors index and the door coordinates in sx, sy */ -static int +staticfn int good_shopdoor(struct mkroom *sroom, coordxy *sx, coordxy *sy) { int i; @@ -590,12 +581,12 @@ good_shopdoor(struct mkroom *sroom, coordxy *sx, coordxy *sy) for (i = 0; i < sroom->doorct; i++) { int di = sroom->fdoor + i; - *sx = gd.doors[di].x; - *sy = gd.doors[di].y; + *sx = svd.doors[di].x; + *sy = svd.doors[di].y; /* check that the shopkeeper placement is sane */ if (sroom->irregular) { - int rmno = (int) ((sroom - gr.rooms) + ROOMOFFSET); + int rmno = (int) ((sroom - svr.rooms) + ROOMOFFSET); if (isok(*sx - 1, *sy) && !levl[*sx - 1][*sy].edge && (int) levl[*sx - 1][*sy].roomno == rmno) @@ -628,8 +619,8 @@ good_shopdoor(struct mkroom *sroom, coordxy *sx, coordxy *sy) } /* create a new shopkeeper in the given room */ -static int -shkinit(const struct shclass* shp, struct mkroom* sroom) +staticfn int +shkinit(const struct shclass *shp, struct mkroom *sroom) { int sh; coordxy sx, sy; @@ -643,7 +634,7 @@ shkinit(const struct shclass* shp, struct mkroom* sroom) /* Said to happen sometimes, but I have never seen it. */ /* Supposedly fixed by fdoor change in mklev.c */ if (wizard) { - register int j = sroom->doorct; + int j = sroom->doorct; impossible("Where is shopdoor?"); pline("Room at (%d,%d),(%d,%d).", sroom->lx, sroom->ly, sroom->hx, @@ -651,7 +642,7 @@ shkinit(const struct shclass* shp, struct mkroom* sroom) pline("doormax=%d doorct=%d fdoor=%d", gd.doorindex, sroom->doorct, sh); while (j--) { - pline("door [%d,%d]", gd.doors[sh].x, gd.doors[sh].y); + pline("door [%d,%d]", svd.doors[sh].x, svd.doors[sh].y); sh++; } display_nhwindow(WIN_MESSAGE, FALSE); @@ -671,11 +662,11 @@ shkinit(const struct shclass* shp, struct mkroom* sroom) set_malign(shk); shk->msleeping = 0; mon_learns_traps(shk, ALL_TRAPS); /* we know all the traps already */ - eshkp->shoproom = (schar) ((sroom - gr.rooms) + ROOMOFFSET); + eshkp->shoproom = (schar) ((sroom - svr.rooms) + ROOMOFFSET); sroom->resident = shk; eshkp->shoptype = sroom->rtype; assign_level(&eshkp->shoplevel, &u.uz); - eshkp->shd = gd.doors[sh]; + eshkp->shd = svd.doors[sh]; eshkp->shk.x = sx; eshkp->shk.y = sy; eshkp->robbed = eshkp->credit = eshkp->debit = eshkp->loan = 0L; @@ -691,18 +682,18 @@ shkinit(const struct shclass* shp, struct mkroom* sroom) return sh; } -static boolean -stock_room_goodpos(struct mkroom* sroom, int rmno, int sh, int sx, int sy) +staticfn boolean +stock_room_goodpos(struct mkroom *sroom, int rmno, int sh, int sx, int sy) { if (sroom->irregular) { if (levl[sx][sy].edge || (int) levl[sx][sy].roomno != rmno - || distmin(sx, sy, gd.doors[sh].x, gd.doors[sh].y) <= 1) + || distmin(sx, sy, svd.doors[sh].x, svd.doors[sh].y) <= 1) return FALSE; - } else if ((sx == sroom->lx && gd.doors[sh].x == sx - 1) - || (sx == sroom->hx && gd.doors[sh].x == sx + 1) - || (sy == sroom->ly && gd.doors[sh].y == sy - 1) - || (sy == sroom->hy && gd.doors[sh].y == sy + 1)) + } else if ((sx == sroom->lx && svd.doors[sh].x == sx - 1) + || (sx == sroom->hx && svd.doors[sh].x == sx + 1) + || (sy == sroom->ly && svd.doors[sh].y == sy - 1) + || (sy == sroom->hy && svd.doors[sh].y == sy + 1)) return FALSE; /* only generate items on solid floor squares */ @@ -715,7 +706,7 @@ stock_room_goodpos(struct mkroom* sroom, int rmno, int sh, int sx, int sy) /* stock a newly-created room with objects */ void -stock_room(int shp_indx, register struct mkroom* sroom) +stock_room(int shp_indx, struct mkroom *sroom) { /* * Someday soon we'll dispatch on the shdist field of shclass to do @@ -726,7 +717,7 @@ stock_room(int shp_indx, register struct mkroom* sroom) int sx, sy, sh; int stockcount = 0, specialspot = 0; char buf[BUFSZ]; - int rmno = (int) ((sroom - gr.rooms) + ROOMOFFSET); + int rmno = (int) ((sroom - svr.rooms) + ROOMOFFSET); const struct shclass *shp = &shtypes[shp_indx]; /* first, try to place a shopkeeper in the room */ @@ -734,8 +725,8 @@ stock_room(int shp_indx, register struct mkroom* sroom) return; /* make sure no doorways without doors, and no trapped doors, in shops */ - sx = gd.doors[sroom->fdoor].x; - sy = gd.doors[sroom->fdoor].y; + sx = svd.doors[sroom->fdoor].x; + sy = svd.doors[sroom->fdoor].y; if (doorstate(&levl[sx][sy]) == D_NODOOR) { set_doorstate(&levl[sx][sy], D_ISOPEN); newsym(sx, sy); @@ -751,7 +742,7 @@ stock_room(int shp_indx, register struct mkroom* sroom) } if (door_is_locked(&levl[sx][sy])) { - register int m = sx, n = sy; + int m = sx, n = sy; if (inside_shop(sx + 1, sy)) m--; @@ -768,7 +759,7 @@ stock_room(int shp_indx, register struct mkroom* sroom) || *in_rooms(m, n, 0)) ? ROOM : CORR; } - if (gc.context.tribute.enabled && !gc.context.tribute.bookstock) { + if (svc.context.tribute.enabled && !svc.context.tribute.bookstock) { /* * Out of the number of spots where we're actually * going to put stuff, randomly single out one in particular. @@ -796,16 +787,16 @@ stock_room(int shp_indx, register struct mkroom* sroom) /* Hack for Orcus's level: it's a ghost town, get rid of shopkeepers */ if (on_level(&u.uz, &orcus_level)) { - struct monst* mtmp = shop_keeper(rmno); + struct monst *mtmp = shop_keeper(rmno); mongone(mtmp); } - gl.level.flags.has_shop = TRUE; + svl.level.flags.has_shop = TRUE; } /* does shkp's shop stock this item type? */ boolean -saleable(struct monst* shkp, struct obj* obj) +saleable(struct monst *shkp, struct obj *obj) { int i, shp_indx = ESHK(shkp)->shoptype - SHOPBASE; const struct shclass *shp = &shtypes[shp_indx]; @@ -832,7 +823,7 @@ int get_shop_item(int type) { const struct shclass *shp = shtypes + type; - register int i, j; + int i, j; /* select an appropriate object type at random */ for (j = rnd(100), i = 0; (j -= shp->iprobs[i].iprob) > 0; i++) @@ -843,7 +834,7 @@ get_shop_item(int type) /* version of shkname() for beginning of sentence */ char * -Shknam(struct monst* mtmp) +Shknam(struct monst *mtmp) { char *nam = shkname(mtmp); @@ -856,7 +847,7 @@ Shknam(struct monst* mtmp) will yield some other shopkeeper's name (not necessarily one residing in the current game's dungeon, or who keeps same type of shop) */ char * -shkname(struct monst* mtmp) +shkname(struct monst *mtmp) { char *nam; unsigned save_isshk = mtmp->isshk; @@ -873,7 +864,7 @@ shkname(struct monst* mtmp) } else { const char *shknm = ESHK(mtmp)->shknam; - if (Hallucination && !gp.program_state.gameover) { + if (Hallucination && !program_state.gameover) { const char *const *nlp; int num; @@ -900,7 +891,7 @@ shkname(struct monst* mtmp) } boolean -shkname_is_pname(struct monst* mtmp) +shkname_is_pname(struct monst *mtmp) { const char *shknm = ESHK(mtmp)->shknam; @@ -908,7 +899,7 @@ shkname_is_pname(struct monst* mtmp) } boolean -is_izchak(struct monst* shkp, boolean override_hallucination) +is_izchak(struct monst *shkp, boolean override_hallucination) { const char *shknm; @@ -926,15 +917,6 @@ is_izchak(struct monst* shkp, boolean override_hallucination) return (boolean) !strcmp(shknm, "Izchak"); } -/* Return a pointer to the appropriate shtype for the given room type. */ -const struct shclass* -get_shtype(int rtype) -{ - if (rtype < SHOPBASE) { - impossible("get_shtype: given non-shop %d", rtype); - return &shtypes[0]; - } - return &shtypes[rtype - SHOPBASE]; -} +#undef VEGETARIAN_CLASS /*shknam.c*/ diff --git a/src/sit.c b/src/sit.c index 784ac07539..de11501da3 100644 --- a/src/sit.c +++ b/src/sit.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 sit.c $NHDT-Date: 1627414178 2021/07/27 19:29:38 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.73 $ */ +/* NetHack 3.7 sit.c $NHDT-Date: 1718136168 2024/06/11 20:02:48 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.95 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -6,8 +6,8 @@ #include "hack.h" #include "artifact.h" -static void throne_sit_effect(void); -static int lay_an_egg(void); +staticfn void throne_sit_effect(void); +staticfn int lay_an_egg(void); /* take away the hero's money */ void @@ -28,16 +28,35 @@ take_gold(void) You_feel("a strange sensation."); } else { You("notice you have no gold!"); - gc.context.botl = 1; + disp.botl = TRUE; } } /* maybe do something when hero sits on a throne */ -static void +staticfn void throne_sit_effect(void) { - if (rnd(6) > 4) { - switch (rnd(13)) { + coordxy tx = u.ux, ty = u.uy; + + if (rnd(6) > 4) { /* [why so convoluted? it's the same as '!rn2(3)'] */ + int effect = rnd(13); + + if (wizard && !iflags.debug_fuzzer) { + char buf[BUFSZ]; + int which; + + buf[0] = '\0'; + getlin("Throne sit effect (1..13) [0=random]", buf); + if (buf[0] == '\033') { + pline("%s", Never_mind); + return; /* caller will still cause a move to elapse */ + } + which = atoi(buf); + if (which >= 1 && which <= 13) + effect = which; + } + + switch (effect) { case 1: (void) adjattrib(rn2(A_MAX), -rn1(4, 3), AA_YESMSG); losehp(rnd(10), "cursed throne", KILLED_BY_AN); @@ -69,7 +88,7 @@ throne_sit_effect(void) make_blinded(0L, TRUE); make_sick(0L, (char *) 0, FALSE, SICK_ALL); heal_legs(0); - gc.context.botl = 1; + disp.botl = TRUE; break; case 5: take_gold(); @@ -95,7 +114,7 @@ throne_sit_effect(void) verbalize("Thine audience hath been summoned, %s!", flags.female ? "Dame" : "Sire"); while (cnt--) - (void) makemon(courtmon(), u.ux, u.uy, NO_MM_FLAGS); + (void) makemon(courtmon(), tx, ty, NO_MM_FLAGS); break; } case 8: @@ -143,6 +162,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")); @@ -189,16 +209,26 @@ throne_sit_effect(void) You_feel("somehow out of place..."); } - if (!rn2(3) && IS_THRONE(levl[u.ux][u.uy].typ)) { - /* may have teleported */ - levl[u.ux][u.uy].typ = ROOM, levl[u.ux][u.uy].flags = 0; - pline_The("throne vanishes in a puff of logic."); - newsym(u.ux, u.uy); + /* 3.7: when the random chance for removal is hit, ask for confirmation + if in wizard mode, and remove the throne even if hero was teleported + away from it. [This used to remove a throne at hero's current + location if there happened to be one, so for the teleport case that + 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')) { + levl[tx][ty].typ = ROOM, levl[tx][ty].flags = 0; + map_background(tx, ty, FALSE); + newsym_force(tx, ty); + /* "[God] promptly vanishes in a puff of logic" is from + Douglas Adams' _The_Hitchhiker's_Guide_to_the_Galaxy_. */ + pline_The("throne %s in a puff of logic.", + cansee(tx, ty) ? "vanishes" : "has vanished"); } } /* hero lays an egg */ -static int +staticfn int lay_an_egg(void) { struct obj *uegg; @@ -243,14 +273,15 @@ int dosit(void) { static const char sit_message[] = "sit on the %s."; - register struct trap *trap = t_at(u.ux, u.uy); - register int typ = levl[u.ux][u.uy].typ; + struct trap *trap = t_at(u.ux, u.uy); + int typ = levl[u.ux][u.uy].typ; if (u.usteed) { You("are already sitting on %s.", mon_nam(u.usteed)); return ECMD_OK; } - if (u.uundetected && is_hider(gy.youmonst.data) && u.umonnum != PM_TRAPPER) + if (u.uundetected && is_hider(gy.youmonst.data) + && u.umonnum != PM_TRAPPER) /* trapper can stay hidden on floor */ u.uundetected = 0; /* no longer on the ceiling */ if (!can_reach_floor(FALSE)) { @@ -271,15 +302,18 @@ dosit(void) return ECMD_OK; } else if (is_pool(u.ux, u.uy) && !Underwater) { /* water walking */ goto in_water; + } else if (Upolyd && u.umonnum == PM_GREMLIN + && (levl[u.ux][u.uy].typ == FOUNTAIN || is_pool(u.ux, u.uy))) { + goto in_water; } u_wipe_engr(rnd(5)); if (OBJ_AT(u.ux, u.uy) /* ensure we're not standing on the precipice */ && !(uteetering_at_seen_pit(trap) || uescaped_shaft(trap))) { - register struct obj *obj; + struct obj *obj; - obj = gl.level.objects[u.ux][u.uy]; + obj = svl.level.objects[u.ux][u.uy]; if (gy.youmonst.data->mlet == S_DRAGON && obj->oclass == COIN_CLASS) { You("coil up around your %shoard.", (obj->quan + money_cnt(gi.invent) < u.ulevel * 1000) @@ -287,10 +321,20 @@ 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..."); - if (!(Is_box(obj) || obj->material == CLOTH)) + else if (obj->otyp == CREAM_PIE) { + if (!Deaf) { + Soundeffect(se_squelch, 30); + pline("Squelch!"); + } + useupf(obj, obj->quan); + } + else if (!(Is_box(obj) || obj->material == CLOTH)) pline("It's not very comfortable..."); } } else if (trap != 0 || (u.utrap && (u.utraptype >= TT_LAVA))) { @@ -328,7 +372,10 @@ dosit(void) u.utrap++; } } else { - You("sit down."); + /* when flying, "you land" might need some refinement; it sounds + as if you're staying on the ground but you will immediately + take off again unless you become stuck in a holding trap */ + You("%s.", Flying ? "land" : "sit down"); dotrap(trap, VIASITTING); } } else if ((Underwater || Is_waterlevel(&u.uz)) @@ -340,10 +387,18 @@ dosit(void) } else if (is_pool(u.ux, u.uy) && !eggs_in_water(gy.youmonst.data)) { in_water: You("sit in the %s.", hliquid("water")); - if (!rn2(10) && uarm) - (void) water_damage(uarm, "armor", TRUE); - if (!rn2(10) && uarmf && uarmf->otyp != WATER_WALKING_BOOTS) - (void) water_damage(uarm, "armor", TRUE); + if (Upolyd && u.umonnum == PM_GREMLIN) { + if (split_mon(&gy.youmonst, (struct monst *) 0)) { + if (levl[u.ux][u.uy].typ == FOUNTAIN) + dryup(u.ux, u.uy, TRUE); + } + /* splitting--or failing to do so--protects gear from the water */ + } else { + if (!rn2(10) && uarm) + (void) water_damage(uarm, "armor", TRUE); + if (!rn2(10) && uarmf && uarmf->otyp != WATER_WALKING_BOOTS) + (void) water_damage(uarm, "armor", TRUE); + } } else if (IS_SINK(typ)) { You(sit_message, defsyms[S_sink].explanation); Your("%s gets wet.", @@ -487,6 +542,7 @@ attrcurse(void) ret = FIRE_RES; break; } + FALLTHROUGH; /*FALLTHRU*/ case 2: if (HTeleportation & INTRINSIC) { @@ -495,6 +551,7 @@ attrcurse(void) ret = TELEPORT; break; } + FALLTHROUGH; /*FALLTHRU*/ case 3: if (HPoison_resistance & INTRINSIC) { @@ -503,6 +560,7 @@ attrcurse(void) ret = POISON_RES; break; } + FALLTHROUGH; /*FALLTHRU*/ case 4: if (HTelepat & INTRINSIC) { @@ -513,6 +571,7 @@ attrcurse(void) ret = TELEPAT; break; } + FALLTHROUGH; /*FALLTHRU*/ case 5: if (HCold_resistance & INTRINSIC) { @@ -521,6 +580,7 @@ attrcurse(void) ret = COLD_RES; break; } + FALLTHROUGH; /*FALLTHRU*/ case 6: if (HInvis & INTRINSIC) { @@ -529,6 +589,7 @@ attrcurse(void) ret = INVIS; break; } + FALLTHROUGH; /*FALLTHRU*/ case 7: if (HSee_invisible & INTRINSIC) { @@ -544,6 +605,7 @@ attrcurse(void) ret = SEE_INVIS; break; } + FALLTHROUGH; /*FALLTHRU*/ case 8: if (HFast & INTRINSIC) { @@ -552,6 +614,7 @@ attrcurse(void) ret = FAST; break; } + FALLTHROUGH; /*FALLTHRU*/ case 9: if (HStealth & INTRINSIC) { @@ -560,6 +623,7 @@ attrcurse(void) ret = STEALTH; break; } + FALLTHROUGH; /*FALLTHRU*/ case 10: /* intrinsic protection is just disabled, not set back to 0 */ @@ -569,6 +633,7 @@ attrcurse(void) ret = PROTECTION; break; } + FALLTHROUGH; /*FALLTHRU*/ case 11: if (HAggravate_monster & INTRINSIC) { @@ -577,6 +642,7 @@ attrcurse(void) ret = AGGRAVATE_MONSTER; break; } + FALLTHROUGH; /*FALLTHRU*/ default: break; diff --git a/src/sounds.c b/src/sounds.c index 813cbdb2de..322a8832aa 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -1,29 +1,29 @@ -/* 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. */ #include "hack.h" -static boolean throne_mon_sound(struct monst *); -static boolean beehive_mon_sound(struct monst *); -static boolean morgue_mon_sound(struct monst *); -static boolean zoo_mon_sound(struct monst *); -static boolean temple_priest_sound(struct monst *); -static boolean mon_is_gecko(struct monst *); -static int domonnoise(struct monst *); -static int dochat(void); -static boolean is_stormy_monster(struct monst *); -static void pacify_with_words(struct monst *); -static struct monst *responsive_mon_at(int, int); -static int mon_in_room(struct monst *, int); +staticfn boolean throne_mon_sound(struct monst *); +staticfn boolean beehive_mon_sound(struct monst *); +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 dochat(void); +staticfn boolean is_stormy_monster(struct monst *); +staticfn void pacify_with_words(struct monst *); +staticfn struct monst *responsive_mon_at(int, int); +staticfn int mon_in_room(struct monst *, int); +staticfn boolean oracle_sound(struct monst *); /* this easily could be a macro, but it might overtax dumb compilers */ -static int -mon_in_room(struct monst* mon, int rmtyp) +staticfn int +mon_in_room(struct monst *mon, int rmtyp) { int rno = levl[mon->mx][mon->my].roomno; if (rno >= ROOMOFFSET) - return gr.rooms[rno - ROOMOFFSET].rtype == rmtyp; + return svr.rooms[rno - ROOMOFFSET].rtype == rmtyp; return FALSE; } @@ -37,14 +37,14 @@ getroomtype(coordxy x, coordxy y) * the player first enters the room. orig_rtype is only initialized late * in level creation and is effectively valid only after the level is * created. */ - return gi.in_mklev ? gr.rooms[rno - ROOMOFFSET].rtype - : gr.rooms[rno - ROOMOFFSET].orig_rtype; + return gi.in_mklev ? svr.rooms[rno - ROOMOFFSET].rtype + : svr.rooms[rno - ROOMOFFSET].orig_rtype; } /* not a room */ return OROOM; } -static boolean +staticfn boolean throne_mon_sound(struct monst *mtmp) { if ((mtmp->msleeping || is_lord(mtmp->data) @@ -76,7 +76,7 @@ throne_mon_sound(struct monst *mtmp) } -static boolean +staticfn boolean beehive_mon_sound(struct monst *mtmp) { if (is_bee(mtmp->data) && mon_in_room(mtmp, BEEHIVE)) { @@ -102,7 +102,7 @@ beehive_mon_sound(struct monst *mtmp) return FALSE; } -static boolean +staticfn boolean morgue_mon_sound(struct monst *mtmp) { if ((is_undead(mtmp->data) || is_vampshifter(mtmp)) @@ -128,7 +128,7 @@ morgue_mon_sound(struct monst *mtmp) return FALSE; } -static boolean +staticfn boolean zoo_mon_sound(struct monst *mtmp) { if ((mtmp->msleeping || is_animal(mtmp->data)) @@ -144,7 +144,7 @@ zoo_mon_sound(struct monst *mtmp) return FALSE; } -static boolean +staticfn boolean temple_priest_sound(struct monst *mtmp) { if (mtmp->ispriest && inhistemple(mtmp) @@ -194,7 +194,7 @@ temple_priest_sound(struct monst *mtmp) return FALSE; } -static boolean +staticfn boolean oracle_sound(struct monst *mtmp) { if (mtmp->data != &mons[PM_ORACLE]) @@ -218,8 +218,8 @@ oracle_sound(struct monst *mtmp) void dosounds(void) { - register struct mkroom *sroom; - register int hallu, vx, vy; + struct mkroom *sroom; + int hallu, vx, vy; struct monst *mtmp; boolean cant_hear = (Deaf || !flags.acoustics || u.uswallow || Underwater); @@ -237,8 +237,8 @@ dosounds(void) return; } if (In_quest(&u.uz) && u.uz.dlevel >= qlocate_level.dlevel - && Role_if(PM_ARCHEOLOGIST) && !gq.quest_status.touched_artifact - && !gq.quest_status.met_nemesis && !rn2(300)) { + && Role_if(PM_ARCHEOLOGIST) && !svq.quest_status.touched_artifact + && !svq.quest_status.met_nemesis && !rn2(300)) { /* special sounds for Arc quest */ if (!cant_hear) { You_hear("another explosion."); @@ -256,24 +256,24 @@ dosounds(void) if (cant_hear) return; - if (gl.level.flags.nfountains && !rn2(400)) { + if (svl.level.flags.nfountains && !rn2(400)) { static const char *const fountain_msg[4] = { "bubbling water.", "water falling on coins.", "the splashing of a naiad.", "a soda fountain!", }; You_hear1(fountain_msg[rn2(3) + hallu]); } - if (gl.level.flags.nsinks && !rn2(300)) { + if (svl.level.flags.nsinks && !rn2(300)) { static const char *const sink_msg[3] = { "a slow drip.", "a gurgling noise.", "dishes being washed!", }; You_hear1(sink_msg[rn2(2) + hallu]); } - if (gl.level.flags.has_court && !rn2(200)) { + if (svl.level.flags.has_court && !rn2(200)) { if (get_iter_mons(throne_mon_sound)) return; } - if (gl.level.flags.has_swamp && !rn2(200)) { + if (svl.level.flags.has_swamp && !rn2(200)) { static const char *const swamp_msg[3] = { "hear mosquitoes!", "smell marsh gas!", /* so it's a smell...*/ "hear Donald Duck!", @@ -281,10 +281,10 @@ dosounds(void) You1(swamp_msg[rn2(2) + hallu]); return; } - if (gl.level.flags.has_vault && !rn2(200)) { + if (svl.level.flags.has_vault && !rn2(200)) { if (!(sroom = search_special(VAULT))) { /* strange ... */ - gl.level.flags.has_vault = 0; + svl.level.flags.has_vault = 0; return; } if (gd_sound()) @@ -309,6 +309,7 @@ dosounds(void) break; } } + FALLTHROUGH; /*FALLTHRU*/ case 0: Soundeffect(se_guards_footsteps, 30); @@ -320,15 +321,15 @@ dosounds(void) } return; } - if (gl.level.flags.has_beehive && !rn2(200)) { + if (svl.level.flags.has_beehive && !rn2(200)) { if (get_iter_mons(beehive_mon_sound)) return; } - if (gl.level.flags.has_morgue && !rn2(200)) { + if (svl.level.flags.has_morgue && !rn2(200)) { if (get_iter_mons(morgue_mon_sound)) return; } - if (gl.level.flags.has_barracks && !rn2(200)) { + if (svl.level.flags.has_barracks && !rn2(200)) { static const char *const barracks_msg[4] = { "blades being honed.", "loud snoring.", "dice being thrown.", "General MacArthur!", @@ -351,14 +352,14 @@ dosounds(void) } } } - if (gl.level.flags.has_zoo && !rn2(200)) { + if (svl.level.flags.has_zoo && !rn2(200)) { if (get_iter_mons(zoo_mon_sound)) return; } - if (gl.level.flags.has_shop && !rn2(200)) { + if (svl.level.flags.has_shop && !rn2(200)) { if (!(sroom = search_special(ANY_SHOP))) { /* strange... */ - gl.level.flags.has_shop = 0; + svl.level.flags.has_shop = 0; return; } if (tended_shop(sroom) @@ -366,7 +367,7 @@ dosounds(void) const char *name = shkname(sroom->resident); struct eshk *eshkp = ESHK(sroom->resident); if (!eshkp->visitct || !*eshkp->customer - || strncmp(eshkp->customer, gp.plname, PL_NSIZ)) { + || strncmp(eshkp->customer, svp.plname, PL_NSIZ)) { name = "someone"; } if (hallu && rn2(2)) @@ -378,7 +379,7 @@ dosounds(void) } return; } - if (gl.level.flags.has_temple && !rn2(200) + if (svl.level.flags.has_temple && !rn2(200) && !(Is_astralevel(&u.uz) || Is_sanctum(&u.uz))) { if (get_iter_mons(temple_priest_sound)) return; @@ -405,7 +406,7 @@ dosounds(void) return; } else if (br && In_V_tower(&br->end2) && !In_V_tower(&u.uz) - && gm.mvitals[PM_VLAD_THE_IMPALER].died == 0) { + && svm.mvitals[PM_VLAD_THE_IMPALER].died == 0) { static const char *const vlad_msgs[3] = { "the whispering of leathery wings.", "a horrible choking scream.", @@ -427,7 +428,7 @@ static const char *const h_sounds[] = { }; const char * -growl_sound(register struct monst* mtmp) +growl_sound(struct monst *mtmp) { const char *ret; @@ -478,23 +479,23 @@ growl_sound(register struct monst* mtmp) /* the sounds of a seriously abused pet, including player attacking it */ void -growl(register struct monst* mtmp) +growl(struct monst *mtmp) { - register const char *growl_verb = 0; + const char *growl_verb = 0; if (helpless(mtmp) || mtmp->data->msound == MS_SILENT) return; /* presumably nearness and soundok checks have already been made */ if (Hallucination) - growl_verb = h_sounds[rn2(SIZE(h_sounds))]; + growl_verb = ROLL_FROM(h_sounds); else growl_verb = growl_sound(mtmp); if (growl_verb) { if (canseemon(mtmp) || !Deaf) { pline("%s %s!", Monnam(mtmp), vtense((char *) 0, growl_verb)); iflags.last_msg = PLNMSG_GROWL; - if (gc.context.run) + if (svc.context.run) nomul(0); } wake_nearto(mtmp->mx, mtmp->my, mtmp->data->mlevel * 18); @@ -503,9 +504,9 @@ growl(register struct monst* mtmp) /* the sounds of mistreated pets */ void -yelp(register struct monst* mtmp) +yelp(struct monst *mtmp) { - register const char *yelp_verb = 0; + const char *yelp_verb = 0; enum sound_effect_entries se = se_yelp; if (helpless(mtmp) || !mtmp->data->msound) @@ -513,7 +514,7 @@ yelp(register struct monst* mtmp) /* presumably nearness and soundok checks have already been made */ if (Hallucination) - yelp_verb = h_sounds[rn2(SIZE(h_sounds))]; + yelp_verb = ROLL_FROM(h_sounds); else switch (mtmp->data->msound) { case MS_MEW: @@ -544,7 +545,7 @@ yelp(register struct monst* mtmp) if (yelp_verb) { Soundeffect(se, 70); /* Soundeffect() handles Deaf or not Deaf */ pline("%s %s!", Monnam(mtmp), vtense((char *) 0, yelp_verb)); - if (gc.context.run) + if (svc.context.run) nomul(0); wake_nearto(mtmp->mx, mtmp->my, mtmp->data->mlevel * 12); } @@ -555,16 +556,16 @@ yelp(register struct monst* mtmp) /* the sounds of distressed pets */ void -whimper(register struct monst* mtmp) +whimper(struct monst *mtmp) { - register const char *whimper_verb = 0; + const char *whimper_verb = 0; enum sound_effect_entries se = se_canine_whine; if (helpless(mtmp) || !mtmp->data->msound) return; /* presumably nearness and soundok checks have already been made */ if (Hallucination) - whimper_verb = h_sounds[rn2(SIZE(h_sounds))]; + whimper_verb = ROLL_FROM(h_sounds); else switch (mtmp->data->msound) { case MS_MEW: @@ -584,7 +585,7 @@ whimper(register struct monst* mtmp) Soundeffect(se, 50); } pline("%s %s.", Monnam(mtmp), vtense((char *) 0, whimper_verb)); - if (gc.context.run) + if (svc.context.run) nomul(0); wake_nearto(mtmp->mx, mtmp->my, mtmp->data->mlevel * 6); } @@ -595,7 +596,7 @@ whimper(register struct monst* mtmp) /* pet makes "I'm hungry" noises */ void -beg(register struct monst* mtmp) +beg(struct monst *mtmp) { if (helpless(mtmp) || !(carnivorous(mtmp->data) || herbivorous(mtmp->data))) @@ -622,7 +623,7 @@ beg(register struct monst* mtmp) /* hero has attacked a peaceful monster within 'mon's view */ const char * -maybe_gasp(struct monst* mon) +maybe_gasp(struct monst *mon) { static const char *const Exclam[] = { "Gasp!", "Uh-oh.", "Oh my!", "What?", "Why?", @@ -683,7 +684,7 @@ maybe_gasp(struct monst* mon) break; } if (dogasp) { - return Exclam[rn2(SIZE(Exclam))]; /* [mon->m_id % SIZE(Exclam)]; */ + return ROLL_FROM(Exclam); /* [mon->m_id % SIZE(Exclam)]; */ } return (const char *) 0; } @@ -711,7 +712,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; @@ -735,7 +735,7 @@ cry_sound(struct monst *mtmp) } /* return True if mon is a gecko or seems to look like one (hallucination) */ -static boolean +staticfn boolean mon_is_gecko(struct monst *mon) { int glyph; @@ -755,11 +755,11 @@ mon_is_gecko(struct monst *mon) DISABLE_WARNING_FORMAT_NONLITERAL -static int /* check calls to this */ -domonnoise(register struct monst* mtmp) +int /* check calls to this */ +domonnoise(struct monst *mtmp) { char verbuf[BUFSZ]; - register const char *pline_msg = 0, /* Monnam(mtmp) will be prepended */ + const char *pline_msg = 0, /* Monnam(mtmp) will be prepended */ *verbl_msg = 0, /* verbalize() */ *verbl_msg_mcan = 0; /* verbalize() if cancelled */ struct permonst *ptr = mtmp->data; @@ -773,9 +773,9 @@ domonnoise(register struct monst* mtmp) return ECMD_OK; /* leader might be poly'd; if he can still speak, give leader speech */ - if (mtmp->m_id == gq.quest_status.leader_m_id && msound > MS_ANIMAL) + if (mtmp->m_id == svq.quest_status.leader_m_id && msound > MS_ANIMAL) msound = MS_LEADER; - if (mtmp->m_id == gq.quest_status.nemesis_m_id && msound > MS_ANIMAL) + if (mtmp->m_id == svq.quest_status.nemesis_m_id && msound > MS_ANIMAL) msound = MS_NEMESIS; /* make sure it's your role's quest guardian; adjust if not */ else if (msound == MS_GUARDIAN && ptr != &mons[gu.urole.guardnum]) @@ -872,12 +872,13 @@ domonnoise(register struct monst* mtmp) int vampindex; if (kindred) { - verbl_msg = - "This is my hunting ground that you dare to prowl!"; + verbl_msg = "This is my hunting ground" + " that you dare to prowl!"; } else if (gy.youmonst.data == &mons[PM_SILVER_DRAGON] || gy.youmonst.data == &mons[PM_BABY_SILVER_DRAGON]) { /* Silver dragons are silver in color, not made of silver */ - Sprintf(verbuf, "%s! Your silver sheen does not frighten me!", + Sprintf(verbuf, + "%s! Your silver sheen"" does not frighten me!", (gy.youmonst.data == &mons[PM_SILVER_DRAGON]) ? "Fool" : "Young Fool"); @@ -919,9 +920,9 @@ domonnoise(register struct monst* mtmp) } else if (mtmp->mpeaceful) { if (mtmp->mtame && (mtmp->mconf || mtmp->mflee || mtmp->mtrapped - || gm.moves > EDOG(mtmp)->hungrytime || mtmp->mtame < 5)) + || svm.moves > EDOG(mtmp)->hungrytime || mtmp->mtame < 5)) pline_msg = "whines."; - else if (mtmp->mtame && EDOG(mtmp)->hungrytime > gm.moves + 1000) + else if (mtmp->mtame && EDOG(mtmp)->hungrytime > svm.moves + 1000) pline_msg = "yips."; else { if (ptr != &mons[PM_DINGO]) /* dingos do not actually bark */ @@ -937,10 +938,10 @@ domonnoise(register struct monst* mtmp) || mtmp->mtame < 5) { Soundeffect(se_feline_yowl, 80); pline_msg = "yowls."; - } else if (gm.moves > EDOG(mtmp)->hungrytime) { + } else if (svm.moves > EDOG(mtmp)->hungrytime) { Soundeffect(se_feline_meow, 80); pline_msg = "meows."; - } else if (EDOG(mtmp)->hungrytime > gm.moves + 1000) { + } else if (EDOG(mtmp)->hungrytime > svm.moves + 1000) { Soundeffect(se_feline_purr, 40); pline_msg = "purrs."; } else { @@ -949,6 +950,7 @@ domonnoise(register struct monst* mtmp) } break; } + FALLTHROUGH; /*FALLTHRU*/ case MS_GROWL: Soundeffect((mtmp->mpeaceful ? se_snarl : se_growl), 80); @@ -1006,7 +1008,7 @@ domonnoise(register struct monst* mtmp) if (mtmp->mtame < 5) { Soundeffect(se_equine_neigh, 60); pline_msg = "neighs."; - } else if (gm.moves > EDOG(mtmp)->hungrytime) { + } else if (svm.moves > EDOG(mtmp)->hungrytime) { Soundeffect(se_equine_whinny, 60); pline_msg = "whinnies."; } else { @@ -1121,6 +1123,7 @@ domonnoise(register struct monst* mtmp) } break; } + FALLTHROUGH; /*FALLTHRU*/ case MS_HUMANOID: if (!mtmp->mpeaceful) { @@ -1148,7 +1151,7 @@ domonnoise(register struct monst* mtmp) } else if (mtmp->mhp < mtmp->mhpmax / 2) pline_msg = "asks for a potion of healing."; else if (mtmp->mtame && !mtmp->isminion - && gm.moves > EDOG(mtmp)->hungrytime) + && svm.moves > EDOG(mtmp)->hungrytime) verbl_msg = "I'm hungry."; /* Specific monsters' interests */ else if (is_elf(ptr)) @@ -1173,11 +1176,12 @@ domonnoise(register struct monst* mtmp) Phase 1 Phase 2 Phase 3 Collect underpants ? Profit and they never verbalize step 2 so we don't either */ - verbl_msg = (gnomeplan == 1) ? "Phase one, collect underpants." - : "Phase three, profit!"; + verbl_msg = (gnomeplan == 1) + ? "Phase one, collect underpants." + : "Phase three, profit!"; } else { - verbl_msg = - "Many enter the dungeon, and few return to the sunlit lands."; + verbl_msg = "Many enter the dungeon," + " and few return to the sunlit lands."; } } else switch (monsndx(ptr)) { @@ -1245,7 +1249,8 @@ domonnoise(register struct monst* mtmp) (void) demon_talk(mtmp); break; } - /* fall through */ + FALLTHROUGH; + /* FALLTHRU */ case MS_CUSS: if (!mtmp->mpeaceful) cuss(mtmp); @@ -1297,7 +1302,7 @@ domonnoise(register struct monst* mtmp) boolean ms_Death = (ptr == &mons[PM_DEATH]); /* 3.6 tribute */ - if (ms_Death && !gc.context.tribute.Deathnotice + if (ms_Death && !svc.context.tribute.Deathnotice && (book = u_have_novel()) != 0) { if ((tribtitle = noveltitle(&book->novelidx)) != 0) { Sprintf(verbuf, "Ah, so you have a copy of /%s/.", tribtitle); @@ -1307,7 +1312,7 @@ domonnoise(register struct monst* mtmp) Strcat(verbuf, " I may have been misquoted there."); verbl_msg = verbuf; } - gc.context.tribute.Deathnotice = 1; + svc.context.tribute.Deathnotice = 1; } else if (ms_Death && rn2(3) && Death_quote(verbuf, sizeof verbuf)) { verbl_msg = verbuf; /* end of tribute addition */ @@ -1354,7 +1359,7 @@ dotalk(void) return result; } -static int +staticfn int dochat(void) { struct monst *mtmp; @@ -1468,11 +1473,12 @@ dochat(void) Hallucination ? rndmonnam((char *) 0) : "statue"); return ECMD_OK; } - if (!Deaf && (IS_WALL(levl[tx][ty].typ) || levl[tx][ty].typ == SDOOR)) { + if (!Deaf && (IS_WALL(levl[tx][ty].typ) + || levl[tx][ty].typ == SDOOR)) { /* Talking to a wall; secret door remains hidden by behaving like a wall; IS_WALL() test excludes solid rock even when that serves as a wall bordering a corridor */ - if (Blind && !IS_WALL(gl.lastseentyp[tx][ty])) { + if (Blind && !IS_WALL(svl.lastseentyp[tx][ty])) { /* when blind, you can only talk to a wall if it has already been mapped as a wall */ ; @@ -1545,7 +1551,7 @@ dochat(void) } /* is mtmp a storm-like monster pacifiable by the Amulet of Storms? */ -static boolean +staticfn boolean is_stormy_monster(struct monst *mtmp) { /* already peaceful */ @@ -1562,7 +1568,7 @@ is_stormy_monster(struct monst *mtmp) || mtmp->data == &mons[PM_STORM_GIANT]); } -static void +staticfn void pacify_with_words(struct monst *mtmp) { pline("%s recognizes your amulet.", Monnam(mtmp)); @@ -1573,7 +1579,7 @@ pacify_with_words(struct monst *mtmp) } /* is there a monster at that can see the hero and react? */ -static struct monst * +staticfn struct monst * responsive_mon_at(int x, int y) { struct monst *mtmp = isok(x, y) ? m_at(x, y) : 0; @@ -1632,7 +1638,7 @@ tiphat(void) for (range = 1; range <= BOLT_LIM + 1; ++range) { x += u.dx, y += u.dy; if (!isok(x, y) || (range > 1 && !couldsee(x, y))) { - /* switch back to coordinates for previous interation's 'mtmp' */ + /* switch back to coordinates for previous iteration's 'mtmp' */ x -= u.dx, y -= u.dy; break; } @@ -1717,7 +1723,7 @@ char *sounddir = 0; /* set in files.c */ /* adds a sound file mapping, returns 0 on failure, 1 on success */ int -add_sound_mapping(const char* mapping) +add_sound_mapping(const char *mapping) { char text[256]; char filename[256]; @@ -1726,11 +1732,16 @@ add_sound_mapping(const char* mapping) int volume, idx = -1; msgtyp[0] = '\0'; + filename[sizeof filename - 1] = '\0'; + filespec[sizeof filespec - 1] = '\0'; + text[sizeof text - 1] = '\0'; if (sscanf(mapping, "MESG \"%255[^\"]\"%*[\t ]\"%255[^\"]\" %d %d", text, filename, &volume, &idx) == 4 - || sscanf(mapping, "MESG %10[^\"] \"%255[^\"]\"%*[\t ]\"%255[^\"]\" %d %d", + || sscanf(mapping, + "MESG %10[^\"] \"%255[^\"]\"%*[\t ]\"%255[^\"]\" %d %d", msgtyp, text, filename, &volume, &idx) == 5 - || sscanf(mapping, "MESG %10[^\"] \"%255[^\"]\"%*[\t ]\"%255[^\"]\" %d", + || sscanf(mapping, + "MESG %10[^\"] \"%255[^\"]\"%*[\t ]\"%255[^\"]\" %d", msgtyp, text, filename, &volume) == 4 || sscanf(mapping, "MESG \"%255[^\"]\"%*[\t ]\"%255[^\"]\" %d", text, filename, &volume) == 3) { @@ -1754,7 +1765,8 @@ add_sound_mapping(const char* mapping) if (!regex_compile(text, new_map->regex)) { char errbuf[BUFSZ]; - char *re_error_desc = regex_error_desc(new_map->regex, errbuf); + char *re_error_desc + = regex_error_desc(new_map->regex, errbuf); regex_free(new_map->regex); free((genericptr_t) new_map->filename); @@ -1783,8 +1795,8 @@ add_sound_mapping(const char* mapping) return 1; } -static audio_mapping * -sound_matches_message(const char* msg) +staticfn audio_mapping * +sound_matches_message(const char *msg) { audio_mapping *snd = soundmap; @@ -1797,7 +1809,7 @@ sound_matches_message(const char* msg) } void -play_sound_for_message(const char* msg) +play_sound_for_message(const char *msg) { audio_mapping *snd; @@ -1814,7 +1826,7 @@ play_sound_for_message(const char* msg) } void -maybe_play_sound(const char* msg) +maybe_play_sound(const char *msg) { audio_mapping *snd; @@ -1881,7 +1893,7 @@ extern struct sound_procs macsound_procs; extern struct sound_procs qtsound_procs; #endif -struct sound_procs nosound_procs = { +static struct sound_procs nosound_procs = { SOUNDID(nosound), 0L, (void (*)(void)) 0, /* init_nhsound */ @@ -1938,7 +1950,7 @@ activate_chosen_soundlib(void) { int idx = gc.chosen_soundlib; - if (idx < soundlib_nosound || idx >= SIZE(soundlib_choices)) + if (!IndexOk(idx, soundlib_choices)) panic("activate_chosen_soundlib: invalid soundlib (%d)", idx); if (ga.active_soundlib != soundlib_nosound || idx != soundlib_nosound) { @@ -1955,7 +1967,7 @@ activate_chosen_soundlib(void) void assign_soundlib(int idx) { - if (idx < soundlib_nosound || idx >= SIZE(soundlib_choices)) + if (!IndexOk(idx, soundlib_choices)) panic("assign_soundlib: invalid soundlib (%d)", idx); gc.chosen_soundlib @@ -1963,7 +1975,7 @@ assign_soundlib(int idx) } #if 0 -static void +staticfn void choose_soundlib(const char *s) { int i; @@ -2025,7 +2037,7 @@ get_soundlib_name(char *dest, int maxlen) const char *src; idx = ga.active_soundlib; - if (idx < soundlib_nosound || idx >= SIZE(soundlib_choices)) + if (!IndexOk(idx, soundlib_choices)) panic("get_soundlib_name: invalid active_soundlib (%d)", idx); src = soundlib_choices[idx].sndprocs->soundname; @@ -2037,6 +2049,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 * @@ -2045,54 +2072,54 @@ get_soundlib_name(char *dest, int maxlen) */ #if 0 -static void nosound_init_nhsound(void); -static void nosound_exit_nhsound(const char *); -static void nosound_suspend_nhsound(const char *); -static void nosound_resume_nhsound(void); -static void nosound_achievement(schar, schar, int32_t); -static void nosound_soundeffect(int32_t, int32_t); -static void nosound_play_usersound(char *, int32_t, int32_t); -static void nosound_ambience(int32_t, int32_t, int32_t); -static void nosound_verbal(char *text, int32_t gender, int32_t tone, +staticfn void nosound_init_nhsound(void); +staticfn void nosound_exit_nhsound(const char *); +staticfn void nosound_suspend_nhsound(const char *); +staticfn void nosound_resume_nhsound(void); +staticfn void nosound_achievement(schar, schar, int32_t); +staticfn void nosound_soundeffect(int32_t, int32_t); +staticfn void nosound_play_usersound(char *, int32_t, int32_t); +staticfn void nosound_ambience(int32_t, int32_t, int32_t); +staticfn void nosound_verbal(char *text, int32_t gender, int32_t tone, int32_t vol, int32_t moreinfo); -static void +staticfn void nosound_init_nhsound(void) { } -static void +staticfn void nosound_exit_nhsound(const char *reason) { } -static void +staticfn void nosound_achievement(schar ach1, schar ach2, int32_t repeat) { } -static void +staticfn void nosound_soundeffect(int32_t seid, int volume) { } -static void +staticfn void nosound_hero_playnotes(int32_t instr, const char *notes, int32_t vol) { } -static void +staticfn void nosound_play_usersound(char *filename, int volume, int idx) { } -static void +staticfn void nosound_ambience(int32_t ambienceid, int32_t ambience_action, int32_t hero_proximity) { } -static void +staticfn void nosound_verbal(char *text, int32_t gender, int32_t tone, int32_t vol, int32_t moreinfo) { @@ -2100,6 +2127,10 @@ nosound_verbal(char *text, int32_t gender, int32_t tone, #endif #ifdef SND_SOUNDEFFECTS_AUTOMAP + +/* prototype in case a build defines staticfn to nothing */ +staticfn void initialize_semap_basenames(void); + struct soundeffect_automapping { enum sound_effect_entries seid; const char *base_filename; @@ -2116,7 +2147,7 @@ static const struct soundeffect_automapping static const char *semap_basenames[SIZE(se_mappings_init)]; static boolean basenames_initialized = FALSE; -static void +staticfn void initialize_semap_basenames(void) { int i; @@ -2297,7 +2328,11 @@ base_soundname_to_filename( #endif void -set_voice(struct monst *mtmp SPEECHONLY, int32_t tone SPEECHONLY, int32_t volume SPEECHONLY, int32_t moreinfo SPEECHONLY) +set_voice( + struct monst *mtmp SPEECHONLY, + int32_t tone SPEECHONLY, + int32_t volume SPEECHONLY, + int32_t moreinfo SPEECHONLY) { #ifdef SND_SPEECH int32_t gender = (mtmp && mtmp->female) ? FEMALE : MALE; diff --git a/src/sp_lev.c b/src/sp_lev.c index 9a15c99c47..1ac0ca10e7 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 sp_lev.c $NHDT-Date: 1646428015 2022/03/04 21:06:55 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.259 $ */ +/* NetHack 3.7 sp_lev.c $NHDT-Date: 1709921020 2024/03/08 18:03:40 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.359 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -14,114 +14,118 @@ #include "hack.h" #include "sp_lev.h" -typedef void (*select_iter_func)(coordxy, coordxy, genericptr); - extern void mkmap(lev_init *); -static boolean match_maptyps(xint16, xint16); -static void solidify_map(void); -static void map_cleanup(void); -static void lvlfill_swamp(schar, schar, schar); -static void flip_drawbridge_horizontal(struct rm *); -static void flip_drawbridge_vertical(struct rm *); -static void flip_visuals(int, int, int, int, int); -static int flip_encoded_direction_bits(int, int); -static void flip_vault_guard(int, struct monst *, +staticfn void solidify_map(void); +staticfn void map_cleanup(void); +staticfn void lvlfill_maze_grid(int, int, int, int, schar); +staticfn void lvlfill_solid(schar, schar); +staticfn void lvlfill_swamp(schar, schar, schar); +staticfn void flip_dbridge_horizontal(struct rm *); +staticfn void flip_dbridge_vertical(struct rm *); +staticfn void flip_visuals(int, int, int, int, int); +staticfn int flip_encoded_dir_bits(int, int); +staticfn void flip_vault_guard(int, struct monst *, coordxy, coordxy, coordxy, coordxy); -static void sel_set_wall_property(coordxy, coordxy, genericptr_t); -static void set_wall_property(coordxy, coordxy, coordxy, coordxy, int); -static void count_features(void); -static void remove_boundary_syms(void); -static void set_door_orientation(int, int); -static boolean shared_with_room(int, int, struct mkroom *); -static void maybe_add_door(int, int, struct mkroom *); -static void link_doors_rooms(void); -static int rndtrap(void); -static void get_location(coordxy *, coordxy *, getloc_flags_t, struct mkroom *); -static void set_ok_location_func(boolean (*)(coordxy, coordxy)); -static boolean is_ok_location(coordxy, coordxy, getloc_flags_t); -static unpacked_coord get_unpacked_coord(long, int); -static void get_room_loc(coordxy *, coordxy *, struct mkroom *); -static void get_free_room_loc(coordxy *, coordxy *, struct mkroom *, +staticfn void sel_set_wall_property(coordxy, coordxy, genericptr_t); +staticfn void set_wall_property(coordxy, coordxy, coordxy, coordxy, int); +staticfn void remove_boundary_syms(void); +staticfn void set_door_orientation(int, int); +staticfn boolean shared_with_room(int, int, struct mkroom *); +staticfn void maybe_add_door(int, int, struct mkroom *); +staticfn void link_doors_rooms(void); +staticfn int rnddoor(void); +staticfn int rndtrap(void); +staticfn void get_location(coordxy *, coordxy *, getloc_flags_t, + struct mkroom *); +staticfn void set_ok_location_func(boolean (*)(coordxy, coordxy)); +staticfn boolean is_ok_location(coordxy, coordxy, getloc_flags_t); +staticfn unpacked_coord get_unpacked_coord(long, int); +staticfn void get_room_loc(coordxy *, coordxy *, struct mkroom *); +staticfn void get_free_room_loc(coordxy *, coordxy *, struct mkroom *, packed_coord); -static boolean create_subroom(struct mkroom *, coordxy, coordxy, coordxy, +staticfn boolean create_subroom(struct mkroom *, coordxy, coordxy, coordxy, coordxy, xint16, xint16); -static void create_door(room_door *, struct mkroom *); -static void create_trap(spltrap *, struct mkroom *); -static int noncoalignment(aligntyp); -static boolean m_bad_boulder_spot(coordxy, coordxy); -static int pm_to_humidity(struct permonst *); -static unsigned int sp_amask_to_amask(unsigned int sp_amask); -static void create_monster(monster *, struct mkroom *); -static struct obj *create_object(object *, struct mkroom *); -static void create_altar(altar *, struct mkroom *); -static boolean search_door(struct mkroom *, coordxy *, coordxy *, xint16, int); -static void create_corridor(corridor *); -static struct mkroom *build_room(room *, struct mkroom *); -static void light_region(region *); -static void maze1xy(coord *, int); -static void fill_empty_maze(void); -static void splev_initlev(lev_init *); -static boolean generate_way_out_method(coordxy nx, coordxy ny, +staticfn void create_door(room_door *, struct mkroom *); +staticfn void create_trap(spltrap *, struct mkroom *); +staticfn int noncoalignment(aligntyp); +staticfn boolean m_bad_boulder_spot(coordxy, coordxy); +staticfn int pm_to_humidity(struct permonst *); +staticfn unsigned int sp_amask_to_amask(unsigned int sp_amask); +staticfn void create_monster(monster *, struct mkroom *); +staticfn struct obj *create_object(object *, struct mkroom *); +staticfn void create_altar(altar *, struct mkroom *); +staticfn boolean search_door(struct mkroom *, coordxy *, coordxy *, xint16, + int) NONNULLPTRS; +staticfn void create_corridor(corridor *); +staticfn struct mkroom *build_room(room *, struct mkroom *); +staticfn void light_region(region *); +staticfn void maze1xy(coord *, int); +staticfn void fill_empty_maze(void); +staticfn void splev_initlev(lev_init *); +staticfn boolean generate_way_out_method(coordxy nx, coordxy ny, struct selectionvar *ov); +staticfn void l_push_wid_hei_table(lua_State *, int, int); +staticfn boolean good_stair_loc(coordxy, coordxy); +staticfn void ensure_way_out(void); + #if 0 /* macosx complains that these are unused */ -static long sp_code_jmpaddr(long, long); -static void spo_room(struct sp_coder *); -static void spo_trap(struct sp_coder *); -static void spo_gold(struct sp_coder *); -static void spo_corridor(struct sp_coder *); -static void spo_feature(struct sp_coder *); -static void spo_terrain(struct sp_coder *); -static void spo_replace_terrain(struct sp_coder *); -static void spo_levregion(struct sp_coder *); -static void spo_region(struct sp_coder *); -static void spo_drawbridge(struct sp_coder *); -static void spo_mazewalk(struct sp_coder *); -static void spo_wall_property(struct sp_coder *); -static void spo_room_door(struct sp_coder *); -static void spo_wallify(struct sp_coder *); -static void sel_set_wallify(coordxy, coordxy, genericptr_t); +staticfn long sp_code_jmpaddr(long, long); +staticfn void spo_room(struct sp_coder *); +staticfn void spo_trap(struct sp_coder *); +staticfn void spo_gold(struct sp_coder *); +staticfn void spo_corridor(struct sp_coder *); +staticfn void spo_feature(struct sp_coder *); +staticfn void spo_terrain(struct sp_coder *); +staticfn void spo_replace_terrain(struct sp_coder *); +staticfn void spo_levregion(struct sp_coder *); +staticfn void spo_region(struct sp_coder *); +staticfn void spo_drawbridge(struct sp_coder *); +staticfn void spo_mazewalk(struct sp_coder *); +staticfn void spo_wall_property(struct sp_coder *); +staticfn void spo_room_door(struct sp_coder *); +staticfn void spo_wallify(struct sp_coder *); +staticfn void sel_set_wallify(coordxy, coordxy, genericptr_t); #endif -static void spo_end_moninvent(void); -static void spo_pop_container(void); -static int l_create_stairway(lua_State *, boolean); -static void spo_endroom(struct sp_coder *); -static void l_table_getset_feature_flag(lua_State *, int, int, const char *, +staticfn void spo_end_moninvent(void); +staticfn void spo_pop_container(void); +staticfn int l_create_stairway(lua_State *, boolean); +staticfn void spo_endroom(struct sp_coder *); +staticfn void l_table_getset_feature_flag(lua_State *, int, int, const char *, int); -static void l_get_lregion(lua_State *, lev_region *); -static void sel_set_lit(coordxy, coordxy, genericptr_t); -static void add_doors_to_room(struct mkroom *); -static void selection_iterate(struct selectionvar *, select_iter_func, - genericptr_t); -static void sel_set_ter(coordxy, coordxy, genericptr_t); -static void sel_set_door(coordxy, coordxy, genericptr_t); -static void sel_set_feature(coordxy, coordxy, genericptr_t); -static void levregion_add(lev_region *); -static void get_table_xy_or_coord(lua_State *, lua_Integer *, lua_Integer *); -static int get_table_region(lua_State *, const char *, lua_Integer *, +staticfn void l_get_lregion(lua_State *, lev_region *); +staticfn void sel_set_lit(coordxy, coordxy, genericptr_t); +staticfn void add_doors_to_room(struct mkroom *); +staticfn void get_table_coords_or_region(lua_State *, + coordxy *, coordxy *, coordxy *, coordxy *); +staticfn void sel_set_ter(coordxy, coordxy, genericptr_t); +staticfn void sel_set_door(coordxy, coordxy, genericptr_t); +staticfn void sel_set_feature(coordxy, coordxy, genericptr_t); +staticfn void levregion_add(lev_region *); +staticfn void get_table_xy_or_coord(lua_State *, lua_Integer *, + lua_Integer *) NONNULLPTRS; +staticfn int get_table_region(lua_State *, const char *, lua_Integer *, lua_Integer *, lua_Integer *, lua_Integer *, boolean); -static void set_wallprop_in_selection(lua_State *, int); -static coordxy random_wdir(void); -static int floodfillchk_match_under(coordxy, coordxy); -static int floodfillchk_match_accessible(coordxy, coordxy); -static boolean sel_flood_havepoint(coordxy, coordxy, coordxy *, coordxy *, int); -static long line_dist_coord(long, long, long, long, long, long); -static void l_push_mkroom_table(lua_State *, struct mkroom *); -static int get_table_align(lua_State *); -static int get_table_monclass(lua_State *); -static int get_table_montype(lua_State *, int *); -static lua_Integer get_table_int_or_random(lua_State *, const char *, int); -static int get_table_buc(lua_State *); -static int get_table_objclass(lua_State *); -static int find_objtype(lua_State *, const char *); -static int get_table_objtype(lua_State *); -static const char *get_mkroom_name(int); -static int get_table_roomtype_opt(lua_State *, const char *, int); -static int get_table_traptype_opt(lua_State *, const char *, int); -static int get_traptype_byname(const char *); -static lua_Integer get_table_intarray_entry(lua_State *, int, int); -static struct sp_coder *sp_level_coder_init(void); +staticfn void set_wallprop_in_selection(lua_State *, int); +staticfn int floodfillchk_match_under(coordxy, coordxy); +staticfn int floodfillchk_match_accessible(coordxy, coordxy); +staticfn void l_push_mkroom_table(lua_State *, struct mkroom *); +staticfn int get_table_align(lua_State *); +staticfn int get_table_monclass(lua_State *); +staticfn int find_montype(lua_State *, const char *, int *); +staticfn int get_table_montype(lua_State *, int *); +staticfn lua_Integer get_table_int_or_random(lua_State *, const char *, int); +staticfn int get_table_buc(lua_State *); +staticfn int get_table_objclass(lua_State *); +staticfn int find_objtype(lua_State *, const char *); +staticfn int get_table_objtype(lua_State *); +staticfn const char *get_mkroom_name(int) NONNULL; +staticfn int get_table_roomtype_opt(lua_State *, const char *, int); +staticfn int get_table_traptype_opt(lua_State *, const char *, int); +staticfn int get_traptype_byname(const char *); +staticfn lua_Integer get_table_intarray_entry(lua_State *, int, int); +staticfn struct sp_coder *sp_level_coder_init(void); /* lua_CFunction prototypes */ int lspo_altar(lua_State *); @@ -137,6 +141,7 @@ int lspo_ladder(lua_State *); int lspo_level_flags(lua_State *); int lspo_level_init(lua_State *); int lspo_levregion(lua_State *); +int lspo_exclusion(lua_State *); int lspo_map(lua_State *); int lspo_mazewalk(lua_State *); int lspo_message(lua_State *); @@ -154,6 +159,7 @@ int lspo_finalize_level(lua_State *); int lspo_room(lua_State *); int lspo_stair(lua_State *); int lspo_teleport_region(lua_State *); +int lspo_gas_cloud(lua_State *); int lspo_terrain(lua_State *); int lspo_trap(lua_State *); int lspo_wall_property(lua_State *); @@ -202,7 +208,7 @@ reset_xystart_size(void) /* Does typ match with levl[][].typ, considering special types MATCH_WALL and MAX_TYPE (aka transparency)? */ -static boolean +boolean match_maptyps(xint16 typ, xint16 levltyp) { if ((typ == MATCH_WALL) && !IS_STWALL(levltyp)) @@ -284,7 +290,7 @@ mapfrag_error(struct mapfragment *mf) } boolean -mapfrag_match(struct mapfragment* mf, int x, int y) +mapfrag_match(struct mapfragment *mf, int x, int y) { int rx, ry; @@ -300,7 +306,7 @@ mapfrag_match(struct mapfragment* mf, int x, int y) return TRUE; } -static void +staticfn void solidify_map(void) { coordxy x, y; @@ -332,7 +338,7 @@ lvlfill_maze_grid(int x1, int y1, int x2, int y2, schar filling) /* do a post-level-creation cleanup of map, such as removing boulders and traps from lava */ -static void +staticfn void map_cleanup(void) { struct obj *otmp; @@ -344,11 +350,11 @@ map_cleanup(void) for (y = 0; y < ROWNO; y++) { schar typ = levl[x][y].typ; - if (typ == LAVAPOOL || typ == LAVAWALL || IS_POOL(typ)) { + if (IS_LAVA(typ) || IS_POOL(typ)) { /* in case any boulders are on liquid, delete them */ while ((otmp = sobj_at(BOULDER, x, y)) != 0) { obj_extract_self(otmp); - obfree(otmp, (struct obj *)0); + obfree(otmp, (struct obj *) 0); } /* traps on liquid? */ @@ -380,7 +386,7 @@ lvlfill_solid(schar filling, schar lit) } } -static void +staticfn void lvlfill_swamp(schar fg, schar bg, schar lit) { int x, y; @@ -417,8 +423,8 @@ lvlfill_swamp(schar fg, schar bg, schar lit) } } -static void -flip_drawbridge_horizontal(struct rm *lev) +staticfn void +flip_dbridge_horizontal(struct rm *lev) { if (IS_DRAWBRIDGE(lev->typ)) { if ((lev->drawbridgemask & DB_DIR) == DB_WEST) { @@ -431,8 +437,8 @@ flip_drawbridge_horizontal(struct rm *lev) } } -static void -flip_drawbridge_vertical(struct rm *lev) +staticfn void +flip_dbridge_vertical(struct rm *lev) { if (IS_DRAWBRIDGE(lev->typ)) { if ((lev->drawbridgemask & DB_DIR) == DB_NORTH) { @@ -447,7 +453,7 @@ flip_drawbridge_vertical(struct rm *lev) /* for #wizfliplevel; not needed when flipping during level creation; update seen vector for whole flip area and glyph for known walls */ -static void +staticfn void flip_visuals(int flp, int minx, int miny, int maxx, int maxy) { struct rm *lev; @@ -487,10 +493,11 @@ flip_visuals(int flp, int minx, int miny, int maxx, int maxy) } } -static int -flip_encoded_direction_bits(int flp, int val) +/* transpose an encoded direction */ +staticfn int +flip_encoded_dir_bits(int flp, int val) { - /* These depend on xdir[] and ydir[] order */ + /* these depend on xdir[] and ydir[] order */ if (flp & 1) { val = swapbits(val, 1, 7); val = swapbits(val, 2, 6); @@ -538,6 +545,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) @@ -592,8 +600,7 @@ flip_level( ttmp->launch.y = FlipY(ttmp->launch.y); ttmp->launch2.y = FlipY(ttmp->launch2.y); } else if (is_pit(ttmp->ttyp) && ttmp->conjoined) { - ttmp->conjoined = flip_encoded_direction_bits(flp, - ttmp->conjoined); + ttmp->conjoined = flip_encoded_dir_bits(flp, ttmp->conjoined); } } if (flp & 2) { @@ -602,8 +609,7 @@ flip_level( ttmp->launch.x = FlipX(ttmp->launch.x); ttmp->launch2.x = FlipX(ttmp->launch2.x); } else if (is_pit(ttmp->ttyp) && ttmp->conjoined) { - ttmp->conjoined = flip_encoded_direction_bits(flp, - ttmp->conjoined); + ttmp->conjoined = flip_encoded_dir_bits(flp, ttmp->conjoined); } } } @@ -619,7 +625,7 @@ flip_level( } /* buried objects */ - for (otmp = gl.level.buriedobjlist; otmp; otmp = otmp->nobj) { + for (otmp = svl.level.buriedobjlist; otmp; otmp = otmp->nobj) { if (!inFlipArea(otmp->ox, otmp->oy)) continue; if (flp & 1) @@ -724,7 +730,7 @@ flip_level( } /* regions (poison clouds, etc) */ - for (i = 0; i < gn.n_regions; i++) { + for (i = 0; i < svn.n_regions; i++) { int j, tmp1, tmp2; if (flp & 1) { tmp1 = FlipY(gr.regions[i]->bounding_box.ly); @@ -753,7 +759,7 @@ flip_level( } /* rooms */ - for (sroom = &gr.rooms[0]; ; sroom++) { + for (sroom = &svr.rooms[0]; ; sroom++) { if (sroom->hx < 0) break; @@ -803,7 +809,7 @@ flip_level( /* doors */ for (i = 0; i < gd.doorindex; i++) { - Flip_coord(gd.doors[i]); + Flip_coord(svd.doors[i]); } /* the map */ @@ -812,20 +818,20 @@ flip_level( for (y = miny; y < (miny + ((maxy - miny + 1) / 2)); y++) { int ny = FlipY(y); - flip_drawbridge_vertical(&levl[x][y]); - flip_drawbridge_vertical(&levl[x][ny]); + flip_dbridge_vertical(&levl[x][y]); + flip_dbridge_vertical(&levl[x][ny]); trm = levl[x][y]; levl[x][y] = levl[x][ny]; levl[x][ny] = trm; - otmp = gl.level.objects[x][y]; - gl.level.objects[x][y] = gl.level.objects[x][ny]; - gl.level.objects[x][ny] = otmp; + otmp = svl.level.objects[x][y]; + svl.level.objects[x][y] = svl.level.objects[x][ny]; + svl.level.objects[x][ny] = otmp; - mtmp = gl.level.monsters[x][y]; - gl.level.monsters[x][y] = gl.level.monsters[x][ny]; - gl.level.monsters[x][ny] = mtmp; + mtmp = svl.level.monsters[x][y]; + svl.level.monsters[x][y] = svl.level.monsters[x][ny]; + svl.level.monsters[x][ny] = mtmp; } } if (flp & 2) { @@ -833,20 +839,20 @@ flip_level( for (y = miny; y <= maxy; y++) { int nx = FlipX(x); - flip_drawbridge_horizontal(&levl[x][y]); - flip_drawbridge_horizontal(&levl[nx][y]); + flip_dbridge_horizontal(&levl[x][y]); + flip_dbridge_horizontal(&levl[nx][y]); trm = levl[x][y]; levl[x][y] = levl[nx][y]; levl[nx][y] = trm; - otmp = gl.level.objects[x][y]; - gl.level.objects[x][y] = gl.level.objects[nx][y]; - gl.level.objects[nx][y] = otmp; + otmp = svl.level.objects[x][y]; + svl.level.objects[x][y] = svl.level.objects[nx][y]; + svl.level.objects[nx][y] = otmp; - mtmp = gl.level.monsters[x][y]; - gl.level.monsters[x][y] = gl.level.monsters[nx][y]; - gl.level.monsters[nx][y] = mtmp; + mtmp = svl.level.monsters[x][y]; + svl.level.monsters[x][y] = svl.level.monsters[nx][y]; + svl.level.monsters[nx][y] = mtmp; } } @@ -864,6 +870,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)) { @@ -878,7 +906,7 @@ flip_level( if (ball_active && !ball_fliparea) placebc(); Flip_coord(iflags.travelcc); - Flip_coord(gc.context.digging.pos); + Flip_coord(svc.context.digging.pos); } fix_wall_spines(1, 0, COLNO - 1, ROWNO - 1); @@ -891,7 +919,7 @@ flip_level( } /* for #wizfliplevel, flip guard's egd data; not needed for level creation */ -static void +staticfn void flip_vault_guard( int flp, /* 1: transpose vertically, 2: transpose horizontally, 3: both */ struct monst *grd, /* the vault guard, has monst->mextra->egd data */ @@ -951,10 +979,10 @@ flip_level_rnd(int flp, boolean extras) } -static void +staticfn void sel_set_wall_property(coordxy x, coordxy y, genericptr_t arg) { - int prop = *(int *)arg; + int prop = *(int *) arg; if (IS_STWALL(levl[x][y].typ) || IS_TREE(levl[x][y].typ) /* 3.6.2: made iron bars eligible to be flagged nondiggable @@ -966,10 +994,10 @@ sel_set_wall_property(coordxy x, coordxy y, genericptr_t arg) /* * Make walls of the area (x1, y1, x2, y2) non diggable/non passwall-able */ -static void +staticfn void set_wall_property(coordxy x1, coordxy y1, coordxy x2, coordxy y2, int prop) { - register coordxy x, y; + coordxy x, y; x1 = max(x1, 1); x2 = min(x2, COLNO - 1); @@ -977,30 +1005,11 @@ set_wall_property(coordxy x1, coordxy y1, coordxy x2, coordxy y2, int prop) y2 = min(y2, ROWNO - 1); for (y = y1; y <= y2; y++) for (x = x1; x <= x2; x++) { - sel_set_wall_property(x, y, (genericptr_t)&prop); - } -} - -/* - * Count the different features (sinks, fountains) in the level. - */ -static void -count_features(void) -{ - coordxy x, y; - - gl.level.flags.nfountains = gl.level.flags.nsinks = 0; - for (y = 0; y < ROWNO; y++) - for (x = 0; x < COLNO; x++) { - int typ = levl[x][y].typ; - if (typ == FOUNTAIN) - gl.level.flags.nfountains++; - else if (typ == SINK) - gl.level.flags.nsinks++; + sel_set_wall_property(x, y, (genericptr_t) &prop); } } -static void +staticfn void remove_boundary_syms(void) { /* @@ -1026,7 +1035,7 @@ remove_boundary_syms(void) } /* used by sel_set_door() and link_doors_rooms() */ -static void +staticfn void set_door_orientation(int x, int y) { boolean wleft, wright, wup, wdown; @@ -1073,10 +1082,10 @@ set_door_orientation(int x, int y) } /* is x,y right next to room droom? */ -static boolean +staticfn boolean shared_with_room(int x, int y, struct mkroom *droom) { - int rmno = (droom - gr.rooms) + ROOMOFFSET; + int rmno = (droom - svr.rooms) + ROOMOFFSET; if (!isok(x,y)) return FALSE; @@ -1094,19 +1103,19 @@ shared_with_room(int x, int y, struct mkroom *droom) } /* maybe add door at x,y to room droom */ -static void -maybe_add_door(int x, int y, struct mkroom* droom) +staticfn void +maybe_add_door(int x, int y, struct mkroom *droom) { if (droom->hx >= 0 && ((!droom->irregular && inside_room(droom, x, y)) - || (int) levl[x][y].roomno == (droom - gr.rooms) + ROOMOFFSET + || (int) levl[x][y].roomno == (droom - svr.rooms) + ROOMOFFSET || shared_with_room(x, y, droom))) { add_door(x, y, droom); } } /* link all doors in the map to their corresponding rooms */ -static void +staticfn void link_doors_rooms(void) { int x, y; @@ -1120,10 +1129,10 @@ link_doors_rooms(void) directive, set/clear levl[][].horizontal for it */ set_door_orientation(x, y); - for (tmpi = 0; tmpi < gn.nroom; tmpi++) { - maybe_add_door(x, y, &gr.rooms[tmpi]); - for (m = 0; m < gr.rooms[tmpi].nsubrooms; m++) { - maybe_add_door(x, y, gr.rooms[tmpi].sbrooms[m]); + for (tmpi = 0; tmpi < svn.nroom; tmpi++) { + maybe_add_door(x, y, &svr.rooms[tmpi]); + for (m = 0; m < svr.rooms[tmpi].nsubrooms; m++) { + maybe_add_door(x, y, svr.rooms[tmpi].sbrooms[m]); } } } @@ -1132,7 +1141,7 @@ link_doors_rooms(void) /* * Select a random trap */ -static int +staticfn int rndtrap(void) { int rtrap; @@ -1151,11 +1160,11 @@ rndtrap(void) break; case LEVEL_TELEP: case TELEP_TRAP: - if (gl.level.flags.noteleport) + if (svl.level.flags.noteleport) rtrap = NO_TRAP; break; case ROCKTRAP: - if (gl.level.flags.outdoors) + if (svl.level.flags.outdoors) rtrap = NO_TRAP; /* FALLTHRU */ case ROLLING_BOULDER_TRAP: @@ -1173,12 +1182,12 @@ rndtrap(void) * * If x or y is negative, we generate a random coordinate within the area. If * not negative, they are interpreted as relative to the last defined map or - * room, and are output as absolute gl.level.locations coordinates. + * room, and are output as absolute svl.level.locations coordinates. * * The "humidity" flag is used to ensure that engravings aren't created * underwater, or eels on dry land. */ -static void +staticfn void get_location( coordxy *x, coordxy *y, getloc_flags_t humidity, @@ -1217,7 +1226,7 @@ get_location( break; } while (++cpt < 100); if (cpt >= 100) { - register int xx, yy; + int xx, yy; /* last try */ for (xx = 0; xx < sx; xx++) @@ -1250,16 +1259,16 @@ get_location( static boolean (*is_ok_location_func)(coordxy, coordxy) = NULL; -static void +staticfn void set_ok_location_func(boolean (*func)(coordxy, coordxy)) { is_ok_location_func = func; } -static boolean +staticfn boolean is_ok_location(coordxy x, coordxy y, getloc_flags_t humidity) { - register int typ = levl[x][y].typ; + int typ = levl[x][y].typ; if (Is_waterlevel(&u.uz)) return TRUE; /* accept any spot */ @@ -1271,7 +1280,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 (is_open_air(x, y)) @@ -1291,12 +1300,12 @@ is_ok_location(coordxy x, coordxy y, getloc_flags_t humidity) } boolean -pm_good_location(coordxy x, coordxy y, struct permonst* pm) +pm_good_location(coordxy x, coordxy y, struct permonst *pm) { return is_ok_location(x, y, pm_to_humidity(pm)); } -static unpacked_coord +staticfn unpacked_coord get_unpacked_coord(long loc, int defhumidity) { static unpacked_coord c; @@ -1339,7 +1348,7 @@ get_location_coord( * Get a relative position inside a room. * negative values for x or y means RANDOM! */ -static void +staticfn void get_room_loc(coordxy *x, coordxy *y, struct mkroom *croom) { coord c; @@ -1364,14 +1373,14 @@ get_room_loc(coordxy *x, coordxy *y, struct mkroom *croom) * Get a relative position inside a room. * negative values for x or y means RANDOM! */ -static void +staticfn void get_free_room_loc( coordxy *x, coordxy *y, struct mkroom *croom, packed_coord pos) { coordxy try_x, try_y; - register int trycnt = 0; + int trycnt = 0; get_location_coord(&try_x, &try_y, DRY, croom, pos); if (levl[try_x][try_y].typ != ROOM) { @@ -1405,8 +1414,8 @@ check_room( coordxy *lowy, coordxy *ddy, boolean vault) { - register int x, y, hix = *lowx + *ddx, hiy = *lowy + *ddy; - register struct rm *lev; + int x, y, hix = *lowx + *ddx, hiy = *lowy + *ddy; + struct rm *lev; int xlim, ylim, ymax; coordxy s_lowx, s_ddx, s_lowy, s_ddy; @@ -1452,7 +1461,7 @@ check_room( return FALSE; if (gi.in_mk_themerooms) return FALSE; - if (gl.level.flags.is_maze_lev) + if (svl.level.flags.is_maze_lev) return FALSE; if (x < *lowx) *lowx = x + xlim + 1; @@ -1564,9 +1573,9 @@ create_room( hy = r1->hy; lx = r1->lx; ly = r1->ly; - if (vault) /* always 2x2 */ + if (vault) { /* always 2x2 */ dx = dy = 1; - else { + } else { /* if in a very wide rectangle, allow room width to vary from * 3 to 14, otherwise 3 to 10; * vary room height from 3 to 6. @@ -1629,10 +1638,11 @@ create_room( * more or less just a horizontal string of rooms, which * occasionally does happen under this algorithm. */ - if (ly == 0 && hy >= (ROWNO - 1) && (!gn.nroom || !rn2(gn.nroom)) + if (ly == 0 && hy >= ROWNO - 1 + && (!svn.nroom || !rn2(svn.nroom)) && (yabs + dy > ROWNO / 2)) { yabs = rn1(3, 2); - if (gn.nroom < 4 && dy > 1) + if (svn.nroom < 4 && dy > 1) dy--; } @@ -1723,7 +1733,7 @@ create_room( if (!vault) { /* set this room's id number to be unique for joining purposes */ - gs.smeq[gn.nroom] = gn.nroom; + gs.smeq[svn.nroom] = svn.nroom; /* actually add the room, setting the terrain properly */ add_room(xabs, yabs, xabs + wtmp - 1, yabs + htmp - 1, rlit, rtype, FALSE); @@ -1731,8 +1741,8 @@ create_room( /* vaults are isolated so don't get added to smeq; also apparently * don't have add_room() called on them. The lx and ly is set so that * makerooms() can store them in vault_x and vault_y. */ - gr.rooms[gn.nroom].lx = xabs; - gr.rooms[gn.nroom].ly = yabs; + svr.rooms[svn.nroom].lx = xabs; + svr.rooms[svn.nroom].ly = yabs; } return TRUE; } @@ -1741,7 +1751,7 @@ create_room( * Create a subroom in room proom at pos x,y with width w & height h. * x & y are relative to the parent room. */ -static boolean +staticfn boolean create_subroom( struct mkroom *proom, coordxy x, coordxy y, @@ -1787,7 +1797,7 @@ create_subroom( * Create a new door in a room. * It's placed on a wall (north, south, east or west). */ -static void +staticfn void create_door(room_door *dd, struct mkroom *broom) { int x = 0, y = 0; @@ -1807,7 +1817,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: @@ -1816,7 +1826,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: @@ -1825,7 +1835,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: @@ -1834,7 +1844,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: @@ -1858,12 +1868,12 @@ create_door(room_door *dd, struct mkroom *broom) /* * Create a trap in a room. */ -static void -create_trap(spltrap* t, struct mkroom* croom) +staticfn void +create_trap(spltrap *t, struct mkroom *croom) { coordxy x = -1, y = -1; coord tm; - int mktrap_flags = MKTRAP_MAZEFLAG; + unsigned mktrap_flags = MKTRAP_MAZEFLAG; if (croom) { get_free_room_loc(&x, &y, croom, t->coord); @@ -1891,7 +1901,7 @@ create_trap(spltrap* t, struct mkroom* croom) mktrap(t->type, mktrap_flags, (struct mkroom *) 0, &tm); } -static int +staticfn int noncoalignment(aligntyp alignment) { int k; @@ -1903,7 +1913,7 @@ noncoalignment(aligntyp alignment) } /* attempt to screen out locations where a mimic-as-boulder shouldn't occur */ -static boolean +staticfn boolean m_bad_boulder_spot(coordxy x, coordxy y) { struct rm *lev; @@ -1923,8 +1933,8 @@ m_bad_boulder_spot(coordxy x, coordxy y) return FALSE; } -static int -pm_to_humidity(struct permonst* pm) +staticfn int +pm_to_humidity(struct permonst *pm) { int loc = DRY; @@ -1947,7 +1957,7 @@ pm_to_humidity(struct permonst* pm) * * When random: there is an 80% chance that the altar will be co-aligned. */ -static unsigned int +staticfn unsigned int sp_amask_to_amask(unsigned int sp_amask) { unsigned int amask; @@ -1967,7 +1977,7 @@ sp_amask_to_amask(unsigned int sp_amask) /* * Create a monster in a room. */ -static void +staticfn void create_monster(monster* m, struct mkroom* croom) { struct monst *mtmp; @@ -1990,14 +2000,14 @@ create_monster(monster* m, struct mkroom* croom) amask = sp_amask_to_amask(m->sp_amask); - if (!class) + if (!class) { pm = (struct permonst *) 0; - else if (m->id != NON_PM) { + } else if (m->id != NON_PM) { pm = &mons[m->id]; - g_mvflags = (unsigned) gm.mvitals[monsndx(pm)].mvflags; + g_mvflags = (unsigned) svm.mvitals[monsndx(pm)].mvflags; if ((pm->geno & G_UNIQ) && (g_mvflags & G_EXTINCT)) return; - else if (g_mvflags & G_GONE) /* genocided or extinct */ + if (g_mvflags & G_GONE) /* genocided or extinct */ pm = (struct permonst *) 0; /* make random monster */ } else { if (m->sp_amask != AM_SPLEV_RANDOM) { @@ -2062,7 +2072,7 @@ create_monster(monster* m, struct mkroom* croom) if (m->appear_as.str && ((mtmp->data->mlet == S_MIMIC) /* shapechanger (chameleons, et al, and vampires) */ - || (mtmp->cham >= LOW_PM && m->appear == M_AP_MONSTER)) + || (ismnum(mtmp->cham) && m->appear == M_AP_MONSTER)) && !Protection_from_shape_changers) { int i; @@ -2219,16 +2229,27 @@ create_monster(monster* m, struct mkroom* croom) } if (m->waiting) { mtmp->mstrategy |= STRAT_WAITFORU; + /* if this is a vampire that got created already shifted into + bat/fog/wolf form and the special level or theme room didn't + explicitly request that, shift back to vampire */ + 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; } if (m->dead) { mondied(mtmp); /* kludge for this: it didn't actually die while the player was * around, so revert mondead() incrementing this */ - gm.mvitals[monsndx(mtmp->data)].died--; + svm.mvitals[monsndx(mtmp->data)].died--; } if (m->waiting) { mtmp->mstrategy |= STRAT_WAITFORU; @@ -2239,8 +2260,8 @@ create_monster(monster* m, struct mkroom* croom) /* * Create an object in a room. */ -static struct obj * -create_object(object* o, struct mkroom* croom) +staticfn struct obj * +create_object(object *o, struct mkroom *croom) { struct obj *otmp; coordxy x, y; @@ -2339,17 +2360,15 @@ create_object(object* o, struct mkroom* croom) otmp->obroken = 1; otmp->olocked = 0; /* obj generation may set */ } - if (o->trapped == 0 || o->trapped == 1) { - if (otmp->otyp == STATUE && o->trapped) - maketrap(x, y, STATUE_TRAP); - else - otmp->otrapped = o->trapped; - } - if (o->greased) - otmp->greased = 1; - else { - otmp->greased = 0; + if (o->trapped == 0 || o->trapped == 1) + otmp->otrapped = o->trapped; + if (o->trapped && (o->tknown == 0 || o->tknown == 1)) + otmp->tknown = o->tknown; + if (o->id == STATUE && o->trapped) { + otmp->otrapped = 0; + maketrap(x, y, STATUE_TRAP); } + otmp->greased = o->greased ? 1 : 0; if (o->material > 0) set_material(otmp, o->material); @@ -2451,19 +2470,21 @@ create_object(object* o, struct mkroom* croom) static const char prize_warning[] = "multiple prizes on %s level"; if (Is_mineend_level(&u.uz)) { - if (!gc.context.achieveo.mines_prize_oid) { - gc.context.achieveo.mines_prize_oid = otmp->o_id; - gc.context.achieveo.mines_prize_otyp = otmp->otyp; - /* prevent stacking; cleared when achievement is recorded */ + if (!svc.context.achieveo.mines_prize_oid) { + svc.context.achieveo.mines_prize_oid = otmp->o_id; + svc.context.achieveo.mines_prize_otyp = otmp->otyp; + /* prevent stacking; cleared when achievement is recorded; + will be reset in addinv_core1() */ otmp->nomerge = 1; } else { impossible(prize_warning, "mines end"); } } else if (Is_sokoend_level(&u.uz)) { - if (!gc.context.achieveo.soko_prize_oid) { - gc.context.achieveo.soko_prize_oid = otmp->o_id; - gc.context.achieveo.soko_prize_otyp = otmp->otyp; - otmp->nomerge = 1; /* redundant; Sokoban prizes don't stack */ + if (!svc.context.achieveo.soko_prize_oid) { + svc.context.achieveo.soko_prize_oid = otmp->o_id; + svc.context.achieveo.soko_prize_otyp = otmp->otyp; + otmp->nomerge = 1; /* redundant; Sokoban prizes don't stack; + * will be reset in addinv_core1() */ } else { impossible(prize_warning, "sokoban end"); } @@ -2499,8 +2520,8 @@ create_object(object* o, struct mkroom* croom) /* * Create an altar in a room. */ -static void -create_altar(altar* a, struct mkroom* croom) +staticfn void +create_altar(altar *a, struct mkroom *croom) { schar sproom; coordxy x = -1, y = -1; @@ -2514,7 +2535,7 @@ create_altar(altar* a, struct mkroom* croom) } else { get_location_coord(&x, &y, DRY, croom, a->coord); if ((sproom = (schar) *in_rooms(x, y, TEMPLE)) != 0) - croom = &gr.rooms[sproom - ROOMOFFSET]; + croom = &svr.rooms[sproom - ROOMOFFSET]; else croom_is_temple = FALSE; } @@ -2538,17 +2559,17 @@ create_altar(altar* a, struct mkroom* croom) levl[x][y].altarmask |= AM_SHRINE; if (a->shrine == 2) /* high altar or sanctum */ levl[x][y].altarmask |= AM_SANCTUM; - gl.level.flags.has_temple = TRUE; + svl.level.flags.has_temple = TRUE; } } /* * Search for a door in a room on a specified wall. */ -static boolean +staticfn boolean search_door( - struct mkroom* croom, - coordxy *x, coordxy * y, + struct mkroom *croom, + coordxy *x, coordxy *y, xint16 wall, int cnt) { int dx, dy; @@ -2598,15 +2619,15 @@ search_door( } /* - * Dig a corridor between two points. + * 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. */ boolean dig_corridor( - coord *org, - coord *dest, + coord *org, coord *dest, boolean nxcor, - schar ftyp, - schar btyp) + schar ftyp, schar btyp) { int dx = 0, dy = 0, dix, diy, cct; struct rm *crm; @@ -2667,7 +2688,7 @@ dig_corridor( /* do we have to change direction ? */ if (dy && dix > diy) { - register int ddx = (xx > tx) ? -1 : 1; + int ddx = (xx > tx) ? -1 : 1; crm = &levl[xx + ddx][yy]; if (crm->typ == btyp || crm->typ == ftyp || crm->typ == SCORR) { @@ -2676,7 +2697,7 @@ dig_corridor( continue; } } else if (dx && diy > dix) { - register int ddy = (yy > ty) ? -1 : 1; + int ddy = (yy > ty) ? -1 : 1; crm = &levl[xx][yy + ddy]; if (crm->typ == btyp || crm->typ == ftyp || crm->typ == SCORR) { @@ -2713,7 +2734,7 @@ dig_corridor( * Basically we search for door coordinates or for endpoints coordinates * (from a distance). */ -static void +staticfn void create_corridor(corridor *c) { coord org, dest; @@ -2723,19 +2744,19 @@ create_corridor(corridor *c) return; } - /* Safety railings - if there's ever a case where des.corridor() needs to be - * called with src/destwall="random", that logic first needs to be + /* Safety railings - if there's ever a case where des.corridor() needs + * to be called with src/destwall="random", that logic first needs to be * implemented in search_door. */ if (c->src.wall == W_ANY || c->src.wall == W_RANDOM || c->dest.wall == W_ANY || c->dest.wall == W_RANDOM) { impossible("create_corridor to/from a random wall"); return; } - if (!search_door(&gr.rooms[c->src.room], &org.x, &org.y, c->src.wall, + if (!search_door(&svr.rooms[c->src.room], &org.x, &org.y, c->src.wall, c->src.door)) return; if (c->dest.room != -1) { - if (!search_door(&gr.rooms[c->dest.room], + if (!search_door(&svr.rooms[c->dest.room], &dest.x, &dest.y, c->dest.wall, c->dest.door)) return; switch (c->src.wall) { @@ -2774,16 +2795,16 @@ create_corridor(corridor *c) * Fill a room (shop, zoo, etc...) with appropriate stuff. */ void -fill_special_room(struct mkroom* croom) +fill_special_room(struct mkroom *croom) { int i; if (!croom) return; - /* First recurse into subrooms. We don't want to block an ordinary room with - * a special subroom from having the subroom filled, or an unfilled outer - * room preventing a special subroom from being filled. */ + /* First recurse into subrooms. We don't want to block an ordinary room + * with a special subroom from having the subroom filled, or an unfilled + * outer room preventing a special subroom from being filled. */ for (i = 0; i < croom->nsubrooms; ++i) { fill_special_room(croom->sbrooms[i]); } @@ -2798,7 +2819,7 @@ fill_special_room(struct mkroom* croom) /* Shop ? */ if (croom->rtype >= SHOPBASE) { stock_room(croom->rtype - SHOPBASE, croom); - gl.level.flags.has_shop = TRUE; + svl.level.flags.has_shop = TRUE; return; } @@ -2826,34 +2847,34 @@ fill_special_room(struct mkroom* croom) } switch (croom->rtype) { case VAULT: - gl.level.flags.has_vault = TRUE; + svl.level.flags.has_vault = TRUE; break; case ZOO: - gl.level.flags.has_zoo = TRUE; + svl.level.flags.has_zoo = TRUE; break; case COURT: - gl.level.flags.has_court = TRUE; + svl.level.flags.has_court = TRUE; break; case MORGUE: - gl.level.flags.has_morgue = TRUE; + svl.level.flags.has_morgue = TRUE; break; case BEEHIVE: - gl.level.flags.has_beehive = TRUE; + svl.level.flags.has_beehive = TRUE; break; case BARRACKS: - gl.level.flags.has_barracks = TRUE; + svl.level.flags.has_barracks = TRUE; break; case TEMPLE: - gl.level.flags.has_temple = TRUE; + svl.level.flags.has_temple = TRUE; break; case SWAMP: - gl.level.flags.has_swamp = TRUE; + svl.level.flags.has_swamp = TRUE; break; } } -static struct mkroom * -build_room(room *r, struct mkroom* mkr) +staticfn struct mkroom * +build_room(room *r, struct mkroom *mkr) { boolean okroom; struct mkroom *aroom; @@ -2863,7 +2884,7 @@ build_room(room *r, struct mkroom* mkr) aroom = &gs.subrooms[gn.nsubroom]; okroom = create_subroom(mkr, r->x, r->y, r->w, r->h, rtype, r->rlit); } else { - aroom = &gr.rooms[gn.nroom]; + aroom = &svr.rooms[svn.nroom]; okroom = create_room(r->x, r->y, r->w, r->h, r->xalign, r->yalign, rtype, r->rlit); } @@ -2884,13 +2905,13 @@ build_room(room *r, struct mkroom* mkr) /* * set lighting in a region that will not become a room. */ -static void -light_region(region* tmpregion) +staticfn void +light_region(region *tmpregion) { - register boolean litstate = tmpregion->rlit ? 1 : 0; - register int hiy = tmpregion->y2; - register int x, y; - register struct rm *lev; + boolean litstate = tmpregion->rlit ? 1 : 0; + int hiy = tmpregion->y2; + int x, y; + struct rm *lev; int lowy = tmpregion->y1; int lowx = tmpregion->x1, hix = tmpregion->x2; @@ -2904,8 +2925,7 @@ light_region(region* tmpregion) for (x = lowx; x <= hix; x++) { lev = &levl[x][lowy]; for (y = lowy; y <= hiy; y++) { - if (lev->typ != LAVAPOOL) /* this overrides normal lighting */ - lev->lit = litstate; + lev->lit = IS_LAVA(lev->typ) ? 1 : litstate; lev++; } } @@ -2949,10 +2969,10 @@ wallify_map(coordxy x1, coordxy y1, coordxy x2, coordxy y2) * We want a place not 'touched' by the loader. That is, a place in * the maze outside every part of the special level. */ -static void +staticfn void maze1xy(coord *m, int humidity) { - register int x, y, tryct = 2000; + int x, y, tryct = 2000; /* tryct: normally it won't take more than ten or so tries due to the circumstances under which we'll be called, but the `humidity' screening might drastically change the chances */ @@ -2975,7 +2995,7 @@ maze1xy(coord *m, int humidity) * Makes the number of traps, monsters, etc. proportional * to the size of the maze. */ -static void +staticfn void fill_empty_maze(void) { int mapcountmax, mapcount, mapfact; @@ -2998,7 +3018,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--) { @@ -3028,8 +3053,8 @@ fill_empty_maze(void) } } -static void -splev_initlev(lev_init* linit) +staticfn void +splev_initlev(lev_init *linit) { switch (linit->init_style) { default: @@ -3076,7 +3101,7 @@ splev_initlev(lev_init* linit) } #if 0 -static long +staticfn long sp_code_jmpaddr(long curpos, long jmpaddr) { return (curpos + jmpaddr); @@ -3085,7 +3110,7 @@ sp_code_jmpaddr(long curpos, long jmpaddr) /*ARGUSED*/ -static void +staticfn void spo_end_moninvent(void) { if (invent_carrying_monster) { @@ -3103,7 +3128,7 @@ spo_end_moninvent(void) } /*ARGUSED*/ -static void +staticfn void spo_pop_container(void) { if (container_idx > 0) { @@ -3113,7 +3138,7 @@ spo_pop_container(void) } /* push a table on lua stack: {width=wid, height=hei} */ -static void +staticfn void l_push_wid_hei_table(lua_State *L, int wid, int hei) { lua_newtable(L); @@ -3122,7 +3147,7 @@ l_push_wid_hei_table(lua_State *L, int wid, int hei) } /* push a table on lua stack containing room data */ -static void +staticfn void l_push_mkroom_table(lua_State *L, struct mkroom *tmpr) { lua_newtable(L); @@ -3177,7 +3202,7 @@ lspo_message(lua_State *L) RESTORE_WARNING_UNREACHABLE_CODE -static int +staticfn int get_table_align(lua_State *L) { static const char *const gtaligns[] = { @@ -3194,7 +3219,7 @@ get_table_align(lua_State *L) return a; } -static int +staticfn int get_table_monclass(lua_State *L) { char *s = get_table_str_opt(L, "class", NULL); @@ -3230,7 +3255,7 @@ find_montype( return NON_PM; } -static int +staticfn int get_table_montype(lua_State *L, int *mgender) { char *s = get_table_str_opt(L, "id", NULL); @@ -3245,14 +3270,17 @@ get_table_montype(lua_State *L, int *mgender) return ret; } -/* Get x and y values from a table (which the caller has already checked for the - * existence of), handling both a table with x= and y= specified and a table - * with coord= specified. +/* Get x and y values from a table (which the caller has already checked for + * the existence of), handling both a table with x= and y= specified and a + * table with coord= specified. * Returns absolute rather than map-relative coordinates; the caller of this - * function must decide if it wants to interpret the coordinates as map-relative - * and adjust accordingly. */ -static void -get_table_xy_or_coord(lua_State *L, lua_Integer *x, lua_Integer *y) + * function must decide if it wants to interpret the coordinates as + * map-relative and adjust accordingly. */ +staticfn void +get_table_xy_or_coord( + lua_State *L, + lua_Integer *x, + lua_Integer *y) { lua_Integer mx = get_table_int_opt(L, "x", -1); lua_Integer my = get_table_int_opt(L, "y", -1); @@ -3302,7 +3330,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.dead = 0; tmpmons.waiting = 0; tmpmons.mm_flags = NO_MM_FLAGS; @@ -3351,6 +3379,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); @@ -3372,7 +3401,8 @@ lspo_monster(lua_State *L) tmpmons.waiting = get_table_boolean_opt(L, "waiting", FALSE); tmpmons.dead = get_table_boolean_opt(L, "dead", 0); 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; @@ -3421,7 +3451,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; } } @@ -3431,15 +3473,14 @@ lspo_monster(lua_State *L) tmpmons.coord = SP_COORD_PACK(mx, my); if (tmpmons.id != NON_PM && tmpmons.class == -1) - tmpmons.class = def_monsyms[(int) mons[tmpmons.id].mlet].sym; + tmpmons.class = monsym(&mons[tmpmons.id]); 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); - if (nhl_pcall(L, 0, 0)){ - impossible("Lua error: %s", lua_tostring(L, -1)); - } + nhl_pcall_handle(L, 0, 0, "lspo_monster", NHLpa_panic); spo_end_moninvent(); } else lua_pop(L, 1); @@ -3454,7 +3495,7 @@ DISABLE_WARNING_UNREACHABLE_CODE /* the hash key 'name' is an integer or "random", or if not existent, also return rndval */ -static lua_Integer +staticfn lua_Integer get_table_int_or_random(lua_State *L, const char *name, int rndval) { lua_Integer ret; @@ -3489,7 +3530,7 @@ get_table_int_or_random(lua_State *L, const char *name, int rndval) RESTORE_WARNING_UNREACHABLE_CODE -static int +staticfn int get_table_buc(lua_State *L) { static const char *const bucs[] = { @@ -3502,7 +3543,7 @@ get_table_buc(lua_State *L) return curse_state; } -static int +staticfn int get_table_objclass(lua_State *L) { char *s = get_table_str_opt(L, "class", NULL); @@ -3514,7 +3555,7 @@ get_table_objclass(lua_State *L) return ret; } -static int +staticfn int find_objtype(lua_State *L, const char *s) { if (s && *s) { @@ -3582,7 +3623,7 @@ find_objtype(lua_State *L, const char *s) return STRANGE_OBJECT; } -static int +staticfn int get_table_objtype(lua_State *L) { char *s = get_table_str_opt(L, "id", NULL); @@ -3613,8 +3654,9 @@ lspo_object(lua_State *L) 0, /* quan */ 0, /* buried */ 0, /* lit */ - 0, 0, 0, 0, 0, 0, 0, 0, /* eroded, locked, trapped, recharged, - invis, greased, broken, achievment */ + 0, 0, 0, 0, 0, /* eroded, locked, trapped, tknown, recharged */ + 0, 0, 0, 0, /* invis, greased, broken, achievement */ + 0, /* material */ }; #if 0 int nparams = 0; @@ -3633,6 +3675,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; @@ -3686,6 +3729,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); @@ -3788,9 +3832,7 @@ lspo_object(lua_State *L) if (lua_type(L, -1) == LUA_TFUNCTION) { lua_remove(L, -2); nhl_push_obj(L, otmp); - if (nhl_pcall(L, 1, 0)){ - impossible("Lua error: %s", lua_tostring(L, -1)); - } + nhl_pcall_handle(L, 1, 0, "lspo_object", NHLpa_panic); } else lua_pop(L, 1); @@ -3820,33 +3862,35 @@ lspo_level_flags(lua_State *L) const char *s = luaL_checkstring(L, i); if (!strcmpi(s, "noteleport")) - gl.level.flags.noteleport = 1; + svl.level.flags.noteleport = 1; else if (!strcmpi(s, "hardfloor")) - gl.level.flags.hardfloor = 1; + svl.level.flags.hardfloor = 1; else if (!strcmpi(s, "nommap")) - gl.level.flags.nommap = MAPPABLE_NEVER; + svl.level.flags.nommap = MAPPABLE_NEVER; else if (!strcmpi(s, "nommap-boss")) - gl.level.flags.nommap = MAPPABLE_BOSSBLOCKED; + svl.level.flags.nommap = MAPPABLE_BOSSBLOCKED; else if (!strcmpi(s, "shortsighted")) - gl.level.flags.shortsighted = 1; + svl.level.flags.shortsighted = 1; else if (!strcmpi(s, "arboreal")) - gl.level.flags.arboreal = 1; + svl.level.flags.arboreal = 1; else if (!strcmpi(s, "mazelevel")) - gl.level.flags.is_maze_lev = 1; + svl.level.flags.is_maze_lev = 1; else if (!strcmpi(s, "shroud")) - gl.level.flags.hero_memory = 1; + svl.level.flags.hero_memory = 1; else if (!strcmpi(s, "graveyard")) - gl.level.flags.graveyard = 1; + svl.level.flags.graveyard = 1; else if (!strcmpi(s, "icedpools")) icedpools = 1; else if (!strcmpi(s, "premapped")) gc.coder->premapped = 1; else if (!strcmpi(s, "solidify")) gc.coder->solidify = 1; + else if (!strcmpi(s, "sokoban")) + Sokoban = 1; /* svl.level.flags.sokoban_rules */ else if (!strcmpi(s, "inaccessibles")) gc.coder->check_inaccessibles = 1; else if (!strcmpi(s, "outdoors")) - gl.level.flags.outdoors = 1; + svl.level.flags.outdoors = 1; else if (!strcmpi(s, "noflipx")) gc.coder->allow_flips &= ~2; else if (!strcmpi(s, "noflipy")) @@ -3854,23 +3898,24 @@ lspo_level_flags(lua_State *L) else if (!strcmpi(s, "noflip")) gc.coder->allow_flips = 0; else if (!strcmpi(s, "temperate")) - gl.level.flags.temperature = 0; + svl.level.flags.temperature = 0; else if (!strcmpi(s, "hot")) - gl.level.flags.temperature = 1; + svl.level.flags.temperature = 1; else if (!strcmpi(s, "cold")) - gl.level.flags.temperature = -1; + svl.level.flags.temperature = -1; else if (!strcmpi(s, "nomongen")) - gl.level.flags.rndmongen = 0; + svl.level.flags.rndmongen = 0; else if (!strcmpi(s, "nodeathdrops")) - gl.level.flags.deathdrops = 0; + svl.level.flags.deathdrops = 0; else if (!strcmpi(s, "noautosearch")) - gl.level.flags.noautosearch = 1; + svl.level.flags.noautosearch = 1; else if (!strcmpi(s, "fumaroles")) - gl.level.flags.fumaroles = 1; + svl.level.flags.fumaroles = 1; else if (!strcmpi(s, "stormy")) - gl.level.flags.stormy = 1; + svl.level.flags.stormy = 1; else { char buf[BUFSZ]; + Sprintf(buf, "Unknown level flag %s", s); nhl_error(L, buf); } @@ -4055,7 +4100,7 @@ static const struct { { 0, 0 } }; -static const char * +staticfn const char * get_mkroom_name(int rtype) { int i; @@ -4063,10 +4108,12 @@ get_mkroom_name(int rtype) for (i = 0; room_types[i].name; i++) if (room_types[i].type == rtype) return room_types[i].name; - return NULL; + + impossible("get_mkroom_name unknown rtype %d", rtype); + return "unknown"; /* not NULL */ } -static int +staticfn int get_table_roomtype_opt(lua_State *L, const char *name, int defval) { char *roomstr = get_table_str_opt(L, name, emptystr); @@ -4085,9 +4132,11 @@ get_table_roomtype_opt(lua_State *L, const char *name, int defval) return res; } -/* room({ type="ordinary", lit=1, x=3,y=3, xalign="center",yalign="center", w=11,h=9 }); */ +/* room({ type="ordinary", lit=1, x=3,y=3, xalign="center",yalign="center", + * w=11,h=9 }); */ /* room({ lit=1, coord={3,3}, xalign="center",yalign="center", w=11,h=9 }); */ -/* room({ coord={3,3}, xalign="center",yalign="center", w=11,h=9, contents=function(room) ... end }); */ +/* room({ coord={3,3}, xalign="center",yalign="center", w=11,h=9, + * contents=function(room) ... end }); */ int lspo_room(lua_State *L) { @@ -4106,7 +4155,8 @@ lspo_room(lua_State *L) "none", "random", NULL }; static const int l_or_r2i[] = { - SPLEV_LEFT, SPLEV_H_LEFT, SPLEV_CENTER, SPLEV_H_RIGHT, SPLEV_RIGHT, -1, -1, -1 + SPLEV_LEFT, SPLEV_H_LEFT, SPLEV_CENTER, SPLEV_H_RIGHT, + SPLEV_RIGHT, -1, -1, -1 }; static const char *const top_or_bot[] = { "top", "center", "bottom", "none", "random", NULL @@ -4142,20 +4192,20 @@ lspo_room(lua_State *L) if (!gc.coder->failed_room[gc.coder->n_subroom - 1]) { tmpcr = build_room(&tmproom, gc.coder->croom); if (tmpcr) { - gc.coder->tmproomlist[gc.coder->n_subroom] = tmpcr; - gc.coder->failed_room[gc.coder->n_subroom] = FALSE; + int n = gc.coder->n_subroom; + + gc.coder->tmproomlist[n] = tmpcr; /* TRUE to get here... */ + gc.coder->failed_room[n] = FALSE; /* added a subroom, make parent room irregular */ - if (gc.coder->tmproomlist[gc.coder->n_subroom - 1]) - gc.coder->tmproomlist[gc.coder->n_subroom - 1]->irregular = TRUE; + if (gc.coder->tmproomlist[n - 1]) + gc.coder->tmproomlist[n - 1]->irregular = TRUE; gc.coder->n_subroom++; update_croom(); lua_getfield(L, 1, "contents"); if (lua_type(L, -1) == LUA_TFUNCTION) { lua_remove(L, -2); l_push_mkroom_table(L, tmpcr); - if (nhl_pcall(L, 1, 0)){ - impossible("Lua error: %s", lua_tostring(L, -1)); - } + nhl_pcall_handle(L, 1, 0, "lspo_room", NHLpa_panic); } else lua_pop(L, 1); spo_endroom(gc.coder); @@ -4177,8 +4227,8 @@ lspo_room(lua_State *L) return 0; } -static void -spo_endroom(struct sp_coder* coder UNUSED) +staticfn void +spo_endroom(struct sp_coder *coder UNUSED) { if (gc.coder->n_subroom > 1) { gc.coder->n_subroom--; @@ -4197,7 +4247,7 @@ spo_endroom(struct sp_coder* coder UNUSED) /* callback for is_ok_location. stairs generated at random location shouldn't overwrite special terrain */ -static boolean +staticfn boolean good_stair_loc(coordxy x, coordxy y) { schar typ = levl[x][y].typ; @@ -4205,7 +4255,7 @@ good_stair_loc(coordxy x, coordxy y) return (typ == ROOM || typ == CORR || typ == ICE); } -static int +staticfn int l_create_stairway(lua_State *L, boolean using_ladder) { static const char *const stairdirs[] = { "down", "up", NULL }; @@ -4409,7 +4459,7 @@ static const struct { { "random", -1 }, { 0, NO_TRAP } }; -static int +staticfn int get_table_traptype_opt(lua_State *L, const char *name, int defval) { char *trapstr = get_table_str_opt(L, name, emptystr); @@ -4438,7 +4488,7 @@ get_trapname_bytype(int ttyp) return NULL; } -static int +staticfn int get_traptype_byname(const char *trapname) { int i; @@ -4503,17 +4553,19 @@ lspo_trap(lua_State *L) lua_pop(L, 1); gl.launchplace.x = lx; gl.launchplace.y = ly; - } - lua_pop(L, 1); + } else + lua_pop(L, 1); + lua_getfield(L, -1, "teledest"); if (lua_type(L, -1) == LUA_TTABLE) { lua_Integer lx = -1, ly = -1; (void) get_coord(L, -1, &lx, &ly); + lua_pop(L, 1); gl.launchplace.x = lx; gl.launchplace.y = ly; - } - lua_pop(L, 1); + } else + lua_pop(L, 1); } if (tmptrap.type == NO_TRAP) @@ -4584,7 +4636,8 @@ lspo_gold(lua_State *L) RESTORE_WARNING_UNREACHABLE_CODE -/* corridor({ srcroom=1, srcdoor=2, srcwall="north", destroom=2, destdoor=1, destwall="west" });*/ +/* corridor({ srcroom=1, srcdoor=2, srcwall="north", + * destroom=2, destdoor=1, destwall="west" }); */ int lspo_corridor(lua_State *L) { @@ -4605,7 +4658,8 @@ lspo_corridor(lua_State *L) tc.src.wall = walldirs2i[get_table_option(L, "srcwall", "all", walldirs)]; tc.dest.room = get_table_int(L, "destroom"); tc.dest.door = get_table_int(L, "destdoor"); - tc.dest.wall = walldirs2i[get_table_option(L, "destwall", "all", walldirs)]; + tc.dest.wall = walldirs2i[get_table_option(L, "destwall", "all", + walldirs)]; create_corridor(&tc); @@ -4632,364 +4686,17 @@ lspo_random_corridors(lua_State *L UNUSED) return 0; } -/* selection */ -struct selectionvar * -selection_new(void) -{ - struct selectionvar *tmps = (struct selectionvar *) alloc(sizeof *tmps); - - tmps->wid = COLNO; - tmps->hei = ROWNO; - tmps->bounds_dirty = FALSE; - tmps->bounds.lx = COLNO; - tmps->bounds.ly = ROWNO; - tmps->bounds.hx = tmps->bounds.hy = 0; - tmps->map = (char *) alloc((COLNO * ROWNO) + 1); - (void) memset(tmps->map, 1, (COLNO * ROWNO)); - tmps->map[(COLNO * ROWNO)] = '\0'; - - return tmps; -} - -void -selection_free(struct selectionvar* sel, boolean freesel) -{ - if (sel) { - Free(sel->map); - sel->map = NULL; - if (freesel) - free((genericptr_t) sel); - else - (void) memset((genericptr_t) sel, 0, sizeof *sel); - } -} - -/* clear selection, setting all locations to value val */ -void -selection_clear(struct selectionvar *sel, int val) -{ - (void) memset(sel->map, 1 + val, (COLNO * ROWNO)); - if (val) { - sel->bounds.lx = 0; - sel->bounds.ly = 0; - sel->bounds.hx = COLNO - 1; - sel->bounds.hy = ROWNO - 1; - } else { - sel->bounds.lx = COLNO; - sel->bounds.ly = ROWNO; - sel->bounds.hx = sel->bounds.hy = 0; - } - sel->bounds_dirty = FALSE; -} - -struct selectionvar * -selection_clone(struct selectionvar* sel) -{ - struct selectionvar *tmps = (struct selectionvar *) alloc(sizeof *tmps); - - *tmps = *sel; - tmps->map = dupstr(sel->map); - - return tmps; -} - -/* get boundary rect of selection sel into b */ -void -selection_getbounds(struct selectionvar *sel, NhRect *b) -{ - if (!sel || !b) - return; - - selection_recalc_bounds(sel); - - if (sel->bounds.lx >= sel->wid) { - b->lx = 0; - b->ly = 0; - b->hx = COLNO - 1; - b->hy = ROWNO - 1; - } else { - b->lx = sel->bounds.lx; - b->ly = sel->bounds.ly; - b->hx = sel->bounds.hx; - b->hy = sel->bounds.hy; - } -} - -/* recalc the boundary of selection, if necessary */ -void -selection_recalc_bounds(struct selectionvar *sel) -{ - coordxy x, y; - NhRect r; - - if (!sel->bounds_dirty) - return; - - sel->bounds.lx = COLNO; - sel->bounds.ly = ROWNO; - sel->bounds.hx = sel->bounds.hy = 0; - - r.lx = r.ly = r.hx = r.hy = -1; - - /* left */ - for (x = 0; x < sel->wid; x++) { - for (y = 0; y < sel->hei; y++) { - if (selection_getpoint(x, y, sel)) { - r.lx = x; - break; - } - } - if (r.lx > -1) - break; - } - - if (r.lx > -1) { - /* right */ - for (x = sel->wid-1; x >= r.lx; x--) { - for (y = 0; y < sel->hei; y++) { - if (selection_getpoint(x, y, sel)) { - r.hx = x; - break; - } - } - if (r.hx > -1) - break; - } - - /* top */ - for (y = 0; y < sel->hei; y++) { - for (x = r.lx; x <= r.hx; x++) { - if (selection_getpoint(x, y, sel)) { - r.ly = y; - break; - } - } - if (r.ly > -1) - break; - } - - /* bottom */ - for (y = sel->hei-1; y >= r.ly; y--) { - for (x = r.lx; x <= r.hx; x++) { - if (selection_getpoint(x, y, sel)) { - r.hy = y; - break; - } - } - if (r.hy > -1) - break; - } - sel->bounds = r; - } - - sel->bounds_dirty = FALSE; -} - -coordxy -selection_getpoint(coordxy x, coordxy y, struct selectionvar* sel) -{ - if (!sel || !sel->map) - return 0; - if (x < 0 || y < 0 || x >= sel->wid || y >= sel->hei) - return 0; - - return (sel->map[sel->wid * y + x] - 1); -} - -void -selection_setpoint(coordxy x, coordxy y, struct selectionvar* sel, int c) -{ - if (!sel || !sel->map) - return; - if (x < 0 || y < 0 || x >= sel->wid || y >= sel->hei) - return; - - if (c && !sel->bounds_dirty) { - if (sel->bounds.lx > x) sel->bounds.lx = x; - if (sel->bounds.ly > y) sel->bounds.ly = y; - if (sel->bounds.hx < x) sel->bounds.hx = x; - if (sel->bounds.hy < y) sel->bounds.hy = y; - } else { - sel->bounds_dirty = TRUE; - } - - sel->map[sel->wid * y + x] = (char) (c + 1); -} - -struct selectionvar * -selection_not(struct selectionvar* s) -{ - int x, y; - NhRect tmprect; - - for (x = 0; x < s->wid; x++) - for (y = 0; y < s->hei; y++) - selection_setpoint(x, y, s, selection_getpoint(x, y, s) ? 0 : 1); - selection_getbounds(s, &tmprect); - return s; -} - -struct selectionvar * -selection_filter_mapchar(struct selectionvar* ov, xint16 typ, int lit) -{ - int x, y; - struct selectionvar *ret; - NhRect rect; - - if (!ov) - return NULL; - - ret = selection_new(); - - selection_getbounds(ov, &rect); - - for (x = rect.lx; x <= rect.hx; x++) - for (y = rect.ly; y <= rect.hy; y++) - if (selection_getpoint(x, y, ov) - && match_maptyps(typ, levl[x][y].typ)) { - switch (lit) { - default: - case -2: - selection_setpoint(x, y, ret, 1); - break; - case -1: - selection_setpoint(x, y, ret, rn2(2)); - break; - case 0: - case 1: - if (levl[x][y].lit == (unsigned int) lit) - selection_setpoint(x, y, ret, 1); - break; - } - } - return ret; -} - -struct selectionvar * -selection_filter_percent(struct selectionvar* ov, int percent) -{ - int x, y; - struct selectionvar *ret; - NhRect rect; - - if (!ov) - return NULL; - - ret = selection_new(); - - selection_getbounds(ov, &rect); - - for (x = rect.lx; x <= rect.hx; x++) - for (y = rect.ly; y <= rect.hy; y++) - if (selection_getpoint(x, y, ov) && (rn2(100) < percent)) - selection_setpoint(x, y, ret, 1); - - return ret; -} - -int -selection_rndcoord(struct selectionvar* ov, coordxy *x, coordxy *y, boolean removeit) -{ - int idx = 0; - int c; - int dx, dy; - NhRect rect; - - selection_getbounds(ov, &rect); - - for (dx = rect.lx; dx <= rect.hx; dx++) - for (dy = rect.ly; dy <= rect.hy; dy++) - if (selection_getpoint(dx, dy, ov)) - idx++; - - if (idx) { - c = rn2(idx); - for (dx = rect.lx; dx <= rect.hx; dx++) - for (dy = rect.ly; dy <= rect.hy; dy++) - if (selection_getpoint(dx, dy, ov)) { - if (!c) { - *x = dx; - *y = dy; - if (removeit) - selection_setpoint(dx, dy, ov, 0); - return 1; - } - c--; - } - } - *x = *y = -1; - return 0; -} - /* Choose a single random W_* direction. */ -static coordxy +coordxy random_wdir(void) { static const coordxy wdirs[4] = { W_NORTH, W_SOUTH, W_EAST, W_WEST }; return wdirs[rn2(4)]; } -void -selection_do_grow(struct selectionvar* ov, int dir) -{ - coordxy x, y; - struct selectionvar *tmp; - NhRect rect; - - if (!ov) - return; - - tmp = selection_new(); - - if (dir == W_RANDOM) - dir = random_wdir(); - - selection_getbounds(ov, &rect); - - for (x = max(0, rect.lx-1); x <= min(COLNO-1, rect.hx+1); x++) - for (y = max(0, rect.ly-1); y <= min(ROWNO-1, rect.hy+1); y++) { - /* note: dir is a mask of multiple directions, but the only - way to specify diagonals is by including the two adjacent - orthogonal directions, which effectively specifies three- - way growth [WEST|NORTH => WEST plus WEST|NORTH plus NORTH] */ - if (((dir & W_WEST) && selection_getpoint(x + 1, y, ov)) - /* Note: extra parens around these operands are critical, due to - * == having precedence over & and |. */ - || (((dir & (W_WEST | W_NORTH)) == (W_WEST | W_NORTH)) - && selection_getpoint(x + 1, y + 1, ov)) - || ((dir & W_NORTH) && selection_getpoint(x, y + 1, ov)) - || (((dir & (W_NORTH | W_EAST)) == (W_NORTH | W_EAST)) - && selection_getpoint(x - 1, y + 1, ov)) - || ((dir & W_EAST) && selection_getpoint(x - 1, y, ov)) - || (((dir & (W_EAST | W_SOUTH)) == (W_EAST | W_SOUTH)) - && selection_getpoint(x - 1, y - 1, ov)) - || ((dir & W_SOUTH) && selection_getpoint(x, y - 1, ov)) - || (((dir & (W_SOUTH | W_WEST)) == (W_SOUTH | W_WEST)) - && selection_getpoint(x + 1, y - 1, ov))) { - selection_setpoint(x, y, tmp, 1); - } - } - - selection_getbounds(tmp, &rect); - - for (x = rect.lx; x <= rect.hx; x++) - for (y = rect.ly; y <= rect.hy; y++) - if (selection_getpoint(x, y, tmp)) - selection_setpoint(x, y, ov, 1); - - selection_free(tmp, TRUE); -} - -static int (*selection_flood_check_func)(coordxy, coordxy); static schar floodfillchk_match_under_typ; -void -set_selection_floodfillchk(int (*f)(coordxy, coordxy)) -{ - selection_flood_check_func = f; -} - -static int +staticfn int floodfillchk_match_under(coordxy x, coordxy y) { return (floodfillchk_match_under_typ == levl[x][y].typ); @@ -5002,7 +4709,7 @@ set_floodfillchk_match_under(coordxy typ) set_selection_floodfillchk(floodfillchk_match_under); } -static int +staticfn int floodfillchk_match_accessible(coordxy x, coordxy y) { return (ACCESSIBLE(levl[x][y].typ) @@ -5010,371 +4717,8 @@ floodfillchk_match_accessible(coordxy x, coordxy y) || levl[x][y].typ == SCORR); } -/* check whethere is already in xs[],ys[] */ -static boolean -sel_flood_havepoint(coordxy x, coordxy y, coordxy xs[], coordxy ys[], int n) -{ - coordxy xx = x, yy = y; - - while (n > 0) { - --n; - if (xs[n] == xx && ys[n] == yy) - return TRUE; - } - return FALSE; -} - -void -selection_floodfill(struct selectionvar* ov, coordxy x, coordxy y, boolean diagonals) -{ - struct selectionvar *tmp = selection_new(); -#define SEL_FLOOD_STACK (COLNO * ROWNO) -#define SEL_FLOOD(nx, ny) \ - do { \ - if (idx < SEL_FLOOD_STACK) { \ - dx[idx] = (nx); \ - dy[idx] = (ny); \ - idx++; \ - } else \ - panic(floodfill_stack_overrun); \ - } while (0) -#define SEL_FLOOD_CHKDIR(mx, my, sel) \ - do { \ - if (isok((mx), (my)) \ - && (*selection_flood_check_func)((mx), (my)) \ - && !selection_getpoint((mx), (my), (sel)) \ - && !sel_flood_havepoint((mx), (my), dx, dy, idx)) \ - SEL_FLOOD((mx), (my)); \ - } while (0) - static const char floodfill_stack_overrun[] = "floodfill stack overrun"; - int idx = 0; - coordxy dx[SEL_FLOOD_STACK]; - coordxy dy[SEL_FLOOD_STACK]; - - if (selection_flood_check_func == (int (*)(coordxy, coordxy)) 0) { - selection_free(tmp, TRUE); - return; - } - if (!isok(x, y)) { - return; - } - SEL_FLOOD(x, y); - do { - idx--; - x = dx[idx]; - y = dy[idx]; - if (isok(x, y)) { - selection_setpoint(x, y, ov, 1); - selection_setpoint(x, y, tmp, 1); - } - SEL_FLOOD_CHKDIR((x + 1), y, tmp); - SEL_FLOOD_CHKDIR((x - 1), y, tmp); - SEL_FLOOD_CHKDIR(x, (y + 1), tmp); - SEL_FLOOD_CHKDIR(x, (y - 1), tmp); - if (diagonals) { - SEL_FLOOD_CHKDIR((x + 1), (y + 1), tmp); - SEL_FLOOD_CHKDIR((x - 1), (y - 1), tmp); - SEL_FLOOD_CHKDIR((x - 1), (y + 1), tmp); - SEL_FLOOD_CHKDIR((x + 1), (y - 1), tmp); - } - } while (idx > 0); -#undef SEL_FLOOD -#undef SEL_FLOOD_STACK -#undef SEL_FLOOD_CHKDIR - selection_free(tmp, TRUE); -} - -/* McIlroy's Ellipse Algorithm */ -void -selection_do_ellipse( - struct selectionvar *ov, - int xc, int yc, - int a, int b, - int filled) -{ /* e(x,y) = b^2*x^2 + a^2*y^2 - a^2*b^2 */ - int x = 0, y = b; - long a2 = (long) a * a, b2 = (long) b * b; - long crit1 = -(a2 / 4 + a % 2 + b2); - long crit2 = -(b2 / 4 + b % 2 + a2); - long crit3 = -(b2 / 4 + b % 2); - long t = -a2 * y; /* e(x+1/2,y-1/2) - (a^2+b^2)/4 */ - long dxt = 2 * b2 * x, dyt = -2 * a2 * y; - long d2xt = 2 * b2, d2yt = 2 * a2; - long width = 1; - long i; - - if (!ov) - return; - - filled = !filled; - - if (!filled) { - while (y >= 0 && x <= a) { - selection_setpoint(xc + x, yc + y, ov, 1); - if (x != 0 || y != 0) - selection_setpoint(xc - x, yc - y, ov, 1); - if (x != 0 && y != 0) { - selection_setpoint(xc + x, yc - y, ov, 1); - selection_setpoint(xc - x, yc + y, ov, 1); - } - if (t + b2 * x <= crit1 /* e(x+1,y-1/2) <= 0 */ - || t + a2 * y <= crit3) { /* e(x+1/2,y) <= 0 */ - x++; - dxt += d2xt; - t += dxt; - } else if (t - a2 * y > crit2) { /* e(x+1/2,y-1) > 0 */ - y--; - dyt += d2yt; - t += dyt; - } else { - x++; - dxt += d2xt; - t += dxt; - y--; - dyt += d2yt; - t += dyt; - } - } - } else { - while (y >= 0 && x <= a) { - if (t + b2 * x <= crit1 /* e(x+1,y-1/2) <= 0 */ - || t + a2 * y <= crit3) { /* e(x+1/2,y) <= 0 */ - x++; - dxt += d2xt; - t += dxt; - width += 2; - } else if (t - a2 * y > crit2) { /* e(x+1/2,y-1) > 0 */ - for (i = 0; i < width; i++) - selection_setpoint(xc - x + i, yc - y, ov, 1); - if (y != 0) - for (i = 0; i < width; i++) - selection_setpoint(xc - x + i, yc + y, ov, 1); - y--; - dyt += d2yt; - t += dyt; - } else { - for (i = 0; i < width; i++) - selection_setpoint(xc - x + i, yc - y, ov, 1); - if (y != 0) - for (i = 0; i < width; i++) - selection_setpoint(xc - x + i, yc + y, ov, 1); - x++; - dxt += d2xt; - t += dxt; - y--; - dyt += d2yt; - t += dyt; - width += 2; - } - } - } -} - -/* square of distance from line segment (x1,y1, x2,y2) to point (x3,y3) */ -static long -line_dist_coord(long x1, long y1, long x2, long y2, long x3, long y3) -{ - long px = x2 - x1; - long py = y2 - y1; - long s = px * px + py * py; - long x, y, dx, dy, distsq = 0; - float lu = 0; - - if (x1 == x2 && y1 == y2) - return dist2(x1, y1, x3, y3); - - lu = ((x3 - x1) * px + (y3 - y1) * py) / (float) s; - if (lu > 1) - lu = 1; - else if (lu < 0) - lu = 0; - - x = x1 + lu * px; - y = y1 + lu * py; - dx = x - x3; - dy = y - y3; - distsq = dx * dx + dy * dy; - - return distsq; -} - -/* guts of l_selection_gradient */ -void -selection_do_gradient( - struct selectionvar *ov, - long x, long y, - long x2,long y2, - long gtyp, - long mind, long maxd) -{ - long dx, dy, dofs; - - if (mind > maxd) { - long tmp = mind; - mind = maxd; - maxd = tmp; - } - - dofs = maxd * maxd - mind * mind; - if (dofs < 1) - dofs = 1; - - switch (gtyp) { - default: - impossible("Unrecognized gradient type! Defaulting to radial..."); - /* FALLTHRU */ - case SEL_GRADIENT_RADIAL: { - for (dx = 0; dx < COLNO; dx++) - for (dy = 0; dy < ROWNO; dy++) { - long d0 = line_dist_coord(x, y, x2, y2, dx, dy); - if (d0 <= mind * mind - || (d0 <= maxd * maxd && d0 - mind * mind < rn2(dofs))) - selection_setpoint(dx, dy, ov, 1); - } - break; - } - case SEL_GRADIENT_SQUARE: { - for (dx = 0; dx < COLNO; dx++) - for (dy = 0; dy < ROWNO; dy++) { - long d1 = line_dist_coord(x, y, x2, y2, x, dy); - long d2 = line_dist_coord(x, y, x2, y2, dx, y); - long d3 = line_dist_coord(x, y, x2, y2, x2, dy); - long d4 = line_dist_coord(x, y, x2, y2, dx, y2); - long d5 = line_dist_coord(x, y, x2, y2, dx, dy); - long d0 = min(d5, min(max(d1, d2), max(d3, d4))); - - if (d0 <= mind * mind - || (d0 <= maxd * maxd && d0 - mind * mind < rn2(dofs))) - selection_setpoint(dx, dy, ov, 1); - } - break; - } /*case*/ - } /*switch*/ -} - -/* bresenham line algo */ -void -selection_do_line( - coordxy x1, coordxy y1, - coordxy x2, coordxy y2, - struct selectionvar *ov) -{ - int d0, dx, dy, ai, bi, xi, yi; - - if (x1 < x2) { - xi = 1; - dx = x2 - x1; - } else { - xi = -1; - dx = x1 - x2; - } - if (y1 < y2) { - yi = 1; - dy = y2 - y1; - } else { - yi = -1; - dy = y1 - y2; - } - - selection_setpoint(x1, y1, ov, 1); - - if (!dx && !dy) { - /* single point - already all done */ - ; - } else if (dx > dy) { - ai = (dy - dx) * 2; - bi = dy * 2; - d0 = bi - dx; - do { - if (d0 >= 0) { - y1 += yi; - d0 += ai; - } else - d0 += bi; - x1 += xi; - selection_setpoint(x1, y1, ov, 1); - } while (x1 != x2); - } else { - ai = (dx - dy) * 2; - bi = dx * 2; - d0 = bi - dy; - do { - if (d0 >= 0) { - x1 += xi; - d0 += ai; - } else - d0 += bi; - y1 += yi; - selection_setpoint(x1, y1, ov, 1); - } while (y1 != y2); - } -} - -void -selection_do_randline( - coordxy x1, coordxy y1, - coordxy x2, coordxy y2, - schar rough, - schar rec, - struct selectionvar *ov) -{ - int mx, my; - int dx, dy; - - if (rec < 1 || (x2 == x1 && y2 == y1)) - return; - - if (rough > max(abs(x2 - x1), abs(y2 - y1))) - rough = max(abs(x2 - x1), abs(y2 - y1)); - - if (rough < 2) { - mx = ((x1 + x2) / 2); - my = ((y1 + y2) / 2); - } else { - do { - dx = rn2(rough) - (rough / 2); - dy = rn2(rough) - (rough / 2); - mx = ((x1 + x2) / 2) + dx; - my = ((y1 + y2) / 2) + dy; - } while ((mx > COLNO - 1 || mx < 0 || my < 0 || my > ROWNO - 1)); - } - - if (!selection_getpoint(mx, my, ov)) { - selection_setpoint(mx, my, ov, 1); - } - - rough = (rough * 2) / 3; - - rec--; - - selection_do_randline(x1, y1, mx, my, rough, rec, ov); - selection_do_randline(mx, my, x2, y2, rough, rec, ov); - - selection_setpoint(x2, y2, ov, 1); -} - -static void -selection_iterate( - struct selectionvar *ov, - select_iter_func func, - genericptr_t arg) -{ - coordxy x, y; - NhRect rect; - - if (!ov) - return; - - selection_getbounds(ov, &rect); - - for (x = rect.lx; x <= rect.hx; x++) - for (y = rect.ly; y <= rect.hy; y++) - if (isok(x,y) && selection_getpoint(x, y, ov)) - (*func)(x, y, arg); -} - /* change map location terrain type during level creation */ -static void +staticfn void sel_set_ter(coordxy x, coordxy y, genericptr_t arg) { terrain terr; @@ -5389,14 +4733,16 @@ sel_set_ter(coordxy x, coordxy y, genericptr_t arg) set_doorstate(&levl[x][y], D_CLOSED); if (x && (IS_WALL(levl[x - 1][y].typ) || levl[x - 1][y].horizontal)) levl[x][y].horizontal = 1; - } else if (levl[x][y].typ == HWALL - || levl[x][y].typ == IRONBARS) + } else if (levl[x][y].typ == HWALL || levl[x][y].typ == IRONBARS) { levl[x][y].horizontal = 1; - else if (splev_init_present && levl[x][y].typ == ICE) + } else if (splev_init_present && levl[x][y].typ == ICE) { levl[x][y].icedpool = icedpools ? ICED_POOL : ICED_MOAT; + } else if (levl[x][y].typ == CLOUD) { + del_engr_at(x, y); /* clouds cannot have engravings */ + } } -static void +staticfn void sel_set_feature(coordxy x, coordxy y, genericptr_t arg) { if (!isok(x, y)) { @@ -5410,7 +4756,7 @@ sel_set_feature(coordxy x, coordxy y, genericptr_t arg) levl[x][y].typ = (*(int *) arg); } -static void +staticfn void sel_set_door(coordxy dx, coordxy dy, genericptr_t arg) { coordxy typ = *(coordxy *) arg; @@ -5429,6 +4775,8 @@ sel_set_door(coordxy dx, coordxy dy, genericptr_t arg) SpLev_Map[x][y] = 1; } +DISABLE_WARNING_UNREACHABLE_CODE + /* door({ x = 1, y = 1, state = "nodoor" }); */ /* door({ coord = {1, 1}, state = "nodoor" }); */ /* door({ wall = "north", pos = 3, state="secret", locked = 1, iron = 1 }); */ @@ -5559,6 +4907,7 @@ lspo_door(lua_State *L) SP_COORD_PACK(x, y)); if (!isok(x, y)) { nhl_error(L, "door coord not ok"); + /*NOTREACHED*/ return 0; } sel_set_door(x, y, (genericptr_t) &msk); @@ -5568,7 +4917,9 @@ lspo_door(lua_State *L) } #undef UNSPECIFIED -static void +RESTORE_WARNING_UNREACHABLE_CODE + +staticfn void l_table_getset_feature_flag( lua_State *L, int x, int y, @@ -5587,8 +4938,8 @@ l_table_getset_feature_flag( } } -/* guts of nhl_abs_coord; convert a coordinate relative to a map or room into an - * absolute coordinate in gl.level.locations. +/* guts of nhl_abs_coord; convert a coordinate relative to a map or room + * into an absolute coordinate in svl.level.locations. * * If there is no enclosing map or room, the coordinates are assumed to be * absolute already. @@ -5599,29 +4950,28 @@ l_table_getset_feature_flag( * NOTE: if the coordinates are going to get passed to one of the get_location * family of functions, this should NOT be called; get_location already makes * an adjustment like this. (What this function supports which get_location - * doesn't is the input coordinates being negative. get_location will treat that - * as "level designer wants a random coordinate".) */ + * doesn't is the input coordinates being negative. get_location will treat + * that as "level designer wants a random coordinate".) */ void cvt_to_abscoord(coordxy *x, coordxy *y) { /* since commit 99715e0, xstart and ystart are only relevant in mklev when * maps are being used, and 0 otherwise. It is possible in the future that - * map positions and dimensions can be saved and retrieved outside of mklev - * which would reintroduce nonzero xstart/ystart/xsiz/ysiz, but this is not - * currently implemented, so this function can be assumed to have no effect - * outside of mklev. + * map positions and dimensions can be saved and retrieved outside of + * mklev which would reintroduce nonzero xstart/ystart/xsiz/ysiz, but + * this is not currently implemented, so this function can be assumed to + * have no effect outside of mklev. */ if (gc.coder && gc.coder->croom) { *x += gc.coder->croom->lx; *y += gc.coder->croom->ly; - } - else { + } else { *x += gx.xstart; *y += gy.ystart; } } -/* inverse of cvt_to_abscoord; turn an absolute gl.level.locations coordinate +/* inverse of cvt_to_abscoord; turn an absolute svl.level.locations coordinate * into one relative to the current map or room. */ void cvt_to_relcoord(coordxy *x, coordxy *y) @@ -5629,8 +4979,7 @@ cvt_to_relcoord(coordxy *x, coordxy *y) if (gc.coder && gc.coder->croom) { *x -= gc.coder->croom->lx; *y -= gc.coder->croom->ly; - } - else { + } else { *x -= gx.xstart; *y -= gy.ystart; } @@ -5770,7 +5119,48 @@ lspo_feature(lua_State *L) return 0; } -RESTORE_WARNING_UNREACHABLE_CODE +/* gas_cloud({ selection=SELECTION }); */ +/* gas_cloud({ selection=SELECTION, damage=N }); */ +/* gas_cloud({ selection=SELECTION, damage=N, ttl=N }); */ +int +lspo_gas_cloud(lua_State *L) +{ + coordxy x = 0, y = 0; + struct selectionvar *sel = NULL; + int argc = lua_gettop(L); + int damage = 0; + int ttl = -2; + + create_des_coder(); + + if (argc == 1 && lua_type(L, 1) == LUA_TTABLE) { + lua_Integer tx, ty; + NhRegion *reg; + + lcheck_param_table(L); + + get_table_xy_or_coord(L, &tx, &ty); + x = tx, y = ty; + if (tx == -1 && ty == -1) { + lua_getfield(L, 1, "selection"); + sel = l_selection_check(L, -1); + lua_pop(L, 1); + } + damage = get_table_int_opt(L, "damage", 0); + ttl = get_table_int_opt(L, "ttl", -2); + if (!sel) { + reg = create_gas_cloud(x, y, 1, damage); + } else + reg = create_gas_cloud_selection(sel, damage); + if (ttl > -2) + reg->ttl = ttl; + } else { + nhl_error(L, "wrong parameters"); + } + + return 0; +} + /* * [lit_state: 1 On, 0 Off, -1 random, -2 leave as-is] @@ -5835,6 +5225,7 @@ lspo_terrain(lua_State *L) SP_COORD_PACK(x, y)); if (!isok(x, y)) { nhl_error(L, "terrain coord not ok"); + /*NOTREACHED*/ return 0; } sel_set_ter(x, y, (genericptr_t) &tmpterrain); @@ -5864,7 +5255,7 @@ lspo_replace_terrain(lua_State *L) lua_Integer x1, y1, x2, y2; int chance; int tolit; - NhRect rect; + NhRect rect = cg.zeroNhRect; create_des_coder(); @@ -5948,7 +5339,7 @@ lspo_replace_terrain(lua_State *L) return 0; } -static boolean +staticfn boolean generate_way_out_method( coordxy nx, coordxy ny, struct selectionvar *ov) @@ -6008,7 +5399,7 @@ generate_way_out_method( /* generate one of the escape items */ if (selection_rndcoord(ov2, &x, &y, FALSE)) { - mksobj_at(escapeitems[rn2(SIZE(escapeitems))], x, y, TRUE, FALSE); + mksobj_at(ROLL_FROM(escapeitems), x, y, TRUE, FALSE); goto gotitdone; } @@ -6019,7 +5410,7 @@ generate_way_out_method( return res; } -static void +staticfn void ensure_way_out(void) { struct selectionvar *ov = selection_new(); @@ -6062,7 +5453,7 @@ ensure_way_out(void) DISABLE_WARNING_UNREACHABLE_CODE -static lua_Integer +staticfn lua_Integer get_table_intarray_entry(lua_State *L, int tableidx, int entrynum) { lua_Integer ret = 0; @@ -6084,7 +5475,7 @@ get_table_intarray_entry(lua_State *L, int tableidx, int entrynum) return ret; } -static int +staticfn int get_table_region( lua_State *L, const char *name, @@ -6173,8 +5564,8 @@ get_coord(lua_State *L, int i, lua_Integer *x, lua_Integer *y) RESTORE_WARNING_UNREACHABLE_CODE -static void -levregion_add(lev_region* lregion) +staticfn void +levregion_add(lev_region *lregion) { if (!lregion->in_islev) { get_location(&lregion->inarea.x1, &lregion->inarea.y1, ANY_LOC, @@ -6212,7 +5603,7 @@ levregion_add(lev_region* lregion) - exclude = {x1,y1,x2,y2} (optional) - region_islev=true, exclude_islev=true (optional) - negative x and y are invalid */ -static void +staticfn void l_get_lregion(lua_State *L, lev_region *tmplregion) { lua_Integer x1,y1,x2,y2; @@ -6233,17 +5624,18 @@ l_get_lregion(lua_State *L, lev_region *tmplregion) tmplregion->in_islev = get_table_boolean_opt(L, "region_islev", 0); tmplregion->del_islev = get_table_boolean_opt(L, "exclude_islev", 0); - /* if x1 is still negative, exclude wasn't specified, so we should treat it - * as if there is no exclude region at all. Force exclude_islev to true so - * the -1,-1,-1,-1 region is safely off the map and won't interfere with - * branch or portal placement. */ + /* if x1 is still negative, exclude wasn't specified, so we should treat + * it as if there is no exclude region at all. Force exclude_islev to + * true so the -1,-1,-1,-1 region is safely off the map and won't + * interfere with branch or portal placement. */ if (x1 < 0) tmplregion->del_islev = TRUE; } -/* teleport_region({ region = { x1,y1, x2,y2} }); */ -/* teleport_region({ region = { x1,y1, x2,y2}, [ region_islev = 1, ] exclude = { x1,y1, x2,y2}, [ exclude_islen = 1, ] [ dir = "up" ] }); */ -/* TODO: maybe allow using selection, with a new selection method "getextents()"? */ +/* teleport_region({ region = { x1,y1, x2,y2 } }); */ +/* teleport_region({ region = { x1,y1, x2,y2 }, [ region_islev = 1, ] + * exclude = { x1,y1, x2,y2 }, [ exclude_islen = 1, ] [ dir = "up" ] }); */ +/* TODO: maybe allow using selection, with a new method "getextents()"? */ int lspo_teleport_region(lua_State *L) { @@ -6269,9 +5661,9 @@ lspo_teleport_region(lua_State *L) - STAIR:(x1,y1,x2,y2),(x1,y1,x2,y2),dir - PORTAL:(x1,y1,x2,y2),(x1,y1,x2,y2),string - BRANCH:(x1,y1,x2,y2),(x1,y1,x2,y2),dir - */ -/* levregion({ region = { x1,y1, x2,y2 }, exclude = { x1,y1, x2,y2 }, type = "portal", name="air" }); */ +/* levregion({ region = { x1,y1, x2,y2 }, exclude = { x1,y1, x2,y2 }, + * type = "portal", name="air" }); */ /* TODO: allow region to be optional, defaulting to whole level */ int lspo_levregion(lua_State *L) @@ -6298,16 +5690,54 @@ lspo_levregion(lua_State *L) return 0; } -static void +/* exclusion({ type = "teleport", region = { x1,y1, x2,y2 } }); */ +int +lspo_exclusion(lua_State *L) +{ + static const char *const ez_types[] = { + "teleport", "teleport-up", "teleport-down", "monster-generation", NULL + }; + static const int ez_types2i[] = { + 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); + + 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; +} + +staticfn void sel_set_lit(coordxy x, coordxy y, genericptr_t arg) { - int lit = *(int *)arg; + int lit = *(int *) arg; - levl[x][y].lit = (levl[x][y].typ == LAVAPOOL) ? 1 : lit; + levl[x][y].lit = (IS_LAVA(levl[x][y].typ) || lit) ? 1 : 0; } /* Add to the room any doors within/bordering it */ -static void +staticfn void add_doors_to_room(struct mkroom *croom) { coordxy x, y; @@ -6323,15 +5753,35 @@ add_doors_to_room(struct mkroom *croom) DISABLE_WARNING_UNREACHABLE_CODE +/* inside a lua table, get fields x1,y1,x2,y2 or region table */ +staticfn void +get_table_coords_or_region(lua_State *L, + coordxy *dx1, coordxy *dy1, + coordxy *dx2, coordxy *dy2) +{ + *dx1 = get_table_int_opt(L, "x1", -1); + *dy1 = get_table_int_opt(L, "y1", -1); + *dx2 = get_table_int_opt(L, "x2", -1); + *dy2 = get_table_int_opt(L, "y2", -1); + + if (*dx1 == -1 && *dy1 == -1 && *dx2 == -1 && *dy2 == -1) { + lua_Integer rx1, ry1, rx2, ry2; + + get_table_region(L, "region", &rx1, &ry1, &rx2, &ry2, FALSE); + *dx1 = rx1; *dy1 = ry1; + *dx2 = rx2; *dy2 = ry2; + } +} + /* region(selection, lit); */ /* region({ x1=NN, y1=NN, x2=NN, y2=NN, lit=BOOL, type=ROOMTYPE, joined=BOOL, - irregular=BOOL, filled=NN [ , contents = FUNCTION ] }); */ + * irregular=BOOL, filled=NN [ , contents = FUNCTION ] }); */ /* region({ region={x1,y1, x2,y2}, type="ordinary" }); */ int lspo_region(lua_State *L) { coordxy dx1, dy1, dx2, dy2; - register struct mkroom *troom; + struct mkroom *troom; boolean do_arrival_room = FALSE, room_not_needed, irregular = FALSE, joined = TRUE; int rtype = OROOM, rlit = 1, needfill = 0; @@ -6342,25 +5792,17 @@ lspo_region(lua_State *L) if (argc <= 1) { lcheck_param_table(L); - /* TODO: "unfilled" ==> filled=0, "filled" ==> filled=1, and - * "lvflags_only" ==> filled=2, probably in a get_table_needfill_opt */ + /* TODO: "unfilled" => filled=0, "filled" => filled=1, and + * "lvflags_only" => filled=2, probably in a get_table_needfill_opt */ needfill = get_table_int_opt(L, "filled", 0); irregular = get_table_boolean_opt(L, "irregular", 0); joined = get_table_boolean_opt(L, "joined", TRUE); do_arrival_room = get_table_boolean_opt(L, "arrival_room", 0); rtype = get_table_roomtype_opt(L, "type", OROOM); rlit = get_table_int_opt(L, "lit", -1); - dx1 = get_table_int_opt(L, "x1", -1); /* TODO: area */ - dy1 = get_table_int_opt(L, "y1", -1); - dx2 = get_table_int_opt(L, "x2", -1); - dy2 = get_table_int_opt(L, "y2", -1); - if (dx1 == -1 && dy1 == -1 && dx2 == -1 && dy2 == -1) { - lua_Integer rx1, ry1, rx2, ry2; - get_table_region(L, "region", &rx1, &ry1, &rx2, &ry2, FALSE); - dx1 = rx1; dy1 = ry1; - dx2 = rx2; dy2 = ry2; - } + get_table_coords_or_region(L, &dx1, &dy1, &dx2, &dy2); + if (dx1 == -1 && dy1 == -1 && dx2 == -1 && dy2 == -1) { nhl_error(L, "region needs region"); } @@ -6380,6 +5822,8 @@ lspo_region(lua_State *L) selection_do_grow(sel, W_ANY); selection_iterate(sel, sel_set_lit, (genericptr_t) &rlit); + selection_free(sel, TRUE); + /* TODO: skip the rest of this function? */ return 0; } else { @@ -6399,12 +5843,12 @@ lspo_region(lua_State *L) * - Special rooms (which usually need to be filled). * - Irregular regions (more convenient to use the room-making code). * - Themed room regions (which often have contents). - * - When a room is desired to constrain the arrival of migrating monsters - * (see the mon_arrive function for details). + * - When a room is desired to constrain the arrival of migrating + * monsters (see the mon_arrive function for details). */ room_not_needed = (rtype == OROOM && !irregular && !do_arrival_room && !gi.in_mk_themerooms); - if (room_not_needed || gn.nroom >= MAXNROFROOMS) { + if (room_not_needed || svn.nroom >= MAXNROFROOMS) { region tmpregion; if (!room_not_needed) impossible("Too many rooms on new level!"); @@ -6418,7 +5862,7 @@ lspo_region(lua_State *L) return 0; } - troom = &gr.rooms[gn.nroom]; + troom = &svr.rooms[svn.nroom]; /* mark rooms that must be filled, but do it later */ troom->needfill = needfill; @@ -6428,9 +5872,10 @@ lspo_region(lua_State *L) if (irregular) { gm.min_rx = gm.max_rx = dx1; gm.min_ry = gm.max_ry = dy1; - gs.smeq[gn.nroom] = gn.nroom; - flood_fill_rm(dx1, dy1, gn.nroom + ROOMOFFSET, rlit, TRUE); - add_room(gm.min_rx, gm.min_ry, gm.max_rx, gm.max_ry, FALSE, rtype, TRUE); + gs.smeq[svn.nroom] = svn.nroom; + flood_fill_rm(dx1, dy1, svn.nroom + ROOMOFFSET, rlit, TRUE); + add_room(gm.min_rx, gm.min_ry, gm.max_rx, gm.max_ry, FALSE, rtype, + TRUE); troom->rlit = rlit; troom->irregular = TRUE; } else { @@ -6443,9 +5888,9 @@ lspo_region(lua_State *L) } if (!room_not_needed) { - if (gc.coder->n_subroom > 1) + if (gc.coder->n_subroom > 1) { impossible("region as subroom"); - else { + } else { gc.coder->tmproomlist[gc.coder->n_subroom] = troom; gc.coder->failed_room[gc.coder->n_subroom] = FALSE; gc.coder->n_subroom++; @@ -6454,11 +5899,10 @@ lspo_region(lua_State *L) if (lua_type(L, -1) == LUA_TFUNCTION) { lua_remove(L, -2); l_push_mkroom_table(L, troom); - if (nhl_pcall(L, 1, 0)){ - impossible("Lua error: %s", lua_tostring(L, -1)); - } - } else + nhl_pcall_handle(L, 1, 0, "lspo_region", NHLpa_panic); + } else { lua_pop(L, 1); + } spo_endroom(gc.coder); add_doors_to_room(troom); } @@ -6467,8 +5911,6 @@ lspo_region(lua_State *L) return 0; } -RESTORE_WARNING_UNREACHABLE_CODE - /* drawbridge({ dir="east", state="closed", x=05,y=08 }); */ /* drawbridge({ dir="east", state="closed", coord={05,08} }); */ int @@ -6505,6 +5947,7 @@ lspo_drawbridge(lua_State *L) get_location_coord(&x, &y, DRY | WET | HOT, gc.coder->croom, dcoord); if (!isok(mx, my)) { nhl_error(L, "drawbridge coord not ok"); + /*NOTREACHED*/ return 0; } if (db_open == -1) @@ -6558,6 +6001,7 @@ lspo_mazewalk(lua_State *L) if (!isok(x, y)) { nhl_error(L, "mazewalk coord not ok"); + /*NOTREACHED*/ return 0; } @@ -6621,12 +6065,16 @@ lspo_mazewalk(lua_State *L) return 0; } +RESTORE_WARNING_UNREACHABLE_CODE + /* wall_property({ x1=0, y1=0, x2=78, y2=20, property="nondiggable" }); */ /* wall_property({ region = {1,0, 78,20}, property="nonpasswall" }); */ int lspo_wall_property(lua_State *L) { - static const char *const wprops[] = { "nondiggable", "nonpasswall", NULL }; + static const char *const wprops[] = { + "nondiggable", "nonpasswall", NULL + }; static const int wprop2i[] = { W_NONDIGGABLE, W_NONPASSWALL, -1 }; coordxy dx1 = -1, dy1 = -1, dx2 = -1, dy2 = -1; int wprop; @@ -6635,17 +6083,7 @@ lspo_wall_property(lua_State *L) lcheck_param_table(L); - dx1 = get_table_int_opt(L, "x1", -1); - dy1 = get_table_int_opt(L, "y1", -1); - dx2 = get_table_int_opt(L, "x2", -1); - dy2 = get_table_int_opt(L, "y2", -1); - - if (dx1 == -1 && dy1 == -1 && dx2 == -1 && dy2 == -1) { - lua_Integer rx1, ry1, rx2, ry2; - get_table_region(L, "region", &rx1, &ry1, &rx2, &ry2, FALSE); - dx1 = rx1; dy1 = ry1; - dx2 = rx2; dy2 = ry2; - } + get_table_coords_or_region(L, &dx1, &dy1, &dx2, &dy2); wprop = wprop2i[get_table_option(L, "property", "nondiggable", wprops)]; @@ -6666,7 +6104,7 @@ lspo_wall_property(lua_State *L) return 0; } -static void +staticfn void set_wallprop_in_selection(lua_State *L, int prop) { int argc = lua_gettop(L); @@ -6710,7 +6148,7 @@ lspo_non_passwall(lua_State *L) #if 0 /*ARGSUSED*/ -static void +staticfn void sel_set_wallify(coordxy x, coordxy y, genericptr_t arg UNUSED) { wallify_map(x, y, x, y); @@ -6749,7 +6187,7 @@ lspo_wallify(lua_State *L) /* reset_level is only needed for testing purposes */ int -lspo_reset_level(lua_State *L UNUSED) +lspo_reset_level(lua_State *L) { iflags.lua_testing = TRUE; if (L) @@ -6763,7 +6201,7 @@ lspo_reset_level(lua_State *L UNUSED) /* finalize_level is only needed for testing purposes */ int -lspo_finalize_level(lua_State *L UNUSED) +lspo_finalize_level(lua_State *L) { int i; @@ -6783,22 +6221,22 @@ lspo_finalize_level(lua_State *L UNUSED) if (L) flip_level_rnd(gc.coder->allow_flips, FALSE); - count_features(); + count_level_features(); if (L && gc.coder->solidify) solidify_map(); - /* This must be done before sokoban_detect(), + /* This must be done before premap_detect(), * otherwise branch stairs won't be premapped. */ fixup_special(); if (L && gc.coder->premapped) - sokoban_detect(); + premap_detect(); level_finalize_topology(); - for (i = 0; i < gn.nroom; ++i) { - fill_special_room(&gr.rooms[i]); + for (i = 0; i < svn.nroom; ++i) { + fill_special_room(&svr.rooms[i]); } makemap_prepost(FALSE); @@ -6827,7 +6265,8 @@ TODO: gc.coder->croom needs to be updated "left", "half-left", "center", "half-right", "right", "none", NULL }; static const int l_or_r2i[] = { - SPLEV_LEFT, SPLEV_H_LEFT, SPLEV_CENTER, SPLEV_H_RIGHT, SPLEV_RIGHT, -1, -1 + SPLEV_LEFT, SPLEV_H_LEFT, SPLEV_CENTER, SPLEV_H_RIGHT, + SPLEV_RIGHT, -1, -1 }; static const char *const top_or_bot[] = { "top", "center", "bottom", "none", NULL @@ -7051,9 +6490,7 @@ TODO: gc.coder->croom needs to be updated } else if (has_contents) { l_push_wid_hei_table(L, gx.xsize, gy.ysize); - if (nhl_pcall(L, 1, 0)){ - impossible("Lua error: %s", lua_tostring(L, -1)); - } + nhl_pcall_handle(L, 1, 0, "lspo_map", NHLpa_panic); reset_xystart_size(); } @@ -7066,27 +6503,6 @@ TODO: gc.coder->croom needs to be updated RESTORE_WARNING_UNREACHABLE_CODE -struct selectionvar * -selection_from_mkroom(struct mkroom *croom) -{ - struct selectionvar *sel = selection_new(); - coordxy x, y; - unsigned rmno; - - if (!croom && gc.coder && gc.coder->croom) - croom = gc.coder->croom; - if (!croom) - return sel; - - rmno = (unsigned)((croom - gr.rooms) + ROOMOFFSET); - for (y = croom->ly; y <= croom->hy; y++) - for (x = croom->lx; x <= croom->hx; x++) - if (isok(x, y) && !levl[x][y].edge - && levl[x][y].roomno == rmno) - selection_setpoint(x, y, sel, 1); - return sel; -} - void update_croom(void) { @@ -7099,7 +6515,7 @@ update_croom(void) gc.coder->croom = NULL; } -static struct sp_coder * +staticfn struct sp_coder * sp_level_coder_init(void) { int tmpi; @@ -7132,10 +6548,10 @@ sp_level_coder_init(void) (void) memset((genericptr_t) SpLev_Map, 0, sizeof SpLev_Map); - gl.level.flags.is_maze_lev = 0; - gl.level.flags.temperature = In_hell(&u.uz) ? 1 : 0; - gl.level.flags.rndmongen = 1; - gl.level.flags.deathdrops = 1; + svl.level.flags.is_maze_lev = 0; + svl.level.flags.temperature = In_hell(&u.uz) ? 1 : 0; + svl.level.flags.rndmongen = 1; + svl.level.flags.deathdrops = 1; reset_xystart_size(); @@ -7169,6 +6585,7 @@ static const struct luaL_Reg nhl_functions[] = { { "drawbridge", lspo_drawbridge }, { "region", lspo_region }, { "levregion", lspo_levregion }, + { "exclusion", lspo_exclusion }, { "wallify", lspo_wallify }, { "wall_property", lspo_wall_property }, { "non_diggable", lspo_non_diggable }, @@ -7176,6 +6593,7 @@ static const struct luaL_Reg nhl_functions[] = { { "teleport_region", lspo_teleport_region }, { "reset_level", lspo_reset_level }, { "finalize_level", lspo_finalize_level }, + { "gas_cloud", lspo_gas_cloud }, /* TODO: { "branch", lspo_branch }, */ /* TODO: { "portal", lspo_portal }, */ { NULL, NULL } @@ -7192,7 +6610,7 @@ static const struct luaL_Reg nhl_functions[] = { - automatically add shuffle(array) - automatically add align = { "law", "neutral", "chaos" } and shuffle it. (remove from lua files) - - grab the header comments from des-files and add add them to the lua files + - grab the header comments from des-files and add them to the lua files */ @@ -7219,7 +6637,7 @@ boolean load_special(const char *name) { boolean result = FALSE; - nhl_sandbox_info sbi = {NHL_SB_SAFE, 0, 0, 0}; + nhl_sandbox_info sbi = {NHL_SB_SAFE, 1*1024*1024, 0, 1*1024*1024}; create_des_coder(); @@ -7234,21 +6652,22 @@ load_special(const char *name) ensure_way_out(); map_cleanup(); + wallification(1, 0, COLNO - 1, ROWNO - 1); flip_level_rnd(gc.coder->allow_flips, FALSE); - count_features(); + count_level_features(); if (gc.coder->solidify) solidify_map(); - /* This must be done before sokoban_detect(), + /* This must be done before premap_detect(), * otherwise branch stairs won't be premapped. */ fixup_special(); if (gc.coder->premapped) - sokoban_detect(); + premap_detect(); result = TRUE; give_up: diff --git a/src/spell.c b/src/spell.c index e2f5161ce6..2ad7cc7ccc 100644 --- a/src/spell.c +++ b/src/spell.c @@ -1,10 +1,10 @@ -/* NetHack 3.7 spell.c $NHDT-Date: 1646838390 2022/03/09 15:06:30 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.131 $ */ +/* NetHack 3.7 spell.c $NHDT-Date: 1725227807 2024/09/01 21:56:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.173 $ */ /* Copyright (c) M. Stephenson 1988 */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" -/* spellmenu arguments; 0 thru n-1 used as gs.spl_book[] index when swapping */ +/* spellmenu arguments; 0..n-1 used as svs.spl_book[] index when swapping */ #define SPELLMENU_CAST (-2) #define SPELLMENU_VIEW (-1) #define SPELLMENU_SORT (MAXSPELL) /* special menu entry */ @@ -18,33 +18,43 @@ initialization; spell memory is decremented at the end of each turn, including the turn on which the spellbook is read; without the extra increment, the hero used to get cheated out of 1 turn of retention */ -#define incrnknow(spell, x) (gs.spl_book[spell].sp_know = KEEN + (x)) +#define incrnknow(spell, x) (svs.spl_book[spell].sp_know = KEEN + (x)) -#define spellev(spell) gs.spl_book[spell].sp_lev +#define spellev(spell) svs.spl_book[spell].sp_lev #define spellname(spell) OBJ_NAME(objects[spellid(spell)]) #define spellet(spell) \ ((char) ((spell < 26) ? ('a' + spell) : ('A' + spell - 26))) -static int spell_let_to_idx(char); -static boolean cursed_book(struct obj * bp); -static boolean confused_book(struct obj *); -static void deadbook(struct obj *); -static int learn(void); -static boolean rejectcasting(void); -static boolean getspell(int *); -static int QSORTCALLBACK spell_cmp(const genericptr, const genericptr); -static void sortspells(void); -static boolean spellsortmenu(void); -static boolean dospellmenu(const char *, int, int *); -static int percent_success(int); -static int energy_cost(int); -static int throwspell(void); -static void cast_protection(void); -static void spell_backfire(int); -static int spell_hunger(int); -static boolean spelleffects_check(int, int *, int *); -static boolean can_center_spell_location(coordxy, coordxy); -static boolean spell_aim_step(genericptr_t, coordxy, coordxy); +struct chain_lightning_queue; +struct chain_lightning_zap; + +staticfn int spell_let_to_idx(char); +staticfn boolean cursed_book(struct obj * bp); +staticfn boolean confused_book(struct obj *); +staticfn void deadbook_pacify_undead(struct monst *); +staticfn void deadbook(struct obj *); +staticfn int learn(void); +staticfn boolean rejectcasting(void); +staticfn boolean getspell(int *); +staticfn int QSORTCALLBACK spell_cmp(const genericptr, const genericptr); +staticfn void sortspells(void); +staticfn boolean spellsortmenu(void); +staticfn boolean dospellmenu(const char *, int, int *); +staticfn int percent_success(int); +staticfn int energy_cost(int); +staticfn char *spellretention(int, char *); +staticfn int throwspell(void); +staticfn void cast_protection(void); +staticfn void cast_chain_lightning(void); +staticfn void spell_backfire(int); +staticfn int spell_hunger(int); +staticfn boolean spelleffects_check(int, int *, int *); +staticfn const char *spelltypemnemonic(int); +staticfn boolean can_center_spell_location(coordxy, coordxy); +staticfn void display_spell_target_positions(boolean); +staticfn boolean spell_aim_step(genericptr_t, coordxy, coordxy); +staticfn void propagate_chain_lightning(struct chain_lightning_queue *, + struct chain_lightning_zap); /* The roles[] table lists the role-specific values for tuning * percent_success(). @@ -75,7 +85,7 @@ static boolean spell_aim_step(genericptr_t, coordxy, coordxy); static const char explodes[] = "radiates explosive energy"; /* convert a letter into a number in the range 0..51, or -1 if not a letter */ -static int +staticfn int spell_let_to_idx(char ilet) { int indx; @@ -90,8 +100,8 @@ spell_let_to_idx(char ilet) } /* TRUE: book should be destroyed by caller */ -static boolean -cursed_book(struct obj* bp) +staticfn boolean +cursed_book(struct obj *bp) { boolean was_in_use; int lev = objects[bp->otyp].oc_level; @@ -173,8 +183,8 @@ cursed_book(struct obj* bp) } /* study while confused: returns TRUE if the book is destroyed */ -static boolean -confused_book(struct obj* spellbook) +staticfn boolean +confused_book(struct obj *spellbook) { if (!rn2(3) && spellbook->otyp != SPE_BOOK_OF_THE_DEAD) { spellbook->in_use = TRUE; /* in case called from learn */ @@ -187,17 +197,36 @@ confused_book(struct obj* spellbook) return TRUE; } else { You("find yourself reading the %s line over and over again.", - spellbook == gc.context.spbook.book ? "next" : "first"); + spellbook == svc.context.spbook.book ? "next" : "first"); } return FALSE; } +/* pacify or tame an undead monster */ +staticfn void +deadbook_pacify_undead(struct monst *mtmp) +{ + if ((is_undead(mtmp->data) || is_vampshifter(mtmp)) + && cansee(mtmp->mx, mtmp->my)) { + mtmp->mpeaceful = TRUE; + if (sgn(mtmp->data->maligntyp) == sgn(u.ualign.type) + && mdistu(mtmp) < 4) + if (mtmp->mtame) { + if (mtmp->mtame < 20) + mtmp->mtame++; + } else + (void) tamedog(mtmp, (struct obj *) 0, FALSE, TRUE); + else + monflee(mtmp, 0, FALSE, TRUE); + } +} + /* special effects for The Book of the Dead; reading it while blind is allowed so that needs to be taken into account too */ -static void -deadbook(struct obj* book2) +staticfn void +deadbook(struct obj *book2) { - struct monst *mtmp, *mtmp2; + struct monst *mtmp; coord mm; You("turn the pages of the Book of the Dead..."); @@ -206,8 +235,8 @@ deadbook(struct obj* book2) /* KMH -- Need ->known to avoid "_a_ Book of the Dead" */ book2->known = 1; if (invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)) { - register struct obj *otmp; - register boolean arti1_primed = FALSE, arti2_primed = FALSE, + struct obj *otmp; + boolean arti1_primed = FALSE, arti2_primed = FALSE, arti_cursed = FALSE; if (book2->cursed) { @@ -237,7 +266,7 @@ deadbook(struct obj* book2) arti_cursed = TRUE; } if (otmp->otyp == BELL_OF_OPENING - && (gm.moves - otmp->age) < 5L) { /* you rang it recently */ + && (svm.moves - otmp->age) < 5L) { /* you rang it recently */ if (!otmp->cursed) arti2_primed = TRUE; else @@ -251,8 +280,7 @@ deadbook(struct obj* book2) are not artifacts */ pline("At least one of your relics is cursed..."); } else if (arti1_primed && arti2_primed) { - unsigned soon = - (unsigned) d(2, 6); /* time til next intervene() */ + unsigned soon = (unsigned) d(2, 6); /* time til next intervene() */ /* successful invocation */ mkinvokearea(); @@ -260,7 +288,7 @@ deadbook(struct obj* book2) record_achievement(ACH_INVK); /* in case you haven't killed the Wizard yet, behave as if you just did */ - u.uevent.udemigod = 1; /* wizdead() */ + u.uevent.udemigod = 1; /* wizdeadorgone() */ if (!u.udg_cnt || u.udg_cnt > soon) u.udg_cnt = soon; } else { /* at least one relic not prepared properly */ @@ -290,25 +318,7 @@ deadbook(struct obj* book2) mm.y = u.uy; mkundead(&mm, TRUE, NO_MINVENT); } else if (book2->blessed) { - for (mtmp = fmon; mtmp; mtmp = mtmp2) { - mtmp2 = mtmp->nmon; /* tamedog() changes chain */ - if (DEADMONSTER(mtmp)) - continue; - - if ((is_undead(mtmp->data) || is_vampshifter(mtmp)) - && cansee(mtmp->mx, mtmp->my)) { - mtmp->mpeaceful = TRUE; - if (sgn(mtmp->data->maligntyp) == sgn(u.ualign.type) - && mdistu(mtmp) < 4) - if (mtmp->mtame) { - if (mtmp->mtame < 20) - mtmp->mtame++; - } else - (void) tamedog(mtmp, (struct obj *) 0, FALSE); - else - monflee(mtmp, 0, FALSE, TRUE); - } - } + iter_mons(deadbook_pacify_undead); } else { switch (rn2(3)) { case 0: @@ -329,7 +339,7 @@ void book_cursed(struct obj *book) { if (book->cursed && gm.multi >= 0 - && go.occupation == learn && gc.context.spbook.book == book) { + && go.occupation == learn && svc.context.spbook.book == book) { pline("%s shut!", Tobjnam(book, "slam")); set_bknown(book, 1); stop_occupation(); @@ -338,30 +348,31 @@ book_cursed(struct obj *book) DISABLE_WARNING_FORMAT_NONLITERAL -static int +staticfn int learn(void) { int i; short booktype; char splname[BUFSZ]; - boolean costly = TRUE; - struct obj *book = gc.context.spbook.book; + boolean costly = TRUE, faded_to_blank = FALSE; + struct obj *book = svc.context.spbook.book; /* JDS: lenses give 50% faster reading; 33% smaller read time */ - if (gc.context.spbook.delay && ublindf && ublindf->otyp == LENSES && rn2(2)) - gc.context.spbook.delay++; + if (svc.context.spbook.delay && ublindf + && ublindf->otyp == LENSES && rn2(2)) + svc.context.spbook.delay++; if (Confusion) { /* became confused while learning */ if (!confused_book(book)) { You("can no longer focus on the book."); } - gc.context.spbook.book = 0; /* no longer studying */ - gc.context.spbook.o_id = 0; - gc.context.spbook.delay = 0; + svc.context.spbook.book = 0; /* no longer studying */ + svc.context.spbook.o_id = 0; + svc.context.spbook.delay = 0; return 0; } - if (gc.context.spbook.delay) { - /* not if (gc.context.spbook.delay++), so at end delay == 0 */ - gc.context.spbook.delay++; + if (svc.context.spbook.delay) { + /* not if (svc.context.spbook.delay++), so at end delay == 0 */ + svc.context.spbook.delay++; return 1; /* still busy */ } exercise(A_WIS, TRUE); /* you're studying. */ @@ -385,6 +396,7 @@ learn(void) if (book->spestudied > MAX_SPELL_STUDY) { pline("This spellbook is too faint to be read any more."); book->otyp = booktype = SPE_BLANK_PAPER; + faded_to_blank = TRUE; /* reset spestudied as if polymorph had taken place */ book->spestudied = rn2(book->spestudied); } else { @@ -394,7 +406,6 @@ learn(void) book->spestudied++; exercise(A_WIS, TRUE); /* extra study */ } - makeknown((int) booktype); } else { /* (spellid(i) == NO_SPELL) */ /* for a normal book, spestudied will be zero, but for a polymorphed one, spestudied will be non-zero and @@ -403,11 +414,12 @@ learn(void) /* pre-used due to being the product of polymorph */ pline("This spellbook is too faint to read even once."); book->otyp = booktype = SPE_BLANK_PAPER; + faded_to_blank = TRUE; /* reset spestudied as if polymorph had taken place */ book->spestudied = rn2(book->spestudied); } else { - gs.spl_book[i].sp_id = booktype; - gs.spl_book[i].sp_lev = objects[booktype].oc_level; + svs.spl_book[i].sp_id = booktype; + svs.spl_book[i].sp_lev = objects[booktype].oc_level; incrnknow(i, 1); book->spestudied++; if (!i) @@ -417,28 +429,38 @@ learn(void) You("add %s to your repertoire, as '%c'.", splname, spellet(i)); } + } + if (i < MAXSPELL) { + /* might be learning a new spellbook type or spellbook of blank paper; + if so, persistent inventory will get updated */ makeknown((int) booktype); + /* makeknown() calls update_inventory() when discovering something + new but is a no-op for something that's already known so wouldn't + update persistent inventory to reflect faded book if spellbook of + blank paper happens to already be discovered */ + if (faded_to_blank) + update_inventory(); } if (book->cursed) { /* maybe a demon cursed it */ if (cursed_book(book)) { useup(book); - gc.context.spbook.book = 0; - gc.context.spbook.o_id = 0; + svc.context.spbook.book = 0; + svc.context.spbook.o_id = 0; return 0; } } if (costly) check_unpaid(book); - gc.context.spbook.book = 0; - gc.context.spbook.o_id = 0; + svc.context.spbook.book = 0; + svc.context.spbook.o_id = 0; return 0; } RESTORE_WARNING_FORMAT_NONLITERAL int -study_book(register struct obj* spellbook) +study_book(struct obj *spellbook) { int booktype = spellbook->otyp, i; boolean confused = (Confusion != 0); @@ -451,7 +473,7 @@ study_book(register struct obj* spellbook) int dullbook = rnd(25) - ACURR(A_WIS); /* adjust chance if hero stayed awake, got interrupted, retries */ - if (gc.context.spbook.delay && spellbook == gc.context.spbook.book) + if (svc.context.spbook.delay && spellbook == svc.context.spbook.book) dullbook -= rnd(objects[booktype].oc_level); if (dullbook > 0) { @@ -471,17 +493,18 @@ study_book(register struct obj* spellbook) && olfaction(gy.youmonst.data)) { pline("The book's dust gets in your %s and makes you sneeze!", body_part(NOSE)); - wake_nearby(); + wake_nearby(FALSE); return 1; } - if (gc.context.spbook.delay && !confused - && spellbook == gc.context.spbook.book + if (svc.context.spbook.delay && !confused + && spellbook == svc.context.spbook.book /* handle the sequence: start reading, get interrupted, have - gc.context.spbook.book become erased somehow, resume reading it */ + svc.context.spbook.book become erased somehow, resume reading it */ && booktype != SPE_BLANK_PAPER) { You("continue your efforts to %s.", - (booktype == SPE_NOVEL) ? "read the novel" : "memorize the spell"); + (booktype == SPE_NOVEL) ? "read the novel" + : "memorize the spell"); } else { /* KMH -- Simplified this code */ if (booktype == SPE_BLANK_PAPER) { @@ -499,7 +522,8 @@ study_book(register struct obj* spellbook) spellbook->o_id)) { if (!u.uconduct.literate++) livelog_printf(LL_CONDUCT, - "became literate by reading %s", tribtitle); + "became literate by reading %s", + tribtitle); check_unpaid(spellbook); makeknown(booktype); @@ -521,7 +545,7 @@ study_book(register struct obj* spellbook) } /* currently level * 10 */ - gc.context.spbook.delay = -objects[booktype].oc_delay; + svc.context.spbook.delay = -objects[booktype].oc_delay; /* check to see if we already know it and want to refresh our memory */ for (i = 0; i < MAXSPELL; i++) @@ -567,7 +591,7 @@ study_book(register struct obj* spellbook) if (too_hard) { You("can't comprehend the runes!"); - make_confused(-gc.context.spbook.delay, FALSE); /* study time */ + make_confused(-svc.context.spbook.delay, FALSE); /* study time */ bot(); /* show Conf on status line before tele, crumbling, etc */ boolean gone = cursed_book(spellbook); @@ -592,9 +616,9 @@ study_book(register struct obj* spellbook) spellbook->otyp == SPE_BOOK_OF_THE_DEAD ? "recite" : "memorize"); } - gc.context.spbook.book = spellbook; - if (gc.context.spbook.book) - gc.context.spbook.o_id = gc.context.spbook.book->o_id; + svc.context.spbook.book = spellbook; + if (svc.context.spbook.book) + svc.context.spbook.o_id = svc.context.spbook.book->o_id; set_occupation(learn, "studying", 0); return 1; } @@ -602,11 +626,11 @@ study_book(register struct obj* spellbook) /* a spellbook has been destroyed or the character has changed levels; the stored address for the current book is no longer valid */ void -book_disappears(struct obj* obj) +book_disappears(struct obj *obj) { - if (obj == gc.context.spbook.book) { - gc.context.spbook.book = (struct obj *) 0; - gc.context.spbook.o_id = 0; + if (obj == svc.context.spbook.book) { + svc.context.spbook.book = (struct obj *) 0; + svc.context.spbook.o_id = 0; } } @@ -614,12 +638,12 @@ book_disappears(struct obj* obj) so the sequence start reading, get interrupted, name the book, resume reading would read the "new" book from scratch */ void -book_substitution(struct obj* old_obj, struct obj* new_obj) +book_substitution(struct obj *old_obj, struct obj *new_obj) { - if (old_obj == gc.context.spbook.book) { - gc.context.spbook.book = new_obj; - if (gc.context.spbook.book) - gc.context.spbook.o_id = gc.context.spbook.book->o_id; + if (old_obj == svc.context.spbook.book) { + svc.context.spbook.book = new_obj; + if (svc.context.spbook.book) + svc.context.spbook.o_id = svc.context.spbook.book->o_id; } } @@ -642,7 +666,7 @@ age_spells(void) /* return True if spellcasting is inhibited; only covers a small subset of reasons why casting won't work */ -static boolean +staticfn boolean rejectcasting(void) { /* rejections which take place before selecting a particular spell */ @@ -670,11 +694,12 @@ rejectcasting(void) * Return TRUE if a spell was picked, with the spell index in the return * parameter. Otherwise return FALSE. */ -static boolean -getspell(int* spell_no) +staticfn boolean +getspell(int *spell_no) { int nspells, idx; char ilet, lets[BUFSZ], qbuf[QBUFSZ]; + struct _cmd_queue cq, *cmdq; if (spellid(0) == NO_SPELL) { You("don't know any spells right now."); @@ -683,6 +708,21 @@ getspell(int* spell_no) if (rejectcasting()) return FALSE; /* no spell chosen */ + if ((cmdq = cmdq_pop()) != 0) { + cq = *cmdq; + free(cmdq); + if (cq.typ == CMDQ_KEY) { + nspells = num_spells(); + idx = spell_let_to_idx(cq.key); + if (idx < 0 || idx >= nspells) + return FALSE; + *spell_no = idx; + return TRUE; + } else { + return FALSE; + } + } + if (flags.menu_style == MENU_TRADITIONAL) { /* we know there is at least 1 known spell */ nspells = num_spells(); @@ -737,7 +777,8 @@ dowizcast(void) if (n >= SPE_BLANK_PAPER) break; any.a_int = n; - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, 0, OBJ_NAME(objects[n]), MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, NO_COLOR, + OBJ_NAME(objects[n]), MENU_ITEMFLAGS_NONE); } end_menu(win, "Cast which spell?"); n = select_menu(win, PICK_ONE, &selected); @@ -757,9 +798,11 @@ docast(void) { int spell_no; - if (getspell(&spell_no)) - return spelleffects(gs.spl_book[spell_no].sp_id, FALSE, FALSE); - return ECMD_OK; + if (getspell(&spell_no)) { + cmdq_add_key(CQ_REPEAT, spellet(spell_no)); + return spelleffects(svs.spl_book[spell_no].sp_id, FALSE, FALSE); + } + return ECMD_FAIL; } const char * @@ -792,7 +835,246 @@ spell_skilltype(int booktype) return objects[booktype].oc_skill; } -static void +/* Wizards learn what spellbooks look like based on their skill in the + spell's school */ +void +skill_based_spellbook_id(void) +{ + if (!Role_if(PM_WIZARD)) + return; + + int booktype; + const uchar spbook_class = (uchar) SPBOOK_CLASS; + + for (booktype = svb.bases[spbook_class]; + booktype < svb.bases[spbook_class + 1]; + booktype++) { + int known_up_to_level; + int skill = spell_skilltype(booktype); + + if (skill == P_NONE) + continue; + + switch (P_SKILL(skill)) { + case P_BASIC: + known_up_to_level = 3; + break; + case P_SKILLED: + known_up_to_level = 5; + break; + case P_EXPERT: + case P_MASTER: + case P_GRAND_MASTER: + known_up_to_level = 7; + break; + case P_UNSKILLED: + default: + known_up_to_level = 1; + break; + } + + if (objects[booktype].oc_level <= known_up_to_level) + /* makeknown(booktype) but don't exercise Wisdom */ + discover_object(booktype, TRUE, FALSE); + } +} + + +/* Limit the total area chain lightning can cover; this is both for + technical reasons (making it possible to limit the size of arrays + here and in the display code) and for gameplay balance reasons; + this value should be smaller than TMP_AT_MAX_GLYPHS (display.c) in + order for chain lightning to display properly */ +#define CHAIN_LIGHTNING_LIMIT 100 +/* Unlike most zaps, chain lightning can't hit solid terrain (it + doesn't have enough power), it only covers open space; this also + means that it can't hit monsters inside walls, which makes sense as + they would be earthed */ +#define CHAIN_LIGHTNING_TYP(typ) \ + (SPACE_POS(typ) || (typ) == POOL || (typ) == MOAT /* not WATER */ \ + || (typ) == DRAWBRIDGE_UP || (typ) == LAVAPOOL) /* not LAVAWALL */ +#define CHAIN_LIGHTNING_POS(x, y) \ + (isok(x, y) && (CHAIN_LIGHTNING_TYP(levl[x][y].typ) \ + || (IS_DOOR(levl[x][y].typ) \ + && !(levl[x][y].doormask & (D_CLOSED | D_LOCKED))))) + +struct chain_lightning_zap { + /* direction in which this zap is currently moving; this is an + enum movementdirs, clamped to the range 0 inclusive to N_DIRS + exclusive */ + uchar dir; + /* current location of the zap */ + coordxy x, y; + /* distance this zap can cover without chaining */ + char strength; +}; + +struct chain_lightning_queue { + struct chain_lightning_zap q[CHAIN_LIGHTNING_LIMIT]; + int head; + int tail; + int displayed_beam; +}; + +/* Given a potential chain lightning zap, moves it one square forward in + the given direction, then adds it to the queue unless it would hit an + invalid square or is out of power. + + zap is passed by value, so the move-forward doesn't change the passed + argument. */ +staticfn void +propagate_chain_lightning( + struct chain_lightning_queue *clq, + struct chain_lightning_zap zap) +{ + struct monst *mon; + + zap.x += xdir[zap.dir]; + zap.y += ydir[zap.dir]; + + if (clq->tail >= CHAIN_LIGHTNING_LIMIT) + return; /* zap has covered too many squares */ + if (!CHAIN_LIGHTNING_POS(zap.x, zap.y)) + return; /* zap can't go to this square */ + + mon = m_at(zap.x, zap.y); + if (mon && mon->mpeaceful) + return; /* chain lightning avoids peaceful and tame monsters */ + + /* When hitting a monster that isn't electricity-resistant, a + particular chain lightning zap regains all its power, allowing it to + chain to other monsters; upon hitting a shock-resistant monster it + can't continue any further, but we let it hit the monster to show + the shield effect */ + if (mon && !resists_elec(mon) && !defended(mon, AD_ELEC)) + zap.strength = 3; + else if (mon) + zap.strength = 0; + + /* Unless it hits a monster, the last square of a zap isn't drawn on + screen and can't propagate further, so it may as well be discarded + now */ + if (!mon && !zap.strength) + return; + + /* The same square can't be chained to twice. */ + for (int i = 0; i < clq->tail; i++) { + if (clq->q[i].x == zap.x && clq->q[i].y == zap.y) + return; + } + + /* This array access must be inbounds due to the CHAIN_LIGHTNING_LIMIT + check earlier. */ + clq->q[clq->tail++] = zap; + + /* Draw it. */ + tmp_at(DISP_CHANGE, zapdir_to_glyph(xdir[zap.dir], ydir[zap.dir], + clq->displayed_beam)); + tmp_at(zap.x, zap.y); +} + +staticfn void +cast_chain_lightning(void) +{ + struct chain_lightning_queue clq = { + {{0}}, 0, 0, Hallucination ? rn2_on_display_rng(6) : (AD_ELEC - 1) + }; + + if (u.uswallow) { + // TODO: damage the engulfer + return; + } + + /* set the type of beam we're using; the direction here is arbitrary + because we change the beam direction just before drawing the beam + anyway */ + tmp_at(DISP_BEAM, zapdir_to_glyph(0, 1, clq.displayed_beam)); + + /* start by propagating in all directions from the caster */ + for (int dir = 0; dir < N_DIRS; dir++) { + struct chain_lightning_zap zap = { dir, u.ux, u.uy, 2 }; + + propagate_chain_lightning(&clq, zap); + } + nh_delay_output(); + + while (clq.head < clq.tail) { + int delay_tail = clq.tail; + + while (clq.head < delay_tail) { + struct chain_lightning_zap zap = clq.q[clq.head++]; + /* damage any monster that was hit */ + struct monst *mon = m_at(zap.x, zap.y); + + if (mon) { + struct obj *unused = 0; /* AD_ELEC can't destroy armor */ + int dmg; + + gn.notonhead = (mon->mx != gb.bhitpos.x + || mon->my != gb.bhitpos.y); + dmg = zhitm(mon, BZ_U_SPELL(AD_ELEC - 1), 2, &unused); + + if (dmg) { + /* mon has been damaged, but we haven't yet printed the + messages or given kill credit; assume the hero can + sense their spell hitting monsters, because they can + steer it away from peacefuls */ + if (DEADMONSTER(mon)) { + xkilled(mon, XKILL_GIVEMSG); + } else { + pline("You shock %s%s", mon_nam(mon), exclam(dmg)); + /* if a long worm, only map 'I' for its head */ + if (!canseemon(mon) && !gn.notonhead) + /* FIXME: this doesn't work, possibly because + cleaning up tmp_at() restores old glyph? */ + map_invisible(zap.x, zap.y); + } + } else if (canseemon(mon)) { + pline("%s resists.", Monnam(mon)); + } + if (!DEADMONSTER(mon)) { + /* wakeup is via attack, but since mon is already + hostile we pass via_attack==False rather than True, + otherwise other monsters witnessing this would treat + it as seeing hero attack a peaceful; mimic will be + exposed; forcefight makes hider unhide */ + svc.context.forcefight++; + wakeup(mon, FALSE, TRUE); + svc.context.forcefight--; + } + } + + /* each zap propagates forwards with 1 less strength, and + diagonally with 0 strength (thus the diagonal zaps aren't + drawn and don't spread unless they hit a monster); + exception: if the zap just hit a monster, the diagonals have + as much strength as the forwards zap */ + if (!zap.strength) + continue; /* happens upon hitting a shock-resistant monster */ + zap.strength--; + + propagate_chain_lightning(&clq, zap); + + if (zap.strength < 2) + zap.strength = 0; + else if (u.uen > 0) + u.uen--; /* propagating past mons increases Pw cost a bit */ + zap.dir = DIR_LEFT(zap.dir); + propagate_chain_lightning(&clq, zap); + + zap.dir = DIR_RIGHT2(zap.dir); + propagate_chain_lightning(&clq, zap); + } + nh_delay_output(); + } + nh_delay_output(); + nh_delay_output(); + + tmp_at(DISP_END, 0); +} + + +staticfn void cast_protection(void) { int l = u.ulevel, loglev = 0, @@ -869,7 +1151,7 @@ cast_protection(void) } /* attempting to cast a forgotten spell will cause disorientation */ -static void +staticfn void spell_backfire(int spell) { long duration = (long) ((spellev(spell) + 1) * 3), /* 6..24 */ @@ -911,7 +1193,7 @@ spell_backfire(int spell) /* Given an expected amount of hunger for a spell, return the amount it should * be reduced to. High-intelligence Wizards get to cast spells with less or no * hunger penalty. */ -static int +staticfn int spell_hunger(int hungr) { /* If hero is a wizard, their current intelligence @@ -942,7 +1224,7 @@ spell_hunger(int hungr) /* for using this function to test whether hunger would be eliminated */ #define spell_would_hunger() (spell_hunger(100) > 0) -static boolean +staticfn boolean spelleffects_check(int spell, int *res, int *energy) { boolean confused = (Confusion != 0); @@ -980,7 +1262,7 @@ spelleffects_check(int spell, int *res, int *energy) u.uen -= rnd(*energy); if (u.uen < 0) u.uen = 0; - gc.context.botl = 1; + disp.botl = TRUE; *res = ECMD_TIME; return TRUE; } else if (spellknow(spell) <= KEEN / 200) { /* 100 turns left */ @@ -1035,7 +1317,7 @@ spelleffects_check(int spell, int *res, int *energy) u.uen -= rnd(2 * *energy); if (u.uen < 0) u.uen = 0; - gc.context.botl = 1; + disp.botl = TRUE; *res = ECMD_TIME; /* time is used even if spell doesn't get cast */ } @@ -1073,7 +1355,7 @@ spelleffects_check(int spell, int *res, int *energy) if (confused) { You("fail to cast the spell correctly."); u.uen -= *energy / 2; - gc.context.botl = 1; + disp.botl = TRUE; *res = ECMD_TIME; return TRUE; } @@ -1096,7 +1378,7 @@ spelleffects(int spell_otyp, boolean atme, boolean force) return res; u.uen -= energy; - gc.context.botl = 1; + disp.botl = TRUE; exercise(A_WIS, TRUE); /* pseudo is a temporary "false" object containing the spell stats */ pseudo = mksobj(force ? spell : spellid(spell), FALSE, FALSE); @@ -1155,12 +1437,14 @@ spelleffects(int spell_otyp, boolean atme, boolean force) /* player said not to cast advanced spell; return up to half of the * magical energy */ u.uen += rnd(energy / 2); - } + } /* 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: @@ -1224,8 +1508,8 @@ spelleffects(int spell_otyp, boolean atme, boolean force) /* 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); @@ -1240,6 +1524,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); @@ -1279,6 +1564,9 @@ spelleffects(int spell_otyp, boolean atme, boolean force) if (!(jump(max(role_skill, 1)) & ECMD_TIME)) pline1(nothing_happens); break; + case SPE_CHAIN_LIGHTNING: + cast_chain_lightning(); + break; default: impossible("Unknown spell %d attempted.", spell); obfree(pseudo, (struct obj *) 0); @@ -1286,14 +1574,15 @@ 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; } /*ARGSUSED*/ -static boolean +staticfn boolean spell_aim_step(genericptr_t arg UNUSED, coordxy x, coordxy y) { if (!isok(x,y)) @@ -1305,7 +1594,7 @@ spell_aim_step(genericptr_t arg UNUSED, coordxy x, coordxy y) } /* not quite the same as throwspell limits, but close enough */ -static boolean +staticfn boolean can_center_spell_location(coordxy x, coordxy y) { if (distmin(u.ux, u.uy, x, y) > 10) @@ -1313,8 +1602,35 @@ can_center_spell_location(coordxy x, coordxy y) return (isok(x, y) && cansee(x, y) && !(IS_STWALL(levl[x][y].typ))); } +staticfn void +display_spell_target_positions(boolean on_off) +{ + coordxy x, y, dx, dy; + int dist = 10; + + if (on_off) { + /* on */ + tmp_at(DISP_BEAM, cmap_to_glyph(S_goodpos)); + for (dx = -dist; dx <= dist; dx++) + for (dy = -dist; dy <= dist; dy++) { + x = u.ux + dx; + y = u.uy + dy; + /* hero's location is allowed but highlighting the hero's + spot makes map harder to read (if using '$' rather than + by changing background color) */ + if (u_at(x, y)) + continue; + if (can_center_spell_location(x, y)) + tmp_at(x, y); + } + } else { + /* off */ + tmp_at(DISP_END, 0); + } +} + /* Choose location where spell takes effect. */ -static int +staticfn int throwspell(void) { coord cc, uc; @@ -1331,7 +1647,8 @@ throwspell(void) pline("Where do you want to cast the spell?"); cc.x = u.ux; cc.y = u.uy; - getpos_sethilite(NULL, can_center_spell_location); + getpos_sethilite(display_spell_target_positions, + can_center_spell_location); if (getpos(&cc, TRUE, "the desired position") < 0) return 0; /* user pressed ESC */ clear_nhwindow(WIN_MESSAGE); /* discard any autodescribe feedback */ @@ -1391,14 +1708,14 @@ tport_spell(int what) save_tport.tport_indx = MAXSPELL; } else if (what == UNHIDESPELL) { /*assert( save_tport.savespell.sp_id == SPE_TELEPORT_AWAY );*/ - gs.spl_book[save_tport.tport_indx] = save_tport.savespell; + svs.spl_book[save_tport.tport_indx] = save_tport.savespell; save_tport.tport_indx = MAXSPELL; /* burn bridge... */ } else if (what == ADD_SPELL) { - save_tport.savespell = gs.spl_book[i]; + save_tport.savespell = svs.spl_book[i]; save_tport.tport_indx = i; - gs.spl_book[i].sp_id = SPE_TELEPORT_AWAY; - gs.spl_book[i].sp_lev = objects[SPE_TELEPORT_AWAY].oc_level; - gs.spl_book[i].sp_know = KEEN; + svs.spl_book[i].sp_id = SPE_TELEPORT_AWAY; + svs.spl_book[i].sp_lev = objects[SPE_TELEPORT_AWAY].oc_level; + svs.spl_book[i].sp_know = KEEN; return REMOVESPELL; /* operation needed to reverse */ } } else { /* spellid(i) == SPE_TELEPORT_AWAY */ @@ -1406,12 +1723,12 @@ tport_spell(int what) save_tport.tport_indx = MAXSPELL; } else if (what == REMOVESPELL) { /*assert( i == save_tport.tport_indx );*/ - gs.spl_book[i] = save_tport.savespell; + svs.spl_book[i] = save_tport.savespell; save_tport.tport_indx = MAXSPELL; } else if (what == HIDE_SPELL) { - save_tport.savespell = gs.spl_book[i]; + save_tport.savespell = svs.spl_book[i]; save_tport.tport_indx = i; - gs.spl_book[i].sp_id = NO_SPELL; + svs.spl_book[i].sp_id = NO_SPELL; return UNHIDESPELL; /* operation needed to reverse */ } } @@ -1427,8 +1744,8 @@ losespells(void) int n, nzap, i; /* in case reading has been interrupted earlier, discard context */ - gc.context.spbook.book = 0; - gc.context.spbook.o_id = 0; + svc.context.spbook.book = 0; + svc.context.spbook.o_id = 0; /* count the number of known spells */ for (n = 0; n < MAXSPELL; ++n) if (spellid(n) == NO_SPELL) @@ -1528,19 +1845,19 @@ static const char *const spl_sortchoices[NUM_SPELL_SORTBY] = { }; /* qsort callback routine */ -static int QSORTCALLBACK +staticfn int QSORTCALLBACK spell_cmp(const genericptr vptr1, const genericptr vptr2) { /* * gather up all of the possible parameters except spell name * in advance, even though some might not be needed: - * indx. = spl_orderindx[] index into gs.spl_book[]; - * otyp. = gs.spl_book[] index into objects[]; + * indx. = spl_orderindx[] index into svs.spl_book[]; + * otyp. = svs.spl_book[] index into objects[]; * levl. = spell level; * skil. = skill group aka spell class. */ int indx1 = *(int *) vptr1, indx2 = *(int *) vptr2, - otyp1 = gs.spl_book[indx1].sp_id, otyp2 = gs.spl_book[indx2].sp_id, + otyp1 = svs.spl_book[indx1].sp_id, otyp2 = svs.spl_book[indx2].sp_id, levl1 = objects[otyp1].oc_level, levl2 = objects[otyp2].oc_level, skil1 = objects[otyp1].oc_skill, skil2 = objects[otyp2].oc_skill; @@ -1585,7 +1902,7 @@ spell_cmp(const genericptr vptr1, const genericptr vptr2) /* sort the index used for display order of the "view known spells" list (sortmode == SORTBY_xxx), or sort the spellbook itself to make the current display order stick (sortmode == SORTRETAINORDER) */ -static void +staticfn void sortspells(void) { int i; @@ -1616,13 +1933,13 @@ sortspells(void) if (gs.spl_sortmode == SORTRETAINORDER) { struct spell tmp_book[MAXSPELL]; - /* sort gs.spl_book[] rather than spl_orderindx[]; + /* sort svs.spl_book[] rather than spl_orderindx[]; this also updates the index to reflect the new ordering (we could just free it since that ordering becomes the default) */ for (i = 0; i < MAXSPELL; i++) - tmp_book[i] = gs.spl_book[gs.spl_orderindx[i]]; + tmp_book[i] = svs.spl_book[gs.spl_orderindx[i]]; for (i = 0; i < MAXSPELL; i++) - gs.spl_book[i] = tmp_book[i], gs.spl_orderindx[i] = i; + svs.spl_book[i] = tmp_book[i], gs.spl_orderindx[i] = i; gs.spl_sortmode = SORTBY_LETTER; /* reset */ return; } @@ -1634,7 +1951,7 @@ sortspells(void) } /* called if the [sort spells] entry in the view spells menu gets chosen */ -static boolean +staticfn boolean spellsortmenu(void) { winid tmpwin; @@ -1642,7 +1959,7 @@ spellsortmenu(void) anything any; char let; int i, n, choice; - int clr = 0; + int clr = NO_COLOR; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -1652,9 +1969,7 @@ spellsortmenu(void) if (i == SORTRETAINORDER) { let = 'z'; /* assumes fewer than 26 sort choices... */ /* separate final choice from others with a blank line */ - any.a_int = 0; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); + add_menu_str(tmpwin, ""); } else { let = 'a' + i; } @@ -1702,9 +2017,9 @@ dovspell(void) if (!dospellmenu(qbuf, splnum, &othnum)) break; - spl_tmp = gs.spl_book[splnum]; - gs.spl_book[splnum] = gs.spl_book[othnum]; - gs.spl_book[othnum] = spl_tmp; + spl_tmp = svs.spl_book[splnum]; + svs.spl_book[splnum] = svs.spl_book[othnum]; + svs.spl_book[othnum] = spl_tmp; } } } @@ -1721,10 +2036,11 @@ DISABLE_WARNING_FORMAT_NONLITERAL /* shows menu of known spells, with options to sort them. return FALSE on cancel, TRUE otherwise. spell_no is set to the internal spl_book index, if any selected */ -static boolean +staticfn boolean dospellmenu( const char *prompt, - int splaction, /* SPELLMENU_CAST, SPELLMENU_VIEW, or gs.spl_book[] index */ + int splaction, /* SPELLMENU_CAST, SPELLMENU_VIEW, or + * svs.spl_book[] index */ int *spell_no) { winid tmpwin; @@ -1733,7 +2049,7 @@ dospellmenu( const char *fmt; menu_item *selected; anything any; - int clr = 0; + int clr = NO_COLOR; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); @@ -1759,8 +2075,7 @@ dospellmenu( if (wizard) Sprintf(eos(buf), "%c%6s", sep, "turns"); - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - iflags.menu_headings, clr, buf, MENU_ITEMFLAGS_NONE); + add_menu_heading(tmpwin, buf); for (i = 0; i < MAXSPELL && spellid(i) != NO_SPELL; i++) { splnum = !gs.spl_orderindx ? i : gs.spl_orderindx[i]; if (energy_cost(splnum) < 0) { @@ -1851,7 +2166,7 @@ static const struct spellwand wand_combos[] = { * In xNetHack this number isn't actually used directly, but rather influences * how much extra Pw a difficult spell will take to cast. * Original formula by FIQ, with some modifications. */ -static int +staticfn int percent_success(int spell) { int chance, armor_penalty, cap; @@ -1945,7 +2260,7 @@ percent_success(int spell) /* One of Orcus's negative effects during the ascension run is to apply a * fluctuating penalty to spellcasting. */ if (fiend_adversity(PM_ORCUS)) { - chance = ((hash1(gm.moves / 20) % 10) + 10); + chance = ((hash1(svm.moves / 20) % 10) + 10); } /* The less skilled you are, the worse the cap on your spellcasting ability. */ @@ -1977,7 +2292,7 @@ percent_success(int spell) as much Pw. Return -1 if the success rate would be 0 and the spell cannot be cast. */ -static int +staticfn int energy_cost(int spell) { int base_energy = (spellev(spell) * 5); /* 5 <= energy <= 35 */ @@ -2009,7 +2324,7 @@ energy_cost(int spell) /* Learn a spell during creation of the initial inventory */ void -initialspell(struct obj* obj) +initialspell(struct obj *obj) { int i, otyp = obj->otyp; @@ -2023,8 +2338,8 @@ initialspell(struct obj* obj) /* initial inventory shouldn't contain duplicate spellbooks */ impossible("Spell %s already known.", OBJ_NAME(objects[otyp])); } else { - gs.spl_book[i].sp_id = otyp; - gs.spl_book[i].sp_lev = objects[otyp].oc_level; + svs.spl_book[i].sp_id = otyp; + svs.spl_book[i].sp_lev = objects[otyp].oc_level; incrnknow(i, 0); } return; @@ -2077,8 +2392,8 @@ force_learn_spell(short otyp) } /* for a going-stale or forgotten spell the sp_id and sp_lev assignments are redundant but harmless; for an unknown spell, they're essential */ - gs.spl_book[i].sp_id = otyp; - gs.spl_book[i].sp_lev = objects[otyp].oc_level; + svs.spl_book[i].sp_id = otyp; + svs.spl_book[i].sp_lev = objects[otyp].oc_level; incrnknow(i, 0); /* set spl_book[i].sp_know to KEEN; unlike when learning * a spell by reading its book, we don't need to add 1 */ return spellet(i); diff --git a/src/stairs.c b/src/stairs.c new file mode 100644 index 0000000000..89b9ff3e82 --- /dev/null +++ b/src/stairs.c @@ -0,0 +1,237 @@ +/* NetHack 3.7 stairs.c $NHDT-Date: 1704043695 2023/12/31 17:28:15 $ $NHDT-Branch: keni-luabits2 $:$NHDT-Revision: 1.207 $ */ +/* Copyright (c) 2024 by Pasi Kallinen */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +void +stairway_add( + coordxy x, coordxy y, + boolean up, boolean isladder, + d_level *dest) +{ + stairway *tmp = (stairway *) alloc(sizeof (stairway)); + + (void) memset((genericptr_t) tmp, 0, sizeof (stairway)); + tmp->sx = x; + tmp->sy = y; + tmp->up = up; + tmp->isladder = isladder; + tmp->u_traversed = FALSE; + assign_level(&(tmp->tolev), dest); + tmp->next = gs.stairs; + gs.stairs = tmp; +} + +void +stairway_free_all(void) +{ + stairway *tmp = gs.stairs; + + while (tmp) { + stairway *tmp2 = tmp->next; + free(tmp); + tmp = tmp2; + } + gs.stairs = NULL; +} + +stairway * +stairway_at(coordxy x, coordxy y) +{ + stairway *tmp = gs.stairs; + + while (tmp && !(tmp->sx == x && tmp->sy == y)) + tmp = tmp->next; + return tmp; +} + +stairway * +stairway_find(d_level *fromdlev) +{ + stairway *tmp = gs.stairs; + + while (tmp) { + if (tmp->tolev.dnum == fromdlev->dnum + && tmp->tolev.dlevel == fromdlev->dlevel) + break; /* return */ + tmp = tmp->next; + } + return tmp; +} + +stairway * +stairway_find_from(d_level *fromdlev, boolean isladder) +{ + stairway *tmp = gs.stairs; + + while (tmp) { + if (tmp->tolev.dnum == fromdlev->dnum + && tmp->tolev.dlevel == fromdlev->dlevel + && tmp->isladder == isladder) + break; /* return */ + tmp = tmp->next; + } + return tmp; +} + +stairway * +stairway_find_dir(boolean up) +{ + stairway *tmp = gs.stairs; + + while (tmp && !(tmp->up == up)) + tmp = tmp->next; + return tmp; +} + +stairway * +stairway_find_type_dir(boolean isladder, boolean up) +{ + stairway *tmp = gs.stairs; + + while (tmp && !(tmp->isladder == isladder && tmp->up == up)) + tmp = tmp->next; + return tmp; +} + +stairway * +stairway_find_special_dir(boolean up) +{ + stairway *tmp = gs.stairs; + + while (tmp) { + if (tmp->tolev.dnum != u.uz.dnum && tmp->up != up) + return tmp; + tmp = tmp->next; + } + return tmp; +} + +/* place you on the special staircase */ +void +u_on_sstairs(int upflag) +{ + stairway *stway = stairway_find_special_dir(upflag); + + if (stway) + u_on_newpos(stway->sx, stway->sy); + else + u_on_rndspot(upflag); +} + +/* place you on upstairs (or special equivalent) */ +void +u_on_upstairs(void) +{ + stairway *stway = stairway_find_dir(TRUE); + + if (stway) + u_on_newpos(stway->sx, stway->sy); + else + u_on_sstairs(0); /* destination upstairs implies moving down */ +} + +/* place you on dnstairs (or special equivalent) */ +void +u_on_dnstairs(void) +{ + stairway *stway = stairway_find_dir(FALSE); + + if (stway) + u_on_newpos(stway->sx, stway->sy); + else + u_on_sstairs(1); /* destination dnstairs implies moving up */ +} + +boolean +On_stairs(coordxy x, coordxy y) +{ + return (stairway_at(x, y) != NULL); +} + +boolean +On_ladder(coordxy x, coordxy y) +{ + stairway *stway = stairway_at(x, y); + + return (boolean) (stway && stway->isladder); +} + +boolean +On_stairs_up(coordxy x, coordxy y) +{ + stairway *stway = stairway_at(x, y); + + return (boolean) (stway && stway->up); +} + +boolean +On_stairs_dn(coordxy x, coordxy y) +{ + stairway *stway = stairway_at(x, y); + + return (boolean) (stway && !stway->up); +} + +/* return True if 'sway' is a branch staircase and hero has used these stairs + to visit the branch */ +boolean +known_branch_stairs(stairway *sway) +{ + return (sway && sway->tolev.dnum != u.uz.dnum && sway->u_traversed); +} + +/* describe staircase 'sway' based on whether hero knows the destination */ +char * +stairs_description( + stairway *sway, /* stairs/ladder to describe */ + char *outbuf, /* result buffer */ + boolean stcase) /* True: "staircase" or "ladder", always singular; + * False: "stairs" or "ladder"; caller needs to deal + * with singular vs plural when forming a sentence */ +{ + d_level tolev; + const char *stairs, *updown; + + tolev = sway->tolev; + stairs = sway->isladder ? "ladder" : stcase ? "staircase" : "stairs"; + updown = sway->up ? "up" : "down"; + + if (!known_branch_stairs(sway)) { + /* ordinary stairs or branch stairs to not-yet-visited branch */ + Sprintf(outbuf, "%s %s", stairs, updown); + if (sway->u_traversed) { + boolean specialdepth = (tolev.dnum == quest_dnum + || single_level_branch(&tolev)); /* knox */ + int to_dlev = specialdepth ? dunlev(&tolev) : depth(&tolev); + + Sprintf(eos(outbuf), " to level %d", to_dlev); + } + } else if (u.uz.dnum == 0 && u.uz.dlevel == 1 && sway->up) { + /* stairs up from level one are a special case; they are marked + as having been traversed because the hero obviously started + the game by coming down them, but the remote side varies + depending on whether the Amulet is being carried */ + Sprintf(outbuf, "%s%s %s %s", + !u.uhave.amulet ? "" : "branch ", + stairs, updown, + !u.uhave.amulet ? "out of the dungeon" + /* minimize our expectations about what comes next */ + : (on_level(&tolev, &earth_level) + || on_level(&tolev, &air_level) + || on_level(&tolev, &fire_level) + || on_level(&tolev, &water_level)) + ? "to the Elemental Planes" + : "to the end game"); + } else { + /* known branch stairs; tacking on destination level is too verbose */ + Sprintf(outbuf, "branch %s %s to %s", + stairs, updown, svd.dungeons[tolev.dnum].dname); + /* dungeons[].dname is capitalized; undo that for "The " */ + (void) strsubst(outbuf, "The ", "the "); + } + return outbuf; +} + +/*stairs.c*/ diff --git a/src/steal.c b/src/steal.c index 6d6316d25c..b71daad4d7 100644 --- a/src/steal.c +++ b/src/steal.c @@ -1,25 +1,13 @@ -/* NetHack 3.7 steal.c $NHDT-Date: 1646688070 2022/03/07 21:21:10 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.98 $ */ +/* NetHack 3.7 steal.c $NHDT-Date: 1720895742 2024/07/13 18:35:42 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.132 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" -static int stealarm(void); -static int unstolenarm(void); -static const char *equipname(struct obj *); - -static const char * -equipname(register struct obj* otmp) -{ - return ((otmp == uarmu) ? shirt_simple_name(otmp) - : (otmp == uarmf) ? boots_simple_name(otmp) - : (otmp == uarms) ? shield_simple_name(otmp) - : (otmp == uarmg) ? gloves_simple_name(otmp) - : (otmp == uarmc) ? cloak_simple_name(otmp) - : (otmp == uarmh) ? helm_simple_name(otmp) - : suit_simple_name(otmp)); -} +staticfn int unstolenarm(void); +staticfn int stealarm(void); +staticfn void worn_item_removal(struct monst *, struct obj *); /* proportional subset of gold; return value actually fits in an int */ long @@ -57,8 +45,9 @@ somegold(long lmoney) * pieces. */ struct obj * -findgold(register struct obj* chain, boolean only_coins) +findgold(struct obj* argchain, boolean only_coins) { + struct obj *chain = argchain; /* allow arg to be nonnull */ struct obj* gold = (struct obj *) 0; int ngoldobjs = 0; while (chain) { @@ -81,16 +70,16 @@ findgold(register struct obj* chain, boolean only_coins) * Steal gold coins only. Leprechauns don't care for lesser coins. */ void -stealgold(register struct monst* mtmp) +stealgold(struct monst *mtmp) { - register struct obj *fgold; - register struct obj *ygold; - register long tmp; + struct obj *fgold; + struct obj *ygold; + long tmp; struct monst *who; const char *whose, *what; /* look for gold on the floor */ - fgold = gl.level.objects[u.ux][u.uy]; + fgold = svl.level.objects[u.ux][u.uy]; while (fgold && fgold->material != GOLD) fgold = fgold->nexthere; @@ -144,7 +133,7 @@ stealgold(register struct monst* mtmp) if (!tele_restrict(mtmp)) (void) rloc(mtmp, RLOC_MSG); monflee(mtmp, 0, FALSE, FALSE); - gc.context.botl = 1; + disp.botl = TRUE; } } @@ -176,7 +165,7 @@ unresponsive(void) /* called via (*ga.afternmv)() when hero finishes taking off armor that was slated to be stolen but the thief died in the interim */ -static int +staticfn int unstolenarm(void) { struct obj *obj; @@ -188,28 +177,37 @@ unstolenarm(void) break; gs.stealoid = 0; if (obj) { - You("finish taking off your %s.", equipname(obj)); + You("finish taking off your %s.", armor_simple_name(obj)); } return 0; } -static int +/* finish stealing an item of armor which takes multiple turns to take off */ +staticfn int stealarm(void) { - register struct monst *mtmp; - register struct obj *otmp; + struct monst *mtmp; + 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) { - if (DEADMONSTER(mtmp)) + if (DEADMONSTER(mtmp)) { impossible("stealarm(): dead monster stealing"); - if (!dmgtype(mtmp->data, AD_SITM)) /* polymorphed */ - goto botm; + goto botm; /* (could just use 'break' here) */ + } + /* maybe the thief polymorphed into something without a + steal attack, or perhaps while stealing hero's suit + the thief took away other items causing hero to fall + into water or lava and then teleport to safety */ + if (!dmgtype(mtmp->data, AD_SITM) + || distu(mtmp->mx, mtmp->my) > 2) + goto botm; /* (could just use 'break' here) */ if (otmp->unpaid) subfrombill(otmp, shop_keeper(*u.ushops)); freeinv(otmp); @@ -238,11 +236,31 @@ remove_worn_item( struct obj *obj, boolean unchain_ball) /* whether to unpunish or just unwield */ { + unsigned oldinuse; + if (donning(obj)) cancel_don(); if (!obj->owornmask) return; + /* + * Losing worn gear might drop hero into water or lava or onto a + * location-changing trap or take away the ability to breathe in water. + * Marking it 'in_use' prevents emergency_disrobe() from dropping it + * and lava_effects() from destroying it; other cases impacting object + * location (or destruction) might still have issues. + * + * Note: if a hangup save occurs when 'in_use' is set, the item will + * be destroyed via useup() during restore. Maybe remove_worn_item() + * and emergency_disrobe() should switch to using obj->bypass instead + * but that would need a lot more cooperation by callers. It's a + * tradeoff between protecting the player against unintentional hangup + * and defending the game against deliberate hangup when player sees a + * message about something undesirable followed by --More--. + */ + oldinuse = obj->in_use; + obj->in_use = 1; + if (obj->owornmask & W_ARMOR) { if (obj == uskin) { impossible("Removing embedded scales?"); @@ -287,20 +305,71 @@ remove_worn_item( /* catchall */ setnotworn(obj); } + + if (obj->where == OBJ_DELETED) + debugpline1("remove_worn_item() \"%s\" deleted!", simpleonames(obj)); + obj->in_use = oldinuse; +} + +/* during theft of a worn item: remove_worn_item(), prefaced by a message */ +staticfn void +worn_item_removal( + struct monst *mon, + struct obj *obj) +{ + char objbuf[BUFSZ], article[20], *p; + const char *verb; + int strip_art; + + Strcpy(objbuf, doname(obj)); + /* massage the object description */ + strip_art = !strncmp(objbuf, "the ", 4) ? 4 + : !strncmp(objbuf, "an ", 3) ? 3 + : !strncmp(objbuf, "a ", 2) ? 2 + : 0; + if (strip_art) { /* convert "a/an/the " to "your object" */ + copynchars(article, objbuf, strip_art); + /* when removing attached iron ball, caller passes 'uchain'; + when formatted, it will be "an iron chain (attached to you)"; + change "an" to "the" rather than to "your" in that situation */ + (void) strsubst(objbuf, article, (obj == uchain) ? "the " : "your "); + } + /* these ought to be guarded against matching user-supplied name */ + (void) strsubst(objbuf, " (being worn)", ""); + (void) strsubst(objbuf, " (alternate weapon; not wielded)", ""); + /* convert "ring (on left hand)" to "ring (from left hand)" */ + if ((p = strstri(objbuf, " (on ")) + && (!strncmp(p + 5, "left ", 5) || !strncmp(p + 5, "right ", 6))) + (void) strsubst(p + 2, "on", "from"); + + /* slightly iffy for alternate weapon that isn't actively dual-wielded, + but it's better to alert the player to the change in equipment than + to suppress the message for that case */ + verb = ((obj->owornmask & W_WEAPONS) != 0L) ? "disarms" + : ((obj->owornmask & W_ACCESSORY) != 0L) ? "removes" + : "takes off"; + pline("%s %s %s.", Some_Monnam(mon), verb, objbuf); + iflags.last_msg = PLNMSG_MON_TAKES_OFF_ITEM; + /* removal might trigger more messages (due to loss of Lev|Fly; + descending happens before the theft in progress finishes) */ + remove_worn_item(obj, TRUE); } /* Returns 1 when something was stolen (or at least, when N should flee now), * returns -1 if the monster died in the attempt. * Avoid stealing the object 'stealoid'. - * Nymphs and monkeys won't steal coins. + * Nymphs and monkeys won't steal coins (so that their "steal item" attack + * doesn't become a superset of leprechaun's "steal gold" attack). */ int -steal(struct monst* mtmp, char* objnambuf) +steal(struct monst *mtmp, char *objnambuf) { struct obj *otmp; + char Monnambuf[BUFSZ]; int tmp, could_petrify, armordelay, olddelay, icnt, named = 0, retrycnt = 0; - boolean monkey_business, /* true iff an animal is doing the thievery */ + boolean monkey_business = is_animal(mtmp->data), + seen = canspotmon(mtmp), was_doffing, was_punished = Punished; if (objnambuf) @@ -309,6 +378,14 @@ steal(struct monst* mtmp, char* objnambuf) if (!monnear(mtmp, u.ux, u.uy)) return 0; + /* stealing a worn item might drop hero into water or lava where + teleporting to safety could result in a previously visible thief + no longer being visible; it could also be a case of a blinded + hero being able to see via wearing the Eyes of the Overworld and + having those stolen; remember the name as it is now; if unseen, + nymphs will be "Someone" and monkeys will be "Something" */ + Strcpy(Monnambuf, Some_Monnam(mtmp)); + /* food being eaten might already be used up but will not have been removed from inventory yet; we don't want to steal that, so this will cause it to be removed now */ @@ -317,20 +394,34 @@ steal(struct monst* mtmp, char* objnambuf) icnt = inv_cnt(FALSE); /* don't include gold */ if (!icnt || (icnt == 1 && uskin)) { - nothing_to_steal: /* Not even a thousand men in armor can strip a naked man. */ - if (Blind) + nothing_to_steal: + /* nymphs might target uchain if invent is empty; monkeys won't; + hero becomes unpunished but nymph ends up empty handed */ + if (Punished && !monkey_business && rn2(4)) { + /* uball is not carried (uchain never is) */ + assert(uball != NULL && uball->where == OBJ_FLOOR); + worn_item_removal(mtmp, uchain); + } else if (u.utrap && u.utraptype == TT_BURIEDBALL + && !monkey_business && !rn2(4)) { + boolean dummy; + + /* buried ball is not tracked via 'uball' and there is no chain + at all (hence no uchain to take off) */ + pline("%s takes off your unseen chain.", Monnambuf); + (void) openholdingtrap(&gy.youmonst, &dummy); + } else if (Blind) { pline("Somebody tries to rob you, but finds nothing to steal."); - else if (inv_cnt(TRUE) > inv_cnt(FALSE)) /* ('icnt' might be stale) */ + } else if (inv_cnt(TRUE) > inv_cnt(FALSE)) { pline("%s tries to rob you, but isn't interested in gold.", - Monnam(mtmp)); - else + Monnambuf); + } else { pline("%s tries to rob you, but there is nothing to steal!", - Monnam(mtmp)); + Monnambuf); + } return 1; /* let her flee */ } - monkey_business = is_animal(mtmp->data); if (monkey_business || uarmg) { ; /* skip ring special cases */ } else if (Adornment & LEFT_RING) { @@ -398,18 +489,20 @@ steal(struct monst* mtmp, char* objnambuf) ostuck = ((otmp->cursed && otmp->owornmask) /* nymphs can steal rings from under cursed weapon but animals can't */ - || (otmp == uright && welded(uwep)) - || (otmp == uleft && welded(uwep) && bimanual(uwep)) + || (otmp == RING_ON_PRIMARY && welded(uwep)) + || (otmp == RING_ON_SECONDARY && welded(uwep) + && bimanual(uwep)) || undroppable(otmp)); if (ostuck || can_carry(mtmp, otmp) == 0) { - static const char *const how[] = { "steal", "snatch", "grab", - "take" }; + static const char *const how[] = { + "steal", "snatch", "grab", "take" + }; cant_take: - pline("%s tries to %s %s%s but gives up.", Monnam(mtmp), - how[rn2(SIZE(how))], + pline("%s tries to %s %s%s but gives up.", Monnambuf, + ROLL_FROM(how), (otmp->owornmask & W_ARMOR) ? "your " : "", - (otmp->owornmask & W_ARMOR) ? equipname(otmp) + (otmp->owornmask & W_ARMOR) ? armor_simple_name(otmp) : yname(otmp)); /* the fewer items you have, the less likely the thief is going to stick around to try again (0) instead of @@ -437,7 +530,7 @@ steal(struct monst* mtmp, char* objnambuf) case AMULET_CLASS: case RING_CLASS: case FOOD_CLASS: /* meat ring */ - remove_worn_item(otmp, TRUE); + worn_item_removal(mtmp, otmp); break; case ARMOR_CLASS: armordelay = objects[otmp->otyp].oc_delay; @@ -449,23 +542,22 @@ steal(struct monst* mtmp, char* objnambuf) hero can't be charmed into taking off his own armor */ if (armordelay >= 1 && !olddelay && rn2(10)) goto cant_take; - remove_worn_item(otmp, TRUE); + worn_item_removal(mtmp, otmp); break; } else { int curssv = otmp->cursed; int slowly; - boolean seen = canspotmon(mtmp); otmp->cursed = 0; slowly = (armordelay >= 1 || gm.multi < 0); if (flags.female) urgent_pline("%s charms you. You gladly %s your %s.", - !seen ? "She" : Monnam(mtmp), + !seen ? "She" : Monnambuf, curssv ? "let her take" : !slowly ? "hand over" : was_doffing ? "continue removing" : "start removing", - equipname(otmp)); + armor_simple_name(otmp)); else urgent_pline("%s seduces you and %s off your %s.", !seen ? "She" : Adjmonnam(mtmp, "beautiful"), @@ -473,7 +565,7 @@ steal(struct monst* mtmp, char* objnambuf) : !slowly ? "you take" : was_doffing ? "you continue taking" : "you start taking", - equipname(otmp)); + armor_simple_name(otmp)); named++; /* the following is to set multi for later on */ nomul(-armordelay); @@ -493,8 +585,25 @@ steal(struct monst* mtmp, char* objnambuf) impossible("Tried to steal a strange worn thing. [%d]", otmp->oclass); } - } else if (otmp->owornmask) /* weapon or ball&chain */ - remove_worn_item(otmp, TRUE); + /* hero's blindfold might have just been stolen; if so, replace + cached "Someone" or "Something" with Monnam */ + if (!seen && canspotmon(mtmp)) + Strcpy(Monnambuf, Monnam(mtmp)); + } else if (otmp->owornmask) { /* weapon or ball&chain */ + struct obj *item = otmp; + + if (otmp == uball) /* non-Null uball implies non-Null uchain */ + item = uchain; /* yields a more accurate 'takes off' message */ + worn_item_removal(mtmp, item); + /* if we switched from uball to uchain for the preface message, + then unpunish() took place and both those pointers are now Null, + with 'item' a stale pointer to freed chain; the ball is still + present though and 'otmp' is still valid; if uball was also + wielded or quivered, the corresponding weapon pointer hasn't + been cleared yet; do that, with no preface message this time */ + if ((otmp->owornmask & W_WEAPONS) != 0L) + remove_worn_item(otmp, FALSE); + } /* do this before removing it from inventory */ if (objnambuf) @@ -507,13 +616,17 @@ steal(struct monst* mtmp, char* objnambuf) if (otmp->unpaid) subfrombill(otmp, shop_keeper(*u.ushops)); freeinv(otmp); - /* if attached ball was taken, uball and uchain are now Null */ - urgent_pline("%s%s stole %s.", named ? "She" : Monnam(mtmp), - (was_punished && !Punished) ? " removed your chain and" : "", - doname(otmp)); + + /* if we just gave a message about removing a worn item and there have + been no intervening messages, shorten ' stole ' message */ + if (iflags.last_msg == PLNMSG_MON_TAKES_OFF_ITEM + && mtmp->data->mlet == S_NYMPH) + ++named; + urgent_pline("%s stole %s.", named ? "She" : Monnambuf, doname(otmp)); (void) encumber_msg(); could_petrify = (otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm])); + otmp->how_lost = LOST_STOLEN; (void) mpickobj(mtmp, otmp); /* may free otmp */ if (could_petrify && !(mtmp->misc_worn_check & W_ARMG)) { minstapetrify(mtmp, TRUE); @@ -571,17 +684,29 @@ 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 */ - if (!mtmp->mtame) + /* 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 monster is unseen, 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 because - vision is disabled), or when held (or poly'd and holding) while blind, - behave as if the monster can be 'seen' by touch */ - if (!mtmp->mtame && !(canseemon(mtmp) || mtmp == u.ustuck)) - unknow_object(otmp); + /* 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 + because vision is disabled), or when held (or poly'd and holding) + while blind, behave as if the monster can be 'seen' by touch */ + if (!canseemon(mtmp) && mtmp != u.ustuck) + unknow_object(otmp); + /* if otmp has flags set for how it left hero's inventory, change + those flags; if thrown, now stolen and autopickup might override + pickup_types and autopickup exceptions based on 'pickup_stolen' + rather than 'pickup_thrown'; if previously stolen, stays stolen; + if previously dropped, now forgotten and autopickup will operate + normally regardless of the setting for 'dropped_nopick' */ + if (otmp->how_lost == LOST_THROWN) + otmp->how_lost = LOST_STOLEN; + else if (otmp->how_lost == LOST_DROPPED) + otmp->how_lost = LOST_NONE; + } /* Must do carrying effects on object prior to add_to_minv() */ carry_obj_effects(otmp); /* add_to_minv() might free otmp [if merged with something else], @@ -596,27 +721,28 @@ mpickobj(struct monst *mtmp, struct obj *otmp) /* Take off gear which is blocking the target obj from being removed [by a quest * artifact steal, or a demon lord interested in the item] */ void -remove_outer_gear(struct obj *target) +remove_outer_gear(struct monst *remover, struct obj *target) { if ((target == uarm || target == uarmu) && uarmc) - remove_worn_item(uarmc, FALSE); + worn_item_removal(remover, uarmc); if (target == uarmu && uarm) - remove_worn_item(uarm, FALSE); + worn_item_removal(remover, uarm); if ((target == uarmg || ((target == uright || target == uleft) && uarmg)) && uwep) { /* gloves are about to be unworn; unwield weapon(s) first */ if (u.twoweap) /* remove_worn_item(uswapwep) indirectly */ - remove_worn_item(uswapwep, FALSE); /* clears u.twoweap */ - remove_worn_item(uwep, FALSE); + worn_item_removal(remover, uswapwep); + worn_item_removal(remover, uwep); } - if ((target == uright || target == uleft) && uarmg) + if ((target == uright || target == uleft) && uarmg) { /* calls Gloves_off() to handle wielded cockatrice corpse */ - remove_worn_item(uarmg, FALSE); + worn_item_removal(remover, uarmg); + } } /* called for AD_SAMU (the Wizard and quest nemeses) */ void -stealamulet(struct monst* mtmp) +stealamulet(struct monst *mtmp) { char buf[BUFSZ]; struct obj *otmp = 0, *obj = 0; @@ -664,17 +790,17 @@ stealamulet(struct monst* mtmp) } if (otmp) { /* we have something to snatch */ - remove_outer_gear(otmp); + remove_outer_gear(mtmp, otmp); /* finally, steal the target item */ if (otmp->owornmask) - remove_worn_item(otmp, TRUE); + worn_item_removal(mtmp, otmp); if (otmp->unpaid) subfrombill(otmp, shop_keeper(*u.ushops)); freeinv(otmp); Strcpy(buf, doname(otmp)); (void) mpickobj(mtmp, otmp); /* could merge and free otmp but won't */ - pline("%s steals %s!", Monnam(mtmp), buf); + pline("%s steals %s!", Some_Monnam(mtmp), buf); if (can_teleport(mtmp->data) && !tele_restrict(mtmp)) (void) rloc(mtmp, RLOC_MSG); (void) encumber_msg(); @@ -748,7 +874,7 @@ mdrop_obj( } /* obj_no_longer_held(obj); -- done by place_object */ if (verbosely && cansee(omx, omy)) - pline("%s drops %s.", Monnam(mon), obj_name); + pline_mon(mon, "%s drops %s.", Monnam(mon), obj_name); if (!flooreffects(obj, omx, omy, "fall")) { place_object(obj, omx, omy); stackobj(obj); @@ -811,7 +937,7 @@ relobj( && inhishop(shkp)) { subfrombill(otmp, shkp); } - mdrop_obj(mtmp, otmp, is_pet && Verbose(1, relobj)); + mdrop_obj(mtmp, otmp, is_pet && flags.verbose); } if (show && cansee(omx, omy)) diff --git a/src/steed.c b/src/steed.c index 2a955945e5..86a458b6a1 100644 --- a/src/steed.c +++ b/src/steed.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 steed.c $NHDT-Date: 1671838909 2022/12/23 23:41:49 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.108 $ */ +/* NetHack 3.7 steed.c $NHDT-Date: 1720128167 2024/07/04 21:22:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.121 $ */ /* Copyright (c) Kevin Hugo, 1998-1999. */ /* NetHack may be freely redistributed. See license for details. */ @@ -9,8 +9,8 @@ static NEARDATA const char steeds[] = { S_QUADRUPED, S_UNICORN, S_ANGEL, S_CENTAUR, S_DRAGON, S_JABBERWOCK, '\0' }; -static boolean landing_spot(coord *, int, int); -static void maybewakesteed(struct monst *); +staticfn boolean landing_spot(coord *, int, int); +staticfn void maybewakesteed(struct monst *); /* caller has decided that hero can't reach something while mounted */ void @@ -23,7 +23,7 @@ rider_cant_reach(void) /* Can this monster wear a saddle? */ boolean -can_saddle(struct monst* mtmp) +can_saddle(struct monst *mtmp) { struct permonst *ptr = mtmp->data; @@ -33,7 +33,7 @@ can_saddle(struct monst* mtmp) } int -use_saddle(struct obj* otmp) +use_saddle(struct obj *otmp) { struct monst *mtmp; struct permonst *ptr; @@ -139,7 +139,7 @@ use_saddle(struct obj* otmp) } void -put_saddle_on_mon(struct obj* saddle, struct monst* mtmp) +put_saddle_on_mon(struct obj *saddle, struct monst *mtmp) { if (!can_saddle(mtmp) || which_armor(mtmp, W_SADDLE)) return; @@ -155,7 +155,7 @@ put_saddle_on_mon(struct obj* saddle, struct monst* mtmp) /* Can we ride this monster? Caller should also check can_saddle() */ boolean -can_ride(struct monst* mtmp) +can_ride(struct monst *mtmp) { return (mtmp->mtame && humanoid(gy.youmonst.data) && !verysmall(gy.youmonst.data) && !bigmonst(gy.youmonst.data) @@ -227,8 +227,10 @@ mount_steed( return (FALSE); } - if (Upolyd && (!humanoid(gy.youmonst.data) || verysmall(gy.youmonst.data) - || bigmonst(gy.youmonst.data) || slithy(gy.youmonst.data))) { + if (Upolyd && (!humanoid(gy.youmonst.data) + || verysmall(gy.youmonst.data) + || bigmonst(gy.youmonst.data) + || slithy(gy.youmonst.data))) { You("won't fit on a saddle."); return (FALSE); } @@ -270,6 +272,7 @@ mount_steed( pline("%s is not saddled.", Monnam(mtmp)); return (FALSE); } + ptr = mtmp->data; if (touch_petrifies(ptr) && !Stone_resistance) { char kbuf[BUFSZ]; @@ -355,9 +358,16 @@ mount_steed( if (uwep && is_pole(uwep)) gu.unweapon = FALSE; u.usteed = mtmp; + { + boolean was_stealthy = Stealth != 0; + + steed_vs_stealth(); + if (was_stealthy && !Stealth) + You("aren't stealthy anymore."); + } remove_monster(mtmp->mx, mtmp->my); teleds(mtmp->mx, mtmp->my, TELEDS_ALLOW_DRAG); - gc.context.botl = TRUE; + disp.botl = TRUE; return TRUE; } @@ -435,7 +445,7 @@ kick_steed(void) * room's walls, which is not what we want. * Adapted from mail daemon code. */ -static boolean +staticfn boolean landing_spot( coord *spot, /* landing position (we fill it in) */ int reason, @@ -589,6 +599,7 @@ dismount_steed( switch (reason) { case DISMOUNT_THROWN: verb = "are thrown"; + FALLTHROUGH; /*FALLTHRU*/ case DISMOUNT_KNOCKED: case DISMOUNT_FELL: @@ -641,9 +652,16 @@ dismount_steed( if (repair_leg_damage) heal_legs(1); - /* Release the steed and saddle */ - u.usteed = 0; + /* Release the steed */ + u.usteed = (struct monst *) NULL; u.ugallop = 0L; + { + boolean was_stealthy = Stealth != 0; + + steed_vs_stealth(); + if (Stealth && !was_stealthy) + You("seem less noisy now."); + } if (u.utraptype == TT_BEARTRAP || u.utraptype == TT_PIT @@ -792,11 +810,11 @@ dismount_steed( gi.in_steed_dismounting = TRUE; (void) float_down(0L, W_SADDLE); gi.in_steed_dismounting = FALSE; - gc.context.botl = TRUE; + disp.botl = TRUE; (void) encumber_msg(); gv.vision_full_recalc = 1; } else - gc.context.botl = TRUE; + disp.botl = TRUE; /* polearms behave differently when not mounted */ if (uwep && is_pole(uwep)) gu.unweapon = TRUE; @@ -805,8 +823,8 @@ dismount_steed( /* when attempting to saddle or mount a sleeping steed, try to wake it up (for the saddling case, it won't be u.usteed yet) */ -static void -maybewakesteed(struct monst* steed) +staticfn void +maybewakesteed(struct monst *steed) { int frozen = (int) steed->mfrozen; boolean wasimmobile = helpless(steed); @@ -829,6 +847,31 @@ maybewakesteed(struct monst* steed) finish_meating(steed); } +/* steed has taken on a new shape */ +void +poly_steed( + struct monst *steed, + struct permonst *oldshape) +{ + if (!can_saddle(steed) || !can_ride(steed)) { + /* can't get here; newcham() -> mon_break_armor() -> m_lose_armor() + removes saddle and/or forces hero to dismount, if applicable, + before newcham() calls us */ + dismount_steed(DISMOUNT_FELL); + } else { + char buf[BUFSZ]; + + Strcpy(buf, x_monnam(steed, ARTICLE_YOUR, (char *) 0, + SUPPRESS_SADDLE, FALSE)); + if (oldshape != steed->data) + (void) strsubst(buf, "your ", "your new "); + You("adjust yourself in the saddle on %s.", buf); + + /* riding blocks stealth unless hero+steed fly */ + steed_vs_stealth(); + } +} + /* decide whether hero's steed is able to move; doesn't check for holding traps--those affect the hero directly */ boolean @@ -839,12 +882,12 @@ stucksteed(boolean checkfeeding) if (steed) { /* check whether steed can move */ if (helpless(steed)) { - pline("%s won't move!", upstart(y_monnam(steed))); + pline("%s won't move!", YMonnam(steed)); return TRUE; } /* optionally check whether steed is in the midst of a meal */ if (checkfeeding && steed->meating) { - pline("%s is still eating.", upstart(y_monnam(steed))); + pline("%s is still eating.", YMonnam(steed)); return TRUE; } } @@ -852,7 +895,7 @@ stucksteed(boolean checkfeeding) } void -place_monster(struct monst* mon, coordxy x, coordxy y) +place_monster(struct monst *mon, coordxy x, coordxy y) { struct monst *othermon; const char *monnm, *othnm; @@ -880,7 +923,7 @@ place_monster(struct monst* mon, coordxy x, coordxy y) mon->mstate, buf); return; } - if ((othermon = gl.level.monsters[x][y]) != 0) { + if ((othermon = svl.level.monsters[x][y]) != 0) { describe_level(buf, 0); monnm = minimal_monnam(mon, FALSE); othnm = (mon != othermon) ? minimal_monnam(othermon, TRUE) : "itself"; @@ -888,9 +931,8 @@ place_monster(struct monst* mon, coordxy x, coordxy y) monnm, othnm, x, y, othermon->mstate, mon->mstate, buf); } mon->mx = x, mon->my = y; - gl.level.monsters[x][y] = mon; - mon->mstate &= ~(MON_OFFMAP | MON_MIGRATING | MON_LIMBO | MON_BUBBLEMOVE - | MON_ENDGAME_FREE | MON_ENDGAME_MIGR); + svl.level.monsters[x][y] = mon; + mon->mstate = MON_FLOOR; } /*steed.c*/ diff --git a/src/strutil.c b/src/strutil.c new file mode 100644 index 0000000000..c35d22182a --- /dev/null +++ b/src/strutil.c @@ -0,0 +1,158 @@ +/* NetHack 3.7 strutil.c $NHDT-Date: 1709571807 2024/03/04 17:03:27 $ $NHDT-Branch: keni-mdlib-followup $:$NHDT-Revision: 1.0 $ */ +/* Copyright (c) Robert Patrick Rankin, 1991 */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" /* for config.h+extern.h */ + +/* strbuf_init() initializes strbuf state for use */ +void +strbuf_init(strbuf_t *strbuf) +{ + strbuf->str = NULL; + strbuf->len = 0; +} + +/* strbuf_append() appends given str to strbuf->str */ +void +strbuf_append(strbuf_t *strbuf, const char *str) +{ + int len = (int) strlen(str) + 1; + + strbuf_reserve(strbuf, + len + (strbuf->str ? (int) strlen(strbuf->str) : 0)); + Strcat(strbuf->str, str); +} + +/* strbuf_reserve() ensure strbuf->str has storage for len characters */ +void +strbuf_reserve(strbuf_t *strbuf, int len) +{ + if (strbuf->str == NULL) { + strbuf->str = strbuf->buf; + strbuf->str[0] = '\0'; + strbuf->len = (int) sizeof strbuf->buf; + } + + if (len > strbuf->len) { + char *oldbuf = strbuf->str; + + strbuf->len = len + (int) sizeof strbuf->buf; + strbuf->str = (char *) alloc(strbuf->len); + Strcpy(strbuf->str, oldbuf); + if (oldbuf != strbuf->buf) + free((genericptr_t) oldbuf); + } +} + +/* strbuf_empty() frees allocated memory and set strbuf to initial state */ +void +strbuf_empty(strbuf_t *strbuf) +{ + if (strbuf->str != NULL && strbuf->str != strbuf->buf) + free((genericptr_t) strbuf->str); + strbuf_init(strbuf); +} + +/* strbuf_nl_to_crlf() converts all occurrences of \n to \r\n */ +void +strbuf_nl_to_crlf(strbuf_t *strbuf) +{ + if (strbuf->str) { + int len = (int) strlen(strbuf->str); + int count = 0; + char *cp = strbuf->str; + + while (*cp) + if (*cp++ == '\n') + count++; + if (count) { + strbuf_reserve(strbuf, len + count + 1); + for (cp = strbuf->str + len + count; count; --cp) + if ((*cp = cp[-count]) == '\n') { + *--cp = '\r'; + --count; + } + } + } +} + +/* strlen() but returns unsigned and panics if string is unreasonably long; + used by dlb as well as by nethack */ +unsigned +Strlen_( + const char *str, + const char *file, + int line) +{ + const char *p; + size_t len; + + /* strnlen(str, LARGEST_INT) w/o requiring posix.1 headers or libraries */ + for (p = str, len = 0; len < LARGEST_INT; ++len) + if (*p++ == '\0') + break; + + if (len == LARGEST_INT) + panic("%s:%d string too long", file, line); + return (unsigned) len; +} + +staticfn boolean pmatch_internal(const char *, const char *, boolean, + const char *); +/* guts of pmatch(), pmatchi(), and pmatchz(); + match a string against a pattern */ +staticfn boolean +pmatch_internal(const char *patrn, const char *strng, + boolean ci, /* True => case-insensitive, + False => case-sensitive */ + const char *sk) /* set of characters to skip */ +{ + char s, p; + /* + * Simple pattern matcher: '*' matches 0 or more characters, '?' matches + * any single character. Returns TRUE if 'strng' matches 'patrn'. + */ + pmatch_top: + if (!sk) { + s = *strng++; + p = *patrn++; /* get next chars and pre-advance */ + } else { + /* fuzzy match variant of pmatch; particular characters are ignored */ + do { + s = *strng++; + } while (strchr(sk, s)); + do { + p = *patrn++; + } while (strchr(sk, p)); + } + if (!p) /* end of pattern */ + return (boolean) (s == '\0'); /* matches iff end of string too */ + else if (p == '*') /* wildcard reached */ + return (boolean) ((!*patrn + || pmatch_internal(patrn, strng - 1, ci, sk)) + ? TRUE + : s ? pmatch_internal(patrn - 1, strng, ci, sk) + : FALSE); + else if ((ci ? lowc(p) != lowc(s) : p != s) /* check single character */ + && (p != '?' || !s)) /* & single-char wildcard */ + return FALSE; /* doesn't match */ + else /* return pmatch_internal(patrn, strng, ci, sk); */ + goto pmatch_top; /* optimize tail recursion */ +} + +/* case-sensitive wildcard match */ +boolean +pmatch(const char *patrn, const char *strng) +{ + return pmatch_internal(patrn, strng, FALSE, (const char *) 0); +} + +/* case-insensitive wildcard match */ +boolean +pmatchi(const char *patrn, const char *strng) +{ + return pmatch_internal(patrn, strng, TRUE, (const char *) 0); +} + + +/*strutil.c*/ diff --git a/src/symbols.c b/src/symbols.c index fed4f4d608..67e71a5f66 100644 --- a/src/symbols.c +++ b/src/symbols.c @@ -1,25 +1,22 @@ -/* NetHack 3.7 symbols.c $NHDT-Date: 1661295669 2022/08/23 23:01:09 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.90 $ */ +/* 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. */ #include "hack.h" #include "tcap.h" -static void savedsym_add(const char *, const char *, int); -static struct _savedsym *savedsym_find(const char *, int); -#ifdef ENHANCED_SYMBOLS -static void purge_custom_entries(enum graphics_sets which_set); -#endif +staticfn void savedsym_add(const char *, const char *, int); +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; @@ -28,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 @@ -85,7 +82,7 @@ init_symbols(void) void init_showsyms(void) { - register int i; + int i; for (i = 0; i < MAXPCHARS; i++) gs.showsyms[i + SYM_OFF_P] = defsyms[i].sym; @@ -103,7 +100,7 @@ init_showsyms(void) void init_ov_primary_symbols(void) { - register int i; + int i; for (i = 0; i < SYM_MAX; i++) go.ov_primary_syms[i] = (nhsym) 0; @@ -144,7 +141,7 @@ get_othersym(int idx, int which_set UNUSED) void init_primary_symbols(void) { - register int i; + int i; for (i = 0; i < MAXPCHARS; i++) gp.primary_syms[i + SYM_OFF_P] = defsyms[i].sym; @@ -163,7 +160,7 @@ init_primary_symbols(void) void assign_graphics(int whichset) { - register int i; + int i; switch (whichset) { case PRIMARYSET: @@ -185,7 +182,7 @@ assign_graphics(int whichset) void switch_symbols(int nondefault) { - register int i; + int i; if (nondefault) { for (i = 0; i < SYM_MAX; i++) @@ -225,7 +222,7 @@ switch_symbols(int nondefault) } void -update_ov_primary_symset(const struct symparse* symp, int val) +update_ov_primary_symset(const struct symparse *symp, int val) { go.ov_primary_syms[symp->idx] = val; } @@ -239,6 +236,11 @@ update_primary_symset(const struct symparse* symp, int val) void clear_symsetentry(int which_set, boolean name_too) { +#ifdef ENHANCED_SYMBOLS + int other_set = (which_set == PRIMARYSET) ? ROGUESET : PRIMARYSET; + enum symset_handling_types old_handling = gs.symset[which_set].handling; +#endif + if (gs.symset[which_set].desc) free((genericptr_t) gs.symset[which_set].desc); gs.symset[which_set].desc = (char *) 0; @@ -254,9 +256,13 @@ clear_symsetentry(int which_set, boolean name_too) gs.symset[which_set].name = (char *) 0; } #ifdef ENHANCED_SYMBOLS - free_all_glyphmap_u(); - purge_custom_entries(which_set); + /* if 'which_set' was using UTF8, it isn't anymore; if the other set + isn't using UTF8, discard the data for that */ + if (old_handling == H_UTF8 && gs.symset[other_set].handling != H_UTF8) + free_all_glyphmap_u(); #endif + purge_custom_entries(which_set); + clear_all_glyphmap_colors(); } /* called from windmain.c */ @@ -266,7 +272,7 @@ symset_is_compatible( unsigned long wincap2) { #ifdef ENHANCED_SYMBOLS -#define WC2_utf8_bits (WC2_U_UTF8STR | WC2_U_24BITCOLOR) +#define WC2_utf8_bits (WC2_U_UTF8STR) if (handling == H_UTF8 && ((wincap2 & WC2_utf8_bits) != WC2_utf8_bits)) return FALSE; #undef WC2_bits @@ -282,6 +288,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. */ const char *const known_handling[] = { "UNKNOWN", /* H_UNK */ @@ -334,7 +341,7 @@ const struct symparse loadsyms[] = { { SYM_OTH, SYM_INVISIBLE + SYM_OFF_X, "S_invisible" }, { SYM_OTH, SYM_PET_OVERRIDE + SYM_OFF_X, "S_pet_override" }, { SYM_OTH, SYM_HERO_OVERRIDE + SYM_OFF_X, "S_hero_override" }, - { 0, 0, (const char *) 0 } /* fence post */ + { SYM_INVALID, 0, (const char *) 0 } /* fence post */ }; boolean @@ -370,8 +377,10 @@ parse_sym_line(char *buf, int which_set) /* find the '=' or ':' */ bufp = strchr(buf, '='); altp = strchr(buf, ':'); + if (!bufp || (altp && altp < bufp)) bufp = altp; + if (!bufp) { if (strncmpi(buf, "finish", 6) == 0) { /* end current graphics set */ @@ -388,15 +397,15 @@ parse_sym_line(char *buf, int which_set) if (*bufp == ' ') ++bufp; - symp = match_sym(buf); if (!symp && buf[0] == 'G' && buf[1] == '_') { -#ifdef ENHANCED_SYMBOLS if (gc.chosen_symset_start) { is_glyph = match_glyph(buf); } else { is_glyph = TRUE; /* report error only once */ } +#ifdef ENHANCED_SYMBOLS + enhanced_unavailable = FALSE; #else enhanced_unavailable = TRUE; #endif @@ -438,7 +447,8 @@ parse_sym_line(char *buf, int which_set) tmpsp = lastsp; /* most recent symset */ for (i = 0; known_handling[i]; ++i) if (!strcmpi(known_handling[i], bufp)) { - tmpsp->handling = i; + if (tmpsp) + tmpsp->handling = i; break; /* for loop */ } break; @@ -453,13 +463,15 @@ parse_sym_line(char *buf, int which_set) tmpsp = lastsp; /* most recent symset */ for (i = 0; known_restrictions[i]; ++i) { if (!strcmpi(known_restrictions[i], bufp)) { - switch (i) { - case 0: - tmpsp->primary = 1; - break; - case 1: - tmpsp->rogue = 1; - break; + if (tmpsp) { + switch (i) { + case 0: + tmpsp->primary = 1; + break; + case 1: + tmpsp->rogue = 1; + break; + } } break; /* while loop */ } @@ -530,8 +542,8 @@ parse_sym_line(char *buf, int which_set) } else { /* Not SYM_CONTROL */ if (gs.symset[which_set].handling != H_UTF8) { - val = sym_val(bufp); if (gc.chosen_symset_start) { + val = sym_val(bufp); if (which_set == PRIMARYSET) { update_primary_symset(symp, val); } @@ -544,6 +556,9 @@ parse_sym_line(char *buf, int which_set) #endif } } + } else if (gc.chosen_symset_start) { + /* glyph, not symbol */ + glyphrep_to_custom_map_entries(buf, &glyph); } #ifndef ENHANCED_SYMBOLS nhUse(glyph); @@ -578,6 +593,8 @@ load_symset(const char *s, int which_set) if (read_sym_file(which_set)) { switch_symbols(TRUE); + apply_customizations(gc.currentgraphics, + do_custom_symbols | do_custom_colors); } else { clear_symsetentry(which_set, TRUE); return 0; @@ -617,7 +634,7 @@ savedsym_free(void) } } -static struct _savedsym * +staticfn struct _savedsym * savedsym_find(const char *name, int which_set) { struct _savedsym *tmp = saved_symbols; @@ -630,7 +647,7 @@ savedsym_find(const char *name, int which_set) return NULL; } -static void +staticfn void savedsym_add(const char *name, const char *val, int which_set) { struct _savedsym *tmp = NULL; @@ -665,28 +682,46 @@ savedsym_strbuf(strbuf_t *sbuf) /* Parse the value of a SYMBOLS line from a config file */ boolean -parsesymbols(register char *opts, int which_set) +parsesymbols(char *opts, int which_set) { int val; - char *op, *symname, *strval; + char *symname, *strval, *ch, + *first_unquoted_comma = 0, *first_unquoted_colon = 0; const struct symparse *symp; boolean is_glyph = FALSE; - /* - * FIXME: - * The parsing here (and next) yields incorrect results for - * "S_sample=','" or "S_sample=':'". - */ + /* are there any commas or colons that aren't quoted? */ + for (ch = opts + 1; *ch; ++ch) { + char *prech, *postch; - if ((op = strchr(opts, ',')) != 0) { - *op++ = '\0'; - if (!parsesymbols(op, which_set)) + prech = ch - 1; + postch = ch + 1; + if (!*postch) + break; + if (*ch == ',') { + if (*prech == '\'' && *postch == '\'') + continue; + if (*prech == '\\') + continue; + } + if (*ch == ':') { + if (*prech == '\'' && *postch == '\'') + continue; + } + if (*ch == ',' && !first_unquoted_comma) + first_unquoted_comma = ch; + if (*ch == ':' && !first_unquoted_colon) + first_unquoted_colon = ch; + } + if (first_unquoted_comma != 0) { + *first_unquoted_comma++ = '\0'; + if (!parsesymbols(first_unquoted_comma, which_set)) return FALSE; } /* S_sample:string */ symname = opts; - strval = strchr(opts, ':'); + strval = first_unquoted_colon; if (!strval) strval = strchr(opts, '='); if (!strval) @@ -698,24 +733,20 @@ parsesymbols(register char *opts, int which_set) mungspaces(strval); symp = match_sym(symname); -#ifdef ENHANCED_SYMBOLS if (!symp && symname[0] == 'G' && symname[1] == '_') { is_glyph = match_glyph(symname); } -#endif if (!symp && !is_glyph) return FALSE; if (symp) { if (symp->range && symp->range != SYM_CONTROL) { if (gs.symset[which_set].handling == H_UTF8 || (lowc(strval[0]) == 'u' && strval[1] == '+')) { -#ifdef ENHANCED_SYMBOLS char buf[BUFSZ]; int glyph; Snprintf(buf, sizeof buf, "%s:%s", opts, strval); glyphrep_to_custom_map_entries(buf, &glyph); -#endif /* ENHANCED_SYMBOLS */ } else { val = sym_val(strval); update_ov_primary_symset(symp, val); @@ -729,17 +760,19 @@ parsesymbols(register char *opts, int which_set) const struct symparse * match_sym(char *buf) { - int i; - struct alternate_parse { + static struct alternate_parse { const char *altnm; const char *nm; } alternates[] = { - { "S_armour", "S_armor" }, { "S_explode1", "S_expl_tl" }, + { "S_armour", "S_armor" }, + /* alt explosion names are numbered in phone key/button layout */ + { "S_explode1", "S_expl_tl" }, { "S_explode2", "S_expl_tc" }, { "S_explode3", "S_expl_tr" }, { "S_explode4", "S_expl_ml" }, { "S_explode5", "S_expl_mc" }, { "S_explode6", "S_expl_mr" }, { "S_explode7", "S_expl_bl" }, { "S_explode8", "S_expl_bc" }, { "S_explode9", "S_expl_br" }, }; + int i; size_t len = strlen(buf); const char *p = strchr(buf, ':'), *q = strchr(buf, '='); const struct symparse *sp = loadsyms; @@ -794,7 +827,7 @@ do_symset(void) char *symset_name, fmtstr[20]; struct symsetentry *sl; int res, which_set, setcount = 0, chosen = -2, defindx = 0; - int clr = 0; + int clr = NO_COLOR; which_set = PRIMARYSET; gs.symset_list = (struct symsetentry *) 0; @@ -940,17 +973,13 @@ do_symset(void) if (gs.symset[which_set].name) { /* non-default symbols */ int ok; -#ifdef ENHANCED_SYMBOLS if (!glyphid_cache_status()) { fill_glyphid_cache(); } -#endif ok = read_sym_file(which_set); -#ifdef ENHANCED_SYMBOLS if (glyphid_cache_status()) { free_glyphid_cache(); } -#endif if (ok) { ready_to_switch = TRUE; } else { @@ -963,173 +992,11 @@ do_symset(void) switch_symbols(TRUE); assign_graphics(PRIMARYSET); -#ifdef ENHANCED_SYMBOLS - apply_customizations_to_symset(PRIMARYSET); -#endif + apply_customizations(PRIMARYSET, (do_custom_symbols | do_custom_colors)); preference_update("symset"); return TRUE; } -#ifdef ENHANCED_SYMBOLS - RESTORE_WARNING_FORMAT_NONLITERAL -struct customization_detail *find_display_sym_customization( - const char *customization_name, const struct symparse *symparse, - enum graphics_sets which_set); -struct customization_detail *find_matching_symset_customiz( - const char *customization_name, int custtype, - enum graphics_sets which_set); -struct customization_detail *find_display_urep_customization( - const char *customization_name, int glyphidx, - enum graphics_sets which_set); -extern glyph_map glyphmap[MAX_GLYPH]; -static void shuffle_customizations(void); - -void -apply_customizations_to_symset(enum graphics_sets which_set) -{ - glyph_map *gmap; - struct customization_detail *details; - - if (gs.symset[which_set].handling == H_UTF8 - && gs.sym_customizations[which_set].count - && gs.sym_customizations[which_set].details) { - /* These UTF-8 customizations get applied to the glyphmap array, - not to symset entries */ - details = gs.sym_customizations[which_set].details; - while (details) { - gmap = &glyphmap[details->content.urep.glyphidx]; - (void) set_map_u(gmap, - details->content.urep.u.utf32ch, - details->content.urep.u.utf8str, - details->content.urep.u.ucolor); - details = details->next; - } - shuffle_customizations(); - } -} -/* Shuffle the customizations to match shuffled object descriptions, - * so a red potion isn't displayed with a blue customization, and so on. - */ -static void -shuffle_customizations(void) -{ - static const int offsets[2] = { GLYPH_OBJ_OFF, GLYPH_OBJ_PILETOP_OFF }; - int j; - - for (j = 0; j < SIZE(offsets); j++) { - glyph_map *obj_glyphs = glyphmap + offsets[j]; - int i; - struct unicode_representation *tmp_u[NUM_OBJECTS]; - int duplicate[NUM_OBJECTS]; - - for (i = 0; i < NUM_OBJECTS; i++) { - duplicate[i] = -1; - tmp_u[i] = (struct unicode_representation *) 0; - } - for (i = 0; i < NUM_OBJECTS; i++) { - int idx = objects[i].oc_descr_idx; - - /* - * Shuffling gem appearances can cause the same oc_descr_idx to - * appear more than once. Detect this condition and ensure that - * each pointer points to a unique allocation. - */ - if (duplicate[idx] >= 0) { - /* Current structure already appears in tmp_u */ - struct unicode_representation *other = tmp_u[duplicate[idx]]; - - 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); - } - } else { - tmp_u[i] = obj_glyphs[idx].u; - if (obj_glyphs[idx].u != NULL) { - duplicate[idx] = i; - obj_glyphs[idx].u = NULL; - } - } - } - for (i = 0; i < NUM_OBJECTS; i++) { - /* Some glyphmaps may not have been transferred */ - if (obj_glyphs[i].u != NULL) { - free(obj_glyphs[i].u->utf8str); - free(obj_glyphs[i].u); - } - obj_glyphs[i].u = tmp_u[i]; - } - } -} - -struct customization_detail * -find_matching_symset_customiz( - const char *customization_name, - int custtype, - enum graphics_sets which_set) -{ - struct symset_customization *gdc = &gs.sym_customizations[which_set]; - - if ((gdc->custtype == custtype) - && (strcmp(customization_name, gdc->customization_name) != 0)) - return gdc->details; - return (struct customization_detail *) 0; -} - -static void -purge_custom_entries(enum graphics_sets which_set) -{ - struct symset_customization *gdc = &gs.sym_customizations[which_set]; - struct customization_detail *details = gdc->details, *next; - - while (details) { - next = details->next; - if (gdc->custtype == custom_ureps) { - if (details->content.urep.u.utf8str) - free(details->content.urep.u.utf8str); - details->content.urep.u.utf8str = (uint8 *) 0; - details->content.urep.u.ucolor = 0L; - details->content.urep.u.u256coloridx = 0L; - } else if (gdc->custtype == custom_symbols) { - details->content.sym.symparse = (struct symparse *) 0; - details->content.sym.val = 0; - } - free(details); - details = next; - } - gdc->details = 0; - gdc->details_end = 0; - if (gdc->customization_name) { - free((genericptr_t) gdc->customization_name); - gdc->customization_name = 0; - } - gdc->count = 0; -} - -struct customization_detail * -find_display_sym_customization( - const char *customization_name, - const struct symparse *symparse, - enum graphics_sets which_set) -{ - struct symset_customization *gdc = &gs.sym_customizations[which_set]; - struct customization_detail *symdetails; - - if ((gdc->custtype == custom_symbols) - && (strcmp(customization_name, gdc->customization_name) == 0)) { - symdetails = gdc->details; - while (symdetails) { - if (symdetails->content.sym.symparse == symparse) - return symdetails; - symdetails = symdetails->next; - } - } - return (struct customization_detail *) 0; -} -#endif /* ENHANCED_SYMBOLS */ - /*symbols.c*/ diff --git a/src/sys.c b/src/sys.c index 6cd1ad4b36..10a155d802 100644 --- a/src/sys.c +++ b/src/sys.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 sys.c $NHDT-Date: 1596498215 2020/08/03 23:43:35 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.57 $ */ +/* NetHack 3.7 sys.c $NHDT-Date: 1717449153 2024/06/03 21:12:33 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.64 $ */ /* Copyright (c) Kenneth Lorber, Kensington, Maryland, 2008. */ /* NetHack may be freely redistributed. See license for details. */ @@ -15,28 +15,47 @@ 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; sysopt.livelog = 0; #ifdef SYSCF sysopt.wizards = (char *) 0; #else + if (sysopt.wizards) + free((genericptr_t) sysopt.wizards); sysopt.wizards = dupstr(WIZARD_NAME); #ifdef LIVELOGFILE sysopt.livelog = LIVELOG_DETAIL; sysopt.ll_conduct_turns = 0; #endif #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; + sysopt.debugfiles = (char *) 0; #else - sysopt.debugfiles = dupstr(DEBUGFILES); + if (sysopt.debugfiles) + free((genericptr_t) sysopt.debugfiles); + sysopt.debugfiles = dupstr(DEBUGFILES); #endif + sysopt.env_dbgfl = 0; + } + #ifdef DUMPLOG sysopt.dumplogfile = (char *) 0; sysopt.dumplogurl = (char *) 0; @@ -47,10 +66,10 @@ sys_early_init(void) #ifdef DUMPHTML sysopt.dumphtmlfile = (char *) 0; #endif - sysopt.env_dbgfl = 0; /* haven't checked getenv("DEBUGFILES") yet */ 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; @@ -68,7 +87,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; @@ -82,6 +105,7 @@ sys_early_init(void) #endif #endif #endif + sysopt.crashreporturl = NULL; sysopt.check_save_uid = 1; sysopt.check_plname = 0; @@ -117,9 +141,12 @@ sysopt_release(void) if (sysopt.debugfiles) 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; + free((genericptr_t) sysopt.dumplogfile), sysopt.dumplogfile=(char *) 0; #endif #ifdef DUMPHTML if (sysopt.dumphtmlfile) @@ -127,7 +154,7 @@ sysopt_release(void) #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) @@ -137,7 +164,7 @@ sysopt_release(void) 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 bc9f4f9efc..59eae6401a 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 teleport.c $NHDT-Date: 1684374686 2023/05/18 01:51:26 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.202 $ */ +/* 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. */ @@ -6,17 +6,18 @@ #include "hack.h" #define NEW_ENEXTO -static boolean goodpos_onscary(coordxy, coordxy, struct permonst *); -static boolean tele_jump_ok(coordxy, coordxy, coordxy, coordxy); -static boolean teleok(coordxy, coordxy, boolean); -static void vault_tele(void); -static boolean rloc_pos_ok(coordxy, coordxy, struct monst *); -static void rloc_to_core(struct monst *, coordxy, coordxy, unsigned); -static void mvault_tele(struct monst *); -static boolean m_blocks_teleporting(struct monst *); +staticfn boolean goodpos_onscary(coordxy, coordxy, struct permonst *); +staticfn boolean tele_jump_ok(coordxy, coordxy, coordxy, coordxy); +staticfn boolean teleok(coordxy, coordxy, boolean); +staticfn void vault_tele(void); +staticfn boolean rloc_pos_ok(coordxy, coordxy, struct monst *); +staticfn void rloc_to_core(struct monst *, coordxy, coordxy, unsigned); +staticfn void mvault_tele(struct monst *); +staticfn boolean m_blocks_teleporting(struct monst *); +staticfn stairway *stairway_find_forwiz(boolean, boolean); /* does monster block others from teleporting? */ -static boolean +staticfn boolean m_blocks_teleporting(struct monst *mtmp) { if (is_archfiend(mtmp->data)) @@ -26,7 +27,7 @@ m_blocks_teleporting(struct monst *mtmp) /* teleporting is prevented on this level for this monster? */ boolean -noteleport_level(struct monst* mon) +noteleport_level(struct monst *mon) { /* demon court in Gehennom prevent others from teleporting */ if (In_hell(&u.uz) && !(is_archfiend(mon->data))) @@ -38,11 +39,11 @@ noteleport_level(struct monst* mon) * nemesis is a being whose magic powers or even very presence would forbid * you from advancing rapidly, and block teleport there, while allowing * teleport for lesser nemeses. */ - if (In_quest(&u.uz) && !gq.quest_status.killed_nemesis) + if (In_quest(&u.uz) && !svq.quest_status.killed_nemesis) return TRUE; /* natural no-teleport level */ - if (gl.level.flags.noteleport) + if (svl.level.flags.noteleport) return TRUE; return FALSE; @@ -51,7 +52,7 @@ noteleport_level(struct monst* mon) /* this is an approximation of onscary() that doesn't use any 'struct monst' fields aside from 'monst->data'; used primarily for new monster creation and monster teleport destination, not for ordinary monster movement */ -static boolean +staticfn boolean goodpos_onscary( coordxy x, coordxy y, struct permonst *mptr) @@ -94,7 +95,8 @@ goodpos( boolean ignorewater = ((gpflags & MM_IGNOREWATER) != 0), ignorelava = ((gpflags & MM_IGNORELAVA) != 0), checkscary = ((gpflags & GP_CHECKSCARY) != 0), - allow_u = ((gpflags & GP_ALLOW_U) != 0); + allow_u = ((gpflags & GP_ALLOW_U) != 0), + avoid_monpos = ((gpflags & GP_AVOID_MONPOS) != 0); if (!isok(x, y)) return FALSE; @@ -112,6 +114,9 @@ goodpos( return FALSE; } + if (MON_AT(x, y) && avoid_monpos) + return FALSE; + if (mtmp) { struct monst *mtmp2 = m_at(x, y); @@ -177,6 +182,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; } @@ -199,6 +207,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 @@ -368,41 +387,41 @@ enexto_core( * need to be augmented to allow deliberate passage in wizard mode, but * only for explicitly chosen destinations.) */ -static boolean +staticfn boolean tele_jump_ok(coordxy x1, coordxy y1, coordxy x2, coordxy y2) { if (!isok(x2, y2)) return FALSE; - if (gd.dndest.nlx > 0) { + if (svd.dndest.nlx > 0) { /* if inside a restricted region, can't teleport outside */ - if (within_bounded_area(x1, y1, gd.dndest.nlx, gd.dndest.nly, - gd.dndest.nhx, gd.dndest.nhy) - && !within_bounded_area(x2, y2, gd.dndest.nlx, gd.dndest.nly, - gd.dndest.nhx, gd.dndest.nhy)) + if (within_bounded_area(x1, y1, svd.dndest.nlx, svd.dndest.nly, + svd.dndest.nhx, svd.dndest.nhy) + && !within_bounded_area(x2, y2, svd.dndest.nlx, svd.dndest.nly, + svd.dndest.nhx, svd.dndest.nhy)) return FALSE; /* and if outside, can't teleport inside */ - if (!within_bounded_area(x1, y1, gd.dndest.nlx, gd.dndest.nly, - gd.dndest.nhx, gd.dndest.nhy) - && within_bounded_area(x2, y2, gd.dndest.nlx, gd.dndest.nly, - gd.dndest.nhx, gd.dndest.nhy)) + if (!within_bounded_area(x1, y1, svd.dndest.nlx, svd.dndest.nly, + svd.dndest.nhx, svd.dndest.nhy) + && within_bounded_area(x2, y2, svd.dndest.nlx, svd.dndest.nly, + svd.dndest.nhx, svd.dndest.nhy)) return FALSE; } - if (gu.updest.nlx > 0) { /* ditto */ - if (within_bounded_area(x1, y1, gu.updest.nlx, gu.updest.nly, - gu.updest.nhx, gu.updest.nhy) - && !within_bounded_area(x2, y2, gu.updest.nlx, gu.updest.nly, - gu.updest.nhx, gu.updest.nhy)) + if (svu.updest.nlx > 0) { /* ditto */ + if (within_bounded_area(x1, y1, svu.updest.nlx, svu.updest.nly, + svu.updest.nhx, svu.updest.nhy) + && !within_bounded_area(x2, y2, svu.updest.nlx, svu.updest.nly, + svu.updest.nhx, svu.updest.nhy)) return FALSE; - if (!within_bounded_area(x1, y1, gu.updest.nlx, gu.updest.nly, - gu.updest.nhx, gu.updest.nhy) - && within_bounded_area(x2, y2, gu.updest.nlx, gu.updest.nly, - gu.updest.nhx, gu.updest.nhy)) + if (!within_bounded_area(x1, y1, svu.updest.nlx, svu.updest.nly, + svu.updest.nhx, svu.updest.nhy) + && within_bounded_area(x2, y2, svu.updest.nlx, svu.updest.nly, + svu.updest.nhx, svu.updest.nhy)) return FALSE; } return TRUE; } -static boolean +staticfn boolean teleok(coordxy x, coordxy y, boolean trapok) { if (!trapok) { @@ -512,7 +531,6 @@ teleds(coordxy nux, coordxy nuy, int teleds_flags) fill_pit(u.ux0, u.uy0); if (ball_active && uchain && uchain->where == OBJ_FREE) placebc(); /* put back the ball&chain if they were taken off map */ - initrack(); /* teleports mess up tracking monsters without this */ update_player_regions(); /* * Make sure the hero disappears from the old location. This will @@ -524,11 +542,12 @@ teleds(coordxy nux, coordxy nuy, int teleds_flags) see_monsters(); gv.vision_full_recalc = 1; nomul(0); + notice_mon_off(); vision_recalc(0); /* vision before effects */ /* this used to take place sooner, but if a --More-- prompt was issued then the old map display was shown instead of the new one */ - if (is_teleport && Verbose(2, teleds)) + if (is_teleport && flags.verbose) You("materialize in %s location!", (nux == u.ux0 && nuy == u.uy0) ? "the same" : "a different"); /* if terrain type changes, levitation or flying might become blocked @@ -553,6 +572,8 @@ teleds(coordxy nux, coordxy nuy, int teleds_flags) /* possible shop entry message comes after guard's shrill whistle */ spoteffects(TRUE); invocation_message(); + notice_mon_on(); + notice_all_mons(TRUE); return; } @@ -561,7 +582,7 @@ teleds(coordxy nux, coordxy nuy, int teleds_flags) int collect_coords( coord *ccc, /* pointer to array of at least size ROWNO*(COLNO-1) */ - coordxy cx, coordxy cy, /* center point, not necessarly */ + coordxy cx, coordxy cy, /* center point, not necessarily */ int maxradius, /* how far from center to go collecting spots; * 0 means collect entire map */ unsigned cc_flags, /* incl_center: put in output list @@ -625,7 +646,7 @@ collect_coords( * (unshown) 4's will be mixed together, and so forth. * * If caller processes the output list in order, the closest viable - * spot will be chosen. If a compeletely random spot is preferred, + * spot will be chosen. If a completely random spot is preferred, * the list can be requested to be unscrambled and then the caller * can shuffle it, overriding the collection rings. A filter function * could be used to skip everything after the first acceptable spot. @@ -753,7 +774,7 @@ safe_teleds(int teleds_flags) return FALSE; } -static void +staticfn void vault_tele(void) { struct mkroom *croom = search_special(VAULT); @@ -767,7 +788,7 @@ vault_tele(void) } boolean -teleport_pet(register struct monst* mtmp, boolean force_it) +teleport_pet(struct monst *mtmp, boolean force_it) { struct obj *otmp; @@ -802,7 +823,7 @@ tele(void) /* teleport the hero; usually discover scroll of teleportation if via scroll */ void -scrolltele(struct obj* scroll) +scrolltele(struct obj *scroll) { coord cc; @@ -943,7 +964,7 @@ dotelecmd(void) menu_item *picks = (menu_item *) 0; anything any; winid win; - int i, tmode, clr = 0; + int i, tmode, clr = NO_COLOR; win = create_nhwindow(NHW_MENU); start_menu(win, MENU_BEHAVE_STANDARD); @@ -1116,17 +1137,19 @@ dotele( } else { /* bypassing spelleffects(); apply energy cost directly */ u.uen -= energy; - gc.context.botl = 1; + disp.botl = TRUE; } } if (next_to_u()) { - if (trap && trap_once) + if (trap && trap_once) { vault_tele(); - else if (trap && trap->teledest.x > 0) + } 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); @@ -1141,7 +1164,7 @@ void level_tele(void) { static const char get_there_from[] = "get there from %s."; - register int newlev; + int newlev; d_level newlevel; const char *escape_by_flying = 0; /* when surviving dest of -N */ char buf[BUFSZ]; @@ -1149,10 +1172,10 @@ level_tele(void) if (iflags.debug_fuzzer) { do { - newlevel.dnum = rn2(gn.n_dgns); + newlevel.dnum = rn2(svn.n_dgns); } while (newlevel.dnum == astral_level.dnum - || gd.dungeons[newlevel.dnum].flags.unconnected - || !gd.dungeons[newlevel.dnum].num_dunlevs); + || svd.dungeons[newlevel.dnum].flags.unconnected + || !svd.dungeons[newlevel.dnum].num_dunlevs); newlevel.dlevel = 1 + rn2(dunlevs_in_dungeon(&newlevel)); assign_level(&u.ucamefrom, &u.uz); schedule_goto(&newlevel, UTOTYPE_NONE, (char *) 0, (char *) 0); @@ -1187,17 +1210,13 @@ level_tele(void) the previous input was invalid so don't use it as getlin()'s preloaded default answer */ getlin(qbuf, buf); - if (!strcmp(buf, "\033")) { /* cancelled */ - if (Confusion && rnl(5)) { - pline("Oops..."); - goto random_levtport; - } - return; - } else if (!strcmp(buf, "*")) { + if (!strcmp(buf, "*")) { goto random_levtport; } else if (Confusion && rnl(5)) { pline("Oops..."); goto random_levtport; + } else if (!strcmp(buf, "\033")) { /* cancelled */ + return; } if (wizard && !strcmp(buf, "?")) { schar destlev; @@ -1244,8 +1263,8 @@ level_tele(void) if (gi.invent) Your("possessions land on the %s with a thud.", surface(u.ux, u.uy)); - gk.killer.format = NO_KILLER_PREFIX; - Strcpy(gk.killer.name, "committed suicide"); + svk.killer.format = NO_KILLER_PREFIX; + Strcpy(svk.killer.name, "committed suicide"); done(DIED); pline("An energized cloud of dust begins to coalesce."); Your("body rematerializes%s.", @@ -1269,7 +1288,7 @@ level_tele(void) * we let negative values requests fall into the "heaven" handling. */ if (In_quest(&u.uz) && newlev > 0) - newlev = newlev + gd.dungeons[u.uz.dnum].depth_start - 1; + newlev = newlev + svd.dungeons[u.uz.dnum].depth_start - 1; } else { /* involuntary level tele */ random_levtport: newlev = random_teleport_level(); @@ -1283,7 +1302,7 @@ level_tele(void) buried_ball_to_punishment(); /* no levelporting down in the quest before the nemesis is killed */ - if (In_quest(&u.uz) && !gq.quest_status.killed_nemesis + if (In_quest(&u.uz) && !svq.quest_status.killed_nemesis && newlev > depth(&u.uz)) { if (wizard) { pline("Overriding quest-levelport restriction."); @@ -1310,7 +1329,7 @@ level_tele(void) return; } - gk.killer.name[0] = 0; /* still alive, so far... */ + svk.killer.name[0] = 0; /* still alive, so far... */ if (iflags.debug_fuzzer && newlev < 0) goto random_levtport; @@ -1327,8 +1346,8 @@ level_tele(void) You("arrive in heaven."); SetVoice((struct monst *) 0, 0, 80, voice_deity); verbalize("Thou art early, but we'll admit thee."); - gk.killer.format = NO_KILLER_PREFIX; - Strcpy(gk.killer.name, "went to heaven prematurely"); + svk.killer.format = NO_KILLER_PREFIX; + Strcpy(svk.killer.name, "went to heaven prematurely"); } else if (newlev == -9) { You_feel("deliriously happy."); pline("(In fact, you're on Cloud 9!)"); @@ -1336,7 +1355,7 @@ level_tele(void) } else You("are now high above the clouds..."); - if (gk.killer.name[0]) { + if (svk.killer.name[0]) { ; /* arrival in heaven is pending */ } else if (Levitation) { escape_by_flying = "float gently down to earth"; @@ -1345,14 +1364,14 @@ level_tele(void) } else { pline("Unfortunately, you don't know how to fly."); You("plummet a few thousand feet to your death."); - Sprintf(gk.killer.name, + Sprintf(svk.killer.name, "teleported out of the dungeon and fell to %s death", uhis()); - gk.killer.format = NO_KILLER_PREFIX; + svk.killer.format = NO_KILLER_PREFIX; } } - if (gk.killer.name[0]) { /* the chosen destination was not survivable */ + if (svk.killer.name[0]) { /* the chosen destination was not survivable */ d_level lsav; /* set specific death location; this also suppresses bones */ @@ -1382,7 +1401,7 @@ level_tele(void) /* wizard mode menu; no further validation needed */ ; } else if (u.uz.dnum == medusa_level.dnum - && newlev >= gd.dungeons[u.uz.dnum].depth_start + && newlev >= svd.dungeons[u.uz.dnum].depth_start + dunlevs_in_dungeon(&u.uz)) { find_hell(&newlevel); } else { @@ -1393,7 +1412,7 @@ level_tele(void) d_level *qbranch = In_quest(&u.uz) ? &qstart_level : In_mines(&u.uz) ? &mineend_level : &sanctum_level; - int deepest = gd.dungeons[qbranch->dnum].depth_start + int deepest = svd.dungeons[qbranch->dnum].depth_start + dunlevs_in_dungeon(qbranch) - 1; /* if invocation did not yet occur, teleporting into @@ -1420,9 +1439,8 @@ level_tele(void) } schedule_goto(&newlevel, UTOTYPE_NONE, (char *) 0, - Verbose(2, level_tele) - ? "You materialize on a different level!" - : (char *) 0); + flags.verbose ? "You materialize on a different level!" + : (char *) 0); #if 0 /* always wait until end of turn to change level, otherwise code * that references monsters as this call stack unwinds won't be * able to access them reliably; the do-the-change-now code here @@ -1432,7 +1450,7 @@ level_tele(void) /* in case player just read a scroll and is about to be asked to call it something, we can't defer until the end of the turn */ - if (u.utotype && !gc.context.mon_moving) + if (u.utotype && !svc.context.mon_moving) deferred_goto(); #endif } @@ -1441,7 +1459,7 @@ void domagicportal(struct trap *ttmp) { struct d_level target_level; - s_level *tutlvl = find_level("tut-1"); + int totype; const char *stunmsg = (char *) 0; if (u.utrap && u.utraptype == TT_BURIEDBALL) @@ -1471,13 +1489,18 @@ domagicportal(struct trap *ttmp) target_level = ttmp->dst; /* coming back from tutorial doesn't trigger stunning */ - if (!(tutlvl && tutlvl->dlevel.dnum == u.uz.dnum)) { + if (In_tutorial(&u.uz) && !In_tutorial(&target_level)) { + /* returning to normal play => arrive on level 1 stairs */ + totype = UTOTYPE_ATSTAIRS; + stunmsg = "Resuming regular play."; + } else { + totype = UTOTYPE_PORTAL; stunmsg = !Stunned ? "You feel slightly dizzy." : "You feel dizzier."; make_stunned((HStun & TIMEOUT) + 3L, FALSE); } - schedule_goto(&target_level, UTOTYPE_PORTAL, stunmsg, (char *) 0); + schedule_goto(&target_level, totype, stunmsg, (char *) 0); } void @@ -1493,9 +1516,11 @@ tele_trap(struct trap *trap) deltrap(trap); newsym(u.ux, u.uy); /* get rid of trap symbol */ vault_tele(); - } else if (trap->teledest.x > 0) { + } else if (isok(trap->teledest.x, trap->teledest.y)) { coord cc; struct monst *mtmp = m_at(trap->teledest.x, trap->teledest.y); + + settrack(); if (mtmp) { if (!enexto(&cc, mtmp->mx, mtmp->my, mtmp->data)) { /* could not find some other place to put mtmp; the level must @@ -1547,7 +1572,7 @@ level_tele_trap(struct trap *trap, unsigned int trflags) } /* check whether monster can arrive at location via Tport (or fall) */ -static boolean +staticfn boolean rloc_pos_ok( coordxy x, coordxy y, /* coordinates of candidate location */ struct monst *mtmp) @@ -1567,20 +1592,22 @@ rloc_pos_ok( yy = mtmp->my; if (!xx) { /* no current location (migrating monster arrival) */ - if (gu.updest.lx && (yy & 1) != 0) /* moving up */ - return (within_bounded_area(x, y, gu.updest.lx, gu.updest.ly, - gu.updest.hx, gu.updest.hy) - && (!gu.updest.nlx + if (svu.updest.lx && (yy & 1) != 0) /* moving up */ + return (within_bounded_area(x, y, + svu.updest.lx, svu.updest.ly, + svu.updest.hx, svu.updest.hy) + && (!svu.updest.nlx || !within_bounded_area(x, y, - gu.updest.nlx, gu.updest.nly, - gu.updest.nhx, gu.updest.nhy))); - if (gd.dndest.lx && (yy & 1) == 0) /* moving down */ - return (within_bounded_area(x, y, gd.dndest.lx, gd.dndest.ly, - gd.dndest.hx, gd.dndest.hy) - && (!gd.dndest.nlx + svu.updest.nlx, svu.updest.nly, + svu.updest.nhx, svu.updest.nhy))); + if (svd.dndest.lx && (yy & 1) == 0) /* moving down */ + return (within_bounded_area(x, y, + svd.dndest.lx, svd.dndest.ly, + svd.dndest.hx, svd.dndest.hy) + && (!svd.dndest.nlx || !within_bounded_area(x, y, - gd.dndest.nlx, gd.dndest.nly, - gd.dndest.nhx, gd.dndest.nhy))); + svd.dndest.nlx, svd.dndest.nly, + svd.dndest.nhx, svd.dndest.nhy))); } else { /* [try to] prevent a shopkeeper or temple priest from being sent out of his room (caller might resort to goodpos() if @@ -1609,7 +1636,7 @@ rloc_pos_ok( * a value because mtmp is a migrating_mon. Worm tails are always * placed randomly around the head of the worm. */ -static void +staticfn void rloc_to_core( struct monst *mtmp, coordxy x, coordxy y, @@ -1658,8 +1685,9 @@ rloc_to_core( if (u.ustuck == mtmp) { if (u.uswallow) { u_on_newpos(mtmp->mx, mtmp->my); + check_special_room(FALSE); docrt(); - } else if (!next2u(mtmp->mx, mtmp->my)) { + } else if (!m_next2u(mtmp)) { unstuck(mtmp); } } @@ -1667,13 +1695,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 @@ -1688,11 +1719,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); @@ -1739,6 +1777,8 @@ rloc_to_flag( rloc_to_core(mtmp, x, y, rlocflags); } +/* place a monster at a random location, typically due to teleport; + return TRUE if successful, FALSE if not; rlocflags is RLOC_foo flags */ boolean rloc( struct monst *mtmp, /* mtmp->mx==0 implies migrating monster arrival */ @@ -1773,6 +1813,16 @@ rloc( goto found_xy; } + /* wizard-mode player can choose destination by setting 'montelecontrol' + option; ignored if/when this is arrival of a migrating monster */ + if (iflags.mon_telecontrol && mtmp->mx) { + cc.x = mtmp->mx, cc.y = mtmp->my; + if (control_mon_tele(mtmp, &cc, rlocflags, TRUE)) { + x = cc.x, y = cc.y; + goto found_xy; + } + } + /* this used to try randomly 1000 times, then fallback to left-to-right top-to-bottom exhaustive check; now that the exhaustive check uses randomized order, reduce the number of random attempts to 50; @@ -1826,8 +1876,47 @@ rloc( return TRUE; } -static void -mvault_tele(struct monst* mtmp) +/* let wizard-mode player choose a teleporting monster's destination */ +boolean +control_mon_tele( + struct monst *mon, + coord *cc_p, /* input: default spot; output: player selected spot */ + unsigned rlocflags, + boolean via_rloc) +{ + char tcbuf[BUFSZ]; + + if (!isok(cc_p->x, cc_p->y)) { + cc_p->x = mon->mx, cc_p->y = mon->my; + if (!isok(cc_p->x, cc_p->y)) + cc_p->x = u.ux, cc_p->y = u.uy; + } + + if (!wizard || !iflags.mon_telecontrol) + return FALSE; + + pline("Teleport %s @ <%d,%d> where?", + noit_mon_nam(mon), mon->mx, mon->my); + /* getpos '?' will show "Move the cursor to :" */ + Sprintf(tcbuf, "where to teleport %s", noit_mon_nam(mon)); + if (getpos(cc_p, FALSE, tcbuf) >= 0 && !u_at(cc_p->x, cc_p->y)) { + if (via_rloc + ? rloc_pos_ok(cc_p->x, cc_p->y, mon) + : goodpos(cc_p->x, cc_p->y, mon, rlocflags)) + return TRUE; + if (!iflags.debug_fuzzer) { + Sprintf(tcbuf, "<%d,%d> is not considered viable; force anyway?", + mon->mx, mon->my); + if (y_n(tcbuf) == 'y') + return TRUE; + } + } + pline("%s destination.", via_rloc ? "Picking random" : "Using derived"); + return FALSE; +} + +staticfn void +mvault_tele(struct monst *mtmp) { struct mkroom *croom = search_special(VAULT); coord c; @@ -1840,7 +1929,7 @@ mvault_tele(struct monst* mtmp) } boolean -tele_restrict(struct monst* mon) +tele_restrict(struct monst *mon) { if (noteleport_level(mon)) { if (canseemon(mon)) @@ -1852,7 +1941,7 @@ tele_restrict(struct monst* mon) } void -mtele_trap(struct monst* mtmp, struct trap* trap, int in_sight) +mtele_trap(struct monst *mtmp, struct trap *trap, int in_sight) { char *monname; @@ -1868,17 +1957,17 @@ mtele_trap(struct monst* mtmp, struct trap* trap, int in_sight) */ if (trap->once) mvault_tele(mtmp); - else if (trap->teledest.x > 0) { + else if (isok(trap->teledest.x, trap->teledest.y)) { /* monster teleporting onto hero's or another monster's spot does * not work the same as hero teleporting onto monster's spot where * the incoming monster displaces the resident to the nearest * possible space - instead it just doesn't work. */ if (!(m_at(trap->teledest.x, trap->teledest.y) || u_at(trap->teledest.x, trap->teledest.y))) { - rloc_to_core(mtmp, trap->teledest.x, trap->teledest.y, RLOC_MSG); + rloc_to_core(mtmp, trap->teledest.x, trap->teledest.y, + RLOC_MSG); } - } - else + } else (void) rloc(mtmp, RLOC_NONE); if (in_sight) { @@ -1901,7 +1990,7 @@ mlevel_tele_trap( { int tt = (trap ? trap->ttyp : NO_TRAP); - if (mtmp == u.ustuck && gc.context.mon_moving) { + if (mtmp == u.ustuck && svc.context.mon_moving) { /* ustuck case is probably a vortex, but check mon_moving so that if * hero is forcing a monster into a trap (i.e. with an AD_PITS attack), * this will proc correctly. */ @@ -1916,8 +2005,9 @@ mlevel_tele_trap( assign_level(&tolevel, &valley_level); } else if (Is_botlevel(&u.uz)) { if (in_sight && trap->tseen) - pline("%s avoids the %s.", Monnam(mtmp), - (tt == HOLE) ? "hole" : "trap"); + pline_mon(mtmp, "%s avoids the %s.", + Monnam(mtmp), + (tt == HOLE) ? "hole" : "trap"); return Trap_Effect_Finished; } else { assign_level(&tolevel, &trap->dst); @@ -1928,7 +2018,9 @@ mlevel_tele_trap( || is_home_elemental(mtmp->data) || rn2(7))) { if (in_sight && mtmp->data->mlet != S_ELEMENTAL) { - pline("%s seems to shimmer for a moment.", Monnam(mtmp)); + pline_mon(mtmp, + "%s seems to shimmer for a moment.", + Monnam(mtmp)); seetrap(trap); } return Trap_Effect_Finished; @@ -1946,8 +2038,9 @@ mlevel_tele_trap( currently inside his or her own special room */ || (tt == NO_TRAP && onscary(0, 0, mtmp))) { if (in_sight) - pline("%s seems very disoriented for a moment.", - Monnam(mtmp)); + pline_mon(mtmp, + "%s seems very disoriented for a moment.", + Monnam(mtmp)); return Trap_Effect_Finished; } if (tt == NO_TRAP) { @@ -1960,7 +2053,8 @@ mlevel_tele_trap( nlev = random_teleport_level(); if (nlev == depth(&u.uz)) { if (in_sight) - pline("%s shudders for a moment.", Monnam(mtmp)); + pline_mon(mtmp, "%s shudders for a moment.", + Monnam(mtmp)); return Trap_Effect_Finished; } get_level(&tolevel, nlev); @@ -1971,10 +2065,10 @@ mlevel_tele_trap( } if (in_sight) { - pline("Suddenly, %s %s.", mon_nam(mtmp), - (tt == HOLE) ? "falls into a hole" - : (tt == TRAPDOOR) ? "falls through a trap door" - : "disappears out of sight"); + pline_mon(mtmp, "Suddenly, %s %s.", mon_nam(mtmp), + (tt == HOLE) ? "falls into a hole" + : (tt == TRAPDOOR) ? "falls through a trap door" + : "disappears out of sight"); if (trap) seetrap(trap); } @@ -2002,7 +2096,7 @@ rloco(struct obj *obj) obj_extract_self(obj); otx = obj->ox; oty = obj->oy; - restricted_fall = (otx == 0 && gd.dndest.lx); + restricted_fall = (otx == 0 && svd.dndest.lx); do { tx = rn1(COLNO - 3, 2); ty = rn2(ROWNO); @@ -2010,20 +2104,22 @@ rloco(struct obj *obj) break; } while (!goodpos(tx, ty, (struct monst *) 0, 0) || (restricted_fall - && (!within_bounded_area(tx, ty, gd.dndest.lx, gd.dndest.ly, - gd.dndest.hx, gd.dndest.hy) - || (gd.dndest.nlx + && (!within_bounded_area(tx, ty, + svd.dndest.lx, svd.dndest.ly, + svd.dndest.hx, svd.dndest.hy) + || (svd.dndest.nlx && within_bounded_area(tx, ty, - gd.dndest.nlx, gd.dndest.nly, - gd.dndest.nhx, gd.dndest.nhy)))) + svd.dndest.nlx, svd.dndest.nly, + svd.dndest.nhx, svd.dndest.nhy)))) /* on the Wizard Tower levels, objects inside should stay inside and objects outside should stay outside */ - || (gd.dndest.nlx && On_W_tower_level(&u.uz) - && within_bounded_area(tx, ty, gd.dndest.nlx, gd.dndest.nly, - gd.dndest.nhx, gd.dndest.nhy) + || (svd.dndest.nlx && On_W_tower_level(&u.uz) + && within_bounded_area(tx, ty, + svd.dndest.nlx, svd.dndest.nly, + svd.dndest.nhx, svd.dndest.nhy) != within_bounded_area(otx, oty, - gd.dndest.nlx, gd.dndest.nly, - gd.dndest.nhx, gd.dndest.nhy))); + svd.dndest.nlx, svd.dndest.nly, + svd.dndest.nhx, svd.dndest.nhy))); if (flooreffects(obj, tx, ty, "fall")) { /* update old location since flooreffects() couldn't; @@ -2109,12 +2205,12 @@ random_teleport_level(void) no one can randomly teleport past it */ if (dunlev_reached(&u.uz) < qlocate_depth) bottom = qlocate_depth; - min_depth = gd.dungeons[u.uz.dnum].depth_start; - max_depth = bottom + (gd.dungeons[u.uz.dnum].depth_start - 1); + min_depth = svd.dungeons[u.uz.dnum].depth_start; + max_depth = bottom + (svd.dungeons[u.uz.dnum].depth_start - 1); } else { min_depth = 1; max_depth = dunlevs_in_dungeon(&u.uz) - + (gd.dungeons[u.uz.dnum].depth_start - 1); + + (svd.dungeons[u.uz.dnum].depth_start - 1); /* can't reach Sanctum if the invocation hasn't been performed */ if (Inhell && !u.uevent.invoked) max_depth -= 1; @@ -2124,7 +2220,7 @@ random_teleport_level(void) * Gehennom because it is now only possible to levelport from the * Valley) */ if (Inhell) - max_depth = gd.dungeons[u.uz.dnum].depth_start; + max_depth = svd.dungeons[u.uz.dnum].depth_start; } /* Get a random value relative to the current dungeon */ @@ -2153,7 +2249,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; @@ -2165,12 +2263,15 @@ u_teleport_mon(struct monst* mtmp, boolean give_feedback) if (give_feedback) You("are no longer inside %s!", mon_nam(mtmp)); unstuck(mtmp); - (void) rloc(mtmp, RLOC_MSG); - } else if (is_rider(mtmp->data) && rn2(13) - && enexto(&cc, u.ux, u.uy, mtmp->data)) + 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)) { 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 db19165dfb..d4075d647a 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -1,24 +1,26 @@ -/* NetHack 3.7 timeout.c $NHDT-Date: 1658390077 2022/07/21 07:54:37 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.142 $ */ +/* NetHack 3.7 timeout.c $NHDT-Date: 1727251273 2024/09/25 08:01:13 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.193 $ */ /* 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" -static void stoned_dialogue(void); -static void vomiting_dialogue(void); -static void choke_dialogue(void); -static void levitation_dialogue(void); -static void reappear_dialogue(void); -static void slime_dialogue(void); -static void slimed_to_death(struct kinfo *); -static void sickness_dialogue(void); -static void phaze_dialogue(void); -static void done_timeout(int, int); -static void slip_or_trip(void); -static void see_lamp_flicker(struct obj *, const char *); -static void lantern_message(struct obj *); -static void cleanup_burn(ANY_P *, long); +staticfn void stoned_dialogue(void); +staticfn void vomiting_dialogue(void); +staticfn void sleep_dialogue(void); +staticfn void choke_dialogue(void); +staticfn void levitation_dialogue(void); +staticfn void reappear_dialogue(void); +staticfn void slime_dialogue(void); +staticfn void slimed_to_death(struct kinfo *) NO_NNARGS; +staticfn void sickness_dialogue(void); +staticfn void phaze_dialogue(void); +staticfn void region_dialogue(void); +staticfn void done_timeout(int, int); +staticfn void slip_or_trip(void); +staticfn void see_lamp_flicker(struct obj *, const char *) NONNULLPTRS; +staticfn void lantern_message(struct obj *) NONNULLARG1; +staticfn void cleanup_burn(ANY_P *, long) NONNULLARG1; /* used by wizard mode #timeout and #wizintrinsic; order by 'interest' for timeout countdown, where most won't occur in normal play; @@ -57,6 +59,8 @@ const struct propname { /* timed pass-walls is a potential prayer result if surrounded by stone with nowhere to be safely teleported to */ { PASSES_WALLS, "pass thru walls" }, + /* likewise for magical breathing vs poison gas regions */ + { MAGICAL_BREATHING, "magical breathing" }, /* timed fire resistance and water walking are possible in explore mode (as well as in wizard mode) after life-saving in lava if it fails to teleport the hero to safety and player declines to die */ @@ -77,6 +81,7 @@ 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" }, @@ -93,7 +98,6 @@ const struct propname { { TELEPORT_CONTROL, "teleport control" }, { FLYING, "flying" }, { SWIMMING, "swimming" }, - { MAGICAL_BREATHING, "magical breathing" }, { SLOW_DIGESTION, "slow digestion" }, { HALF_SPDAM, "half spell damage" }, { HALF_PHDAM, "half physical damage" }, @@ -140,7 +144,7 @@ can_slime_with_unchanging(void) const char * property_by_index(int idx, int *propertynum) { - if (!(idx >= 0 && idx < SIZE(propertynames) - 1)) + if (!IndexOkT(idx, propertynames)) idx = SIZE(propertynames) - 1; if (propertynum) @@ -157,7 +161,7 @@ static NEARDATA const char *const stoned_texts[] = { "You are a statue." /* 1 */ }; -static void +staticfn void stoned_dialogue(void) { long i = (Stoned & TIMEOUT); @@ -217,7 +221,7 @@ static NEARDATA const char *const vomiting_texts[] = { "are about to vomit." /* 2 */ }; -static void +staticfn void vomiting_dialogue(void) { const char *txt = 0; @@ -239,6 +243,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); @@ -287,6 +292,15 @@ vomiting_dialogue(void) exercise(A_CON, FALSE); } +staticfn void +sleep_dialogue(void) +{ + long i = (HSleepy & TIMEOUT); + + if (i == 4) + You("yawn."); +} + DISABLE_WARNING_FORMAT_NONLITERAL /* RESTORE is after slime_dialogue */ static NEARDATA const char *const choke_texts[] = { @@ -305,7 +319,7 @@ static NEARDATA const char *const choke_texts2[] = { "You suffocate." }; -static void +staticfn void choke_dialogue(void) { long i = (Strangled & TIMEOUT); @@ -321,6 +335,7 @@ choke_dialogue(void) urgent_pline(str, hcolor(NH_BLUE)); else urgent_pline("%s", str); + stop_occupation(); } } exercise(A_STR, FALSE); @@ -332,7 +347,7 @@ static NEARDATA const char *const sickness_texts[] = { "You are at Death's door.", }; -static void +staticfn void sickness_dialogue(void) { long j = (Sick & TIMEOUT), i = j / 2L; @@ -362,7 +377,7 @@ static NEARDATA const char *const levi_texts[] = { "You wobble unsteadily %s the %s." }; -static void +staticfn void levitation_dialogue(void) { /* -1 because the last message comes via float_down() */ @@ -386,10 +401,11 @@ levitation_dialogue(void) danger ? surface(u.ux, u.uy) : "air"); } else pline1(s); + stop_occupation(); } } -static void +staticfn void reappear_dialogue(void) { if (EInvis) @@ -414,7 +430,7 @@ static NEARDATA const char *const slime_texts[] = { "You have become %s." /* 1 */ }; -static void +staticfn void slime_dialogue(void) { long t = (Slimed & TIMEOUT), i = t / 2L; @@ -488,8 +504,8 @@ burn_away_slime(void) } /* countdown timer for turning into green slime has run out; kill our hero */ -static void -slimed_to_death(struct kinfo* kptr) +staticfn void +slimed_to_death(struct kinfo *kptr) { uchar save_mvflags; @@ -500,11 +516,11 @@ slimed_to_death(struct kinfo* kptr) } /* more sure killer reason is set up */ if (kptr && kptr->name[0]) { - gk.killer.format = kptr->format; - Strcpy(gk.killer.name, kptr->name); + svk.killer.format = kptr->format; + Strcpy(svk.killer.name, kptr->name); } else { - gk.killer.format = NO_KILLER_PREFIX; - Strcpy(gk.killer.name, "turned into green slime"); + svk.killer.format = NO_KILLER_PREFIX; + Strcpy(svk.killer.name, "turned into green slime"); } dealloc_killer(kptr); @@ -522,13 +538,13 @@ slimed_to_death(struct kinfo* kptr) */ if (emits_light(gy.youmonst.data)) del_light_source(LS_MONSTER, monst_to_any(&gy.youmonst)); - save_mvflags = gm.mvitals[PM_GREEN_SLIME].mvflags; - gm.mvitals[PM_GREEN_SLIME].mvflags = save_mvflags & ~G_GENOD; + save_mvflags = svm.mvitals[PM_GREEN_SLIME].mvflags; + svm.mvitals[PM_GREEN_SLIME].mvflags = save_mvflags & ~G_GENOD; /* become a green slime; also resets youmonst.m_ap_type+.mappearance */ /* suppress the transformation message to avoid "You have become a green * slime. You turn into a green slime!" */ (void) polymon(PM_GREEN_SLIME, (POLYMON_ALL_MSGS & ~POLYMON_TRANSFORM_MSG)); - gm.mvitals[PM_GREEN_SLIME].mvflags = save_mvflags; + svm.mvitals[PM_GREEN_SLIME].mvflags = save_mvflags; done_timeout(TURNED_SLIME, SLIMED); return; } @@ -545,7 +561,7 @@ static NEARDATA const char *const phaze_texts[] = { "You are feeling rather flabby.", }; -static void +staticfn void phaze_dialogue(void) { long i = ((HPasses_walls & TIMEOUT) / 2L); @@ -554,13 +570,39 @@ phaze_dialogue(void) return; if (((HPasses_walls & TIMEOUT) % 2L) && i > 0L && i <= SIZE(phaze_texts)) - pline1(phaze_texts[SIZE(phaze_texts) - i]); + pline("%s", phaze_texts[SIZE(phaze_texts) - i]); +} + +/* Similar to Passes_walls, if prayer tries to save hero from a poison + gas region but can't, (HMagical_breathing & TIMEOUT) will be set to + a small value. Unlike Passes_walls, there's no joke message. */ +static NEARDATA const char *const region_texts[] = { + "You seem to have some trouble breathing.", + "The air here seems foul.", +}; + +staticfn void +region_dialogue(void) +{ + boolean no_need_to_breathe, in_poison_gas_cloud; + long r = (HMagical_breathing & TIMEOUT), i = r / 2L; + + /* might have poly'd into non-breather or moved out of gas cloud */ + HMagical_breathing &= ~TIMEOUT; + no_need_to_breathe = Breathless; + in_poison_gas_cloud = region_danger(); + HMagical_breathing |= r; + if (no_need_to_breathe || !in_poison_gas_cloud) + return; + + if ((r % 2L) && i > 0L && i <= SIZE(region_texts)) + pline("%s", region_texts[SIZE(region_texts) - i]); } /* when a status timeout is fatal, keep the status line indicator shown during end of game rundown (and potential dumplog); timeout has already counted down to 0 by the time we get here */ -static void +staticfn void done_timeout(int how, int which) { long *intrinsic_p = &u.uprops[which].intrinsic; @@ -570,13 +612,13 @@ done_timeout(int how, int which) /* life-saved */ *intrinsic_p &= ~I_SPECIAL; - gc.context.botl = TRUE; + disp.botl = TRUE; } void nh_timeout(void) { - register struct prop *upp; + struct prop *upp; struct kinfo *kptr; boolean was_flying; int sleeptime; @@ -587,17 +629,20 @@ nh_timeout(void) baseluck -= 1; /* letting your quest leader die brings bad luck */ - if (gq.quest_status.killed_leader) - baseluck = -4; + if (svq.quest_status.killed_leader) + baseluck -= 4; + + if (Role_if(PM_ARCHEOLOGIST) && uarmh && uarmh->otyp == FEDORA) + baseluck += 1; if (u.uluck != baseluck - && gm.moves % ((u.uhave.amulet || u.ugangr) ? 300 : 600) == 0) { + && svm.moves % ((u.uhave.amulet || u.ugangr) ? 300 : 600) == 0) { /* Cursed luckstones stop bad luck from timing out; blessed luckstones * stop good luck from timing out; normal luckstones stop both; * neither is stopped if you don't have a luckstone. * Luck is based at 0 usually, +1 if a full moon and -1 on Friday 13th */ - register int time_luck = stone_luck(FALSE); + int time_luck = stone_luck(FALSE); boolean nostone = !carrying(LUCKSTONE) && !stone_luck(TRUE); if (u.uluck > baseluck && (nostone || time_luck < 0)) @@ -623,6 +668,10 @@ nh_timeout(void) levitation_dialogue(); if (HPasses_walls & TIMEOUT) phaze_dialogue(); + if (HMagical_breathing & TIMEOUT) + region_dialogue(); + if (HSleepy & TIMEOUT) + sleep_dialogue(); if (u.mtimedone && !--u.mtimedone) { if (Unchanging) u.mtimedone = rnd(100 * gy.youmonst.data->mlevel + 1); @@ -674,11 +723,11 @@ nh_timeout(void) switch (upp - u.uprops) { case STONED: if (kptr && kptr->name[0]) { - gk.killer.format = kptr->format; - Strcpy(gk.killer.name, kptr->name); + svk.killer.format = kptr->format; + Strcpy(svk.killer.name, kptr->name); } else { - gk.killer.format = NO_KILLER_PREFIX; - Strcpy(gk.killer.name, "killed by petrification"); + svk.killer.format = NO_KILLER_PREFIX; + Strcpy(svk.killer.name, "killed by petrification"); } dealloc_killer(kptr); /* (unlike sliming, you aren't changing form here) */ @@ -703,21 +752,21 @@ nh_timeout(void) } urgent_pline("You die from your illness."); if (kptr && kptr->name[0]) { - gk.killer.format = kptr->format; - Strcpy(gk.killer.name, kptr->name); + svk.killer.format = kptr->format; + Strcpy(svk.killer.name, kptr->name); } else { - gk.killer.format = KILLED_BY_AN; - gk.killer.name[0] = 0; /* take the default */ + svk.killer.format = KILLED_BY_AN; + svk.killer.name[0] = 0; /* take the default */ } dealloc_killer(kptr); - if ((m_idx = name_to_mon(gk.killer.name, + if ((m_idx = name_to_mon(svk.killer.name, (int *) 0)) >= LOW_PM) { if (type_is_pname(&mons[m_idx])) { - gk.killer.format = KILLED_BY; + svk.killer.format = KILLED_BY; } else if (mons[m_idx].geno & G_UNIQ) { - Strcpy(gk.killer.name, the(gk.killer.name)); - gk.killer.format = KILLED_BY; + Strcpy(svk.killer.name, the(svk.killer.name)); + svk.killer.format = KILLED_BY; } } done_timeout(POISONING, SICK); @@ -725,7 +774,7 @@ nh_timeout(void) break; case WITHERING: You("are no longer withering away."); - gc.context.botl = TRUE; + disp.botl = TRUE; break; case DOOMED: pline("The pall of doom around you lifts."); @@ -760,7 +809,7 @@ nh_timeout(void) case DEAF: set_itimeout(&HDeaf, 1L); make_deaf(0L, TRUE); - gc.context.botl = TRUE; + disp.botl = TRUE; if (!Deaf) stop_occupation(); break; @@ -813,7 +862,7 @@ nh_timeout(void) case FLYING: /* timed Flying is via #wizintrinsic only */ if (was_flying && !Flying) { - gc.context.botl = 1; + disp.botl = TRUE; if (!is_open_air(u.ux, u.uy)) { You("land."); } @@ -821,15 +870,33 @@ nh_timeout(void) } break; case ACID_RES: - if (!Acid_resistance && !Unaware) - You("no longer feel safe from acid."); + if (!Acid_resistance) { + if (eating_dangerous_corpse(ACID_RES)) { + /* extend temporary acid resistance if in midst + of eating an acidic corpse; this will repeat + until eating is finished or interrupted */ + set_itimeout(&u.uprops[ACID_RES].intrinsic, 1L); + break; + } + if (!Unaware) + You("no longer feel safe from acid."); + } break; case STONE_RES: if (!Stone_resistance) { + if (eating_dangerous_corpse(STONE_RES)) { + /* extend temporary stoning resistance if in midst + of eating a stoning corpse; this will repeat + until eating is finished or interrupted */ + set_itimeout(&u.uprops[STONE_RES].intrinsic, 1L); + break; + } if (!Unaware) You("no longer feel secure from petrification."); /* no-op if not wielding a cockatrice corpse; - uswapwep case is always a no-op (see Gloves_off()) */ + uswapwep case is always a no-op because two-weapon + combat is only possible with two one-handed weapons + or weapon tools, not corpses */ wielding_corpse(uwep, (struct obj *) 0, FALSE); wielding_corpse(uswapwep, (struct obj *) 0, FALSE); } @@ -843,7 +910,7 @@ nh_timeout(void) Your("temporary ability to survive burning has ended."); break; case WWALKING: - /* [see fire reeistance] */ + /* [see fire resistance] */ if (!Wwalking) Your("temporary ability to walk on liquid has ended."); break; @@ -854,10 +921,10 @@ nh_timeout(void) case WARN_OF_MON: /* timed Warn_of_mon is via #wizintrinsic only */ if (!Warn_of_mon) { - struct permonst *wptr = gc.context.warntype.species; + struct permonst *wptr = svc.context.warntype.species; - gc.context.warntype.species = (struct permonst *) 0; - gc.context.warntype.speciesidx = NON_PM; + svc.context.warntype.species = (struct permonst *) 0; + svc.context.warntype.speciesidx = NON_PM; if (wptr) You("are no longer warned about %s.", makeplural(wptr->pmnames[NEUTRAL])); @@ -872,9 +939,16 @@ nh_timeout(void) !Upolyd ? "normal" : "unusual"); } break; + case MAGICAL_BREATHING: + if (!Breathless) { + if (region_danger()) + You("cough%s", + Poison_resistance ? "." : " and spit blood!"); + } + break; case STRANGLED: - gk.killer.format = KILLED_BY; - Strcpy(gk.killer.name, + svk.killer.format = KILLED_BY; + Strcpy(svk.killer.name, (u.uburied) ? "suffocation" : "strangulation"); done_timeout(DIED, STRANGLED); /* must be declining to die in explore|wizard mode; @@ -901,7 +975,7 @@ nh_timeout(void) You("forget the rules to Charades!"); else if (!Deaf) You("make a lot of noise!"); - wake_nearby(); + wake_nearby(FALSE); } } /* from outside means slippery ice; don't reset @@ -926,6 +1000,12 @@ nh_timeout(void) if (!Regeneration) You_feel("enervated."); break; + case PROT_FROM_SHAPE_CHANGERS: + /* timed Protection_from_shape_changers is via + #wizintrinsic only */ + if (!Protection_from_shape_changers) + restartcham(); + break; } } } @@ -950,12 +1030,12 @@ fall_asleep(int how_long, boolean wakeup_msg) /* 3.7: how_long is negative so wasn't actually incrementing the deafness timeout when it used to be passed as-is */ incr_itimeout(&HDeaf, abs(how_long)); - gc.context.botl = TRUE; + disp.botl = TRUE; ga.afternmv = Hear_again; /* this won't give any messages */ } #endif /* early wakeup from combat won't be possible until next monster turn */ - u.usleep = gm.moves; + u.usleep = svm.moves; gn.nomovemsg = wakeup_msg ? "You wake up." : You_can_move_again; } @@ -964,7 +1044,7 @@ fall_asleep(int how_long, boolean wakeup_msg) * existing hatch timer. Pass 0L for random hatch time. */ void -attach_egg_hatch_timeout(struct obj* egg, long when) +attach_egg_hatch_timeout(struct obj *egg, long when) { int i; @@ -992,7 +1072,7 @@ attach_egg_hatch_timeout(struct obj* egg, long when) /* prevent an egg from ever hatching */ void -kill_egg(struct obj* egg) +kill_egg(struct obj *egg) { /* stop previous timer, if any */ (void) stop_timer(HATCH_EGG, obj_to_any(egg)); @@ -1019,32 +1099,32 @@ hatch_egg(anything *arg, long timeout) mnum = big_to_little(egg->corpsenm); /* The identity of one's father is learned, not innate */ yours = (egg->spe || (!flags.female && carried(egg) && !rn2(2))); - silent = (timeout != gm.moves); /* hatched while away */ + silent = (timeout != svm.moves); /* hatched while away */ - /* only can hatch when in INVENT, FLOOR, MINVENT */ + /* only can hatch when in INVENT, FLOOR, MINVENT; + get_obj_location() will fail for MIGRATING, also for CONTAINED + and BURIED when the flags for those aren't included in the call */ if (get_obj_location(egg, &x, &y, 0)) { hatchcount = rnd((int) egg->quan); cansee_hatchspot = cansee(x, y) && !silent; if (!(mons[mnum].geno & G_UNIQ) - && !(gm.mvitals[mnum].mvflags & (G_GENOD | G_EXTINCT))) { + && !(svm.mvitals[mnum].mvflags & (G_GENOD | G_EXTINCT))) { for (i = hatchcount; i > 0; i--) { if (!enexto(&cc, x, y, &mons[mnum]) || !(mon = makemon(&mons[mnum], cc.x, cc.y, - NO_MINVENT|MM_NOMSG))) + NO_MINVENT | MM_NOMSG))) break; /* tame if your own egg hatches while you're on the same dungeon level, or any dragon egg which hatches while it's in your inventory */ if ((yours && !silent) || (carried(egg) && mon->data->mlet == S_DRAGON)) { - if (tamedog(mon, (struct obj *) 0, FALSE)) { - if (carried(egg) && mon->data->mlet != S_DRAGON) { + if (tamedog(mon, (struct obj *) 0, FALSE, FALSE)) { + if (carried(egg) && mon->data->mlet != S_DRAGON) mon->mtame = 20; - u.uconduct.pets++; - } } } - if (gm.mvitals[mnum].mvflags & G_EXTINCT) + if (svm.mvitals[mnum].mvflags & G_EXTINCT) break; /* just made last one */ mon2 = mon; /* in case makemon() fails on 2nd egg */ } @@ -1152,9 +1232,14 @@ hatch_egg(anything *arg, long timeout) learn_egg_type(mnum); if (egg->quan > 0) { - /* still some eggs left */ - /* Instead of ordinary egg timeout use a short one */ + /* still some eggs left; we didn't split the stack, just + subtracted from quantity so weight needs to be updated; + for remainder of stack, add a new, short hatch timer */ attach_egg_hatch_timeout(egg, (long) rnd(12)); + /* container_weight(arg) updates arg->owt, and if contained, + its enclosing container arg->ocontainer (recursively) + [egg won't be contained due to conditions imposed above] */ + container_weight(egg); } else if (carried(egg)) { useup(egg); } else { @@ -1175,7 +1260,7 @@ learn_egg_type(int mnum) { /* baby monsters hatch from grown-up eggs */ mnum = little_to_big(mnum); - gm.mvitals[mnum].mvflags |= MV_KNOWS_EGG; + svm.mvitals[mnum].mvflags |= MV_KNOWS_EGG; /* we might have just learned about other eggs being carried */ update_inventory(); } @@ -1199,15 +1284,13 @@ attach_fig_transform_timeout(struct obj *figurine) } /* give a fumble message */ -static void +staticfn void slip_or_trip(void) { - struct obj *otmp = vobj_at(u.ux, u.uy), *otmp2; + struct obj *otmp = vobj_at(u.ux, u.uy), *otmp2, *saddle; const char *what; char buf[BUFSZ]; - boolean on_foot = TRUE; - if (u.usteed) - on_foot = FALSE; + boolean on_foot = !u.usteed; if (otmp && on_foot && !u.uinwater && is_pool(u.ux, u.uy)) otmp = 0; @@ -1238,18 +1321,48 @@ slip_or_trip(void) } if (!uarmf && otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm]) && !Stone_resistance) { - Sprintf(gk.killer.name, "tripping over %s corpse", + Sprintf(svk.killer.name, "tripping over %s corpse", an(mons[otmp->corpsenm].pmnames[NEUTRAL])); - instapetrify(gk.killer.name); + instapetrify(svk.killer.name); } - } else if (rn2(3) && is_ice(u.ux, u.uy)) { - pline("%s %s%s on the ice.", - u.usteed ? upstart(x_monnam(u.usteed, - (has_mgivenname(u.usteed)) ? ARTICLE_NONE - : ARTICLE_THE, - (char *) 0, SUPPRESS_SADDLE, FALSE)) + } else if ((HFumbling & FROMOUTSIDE) || (is_ice(u.ux, u.uy) && !rn2(3))) { + /* is fumbling from ice alone? */ + boolean ice_only = !(EFumbling || (HFumbling & ~FROMOUTSIDE)); + + pline("%s %s %s the ice.", + u.usteed ? upstart(x_monnam(u.usteed, ARTICLE_THE, (char *) 0, + SUPPRESS_SADDLE, FALSE)) : "You", - rn2(2) ? "slip" : "slide", on_foot ? "" : "s"); + /* "steed": arbitrary value that will use third person verb + regardless of what u.usteed might be named, as opposed to + "you" (second person, which won't have final 's' added) */ + vtense(u.usteed ? "steed" : "you", rn2(2) ? "slip" : "slide"), + /* sometimes slipping due to ice occurs during turn that hero + has just moved off the ice; phrase things differently then */ + is_ice(u.ux, u.uy) ? "on" : "off"); + /* fumbling outside of ice while mounted always causes the hero to + fall from the saddle (unless it is cursed), so to avoid a + counterintuitive effect where ice makes riding _less_ hazardous, + unconditionally dismount if fumbling is from a non-ice source */ + if (!on_foot + && ((saddle = which_armor(u.usteed, W_SADDLE)) == 0 + || !saddle->cursed) + && (!ice_only || !rn2(3))) { + You("lose your balance."); + dismount_steed(DISMOUNT_FELL); + } else if (!rn2(10 + ACURR(A_DEX))) { + /* Maybe slip in a random direction. This takes place after + the hero has already changed location. If the hero is + in grid bug form, only allow forward hurtle, otherwise a + 90 degree orthogonal one after the step would make the + combined move appear to be a single diagonal step. */ + if (!NODIAG(u.umonnum)) + confdir(TRUE); /* sets u.dx and u.dy */ + /* Only hurtle if the random direction won't move hero back + to same spot where this move started. */ + if (u.ux + u.dx != u.ux0 || u.uy + u.dy != u.uy0) + hurtle(u.dx, u.dy, 1, FALSE); + } } else { if (on_foot) { switch (rn2(4)) { @@ -1268,7 +1381,11 @@ slip_or_trip(void) You("stumble."); break; } - } else { + + /* mounted; saddle should never end up being Null here; + don't fall off when it happens to be cursed */ + } else if ((saddle = which_armor(u.usteed, W_SADDLE)) == 0 + || !saddle->cursed) { switch (rn2(4)) { case 1: Your("%s slip out of the stirrups.", @@ -1290,7 +1407,7 @@ slip_or_trip(void) } /* Print a lamp flicker message with tailer. Only called if seen. */ -static void +staticfn void see_lamp_flicker(struct obj *obj, const char *tailer) { switch (obj->where) { @@ -1305,7 +1422,7 @@ see_lamp_flicker(struct obj *obj, const char *tailer) } /* Print a dimming message for brass lanterns. Only called if seen. */ -static void +staticfn void lantern_message(struct obj *obj) { /* from adventure */ @@ -1328,7 +1445,7 @@ lantern_message(struct obj *obj) } /* - * Timeout callback for for objects that are burning. E.g. lamps, candles. + * Timeout callback for objects that are burning. E.g. lamps, candles. * See begin_burn() for meanings of obj->age and obj->spe. */ void @@ -1343,8 +1460,8 @@ burn_object(anything *arg, long timeout) many = menorah ? obj->spe > 1 : obj->quan > 1L; /* timeout while away */ - if (timeout != gm.moves) { - long how_long = gm.moves - timeout; + if (timeout != svm.moves) { + long how_long = svm.moves - timeout; if (how_long >= obj->age) { obj->age = 0; @@ -1398,6 +1515,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); @@ -1461,6 +1579,7 @@ burn_object(anything *arg, long timeout) switch (obj->where) { case OBJ_INVENT: need_invupdate = TRUE; + FALLTHROUGH; /*FALLTHRU*/ case OBJ_MINVENT: if (obj->otyp == LANTERN) @@ -1540,6 +1659,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, @@ -1555,6 +1675,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), @@ -1772,7 +1893,7 @@ end_burn(struct obj *obj, boolean timer_attached) /* * Cleanup a burning object if timer stopped. */ -static void +staticfn void cleanup_burn(anything *arg, long expire_time) { struct obj *obj = arg->a_obj; @@ -1784,7 +1905,7 @@ cleanup_burn(anything *arg, long expire_time) del_light_source(LS_OBJECT, obj_to_any(obj)); /* restore unused time */ - obj->age += expire_time - gm.moves; + obj->age += expire_time - svm.moves; obj->lamplit = 0; if (obj->where == OBJ_INVENT) @@ -1795,12 +1916,12 @@ void do_storms(void) { int nstrike; - register int x, y; + int x, y; int dirx, diry; int count; /* no lightning if not stormy level or too often, even then */ - if (!gl.level.flags.stormy || rn2(8)) + if (!svl.level.flags.stormy || rn2(8)) return; /* the number of strikes is 8-log2(nstrike) */ @@ -1828,7 +1949,7 @@ do_storms(void) Soundeffect(se_kaboom_boom_boom, 80); pline("Kaboom!!! Boom!! Boom!!"); incr_itimeout(&HDeaf, rn1(20, 30)); - gc.context.botl = TRUE; + disp.botl = TRUE; if (!u.uinvulnerable || !(uamul && uamul->oartifact == ART_AMULET_OF_STORMS)) { stop_occupation(); @@ -1851,7 +1972,7 @@ do_storms(void) * boolean start_timer(long timeout,short kind,short func_index, * anything *arg) * Start a timer of kind 'kind' that will expire at time - * gm.moves+'timeout'. Call the function at 'func_index' + * svm.moves+'timeout'. Call the function at 'func_index' * in the timeout table using argument 'arg'. Return TRUE if * a timer was started. This places the timer on a list ordered * "sooner" to "later". If an object, increment the object's @@ -1899,14 +2020,14 @@ do_storms(void) * Check whether object has a timer of type timer_type. */ -static const char *kind_name(short); -static void print_queue(winid, timer_element *); -static void insert_timer(timer_element *); -static timer_element *remove_timer(timer_element **, short, ANY_P *); -static void write_timer(NHFILE *, timer_element *); -static boolean mon_is_local(struct monst *); -static boolean timer_is_local(timer_element *); -static int maybe_write_timer(NHFILE *, int, boolean); +staticfn const char *kind_name(short); +staticfn void print_queue(winid, timer_element *); +staticfn void insert_timer(timer_element *); +staticfn timer_element *remove_timer(timer_element **, short, ANY_P *); +staticfn void write_timer(NHFILE *, timer_element *); +staticfn boolean mon_is_local(struct monst *); +staticfn boolean timer_is_local(timer_element *); +staticfn int maybe_write_timer(NHFILE *, int, boolean); /* If defined, then include names when printing out the timer queue */ #define VERBOSE_TIMER @@ -1921,8 +2042,11 @@ typedef struct { #endif } ttable; -/* table of timeout functions */ +/* + * Table of timeout functions, listed in order of enum timeout_types: + */ static const ttable timeout_funcs[NUM_TIME_FUNCS] = { + /* object timers */ TTAB(rot_organic, (timeout_proc) 0, "rot_organic"), TTAB(rot_corpse, (timeout_proc) 0, "rot_corpse"), TTAB(revive_mon, (timeout_proc) 0, "revive_mon"), @@ -1930,17 +2054,22 @@ static const ttable timeout_funcs[NUM_TIME_FUNCS] = { TTAB(burn_object, cleanup_burn, "burn_object"), TTAB(hatch_egg, (timeout_proc) 0, "hatch_egg"), TTAB(fig_transform, (timeout_proc) 0, "fig_transform"), - TTAB(melt_ice_away, (timeout_proc) 0, "melt_ice_away"), TTAB(shrink_glob, (timeout_proc) 0, "shrink_glob"), TTAB(moldy_corpse, (timeout_proc) 0, "moldy_corpse"), - TTAB(ferment, (timeout_proc) 0, "ferment") + TTAB(ferment, (timeout_proc) 0, "ferment"), + /* level timers */ + TTAB(melt_ice_away, (timeout_proc) 0, "melt_ice_away"), + /* currently no monster or global timers */ }; #undef TTAB -static const char * +staticfn const char * kind_name(short kind) { switch (kind) { + case TIMER_NONE: + impossible("no timer type"); + return "none"; case TIMER_LEVEL: return "level"; case TIMER_GLOBAL: @@ -1953,8 +2082,8 @@ kind_name(short kind) return "unknown"; } -static void -print_queue(winid win, timer_element* base) +staticfn void +print_queue(winid win, timer_element *base) { timer_element *curr; char buf[BUFSZ]; @@ -1993,7 +2122,7 @@ wiz_timeout_queue(void) if (win == WIN_ERR) return ECMD_OK; - Sprintf(buf, "Current time = %ld.", gm.moves); + Sprintf(buf, "Current time = %ld.", svm.moves); putstr(win, 0, buf); putstr(win, 0, ""); putstr(win, 0, "Active timeout queue:"); @@ -2027,7 +2156,7 @@ wiz_timeout_queue(void) intrinsic = u.uprops[p].intrinsic; if (intrinsic & TIMEOUT) { if (specindx > 0 && i >= specindx) { - putstr(win, 0, " -- settable via #wizinstrinc only --"); + putstr(win, 0, " -- settable via #wizintrinsic only --"); specindx = 0; } /* timeout value can be up to 16777215 (0x00ffffff) but @@ -2052,6 +2181,9 @@ wiz_timeout_queue(void) Sprintf(buf, "Vault counter is %d.", u.uinvault); putstr(win, 0, buf); } + if (any_visible_region()) { + visible_region_summary(win); + } display_nhwindow(win, FALSE); destroy_nhwindow(win); @@ -2062,28 +2194,80 @@ void timer_sanity_check(void) { timer_element *curr; + unsigned long t_id; + coordxy x, y; - /* this should be much more complete */ for (curr = gt.timer_base; curr; curr = curr->next) { - if (curr->kind == TIMER_OBJECT) { - struct obj *obj = curr->arg.a_obj; + t_id = curr->tid; + switch (curr->kind) { + case TIMER_OBJECT: { + /* TODO? verify that the timer type is attached to applicable + object (egg for hatch, glob for shrink, and so forth) */ + struct obj *obj = curr->arg.a_obj, *top; + char *obj_adr = fmt_ptr((genericptr_t) obj); + int owhere = obj->where; if (obj->timed == 0) { - impossible("timer sanity: untimed obj %s, timer %ld", - fmt_ptr((genericptr_t) obj), curr->tid); + impossible("timer sanity: untimed obj %s, timer %lu", + obj_adr, t_id); } - } else if (curr->kind == TIMER_LEVEL) { - long where = curr->arg.a_long; - coordxy x = (coordxy) ((where >> 16) & 0xFFFF), - y = (coordxy) (where & 0xFFFF); - - if (!isok(x, y)) { + x = y = 0; + /* if obj is in a container, possibly a nested one, figure out + where the outermost container is */ + for (top = obj; top; top = top->ocontainer) + if ((owhere = top->where) != OBJ_CONTAINED) + break; + assert(top != NULL); + if (owhere == OBJ_MIGRATING + || (owhere == OBJ_MINVENT && !mon_is_local(top->ocarry))) { + /* migrating directly or carried by migrating monster */ + ; /* not able to validate location so skip checks */ + } else if (!get_obj_location(obj, &x, &y, + CONTAINED_TOO | BURIED_TOO)) { + /* free? or on a shop's used-up bill? */ + impossible( + "timer sanity: can't locate obj %s [where=%d], timer %lu", + obj_adr, obj->where, t_id); + } else if (!isok(x, y)) { + impossible( + "timer sanity: obj %s [where=%d] located at <%d,%d>, timer %lu", + obj_adr, obj->where, x, y, t_id); + } + break; + } + case TIMER_MONSTER: + impossible("timer sanity: unexpected monster timer %lu", t_id); + break; + case TIMER_LEVEL: { + long lwhere = curr->arg.a_long; + + x = (coordxy) ((lwhere >> 16) & 0xFFFF); + y = (coordxy) (lwhere & 0xFFFF); + if (isok(x, y)) { + /* replicate isok() in order to convince static analysis + that the decoding via '& 0xFFFF' hasn't produced a value + too big for levl[][] and that the cast to a narrower type + hasn't intruded on the sign bit to yield a negative value; + 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)) + impossible( + "timer sanity: melt timer %lu on non-ice %d <%d,%d>", + t_id, levl[x][y].typ, x, y); + } else { impossible("timer sanity: spot timer %lu at <%d,%d>", - curr->tid, x, y); - } else if (curr->func_index == MELT_ICE_AWAY && !is_ice(x, y)) { - impossible("timer sanity: melt timer %lu on non-ice %d <%d,%d>", - curr->tid, levl[x][y].typ, x, y); + t_id, x, y); } + break; + } + case TIMER_GLOBAL: + impossible("timer sanity: unexpected global timer %lu", t_id); + break; + default: + impossible("timer sanity: unknown timer %lu, type: %d", + t_id, curr->kind); + break; } } } @@ -2102,13 +2286,14 @@ run_timers(void) * any time. The list is ordered, we are done when the first element * is in the future. */ - while (gt.timer_base && gt.timer_base->timeout <= gm.moves) { + while (gt.timer_base && gt.timer_base->timeout <= svm.moves) { curr = gt.timer_base; gt.timer_base = curr->next; if (curr->kind == TIMER_OBJECT) (curr->arg.a_obj)->timed--; (*timeout_funcs[curr->func_index].f)(&curr->arg, curr->timeout); + (void) memset((genericptr_t) curr, 0, sizeof(timer_element)); free((genericptr_t) curr); } } @@ -2125,7 +2310,7 @@ start_timer( { timer_element *gnu, *dup; - if (kind < 0 || kind >= NUM_TIMER_KINDS + if (kind <= TIMER_NONE || kind >= NUM_TIMER_KINDS || func_index < 0 || func_index >= NUM_TIME_FUNCS) panic("start_timer (%s: %d)", kind_name(kind), (int) func_index); @@ -2150,8 +2335,8 @@ start_timer( gnu = (timer_element *) alloc(sizeof *gnu); (void) memset((genericptr_t) gnu, 0, sizeof *gnu); gnu->next = 0; - gnu->tid = gt.timer_id++; - gnu->timeout = gm.moves + when; + gnu->tid = svt.timer_id++; + gnu->timeout = svm.moves + when; gnu->kind = kind; gnu->needs_fixup = 0; gnu->func_index = func_index; @@ -2183,8 +2368,9 @@ stop_timer(short func_index, anything *arg) (arg->a_obj)->timed--; if ((cleanup_func = timeout_funcs[doomed->func_index].cleanup) != 0) (*cleanup_func)(arg, timeout); + (void) memset((genericptr_t) doomed, 0, sizeof(timer_element)); free((genericptr_t) doomed); - return (timeout - gm.moves); + return (timeout - svm.moves); } return 0L; } @@ -2208,7 +2394,7 @@ peek_timer(short type, anything *arg) * Move all object timers from src to dest, leaving src untimed. */ void -obj_move_timers(struct obj* src, struct obj* dest) +obj_move_timers(struct obj *src, struct obj *dest) { int count; timer_element *curr; @@ -2228,14 +2414,14 @@ obj_move_timers(struct obj* src, struct obj* dest) * Find all object timers and duplicate them for the new object "dest". */ void -obj_split_timers(struct obj* src, struct obj* dest) +obj_split_timers(struct obj *src, struct obj *dest) { timer_element *curr, *next_timer = 0; for (curr = gt.timer_base; curr; curr = next_timer) { next_timer = curr->next; /* things may be inserted */ if (curr->kind == TIMER_OBJECT && curr->arg.a_obj == src) { - (void) start_timer(curr->timeout - gm.moves, TIMER_OBJECT, + (void) start_timer(curr->timeout - svm.moves, TIMER_OBJECT, curr->func_index, obj_to_any(dest)); } } @@ -2246,7 +2432,7 @@ obj_split_timers(struct obj* src, struct obj* dest) * all object pointers are unique. */ void -obj_stop_timers(struct obj* obj) +obj_stop_timers(struct obj *obj) { timeout_proc cleanup_func; timer_element *curr, *prev, *next_timer = 0; @@ -2260,6 +2446,7 @@ obj_stop_timers(struct obj* obj) gt.timer_base = curr->next; if ((cleanup_func = timeout_funcs[curr->func_index].cleanup) != 0) (*cleanup_func)(&curr->arg, curr->timeout); + (void) memset((genericptr_t) curr, 0, sizeof(timer_element)); free((genericptr_t) curr); } else { prev = curr; @@ -2272,7 +2459,7 @@ obj_stop_timers(struct obj* obj) * Check whether object has a timer of type timer_type. */ boolean -obj_has_timer(struct obj* object, short timer_type) +obj_has_timer(struct obj *object, short timer_type) { long timeout = peek_timer(timer_type, obj_to_any(object)); @@ -2300,6 +2487,7 @@ spot_stop_timers(coordxy x, coordxy y, short func_index) gt.timer_base = curr->next; if ((cleanup_func = timeout_funcs[curr->func_index].cleanup) != 0) (*cleanup_func)(&curr->arg, curr->timeout); + (void) memset((genericptr_t) curr, 0, sizeof(timer_element)); free((genericptr_t) curr); } else { prev = curr; @@ -2329,12 +2517,12 @@ long spot_time_left(coordxy x, coordxy y, short func_index) { long expires = spot_time_expires(x, y, func_index); - return (expires > 0L) ? expires - gm.moves : 0L; + return (expires > 0L) ? expires - svm.moves : 0L; } /* Insert timer into the global queue */ -static void -insert_timer(timer_element* gnu) +staticfn void +insert_timer(timer_element *gnu) { timer_element *curr, *prev; @@ -2349,7 +2537,7 @@ insert_timer(timer_element* gnu) gt.timer_base = gnu; } -static timer_element * +staticfn timer_element * remove_timer( timer_element **base, short func_index, @@ -2371,8 +2559,8 @@ remove_timer( return curr; } -static void -write_timer(NHFILE* nhfp, timer_element* timer) +staticfn void +write_timer(NHFILE *nhfp, timer_element *timer) { anything arg_save; @@ -2388,7 +2576,7 @@ write_timer(NHFILE* nhfp, timer_element* timer) case TIMER_OBJECT: if (timer->needs_fixup) { if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t)timer, sizeof(timer_element)); + bwrite(nhfp->fd, (genericptr_t) timer, sizeof(timer_element)); } else { /* replace object pointer with id */ arg_save.a_obj = timer->arg.a_obj; @@ -2396,7 +2584,7 @@ write_timer(NHFILE* nhfp, timer_element* timer) 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)); + bwrite(nhfp->fd, (genericptr_t) timer, sizeof(timer_element)); timer->arg.a_obj = arg_save.a_obj; timer->needs_fixup = 0; } @@ -2405,7 +2593,7 @@ 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)); + bwrite(nhfp->fd, (genericptr_t) timer, sizeof(timer_element)); } else { /* replace monster pointer with id */ arg_save.a_monst = timer->arg.a_monst; @@ -2413,7 +2601,7 @@ write_timer(NHFILE* nhfp, timer_element* timer) 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)); + bwrite(nhfp->fd, (genericptr_t) timer, sizeof(timer_element)); timer->arg.a_monst = arg_save.a_monst; timer->needs_fixup = 0; } @@ -2432,7 +2620,7 @@ DISABLE_WARNING_UNREACHABLE_CODE * saved. */ boolean -obj_is_local(struct obj* obj) +obj_is_local(struct obj *obj) { switch (obj->where) { case OBJ_INVENT: @@ -2455,8 +2643,8 @@ obj_is_local(struct obj* obj) * Return TRUE if the given monster will stay on the level when the * level is saved. */ -static boolean -mon_is_local(struct monst* mon) +staticfn boolean +mon_is_local(struct monst *mon) { struct monst *curr; @@ -2474,8 +2662,8 @@ mon_is_local(struct monst* mon) * Return TRUE if the timer is attached to something that will stay on the * level when the level is saved. */ -static boolean -timer_is_local(timer_element* timer) +staticfn boolean +timer_is_local(timer_element *timer) { switch (timer->kind) { case TIMER_LEVEL: @@ -2498,8 +2686,8 @@ RESTORE_WARNING_UNREACHABLE_CODE * Part of the save routine. Count up the number of timers that would * be written. If write_it is true, actually write the timer. */ -static int -maybe_write_timer(NHFILE* nhfp, int range, boolean write_it) +staticfn int +maybe_write_timer(NHFILE *nhfp, int range, boolean write_it) { int count = 0; timer_element *curr; @@ -2540,7 +2728,7 @@ maybe_write_timer(NHFILE* nhfp, int range, boolean write_it) * + timeouts that stay with the level (obj & monst) */ void -save_timers(NHFILE* nhfp, int range) +save_timers(NHFILE *nhfp, int range) { timer_element *curr, *prev, *next_timer = 0; int count; @@ -2548,7 +2736,8 @@ save_timers(NHFILE* nhfp, int range) if (perform_bwrite(nhfp)) { if (range == RANGE_GLOBAL) { if (nhfp->structlevel) - bwrite(nhfp->fd, (genericptr_t) >.timer_id, sizeof(gt.timer_id)); + bwrite(nhfp->fd, (genericptr_t) &svt.timer_id, + sizeof svt.timer_id); } count = maybe_write_timer(nhfp, range, FALSE); if (nhfp->structlevel) @@ -2565,6 +2754,7 @@ save_timers(NHFILE* nhfp, int range) prev->next = curr->next; else gt.timer_base = curr->next; + (void) memset((genericptr_t) curr, 0, sizeof(timer_element)); free((genericptr_t) curr); /* prev stays the same */ } else { @@ -2579,7 +2769,7 @@ save_timers(NHFILE* nhfp, int range) * monster pointers. */ void -restore_timers(NHFILE* nhfp, int range, long adjust) +restore_timers(NHFILE *nhfp, int range, long adjust) { int count = 0; timer_element *curr; @@ -2587,7 +2777,8 @@ restore_timers(NHFILE* nhfp, int range, long adjust) if (range == RANGE_GLOBAL) { if (nhfp->structlevel) - mread(nhfp->fd, (genericptr_t) >.timer_id, sizeof gt.timer_id); + mread(nhfp->fd, (genericptr_t) &svt.timer_id, + sizeof svt.timer_id); } /* restore elements */ @@ -2639,7 +2830,7 @@ relink_timers(boolean ghostly) nid = curr->arg.a_uint; curr->arg.a_obj = find_oid(nid); if (!curr->arg.a_obj) - panic("cant find o_id %d", nid); + panic("can't find o_id %d", nid); curr->needs_fixup = 0; } else if (curr->kind == TIMER_MONSTER) { panic("relink_timers: no monster timer implemented"); diff --git a/src/topten.c b/src/topten.c index c618b74bb7..028e530caf 100644 --- a/src/topten.c +++ b/src/topten.c @@ -24,7 +24,7 @@ static long final_fpos; /* [note: do not move this to the 'g' struct] */ #endif -#define done_stopprint gp.program_state.stopprint +#define done_stopprint program_state.stopprint #define newttentry() (struct toptenentry *) alloc(sizeof (struct toptenentry)) #define dealloc_ttentry(ttent) free((genericptr_t) (ttent)) @@ -52,38 +52,39 @@ struct toptenentry { char plalign[ROLESZ + 1]; char name[NAMSZ + 1]; char death[DTHSZ + 1]; -} *tt_head; +}; +static struct toptenentry *tt_head; /* size big enough to read in all the string fields at once; includes room for separating space or trailing newline plus string terminator */ #define SCANBUFSZ (4 * (ROLESZ + 1) + (NAMSZ + 1) + (DTHSZ + 1) + 1) static struct toptenentry zerott; -static void topten_print(const char *); -static void topten_print_bold(const char *); -static void outheader(void); -static void outentry(int, struct toptenentry *, boolean); -static void discardexcess(FILE *); -static void readentry(FILE *, struct toptenentry *); -static void writeentry(FILE *, struct toptenentry *); +staticfn void topten_print(const char *); +staticfn void topten_print_bold(const char *); +staticfn void outheader(void); +staticfn void outentry(int, struct toptenentry *, boolean); +staticfn void discardexcess(FILE *); +staticfn void readentry(FILE *, struct toptenentry *); +staticfn void writeentry(FILE *, struct toptenentry *); #ifdef XLOGFILE -static void writexlentry(FILE *, struct toptenentry *, int); -static long encodexlogflags(void); -static long encodeachieve(boolean); -static void add_achieveX(char *, const char *, boolean); -static char *encode_extended_achievements(char *); -static char *encode_extended_conducts(char *); +staticfn void writexlentry(FILE *, struct toptenentry *, int); +staticfn long encodexlogflags(void); +staticfn long encodeachieve(boolean); +staticfn void add_achieveX(char *, const char *, boolean); +staticfn char *encode_extended_achievements(char *); +staticfn char *encode_extended_conducts(char *); #endif -static void free_ttlist(struct toptenentry *); -static int classmon(char *); -static int score_wanted(boolean, int, struct toptenentry *, int, +staticfn void free_ttlist(struct toptenentry *); +staticfn int classmon(char *); +staticfn int score_wanted(boolean, int, struct toptenentry *, int, const char **, int); #ifdef NO_SCAN_BRACK -static void nsb_mung_line(char *); -static void nsb_unmung_line(char *); +staticfn void nsb_mung_line(char *); +staticfn void nsb_unmung_line(char *); #endif -/* "killed by",&c ["an"] 'gk.killer.name' */ +/* "killed by",&c ["an"] 'svk.killer.name' */ void formatkiller( char *buf, @@ -102,17 +103,19 @@ formatkiller( "", "", "", "", "" }; unsigned l; - char c, *kname = gk.killer.name; + char c, *kname = svk.killer.name; buf[0] = '\0'; /* lint suppression */ - switch (gk.killer.format) { + switch (svk.killer.format) { default: - impossible("bad killer format? (%d)", gk.killer.format); + 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); @@ -157,7 +160,7 @@ formatkiller( } } -static void +staticfn void topten_print(const char *x) { if (gt.toptenwin == WIN_ERR) @@ -166,7 +169,7 @@ topten_print(const char *x) putstr(gt.toptenwin, ATR_NONE, x); } -static void +staticfn void topten_print_bold(const char *x) { if (gt.toptenwin == WIN_ERR) @@ -200,7 +203,7 @@ observable_depth(d_level *lev) } /* throw away characters until current record has been entirely consumed */ -static void +staticfn void discardexcess(FILE *rfile) { int c; @@ -212,7 +215,7 @@ discardexcess(FILE *rfile) DISABLE_WARNING_FORMAT_NONLITERAL -static void +staticfn void readentry(FILE *rfile, struct toptenentry *tt) { char inbuf[SCANBUFSZ], s1[SCANBUFSZ], s2[SCANBUFSZ], s3[SCANBUFSZ], @@ -293,7 +296,7 @@ readentry(FILE *rfile, struct toptenentry *tt) } } -static void +staticfn void writeentry(FILE *rfile, struct toptenentry *tt) { static const char fmt32[] = "%c%c "; /* role,gender */ @@ -331,8 +334,8 @@ RESTORE_WARNING_FORMAT_NONLITERAL #ifdef XLOGFILE -/* as tab is never used in eg. gp.plname or death, no need to mangle those. */ -static void +/* as tab is never used in eg. svp.plname or death, no need to mangle those. */ +staticfn void writexlentry(FILE *rfile, struct toptenentry *tt, int how) { #define Fprintf (void) fprintf @@ -359,7 +362,7 @@ writexlentry(FILE *rfile, struct toptenentry *tt, int how) formatkiller(tmpbuf, sizeof tmpbuf, how, FALSE); Fprintf(rfile, "%s%cname=%s%cdeath=%s", buf, /* (already includes separator) */ - XLOG_SEP, gp.plname, XLOG_SEP, tmpbuf); + XLOG_SEP, svp.plname, XLOG_SEP, tmpbuf); if (gm.multi < 0 || stuck) { tmpbuf[0] = '\0'; if (gm.multi < 0) { @@ -372,7 +375,7 @@ writexlentry(FILE *rfile, struct toptenentry *tt, int how) Fprintf(rfile, "%cwhile=%s", XLOG_SEP, tmpbuf); } Fprintf(rfile, "%cconduct=0x%lx%cturns=%ld%cachieve=0x%lx", XLOG_SEP, - encodeconduct(), XLOG_SEP, gm.moves, XLOG_SEP, + encodeconduct(), XLOG_SEP, svm.moves, XLOG_SEP, encodeachieve(FALSE)); Fprintf(rfile, "%cachieveX=%s", XLOG_SEP, encode_extended_achievements(achbuf)); @@ -397,7 +400,7 @@ writexlentry(FILE *rfile, struct toptenentry *tt, int how) #undef XLOG_SEP } -static long +staticfn long encodexlogflags(void) { long e = 0L; @@ -485,7 +488,7 @@ encodeconduct(void) return e; } -static long +staticfn long encodeachieve( boolean secondlong) /* False: handle achievements 1..31, True: 32..62 */ { @@ -510,7 +513,7 @@ encodeachieve( } /* add the achievement or conduct comma-separated to string */ -static void +staticfn void add_achieveX(char *buf, const char *achievement, boolean condition) { if (condition) { @@ -521,7 +524,7 @@ add_achieveX(char *buf, const char *achievement, boolean condition) } } -static char * +staticfn char * encode_extended_achievements(char *buf) { char rnkbuf[40]; @@ -626,10 +629,10 @@ encode_extended_achievements(char *buf) * becomes > 0 when you get the Amulet; you can kill all the archfiends * without getting the Amulet */ struct fiend_info *fnd = lookup_fiend(i); - if (fnd->escaped || fnd->num_in_dgn > 0 || gm.mvitals[i].born == 0) + if (fnd->escaped || fnd->num_in_dgn > 0 || svm.mvitals[i].born == 0) /* did not dispatch the fiend */ break; - if (gm.mvitals[i].born > gm.mvitals[i].died) + if (svm.mvitals[i].born > svm.mvitals[i].died) /* bribed, doesn't count for the achievement */ break; } @@ -640,7 +643,7 @@ encode_extended_achievements(char *buf) return buf; } -static char * +staticfn char * encode_extended_conducts(char *buf) { buf[0] = '\0'; @@ -661,6 +664,7 @@ encode_extended_conducts(char *buf) add_achieveX(buf, "blind", u.uroleplay.blind); add_achieveX(buf, "deaf", u.uroleplay.deaf); add_achieveX(buf, "nudist", u.uroleplay.nudist); + add_achieveX(buf, "pauper", u.uroleplay.pauper); add_achieveX(buf, "bonesless", !u.uroleplay.numbones); add_achieveX(buf, "petless", !u.uconduct.pets); add_achieveX(buf, "artifactless", !u.uconduct.artitouch); @@ -676,7 +680,7 @@ encode_extended_conducts(char *buf) #endif /* XLOGFILE */ -static void +staticfn void free_ttlist(struct toptenentry *tt) { struct toptenentry *ttnext; @@ -692,7 +696,7 @@ free_ttlist(struct toptenentry *tt) void topten(int how, time_t when) { - register struct toptenentry *t0, *tprev; + struct toptenentry *t0, *tprev; struct toptenentry *t1; FILE *rfile; #ifdef LOGFILE @@ -714,7 +718,7 @@ topten(int how, time_t when) * topten uses alloc() several times, which will lead to * problems if the panic was the result of an alloc() failure. */ - if (gp.program_state.panicking) + if (program_state.panicking) return; if (iflags.toptenwin) { @@ -722,7 +726,7 @@ topten(int how, time_t when) } #if defined(HANGUPHANDLING) -#define HUP if (!gp.program_state.done_hup) +#define HUP if (!program_state.done_hup) #else #define HUP #endif @@ -755,7 +759,7 @@ topten(int how, time_t when) copynchars(t0->plrace, gu.urace.filecode, ROLESZ); copynchars(t0->plgend, genders[flags.female].filecode, ROLESZ); copynchars(t0->plalign, aligns[1 - u.ualign.type].filecode, ROLESZ); - copynchars(t0->name, gp.plname, NAMSZ); + copynchars(t0->name, svp.plname, NAMSZ); formatkiller(t0->death, sizeof t0->death, how, TRUE); t0->birthdate = yyyymmdd(ubirthday); t0->deathdate = yyyymmdd(when); @@ -990,11 +994,11 @@ topten(int how, time_t when) } } -static void +staticfn void outheader(void) { char linebuf[BUFSZ]; - register char *bp; + char *bp; Strcpy(linebuf, " No Points Name"); bp = eos(linebuf); @@ -1007,7 +1011,7 @@ outheader(void) DISABLE_WARNING_FORMAT_NONLITERAL /* so>0: standout line; so=0: ordinary line */ -static void +staticfn void outentry(int rank, struct toptenentry *t1, boolean so) { boolean second_line = TRUE; @@ -1092,7 +1096,7 @@ outentry(int rank, struct toptenentry *t1, boolean so) } Sprintf(eos(linebuf), fmt, arg); } else { - Sprintf(eos(linebuf), " in %s", gd.dungeons[t1->deathdnum].dname); + Sprintf(eos(linebuf), " in %s", svd.dungeons[t1->deathdnum].dname); if (t1->deathdnum != knox_level.dnum) Sprintf(eos(linebuf), " on level %d", t1->deathlev); if (t1->deathlev != t1->maxlvl) @@ -1173,7 +1177,7 @@ outentry(int rank, struct toptenentry *t1, boolean so) RESTORE_WARNING_FORMAT_NONLITERAL -static int +staticfn int score_wanted( boolean current_ver, int rank, @@ -1260,7 +1264,7 @@ prscore(int argc, char **argv) { const char **players, *player0; int i, playerct, rank; - register struct toptenentry *t1; + struct toptenentry *t1; FILE *rfile; char pbuf[BUFSZ], *p; unsigned ln; @@ -1323,7 +1327,7 @@ prscore(int argc, char **argv) playerct = 0; players = (const char **) 0; } else { - player0 = gp.plname; + player0 = svp.plname; if (!*player0) player0 = "all"; /* if no plname[], show all scores * (possibly filtered by '-v') */ @@ -1417,7 +1421,7 @@ prscore(int argc, char **argv) #endif } -static int +staticfn int classmon(char *plch) { int i; @@ -1447,7 +1451,7 @@ get_rnd_toptenentry(void) { int rank, i; FILE *rfile; - register struct toptenentry *tt; + struct toptenentry *tt; static struct toptenentry tt_buf; rfile = fopen_datafile(RECORD, "r", SCOREPREFIX); @@ -1515,11 +1519,34 @@ tt_oname(struct obj *otmp) return otmp; } +/* Randomly select a topten entry to mimic */ +int +tt_doppel(struct monst *mon) { + struct toptenentry *tt = rn2(13) ? get_rnd_toptenentry() : NULL; + int ret; + + if (!tt) + ret = rn1(PM_WIZARD - PM_ARCHEOLOGIST + 1, PM_ARCHEOLOGIST); + else { + if (tt->plgend[0] == 'F') + mon->female = 1; + else if (tt->plgend[0] == 'M') + mon->female = 0; + ret = classmon(tt->plrole); + /* Only take on a name if the player can see + the doppelganger, otherwise we end up with + named monsters spoiling the fun - Kes */ + if (canseemon(mon)) + christen_monst(mon, tt->name); + } + return ret; +} + #ifdef NO_SCAN_BRACK /* Lattice scanf isn't up to reading the scorefile. What */ /* follows deals with that; I admit it's ugly. (KL) */ /* Now generally available (KL) */ -static void +staticfn void nsb_mung_line(p) char *p; { @@ -1527,7 +1554,7 @@ char *p; *p = '|'; } -static void +staticfn void nsb_unmung_line(p) char *p; { diff --git a/src/track.c b/src/track.c index b5ccd2d7fc..e9c4923f80 100644 --- a/src/track.c +++ b/src/track.c @@ -6,7 +6,7 @@ #include "hack.h" -#define UTSZ 50 +#define UTSZ 100 static NEARDATA int utcnt, utpnt; static NEARDATA coord utrack[UTSZ]; @@ -15,6 +15,7 @@ void initrack(void) { utcnt = utpnt = 0; + (void) memset((genericptr_t) &utrack, 0, sizeof(utrack)); } /* add to track */ @@ -30,11 +31,13 @@ settrack(void) utpnt++; } +/* get a track coord on or next to x,y and last tracked by hero, + returns null if no such track */ coord * gettrack(coordxy x, coordxy y) { - register int cnt, ndist; - register coord *tc; + int cnt, ndist; + coord *tc; cnt = utcnt; for (tc = &utrack[utpnt]; cnt--;) { if (tc == utrack) @@ -43,20 +46,63 @@ gettrack(coordxy x, coordxy y) tc--; ndist = distmin(x, y, tc->x, tc->y); - /* if far away, skip track entries til we're closer */ - if (ndist > 2) { - ndist -= 2; /* be careful due to extra decrement at top of loop */ - cnt -= ndist; - if (cnt <= 0) - return (coord *) 0; /* too far away, no matches possible */ - if (tc < &utrack[ndist]) - tc += (UTSZ - ndist); - else - tc -= ndist; - } else if (ndist <= 1) + if (ndist <= 1) return (ndist ? tc : 0); } return (coord *) 0; } +/* return TRUE if x,y has hero tracks on it */ +boolean +hastrack(coordxy x, coordxy y) +{ + int i; + + for (i = 0; i < utcnt; i++) + if (utrack[i].x == x && utrack[i].y == y) + return TRUE; + + return FALSE; +} + +/* save the hero tracking info */ +void +save_track(NHFILE *nhfp) +{ + if (perform_bwrite(nhfp)) { + if (nhfp->structlevel) { + 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); + } + } + } + if (release_data(nhfp)) + initrack(); +} + +/* restore the hero tracking info */ +void +rest_track(NHFILE *nhfp) +{ + if (nhfp->structlevel) { + 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); + } + } +} + /*track.c*/ diff --git a/src/trap.c b/src/trap.c index 01aacd52cf..2e1d079b2f 100644 --- a/src/trap.c +++ b/src/trap.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 trap.c $NHDT-Date: 1684140131 2023/05/15 08:42:11 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.536 $ */ +/* NetHack 3.7 trap.c $NHDT-Date: 1720128169 2024/07/04 21:22:49 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.602 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -7,64 +7,72 @@ extern const char *const destroy_strings[][3]; /* from zap.c */ -static int dng_bottom(d_level *lev); -static void hole_destination(d_level *); -static boolean keep_saddle_with_steedcorpse(unsigned, struct obj *, +staticfn void mk_trap_statue(coordxy, coordxy); +staticfn int dng_bottom(d_level *lev); +staticfn void hole_destination(d_level *); +staticfn boolean keep_saddle_with_steedcorpse(unsigned, struct obj *, struct obj *); -static boolean mu_maybe_destroy_web(struct monst *, boolean, struct trap *); -static boolean floor_trigger(int); -static boolean check_in_air(struct monst *, unsigned); -static int trapeffect_arrow_trap(struct monst *, struct trap *, unsigned); -static int trapeffect_rocktrap(struct monst *, struct trap *, unsigned); -static int trapeffect_sqky_board(struct monst *, struct trap *, unsigned); -static int trapeffect_bear_trap(struct monst *, struct trap *, unsigned); -static int trapeffect_slp_gas_trap(struct monst *, struct trap *, unsigned); -static int trapeffect_rust_trap(struct monst *, struct trap *, unsigned); -static int trapeffect_fire_trap(struct monst *, struct trap *, unsigned); -static int trapeffect_pit(struct monst *, struct trap *, unsigned); -static int trapeffect_hole(struct monst *, struct trap *, unsigned); -static int trapeffect_telep_trap(struct monst *, struct trap *, unsigned); -static int trapeffect_level_telep(struct monst *, struct trap *, unsigned); -static int trapeffect_web(struct monst *, struct trap *, unsigned); -static int trapeffect_statue_trap(struct monst *, struct trap *, unsigned); -static int trapeffect_magic_trap(struct monst *, struct trap *, unsigned); -static int trapeffect_anti_magic(struct monst *, struct trap *, unsigned); -static int trapeffect_poly_trap(struct monst *, struct trap *, unsigned); -static int trapeffect_landmine(struct monst *, struct trap *, unsigned); -static int trapeffect_rolling_boulder_trap(struct monst *, struct trap *, +staticfn boolean mu_maybe_destroy_web(struct monst *, boolean, struct trap *); +staticfn struct obj *t_missile(int, struct trap *); +staticfn boolean floor_trigger(int); +staticfn boolean check_in_air(struct monst *, unsigned); +staticfn int trapeffect_arrow_trap(struct monst *, struct trap *, unsigned); +staticfn int trapeffect_dart_trap(struct monst *, struct trap *, unsigned); +staticfn int trapeffect_rocktrap(struct monst *, struct trap *, unsigned); +staticfn int trapeffect_sqky_board(struct monst *, struct trap *, unsigned); +staticfn int trapeffect_bear_trap(struct monst *, struct trap *, unsigned); +staticfn int trapeffect_slp_gas_trap(struct monst *, struct trap *, unsigned); +staticfn int trapeffect_rust_trap(struct monst *, struct trap *, unsigned); +staticfn int trapeffect_fire_trap(struct monst *, struct trap *, unsigned); +staticfn int trapeffect_pit(struct monst *, struct trap *, unsigned); +staticfn int trapeffect_hole(struct monst *, struct trap *, unsigned); +staticfn int trapeffect_telep_trap(struct monst *, struct trap *, unsigned); +staticfn int trapeffect_level_telep(struct monst *, struct trap *, unsigned); +staticfn int trapeffect_web(struct monst *, struct trap *, unsigned); +staticfn int trapeffect_statue_trap(struct monst *, struct trap *, unsigned); +staticfn int trapeffect_magic_trap(struct monst *, struct trap *, unsigned); +staticfn int trapeffect_anti_magic(struct monst *, struct trap *, unsigned); +staticfn int trapeffect_poly_trap(struct monst *, struct trap *, unsigned); +staticfn int trapeffect_landmine(struct monst *, struct trap *, unsigned); +staticfn int trapeffect_rolling_boulder_trap(struct monst *, struct trap *, unsigned); -static int trapeffect_magic_portal(struct monst *, struct trap *, unsigned); -static int trapeffect_vibrating_square(struct monst *, struct trap *, - unsigned); -static int trapeffect_selector(struct monst *, struct trap *, unsigned); -static int choose_trapnote(struct trap *); -static int steedintrap(struct trap *, struct obj *); -static void launch_drop_spot(struct obj *, coordxy, coordxy); -static boolean find_random_launch_coord(struct trap *, coord *); -static int mkroll_launch(struct trap *, coordxy, coordxy, short, long); -static boolean isclearpath(coord *, int, schar, schar); -static void dofiretrap(struct obj *); -static void domagictrap(void); -static boolean emergency_disrobe(boolean *); -static int untrap_prob(struct trap *); -static void move_into_trap(struct trap *); -static int try_disarm(struct trap *, boolean); -static void reward_untrap(struct trap *, struct monst *); -static int disarm_holdingtrap(struct trap *); -static int disarm_landmine(struct trap *); -static int unsqueak_ok(struct obj *); -static int disarm_squeaky_board(struct trap *); -static int disarm_shooting_trap(struct trap *); -static void clear_conjoined_pits(struct trap *); -static boolean adj_nonconjoined_pit(struct trap *); -static int try_lift(struct monst *, struct trap *, int, boolean); -static int help_monster_out(struct monst *, struct trap *); -static void untrap_box(struct obj *, boolean, boolean); +staticfn int trapeffect_magic_portal(struct monst *, struct trap *, unsigned); +staticfn int trapeffect_vibrating_square(struct monst *, struct trap *, + unsigned); +staticfn int trapeffect_selector(struct monst *, struct trap *, unsigned); +staticfn char *trapnote(struct trap *, boolean); +staticfn int choose_trapnote(struct trap *); +staticfn int steedintrap(struct trap *, struct obj *); +staticfn void launch_drop_spot(struct obj *, coordxy, coordxy); +staticfn boolean find_random_launch_coord(struct trap *, coord *); +staticfn int mkroll_launch(struct trap *, coordxy, coordxy, short, long); +staticfn boolean isclearpath(coord *, int, schar, schar); +staticfn boolean m_easy_escape_pit(struct monst *) NONNULLARG1; +staticfn void dofiretrap(struct obj *); +staticfn void domagictrap(void); +staticfn void pot_acid_damage(struct obj *, boolean, boolean); +staticfn boolean emergency_disrobe(boolean *); +staticfn int untrap_prob(struct trap *); +staticfn void move_into_trap(struct trap *); +staticfn int try_disarm(struct trap *, boolean); +staticfn void reward_untrap(struct trap *, struct monst *); +staticfn int disarm_holdingtrap(struct trap *); +staticfn int disarm_landmine(struct trap *); +staticfn int unsqueak_ok(struct obj *); +staticfn int disarm_squeaky_board(struct trap *); +staticfn int disarm_shooting_trap(struct trap *); +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 -static void join_adjacent_pits(struct trap *); +staticfn void join_adjacent_pits(struct trap *); #endif -static boolean thitm(int, struct monst *, struct obj *, int, boolean); -static void maybe_finish_sokoban(void); +staticfn boolean thitm(int, struct monst *, struct obj *, int, + boolean) NONNULLARG2; +staticfn void maybe_finish_sokoban(void); static const char *const a_your[2] = { "a", "your" }; static const char *const A_Your[2] = { "A", "Your" }; @@ -77,7 +85,7 @@ static const char *const blindgas[6] = { "humid", "odorless", /* called when you're hit by fire (dofiretrap,buzz,zapyourself,explode); returns TRUE if hit on torso */ boolean -burnarmor(struct monst* victim) +burnarmor(struct monst *victim) { struct obj *item; char buf[BUFSZ]; @@ -161,18 +169,20 @@ burnarmor(struct monst* victim) */ int erode_obj( - register struct obj *otmp, + struct obj *otmp, const char *ostr, int type, int ef_flags) { static NEARDATA const char - *const action[] = { "smoulder", "rust", "rot", "corrode" }, - *const msg[] = { "burnt", "rusted", "rotten", "corroded" }, - *const bythe[] = { "heat", "oxidation", "decay", "corrosion" }; + *const action[] = { "smoulder", "rust", "rot", "corrode", "crack" }, + *const msg[] = { "burnt", "rusted", "rotten", "corroded", "cracked" }, + *const bythe[] = { "heat", "oxidation", "decay", "corrosion", + "impact" }; /* this could use improvement... */ boolean vulnerable = FALSE, is_primary = TRUE, check_grease = (ef_flags & EF_GREASE) ? TRUE : FALSE, print = (ef_flags & EF_VERBOSE) ? TRUE : FALSE, + crackers = FALSE, /* True: different feedback if otmp destroyed */ uvictim, vismon, visobj; int erosion, cost_type; struct monst *victim; @@ -192,7 +202,7 @@ erode_obj( switch (type) { case ERODE_BURN: - if (victim && adtyp_resistance_obj(victim, AD_FIRE)) + if (victim && inventory_resistance_check(victim, AD_FIRE)) return ER_NOTHING; vulnerable = is_flammable(otmp); check_grease = FALSE; @@ -209,12 +219,18 @@ erode_obj( cost_type = COST_ROT; break; case ERODE_CORRODE: - if (victim && adtyp_resistance_obj(victim, AD_ACID)) + if (victim && inventory_resistance_check(victim, AD_ACID)) return ER_NOTHING; vulnerable = is_corrodeable(otmp); is_primary = FALSE; cost_type = COST_CORRODE; break; + case ERODE_CRACK: /* crystal armor */ + vulnerable = is_crackable(otmp); + is_primary = TRUE; + crackers = TRUE; + cost_type = COST_CRACK; + break; default: impossible("Invalid erosion type in erode_obj"); return ER_NOTHING; @@ -233,18 +249,18 @@ erode_obj( } else if (!erosion_matters(otmp)) { return ER_NOTHING; } else if (!vulnerable || (otmp->oerodeproof && otmp->rknown)) { - if (Verbose(3, erode_obj1) && print && (uvictim || vismon)) + if (flags.verbose && print && (uvictim || vismon)) pline("%s %s %s not affected by %s.", uvictim ? "Your" : s_suffix(Monnam(victim)), ostr, vtense(ostr, "are"), bythe[type]); return ER_NOTHING; } else if (otmp->oerodeproof || (otmp->blessed && !rnl(4))) { - if (Verbose(3, erode_obj2) && (print || otmp->oerodeproof) + if (flags.verbose && (print || otmp->oerodeproof) && (uvictim || vismon || visobj)) pline("Somehow, %s %s %s not affected by the %s.", uvictim ? "your" - : !vismon ? "the" /* visobj */ - : s_suffix(mon_nam(victim)), + : !vismon ? "the" /* visobj */ + : s_suffix(mon_nam(victim)), ostr, vtense(ostr, "are"), bythe[type]); /* We assume here that if the object is protected because it * is blessed, it still shows some minor signs of wear, and @@ -266,8 +282,8 @@ erode_obj( if (uvictim || vismon || visobj) pline("%s %s %s%s!", uvictim ? "Your" - : !vismon ? "The" /* visobj */ - : s_suffix(Monnam(victim)), + : !vismon ? "The" /* visobj */ + : s_suffix(Monnam(victim)), ostr, vtense(ostr, action[type]), adverb); if (ef_flags & EF_PAY) @@ -284,13 +300,19 @@ erode_obj( return ER_DAMAGED; } else if (ef_flags & EF_DESTROY) { otmp->in_use = 1; /* in case of hangup during message w/ --More-- */ - if (uvictim || vismon || visobj) - pline("%s %s %s away!", - uvictim ? "Your" - : !vismon ? "The" /* visobj */ - : s_suffix(Monnam(victim)), - ostr, vtense(ostr, action[type])); + if (uvictim || vismon || visobj) { + char actbuf[BUFSZ]; + if (!crackers) + Sprintf(actbuf, "%s away", vtense(ostr, action[type])); + else + Sprintf(actbuf, "shatters"); + pline("%s %s %s!", + uvictim ? "Your" + : !vismon ? "The" /* visobj */ + : s_suffix(Monnam(victim)), + ostr, actbuf); + } if (ef_flags & EF_PAY) costly_alteration(otmp, cost_type); @@ -318,7 +340,7 @@ erode_obj( delobj(otmp); return ER_DESTROYED; } else { - if (Verbose(3, erode_obj3) && print) { + if (flags.verbose && print) { if (uvictim) Your("%s %s completely %s.", ostr, vtense(ostr, Blind ? "feel" : "look"), msg[type]); @@ -336,7 +358,7 @@ erode_obj( */ boolean grease_protect( - register struct obj *otmp, + struct obj *otmp, const char *ostr, struct monst *victim) { @@ -373,15 +395,14 @@ mk_trap_statue(coordxy x, coordxy y) int trycount = 10; do { /* avoid ultimately hostile co-aligned unicorn */ - mptr = &mons[rndmonnum()]; + mptr = &mons[rndmonnum_adj(3, 6)]; } while (--trycount > 0 && is_unicorn(mptr) && sgn(u.ualign.type) == sgn(mptr->maligntyp)); statue = mkcorpstat(STATUE, (struct monst *) 0, mptr, x, y, CORPSTAT_NONE); /* randomize the material */ init_obj_material(statue); - mtmp = makemon(&mons[statue->corpsenm], 0, 0, - MM_NOCOUNTBIRTH|MM_NOMSG); + mtmp = makemon(&mons[statue->corpsenm], 0, 0, MM_NOCOUNTBIRTH|MM_NOMSG); if (!mtmp) return; /* should never happen */ while (mtmp->minvent) { @@ -395,7 +416,7 @@ mk_trap_statue(coordxy x, coordxy y) } /* find "bottom" level of specified dungeon, stopping at quest locate */ -static int +staticfn int dng_bottom(d_level *lev) { int bottom = dunlevs_in_dungeon(lev); @@ -419,7 +440,7 @@ dng_bottom(d_level *lev) } /* destination dlevel for holes or trapdoors */ -static void +staticfn void hole_destination(d_level *dst) { int bottom = dng_bottom(&u.uz); @@ -437,7 +458,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]; struct obj *otmp; @@ -456,9 +477,10 @@ maketrap(coordxy x, coordxy y, int typ) || (u.utraptype == TT_LAVA && !is_lava(x, y)))) reset_utrap(FALSE); /* old remain valid */ - } else if ((IS_FURNITURE(lev->typ) - && (!IS_GRAVE(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 @@ -468,7 +490,7 @@ maketrap(coordxy x, coordxy y, int typ) } else { oldplace = FALSE; ttmp = newtrap(); - (void) memset((genericptr_t)ttmp, 0, sizeof(struct trap)); + (void) memset((genericptr_t) ttmp, 0, sizeof(struct trap)); ttmp->ntrap = 0; ttmp->tx = x; ttmp->ty = y; @@ -493,6 +515,7 @@ maketrap(coordxy x, coordxy y, int typ) case PIT: case SPIKED_PIT: ttmp->conjoined = 0; + FALLTHROUGH; /*FALLTHRU*/ case HOLE: case TRAPDOOR: @@ -502,24 +525,53 @@ 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)) - && !gc.context.mon_moving) - ? SHOP_HOLE_COST - : 0L); - lev->flags = 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) - (void) set_levltyp(x, y, gl.level.flags.is_maze_lev ? ROOM - : gl.level.flags.is_cavernous_lev ? CORR - : DOOR); + } 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)) { + ttmp->teledest.x = gx.xstart + gl.launchplace.x; + ttmp->teledest.y = gy.ystart + gl.launchplace.y; + if (ttmp->teledest.x == x && ttmp->teledest.y == y) { + impossible("making fixed-dest tele trap pointing to itself"); + } + } break; case ROCKTRAP: otmp = mksobj(ROCK, TRUE, FALSE); @@ -547,22 +599,13 @@ maketrap(coordxy x, coordxy y, int typ) case LANDMINE: set_trap_ammo(ttmp, mksobj(LAND_MINE, TRUE, FALSE)); break; - case TELEP_TRAP: - if (gl.launchplace.x > 0) { - ttmp->teledest.x = gx.xstart + gl.launchplace.x; - ttmp->teledest.y = gy.ystart + gl.launchplace.y; - if (ttmp->teledest.x == x && ttmp->teledest.y == y) { - impossible("making fixed-dest tele trap pointing to itself"); - } - } - break; case VIBRATING_SQUARE: if (!Invocation_lev(&u.uz)) { impossible("creating vibrating square on wrong level"); } else { - gi.inv_pos.x = x; - gi.inv_pos.y = y; + svi.inv_pos.x = x; + svi.inv_pos.y = y; } break; } @@ -800,7 +843,7 @@ animate_statue( mon = makemon(&mons[PM_DOPPELGANGER], x, y, mmflags); /* if hero has protection from shape changers, cham field will be NON_PM; otherwise, set form to match the statue */ - if (mon && mon->cham >= LOW_PM) + if (mon && ismnum(mon->cham)) (void) newcham(mon, mptr, NO_NC_FLAGS); } else { if (cause == ANIMATE_SPELL) @@ -835,7 +878,8 @@ animate_statue( comes_to_life = !canspotmon(mon) ? "disappears" : golem_xform ? "turns into flesh" - : (nonliving(mon->data) || is_vampshifter(mon)) ? "moves" + : (nonliving(mon->data) || is_vampshifter(mon)) + ? "moves" : "comes to life"; if (u_at(x, y) || cause == ANIMATE_SPELL) { /* "the|your|Manlobbi's statue [of a wombat]" */ @@ -861,6 +905,7 @@ animate_statue( pline("Instead of shattering, %s suddenly %s!", statuename, comes_to_life); } else { /* cause == ANIMATE_NORMAL */ + set_msg_xy(x, y); You("find %s posing as a statue.", canspotmon(mon) ? a_monnam(mon) : something); if (!canspotmon(mon) && Blind) @@ -870,7 +915,7 @@ animate_statue( /* if this isn't caused by a monster using a wand of striking, there might be consequences for the hero */ - if (!gc.context.mon_moving) { + if (!svc.context.mon_moving) { /* if statue is owned by a shop, hero will have to pay for it; stolen_value gives a message (about debt or use of credit) which refers to "it" so needs to follow a message describing @@ -952,7 +997,7 @@ activate_statue_trap( return mtmp; } -static boolean +staticfn boolean keep_saddle_with_steedcorpse( unsigned steed_mid, struct obj *objchn, @@ -985,7 +1030,7 @@ keep_saddle_with_steedcorpse( /* monster or you go through and possibly destroy a web. return TRUE if could go through. */ -static boolean +staticfn boolean mu_maybe_destroy_web( struct monst *mtmp, boolean domsg, @@ -1006,7 +1051,8 @@ mu_maybe_destroy_web( (flaming(mptr)) ? "burn" : "dissolve", a_your[trap->madeby_u]); else - pline("%s %s %s spider web!", Monnam(mtmp), + pline_mon(mtmp, + "%s %s %s spider web!", Monnam(mtmp), (flaming(mptr)) ? "burns" : "dissolves", a_your[trap->madeby_u]); } @@ -1018,7 +1064,8 @@ mu_maybe_destroy_web( if (isyou) { You("flow through %s spider web.", a_your[trap->madeby_u]); } else { - pline("%s flows through %s spider web.", Monnam(mtmp), + pline_mon(mtmp, + "%s flows through %s spider web.", Monnam(mtmp), a_your[trap->madeby_u]); seetrap(trap); } @@ -1028,173 +1075,6 @@ mu_maybe_destroy_web( return FALSE; } -/* Will a monster suffer any adverse effects from a certain trap? - * Note: this does NOT mean "will a monster trigger a trap in the first place", - * though if it won't that does imply that they'll not suffer adverse effects. - * For example, an elf is considered immune to sleeping gas traps even though - * they'll set the trap off. - * Return value: - * TRAP_NOT_IMMUNE = not immune at the moment - * TRAP_CLEARLY_IMMUNE = obviously immune (assuming player is spoiled on which - * monsters are immune to various traps) - * TRAP_HIDDEN_IMMUNE = immune but in a non-obvious way such as an unidentified - * item or hidden intrinsic providing a resistance; the player should still - * be warned of this trap, while monsters implicitly know they're immune. - * When mon is not the player, this function should never return this. */ -enum trap_immunities -immune_to_trap(struct monst *mon, int ttype) -{ - if (!mon) { - impossible("immune_to_trap: null monster"); - return TRAP_NOT_IMMUNE; - } - struct permonst * pm = mon->data; - boolean you = (mon == &gy.youmonst); - - switch(ttype) { - case ARROW_TRAP: - case DART_TRAP: - case ROCKTRAP: - /* can hit anything. Even noncorporeal monsters might get a blessed - * projectile. */ - return TRAP_NOT_IMMUNE; - case BEAR_TRAP: - if (pm->msize <= MZ_SMALL || amorphous(pm) || is_whirly(pm) - || unsolid(pm)) - return TRAP_CLEARLY_IMMUNE; - /* FALLTHRU */ - case SQKY_BOARD: - case LANDMINE: - case ROLLING_BOULDER_TRAP: - case HOLE: - case TRAPDOOR: - case PIT: - case SPIKED_PIT: - /* ground-based traps, which can be evaded by levitation, flying, or - * hanging to the ceiling */ - if (Sokoban && (ttype == PIT || ttype == SPIKED_PIT || ttype == HOLE - || ttype == TRAPDOOR)) - return TRAP_NOT_IMMUNE; - if (!grounded(pm)) - return TRAP_CLEARLY_IMMUNE; - else if (you && (Levitation || Flying)) - return TRAP_CLEARLY_IMMUNE; - return TRAP_NOT_IMMUNE; - case SLP_GAS_TRAP: - if (breathless(pm)) - return TRAP_CLEARLY_IMMUNE; - else if (!you && resists_sleep(mon)) - return TRAP_CLEARLY_IMMUNE; - else if (you && Sleep_resistance) - return TRAP_HIDDEN_IMMUNE; - return TRAP_NOT_IMMUNE; - case LEVEL_TELEP: - case TELEP_TRAP: - /* Consider unintended teleporting to be an adverse effect. If in the - * endgame or carrying the Amulet, the teleport trap won't work anyway, - * so anything hitting it is immune. */ - if (In_endgame(&u.uz) || mon_has_amulet(mon)) - return TRAP_CLEARLY_IMMUNE; - return TRAP_NOT_IMMUNE; - case POLY_TRAP: - if (resists_magm(mon)) - /* covers Antimagic for player */ - return (you ? TRAP_HIDDEN_IMMUNE : TRAP_CLEARLY_IMMUNE); - return TRAP_NOT_IMMUNE; - case STATUE_TRAP: - /* no effect on monsters, only affects players; even so, a player should - * never actually be able to see a statue trap without triggering it; - * in case they ever can, do consider it an adverse effect */ - if (!you) - return TRAP_CLEARLY_IMMUNE; - return TRAP_NOT_IMMUNE; - case WEB: - /* most of this code is lifted from mu_maybe_destroy_web */ - if (webmaker(pm) || amorphous(pm) || is_whirly(pm) || flaming(pm) - || unsolid(pm) || pm == &mons[PM_GELATINOUS_CUBE]) - return TRAP_CLEARLY_IMMUNE; - return TRAP_NOT_IMMUNE; - case ANTI_MAGIC: - /* doesn't hurt any non-magic-resistant monster with no magic */ - if (you) { - if (Antimagic) - return TRAP_NOT_IMMUNE; - else if (u.uenmax == 0) - /* player won't lose HP and can't lose more Pw */ - return TRAP_HIDDEN_IMMUNE; - } - /* following conditional lifted from mintrap ANTI_MAGIC logic */ - else if (!resists_magm(mon) - && (mon->mcan || (!attacktype(pm, AT_MAGC) - && !attacktype(pm, AT_BREA)))) { - return TRAP_CLEARLY_IMMUNE; - } - return TRAP_NOT_IMMUNE; - case RUST_TRAP: - /* harmful if wearing anything rustable or if the monster is an iron - * golem */ - if (pm == &mons[PM_IRON_GOLEM]) - return TRAP_NOT_IMMUNE; - else { - struct obj * obj = (you ? gi.invent : mon->minvent); - for (; obj; obj = obj->nobj) { - /* rust traps can currently hit only worn armor and weapons */ - if (is_rustprone(obj) && obj->owornmask) { - return TRAP_NOT_IMMUNE; - } - } - } - return TRAP_CLEARLY_IMMUNE; - case MAGIC_TRAP: - /* for player, any number of bad effects. - * for monsters, only replicates fire trap, so fall through */ - if (you) - return TRAP_NOT_IMMUNE; - /* else FALLTHRU */ - case FIRE_TRAP: /* can always destroy items being carried */ - /* harmful if wearing anything burnable or if the monster isn't - * resistant */ - if (you && !Fire_resistance) - return TRAP_NOT_IMMUNE; - else if (!you && !resists_fire(mon)) - return TRAP_NOT_IMMUNE; - else { - struct obj * obj = (you ? gi.invent : mon->minvent); - for (; obj; obj = obj->nobj) { - if (obj->oclass == SCROLL_CLASS || obj->oclass == POTION_CLASS - || obj->oclass == SPBOOK_CLASS - || (obj->owornmask && is_flammable(obj))) - return TRAP_NOT_IMMUNE; - } - } - return (you ? TRAP_HIDDEN_IMMUNE : TRAP_CLEARLY_IMMUNE); - case COLD_TRAP: - /* always potentially harmful to player */ - if (you) - return TRAP_NOT_IMMUNE; - /* but normally not harmful to monsters since they can't lose cold - * resistance; however, they can lose it if it's acquired, so check for - * that */ - else { - if (mon->mintrinsics & MR_COLD) - return TRAP_NOT_IMMUNE; - return resists_cold(mon) ? TRAP_CLEARLY_IMMUNE : TRAP_NOT_IMMUNE; - } - case MAGIC_PORTAL: - /* never hurts anything, but player is considered non-immune so they - * can be asked about entering it */ - if (!you) - return TRAP_CLEARLY_IMMUNE; - return TRAP_NOT_IMMUNE; - case VIBRATING_SQUARE: - /* no adverse effects */ - return TRAP_CLEARLY_IMMUNE; - default: - impossible("immune_to_trap: bad ttype %d", ttype); - } - return TRAP_NOT_IMMUNE; -} - void set_utrap(unsigned int tim, unsigned int typ) { @@ -1202,7 +1082,7 @@ set_utrap(unsigned int tim, unsigned int typ) have already set u.utrap to 0 so this check won't be sufficient in that situation; caller will need to set context.botl itself */ if (!u.utrap ^ !tim) - gc.context.botl = TRUE; + disp.botl = TRUE; u.utrap = tim; u.utraptype = tim ? typ : TT_NONE; @@ -1225,8 +1105,8 @@ reset_utrap(boolean msg) } } -/* is trap type ttyp triggered by touching the floor? */ -static boolean +/* is trap type ttyp triggered by touching the floor? */ +staticfn boolean floor_trigger(int ttyp) { switch (ttyp) { @@ -1253,20 +1133,19 @@ floor_trigger(int ttyp) /* return TRUE if monster mtmp is up in the air, considering trap flags * This has nothing to do with air terrain. */ -static boolean +staticfn boolean check_in_air(struct monst *mtmp, unsigned trflags) { - boolean plunged = (trflags & TOOKPLUNGE) != 0; + boolean is_you = mtmp == &gy.youmonst, + plunged = (trflags & (TOOKPLUNGE | VIASITTING)) != 0; - return (is_floater(mtmp->data) - || (is_flyer(mtmp->data) && !plunged) - || (trflags & HURTLING) != 0 - || (mtmp == &gy.youmonst ? - (Levitation || (Flying && !plunged)) : 0)); + return ((trflags & HURTLING) != 0 + || (is_you ? Levitation : is_floater(mtmp->data)) + || ((is_you ? Flying : is_flyer(mtmp->data)) && !plunged)); } /* also used for dart traps - they behave the same */ -static int +staticfn int trapeffect_arrow_trap( struct monst *mtmp, struct trap *trap, @@ -1318,8 +1197,9 @@ trapeffect_arrow_trap( if (!trap->ammo) { if (in_sight && see_it) - pline("%s triggers a trap but nothing happens.", - Monnam(mtmp)); + pline_mon(mtmp, + "%s triggers a trap but nothing happens.", + Monnam(mtmp)); deltrap(trap); newsym(mtmp->mx, mtmp->my); return Trap_Is_Gone; @@ -1340,7 +1220,7 @@ trapeffect_arrow_trap( return Trap_Effect_Finished; } -static int +staticfn int trapeffect_rocktrap( struct monst *mtmp, struct trap *trap, @@ -1375,10 +1255,10 @@ trapeffect_rocktrap( pline("Unfortunately, you are wearing %s.", an(helm_simple_name(uarmh))); /* helm or hat */ dmg = 2; - } else if (is_hard(uarmh)) { + } else if (hard_helmet(uarmh)) { pline("Fortunately, you are wearing a hard helmet."); dmg = 2; - } else if (Verbose(3, trapeffect_rocktrap)) { + } else if (flags.verbose) { pline("%s does not protect you.", Yname2(uarmh)); } } else if (passes_rocks(gy.youmonst.data)) { @@ -1402,8 +1282,9 @@ trapeffect_rocktrap( if (!trap->ammo) { if (in_sight && see_it) - pline("A trap door above %s opens, but nothing falls out!", - mon_nam(mtmp)); + pline_mon(mtmp, + "A trap door above %s opens, but nothing falls out!", + mon_nam(mtmp)); deltrap(trap); newsym(mtmp->mx, mtmp->my); return Trap_Is_Gone; @@ -1424,7 +1305,7 @@ trapeffect_rocktrap( return Trap_Effect_Finished; } -static int +staticfn int trapeffect_sqky_board( struct monst *mtmp, struct trap *trap, @@ -1437,7 +1318,8 @@ trapeffect_sqky_board( se_squeak_A, se_squeak_B_flat, se_squeak_B, }; boolean forcetrap = ((trflags & FORCETRAP) != 0 - || (trflags & FAILEDUNTRAP) != 0); + || (trflags & FAILEDUNTRAP) != 0 + || (Flying && (trflags & VIASITTING) != 0)); const char *board = Hallucination ? "chicken" : "board"; const char *squeak = Hallucination ? "squawk" : "squeak"; @@ -1452,14 +1334,14 @@ trapeffect_sqky_board( } } else { seetrap(trap); - if (trap->tnote >= 0 && trap->tnote < SIZE(tsnds)) { + if (IndexOk(trap->tnote, tsnds)) { Soundeffect(tsnds[trap->tnote], 50); } pline("A %s beneath you %ss %s%s.", board, Deaf ? "vibrate" : squeak, Deaf ? "" : trapnote(trap, FALSE), Deaf ? "" : " loudly"); - wake_nearby(); + wake_nearby(FALSE); if (Is_wizpuzzle_lev(&u.uz)) { /* squeaky boards are the activation mechanism for the puzzle */ wizpuzzle_activate_mechanism(u.ux, u.uy); @@ -1468,19 +1350,20 @@ trapeffect_sqky_board( } else { boolean in_sight = canseemon(mtmp) || (mtmp == u.usteed); - if (!grounded(mtmp->data)) + if (m_in_air(mtmp)) return Trap_Effect_Finished; /* stepped on a squeaky board */ if (in_sight) { if (!Deaf) { - if (trap->tnote >= 0 && trap->tnote < SIZE(tsnds)) { + if (IndexOk(trap->tnote, tsnds)) { Soundeffect(tsnds[trap->tnote], 50); } - pline("A %s beneath %s %ss %s loudly.", board, - mon_nam(mtmp), squeak, trapnote(trap, FALSE)); + pline_mon(mtmp, "A %s beneath %s %ss %s loudly.", + board, mon_nam(mtmp), squeak, trapnote(trap, FALSE)); seetrap(trap); } else if (!mindless(mtmp->data)) { - pline("%s stops momentarily and appears to cringe.", + pline_mon(mtmp, + "%s stops momentarily and appears to cringe.", Monnam(mtmp)); } } else { @@ -1488,7 +1371,7 @@ trapeffect_sqky_board( int range = couldsee(mtmp->mx, mtmp->my) /* 9 or 5 */ ? (BOLT_LIM + 1) : (BOLT_LIM - 3); - if (trap->tnote >= 0 && trap->tnote < SIZE(tsnds)) { + if (IndexOk(trap->tnote, tsnds)) { Soundeffect(tsnds[trap->tnote], ((mdistu(mtmp) <= range * range) ? 40 : 20)); @@ -1500,7 +1383,7 @@ trapeffect_sqky_board( /* wake up nearby monsters */ wake_nearto(mtmp->mx, mtmp->my, 40); /* wake the hero if nearby */ - if (u.usleep && u.usleep < gm.moves + if (u.usleep && u.usleep < svm.moves && dist2(mtmp->mx, mtmp->my, u.ux, u.uy) < 40 /* sleeping causes deafness so don't check regular Deaf here, only * stricter forms of it */ @@ -1512,16 +1395,18 @@ trapeffect_sqky_board( return Trap_Effect_Finished; } -static int +staticfn int trapeffect_bear_trap( struct monst *mtmp, struct trap *trap, unsigned trflags) { - boolean forcetrap = ((trflags & FORCETRAP) != 0 - || (trflags & FAILEDUNTRAP) != 0); + boolean is_you = mtmp == &gy.youmonst, + forcetrap = ((trflags & FORCETRAP) != 0 + || (trflags & FAILEDUNTRAP) != 0 + || (is_you && (trflags & VIASITTING) != 0)); - if (mtmp == &gy.youmonst) { + if (is_you) { int dmg = d(2, 4); if ((Levitation || Flying) && !forcetrap) @@ -1558,11 +1443,12 @@ trapeffect_bear_trap( boolean in_sight = canseemon(mtmp) || (mtmp == u.usteed); boolean trapkilled = FALSE; - if (mptr->msize > MZ_SMALL && !amorphous(mptr) && !grounded(mptr) + if (mptr->msize > MZ_SMALL && !amorphous(mptr) && !m_in_air(mtmp) && !is_whirly(mptr) && !unsolid(mptr)) { mtmp->mtrapped = 1; if (in_sight) { - pline("%s is caught in %s bear trap!", Monnam(mtmp), + pline_mon(mtmp, + "%s is caught in %s bear trap!", Monnam(mtmp), a_your[trap->madeby_u]); seetrap(trap); } else { @@ -1574,7 +1460,8 @@ trapeffect_bear_trap( } } else if (forcetrap) { if (in_sight) { - pline("%s evades %s bear trap!", Monnam(mtmp), + pline_mon(mtmp, + "%s evades %s bear trap!", Monnam(mtmp), a_your[trap->madeby_u]); seetrap(trap); } @@ -1588,7 +1475,7 @@ trapeffect_bear_trap( return Trap_Effect_Finished; } -static int +staticfn int trapeffect_slp_gas_trap( struct monst *mtmp, struct trap *trap, @@ -1602,6 +1489,7 @@ trapeffect_slp_gas_trap( } else { pline("A cloud of gas puts you to sleep!"); fall_asleep(-rnd(25), TRUE); + monstunseesu(M_SEEN_SLEEP); } (void) steedintrap(trap, (struct obj *) 0); } else { @@ -1610,7 +1498,8 @@ trapeffect_slp_gas_trap( if (!resists_sleep(mtmp) && !breathless(mtmp->data) && !helpless(mtmp)) { if (sleep_monst(mtmp, rnd(25), -1) && in_sight) { - pline("%s suddenly falls asleep!", Monnam(mtmp)); + pline_mon(mtmp, + "%s suddenly falls asleep!", Monnam(mtmp)); seetrap(trap); } } @@ -1618,13 +1507,13 @@ trapeffect_slp_gas_trap( return Trap_Effect_Finished; } -static int +staticfn int trapeffect_rust_trap( struct monst *mtmp, struct trap *trap, unsigned int trflags UNUSED) { - struct obj *otmp; + struct obj *otmp, *nextobj; if (mtmp == &gy.youmonst) { seetrap(trap); @@ -1656,10 +1545,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) @@ -1688,14 +1579,16 @@ trapeffect_rust_trap( switch (rn2(5)) { case 0: if (in_sight) - pline("%s %s on the %s!", A_gush_of_water_hits, + pline_mon(mtmp, + "%s %s on the %s!", A_gush_of_water_hits, mon_nam(mtmp), mbodypart(mtmp, HEAD)); target = which_armor(mtmp, W_ARMH); (void) water_damage(target, helm_simple_name(target), TRUE); break; case 1: if (in_sight) - pline("%s %s's left %s!", A_gush_of_water_hits, + pline_mon(mtmp, + "%s %s's left %s!", A_gush_of_water_hits, mon_nam(mtmp), mbodypart(mtmp, ARM)); target = which_armor(mtmp, W_ARMS); if (water_damage(target, "shield", TRUE) != ER_NOTHING) @@ -1709,7 +1602,8 @@ trapeffect_rust_trap( break; case 2: if (in_sight) - pline("%s %s's right %s!", A_gush_of_water_hits, + pline_mon(mtmp, + "%s %s's right %s!", A_gush_of_water_hits, mon_nam(mtmp), mbodypart(mtmp, ARM)); (void) water_damage(MON_WEP(mtmp), 0, TRUE); goto mglovecheck; @@ -1733,7 +1627,7 @@ trapeffect_rust_trap( if (completelyrusts(mptr)) { if (in_sight) - pline("%s %s to pieces!", Monnam(mtmp), + pline_mon(mtmp, "%s %s to pieces!", Monnam(mtmp), !mlifesaver(mtmp) ? "falls" : "starts to fall"); monkilled(mtmp, (const char *) 0, AD_RUST); if (DEADMONSTER(mtmp)) @@ -1748,7 +1642,7 @@ trapeffect_rust_trap( return Trap_Effect_Finished; } -static int +staticfn int trapeffect_fire_trap( struct monst *mtmp, struct trap *trap, @@ -1766,12 +1660,14 @@ trapeffect_fire_trap( int orig_dmg = d(2, 4); if (in_sight) - pline("A %s erupts from the %s under %s!", tower_of_flame, + pline_mon(mtmp, + "A %s erupts from the %s under %s!", tower_of_flame, surface(mtmp->mx, mtmp->my), mon_nam(mtmp)); - else if (see_it) /* evidently `mtmp' is invisible */ + else if (see_it) { /* evidently `mtmp' is invisible */ + set_msg_xy(mtmp->mx, mtmp->my); You_see("a %s erupt from the %s!", tower_of_flame, surface(mtmp->mx, mtmp->my)); - + } if (resists_fire(mtmp)) { if (in_sight) { shieldeff(mtmp->mx, mtmp->my); @@ -1808,10 +1704,11 @@ trapeffect_fire_trap( if (thitm(0, mtmp, (struct obj *) 0, num, immolate)) trapkilled = TRUE; - else - /* we know mhp is at least `num' below mhpmax, - so no (mhp > mhpmax) check is needed here */ + else { mtmp->mhpmax -= rn2(num + 1); + if (mtmp->mhp > mtmp->mhpmax) + mtmp->mhp = mtmp->mhpmax; + } } if (burnarmor(mtmp) || rn2(3)) { int xtradmg = destroy_items(mtmp, AD_FIRE, orig_dmg); @@ -1895,15 +1792,18 @@ trapeffect_cold_trap( int uhpmin = minuhpmax(1), olduhpmax = u.uhpmax; if (u.uhpmax > uhpmin) { - u.uhpmax -= rn2(min(u.uhpmax, dmg + 1)), gc.context.botl = TRUE; + u.uhpmax -= rn2(min(u.uhpmax, dmg + 1)); + disp.botl = TRUE; } /* note: no 'else' here */ if (u.uhpmax < uhpmin) { - setuhpmax(min(olduhpmax, uhpmin)); /* sets gc.context.botl */ + setuhpmax(min(olduhpmax, uhpmin), FALSE); /* sets disp.botl */ if (!Drain_resistance) losexp(NULL); /* never fatal when 'drainer' is Null */ } - if (u.uhp > u.uhpmax) - u.uhp = u.uhpmax, gc.context.botl = TRUE; + if (u.uhp > u.uhpmax) { + u.uhp = u.uhpmax; + disp.botl = TRUE; + } } if (!dmg) { if (!lost_resistance) @@ -1961,7 +1861,7 @@ trapeffect_cold_trap( return Trap_Effect_Finished; } -static int +staticfn int trapeffect_pit( struct monst *mtmp, struct trap *trap, @@ -1971,12 +1871,12 @@ trapeffect_pit( if (mtmp == &gy.youmonst) { boolean plunged = (trflags & TOOKPLUNGE) != 0; + boolean viasitting = (trflags & VIASITTING) != 0; boolean conj_pit = conjoined_pits(trap, t_at(u.ux0, u.uy0), TRUE); boolean adj_pit = adj_nonconjoined_pit(trap); boolean already_known = trap->tseen ? TRUE : FALSE; boolean deliberate = FALSE; int steed_article = ARTICLE_THE; - int oldumort; /* suppress article in various steed messages when using its name (which won't occur when hallucinating) */ @@ -1984,7 +1884,7 @@ trapeffect_pit( steed_article = ARTICLE_NONE; /* KMH -- You can't escape the Sokoban level traps */ - if (!Sokoban && (Levitation || (Flying && !plunged))) + if (!Sokoban && (Levitation || (Flying && !plunged && !viasitting))) return Trap_Effect_Finished; feeltrap(trap); if (!Sokoban && !grounded(gy.youmonst.data) && !plunged) { @@ -2068,7 +1968,7 @@ trapeffect_pit( int dmg = rnd(conj_pit ? 4 : adj_pit ? 6 : 10); if (mon_hates_material(mtmp, IRON)) dmg += rnd(sear_damage(IRON)); - oldumort = u.umortality; + int oldumort = u.umortality; losehp(Maybe_Half_Phys(dmg), /* note: these don't need locomotion() handling; if fatal while poly'd and Unchanging, the @@ -2111,13 +2011,11 @@ trapeffect_pit( exercise(A_DEX, FALSE); } } else { - int tt = trap->ttyp, dmg; + int dmg; boolean in_sight = canseemon(mtmp) || (mtmp == u.usteed); boolean trapkilled = FALSE; boolean forcetrap = ((trflags & FORCETRAP) != 0); - boolean inescapable = (forcetrap - || ((tt == HOLE || tt == PIT) - && Sokoban && !trap->madeby_u)); + boolean inescapable = (forcetrap || (Sokoban && !trap->madeby_u)); struct permonst *mptr = mtmp->data; const char *fallverb; @@ -2127,27 +2025,29 @@ trapeffect_pit( /* openfallingtrap; not inescapable here */ if (in_sight) { seetrap(trap); - pline("%s doesn't fall into the pit.", Monnam(mtmp)); + pline_mon(mtmp, + "%s doesn't fall into the pit.", Monnam(mtmp)); } return Trap_Effect_Finished; } if (!inescapable) - return Trap_Effect_Finished; /* avoids trap */ + return Trap_Effect_Finished; /* avoids trap */ fallverb = "is dragged"; /* sokoban pit */ } if (!passes_walls(mptr)) mtmp->mtrapped = 1; if (in_sight) { - pline("%s %s into %s pit!", Monnam(mtmp), fallverb, - a_your[trap->madeby_u]); + pline_mon(mtmp, + "%s %s into %s pit!", Monnam(mtmp), fallverb, + a_your[trap->madeby_u]); if (mptr == &mons[PM_PIT_VIPER] || mptr == &mons[PM_PIT_FIEND]) pline("How pitiful. Isn't that the pits?"); seetrap(trap); } mselftouch(mtmp, "Falling, ", FALSE); - dmg = rnd((tt == PIT) ? 6 : 10); - if (tt == SPIKED_PIT && mon_hates_material(mtmp, IRON)) + dmg = rnd((ttype == PIT) ? 6 : 10); + if (ttype == SPIKED_PIT && mon_hates_material(mtmp, IRON)) dmg += rnd(sear_damage(IRON)); if (DEADMONSTER(mtmp) || thitm(0, mtmp, (struct obj *) 0, dmg, FALSE)) @@ -2159,10 +2059,10 @@ trapeffect_pit( return Trap_Effect_Finished; } -static int +staticfn int trapeffect_hole( - struct monst* mtmp, - struct trap* trap, + struct monst *mtmp, + struct trap *trap, unsigned int trflags) { if (mtmp == &gy.youmonst) { @@ -2178,9 +2078,7 @@ trapeffect_hole( struct permonst *mptr = mtmp->data; boolean in_sight = canseemon(mtmp) || (mtmp == u.usteed); boolean forcetrap = ((trflags & FORCETRAP) != 0); - boolean inescapable = (forcetrap - || ((tt == HOLE || tt == PIT) - && Sokoban && !trap->madeby_u)); + boolean inescapable = (forcetrap || (Sokoban && !trap->madeby_u)); if (!Can_fall_thru(&u.uz)) { impossible("mintrap: %ss cannot exist on this level.", @@ -2194,20 +2092,20 @@ trapeffect_hole( if (in_sight) { seetrap(trap); if (tt == TRAPDOOR) - pline( + pline_mon(mtmp, "A trap door opens, but %s doesn't fall through.", mon_nam(mtmp)); else /* (tt == HOLE) */ - pline("%s doesn't fall through the hole.", - Monnam(mtmp)); + pline_mon(mtmp, + "%s doesn't fall through the hole.", + Monnam(mtmp)); } return Trap_Effect_Finished; /* inescapable = FALSE; */ } if (inescapable) { /* sokoban hole */ if (in_sight) { - pline("%s seems to be yanked down!", Monnam(mtmp)); - /* suppress message in mlevel_tele_trap() */ - in_sight = FALSE; + pline_mon(mtmp, + "%s seems to be yanked down!", Monnam(mtmp)); seetrap(trap); } } else @@ -2218,10 +2116,10 @@ trapeffect_hole( return Trap_Effect_Finished; } -static int +staticfn int trapeffect_telep_trap( - struct monst* mtmp, - struct trap* trap, + struct monst *mtmp, + struct trap *trap, unsigned int trflags UNUSED) { if (mtmp == &gy.youmonst) { @@ -2236,32 +2134,28 @@ trapeffect_telep_trap( return Trap_Effect_Finished; } -static int +staticfn int trapeffect_level_telep( - struct monst* mtmp, - struct trap* trap, + struct monst *mtmp, + struct trap *trap, unsigned int trflags) { if (mtmp == &gy.youmonst) { seetrap(trap); level_tele_trap(trap, trflags); } else { - int tt = trap->ttyp; boolean in_sight = canseemon(mtmp) || (mtmp == u.usteed); boolean forcetrap = ((trflags & FORCETRAP) != 0); - boolean inescapable = (forcetrap - || ((tt == HOLE || tt == PIT) - && Sokoban && !trap->madeby_u)); - return mlevel_tele_trap(mtmp, trap, inescapable, in_sight); + return mlevel_tele_trap(mtmp, trap, forcetrap, in_sight); } return Trap_Effect_Finished; } -static int +staticfn int trapeffect_web( - struct monst* mtmp, - struct trap* trap, + struct monst *mtmp, + struct trap *trap, unsigned int trflags) { if (mtmp == &gy.youmonst) { @@ -2288,7 +2182,7 @@ trapeffect_web( if (webmsgok) { char verbbuf[BUFSZ]; - if (forcetrap || viasitting || gc.context.nopick) { + if (forcetrap || viasitting || svc.context.nopick) { Strcpy(verbbuf, "are caught by"); } else if (u.usteed) { Sprintf(verbbuf, "lead %s into", @@ -2378,6 +2272,7 @@ trapeffect_web( mtmp->mtrapped = 1; break; } + FALLTHROUGH; /*FALLTHRU*/ default: if (mptr->mlet == S_GIANT @@ -2386,7 +2281,8 @@ trapeffect_web( || (mtmp->wormno && count_wsegs(mtmp) > 5)) { tear_web = TRUE; } else if (in_sight) { - pline("%s is caught in %s spider web.", Monnam(mtmp), + pline_mon(mtmp, + "%s is caught in %s spider web.", Monnam(mtmp), a_your[trap->madeby_u]); seetrap(trap); } @@ -2411,13 +2307,15 @@ trapeffect_web( } if (tear_web) { if (in_sight) - pline("%s tears through %s spider web!", Monnam(mtmp), + pline_mon(mtmp, + "%s tears through %s spider web!", Monnam(mtmp), a_your[trap->madeby_u]); deltrap(trap); newsym(mtmp->mx, mtmp->my); } else if (forcetrap && !mtmp->mtrapped) { if (in_sight) { - pline("%s avoids %s spider web!", Monnam(mtmp), + pline_mon(mtmp, + "%s avoids %s spider web!", Monnam(mtmp), a_your[trap->madeby_u]); seetrap(trap); } @@ -2427,10 +2325,10 @@ trapeffect_web( return Trap_Effect_Finished; } -static int +staticfn int trapeffect_statue_trap( - struct monst* mtmp, - struct trap* trap, + struct monst *mtmp, + struct trap *trap, unsigned int trflags UNUSED) { if (mtmp == &gy.youmonst) { @@ -2441,10 +2339,10 @@ trapeffect_statue_trap( return Trap_Effect_Finished; } -static int +staticfn int trapeffect_magic_trap( - struct monst* mtmp, - struct trap* trap, + struct monst *mtmp, + struct trap *trap, unsigned int trflags) { if (mtmp == &gy.youmonst) { @@ -2471,7 +2369,7 @@ trapeffect_magic_trap( return Trap_Effect_Finished; } -static int +staticfn int trapeffect_anti_magic( struct monst *mtmp, /* monster, possibly youmonst */ struct trap *trap, /* trap->ttyp == ANTI_MAGIC */ @@ -2523,7 +2421,8 @@ trapeffect_anti_magic( mtmp->mspec_used += d(2, 6); if (in_sight) { seetrap(trap); - pline("%s seems lethargic.", Monnam(mtmp)); + pline_mon(mtmp, "%s seems lethargic.", + Monnam(mtmp)); } } } else { /* take some damage */ @@ -2562,10 +2461,10 @@ trapeffect_anti_magic( return Trap_Effect_Finished; } -static int +staticfn int trapeffect_poly_trap( - struct monst* mtmp, - struct trap* trap, + struct monst *mtmp, + struct trap *trap, unsigned int trflags) { if (mtmp == &gy.youmonst) { @@ -2610,7 +2509,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)) { if (newcham(mtmp, (struct permonst *) 0, NC_SHOW_MSG)) { deltrap(trap); @@ -2623,10 +2522,10 @@ trapeffect_poly_trap( return Trap_Effect_Finished; } -static int +staticfn int trapeffect_landmine( - struct monst* mtmp, - struct trap* trap, + struct monst *mtmp, + struct trap *trap, unsigned int trflags) { if (mtmp == &gy.youmonst) { @@ -2675,7 +2574,7 @@ trapeffect_landmine( exercise(A_DEX, FALSE); } /* add a pit before calling losehp so bones won't keep the landmine; - * blow_up_landmine will remove the pit afterwards if inappropriate */ + blow_up_landmine() will remove pit afterwards if inappropriate */ trap->ttyp = PIT; trap->madeby_u = FALSE; losehp(Maybe_Half_Phys(rnd(16)), "land mine", KILLED_BY_AN); @@ -2690,19 +2589,19 @@ trapeffect_landmine( } else { boolean trapkilled = FALSE; boolean in_sight = canseemon(mtmp) || (mtmp == u.usteed); - struct permonst *mptr = mtmp->data; coordxy tx = trap->tx, ty = trap->ty; /* heavier monsters are more likely to set off a land mine; on the - other hand, any mon lighter than the trigger weight is immune. */ -#define MINE_TRIGGER_WT (WT_ELF / 2) - if (rn2(mtmp->data->cwt + 1) < MINE_TRIGGER_WT) + other hand, any mon lighter than the trigger weight is immune */ +#define MINE_TRIGGER_WT (WT_ELF / 2U) + if (rn2(mtmp->data->cwt + 1) < (int) MINE_TRIGGER_WT) return Trap_Effect_Finished; - if (!grounded(mptr)) { + if (m_in_air(mtmp)) { boolean already_seen = trap->tseen; if (in_sight && !already_seen) { - pline("A trigger appears in a pile of soil below %s.", + pline_mon(mtmp, + "A trigger appears in a pile of soil below %s.", mon_nam(mtmp)); seetrap(trap); } @@ -2715,7 +2614,8 @@ trapeffect_landmine( } } else if (in_sight) { newsym(mtmp->mx, mtmp->my); - pline("%s%s triggers %s land mine!", + pline_mon(mtmp, + "%s%s triggers %s land mine!", !Deaf ? "KAABLAMM!!! " : "", Monnam(mtmp), a_your[trap->madeby_u]); } @@ -2748,10 +2648,10 @@ trapeffect_landmine( } #undef MINE_TRIGGER_WT -static int +staticfn int trapeffect_rolling_boulder_trap( - struct monst* mtmp, - struct trap* trap, + struct monst *mtmp, + struct trap *trap, unsigned int trflags UNUSED) { if (mtmp == &gy.youmonst) { @@ -2769,9 +2669,7 @@ trapeffect_rolling_boulder_trap( pline("Fortunately for you, no boulder was released."); } } else { - struct permonst *mptr = mtmp->data; - - if (grounded(mptr)) { + if (!m_in_air(mtmp)) { boolean in_sight = (mtmp == u.usteed || (cansee(mtmp->mx, mtmp->my) && canspotmon(mtmp))); @@ -2780,7 +2678,7 @@ trapeffect_rolling_boulder_trap( newsym(mtmp->mx, mtmp->my); if (in_sight) - pline("%s%s triggers %s.", + pline_mon(mtmp, "%s%s triggers %s.", !Deaf ? "Click! " : "", Monnam(mtmp), trap->tseen ? "a rolling boulder trap" : something); if (launch_obj(BOULDER, trap->launch.x, trap->launch.y, @@ -2800,10 +2698,10 @@ trapeffect_rolling_boulder_trap( return Trap_Effect_Finished; } -static int +staticfn int trapeffect_magic_portal( - struct monst* mtmp, - struct trap* trap, + struct monst *mtmp, + struct trap *trap, unsigned int trflags) { if (mtmp == &gy.youmonst) { @@ -2815,16 +2713,16 @@ trapeffect_magic_portal( return Trap_Effect_Finished; } -static int +staticfn int trapeffect_vibrating_square( - struct monst* mtmp, - struct trap* trap, + struct monst *mtmp, + struct trap *trap, unsigned int trflags UNUSED) { if (mtmp == &gy.youmonst) { feeltrap(trap); /* messages handled elsewhere; the trap symbol is merely to mark the - * square for future reference */ + square for future reference */ } else { boolean in_sight = canseemon(mtmp) || (mtmp == u.usteed); boolean see_it = cansee(mtmp->mx, mtmp->my); @@ -2834,8 +2732,7 @@ trapeffect_vibrating_square( if (in_sight) { char buf[BUFSZ], *p, *monnm = mon_nam(mtmp); - if (nolimbs(mtmp->data) - || is_floater(mtmp->data) || is_flyer(mtmp->data)) { + if (nolimbs(mtmp->data) || m_in_air(mtmp)) { /* just "beneath " */ Strcpy(buf, monnm); } else { @@ -2858,10 +2755,189 @@ trapeffect_vibrating_square( return Trap_Effect_Finished; } -static int +/* + * for PR#259 - paranoid_confirm:trap + * + * Will a monster suffer any adverse effects from a certain trap? + * Note: does NOT mean "will a monster trigger a trap in the first place", + * though if it won't that does imply that they'll not suffer adverse effects. + * For example, an elf is considered immune to sleeping gas traps even though + * they'll set the trap off. + * Return value: + * TRAP_NOT_IMMUNE = not immune at the moment; + * TRAP_CLEARLY_IMMUNE = obviously immune (if player is polymorphed, assume + * they know which traps they are immune to in their current form); + * TRAP_HIDDEN_IMMUNE = immune but in non-obvious way such as an unidentified + * item or hidden intrinsic providing a resistance; the player should still + * be warned of this trap, while monsters implicitly know they're immune. + */ +int +immune_to_trap(struct monst *mon, unsigned ttype) +{ + struct permonst *pm; + struct obj *obj; + boolean is_you; + + if (!mon) { + impossible("immune_to_trap: null monster"); + return TRAP_NOT_IMMUNE; + } + pm = mon->data; + is_you = (mon == &gy.youmonst); + + switch (ttype) { + case ARROW_TRAP: + case DART_TRAP: + case ROCKTRAP: + /* can hit anything; even noncorporeal monsters might get a blessed + projectile */ + return TRAP_NOT_IMMUNE; + case BEAR_TRAP: + if (pm->msize <= MZ_SMALL + || amorphous(pm) || is_whirly(pm) || unsolid(pm)) + return TRAP_CLEARLY_IMMUNE; + FALLTHROUGH; + /*FALLTHRU*/ + case SQKY_BOARD: + case LANDMINE: + case ROLLING_BOULDER_TRAP: + case HOLE: + case TRAPDOOR: + case PIT: + case SPIKED_PIT: + /* ground-based traps, which can be evaded by levitation, flying, or + hanging to the ceiling */ + if (Sokoban && (is_pit(ttype) || is_hole(ttype))) + return TRAP_NOT_IMMUNE; + if (!grounded(pm)) + return TRAP_CLEARLY_IMMUNE; + else if (is_you && (Levitation || Flying)) + return TRAP_CLEARLY_IMMUNE; + return TRAP_NOT_IMMUNE; + case SLP_GAS_TRAP: + if (breathless(pm)) + return TRAP_CLEARLY_IMMUNE; + else if (!is_you && resists_sleep(mon)) + return TRAP_CLEARLY_IMMUNE; + else if (is_you && Sleep_resistance) + return TRAP_HIDDEN_IMMUNE; + return TRAP_NOT_IMMUNE; + case LEVEL_TELEP: + case TELEP_TRAP: + /* consider unintended teleporting to be an adverse effect; if in + the endgame or carrying the Amulet, the teleport trap won't work + anyway, so anything hitting it is immune. */ + if (In_endgame(&u.uz) || mon_has_amulet(mon)) + return TRAP_CLEARLY_IMMUNE; + return TRAP_NOT_IMMUNE; + case POLY_TRAP: + if (resists_magm(mon)) + /* covers Antimagic for player */ + return (is_you ? TRAP_HIDDEN_IMMUNE : TRAP_CLEARLY_IMMUNE); + return TRAP_NOT_IMMUNE; + case STATUE_TRAP: + /* no effect on monsters, only affects players; only trap detection + can let player know that this is a statue trap there ahead of time; + in the rare case this happens, do consider it an adverse effect */ + if (!is_you) + return TRAP_CLEARLY_IMMUNE; + return TRAP_NOT_IMMUNE; + case WEB: + /* most of this code is lifted from mu_maybe_destroy_web */ + if (webmaker(pm) || amorphous(pm) || is_whirly(pm) || flaming(pm) + || unsolid(pm) || pm == &mons[PM_GELATINOUS_CUBE]) + return TRAP_CLEARLY_IMMUNE; + return TRAP_NOT_IMMUNE; + case ANTI_MAGIC: + /* doesn't hurt any non-magic-resistant monster with no magic */ + if (is_you) { + if (Antimagic) + return TRAP_NOT_IMMUNE; + else if (u.uenmax == 0) + /* player won't lose HP and can't lose more Pw */ + return TRAP_HIDDEN_IMMUNE; + + /* following conditional lifted from mintrap ANTI_MAGIC logic */ + } else if (!resists_magm(mon) + && (mon->mcan || (!attacktype(pm, AT_MAGC) + && !attacktype(pm, AT_BREA)))) { + return TRAP_CLEARLY_IMMUNE; + } + return TRAP_NOT_IMMUNE; + case RUST_TRAP: + /* harmful if wearing anything rustable or if mon is an iron golem */ + if (pm == &mons[PM_IRON_GOLEM]) + return TRAP_NOT_IMMUNE; + + for (obj = is_you ? gi.invent : mon->minvent; obj; obj = obj->nobj) { + /* rust traps can currently hit only worn armor and weapons */ + if (is_rustprone(obj) && obj->owornmask) { + if (is_you && (obj == uquiver + || (obj == uswapwep && !u.twoweap))) + continue; + return TRAP_NOT_IMMUNE; + } + } + return TRAP_CLEARLY_IMMUNE; + case MAGIC_TRAP: + /* for player, any number of bad effects; + 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 */ + if (is_you ? !Fire_resistance : !resists_fire(mon)) + return TRAP_NOT_IMMUNE; + + for (obj = is_you ? gi.invent : mon->minvent; obj; obj = obj->nobj) { + if (obj->oclass == SCROLL_CLASS || obj->oclass == POTION_CLASS + || obj->oclass == SPBOOK_CLASS + || (obj->owornmask && is_flammable(obj))) { + if ((obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL) + /* mon knows scroll of fire or spellbook of fireball + won't be affected; hero knows iff this one has been + seen and its type has been discovered */ + && (!is_you + || (obj->dknown && objects[obj->otyp].oc_name_known))) + continue; + return TRAP_NOT_IMMUNE; + } + } + return (is_you ? TRAP_HIDDEN_IMMUNE : TRAP_CLEARLY_IMMUNE); + case COLD_TRAP: + /* always potentially harmful to player */ + if (is_you) + return TRAP_NOT_IMMUNE; + /* but normally not harmful to monsters since they can't lose cold + * resistance; however, they can lose it if it's acquired, so check for + * that */ + else { + if (mon->mintrinsics & MR_COLD) + return TRAP_NOT_IMMUNE; + return resists_cold(mon) ? TRAP_CLEARLY_IMMUNE : TRAP_NOT_IMMUNE; + } + case MAGIC_PORTAL: + /* never hurts anything, but player is considered non-immune so they + can be asked about entering it */ + if (!is_you) + return TRAP_CLEARLY_IMMUNE; + return TRAP_NOT_IMMUNE; + case VIBRATING_SQUARE: + /* no adverse effects */ + return TRAP_CLEARLY_IMMUNE; + default: + impossible("immune_to_trap: bad ttype %u", ttype); + break; + } + return TRAP_NOT_IMMUNE; +} + +staticfn int trapeffect_selector( - struct monst* mtmp, - struct trap* trap, + struct monst *mtmp, + struct trap *trap, unsigned int trflags) { switch (trap->ttyp) { @@ -2935,10 +3011,15 @@ dotrap(struct trap *trap, unsigned trflags) /* Correct conj_pit and adj_pit if the player isn't moving; this function * can also be called by a pit fiend hurling you into a pit on its turn, * which has nothing to do with moving between pits */ - if (!gc.context.mon_moving) { + if (!svc.context.mon_moving) { conj_pit = adj_pit = FALSE; } + if (fixed_tele_trap(trap)) { + trflags |= FORCETRAP; + forcetrap = TRUE; + } + /* KMH -- You can't escape the Sokoban level traps */ if (Sokoban && (is_pit(ttype) || is_hole(ttype))) { /* The "air currents" message is still appropriate -- even when @@ -3012,7 +3093,7 @@ trapnote(struct trap* trap, boolean noprefix) /* choose a note not used by any trap on current level, ignoring ttmp; if all are in use, pick a random one */ -static int +staticfn int choose_trapnote(struct trap *ttmp) { int tavail[12], tpick[12], tcnt = 0, k; @@ -3031,7 +3112,7 @@ choose_trapnote(struct trap *ttmp) return ((tcnt > 0) ? tpick[rn2(tcnt)] : rn2(12)); } -static int +staticfn int steedintrap(struct trap *trap, struct obj *otmp) { struct monst *steed = u.usteed; @@ -3077,22 +3158,10 @@ steedintrap(struct trap *trap, struct obj *otmp) break; case POLY_TRAP: if (!resists_magm(steed) && !resist(steed, WAND_CLASS, 0, NOTELL)) { - struct permonst *mdat = steed->data; - deltrap(trap); newsym(steed->mx, steed->my); /* get rid of trap symbol */ + /* newcham() will probably end up calling poly_steed() */ (void) newcham(steed, (struct permonst *) 0, NC_SHOW_MSG); - if (!can_saddle(steed) || !can_ride(steed)) { - dismount_steed(DISMOUNT_POLY); - } else { - char buf[BUFSZ]; - - Strcpy(buf, x_monnam(steed, ARTICLE_YOUR, (char *) 0, - SUPPRESS_SADDLE, FALSE)); - if (mdat != steed->data) - (void) strsubst(buf, "your ", "your new "); - You("adjust yourself in the saddle on %s.", buf); - } } steedhit = TRUE; break; @@ -3109,7 +3178,7 @@ steedintrap(struct trap *trap, struct obj *otmp) /* some actions common to both player and monsters for triggered landmine */ void -blow_up_landmine(struct trap* trap) +blow_up_landmine(struct trap *trap) { coordxy x = trap->tx, y = trap->ty, dbx, dby; struct rm *lev = &levl[x][y]; @@ -3130,8 +3199,9 @@ blow_up_landmine(struct trap* trap) /* if under the portcullis, the bridge is adjacent */ if (find_drawbridge(&dbx, &dby)) destroy_drawbridge(dbx, dby); - trap = t_at(x, y); /* expected to be null after destruction */ } + trap = t_at(x, y); /* expected to be null after destruction */ + /* or could be null if scatter blew up oil which melted ice */ /* convert landmine into pit */ if (trap) { if (Is_waterlevel(&u.uz) || Is_airlevel(&u.uz)) { @@ -3152,11 +3222,12 @@ blow_up_landmine(struct trap* trap) } } } + fill_pit(x, y); spot_checks(x, y, old_typ); } -static void -launch_drop_spot(struct obj* obj, coordxy x, coordxy y) +staticfn void +launch_drop_spot(struct obj *obj, coordxy x, coordxy y) { if (!obj) { gl.launchplace.obj = (struct obj *) 0; @@ -3203,12 +3274,10 @@ launch_obj( struct monst *mtmp; struct obj *otmp, *otmp2; int dx, dy; + coordxy x, y; struct obj *singleobj; - boolean used_up = FALSE; - boolean otherside = FALSE; - int dist; - int tmp; - int delaycnt = 0; + boolean used_up = FALSE, otherside = FALSE; + int dist, tmp, delaycnt = 0; otmp = sobj_at(otyp, x1, y1); /* Try the other side too, for rolling boulder traps */ @@ -3243,14 +3312,14 @@ launch_obj( launched (perhaps a monster triggered it), destroy context so that next dig attempt never thinks you're resuming previous effort */ if ((otyp == BOULDER || otyp == STATUE) - && singleobj->ox == gc.context.digging.pos.x - && singleobj->oy == gc.context.digging.pos.y) - (void) memset((genericptr_t) &gc.context.digging, 0, + && singleobj->ox == svc.context.digging.pos.x + && singleobj->oy == svc.context.digging.pos.y) + (void) memset((genericptr_t) &svc.context.digging, 0, sizeof(struct dig_info)); dist = distmin(x1, y1, x2, y2); - gb.bhitpos.x = x1; - gb.bhitpos.y = y1; + x = gb.bhitpos.x = x1; + y = gb.bhitpos.y = y1; dx = sgn(x2 - x1); dy = sgn(y2 - y1); switch (style) { @@ -3273,18 +3342,20 @@ 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) delaycnt = 1; - if (!cansee(gb.bhitpos.x, gb.bhitpos.y)) + if (!cansee(x, y)) curs_on_u(); tmp_at(DISP_FLASH, obj_to_glyph(singleobj, rn2_on_display_rng)); - tmp_at(gb.bhitpos.x, gb.bhitpos.y); + tmp_at(x, y); } /* Mark a spot to place object in bones files to prevent * loss of object. Use the starting spot to ensure that @@ -3294,28 +3365,29 @@ launch_obj( * that would prevent it from ever getting there (bars), and we * can't tell that yet. */ - launch_drop_spot(singleobj, gb.bhitpos.x, gb.bhitpos.y); + launch_drop_spot(singleobj, x, y); /* Set the object in motion */ while (dist-- > 0 && !used_up) { struct trap *t; - tmp_at(gb.bhitpos.x, gb.bhitpos.y); + tmp_at(x, y); tmp = delaycnt; /* dstage@u.washington.edu -- Delay only if hero sees it */ - if (cansee(gb.bhitpos.x, gb.bhitpos.y)) + if (cansee(x, y)) while (tmp-- > 0) nh_delay_output(); - gb.bhitpos.x += dx; - gb.bhitpos.y += dy; + x = (gb.bhitpos.x += dx); + y = (gb.bhitpos.y += dy); - if ((mtmp = m_at(gb.bhitpos.x, gb.bhitpos.y)) != 0) { + if ((mtmp = m_at(x, y)) != 0) { if (otyp == BOULDER && throws_rocks(mtmp->data)) { if (rn2(3)) { - if (cansee(gb.bhitpos.x, gb.bhitpos.y)) - pline("%s snatches the boulder.", Monnam(mtmp)); + if (cansee(x, y)) + pline_mon(mtmp, "%s snatches the boulder.", + Monnam(mtmp)); singleobj->otrapped = 0; (void) mpickobj(mtmp, singleobj); used_up = TRUE; @@ -3329,7 +3401,7 @@ launch_obj( launch_drop_spot((struct obj *) 0, 0, 0); break; } - } else if (u_at(gb.bhitpos.x, gb.bhitpos.y)) { + } else if (u_at(x, y)) { if (gm.multi) nomul(0); if (thitu(9 + singleobj->spe, dmgval(singleobj, &gy.youmonst), @@ -3338,37 +3410,40 @@ launch_obj( } if (style == ROLL) { if (otyp == BOULDER) { - wake_nearto(gb.bhitpos.x, gb.bhitpos.y, 25); + wake_nearto(x, y, 25); } - if (down_gate(gb.bhitpos.x, gb.bhitpos.y) != -1) { - if (ship_object(singleobj, gb.bhitpos.x, gb.bhitpos.y, FALSE)) { + if (down_gate(x, y) != -1) { + if (ship_object(singleobj, x, y, FALSE)) { used_up = TRUE; launch_drop_spot((struct obj *) 0, 0, 0); break; } } - if ((t = t_at(gb.bhitpos.x, gb.bhitpos.y)) != 0 && otyp == BOULDER) { + if ((t = t_at(x, y)) != 0 + && otyp == BOULDER) { int newlev = 0; d_level dest; switch (t->ttyp) { case LANDMINE: if (rn2(10) > 2) { + if (cansee(x, y)) + set_msg_xy(x, y); pline("KAABLAMM!!!%s", - cansee(gb.bhitpos.x, gb.bhitpos.y) + cansee(x, y) ? " The rolling boulder triggers a land mine." : ""); deltrap_with_ammo(t, DELTRAP_DESTROY_AMMO); - del_engr_at(gb.bhitpos.x, gb.bhitpos.y); - place_object(singleobj, gb.bhitpos.x, gb.bhitpos.y); + del_engr_at(x, y); + place_object(singleobj, x, y); singleobj->otrapped = 0; fracture_rock(singleobj); - (void) scatter(gb.bhitpos.x, gb.bhitpos.y, 4, + (void) scatter(x, y, 4, MAY_DESTROY | MAY_HIT | MAY_FRACTURE | VIS_EFFECTS, (struct obj *) 0); - if (cansee(gb.bhitpos.x, gb.bhitpos.y)) - newsym(gb.bhitpos.x, gb.bhitpos.y); + if (cansee(x, y)) + newsym(x, y); used_up = TRUE; launch_drop_spot((struct obj *) 0, 0, 0); } @@ -3380,10 +3455,12 @@ launch_obj( /* if trap doesn't work, skip "disappears" message */ if (newlev == depth(&u.uz)) break; + FALLTHROUGH; /*FALLTHRU*/ case TELEP_TRAP: - if (cansee(gb.bhitpos.x, gb.bhitpos.y)) - pline("Suddenly the rolling boulder disappears!"); + if (cansee(x, y)) + pline_xy(x, y, + "Suddenly the rolling boulder disappears!"); else if (!Deaf) You_hear("a rumbling stop abruptly."); singleobj->otrapped = 0; @@ -3406,7 +3483,7 @@ launch_obj( case TRAPDOOR: /* the boulder won't be used up if there is a monster in the trap; stop rolling anyway */ - x2 = gb.bhitpos.x, y2 = gb.bhitpos.y; /* stops here */ + x2 = x, y2 = y; /* stops here */ if (flooreffects(singleobj, x2, y2, "fall")) { used_up = TRUE; launch_drop_spot((struct obj *) 0, 0, 0); @@ -3420,39 +3497,39 @@ launch_obj( if (used_up || dist == -1) break; /* from 'while' loop */ } - if (flooreffects(singleobj, gb.bhitpos.x, gb.bhitpos.y, "fall")) { + if (flooreffects(singleobj, x, y, "fall")) { used_up = TRUE; launch_drop_spot((struct obj *) 0, 0, 0); break; } - if (otyp == BOULDER - && (otmp2 = sobj_at(BOULDER, gb.bhitpos.x, gb.bhitpos.y)) != 0) { + if (otyp == BOULDER && (otmp2 = sobj_at(BOULDER, x, y)) != 0) { const char *bmsg = " as one boulder sets another in motion"; + coordxy fx = x + dx, fy = y + dy; - if (!isok(gb.bhitpos.x + dx, gb.bhitpos.y + dy) || !dist - || IS_ROCK(levl[gb.bhitpos.x + dx][gb.bhitpos.y + dy].typ)) + if (!isok(fx, fy) || !dist || IS_OBSTRUCTED(levl[fx][fy].typ)) bmsg = " as one boulder hits another"; Soundeffect(se_loud_crash, 80); - You_hear("a loud crash%s!", - cansee(gb.bhitpos.x, gb.bhitpos.y) ? bmsg : ""); + You_hear("a loud crash%s!", cansee(x, y) ? bmsg : ""); obj_extract_self(otmp2); /* pass off the otrapped flag to the next boulder */ otmp2->otrapped = singleobj->otrapped; singleobj->otrapped = 0; - place_object(singleobj, gb.bhitpos.x, gb.bhitpos.y); + place_object(singleobj, x, y); singleobj = otmp2; otmp2 = (struct obj *) 0; - wake_nearto(gb.bhitpos.x, gb.bhitpos.y, 10 * 10); + wake_nearto(x, y, 10 * 10); } } - if (otyp == BOULDER && closed_door(gb.bhitpos.x, gb.bhitpos.y)) { - if (!door_is_iron(&levl[gb.bhitpos.x][gb.bhitpos.y])) { - if (cansee(gb.bhitpos.x, gb.bhitpos.y)) + if (otyp == BOULDER && closed_door(x, y)) { + if (!door_is_iron(&levl[x][y])) { + if (cansee(x, y)) { + set_msg_xy(x, y); pline_The("boulder crashes through a door."); - set_doorstate(&levl[gb.bhitpos.x][gb.bhitpos.y], D_BROKEN); + } + set_doorstate(&levl[x][y], D_BROKEN); if (dist) - unblock_point(gb.bhitpos.x, gb.bhitpos.y); + unblock_point(x, y); } else { /* boulder hits iron door, stop the boulder */ @@ -3460,19 +3537,29 @@ launch_obj( } } - /* if about to hit iron bars, do so now */ - if (dist > 0 && isok(gb.bhitpos.x + dx, gb.bhitpos.y + dy) - && levl[gb.bhitpos.x + dx][gb.bhitpos.y + dy].typ == IRONBARS) { - x2 = gb.bhitpos.x, y2 = gb.bhitpos.y; /* object stops here */ - if (hits_bars(&singleobj, x2, y2, x2 + dx, y2 + dy, !rn2(20), 0)) { - if (!singleobj) { - used_up = TRUE; - launch_drop_spot((struct obj *) 0, 0, 0); + /* if about to hit something, do so now */ + if (dist > 0 && isok(x + dx, y + dy)) { + coordxy fx = x + dx, fy = y + dy; + uchar typ = levl[fx][fy].typ; + + if (typ == IRONBARS) { + x2 = x, y2 = y; /* object stops here */ + if (hits_bars(&singleobj, x2, y2, fx, fy, !rn2(20), 0)) { + if (!singleobj) { + used_up = TRUE; + launch_drop_spot((struct obj *) 0, 0, 0); + } + break; } + } else if (IS_STWALL(typ) || IS_TREE(typ)) { + x2 = x, y2 = y; /* object stops here */ + if (!Deaf) + pline("Thump!"); + wake_nearto(x2, y2, 16); break; } } - } + } /* while dist > 0 */ tmp_at(DISP_END, 0); launch_drop_spot((struct obj *) 0, 0, 0); if (!used_up) { @@ -3480,12 +3567,12 @@ launch_obj( place_object(singleobj, x2, y2); newsym(x2, y2); return 1; - } else - return 2; + } + return 2; } void -seetrap(struct trap* trap) +seetrap(struct trap *trap) { if (!trap->tseen) { trap->tseen = 1; @@ -3495,7 +3582,7 @@ seetrap(struct trap* trap) /* like seetrap() but overrides vision */ void -feeltrap(struct trap* trap) +feeltrap(struct trap *trap) { trap->tseen = 1; map_trap(trap, 1); @@ -3505,10 +3592,10 @@ feeltrap(struct trap* trap) /* try to find a random coordinate where launching a rolling boulder could work. return TRUE if found, with coordinate in cc. */ -static boolean +staticfn boolean find_random_launch_coord(struct trap *ttmp, coord *cc) { - register int tmp; + int tmp; boolean success = FALSE; coord bcc = UNDEFINED_VALUES; int distance; @@ -3534,7 +3621,7 @@ find_random_launch_coord(struct trap *ttmp, coord *cc) if (ttmp->ttyp == ROLLING_BOULDER_TRAP) mindist = 2; distance = rn1(5, 4); /* 4..8 away */ - tmp = rn2(N_DIRS); /* randomly pick a direction to try first */ + tmp = rn2(N_DIRS); /* randomly pick a direction to try first */ while (distance >= mindist) { dx = xdir[tmp]; dy = ydir[tmp]; @@ -3565,7 +3652,7 @@ find_random_launch_coord(struct trap *ttmp, coord *cc) return success; } -static int +staticfn int mkroll_launch( struct trap *ttmp, coordxy x, @@ -3618,7 +3705,7 @@ mkroll_launch( return 1; } -static boolean +staticfn boolean isclearpath( coord *cc, int distance, @@ -3634,8 +3721,10 @@ isclearpath( while (distance-- > 0) { x += dx; y += dy; + if (!isok(x, y)) + return FALSE; typ = levl[x][y].typ; - if (!isok(x, y) || !ZAP_POS(typ) || closed_door(x, y)) + if (!ZAP_POS(typ) || closed_door(x, y)) return FALSE; if ((t = t_at(x, y)) != 0 && (is_pit(t->ttyp) || is_hole(t->ttyp) || is_xport(t->ttyp))) @@ -3646,6 +3735,14 @@ isclearpath( return TRUE; } +/* can monster escape from a pit easily */ +staticfn boolean +m_easy_escape_pit(struct monst *mtmp) +{ + return (mtmp->data == &mons[PM_PIT_FIEND] + || mtmp->data->msize >= MZ_HUGE); +} + int mintrap(struct monst *mtmp, unsigned mintrapflags) { @@ -3661,25 +3758,26 @@ mintrap(struct monst *mtmp, unsigned mintrapflags) || trap->ttyp == HOLE || trap->ttyp == WEB)) { /* If you come upon an obviously trapped monster, then - * you must be able to see the trap it's in too. - */ + you must be able to see the trap it's in too. */ seetrap(trap); } - if (!rn2(40) || (is_pit(trap->ttyp) && mtmp->data->msize >= MZ_HUGE)) { + if (!rn2(40) || (is_pit(trap->ttyp) && m_easy_escape_pit(mtmp))) { if (sobj_at(BOULDER, mtmp->mx, mtmp->my) && is_pit(trap->ttyp)) { if (!rn2(2)) { mtmp->mtrapped = 0; if (canseemon(mtmp)) - pline("%s pulls free...", Monnam(mtmp)); + pline_mon(mtmp, "%s pulls free...", + Monnam(mtmp)); fill_pit(mtmp->mx, mtmp->my); } } else { if (canseemon(mtmp)) { + set_msg_xy(mtmp->mx, mtmp->my); if (is_pit(trap->ttyp)) pline("%s climbs %sout of the pit.", Monnam(mtmp), - mtmp->data->msize >= MZ_HUGE ? "easily " : ""); + m_easy_escape_pit(mtmp) ? "easily " : ""); else if (trap->ttyp == BEAR_TRAP || trap->ttyp == WEB) pline("%s pulls free of the %s.", Monnam(mtmp), trapname(trap->ttyp, FALSE)); @@ -3689,29 +3787,37 @@ mintrap(struct monst *mtmp, unsigned mintrapflags) } else if (metallivorous(mptr)) { if (trap->ttyp == BEAR_TRAP) { if (canseemon(mtmp)) - pline("%s eats a bear trap!", Monnam(mtmp)); + pline_mon(mtmp, "%s eats a bear trap!", + Monnam(mtmp)); deltrap_with_ammo(trap, DELTRAP_DESTROY_AMMO); mtmp->meating = 5; mtmp->mtrapped = 0; } else if (trap->ttyp == SPIKED_PIT) { if (canseemon(mtmp)) - pline("%s munches on some spikes!", Monnam(mtmp)); + pline_mon(mtmp, "%s munches on some spikes!", + Monnam(mtmp)); trap->ttyp = PIT; mtmp->meating = 5; } } trap_result = mtmp->mtrapped ? Trap_Caught_Mon : Trap_Effect_Finished; } else { - register int tt = trap->ttyp; + int tt = trap->ttyp; boolean forcetrap = ((mintrapflags & FORCETRAP) != 0); boolean forcebungle = (mintrapflags & FORCEBUNGLE) != 0; /* monster has seen such a trap before */ boolean already_seen = (mon_knows_traps(mtmp, tt) || (tt == HOLE && !mindless(mptr))); + if (fixed_tele_trap(trap)) { + mintrapflags |= FORCETRAP; + forcetrap = TRUE; + } + if (mtmp == u.usteed) { ; /* true when called from dotrap, inescapable is not an option */ - } else if (Sokoban && (is_pit(tt) || is_hole(tt)) && !trap->madeby_u) { + } else if (Sokoban && (is_pit(tt) || is_hole(tt)) + && !trap->madeby_u) { ; /* nothing here, the trap effects will handle messaging */ } else if (!forcetrap) { if (floor_trigger(tt) && check_in_air(mtmp, mintrapflags)) { @@ -3741,7 +3847,7 @@ mintrap(struct monst *mtmp, unsigned mintrapflags) maybe_unhide_at(mtmp->mx, mtmp->my); if (!alreadyspotted && canseemon(mtmp)) - pline("%s appears.", Amonnam(mtmp)); + pline_mon(mtmp, "%s appears.", Amonnam(mtmp)); } } return trap_result; @@ -3764,9 +3870,9 @@ instapetrify(const char *str) && polymon(PM_STONE_GOLEM, POLYMON_ALL_MSGS)) return; urgent_pline("You turn to stone..."); - gk.killer.format = KILLED_BY; - if (str != gk.killer.name) - Strcpy(gk.killer.name, str ? str : ""); + svk.killer.format = KILLED_BY; + if (str != svk.killer.name) + Strcpy(svk.killer.name, str ? str : ""); done(STONING); } @@ -3787,7 +3893,7 @@ minstapetrify(struct monst *mon, boolean byplayer) mon_adjust_speed(mon, -3, (struct obj *) 0); if (cansee(mon->mx, mon->my)) - pline("%s turns to stone.", Monnam(mon)); + pline_mon(mon, "%s turns to stone.", Monnam(mon)); if (byplayer) { gs.stoned = TRUE; xkilled(mon, XKILL_NOMSG); @@ -3836,7 +3942,7 @@ mselftouch( if (mwep && mwep->otyp == CORPSE && touch_petrifies(&mons[mwep->corpsenm]) && !resists_ston(mon)) { if (cansee(mon->mx, mon->my)) { - pline("%s%s touches %s.", arg ? arg : "", + pline_mon(mon, "%s%s touches %s.", arg ? arg : "", arg ? mon_nam(mon) : Monnam(mon), corpse_xname(mwep, (const char *) 0, CXN_PFX_THE)); } @@ -3852,7 +3958,7 @@ mselftouch( void float_up(void) { - gc.context.botl = TRUE; + disp.botl = TRUE; if (u.utrap) { if (u.utraptype == TT_PIT) { reset_utrap(FALSE); @@ -3903,7 +4009,8 @@ float_up(void) } else { You("start to float in the air!"); } - if (u.usteed && !is_floater(u.usteed->data) && !is_flyer(u.usteed->data)) { + if (u.usteed && !is_floater(u.usteed->data) + && !is_flyer(u.usteed->data)) { if (Lev_at_will) { pline("%s magically floats up!", Monnam(u.usteed)); } else { @@ -3920,13 +4027,14 @@ float_up(void) return; } +/* a boulder fills a pit or a hole at x,y */ void fill_pit(coordxy x, coordxy y) { struct obj *otmp; struct trap *t; - if ((t = t_at(x, y)) != 0 && is_pit(t->ttyp) + if ((t = t_at(x, y)) != 0 && (is_pit(t->ttyp) || is_hole(t->ttyp)) && (otmp = sobj_at(BOULDER, x, y)) != 0) { obj_extract_self(otmp); (void) flooreffects(otmp, x, y, "settle"); @@ -3939,7 +4047,7 @@ float_down( long hmask, long emask) /* might cancel timeout */ { - register struct trap *trap = (struct trap *) 0; + struct trap *trap = (struct trap *) 0; d_level current_dungeon_level; boolean no_msg = FALSE; @@ -3965,7 +4073,7 @@ float_down( (void) encumber_msg(); /* carrying capacity might have changed */ return 0; } - gc.context.botl = TRUE; + disp.botl = TRUE; nomul(0); /* stop running or resting */ if (BFlying) { /* controlled flight no longer overridden by levitation */ @@ -4082,6 +4190,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 */ @@ -4124,7 +4233,7 @@ climb_pit(void) reset_utrap(FALSE); fill_pit(u.ux, u.uy); gv.vision_full_recalc = 1; /* vision limits change */ - } else if (!(--u.utrap) || gy.youmonst.data->msize >= MZ_HUGE) { + } else if (!(--u.utrap) || m_easy_escape_pit(&gy.youmonst)) { reset_utrap(FALSE); You("%s to the edge of the %s.", (Sokoban && Levitation) @@ -4133,13 +4242,13 @@ climb_pit(void) pitname); fill_pit(u.ux, u.uy); gv.vision_full_recalc = 1; /* vision limits change */ - } else if (u.dz || Verbose(3, climb_pit)) { + } else if (u.dz || flags.verbose) { /* these should use 'pitname' rather than "pit" for hallucination but that would nullify Norep (this message can be repeated many times without further user intervention by using a run attempt to keep retrying to escape from the pit) */ if (u.usteed) - Norep("%s is still in a pit.", upstart(y_monnam(u.usteed))); + Norep("%s is still in a pit.", YMonnam(u.usteed)); else Norep((Hallucination && !rn2(5)) ? "You've fallen, and you can't get up." @@ -4147,7 +4256,7 @@ climb_pit(void) } } -static void +staticfn void dofiretrap( struct obj *box) /* null for floor trap */ { @@ -4195,22 +4304,24 @@ dofiretrap( if (alt > num) num = alt; if (u.mhmax > mons[u.umonnum].mlevel) - u.mhmax -= rn2(min(u.mhmax, num + 1)), gc.context.botl = TRUE; + u.mhmax -= rn2(min(u.mhmax, num + 1)), disp.botl = TRUE; if (u.mh > u.mhmax) - u.mh = u.mhmax, gc.context.botl = TRUE; + u.mh = u.mhmax, disp.botl = TRUE; + monstunseesu(M_SEEN_FIRE); } else { int uhpmin = minuhpmax(1), olduhpmax = u.uhpmax; if (u.uhpmax > uhpmin) { - u.uhpmax -= rn2(min(u.uhpmax, num + 1)), gc.context.botl = TRUE; + u.uhpmax -= rn2(min(u.uhpmax, num + 1)), disp.botl = TRUE; } /* note: no 'else' here */ if (u.uhpmax < uhpmin) { - setuhpmax(min(olduhpmax, uhpmin)); /* sets gc.context.botl */ + setuhpmax(min(olduhpmax, uhpmin), FALSE); /* sets disp.botl */ if (!Drain_resistance) losexp(NULL); /* never fatal when 'drainer' is Null */ } if (u.uhp > u.uhpmax) - u.uhp = u.uhpmax, gc.context.botl = TRUE; + u.uhp = u.uhpmax, disp.botl = TRUE; + monstunseesu(M_SEEN_FIRE); } if (!num) You("are uninjured."); @@ -4228,10 +4339,10 @@ dofiretrap( melt_ice(u.ux, u.uy, (char *) 0); } -static void +staticfn void domagictrap(void) { - register int fate = rnd(20); + int fate = rnd(20); /* What happened to the poor sucker? */ @@ -4254,12 +4365,12 @@ domagictrap(void) Soundeffect(se_deafening_roar_atmospheric, 100); You_hear("a deafening roar!"); incr_itimeout(&HDeaf, rn1(20, 30)); - gc.context.botl = TRUE; + disp.botl = TRUE; } else { /* magic vibrations still hit you */ You_feel("rankled."); incr_itimeout(&HDeaf, rn1(5, 15)); - gc.context.botl = TRUE; + disp.botl = TRUE; } while (cnt--) (void) makemon((struct permonst *) 0, u.ux, u.uy, NO_MM_FLAGS); @@ -4343,7 +4454,7 @@ domagictrap(void) continue; mtmp = m_at(u.ux + i, u.uy + j); if (mtmp) - (void) tamedog(mtmp, (struct obj *) 0, TRUE); + (void) tamedog(mtmp, (struct obj *) 0, TRUE, TRUE); } break; } @@ -4351,9 +4462,11 @@ domagictrap(void) struct obj pseudo; long save_conf = HConfusion; - pseudo = cg.zeroobj; /* neither cursed nor blessed, - and zero out oextra */ - pseudo.otyp = SCR_REMOVE_CURSE; + pseudo = cg.zeroobj; /* force 'uncursed' and zero out oextra */ + /* used to be SCR_REMOVE_CURSE but that could cause seffects() + to have hero discover scroll of remove curse */ + pseudo.otyp = SPE_REMOVE_CURSE; + pseudo.oclass = SPBOOK_CLASS; HConfusion = 0L; (void) seffects(&pseudo); HConfusion = save_conf; @@ -4365,20 +4478,12 @@ domagictrap(void) } } -/* Set an item on fire. - * "force" means not to roll a luck-based protection check for the - * item. - * "x" and "y" are the coordinates to dump the contents of a - * container, if it burns up. - * - * Return whether the object was destroyed. - */ +/* Set an item on fire. Return whether the object was destroyed. */ boolean fire_damage( struct obj *obj, - boolean force, - coordxy x, - coordxy y) + boolean force, /* if True, skip luck-based protection check */ + coordxy x, coordxy y) /* where to place contents of burned up container */ { int chance; struct obj *otmp, *ncobj; @@ -4389,11 +4494,12 @@ fire_damage( if (catch_lit(obj)) return FALSE; - if (Is_container(obj)) { + if (Is_container(obj) || obj->otyp == STATUE) { if (!is_flammable(obj)) { return FALSE; /* immune */ } switch (obj->otyp) { + case STATUE: case ICE_BOX: return FALSE; /* Immune */ case CHEST: @@ -4407,8 +4513,12 @@ fire_damage( break; } if ((!force && (Luck + 5) > rn2(chance)) - || (is_flammable(obj) && obj->oerodeproof)) + /* note: containers aren't subject to erosion so are never + marked fireproof/corrodeproof/&c */ + /*|| (is_flammable(obj) && obj->oerodeproof)*/ + ) { return FALSE; + } /* Container is burnt up - dump contents out */ if (in_sight) pline("%s catches fire and burns.", Yname2(obj)); @@ -4475,8 +4585,7 @@ fire_damage_chain( struct obj *chain, boolean force, boolean here, - coordxy x, - coordxy y) + coordxy x, coordxy y) { struct obj *obj, *nobj; int num = 0; @@ -4498,7 +4607,7 @@ fire_damage_chain( /* obj has been thrown or dropped into lava; damage is worse than mere fire */ boolean -lava_damage(struct obj* obj, coordxy x, coordxy y) +lava_damage(struct obj *obj, coordxy x, coordxy y) { int otyp = obj->otyp, ocls = obj->oclass; @@ -4544,7 +4653,7 @@ lava_damage(struct obj* obj, coordxy x, coordxy y) } void -acid_damage(struct obj* obj) +acid_damage(struct obj *obj) { /* Scrolls but not spellbooks can be erased by acid. */ struct monst *victim; @@ -4556,7 +4665,7 @@ acid_damage(struct obj* obj) victim = carried(obj) ? &gy.youmonst : mcarried(obj) ? obj->ocarry : NULL; vismon = victim && (victim != &gy.youmonst) && canseemon(victim); - if (victim && adtyp_resistance_obj(victim, AD_ACID)) + if (victim && inventory_resistance_check(victim, AD_ACID)) return; if (obj->greased) { @@ -4582,18 +4691,68 @@ acid_damage(struct obj* obj) erode_obj(obj, (char *) 0, ERODE_CORRODE, EF_GREASE | EF_VERBOSE); } +staticfn void +pot_acid_damage( + struct obj *obj, + boolean in_invent, + boolean described) +{ + char *bufp; + boolean one, exploded; + + one = (obj->quan == 1L); + exploded = FALSE; + + if (Blind && !in_invent) + obj->dknown = 0; + if (ga.acid_ctx.ctx_valid) + exploded = ((obj->dknown ? ga.acid_ctx.dkn_boom + : ga.acid_ctx.unk_boom) > 0); + if (described) { + /* just gave "The grease washes off your potion of acid." + or "...your potion." (or just "...your potion."); + don't re-describe potion here; if we used "It explodes!" + then "it" might be misconstrued as applying to "grease" */ + pline_The("potion%s %s!", + plur(obj->quan), otense(obj, "explode")); + } else { + /* First message is + * "a [potion| potion|potion of acid] explodes" + * depending on obj->dknown (potion has been seen) and + * objects[POT_ACID].oc_name_known (fully discovered), + * or "some {plural version} explode" when relevant. + * Second and subsequent messages for same chain and + * matching dknown status are + * "another [potion| &c] explodes" or plural + * variant. + */ + bufp = simpleonames(obj); + pline("%s%s %s!", /* "A potion explodes!" */ + !exploded ? (one ? "A " : "Some ") + : (one ? "Another " : "More "), + bufp, vtense(bufp, "explode")); + } + if (ga.acid_ctx.ctx_valid) { + if (obj->dknown) + ga.acid_ctx.dkn_boom++; + else + ga.acid_ctx.unk_boom++; + } + if (obj->dknown) + makeknown(POT_ACID); + setnotworn(obj); + delobj(obj); + if (in_invent) + update_inventory(); +} + /* Get an object wet and damage it appropriately. - * "obj": if null, returns ER_NOTHING - * "ostr", if present, is used instead of the object name in some - * messages. - * "force" means not to roll luck to protect some objects. - * Returns an erosion return value (ER_*) - */ + Returns an erosion return value (ER_*). */ int water_damage( - struct obj *obj, - const char *ostr, - boolean force) + struct obj *obj, /* might be Null; return ER_NOTHING if so */ + const char *ostr, /* if non-Null, use instead of cxname() in messages */ + boolean force) /* if True, skip luck-based protection check */ { boolean in_invent = obj && carried(obj), described = FALSE; @@ -4623,17 +4782,20 @@ water_damage( update_inventory(); } /* ungreased potions of acid will always be destroyed by water */ - if (obj->otyp == POT_ACID) - goto pot_acid; + if (obj->otyp == POT_ACID) { + pot_acid_damage(obj, in_invent, described); + return ER_DESTROYED; + } } return ER_GREASED; } else if (obj->oerodeproof) { return ER_NOTHING; } else if (Is_container(obj) && (!Waterproof_container(obj) || (obj->cursed && !rn2(3)))) { - if (in_invent) + if (in_invent) { pline("Some %s gets into your %s!", hliquid("water"), ostr); - + gm.mentioned_water = !Hallucination; + } /* assume that if we're getting water into a container, further water * damage should also get inside nested containers */ water_damage_chain(obj->cobj, FALSE, 0, TRUE); @@ -4641,7 +4803,9 @@ water_damage( } else if (Waterproof_container(obj)) { if (in_invent) { pline_The("%s slides right off your %s.", hliquid("water"), ostr); - makeknown(obj->otyp); + gm.mentioned_water = !Hallucination; + makeknown(obj->otyp); /* if an oilskin sack, discover it; doesn't + * matter for chest, large box, ice box */ } /* not actually damaged, but because we /didn't/ get the "water gets into!" message, the player now has more information and @@ -4671,26 +4835,23 @@ water_damage( update_inventory(); return ER_DAMAGED; } else if (obj->oclass == SPBOOK_CLASS) { - if (obj->otyp == SPE_BOOK_OF_THE_DEAD) { - /* - * FIXME? - * This might be given when hero can't see or feel it. - * The book can't be inside a container but it could get - * dunked away from hero if laying on ice which melts. - */ - pline("Steam rises from %s.", the(xname(obj))); + int otyp = obj->otyp; + + if (otyp == SPE_BOOK_OF_THE_DEAD) { + coordxy ox = 0, oy = 0; + + /* note: The Book of the Dead can't be contained or buried */ + if (get_obj_location(obj, &ox, &oy, CONTAINED_TOO | BURIED_TOO)) + obj->ox = ox, obj->oy = oy; + if (isok(ox, oy) && cansee(ox, oy)) + pline("Steam rises from %s.", the(xname(obj))); return 0; - } else if (obj->otyp == SPE_BLANK_PAPER) { + } else if (otyp == SPE_BLANK_PAPER) { return 0; } if (in_invent) Your("%s %s.", ostr, vtense(ostr, "fade")); - if (obj->otyp == SPE_NOVEL) { - obj->novelidx = 0; - free_oname(obj); - } - obj->otyp = SPE_BLANK_PAPER; set_material(obj, PAPER); /* in case it was one of the LEATHER books */ /* same re-init as over-reading or polymorph; matters if it gets @@ -4699,59 +4860,15 @@ water_damage( if (obj->spestudied) obj->spestudied = rn2(obj->spestudied); obj->dknown = 0; + /* blanking a novel is more involved than blanking a spellbook */ + if (otyp == SPE_NOVEL) /* old type */ + blank_novel(obj); if (in_invent) update_inventory(); return ER_DAMAGED; } else if (obj->oclass == POTION_CLASS) { if (obj->otyp == POT_ACID) { - char *bufp; - boolean one, exploded; - - pot_acid: - one = (obj->quan == 1L); - exploded = FALSE; - - if (Blind && !in_invent) - obj->dknown = 0; - if (ga.acid_ctx.ctx_valid) - exploded = ((obj->dknown ? ga.acid_ctx.dkn_boom - : ga.acid_ctx.unk_boom) > 0); - if (described) { - /* just gave "The grease washes off your potion of acid." - or "...your potion." (or just "...your potion."); - don't re-describe potion here; if we used "It explodes!" - then "it" might be misconstrued as applying to "grease" */ - pline_The("potion%s %s!", - plur(obj->quan), otense(obj, "explode")); - } else { - /* First message is - * "a [potion| potion|potion of acid] explodes" - * depending on obj->dknown (potion has been seen) and - * objects[POT_ACID].oc_name_known (fully discovered), - * or "some {plural version} explode" when relevant. - * Second and subsequent messages for same chain and - * matching dknown status are - * "another [potion| &c] explodes" or plural - * variant. - */ - bufp = simpleonames(obj); - pline("%s%s %s!", /* "A potion explodes!" */ - !exploded ? (one ? "A " : "Some ") - : (one ? "Another " : "More "), - bufp, vtense(bufp, "explode")); - } - if (ga.acid_ctx.ctx_valid) { - if (obj->dknown) - ga.acid_ctx.dkn_boom++; - else - ga.acid_ctx.unk_boom++; - } - if (obj->dknown) - makeknown(POT_ACID); - setnotworn(obj); - delobj(obj); - if (in_invent) - update_inventory(); + pot_acid_damage(obj, in_invent, described); return ER_DESTROYED; } else if (obj->odiluted) { if (in_invent) @@ -4801,6 +4918,7 @@ water_damage_chain( int i = 0, j; struct obj** to_damage = NULL; coordxy x, y; + coord save_bhitpos; if (!obj) return; @@ -4827,7 +4945,10 @@ water_damage_chain( acid nor unseen have exploded during this water damage sequence */ ga.acid_ctx.dkn_boom = ga.acid_ctx.unk_boom = 0; ga.acid_ctx.ctx_valid = TRUE; - + /* we don't want to permanently overwrite bhitpos below, since we can get + here from scenarios where it was in use up the call stack (e.g. thrown + item hurtling the levitating hero into a wall of water) */ + save_bhitpos = gb.bhitpos; /* erode_obj() relies on bhitpos if target objects aren't carried by the hero or a monster, to check visibility controlling feedback */ if (get_obj_location(obj, &x, &y, CONTAINED_TOO)) @@ -4866,9 +4987,10 @@ water_damage_chain( free((genericptr_t) to_damage); } - /* reset acid context */ + /* reset acid context and bhitpos */ ga.acid_ctx.dkn_boom = ga.acid_ctx.unk_boom = 0; ga.acid_ctx.ctx_valid = FALSE; + gb.bhitpos = save_bhitpos; } /* @@ -4877,31 +4999,34 @@ water_damage_chain( * Returns TRUE if disrobing made player unencumbered enough to * crawl out of the current predicament. */ -static boolean +staticfn boolean emergency_disrobe(boolean *lostsome) { int invc = inv_cnt(TRUE); while (near_capacity() > (Punished ? UNENCUMBERED : SLT_ENCUMBER)) { - register struct obj *obj, *otmp = (struct obj *) 0; - register int i; + 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 * in removing them and other cursed stuff - * for obvious reasons. + * for obvious reasons. Also, any item in the midst + * of being taken off or stolen. */ if (!(undroppable(obj) || obj == uamul || obj == uleft || obj == uright || obj == ublindf || obj == uarm || obj == uarmc || obj == uarmg || obj == uarmf || obj == uarmu || (obj->cursed && (obj == uarmh || obj == uarms)) - || welded(obj))) + || welded(obj) + || obj->o_id == gs.stealoid || obj->in_use)) otmp = obj; /* reached the mark and found some stuff to drop? */ if (--i < 0 && otmp) @@ -4952,11 +5077,47 @@ rnd_nextto_goodpos(coordxy *x, coordxy *y, struct monst *mtmp) return FALSE; } +/* print a message about being back on the ground after leaving a pool */ +void +back_on_ground(boolean rescued) +{ + const char *preposit = (Levitation || Flying) ? "over" : "on", + *surf = surface(u.ux, u.uy), *you_are_back; + char icebuf[QBUFSZ]; + + if (is_ice(u.ux, u.uy)) { + /* "on ice" */ + surf = ice_descr(u.ux, u.uy, icebuf); + } else if (!strcmpi(surf, "floor") || !strcmpi(surf, "ground")) { + /* "on solid ground" */ + surf = "solid ground"; + } else if (!strcmpi(surf, "bridge") || !strcmpi(surf, "altar") + || !strcmpi(surf, "headstone")) { + /* "on a bridge" */ + surf = an(surf); + } else if (!strcmpi(surf, "stairs") || !strcmpi(surf, "lava") + || !strcmpi(surf, "bottom")) { + /* "on the stairs" */ + surf = the(surf); + } else { /* "cloud", "air", "air bubble", "wall", "fountain", "doorway" */ + /* "in a cloud", "in the air" */ + surf = !strcmp(surf, "air") ? the(surf) : an(surf); + preposit = "in"; + } + if (rescued) { + you_are_back = "You find yourself"; + } else { + you_are_back = flags.verbose ? "You are back" : "Back"; + } + pline("%s %s %s.", you_are_back, preposit, surf); + iflags.last_msg = PLNMSG_BACK_ON_GROUND; +} + /* life-saving or divine rescue has attempted to get the hero out of hostile terrain and put hero in an unexpected spot or failed due to overfull level and just prevented death so "back on solid ground" may be inappropriate */ void -back_on_ground(int how) +rescued_from_terrain(int how) { static const char find_yourself[] = "find yourself"; struct rm *lev = &levl[u.ux][u.uy]; @@ -4991,11 +5152,12 @@ back_on_ground(int how) break; } if (!mesggiven) - You("%s back on solid %s.", find_yourself, surface(u.ux, u.uy)); + back_on_ground(TRUE); iflags.last_msg = PLNMSG_BACK_ON_GROUND; /* for describe_decor() */ /* feedback just disclosed this */ - iflags.prev_decor = gl.lastseentyp[u.ux][u.uy] = lev->typ; + update_lastseentyp(u.ux, u.uy); + iflags.prev_decor = svl.lastseentyp[u.ux][u.uy]; } /* return TRUE iff player relocated */ @@ -5070,7 +5232,7 @@ drown(void) if (Amphibious || Breathless || Swimming) { if (Amphibious || Breathless) { - if (Verbose(3, drown)) + if (flags.verbose) pline("But you aren't drowning."); if (!Is_waterlevel(&u.uz)) { if (Hallucination) @@ -5141,11 +5303,14 @@ drown(void) /* killer format and name are reconstructed every iteration because lifesaving resets them */ pool_of_water = waterbody_name(u.ux, u.uy); - gk.killer.format = KILLED_BY_AN; + svk.killer.format = KILLED_BY_AN; /* avoid "drowned in [a] water" */ if (!strcmp(pool_of_water, "water")) - pool_of_water = "deep water", gk.killer.format = KILLED_BY; - Strcpy(gk.killer.name, pool_of_water); + pool_of_water = "deep water", svk.killer.format = KILLED_BY; + /* avoid "drowned in _a_ limitless water" on Plane of Water */ + else if (!strcmp(pool_of_water, "limitless water")) + svk.killer.format = KILLED_BY; + Strcpy(svk.killer.name, pool_of_water); done(DROWNING); /* oops, we're still alive. better get out of the water. */ if (safe_teleds(TELEDS_ALLOW_DRAG | TELEDS_TELEPORT)) @@ -5156,7 +5321,7 @@ drown(void) if (u.uinwater) set_uinwater(0); /* u.uinwater = 0 */ - back_on_ground(DROWNING); + rescued_from_terrain(DROWNING); return TRUE; } @@ -5168,14 +5333,14 @@ drain_en(int n, boolean max_already_drained) /* * FIXME? - * u.uenmax should probably have a higher mininum than 0; + * u.uenmax should probably have a higher minimum than 0; * perhaps u.ulevel or (u.ulevel + 1) / 2 */ if (u.uenmax < 1) { /* energy is completely gone */ if (u.uen || u.uenmax) { /* paranoia */ u.uen = u.uenmax = 0; - gc.context.botl = TRUE; + disp.botl = TRUE; } mesg = "momentarily lethargic"; } else { @@ -5198,7 +5363,7 @@ drain_en(int n, boolean max_already_drained) and then we throttled the loss being applied to current */ u.uen = u.uenmax; } - gc.context.botl = TRUE; + disp.botl = TRUE; } /* after manipulating u.uen,uenmax and setting context.botl, so that You_feel() -> pline() will update status before the message */ @@ -5245,16 +5410,34 @@ could_untrap(boolean verbosely, boolean check_floor) return 1; } -/* Probability of disabling a trap. Helge Hafting */ -static int +/* Probability of disabling a trap. Helge Hafting; + Returns 0 for success, non-0 for failure. */ +staticfn int untrap_prob( struct trap *ttmp) /* must not be Null */ { int chance = 3; /* non-spiders are less adept at dealing with webs */ - if (ttmp->ttyp == WEB && !webmaker(gy.youmonst.data)) - chance = 7; /* 3.7: used to be 30 */ + if (ttmp->ttyp == WEB) { + /* this assumes that all fiery artifacts are blades; no need to + make it more complicated unless/until that changes */ + struct obj *wep = (uwep && is_blade(uwep)) ? uwep + : (uswapwep && u.twoweap && is_blade(uswapwep)) + ? uswapwep : NULL; + + /* FIXME? Forcefight of adjacent web works with bare-handed and + martial arts but #untrap of same resorts to !webmaker() chance */ + if (wep && !m_at(ttmp->tx, ttmp->ty)) { + /* primary or secondary weapon is a blade (which includes + daggers but not axes or bladed polearms) */ + if (u_wield_art(ART_STING) || attacks(AD_FIRE, wep)) + chance = 1; + /* else chance stays 3 */ + } else if (!webmaker(gy.youmonst.data)) { + chance = 7; /* 3.7: used to be 30 */ + } + } if (Confusion || Hallucination) chance++; if (Blind) @@ -5275,11 +5458,31 @@ untrap_prob( chance--; } else if (Role_if(PM_RANGER) && chance > 1) chance--; + if (chance < 1) + chance = 1; return rn2(chance); } +/* 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 */ -static void +staticfn void move_into_trap(struct trap *ttmp) { int bc = 0; @@ -5287,9 +5490,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); @@ -5314,14 +5521,16 @@ 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"); } } -/* 0: doesn't even try - * 1: tries and fails - * 2: succeeds - */ -static int +/* 0: doesn't even try; 1: tries and fails; 2: succeeds */ +staticfn int try_disarm( struct trap *ttmp, boolean force_failure) @@ -5410,8 +5619,8 @@ try_disarm( return 2; } -static void -reward_untrap(struct trap* ttmp, struct monst* mtmp) +staticfn void +reward_untrap(struct trap *ttmp, struct monst *mtmp) { if (!ttmp->madeby_u) { if (rnl(10) < 8 && !mtmp->mpeaceful && !helpless(mtmp) @@ -5422,8 +5631,8 @@ reward_untrap(struct trap* ttmp, struct monst* mtmp) set_malign(mtmp); /* reset alignment */ pline("%s is grateful.", Monnam(mtmp)); } - /* Helping someone out of a trap is a nice thing to do, - * A lawful may be rewarded, but not too often. */ + /* Helping someone out of a trap is a nice thing to do. + A lawful may be rewarded, but not too often. */ if (!rn2(3) && !rnl(8) && u.ualign.type == A_LAWFUL) { adjalign(1); You_feel("that you did the right thing."); @@ -5431,10 +5640,13 @@ reward_untrap(struct trap* ttmp, struct monst* mtmp) } } -static int -disarm_holdingtrap(struct trap* ttmp) /* Helge Hafting */ +/* Help a monster out of a bear trap or web, or if no monster is + present, disarm a bear trap or destroy a web. Helge Hafting */ +staticfn int +disarm_holdingtrap(struct trap *ttmp) { struct monst *mtmp; + const char *which = the_your[ttmp->madeby_u]; int fails = try_disarm(ttmp, FALSE); if (fails < 2) @@ -5446,25 +5658,33 @@ disarm_holdingtrap(struct trap* ttmp) /* Helge Hafting */ There's no need for a cockatrice test, only the trap is touched */ if ((mtmp = m_at(ttmp->tx, ttmp->ty)) != 0) { mtmp->mtrapped = 0; - You("remove %s %s from %s.", the_your[ttmp->madeby_u], - (ttmp->ttyp == BEAR_TRAP) ? "bear trap" : "webbing", - mon_nam(mtmp)); + You("extract %s from %s %s.", mon_nam(mtmp), + which, (ttmp->ttyp == BEAR_TRAP) ? "bear trap" : "web"); reward_untrap(ttmp, mtmp); - } else { - if (ttmp->ttyp == BEAR_TRAP) { - You("disarm %s bear trap.", the_your[ttmp->madeby_u]); - deltrap_with_ammo(ttmp, DELTRAP_PLACE_AMMO); - } else /* if (ttmp->ttyp == WEB) */ { - You("succeed in removing %s web.", the_your[ttmp->madeby_u]); - deltrap(ttmp); - } + } else if (ttmp->ttyp == BEAR_TRAP) { + You("disarm %s bear trap.", which); + deltrap_with_ammo(ttmp, DELTRAP_PLACE_AMMO); + } else if (ttmp->ttyp == WEB) { + struct obj *wep = (uwep && is_blade(uwep)) ? uwep + : (uswapwep && u.twoweap && is_blade(uswapwep)) + ? uswapwep : NULL; + + if (wep && wep->oartifact + && (u_wield_art(ART_STING) || attacks(AD_FIRE, wep))) + pline("%s %s through %s web!", bare_artifactname(uwep), + u_wield_art(ART_STING) ? "cuts" : "burns", which); + else if (wep) + You("cut through %s web.", which); + else + You("succeed in removing %s web.", which); + deltrap(ttmp); } newsym(u.ux + u.dx, u.uy + u.dy); return 1; } -static int -disarm_landmine(struct trap* ttmp) /* Helge Hafting */ +staticfn int +disarm_landmine(struct trap *ttmp) /* Helge Hafting */ { int fails = try_disarm(ttmp, FALSE); @@ -5476,8 +5696,8 @@ disarm_landmine(struct trap* ttmp) /* Helge Hafting */ } /* getobj callback for object to disarm a squeaky board with */ -static int -unsqueak_ok(struct obj* obj) +staticfn int +unsqueak_ok(struct obj *obj) { if (!obj) return GETOBJ_EXCLUDE; @@ -5499,8 +5719,8 @@ unsqueak_ok(struct obj* obj) } /* it may not make much sense to use grease on floor boards, but so what? */ -static int -disarm_squeaky_board(struct trap* ttmp) +staticfn int +disarm_squeaky_board(struct trap *ttmp) { struct obj *obj; boolean bad_tool; @@ -5533,7 +5753,7 @@ disarm_squeaky_board(struct trap* ttmp) } /* removes traps that shoot arrows, darts, etc. */ -static int +staticfn int disarm_shooting_trap(struct trap* ttmp) { int fails = try_disarm(ttmp, FALSE); @@ -5546,7 +5766,7 @@ disarm_shooting_trap(struct trap* ttmp) } /* trying to #untrap a monster from a pit; is the weight too heavy? */ -static int +staticfn int try_lift( struct monst *mtmp, /* trapped monster */ struct trap *ttmp, /* pit, possibly made by hero, or spiked pit */ @@ -5569,7 +5789,7 @@ try_lift( } /* Help trapped monster (out of a (spiked) pit) */ -static int +staticfn int help_monster_out( struct monst *mtmp, struct trap *ttmp) @@ -5596,7 +5816,7 @@ help_monster_out( return 1; /* Will our hero succeed? */ - if ((uprob = untrap_prob(ttmp)) && !helpless(mtmp)) { + if ((uprob = untrap_prob(ttmp)) != 0 && !helpless(mtmp)) { You("try to reach out your %s, but %s backs away skeptically.", makeplural(body_part(ARM)), mon_nam(mtmp)); return 1; @@ -5662,8 +5882,35 @@ 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 + || Is_mummychest(box))) { + (void) chest_trap(box, FINGER, TRUE); + /* 'box' might be gone now */ + } else { + You("disarm it!"); + box->otrapped = 0; + box->tknown = 0; + 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 */ -static void +staticfn void untrap_box( struct obj *box, boolean force, @@ -5671,31 +5918,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 - || Is_mummychest(box))) { - (void) chest_trap(box, FINGER, TRUE); - /* 'box' might be gone now */ - } else { - You("disarm it!"); - box->otrapped = 0; - } - 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))); } @@ -5708,7 +5943,7 @@ untrap( coordxy rx, coordxy ry, struct obj *container) { - register struct obj *otmp; + struct obj *otmp; coordxy x, y; int ch; struct trap *ttmp; @@ -5753,7 +5988,7 @@ untrap( here = u_at(x, y); /* !u.dx && !u.dy */ if (here) /* are there are one or more containers here? */ - for (otmp = gl.level.objects[x][y]; otmp; otmp = otmp->nexthere) + for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp->nexthere) if (Is_box(otmp)) { if (++boxcnt > 1) break; @@ -5855,20 +6090,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 = gl.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."); } @@ -5924,6 +6167,8 @@ untrap( } else { You("disarm it!"); set_door_trap(&levl[x][y], FALSE); + more_experienced(8, 0); + newexplevel(); } } else pline("This door was not trapped."); @@ -5939,9 +6184,10 @@ untrap( boolean openholdingtrap( struct monst *mon, - boolean *noticed) /* set to true iff hero notices the effect; */ -{ /* otherwise left with its previous value intact */ - struct trap *t; + boolean *noticed) /* set to true iff hero notices the effect; + * otherwise left with its previous value intact */ +{ + struct trap *t, tdummy; char buf[BUFSZ], whichbuf[20]; const char *trapdescr = 0, *which = 0; boolean ishero = (mon == &gy.youmonst); @@ -5954,7 +6200,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"; @@ -5970,7 +6224,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 @@ -5982,13 +6238,13 @@ 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"; + assert(which != 0); if (*which) which = strcat(strcpy(whichbuf, which), " "); @@ -5996,13 +6252,19 @@ openholdingtrap( if (!u.utrap) return FALSE; *noticed = TRUE; - if (u.usteed) - Sprintf(buf, "%s is", noit_Monnam(u.usteed)); - else + if (!u.usteed) Strcpy(buf, "You are"); - reset_utrap(TRUE); - gv.vision_full_recalc = 1; /* vision limits can change (pit escape) */ + else if (u.utraptype == TT_BURIEDBALL) + Sprintf(buf, "You and %s are", y_monnam(u.usteed)); + else + Sprintf(buf, "%s is", noit_Monnam(u.usteed)); + /* give release message before untrap in case it triggers a message */ pline("%s released from %s%s.", buf, which, trapdescr); + /* might float up if Levitation is being unblocked */ + gv.vision_full_recalc = 1; /* vision limits can change (pit escape) */ + reset_utrap(TRUE); + if (gv.vision_full_recalc) + vision_recalc(0); } else { if (!mon->mtrapped) return FALSE; @@ -6020,7 +6282,7 @@ openholdingtrap( pline("%s%s opens.", upstart(strcpy(buf, which)), trapdescr); } /* might pacify monster if adjacent */ - if (rn2(2) && next2u(mon->mx, mon->my)) + if (rn2(2) && m_next2u(mon)) reward_untrap(t, mon); } return TRUE; @@ -6031,8 +6293,9 @@ openholdingtrap( boolean closeholdingtrap( struct monst *mon, - boolean *noticed) /* set to true iff hero notices the effect; */ -{ /* otherwise left with its previous value intact */ + boolean *noticed) /* set to true iff hero notices the effect; + * otherwise left with its previous value intact */ +{ struct trap *t; unsigned dotrapflags; boolean ishero = (mon == &gy.youmonst), result; @@ -6112,11 +6375,11 @@ openfallingtrap( /* only called when the player is doing something to the chest directly */ boolean chest_trap( - register struct obj *obj, - register int bodypart, + struct obj *obj, + int bodypart, boolean disarm) { - register struct obj *otmp = obj, *otmp2; + struct obj *otmp = obj, *otmp2; char buf[80]; const char *msg; coord cc; @@ -6124,6 +6387,7 @@ 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; otmp->otrapped = 0; /* trap is one-shot; clear flag first in case chest kills you and ends up in bones file */ You(disarm ? "set it off!" : "trigger a trap!"); @@ -6227,14 +6491,14 @@ chest_trap( unpunish(); /* destroy everything at the spot (the Amulet, the invocation tools, and Rider corpses will remain intact) */ - for (otmp = gl.level.objects[ox][oy]; otmp; otmp = otmp2) { + 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); delobj(otmp); } - wake_nearby(); + wake_nearby(FALSE); losehp(Maybe_Half_Phys(d(6, 6)), buf, KILLED_BY_AN); exercise(A_STR, FALSE); if (costly && loss) { @@ -6255,7 +6519,8 @@ chest_trap( case 17: pline("A cloud of noxious gas billows from %s.", the(xname(obj))); if (rn2(3)) - poisoned("gas cloud", A_STR, "cloud of poison gas", 15, FALSE); + poisoned("gas cloud", A_STR, "cloud of poison gas", 15, + FALSE); else create_gas_cloud(obj->ox, obj->oy, 1, 8); exercise(A_CON, FALSE); @@ -6285,6 +6550,8 @@ chest_trap( You("don't seem to be affected."); monstseesu(M_SEEN_ELEC); dmg = 0; + } else { + monstunseesu(M_SEEN_ELEC); } (void) destroy_items(&gy.youmonst, AD_ELEC, orig_dmg); if (dmg) @@ -6300,7 +6567,7 @@ chest_trap( case 1: case 0: pline("A cloud of %s gas billows from %s.", - Blind ? blindgas[rn2(SIZE(blindgas))] : rndcolor(), + Blind ? ROLL_FROM(blindgas) : rndcolor(), the(xname(obj))); if (!Stunned) { if (Hallucination) @@ -6328,7 +6595,7 @@ chest_trap( struct trap * t_at(coordxy x, coordxy y) { - register struct trap *trap = gf.ftrap; + struct trap *trap = gf.ftrap; while (trap) { if (trap->tx == x && trap->ty == y) @@ -6355,9 +6622,9 @@ count_traps(int ttyp) } void -deltrap(register struct trap* trap) +deltrap(struct trap *trap) { - register struct trap *ttmp; + struct trap *ttmp; if (trap->ammo) { impossible("deleting trap (%d) containing ammo (%d)?", @@ -6452,7 +6719,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; @@ -6473,8 +6741,8 @@ conjoined_pits( return FALSE; } -static void -clear_conjoined_pits(struct trap* trap) +staticfn void +clear_conjoined_pits(struct trap *trap) { int diridx, adjidx; coordxy x, y; @@ -6497,8 +6765,8 @@ clear_conjoined_pits(struct trap* trap) } } -static boolean -adj_nonconjoined_pit(struct trap* adjtrap) +staticfn boolean +adj_nonconjoined_pit(struct trap *adjtrap) { struct trap *trap_with_u = t_at(u.ux0, u.uy0); @@ -6515,8 +6783,8 @@ adj_nonconjoined_pit(struct trap* adjtrap) * Mark all neighboring pits as conjoined pits. * (currently not called from anywhere) */ -static void -join_adjacent_pits(struct trap* trap) +staticfn void +join_adjacent_pits(struct trap *trap) { struct trap *t; int diridx; @@ -6542,7 +6810,7 @@ join_adjacent_pits(struct trap* trap) * Returns TRUE if you escaped a pit and are standing on the precipice. */ boolean -uteetering_at_seen_pit(struct trap* trap) +uteetering_at_seen_pit(struct trap *trap) { return (trap && is_pit(trap->ttyp) && trap->tseen && u_at(trap->tx, trap->ty) @@ -6554,7 +6822,7 @@ uteetering_at_seen_pit(struct trap* trap) * release a trap door */ boolean -uescaped_shaft(struct trap* trap) +uescaped_shaft(struct trap *trap) { return (trap && is_hole(trap->ttyp) && trap->tseen && u_at(trap->tx, trap->ty)); @@ -6562,7 +6830,7 @@ uescaped_shaft(struct trap* trap) /* Destroy a trap that emanates from the floor. */ boolean -delfloortrap(struct trap* ttmp) +delfloortrap(struct trap *ttmp) { /* some of these are arbitrary -dlc */ if (ttmp && ((ttmp->ttyp == SQKY_BOARD) || (ttmp->ttyp == BEAR_TRAP) @@ -6573,7 +6841,7 @@ delfloortrap(struct trap* ttmp) || (ttmp->ttyp == TELEP_TRAP) || (ttmp->ttyp == LEVEL_TELEP) || (ttmp->ttyp == WEB) || (ttmp->ttyp == MAGIC_TRAP) || (ttmp->ttyp == ANTI_MAGIC))) { - register struct monst *mtmp; + struct monst *mtmp; if (u_at(ttmp->tx, ttmp->ty)) { if (u.utraptype != TT_BURIEDBALL) @@ -7013,7 +7281,7 @@ doortrapped(int x, int y, struct monst * mon, int bodypart, int action, } /* Monster is hit by trap. */ -static boolean +staticfn boolean thitm( int tlev, /* missile's attack level */ struct monst *mon, /* target */ @@ -7036,14 +7304,17 @@ thitm( */ if (!strike) { if (obj && cansee(mon->mx, mon->my)) - pline("%s is almost hit by %s!", Monnam(mon), doname(obj)); + pline_mon(mon, "%s is almost hit by %s!", + Monnam(mon), doname(obj)); } else { int dam = 1; - boolean harmless = (stone_missile(obj) && passes_rocks(mon->data)); + boolean harmless = (obj && stone_missile(obj) + && passes_rocks(mon->data)); if (obj && cansee(mon->mx, mon->my)) - pline("%s is hit by %s%s", Monnam(mon), doname(obj), - harmless ? " but is not harmed." : "!"); + pline_mon(mon, "%s is hit by %s%s", + Monnam(mon), doname(obj), + harmless ? " but is not harmed." : "!"); if (d_override) { dam = d_override; } else if (obj) { @@ -7063,7 +7334,7 @@ thitm( /* If a monster dies in a trap on the player's turn (e.g. forced * onto one by jousting or staggering blow), the player is probably * responsible. */ - if (gc.context.mon_moving) { + if (svc.context.mon_moving) { monkilled(mon, "", nocorpse ? -AD_RBRE : AD_PHYS); } else { @@ -7108,10 +7379,11 @@ static const char lava_killer[] = "molten lava"; boolean lava_effects(void) { - register struct obj *obj, *obj2; + struct obj *obj, *obj2, *nextobj; boolean usurvive, boil_away; + unsigned protect_oid = 0; int burncount = 0, burnmesgcount = 0; - int dmg = d(6, 6); /* only applicable for water walking */ + const int dmg = d(6, 6); /* only applicable for water walking */ if (iflags.in_lava_effects) { debugpline0("Skipping recursive lava_effects()."); @@ -7133,14 +7405,35 @@ lava_effects(void) * that player causing hangup at --More-- won't get an * emergency save file created before item destruction. */ - if (!usurvive) - for (obj = gi.invent; obj; obj = obj->nobj) + if (!usurvive) { + 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 + into lava (which happens before item is transferred + from invent to thief->minvent)]; item will still be in + inventory when we return to caller or save bones (or + perform hangup save if that occurs) */ + if (!protect_oid) { + protect_oid = obj->o_id; + obj->in_use = 0; + } else { + impossible( + "lava_effects: '%s' (#%u) is already in use; so is #%u.", + simpleonames(obj), obj->o_id, protect_oid); + } + continue; + } + /* set obj->in_use for items which will be destroyed below */ if ((is_organic(obj) || obj->oclass == POTION_CLASS) && !obj->oerodeproof && objects[obj->otyp].oc_oprop != FIRE_RES && obj->otyp != SCR_FIRE && obj->otyp != SPE_FIREBALL && !obj_resists(obj, 0, 0)) /* for invocation items */ obj->in_use = 1; + } + } /* Check whether we should burn away boots *first* so we know whether to * make the player sink into the lava. Assumption: water walking only @@ -7148,13 +7441,15 @@ lava_effects(void) * (3.7: that assumption is no longer true, but having boots be the first * thing to come into contact with lava makes sense.) */ - if (uarmf && is_organic(uarmf) && !uarmf->oerodeproof) { + if (uarmf && (uarmf->in_use + || (is_organic(uarmf) && !uarmf->oerodeproof))) { obj = uarmf; pline("%s into flame!", Yobjnam2(obj, "burst")); ++burnmesgcount; iflags.in_lava_effects++; /* (see above) */ (void) Boots_off(); - useup(obj); + if (obj->o_id != protect_oid) + useup(obj); iflags.in_lava_effects--; ++burncount; } @@ -7193,9 +7488,11 @@ lava_effects(void) for (obj = gi.invent; obj; obj = obj2) { obj2 = obj->nobj; - /* above, we set in_use for objects which are to be destroyed */ - if (obj->otyp == SPE_BOOK_OF_THE_DEAD && !Blind) { - if (usurvive) + if (obj->o_id == protect_oid) { + /* skip protected item; caller expects to retain access */ + obj->in_use = 1; /* was cleared when setting protect_oid */ + } else if (obj->otyp == SPE_BOOK_OF_THE_DEAD) { + if (usurvive && !Blind) pline("%s glows a strange %s, but remains intact.", The(xname(obj)), hcolor("dark red")); } else if (obj->in_use) { @@ -7228,8 +7525,8 @@ lava_effects(void) u.uhp = -1; /* killer format and name are reconstructed every iteration because lifesaving resets them */ - gk.killer.format = KILLED_BY; - Strcpy(gk.killer.name, lava_killer); + svk.killer.format = KILLED_BY; + Strcpy(svk.killer.name, lava_killer); urgent_pline("You %s...", boil_away ? "boil away" : "burn to a crisp"); done(BURNING); @@ -7254,7 +7551,7 @@ lava_effects(void) set_itimeout(&HWwalking, 5L); goto burn_stuff; } - back_on_ground(BURNING); + rescued_from_terrain(BURNING); /* normally done via safe_teleds() -> teleds() -> spoteffects() but spoteffects() was no-op when called with nonzero in_lava_effects */ @@ -7265,13 +7562,16 @@ lava_effects(void) boil_away = !Fire_resistance; /* if not fire resistant, sink_into_lava() will quickly be fatal; hero needs to escape immediately */ - set_utrap((unsigned) (rn1(4, 4) + ((boil_away ? 2 : rn1(4, 12)) << 8)), + set_utrap((unsigned) (rn1(4, 4) + ((boil_away ? 2 + : rn1(4, 12)) << 8)), TT_LAVA); You("sink into the %s%s!", waterbody_name(u.ux, u.uy), !boil_away ? ", but it only burns slightly" : " and are about to be immolated"); if (Fire_resistance) monstseesu(M_SEEN_FIRE); + else + monstunseesu(M_SEEN_FIRE); if (u.uhp > 1) losehp(!boil_away ? 1 : (u.uhp / 2), lava_killer, KILLED_BY); /* lava damage */ @@ -7306,8 +7606,8 @@ sink_into_lava(void) u.utrap -= (1 << 8); if (u.utrap < (1 << 8)) { - gk.killer.format = KILLED_BY; - Strcpy(gk.killer.name, "molten lava"); + svk.killer.format = KILLED_BY; + Strcpy(svk.killer.name, "molten lava"); urgent_pline("You sink below the surface and die."); burn_away_slime(); /* add insult to injury? */ done(DISSOLVED); @@ -7352,7 +7652,7 @@ sokoban_guilt(void) } /* called when a trap has been deleted or had its ttyp replaced */ -static void +staticfn void maybe_finish_sokoban(void) { struct trap *t; @@ -7370,13 +7670,13 @@ maybe_finish_sokoban(void) if (!t) { /* for livelog to report the sokoban depth in the way that players tend to think about it: 1 for entry level, 4 for top */ - int sokonum = gd.dungeons[u.uz.dnum].entry_lev - u.uz.dlevel + 1; + int sokonum = svd.dungeons[u.uz.dnum].entry_lev - u.uz.dlevel + 1; /* we've passed the last trap without finding a pit or hole; clear the sokoban_rules flag so that luck penalties for things like breaking boulders or jumping will no longer be given, and restrictions on diagonal moves are lifted */ - Sokoban = 0; /* clear gl.level.flags.sokoban_rules */ + Sokoban = 0; /* clear svl.level.flags.sokoban_rules */ /* * TODO: give some feedback about solving the sokoban puzzle * (perhaps say "congratulations" in Japanese?). @@ -7392,13 +7692,11 @@ maybe_finish_sokoban(void) } /* Return the string name of the trap type passed in, unless the player is - * hallucinating, in which case return a random or hallucinatory trap name. - * If the second argument is true, return the correct trap name even when - * hallucinating (for things like wizard mode wishing for traps and impossible - * calls). - */ + hallucinating, in which case return a random or hallucinatory trap name. */ const char * -trapname(int ttyp, boolean override) +trapname( + int ttyp, + boolean override) /* if True, ignore Hallucination */ { static const char *const halu_trapnames[] = { /* riffs on actual nethack traps */ @@ -7435,7 +7733,7 @@ trapname(int ttyp, boolean override) /* inspired by "tourist trap" */ copynchars(roletrap, rn2(3) ? ((fem && gu.urole.name.f) ? gu.urole.name.f - : gu.urole.name.m) + : gu.urole.name.m) : rank_of(u.ulevel, Role_switch, fem), (int) (sizeof roletrap - sizeof " trap")); Strcat(roletrap, " trap"); @@ -7456,12 +7754,13 @@ trapname(int ttyp, boolean override) This is modeled after destroy_items() somewhat and hopefully will be able to merge into it in the future. */ void -ignite_items(struct obj* objchn) +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); @@ -7485,7 +7784,8 @@ trap_ice_effects(coordxy x, coordxy y, boolean ice_is_melting) } else if (ttmp->ammo) { /* shouldn't really happen but... */ deltrap_with_ammo(ttmp, DELTRAP_DESTROY_AMMO); } else { - deltrap(ttmp); + if (!undestroyable_trap(ttmp->ttyp)) + deltrap(ttmp); } } } diff --git a/src/u_init.c b/src/u_init.c index 1ca862ec10..2ae99aaa56 100644 --- a/src/u_init.c +++ b/src/u_init.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 u_init.c $NHDT-Date: 1621131203 2021/05/16 02:13:23 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.75 $ */ +/* NetHack 3.7 u_init.c $NHDT-Date: 1725227809 2024/09/01 21:56:49 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.111 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2017. */ /* NetHack may be freely redistributed. See license for details. */ @@ -13,11 +13,21 @@ struct trobj { Bitfield(trbless, 2); }; -static void ini_inv(struct trobj *); -static void knows_object(int); -static void knows_class(char); -static void set_skill_cap_minimum(int, int); -static boolean restricted_spell_discipline(int); +staticfn struct obj *ini_inv_mkobj_filter(int, boolean); +staticfn short ini_inv_obj_substitution(struct trobj *, + struct obj *) NONNULLPTRS; +staticfn void ini_inv_adjust_obj(struct trobj *, + struct obj *) NONNULLPTRS; +staticfn void ini_inv_use_obj(struct obj *) NONNULLARG1; +staticfn void ini_inv(struct trobj *) NONNULLARG1; +staticfn void knows_object(int, boolean); +staticfn void knows_class(char); +staticfn void u_init_role(void); +staticfn void u_init_race(void); +staticfn void pauper_reinit(void); +staticfn void u_init_carry_attr_boost(void); +staticfn void set_skill_cap_minimum(int, int); +staticfn boolean restricted_spell_discipline(int); #define UNDEF_TYP 0 #define UNDEF_SPE '\177' @@ -160,14 +170,13 @@ static struct trobj Valkyrie[] = { { 0, 0, 0, 0, 0 } }; static struct trobj Wizard[] = { -#define W_MULTSTART 2 -#define W_MULTEND 6 { QUARTERSTAFF, 1, WEAPON_CLASS, 1, 1 }, { CLOAK_OF_MAGIC_RESISTANCE, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, { SPE_FORCE_BOLT, 0, SPBOOK_CLASS, 1, 1 }, { SPE_MAGIC_MISSILE, 0, SPBOOK_CLASS, 1, 1 }, { UNDEF_TYP, UNDEF_SPE, SPBOOK_CLASS, 1, UNDEF_BLESS }, { UNDEF_TYP, UNDEF_SPE, SPBOOK_CLASS, 1, UNDEF_BLESS }, + { MAGIC_MARKER, 19, TOOL_CLASS, 1, 0 }, /* actually spe = 18 + d4 */ { 0, 0, 0, 0, 0 } }; @@ -177,8 +186,7 @@ static struct trobj Wizard[] = { static struct trobj Tinopener[] = { { TIN_OPENER, 0, TOOL_CLASS, 1, 0 }, { 0, 0, 0, 0, 0 } }; -static struct trobj Magicmarker[] = { { MAGIC_MARKER, UNDEF_SPE, TOOL_CLASS, - 1, 0 }, +static struct trobj Magicmarker[] = { { MAGIC_MARKER, 19, TOOL_CLASS, 1, 0 }, { 0, 0, 0, 0, 0 } }; static struct trobj Lamp[] = { { OIL_LAMP, 1, TOOL_CLASS, 1, 0 }, { 0, 0, 0, 0, 0 } }; @@ -186,7 +194,7 @@ static struct trobj Blindfold[] = { { BLINDFOLD, 0, TOOL_CLASS, 1, 0 }, { 0, 0, 0, 0, 0 } }; static struct trobj Instrument[] = { { FLUTE, 0, TOOL_CLASS, 1, 0 }, { 0, 0, 0, 0, 0 } }; -static struct trobj Xtra_food[] = { { UNDEF_TYP, UNDEF_SPE, FOOD_CLASS, 2, 0 }, +static struct trobj Xtra_food[] = { { UNDEF_TYP, UNDEF_SPE, FOOD_CLASS, 2, 0}, { 0, 0, 0, 0, 0 } }; static struct trobj Leash[] = { { LEASH, 0, TOOL_CLASS, 1, 0 }, { 0, 0, 0, 0, 0 } }; @@ -550,21 +558,26 @@ static const struct def_skill Skill_W[] = { { P_NONE, 0 } }; -static void -knows_object(int obj) +staticfn void +knows_object(int obj, boolean override_pauper) { + if (u.uroleplay.pauper && !override_pauper) + return; discover_object(obj, TRUE, FALSE); objects[obj].oc_pre_discovered = 1; /* not a "discovery" */ } /* Know ordinary (non-magical) objects of a certain class, * like all gems except the luckstone. */ -static void +staticfn void knows_class(char sym) { struct obj odummy, *o; int ct; + if (u.uroleplay.pauper) + return; + odummy = cg.zeroobj; odummy.oclass = sym; o = &odummy; /* for use in various obj.h macros */ @@ -576,7 +589,7 @@ knows_class(char sym) * arrow, and spear limitation below. */ - for (ct = gb.bases[(uchar) sym]; ct < gb.bases[(uchar) sym + 1]; ct++) { + for (ct = svb.bases[(uchar) sym]; ct < svb.bases[(uchar) sym + 1]; ct++) { /* not flagged as magic but shouldn't be pre-discovered */ if (ct == CORNUTHAUM || ct == DUNCE_CAP) continue; @@ -596,126 +609,44 @@ knows_class(char sym) } if (objects[ct].oc_class == sym && !objects[ct].oc_magic) - knows_object(ct); - } -} - -/* Adjust a skill cap to a specified minimum. */ -void -set_skill_cap_minimum(int skill, int minimum) -{ - if (P_MAX_SKILL(skill) < minimum) { - P_MAX_SKILL(skill) = minimum; + knows_object(ct, FALSE); } } -void -u_init(void) +/* role-specific initializations */ +staticfn void +u_init_role(void) { - register int i; - struct u_roleplay tmpuroleplay = u.uroleplay; /* set by rcfile options */ - - flags.female = flags.initgend; - flags.beginner = 1; - - /* zero u, including pointer values -- - * necessary when aborting from a failed restore */ - (void) memset((genericptr_t) &u, 0, sizeof(u)); - u.ustuck = (struct monst *) 0; - (void) memset((genericptr_t) &ubirthday, 0, sizeof(ubirthday)); - (void) memset((genericptr_t) &urealtime, 0, sizeof(urealtime)); - - u.uroleplay = tmpuroleplay; /* restore options set via rcfile */ - -#if 0 /* documentation of more zero values as desirable */ - u.usick_cause[0] = 0; - u.uluck = u.moreluck = 0; - uarmu = 0; - uarm = uarmc = uarmh = uarms = uarmg = uarmf = 0; - uwep = uball = uchain = uleft = uright = 0; - uswapwep = uquiver = 0; - u.twoweap = FALSE; /* bypass set_twoweap() */ - u.ublessed = 0; /* not worthy yet */ - u.ugangr = 0; /* gods not angry */ - u.ugifts = 0; /* no divine gifts bestowed */ - u.uevent.uhand_of_elbereth = 0; - u.uevent.uheard_tune = 0; - u.uevent.uopened_dbridge = 0; - u.uevent.udemigod = 0; /* not a demi-god yet... */ - u.udg_cnt = 0; - u.mh = u.mhmax = u.mtimedone = 0; - u.uz.dnum = u.uz0.dnum = 0; - u.utotype = UTOTYPE_NONE; -#endif /* 0 */ - - u.uz.dlevel = 1; - u.uz0.dlevel = 0; - u.utolev = u.uz; - - u.umoved = FALSE; - u.umortality = 0; - u.ugrave_arise = NON_PM; - - u.umonnum = u.umonster = gu.urole.mnum; - u.ulycn = NON_PM; - set_uasmon(); - - u.ulevel = 0; /* set up some of the initial attributes */ - u.uhp = u.uhpmax = u.uhppeak = newhp(); - u.uen = u.uenmax = u.uenpeak = newpw(); - u.uspellprot = 0; - adjabil(0, 1); - u.ulevel = u.ulevelmax = 1; - - init_uhunger(); - for (i = 0; i <= MAXSPELL; i++) - gs.spl_book[i].sp_id = NO_SPELL; - u.ublesscnt = 300; /* no prayers just yet */ - u.ulastprayed = -1; /* has never prayed */ - u.ualignbase[A_CURRENT] = u.ualignbase[A_ORIGINAL] = u.ualign.type = - aligns[flags.initalign].value; + int i; -#if defined(BSD) && !defined(POSIX_TYPES) - (void) time((long *) &ubirthday); -#else - (void) time(&ubirthday); -#endif + /* the program used to check moves<=1 && invent==NULL do decide whether + a new game has started, but due to the 'pauper' option/conduct, can't + rely on invent becoming non-Null anymore; instead, initialize moves + to 0 instead of 1, then set it to 1 here, where invent init occurs */ + svm.moves = 1L; - /* - * For now, everyone starts out with a night vision range of 1 and - * their xray range disabled. - */ - u.nv_range = 1; - u.xray_range = -1; - /* OPTIONS:blind results in permanent blindness (unless overridden - by the Eyes of the Overworld, which will clear 'u.uroleplay.blind' - to void the conduct, but will leave the PermaBlind bit set so that - blindness resumes when the Eyes are removed). */ - if (u.uroleplay.blind) - HBlinded |= FROMROLEPLAY; /* set PermaBlind */ - - /*** Role-specific initializations ***/ switch (Role_switch) { /* rn2(100) > 50 necessary for some choices because some * random number generators are bad enough to seriously * skew the results if we use rn2(2)... --KAA */ - case PM_ARCHEOLOGIST: { + case PM_ARCHEOLOGIST: ini_inv(Archeologist); if (!rn2(10)) ini_inv(Tinopener); else if (!rn2(4)) ini_inv(Lamp); - else if (!rn2(10)) + else if (!rn2(5)) ini_inv(Magicmarker); - knows_object(SACK); - knows_object(TOUCHSTONE); + knows_object(SACK, FALSE); + knows_object(TOUCHSTONE, FALSE); /* FALSE: don't override pauper here, + * but TOUCHSTONE will be made known + * in pauper_reinit() */ for (i = FIRST_GLASS_GEM; i <= LAST_GLASS_GEM; ++i) { - knows_object(i); + knows_object(i, FALSE); } skill_init(Skill_A); break; - } case PM_BARBARIAN: if (rn2(100) >= 50) { /* see above comment */ Barbarian[B_MAJOR].trotyp = BATTLE_AXE; @@ -738,7 +669,7 @@ u_init(void) ini_inv(Healer); if (!rn2(25)) ini_inv(Lamp); - knows_object(POT_FULL_HEALING); + knows_object(POT_FULL_HEALING, FALSE); skill_init(Skill_H); break; case PM_KNIGHT: @@ -756,23 +687,23 @@ u_init(void) Monk[M_BOOK].trotyp = M_spell[rn2(90) / 30]; /* [0..2] */ ini_inv(Monk); - if (!rn2(5)) + if (!rn2(4)) ini_inv(Magicmarker); else if (!rn2(10)) ini_inv(Lamp); knows_class(ARMOR_CLASS); /* sufficiently martial-arts oriented item to ignore language issue */ - knows_object(SHURIKEN); + knows_object(SHURIKEN, FALSE); skill_init(Skill_Mon); break; } case PM_CLERIC: /* priest/priestess */ ini_inv(Priest); - if (!rn2(10)) + if (!rn2(5)) ini_inv(Magicmarker); else if (!rn2(10)) ini_inv(Lamp); - knows_object(POT_WATER); + knows_object(POT_WATER, TRUE); /* override pauper */ skill_init(Skill_P); /* KMH, conduct -- * Some may claim that this isn't agnostic, since they @@ -795,8 +726,10 @@ u_init(void) ini_inv(Rogue); if (!rn2(5)) ini_inv(Blindfold); - knows_object(SACK); - knows_object(THIEFSTONE); + knows_object(SACK, FALSE); /* FALSE: don't override pauper here, + * but sack will be made known in + * pauper_reinit() */ + knows_object(THIEFSTONE, FALSE); knows_class(WEAPON_CLASS); /* daggers only */ skill_init(Skill_R); break; @@ -813,7 +746,9 @@ u_init(void) if (objects[i].oc_magic) /* skip "magic koto" */ continue; if (Japanese_item_name(i, (const char *) 0)) - knows_object(i); + /* we don't override pauper here because that would give + samarai an advantage of knowing several items in advance */ + knows_object(i, FALSE); } skill_init(Skill_S); break; @@ -827,7 +762,7 @@ u_init(void) ini_inv(Leash); else if (!rn2(25)) ini_inv(Towel); - else if (!rn2(25)) + else if (!rn2(20)) ini_inv(Magicmarker); skill_init(Skill_T); break; @@ -841,8 +776,6 @@ u_init(void) break; case PM_WIZARD: ini_inv(Wizard); - if (!rn2(5)) - ini_inv(Magicmarker); if (!rn2(5)) ini_inv(Blindfold); skill_init(Skill_W); @@ -851,8 +784,12 @@ u_init(void) default: /* impossible */ break; } +} - /*** Race-specific initializations ***/ +/* race-specific initializations */ +staticfn void +u_init_race(void) +{ switch (Race_switch) { case PM_HUMAN: /* Nothing special */ @@ -865,37 +802,37 @@ u_init(void) * get only non-magic instruments. */ if (Role_if(PM_CLERIC) || Role_if(PM_WIZARD)) { - static int trotyp[] = { FLUTE, TOOLED_HORN, HARP, + static int trotyp[] = { FLUTE, TOOLED_HORN, HARP, BELL, BUGLE, LEATHER_DRUM }; - Instrument[0].trotyp = trotyp[rn2(SIZE(trotyp))]; + Instrument[0].trotyp = ROLL_FROM(trotyp); ini_inv(Instrument); } /* Elves can recognize all elvish objects */ - knows_object(ELVEN_SHORT_SWORD); - knows_object(ELVEN_ARROW); - knows_object(ELVEN_BOW); - knows_object(ELVEN_SPEAR); - knows_object(ELVEN_DAGGER); - knows_object(ELVEN_BROADSWORD); - knows_object(ELVEN_RING_MAIL); - knows_object(ELVEN_HELM); - knows_object(ELVEN_SHIELD); - knows_object(ELVEN_BOOTS); - knows_object(ELVEN_CLOAK); + knows_object(ELVEN_SHORT_SWORD, FALSE); + knows_object(ELVEN_ARROW, FALSE); + knows_object(ELVEN_BOW, FALSE); + knows_object(ELVEN_SPEAR, FALSE); + knows_object(ELVEN_DAGGER, FALSE); + knows_object(ELVEN_BROADSWORD, FALSE); + knows_object(ELVEN_RING_MAIL, FALSE); + knows_object(ELVEN_HELM, FALSE); + knows_object(ELVEN_SHIELD, FALSE); + knows_object(ELVEN_BOOTS, FALSE); + knows_object(ELVEN_CLOAK, FALSE); /* All elves have a natural affinity for enchantments */ set_skill_cap_minimum(P_ENCHANTMENT_SPELL, P_BASIC); break; case PM_DWARF: /* Dwarves can recognize all dwarvish objects */ - knows_object(DWARVISH_SPEAR); - knows_object(DWARVISH_SHORT_SWORD); - knows_object(DWARVISH_MATTOCK); - knows_object(DWARVISH_HELM); - knows_object(DWARVISH_RING_MAIL); - knows_object(DWARVISH_CLOAK); - knows_object(DWARVISH_ROUNDSHIELD); + knows_object(DWARVISH_SPEAR, FALSE); + knows_object(DWARVISH_SHORT_SWORD, FALSE); + knows_object(DWARVISH_MATTOCK, FALSE); + knows_object(DWARVISH_HELM, FALSE); + knows_object(DWARVISH_RING_MAIL, FALSE); + knows_object(DWARVISH_CLOAK, FALSE); + knows_object(DWARVISH_ROUNDSHIELD, FALSE); /* All dwarves have skill with digging tools */ set_skill_cap_minimum(P_PICK_AXE, P_SKILLED); break; @@ -911,21 +848,202 @@ u_init(void) if (!Role_if(PM_WIZARD)) ini_inv(Xtra_food); /* Orcs can recognize all orcish objects */ - knows_object(ORCISH_SHORT_SWORD); - knows_object(ORCISH_ARROW); - knows_object(ORCISH_BOW); - knows_object(ORCISH_SPEAR); - knows_object(ORCISH_DAGGER); - knows_object(ORCISH_RING_MAIL); - knows_object(ORCISH_HELM); - knows_object(ORCISH_SHIELD); - knows_object(URUK_HAI_SHIELD); - knows_object(ORCISH_CLOAK); + knows_object(ORCISH_SHORT_SWORD, FALSE); + knows_object(ORCISH_ARROW, FALSE); + knows_object(ORCISH_BOW, FALSE); + knows_object(ORCISH_SPEAR, FALSE); + knows_object(ORCISH_DAGGER, FALSE); + knows_object(ORCISH_RING_MAIL, FALSE); + knows_object(ORCISH_HELM, FALSE); + knows_object(ORCISH_SHIELD, FALSE); + knows_object(URUK_HAI_SHIELD, FALSE); + knows_object(ORCISH_CLOAK, FALSE); break; default: /* impossible */ break; } +} + +/* for 'pauper' aka 'unpreparsed'; take away any skills (bare-handed combat, + riding) that are better than unskilled; learn the book (without carrying + it or knowing its spell yet) for some key spells */ +staticfn void +pauper_reinit(void) +{ + int skill, preknown = STRANGE_OBJECT; + + if (!u.uroleplay.pauper) + return; + + for (skill = 0; skill < P_NUM_SKILLS; skill++) + if (P_SKILL(skill) > P_UNSKILLED) { + P_SKILL(skill) = P_UNSKILLED; + P_ADVANCE(skill) = 0; + } + /* pauper has lost out on initial skills, but provide some unspent skill + credits to make up for that */ + u.weapon_slots = 2; + + /* paupers don't know any spells yet, but several roles will recognize + the spellbook for a key spell (not necessarily that role's special + spell); "supply chests" on the first few levels provide a fairly + high chance to find the book; some other roles know a non-book item */ + switch (Role_switch) { + case PM_HEALER: + preknown = SPE_HEALING; + break; + case PM_CLERIC: + case PM_KNIGHT: + case PM_MONK: + preknown = SPE_PROTECTION; + break; + case PM_WIZARD: + preknown = SPE_FORCE_BOLT; + break; + case PM_ARCHEOLOGIST: + preknown = TOUCHSTONE; + break; + case PM_CAVE_DWELLER: + preknown = FLINT; + break; + case PM_ROGUE: + case PM_TOURIST: + preknown = SACK; + break; + case PM_SAMURAI: + /* food ration isn't interesting to discover, but put "gunyoki" into + discoveries list for players who might not recognize what it is */ + preknown = FOOD_RATION; + break; + default: + case PM_BARBARIAN: + case PM_RANGER: + case PM_VALKYRIE: + break; + } + if (preknown != STRANGE_OBJECT) + knows_object(preknown, TRUE); +} + +/* boost STR and CON until hero can carry inventory */ +staticfn void +u_init_carry_attr_boost(void) +{ + /* make sure you can carry all you have - especially for Tourists */ + while (inv_weight() > 0) { + if (adjattrib(A_STR, 1, AA_NOMSG)) + continue; + if (adjattrib(A_CON, 1, AA_NOMSG)) + continue; + /* only get here when didn't boost strength or constitution */ + break; + } +} + +/* Adjust a skill cap to a specified minimum. */ +void +set_skill_cap_minimum(int skill, int minimum) +{ + if (P_MAX_SKILL(skill) < minimum) { + P_MAX_SKILL(skill) = minimum; + } +} + +void +u_init(void) +{ + int i; + struct u_roleplay tmpuroleplay = u.uroleplay; /* set by rcfile options */ + + flags.female = flags.initgend; + flags.beginner = TRUE; + + /* zero u, including pointer values -- + * necessary when aborting from a failed restore */ + (void) memset((genericptr_t) &u, 0, sizeof(u)); + u.ustuck = (struct monst *) 0; + (void) memset((genericptr_t) &ubirthday, 0, sizeof(ubirthday)); + (void) memset((genericptr_t) &urealtime, 0, sizeof(urealtime)); + + u.uroleplay = tmpuroleplay; /* restore options set via rcfile */ + +#if 0 /* documentation of more zero values as desirable */ + u.usick_cause[0] = 0; + u.uluck = u.moreluck = 0; + uarmu = 0; + uarm = uarmc = uarmh = uarms = uarmg = uarmf = 0; + uwep = uball = uchain = uleft = uright = 0; + uswapwep = uquiver = 0; + u.twoweap = FALSE; /* bypass set_twoweap() */ + u.ublessed = 0; /* not worthy yet */ + u.ugangr = 0; /* gods not angry */ + u.ugifts = 0; /* no divine gifts bestowed */ + u.uevent.uhand_of_elbereth = 0; + u.uevent.uheard_tune = 0; + u.uevent.uopened_dbridge = 0; + u.uevent.udemigod = 0; /* not a demi-god yet... */ + u.udg_cnt = 0; + u.mh = u.mhmax = u.mtimedone = 0; + u.uz.dnum = u.uz0.dnum = 0; + u.utotype = UTOTYPE_NONE; +#endif /* 0 */ + + u.uz.dlevel = 1; + u.uz0.dlevel = 0; + u.utolev = u.uz; + + u.umoved = FALSE; + u.umortality = 0; + u.ugrave_arise = NON_PM; + + u.umonnum = u.umonster = gu.urole.mnum; + u.ulycn = NON_PM; + set_uasmon(); + + u.ulevel = 0; /* set up some of the initial attributes */ + u.uhp = u.uhpmax = u.uhppeak = newhp(); + u.uen = u.uenmax = u.uenpeak = newpw(); + u.uspellprot = 0; + adjabil(0, 1); + u.ulevel = u.ulevelmax = 1; + + init_uhunger(); + for (i = 0; i <= MAXSPELL; i++) + svs.spl_book[i].sp_id = NO_SPELL; + u.ublesscnt = 300; /* no prayers just yet */ + u.ulastprayed = -1; /* has never prayed */ + u.ualignbase[A_CURRENT] = u.ualignbase[A_ORIGINAL] = u.ualign.type = + aligns[flags.initalign].value; + +#if defined(BSD) && !defined(POSIX_TYPES) + (void) time((long *) &ubirthday); +#else + (void) time(&ubirthday); +#endif + + /* + * For now, everyone starts out with a night vision range of 1 and + * their xray range disabled. + */ + u.nv_range = 1; + u.xray_range = -1; + u.unblind_telepat_range = -1; + + /* OPTIONS:blind results in permanent blindness (unless overridden + by the Eyes of the Overworld, which will clear 'u.uroleplay.blind' + to void the conduct, but will leave the PermaBlind bit set so that + blindness resumes when the Eyes are removed). */ + if (u.uroleplay.blind) + HBlinded |= FROMROLEPLAY; /* set PermaBlind */ + + u_init_role(); + u_init_race(); + if (u.uroleplay.pauper) + pauper_reinit(); + + /* roughly based on distribution in human population */ + u.uhandedness = rn2(10) ? RIGHT_HANDED : LEFT_HANDED; if (discover) ini_inv(Wishing); @@ -939,28 +1057,9 @@ u_init(void) find_ac(); /* get initial ac value */ init_attr(75); /* init attribute values */ + vary_init_attr(); /* minor variation to attrs */ + u_init_carry_attr_boost(); max_rank_sz(); /* set max str size for class ranks */ - /* - * Do we really need this? - */ - for (i = 0; i < A_MAX; i++) - if (!rn2(20)) { - register int xd = rn2(7) - 2; /* biased variation */ - - (void) adjattrib(i, xd, AA_NOMSG); - if (ABASE(i) < AMAX(i)) - AMAX(i) = ABASE(i); - } - - /* make sure you can carry all you have - especially for Tourists */ - while (inv_weight() > 0) { - if (adjattrib(A_STR, 1, AA_NOMSG) == AA_CURRCHNG) - continue; - if (adjattrib(A_CON, 1, AA_NOMSG) == AA_CURRCHNG) - continue; - /* only get here when didn't boost strength or constitution */ - break; - } /* If we have at least one spell, force starting Pw to be enough, so hero can cast the level 1 spell they should have */ @@ -971,7 +1070,7 @@ u_init(void) } /* skills aren't initialized, so we use the role-specific skill lists */ -static boolean +staticfn boolean restricted_spell_discipline(int otyp) { const struct def_skill *skills; @@ -1030,69 +1129,221 @@ restricted_spell_discipline(int otyp) return TRUE; } -static void +/* create random object of certain class, filtering out too powerful items */ +staticfn struct obj * +ini_inv_mkobj_filter(int oclass, boolean got_level1_spellbook) +{ + struct obj *obj; + int otyp, trycnt = 0; + + /* + * For random objects, do not create certain overly powerful + * items: wand of wishing, ring of levitation, or the + * polymorph/polymorph control combination. Specific objects, + * i.e. the discovery wishing, are still OK. + * Also, don't get a couple of really useless items. (Note: + * punishment isn't "useless". Some players who start out with + * one will immediately read it and use the iron ball as a + * weapon.) + */ + obj = mkobj(oclass, FALSE); + otyp = obj->otyp; + + while (otyp == WAN_WISHING || otyp == gn.nocreate + || otyp == gn.nocreate2 || otyp == gn.nocreate3 + || otyp == gn.nocreate4 || otyp == RIN_LEVITATION + /* 'useless' items */ + || otyp == POT_HALLUCINATION + || otyp == POT_ACID + || otyp == SCR_AMNESIA + || otyp == SCR_WATER + || otyp == SCR_FIRE + || otyp == SCR_BLANK_PAPER + || otyp == SPE_BLANK_PAPER + || otyp == RIN_AGGRAVATE_MONSTER + || otyp == RIN_HUNGER + || otyp == WAN_NOTHING + /* orcs start with poison resistance */ + || (otyp == RIN_POISON_RESISTANCE && Race_if(PM_ORC)) + /* Monks don't use weapons */ + || (otyp == SCR_ENCHANT_WEAPON && Role_if(PM_MONK)) + /* wizard patch -- they already have one */ + || (otyp == SPE_FORCE_BOLT && Role_if(PM_WIZARD)) + || (otyp == SPE_MAGIC_MISSILE && Role_if(PM_WIZARD)) + /* powerful spells are either useless to + low level players or unbalancing; also + spells in restricted skill categories */ + || (obj->oclass == SPBOOK_CLASS + && (objects[otyp].oc_level > (got_level1_spellbook ? 3 : 1) + || restricted_spell_discipline(otyp))) + || otyp == SPE_NOVEL + /* items that will be iron for elves (rings/wands perhaps) that can't + * become copper */ + || (Race_if(PM_ELF) && objects[otyp].oc_material == IRON + && !valid_obj_material(obj, COPPER))) { + dealloc_obj(obj); + if (++trycnt > 1000) { + /* This lonely pancake's potential will never be realized. + * It will exist only as a thought, of something that could have + * been, but never will be. It will never experience maple syrup + * oozing into its nooks, or see the delightful expression on + * someone's face as they are about to let it dance across their + * taste buds. */ + obj = mksobj(PANCAKE, TRUE, FALSE); + break; + } + obj = mkobj(oclass, FALSE); + otyp = obj->otyp; + } + return obj; +} + +/* substitute object with something else based on race. + only changes otyp, and returns it. */ +staticfn short +ini_inv_obj_substitution(struct trobj *trop, struct obj *obj) +{ + if (gu.urace.mnum != PM_HUMAN) { + int i; + + /* substitute race-specific items; this used to be in + the 'if (otyp != UNDEF_TYP) { }' block above, but then + substitutions didn't occur for randomly generated items + (particularly food) which have racial substitutes */ + for (i = 0; inv_subs[i].race_pm != NON_PM; ++i) + if (inv_subs[i].race_pm == gu.urace.mnum + && obj->otyp == inv_subs[i].item_otyp) { + debugpline3("ini_inv: substituting %s for %s%s", + OBJ_NAME(objects[inv_subs[i].subs_otyp]), + (trop->trotyp == UNDEF_TYP) ? "random " : "", + OBJ_NAME(objects[obj->otyp])); + obj->otyp = inv_subs[i].subs_otyp; + break; + } + } + return obj->otyp; +} + +staticfn void +ini_inv_adjust_obj(struct trobj *trop, struct obj *obj) +{ + if (trop->trclass == COIN_CLASS) { + /* no "blessed" or "identified" money */ + obj->quan = u.umoney0; + } else { + if (objects[obj->otyp].oc_uses_known) + obj->known = 1; + obj->dknown = obj->bknown = obj->rknown = 1; + if (Is_container(obj) || obj->otyp == STATUE) { + obj->cknown = obj->lknown = 1; + obj->otrapped = 0; + } + obj->cursed = 0; + if (obj->opoisoned && u.ualign.type != A_CHAOTIC) + obj->opoisoned = 0; + if (obj->oclass == WEAPON_CLASS || obj->oclass == TOOL_CLASS) { + obj->quan = (long) trop->trquan; + trop->trquan = 1; + } else if (obj->oclass == GEM_CLASS && is_graystone(obj) + && obj->otyp != FLINT) { + obj->quan = 1L; + } + if (trop->trspe != UNDEF_SPE) { + obj->spe = trop->trspe; + if (trop->trotyp == MAGIC_MARKER && obj->spe < 96) + obj->spe += rn2(4); + } else { + /* Don't start with +0 or negative rings */ + if (objects[obj->otyp].oc_class == RING_CLASS + && objects[obj->otyp].oc_charged && obj->spe <= 0) + obj->spe = rne(3); + } + if (trop->trbless != UNDEF_BLESS) + obj->blessed = trop->trbless; + + /* Don't allow materials to be start scummed for */ + set_material(obj, objects[obj->otyp].oc_material); + + /* Replace iron objects (e.g. Priest's mace) with copper for elves */ + if (Race_if(PM_ELF) && obj->material == IRON) { + set_material(obj, COPPER); + } + + /* Don't allow weapons to roll high enchantment and get an oname + * when they'll then have their enchantment set after this */ + if (has_oname(obj)) { + free_oname(obj); + } + } + /* defined after setting otyp+quan + blessedness */ + obj->owt = weight(obj); +} + +/* initial inventory: wear, wield, learn the spell/obj */ +staticfn void +ini_inv_use_obj(struct obj *obj) +{ + /* Make the type known if necessary */ + if (OBJ_DESCR(objects[obj->otyp]) && obj->known) + discover_object(obj->otyp, TRUE, FALSE); + if (obj->otyp == OIL_LAMP) + discover_object(POT_OIL, TRUE, FALSE); + + if (obj->oclass == ARMOR_CLASS) { + if (is_shield(obj) && !uarms && !(uwep && bimanual(uwep))) { + setworn(obj, W_ARMS); + /* Prior to 3.6.2 this used to unset uswapwep if it was set, + but wearing a shield doesn't prevent having an alternate + weapon ready to swap with the primary; just make sure we + aren't two-weaponing (academic; no one starts that way) */ + set_twoweap(FALSE); /* u.twoweap = FALSE */ + } else if (is_helmet(obj) && !uarmh) + setworn(obj, W_ARMH); + else if (is_gloves(obj) && !uarmg) + setworn(obj, W_ARMG); + else if (is_shirt(obj) && !uarmu) + setworn(obj, W_ARMU); + else if (is_cloak(obj) && !uarmc) + setworn(obj, W_ARMC); + else if (is_boots(obj) && !uarmf) + setworn(obj, W_ARMF); + else if (is_suit(obj) && !uarm) + setworn(obj, W_ARM); + } + + if (obj->oclass == WEAPON_CLASS || is_weptool(obj) + || obj->otyp == TIN_OPENER + || obj->otyp == FLINT || obj->otyp == ROCK) { + if (is_ammo(obj) || is_missile(obj)) { + if (!uquiver) + setuqwep(obj); + } else if (!uwep && (!uarms || !bimanual(obj))) { + setuwep(obj); + } else if (!uswapwep) { + setuswapwep(obj); + } + } + if (obj->oclass == SPBOOK_CLASS && obj->otyp != SPE_BLANK_PAPER) + initialspell(obj); +} + +staticfn void ini_inv(struct trobj *trop) { struct obj *obj; - int otyp, i; + int otyp; boolean got_sp1 = FALSE; /* got a level 1 spellbook? */ + if (u.uroleplay.pauper) /* pauper gets no items */ + return; + while (trop->trclass) { otyp = (int) trop->trotyp; if (otyp != UNDEF_TYP) { obj = mksobj(otyp, TRUE, FALSE); } else { /* UNDEF_TYP */ - int trycnt = 0; - /* - * For random objects, do not create certain overly powerful - * items: wand of wishing, ring of levitation, or the - * polymorph/polymorph control combination. Specific objects, - * i.e. the discovery wishing, are still OK. - * Also, don't get a couple of really useless items. (Note: - * punishment isn't "useless". Some players who start out with - * one will immediately read it and use the iron ball as a - * weapon.) - */ - obj = mkobj(trop->trclass, FALSE); + obj = ini_inv_mkobj_filter(trop->trclass, got_sp1); otyp = obj->otyp; - while (otyp == WAN_WISHING || otyp == gn.nocreate - || otyp == gn.nocreate2 || otyp == gn.nocreate3 - || otyp == gn.nocreate4 || otyp == RIN_LEVITATION - /* 'useless' items */ - || otyp == POT_HALLUCINATION - || otyp == POT_ACID - || otyp == SCR_WATER - || otyp == SCR_FIRE - || otyp == SCR_BLANK_PAPER - || otyp == SPE_BLANK_PAPER - || otyp == RIN_AGGRAVATE_MONSTER - || otyp == RIN_HUNGER - || otyp == WAN_NOTHING - /* orcs start with poison resistance */ - || (otyp == RIN_POISON_RESISTANCE && Race_if(PM_ORC)) - /* Monks don't use weapons */ - || (otyp == SCR_ENCHANT_WEAPON && Role_if(PM_MONK)) - /* wizard patch -- they already have one */ - || (otyp == SPE_FORCE_BOLT && Role_if(PM_WIZARD)) - || (otyp == SPE_MAGIC_MISSILE && Role_if(PM_WIZARD)) - /* powerful spells are either useless to - low level players or unbalancing; also - spells in restricted skill categories */ - || (obj->oclass == SPBOOK_CLASS - && (objects[otyp].oc_level > (got_sp1 ? 3 : 1) - || restricted_spell_discipline(otyp))) - || otyp == SPE_NOVEL - /* items that will be iron for elves (rings/wands perhaps) - * that can't become copper */ - || (Race_if(PM_ELF) && objects[otyp].oc_material == IRON - && !valid_obj_material(obj, COPPER))) { - dealloc_obj(obj); - obj = mkobj(trop->trclass, FALSE); - otyp = obj->otyp; - if (++trycnt > 1000) - break; - } - /* Heavily relies on the fact that 1) we create wands * before rings, 2) that we create rings before * spellbooks, and that 3) not more than 1 object of a @@ -1114,49 +1365,10 @@ ini_inv(struct trobj *trop) if (obj->oclass == RING_CLASS || obj->oclass == SPBOOK_CLASS) gn.nocreate4 = otyp; } - /* Put post-creation object adjustments that don't depend on whether it - * was UNDEF_TYP or not after this. */ + /* Put post-creation object adjustments that don't depend on whether + * it was UNDEF_TYP or not after this. */ - /* Don't start with +0 or negative rings */ - if (objects[otyp].oc_class == RING_CLASS && objects[otyp].oc_charged - && obj->spe <= 0) - obj->spe = rne(3); - - /* Don't allow materials to be start scummed for */ - set_material(obj, objects[otyp].oc_material); - - /* Replace iron objects (e.g. Priest's mace) with copper for elves */ - if (Race_if(PM_ELF) && obj->material == IRON) { - set_material(obj, COPPER); - } - - /* Don't allow weapons to roll high enchantment and get an oname - * when they'll then have their enchantment set after this */ - if (has_oname(obj)) { - free_oname(obj); - } - - if (gu.urace.mnum != PM_HUMAN) { - /* substitute race-specific items; this used to be in - the 'if (otyp != UNDEF_TYP) { }' block above, but then - substitutions didn't occur for randomly generated items - (particularly food) which have racial substitutes */ - for (i = 0; inv_subs[i].race_pm != NON_PM; ++i) - if (inv_subs[i].race_pm == gu.urace.mnum - && otyp == inv_subs[i].item_otyp) { - debugpline3("ini_inv: substituting %s for %s%s", - OBJ_NAME(objects[inv_subs[i].subs_otyp]), - (trop->trotyp == UNDEF_TYP) ? "random " : "", - OBJ_NAME(objects[otyp])); - otyp = obj->otyp = inv_subs[i].subs_otyp; - /* This might have created a bad material combination, such - * as a dagger (which was forced to be iron earlier) turning - * into an elven dagger, but now remaining iron. Fix this up - * here as well. */ - set_material(obj, objects[otyp].oc_material); - break; - } - } + otyp = ini_inv_obj_substitution(trop, obj); /* nudist gets no armor */ if (u.uroleplay.nudist && obj->oclass == ARMOR_CLASS) { @@ -1165,77 +1377,10 @@ ini_inv(struct trobj *trop) continue; } - if (trop->trclass == COIN_CLASS) { - /* no "blessed" or "identified" money */ - obj->quan = u.umoney0; - } else { - if (objects[otyp].oc_uses_known) - obj->known = 1; - obj->dknown = obj->bknown = obj->rknown = 1; - if (Is_container(obj) || obj->otyp == STATUE) { - obj->cknown = obj->lknown = 1; - obj->otrapped = 0; - } - obj->cursed = 0; - if (obj->opoisoned && u.ualign.type != A_CHAOTIC) - obj->opoisoned = 0; - if (obj->oclass == WEAPON_CLASS || obj->oclass == TOOL_CLASS) { - obj->quan = (long) trop->trquan; - trop->trquan = 1; - } else if (obj->oclass == GEM_CLASS && is_graystone(obj) - && obj->otyp != FLINT) { - obj->quan = 1L; - } - if (trop->trspe != UNDEF_SPE) - obj->spe = trop->trspe; - if (trop->trbless != UNDEF_BLESS) - obj->blessed = trop->trbless; - } - /* defined after setting otyp+quan + blessedness */ - obj->owt = weight(obj); + ini_inv_adjust_obj(trop, obj); obj = addinv(obj); - /* Make the type known if necessary */ - if (OBJ_DESCR(objects[otyp]) && obj->known) - discover_object(otyp, TRUE, FALSE); - if (otyp == OIL_LAMP) - discover_object(POT_OIL, TRUE, FALSE); - - if (obj->oclass == ARMOR_CLASS) { - if (is_shield(obj) && !uarms && !(uwep && bimanual(uwep))) { - setworn(obj, W_ARMS); - /* Prior to 3.6.2 this used to unset uswapwep if it was set, - but wearing a shield doesn't prevent having an alternate - weapon ready to swap with the primary; just make sure we - aren't two-weaponing (academic; no one starts that way) */ - set_twoweap(FALSE); /* u.twoweap = FALSE */ - } else if (is_helmet(obj) && !uarmh) - setworn(obj, W_ARMH); - else if (is_gloves(obj) && !uarmg) - setworn(obj, W_ARMG); - else if (is_shirt(obj) && !uarmu) - setworn(obj, W_ARMU); - else if (is_cloak(obj) && !uarmc) - setworn(obj, W_ARMC); - else if (is_boots(obj) && !uarmf) - setworn(obj, W_ARMF); - else if (is_suit(obj) && !uarm) - setworn(obj, W_ARM); - } - - if (obj->oclass == WEAPON_CLASS || is_weptool(obj) - || otyp == TIN_OPENER || otyp == FLINT || otyp == ROCK) { - if (is_ammo(obj) || is_missile(obj)) { - if (!uquiver) - setuqwep(obj); - } else if (!uwep && (!uarms || !bimanual(obj))) { - setuwep(obj); - } else if (!uswapwep) { - setuswapwep(obj); - } - } - if (obj->oclass == SPBOOK_CLASS && obj->otyp != SPE_BLANK_PAPER) - initialspell(obj); + ini_inv_use_obj(obj); /* First spellbook should be level 1 - did we get it? */ if (obj->oclass == SPBOOK_CLASS && objects[obj->otyp].oc_level == 1) @@ -1247,4 +1392,18 @@ ini_inv(struct trobj *trop) } } +#undef UNDEF_TYP +#undef UNDEF_SPE +#undef UNDEF_BLESS +#undef B_MAJOR +#undef B_MINOR +#undef C_AMMO +#undef M_BOOK +#undef RAN_BOW +#undef RAN_TWO_ARROWS +#undef RAN_ZERO_ARROWS +#undef R_DAGGERS +#undef S_ARROWS +#undef T_DARTS + /*u_init.c*/ diff --git a/src/uhitm.c b/src/uhitm.c index d3c35cf230..3267d433a1 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 uhitm.c $NHDT-Date: 1665130027 2022/10/07 08:07:07 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.366 $ */ +/* NetHack 3.7 uhitm.c $NHDT-Date: 1736575153 2025/01/10 21:59:13 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.461 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -8,59 +8,69 @@ static const char brief_feeling[] = "have a %s feeling for a moment, then it passes."; -static boolean attack_check_conducts(struct obj *); -static boolean mhitm_mgc_atk_negated(struct monst *, struct monst *, boolean); -static boolean known_hitum(struct monst *, struct obj *, int *, int, int, - struct attack *, int); -static boolean theft_petrifies(struct obj *); -static void steal_it(struct monst *, struct attack *); -static int really_steal(struct obj *, struct monst *); -static void mhitm_really_poison(struct monst *, struct attack *, - struct monst *, struct mhitm_data *); -static void mhitm_ad_slow_core(struct monst *, struct monst *); -static boolean should_cleave(void); -static boolean hitum_cleave(struct monst *, struct attack *); -static boolean hitum(struct monst *, struct attack *); -static void hmon_hitmon_barehands(struct _hitmon_data *, struct monst *); -static void hmon_hitmon_weapon_ranged(struct _hitmon_data *, struct monst *, - struct obj *); -static void hmon_hitmon_weapon_melee(struct _hitmon_data *, struct monst *, - struct obj *); -static void hmon_hitmon_weapon(struct _hitmon_data *, struct monst *, - struct obj *); -static void hmon_hitmon_potion(struct _hitmon_data *, struct monst *, - struct obj *); -static void hmon_hitmon_misc_obj(struct _hitmon_data *, struct monst *, - struct obj *); -static void hmon_hitmon_do_hit(struct _hitmon_data *, struct monst *, - struct obj *); -static void hmon_hitmon_dmg_recalc(struct _hitmon_data *, struct obj *); -static void hmon_hitmon_poison(struct _hitmon_data *, struct monst *, - struct obj *); -static void hmon_hitmon_jousting(struct _hitmon_data *, struct monst *, - struct obj *); -static void hmon_hitmon_stagger(struct _hitmon_data *, struct monst *, - struct obj *); -static void hmon_hitmon_pet(struct _hitmon_data *, struct monst *, - struct obj *); -static void hmon_hitmon_splitmon(struct _hitmon_data *, struct monst *, - struct obj *); -static void hmon_hitmon_msg_hit(struct _hitmon_data *, struct monst *, - struct obj *); -static void hmon_hitmon_msg_lightobj(struct _hitmon_data *, struct monst *, - struct obj *); -static boolean hmon_hitmon(struct monst *, struct obj *, int, int); -static int joust(struct monst *, struct obj *); -static void demonpet(void); -static boolean m_slips_free(struct monst *, struct attack *); -static void start_engulf(struct monst *); -static void end_engulf(void); -static int gulpum(struct monst *, struct attack *); -static boolean hmonas(struct monst *); -static void nohandglow(struct monst *); -static boolean mhurtle_to_doom(struct monst *, int, struct permonst **); -static void first_weapon_hit(struct obj *); -static boolean shade_aware(struct obj *); +staticfn boolean mhitm_mgc_atk_negated(struct monst *, struct monst *, + boolean) NONNULLPTRS; +staticfn boolean attack_check_conducts(struct obj *); +staticfn boolean known_hitum(struct monst *, struct obj *, int *, int, int, + struct attack *, int) NONNULLARG13; +staticfn boolean theft_petrifies(struct obj *) NONNULLARG1; +staticfn int really_steal(struct obj *, struct monst *); +staticfn void mhitm_really_poison(struct monst *, struct attack *, + struct monst *, struct mhitm_data *); +staticfn void mhitm_ad_slow_core(struct monst *, struct monst *); +staticfn boolean should_cleave(void); +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 + * is a test for 'target' being null */ +staticfn boolean hitum_cleave(struct monst *, struct attack *) NO_NNARGS; +staticfn boolean double_punch(void); +staticfn boolean hitum(struct monst *, struct attack *) NONNULLARG1; +staticfn void hmon_hitmon_barehands(struct _hitmon_data *, + struct monst *) NONNULLARG12; +staticfn void hmon_hitmon_weapon_ranged(struct _hitmon_data *, struct monst *, + struct obj *) NONNULLARG123; +staticfn void hmon_hitmon_weapon_melee(struct _hitmon_data *, struct monst *, + struct obj *) NONNULLARG123; +staticfn void hmon_hitmon_weapon(struct _hitmon_data *, struct monst *, + struct obj *) NONNULLARG123; +staticfn void hmon_hitmon_potion(struct _hitmon_data *, struct monst *, + struct obj *) NONNULLARG123; +staticfn void hmon_hitmon_misc_obj(struct _hitmon_data *, struct monst *, + struct obj *) NONNULLARG123; +staticfn void hmon_hitmon_do_hit(struct _hitmon_data *, struct monst *, + struct obj *) NONNULLARG12; +staticfn void hmon_hitmon_dmg_recalc(struct _hitmon_data *, struct obj *); +staticfn void hmon_hitmon_poison(struct _hitmon_data *, struct monst *, + struct obj *) NONNULLARG123; +staticfn void hmon_hitmon_jousting(struct _hitmon_data *, struct monst *, + struct obj *) NONNULLARG123; +staticfn void hmon_hitmon_stagger(struct _hitmon_data *, struct monst *, + struct obj *) NONNULLARG12; +staticfn void hmon_hitmon_pet(struct _hitmon_data *, struct monst *, + struct obj *) NONNULLARG12; +staticfn void hmon_hitmon_splitmon(struct _hitmon_data *, struct monst *, + struct obj *) NONNULLARG12; +staticfn void hmon_hitmon_msg_hit(struct _hitmon_data *, struct monst *, + struct obj *) NONNULLARG12; +staticfn void hmon_hitmon_msg_silver(struct _hitmon_data *, struct monst *, + struct obj *) NONNULLARG12; +staticfn void hmon_hitmon_msg_lightobj(struct _hitmon_data *, struct monst *, + struct obj *) NONNULLARG12; +staticfn boolean hmon_hitmon(struct monst *, struct obj *, int, int) + NONNULLARG1; +staticfn int joust(struct monst *, struct obj *) NONNULLARG12; +staticfn void demonpet(void); +staticfn boolean m_slips_free(struct monst *, struct attack *) NONNULLPTRS; +staticfn void start_engulf(struct monst *) NONNULLARG1; +staticfn void end_engulf(void); +staticfn int gulpum(struct monst *, struct attack *) NONNULLPTRS; +staticfn boolean hmonas(struct monst *) NONNULLARG1; +staticfn void nohandglow(struct monst *) NONNULLARG1; +staticfn boolean mhurtle_to_doom(struct monst *, int, + struct permonst **) NONNULLARG13; +staticfn void first_weapon_hit(struct obj *) NONNULLARG1; +staticfn boolean shade_aware(struct obj *) NO_NNARGS; #define PROJECTILE(obj) ((obj) && is_ammo(obj)) @@ -71,7 +81,7 @@ enum really_steal_returns { STEAL_ABORT = 2 /* something made us stop stealing (petrification) */ }; -static boolean +staticfn boolean mhitm_mgc_atk_negated( struct monst *magr, struct monst *mdef, boolean verbosely) /* give mesg if magical cancellation prevents damage */ @@ -91,7 +101,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; } @@ -198,7 +208,7 @@ attack_checks( if (engulfing_u(mtmp)) return attack_check_conducts(wep); - if (gc.context.forcefight) { + if (svc.context.forcefight) { /* Do this in the caller, after we checked that the monster * didn't die from the blow. Reason: putting the 'I' there * causes the hero to forget the square's contents since @@ -238,7 +248,7 @@ attack_checks( if (M_AP_TYPE(mtmp) && !Protection_from_shape_changers) { if (!u.ustuck && !mtmp->mflee && dmgtype(mtmp->data, AD_STCK) /* applied pole-arm attack is too far to get stuck */ - && next2u(mtmp->mx, mtmp->my)) + && m_next2u(mtmp)) set_ustuck(mtmp); } /* #H7329 - if hero is on engraved "Elbereth", this will end up @@ -290,7 +300,7 @@ attack_checks( else if (Blind || (is_pool(mtmp->mx, mtmp->my) && !Underwater)) pline("Wait! There's a hidden monster there!"); else if (concealed_spot(mtmp->mx, mtmp->my)) { - obj = gl.level.objects[mtmp->mx][mtmp->my]; + obj = svl.level.objects[mtmp->mx][mtmp->my]; pline("Wait! There's %s hiding under %s%s!", an(l_monnam(mtmp)), obj ? "" : "the ", obj ? doname(obj) : explain_terrain(mtmp->mx, mtmp->my)); @@ -324,7 +334,7 @@ attack_checks( Sprintf(qbuf, "Really attack %s?", mon_nam(mtmp)); if (!paranoid_query(ParanoidHit, qbuf)) { - gc.context.move = 0; + svc.context.move = 0; return TRUE; } } @@ -342,7 +352,7 @@ attack_checks( * FALSE means OK to attack. This also sets context.move to 0 as a side effect * (so that it takes no time) if the player elects not to attack. */ -static boolean +staticfn boolean attack_check_conducts(struct obj* wep) { /* Weaponless: about to break weaponless conduct as a non-beginner @@ -350,7 +360,7 @@ attack_check_conducts(struct obj* wep) if (wep && (wep->oclass == WEAPON_CLASS || is_weptool(wep)) && !u.uconduct.weaphit && !flags.beginner) { if (!paranoid_query(TRUE, "Break weaponless conduct?")) { - gc.context.move = 0; + svc.context.move = 0; return TRUE; } } @@ -367,11 +377,11 @@ attack_check_conducts(struct obj* wep) * prompt before it attacks anything, and of course the chance of it * entering "Yes" randomly is extremely low */ said_yes_to_attack = TRUE; - if (!said_yes_to_attack && !u.uconduct.killer && !gc.context.forcefight - && gm.moves > 100) { + if (!said_yes_to_attack && !u.uconduct.killer && !svc.context.forcefight + && svm.moves > 100) { pline("Killing a monster violates pacifist conduct."); if (!paranoid_query(TRUE, "Attack anyway?")) { - gc.context.move = 0; + svc.context.move = 0; return TRUE; } said_yes_to_attack = TRUE; /* don't ask again */ @@ -380,9 +390,7 @@ attack_check_conducts(struct obj* wep) return FALSE; } -/* - * It is unchivalrous for a knight to attack the defenseless or from behind. - */ +/* it is unchivalrous for a knight to attack the defenseless or from behind */ void check_caitiff(struct monst *mtmp) { @@ -495,12 +503,12 @@ force_attack(struct monst *mtmp, boolean pets_too) { boolean attacked, save_Forcefight; - save_Forcefight = gc.context.forcefight; + save_Forcefight = svc.context.forcefight; /* always set forcefight On for hostiles and peacefuls, maybe for pets */ if (pets_too || !mtmp->mtame) - gc.context.forcefight = TRUE; + svc.context.forcefight = TRUE; attacked = do_attack(mtmp); - gc.context.forcefight = save_Forcefight; + svc.context.forcefight = save_Forcefight; return attacked; } @@ -521,7 +529,7 @@ do_attack(struct monst *mtmp) * you'll usually just swap places if this is a movement command */ /* Intelligent chaotic weapons (Stormbringer) want blood */ - if (is_safemon(mtmp) && !gc.context.forcefight) { + if (is_safemon(mtmp) && !svc.context.forcefight) { if (!u_wield_art(ART_STORMBRINGER)) { /* There are some additional considerations: this won't work * if in a shop or Punished or you miss a random roll or @@ -535,7 +543,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; @@ -543,7 +551,7 @@ do_attack(struct monst *mtmp) /* only check for in-shop if don't already have reason to stop */ if (!foo) { for (p = in_rooms(mtmp->mx, mtmp->my, SHOPBASE); *p; p++) - if (tended_shop(&gr.rooms[*p - ROOMOFFSET])) { + if (tended_shop(&svr.rooms[*p - ROOMOFFSET])) { inshop = TRUE; break; } @@ -551,6 +559,10 @@ do_attack(struct monst *mtmp) if (inshop || foo) { char buf[BUFSZ]; + if (!svc.context.travel && !svc.context.run) + if (canspotmon(mtmp) && mtmp->isshk) + return ECMD_TIME | dopay(); + if (mtmp->mtame) /* see 'additional considerations' above */ monflee(mtmp, rnd(6), FALSE, FALSE); Strcpy(buf, y_monnam(mtmp)); @@ -596,7 +608,7 @@ do_attack(struct monst *mtmp) if (gu.unweapon) { gu.unweapon = FALSE; - if (Verbose(4, do_attack)) { + if (flags.verbose) { if (uwep) You("begin bashing monsters with %s.", yname(uwep)); else if (!cantwield(gy.youmonst.data)) @@ -632,7 +644,7 @@ do_attack(struct monst *mtmp) * and it returned 0 (it's okay to attack), and the monster didn't * evade. */ - if (gc.context.forcefight && !DEADMONSTER(mtmp) && !canspotmon(mtmp) + if (svc.context.forcefight && !DEADMONSTER(mtmp) && !canspotmon(mtmp) && !glyph_is_invisible(levl[u.ux + u.dx][u.uy + u.dy].glyph) /* thiefstone may have teleported target */ && (m_at(u.ux + u.dx, u.uy + u.dy) == mtmp @@ -644,7 +656,7 @@ do_attack(struct monst *mtmp) } /* really hit target monster; returns TRUE if it still lives */ -static boolean +staticfn boolean known_hitum( struct monst *mon, /* target */ struct obj *weapon, /* uwep or uswapwep */ @@ -663,7 +675,7 @@ known_hitum( if (go.override_confirmation) { /* this may need to be generalized if weapons other than Stormbringer acquire similar anti-social behavior... */ - if (Verbose(4, known_hitum)) + if (flags.verbose) Your("bloodthirsty blade attacks!"); } @@ -709,7 +721,7 @@ known_hitum( /* return TRUE iff no peaceful targets are found in cleaving range to the left * and right of the target space * assumes u.dx and u.dy have been set */ -static boolean +staticfn boolean should_cleave(void) { int i; @@ -737,7 +749,7 @@ should_cleave(void) } } if (bystanders) { - if (!gc.context.forcefight) + if (!svc.context.forcefight) return FALSE; /* if (ParanoidHit) { @@ -753,7 +765,7 @@ should_cleave(void) /* hit the monster next to you and the monsters to the left and right of it; return False if the primary target is killed, True otherwise */ -static boolean +staticfn boolean hitum_cleave( struct monst *target, /* non-Null; forcefight at nothing doesn't cleave +*/ struct attack *uattk) /*+ but we don't enforce that here; Null works ok */ @@ -836,21 +848,38 @@ hitum_cleave( return (target && DEADMONSTER(target)) ? FALSE : TRUE; } +/* returns True if hero is fighting without a weapon and without a shield and + has sufficient skill in bare-handed/martial arts to attack twice */ +staticfn boolean +double_punch(void) +{ + /* note: P_BARE_HANDED_COMBAT and P_MARTIAL_ARTS are equivalent */ + int skl_lvl = P_SKILL(P_BARE_HANDED_COMBAT); + + /* + * Chance to attempt a second bare-handed or martial arts hit: + * restricted (0), [not applicable; no one is restricted] + * unskilled (1) : 0% + * basic (2) : 0% + * skilled (3) : 20% + * expert (4) : 40% + * master (5) : 60% + * grandmaster (6) : 80% + */ + if (!uwep && !uarms && skl_lvl > P_BASIC) + return (skl_lvl - P_BASIC) > rn2(5); + return FALSE; +} + /* hit target monster; returns TRUE if it still lives */ -static boolean +staticfn boolean hitum(struct monst *mon, struct attack *uattk) { boolean malive, wep_was_destroyed = FALSE; - struct obj *wepbefore = uwep; - int armorpenalty, attknum = 0, - x = u.ux + u.dx, y = u.uy + u.dy, - oldumort = u.umortality, - tmp = find_roll_to_hit(mon, uattk->aatyp, uwep, - &attknum, &armorpenalty), - dieroll = rnd(20), - mhit = (tmp > dieroll || u.uswallow); - - mon_maybe_unparalyze(mon); + struct obj *wepbefore = uwep, + *secondwep = u.twoweap ? uswapwep : (struct obj *) 0; + int tmp, dieroll, mhit, armorpenalty, attknum = 0, + x = u.ux + u.dx, y = u.uy + u.dy, oldumort = u.umortality; /* Cleaver attacks three spots, 'mon' and one on either side of 'mon'; it can't be part of dual-wielding but we guard against that anyway; @@ -859,33 +888,47 @@ hitum(struct monst *mon, struct attack *uattk) && !u.uswallow && !u.ustuck && !NODIAG(u.umonnum) && should_cleave()) return hitum_cleave(mon, uattk); + /* 0: single hit, 1: first of two hits; affects strength bonus and + silver rings; known_hitum() -> hmon() -> hmon_hitmon() will copy + gt.twohits into struct _hitmon_data hmd.twohits */ + gt.twohits = (uwep ? u.twoweap : double_punch()) ? 1 : 0; + + tmp = find_roll_to_hit(mon, uattk->aatyp, uwep, &attknum, &armorpenalty); + mon_maybe_unparalyze(mon); + dieroll = rnd(20); + mhit = (tmp > dieroll || u.uswallow); if (tmp > dieroll) exercise(A_DEX, TRUE); + /* gb.bhitpos is set up by caller */ malive = known_hitum(mon, uwep, &mhit, tmp, armorpenalty, uattk, dieroll); if (wepbefore && !uwep) wep_was_destroyed = TRUE; (void) passive(mon, uwep, mhit, malive, AT_WEAP, wep_was_destroyed); - /* second attack for two-weapon combat; won't occur if Stormbringer - overrode confirmation (assumes Stormbringer is primary weapon), - or if hero became paralyzed by passive counter-attack, or if hero - was killed by passive counter-attack and got life-saved, or if - monster was killed or knocked to different location */ - if (u.twoweap && !(go.override_confirmation - || gm.multi < 0 || u.umortality > oldumort - || !malive || m_at(x, y) != mon)) { + /* second attack for two-weapon combat or skilled unarmed combat; + won't occur if Stormbringer overrode confirmation (assumes + Stormbringer is primary weapon), or if hero became paralyzed by + passive counter-attack, or if hero was killed by passive + counter-attack and got life-saved, or if monster was killed or + knocked to different location */ + if (gt.twohits && !(go.override_confirmation + || gm.multi < 0 || u.umortality > oldumort + || !malive || m_at(x, y) != mon)) { + gt.twohits = 2; /* second of 2 hits */ tmp = find_roll_to_hit(mon, uattk->aatyp, uswapwep, &attknum, &armorpenalty); mon_maybe_unparalyze(mon); dieroll = rnd(20); mhit = (tmp > dieroll || u.uswallow); - malive = known_hitum(mon, uswapwep, &mhit, tmp, armorpenalty, uattk, + malive = known_hitum(mon, secondwep, &mhit, tmp, armorpenalty, uattk, dieroll); /* second passive counter-attack only occurs if second attack hits */ if (mhit) - (void) passive(mon, uswapwep, mhit, malive, AT_WEAP, !uswapwep); + (void) passive(mon, secondwep, mhit, malive, AT_WEAP, + secondwep && !uswapwep); } + gt.twohits = 0; return malive; } @@ -909,14 +952,15 @@ hmon(struct monst *mon, } /* hero hits monster bare handed */ -static void +staticfn void hmon_hitmon_barehands(struct _hitmon_data *hmd, struct monst *mon) { struct obj *mwep; + long ringmask; if (noncorporeal(hmd->mdat)) { hmd->dmg = 0; } else { - /* note: 1..2 or 1..4 can be substantiallly increased by + /* note: 1..2 or 1..4 can be substantially increased by strength bonus or skill bonus, usually both... */ hmd->dmg = rnd(!martial_bonus() ? 2 : 4); hmd->use_weapon_skill = TRUE; @@ -924,10 +968,28 @@ hmon_hitmon_barehands(struct _hitmon_data *hmd, struct monst *mon) } /* Blessed gloves give bonuses when fighting 'bare-handed'. So do - * rings or gloves made of a hated material. Note: rings are worn - * under gloves, so you don't get both bonuses, and two hated rings - * don't give double bonus. */ - hmd->dmg += special_dmgval(&gy.youmonst, mon, (W_ARMG | W_RINGL | W_RINGR), + * rings or gloves made of a hated material. + * Note: rings are worn under gloves, so you don't get both bonuses, and + * two silver rings don't give double bonus. When making only one hit, both + * rings are checked (backwards compatibility => playability), but when + * making two hits, only the ring on the hand making the attack is checked. + */ + switch (hmd->twohits) { + case 0: /* only one hit being attempted; a hated ring on either hand + * applies but having hated rings on both is same as just one */ + ringmask = (W_RINGL | W_RINGR); + break; + case 1: /* first of two or more hit attempts; right ring applies */ + ringmask = W_RINGR; + break; + case 2: /* second of two or more hit attempts; left ring applies */ + ringmask = W_RINGL; + break; + default: /* third or later of more than two hit attempts (poly'd hero); + * rings were applied on first and second hits */ + break; + } + hmd->dmg += special_dmgval(&gy.youmonst, mon, (W_ARMG | ringmask), &hmd->hated_obj); /* critical hits at sufficiently high skill may disarm */ @@ -961,11 +1023,11 @@ hmon_hitmon_barehands(struct _hitmon_data *hmd, struct monst *mon) } } -static void +staticfn void hmon_hitmon_weapon_ranged( struct _hitmon_data *hmd, struct monst *mon, - struct obj *obj) + struct obj *obj) /* obj is not NULL */ { /* then do only 1-2 points of damage and don't use or train weapon's skill */ @@ -1001,11 +1063,11 @@ hmon_hitmon_weapon_ranged( } } -static void +staticfn void hmon_hitmon_weapon_melee( struct _hitmon_data *hmd, struct monst *mon, - struct obj *obj) + struct obj *obj) /* obj is not NULL */ { int wtype; struct obj *monwep; @@ -1122,6 +1184,8 @@ hmon_hitmon_weapon_melee( 50 + 15 * (greatest_erosion(obj) - greatest_erosion(monwep)), 100))) { + static const char from_your_blow[] = " from the force of your blow!"; + char buf[BUFSZ]; /* * 2.5% chance of shattering defender's weapon when * using a two-handed weapon; less if uwep is rusted. @@ -1134,8 +1198,20 @@ hmon_hitmon_weapon_melee( */ setmnotwielded(mon, monwep); mon->weapon_check = NEED_WEAPON; - pline("%s from the force of your blow!", - Yobjnam2(monwep, "shatter")); + if (canseemon(mon)) + /* Yobjnam2(X,"shatter") yields "Shk's X shatters" if X is owned + by a shop or "Mon's X shatters" if X is carried by a monster + (or "{Your|The} X shatters" if {carried by hero|last resort})*/ + Strcpy(buf, Yobjnam2(monwep, "shatter")); + else /* hero is blind or can't see invisible mon */ + /* construct "Its weapon shatters"; not an exact replacement + for Yobjnam2() if an unseen mon other than the shopkeeper + is wielding a shop-owned weapon; telepathy or extended + monster detection will name mon but not its weapon */ + Sprintf(buf, "%s weapon%s %s", s_suffix(Monnam(mon)), + plur(monwep->quan), otense(monwep, "shatter")); + buf[sizeof buf - sizeof from_your_blow] = '\0'; + pline("%s%s", buf, from_your_blow); m_useupall(mon, monwep); /* If someone just shattered MY weapon, I'd flee! */ if (rn2(4)) { @@ -1225,11 +1301,11 @@ hmon_hitmon_weapon_melee( } } -static void +staticfn void hmon_hitmon_weapon( struct _hitmon_data *hmd, struct monst *mon, - struct obj *obj) + struct obj *obj) /* obj is not NULL */ { /* is it not a melee weapon? */ if (/* if you strike with a bow... */ @@ -1249,11 +1325,11 @@ hmon_hitmon_weapon( } } -static void +staticfn void hmon_hitmon_potion( struct _hitmon_data *hmd, struct monst *mon, - struct obj *obj) + struct obj *obj) /* obj is not NULL */ { if (obj->quan > 1L) obj = splitobj(obj, 1L); @@ -1273,11 +1349,11 @@ hmon_hitmon_potion( hmd->dmg = (noncorporeal(hmd->mdat)) ? 0 : 1; } -static void +staticfn void hmon_hitmon_misc_obj( struct _hitmon_data *hmd, struct monst *mon, - struct obj *obj) + struct obj *obj) /* obj is not NULL */ { switch (obj->otyp) { case BOULDER: /* 1d20 */ @@ -1333,7 +1409,7 @@ hmon_hitmon_misc_obj( ; /* maybe turn the corpse into a statue? */ #endif } - hmd->dmg = (obj->corpsenm >= LOW_PM ? mons[obj->corpsenm].msize + hmd->dmg = (ismnum(obj->corpsenm) ? mons[obj->corpsenm].msize : 0) + 1; break; @@ -1355,14 +1431,15 @@ hmon_hitmon_misc_obj( hand-to-hand attack should yield a "bashing" mesg */ if (obj == uwep) gu.unweapon = TRUE; - if (obj->spe && obj->corpsenm >= LOW_PM) { + if (obj->spe && ismnum(obj->corpsenm)) { if (obj->quan < 5L) change_luck((schar) - (obj->quan)); else change_luck(-5); } - if (touch_petrifies(&mons[obj->corpsenm])) { + if (ismnum(obj->corpsenm) + && touch_petrifies(&mons[obj->corpsenm])) { /*learn_egg_type(obj->corpsenm);*/ pline("Splat! You hit %s with %s %s egg%s!", mon_nam(mon), @@ -1381,10 +1458,11 @@ hmon_hitmon_misc_obj( return; /*return (boolean) (!DEADMONSTER(mon));*/ } else { /* ordinary egg(s) */ - const char *eggp = (obj->corpsenm != NON_PM - && obj->known) - ? the(mons[obj->corpsenm].pmnames[NEUTRAL]) - : (cnt > 1L) ? "some" : "an"; + enum monnums mnum = obj->corpsenm; + const char *eggp = + (ismnum(mnum) && obj->known) + ? the(mons[mnum].pmnames[NEUTRAL]) + : (cnt > 1L) ? "some" : "an"; if (strstri(pmname(mon->data, Mgender(mon)), "devil")) pline("Deviled egg%s!", plur(cnt)); @@ -1404,6 +1482,12 @@ hmon_hitmon_misc_obj( obj->owt = weight(obj); if (hmd->thrown) place_object(obj, mon->mx, mon->my); + } else if (obj->corpsenm == PM_PYROLISK) { + useup_eggs(obj); + explode(mon->mx, mon->my, -11, d(3, 6), 0, EXPL_FIERY); + hmd->doreturn = TRUE; + hmd->retval = !DEADMONSTER(mon); + return; } else { pline("Splat!"); useup_eggs(obj); @@ -1530,11 +1614,11 @@ hmon_hitmon_misc_obj( } /* do the actual hitting monster with obj/fists */ -static void +staticfn void hmon_hitmon_do_hit( struct _hitmon_data *hmd, struct monst *mon, - struct obj *obj) + struct obj *obj) /* obj can be NULL */ { if (!(is_silent(gy.youmonst.data) && helpless(mon)) && hmd->hand_to_hand && rn2(Stealth ? 10 : 5)) { @@ -1546,9 +1630,12 @@ hmon_hitmon_do_hit( if (!obj) { /* attack with bare hands */ hmon_hitmon_barehands(hmd, mon); } else { + /* obj is not NULL here because of the !obj check in this if block, + , so no guard is needed ahead of stone_missile(obj) */ /* stone missile does not hurt xorn or earth elemental, but doesn't pass all the way through and continue on to some further target */ - if ((hmd->thrown == HMON_THROWN || hmd->thrown == HMON_KICKED) /* not Applied */ + if ((hmd->thrown == HMON_THROWN + || hmd->thrown == HMON_KICKED) /* not Applied */ && stone_missile(obj) && passes_rocks(hmd->mdat)) { hit(mshot_xname(obj), mon, " but does no harm."); wakeup(mon, TRUE, TRUE); @@ -1583,23 +1670,43 @@ hmon_hitmon_do_hit( } } -static void +staticfn void hmon_hitmon_dmg_recalc(struct _hitmon_data *hmd, struct obj *obj) { - int dmgbonus = 0; + int dmgbonus = 0, strbonus, absbonus; /* * Potential bonus (or penalty) from worn ring of increase damage - * (or intrinsic bonus from eating same) or from strength. + * (or intrinsic bonus from eating same) or from strength. Strength + * bonus is increased for melee with two-handed weapons and decreased + * for dual attacks (but when both hit, the total for the two is more + * than the bonus for a regular single hit). */ if (hmd->get_dmg_bonus) { + /* for dual attacks, udaminc applies to both, and two-handed + weapons use it as-is */ dmgbonus = u.udaminc; /* throwing using a propellor gets an increase-damage bonus - but not a strength one; other attacks get both */ + but not a strength one; other attacks get both; + for dual attacks, 3/4 of the strength bonus is used; when + both attacks hit, overall bonus is 3/2 rather than doubled; + melee hit with two-handed weapon uses 3/2 strength bonus to + approximately match double hit with two-weapon ('approximate' + because udaminc skews in favor of two-weapon); the 3/2 factor + for two-handed strength does not apply to polearms unless + hero is simply bashing with one of those and does not apply + to jousting because lances are one-handed */ if (hmd->thrown != HMON_THROWN || !obj || !uwep || !ammo_and_launcher(obj, uwep) - || uslinging()) - dmgbonus += dbon(); + || uslinging()) { + strbonus = dbon(); + absbonus = abs(strbonus); + if (hmd->twohits) + strbonus = ((3 * absbonus + 2) / 4) * sgn(strbonus); + else if (hmd->thrown == HMON_MELEE && uwep && bimanual(uwep)) + strbonus = ((3 * absbonus + 1) / 2) * sgn(strbonus); + dmgbonus += strbonus; + } } /* @@ -1638,11 +1745,11 @@ hmon_hitmon_dmg_recalc(struct _hitmon_data *hmd, struct obj *obj) hmd->dmg = 1; } -static void +staticfn void hmon_hitmon_poison( struct _hitmon_data *hmd, struct monst *mon, - struct obj *obj) + struct obj *obj) /* obj is not NULL */ { int nopoison = (10 - (obj->owt / 10)); @@ -1667,11 +1774,11 @@ hmon_hitmon_poison( hmd->dmg += (hmd->thrown == HMON_THROWN ? rnd(6) : rn1(10, 6)); } -static void +staticfn void hmon_hitmon_jousting( struct _hitmon_data *hmd, struct monst *mon, /* target */ - struct obj *obj) /* lance */ + struct obj *obj) /* lance; obj is not NULL */ { hmd->dmg += d(2, (obj == uwep) ? 10 : 2); /* [was in dmgval()] */ You("joust %s%s", mon_nam(mon), canseemon(mon) ? exclam(hmd->dmg) : "."); @@ -1696,7 +1803,7 @@ hmon_hitmon_jousting( hmd->hittxt = TRUE; } -static void +staticfn void hmon_hitmon_stagger( struct _hitmon_data *hmd, struct monst *mon, @@ -1714,7 +1821,7 @@ hmon_hitmon_stagger( } } -static void +staticfn void hmon_hitmon_pet( struct _hitmon_data *hmd, struct monst *mon, @@ -1730,11 +1837,11 @@ hmon_hitmon_pet( } } -static void +staticfn void hmon_hitmon_splitmon( struct _hitmon_data *hmd, struct monst *mon, - struct obj *obj) + struct obj *obj) /* obj can be NULL but guards are in place below */ { if ((hmd->mdat == &mons[PM_BLACK_PUDDING] || hmd->mdat == &mons[PM_BROWN_PUDDING] @@ -1756,7 +1863,7 @@ hmon_hitmon_splitmon( if ((mclone = clone_mon(mon, 0, 0)) != 0) { if (canspotmon(mclone) && !u.uswallow) { withwhat[0] = '\0'; - if (u.twoweap && Verbose(4, hmon_hitmon_splitmon)) + if (u.twoweap && flags.verbose) Sprintf(withwhat, " with %s", yname(obj)); pline("%s divides as you hit it%s!", Monnam(mon), withwhat); @@ -1767,18 +1874,19 @@ hmon_hitmon_splitmon( } } -static void +staticfn void hmon_hitmon_msg_hit( struct _hitmon_data *hmd, struct monst *mon, - struct obj *obj) + struct obj *obj) /* obj can be NULL for hand_to_hand; otherwise not */ { if (!hmd->hittxt /*( thrown => obj exists )*/ && (!hmd->destroyed - || (hmd->thrown && gm.m_shot.n > 1 && gm.m_shot.o == obj->otyp))) { + || (hmd->thrown && gm.m_shot.n > 1 + && gm.m_shot.o == obj->otyp))) { if (hmd->thrown) hit(mshot_xname(obj), mon, exclam(hmd->dmg)); - else if (!Verbose(4, hmon_hitmon2)) + else if (!flags.verbose) You("hit it."); else { const char *verb = !obj ? barehitmsg(&gy.youmonst) @@ -1794,7 +1902,7 @@ hmon_hitmon_msg_hit( } } -static void +staticfn void hmon_hitmon_msg_lightobj( struct _hitmon_data *hmd, struct monst *mon, @@ -1825,8 +1933,28 @@ hmon_hitmon_msg_lightobj( RESTORE_WARNING_FORMAT_NONLITERAL } +/* + * These will segfault if passed a NULL obj pointer: + * hmon_hitmon_weapon_ranged, + * hmon_hitmon_weapon_melee, + * hmon_hitmon_weapon, + * hmon_hitmon_potion, + * hmon_hitmon_misc_obj, + * hmon_hitmon_poison, + * hmon_hitmon_jousting, + * + * These are equipped to handle a NULL obj pointer: + * hmon_hitmon_stagger, - obj arg is unused + * hmon_hitmon_pet, - obj arg is unused + * hmon_hitmon_msg_silver, - obj arg is unused + * hmon_hitmon_msg_lightobj, - obj arg is unused + * hmon_hitmon_do_hit, - has obj and !obj code paths + * hmon_hitmon_splitmon, - has !obj guards + * hmon_hitmon_msg_hit, - has !obj guards exc. thrown which is ok + */ + /* guts of hmon(); returns True if 'mon' survives */ -static boolean +staticfn boolean hmon_hitmon( struct monst *mon, struct obj *obj, @@ -1838,6 +1966,7 @@ hmon_hitmon( hmd.dmg = 0; hmd.thrown = thrown; + hmd.twohits = thrown ? 0 : gt.twohits; hmd.dieroll = dieroll; hmd.mdat = mon->data; hmd.use_weapon_skill = FALSE; @@ -1981,7 +2110,7 @@ hmon_hitmon( /* joust or martial arts punch is knocking the target back; that might kill 'mon' (via trap) before known_hitum() has a chance to do so; return True if we kill mon, False otherwise */ -static boolean +staticfn boolean mhurtle_to_doom( struct monst *mon, /* target monster */ int tmp, /* amount of pending damage */ @@ -2002,7 +2131,7 @@ mhurtle_to_doom( /* gamelog version of "you've broken never-hit-with-wielded-weapon conduct; the conduct is tracked in known_hitum(); we're called by hmon_hitmon() */ -static void +staticfn void first_weapon_hit(struct obj *weapon) { char buf[BUFSZ]; @@ -2031,7 +2160,7 @@ first_weapon_hit(struct obj *weapon) "hit with a wielded weapon (%s) for the first time", buf); } -static boolean +staticfn boolean shade_aware(struct obj *obj) { if (!obj) @@ -2075,7 +2204,7 @@ shade_miss( if (verbose && ((youdef || cansee(mdef->mx, mdef->my) || sensemon(mdef)) - || (magr == &gy.youmonst && next2u(mdef->mx, mdef->my)))) { + || (magr == &gy.youmonst && m_next2u(mdef)))) { static const char harmlessly_thru[] = " harmlessly through "; what = (!obj ? "attack" : cxname(obj)); @@ -2098,7 +2227,7 @@ shade_miss( /* check whether slippery clothing protects from hug or wrap attack */ /* [currently assumes that you are the attacker] */ -static boolean +staticfn boolean m_slips_free(struct monst *mdef, struct attack *mattk) { struct obj *obj; @@ -2140,7 +2269,7 @@ m_slips_free(struct monst *mdef, struct attack *mattk) /* used when hitting a monster with a lance while mounted; 1: joust hit; 0: ordinary hit; -1: joust but break lance */ -static int +staticfn int joust(struct monst *mon, /* target */ struct obj *obj) /* weapon */ { @@ -2170,10 +2299,8 @@ joust(struct monst *mon, /* target */ return 0; /* no joust bonus; revert to ordinary attack */ } -/* - * Send in a demon pet for the hero. Exercise wisdom. - */ -static void +/* send in a demon pet for the hero; exercise wisdom */ +staticfn void demonpet(void) { int i; @@ -2184,11 +2311,11 @@ demonpet(void) i = !rn2(6) ? ndemon(u.ualign.type) : NON_PM; pm = i != NON_PM ? &mons[i] : gy.youmonst.data; if ((dtmp = makemon(pm, u.ux, u.uy, NO_MM_FLAGS)) != 0) - (void) tamedog(dtmp, (struct obj *) 0, FALSE); + (void) tamedog(dtmp, (struct obj *) 0, FALSE, FALSE); exercise(A_WIS, TRUE); } -static boolean +staticfn boolean theft_petrifies(struct obj *otmp) { if (uarmg || otmp->otyp != CORPSE @@ -2215,7 +2342,7 @@ theft_petrifies(struct obj *otmp) * If the target is wearing body armor, take all of its possessions; * otherwise, take one object. [Is this really the behavior we want?] */ -static void +staticfn void steal_it(struct monst *mdef, struct attack *mattk) { struct obj *otmp, *gold = 0, *ustealo, **minvent_ptr; @@ -2309,7 +2436,7 @@ steal_it(struct monst *mdef, struct attack *mattk) * Assumes caller handles whatever other messages are necessary; this takes care * of the "You steal e - an imaginary widget" message. * Returns one of the STEAL_* values. */ -static int +staticfn int really_steal(struct obj *obj, struct monst *mdef) { long unwornmask = obj->owornmask; @@ -2340,8 +2467,9 @@ really_steal(struct obj *obj, struct monst *mdef) } void -mhitm_ad_rust(struct monst *magr, struct attack *mattk, struct monst *mdef, - struct mhitm_data *mhm) +mhitm_ad_rust( + struct monst *magr, struct attack *mattk, + struct monst *mdef, struct mhitm_data *mhm) { struct permonst *pd = mdef->data; @@ -2366,8 +2494,8 @@ mhitm_ad_rust(struct monst *magr, struct attack *mattk, struct monst *mdef, if (completelyrusts(pd)) { You("rust!"); /* KMH -- this is okay with unchanging */ - Strcpy(gk.killer.name, "rusted away"); - gk.killer.format = NO_KILLER_PREFIX; + Strcpy(svk.killer.name, "rusted away"); + svk.killer.format = NO_KILLER_PREFIX; rehumanize(); return; } @@ -2378,7 +2506,7 @@ mhitm_ad_rust(struct monst *magr, struct attack *mattk, struct monst *mdef, 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)) { @@ -2398,8 +2526,9 @@ mhitm_ad_rust(struct monst *magr, struct attack *mattk, struct monst *mdef, } void -mhitm_ad_corr(struct monst *magr, struct attack *mattk, struct monst *mdef, - struct mhitm_data *mhm) +mhitm_ad_corr( + struct monst *magr, struct attack *mattk, + struct monst *mdef, struct mhitm_data *mhm) { if (magr == &gy.youmonst) { /* uhitm */ @@ -2422,8 +2551,9 @@ mhitm_ad_corr(struct monst *magr, struct attack *mattk, struct monst *mdef, } void -mhitm_ad_dcay(struct monst *magr, struct attack *mattk, struct monst *mdef, - struct mhitm_data *mhm) +mhitm_ad_dcay( + struct monst *magr, struct attack *mattk, + struct monst *mdef, struct mhitm_data *mhm) { struct permonst *pd = mdef->data; @@ -2444,8 +2574,8 @@ mhitm_ad_dcay(struct monst *magr, struct attack *mattk, struct monst *mdef, if (completelyrots(pd)) { You("rot!"); /* KMH -- this is okay with unchanging */ - strcpy(gk.killer.name, "rotted away"); - gk.killer.format = NO_KILLER_PREFIX; + strcpy(svk.killer.name, "rotted away"); + svk.killer.format = NO_KILLER_PREFIX; rehumanize(); return; } @@ -2458,7 +2588,7 @@ mhitm_ad_dcay(struct monst *magr, struct attack *mattk, struct monst *mdef, /* 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)) { @@ -2567,7 +2697,7 @@ mhitm_ad_drli( if (!is_death) mhm->damage = d(2, 6); /* Stormbringer uses monhp_per_lvl (1d8) */ 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 { @@ -2635,15 +2765,18 @@ mhitm_ad_fire( pline("You're %s!", on_fire(pd, mattk)); if (completelyburns(pd)) { /* paper or straw golem */ You("go up in flames!"); + monstunseesu(M_SEEN_FIRE); /* KMH -- this is okay with unchanging */ - Strcpy(gk.killer.name, "immolated"); - gk.killer.format = NO_KILLER_PREFIX; + Strcpy(svk.killer.name, "immolated"); + svk.killer.format = NO_KILLER_PREFIX; rehumanize(); return; } else if (Fire_resistance) { pline_The("fire doesn't feel hot!"); monstseesu(M_SEEN_FIRE); mhm->damage = 0; + } else { + monstunseesu(M_SEEN_FIRE); } if ((int) magr->m_lev > rn2(20)) { (void) destroy_items(&gy.youmonst, AD_FIRE, orig_dmg); @@ -2660,12 +2793,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); @@ -2697,6 +2830,7 @@ mhitm_ad_cold( struct monst *mdef, struct mhitm_data *mhm) { const int orig_dmg = mhm->damage; + if (magr == &gy.youmonst) { /* uhitm */ if (mhitm_mgc_atk_negated(magr, mdef, TRUE)) { @@ -2722,6 +2856,8 @@ mhitm_ad_cold( pline_The("frost doesn't seem cold!"); monstseesu(M_SEEN_COLD); mhm->damage = 0; + } else { + monstunseesu(M_SEEN_COLD); } if ((int) magr->m_lev > rn2(20)) (void) destroy_items(&gy.youmonst, AD_COLD, orig_dmg); @@ -2734,7 +2870,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)); @@ -2785,6 +2921,8 @@ mhitm_ad_elec( pline_The("zap doesn't shock you!"); monstseesu(M_SEEN_ELEC); mhm->damage = 0; + } else { + monstunseesu(M_SEEN_ELEC); } if ((int) magr->m_lev > rn2(20)) (void) destroy_items(&gy.youmonst, AD_ELEC, orig_dmg); @@ -2797,7 +2935,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)); @@ -2830,6 +2968,7 @@ mhitm_ad_acid( } else { pline("You're covered in %s! It burns!", hliquid("acid")); exercise(A_STR, FALSE); + monstunseesu(M_SEEN_ACID); } else mhm->damage = 0; @@ -2845,7 +2984,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)) @@ -2873,7 +3012,8 @@ mhitm_ad_sgld( /* stole a gold non-coin object */ really_steal(mongold, mdef); } - else if (merge_choice(gi.invent, mongold) || inv_cnt(FALSE) < 52) { + if (merge_choice(gi.invent, mongold) + || inv_cnt(FALSE) < invlet_basic) { obj_extract_self(mongold); addinv(mongold); Your("purse feels heavier."); @@ -2925,6 +3065,7 @@ mhitm_ad_sgld( if (!tele_restrict(magr)) { boolean couldspot = canspotmon(magr); + mhm->hitflags = M_ATTK_AGR_DONE; (void) rloc(magr, RLOC_NOMSG); /* TODO: use RLOC_MSG instead? */ if (gv.vis && couldspot && !canspotmon(magr)) @@ -2965,10 +3106,10 @@ 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 (Verbose(4, mhitm_ad_tlpt)) + if (flags.verbose) Your("position suddenly seems %suncertain!", (Teleport_control && !Stunned && !unconscious()) ? "" : "very "); @@ -2986,7 +3127,7 @@ mhitm_ad_tlpt( if (Half_physical_damage) mhm->damage *= 2; /* doesn't actually increase damage; * we only get here if half the - * original damage would would have + * original damage would have * been fatal, so double reduced * damage will be less than original */ if (mhm->damage < 1) { /* implies (tmphp <= 1) */ @@ -3010,7 +3151,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); @@ -3093,8 +3234,9 @@ mhitm_ad_blnd( } void -mhitm_ad_curs(struct monst *magr, struct attack *mattk, struct monst *mdef, - struct mhitm_data *mhm) +mhitm_ad_curs( + struct monst *magr, struct attack *mattk, + struct monst *mdef, struct mhitm_data *mhm) { struct permonst *pa = magr->data; struct permonst *pd = mdef->data; @@ -3125,14 +3267,14 @@ mhitm_ad_curs(struct monst *magr, struct attack *mattk, struct monst *mdef, if (Blind) { You_hear("laughter."); } else { - pline("%s chuckles.", Monnam(magr)); + pline_mon(magr, "%s chuckles.", Monnam(magr)); } } if (u.umonnum == PM_CLAY_GOLEM) { pline("Some writing vanishes from your head!"); /* KMH -- this is okay with unchanging */ - Sprintf(gk.killer.name, "identity theft"); - gk.killer.format = KILLED_BY; + Sprintf(svk.killer.name, "identity theft"); + svk.killer.format = KILLED_BY; rehumanize(); return; } @@ -3151,7 +3293,7 @@ mhitm_ad_curs(struct monst *magr, struct attack *mattk, struct monst *mdef, 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)) { @@ -3162,7 +3304,8 @@ mhitm_ad_curs(struct monst *magr, struct attack *mattk, struct monst *mdef, You(brief_feeling, "strangely sad"); } mhm->hitflags = (M_ATTK_DEF_DIED - | (grow_up(magr, mdef) ? 0 : M_ATTK_AGR_DIED)); + | (grow_up(magr, mdef) ? 0 + : M_ATTK_AGR_DIED)); mhm->done = TRUE; return; } @@ -3170,7 +3313,7 @@ mhitm_ad_curs(struct monst *magr, struct attack *mattk, struct monst *mdef, if (!gv.vis) You_hear("laughter."); else if (canseemon(magr)) - pline("%s chuckles.", Monnam(magr)); + pline_mon(magr, "%s chuckles.", Monnam(magr)); } } } @@ -3181,7 +3324,7 @@ mhitm_ad_curs(struct monst *magr, struct attack *mattk, struct monst *mdef, * 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. */ -static void +staticfn void mhitm_really_poison(struct monst *magr, struct attack *mattk, struct monst *mdef, struct mhitm_data *mhm) { @@ -3344,7 +3487,7 @@ mhitm_ad_drin( 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 @@ -3378,16 +3521,22 @@ mhitm_ad_stck( { boolean negated = mhitm_mgc_atk_negated(magr, mdef, FALSE); struct permonst *pd = mdef->data; + boolean barbs = (magr->data == &mons[PM_BARBED_DEVIL]); if (magr == &gy.youmonst) { /* uhitm */ - if (!negated && !sticks(pd) && next2u(mdef->mx, mdef->my)) + if (!negated && !sticks(pd) && m_next2u(mdef)) { set_ustuck(mdef); /* it's now stuck to you */ + if (barbs) + Your("barbs stick to %s!", y_monnam(mdef)); + } } else if (mdef == &gy.youmonst) { /* mhitu */ hitmsg(magr, mattk); if (!negated && !u.ustuck && !sticks(pd)) { set_ustuck(magr); + if (barbs) + pline("The barbs stick to you!"); } } else { /* mhitm */ @@ -3426,7 +3575,7 @@ mhitm_ad_wrap( pline("%s is being crushed.", Monnam(mdef)); } else { mhm->damage = 0; - if (Verbose(4, mhitm_ad_wrap1)) { + if (flags.verbose) { if (coil && !tailmiss) You("brush against %s.", mon_nam(mdef)); else @@ -3445,7 +3594,8 @@ mhitm_ad_wrap( } else { set_ustuck(magr); /* before message, for botl update */ urgent_pline("%s %s itself around you!", - Some_Monnam(magr), coil ? "coils" : "swings"); + Some_Monnam(magr), + coil ? "coils" : "swings"); } } else if (u.ustuck == magr) { if (is_pool(magr->mx, magr->my) && !Swimming && !Amphibious @@ -3456,8 +3606,8 @@ mhitm_ad_wrap( && !Is_waterlevel(&u.uz); urgent_pline("%s drowns you...", Monnam(magr)); - gk.killer.format = KILLED_BY_AN; - Sprintf(gk.killer.name, "%s by %s", + svk.killer.format = KILLED_BY_AN; + Sprintf(svk.killer.name, "%s by %s", moat ? "moat" : "pool of water", an(pmname(magr->data, Mgender(magr)))); done(DROWNING); @@ -3466,12 +3616,13 @@ mhitm_ad_wrap( } } else { mhm->damage = 0; - if (Verbose(4, mhitm_ad_wrap2)) { + 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 @@ -3573,7 +3724,8 @@ mhitm_ad_plys( void mhitm_ad_slee( struct monst *magr, struct attack *mattk, - struct monst *mdef, struct mhitm_data *mhm UNUSED) + struct monst *mdef, + struct mhitm_data *mhm UNUSED) { if (magr == &gy.youmonst) { /* uhitm */ @@ -3592,6 +3744,7 @@ mhitm_ad_slee( monstseesu(M_SEEN_SLEEP); return; } + monstunseesu(M_SEEN_SLEEP); fall_asleep(-rnd(10), TRUE); if (Blind) You("are put to sleep!"); @@ -3695,7 +3848,8 @@ mhitm_ad_slim( void mhitm_ad_ench( struct monst *magr, struct attack *mattk, - struct monst *mdef, struct mhitm_data *mhm UNUSED) + struct monst *mdef, + struct mhitm_data *mhm UNUSED) { if (magr == &gy.youmonst) { /* uhitm */ @@ -3743,7 +3897,7 @@ mhitm_ad_ench( /* guts of mhitm_ad_slow(); extracted so that ice devils' cold attack can cause * slowness; assumes checks for avoiding slowing have already been performed. * Note that this doesn't take mhm_data so it can't adjust damage. */ -static void +staticfn void mhitm_ad_slow_core(struct monst *magr, struct monst *mdef) { if (magr == &gy.youmonst) { @@ -3753,7 +3907,7 @@ mhitm_ad_slow_core(struct monst *magr, struct monst *mdef) mon_adjust_speed(mdef, -1, (struct obj *) 0); if (mdef->mspeed != oldspeed && canseemon(mdef)) - pline("%s slows down.", Monnam(mdef)); + pline_mon(mdef, "%s slows down.", Monnam(mdef)); } } else if (mdef == &gy.youmonst) { /* mhitu */ @@ -3767,7 +3921,7 @@ mhitm_ad_slow_core(struct monst *magr, struct monst *mdef) 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)); } } } @@ -3775,7 +3929,8 @@ mhitm_ad_slow_core(struct monst *magr, struct monst *mdef) void mhitm_ad_slow( struct monst *magr, struct attack *mattk, - struct monst *mdef, struct mhitm_data *mhm UNUSED) + struct monst *mdef, + struct mhitm_data *mhm UNUSED) { boolean negated = mhitm_mgc_atk_negated(magr, mdef, FALSE); @@ -3791,8 +3946,9 @@ mhitm_ad_slow( } void -mhitm_ad_conf(struct monst *magr, struct attack *mattk, struct monst *mdef, - struct mhitm_data *mhm) +mhitm_ad_conf( + struct monst *magr, struct attack *mattk, + struct monst *mdef, struct mhitm_data *mhm) { if (magr == &gy.youmonst) { /* uhitm */ @@ -3821,7 +3977,7 @@ mhitm_ad_conf(struct monst *magr, struct attack *mattk, struct monst *mdef, */ 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; } @@ -3946,7 +4102,7 @@ mhitm_ad_wthr(struct monst *magr, struct attack *mattk, u.uhp = min(u.uhp, u.uhpmax); } } - gc.context.botl = TRUE; + disp.botl = TRUE; } } else { /* uhitm, mhitm */ @@ -3970,8 +4126,10 @@ mhitm_ad_wthr(struct monst *magr, struct attack *mattk, } void -mhitm_ad_famn(struct monst *magr, struct attack *mattk UNUSED, - struct monst *mdef, struct mhitm_data *mhm) +mhitm_ad_famn( + struct monst *magr, + struct attack *mattk UNUSED, + struct monst *mdef, struct mhitm_data *mhm) { struct permonst *pd = mdef->data; @@ -3982,7 +4140,8 @@ mhitm_ad_famn(struct monst *magr, struct attack *mattk UNUSED, 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)); @@ -3998,8 +4157,9 @@ mhitm_ad_famn(struct monst *magr, struct attack *mattk UNUSED, } void -mhitm_ad_pest(struct monst *magr, struct attack *mattk, - struct monst *mdef, struct mhitm_data *mhm) +mhitm_ad_pest( + struct monst *magr, struct attack *mattk, + struct monst *mdef, struct mhitm_data *mhm) { struct attack alt_attk; struct permonst *pa = magr->data; @@ -4011,7 +4171,8 @@ mhitm_ad_pest(struct monst *magr, struct attack *mattk, 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 { @@ -4027,7 +4188,7 @@ mhitm_ad_pest(struct monst *magr, struct attack *mattk, void mhitm_ad_deth( struct monst *magr, - struct attack *mattk UNUSED, + struct attack *mattk, struct monst *mdef, struct mhitm_data *mhm) { struct permonst *pd = mdef->data; @@ -4039,7 +4200,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; @@ -4055,6 +4216,7 @@ mhitm_ad_deth( mhm->damage = 0; return; } + FALLTHROUGH; /*FALLTHRU*/ default: /* case 16: ... case 5: */ You_feel("your life force draining away..."); @@ -4084,8 +4246,10 @@ mhitm_ad_deth( } void -mhitm_ad_halu(struct monst *magr, struct attack *mattk UNUSED, - struct monst *mdef, struct mhitm_data *mhm) +mhitm_ad_halu( + struct monst *magr, + struct attack *mattk UNUSED, + struct monst *mdef, struct mhitm_data *mhm) { mhm->damage = 0; @@ -4113,8 +4277,8 @@ mhitm_ad_halu(struct monst *magr, struct attack *mattk UNUSED, } else { /* mhitm */ if (gv.vis && canseemon(mdef)) - pline("%s looks %sconfused.", Monnam(mdef), - mdef->mconf ? "more " : ""); + pline_mon(mdef, "%s looks %sconfused.", Monnam(mdef), + mdef->mconf ? "more " : ""); mdef->mconf = 1; mdef->mstrategy &= ~STRAT_WAITFORU; } @@ -4142,8 +4306,10 @@ do_stone_u(struct monst *mtmp) } void -do_stone_mon(struct monst *magr, struct attack *mattk UNUSED, - struct monst *mdef, struct mhitm_data *mhm) +do_stone_mon( + struct monst *magr, + struct attack *mattk, + struct monst *mdef, struct mhitm_data *mhm) { struct permonst *pd = mdef->data; @@ -4157,7 +4323,7 @@ do_stone_mon(struct monst *magr, struct attack *mattk UNUSED, } 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)) { @@ -4226,7 +4392,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) { @@ -4246,8 +4412,9 @@ mhitm_ad_phys( 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; @@ -4303,7 +4470,7 @@ mhitm_ad_phys( exercise(A_STR, FALSE); /* inflict damage now; we know it can't be fatal */ u.mh -= tmp; - gc.context.botl = 1; + disp.botl = TRUE; mhm->damage = 0; /* don't inflict more damage below */ if (cloneu()) You("divide as %s hits you!", mon_nam(magr)); @@ -4404,7 +4571,7 @@ mhitm_ad_phys( mhm->dieroll); if (artimsg == ARTIFACTHIT_NOMSG) { 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; } @@ -4412,8 +4579,9 @@ mhitm_ad_phys( damage; however, it might cause carried items to be destroyed and they might do so */ if (DEADMONSTER(mdef)) { - mhm->hitflags = (M_ATTK_DEF_DIED | (grow_up(magr, mdef) ? 0 - : M_ATTK_AGR_DIED)); + mhm->hitflags = (M_ATTK_DEF_DIED + | (grow_up(magr, mdef) ? 0 + : M_ATTK_AGR_DIED)); mhm->done = TRUE; return; } @@ -4486,7 +4654,7 @@ mhitm_ad_ston( * * Maintaining foodless conduct during a new moon might * become a little harder. Clearing out cockatrice nests - * could become quite a bit harder. + * during a new moon could become quite a bit harder. */ if (!rn2(10) || flags.moonphase == NEW_MOON) { if (Hallucination) { @@ -4542,8 +4710,9 @@ mhitm_ad_were( } void -mhitm_ad_heal(struct monst *magr, struct attack *mattk, struct monst *mdef, - struct mhitm_data *mhm) +mhitm_ad_heal( + struct monst *magr, struct attack *mattk, + struct monst *mdef, struct mhitm_data *mhm) { struct permonst *pd = mdef->data; @@ -4566,7 +4735,8 @@ mhitm_ad_heal(struct monst *magr, struct attack *mattk, struct monst *mdef, && !uarms && !uarmg && !uarmf && !uarmh) { boolean goaway = FALSE; - pline("%s touches you! (I hope you don't mind.)", Monnam(magr)); + pline_mon(magr, "%s touches you! (I hope you don't mind.)", + Monnam(magr)); if (Upolyd) { u.mh += rnd(7); if (!rn2(7)) { @@ -4598,7 +4768,7 @@ mhitm_ad_heal(struct monst *magr, struct attack *mattk, struct monst *mdef, exercise(A_CON, TRUE); if (Sick) make_sick(0L, (char *) 0, FALSE, SICK_ALL); - gc.context.botl = 1; + disp.botl = TRUE; if (goaway) { mongone(magr); mhm->done = TRUE; @@ -4615,7 +4785,7 @@ mhitm_ad_heal(struct monst *magr, struct attack *mattk, struct monst *mdef, mhm->damage = 0; } else { if (Role_if(PM_HEALER)) { - if (!Deaf && !(gm.moves % 5)) { + if (!Deaf && !(svm.moves % 5)) { SetVoice(magr, 0, 80, 0); verbalize("Doc, I can't help you unless you cooperate."); } @@ -4632,8 +4802,9 @@ mhitm_ad_heal(struct monst *magr, struct attack *mattk, struct monst *mdef, } void -mhitm_ad_stun(struct monst *magr, struct attack *mattk, struct monst *mdef, - struct mhitm_data *mhm) +mhitm_ad_stun( + struct monst *magr, struct attack *mattk, + struct monst *mdef, struct mhitm_data *mhm) { struct permonst *pd = mdef->data; @@ -4658,7 +4829,7 @@ mhitm_ad_stun(struct monst *magr, struct attack *mattk, struct monst *mdef, 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); @@ -4668,8 +4839,9 @@ mhitm_ad_stun(struct monst *magr, struct attack *mattk, struct monst *mdef, } void -mhitm_ad_legs(struct monst *magr, struct attack *mattk, struct monst *mdef, - struct mhitm_data *mhm) +mhitm_ad_legs( + struct monst *magr, struct attack *mattk, + struct monst *mdef, struct mhitm_data *mhm) { if (magr == &gy.youmonst) { /* uhitm */ @@ -4696,7 +4868,7 @@ mhitm_ad_legs(struct monst *magr, struct attack *mattk, struct monst *mdef, 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 { @@ -4734,8 +4906,10 @@ mhitm_ad_legs(struct monst *magr, struct attack *mattk, struct monst *mdef, } void -mhitm_ad_dgst(struct monst *magr, struct attack *mattk UNUSED, - struct monst *mdef, struct mhitm_data *mhm) +mhitm_ad_dgst( + struct monst *magr, + struct attack *mattk UNUSED, + struct monst *mdef, struct mhitm_data *mhm) { struct permonst *pd = mdef->data; @@ -4753,7 +4927,7 @@ mhitm_ad_dgst(struct monst *magr, struct attack *mattk UNUSED, /* 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]) @@ -4770,7 +4944,7 @@ mhitm_ad_dgst(struct monst *magr, struct attack *mattk UNUSED, mhm->done = TRUE; return; } - if (Verbose(4, mhitm_ad_dgst) && !Deaf) { + if (flags.verbose && !Deaf) { /* Soundeffect? */ SetVoice(magr, 0, 80, 0); verbalize("Burrrrp!"); @@ -4791,7 +4965,7 @@ mhitm_ad_dgst(struct monst *magr, struct attack *mattk UNUSED, */ num = monsndx(pd); if (magr->mtame && !magr->isminion - && !(gm.mvitals[num].mvflags & G_NOCORPSE)) { + && !(svm.mvitals[num].mvflags & G_NOCORPSE)) { struct obj *virtualcorpse = mksobj(CORPSE, FALSE, FALSE); int nutrit; @@ -4810,8 +4984,9 @@ mhitm_ad_dgst(struct monst *magr, struct attack *mattk UNUSED, } void -mhitm_ad_samu(struct monst *magr, struct attack *mattk, struct monst *mdef, - struct mhitm_data *mhm) +mhitm_ad_samu( + struct monst *magr, struct attack *mattk, + struct monst *mdef, struct mhitm_data *mhm) { if (magr == &gy.youmonst) { /* uhitm */ @@ -4886,10 +5061,10 @@ 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" + : magr->minvent + ? "brags about the goods some dungeon explorer provided" : "makes some remarks about how difficult theft is lately"); if (!tele_restrict(magr)) (void) rloc(magr, RLOC_MSG); @@ -4930,8 +5105,9 @@ mhitm_ad_sedu( } 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??? */ @@ -4975,13 +5151,15 @@ mhitm_ad_sedu( mselftouch(mdef, (const char *) 0, FALSE); if (DEADMONSTER(mdef)) { mhm->hitflags = (M_ATTK_DEF_DIED - | (grow_up(magr, mdef) ? 0 : M_ATTK_AGR_DIED)); + | (grow_up(magr, mdef) ? 0 + : M_ATTK_AGR_DIED)); mhm->done = TRUE; return; } if (pa->mlet == S_NYMPH && !tele_restrict(magr)) { boolean couldspot = canspotmon(magr); + mhm->hitflags = M_ATTK_AGR_DONE; (void) rloc(magr, RLOC_NOMSG); /* TODO: use RLOC_MSG instead? */ if (gv.vis && couldspot && !canspotmon(magr)) @@ -5119,7 +5297,7 @@ damageum( You_feel("embarrassed for a moment."); if (mhm.damage) xkilled(mdef, XKILL_NOMSG); - } else if (!Verbose(4, damageum)) { + } else if (!flags.verbose) { You("destroy it!"); if (mhm.damage) xkilled(mdef, XKILL_NOMSG); @@ -5139,7 +5317,7 @@ damageum( int explum(struct monst *mdef, struct attack *mattk) { - register int tmp = d((int) mattk->damn, (int) mattk->damd); + int tmp = d((int) mattk->damn, (int) mattk->damd); switch (mattk->adtyp) { case AD_BLND: @@ -5167,7 +5345,7 @@ explum(struct monst *mdef, struct attack *mattk) return M_ATTK_HIT; } -static void +staticfn void start_engulf(struct monst *mdef) { boolean u_digest = digests(gy.youmonst.data), @@ -5185,7 +5363,7 @@ start_engulf(struct monst *mdef) nh_delay_output(); } -static void +staticfn void end_engulf(void) { if (!Invisible) { @@ -5194,12 +5372,12 @@ end_engulf(void) } } -static int +staticfn int gulpum(struct monst *mdef, struct attack *mattk) { static char msgbuf[BUFSZ]; /* for gn.nomovemsg */ - register int tmp; - register int dam = d((int) mattk->damn, (int) mattk->damd); + int tmp; + int dam = d((int) mattk->damn, (int) mattk->damd); boolean fatal_gulp, u_digest = digests(gy.youmonst.data), u_enfold = enfolds(gy.youmonst.data); @@ -5277,9 +5455,9 @@ gulpum(struct monst *mdef, struct attack *mattk) if (is_rider(pd)) { pline("Unfortunately, digesting any of it is fatal."); end_engulf(); - Sprintf(gk.killer.name, "unwisely tried to eat %s", + Sprintf(svk.killer.name, "unwisely tried to eat %s", pmname(pd, Mgender(mdef))); - gk.killer.format = NO_KILLER_PREFIX; + svk.killer.format = NO_KILLER_PREFIX; done(DIED); return M_ATTK_MISS; /* lifesaved */ } @@ -5307,7 +5485,7 @@ gulpum(struct monst *mdef, struct attack *mattk) } else { tmp = 1 + (pd->cwt >> 8); if (corpse_chance(mdef, &gy.youmonst, TRUE) - && !(gm.mvitals[monsndx(pd)].mvflags & G_NOCORPSE)) { + && !(svm.mvitals[monsndx(pd)].mvflags & G_NOCORPSE)) { /* nutrition only if there can be a corpse */ int nutr = (pd->cnutrit + 1) / 2; u.uhunger += (Hunger ? (nutr + 1) / 2 : nutr); @@ -5494,7 +5672,7 @@ missum( if (could_seduce(&gy.youmonst, mdef, mattk)) You("pretend to be friendly to %s.", mon_nam(mdef)); - else if (canspotmon(mdef) && Verbose(4, missum)) { + else if (canspotmon(mdef) && flags.verbose) { const char *blocker = attack_blocker(mdef); if (blocker && !rn2(3)) pline("%s %s %s your attack.", s_suffix(Monnam(mdef)), blocker, @@ -5550,6 +5728,28 @@ mhitm_knockback( if (rn2(6)) 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) { @@ -5601,13 +5801,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" @@ -5633,6 +5826,9 @@ mhitm_knockback( You_feel("%s be knocked %s!", some_mon_nam(mdef), knockedhow); } + if (u.ustuck && (u_def || u_agr)) + unstuck(u.ustuck); + /* do the actual knockback effect */ if (u_def) { if (dismount) { @@ -5673,19 +5869,34 @@ mhitm_knockback( } /* attack monster as a monster; returns True if mon survives */ -static boolean +staticfn boolean hmonas(struct monst *mon) { struct attack *mattk, alt_attk; struct obj *weapon, **originalweapon; - boolean altwep = FALSE, weapon_used = FALSE; + boolean altwep = FALSE, weapon_used = FALSE, odd_claw = TRUE; int i, tmp, armorpenalty, sum[NATTK], - dhit = 0, attknum = 0; + dhit = 0, attknum = 0, multi_claw = 0, multi_weap = 0; int dieroll; boolean monster_survived; /* not used here but umpteen mhitm_ad_xxxx() need this */ - gv.vis = (canseemon(mon) || next2u(mon->mx, mon->my)); + gv.vis = (canseemon(mon) || m_next2u(mon)); + + /* with just one touch/claw/weapon attack, both rings matter; + with more than one, alternate right and left when checking + whether ring causes successful hit */ + for (i = 0; i < NATTK; i++) { + sum[i] = M_ATTK_MISS; + mattk = getmattk(&gy.youmonst, mon, i, sum, &alt_attk); + if (mattk->aatyp == AT_WEAP) + ++multi_weap; + if (mattk->aatyp == AT_WEAP + || mattk->aatyp == AT_CLAW || mattk->aatyp == AT_TUCH) + ++multi_claw; + } + multi_claw = (multi_claw > 1); /* switch from count to yes/no */ + gt.twohits = 0; gs.skipdrin = FALSE; /* [see mattackm(mhitm.c)] */ @@ -5722,7 +5933,7 @@ hmonas(struct monst *mon) be Null, and we want to track that for passive() */ originalweapon = (altwep && uswapwep) ? &uswapwep : &uwep; if (uswapwep /* set up 'altwep' flag for next iteration */ - /* only consider seconary when wielding one-handed primary */ + /* only consider secondary when wielding one-handed primary */ && uwep && (uwep->oclass == WEAPON_CLASS || is_weptool(uwep)) && !bimanual(uwep) /* only switch if not wearing shield and not at artifact; @@ -5749,6 +5960,8 @@ hmonas(struct monst *mon) mon_maybe_unparalyze(mon); dieroll = rnd(20); dhit = (tmp > dieroll || u.uswallow); + if (multi_weap > 1) + ++gt.twohits; /* caller must set gb.bhitpos */ monster_survived = known_hitum(mon, weapon, &dhit, tmp, armorpenalty, mattk, dieroll); @@ -5775,10 +5988,12 @@ 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: case AT_BITE: @@ -5795,6 +6010,8 @@ hmonas(struct monst *mon) int compat, specialdmg; struct obj * hated_obj = NULL; const char *verb = 0; /* verb or body part */ + long contact_slots = attack_contact_slots(&gy.youmonst, + mattk->aatyp); if (!u.uswallow && (compat = could_seduce(&gy.youmonst, mon, mattk)) @@ -5809,24 +6026,31 @@ hmonas(struct monst *mon) break; } wakeup(mon, TRUE, TRUE); - /* There used to be a bunch of code here to ensure that W_RINGL - * and W_RINGR slots got chosen on alternating claw/touch - * attacks. There's no such logic for monsters, and if you know - * that the ring on one of your hands will be especially - * effective, you'll probably keep hitting with that hand. So - * just do the default and take whatever the most damaging piece - * of gear is. */ - specialdmg = special_dmgval(&gy.youmonst, mon, - attack_contact_slots(&gy.youmonst, - mattk->aatyp), - &hated_obj); + switch (mattk->aatyp) { case AT_CLAW: /* verb=="claws" may be overridden below */ - verb = "claws"; - break; case AT_TUCH: - verb = "touch"; + verb = (mattk->aatyp == AT_TUCH) ? "touch" : "claws"; + /* decide if material-hater will be hit by ring(s); + for 'multi_claw' where attacks alternate right/left, + assume 'even' claw or touch attacks use dominant hand + or paw, 'odd' ones use non-dominant hand for ring + interaction; even vs odd is based on actual attacks + rather than on index into mon->dat->mattk[] so that + {bite,claw,claw} instead of {claw,claw,bite} doesn't + make poly'd hero mysteriously switch handedness */ + odd_claw = !odd_claw; + /* xNetHack: other pieces of gear such as helms can hit the + * monster, so the special_dmgval call is moved to after + * this switch. Here we first suppress ring slots, then + * re-add them as necessary. W_ARMG should already have been + * set by attack_contact_slots and isn't modified here. */ + contact_slots &= ~(W_RINGL | W_RINGR); + if (!multi_claw || odd_claw) + contact_slots |= W_RINGL; + if (!multi_claw || !odd_claw) + contact_slots |= W_RINGR; break; case AT_TENT: /* assumes mind flayer's tentacles-on-head rather @@ -5849,6 +6073,9 @@ hmonas(struct monst *mon) verb = "hit"; break; } + specialdmg = special_dmgval(&gy.youmonst, mon, contact_slots, + &hated_obj); + if (noncorporeal(mon->data) && !specialdmg) { if (!strcmp(verb, "hit") || (mattk->aatyp == AT_CLAW && humanoid(mon->data))) @@ -5869,7 +6096,7 @@ hmonas(struct monst *mon) if (!verb) verb = "hit"; You("%s %s.", verb, mon_nam(mon)); - if (hated_obj && Verbose(4, hmonas1)) + if (hated_obj && flags.verbose) searmsg(&gy.youmonst, mon, hated_obj, FALSE); } sum[i] = damageum(mon, mattk, specialdmg); @@ -5931,7 +6158,7 @@ hmonas(struct monst *mon) choking hug; deals damage but never grabs hold */ if (specialdmg) { You("%s %s%s", verb, mon_nam(mon), exclam(specialdmg)); - if (hated_obj && Verbose(4, hmonas2)) + if (hated_obj && flags.verbose) searmsg(&gy.youmonst, mon, hated_obj, FALSE); sum[i] = damageum(mon, mattk, specialdmg); } else { @@ -5949,7 +6176,7 @@ hmonas(struct monst *mon) byhand ? "throttled" : "crushed", /* extra feedback for non-breather being choked */ unconcerned ? " but doesn't seem concerned" : ""); - if (hated_obj && Verbose(4, hmonas3)) + if (hated_obj && flags.verbose) searmsg(&gy.youmonst, mon, hated_obj, FALSE); sum[i] = damageum(mon, mattk, specialdmg); } else if (i >= 2 && (sum[i - 1] > M_ATTK_MISS) @@ -5961,7 +6188,7 @@ hmonas(struct monst *mon) uunstick(); You("grab %s!", mon_nam(mon)); set_ustuck(mon); - if (hated_obj && Verbose(4, hmonas4)) + if (hated_obj && flags.verbose) searmsg(&gy.youmonst, mon, hated_obj, FALSE); sum[i] = damageum(mon, mattk, specialdmg); } @@ -6010,6 +6237,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: @@ -6028,8 +6256,8 @@ hmonas(struct monst *mon) } if (dhit == -1) { u.mh = -1; /* dead in the current form */ - Sprintf(gk.killer.name, "blew %sself up", uhim()); - gk.killer.format = NO_KILLER_PREFIX; + Sprintf(svk.killer.name, "blew %sself up", uhim()); + svk.killer.format = NO_KILLER_PREFIX; rehumanize(); } if (sum[i] == M_ATTK_DEF_DIED) { @@ -6064,6 +6292,7 @@ hmonas(struct monst *mon) } gv.vis = FALSE; /* reset */ + gt.twohits = 0; /* return value isn't used, but make it match hitum()'s */ return !DEADMONSTER(mon); } @@ -6071,15 +6300,16 @@ hmonas(struct monst *mon) /* Special (passive) attacks on you by monsters done here. */ int -passive(struct monst *mon, - struct obj *weapon, /* uwep or uswapwep or uarmg or uarmf or Null */ - boolean mhitb, - boolean maliveb, - uchar aatyp, - boolean wep_was_destroyed) +passive( + struct monst *mon, + struct obj *weapon, /* uwep or uswapwep or uarmg or uarmf or Null */ + boolean mhitb, + boolean maliveb, + uchar aatyp, + boolean wep_was_destroyed) { - register struct permonst *ptr = mon->data; - register int i, tmp; + struct permonst *ptr = mon->data; + int i, tmp; int mhit = mhitb ? M_ATTK_HIT : M_ATTK_MISS; int malive = maliveb ? M_ATTK_HIT : M_ATTK_MISS; @@ -6113,16 +6343,18 @@ passive(struct monst *mon, break; case AD_ACID: if (mhitb && rn2(2)) { - if (Blind || !Verbose(4, passive)) + if (Blind || !flags.verbose) You("are splashed!"); else You("are splashed by %s %s!", s_suffix(mon_nam(mon)), hliquid("acid")); - if (!Acid_resistance) + if (!Acid_resistance) { mdamageu(mon, tmp); - else + monstunseesu(M_SEEN_ACID); + } else { monstseesu(M_SEEN_ACID); + } if (!rn2(30)) erode_armor(&gy.youmonst, ERODE_CORRODE); } @@ -6192,6 +6424,7 @@ passive(struct monst *mon, } else { You("are hit by magic missiles appearing from thin air!"); mdamageu(mon, tmp); + monstunseesu(M_SEEN_MAGR); } break; case AD_ENCH: /* KMH -- remove enchantment (disenchanter) */ @@ -6288,14 +6521,13 @@ passive(struct monst *mon, ugolemeffects(AD_COLD, tmp); break; } + monstunseesu(M_SEEN_COLD); You("are suddenly very cold!"); mdamageu(mon, tmp); /* monster gets stronger with your heat! */ - mon->mhp += tmp / 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)) + if (mon->mhpmax > (((int) mon->m_lev) + 1) * 8) (void) split_mon(mon, &gy.youmonst); } break; @@ -6312,6 +6544,7 @@ passive(struct monst *mon, ugolemeffects(AD_FIRE, tmp); break; } + monstunseesu(M_SEEN_FIRE); You("are suddenly very hot!"); mdamageu(mon, tmp); /* fire damage */ } @@ -6324,6 +6557,7 @@ passive(struct monst *mon, ugolemeffects(AD_ELEC, tmp); break; } + monstunseesu(M_SEEN_ELEC); You("are jolted with electricity!"); mdamageu(mon, tmp); break; @@ -6415,6 +6649,8 @@ passive_obj( } break; } + FALLTHROUGH; + /* FALLTHRU */ default: break; } @@ -6443,7 +6679,7 @@ item_catches_drain(struct monst *mdef) } if (interceptor) { /* use mon_moving to check for hero responsibility */ - drain_item(interceptor, !gc.context.mon_moving); + drain_item(interceptor, !svc.context.mon_moving); pline("%s less effective.", Yobjnam2(interceptor, "seem")); /* Drain was intercepted, so the monster "resisted" it. */ return TRUE; @@ -6454,40 +6690,92 @@ item_catches_drain(struct monst *mdef) 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) */ + boolean reveal_it) /* True: remove its disguise */ { - 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 */ - && next2u(mtmp->mx, mtmp->my)) - set_ustuck(mtmp); + static char generic[] = "a monster"; + char fmtbuf[BUFSZ]; + const char *what = NULL; + 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 = u.ux + u.dx, y = u.uy + u.dy; + int glyph = glyph_at(x, y); + + 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); - 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!"; + 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); + pline(fmtbuf, 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, TRUE); + + 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, TRUE); /* clears mimicking */ /* if hero is blind, wakeup() won't display the monster even though @@ -6497,9 +6785,7 @@ stumble_onto_mimic(struct monst *mtmp) map_invisible(mtmp->mx, mtmp->my); } -RESTORE_WARNING_FORMAT_NONLITERAL - -static void +staticfn void nohandglow(struct monst *mon) { char *hands; @@ -6526,17 +6812,42 @@ nohandglow(struct monst *mon) /* returns 1 if light flash has noticeable effect on 'mtmp', 0 otherwise */ int -flash_hits_mon(struct monst *mtmp, - struct obj *otmp) /* source of flash */ +flash_hits_mon( + struct monst *mtmp, + struct obj *otmp) /* source of flash */ { struct rm *lev; + coordxy mx = mtmp->mx, my = mtmp->my; int tmp, amt, useeit, res = 0; if (gn.notonhead) return 0; - lev = &levl[mtmp->mx][mtmp->my]; + lev = &levl[mx][my]; useeit = canseemon(mtmp); + if (M_AP_TYPE(mtmp) != M_AP_NOTHING) { + char whatbuf[BUFSZ]; + int oldglyph = glyph_at(mx, my); + + /* 'altmon' probably doesn't matter here because 'whatbuf' will + only be shown if the glyph changes and wakeup() doesn't call + seemimic() for M_AP_MONSTER */ + mhidden_description(mtmp, MHID_ALTMON, whatbuf); + + wakeup(mtmp, FALSE, TRUE); /* -> seemimic() -> newsym(); also calls + * finish_meating() to end quickmimic */ + + /* if glyph has changed then hero saw something happen */ + if (glyph_at(mx, my) != oldglyph) { + pline("That %s is really %s%c", whatbuf, + /* y_monnam()+a_monnam() */ + x_monnam(mtmp, mtmp->mtame ? ARTICLE_YOUR : ARTICLE_A, + (char *) 0, 0, FALSE), + mtmp->mtame ? '.' : '!'); + res = 1; + } + } + if (mtmp->msleeping && haseyes(mtmp->data)) { mtmp->msleeping = 0; if (useeit) { @@ -6545,31 +6856,35 @@ flash_hits_mon(struct monst *mtmp, } } else if (mtmp->data->mlet != S_LIGHT) { if (!resists_blnd(mtmp)) { - tmp = dist2(otmp->ox, otmp->oy, mtmp->mx, mtmp->my); + tmp = dist2(otmp->ox, otmp->oy, mx, my); if (useeit) { pline("%s is blinded by the flash!", Monnam(mtmp)); res = 1; } if (mtmp->data == &mons[PM_GREMLIN]) { /* Rule #1: Keep them out of the light. */ - amt = otmp->otyp == WAN_LIGHT ? d(1 + otmp->spe, 4) - : rn2(min(mtmp->mhp, 4)); + amt = (otmp->otyp == WAN_LIGHT) ? d(1 + otmp->spe, 4) + : rnd(min(mtmp->mhp, 4)); light_hits_gremlin(mtmp, amt); } if (!DEADMONSTER(mtmp)) { - if (!gc.context.mon_moving) + if (!svc.context.mon_moving) setmangry(mtmp, TRUE); if (tmp < 9 && !mtmp->isshk && rn2(4)) monflee(mtmp, rn2(4) ? rnd(100) : 0, FALSE, TRUE); mtmp->mcansee = 0; mtmp->mblinded = (tmp < 3) ? 0 : rnd(1 + 50 / tmp); } - } else if (Verbose(4, flash_hits_mon) && useeit) { - if (lev->lit) - pline("The flash of light shines on %s.", mon_nam(mtmp)); - else - pline("%s is illuminated.", Monnam(mtmp)); - res = 2; /* 'message has been given' temporary value */ + } else if (useeit) { + if (resists_blnd_by_arti(mtmp)) + shieldeff(mx, my); + if (flags.verbose) { + if (lev->lit) + pline("The flash of light shines on %s.", mon_nam(mtmp)); + else + pline("%s is illuminated.", Monnam(mtmp)); + res = 2; /* 'message has been given' temporary value */ + } } } if (res) { @@ -6584,13 +6899,14 @@ void light_hits_gremlin(struct monst *mon, int dmg) { if (canspotmon(mon)) { - pline("%s %s!", Monnam(mon), - (dmg > mon->mhp / 2) ? "wails in agony" : "cries out in pain"); + pline_mon(mon, "%s %s!", Monnam(mon), + (dmg > mon->mhp / 2) ? "wails in agony" + : "cries out in pain"); } mon->mhp -= dmg; wake_nearto(mon->mx, mon->my, 30); if (DEADMONSTER(mon)) { - if (gc.context.mon_moving) + if (svc.context.mon_moving) monkilled(mon, (char *) 0, AD_BLND); else killed(mon); @@ -6598,7 +6914,7 @@ light_hits_gremlin(struct monst *mon, int dmg) if (cansee(mon->mx, mon->my) && !canspotmon(mon)) { map_invisible(mon->mx, mon->my); } - if (!gc.context.mon_moving) { + if (!svc.context.mon_moving) { setmangry(mon, FALSE); } } diff --git a/src/utf8map.c b/src/utf8map.c index 136ba4c788..87688b9ceb 100644 --- a/src/utf8map.c +++ b/src/utf8map.c @@ -3,7 +3,8 @@ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" -#include + +#ifdef ENHANCED_SYMBOLS extern const struct symparse loadsyms[]; extern struct enum_dump monsdump[]; @@ -11,446 +12,47 @@ extern struct enum_dump objdump[]; extern glyph_map glyphmap[MAX_GLYPH]; extern const char *const known_handling[]; /* symbols.c */ -#ifdef ENHANCED_SYMBOLS - -#define Fprintf (void) fprintf -enum reserved_activities { res_nothing, res_dump_glyphids, res_fill_cache }; -enum things_to_find { find_nothing, find_pm, find_oc, find_cmap, find_glyph }; -struct find_struct { - enum things_to_find findtype; - int val; - int loadsyms_offset; - int loadsyms_count; - int *extraval; - long color; - const char *unicode_val; /* U+NNNN format */ - void (*callback)(int glyph, struct find_struct *); - enum reserved_activities restype; - genericptr_t reserved; -}; -const struct find_struct zero_find = { 0 }; -struct glyphid_cache_t { - int glyphnum; - char *id; -}; -static struct glyphid_cache_t *glyphid_cache; -static unsigned glyphid_cache_lsize; -static size_t glyphid_cache_size; -struct find_struct glyphcache_find, to_custom_symbol_find; -static void init_glyph_cache(void); -static void add_glyph_to_cache(int glyphnum, const char *id); -static int find_glyph_in_cache(const char *id); -static uint32 glyph_hash(const char *id); -static void to_custom_symset_entry_callback(int glyph, - struct find_struct *findwhat); -static int unicode_val(const char *cp); -static int parse_id(const char *id, struct find_struct *findwhat); -static int glyph_find_core(const char *id, struct find_struct *findwhat); -static char *fix_glyphname(char *str); -static int32_t rgbstr_to_int32(const char *rgbstr); -boolean closest_color(uint32_t lcolor, uint32_t *closecolor, int *clridx); - -static void -to_custom_symset_entry_callback(int glyph, struct find_struct *findwhat) -{ -#ifdef NO_PARSING_SYMSET - glyph_map *gmap = &glyphmap[glyph]; -#endif - uint8 utf8str[6] = { 0, 0, 0, 0, 0, 0 }; - int uval; +/* hexdd[] is defined in decl.c */ - if (!findwhat->unicode_val) - return; - if (findwhat->extraval) - *findwhat->extraval = glyph; - uval = unicode_val(findwhat->unicode_val); - if (unicodeval_to_utf8str(uval, utf8str, sizeof utf8str)) { -#ifdef NO_PARSING_SYMSET - set_map_u(gmap, uval, utf8str, - (findwhat->color != 0L) ? findwhat->color : 0L); -#endif - add_custom_urep_entry(known_handling[H_UTF8], glyph, - uval, utf8str, findwhat->color, - gs.symset_which_set); - } -} - -/* - * Return value: - * 1 = success - * 0 = failure - */ int -glyphrep_to_custom_map_entries(const char *op, int *glyphptr) -{ - to_custom_symbol_find = zero_find; - char buf[BUFSZ], *c_glyphid, *c_unicode, *c_rgb, *cp; - int milestone, reslt = 0; - long rgb; - boolean slash = FALSE; - - if (!glyphid_cache) - reslt = 1; /* for debugger use only; no cache available */ - - milestone = 0; - Snprintf(buf, sizeof buf, "%s", op); - c_unicode = c_rgb = (char *) 0; - c_glyphid = cp = buf; - while (*cp) { - if ((*cp == '/') || (*cp == ':')) { - *cp = '\0'; - milestone++; - slash = TRUE; - } - cp++; - if (slash) { - if (milestone < 2) - c_unicode = cp; - else - c_rgb = cp; - slash = FALSE; - } - } - /* some sanity checks */ - if (c_glyphid && *c_glyphid == ' ') - c_glyphid++; - if (c_unicode && *c_unicode == ' ') - c_unicode++; - if (c_rgb && *c_rgb == ' ') - c_rgb++; - if (c_unicode && (*c_unicode == 'U' || *c_unicode == 'u') - && (c_unicode[1] == '+')) { - /* unicode = unicode_val(c_unicode); */ - if ((rgb = rgbstr_to_int32(c_rgb)) != -1L || !c_rgb) { - to_custom_symbol_find.unicode_val = c_unicode; - /* if the color 0 is an actual color, as opposed to just "not set" - we set a marker bit outside the 24-bit range to indicate a - valid color value 0. That allows valid color 0, but allows a - simple checking for 0 to detect "not set". The window port that - implements the color switch, needs to either check that bit - or appropriately mask colors with 0xFFFFFF. */ - to_custom_symbol_find.color = (rgb == -1 || !c_rgb) ? 0L - : (rgb == 0L) ? (0 & 0x1000000) - : rgb; - to_custom_symbol_find.extraval = glyphptr; - to_custom_symbol_find.callback = to_custom_symset_entry_callback; - reslt = glyph_find_core(c_glyphid, &to_custom_symbol_find); - return reslt; - } - } - return 0; -} - -static int32_t -rgbstr_to_int32(const char *rgbstr) -{ - int r, g, b, milestone = 0; - char *cp, *c_r,*c_g,*c_b; - int32_t rgb = 0; - char buf[BUFSZ]; - boolean dash = FALSE; - - r = g = b = 0; - c_g = c_b = (char *) 0; - Snprintf(buf, sizeof buf, "%s", rgbstr); - c_r = cp = buf; - while (*cp) { - if (digit(*cp) || *cp == '-') { - if (*cp == '-') { - *cp = '\0'; - milestone++; - dash = TRUE; - } - cp++; - if (dash) { - if (milestone < 2) - c_g = cp; - else - c_b = cp; - dash = FALSE; - } - } else { - return -1L; - } - } - /* sanity checks */ - if (c_r && c_g && c_b - && (strlen(c_r) > 0 && strlen(c_r) < 4) - && (strlen(c_g) > 0 && strlen(c_g) < 4) - && (strlen(c_b) > 0 && strlen(c_b) < 4)) { - r = atoi(c_r); - g = atoi(c_g); - b = atoi(c_b); - rgb = (r << 16) | (g << 8) | (b << 0); - return rgb; - } - return -1L; -} - -static char * -fix_glyphname(char *str) -{ - char *c; - - for (c = str; *c; c++) { - if (*c >= 'A' && *c <= 'Z') - *c += (char) ('a' - 'A'); - else if (*c >= '0' && *c <= '9') - ; - else if (*c < 'a' || *c > 'z') - *c = '_'; - } - return str; -} - -static int unicode_val(const char *cp) { const char *dp; - int cval = 0, dcount, unicode = 0; - static const char hex[] = "00112233445566778899aAbBcCdDeEfF"; + int cval = 0, dcount; if (cp && *cp) { cval = dcount = 0; - if ((unicode = ((*cp == 'U' || *cp == 'u') && cp[1] == '+')) && cp[2] - && (dp = strchr(hex, cp[2])) != 0) { + if ((*cp == 'U' || *cp == 'u') + && cp[1] == '+' && cp[2] && (dp = strchr(hexdd, cp[2])) != 0) { cp += 2; /* move past the 'U' and '+' */ do { - cval = (cval * 16) + ((int) (dp - hex) / 2); - } while (*++cp && (dp = strchr(hex, *cp)) != 0 && ++dcount < 7); + cval = (cval * 16) + ((int) (dp - hexdd) / 2); + } while (*++cp && (dp = strchr(hexdd, *cp)) != 0 && ++dcount < 7); } } return cval; } int -set_map_u(glyph_map *gmap, uint32 utf32ch, const uint8 *utf8str, long ucolor) -{ - static uint32_t closecolor = 0; - static int clridx = 0; - - if (gmap) { - if (gmap->u == 0) { - gmap->u = (struct unicode_representation *) alloc(sizeof *gmap->u); - gmap->u->utf8str = 0; - } - if (gmap->u->utf8str != 0) { - free(gmap->u->utf8str); - gmap->u->utf8str = 0; - } - gmap->u->utf8str = (uint8 *) dupstr((const char *) utf8str); - gmap->u->ucolor = ucolor; - if (closest_color(ucolor, &closecolor, &clridx)) - gmap->u->u256coloridx = clridx; - else - gmap->u->u256coloridx = 0; - gmap->u->utf32ch = utf32ch; - return 1; - } - return 0; -} - - -static int -glyph_find_core(const char *id, struct find_struct *findwhat) -{ - int glyph; - boolean do_callback, end_find = FALSE; - - if (parse_id(id, findwhat)) { - if (findwhat->findtype == find_glyph) { - (findwhat->callback)(findwhat->val, findwhat); - } else { - for (glyph = 0; glyph < MAX_GLYPH; ++glyph) { - do_callback = FALSE; - switch (findwhat->findtype) { - case find_cmap: - if (glyph_to_cmap(glyph) == findwhat->val) - do_callback = TRUE; - break; - case find_pm: - if (glyph_is_monster(glyph) - && mons[glyph_to_mon(glyph)].mlet == findwhat->val) - do_callback = TRUE; - break; - case find_oc: - if (glyph_is_object(glyph) - && glyph_to_obj(glyph) == findwhat->val) - do_callback = TRUE; - break; - case find_glyph: - if (glyph == findwhat->val) { - do_callback = TRUE; - end_find = TRUE; - } - break; - case find_nothing: - default: - end_find = TRUE; - break; - } - if (do_callback) - (findwhat->callback)(glyph, findwhat); - if (end_find) - break; - } - } - return 1; - } - return 0; -} - -/* - When we start to process a config file or a symbol file, - that might have G_ entries, generating all 9000+ glyphid - for comparison repeatedly each time we encounter a G_ - entry to decipher, then comparing against them, is obviously - extremely performance-poor. - - Setting aside the "comparison" part for now (that has to be - done in some manner), we can likely do something about the - repeated "generation" of the names for parsing prior to the - actual comparison part by generating them once, ahead of the - bulk of the potential parsings. We can later free up - all the memory those names consumed once the bulk parsing is - overwith. -*/ - - -void fill_glyphid_cache(void) +set_map_u(glyph_map *gmap, uint32 utf32ch, const uint8 *utf8str) { - int reslt = 0; + glyph_map *tmpgm = gmap; - if (!glyphid_cache) { - init_glyph_cache(); - } - if (glyphid_cache) { - glyphcache_find = zero_find; - glyphcache_find.findtype = find_nothing; - glyphcache_find.reserved = (genericptr_t) glyphid_cache; - glyphcache_find.restype = res_fill_cache; - reslt = parse_id((char *) 0, &glyphcache_find); - if (!reslt) { - free_glyphid_cache(); - glyphid_cache = (struct glyphid_cache_t *) 0; - } - } -} - -/* - * The glyph ID cache is a simple double-hash table. - * The cache size is a power of two, and two hashes are derived from the - * cache ID. The first is a location in the table, and the second is an - * offset. On any collision, the second hash is added to the first until - * a match or an empty bucket is found. - * The second hash is an odd number, which is necessary and sufficient - * to traverse the entire table. - */ + if (!tmpgm || !utf32ch) + return 0; -static void -init_glyph_cache(void) -{ - size_t glyph; - - /* Cache size of power of 2 not less than 2*MAX_GLYPH */ - glyphid_cache_lsize = 0; - glyphid_cache_size = 1; - while (glyphid_cache_size < 2*MAX_GLYPH) { - ++glyphid_cache_lsize; - glyphid_cache_size <<= 1; - } - - glyphid_cache = (struct glyphid_cache_t *) alloc( - glyphid_cache_size * sizeof(struct glyphid_cache_t)); - for (glyph = 0; glyph < glyphid_cache_size; ++glyph) { - glyphid_cache[glyph].glyphnum = 0; - glyphid_cache[glyph].id = (char *) 0; + if (gmap->u == 0) { + gmap->u = + (struct unicode_representation *) alloc(sizeof *gmap->u); + gmap->u->utf8str = 0; } -} - -void free_glyphid_cache(void) -{ - size_t idx; - - if (!glyphid_cache) - return; - for (idx = 0; idx < glyphid_cache_size; ++idx) { - if (glyphid_cache[idx].id) { - free(glyphid_cache[idx].id); - glyphid_cache[idx].id = (char *) 0; - } + if (gmap->u->utf8str != 0) { + free(gmap->u->utf8str); + gmap->u->utf8str = 0; } - free(glyphid_cache); - glyphid_cache = (struct glyphid_cache_t *) 0; -} - -static void -add_glyph_to_cache(int glyphnum, const char *id) -{ - uint32 hash = glyph_hash(id); - size_t hash1 = (size_t) (hash & (glyphid_cache_size - 1)); - size_t hash2 = (size_t) - (((hash >> glyphid_cache_lsize) & (glyphid_cache_size - 1)) | 1); - size_t i = hash1; - - do { - if (glyphid_cache[i].id == NULL) { - /* Empty bucket found */ - glyphid_cache[i].id = dupstr(id); - glyphid_cache[i].glyphnum = glyphnum; - return; - } - /* For speed, assume that no ID occurs twice */ - i = (i + hash2) & (glyphid_cache_size - 1); - } while (i != hash1); - /* This should never happen */ - panic("glyphid_cache full"); -} - -static int -find_glyph_in_cache(const char *id) -{ - uint32 hash = glyph_hash(id); - size_t hash1 = (size_t) (hash & (glyphid_cache_size - 1)); - size_t hash2 = (size_t) - (((hash >> glyphid_cache_lsize) & (glyphid_cache_size - 1)) | 1); - size_t i = hash1; - - do { - if (glyphid_cache[i].id == NULL) { - /* Empty bucket found */ - return -1; - } - if (strcmpi(id, glyphid_cache[i].id) == 0) { - /* Match found */ - return glyphid_cache[i].glyphnum; - } - i = (i + hash2) & (glyphid_cache_size - 1); - } while (i != hash1); - return -1; -} - -static uint32 -glyph_hash(const char *id) -{ - uint32 hash = 0; - size_t i; - - for (i = 0; id[i] != '\0'; ++i) { - char ch = id[i]; - if ('A' <= ch && ch <= 'Z') { - ch += 'a' - 'A'; - } - hash = (hash << 1) | (hash >> 31); - hash ^= ch; - } - return hash; -} - -boolean -glyphid_cache_status(void) -{ - return (glyphid_cache != 0); + gmap->u->utf8str = (uint8 *) dupstr((const char *) utf8str); + gmap->u->utf32ch = utf32ch; + return 1; } void @@ -542,53 +144,16 @@ mixed_to_utf8(char *buf, size_t bufsz, const char *str, int *retflags) return buf; } -void -dump_all_glyphids(FILE *fp) -{ - struct find_struct dump_glyphid_find = zero_find; - - dump_glyphid_find.findtype = find_nothing; - dump_glyphid_find.reserved = (genericptr_t) fp; - dump_glyphid_find.restype = res_dump_glyphids; - (void) parse_id((char *) 0, &dump_glyphid_find); -} - -int -match_glyph(char *buf) -{ - char workbuf[BUFSZ]; - - /* buf contains a G_ glyph reference, not an S_ symbol. - There could be an R-G-B color attached too. - Let's get a copy to work with. */ - Snprintf(workbuf, sizeof workbuf, "%s", buf); /* get a copy */ - return glyphrep(workbuf); -} - -int -glyphrep(const char *op) -{ - int reslt = 0, glyph = NO_GLYPH; - if (!glyphid_cache) - reslt = 1; /* for debugger use only; no cache available */ - reslt = glyphrep_to_custom_map_entries(op, &glyph); - if (reslt) - return 1; - return 0; -} - int add_custom_urep_entry( const char *customization_name, int glyphidx, uint32 utf32ch, const uint8 *utf8str, - long ucolor, enum graphics_sets which_set) { - static uint32_t closecolor = 0; - static int clridx = 0; - struct symset_customization *gdc = &gs.sym_customizations[which_set]; + struct symset_customization *gdc + = &gs.sym_customizations[which_set][custom_ureps]; struct customization_detail *details, *newdetails = 0; @@ -598,21 +163,21 @@ add_custom_urep_entry( gdc->details = 0; gdc->details_end = 0; } - details = find_matching_symset_customiz(customization_name, - custom_symbols, which_set); + details = find_matching_customization(customization_name, + custom_ureps, which_set); /* FIXME */ if (details) { while (details) { if (details->content.urep.glyphidx == glyphidx) { if (details->content.urep.u.utf8str) free(details->content.urep.u.utf8str); - details->content.urep.u.utf8str = - (uint8 *) dupstr((const char *) utf8str); - details->content.urep.u.ucolor = ucolor; - if (closest_color(ucolor, &closecolor, &clridx)) - details->content.urep.u.u256coloridx = clridx; - else - details->content.urep.u.u256coloridx = 0; - details->content.urep.u.utf32ch = utf32ch; + if (utf32ch) { + details->content.urep.u.utf8str = + (uint8 *) dupstr((const char *) utf8str); + details->content.urep.u.utf32ch = utf32ch; + } else { + details->content.urep.u.utf8str = (uint8 *) 0; + details->content.urep.u.utf32ch = 0; + } return 1; } details = details->next; @@ -620,15 +185,15 @@ add_custom_urep_entry( } /* create new details entry */ newdetails = (struct customization_detail *) alloc( - sizeof(struct customization_detail)); + sizeof (struct customization_detail)); newdetails->content.urep.glyphidx = glyphidx; - newdetails->content.urep.u.utf8str = - (uint8 *) dupstr((const char *) utf8str); - newdetails->content.urep.u.ucolor = ucolor; - if (closest_color(ucolor, &closecolor, &clridx)) - newdetails->content.urep.u.u256coloridx = clridx; - else - newdetails->content.urep.u.u256coloridx = 0; + if (utf8str && *utf8str) { + newdetails->content.urep.u.utf8str = + (uint8 *) dupstr((const char *) utf8str); + } else { + newdetails->content.urep.u.utf8str = + (uint8 *) 0; + } newdetails->content.urep.u.utf32ch = utf32ch; newdetails->next = (struct customization_detail *) 0; if (gdc->details == NULL) { @@ -640,596 +205,15 @@ add_custom_urep_entry( gdc->count++; return 1; } - -static int -parse_id(const char *id, struct find_struct *findwhat) -{ - FILE *fp = (FILE *) 0; - int i = 0, j, mnum, glyph, pm_offset = 0, oc_offset = 0, cmap_offset = 0, - pm_count = 0, oc_count = 0, cmap_count = 0; - boolean skip_base = FALSE, skip_this_one, dump_ids = FALSE, - filling_cache = FALSE, is_S = FALSE, is_G = FALSE; - char buf[4][QBUFSZ]; - - if (findwhat->findtype == find_nothing && findwhat->restype) { - if (findwhat->restype == res_dump_glyphids) { - if (findwhat->reserved) { - fp = (FILE *) findwhat->reserved; - dump_ids = TRUE; - } else { - return 0; - } - } - if (findwhat->restype == res_fill_cache) { - if (findwhat->reserved - && findwhat->reserved == (genericptr_t) glyphid_cache) { - filling_cache = TRUE; - } else { - return 0; - } - } - } - - is_G = (id && id[0] == 'G' && id[1] == '_'); - is_S = (id && id[0] == 'S' && id[1] == '_'); - - if ((is_G && !glyphid_cache) || filling_cache || dump_ids || is_S) { - while (loadsyms[i].range) { - if (!pm_offset && loadsyms[i].range == SYM_MON) - pm_offset = i; - if (!pm_count && pm_offset && loadsyms[i].range != SYM_MON) - pm_count = i - pm_offset; - if (!oc_offset && loadsyms[i].range == SYM_OC) - oc_offset = i; - if (!oc_count && oc_offset && loadsyms[i].range != SYM_OC) - oc_count = i - oc_offset; - if (!cmap_offset && loadsyms[i].range == SYM_PCHAR) - cmap_offset = i; - if (!cmap_count && cmap_offset && loadsyms[i].range != SYM_PCHAR) - cmap_count = i - cmap_offset; - i++; - } - } - if (is_G || filling_cache || dump_ids) { - if (!filling_cache && id && glyphid_cache) { - int val = find_glyph_in_cache(id); - if (val >= 0) { - findwhat->findtype = find_glyph; - findwhat->val = val; - findwhat->loadsyms_offset = 0; - return 1; - } else { - return 0; - } - } else { - /* individual matching glyph entries */ - for (glyph = 0; glyph < MAX_GLYPH; ++glyph) { - skip_base = FALSE; - skip_this_one = FALSE; - buf[0][0] = buf[1][0] = buf[2][0] = buf[3][0] = '\0'; - if (glyph_is_monster(glyph)) { - /* buf2 will hold the distinguishing prefix */ - /* buf3 will hold the base name */ - const char *buf2 = ""; - const char *buf3 = monsdump[glyph_to_mon(glyph)].nm; - if (glyph_is_normal_male_monster(glyph)) { - buf2 = "male_"; - } else if (glyph_is_normal_female_monster(glyph)) { - buf2 = "female_"; - } else if (glyph_is_ridden_male_monster(glyph)) { - buf2 = "ridden_male_"; - } else if (glyph_is_ridden_female_monster(glyph)) { - buf2 = "ridden_female_"; - } else if (glyph_is_detected_male_monster(glyph)) { - buf2 = "detected_male_"; - } else if (glyph_is_detected_female_monster(glyph)) { - buf2 = "detected_female_"; - } else if (glyph_is_male_pet(glyph)) { - buf2 = "pet_male_"; - } else if (glyph_is_female_pet(glyph)) { - buf2 = "pet_female_"; - } - Strcpy(buf[0], "G_"); - Strcat(buf[0], buf2); - Strcat(buf[0], buf3); - } else if (glyph_is_body(glyph)) { - /* buf2 will hold the distinguishing prefix */ - /* buf3 will hold the base name */ - const char *buf2 = ""; - const char *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 */ - const char *buf2 = ""; - const char *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); - } else if (glyph_is_object(glyph)) { - i = glyph_to_obj(glyph); - /* buf2 will hold the distinguishing prefix */ - /* buf3 will hold the base name */ - const char *buf2 = ""; - const char *buf3 = ""; - if (((i > SCR_STINKING_CLOUD) && (i < SCR_MAIL)) - || ((i > WAN_LIGHTNING) && (i < GOLD_PIECE))) - skip_this_one = TRUE; - if (!skip_this_one) { - if ((i >= WAN_LIGHT) && (i <= WAN_LIGHTNING)) - buf2 = "wand of "; - else if ((i >= SPE_DIG) && (i < SPE_BLANK_PAPER)) - buf2 = "spellbook of "; - else if ((i >= SCR_ENCHANT_ARMOR) - && (i <= SCR_STINKING_CLOUD)) - buf2 = "scroll of "; - else if ((i >= POT_GAIN_ABILITY) && (i <= POT_WATER)) - buf2 = (i == POT_WATER) ? "flask of n" - : "potion of "; - else if ((i >= RIN_ADORNMENT) - && (i <= RIN_PROTECTION_FROM_SHAPE_CHAN)) - buf2 = "ring of "; - else if (i == LAND_MINE) - buf2 = "unset "; - buf3 = (i == SCR_BLANK_PAPER) ? "blank scroll" - : (i == SPE_BLANK_PAPER) ? "blank spellbook" - : (i == SLIME_MOLD) ? "slime mold" - : obj_descr[i].oc_name - ? obj_descr[i].oc_name - : obj_descr[i].oc_descr; - Strcpy(buf[0], "G_"); - if (glyph_is_normal_piletop_obj(glyph)) - Strcat(buf[0], "piletop_"); - Strcat(buf[0], buf2); - Strcat(buf[0], buf3); - } - } else if (glyph_is_cmap(glyph) || glyph_is_cmap_zap(glyph) - || glyph_is_swallow(glyph) - || glyph_is_explosion(glyph)) { - int cmap = -1; - - /* buf2 will hold the distinguishing prefix */ - /* buf3 will hold the base name */ - /* buf4 will hold the distinguishing suffix */ - const char *buf2 = ""; - const char *buf3 = ""; - const char *buf4 = ""; - if (glyph == GLYPH_CMAP_OFF) { - cmap = S_stone; - buf3 = "stone substrate"; - skip_base = TRUE; - } else if (glyph_is_cmap_gehennom(glyph)) { - cmap = (glyph - GLYPH_CMAP_GEH_OFF) + S_vwall; - buf4 = "_gehennom"; - } else if (glyph_is_cmap_knox(glyph)) { - cmap = (glyph - GLYPH_CMAP_KNOX_OFF) + S_vwall; - buf4 = "_knox"; - } else if (glyph_is_cmap_main(glyph)) { - cmap = (glyph - GLYPH_CMAP_MAIN_OFF) + S_vwall; - buf4 = "_main"; - } else if (glyph_is_cmap_mines(glyph)) { - cmap = (glyph - GLYPH_CMAP_MINES_OFF) + S_vwall; - buf4 = "_mines"; - } else if (glyph_is_cmap_sokoban(glyph)) { - cmap = (glyph - GLYPH_CMAP_SOKO_OFF) + S_vwall; - buf4 = "_sokoban"; - } else if (glyph_is_cmap_a(glyph)) { - cmap = (glyph - GLYPH_CMAP_A_OFF) + S_ndoor; - } else if (glyph_is_cmap_altar(glyph)) { - const char *altar_text[] = { - "unaligned", "chaotic", "neutral", - "lawful", "other", - }; - j = (glyph - GLYPH_ALTAR_OFF); - cmap = S_altar; - if (j != altar_other) { - Snprintf(buf[2], sizeof buf[2], "%s_", - altar_text[j]); - buf2 = buf[2]; - } else { - buf3 = "altar other"; - skip_base = TRUE; - } - } else if (glyph_is_cmap_b(glyph)) { - cmap = (glyph - GLYPH_CMAP_B_OFF) + S_grave; - } else if (glyph_is_cmap_zap(glyph)) { - static const char *zap_texts[] = { - "missile", "fire", "frost", "sleep", - "death", "lightning", "poison gas", "acid" - }; - j = (glyph - GLYPH_ZAP_OFF); - cmap = (j % 4) + S_vbeam; - Snprintf(buf[2], sizeof buf[2], "%s", - loadsyms[cmap + cmap_offset].name + 2); - Snprintf(buf[3], sizeof buf[3], "%s zap %s", - zap_texts[j / 4], fix_glyphname(buf[2])); - buf3 = buf[3]; - buf2 = ""; - skip_base = TRUE; - } else if (glyph_is_cmap_c(glyph)) { - cmap = (glyph - GLYPH_CMAP_C_OFF) + S_digbeam; - } else if (glyph_is_swallow(glyph)) { - static const char *swallow_texts[] = { - "top left", "top center", "top right", - "middle left", "middle right", "bottom left", - "bottom center", "bottom right", - }; - 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], " "); - Strcat(buf[3], swallow_texts[cmap]); - buf3 = buf[3]; - skip_base = TRUE; - } else if (glyph_is_explosion(glyph)) { - int expl; - static const char *expl_type_texts[] = { - "dark", "noxious", "muddy", "wet", - "magical", "fiery", "frosty", - }; - static const char *expl_texts[] = { - "tl", "tc", "tr", "ml", "mc", - "mr", "bl", "bc", "br", - }; - - j = glyph - GLYPH_EXPLODE_OFF; - expl = j / ((S_expl_br - S_expl_tl) + 1); - cmap = - (j % ((S_expl_br - S_expl_tl) + 1)) + S_expl_tl; - i = cmap - S_expl_tl; - Snprintf(buf[2], sizeof buf[2], "%s ", - expl_type_texts[expl]); - buf2 = buf[2]; - Snprintf(buf[3], sizeof buf[3], "%s%s", "expl_", - expl_texts[i]); - buf3 = buf[3]; - skip_base = TRUE; - } - if (!skip_base) { - if (cmap >= 0 && cmap < MAXPCHARS) { - buf3 = loadsyms[cmap + cmap_offset].name + 2; - } - } - Strcpy(buf[0], "G_"); - Strcat(buf[0], buf2); - Strcat(buf[0], buf3); - Strcat(buf[0], buf4); - } else if (glyph_is_invisible(glyph)) { - Strcpy(buf[0], "G_invisible"); - } else if (glyph_is_nothing(glyph)) { - Strcpy(buf[0], "G_nothing"); - } else if (glyph_is_unexplored(glyph)) { - Strcpy(buf[0], "G_unexplored"); - } else if (glyph_is_warning(glyph)) { - j = glyph - GLYPH_WARNING_OFF; - Snprintf(buf[0], sizeof buf[0], "G_%s%d", "warning", j); - } - if (memchr(buf[0], '\0', sizeof buf[0]) == NULL) - panic("parse_id: buf[0] overflowed"); - if (!skip_this_one) { - fix_glyphname(buf[0]+2); - if (dump_ids) { - Fprintf(fp, "(%04d) %s\n", glyph, buf[0]); - } else if (filling_cache) { - add_glyph_to_cache(glyph, buf[0]); - } else if (id) { - if (!strcmpi(id, buf[0])) { - findwhat->findtype = find_glyph; - findwhat->val = glyph; - findwhat->loadsyms_offset = 0; - return 1; - } - } - } - } - } /* not glyphid_cache */ - } else if (is_S) { - /* cmap entries */ - for (i = 0; i < cmap_count; ++i) { - if (!strcmpi(loadsyms[i + cmap_offset].name + 2, id + 2)) { - findwhat->findtype = find_cmap; - findwhat->val = i; - findwhat->loadsyms_offset = i + cmap_offset; - return 1; - } - } - /* objclass entries */ - for (i = 0; i < oc_count; ++i) { - if (!strcmpi(loadsyms[i + oc_offset].name + 2, id + 2)) { - findwhat->findtype = find_oc; - findwhat->val = i; - findwhat->loadsyms_offset = i + oc_offset; - return 1; - } - } - /* permonst entries */ - for (i = 0; i <= pm_count; ++i) { - if (!strcmpi(loadsyms[i + pm_offset].name + 2, id + 2)) { - findwhat->findtype = find_pm; - findwhat->val = i + 1; /* starts at 1 */ - findwhat->loadsyms_offset = i + pm_offset; - return 1; - } - } - } - if (dump_ids || filling_cache) - return 1; - findwhat->findtype = find_nothing; - findwhat->val = 0; - findwhat->loadsyms_offset = 0; - return 0; -} - -static struct { - int index; - uint32_t value; -} color_definitions_256[] = { - /* color values are from unnethack */ - { 16, 0x000000 }, { 17, 0x00005f }, { 18, 0x000087 }, - { 19, 0x0000af }, { 20, 0x0000d7 }, { 21, 0x0000ff }, - { 22, 0x005f00 }, { 23, 0x005f5f }, { 24, 0x005f87 }, - { 25, 0x005faf }, { 26, 0x005fd7 }, { 27, 0x005fff }, - { 28, 0x008700 }, { 29, 0x00875f }, { 30, 0x008787 }, - { 31, 0x0087af }, { 32, 0x0087d7 }, { 33, 0x0087ff }, - { 34, 0x00af00 }, { 35, 0x00af5f }, { 36, 0x00af87 }, - { 37, 0x00afaf }, { 38, 0x00afd7 }, { 39, 0x00afff }, - { 40, 0x00d700 }, { 41, 0x00d75f }, { 42, 0x00d787 }, - { 43, 0x00d7af }, { 44, 0x00d7d7 }, { 45, 0x00d7ff }, - { 46, 0x00ff00 }, { 47, 0x00ff5f }, { 48, 0x00ff87 }, - { 49, 0x00ffaf }, { 50, 0x00ffd7 }, { 51, 0x00ffff }, - { 52, 0x5f0000 }, { 53, 0x5f005f }, { 54, 0x5f0087 }, - { 55, 0x5f00af }, { 56, 0x5f00d7 }, { 57, 0x5f00ff }, - { 58, 0x5f5f00 }, { 59, 0x5f5f5f }, { 60, 0x5f5f87 }, - { 61, 0x5f5faf }, { 62, 0x5f5fd7 }, { 63, 0x5f5fff }, - { 64, 0x5f8700 }, { 65, 0x5f875f }, { 66, 0x5f8787 }, - { 67, 0x5f87af }, { 68, 0x5f87d7 }, { 69, 0x5f87ff }, - { 70, 0x5faf00 }, { 71, 0x5faf5f }, { 72, 0x5faf87 }, - { 73, 0x5fafaf }, { 74, 0x5fafd7 }, { 75, 0x5fafff }, - { 76, 0x5fd700 }, { 77, 0x5fd75f }, { 78, 0x5fd787 }, - { 79, 0x5fd7af }, { 80, 0x5fd7d7 }, { 81, 0x5fd7ff }, - { 82, 0x5fff00 }, { 83, 0x5fff5f }, { 84, 0x5fff87 }, - { 85, 0x5fffaf }, { 86, 0x5fffd7 }, { 87, 0x5fffff }, - { 88, 0x870000 }, { 89, 0x87005f }, { 90, 0x870087 }, - { 91, 0x8700af }, { 92, 0x8700d7 }, { 93, 0x8700ff }, - { 94, 0x875f00 }, { 95, 0x875f5f }, { 96, 0x875f87 }, - { 97, 0x875faf }, { 98, 0x875fd7 }, { 99, 0x875fff }, - { 100, 0x878700 }, { 101, 0x87875f }, { 102, 0x878787 }, - { 103, 0x8787af }, { 104, 0x8787d7 }, { 105, 0x8787ff }, - { 106, 0x87af00 }, { 107, 0x87af5f }, { 108, 0x87af87 }, - { 109, 0x87afaf }, { 110, 0x87afd7 }, { 111, 0x87afff }, - { 112, 0x87d700 }, { 113, 0x87d75f }, { 114, 0x87d787 }, - { 115, 0x87d7af }, { 116, 0x87d7d7 }, { 117, 0x87d7ff }, - { 118, 0x87ff00 }, { 119, 0x87ff5f }, { 120, 0x87ff87 }, - { 121, 0x87ffaf }, { 122, 0x87ffd7 }, { 123, 0x87ffff }, - { 124, 0xaf0000 }, { 125, 0xaf005f }, { 126, 0xaf0087 }, - { 127, 0xaf00af }, { 128, 0xaf00d7 }, { 129, 0xaf00ff }, - { 130, 0xaf5f00 }, { 131, 0xaf5f5f }, { 132, 0xaf5f87 }, - { 133, 0xaf5faf }, { 134, 0xaf5fd7 }, { 135, 0xaf5fff }, - { 136, 0xaf8700 }, { 137, 0xaf875f }, { 138, 0xaf8787 }, - { 139, 0xaf87af }, { 140, 0xaf87d7 }, { 141, 0xaf87ff }, - { 142, 0xafaf00 }, { 143, 0xafaf5f }, { 144, 0xafaf87 }, - { 145, 0xafafaf }, { 146, 0xafafd7 }, { 147, 0xafafff }, - { 148, 0xafd700 }, { 149, 0xafd75f }, { 150, 0xafd787 }, - { 151, 0xafd7af }, { 152, 0xafd7d7 }, { 153, 0xafd7ff }, - { 154, 0xafff00 }, { 155, 0xafff5f }, { 156, 0xafff87 }, - { 157, 0xafffaf }, { 158, 0xafffd7 }, { 159, 0xafffff }, - { 160, 0xd70000 }, { 161, 0xd7005f }, { 162, 0xd70087 }, - { 163, 0xd700af }, { 164, 0xd700d7 }, { 165, 0xd700ff }, - { 166, 0xd75f00 }, { 167, 0xd75f5f }, { 168, 0xd75f87 }, - { 169, 0xd75faf }, { 170, 0xd75fd7 }, { 171, 0xd75fff }, - { 172, 0xd78700 }, { 173, 0xd7875f }, { 174, 0xd78787 }, - { 175, 0xd787af }, { 176, 0xd787d7 }, { 177, 0xd787ff }, - { 178, 0xd7af00 }, { 179, 0xd7af5f }, { 180, 0xd7af87 }, - { 181, 0xd7afaf }, { 182, 0xd7afd7 }, { 183, 0xd7afff }, - { 184, 0xd7d700 }, { 185, 0xd7d75f }, { 186, 0xd7d787 }, - { 187, 0xd7d7af }, { 188, 0xd7d7d7 }, { 189, 0xd7d7ff }, - { 190, 0xd7ff00 }, { 191, 0xd7ff5f }, { 192, 0xd7ff87 }, - { 193, 0xd7ffaf }, { 194, 0xd7ffd7 }, { 195, 0xd7ffff }, - { 196, 0xff0000 }, { 197, 0xff005f }, { 198, 0xff0087 }, - { 199, 0xff00af }, { 200, 0xff00d7 }, { 201, 0xff00ff }, - { 202, 0xff5f00 }, { 203, 0xff5f5f }, { 204, 0xff5f87 }, - { 205, 0xff5faf }, { 206, 0xff5fd7 }, { 207, 0xff5fff }, - { 208, 0xff8700 }, { 209, 0xff875f }, { 210, 0xff8787 }, - { 211, 0xff87af }, { 212, 0xff87d7 }, { 213, 0xff87ff }, - { 214, 0xffaf00 }, { 215, 0xffaf5f }, { 216, 0xffaf87 }, - { 217, 0xffafaf }, { 218, 0xffafd7 }, { 219, 0xffafff }, - { 220, 0xffd700 }, { 221, 0xffd75f }, { 222, 0xffd787 }, - { 223, 0xffd7af }, { 224, 0xffd7d7 }, { 225, 0xffd7ff }, - { 226, 0xffff00 }, { 227, 0xffff5f }, { 228, 0xffff87 }, - { 229, 0xffffaf }, { 230, 0xffffd7 }, { 231, 0xffffff }, - { 232, 0x080808 }, { 233, 0x121212 }, { 234, 0x1c1c1c }, - { 235, 0x262626 }, { 236, 0x303030 }, { 237, 0x3a3a3a }, - { 238, 0x444444 }, { 239, 0x4e4e4e }, { 240, 0x585858 }, - { 241, 0x626262 }, { 242, 0x6c6c6c }, { 243, 0x767676 }, - { 244, 0x808080 }, { 245, 0x8a8a8a }, { 246, 0x949494 }, - { 247, 0x9e9e9e }, { 248, 0xa8a8a8 }, { 249, 0xb2b2b2 }, - { 250, 0xbcbcbc }, { 251, 0xc6c6c6 }, { 252, 0xd0d0d0 }, - { 253, 0xdadada }, { 254, 0xe4e4e4 }, { 255, 0xeeeeee }, -}; - -/** Calculate the color distance between two colors. - * - * Algorithm taken from UnNetHack which took it from - * https://www.compuphase.com/cmetric.htm - **/ - -static int -color_distance(uint32_t rgb1, uint32_t rgb2) -{ - int r1 = (rgb1 >> 16) & 0xFF; - int g1 = (rgb1 >> 8) & 0xFF; - int b1 = (rgb1) &0xFF; - int r2 = (rgb2 >> 16) & 0xFF; - int g2 = (rgb2 >> 8) & 0xFF; - int b2 = (rgb2) &0xFF; - - int rmean = (r1 + r2) / 2; - int r = r1 - r2; - int g = g1 - g2; - int b = b1 - b2; - return ((((512 + rmean) * r * r) >> 8) + 4 * g * g - + (((767 - rmean) * b * b) >> 8)); -} - -boolean -closest_color(uint32_t lcolor, uint32_t *closecolor, int *clridx) -{ - int i, color_index = -1, similar = INT_MAX, current; - boolean retbool = FALSE; - - for (i = 0; i < SIZE(color_definitions_256); i++) { - /* look for an exact match */ - if (lcolor == color_definitions_256[i].value) { - color_index = i; - break; - } - /* find a close color match */ - current = color_distance(lcolor, color_definitions_256[i].value); - if (current < similar) { - color_index = i; - similar = current; - } - } - if (closecolor && clridx && color_index >= 0) { - *closecolor = color_definitions_256[color_index].value; - *clridx = color_definitions_256[color_index].index; - retbool = TRUE; - } - return retbool; -} #endif /* ENHANCED_SYMBOLS */ -#ifdef TEST_GLYPHNAMES - -static struct { - int idx; - const char *nm1; - const char *nm2; -} cmapname[MAXPCHARS] = { -#define PCHAR_TILES -#include "defsym.h" -#undef PCHAR_TILES -}; - -static int glyphs_to_unicode(const char *id, const char *unicode_val, - long clr); -static int find_glyphs(const char *id); -static void just_find_callback(int glyph, struct find_struct *findwhat); -static void to_unicode_callback(int glyph, struct find_struct *findwhat); -static struct customization_detail *find_display_urep_customization( - const char *customization_name, int glyphidx, - enum graphics_sets which_set); - -void -test_glyphnames(void) -{ - int reslt; - - reslt = find_glyphs("G_potion_of_monster_detection"); - reslt = find_glyphs("G_piletop_body_chickatrice"); - reslt = find_glyphs("G_detected_male_homunculus"); - reslt = find_glyphs("S_pool"); - reslt = find_glyphs("S_dog"); - reslt = glyphs_to_unicode("S_dog", "U+130E6", 0L); -} - -static void -just_find_callback(int glyph UNUSED, struct find_struct *findwhat UNUSED) -{ - return; -} - -static int -find_glyphs(const char *id) +void reset_customsymbols(void) { - struct find_struct find_only = zero_find; - - find_only.unicode_val = 0; - find_only.callback = just_find_callback; - return glyph_find_core(id, &find_only); -} - -static void -to_unicode_callback(int glyph UNUSED, struct find_struct *findwhat) -{ - int uval; -#ifdef NO_PARSING_SYMSET - glyph_map *gm = &glyphmap[glyph]; -#endif - uint8 utf8str[6]; - - if (!findwhat->unicode_val) - return; - uval = unicode_val(findwhat->unicode_val); - if (unicodeval_to_utf8str(uval, utf8str, sizeof utf8str)) { -#ifdef NO_PARSING_SYMSET - set_map_u(gm, uval, utf8str, - (findwhat->color != 0L) ? findwhat->color : 0L); -#else - +#ifdef ENHANCED_SYMBOLS + free_all_glyphmap_u(); + apply_customizations(gc.currentgraphics, do_custom_symbols); #endif - } -} - -int -glyphs_to_unicode(const char *id, const char *unicode_val, long clr) -{ - struct find_struct to_unicode = zero_find; - - to_unicode.unicode_val = unicode_val; - to_unicode.callback = to_unicode_callback; - /* if the color 0 is an actual color, as opposed to just "not set" - we set a marker bit outside the 24-bit range to indicate a - valid color value 0. That allows valid color 0, but allows a - simple checking for 0 to detect "not set". The window port that - implements the color switch, needs to either check that bit - or appropriately mask colors with 0xFFFFFF. */ - to_unicode.color = (clr == -1) ? 0L : (clr == 0L) ? (0 & 0x1000000) : clr; - return glyph_find_core(id, &to_unicode); -} - -#if 0 -struct customization_detail * -find_display_urep_customization( - const char *customization_name, - int glyphidx, - enum graphics_sets which_set) -{ - struct symset_customization *gdc = &gs.sym_customizations[which_set]; - struct customization_detail *urepdetails; - - if ((gdc->custtype == custom_ureps) - || (strcmp(customization_name, gdc->customization_name) == 0)) { - urepdetails = gdc->details; - while (urepdetails) { - if (urepdetails->content.urep.glyphidx == glyphidx) - return urepdetails; - urepdetails = urepdetails->next; - } - } - return (struct customization_detail *) 0; } -#endif -#endif /* SOME TEST STUFF */ /* utf8map.c */ diff --git a/src/vault.c b/src/vault.c index 85c2437492..b367142c2c 100644 --- a/src/vault.c +++ b/src/vault.c @@ -5,18 +5,19 @@ #include "hack.h" -static boolean clear_fcorr(struct monst *, boolean); -static void blackout(coordxy, coordxy); -static void restfakecorr(struct monst *); -static void parkguard(struct monst *); -static boolean in_fcorridor(struct monst *, coordxy, coordxy); -static boolean find_guard_dest(struct monst *, coordxy *, coordxy *); -static void move_gold(struct obj *, int); -static void wallify_vault(struct monst *); -static void gd_mv_monaway(struct monst *, int, int); -static void gd_pick_corridor_gold(struct monst *, int, int); -static int gd_move_cleanup(struct monst *, boolean, boolean); -static void gd_letknow(struct monst *); +staticfn boolean clear_fcorr(struct monst *, boolean) NONNULLARG1; +staticfn void blackout(coordxy, coordxy); +staticfn void restfakecorr(struct monst *) NONNULLARG1; +staticfn void parkguard(struct monst *) NONNULLARG1; +staticfn boolean in_fcorridor(struct monst *, coordxy, coordxy) NONNULLARG1; +staticfn boolean find_guard_dest(struct monst *, coordxy *, coordxy *) + NONNULLARG23; +staticfn void move_gold(struct obj *, int) NONNULLARG1; +staticfn void wallify_vault(struct monst *) NONNULLARG1; +staticfn void gd_mv_monaway(struct monst *, int, int) NONNULLARG1; +staticfn void gd_pick_corridor_gold(struct monst *, int, int) NONNULLARG1; +staticfn int gd_move_cleanup(struct monst *, boolean, boolean) NONNULLARG1; +staticfn void gd_letknow(struct monst *) NONNULLARG1; void newegd(struct monst *mtmp) @@ -42,13 +43,13 @@ free_egd(struct monst *mtmp) /* try to remove the temporary corridor (from vault to rest of map) being maintained by guard 'grd'; if guard is still in it, removal will fail, to be tried again later */ -static boolean +staticfn boolean clear_fcorr(struct monst *grd, boolean forceshow) { coordxy fcx, fcy, fcbeg; struct monst *mtmp; boolean sawcorridor = FALSE, - silently = gp.program_state.stopprint ? TRUE : FALSE; + silently = program_state.stopprint ? TRUE : FALSE; struct egd *egrd = EGD(grd); struct trap *trap; struct rm *lev; @@ -108,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; @@ -118,7 +119,7 @@ clear_fcorr(struct monst *grd, boolean forceshow) spots to unlit; if player used scroll/wand/spell of light while inside the corridor, we don't want the light to reappear if/when a new tunnel goes through the same area */ -static void +staticfn void blackout(coordxy x, coordxy y) { struct rm *lev; @@ -139,7 +140,7 @@ blackout(coordxy x, coordxy y) } } -static void +staticfn void restfakecorr(struct monst *grd) { /* it seems you left the corridor - let the guard disappear */ @@ -150,13 +151,13 @@ restfakecorr(struct monst *grd) } /* move guard--dead to alive--to <0,0> until temporary corridor is removed */ -static void +staticfn void parkguard(struct monst *grd) { /* either guard is dead or will now be treated as if so; monster traversal loops should skip it */ - if (grd == gc.context.polearm.hitmon) - gc.context.polearm.hitmon = 0; + if (grd == svc.context.polearm.hitmon) + svc.context.polearm.hitmon = 0; if (grd->mx) { remove_monster(grd->mx, grd->my); newsym(grd->mx, grd->my); @@ -187,10 +188,10 @@ grddead(struct monst *grd) return dispose; } -static boolean +staticfn boolean in_fcorridor(struct monst *grd, coordxy x, coordxy y) { - register int fci; + int fci; struct egd *egrd = EGD(grd); for (fci = egrd->fcbeg; fci < egrd->fcend; fci++) @@ -242,10 +243,10 @@ vault_summon_gd(void) char vault_occupied(char *array) { - register char *ptr; + char *ptr; for (ptr = array; *ptr; ptr++) - if (gr.rooms[*ptr - ROOMOFFSET].rtype == VAULT) + if (svr.rooms[*ptr - ROOMOFFSET].rtype == VAULT) return *ptr; return '\0'; } @@ -276,7 +277,7 @@ uleftvault(struct monst *grd) } } -static boolean +staticfn boolean find_guard_dest(struct monst *guard, coordxy *rx, coordxy *ry) { coordxy x, y, dd, lx, ly; @@ -370,7 +371,7 @@ invault(void) } } while (levl[x][y].typ == ROOM) { - register int dx, dy; + int dx, dy; dx = (gdx > x) ? 1 : (gdx < x) ? -1 : 0; dy = (gdy > y) ? 1 : (gdy < y) ? -1 : 0; @@ -466,7 +467,7 @@ invault(void) return; } if (Strangled || is_silent(gy.youmonst.data) || gm.multi < 0) { - /* [we ought to record whether this this message has already + /* [we ought to record whether this message has already been given in order to vary it upon repeat visits, but discarding the monster and its egd data renders that hard] */ if (Deaf) { @@ -494,13 +495,13 @@ invault(void) if (u.ualign.type == A_LAWFUL /* ignore trailing text, in case player includes rank */ - && strncmpi(buf, gp.plname, (int) strlen(gp.plname)) != 0) { + && strncmpi(buf, svp.plname, (int) strlen(svp.plname)) != 0) { adjalign(-1); /* Liar! */ } if (!strcmpi(buf, "Croesus") || !strcmpi(buf, "Kroisos") || !strcmpi(buf, "Creosote")) { /* Discworld */ - if (!gm.mvitals[PM_CROESUS].died) { + if (!svm.mvitals[PM_CROESUS].died) { if (Deaf) { if (!Blind) pline("%s waves goodbye.", noit_Monnam(guard)); @@ -582,8 +583,8 @@ invault(void) dug into an empty doorway (which could subsequently have been plugged with an intact door by use of locking magic) */ int vlt = EGD(guard)->vroom; - coordxy lowx = gr.rooms[vlt].lx, hix = gr.rooms[vlt].hx; - coordxy lowy = gr.rooms[vlt].ly, hiy = gr.rooms[vlt].hy; + coordxy lowx = svr.rooms[vlt].lx, hix = svr.rooms[vlt].hx; + coordxy lowy = svr.rooms[vlt].ly, hiy = svr.rooms[vlt].hy; if (x == lowx - 1 && y == lowy - 1) typ = TLCORNER; @@ -607,6 +608,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; set_doorstate(&levl[x][y], D_NODOOR); unblock_point(x, y); /* empty doorway doesn't block light */ @@ -615,29 +617,29 @@ invault(void) } } -static void +staticfn void move_gold(struct obj *gold, int vroom) { coordxy nx, ny; remove_object(gold); newsym(gold->ox, gold->oy); - nx = gr.rooms[vroom].lx + rn2(2); - ny = gr.rooms[vroom].ly + rn2(2); + nx = svr.rooms[vroom].lx + rn2(2); + ny = svr.rooms[vroom].ly + rn2(2); place_object(gold, nx, ny); stackobj(gold); newsym(nx, ny); } -static void +staticfn void wallify_vault(struct monst *grd) { int typ; coordxy x, y; int vlt = EGD(grd)->vroom; char tmp_viz; - coordxy lox = gr.rooms[vlt].lx - 1, hix = gr.rooms[vlt].hx + 1, - loy = gr.rooms[vlt].ly - 1, hiy = gr.rooms[vlt].hy + 1; + coordxy lox = svr.rooms[vlt].lx - 1, hix = svr.rooms[vlt].hx + 1, + loy = svr.rooms[vlt].ly - 1, hiy = svr.rooms[vlt].hy + 1; struct monst *mon; struct obj *gold, *rocks; struct trap *trap; @@ -656,7 +658,8 @@ wallify_vault(struct monst *grd) if ((mon = m_at(x, y)) != 0 && mon != grd) { if (mon->mtame) yelp(mon); - (void) rloc(mon, RLOC_ERR); + if (!rloc(mon, RLOC_MSG)) + m_into_limbo(mon); } /* move gold at wall locations into the vault */ if ((gold = g_at(x, y)) != 0) { @@ -716,7 +719,7 @@ wallify_vault(struct monst *grd) } } -static void +staticfn void gd_mv_monaway(struct monst *grd, int nx, int ny) { struct monst *mtmp = m_at(nx, ny); @@ -728,12 +731,13 @@ 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); } } /* have guard pick gold off the floor, possibly moving to the gold's position before message and back to his current spot after */ -static void +staticfn void gd_pick_corridor_gold(struct monst *grd, int goldx, int goldy) { struct obj *gold; @@ -817,7 +821,7 @@ gd_pick_corridor_gold(struct monst *grd, int goldx, int goldy) /* return 1: guard moved, -2: died */ -static int +staticfn int gd_move_cleanup( struct monst *grd, boolean semi_dead, @@ -850,7 +854,7 @@ gd_move_cleanup( return -2; } -static void +staticfn void gd_letknow(struct monst *grd) { if (!cansee(grd->mx, grd->my) || !mon_visible(grd)) @@ -956,6 +960,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; @@ -1208,10 +1213,10 @@ paygd(boolean silently) mnexto(grd, RLOC_NOMSG); if (!silently) pline("%s remits your gold to the vault.", Monnam(grd)); - gdx = gr.rooms[EGD(grd)->vroom].lx + rn2(2); - gdy = gr.rooms[EGD(grd)->vroom].ly + rn2(2); + gdx = svr.rooms[EGD(grd)->vroom].lx + rn2(2); + gdy = svr.rooms[EGD(grd)->vroom].ly + rn2(2); Sprintf(buf, "To Croesus: here's the gold recovered from %s the %s.", - gp.plname, + svp.plname, pmname(&mons[u.umonster], flags.female ? FEMALE : MALE)); if (!in_fcorridor(grd, gdx, gdy)) { /* don't place a grave in a temporary vault corridor that will be diff --git a/src/version.c b/src/version.c index c70805ad82..34085a8af3 100644 --- a/src/version.c +++ b/src/version.c @@ -10,10 +10,10 @@ #define OPTIONS_AT_RUNTIME #endif -extern char *mdlib_version_string(char *, const char *); -static void insert_rtoption(char *); +staticfn void insert_rtoption(char *) NONNULLARG1; -/* fill buffer with short version (so caller can avoid including date.h) */ +/* fill buffer with short version (so caller can avoid including date.h) + * buf cannot be NULL */ char * version_string(char *buf, size_t bufsz) { @@ -58,6 +58,10 @@ getversionstring(char *buf, size_t bufsz) "%sbranch:%s", c++ ? "," : "", nomakedefs.git_branch); #endif + if (nomakedefs.git_prefix) + Snprintf(eos(buf), (bufsz - strlen(buf)) - 1, + "%sprefix:%s", + c++ ? "," : "", nomakedefs.git_prefix); if (c) Snprintf(eos(buf), (bufsz - strlen(buf)) - 1, "%s", ")"); @@ -70,6 +74,79 @@ getversionstring(char *buf, size_t bufsz) return buf; } +/* version info that could be displayed on status lines; + " "; + if game name is a prefix of--or same as--branch name, it is omitted + " "; + after release--or if branch info is unavailable--it will be + " "; + game name or branch name or both can be requested via flags */ +char * +status_version(char *buf, size_t bufsz, boolean indent) +{ + const char *name = NULL, *altname = NULL, *indentation; + unsigned vflags = flags.versinfo; + boolean shownum = ((vflags & VI_NUMBER) != 0), + showname = ((vflags & VI_NAME) != 0), + showbranch = ((vflags & VI_BRANCH) != 0); + + /* game's name {variants should use own name, not "NetHack"} */ + if (showname) { +#ifdef VERS_GAME_NAME /* can be set to override default (base of filename) */ + name = VERS_GAME_NAME; +#else + name = nh_basename(gh.hname, FALSE); /* hname is from xxxmain.c */ +#endif + if (!name || !*name) /* shouldn't happen */ + showname = FALSE; + } + /* git branch name, if available */ + if (showbranch) { +#if 1 /*#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)*/ + altname = nomakedefs.git_branch; +#endif + if (!altname || !*altname) + showbranch = FALSE; + } + if (showname && showbranch) { + if (!strncmpi(name, altname, strlen(name))) + showname = FALSE; +#if 0 + /* note: it's possible for branch name to be a prefix of game name + but that's unlikely enough that we won't bother with it; having + branch "nethack-3.7" be a superset of game "nethack" seems like + including both is redundant, but having branch "net" be a subset + of game "nethack" doesn't feel that way; optimizing "net" out + seems like it would be a mistake */ + else if (!strncmpi(altname, name, strlen(altname))) + showbranch = FALSE; +#endif + } else if (!showname && !showbranch) { + /* flags.versinfo could be set to only 'branch' but it might not + be available */ + shownum = TRUE; + } + + *buf = '\0'; + indentation = indent ? " " : ""; + if (showname) { + Snprintf(eos(buf), bufsz - strlen(buf), "%s%s", indentation, name); + indentation = " "; /* forced separator rather than optional indent */ + } + if (showbranch) { + Snprintf(eos(buf), bufsz - strlen(buf), "%s%s", indentation, altname); + indentation = " "; + } + if (shownum) { + /* x.y.z version number */ + Snprintf(eos(buf), bufsz - strlen(buf), "%s%s", indentation, + (nomakedefs.version_string && nomakedefs.version_string[0]) + ? nomakedefs.version_string + : mdlib_version_string(buf, ".")); + } + return buf; +} + /* the #versionshort command */ int doversion(void) @@ -93,24 +170,8 @@ doextversion(void) done_rt = FALSE, done_dlb = FALSE, prolog; -#if 0 /* moved to util/mdlib.c and rendered via do_runtime_info() */ - const char *lua_info[] = { - "About Lua: Copyright (c) 1994-2017 Lua.org, PUC-Rio.", - /* 1 2 3 4 5 6 7 - 1234567890123456789012345678901234567890123456789012345678901234567890123456789 - */ - " \"Permission is hereby granted, free of charge, to any person obtaining", - " a copy of this software and associated documentation files (the ", - " \"Software\"), to deal in the Software without restriction including", - " without limitation the rights to use, copy, modify, merge, publish,", - " distribute, sublicense, and/or sell copies of the Software, and to ", - " permit persons to whom the Software is furnished to do so, subject to", - " the following conditions:", - " The above copyright notice and this permission notice shall be", - " included in all copies or substantial portions of the Software.\"", - (const char *) 0 - }; -#endif /*0*/ + /* lua_info[] moved to util/mdlib.c and rendered via do_runtime_info() */ + #if defined(OPTIONS_AT_RUNTIME) use_dlb = FALSE; #else @@ -267,7 +328,7 @@ static struct rt_opt { * it depends which of several object files got linked into the * game image, so we insert those options here. */ -static void +staticfn void insert_rtoption(char *buf) { int i; @@ -301,6 +362,14 @@ 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 ( #ifdef VERSION_COMPATIBILITY /* patchlevel.h */ version_data->incarnation < VERSION_COMPATIBILITY @@ -311,7 +380,8 @@ check_version( ) { if (complain) { pline("Version mismatch for file \"%s\".", filename); - display_nhwindow(WIN_MESSAGE, TRUE); + if (WIN_MESSAGE != WIN_ERR) + display_nhwindow(WIN_MESSAGE, TRUE); } return FALSE; } else if ( @@ -333,7 +403,7 @@ check_version( return TRUE; } -/* this used to be based on file date and somewhat OS-dependant, +/* 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) @@ -358,14 +428,17 @@ uptodate(NHFILE *nhfp, const char *name, unsigned long utdflags) if (rlen == 0) { if (verbose) { pline("File \"%s\" is empty?", name); - wait_synch(); + if ((utdflags & UTD_WITHOUT_WAITSYNCH_PERFILE) == 0) + wait_synch(); } return FALSE; } if (!check_version(&vers_info, name, verbose, utdflags)) { - if (verbose) - wait_synch(); + if (verbose) { + if ((utdflags & UTD_WITHOUT_WAITSYNCH_PERFILE) == 0) + wait_synch(); + } return FALSE; } return TRUE; @@ -481,4 +554,26 @@ copyright_banner_line(int indx) return ""; } +/* called by argcheck(allmain.c) from early_options(sys/xxx/xxxmain.c) */ +void +dump_version_info(void) +{ + char buf[BUFSZ]; + const char *hname = gh.hname ? gh.hname : "nethack"; + + 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", + hname, + nomakedefs.version_number, + (nomakedefs.version_features & ~nomakedefs.ignored_features), + nomakedefs.version_sanity1, + nomakedefs.version_sanity2, + nomakedefs.version_sanity3); + raw_print(buf); + release_runtime_info(); + return; +} + /*version.c*/ diff --git a/src/vision.c b/src/vision.c index 82a504c7f3..fd0ed7dbbb 100644 --- a/src/vision.c +++ b/src/vision.c @@ -1,9 +1,8 @@ -/* NetHack 3.7 vision.c $NHDT-Date: 1657918095 2022/07/15 20:48:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.49 $ */ +/* NetHack 3.7 vision.c $NHDT-Date: 1724939600 2024/08/29 13:53:20 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.70 $ */ /* Copyright (c) Dean Luick, with acknowledgements to Dave Cohrs, 1990. */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" -#include /* Circles * ==================================================================*/ @@ -23,7 +22,8 @@ * ....X +4 * @...X +4 * - */ + * Externally referenced from light.c +*/ const coordxy circle_data[] = { /* 0*/ 0, /* 1*/ 1, 1, @@ -87,18 +87,27 @@ static coordxy left_ptrs[ROWNO][COLNO]; /* LOS algorithm helpers */ static coordxy right_ptrs[ROWNO][COLNO]; /* Forward declarations. */ -static void fill_point(int, int); -static void dig_point(int, int); -static void view_init(void); -static void view_from(coordxy, coordxy, seenV **, coordxy *, coordxy *, int, +staticfn void fill_point(int, int); +staticfn void dig_point(int, int); +staticfn void view_init(void); +staticfn void view_from(coordxy, coordxy, seenV **, coordxy *, coordxy *, int, void (*)(coordxy, coordxy, genericptr_t), genericptr_t); -static void get_unused_cs(seenV ***, coordxy **, coordxy **); +staticfn void get_unused_cs(seenV ***, coordxy **, coordxy **); /* Macro definitions that I can't find anywhere. */ #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() * @@ -134,14 +143,16 @@ vision_init(void) /* * does_block() * - * Returns true if something at (x,y) blocks sight. + * Returns 0 if nothing at (x,y) blocks sight, 1 if anything other than + * an opaque region (gas cloud rather than CLOUD terrain) blocks sight, + * or 2 if an opaque region blocks sight. [At present, the rest of the + * code makes no distinction between 1 and 2, just between 0 and non-0.] */ int does_block(int x, int y, struct rm *lev) { struct obj *obj; struct monst *mon; - int i; #ifdef DEBUG /* set DEBUGFILES=seethru in environment to see through bubbles */ @@ -151,15 +162,14 @@ 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) && door_is_closed(lev))) return 1; #ifdef DEBUG if (gs.seethru != 1) { #endif - if (lev->typ == CLOUD || IS_WATERWALL(lev->typ) - || lev->typ == LAVAWALL + if (lev->typ == CLOUD || IS_WATERWALL(lev->typ) || lev->typ == LAVAWALL || (Underwater && is_moat(x, y))) return 1; #ifdef DEBUG @@ -167,11 +177,11 @@ does_block(int x, int y, struct rm *lev) #endif /* Boulders block light. */ - for (obj = gl.level.objects[x][y]; obj; obj = obj->nexthere) + for (obj = svl.level.objects[x][y]; obj; obj = obj->nexthere) if (obj->otyp == BOULDER) return 1; - /* Mimics mimicing a door or boulder or ... block light. */ + /* Mimics mimicking a door or boulder or ... block light. */ if ((mon = m_at(x, y)) && (!mon->minvis || See_invisible) && is_lightblocker_mappear(mon)) return 1; @@ -180,14 +190,8 @@ does_block(int x, int y, struct rm *lev) if (gs.seethru != 1) { #endif /* Clouds (poisonous or not) block light. */ - for (i = 0; i < gn.n_regions; i++) { - /* Ignore regions with ttl == 0 - expire_gas_cloud must unblock its - * points prior to being removed itself. */ - if (gr.regions[i]->ttl > 0 && gr.regions[i]->visible - && inside_region(gr.regions[i], x, y)) { - return 1; - } - } + if (visible_region_at(x, y)) + return 2; #ifdef DEBUG } /* gs.seethru */ #endif @@ -205,8 +209,8 @@ void vision_reset(void) { int y; - register int x, i, dig_left, block; - register struct rm *lev; + int x, i, dig_left, block; + struct rm *lev; /* Start out with cs0 as our current array */ gv.viz_array = cs_rows0; @@ -224,7 +228,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; @@ -254,7 +258,7 @@ vision_reset(void) } } - iflags.vision_inited = 1; /* vision is ready */ + iflags.vision_inited = TRUE; /* vision is ready */ gv.vision_full_recalc = 1; /* we want to run vision_recalc() */ } @@ -264,11 +268,11 @@ vision_reset(void) * Called from vision_recalc() and at least one light routine. Get pointers * to the unused vision work area. */ -static void +staticfn void get_unused_cs(seenV ***rows, coordxy **rmin, coordxy **rmax) { - register int row; - register coordxy *nrmin, *nrmax; + int row; + coordxy *nrmin, *nrmax; if (gv.viz_array == cs_rows0) { *rows = cs_rows1; @@ -296,7 +300,7 @@ get_unused_cs(seenV ***rows, coordxy **rmin, coordxy **rmax) #ifdef EXTEND_SPINE -static int new_angle(struct rm *, unsigned char *, int, int); +staticfn int new_angle(struct rm *, unsigned char *, int, int); /* * new_angle() * @@ -339,10 +343,10 @@ static int new_angle(struct rm *, unsigned char *, int, int); * many exceptions. I may have to bite the bullet and do more * checks. - Dean 2/11/93 */ -static int +staticfn int new_angle(struct rm *lev, unsigned char *sv, int row, int col) { - register int res = *sv; + int res = *sv; /* * Do extra checks for crosswalls and T walls if we see them from @@ -452,14 +456,14 @@ vision_recalc(int control) int row = 0; /* row counter (outer loop) */ int start, stop; /* inner loop starting/stopping index */ int dx, dy; /* one step from a lit door or lit wall (see below) */ - register int col; /* inner loop counter */ - register struct rm *lev; /* pointer to current pos */ + int col; /* inner loop counter */ + struct rm *lev; /* pointer to current pos */ struct rm *flev; /* pointer to position in "front" of current pos */ const seenV *sv; /* ptr to seen angle bits */ int oldseenv; /* previous seenv value */ gv.vision_full_recalc = 0; /* reset flag */ - if (gi.in_mklev || !iflags.vision_inited) + if (gi.in_mklev || program_state.in_getlev || !iflags.vision_inited) return; /* @@ -733,7 +737,7 @@ vision_recalc(int control) /* * At this point we know that the row position is *not* in normal - * sight. That is, the position is could be seen, but is dark + * sight. That is, the position could be seen, but is dark * or LOS is just plain blocked. * * Update the position if: @@ -741,7 +745,7 @@ vision_recalc(int control) * the glyph -- E.g. darken room spot, etc. * o If we now could see the location (yet the location is not * lit), but previously we couldn't see the location, or vice - * versa. Update the spot because there there may be an + * versa. Update the spot because there may be an * infrared monster there. */ } else { @@ -760,16 +764,16 @@ vision_recalc(int control) /* This newsym() caused a crash delivering msg about failure to open * dungeon file init_dungeons() -> panic() -> done(11) -> * vision_recalc(2) -> newsym() -> crash! u.ux and u.uy are 0 and - * gp.program_state.panicking == 1 under those circumstances + * program_state.panicking == 1 under those circumstances */ - if (!gp.program_state.panicking) + if (!program_state.panicking) newsym(u.ux, u.uy); /* Make sure the hero shows up! */ /* Set the new min and max pointers. */ gv.viz_rmin = next_rmin; gv.viz_rmax = next_rmax; - recalc_mapseen(); + notice_all_mons(TRUE); } /* @@ -822,7 +826,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 : @@ -867,9 +881,9 @@ unblock_point(int x, int y) * + If you are a blocked spot, then your right will point to the * right-most blocked spot to your right that is connected to you. * This means that a right-edge (a blocked spot that has an open - * spot on its right) will point to itself. + * spot on its right) will point to itself. */ -static void +staticfn void dig_point(int row, int col) { int i; @@ -953,7 +967,7 @@ dig_point(int row, int col) } } -static void +staticfn void fill_point(int row, int col) { int i; @@ -1118,7 +1132,7 @@ static genericptr_t varg; #define q1_path(srow, scol, y2, x2, label) \ { \ int dx, dy; \ - register int k, err, x, y, dxs, dys; \ + int k, err, x, y, dxs, dys; \ \ x = (scol); \ y = (srow); \ @@ -1166,7 +1180,7 @@ static genericptr_t varg; #define q4_path(srow, scol, y2, x2, label) \ { \ int dx, dy; \ - register int k, err, x, y, dxs, dys; \ + int k, err, x, y, dxs, dys; \ \ x = (scol); \ y = (srow); \ @@ -1215,7 +1229,7 @@ static genericptr_t varg; #define q2_path(srow, scol, y2, x2, label) \ { \ int dx, dy; \ - register int k, err, x, y, dxs, dys; \ + int k, err, x, y, dxs, dys; \ \ x = (scol); \ y = (srow); \ @@ -1263,7 +1277,7 @@ static genericptr_t varg; #define q3_path(srow, scol, y2, x2, label) \ { \ int dx, dy; \ - register int k, err, x, y, dxs, dys; \ + int k, err, x, y, dxs, dys; \ \ x = (scol); \ y = (srow); \ @@ -1308,10 +1322,10 @@ static genericptr_t varg; #else /* !MACRO_CPATH -- quadrants are really functions */ -static int _q1_path(int, int, int, int); -static int _q2_path(int, int, int, int); -static int _q3_path(int, int, int, int); -static int _q4_path(int, int, int, int); +staticfn int _q1_path(int, int, int, int); +staticfn int _q2_path(int, int, int, int); +staticfn int _q3_path(int, int, int, int); +staticfn int _q4_path(int, int, int, int); #define q1_path(sy, sx, y, x, dummy) result = _q1_path(sy, sx, y, x) #define q2_path(sy, sx, y, x, dummy) result = _q2_path(sy, sx, y, x) @@ -1321,11 +1335,11 @@ static int _q4_path(int, int, int, int); /* * Quadrant I (step < 0). */ -static int +staticfn int _q1_path(int scol, int srow, int y2, int x2) { int dx, dy; - register int k, err, x, y, dxs, dys; + int k, err, x, y, dxs, dys; x = scol; y = srow; @@ -1368,11 +1382,11 @@ _q1_path(int scol, int srow, int y2, int x2) /* * Quadrant IV (step > 0). */ -static int +staticfn int _q4_path(int scol, int srow, int y2, int x2) { int dx, dy; - register int k, err, x, y, dxs, dys; + int k, err, x, y, dxs, dys; x = scol; y = srow; @@ -1415,11 +1429,11 @@ _q4_path(int scol, int srow, int y2, int x2) /* * Quadrant II (step < 0). */ -static int +staticfn int _q2_path(int scol, int srow, int y2, int x2) { int dx, dy; - register int k, err, x, y, dxs, dys; + int k, err, x, y, dxs, dys; x = scol; y = srow; @@ -1462,11 +1476,11 @@ _q2_path(int scol, int srow, int y2, int x2) /* * Quadrant III (step > 0). */ -static int +staticfn int _q3_path(int scol, int srow, int y2, int x2) { int dx, dy; - register int k, err, x, y, dxs, dys; + int k, err, x, y, dxs, dys; x = scol; y = srow; @@ -1549,11 +1563,11 @@ clear_path(int col1, int row1, int col2, int row2) /* * Defines local to Algorithm C. */ -static void right_side(int, int, int, const coordxy *); -static void left_side(int, int, int, const coordxy *); +staticfn void right_side(int, int, int, const coordxy *); +staticfn void left_side(int, int, int, const coordxy *); /* Initialize algorithm C (nothing). */ -static void +staticfn void view_init(void) { } @@ -1568,7 +1582,7 @@ view_init(void) * right_mark last (right side) visible spot on prev row * limits points at range limit for current row, or NULL */ -static void +staticfn void right_side( int row, int left, @@ -1580,8 +1594,8 @@ right_side( int nrow; /* new row (calculate once) */ int deeper; /* if TRUE, call self as needed */ int result; /* set by q?_path() */ - register int i; /* loop counter */ - register seenV *rowp = NULL; /* row optimization */ + int i; /* loop counter */ + seenV *rowp = NULL; /* row optimization */ coordxy *row_min = NULL; /* left most [used by macro set_min()] */ coordxy *row_max = NULL; /* right most [used by macro set_max()] */ int lim_max; /* right most limit of circle */ @@ -1760,7 +1774,7 @@ right_side( * This routine is the mirror image of right_side(). See right_side() for * extensive comments. */ -static void +staticfn void left_side( int row, int left_mark, @@ -1768,8 +1782,8 @@ left_side( const coordxy *limits) { int left, left_edge, nrow, deeper, result; - register int i; - register seenV *rowp = NULL; + int i; + seenV *rowp = NULL; coordxy *row_min = NULL; coordxy *row_max = NULL; int lim_min; @@ -1904,7 +1918,7 @@ left_side( * func function to call on each spot * arg argument for func */ -static void +staticfn void view_from( coordxy srow, coordxy scol, seenV **loc_cs_rows, @@ -1913,7 +1927,7 @@ view_from( void (*func)(coordxy, coordxy, genericptr_t), genericptr_t arg) { - register int i; /* loop counter */ + int i; /* loop counter */ seenV *rowp; /* optimization for setting could_see */ int nrow; /* the next row */ int left; /* the left-most visible column */ @@ -2021,15 +2035,15 @@ do_clear_area( view_from(srow, scol, (seenV **) 0, (coordxy *) 0, (coordxy *) 0, range, func, arg); } else { - register int x; + int x; int y, min_x, max_x, max_y, offset; const coordxy *limits; boolean override_vision; /* vision doesn't pass through water or clouds, detection should [this probably ought to be an arg supplied by our caller...] */ - override_vision = - (Is_waterlevel(&u.uz) || Is_airlevel(&u.uz)) && detecting(func); + override_vision = (detecting(func) + && (Is_waterlevel(&u.uz) || Is_airlevel(&u.uz))); if (range > MAX_RADIUS || range < 1) panic("do_clear_area: illegal range %d", range); @@ -2042,8 +2056,8 @@ do_clear_area( y = 0; for (; y <= max_y; y++) { offset = limits[v_abs(y - srow)]; - if ((min_x = (scol - offset)) < 0) - min_x = 0; + if ((min_x = (scol - offset)) < 1) + min_x = 1; if ((max_x = (scol + offset)) >= COLNO) max_x = COLNO - 1; for (x = min_x; x <= max_x; x++) @@ -2061,6 +2075,7 @@ howmonseen(struct monst *mon) int xraydist = (u.xray_range < 0) ? -1 : (u.xray_range * u.xray_range); unsigned how_seen = 0; /* result */ + /* assert(mon != NULL) */ /* normal vision; cansee is true for both normal and astral vision, but couldsee it not true for astral vision */ diff --git a/src/weapon.c b/src/weapon.c index 8ab2d276c8..7ed5e5c65d 100644 --- a/src/weapon.c +++ b/src/weapon.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 weapon.c $NHDT-Date: 1646688071 2022/03/07 21:21:11 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.100 $ */ +/* NetHack 3.7 weapon.c $NHDT-Date: 1725227810 2024/09/01 21:56:50 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.128 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -10,12 +10,12 @@ */ #include "hack.h" -static void give_may_advance_msg(int); -static void finish_towel_change(struct obj *obj, int); -static boolean could_advance(int); -static boolean peaked_skill(int); -static int slots_required(int); -static void skill_advance(int); +staticfn void give_may_advance_msg(int); +staticfn void finish_towel_change(struct obj *obj, int) NONNULLARG1; +staticfn boolean could_advance(int); +staticfn boolean peaked_skill(int); +staticfn int slots_required(int); +staticfn void skill_advance(int); /* Categories whose names don't come from OBJ_NAME(objects[type]) */ @@ -66,10 +66,12 @@ static NEARDATA const char *const barehands_or_martial[] = { ? barehands_or_martial[martial_bonus()] \ : odd_skill_names[-skill_names_indices[type]]) -static NEARDATA const char kebabable[] = { S_XORN, S_DRAGON, S_JABBERWOCK, - S_NAGA, S_GIANT, '\0' }; +/* targets that provide attacker with small to-hit bonus when using a spear */ +static NEARDATA const char kebabable[] = { + S_XORN, S_DRAGON, S_JABBERWOCK, S_NAGA, S_GIANT, '\0' +}; -static void +staticfn void give_may_advance_msg(int skill) { You_feel("more confident in your %sskills.", @@ -77,7 +79,7 @@ give_may_advance_msg(int skill) : (skill <= P_LAST_WEAPON) ? "weapon " : (skill <= P_LAST_SPELL) ? "spell casting " : "fighting "); - handle_tip(TIP_ENHANCE); + (void) handle_tip(TIP_ENHANCE); } /* weapon's skill category name for use as generalized description of weapon; @@ -184,7 +186,7 @@ hitval(struct obj *otmp, struct monst *mon) } /* Historical note: The original versions of Hack used a range of damage - * which was similar to, but not identical to the damage used in Advanced + * which was similar to, but not identical to, the damage used in Advanced * Dungeons and Dragons. I figured that since it was so close, I may as well * make it exactly the same as AD&D, adding some more weapons in the process. * This has the advantage that it is at least possible that the player would @@ -497,7 +499,7 @@ special_dmgval(struct monst *magr, */ void searmsg( - struct monst *magr, + struct monst *magr, /* can be null if mdef is searing themselves */ struct monst *mdef, struct obj *obj, /* the offending item, or &cg.zeroobj if magr's body */ boolean minimal) /* print a shorter message leaving out obj details */ @@ -615,19 +617,27 @@ static struct obj *oselect(struct monst *, int, boolean); } while (0) static struct obj * -oselect(struct monst *mtmp, int otyp, boolean missile) +oselect(struct monst *mtmp, int type, boolean missile) { struct obj *otmp; for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) { - if (otmp->otyp == otyp - /* never select non-cockatrice corpses */ - && !((otyp == CORPSE || otyp == EGG) - && !touch_petrifies(&mons[otmp->corpsenm])) - && (!otmp->oartifact || touch_artifact(otmp, mtmp)) - && !mon_hates_material(mtmp, otmp->material) - && !(missile && undroppable(otmp))) - return otmp; + if (otmp->otyp != type) + continue; + + /* never select non-cockatrice corpses */ + if ((type == CORPSE || type == EGG) + && (otmp->corpsenm == NON_PM + || !touch_petrifies(&mons[otmp->corpsenm]))) + continue; + + if (!can_touch_safely(mtmp, otmp)) + continue; + + if (missile && undroppable(otmp)) + continue; + + return otmp; } return (struct obj *) 0; } @@ -647,7 +657,7 @@ static NEARDATA const int pwep[] = { HALBERD, GLAIVE, BEC_DE_CORBIN, struct obj * select_rwep(struct monst *mtmp) { - register struct obj *otmp; + struct obj *otmp; struct obj *mwep; boolean mweponly; int i; @@ -712,7 +722,7 @@ select_rwep(struct monst *mtmp) } /* KMH -- This belongs here so darts will work */ - gp.propellor = (struct obj *) &cg.zeroobj; + gp.propellor = &hands_obj; prop = objects[rwep[i]].oc_skill; if (prop < 0) { @@ -736,9 +746,9 @@ select_rwep(struct monst *mtmp) && mtmp->weapon_check == NO_WEAPON_WANTED) gp.propellor = 0; } - /* gp.propellor = obj, propellor to use - * gp.propellor = &cg.zeroobj, doesn't need a propellor - * gp.propellor = 0, needed one and didn't have one + /* propellor = obj, propellor to use + * propellor = &hands_obj, doesn't need a propellor + * propellor = 0, needed one and didn't have one */ if (gp.propellor != 0) { /* Don't throw a cursed weapon-in-hand or an artifact */ @@ -782,8 +792,8 @@ static const NEARDATA short hwep[] = { struct obj * select_hwep(struct monst *mtmp) { - register struct obj *otmp; - register int i; + struct obj *otmp; + int i; boolean strong = strongmonst(mtmp->data); boolean wearing_shield = (mtmp->misc_worn_check & W_ARMS) != 0; @@ -799,6 +809,8 @@ select_hwep(struct monst *mtmp) if (is_giant(mtmp->data)) /* giants just love to use clubs */ Oselect(CLUB, FALSE); + else if (mtmp->data == &mons[PM_BALROG] && uwep) + Oselect(BULLWHIP, FALSE); /* only strong monsters can wield big (esp. long) weapons */ /* big weapon is basically the same as bimanual */ @@ -838,7 +850,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); @@ -941,7 +953,7 @@ mon_wield_item(struct monst *mon) mon_nam(mon)); return 0; } - if (obj && obj != &cg.zeroobj) { + if (obj && obj != &hands_obj) { struct obj *mw_tmp = MON_WEP(mon); if (mw_tmp && mw_tmp->otyp == obj->otyp) { @@ -970,7 +982,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; @@ -984,8 +996,9 @@ mon_wield_item(struct monst *mon) if (!gi.in_mklev && canseemon(mon)) { boolean newly_welded; - pline("%s wields %s%c", Monnam(mon), doname(obj), - exclaim ? '!' : '.'); + pline_mon(mon, "%s wields %s%c", + Monnam(mon), doname(obj), + exclaim ? '!' : '.'); /* 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 @@ -1101,7 +1114,7 @@ dbon(void) } /* called when wet_a_towel() or dry_a_towel() is changing a towel's wetness */ -static void +staticfn void finish_towel_change(struct obj *obj, int newspe) { /* towel wetness is always between 0 (dry) and 7, inclusive */ @@ -1120,10 +1133,10 @@ finish_towel_change(struct obj *obj, int newspe) /* increase a towel's wetness */ void -wet_a_towel(struct obj *obj, - int amt, /* positive: new value; negative: increment by -amt; - zero: no-op */ - boolean verbose) +wet_a_towel( + struct obj *obj, + int amt, /* positive: new val; negative: increment by -amt; zero: no-op */ + boolean verbose) { int newspe = (amt <= 0) ? obj->spe - amt : amt; @@ -1243,7 +1256,7 @@ skill_name(int skill) } /* return the # of slots required to advance the skill */ -static int +staticfn int slots_required(int skill) { int tmp = P_SKILL(skill); @@ -1284,7 +1297,7 @@ can_advance(int skill, boolean speedy) } /* return true if this skill could be advanced if more slots were available */ -static boolean +staticfn boolean could_advance(int skill) { if (P_RESTRICTED(skill) @@ -1298,7 +1311,7 @@ could_advance(int skill) /* return true if this skill has reached its maximum and there's been enough practice to become eligible for the next step if that had been possible */ -static boolean +staticfn boolean peaked_skill(int skill) { if (P_RESTRICTED(skill)) @@ -1309,7 +1322,7 @@ peaked_skill(int skill) >= practice_needed_to_advance(P_SKILL(skill)))); } -static void +staticfn void skill_advance(int skill) { u.weapon_slots -= slots_required(skill); @@ -1324,6 +1337,12 @@ skill_advance(int skill) You("are now %s skilled in %s.", P_SKILL(skill) >= P_MAX_SKILL(skill) ? "most" : "more", P_NAME(skill)); + + /* wizards discover spellbook IDs depending on spell 'school' skill limits; + this allows them to successfully write books for unknown spells without + the Luck bias they used to have over other roles */ + if (skill >= P_FIRST_SPELL && skill <= P_LAST_SPELL) + skill_based_spellbook_id(); } static const struct skill_range { @@ -1353,10 +1372,10 @@ enhance_weapon_skill(void) anything any; winid win; boolean speedy = FALSE; - int clr = 0; + int clr = NO_COLOR; /* player knows about #enhance, don't show tip anymore */ - gc.context.tips[TIP_ENHANCE] = TRUE; + svc.context.tips[TIP_ENHANCE] = TRUE; if (wizard && y_n("Advance skills without practice?") == 'y') speedy = TRUE; @@ -1383,25 +1402,21 @@ enhance_weapon_skill(void) /* start with a legend if any entries will be annotated with "*" or "#" below */ if (eventually_advance > 0 || maxxed_cnt > 0) { - any = cg.zeroany; if (eventually_advance > 0) { Sprintf(buf, "(Skill%s flagged by \"*\" may be enhanced %s.)", plur(eventually_advance), (u.ulevel < MAXULEV) ? "when you're more experienced" : "if skill slots become available"); - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, buf, MENU_ITEMFLAGS_NONE); + add_menu_str(win, buf); } if (maxxed_cnt > 0) { Sprintf(buf, "(Skill%s flagged by \"#\" cannot be enhanced any further.)", plur(maxxed_cnt)); - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, buf, MENU_ITEMFLAGS_NONE); + add_menu_str(win, buf); } - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); + add_menu_str(win, ""); } /* List the skills, making ones that could be advanced @@ -1414,9 +1429,7 @@ enhance_weapon_skill(void) /* Print headings for skill types */ any = cg.zeroany; if (i == skill_ranges[pass].first) - add_menu(win, &nul_glyphinfo, &any, 0, 0, - iflags.menu_headings, clr, - skill_ranges[pass].name, MENU_ITEMFLAGS_NONE); + add_menu_heading(win, skill_ranges[pass].name); if (P_RESTRICTED(i)) continue; @@ -1454,20 +1467,24 @@ enhance_weapon_skill(void) if (wizard) { if (!iflags.menu_tab_sep) - Snprintf(buf, sizeof(buf), " %s%-*s %-12s %5d(%4d)", prefix, - longest, P_NAME(i), sklnambuf, P_ADVANCE(i), - practice_needed_to_advance(P_SKILL(i))); + Snprintf(buf, sizeof(buf), + " %s%-*s %-12s %5d(%4d)", prefix, + longest, P_NAME(i), sklnambuf, P_ADVANCE(i), + practice_needed_to_advance(P_SKILL(i))); else - Snprintf(buf, sizeof(buf), " %s%s\t%s\t%5d(%4d)", prefix, P_NAME(i), - sklnambuf, P_ADVANCE(i), - practice_needed_to_advance(P_SKILL(i))); + Snprintf(buf, sizeof(buf), + " %s%s\t%s\t%5d(%4d)", prefix, P_NAME(i), + sklnambuf, P_ADVANCE(i), + practice_needed_to_advance(P_SKILL(i))); } else { if (!iflags.menu_tab_sep) - Snprintf(buf, sizeof(buf), " %s %-*s [%12s / %-12s] %4s", + Snprintf(buf, sizeof(buf), + " %s %-*s [%12s / %-12s] %4s", prefix, longest, P_NAME(i), sklnambuf, maxsklnambuf, percentbuf); else - Snprintf(buf, sizeof(buf), " %s%s\t[%s\t /%s] %4s", + Snprintf(buf, sizeof(buf), + " %s%s\t[%s\t /%s] %4s", prefix, P_NAME(i), sklnambuf, maxsklnambuf, percentbuf); } @@ -1634,6 +1651,7 @@ uwep_skill_type(void) /* * Return hit bonus/penalty based on skill of weapon. + * weapon can be null, meaning bare-handed combat. * Treat restricted weapons as unskilled. */ int @@ -1653,7 +1671,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; @@ -1674,7 +1694,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; @@ -1728,6 +1750,7 @@ weapon_hit_bonus(struct obj *weapon) /* * Return damage bonus/penalty based on skill of weapon. + * weapon can be null, meaning bare-handed combat. * Treat restricted weapons as unskilled. */ int @@ -1747,7 +1770,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; @@ -1890,6 +1914,9 @@ skill_init(const struct def_skill *class_skill) P_ADVANCE(skill) = practice_needed_to_advance(P_SKILL(skill) - 1); } } + + if (!u.uroleplay.pauper) /* paupers lack advanced access to books */ + skill_based_spellbook_id(); } void diff --git a/src/were.c b/src/were.c index bccd4d57c1..edea871b41 100644 --- a/src/were.c +++ b/src/were.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 were.c $NHDT-Date: 1596498227 2020/08/03 23:43:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.25 $ */ +/* NetHack 3.7 were.c $NHDT-Date: 1717570494 2024/06/05 06:54:54 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.36 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -106,6 +106,12 @@ new_were(struct monst *mon) { int pm; + /* neither hero nor werecreature can change from human form to + critter form if hero has Protection_from_shape_changers extrinsic; + if already in critter form, always change to human form for that */ + if (Protection_from_shape_changers && is_human(mon->data)) + return; + pm = counter_were(monsndx(mon->data)); if (pm < LOW_PM) { impossible("unknown lycanthrope %s.", @@ -116,6 +122,7 @@ new_were(struct monst *mon) if (canseemon(mon) && !Hallucination) pline("%s changes into a %s.", Monnam(mon), is_human(&mons[pm]) ? "human" + /* pmname()+4: skip past "were" prefix */ : pmname(&mons[pm], Mgender(mon)) + 4); set_mon_data(mon, &mons[pm]); @@ -126,10 +133,17 @@ 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); + + /* vision capability isn't changing so we don't call set_apparxy() to + update mon's idea of where hero is; peaceful check is redundant */ + if (svc.context.mon_moving && !mon->mpeaceful + && onscary(mon->mux, mon->muy, mon) + && monnear(mon, mon->mux, mon->muy)) + monflee(mon, rn1(9, 2), TRUE, TRUE); /* 2..10 turns */ } /* were-creature (even you) summons a horde */ @@ -178,7 +192,7 @@ were_summon( *visible += 1; } if (yours && mtmp) - (void) tamedog(mtmp, (struct obj *) 0, FALSE); + (void) tamedog(mtmp, (struct obj *) 0, FALSE, FALSE); } return total; } diff --git a/src/wield.c b/src/wield.c index 8904242eed..5ade267c1e 100644 --- a/src/wield.c +++ b/src/wield.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 wield.c $NHDT-Date: 1650875488 2022/04/25 08:31:28 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.90 $ */ +/* NetHack 3.7 wield.c $NHDT-Date: 1707525193 2024/02/10 00:33:13 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.110 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2009. */ /* NetHack may be freely redistributed. See license for details. */ @@ -52,9 +52,10 @@ * No item may be in more than one of these slots. */ -static int ready_weapon(struct obj *); -static int ready_ok(struct obj *); -static int wield_ok(struct obj *); +staticfn int ready_weapon(struct obj *) NO_NNARGS; +staticfn int ready_ok(struct obj *) NO_NNARGS; +staticfn int wield_ok(struct obj *) NO_NNARGS; +staticfn void finish_splitting(struct obj *); /* to dual-wield, 'obj' must be a weapon or a weapon-tool, and not a bow or arrow or missile (dart, shuriken, boomerang), so not matching the @@ -62,7 +63,7 @@ static int wield_ok(struct obj *); empty hands and two-handed weapons have to be handled separately */ #define TWOWEAPOK(obj) \ (((obj)->oclass == WEAPON_CLASS) \ - ? !(is_launcher(obj) ||is_ammo(obj) || is_missile(obj)) \ + ? !(is_launcher(obj) || is_ammo(obj) || is_missile(obj)) \ : is_weptool(obj)) static const char @@ -103,7 +104,7 @@ setuwep(struct obj *obj) if (uwep == obj && (u_wield_art(ART_OGRESMASHER) || is_art(olduwep, ART_OGRESMASHER))) - gc.context.botl = 1; + disp.botl = TRUE; /* Note: Explicitly wielding a pick-axe will not give a "bashing" * message. Wielding one via 'a'pplying it will. * 3.2.2: Wielding arbitrary objects will give bashing message too. @@ -115,7 +116,7 @@ setuwep(struct obj *obj) || (is_pole(obj) && !u.usteed) : !is_weptool(obj) && !is_wet_towel(obj); if (skill != P_NONE && P_SKILL(skill) < P_BASIC - && gm.moves > 1) { + && svm.moves > 1) { /* check moves because starting weapon is wielded before skills are * initialized */ gu.unweapon = TRUE; @@ -160,7 +161,7 @@ empty_handed(void) : "not wielding anything"; } -static int +staticfn int ready_weapon(struct obj *wep) { /* Separated function so swapping works easily */ @@ -198,8 +199,10 @@ ready_weapon(struct obj *wep) tmp = thestr; else tmp = ""; - pline("%s%s %s to your %s!", tmp, aobjnam(wep, "weld"), + pline("%s%s %s to your %s%s!", tmp, aobjnam(wep, "weld"), (wep->quan == 1L) ? "itself" : "themselves", /* a3 */ + bimanual(wep) ? "" : + (URIGHTY ? "dominant right " : "dominant left "), bimanual(wep) ? (const char *) makeplural(body_part(HAND)) : body_part(HAND)); set_bknown(wep, 1); @@ -224,7 +227,7 @@ ready_weapon(struct obj *wep) } setuwep(wep); - if (was_twoweap && !u.twoweap && Verbose(1, ready_weapon)) { + if (was_twoweap && !u.twoweap && flags.verbose) { /* skip this message if we already got "empty handed" one above; also, Null is not safe for neither TWOWEAPOK() or bimanual() */ if (uwep) @@ -234,7 +237,7 @@ ready_weapon(struct obj *wep) } /* KMH -- Talking artifacts are finally implemented */ - if (wep && wep->oartifact) { + if (wep->oartifact) { res |= arti_speak(wep); /* sets ECMD_TIME bit if artifact speaks */ } @@ -264,7 +267,7 @@ ready_weapon(struct obj *wep) } } if ((had_wep != (uwep != 0)) && condtests[bl_bareh].enabled) - gc.context.botl = 1; + disp.botl = TRUE; return res; } @@ -286,11 +289,11 @@ setuswapwep(struct obj *obj) /* getobj callback for object to ready for throwing/shooting; this filter lets worn items through so that caller can reject them */ -static int +staticfn int ready_ok(struct obj *obj) { - if (!obj) - return GETOBJ_SUGGEST; /* '-', will empty quiver slot if chosen */ + if (!obj) /* '-', will empty quiver slot if chosen */ + return uquiver ? GETOBJ_SUGGEST : GETOBJ_DOWNPLAY; /* downplay when wielded, unless more than one */ if (obj == uwep || (obj == uswapwep && u.twoweap)) @@ -323,7 +326,7 @@ ready_ok(struct obj *obj) } /* getobj callback for object to wield */ -static int +staticfn int wield_ok(struct obj *obj) { if (!obj) @@ -338,13 +341,20 @@ wield_ok(struct obj *obj) return GETOBJ_DOWNPLAY; } +staticfn void +finish_splitting(struct obj *obj) +{ + /* obj was split off from something; give it its own invlet */ + freeinv(obj); + addinv_nomerge(obj); +} + /* the #wield command - wield a weapon */ int dowield(void) { char qbuf[QBUFSZ]; struct obj *wep, *oldwep; - boolean finish_splitting = FALSE; int result; /* May we attempt this? */ @@ -353,6 +363,9 @@ dowield(void) pline("Don't be ridiculous!"); return ECMD_FAIL; } + /* Keep going even if inventory is completely empty, since wielding '-' + to wield nothing can be construed as a positive act even when done + so redundantly. */ /* Prompt for a new weapon */ clear_splitobjs(); @@ -370,31 +383,32 @@ dowield(void) /* previously interrupted armor removal mustn't be resumed */ reset_remarm(); /* if player chose a partial stack but can't wield it, undo split */ - if (wep->o_id && wep->o_id == gc.context.objsplit.child_oid) + if (wep->o_id && wep->o_id == svc.context.objsplit.child_oid) unsplitobj(wep); return ECMD_FAIL; - } else if (wep->o_id && wep->o_id == gc.context.objsplit.child_oid) { + } else if (wep->o_id && wep->o_id == svc.context.objsplit.child_oid) { /* if wep is the result of supplying a count to getobj() we don't want to split something already wielded; for any other item, we need to give it its own inventory slot */ - if (uwep && uwep->o_id == gc.context.objsplit.parent_oid) { + if (uwep && uwep->o_id == svc.context.objsplit.parent_oid) { unsplitobj(wep); /* wep was merged back to uwep, already_wielded uses wep */ wep = uwep; goto already_wielded; } - finish_splitting = TRUE; + finish_splitting(wep); goto wielding; } /* Handle no object, or object in other slot */ - if (wep == &cg.zeroobj) { + if (wep == &hands_obj) { wep = (struct obj *) 0; } else if (wep == uswapwep) { return doswapweapon(); } else if (wep == uquiver) { /* offer to split stack if multiple are quivered */ - if (uquiver->quan > 1L && inv_cnt(FALSE) < 52 && splittable(uquiver)) { + if (uquiver->quan > 1L && inv_cnt(FALSE) < invlet_basic + && splittable(uquiver)) { Sprintf(qbuf, "You have %ld %s readied. Wield one?", uquiver->quan, simpleonames(uquiver)); switch (ynq(qbuf)) { @@ -403,7 +417,7 @@ dowield(void) case 'y': /* leave N-1 quivered, split off 1 to wield */ wep = splitobj(uquiver, 1L); - finish_splitting = TRUE; + finish_splitting(wep); goto wielding; default: break; @@ -431,14 +445,6 @@ dowield(void) } wielding: - if (finish_splitting) { - /* wep was split off from something; give it its own invlet */ - freeinv(wep); - wep->nomerge = 1; - addinv(wep); - wep->nomerge = 0; - } - /* Set your new primary weapon */ oldwep = uwep; result = ready_weapon(wep); @@ -453,7 +459,7 @@ dowield(void) int doswapweapon(void) { - register struct obj *oldwep, *oldswap; + struct obj *oldwep, *oldswap; int result = 0; /* May we attempt this? */ @@ -507,22 +513,27 @@ doquiver_core(const char *verb) /* "ready" or "fire" */ char qbuf[QBUFSZ]; struct obj *newquiver; int res; - boolean finish_splitting = FALSE, - was_uwep = FALSE, was_twoweap = u.twoweap; + boolean was_uwep = FALSE, was_twoweap = u.twoweap; - /* Since the quiver isn't in your hands, don't check cantwield(), */ - /* will_weld(), touch_petrifies(), etc. */ + /* Since the quiver isn't in your hands, don't check cantwield(), + will_weld(), touch_petrifies(), etc. */ gm.multi = 0; + if (!gi.invent) { + /* could accept '-' to empty quiver, but there's no point since + inventory is empty so uquiver is already Null */ + You("have nothing to ready for firing."); + return ECMD_OK; + } + /* forget last splitobj() before calling getobj() with GETOBJ_ALLOWCNT */ clear_splitobjs(); - /* Prompt for a new quiver: "What do you want to {ready|fire}?" */ newquiver = getobj(verb, ready_ok, GETOBJ_PROMPT | GETOBJ_ALLOWCNT); if (!newquiver) { /* Cancelled */ return ECMD_CANCEL; - } else if (newquiver == &cg.zeroobj) { /* no object */ + } else if (newquiver == &hands_obj) { /* no object */ /* Explicitly nothing */ if (uquiver) { You("now have no ammunition readied."); @@ -532,11 +543,11 @@ doquiver_core(const char *verb) /* "ready" or "fire" */ You("already have no ammunition readied!"); } return ECMD_OK; - } else if (newquiver->o_id == gc.context.objsplit.child_oid) { + } else if (newquiver->o_id == svc.context.objsplit.child_oid) { /* if newquiver is the result of supplying a count to getobj() we don't want to split something already in the quiver; for any other item, we need to give it its own inventory slot */ - if (uquiver && uquiver->o_id == gc.context.objsplit.parent_oid) { + if (uquiver && uquiver->o_id == svc.context.objsplit.parent_oid) { unsplitobj(newquiver); goto already_quivered; } else if (newquiver->oclass == COIN_CLASS) { @@ -545,13 +556,7 @@ doquiver_core(const char *verb) /* "ready" or "fire" */ unsplitobj(newquiver); return ECMD_OK; } - else if (newquiver->oclass == COIN_CLASS) { - /* don't allow splitting a stack of coins into quiver */ - You("can't ready only part of your gold."); - unsplitobj(newquiver); - return 0; - } - finish_splitting = TRUE; + finish_splitting(newquiver); } else if (newquiver == uquiver) { already_quivered: pline("That ammunition is already readied!"); @@ -568,7 +573,8 @@ doquiver_core(const char *verb) /* "ready" or "fire" */ return weld_res ? ECMD_TIME : ECMD_OK; } /* offer to split stack if wielding more than 1 */ - if (uwep->quan > 1L && inv_cnt(FALSE) < 52 && splittable(uwep)) { + if (uwep->quan > 1L && inv_cnt(FALSE) < invlet_basic + && splittable(uwep)) { Sprintf(qbuf, "You are wielding %ld %s. Ready %ld of them?", uwep->quan, simpleonames(uwep), uwep->quan - 1L); switch (ynq(qbuf)) { @@ -577,7 +583,7 @@ doquiver_core(const char *verb) /* "ready" or "fire" */ case 'y': /* leave 1 wielded, split rest off and put into quiver */ newquiver = splitobj(uwep, uwep->quan - 1L); - finish_splitting = TRUE; + finish_splitting(newquiver); goto quivering; default: break; @@ -602,7 +608,7 @@ doquiver_core(const char *verb) /* "ready" or "fire" */ untwoweapon(); was_uwep = TRUE; } else if (newquiver == uswapwep) { - if (uswapwep->quan > 1L && inv_cnt(FALSE) < 52 + if (uswapwep->quan > 1L && inv_cnt(FALSE) < invlet_basic && splittable(uswapwep)) { Sprintf(qbuf, "%s %ld %s. Ready %ld of them?", u.twoweap ? "You are dual wielding" @@ -615,7 +621,7 @@ doquiver_core(const char *verb) /* "ready" or "fire" */ case 'y': /* leave 1 alt-wielded, split rest off and put into quiver */ newquiver = splitobj(uswapwep, uswapwep->quan - 1L); - finish_splitting = TRUE; + finish_splitting(newquiver); goto quivering; default: break; @@ -643,13 +649,6 @@ doquiver_core(const char *verb) /* "ready" or "fire" */ } quivering: - if (finish_splitting) { - freeinv(newquiver); - newquiver->nomerge = 1; - addinv(newquiver); - newquiver->nomerge = 0; - } - if (!strcmp(verb, "ready")) { /* place item in quiver before printing so that inventory feedback includes "(at the ready)" */ @@ -686,7 +685,7 @@ wield_tool(struct obj *obj, const char *what; boolean more_than_1; - if (obj == uwep) + if (uwep && obj == uwep) return TRUE; /* nothing to do if already wielding it */ if (!verb) @@ -700,8 +699,8 @@ wield_tool(struct obj *obj, more_than_1 ? "them" : "it"); return FALSE; } - if (welded(uwep)) { - if (Verbose(1, wield_tool)) { + if (uwep && welded(uwep)) { + if (flags.verbose) { const char *hand = body_part(HAND); if (bimanual(uwep)) @@ -747,7 +746,7 @@ wield_tool(struct obj *obj, if (flags.pushweapon && oldwep && uwep != oldwep) setuswapwep(oldwep); } - if (uwep != obj) + if (uwep && uwep != obj) return FALSE; /* rewielded old object after dying */ /* applying weapon or tool that gets wielded ends two-weapon combat */ if (u.twoweap) @@ -811,15 +810,30 @@ can_twoweapon(void) return FALSE; } +/* uswapwep has become cursed while in two-weapon combat mode or hero is + attempting to dual-wield when it is already cursed or hands are slippery */ void drop_uswapwep(void) { - char str[BUFSZ]; + char left_hand[QBUFSZ]; struct obj *obj = uswapwep; - /* Avoid trashing makeplural's static buffer */ - Strcpy(str, makeplural(body_part(HAND))); - pline("%s from your %s!", Yobjnam2(obj, "slip"), str); + /* this used to use makeplural(body_part(HAND)) but in order to be + dual-wielded, or to get this far attempting to achieve that, + uswapwep must be one-handed; since it's secondary, the hand must + be the left one */ + Sprintf(left_hand, "left %s", body_part(HAND)); + if (!obj->cursed) + /* attempting to two-weapon while Glib */ + pline("%s from your %s!", Yobjnam2(obj, "slip"), left_hand); + else if (!u.twoweap) + /* attempting to two-weapon when uswapwep is cursed */ + pline("%s your grasp and %s from your %s!", + Yobjnam2(obj, "evade"), otense(obj, "drop"), left_hand); + else + /* already two-weaponing but can't anymore because uswapwep has + become cursed */ + Your("%s spasms and drops %s!", left_hand, yobjnam(obj, (char *) 0)); dropx(obj); } diff --git a/src/windows.c b/src/windows.c index 7dfa73107e..fa63647e35 100644 --- a/src/windows.c +++ b/src/windows.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 windows.c $NHDT-Date: 1661202202 2022/08/22 21:03:22 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.97 $ */ +/* 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. */ @@ -62,35 +62,42 @@ extern void trace_procs_init(int); extern void *trace_procs_chain(int, int, void *, void *, void *); #endif -static void def_raw_print(const char *s); -static void def_wait_synch(void); +#if defined(WINCHAIN) || defined(TTY_GRAPHICS) +staticfn struct win_choices *win_choices_find(const char *s) NONNULLARG1; +#endif -#if defined(DUMPLOG) || defined(DUMPHTML) -static winid dump_create_nhwindow(int); -static void dump_clear_nhwindow(winid); -static void dump_display_nhwindow(winid, boolean); -static void dump_destroy_nhwindow(winid); -static void dump_start_menu(winid, unsigned long); -static void dump_add_menu(winid, const glyph_info *, const ANY_P *, char, +staticfn void def_raw_print(const char *s) NONNULLARG1; +staticfn void def_wait_synch(void); +staticfn boolean get_menu_coloring(const char *, int *, int *) NONNULLPTRS; + +staticfn winid dump_create_nhwindow(int); +staticfn void dump_clear_nhwindow(winid); +staticfn void dump_display_nhwindow(winid, boolean); +staticfn void dump_destroy_nhwindow(winid); +staticfn void dump_start_menu(winid, unsigned long); +staticfn void dump_add_menu(winid, const glyph_info *, const ANY_P *, char, char, int, int, const char *, unsigned int); -static void dump_end_menu(winid, const char *); -static int dump_select_menu(winid, int, MENU_ITEM_P **); -static void dump_putstr(winid, int, const char *); -static void dump_headers(void); -static void dump_footers(void); -static void dump_set_color_attr(int, int, boolean); +staticfn void dump_end_menu(winid, const char *); +staticfn int dump_select_menu(winid, int, MENU_ITEM_P **); +staticfn void dump_putstr(winid, int, const char *); + +/* HTML dumplog functions */ +/* these exist without DUMPHTML defined but are no-ops */ +staticfn void dump_headers(void); +staticfn void dump_footers(void); +staticfn void dump_set_color_attr(int, int, boolean); #ifdef DUMPHTML -static void html_write_tags(FILE *, int, boolean); -static void html_dump_char(FILE *, char); -static void html_dump_str(FILE *, const char *); -static void html_dump_line(FILE *, int, const char *); -static void dump_set_color_attr(int, int, boolean); -static void html_init_sym(void); -static void dump_css(void); -static void dump_outrip(winid, int, time_t); +staticfn void html_write_tags(FILE *, int, boolean); +staticfn void html_dump_char(FILE *, char); +staticfn void html_dump_str(FILE *, const char *); +staticfn void html_dump_line(FILE *, int, const char *); +staticfn void dump_set_color_attr(int, int, boolean); +staticfn void html_init_sym(void); +staticfn unsigned mg_hl_attr(unsigned special); +staticfn void dump_css(void); +staticfn void dump_outrip(winid, int, time_t); #endif /* DUMPHTML */ - -#endif /* DUMPLOG */ +/* end HTML dumplog functions */ #ifdef HANGUPHANDLING volatile @@ -167,7 +174,7 @@ struct winlink { static struct winlink *chain = 0; -static struct winlink * +staticfn struct winlink * wl_new(void) { struct winlink *wl = (struct winlink *) alloc(sizeof *wl); @@ -179,14 +186,14 @@ wl_new(void) return wl; } -static void +staticfn void wl_addhead(struct winlink *wl) { wl->nextlink = chain; chain = wl; } -static void +staticfn void wl_addtail(struct winlink *wl) { struct winlink *p = chain; @@ -215,14 +222,16 @@ genl_can_suspend_yes(void) return TRUE; } -static +staticfn void def_raw_print(const char *s) { puts(s); + if (*s) + iflags.raw_printed++; } -static +staticfn void def_wait_synch(void) { @@ -238,11 +247,33 @@ def_wait_synch(void) return; } -#ifdef WINCHAIN -static struct win_choices * +#ifdef TTY_GRAPHICS +boolean +check_tty_wincap(unsigned long wincap) +{ + struct win_choices *wc = win_choices_find("tty"); + + if (wc) + return ((wc->procs->wincap & wincap) == wincap); + return FALSE; +} + +boolean +check_tty_wincap2(unsigned long wincap2) +{ + struct win_choices *wc = win_choices_find("tty"); + + if (wc) + return ((wc->procs->wincap2 & wincap2) == wincap2); + return FALSE; +} +#endif + +#if defined(WINCHAIN) || defined(TTY_GRAPHICS) +staticfn struct win_choices * win_choices_find(const char *s) { - register int i; + int i; for (i = 0; winchoices[i].procs; i++) { if (!strcmpi(s, winchoices[i].procs->name)) { @@ -331,7 +362,7 @@ choose_windows(const char *s) void addto_windowchain(const char *s) { - register int i; + int i; for (i = 0; winchoices[i].procs; i++) { if ('+' != winchoices[i].procs->name[0]) @@ -510,44 +541,44 @@ genl_putmsghistory(const char *msg, boolean is_restoring) * in order to avoid all terminal I/O after hangup/disconnect. */ -static int hup_nhgetch(void); -static char hup_yn_function(const char *, const char *, char); -static int hup_nh_poskey(coordxy *, coordxy *, int *); -static void hup_getlin(const char *, char *); -static void hup_init_nhwindows(int *, char **); -static void hup_exit_nhwindows(const char *); -static winid hup_create_nhwindow(int); -static int hup_select_menu(winid, int, MENU_ITEM_P **); -static void hup_add_menu(winid, const glyph_info *, const anything *, char, +staticfn int hup_nhgetch(void); +staticfn char hup_yn_function(const char *, const char *, char); +staticfn int hup_nh_poskey(coordxy *, coordxy *, int *); +staticfn void hup_getlin(const char *, char *); +staticfn void hup_init_nhwindows(int *, char **); +staticfn void hup_exit_nhwindows(const char *); +staticfn winid hup_create_nhwindow(int); +staticfn int hup_select_menu(winid, int, MENU_ITEM_P **); +staticfn void hup_add_menu(winid, const glyph_info *, const anything *, char, char, int, int, const char *, unsigned int); -static void hup_end_menu(winid, const char *); -static void hup_putstr(winid, int, const char *); -static void hup_print_glyph(winid, coordxy, coordxy, const glyph_info *, +staticfn void hup_end_menu(winid, const char *); +staticfn void hup_putstr(winid, int, const char *); +staticfn void hup_print_glyph(winid, coordxy, coordxy, const glyph_info *, const glyph_info *); -static void hup_outrip(winid, int, time_t); -static void hup_curs(winid, int, int); -static void hup_display_nhwindow(winid, boolean); -static void hup_display_file(const char *, boolean); +staticfn void hup_outrip(winid, int, time_t); +staticfn void hup_curs(winid, int, int); +staticfn void hup_display_nhwindow(winid, boolean); +staticfn void hup_display_file(const char *, boolean); #ifdef CLIPPING -static void hup_cliparound(int, int); +staticfn void hup_cliparound(int, int); #endif #ifdef CHANGE_COLOR -static void hup_change_color(int, long, int); +staticfn void hup_change_color(int, long, int); #ifdef MAC -static short hup_set_font_name(winid, char *); +staticfn short hup_set_font_name(winid, char *); #endif -static char *hup_get_color_string(void); +staticfn char *hup_get_color_string(void); #endif /* CHANGE_COLOR */ -static void hup_status_update(int, genericptr_t, int, int, int, +staticfn void hup_status_update(int, genericptr_t, int, int, int, unsigned long *); -static int hup_int_ndecl(void); -static void hup_void_ndecl(void); -static void hup_void_fdecl_int(int); -static void hup_void_fdecl_winid(winid); -static void hup_void_fdecl_winid_ulong(winid, unsigned long); -static void hup_void_fdecl_constchar_p(const char *); -static win_request_info *hup_ctrl_nhwindow(winid, int, win_request_info *); +staticfn int hup_int_ndecl(void); +staticfn void hup_void_ndecl(void); +staticfn void hup_void_fdecl_int(int); +staticfn void hup_void_fdecl_winid(winid); +staticfn void hup_void_fdecl_winid_ulong(winid, unsigned long); +staticfn void hup_void_fdecl_constchar_p(const char *); +staticfn win_request_info *hup_ctrl_nhwindow(winid, int, win_request_info *); static struct window_procs hup_procs = { WPID(hup), 0L, 0L, @@ -588,8 +619,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 */ @@ -631,7 +660,7 @@ nhwindows_hangup(void) windowprocs.win_getmsghistory = previnterface_getmsghistory; } -static void +staticfn void hup_exit_nhwindows(const char *lastgasp) { /* core has called exit_nhwindows(); call the previous interface's @@ -642,17 +671,17 @@ hup_exit_nhwindows(const char *lastgasp) (*previnterface_exit_nhwindows)(lastgasp); previnterface_exit_nhwindows = 0; } - iflags.window_inited = 0; + iflags.window_inited = FALSE; } -static int +staticfn int hup_nhgetch(void) { return '\033'; /* ESC */ } /*ARGSUSED*/ -static char +staticfn char hup_yn_function( const char *prompt UNUSED, const char *resp UNUSED, @@ -664,35 +693,35 @@ hup_yn_function( } /*ARGSUSED*/ -static int +staticfn int hup_nh_poskey(coordxy *x UNUSED, coordxy *y UNUSED, int *mod UNUSED) { return '\033'; } /*ARGSUSED*/ -static void +staticfn void hup_getlin(const char *prompt UNUSED, char *outbuf) { Strcpy(outbuf, "\033"); } /*ARGSUSED*/ -static void +staticfn void hup_init_nhwindows(int *argc_p UNUSED, char **argv UNUSED) { - iflags.window_inited = 1; + iflags.window_inited = TRUE; } /*ARGUSED*/ -static winid +staticfn winid hup_create_nhwindow(int type UNUSED) { return WIN_ERR; } /*ARGSUSED*/ -static int +staticfn int hup_select_menu( winid window UNUSED, int how UNUSED, @@ -702,7 +731,7 @@ hup_select_menu( } /*ARGSUSED*/ -static void +staticfn void hup_add_menu( winid window UNUSED, const glyph_info *glyphinfo UNUSED, @@ -718,21 +747,21 @@ hup_add_menu( } /*ARGSUSED*/ -static void +staticfn void hup_end_menu(winid window UNUSED, const char *prompt UNUSED) { return; } /*ARGSUSED*/ -static void +staticfn void hup_putstr(winid window UNUSED, int attr UNUSED, const char *text UNUSED) { return; } /*ARGSUSED*/ -static void +staticfn void hup_print_glyph( winid window UNUSED, coordxy x UNUSED, coordxy y UNUSED, @@ -743,28 +772,28 @@ hup_print_glyph( } /*ARGSUSED*/ -static void +staticfn void hup_outrip(winid tmpwin UNUSED, int how UNUSED, time_t when UNUSED) { return; } /*ARGSUSED*/ -static void +staticfn void hup_curs(winid window UNUSED, int x UNUSED, int y UNUSED) { return; } /*ARGSUSED*/ -static void +staticfn void hup_display_nhwindow(winid window UNUSED, boolean blocking UNUSED) { return; } /*ARGSUSED*/ -static void +staticfn void hup_display_file(const char *fname UNUSED, boolean complain UNUSED) { return; @@ -772,7 +801,7 @@ hup_display_file(const char *fname UNUSED, boolean complain UNUSED) #ifdef CLIPPING /*ARGSUSED*/ -static void +staticfn void hup_cliparound(int x UNUSED, int y UNUSED) { return; @@ -781,22 +810,22 @@ hup_cliparound(int x UNUSED, int y UNUSED) #ifdef CHANGE_COLOR /*ARGSUSED*/ -static void -hup_change_color(int color, int reverse, long rgb) +staticfn void +hup_change_color(int color UNUSED, long rgb UNUSED, int reverse UNUSED) { return; } #ifdef MAC /*ARGSUSED*/ -static short -hup_set_font_name(winid window, char *fontname) +staticfn short +hup_set_font_name(winid window UNUSED, char *fontname UNUSED) { return 0; } #endif /* MAC */ -static char * +staticfn char * hup_get_color_string(void) { return (char *) 0; @@ -804,7 +833,7 @@ hup_get_color_string(void) #endif /* CHANGE_COLOR */ /*ARGSUSED*/ -static void +staticfn void hup_status_update( int idx UNUSED, genericptr_t ptr UNUSED, int chg UNUSED, int pc UNUSED, @@ -817,34 +846,34 @@ hup_status_update( * Non-specific stubs. */ -static int +staticfn int hup_int_ndecl(void) { return -1; } -static void +staticfn void hup_void_ndecl(void) { return; } /*ARGUSED*/ -static void +staticfn void hup_void_fdecl_int(int arg UNUSED) { return; } /*ARGUSED*/ -static void +staticfn void hup_void_fdecl_winid(winid window UNUSED) { return; } /*ARGUSED*/ -static void +staticfn void hup_void_fdecl_winid_ulong( winid window UNUSED, unsigned long mbehavior UNUSED) @@ -853,7 +882,7 @@ hup_void_fdecl_winid_ulong( } /*ARGUSED*/ -static void +staticfn void hup_void_fdecl_constchar_p(const char *string UNUSED) { return; @@ -934,7 +963,7 @@ genl_status_update( { char newbot1[MAXCO], newbot2[MAXCO]; long cond, *condptr = (long *) ptr; - register int i; + int i; unsigned pass, lndelta; enum statusfields idx1, idx2, *fieldlist; char *nb, *text = (char *) ptr; @@ -1111,7 +1140,7 @@ genl_status_update( RESTORE_WARNING_FORMAT_NONLITERAL static struct window_procs dumplog_windowprocs_backup; -static int menu_headings_backup; +static color_attr menu_headings_backup; static FILE *dumplog_file; static FILE *dumphtml_file; @@ -1191,13 +1220,14 @@ dump_fmtstr( break; case 'n': /* player name */ if (fullsubs) - Sprintf(tmpbuf, "%s", *gp.plname ? gp.plname : "unknown"); + Sprintf(tmpbuf, "%s", + *svp.plname ? svp.plname : "unknown"); else Strcpy(tmpbuf, "{hero name}"); break; case 'N': /* first character of player name */ if (fullsubs) - Sprintf(tmpbuf, "%c", *gp.plname ? *gp.plname : 'u'); + Sprintf(tmpbuf, "%c", *svp.plname ? *svp.plname : 'u'); else Strcpy(tmpbuf, "{hero initial}"); break; @@ -1276,7 +1306,7 @@ dump_fmtstr( then delimit the item with
  • for preformatted text, we don't mess with any existing bullet list, but try to keep consecutive preformatted strings in a single block. */ -static void +staticfn void html_write_tags(FILE *fp, int attr, boolean before) { static boolean in_list = FALSE; @@ -1327,7 +1357,7 @@ html_write_tags(FILE *fp, int attr, boolean before) } /* Write HTML-escaped char to a file */ -static void +staticfn void html_dump_char(FILE *fp, char c) { if (!fp) return; @@ -1356,7 +1386,7 @@ html_dump_char(FILE *fp, char c) } /* Write HTML-escaped string to a file */ -static void +staticfn void html_dump_str(FILE *fp, const char *str) { const char *p; @@ -1365,7 +1395,7 @@ html_dump_str(FILE *fp, const char *str) html_dump_char(fp, *p); } -static void +staticfn void html_dump_line(FILE *fp, int attr, const char *str) { if (strlen(str) == 0) { @@ -1402,7 +1432,7 @@ dump_end_screendump(void) } /* Status and map highlighting */ -static void +staticfn void dump_set_color_attr(int coloridx, int attrmask, boolean onoff) { #ifdef DUMPHTML @@ -1446,7 +1476,7 @@ dump_set_color_attr(int coloridx, int attrmask, boolean onoff) static int htmlsym[SYM_MAX] = DUMMY; -static void +staticfn void html_init_sym(void) { /* see https://html-css-js.com/html/character-codes/drawing/ */ @@ -1478,7 +1508,7 @@ html_init_sym(void) /* convert 'special' flags returned from mapglyph to highlight attrs (currently just inverse) */ -static unsigned +staticfn unsigned mg_hl_attr(unsigned special) { unsigned hl = 0; @@ -1577,7 +1607,6 @@ static int hpbar_percent, hpbar_color; static int condcolor(long bm, unsigned long *bmarray) { -#if defined(STATUS_HILITES) && defined(TEXTCOLOR) int i; if (bm && bmarray) @@ -1585,7 +1614,6 @@ condcolor(long bm, unsigned long *bmarray) if ((bmarray[i] & bm) != 0) return i; } -#endif return NO_COLOR; } @@ -1818,9 +1846,7 @@ dump_status_update(int fldidx, genericptr_t ptr, int chg UNUSED, int percent, /*FALLTHRU*/ default: attrmask = (color >> 8) & 0x00FF; -#ifndef TEXTCOLOR color = NO_COLOR; -#endif fmt = status_fieldfmt[fldidx]; if (!fmt) fmt = "%s"; @@ -1864,7 +1890,7 @@ RESTORE_WARNING_FORMAT_NONLITERAL /** HTML Headers and footers **/ -static void +staticfn void dump_headers(void) { #ifdef DUMPHTML @@ -1893,7 +1919,7 @@ dump_headers(void) #endif } -static void +staticfn void dump_footers(void) { #ifdef DUMPHTML @@ -1905,7 +1931,7 @@ dump_footers(void) } #ifdef DUMPHTML -static void +staticfn void dump_css(void) { int c = 0; @@ -1923,7 +1949,7 @@ dump_css(void) fclose(css); } -static void +staticfn void dump_outrip(winid win, int how, time_t when) { if (dumphtml_file) { @@ -2000,8 +2026,8 @@ dump_forward_putstr(winid win, int attr, const char *str, int no_forward) #if defined(DUMPLOG) || defined (DUMPHTML) /*ARGSUSED*/ -static void -dump_putstr(winid win, int attr, const char *str) +staticfn void +dump_putstr(winid win UNUSED, int attr UNUSED, const char *str) { /* Suppress newline for NHW_STATUS Send NHW_STATUS to HTML only */ @@ -2017,8 +2043,8 @@ dump_putstr(winid win, int attr, const char *str) #endif } -static winid -dump_create_nhwindow(int type) +staticfn winid +dump_create_nhwindow(int type UNUSED) { #ifdef DUMPHTML gd.dumping_list = (boolean) (type == NHW_MENU); @@ -2027,21 +2053,21 @@ dump_create_nhwindow(int type) } /*ARGUSED*/ -static void +staticfn void dump_clear_nhwindow(winid win UNUSED) { return; } /*ARGSUSED*/ -static void +staticfn void dump_display_nhwindow(winid win UNUSED, boolean p UNUSED) { return; } /*ARGUSED*/ -static void +staticfn void dump_destroy_nhwindow(winid win UNUSED) { #ifdef DUMPHTML @@ -2051,14 +2077,14 @@ dump_destroy_nhwindow(winid win UNUSED) } /*ARGUSED*/ -static void +staticfn void dump_start_menu(winid win UNUSED, unsigned long mbehavior UNUSED) { return; } /*ARGSUSED*/ -static void +staticfn void dump_add_menu(winid win UNUSED, const glyph_info *glyphinfo, const anything *identifier UNUSED, @@ -2098,7 +2124,7 @@ dump_add_menu(winid win UNUSED, } /*ARGSUSED*/ -static void +staticfn void dump_end_menu(winid win UNUSED, const char *str) { if (dumplog_file) { @@ -2113,7 +2139,7 @@ dump_end_menu(winid win UNUSED, const char *str) #endif } -static int +staticfn int dump_select_menu(winid win UNUSED, int how UNUSED, menu_item **item) { *item = (menu_item *) 0; @@ -2143,7 +2169,7 @@ dump_redirect(boolean onoff_flag) iflags.menu_headings = menu_headings_backup; } iflags.in_dumplog = onoff_flag; - iflags.menu_headings |= ATR_SUBHEAD; /* ATR_SUBHEAD changes with in_dumplog */ + iflags.menu_headings.attr |= ATR_SUBHEAD; /* ATR_SUBHEAD changes with in_dumplog */ } else { iflags.in_dumplog = FALSE; } @@ -2151,14 +2177,12 @@ dump_redirect(boolean onoff_flag) #endif #ifdef TTY_GRAPHICS -#ifdef TEXTCOLOR #ifdef TOS extern const char *hilites[CLR_MAX]; #else extern NEARDATA char *hilites[CLR_MAX]; #endif #endif -#endif int has_color(int color) @@ -2166,7 +2190,7 @@ has_color(int color) return (iflags.use_color && windowprocs.name && (windowprocs.wincap & WC_COLOR) && windowprocs.has_color[color] #ifdef TTY_GRAPHICS -#if defined(TEXTCOLOR) && defined(TERMLIB) && !defined(NO_TERMS) +#if defined(TERMLIB) && !defined(NO_TERMS) && (hilites[color] != 0) #endif #endif @@ -2268,30 +2292,31 @@ encglyph(int glyph) { static char encbuf[20]; /* 10+1 would suffice */ - Sprintf(encbuf, "\\G%04X%04X", gc.context.rndencode, glyph); + Sprintf(encbuf, "\\G%04X%04X", svc.context.rndencode, glyph); return encbuf; } +/* hexdd[] is defined in decl.c */ + int decode_glyph(const char *str, int *glyph_ptr) { - static const char hex[] = "00112233445566778899aAbBcCdDeEfF"; int rndchk = 0, dcount = 0, retval = 0; const char *dp; for (; *str && ++dcount <= 4; ++str) { - if ((dp = strchr(hex, *str)) != 0) { + if ((dp = strchr(hexdd, *str)) != 0) { retval++; - rndchk = (rndchk * 16) + ((int) (dp - hex) / 2); + rndchk = (rndchk * 16) + ((int) (dp - hexdd) / 2); } else break; } - if (rndchk == gc.context.rndencode) { + if (rndchk == svc.context.rndencode) { *glyph_ptr = dcount = 0; for (; *str && ++dcount <= 4; ++str) { - if ((dp = strchr(hex, *str)) != 0) { + if ((dp = strchr(hexdd, *str)) != 0) { retval++; - *glyph_ptr = (*glyph_ptr * 16) + ((int) (dp - hex) / 2); + *glyph_ptr = (*glyph_ptr * 16) + ((int) (dp - hexdd) / 2); } else break; } @@ -2426,4 +2451,290 @@ menuitem_invert_test( return TRUE; } +/* + * helper routine if a window port wants to extract the glyph + * information from a glyph number representation in the string; + * the returned string is the remainder of the string after + * extracting the \GNNNNNNNN information. The glyph details, + * including the utf8 representation under ENHANCED_SYMBOLS, + * will be stored in the glyph_info struct pointed to by gip. + */ +const char * +mixed_to_glyphinfo(const char *str, glyph_info *gip) +{ + int dcount, ggv; + + if (!str || !gip) + return " "; + + *gip = nul_glyphinfo; + if (*str == '\\' && *(str + 1) == 'G') { + if ((dcount = decode_glyph(str + 2, &ggv))) { + map_glyphinfo(0, 0, ggv, 0, gip); + /* 'str' is ready for the next loop iteration and + '*str' should not be copied at the end of this + iteration */ + str += (dcount + 2); + } + } + return str; +} + +/* + * This is a somewhat generic menu for taking a list of NetHack style + * class choices and presenting them via a description + * rather than the traditional NetHack characters. + * (Benefits users whose first exposure to NetHack is via tiles). + * + * prompt + * The title at the top of the menu. + * + * category: 0 = monster class + * 1 = object class + * + * way + * FALSE = PICK_ONE, TRUE = PICK_ANY + * + * class_list + * a null terminated string containing the list of choices. + * + * class_selection + * a null terminated string containing the selected characters. + * + * Returns number selected. + */ +int +choose_classes_menu(const char *prompt, + int category, + boolean way, + char *class_list, + char *class_select) +{ + menu_item *pick_list = (menu_item *) 0; + winid win; + anything any; + char buf[BUFSZ]; + const char *text = 0; + boolean selected; + int ret, i, n, next_accelerator, accelerator = 0; + int clr = NO_COLOR; + + if (!class_list || !class_select) + return 0; + next_accelerator = 'a'; + any = cg.zeroany; + win = create_nhwindow(NHW_MENU); + start_menu(win, MENU_BEHAVE_STANDARD); + while (*class_list) { + int idx; + + selected = FALSE; + switch (category) { + case 0: + idx = def_char_to_monclass(*class_list); + if (!IndexOk(idx, def_monsyms)) { + panic("choose_classes_menu: invalid monclass '%c'", + *class_list); + /*NOTREACHED*/ + } + text = def_monsyms[idx].explain; + accelerator = *class_list; + Sprintf(buf, "%s", text); + break; + case 1: + idx = def_char_to_objclass(*class_list); + if (!IndexOk(idx, def_oc_syms)) { + panic("choose_classes_menu: invalid objclass '%c'", + *class_list); + /*NOTREACHED*/ + } + text = def_oc_syms[idx].explain; + accelerator = next_accelerator; + Sprintf(buf, "%c %s", *class_list, text); + break; + default: + panic("choose_classes_menu: invalid category %d", category); + /*NOTREACHED*/ + } + if (way && *class_select) { /* Selections there already */ + if (strchr(class_select, *class_list)) { + selected = TRUE; + } + } + any.a_int = *class_list; + add_menu(win, &nul_glyphinfo, &any, accelerator, + category ? *class_list : 0, ATR_NONE, clr, buf, + selected ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE); + if (category > 0) { + if (next_accelerator == 'Z') + break; + else if (next_accelerator == 'z') + next_accelerator = 'A'; + else + ++next_accelerator; + } + ++class_list; + } + if (category == 1 && next_accelerator <= 'z') { + /* for objects, add "A - ' ' all classes", after a separator */ + add_menu_str(win, ""); + any = cg.zeroany; + any.a_int = (int) ' '; + Sprintf(buf, "%c %s", (char) any.a_int, "All classes of objects"); + /* we won't preselect this even if the incoming list is empty; + having it selected means that it would have to be explicitly + de-selected in order to select anything else */ + add_menu(win, &nul_glyphinfo, &any, 'A', 0, + ATR_NONE, clr, buf, MENU_ITEMFLAGS_SKIPINVERT); + if (!strcmp(prompt, "Autopickup what?")) { + add_menu_str(win, + "Note: when no choices are selected, \"all\" is implied."); + /* for 'O', "toggle" should be intuitive; for 'm O', it would + probably be better to say "Set 'autopickup' to true|false" */ + add_menu_str(win, flags.pickup + ? "Toggle off 'autopickup' to not pick up anything." + : "Toggle on 'autopickup' to automatically pick these things up."); + } + } + end_menu(win, prompt); + n = select_menu(win, way ? PICK_ANY : PICK_ONE, &pick_list); + destroy_nhwindow(win); + if (n > 0) { + if (category == 1) { + /* for object classes, first check for 'all'; it means 'use + a blank list' rather than 'collect every possible choice' */ + for (i = 0; i < n; ++i) + if (pick_list[i].item.a_int == ' ') { + pick_list[0].item.a_int = ' '; + n = 1; /* return 1; also an implicit 'break;' */ + } + } + for (i = 0; i < n; ++i) + *class_select++ = (char) pick_list[i].item.a_int; + free((genericptr_t) pick_list); + ret = n; + } else if (n == -1) { + class_select = eos(class_select); + ret = -1; + } else { + ret = 0; + } + *class_select = '\0'; + return ret; +} + +/* enum and structs are defined in wintype.h */ + +win_request_info zerowri = { { 0L, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, { NO_COLOR, ATR_NONE }}}; + +void +adjust_menu_promptstyle(winid window, color_attr *style) +{ + win_request_info wri = zerowri; + wri.fromcore.menu_promptstyle.color = style->color; + wri.fromcore.menu_promptstyle.attr = style->attr; + /* relay the style change to the window port */ + (void) ctrl_nhwindow(window, set_menu_promptstyle, &wri); + go.opt_need_promptstyle = FALSE; +} + +/* + * Common code point leading into the interface-specific + * add_menu() to allow single-spot adjustments to the parameters, + * such as those done by menu_colors. + */ +void +add_menu( + winid window, /* window to use, must be of type NHW_MENU */ + 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) */ + int attr, /* attribute for menu text (str) */ + int color, /* color for menu text (str) */ + const char *str, /* menu text */ + unsigned int itemflags) /* itemflags such as MENU_ITEMFLAGS_SELECTED */ +{ + if (!str) { + /* if 'str' is Null, just return without adding any menu entry */ + debugpline0("add_menu(Null)"); + return; + } + + if (iflags.use_menu_color) { + if ((itemflags & MENU_ITEMFLAGS_SKIPMENUCOLORS) == 0) + (void) get_menu_coloring(str, &color, &attr); + } + /* this is the only function that cared about this flag; remove it now */ + itemflags &= ~MENU_ITEMFLAGS_SKIPMENUCOLORS; + + (*windowprocs.win_add_menu)(window, glyphinfo, identifier, + ch, gch, attr, color, str, itemflags); +} + +/* insert a non-selectable, possibly highlighted line of text into a menu */ +void +add_menu_heading(winid tmpwin, const char *buf) +{ + anything any = cg.zeroany; + int attr = iflags.menu_headings.attr, + color = iflags.menu_headings.color; + + /* suppress highlighting during end-of-game disclosure */ + if (program_state.gameover) + attr = ATR_NONE, color = NO_COLOR; + + add_menu(tmpwin, &nul_glyphinfo, &any, '\0', '\0', attr, color, + buf, MENU_ITEMFLAGS_SKIPMENUCOLORS); +} + +/* insert a non-selectable, unhighlighted line of text into a menu */ +void +add_menu_str(winid tmpwin, const char *buf) +{ + anything any = cg.zeroany; + + add_menu(tmpwin, &nul_glyphinfo, &any, '\0', '\0', ATR_NONE, NO_COLOR, + buf, MENU_ITEMFLAGS_NONE); +} + +staticfn boolean +get_menu_coloring(const char *str, int *color, int *attr) +{ + struct menucoloring *tmpmc; + + if (iflags.use_menu_color) + for (tmpmc = gm.menu_colorings; tmpmc; tmpmc = tmpmc->next) + if (regex_match(str, tmpmc->match)) { + *color = tmpmc->color; + *attr = tmpmc->attr; + return TRUE; + } + return FALSE; +} + +int select_menu(winid window, int how, menu_item **menu_list) +{ + int reslt; + boolean old_bot_disabled = gb.bot_disabled; + + gb.bot_disabled = TRUE; + reslt = (*windowprocs.win_select_menu)(window, how, menu_list); + gb.bot_disabled = old_bot_disabled; + return reslt; +} + +void +getlin(const char *query, char *bufp) +{ + boolean old_bot_disabled = gb.bot_disabled; + + program_state.in_getlin = 1; + gb.bot_disabled = TRUE; + (*windowprocs.win_getlin)(query, bufp); + gb.bot_disabled = old_bot_disabled; + program_state.in_getlin = 0; +} /*windows.c*/ diff --git a/src/wizard.c b/src/wizard.c index 32d2e9c259..206b0b1808 100644 --- a/src/wizard.c +++ b/src/wizard.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 wizard.c $NHDT-Date: 1646688073 2022/03/07 21:21:13 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.85 $ */ +/* NetHack 3.7 wizard.c $NHDT-Date: 1718303204 2024/06/13 18:26:44 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.110 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2016. */ /* NetHack may be freely redistributed. See license for details. */ @@ -10,13 +10,16 @@ #include "hack.h" -static short which_arti(int); -static boolean mon_has_arti(struct monst *, short); -static struct monst *other_mon_has_arti(struct monst *, short); -static struct obj *on_ground(short); -static boolean you_have(int); -static unsigned long target_on(int, struct monst *); -static unsigned long strategy(struct monst *); +staticfn short which_arti(int); +staticfn boolean mon_has_arti(struct monst *, short) NONNULLARG1; +/* other_mon_has_arti() won't blow up if passed a NULL monst, + * but its caller target_on() passes it a nonnull monst; + * it may return a NULL monst pointer */ +staticfn struct monst *other_mon_has_arti(struct monst *, short) NONNULLARG1; +staticfn struct obj *on_ground(short); /* might return NULL obj pointer */ +staticfn boolean you_have(int); +staticfn unsigned long target_on(int, struct monst *) NONNULLARG2; +staticfn unsigned long strategy(struct monst *) NONNULLARG1; /* adding more neutral creatures will tend to reduce the number of monsters summoned by nasty(); adding more lawful creatures will reduce the number @@ -83,7 +86,7 @@ amulet(void) } } - if (!gc.context.no_of_wizards) + if (!svc.context.no_of_wizards) return; /* find Wizard, and wake him if necessary */ for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { @@ -91,7 +94,7 @@ amulet(void) continue; if (mtmp->iswiz && mtmp->msleeping && !rn2(40)) { wakeup(mtmp, FALSE, TRUE); - if (!next2u(mtmp->mx, mtmp->my)) + if (!m_next2u(mtmp)) You( "get the creepy feeling that somebody noticed your taking the Amulet."); return; @@ -102,7 +105,7 @@ amulet(void) int mon_has_amulet(struct monst *mtmp) { - register struct obj *otmp; + struct obj *otmp; for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) if (otmp->otyp == AMULET_OF_YENDOR) @@ -113,7 +116,7 @@ mon_has_amulet(struct monst *mtmp) int mon_has_special(struct monst *mtmp) { - register struct obj *otmp; + struct obj *otmp; for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) if (otmp->otyp == AMULET_OF_YENDOR @@ -138,7 +141,7 @@ mon_has_special(struct monst *mtmp) #define M_Wants(mask) (mtmp->data->mflags3 & (mask)) -static short +staticfn short which_arti(int mask) { switch (mask) { @@ -161,10 +164,10 @@ which_arti(int mask) * since bell, book, candle, and amulet are all objects, not really * artifacts right now. [MRS] */ -static boolean +staticfn boolean mon_has_arti(struct monst *mtmp, short otyp) { - register struct obj *otmp; + struct obj *otmp; for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) { if (otyp) { @@ -176,10 +179,14 @@ mon_has_arti(struct monst *mtmp, short otyp) return 0; } -static struct monst * +/* + * Returns some monster other than mtmp that + * has artifact, or NULL monst pointer. + */ +staticfn struct monst * other_mon_has_arti(struct monst *mtmp, short otyp) { - register struct monst *mtmp2; + struct monst *mtmp2; for (mtmp2 = fmon; mtmp2; mtmp2 = mtmp2->nmon) /* no need for !DEADMONSTER check here since they have no inventory */ @@ -190,10 +197,14 @@ other_mon_has_arti(struct monst *mtmp, short otyp) return (struct monst *) 0; } -static struct obj * +/* + * Returns obj of type specified if there is one + * on the ground, otherwise returns NULL obj pointer. + */ +staticfn struct obj * on_ground(short otyp) { - register struct obj *otmp; + struct obj *otmp; for (otmp = fobj; otmp; otmp = otmp->nobj) if (otyp) { @@ -204,7 +215,7 @@ on_ground(short otyp) return (struct obj *) 0; } -static boolean +staticfn boolean you_have(int mask) { switch (mask) { @@ -224,12 +235,12 @@ you_have(int mask) return 0; } -static unsigned long +staticfn unsigned long target_on(int mask, struct monst *mtmp) { - register short otyp; - register struct obj *otmp; - register struct monst *mtmp2; + short otyp; + struct obj *otmp; + struct monst *mtmp2; if (!M_Wants(mask)) return (unsigned long) STRAT_NONE; @@ -250,7 +261,7 @@ target_on(int mask, struct monst *mtmp) return (unsigned long) STRAT_NONE; } -static unsigned long +staticfn unsigned long strategy(struct monst *mtmp) { unsigned long strat, dstrat; @@ -281,8 +292,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; @@ -293,7 +304,7 @@ strategy(struct monst *mtmp) } } - if (gc.context.made_amulet) + if (svc.context.made_amulet) if ((strat = target_on(M3_WANTSAMUL, mtmp)) != STRAT_NONE) return strat; @@ -376,6 +387,10 @@ tactics(struct monst *mtmp) switch (strat) { case STRAT_HEAL: /* hide and recover */ mx = mtmp->mx, my = mtmp->my; + + if (u.uswallow && u.ustuck == mtmp) + expels(mtmp, mtmp->data, TRUE); + /* if wounded, hole up on or near the stairs (to block them) */ choose_stairs(&sx, &sy, (mtmp->m_id % 2)); mtmp->mavenge = 1; /* covetous monsters attack while fleeing */ @@ -396,9 +411,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 */ @@ -418,7 +434,9 @@ tactics(struct monst *mtmp) } if (u_at(tx, ty) || where == STRAT_PLAYER) { /* player is standing on it (or has it) */ - mnexto(mtmp, RLOC_MSG); + mx = mtmp->mx, my = mtmp->my; + if (!mnearto(mtmp, tx, ty, FALSE, RLOC_MSG)) + rloc_to(mtmp, mx, my); /* no room? stay put */ return 0; } if (where == STRAT_GROUND) { @@ -475,7 +493,7 @@ has_aggravatables(struct monst *mon UNUSED) void aggravate(void) { - register struct monst *mtmp; + struct monst *mtmp; for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) @@ -495,7 +513,7 @@ aggravate(void) void clonewiz(void) { - register struct monst *mtmp2; + struct monst *mtmp2; if ((mtmp2 = makemon(&mons[PM_WIZARD_OF_YENDOR], u.ux, u.uy, MM_NOWAIT)) != 0) { @@ -506,7 +524,7 @@ clonewiz(void) } if (!Protection_from_shape_changers) { mtmp2->m_ap_type = M_AP_MONSTER; - mtmp2->mappearance = wizapp[rn2(SIZE(wizapp))]; + mtmp2->mappearance = ROLL_FROM(wizapp); } newsym(mtmp2->mx, mtmp2->my); } @@ -517,7 +535,7 @@ int pick_nasty( int difcap) /* if non-zero, try to make difficulty be lower than this */ { - int alt, res = nasties[rn2(SIZE(nasties))]; + int alt, res = ROLL_FROM(nasties); /* To do? Possibly should filter for appropriate forms when * in the elemental planes or surrounded by water or lava. @@ -528,7 +546,7 @@ pick_nasty( master mind flayer -> mind flayer, but the substitutes are likely to be genocided too */ alt = res; - if ((gm.mvitals[res].mvflags & G_GENOD) != 0 + if ((svm.mvitals[res].mvflags & G_GENOD) != 0 || (difcap > 0 && mons[res].difficulty >= difcap) /* note: nasty() -> makemon() ignores G_HELL|G_NOHELL; arch-lich and master lich are both flagged as hell-only; @@ -536,7 +554,7 @@ pick_nasty( outside of Gehennom (unless the latter has been genocided) */ || (mons[res].geno & (Inhell ? G_NOHELL : G_HELL)) != 0) alt = big_to_little(res); - if (alt != res && (gm.mvitals[alt].mvflags & G_GENOD) == 0) { + if (alt != res && (svm.mvitals[alt].mvflags & G_GENOD) == 0) { const char *mnam = mons[alt].pmnames[NEUTRAL], *lastspace = strrchr(mnam, ' '); @@ -594,7 +612,7 @@ nasty(struct monst *summoner) * and 20 are neutral. [These numbers are up date for * 3.7.0; the ones in the next paragraph are not....] * - * Neutral caster, used for late-game harrassment, + * Neutral caster, used for late-game harassment, * has 18/42 chance to stop the inner loop on each * critter, 24/42 chance for another iteration. * Lawful caster has 28/42 chance to stop unless the @@ -710,7 +728,7 @@ resurrect(void) long elapsed; const char *verb; - if (!gc.context.no_of_wizards) { + if (!svc.context.no_of_wizards) { /* make a new Wizard */ verb = "kill"; mtmp = makemon(&mons[PM_WIZARD_OF_YENDOR], u.ux, u.uy, MM_NOWAIT); @@ -726,7 +744,7 @@ resurrect(void) if (mtmp->iswiz /* if he has the Amulet, he won't bring it to you */ && !mon_has_amulet(mtmp) - && (elapsed = gm.moves - mtmp->mlstmv) > 0L) { + && (elapsed = svm.moves - mtmp->mlstmv) > 0L) { mon_catchup_elapsed_time(mtmp, elapsed); if (elapsed >= LARGEST_INT) elapsed = LARGEST_INT - 1; @@ -801,10 +819,12 @@ intervene(void) } } +/* Wizard of Yendor is being removed from play (dead or escaped the dungeon); + keep the bookkeeping for him up to date */ void -wizdead(void) +wizdeadorgone(void) { - gc.context.no_of_wizards--; + svc.context.no_of_wizards--; if (!u.uevent.udemigod) { u.uevent.udemigod = TRUE; u.udg_cnt = rn1(250, 50); @@ -843,20 +863,20 @@ cuss(struct monst *mtmp) } else if (u.uhave.amulet && !rn2(SIZE(random_insult))) { SetVoice(mtmp, 0, 80, 0); verbalize("Relinquish the amulet, %s!", - random_insult[rn2(SIZE(random_insult))]); + ROLL_FROM(random_insult)); } else if (u.uhp < 5 && !rn2(2)) { /* Panic */ SetVoice(mtmp, 0, 80, 0); verbalize(rn2(2) ? "Even now thy life force ebbs, %s!" : "Savor thy breath, %s, it be thy last!", - random_insult[rn2(SIZE(random_insult))]); + ROLL_FROM(random_insult)); } else if (mtmp->mhp < 5 && !rn2(2)) { /* Parthian shot */ SetVoice(mtmp, 0, 80, 0); verbalize(rn2(2) ? "I shall return." : "I'll be back."); } else { SetVoice(mtmp, 0, 80, 0); verbalize("%s %s!", - random_malediction[rn2(SIZE(random_malediction))], - random_insult[rn2(SIZE(random_insult))]); + ROLL_FROM(random_malediction), + ROLL_FROM(random_insult)); } } else if (is_minion(mtmp->data) && !(mtmp->isminion && EMIN(mtmp)->renegade)) { @@ -896,15 +916,15 @@ wizpuzzle_close_space(coordxy x, coordxy y, if (u_at(x, y)) { You("are crushed beneath the falling wall!"); - gk.killer.format = KILLED_BY_AN; - Strcpy(gk.killer.name, "falling section of wall"); + svk.killer.format = KILLED_BY_AN; + Strcpy(svk.killer.name, "falling section of wall"); done(CRUSHING); /* life-saved */ enexto(&cc, u.ux, u.uy, gy.youmonst.data); teleds(cc.x, cc.y, TELEDS_TELEPORT); } else if ((mtmp = m_at(x, y)) != (struct monst *) 0) { - if (!gc.context.mon_moving) + if (!svc.context.mon_moving) xkilled(mtmp, XKILL_GIVEMSG | XKILL_NOCORPSE); else /* AD_DGST prevents corpse creation */ @@ -975,8 +995,8 @@ wizpuzzle_give_clues(void) * instead of walls so you could see where the gap was now, but those * are trivially passable (with all carried gear) by polyselfing into a * whirly monster. */ - for (x = gr.rooms[openroom].lx; x <= gr.rooms[openroom].hx; ++x) { - for (y = gr.rooms[openroom].ly; y <= gr.rooms[openroom].hy; ++y) { + for (x = svr.rooms[openroom].lx; x <= svr.rooms[openroom].hx; ++x) { + for (y = svr.rooms[openroom].ly; y <= svr.rooms[openroom].hy; ++y) { struct obj *otmp; if (levl[x][y].roomno - ROOMOFFSET != openroom) continue; /* not part of this irregular room */ @@ -1124,8 +1144,10 @@ wizpuzzle_move_gap(int newc, xint8 ring) for (i = 0; i < SIZE(gap_spaces); ++i) { if (gap_spaces[i].ring != ring) continue; - x = gr.rooms[gap_spaces[i].roomno].lx + gap_spaces[i].lx_offset; - y = gr.rooms[gap_spaces[i].roomno].ly + gap_spaces[i].ly_offset; + x = svr.rooms[gap_spaces[i].roomno].lx + + gap_spaces[i].lx_offset; + y = svr.rooms[gap_spaces[i].roomno].ly + + gap_spaces[i].ly_offset; if (round == 1 && gap_spaces[i].roomno != newc) { wizpuzzle_close_space(x, y, gap_spaces[i].typ, diff --git a/src/wizcmds.c b/src/wizcmds.c new file mode 100644 index 0000000000..8cb6e832ac --- /dev/null +++ b/src/wizcmds.c @@ -0,0 +1,1981 @@ +/* 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. */ + +#include "hack.h" +#include "func_tab.h" + +extern const char unavailcmd[]; /* cmd.c [27] */ +extern const char *levltyp[MAX_TYPE + 2]; /* cmd.c */ + +staticfn int size_monst(struct monst *, boolean); +staticfn int size_obj(struct obj *); +staticfn void count_obj(struct obj *, long *, long *, boolean, boolean); +staticfn void obj_chain(winid, const char *, struct obj *, boolean, long *, + long *); +staticfn void mon_invent_chain(winid, const char *, struct monst *, long *, + long *); +staticfn void mon_chain(winid, const char *, struct monst *, boolean, long *, + 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 *); + +DISABLE_WARNING_FORMAT_NONLITERAL + +/* #wizwish command - wish for something */ +int +wiz_wish(void) /* Unlimited wishes for debug mode by Paul Polderman */ +{ + if (wizard) { + boolean save_verbose = flags.verbose; + + flags.verbose = FALSE; + makewish(); + flags.verbose = save_verbose; + (void) encumber_msg(); + } else + pline(unavailcmd, ecname_from_fn(wiz_wish)); + return ECMD_OK; +} + +DISABLE_WARNING_FORMAT_NONLITERAL + +/* #wizidentify command - reveal and optionally identify hero's inventory */ +int +wiz_identify(void) +{ + if (wizard) { + iflags.override_ID = (int) cmd_from_func(wiz_identify); + /* command remapping might leave #wizidentify as the only way + to invoke us, in which case cmd_from_func() will yield NUL; + it won't matter to display_inventory()/display_pickinv() + if ^I invokes some other command--what matters is that + display_pickinv() and xname() see override_ID as nonzero */ + if (!iflags.override_ID) + iflags.override_ID = C('I'); + (void) display_inventory((char *) 0, FALSE); + iflags.override_ID = 0; + } else + pline(unavailcmd, ecname_from_fn(wiz_identify)); + return ECMD_OK; +} + +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 */ +staticfn void +makemap_unmakemon(struct monst *mtmp, boolean migratory) +{ + int ndx = monsndx(mtmp->data); + + /* uncreate any unique monster so that it is eligible to be remade + on the new incarnation of the level; ignores DEADMONSTER() [why?] */ + if (mtmp->data->geno & G_UNIQ) + svm.mvitals[ndx].mvflags &= ~G_EXTINCT; + if (svm.mvitals[ndx].born) + svm.mvitals[ndx].born--; + + /* vault is going away; get rid of guard who might be in play or + be parked at <0,0>; for the latter, might already be flagged as + dead but is being kept around because of the 'isgd' flag */ + if (mtmp->isgd) { + mtmp->isgd = 0; /* after this, fall through to mongone() */ + } else if (DEADMONSTER(mtmp)) { + return; /* already set to be discarded */ + } else if (mtmp->isshk && on_level(&u.uz, &ESHK(mtmp)->shoplevel)) { + setpaid(mtmp); + } + if (migratory) { + /* caller has removed 'mtmp' from migrating_mons; put it onto fmon + so that dmonsfree() bookkeeping for number of dead or removed + monsters won't get out of sync; it is not on the map but + mongone() -> m_detach() -> mon_leaving_level() copes with that */ + mtmp->mstate |= MON_OFFMAP; + mtmp->mstate &= ~(MON_MIGRATING | MON_LIMBO | MON_ENDGAME_MIGR); + mtmp->nmon = fmon; + fmon = mtmp; + } + mongone(mtmp); +} + +/* get rid of the all the monsters on--or intimately involved with--current + level; used when #wizmakemap destroys the level before replacing it */ +void +makemap_remove_mons(void) +{ + struct monst *mtmp, **mprev; + + /* keep steed and other adjacent pets after releasing them + from traps, stopping eating, &c as if hero were ascending */ + keepdogs(TRUE, FALSE); /* (pets-only; normally we'd be using 'FALSE') */ + /* get rid of all the monsters that didn't make it to 'mydogs' */ + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + /* if already dead, dmonsfree(below) will get rid of it */ + if (DEADMONSTER(mtmp)) + continue; + makemap_unmakemon(mtmp, FALSE); + } + /* some monsters retain details of this level in mon->mextra; that + data becomes invalid when the level is replaced by a new one; + get rid of them now if migrating or already arrived elsewhere; + [when on their 'home' level, the previous loop got rid of them; + if they aren't actually migrating but have been placed on some + 'away' level, such monsters are treated like the Wizard: kept + on migrating monsters list, scheduled to migrate back to their + present location instead of being saved with whatever level they + happen to be on; see keepdogs() and keep_mon_accessible(dog.c)] */ + for (mprev = &gm.migrating_mons; (mtmp = *mprev) != 0; ) { + if (mtmp->mextra + && ((mtmp->isshk && on_level(&u.uz, &ESHK(mtmp)->shoplevel)) + || (mtmp->ispriest && on_level(&u.uz, &EPRI(mtmp)->shrlevel)) + || (mtmp->isgd && on_level(&u.uz, &EGD(mtmp)->gdlevel)))) { + *mprev = mtmp->nmon; + makemap_unmakemon(mtmp, TRUE); + } else { + mprev = &mtmp->nmon; + } + } + /* release dead and 'unmade' monsters */ + dmonsfree(); + if (fmon) { + impossible("makemap_remove_mons: 'fmon' did not get emptied?"); + } + return; +} + +DISABLE_WARNING_FORMAT_NONLITERAL + +/* #wizmakemap - discard current dungeon level and replace with a new one */ +int +wiz_makemap(void) +{ + if (wizard) { + makemap_prepost(TRUE); + /* create a new level; various things like bestowing a guardian + angel on Astral or setting off alarm on Ft.Ludios are handled + by goto_level(do.c) so won't occur for replacement levels */ + mklev(); + makemap_prepost(FALSE); + } else { + pline(unavailcmd, ecname_from_fn(wiz_makemap)); + } + return ECMD_OK; +} + +/* the #wizmap command - reveal the level map + and any traps or engravings on it */ +int +wiz_map(void) +{ + if (wizard) { + struct trap *t; + struct engr *ep; + long save_Hconf = HConfusion, save_Hhallu = HHallucination; + + notice_mon_off(); + HConfusion = HHallucination = 0L; + for (t = gf.ftrap; t != 0; t = t->ntrap) { + t->tseen = 1; + map_trap(t, TRUE); + } + for (ep = head_engr; ep != 0; ep = ep->nxt_engr) { + map_engraving(ep, TRUE); + } + do_mapping(); + notice_mon_on(); + HConfusion = save_Hconf; + HHallucination = save_Hhallu; + } else + pline(unavailcmd, ecname_from_fn(wiz_map)); + return ECMD_OK; +} + +/* #wizgenesis - generate monster(s); a count prefix will be honored */ +int +wiz_genesis(void) +{ + if (wizard) { + boolean mongen_saved = iflags.debug_mongen; + + iflags.debug_mongen = FALSE; + (void) create_particular(); + iflags.debug_mongen = mongen_saved; + } else + pline(unavailcmd, ecname_from_fn(wiz_genesis)); + return ECMD_OK; +} + +/* #wizwhere command - display dungeon layout */ +int +wiz_where(void) +{ + if (wizard) + (void) print_dungeon(FALSE, (schar *) 0, (xint16 *) 0); + else + pline(unavailcmd, ecname_from_fn(wiz_where)); + return ECMD_OK; +} + +/* the #wizdetect command - detect secret doors, traps, hidden monsters */ +int +wiz_detect(void) +{ + if (wizard) + (void) findit(); + else + pline(unavailcmd, ecname_from_fn(wiz_detect)); + return ECMD_OK; +} + +RESTORE_WARNING_FORMAT_NONLITERAL + +/* the #wizkill command - pick targets and reduce them to 0HP; + by default, the hero is credited/blamed; use 'm' prefix to avoid that */ +int +wiz_kill(void) +{ + struct monst *mtmp; + coord cc; + int ans; + char c, qbuf[QBUFSZ]; + const char *prompt = "Pick first monster to slay"; + boolean save_verbose = flags.verbose, + save_autodescribe = iflags.autodescribe; + d_level uarehere = u.uz; + + cc.x = u.ux, cc.y = u.uy; + for (;;) { + pline("%s:", prompt); + prompt = "Next monster"; + + flags.verbose = FALSE; + iflags.autodescribe = TRUE; + ans = getpos(&cc, TRUE, "a monster"); + flags.verbose = save_verbose; + iflags.autodescribe = save_autodescribe; + if (ans < 0 || cc.x < 1) + break; + + mtmp = 0; + if (u_at(cc.x, cc.y)) { + if (u.usteed) { + Sprintf(qbuf, "Kill %.110s?", mon_nam(u.usteed)); + if ((c = ynq(qbuf)) == 'q') + break; + if (c == 'y') + mtmp = u.usteed; + } + if (!mtmp) { + Sprintf(qbuf, "%s?", Role_if(PM_SAMURAI) ? "Perform seppuku" + : "Commit suicide"); + if (paranoid_query(TRUE, qbuf)) { + Sprintf(svk.killer.name, "%s own player", uhis()); + svk.killer.format = KILLED_BY; + done(DIED); + } + break; + } + } else if (u.uswallow) { + mtmp = next2u(cc.x, cc.y) ? u.ustuck : 0; + } else { + mtmp = m_at(cc.x, cc.y); + } + + /* whether there's an unseen monster here or not, player will know + that there's no monster here after the kill or failed attempt; + let hero know too */ + (void) unmap_invisible(cc.x, cc.y); + + if (mtmp) { + /* we don't require that the monster be seen or sensed so + we issue our own message in order to name it in case it + isn't; note that if it triggers other kills, those might + be referred to as "it" */ + int tame = !!mtmp->mtame, + seen = (canspotmon(mtmp) || (u.uswallow && mtmp == u.ustuck)), + flgs = (SUPPRESS_IT | SUPPRESS_HALLUCINATION + | ((tame && has_mgivenname(mtmp)) ? SUPPRESS_SADDLE + : 0)), + articl = tame ? ARTICLE_YOUR : seen ? ARTICLE_THE : ARTICLE_A; + const char *adjs = tame ? (!seen ? "poor, unseen" : "poor") + : (!seen ? "unseen" : (const char *) 0); + char *Mn = x_monnam(mtmp, articl, adjs, flgs, FALSE); + + if (!iflags.menu_requested) { + /* normal case: hero is credited/blamed */ + You("%s %s!", nonliving(mtmp->data) ? "destroy" : "kill", Mn); + xkilled(mtmp, XKILL_NOMSG); + } else { /* 'm'-prefix */ + /* we know that monsters aren't moving because player has + just issued this #wizkill command, but if 'mtmp' is a + gas spore whose explosion kills any other monsters we + need to have the mon_moving flag be True in order to + avoid blaming or crediting hero for their deaths */ + svc.context.mon_moving = TRUE; + pline("%s is %s.", upstart(Mn), + nonliving(mtmp->data) ? "destroyed" : "killed"); + /* Null second arg suppresses the usual message */ + monkilled(mtmp, (char *) 0, AD_PHYS); + svc.context.mon_moving = FALSE; + } + /* end targetting loop if an engulfer dropped hero onto a level- + changing trap */ + if (u.utotype || !on_level(&u.uz, &uarehere)) + break; + } else { + There("is no monster there."); + break; + } + } + /* since #wizkill takes no game time, it is possible to kill something + in the main dungeon and immediately level teleport into the endgame + which will delete the main dungeon's level files; avoid triggering + impossible "dmonsfree: 0 removed doesn't match N pending" by forcing + dead monster cleanup; we don't track whether anything was actually + killed above--if nothing was, this will be benign */ + dmonsfree(); + /* distinction between ECMD_CANCEL and ECMD_OK is unimportant here */ + return ECMD_OK; /* no time elapses */ +} + +DISABLE_WARNING_FORMAT_NONLITERAL + +/* the #wizloadlua command - load an arbitrary lua file */ +int +wiz_load_lua(void) +{ + if (wizard) { + char buf[BUFSZ]; + /* Large but not unlimited memory and CPU so random bits of + * code can be tested by wizards. */ + nhl_sandbox_info sbi = {NHL_SB_SAFE | NHL_SB_DEBUGGING, + 16*1024*1024, 0, 16*1024*1024}; + + buf[0] = '\0'; + getlin("Load which lua file?", buf); + if (buf[0] == '\033' || buf[0] == '\0') + return ECMD_CANCEL; + if (!strchr(buf, '.')) + strcat(buf, ".lua"); + (void) load_lua(buf, &sbi); + } else + pline(unavailcmd, ecname_from_fn(wiz_load_lua)); + return ECMD_OK; +} + +/* the #wizloaddes command - load a special level lua file */ +int +wiz_load_splua(void) +{ + if (wizard) { + char buf[BUFSZ]; + + buf[0] = '\0'; + getlin("Load which des lua file?", buf); + if (buf[0] == '\033' || buf[0] == '\0') + return ECMD_CANCEL; + if (!strchr(buf, '.')) + strcat(buf, ".lua"); + + lspo_reset_level(NULL); + (void) load_special(buf); + lspo_finalize_level(NULL); + + } else + pline(unavailcmd, ecname_from_fn(wiz_load_splua)); + return ECMD_OK; +} + +/* the #wizlevelport command - level teleport */ +int +wiz_level_tele(void) +{ + if (wizard) + level_tele(); + else + pline(unavailcmd, ecname_from_fn(wiz_level_tele)); + return ECMD_OK; +} + +RESTORE_WARNING_FORMAT_NONLITERAL + +/* #wizfliplevel - transpose the current level */ +int +wiz_flip_level(void) +{ + static const char choices[] = "0123", + prmpt[] = "Flip 0=randomly, 1=vertically, 2=horizontally, 3=both:"; + + /* + * Does not handle + * levregions, + * monster mtrack, + * migrating monsters aimed at returning to specific coordinates + * on this level + * as flipping is normally done only during level creation. + */ + if (wizard) { + char c = yn_function(prmpt, choices, '\0', TRUE); + + if (c && strchr(choices, c)) { + c -= '0'; + + if (!c) + flip_level_rnd(3, TRUE); + else + flip_level((int) c, TRUE); + + docrt(); + } else { + pline("%s", Never_mind); + } + } + return ECMD_OK; +} + +/* #levelchange command - adjust hero's experience level */ +int +wiz_level_change(void) +{ + char buf[BUFSZ], dummy = '\0'; + int newlevel = 0; + int ret; + + buf[0] = '\0'; /* in case EDIT_GETLIN is enabled */ + getlin("To what experience level do you want to be set?", buf); + (void) mungspaces(buf); + if (buf[0] == '\033' || buf[0] == '\0') + ret = 0; + else + ret = sscanf(buf, "%d%c", &newlevel, &dummy); + + if (ret != 1) { + pline1(Never_mind); + return ECMD_OK; + } + if (newlevel == u.ulevel) { + You("are already that experienced."); + } else if (newlevel < u.ulevel) { + if (u.ulevel == 1) { + You("are already as inexperienced as you can get."); + return ECMD_OK; + } + if (newlevel < 1) + newlevel = 1; + while (u.ulevel > newlevel) + losexp("#levelchange"); + } else { + if (u.ulevel >= MAXULEV) { + You("are already as experienced as you can get."); + return ECMD_OK; + } + if (newlevel > MAXULEV) + newlevel = MAXULEV; + while (u.ulevel < newlevel) + pluslvl(FALSE); + } + /* blessed full healing or restore ability won't fix any lost levels */ + u.ulevelmax = u.ulevel; + return ECMD_OK; +} + +DISABLE_WARNING_CONDEXPR_IS_CONSTANT + +/* #wiztelekinesis */ +int +wiz_telekinesis(void) +{ + int ans = 0; + coord cc; + struct monst *mtmp = (struct monst *) 0; + + cc.x = u.ux; + cc.y = u.uy; + + pline("Pick a monster to hurtle."); + do { + ans = getpos(&cc, TRUE, "a monster"); + if (ans < 0 || cc.x < 1) + return ECMD_CANCEL; + + if ((((mtmp = m_at(cc.x, cc.y)) != 0) && canspotmon(mtmp)) + || u_at(cc.x, cc.y)) { + if (!getdir("which direction?")) + return ECMD_CANCEL; + + if (mtmp) { + mhurtle(mtmp, u.dx, u.dy, 6); + if (!DEADMONSTER(mtmp) && canspotmon(mtmp)) { + cc.x = mtmp->mx; + cc.y = mtmp->my; + } + } else { + hurtle(u.dx, u.dy, 6, FALSE); + cc.x = u.ux, cc.y = u.uy; + } + } + + } while (u.utotype == UTOTYPE_NONE); + return ECMD_OK; +} + +RESTORE_WARNING_CONDEXPR_IS_CONSTANT + +/* #panic command - test program's panic handling */ +int +wiz_panic(void) +{ + if (iflags.debug_fuzzer) { + u.uhp = u.uhpmax = 1000; + u.uen = u.uenmax = 1000; + return ECMD_OK; + } + if (paranoid_query(TRUE, + "Do you want to call panic() and end your game?")) + panic("Crash test (#panic)."); + return ECMD_OK; +} + +/* #debugfuzzer command - fuzztest the program */ +int +wiz_fuzzer(void) +{ + if (flags.suppress_alert < FEATURE_NOTICE_VER(3,7,0)) { + 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?")) { + /* 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; +} + +/* #polyself command - change hero's form */ +int +wiz_polyself(void) +{ + polyself(POLY_CONTROLLED); + return ECMD_OK; +} + +/* #seenv command */ +int +wiz_show_seenv(void) +{ + winid win; + coordxy x, y, startx, stopx, curx; + int v; + char row[COLNO + 1]; + + win = create_nhwindow(NHW_TEXT); + /* + * Each seenv description takes up 2 characters, so center + * the seenv display around the hero. + */ + startx = max(1, u.ux - (COLNO / 4)); + stopx = min(startx + (COLNO / 2), COLNO); + /* can't have a line exactly 80 chars long */ + if (stopx - startx == COLNO / 2) + startx++; + + for (y = 0; y < ROWNO; y++) { + for (x = startx, curx = 0; x < stopx; x++, curx += 2) { + if (u_at(x, y)) { + row[curx] = row[curx + 1] = '@'; + } else { + v = levl[x][y].seenv & 0xff; + if (v == 0) + row[curx] = row[curx + 1] = ' '; + else + Sprintf(&row[curx], "%02x", v); + } + } + /* remove trailing spaces */ + for (x = curx - 1; x >= 0; x--) + if (row[x] != ' ') + break; + row[x + 1] = '\0'; + + putstr(win, 0, row); + } + display_nhwindow(win, TRUE); + destroy_nhwindow(win); + return ECMD_OK; +} + +/* #vision command */ +int +wiz_show_vision(void) +{ + winid win; + coordxy x, y; + int v; + char row[COLNO + 1]; + + win = create_nhwindow(NHW_TEXT); + Sprintf(row, "Flags: 0x%x could see, 0x%x in sight, 0x%x temp lit", + COULD_SEE, IN_SIGHT, TEMP_LIT); + putstr(win, 0, row); + putstr(win, 0, ""); + for (y = 0; y < ROWNO; y++) { + for (x = 1; x < COLNO; x++) { + if (u_at(x, y)) { + row[x] = '@'; + } else { + v = gv.viz_array[y][x]; /* data access should be hidden */ + row[x] = (v == 0) ? ' ' : ('0' + v); + } + } + /* remove trailing spaces */ + for (x = COLNO - 1; x >= 1; x--) + if (row[x] != ' ') + break; + row[x + 1] = '\0'; + + putstr(win, 0, &row[1]); + } + display_nhwindow(win, TRUE); + destroy_nhwindow(win); + return ECMD_OK; +} + +/* #wmode command */ +int +wiz_show_wmodes(void) +{ + winid win; + coordxy x, y; + char row[COLNO + 1]; + struct rm *lev; + boolean istty = WINDOWPORT(tty); + + win = create_nhwindow(NHW_TEXT); + if (istty) + putstr(win, 0, ""); /* tty only: blank top line */ + for (y = 0; y < ROWNO; y++) { + for (x = 0; x < COLNO; x++) { + lev = &levl[x][y]; + if (u_at(x, y)) + row[x] = '@'; + else if (IS_WALL(lev->typ) || lev->typ == SDOOR) + row[x] = '0' + (lev->wall_info & WM_MASK); + else if (lev->typ == CORR) + row[x] = '#'; + else if (IS_ROOM(lev->typ) || IS_DOOR(lev->typ)) + row[x] = '.'; + else + row[x] = 'x'; + } + row[COLNO] = '\0'; + /* map column 0, levl[0][], is off the left edge of the screen */ + putstr(win, 0, &row[1]); + } + display_nhwindow(win, TRUE); + destroy_nhwindow(win); + return ECMD_OK; +} + +/* wizard mode variant of #terrain; internal levl[][].typ values in base-36 */ +void +wiz_map_levltyp(void) +{ + winid win; + coordxy x, y; + int terrain; + char row[COLNO + 1]; + boolean istty = !strcmp(windowprocs.name, "tty"); + + win = create_nhwindow(NHW_TEXT); + /* map row 0, levl[][0], is drawn on the second line of tty screen */ + if (istty) + putstr(win, 0, ""); /* tty only: blank top line */ + for (y = 0; y < ROWNO; y++) { + /* map column 0, levl[0][], is off the left edge of the screen; + it should always have terrain type "undiggable stone" */ + for (x = 1; x < COLNO; x++) { + terrain = levl[x][y].typ; + /* assumes there aren't more than 10+26+26 terrain types */ + row[x - 1] = (char) ((terrain == STONE && !may_dig(x, y)) + ? '*' + : (terrain < 10) + ? '0' + terrain + : (terrain < 36) + ? 'a' + terrain - 10 + : 'A' + terrain - 36); + } + x--; + if (levl[0][y].typ != STONE || may_dig(0, y)) + row[x++] = '!'; + row[x] = '\0'; + putstr(win, 0, row); + } + + { + char dsc[COLBUFSZ]; + s_level *slev = Is_special(&u.uz); + + Sprintf(dsc, "D:%d,L:%d", u.uz.dnum, u.uz.dlevel); + /* [dungeon branch features currently omitted] */ + /* special level features */ + if (slev) { + Sprintf(eos(dsc), " \"%s\"", slev->proto); + /* special level flags (note: dungeon.def doesn't set `maze' + or `hell' for any specific levels so those never show up) */ + if (slev->flags.maze_like) + Strcat(dsc, " mazelike"); + if (slev->flags.hellish) + Strcat(dsc, " hellish"); + if (slev->flags.town) + Strcat(dsc, " town"); + /* alignment currently omitted to save space */ + } + /* level features */ + if (svl.level.flags.nfountains) + Sprintf(eos(dsc), " %c:%d", defsyms[S_fountain].sym, + (int) svl.level.flags.nfountains); + if (svl.level.flags.nsinks) + Sprintf(eos(dsc), " %c:%d", defsyms[S_sink].sym, + (int) svl.level.flags.nsinks); + if (svl.level.flags.has_vault) + Strcat(dsc, " vault"); + if (svl.level.flags.has_shop) + Strcat(dsc, " shop"); + if (svl.level.flags.has_temple) + Strcat(dsc, " temple"); + if (svl.level.flags.has_court) + Strcat(dsc, " throne"); + if (svl.level.flags.has_zoo) + Strcat(dsc, " zoo"); + if (svl.level.flags.has_morgue) + Strcat(dsc, " morgue"); + if (svl.level.flags.has_barracks) + Strcat(dsc, " barracks"); + if (svl.level.flags.has_beehive) + Strcat(dsc, " hive"); + if (svl.level.flags.has_swamp) + Strcat(dsc, " swamp"); + /* level flags */ + if (svl.level.flags.noteleport) + Strcat(dsc, " noTport"); + if (svl.level.flags.hardfloor) + Strcat(dsc, " noDig"); + if (svl.level.flags.nommap == MAPPABLE_NEVER) + Strcat(dsc, " noMMap"); + else if (svl.level.flags.nommap == MAPPABLE_BOSSBLOCKED) + Strcat(dsc, " noMMapBoss"); + if (!svl.level.flags.hero_memory) + Strcat(dsc, " noMem"); + if (svl.level.flags.shortsighted) + Strcat(dsc, " shortsight"); + if (svl.level.flags.graveyard) + Strcat(dsc, " graveyard"); + if (svl.level.flags.is_maze_lev) + Strcat(dsc, " maze"); + if (svl.level.flags.is_cavernous_lev) + Strcat(dsc, " cave"); + if (svl.level.flags.arboreal) + Strcat(dsc, " tree"); + if (Sokoban) + Strcat(dsc, " sokoban-rules"); + /* non-flag info; probably should include dungeon branching + checks (extra stairs and magic portals) here */ + if (Invocation_lev(&u.uz)) + Strcat(dsc, " invoke"); + if (On_W_tower_level(&u.uz)) + Strcat(dsc, " tower"); + /* append a branch identifier for completeness' sake */ + if (u.uz.dnum == 0) + Strcat(dsc, " dungeon"); + else if (u.uz.dnum == mines_dnum) + Strcat(dsc, " mines"); + else if (In_sokoban(&u.uz)) + Strcat(dsc, " sokoban"); + else if (u.uz.dnum == quest_dnum) + Strcat(dsc, " quest"); + else if (Is_knox(&u.uz)) + Strcat(dsc, " ludios"); + else if (u.uz.dnum == gehennom_dnum) + Strcat(dsc, " gehennom"); + else if (u.uz.dnum == tower_dnum) + Strcat(dsc, " vlad"); + else if (In_endgame(&u.uz)) + Strcat(dsc, " endgame"); + else { + /* somebody's added a dungeon branch we're not expecting */ + const char *brname = svd.dungeons[u.uz.dnum].dname; + + if (!brname || !*brname) + brname = "unknown"; + if (!strncmpi(brname, "the ", 4)) + brname += 4; + Sprintf(eos(dsc), " %s", brname); + } + /* limit the line length to map width */ + if (strlen(dsc) >= COLNO) + dsc[COLNO - 1] = '\0'; /* truncate */ + putstr(win, 0, dsc); + } + + display_nhwindow(win, TRUE); + destroy_nhwindow(win); + return; +} + +DISABLE_WARNING_FORMAT_NONLITERAL + +/* explanation of base-36 output from wiz_map_levltyp() */ +void +wiz_levltyp_legend(void) +{ + winid win; + int i, j, last, c; + const char *dsc, *fmt; + char buf[BUFSZ]; + + win = create_nhwindow(NHW_TEXT); + putstr(win, 0, "#terrain encodings:"); + putstr(win, 0, ""); + fmt = " %c - %-28s"; /* TODO: include tab-separated variant for win32 */ + *buf = '\0'; + /* output in pairs, left hand column holds [0],[1],...,[N/2-1] + and right hand column holds [N/2],[N/2+1],...,[N-1]; + N ('last') will always be even, and may or may not include + the empty string entry to pad out the final pair, depending + upon how many other entries are present in levltyp[] */ + last = SIZE(levltyp) & ~1; + for (i = 0; i < last / 2; ++i) + for (j = i; j < last; j += last / 2) { + dsc = levltyp[j]; + c = !*dsc ? ' ' + : !strncmp(dsc, "unreachable", 11) ? '*' + /* same int-to-char conversion as wiz_map_levltyp() */ + : (j < 10) ? '0' + j + : (j < 36) ? 'a' + j - 10 + : 'A' + j - 36; + Sprintf(eos(buf), fmt, c, dsc); + if (j > i) { + putstr(win, 0, buf); + *buf = '\0'; + } + } + display_nhwindow(win, TRUE); + destroy_nhwindow(win); + return; +} + +RESTORE_WARNING_FORMAT_NONLITERAL + +DISABLE_WARNING_CONDEXPR_IS_CONSTANT + +/* #wizsmell command - test usmellmon(). */ +int +wiz_smell(void) +{ + struct monst *mtmp; /* monster being smelled */ + struct permonst *mptr; + int ans, glyph; + coord cc; /* screen pos to sniff */ + boolean is_you; + + cc.x = u.ux; + cc.y = u.uy; + if (!olfaction(gy.youmonst.data)) { + You("are incapable of detecting odors in your present form."); + return ECMD_OK; + } + + You("can move the cursor to a monster that you want to smell."); + do { + pline("Pick a monster to smell."); + ans = getpos(&cc, TRUE, "a monster"); + if (ans < 0 || cc.x < 0) { + return ECMD_CANCEL; /* done */ + } + is_you = FALSE; + if (u_at(cc.x, cc.y)) { + if (u.usteed) { + mptr = u.usteed->data; + } else { + mptr = gy.youmonst.data; + is_you = TRUE; + } + } else if ((mtmp = m_at(cc.x, cc.y)) != (struct monst *) 0) { + mptr = mtmp->data; + } else { + mptr = (struct permonst *) 0; + } + /* Buglet: mapping or unmapping "remembered, unseen monster" should + cause time to elapse; since we're in wizmode, don't bother */ + glyph = glyph_at(cc.x, cc.y); + /* Is it a monster? */ + if (mptr) { + if (is_you) + You("surreptitiously sniff under your %s.", body_part(ARM)); + if (!usmellmon(mptr)) + pline("%s to not give off any smell.", + is_you ? "You seem" : "That monster seems"); + if (!glyph_is_monster(glyph)) + map_invisible(cc.x, cc.y); + } else { + You("don't smell any monster there."); + if (glyph_is_invisible(glyph)) + unmap_invisible(cc.x, cc.y); + } + } while (TRUE); + return ECMD_OK; +} + +RESTORE_WARNING_CONDEXPR_IS_CONSTANT + +DISABLE_WARNING_FORMAT_NONLITERAL + +#define DEFAULT_TIMEOUT_INCR 30 + +/* #wizinstrinsic command to set some intrinsics for testing */ +int +wiz_intrinsic(void) +{ + if (wizard) { + static const char wizintrinsic[] = "#wizintrinsic"; + static const char fmt[] = "You are%s %s."; + winid win; + anything any; + char buf[BUFSZ]; + int i, j, n, amt, typ, p = 0; + long oldtimeout, newtimeout; + const char *propname; + menu_item *pick_list = (menu_item *) 0; + int clr = NO_COLOR; + + any = cg.zeroany; + win = create_nhwindow(NHW_MENU); + start_menu(win, MENU_BEHAVE_STANDARD); + if (iflags.cmdassist) { + /* start menu with a subtitle */ + Sprintf(buf, + "[Precede any selection with a count to increment by other than %d.]", + DEFAULT_TIMEOUT_INCR); + add_menu_str(win, buf); + } + for (i = 0; (propname = property_by_index(i, &p)) != 0; ++i) { + if (p == HALLUC_RES) { + /* Grayswandir vs hallucination; ought to be redone to + use u.uprops[HALLUC].blocked instead of being treated + as a separate property; letting in be manually toggled + even only in wizard mode would be asking for trouble... */ + continue; + } + if (p == FIRE_RES) { + /* FIRE_RES and properties beyond it (in the propertynames[] + ordering, not their numerical PROP values), can only be + set to timed values here so show a separator */ + add_menu_str(win, "--"); + } + any.a_int = i + 1; /* +1: avoid 0 */ + oldtimeout = u.uprops[p].intrinsic & TIMEOUT; + if (oldtimeout) + Sprintf(buf, "%-27s [%li]", propname, oldtimeout); + else + Sprintf(buf, "%s", propname); + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, buf, + MENU_ITEMFLAGS_NONE); + } + end_menu(win, "Which intrinsics?"); + n = select_menu(win, PICK_ANY, &pick_list); + destroy_nhwindow(win); + + for (j = 0; j < n; ++j) { + i = pick_list[j].item.a_int - 1; /* -1: reverse +1 above */ + propname = property_by_index(i, &p); + oldtimeout = u.uprops[p].intrinsic & TIMEOUT; + amt = (pick_list[j].count == -1L) ? DEFAULT_TIMEOUT_INCR + : (int) pick_list[j].count; + if (amt <= 0) /* paranoia */ + continue; + newtimeout = oldtimeout + (long) amt; + + switch (p) { + case SICK: + case SLIMED: + case STONED: + if (oldtimeout > 0L && newtimeout > oldtimeout) + newtimeout = oldtimeout; + break; + } + + switch (p) { + case BLINDED: + make_blinded(newtimeout, TRUE); + break; +#if 0 /* make_confused() only gives feedback when confusion is + * ending so use the 'default' case for it instead */ + case CONFUSION: + make_confused(newtimeout, TRUE); + break; +#endif /*0*/ + case DEAF: + make_deaf(newtimeout, TRUE); + break; + case HALLUC: + make_hallucinated(newtimeout, TRUE, 0L); + break; + case SICK: + typ = !rn2(2) ? SICK_VOMITABLE : SICK_NONVOMITABLE; + make_sick(newtimeout, wizintrinsic, TRUE, typ); + break; + case SLIMED: + Sprintf(buf, fmt, + !Slimed ? "" : " still", "turning into slime"); + make_slimed(newtimeout, buf); + break; + case STONED: + Sprintf(buf, fmt, + !Stoned ? "" : " still", "turning into stone"); + make_stoned(newtimeout, buf, KILLED_BY, wizintrinsic); + break; + case STUNNED: + make_stunned(newtimeout, TRUE); + break; + case WITHERING: + make_withering(newtimeout, TRUE); + break; + case VOMITING: + Sprintf(buf, fmt, !Vomiting ? "" : " still", "vomiting"); + make_vomiting(newtimeout, FALSE); + pline1(buf); + break; + case WARN_OF_MON: + if (!Warn_of_mon) { + svc.context.warntype.speciesidx = PM_GRID_BUG; + svc.context.warntype.species + = &mons[svc.context.warntype.speciesidx]; + } + goto def_feedback; + case GLIB: + /* slippery fingers might need a persistent inventory update + so needs more than simple incr_itimeout() but we want + the pline() issued with that */ + make_glib((int) newtimeout); + FALLTHROUGH; + /*FALLTHRU*/ + default: + def_feedback: + if (p != GLIB) + incr_itimeout(&u.uprops[p].intrinsic, amt); + disp.botl = TRUE; /* have pline() do a status update */ + pline("Timeout for %s %s %d.", propname, + oldtimeout ? "increased by" : "set to", amt); + break; + } + /* this has to be after incr_itimeout() */ + if (p == LEVITATION || p == FLYING) + 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); + docrt(); + } else + pline(unavailcmd, ecname_from_fn(wiz_intrinsic)); + return ECMD_OK; +} + +RESTORE_WARNING_FORMAT_NONLITERAL + +/* #wizrumorcheck command - verify each rumor access */ +int +wiz_rumor_check(void) +{ + rumor_check(); + return ECMD_OK; +} + +/* + * wizard mode sanity_check code + */ + +static const char template[] = "%-27s %4ld %6ld"; +static const char stats_hdr[] = " count bytes"; +static const char stats_sep[] = "--------------------------- ----- -------"; + +staticfn int +size_obj(struct obj *otmp) +{ + int sz = (int) sizeof (struct obj); + + if (otmp->oextra) { + sz += (int) sizeof (struct oextra); + if (ONAME(otmp)) + sz += (int) strlen(ONAME(otmp)) + 1; + if (OMONST(otmp)) + sz += size_monst(OMONST(otmp), FALSE); + if (OMAILCMD(otmp)) + sz += (int) strlen(OMAILCMD(otmp)) + 1; + /* sz += (int) sizeof (unsigned); -- now part of oextra itself */ + } + return sz; +} + +staticfn void +count_obj(struct obj *chain, long *total_count, long *total_size, + boolean top, boolean recurse) +{ + long count, size; + struct obj *obj; + + for (count = size = 0, obj = chain; obj; obj = obj->nobj) { + if (top) { + count++; + size += size_obj(obj); + } + if (recurse && obj->cobj) + count_obj(obj->cobj, total_count, total_size, TRUE, TRUE); + } + *total_count += count; + *total_size += size; +} + +DISABLE_WARNING_FORMAT_NONLITERAL /* RESTORE_WARNING follows wiz_show_stats */ + +staticfn void +obj_chain( + winid win, + const char *src, + struct obj *chain, + boolean force, + long *total_count, long *total_size) +{ + char buf[BUFSZ]; + long count = 0L, size = 0L; + + count_obj(chain, &count, &size, TRUE, FALSE); + + if (count || size || force) { + *total_count += count; + *total_size += size; + Sprintf(buf, template, src, count, size); + putstr(win, 0, buf); + } +} + +staticfn void +mon_invent_chain( + winid win, + const char *src, + struct monst *chain, + long *total_count, long *total_size) +{ + char buf[BUFSZ]; + long count = 0, size = 0; + struct monst *mon; + + for (mon = chain; mon; mon = mon->nmon) + count_obj(mon->minvent, &count, &size, TRUE, FALSE); + + if (count || size) { + *total_count += count; + *total_size += size; + Sprintf(buf, template, src, count, size); + putstr(win, 0, buf); + } +} + +staticfn void +contained_stats( + winid win, + const char *src, + long *total_count, long *total_size) +{ + char buf[BUFSZ]; + long count = 0, size = 0; + struct monst *mon; + + count_obj(gi.invent, &count, &size, FALSE, TRUE); + count_obj(fobj, &count, &size, FALSE, TRUE); + count_obj(svl.level.buriedobjlist, &count, &size, FALSE, TRUE); + count_obj(gm.migrating_objs, &count, &size, FALSE, TRUE); + /* DEADMONSTER check not required in this loop since they have no + * inventory */ + for (mon = fmon; mon; mon = mon->nmon) + count_obj(mon->minvent, &count, &size, FALSE, TRUE); + for (mon = gm.migrating_mons; mon; mon = mon->nmon) + count_obj(mon->minvent, &count, &size, FALSE, TRUE); + + if (count || size) { + *total_count += count; + *total_size += size; + Sprintf(buf, template, src, count, size); + putstr(win, 0, buf); + } +} + +staticfn int +size_monst(struct monst *mtmp, boolean incl_wsegs) +{ + int sz = (int) sizeof (struct monst); + + if (mtmp->wormno && incl_wsegs) + sz += size_wseg(mtmp); + + if (mtmp->mextra) { + sz += (int) sizeof (struct mextra); + if (MGIVENNAME(mtmp)) + sz += (int) strlen(MGIVENNAME(mtmp)) + 1; + if (EGD(mtmp)) + sz += (int) sizeof (struct egd); + if (EPRI(mtmp)) + sz += (int) sizeof (struct epri); + if (ESHK(mtmp)) + sz += (int) sizeof (struct eshk); + if (EMIN(mtmp)) + 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; +} + +staticfn void +mon_chain( + winid win, + const char *src, + struct monst *chain, + boolean force, + long *total_count, long *total_size) +{ + char buf[BUFSZ]; + long count, size; + struct monst *mon; + /* mon->wormno means something different for migrating_mons and mydogs */ + boolean incl_wsegs = !strcmpi(src, "fmon"); + + count = size = 0L; + for (mon = chain; mon; mon = mon->nmon) { + count++; + size += size_monst(mon, incl_wsegs); + } + if (count || size || force) { + *total_count += count; + *total_size += size; + Sprintf(buf, template, src, count, size); + putstr(win, 0, buf); + } +} + +staticfn void +misc_stats( + winid win, + long *total_count, long *total_size) +{ + char buf[BUFSZ], hdrbuf[QBUFSZ]; + long count, size; + int idx; + struct trap *tt; + struct damage *sd; /* shop damage */ + struct kinfo *k; /* delayed killer */ + struct cemetery *bi; /* bones info */ + + /* traps and engravings are output unconditionally; + * others only if nonzero + */ + count = size = 0L; + for (tt = gf.ftrap; tt; tt = tt->ntrap) { + ++count; + size += (long) sizeof *tt; + } + *total_count += count; + *total_size += size; + Sprintf(hdrbuf, "traps, size %ld", (long) sizeof (struct trap)); + Sprintf(buf, template, hdrbuf, count, size); + putstr(win, 0, buf); + + count = size = 0L; + engr_stats("engravings, size %ld+text", hdrbuf, &count, &size); + *total_count += count; + *total_size += size; + Sprintf(buf, template, hdrbuf, count, size); + putstr(win, 0, buf); + + count = size = 0L; + light_stats("light sources, size %ld", hdrbuf, &count, &size); + if (count || size) { + *total_count += count; + *total_size += size; + Sprintf(buf, template, hdrbuf, count, size); + putstr(win, 0, buf); + } + + count = size = 0L; + timer_stats("timers, size %ld", hdrbuf, &count, &size); + if (count || size) { + *total_count += count; + *total_size += size; + Sprintf(buf, template, hdrbuf, count, size); + putstr(win, 0, buf); + } + + count = size = 0L; + for (sd = svl.level.damagelist; sd; sd = sd->next) { + ++count; + size += (long) sizeof *sd; + } + if (count || size) { + *total_count += count; + *total_size += size; + Sprintf(hdrbuf, "shop damage, size %ld", + (long) sizeof (struct damage)); + Sprintf(buf, template, hdrbuf, count, size); + putstr(win, 0, buf); + } + + count = size = 0L; + region_stats("regions, size %ld+%ld*rect+N", hdrbuf, &count, &size); + if (count || size) { + *total_count += count; + *total_size += size; + Sprintf(buf, template, hdrbuf, count, size); + putstr(win, 0, buf); + } + + count = size = 0L; + for (k = svk.killer.next; k; k = k->next) { + ++count; + size += (long) sizeof *k; + } + if (count || size) { + *total_count += count; + *total_size += size; + Sprintf(hdrbuf, "delayed killer%s, size %ld", + plur(count), (long) sizeof (struct kinfo)); + Sprintf(buf, template, hdrbuf, count, size); + putstr(win, 0, buf); + } + + count = size = 0L; + for (bi = svl.level.bonesinfo; bi; bi = bi->next) { + ++count; + size += (long) sizeof *bi; + } + if (count || size) { + *total_count += count; + *total_size += size; + Sprintf(hdrbuf, "bones history, size %ld", + (long) sizeof (struct cemetery)); + Sprintf(buf, template, hdrbuf, count, size); + putstr(win, 0, buf); + } + + count = size = 0L; + for (idx = 0; idx < NUM_OBJECTS; ++idx) + if (objects[idx].oc_uname) { + ++count; + size += (long) (strlen(objects[idx].oc_uname) + 1); + } + if (count || size) { + *total_count += count; + *total_size += size; + Strcpy(hdrbuf, "object type names, text"); + Sprintf(buf, template, hdrbuf, count, size); + putstr(win, 0, buf); + } +} + +staticfn void +you_sanity_check(void) +{ + struct monst *mtmp; + + if (u.uswallow && !u.ustuck) { + /* this probably ought to be panic() */ + impossible("sanity_check: swallowed by nothing?"); + display_nhwindow(WIN_MESSAGE, TRUE); + /* try to recover from whatever the problem is */ + u.uswallow = 0; + u.uswldtim = 0; + docrt(); + } + if ((mtmp = m_at(u.ux, u.uy)) != 0) { + /* u.usteed isn't on the map */ + if (u.ustuck != mtmp) + impossible("sanity_check: you over monster"); + } + /* [should we also check for (u.uhp < 1), (Upolyd && u.mh < 1), + and (u.uen < 0) here?] */ + if (u.uhp > u.uhpmax) { + impossible("current hero health (%d) better than maximum? (%d)", + u.uhp, u.uhpmax); + u.uhp = u.uhpmax; + } + if (Upolyd && u.mh > u.mhmax) { + impossible( + "current hero health as monster (%d) better than maximum? (%d)", + u.mh, u.mhmax); + u.mh = u.mhmax; + } + if (u.uen > u.uenmax) { + impossible("current hero energy (%d) better than maximum? (%d)", + u.uen, u.uenmax); + u.uen = u.uenmax; + } + + check_wornmask_slots(); + (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) +{ + if (iflags.sanity_no_check) { + /* in case a recurring sanity_check warning occurs, we mustn't + re-trigger it when ^P is used, otherwise msg_window:Single + and msg_window:Combination will always repeat the most recent + instance, never able to go back to any earlier messages */ + iflags.sanity_no_check = FALSE; + return; + } + program_state.in_sanity_check++; + you_sanity_check(); + obj_sanity_check(); + timer_sanity_check(); + mon_sanity_check(); + light_sources_sanity_check(); + bc_sanity_check(); + trap_sanity_check(); + engraving_sanity_check(); + levl_sanity_check(); + program_state.in_sanity_check--; +} + +/* qsort() comparison routine for use in list_migrating_mons() */ +staticfn int QSORTCALLBACK +migrsort_cmp(const genericptr vptr1, const genericptr vptr2) +{ + const struct monst *m1 = *(const struct monst **) vptr1, + *m2 = *(const struct monst **) vptr2; + int d1 = (int) m1->mux, l1 = (int) m1->muy, + d2 = (int) m2->mux, l2 = (int) m2->muy; + + /* if different branches, sort by dungeon number */ + if (d1 != d2) + return d1 - d2; + /* within same branch, sort by level number */ + if (l1 != l2) + return l1 - l2; + /* same destination level: use a tie-breaker to force stable sort; + monst->m_id is unsigned so we need more than just simple subtraction */ + return (m1->m_id < m2->m_id) ? -1 : (m1->m_id > m2->m_id); +} + +/* called by #migratemons; displays count of migrating monsters, optionally + displays them as well */ +staticfn void +list_migrating_mons( + d_level *nextlevl) /* default destination for wiz_migrate_mons() */ +{ + winid win = WIN_ERR; + boolean showit = FALSE; + unsigned n; + int xyloc; + coordxy x, y; + char c, prmpt[10], xtra[10], buf[BUFSZ]; + struct monst *mtmp, **marray; + int here = 0, nxtlv = 0, other = 0; + + for (mtmp = gm.migrating_mons; mtmp; mtmp = mtmp->nmon) { + if (mtmp->mux == u.uz.dnum && mtmp->muy == u.uz.dlevel) + ++here; + else if (mtmp->mux == nextlevl->dnum && mtmp->muy == nextlevl->dlevel) + ++nxtlv; + else + ++other; + } + if (here + nxtlv + other == 0) { + pline("No monsters currently migrating."); + } else { + pline( + "%d mon%s pending for current level, %d for next level, %d for others.", + here, plur(here), nxtlv, other); + prmpt[0] = xtra[0] = '\0'; + (void) strkitten(here ? prmpt : xtra, 'c'); + (void) strkitten(nxtlv ? prmpt : xtra, 'n'); + (void) strkitten(other ? prmpt : xtra, 'o'); + Strcat(prmpt, "a q"); + if (*xtra) + Sprintf(eos(prmpt), "%c%s", '\033', xtra); + c = yn_function("List which?", prmpt, 'q', TRUE); + n = (c == 'c') ? here + : (c == 'n') ? nxtlv + : (c == 'o') ? other + : (c == 'a') ? here + nxtlv + other + : 0; + if (n > 0) { + win = create_nhwindow(NHW_TEXT); + switch (c) { + case 'c': + case 'n': + case 'o': + Sprintf(buf, "Monster%s migrating to %s:", plur(n), + (c == 'c') ? "current level" + : (c == 'n') ? "next level" + : "'other' levels"); + break; + default: + Strcpy(buf, "All migrating monsters:"); + break; + } + putstr(win, 0, buf); + putstr(win, 0, ""); + /* collect the migrating monsters into an array; for 'o' and 'a' + where multiple destination levels might be present, sort by + the destination; 'c' and 'n' don't need to be sorted but we + do that anyway to get the same tie-breaker as 'o' and 'a' */ + marray = (struct monst **) alloc((n + 1) * sizeof *marray); + n = 0; + for (mtmp = gm.migrating_mons; mtmp; mtmp = mtmp->nmon) { + if (c == 'a') + showit = TRUE; + else if (mtmp->mux == u.uz.dnum && mtmp->muy == u.uz.dlevel) + showit = (c == 'c'); + else if (mtmp->mux == nextlevl->dnum + && mtmp->muy == nextlevl->dlevel) + showit = (c == 'n'); + else + showit = (c == 'o'); + + if (showit) + marray[n++] = mtmp; + } + marray[n] = (struct monst *) 0; /* mark end for traversal loop */ + if (n > 1) + qsort((genericptr_t) marray, (size_t) n, sizeof *marray, + migrsort_cmp); /* sort elements [0] through [n-1] */ + for (n = 0; (mtmp = marray[n]) != 0; ++n) { + Sprintf(buf, " %s", minimal_monnam(mtmp, FALSE)); + /* minimal_monnam() appends map coordinates; strip that */ + (void) strsubst(buf, " <0,0>", ""); + if (has_mgivenname(mtmp)) /* if mtmp is named, include that */ + Sprintf(eos(buf), " named %s", MGIVENNAME(mtmp)); + if (c == 'o' || c == 'a') + Sprintf(eos(buf), " to %d:%d", mtmp->mux, mtmp->muy); + xyloc = mtmp->mtrack[0].x; /* (for legibility) */ + if (xyloc == MIGR_EXACT_XY) { + x = mtmp->mtrack[1].x; + y = mtmp->mtrack[1].y; + Sprintf(eos(buf), " at <%d,%d>", (int) x, (int) y); + } + putstr(win, 0, buf); + } + free((genericptr_t) marray); + display_nhwindow(win, FALSE); + destroy_nhwindow(win); + } else if (c != 'q') { + pline("None."); + } + + } +} + +/* the #stats command + * Display memory usage of all monsters and objects on the level. + */ +int +wiz_show_stats(void) +{ + char buf[BUFSZ]; + winid win; + long total_obj_size, total_obj_count, + total_mon_size, total_mon_count, + total_ovr_size, total_ovr_count, + total_misc_size, total_misc_count; + + win = create_nhwindow(NHW_TEXT); + putstr(win, 0, "Current memory statistics:"); + + total_obj_count = total_obj_size = 0L; + putstr(win, 0, stats_hdr); + Sprintf(buf, " Objects, base size %ld", (long) sizeof (struct obj)); + putstr(win, 0, buf); + obj_chain(win, "invent", gi.invent, TRUE, + &total_obj_count, &total_obj_size); + obj_chain(win, "fobj", fobj, TRUE, &total_obj_count, &total_obj_size); + obj_chain(win, "buried", svl.level.buriedobjlist, FALSE, + &total_obj_count, &total_obj_size); + obj_chain(win, "migrating obj", gm.migrating_objs, FALSE, + &total_obj_count, &total_obj_size); + obj_chain(win, "billobjs", gb.billobjs, FALSE, + &total_obj_count, &total_obj_size); + mon_invent_chain(win, "minvent", fmon, &total_obj_count, &total_obj_size); + mon_invent_chain(win, "migrating minvent", gm.migrating_mons, + &total_obj_count, &total_obj_size); + contained_stats(win, "contained", &total_obj_count, &total_obj_size); + putstr(win, 0, stats_sep); + Sprintf(buf, template, " Obj total", total_obj_count, total_obj_size); + putstr(win, 0, buf); + + total_mon_count = total_mon_size = 0L; + putstr(win, 0, ""); + Sprintf(buf, " Monsters, base size %ld", (long) sizeof (struct monst)); + putstr(win, 0, buf); + mon_chain(win, "fmon", fmon, TRUE, &total_mon_count, &total_mon_size); + mon_chain(win, "migrating", gm.migrating_mons, FALSE, + &total_mon_count, &total_mon_size); + /* 'gm.mydogs' is only valid during level change or end of game disclosure, + but conceivably we've been called from within debugger at such time */ + if (gm.mydogs) /* monsters accompanying hero */ + mon_chain(win, "mydogs", gm.mydogs, FALSE, + &total_mon_count, &total_mon_size); + putstr(win, 0, stats_sep); + Sprintf(buf, template, " Mon total", total_mon_count, total_mon_size); + putstr(win, 0, buf); + + total_ovr_count = total_ovr_size = 0L; + putstr(win, 0, ""); + putstr(win, 0, " Overview"); + overview_stats(win, template, &total_ovr_count, &total_ovr_size); + putstr(win, 0, stats_sep); + Sprintf(buf, template, " Over total", total_ovr_count, total_ovr_size); + putstr(win, 0, buf); + + total_misc_count = total_misc_size = 0L; + putstr(win, 0, ""); + putstr(win, 0, " Miscellaneous"); + misc_stats(win, &total_misc_count, &total_misc_size); + putstr(win, 0, stats_sep); + Sprintf(buf, template, " Misc total", total_misc_count, total_misc_size); + putstr(win, 0, buf); + + putstr(win, 0, ""); + putstr(win, 0, stats_sep); + Sprintf(buf, template, " Grand total", + (total_obj_count + total_mon_count + + total_ovr_count + total_misc_count), + (total_obj_size + total_mon_size + + total_ovr_size + total_misc_size)); + putstr(win, 0, buf); + +#if defined(__BORLANDC__) && !defined(_WIN32) + show_borlandc_stats(win); +#endif + + display_nhwindow(win, FALSE); + destroy_nhwindow(win); + return ECMD_OK; +} + +RESTORE_WARNING_FORMAT_NONLITERAL + +#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) +/* the #wizdispmacros command + * Verify that some display macros are returning sane values */ +int +wiz_display_macros(void) +{ + static const char display_issues[] = "Display macro issues:"; + char buf[BUFSZ]; + winid win; + int glyph, test, trouble = 0, no_glyph = NO_GLYPH, max_glyph = MAX_GLYPH; + + win = create_nhwindow(NHW_TEXT); + + for (glyph = 0; glyph < MAX_GLYPH; ++glyph) { + /* glyph_is_cmap / glyph_to_cmap() */ + if (glyph_is_cmap(glyph)) { + test = glyph_to_cmap(glyph); + /* check for MAX_GLYPH return */ + if (test == no_glyph) { + if (!trouble++) + putstr(win, 0, display_issues); + Sprintf(buf, "glyph_is_cmap() / glyph_to_cmap(glyph=%d)" + " sync failure, returned NO_GLYPH (%d)", + glyph, test); + putstr(win, 0, buf); + } + if (glyph_is_cmap_zap(glyph) + && !(test >= S_vbeam && test <= S_rslant)) { + if (!trouble++) + putstr(win, 0, display_issues); + Sprintf(buf, + "glyph_is_cmap_zap(glyph=%d) returned non-zap cmap %d", + glyph, test); + putstr(win, 0, buf); + } + /* check against defsyms array subscripts */ + if (!IndexOk(test, defsyms)) { + if (!trouble++) + putstr(win, 0, display_issues); + Sprintf(buf, "glyph_to_cmap(glyph=%d) returns %d" + " exceeds defsyms[%d] bounds (MAX_GLYPH = %d)", + glyph, test, SIZE(defsyms), max_glyph); + putstr(win, 0, buf); + } + } + /* glyph_is_monster / glyph_to_mon */ + if (glyph_is_monster(glyph)) { + test = glyph_to_mon(glyph); + /* check against mons array subscripts */ + if (test < 0 || test >= NUMMONS) { + if (!trouble++) + putstr(win, 0, display_issues); + Sprintf(buf, "glyph_to_mon(glyph=%d) returns %d" + " exceeds mons[%d] bounds", + glyph, test, NUMMONS); + putstr(win, 0, buf); + } + } + /* glyph_is_object / glyph_to_obj */ + if (glyph_is_object(glyph)) { + test = glyph_to_obj(glyph); + /* check against objects array subscripts */ + if (test < 0 || test > NUM_OBJECTS) { + if (!trouble++) + putstr(win, 0, display_issues); + Sprintf(buf, "glyph_to_obj(glyph=%d) returns %d" + " exceeds objects[%d] bounds", + glyph, test, NUM_OBJECTS); + putstr(win, 0, buf); + } + } + } + if (!trouble) + putstr(win, 0, "No display macro issues detected."); + display_nhwindow(win, FALSE); + destroy_nhwindow(win); + return ECMD_OK; +} +#endif /* (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) */ + +#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) +/* the #wizmondiff command */ +int +wiz_mon_diff(void) +{ + static const char window_title[] = "Review of monster difficulty ratings" + " [index:level]:"; + char buf[BUFSZ]; + winid win; + int mhardcoded = 0, mcalculated = 0, trouble = 0, cnt = 0, mdiff = 0; + int mlev; + struct permonst *ptr; + + /* + * Possible extension: choose between showing discrepancies, + * showing all monsters, or monsters within a particular class. + */ + + win = create_nhwindow(NHW_TEXT); + for (ptr = &mons[0]; ptr->mlet; ptr++, cnt++) { + mcalculated = mstrength(ptr); + mhardcoded = (int) ptr->difficulty; + mdiff = mhardcoded - mcalculated; + if (mdiff) { + if (!trouble++) + putstr(win, 0, window_title); + mlev = (int) ptr->mlevel; + if (mlev > 50) /* hack for named demons */ + mlev = 50; + Snprintf(buf, sizeof buf, + "%-18s [%3d:%2d]: calculated: %2d, hardcoded: %2d (%+d)", + ptr->pmnames[NEUTRAL], cnt, mlev, + mcalculated, mhardcoded, mdiff); + putstr(win, 0, buf); + } + } + if (!trouble) + putstr(win, 0, "No monster difficulty discrepancies were detected."); + display_nhwindow(win, FALSE); + destroy_nhwindow(win); + return ECMD_OK; +} +#endif /* (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) */ + +/* #migratemons command */ +int +wiz_migrate_mons(void) +{ +#ifdef DEBUG_MIGRATING_MONS + int mcount; + char inbuf[BUFSZ]; + struct permonst *ptr; + struct monst *mtmp; + boolean use_random_mon = TRUE; +#endif + d_level tolevel; + + if (Is_stronghold(&u.uz)) + assign_level(&tolevel, &valley_level); + else if (!Is_botlevel(&u.uz)) + get_level(&tolevel, depth(&u.uz) + 1); + else + tolevel.dnum = 0, tolevel.dlevel = 0; + + list_migrating_mons(&tolevel); + +#ifdef DEBUG_MIGRATING_MONS + inbuf[0] = inbuf[1] = '\0'; + if (tolevel.dnum || tolevel.dlevel) + getlin("How many random monsters to migrate to next level? [0]", + inbuf); + else + pline("Can't get there from here."); + if (*inbuf == '\033' || *inbuf == '\0') + 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) { + 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); + mcount--; + } +#endif /* DEBUG_MIGRATING_MONS */ + return ECMD_OK; +} + +/* #wizcustom command to see glyphmap customizations */ +int +wiz_custom(void) +{ + extern const char *const known_handling[]; /* symbols.c */ + + if (wizard) { + static const char wizcustom[] = "#wizcustom"; + winid win; + char buf[BUFSZ], bufa[BUFSZ]; + int n; +#if 0 + int j, glyph; +#endif + menu_item *pick_list = (menu_item *) 0; + + if (!glyphid_cache_status()) + fill_glyphid_cache(); + + win = create_nhwindow(NHW_MENU); + start_menu(win, MENU_BEHAVE_STANDARD); + add_menu_heading(win, + " glyph glyph identifier " + " sym clr customcolor unicode utf8"); + Sprintf(bufa, "%s: colorcount=%ld %s", wizcustom, + (long) iflags.colorcount, + gs.symset[PRIMARYSET].name ? gs.symset[PRIMARYSET].name + : "default"); + if (gc.currentgraphics == PRIMARYSET && gs.symset[PRIMARYSET].name) + Strcat(bufa, ", active"); + if (gs.symset[PRIMARYSET].handling) { + Sprintf(eos(bufa), ", handler=%s", + known_handling[gs.symset[PRIMARYSET].handling]); + } + Sprintf(buf, "%s", bufa); + wizcustom_glyphids(win); + end_menu(win, bufa); + n = select_menu(win, PICK_NONE, &pick_list); + destroy_nhwindow(win); +#if 0 + for (j = 0; j < n; ++j) { + glyph = pick_list[j].item.a_int - 1; /* -1: reverse +1 above */ + } +#endif + if (n >= 1) + free((genericptr_t) pick_list); + if (glyphid_cache_status()) + free_glyphid_cache(); + docrt(); + } else + pline(unavailcmd, ecname_from_fn(wiz_custom)); + return ECMD_OK; +} + +void +wizcustom_callback(winid win, int glyphnum, char *id) +{ + extern glyph_map glyphmap[MAX_GLYPH]; + glyph_map *cgm; + int clr = NO_COLOR; + char buf[BUFSZ], bufa[BUFSZ], bufb[BUFSZ], bufc[BUFSZ], bufd[BUFSZ], + bufu[BUFSZ]; + anything any; + uint8 *cp; + + if (win && id) { + cgm = &glyphmap[glyphnum]; + if ( +#ifdef ENHANCED_SYMBOLS + cgm->u || +#endif + cgm->customcolor != 0) { + Sprintf(bufa, "[%04d] %-44s", glyphnum, id); + Sprintf(bufb, "'\\%03d' %02d", + gs.showsyms[cgm->sym.symidx], cgm->sym.color); + Sprintf(bufc, "%011lx", (unsigned long) cgm->customcolor); + bufu[0] = '\0'; +#ifdef ENHANCED_SYMBOLS + if (cgm->u && cgm->u->utf8str) { + Sprintf(bufu, "U+%04lx", (unsigned long) cgm->u->utf32ch); + cp = cgm->u->utf8str; + while (*cp) { + Sprintf(bufd, " <%d>", (int) *cp); + Strcat(bufu, bufd); + cp++; + } + } +#endif + any.a_int = glyphnum + 1; /* avoid 0 */ + Snprintf(buf, sizeof buf, "%s %s %s %s", bufa, bufb, bufc, bufu); + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, buf, + MENU_ITEMFLAGS_NONE); + } + } + return; +} + +/*wizcmds.c*/ diff --git a/src/worm.c b/src/worm.c index cb855dd6c7..fbf213cc87 100644 --- a/src/worm.c +++ b/src/worm.c @@ -14,12 +14,12 @@ struct wseg { coordxy wx, wy; /* the segment's position */ }; -static void toss_wsegs(struct wseg *, boolean); -static void shrink_worm(int); +staticfn void toss_wsegs(struct wseg *, boolean) NO_NNARGS; +staticfn void shrink_worm(int); #if 0 -static void random_dir(int, int, int *, int *); +staticfn void random_dir(int, int, int *, int *); #endif -static struct wseg *create_worm_tail(int); +staticfn struct wseg *create_worm_tail(int); /* may return NULL */ /* Description of long worm implementation. * @@ -138,8 +138,7 @@ initworm(struct monst *worm, int wseg_count) * Get rid of all worm segments on and following the given pointer curr. * The display may or may not need to be updated as we free the segments. */ -static -void +staticfn void toss_wsegs(struct wseg *curr, boolean display_update) { struct wseg *nxtseg; @@ -168,8 +167,7 @@ toss_wsegs(struct wseg *curr, boolean display_update) * * Remove the tail segment of the worm (the starting segment of the list). */ -static -void +staticfn void shrink_worm(int wnum) /* worm number */ { struct wseg *seg; @@ -213,13 +211,13 @@ worm_move(struct monst *worm) seg->nseg = new_seg; /* attach it to the end of the list */ wheads[wnum] = new_seg; /* move the end pointer */ - if (wgrowtime[wnum] <= gm.moves) { + if (wgrowtime[wnum] <= svm.moves) { int whplimit, whpcap, prev_mhp, wsegs = count_wsegs(worm); /* first set up for the next time to grow */ if (!wgrowtime[wnum]) { /* new worm; usually grow a tail segment on its next turn */ - wgrowtime[wnum] = gm.moves + rnd(5); + wgrowtime[wnum] = svm.moves + rnd(5); } else { int mmove = mcalcmove(worm, FALSE), /* prior to 3.7.0, next-grow increment was 3..17 but since @@ -232,7 +230,7 @@ worm_move(struct monst *worm) * speed of 3, effective value is 8..48 */ incr = (incr * NORMAL_SPEED) / max(mmove, 1); - wgrowtime[wnum] = gm.moves + incr; + wgrowtime[wnum] = svm.moves + incr; } /* increase HP based on number of segments; if it has shrunk, it @@ -434,7 +432,7 @@ cutworm(struct monst *worm, coordxy x, coordxy y, /* Sometimes the tail end dies. */ if (!new_worm) { place_worm_seg(worm, x, y); /* place the "head" segment back */ - if (gc.context.mon_moving) { + if (svc.context.mon_moving) { if (canspotmon(worm)) pline("Part of %s tail has been cut off.", s_suffix(mon_nam(worm))); @@ -468,7 +466,7 @@ cutworm(struct monst *worm, coordxy x, coordxy y, /* Place the new monster at all the segment locations. */ place_wsegs(new_worm, worm); - if (gc.context.mon_moving) + if (svc.context.mon_moving) pline("%s is cut in half.", Monnam(worm)); else You("cut %s in half.", mon_nam(worm)); @@ -674,9 +672,9 @@ sanity_check_worm(struct monst *worm) x = curr->wx, y = curr->wy; if (!isok(x, y)) impossible("worm seg not isok <%d,%d>", x, y); - else if (gl.level.monsters[x][y] != worm) + else if (svl.level.monsters[x][y] != worm) impossible("mon (%s) at seg location is not worm (%s)", - fmt_ptr((genericptr_t) gl.level.monsters[x][y]), + fmt_ptr((genericptr_t) svl.level.monsters[x][y]), fmt_ptr((genericptr_t) worm)); curr = curr->nseg; @@ -805,7 +803,7 @@ place_worm_tail_randomly(struct monst *worm, coordxy x, coordxy y) * This function, and the loop it serves, could be eliminated by coding * enexto() with a search radius. */ -static void +staticfn void random_dir(int x, int y, int *nx, int *ny) { *nx = x + (x > 1 /* extreme left ? */ @@ -854,8 +852,7 @@ count_wsegs(struct monst *mtmp) /* create_worm_tail() * will create a worm tail chain of (num_segs + 1) and return pointer to it. */ -static -struct wseg * +staticfn struct wseg * create_worm_tail(int num_segs) { int i = 0; @@ -993,6 +990,7 @@ flip_worm_segs_horizontal(struct monst *worm, int minx, int maxx) } } +/* previously a macro in rm.h; moved here to handle fuzzer logging */ void place_worm_seg(struct monst *worm, coordxy x, coordxy y) { @@ -1000,10 +998,21 @@ place_worm_seg(struct monst *worm, coordxy x, coordxy y) fuzl_xy("place_worm_seg", x,y); #endif #ifdef EXTRA_SANITY_CHECKS - if (gl.level.monsters[x][y] && gl.level.monsters[x][y] != worm) + if (svl.level.monsters[x][y] && svl.level.monsters[x][y] != worm) impossible("place_worm_seg over mon"); #endif - gl.level.monsters[x][y] = worm; + svl.level.monsters[x][y] = worm; +} + +void +redraw_worm(struct monst *worm) +{ + struct wseg *curr = wtails[worm->wormno]; + + while (curr) { + newsym(curr->wx, curr->wy); + curr = curr->nseg; + } } /*worm.c*/ diff --git a/src/worn.c b/src/worn.c index 1211675bce..3d6573b0c9 100644 --- a/src/worn.c +++ b/src/worn.c @@ -1,35 +1,37 @@ -/* NetHack 3.7 worn.c $NHDT-Date: 1652577035 2022/05/15 01:10:35 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.84 $ */ +/* 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. */ #include "hack.h" -static void m_lose_armor(struct monst *, struct obj *); -static void clear_bypass(struct obj *); -static void m_dowear_type(struct monst *, long, boolean, boolean); -static int extra_pref(struct monst *, struct obj *); +staticfn void m_lose_armor(struct monst *, struct obj *, boolean) NONNULLPTRS; +staticfn void clear_bypass(struct obj *) NO_NNARGS; +staticfn void m_dowear_type(struct monst *, long, boolean, boolean) + NONNULLARG1; +staticfn int extra_pref(struct monst *, struct obj *) NONNULLARG1; -const struct worn { +static const struct worn { long w_mask; struct obj **w_obj; -} worn[] = { { W_ARM, &uarm }, - { W_ARMC, &uarmc }, - { W_ARMH, &uarmh }, - { W_ARMS, &uarms }, - { W_ARMG, &uarmg }, - { W_ARMF, &uarmf }, - { W_ARMU, &uarmu }, - { W_RINGL, &uleft }, - { W_RINGR, &uright }, - { W_WEP, &uwep }, - { W_SWAPWEP, &uswapwep }, - { W_QUIVER, &uquiver }, - { W_AMUL, &uamul }, - { W_TOOL, &ublindf }, - { W_BALL, &uball }, - { W_CHAIN, &uchain }, - { 0, 0 } + const char *w_what; /* for failing sanity check's feedback */ +} worn[] = { { W_ARM, &uarm, "suit" }, + { W_ARMC, &uarmc, "cloak" }, + { W_ARMH, &uarmh, "helmet" }, + { W_ARMS, &uarms, "shield" }, + { W_ARMG, &uarmg, "gloves" }, + { W_ARMF, &uarmf, "boots" }, + { W_ARMU, &uarmu, "shirt" }, + { W_RINGL, &uleft, "left ring" }, + { W_RINGR, &uright, "right ring" }, + { W_WEP, &uwep, "weapon" }, + { W_SWAPWEP, &uswapwep, "alternate weapon" }, + { W_QUIVER, &uquiver, "quiver" }, + { W_AMUL, &uamul, "amulet" }, + { W_TOOL, &ublindf, "facewear" }, /* blindfold|towel|lenses */ + { W_BALL, &uball, "chained ball" }, + { W_CHAIN, &uchain, "attached chain" }, + { 0, 0, (char *) 0 } }; /* This only allows for one blocking item per property */ @@ -43,13 +45,36 @@ const struct worn { /* note: monsters don't have clairvoyance, so dependency on hero's role here has no significant effect on their use of w_blocks() */ +/* calc the range of hero's unblind telepathy */ +void +recalc_telepat_range(void) +{ + const struct worn *wp; + int nobjs = 0; + + for (wp = worn; wp->w_mask; wp++) { + struct obj *oobj = *(wp->w_obj); + + if (oobj && objects[oobj->otyp].oc_oprop == TELEPAT) + nobjs++; + } + /* count all artifacts with SPFX_ESP as one */ + if (ETelepat & W_ART) + nobjs++; + + if (nobjs) + u.unblind_telepat_range = (BOLT_LIM * BOLT_LIM) * nobjs; + else + u.unblind_telepat_range = -1; +} + /* Updated to use the extrinsic and blocked fields. */ void setworn(struct obj *obj, long mask) { - register const struct worn *wp; - register struct obj *oobj; - register int p; + const struct worn *wp; + struct obj *oobj; + int p; if ((mask & I_SPECIAL) != 0 && (mask & (W_ARM | W_ARMC)) != 0) { /* restoring saved game; no properties are conferred via skin */ @@ -72,6 +97,10 @@ setworn(struct obj *obj, long mask) p = armor_provides_extrinsic(oobj); u.uprops[p].extrinsic = u.uprops[p].extrinsic & ~wp->w_mask; + /* if the hero removed an extrinsic-granting item, + nearby monsters will notice and attempt attacks of + that type again */ + monstunseesu_prop(p); if ((p = w_blocks(oobj, mask)) != 0) u.uprops[p].blocked &= ~wp->w_mask; if (oobj->oartifact) @@ -111,6 +140,7 @@ setworn(struct obj *obj, long mask) iflags.tux_penalty = (uarm && Role_if(PM_MONK)); } update_inventory(); + recalc_telepat_range(); } /* called e.g. when obj is destroyed */ @@ -118,8 +148,8 @@ setworn(struct obj *obj, long mask) void setnotworn(struct obj *obj) { - register const struct worn *wp; - register int p; + const struct worn *wp; + int p; if (!obj) return; @@ -134,6 +164,7 @@ setnotworn(struct obj *obj) *(wp->w_obj) = (struct obj *) 0; p = armor_provides_extrinsic(obj); u.uprops[p].extrinsic = u.uprops[p].extrinsic & ~wp->w_mask; + monstunseesu_prop(p); /* remove this extrinsic from seenres */ obj->owornmask &= ~wp->w_mask; if (wp->w_mask & W_ARMOR) /* this function can technically be called with wielded or @@ -148,6 +179,7 @@ setnotworn(struct obj *obj) if (!uarm) iflags.tux_penalty = FALSE; update_inventory(); + recalc_telepat_range(); } /* called when saving with FREEING flag set has just discarded inventory */ @@ -253,6 +285,126 @@ wearslot(struct obj *obj) return res; } +/* for 'sanity_check' option, called by you_sanity_check() */ +void +check_wornmask_slots(void) +{ + /* we'll skip ball and chain here--they warrant separate sanity check */ +#define IGNORE_SLOTS (W_ART | W_ARTI | W_SADDLE | W_BALL| W_CHAIN) + char whybuf[BUFSZ]; + const struct worn *wp; + struct obj *o, *otmp; + long m; + + for (wp = worn; wp->w_mask; wp++) { + m = wp->w_mask; + if ((m & IGNORE_SLOTS) != 0L && (m & ~IGNORE_SLOTS) == 0L) + continue; + if ((o = *wp->w_obj) != 0) { + whybuf[0] = '\0'; + /* slot pointer (uarm, uwep, &c) is populated; check that object + is in inventory and has the relevant owornmask bit set */ + for (otmp = gi.invent; otmp; otmp = otmp->nobj) + if (otmp == o) + break; + if (!otmp) + Sprintf(whybuf, "%s (%s) not found in invent", + wp->w_what, fmt_ptr(o)); + else if ((o->owornmask & m) == 0L) + Sprintf(whybuf, "%s bit not set in owornmask [0x%08lx]", + wp->w_what, o->owornmask); + else if ((o->owornmask & ~(m | IGNORE_SLOTS)) != 0L) + Sprintf(whybuf, "%s wrong bit set in owornmask [0x%08lx]", + wp->w_what, o->owornmask); + if (whybuf[0]) + impossible("Worn-slot insanity: %s.", whybuf); + } /* o != NULL */ + + /* check whether any item other than the one in the slot pointer + claims to be worn/wielded in this slot; make this test whether + 'o' is Null or not; [sanity_check_worn(mkobj.c) for object by + object checking will most likely have already caught this] */ + for (otmp = gi.invent; otmp; otmp = otmp->nobj) { + if (otmp != o && (otmp->owornmask & m) != 0L + /* embedded scales owornmask is W_ARM|I_SPECIAL so would + give a false complaint about item other than uarm having + W_ARM bit set if we didn't screen it out here */ + && (m != W_ARM || otmp != uskin + || (otmp->owornmask & I_SPECIAL) == 0L)) { + Sprintf(whybuf, "%s [0x%08lx] has %s mask 0x%08lx bit set", + simpleonames(otmp), otmp->owornmask, wp->w_what, m); + impossible("Worn-slot insanity: %s.", whybuf); + } + } + } /* for wp in worn[] */ + +#ifdef EXTRA_SANITY_CHECKS + if (uskin) { + const char *what = "embedded scales"; + + o = uskin; + m = W_ARM | I_SPECIAL; + whybuf[0] = '\0'; + for (otmp = gi.invent; otmp; otmp = otmp->nobj) + if (otmp == o) + break; + if (!otmp) + Sprintf(whybuf, "%s (%s) not found in invent", + what, fmt_ptr(o)); + else if ((o->owornmask & m) != m) + Sprintf(whybuf, "%s bits not set in owornmask [0x%08lx]", + what, o->owornmask); + else if ((o->owornmask & ~(m | IGNORE_SLOTS)) != 0L) + Sprintf(whybuf, "%s wrong bit set in owornmask [0x%08lx]", + what, o->owornmask); + else if (!Is_dragon_scales(o)) + Sprintf(whybuf, "%s (%s) %s not dragon scales", + what, simpleonames(o), otense(o, "are")); + else if (Dragon_armor_to_pm(o) != u.umonnum) + Sprintf(whybuf, "%s, hero is not %s", + what, an(mons[u.umonnum].pmnames[NEUTRAL])); + if (whybuf[0]) + impossible("Worn-slot insanity: %s.", whybuf); + } /* uskin */ +#endif /* EXTRA_SANITY_CHECKS */ + +#ifdef EXTRA_SANITY_CHECKS + /* dual wielding: not a slot but lots of things to verify */ + if (u.twoweap) { + const char *why = NULL; + + if (!uwep || !uswapwep) { + Sprintf(whybuf, "without %s%s%s", + !uwep ? "uwep" : "", + (!uwep && !uswapwep) ? " and without " : "", + !uswapwep ? "uswapwep" : ""); + why = whybuf; + } else if (uarms) + why = "while wearing shield"; + else if (uwep->oclass != WEAPON_CLASS && !is_weptool(uwep)) + why = "uwep is not a weapon"; + else if (is_launcher(uwep) || is_ammo(uwep) || is_missile(uwep)) + why = "uwep is not a melee weapon"; + else if (bimanual(uwep)) + why = "uwep is two-handed"; + else if (uswapwep->oclass != WEAPON_CLASS && !is_weptool(uswapwep)) + why = "uswapwep is not a weapon"; + else if (is_launcher(uswapwep) || is_ammo(uswapwep) + || is_missile(uswapwep)) + why = "uswapwep is not a melee weapon"; + else if (bimanual(uswapwep)) + why = "uswapwep is two-handed"; + else if (!could_twoweap(gy.youmonst.data)) + why = "without two weapon attacks"; + + if (why) + impossible("Two-weapon insanity: %s.", why); + } +#endif /* EXTRA_SANITY_CHECKS */ + return; +#undef IGNORE_SLOTS +} /* check_wornmask_slots() */ + void mon_set_minvis(struct monst *mon) { @@ -329,12 +481,14 @@ mon_adjust_speed( if (petrify) { /* mimic the player's petrification countdown; "slowing down" even if fast movement rate retained via worn speed boots */ - if (Verbose(3, mon_adjust_speed)) - pline("%s is slowing down.", Monnam(mon)); + if (flags.verbose) + 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) @@ -408,12 +562,7 @@ update_mon_extrinsics( mon->mextrinsics |= MR2_TELEPATHY; break; default: - /* 1 through 8 correspond to MR_xxx mask values */ - if (which >= 1 && which <= 8) { - /* FIRE,COLD,SLEEP,DISINT,SHOCK,POISON,ACID,STONE */ - mask = 1 << (which - 1); - mon->mextrinsics |= mask; - } + mon->mextrinsics |= (unsigned short) res_to_mr(which); break; } } else { /* off */ @@ -456,6 +605,7 @@ update_mon_extrinsics( * only one pass but a worn alchemy smock will be an * alternate source for either of those two resistances. */ + mask = res_to_mr(which); for (otmp = mon->minvent; otmp; otmp = otmp->nobj) { if (otmp == obj || !otmp->owornmask) continue; @@ -507,7 +657,7 @@ update_mon_extrinsics( int find_mac(struct monst *mon) { - register struct obj *obj; + struct obj *obj; int base = mon->data->ac; long mwflags = mon->misc_worn_check; @@ -586,11 +736,11 @@ m_dowear(struct monst *mon, boolean creation) m_dowear_type(mon, W_ARM, creation, RACE_EXCEPTION); } -static void +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 */ { @@ -720,14 +870,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"), @@ -812,7 +979,7 @@ which_armor(struct monst *mon, long flag) return 0; } } else { - register struct obj *obj; + struct obj *obj; for (obj = mon->minvent; obj; obj = obj->nobj) if (obj->owornmask & flag) @@ -822,17 +989,22 @@ which_armor(struct monst *mon, long flag) } /* remove an item of armor and then drop it */ -static void -m_lose_armor(struct monst *mon, struct obj *obj) +staticfn void +m_lose_armor( + struct monst *mon, + struct obj *obj, + boolean polyspot) { extract_from_minvent(mon, obj, TRUE, FALSE); place_object(obj, mon->mx, mon->my); + if (polyspot) + bypass_obj(obj); /* call stackobj() if we ever drop anything that can merge */ newsym(mon->mx, mon->my); } /* clear bypass bits for an object chain, plus contents if applicable */ -static void +staticfn void clear_bypass(struct obj *objchn) { struct obj *o; @@ -846,7 +1018,7 @@ clear_bypass(struct obj *objchn) /* all objects with their bypass bit set should now be reset to normal; this can be a relatively expensive operation so is only called if - gc.context.bypasses is set */ + svc.context.bypasses is set */ void clear_bypasses(void) { @@ -855,15 +1027,16 @@ clear_bypasses(void) /* * 'Object' bypass is also used for one monster function: * polymorph control of long worms. Activated via setting - * gc.context.bypasses even if no specific object has been + * svc.context.bypasses even if no specific object has been * bypassed. */ clear_bypass(fobj); clear_bypass(gi.invent); clear_bypass(gm.migrating_objs); - clear_bypass(gl.level.buriedobjlist); + clear_bypass(svl.level.buriedobjlist); clear_bypass(gb.billobjs); + clear_bypass(go.objs_deleted); for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; @@ -892,14 +1065,14 @@ clear_bypasses(void) if (uchain) uchain->bypass = 0; - gc.context.bypasses = FALSE; + svc.context.bypasses = FALSE; } void bypass_obj(struct obj *obj) { obj->bypass = 1; - gc.context.bypasses = TRUE; + svc.context.bypasses = TRUE; } /* set or clear the bypass bit in a list of objects */ @@ -909,7 +1082,7 @@ bypass_objlist( boolean on) /* TRUE => set, FALSE => clear */ { if (on && objchain) - gc.context.bypasses = TRUE; + svc.context.bypasses = TRUE; while (objchain) { objchain->bypass = on ? 1 : 0; objchain = objchain->nobj; @@ -956,10 +1129,11 @@ nxt_unbypassed_loot(Loot *lootarray, struct obj *listhead) void mon_break_armor(struct monst *mon, boolean polyspot) { - register struct obj *otmp; + struct obj *otmp; struct permonst *mdat = mon->data; - boolean vis = cansee(mon->mx, mon->my); - boolean handless_or_tiny = (nohands(mdat) || verysmall(mdat)); + boolean vis = cansee(mon->mx, mon->my), + handless_or_tiny = (nohands(mdat) || verysmall(mdat)), + noride = FALSE; const char *pronoun = mhim(mon), *ppronoun = mhis(mon); if (breakarm(mdat)) { @@ -972,7 +1146,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."); } @@ -983,11 +1158,9 @@ 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)); - if (polyspot) - bypass_obj(otmp); - m_lose_armor(mon, otmp); + m_lose_armor(mon, otmp, polyspot); } else { if (Is_dragon_armor(otmp) && mdat == &mons[Dragon_armor_to_pm(otmp)]) { @@ -996,8 +1169,9 @@ mon_break_armor(struct monst *mon, boolean polyspot) else { Soundeffect(se_ripping_sound, 100); if (vis) - pline("%s %s tears apart!", s_suffix(Monnam(mon)), - cloak_simple_name(otmp)); + pline_mon(mon, "%s %s tears apart!", + s_suffix(Monnam(mon)), + cloak_simple_name(otmp)); else You_hear("a ripping sound."); } @@ -1006,7 +1180,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); @@ -1019,71 +1194,62 @@ mon_break_armor(struct monst *mon, boolean polyspot) Soundeffect(se_thud, 50); if (vis) { if (slithy(mon->data)) - pline("%s slithers out of %s armor!", Monnam(mon), - ppronoun); + pline_mon(mon, "%s slithers out of %s armor!", + Monnam(mon), ppronoun); else - 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."); - if (polyspot) - bypass_obj(otmp); - m_lose_armor(mon, otmp); + m_lose_armor(mon, otmp, polyspot); } if ((otmp = which_armor(mon, W_ARMC)) != 0 /* mummy wrapping adapts to small and very big sizes */ && (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 %ss out of %s %s!", Monnam(mon), - slithy(mon->data) ? "slither" : "shrink", ppronoun, - cloak_simple_name(otmp)); + pline_mon(mon, "%s %ss out of %s %s!", Monnam(mon), + slithy(mon->data) ? "slither" : "shrink", + ppronoun, cloak_simple_name(otmp)); } - if (polyspot) - bypass_obj(otmp); - m_lose_armor(mon, 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 if (slithy(mon->data)) - pline("%s slithers out of %s shirt!", Monnam(mon), - ppronoun); + pline_mon(mon, "%s slithers out of %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); } - if (polyspot) - bypass_obj(otmp); - m_lose_armor(mon, otmp); + m_lose_armor(mon, otmp, polyspot); } } if (handless_or_tiny) { /* [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" : ""); - if (polyspot) - bypass_obj(otmp); - m_lose_armor(mon, otmp); + 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."); - if (polyspot) - bypass_obj(otmp); - m_lose_armor(mon, otmp); + m_lose_armor(mon, otmp, polyspot); } } if (handless_or_tiny || has_horns(mdat)) { @@ -1091,41 +1257,37 @@ 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."); - if (polyspot) - bypass_obj(otmp); - m_lose_armor(mon, otmp); + m_lose_armor(mon, otmp, polyspot); } } if (handless_or_tiny || slithy(mdat) || mdat->mlet == S_CENTAUR) { 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); } - if (polyspot) - bypass_obj(otmp); - m_lose_armor(mon, otmp); + m_lose_armor(mon, otmp, polyspot); } } if (!can_saddle(mon)) { if ((otmp = which_armor(mon, W_SADDLE)) != 0) { - if (polyspot) - bypass_obj(otmp); - m_lose_armor(mon, otmp); + 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) - goto noride; - } else if (mon == u.usteed && !can_ride(mon)) { - noride: + noride = TRUE; + } + if (noride || (mon == u.usteed && !can_ride(mon))) { You("can no longer ride %s.", mon_nam(mon)); if (touch_petrifies(u.usteed->data) && !Stone_resistance && rnl(3)) { char buf[BUFSZ]; @@ -1141,7 +1303,7 @@ mon_break_armor(struct monst *mon, boolean polyspot) } /* bias a monster's preferences towards armor that has special benefits. */ -static int +staticfn int extra_pref(struct monst *mon, struct obj *obj) { /* currently only does speed boots, but might be expanded if monsters diff --git a/src/write.c b/src/write.c index 9cc04b183d..d6cccfd865 100644 --- a/src/write.c +++ b/src/write.c @@ -1,11 +1,11 @@ -/* NetHack 3.7 write.c $NHDT-Date: 1596498232 2020/08/03 23:43:52 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.26 $ */ +/* NetHack 3.7 write.c $NHDT-Date: 1702023275 2023/12/08 08:14:35 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.41 $ */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" -static boolean label_known(int, struct obj *); -static int write_ok(struct obj *); -static char *new_book_description(int, char *); +staticfn boolean label_known(int, struct obj *) NO_NNARGS; +staticfn int write_ok(struct obj *) NO_NNARGS; +staticfn char *new_book_description(int, char *) NONNULL NONNULLPTRS; /* * returns basecost of a scroll or a spellbook @@ -61,7 +61,7 @@ ink_cost(short otyp) unfortunately, we can't track things that haven't been added to the discoveries list and aren't present in current inventory, so some scrolls with ought to yield True will end up False */ -static boolean +staticfn boolean label_known(int scrolltype, struct obj *objlist) { struct obj *otmp; @@ -86,7 +86,7 @@ label_known(int scrolltype, struct obj *objlist) } /* getobj callback for object to write on */ -static int +staticfn int write_ok(struct obj *obj) { if (!obj || (obj->oclass != SCROLL_CLASS && obj->oclass != SPBOOK_CLASS)) @@ -102,15 +102,16 @@ write_ok(struct obj *obj) int dowrite(struct obj *pen) { - register struct obj *paper; + struct obj *paper; char namebuf[BUFSZ] = DUMMY, *nm, *bp; - register struct obj *new_obj; + struct obj *new_obj; int basecost, actualcost; int curseval; char qbuf[QBUFSZ]; int first, last, i, deferred, deferralchance, real; boolean by_descr = FALSE; const char *typeword; + int spell_knowledge; /* Orcus interferes with writing if you've got the Amulet */ boolean interference = fiend_adversity(PM_ORCUS); @@ -172,8 +173,8 @@ dowrite(struct obj *pen) deferred = real = 0; /* not any scroll or book */ deferralchance = 0; /* incremented for each oc_uname match */ - first = gb.bases[(int) paper->oclass]; - last = gb.bases[(int) paper->oclass + 1] - 1; + first = svb.bases[(int) paper->oclass]; + last = svb.bases[(int) paper->oclass + 1] - 1; /* first loop: look for match with name/description */ for (i = first; i <= last; i++) { /* extra shufflable descr not representing a real object */ @@ -314,8 +315,12 @@ dowrite(struct obj *pen) /* * Writing by name requires that the hero knows the scroll or * book type. One has previously been read (and its effect - * was evident) or been ID'd via scroll/spell/throne and it - * will be on the discoveries list. + * was evident) or been ID'd via scroll/spell/throne (or skill + * for Wizards) and it will be on the discoveries list. + * Unknown spellbooks can also be written by name if the hero + * has fresh knowledge of the spell, or if the spell is almost + * forgotten and the hero is Lucky (with a greater chance than + * if the spell is unknown or forgotten). * (Previous versions allowed scrolls and books to be written * by type name if they were on the discoveries list via being * given a user-assigned name, even though doing the latter @@ -334,13 +339,22 @@ dowrite(struct obj *pen) * Normal requirements can be overridden if hero is Lucky. */ + if (paper->oclass == SPBOOK_CLASS) { + spell_knowledge = known_spell(new_obj->otyp); + } else { + spell_knowledge = spe_Unknown; + } /* if known, then either by-name or by-descr works */ if ((interference && percent(35)) || (!objects[new_obj->otyp].oc_name_known /* else if named, then only by-descr works */ && !(by_descr && label_known(new_obj->otyp, gi.invent)) - /* and Luck might override after both checks have failed */ - && (rnl(Role_if(PM_WIZARD) ? 5 : 15)))) { + /* else fresh knowledge of the spell works */ + && spell_knowledge != spe_Fresh + /* and Luck might override after previous checks have failed */ + && rnl(((Role_if(PM_WIZARD) && paper->oclass != SPBOOK_CLASS) + || spell_knowledge == spe_GoingStale) + ? 5 : 15))) { You("%s to write that.", interference && objects[new_obj->otyp].oc_name_known ? "temporarily forget how" @@ -355,7 +369,7 @@ dowrite(struct obj *pen) Strcpy(namebuf, OBJ_DESCR(objects[new_obj->otyp])); wipeout_text(namebuf, (6 + MAXULEV - u.ulevel) / 6, 0); } else - Sprintf(namebuf, "%s was here!", gp.plname); + Sprintf(namebuf, "%s was here!", svp.plname); You("write \"%s\" and the scroll disappears.", namebuf); useup(paper); } @@ -413,7 +427,7 @@ dowrite(struct obj *pen) looks funny, so we want to insert "into " prior to such descriptions; even that's rather iffy, indicating that such descriptions probably ought to be eliminated (especially "cloth"!) */ -static char * +staticfn char * new_book_description(int booktype, char *outbuf) { /* subset of description strings from objects.c; if it grows diff --git a/src/zap.c b/src/zap.c index 98d7bee0ce..82891a5d6e 100644 --- a/src/zap.c +++ b/src/zap.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 zap.c $NHDT-Date: 1664739715 2022/10/02 19:41:55 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.440 $ */ +/* NetHack 3.7 zap.c $NHDT-Date: 1737344505 2025/01/19 19:41:45 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.562 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -12,27 +12,29 @@ */ #define MAGIC_COOKIE 1000 -static int zaptype(int); -static void probe_objchain(struct obj *); -static boolean zombie_can_dig(coordxy x, coordxy y); -static void polyuse(struct obj *, int, int); -static void create_polymon(struct obj *, int); -static int stone_to_flesh_obj(struct obj *); -static boolean zap_updown(struct obj *); -static void zhitu(int, int, const char *, coordxy, coordxy); -static boolean zap_steed(struct obj *); -static void skiprange(int, int *, int *); -static void maybe_explode_trap(struct trap *, struct obj *); -static int zap_hit(int, int); -static void disintegrate_mon(struct monst *, int, const char *); -static int adtyp_to_prop(int); -static void backfire(struct obj *); -static int zap_ok(struct obj *); -static void boxlock_invent(struct obj *); -static int spell_hit_bonus(int); -static boolean destroyable(struct obj *, int); -static int maybe_destroy_item(struct monst *, struct obj *, int); -static void wishcmdassist(void); +staticfn int zaptype(int); +staticfn void probe_objchain(struct obj *); +staticfn boolean zombie_can_dig(coordxy x, coordxy y); +staticfn void polyuse(struct obj *, int, int); +staticfn void create_polymon(struct obj *, int); +staticfn int stone_to_flesh_obj(struct obj *); +staticfn boolean zap_updown(struct obj *); +staticfn void zhitu(int, int, const char *, coordxy, coordxy); +staticfn boolean zap_steed(struct obj *); +staticfn void skiprange(int, int *, int *); +staticfn void maybe_explode_trap(struct trap *, struct obj *, + boolean *) NONNULLARG3; +staticfn void zap_map(coordxy, coordxy, struct obj *) NONNULLARG3; +staticfn int zap_hit(int, int); +staticfn void disintegrate_mon(struct monst *, int, const char *); +staticfn int adtyp_to_prop(int); +staticfn void backfire(struct obj *); +staticfn int zap_ok(struct obj *); +staticfn void boxlock_invent(struct obj *); +staticfn int spell_hit_bonus(int); +staticfn boolean destroyable(struct obj *, int); +staticfn int maybe_destroy_item(struct monst *, struct obj *, int); +staticfn void wishcmdassist(void); #define ZT_MAGIC_MISSILE (AD_MAGM - 1) #define ZT_FIRE (AD_FIRE - 1) @@ -77,7 +79,7 @@ static const char *const flash_types[] = { }; /* convert monster zap/spell/breath value to hero zap/spell/breath value */ -static int +staticfn int zaptype(int type) { if (type <= -30 && -39 <= type) /* monster wand zap */ @@ -142,7 +144,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) @@ -152,30 +158,41 @@ bhitm(struct monst *mtmp, struct obj *otmp) boolean reveal_invis = FALSE, learn_it = FALSE; boolean dbldam = Role_if(PM_KNIGHT) && u.uhave.questart; boolean skilled_spell, helpful_gesture = FALSE; - int dmg, otyp = otmp->otyp; + int dmg, otyp = otmp->otyp; /* otmp is not NULL */ const char *zap_type_text = "spell"; 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; gn.notonhead = (mtmp->mx != gb.bhitpos.x || mtmp->my != gb.bhitpos.y); - skilled_spell = (otmp && otmp->oclass == SPBOOK_CLASS && otmp->blessed); + skilled_spell = (otmp->oclass == SPBOOK_CLASS && otmp->blessed); switch (otyp) { case WAN_STRIKING: zap_type_text = "wand"; + FALLTHROUGH; /*FALLTHRU*/ case SPE_FORCE_BOLT: reveal_invis = TRUE; if (disguised_mimic) seemimic(mtmp); + learn_it = cansee(gb.bhitpos.x, gb.bhitpos.y); if (resists_magm(mtmp)) { /* match effect on player */ shieldeff(mtmp->mx, mtmp->my); pline("Boing!"); - break; /* skip makeknown */ + /* 3.7: used to 'break' to avoid setting learn_it here */ } else if (u.uswallow || rnd(20) < 10 + find_mac(mtmp)) { dmg = d(2, 12); if (dbldam) @@ -184,9 +201,10 @@ bhitm(struct monst *mtmp, struct obj *otmp) dmg = spell_damage_bonus(dmg); hit(zap_type_text, mtmp, exclam(dmg)); (void) resist(mtmp, otmp->oclass, dmg, TELL); - } else + } else { miss(zap_type_text, mtmp); - learn_it = TRUE; + learn_it = FALSE; + } break; case WAN_SLOW_MONSTER: case SPE_SLOW_MONSTER: @@ -210,8 +228,8 @@ bhitm(struct monst *mtmp, struct obj *otmp) mon_adjust_speed(mtmp, 1, otmp); check_gear_next_turn(mtmp); /* might want speed boots */ } - if (mtmp->mtame) - helpful_gesture = TRUE; + /* wake but don't anger a peaceful target */ + helpful_gesture = TRUE; break; case WAN_UNDEAD_TURNING: case SPE_TURN_UNDEAD: @@ -226,7 +244,7 @@ bhitm(struct monst *mtmp, struct obj *otmp) dmg *= 2; if (otyp == SPE_TURN_UNDEAD) dmg = spell_damage_bonus(dmg); - gc.context.bypasses = TRUE; /* for make_corpse() */ + svc.context.bypasses = TRUE; /* for make_corpse() */ if (!resist(mtmp, otmp->oclass, dmg, NOTELL)) { if (!DEADMONSTER(mtmp)) monflee(mtmp, 0, FALSE, TRUE); @@ -243,7 +261,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 @@ -265,7 +283,7 @@ bhitm(struct monst *mtmp, struct obj *otmp) pline("%s shudders!", Monnam(mtmp)); learn_it = TRUE; } - /* gc.context.bypasses = TRUE; ## for make_corpse() */ + /* svc.context.bypasses = TRUE; ## for make_corpse() */ /* no corpse after system shock */ xkilled(mtmp, XKILL_GIVEMSG | XKILL_NOCORPSE); } else { @@ -279,7 +297,7 @@ bhitm(struct monst *mtmp, struct obj *otmp) /* if shapechange failed because there aren't enough eligible candidates (most likely for vampshifter), try reverting to original form */ - || (mtmp->cham >= LOW_PM + || (ismnum(mtmp->cham) && newcham(mtmp, &mons[mtmp->cham], ncflags) != 0)) { if (give_msg && (canspotmon(mtmp) @@ -301,7 +319,7 @@ bhitm(struct monst *mtmp, struct obj *otmp) /* flag to indicate that cleanup is needed; object bypass cleanup also clears mon->mextra->mcorpsenm for all long worms on the level */ - gc.context.bypasses = TRUE; + svc.context.bypasses = TRUE; } } break; @@ -332,17 +350,18 @@ bhitm(struct monst *mtmp, struct obj *otmp) pline("%s turns transparent!", nambuf); reveal_invis = TRUE; learn_it = TRUE; - } - else if (couldsee && !canseemon(mtmp)) { + } else if (couldsee && !canseemon(mtmp)) { /* keep the immediate effects of make invisible and teleportation - * ambiguous by using the same message that's used if we teleported - * mtmp (and it ended up somewhere you can't see) */ + ambiguous by using the same message that's used if we + teleported mtmp (and it ended up somewhere you can't see) */ pline("%s vanishes!", nambuf); } break; } case WAN_LOCKING: case SPE_WIZARD_LOCK: + if (disguised_mimic && box_or_door(mtmp)) + that_is_a_mimic(mtmp, TRUE); /*seemimic()*/ wake = closeholdingtrap(mtmp, &learn_it); break; case WAN_PROBING: @@ -353,6 +372,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, TRUE); /*seemimic()*/ wake = FALSE; /* don't want immediate counterattack */ if (mtmp == u.ustuck) { /* zapping either holder/holdee or self [zapyourself()] will @@ -401,14 +422,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]) { - boolean already_max = (mtmp->mhp == mtmp->mhpmax); + 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; @@ -417,7 +439,7 @@ bhitm(struct monst *mtmp, struct obj *otmp) mcureblindness(mtmp, canseemon(mtmp)); if (canseemon(mtmp)) { if (disguised_mimic) { - if (is_obj_mappear(mtmp,STRANGE_OBJECT)) { + if (is_obj_mappear(mtmp, STRANGE_OBJECT)) { /* it can do better now */ set_mimic_sym(mtmp); newsym(mtmp->mx, mtmp->my); @@ -427,7 +449,11 @@ bhitm(struct monst *mtmp, struct obj *otmp) pline("%s looks%s better.", Monnam(mtmp), otyp == SPE_EXTRA_HEALING ? " much" : ""); } - if ((mtmp->mtame || mtmp->mpeaceful) && !already_max) { + if (mtmp->mtame && Role_if(PM_HEALER) && (delta > 0)) { + more_experienced(min(delta, healamt), 0); + newexplevel(); + } + if ((mtmp->mtame || mtmp->mpeaceful) && (delta > 0)) { if (Role_if(PM_HEALER)) { adjalign(1); } @@ -438,11 +464,11 @@ bhitm(struct monst *mtmp, struct obj *otmp) } } } 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; @@ -451,7 +477,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); @@ -459,6 +486,8 @@ bhitm(struct monst *mtmp, struct obj *otmp) learn_it = TRUE; break; case SPE_STONE_TO_FLESH: + /* FIXME: mimics disguished as stone furniture or stone object + should be taken out of concealment. */ if (monsndx(mtmp->data) == PM_STONE_GOLEM) { char *name = Monnam(mtmp); @@ -482,7 +511,7 @@ bhitm(struct monst *mtmp, struct obj *otmp) if (otyp == SPE_DRAIN_LIFE) dmg = spell_damage_bonus(dmg); if (resists_drli(mtmp) || item_catches_drain(mtmp)) { - shieldeff(mtmp->mx, mtmp->my); + shieldeff_mon(mtmp); } else if (!resist(mtmp, otmp->oclass, dmg, NOTELL) && !DEADMONSTER(mtmp)) { mtmp->mhp -= dmg; @@ -511,7 +540,7 @@ bhitm(struct monst *mtmp, struct obj *otmp) if (mtmp->isshk && !*u.ushops) hot_pursuit(mtmp); } else if (M_AP_TYPE(mtmp)) - seemimic(mtmp); /* might unblock if mimicing a boulder/door */ + seemimic(mtmp); /* might unblock if mimicking a boulder/door */ } /* 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 @@ -527,6 +556,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 @@ -565,7 +595,7 @@ release_hold(void) } } -static void +staticfn void probe_objchain(struct obj *otmp) { for (; otmp; otmp = otmp->nobj) { @@ -574,7 +604,8 @@ probe_objchain(struct obj *otmp) otmp->lknown = 1; if (!SchroedingersBox(otmp)) otmp->cknown = 1; - } + } else if (otmp->otyp == TIN) + otmp->known = 1; } } @@ -724,13 +755,13 @@ montraits( if (mtmp->m_id) { mtmp2->m_id = mtmp->m_id; /* might be bringing quest leader/nemesis back to life */ - if (gq.quest_status.killed_leader + if (svq.quest_status.killed_leader /* killed_leader implies leader_m_id is valid */ - && mtmp2->m_id == gq.quest_status.leader_m_id) - gq.quest_status.killed_leader = FALSE; - if (gq.quest_status.killed_nemesis - && mtmp2->m_id == gq.quest_status.nemesis_m_id) - gq.quest_status.killed_nemesis = FALSE; + && mtmp2->m_id == svq.quest_status.leader_m_id) + svq.quest_status.killed_leader = FALSE; + if (svq.quest_status.killed_nemesis + && mtmp2->m_id == svq.quest_status.nemesis_m_id) + svq.quest_status.killed_nemesis = FALSE; } mtmp2->mx = mtmp->mx; mtmp2->my = mtmp->my; @@ -820,7 +851,7 @@ get_container_location( } /* can zombie dig the location at x,y */ -static boolean +staticfn boolean zombie_can_dig(coordxy x, coordxy y) { if (isok(x, y)) { @@ -982,7 +1013,7 @@ revive(struct obj *corpse, boolean by_hero) /* if the revived is a zombie, it might be specified as tame */ if (corpse->tamed_zombie) { - tamedog(mtmp, (struct obj *) 0, FALSE); + tamedog(mtmp, (struct obj *) 0, FALSE, FALSE); } one_of = (corpse->quan > 1L); @@ -1052,7 +1083,7 @@ revive(struct obj *corpse, boolean by_hero) } /* tame the revived monster if its ghost was tame */ if (ghost->mtame && !mtmp->mtame) { - if (tamedog(mtmp, (struct obj *) 0, FALSE)) { + if (tamedog(mtmp, (struct obj *) 0, FALSE, FALSE)) { /* ghost's edog data is ignored */ mtmp->mtame = ghost->mtame; } @@ -1088,6 +1119,7 @@ revive(struct obj *corpse, boolean by_hero) m_useup(corpse->ocarry, corpse); break; case OBJ_CONTAINED: + /* obj_extract_self() will update corpse->ocontainer->owt */ obj_extract_self(corpse); obfree(corpse, (struct obj *) 0); break; @@ -1097,6 +1129,7 @@ revive(struct obj *corpse, boolean by_hero) obfree(corpse, (struct obj *) 0); break; } + FALLTHROUGH; /*FALLTHRU*/ case OBJ_FREE: case OBJ_MIGRATING: @@ -1110,7 +1143,7 @@ revive(struct obj *corpse, boolean by_hero) } void -revive_egg(struct obj *obj) +revive_egg(struct obj *obj) /* nonnull */ { /* * Note: generic eggs with corpsenm set to NON_PM will never hatch. @@ -1159,7 +1192,7 @@ unturn_dead(struct monst *mon) save_norevive = otmp->norevive; otmp->norevive = 0; - if ((mtmp2 = revive(otmp, !gc.context.mon_moving)) != 0) { + if ((mtmp2 = revive(otmp, !svc.context.mon_moving)) != 0) { ++res; /* might get revived as a zombie rather than corpse's monster */ different_type = (mtmp2->data != &mons[corpsenm]); @@ -1216,19 +1249,19 @@ cancel_item(struct obj *obj) case RIN_GAIN_STRENGTH: if ((obj->owornmask & W_RING) != 0L) { ABON(A_STR) -= obj->spe; - gc.context.botl = TRUE; + disp.botl = TRUE; } break; case RIN_GAIN_CONSTITUTION: if ((obj->owornmask & W_RING) != 0L) { ABON(A_CON) -= obj->spe; - gc.context.botl = TRUE; + disp.botl = TRUE; } break; case RIN_ADORNMENT: if ((obj->owornmask & W_RING) != 0L) { ABON(A_CHA) -= obj->spe; - gc.context.botl = TRUE; + disp.botl = TRUE; } break; case RIN_INCREASE_ACCURACY: @@ -1241,24 +1274,24 @@ cancel_item(struct obj *obj) break; case RIN_PROTECTION: if ((obj->owornmask & W_RING) != 0L) - gc.context.botl = TRUE; + disp.botl = TRUE; break; case GAUNTLETS_OF_DEXTERITY: if ((obj->owornmask & W_ARMG) != 0L) { ABON(A_DEX) -= obj->spe; - gc.context.botl = TRUE; + disp.botl = TRUE; } break; case HELM_OF_BRILLIANCE: if ((obj->owornmask & W_ARMH) != 0L) { ABON(A_INT) -= obj->spe; ABON(A_WIS) -= obj->spe; - gc.context.botl = TRUE; + disp.botl = TRUE; } break; default: if ((obj->owornmask & W_ARMOR) != 0L) /* AC */ - gc.context.botl = TRUE; + disp.botl = TRUE; break; } } @@ -1269,9 +1302,11 @@ cancel_item(struct obj *obj) || obj->oclass == WEAPON_CLASS || is_weptool(obj))) || otyp == POT_ACID || otyp == POT_SICKNESS - || (otyp == POT_WATER && (obj->blessed || obj->cursed))) { + || (otyp == POT_WATER && (obj->blessed || obj->cursed)) + /* not magic; cancels to blank spellbook */ + || otyp == SPE_NOVEL) { int cancelled_spe = (obj->oclass == WAND_CLASS - || obj->otyp == CRYSTAL_BALL) ? -1 : 0; + || otyp == CRYSTAL_BALL) ? -1 : 0; if (obj->spe != cancelled_spe && otyp != WAN_CANCELLATION /* can't cancel cancellation */ @@ -1287,10 +1322,12 @@ cancel_item(struct obj *obj) obj->spe = 0; break; case SPBOOK_CLASS: - if (otyp != SPE_CANCELLATION && otyp != SPE_NOVEL - && otyp != SPE_BOOK_OF_THE_DEAD) { + if (otyp != SPE_CANCELLATION && otyp != SPE_BOOK_OF_THE_DEAD) { costly_alteration(obj, COST_CANCEL); obj->otyp = SPE_BLANK_PAPER; + /* cancelling a novel is more involved than a spellbook */ + if (otyp == SPE_NOVEL) /* old type */ + blank_novel(obj); } break; case POTION_CLASS: @@ -1331,6 +1368,20 @@ cancel_item(struct obj *obj) return; } +/* soaking or cancelling a novel converts it into a blank spellbook but + needs more than just changing its otyp (caller is responsible for that) */ +void +blank_novel(struct obj *obj) +{ + assert(obj->otyp == SPE_BLANK_PAPER); + /* novelidx overloads corpsenm, not used for spellbooks */ + obj->novelidx = 0; + free_oname(obj); /* get rid of [former] novel's title */ + /* a blank spellbook weighs more than a novel; update obj's weight and + recursively the weight of any container holding it */ + container_weight(obj); +} + /* Remove a positive enchantment or charge from obj, * possibly carried by you or a monster */ @@ -1360,19 +1411,19 @@ drain_item(struct obj *obj, boolean by_you) case RIN_GAIN_STRENGTH: if ((obj->owornmask & W_RING) && u_ring) { ABON(A_STR)--; - gc.context.botl = 1; + disp.botl = TRUE; } break; case RIN_GAIN_CONSTITUTION: if ((obj->owornmask & W_RING) && u_ring) { ABON(A_CON)--; - gc.context.botl = 1; + disp.botl = TRUE; } break; case RIN_ADORNMENT: if ((obj->owornmask & W_RING) && u_ring) { ABON(A_CHA)--; - gc.context.botl = 1; + disp.botl = TRUE; } break; case RIN_INCREASE_ACCURACY: @@ -1385,25 +1436,25 @@ drain_item(struct obj *obj, boolean by_you) break; case RIN_PROTECTION: if (u_ring) - gc.context.botl = 1; /* bot() will recalc u.uac */ + disp.botl = TRUE; /* bot() will recalc u.uac */ break; case HELM_OF_BRILLIANCE: if ((obj->owornmask & W_ARMH) && (obj == uarmh)) { ABON(A_INT)--; ABON(A_WIS)--; - gc.context.botl = 1; + disp.botl = TRUE; } break; case GAUNTLETS_OF_DEXTERITY: if ((obj->owornmask & W_ARMG) && (obj == uarmg)) { ABON(A_DEX)--; - gc.context.botl = 1; + disp.botl = TRUE; } break; default: break; } - if (gc.context.botl) + if (disp.botl) bot(); if (carried(obj)) update_inventory(); @@ -1433,7 +1484,7 @@ obj_shudders(struct obj *obj) { int zap_odds; - if (gc.context.bypasses && obj->bypass) + if (svc.context.bypasses && obj->bypass) return FALSE; if (obj->oclass == WAND_CLASS) @@ -1457,14 +1508,14 @@ obj_shudders(struct obj *obj) * there's a random factor here to keep from always using the stuff * at the top of the pile. */ -static void +staticfn void polyuse(struct obj *objhdr, int mat, int minwt) { - register struct obj *otmp, *otmp2; + struct obj *otmp, *otmp2; for (otmp = objhdr; minwt > 0 && otmp; otmp = otmp2) { otmp2 = otmp->nexthere; - if (gc.context.bypasses && otmp->bypass) + if (svc.context.bypasses && otmp->bypass) continue; if (otmp == uball || otmp == uchain) continue; @@ -1498,7 +1549,7 @@ polyuse(struct obj *objhdr, int mat, int minwt) * Polymorph some of the stuff in this pile into a monster, preferably * a golem of the kind okind. */ -static void +staticfn void create_polymon(struct obj *obj, int okind) { struct permonst *mdat = (struct permonst *) 0; @@ -1506,7 +1557,7 @@ create_polymon(struct obj *obj, int okind) const char *material; int pm_index; - if (gc.context.bypasses) { + if (svc.context.bypasses) { /* this is approximate because the "no golems" !obj->nexthere check below doesn't understand bypassed objects; but it should suffice since bypassed objects always end up as a @@ -1576,7 +1627,7 @@ create_polymon(struct obj *obj, int okind) break; } - if (!(gm.mvitals[pm_index].mvflags & G_GENOD)) + if (!(svm.mvitals[pm_index].mvflags & G_GENOD)) mdat = &mons[pm_index]; mtmp = makemon(mdat, obj->ox, obj->oy, MM_NOMSG); @@ -1646,7 +1697,7 @@ obj_unpolyable(struct obj *obj) /* classes of items whose current charge count carries over across polymorph */ -static const char charged_objs[] = { WAND_CLASS, WEAPON_CLASS, ARMOR_CLASS, +staticfn const char charged_objs[] = { WAND_CLASS, WEAPON_CLASS, ARMOR_CLASS, '\0' }; /* @@ -1764,7 +1815,7 @@ poly_obj(struct obj *obj, int id) otmp->blessed = obj->blessed; if (erosion_matters(otmp) || destroyable_oclass(otmp->oclass)) { - if (is_flammable(otmp) || is_rustprone(otmp)) + if (is_flammable(otmp) || is_rustprone(otmp) || is_crackable(otmp)) otmp->oeroded = obj->oeroded; if (is_corrodeable(otmp) || is_rottable(otmp)) otmp->oeroded2 = obj->oeroded2; @@ -1775,10 +1826,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 */ @@ -1793,6 +1843,16 @@ poly_obj(struct obj *obj, int id) set_material(otmp, LEATHER); } } + if (obj->otyp == LEASH && obj->leashmon != 0) { + if (otmp->otyp == LEASH) { + otmp->leashmon = obj->leashmon; + /* clear m_id before delobj(), to avoid o_unleash() by obfree() */ + obj->leashmon = 0; + } else { + /* obfree() would do this if we didn't do it here */ + o_unleash(obj); + } + } /* no box contents --KAA * Note: this doesn't delete the contents of the original container being @@ -1829,13 +1889,19 @@ poly_obj(struct obj *obj, int id) case POTION_CLASS: while (otmp->otyp == POT_POLYMORPH) otmp->otyp = rnd_class(POT_GAIN_ABILITY, POT_WATER); + /* potions of oil use obj->age field differently from other potions */ + if (otmp->otyp == POT_OIL || obj->otyp == POT_OIL) + fixup_oil(otmp, obj); break; case SPBOOK_CLASS: while (otmp->otyp == SPE_POLYMORPH) - otmp->otyp = rnd_class(SPE_DIG, SPE_BLANK_PAPER); - /* reduce spellbook abuse; non-blank books degrade */ - if (otmp->otyp != SPE_BLANK_PAPER) { + otmp->otyp = rnd_class(svb.bases[SPBOOK_CLASS], SPE_BLANK_PAPER); + /* reduce spellbook abuse; non-blank books degrade; + 3.7: novels don't use spestudied so shouldn't degrade to blank + (but don't force spestudied to zero for them since a non-zero + value could get passed along to a future polymorph) */ + if (otmp->otyp != SPE_BLANK_PAPER && otmp->otyp != SPE_NOVEL) { otmp->spestudied = obj->spestudied + 1; if (otmp->spestudied > MAX_SPELL_STUDY) { otmp->otyp = SPE_BLANK_PAPER; @@ -1872,8 +1938,8 @@ poly_obj(struct obj *obj, int id) /* * We may need to do extra adjustments for the hero if we're * messing with the hero's inventory. The following calls are - * equivalent to calling freeinv on obj and addinv on otmp, - * while doing an in-place swap of the actual objects. + * equivalent to calling freeinv() on obj and addinv_nomerge() + * on otmp, while doing an in-place swap of the actual objects. */ freeinv_core(obj); addinv_core1(otmp); @@ -1888,9 +1954,12 @@ poly_obj(struct obj *obj, int id) if (old_wornmask) { boolean was_twohanded = bimanual(obj), was_twoweap = u.twoweap; - /* wearslot() returns a mask which might have multiple bits set; - narrow that down to the bit(s) currently in use */ - new_wornmask = wearslot(otmp) & old_wornmask; + /* wearslot() expects us to deal with wielded/alt-wep/quivered + items in case they're not weapons; for other slots it might + return multiple bits (ring left|right); narrow that down to + the bit(s) currently in use */ + new_wornmask = ((old_wornmask & W_WEAPONS) != 0L) ? old_wornmask + : (wearslot(otmp) & old_wornmask); remove_worn_item(obj, TRUE); /* if the new form can be worn in the same slot, make it so */ if ((new_wornmask & W_WEP) != 0L) { @@ -1954,8 +2023,8 @@ poly_obj(struct obj *obj, int id) /* stone-to-flesh spell hits and maybe transforms or animates obj; * return 1 if it did affect the object, otherwise 0 */ -static int -stone_to_flesh_obj(struct obj *obj) +staticfn int +stone_to_flesh_obj(struct obj *obj) /* nonnull */ { struct permonst *ptr; struct monst *mon = NULL, *shkp; @@ -2036,7 +2105,11 @@ stone_to_flesh_obj(struct obj *obj) } /* Any other tools not covered here, statues and figurines that didn't * animate because they were vegetarian or for any other reason: */ + FALLTHROUGH; /* FALLTHRU */ + case WEAPON_CLASS: /* crysknife */ + FALLTHROUGH; + /*FALLTHRU*/ default: if (valid_obj_material(obj, FLESH)) { /* Currently no objects are valid as both stone and flesh. */ @@ -2062,6 +2135,7 @@ stone_to_flesh_obj(struct obj *obj) smell = TRUE; break; } + nhUse(obj); /* avoid 'assigned value not used' for poly_obj() calls */ if (smell) { /* non-meat eaters smell meat, meat eaters smell its flavor; @@ -2125,13 +2199,13 @@ bhito(struct obj *obj, struct obj *otmp) * the invent or mon->minvent chain, possibly recursively. * * The bypass bit on all objects is reset each turn, whenever - * gc.context.bypasses is set. + * svc.context.bypasses is set. * - * We check the obj->bypass bit above AND gc.context.bypasses + * We check the obj->bypass bit above AND svc.context.bypasses * as a safeguard against any stray occurrence left in an obj * struct someplace, although that should never happen. */ - if (gc.context.bypasses) { + if (svc.context.bypasses) { return 0; } else { debugpline1("%s for a moment.", Tobjnam(obj, "pulsate")); @@ -2177,7 +2251,7 @@ bhito(struct obj *obj, struct obj *otmp) * If we are currently hiding and the shuddering object might * have been the only thing on our square, call hideunder * again. */ - boolean cover = ((obj == gl.level.objects[u.ux][u.uy]) + boolean cover = ((obj == svl.level.objects[u.ux][u.uy]) && u.uundetected && hides_under(gy.youmonst.data)); @@ -2224,6 +2298,19 @@ bhito(struct obj *obj, struct obj *otmp) (void) display_cinventory(obj); } res = 1; + } else if (obj->otyp == TIN) { + /* don't learn wand if tin is already known */ + if (!obj->known || !obj->cknown) + res = 1; + obj->known = 1; + set_cknown_lknown(obj); /* if TIN obj->cknown = 1 */ + } else if (obj->otyp == EGG) { + /* if egg is unhatchable, probing it won't learn wand + because even when flagged as known, it's just "an egg" */ + if (!obj->known && obj->corpsenm != NON_PM) + res = 1; + obj->known = 1; + /* [should this call learn_egg_type()?] */ } if (res) learn_it = TRUE; @@ -2253,7 +2340,7 @@ bhito(struct obj *obj, struct obj *otmp) } else { int oox = obj->ox, ooy = obj->oy; - if (gc.context.mon_moving ? !breaks(obj, oox, ooy) + if (svc.context.mon_moving ? !breaks(obj, oox, ooy) : !hero_breaks(obj, oox, ooy, 0)) maybelearnit = FALSE; /* nothing broke */ else @@ -2269,9 +2356,7 @@ bhito(struct obj *obj, struct obj *otmp) case WAN_CANCELLATION: case SPE_CANCELLATION: cancel_item(obj); -#ifdef TEXTCOLOR newsym(obj->ox, obj->oy); /* might change color */ -#endif break; case SPE_DRAIN_LIFE: (void) drain_item(obj, TRUE); @@ -2295,7 +2380,7 @@ bhito(struct obj *obj, struct obj *otmp) struct monst *mtmp; coordxy ox, oy; unsigned save_norevive; - boolean by_u = !gc.context.mon_moving; + boolean by_u = !svc.context.mon_moving; int corpsenm = corpse_revive_type(obj); char *corpsname = cxname_singular(obj); @@ -2395,11 +2480,11 @@ bhitpile( coordxy tx, coordxy ty, /* target location */ schar zz) /* direction for up/down zaps */ { - register struct obj *otmp, *next_obj; + struct obj *otmp, *next_obj; boolean hidingunder, first; int prevotyp, hitanything = 0; - if (!gl.level.objects[tx][ty]) + if (!svl.level.objects[tx][ty]) return 0; /* if hiding underneath an object and zapping up or down, the top item @@ -2409,7 +2494,7 @@ bhitpile( if (obj->otyp == SPE_FORCE_BOLT || obj->otyp == WAN_STRIKING) { struct trap *t = t_at(tx, ty); - struct obj *topofpile = gl.level.objects[tx][ty]; + struct obj *topofpile = svl.level.objects[tx][ty]; /* We can't settle for the default calling sequence of bhito(otmp) -> break_statue(otmp) -> activate_statue_trap(ox,oy) @@ -2422,12 +2507,12 @@ bhitpile( /* assume zapping up or down while hiding under the top item can still activate the trap even if it's below (when zapping up) or above (when zapping down) */ - if (gl.level.objects[tx][ty] != topofpile) + if (svl.level.objects[tx][ty] != topofpile) first = FALSE; /* top item was statue which activated */ } gp.poly_zapped = -1; - for (otmp = gl.level.objects[tx][ty]; otmp; otmp = next_obj) { + for (otmp = svl.level.objects[tx][ty]; otmp; otmp = next_obj) { next_obj = otmp->nexthere; if (hidingunder) { if (first) { @@ -2440,18 +2525,20 @@ bhitpile( continue; } } + if (otmp->where != OBJ_FLOOR || otmp->ox != tx || otmp->oy != ty) + continue; hitanything += (*fhito)(otmp, obj); } if (gp.poly_zapped >= 0) - create_polymon(gl.level.objects[tx][ty], gp.poly_zapped); + create_polymon(svl.level.objects[tx][ty], gp.poly_zapped); /* when boulders are present they're expected to be on top; with multiple boulders it's possible for some to have been changed into non-boulders (polymorph, stone-to-flesh) while ones beneath resist, so re-stack pile if there are any non-boulders above boulders */ prevotyp = BOULDER; - for (otmp = gl.level.objects[tx][ty]; otmp; otmp = otmp->nexthere) { + for (otmp = svl.level.objects[tx][ty]; otmp; otmp = otmp->nexthere) { if (otmp->otyp == BOULDER && prevotyp != BOULDER) { recreate_pile_at(tx, ty); break; @@ -2462,6 +2549,8 @@ bhitpile( if (hidingunder) /* pile might have been destroyed or dispersed */ maybe_unhide_at(tx, ty); + fill_pit(tx, ty); + return hitanything; } @@ -2494,7 +2583,7 @@ do_enlightenment_effect(void) /* * zapnodir - zaps a NODIR wand/spell. - * added by GAN 11/03/86 + * Won't get here if wand has no charges (unless wresting 1 last charge). */ void zapnodir(struct obj *obj) @@ -2504,36 +2593,45 @@ zapnodir(struct obj *obj) switch (obj->otyp) { case WAN_LIGHT: case SPE_LIGHT: + /* FIXME? wand of light becoming discovered should be contingent upon + seeing at least one previously unlit spot become lit */ + known = (obj->dknown && !Blind); litroom(TRUE, obj); - if (!Blind) - known = TRUE; - if (lightdamage(obj, TRUE, 5)) - known = TRUE; + (void) lightdamage(obj, TRUE, 5); break; case WAN_SECRET_DOOR_DETECTION: case SPE_DETECT_UNSEEN: - if (!findit()) - return; - if (!Blind) - known = TRUE; + /* findit() gives sufficient feedback to discover the wand even when + blinded or when it fails to find anything */ + known = !!obj->dknown; + (void) findit(); break; case WAN_CREATE_MONSTER: - known = create_critters(rn2(23) ? 1 : rn1(7, 2), - (struct permonst *) 0, FALSE, &gy.youmonst); + /* create_critters() returns True iff hero sees a new monster appear */ + if (create_critters(rn2(23) ? 1 : rn1(7, 2), + (struct permonst *) 0, FALSE, &gy.youmonst)) + known = !!obj->dknown; break; case WAN_WISHING: - known = TRUE; if (Luck + rn2(5) < 0) { pline("Unfortunately, nothing happens."); - break; + known = FALSE; + } else { + known = !!obj->dknown; + /* wand of wishing asks player what to wish for so always becomes + discovered (unless it hasn't been seen) */ + makewish(); } - makewish(); break; case WAN_ENLIGHTENMENT: - known = TRUE; + known = !!obj->dknown; + /* do_enlightenmnt_effect() always describes enlightenment */ do_enlightenment_effect(); break; + default: + break; } + if (known) { if (!objects[obj->otyp].oc_name_known) more_experienced(0, 10); @@ -2543,7 +2641,7 @@ zapnodir(struct obj *obj) } } -static void +staticfn void backfire(struct obj *otmp) { int dmg; @@ -2556,7 +2654,7 @@ backfire(struct obj *otmp) } /* getobj callback for object to zap */ -static int +staticfn int zap_ok(struct obj *obj) { if (obj && obj->oclass == WAND_CLASS) @@ -2636,14 +2734,15 @@ dozap(void) } /* Lock or unlock all boxes in inventory */ -static 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; @@ -2678,6 +2777,7 @@ zapyourself(struct obj *obj, boolean ordinary) } else damage = d(1 + obj->spe, 6); exercise(A_STR, FALSE); + monstunseesu(M_SEEN_MAGR); } break; @@ -2688,6 +2788,7 @@ zapyourself(struct obj *obj, boolean ordinary) You("shock yourself!"); damage = orig_dmg; exercise(A_CON, FALSE); + monstunseesu(M_SEEN_ELEC); } else { shieldeff(u.ux, u.uy); You("zap yourself, but seem unharmed."); @@ -2695,7 +2796,7 @@ zapyourself(struct obj *obj, boolean ordinary) ugolemeffects(AD_ELEC, orig_dmg); } (void) destroy_items(&gy.youmonst, AD_ELEC, orig_dmg); - (void) flashburn((long) rnd(100)); + (void) flashburn((long) rnd(100), TRUE); break; case SPE_FIREBALL: @@ -2714,6 +2815,7 @@ zapyourself(struct obj *obj, boolean ordinary) } else { pline("You've set yourself afire!"); damage = orig_dmg; + monstunseesu(M_SEEN_FIRE); } burn_away_slime(); (void) burnarmor(&gy.youmonst); @@ -2734,6 +2836,7 @@ zapyourself(struct obj *obj, boolean ordinary) } else { You("imitate a popsicle!"); damage = orig_dmg; + monstunseesu(M_SEEN_COLD); } (void) destroy_items(&gy.youmonst, AD_COLD, orig_dmg); break; @@ -2748,7 +2851,9 @@ zapyourself(struct obj *obj, boolean ordinary) pline("Some of the missiles bounce off!"); damage = (damage + 1) / 2; monstseesu(M_SEEN_MAGR); - } + } else { + monstunseesu(M_SEEN_MAGR); + } if (Half_spell_damage) { /* stacks with Antimagic */ damage = (damage + 1) / 2; } @@ -2786,7 +2891,7 @@ zapyourself(struct obj *obj, boolean ordinary) You_feel("rather itchy under %s.", yname(uarmc)); break; } - incr_itimeout(&HInvis, ordinary ? rn1(50, 50) : d(obj->spe, 250)); + incr_itimeout(&HInvis, ordinary ? rn1(15, 31) : d(obj->spe, 250)); if (msg) { learn_it = TRUE; newsym(u.ux, u.uy); @@ -2812,7 +2917,8 @@ zapyourself(struct obj *obj, boolean ordinary) if (ordinary) pline_The("sleep ray hits you!"); else - You("fall alseep!"); + You("fall asleep!"); + monstunseesu(M_SEEN_SLEEP); fall_asleep(-rnd(50), TRUE); } break; @@ -2844,8 +2950,8 @@ zapyourself(struct obj *obj, boolean ordinary) break; } learn_it = TRUE; - Sprintf(gk.killer.name, "shot %sself with a death ray", uhim()); - gk.killer.format = NO_KILLER_PREFIX; + Sprintf(svk.killer.name, "shot %sself with a death ray", uhim()); + svk.killer.format = NO_KILLER_PREFIX; /* probably don't need these to be urgent; player just gave input without subsequent opportunity to dismiss --More-- with ESC */ urgent_pline("You irradiate yourself with pure energy!"); @@ -2868,13 +2974,14 @@ 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) damage = 5; damage = lightdamage(obj, ordinary, damage); damage += rnd(25); - if (flashburn((long) damage)) + if (flashburn((long) damage, FALSE)) learn_it = TRUE; damage = 0; /* reset */ break; @@ -2935,17 +3042,21 @@ zapyourself(struct obj *obj, boolean ordinary) } /* * It is possible that we can now merge some inventory. - * Do a highly paranoid merge. Restart from the beginning - * until no merges. + * Do a highly paranoid merge. Restart from the beginning until + * no merges. Don't merge worn items (in case of stone-to-flesh + * of rocks wielded in differing weapon/alt-wep/quiver slot). */ do { didmerge = FALSE; - for (otmp = gi.invent; !didmerge && otmp; otmp = otmp->nobj) + for (otmp = gi.invent; !didmerge && otmp; otmp = otmp->nobj) { + if (otmp->owornmask) + continue; for (onxt = otmp->nobj; onxt; onxt = onxt->nobj) if (merged(&otmp, &onxt)) { didmerge = TRUE; break; } + } } while (didmerge); break; } @@ -2994,9 +3105,9 @@ lightdamage( of death will always be "killed while stuck in creature form"] */ if (obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS) ordinary = FALSE; /* say blasted rather than zapped */ - how = (obj->oclass != SPBOOK_CLASS) - ? (const char *) ansimpleoname(obj) - : "spell of light"; + how = (obj->oclass == SPBOOK_CLASS) ? "spell of light" + : (!obj->oartifact) ? ansimpleoname(obj) + : bare_artifactname(obj); Sprintf(buf, "%s %sself with %s", ordinary ? "zapped" : "blasted", uhim(), how); /* might rehumanize(); could be fatal, but only for Unchanging */ @@ -3007,7 +3118,7 @@ lightdamage( /* light[ning] causes blindness */ boolean -flashburn(long duration) +flashburn(long duration, boolean via_lightning) { if (!resists_blnd(&gy.youmonst)) { You(are_blinded_by_the_flash); @@ -3016,6 +3127,16 @@ flashburn(long duration) Your1(vision_clears); return TRUE; } + /* if blinding is resisted due to magical equipment (Sunsword), give + a sparkle animation (even if also resisted due to being blind) + _unless_ this is lightning-induced; we don't want a double sparkle + if hero is both lightning resistant and blindness resistant, or + worse, have a single sparkle where the player confuses blindness + resistance for lightning resistance */ + if (!via_lightning && resists_blnd_by_arti(&gy.youmonst)) { + shieldeff(u.ux, u.uy); + return TRUE; + } return FALSE; } @@ -3023,7 +3144,7 @@ flashburn(long duration) * Return TRUE if the steed was hit by the wand. * Return FALSE if the steed was not hit by the wand. */ -static boolean +staticfn boolean zap_steed(struct obj *obj) /* wand or spell */ { int steedhit = FALSE; @@ -3107,7 +3228,7 @@ cancel_monst(struct monst *mdef, struct obj *obj, boolean youattack, cancel_item(otmp); if (youdefend) { - gc.context.botl = 1; /* potential AC change */ + disp.botl = TRUE; /* potential AC change */ find_ac(); /* update_inventory(); -- handled by caller */ } @@ -3134,8 +3255,8 @@ cancel_monst(struct monst *mdef, struct obj *obj, boolean youattack, } } else { - Strcpy(gk.killer.name, "identity theft"); - gk.killer.format = KILLED_BY; + Strcpy(svk.killer.name, "identity theft"); + svk.killer.format = KILLED_BY; rehumanize(); } } @@ -3161,16 +3282,15 @@ cancel_monst(struct monst *mdef, struct obj *obj, boolean youattack, } /* you've zapped an immediate type wand up or down */ -static boolean -zap_updown(struct obj *obj) /* wand or spell */ +staticfn boolean +zap_updown(struct obj *obj) /* wand or spell, nonnull */ { - boolean striking = FALSE, disclose = FALSE; + boolean striking = FALSE, disclose = FALSE, map_zapped = FALSE; coordxy x, y, xx, yy; int ptmp; struct obj *otmp; struct engr *e; struct trap *ttmp; - char buf[BUFSZ]; stairway *stway = gs.stairs; /* some wands have special effects other than normal bhitpile */ @@ -3184,9 +3304,24 @@ zap_updown(struct obj *obj) /* wand or spell */ ptmp = 0; if (u.dz < 0) { You("probe towards the %s.", ceiling(x, y)); - } else { + } else { /* down */ + const char *surf; + schar ltyp, rememberedltyp = update_mapseen_for(x, y); + ptmp += bhitpile(obj, bhito, x, y, u.dz); - You("probe beneath the %s.", surface(x, y)); + /* sequencing: zap_map() calls force_decor() for ice or furniture; + we need to call it before probing for buried objects */ + ltyp = SURFACE_AT(x, y); + zap_map(x, y, obj); + /*map_zapped = TRUE; // not needed due to early return*/ + if (ltyp == ICE || IS_FURNITURE(ltyp)) { + surf = "it"; + if (svl.lastseentyp[x][y] != rememberedltyp) + ptmp += 1; + } else { + surf = the(surface(x, y)); + } + You("probe beneath %s.", surf); ptmp += display_binventory(x, y, TRUE); } if (!ptmp) @@ -3222,6 +3357,7 @@ zap_updown(struct obj *obj) /* wand or spell */ case WAN_STRIKING: case SPE_FORCE_BOLT: striking = TRUE; + FALLTHROUGH; /*FALLTHRU*/ case WAN_LOCKING: case SPE_WIZARD_LOCK: @@ -3242,7 +3378,7 @@ zap_updown(struct obj *obj) /* wand or spell */ /* similar to zap_dig() */ pline("A rock is dislodged from the %s and falls on your %s.", ceiling(x, y), body_part(HEAD)); - dmg = rnd((uarmh && is_hard(uarmh)) ? 2 : 6); + dmg = rnd(hard_helmet(uarmh) ? 2 : 6); losehp(Maybe_Half_Phys(dmg), "falling rock", KILLED_BY_AN); if ((otmp = mksobj(ROCK, FALSE, FALSE)) != 0) { (void) xname(otmp); /* set dknown, maybe bknown */ @@ -3314,43 +3450,10 @@ zap_updown(struct obj *obj) /* wand or spell */ /* zapping downward */ (void) bhitpile(obj, bhito, x, y, u.dz); - /* subset of engraving effects; none sets `disclose' */ - if ((e = engr_at(x, y)) != 0 && e->engr_type != HEADSTONE) { - switch (obj->otyp) { - case WAN_POLYMORPH: - case SPE_POLYMORPH: - del_engr(e); - make_engr_at(x, y, random_engraving(buf), gm.moves, 0); - break; - case WAN_CANCELLATION: - case SPE_CANCELLATION: - case WAN_MAKE_INVISIBLE: - del_engr(e); - break; - case WAN_TELEPORTATION: - case SPE_TELEPORT_AWAY: - rloc_engr(e); - break; - case SPE_STONE_TO_FLESH: - if (e->engr_type == ENGRAVE) { - /* only affects things in stone */ - pline_The(Hallucination - ? "floor runs like butter!" - : "edges on the floor get smoother."); - wipe_engr_at(x, y, d(2, 4), TRUE); - } - break; - case WAN_STRIKING: - case SPE_FORCE_BOLT: - wipe_engr_at(x, y, d(2, 4), TRUE); - break; - default: - break; - } - } - - maybe_explode_trap(ttmp, obj); - /* note: ttmp might be now gone */ + /* note: engraving handling that used to be here has been moved + to zap_map() */ + if (!map_zapped) + zap_map(x, y, obj); } else if (u.dz < 0) { /* zapping upward */ @@ -3360,7 +3463,7 @@ zap_updown(struct obj *obj) /* wand or spell */ */ if (u.uundetected && hides_under(gy.youmonst.data)) { int hitit = 0; - otmp = gl.level.objects[u.ux][u.uy]; + otmp = svl.level.objects[u.ux][u.uy]; if (otmp) hitit = bhito(otmp, obj); @@ -3469,7 +3572,7 @@ spell_damage_bonus( * Generate the to hit bonus for a spell. Based on the hero's skill in * spell class and dexterity. */ -static int +staticfn int spell_hit_bonus(int skill) { int hit_bon = 0; @@ -3517,20 +3620,18 @@ 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() */ { boolean verbosely = (mtmp == &gy.youmonst - || (Verbose(3, hit) + || (flags.verbose && (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 @@ -3538,10 +3639,10 @@ miss(const char *str, struct monst *mtmp) { pline("%s %s %s.", The(str), vtense(str, "miss"), ((cansee(gb.bhitpos.x, gb.bhitpos.y) || canspotmon(mtmp)) - && Verbose(3, miss)) ? mon_nam(mtmp) : "it"); + && flags.verbose) ? mon_nam(mtmp) : "it"); } -static void +staticfn void skiprange(int range, int *skipstart, int *skipend) { int tr = (range / 4); @@ -3553,11 +3654,14 @@ skiprange(int range, int *skipstart, int *skipend) *skipend = tmp - 1; } -/* maybe explode a trap hit by object otmp's effect; +/* Maybe explode a trap hit by object otmp's effect; cancellation beam hitting a magical trap causes an explosion. - might delete the trap. */ -static void -maybe_explode_trap(struct trap *ttmp, struct obj *otmp) + Might delete the trap; won't destroy otmp. */ +staticfn void +maybe_explode_trap( + struct trap *ttmp, + struct obj *otmp, + boolean *learn_it) { if (!ttmp || !otmp) return; @@ -3569,18 +3673,197 @@ maybe_explode_trap(struct trap *ttmp, struct obj *otmp) if (cansee(x, y)) { ttmp->tseen = 1; newsym(x, y); + *learn_it = TRUE; } } else if (is_magical_trap(ttmp->ttyp)) { - if (!Deaf) - pline("Kaboom!"); + int seeit = cansee(x, y); + + /* note: this explosion mustn't destroy otmp */ explode(x, y, -WAN_CANCELLATION, - 20+d(3,6), TRAP_EXPLODE, EXPL_MAGICAL); + 20 + d(3, 6), TRAP_EXPLODE, EXPL_MAGICAL); deltrap(ttmp); newsym(x, y); + if (seeit) + *learn_it = TRUE; } } } +/* zap_map() occurs before hitting monsters or objects and handles wands or + spells that don't dish out 'elemental' damage */ +staticfn void +zap_map( + coordxy x, coordxy y, + struct obj *obj) /* zapped wand, or book for cast spell */ +{ + struct trap *ttmp = t_at(x, y); + coordxy dbx = x, dby = y; /* might be changed by drawbridge handling */ + boolean learn_it = FALSE; + + /* + * We handle drawbridge for lateral zaps; zap_updown() handles up/down. + * Engravings only get hit by down zaps and we handle that here. + */ + + /* cancellation */ + maybe_explode_trap(ttmp, obj, &learn_it); + ttmp = t_at(x, y); /* refresh in case trap was altered or is gone */ + + if (u.dz > 0) { /* zapping down */ + char ebuf[BUFSZ]; + struct engr *e = engr_at(x, y); + + /* subset of engraving effects; none sets `disclose' */ + if (e && e->engr_type != HEADSTONE) { + switch (obj->otyp) { + case WAN_POLYMORPH: + case SPE_POLYMORPH: + del_engr(e); + make_engr_at(x, y, random_engraving(ebuf), svm.moves, 0); + break; + case WAN_CANCELLATION: + case SPE_CANCELLATION: + case WAN_MAKE_INVISIBLE: + del_engr(e); + break; + case WAN_TELEPORTATION: + case SPE_TELEPORT_AWAY: + rloc_engr(e); + break; + case SPE_STONE_TO_FLESH: + if (e->engr_type == ENGRAVE) { + /* only affects things in stone */ + pline_The(Hallucination + ? "floor runs like butter!" + : "edges on the floor get smoother."); + wipe_engr_at(x, y, d(2, 4), TRUE); + } + break; + case WAN_STRIKING: + case SPE_FORCE_BOLT: + wipe_engr_at(x, y, d(2, 4), TRUE); + break; + default: + break; + } + } + + } else if (!u.dz) { + int ltyp = levl[x][y].typ; + + if (find_drawbridge(&dbx, &dby)) { + switch (obj->otyp) { + case WAN_OPENING: + case SPE_KNOCK: + /* dbwall: 'closed door' of raised drawbridge */ + if (is_db_wall(x, y)) { + if (cansee(dbx, dby) || cansee(x, y)) + learn_it = TRUE; + open_drawbridge(dbx, dby); + } + break; + case WAN_LOCKING: + case SPE_WIZARD_LOCK: + /* drawbridge_down: span of lowered drawbridge */ + if ((cansee(dbx, dby) || cansee(x, y)) + && levl[dbx][dby].typ == DRAWBRIDGE_DOWN) + learn_it = TRUE; + close_drawbridge(dbx, dby); + break; + case WAN_STRIKING: + case SPE_FORCE_BOLT: + /* !drawbridge_up: not spot in front of raised bridge, + so either span of lowered bridge or portcullis */ + if (ltyp != DRAWBRIDGE_UP) { + learn_it = TRUE; + destroy_drawbridge(dbx, dby); + } + break; + } + } /* find_drawbridge */ + } /* !u.uz */ + + if (obj->otyp == WAN_PROBING) { + /* + * Probing, either up/down or lateral. + */ + schar ltyp; + int oldtyp, oldglyph; + + /* map terrain; might reveal a special room which is already within + view that hasn't been entered yet */ + oldtyp = svl.lastseentyp[x][y]; + oldglyph = glyph_at(x, y); + show_map_spot(x, y, FALSE); + if (oldtyp != svl.lastseentyp[x][y] || oldglyph != glyph_at(x, y)) { + /* TODO: ought to give some message */ + learn_it = TRUE; + } + ltyp = SURFACE_AT(x, y); + /* 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."); + learn_it = TRUE; + } + + /* secret corridor likewise, although only ones within view will + still be secret; for the !cansee(x,y) case, show_map_spot() + above has already converted the spot to regular corridor */ + } else if (ltyp == SCORR) { + levl[x][y].typ = CORR; + unblock_point(x, y); + newsym(x, y); + pline("Probing exposes a secret corridor."); + learn_it = TRUE; + + /* if on or over ice, describe it ("solid ice", "thin ice", &c); + likewise for furniture in case hero is levitating while blind */ + } else if (ltyp == ICE || IS_FURNITURE(ltyp)) { + if (u.dz > 0) { /* down, which also means x,y == u.ux,u.uy */ + force_decor(TRUE); + learn_it = TRUE; + } + } + /* + * Probing reveals undiscovered traps. + * + * FIXME? This finds floor traps even when zapping up and + * ceiling traps even when zapping down. + */ + if (ttmp) { + const char *ttmpname; + unsigned t_already_seen = ttmp->tseen; + boolean use_the, hallu = Hallucination != 0; + + /* should probably be changed to use sense_trap(detect.c) + so that trap can temporarily be forced to be shown and + map browsing can take place before it reverts to being + covered by monster or object(s) */ + ttmp->tseen = 1; + newsym(x, y); + + if (!t_already_seen || hallu) { + ttmpname = trapname(ttmp->ttyp, FALSE); + use_the = !hallu ? (ttmp->ttyp == VIBRATING_SQUARE + && Invocation_lev(&u.uz)) + : !rn2(4); + You("find %s%c", + use_the ? the(ttmpname) : an(ttmpname), + use_the ? '!' : '.'); + learn_it = !hallu; + } + } /* t_at() */ + } /* probing */ + + if (learn_it) + learnwand(obj); + return; +} + /* * Called for the following distance effects: * when a weapon is thrown (weapon == THROWN_WEAPON) @@ -3621,6 +3904,8 @@ bhit( boolean in_skip = FALSE, allow_skip = FALSE; boolean tethered_weapon = FALSE; int skiprange_start = 0, skiprange_end = 0, skipcount = 0; + struct obj *was_returning = + (iflags.returning_missile == obj) ? obj : (struct obj *) 0; if (weapon == KICKED_WEAPON) { /* object starts one square in front of player */ @@ -3667,7 +3952,7 @@ bhit( goto bhit_done; } - typ = levl[gb.bhitpos.x][gb.bhitpos.y].typ; + typ = levl[x][y].typ; if (typ == IRONBARS && ((levl[gb.bhitpos.x][gb.bhitpos.y].wall_info & W_NONDIGGABLE) == 0) @@ -3694,67 +3979,41 @@ bhit( /* iron bars will block anything big enough and break some things */ if (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON) { if (obj->lamplit && !Blind) - show_transient_light(obj, gb.bhitpos.x, gb.bhitpos.y); + show_transient_light(obj, x, y); if (typ == IRONBARS - && hits_bars(pobj, x - ddx, y - ddy, gb.bhitpos.x, gb.bhitpos.y, + && 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; } } else if (weapon == FLASHED_LIGHT) { if (!Blind) - show_transient_light((struct obj *) 0, - gb.bhitpos.x, gb.bhitpos.y); + show_transient_light((struct obj *) 0, x, y); } - if (weapon == ZAPPED_WAND && find_drawbridge(&x, &y)) { - boolean learn_it = FALSE; - - switch (obj->otyp) { - case WAN_OPENING: - case SPE_KNOCK: - if (is_db_wall(gb.bhitpos.x, gb.bhitpos.y)) { - if (cansee(x, y) || cansee(gb.bhitpos.x, gb.bhitpos.y)) - learn_it = TRUE; - open_drawbridge(x, y); - } - break; - case WAN_LOCKING: - case SPE_WIZARD_LOCK: - if ((cansee(x, y) || cansee(gb.bhitpos.x, gb.bhitpos.y)) - && levl[x][y].typ == DRAWBRIDGE_DOWN) - learn_it = TRUE; - close_drawbridge(x, y); - break; - case WAN_STRIKING: - case SPE_FORCE_BOLT: - if (cansee(x, y)) - learn_it = TRUE; - if (typ != DRAWBRIDGE_UP) - destroy_drawbridge(x, y); - break; - } - if (learn_it) - learnwand(obj); + if (weapon == ZAPPED_WAND) { + /* cancellation/opening/locking/striking/probing */ + zap_map(x, y, obj); + /* terrain might have changed (exposed secret door|corridor) */ + typ = levl[x][y].typ; } - if (weapon == ZAPPED_WAND) - maybe_explode_trap(t_at(gb.bhitpos.x, gb.bhitpos.y), obj); - - mtmp = m_at(gb.bhitpos.x, gb.bhitpos.y); - ttmp = t_at(gb.bhitpos.x, gb.bhitpos.y); - + mtmp = m_at(x, y); + ttmp = t_at(x, y); if (!mtmp && ttmp && (ttmp->ttyp == WEB) && (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON) && !rn2(3)) { - if (cansee(gb.bhitpos.x, gb.bhitpos.y)) { + if (cansee(x, y)) { pline("%s gets stuck in a web!", Yname2(obj)); ttmp->tseen = TRUE; - newsym(gb.bhitpos.x, gb.bhitpos.y); + newsym(x, y); } + if (was_returning) + iflags.returning_missile = (genericptr_t) 0; break; } @@ -3764,7 +4023,7 @@ bhit( * skiprange_start is only set if this is a thrown rock */ if (skiprange_start && (range == skiprange_start) && allow_skip) { - if (is_pool(gb.bhitpos.x, gb.bhitpos.y) && !mtmp) { + if (is_pool(x, y) && !mtmp) { in_skip = TRUE; if (!Blind) pline("%s %s%s.", Yname2(obj), otense(obj, "skip"), @@ -3790,28 +4049,31 @@ bhit( } /* if mtmp is a shade and missile passes harmlessly through it, - give message and skip it in order to keep going */ - if (mtmp && (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON) - && shade_miss(&gy.youmonst, mtmp, obj, TRUE, TRUE)) + 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()) */ + if (mtmp && (((weapon == THROWN_WEAPON || weapon == KICKED_WEAPON) + && shade_miss(&gy.youmonst, mtmp, obj, TRUE, TRUE)) + || (weapon == FLASHED_LIGHT + && M_AP_TYPE(mtmp) == M_AP_OBJECT))) mtmp = (struct monst *) 0; if (mtmp) { - gn.notonhead = (gb.bhitpos.x != mtmp->mx - || gb.bhitpos.y != mtmp->my); + gn.notonhead = (x != mtmp->mx || y != mtmp->my); if (weapon == FLASHED_LIGHT) { - /* FLASHED_LIGHT hitting invisible monster should - pass through instead of stop so we call - flash_hits_mon() directly rather than returning - mtmp back to caller. That allows the flash to - keep on going. Note that we use mtmp->minvis - not canspotmon() because it makes no difference - whether the hero can see the monster or not. */ + /* FLASHED_LIGHT hitting invisible monster should pass + through instead of stop so we call flash_hits_mon() + directly rather than returning mtmp back to caller. + That allows the flash to keep on going. Note that we + use mtmp->minvis not canspotmon() because it makes no + difference whether hero can see the monster or not. */ if (mtmp->minvis) { obj->ox = u.ux, obj->oy = u.uy; (void) flash_hits_mon(mtmp, obj); } else { tmp_at(DISP_END, 0); - result = mtmp; /* caller will call flash_hits_mon */ + result = mtmp; /* caller will call flash_hits_mon() */ goto bhit_done; } } else if (weapon == INVIS_BEAM) { @@ -3825,13 +4087,12 @@ bhit( goto bhit_done; } } else if (weapon != ZAPPED_WAND) { - /* THROWN_WEAPON, KICKED_WEAPON */ if (!tethered_weapon) tmp_at(DISP_END, 0); - if (cansee(gb.bhitpos.x, gb.bhitpos.y) && !canspotmon(mtmp)) - map_invisible(gb.bhitpos.x, gb.bhitpos.y); + if (cansee(x, y) && !canspotmon(mtmp)) + map_invisible(x, y); result = mtmp; goto bhit_done; } else { @@ -3844,20 +4105,18 @@ bhit( } } else { if (weapon == ZAPPED_WAND && obj->otyp == WAN_PROBING - && glyph_is_invisible(levl[gb.bhitpos.x][gb.bhitpos.y].glyph)) { - unmap_object(gb.bhitpos.x, gb.bhitpos.y); + && glyph_is_invisible(levl[x][y].glyph)) { + unmap_object(x, y); newsym(x, y); } } if (fhito) { - if (bhitpile(obj, fhito, gb.bhitpos.x, gb.bhitpos.y, 0)) + if (bhitpile(obj, fhito, x, y, 0)) range--; } else { if (weapon == KICKED_WEAPON - && ((obj->oclass == COIN_CLASS - && OBJ_AT(gb.bhitpos.x, gb.bhitpos.y)) - || ship_object(obj, gb.bhitpos.x, gb.bhitpos.y, - costly_spot(gb.bhitpos.x, gb.bhitpos.y)))) { + && ((obj->oclass == COIN_CLASS && OBJ_AT(x, y)) + || ship_object(obj, x, y, costly_spot(x, y)))) { tmp_at(DISP_END, 0); goto bhit_done; /* result == (struct monst *) 0 */ } @@ -3870,20 +4129,19 @@ bhit( case SPE_KNOCK: case SPE_WIZARD_LOCK: case SPE_FORCE_BOLT: - if (doorlock(obj, &gy.youmonst, gb.bhitpos.x, gb.bhitpos.y)) { - if (cansee(gb.bhitpos.x, gb.bhitpos.y) - || (obj->otyp == WAN_STRIKING && !Deaf)) + if (doorlock(obj, &gy.youmonst, x, y)) { + if (cansee(x, y) || (obj->otyp == WAN_STRIKING && !Deaf)) learnwand(obj); - if (doorstate(&levl[gb.bhitpos.x][gb.bhitpos.y]) == D_BROKEN - && *in_rooms(gb.bhitpos.x, gb.bhitpos.y, SHOPBASE)) { + if (doorstate(&levl[x][y]) == D_BROKEN + && *in_rooms(x, y, SHOPBASE)) { shopdoor = TRUE; - add_damage(gb.bhitpos.x, gb.bhitpos.y, SHOP_DOOR_COST); + add_damage(x, y, SHOP_DOOR_COST); } } break; } } - if (!ZAP_POS(typ) || closed_door(gb.bhitpos.x, gb.bhitpos.y)) { + if (!ZAP_POS(typ) || closed_door(x, y)) { gb.bhitpos.x -= ddx; gb.bhitpos.y -= ddy; break; @@ -3891,17 +4149,14 @@ bhit( if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM) { /* 'I' present but no monster: erase */ /* do this before the tmp_at() */ - if (glyph_is_invisible(levl[gb.bhitpos.x][gb.bhitpos.y].glyph) - && cansee(x, y)) { - unmap_object(gb.bhitpos.x, gb.bhitpos.y); + if (glyph_is_invisible(levl[x][y].glyph) && cansee(x, y)) { + unmap_object(x, y); newsym(x, y); } - tmp_at(gb.bhitpos.x, gb.bhitpos.y); + tmp_at(x, y); nh_delay_output(); /* kicked objects fall in pools */ - if ((weapon == KICKED_WEAPON) - && (is_pool(gb.bhitpos.x, gb.bhitpos.y) - || is_lava(gb.bhitpos.x, gb.bhitpos.y))) + if ((weapon == KICKED_WEAPON) && is_pool_or_lava(x, y)) break; if (IS_SINK(typ) && weapon != FLASHED_LIGHT) break; /* physical objects fall onto sink */ @@ -3936,7 +4191,8 @@ bhit( point_blank = FALSE; /* affects passing through iron bars */ } - if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM && !tethered_weapon) + if ((weapon != ZAPPED_WAND && weapon != INVIS_BEAM && !tethered_weapon) + || (was_returning && was_returning != iflags.returning_missile)) tmp_at(DISP_END, 0); if (shopdoor) @@ -3960,12 +4216,12 @@ bhit( struct monst * boomhit(struct obj *obj, coordxy dx, coordxy dy) { - register int i, ct; + int i, ct; int boom; /* showsym[] index */ struct monst *mtmp; - boolean counterclockwise = TRUE; /* right-handed throw */ + boolean counterclockwise = URIGHTY; /* ULEFTY => clockwise */ - /* counterclockwise traversal patterns: + /* counterclockwise traversal patterns, from @ to 1 then on through to 9 * ..........................54................................. * ..................43.....6..3....765......................... * ..........32.....5..2...7...2...8...4....87.................. @@ -3981,7 +4237,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); @@ -3991,6 +4247,11 @@ boomhit(struct obj *obj, coordxy dx, coordxy dy) dy = ydir[i]; gb.bhitpos.x += dx; gb.bhitpos.y += dy; + if (!isok(gb.bhitpos.x, gb.bhitpos.y)) { + gb.bhitpos.x -= dx; + gb.bhitpos.y -= dy; + break; + } if ((mtmp = m_at(gb.bhitpos.x, gb.bhitpos.y)) != 0) { m_respond(mtmp); tmp_at(DISP_END, 0); @@ -4094,7 +4355,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'); @@ -4102,10 +4364,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; } @@ -4155,8 +4416,10 @@ zhitm( /* can still blind the monster */ } if (!resists_blnd(mon) - && !(type > 0 && engulfing_u(mon))) { - register unsigned rnd_tmp = rnd(50); + && !(type > 0 && engulfing_u(mon)) + && nd > 2) { + /* sufficiently powerful lightning blinds monsters */ + unsigned rnd_tmp = rnd(50); mon->mcansee = 0; if ((mon->mblinded + rnd_tmp) > 127) mon->mblinded = 127; @@ -4200,7 +4463,7 @@ zhitm( return tmp; } -static void +staticfn void zhitu( int type, int nd, const char *fltxt, @@ -4218,6 +4481,8 @@ zhitu( pline("Some missiles bounce off!"); dam = (dam + 1) / 2; monstseesu(M_SEEN_MAGR); + } else { + monstunseesu(M_SEEN_MAGR); } /* Half spell damage stacks with Antimagic, but is already counted after * this switch */ @@ -4231,13 +4496,14 @@ zhitu( ugolemeffects(AD_FIRE, orig_dam); } else { dam = orig_dam; + monstunseesu(M_SEEN_FIRE); } burn_away_slime(); if (burnarmor(&gy.youmonst)) { /* "body hit" */ - if (!rn2(3)) { + if (!rn2(3)) (void) destroy_items(&gy.youmonst, AD_FIRE, orig_dam); + if (!rn2(3)) ignite_items(gi.invent); - } } break; case ZT_COLD: @@ -4249,6 +4515,7 @@ zhitu( ugolemeffects(AD_COLD, orig_dam); } else { dam = orig_dam; + monstunseesu(M_SEEN_COLD); } if (!rn2(3)) (void) destroy_items(&gy.youmonst, AD_COLD, orig_dam); @@ -4259,12 +4526,14 @@ zhitu( You("don't feel sleepy."); monstseesu(M_SEEN_SLEEP); } else { + monstunseesu(M_SEEN_SLEEP); fall_asleep(-d(nd, 25), TRUE); /* sleep ray */ } break; case ZT_DEATH: if (abstyp == ZT_BREATH(ZT_DEATH)) { - boolean disn_prot = adtyp_resistance_obj(&gy.youmonst, AD_DISN); + boolean disn_prot = inventory_resistance_check(&gy.youmonst, + AD_DISN); if (Disint_resistance) { You("are not disintegrated."); @@ -4272,23 +4541,25 @@ zhitu( break; } else if (disn_prot) { break; - } else if (uarms) { + } + monstunseesu(M_SEEN_DISINT); + if (uarms) { /* destroy shield; other possessions are safe */ - (void) destroy_arm(uarms, FALSE); + (void) destroy_arm(uarms); break; } else if (uarm) { /* destroy suit; if present, cloak goes too */ if (uarmc) - (void) destroy_arm(uarmc, FALSE); - (void) destroy_arm(uarm, FALSE); + (void) destroy_arm(uarmc); + (void) destroy_arm(uarm); break; } /* no shield or suit, you're dead; wipe out cloak and/or shirt in case of life-saving or bones */ if (uarmc) - (void) destroy_arm(uarmc, FALSE); + (void) destroy_arm(uarmc); if (uarmu) - (void) destroy_arm(uarmu, FALSE); + (void) destroy_arm(uarmu); } else if (nonliving(gy.youmonst.data) || is_demon(gy.youmonst.data) || gy.youmonst.data->mlet == S_ANGEL) { shieldeff(sx, sy); @@ -4300,8 +4571,9 @@ zhitu( You("aren't affected."); break; } - gk.killer.format = KILLED_BY_AN; - Strcpy(gk.killer.name, fltxt ? fltxt : ""); + monstunseesu(M_SEEN_MAGR); + svk.killer.format = KILLED_BY_AN; + Strcpy(svk.killer.name, fltxt ? fltxt : ""); /* when killed by disintegration breath, don't leave corpse */ u.ugrave_arise = (type == -ZT_BREATH(ZT_DEATH)) ? -3 : NON_PM; done(DIED); @@ -4316,6 +4588,7 @@ zhitu( } else { dam = orig_dam; exercise(A_CON, FALSE); + monstunseesu(M_SEEN_ELEC); } if (!rn2(3)) (void) destroy_items(&gy.youmonst, AD_ELEC, orig_dam); @@ -4332,6 +4605,7 @@ zhitu( pline_The("%s burns!", hliquid("acid")); dam = d(nd, 6); exercise(A_STR, FALSE); + monstunseesu(M_SEEN_ACID); } /* using two weapons at once makes both of them more vulnerable */ if (!rn2(u.twoweap ? 3 : 6)) @@ -4402,7 +4676,7 @@ burn_floor_objects( char buf1[BUFSZ], buf2[BUFSZ]; int cnt = 0; - for (obj = gl.level.objects[x][y]; obj; obj = obj2) { + for (obj = svl.level.objects[x][y]; obj; obj = obj2) { obj2 = obj->nexthere; if (obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS || (obj->oclass == FOOD_CLASS @@ -4431,9 +4705,10 @@ burn_floor_objects( /* useupf(), which charges, only if hero caused damage */ if (u_caused) useupf(obj, delquan); - else if (delquan < scrquan) + else if (delquan < scrquan) { obj->quan -= delquan; - else + obj->owt = weight(obj); + } else delobj(obj); cnt += delquan; if (give_feedback) { @@ -4451,14 +4726,15 @@ burn_floor_objects( } /* This also ignites floor items, but does not change cnt because they weren't consumed. */ - ignite_items(gl.level.objects[x][y]); + ignite_items(svl.level.objects[x][y]); return cnt; } /* will zap/spell/breath attack score a hit against armor class `ac'? */ -static int -zap_hit(int ac, - int type) /* either hero cast spell type or 0 */ +staticfn int +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; @@ -4473,7 +4749,7 @@ zap_hit(int ac, return (3 - chance < ac + spell_bonus); } -static void +staticfn void disintegrate_mon( struct monst *mon, int type, /* hero vs other */ @@ -4539,19 +4815,21 @@ dobuzz( boolean say) /* announce out of sight hit/miss events if true */ { int range, fltyp = zaptype(type), damgtype = fltyp % 10; - register coordxy lsx, lsy; + coordxy lsx, lsy; struct monst *mon; coord save_bhitpos; - boolean shopdamage = FALSE; + boolean shopdamage = FALSE, + fireball = (type == ZT_SPELL(ZT_FIRE)), /* set once */ + gas_hit = FALSE; /* will be set during each iteration */ struct obj *otmp; int spell_type; int hdmgtype = Hallucination ? rn2(6) : damgtype; - /* if its a Hero Spell then get its SPE_TYPE */ + /* if it's a Hero Spell then get its SPE_TYPE */ spell_type = is_hero_spell(type) ? SPE_MAGIC_MISSILE + damgtype : 0; if (u.uswallow) { - register int tmp; + int tmp; if (type < 0) return; @@ -4600,8 +4878,12 @@ dobuzz( /* hit() and miss() need gb.bhitpos to match the target */ gb.bhitpos.x = sx, gb.bhitpos.y = sy; - /* Fireballs only damage when they explode */ - if (type != ZT_SPELL(ZT_FIRE)) { + gas_hit = (damgtype == ZT_POISON_GAS); + /* fireballs only damage when they explode; poison gas leaves + a trail of 1x1 clouds via zap_over_floor(), but that gets + skipped for a hit that is reflected so is deferred until we + know whether reflection is happening */ + if (!fireball && !gas_hit) { range += zap_over_floor(sx, sy, type, &shopdamage, TRUE, 0); /* zap with fire -> melt ice -> drown monster, so monster found and cached above might not be here any more */ @@ -4610,7 +4892,7 @@ dobuzz( if (mon) { int saved_mhp; - if (type == ZT_SPELL(ZT_FIRE)) + if (fireball) break; if (type >= 0) mon->mstrategy &= ~STRAT_WAITMASK; @@ -4624,6 +4906,7 @@ dobuzz( shieldeff(mon->mx, mon->my); (void) mon_reflects(mon, "But it reflects from %s %s!"); + gas_hit = FALSE; } dx = -dx; dy = -dy; @@ -4670,7 +4953,7 @@ dobuzz( if it's fire, highly flammable monsters leave no corpse; don't bother reporting that they "burn completely" -- unnecessary verbosity */ - if ((type % 10 == ZT_FIRE) + if (damgtype == ZT_FIRE /* paper golem or straw golem */ && completelyburns(mon->data)) xkflags |= XKILL_NOCORPSE; @@ -4709,11 +4992,11 @@ dobuzz( goto buzzmonst; } else if (zap_hit((int) u.uac, 0)) { range -= 2; - pline("%s hits you!", The(flash_str(fltyp, FALSE))); - const char* reflectsrc = ureflectsrc(); - if (reflectsrc) { + pline_dir(xytod(-dx, -dy), "%s hits you!", + The(flash_str(fltyp, FALSE))); + if (Reflecting) { if (!Blind) { - pline("But it reflects from your %s!", reflectsrc); + pline("But it reflects from your %s!", ureflectsrc()); } else { pline("For some reason you are not affected."); @@ -4722,10 +5005,12 @@ dobuzz( dx = -dx; dy = -dy; shieldeff(sx, sy); + gas_hit = FALSE; } else { /* flash_str here only used for killer; suppress * hallucination */ zhitu(type, nd, flash_str(fltyp, TRUE), sx, sy); + monstunseesu(M_SEEN_REFL); } } else if (!Blind) { pline("%s whizzes by you!", The(flash_str(fltyp, FALSE))); @@ -4733,23 +5018,25 @@ dobuzz( Your("%s tingles.", body_part(ARM)); } if (damgtype == ZT_LIGHTNING) - (void) flashburn((long) d(nd, 50)); + (void) flashburn((long) d(nd, 50), TRUE); stop_occupation(); nomul(0); } + /* gas that missed or that hit without being reflected will leave + a 1x1 cloud here; the earlier zap_over_floor() was deferred */ + if (gas_hit) + (void) zap_over_floor(sx, sy, type, &shopdamage, TRUE, 0); if (!ZAP_POS(levl[sx][sy].typ) || (closed_door(sx, sy) && range >= 0)) { int bounce, bchance; uchar rmn; - boolean fireball; make_bounce: bchance = (!isok(sx, sy) || levl[sx][sy].typ == STONE) ? 10 : (In_mines(&u.uz) && IS_WALL(levl[sx][sy].typ)) ? 20 : 75; bounce = 0; - fireball = (type == ZT_SPELL(ZT_FIRE)); if ((--range > 0 && isok(lsx, lsy) && cansee(lsx, lsy)) || fireball) { if (Is_airlevel(&u.uz)) { /* nothing to bounce off of */ @@ -4784,6 +5071,7 @@ dobuzz( switch (bounce) { case 0: dx = -dx; + FALLTHROUGH; /*FALLTHRU*/ case 1: dy = -dy; @@ -4797,7 +5085,7 @@ dobuzz( } } tmp_at(DISP_END, 0); - if (type == ZT_SPELL(ZT_FIRE)) + if (fireball) explode(sx, sy, type, d(12, 6), 0, EXPL_FIERY); if (shopdamage) pay_for_damage(damgtype == ZT_FIRE ? "burn away" @@ -4894,25 +5182,26 @@ melt_ice_away(anything *arg, long timeout UNUSED) { coordxy x, y; long where = arg->a_long; - boolean save_mon_moving = gc.context.mon_moving; /* will be False */ + boolean save_mon_moving = svc.context.mon_moving; /* will be False */ /* melt_ice -> minliquid -> mondead|xkilled shouldn't credit/blame hero */ - gc.context.mon_moving = TRUE; /* hero isn't causing this ice to melt */ + svc.context.mon_moving = TRUE; /* hero isn't causing this ice to melt */ y = (coordxy) (where & 0xFFFF); x = (coordxy) ((where >> 16) & 0xFFFF); /* melt_ice does newsym when appropriate */ melt_ice(x, y, "Some ice melts away."); - gc.context.mon_moving = save_mon_moving; + svc.context.mon_moving = save_mon_moving; } /* Burn floor scrolls, evaporate pools, etc... in a single square. * Used both for normal bolts of fire, cold, etc... and for fireballs. * Sets shopdamage to TRUE if a shop door is destroyed, and returns the - * amount by which range is reduced (the latter is just ignored by fireballs) + * amount by which range is reduced (value is negative and will be added + * to remaining range by caller; ignored by fireballs and poison gas). */ int zap_over_floor( - coordxy x, coordxy y, /* location */ + coordxy x, coordxy y, /* location */ int type, /* damage type plus {wand|spell|breath} info */ boolean *shopdamage, /* extra output if shop door is destroyed */ boolean ignoremon, /* ignore any monster here */ @@ -4939,14 +5228,14 @@ zap_over_floor( /* a burning web is too flimsy to notice if you can't see it */ if (see_it) Norep("A web bursts into flames!"); - (void) delfloortrap(t); + (void) delfloortrap(t), t = (struct trap *) 0; if (see_it) newsym(x, y); } if (is_ice(x, y)) { melt_ice(x, y, (char *) 0); } else if (is_pool(x, y)) { - boolean on_water_level = Is_waterlevel(&u.uz); + boolean on_water_level = Is_waterlevel(&u.uz), msggiven = FALSE; const char *msgtxt = (!Deaf) ? "You hear hissing gas." /* Deaf-aware */ : (type >= 0) @@ -4955,10 +5244,14 @@ zap_over_floor( /* don't create steam clouds on Plane of Water; air bubble movement and gas regions don't understand each other */ - if (!on_water_level) + if (!on_water_level) { create_gas_cloud(x, y, rnd(5), 0); /* 1..5, no damg */ + if (iflags.last_msg == PLNMSG_ENVELOPED_IN_GAS) + msggiven = TRUE; + } if (lev->typ != POOL) { /* MOAT or DRAWBRIDGE_UP or WATER */ + t = (struct trap *) 0; if (on_water_level) msgtxt = (see_it || !Deaf) ? "Some water boils." : 0; else if (see_it) @@ -4972,9 +5265,10 @@ zap_over_floor( if (see_it) msgtxt = "The water evaporates."; } - if (msgtxt) + if (msgtxt && !msggiven) Norep("%s", msgtxt); - if (lev->typ == ROOM) { + + if (lev->typ == ROOM) { /* POOL changed to ROOM above */ if ((mon = m_at(x, y)) != 0) { /* probably ought to do some hefty damage to any creature caught in boiling water; @@ -4984,6 +5278,15 @@ zap_over_floor( } } newsym(x, y); + if (t) { + /* if water walking/swimming/magical breathing, maybe fall + into the new pit (after the water evaporation message); + if flying or levitating, nothing will happen */ + if (u_at(x, y)) + dotrap(t, NO_TRAP_FLAGS); + else if (mon) + mintrap(mon, NO_TRAP_FLAGS); + } } } else if (IS_FOUNTAIN(lev->typ)) { create_gas_cloud(x, y, rnd(3), 0); /* 1..3, no damage */ @@ -5005,7 +5308,7 @@ zap_over_floor( if (is_pool(x, y) || is_lava(x, y) || lavawall) { boolean lava = (is_lava(x, y) || lavawall), moat = is_moat(x, y); - int chance = max(2, 5 + gl.level.flags.temperature * 10); + int chance = max(2, 5 + svl.level.flags.temperature * 10); if (IS_WATERWALL(lev->typ) || (lavawall && rn2(chance))) { /* For now, don't let WATER freeze. */ @@ -5035,7 +5338,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; } @@ -5100,33 +5403,36 @@ zap_over_floor( break; /* ZT_COLD */ case ZT_POISON_GAS: - (void) create_gas_cloud(x, y, rnd(5), 8); + /* poison gas with range 1: green dragon/iron golem breath (AD_DRST); + caller is placing a series of 1x1 clouds along the zap's path; + for wall locations might be included--reject those */ + if (ZAP_POS(lev->typ)) + (void) create_gas_cloud(x, y, rnd(5), 8); break; + case ZT_LIGHTNING: + FALLTHROUGH; + /*FALLTHRU*/ case ZT_ACID: if (lev->typ == IRONBARS) { + if (damgtype == ZT_LIGHTNING && rn2(10)) + break; if ((lev->wall_info & W_NONDIGGABLE) != 0) { if (see_it) - Norep("The %s corrode somewhat but remain intact.", - defsyms[S_bars].explanation); + Norep("The %s %s somewhat but remain intact.", + 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)) { - /* in case we ever have a shop bounded by bars */ - lev->typ = ROOM, lev->flags = 0; - if (see_it) - newsym(x, y); add_damage(x, y, (type >= 0) ? SHOP_BARS_COST : 0L); if (type >= 0) *shopdamage = TRUE; - } else { - lev->typ = DOOR; - set_doorstate(lev, D_NODOOR); - if (see_it) - newsym(x, y); } } } @@ -5160,6 +5466,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); @@ -5236,7 +5543,7 @@ zap_over_floor( add_damage(x, y, 0L); } set_doorstate(lev, new_doormask); - unblock_point(x, y); /* vision */ + recalc_block_point(x, y); /* vision */ if (see_it) { pline1(see_txt); newsym(x, y); @@ -5257,16 +5564,53 @@ zap_over_floor( You("%s of smoke.", !Blind ? "see a puff" : "smell a whiff"); } if (!ignoremon && (mon = m_at(x, y)) != 0) - wakeup(mon, (type >= 0 && !gc.context.mon_moving) ? TRUE : FALSE, TRUE); + wakeup(mon, (type >= 0 && !svc.context.mon_moving) ? TRUE : FALSE, + TRUE); return rangemod; } -/* fractured by pick-axe or wand of striking or by vault guard */ +/* monster has cast flames or frost at target on ; called by mcastu() */ +void +mon_spell_hits_spot( + struct monst *caster UNUSED, + int adtyp, /* canonical damage type */ + coordxy x, coordxy y) /* so far, only used for targeting */ +{ + /* "shower of missiles" or [hypothetical] "acid rain" attack: + thoroughly clobber an engraving (unless its type makes it be + scuff-protected); zap_over_floor() doesn't handle this */ + if (adtyp == AD_MAGM || adtyp == AD_ACID) { + struct engr *ep = engr_at(x, y); + char *etext = ep ? ep->engr_txt[actual_text] : NULL; + + if (etext) + wipe_engr_at(x, y, (int) strlen(etext) + d(6, 6), TRUE); + /* hero and player will still remember prior text until the spot + is re-examined (lookhere or move off and back on) */ + } + + /* hit items and/or terrain; only matters for AD_FIRE and AD_COLD but + accept any basic damage type that zap_over_floor() might handle */ + if (adtyp >= AD_MAGM && adtyp <= AD_ACID) { + boolean shopdummy = FALSE; /* zap_over_floor() requires this even + * though it's only used when zapdmgtyp + * is non-negative (hero's fault) */ + int zt_typ = adtyp - 1, /* convert AD_xxxx to ZT_xxxx */ + zapdmgtyp = -ZT_SPELL(zt_typ); /* damage is from monster spell */ + + (void) zap_over_floor(x, y, zapdmgtyp, &shopdummy, TRUE, 0); + } else { + impossible("Unsupported damage type (%d) for mon_spell_hits_spot.", + adtyp); + } +} + +/* fractured by pick-axe or wand of striking or by vault guard or shopkeeper */ void fracture_rock(struct obj *obj) /* no texts here! */ { coordxy x, y; - boolean by_you = !gc.context.mon_moving; + boolean by_you = !svc.context.mon_moving; if (by_you && get_obj_location(obj, &x, &y, 0) && costly_spot(x, y)) { struct monst *shkp = 0; @@ -5276,7 +5620,9 @@ fracture_rock(struct obj *obj) /* no texts here! */ /* shop message says "you owe <$> for it!" so we need to precede that with a message explaining what "it" is */ You("fracture %s %s.", s_suffix(shkname(shkp)), xname(obj)); - breakobj(obj, x, y, TRUE, FALSE); /* charges for shop goods */ + /* breakobj won't destroy fracturing statue or boulder but + will charge for shop goods */ + (void) breakobj(obj, x, y, TRUE, FALSE); } } if (by_you && obj->otyp == BOULDER) @@ -5310,7 +5656,7 @@ break_statue(struct obj *obj) { /* [obj is assumed to be on floor, so no get_obj_location() needed] */ struct trap *trap = t_at(obj->ox, obj->oy); - boolean by_you = !gc.context.mon_moving; + boolean by_you = !svc.context.mon_moving; if (trap && trap->ttyp == STATUE_TRAP && activate_statue_trap(trap, obj->ox, obj->oy, TRUE)) @@ -5351,12 +5697,12 @@ destroyable_oclass(char oclass) return FALSE; } -/* Return TRUE if obj is eligible to pass to maybe_destroy_item given the type of - * elemental damage it's being subjected to. +/* Return TRUE if obj is eligible to pass to maybe_destroy_item given + * the type of elemental damage it's being subjected to. * Note that things like the Book of the Dead are eligible even though they * won't get destroyed, because it will attempt to be destroyed but print a * special message instead. */ -static boolean +staticfn boolean destroyable(struct obj *obj, int adtyp) { if (obj->oartifact) { @@ -5376,21 +5722,17 @@ destroyable(struct obj *obj, int adtyp) if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL) { return FALSE; } - if (obj->otyp == GLOB_OF_GREEN_SLIME - || obj->oclass == POTION_CLASS - || obj->oclass == SCROLL_CLASS - || obj->oclass == SPBOOK_CLASS) { + if (obj->otyp == GLOB_OF_GREEN_SLIME || obj->oclass == POTION_CLASS + || obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS) { return TRUE; } - } - else if (adtyp == AD_COLD) { + } else if (adtyp == AD_COLD) { /* non-water potions don't freeze and shatter */ if (obj->oclass == POTION_CLASS && obj->otyp != POT_ACID && obj->otyp != POT_OIL) { return TRUE; } - } - else if (adtyp == AD_ELEC) { + } else if (adtyp == AD_ELEC) { if (obj->oclass != RING_CLASS && obj->oclass != WAND_CLASS) { return FALSE; } @@ -5406,7 +5748,7 @@ destroyable(struct obj *obj, int adtyp) } /* convert attack damage AD_foo to property resistance */ -static int +staticfn int adtyp_to_prop(int dmgtyp) { switch (dmgtyp) { @@ -5426,26 +5768,60 @@ adtyp_to_prop(int dmgtyp) return 0; /* prop_types start at 1 */ } -/* is carrier wearing or wielding an object with resistance - to attack damage type */ -boolean +/* is carrier wearing or wielding an object with resistance to attack damage + * type? + * note: upstream NetHack has u_adtyp_resistance_obj and monsters do not enjoy + * any protection from item destruction; u_adtyp_resistance_obj(typ) is + * equivalent to adtyp_resistance_obj(&gy.youmonst, typ). There's no wrapper in + * order to force any new u_adtyp_resistance_obj upstream to cause a merge + * conflict so it can be examined. */ +int adtyp_resistance_obj(struct monst *carrier, int dmgtyp) { int prop = adtyp_to_prop(dmgtyp); if (!prop) - return FALSE; + return 0; + /* Items that give an extrinsic resistance give 100% protection to + your items */ if (carrier == &gy.youmonst && (u.uprops[prop].extrinsic & (W_ARMOR | W_ACCESSORY | W_WEP)) != 0) - return TRUE; + return 100; else if (carrier != &gy.youmonst && prop <= 8 && (carrier->mextrinsics & (1 << (prop - 1)))) { /* this relies on the assumption that MR_ 1-8 correspond to props 1-8 */ - return TRUE; + return 100; } - return FALSE; + /* Dwarvish cloaks give a 90% protection to items against heat and cold */ + if (dmgtyp == AD_COLD || dmgtyp == AD_FIRE) { + struct obj *cloak = which_armor(carrier, W_ARMC); + if (cloak && cloak->otyp == DWARVISH_CLOAK) + return 90; + } + + return 0; +} + +/* Rolls to see whether an object in inventory resists damage from the + given damage type, due to an equipped item protecting it. Use + u_adtyp_resistance_obj to discover whether objects are protected in + general (e.g. for enlightenment) and this function to actually do + the roll to see whether a specific object is protected. + + This function doesn't check for other reasons why an object might + be protected; you will usually need to do an obj_resists() call + too. */ +boolean +inventory_resistance_check(struct monst *targ, int dmgtyp) +{ + int prob = adtyp_resistance_obj(targ, dmgtyp); + + if (!prob) + return FALSE; + + return rn2(100) < prob; } /* for enlightenment; currently only useful in wizard mode; cf from_what() */ @@ -5504,6 +5880,7 @@ item_what(int dmgtyp) * [6] shocked wand * (books, rings, and wands don't stack so don't need plural form; * crumbling ring doesn't do damage so doesn't need killer reason) + * externally referenced from trap.c. */ const char *const destroy_strings[][3] = { /* also used in trap.c */ @@ -5517,15 +5894,18 @@ const char *const destroy_strings[][3] = { }; /* guts of destroy_items(); - caller must decide whether obj is eligible, though there's one case (Book of - the Dead) in which an eligible item shouldn't be destroyed (it prints a + caller must decide whether obj is eligible, though there's one case (Book + of the Dead) in which an eligible item shouldn't be destroyed (it prints a special message instead). - Returns the amount of damage done, but this is used differently depending on + Returns the amount of damage done, but it's used differently depending on whether it's the player or a monster having an item destroyed: players lose the HP and possibly die in this function, and the return value is unused, whereas monsters return the damage to their caller to be taken off later */ -static int -maybe_destroy_item(struct monst *carrier, struct obj *obj, int dmgtyp) +staticfn int +maybe_destroy_item( + struct monst *carrier, + struct obj *obj, + int dmgtyp) { long i, cnt, quan; int dmg, xresist, skip, dindx; @@ -5540,7 +5920,7 @@ maybe_destroy_item(struct monst *carrier, struct obj *obj, int dmgtyp) quan = 0L; /* external worn item protects inventory? */ - if (adtyp_resistance_obj(carrier, dmgtyp)) + if (inventory_resistance_check(carrier, dmgtyp)) return 0; switch (dmgtyp) { @@ -5612,9 +5992,10 @@ maybe_destroy_item(struct monst *carrier, struct obj *obj, int dmgtyp) } if (chargeit) { - recharge(obj, 0); - } - else if (!skip) { + /* FIXME: recharge only handles items in hero's inventory */ + if (u_carry) + recharge(obj, 0); + } else if (!skip) { char osym = obj->oclass; /* for checking glob of slime after it's destroyed */ if (obj->in_use) @@ -5627,19 +6008,19 @@ maybe_destroy_item(struct monst *carrier, struct obj *obj, int dmgtyp) return 0; if (u_carry || vis) { - mult = (cnt == 1L) - ? ((quan == 1L) ? "" /* 1 of 1 */ - : "One of ") /* 1 of N */ - : ((cnt < quan) ? "Some of " /* n of N */ - : (quan == 2L) ? "Both of " /* 2 of 2 */ - : "All of "); /* N of N */ + mult = (cnt == 1L) ? ((quan == 1L) ? "" /* 1 of 1 */ + : "One of ") /* 1 of N */ + : ((cnt < quan) ? "Some of " /* n of N */ + : (quan == 2L) ? "Both of " /* 2 of 2 */ + : "All of "); /* N of N */ pline("%s%s %s!", mult, (cnt == 1L && quan == 1L) ? Yname2(obj) : yname(obj), destroy_strings[dindx][(cnt > 1L)]); } if (u_carry) { /* effects that happen only to the player */ if (osym == POTION_CLASS && dmgtyp != AD_COLD - && (!breathless(gy.youmonst.data) || haseyes(gy.youmonst.data))) { + && (!breathless(gy.youmonst.data) + || haseyes(gy.youmonst.data))) { potionbreathe(obj); } if (obj->owornmask) { /* m_useup handles these for monster */ @@ -5679,25 +6060,27 @@ maybe_destroy_item(struct monst *carrier, struct obj *obj, int dmgtyp) return dmg; } -/* scaling factor; dmg/5 stacks of items will be subjected to destroy_items() */ +/* scaling factor; dmg/5 stacks will be subjected to destroy_items() */ #define DMG_DESTROY_SCALE 5 /* largest amount of stacks that will be destroyed in a single call */ #define MAX_ITEMS_DESTROYED 20 /* target items of specified class in mon's inventory for possible destruction - * return total amount of damage inflicted, though this is unused if mon is the - * player */ + return total amount of damage inflicted, though this is unused if mon is + the player */ int -destroy_items(struct monst *mon, /* monster whose invent is being subjected to - * destruction */ - int dmgtyp, /* AD_**** - currently only cold, fire, elec */ - int dmg_in) /* the amount of HP damage the attack dealt */ +destroy_items( + struct monst *mon, /* monster whose invent is being subjected to + * destruction */ + int dmgtyp, /* AD_**** - currently only cold, fire, elec */ + int dmg_in) /* the amount of HP damage the attack dealt */ { - register struct obj *obj; + struct obj *obj; int i, defer; int limit; /* max amount of item stacks destroyed, based on damage */ struct { unsigned oid; + struct obj *otmp; boolean deferred; } items_to_destroy[MAX_ITEMS_DESTROYED]; int elig_stacks = 0; /* number of destroyable objects found so far */ @@ -5705,17 +6088,19 @@ destroy_items(struct monst *mon, /* monster whose invent is being subjected to /* this is a struct obj** because we might destroy the first item in it */ struct obj **objchn = u_carry ? &gi.invent : &mon->minvent; int dmg_out = 0; /* damage caused by items getting destroyed */ + xint8 where = NOBJ_STATES; /* initialize items_to_destroy */ for (i = 0; i < MAX_ITEMS_DESTROYED; ++i) { /* 0 should not be a valid o_id for anything */ items_to_destroy[i].oid = 0; + items_to_destroy[i].otmp = (struct obj *) 0; items_to_destroy[i].deferred = FALSE; } - /* Don't straight up destroy all items with an equal chance; limit it based - * on the amount of damage being dealt by the source of the item - * destruction. */ + /* don't straight up destroy all items with an equal chance; limit it + based on the amount of damage being dealt by the source of the item + destruction */ limit = dmg_in / DMG_DESTROY_SCALE; if (dmg_in % DMG_DESTROY_SCALE > rn2(DMG_DESTROY_SCALE)) { limit++; /* dmg = 9: 20% chance of limit=1, 80% of limit=2, etc */ @@ -5766,7 +6151,6 @@ destroy_items(struct monst *mon, /* monster whose invent is being subjected to && obj->corpsenm == PM_PHOENIX) { revive_egg(obj); } - if (!destroyable(obj, dmgtyp)) continue; /* this dmg type can't destroy this obj */ @@ -5774,11 +6158,17 @@ destroy_items(struct monst *mon, /* monster whose invent is being subjected to i = (elig_stacks < limit) ? elig_stacks : rn2(elig_stacks); /* do this afterwards to avoid not filling items_to_destroy[0] */ elig_stacks++; - if (i >= limit) { - /* random index was too high */ + if (i < 0 || i >= limit) { + /* random index was too high; mollify analyzer by including < 0 */ continue; } items_to_destroy[i].oid = obj->o_id; + items_to_destroy[i].otmp = obj; + if (where == NOBJ_STATES) + where = obj->where; + else if (where != obj->where) + impossible("destroy_item: items in multiple chains"); + /* if loss of this item might dump us onto a trap, hold off until later because potential recursive destroy_items() will result in setting bypass bits on whole chain--we would skip @@ -5789,11 +6179,10 @@ destroy_items(struct monst *mon, /* monster whose invent is being subjected to || objects[obj->otyp].oc_oprop == FLYING)) /* destroyed wands and potions of polymorph don't trigger polymorph so don't need to be deferred */ - || (obj->otyp == POT_WATER && u.ulycn >= LOW_PM + || (obj->otyp == POT_WATER && ismnum(u.ulycn) && (Upolyd ? obj->blessed : obj->cursed)))) { items_to_destroy[i].deferred = TRUE; - } - else { + } else { items_to_destroy[i].deferred = FALSE; } } @@ -5804,20 +6193,18 @@ destroy_items(struct monst *mon, /* monster whose invent is being subjected to /* if we saved some items for later (most likely just a worn ring of levitation) and they're still in inventory, handle them on the second iteration of the loop */ - struct obj *nobj; - for (obj = *objchn; obj; obj = nobj) { - nobj = obj->nobj; - for (i = 0; i < elig_stacks; ++i) { - if (obj->o_id == items_to_destroy[i].oid - && (items_to_destroy[i].deferred == (defer == 1))) { - dmg_out += maybe_destroy_item(mon, obj, dmgtyp); - break; - } + for (i = 0; i < elig_stacks; ++i) { + obj = items_to_destroy[i].otmp; + if (obj && obj->o_id == items_to_destroy[i].oid + && obj->where == where + && (items_to_destroy[i].deferred == (defer == 1))) { + dmg_out += maybe_destroy_item(mon, obj, dmgtyp); + items_to_destroy[i].otmp = (struct obj *) 0; } } } - /* almost certainly not everything was destroyed; remove bypass bit after it - * was set earlier */ + /* almost certainly not everything was destroyed; clear bypass bit after + it was set earlier */ bypass_objlist(*objchn, FALSE); return dmg_out; } @@ -5866,10 +6253,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; } @@ -5890,7 +6275,7 @@ resist(struct monst *mtmp, char oclass, int damage, int tell) DISABLE_WARNING_FORMAT_NONLITERAL -static void +staticfn void wishcmdassist(void) { static NEARDATA const char * @@ -5947,7 +6332,7 @@ makewish(void) promptbuf[0] = '\0'; nothing = cg.zeroobj; /* lint suppression; only its address matters */ - if (Verbose(3, makewish)) + if (flags.verbose) You("may wish for an object."); retry: Strcpy(promptbuf, "For what do you wish"); @@ -5969,7 +6354,7 @@ makewish(void) Strcpy(buf, "nothing"); } #ifdef HANGUPHANDLING - else if (gp.program_state.done_hup) { + else if (program_state.done_hup) { Strcpy(buf, "nothing"); } #endif @@ -5979,8 +6364,9 @@ makewish(void) } /* * Note: if they wished for and got a non-object successfully, - * otmp == &zeroobj. That includes an artifact which has been denied. - * Wishing for "nothing" requires a separate value to remain distinct. + * otmp == &hands_obj. That includes an artifact which has been + * denied. Wishing for "nothing" requires a separate value to remain + * distinct. */ strcpy(bufcpy, buf); otmp = readobjnam(buf, ¬hing); @@ -5998,7 +6384,7 @@ makewish(void) to retain wishless conduct */ livelog_printf(LL_WISH, "declined to make a wish"); return; - } else if (otmp == &cg.zeroobj) { + } else if (otmp == &hands_obj) { /* wizard mode terrain wish: skip livelogging, etc */ return; } @@ -6020,17 +6406,25 @@ makewish(void) "made %s first artifact wish - %s", uhis(), wish); else livelog_printf((LL_WISH | maybe_LL_arti), "wished for %s", wish); - /* TODO? maybe generate a second event decribing what was received since - those just echo player's request rather than show actual result */ + /* TODO? maybe generate a second event describing what was received since + these just echo player's request rather than show actual result */ + + 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" : "drop"), + 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)), @@ -6051,12 +6445,11 @@ flash_str( typ = zaptype(typ); if (Hallucination && !nohallu) { - /* always return "blast of foo" for simplicity. - * This could be extended with hallucinatory rays, but probably - * not worth it at this time. */ + /* always return "blast of foo" for simplicity; + this could be extended with hallucinatory rays, but probably + not worth it at this time */ Sprintf(fltxt, "blast of %s", rnd_hallublast()); - } - else { + } else { Strcpy(fltxt, flash_types[typ]); } return fltxt; diff --git a/submodules/CHKSUMS b/submodules/CHKSUMS new file mode 100644 index 0000000000..bea0465478 --- /dev/null +++ b/submodules/CHKSUMS @@ -0,0 +1,8 @@ +# checksums for fetched submodules +# NetHack 3.7 CHKSUMS $NHDT-Date: 1693155877 2023/08/27 17:04:37 $ $NHDT-Branch: keni-luacksum $ +# check: +# shasum -a 256 -c -s THISFILE < FILETOCHECK +# generate: +# shasum -a 256 FILETOCHECK >>THISFILE +7d5ea1b9cb6aa0b59ca3dde1c6adcb57ef83a1ba8e5432c0ecd06bf439b3ad88 lua-5.4.6.tar.gz +9fbf5e28ef86c69858f6d3d34eccc32e911c1a28b4120ff3e84aaa70cfbf1e30 lua-5.4.7.tar.gz diff --git a/submodules/pdcurses b/submodules/pdcurses index 2fa0f10dd8..5b5c3d349b 160000 --- a/submodules/pdcurses +++ b/submodules/pdcurses @@ -1 +1 @@ -Subproject commit 2fa0f10dd844da47ee83c05a40a1ec541ceb95e1 +Subproject commit 5b5c3d349b9948cc37da39c985d65f395059e833 diff --git a/submodules/pdcursesmod b/submodules/pdcursesmod index abbd8f5268..e2b2205da6 160000 --- a/submodules/pdcursesmod +++ b/submodules/pdcursesmod @@ -1 +1 @@ -Subproject commit abbd8f526875aede5fc3b5978a943f2bdf0678bf +Subproject commit e2b2205da6b3256f9a66e60592853e6c1d9c4a59 diff --git a/sys/libnh/README.md b/sys/libnh/README.md index 4458cbab03..b887b57309 100644 --- a/sys/libnh/README.md +++ b/sys/libnh/README.md @@ -25,11 +25,11 @@ Generally the build is the same as the unix build: [Edit Oct 4, 2020:] -Resulting libaries will be in the `targets/wasm` directory for `CROSS_TO_WASM=1`. -Resulting libaries will be in the `src` directory for `WANT_LIBNH=1`. +Resulting libraries will be in the `targets/wasm` directory for `CROSS_TO_WASM=1`. +Resulting libraries will be in the `src` directory for `WANT_LIBNH=1`. [Original text:] -Resulting libaries will be in the `src` directory. +Resulting libraries will be in the `src` directory. WASM also has a npm module that can be published out of `sys/lib/npm-library`. After building the `nethack.js` it can be published by: 1. `cd sys/lib/npm-library` @@ -38,13 +38,13 @@ WASM also has a npm module that can be published out of `sys/lib/npm-library`. A ## API: libnethack.a The API is two functions: * `nhmain(int argc, char *argv[])` - The main function for NetHack that configures the program and runs the `moveloop()` until the game is over. The arguments to this function are the [command line arguments](https://nethackwiki.com/wiki/Options) to NetHack. -* `shim_graphics_set_callback(shim_callback_t cb)` - A single function that sets a callback to gather graphics events: write a string to screen, get user input, etc. Your job is to pass in a callback and handle all the requested rendering events to show NetHack on the scrren. The callback is `void shim_callback_t(const char *name, void *ret_ptr, const char *fmt, ...)` +* `shim_graphics_set_callback(shim_callback_t cb)` - A single function that sets a callback to gather graphics events: write a string to screen, get user input, etc. Your job is to pass in a callback and handle all the requested rendering events to show NetHack on the screen. The callback is `void shim_callback_t(const char *name, void *ret_ptr, const char *fmt, ...)` * `name` is the name of the [window function](https://github.com/NetHack/NetHack/blob/NetHack-3.7/doc/window.txt) that needs to be handled * `ret_ptr` is a pointer to a memory space for the return value. The type expected to be returned in this pointer is described by the first character of the `fmt` string. * `fmt` is a string that describes the signature of the callback. The first character in the string is the return type and any additional characters describe the variable arguments: `i` for integer, `s` for string, `p` for pointer, `c` for character, `v` for void. For example, if format is "vis" the callback will have no return (void), the first argument will be an integer, and the second argument will be a string. If format is "iii" the callback must return an integer, and both the arguments passed in will be integers. * [Variadic arguments](https://www.gnu.org/software/libc/manual/html_node/Variadic-Example.html): a variable number and type of arguments depending on the `window function` that is being called. The arguments associated with each `name` are described in the [NetHack window.txt](https://github.com/NetHack/NetHack/blob/NetHack-3.7/doc/window.txt). -Where is the header file for the API you ask? There isn't one. It's three functions, just drop the forward declarations at the top of your file (or create your own header). It's more work figuring out how to install and copy around header files than it's worth for such a small API. If you disagree, feel free to sumbit a PR to fix it. :) +Where is the header file for the API you ask? There isn't one. It's three functions, just drop the forward declarations at the top of your file (or create your own header). It's more work figuring out how to install and copy around header files than it's worth for such a small API. If you disagree, feel free to submit a PR to fix it. :) ## API: nethack.js The WebAssembly API has a similar signature to `libnethack.a` with minor syntactic differences: diff --git a/sys/libnh/libnhmain.c b/sys/libnh/libnhmain.c index 0a42175cb1..0bfb988b70 100644 --- a/sys/libnh/libnhmain.c +++ b/sys/libnh/libnhmain.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 libnhmain.c $NHDT-Date: 1596498297 2020/08/03 23:44:57 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.87 $ */ +/* 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 @@ -50,8 +50,8 @@ extern void init_linux_cons(void); #endif static void wd_message(void); -static boolean wiz_error_flag = FALSE; 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 */ @@ -68,7 +68,7 @@ nhmain(int argc, char *argv[]) #endif /* __EMSCRIPTEN__ */ { #ifdef CHDIR - register char *dir; + char *dir; #endif NHFILE *nhfp; boolean exact_username; @@ -79,10 +79,10 @@ nhmain(int argc, char *argv[]) // printf ("argv[%d]: %s\n", i, argv[i]); // } - early_init(); + early_init(argc, argv); gh.hname = argv[0]; - gh.hackpid = getpid(); + svh.hackpid = getpid(); (void) umask(0777 & ~FCMASK); choose_windows(DEFAULT_WINDOW_SYS); @@ -103,15 +103,14 @@ nhmain(int argc, char *argv[]) if (argcheck(argc, argv, ARG_VERSION) == 2) 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(); +#ifdef CRASHREPORT + if (argcheck(argc, argv, ARG_BIDSHOW)) exit(EXIT_SUCCESS); +#endif + + if (argcheck(argc, argv, ARG_SHOWPATHS) == 2) { + gd.deferred_showpaths = TRUE; + return; } if (argcheck(argc, argv, ARG_DEBUG) == 1) { argc--; @@ -170,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 @@ -189,7 +194,7 @@ nhmain(int argc, char *argv[]) * It seems you really want to play. */ u.uhp = 1; /* prevent RIP on early quits */ - gp.program_state.preserve_locks = 1; + program_state.preserve_locks = 1; #ifndef NO_SIGNAL sethanguphandler((SIG_RET_TYPE) hangup); #endif @@ -197,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 @@ -223,7 +223,7 @@ nhmain(int argc, char *argv[]) /* wizard mode access is deferred until here */ set_playmode(); /* sets plname to "wizard" for wizard mode */ /* hide any hyphens from plnamesuffix() */ - gp.plnamelen = exact_username ? (int) strlen(gp.plname) : 0; + gp.plnamelen = exact_username ? (int) strlen(svp.plname) : 0; /* strip role,race,&c suffix; calls askname() if plname[] is empty or holds a generic user name like "player" or "games" */ plnamesuffix(); @@ -266,12 +266,12 @@ nhmain(int argc, char *argv[]) * clock, &c not currently in use in the playground directory * (for gl.locknum > 0). */ - if (*gp.plname) { + if (*svp.plname) { getlock(); - gp.program_state.preserve_locks = 0; /* after getlock() */ + program_state.preserve_locks = 0; /* after getlock() */ } - if (*gp.plname && (nhfp = restore_saved_game()) != 0) { + if (*svp.plname && (nhfp = restore_saved_game()) != 0) { const char *fq_save = fqname(gs.SAVEF, SAVEPREFIX, 1); (void) chmod(fq_save, 0); /* disallow parallel restores */ @@ -291,7 +291,7 @@ nhmain(int argc, char *argv[]) wd_message(); if (discover || wizard) { /* this seems like a candidate for paranoid_confirmation... */ - if (yn("Do you want to keep the save file?") == 'n') { + if (y_n("Do you want to keep the save file?") == 'n') { (void) delete_savefile(); } else { (void) chmod(fq_save, FCMASK); /* back to readable */ @@ -302,7 +302,7 @@ nhmain(int argc, char *argv[]) } if (!resuming) { - boolean neednewlock = (!*gp.plname); + boolean neednewlock = (!*svp.plname); /* new game: start by choosing role, race, etc; player might change the hero's name while doing that, in which case we try to restore under the new name @@ -311,7 +311,7 @@ nhmain(int argc, char *argv[]) if (!plsel_once) player_selection(); plsel_once = TRUE; - if (neednewlock && *gp.plname) + if (neednewlock && *svp.plname) goto attempt_restore; if (iflags.renameinprogress) { /* player has renamed the hero while selecting role; @@ -377,12 +377,12 @@ process_options(int argc, char *argv[]) #endif case 'u': if (argv[0][2]) { - (void) strncpy(gp.plname, argv[0] + 2, sizeof gp.plname - 1); + (void) strncpy(svp.plname, argv[0] + 2, sizeof svp.plname - 1); gp.plnamelen = 0; /* plname[] might have -role-race attached */ } else if (argc > 1) { argc--; argv++; - (void) strncpy(gp.plname, argv[0], sizeof gp.plname - 1); + (void) strncpy(svp.plname, argv[0], sizeof svp.plname - 1); gp.plnamelen = 0; } else { raw_print("Player name expected after -u"); @@ -530,8 +530,8 @@ whoami(void) * Note that we trust the user here; it is possible to play under * somebody else's name. */ - if (!*gp.plname) { - register const char *s; + if (!*svp.plname) { + const char *s; s = nh_getenv("USER"); if (!s || !*s) @@ -540,8 +540,8 @@ whoami(void) s = getlogin(); if (s && *s) { - (void) strncpy(gp.plname, s, sizeof gp.plname - 1); - if (strchr(gp.plname, '-')) + (void) strncpy(svp.plname, s, sizeof svp.plname - 1); + if (strchr(svp.plname, '-')) return TRUE; } } @@ -592,28 +592,48 @@ port_help(void) boolean authorize_wizard_mode(void) { - struct passwd *pw = get_unix_pw(); - - if (pw && sysopt.wizards && sysopt.wizards[0]) { + if (sysopt.wizards && sysopt.wizards[0]) { if (check_user_string(sysopt.wizards)) return TRUE; } - wiz_error_flag = TRUE; /* not being allowed into wizard mode */ + iflags.wiz_error_flag = TRUE; /* not being allowed into wizard mode */ + return FALSE; +} + +/* similar to above, validate explore mode access */ +boolean +authorize_explore_mode(void) +{ +#ifdef SYSCF + if (sysopt.explorers && sysopt.explorers[0]) { + if (check_user_string(sysopt.explorers)) + return TRUE; + } + iflags.explore_error_flag = TRUE; /* not allowed into explore mode */ return FALSE; +#else + return TRUE; /* if sysconf disabled, no restrictions on explore mode */ +#endif } static void wd_message(void) { - if (wiz_error_flag) { + if (iflags.wiz_error_flag) { if (sysopt.wizards && sysopt.wizards[0]) { char *tmp = build_english_list(sysopt.wizards); pline("Only user%s %s may access debug (wizard) mode.", strchr(sysopt.wizards, ' ') ? "s" : "", tmp); free(tmp); - } else + } else { + You("cannot access debug (wizard) mode."); + } + wizard = FALSE; /* (paranoia) */ + if (!iflags.explore_error_flag) pline("Entering explore/discovery mode instead."); - wizard = 0, discover = 1; /* (paranoia) */ + } else if (iflags.explore_error_flag) { + You("cannot access explore mode."); /* same as enter_explore_mode */ + discover = iflags.deferred_X = FALSE; /* (more paranoia) */ } else if (discover) You("are in non-scoring explore/discovery mode."); } @@ -648,7 +668,7 @@ check_user_string(const char *optstr) if (optstr[0] == '*') return TRUE; /* allow any user */ if (sysopt.check_plname) - pwname = gp.plname; + pwname = svp.plname; else if ((pw = get_unix_pw()) != 0) pwname = pw->pw_name; if (!pwname || !*pwname) @@ -749,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 @@ -757,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() { @@ -780,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 */ @@ -792,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); } @@ -804,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`); @@ -817,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`); @@ -833,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: @@ -872,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 @@ -965,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 @@ -1011,12 +1076,126 @@ void js_constants_init() { SET_CONSTANT("COLORS", CLR_MAX); // color attributes (?) + SET_CONSTANT("COLOR_ATTR", HL_ATTCLR_BOLD); SET_CONSTANT("COLOR_ATTR", HL_ATTCLR_DIM); - SET_CONSTANT("COLOR_ATTR", HL_ATTCLR_BLINK); + SET_CONSTANT("COLOR_ATTR", HL_ATTCLR_ITALIC); SET_CONSTANT("COLOR_ATTR", HL_ATTCLR_ULINE); + SET_CONSTANT("COLOR_ATTR", HL_ATTCLR_BLINK); SET_CONSTANT("COLOR_ATTR", HL_ATTCLR_INVERSE); - SET_CONSTANT("COLOR_ATTR", HL_ATTCLR_BOLD); 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); } /*** @@ -1041,13 +1220,27 @@ void js_globals_init() { }); /* globals */ - CREATE_GLOBAL(gp.plname, "s"); + CREATE_GLOBAL(svp.plname, "s"); /* window globals */ CREATE_GLOBAL(WIN_MAP, "i"); 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 5a084c6fa3..10b94888d8 100644 --- a/sys/msdos/Install.dos +++ b/sys/msdos/Install.dos @@ -1,5 +1,5 @@ - Copyright (c) NetHack PC Development Team 1990-2021. + Copyright (c) NetHack PC Development Team 1990-2023. NetHack may be freely redistributed. See license for details. ============================================================== Instructions for compiling and installing @@ -42,7 +42,7 @@ II. There once was a time when people built NetHack right on their DOS machine. a DOS-extender (for including in msdos packaging) from: http://sandmann.dotster.com/cwsdpmi/csdpmi7b.zip and Lua from: - http://www.lua.org/ftp/lua-5.4.4.tar.gz + http://www.lua.org/ftp/lua-5.4.6.tar.gz If you want the DOSVGA build, to support higher resolutions and Unicode symbols, you also need: @@ -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 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. @@ -83,19 +83,17 @@ II. There once was a time when people built NetHack right on their DOS machine. The MSDOS cross-compile can then be carried out by specifying CROSS_TO_MSDOS=1 on the make command line: - make CROSS_TO_MSDOS=1 all make CROSS_TO_MSDOS=1 package You can explicitly include tty and curses support if desired. The default - you'll end up with is a tty-only cross-compile build: + you'll end up with if you don't specify any WANT_ command line options + is a tty-only cross-compile build: - make WANT_WIN_TTY=1 WANT_WIN_CURSES=1 CROSS_TO_MSDOS=1 all make WANT_WIN_TTY=1 WANT_WIN_CURSES=1 CROSS_TO_MSDOS=1 package Add WANT_DOSVGA for a curses build that supports higher resolutions, external fonts and Unicode symbols: - make WANT_WIN_TTY=1 WANT_WIN_CURSES=1 CROSS_TO_MSDOS=1 WANT_DOSVGA=1 all make WANT_WIN_TTY=1 WANT_WIN_CURSES=1 CROSS_TO_MSDOS=1 WANT_DOSVGA=1 package Result: The "make package" target will bundle all of the necessary @@ -118,6 +116,46 @@ Appendix A - Additional Notes 2. Play NetHack. If it works, you're done! +Appendix B - Common Difficulties Encountered + + i) The cross-compile stops with an error about "No rule to make target + '../lib/djgpp/djgpp-patch/src/libc/go32/exceptn.S'" + + For example: + make CROSS_TO_MSDOS=1 package + ( cd src ; make LUA_VERSION=5.4.6 nethack ) + make[1]: Entering directory '/home/johndoe/NHsource/src' + mkdir -p ../targets/msdos ; make ../targets/msdos/exceptn.o ; + make[2]: Entering directory '/home/johndoe/NHsource/src' + make[2]: *** No rule to make target + '../lib/djgpp/djgpp-patch/src/libc/go32/exceptn.S', + needed by '../targets/msdos/exceptn.o'. Stop. + make[2]: Leaving directory '/home/johndoe/NHsource/src' + make[1]: *** [Makefile:2027: pregame] Error 2 + make[1]: Leaving directory '/home/johndoe/NHsource/src' + make: *** [Makefile:1531: nethack] Error 2 + + That result usually indicates that you haven't successfully run + 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. + + 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 the flex shared library as follows: + + sudo apt install libfl2 + + Reading package lists... Done + Building dependency tree... Done + Reading state information... Done + The following NEW packages will be installed: + libfl2 + 0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. + Appendix D - Contacting the Development Team If you discover a bug and wish to report it, or if you have comments diff --git a/sys/msdos/Makefile.GCC b/sys/msdos/Makefile.GCC index 065d529677..9ed3b27a20 100644 --- a/sys/msdos/Makefile.GCC +++ b/sys/msdos/Makefile.GCC @@ -1,5 +1,5 @@ # NetHack 3.7 Makefile.GCC $NHDT-Date: 1596498268 2020/08/03 23:44:28 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.53 $ -# Copyright (c) NetHack PC Development Team 1996-2022. +# Copyright (c) NetHack PC Development Team 1996-2024. # PC NetHack 3.7 Makefile for djgpp V2 # # Gnu gcc compiler for msdos (djgpp) @@ -50,13 +50,13 @@ endif ifeq "$(LUA_VERSION)" "5.3.5" LUAVER=535 else -LUAVER=544 +LUAVER=546 endif #--------------------------------------------------------------- # Location of LUA # # Original source needs to be obtained from: -# http://www.lua.org/ftp/lua-5.4.4.tar.gz +# http://www.lua.org/ftp/lua-5.4.6.tar.gz # # This build assumes that the LUA sources are located # at the specified location. If they are actually elsewhere @@ -163,6 +163,9 @@ else DLBFLG = endif +# +HACKLIB = $(O)hacklib.a + TERMLIB = # Build NetHack suitable for blind players @@ -269,29 +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)cmd.o $(O)date.o $(O)dbridge.o -VOBJ03 = $(O)decl.o $(O)detect.o $(O)dig.o $(O)display.o $(O)do.o -VOBJ04 = $(O)do_name.o $(O)do_wear.o $(O)dog.o $(O)dogmove.o $(O)dokick.o -VOBJ05 = $(O)dothrow.o $(O)drawing.o $(O)dungeon.o $(O)eat.o $(O)end.o -VOBJ06 = $(O)engrave.o $(O)exper.o $(O)explode.o $(O)extralev.o $(O)files.o -VOBJ07 = $(O)fountain.o $(O)getline.o $(O)hack.o $(O)hacklib.o $(O)insight.o $(O)invent.o -VOBJ08 = $(O)isaac64.o $(O)lock.o $(O)mail.o $(O)main.o $(O)makemon.o -VOBJ09 = $(O)mcastu.o $(O)mdlib.o $(O)mhitm.o $(O)mhitu.o $(O)minion.o $(O)mkmap.o -VOBJ10 = $(O)mklev.o $(O)mkmaze.o $(O)mkobj.o $(O)mkroom.o $(O)mon.o -VOBJ11 = $(O)mondata.o $(O)monmove.o $(O)monst.o $(O)mplayer.o $(O)mthrowu.o -VOBJ12 = $(O)muse.o $(O)music.o $(O)o_init.o $(O)objects.o $(O)objnam.o -VOBJ13 = $(O)options.o $(O)pickup.o $(O)pline.o $(O)polyself.o $(O)potion.o -VOBJ14 = $(O)quest.o $(O)questpgr.o $(O)pager.o $(O)pray.o $(O)priest.o -VOBJ15 = $(O)read.o $(O)rect.o $(O)region.o $(O)restore.o $(O)rip.o -VOBJ16 = $(O)rnd.o $(O)role.o $(O)rumors.o $(O)save.o $(O)sfstruct.o -VOBJ17 = $(O)shk.o $(O)shknam.o $(O)sit.o $(O)sounds.o $(O)sp_lev.o -VOBJ18 = $(O)spell.o $(O)steal.o $(O)steed.o $(O)symbols.o $(O)sys.o -VOBJ19 = $(O)teleport.o $(O)termcap.o $(O)timeout.o $(O)topl.o $(O)topten.o -VOBJ20 = $(O)track.o $(O)trap.o $(O)u_init.o $(O)uhitm.o $(O)utf8map.o $(O)vault.o -VOBJ21 = $(O)vision.o $(O)weapon.o $(O)were.o $(O)wield.o $(O)windows.o -VOBJ22 = $(O)wintty.o $(O)wizard.o $(O)worm.o $(O)worn.o $(O)write.o -VOBJ23 = $(O)zap.o $(O)light.o $(O)dlb.o -VOBJ24 = $(REGEX) +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) SOBJ = $(O)msdos.o $(O)pcsys.o $(O)tty.o $(O)unix.o \ $(O)video.o $(O)vidtxt.o $(O)pckeys.o @@ -313,13 +318,13 @@ VOBJ = $(VOBJ01) $(VOBJ02) $(VOBJ03) $(VOBJ04) $(VOBJ05) \ $(VOBJ11) $(VOBJ12) $(VOBJ13) $(VOBJ14) $(VOBJ15) \ $(VOBJ16) $(VOBJ17) $(VOBJ18) $(VOBJ19) $(VOBJ20) \ $(VOBJ21) $(VOBJ22) $(VOBJ23) $(VOBJ24) $(VOBJ25) \ - $(CURSESOBJ) $(LUAOBJ) + $(VOBJ26) $(CURSESOBJ) $(LUAOBJ) ALLOBJ = $(VOBJ) $(SOBJ) $(TILOBJ) $(TILOBJ2) $(VVOBJ) #===============-================================================= # LUA library -# Source from http://www.lua.org/ftp/lua-5.4.4.tar.gz +# Source from http://www.lua.org/ftp/lua-5.4.6.tar.gz #================================================================= LUASRC = $(LUATOP)/src @@ -441,7 +446,7 @@ endif #========================================== # ifeq "$(ADD_CURSES)" "Y" -CURSESDEF=-D"CURSES_GRAPHICS" -D"CURSES_BRIEF_INCLUDE" +CURSESDEF=-D"CURSES_GRAPHICS" -D"CURSES_BRIEF_INCLUDE" -DPDC_RGB ifdef WANT_DOSVGA CURSESDEF += -DCURSES_UNICODE -DPDC_WIDE endif @@ -453,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 = #========================================== @@ -533,13 +531,13 @@ $(OBJ)/%.o : $(WCURSES)/%.c #========================================== $(OBJ)/%.o : $(PDCURSES_TOP)/%.c - $(CC) $(cflags) -o$@ $< + $(CC) $(cflags) -o$@ $< $(OBJ)/%.o : $(PDCSRC)/%.c - $(CC) $(cflags) -o$@ $< + $(CC) $(cflags) -o$@ $< $(OBJ)/%.o : $(PDCDOS)/%.c - $(CC) $(cflags) -o$@ $< + $(CC) $(cflags) -o$@ $< #========================================== # Rules for LUA files @@ -609,7 +607,7 @@ endif #========================================== $(GAMEFILE): $(O)obj.tag $(PATCHLEV_H) $(PDCLIB) $(LUATARGETS) \ - $(O)utility.tag $(ALLOBJ) + $(O)utility.tag $(ALLOBJ) $(O)hacklib.a @if exist temp.a del temp.a @ar ruS temp.a $(VOBJ01) @ar ruS temp.a $(VOBJ02) @@ -636,6 +634,7 @@ $(GAMEFILE): $(O)obj.tag $(PATCHLEV_H) $(PDCLIB) $(LUATARGETS) \ @ar ruS temp.a $(VOBJ23) @ar ruS temp.a $(VOBJ24) @ar ruS temp.a $(VOBJ25) + @ar ruS temp.a $(VOBJ26) @ar ruS temp.a $(SOBJ) @ar ruS temp.a $(TILOBJ) @ar ruS temp.a $(TILOBJ2) @@ -674,18 +673,30 @@ $(INCL)/pm.h: $(U)makedefs.exe # Makedefs Stuff #========================================== -$(U)makedefs.exe: $(MAKEDEFSOBJS) - $(LINK) $(LFLAGS) -o$@ $(MAKEDEFSOBJS) +$(U)makedefs.exe: $(MAKEDEFSOBJS) $(HACKLIB) + $(LINK) $(LFLAGS) -o$@ $(MAKEDEFSOBJS) $(HACKLIB) $(O)makedefs.o: $(CONFIG_H) $(PERMONST_H) $(INCL)/objclass.h \ $(INCL)/sym.h $(INCL)/defsym.h $(U)makedefs.c +#========================================== +# hacklib.a static library +#========================================== + +$(O)hacklib.a: $(O)hacklibu.o + @ar rcs $@ $(O)hacklibu.o +# @ranlib $@ + +$(O)hacklibu.o: $(CONFIG_H) $(SRC)/hacklib.c + $(CC) $(cflags) -I$(SRC) -I$(MSYS) \ + -o$@ $(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 @@ -707,8 +718,8 @@ $(SRC)/tile.c: $(U)tilemap.exe TILEMAPOBJS = $(O)tilemap.o $(O)monst.o $(O)objects.o $(O)drawing.o -$(U)tilemap.exe: $(TILEMAPOBJS) - $(LINK) $(LFLAGS) -o$@ $(TILEMAPOBJS) +$(U)tilemap.exe: $(TILEMAPOBJS) $(HACKLIB) + $(LINK) $(LFLAGS) -o$@ $(TILEMAPOBJS) $(HACKLIB) $(O)tilemap.o: $(WSHR)/tilemap.c $(HACK_H) $(TILE_H) $(CC) $(cflags) -I$(WSHR) -I$(MSYS) -o$@ $(WSHR)/tilemap.c @@ -726,23 +737,23 @@ $(DAT)/nhtiles.bmp: $(TILEFILES) $(U)tile2bmp.exe @$(subst /,\,$(U)tile2bmp.exe $@) @$(subst /,\,chdir $(SRC)) -$(U)tile2bmp.exe: $(O)tile2bmp.o $(TEXTIO) +$(U)tile2bmp.exe: $(O)tile2bmp.o $(TEXTIO) $(HACKLIB) -rm -f temp.a @ar ru temp.a $(TEXTIO) - $(LINK) $(LFLAGS) -o$@ $(O)tile2bmp.o temp.a + $(LINK) $(LFLAGS) -o$@ $(O)tile2bmp.o temp.a $(HACKLIB) -$(U)tile2bin.exe: $(O)tile2bin.o $(TEXTIO) +$(U)tile2bin.exe: $(O)tile2bin.o $(TEXTIO) $(HACKLIB) -rm -f temp.a @ar ru temp.a $(TEXTIO) - $(LINK) $(LFLAGS) -o$@ $(O)tile2bin.o temp.a + $(LINK) $(LFLAGS) -o$@ $(O)tile2bin.o temp.a $(HACKLIB) -$(U)til2bin2.exe: $(O)til2bin2.o $(TEXTIO2) +$(U)til2bin2.exe: $(O)til2bin2.o $(TEXTIO2) $(HACKLIB) -rm -f temp.a @ar ru temp.a $(TEXTIO2) - $(LINK) $(LFLAGS) -o$@ $(O)til2bin2.o temp.a + $(LINK) $(LFLAGS) -o$@ $(O)til2bin2.o temp.a $(HACKLIB) -$(U)thintile.exe: $(O)thintile.o - $(LINK) $(LFLAGS) -o$@ $(O)thintile.o +$(U)thintile.exe: $(O)thintile.o $(HACKLIB) + $(LINK) $(LFLAGS) -o$@ $(O)thintile.o $(HACKLIB) $(O)thintile.o: $(HACK_H) $(WSHR)/tile.h $(WSHR)/thintile.c $(CC) $(cflags) -o$@ $(WSHR)/thintile.c @@ -895,8 +906,8 @@ $(DAT)/nhdat: $(U)dlb_main.exe $(DAT)/data $(DAT)/rumors \ @$(subst /,\,$(U)dlb_main cvIf dlb.lst nhdat) @$(subst /,\,cd $(SRC)) -$(U)dlb_main.exe: $(DLBOBJS) - $(LINK) $(LFLAGS) -o$@ $(DLBOBJS) +$(U)dlb_main.exe: $(DLBOBJS) $(HACKLIB) + $(LINK) $(LFLAGS) -o$@ $(DLBOBJS) $(HACKLIB) $(O)dlb_main.o: $(U)dlb_main.c $(INCL)/config.h $(DLB_H) $(CC) $(cflags) -o$@ $(U)dlb_main.c @@ -981,6 +992,7 @@ endif $(subst /,\,if exist $(WSHR)/monthin.txt del $(WSHR)/monthin.txt) $(subst /,\,if exist $(WSHR)/objthin.txt del $(WSHR)/objthin.txt) $(subst /,\,if exist $(WSHR)/oththin.txt del $(WSHR)/oththin.txt) + $(subst /,\,if exist $(O)hacklib.a del $(O)hacklib.a) #========================================== # Create directory for holding object files diff --git a/sys/msdos/fetch-cross-compiler.sh b/sys/msdos/fetch-cross-compiler.sh index 1bf846e652..36558a00c7 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 @@ -11,11 +11,11 @@ else fi if [ -z "$GCCVER" ]; then - export GCCVER=gcc1210 + export GCCVER=gcc1220 fi if [ -z "$LUA_VERSION" ]; then - export LUA_VERSION=5.4.4 + export LUA_VERSION=5.4.6 fi if [ ! -d "$(pwd)/lib" ]; then @@ -27,7 +27,8 @@ fi #DJGPP_URL="https://github.com/andrewwutw/build-djgpp/releases/download/v3.0/" #DJGPP_URL="https://github.com/andrewwutw/build-djgpp/releases/download/v3.1/" #DJGPP_URL="https://github.com/andrewwutw/build-djgpp/releases/download/v3.1/" -DJGPP_URL="https://github.com/andrewwutw/build-djgpp/releases/download/v3.3/" +#DJGPP_URL="https://github.com/andrewwutw/build-djgpp/releases/download/v3.3/" +DJGPP_URL="https://github.com/andrewwutw/build-djgpp/releases/download/v3.4/" if [ "$(uname)" = "Darwin" ]; then #Mac @@ -59,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 @@ -68,17 +70,22 @@ 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 else - wget --quiet --no-hsts http://sandmann.dotster.com/cwsdpmi/csdpmi7b.zip + wget --quiet --no-hsts https://www.delorie.com/pub/djgpp/current/v2misc/csdpmi7b.zip + if [ $? -ne 0 ]; then + wget --quiet --no-hsts --timeout=20 http://sandmann.dotster.com/cwsdpmi/csdpmi7b.zip + fi fi cd djgpp mkdir -p cwsdpmi @@ -90,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 @@ -128,9 +135,86 @@ 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 +# create a directories to hold some native DOS pieces that might get deployed +if [ ! -d djgpp/target ]; then + cd djgpp + 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 + + FONT_VERSION="4.49" FONT_FILE="terminus-font-$FONT_VERSION" FONT_LFILE="$FONT_FILE.1" @@ -148,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 735e84dd9a..8a059008d6 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/pckeys.c b/sys/msdos/pckeys.c index 8376597ef7..e711fc42da 100644 --- a/sys/msdos/pckeys.c +++ b/sys/msdos/pckeys.c @@ -36,7 +36,7 @@ pckeys(unsigned char scancode, unsigned char shift) { boolean opening_dialog; - opening_dialog = gp.pl_character[0] ? FALSE : TRUE; + opening_dialog = svp.pl_character[0] ? FALSE : TRUE; switch (scancode) { #ifdef SIMULATE_CURSOR case 0x3d: /* F3 = toggle cursor type */ diff --git a/sys/msdos/pcvideo.h b/sys/msdos/pcvideo.h index dfd9fc1c22..1d889a8b25 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 1fa622d119..0e90935fc0 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 2fa7f87332..916a3518d8 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__ @@ -111,7 +119,7 @@ typedef long clock_t; #include /* needed for delay() */ #endif -#ifdef SCREEN_DJGPPFAST /* parts of this block may be unecessary now */ +#ifdef SCREEN_DJGPPFAST /* parts of this block may be unnecessary now */ #define get_cursor(x, y) ScreenGetCursor(y, x) #endif @@ -128,16 +136,70 @@ int savevmode; /* store the original video mode in here */ int curcol, currow; /* graphics mode current cursor locations */ int g_attribute; /* Current attribute to use */ int monoflag; /* 0 = not monochrome, else monochrome */ +int inversed; int attrib_text_normal; /* text mode normal attribute */ int attrib_gr_normal; /* graphics mode normal attribute */ int attrib_text_intense; /* text mode intense attribute */ int attrib_gr_intense; /* graphics mode intense attribute */ uint32 curframecolor = NO_COLOR; /* current background text color */ -boolean traditional = FALSE; /* traditonal TTY character mode */ +boolean traditional = FALSE; /* traditional TTY character mode */ boolean inmap = FALSE; /* in the map window */ -#ifdef TEXTCOLOR char ttycolors[CLR_MAX]; /* also used/set in options.c */ -#endif /* TEXTCOLOR */ + +/* 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) @@ -232,11 +294,7 @@ int has_color(int color) { ++color; /* prevents compiler warning (unref. param) */ -#ifdef TEXTCOLOR return (monoflag) ? 0 : 1; -#else - return 0; -#endif } #endif @@ -296,10 +354,13 @@ void term_end_attr(int attr) { switch (attr) { + case ATR_INVERSE: + inversed = 0; + FALLTHROUGH; + /*FALLTHRU*/ case ATR_ULINE: case ATR_BOLD: case ATR_BLINK: - case ATR_INVERSE: default: g_attribute = iflags.grmode ? attrib_gr_normal : attrib_text_normal; } @@ -343,13 +404,9 @@ term_start_attr(int attr) } break; case ATR_INVERSE: - if (monoflag) { - g_attribute = ATTRIB_MONO_REVERSE; - } else { - g_attribute = - iflags.grmode ? attrib_gr_intense : attrib_text_intense; - } - break; + inversed = 1; + FALLTHROUGH; + /*FALLTHRU*/ default: g_attribute = iflags.grmode ? attrib_gr_normal : attrib_text_normal; break; @@ -359,33 +416,42 @@ term_start_attr(int attr) void term_start_color(int color) { -#ifdef TEXTCOLOR if (monoflag) { g_attribute = attrib_text_normal; } else { - if (color >= 0 && color < CLR_MAX) { + if (color == NO_COLOR) { /* 3.7 behave like term_end_color() */ + g_attribute = iflags.grmode ? attrib_gr_normal : attrib_text_normal; + curframecolor = NO_COLOR; + } else if (color >= 0 && color < CLR_MAX) { if (iflags.grmode) g_attribute = color; else g_attribute = ttycolors[color]; } } -#endif } void term_start_bgcolor(int bgcolor) { // pline("before bgcolor = %d, curframecolor = %d", bgcolor, curframecolor); -#ifdef TEXTCOLOR if (!monoflag) { if (bgcolor >= 0 && bgcolor < CLR_MAX) - curframecolor = bgcolor; + curframecolor = bgcolor; } -#endif // pline("after bgcolor = %d, curframecolor = %d", bgcolor, curframecolor); } +void +term_start_extracolor(uint32 nhcolor UNUSED, uint16 color256idx UNUSED) +{ +} + +void +term_end_extracolor(void) +{ +} + void term_start_raw_bold(void) { @@ -405,7 +471,7 @@ tty_delay_output(void) } void -tty_end_screen(void) +term_end_screen(void) { if (!iflags.grmode) { txt_clear_screen(); @@ -414,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 } } @@ -436,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 */ @@ -450,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); @@ -468,19 +534,19 @@ tty_startup(int *wid, int *hgt) setclipped(); #endif -#ifdef TEXTCOLOR init_ttycolor(); -#endif #ifdef MONO_CHECK monoflag = txt_monoadapt_check(); #else monoflag = 0; #endif + + inversed = 0; } void -tty_start_screen(void) +term_start_screen(void) { #ifdef PC9800 fputs("\033[>1h", stdout); @@ -492,7 +558,7 @@ tty_start_screen(void) void gr_init(void) { - windowprocs.wincap2 &= ~WC2_U_24BITCOLOR; + windowprocs.wincap2 &= ~WC2_EXTRACOLORS; #ifdef SCREEN_VGA if (iflags.usevga) { vga_Init(); @@ -600,6 +666,9 @@ xputc(int ch) /* write out character (and attribute) */ } if (!iflags.grmode) { + if (inversed) { + attribute = (g_attribute % 8) << 4; + } txt_xputc(ch, attribute); #ifdef SCREEN_VGA } else if (iflags.usevga) { @@ -701,7 +770,6 @@ HideCursor(void) #endif /* SIMULATE_CURSOR */ -#ifdef TEXTCOLOR /* * CLR_BLACK 0 * CLR_RED 1 @@ -925,7 +993,6 @@ convert_uchars(char *bufp, /* current pointer */ } #endif /* VIDEOSHADES */ -#endif /* TEXTCOLOR */ /* * Process defaults.nh OPTIONS=video:xxxx diff --git a/sys/msdos/vidtxt.c b/sys/msdos/vidtxt.c index b89d3616c7..680f23e3c3 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 4ba0bb3d55..f9c1346bb2 100644 --- a/sys/msdos/vidvesa.c +++ b/sys/msdos/vidvesa.c @@ -24,6 +24,7 @@ extern int total_tiles_used, Tile_corr, Tile_unexplored; /* from tile.c */ struct VesaCharacter { uint32 colour, bgcolour; uint32 chr; + char inverse; }; static unsigned long vesa_SetWindow(int window, unsigned long offset); @@ -69,6 +70,7 @@ extern int clipx, clipxmax; /* current clipping column from wintty.c */ extern int clipy, clipymax; /* current clipping row from wintty.c */ extern int curcol, currow; /* current column and row */ extern int g_attribute; +extern int inversed; extern int attrib_text_normal; /* text mode normal attribute */ extern int attrib_gr_normal; /* graphics mode normal attribute */ extern int attrib_gr_intense; /* graphics mode intense attribute */ @@ -87,6 +89,7 @@ static struct map_struct { unsigned special; short int tileidx; int framecolor; + int inverse; } map[ROWNO][COLNO]; /* track the glyphs */ #define vesa_clearmap() \ @@ -96,10 +99,11 @@ static struct map_struct { for (x = 0; x < COLNO; ++x) { \ map[y][x].glyph = GLYPH_UNEXPLORED; \ map[y][x].ch = glyph2ttychar(GLYPH_UNEXPLORED); \ - map[y][x].framecolor = NO_COLOR; \ + map[y][x].framecolor = NO_COLOR; \ map[y][x].attr = 0; \ map[y][x].special = 0; \ map[y][x].tileidx = Tile_unexplored; \ + map[y][x].inverse = 0; \ } \ } #define TOP_MAP_ROW 1 @@ -592,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 */ @@ -684,13 +688,13 @@ vesa_xputg(const glyph_info *glyphinfo, const glyph_info *bkglyphinfo UNUSED) #ifdef ENHANCED_SYMBOLS if (SYMHANDLING(H_UTF8) && glyphinfo->gm.u && glyphinfo->gm.u->utf8str) { ch = glyphinfo->gm.u->utf32ch; - if (vesa_pixel_size > 8 && glyphinfo->gm.u->ucolor != 0) { - /* FIXME: won't display black (0,0,0) correctly, but the background - is usually black anyway */ - attr = glyphinfo->gm.u->ucolor | 0x80000000; - } } #endif + if (vesa_pixel_size > 8 && glyphinfo->gm.customcolor != 0) { + /* FIXME: won't display black (0,0,0) correctly, but the background + is usually black anyway */ + attr = glyphinfo->gm.customcolor | 0x80000000; + } row = currow; col = curcol; @@ -703,6 +707,7 @@ vesa_xputg(const glyph_info *glyphinfo, const glyph_info *bkglyphinfo UNUSED) map[ry][col].special = special; map[ry][col].tileidx = glyphinfo->gm.tileidx; map[ry][col].attr = attr; + map[ry][col].inverse = inversed; if (bkglyphinfo->framecolor != NO_COLOR) { map[ry][col].framecolor = bkglyphinfo->framecolor; @@ -718,6 +723,16 @@ vesa_xputg(const glyph_info *glyphinfo, const glyph_info *bkglyphinfo UNUSED) decal_packed(packcell, special); #endif vesa_DisplayCell(glyphinfo->gm.tileidx, col - clipx, ry - clipy); + if (bkglyphinfo->framecolor != NO_COLOR) { + int curtypbak = cursor_type; + int cclr = cursor_color; + + cursor_type = CURSOR_FRAME; + cursor_color = bkglyphinfo->framecolor; + vesa_DrawCursor(); + cursor_type = curtypbak; + cursor_color = cclr; + } } } if (col < (CO - 1)) @@ -779,7 +794,7 @@ vesa_cliparound(int x, int y) clipymax = ROWNO - 1; } if (clipx != oldx || clipy != oldy) { - if (on_level(&u.uz0, &u.uz) && !gp.program_state.restoring) + if (on_level(&u.uz0, &u.uz) && !program_state.restoring) /* (void) doredraw(); */ vesa_redrawmap(); } @@ -808,6 +823,8 @@ vesa_redrawmap(void) for (cx = clipx; cx <= (unsigned) clipxmax && cx < COLNO; ++cx) { t_row[cx].chr = map[cy][cx].ch; t_row[cx].colour = map[cy][cx].attr; + t_row[cx].bgcolour = BACKGROUND_VESA_COLOR; + t_row[cx].inverse = map[cy][cx].inverse; } vesa_WriteTextRow(0, y, t_row + clipx, cx - clipx); x = (cx - clipx) * vesa_char_width; @@ -1085,11 +1102,9 @@ vesa_Init(void) vesa_SwitchMode(vesa_mode); vesa_SetViewPort(); windowprocs.win_cliparound = vesa_cliparound; -#ifdef ENHANCED_SYMBOLS if (vesa_pixel_size > 8) { - windowprocs.wincap2 |= WC2_U_24BITCOLOR; + windowprocs.wincap2 |= WC2_EXTRACOLORS; } -#endif #ifdef TILES_IN_GLYPHMAP paletteptr = get_palette(); iflags.tile_view = TRUE; @@ -1323,7 +1338,7 @@ vesa_FontPtrs(void) } /* - * This will verify the existance of a VGA adapter on the machine. + * This will verify the existence of a VGA adapter on the machine. * Video function call 0x4F00 returns 0x004F in AX if successful, and * returns a VbeInfoBlock describing the features of the VESA BIOS. */ @@ -1603,13 +1618,15 @@ vesa_WriteCharXY(uint32 chr, int pixx, int pixy, uint32 colour) } chr_cache_lastx = pixx; chr_cache[chr_cache_size].chr = chr; + chr_cache[chr_cache_size].inverse = inversed; chr_cache[chr_cache_size].colour = colour; + chr_cache[chr_cache_size].bgcolour = BACKGROUND_VESA_COLOR; ++chr_cache_size; } /* * Draw a character with a transparent background - * Don't bother cacheing; only the position bar and the cursor use this + * Don't bother caching; only the position bar and the cursor use this */ static void vesa_WriteCharTransparent(int chr, int pixx, int pixy, int colour) @@ -1693,6 +1710,15 @@ vesa_WriteTextRow(int pixx, int pixy, struct VesaCharacter const *t_row, fg[2] = (pix >> 16) & 0xFF; fg[3] = (pix >> 24) & 0xFF; } + + if (t_row[i].inverse) { + unsigned char tmpx[4]; + tmpx[0] = bg[0]; bg[0] = fg[0]; fg[0] = tmpx[0]; + tmpx[1] = bg[1]; bg[1] = fg[1]; fg[1] = tmpx[1]; + tmpx[2] = bg[2]; bg[2] = fg[2]; fg[2] = tmpx[2]; + tmpx[3] = bg[3]; bg[3] = fg[3]; fg[3] = tmpx[3]; + } + /* Third loop: draw eight pixels */ for (px = 0; px < (int) vesa_char_width; px += 8) { /* Fourth loop: draw one pixel */ @@ -2016,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) @@ -2213,14 +2249,14 @@ vesa_DrawCursor(void) default: for (x = left; x <= right; ++x) { - vesa_WritePixel(x, top, FIRST_TEXT_COLOR + 15); + vesa_WritePixel(x, top, FIRST_TEXT_COLOR + cursor_color); } for (y = top + 1; y <= bottom - 1; ++y) { - vesa_WritePixel(left , y, FIRST_TEXT_COLOR + 15); - vesa_WritePixel(right, y, FIRST_TEXT_COLOR + 15); + vesa_WritePixel(left , y, FIRST_TEXT_COLOR + cursor_color); + vesa_WritePixel(right, y, FIRST_TEXT_COLOR + cursor_color); } for (x = left; x <= right; ++x) { - vesa_WritePixel(x, bottom, FIRST_TEXT_COLOR + 15); + vesa_WritePixel(x, bottom, FIRST_TEXT_COLOR + cursor_color); } break; } diff --git a/sys/msdos/vidvga.c b/sys/msdos/vidvga.c index ed7eb186d0..3e3a79281a 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__ @@ -138,6 +137,7 @@ extern boolean clipping; /* clipping on? from wintty.c */ extern int savevmode; /* store the original video mode */ extern int curcol, currow; /* current column and row */ extern int g_attribute; +extern int inversed; extern int attrib_text_normal; /* text mode normal attribute */ extern int attrib_gr_normal; /* graphics mode normal attribute */ extern int attrib_text_intense; /* text mode intense attribute */ @@ -290,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 */ @@ -414,6 +414,16 @@ vga_xputg(const glyph_info *glyphinfo, if (map[ry][col].special) decal_planar(&planecell, special); vga_DisplayCell(&planecell, col - clipx, row); + if (bkglyphinfo->framecolor != NO_COLOR) { + int curtypbak = cursor_type; + int cclr = cursor_color; + + cursor_type = CURSOR_FRAME; + cursor_color = bkglyphinfo->framecolor; + vga_DrawCursor(); + cursor_type = curtypbak; + cursor_color = cclr; + } } } else { read_planar_tile_O(glyphnum, &planecell_O); @@ -461,7 +471,7 @@ vga_cliparound(int x, int y UNUSED) clipx = clipxmax - (viewport_size - 1); } if (clipx != oldx) { - if (on_level(&u.uz0, &u.uz) && !gp.program_state.restoring) + if (on_level(&u.uz0, &u.uz) && !program_state.restoring) /* (void) doredraw(); */ vga_redrawmap(1); } @@ -906,7 +916,7 @@ vga_FontPtrs(void) } /* - * This will verify the existance of a VGA adapter on the machine. + * This will verify the existence of a VGA adapter on the machine. * Video function call 0x1a returns 0x1a in AL if successful, and * returns the following values in BL for the active display: * @@ -965,6 +975,12 @@ vga_WriteChar(uint32 chr, int col, int row, int colour) else bgcolor = BACKGROUND_VGA_COLOR; + if (inversed) { + int tmpc = actual_colour; + actual_colour = bgcolor; + bgcolor = tmpc; + } + x = min(col, (CO - 1)); /* min() used protection from callers */ pixy = min(row, (LI - 1)) * 16; /* assumes 8 x 16 char set */ vplane = ~actual_colour & ~bgcolor & 0xF; @@ -1143,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/NetHack.cnf b/sys/share/NetHack.cnf index a7f92d69fb..4e41ceab93 100644 --- a/sys/share/NetHack.cnf +++ b/sys/share/NetHack.cnf @@ -128,4 +128,17 @@ OPTIONS=time,noshowexp,number_pad:2,lit_corridor #MENUCOLOR=" holy "=green #MENUCOLOR=" cursed "=red #MENUCOLOR=" unholy "=red -#MENUCOLOR=" cursed .* (being worn)"=orange&underline +#MENUCOLOR=" cursed .* \(being worn\)"=orange&underline + +# statusbar highlighting +#OPTIONS=hitpointbar +#OPTIONS=hilite_status:hitpoints/100%/gray&normal +#OPTIONS=hilite_status:hitpoints/<100%/green&normal +#OPTIONS=hilite_status:hitpoints/<66%/yellow&normal +#OPTIONS=hilite_status:hitpoints/<50%/orange&normal +#OPTIONS=hilite_status:hitpoints/<33%/red&bold +#OPTIONS=hilite_status:hitpoints/<15%/red&inverse +#OPTIONS=hilite_status:power-max/up/green +#OPTIONS=hilite_status:power-max/down/red +#OPTIONS=hilite_status:armor-class/down/green +#OPTIONS=hilite_status:armor-class/up/red diff --git a/sys/share/cppregex.cpp b/sys/share/cppregex.cpp index eeec30b980..9835a1f5ca 100644 --- a/sys/share/cppregex.cpp +++ b/sys/share/cppregex.cpp @@ -3,15 +3,16 @@ /* Copyright (c) Sean Hunt 2015. */ /* NetHack may be freely redistributed. See license for details. */ +#include +#include +#include + extern "C" { #include "config.h" #define CPPREGEX_C -#include "extern.h" +#include "nhregex.h" } // extern "C" -#include -#include -#include extern "C" { // rest of file diff --git a/sys/share/pcmain.c b/sys/share/pcmain.c index 90610ed4b6..af642d696b 100644 --- a/sys/share/pcmain.c +++ b/sys/share/pcmain.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 pcmain.c $NHDT-Date: 1596498282 2020/08/03 23:44:42 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.121 $ */ +/* NetHack 3.7 pcmain.c $NHDT-Date: 1693359605 2023/08/30 01:40:05 $ $NHDT-Branch: keni-crashweb2 $:$NHDT-Revision: 1.133 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -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 */ @@ -76,7 +68,7 @@ mingw_main(int argc, char *argv[]) { boolean resuming; - early_init(); + early_init(argc, argv); resuming = pcmain(argc, argv); moveloop(resuming); nethack_exit(EXIT_SUCCESS); @@ -88,7 +80,7 @@ boolean pcmain(int argc, char *argv[]) { NHFILE *nhfp; - register char *dir; + char *dir; #if defined(MSDOS) char *envp = NULL; char *sptr = NULL; @@ -209,7 +201,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ } } - /* okay so we have the overriding and definitive locaton + /* okay so we have the overriding and definitive location for sysconf, but only in the event that there is not a sysconf file there (for whatever reason), check a secondary location rather than abort. */ @@ -279,7 +271,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ } #endif -#if defined(TOS) && defined(TEXTCOLOR) +#if defined(TOS) if (iflags.BIOS && iflags.use_color) set_colors(); #endif @@ -411,19 +403,19 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ * overwritten without confirmation when a user starts up * another game with the same player name. */ - Strcpy(gl.lock, gp.plname); + Strcpy(gl.lock, svp.plname); regularize(gl.lock); getlock(); #else /* What follows is !PC_LOCKING */ #ifdef AMIGA /* We'll put the bones & levels in the user specified directory \ -jhsa */ - Strcat(gl.lock, gp.plname); + Strcat(gl.lock, svp.plname); Strcat(gl.lock, ".99"); #else /* I'm not sure what, if anything, is left here, but old MFLOPPY had * conflicts with set_lock_and_bones() in files.c. */ - Strcpy(gl.lock, gp.plname); + Strcpy(gl.lock, svp.plname); Strcat(gl.lock, ".99"); regularize(gl.lock); /* is this necessary? */ /* not compatible with full path a la AMIGA */ @@ -436,9 +428,9 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ if (!nhfp) { raw_print("Cannot create lock file"); } else { - gh.hackpid = 1; + svh.hackpid = 1; if (nhfp->structlevel) - write(nhfp->fd, (genericptr_t) &gh.hackpid, sizeof(gh.hackpid)); + write(nhfp->fd, (genericptr_t) &svh.hackpid, sizeof(svh.hackpid)); close_nhfile(nhfp); } @@ -549,11 +541,11 @@ process_options(int argc, char *argv[]) #endif case 'u': if (argv[0][2]) - (void) strncpy(gp.plname, argv[0] + 2, sizeof(gp.plname) - 1); + (void) strncpy(svp.plname, argv[0] + 2, sizeof(svp.plname) - 1); else if (argc > 1) { argc--; argv++; - (void) strncpy(gp.plname, argv[0], sizeof(gp.plname) - 1); + (void) strncpy(svp.plname, argv[0], sizeof(svp.plname) - 1); } else raw_print("Player name expected after -u"); break; @@ -714,11 +706,18 @@ port_help(void) boolean authorize_wizard_mode(void) { - if (!strcmp(gp.plname, WIZARD_NAME)) + if (!strcmp(svp.plname, WIZARD_NAME)) return TRUE; return FALSE; } +/* similar to above, validate explore mode access */ +boolean +authorize_explore_mode(void) +{ + return TRUE; /* no restrictions on explore mode */ +} + #ifdef EXEPATH #ifdef __DJGPP__ #define PATH_SEPARATOR '/' diff --git a/sys/share/pcsys.c b/sys/share/pcsys.c index 482133ee2f..07cab8e42a 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 @@ -273,12 +267,11 @@ msexit(void) #ifdef TOS if (run_from_desktop) getreturn("to continue"); /* so the user can read the score list */ -#ifdef TEXTCOLOR if (colors_changed) restore_colors(); -#endif #endif wait_synch(); + term_curs_set(1); return; } #endif /* MICRO || OS2 */ diff --git a/sys/share/pctty.c b/sys/share/pctty.c index 6b718d66a5..fee616980a 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/pcunix.c b/sys/share/pcunix.c index 68c6fe0513..f95a26a79e 100644 --- a/sys/share/pcunix.c +++ b/sys/share/pcunix.c @@ -34,50 +34,12 @@ static struct stat hbuf; static int eraseoldlocks(void); #endif -#if 0 -int -uptodate(int fd) -{ -#ifdef WANT_GETHDATE - if(fstat(fd, &buf)) { - pline("Cannot get status of saved level? "); - return(0); - } - if(buf.st_mtime < hbuf.st_mtime) { - pline("Saved level is out of date. "); - return(0); - } -#else -#if (defined(MICRO)) && !defined(NO_FSTAT) - if(fstat(fd, &buf)) { - if(gm.moves > 1) pline("Cannot get status of saved level? "); - else pline("Cannot get status of saved game."); - return(0); - } - if(comp_times(buf.st_mtime)) { - if(gm.moves > 1) pline("Saved level is out of date."); - else pline("Saved game is out of date. "); - /* This problem occurs enough times we need to give the player - * some more information about what causes it, and how to fix. - */ -#ifdef MSDOS - pline("Make sure that your system's date and time are correct."); - pline("They must be more current than NetHack.EXE's date/time stamp."); -#endif /* MSDOS */ - return(0); - } -#endif /* MICRO */ -#endif /* WANT_GETHDATE */ - return(1); -} -#endif - #if defined(PC_LOCKING) #if !defined(SELF_RECOVER) static int eraseoldlocks(void) { - register int i; + int i; /* cannot use maxledgerno() here, because we need to find a lock name * before starting everything (including the dungeon initialization @@ -101,7 +63,7 @@ eraseoldlocks(void) void getlock(void) { - register int fd, c, ci, ct; + int fd, c, ci, ct; int fcmask = FCMASK; char tbuf[BUFSZ]; const char *fq_lock; @@ -217,8 +179,8 @@ getlock(void) #endif error("cannot creat file (%s.)", fq_lock); } else { - if (write(fd, (char *) &gh.hackpid, sizeof(gh.hackpid)) - != sizeof(gh.hackpid)) { + if (write(fd, (char *) &svh.hackpid, sizeof(svh.hackpid)) + != sizeof(svh.hackpid)) { #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) chdirx(orgdir, 0); #endif @@ -239,13 +201,13 @@ getlock(void) #endif /* PC_LOCKING */ void -regularize(register char *s) +regularize(char *s) /* * normalize file name - we don't like .'s, /'s, spaces, and * lots of other things */ { - register char *lp; + char *lp; for (lp = s; *lp; lp++) if (*lp <= ' ' || *lp == '"' || (*lp >= '*' && *lp <= ',') diff --git a/sys/share/random.c b/sys/share/random.c index 5c94a03c07..bb045e01a7 100644 --- a/sys/share/random.c +++ b/sys/share/random.c @@ -12,7 +12,7 @@ * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ /* Several minor changes were made for the NetHack distribution to satisfy @@ -56,7 +56,7 @@ static char sccsid[] = "@(#)random.c 5.5 (Berkeley) 7/6/88"; * initialized to contain information for random number generation with that * much state information. Good sizes for the amount of state information are * 32, 64, 128, and 256 bytes. The state can be switched by calling the - * setstate() routine with the same array as was initiallized with + * setstate() routine with the same array as was initialized with * initstate(). * By default, the package runs with 128 bytes of state information and * generates far better random numbers than a linear congruential generator. @@ -348,7 +348,7 @@ char *arg_state; * random: * If we are using the trivial TYPE_0 R.N.G., just do the old linear * congruential bit. Otherwise, we do our fancy trinomial stuff, which is the - * same in all ther other cases due to all the global variables that have been + * same in all the other cases due to all the global variables that have been * set up. The basic operation is to add the number at the rear pointer into * the one at the front pointer. Then both pointers are advanced to the next * location cyclically in the table. The value returned is the sum generated, diff --git a/sys/share/tclib.c b/sys/share/tclib.c index ca2d95aeb3..d9ecace069 100644 --- a/sys/share/tclib.c +++ b/sys/share/tclib.c @@ -489,8 +489,8 @@ tputs(const char *string, /* characters to output */ int (*output_func)(int)) /* actual output routine; * return value ignored */ { - register int c, num = 0; - register const char *p = string; + int c, num = 0; + const char *p = string; if (!p || !*p) return; diff --git a/sys/share/unixtty.c b/sys/share/unixtty.c index 12e11ec9ae..624418d421 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,10 +312,14 @@ setftty(void) if (change) setctty(); - start_screen(); + +#ifdef TTY_GRAPHICS + if (WINDOWPORT(tty)) + term_start_screen(); +#endif } -void intron(void) /* enable kbd interupts if enabled when game started */ +void intron(void) /* enable kbd interrupts if enabled when game started */ { #ifdef TTY_GRAPHICS /* Ugly hack to keep from changing tty modes for non-tty games -dlc */ @@ -392,10 +402,8 @@ init_sco_cons(void) sco_mapoff(); load_symset("IBMGraphics", PRIMARYSET); switch_symbols(TRUE); -#ifdef TEXTCOLOR if (has_colors()) iflags.use_color = TRUE; -#endif } #endif } @@ -449,10 +457,8 @@ init_linux_cons(void) if (WINDOWPORT(tty) && linux_flag_console) { atexit(linux_mapon); linux_mapoff(); -#ifdef TEXTCOLOR if (has_colors()) iflags.use_color = TRUE; -#endif } #endif } @@ -483,7 +489,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/share/uudecode.c b/sys/share/uudecode.c index 9d2eacbb15..223b94031a 100644 --- a/sys/share/uudecode.c +++ b/sys/share/uudecode.c @@ -12,14 +12,14 @@ * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ /* * Modified 12 April 1990 by Mark Adler for use on MSDOS systems with * Microsoft C and Turbo C. * - * Modifed 13 February 1991 by Greg Roelofs for use on VMS systems. As + * Modified 13 February 1991 by Greg Roelofs for use on VMS systems. As * with the MS-DOS version, the setting of the file mode has been disabled. * Compile and link normally (but note that the shared-image link option * produces a binary only 6 blocks long, as opposed to the 137-block one @@ -41,6 +41,8 @@ * Modified 08 July 2006 to cast strlen() result to int to suppress a * warning on platforms where size_t > sizeof(int). * + * Modified 05 Jan 2024 to avoid K&R function declarations, marked KR_PROTO. + * * $NHDT-Date: 1432512787 2015/05/25 00:13:07 $ $NHDT-Branch: master $:$NHDT-Revision: 1.7 $ */ @@ -132,9 +134,12 @@ main(int argc, char **argv) /* handle ~user/file format */ if (dest[0] == '~') { char *sl; - struct passwd *getpwnam(); struct passwd *user; - char dnbuf[100], *strchr(), *strcat(), *strcpy(); + char dnbuf[100]; +#ifdef KR_PROTO + struct passwd *getpwnam(); + char *strchr(), *strcat(), *strcpy(); +#endif sl = strchr(dest, '/'); if (sl == NULL) { @@ -153,6 +158,7 @@ main(int argc, char **argv) strcpy(dest, dnbuf); } #endif /* !MSDOS && !VMS && !WIN32 && !__APPLE__ */ + dest[sizeof dest - 1] = '\0'; /* create output file */ #if defined(MSDOS) || defined(WIN32) @@ -237,8 +243,7 @@ outdec(char *p, FILE *f, int n) putc(c3, f); } -#if !defined(MSDOS) && !defined(VMS) && !defined(WIN32) && !defined(__APPLE__) - +#if !defined(MSDOS) && !defined(VMS) && !defined(WIN32) && !defined(__APPLE__) && !defined(__linux__) /* * Return the ptr in sp at which the character c appears; * NULL if not found @@ -250,7 +255,7 @@ outdec(char *p, FILE *f, int n) char * index(sp, c) -register char *sp, c; +char *sp, c; { do { if (*sp == c) diff --git a/sys/unix/Install.unx b/sys/unix/Install.unx index 357f3257e8..60596cbede 100644 --- a/sys/unix/Install.unx +++ b/sys/unix/Install.unx @@ -229,10 +229,11 @@ Notes: config.h: UNIX, TTY_GRAPHICS unixconf.h: SYSV, SVR4, TERMINFO, POSIX_JOB_CONTROL, POSIX_TYPES - X11_GRAPHICS does work. You may safely define - NETWORK, TEXTCOLOR if desired. Other #defines in these files may be - defined too, as needed. Just make sure that the set mentioned here are - not misdefined, or your compile will fail (do _not_ define BSD or SUNOS4). + X11_GRAPHICS does work. You may safely define NETWORK if desired. + Other #defines in these files may be defined too, as needed. Just make + sure that the set mentioned here are not misdefined, or your compile + will fail (do _not_ define BSD or SUNOS4). + Unless you are using gzip you will probably want to define COMPRESS to be "/usr/bin/compress". diff --git a/sys/unix/Makefile.check b/sys/unix/Makefile.check new file mode 100755 index 0000000000..4265e5f5ac --- /dev/null +++ b/sys/unix/Makefile.check @@ -0,0 +1,37 @@ +# NetHack 3.7 Makefile.check $NHDT-Date: 1599687610 2020/09/09 21:40:10 $ $NHDT-Branch: NetHack-3.7 + +SUPATH := sys/unix/ +MKHINTSPATH := $(SUPATH)hints/ +MKINCLPATH := $(SUPATH)hints/include/ + +# +# Expectations: +# +# HINTSFILE and HINTSINCLFILES are passed from the calling Makefile +# which is the case with the hints/*.370 and hints/include/*.370 +# set. +# +ifneq ("$(HINTSFILE)","") +MKHINTSFILE := $(addprefix $(MKHINTSPATH), $(notdir $(HINTSFILE))) +endif +ifneq ("$(HINTSINCLFILES)","") +MKINCLFILES := $(addprefix $(MKINCLPATH), $(HINTSINCLFILES)) +endif + +checkmakefiles: dat/Makefile src/Makefile util/Makefile doc/Makefile Makefile + +dat/Makefile: $(SUPATH)Makefile.dat $(MKHINTSFILE) $(MKINCLFILES) + @echo Attention: $@ is older than: $? + +src/Makefile: $(SUPATH)Makefile.src $(MKHINTSFILE) $(MKINCLFILES) + @echo Attention: $@ is older than: $? + +util/Makefile: $(SUPATH)Makefile.utl $(MKHINTSFILE) $(MKINCLFILES) + @echo Attention: $@ is older than: $? + +doc/Makefile: $(SUPATH)Makefile.doc $(MKHINTSFILE) $(MKINCLFILES) + @echo Attention: $@ is older than: $? + +Makefile: $(SUPATH)Makefile.top $(MKHINTSFILE) $(MKINCLFILES) + @echo Attention: $@ is older than: $? + diff --git a/sys/unix/Makefile.dat b/sys/unix/Makefile.dat index e12b923df4..ee5e683796 100644 --- a/sys/unix/Makefile.dat +++ b/sys/unix/Makefile.dat @@ -9,7 +9,7 @@ NHSROOT=.. # SHELL=E:/GEMINI2/MUPFEL.TTP # UUDECODE=uudecode -VARDAT = bogusmon data engrave epitaph rumors shirts oracles options +VARDAT = bogusmon data engrave epitaph rumors shirts oracles all: $(VARDAT) spec_levs quest_levs @@ -104,18 +104,17 @@ oracles: oracles.txt ../util/makedefs ../util/makedefs -h engrave: engrave.txt ../util/makedefs - ../util/makedefs -s + ../util/makedefs -2 epitaph: epitaph.txt ../util/makedefs - ../util/makedefs -s + ../util/makedefs -1 bogusmon: bogusmon.txt ../util/makedefs - ../util/makedefs -s + ../util/makedefs -3 shirts: shirts.txt ../util/makedefs ../util/makedefs -s -# note: 'options' should have already been made when include/date.h was created options: ../util/makedefs ../util/makedefs -v diff --git a/sys/unix/Makefile.doc b/sys/unix/Makefile.doc index aa31649f81..23b0f20896 100644 --- a/sys/unix/Makefile.doc +++ b/sys/unix/Makefile.doc @@ -1,5 +1,5 @@ # NetHack Documentation Makefile. -# NetHack 3.7 Makefile.doc $NHDT-Date: 1596498290 2020/08/03 23:44:50 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.21 $ +# NetHack 3.7 Makefile.doc $NHDT-Date: 1700788000 2023/11/24 01:06:40 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.28 $ # Copyright (c) 2015 by Kenneth Lorber, Kensington, Maryland # NetHack may be freely redistributed. See license for details. @@ -19,6 +19,8 @@ GUIDEBOOK = Guidebook # regular ASCII file # Some versions of col need -x to keep them from converting spaces to tabs; # some versions of col don't do the conversion by default and don't # recognize the option. Sigh. +# +# col is unnecessary, but harmless, with groff. See grotty(1). COLCMD = col -bx #COLCMD = col -b @@ -33,9 +35,8 @@ PSCMD = groff # Single page. Might need adjustment to .pl value # GUIDECMD= $(GUIDE_PREFORMAT) | perl -pe 's/^(.mt)$$/.pl 4720v\n.in 0\n.po 8\n.ll 64m\n$$1/' | nroff -c -Tascii | $(COLCMD) # -GUIDECMD = $(GUIDE_PREFORMAT) | nroff -c -Tascii | $(COLCMD) -ONEPAGECMD = $(ONEPAGE_PREFORMAT) | nroff -c -Tascii | $(COLCMD) \ - | sed -e '/EOF--EOF/,12345D' +GUIDECMD = $(GUIDE_PREFORMAT) | nroff -c -Tascii $(NROFF_FLAGS) | $(COLCMD) +ONEPAGECMD = $(ONEPAGE_PREFORMAT) | nroff -c -Tascii $(NROFF_FLAGS) | $(COLCMD) # Only generate output for the current configuration: NHGREP = $(MAKEDEFS) --grep --input - --output - @@ -62,6 +63,7 @@ Guidebook : $(GUIDEBOOK_MN) tmac.n tmac.nh $(NEEDMAKEDEFS) $(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. Guidebook.ps : $(GUIDEBOOK_MN) tmac.n tmac.nh $(NEEDMAKEDEFS) $(GUIDE_PREFORMAT) | $(PSCMD) > Guidebook.ps @@ -81,8 +83,9 @@ $(MAKEDEFS) : ../util/makedefs.c ../include/config.h ../src/mdlib.c \ ( cd ../util ; make makedefs ) GAME = xnethack -MANDIR = /usr/man/man6 +MANDIR ?= /usr/man/man6 MANEXT = 6 +NROFF ?= nroff # manual installation for most BSD-style systems GAMEMANCREATE = cat xnethack.6 | $(NHGREP) > @@ -95,7 +98,7 @@ MDMANCREATE = cat makedefs.6 | $(NHGREP) > # DLBMANCREATE = cat dlb.6 | $(NHGREP) | nroff -man - > # MDMANCREATE = cat makedefs.6 | $(NHGREP) | nroff -man - > -manpages: +manpages: $(PREMANPAGES) -$(GAMEMANCREATE) $(MANDIR)/$(GAME).$(MANEXT) -$(RCVRMANCREATE) $(MANDIR)/recover.$(MANEXT) -$(DLBMANCREATE) $(MANDIR)/dlb.$(MANEXT) @@ -114,8 +117,16 @@ Guidebook.dat : Gbk-1pg-pfx.mn Gbk-1pg-sfx.mn $(GUIDEBOOK_MN) tmac.n tmac.nh \ $(NEEDMAKEDEFS) $(ONEPAGECMD) > $@ -MAN2TXT = $(NHGREP) | nroff -man - | $(COLCMD) -xnethack.txt : xnethack.6 +# plain text output +# +# MAN2TXTPRE and MAN2TXTPOST are included so that a hints file can alter +# both of them as required +MAN2TXTPOST ?= | col -b +MAN2TXT = $(NHGREP) | $(NROFF) -man $(MAN2TXTPRE) $(MAN2TXTPOST) +# If you aren't using a hints file, you can uncomment the following +# line if you have groff 1.23 or greater. +#MAN2TXT = $(NHGREP) | groff -man -Tascii -P -cbou +nethack.txt : nethack.6 cat xnethack.6 | $(MAN2TXT) > xnethack.txt recover.txt : recover.6 cat recover.6 | $(MAN2TXT) > recover.txt diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index cbf53bac07..d1460ae04f 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -1,5 +1,5 @@ # NetHack Makefile. -# NetHack 3.7 Makefile.src $NHDT-Date: 1654287121 2022/06/03 20:12:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.145 $ +# NetHack 3.7 Makefile.src $NHDT-Date: 1697316523 2023/10/14 20:48:43 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.164 $ # Copyright (c) 2018 by Pasi Kallinen # NetHack may be freely redistributed. See license for details. @@ -119,6 +119,11 @@ SYSOBJ = $(TARGETPFX)ioctl.o $(TARGETPFX)unixmain.o $(TARGETPFX)unixtty.o \ # CFLAGS = -mshort -O2 -fomit-frame-pointer -I../include # LFLAGS = -mshort -s +# Curses, use one of the following: +# -D_XOPEN_SOURCE_EXTENDED=1 allows any map character to appear in the message window +# during far look with the '/' command. +# -DCURSES_GENL_PUTMIXED to disable that and fall back to displaying only the historical tty character. +# # flags for AIX 3.1 cc on IBM RS/6000 to define # a suitable subset of standard libraries # (note that there is more info regarding the "-qchars=signed" @@ -183,6 +188,9 @@ SYSOBJ = $(TARGETPFX)ioctl.o $(TARGETPFX)unixmain.o $(TARGETPFX)unixtty.o \ #CFLAGS = -O -I../include #LFLAGS = +AR = ar +ARFLAGS = rcs + # The Qt and Be window systems are written in C++, while the rest of # NetHack is standard C. If using Qt, uncomment the LINK line here to get # the C++ libraries linked in. @@ -205,6 +213,8 @@ TARGET_LFLAGS = $(LFLAGS) TARGET_CXX = $(CXX) TARGET_CXXFLAGS = $(CXXFLAGS) TARGET_LIBS = $(LIBS) +TARGET_AR = $(AR) +TARGET_ARFLAGS = $(ARFLAGS) # we specify C preprocessor flags via CFLAGS; files built with default rules # might include $(CPPFLAGS) which could get a value from user's environment; @@ -442,7 +452,10 @@ QUIETCC=1 # Other things that have to be reconfigured are in config.h, # {unixconf.h, pcconf.h}, and possibly cstd.h -# Add rule for possible cross-compiler + +# NB: This is not used for all .c files (see explicit rules in +# dependencies). +# A normal or cross compile. $(TARGETPFX)%.o : %.c $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ $< @@ -481,13 +494,19 @@ AT = $(AT_V$(QUIETCC)) PREGAME=@true PACKAGE=@true +HACKLIBSRC = hacklib.c +HACKLIBOBJS = hacklib.o +HACKLIB = hacklib.a +TARGET_HACKLIB = $(TARGETPFX)hacklib.a + MAKEDEFS = ../util/makedefs # -lm required by lua -LUA_VERSION ?=5.4.4 +LUA_VERSION ?=5.4.6 LUABASE = liblua-$(LUA_VERSION).a LUALIB = ../lib/lua/$(LUABASE) LUALIBS = $(LUALIB) -lm $(DLLIB) +LUAHEADERS = lib/lua-$(LUA_VERSION)/src # timestamp files to reduce `make' overhead and shorten .o dependency lists CONFIG_H = ../src/config.h-t @@ -496,23 +515,23 @@ HACK_H = ../src/hack.h-t # all .c that are part of the main NetHack program and are not operating- or # windowing-system specific. Do not include date.c in this list. HACKCSRC = allmain.c alloc.c apply.c artifact.c attrib.c ball.c bones.c \ - botl.c cmd.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 hack.c hacklib.c \ - insight.c invent.c isaac64.c light.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 \ + extralev.c files.c fountain.c hack.c hacklib.c \ + getpos.c glyphs.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 \ + 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 restore.c \ - rip.c rnd.c role.c rumors.c save.c sfstruct.c \ - shk.c shknam.c sit.c sounds.c \ - sp_lev.c spell.c steal.c steed.c symbols.c sys.c teleport.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 utf8map.c \ uhitm.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 + windows.c wizard.c wizcmds.c worm.c worn.c write.c zap.c # all operating-system-dependent .c (for dependencies and such) SYSCSRC = ../sys/share/pcmain.c ../sys/share/pcsys.c \ @@ -538,10 +557,10 @@ CHAINOBJ = $(TARGETPFX)wc_chainin.o $(TARGETPFX)wc_chainout.o \ $(TARGETPFX)wc_trace.o # .c files for this version (for date.h) -VERSOURCES = $(HACKCSRC) $(SYSSRC) $(WINSRC) $(CHAINSRC) $(GENCSRC) +VERSOURCES = $(HACKCSRC) $(HACKLIBSRC) $(SYSSRC) $(WINSRC) $(CHAINSRC) $(GENCSRC) # .c files for all versions using this Makefile (for lint and tags) -CSOURCES = $(HACKCSRC) $(SYSCSRC) $(WINCSRC) $(CHAINSRC) $(GENCSRC) +CSOURCES = $(HACKCSRC) $(HACKLIBSRC) $(SYSCSRC) $(WINCSRC) $(CHAINSRC) $(GENCSRC) # all .h files except date.h, which would @@ -553,9 +572,9 @@ HACKINCL = align.h artifact.h artilist.h attrib.h botl.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 \ - obj.h objects.h objclass.h optlist.h patchlevel.h pcconf.h \ - permonst.h prop.h rect.h region.h sym.h defsym.h rm.h sp_lev.h \ - spell.h sndprocs.h seffects.h sys.h tcap.h timeout.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 @@ -571,45 +590,49 @@ 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)cmd.o $(TARGETPFX)dbridge.o $(TARGETPFX)decl.o \ + $(TARGETPFX)calendar.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 \ $(TARGETPFX)do_wear.o $(TARGETPFX)dog.o $(TARGETPFX)dogmove.o \ $(TARGETPFX)dokick.o $(TARGETPFX)dothrow.o $(TARGETPFX)drawing.o \ $(TARGETPFX)dungeon.o $(TARGETPFX)eat.o $(TARGETPFX)end.o \ $(TARGETPFX)engrave.o $(TARGETPFX)exper.o $(TARGETPFX)explode.o \ - $(TARGETPFX)files.o $(TARGETPFX)fountain.o \ - $(TARGETPFX)hack.o $(TARGETPFX)hacklib.o $(TARGETPFX)insight.o \ - $(TARGETPFX)invent.o $(TARGETPFX)isaac64.o $(TARGETPFX)light.o \ - $(TARGETPFX)lock.o $(TARGETPFX)mail.o $(TARGETPFX)makemon.o \ - $(TARGETPFX)mcastu.o $(TARGETPFX)mdlib.o \ + $(TARGETPFX)extralev.o $(TARGETPFX)files.o $(TARGETPFX)fountain.o \ + $(TARGETPFX)getpos.o $(TARGETPFX)glyphs.o $(TARGETPFX)hack.o \ + $(TARGETPFX)insight.o $(TARGETPFX)invent.o $(TARGETPFX)isaac64.o \ + $(TARGETPFX)light.o $(TARGETPFX)lock.o $(TARGETPFX)mail.o \ + $(TARGETPFX)makemon.o $(TARGETPFX)mcastu.o $(TARGETPFX)mdlib.o \ $(TARGETPFX)mhitm.o $(TARGETPFX)mhitu.o $(TARGETPFX)minion.o \ $(TARGETPFX)mklev.o $(TARGETPFX)mkmap.o $(TARGETPFX)mkmaze.o \ $(TARGETPFX)mkobj.o $(TARGETPFX)mkroom.o $(TARGETPFX)mon.o \ $(TARGETPFX)mondata.o $(TARGETPFX)monmove.o $(TARGETPFX)monst.o \ $(TARGETPFX)mplayer.o $(TARGETPFX)mthrowu.o $(TARGETPFX)muse.o \ $(TARGETPFX)music.o $(TARGETPFX)nhlua.o $(TARGETPFX)nhlsel.o \ - $(TARGETPFX)nhlobj.o $(TARGETPFX)objects.o $(TARGETPFX)o_init.o \ + $(TARGETPFX)nhlobj.o $(TARGETPFX)nhmd4.o \ + $(TARGETPFX)objects.o $(TARGETPFX)o_init.o \ $(TARGETPFX)objnam.o $(TARGETPFX)options.o $(TARGETPFX)pager.o \ $(TARGETPFX)pickup.o $(TARGETPFX)pline.o $(TARGETPFX)polyself.o \ $(TARGETPFX)potion.o $(TARGETPFX)pray.o $(TARGETPFX)priest.o \ $(TARGETPFX)quest.o $(TARGETPFX)questpgr.o $(TARGETPFX)read.o \ - $(TARGETPFX)rect.o $(TARGETPFX)region.o $(TARGETPFX)restore.o \ - $(TARGETPFX)rip.o $(TARGETPFX)rnd.o $(TARGETPFX)role.o \ - $(TARGETPFX)rumors.o $(TARGETPFX)save.o $(TARGETPFX)sfstruct.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)shk.o $(TARGETPFX)shknam.o $(TARGETPFX)sit.o \ $(TARGETPFX)sounds.o $(TARGETPFX)sp_lev.o $(TARGETPFX)spell.o \ - $(TARGETPFX)symbols.o $(TARGETPFX)sys.o $(TARGETPFX)steal.o \ - $(TARGETPFX)steed.o $(TARGETPFX)teleport.o $(TARGETPFX)timeout.o \ - $(TARGETPFX)topten.o $(TARGETPFX)track.o $(TARGETPFX)trap.o \ - $(TARGETPFX)u_init.o $(TARGETPFX)uhitm.o $(TARGETPFX)utf8map.o \ - $(TARGETPFX)vault.o $(TARGETPFX)vision.o $(TARGETPFX)weapon.o \ + $(TARGETPFX)stairs.o $(TARGETPFX)symbols.o $(TARGETPFX)sys.o \ + $(TARGETPFX)steal.o $(TARGETPFX)steed.o $(TARGETPFX)strutil.o \ + $(TARGETPFX)teleport.o $(TARGETPFX)timeout.o $(TARGETPFX)topten.o \ + $(TARGETPFX)track.o $(TARGETPFX)trap.o $(TARGETPFX)u_init.o \ + $(TARGETPFX)uhitm.o $(TARGETPFX)utf8map.o $(TARGETPFX)vault.o \ + $(TARGETPFX)vision.o $(TARGETPFX)weapon.o \ $(TARGETPFX)were.o $(TARGETPFX)wield.o $(TARGETPFX)windows.o \ - $(TARGETPFX)wizard.o $(TARGETPFX)worm.o $(TARGETPFX)worn.o \ - $(TARGETPFX)write.o $(TARGETPFX)zap.o \ + $(TARGETPFX)wizard.o $(TARGETPFX)wizcmds.o $(TARGETPFX)worm.o \ + $(TARGETPFX)worn.o $(TARGETPFX)write.o $(TARGETPFX)zap.o \ $(REGEXOBJ) $(RANDOBJ) $(SYSOBJ) $(WINOBJ) $(HINTOBJ) $(SNDLIBOBJ) \ $(TARGETPFX)version.o -# + DATE_O = $(TARGETPFX)date.o # the .o files from the HACKCSRC, SYSSRC, and WINSRC lists @@ -624,40 +647,46 @@ pregame: $(GAME): pregame $(MAKEDEFS) $(LUALIB) $(WAVS) $(SYSTEM) @echo "$(GAME) is up to date." -Sysunix: $(HOSTOBJ) $(HOBJ) $(DATE_O) $(BUILDMORE) Makefile +Sysunix: $(HOSTOBJ) $(HOBJ) $(TARGET_HACKLIB) $(DATE_O) $(BUILDMORE) Makefile @echo "Linking $(GAME)." $(AT)$(TARGET_LINK) $(TARGET_LFLAGS) -o $(GAMEBIN) \ - $(HOBJ) $(DATE_O) $(WINLIB) $(TARGET_LIBS) $(LUALIBS) + $(HOBJ) $(DATE_O) $(TARGET_HACKLIB) $(WINLIB) \ + $(TARGET_LIBS) $(LUALIBS) $(AUTOLIBS) @touch Sysunix -Sys3B2: $(HOSTOBJ) $(HOBJ) $(DATE_O) $(BUILDMORE) Makefile +Sys3B2: $(HOSTOBJ) $(HOBJ) $(TARGET_HACKLIB) $(DATE_O) $(BUILDMORE) Makefile @echo "Linking $(GAME)." $(AT)$(TARGET_LINK) $(TARGET_LFLAGS) -o $(GAMEBIN) \ - $(HOBJ) $(DATE_O) $(WINLIB) $(LUALIBS) -lmalloc + $(HOBJ) $(DATE_O) $(TARGET_HACKLIB) $(WINLIB) \ + $(LUALIBS) -lmalloc @touch Sys3B2 -Sysatt: $(HOSTOBJ) $(HOBJ) $(DATE_O) $(BUILDMORE) Makefile +Sysatt: $(HOSTOBJ) $(HOBJ) $(TARGET_HACKLIB) $(DATE_O) $(BUILDMORE) Makefile @echo "Loading $(GAME)." $(AT)$(LD) $(TARGET_LFLAGS) /lib/crt0s.o /lib/shlib.ifile \ - -o $(GAMEBIN) $(HOSTOBJ) $(HOBJ) $(DATE_O) $(LUALIBS) + -o $(GAMEBIN) $(HOSTOBJ) $(HOBJ) $(DATE_O) $(TARGET_HACKLIB) \ + $(LUALIBS) @touch Sysatt -Systos: $(HOSTOBJ) $(HOBJ) $(DATE_O) $(BUILDMORE) Makefile +Systos: $(HOSTOBJ) $(HOBJ) $(TARGET_HACKLIB) $(DATE_O) $(BUILDMORE) Makefile @echo "Linking $(GAME)." $(AT)$(TARGET_LINK) $(TARGET_LFLAGS) -o $(GAMEBIN) \ - $(HOBJ) $(DATE_O) $(WINLIB) $(LUALIBS) + $(HOBJ) $(DATE_O) $(TARGET_HACKLIB) \ + $(WINLIB) $(LUALIBS) @touch Systos -SysV-AT: DUMB.Setup $(HOSTOBJ) $(HOBJ) $(DATE_O) $(BUILDMORE) Makefile +SysV-AT: DUMB.Setup $(HOSTOBJ) $(HOBJ) $(TARGET_HACKLIB) $(DATE_O) $(BUILDMORE) Makefile @echo "Linking $(GAME)." $(AT)$(TARGET_LINK) $(TARGET_LFLAGS) -o $(GAMEBIN) \ - $(HOBJ) $(DATE_O) $(WINLIB) $(LUALIBS) + $(HOBJ) $(DATE_O) $(TARGET_HACKLIB) \ + $(WINLIB) $(LUALIBS) @touch SysV-AT -SysBe: $(HOSTOBJ) $(HOBJ) $(DATE_O) $(BUILDMORE) Makefile +SysBe: $(HOSTOBJ) $(HOBJ) $(TARGET_HACKLIB) $(DATE_O) $(BUILDMORE) Makefile @echo "Linking $(GAME)." $(AT)$(TARGET_LINK) $(TARGET_LFLAGS) -o $(GAME) \ - $(HOBJ) $(DATE_O) $(WINLIB) $(TARGET_LIBS) $(LUALIBS) + $(HOBJ) $(DATE_O) $(TARGET_HACKLIB) \ + $(WINLIB) $(TARGET_LIBS) $(LUALIBS) @xres -o $(GAME) ../win/BeOS/nethack.rsrc @mimeset -f $(GAME) @touch SysBe @@ -729,6 +758,12 @@ $(MAKEDEFS): $(FIRSTOBJ) \ tile.c: ../win/share/tilemap.c $(HACK_H) @( cd ../util ; $(MAKE) ../src/tile.c ) +# +# hacklib (a library of utility routines) +# + +hacklib.a: hacklib.o + $(AR) $(ARFLAGS) $@ hacklib.o ../win/gnome/gn_rip.h: ../win/X11/rip.xpm cp ../win/X11/rip.xpm ../win/gnome/gn_rip.h @@ -737,8 +772,9 @@ $(TARGETPFX)sfstruct.o: sfstruct.c $(HACK_H) # date.c should be recompiled any time any of the source or include code # is modified. -$(TARGETPFX)date.o: date.c $(HACK_H) $(HACKCSRC) $(HOBJ) - $(TARGET_CC) $(TARGET_CFLAGS) $(GITHASH) $(GITBRANCH) -c -o $@ date.c +DATECFLAGS = $(TARGET_CFLAGS) $(GITHASH) $(GITBRANCH) $(GITPREFIX) +$(TARGETPFX)date.o: date.c $(HACK_H) $(HACKCSRC) $(HOBJ) $(TARGET_HACKLIB) + $(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 @@ -773,7 +809,7 @@ clean: true; $(CLEANMORE) spotless: clean - -rm -f a.out core $(GAMEBIN) Sys* + -rm -f a.out core $(HACKLIB) $(GAMEBIN) Sys* -rm -f ../include/nhlua.h -rm -f ../include/date.h #created but no longer used, at least by core -rm -f ../include/onames.h ../include/pm.h #obsolete generated files @@ -784,53 +820,92 @@ package: $(PACKAGE) @echo packaging complete. -depend: ../sys/unix/depend.awk \ - $(SYSCSRC) $(WINCSRC) $(SYSCXXSRC) $(WINCXXSRC) \ - $(CHAINSRC) $(GENCSRC) $(HACKCSRC) +# A more automated "make depend" target +# cd sys/unix ; make -f Makefile.src updatedepend +updatedepend: updatedepend-setup + cp Makefile.src ../../src && \ + make -C ../../src MAKEFILE_NAME=Makefile.src -f Makefile.src depend2 && \ + mv ../../src/Makefile.src . + @rm -f ../$(HACK_H) ../../include/nhlua.h + +# check pwd, create bogus derived files +updatedepend-setup: + @ # make sure we're in sys/unix before we start writing + @p=`pwd`; \ + if [ $$p = $${p%/sys/unix} ]; then \ + echo "updatedepend must be run in sys/unix"; exit 1; \ + fi + touch ../$(HACK_H) + @ # generate bogus files + echo "Generating bogus include/nhlua.h" + (HFILE=../../include/nhlua.h && \ + echo '/* bogus nhlua.h for updatedepend ONLY */' > $$HFILE && \ + echo '#error bogus include/nhlua.h' >> $$HFILE && \ + echo '#include "../$(LUAHEADERS)/lua.h"' >> $$HFILE && \ + echo '#include "../$(LUAHEADERS)/lualib.h"' >> $$HFILE && \ + echo '#include "../$(LUAHEADERS)/lauxlib.h"' >> $$HFILE && \ + echo '/*nhlua.h*/' >> $$HFILE ) + @echo "Generating bogus src/tile.c" + @echo '#error bogus include/nhlua.h' > ../../src/tile.c + @echo "#include \"hack.h\"" >> ../../src/tile.c + +MAKEFILE_NAME=Makefile +depend: ../sys/unix/depend.awk \ + $(SYSCSRC) $(WINCSRC) $(SYSCXXSRC) $(WINCXXSRC) \ + $(CHAINSRC) $(GENCSRC) $(HACKCSRC) + $(MAKE) depend2 + +depend2: $(WINQTMOC) $(AWK) -f ../sys/unix/depend.awk ../include/*.h ../win/*/*.h \ $(SYSCSRC) $(WINCSRC) $(SYSCXXSRC) $(WINCXXSRC) \ $(CHAINSRC) $(GENCSRC) $(HACKCSRC) >makedep @echo '/^# DO NOT DELETE THIS LINE OR CHANGE ANYTHING BEYOND IT/+2,$$d' >eddep @echo '$$r makedep' >>eddep @echo 'w' >>eddep - @cp Makefile Makefile.bak - ed - Makefile < eddep + @cp $(MAKEFILE_NAME) Makefile.bak + @ed - $(MAKEFILE_NAME) < eddep @rm -f eddep makedep - @echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile - @echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile - @echo '# see make depend above' >> Makefile - - diff Makefile.bak Makefile + @echo '# DEPENDENCIES MUST END AT END OF FILE' >> $(MAKEFILE_NAME) + @echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> $(MAKEFILE_NAME) + @echo '# see make depend above' >> $(MAKEFILE_NAME) + - diff Makefile.bak $(MAKEFILE_NAME) @rm -f Makefile.bak + @rm -f ../include/nhlua.h tile.c # DO NOT DELETE THIS LINE OR CHANGE ANYTHING BEYOND IT # config.h timestamp -$(CONFIG_H): ../include/config.h ../include/config1.h ../include/patchlevel.h \ - ../include/tradstdc.h ../include/integer.h \ - ../include/global.h ../include/coord.h ../include/vmsconf.h \ - ../include/cstd.h ../include/nhlua.h ../include/unixconf.h \ - ../include/pcconf.h ../include/micro.h ../include/windconf.h \ - ../include/warnings.h ../include/fnamesiz.h +$(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 touch $(CONFIG_H) # hack.h timestamp -$(HACK_H): ../include/hack.h $(CONFIG_H) ../include/lint.h ../include/align.h \ - ../include/dungeon.h ../include/wintype.h ../include/sym.h \ - ../include/defsym.h ../include/mkroom.h ../include/artilist.h \ - ../include/objclass.h ../include/objects.h \ - ../include/youprop.h ../include/prop.h ../include/permonst.h \ - ../include/monattk.h ../include/monflag.h \ - ../include/monsters.h ../include/mondata.h \ - ../include/context.h ../include/rm.h ../include/botl.h \ - ../include/rect.h ../include/region.h ../include/trap.h \ - ../include/display.h ../include/vision.h ../include/color.h \ - ../include/sndprocs.h ../include/seffects.h ../include/decl.h \ - ../include/quest.h ../include/spell.h ../include/obj.h \ - ../include/engrave.h ../include/you.h ../include/attrib.h \ - ../include/monst.h ../include/mextra.h ../include/skills.h \ - ../include/timeout.h ../include/flag.h ../include/winprocs.h \ - ../include/sys.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/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 touch $(HACK_H) # +$(TARGETPFX)cppregex.o: ../sys/share/cppregex.cpp $(CONFIG_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../sys/share/cppregex.cpp +$(TARGETPFX)ioctl.o: ../sys/share/ioctl.c $(HACK_H) ../include/tcap.h + $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../sys/share/ioctl.c $(TARGETPFX)pcmain.o: ../sys/share/pcmain.c $(HACK_H) ../include/dlb.h $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../sys/share/pcmain.c $(TARGETPFX)pcsys.o: ../sys/share/pcsys.c $(HACK_H) ../include/wintty.h @@ -845,249 +920,223 @@ $(TARGETPFX)posixregex.o: ../sys/share/posixregex.c $(HACK_H) $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../sys/share/posixregex.c $(TARGETPFX)random.o: ../sys/share/random.c $(HACK_H) $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../sys/share/random.c -$(TARGETPFX)ioctl.o: ../sys/share/ioctl.c $(HACK_H) ../include/tcap.h - $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../sys/share/ioctl.c $(TARGETPFX)unixtty.o: ../sys/share/unixtty.c $(HACK_H) $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../sys/share/unixtty.c $(TARGETPFX)unixmain.o: ../sys/unix/unixmain.c $(HACK_H) ../include/dlb.h $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../sys/unix/unixmain.c -$(TARGETPFX)unixunix.o: ../sys/unix/unixunix.c $(HACK_H) - $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../sys/unix/unixunix.c $(TARGETPFX)unixres.o: ../sys/unix/unixres.c $(CONFIG_H) $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../sys/unix/unixres.c -$(TARGETPFX)getline.o: ../win/tty/getline.c $(HACK_H) ../include/wintty.h \ - ../include/func_tab.h - $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/tty/getline.c -$(TARGETPFX)termcap.o: ../win/tty/termcap.c $(HACK_H) ../include/wintty.h \ - ../include/tcap.h - $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/tty/termcap.c -$(TARGETPFX)topl.o: ../win/tty/topl.c $(HACK_H) ../include/tcap.h \ - ../include/wintty.h - $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/tty/topl.c -$(TARGETPFX)wintty.o: ../win/tty/wintty.c $(HACK_H) ../include/dlb.h \ - ../include/tcap.h ../include/wintty.h - $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/tty/wintty.c -$(TARGETPFX)cursmain.o: ../win/curses/cursmain.c $(HACK_H) ../include/wincurs.h - $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/curses/cursmain.c -$(TARGETPFX)curswins.o: ../win/curses/curswins.c $(HACK_H) \ - ../include/wincurs.h ../win/curses/curswins.h - $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/curses/curswins.c -$(TARGETPFX)cursmisc.o: ../win/curses/cursmisc.c $(HACK_H) \ - ../include/wincurs.h ../win/curses/cursmisc.h \ - ../include/func_tab.h ../include/dlb.h - $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/curses/cursmisc.c -$(TARGETPFX)cursdial.o: ../win/curses/cursdial.c $(HACK_H) \ - ../include/wincurs.h ../win/curses/cursdial.h \ - ../include/func_tab.h - $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/curses/cursdial.c -$(TARGETPFX)cursstat.o: ../win/curses/cursstat.c $(HACK_H) \ - ../include/wincurs.h ../win/curses/cursstat.h - $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/curses/cursstat.c -$(TARGETPFX)cursinit.o: ../win/curses/cursinit.c $(HACK_H) \ - ../include/wincurs.h ../win/curses/cursinit.h - $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/curses/cursinit.c -$(TARGETPFX)cursmesg.o: ../win/curses/cursmesg.c $(HACK_H) \ - ../include/wincurs.h ../win/curses/cursmesg.h - $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/curses/cursmesg.c -$(TARGETPFX)cursinvt.o: ../win/curses/cursinvt.c $(HACK_H) \ - ../include/wincurs.h ../win/curses/cursinvt.h - $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/curses/cursinvt.c -$(TARGETPFX)Window.o: ../win/X11/Window.c ../include/xwindowp.h \ - ../include/xwindow.h $(CONFIG_H) ../include/lint.h \ - ../include/winX.h ../include/color.h ../include/wintype.h - $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/Window.c -$(TARGETPFX)dialogs.o: ../win/X11/dialogs.c $(CONFIG_H) ../include/lint.h \ - ../include/winX.h ../include/color.h ../include/wintype.h - $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/dialogs.c -$(TARGETPFX)winX.o: ../win/X11/winX.c $(HACK_H) ../include/winX.h \ - ../include/dlb.h ../include/xwindow.h ../win/X11/nh72icon \ - ../win/X11/nh56icon ../win/X11/nh32icon - $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winX.c -$(TARGETPFX)winmap.o: ../win/X11/winmap.c ../include/xwindow.h $(HACK_H) \ - ../include/dlb.h ../include/winX.h ../include/tile2x11.h - $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winmap.c -$(TARGETPFX)winmenu.o: ../win/X11/winmenu.c $(HACK_H) ../include/winX.h - $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winmenu.c -$(TARGETPFX)winmesg.o: ../win/X11/winmesg.c ../include/xwindow.h $(HACK_H) \ - ../include/winX.h - $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winmesg.c -$(TARGETPFX)winmisc.o: ../win/X11/winmisc.c $(HACK_H) ../include/func_tab.h \ - ../include/winX.h - $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winmisc.c -$(TARGETPFX)winstat.o: ../win/X11/winstat.c $(HACK_H) ../include/winX.h \ - ../include/xwindow.h - $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winstat.c -$(TARGETPFX)wintext.o: ../win/X11/wintext.c $(HACK_H) ../include/winX.h \ - ../include/xwindow.h - $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/wintext.c -$(TARGETPFX)winval.o: ../win/X11/winval.c $(HACK_H) ../include/winX.h - $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winval.c -$(TARGETPFX)tile.o: tile.c $(HACK_H) -$(TARGETPFX)winshim.o: ../win/shim/winshim.c $(HACK_H) - $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/shim/winshim.c -$(TARGETPFX)cppregex.o: ../sys/share/cppregex.cpp $(CONFIG_H) - $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../sys/share/cppregex.cpp -$(TARGETPFX)qt_bind.o: ../win/Qt/qt_bind.cpp $(HACK_H) ../win/Qt/qt_pre.h \ - ../win/Qt/qt_post.h ../win/Qt/qt_bind.h ../win/Qt/qt_main.h \ - ../win/Qt/qt_kde0.h ../win/Qt/qt_click.h ../win/Qt/qt_delay.h \ - ../win/Qt/qt_xcmd.h ../win/Qt/qt_key.h ../win/Qt/qt_map.h \ - ../win/Qt/qt_win.h ../win/Qt/qt_clust.h ../win/Qt/qt_menu.h \ - ../win/Qt/qt_rip.h ../win/Qt/qt_msg.h ../win/Qt/qt_plsel.h \ - ../win/Qt/qt_svsel.h ../win/Qt/qt_set.h ../win/Qt/qt_stat.h \ - ../win/Qt/qt_icon.h ../win/Qt/qt_streq.h ../win/Qt/qt_line.h \ - ../win/Qt/qt_yndlg.h ../win/Qt/qt_str.h ../include/dlb.h \ - $(QTn_H) +$(TARGETPFX)unixunix.o: ../sys/unix/unixunix.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../sys/unix/unixunix.c +$(TARGETPFX)qt_bind.o: ../win/Qt/qt_bind.cpp $(HACK_H) $(QTn_H) \ + ../include/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 \ + ../win/Qt/qt_msg.h ../win/Qt/qt_plsel.h ../win/Qt/qt_post.h \ + ../win/Qt/qt_pre.h ../win/Qt/qt_rip.h ../win/Qt/qt_set.h \ + ../win/Qt/qt_stat.h ../win/Qt/qt_str.h ../win/Qt/qt_streq.h \ + ../win/Qt/qt_svsel.h ../win/Qt/qt_win.h ../win/Qt/qt_xcmd.h \ + ../win/Qt/qt_yndlg.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../win/Qt/qt_bind.cpp -$(TARGETPFX)qt_click.o: ../win/Qt/qt_click.cpp $(HACK_H) ../win/Qt/qt_pre.h \ - ../win/Qt/qt_post.h ../win/Qt/qt_click.h $(QTn_H) +$(TARGETPFX)qt_click.o: ../win/Qt/qt_click.cpp $(HACK_H) $(QTn_H) \ + ../win/Qt/qt_click.h ../win/Qt/qt_post.h ../win/Qt/qt_pre.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../win/Qt/qt_click.cpp -$(TARGETPFX)qt_clust.o: ../win/Qt/qt_clust.cpp ../win/Qt/qt_clust.h $(QTn_H) +$(TARGETPFX)qt_clust.o: ../win/Qt/qt_clust.cpp $(QTn_H) ../win/Qt/qt_clust.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../win/Qt/qt_clust.cpp -$(TARGETPFX)qt_delay.o: ../win/Qt/qt_delay.cpp $(HACK_H) ../win/Qt/qt_pre.h \ - ../win/Qt/qt_post.h ../win/Qt/qt_delay.h $(QTn_H) +$(TARGETPFX)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) -c -o $@ ../win/Qt/qt_delay.cpp -$(TARGETPFX)qt_glyph.o: ../win/Qt/qt_glyph.cpp $(HACK_H) \ - ../include/tile2x11.h ../win/Qt/qt_pre.h ../win/Qt/qt_post.h \ - ../win/Qt/qt_glyph.h ../win/Qt/qt_bind.h ../win/Qt/qt_main.h \ - ../win/Qt/qt_kde0.h ../win/Qt/qt_set.h ../win/Qt/qt_inv.h \ - ../win/Qt/qt_map.h ../win/Qt/qt_win.h ../win/Qt/qt_clust.h \ - ../win/Qt/qt_str.h $(QTn_H) +$(TARGETPFX)qt_glyph.o: ../win/Qt/qt_glyph.cpp $(HACK_H) $(QTn_H) \ + ../include/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 \ + ../win/Qt/qt_str.h ../win/Qt/qt_win.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../win/Qt/qt_glyph.cpp -$(TARGETPFX)qt_icon.o: ../win/Qt/qt_icon.cpp $(HACK_H) ../win/Qt/qt_pre.h \ - ../win/Qt/qt_post.h ../win/Qt/qt_icon.h ../win/Qt/qt_str.h \ - $(QTn_H) +$(TARGETPFX)qt_icon.o: ../win/Qt/qt_icon.cpp $(HACK_H) $(QTn_H) \ + ../win/Qt/qt_icon.h ../win/Qt/qt_post.h ../win/Qt/qt_pre.h \ + ../win/Qt/qt_str.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../win/Qt/qt_icon.cpp -$(TARGETPFX)qt_inv.o: ../win/Qt/qt_inv.cpp $(HACK_H) ../win/Qt/qt_pre.h \ - ../win/Qt/qt_post.h ../win/Qt/qt_inv.h ../win/Qt/qt_glyph.h \ - ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h ../win/Qt/qt_set.h \ - ../win/Qt/qt_bind.h $(QTn_H) +$(TARGETPFX)qt_inv.o: ../win/Qt/qt_inv.cpp $(HACK_H) $(QTn_H) \ + ../win/Qt/qt_bind.h ../win/Qt/qt_glyph.h ../win/Qt/qt_inv.h \ + ../win/Qt/qt_kde0.h ../win/Qt/qt_main.h ../win/Qt/qt_post.h \ + ../win/Qt/qt_pre.h ../win/Qt/qt_set.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../win/Qt/qt_inv.cpp -$(TARGETPFX)qt_key.o: ../win/Qt/qt_key.cpp $(HACK_H) ../win/Qt/qt_pre.h \ - ../win/Qt/qt_post.h ../win/Qt/qt_key.h $(QTn_H) +$(TARGETPFX)qt_key.o: ../win/Qt/qt_key.cpp $(HACK_H) $(QTn_H) \ + ../win/Qt/qt_key.h ../win/Qt/qt_post.h ../win/Qt/qt_pre.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../win/Qt/qt_key.cpp -$(TARGETPFX)qt_line.o: ../win/Qt/qt_line.cpp $(HACK_H) ../win/Qt/qt_pre.h \ - ../win/Qt/qt_post.h ../win/Qt/qt_line.h $(QTn_H) +$(TARGETPFX)qt_line.o: ../win/Qt/qt_line.cpp $(HACK_H) $(QTn_H) \ + ../win/Qt/qt_line.h ../win/Qt/qt_post.h ../win/Qt/qt_pre.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../win/Qt/qt_line.cpp -$(TARGETPFX)qt_main.o: ../win/Qt/qt_main.cpp $(HACK_H) ../win/Qt/qt_pre.h \ - ../win/Qt/qt_post.h ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h \ - qt_main.moc ../win/Qt/qt_bind.h ../win/Qt/qt_glyph.h \ - ../win/Qt/qt_inv.h ../win/Qt/qt_key.h ../win/Qt/qt_map.h \ - ../win/Qt/qt_win.h ../win/Qt/qt_clust.h ../win/Qt/qt_msg.h \ - ../win/Qt/qt_set.h ../win/Qt/qt_stat.h ../win/Qt/qt_icon.h \ - ../win/Qt/qt_str.h qt_kde0.moc $(QTn_H) +$(TARGETPFX)qt_main.o: ../win/Qt/qt_main.cpp $(HACK_H) $(QTn_H) \ + ../win/Qt/qt_bind.h ../win/Qt/qt_clust.h ../win/Qt/qt_glyph.h \ + ../win/Qt/qt_icon.h ../win/Qt/qt_inv.h ../win/Qt/qt_kde0.h \ + ../win/Qt/qt_key.h ../win/Qt/qt_main.h ../win/Qt/qt_map.h \ + ../win/Qt/qt_msg.h ../win/Qt/qt_post.h ../win/Qt/qt_pre.h \ + ../win/Qt/qt_set.h ../win/Qt/qt_stat.h ../win/Qt/qt_str.h \ + ../win/Qt/qt_win.h qt_kde0.moc qt_main.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../win/Qt/qt_main.cpp -$(TARGETPFX)qt_map.o: ../win/Qt/qt_map.cpp $(HACK_H) ../win/Qt/qt_pre.h \ - ../win/Qt/qt_post.h ../win/Qt/qt_map.h ../win/Qt/qt_win.h \ - ../win/Qt/qt_clust.h qt_map.moc ../win/Qt/qt_click.h \ - ../win/Qt/qt_glyph.h ../win/Qt/qt_set.h ../win/Qt/qt_bind.h \ - ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h ../win/Qt/qt_str.h \ - $(QTn_H) +$(TARGETPFX)qt_map.o: ../win/Qt/qt_map.cpp $(HACK_H) $(QTn_H) \ + ../win/Qt/qt_bind.h ../win/Qt/qt_click.h ../win/Qt/qt_clust.h \ + ../win/Qt/qt_glyph.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 ../win/Qt/qt_str.h ../win/Qt/qt_win.h \ + qt_map.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../win/Qt/qt_map.cpp -$(TARGETPFX)qt_menu.o: ../win/Qt/qt_menu.cpp $(HACK_H) ../win/Qt/qt_pre.h \ - ../win/Qt/qt_post.h ../win/Qt/qt_menu.h ../win/Qt/qt_win.h \ - ../win/Qt/qt_rip.h qt_menu.moc ../win/Qt/qt_key.h \ - ../win/Qt/qt_glyph.h ../win/Qt/qt_set.h ../win/Qt/qt_bind.h \ - ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h ../win/Qt/qt_streq.h \ - ../win/Qt/qt_line.h ../win/Qt/qt_str.h $(QTn_H) +$(TARGETPFX)qt_menu.o: ../win/Qt/qt_menu.cpp $(HACK_H) $(QTn_H) \ + ../win/Qt/qt_bind.h ../win/Qt/qt_glyph.h ../win/Qt/qt_kde0.h \ + ../win/Qt/qt_key.h ../win/Qt/qt_line.h ../win/Qt/qt_main.h \ + ../win/Qt/qt_menu.h ../win/Qt/qt_post.h ../win/Qt/qt_pre.h \ + ../win/Qt/qt_rip.h ../win/Qt/qt_set.h ../win/Qt/qt_str.h \ + ../win/Qt/qt_streq.h ../win/Qt/qt_win.h qt_menu.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../win/Qt/qt_menu.cpp -$(TARGETPFX)qt_msg.o: ../win/Qt/qt_msg.cpp $(HACK_H) ../win/Qt/qt_pre.h \ - ../win/Qt/qt_post.h ../win/Qt/qt_msg.h ../win/Qt/qt_win.h \ - qt_msg.moc ../win/Qt/qt_map.h ../win/Qt/qt_clust.h \ - ../win/Qt/qt_set.h ../win/Qt/qt_bind.h ../win/Qt/qt_main.h \ - ../win/Qt/qt_kde0.h ../win/Qt/qt_str.h $(QTn_H) +$(TARGETPFX)qt_msg.o: ../win/Qt/qt_msg.cpp $(HACK_H) $(QTn_H) \ + ../win/Qt/qt_bind.h ../win/Qt/qt_clust.h ../win/Qt/qt_kde0.h \ + ../win/Qt/qt_main.h ../win/Qt/qt_map.h ../win/Qt/qt_msg.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_pre.h ../win/Qt/qt_set.h \ + ../win/Qt/qt_str.h ../win/Qt/qt_win.h qt_msg.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../win/Qt/qt_msg.cpp -$(TARGETPFX)qt_plsel.o: ../win/Qt/qt_plsel.cpp $(HACK_H) ../win/Qt/qt_pre.h \ - ../win/Qt/qt_post.h ../win/Qt/qt_plsel.h qt_plsel.moc \ - ../win/Qt/qt_bind.h ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h \ - ../win/Qt/qt_glyph.h ../win/Qt/qt_set.h ../win/Qt/qt_str.h \ - $(QTn_H) +$(TARGETPFX)qt_plsel.o: ../win/Qt/qt_plsel.cpp $(HACK_H) $(QTn_H) \ + ../win/Qt/qt_bind.h ../win/Qt/qt_glyph.h ../win/Qt/qt_kde0.h \ + ../win/Qt/qt_main.h ../win/Qt/qt_plsel.h ../win/Qt/qt_post.h \ + ../win/Qt/qt_pre.h ../win/Qt/qt_set.h ../win/Qt/qt_str.h \ + qt_plsel.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../win/Qt/qt_plsel.cpp -$(TARGETPFX)qt_rip.o: ../win/Qt/qt_rip.cpp $(HACK_H) ../win/Qt/qt_pre.h \ - ../win/Qt/qt_post.h ../win/Qt/qt_rip.h ../win/Qt/qt_bind.h \ - ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h ../win/Qt/qt_str.h \ - $(QTn_H) +$(TARGETPFX)qt_rip.o: ../win/Qt/qt_rip.cpp $(HACK_H) $(QTn_H) \ + ../win/Qt/qt_bind.h ../win/Qt/qt_kde0.h ../win/Qt/qt_main.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_pre.h ../win/Qt/qt_rip.h \ + ../win/Qt/qt_str.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../win/Qt/qt_rip.cpp -$(TARGETPFX)qt_set.o: ../win/Qt/qt_set.cpp $(HACK_H) ../win/Qt/qt_pre.h \ - ../win/Qt/qt_post.h ../win/Qt/qt_set.h ../win/Qt/qt_bind.h \ - ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h qt_set.moc \ - ../win/Qt/qt_glyph.h ../win/Qt/qt_xcmd.h ../win/Qt/qt_str.h \ - $(QTn_H) +$(TARGETPFX)qt_set.o: ../win/Qt/qt_set.cpp $(HACK_H) $(QTn_H) \ + ../win/Qt/qt_bind.h ../win/Qt/qt_glyph.h ../win/Qt/qt_kde0.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_set.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../win/Qt/qt_set.cpp -$(TARGETPFX)qt_stat.o: ../win/Qt/qt_stat.cpp $(HACK_H) ../win/Qt/qt_pre.h \ - ../win/Qt/qt_post.h ../win/Qt/qt_stat.h ../win/Qt/qt_win.h \ - ../win/Qt/qt_icon.h qt_stat.moc ../win/Qt/qt_set.h \ - ../win/Qt/qt_bind.h ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h \ - ../win/Qt/qt_str.h ../win/Qt/qt_xpms.h $(QTn_H) +$(TARGETPFX)qt_stat.o: ../win/Qt/qt_stat.cpp $(HACK_H) $(QTn_H) \ + ../win/Qt/qt_bind.h ../win/Qt/qt_icon.h ../win/Qt/qt_kde0.h \ + ../win/Qt/qt_main.h ../win/Qt/qt_post.h ../win/Qt/qt_pre.h \ + ../win/Qt/qt_set.h ../win/Qt/qt_stat.h ../win/Qt/qt_str.h \ + ../win/Qt/qt_win.h ../win/Qt/qt_xpms.h qt_stat.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../win/Qt/qt_stat.cpp -$(TARGETPFX)qt_str.o: ../win/Qt/qt_str.cpp ../win/Qt/qt_str.h $(QTn_H) +$(TARGETPFX)qt_str.o: ../win/Qt/qt_str.cpp $(QTn_H) ../win/Qt/qt_str.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../win/Qt/qt_str.cpp -$(TARGETPFX)qt_streq.o: ../win/Qt/qt_streq.cpp $(HACK_H) ../win/Qt/qt_pre.h \ - ../win/Qt/qt_post.h ../win/Qt/qt_streq.h ../win/Qt/qt_line.h \ - ../win/Qt/qt_str.h ../win/Qt/qt_set.h ../win/Qt/qt_bind.h \ - ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h $(QTn_H) +$(TARGETPFX)qt_streq.o: ../win/Qt/qt_streq.cpp $(HACK_H) $(QTn_H) \ + ../win/Qt/qt_bind.h ../win/Qt/qt_kde0.h ../win/Qt/qt_line.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_streq.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../win/Qt/qt_streq.cpp -$(TARGETPFX)qt_svsel.o: ../win/Qt/qt_svsel.cpp $(HACK_H) ../win/Qt/qt_pre.h \ - ../win/Qt/qt_post.h ../win/Qt/qt_svsel.h ../win/Qt/qt_bind.h \ - ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h ../win/Qt/qt_str.h \ - $(QTn_H) +$(TARGETPFX)qt_svsel.o: ../win/Qt/qt_svsel.cpp $(HACK_H) $(QTn_H) \ + ../win/Qt/qt_bind.h ../win/Qt/qt_kde0.h ../win/Qt/qt_main.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_pre.h ../win/Qt/qt_str.h \ + ../win/Qt/qt_svsel.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../win/Qt/qt_svsel.cpp -$(TARGETPFX)qt_win.o: ../win/Qt/qt_win.cpp $(HACK_H) ../win/Qt/qt_pre.h \ - ../win/Qt/qt_post.h ../win/Qt/qt_win.h ../win/Qt/qt_bind.h \ - ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h ../win/Qt/qt_click.h \ - ../win/Qt/qt_glyph.h ../win/Qt/qt_inv.h ../win/Qt/qt_key.h \ - ../win/Qt/qt_icon.h ../win/Qt/qt_map.h ../win/Qt/qt_clust.h \ - ../win/Qt/qt_menu.h ../win/Qt/qt_rip.h ../win/Qt/qt_msg.h \ - ../win/Qt/qt_set.h $(QTn_H) +$(TARGETPFX)qt_win.o: ../win/Qt/qt_win.cpp $(HACK_H) $(QTn_H) \ + ../win/Qt/qt_bind.h ../win/Qt/qt_click.h ../win/Qt/qt_clust.h \ + ../win/Qt/qt_glyph.h ../win/Qt/qt_icon.h ../win/Qt/qt_inv.h \ + ../win/Qt/qt_kde0.h ../win/Qt/qt_key.h ../win/Qt/qt_main.h \ + ../win/Qt/qt_map.h ../win/Qt/qt_menu.h ../win/Qt/qt_msg.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_pre.h ../win/Qt/qt_rip.h \ + ../win/Qt/qt_set.h ../win/Qt/qt_win.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../win/Qt/qt_win.cpp -$(TARGETPFX)qt_xcmd.o: ../win/Qt/qt_xcmd.cpp $(HACK_H) ../include/func_tab.h \ - ../win/Qt/qt_pre.h ../win/Qt/qt_post.h ../win/Qt/qt_xcmd.h \ - qt_xcmd.moc ../win/Qt/qt_key.h ../win/Qt/qt_bind.h \ - ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h ../win/Qt/qt_set.h \ - ../win/Qt/qt_str.h $(QTn_H) +$(TARGETPFX)qt_xcmd.o: ../win/Qt/qt_xcmd.cpp $(HACK_H) $(QTn_H) \ + ../include/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 $(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ ../win/Qt/qt_xcmd.cpp -$(TARGETPFX)qt_yndlg.o: ../win/Qt/qt_yndlg.cpp $(HACK_H) ../win/Qt/qt_pre.h \ - ../win/Qt/qt_post.h ../win/Qt/qt_yndlg.h qt_yndlg.moc \ - ../win/Qt/qt_key.h ../win/Qt/qt_str.h $(QTn_H) +$(TARGETPFX)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) -c -o $@ ../win/Qt/qt_yndlg.cpp -qt_kde0.moc: ../win/Qt/qt_kde0.h $(QTn_H) - $(MOCPATH) -o $@ ../win/Qt/qt_kde0.h -qt_main.moc: ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h $(QTn_H) - $(MOCPATH) -o $@ ../win/Qt/qt_main.h -qt_map.moc: ../win/Qt/qt_map.h ../win/Qt/qt_win.h ../win/Qt/qt_clust.h $(QTn_H) - $(MOCPATH) -o $@ ../win/Qt/qt_map.h -qt_menu.moc: ../win/Qt/qt_menu.h ../win/Qt/qt_win.h ../win/Qt/qt_rip.h $(QTn_H) - $(MOCPATH) -o $@ ../win/Qt/qt_menu.h -qt_msg.moc: ../win/Qt/qt_msg.h ../win/Qt/qt_win.h $(QTn_H) - $(MOCPATH) -o $@ ../win/Qt/qt_msg.h -qt_plsel.moc: ../win/Qt/qt_plsel.h $(QTn_H) - $(MOCPATH) -o $@ ../win/Qt/qt_plsel.h -qt_set.moc: ../win/Qt/qt_set.h ../win/Qt/qt_bind.h ../win/Qt/qt_main.h \ - ../win/Qt/qt_kde0.h $(QTn_H) - $(MOCPATH) -o $@ ../win/Qt/qt_set.h -qt_stat.moc: ../win/Qt/qt_stat.h ../win/Qt/qt_win.h ../win/Qt/qt_icon.h \ - $(QTn_H) - $(MOCPATH) -o $@ ../win/Qt/qt_stat.h -qt_xcmd.moc: ../win/Qt/qt_xcmd.h $(QTn_H) - $(MOCPATH) -o $@ ../win/Qt/qt_xcmd.h -qt_yndlg.moc: ../win/Qt/qt_yndlg.h $(QTn_H) - $(MOCPATH) -o $@ ../win/Qt/qt_yndlg.h -$(TARGETPFX)tile.o: tile.c $(HACK_H) +$(TARGETPFX)Window.o: ../win/X11/Window.c $(CONFIG_H) ../include/lint.h \ + ../include/winX.h ../include/wintype.h ../include/xwindow.h \ + ../include/xwindowp.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/Window.c +$(TARGETPFX)dialogs.o: ../win/X11/dialogs.c $(CONFIG_H) ../include/lint.h \ + ../include/winX.h ../include/wintype.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/dialogs.c +$(TARGETPFX)winX.o: ../win/X11/winX.c $(HACK_H) ../include/dlb.h \ + ../include/winX.h ../include/xwindow.h ../win/X11/nh32icon \ + ../win/X11/nh56icon ../win/X11/nh72icon + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winX.c +$(TARGETPFX)winmap.o: ../win/X11/winmap.c $(HACK_H) ../include/dlb.h \ + ../include/tile2x11.h ../include/winX.h ../include/xwindow.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winmap.c +$(TARGETPFX)winmenu.o: ../win/X11/winmenu.c $(HACK_H) ../include/winX.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winmenu.c +$(TARGETPFX)winmesg.o: ../win/X11/winmesg.c $(HACK_H) ../include/winX.h \ + ../include/xwindow.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winmesg.c +$(TARGETPFX)winmisc.o: ../win/X11/winmisc.c $(HACK_H) ../include/func_tab.h \ + ../include/winX.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winmisc.c +$(TARGETPFX)winstat.o: ../win/X11/winstat.c $(HACK_H) ../include/winX.h \ + ../include/xwindow.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winstat.c +$(TARGETPFX)wintext.o: ../win/X11/wintext.c $(HACK_H) ../include/winX.h \ + ../include/xwindow.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/wintext.c +$(TARGETPFX)winval.o: ../win/X11/winval.c $(HACK_H) ../include/winX.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winval.c $(TARGETPFX)wc_chainin.o: ../win/chain/wc_chainin.c $(HACK_H) $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/chain/wc_chainin.c $(TARGETPFX)wc_chainout.o: ../win/chain/wc_chainout.c $(HACK_H) $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/chain/wc_chainout.c -$(TARGETPFX)wc_trace.o: ../win/chain/wc_trace.c $(HACK_H) ../include/wintty.h \ - ../include/func_tab.h +$(TARGETPFX)wc_trace.o: ../win/chain/wc_trace.c $(HACK_H) \ + ../include/func_tab.h ../include/wintty.h $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/chain/wc_trace.c +$(TARGETPFX)cursdial.o: ../win/curses/cursdial.c $(HACK_H) \ + ../include/func_tab.h ../include/wincurs.h \ + ../win/curses/cursdial.h + $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/curses/cursdial.c +$(TARGETPFX)cursinit.o: ../win/curses/cursinit.c $(HACK_H) \ + ../include/wincurs.h ../win/curses/cursinit.h + $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/curses/cursinit.c +$(TARGETPFX)cursinvt.o: ../win/curses/cursinvt.c $(HACK_H) \ + ../include/wincurs.h ../win/curses/cursinvt.h + $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/curses/cursinvt.c +$(TARGETPFX)cursmain.o: ../win/curses/cursmain.c $(HACK_H) ../include/wincurs.h + $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/curses/cursmain.c +$(TARGETPFX)cursmesg.o: ../win/curses/cursmesg.c $(HACK_H) \ + ../include/wincurs.h ../win/curses/cursmesg.h + $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/curses/cursmesg.c +$(TARGETPFX)cursmisc.o: ../win/curses/cursmisc.c $(HACK_H) ../include/dlb.h \ + ../include/func_tab.h ../include/wincurs.h \ + ../win/curses/cursmisc.h + $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/curses/cursmisc.c +$(TARGETPFX)cursstat.o: ../win/curses/cursstat.c $(HACK_H) \ + ../include/wincurs.h ../win/curses/cursstat.h + $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/curses/cursstat.c +$(TARGETPFX)curswins.o: ../win/curses/curswins.c $(HACK_H) \ + ../include/wincurs.h ../win/curses/curswins.h + $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/curses/curswins.c +$(TARGETPFX)winshim.o: ../win/shim/winshim.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/shim/winshim.c +$(TARGETPFX)getline.o: ../win/tty/getline.c $(HACK_H) ../include/func_tab.h \ + ../include/wintty.h + $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/tty/getline.c +$(TARGETPFX)termcap.o: ../win/tty/termcap.c $(HACK_H) ../include/tcap.h \ + ../include/wintty.h + $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/tty/termcap.c +$(TARGETPFX)topl.o: ../win/tty/topl.c $(HACK_H) ../include/tcap.h \ + ../include/wintty.h + $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/tty/topl.c +$(TARGETPFX)wintty.o: ../win/tty/wintty.c $(HACK_H) ../include/dlb.h \ + ../include/tcap.h ../include/wintty.h + $(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ ../win/tty/wintty.c $(TARGETPFX)allmain.o: allmain.c $(HACK_H) -$(TARGETPFX)alloc.o: alloc.c $(CONFIG_H) +$(TARGETPFX)alloc.o: alloc.c $(CONFIG_H) ../include/nhlua.h $(TARGETPFX)apply.o: apply.c $(HACK_H) $(TARGETPFX)artifact.o: artifact.c $(HACK_H) ../include/artifact.h $(TARGETPFX)attrib.o: attrib.c $(HACK_H) $(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)cmd.o: cmd.c $(HACK_H) ../include/func_tab.h +$(TARGETPFX)coloratt.o: coloratt.c $(HACK_H) $(TARGETPFX)dbridge.o: dbridge.c $(HACK_H) $(TARGETPFX)decl.o: decl.c $(HACK_H) $(TARGETPFX)detect.o: detect.c $(HACK_H) ../include/artifact.h @@ -1101,9 +1150,9 @@ $(TARGETPFX)dog.o: dog.c $(HACK_H) $(TARGETPFX)dogmove.o: dogmove.c $(HACK_H) ../include/mfndpos.h $(TARGETPFX)dokick.o: dokick.c $(HACK_H) $(TARGETPFX)dothrow.o: dothrow.c $(HACK_H) -$(TARGETPFX)drawing.o: drawing.c $(CONFIG_H) ../include/color.h \ - ../include/rm.h ../include/objclass.h ../include/defsym.h \ - ../include/objects.h ../include/wintype.h ../include/sym.h +$(TARGETPFX)drawing.o: drawing.c $(CONFIG_H) ../include/defsym.h \ + ../include/objclass.h ../include/objects.h ../include/rm.h \ + ../include/sym.h ../include/wintype.h $(TARGETPFX)dungeon.o: dungeon.c $(HACK_H) ../include/dgn_file.h \ ../include/dlb.h $(TARGETPFX)eat.o: eat.c $(HACK_H) @@ -1114,6 +1163,8 @@ $(TARGETPFX)explode.o: explode.c $(HACK_H) $(TARGETPFX)files.o: files.c $(HACK_H) ../include/dlb.h ../include/wintty.h \ #zlib.h $(TARGETPFX)fountain.o: fountain.c $(HACK_H) +$(TARGETPFX)getpos.o: getpos.c $(HACK_H) +$(TARGETPFX)glyphs.o: glyphs.c $(HACK_H) $(TARGETPFX)hack.o: hack.c $(HACK_H) $(TARGETPFX)hacklib.o: hacklib.c $(HACK_H) $(TARGETPFX)insight.o: insight.c $(HACK_H) @@ -1124,15 +1175,16 @@ $(TARGETPFX)lock.o: lock.c $(HACK_H) $(TARGETPFX)mail.o: mail.c $(HACK_H) ../include/mail.h $(TARGETPFX)makemon.o: makemon.c $(HACK_H) $(TARGETPFX)mcastu.o: mcastu.c $(HACK_H) -$(TARGETPFX)mdlib.o: mdlib.c $(CONFIG_H) ../include/permonst.h \ - ../include/align.h ../include/monattk.h ../include/monflag.h \ - ../include/monsters.h ../include/objclass.h \ - ../include/defsym.h ../include/objects.h ../include/wintype.h \ - ../include/sym.h ../include/artilist.h ../include/dungeon.h \ - ../include/sndprocs.h ../include/seffects.h ../include/obj.h \ - ../include/monst.h ../include/mextra.h ../include/you.h \ - ../include/attrib.h ../include/prop.h ../include/skills.h \ - ../include/context.h ../include/flag.h ../include/dlb.h +$(TARGETPFX)mdlib.o: mdlib.c $(CONFIG_H) ../include/align.h \ + ../include/artilist.h ../include/attrib.h \ + ../include/context.h ../include/defsym.h ../include/dlb.h \ + ../include/dungeon.h ../include/flag.h ../include/hacklib.h \ + ../include/mextra.h ../include/monattk.h ../include/monflag.h \ + ../include/monst.h ../include/monsters.h ../include/obj.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 $(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) @@ -1143,27 +1195,28 @@ $(TARGETPFX)mkobj.o: mkobj.c $(HACK_H) $(TARGETPFX)mkroom.o: mkroom.c $(HACK_H) $(TARGETPFX)mon.o: mon.c $(HACK_H) ../include/mfndpos.h $(TARGETPFX)mondata.o: mondata.c $(HACK_H) -$(TARGETPFX)monmove.o: monmove.c $(HACK_H) ../include/mfndpos.h \ - ../include/artifact.h -$(TARGETPFX)monst.o: monst.c $(CONFIG_H) ../include/permonst.h \ - ../include/align.h ../include/monattk.h ../include/monflag.h \ - ../include/monsters.h ../include/wintype.h ../include/sym.h \ - ../include/defsym.h ../include/color.h +$(TARGETPFX)monmove.o: monmove.c $(HACK_H) ../include/artifact.h \ + ../include/mfndpos.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 $(TARGETPFX)mplayer.o: mplayer.c $(HACK_H) $(TARGETPFX)mthrowu.o: mthrowu.c $(HACK_H) $(TARGETPFX)muse.o: muse.c $(HACK_H) $(TARGETPFX)music.o: music.c $(HACK_H) -$(TARGETPFX)nhlua.o: nhlua.c $(HACK_H) ../include/dlb.h -$(TARGETPFX)nhlsel.o: nhlsel.c $(HACK_H) ../include/sp_lev.h $(TARGETPFX)nhlobj.o: nhlobj.c $(HACK_H) ../include/sp_lev.h +$(TARGETPFX)nhlsel.o: nhlsel.c $(HACK_H) ../include/sp_lev.h +$(TARGETPFX)nhlua.o: nhlua.c $(HACK_H) ../include/dlb.h +$(TARGETPFX)nhmd4.o: nhmd4.c $(HACK_H) ../include/nhmd4.h $(TARGETPFX)o_init.o: o_init.c $(HACK_H) -$(TARGETPFX)objects.o: objects.c $(CONFIG_H) ../include/obj.h \ - ../include/prop.h ../include/skills.h ../include/color.h \ - ../include/objclass.h ../include/defsym.h ../include/objects.h +$(TARGETPFX)objects.o: objects.c $(CONFIG_H) ../include/defsym.h \ + ../include/obj.h ../include/objclass.h ../include/objects.h \ + ../include/prop.h ../include/skills.h $(TARGETPFX)objnam.o: objnam.c $(HACK_H) -$(TARGETPFX)options.o: options.c $(CONFIG_H) ../include/objclass.h \ - ../include/defsym.h ../include/objects.h ../include/flag.h \ - $(HACK_H) ../include/tcap.h ../include/optlist.h +$(TARGETPFX)options.o: options.c $(CONFIG_H) $(HACK_H) ../include/defsym.h \ + ../include/flag.h ../include/objclass.h ../include/objects.h \ + ../include/optlist.h ../include/tcap.h $(TARGETPFX)pager.o: pager.c $(HACK_H) ../include/dlb.h $(TARGETPFX)pickup.o: pickup.c $(HACK_H) $(TARGETPFX)pline.o: pline.c $(HACK_H) @@ -1177,12 +1230,14 @@ $(TARGETPFX)questpgr.o: questpgr.c $(HACK_H) ../include/dlb.h \ $(TARGETPFX)read.o: read.c $(HACK_H) $(TARGETPFX)rect.o: rect.c $(HACK_H) $(TARGETPFX)region.o: region.c $(HACK_H) +$(TARGETPFX)report.o: report.c $(HACK_H) ../include/dlb.h ../include/nhmd4.h $(TARGETPFX)restore.o: restore.c $(HACK_H) ../include/tcap.h $(TARGETPFX)rip.o: rip.c $(HACK_H) $(TARGETPFX)rnd.o: rnd.c $(HACK_H) ../include/isaac64.h $(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)shk.o: shk.c $(HACK_H) $(TARGETPFX)shknam.o: shknam.c $(HACK_H) @@ -1190,18 +1245,21 @@ $(TARGETPFX)sit.o: sit.c $(HACK_H) ../include/artifact.h $(TARGETPFX)sounds.o: sounds.c $(HACK_H) $(TARGETPFX)sp_lev.o: sp_lev.c $(HACK_H) ../include/sp_lev.h $(TARGETPFX)spell.o: spell.c $(HACK_H) +$(TARGETPFX)stairs.o: stairs.c $(HACK_H) $(TARGETPFX)steal.o: steal.c $(HACK_H) $(TARGETPFX)steed.o: steed.c $(HACK_H) +$(TARGETPFX)strutil.o: strutil.c $(HACK_H) $(TARGETPFX)symbols.o: symbols.c $(HACK_H) ../include/tcap.h $(TARGETPFX)sys.o: sys.c $(HACK_H) $(TARGETPFX)teleport.o: teleport.c $(HACK_H) +$(TARGETPFX)tile.o: tile.c $(HACK_H) $(TARGETPFX)timeout.o: timeout.c $(HACK_H) $(TARGETPFX)topten.o: topten.c $(HACK_H) ../include/dlb.h $(TARGETPFX)track.o: track.c $(HACK_H) $(TARGETPFX)trap.o: trap.c $(HACK_H) $(TARGETPFX)u_init.o: u_init.c $(HACK_H) -$(TARGETPFX)utf8map.o: utf8map.c $(HACK_H) $(TARGETPFX)uhitm.o: uhitm.c $(HACK_H) +$(TARGETPFX)utf8map.o: utf8map.c $(HACK_H) $(TARGETPFX)vault.o: vault.c $(HACK_H) $(TARGETPFX)version.o: version.c $(HACK_H) ../include/dlb.h $(TARGETPFX)vision.o: vision.c $(HACK_H) @@ -1210,10 +1268,33 @@ $(TARGETPFX)were.o: were.c $(HACK_H) $(TARGETPFX)wield.o: wield.c $(HACK_H) $(TARGETPFX)windows.o: windows.c $(HACK_H) ../include/dlb.h ../include/wintty.h $(TARGETPFX)wizard.o: wizard.c $(HACK_H) +$(TARGETPFX)wizcmds.o: wizcmds.c $(HACK_H) ../include/func_tab.h $(TARGETPFX)worm.o: worm.c $(HACK_H) $(TARGETPFX)worn.o: worn.c $(HACK_H) $(TARGETPFX)write.o: write.c $(HACK_H) $(TARGETPFX)zap.o: zap.c $(HACK_H) +qt_kde0.moc: $(QTn_H) ../win/Qt/qt_kde0.h + $(MOCPATH) -o $@ ../win/Qt/qt_kde0.h +qt_main.moc: $(QTn_H) ../win/Qt/qt_kde0.h ../win/Qt/qt_main.h + $(MOCPATH) -o $@ ../win/Qt/qt_main.h +qt_map.moc: $(QTn_H) ../win/Qt/qt_clust.h ../win/Qt/qt_map.h ../win/Qt/qt_win.h + $(MOCPATH) -o $@ ../win/Qt/qt_map.h +qt_menu.moc: $(QTn_H) ../win/Qt/qt_menu.h ../win/Qt/qt_rip.h ../win/Qt/qt_win.h + $(MOCPATH) -o $@ ../win/Qt/qt_menu.h +qt_msg.moc: $(QTn_H) ../win/Qt/qt_msg.h ../win/Qt/qt_win.h + $(MOCPATH) -o $@ ../win/Qt/qt_msg.h +qt_plsel.moc: $(QTn_H) ../win/Qt/qt_plsel.h + $(MOCPATH) -o $@ ../win/Qt/qt_plsel.h +qt_set.moc: $(QTn_H) ../win/Qt/qt_bind.h ../win/Qt/qt_kde0.h \ + ../win/Qt/qt_main.h ../win/Qt/qt_set.h + $(MOCPATH) -o $@ ../win/Qt/qt_set.h +qt_stat.moc: $(QTn_H) ../win/Qt/qt_icon.h ../win/Qt/qt_stat.h \ + ../win/Qt/qt_win.h + $(MOCPATH) -o $@ ../win/Qt/qt_stat.h +qt_xcmd.moc: $(QTn_H) ../win/Qt/qt_xcmd.h + $(MOCPATH) -o $@ ../win/Qt/qt_xcmd.h +qt_yndlg.moc: $(QTn_H) ../win/Qt/qt_yndlg.h + $(MOCPATH) -o $@ ../win/Qt/qt_yndlg.h # DEPENDENCIES MUST END AT END OF FILE # IF YOU PUT STUFF HERE IT WILL GO AWAY # see make depend above diff --git a/sys/unix/Makefile.top b/sys/unix/Makefile.top index 38239f648b..cf867d75b5 100644 --- a/sys/unix/Makefile.top +++ b/sys/unix/Makefile.top @@ -1,5 +1,5 @@ # NetHack Top-level Makefile. -# NetHack 3.7 Makefile.top $NHDT-Date: 1642630921 2022/01/19 22:22:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.72 $ +# NetHack 3.7 Makefile.top $NHDT-Date: 1722119081 2024/07/27 22:24:41 $ $NHDT-Branch: keni-fetchlua $:$NHDT-Revision: 1.109 $ # Copyright (c) 2015 by Kenneth Lorber, Kensington, Maryland # NetHack may be freely redistributed. See license for details. @@ -53,7 +53,7 @@ DIRPERM = 0755 # per discussion in Install.X11 and Install.Qt # Qt prefers nhtiles.bmp but can use x11tiles, and it ignores NetHack.ad. # X11 uses those last two. -# Both can display a convential text map instead of tiles. +# Both can display a conventional text map instead of tiles. # tty and/or curses (no extra data files outside of the dlb container): #VARDATND = # All X11 and/or Qt variations may also include tty or curses or both): @@ -84,7 +84,7 @@ VARDAT = $(VARDATD) $(VARDATND) #CHGRP = chgrp # Lua version -LUA_VERSION = 5.4.4 +LUA_VERSION = 5.4.6 # # end of configuration @@ -109,6 +109,23 @@ DATNODLB = $(VARDATND) license symbols $(CSS) DATDLB = $(DATHELP) dungeon.lua tribute $(SPEC_LEVS) $(QUEST_LEVS) $(VARDATD) DAT = $(DATNODLB) $(DATDLB) +# These get set only if they weren't already set above or in a hints file +#CHOWN ?= true +#CHGRP ?= true +#SYSCONFCREATE ?= cp sys/unix/sysconf $(INSTDIR)/sysconf +#SYSCONFINSTALL ?= $(SYSCONFCREATE) && \ +# $(CHOWN) $(GAMEUID) $(INSTDIR)/sysconf && \ +# $(CHGRP) $(GAMEGRP) $(INSTDIR)/sysconf && \ +# chmod $(VARFILEPERM) $(INSTDIR)/sysconf; +#SYSCONFENSURE ?= (if ! test -f $(INSTDIR)/sysconf ; then \ +# $(SYSCONFCREATE) && \ +# $(CHOWN) $(GAMEUID) $(INSTDIR)/sysconf && \ +# $(CHGRP) $(GAMEGRP) $(INSTDIR)/sysconf && \ +# chmod $(VARFILEPERM) $(INSTDIR)/sysconf; fi ); + +RECOVERBIN = recover + +# Lua LUAHEADERS = lib/lua-$(LUA_VERSION)/src LUATESTTARGET = $(LUAHEADERS)/lua.h LUATOP = $(LUAHEADERS) @@ -117,7 +134,7 @@ LUA2NHTOP = ../../.. LUABASELIB = liblua-$(LUA_VERSION).a TOPLUALIB = lib/lua/$(LUABASELIB) -ALLDEP = $(GAME) recover Guidebook $(VARDAT) spec_levs check-dlb +ALLDEP = $(PRECHECK) $(GAME) recover Guidebook $(VARDAT) spec_levs check-dlb # first target is also the default target for 'make' without any arguments all: $(ALLDEP) @@ -130,8 +147,7 @@ $(GAME): lua_support lua_support: include/nhlua.h @true $(LUATOP)/liblua.a: $(LUAHEADERS)/lua.h - ( cd $(LUATOP) \ - && make $(LUAMAKEFLAGS) a && cd $(LUA2NHTOP) ) + ( cd $(LUATOP) && make $(LUAMAKEFLAGS) a ) $(TOPLUALIB): $(LUATOP)/liblua.a @( if test -d lib/lua ; then true ; else mkdir -p lib/lua ; fi ) cp $(LUATOP)/liblua.a $@ @@ -139,23 +155,35 @@ $(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*/' >> $@ # LUATESTTARGET is this by default lib/lua-$(LUA_VERSION)/src/lua.h: - @echo "Please do 'make fetch-lua' to obtain lua-$(LUA_VERSION)" + @echo "Please do 'make fetch-lua' in the top directory to obtain lua-$(LUA_VERSION)" @false luabin: ( cd $(LUATOP) \ && make $(LUAMAKEFILES) all && cd $(LUA2NHTOP) ) +# This is only needed for some internal tools. +nhlua: + base=`ls -td lib/lua-*|head -1` ; \ + [ -z $$base ] && $(MAKE) fetch-lua ; \ + base=`ls -td lib/lua-*|head -1` ; \ + cp -R $$base/ lib/nhlsrc ; \ + rm -f util/nhlua ; \ + ( cd lib/nhlsrc && $(MAKE) clean posix ) ; \ + cp lib/nhlsrc/src/lua util/nhlua + # hints file could set LUATESTTARGET to this if GITSUBMODULES is defined submodules/lua/lua.h: git submodule init submodules/lua - git submodule update --remote submodules/lua + git submodule update submodules/lua +# git submodule update --remote submodules/lua # Note: many of the dependencies below are here to allow parallel make # to generate valid output @@ -172,6 +200,9 @@ Guidebook.pdf: manpages: ( cd doc ; $(MAKE) manpages ) +distrib: + ( cd doc ; $(MAKE) distrib ) + data: $(GAME) ( cd dat ; $(MAKE) data ) @@ -263,7 +294,7 @@ 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) ) dofiles: target=`sed -n \ @@ -309,22 +340,75 @@ dofiles-nodlb: # # This is not part of the dependency build hierarchy. # It requires an explicit "make fetch-Lua". + +LUA_URL :=www.lua.org/ftp +LUA_URL_MIRROR :=www.tecgraf.puc-rio.br/lua/mirror/ftp +LUA_URL_NHD :=www.nethack.org/download/thirdparty +LUA_URL_list:=$(LUA_URL) $(LUA_URL_MIRROR) $(LUA_URL_NHD) + +fetch-lua-mirror: LUA_URL_list:=$(LUA_URL_MIRROR) +fetch-lua-mirror: fetch-Lua + @true + +fetch-lua-nhd: LUA_URL_list:=$(LUA_URL_NHD) +fetch-lua-nhd: fetch-Lua + @true + fetch-lua: fetch-Lua @true fetch-Lua: - ( mkdir -p lib && cd lib && \ - curl -R -O -L https://www.lua.org/ftp/lua-$(LUA_VERSION).tar.gz && \ - tar zxf lua-$(LUA_VERSION).tar.gz && \ - rm -f lua-$(LUA_VERSION).tar.gz ) + @( \ + shac1=`command -v shasum`; \ + shac2=`command -v sha256sum`; \ + if [ ! -z $$shac1 ]; then \ + shac="$$shac1 -a 256"; elif [ ! -z $$shac2 ]; then \ + shac=$$shac2; else echo "CAUTION: no way to check integrity"; \ + fi; \ + set -- DUMMY $(LUA_URL_list); \ + luafile=lua-$(LUA_VERSION).tar.gz; \ + export curlstatus=1; \ + mkdir -p lib && cd lib && \ + while [ $$# -gt 0 ]; do \ + shift; \ + if [ $$curlstatus -ne 0 ]; then \ + luaurl=https://$$1/$$luafile; \ + echo Trying $$luaurl; \ + curl -R -O $$luaurl; \ + curlstatus=$$?; \ + if [ $$curlstatus -eq 0 ]; then \ + if [ ! -z "$$shac" ]; then \ + CHKSUMS=../submodules/CHKSUMS; \ + CHKSUMSTMP=../submodules/CHKSUMS.tmp; \ + fgrep $$luafile < $$CHKSUMS > $$CHKSUMSTMP; \ + if [ -z $$CHKSUMSTMP ]; then \ + echo "Cannot check $$luafile - no checksum known"; \ + else \ + echo Checking integrity of $$luafile with $$shac; \ + $$shac -w --ignore-missing -c $$CHKSUMSTMP < $$luafile; \ + fi; \ + fi; \ + tar zxf $$luafile && \ + rm -f $$luafile; \ + fi; \ + fi; \ + done; \ + true ) + # remove include/nhlua.h in case it was created for some other Lua version @( if test -f include/nhlua.h ; then \ rm -f include/nhlua.h && echo 'rm include/nhlua.h' ; fi ) +fetch-lua-http: + ( mkdir -p lib && cd lib && \ + curl -R -O https://$(LUA_URL)/lua-$(LUA_VERSION).tar.gz && \ + tar zxf lua-$(LUA_VERSION).tar.gz && \ + rm -f lua-$(LUA_VERSION).tar.gz ) + # '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. -update: $(GAME) recover $(VARDAT) spec_levs +update: $(PRECHECK) $(GAME) recover $(VARDAT) spec_levs sys/unix/sysconf # (don't yank the old version out from under people who're playing it) -mv $(INSTDIR)/$(GAME) $(INSTDIR)/$(GAME).old -mv $(INSTDIR)/nhdat $(INSTDIR)/nhdat.old @@ -332,13 +416,15 @@ update: $(GAME) recover $(VARDAT) spec_levs ( $(MAKE) dofiles ) # should already be present, but make sure touch $(VARDIR)/perm $(VARDIR)/record +# sysconf, but only if it does not exist + true; $(SYSCONFENSURE) # and a reminder @echo You may also want to install the man pages via the doc Makefile. rootcheck: @true; $(ROOTCHECK) -install: rootcheck $(GAME) recover $(VARDAT) spec_levs +install: rootcheck $(PRECHECK) $(GAME) recover $(VARDAT) spec_levs sys/unix/sysconf true; $(PREINSTALL) # set up the directories # not all mkdirs have -p; those that don't will create a -p directory @@ -360,6 +446,9 @@ install: rootcheck $(GAME) recover $(VARDAT) spec_levs -( cd $(VARDIR) ; $(CHOWN) $(GAMEUID) perm record logfile xlogfile livelog ; \ $(CHGRP) $(GAMEGRP) perm record logfile xlogfile livelog ; \ chmod $(VARFILEPERM) perm record logfile xlogfile livelog ) +# sysconf + true; $(SYSCONFINSTALL) +# other steps from hints file true; $(POSTINSTALL) # and a reminder @echo You may also want to reinstall the man pages via the doc Makefile. diff --git a/sys/unix/Makefile.utl b/sys/unix/Makefile.utl index 6c85f7e8de..ebe974cc10 100644 --- a/sys/unix/Makefile.utl +++ b/sys/unix/Makefile.utl @@ -1,5 +1,5 @@ # Makefile for NetHack's utility programs. -# NetHack 3.7 Makefile.utl $NHDT-Date: 1602258295 2020/10/09 15:44:55 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.53 $ +# NetHack 3.7 Makefile.utl $NHDT-Date: 1693334279 2023/08/29 18:37:59 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.68 $ # Copyright (c) 2018 by Robert Patrick Rankin # NetHack may be freely redistributed. See license for details. @@ -189,13 +189,18 @@ CALLOC = ../src/alloc.c panic.c OALLOC = $(OBJDIR)/alloc.o panic.o # build time info CDATE = ../src/date.c -ODATE = $(OBJDIR)/date.o +ODATE = $(OBJDIR)/date.o + +# hacklib utility routines +HACKLIB = $(OBJDIR)/hacklib.a +HACKLIBSRC = ../src/hacklib.c +HACKLIBOBJ = $(OBJDIR)/hacklib.o panic.o # object files for makedefs MAKEOBJS = makedefs.o $(OMONOBJ) $(ODATE) $(OALLOC) # object files for recovery utility -RECOVOBJS = $(TARGETPFX)recover.o +RECOVOBJS = recover.o # object files for the data librarian DLBOBJS = dlb_main.o $(OBJDIR)/dlb.o $(OALLOC) @@ -213,11 +218,12 @@ TARGET_CLINK = $(CLINK) TARGET_LFLAGS = $(LFLAGS) TARGET_CXX = $(CXX) TARGET_CXXFLAGS = $(CXXFLAGS) +TARGET_AR = $(AR) # dependencies for makedefs # -makedefs: $(MAKEOBJS) mdgrep.h - $(CLINK) $(LFLAGS) -o makedefs $(MAKEOBJS) +makedefs: $(HACKLIB) $(MAKEOBJS) mdgrep.h + $(CLINK) $(LFLAGS) -o makedefs $(MAKEOBJS) $(HACKLIB) # note: the headers listed here are maintained manually rather than via @@ -233,6 +239,8 @@ makedefs.o: makedefs.c ../src/mdlib.c $(CONFIG_H) \ ../include/dlb.h ../include/patchlevel.h mdgrep.h $(CC) $(CFLAGS) $(CSTD) -c makedefs.c -o $@ +$(OBJDIR)/hacklib.o: $(HACKLIBSRC) + # Don't require perl to build; that is why mdgrep.h is spelled wrong below. mdgreph: mdgrep.pl perl mdgrep.pl @@ -252,6 +260,9 @@ lintdefs: ../include/date.h:: @( cd ../src ; $(MAKE) ../include/date.h ) +$(HACKLIB): $(CONFIG_H) $(HACKLIBSRC) panic.o + @( cd ../src ; $(MAKE) hacklib.a ) + # support code used by several of the utility programs (but not makedefs) panic.o: panic.c $(CONFIG_H) $(CC) $(CFLAGS) $(CSTD) -c panic.c -o $@ @@ -266,17 +277,18 @@ 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 $@ # dependencies for dlb # -dlb: $(DLBOBJS) - $(CLINK) $(LFLAGS) -o dlb $(DLBOBJS) $(LIBS) +dlb: $(DLBOBJS) $(HACKLIB) + $(CLINK) $(LFLAGS) -o dlb $(DLBOBJS) $(HACKLIB) $(LIBS) dlb_main.o: dlb_main.c $(CONFIG_H) ../include/dlb.h $(CC) $(CFLAGS) $(CSTD) -c dlb_main.c -o $@ @@ -295,30 +307,34 @@ gif2txt: $(GIFREADERS) $(TEXT_IO) txt2ppm: $(PPMWRITERS) $(TEXT_IO) $(CLINK) $(LFLAGS) -o txt2ppm $(PPMWRITERS) $(TEXT_IO) $(LIBS) -tile2x11: tile2x11.o $(TEXT_IO) - $(CLINK) $(LFLAGS) -o tile2x11 tile2x11.o $(TEXT_IO) $(LIBS) +tile2x11: tile2x11.o $(TEXT_IO) $(HACKLIB) + $(CLINK) $(LFLAGS) -o tile2x11 tile2x11.o $(TEXT_IO) \ + $(HACKLIB) $(LIBS) tile2img.ttp: tile2img.o bitmfile.o $(TEXT_IO) $(CLINK) $(LFLAGS) -o tile2img.ttp tile2img.o bitmfile.o \ $(TEXT_IO) $(LIBS) -tile2bmp: tile2bmp.o $(TEXT_IO) - $(CLINK) $(LFLAGS) -o tile2bmp tile2bmp.o $(TEXT_IO) +tile2bmp: tile2bmp.o $(TEXT_IO) $(HACKLIB) + $(CLINK) $(LFLAGS) -o tile2bmp tile2bmp.o $(TEXT_IO) $(HACKLIB) xpm2img.ttp: xpm2img.o bitmfile.o $(CLINK) $(LFLAGS) -o xpm2img.ttp xpm2img.o bitmfile.o $(LIBS) -tile2beos: tile2beos.o $(TEXT_IO) - $(CXXLINK) $(LFLAGS) -o tile2beos tile2beos.o $(TEXT_IO) -lbe +tile2beos: tile2beos.o $(TEXT_IO) $(HACKLIB) + $(CXXLINK) $(LFLAGS) -o tile2beos tile2beos.o \ + $(TEXT_IO) $(HACKLIB) -lbe #--compiling and linking in one step leaves extra debugging files (in their # own subdirectories!) on OSX; compile and link separately to suppress # that without mucking about with extra OS-specific CFLAGS and/or LFLAGS #tilemap: ../win/share/tilemap.c $(HACK_H) # $(CC) $(CFLAGS) $(LFLAGS) -o tilemap ../win/share/tilemap.c $(LIBS) -tilemap: tilemap.o $(OBJDIR)/objects.o $(OBJDIR)/monst.o $(OBJDIR)/drawing.o +tilemap: tilemap.o $(OBJDIR)/objects.o $(OBJDIR)/monst.o $(OBJDIR)/drawing.o \ + $(HACKLIB) $(CLINK) $(LFLAGS) -o tilemap tilemap.o $(OBJDIR)/objects.o \ - $(OBJDIR)/monst.o $(OBJDIR)/drawing.o $(LIBS) + $(OBJDIR)/monst.o $(OBJDIR)/drawing.o $(HACKLIB) \ + $(LIBS) ../src/tile.c: tilemap ./tilemap @@ -378,7 +394,7 @@ uudecode.o: ../sys/share/uudecode.c # make sure host object files from src are available when needed # (note: these dependencies have been copied from Makefile.src so only come # indirectly from 'make depend', hence are subject to bit rot as src changes) -$(OBJDIR)/alloc.o: ../src/alloc.c $(CONFIG_H) +$(OBJDIR)/alloc.o: ../src/alloc.c $(CONFIG_H) ../include/nhlua.h $(CC) $(CFLAGS) $(CSTD) -c ../src/alloc.c -o $@ $(OBJDIR)/drawing.o: ../src/drawing.c $(CONFIG_H) ../include/color.h \ ../include/rm.h ../include/objclass.h ../include/defsym.h \ @@ -388,8 +404,8 @@ $(OBJDIR)/decl.o: ../src/decl.c $(HACK_H) $(CC) $(CFLAGS) $(CSTD) -c ../src/decl.c -o $@ $(OBJDIR)/monst.o: ../src/monst.c $(CONFIG_H) ../include/permonst.h \ ../include/align.h ../include/monattk.h ../include/monflag.h \ - ../include/monsters.h ../include/sym.h ../include/defsym.h \ - ../include/color.h + ../include/monsters.h ../include/wintype.h ../include/sym.h \ + ../include/defsym.h ../include/color.h $(CC) $(CFLAGS) $(CSTD) -c ../src/monst.c -o $@ $(OBJDIR)/objects.o: ../src/objects.c $(CONFIG_H) ../include/obj.h \ ../include/prop.h ../include/skills.h ../include/color.h \ @@ -400,7 +416,8 @@ $(OBJDIR)/dlb.o: ../src/dlb.c $(CONFIG_H) ../include/dlb.h # this differs substantially from what Makefile.src specifies $(OBJDIR)/date.o: ../src/date.c $(CONFIG_H) $(CC) $(CFLAGS) $(CSTD) -c ../src/date.c -o $@ - +../include/nhlua.h: + @( cd .. ; $(MAKE) lua_support ) # make sure hack.h dependencies get transitive information $(HACK_H): $(CONFIG_H) @( cd ../src ; $(MAKE) $(HACK_H) ) @@ -424,7 +441,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 + -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 1de9ee5ffc..f862b1e60c 100644 --- a/sys/unix/NetHack.xcodeproj/project.pbxproj +++ b/sys/unix/NetHack.xcodeproj/project.pbxproj @@ -7,6 +7,19 @@ objects = { /* Begin PBXBuildFile section */ + 056E43C62C810EE800FD1F52 /* coloratt.c in Sources */ = {isa = PBXBuildFile; fileRef = 056E43BC2C810EE800FD1F52 /* coloratt.c */; }; + 056E43C72C810EE800FD1F52 /* nhmd4.c in Sources */ = {isa = PBXBuildFile; fileRef = 056E43C02C810EE800FD1F52 /* nhmd4.c */; }; + 056E43C82C810EE800FD1F52 /* Makefile in Sources */ = {isa = PBXBuildFile; fileRef = 056E43BF2C810EE800FD1F52 /* Makefile */; }; + 056E43C92C810EE800FD1F52 /* report.c in Sources */ = {isa = PBXBuildFile; fileRef = 056E43C12C810EE800FD1F52 /* report.c */; }; + 056E43CA2C810EE800FD1F52 /* selvar.c in Sources */ = {isa = PBXBuildFile; fileRef = 056E43C22C810EE800FD1F52 /* selvar.c */; }; + 056E43CB2C810EE800FD1F52 /* calendar.c in Sources */ = {isa = PBXBuildFile; fileRef = 056E43BB2C810EE800FD1F52 /* calendar.c */; }; + 056E43CC2C810EE800FD1F52 /* stairs.c in Sources */ = {isa = PBXBuildFile; fileRef = 056E43C32C810EE800FD1F52 /* stairs.c */; }; + 056E43CD2C810EE800FD1F52 /* glyphs.c in Sources */ = {isa = PBXBuildFile; fileRef = 056E43BE2C810EE800FD1F52 /* glyphs.c */; }; + 056E43CE2C810EE800FD1F52 /* strutil.c in Sources */ = {isa = PBXBuildFile; fileRef = 056E43C42C810EE800FD1F52 /* strutil.c */; }; + 056E43CF2C810EE800FD1F52 /* getpos.c in Sources */ = {isa = PBXBuildFile; fileRef = 056E43BD2C810EE800FD1F52 /* getpos.c */; }; + 056E43D02C810EE800FD1F52 /* wizcmds.c in Sources */ = {isa = PBXBuildFile; fileRef = 056E43C52C810EE800FD1F52 /* wizcmds.c */; }; + 059660BE2C80B00400398EDE /* hacklib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31B8A36421A238040055BD01 /* hacklib.c */; }; + 059660C12C80B0C700398EDE /* hacklib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31B8A36421A238040055BD01 /* hacklib.c */; }; 31B8A30C21A20D8B0055BD01 /* makedefs.c in Sources */ = {isa = PBXBuildFile; fileRef = 31B8A30B21A20D8B0055BD01 /* makedefs.c */; }; 31B8A30F21A20DC10055BD01 /* objects.c in Sources */ = {isa = PBXBuildFile; fileRef = 31B8A30D21A20DC10055BD01 /* objects.c */; }; 31B8A31021A20DC10055BD01 /* monst.c in Sources */ = {isa = PBXBuildFile; fileRef = 31B8A30E21A20DC10055BD01 /* monst.c */; }; @@ -243,16 +256,16 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - 3189576F21A1FCC100FB2ABE /* CopyFiles */ = { + 059660C42C80B15300398EDE /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; + buildActionMask = 8; dstPath = /usr/share/man/man1; dstSubfolderSpec = 0; files = ( ); runOnlyForDeploymentPostprocessing = 1; }; - 3189577D21A1FDA400FB2ABE /* CopyFiles */ = { + 3189576F21A1FCC100FB2ABE /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = /usr/share/man/man1; @@ -261,16 +274,16 @@ ); runOnlyForDeploymentPostprocessing = 1; }; - 31B8A44821A26A4B0055BD01 /* CopyFiles */ = { + 3189577D21A1FDA400FB2ABE /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; - dstPath = /usr/share/man/man1/; + dstPath = /usr/share/man/man1; dstSubfolderSpec = 0; files = ( ); runOnlyForDeploymentPostprocessing = 1; }; - 31B8A45521A26A970055BD01 /* CopyFiles */ = { + 31B8A44821A26A4B0055BD01 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = /usr/share/man/man1/; @@ -282,6 +295,17 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 056E43BB2C810EE800FD1F52 /* calendar.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = calendar.c; path = ../../src/calendar.c; sourceTree = ""; }; + 056E43BC2C810EE800FD1F52 /* coloratt.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = coloratt.c; path = ../../src/coloratt.c; sourceTree = ""; }; + 056E43BD2C810EE800FD1F52 /* getpos.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = getpos.c; path = ../../src/getpos.c; sourceTree = ""; }; + 056E43BE2C810EE800FD1F52 /* glyphs.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = glyphs.c; path = ../../src/glyphs.c; sourceTree = ""; }; + 056E43BF2C810EE800FD1F52 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; name = Makefile; path = ../../src/Makefile; sourceTree = ""; }; + 056E43C02C810EE800FD1F52 /* nhmd4.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = nhmd4.c; path = ../../src/nhmd4.c; sourceTree = ""; }; + 056E43C12C810EE800FD1F52 /* report.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = report.c; path = ../../src/report.c; sourceTree = ""; }; + 056E43C22C810EE800FD1F52 /* selvar.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = selvar.c; path = ../../src/selvar.c; sourceTree = ""; }; + 056E43C32C810EE800FD1F52 /* stairs.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = stairs.c; path = ../../src/stairs.c; sourceTree = ""; }; + 056E43C42C810EE800FD1F52 /* strutil.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = strutil.c; path = ../../src/strutil.c; sourceTree = ""; }; + 056E43C52C810EE800FD1F52 /* wizcmds.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = wizcmds.c; path = ../../src/wizcmds.c; sourceTree = ""; }; 2A953FB221A3F404007906E5 /* XCode.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = XCode.xcconfig; sourceTree = ""; }; 3186A36D21A4B0F90052BF02 /* xwindowp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = xwindowp.h; path = ../../include/xwindowp.h; sourceTree = ""; }; 3186A36E21A4B0FA0052BF02 /* botl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = botl.h; path = ../../include/botl.h; sourceTree = ""; }; @@ -517,40 +541,40 @@ 54FB2B4A246310A600397C0E /* symbols.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = symbols.c; path = ../../src/symbols.c; sourceTree = ""; }; 54FCE8282223261F00F393C8 /* isaac64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = isaac64.c; path = ../../src/isaac64.c; sourceTree = ""; }; BAE8010A27B97760002B3786 /* libnhlua.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libnhlua.a; sourceTree = BUILT_PRODUCTS_DIR; }; - BAE8011427B99CAB002B3786 /* lopcodes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lopcodes.c; path = "../../lib/lua-5.4.4/src/lopcodes.c"; sourceTree = ""; }; - BAE8011527B99CAB002B3786 /* linit.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = linit.c; path = "../../lib/lua-5.4.4/src/linit.c"; sourceTree = ""; }; - BAE8011627B99CAB002B3786 /* lobject.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lobject.c; path = "../../lib/lua-5.4.4/src/lobject.c"; sourceTree = ""; }; - BAE8011727B99CAC002B3786 /* loslib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = loslib.c; path = "../../lib/lua-5.4.4/src/loslib.c"; sourceTree = ""; }; - BAE8011827B99CAC002B3786 /* lcorolib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lcorolib.c; path = "../../lib/lua-5.4.4/src/lcorolib.c"; sourceTree = ""; }; - BAE8011927B99CAC002B3786 /* lutf8lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lutf8lib.c; path = "../../lib/lua-5.4.4/src/lutf8lib.c"; sourceTree = ""; }; - BAE8011A27B99CAC002B3786 /* ltable.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ltable.c; path = "../../lib/lua-5.4.4/src/ltable.c"; sourceTree = ""; }; - BAE8011B27B99CAC002B3786 /* ltablib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ltablib.c; path = "../../lib/lua-5.4.4/src/ltablib.c"; sourceTree = ""; }; - BAE8011C27B99CAC002B3786 /* llex.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = llex.c; path = "../../lib/lua-5.4.4/src/llex.c"; sourceTree = ""; }; - BAE8011D27B99CAC002B3786 /* lcode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lcode.c; path = "../../lib/lua-5.4.4/src/lcode.c"; sourceTree = ""; }; - BAE8011E27B99CAC002B3786 /* lua.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lua.c; path = "../../lib/lua-5.4.4/src/lua.c"; sourceTree = ""; }; - BAE8011F27B99CAC002B3786 /* ldo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldo.c; path = "../../lib/lua-5.4.4/src/ldo.c"; sourceTree = ""; }; - BAE8012027B99CAC002B3786 /* ldump.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldump.c; path = "../../lib/lua-5.4.4/src/ldump.c"; sourceTree = ""; }; - BAE8012127B99CAC002B3786 /* lparser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lparser.c; path = "../../lib/lua-5.4.4/src/lparser.c"; sourceTree = ""; }; - BAE8012227B99CAC002B3786 /* lstate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lstate.c; path = "../../lib/lua-5.4.4/src/lstate.c"; sourceTree = ""; }; - BAE8012327B99CAC002B3786 /* lapi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lapi.c; path = "../../lib/lua-5.4.4/src/lapi.c"; sourceTree = ""; }; - BAE8012427B99CAC002B3786 /* ldebug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldebug.c; path = "../../lib/lua-5.4.4/src/ldebug.c"; sourceTree = ""; }; - BAE8012527B99CAC002B3786 /* lstring.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lstring.c; path = "../../lib/lua-5.4.4/src/lstring.c"; sourceTree = ""; }; - BAE8012627B99CAC002B3786 /* ltm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ltm.c; path = "../../lib/lua-5.4.4/src/ltm.c"; sourceTree = ""; }; - BAE8012727B99CAC002B3786 /* lmem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lmem.c; path = "../../lib/lua-5.4.4/src/lmem.c"; sourceTree = ""; }; - BAE8012827B99CAC002B3786 /* lvm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lvm.c; path = "../../lib/lua-5.4.4/src/lvm.c"; sourceTree = ""; }; - BAE8012927B99CAC002B3786 /* lfunc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lfunc.c; path = "../../lib/lua-5.4.4/src/lfunc.c"; sourceTree = ""; }; - BAE8012A27B99CAC002B3786 /* lgc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lgc.c; path = "../../lib/lua-5.4.4/src/lgc.c"; sourceTree = ""; }; - BAE8012B27B99CAD002B3786 /* lundump.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lundump.c; path = "../../lib/lua-5.4.4/src/lundump.c"; sourceTree = ""; }; - BAE8012C27B99CAD002B3786 /* lbaselib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lbaselib.c; path = "../../lib/lua-5.4.4/src/lbaselib.c"; sourceTree = ""; }; - BAE8012D27B99CAD002B3786 /* ldblib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldblib.c; path = "../../lib/lua-5.4.4/src/ldblib.c"; sourceTree = ""; }; - BAE8012E27B99CAD002B3786 /* lauxlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lauxlib.c; path = "../../lib/lua-5.4.4/src/lauxlib.c"; sourceTree = ""; }; - BAE8012F27B99CAD002B3786 /* liolib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = liolib.c; path = "../../lib/lua-5.4.4/src/liolib.c"; sourceTree = ""; }; - BAE8013027B99CAD002B3786 /* lctype.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lctype.c; path = "../../lib/lua-5.4.4/src/lctype.c"; sourceTree = ""; }; - BAE8013127B99CAD002B3786 /* luac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = luac.c; path = "../../lib/lua-5.4.4/src/luac.c"; sourceTree = ""; }; - BAE8013227B99CAD002B3786 /* loadlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = loadlib.c; path = "../../lib/lua-5.4.4/src/loadlib.c"; sourceTree = ""; }; - BAE8013327B99CAD002B3786 /* lmathlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lmathlib.c; path = "../../lib/lua-5.4.4/src/lmathlib.c"; sourceTree = ""; }; - BAE8013427B99CAD002B3786 /* lstrlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lstrlib.c; path = "../../lib/lua-5.4.4/src/lstrlib.c"; sourceTree = ""; }; - BAE8013527B99CAD002B3786 /* lzio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lzio.c; path = "../../lib/lua-5.4.4/src/lzio.c"; sourceTree = ""; }; + BAE8011427B99CAB002B3786 /* lopcodes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lopcodes.c; path = "../../lib/lua-5.4.6/src/lopcodes.c"; sourceTree = ""; }; + BAE8011527B99CAB002B3786 /* linit.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = linit.c; path = "../../lib/lua-5.4.6/src/linit.c"; sourceTree = ""; }; + BAE8011627B99CAB002B3786 /* lobject.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lobject.c; path = "../../lib/lua-5.4.6/src/lobject.c"; sourceTree = ""; }; + BAE8011727B99CAC002B3786 /* loslib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = loslib.c; path = "../../lib/lua-5.4.6/src/loslib.c"; sourceTree = ""; }; + BAE8011827B99CAC002B3786 /* lcorolib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lcorolib.c; path = "../../lib/lua-5.4.6/src/lcorolib.c"; sourceTree = ""; }; + BAE8011927B99CAC002B3786 /* lutf8lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lutf8lib.c; path = "../../lib/lua-5.4.6/src/lutf8lib.c"; sourceTree = ""; }; + BAE8011A27B99CAC002B3786 /* ltable.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ltable.c; path = "../../lib/lua-5.4.6/src/ltable.c"; sourceTree = ""; }; + BAE8011B27B99CAC002B3786 /* ltablib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ltablib.c; path = "../../lib/lua-5.4.6/src/ltablib.c"; sourceTree = ""; }; + BAE8011C27B99CAC002B3786 /* llex.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = llex.c; path = "../../lib/lua-5.4.6/src/llex.c"; sourceTree = ""; }; + BAE8011D27B99CAC002B3786 /* lcode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lcode.c; path = "../../lib/lua-5.4.6/src/lcode.c"; sourceTree = ""; }; + BAE8011E27B99CAC002B3786 /* lua.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lua.c; path = "../../lib/lua-5.4.6/src/lua.c"; sourceTree = ""; }; + BAE8011F27B99CAC002B3786 /* ldo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldo.c; path = "../../lib/lua-5.4.6/src/ldo.c"; sourceTree = ""; }; + BAE8012027B99CAC002B3786 /* ldump.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldump.c; path = "../../lib/lua-5.4.6/src/ldump.c"; sourceTree = ""; }; + BAE8012127B99CAC002B3786 /* lparser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lparser.c; path = "../../lib/lua-5.4.6/src/lparser.c"; sourceTree = ""; }; + BAE8012227B99CAC002B3786 /* lstate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lstate.c; path = "../../lib/lua-5.4.6/src/lstate.c"; sourceTree = ""; }; + BAE8012327B99CAC002B3786 /* lapi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lapi.c; path = "../../lib/lua-5.4.6/src/lapi.c"; sourceTree = ""; }; + BAE8012427B99CAC002B3786 /* ldebug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldebug.c; path = "../../lib/lua-5.4.6/src/ldebug.c"; sourceTree = ""; }; + BAE8012527B99CAC002B3786 /* lstring.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lstring.c; path = "../../lib/lua-5.4.6/src/lstring.c"; sourceTree = ""; }; + BAE8012627B99CAC002B3786 /* ltm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ltm.c; path = "../../lib/lua-5.4.6/src/ltm.c"; sourceTree = ""; }; + BAE8012727B99CAC002B3786 /* lmem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lmem.c; path = "../../lib/lua-5.4.6/src/lmem.c"; sourceTree = ""; }; + BAE8012827B99CAC002B3786 /* lvm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lvm.c; path = "../../lib/lua-5.4.6/src/lvm.c"; sourceTree = ""; }; + BAE8012927B99CAC002B3786 /* lfunc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lfunc.c; path = "../../lib/lua-5.4.6/src/lfunc.c"; sourceTree = ""; }; + BAE8012A27B99CAC002B3786 /* lgc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lgc.c; path = "../../lib/lua-5.4.6/src/lgc.c"; sourceTree = ""; }; + BAE8012B27B99CAD002B3786 /* lundump.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lundump.c; path = "../../lib/lua-5.4.6/src/lundump.c"; sourceTree = ""; }; + BAE8012C27B99CAD002B3786 /* lbaselib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lbaselib.c; path = "../../lib/lua-5.4.6/src/lbaselib.c"; sourceTree = ""; }; + BAE8012D27B99CAD002B3786 /* ldblib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ldblib.c; path = "../../lib/lua-5.4.6/src/ldblib.c"; sourceTree = ""; }; + BAE8012E27B99CAD002B3786 /* lauxlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lauxlib.c; path = "../../lib/lua-5.4.6/src/lauxlib.c"; sourceTree = ""; }; + BAE8012F27B99CAD002B3786 /* liolib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = liolib.c; path = "../../lib/lua-5.4.6/src/liolib.c"; sourceTree = ""; }; + BAE8013027B99CAD002B3786 /* lctype.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lctype.c; path = "../../lib/lua-5.4.6/src/lctype.c"; sourceTree = ""; }; + BAE8013127B99CAD002B3786 /* luac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = luac.c; path = "../../lib/lua-5.4.6/src/luac.c"; sourceTree = ""; }; + BAE8013227B99CAD002B3786 /* loadlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = loadlib.c; path = "../../lib/lua-5.4.6/src/loadlib.c"; sourceTree = ""; }; + BAE8013327B99CAD002B3786 /* lmathlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lmathlib.c; path = "../../lib/lua-5.4.6/src/lmathlib.c"; sourceTree = ""; }; + BAE8013427B99CAD002B3786 /* lstrlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lstrlib.c; path = "../../lib/lua-5.4.6/src/lstrlib.c"; sourceTree = ""; }; + BAE8013527B99CAD002B3786 /* lzio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lzio.c; path = "../../lib/lua-5.4.6/src/lzio.c"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -626,6 +650,17 @@ 3189578C21A1FF8200FB2ABE /* src */ = { isa = PBXGroup; children = ( + 056E43BB2C810EE800FD1F52 /* calendar.c */, + 056E43BC2C810EE800FD1F52 /* coloratt.c */, + 056E43BD2C810EE800FD1F52 /* getpos.c */, + 056E43BE2C810EE800FD1F52 /* glyphs.c */, + 056E43BF2C810EE800FD1F52 /* Makefile */, + 056E43C02C810EE800FD1F52 /* nhmd4.c */, + 056E43C12C810EE800FD1F52 /* report.c */, + 056E43C22C810EE800FD1F52 /* selvar.c */, + 056E43C32C810EE800FD1F52 /* stairs.c */, + 056E43C42C810EE800FD1F52 /* strutil.c */, + 056E43C52C810EE800FD1F52 /* wizcmds.c */, 54A3D3EB282C55A900143F8C /* utf8map.c */, 54435B51247999CB00804CB3 /* nhlobj.c */, 54FB2B4A246310A600397C0E /* symbols.c */, @@ -1019,6 +1054,7 @@ 3189577C21A1FDA400FB2ABE /* Frameworks */, 3189577D21A1FDA400FB2ABE /* CopyFiles */, 317E7C4B21A35F0500F6E4E5 /* Copy makedefs */, + 059660C02C80B07100398EDE /* Codesign makedefs */, 319CBA3821A3458100150830 /* Build data */, 317E7C4521A3548F00F6E4E5 /* Build rumors */, 317E7C4E21A3697300F6E4E5 /* Build options */, @@ -1042,6 +1078,8 @@ 31B8A44621A26A4B0055BD01 /* Sources */, 31B8A44721A26A4B0055BD01 /* Frameworks */, 31B8A44821A26A4B0055BD01 /* CopyFiles */, + 059660C32C80B12300398EDE /* Copy recover */, + 059660C52C80B1A000398EDE /* Codesign recover */, ); buildRules = ( ); @@ -1059,8 +1097,9 @@ buildPhases = ( 31B8A45321A26A970055BD01 /* Sources */, 31B8A45421A26A970055BD01 /* Frameworks */, - 31B8A45521A26A970055BD01 /* CopyFiles */, - 3192867221A3AA5700325BEB /* copy dlb */, + 059660C42C80B15300398EDE /* CopyFiles */, + 3192867221A3AA5700325BEB /* Copy dlb */, + 059660C22C80B0FD00398EDE /* Codesign dlb */, ); buildRules = ( ); @@ -1081,6 +1120,7 @@ BAE8010727B97760002B3786 /* Sources */, BAE8010827B97760002B3786 /* Frameworks */, BAE8010F27B9825E002B3786 /* Build nhlua.h */, + 056E43A42C81094100FD1F52 /* Copy libnhlua.a */, ); buildRules = ( ); @@ -1140,6 +1180,97 @@ /* End PBXProject section */ /* Begin PBXShellScriptBuildPhase section */ + 056E43A42C81094100FD1F52 /* Copy libnhlua.a */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Copy libnhlua.a"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"Copying ${BUILT_PRODUCTS_DIR}/libnhlua.a to ${NH_LIB_DIR}/libnhlua.a\"\ncp \"${BUILT_PRODUCTS_DIR}\"/libnhlua.a \"${NH_LIB_DIR}\"/libnhlua.a\necho \"Copying ${BUILT_PRODUCTS_DIR}/libnhlua.a to ${NH_LIB_DIR}/libnhlua.a\"\ncp \"${BUILT_PRODUCTS_DIR}\"/libnhlua.a \"${NH_LIB_DIR}\"/libnhlua.a\n"; + }; + 059660C02C80B07100398EDE /* Codesign makedefs */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Codesign makedefs"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "codesign --force --deep -s - \"${NH_UTIL_DIR}\"/makedefs\n"; + }; + 059660C22C80B0FD00398EDE /* Codesign dlb */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Codesign dlb"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "codesign --force --deep -s - \"${NH_UTIL_DIR}\"/dlb\n"; + }; + 059660C32C80B12300398EDE /* Copy recover */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Copy recover"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "cp \"${BUILT_PRODUCTS_DIR}\"/recover \"${NH_UTIL_DIR}\"/recover\n"; + }; + 059660C52C80B1A000398EDE /* Codesign recover */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Codesign recover"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "codesign --force --deep -s - \"${NH_UTIL_DIR}\"/recover\n"; + }; 317E7C4521A3548F00F6E4E5 /* Build rumors */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1290,6 +1421,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", @@ -1474,7 +1607,7 @@ shellPath = /bin/sh; shellScript = "cp \"${BUILT_PRODUCTS_DIR}\"/nethack \"${NH_SRC_DIR}\"/nethack\n"; }; - 3192867221A3AA5700325BEB /* copy dlb */ = { + 3192867221A3AA5700325BEB /* Copy dlb */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1484,7 +1617,7 @@ inputPaths = ( "${BUILT_PRODUCTS_DIR}/dlb", ); - name = "copy dlb"; + name = "Copy dlb"; outputFileListPaths = ( ); outputPaths = ( @@ -1553,7 +1686,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "cd $NH_ROOT_DIR\necho LUA VERSION: $LUA_VERSION\nif test ! -d lib/lua-$LUA_VERSION ; then ( echo \"Fetching $LUA_VERSION\" && \\\n mkdir -p lib && cd lib && \\\n curl -s -S -R -O http://www.lua.org/ftp/lua-$LUA_VERSION.tar.gz && \\\n tar zxf lua-$LUA_VERSION.tar.gz && \\\n rm -f lua-$LUA_VERSION.tar.gz ) ; fi\n"; + shellScript = "cd $NH_ROOT_DIR\necho LUA VERSION: $LUA_VERSION\nif test ! -d lib/lua-$LUA_VERSION ; then ( echo \"Fetching $LUA_VERSION\" && \\\n mkdir -p lib && cd lib && \\\n curl -s -S -R -O https://www.lua.org/ftp/lua-$LUA_VERSION.tar.gz && \\\n tar zxf lua-$LUA_VERSION.tar.gz && \\\n rm -f lua-$LUA_VERSION.tar.gz ) ; fi\n"; }; BAE8010F27B9825E002B3786 /* Build nhlua.h */ = { isa = PBXShellScriptBuildPhase; @@ -1624,6 +1757,17 @@ 31B8A3CB21A238060055BD01 /* alloc.c in Sources */, 31B8A39821A238060055BD01 /* mail.c in Sources */, 31B8A3C821A238060055BD01 /* options.c in Sources */, + 056E43C62C810EE800FD1F52 /* coloratt.c in Sources */, + 056E43C72C810EE800FD1F52 /* nhmd4.c in Sources */, + 056E43C82C810EE800FD1F52 /* Makefile in Sources */, + 056E43C92C810EE800FD1F52 /* report.c in Sources */, + 056E43CA2C810EE800FD1F52 /* selvar.c in Sources */, + 056E43CB2C810EE800FD1F52 /* calendar.c in Sources */, + 056E43CC2C810EE800FD1F52 /* stairs.c in Sources */, + 056E43CD2C810EE800FD1F52 /* glyphs.c in Sources */, + 056E43CE2C810EE800FD1F52 /* strutil.c in Sources */, + 056E43CF2C810EE800FD1F52 /* getpos.c in Sources */, + 056E43D02C810EE800FD1F52 /* wizcmds.c in Sources */, 31B8A3CD21A238060055BD01 /* write.c in Sources */, 31B8A40F21A23EEC0055BD01 /* cursmesg.c in Sources */, 31B8A3DF21A238060055BD01 /* end.c in Sources */, @@ -1724,6 +1868,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 059660BE2C80B00400398EDE /* hacklib.c in Sources */, 544A5CF0277B40CF00734B53 /* panic.c in Sources */, 5493735A277AAE830031FE02 /* alloc.c in Sources */, 5439B3BC275AADC600B8FB2F /* date.c in Sources */, @@ -1737,6 +1882,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 059660BE2C80B00400398EDE /* hacklib.c in Sources */, 31B8A45221A26A750055BD01 /* recover.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1745,6 +1891,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 059660C12C80B0C700398EDE /* hacklib.c in Sources */, 31B8A46121A26AF60055BD01 /* panic.c in Sources */, 31B8A45E21A26ACF0055BD01 /* dlb.c in Sources */, 31B8A46021A26AE70055BD01 /* dlb_main.c in Sources */, @@ -1890,7 +2037,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = "$(SRCROOT)/../../include"; - LUA_VERSION = 5.4.4; + LUA_VERSION = 5.4.6; MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -1973,7 +2120,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = "$(SRCROOT)/../../include"; - LUA_VERSION = 5.4.4; + LUA_VERSION = 5.4.6; MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -2011,6 +2158,23 @@ GCC_C_LANGUAGE_STANDARD = c99; INSTALL_PATH = "$(NH_INSTALL_DIR)"; MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + OTHER_CFLAGS = ( + "-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", + "-DCURSES_UNICODE", + "-D_XOPEN_SOURCE_EXTENDED", + ); "OTHER_LDFLAGS[arch=*]" = "-L${NH_LIB_DIR}/lua"; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -2025,6 +2189,20 @@ GCC_C_LANGUAGE_STANDARD = c99; INSTALL_PATH = "$(NH_INSTALL_DIR)"; MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + OTHER_CFLAGS = ( + "-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", + "-DCURSES_UNICODE", + "-D_XOPEN_SOURCE_EXTENDED", + ); "OTHER_LDFLAGS[arch=*]" = "-L${NH_LIB_DIR}/lua"; PRODUCT_NAME = "$(TARGET_NAME)"; }; diff --git a/sys/unix/NewInstall.unx b/sys/unix/NewInstall.unx index 6de5b994d4..97e066df91 100644 --- a/sys/unix/NewInstall.unx +++ b/sys/unix/NewInstall.unx @@ -56,7 +56,7 @@ If you are using the traditional configuration system, see Install.unx. NetHack can support multiple user interfaces within the same binary on many platforms. The various interface options can be specified by defining specific variables, either on the make command line, or by setting environment variables -prior to the "make all" and "make install" steps. If you dont specify any +prior to the "make all" and "make install" steps. If you don't specify any WANT_WIN_* customization, the build will only include traditional tty support. As an example, to build a binary with tty + curses + X11 support, you can use @@ -83,68 +83,76 @@ Note that curses, X11, and Qt will almost certainly require the installation of prerequisite packages in order to successfully build with support for additional interfaces. See below. -+----------+---------+-----------------+--------------------------------------+ -| Platform |Interface| Build Variable | Prerequisite package | -|----------+---------+-----------------+--------------------------------------| -| MacOS | tty | WANT_WIN_TTY | none | -|----------+---------+-----------------+--------------------------------------| -| MacOS | curses | WANT_WIN_CURSES | ncurses development libraries | -|----------+---------+-----------------+--------------------------------------| -| MacOS | X11 | WANT_WIN_X11 | You will need to obtain and | -| | | | install XQuartz if you want X11 | -| | | | support in your build. | -| | | | (Attempting to run X11.app will | -| | | | describe where to get it) | -| | | | | -| | | | One possible way: | -| | | | brew install xquartz | -|----------+---------+-----------------+--------------------------------------| -| MacOS | Qt | WANT_WIN_QT | You will need to obtain and | -| | | | install Qt if you want Qt | -| | | | support in your build. | -| | | | | -| | | | One possible way: | -| | | | brew install Qt | -|----------+---------+-----------------+--------------------------------------| -| Linux | tty | WANT_WIN_TTY | | -| (Ubuntu) | | | | -|----------+---------+-----------------+--------------------------------------| -| Linux | curses | WANT_WIN_CURSES | If it isn't already included | -| (Ubuntu) | | | in your distribution, here is one | -| | | | possible way: | -| | | | | -| | | | sudo apt-get install libncurses-dev | -|----------+---------+-----------------+--------------------------------------| -| Linux | X11 | WANT_WIN_X11 | Here is one possible way to obtain | -| (Ubuntu) | | | the required packages: | -| | | | | -| | | |sudo apt-get install libx11-dev | -| | | |sudo apt-get install libmotif-dev | -| | | |sudo apt-get install libxaw7-dev | -| | | |sudo apt install xfonts-utils | -| | | |(That last one is for bdftopcf and | -| | | | mkfontdir utils) | -| | | | | -|----------+---------+-----------------+--------------------------------------| -| Linux | Qt | WANT_WIN_QT | Here is one possible way to obtain | -| (Ubuntu) | | | the required packages: | -| | | | | -| | | |sudo apt-get install qtbase5-dev | -| | | |sudo apt-get install qtmultimedia5-dev| -| | | | | -| | | |Another odd note about Qt on Linux is | -| | | |that if you find you are getting the | -| | | |following error trying to run NetHack | -| | | |after you build it with Qt support: | -| | | |"error while loading shared | -| | | |libraries: libQt5Core.so.5: cannot | -| | | |open shared object file: No such file | -| | | |or directory", | -| | | |you may have to fix that (one-time) | -| | | |by issuing the command below: | -| sudo strip \ | -| --remove-section=.note.ABI-tag/usr/lib/x86_64-linux-gnu/libQt5Core.so.5 | -+----------+---------+-----------------+--------------------------------------+ ++----------+-------+-----------------+--------------------------------------+ +| Platform |Interf.| Build Variable | Prerequisite package | +|----------+-------+-----------------+--------------------------------------| +| MacOS | tty | WANT_WIN_TTY | none | +|----------+-------+-----------------+--------------------------------------| +| MacOS | curses| WANT_WIN_CURSES | ncurses development libraries | +|----------+-------+-----------------+--------------------------------------| +| MacOS | X11 | WANT_WIN_X11 | You will need to obtain and | +| | | | install XQuartz if you want X11 | +| | | | support in your build. | +| | | | (Attempting to run X11.app will | +| | | | describe where to get it) | +| | | | | +| | | | One possible way: | +| | | | brew install xquartz | +|----------+-------+-----------------+--------------------------------------| +| MacOS | Qt | WANT_WIN_QT | You will need to obtain and | +| | | | install Qt if you want Qt | +| | | | support in your build. | +| | | | | +| | | | One possible way: | +| | | | brew install Qt | +|----------+-------+-----------------+--------------------------------------| +| Linux | tty | WANT_WIN_TTY | | +| (Ubuntu) | | | | +|----------+-------+-----------------+--------------------------------------| +| Linux | curses|WANT_WIN_CURSES=1| If it isn't already included | +| (Ubuntu) | | | in your distribution, here is one | +| | | | possible way: | +| | | | | +| | | | sudo apt-get install libncurses-dev | +|----------+-------+-----------------+--------------------------------------| +| Linux | X11 | WANT_WIN_X11=1 | Here is one possible way to obtain | +| (Ubuntu) | | | the required packages: | +| | | | | +| | | |sudo apt-get install libx11-dev | +| | | |sudo apt-get install libmotif-dev | +| | | |sudo apt-get install libxaw7-dev | +| | | |sudo apt install xfonts-utils | +| | | |(That last one is for bdftopcf and | +| | | | mkfontdir utils) | +| | | | | +|----------+-------+-----------------+--------------------------------------| +| Linux | Qt5 | WANT_WIN_QT=1 | Here is one possible way to obtain | +| (Ubuntu) | | | the required packages: | +| | | | | +| | | |sudo apt-get install qtbase5-dev | +| | | |sudo apt-get install qtmultimedia5-dev| +| | | | | +| | | |Another odd note about Qt on Linux is | +| | | |that if you find you are getting the | +| | | |following error trying to run NetHack | +| | | |after you build it with Qt support: | +| | | |"error while loading shared | +| | | |libraries: libQt5Core.so.5: cannot | +| | | |open shared object file: No such file | +| | | |or directory", | +| | | |you may have to fix that (one-time) | +| | | |by issuing the command below: | +| | | | | +| sudo strip \ | +| --remove-section=.note.ABI-tag/usr/lib/x86_64-linux-gnu/libQt5Core.so.5 | +|----------+-------+-----------------+--------------------------------------| +| Linux | Qt6 | WANT_WIN_QT6=1 | Here is one possible way to obtain | +| (Ubuntu) | | | the required packages: | +| | | | | +| | | |sudo apt-get install qt6-base-dev | +| | | |sudo apt-get install \ | +| | | | qt6-multimedia-dev | ++----------+-------+-----------------+--------------------------------------+ # NetHack 3.7 NewInstall.unx $NHDT-Date: 1652276817 2022/05/11 13:46:57 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.11 $ # Copyright (c) 2009 by Kenneth Lorber, Kensington, Maryland diff --git a/sys/unix/README-hints b/sys/unix/README-hints new file mode 100644 index 0000000000..5a531efbd5 --- /dev/null +++ b/sys/unix/README-hints @@ -0,0 +1,74 @@ +The sys/unix/hints subfolder contains Makefile content building blocks +for various platforms and purposes. + +The sys/unix/hints/include subfolder contains Makefile content that +is included in the distributed Makefiles. + + +[.370 hints] + + +There are two .370 series hints files provided with NetHack: + linux.370 for use on Linux + macOS.370 for use on macOS + +Those will also pull in content from sys/unix/hints/include/*.370, +that is shared between the linux.370 and macOS.370 hints files. + + +[.370 make build options] + + +Here are some options that can be specified on the make command +line to trigger specific build behavior. These are not mutually +exclusive and multiple build options can be specified in many +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_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. +make WANT_WIN_QT6=1 Include support for Qt6 interface. Only one Qtx + option can be specified. +make WANT_WIN_ALL=1 Include support for TTY,curses,X11, and Qt. +make WANT_WINCHAIN=1 Include support for WINCHAIN. +make WANT_LIBNH=1 Include support for libnh NetHack library. + +make WANT_DEFAULT=tty Make tty the default interface if multiple window + interfaces were specified. +make WANT_DEFAULT=curses Make curses the default interface if multiple window + interfaces were specified. +make WANT_DEFAULT=X11 Make X11 the default interface if multiple window + interfaces were specified. +make WANT_DEFAULT=Qt Make Qt the default interface if multiple window + interfaces were specified. + +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. + +make WANT_BUNDLE=1 For macOS.370 only, place the build results into + a macOS bundle layout. +make WANT_MACSOUND=1 For macOS.370, include support for built-in macOS + sound libraries. +make WANT_SPEECH=1 If one of the sound libraries was included, also + include speech synthesis support (Recent macOS + build that also includes WANT_MACSOUND=1). + +make WANT_ASAN=1 Include support for the address sanitizer in the + build. + +make WANT_UBSAN=1 Include support for the undefined behaviour + sanitizer in the build. + +make WANT_SHARE_INSTALL=1 Place the results of the install/update portion + of the build into a shared area on a multiuser + system. + +make WANT_SOURCE_INSTALL=1 Place the results of the install/update portion + of the build into a subfolder of the source + 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/XCode.xcconfig b/sys/unix/XCode.xcconfig index 06ce911fa6..212b93f897 100644 --- a/sys/unix/XCode.xcconfig +++ b/sys/unix/XCode.xcconfig @@ -16,7 +16,7 @@ // // Your DEVELOPMENT_TEAM can be found by opening Keychain Access // (found by Finder->Applications->Utilities). Click on "Login" key chain. -// Click on catagory "My Certificates". +// Click on category "My Certificates". // // Look for your "Mac Developer" certificate. Double click on // the certificate to open a dialog that shows certificate details. diff --git a/sys/unix/depend.awk b/sys/unix/depend.awk index b62c4e02f0..afe86a0ad3 100644 --- a/sys/unix/depend.awk +++ b/sys/unix/depend.awk @@ -1,6 +1,6 @@ # depend.awk -- awk script used to construct makefile dependencies # for nethack's source files (`make depend' support for Makefile.src). -# $NHDT-Date: 1612127123 2021/01/31 21:05:23 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.13 $ +# $NHDT-Date: 1709577497 2024/03/04 18:38:17 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.18 $ # # usage: # awk -f depend.awk ../include/*.h list-of-.c/.cpp-files @@ -9,6 +9,8 @@ # ( cd src ; make all ; cp ../sys/unix/Makefile.src ./Makefile ; \ # make depend ; cp ./Makefile ../sys/unix/Makefile.src ; \ # cd .. ; sh sys/unix/setup.sh [sys/unix/hints/FOO] ) +# newer usage: +# cd sys/unix ; make -f Makefile.src updatedepend # # This awk program scans each file in sequence, looking for lines beginning # with `#include "' and recording the name inside the quotes. For .h files, @@ -16,6 +18,10 @@ # corresponding .o file; dependencies in nested header files are propagated # to the .o target. # +# Variables that can be set on the command line: +# -v dontsortdeps=1 do not sort the dependencies +# -v dontsortfules=1 do not sort the rules +# # config.h and hack.h get special handling because of their heavy use; # timestamps for them allow make to avoid rechecking dates on # subsidiary headers for every source file; @@ -73,7 +79,10 @@ FNR == 1 { output_dep() #finish previous file } deps[file] = deps[file] " " incl } -END { output_dep() } #finish the last file +END { + output_dep() #finish the last file + output_final() #write output + } # @@ -81,7 +90,29 @@ END { output_dep() } #finish the last file # don't do anything (we've just been collecting their dependencies); # for .c files, output the `make' rule for corresponding .o file # -function output_dep( base, targ, moc) + +function output_dep(){ + if(!dontsortrules){ + worklist[++worklistctr] = file + } else { + output_final2() + } +} + +function output_final( x) +{ + if(!dontsortrules){ + nhsort(worklist, 1, worklistctr, 1) + for(x=1;x<=worklistctr;x++){ + file = worklist[x] + output_final2() + } + } else { + return + } +} + +function output_final2( base, targ, moc) { #get the file's base name (including suffix) base = file; sub("^.+/", "", base) @@ -90,7 +121,7 @@ function output_dep( base, targ, moc) if (moc || base ~ /(.+\/)*qt_.*[.]cpp$/) { deps[file] = deps[file] " $(QTn_H)" } - if (base ~ /[.]cp*$/ || moc) { + if ((base ~ /[.]cp*$/ || moc) && !(file in filedone)) { #prior to very first .c|.cpp file, handle some special header file cases if (!c_count++) output_specials() @@ -98,6 +129,9 @@ function output_dep( base, targ, moc) targ = base; sub("[.]cp*$", ".o", targ) #format and write the collected dependencies format_dep(targ, file) + #generated file tile.c can appear more than once in the list of files + #so track which files have already been handled; can't reuse done[] here + filedone[file]++; } } @@ -139,6 +173,10 @@ function format_dep(target, source, col, n, i, list, prefix, moc) #first: leading whitespace yields empty 1st element; not sure why moc #files duplicate the target as next element but we need to skip that too first = moc ? 3 : 2 + source = list[first] + if (!dontsortdeps){ + nhsort(list, first, n, 0) + } for (i = first; i <= n; i++) { if (col + length(list[i]) >= (i < n ? 78 : 80) - 1) { printf(" \\\n\t\t"); col = 16 #make a backslash+newline split @@ -149,7 +187,6 @@ function format_dep(target, source, col, n, i, list, prefix, moc) } printf("\n") #terminate #write build command if first source entry has non-include path prefix - source = list[first] if (moc) { print "\t$(MOCPATH) -o $@ " source } else if (source ~ /\// && substr(source, 1, 11) != "../include/") { @@ -194,4 +231,58 @@ function depend(inout, name, skip, n, i, list) return inout } +# +# sort list[first]..list[last] +# Derived from: https://www.baeldung.com/linux/awk-begin-and-end-rules +# +function nhsort(list, first, last, cmpid, i,j,temp) +{ + for (i = first; i <= last-1; i++) { + for (j = i+1; j <= last; j++) { + if (nhcmp(list[i], list[j], cmpid)) { + temp = list[i] + list[i] = list[j] + list[j] = temp + } + } + } +} + +function nhcmp(a,b,cmpid) +{ + if(cmpid == 0){ # sort dependencies + # commented out entry (there can be only one) MUST be last + if (a ~ /^#/){ return 1} + if (b ~ /^#/){ return 0} + # 2 .c or .cpp files + if (a ~ /\.c(pp)?$/ && b ~ /\.c(pp)?$/ ){ return a > b } + # a .c or .cpp file and anything else + if (a ~ /\.c(pp)?$/){ return 0 } + if (b ~ /\.c(pp)?$/){ return 1 } + # default + return a > b + } else if(cmpid == 1){ # sort rules + # 2 .h files + if (a ~ /\.h$/ && b ~ /\/.h$/){ return a > b } + # a .h and anything else + if (a ~ /\.h$/){ return 0 } + if (b ~ /\.h$/){ return 1 } + # 2 .c or .cpp files + if (a ~ /\.c(pp)?$/ && b ~ /\.c(pp)?$/ ){ return a > b } + # a .c or .cpp file and anything else + if (a ~ /\.c(pp)?$/){ return 0 } + if (b ~ /\.c(pp)?$/){ return 1 } + # 2 .moc files + if (a ~ /\.moc$/ && b ~ /\.moc$/){ return a > b } + # a .moc and anything else + if (a ~ /\.moc$/){ return 0 } + if (b ~ /\.moc$/){ return 1 } + # default + return a > b + } else { + print "internal error cmpid=" cmpid + exit 1 + } +} + #depend.awk# diff --git a/sys/unix/hints/include/compiler.370 b/sys/unix/hints/include/compiler.370 index bfeefca787..62486d3ad7 100755 --- a/sys/unix/hints/include/compiler.370 +++ b/sys/unix/hints/include/compiler.370 @@ -33,6 +33,10 @@ ifeq "$(WANT_ASAN)" "1" USE_ASAN=1 endif +ifeq "$(WANT_UBSAN)" "1" +USE_UBSAN=1 +endif + # If you want to override the compiler detection just carried out # uncomment one of the following pairs. Note, however, that # doing this after the detection above will likely result in @@ -45,10 +49,11 @@ endif #CXX=clang++ -std=gnu++11 CFLAGS=$(CCFLAGS) -I../include -DNOTPARMDECL -CFLAGS+=-Wall -Wextra -Wno-missing-field-initializers \ +CFLAGS+=-Wall -Wextra \ -Wreturn-type -Wunused -Wformat -Wswitch -Wshadow -Wwrite-strings CFLAGS+=-pedantic CFLAGS+=-Wmissing-declarations +#CFLAGS+=-Wformat=2 # these are left out of the C++ flags CFLAGS+=-Wformat-nonliteral @@ -62,6 +67,7 @@ CFLAGS+=-Wimplicit-int CFLAGS+=-Wmissing-prototypes CFLAGS+=-Wold-style-definition CFLAGS+=-Wstrict-prototypes +CFLAGS+=-Wnonnull #detection of clang vs gcc CCISCLANG := $(shell echo `$(CC) --version` | grep clang) @@ -72,22 +78,35 @@ 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 +endif # not VIEWDEPRECATIONS +else +# older versions complain about things newer ones don't without this +CFLAGS+=-Wno-missing-field-initializers endif # none endif # clang-specific ends here @@ -108,11 +127,14 @@ 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 +ifneq "$(VIEWDEPRECATIONS)" "1" CCXXFLAGS+=-Wno-deprecated-copy CCXXFLAGS+=-Wno-deprecated-declarations +endif # not VIEWDEPRECATIONS endif # CPLUSPLUS_NEED_DEPSUPPRESS endif # g++ version greater than or equal to 9 ifeq "$(GPPGTEQ11)" "1" @@ -122,7 +144,7 @@ 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 @@ -136,6 +158,8 @@ 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 endif @@ -143,18 +167,21 @@ ifeq "$(CLANGPPGTEQ14)" "1" CPLUSPLUS_NEED_DEPSUPPRESS=1 endif ifdef CPLUSPLUS_NEED_DEPSUPPRESS +ifneq "$(VIEWDEPRECATIONS)" "1" CCXXFLAGS+=-Wno-deprecated CCXXFLAGS+=-Wno-deprecated-declarations +endif # not VIEWDEPRECATIONS endif # CPLUSPLUS_NEED_DEPSUPPRESS # The clang++ linker seems to have trouble linking if the following isn't # included when compiling the C files by clang.. CFLAGS+=-fPIC ifdef CPLUSPLUS_NEED17 ifeq "$(CLANGPPGTEQ14)" "1" -CCXX=clang++ -std=c++20 -else CCXX=clang++ -std=c++17 endif # clang++ greater than or equal to 14 +ifeq "$(CLANGPPGTEQ17)" "1" +CCXX=clang++ -std=c++17 +endif # clang++ greater than or equal to 17 endif # CPLUSPLUS_NEED17 endif # end of clang++-specific section CXX=$(CCXX) diff --git a/sys/unix/hints/include/cross-post.370 b/sys/unix/hints/include/cross-post.370 index b2f92f5c39..93f01f9dcb 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) $(LUACROSSLIB) - $(TARGET_LINK) $(TARGET_LFLAGS) -o $(GAMEBIN) \ - $(HOBJ) $(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) @@ -65,6 +65,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/target/bin/symify.exe ]; then \ + cp ../lib/djgpp/target/bin/symify.exe $(TARGETPFX)pkg/SYMIFY.EXE; \ + else \ + 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 @@ -73,14 +96,65 @@ $(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 -$(WASM_TARGET): pregame $(TARGETPFX)date.o $(HOSTOBJ) $(HOBJ) $(LUACROSSLIB) $(WASM_DATA_DIR) +$(WASM_TARGET): pregame $(TARGET_HACKLIB) $(TARGETPFX)date.o $(HOSTOBJ) $(HOBJ) $(LUACROSSLIB) $(WASM_DATA_DIR) -rm $@ $(TARGET_CC) $(TARGET_LFLAGS) $(TARGET_CFLAGS) -o $@ \ - $(HOBJ) $(TARGETPFX)date.o $(TARGET_LIBS) + $(HOBJ) $(TARGETPFX)date.o $(TARGET_HACKLIB) $(TARGET_LIBS) $(WASM_DATA_DIR): $(WASM_DATA_DIR)/nhdat touch $(WASM_DATA_DIR)/perm @@ -102,7 +176,57 @@ $(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) \ + $(TARGETPFX)ncurses/lib/libncurses.a \ + $(MIPS_DATA_DIR) + -rm $@ + $(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 + touch $(MIPS_DATA_DIR)/record + touch $(MIPS_DATA_DIR)/logfile + touch $(MIPS_DATA_DIR)/xlogfile + touch $(MIPS_DATA_DIR)/livelog + cp ../sys/unix/sysconf $(MIPS_DATA_DIR)/sysconf + +$(MIPS_DATA_DIR)/nhdat: + ( cd ..; $(MAKE) INSTDIR='$(MIPS_DATA_DIR)' $(MIPSDEP) dofiles-dlb ) + # +$(TARGETPFX)unixmain.o : ../sys/unix/unixmain.c $(HACK_H) +$(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) + +$(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 @@ -113,11 +237,26 @@ $(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)hacklib.a + $(TARGET_LINK) $(TARGET_LFLAGS) \ + $(TARGETPFX)recover.o $(TARGETPFX)hacklib.a -o $@ +else +$(TARGETPFX)recover : $(TARGETPFX)recover.o $(TARGETPFX)hacklib.a + $(TARGET_LINK) $(TARGET_LFLAGS) \ + $(TARGETPFX)recover.o $(TARGETPFX)hacklib.a -o $@ +endif +endif # MAKEFILE_UTL +endif # CROSS + ifdef BUILD_TARGET_LUA # Lua lib $(LUACROSSLIB): $(LUALIBOBJS) @@ -165,12 +304,31 @@ $(TARGETPFX)lvm.o : $(LUATOP)/src/lvm.c $(TARGETPFX)lzio.o : $(LUATOP)/src/lzio.c endif # BUILD_TARGET_LUA +ifdef BUILD_TARGET_NCURSES +.PHONY: build-ncurses +ifdef MAKEFILE_SRC +../lib/ncurses.tar.gz: + @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 $(NCURSES_CONFIGURE_BUILD) --host $(NCURSES_CONFIGURE_HOST) ; \ + make ; \ + cd ../../../src) +endif #MAKEFILE_SRC +ifdef MAKEFILE_TOP +.PHONY: fetch-ncurses +fetch-ncurses: + (cd lib ; curl https://invisible-island.net/datafiles/release/ncurses.tar.gz --output ncurses.tar.gz ; cd ..) +endif +endif #BUILD_TARGET_NCURSES + ifdef BUILD_PDCURSES ifdef WANT_WIN_CURSES $(TARGETPFX)pdclib.a : $(PDCLIBOBJS) $(PDCOBJS) if [ -f $@ ]; then rm $@; fi; $(TARGET_AR) rcs $@ $(PDCLIBOBJS) $(PDCOBJS) -endif +endif #WANT_WIN_CURSES # PDCurses src $(TARGETPFX)addch.o : $(PDCTOP)/pdcurses/addch.c $(TARGETPFX)addchstr.o : $(PDCTOP)/pdcurses/addchstr.c @@ -219,9 +377,10 @@ $(TARGETPFX)pdckbd.o : $(PDCPORT)/pdckbd.c $(TARGETPFX)pdcscrn.o : $(PDCPORT)/pdcscrn.c $(TARGETPFX)pdcsetsc.o : $(PDCPORT)/pdcsetsc.c $(TARGETPFX)pdcutil.o : $(PDCPORT)/pdcutil.c + $(TARGET_CC) $(PDCINCL) $(PDC_TARGET_CFLAGS) \ + -Wno-sign-compare -o$@ $(PDCPORT)/pdcutil.c +# -Wno-sign-compare endif # BUILD_PDCURSES # # End of cross-compiling -POST section #===============-================================================= - - diff --git a/sys/unix/hints/include/cross-pre1.370 b/sys/unix/hints/include/cross-pre1.370 new file mode 100644 index 0000000000..203c06048a --- /dev/null +++ b/sys/unix/hints/include/cross-pre1.370 @@ -0,0 +1,49 @@ +#===============-================================================= +# NetHack 3.7 include/cross-pre1. $NHDT-Date: 1597332785 2020/08/13 15:33:05 $ $NHDT-Branch: NetHack-3.7 $ +# +# Cross-compiling -PRE section 1 +# + +ifdef CROSS_TO_MSDOS +CROSS=1 +BUILD_TARGET_LUA=1 +BUILD_PDCURSES=1 +CROSS_SHARED=1 +override TARGET = msdos +override TARGETDIR=../targets/$(TARGET) +override TARGETPFX = $(TARGETDIR)/ +override TARGET_LIBS= +endif + +ifdef CROSS_TO_WASM +CROSS=1 +BUILD_TARGET_LUA=1 +HACKDIR=/ +PREFIX= +override TARGET = wasm +override TARGETDIR=../targets/$(TARGET) +override TARGETPFX = $(TARGETDIR)/ +override TARGET_LIBS= +endif + +ifdef CROSS_TO_MIPS +CROSS=1 +BUILD_TARGET_LUA=1 +BUILD_TARGET_NCURSES=1 +HACKDIR=/ +PREFIX= +override TARGET = mips +override TARGETDIR=../targets/$(TARGET) +override TARGETPFX = $(TARGETDIR)/ +override TARGET_LIBS= +endif + +ifdef CROSS +override PREGAME= +override BUILDMORE= +override CLEANMORE= +override PACKAGE= +endif +# End of cross-compiling -PRE section 1 +#===============-================================================= + diff --git a/sys/unix/hints/include/cross-pre.370 b/sys/unix/hints/include/cross-pre2.370 similarity index 68% rename from sys/unix/hints/include/cross-pre.370 rename to sys/unix/hints/include/cross-pre2.370 index 2bd09ba31f..9730be4dad 100644 --- a/sys/unix/hints/include/cross-pre.370 +++ b/sys/unix/hints/include/cross-pre2.370 @@ -1,44 +1,15 @@ #===============-================================================= -# NetHack 3.7 include/cross-pre $NHDT-Date: 1597332785 2020/08/13 15:33:05 $ $NHDT-Branch: NetHack-3.7 $ +# NetHack 3.7 include/cross-pre2 $NHDT-Date: 1597332785 2020/08/13 15:33:05 $ $NHDT-Branch: NetHack-3.7 $ # -# Cross-compiling -PRE section +# Cross-compiling -PRE section 2 # -ifdef CROSS_TO_MSDOS -CROSS=1 -BUILD_TARGET_LUA=1 -BUILD_PDCURSES=1 -CROSS_SHARED=1 -override TARGET = msdos -override TARGETDIR=../targets/$(TARGET) -override TARGETPFX = $(TARGETDIR)/ -override TARGET_LIBS= -endif - -ifdef CROSS_TO_WASM -CROSS=1 -BUILD_TARGET_LUA=1 -HACKDIR=/ -PREFIX= -override TARGET = wasm -override TARGETDIR=../targets/$(TARGET) -override TARGETPFX = $(TARGETDIR)/ -override TARGET_LIBS= -endif - -ifdef CROSS -override PREGAME= -override BUILDMORE= -override CLEANMORE= -override PACKAGE= -endif - ifdef BUILD_TARGET_LUA #===============-================================================= # LUA library -# Source from http://www.lua.org/ftp/lua-5.4.4.tar.gz +# Source from http://www.lua.org/ftp/lua-5.4.6.tar.gz #================================================================= -LUA_VERSION ?=5.4.4 +LUA_VERSION ?=5.4.6 LUATOP ?= ../lib/lua-$(LUA_VERSION) LUASRCDIR ?= $(LUATOP)/src LUAOBJFILES1 = $(TARGETPFX)lapi.o $(TARGETPFX)lauxlib.o \ @@ -64,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 @@ -74,14 +46,13 @@ ifdef BUILD_PDCURSES # PD Curses library #===============-================================================= ifdef WANT_WIN_CURSES -ifdef WANT_DOSVGA PDCTOP = ../lib/pdcursesmod +ifdef WANT_DOSVGA PDCPORT = $(PDCTOP)/dosvga PDCURSESDEF= -I$(PDCTOP) -I$(PDCPORT) \ -D"CURSES_GRAPHICS" -D"CURSES_BRIEF_INCLUDE" \ - -D"PDC_WIDE" -D"CURSES_UNICODE" + -D"PDC_WIDE" -D"PDC_RGB" -D"CURSES_UNICODE" else -PDCTOP = ../lib/pdcurses PDCPORT = $(PDCTOP)/dos PDCURSESDEF= -I$(PDCTOP) -I$(PDCPORT) \ -D"CURSES_GRAPHICS" -D"CURSES_BRIEF_INCLUDE" @@ -114,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 @@ -127,10 +96,22 @@ PDCINCL= endif # WANT_WIN_CURSES endif # BUILD_PDCURSES +ifdef BUILD_TARGET_NCURSES +#================================================================= +# ncurses +# Source from https://invisible-island.net/datafiles/release/ncurses.tar.gz +# +#================================================================= +NCURSESLIBDIR ?= $(TARGETPFX)ncurses/lib +NCURSESLIB ?= $(NCURSESLIBDIR)/libncurses.a +#override TARGET_LIBS += $(NCURSESLIB) +override BUILDMORE += $(NCURSESLIB) +endif # BUILD_TARGET_NCURSES + ifdef CROSS_TO_MSDOS -#===============-================================================= +#================================================================= # MSDOS cross-compile recipe -#===============-================================================= +#================================================================= # Uses an MSDOS djgpp cross-compiler on linux or macos. # # 1. You can obtain the cross-compiler for your system via: @@ -138,7 +119,7 @@ ifdef CROSS_TO_MSDOS # 2. Then # make CROSS_TO_MSDOS=1 WANT_WIN_TTY=1 WANT_WIN_CURSES=1 all # -# Source from http://www.lua.org/ftp/lua-5.4.4.tar.gz +# Source from http://www.lua.org/ftp/lua-5.4.6.tar.gz #================================================================= CFLAGS += -DCROSSCOMPILE @@ -153,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 @@ -178,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++ @@ -188,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 \ @@ -210,9 +227,21 @@ override LUALIB= override LUALIBS= override TOPLUALIB= override GAMEBIN = $(TARGETPFX)nethack.exe +override RECOVERBIN = $(TARGETPFX)recover.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 @@ -234,7 +263,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 = @@ -245,10 +274,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)@/ @@ -293,7 +325,8 @@ WASM_CFLAGS += -Wshadow # Nethack C flags WASM_CFLAGS += $(WINCFLAGS) #WINCFLAGS set from multiw-2.370 WASM_CFLAGS += -DSYSCF -DSYSCF_FILE=\"/sysconf\" -DSECURE -WASM_CFLAGS += -g -I../include -DNOTPARMDECL +#WASM_CFLAGS += -g -I../include -DNOTPARMDECL +WASM_CFLAGS += -I../include -DNOTPARMDECL # NetHack sources control WASM_CFLAGS += -DDLB WASM_CFLAGS += -DHACKDIR=\"$(HACKDIR)\" @@ -328,7 +361,7 @@ override WINOBJ= # don't bother Making regular NetHack executable override GAME= # the real VARDAT hasn't been defined yet for use in ALLDEP override -WASM_DAT = bogusmon data engrave epitaph oracles options quest.lua rumors +WASM_DAT = bogusmon data engrave epitaph oracles quest.lua rumors WASMDEP = include/nhlua.h $(WASM_DAT) spec_levs check-dlb override ALLDEP = $(WASMDEP) wasm override PREGAME += mkdir -p $(TARGETDIR)/wasm-data ; @@ -345,6 +378,81 @@ $(TARGETPFX)%.o : ../sys/libnh/%.c $(TARGETPFX)%.o : ../win/shim/%.c $(TARGET_CC) $(TARGET_CFLAGS) -c -o$@ $< endif # CROSS_TO_WASM + + +ifdef CROSS_TO_MIPS +#================================================================ +# MIPS cross-compile recipe +#================================================================ +# Uses an MIPS linux cross-compiler on linux or macos. +# +# 1. You can obtain the cross-compiler for ubuntu via: +# sudo apt -y install gcc-mipsel-linux-gnu g++-mipsel-linux-gnu +# For macOS, perhaps check for a mips cross-compiler on +# home-brew or macports. +# +# 2. Then +# make CROSS_TO_MIPS=1 WANT_WIN_TTY=1 WANT_WIN_CURSES=1 all +# +#================================================================= + +CFLAGS += -DCROSSCOMPILE + +# +# Override the build tools and some obj files to +# reflect the mips cross-compiler. +# +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 \ + -DCROSSCOMPILE -DCROSSCOMPILE_TARGET \ + -DCROSS_TO_MIPS \ + -DCURSES_GENL_PUTMIXED \ + -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 \ + -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 \ + -Wall -Wextra -Wno-missing-field-initializers -Wreturn-type -Wunused \ + -Wformat -Wswitch -Wshadow -Wwrite-strings -Wno-maybe-uninitialized +override TARGET_CFLAGS = $(MIPS_TARGET_CFLAGS) -Wmissing-declarations \ + -Wmissing-prototypes -pedantic -Wmissing-declarations \ + -Wformat-nonliteral +override TARGET_CXXFLAGS = $(MIPS_TARGET_CXXFLAGS) +ifdef CPLUSPLUS_NEEDED +override TARGET_LINK = mipsel-linux-gnu-gcc +else +override TARGET_LINK = mipsel-linux-gnu-g++ +endif +override TARGET_LFLAGS= +override SYSOBJ = $(TARGETPFX)ioctl.o $(TARGETPFX)unixmain.o $(TARGETPFX)unixtty.o \ + $(TARGETPFX)unixunix.o $(TARGETPFX)unixres.o +override WINLIB = $(NCURSESLIB) +override LUALIB= +override LUALIBS= +override TOPLUALIB= +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$@ $< +# Rule for file in sys/share +#$(TARGETPFX)%.o : ../sys/share/%.c +# $(TARGET_CC) $(TARGET_CFLAGS) -c -o$@ $< +endif # CROSS_TO_MIPS #================================================================= ifdef WANT_WIN_CURSES diff --git a/sys/unix/hints/include/gbdates-post.370 b/sys/unix/hints/include/gbdates-post.370 index a736e30f9a..9809a6df94 100755 --- a/sys/unix/hints/include/gbdates-post.370 +++ b/sys/unix/hints/include/gbdates-post.370 @@ -2,7 +2,7 @@ ifdef MAKEFILE_DOC ifdef GB_DATESTAMP Guidebook.dated.mn: $(GUIDEBOOK_MN_SRC) - @awk 'f{$$0=".ds f2 \"$(GB_DATESTAMP)";f=0}/NH_DATESUB/{f=1} 1' < $(GUIDEBOOK_MN_SRC) > $@ + @awk 'f{$$0=".ds f2 $(GB_DATESTAMP)";f=0}/NH_DATESUB/{f=1} 1' < $(GUIDEBOOK_MN_SRC) > $@ Guidebook.dated.tex: $(GUIDEBOOK_TEX_SRC) @awk 'f{$$0="\\date{$(GB_DATESTAMP)}";f=0}/NH_DATESUB/{f=1} 1' < $(GUIDEBOOK_TEX_SRC) > $@ endif # GB_DATESTAMP diff --git a/sys/unix/hints/include/multiw-3.370 b/sys/unix/hints/include/misc.370 similarity index 51% rename from sys/unix/hints/include/multiw-3.370 rename to sys/unix/hints/include/misc.370 index 5d11441d68..6608cf45a8 100644 --- a/sys/unix/hints/include/multiw-3.370 +++ b/sys/unix/hints/include/misc.370 @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------ -# NetHack 3.7 multiw-3.370 $NHDT-Date: 1668359836 2022/11/13 17:17:16 $ $NHDT-Branch: NetHack-3.7 $ +# NetHack 3.7 misc.370 $NHDT-Date: 1668359836 2022/11/13 17:17:16 $ $NHDT-Branch: NetHack-3.7 $ # -# Further set-up for multiple window ports (interfaces) that comes after compiler.370. +# Further set-up for miscellaneous odds and ends (after compiler.370) # # Included from: # hints/linux.370 @@ -14,7 +14,7 @@ ifdef MAKEFILE_SRC ifdef WANT_WIN_QT # when switching from Qt5 to Qt6 or vice versa, any old .moc files will be # incompatible; get rid of them in case user hasn't run 'make spotless'; -# object files are incompatable with Qt library, so get rid of them too; +# object files are incompatible with Qt library, so get rid of them too; # Qt*.h-t are empty timestamp files and at most one should exist QTany_H = Qt*.h-t ifdef WANT_WIN_QT6 @@ -65,6 +65,42 @@ CPLUSPLUS_NEEDED = 1 endif endif -#end of hints/include/multiw-3.370 +ifdef USE_MANDOC +NROFF = mandoc +MAN2TXTPRE = -T ascii +MAN2TXTPOST= | col -b +else +#detection of groff +NROFFISGROFF := $(shell echo `nroff --version | grep "GNU groff version"`) +#$(info NROFFISGROFF=$(NROFFISGROFF)) +ifneq "$(NROFFISGROFF)" "" +# get the version of groff and flag if it is gt or eq to 1.23 +GROFFGE123 := $(shell expr `echo $(NROFFISGROFF) | cut -f2 -d.` \>= 23) +# or less than 1.24 +GROFFLT124 := $(shell expr `echo $(NROFFISGROFF) | cut -f2 -d.` \< 24) +# -Wtab -Wrange are for the sake of tmac.n. +NROFF_FLAGS := -wall -Wtab -Wrange +ifneq "$(GROFFLT124)" "" +NROFF_FLAGS += -Wel -Wscale +endif +endif # NROFFISGROFF + +ifneq "$(NROFFISGROFF)" "" # It's groff +# add the -Tascii flag used by groff +MAN2TXTPRE += -Tascii +ifneq "$(GROFFGE123)" "" # It's groff 1.23 or greater +#$(info GROFFGE123=$(GROFFGE123)) +# nroff in groff 1.23 supports the -P option to pass arguments to the +# output driver. -cbou are flags to grotty(1). +MAN2TXTPRE += -P -cbou +MAN2TXTPOST= +else +MAN2TXTPRE += -c +# groff less than 1.23 +endif +endif # end groff-specific +endif # not USE_MANDOC + +#end of hints/include/misc.370 #------------------------------------------------------------------------------ diff --git a/sys/unix/hints/include/multiw-2.370 b/sys/unix/hints/include/multiw-2.370 index f671335bb8..938c958f30 100644 --- a/sys/unix/hints/include/multiw-2.370 +++ b/sys/unix/hints/include/multiw-2.370 @@ -91,6 +91,11 @@ endif ifdef WANT_WIN_CURSES WINCFLAGS += -DCURSES_GRAPHICS +ifeq "$(USE_GENL_PUTMIXED)" "1" +WINCFLAGS += -DUSE_GENL_PUTMIXED +else +WINCFLAGS += -D_XOPEN_SOURCE_EXTENDED=1 +endif WINSRC += $(WINCURSESSRC) WINOBJ0 += $(WINCURSESOBJ) endif @@ -145,7 +150,9 @@ ifdef WANT_WIN_QT WINCFLAGS += -DQT_GRAPHICS WINSRC += $(WINQTSRC) WINOBJ0 += $(WINQTOBJ) +ifndef WANT_WIN_QT4 SNDCFLAGS += -DSND_LIB_QTSOUND +endif #! WANT_WIN_QT4 HAVE_SNDLIB = 1 NEEDS_SND_USERSOUNDS = 1 NEEDS_SND_SEAUTOMAP = 1 @@ -187,6 +194,10 @@ CPLUSPLUS_NEEDED = 1 endif endif +ifeq "$(NOCRASHREPORT)" "1" +WINCFLAGS += -DNOCRASHREPORT +endif + #end of hints/include/multiw-2.370 #------------------------------------------------------------------------------ diff --git a/sys/unix/hints/linux-xnh-debug b/sys/unix/hints/linux-xnh-debug index 969e086fa3..6492e71184 100755 --- a/sys/unix/hints/linux-xnh-debug +++ b/sys/unix/hints/linux-xnh-debug @@ -1,4 +1,4 @@ -# NetHack 3.7 linux.370 $NHDT-Date: 1668359835 2022/11/13 17:17:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.50 $ +# NetHack 3.7 linux.370 $NHDT-Date: 1693519390 2023/08/31 22:03:10 $ $NHDT-Branch: keni-crashweb2 $:$NHDT-Revision: 1.61 $ # Copyright (c) Kenneth Lorber, Kensington, Maryland, 2007. # NetHack may be freely redistributed. See license for details. # @@ -20,6 +20,20 @@ # multiw-1.370 contains sections 1 to 2 #-INCLUDE multiw-1.370 +HINTSVERSION := 370 + +ifdef MAKEFILE_TOP +ifeq ($(MAKELEVEL),0) +PRECHECK+=checkmakefiles +# all files included from this hints file get listed +# in HINTSINCLNAMES (without suffix and without a path) +HINTSINCLNAMES := compiler cross-pre1 cross-pre2 cross-post \ + gbdates-pre gbdates-post \ + multiw-1 multiw-2 misc \ + multisnd1-pre multisnd2-pre multisnd-post +HINTSINCLFILES := $(addsuffix .$(HINTSVERSION), $(HINTSINCLNAMES)) +endif +endif ifndef LIBXPM LIBXPM= -L/opt/X11/lib -lXpm @@ -29,6 +43,18 @@ endif GAMEUID = $(USER) GAMEGRP = games +# This gives better backtraces by making all core functions global; this +# works around a limitation in glibc's backtrace(3) function. +# This will be turned on automatically with CRASHREPROT. +# 1 to enable, 0 to disable +USE_NOSTATICFN = 1 +# If you want CRASHREPORT but absolutely don't want NOSTATICFN, define this: +#USE_NONOSTATICFN = 1 + +#----------------------------------------------------------------------------- +#-INCLUDE cross-pre1.370 + + #----------------------------------------------------------------------------- # You shouldn't need to change anything below here (in the hints file; if # you're reading this in Makefile augmented by hints, that may not be true). @@ -48,19 +74,27 @@ 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 -# multiw-3.370 must come after compiler.370 +# misc.370 must come after compiler.370 # and after QTDIR is defined. # -#-INCLUDE multiw-3.370 +#-INCLUDE misc.370 ifeq "$(USE_ASAN)" "1" CFLAGS+=-fsanitize=address LFLAGS+=-fsanitize=address endif +ifeq "$(USE_UBSAN)" "1" +CFLAGS+=-fsanitize=undefined +LFLAGS+=-fsanitize=undefined +endif + ifeq "$(USE_CURSESLIB)" "1" # default CURSESLIB = -lncurses -ltinfo @@ -70,10 +104,17 @@ CURSESLIB = -lncurses -ltinfo ifdef MAKEFILE_SRC comma:=, NCURSES_LFLAGS = $(shell pkg-config ncursesw --libs) -ifneq (,$(findstring ncursesw, $(NCURSES_LFLAGS))) -HAVE_NCURSESW=1 -else +ifeq (,$(findstring ncursesw, $(NCURSES_LFLAGS))) +ifeq (,$(findstring ncurses, $(NCURSES_LFLAGS))) +#this indicates that pkg-config itself was unavailable +NCURSES_LFLAGS = -lncursesw -ltinfo +endif +endif +#$(info $(NCURSES_LFLAGS)) +ifeq (,$(findstring ncursesw, $(NCURSES_LFLAGS))) HAVE_NCURSESW=0 +else +HAVE_NCURSESW=1 endif #$(info $(NCURSES_LFLAGS)) #$(info HAVE_NCURSESW=$(HAVE_NCURSESW)) @@ -108,15 +149,8 @@ NHCFLAGS+=-DCOMPRESS=\"/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\" #NHCFLAGS+=-DTTY_SOUND_ESCCODES #NHCFLAGS+=-DNO_CHRONICLE #NHCFLAGS+=-DLIVELOG -ifdef WANT_WIN_CURSES -ifeq "$(HAVE_NCURSESW)" "1" -NHCFLAGS+=-DCURSES_UNICODE -else -ifdef MAKEFILE_SRC -$(info Attention: CURSES_UNICODE is not being defined without ncursesw) -endif #MAKEFILE_SRC -endif #HAVE_NCURSESW -endif #WANT_WIN_CURSES +#NHCFLAGS+=-DCHANGE_COLOR +NHCFLAGS+=-DSELF_RECOVER # CUSTOM COMPILER OPTIONS FOR ENSURING HARDFOUGHT SERVER BUILD WORKS NHCFLAGS+=-DDUMPHTML @@ -129,7 +163,7 @@ NHCFLAGS+=-g # -g3 will include highest level of debug symbols (macros) but NHCFLAGS+=-Wformat=2 # -Wformat=2 includes format-nonliteral, which NetHack liberally violates; # however we still want those warnings outside # DISABLE_WARNING_FORMAT_NONLITERAL blocks -NHCFLAGS+=-Werror # warnings are Serious Business +NHCFLAGS+=-Wall -Werror # warnings are Serious Business NHCFLAGS+= -Wimplicit-fallthrough=2 # 3 usually ok, but nice to enforce # /* else FALLTHRU */ NHCFLAGS+=-Warray-bounds=2 # extra checking for struct/pointer array access @@ -137,6 +171,16 @@ NHCFLAGS+=-DFUZZER_LOG # log things so fuzzer crashes can trace what was happeni NHCFLAGS+=-DDEBUG # duh # END CUSTOM FLAGS +ifdef WANT_WIN_CURSES +ifeq "$(HAVE_NCURSESW)" "1" +NHCFLAGS+=-DCURSES_UNICODE +else +ifdef MAKEFILE_SRC +$(info Attention: CURSES_UNICODE is not being defined without ncursesw) +endif #MAKEFILE_SRC +endif #HAVE_NCURSESW +endif #WANT_WIN_CURSES + # #-INCLUDE multisnd1-pre.370 # @@ -197,6 +241,7 @@ QTCXXFLAGS += $(sort $(shell PKG_CONFIG_PATH=$(QTDIR)/lib/pkgconfig pkg-config Q WINLIB += $(shell PKG_CONFIG_PATH=$(QTDIR)/lib/pkgconfig pkg-config Qt5Gui Qt5Widgets Qt5Multimedia --libs) endif # WANT_WIN_QT5 ifdef WANT_WIN_QT6 +ifdef QT6MANUAL # Try some likely spots for a self-built Qt6. # You'll have to change these manually before using the hints file # if they don't match the installed location on your system. @@ -216,7 +261,7 @@ QTCXXFLAGS += -I$(QTINCDIR)/QtNetwork -I$(QTINCDIR)/QtGui -I$(QTINCDIR)/QtCore WINLIB += -L$(QTLIBDIR) -lQt6Widgets -lQt6Multimedia -lQt6Network -lQt6Gui -lQt6Core MOCPATH = /usr/lib/qt6/libexec/moc #/usr/lib/x86_64-linux-gnu/libQt6EglFSDeviceIntegration.so -endif +endif # QTTOP/* ifndef QTINCDIR ifneq ($(wildcard /usr/local/Qt6/*),) QTDIR=/usr/local/Qt6 @@ -235,7 +280,17 @@ QTCXXFLAGS += -I$(QTDIR)/include/QtWidgets MOCPATH = $(QTDIR)/libexec/moc WINLIB += -L$(QTDIR)/lib -lQt6Widgets -lQt6Multimedia -lQt6Network -lQt6Gui -lQt6Core endif # QTLOCATED -endif # !QTINCDIR +else # !QT6MANUAL +MOCDIR = $(shell pkg-config Qt6Gui --variable=libexecdir) +ifneq ($(wildcard $(MOCDIR)/*),) +MOCPATH = $(MOCDIR)/moc +else +MOCPATH = /usr/lib/qt6/libexec/moc +endif # MOCDIR +QTCXXFLAGS += $(sort $(shell pkg-config Qt6Gui Qt6Widgets Qt6Multimedia --cflags)) +WINLIB += $(shell pkg-config Qt6Gui Qt6Widgets Qt6Multimedia --libs) +endif # QT6MANUAL +endif # WANT_WIN_QT6 ifndef QTDIR $(error QTDIR not defined in the environment or Makefile) endif # QTDIR @@ -256,6 +311,7 @@ VARDATND += $(sort $(VARDATND0)) GIT_HASH := $(shell echo `git rev-parse --verify HEAD` 2>&1) GIT_BRANCH := $(shell echo `git rev-parse --abbrev-ref HEAD` 2>&1) +GIT_PREFIX := $(shell echo `git config nethack.substprefix` 2>&1) ifdef GIT_HASH GITHASH = -DNETHACK_GIT_SHA=\"$(GIT_HASH)\" @@ -263,6 +319,9 @@ endif ifdef GIT_BRANCH GITBRANCH = -DNETHACK_GIT_BRANCH=\"$(GIT_BRANCH)\" endif +ifdef GIT_PREFIX +GITPREFIX = -DNETHACK_GIT_PREFIX=\"$(GIT_PREFIX)\" +endif ifdef WANT_LIBNH CFLAGS += -DSHIM_GRAPHICS -DNOTTYGRAPHICS -DNOSHELL -DLIBNH -fpic @@ -280,18 +339,39 @@ override GAME= MOREALL += ( cd src ; $(MAKE) pregame ; $(MAKE) $(TARGETPFX)libnh.a ) endif # WANT_LIBNH +SYSCONFCREATE = cp sys/unix/sysconf $(INSTDIR)/sysconf +SYSCONFINSTALL = $(SYSCONFCREATE) && \ + $(CHOWN) $(GAMEUID) $(INSTDIR)/sysconf && \ + $(CHGRP) $(GAMEGRP) $(INSTDIR)/sysconf && \ + chmod $(VARFILEPERM) $(INSTDIR)/sysconf; +SYSCONFENSURE = (if ! test -f $(INSTDIR)/sysconf ; then \ + $(SYSCONFCREATE) && \ + $(CHOWN) $(GAMEUID) $(INSTDIR)/sysconf && \ + $(CHGRP) $(GAMEGRP) $(INSTDIR)/sysconf && \ + chmod $(VARFILEPERM) $(INSTDIR)/sysconf; fi ); + +ifdef WANT_SOURCE_INSTALL +PREFIX=$(abspath $(NHSROOT)) +#SHELLDIR= +HACKDIR=$(PREFIX)/playground +GAMEPERM = 0700 +VARFILEPERM = 0600 +VARDIRPERM = 0700 +CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" -DSECURE +MOREALL=$(MAKE) install +else #!WANT_SOURCE_INSTALL #PREFIX=/usr PREFIX=$(wildcard ~)/xnh/install HACKDIR=$(PREFIX)/games/lib/xnethackdir SHELLDIR = $(PREFIX)/games +VARDIRPERM = 0755 +VARFILEPERM = 0600 +GAMEPERM = 0755 +endif #?WANT_SOURCE_INSTALL + INSTDIR=$(HACKDIR) VARDIR = $(HACKDIR) -POSTINSTALL+= cp -n sys/unix/sysconf $(INSTDIR)/sysconf; \ - $(CHOWN) $(GAMEUID) $(INSTDIR)/sysconf; \ - $(CHGRP) $(GAMEGRP) $(INSTDIR)/sysconf; \ - chmod $(VARFILEPERM) $(INSTDIR)/sysconf; - ifneq "$(CCISCLANG)" "" # gdb may not be installed if clang is chosen compiler so the game # won't start in that case due to a sysconf error. Comment out @@ -305,6 +385,14 @@ endif POSTINSTALL+=sed -i"" 's/root games/*/' $(INSTDIR)/sysconf; # END CUSTOM +ifeq 'USENONOSTATICFN' '1' +CFLAGS += -DNONOSTATICFN +else +ifeq 'USE_NOSTATICFN' '1' +CFLAGS += -DNOSTATICFN +endif +endif + # Lua # when building liblua.a, avoid warning that use of tmpnam() should be # replaced by mkstemp(); the lua code doesn't use nethack's config.h so @@ -333,11 +421,10 @@ LFLAGS+=-rdynamic CHOWN=true CHGRP=true -VARDIRPERM = 0755 -VARFILEPERM = 0600 -GAMEPERM = 0755 +# manpages directory +MANDIR=/usr/share/man/man6 # -#-INCLUDE cross-pre.370 +#-INCLUDE cross-pre2.370 # # @@ -357,6 +444,15 @@ GAMEPERM = 0755 #-INCLUDE multisnd-post.370 # +ifdef MAKEFILE_TOP +ifeq ($(MAKELEVEL),0) +.PHONY: checkmakefiles +checkmakefiles: + @$(MAKE) -f sys/unix/Makefile.check \ + HINTSFILE="$(HINTSFILE)" HINTSINCLFILES="$(HINTSINCLFILES)" +endif +endif + ifdef WANT_LIBNH $(TARGETPFX)libnh.a: $(HOBJ) $(LIBNHSYSOBJ) ../lib/lua/liblua.a $(AR) rcs $@ $(HOBJ) $(LIBNHSYSOBJ) ../lib/lua/liblua.a diff --git a/sys/unix/hints/linux.370 b/sys/unix/hints/linux.370 index 2cdfdf0deb..39286e0274 100755 --- a/sys/unix/hints/linux.370 +++ b/sys/unix/hints/linux.370 @@ -1,4 +1,4 @@ -# NetHack 3.7 linux.370 $NHDT-Date: 1668359835 2022/11/13 17:17:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.50 $ +# NetHack 3.7 linux.370 $NHDT-Date: 1693519390 2023/08/31 22:03:10 $ $NHDT-Branch: keni-crashweb2 $:$NHDT-Revision: 1.61 $ # Copyright (c) Kenneth Lorber, Kensington, Maryland, 2007. # NetHack may be freely redistributed. See license for details. # @@ -20,6 +20,20 @@ # multiw-1.370 contains sections 1 to 2 #-INCLUDE multiw-1.370 +HINTSVERSION := 370 + +ifdef MAKEFILE_TOP +ifeq ($(MAKELEVEL),0) +PRECHECK+=checkmakefiles +# all files included from this hints file get listed +# in HINTSINCLNAMES (without suffix and without a path) +HINTSINCLNAMES := compiler cross-pre1 cross-pre2 cross-post \ + gbdates-pre gbdates-post \ + multiw-1 multiw-2 misc \ + multisnd1-pre multisnd2-pre multisnd-post +HINTSINCLFILES := $(addsuffix .$(HINTSVERSION), $(HINTSINCLNAMES)) +endif +endif ifndef LIBXPM LIBXPM= -L/opt/X11/lib -lXpm @@ -29,6 +43,18 @@ endif GAMEUID = $(USER) GAMEGRP = games +# This gives better backtraces by making all core functions global; this +# works around a limitation in glibc's backtrace(3) function. +# This will be turned on automatically with CRASHREPROT. +# 1 to enable, 0 to disable +USE_NOSTATICFN = 1 +# If you want CRASHREPORT but absolutely don't want NOSTATICFN, define this: +#USE_NONOSTATICFN = 1 + +#----------------------------------------------------------------------------- +#-INCLUDE cross-pre1.370 + + #----------------------------------------------------------------------------- # You shouldn't need to change anything below here (in the hints file; if # you're reading this in Makefile augmented by hints, that may not be true). @@ -48,19 +74,27 @@ 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 -# multiw-3.370 must come after compiler.370 +# misc.370 must come after compiler.370 # and after QTDIR is defined. # -#-INCLUDE multiw-3.370 +#-INCLUDE misc.370 ifeq "$(USE_ASAN)" "1" CFLAGS+=-fsanitize=address LFLAGS+=-fsanitize=address endif +ifeq "$(USE_UBSAN)" "1" +CFLAGS+=-fsanitize=undefined +LFLAGS+=-fsanitize=undefined +endif + ifeq "$(USE_CURSESLIB)" "1" # default CURSESLIB = -lncurses -ltinfo @@ -70,10 +104,17 @@ CURSESLIB = -lncurses -ltinfo ifdef MAKEFILE_SRC comma:=, NCURSES_LFLAGS = $(shell pkg-config ncursesw --libs) -ifneq (,$(findstring ncursesw, $(NCURSES_LFLAGS))) -HAVE_NCURSESW=1 -else +ifeq (,$(findstring ncursesw, $(NCURSES_LFLAGS))) +ifeq (,$(findstring ncurses, $(NCURSES_LFLAGS))) +#this indicates that pkg-config itself was unavailable +NCURSES_LFLAGS = -lncursesw -ltinfo +endif +endif +#$(info $(NCURSES_LFLAGS)) +ifeq (,$(findstring ncursesw, $(NCURSES_LFLAGS))) HAVE_NCURSESW=0 +else +HAVE_NCURSESW=1 endif #$(info $(NCURSES_LFLAGS)) #$(info HAVE_NCURSESW=$(HAVE_NCURSESW)) @@ -108,6 +149,8 @@ NHCFLAGS+=-DCOMPRESS=\"/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\" #NHCFLAGS+=-DTTY_SOUND_ESCCODES #NHCFLAGS+=-DNO_CHRONICLE #NHCFLAGS+=-DLIVELOG +#NHCFLAGS+=-DCHANGE_COLOR +NHCFLAGS+=-DSELF_RECOVER ifdef WANT_WIN_CURSES ifeq "$(HAVE_NCURSESW)" "1" NHCFLAGS+=-DCURSES_UNICODE @@ -178,6 +221,7 @@ QTCXXFLAGS += $(sort $(shell PKG_CONFIG_PATH=$(QTDIR)/lib/pkgconfig pkg-config Q WINLIB += $(shell PKG_CONFIG_PATH=$(QTDIR)/lib/pkgconfig pkg-config Qt5Gui Qt5Widgets Qt5Multimedia --libs) endif # WANT_WIN_QT5 ifdef WANT_WIN_QT6 +ifdef QT6MANUAL # Try some likely spots for a self-built Qt6. # You'll have to change these manually before using the hints file # if they don't match the installed location on your system. @@ -197,7 +241,7 @@ QTCXXFLAGS += -I$(QTINCDIR)/QtNetwork -I$(QTINCDIR)/QtGui -I$(QTINCDIR)/QtCore WINLIB += -L$(QTLIBDIR) -lQt6Widgets -lQt6Multimedia -lQt6Network -lQt6Gui -lQt6Core MOCPATH = /usr/lib/qt6/libexec/moc #/usr/lib/x86_64-linux-gnu/libQt6EglFSDeviceIntegration.so -endif +endif # QTTOP/* ifndef QTINCDIR ifneq ($(wildcard /usr/local/Qt6/*),) QTDIR=/usr/local/Qt6 @@ -216,7 +260,17 @@ QTCXXFLAGS += -I$(QTDIR)/include/QtWidgets MOCPATH = $(QTDIR)/libexec/moc WINLIB += -L$(QTDIR)/lib -lQt6Widgets -lQt6Multimedia -lQt6Network -lQt6Gui -lQt6Core endif # QTLOCATED -endif # !QTINCDIR +else # !QT6MANUAL +MOCDIR = $(shell pkg-config Qt6Gui --variable=libexecdir) +ifneq ($(wildcard $(MOCDIR)/*),) +MOCPATH = $(MOCDIR)/moc +else +MOCPATH = /usr/lib/qt6/libexec/moc +endif # MOCDIR +QTCXXFLAGS += $(sort $(shell pkg-config Qt6Gui Qt6Widgets Qt6Multimedia --cflags)) +WINLIB += $(shell pkg-config Qt6Gui Qt6Widgets Qt6Multimedia --libs) +endif # QT6MANUAL +endif # WANT_WIN_QT6 ifndef QTDIR $(error QTDIR not defined in the environment or Makefile) endif # QTDIR @@ -237,6 +291,7 @@ VARDATND += $(sort $(VARDATND0)) GIT_HASH := $(shell echo `git rev-parse --verify HEAD` 2>&1) GIT_BRANCH := $(shell echo `git rev-parse --abbrev-ref HEAD` 2>&1) +GIT_PREFIX := $(shell echo `git config nethack.substprefix` 2>&1) ifdef GIT_HASH GITHASH = -DNETHACK_GIT_SHA=\"$(GIT_HASH)\" @@ -244,6 +299,9 @@ endif ifdef GIT_BRANCH GITBRANCH = -DNETHACK_GIT_BRANCH=\"$(GIT_BRANCH)\" endif +ifdef GIT_PREFIX +GITPREFIX = -DNETHACK_GIT_PREFIX=\"$(GIT_PREFIX)\" +endif ifdef WANT_LIBNH CFLAGS += -DSHIM_GRAPHICS -DNOTTYGRAPHICS -DNOSHELL -DLIBNH -fpic @@ -261,18 +319,39 @@ override GAME= MOREALL += ( cd src ; $(MAKE) pregame ; $(MAKE) $(TARGETPFX)libnh.a ) endif # WANT_LIBNH +SYSCONFCREATE = cp sys/unix/sysconf $(INSTDIR)/sysconf +SYSCONFINSTALL = $(SYSCONFCREATE) && \ + $(CHOWN) $(GAMEUID) $(INSTDIR)/sysconf && \ + $(CHGRP) $(GAMEGRP) $(INSTDIR)/sysconf && \ + chmod $(VARFILEPERM) $(INSTDIR)/sysconf; +SYSCONFENSURE = (if ! test -f $(INSTDIR)/sysconf ; then \ + $(SYSCONFCREATE) && \ + $(CHOWN) $(GAMEUID) $(INSTDIR)/sysconf && \ + $(CHGRP) $(GAMEGRP) $(INSTDIR)/sysconf && \ + chmod $(VARFILEPERM) $(INSTDIR)/sysconf; fi ); + +ifdef WANT_SOURCE_INSTALL +PREFIX=$(abspath $(NHSROOT)) +#SHELLDIR= +HACKDIR=$(PREFIX)/playground +GAMEPERM = 0700 +VARFILEPERM = 0600 +VARDIRPERM = 0700 +CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" -DSECURE +MOREALL=$(MAKE) install +else #!WANT_SOURCE_INSTALL #PREFIX=/usr PREFIX=$(wildcard ~)/nh/install HACKDIR=$(PREFIX)/games/lib/nethackdir SHELLDIR = $(PREFIX)/games +VARDIRPERM = 0755 +VARFILEPERM = 0600 +GAMEPERM = 0755 +endif #?WANT_SOURCE_INSTALL + INSTDIR=$(HACKDIR) VARDIR = $(HACKDIR) -POSTINSTALL+= cp -n sys/unix/sysconf $(INSTDIR)/sysconf; \ - $(CHOWN) $(GAMEUID) $(INSTDIR)/sysconf; \ - $(CHGRP) $(GAMEGRP) $(INSTDIR)/sysconf; \ - chmod $(VARFILEPERM) $(INSTDIR)/sysconf; - ifneq "$(CCISCLANG)" "" # gdb may not be installed if clang is chosen compiler so the game # won't start in that case due to a sysconf error. Comment out @@ -281,6 +360,14 @@ 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' +CFLAGS += -DNONOSTATICFN +else +ifeq 'USE_NOSTATICFN' '1' +CFLAGS += -DNOSTATICFN +endif +endif + # Lua # when building liblua.a, avoid warning that use of tmpnam() should be # replaced by mkstemp(); the lua code doesn't use nethack's config.h so @@ -309,11 +396,10 @@ LFLAGS+=-rdynamic CHOWN=true CHGRP=true -VARDIRPERM = 0755 -VARFILEPERM = 0600 -GAMEPERM = 0755 +# manpages directory +MANDIR=/usr/share/man/man6 # -#-INCLUDE cross-pre.370 +#-INCLUDE cross-pre2.370 # # @@ -333,6 +419,15 @@ GAMEPERM = 0755 #-INCLUDE multisnd-post.370 # +ifdef MAKEFILE_TOP +ifeq ($(MAKELEVEL),0) +.PHONY: checkmakefiles +checkmakefiles: + @$(MAKE) -f sys/unix/Makefile.check \ + HINTSFILE="$(HINTSFILE)" HINTSINCLFILES="$(HINTSINCLFILES)" +endif +endif + ifdef WANT_LIBNH $(TARGETPFX)libnh.a: $(HOBJ) $(LIBNHSYSOBJ) ../lib/lua/liblua.a $(AR) rcs $@ $(HOBJ) $(LIBNHSYSOBJ) ../lib/lua/liblua.a diff --git a/sys/unix/hints/macOS.370 b/sys/unix/hints/macOS.370 index ff3180b623..44b9971053 100755 --- a/sys/unix/hints/macOS.370 +++ b/sys/unix/hints/macOS.370 @@ -1,4 +1,4 @@ -# NetHack 3.7 macOS.370 $NHDT-Date: 1668359835 2022/11/13 17:17:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.116 $ +# NetHack 3.7 macOS.370 $NHDT-Date: 1693359574 2023/08/30 01:39:34 $ $NHDT-Branch: keni-crashweb2 $:$NHDT-Revision: 1.138 $ # Copyright (c) Kenneth Lorber, Kensington, Maryland, 2015. # NetHack may be freely redistributed. See license for details. # @@ -20,6 +20,21 @@ # note: '#-INCLUDE' is not just a comment; multiw-1 contains sections 1 to 2 #-INCLUDE multiw-1.370 +HINTSVERSION := 370 + +ifdef MAKEFILE_TOP +ifeq ($(MAKELEVEL),0) +PRECHECK+=checkmakefiles +# all files included from this hints file get listed +# in HINTSINCLNAMES (without suffix and without a path) +HINTSINCLNAMES := compiler cross-pre1 cross-pre2 cross-post \ + gbdates-pre gbdates-post \ + multiw-1 multiw-2 misc \ + multisnd1-pre multisnd2-pre multisnd-post +HINTSINCLFILES := $(addsuffix .$(HINTSVERSION), $(HINTSINCLNAMES)) +endif +endif + ifneq "$(USEMACPORTS)" "1" HAVE_HOMEBREW := $(shell expr `which -s brew; echo $$?` = 0) endif @@ -31,6 +46,8 @@ ifndef LIBXPM LIBXPM= -L/opt/X11/lib -lXpm endif +#WANT_WIN_CHAIN=1 + # 4. Other #----------------------------------------------------------------------------- @@ -38,6 +55,8 @@ endif # you're reading this in Makefile augmented by hints, that may not be true). # +#-INCLUDE cross-pre1.370 + #-INCLUDE multiw-2.370 # compiler.370 contains compiler detection and adjustments common @@ -68,10 +87,21 @@ endif # HAVE_MACPORTS endif # QTDIR endif # WANT_WIN_QT -# multiw-3.370 must come after compiler.370 +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 -#-INCLUDE multiw-3.370 +#-INCLUDE misc.370 + +# manpages directory +MANDIR=/usr/local/share/man/man6 +PREMANPAGES = checkmandir ifeq "$(USE_ASAN)" "1" CFLAGS +=-fsanitize=address @@ -97,6 +127,8 @@ NHCFLAGS+=-DNOMAIL #NHCFLAGS+=-DTTY_SOUND_ESCCODES #NHCFLAGS+=-DNO_CHRONICLE #NHCFLAGS+=-DLIVELOG +# not NHCFLAGS - needed for makedefs +CFLAGS+=-DCRASHREPORT=\"/usr/bin/open\" ifdef MAKEFILE_SRC # default @@ -116,10 +148,18 @@ else # CURSESLIB = -lncurses -ltinfo comma:=, NCURSES_LFLAGS = $(shell pkg-config ncursesw --libs) -ifneq (,$(findstring ncurses, $(NCURSES_LFLAGS))) -HAVE_NCURSESW=1 -else +ifeq (,$(findstring ncursesw, $(NCURSES_LFLAGS))) +ifeq (,$(findstring ncurses, $(NCURSES_LFLAGS))) +#this indicates that pkg-config itself was unavailable +CURSESLIB = $(NCURSES_LFLAGS) +NHCFLAGS+=-DCURSES_UNICODE +endif +endif +#$(info $(NCURSES_LFLAGS)) +ifeq (,$(findstring ncursesw, $(NCURSES_LFLAGS))) HAVE_NCURSESW=0 +else +HAVE_NCURSESW=1 endif ifeq "$(HAVE_NCURSESW)" "1" CURSESLIB = $(NCURSES_LFLAGS) @@ -252,6 +292,7 @@ VARDATND += $(sort $(VARDATND0)) GIT_HASH := $(shell echo `git rev-parse --verify HEAD` 2>&1) GIT_BRANCH := $(shell echo `git rev-parse --abbrev-ref HEAD` 2>&1) +GIT_PREFIX := $(shell echo `git config nethack.substprefix` 2>&1) ifdef GIT_HASH GITHASH = -DNETHACK_GIT_SHA=\"$(GIT_HASH)\" @@ -259,6 +300,9 @@ endif ifdef GIT_BRANCH GITBRANCH = -DNETHACK_GIT_BRANCH=\"$(GIT_BRANCH)\" endif +ifdef GIT_PREFIX +GITPREFIX = -DNETHACK_GIT_PREFIX=\"$(GIT_PREFIX)\" +endif ifdef WANT_LIBNH CFLAGS += -DSHIM_GRAPHICS -DNOTTYGRAPHICS -DNOSHELL -DLIBNH @@ -320,11 +364,13 @@ ROOTCHECK= [[ `id -u` == 0 ]] || ( echo "Must run install with sudo."; exit 1) # make sure we have group GAMEUID and group GAMEGRP PREINSTALL= . sys/unix/hints/macosx.sh user2 $(GAMEUID); \ . sys/unix/hints/macosx.sh group2 $(GAMEGRP); \ - mkdir $(SHELLDIR); chown $(GAMEUID) $(SHELLDIR) -POSTINSTALL+= sys/unix/hints/macosx.sh editsysconf sys/unix/sysconf $(HACKDIR)/sysconf; \ - $(CHOWN) $(GAMEUID) $(HACKDIR)/sysconf; \ - $(CHGRP) $(GAMEGRP) $(HACKDIR)/sysconf; \ - chmod $(VARFILEPERM) $(HACKDIR)/sysconf; + (mkdir $(SHELLDIR) || true); chown $(GAMEUID) $(SHELLDIR) +#======= XXX can this go away? +#POSTINSTALL+= sys/unix/hints/macosx.sh editsysconf sys/unix/sysconf $(HACKDIR)/sysconf; \ +# $(CHOWN) $(GAMEUID) $(HACKDIR)/sysconf; \ +# $(CHGRP) $(GAMEGRP) $(HACKDIR)/sysconf; \ +# chmod $(VARFILEPERM) $(HACKDIR)/sysconf; +#>>>>>>> keni-wincw2 else ifdef WANT_SOURCE_INSTALL @@ -341,6 +387,7 @@ GAMEPERM = 0700 VARFILEPERM = 0600 VARDIRPERM = 0700 POSTINSTALL+= sys/unix/hints/macosx.sh editsysconf sys/unix/sysconf $(HACKDIR)/sysconf; + # We can use "make all" to build the whole thing - but it misses some things: MOREALL=$(MAKE) install CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" -DSECURE @@ -364,7 +411,7 @@ ifdef ($(WANT_DEFAULT),X11) PREINSTALL+= cp -n win/X11/nethack.rc ~/.xnethackrc || true; endif # WANT_DEFAULT X11 -PREINSTALL+= mkdir $(SHELLDIR); +PREINSTALL+= (mkdir $(SHELLDIR) || true); POSTINSTALL+= sys/unix/hints/macosx.sh editsysconf sys/unix/sysconf $(HACKDIR)/sysconf; \ $(CHOWN) $(GAMEUID) $(HACKDIR)/sysconf; \ $(CHGRP) $(GAMEGRP) $(HACKDIR)/sysconf; \ @@ -375,6 +422,16 @@ endif # !WANT_SHARE_INSTALL INSTDIR=$(HACKDIR) VARDIR=$(HACKDIR) +SYSCONFCREATE = sys/unix/hints/macosx.sh editsysconf sys/unix/sysconf $(HACKDIR)/sysconf +SYSCONFINSTALL = $(SYSCONFCREATE) && \ + $(CHOWN) $(GAMEUID) $(INSTDIR)/sysconf && \ + $(CHGRP) $(GAMEGRP) $(INSTDIR)/sysconf && \ + chmod $(VARFILEPERM) $(INSTDIR)/sysconf; +SYSCONFENSURE = (if ! test -f $(INSTDIR)/sysconf ; then \ + $(SYSCONFCREATE) && \ + $(CHOWN) $(GAMEUID) $(INSTDIR)/sysconf && \ + $(CHGRP) $(GAMEGRP) $(INSTDIR)/sysconf && \ + chmod $(VARFILEPERM) $(INSTDIR)/sysconf; fi ); # ~/Library/Preferences/NetHack Defaults # OPTIONS=name:player,number_pad,menustyle:partial,!time,showexp @@ -383,7 +440,7 @@ VARDIR=$(HACKDIR) # Install.Qt mentions a patch for macos - it's not there (it seems to be in the Qt binary # package under the docs directory). # -#-INCLUDE cross-pre.370 +#-INCLUDE cross-pre2.370 # # #-INCLUDE gbdates-pre.370 @@ -465,6 +522,15 @@ endif # MAKEFILE_SRC #-INCLUDE multisnd-post.370 # +ifdef MAKEFILE_TOP +ifeq ($(MAKELEVEL),0) +.PHONY: checkmakefiles +checkmakefiles: + @$(MAKE) -f sys/unix/Makefile.check \ + HINTSFILE="$(HINTSFILE)" HINTSINCLFILES="$(HINTSINCLFILES)" +endif +endif + ifdef WANT_LIBNH $(TARGETPFX)libnh.a: $(HOBJ) $(LIBNHSYSOBJ) ../lib/lua/liblua.a $(AR) rcs $@ $(HOBJ) $(LIBNHSYSOBJ) ../lib/lua/liblua.a @@ -475,6 +541,11 @@ $(TARGETPFX)winshim.o : ../win/shim/winshim.c $(HACK_H) $(CC) $(CFLAGS) -c -o$@ $< endif # WANT_LIBNH +ifdef MAKEFILE_DOC +checkmandir: + @( if test -d $(MANDIR) ; then true ; else mkdir -p $(MANDIR) ; fi ) +endif # MAKEFILE_DOC + # -- bundle --- # ifdef MAKEFILE_TOP @@ -619,7 +690,7 @@ ifdef MAKEFILE_TOP # Installer certificate. Set DEVELOPER_CERT to the name of the certificate # if you wish for your package to be signed for distribution. # -# If building a package for signing, you must use sudo approriately. +# If building a package for signing, you must use sudo appropriately. # the binaries and package using sudo but you DO NOT use sudo to sign the # package. If you use sudo to sign the package, it will fail. # @@ -787,4 +858,3 @@ endif # end of build_qt_pkg endif # WANT_WIN_QT for packaging endif # MAKEFILE_TOP # - diff --git a/sys/unix/hints/solaris b/sys/unix/hints/solaris index 9d02d4bd30..1f3e22e647 100644 --- a/sys/unix/hints/solaris +++ b/sys/unix/hints/solaris @@ -8,7 +8,7 @@ # This hints file provides a single-user x11 build for Solaris, specifically # for Solaris 10 and 11, but should work just fine on older versions -# Build using using included gcc and gmake, optional flex and bison come from csw for solaris 10 and included +# Build using included gcc and gmake, optional flex and bison come from csw for solaris 10 and included # with Solaris 11 # Build NetHack off your home directory diff --git a/sys/unix/hints/solaris-playground b/sys/unix/hints/solaris-playground index 82ff1fd0cb..91f376c2f3 100644 --- a/sys/unix/hints/solaris-playground +++ b/sys/unix/hints/solaris-playground @@ -9,9 +9,9 @@ # Nethack will install suid games, and will expect to read a users .xnethackrc file from # their home directory which may be a problem on secure systems with read restricted home -# directories, not that you would problably run NetHack on such a system anyway. :) +# directories, not that you would probably run NetHack on such a system anyway. :) -# Build using using included gcc and gmake, optional flex and bison come from csw for solaris 10 +# Build using included gcc and gmake, optional flex and bison come from csw for solaris 10 # and included on Solaris 11 diff --git a/sys/unix/mkmkfile.sh b/sys/unix/mkmkfile.sh index 90e1346576..12894305b5 100755 --- a/sys/unix/mkmkfile.sh +++ b/sys/unix/mkmkfile.sh @@ -17,6 +17,8 @@ echo "# Your changes will be lost. See sys/unix/NewInstall.unx." >> $3 echo "# Identify this file:" >> $3 echo "MAKEFILE_$2=1" >> $3 echo "" >> $3 +echo "HINTSFILE=$5" >> $3 +echo "" >> $3 echo "###" >> $3 echo "### Start $5 PRE" >> $3 diff --git a/sys/unix/sysconf b/sys/unix/sysconf index f7fb6d9905..ad14e47565 100644 --- a/sys/unix/sysconf +++ b/sys/unix/sysconf @@ -1,4 +1,4 @@ -# NetHack 3.7 sysconf $NHDT-Date: 1646322470 2022/03/03 15:47:50 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.41 $ +# NetHack 3.7 sysconf $NHDT-Date: 1704043695 2023/12/31 17:28:15 $ $NHDT-Branch: keni-luabits2 $:$NHDT-Revision: 1.44 $ # Copyright (c) 2015 by Kenneth Lorber, Kensington, Maryland # NetHack may be freely redistributed. See license for details. # @@ -32,6 +32,10 @@ EXPLORERS=nobody # 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. @@ -100,7 +104,7 @@ ENTRYMAX=2000 # Use "Live logging" for important events (achievements, wishes, etc) # Only available if NetHack was compiled with LIVELOG. -# Only really meaningful for public servers. +# Only really meaningful for public servers or debugging. # See the log in-game with #chronicle -command. # Bitmask for kinds of things you want to log - combine the following values # as desired. @@ -120,7 +124,7 @@ ENTRYMAX=2000 # 0x001000 - Log 'minor' achievements - can be spammy # 0x002000 - Spoiler event; can include in livelog but hidden from #chronicle # 0x004000 - Include as 'major' event in dumplog; can be hidden from livelog -# 0x008000 - Livelog debug msgs (currently only 'enter new level') +# 0x008000 - Livelog debug msgs # 0x010000 - Shouts by the player LIVELOG=0x11FFF @@ -172,6 +176,9 @@ GREPPATH=/bin/grep PANICTRACE_GDB=1 PANICTRACE_LIBC=2 +# URL loaded for creating reports to the NetHack DevTeam +#CRASHREPORTURL=https://nethack.org/links/cr-37BETA.html + # 'portable_device_paths' is only supported for Windows. Starting with # 3.6.3, nethack on Windows treats the folder containing nethack.exe and # nethackW.exe as read-only and puts data files which are generated or diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c index 452ac80592..c525b36636 100644 --- a/sys/unix/unixmain.c +++ b/sys/unix/unixmain.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 unixmain.c $NHDT-Date: 1646313937 2022/03/03 13:25:37 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.99 $ */ +/* NetHack 3.7 unixmain.c $NHDT-Date: 1711213891 2024/03/23 17:11:31 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.127 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -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; @@ -53,7 +51,6 @@ extern void init_linux_cons(void); #endif static void wd_message(void); -static boolean wiz_error_flag = FALSE; static struct passwd *get_unix_pw(void); int @@ -65,7 +62,7 @@ main(int argc, char *argv[]) boolean resuming = FALSE; /* assume new game */ boolean plsel_once = FALSE; - early_init(); + early_init(argc, argv); #if defined(__APPLE__) { @@ -100,7 +97,7 @@ main(int argc, char *argv[]) #endif gh.hname = argv[0]; - gh.hackpid = getpid(); + svh.hackpid = getpid(); (void) umask(0777 & ~FCMASK); choose_windows(DEFAULT_WINDOW_SYS); @@ -165,7 +162,7 @@ main(int argc, char *argv[]) */ u.uhp = 1; /* prevent RIP on early quits */ #if defined(HANGUPHANDLING) - gp.program_state.preserve_locks = 1; + program_state.preserve_locks = 1; #ifndef NO_SIGNAL sethanguphandler((SIG_RET_TYPE) hangup); #endif @@ -196,7 +193,7 @@ main(int argc, char *argv[]) /* wizard mode access is deferred until here */ set_playmode(); /* sets plname to "wizard" for wizard mode */ /* hide any hyphens from plnamesuffix() */ - gp.plnamelen = exact_username ? (int) strlen(gp.plname) : 0; + gp.plnamelen = exact_username ? (int) strlen(svp.plname) : 0; /* strip role,race,&c suffix; calls askname() if plname[] is empty or holds a generic user name like "player" or "games" */ plnamesuffix(); @@ -237,14 +234,14 @@ main(int argc, char *argv[]) * clock, &c not currently in use in the playground directory * (for gl.locknum > 0). */ - if (*gp.plname) { + if (*svp.plname) { getlock(); #if defined(HANGUPHANDLING) - gp.program_state.preserve_locks = 0; /* after getlock() */ + program_state.preserve_locks = 0; /* after getlock() */ #endif } - if (*gp.plname && (nhfp = restore_saved_game()) != 0) { + if (*svp.plname && (nhfp = restore_saved_game()) != 0) { const char *fq_save = fqname(gs.SAVEF, SAVEPREFIX, 1); (void) chmod(fq_save, 0); /* disallow parallel restores */ @@ -277,10 +274,13 @@ main(int argc, char *argv[]) } } } + if (program_state.in_self_recover) { + program_state.in_self_recover = FALSE; + } } if (!resuming) { - boolean neednewlock = (!*gp.plname); + boolean neednewlock = (!*svp.plname); /* new game: start by choosing role, race, etc; player might change the hero's name while doing that, in which case we try to restore under the new name @@ -289,7 +289,7 @@ main(int argc, char *argv[]) if (!plsel_once) player_selection(); plsel_once = TRUE; - if (neednewlock && *gp.plname) + if (neednewlock && *svp.plname) goto attempt_restore; if (iflags.renameinprogress) { /* player has renamed the hero while selecting role; @@ -303,6 +303,16 @@ main(int argc, char *argv[]) goto attempt_restore; } } + +#ifdef CHECK_PANIC_SAVE + /* no save file; check for a panic save; if the check finds one, + ask the player whether to proceed with a new game; it will + quit instead of returning if the answer isn't yes */ + if (check_panic_save()) + ask_about_panic_save(); +#endif + + /* no save file; start a new game */ newgame(); wd_message(); } @@ -462,12 +472,12 @@ process_options(int argc, char *argv[]) break; case 'u': if (arg[2]) { - (void) strncpy(gp.plname, arg + 2, sizeof gp.plname - 1); + (void) strncpy(svp.plname, arg + 2, sizeof svp.plname - 1); gp.plnamelen = 0; /* plname[] might have -role-race attached */ } else if (argc > 1) { argc--; argv++; - (void) strncpy(gp.plname, argv[0], sizeof gp.plname - 1); + (void) strncpy(svp.plname, argv[0], sizeof svp.plname - 1); gp.plnamelen = 0; } else { config_error_add("Character name expected after -u"); @@ -482,6 +492,14 @@ process_options(int argc, char *argv[]) config_error_add("Unknown option: %.60s", origarg); } break; + case 'l': +#ifdef LIVELOG + if(!strncmp(arg, "-loglua", 7)){ + gl.loglua = 1; + } else +#endif + config_error_add("Unknown option: %.60s", origarg); + break; case 'p': /* profession (role) */ if (arg[2]) { if ((i = str2role(&arg[2])) >= 0) @@ -605,7 +623,8 @@ early_options(int *argc_p, char ***argv_p, char **hackdir_p) /* * Both *argc_p and *argv_p account for the program name as (*argv_p)[0]; * local argc and argv impicitly discard that (by starting 'ndx' at 1). - * argcheck() doesn't mind, prscore() (via scores_only()) does. + * argcheck() doesn't mind, prscore() (via scores_only()) does (for the + * number of args it gets passed, not for the value of argv[0]). */ for (ndx = 1; ndx < *argc_p; ndx += (consumed ? 0 : 1)) { consumed = 0; @@ -622,6 +641,15 @@ early_options(int *argc_p, char ***argv_p, char **hackdir_p) ++arg; switch (arg[1]) { /* char after leading dash */ + case 'b': +#ifdef CRASHREPORT + // --bidshow + if (argcheck(argc, argv, ARG_BIDSHOW) == 2){ + opt_terminate(); + /*NOTREACHED*/ + } +#endif + break; case 'd': if (argcheck(argc, argv, ARG_DEBUG) == 1) { consume_arg(ndx, argc_p, argv_p), consumed = 1; @@ -672,13 +700,22 @@ 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 | ArgNamOneLetter | ArgErrComplain), + if (lopt(arg, ((ArgValDisallowed | ArgErrComplain) + /* only accept one-letter if there is just one + dash; reject "--s" because prscore() via + scores_only() doesn't understand it */ + | ((origarg[1] != '-') ? ArgNamOneLetter : 0)), + /* [ought to omit val-disallowed and accept + --scores=foo since -s foo and -sfoo are + allowed, but -s form can take more than one + space-separated argument and --scores=foo + isn't suited for that] */ "-scores", origarg, &argc, &argv)) { /* at this point, argv[0] contains "-scores" or a leading substring of it; prscore() (via scores_only()) expects @@ -747,18 +784,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 @@ -877,7 +912,7 @@ whoami(void) * Note that we trust the user here; it is possible to play under * somebody else's name. */ - if (!*gp.plname) { + if (!*svp.plname) { register const char *s; s = nh_getenv("USER"); @@ -887,8 +922,8 @@ whoami(void) s = getlogin(); if (s && *s) { - (void) strncpy(gp.plname, s, sizeof gp.plname - 1); - if (strchr(gp.plname, '-')) + (void) strncpy(svp.plname, s, sizeof svp.plname - 1); + if (strchr(svp.plname, '-')) return TRUE; } } @@ -940,28 +975,48 @@ port_help(void) boolean authorize_wizard_mode(void) { - struct passwd *pw = get_unix_pw(); - - if (pw && sysopt.wizards && sysopt.wizards[0]) { + if (sysopt.wizards && sysopt.wizards[0]) { if (check_user_string(sysopt.wizards)) return TRUE; } - wiz_error_flag = TRUE; /* not being allowed into wizard mode */ + iflags.wiz_error_flag = TRUE; /* not being allowed into wizard mode */ return FALSE; } +/* similar to above, validate explore mode access */ +boolean +authorize_explore_mode(void) +{ +#ifdef SYSCF + if (sysopt.explorers && sysopt.explorers[0]) { + if (check_user_string(sysopt.explorers)) + return TRUE; + } + iflags.explore_error_flag = TRUE; /* not allowed into explore mode */ + return FALSE; +#else + return TRUE; /* if sysconf disabled, no restrictions on explore mode */ +#endif +} + static void wd_message(void) { - if (wiz_error_flag) { + if (iflags.wiz_error_flag) { if (sysopt.wizards && sysopt.wizards[0]) { char *tmp = build_english_list(sysopt.wizards); pline("Only user%s %s may access debug (wizard) mode.", strchr(sysopt.wizards, ' ') ? "s" : "", tmp); free(tmp); - } else + } else { + You("cannot access debug (wizard) mode."); + } + wizard = FALSE; /* (paranoia) */ + if (!iflags.explore_error_flag) pline("Entering explore/discovery mode instead."); - wizard = 0, discover = 1; /* (paranoia) */ + } else if (iflags.explore_error_flag) { + You("cannot access explore mode."); /* same as enter_explore_mode */ + discover = iflags.deferred_X = FALSE; /* (more paranoia) */ } else if (discover) You("are in non-scoring explore/discovery mode."); } @@ -996,7 +1051,7 @@ check_user_string(const char *optstr) if (optstr[0] == '*') return TRUE; /* allow any user */ if (sysopt.check_plname) - pwname = gp.plname; + pwname = svp.plname; else if ((pw = get_unix_pw()) != 0) pwname = pw->pw_name; if (!pwname || !*pwname) diff --git a/sys/unix/unixunix.c b/sys/unix/unixunix.c index 1d04f16e73..8450da5f86 100644 --- a/sys/unix/unixunix.c +++ b/sys/unix/unixunix.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 unixunix.c $NHDT-Date: 1605493693 2020/11/16 02:28:13 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.32 $ */ +/* NetHack 3.7 unixunix.c $NHDT-Date: 1711213894 2024/03/23 17:11:34 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.43 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Kenneth Lorber, Kensington, Maryland, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -14,6 +14,9 @@ #endif #include +static int veryold(int); +static int eraseoldlocks(void); + #ifdef _M_UNIX extern void sco_mapon(void); extern void sco_mapoff(void); @@ -65,17 +68,21 @@ veryold(int fd) #endif return 0; } - (void) close(fd); + /* this used to close the file upon success, leave it open upon failure; + that was supposed to simplify the caller's usage but ended up making + that be more complicated; always leave the file open so that caller + can close it unconditionally */ + /*(void) close(fd);*/ return 1; } static int eraseoldlocks(void) { - register int i; + int i; #if defined(HANGUPHANDLING) - gp.program_state.preserve_locks = 0; /* not required but shows intent */ + program_state.preserve_locks = 0; /* not required but shows intent */ /* cannot use maxledgerno() here, because we need to find a lock name * before starting everything (including the dungeon initialization * that sets astral_level, needed for maxledgerno()) up @@ -95,7 +102,11 @@ eraseoldlocks(void) void getlock(void) { - register int i = 0, fd, c; +#ifndef SELF_RECOVER + static const char destroy_old_game_prompt[] = + "There is already a game in progress under your name. Destroy old game?"; +#endif + int i = 0, fd, c, too_old; const char *fq_lock; #ifdef TTY_GRAPHICS @@ -121,7 +132,7 @@ getlock(void) 'a','b',&c below; override the default and use if we aren't restricting the number of simultaneous games */ if (!gl.locknum) - Sprintf(gl.lock, "%u%s", (unsigned) getuid(), gp.plname); + Sprintf(gl.lock, "%u%s", (unsigned) getuid(), svp.plname); regularize(gl.lock); set_levelfile_name(gl.lock, 0); @@ -142,10 +153,11 @@ getlock(void) error("Cannot open %s", fq_lock); } - /* veryold() closes fd if true */ - if (veryold(fd) && eraseoldlocks()) - goto gotlock; + /* veryold() no longer conditionally closes fd */ + too_old = veryold(fd); (void) close(fd); + if (too_old && eraseoldlocks()) + goto gotlock; } while (i < gl.locknum); unlock_file(HLOCK); @@ -160,19 +172,36 @@ getlock(void) error("Cannot open %s", fq_lock); } - /* veryold() closes fd if true */ - if (veryold(fd) && eraseoldlocks()) - goto gotlock; + /* veryold() no longer conditionally closes fd */ + too_old = veryold(fd); (void) close(fd); + if (too_old && eraseoldlocks()) + goto gotlock; - { - const char destroy_old_game_prompt[] = - "There is already a game in progress under your name. Destroy old game?"; - + /* drop the "perm" lock while the user decides */ + unlock_file(HLOCK); if (iflags.window_inited) { +#ifdef SELF_RECOVER + c = yn_function( + "Old game in progress. Destroy [y], Recover [r], or Cancel [n]?", + "ynr", 'n', FALSE); +#else /* this is a candidate for paranoid_confirmation */ c = y_n(destroy_old_game_prompt); +#endif } else { +#ifdef SELF_RECOVER + (void) raw_printf( + "\nThere is already a game in progress under your name. Do what?\n"); + (void) raw_printf("\n y - Destroy old game"); + (void) raw_printf("\n r - Try to recover it"); + (void) raw_printf("\n n - Cancel"); + (void) raw_printf("\n\n => "); + (void) fflush(stdout); + do { + c = getchar(); + } while (!strchr("rRyYnN", c) && c != -1); +#else (void) raw_printf("\n%s [yn] ", destroy_old_game_prompt); (void) fflush(stdout); if ((c = getchar()) != EOF) { @@ -183,8 +212,20 @@ getlock(void) while ((tmp = getchar()) != '\n' && tmp != EOF) ; /* eat rest of line and newline */ } +#endif } - } +#ifdef SELF_RECOVER + if (c == 'r' || c == 'R') { + if (recover_savefile() && program_state.in_self_recover) { + set_levelfile_name(gl.lock, 0); + fq_lock = fqname(gl.lock, LEVELPREFIX, 0); + goto gotlock; + } else { + unlock_file(HLOCK); + error("Couldn't recover old game."); + } + } else +#endif if (c == 'y' || c == 'Y') { if (eraseoldlocks()) { goto gotlock; @@ -198,27 +239,64 @@ getlock(void) } } -gotlock: + gotlock: fd = creat(fq_lock, FCMASK); unlock_file(HLOCK); if (fd == -1) { error("cannot creat lock file (%s).", fq_lock); + /*NOTREACHED*/ } else { - if (write(fd, (genericptr_t) &gh.hackpid, sizeof gh.hackpid) - != sizeof gh.hackpid) { + if (write(fd, (genericptr_t) &svh.hackpid, sizeof svh.hackpid) + != sizeof svh.hackpid) { error("cannot write lock (%s)", fq_lock); + /*NOTREACHED*/ } if (close(fd) == -1) { error("cannot close lock (%s)", fq_lock); + /*NOTREACHED*/ } } } +/* caller couldn't find a regular save file but did find a panic one */ +void +ask_about_panic_save(void) +{ +#ifdef CHECK_PANIC_SAVE + static const char Instead_prompt[] = "Start a new game instead?"; + int c = '\0'; + + pline("There is no regular save file but there is a panic one."); + pline("It might be recoverable with demi-divine intervention."); + if (iflags.window_inited) { + c = yn_function(Instead_prompt, "yn\033q", 'n', FALSE); + } else { + raw_printf("%s [yn] (n) ", Instead_prompt); + (void) fflush(stdout); + do { + c = getchar(); + if (c == EOF || c == '\033' || c == '\0') + break; + c = lowc(c); + } while (!strchr("ynq\n", c)); + } + if (c != 'y') { + /* caller successfully called getlock() and made .0 */ + delete_levelfile(0); + unlock_file(HLOCK); /* just in case, release 'perm' */ + if (iflags.window_inited) + exit_nhwindows((char *) 0); + nh_terminate(EXIT_SUCCESS); + } +#endif + return; /* proceed with new game */ +} + /* normalize file name - we don't like .'s, /'s, spaces */ void regularize(char *s) { - register char *lp; + char *lp; while ((lp = strchr(s, '.')) != 0 || (lp = strchr(s, '/')) != 0 || (lp = strchr(s, ' ')) != 0) @@ -291,7 +369,7 @@ dosh(void) int child(int wt) { - register int f; + int f; suspend_nhwindows((char *) 0); /* also calls end_screen() */ #ifdef _M_UNIX @@ -399,3 +477,5 @@ file_exists(const char *path) return TRUE; } #endif + +/*unixunix.c*/ diff --git a/sys/vms/Install.vms b/sys/vms/Install.vms index 46c68fabbc..911e06ad8d 100644 --- a/sys/vms/Install.vms +++ b/sys/vms/Install.vms @@ -8,7 +8,7 @@ 1. NetHack 3.7 was built and tested on OpenVMS on both the Integrity and Alpha platform using the HP C V7.3 for OpenVMS compiler. While not tested, older versions of DEC C will most likely work as compatibility - with older systems is a goal of the VMS porting team. Unfortunatly, + with older systems is a goal of the VMS porting team. Unfortunately, ancient VAX C probably will no longer work. The set of Makefiles provided are known to be out of date; use vmsbuild.com instead. @@ -61,10 +61,7 @@ 3. Prior to beginning compilation, go to the [.include] subdirectory and edit vmsconf.h according to its comments. You should set Local_WIZARD - and Local_HACKDIR to appropriate values, and you might want to define - TEXTCOLOR if you have any color VAXstations or color terminals which - handle ANSI-format escape sequences to set foreground and background - color for text characters. (VT241/VT340 color graphics won't work.) + and Local_HACKDIR to appropriate values. Other things which may be of interest are SECURE if you intend to set up NetHack as an installed image which is granted privileges, and SHELL which should be disabled if you intend to allow captive accounts diff --git a/sys/vms/Install370.vms b/sys/vms/Install370.vms new file mode 100644 index 0000000000..12d020c82d --- /dev/null +++ b/sys/vms/Install370.vms @@ -0,0 +1,364 @@ + Instructions for Building and Installing NetHack 3.7.0 + on a VSI openVMS 9.2 system + ========================================= + +This contains a description of how to build NetHack-3.7.0 on VSI OpenVMS 9.2. +This process uses Gnu Make 4.1 (minimum) for VMS to carry out the build. +Last tested with VSI C x86-64 V7.5-009 (GEM 50XBR) on OpenVMS x86_64 V9.2-2. + +0. Please read this entire file before trying to build or install + NetHack, then read it again! + +1. Make sure all the NetHack files are in the appropriate directory + structure. You should set up a directory--referred to as "top" below + and in some of the assorted files, but which may be a subdirectory-- + that has these subdirectories + [.dat] -- data files + [.doc] -- documentation files + [.include] -- C header files + [.lib.lua546] -- Lua distribution from https://www.lua.org/ + [.src] -- primary source files + [.sys] -- parent for [.sys.*] + [.sys.share] -- files shared by several ports, including VMS + [.sys.vms] -- VMS-specific source and support files + [.util] -- sources for essential utility programs + [.win] -- parent for [.win.*] + [.win.tty] -- "window" routines for ordinary terminals + (including terminal windows on workstations) + +2. From the top of the NetHack 3.7 distribution, issue the following + pair of commands to distribute the GNU make Makefile.vms files to their + working locations for building NetHack: + $ set def [.sys.vms] + $ @vmssetup.com + $ set def [-.-] + That will place renamed copies of the following files like so, + relative to the top of the NetHack source tree: + [.sys.vms]Makefile_top.vms -> Makefile.vms + [.sys.vms]Makefile_dat.vms -> [.dat]Makefile.vms + [.sys.vms]Makefile_doc.vms -> [.doc]Makefile.vms + [.sys.vms]Makefile_src.vms -> [.src]Makefile.vms + [.sys.vms]Makefile_utl.vms -> [.util]Makefile.vms + +3. Prior to beginning compilation, go to the [.include] subdirectory and + edit vmsconf.h according to its comments. You should set Local_WIZARD + and Local_HACKDIR to appropriate values. + Other things which may be of interest are SECURE if you intend to + set up NetHack as an installed image which is granted privileges, and + SHELL which should be disabled if you intend to allow captive accounts + to run NetHack. You may also want to edit file config.h, but that's + only necessary if you want or need to disable some of the game options. + The distributed copy of config.h will work successfully on VMS; + vmsconf.h has conditional code to deal with the UNIX-specific items. + +4. After the above steps, issue the following command from the top of the + NetHack source tree to build the game from sources: + make + +5. [ FIXME: this step is not in the Makefile_top.vms yet 2024/03/08 ] + After compilation, it's time to perform installation. Issue the + following command from the top of the NetHack source tree: + make install + +6. To clean files left over from the build, issue the following command + from the top of the Nethack source tree: + make clean + +7. The file nethack.com which is copied to the playground directory can + be used to invoke NetHack, or nethack.exe can be run directly. Most + of the command-line options specified in the Unix man-page (file + [.doc]nethack.txt) are also applicable to VMS. Some comments at the + beginning of nethack.com illustrate several of the options. New + players should read the file "Guidebook.txt" which will be copied + into the playground directory as "Guidebook.doc". + + +Notes: + +1. Save files and bones files from 3.6.x and earlier versions will not + work with 3.7.0. The scoreboard file (RECORD) from 3.6.x or 3.4.x or + 3.3.x should work. + +2. To specify user-preference options in your environment, define the + logical name NETHACKOPTIONS to have the value of a quoted string + containing a comma separated list of option values. The option names + are case-insensitive. + $ define nethackoptions "noAutoPickup,Dog:Rover,Cat:Felix,DECgraphics" + One value you'll probably want to specify is "noLegacy" to turn off + the initial introductory passage. The "checkpoint" option controls + whether or not enough data is saved to disk so that the set of level + files left behind after a crash contains sufficient information for + recover.exe to be able to construct a save file after the fact. The + tradeoff for enabling checkpoint is that using it makes level changes + do more I/O and take longer. The "menustyle" option controls some + aspects of the user interface, and can be set to "menustyle:traditional" + to make nethack behave more like older versions. + + If logical name or DCL symbol NETHACKOPTIONS is not defined, NetHack + will try HACKOPTIONS instead. Regardless of whether or not either + is defined, it will also try to find a configuration file containing + additional option settings. If the value of the translation of + NETHACKOPTIONS--or HACKOPTIONS--begins with an "@" character then the + rest of the translation is assumed to be the name of the configuration + file. Otherwise, the following are tried: file specified by logical + name NETHACKINI, file SYS$LOGIN:NETHACK.INI, and file HOME:NETHACK.CNF + (note that the C run-time library sets up the value of HOME to match + sys$login). Syntax for the configuration file is similar to + NETHACKOPTIONS, but multiple lines can be used, each must start with + OPTIONS=, and comments can be included by placing '#' in the first + column. Several options which take more complex values (graphics + representation) can also be present; see the "Guidebook" for details. + (Guidebook.txt can be found in the [.doc] subdirectory; a copy gets + placed in the playground directory by install.com. Also, an example + configuration file can be found in [.win.X11]nethack.rc.) + +3. termcap is an ASCII data file containing descriptions of terminal + capabilities and the escape sequences that software must use to take + advantage of them. If you do not already have a termcap file in use + on your system there is a small one in file [.SYS.SHARE]TERMCAP. It + contains definitions for common Digital terminals, also suitable for + most clones and emulators. This file is copied into the playground + by install.com, and NetHack will use it if it can't find any other + one. NetHack uses the following sequence to attempt to locate the + termcap file: translation of the logical name TERMCAP (used as-is), + file NETHACKDIR:TERMCAP, similar file HACKDIR:TERMCAP, GNU-Emacs file + EMACS_LIBRARY:[ETC]TERMCAP.DAT, file []TERMCAP, and lastly file + $TERMCAP (which most likely would be a logical name). If NetHack + can't find the termcap file, or if the above search sequence finds a + different one than you'd prefer, then use the DCL ASSIGN or DEFINE + command to define a value for logical name TERMCAP. + + NetHack also tries fairly hard to figure out what kind of terminal + you're using. It checks for logical names (or symbols) NETHACK_TERM, + HACK_TERM, EMACS_TERM, and lastly TERM. The last is set up by the + C run-time library and you cannot use a logical name or symbol for + it. If all those fail, or if whichever one succeeds has a value of + "undefined" or "unknown" (which can happen under VMS V5.4-* and + V5.5-* for VT420 terminals), NetHack will query the VMS TERMTABLE + database used by the SMG library routines. Whatever value NetHack + eventually comes up with needs to be the name of an entry in the + termcap file, otherwise a message about "Unknown terminal type" will + be printed and NetHack will exit. + +4. Both vmsbuild.com and Makefile.src have provisions to build NetHack's + There is currently no working version of the 'curses' interface for + OpenVMS. The NetHack source code for it won't compile using + the implementation of curses which is supplied with VMS (either the + VMS-specific variant or the BSD-derived one). If someone manages + to port 'ncurses' or 'PDcurses' to OpenVMS, it may be possible to + use either of those, as ncurses works on Unix and PDcurses works + on Windows and MS-DOS. + +5. NetHack contains code which attempts to make it secure in case it's + installed with privileges (to allow the playground to be protected + against world write access). This has only undergone limited testing, + so install NetHack with privileges at your own risk. If you discover + any potential security holes, please let us know so that we can take + steps to correct the problem(s). NetHack always includes filename + punctuation when accessing files, so that it should never be affected + by inadvertent or malicious logical name definitions, and it always + deactivates installed privileges prior to spawning a subprocess. + + Note to end users: "installing with privileges" is an option for + system managers who set up system-wide access to the game. Since + CMKRNL privilege and modification of the system boot routines are + both required, it is not an option for ordinary users. There are + no explicit instructions on how to do such an installation, because + only system managers who are already familiar with the process and + its potential security ramifications should even consider it. + + The default setup by install.com assumes no privileges and uses + world-writable files to allow arbitrary users to play. This is + NOT secure and not advisable in any environment where there are + untrustworthy users, but works fine for many sites. If you allow + users to run NetHack from captive accounts (VMS 5.1-* or earlier) + or from restricted accounts (5.2 and later), you should either make + sure that they do not have TMPMBX privilege or else disable NetHack's + ability to spawn an interactive subprocess. To disable subprocesses, + disable the "!" (shell escape) command by commenting out the definition + of SHELL in vmsconf.h prior to building the program. This necessity + may be removed in some future release, where NetHack will check for + captive accounts instead of spawning unconditionally. Note that + disabling the SHELL command also prevents spawning MAIL when scrolls + of new mail are received. + + In order for installed privileges to be used at all, the value of + HACKDIR (via Local_HACKDIR in vmsconf.h) compiled into the program + must correspond to the actual playground directory. If logical name + HACKDIR (or NETHACKDIR) is used to override that value, installed + privileges will be deactivated unless its value corresponds to the + same device and directory as the internal value. If that internal + value contains a logical name, only an executive-mode translation + will be honored; if there is no such translation, installed privs + will be deactivated. + + To be able to install nethack.exe with privileges (SYSPRV or GRPPRV, + perhaps EXQUOTA, depending on site usage and needs), you'll need to + link it with debugging and tracebacks both disabled. You can do this + by specifying an argument to vmsbuild.com when performing step #6 + above; pass it "/noTrace/noDebug" as the 4th parameter. + $ @[.SYS.VMS]VMSBUILD "" "" "" "/noTrace/noDebug" + /Trace/noDebug is the linker's normal default. If you've already + built NetHack, you can relink with tracebacks disabled by doing + $ @[.SYS.VMS]VMSBUILD "LINK" "" "" "/noTrace/noDebug" + +6. If you can't or won't install nethack.exe with privileges and if you + don't have access to a privileged account yourself, then if you intend + to allow other users to access your copy of NetHack you should probably + place an ACL on the playground directory and its save subdirectory. + The access control list should contain a default protection ACE which + grants delete+control access to the playground owner (ie, your own + account if there's no special games account involved). install.com + does not attempt to do this automatically at the present time. After + executing install.com to create the playground directory, perform a + pair of commands similar to the following + $ SET ACL/ACL=(IDENT=your_id, OPTIONS=DEFAULT, ACCESS=R+W+E+D+C) - + $_ device:[playground's.parent.directory]playground.DIR + $ SET ACL/ACL=(IDENT=your_id, OPTIONS=DEFAULT, ACCESS=R+W+E+D+C) - + $_ device:[playground.directory]SAVE.DIR + The two commands use the same options, but SET ACL won't accept a + list of files to modify. (For recent versions of VMS, SET ACL was + made obsolete in favor of SET FILE/ACL, which in turn has been made + obsolete in favor of SET SECURITY/CLASS=FILE/ACL; however, the older + forms will still work.) 'your_id' should be the rights identifier + which corresponds to the account which should retain access to those + files; 'device:[playground's.parent.directory]' is the name of the + parent directory for the playground (ie, if your playground directory + is disk$foo:[me.games.nethack.play], then you want to specify + disk$foo:[me.games.nethack]play.dir on the SET ACL command), and + 'device:[playground.directory]' is the playground itself. Those ACLs + establish a default protection scheme such that every newly created + file in those directories will have an ACL attached to it, and the + attached ACL will grant 'your_id' full access to the corresponding + file. That should allow you to clear away level files from aborted + games, and to delete old save files if necessary. It will not enable + you to run recover.exe on behalf of other users, because you won't be + able to create files owned by them unless you have elevated privileges. + +7. Many NetHack commands can be aborted by sending it the + character when it wants input. This is displayed as ESC inside the + game. Digital VK201 keyboards (used by VT2xx and VT3xx and older + VAXstations) and VK401 keyboards (used by VT4xx, newer VAXstations, + and DEC's X Terminals) do not have an key. They may + transmit for the key if the terminal or emulator + window is set to operate in VT100 mode, or there may be a setup-type + option for making the <` | ~> key behave as . If your + terminal does not have that, or if it's set to a mode where that + won't work, then just use instead. (Press the "[" key while + holding down the "Ctrl" key, then release both; and + have the same ASCII code and are indistinguishable once they reach + the computer; note that VAXstations and X Terminals _can_ tell the + difference, but that won't matter for NetHack.) + + VMS NetHack is configured to use the SYS$QIOW system service for + reading characters from the keyboard. This allows ^C and ^Y (as well + as ^X and ^O for wizard mode debugging) to be used as commands without + being intercepted or interpreted by the terminal driver. The code + which parses arrow and function keys is not perfect, and it's possible + to get strange results if you hold such keys down or just type too + quickly, particularly on slow multiplexor lines. Those keys are + never needed in actual play, and most function keys are just treated + as for use in aborting partial commands. + + VMS NetHack also still has code to use SMG$READ_KEYSTROKE instead. + That can be activated by modifying vmsconf.h and recompiling, but + it should never be necessary. If you use it, you'll need to press + either or twice to abort partial commands, or else + press an arbitrary function key, such as , once. + + If SUSPEND is defined in vmsconf.h, is used for that command. + Since Unix-style job control is not available, it's used for connecting + to the parent process if NetHack is running in a subprocess. When not + in a subprocess, it doesn't do anything except give a message to the + effect that it's not doing anything.... The suspend command does not + save the current game; if you use ^Z to attach to your parent process, + be sure to remember to eventually reattach to the NetHack subprocess; + otherwise the game in progress won't get saved when you logout. + +8. NetHack optionally maintains a logfile which receives one line appended + to it whenever a game ends. This can be disabled entirely by adding + an "#undef LOGFILE" directive to vmsconf.h prior to building the + program, or it can be disabled later by removing the file(s) LOGFILE.;* + from the playground directory. If not disabled prior to compilation, + the logfile can be reinitialized by simply creating an empty file + named LOGFILE in the playground, but make sure that users are able + to write into it, or new entries will not be appended. A somewhat + more elaborate log file named XLOGFILE containing more information is + handled similarly. + +9. Some attempt at support for VMS versions earlier than V4.6 has been + included, but no such obsolete system was available for testing it. + vmsbuild.com detects the need for the extra support routines and + arranges automatically for them to be compiled. The reason that + special support is needed is that the C Run-Time Library (VAXCRTL) + underwent a major revision for VMS V4.6 and several routines which + NetHack utilizes were not available prior to that upgrade. + + [That was written many years ago and the chance of it still working + is very small.] + +10. To access "wizard mode"--intended for debugging purposes, not to + spoil the game with unlimited wishes--you must be running from the + username compiled into the game via Local_WIZARD in vmsconf.h, and + you must specify "-D" on the command line when invoking NetHack. + Note that -D must be uppercase, and it must be in quotes to prevent + the C run-time library's program startup code from converting it into + lowercase. + $ @hackdir:nethack "-D" + Any character name you specify will be ignored in favor of "wizard". + + [More out of date information. Rather than compile-time Local_WIZARD, + users(s) allowed to run in wizard mode are now controlled by the entry + WIZARDS in the file SYSCONF.] + +11. At program startup time, NetHack uses the empty file PERM to prevent + two different processes from using the same character name (under the + same UIC ownership) at the same time. It does this by temporarily + giving that file a second directory entry named PERM.LOCK, then + removing the alternate entry once started. If the PERM file is + missing or inaccessible, NetHack will give a message and then quit. + Several possible messages and their usual causes are: + Can't find file perm;1 to lock! + PERM.;1 is missing from the playground directory. Fix: reinstall + the playground directory using install.com, or use CREATE or an editor + to make an empty file named PERM. Version number must be 1. + Can't lock perm;1 due to directory protection. + The playground directory is not allowing write access. Fix: players + need to be able to write files for dungeon levels and "bones" into + the playground directory. Set the protection or ACL on the xxx.DIR;1 + file in the playground's parent directory to allow write access. + Can't unlink perm.lock;1. + The empty file PERM.;1 is protected against delete access; only matters + under some versions of VMS. Fix: set the protection or ACL on PERM.;1 + to allow delete access to players. Under VMS V5.5-2, delete access is + not necessary. PERM does not have to remain writable. + Waiting for access to perm;1. (# retries left). + If some other process is also starting up NetHack at about the same + time, you may have to wait a short period. NetHack will retry once + per second, counting down to 0. If 0 is reached, the message + Perhaps there is an old perm.lock;1 around? + will be displayed and then NetHack will give up. Fix: to forcibly + remove a stale PERM.LOCK entry, issue the following command + $ SET FILE/REMOVE PERM.LOCK;1 + from the playground directory. The file PERM should remain intact. + Do not use that command for real files, only alternate directory + entries. If output from a DIRECTORY command on the playground reports + PERM.LOCK;1 no such file + then someone has deleted PERM.;1 while the synonym entry was still + in place, and PERM.LOCK was left as a dangling name which no longer + points at any file. The SET FILE/REMOVE command above will fix the + dangling name; a new PERM.;1 will need to be created as mentioned above. + + In similar fashion, synchronized access to the scoreboard file RECORD + is accomplished using temporary entry RECORD.LOCK and LOGFILE using + entry LOGFILE.LOCK. + +12. If necessary, send problem reports via e-mail to + + Always include version information for NetHack, the operating system + version, and the C compiler and version used, and the version of GNU + make utility used. + +8-March-2024 +# NetHack 3.7 Install370.vms $NHDT-Date: 1575245132 2019/12/02 00:05:32 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.16 $ +# NetHack may be freely redistributed. See license for details. diff --git a/sys/vms/Makefile.src b/sys/vms/Makefile.src index cc300d1c56..e0305218b0 100644 --- a/sys/vms/Makefile.src +++ b/sys/vms/Makefile.src @@ -147,23 +147,22 @@ 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 cmd.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 hack.c hacklib.c \ - insight.c invent.c light.c lock.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 extralev.c files.c fountain.c getpos.c glyphs.c hack.c \ + hacklib.c insight.c invent.c light.c lock.c \ mail.c makemon.c mcastu.c mhitm.c mhitu.c minion.c \ - mklev.c mkmap.c \ - mkmaze.c mkobj.c mkroom.c mon.c mondata.c monmove.c monst.c \ - mplayer.c mthrowu.c muse.c music.c o_init.c 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 shk.c shknam.c sit.c sounds.c sp_lev.c spell.c \ - steal.c steed.c sys.c teleport.c timeout.c topten.c track.c \ - trap.c u_init.c \ - uhitm.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 + mklev.c mkmap.c mkmaze.c mkobj.c mkroom.c mon.c mondata.c \ + monmove.c monst.c mplayer.c mthrowu.c muse.c music.c o_init.c \ + 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 sys.c teleport.c \ + timeout.c topten.c track.c trap.c u_init.c uhitm.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 #-# generated source files (vis_tab.c is gone; tile.c is handled separately #-# via WINxxxSRC) @@ -178,7 +177,7 @@ VERSOURCES = $(HACKCSRC) $(SYSSRC) $(WINSRC) $(RANDSRC) $(GENCSRC) 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 mextra.h mfndpos.h micro.h mkroom.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 \ @@ -192,12 +191,13 @@ 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,cmd.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,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, \ - files.obj,fountain.obj,hack.obj,hacklib.obj, \ - insight.obj,invent.obj + extralev.obj,files.obj,fountain.obj,getpos.obj,glyphs.obj,hack.obj, \ + hacklib.obj,insight.obj,invent.obj HOBJ3 = light.obj,lock.obj,mail.obj,makemon.obj,mcastu.obj, \ mhitm.obj,mhitu.obj,minion.obj,mklev.obj,mkmap.obj,mkmaze.obj, \ mkobj.obj,mkroom.obj,mon.obj,mondata.obj,monmove.obj @@ -205,11 +205,11 @@ HOBJ4 = mplayer.obj,mthrowu.obj,muse.obj,music.obj,o_init.obj,objnam.obj, \ options.obj,pager.obj,pickup.obj,pline.obj,polyself.obj, \ potion.obj,pray.obj,priest.obj,quest.obj,questpgr.obj,read.obj HOBJ5 = rect.obj,region.obj,restore.obj,rip.obj,rnd.obj,role.obj, \ - rumors.obj,save.obj,sfstruct.obj,shk.obj,shknam.obj,sit.obj, \ - sounds.obj,sp_lev.obj,spell.obj,steal.obj,steed.obj,sys.obj, \ + rumors.obj,save.obj,selvar.obj,sfstruct.obj,shk.obj,shknam.obj,sit.obj, \ + sounds.obj,sp_lev.obj,spell.obj,stairs.obj,steal.obj,steed.obj,sys.obj, \ teleport.obj,timeout.obj,topten.obj, track.obj,trap.obj -HOBJ6 = u_init.obj,uhitm.obj,vault.obj,vision.obj,weapon.obj, \ - were.obj,wield.obj,windows.obj,wizard.obj,worm.obj,worn.obj, \ +HOBJ6 = u_init.obj,uhitm.obj,utf8map.obj,vault.obj,vision.obj,weapon.obj, \ + were.obj,wield.obj,windows.obj,wizard.obj,wizcmds.obj,worm.obj,worn.obj, \ write.obj,zap.obj,version.obj HOBJ = $(FIRSTOBJ) $(SYSOBJ) $(WINOBJ) $(RANDOBJ) \ $(HOBJ1) $(HOBJ2) $(HOBJ3) $(HOBJ4) $(HOBJ5) $(HOBJ6) diff --git a/sys/vms/Makefile_dat.vms b/sys/vms/Makefile_dat.vms new file mode 100644 index 0000000000..37dd5e2d3a --- /dev/null +++ b/sys/vms/Makefile_dat.vms @@ -0,0 +1,156 @@ +# NetHack Datafiles Makefile.dat $NHDT-Date: 1596486993 2020/08/03 20:36:33 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.32 $ +# Copyright (c) 2018 by Pasi Kallinen +# NetHack may be freely redistributed. See license for details. + +# Root of source tree: +NHSROOT=.. + +# relative directories to dat +DOC=[-.doc] +DAT=[-.dat] +INCL=[-.include] +SRC=[-.src] +UTIL=[-.util] +SYSVMS=[-.sys.vms] +SYSSHR=[-.sys.share] +SYSUNIX=[-.sys.unix] +WINTTY=[-.win.tty] +WINSHR=[-.win.share] +WINTTY=[-.win.tty] +WINCURS=[-.win.curses] +WINX11=[-.win.X11] +WINQT=[-.win.Qt] +WINSHIM=[-.win.shim] +WINCHAIN=[-.win.chain] + +# Lua location relative to src +LUAVER=546 +LUADOTVER=5.4.6 +LUAUNDERVER=5_4_6 +LUAINC=[-.lib.lua.lua$(LUAVER).src] +LUALIB=[-.lib.lua]lua$(LUAVER).olb +LUASRCDIR =[-.lib.lua.lua$(LUAVER).src] +LUASRCINCDIR=$(LUASRCDIR) +#LUASRCINCDIR=SYS$COMMON:[LUA.INCLUDE] + +# +# If your compiler needs an appropriate switch to accept C99 code. +# VSI C defaults to /STANDARD=relaxed which allows several C +# dialects including C99. +# +# CSTD=/STANDARD=C99 +# + +# Compiler flags +CFLAGS :=/INCLUDE=($(INCL),$(LUAINC)) /NAMES=(AS_IS) $(CSTD) +CXXFLAGS = /INCLUDE_DIR=($(INCL),$(LUAINC)) /NAMES=(AS_IS) + +# Link flags +LFLAGS = + +# external programs +# MAKE = make +CC = CC +#$(TOUCH) :=SET FILE/TRUNCATE +TOUCH = append/New _NLA0: # only one file per $($(TOUCH)) +LINK = link +RM = delete +#TRUE uses an actual helper true.exe +TRUE = true +#FALSE uses an actual helper false.exe +FALSE = false +#ECHO uses an actual helper echo.exe +ECHO = echo +CXX ?= CXX +MOC ?= moc +MOCPATH ?= $(QTDIR)/bin/$(MOC) +# The default is for the TARGET_* variables to match the defaults. +# If we're cross-compiling these will get overridden elsewhere, likely via + +VARDAT = bogusmon data engrave epitaph rumors oracles options + +all: $(VARDAT) spec_levs quest_levs + +$(UTIL)makedefs.exe: + pipe set def [-.util] && $(MAKE) makedefs && set def [-.dat] + +$(UTIL)tile2x11.exe: + pipe set def [-.util] && $(MAKE) tile2x11 && set def [-.dat] + +$(UTIL)tile2bmp.exe: + pipe set def [-.util] && $(MAKE) tile2bmp) && set def [-.dat] + +$(UTIL)x11tiles.exe : $(UTIL)tile2x11.exe ../win/share/monsters.txt \ + ../win/share/objects.txt ../win/share/other.txt \ + ../win/share/monsters.txt + mcr $(UTIL)tile2x11.exe ../win/share/monsters.txt \ + ../win/share/objects.txt ../win/share/other.txt \ + -grayscale ../win/share/monsters.txt + +nhtiles.bmp: $(UTIL)tile2bmp.exe ../win/share/monsters.txt \ + ../win/share/objects.txt \ + ../win/share/other.txt + mcr $(UTIL)tile2bmp.exe $@ + +NetHack.ad: ../win/X11/NetHack.ad +# handle "#define foo bar" -lines + grep ^#define ../win/X11/NetHack.ad | \ + sed -e 's/^#define/s/g' -e 's/ */ /g' \ + -e 's/$$/ g/g' > NetHack.ad.tmp + grep -v ^#define ../win/X11/NetHack.ad | \ + sed -f NetHack.ad.tmp > NetHack.ad + -rm -f NetHack.ad.tmp + +pet_mark.xbm: $(WINX11)pet_mark.xbm + copy $(WINX11)pet_mark.xbm pet_mark.xbm + +pilemark.xbm: $(WINX11)pilemark.xbm + copy $(WINX11)pilemark.xbm pilemark.xbm + +rip.xpm: $(WINX11)rip.xpm + copy $(WINX11)rip.xpm rip.xpm + +nhsplash.xpm: $(WINSHR)nhsplash.xpm + copy $(WINSHR)nhsplash.xpm nhsplash.xpm + +nethack.icns: $(WINSHR)nhicns.uu + mcr $(UUDECODE) $(WINSHR)nhicns.uu + +data: data.base $(UTIL)makedefs.exe + mcr $(UTIL)makedefs.exe -d + +rumors: rumors.tru rumors.fal $(UTIL)makedefs.exe + mcr $(UTIL)makedefs.exe -r + +oracles: oracles.txt $(UTIL)makedefs.exe + mcr $(UTIL)makedefs.exe -h + +engrave: engrave.txt $(UTIL)makedefs.exe + mcr $(UTIL)makedefs.exe -s + +epitaph: epitaph.txt $(UTIL)makedefs.exe + mcr $(UTIL)makedefs.exe -s + +bogusmon: bogusmon.txt $(UTIL)makedefs.exe + mcr $(UTIL)makedefs.exe -s + +# note: 'options' should have already been made when include/date.h was created +options: $(UTIL)makedefs.exe + mcr $(UTIL)makedefs.exe -v + +# these don't actually do anything useful now that levcomp and dngcomp are gone +spec_levs: + $(TOUCH) spec_levs +quest_levs: + $(TOUCH) quest_levs + +clean: + -delete/log spec_levs.;*,quest_levs.;*,gitinfo.txt;* + +spotless: clean + -delete/log nhdat,$(addsuffix ;*$(comma),$(VARDAT) \ + x11tiles, pet_mark.xbm, pilemark.xbm, rip.xpm, mapbg.xpm, \ + rip.img, GEM_RSC.RSC, title.img, nh16.img, NetHack.ad, \ + nhsplash.xpm, nhtiles.bmp) + +#eof# diff --git a/sys/vms/Makefile_doc.vms b/sys/vms/Makefile_doc.vms new file mode 100644 index 0000000000..f797df0b75 --- /dev/null +++ b/sys/vms/Makefile_doc.vms @@ -0,0 +1,202 @@ +# NetHack Documentation Makefile. +# NetHack 3.7 Makefile.doc $NHDT-Date: 1596498290 2020/08/03 23:44:50 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.21 $ +# Copyright (c) 2015 by Kenneth Lorber, Kensington, Maryland +# NetHack may be freely redistributed. See license for details. + +# Root of source tree: +NHSROOT=[-] + +# relative directories from src +DOC=[-.doc] +DAT=[-.dat] +INCL=[-.include] +SRC=[-.src] +UTIL=[-.util] +SYSVMS=[-.sys.vms] +SYSSHR=[-.sys.share] +SYSUNIX=[-.sys.unix] +WINTTY=[-.win.tty] +WINSHR=[-.win.share] +WINTTY=[-.win.tty] +WINCURS=[-.win.curses] +WINX11=[-.win.X11] +WINQT=[-.win.Qt] +WINSHIM=[-.win.shim] +WINCHAIN=[-.win.chain] + +# Lua location relative to src +LUAVER=546 +LUADOTVER=5.4.6 +LUAUNDERVER=5_4_6 +LUAINC=[-.lib.lua.lua$(LUAVER).src] +LUALIB=[-.lib.lua]lua$(LUAVER).olb +LUASRCDIR =[-.lib.lua.lua$(LUAVER).src] +LUASRCINCDIR=$(LUASRCDIR) +#LUASRCINCDIR=SYS$COMMON:[LUA.INCLUDE] + +# +# If your compiler needs an appropriate switch to accept C99 code. +# VSI C defaults to /STANDARD=relaxed which allows several C +# dialects including C99. +# +# CSTD=/STANDARD=C99 +# + +# Compiler flags +CFLAGS :=/INCLUDE=($(INCL),$(LUAINC)) /NAMES=(AS_IS) $(CSTD) +CXXFLAGS = /INCLUDE_DIR=($(INCL),$(LUAINC)) /NAMES=(AS_IS) + +# Link flags +LFLAGS = + +# external programs +# MAKE = make +CC = CC +#touch :=SET FILE/TRUNCATE +touch = append/New _NLA0: +LINK = link +RM = delete +#TRUE uses an actual helper true.exe +TRUE = true +#FALSE uses an actual helper false.exe +FALSE = false +#ECHO uses an actual helper echo.exe +ECHO = echo +CXX ?= CXX +MOC ?= moc +MOCPATH ?= $(QTDIR)/bin/$(MOC) +# The default is for the TARGET_* variables to match the defaults. +# If we're cross-compiling these will get overridden elsewhere, likely via + +MAKEDEFS = $(UTIL)makedefs + +# Which version do we want to build? (XXX These are not used anywhere.) +GUIDEBOOK = Guidebook # regular ASCII file +#GUIDEBOOK = Guidebook.ps # PostScript file +#GUIDEBOOK = Guidebook.dvi # TeX device-independent file + +# Some versions of col need -x to keep them from converting spaces to tabs; +# some versions of col don't do the conversion by default and don't +# recognize the option. Sigh. +COLCMD = col -bx +#COLCMD = col -b + +# The command to use to generate a PostScript file +# PSCMD = ditroff | psdit +PSCMD = groff + +# Use the "cat" GUIDECMD if nroff and/or tbl and/or col are not installed +# Not appropriate for creating Guidebook.txt. +# GUIDECMD = cat Guidebook.txt +# +# Single page. Might need adjustment to .pl value +# GUIDECMD= $(GUIDE_PREFORMAT) | perl -pe 's/^(.mt)$$/.pl 4720v\n.in 0\n.po 8\n.ll 64m\n$$1/' | nroff -c -Tascii | $(COLCMD) +# +GUIDECMD = $(GUIDE_PREFORMAT) | nroff -c -Tascii | $(COLCMD) +ONEPAGECMD = $(ONEPAGE_PREFORMAT) | nroff -c -Tascii | $(COLCMD) \ + | sed -e '/EOF--EOF/,12345D' + +# Only generate output for the current configuration: +NHGREP = mcr $(MAKEDEFS) --grep --input - --output - +NEEDMAKEDEFS = $(MAKEDEFS) +# Generate output for all configurations: +#NHGREP = mcr $(MAKEDEFS) --grep --input - --output - --grep-define ALLDOCS +#NEEDMAKEDEFS = $(MAKEDEFS) +# Fallback: +#NHGREP = cat +#NEEDMAKEDEFS = + +GUIDEBOOK_MN_SRC = Guidebook.mn +GUIDEBOOK_MN = $(GUIDEBOOK_MN_SRC) +GUIDEBOOK_TEX_SRC = Guidebook.tex +GUIDEBOOK_TEX = $(GUIDEBOOK_TEX_SRC) + +GUIDE_PREFORMAT = cat $(GUIDEBOOK_MN) | $(NHGREP) | tbl tmac.n - +# for Guidebook.dat, unpaginated version of Guidebook.txt +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) + @echo disabled $@ on vms +# $(GUIDECMD) > Guidebook + +# Fancier output for those with ditroff, psdit and a PostScript printer. +Guidebook.ps : $(GUIDEBOOK_MN) tmac.n tmac.nh $(NEEDMAKEDEFS) + @echo disabled $@ on vms +# $(GUIDE_PREFORMAT) | $(PSCMD) > Guidebook.ps + +# Guidebook.tex is the same as Guidebook.mn but formatted with LaTeX. +# - The invocation command for LaTeX may vary in different installations. +# - To print Guidebook.dvi you need to use a suitable dvi-driver. +# - LaTeX needs to be run twice; second pass uses Guidebook.aux made by first. +Guidebook.dvi : $(GUIDEBOOK_TEX) + @echo disabled $@ on vms +# latex $(GUIDEBOOK_TEX) +# latex $(GUIDEBOOK_TEX) + +# makedefs has more dependencies than these; this is mainly to cope with the +# case where it hasn't been built yet since it is usually needed for $(NHGREP) +# (note: 'make makedefs', not 'make $(MAKEDEFS)') +$(MAKEDEFS) : ../util/makedefs.c ../include/config.h ../src/mdlib.c \ + ../util/mdgrep.h + pipe set def $(UTIL) && make makedefs && set def [-.doc] + +GAME = nethack +MANDIR = /usr/man/man6 +MANEXT = 6 + +# manual installation for most BSD-style systems +GAMEMANCREATE = cat nethack.6 | $(NHGREP) > +RCVRMANCREATE = cat recover.6 | $(NHGREP) > +DLBMANCREATE = cat dlb.6 | $(NHGREP) > +MDMANCREATE = cat makedefs.6 | $(NHGREP) > +# manual installation for most SYSV-style systems +# GAMEMANCREATE = cat nethack.6 | $(NHGREP) | nroff -man - > +# RCVRMANCREATE = cat recover.6 | $(NHGREP) | nroff -man - > +# DLBMANCREATE = cat dlb.6 | $(NHGREP) | nroff -man - > +# MDMANCREATE = cat makedefs.6 | $(NHGREP) | nroff -man - > + +manpages: + echo $@ disabled on VMS +# -$(GAMEMANCREATE) $(MANDIR)/$(GAME).$(MANEXT) +# -$(RCVRMANCREATE) $(MANDIR)/recover.$(MANEXT) +# -$(DLBMANCREATE) $(MANDIR)/dlb.$(MANEXT) +# -$(MDMANCREATE) $(MANDIR)/makedefs.$(MANEXT) + +# manual creation for distribution +DISTRIB = Guidebook.txt nethack.txt recover.txt \ + dlb.txt makedefs.txt + +distrib: $(DISTRIB) + echo $@ disabled on VMS +# @echo "Plain text documentation is up to date." + +Guidebook.txt : $(GUIDEBOOK_MN) tmac.n tmac.nh $(NEEDMAKEDEFS) + echo $@ disabled on VMS +# $(GUIDECMD) > $@ +Guidebook.dat : Gbk-1pg-pfx.mn Gbk-1pg-sfx.mn $(GUIDEBOOK_MN) tmac.n tmac.nh \ + $(NEEDMAKEDEFS) + echo $@ disabled on VMS +# $(ONEPAGECMD) > $@ + +MAN2TXT = $(NHGREP) | nroff -man - | $(COLCMD) +nethack.txt : nethack.6 + echo $@ disabled on VMS +# cat nethack.6 | $(MAN2TXT) > nethack.txt +recover.txt : recover.6 + echo $@ disabled on VMS +# cat recover.6 | $(MAN2TXT) > recover.txt +dlb.txt : dlb.6 + echo $@ disabled on VMS +# cat dlb.6 | $(MAN2TXT) > dlb.txt +makedefs.txt : makedefs.6 + echo $@ disabled on VMS +# cat makedefs.6 | $(MAN2TXT) > makedefs.txt + +clean: + -delete/log Guidebook.aux;*, Guidebook.log;* + +spotless: clean + -delete/log Guidebook.;*, Guidebook.dat;*, Guidebook.ps;*, \ + Guidebook.dvi;* diff --git a/sys/vms/Makefile_src.vms b/sys/vms/Makefile_src.vms new file mode 100644 index 0000000000..b5a4d793eb --- /dev/null +++ b/sys/vms/Makefile_src.vms @@ -0,0 +1,840 @@ +# NetHack 3.7 Makefile.src $NHDT-Date: 1654287121 2022/06/03 20:12:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.145 $ +# Copyright (c) 2024 by Michael Allison +# NetHack may be freely redistributed. See license for details. +# +# OpenVMS GNU Makefile for OpenVMS 9.x and VSI C +# with GNU Make V4.1 (or greater). +# +# Tested on x86_64 version of OpenVMS V9.2-1. +# + +# Root of source tree: +NHSROOT=.. + +# If we're cross-compiling, a hints file will override this +# to a unique target directory, but otherwise the obj files just go +# into [-.src] +TARGETPFX= + +# relative directories from src +DOC=[-.doc] +DAT=[-.dat] +INCL=[-.include] +SRC=[-.src] +UTIL=[-.util] +SYSVMS=[-.sys.vms] +SYSSHR=[-.sys.share] +SYSUNIX=[-.sys.unix] +WINTTY=[-.win.tty] +WINSHR=[-.win.share] +WINTTY=[-.win.tty] +WINCURS=[-.win.curses] +WINX11=[-.win.X11] +WINQT=[-.win.Qt] +WINSHIM=[-.win.shim] +WINCHAIN=[-.win.chain] + +# Lua +LUAVER=546 +LUADOTVER=5.4.6 +LUAUNDERVER=5_4_6 +# Lua location relative to src +LUAINC=[-.lib.lua.lua$(LUAVER).src] +LUALIB=[-.lib.lua]lua$(LUAVER).olb +LUASRCDIR =[-.lib.lua.lua$(LUAVER).src] +LUASRCINCDIR=$(LUASRCDIR) + +# +# If your compiler needs an appropriate switch to accept C99 code. +# VSI C defaults to /STANDARD=relaxed which allows several C +# dialects including C99. +# +# CSTD=/STANDARD=C99 +# + +# Compiler flags +CFLAGS :=/INCLUDE=($(INCL),$(LUAINC)) /NAMES=(AS_IS) $(CSTD) +CXXFLAGS = /INCLUDE_DIR=($(INCL),$(LUAINC)) /NAMES=(AS_IS) + +# Link flags +LFLAGS = + +# external programs +# MAKE = make +CC = CC +#touch :=SET FILE/TRUNCATE +touch = append/New _NLA0: +LINK = link +RM = delete +#TRUE uses an actual helper true.exe +TRUE = true +#FALSE uses an actual helper false.exe +FALSE = false +#ECHO uses an actual helper echo.exe +ECHO = echo +CXX ?= CXX +MOC ?= moc +MOCPATH ?= $(QTDIR)/bin/$(MOC) +# The default is for the TARGET_* variables to match the defaults. +# If we're cross-compiling these will get overridden elsewhere, likely via +# a hints file. TARGETPFX was set above earlier. +TARGET_CC = $(CC) +TARGET_CFLAGS = $(CFLAGS) +TARGET_LINK = $(LINK) +TARGET_LFLAGS = $(LFLAGS) +TARGET_CXX = $(CXX) +TARGET_CXXFLAGS = $(CXXFLAGS) +TARGET_LIBS = $(LIBS) + +HACKLIB=hacklib.olb +HACKLIBOBJLIST=hacklib.obj,[-.util]panic.obj + +# Set SYSTEM target +SYSTEM = SysVMS + +comma:=, + +# all .h files +CONFIGBASEH := color config config1 patchlevel tradstdc hacklib integer \ + global coord vmsconf cstd nhlua unixconf \ + pcconf micro windconf warnings fnamesiz + +HACKBASEH := hack lint align dungeon wintype sym defsym \ + mkroom artilist objclass objects youprop \ + prop permonst monattk monflag monsters \ + mondata context rm botl rect region trap \ + display vision seffects selvar sndprocs stairs decl \ + quest spell obj engrave you attrib monst \ + mextra skills timeout flag winprocs sys + +CONFIG_H = $(addsuffix .h, $(addprefix $(INCL), $(CONFIGBASEH))) +HACK_H = $(addsuffix .h, $(addprefix $(INCL), $(CONFIGBASEH) $(HACKBASEH))) + +#QTn_H = +# 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 \ + files fountain getpos glyphs hack hacklib insight invent isaac64 \ + light lock mail makemon mcastu mdlib mhitm mhitu minion mklev \ + mkmap mkmaze mkobj mkroom mon \ + mondata monmove monst mplayer mthrowu muse music \ + nhlua nhlsel nhlobj objnam o_init objects \ + options pager pickup pline polyself potion pray \ + priest quest questpgr read rect region report restore \ + rip rnd role rumors save selvar sfstruct \ + shk shknam sit sounds \ + sp_lev spell stairs steal steed strutil symbols sys teleport \ + timeout topten track trap u_init utf8map \ + uhitm vault version vision weapon were wield \ + windows wizard wizcmds worm worn write zap + +# the date file +DATEFILES = date + +# --------------- +# platforms / OS +# --------------- + +# Files for sys.vms +OPENVMSFILES = vmsfiles vmsmail vmsmisc vmstty vmsunix +OPENVMSMAIN = vmsmain + +# --------------- +# window ports +# --------------- + +# files for a straight tty interface +WINTTYFILES = getline termcap topl wintty + +# Files for curses interface +WINCURSESFILES = cursmain curswins cursmisc cursdial cursstat cursinit \ + cursmesg cursinvt + +# Files for X11 interface +WINX11FILES = Window dialogs winX winmap winmenu winmesg winmisc \ + winstat wintext winval + +# Files for Qt interface +WINQTFILES = qt_bind qt_click qt_clust qt_delay qt_glyph qt_icon qt_inv \ + qt_key qt_line qt_main qt_map qt_menu qt_msg qt_plsel qt_rip qt_set \ + qt_stat qt_str qt_streq qt_svsel qt_win qt_xcmd qt_yndlg + +# shared win files +#WINSHAREFILES = ioctl + +# +# Files for a Qt 4, Qt 5, or Qt 6 interface +# +# generated source files made by Qt's 'moc' program from $(WINQT)qt_*.h; +# appended to WINQTSRC for use by 'make depend' +# WINQTMOC = 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 +# WINQTSRC = $(WINQT)qt_bind.cpp $(WINQT)qt_click.cpp \ +# $(WINQT)qt_clust.cpp $(WINQT)qt_delay.cpp \ +# $(WINQT)qt_glyph.cpp $(WINQT)qt_icon.cpp $(WINQT)qt_inv.cpp \ +# $(WINQT)qt_key.cpp $(WINQT)qt_line.cpp $(WINQT)qt_main.cpp \ +# $(WINQT)qt_map.cpp $(WINQT)qt_menu.cpp $(WINQT)qt_msg.cpp \ +# $(WINQT)qt_plsel.cpp $(WINQT)qt_rip.cpp $(WINQT)qt_set.cpp \ +# $(WINQT)qt_stat.cpp $(WINQT)qt_str.cpp $(WINQT)qt_streq.cpp \ +# $(WINQT)qt_svsel.cpp $(WINQT)qt_win.cpp $(WINQT)qt_xcmd.cpp \ +# $(WINQT)qt_yndlg.cpp $(WINQTMOC) tile.c +# WINQTOBJ = $(TARGETPFX)qt_bind.o $(TARGETPFX)qt_click.o \ +# $(TARGETPFX)qt_clust.o $(TARGETPFX)qt_delay.o \ +# $(TARGETPFX)qt_glyph.o $(TARGETPFX)qt_icon.o \ +# $(TARGETPFX)qt_inv.o $(TARGETPFX)qt_key.o $(TARGETPFX)qt_line.o \ +# $(TARGETPFX)qt_main.o $(TARGETPFX)qt_map.o $(TARGETPFX)qt_menu.o \ +# $(TARGETPFX)qt_msg.o $(TARGETPFX)qt_plsel.o $(TARGETPFX)qt_rip.o \ +# $(TARGETPFX)qt_set.o $(TARGETPFX)qt_stat.o $(TARGETPFX)qt_str.o \ +# $(TARGETPFX)qt_streq.o $(TARGETPFX)qt_svsel.o $(TARGETPFX)qt_win.o \ +# $(TARGETPFX)qt_xcmd.o $(TARGETPFX)qt_yndlg.o #$(TARGETPFX)tile.o +# + +# ----------------- +# derived variables +# ----------------- + +HACKSRC = $(addsuffix .c, $(addprefix $(SRC), $(HACKFILES))) +DATESRC = $(addsuffix .c, $(addprefix $(SRC), $(DATEFILES))) +VMSSRC = $(addsuffix .c, $(addprefix $(SYSVMS), $(OPENVMSFILES))) +WINTTYSRC = $(addsuffix .c, $(addprefix $(WINTTY), $(WINTTYFILES))) +WINCURSESSRC = $(addsuffix .c, $(addprefix $(WINCURS), $(WINCURSESFILES))) +WINX11SRC = $(addsuffix .c, $(addprefix $(WINX11), $(WINX11FILES))) +WINQTSRC = $(addsuffix .cpp, $(addprefix $(WINQT), $(WINQTFILES))) +#WINSHARESRC = $(addsuffix .c, $(addprefix $(WINSHR), $(WINSHAREFILES))) +MAINSRC = $(addsuffix .c, $(addprefix $(SYSVMS), $(OPENVMSMAIN))) + +HACKOBJS = $(addsuffix .obj, $(HACKFILES)) +DATEOBJ = $(addsuffix .obj, $(DATEFILES)) +VMSOBJS = $(addsuffix .obj, $(OPENVMSFILES)) +WINTTYOBJS = $(addsuffix .obj, $(WINTTYFILES)) +#WINCURSESOBJS = $(addsuffix .obj, $(WINCURSESFILES)) +#WINX11OBJS = $(addsuffix .obj, $(WINX11FILES)) +#WINQTOBJS = $(addsuffix .obj, $(WINQTFILES)) +#WINSHAREOBJS = $(addsuffix .obj, $(WINSHAREFILES)) +MAINOBJ = $(addsuffix .obj, $(OPENVMSMAIN)) + +# file for regular expression matching +#REGEXFILE ?= posixregex +#REGEXOBJ ?= pmatchregex +REGEXFILE ?= cppregex +ifeq "$(REGEXFILE)" "cppregex" +REGEXSRC = $(addsuffix .cpp, $(addprefix $(SYSSHR), $(REGEXFILE))) +else +REGEXSRC = $(addsuffix .c, $(addprefix $(SYSSHR), $(REGEXFILE))) +endif +REGEXOBJ = $(addsuffix .obj, $(REGEXFILE)) + +# file for termcap +TERMCAPFILE ?= tclib +TERMCAPSRC = $(addsuffix .c, $(addprefix $(SYSSHR), $(TERMCAPFILE))) +TERMCAPOBJ = $(addsuffix .obj, $(TERMCAPFILE)) + +# if you defined RANDOM in unixconf.h since your system did not come +# with a reasonable random number generator +# RANDOBJ = $(TARGETPFX)random.obj +#RANDOBJ = + +LUABASESRC = lapi lauxlib lbaselib lcode lcorolib lctype ldblib \ + ldebug ldo ldump lfunc lgc linit liolib llex \ + lmathlib lmem loadlib lobject lopcodes loslib \ + lparser lstate lstring lstrlib ltable ltablib \ + ltm lundump lutf8lib lvm lzio +#LUASRC = $(addsuffix .c, $(addprefix $(LUASRCDIR), $(LUABASESRC))) +LUAOBJS = $(addsuffix .obj, $(LUABASESRC)) +LUALIBOBJS = $(addsuffix .obj$(comma), $(LUABASESRC)) +LUASPOTLESSOBJS = $(addsuffix .obj;*$(comma), $(LUABASESRC)) + +ALLSRC = $(HACKSRC) $(VMSSRC) $(WINTTYSRC) $(WINCURSESSRC) \ + $(WINX11SRC) $(WINQTSRC) $(WINSHARESRC) \ + $(REGEXSRC) $(TERMCAPSRC) +# +# $(TARGETPFX)date.o is not included in this list +ALLOBJS = $(HACKOBJS) $(VMSOBJS) $(WINTTYOBJS) $(RANDOBJ) \ + $(WINCURSESOBJS) $(WINX11OBJS) $(WINQTOBJS) \ + $(WINSHAREOBJS) $(REGEXOBJ) $(TERMCAPOBJ) + +GAME = nethack +# GAME = nethack.prg +GAMEBIN = $(GAME).exe + +LINKOBJLIST = $(addsuffix $(comma), $(ALLOBJS)) +CLEANFILES = $(addsuffix ;*$(comma), $(ALLOBJS) $(MAINOBJ) \ + $(GAMEBIN)) \ + $(DATEOBJ);* +SPOTLESSFILES = $(addsuffix ;*$(comma), $(LUASPOTLESSOBJS) \ + luaplaceholder.obj) + +# rules +%.obj: $(SRC)%.c + $(CC) $(CFLAGS) $< /OBJECT=$@ + +%.obj: $(SYSVMS)%.c + $(CC) $(CFLAGS) $< /OBJECT=$@ + +%.obj: $(SYSSHR)%.c + $(CC) $(CFLAGS) $< /OBJECT=$@ + +%.obj: $(SYSSHR)%.cpp + $(CXX) $(CXXFLAGS) $< -o $@ + +%.obj: $(LUASRCDIR)%.c + $(CC) $(CFLAGS) /define=(LUA_USE_C89) $< /OBJECT=$@ + +# targets +.PHONY: SysVMS all pregame $(GAME) clean spotless package +.PHONY: tell-fetch-lua fetch-lua + +# first target is also the default target for 'make' without any arguments +all: $(GAME) + @echo "" + +pregame: + $(PREGAME) + +$(GAME): pregame $(HACKLIB) $(MAKEDEFS) $(LUALIB) $(WAVS) $(GAMEBIN) + @echo "$(GAME) is up to date." + +$(GAMEBIN): $(LUALIB) $(MAINOBJ) $(ALLOBJS) $(DATEOBJ) + @echo "Linking $(GAME)." +# @echo $(TARGET_LINK) $(TARGET_LFLAGS) $(MAINOBJ), \ +# $(LINKOBJLIST) $(DATEOBJ) \ +# /EXECUTABLE=$@ \ +# +sys$$common:[lua.lib]liblua.olb/library + $(AT)$(TARGET_LINK) $(TARGET_LFLAGS) $(MAINOBJ), \ + $(LINKOBJLIST) $(DATEOBJ) \ + /EXECUTABLE=$@\ + +$(LUALIB)/library + +hacklib.olb: $(HACKLIBOBJLIST) + if f$$search("hacklib.olb").eqs."" then - + library/create hacklib.olb/object + library/insert/replace hacklib.olb $(HACKLIBOBJLIST) + +#$(WINLIB) $(TARGET_LIBS) $(LUALIB) + +$(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 \#"include ""$(LUASRCINCDIR)lualib.h""" >> $@ + @echo \#"include ""$(LUASRCINCDIR)lauxlib.h""" >> $@ + @echo "/*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 \#"include ""sys$$common:[lua.include]lualib.h""" >> $@ +# @echo \#"include ""sys$$common:[lua.include]lauxlib.h""" >> $@ +# @echo "/*nhlua.h*/" >> $@ + +$(LUALIB): [-.lib.lua]lua$(LUAVER).dir $(LUAOBJS) luaplaceholder.obj + if f$$search("$(LUALIB)").eqs."" then library/create/obj $(LUALIB) + library/insert/replace $(LUALIB) $(LUALIBOBJS) luaplaceholder.obj + + +lapi.obj: $(LUASRCDIR)lapi.c +lauxlib.obj: $(LUASRCDIR)lauxlib.c +lbaselib.obj: $(LUASRCDIR)lbaselib.c +lcode.obj: $(LUASRCDIR)lcode.c +lcorolib.obj: $(LUASRCDIR)lcorolib.c +lctype.obj: $(LUASRCDIR)lctype.c +ldblib.obj: $(LUASRCDIR)ldblib.c +ldebug.obj: $(LUASRCDIR)ldebug.c +ldo.obj: $(LUASRCDIR)ldo.c +ldump.obj: $(LUASRCDIR)ldump.c +lfunc.obj: $(LUASRCDIR)lfunc.c +lgc.obj: $(LUASRCDIR)lgc.c +linit.obj: $(LUASRCDIR)linit.c +liolib.obj: $(LUASRCDIR)liolib.c +llex.obj: $(LUASRCDIR)llex.c +lmathlib.obj: $(LUASRCDIR)lmathlib.c +lmem.obj: $(LUASRCDIR)lmem.c +loadlib.obj: $(LUASRCDIR)loadlib.c +lobject.obj: $(LUASRCDIR)lobject.c +lopcodes.obj: $(LUASRCDIR)lopcodes.c +loslib.obj: $(LUASRCDIR)loslib.c +lparser.obj: $(LUASRCDIR)lparser.c +lstate.obj: $(LUASRCDIR)lstate.c +lstring.obj: $(LUASRCDIR)lstring.c +lstrlib.obj: $(LUASRCDIR)lstrlib.c +ltable.obj: $(LUASRCDIR)ltable.c +ltablib.obj: $(LUASRCDIR)ltablib.c +ltm.obj: $(LUASRCDIR)ltm.c +lundump.obj: $(LUASRCDIR)lundump.c +lutf8lib.obj: $(LUASRCDIR)lutf8lib.c +lvm.obj: $(LUASRCDIR)lvm.c +lzio.obj: $(LUASRCDIR)lzio.c + +#LUABASESRC = lapi lauxlib lbaselib lcode lcorolib lctype ldblib \ +# ldebug ldo ldump lfunc lgc linit liolib llex \ +# lmathlib lmem loadlib lobject lopcodes loslib \ +# lparser lstate lstring lstrlib ltable ltablib \ +# ltm lundump lutf8lib lvm lzio + + +luaplaceholder.obj: luaplaceholder.c + $(CC) $(CFLAGS) luaplaceholder.c /OBJECT=$@ +luaplaceholder.c: + @echo int placeholder = 1; >luaplaceholder.c + +[-.lib]lua.dir: + create/directory [-.lib.lua] + +[-]lib.dir: + create/directory [-.lib] + +[-.lib.lua]lua$(LUAVER).dir: + @echo You need to make fetch-lua + false + +fetch-lua: [-]lib.dir [-.lib]lua.dir + if f$$search("[-.lib.lua]lua$(LUAVER).tgz").eqs."" then \ + curl http://www.lua.org/ftp/lua-$(LUADOTVER).tar.gz \ + --output [-.lib.lua]lua$(LUAVER).tgz + if f$$search("[-.lib.lua]lua$(LUAVER).tgz").nes."" then \ + gzip --force -d [-.lib.lua]lua$(LUAVER).tgz + if f$$search("[-.lib.lua]lua$(LUAVER).tar").nes."" then \ + pipe set def [-.lib.lua] && \ + tar -xf lua$(LUAVER).tar && \ + set def [--.src] + if (f$$search("[-.lib.lua]lua-$(LUAUNDERVER).DIR;1").nes."" \ + .AND. f$$search("[-.lib.lua]lua$(LUAVER).dir;1").eqs."") then \ + rename [-.lib.lua]lua-$(LUAUNDERVER).DIR;1 \ + [-.lib.lua]lua$(LUAVER).dir;1 + +tile.c: $(WINSHR)tilemap.c $(HACK_H) + @( cd $(UTIL) ; $(MAKE) $(SRC)tile.c ) + +# date.c should be recompiled any time any of the source or include code +# is modified. +$(DATEOBJ): $(DATESRC) $(HACK_H) $(HACKCSRC) $(ALLOBJS) + $(TARGET_CC) $(TARGET_CFLAGS) $(GITHASH) $(GITBRANCH) \ + /OBJECT=$@ $(DATESRC) + +tags: $(CSOURCES) + @echo ctags -tw ... + @ctags -tw $(CSOURCES) + @( cd $(INCL) ; ctags -tw $(HSOURCES) ) + @( cd $(UTIL) ; $(MAKE) tags ) + +clean: + -delete/log $(CLEANFILES) + +spotless: clean + -delete/log $(SPOTLESSFILES) $(INCL)nhlua.h;* +# -$(RM) $(GAMEBIN);* + +package: + @echo packaging complete (nothing to do). + +# VMS-specific code +vmsmain.obj: $(SYSVMS)vmsmain.c $(HACK_H) $(INCL)dlb.h +vmstty.obj: $(SYSVMS)vmstty.c $(HACK_H) $(INCL)wintty.h \ + $(INCL)tcap.h +vmsunix.obj: $(SYSVMS)vmsunix.c $(HACK_H) +vmsmisc.obj: $(SYSVMS)vmsmisc.c $(SYSVMS)oldcrtl.c $(CONFIG_H) +vmsfiles.obj: $(SYSVMS)vmsfiles.c $(CONFIG_H) +vmsmail.obj: $(SYSVMS)vmsmail.c $(CONFIG_H) $(INCL)mail.h \ + $(INCL)wintype.h $(INCL)winprocs.h $(INCL)color.h + +# DO NOT DELETE THIS LINE OR CHANGE ANYTHING BEYOND IT +# +$(TARGETPFX)pcmain.obj: $(SYSSHR)pcmain.c $(HACK_H) $(INCL)dlb.h + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(SYSSHR)pcmain.c +$(TARGETPFX)pcsys.obj: $(SYSSHR)pcsys.c $(HACK_H) $(INCL)wintty.h + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(SYSSHR)pcsys.c +$(TARGETPFX)pctty.obj: $(SYSSHR)pctty.c $(HACK_H) $(INCL)wintty.h + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(SYSSHR)pctty.c +$(TARGETPFX)pcunix.obj: $(SYSSHR)pcunix.c $(HACK_H) $(INCL)wintty.h + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(SYSSHR)pcunix.c +$(TARGETPFX)pmatchregex.obj: $(SYSSHR)pmatchregex.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(SYSSHR)pmatchregex.c +$(TARGETPFX)posixregex.obj: $(SYSSHR)posixregex.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(SYSSHR)posixregex.c +$(TARGETPFX)random.obj: $(SYSSHR)random.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(SYSSHR)random.c +$(TARGETPFX)ioctl.obj: $(SYSSHR)ioctl.c $(HACK_H) $(INCL)tcap.h + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(SYSSHR)ioctl.c +$(TARGETPFX)unixtty.obj: $(SYSSHR)unixtty.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(SYSSHR)unixtty.c +$(TARGETPFX)unixmain.obj: $(SYSUNIX)unixmain.c $(HACK_H) $(INCL)dlb.h + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(SYSUNIX)unixmain.c +$(TARGETPFX)unixunix.obj: $(SYSUNIX)unixunix.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(SYSUNIX)unixunix.c +$(TARGETPFX)unixres.obj: $(SYSUNIX)unixres.c $(CONFIG_H) + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(SYSUNIX)unixres.c +$(TARGETPFX)getline.obj: $(WINTTY)getline.c $(HACK_H) $(INCL)wintty.h \ + $(INCL)func_tab.h + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(WINTTY)getline.c +$(TARGETPFX)termcap.obj: $(WINTTY)termcap.c $(HACK_H) $(INCL)wintty.h \ + $(INCL)tcap.h + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(WINTTY)termcap.c +$(TARGETPFX)topl.obj: $(WINTTY)topl.c $(HACK_H) $(INCL)tcap.h \ + $(INCL)wintty.h + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(WINTTY)topl.c +$(TARGETPFX)wintty.obj: $(WINTTY)wintty.c $(HACK_H) $(INCL)dlb.h \ + $(INCL)tcap.h $(INCL)wintty.h + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(WINTTY)wintty.c +$(TARGETPFX)cursmain.obj: $(WINCURS)cursmain.c $(HACK_H) $(INCL)wincurs.h + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(WINCURS)cursmain.c +$(TARGETPFX)curswins.obj: $(WINCURS)curswins.c $(HACK_H) \ + $(INCL)wincurs.h $(WINCURS)curswins.h + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(WINCURS)curswins.c +$(TARGETPFX)cursmisc.obj: $(WINCURS)cursmisc.c $(HACK_H) \ + $(INCL)wincurs.h $(WINCURS)cursmisc.h \ + $(INCL)func_tab.h $(INCL)dlb.h + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(WINCURS)cursmisc.c +$(TARGETPFX)cursdial.obj: $(WINCURS)cursdial.c $(HACK_H) \ + $(INCL)wincurs.h $(WINCURS)cursdial.h \ + $(INCL)func_tab.h + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(WINCURS)cursdial.c +$(TARGETPFX)cursstat.obj: $(WINCURS)cursstat.c $(HACK_H) \ + $(INCL)wincurs.h $(WINCURS)cursstat.h + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(WINCURS)cursstat.c +$(TARGETPFX)cursinit.obj: $(WINCURS)cursinit.c $(HACK_H) \ + $(INCL)wincurs.h $(WINCURS)cursinit.h + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(WINCURS)cursinit.c +$(TARGETPFX)cursmesg.obj: $(WINCURS)cursmesg.c $(HACK_H) \ + $(INCL)wincurs.h $(WINCURS)cursmesg.h + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(WINCURS)cursmesg.c +$(TARGETPFX)cursinvt.obj: $(WINCURS)cursinvt.c $(HACK_H) \ + $(INCL)wincurs.h $(WINCURS)cursinvt.h + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(WINCURS)cursinvt.c +$(TARGETPFX)Window.obj: $(WINX11)Window.c $(INCL)xwindowp.h \ + $(INCL)xwindow.h $(CONFIG_H) $(INCL)lint.h \ + $(INCL)winX.h $(INCL)color.h $(INCL)wintype.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) /OBJECT=$@ $(WINX11)Window.c +$(TARGETPFX)dialogs.obj: $(WINX11)dialogs.c $(CONFIG_H) $(INCL)lint.h \ + $(INCL)winX.h $(INCL)color.h $(INCL)wintype.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) /OBJECT=$@ $(WINX11)dialogs.c +$(TARGETPFX)winX.obj: $(WINX11)winX.c $(HACK_H) $(INCL)winX.h \ + $(INCL)dlb.h $(INCL)xwindow.h $(WINX11)nh72icon \ + $(WINX11)nh56icon $(WINX11)nh32icon + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) /OBJECT=$@ $(WINX11)winX.c +$(TARGETPFX)winmap.obj: $(WINX11)winmap.c $(INCL)xwindow.h $(HACK_H) \ + $(INCL)dlb.h $(INCL)winX.h $(INCL)tile2x11.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) /OBJECT=$@ $(WINX11)winmap.c +$(TARGETPFX)winmenu.obj: $(WINX11)winmenu.c $(HACK_H) $(INCL)winX.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) /OBJECT=$@ $(WINX11)winmenu.c +$(TARGETPFX)winmesg.obj: $(WINX11)winmesg.c $(INCL)xwindow.h $(HACK_H) \ + $(INCL)winX.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) /OBJECT=$@ $(WINX11)winmesg.c +$(TARGETPFX)winmisc.obj: $(WINX11)winmisc.c $(HACK_H) $(INCL)func_tab.h \ + $(INCL)winX.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) /OBJECT=$@ $(WINX11)winmisc.c +$(TARGETPFX)winstat.obj: $(WINX11)winstat.c $(HACK_H) $(INCL)winX.h \ + $(INCL)xwindow.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) /OBJECT=$@ $(WINX11)winstat.c +$(TARGETPFX)wintext.obj: $(WINX11)wintext.c $(HACK_H) $(INCL)winX.h \ + $(INCL)xwindow.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) /OBJECT=$@ $(WINX11)wintext.c +$(TARGETPFX)winval.obj: $(WINX11)winval.c $(HACK_H) $(INCL)winX.h + $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) /OBJECT=$@ $(WINX11)winval.c +$(TARGETPFX)tile.obj: tile.c $(HACK_H) +$(TARGETPFX)winshim.obj: $(WINSHIM)winshim.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(WINSHIM)winshim.c +$(TARGETPFX)cppregex.obj: $(SYSSHR)cppregex.cpp $(CONFIG_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(SYSSHR)cppregex.cpp +$(TARGETPFX)qt_bind.obj: $(WINQT)qt_bind.cpp $(HACK_H) $(WINQT)qt_pre.h \ + $(WINQT)qt_post.h $(WINQT)qt_bind.h $(WINQT)qt_main.h \ + $(WINQT)qt_kde0.h $(WINQT)qt_click.h $(WINQT)qt_delay.h \ + $(WINQT)qt_xcmd.h $(WINQT)qt_key.h $(WINQT)qt_map.h \ + $(WINQT)qt_win.h $(WINQT)qt_clust.h $(WINQT)qt_menu.h \ + $(WINQT)qt_rip.h $(WINQT)qt_msg.h $(WINQT)qt_plsel.h \ + $(WINQT)qt_svsel.h $(WINQT)qt_set.h $(WINQT)qt_stat.h \ + $(WINQT)qt_icon.h $(WINQT)qt_streq.h $(WINQT)qt_line.h \ + $(WINQT)qt_yndlg.h $(WINQT)qt_str.h $(INCL)dlb.h \ + $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_bind.cpp +$(TARGETPFX)qt_click.obj: $(WINQT)qt_click.cpp $(HACK_H) $(WINQT)qt_pre.h \ + $(WINQT)qt_post.h $(WINQT)qt_click.h $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_click.cpp +$(TARGETPFX)qt_clust.obj: $(WINQT)qt_clust.cpp $(WINQT)qt_clust.h $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_clust.cpp +$(TARGETPFX)qt_delay.obj: $(WINQT)qt_delay.cpp $(HACK_H) $(WINQT)qt_pre.h \ + $(WINQT)qt_post.h $(WINQT)qt_delay.h $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_delay.cpp +$(TARGETPFX)qt_glyph.obj: $(WINQT)qt_glyph.cpp $(HACK_H) \ + $(INCL)tile2x11.h $(WINQT)qt_pre.h $(WINQT)qt_post.h \ + $(WINQT)qt_glyph.h $(WINQT)qt_bind.h $(WINQT)qt_main.h \ + $(WINQT)qt_kde0.h $(WINQT)qt_set.h $(WINQT)qt_inv.h \ + $(WINQT)qt_map.h $(WINQT)qt_win.h $(WINQT)qt_clust.h \ + $(WINQT)qt_str.h $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_glyph.cpp +$(TARGETPFX)qt_icon.obj: $(WINQT)qt_icon.cpp $(HACK_H) $(WINQT)qt_pre.h \ + $(WINQT)qt_post.h $(WINQT)qt_icon.h $(WINQT)qt_str.h \ + $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_icon.cpp +$(TARGETPFX)qt_inv.obj: $(WINQT)qt_inv.cpp $(HACK_H) $(WINQT)qt_pre.h \ + $(WINQT)qt_post.h $(WINQT)qt_inv.h $(WINQT)qt_glyph.h \ + $(WINQT)qt_main.h $(WINQT)qt_kde0.h $(WINQT)qt_set.h \ + $(WINQT)qt_bind.h $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_inv.cpp +$(TARGETPFX)qt_key.obj: $(WINQT)qt_key.cpp $(HACK_H) $(WINQT)qt_pre.h \ + $(WINQT)qt_post.h $(WINQT)qt_key.h $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_key.cpp +$(TARGETPFX)qt_line.obj: $(WINQT)qt_line.cpp $(HACK_H) $(WINQT)qt_pre.h \ + $(WINQT)qt_post.h $(WINQT)qt_line.h $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_line.cpp +$(TARGETPFX)qt_main.obj: $(WINQT)qt_main.cpp $(HACK_H) $(WINQT)qt_pre.h \ + $(WINQT)qt_post.h $(WINQT)qt_main.h $(WINQT)qt_kde0.h \ + qt_main.moc $(WINQT)qt_bind.h $(WINQT)qt_glyph.h \ + $(WINQT)qt_inv.h $(WINQT)qt_key.h $(WINQT)qt_map.h \ + $(WINQT)qt_win.h $(WINQT)qt_clust.h $(WINQT)qt_msg.h \ + $(WINQT)qt_set.h $(WINQT)qt_stat.h $(WINQT)qt_icon.h \ + $(WINQT)qt_str.h qt_kde0.moc $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_main.cpp +$(TARGETPFX)qt_map.obj: $(WINQT)qt_map.cpp $(HACK_H) $(WINQT)qt_pre.h \ + $(WINQT)qt_post.h $(WINQT)qt_map.h $(WINQT)qt_win.h \ + $(WINQT)qt_clust.h qt_map.moc $(WINQT)qt_click.h \ + $(WINQT)qt_glyph.h $(WINQT)qt_set.h $(WINQT)qt_bind.h \ + $(WINQT)qt_main.h $(WINQT)qt_kde0.h $(WINQT)qt_str.h \ + $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_map.cpp +$(TARGETPFX)qt_menu.obj: $(WINQT)qt_menu.cpp $(HACK_H) $(WINQT)qt_pre.h \ + $(WINQT)qt_post.h $(WINQT)qt_menu.h $(WINQT)qt_win.h \ + $(WINQT)qt_rip.h qt_menu.moc $(WINQT)qt_key.h \ + $(WINQT)qt_glyph.h $(WINQT)qt_set.h $(WINQT)qt_bind.h \ + $(WINQT)qt_main.h $(WINQT)qt_kde0.h $(WINQT)qt_streq.h \ + $(WINQT)qt_line.h $(WINQT)qt_str.h $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_menu.cpp +$(TARGETPFX)qt_msg.obj: $(WINQT)qt_msg.cpp $(HACK_H) $(WINQT)qt_pre.h \ + $(WINQT)qt_post.h $(WINQT)qt_msg.h $(WINQT)qt_win.h \ + qt_msg.moc $(WINQT)qt_map.h $(WINQT)qt_clust.h \ + $(WINQT)qt_set.h $(WINQT)qt_bind.h $(WINQT)qt_main.h \ + $(WINQT)qt_kde0.h $(WINQT)qt_str.h $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_msg.cpp +$(TARGETPFX)qt_plsel.obj: $(WINQT)qt_plsel.cpp $(HACK_H) $(WINQT)qt_pre.h \ + $(WINQT)qt_post.h $(WINQT)qt_plsel.h qt_plsel.moc \ + $(WINQT)qt_bind.h $(WINQT)qt_main.h $(WINQT)qt_kde0.h \ + $(WINQT)qt_glyph.h $(WINQT)qt_set.h $(WINQT)qt_str.h \ + $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_plsel.cpp +$(TARGETPFX)qt_rip.obj: $(WINQT)qt_rip.cpp $(HACK_H) $(WINQT)qt_pre.h \ + $(WINQT)qt_post.h $(WINQT)qt_rip.h $(WINQT)qt_bind.h \ + $(WINQT)qt_main.h $(WINQT)qt_kde0.h $(WINQT)qt_str.h \ + $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_rip.cpp +$(TARGETPFX)qt_set.obj: $(WINQT)qt_set.cpp $(HACK_H) $(WINQT)qt_pre.h \ + $(WINQT)qt_post.h $(WINQT)qt_set.h $(WINQT)qt_bind.h \ + $(WINQT)qt_main.h $(WINQT)qt_kde0.h qt_set.moc \ + $(WINQT)qt_glyph.h $(WINQT)qt_xcmd.h $(WINQT)qt_str.h \ + $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_set.cpp +$(TARGETPFX)qt_stat.obj: $(WINQT)qt_stat.cpp $(HACK_H) $(WINQT)qt_pre.h \ + $(WINQT)qt_post.h $(WINQT)qt_stat.h $(WINQT)qt_win.h \ + $(WINQT)qt_icon.h qt_stat.moc $(WINQT)qt_set.h \ + $(WINQT)qt_bind.h $(WINQT)qt_main.h $(WINQT)qt_kde0.h \ + $(WINQT)qt_str.h $(WINQT)qt_xpms.h $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_stat.cpp +$(TARGETPFX)qt_str.obj: $(WINQT)qt_str.cpp $(WINQT)qt_str.h $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_str.cpp +$(TARGETPFX)qt_streq.obj: $(WINQT)qt_streq.cpp $(HACK_H) $(WINQT)qt_pre.h \ + $(WINQT)qt_post.h $(WINQT)qt_streq.h $(WINQT)qt_line.h \ + $(WINQT)qt_str.h $(WINQT)qt_set.h $(WINQT)qt_bind.h \ + $(WINQT)qt_main.h $(WINQT)qt_kde0.h $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_streq.cpp +$(TARGETPFX)qt_svsel.obj: $(WINQT)qt_svsel.cpp $(HACK_H) $(WINQT)qt_pre.h \ + $(WINQT)qt_post.h $(WINQT)qt_svsel.h $(WINQT)qt_bind.h \ + $(WINQT)qt_main.h $(WINQT)qt_kde0.h $(WINQT)qt_str.h \ + $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_svsel.cpp +$(TARGETPFX)qt_win.obj: $(WINQT)qt_win.cpp $(HACK_H) $(WINQT)qt_pre.h \ + $(WINQT)qt_post.h $(WINQT)qt_win.h $(WINQT)qt_bind.h \ + $(WINQT)qt_main.h $(WINQT)qt_kde0.h $(WINQT)qt_click.h \ + $(WINQT)qt_glyph.h $(WINQT)qt_inv.h $(WINQT)qt_key.h \ + $(WINQT)qt_icon.h $(WINQT)qt_map.h $(WINQT)qt_clust.h \ + $(WINQT)qt_menu.h $(WINQT)qt_rip.h $(WINQT)qt_msg.h \ + $(WINQT)qt_set.h $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_win.cpp +$(TARGETPFX)qt_xcmd.obj: $(WINQT)qt_xcmd.cpp $(HACK_H) $(INCL)func_tab.h \ + $(WINQT)qt_pre.h $(WINQT)qt_post.h $(WINQT)qt_xcmd.h \ + qt_xcmd.moc $(WINQT)qt_key.h $(WINQT)qt_bind.h \ + $(WINQT)qt_main.h $(WINQT)qt_kde0.h $(WINQT)qt_set.h \ + $(WINQT)qt_str.h $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_xcmd.cpp +$(TARGETPFX)qt_yndlg.obj: $(WINQT)qt_yndlg.cpp $(HACK_H) $(WINQT)qt_pre.h \ + $(WINQT)qt_post.h $(WINQT)qt_yndlg.h qt_yndlg.moc \ + $(WINQT)qt_key.h $(WINQT)qt_str.h $(QTn_H) + $(TARGET_CXX) $(TARGET_CXXFLAGS) /OBJECT=$@ $(WINQT)qt_yndlg.cpp +qt_kde0.moc: $(WINQT)qt_kde0.h $(QTn_H) + $(MOCPATH) /OBJECT=$@ $(WINQT)qt_kde0.h +qt_main.moc: $(WINQT)qt_main.h $(WINQT)qt_kde0.h $(QTn_H) + $(MOCPATH) /OBJECT=$@ $(WINQT)qt_main.h +qt_map.moc: $(WINQT)qt_map.h $(WINQT)qt_win.h $(WINQT)qt_clust.h $(QTn_H) + $(MOCPATH) /OBJECT=$@ $(WINQT)qt_map.h +qt_menu.moc: $(WINQT)qt_menu.h $(WINQT)qt_win.h $(WINQT)qt_rip.h $(QTn_H) + $(MOCPATH) /OBJECT=$@ $(WINQT)qt_menu.h +qt_msg.moc: $(WINQT)qt_msg.h $(WINQT)qt_win.h $(QTn_H) + $(MOCPATH) /OBJECT=$@ $(WINQT)qt_msg.h +qt_plsel.moc: $(WINQT)qt_plsel.h $(QTn_H) + $(MOCPATH) /OBJECT=$@ $(WINQT)qt_plsel.h +qt_set.moc: $(WINQT)qt_set.h $(WINQT)qt_bind.h $(WINQT)qt_main.h \ + $(WINQT)qt_kde0.h $(QTn_H) + $(MOCPATH) /OBJECT=$@ $(WINQT)qt_set.h +qt_stat.moc: $(WINQT)qt_stat.h $(WINQT)qt_win.h $(WINQT)qt_icon.h \ + $(QTn_H) + $(MOCPATH) /OBJECT=$@ $(WINQT)qt_stat.h +qt_xcmd.moc: $(WINQT)qt_xcmd.h $(QTn_H) + $(MOCPATH) /OBJECT=$@ $(WINQT)qt_xcmd.h +qt_yndlg.moc: $(WINQT)qt_yndlg.h $(QTn_H) + $(MOCPATH) /OBJECT=$@ $(WINQT)qt_yndlg.h +$(TARGETPFX)tile.obj: tile.c $(HACK_H) +$(TARGETPFX)wc_chainin.obj: $(WINCHAIN)wc_chainin.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(WINCHAIN)wc_chainin.c +$(TARGETPFX)wc_chainout.obj: $(WINCHAIN)wc_chainout.c $(HACK_H) + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(WINCHAIN)wc_chainout.c +$(TARGETPFX)wc_trace.obj: $(WINCHAIN)wc_trace.c $(HACK_H) $(INCL)wintty.h \ + $(INCL)func_tab.h + $(TARGET_CC) $(TARGET_CFLAGS) /OBJECT=$@ $(WINCHAIN)wc_trace.c +$(TARGETPFX)allmain.obj: allmain.c $(HACK_H) +$(TARGETPFX)alloc.obj: alloc.c $(CONFIG_H) +$(TARGETPFX)apply.obj: apply.c $(HACK_H) +$(TARGETPFX)artifact.obj: artifact.c $(HACK_H) $(INCL)artifact.h +$(TARGETPFX)attrib.obj: attrib.c $(HACK_H) +$(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)cmd.obj: cmd.c $(HACK_H) $(INCL)func_tab.h +$(TARGETPFX)coloratt.obj: coloratt.c $(HACK_H) +$(TARGETPFX)dbridge.obj: dbridge.c $(HACK_H) +$(TARGETPFX)decl.obj: decl.c $(HACK_H) +$(TARGETPFX)detect.obj: detect.c $(HACK_H) $(INCL)artifact.h +$(TARGETPFX)dig.obj: dig.c $(HACK_H) +$(TARGETPFX)display.obj: display.c $(HACK_H) +$(TARGETPFX)dlb.obj: dlb.c $(CONFIG_H) $(INCL)dlb.h +$(TARGETPFX)do.obj: do.c $(HACK_H) +$(TARGETPFX)do_name.obj: do_name.c $(HACK_H) +$(TARGETPFX)do_wear.obj: do_wear.c $(HACK_H) +$(TARGETPFX)dog.obj: dog.c $(HACK_H) +$(TARGETPFX)dogmove.obj: dogmove.c $(HACK_H) $(INCL)mfndpos.h +$(TARGETPFX)dokick.obj: dokick.c $(HACK_H) +$(TARGETPFX)dothrow.obj: dothrow.c $(HACK_H) +$(TARGETPFX)drawing.obj: drawing.c $(CONFIG_H) $(INCL)color.h \ + $(INCL)rm.h $(INCL)objclass.h $(INCL)defsym.h \ + $(INCL)objects.h $(INCL)wintype.h $(INCL)sym.h +$(TARGETPFX)dungeon.obj: dungeon.c $(HACK_H) $(INCL)dgn_file.h \ + $(INCL)dlb.h +$(TARGETPFX)eat.obj: eat.c $(HACK_H) +$(TARGETPFX)end.obj: end.c $(HACK_H) $(INCL)dlb.h +$(TARGETPFX)engrave.obj: engrave.c $(HACK_H) +$(TARGETPFX)exper.obj: exper.c $(HACK_H) +$(TARGETPFX)explode.obj: explode.c $(HACK_H) +$(TARGETPFX)extralev.obj: extralev.c $(HACK_H) +$(TARGETPFX)files.obj: files.c $(HACK_H) $(INCL)dlb.h $(INCL)wintty.h \ + #zlib.h +$(TARGETPFX)fountain.obj: fountain.c $(HACK_H) +$(TARGETPFX)getpos.obj: getpos.c $(HACK_H) +$(TARGETPFX)glyphs.obj: glyphs.c $(HACK_H) +$(TARGETPFX)hack.obj: hack.c $(HACK_H) +$(TARGETPFX)hacklib.obj: hacklib.c $(HACK_H) +$(TARGETPFX)insight.obj: insight.c $(HACK_H) +$(TARGETPFX)invent.obj: invent.c $(HACK_H) +$(TARGETPFX)isaac64.obj: isaac64.c $(CONFIG_H) $(INCL)isaac64.h +$(TARGETPFX)light.obj: light.c $(HACK_H) +$(TARGETPFX)lock.obj: lock.c $(HACK_H) +$(TARGETPFX)mail.obj: mail.c $(HACK_H) $(INCL)mail.h +$(TARGETPFX)makemon.obj: makemon.c $(HACK_H) +$(TARGETPFX)mcastu.obj: mcastu.c $(HACK_H) +$(TARGETPFX)mdlib.obj: mdlib.c $(CONFIG_H) $(INCL)permonst.h \ + $(INCL)align.h $(INCL)monattk.h $(INCL)monflag.h \ + $(INCL)monsters.h $(INCL)objclass.h \ + $(INCL)defsym.h $(INCL)objects.h $(INCL)wintype.h \ + $(INCL)sym.h $(INCL)artilist.h $(INCL)dungeon.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 +$(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) +$(TARGETPFX)mklev.obj: mklev.c $(HACK_H) +$(TARGETPFX)mkmap.obj: mkmap.c $(HACK_H) $(INCL)sp_lev.h +$(TARGETPFX)mkmaze.obj: mkmaze.c $(HACK_H) $(INCL)sp_lev.h +$(TARGETPFX)mkobj.obj: mkobj.c $(HACK_H) +$(TARGETPFX)mkroom.obj: mkroom.c $(HACK_H) +$(TARGETPFX)mon.obj: mon.c $(HACK_H) $(INCL)mfndpos.h +$(TARGETPFX)mondata.obj: mondata.c $(HACK_H) +$(TARGETPFX)monmove.obj: monmove.c $(HACK_H) $(INCL)mfndpos.h \ + $(INCL)artifact.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 +$(TARGETPFX)mplayer.obj: mplayer.c $(HACK_H) +$(TARGETPFX)mthrowu.obj: mthrowu.c $(HACK_H) +$(TARGETPFX)muse.obj: muse.c $(HACK_H) +$(TARGETPFX)music.obj: music.c $(HACK_H) +$(TARGETPFX)nhlua.obj: nhlua.c $(HACK_H) $(INCL)dlb.h +$(TARGETPFX)nhlsel.obj: nhlsel.c $(HACK_H) $(INCL)sp_lev.h +$(TARGETPFX)nhlobj.obj: nhlobj.c $(HACK_H) $(INCL)sp_lev.h +$(TARGETPFX)o_init.obj: o_init.c $(HACK_H) +$(TARGETPFX)objects.obj: objects.c $(CONFIG_H) $(INCL)obj.h \ + $(INCL)prop.h $(INCL)skills.h $(INCL)color.h \ + $(INCL)objclass.h $(INCL)defsym.h $(INCL)objects.h +$(TARGETPFX)objnam.obj: objnam.c $(HACK_H) +$(TARGETPFX)options.obj: options.c $(CONFIG_H) $(INCL)objclass.h \ + $(INCL)defsym.h $(INCL)objects.h $(INCL)flag.h \ + $(HACK_H) $(INCL)tcap.h $(INCL)optlist.h +$(TARGETPFX)pager.obj: pager.c $(HACK_H) $(INCL)dlb.h +$(TARGETPFX)pickup.obj: pickup.c $(HACK_H) +$(TARGETPFX)pline.obj: pline.c $(HACK_H) +$(TARGETPFX)polyself.obj: polyself.c $(HACK_H) +$(TARGETPFX)potion.obj: potion.c $(HACK_H) +$(TARGETPFX)pray.obj: pray.c $(HACK_H) +$(TARGETPFX)priest.obj: priest.c $(HACK_H) $(INCL)mfndpos.h +$(TARGETPFX)quest.obj: quest.c $(HACK_H) +$(TARGETPFX)questpgr.obj: questpgr.c $(HACK_H) $(INCL)dlb.h \ + $(INCL)wintty.h +$(TARGETPFX)read.obj: read.c $(HACK_H) +$(TARGETPFX)rect.obj: rect.c $(HACK_H) +$(TARGETPFX)region.obj: region.c $(HACK_H) +$(TARGETPFX)report.obj: report.c $(HACK_H) +$(TARGETPFX)restore.obj: restore.c $(HACK_H) $(INCL)tcap.h +$(TARGETPFX)rip.obj: rip.c $(HACK_H) +$(TARGETPFX)rnd.obj: rnd.c $(HACK_H) $(INCL)isaac64.h +$(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)shk.obj: shk.c $(HACK_H) +$(TARGETPFX)shknam.obj: shknam.c $(HACK_H) +$(TARGETPFX)sit.obj: sit.c $(HACK_H) $(INCL)artifact.h +$(TARGETPFX)sounds.obj: sounds.c $(HACK_H) +$(TARGETPFX)sp_lev.obj: sp_lev.c $(HACK_H) $(INCL)sp_lev.h +$(TARGETPFX)spell.obj: spell.c $(HACK_H) +$(TARGETPFX)stairs.obj: stairs.c $(HACK_H) +$(TARGETPFX)steal.obj: steal.c $(HACK_H) +$(TARGETPFX)steed.obj: steed.c $(HACK_H) +$(TARGETPFX)symbols.obj: symbols.c $(HACK_H) $(INCL)tcap.h +$(TARGETPFX)sys.obj: sys.c $(HACK_H) +$(TARGETPFX)teleport.obj: teleport.c $(HACK_H) +$(TARGETPFX)timeout.obj: timeout.c $(HACK_H) +$(TARGETPFX)topten.obj: topten.c $(HACK_H) $(INCL)dlb.h +$(TARGETPFX)track.obj: track.c $(HACK_H) +$(TARGETPFX)trap.obj: trap.c $(HACK_H) +$(TARGETPFX)u_init.obj: u_init.c $(HACK_H) +$(TARGETPFX)utf8map.obj: utf8map.c $(HACK_H) +$(TARGETPFX)uhitm.obj: uhitm.c $(HACK_H) +$(TARGETPFX)vault.obj: vault.c $(HACK_H) +$(TARGETPFX)version.obj: version.c $(HACK_H) $(INCL)dlb.h +$(TARGETPFX)vision.obj: vision.c $(HACK_H) +$(TARGETPFX)weapon.obj: weapon.c $(HACK_H) +$(TARGETPFX)were.obj: were.c $(HACK_H) +$(TARGETPFX)wield.obj: wield.c $(HACK_H) +$(TARGETPFX)windows.obj: windows.c $(HACK_H) $(INCL)dlb.h $(INCL)wintty.h +$(TARGETPFX)wizard.obj: wizard.c $(HACK_H) +$(TARGETPFX)wizcmds.obj: wizcmds.c $(HACK_H) +$(TARGETPFX)worm.obj: worm.c $(HACK_H) +$(TARGETPFX)worn.obj: worn.c $(HACK_H) +$(TARGETPFX)write.obj: write.c $(HACK_H) +$(TARGETPFX)zap.obj: zap.c $(HACK_H) +# DEPENDENCIES MUST END AT END OF FILE +# IF YOU PUT STUFF HERE IT WILL GO AWAY diff --git a/sys/vms/Makefile_top.vms b/sys/vms/Makefile_top.vms new file mode 100644 index 0000000000..505b797b07 --- /dev/null +++ b/sys/vms/Makefile_top.vms @@ -0,0 +1,438 @@ +# NetHack Top-level Makefile. +# NetHack 3.7 Makefile.vms $NHDT-Date: 1642630921 2022/01/19 22:22:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.72 $ +# Copyright (c) 2015 by Kenneth Lorber, Kensington, Maryland +# NetHack may be freely redistributed. See license for details. + +# Root of source tree: (Note: dot is after setup.sh has been used +# to create the active Makefiles from sys/unix/Makefile.*. +# It isn't sys/unix/ where you might happen to be reading this.) +NHSROOT=[] + +# Newer makes predefine $(MAKE) to 'make' and do smarter processing +# of recursive make calls if $(MAKE) is used. +# These makes allow $(MAKE) to be overridden by the environment if +# someone wants to (or has to) use something other than the standard +# make, so we do not want to unconditionally set $(MAKE) here. +# +# Unfortunately, some older makes do not predefine $(MAKE); if you +# have one of these, uncomment the following line. +# (You will know that you have one if you get complaints about unable +# to execute things like 'data' and 'rumors'.) +# MAKE = make + + +# NetHack 3.7 Makefile.vms $NHDT-Date: 1654287121 2022/06/03 20:12:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.145 $ +# Copyright (c) 2023 by Michael Allison +# NetHack may be freely redistributed. See license for details. +# +# OpenVMS GNU Makefile for OpenVMS 9.x and VSI C +# with GNU Make V4.1 (or greater). +# +# Tested on x86_64 version of OpenVMS V9.2-1. +# + +# Root of source tree: +NHSROOT= + +# relative directories from top +DOC=[.doc] +DAT=[.dat] +INCL=[.include] +SRC=[.src] +UTIL=[.util] +LIB=[.lib] +SYSVMS=[.sys.vms] +SYSSHR=[.sys.share] +SYSUNIX=[.sys.unix] +WINTTY=[.win.tty] +WINSHR=[.win.share] +WINTTY=[.win.tty] +WINCURS=[.win.curses] +WINX11=[.win.X11] +WINQT=[.win.Qt] +WINSHIM=[.win.shim] +WINCHAIN=[.win.chain] + +# Lua macros +LUAVER=546 +LUADOTVER=5.4.6 +LUAUNDERVER=5_4_6 +LUAINC=[.lib.lua.lua$(LUAVER).src] +LUALIB=[.lib.lua]lua$(LUAVER).olb +LUASRCDIR =[.lib.lua.lua$(LUAVER).src] +LUASRCINCDIR=$(LUASRCDIR) +LUATESTTARGET = $(LUAINC)lua.h +LUABASELIB = liblua-$(LUADOTVER).olb + +# make NetHack (as opposite to variants or such) +#PREFIX = /usr +GAME = nethack +# GAME = nethack.exe +# GAME = nethack.prg +#GAMEUID = games +#GAMEGRP = bin + +# Permissions - some places use setgid instead of setuid, for instance. +# See also the option "SECURE" in include/config.h. +#GAMEPERM = 04755 +FILEPERM = 0644 +# VARFILEPERM = 0644 +EXEPERM = 0755 +DIRPERM = 0755 +# VARDIRPERM = 0755 + +# VARDIR may also appear in unixconf.h as "VAR_PLAYGROUND" else HACKDIR +# +# Note well! 'make install' believes in creating a nice tidy HACKDIR +# for installation, free of debris from previous NetHack versions -- +# therefore there should not be anything in HACKDIR that you want to +# keep (if there is, you'll have to do the installation by hand or +# modify the install commands below). +#HACKDIR = $(PREFIX)/games/lib/$(GAME)dir +#VARDIR = $(HACKDIR) +# Where nethack.sh is installed (as 'nethack'). +# If this is not defined, the shell wrapper script is not used. +#SHELLDIR = $(PREFIX)/games + +# Extra data files depending upon the interface(s) built into nethack. +# per discussion in Install.X11 and Install.Qt +# Qt prefers nhtiles.bmp but can use x11tiles, and it ignores NetHack.ad. +# X11 uses those last two. +# Both can display a conventional text map instead of tiles. +# tty and/or curses (no extra data files outside of the dlb container): +#VARDATND = +# All X11 and/or Qt variations may also include tty or curses or both): +# X11 without Qt: +# VARDATND = x11tiles NetHack.ad pet_mark.xbm pilemark.xbm +# X11 with GRAPHIC_TOMBSTONE (requires 'xpm'): +# VARDATND = x11tiles NetHack.ad pet_mark.xbm pilemark.xbm rip.xpm +# both X11 and Qt: +# VARDATND = x11tiles nhtiles.bmp NetHack.ad pet_mark.xbm pilemark.xbm rip.xpm +# Qt without X11; assumes GRAPHIC_TOMBSTONE: +# VARDATND = nhtiles.bmp pet_mark.xbm pilemark.xbm rip.xpm + +VARDATD = bogusmon data engrave epitaph oracles options quest.lua rumors +VARDAT = $(VARDATD) $(VARDATND) + +# Some versions of make use the SHELL environment variable as the +# shell for running commands. We need this to be a Bourne shell. +# SHELL = /bin/sh +# for Atari (obsolete) +# SHELL=E:/GEMINI2/MUPFEL.TTP + +# Commands for setting the owner and group on files during installation. +# Some systems fail with one or the other when installing over NFS or for +# other permission-related reasons. If that happens, you may want to set the +# command to "true", which is a no-op. Note that disabling chown or chgrp +# will only work if setuid (or setgid) behavior is not desired or required. +#CHOWN = chown +#CHGRP = chgrp + +# +# end of configuration +# + +DATHELP = help hh cmdhelp keyhelp history opthelp optmenu usagehlp wizhelp + +SPEC_LEVS = 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 +QUEST_LEVS = ???-goal.lua ???-fil?.lua ???-loca.lua ???-strt.lua + +DATNODLB = $(VARDATND) license symbols +DATDLB = $(DATHELP) dungeon.lua tribute $(SPEC_LEVS) $(QUEST_LEVS) $(VARDATD) +DODAT = $(DATNODLB) $(DATDLB) + +ALLDEP = $(GAME) recover Guidebook $(VARDAT) spec_levs check-dlb + +# first target is also the default target for 'make' without any arguments +all: $(ALLDEP) + pipe true + @echo "Done." + +$(GAME): lua_support + pipe set default $(SRC) && $(MAKE) LUA_VERSION=$(LUADOTVER) $(GAME) \ + && set def [-] + +lua_support: [.include]nhlua.h + @true + +# Note: many of the dependencies below are here to allow parallel make +# to generate valid output + +Guidebook: + pipe set def $(DOC) && $(MAKE) Guidebook && set def [-] + +Guidebook.txt: + pipe set def $(DOC) && $(MAKE) Guidebook.txt && set def [-] + +Guidebook.pdf: + pipe set def $(DOC) && $(MAKE) Guidebook.pdf && set def [-] + +manpages: + pipe set def $(DOC) && $(MAKE) manpages && set def [-] + +data: $(GAME) + pipe set def $(DAT) && $(MAKE) data && set def [-] + +engrave: $(GAME) + pipe set def $(DAT) && $(MAKE) engrave && set def [-] + +bogusmon: $(GAME) + pipe set def $(DAT) && $(MAKE) bogusmon && set def [-] + +epitaph: $(GAME) + pipe set def $(DAT) && $(MAKE) epitaph && set def [-] + +rumors: $(GAME) + pipe set def $(DAT) && $(MAKE) rumors && set def [-] + +oracles: $(GAME) + pipe set def $(DAT) && $(MAKE) oracles && set def [-] + +# Note: options should have already been made with make, but... +options: $(GAME) + pipe set def $(DAT) && $(MAKE) options && set def [-] + +quest.lua: $(GAME) + +spec_levs: + pipe set def $(DAT) && $(MAKE) spec_levs && set def [-] + pipe set def $(DAT) && $(MAKE) quest_levs && set def [-] + +nhtiles.bmp: $(GAME) + pipe set def $(UTIL) && $(MAKE) tile2bmp && set def [-] + pipe set def $(DAT) && $(MAKE) nhtiles.bmp && set def [-] + +x11tiles: $(GAME) + pipe set def $(UTIL) && $(MAKE) tile2x11 && set def [-] + pipe set def $(DAT) && $(MAKE) x11tiles && set def [-] + +NetHack.ad: $(GAME) + pipe set def $(DAT) && $(MAKE) NetHack.ad && set def [-] + +pet_mark.xbm: + pipe set def $(DAT) && $(MAKE) pet_mark.xbm && set def [-] + +pilemark.xbm: + pipe set def $(DAT) && $(MAKE) pilemark.xbm && set def [-] + +rip.xpm: + pipe set def $(DAT) && $(MAKE) rip.xpm && set def [-] + +mapbg.xpm: + pipe set def $(DAT) && $(MAKE) mapbg.xpm && set def [-] + +nhsplash.xpm: + pipe set def $(DAT) && $(MAKE) nhsplash.xpm && set def [-] + +nh16.img: $(GAME) + pipe set def $(UTIL) && $(MAKE) tile2img.ttp && set def [-] + pipe set def $(DAT) && $(MAKE) nh16.img && set def [-] + +rip.img: + pip set def $(UTIL) && $(MAKE) xpm2img.ttp && set def [-] + pipe set def $(DAT) && $(MAKE) rip.img && set def [-] +GEM_RSC.RSC: + pipe set def $(DAT) && $(MAKE) GEM_RSC.RSC && set def [-] + +title.img: + pipe set def $(DAT) && $(MAKE) title.img && set def [-] + +check-dlb: options + echo disabled $@ +# @if egrep -s librarian dat/options ; then $(MAKE) dlb ; else true ; fi + +dlb: + pipe set def $(UTIL) && $(MAKE) dlb && set def [-] + pipe set def $(DAT) && LC_ALL=C && mcr [-.util]dlb cf nhdat $(DATDLB) \ + && set def [-] + +wasm: + pipe set def $(SRC) && \ + $(MAKE) CROSS_TO_WASM=1 [-.targets.wasm]nethack.js && \ + set def [-] + +package: $(GAME) recover $(VARDAT) spec_levs + pipe set def $(SRC) && $(MAKE) $(PACKAGE) && set def [-] + +# recover can be used when INSURANCE is defined in include/config.h +# and the checkpoint option is true +recover: $(GAME) + pipe set def $(UTIL) && $(MAKE) recover && set def [-] + +dofiles: + echo disabled $@ +# target=`sed -n \ +# -e '/librarian/{' \ +# -e 's/.*/dlb/p' \ +# -e 'q' \ +# -e '}' \ +# -e '$$s/.*/nodlb/p' < dat/options` ; \ +# $(MAKE) dofiles-$${target-nodlb} +# cp src/$(GAME) $(INSTDIR) +# cp util/recover $(INSTDIR) +# -if test -n '$(SHELLDIR)'; then rm -f $(SHELLDIR)/$(GAME); fi +# if test -n '$(SHELLDIR)'; then \ +# sed -e 's;/usr/games/lib/nethackdir;$(HACKDIR);' \ +# -e 's;HACKDIR/nethack;HACKDIR/$(GAME);' \ +# < sys/unix/nethack.sh \ +# > $(SHELLDIR)/$(GAME) ; fi +# set up their permissions +# -( cd $(INSTDIR) ; $(CHOWN) $(GAMEUID) $(GAME) recover ; \ +# $(CHGRP) $(GAMEGRP) $(GAME) recover ) +# chmod $(GAMEPERM) $(INSTDIR)/$(GAME) +# chmod $(EXEPERM) $(INSTDIR)/recover +# -if test -n '$(SHELLDIR)'; then \ +# $(CHOWN) $(GAMEUID) $(SHELLDIR)/$(GAME); fi +# if test -n '$(SHELLDIR)'; then \ +# $(CHGRP) $(GAMEGRP) $(SHELLDIR)/$(GAME); \ +# chmod $(EXEPERM) $(SHELLDIR)/$(GAME); fi + +dofiles-dlb: check-dlb + pipe set def $(DAT) && copy nhdat $(DATNODLB) $(INSTDIR) && \ + set def [-] +# set up their permissions +# -( cd $(INSTDIR) ; $(CHOWN) $(GAMEUID) nhdat $(DATNODLB) ; \ +# $(CHGRP) $(GAMEGRP) nhdat $(DATNODLB) ; \ +# chmod $(FILEPERM) nhdat $(DATNODLB) ) + +dofiles-nodlb: +# copy over the game files + pipe set def $(DAT) && copy $(DAT) $(INSTDIR) && \ + set def [-] +# set up their permissions +# -( cd $(INSTDIR) ; $(CHOWN) $(GAMEUID) $(DAT) ; \ +# $(CHGRP) $(GAMEGRP) $(DAT) ; \ +# chmod $(FILEPERM) $(DAT) ) +# +# This is not part of the dependency build hierarchy. +# It requires an explicit "make fetch-Lua". + +$(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 \#"include ""lualib.h""" >> $@ + @echo \#"include ""lauxlib.h""" >> $@ + @echo "/*nhlua.h*/" >> $@ + +[.lib]lua.dir: + create/directory [.lib.lua] + +[]lib.dir: + create/directory [.lib] + +[.lib.lua]lua$(LUAVER).dir: + @echo You need to make fetch-lua + false + +fetch-lua: []lib.dir [.lib]lua.dir + if f$$search("[.lib.lua]lua$(LUAVER).tgz").eqs."" then \ + curl --insecure https://www.lua.org/ftp/lua-$(LUADOTVER).tar.gz \ + --output [.lib.lua]lua$(LUAVER).tgz + if f$$search("[.lib.lua]lua$(LUAVER).tgz").nes."" then \ + gzip --force -d [.lib.lua]lua$(LUAVER).tgz + if f$$search("[.lib.lua]lua$(LUAVER).tar").nes."" then \ + pipe set def [.lib.lua] && \ + tar -xf lua$(LUAVER).tar && \ + set def [--] + if (f$$search("[.lib.lua]lua-$(LUAUNDERVER).DIR;1").nes."" \ + .AND. f$$search("[.lib.lua]lua$(LUAVER).dir;1").eqs."") then \ + rename [.lib.lua]lua-$(LUAUNDERVER).DIR;1 \ + [.lib.lua]lua$(LUAVER).dir;1 +# remove [.include]nhlua.h in case it was created for some other Lua version + @pipe if f$$search("[.include]nhlua.h").nes."" then \ + delete [.include]nhlua.h;* && echo 'delete [.include]nhlua.h;*' + +# '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. +update: $(GAME) recover $(VARDAT) spec_levs + echo disabled $@ +# (don't yank the old version out from under people who're playing it) +# -mv $(INSTDIR)/$(GAME) $(INSTDIR)/$(GAME).old +# -mv $(INSTDIR)/nhdat $(INSTDIR)/nhdat.old +# set up new versions of the game files +# ( $(MAKE) dofiles ) +# should already be present, but make sure +# touch $(VARDIR)/perm $(VARDIR)/record +# and a reminder +# @echo You may also want to install the man pages via the doc Makefile. + +rootcheck: + @true; $(ROOTCHECK) + +install: rootcheck $(GAME) recover $(VARDAT) spec_levs + true; $(PREINSTALL) + echo disabled $@ +# set up the directories +# not all mkdirs have -p; those that don't will create a -p directory +# -if test -n '$(SHELLDIR)'; then \ +# mkdir -p $(SHELLDIR); fi +# rm -rf $(INSTDIR) $(VARDIR) +# -mkdir -p $(INSTDIR) $(VARDIR) $(VARDIR)/save +# if test -d ./-p; then rmdir ./-p; fi +# -$(CHOWN) $(GAMEUID) $(INSTDIR) $(VARDIR) $(VARDIR)/save +# $(CHGRP) $(GAMEGRP) $(INSTDIR) $(VARDIR) $(VARDIR)/save +# order counts here: +# chmod $(DIRPERM) $(INSTDIR) +# chmod $(VARDIRPERM) $(VARDIR) $(VARDIR)/save +# set up the game files +# ( $(MAKE) dofiles ) +# set up some additional files +# touch $(VARDIR)/perm $(VARDIR)/record $(VARDIR)/logfile $(VARDIR)/xlogfile \ +# $(VARDIR)/livelog +# -( cd $(VARDIR) ; $(CHOWN) $(GAMEUID) perm record logfile xlogfile livelog ; \ +# $(CHGRP) $(GAMEGRP) perm record logfile xlogfile livelog ; \ +# chmod $(VARFILEPERM) perm record logfile xlogfile livelog ) +# true; $(POSTINSTALL) +# and a reminder +# @echo You may also want to reinstall the man pages via the doc Makefile. + + +# 'make clean' removes all the .o files, but leaves around all the executables +# and compiled data files +#clean: clean-lib clean-keep-lib +# @true + +clean-lib: +# -pipe set def $(LUASRCDIR) && $(MAKE) clean && set def [--.util] + +clean-keep-lib: +# ( cd src ; $(MAKE) clean ) +# ( cd util ; $(MAKE) clean ) +# pipe set def $(DAT) $(MAKE) clean && set def [-.util] +# ( cd doc ; $(MAKE) clean ) + +spotless-lib:: clean-lib + -( cd lib/lua && rm $(LUABASELIB) ) + +spotless-keep-lib: clean-keep-lib +# ( cd src ; $(MAKE) spotless ) +# ( cd util ; $(MAKE) spotless ) +# pipe set def $(DAT) && $(MAKE) spotless && set def [-] +# ( cd doc ; $(MAKE) spotless ) +spotless-clean-submodule: + @( if test -f submodules/lua/lua.h ; then \ + git submodule deinit submodules/lua ; fi ) + +clean: + pipe set def $(DAT) && $(MAKE) clean && set def [-] + pipe set def $(SRC) && $(MAKE) clean && set def [-] + pipe set def $(UTIL) && $(MAKE) clean && set def [-] + pipe set def $(DOC) && $(MAKE) clean && set def [-] + +# -delete/log $(CLEANFILES) + +spotless: clean + pipe set def $(DAT) && $(MAKE) spotless && set def [-] + pipe set def $(SRC) && $(MAKE) spotless && set def [-] + pipe set def $(UTIL) && $(MAKE) spotless && set def [-] + pipe set def $(DOC) && $(MAKE) spotless && set def [-] + -delete/log $(SPOTLESSFILES) $(INCL)nhlua.h;* + +# pipe set def $(DAT) && $(MAKE) spotless && set def [-] +# -$(RM) $(GAMEBIN);* diff --git a/sys/vms/Makefile_utl.vms b/sys/vms/Makefile_utl.vms new file mode 100644 index 0000000000..4c845194a2 --- /dev/null +++ b/sys/vms/Makefile_utl.vms @@ -0,0 +1,326 @@ +# Makefile for NetHack's utility programs. +# NetHack 3.7 util Makefile.vms $NHDT-Date: 1602258295 2020/10/09 15:44:55 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.53 $ +# Copyright (c) 2018 by Robert Patrick Rankin +# NetHack may be freely redistributed. See license for details. + +# Root of source tree relative to here: +NHSROOT=[-] + +# NetHack 3.7 began introducing C99 code. +# +# If your compiler needs an appropriate switch to accept C99 code. +# CSTD = -std=c99 + +# directories relative to here (util) +DOC=[-.doc] +DAT=[-.dat] +INCL=[-.include] +SRC=[-.src] +UTIL=[-.util] +SYSVMS=[-.sys.vms] +SYSSHR=[-.sys.share] +SYSUNIX=[-.sys.unix] +WINTTY=[-.win.tty] +WINSHR=[-.win.share] +WINTTY=[-.win.tty] +WINCURS=[-.win.curses] +WINX11=[-.win.X11] +WINQT=[-.win.Qt] +WINSHIM=[-.win.shim] +WINCHAIN=[-.win.chain] + +# Lua location relative to here +LUAVER=546 +LUADOTVER=5.4.6 +LUAUNDERVER=5_4_6 +LUAINC=[-.lib.lua.lua$(LUAVER).src] +LUALIB=[-.lib.lua]lua$(LUAVER).olb +LUASRCDIR =[-.lib.lua.lua$(LUAVER).src] +LUASRCINCDIR=$(LUASRCDIR) +#LUASRCINCDIR=SYS$COMMON:[LUA.INCLUDE] + +# hacklib +HACKLIB=hacklib.olb +HACKLIBOBJS=[-.src]hacklib.obj,[-.util]panic.obj + +comma:=, +LIBS = +OBJDIR = [-.src] + +# Compiler flags +CFLAGS :=/INCLUDE=($(INCL),$(LUAINC)) /NAMES=(AS_IS) $(CSTD) +CXXFLAGS = /INCLUDE_DIR=($(INCL),$(LUAINC)) /NAMES=(AS_IS) + +# Link flags +LFLAGS = + +# external programs +# MAKE = make +CC = CC +#touch :=SET FILE/TRUNCATE +touch = append/New _NLA0: +LINK = link +RM = delete +#TRUE uses an actual helper true.exe +TRUE = true +#FALSE uses an actual helper false.exe +FALSE = false +#ECHO uses an actual helper echo.exe +ECHO = echo +CXX ?= CXX +MOC ?= moc +MOCPATH ?= $(QTDIR)/bin/$(MOC) +# The default is for the TARGET_* variables to match the defaults. +# If we're cross-compiling these will get overridden elsewhere, likely via +# a hints file. TARGETPFX was set above earlier. +TARGET_CC = $(CC) +TARGET_CFLAGS = $(CFLAGS) $(CSTD) +TARGET_LINK = $(LINK) +TARGET_LFLAGS = $(LFLAGS) +TARGET_CXX = $(CXX) +TARGET_CXXFLAGS = $(CXXFLAGS) +TARGET_LIBS = $(LIBS) +# [LINK might be defined to use $(CXX); we don't want that here.] +CLINK=$(TARGET_LINK) +CXXLINK=$(TARGET_LINK) + +# timestamps for primary header files, matching src/Makefile +#CONFIG_H = $(SRC)config.h-t +#HACK_H = $(SRC)hack.h-t + +# utility .c files +MAKESRC = makedefs.c $(SRC)mdlib.c +RECOVSRC = recover.c +DLBSRC = dlb_main.c +UTILSRCS = $(MAKESRC) panic.c $(RECOVSRC) $(DLBSRC) + +# files that define all monsters and objects +CMONOBJ = $(SRC)monst.c $(SRC)objects.c +OMONOBJ = $(OBJDIR)monst.obj $(OBJDIR)objects.obj +# files that provide access to NetHack's names +CNAMING = $(SRC)drawing.c $(CMONOBJ) +ONAMING = $(OBJDIR)drawing.obj $(OMONOBJ) +# dynamic memory allocation +CALLOC = $(SRC)alloc.c +OALLOC = $(OBJDIR)alloc.obj +CPANIC = panic.c +OPANIC = []panic.obj +# build time info +CDATE = $(SRC)date.c +ODATE = $(OBJDIR)date.obj + +# object files for makedefs +MAKEOBJS = []makedefs.obj $(OMONOBJ) $(ODATE) $(OALLOC) $(OPANIC) + +# object files for recovery utility +RECOVOBJS = $(TARGETPFX)recover.obj + +# object files for the data librarian +DLBOBJS = dlb_main.obj $(OBJDIR)dlb.obj $(OALLOC) $(OPANIC) + +# Distinguish between the build tools for the native host +# and the build tools for the target environment in commands. +# This allows the same set of Makefiles to be used for native +# builds and for cross-compiles by overriding these in hints +# files or on the command line. + +TARGETPFX= +TARGET_CC = $(CC) +TARGET_CFLAGS = $(CFLAGS) $(CSTD) +TARGET_CLINK = $(CLINK) +TARGET_LFLAGS = $(LFLAGS) +TARGET_CXX = $(CXX) +TARGET_CXXFLAGS = $(CXXFLAGS) + +# hacklib +# +$(HACKLIB): $(HACKLIBOBJS) [-.util]panic.obj + LIBRARY/CREATE $(HACKLIB)/object + LIBRARY/INSERT $(HACKLIB) $(HACKLIBOBJS) +# dependencies for makedefs +# + +makedefs.exe: $(HACKLIB) $(MAKEOBJS) placeholder.obj mdgrep.h + $(CLINK) $(LFLAGS) /EXE=$@ \ + $(addsuffix $(comma),$(MAKEOBJS)) placeholder.obj, \ + $(HACKLIB)/lib + +# note: the headers listed here are maintained manually rather than via +# 'make depend'; only the ones which are directly included by +# makedefs.c are listed, without various nested ones that they include; +# for makedefs (but not for nethack's core), mdlib.c gets included +# rather than be compiled separately +makedefs.obj: makedefs.c $(SRC)mdlib.c $(CONFIG_H) \ + $(INCL)permonst.h $(INCL)objclass.h \ + $(INCL)artilist.h $(INCL)dungeon.h $(INCL)obj.h \ + $(INCL)monst.h $(INCL)monsters.h $(INCL)objects.h \ + $(INCL)you.h $(INCL)context.h $(INCL)flag.h \ + $(INCL)dlb.h $(INCL)patchlevel.h mdgrep.h + $(CC) $(CFLAGS) $(CSTD) makedefs.c /OBJ=$@ + +placeholder.c: + echo int makedefs_placeholder = 1; >$@ + +placeholder.obj: placeholder.c + + +# Don't require perl to build; that is why mdgrep.h is spelled wrong below. +mdgreph: mdgrep.pl + perl mdgrep.pl + +# These are for reference purposes only. They aren't required in the build. +$(INCL)onames.h: makedefs $(INCL)objects.h + mcr []makedefs -o +$(INCL)pm.h: makedefs $(INCL)monsters.h + mcr []makedefs -p + +# we defer this makedefs call to the src Makefile, since it knows all about +# the main src and include files date.h is a timestamp for +$(INCL)date.h:: + @pipe set def $(SRC) && $(MAKE) $(INCL)date.h && set def [-] + +# support code used by several of the utility programs (but not makedefs) +panic.obj: panic.c $(CONFIG_H) + $(CC) $(CFLAGS) $(CSTD) panic.c /OBJ=$@ + + +# with all of extern.h's functions to complain about, we drown in +# 'defined but not used' without -u +#lintdgn: +# @lint -axhu -I.include -DLINT $(UTILSRCS) $(CALLOC) \ +# | sed '/_flsbuf/d' +# +# +# dependencies for recover +# +$(TARGETPFX)recover.exe: $(HACKLIB) $(RECOVOBJS) + $(TARGET_CLINK) $(TARGET_LFLAGS) /EXE=$@ \ + $(RECOVOBJS),$(HACKLIB)/lib $(LIBS) + +$(TARGETPFX)recover.obj: recover.c $(CONFIG_H) + $(TARGET_CC) $(TARGET_CFLAGS) $(CSTD) recover.c /OBJ=$@ + + +# dependencies for dlb +# +dlb: $(DLBOBJS) + $(CLINK) $(LFLAGS) /EXE=dlb $(DLBOBJS) $(LIBS) + +dlb_main.obj: dlb_main.c $(CONFIG_H) $(INCL)dlb.h + $(CC) $(CFLAGS) $(CSTD) dlb_main.c /OBJ=$@ + + +# dependencies for tile utilities +# +TEXT_IO = tiletext.obj tiletxt.obj $(OALLOC) $(ONAMING) +GIFREADERS = gifread.obj +PPMWRITERS = ppmwrite.obj + +tileutils: tilemap gif2txt txt2ppm tile2x11 + +gif2txt.exe: $(GIFREADERS) $(TEXT_IO) + $(CLINK) $(LFLAGS) /EXE=$@ $(GIFREADERS) $(TEXT_IO) $(LIBS) +txt2ppm.exe: $(PPMWRITERS) $(TEXT_IO) + $(CLINK) $(LFLAGS) /EXE=$@ $(PPMWRITERS) $(TEXT_IO) $(LIBS) + +tile2x11.exe: tile2x11.obj $(TEXT_IO) + $(CLINK) $(LFLAGS) /EXE=$@ tile2x11.obj $(TEXT_IO) $(LIBS) + +tile2bmp.exe: tile2bmp.obj $(TEXT_IO) + $(CLINK) $(LFLAGS) /EXE=$@ tile2bmp.obj $(TEXT_IO) + +tile2beos.exe: tile2beos.obj $(TEXT_IO) + $(CXXLINK) $(LFLAGS) /EXE=$@ tile2beos.obj $(TEXT_IO) + +#--compiling and linking in one step leaves extra debugging files (in their +# own subdirectories!) on OSX; compile and link separately to suppress +# that without mucking about with extra OS-specific CFLAGS and/or LFLAGS +#tilemap: .win/share/tilemap.c $(HACK_H) +# $(CC) $(CFLAGS) $(LFLAGS) -o tilemap .win/share/tilemap.c $(LIBS) +tilemap.exe: tilemap.obj $(OBJDIR)objects.obj $(OBJDIR)monst.obj \ + $(OBJDIR)drawing.obj + $(CLINK) $(LFLAGS) /EXE=$@ tilemap.obj $(OBJDIR)objects.obj \ + $(OBJDIR)monst.obj $(OBJDIR)drawing.obj $(LIBS) +$(SRC)tile.c: tilemap + mcr []tilemap + +tiletext.obj: $(WINSHR)tiletext.c $(CONFIG_H) $(WINSHR)tile.h + $(CC) $(CFLAGS) $(CSTD) /INCLUDE=$(WINSHR) $(WINSHR)tiletext.c /OBJ=$@ +tiletxt.obj: $(WINSHR)tiletxt.c $(WINSHR)tilemap.c $(HACK_H) + $(CC) $(CFLAGS) $(CSTD) /INCLUDE=$(WINSHR) $(WINSHR)tiletxt.c /OBJ=$@ +tilemap.obj: $(WINSHR)tilemap.c $(HACK_H) + $(CC) $(CFLAGS) $(CSTD) $(WINSHR)tilemap.c /OBJ=$@ + +gifread.obj: $(WINSHR)gifread.c $(CONFIG_H) $(WINSHR)tile.h + $(CC) $(CFLAGS) $(CSTD) /INCLUDE=$(WINSHR) $(WINSHR)gifread.c /OBJ=$@ +ppmwrite.obj: $(WINSHR)ppmwrite.c $(CONFIG_H) $(WINSHR)tile.h + $(CC) $(CFLAGS) $(CSTD) /INCLUDE=$(WINSHR) $(WINSHR)ppmwrite.c /OBJ=$@ + +tile2bmp.obj: $(WINSHR)tile2bmp.c $(HACK_H) $(WINSHR)tile.h + $(CC) $(CFLAGS) $(CSTD) /INCLUDE=$(WINSHR) $(WINSHR)tile2bmp.c /OBJ=$@ + +tile2x11.obj: .win/X11/tile2x11.c $(HACK_H) .win/share/tile.h \ + $(INCL)tile2x11.h + $(CC) $(CFLAGS) $(CSTD) /INCLUDE=$(WINSHR) $(WINSHR)tile2x11.c /OBJ=$@ + +tile2img.obj: [-.win.gem]tile2img.c $(HACK_H) $(WINSHR)tile.h \ + $(INCL)bitmfile.h + $(CC) $(CFLAGS) $(CSTD) /INCLUDE=$(WINSHR) [-.win.gem]tile2img.c /OBJ=$@ +xpm2img.obj: [-.win.gem]xpm2img.c $(HACK_H) $(INCL)bitmfile.h + $(CC) $(CFLAGS) $(CSTD) -c .win/gem/xpm2img.c /obj=$@ +bitmfile.obj: [-.win.gem]bitmfile.c $(INCL)bitmfile.h + $(CC) $(CFLAGS) $(CSTD) [-.win.gem]bitmfile.c /obj=$@ + +uudecode.exe: uudecode.obj + $(CLINK) $(LFLAGS) /EXE=$@ uudecode.obj $(LIBS) +uudecode.obj: $(SYSSHR)uudecode.c + $(CC) $(CFLAGS) $(CSTD) /obj=$@ $(SYSSHR)uudecode.c + +# make sure host object files from src are available when needed +# (note: these dependencies have been copied from Makefile.src so only come +# indirectly from 'make depend', hence are subject to bit rot as src changes) +$(OBJDIR)alloc.obj: $(SRC)alloc.c $(CONFIG_H) + $(CC) $(CFLAGS) $(CSTD) $(SRC)alloc.c /obj=$@ +$(OBJDIR)drawing.obj: $(SRC)drawing.c $(CONFIG_H) $(INCL)color.h \ + $(INCL)rm.h $(INCL)objclass.h $(INCL)defsym.h \ + $(INCL)objects.h $(INCL)sym.h + $(CC) $(CFLAGS) $(CSTD) $(SRC)drawing.c /obj=$@ +$(OBJDIR)decl.obj: $(SRC)decl.c $(HACK_H) + $(CC) $(CFLAGS) $(CSTD) $(SRC)decl.c /obj=$@ +$(OBJDIR)monst.obj: $(SRC)monst.c $(CONFIG_H) $(INCL)permonst.h \ + $(INCL)align.h $(INCL)monattk.h $(INCL)monflag.h \ + $(INCL)monsters.h $(INCL)sym.h $(INCL)defsym.h \ + $(INCL)color.h + $(CC) $(CFLAGS) $(CSTD) $(SRC)monst.c /obj=$@ +$(OBJDIR)objects.obj: $(SRC)objects.c $(CONFIG_H) $(INCL)obj.h \ + $(INCL)prop.h $(INCL)skills.h $(INCL)color.h \ + $(INCL)objclass.h $(INCL)defsym.h $(INCL)objects.h + $(CC) $(CFLAGS) $(CSTD) $(SRC)objects.c /obj=$@ +$(OBJDIR)dlb.obj: $(SRC)dlb.c $(CONFIG_H) $(INCL)dlb.h + $(CC) $(CFLAGS) $(CSTD) $(SRC)dlb.c /obj=$@ +# this differs substantially from what Makefile.src specifies +$(OBJDIR)date.obj: $(SRC)date.c $(CONFIG_H) + $(CC) $(CFLAGS) $(CSTD) $(SRC)date.c /obj=$@ + +# make sure hack.h dependencies get transitive information +$(HACK_H): $(CONFIG_H) + @pipe set def $(SRC) && $(MAKE) $(HACK_H) && set def $(UTIL) +$(CONFIG_H): $(INCL)config.h + @pipe set def $(SRC) && $(MAKE) $(CONFIG_H) && set def $(UTIL) + +clean-fixup: + -delete/log $(INCL)tile.h;* + +clean: clean-fixup + -delete/log *.obj;* + +spotless: clean + -delete/log makedefs.exe;*,recover.exe;*,dlb.exe;* + -delete/log gif2txt.exe;*,txt2ppm.exe;*,tile2x11.exe;* \ + ,tile2img.ttp;*,xpm2img.ttp;*, \ + tilemap.exe;*,tileedit.exe;*,tile2bmp.exe;*,uudecode.exe; +.PHONY: recover makedefs + +recover: recover.exe +makedefs: makedefs.exe + diff --git a/sys/vms/sysconf b/sys/vms/sysconf index e465020948..f3e8fd4039 100644 --- a/sys/vms/sysconf +++ b/sys/vms/sysconf @@ -1,4 +1,4 @@ -# NetHack 3.7 sysconf $NHDT-Date: 1596498305 2020/08/03 23:45:05 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.4 $ +# NetHack 3.7 sysconf $NHDT-Date: 1704043695 2023/12/31 17:28:15 $ $NHDT-Branch: keni-luabits2 $:$NHDT-Revision: 1.7 $ # Copyright (c) 2015 by Robert Patrick Rankin # NetHack may be freely redistributed. See license for details. # @@ -84,7 +84,7 @@ HIDEUSAGE=1 # Use "Live logging" for important events (achievements, wishes, etc) # Only available if NetHack was compiled with LIVELOG. -# Only really meaningful for public servers. +# Only really meaningful for public servers or debugging. # See the log in-game with #chronicle -command. # Bitmask for kinds of things you want to log - combine the following values # as desired. @@ -104,7 +104,7 @@ HIDEUSAGE=1 # 0x1000 - Log 'minor' achievements - can be spammy # 0x2000 - Spoiler event; can include in livelog but hidden from #chronicle # 0x4000 - Include as 'major' event in dumplog; can be hidden from livelog -# 0x8000 - Livelog debug msgs (currently only 'enter new level') +# 0x8000 - Livelog debug msgs #LIVELOG=0x1FFF # Show debugging information originating from these source files. diff --git a/sys/vms/vmsbuild.com b/sys/vms/vmsbuild.com index 83bdb14818..b9f8cc7219 100755 --- a/sys/vms/vmsbuild.com +++ b/sys/vms/vmsbuild.com @@ -1,12 +1,8 @@ $ ! vms/vmsbuild.com -- compile and link NetHack 3.7.* [pr] $ version_number = "3.7.0" -$ ! $NHDT-Date: 1609347486 2020/12/30 16:58:06 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.35 $ +$ ! $NHDT-Date: 1687541093 2023/06/23 17:24:53 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.39 $ $ ! Copyright (c) 2018 by Robert Patrick Rankin $ ! NetHack may be freely redistributed. See license for details. -$ -$ luaver = "546" -$ luadotver = "5.4.6" -$ luaunderver = "5_4_6" $ ! $ ! usage: $ ! $ set default [.src] !or [-.-.src] if starting from [.sys.vms] @@ -17,7 +13,7 @@ $ ! compiler-option : either "VSIC", "VAXC", "DECC", $ ! "GNUC" or "" or "fetchlua" !default in 3.7 is VSIC $ ! link-option : either "SHARE[able]" or "LIB[rary]" !default SHARE $ ! cc-switches : optional qualifiers for CC (such as "/noOpt/Debug") -$ ! linker-switches : optional qualifers for LINK (/Debug or /noTraceback) +$ ! linker-switches : optional qualifiers for LINK (/Debug or /noTraceback) $ ! interface : "TTY" or "CURSES" or "TTY+CURSES" or "CURSES+TTY" $ ! notes: $ ! If the symbol "CC" is defined, compiler-option is not used (unless it @@ -30,6 +26,11 @@ $ ! to re-link with GNUC library, 'CC' must begin with "G" (or "g"). $ ! All options are positional; to specify a later one without an earlier $ ! one, use "" in the earlier one's position, such as $ ! $ @[-.sys.vms]vmsbuild "" "" "" "" "TTY+CURSES" +$ ! +$ ! Lua Version +$ luaver = "546" +$ luadotver = "5.4.6" +$ luaunderver = "5_4_6" $ $ decc_dflt = f$trnlnm("DECC$CC_DEFAULT") $ j = (decc_dflt.nes."") .and. 1 @@ -41,7 +42,7 @@ $ if f$type(gcc).eqs."STRING" then gnuc_ = gcc $ gnulib = "gnu_cc:[000000]gcclib/Library" !(not used w/ vaxc) $ ! common CC options (/obj=file doesn't work for GCC 1.36, use rename instead) $ c_c_ = "/INCLUDE=([-.INCLUDE],[-.LIB.lua''luaver'.SRC])" - - + "/DEFINE=(""LUA_USE_C89"")" + + "/DEFINE=(""LUA_USE_C89"",USE_FCNTL)" $ cxx_c_ = "/INCLUDE=([-.INCLUDE],[-.LIB.LUA''luaver'.SRC])" $ veryold_vms = f$extract(1,1,f$getsyi("VERSION")).eqs."4" - .and. f$extract(3,3,f$getsyi("VERSION")).lts."6" @@ -76,7 +77,7 @@ $ copy sys$input: sys$error: !p1 usage or "GNUC" -- use GNU C to compile everything or "LINK" -- skip compilation, just relink nethack.exe or "SPEC[IAL]" -- just compile and link dlb.exe and recover.exe - or "FETCHLUA" -- skip compilaton, just fetch lua from lua.org + or "FETCHLUA" -- skip compilation, just fetch lua from lua.org or "BUILDLUA" -- build [-.lib.lua]lua'LUAVER'.olb or "" -- carry out default operation (VAXC unless 'CC' is defined) @@ -353,8 +354,6 @@ $! $! default to using cppregex $ if f$search("regex.c").eqs."" then - copy [-.sys.share]cppregex.cpp []regex.cpp -$ if f$search("regex.c").eqs."" then - - copy [-.sys.share]pmatchregex.c []regex.c $! if f$search("random.c").eqs."" then copy [-.sys.share]random.c []*.* $ if f$search("tclib.c") .eqs."" then - copy [-.sys.share]tclib.c []*.* @@ -421,10 +420,10 @@ $ c_list = "decl,version,[-.sys.vms]vmsunix" - $ gosub compile_list $ c_list = interface !ttysrc or cursessrc or both $ gosub compile_list -$ c_list = "allmain,apply,artifact,attrib,ball,bones,botl,cmd,dbridge" - - + ",dothrow,drawing,detect,dig,display,do,do_name,do_wear,dog" - - + ",dogmove,dokick,dungeon,eat,end,engrave,exper,explode" - - + ",files,fountain" +$ c_list = "allmain,apply,artifact,attrib,ball,bones,botl,calendar,cmd" - + + ",coloratt,dbridge,dothrow,drawing,detect,dig,display,do,do_name" - + + ",do_wear,dog,dogmove,dokick,dungeon,eat,end,engrave,exper,explode" - + + ",extralev,files,fountain,getpos,glyphs" $ gosub compile_list $ c_list = "hack,hacklib,insight,invent,light,lock,mail,makemon" - + ",mcastu,mdlib,mhitm,mhitu,minion,mklev,mkmap,mkmaze" - @@ -438,12 +437,13 @@ $ c_list = "nhlua,nhlobj,nhlsel" $ gosub compile_list $ c_list = "o_init,objnam,options,pager,pickup" - + ",pline,polyself,potion,pray,priest,quest,questpgr,read" - - + ",rect,region,restore,rip,rnd,role,rumors,save,sfstruct,shk" - - + ",shknam,sit,sounds,sp_lev,spell,steal,steed,symbols" - - + ",sys,teleport,timeout,topten,track,trap,utf8map,u_init" + + ",rect,region,report,restore,rip,rnd,role,rumors,save,selvar" - + + ",sfstruct,shk,shknam,sit,sounds,sp_lev,spell,stairs,steal" - + + ",steed,strutil,symbols,sys,teleport,timeout,topten,track" - + + ",trap,u_init,utf8map" $ gosub compile_list $ c_list = "uhitm,vault,vision,weapon,were,wield,windows" - - + ",wizard,worm,worn,write,zap" + + ",wizard,wizcmds,worm,worn,write,zap" $ gosub compile_list $! $link: diff --git a/sys/vms/vmsfiles.c b/sys/vms/vmsfiles.c index e2ae8d6578..38a80b9a88 100644 --- a/sys/vms/vmsfiles.c +++ b/sys/vms/vmsfiles.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 vmsfiles.c $NHDT-Date: 1596498306 2020/08/03 23:45:06 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.12 $ */ +/* NetHack 3.7 vmsfiles.c $NHDT-Date: 1685522046 2023/05/31 08:34:06 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.19 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2007. */ /* NetHack may be freely redistributed. See license for details. */ @@ -8,7 +8,6 @@ * routines or substitute for ones where we want behavior modification. */ #include "config.h" -#include #ifdef VMSVSI #include @@ -16,7 +15,7 @@ #include #endif -/* lint supression due to lack of extern.h */ +/* lint suppression due to lack of extern.h */ int vms_link(const char *, const char *); int vms_unlink(const char *); int vms_creat(const char *, unsigned int); @@ -36,13 +35,13 @@ int c__translate(int); #ifndef VMSVSI extern unsigned long sys$parse(), sys$search(), sys$enter(), sys$remove(); -extern int VDECL(lib$match_cond, (int, int, ...)); +extern int lib$match_cond(int, int, ...); #endif #define vms_success(sts) ((sts) & 1) /* odd, */ #define vms_failure(sts) (!vms_success(sts)) /* even */ -/* vms_link() -- create an additional directory for an existing file */ +/* vms_link() -- create an additional directory entry for an existing file */ int vms_link(const char *file, const char *new) { @@ -249,7 +248,7 @@ same_dir(const char *d1, const char *d2) int c__translate(int code) { - register int trans; + int trans; /* clang-format off */ /* *INDENT-OFF* */ @@ -309,7 +308,7 @@ vms_basename(const char *name, boolean keep_suffix) { unsigned len; char *base, *base_p, *xtra_p; - register const char *name_p; + const char *name_p; /* skip directory/path */ if ((name_p = strrchr(name, ']')) != 0) diff --git a/sys/vms/vmsmail.c b/sys/vms/vmsmail.c index 15e31405ef..24f9ad5fa9 100644 --- a/sys/vms/vmsmail.c +++ b/sys/vms/vmsmail.c @@ -1,11 +1,11 @@ -/* NetHack 3.7 vmsmail.c $NHDT-Date: 1596498307 2020/08/03 23:45:07 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.11 $ */ +/* NetHack 3.7 vmsmail.c $NHDT-Date: 1685522048 2023/05/31 08:34:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.18 $ */ /* Copyright (c) Robert Patrick Rankin, 1991. */ /* NetHack may be freely redistributed. See license for details. */ #include "config.h" #include "mail.h" -/* lint supression due to lack of extern.h */ +/* lint suppression due to lack of extern.h */ unsigned long init_broadcast_trapping(void); unsigned long enable_broadcast_trapping(void); unsigned long disable_broadcast_trapping(void); @@ -20,7 +20,6 @@ struct mail_info *parse_next_broadcast(void); #ifdef MAIL #include "wintype.h" #include "winprocs.h" -#include #include #include #ifndef __GNUC__ @@ -43,7 +42,7 @@ extern int strncmpi(const char *, const char *, int); extern size_t strspn(const char *, const char *); #ifndef __DECC -extern int VDECL(sscanf, (const char *, const char *, ...)); +extern int sscanf(const char *, const char *, ...); #endif #ifdef VMSVSI @@ -71,7 +70,7 @@ static long pasteboard_id = 0; /* SMG's magic cookie */ * Unrecognized broadcasts result in the mail-daemon * arriving and announcing the display text, but no scroll being created. * If SHELL is undefined, then all broadcasts are treated as 'other'; since - * no subproceses are allowed, there'd be no way to respond to the scroll. + * no subprocesses are allowed, there'd be no way to respond to the scroll. * * When a scroll of mail is read by the character, readmail() extracts * the command string and uses it for the default when prompting the @@ -99,7 +98,7 @@ static long pasteboard_id = 0; /* SMG's magic cookie */ * Anything else results in just the message text being passed along, no * scroll of mail so consequently no command to execute when scroll read. * The user can set up ``$ XYZZY :== SEND'' prior to invoking NetHack if - * vanilla JNET responses to Bitnet messages are prefered. + * vanilla JNET responses to Bitnet messages are preferred. * * Static return buffers are used because only one broadcast gets * processed at a time, and the essential information in each one is @@ -125,7 +124,7 @@ parse_brdcst(char *buf) /* called by parse_next_broadcast() */ char *txt; const char *nam, *cmd; #ifdef SHELL /* only parse if spawned commands are enabled */ - register char *p, *q; + char *p, *q; boolean is_jnet_send; char user[127 + 1], node[127 + 1], sentinel; @@ -305,10 +304,10 @@ parse_brdcst(char *buf) /* called by parse_next_broadcast() */ /* filter out non-printable characters and redundant noise */ -static void filter_brdcst(register char *buf) /* called by parse_next_broadcast() */ +static void filter_brdcst(char *buf) /* called by parse_next_broadcast() */ /* in: original text; out: filtered text */ { - register char c, *p, *buf_p; + char c, *p, *buf_p; /* filter the text; restrict consecutive spaces or dots to just two */ for (p = buf_p = buf; *buf_p; buf_p++) { diff --git a/sys/vms/vmsmain.c b/sys/vms/vmsmain.c index a72a1e7a92..1579707ecd 100644 --- a/sys/vms/vmsmain.c +++ b/sys/vms/vmsmain.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 vmsmain.c $NHDT-Date: 1596498307 2020/08/03 23:45:07 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.45 $ */ +/* NetHack 3.7 vmsmain.c $NHDT-Date: 1693359633 2023/08/30 01:40:33 $ $NHDT-Branch: keni-crashweb2 $:$NHDT-Revision: 1.57 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -33,12 +33,14 @@ static vms_handler_type vms_handler(genericptr_t, genericptr_t); static void wd_message(void); static boolean wiz_error_flag = FALSE; +static char *progname = (char *) 0; + int main(int argc, char *argv[]) { NHFILE *nhfp; #ifdef CHDIR - register char *dir; + char *dir; #endif boolean resuming = FALSE; /* assume new game */ @@ -48,12 +50,15 @@ main(int argc, char *argv[]) privon(); #endif - early_init(); + early_init(argc, argv); atexit(byebye); - gh.hname = argv[0]; - gh.hname = vms_basename(gh.hname); /* name used in 'usage' type messages */ - gh.hackpid = getpid(); + /* vms_basename(,FALSE) strips device, directory, suffix, and version; + the result is returned in a static buffer so we make a copy that + isn't at risk of getting clobbered by core's handling of DEBUGFILES */ + progname = dupstr(vms_basename(argv[0], FALSE)); + gh.hname = progname; + svh.hackpid = getpid(); (void) umask(0); choose_windows(DEFAULT_WINDOW_SYS); @@ -75,15 +80,9 @@ 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; + } if (argcheck(argc, argv, ARG_DEBUG) == 1) { argc--; argv++; @@ -276,11 +275,11 @@ process_options(int argc, char *argv[]) #endif case 'u': if (argv[0][2]) - (void) strncpy(gp.plname, argv[0] + 2, sizeof(gp.plname) - 1); + (void) strncpy(svp.plname, argv[0] + 2, sizeof(svp.plname) - 1); else if (argc > 1) { argc--; argv++; - (void) strncpy(gp.plname, argv[0], sizeof(gp.plname) - 1); + (void) strncpy(svp.plname, argv[0], sizeof(svp.plname) - 1); } else raw_print("Player name expected after -u"); break; @@ -382,16 +381,16 @@ whoami(void) * Note that we trust the user here; it is possible to play under * somebody else's name. */ - register char *s; + char *s; - if (!*gp.plname && (s = nh_getenv("USER"))) - (void) lcase(strncpy(gp.plname, s, sizeof(gp.plname) - 1)); + if (!*svp.plname && (s = nh_getenv("USER"))) + (void) lcase(strncpy(svp.plname, s, sizeof(svp.plname) - 1)); } static void byebye(void) { - void (*hup)(int) ; + void (*hup)(int); #ifdef SHELL extern unsigned long dosh_pid, mail_pid; #ifndef VMSVSI @@ -405,11 +404,19 @@ byebye(void) if (mail_pid) (void) sys$delprc(&mail_pid, (genericptr_t) 0), mail_pid = 0; #endif +#ifdef FREE_ALL_MEMORY + if (progname && !program_state.panicking) { + if (gh.hname == progname) + gh.hname = (char *) 0; + free((genericptr_t) progname), progname = (char *) 0; + } +#endif /* SIGHUP doesn't seem to do anything on VMS, so we fudge it here... */ - hup = (void (*)(int) ) signal(SIGHUP, SIG_IGN); - if (!gp.program_state.exiting++ && hup != (void (*)(int) ) SIG_DFL - && hup != (void (*)(int) ) SIG_IGN) { + hup = (void (*)(int)) signal(SIGHUP, SIG_IGN); + if (!program_state.exiting++ + && hup != (void (*)(int)) SIG_DFL + && hup != (void (*)(int)) SIG_IGN) { (*hup)(SIGHUP); } @@ -432,7 +439,7 @@ genericptr_t mechargs) /* [0] is argc, [1..argc] are the real args */ if (condition == SS$_ACCVIO /* access violation */ || (condition >= SS$_ASTFLT && condition <= SS$_TBIT) || (condition >= SS$_ARTRES && condition <= SS$_INHCHME)) { - gp.program_state.done_hup = TRUE; /* pretend hangup has been attempted */ + program_state.done_hup = TRUE; /* pretend hangup has been attempted */ #if (NH_DEVEL_STATUS == NH_STATUS_RELEASED) if (wizard) #endif @@ -470,6 +477,13 @@ authorize_wizard_mode(void) return FALSE; } +/* similar to above, validate explore mode access */ +boolean +authorize_explore_mode(void) +{ + return TRUE; /* no restrictions on explore mode */ +} + static void wd_message(void) { diff --git a/sys/vms/vmsmisc.c b/sys/vms/vmsmisc.c index 3cd8c3d24d..2c12261748 100644 --- a/sys/vms/vmsmisc.c +++ b/sys/vms/vmsmisc.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 vmsmisc.c $NHDT-Date: 1596498308 2020/08/03 23:45:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.12 $ */ +/* NetHack 3.7 vmsmisc.c $NHDT-Date: 1685522049 2023/05/31 08:34:09 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.18 $ */ /* Copyright (c) 2011 by Robert Patrick Rankin */ /* NetHack may be freely redistributed. See license for details. */ @@ -22,7 +22,7 @@ ATTRNORETURN void vms_abort(void); /* first arg should be unsigned long but has unsigned int */ #ifndef VMSVSI -extern void VDECL(lib$signal, (unsigned, ...)); +extern void lib$signal(unsigned, ...); #endif /* terminate, converting Unix-style exit code into VMS status code */ diff --git a/sys/vms/vmssetup.com b/sys/vms/vmssetup.com new file mode 100644 index 0000000000..b841bb882d --- /dev/null +++ b/sys/vms/vmssetup.com @@ -0,0 +1,25 @@ +$ ! vmssetup.com -- place GNU Makefiles in their target directories +$ version_number = "3.7.0" +$ ! $NHDT-Date: 1687541093 2024/03/08 17:24:53 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.39 $ +$ ! NetHack may be freely redistributed. See license for details. +$ ! +$ relsrc = "" +$ reldst = "-.-" +$ if (f$search("[]dat.dir") .nes. "") .AND. - + (f$search("[]src.dir") .nes. "") .AND. - + (f$search("[]include.dir") .nes. "") .AND. - + (f$search("[]sys.dir") .nes. "") then - + relsrc = ".sys.vms" +$ if (f$search("[]dat.dir") .nes. "") .AND. - + (f$search("[]src.dir") .nes. "") .AND. - + (f$search("[]include.dir") .nes. "") .AND. - + (f$search("[]sys.dir") .nes. "") then - + reldst = "" +$ copy ['relsrc']Makefile_top.vms ['reldst']Makefile.vms +$ copy ['relsrc']Makefile_dat.vms ['reldst'.dat]Makefile.vms +$ copy ['relsrc']Makefile_doc.vms ['reldst'.doc]Makefile.vms +$ copy ['relsrc']Makefile_src.vms ['reldst'.src]Makefile.vms +$ copy ['relsrc']Makefile_utl.vms ['reldst'.util]Makefile.vms +$! +$done: +$ exit diff --git a/sys/vms/vmstty.c b/sys/vms/vmstty.c index 00ded425c6..b6dd2013e3 100644 --- a/sys/vms/vmstty.c +++ b/sys/vms/vmstty.c @@ -158,7 +158,7 @@ vms_getchar(void) static volatile int recurse = 0; /* SMG is not AST re-entrant! */ #endif - if (gp.program_state.done_hup) { + if (program_state.done_hup) { /* hangup has occurred; do not attempt to get further user input */ return ESC; } @@ -293,7 +293,7 @@ static const char *arrow_or_PF = "ABCDPQRS", /* suffix char */ /* Ultimate return value is (index into smg_keypad_codes[] + 256). */ static short -parse_function_key(register int c) +parse_function_key(int c) { struct _rd_iosb iosb; unsigned long sts; @@ -319,8 +319,8 @@ parse_function_key(register int c) } else sts = SS$_NORMAL; if (vms_ok(sts) || sts == SS$_TIMEOUT) { - register int cnt = iosb.trm_offset + iosb.trm_siz + inc; - register char *p = seq_buf; + int cnt = iosb.trm_offset + iosb.trm_siz + inc; + char *p = seq_buf; if (c == ESC) /* check for 7-bit vt100/ANSI, or vt52 */ if (*p == '[' || *p == 'O') @@ -328,7 +328,7 @@ parse_function_key(register int c) else if (strchr(arrow_or_PF, *p)) c = SS3; /*CSI*/ if (cnt > 0 && (c == SS3 || (c == CSI && strchr(arrow_or_PF, *p)))) { - register char *q = strchr(smg_keypad_codes, *p); + char *q = strchr(smg_keypad_codes, *p); if (q) result = 256 + (q - smg_keypad_codes); @@ -513,11 +513,11 @@ setftty(void) sg.sm.tt2_char = tt2_char_active; setctty(); - start_screen(); + term_start_screen(); settty_needed = TRUE; } -/* enable kbd interupts if enabled when game started */ +/* enable kbd interrupts if enabled when game started */ void intron(void) { @@ -582,9 +582,26 @@ VA_DECL(const char *, s) #endif exit(EXIT_FAILURE); } + +#ifdef SIGWINCH +/* called by resize_tty(wintty.c) after receiving a SIGWINCH signal; + terminal size has changed and we should update LI and CO (from termcap) */ +void +getwindowsz(void) +{ + /* + * gettty() has code to do this, but it can't be used directly because + * it fetches terminal state in order to reset that upon termination. + * We need to avoid clobbering other saved state with values used by + * game-in-progress. For now, do nothing. + */ + return; +} +#endif + #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. @@ -592,6 +609,8 @@ VA_DECL(const char *, s) void tty_utf8graphics_fixup(void) { + return; } #endif /* ENHANCED_SYMBOLS */ +/*vmstty.c */ diff --git a/sys/vms/vmsunix.c b/sys/vms/vmsunix.c index 0bcdae7b23..89b18a1747 100644 --- a/sys/vms/vmsunix.c +++ b/sys/vms/vmsunix.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 vmsunix.c $NHDT-Date: 1605493693 2020/11/16 02:28:13 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.24 $ */ +/* NetHack 3.7 vmsunix.c $NHDT-Date: 1685522050 2023/05/31 08:34:10 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.31 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -29,12 +29,11 @@ #include #undef umask #endif -#include extern int debuggable; /* defined in vmsmisc.c */ #ifndef VMSVSI -extern void VDECL(lib$signal, (unsigned, ...)); +extern void lib$signal(unsigned, ...); extern unsigned long sys$setprv(); extern unsigned long lib$getdvi(), lib$getjpi(), lib$spawn(), lib$attach(); extern unsigned long smg$init_term_table_by_type(), smg$del_term_table(); @@ -43,7 +42,7 @@ extern unsigned long smg$init_term_table_by_type(), smg$del_term_table(); #define vms_ok(sts) ((sts) & 1) /* odd => success */ /* this could be static; it's only used within this file; - it won't be used at all if C_LIB$INTIALIZE gets commented out below, + it won't be used at all if C_LIB$INITIALIZE gets commented out below, so make it global so that compiler won't complain that it's not used */ int vmsexeini(const void *, const void *, const unsigned char *); @@ -57,7 +56,7 @@ static void hack_resume(boolean); static int veryold(int fd) { - register int i; + int i; time_t date; struct stat buf; @@ -99,7 +98,7 @@ veryold(int fd) void getlock(void) { - register int i = 0, fd; + int i = 0, fd; /* idea from rpick%ucqais@uccba.uc.edu * prevent automated rerolling of characters @@ -120,7 +119,7 @@ getlock(void) 'a','b',&c below; override the default and use if we aren't restricting the number of simultaneous games */ if (!gl.locknum) - Sprintf(gl.lock, "_%u%s", (unsigned) getuid(), gp.plname); + Sprintf(gl.lock, "_%u%s", (unsigned) getuid(), svp.plname); regularize(gl.lock); set_levelfile_name(gl.lock, 0); @@ -154,8 +153,8 @@ getlock(void) if (fd == -1) { error("cannot creat lock file."); } else { - if (write(fd, (char *) &gh.hackpid, sizeof(gh.hackpid)) - != sizeof(gh.hackpid)) { + if (write(fd, (char *) &svh.hackpid, sizeof(svh.hackpid)) + != sizeof(svh.hackpid)) { error("cannot write lock"); } if (close(fd) == -1) { @@ -165,9 +164,9 @@ getlock(void) } /* normalize file name */ -void regularize(register char *s) +void regularize(char *s) { - register char *lp; + char *lp; for (lp = s; *lp; lp++) /* note: '-' becomes '_' */ if (!(isalpha(*lp) || isdigit(*lp) || *lp == '$')) @@ -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); @@ -308,7 +308,7 @@ verify_term(void) if (devtype && vms_ok(smg$init_term_table_by_type(&devtype, &termtab, &smgdsc))) { - register char *p = &smgdevtyp[smgdsc.dsc$w_length]; + char *p = &smgdevtyp[smgdsc.dsc$w_length]; /* strip trailing blanks */ while (p > smgdevtyp && *--p == ' ') *p = '\0'; @@ -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; @@ -629,7 +630,7 @@ vms_get_saved_games(const char *savetemplate, /* wildcarded save file name in na if (filename[l - 1] != ' ') break; filename[l] = '\0'; - if ((charname = plname_from_file(filename)) != 0) + if ((charname = plname_from_file(filename, FALSE)) != 0) savefile(charname, count++, &asize, outarray); } (void) lib$find_file_end(&context); @@ -845,11 +846,11 @@ vmsexeini(const void *inirtn_unused, const void *clirtn_unused, * are appended rather than overwriting each other). * * VAX C made global variables become named program sections, to be - * compatable with Fortran COMMON blocks, simplifying mixed-language - * programs. GNU C for VAX/VMS did the same, to be compatable with + * compatible with Fortran COMMON blocks, simplifying mixed-language + * programs. GNU C for VAX/VMS did the same, to be compatible with * VAX C. By default, DEC C makes global variables be global symbols * instead, with its /Extern_Model=Relaxed_Ref_Def mode, but can be - * told to be VAX C compatable by using /Extern_Model=Common_Block. + * told to be VAX C compatible by using /Extern_Model=Common_Block. * * We don't want to force that for the whole program; occasional use * of /Extern_Model=Strict_Ref_Def to find mistakes is too useful. @@ -866,7 +867,7 @@ vmsexeini(const void *inirtn_unused, const void *clirtn_unused, * So, we switch modes for this hack only. Besides, psect attributes * for lib$initialize are different from the ones used for ordinary * variables, so we'd need to resort to some linker magic anyway. - * (With assembly language, in addtion to having full control of the + * (With assembly language, in addition to having full control of the * psect attributes in the source code, Macro32 would include enough * information in its object file such that linker wouldn't need any * extra instructions from us to make this work.) [If anyone links diff --git a/sys/windows/Makefile.mingw32 b/sys/windows/GNUmakefile similarity index 79% rename from sys/windows/Makefile.mingw32 rename to sys/windows/GNUmakefile index c2de9f5ddf..29085431ce 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/ @@ -28,7 +28,7 @@ # The default make target (so just typing 'mingw32-make'). # -default: install +default: package #--------------------------------------------------------------- # Where do you want the game to be built (which folder)? @@ -54,15 +54,21 @@ SOUND_LIBRARIES = windsound # Do you want debug information in the executable? # -DEBUGINFO = N +DEBUGINFO = Y # #--------------------------------------------------------------- -# Do you have a connection to the internet available that you want -# to utilize for obtaining prerequisite Lua source code and pdcurses source code +# Required for CRASHREPORT (but doesn't work yet, so off). + +CRASHREPORT = N + # +#--------------------------------------------------------------- +# Do you have a connection to the internet available that you want +# to utilize for obtaining prerequisite Lua source code and pdcursesmod +# source code. Defaults to Y. -INTERNET_AVAILABLE = N +INTERNET_AVAILABLE = Y # #--------------------------------------------------------------- @@ -91,12 +97,14 @@ GCC_EXTRA_WARNINGS = N # #============================================================================== +$(info Using $(lastword $(MAKEFILE_LIST))) + SKIP_NETHACKW = N USE_LUADLL = Y WANT_LUAC = N ifndef LUA_VERSION -LUAVER=5.4.4 +LUAVER=5.4.6 else LUAVER=$(LUA_VERSION) endif @@ -111,6 +119,9 @@ INTERNET_AVAILABLE=Y GIT_AVAILABLE=Y endif +PDCURSES=pdcursesmod +PDCURSESFLAGS = -DPDC_WIDE -DPDC_RGB + # #============================================================================== # Sanity checks for prerequisite Lua and pdcurses @@ -167,29 +178,29 @@ endif # LUA_MAY_PROCEED # Now, pdcurses ifeq "$(INTERNET_AVAILABLE)" "Y" ifeq "$(GIT_AVAILABLE)" "Y" -PDCURSES_TOP=../submodules/pdcurses +PDCURSES_TOP=../submodules/$(PDCURSES) ADD_CURSES=Y else # GIT_AVAILABLE -PDCURSES_TOP=../lib/pdcurses +PDCURSES_TOP=../lib/$(PDCURSES) ADD_CURSES=Y endif # GIT_AVAILABLE else # INTERNET_AVAILABLE is not Y below -# Your Makefile settings to not allow pdcurses to be obtained by +# Your Makefile settings to not allow $(PDCURSES) 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. # -ifneq ("$(wildcard ../submodules/pdcurses/curses.h)", "") -PDCURSES_TOP=../submodules/pdcurses +ifneq ("$(wildcard ../submodules/$(PDCURSES)/curses.h)", "") +PDCURSES_TOP=../submodules/$(PDCURSES) ADD_CURSES=Y -else ifneq ("$(wildcard ../lib/pdcurses/curses.h)", "") -PDCURSES_TOP=../lib/pdcurses +else ifneq ("$(wildcard ../lib/$(PDCURSES)/curses.h)", "") +PDCURSES_TOP=../lib/$(PDCURSES) ADD_CURSES=Y -endif # pdcurses sources available somewhere +endif # $(PDCURSES) sources available somewhere ifeq "$(ADD_CURSES)" "Y" -$(info Your Makefile settings do not allow pdcurses to be obtained by) -$(info git or by download, but a copy of pdcurses was found in $(PDCURSES_TOP),) +$(info Your Makefile settings do not allow $(PDCURSES) to be obtained by) +$(info git or by download, but a copy of $(PDCURSES) was found in $(PDCURSES_TOP),) $(info so that will be used.) endif # ADD_CURSES == Y endif # INTERNET_AVAILABLE @@ -202,12 +213,16 @@ ifeq "$(INTERNET_AVAILABLE)" "Y" ifeq "$(GIT_AVAILABLE)" "Y" GIT_HASH := $(shell echo `git rev-parse --verify HEAD` 2>&1) GIT_BRANCH := $(shell echo `git rev-parse --abbrev-ref HEAD` 2>&1) +GIT_PREFIX := $(shell echo `git config nethack.substprefix` 2>&1) ifdef GIT_HASH GITHASH = -DNETHACK_GIT_SHA=\"$(GIT_HASH)\" endif ifdef GIT_BRANCH GITBRANCH = -DNETHACK_GIT_BRANCH=\"$(GIT_BRANCH)\" endif +ifdef GIT_PREFIX +GITPREFIX = -DNETHACK_GIT_PREFIX=\"$(GIT_PREFIX)\" +endif endif endif @@ -234,8 +249,9 @@ NHV=$(subst ",,$(NHV1)) # TTY - window port files (tty) # MSWIN - window port files (win32) # WCURSES - window port files (curses) +# WCHAIN - window port files (chain) # WSHR - Tile support files -# SNDSYS - sound suppport files for win32 +# SNDSYS - sound support files for win32 # QT - QT window support files INCL =../include @@ -248,10 +264,13 @@ SNDSYS =../sound/windsound MSWSYS =../sys/windows TTY =../win/tty MSWIN =../win/win32 +WCHAIN =../win/chain WCURSES =../win/curses WSHR =../win/share QT =../win/Qt SNDWAVDIR = ../sound/wav +BinDir = ../binary +PkgDir = ../package # # Object directory. @@ -317,15 +336,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 @@ -348,9 +372,9 @@ $(OBJ): CLEAN_DIR = $(GAMEDIR) $(OBJ) -#===============-================================================= +#================================================================= # LUA library -# Source from http://www.lua.org/ftp/lua-5.4.4.tar.gz +# Source from http://www.lua.org/ftp/lua-5.4.6.tar.gz #================================================================= OLUA = $(O)lua @@ -409,7 +433,7 @@ 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 "#include \"$(LUASRC)/lualib.h\"" >> $@ @@ -425,6 +449,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 \ @@ -447,6 +472,29 @@ LUALIST = air Arc-fila Arc-filb Arc-goal Arc-loca Arc-strt \ LUAFILES = $(addprefix $(DAT)/, $(addsuffix .lua, $(LUALIST))) +#========================================== +# Hacklib +#========================================== +HACKLIBLIST = hacklib +HACKLIBSRC = $(addprefix ../src/,$(addsuffix .c, $(HACKLIBLIST))) +HL = $(O)hacklib +HLHACKLIB = $(HL)/hacklib.a +HLHACKLIBOBJS = $(addprefix $(HL)/, $(addsuffix .o, $(HACKLIBLIST))) +HLTARGETS = $(HLHACKLIB) $(HLHACKLIBOBJS) + +$(HL)/hacklib.a: $(HLHACKLIBOBJS) + ar rcs $@ $^ + +$(HL)/hacklib.o: $(HACKLIBSRC) | $(HL) + +$(HL)/%.o: $(SRC)/%.c | $(HL) + $(cc) $(CFLAGSU) $< -o$@ +$(HL): + @mkdir -p $@ + +CLEAN_DIR += $(HL) +CLEAN_FILE += $(HLTARGETS) + #========================================== # Makedefs #========================================== @@ -456,7 +504,7 @@ MTARGETS = $(U)makedefs.exe makedefs: $(MTARGETS) -$(U)makedefs.exe: $(MOBJS) +$(U)makedefs.exe: $(MOBJS) $(HLHACKLIB) $(ld) $(LDFLAGS) $^ -o$@ $(OM)/%.o: $(SRC)/%.c $(NHLUAH) | $(OM) @@ -483,7 +531,7 @@ 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) @@ -498,35 +546,65 @@ CLEAN_FILE += $(RTARGETS) $(ROBJS) # PDCurses #========================================== ifeq "$(ADD_CURSES)" "Y" -OP = $(O)pdcurses -PDCOBJS = $(addprefix $(OP)/, addch.o addchstr.o addstr.o attr.o beep.o bkgd.o border.o \ - clear.o color.o debug.o delch.o deleteln.o getch.o getstr.o getyx.o \ - inch.o inchstr.o initscr.o inopts.o insch.o insstr.o \ - kernel.o keyname.o mouse.o move.o outopts.o overlay.o \ - pad.o panel.o pdcclip.o pdcdisp.o pdcgetsc.o pdckbd.o pdcscrn.o pdcsetsc.o pdcutil.o printw.o \ - refresh.o scanw.o scr_dump.o scroll.o slk.o termattr.o touch.o util.o window.o) +PDCCOMMONSRC = addch addchstr addstr attr beep bkgd border clear \ + color debug delch deleteln getch getstr getyx inch \ + inchstr initscr inopts insch insstr kernel keyname \ + mouse move outopts overlay pad panel printw refresh \ + scanw scr_dump scroll slk termattr touch util window +PDCCOMMONOBJS = $(addsuffix .o, $(PDCCOMMONSRC)) + +OP = $(O)$(PDCURSES) +PDCOBJS = $(addprefix $(OP)/, $(PDCCOMMONOBJS) \ + pdcclip.o pdcdisp.o pdcgetsc.o pdckbd.o pdcscrn.o pdcsetsc.o pdcutil.o) PDCSRC = $(PDCURSES_TOP)/pdcurses PDCWINCON = $(PDCURSES_TOP)/wincon -PDCINCL = -I$(PDCURSES_TOP) -I$(PDCSRC) -I$(PDCWINCON) -PDCLIB = $(O)pdcurses.a +PDCINCL = -I$(PDCURSES_TOP) -I$(PDCSRC) +PDCLIB = $(O)$(PDCURSES).a PDCDEP = $(PDCURSES_TOP)/curses.h -pdcurses: $(PDCLIB) +$(PDCURSES): $(PDCLIB) $(PDCLIB): $(PDCOBJS) ar rcs $@ $^ $(OP)/%.o: $(PDCSRC)/%.c | $(OP) - $(cc) $(CFLAGS) $(PDCINCL) -D_LIB $< -o$@ + $(cc) $(CFLAGS) $(PDCURSESFLAGS) $(PDCINCL) -D_LIB $< -o$@ $(OP)/%.o: $(PDCWINCON)/%.c | $(OP) - $(cc) $(CFLAGS) $(PDCINCL) -D_LIB $< -o$@ + $(cc) $(CFLAGS) $(PDCURSESFLAGS) $(PDCINCL) -I$(PDCWINCON) -D_LIB $< -o$@ $(OP): @mkdir -p $@ CLEAN_DIR += $(OP) CLEAN_FILE += $(PDCLIB) $(PDCOBJS) + +ifneq ("$(wildcard $(PDCURSES_TOP)/wingui/pdcwin.h)", "") +# $(PDCURSES) in use; enable Curses graphics on NetHackW +OPW = $(O)$(PDCURSES)w +PDCWOBJS = $(addprefix $(OPW)/, $(PDCCOMMONOBJS) \ + pdcclip.o pdcdisp.o pdcgetsc.o pdckbd.o pdcscrn.o pdcsetsc.o pdcutil.o) +PDCWINGUI = $(PDCURSES_TOP)/wingui +PDCWLIB = $(O)$(PDCURSES)w.a + +$(PDCURSES)w: $(PDCWLIB) + +$(PDCWLIB): $(PDCWOBJS) + ar rcs $@ $^ + +$(OPW)/%.o: $(PDCSRC)/%.c | $(OPW) + $(cc) $(CFLAGS) $(PDCURSESFLAGS) $(PDCINCL) -D_LIB $< -o$@ + +$(OPW)/%.o: $(PDCWINGUI)/%.c | $(OPW) + $(cc) $(CFLAGS) $(PDCURSESFLAGS) $(PDCINCL) -I$(PDCWINGUI) -D_LIB $< -o$@ + +$(OPW): + @mkdir -p $@ + +CLEAN_DIR += $(OPW) +CLEAN_FILE += $(PDCWLIB) $(PDCWOBJS) +endif + endif #========================================== @@ -538,8 +616,8 @@ TTARGETS = $(U)tile2bmp.exe tile2bmp: $(TTARGETS) -$(U)tile2bmp.exe: $(TOBJS) - $(ld) $(LDFLAGS) $^ -o$@ +$(U)tile2bmp.exe: $(TOBJS) $(HLHACKLIB) + $(ld) $(LDFLAGS) $(HLHACKLIB) $^ -o$@ $(OT)/tiletxt.o: $(WSHR)/tilemap.c $(NHLUAH) | $(OT) $(cc) $(CFLAGSU) -DTILETEXT $< -o$@ @@ -621,8 +699,8 @@ tilemap: $(SRC)/tile.c $(SRC)/tile.c: $(U)tilemap.exe $< -$(U)tilemap.exe: $(TMOBJS) - $(ld) $(LDFLAGS) $^ -o$@ +$(U)tilemap.exe: $(TMOBJS) $(HLHACKLIB) + $(ld) $(LDFLAGS) $(HLHACKLIB) $^ -o$@ $(OTM)/tilemap.o: $(WSHR)/tilemap.c $(NHLUAH) | $(OTM) $(cc) $(CFLAGSU) $< -o$@ @@ -675,8 +753,8 @@ DTARGETS = $(U)dlb.exe $(DAT_CLEAN) $(DLBLST) $(DLB) dlb: $(DTARGETS) -$(U)dlb.exe: $(DLBOBJS) - $(ld) $(LDFLAGS) $^ -o$@ +$(U)dlb.exe: $(DLBOBJS) $(HLHACKLIB) + $(ld) $(LDFLAGS) $(HLHACKLIB) $^ -o$@ $(ODLB)/%.o: $(SRC)/%.c $(NHLUAH) | $(ODLB) $(cc) $(CFLAGSU) $< -o$@ @@ -749,17 +827,19 @@ ifeq "$(GIT_AVAILABLE)" "Y" fetchlua: @if [ ! -f $(LUASRC)/lua.h ] ; then \ git submodule init ../submodules/lua && \ - git submodule update --remote ../submodules/lua ; fi + git submodule update ../submodules/lua ; fi +# git submodule update --remote ../submodules/lua ; fi fetchpdcurses: @if [ ! -f $(PDCURSES_TOP)/curses.h ] ; then \ - git submodule init ../submodules/pdcurses && \ - git submodule update --remote ../submodules/pdcurses ; fi + git submodule init ../submodules/$(PDCURSES) && \ + git submodule update ../submodules/$(PDCURSES) ; fi +# git submodule update --remote ../submodules/$(PDCURSES) ; fi else # GIT_AVAILABLE no -CURLLUASRC=http://www.lua.org/ftp/lua-5.4.4.tar.gz -CURLLUADST=lua-5.4.4.tar.gz +CURLLUASRC=http://www.lua.org/ftp/lua-5.4.6.tar.gz +CURLLUADST=lua-5.4.6.tar.gz -CURLPDCSRC=https://github.com/wmcbrine/PDCurses/archive/refs/tags/3.9.zip -CURLPDCDST=pdcurses.zip +CURLPDCSRC=https://github.com/Bill-Gray/PDCursesMod/archive/refs/tags/v4.4.0.zip +CURLPDCDST=$(PDCURSES).zip fetchlua: @if [ ! -f $(LUASRC)/lua.h ] ; then \ @@ -774,8 +854,8 @@ fetchpdcurses: @if [ ! -f $(PDCURSES_TOP)/curses.h ] ; then \ cd ../lib ; \ curl -L $(CURLPDCSRC) -o $(CURLPDCDST) ; \ - mkdir -p pdcurses ; \ - /c/Windows/System32/tar -C pdcurses --strip-components=1 -xvf $(CURLPDCDST) ; \ + mkdir -p $(PDCURSES) ; \ + /c/Windows/System32/tar -C $(PDCURSES) --strip-components=1 -xvf $(CURLPDCDST) ; \ cd ../src ; \ fi endif # GIT_AVAILABLE @@ -917,20 +997,22 @@ endif # HAVE_SOUNDLIB #========================================== # nethackw #========================================== -COREOBJS = $(addsuffix .o, allmain alloc apply artifact attrib ball bones botl cmd cppregex \ +COREOBJS = $(addsuffix .o, allmain alloc apply artifact attrib ball bones botl \ + calendar 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 \ - hack hacklib insight invent isaac64 light lock \ + getpos glyphs hack insight invent isaac64 light lock \ mail makemon mcastu mdlib mhitm mhitu minion mklev mkmap mkmaze mkobj mkroom \ mon mondata monmove monst mplayer mthrowu muse music \ nhlobj nhlsel nhlua windsound o_init objects objnam options \ pager pickup pline polyself potion pray priest quest questpgr \ - random read rect region restore rip rnd role rumors \ - safeproc save sfstruct shk shknam sit sounds sp_lev spell steal steed \ + random read rect region report restore rip rnd role rumors \ + safeproc save 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 \ - worm worn write zap $(SOUNDLIBOBJS)) + wizcmds worm worn write zap $(SOUNDLIBOBJS)) CFLAGSW = $(CFLAGS) $(CFLAGSXTRA) $(SOUNDLIBINCL) $(COMMONDEF) $(DLBFLG) -DTILES -D_WINDOWS -DMSWIN_GRAPHICS -DSAFEPROCS -DNOTTYGRAPHICS $(SOUNDLIBDEFS) CPPFLAGSW = $(CFLAGS) $(CPPFLAGSXTRA) $(COMMONDEF) $(DLBFLG) -DTILES -D_WINDOWS -DMSWIN_GRAPHICS -DSAFEPROCS -DNOTTYGRAPHICS $(SOUNDLIBDEFS) @@ -945,10 +1027,26 @@ BMPS = $(addprefix $(MSWIN)/, $(addsuffix .bmp, mnsel mnselcnt mnunsel petmark p NHWRES = $(ONHW)/winres.o NHWTARGETS = $(GAMEDIR)/NetHackW.exe +ifneq "$(PDCWINGUI)" "" +CFLAGSW += $(NHCURSESFLAGS) +NHWONLY += $(addsuffix .o, cursdial cursinit cursinvt cursmain cursmesg cursmisc cursstat curswins guitty) +endif + +# uncomment for WINCHAIN +#COREOBJS += $(addsuffix .o, wc_chainin wc_chainout wc_trace) + +# XXX mess for testing libbacktrace +ifeq "$(CRASHREPORT)" "Y" +CFLAGS += -I/mingw64/include -g -static -gdwarf +LIBS += -L/mingw64/lib -lbacktrace +endif + nethackw: $(NHWTARGETS) -$(GAMEDIR)/NetHackW.exe: $(NHWOBJS) $(NHWRES) $(DATEW_O) $(LUALIB) | $(GAMEDIR) - $(ld) $(LDFLAGS) -mwindows $^ $(LIBS) -static -lstdc++ $(SOUNDLIBLIBS) -o$@ +$(GAMEDIR)/NetHackW.exe: $(NHWOBJS) $(NHWRES) $(DATEW_O) \ + $(LUALIB) $(HLHACKLIB) $(PDCWLIB) | $(GAMEDIR) + $(ld) $(LDFLAGS) -mwindows $^ $(HLHACKLIB) $(LIBS) -static -lstdc++ \ + $(SOUNDLIBLIBS) -o$@ $(ONHW)/%.o: $(SRC)/%.c $(NHLUAH) | $(ONHW) $(cc) $(CFLAGSW) $< -o$@ @@ -962,7 +1060,7 @@ $(ONHW)/%.o: $(SRC)/%.c $(NHLUAH) | $(ONHW) # was just recompiled. date.h is not used in the build of NetHack 3.7. # $(ONHW)/date.o: $(SRC)/date.c $(NHWOBJS) | $(ONHW) - $(cc) $(CFLAGSW) $(GITHASH) $(GITBRANCH) $< -o$@ + $(cc) $(CFLAGSW) $(GITHASH) $(GITBRANCH) $(GITPREFIX) $< -o$@ $(ONHW)/cppregex.o: $(SSYS)/cppregex.cpp $(NHLUAH) | $(ONHW) $(cc) $(CPPFLAGSW) $< -o$@ @@ -986,11 +1084,18 @@ $(ONHW)/%.o: $(MSWSYS)/%.c $(NHLUAH) | $(ONHW) $(ONHW)/%.o: $(MSWIN)/%.c $(NHLUAH) | $(ONHW) $(cc) $(CFLAGSW) $< -o$@ +$(ONHW)/%.o: $(WCHAIN)/%.c $(NHLUAH) | $(ONHW) + $(cc) $(CFLAGSW) $< -o$@ + $(ONHW)/%.o: $(WSHR)/%.c $(NHLUAH) | $(ONHW) $(cc) $(CFLAGSW) $< -o$@ -$(NHWRES): $(MSWIN)/NetHackW.rc $(BMPS) $(WAV) $(MSWIN)/NetHack.ico | $(ONHW) - $(rc) --include-dir=$(MSWIN) $(RCFLAGS) --input=$< -o$@ +$(ONHW)/%.o: $(WCURSES)/%.c $(NHLUAH) | $(ONHW) + $(cc) $(CFLAGSW) $(PDCINCL) $< -o$@ + +$(NHWRES): $(MSWIN)/NetHackW.rc $(MSWIN)/NetHackW.exe.manifest \ + $(BMPS) $(WAV) $(MSWIN)/NetHack.ico | $(ONHW) + $(rc) --include-dir=$(MSWIN) $(RCFLAGS) -DVIA_MAKE --input=$< -o$@ $(MSWIN)/tiles.bmp: $(U)tile2bmp.exe $(TILEFILES) $< $@ @@ -1019,9 +1124,11 @@ ONH = $(O)nethack NHONLY = consoletty.o getline.o topl.o wintty.o ifeq "$(ADD_CURSES)" "Y" -CFLAGSNH += -DCURSES_GRAPHICS -DCHTYPE_32 -DPDC_NCMOUSE +NHCURSESFLAGS = -DCURSES_GRAPHICS -DCURSES_UNICODE $(PDCURSESFLAGS) -DPDC_NCMOUSE +CFLAGSNH += $(NHCURSESFLAGS) NHONLY += $(addsuffix .o, cursdial cursinit cursinvt cursmain cursmesg cursmisc cursstat curswins) endif + DATE_O = $(addsuffix .o, $(addprefix $(ONH)/, date)) NHOBJS = $(addprefix $(ONH)/, $(COREOBJS) $(NHONLY)) @@ -1030,12 +1137,36 @@ NHTARGET = $(GAMEDIR)/NetHack.exe nethack: $(NHTARGET) -$(GAMEDIR)/NetHack.exe: $(NHOBJS) $(NHRES) $(DATE_O) $(LUALIB) $(PDCLIB) $(SOUNDLIBLIBS) | $(GAMEDIR) - $(ld) $(LDFLAGS) -mconsole $^ $(LIBS) -static -lstdc++ -o$@ +$(GAMEDIR)/NetHack.exe: $(NHOBJS) $(NHRES) $(DATE_O) $(LUALIB) $(PDCLIB) $(HLHACKLIB) \ + $(SOUNDLIBLIBS) | $(GAMEDIR) + $(ld) $(LDFLAGS) -mconsole $^ $(HLHACKLIB) $(LIBS) -static -lstdc++ -o$@ $(ONH)/%.o: $(SRC)/%.c $(NHLUAH) | $(ONH) $(cc) $(CFLAGSNH) $< -o$@ +#========================================== +# package +#========================================== +TARGET_CPU = x64 +NHV=370 +PKGFILES = nethackrc.template Guidebook.txt license NetHack.exe NetHack.txt \ + NetHackW.exe opthelp nhdat370 record symbols sysconf.template +FILESTOZIP = $(addprefix $(GAMEDIR)/, $(PKGFILES)) +MAINZIP = $(PkgDir)/nethack-$(NHV)-win-$(TARGET_CPU)-msys2.zip + +package: binary $(FILESTOZIP) $(MAINZIP) + @echo NetHack Windows package created: $(MAINZIP) + +$(MAINZIP): $(FILESTOZIP) | $(PkgDir) + /c/Windows/System32/tar -a -cf $(MAINZIP) -C $(GAMEDIR) $(PKGFILES) + @echo NetHack Windows package created: $(MAINZIP) + +$(PkgDir): + @mkdir -p $@ + +CLEAN_DIR += $(PkgDir) +CLEAN_FILE += $(MAINZIP) + # In NetHack 3.7, date.c must be recompiled after any other file is compiled, # otherwise the game internal build timestamp (and potentially git hash) # will not be accurate. @@ -1045,7 +1176,7 @@ $(ONH)/%.o: $(SRC)/%.c $(NHLUAH) | $(ONH) # was just recompiled. date.h is not used in the build of NetHack 3.7. # $(ONH)/date.o: $(SRC)/date.c $(NHOBJS) $(NHRES) | $(ONH) - $(cc) $(CFLAGSNH) $(GITHASH) $(GITBRANCH) $< -o$@ + $(cc) $(CFLAGSNH) $(GITHASH) $(GITBRANCH) $(GITPREFIX) $< -o$@ $(ONH)/cppregex.o: $(SSYS)/cppregex.cpp $(NHLUAH) | $(ONH) $(cc) $(CPPFLAGSNH) $< -o$@ @@ -1069,6 +1200,9 @@ $(ONH)/%.o: $(MSWSYS)/%.c $(NHLUAH) | $(ONH) $(ONH)/%.o: $(MSWIN)/%.c $(NHLUAH) | $(ONH) $(cc) $(CFLAGSNH) $< -o$@ +$(ONH)/%.o: $(WCHAIN)/%.c $(NHLUAH) | $(ONH) + $(cc) $(CFLAGSNH) $< -o$@ + $(ONH)/%.o: $(WSHR)/%.c $(NHLUAH) | $(ONH) $(cc) $(CFLAGSNH) $< -o$@ @@ -1091,7 +1225,7 @@ CLEAN_FILE += $(NHTARGET) $(NHOBJS) $(NHRES) #=============== TARGETS ================== #========================================== -.PHONY: all clean default install lua makedefs recover pdcurses \ +.PHONY: all clean default binary lua makedefs recover $(PDCURSES) $(PDCURSES)w \ tile2bmp tilemap uudecode dlb nethackw nethack tileutil \ fetchlua fetchpdcurses @@ -1099,29 +1233,30 @@ CLEAN_FILE += $(NHTARGET) $(NHOBJS) $(NHRES) # Everything # -all: install +all: package -TO_INSTALL = $(GAMEDIR)/NetHack.exe $(RTARGETS) $(GAMEDIRDLLS) \ - $(addprefix $(GAMEDIR)/, $(addsuffix .template, sysconf .nethackrc) \ - Guidebook.txt NetHack.txt license opthelp record symbols) +TO_BINARY = $(GAMEDIR)/NetHack.exe $(RTARGETS) $(GAMEDIRDLLS) \ + $(addprefix $(GAMEDIR)/, \ + $(addsuffix .template, sysconf nethackrc symbols) \ + Guidebook.txt NetHack.txt license opthelp record) ifeq "$(HAVE_SOUNDLIB)" "Y" -TO_INSTALL += $(addprefix $(GAMEDIR)/, $(addsuffix .wav, $(WAVLIST))) +TO_BINARY += $(addprefix $(GAMEDIR)/, $(addsuffix .wav, $(WAVLIST))) endif ifeq "$(USE_LUADLL)" "Y" -TO_INSTALL += $(LUADLL) +TO_BINARY += $(LUADLL) endif ifneq "$(SKIP_NETHACKW)" "Y" -TO_INSTALL += $(GAMEDIR)/NetHackW.exe +TO_BINARY += $(GAMEDIR)/NetHackW.exe endif ifeq "$(USE_DLB)" "Y" -TO_INSTALL += $(DLB) +TO_BINARY += $(DLB) endif -install: fetchlua fetchpdcurses $(TO_INSTALL) +binary: fetchlua fetchpdcurses $(TO_BINARY) ifdef CI_COMPILER ls -l $(SRC) ls -l $(DAT) @@ -1137,7 +1272,7 @@ $(GAMEDIR)/$(FMODLIBDLL): $(FMODLIBDIR)/$(FMODLIBDLL) cp $< $@ endif -$(GAMEDIR)/symbols: $(DAT)/symbols +$(GAMEDIR)/symbols.template: $(DAT)/symbols cp $< $@ $(GAMEDIR)/NetHack.txt: $(DOC)/nethack.txt @@ -1158,13 +1293,13 @@ $(GAMEDIR)/%: $(MSWSYS)/% $(GAMEDIR)/%: $(SNDWAVDIR)/% cp $< $@ -CLEAN_FILE += $(TO_INSTALL) +CLEAN_FILE += $(TO_BINARY) clean: @-rm -f $(CLEAN_FILE) @$(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 91% rename from sys/windows/Makefile.mingw32.depend rename to sys/windows/GNUmakefile.depend index 960df65f43..230bf8e389 100644 --- a/sys/windows/Makefile.mingw32.depend +++ b/sys/windows/GNUmakefile.depend @@ -2,12 +2,15 @@ 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$@ $(OM)/%.d: $(SRC)/%.c $(NHLUAH) | $(OM) $(cce) $(CFLAGSU) -DENUM_PM $< -o$@ @@ -78,6 +81,9 @@ $(ONHW)/%.d: $(MSWIN)/%.c $(NHLUAH) | $(ONHW) $(ONHW)/%.d: $(WSHR)/%.c $(NHLUAH) | $(ONHW) $(cce) $(CFLAGSW) $< -o$@ +$(ONHW)/%.d: $(WCURSES)/%.c $(NHLUAH) | $(ONHW) + $(cce) $(CFLAGSNH) $(PDCINCL) $< -o$@ + $(ONH)/%.d: $(SRC)/%.c $(NHLUAH) | $(ONH) $(cce) $(CFLAGSNH) $< -o$@ @@ -105,7 +111,7 @@ $(ONH)/%.d: $(TTY)/%.c $(NHLUAH) | $(ONH) $(ONH)/%.d: $(WCURSES)/%.c $(NHLUAH) | $(ONH) $(cce) $(CFLAGSNH) $(PDCINCL) $< -o$@ -OBJS4DEP = $(MOBJS) $(ROBJS) \ +OBJS4DEP = $(MOBJS) $(ROBJS) $(HLHACKLIBOBJS) \ $(TOBJS) $(GIFOBJ) $(GIF32OBJ) $(PPMOBJ) $(BMP32OBJ) $(T32OBJS) $(TUCOMMON) \ $(TMOBJS) $(UOBJS) $(DLBOBJS) $(NHWOBJS) $(NHOBJS) DEP_TARGETS = $(OBJS4DEP:o=d) diff --git a/sys/windows/Install.windows b/sys/windows/Install.windows index 15df767da4..d960c2e01b 100644 --- a/sys/windows/Install.windows +++ b/sys/windows/Install.windows @@ -1,356 +1,91 @@ - Copyright (c) NetHack Development Team 1990-2022 - NetHack may be freely redistributed. See license for details. - ============================================================== - Instructions for compiling and installing - NetHack 3.7 on a Windows system - (Windows 7/8.x/10/11 or later only) - ============================================================== - Last revision: $NHDT-Date: 1594155895 2020/07/07 21:04:55 $ +Copyright (c) NetHack Development Team 1990-2024 +NetHack may be freely redistributed. See license for details. +============================================================== + Instructions for compiling NetHack 3.7 on a Windows system + (only tested on Windows 10/11 or later) +============================================================== +Last revision: $NHDT-Date: 1594155895 2020/07/07 21:04:55 $ -Credit for the porting of NetHack to the Win32 Console Subsystem goes to -the NT Porting Team started by Michael Allison. - -Credit for the Win32 Graphical version of NetHack (aka "NetHack for -Windows" or NetHackW) goes to Alex Kompel who initially developed and -contributed the port. - -Alex Kompel, Dion Nicolaas, Yitzhak Sapir, Derek S. Ray, Michael Allison, -Pasi Kallinen, Bart House, and Janet Walz contributed to the maintainance -of the tty and graphical windows versions of NetHack 3.7.0. - -The build Makefiles and procedures produce two executables: - a. A TTY and curses version of NetHack in nethack.exe - b. A Windows Graphical version in nethackw.exe. - -You can use one of the following build environments: - o A copy of Microsoft Visual Studio 2017 Community Edition or - a copy of Microsoft Visual Studio 2019 Community Edition or - a copy of Microsoft Visual Studio 2022 Community Edition - - OR - - o An up-to-date copy of MinGW-w64. MinGW-w64 is a collection of - GNU C Compiler (GCC) executables, headers, files and import - libraries. The official site for MinGW-w64 is - https://www.mingw-w64.org/ - There are a few packaged distributions of MinGW-w64. We've tried - out these ones, but there are likely others that will work: - - MSYS2 - - https://www.msys2.org/ - - Download the installer, and start the appropriate bash shell. - - MinGW-w64 - winlibs standalone build - - https://github.com/brechtsanders/winlibs_mingw - - Download one of the releases from - https://github.com/brechtsanders/winlibs_mingw/releases - and extract the contents into a folder (ideally in a folder - without spaces in the path), and add the location of the - subfolder containing gcc.exe to your PATH. - -/---------------------------------------------\ -| Directories for a Win32 NetHack build | -\---------------------------------------------/ - - - (NetHack-top) - | - +-----+-----+------+------+-----+-------------+------~------+ - | | | | | | | | - util dat doc include src sys win submodules - | | | - +------+ +------+ +-----------+ - | | | | | | - share windows tty win32 lua pdcurses - | - vs +See file dat\History for credits and history of NetHack for Windows. /-----------------------------------------------------------------------\ -| Required components that are not bundled in the NetHack repository | -\-----------------------------------------------------------------------/ - -NetHack 3.7 for Windows requires some 3rd party sources that are not -directly distributed within the NetHack repository, but they are configured -as submodules if you are online and have git, or you can obtain them -yourself. - -Via git (recommended) - - If you obtained your NetHack sources by cloning from a git repository such - as https://github.com/NetHack/NetHack and you have git installed and - available and you are online, you simply need to execute the following - command from the top level NetHack folder to populate source code into the - submodules/lua and submodules/pdcurses folders: - - git submodule init - git submodule update --remote - - -Via zip download - - If you obtained your NetHack sources from a zip file or source other than - via git, you'll have to obtain Lua and pdcurses from their respective - locations (at the time of this writing). - Windows 10 or newer is assumed below, because wget and tar started to be - included in that version. - - o obtain Lua - cd lib - wget http://www.lua.org/ftp/lua-5.4.4.tar.gz - tar -xvf lua-5.4.4.tar.gz - cd .. - - o obtain pdcurses - cd lib - wget --no-check-certificate ^ - http://github.com/wmcbrine/PDCurses/archive/refs/tags/3.9.zip ^ - --output-document=pdcurses3.9.zip - tar -xvf pdcurses3.9.zip - ren PDCurses-3.9 pdcurses - cd .. - - o update your Makefiles to reflect: - - In sys/windows/Makefile.mingw32 - change from - PDCURSES_TOP = ../submodules/pdcurses - to - PDCURSES_TOP = ../lib/pdcurses - - change from - LUATOP = ../submodules/lua - to - LUATOP = ../lib/lua-5.4.4 - - In sys/windows/Makefile.nmake - change from - PDCURSES_TOP = ..\submodules\pdcurses - to - PDCURSES_TOP = ..\lib\pdcurses - - change from - LUATOP = ..\submodules\lua - to - LUATOP = ..\lib\lua-5.4.4 - -/-----------------------------------------------------------\ -| Building And Running Using Visual Studio 2017, 2019, 2022 | -\-----------------------------------------------------------/ - -If you are NOT using Visual Studio IDE, or you prefer to build -using a Make utility and a Makefile proceed to "Building Using Make". - -When using either Visual Studio 2017, 2019, or 2022, you simply need to -load the solution file within the IDE, build the solution and run the version -of NetHack you wish to run. - -The Visual Studio NetHack solution file can be found here: - sys\windows\vs\NetHack.sln - -Before executing the steps to build listed in the next paragraph, -decide if you want to include optional curses window-port. See -the note just below entitled "Optional curses window-port support." +| Download a prebuilt package of NetHack for Windows from NetHack.org | +\----------------------------------------------------------- -----------/ -So the steps are: - 1. Launch the IDE. - 2. Open the appropriate solution file. - 3. Select the build configuration you wish to use (Release, Debug, etc.). - 4. From the build menu, select build solution. - 5. Type F5 to start debugging. + FIXME: - * Optional curses window-port support * -Since 3.6.2, the community patch for a window-port that uses curses has been -incorporated into the NetHack source code tree. That window-port, which -evolved from work originally done by Karl Garrison, has been used in several -NetHack variants and on nethack.alt.org and on www.hardfought.org/nethack/. - -If you want to include the curses window-port support in your Visual Studio -build, you will have to first obtain the PDCurses sources from -https://github.com/wmcbrine/PDCurses -and have them available prior to building NetHack. There are two ways to -enable curses window-port support during the VS build: Either set the -environment variable PDCURSES to a folder containing a PDCurses -repository/source-tree - OR -Place the PDCurses folder alongside the NetHack source repository prior -to proceeding with steps 1 through 5 above. - - -/-------------------------------------------\ -| Building From the Command Line Using Make | -\-------------------------------------------/ - --------------------------------------------------------------------------- --- Beginning of prerequisite step -- - -The first step in building either version of NetHack via Makefile is to -execute sys\windows\nhsetup.bat to move some files to their required locations. - -From the command prompt: - cd sys\windows - nhsetup - -From a Windows explorer window: - double-click on nhsetup.bat - -If you wish to build from the command line, proceed to "BUILDING FROM -THE COMMAND LINE." - --- end of prerequisite step -- --------------------------------------------------------------------------- - -Two different versions of xNetHack will be built for Windows from the -command line using the Makefile approach: - A tty port utilizing the Win32 Console I/O subsystem, Console - xNetHack; - A Win32 native port built on the Windows API, Graphical xNetHack or - xNetHackW. - -The executable for Console xNetHack will be named xNetHack.exe. The -executable for Graphical xNetHack will be named xNetHackW.exe. The -Makefile configuration will build both; xNetHackW.exe and xNetHack.exe -will be able to use the same datafiles, save files and bones files. - -Since the last official release of NetHack, compilers and computer -architectures have evolved and you can now choose whether to build -a 32-bit x86 version, or a 64-bit x64 version. The default Makefile -is set up for a 32-bit x86 version, but that's only because it will -run on the most number of existing Windows environments. - -NetHack's save files and bones files in the 3.7.0 release have not yet -evolved enough to allow them to interchange between the 32-bit version -and the 64-bit version (or between different platforms). Hopefully -that will change in an upcoming release. - -I. Dispelling the Myths: - - Compiling NetHack for Windows is not as easy as it sounds, nor as hard - as it looks, however it will behoove you to read this entire section - through before beginning the task. - - We have provided a Makefile for each of the following compilers: - - o Microsoft Visual Studio 2017 or 2019 or 2022 C++ Compiler - The Community Editions are fine and available at no cost - o MinGW-w64 - - The Microsoft Visual Studio makefile was created for use - with MS NMAKE which is provided with the Microsoft compiler. - The supplied Makefile may work with earlier versions of the Microsoft - compiler, but that has not been tested. - - The GCC Makefile was created for use with GNU Make. - -II. To compile your copy of NetHack on a Windows machine: - -Setting Up - -1. It almost goes without saying that you should make sure that your - tools are set up and running correctly. That includes ensuring that - all the necessary environment variables for the compiler environment - are set correctly. - - Change your current directory to the src subfolder of the nethack - source tree. - cd src - -2. Since 3.6.2, the community patch for an optional curses window-port - has been incorporated into the NetHack source code tree. That - window-port, which evolved from work originally done by Karl Garrison, - has been used in several NetHack variants and on nethack.alt.org and - on www.hardfought.org/nethack/. The optional curses window-port is - available for Windows, macOS, and Unix (and also DOS). - - The Makefiles are configured by default to include the curses - window-port support in your command line Makefile build. - -3. Make sure all the necessary files are in the appropriate directory - structure. You should have a main NetHack top directory with - subdirectories dat, doc, include, src, sys\share, sys\windows, - win\tty, util. - - (You can check the file "Files" in your top level directory for a - more complete listing of what file is in which directory.) +/-----------------------------------------------------------------------\ +| Build and package NetHack for Windows yourself | +\----------------------------------------------------------- -----------/ - If you downloaded or ftp'd the sources from a UNIX system, the lines - will probably end in UNIX-style newlines, instead of the carriage - return and line feed pairs used by Windows. Some programs have - trouble with them, so you may need to convert them. The compiler - should not have any problems with them however. +We provide documentation for the follow three different approaches +and tool suites that may be used to compile and package +NetHack for Windows: -4. Edit your Makefile if you wish, but it is not required unless - you are altering the build options. + 1. With the Visual Studio Integrated Development Environment (IDE). + The steps are documented in sys/windows/build-vs.txt. -Compiling + 2. At the Windows command line, using Visual Studio's nmake.exe. + The steps are documented in sys/windows/build-nmake.txt. -5. Now that everything is set up... + 3. At the bash shell using MSYS2. + The steps are documented in sys/windows/build-msys2.txt. - Change your current directory to the NetHack src directory. +/--------------------------------------------------------------\ +| Executing NetHack from zip file | +\--------------------------------------------------------------/ - For the Visual Studio compiler: - Issue these commands: - copy ..\sys\windows\Makefile.nmake Makefile - nmake install +Executing NetHack on your machine - For GCC: - Issue these commands: - cp ../sys/windows/Makefile.mingw32* . - mingw32-make -f Makefile.mingw32 clean - mingw32-make -f Makefile.mingw32 depend - mingw32-make -f Makefile.mingw32 + Unzip the files to a folder on your machine, from + Windows explorer right-click | Extract all... + or by using tar: + Windows command prompt: + tar -xf nethack-370-win-x64.zip - If you get any errors along the way then something has not been set - up correctly. The time it takes to compile depends on your - particular machine of course, but you should be able to go for lunch - and return to find everything finished. The less memory, and slower - your machine, the longer the lunch you may take. :-) + MSYS2 bash shell: + /c/Windows/System32/tar -xf nethack-370-win-x64.zip - In any case, it is likely that the command prompt window where you - are doing the compiling will be occupied for a while. If all goes - well, you will get an NetHack executable. + Run NetHack.exe or NetHackW.exe from the folder where you unzipped. -Notes: + Your personal config file, if it doesn't exist, will be created + from a template file the first time you fire up NetHack. -1. To install an update of NetHack after changing something, change - your current directory to src and issue the appropriate command for - your compiler: + If you want to edit your actual personal config file you can + reference the file as follows: - For Microsoft compiler: - nmake + From Windows cmd command prompt: + %USERPROFILE%\nethack\.nethackrc - For GCC: - mingw32-make -f Makefile.mingw32 + From Windows powershell: + $env:USERPROFILE\nethack\.nethackrc - If you add, delete, or reorder monsters or objects, or you change - the format of saved level files, delete any save and bones files. - (Trying to use such files sometimes produces amusing confusions on - the game's part, but usually crashes.) + From MSYS2 bash shell: + $USERPROFILE/nethack/.nethackrc -2. Depending on the build and compiler and tools used above, the - executable produced by the TTY build is either: - - a 32-bit (x86) .exe file, - which should run on any recent Win32 environment. - or - - a 64-bit (x64) .exe file, - which should run on any 64-bit Windows O/S. - **Note**: saved games are NOT compatible between the 32-bit and the - 64-bit versions at this time. + If you want to see where NetHack expects to find the other files it + utilizes, you can issue the following command: + nethack --showpaths -xNetHack.exe is the tty plus curses version. xNetHackW.exe is the -windows graphical version. Play xNetHack. -PROBLEMS +/--------------------------------------------------------------\ +| If you experience a problem | +\--------------------------------------------------------------/ If you discover a bug and wish to report it, or if you have comments or suggestions we recommend using our "Contact Us" web page at: - http://www.nethack.org/common/contact.html + https://www.nethack.org/common/contact.html If you don't have access to the web, or you want to send us a patch to the NetHack source code feel free to drop us a line c/o: DevTeam (at) nethack.org - Happy NetHacking! + The development team also monitors the NetHack issues page on GitHub: + https://github.com/NetHack/NetHack/issues + + + Happy NetHacking! diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index 35f13c4d25..224f231d1c 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-2023 +# Copyright (c) NetHack Development Team 1993-2025 # #============================================================================== # Build Tools Environment @@ -8,9 +8,8 @@ # MS Visual Studio Visual C++ compiler # # Visual Studio Compilers Tested: -# - Microsoft Visual Studio 2017 Community Edition v 15.9.54 -# - Microsoft Visual Studio 2019 Community Edition v 16.11.26 -# - Microsoft Visual Studio 2022 Community Edition v 17.6.0 +# - Microsoft Visual Studio 2019 Community Edition v 16.11.42 +# - Microsoft Visual Studio 2022 Community Edition v 17.12.4 # #============================================================================== # This is used for building two distinct executables of xNetHack: @@ -29,14 +28,14 @@ # The default make target (so just typing 'nmake' is useful). # -default : install +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 # # @@ -50,6 +49,18 @@ GAMEDIR = ..\binary # Default game build directory # windsound uses built-in Microsoft API's, no 3rd party download is required. SOUND_LIBRARIES = windsound +#SOUND_LIBRARIES = windsound fmod + +#------------------------------------------------------------------------------ +# Curses options. +# +# Do you want console curses included? + +CURSES_CONSOLE = Y + +# Do you want graphical curses included? + +CURSES_GRAPHICAL = Y # #------------------------------------------------------------------------------ @@ -58,14 +69,20 @@ SOUND_LIBRARIES = windsound DEBUGINFO = Y -#------------------------------------------------------------------------------ # +#------------------------------------------------------------------------------ +# Do you want to include the address sanitizer? # + +#WANT_ASAN = Y + +#------------------------------------------------------------------------------ # Do you have a connection to the internet available that you want -# to utilize for obtaining prerequisite Lua source code and pdcurses source code? -# +# to utilize for obtaining prerequisite Lua source code and +# pdcursesmod source code? +# Default is now Y. Set it to N if that won't work for you. -INTERNET_AVAILABLE = N +INTERNET_AVAILABLE = Y #------------------------------------------------------------------------------ # @@ -78,13 +95,13 @@ GIT_AVAILABLE = N # You can override INTERNET_AVAILABLE and GIT_AVAILABLE on the nmake command # line by adding GIT=1 # for example: -# nmake GIT=1 install +# nmake GIT=1 package # #------------------------------------------------------------------------------ # This Makefile will attempt to auto-detect your selected target architecture -# based on Visual Studio command prompt configuration settins etc. +# based on Visual Studio command prompt configuration settings etc. # However, if you want to manually override generation of a -# 32-bit or 64-bit build target, you can uncomment the apppropriate +# 32-bit or 64-bit build target, you can uncomment the appropriate # TARGET_CPU line below. # @@ -103,13 +120,28 @@ 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=@ SKIP_NETHACKW = N !IFNDEF LUA_VERSION -LUAVER=5.4.4 +LUAVER=5.4.6 !ELSE LUAVER=$(LUA_VERSION) !ENDIF @@ -133,29 +165,89 @@ NETHACK_VERSION="9.0.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 - +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) #============================================================================== -# Sanity checks for prerequisite Lua and pdcurses +# 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 # LUA_MAY_PROCEED=N ADD_CURSES=N @@ -163,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 @@ -176,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" @@ -191,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: @@ -204,36 +315,75 @@ LUA_MAY_PROCEED=Y !ERROR Stopping because NetHack 3.7 requires Lua for its build. !ENDIF # LUA_MAY_PROCEED -# Now, pdcurses -PDCDIST=pdcursesmod -!IF "$(INTERNET_AVAILABLE)" == "Y" -!IF "$(GIT_AVAILABLE)" == "Y" -PDCURSES_TOP=$(SUBM)\$(PDCDIST) +# Now, pdcursesmod +!IF "$(CURSES_CONSOLE)" == "Y" +WANT_CURSES=Y +!ENDIF +!IF "$(CURSES_GRAPHICAL)" == "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" +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 -# Your Makefile settings do not allow pdcurses or pdcursesmod to be obtained by +! 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 # pdcurses sources available somewhere -!IF "$(ADD_CURSES)" == "Y" -!MESSAGE Your Makefile settings do not allow pdcurses or pdcursesmod to be -!MESSAGE obtained by git or by download, but a copy of pdcurses was +! 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" !MESSAGE NetHack 3.7 will be built without support for the curses window-port. @@ -310,29 +460,51 @@ DLBDEF = # Object file directories. # -OBJTTY_B = objtty -OBJGUI_B = objgui -OBJUTIL_B = objutil -OBJLUA_B = objlua -OBJPDC_B = objpdc - -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) +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_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)^\ +OTTY = $(OBJTTY) +OGUI = $(OBJGUI) +OUTL = $(OBJUTIL) +OLUA = $(OBJLUA) +OPDC = $(OBJPDC) +OPDCC = $(OBJPDCC) +OPDCG = $(OBJPDCG) -U = $(UTIL)^\ +U = $(UTIL) !IFDEF TEST_CROSSCOMPILE HOST=_host @@ -352,9 +524,13 @@ GITHASH = -DNETHACK_GIT_SHA=\"$(GIT_HASH)\" GITBRANCH = -DNETHACK_GIT_BRANCH=\"$(GIT_BRANCH)\" !ENDIF +!IFDEF GIT_PREFIX +GITPREFIX = -DNETHACK_GIT_PREFIX=\"$(GIT_PREFIX)\" +!ENDIF + #===============-================================================= # LUA library -# Official source for Lua is http://www.lua.org/ftp/lua-5.4.4.tar.gz +# Official source for Lua is http://www.lua.org/ftp/lua-5.4.6.tar.gz # # Source for the NetHack repository submodule in ../submodules/lua # is https://github.com/lua/lua.git @@ -363,7 +539,7 @@ GITBRANCH = -DNETHACK_GIT_BRANCH=\"$(GIT_BRANCH)\" # Location of LUA # # Original source needs to be obtained from: -# http://www.lua.org/ftp/lua-5.4.4.tar.gz +# http://www.lua.org/ftp/lua-5.4.6.tar.gz # # This build assumes that the LUA sources are located # at the specified location. If they are actually elsewhere @@ -373,12 +549,12 @@ GITBRANCH = -DNETHACK_GIT_BRANCH=\"$(GIT_BRANCH)\" # !IFNDEF LUAVER -LUAVER=5.4.4 +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 \ @@ -406,124 +582,169 @@ LUAOBJFILES = $(LUAOBJFILES) $(OLUA)lbitlib.o # and removes lbitlib.c !ENDIF #===============-================================================= -# PDCurses build macros +# PDCursesmod build macros +# Source for the NetHack repository submodule in +# ../submodules/pdcursesmod +# is https://github.com/Bill-Gray/PDCursesMod # Source for the NetHack repository submodule in # ../submodules/pdcurses -# or ../submodules/pdcursesmod # is https://github.com/wmcbrine/PDCurses.git -# or https://github.com/Bill-Gray/PDCursesMod #================================================================= + !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 -PDCDEP = $(PDCURSES_TOP)\curses.h - -PDCLIBOBJS = $(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)delch.o $(OPDC)deleteln.o \ - $(OPDC)getch.o $(OPDC)getstr.o $(OPDC)getyx.o $(OPDC)inch.o $(OPDC)inchstr.o \ - $(OPDC)initscr.o $(OPDC)inopts.o $(OPDC)insch.o $(OPDC)insstr.o $(OPDC)instr.o $(OPDC)kernel.o \ - $(OPDC)keyname.o $(OPDC)mouse.o $(OPDC)move.o $(OPDC)outopts.o $(OPDC)overlay.o $(OPDC)pad.o \ - $(OPDC)panel.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 $(OPDC)debug.o - -PDCOBJS = $(OPDC)pdcclip.o $(OPDC)pdcdisp.o $(OPDC)pdcgetsc.o $(OPDC)pdckbd.o $(OPDC)pdcscrn.o \ - $(OPDC)pdcsetsc.o $(OPDC)pdcutil.o - -PDCLIB = $(LIBDIR)\$(PDCDIST)-$(TARGET_CPU).lib - -PDCINCL = /I$(PDCURSES_TOP) /I$(PDCSRC) /I$(PDCWINCON) +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 \ + $(OPDC)delch.o $(OPDC)deleteln.o $(OPDC)getch.o $(OPDC)getstr.o \ + $(OPDC)getyx.o $(OPDC)inch.o $(OPDC)inchstr.o $(OPDC)initscr.o \ + $(OPDC)inopts.o $(OPDC)insch.o $(OPDC)insstr.o $(OPDC)instr.o \ + $(OPDC)kernel.o $(OPDC)keyname.o $(OPDC)mouse.o $(OPDC)move.o \ + $(OPDC)outopts.o $(OPDC)overlay.o $(OPDC)pad.o $(OPDC)panel.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 +PDCINCLGUI = /I$(PDCWINCON) +!ENDIF !ELSE -PDCLIB = -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)\sp_lev.h \ - $(INCL)\spell.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 cmd.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 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 \ - 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 sfstruct.c \ - shk.c shknam.c sit.c sounds.c \ - sp_lev.c spell.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)\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)\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)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)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)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)selvar.h \ + $(INCL)sym.h $(INCL)defsym.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 + +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)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)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 \ $(DAT)\abyssfill.lua \ $(DAT)\bigrm-12.lua $(DAT)\bigrm-13.lua \ $(DAT)\cocytusfill.lua \ @@ -544,8 +765,16 @@ LUA_FILES = $(DAT)\air.lua $(DAT)\Arc-fila.lua $(DAT)\Arc-filb.lua \ # end with one file per line; Lua files that exist in vanilla but are deleted in # xNetHack are deleted in-place without trying to maintain 3 files per line. +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 @@ -555,7 +784,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 @@ -570,30 +799,58 @@ TEXT_IO32 = $(OUTL)tilete32.o $(OUTL)tiletx32.o $(OUTL)drawing$(HOST).o \ GIFREADERS_HOST = $(OUTL)gifread.o $(OUTL)alloc$(HOST).o $(OUTL)panic$(HOST).o GIFREADERS32_HOST = $(OUTL)gifrd32.o $(OUTL)alloc$(HOST).o $(OUTL)panic$(HOST).o -WINDHDR = $(MSWSYS)\win10.h $(MSWSYS)\winos.h $(MSWSYS)\win32api.h +PPMWRITERS = $(OUTL)ppmwrite.o $(OUTL)alloc$(HOST).o $(OUTL)panic$(HOST).o + +WINDHDR = $(MSWSYS)win10.h $(MSWSYS)winos.h $(MSWSYS)win32api.h # # - Curses # !IF "$(ADD_CURSES)" == "Y" -CURSESDEF1=-D"CURSES_GRAPHICS" -CURSESDEF2=-D"CURSES_BRIEF_INCLUDE" -DCHTYPE_32 -CURSESOBJ= $(OTTY)cursdial.o $(OTTY)cursinit.o $(OTTY)cursinvt.o $(OTTY)cursmain.o \ - $(OTTY)cursmesg.o $(OTTY)cursmisc.o $(OTTY)cursstat.o $(OTTY)curswins.o +PDCURSESFLAGS = -DPDC_WIDE -DPDC_RGB +CURSESOBJ= $(OTTY)cursdial.o $(OTTY)cursinit.o $(OTTY)cursinvt.o \ + $(OTTY)cursmain.o $(OTTY)cursmesg.o $(OTTY)cursmisc.o \ + $(OTTY)cursstat.o $(OTTY)curswins.o +!IF "$(CURSES_CONSOLE)" == "Y" +CURSESWINCONOBJS = $(CURSESOBJ) +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 +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 +!UNDEF CURSESWINGUIOBJS +!UNDEF PDCWINCONLIB +!UNDEF PDCWINGUILIB !ENDIF +OUTLHACKLIBOBJS = $(OUTL)hacklib.o +OTTYHACKLIBOBJS = $(OTTY)hacklib.o +OGUIHACKLIBOBJS = $(OGUI)hacklib.o +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 @@ -603,45 +860,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 - -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)cmd.o $(OTTY)dbridge.o $(OTTY)decl.o $(OTTY)detect.o -VTTYOBJ04 = $(OTTY)dig.o $(OTTY)display.o $(OTTY)do.o $(OTTY)do_name.o -VTTYOBJ05 = $(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)hack.o -VTTYOBJ09 = $(OTTY)hacklib.o $(OTTY)insight.o $(OTTY)invent.o $(OTTY)isaac64.o -VTTYOBJ10 = $(OTTY)light.o $(OTTY)lock.o $(OTTY)mail.o $(OTTY)makemon.o -VTTYOBJ11 = $(OTTY)mcastu.o $(OTTY)mhitm.o $(OTTY)mhitu.o $(OTTY)minion.o -VTTYOBJ12 = $(OTTY)mklev.o $(OTTY)mkmap.o $(OTTY)mkmaze.o $(OTTY)mkobj.o -VTTYOBJ13 = $(OTTY)mkroom.o $(OTTY)mon.o $(OTTY)mondata.o $(OTTY)monmove.o -VTTYOBJ14 = $(OTTY)monst.o $(OTTY)mplayer.o $(OTTY)mthrowu.o $(OTTY)muse.o -VTTYOBJ15 = $(OTTY)music.o $(OTTY)o_init.o $(OTTY)objects.o $(OTTY)objnam.o -VTTYOBJ16 = $(OTTY)options.o $(OTTY)pager.o $(OTTY)pickup.o $(OTTY)pline.o -VTTYOBJ17 = $(OTTY)polyself.o $(OTTY)potion.o $(OTTY)pray.o $(OTTY)priest.o -VTTYOBJ18 = $(OTTY)quest.o $(OTTY)questpgr.o $(RANDOM) $(OTTY)read.o -VTTYOBJ19 = $(OTTY)rect.o $(OTTY)region.o $(OTTY)restore.o $(OTTY)rip.o -VTTYOBJ20 = $(OTTY)rnd.o $(OTTY)role.o $(OTTY)rumors.o $(OTTY)save.o -VTTYOBJ21 = $(OTTY)sfstruct.o $(OTTY)shk.o $(OTTY)shknam.o $(OTTY)sit.o -VTTYOBJ22 = $(OTTY)sounds.o $(OTTY)sp_lev.o $(OTTY)spell.o $(OTTY)steal.o -VTTYOBJ23 = $(OTTY)steed.o $(OTTY)symbols.o $(OTTY)sys.o $(OTTY)teleport.o -VTTYOBJ24 = $(OTTY)timeout.o $(OTTY)topten.o $(OTTY)track.o $(OTTY)trap.o -VTTYOBJ25 = $(OTTY)u_init.o $(OTTY)uhitm.o $(OTTY)utf8map.o $(OTTY)vault.o -VTTYOBJ26 = $(OTTY)vision.o $(OTTY)weapon.o $(OTTY)were.o $(OTTY)wield.o -VTTYOBJ27 = $(OTTY)windows.o $(OTTY)wizard.o $(OTTY)worm.o $(OTTY)worn.o -VTTYOBJ28 = $(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) +TTYOBJTTY = $(OTTY)topl.o $(OTTY)getline.o $(OTTY)wintty.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)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)sfstruct.o $(OTTY)shk.o $(OTTY)shknam.o $(OTTY)sit.o \ + $(OTTY)sounds.o $(OTTY)sp_lev.o $(OTTY)spell.o $(OTTY)stairs.o \ + $(OTTY)steal.o $(OTTY)steed.o $(OTTY)strutil.o $(OTTY)symbols.o \ + $(OTTY)sys.o $(OTTY)teleport.o $(OTTY)timeout.o $(OTTY)topten.o \ + $(OTTY)track.o $(OTTY)trap.o $(OTTY)u_init.o $(OTTY)uhitm.o \ + $(OTTY)utf8map.o $(OTTY)vault.o $(OTTY)vision.o $(OTTY)weapon.o \ + $(OTTY)were.o $(OTTY)wield.o $(OTTY)windows.o $(OTTY)wizard.o \ + $(OTTY)wizcmds.o $(OTTY)worm.o $(OTTY)worn.o $(OTTY)write.o \ + $(OTTY)zap.o + +OBJSTTY = $(MDLIBTTY) $(COREOBJTTY) $(REGEXTTY) $(RANDOMTTY) ALLOBJTTY = $(SOBJTTY) $(DLBOBJTTY) $(OBJSTTY) $(VVOBJTTY) $(LUAOBJTTY) @@ -649,15 +902,15 @@ ALLOBJTTY = $(SOBJTTY) $(DLBOBJTTY) $(OBJSTTY) $(VVOBJTTY) $(LUAOBJTTY) # - GUI # -GUIDEF= -DTILES -DMSWIN_GRAPHICS -DNOTTYGRAPHICS +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 @@ -672,43 +925,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)cmd.o $(OGUI)dbridge.o $(OGUI)decl.o $(OGUI)detect.o -VGUIOBJ04 = $(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)hack.o -VGUIOBJ09 = $(OGUI)hacklib.o $(OGUI)insight.o $(OGUI)invent.o $(OGUI)isaac64.o -VGUIOBJ10 = $(OGUI)light.o $(OGUI)lock.o $(OGUI)mail.o $(OGUI)makemon.o -VGUIOBJ11 = $(OGUI)mcastu.o $(OGUI)mhitm.o $(OGUI)mhitu.o $(OGUI)minion.o -VGUIOBJ12 = $(OGUI)mklev.o $(OGUI)mkmap.o $(OGUI)mkmaze.o $(OGUI)mkobj.o -VGUIOBJ13 = $(OGUI)mkroom.o $(OGUI)mon.o $(OGUI)mondata.o $(OGUI)monmove.o -VGUIOBJ14 = $(OGUI)monst.o $(OGUI)mplayer.o $(OGUI)mthrowu.o $(OGUI)muse.o -VGUIOBJ15 = $(OGUI)music.o $(OGUI)o_init.o $(OGUI)objects.o $(OGUI)objnam.o -VGUIOBJ16 = $(OGUI)options.o $(OGUI)pager.o $(OGUI)pickup.o $(OGUI)pline.o -VGUIOBJ17 = $(OGUI)polyself.o $(OGUI)potion.o $(OGUI)pray.o $(OGUI)priest.o -VGUIOBJ18 = $(OGUI)quest.o $(OGUI)questpgr.o $(RANDOMGUI) $(OGUI)read.o -VGUIOBJ19 = $(OGUI)rect.o $(OGUI)region.o $(OGUI)restore.o $(OGUI)rip.o -VGUIOBJ20 = $(OGUI)rnd.o $(OGUI)role.o $(OGUI)rumors.o $(OGUI)save.o -VGUIOBJ21 = $(OGUI)sfstruct.o $(OGUI)shk.o $(OGUI)shknam.o $(OGUI)sit.o -VGUIOBJ22 = $(OGUI)sounds.o $(OGUI)sp_lev.o $(OGUI)spell.o $(OGUI)steal.o -VGUIOBJ23 = $(OGUI)steed.o $(OGUI)symbols.o $(OGUI)sys.o $(OGUI)teleport.o -VGUIOBJ24 = $(OGUI)timeout.o $(OGUI)topten.o $(OGUI)track.o $(OGUI)trap.o -VGUIOBJ25 = $(OGUI)u_init.o $(OGUI)uhitm.o $(OGUI)utf8map.o $(OGUI)vault.o -VGUIOBJ26 = $(OGUI)vision.o $(OGUI)weapon.o $(OGUI)were.o $(OGUI)wield.o -VGUIOBJ27 = $(OGUI)windows.o $(OGUI)wizard.o $(OGUI)worm.o $(OGUI)worn.o -VGUIOBJ28 = $(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) +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)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)sfstruct.o $(OGUI)shk.o $(OGUI)shknam.o $(OGUI)sit.o \ + $(OGUI)sounds.o $(OGUI)sp_lev.o $(OGUI)spell.o $(OGUI)stairs.o \ + $(OGUI)steal.o $(OGUI)steed.o $(OGUI)strutil.o $(OGUI)symbols.o \ + $(OGUI)sys.o $(OGUI)teleport.o $(OGUI)timeout.o $(OGUI)topten.o \ + $(OGUI)track.o $(OGUI)trap.o $(OGUI)u_init.o $(OGUI)uhitm.o \ + $(OGUI)utf8map.o $(OGUI)vault.o $(OGUI)vision.o $(OGUI)weapon.o \ + $(OGUI)were.o $(OGUI)wield.o $(OGUI)windows.o $(OGUI)wizard.o \ + $(OGUI)wizcmds.o $(OGUI)worm.o $(OGUI)worn.o $(OGUI)write.o \ + $(OGUI)zap.o + +OBJSGUI = $(MDLIBGUI) $(COREOBJGUI) $(REGEXGUI) $(RANDOMGUI) ALLOBJGUI = $(SOBJGUI) $(DLBOBJGUI) $(OBJSGUI) $(VVOBJGUI) $(LUAOBJGUI) @@ -717,7 +966,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 @@ -739,7 +988,8 @@ 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 @@ -748,9 +998,11 @@ GUISOUNDOBJS = $(GUISOUNDOBJS) $(OGUI)windsound.o #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= @@ -767,47 +1019,51 @@ HAVE_SOUNDLIB=Y NEED_USERSOUNDS=Y NEED_SEAUTOMAP=Y NEED_WAV=Y -FMODROOT=..\lib\fmod\api\core -SOUNDLIBINCL = $(SOUNDLIBINCL) -I$(FMODLIBROOT)/inc +R_FMODROOT=..\lib\fmod\api\core +FMODROOT=$(R_FMODROOT)$(BACKSLASH) +U_FMODROOT=$(FMODROOT:\=/) +FMODDLLBASENAME = fmod.dll +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 = fmod_vc.dll -!IF "$(TARGET_CPU)" == "x86" -TTYSOUNDLIBS = $(TTYSOUNDLIBS) $(FMODLIBDIR)\fmod_vc.lib -GUISOUNDLIBS = $(GUISOUNDLIBS) $(FMODLIBDIR)\fmod_vc.lib -#!ELSE -#TTYSOUNDLIBS = $(TTYSOUNDLIBS) $(FMODLIBDIR)\fmod_vc.lib -#GUISOUNDLIBS = $(GUISOUNDLIBS) $(FMODLIBDIR)\fmod_vc.lib -!ENDIF -GAMEDIRDLLS = $(GAMEDIRDLLS) $(GAMEDIR)\$(FMODLIBDLL) -FMODDIR = ..\sound\fmod -SOUNDSRCS = $(SOUNDSRCS) $(FMODDIR)\fmod.c +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 +GAMEDIRDLLS = $(GAMEDIRDLLS) $(GAMEDIR)\$(FMODDLLBASENAME) +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 dll : $(FMODLIBDIR)\$(FMODLIBDLL) +!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("$(FMODLIBDIR)\$(FMODLIBDLL)") -FMOD_MISSING= $(FMOD_MISSING) $(FMODLIBDIR)\$(FMODLIBDLL) -!MESSAGE Error: missing $(FMODLIBDIR)\$(FMODLIBDLL) +!IF !EXIST("$(FMODLIBDLL)") +FMOD_MISSING= $(FMOD_MISSING) $(FMODLIBDLL) +!MESSAGE Error: missing $(FMODLIBDLL) !ENDIF !IF "$(FMOD_MISSING)" != "" !ERROR Error: Cannot proceed with fmod integration included @@ -819,38 +1075,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" @@ -867,37 +1123,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 - -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 - -TILE_H = ..\win\share\tile.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 +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)winprocs.h $(INCL)wintype.h $(INCL)you.h \ + $(INCL)youprop.h + +TILE_H = $(WSHR)tile.h #========================================== # Miscellaneous #========================================== -DATABASE = $(DAT)\data.base +DATABASE = $(DAT)data.base #========================================== #========================================== @@ -910,17 +1169,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=..\..\..\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 @@ -933,17 +1195,14 @@ rc=Rc.exe # is too old or untested. # # Recently tested versions: -TESTEDVS2017 = 14.16.27049.0 -TESTEDVS2019 = 14.29.30148.0 -TESTEDVS2022 = 14.36.32532.0 -VS2017CUR = $(TESTEDVS2017:.=) +TESTEDVS2019 = 14.29.30157.0 +TESTEDVS2022 = 14.42.34436.0 + VS2019CUR = $(TESTEDVS2019:.=) VS2022CUR = $(TESTEDVS2022:.=) -VS2017UP1 = $(VS2017CUR) + 1 VS2019UP1 = $(VS2019CUR) + 1 VS2022UP1 = $(VS2022CUR) + 1 -VS20171ST = 1411000000 -VS20191ST = $(VS2017UP1) +VS20191ST = 1419300000 VS20221ST = $(VS2019UP1) #!MESSAGE $(MAKEFLAGS) @@ -957,17 +1216,9 @@ MAKEVERSION=$(MAKEVERSION: =) VSSPECIAL= VSNEWEST=2022 -!IF ($(MAKEVERSION) < 1000000000) +!IF ($(MAKEVERSION) < 1411000000) VSVER=0000 #untested ancient version -!ELSEIF ($(MAKEVERSION) > 1000000000) && ($(MAKEVERSION) < 1100000000) -VSVER=2010 -!ELSEIF ($(MAKEVERSION) > 1100000000) && ($(MAKEVERSION) < 1200000000) -VSVER=2012 -!ELSEIF ($(MAKEVERSION) > 1200000000) && ($(MAKEVERSION) < 1400000000) -VSVER=2013 -!ELSEIF ($(MAKEVERSION) > 1400000000) && ($(MAKEVERSION) < 1411000000) -VSVER=2015 -!ELSEIF ($(MAKEVERSION) > $(VS20171ST)) && ($(MAKEVERSION) < $(VS2017UP1)) +!ELSEIF ($(MAKEVERSION) < $(VS20191ST)) VSVER=2017 !ELSEIF ($(MAKEVERSION) > $(VS20191ST)) && ($(MAKEVERSION) < $(VS2019UP1)) VSVER=2019 @@ -979,9 +1230,13 @@ VSVER=2999 #untested future version !IF ($(VSVER) >= 2012) !IF ($(VSVER) <= $(VSNEWEST)) +!IF ($(VSVER) == 2017) +!MESSAGE Autodetected Visual Studio $(VSVER) which we stopped testing with in March 2024 +!ELSE !MESSAGE Autodetected Visual Studio $(VSVER) $(VSSPECIAL) !ENDIF !ENDIF +!ENDIF !IF ($(VSVER) == 2999) !MESSAGE The NMAKE version of this Visual Studio $(_NMAKE_VER) is newer than the !MESSAGE most recent at the time this Makefile was crafted (Visual Studio $(VSNEWEST)). @@ -1008,7 +1263,7 @@ CL_RECENT=-sdl ! ENDIF !ENDIF -!IF ($(VSVER) >= 2019) +!IF ($(VSVER) >= 2019) && ("$(WANT_ASAN)"=="Y") ASAN=/fsanitize=address !ELSE ASAN= @@ -1063,10 +1318,18 @@ scall = # 4777 format string requires an argument of type 'type', # but variadic argument 'position' has type 'type' # 4820 padding in struct +# 5262 enable fallthrough warnings that lack [[fallthrough]] +# ctmpflags = $(ctmpflags:-W3=-W4) -wd4100 -wd4244 -wd4245 -wd4310 -wd4706 -w44777 -wd4820 !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 /std:clatest +!ENDIF +!ENDIF !ENDIF #More verbose warning output options below @@ -1080,24 +1343,22 @@ cpptmpflags = $(ctmpflags) # declarations for use on Intel x86 systems !IF "$(TARGET_CPU)" == "x86" DLLENTRY = @12 -EXEVER=5.01 MACHINE=/MACHINE:X86 !ENDIF # declarations for use on AMD64 systems !IF "$(TARGET_CPU)" == "x64" DLLENTRY = -EXEVER=5.02 MACHINE=/MACHINE:X64 !ENDIF # for Windows applications -conlflags = $(lflags) -subsystem:console,$(EXEVER) -guilflags = $(lflags) -subsystem:windows,$(EXEVER) +conlflags = $(lflags) -subsystem:console +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 + ole32.lib Shell32.lib dbghelp.lib winlibs = $(baselibs) user32.lib comdlg32.lib winspool.lib # for Windows applications that use the C Run-Time libraries @@ -1105,21 +1366,21 @@ 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) #========================================== # - Game build #========================================== -LIBS= user32.lib winmm.lib $(ZLIB) $(CURSESLIB) +LIBS= user32.lib winmm.lib $(ZLIB) ! IF ("$(USE_DLB)"=="Y") DLB = nhdat$(NHV) @@ -1137,173 +1398,192 @@ 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: $(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) -DPDC_NCMOUSE $(PDCINCL) $(CFLAGS) $(CURSESDEF1) $(CURSESDEF2) $(TTYDEF) $(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) $(CURSESDEF1) $(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= ) $(CURSESDEF1) $(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$@ $< + +!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 -{$(PDCWINCON)}.c{$(OBJPDC)}.o: - $(Q)$(CC) /wd4244 /wd4267 /wd4774 $(PDCINCL) $(CFLAGS) $(CURSESDEF1) $(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) -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) -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 ================== @@ -1316,51 +1596,51 @@ DLB = # Everything # -all : install - -install: envchk.tag libdir.tag ottydir.tag outldir.tag \ - oguidir.tag oluadir.tag opdcdir.tag\ - $(LUASRC)\lua.h $(PDCDEP) \ - $(INCL)\nhlua.h $(OUTL)utility.tag \ - $(DAT)\data $(DAT)\rumors $(DAT)\oracles $(DAT)\engrave \ - $(DAT)\epitaph $(DAT)\bogusmon $(GAMEDIR)\xNetHack.exe \ - $(GAMEDIR)\xNetHackW.exe $(GAMEDIRDLLS) install.tag - @echo Done. +all : package !IF "$(INTERNET_AVAILABLE)" == "Y" !IF "$(GIT_AVAILABLE)" == "Y" -$(LUASRC)\lua.h: - git submodule init ../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 --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.4.tar.gz -CURLLUADST=lua-5.4.4.tar.gz +CURLLUASRC=http://www.lua.org/ftp/lua-5.4.6.tar.gz +CURLLUADST=lua-5.4.6.tar.gz #CURLPDCSRC=https://github.com/wmcbrine/PDCurses/archive/refs/tags/3.9.zip -CURLPDCSRC=https://github.com/Bill-Gray/PDCursesMod/archive/refs/tags/v4.3.5.zip -CURLPDCDST=pdcurses.zip +CURLPDCSRC=https://github.com/Bill-Gray/PDCursesMod/archive/refs/tags/v4.4.0.zip +CURLPDCDST=$(PDCDIST).zip -$(LUASRC)\lua.h: - 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: + 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 #========================================== @@ -1385,7 +1665,7 @@ $(PDCURSES_TOP)\curses.h: # occurrence of string1 with string2 in the # macro mymacro. Special ascii key codes may be # used in the substitution text by preceding it -# with ^ as we have done below. Every occurence +# with ^ as we have done below. Every occurrence # of a in $(ALLOBJ) is replaced by # <+>. @@ -1403,43 +1683,45 @@ GAMEOBJ=$(GAMEOBJ:^ =^ #-------- # -$(GAMEDIR)\xNetHack.exe : gamedir.tag $(OTTY)consoletty.o \ - $(ALLOBJTTY) $(CURSESOBJ) $(TTYSOUNDOBJS) $(OTTY)date.o $(OTTY)console.res \ - $(LUALIB) $(PDCLIB) $(TTYOBJ) - @if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR) +$(GAMEDIR)xNetHack.exe : gamedir.tag $(OTTY)consoletty.o \ + $(ALLOBJTTY) $(CURSESWINCONOBJS) \ + $(TTYSOUNDOBJS) $(OTTY)date.o $(OTTY)console.res \ + $(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 \ - $(LIBS) $(PDCLIB) $(LUALIB) $(TTYSOUNDLIBS) \ + $(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) - $(TTYSOUNDOBJS) - $(CURSESOBJ) + $(TTYOBJTTY) + $(TTYSOUNDOBJS) $(CURSESWINCONOBJS) $(PDCWINCONOBJS) $(OTTY)consoletty.o $(OTTY)date.o $(OTTY)console.res << keep - @if exist install.tag del install.tag + @if exist binary.tag del binary.tag #--------- # xNetHackW #--------- # -$(GAMEDIR)\xNetHackW.exe : gamedir.tag $(OGUI)tile.o \ +$(GAMEDIR)xNetHackW.exe : gamedir.tag $(OGUI)tile.o \ $(ALLOBJGUI) $(GAMEOBJGUI) $(GUIOBJ) $(GUISOUNDOBJS) \ - $(OGUI)date.o \ + $(CURSESWINGUIOBJS) $(OGUI)date.o \ $(OGUI)xNetHackW.res \ - $(LUALIB) - @if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR) + $(LUALIB) $(PDCWINGUILIB) $(PDCWINGUIOBJS) $(OGUIHACKLIB) + @if not exist $(GAMEDIR)*.* mkdir $(R_GAMEDIR) @echo Linking $(@:\=/) - $(link) $(LFLAGS) $(guilflags) /STACK:2048 /PDB:$(GAMEDIR)\$(@B).PDB \ - /MAP:$(OGUI)$(@B).MAP $(LIBS) $(LUALIB) $(GUISOUNDLIBS) \ + $(link) $(LFLAGS) $(guilflags) /STACK:2048 /PDB:$(GAMEDIR)$(@B).PDB \ + /MAP:$(OGUI)$(@B).MAP \ + $(LIBS) $(PDCWINGUILIB) $(LUALIB) \ + $(GUISOUNDLIBS) $(OGUIHACKLIB) \ $(guilibs) $(COMCTRL) $(BCRYPT) -out:$@ @<<$(@B).lnk $(GAMEOBJGUI) $(ALLOBJGUI) $(GUIOBJ) - $(GUISOUNDOBJS) + $(GUISOUNDOBJS) $(CURSESWINGUIOBJS) $(PDCWINGUIOBJS) $(OGUI)tile.o $(OGUI)date.o $(OGUI)xNetHackW.res @@ -1449,58 +1731,58 @@ $(GAMEDIR)\xNetHackW.exe : gamedir.tag $(OGUI)tile.o \ # install #-------- # -install.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 $(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)xnethackrc.template copy $(MSWSYS)xnethackrc.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)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 /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)\.xnethackrc.template copy $(MSWSYS)\.xnethackrc.template $(GAMEDIR) - -if not exist $(GAMEDIR)\record. goto>$(GAMEDIR)\record. - echo install done > $@ - -# copy $(MSWSYS)\windsyshlp $(GAMEDIR) - -recover: $(U)recover.exe + echo binary built > $@ + +# copy $(MSWSYS)windsyshlp $(GAMEDIR) + +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 $(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; >> $@ @@ -1511,16 +1793,16 @@ $(INCL)\nhlua.h: tileutil: $(U)gif2txt.exe $(U)gif2tx32.exe $(U)txt2ppm.exe @echo Optional tile development utilities are up to date. -$(OGUI)xNetHackW.res: $(SRC)\tiles.bmp $(MSWIN)\xNetHackW.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 $(WAV) +$(OGUI)xNetHackW.res: $(SRC)tiles.bmp $(MSWIN)xNetHackW.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)xNetHackW.exe.manifest $(WAV) @echo Building resource file $@ from $** - $(rc) -nologo -r -fo$@ -i$(MSWIN) -i$(SndWavDir) -dNDEBUG $(RCFLAGS) $(MSWIN)\xNetHackW.rc + $(rc) -nologo -r -fo$@ -i$(MSWIN) -i$(SndWavDir) -dNDEBUG -dVIA_MAKE $(RCFLAGS) $(MSWIN)xNetHackW.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. @@ -1536,22 +1818,22 @@ $(U)nhsizes3.exe: $(OUTL)nhsizes3.o $(OUTL)nhsizes3.o: $(CONFIG_H) nhsizes3.c $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) -Fo$@ nhsizes3.c -$(U)makedefs.exe: $(MAKEDEFSOBJS) +$(U)makedefs.exe: $(OUTLHACKLIB) $(MAKEDEFSOBJS) @echo Linking $(@:\=/) - @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" -out:$@ $(MAKEDEFSOBJS) - -$(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) + @$(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 $(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 # -# This is awful, but it allows the GITHASH and GITBRANCH macros to be +# This is awful, but it allows the GITHASH, GITBRANCH and GITPREFIX macros to be # defined and utilized, using only build-in Windows and nmake commands, # as well as the necessary git commands. # @@ -1562,6 +1844,7 @@ $(OTTY)date.o: $(HACKINCL) $(HACKSRC) $(HACKOBJ) $(ALLOBJTTY) $(CURSESOBJ) !IF "$(GIT_AVAILABLE)" == "1" @git rev-parse --verify HEAD 2>&1 >$(OTTY)date1.tmp @git rev-parse --abbrev-ref HEAD 2>&1 >$(OTTY)date2.tmp + @git config nethack.substprefix 2>&1 >$(OTTY)date3.tmp @echo.>date.nmk @echo OBJ = $(OBJTTY)>>date.nmk @echo O = ^$(OBJTTY)^^^\>>date.nmk @@ -1569,6 +1852,7 @@ $(OTTY)date.o: $(HACKINCL) $(HACKSRC) $(HACKOBJ) $(ALLOBJTTY) $(CURSESOBJ) @echo CFLAGS = $(CFLAGS) ^\>>date.nmk @for /F "usebackq" %%A IN ("$(OTTY)date1.tmp") DO @echo. -DNETHACK_GIT_SHA=\"%%A\" ^\>>date.nmk @for /F "usebackq" %%A IN ("$(OTTY)date2.tmp") DO @echo. -DNETHACK_GIT_BRANCH=\"%%A\">>date.nmk + @for /F "usebackq" %%A IN ("$(OTTY)date3.tmp") DO @echo. -DNETHACK_GIT_PREFIX=\"%%A\">>date.nmk @echo.>>date.nmk @echo default: ^$(OTTY)date.o>>date.nmk @echo.>>date.nmk @@ -1582,6 +1866,7 @@ $(OGUI)date.o: $(HACKINCL) $(HACKSRC) $(HACKOBJ) $(ALLOBJGUI) !IF "$(GIT_AVAILABLE)" == "1" @git rev-parse --verify HEAD 2>&1 >$(OGUI)date1.tmp @git rev-parse --abbrev-ref HEAD 2>&1 >$(OGUI)date2.tmp + @git config nethack.substprefix 2>&1 >$(OTTY)date3.tmp @echo.>date.nmk @echo OBJ = $(OBJGUI)>>date.nmk @echo O = ^$(OBJGUI)^^^\>>date.nmk @@ -1589,6 +1874,7 @@ $(OGUI)date.o: $(HACKINCL) $(HACKSRC) $(HACKOBJ) $(ALLOBJGUI) @echo CFLAGS = $(CFLAGS) ^\>>date.nmk @for /F "usebackq" %%A IN ("$(OGUI)date1.tmp") DO @echo. -DNETHACK_GIT_SHA=\"%%A\" ^\>>date.nmk @for /F "usebackq" %%A IN ("$(OGUI)date2.tmp") DO @echo. -DNETHACK_GIT_BRANCH=\"%%A\">>date.nmk + @for /F "usebackq" %%A IN ("$(OTTY)date3.tmp") DO @echo. -DNETHACK_GIT_PREFIX=\"%%A\">>date.nmk @echo.>>date.nmk @echo default: ^$(OGUI)date.o>>date.nmk @echo.>>date.nmk @@ -1603,40 +1889,59 @@ $(OGUI)date.o: $(HACKINCL) $(HACKSRC) $(HACKOBJ) $(ALLOBJGUI) # 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 + #========================================== -# uudecode utility and uuencoded targets +# HACKLIB #========================================== -$(U)uudecode.exe: $(OUTL)uudecode.o - @echo Linking $(@:\=/) - @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" -out:$@ $(OUTL)uudecode.o +$(OUTLHACKLIB): $(OUTLHACKLIBOBJS) + @echo Building library $@ from $** + @$(librarian) /OUT:$@ $(OUTLHACKLIBOBJS) -$(OUTL)uudecode.o: $(SSYS)\uudecode.c - $(Q)$(CC) $(CFLAGS) -wd4702 $(TTYDEF) $(CROSSCOMPILE) /D_CRT_SECURE_NO_DEPRECATE -Fo$@ $(SSYS)\uudecode.c +$(OTTYHACKLIB): $(OTTYHACKLIBOBJS) + @echo Building library $@ from $** + @$(librarian) /OUT:$@ $(OTTYHACKLIBOBJS) -$(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 +$(OGUIHACKLIB): $(OGUIHACKLIBOBJS) + @echo Building library $@ from $** + @$(librarian) /OUT:$@ $(OGUIHACKLIBOBJS) -$(MSWIN)\NetHack.ico: $(U)uudecode.exe $(MSWSYS)\nhico.uu - $(U)uudecode.exe $(MSWSYS)\nhico.uu +#========================================== +# uudecode utility and uuencoded targets +#========================================== + +$(U)uudecode.exe: $(OUTL)uudecode.o + @echo Linking $(@:\=/) + @$(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 + +$(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 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 $@ #================================================= @@ -1644,38 +1949,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.tag: - @if not exist $(OBJTTY)\*.* echo creating directory $(OBJTTY:\=/) - @if not exist $(OBJTTY)\*.* mkdir $(OBJTTY) +ottydir$(TARGET_CPU).tag: + @if not exist $(OBJTTY)*.* echo creating directory $(OBJTTY:\=/) + @if not exist $(OBJTTY)*.* mkdir $(R_OBJTTY) @echo directory created >$@ -oguidir.tag: - @if not exist $(OBJGUI)\*.* echo creating directory $(OBJGUI:\=/) - @if not exist $(OBJGUI)\*.* mkdir $(OBJGUI) +oguidir$(TARGET_CPU).tag: + @if not exist $(OBJGUI)*.* echo creating directory $(OBJGUI:\=/) + @if not exist $(OBJGUI)*.* mkdir $(R_OBJGUI) @echo directory created >$@ -outldir.tag: - @if not exist $(OBJUTIL)\*.* echo creating directory $(OBJUTIL:\=/) - @if not exist $(OBJUTIL)\*.* mkdir $(OBJUTIL) +outldir$(TARGET_CPU).tag: + @if not exist $(OBJUTIL)*.* echo creating directory $(OBJUTIL:\=/) + @if not exist $(OBJUTIL)*.* mkdir $(R_OBJUTIL) @echo directory created >$@ -oluadir.tag: - @if not exist $(OBJLUA)\*.* echo creating directory $(OBJLUA:\=/) - @if not exist $(OBJLUA)\*.* mkdir $(OBJLUA) +oluadir$(TARGET_CPU).tag: + @if not exist $(OBJLUA)*.* echo creating directory $(OBJLUA:\=/) + @if not exist $(OBJLUA)*.* mkdir $(R_OBJLUA) @echo directory created >$@ -opdcdir.tag: - @if not exist $(OBJPDC)\*.* echo creating directory $(OBJPDC:\=/) - @if not exist $(OBJPDC)\*.* mkdir $(OBJPDC) +opdcdir$(TARGET_CPU).tag: + @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 $(R_OBJPDCC) + @echo directory created >$@ + +opdcgdir$(TARGET_CPU).tag: + @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 $(R_PkgDir) @echo directory created >$@ #========================================== @@ -1685,10 +2005,13 @@ libdir.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)"!="" @@ -1714,17 +2037,18 @@ 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 @@ -1734,16 +2058,17 @@ fetch-pdcurses: 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 #========================================== -$(U)dlb.exe: $(DLBOBJ_HOST) $(OUTL)dlb$(HOST).o +$(U)dlb.exe: $(DLBOBJ_HOST) $(OUTL)dlb$(HOST).o $(OUTLHACKLIB) @echo Linking $(@:\=/) - @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" -out:$@ @<<$(@B).lnk + @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" $(OUTLHACKLIB) -out:$@ @<<$(@B).lnk $(OUTL)dlb_main$(HOST).o $(OUTL)dlb$(HOST).o $(OUTL)alloc$(HOST).o @@ -1751,35 +2076,36 @@ $(U)dlb.exe: $(DLBOBJ_HOST) $(OUTL)dlb$(HOST).o << !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 @@ -1802,57 +2128,60 @@ 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 #========================================== # Tile Mapping #========================================== -$(SRC)\tile.c: $(U)tilemap.exe +tile.c: $(U)tilemap.exe @$(U)tilemap @echo A new $(@:\=/) has been created -$(U)tilemap.exe: $(OUTL)tilemap.o $(OUTL)monst.o $(OUTL)objects.o $(OUTL)drawing.o +$(U)tilemap.exe: $(OUTL)tilemap.o $(OUTL)monst.o $(OUTL)objects.o $(OUTL)drawing.o $(OUTLHACKLIB) @echo Linking $(@:\=/) - @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" -out:$@ \ - $(OUTL)tilemap.o $(OUTL)monst.o $(OUTL)objects.o $(OUTL)drawing.o + @$(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 @@ -1885,31 +2214,33 @@ $(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" -out:$@ @<<$(@B).lnk + @$(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) @echo Linking $(@:\=/) - @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" -out:$@ @<<$(@B).lnk + @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" $(OUTLHACKLIB) -out:$@ @<<$(@B).lnk $(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 @@ -1925,27 +2256,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 #=============================================================================== - -$(LIBDIR)\$(PDCDIST)-$(TARGET_CPU).lib : $(PDCLIBOBJS) $(PDCOBJS) +!IF "$(ADD_CURSES)" == "Y" +!IF "$(CURSES_CONSOLE)" == "Y" +$(PDCWINCONLIB) : $(PDCCOMMONOBJS) $(PDCWINCONOBJS) @echo Building library $@ from $** - @$(librarian) -nologo /out:$@ $(PDCLIBOBJS) $(PDCOBJS) - -#$(OPDC)pdcscrn.o : $(PDCURSES_HEADERS) $(PDCWINCON)\pdcscrn.c -# $(Q)$(CC) $(PDCINCL) $(CFLAGS) $(CURSESDEF1) $(CURSESDEF2) -wd4996 $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) \ -# -Fo$@ $(PDCWINCON)\pdcscrn.c + @$(librarian) -nologo /out:$@ $(PDCCOMMONOBJS) $(PDCWINCONOBJS) +!ENDIF +!IF "$(CURSES_GRAPHICAL)" == "Y" +$(PDCWINGUILIB) : $(PDCCOMMONOBJS) $(PDCWINGUIOBJS) + @echo Building library $@ from $** + @$(librarian) -nologo /out:$@ $(PDCCOMMONOBJS) $(PDCWINGUIOBJS) +!ENDIF +!ENDIF +$(OGUI)guitty.o: $(MSWSYS)guitty.c $(WINDHDR) $(HACK_H) $(TILE_H) #=============================================================================== # LUA @@ -1959,91 +2295,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)\FMODLIBDLL: $(FMODLIBDIR)\$(FMODLIBDLL) - copy $(FMODLIBDIR)\$(FMODLIBDLL) $@ +$(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) - -$(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) +$(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: $(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)xNetHackW.o: $(MSWIN)\xNetHackW.c $(ALL_GUIHDR) $(MSWIN)\resource.h $(WINDHDR) $(HACK_H) +$(OGUI)mswproc.o: $(MSWIN)mswproc.c $(ALL_GUIHDR) $(MSWIN)resource.h $(WINDHDR) $(HACK_H) +$(OGUI)xNetHackW.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 @@ -2060,13 +2396,13 @@ $(OGUI)xNetHackW.o: $(MSWIN)\xNetHackW.c $(ALL_GUIHDR) $(MSWIN)\resource.h $(WIN # !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 @@ -2087,28 +2423,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 @@ -2126,180 +2462,237 @@ $(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 #=============================================================================== # 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 xNetHack.exe NetHack.txt \ + NetHackW.exe opthelp nhdat370 record symbols sysconf.template +FILESTOZIP = $(BinDir)nethackrc.template $(BinDir)Guidebook.txt $(BinDir)license \ + $(BinDir)xNetHack.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 + +package: binary $(FILESTOZIP) $(MAINZIP) $(DBGSYMZIP) + @echo NetHack Windows package created: $(MAINZIP) + +$(MAINZIP): $(FILESTOZIP) + if not exist $(PkgDir)*.* mkdir $(R_PkgDir) + tar -a -cf $(MAINZIP) -C $(R_BinDir) $(PKGFILES) +$(DBGSYMZIP): $(PDBTOZIP) + 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)xNetHack.exe \ + $(GAMEDIR)NetHackW.exe $(GAMEDIRDLLS) binary.tag + @echo NetHack is up to date. #=============================================================================== # Housekeeping #=============================================================================== spotless: clean - if exist $(GAMEDIR)\xNetHack.exe del $(GAMEDIR)\xNetHack.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)xNetHack.exe del $(GAMEDIR)xNetHack.exe + if exist $(GAMEDIR)xNetHackW.exe del $(GAMEDIR)xNetHackW.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 outldir.tag del outldir.tag - if exist ottydir.tag del ottydir.tag - if exist oguidir.tag del oguidir.tag - if exist oluadir.tag del oluadir.tag - if exist opdcdir.tag del opdcdir.tag + if exist outldirx86.tag del outldirx86.tag + if exist ottydirx86.tag del ottydirx86.tag + if exist oguidirx86.tag del oguidirx86.tag + if exist oluadirx86.tag del oluadirx86.tag + if exist opdcdirx86.tag del opdcdirx86.tag + if exist opdccdirx86.tag del opdccdirx86.tag + if exist opdcgdirx86.tag del opdcgdirx86.tag + if exist outldirx64.tag del outldirx64.tag + if exist ottydirx64.tag del ottydirx64.tag + if exist oguidirx64.tag del oguidirx64.tag + if exist oluadirx64.tag del oluadirx64.tag + if exist opdcdirx64.tag del opdcdirx64.tag + if exist opdccdirx64.tag del opdccdirx64.tag + if exist opdcgdirx64.tag del opdcgdirx64.tag if exist 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 $(PDCLIB) del $(PDCLIB) + 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 $(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 $(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 $(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 clean: - if exist install.tag del install.tag + if exist binary.tag del binary.tag if exist gamedir.tag del gamedir.tag if exist envchk.tag del envchk.tag if exist cpu.tag del cpu.tag if exist envchk.tag del envchk.tag + if exist pkgdir.tag del pkgdir.tag if exist $(OUTL)utility.tag del $(OUTL)utility.tag if exist $(OTTY)sp_lev.tag del $(OTTY)sp_lev.tag if exist $(OGUI)sp_lev.tag del $(OGUI)sp_lev.tag - if exist $(SRC)\tile.c del $(SRC)\tile.c - if exist $(INCL)\nhlua.h del $(INCL)\nhlua.h + if exist $(SRC)tile.c del $(SRC)tile.c + if exist $(INCL)nhlua.h del $(INCL)nhlua.h if exist $(U)makedefs.exe del $(U)makedefs.exe if exist $(U)dlb_main.exe del $(U)dlb_main.exe if exist $(U)tile2bmp.exe del $(U)tile2bmp.exe if exist $(U)tilemap.exe del $(U)tilemap.exe - if exist $(SRC)\*.lnk del $(SRC)\*.lnk - if exist $(DAT)\dlb.lst del $(DAT)\dlb.lst + if exist $(SRC)*.lnk del $(SRC)*.lnk + if exist $(DAT)dlb.lst del $(DAT)dlb.lst if exist $(OUTL)*.o del $(OUTL)*.o if exist $(OTTY)*.o del $(OTTY)*.o if exist $(OGUI)*.o del $(OGUI)*.o if exist $(OLUA)*.o del $(OLUA)*.o if exist $(OPDC)*.o del $(OPDC)*.o + if exist $(OPDCC)*.o del $(OPDCC)*.o + if exist $(OPDCG)*.o del $(OPDCG)*.o if exist $(OUTL)*.PDB del $(OUTL)*.PDB if exist $(OTTY)*.PDB del $(OTTY)*.PDB if exist $(OGUI)*.PDB del $(OGUI)*.PDB if exist $(OLUA)*.PDB del $(OLUA)*.PDB if exist $(OPDC)*.PDB del $(OPDC)*.PDB + if exist $(OPDCC)*.PDB del $(OPDCC)*.PDB + if exist $(OPDCG)*.PDB del $(OPDCG)*.PDB if exist $(OUTL)*.MAP del $(OUTL)*.MAP if exist $(OTTY)*.MAP del $(OTTY)*.MAP if exist $(OGUI)*.MAP del $(OGUI)*.MAP @@ -2310,12 +2703,16 @@ clean: if exist $(OGUI)*.LIB del $(OGUI)*.LIB if exist $(OLUA)*.LIB del $(OLUA)*.LIB if exist $(OPDC)*.LIB del $(OPDC)*.LIB + if exist $(OPDCC)*.LIB del $(OPDCC)*.LIB + if exist $(OPDCG)*.LIB del $(OPDCG)*.LIB if exist $(OUTL)*.EXP del $(OUTL)*.EXP if exist $(OTTY)*.EXP del $(OTTY)*.EXP if exist $(OGUI)*.EXP del $(OGUI)*.EXP if exist $(OLUA)*.EXP del $(OLUA)*.EXP if exist $(OPDC)*.EXP del $(OPDC)*.EXP - if exist $(SRC)\tiles.bmp del $(SRC)\tiles.bmp + 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 $(OTTY)console.res del $(OTTY)console.res if exist $(OTTY)xNetHack.res del $(OTTY)xNetHack.res if exist $(OGUI)xNetHackW.res del $(OGUI)xNetHackW.res @@ -2323,11 +2720,10 @@ clean: #=================================================================== # OTHER DEPENDENCIES #=================================================================== -# # The rest are stolen from sys/unix/Makefile.src, # twice, with the following changes: -# * the CONFIG_H and HACK_H sections comment out -# * ../include/ changed to $(INCL)\ +# * the CONFIG_H and HACK_H sections comment out or removed completely +# * ../include/ changed to $(INCL) # * slashes changed to back-slashes # * -c (which is included in cflagsBuild) substituted with -Fo$@ # * "-o $@ " is removed @@ -2335,6 +2731,7 @@ clean: # * win/share/cppregex.o commented out due to earlier rule # * commented out $(TARGETPFX)tile.o: tile.c $(HACK_H) # * commented out $(TARGETPFX)cppregex.o because it has its own rule already +# * check for an erroneous embedded Makefile comment char in the files.o line # * $(TARGETPFX) becomes $(OTTY) in 1st batch with $(TTYDEF) # * $(TARGETPFX) becomes $(OGUI) in 2nd batch with $(GUIDEF) # * commented out the lines starting with @@ -2352,40 +2749,17 @@ TARGET_CXX=$(cc) TARGET_CXXFLAGS=$(CPPFLAGS) $(TTYDEF) MOCPATH = moc.exe -# config.h timestamp -#$(CONFIG_H): $(INCL)\config.h $(INCL)\config1.h $(INCL)\patchlevel.h \ -# $(INCL)\tradstdc.h $(INCL)\integer.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 -# touch $(CONFIG_H) -# hack.h timestamp -#$(HACK_H): $(INCL)\hack.h $(CONFIG_H) $(INCL)\lint.h $(INCL)\align.h \ -# $(INCL)\dungeon.h $(INCL)\wintype.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)\context.h $(INCL)\rm.h $(INCL)\botl.h \ -# $(INCL)\rect.h $(INCL)\region.h $(INCL)\trap.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)\flag.h \ -# $(INCL)\winprocs.h $(INCL)\sndprocs.h $(INCL)\seffects.h \ -# $(INCL)\sys.h -# touch $(HACK_H) -# -$(OTTY)pcmain.o: ..\sys\share\pcmain.c $(HACK_H) $(INCL)\dlb.h +#$(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 +# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\ioctl.c +$(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 @@ -2393,419 +2767,375 @@ $(OTTY)posixregex.o: ..\sys\share\posixregex.c $(HACK_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\posixregex.c $(OTTY)random.o: ..\sys\share\random.c $(HACK_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\random.c -$(OTTY)ioctl.o: ..\sys\share\ioctl.c $(HACK_H) $(INCL)\tcap.h -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\ioctl.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)unixunix.o: ..\sys\unix\unixunix.c $(HACK_H) -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\unix\unixunix.c $(OTTY)unixres.o: ..\sys\unix\unixres.c $(CONFIG_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\unix\unixres.c -$(OTTY)getline.o: ..\win\tty\getline.c $(HACK_H) $(INCL)\wintty.h \ - $(INCL)\func_tab.h -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\tty\getline.c -$(OTTY)termcap.o: ..\win\tty\termcap.c $(HACK_H) $(INCL)\wintty.h \ - $(INCL)\tcap.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 -# $(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 -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\tty\wintty.c -$(OTTY)cursmain.o: ..\win\curses\cursmain.c $(HACK_H) $(INCL)\wincurs.h -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursmain.c -$(OTTY)curswins.o: ..\win\curses\curswins.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\curswins.h -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\curswins.c -$(OTTY)cursmisc.o: ..\win\curses\cursmisc.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\cursmisc.h \ - $(INCL)\func_tab.h $(INCL)\dlb.h -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursmisc.c -$(OTTY)cursdial.o: ..\win\curses\cursdial.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\cursdial.h \ - $(INCL)\func_tab.h -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursdial.c -$(OTTY)cursstat.o: ..\win\curses\cursstat.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\cursstat.h -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursstat.c -$(OTTY)cursinit.o: ..\win\curses\cursinit.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\cursinit.h -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursinit.c -$(OTTY)cursmesg.o: ..\win\curses\cursmesg.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\cursmesg.h -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursmesg.c -$(OTTY)cursinvt.o: ..\win\curses\cursinvt.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\cursinvt.h -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursinvt.c -#$(OTTY)Window.o: ..\win\X11\Window.c $(INCL)\xwindowp.h \ -# $(INCL)\xwindow.h $(CONFIG_H) $(INCL)\lint.h \ -# $(INCL)\winX.h $(INCL)\color.h $(INCL)\wintype.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)\color.h $(INCL)\wintype.h -# $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\dialogs.c -$(OTTY)winX.o: ..\win\X11\winX.c $(HACK_H) $(INCL)\winX.h \ - $(INCL)\dlb.h $(INCL)\xwindow.h ..\win\X11\nh72icon \ - ..\win\X11\nh56icon ..\win\X11\nh32icon -# $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winX.c -$(OTTY)winmap.o: ..\win\X11\winmap.c $(INCL)\xwindow.h $(HACK_H) \ - $(INCL)\dlb.h $(INCL)\winX.h $(INCL)\tile2x11.h -# $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winmap.c -$(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 $(INCL)\xwindow.h $(HACK_H) \ - $(INCL)\winX.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 -# $(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 -# $(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 -# $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\wintext.c -$(OTTY)winval.o: ..\win\X11\winval.c $(HACK_H) $(INCL)\winX.h -# $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winval.c -#$(OTTY)tile.o: tile.c $(HACK_H) -$(OTTY)winshim.o: ..\win\shim\winshim.c $(HACK_H) -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\shim\winshim.c -#$(OTTY)cppregex.o: ..\sys\share\cppregex.cpp $(CONFIG_H) -# $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\sys\share\cppregex.cpp -$(OTTY)qt_bind.o: ..\win\Qt\qt_bind.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_bind.h ..\win\Qt\qt_main.h \ - ..\win\Qt\qt_kde0.h ..\win\Qt\qt_click.h ..\win\Qt\qt_delay.h \ - ..\win\Qt\qt_xcmd.h ..\win\Qt\qt_key.h ..\win\Qt\qt_map.h \ - ..\win\Qt\qt_win.h ..\win\Qt\qt_clust.h ..\win\Qt\qt_menu.h \ - ..\win\Qt\qt_rip.h ..\win\Qt\qt_msg.h ..\win\Qt\qt_plsel.h \ - ..\win\Qt\qt_svsel.h ..\win\Qt\qt_set.h ..\win\Qt\qt_stat.h \ - ..\win\Qt\qt_icon.h ..\win\Qt\qt_streq.h ..\win\Qt\qt_line.h \ - ..\win\Qt\qt_yndlg.h ..\win\Qt\qt_str.h $(INCL)\dlb.h \ - $(QTn_H) +$(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 \ + ..\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 \ + ..\win\Qt\qt_msg.h ..\win\Qt\qt_plsel.h ..\win\Qt\qt_post.h \ + ..\win\Qt\qt_pre.h ..\win\Qt\qt_rip.h ..\win\Qt\qt_set.h \ + ..\win\Qt\qt_stat.h ..\win\Qt\qt_str.h ..\win\Qt\qt_streq.h \ + ..\win\Qt\qt_svsel.h ..\win\Qt\qt_win.h ..\win\Qt\qt_xcmd.h \ + ..\win\Qt\qt_yndlg.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_bind.cpp -$(OTTY)qt_click.o: ..\win\Qt\qt_click.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_click.h $(QTn_H) +$(OTTY)qt_click.o: ..\win\Qt\qt_click.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_click.h ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_click.cpp -$(OTTY)qt_clust.o: ..\win\Qt\qt_clust.cpp ..\win\Qt\qt_clust.h $(QTn_H) +$(OTTY)qt_clust.o: ..\win\Qt\qt_clust.cpp $(QTn_H) ..\win\Qt\qt_clust.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_clust.cpp -$(OTTY)qt_delay.o: ..\win\Qt\qt_delay.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_delay.h $(QTn_H) +$(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) \ - $(INCL)\tile2x11.h ..\win\Qt\qt_pre.h ..\win\Qt\qt_post.h \ - ..\win\Qt\qt_glyph.h ..\win\Qt\qt_bind.h ..\win\Qt\qt_main.h \ - ..\win\Qt\qt_kde0.h ..\win\Qt\qt_set.h ..\win\Qt\qt_inv.h \ - ..\win\Qt\qt_map.h ..\win\Qt\qt_win.h ..\win\Qt\qt_clust.h \ - ..\win\Qt\qt_str.h $(QTn_H) +$(OTTY)qt_glyph.o: ..\win\Qt\qt_glyph.cpp $(HACK_H) $(QTn_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 \ + ..\win\Qt\qt_str.h ..\win\Qt\qt_win.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_glyph.cpp -$(OTTY)qt_icon.o: ..\win\Qt\qt_icon.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_icon.h $(QTn_H) +$(OTTY)qt_icon.o: ..\win\Qt\qt_icon.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_icon.h ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h \ + ..\win\Qt\qt_str.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_icon.cpp -$(OTTY)qt_inv.o: ..\win\Qt\qt_inv.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_inv.h ..\win\Qt\qt_glyph.h \ - ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h ..\win\Qt\qt_set.h \ - ..\win\Qt\qt_bind.h $(QTn_H) +$(OTTY)qt_inv.o: ..\win\Qt\qt_inv.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_glyph.h ..\win\Qt\qt_inv.h \ + ..\win\Qt\qt_kde0.h ..\win\Qt\qt_main.h ..\win\Qt\qt_post.h \ + ..\win\Qt\qt_pre.h ..\win\Qt\qt_set.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_inv.cpp -$(OTTY)qt_key.o: ..\win\Qt\qt_key.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_key.h $(QTn_H) +$(OTTY)qt_key.o: ..\win\Qt\qt_key.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_key.h ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_key.cpp -$(OTTY)qt_line.o: ..\win\Qt\qt_line.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_line.h $(QTn_H) +$(OTTY)qt_line.o: ..\win\Qt\qt_line.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_line.h ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_line.cpp -$(OTTY)qt_main.o: ..\win\Qt\qt_main.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h \ - qt_main.moc ..\win\Qt\qt_bind.h ..\win\Qt\qt_glyph.h \ - ..\win\Qt\qt_inv.h ..\win\Qt\qt_key.h ..\win\Qt\qt_map.h \ - ..\win\Qt\qt_win.h ..\win\Qt\qt_clust.h ..\win\Qt\qt_msg.h \ - ..\win\Qt\qt_set.h ..\win\Qt\qt_stat.h ..\win\Qt\qt_icon.h \ - ..\win\Qt\qt_str.h qt_kde0.moc $(QTn_H) +$(OTTY)qt_main.o: ..\win\Qt\qt_main.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_clust.h ..\win\Qt\qt_glyph.h \ + ..\win\Qt\qt_icon.h ..\win\Qt\qt_inv.h ..\win\Qt\qt_kde0.h \ + ..\win\Qt\qt_key.h ..\win\Qt\qt_main.h ..\win\Qt\qt_map.h \ + ..\win\Qt\qt_msg.h ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h \ + ..\win\Qt\qt_set.h ..\win\Qt\qt_stat.h ..\win\Qt\qt_str.h \ + ..\win\Qt\qt_win.h qt_kde0.moc qt_main.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_main.cpp -$(OTTY)qt_map.o: ..\win\Qt\qt_map.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_map.h ..\win\Qt\qt_win.h \ - ..\win\Qt\qt_clust.h qt_map.moc ..\win\Qt\qt_click.h \ - ..\win\Qt\qt_glyph.h ..\win\Qt\qt_set.h ..\win\Qt\qt_bind.h \ - ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h ..\win\Qt\qt_str.h \ - $(QTn_H) +$(OTTY)qt_map.o: ..\win\Qt\qt_map.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_click.h ..\win\Qt\qt_clust.h \ + ..\win\Qt\qt_glyph.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 ..\win\Qt\qt_str.h ..\win\Qt\qt_win.h \ + qt_map.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_map.cpp -$(OTTY)qt_menu.o: ..\win\Qt\qt_menu.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_menu.h ..\win\Qt\qt_win.h \ - ..\win\Qt\qt_rip.h qt_menu.moc ..\win\Qt\qt_key.h \ - ..\win\Qt\qt_glyph.h ..\win\Qt\qt_set.h ..\win\Qt\qt_bind.h \ - ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h ..\win\Qt\qt_streq.h \ - ..\win\Qt\qt_line.h ..\win\Qt\qt_str.h $(QTn_H) +$(OTTY)qt_menu.o: ..\win\Qt\qt_menu.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_glyph.h ..\win\Qt\qt_kde0.h \ + ..\win\Qt\qt_key.h ..\win\Qt\qt_line.h ..\win\Qt\qt_main.h \ + ..\win\Qt\qt_menu.h ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h \ + ..\win\Qt\qt_rip.h ..\win\Qt\qt_set.h ..\win\Qt\qt_str.h \ + ..\win\Qt\qt_streq.h ..\win\Qt\qt_win.h qt_menu.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_menu.cpp -$(OTTY)qt_msg.o: ..\win\Qt\qt_msg.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_msg.h ..\win\Qt\qt_win.h \ - qt_msg.moc ..\win\Qt\qt_map.h ..\win\Qt\qt_clust.h \ - ..\win\Qt\qt_set.h ..\win\Qt\qt_bind.h ..\win\Qt\qt_main.h \ - ..\win\Qt\qt_kde0.h ..\win\Qt\qt_str.h $(QTn_H) +$(OTTY)qt_msg.o: ..\win\Qt\qt_msg.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_clust.h ..\win\Qt\qt_kde0.h \ + ..\win\Qt\qt_main.h ..\win\Qt\qt_map.h ..\win\Qt\qt_msg.h \ + ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h ..\win\Qt\qt_set.h \ + ..\win\Qt\qt_str.h ..\win\Qt\qt_win.h qt_msg.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_msg.cpp -$(OTTY)qt_plsel.o: ..\win\Qt\qt_plsel.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_plsel.h qt_plsel.moc \ - ..\win\Qt\qt_bind.h ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h \ - ..\win\Qt\qt_glyph.h ..\win\Qt\qt_set.h ..\win\Qt\qt_str.h \ - $(QTn_H) +$(OTTY)qt_plsel.o: ..\win\Qt\qt_plsel.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_glyph.h ..\win\Qt\qt_kde0.h \ + ..\win\Qt\qt_main.h ..\win\Qt\qt_plsel.h ..\win\Qt\qt_post.h \ + ..\win\Qt\qt_pre.h ..\win\Qt\qt_set.h ..\win\Qt\qt_str.h \ + qt_plsel.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_plsel.cpp -$(OTTY)qt_rip.o: ..\win\Qt\qt_rip.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_rip.h ..\win\Qt\qt_bind.h \ - ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h ..\win\Qt\qt_str.h \ - $(QTn_H) +$(OTTY)qt_rip.o: ..\win\Qt\qt_rip.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_kde0.h ..\win\Qt\qt_main.h \ + ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h ..\win\Qt\qt_rip.h \ + ..\win\Qt\qt_str.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_rip.cpp -$(OTTY)qt_set.o: ..\win\Qt\qt_set.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_set.h ..\win\Qt\qt_bind.h \ - ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h qt_set.moc \ - ..\win\Qt\qt_glyph.h ..\win\Qt\qt_xcmd.h ..\win\Qt\qt_str.h \ - $(QTn_H) +$(OTTY)qt_set.o: ..\win\Qt\qt_set.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_glyph.h ..\win\Qt\qt_kde0.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_set.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_set.cpp -$(OTTY)qt_stat.o: ..\win\Qt\qt_stat.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_stat.h ..\win\Qt\qt_win.h \ - ..\win\Qt\qt_icon.h qt_stat.moc ..\win\Qt\qt_set.h \ - ..\win\Qt\qt_bind.h ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h \ - ..\win\Qt\qt_str.h ..\win\Qt\qt_xpms.h $(QTn_H) +$(OTTY)qt_stat.o: ..\win\Qt\qt_stat.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_icon.h ..\win\Qt\qt_kde0.h \ + ..\win\Qt\qt_main.h ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h \ + ..\win\Qt\qt_set.h ..\win\Qt\qt_stat.h ..\win\Qt\qt_str.h \ + ..\win\Qt\qt_win.h ..\win\Qt\qt_xpms.h qt_stat.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_stat.cpp -$(OTTY)qt_str.o: ..\win\Qt\qt_str.cpp ..\win\Qt\qt_str.h $(QTn_H) +$(OTTY)qt_str.o: ..\win\Qt\qt_str.cpp $(QTn_H) ..\win\Qt\qt_str.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_str.cpp -$(OTTY)qt_streq.o: ..\win\Qt\qt_streq.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_streq.h ..\win\Qt\qt_line.h \ - ..\win\Qt\qt_str.h ..\win\Qt\qt_set.h ..\win\Qt\qt_bind.h \ - ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h $(QTn_H) +$(OTTY)qt_streq.o: ..\win\Qt\qt_streq.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_kde0.h ..\win\Qt\qt_line.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_streq.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_streq.cpp -$(OTTY)qt_svsel.o: ..\win\Qt\qt_svsel.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_svsel.h ..\win\Qt\qt_bind.h \ - ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h ..\win\Qt\qt_str.h \ - $(QTn_H) +$(OTTY)qt_svsel.o: ..\win\Qt\qt_svsel.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_kde0.h ..\win\Qt\qt_main.h \ + ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h ..\win\Qt\qt_str.h \ + ..\win\Qt\qt_svsel.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_svsel.cpp -$(OTTY)qt_win.o: ..\win\Qt\qt_win.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_win.h ..\win\Qt\qt_bind.h \ - ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h ..\win\Qt\qt_click.h \ - ..\win\Qt\qt_glyph.h ..\win\Qt\qt_inv.h ..\win\Qt\qt_key.h \ - ..\win\Qt\qt_icon.h ..\win\Qt\qt_map.h ..\win\Qt\qt_clust.h \ - ..\win\Qt\qt_menu.h ..\win\Qt\qt_rip.h ..\win\Qt\qt_msg.h \ - ..\win\Qt\qt_set.h $(QTn_H) +$(OTTY)qt_win.o: ..\win\Qt\qt_win.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_click.h ..\win\Qt\qt_clust.h \ + ..\win\Qt\qt_glyph.h ..\win\Qt\qt_icon.h ..\win\Qt\qt_inv.h \ + ..\win\Qt\qt_kde0.h ..\win\Qt\qt_key.h ..\win\Qt\qt_main.h \ + ..\win\Qt\qt_map.h ..\win\Qt\qt_menu.h ..\win\Qt\qt_msg.h \ + ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h ..\win\Qt\qt_rip.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) $(INCL)\func_tab.h \ - ..\win\Qt\qt_pre.h ..\win\Qt\qt_post.h ..\win\Qt\qt_xcmd.h \ - qt_xcmd.moc ..\win\Qt\qt_key.h ..\win\Qt\qt_bind.h \ - ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h ..\win\Qt\qt_set.h \ - ..\win\Qt\qt_str.h $(QTn_H) +$(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 \ + ..\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 $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_xcmd.cpp -$(OTTY)qt_yndlg.o: ..\win\Qt\qt_yndlg.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_yndlg.h qt_yndlg.moc \ - ..\win\Qt\qt_key.h ..\win\Qt\qt_str.h $(QTn_H) +$(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 -qt_kde0.moc: ..\win\Qt\qt_kde0.h $(QTn_H) - $(MOCPATH) ..\win\Qt\qt_kde0.h -qt_main.moc: ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h $(QTn_H) - $(MOCPATH) ..\win\Qt\qt_main.h -qt_map.moc: ..\win\Qt\qt_map.h ..\win\Qt\qt_win.h ..\win\Qt\qt_clust.h $(QTn_H) - $(MOCPATH) ..\win\Qt\qt_map.h -qt_menu.moc: ..\win\Qt\qt_menu.h ..\win\Qt\qt_win.h ..\win\Qt\qt_rip.h $(QTn_H) - $(MOCPATH) ..\win\Qt\qt_menu.h -qt_msg.moc: ..\win\Qt\qt_msg.h ..\win\Qt\qt_win.h $(QTn_H) - $(MOCPATH) ..\win\Qt\qt_msg.h -qt_plsel.moc: ..\win\Qt\qt_plsel.h $(QTn_H) - $(MOCPATH) ..\win\Qt\qt_plsel.h -qt_set.moc: ..\win\Qt\qt_set.h ..\win\Qt\qt_bind.h ..\win\Qt\qt_main.h \ - ..\win\Qt\qt_kde0.h $(QTn_H) - $(MOCPATH) ..\win\Qt\qt_set.h -qt_stat.moc: ..\win\Qt\qt_stat.h ..\win\Qt\qt_win.h ..\win\Qt\qt_icon.h \ - $(QTn_H) - $(MOCPATH) ..\win\Qt\qt_stat.h -qt_xcmd.moc: ..\win\Qt\qt_xcmd.h $(QTn_H) - $(MOCPATH) ..\win\Qt\qt_xcmd.h -qt_yndlg.moc: ..\win\Qt\qt_yndlg.h $(QTn_H) - $(MOCPATH) ..\win\Qt\qt_yndlg.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 +# $(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 \ + ..\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 +# $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winmap.c +$(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 +# $(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 +# $(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 +# $(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 +# $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\wintext.c +$(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)\wintty.h \ - $(INCL)\func_tab.h +$(OTTY)wc_trace.o: ..\win\chain\wc_trace.c $(HACK_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 \ + ..\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 +# $(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 +# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursinvt.c +$(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 +# $(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 \ + ..\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 +# $(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 +# $(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 +# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\tty\getline.c +$(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 +# $(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 +# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\tty\wintty.c $(OTTY)allmain.o: allmain.c $(HACK_H) -$(OTTY)alloc.o: alloc.c $(CONFIG_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)cmd.o: cmd.c $(HACK_H) $(INCL)\func_tab.h +$(OTTY)calendar.o: calendar.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)\color.h \ - $(INCL)\rm.h $(INCL)\objclass.h $(INCL)\defsym.h \ - $(INCL)\objects.h $(INCL)\wintype.h $(INCL)\sym.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 \ - #zlib.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) $(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)\permonst.h \ - $(INCL)\align.h $(INCL)\monattk.h $(INCL)\monflag.h \ - $(INCL)\monsters.h $(INCL)\objclass.h \ - $(INCL)\defsym.h $(INCL)\objects.h $(INCL)\wintype.h \ - $(INCL)\sym.h $(INCL)\artilist.h $(INCL)\dungeon.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 -$(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)\mfndpos.h \ - $(INCL)\artifact.h -$(OTTY)monst.o: 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 +$(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)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)nhlsel.o: nhlsel.c $(HACK_H) $(INCL)\sp_lev.h -$(OTTY)nhlobj.o: nhlobj.c $(HACK_H) $(INCL)\sp_lev.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)\obj.h \ - $(INCL)\prop.h $(INCL)\skills.h $(INCL)\color.h \ - $(INCL)\objclass.h $(INCL)\defsym.h $(INCL)\objects.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) $(INCL)\objclass.h \ - $(INCL)\defsym.h $(INCL)\objects.h $(INCL)\flag.h \ - $(HACK_H) $(INCL)\tcap.h $(INCL)\optlist.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)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)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)symbols.o: symbols.c $(HACK_H) $(INCL)\tcap.h +$(OTTY)strutil.o: strutil.c $(HACK_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)utf8map.o: utf8map.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)\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)worm.o: worm.c $(HACK_H) $(OTTY)worn.o: worn.c $(HACK_H) $(OTTY)write.o: write.c $(HACK_H) $(OTTY)zap.o: zap.c $(HACK_H) - -#--------------------------------------------- -# GUI # -TARGET_CC=$(cc) -TARGET_CFLAGS=$(CFLAGS) $(GUIDEF) -TARGET_CXX=$(cc) -TARGET_CXXFLAGS=$(CPPFLAGS) $(GUIDEF) -MOCPATH = moc.exe - -# config.h timestamp -#$(CONFIG_H): $(INCL)\config.h $(INCL)\config1.h $(INCL)\patchlevel.h \ -# $(INCL)\tradstdc.h $(INCL)\integer.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 -# touch $(CONFIG_H) -# hack.h timestamp -#$(HACK_H): $(INCL)\hack.h $(CONFIG_H) $(INCL)\lint.h $(INCL)\align.h \ -# $(INCL)\dungeon.h $(INCL)\wintype.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)\context.h $(INCL)\rm.h $(INCL)\botl.h \ -# $(INCL)\rect.h $(INCL)\region.h $(INCL)\trap.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)\flag.h \ -# $(INCL)\winprocs.h $(INCL)\sndprocs.h \ -# $(INCL)\seffects.h $(INCL)\sys.h -# touch $(HACK_H) -# -$(OGUI)pcmain.o: ..\sys\share\pcmain.c $(HACK_H) $(INCL)\dlb.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 +# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\ioctl.c +$(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 @@ -2813,374 +3143,383 @@ $(OGUI)posixregex.o: ..\sys\share\posixregex.c $(HACK_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\posixregex.c $(OGUI)random.o: ..\sys\share\random.c $(HACK_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\random.c -$(OGUI)ioctl.o: ..\sys\share\ioctl.c $(HACK_H) $(INCL)\tcap.h -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\share\ioctl.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)unixunix.o: ..\sys\unix\unixunix.c $(HACK_H) -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\unix\unixunix.c $(OGUI)unixres.o: ..\sys\unix\unixres.c $(CONFIG_H) # $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\sys\unix\unixres.c -$(OGUI)getline.o: ..\win\tty\getline.c $(HACK_H) $(INCL)\wintty.h \ - $(INCL)\func_tab.h -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\tty\getline.c -$(OGUI)termcap.o: ..\win\tty\termcap.c $(HACK_H) $(INCL)\wintty.h \ - $(INCL)\tcap.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 -# $(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 -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\tty\wintty.c -$(OGUI)cursmain.o: ..\win\curses\cursmain.c $(HACK_H) $(INCL)\wincurs.h -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursmain.c -$(OGUI)curswins.o: ..\win\curses\curswins.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\curswins.h -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\curswins.c -$(OGUI)cursmisc.o: ..\win\curses\cursmisc.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\cursmisc.h \ - $(INCL)\func_tab.h $(INCL)\dlb.h -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursmisc.c -$(OGUI)cursdial.o: ..\win\curses\cursdial.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\cursdial.h \ - $(INCL)\func_tab.h -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursdial.c -$(OGUI)cursstat.o: ..\win\curses\cursstat.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\cursstat.h -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursstat.c -$(OGUI)cursinit.o: ..\win\curses\cursinit.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\cursinit.h -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursinit.c -$(OGUI)cursmesg.o: ..\win\curses\cursmesg.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\cursmesg.h -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursmesg.c -$(OGUI)cursinvt.o: ..\win\curses\cursinvt.c $(HACK_H) \ - $(INCL)\wincurs.h ..\win\curses\cursinvt.h -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursinvt.c -#$(OGUI)Window.o: ..\win\X11\Window.c $(INCL)\xwindowp.h \ -# $(INCL)\xwindow.h $(CONFIG_H) $(INCL)\lint.h \ -# $(INCL)\winX.h $(INCL)\color.h $(INCL)\wintype.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)\color.h $(INCL)\wintype.h -# $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\dialogs.c -$(OGUI)winX.o: ..\win\X11\winX.c $(HACK_H) $(INCL)\winX.h \ - $(INCL)\dlb.h $(INCL)\xwindow.h ..\win\X11\nh72icon \ - ..\win\X11\nh56icon ..\win\X11\nh32icon -# $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winX.c -$(OGUI)winmap.o: ..\win\X11\winmap.c $(INCL)\xwindow.h $(HACK_H) \ - $(INCL)\dlb.h $(INCL)\winX.h $(INCL)\tile2x11.h -# $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winmap.c -$(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 $(INCL)\xwindow.h $(HACK_H) \ - $(INCL)\winX.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 -# $(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 -# $(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 -# $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\wintext.c -$(OGUI)winval.o: ..\win\X11\winval.c $(HACK_H) $(INCL)\winX.h -# $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winval.c -#$(OGUI)tile.o: tile.c $(HACK_H) -$(OGUI)winshim.o: ..\win\shim\winshim.c $(HACK_H) -# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\shim\winshim.c -#$(OGUI)cppregex.o: ..\sys\share\cppregex.cpp $(CONFIG_H) -# $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\sys\share\cppregex.cpp -$(OGUI)qt_bind.o: ..\win\Qt\qt_bind.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_bind.h ..\win\Qt\qt_main.h \ - ..\win\Qt\qt_kde0.h ..\win\Qt\qt_click.h ..\win\Qt\qt_delay.h \ - ..\win\Qt\qt_xcmd.h ..\win\Qt\qt_key.h ..\win\Qt\qt_map.h \ - ..\win\Qt\qt_win.h ..\win\Qt\qt_clust.h ..\win\Qt\qt_menu.h \ - ..\win\Qt\qt_rip.h ..\win\Qt\qt_msg.h ..\win\Qt\qt_plsel.h \ - ..\win\Qt\qt_svsel.h ..\win\Qt\qt_set.h ..\win\Qt\qt_stat.h \ - ..\win\Qt\qt_icon.h ..\win\Qt\qt_streq.h ..\win\Qt\qt_line.h \ - ..\win\Qt\qt_yndlg.h ..\win\Qt\qt_str.h $(INCL)\dlb.h \ - $(QTn_H) +$(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 \ + ..\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 \ + ..\win\Qt\qt_msg.h ..\win\Qt\qt_plsel.h ..\win\Qt\qt_post.h \ + ..\win\Qt\qt_pre.h ..\win\Qt\qt_rip.h ..\win\Qt\qt_set.h \ + ..\win\Qt\qt_stat.h ..\win\Qt\qt_str.h ..\win\Qt\qt_streq.h \ + ..\win\Qt\qt_svsel.h ..\win\Qt\qt_win.h ..\win\Qt\qt_xcmd.h \ + ..\win\Qt\qt_yndlg.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_bind.cpp -$(OGUI)qt_click.o: ..\win\Qt\qt_click.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_click.h $(QTn_H) +$(OGUI)qt_click.o: ..\win\Qt\qt_click.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_click.h ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_click.cpp -$(OGUI)qt_clust.o: ..\win\Qt\qt_clust.cpp ..\win\Qt\qt_clust.h $(QTn_H) +$(OGUI)qt_clust.o: ..\win\Qt\qt_clust.cpp $(QTn_H) ..\win\Qt\qt_clust.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_clust.cpp -$(OGUI)qt_delay.o: ..\win\Qt\qt_delay.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_delay.h $(QTn_H) +$(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) \ - $(INCL)\tile2x11.h ..\win\Qt\qt_pre.h ..\win\Qt\qt_post.h \ - ..\win\Qt\qt_glyph.h ..\win\Qt\qt_bind.h ..\win\Qt\qt_main.h \ - ..\win\Qt\qt_kde0.h ..\win\Qt\qt_set.h ..\win\Qt\qt_inv.h \ - ..\win\Qt\qt_map.h ..\win\Qt\qt_win.h ..\win\Qt\qt_clust.h \ - ..\win\Qt\qt_str.h $(QTn_H) +$(OGUI)qt_glyph.o: ..\win\Qt\qt_glyph.cpp $(HACK_H) $(QTn_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 \ + ..\win\Qt\qt_str.h ..\win\Qt\qt_win.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_glyph.cpp -$(OGUI)qt_icon.o: ..\win\Qt\qt_icon.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_icon.h $(QTn_H) +$(OGUI)qt_icon.o: ..\win\Qt\qt_icon.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_icon.h ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h \ + ..\win\Qt\qt_str.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_icon.cpp -$(OGUI)qt_inv.o: ..\win\Qt\qt_inv.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_inv.h ..\win\Qt\qt_glyph.h \ - ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h ..\win\Qt\qt_set.h \ - ..\win\Qt\qt_bind.h $(QTn_H) +$(OGUI)qt_inv.o: ..\win\Qt\qt_inv.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_glyph.h ..\win\Qt\qt_inv.h \ + ..\win\Qt\qt_kde0.h ..\win\Qt\qt_main.h ..\win\Qt\qt_post.h \ + ..\win\Qt\qt_pre.h ..\win\Qt\qt_set.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_inv.cpp -$(OGUI)qt_key.o: ..\win\Qt\qt_key.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_key.h $(QTn_H) +$(OGUI)qt_key.o: ..\win\Qt\qt_key.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_key.h ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_key.cpp -$(OGUI)qt_line.o: ..\win\Qt\qt_line.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_line.h $(QTn_H) +$(OGUI)qt_line.o: ..\win\Qt\qt_line.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_line.h ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_line.cpp -$(OGUI)qt_main.o: ..\win\Qt\qt_main.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h \ - qt_main.moc ..\win\Qt\qt_bind.h ..\win\Qt\qt_glyph.h \ - ..\win\Qt\qt_inv.h ..\win\Qt\qt_key.h ..\win\Qt\qt_map.h \ - ..\win\Qt\qt_win.h ..\win\Qt\qt_clust.h ..\win\Qt\qt_msg.h \ - ..\win\Qt\qt_set.h ..\win\Qt\qt_stat.h ..\win\Qt\qt_icon.h \ - ..\win\Qt\qt_str.h qt_kde0.moc $(QTn_H) +$(OGUI)qt_main.o: ..\win\Qt\qt_main.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_clust.h ..\win\Qt\qt_glyph.h \ + ..\win\Qt\qt_icon.h ..\win\Qt\qt_inv.h ..\win\Qt\qt_kde0.h \ + ..\win\Qt\qt_key.h ..\win\Qt\qt_main.h ..\win\Qt\qt_map.h \ + ..\win\Qt\qt_msg.h ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h \ + ..\win\Qt\qt_set.h ..\win\Qt\qt_stat.h ..\win\Qt\qt_str.h \ + ..\win\Qt\qt_win.h qt_kde0.moc qt_main.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_main.cpp -$(OGUI)qt_map.o: ..\win\Qt\qt_map.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_map.h ..\win\Qt\qt_win.h \ - ..\win\Qt\qt_clust.h qt_map.moc ..\win\Qt\qt_click.h \ - ..\win\Qt\qt_glyph.h ..\win\Qt\qt_set.h ..\win\Qt\qt_bind.h \ - ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h ..\win\Qt\qt_str.h \ - $(QTn_H) +$(OGUI)qt_map.o: ..\win\Qt\qt_map.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_click.h ..\win\Qt\qt_clust.h \ + ..\win\Qt\qt_glyph.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 ..\win\Qt\qt_str.h ..\win\Qt\qt_win.h \ + qt_map.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_map.cpp -$(OGUI)qt_menu.o: ..\win\Qt\qt_menu.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_menu.h ..\win\Qt\qt_win.h \ - ..\win\Qt\qt_rip.h qt_menu.moc ..\win\Qt\qt_key.h \ - ..\win\Qt\qt_glyph.h ..\win\Qt\qt_set.h ..\win\Qt\qt_bind.h \ - ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h ..\win\Qt\qt_streq.h \ - ..\win\Qt\qt_line.h ..\win\Qt\qt_str.h $(QTn_H) +$(OGUI)qt_menu.o: ..\win\Qt\qt_menu.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_glyph.h ..\win\Qt\qt_kde0.h \ + ..\win\Qt\qt_key.h ..\win\Qt\qt_line.h ..\win\Qt\qt_main.h \ + ..\win\Qt\qt_menu.h ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h \ + ..\win\Qt\qt_rip.h ..\win\Qt\qt_set.h ..\win\Qt\qt_str.h \ + ..\win\Qt\qt_streq.h ..\win\Qt\qt_win.h qt_menu.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_menu.cpp -$(OGUI)qt_msg.o: ..\win\Qt\qt_msg.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_msg.h ..\win\Qt\qt_win.h \ - qt_msg.moc ..\win\Qt\qt_map.h ..\win\Qt\qt_clust.h \ - ..\win\Qt\qt_set.h ..\win\Qt\qt_bind.h ..\win\Qt\qt_main.h \ - ..\win\Qt\qt_kde0.h ..\win\Qt\qt_str.h $(QTn_H) +$(OGUI)qt_msg.o: ..\win\Qt\qt_msg.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_clust.h ..\win\Qt\qt_kde0.h \ + ..\win\Qt\qt_main.h ..\win\Qt\qt_map.h ..\win\Qt\qt_msg.h \ + ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h ..\win\Qt\qt_set.h \ + ..\win\Qt\qt_str.h ..\win\Qt\qt_win.h qt_msg.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_msg.cpp -$(OGUI)qt_plsel.o: ..\win\Qt\qt_plsel.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_plsel.h qt_plsel.moc \ - ..\win\Qt\qt_bind.h ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h \ - ..\win\Qt\qt_glyph.h ..\win\Qt\qt_set.h ..\win\Qt\qt_str.h \ - $(QTn_H) +$(OGUI)qt_plsel.o: ..\win\Qt\qt_plsel.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_glyph.h ..\win\Qt\qt_kde0.h \ + ..\win\Qt\qt_main.h ..\win\Qt\qt_plsel.h ..\win\Qt\qt_post.h \ + ..\win\Qt\qt_pre.h ..\win\Qt\qt_set.h ..\win\Qt\qt_str.h \ + qt_plsel.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_plsel.cpp -$(OGUI)qt_rip.o: ..\win\Qt\qt_rip.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_rip.h ..\win\Qt\qt_bind.h \ - ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h ..\win\Qt\qt_str.h \ - $(QTn_H) +$(OGUI)qt_rip.o: ..\win\Qt\qt_rip.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_kde0.h ..\win\Qt\qt_main.h \ + ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h ..\win\Qt\qt_rip.h \ + ..\win\Qt\qt_str.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_rip.cpp -$(OGUI)qt_set.o: ..\win\Qt\qt_set.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_set.h ..\win\Qt\qt_bind.h \ - ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h qt_set.moc \ - ..\win\Qt\qt_glyph.h ..\win\Qt\qt_xcmd.h ..\win\Qt\qt_str.h \ - $(QTn_H) +$(OGUI)qt_set.o: ..\win\Qt\qt_set.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_glyph.h ..\win\Qt\qt_kde0.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_set.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_set.cpp -$(OGUI)qt_stat.o: ..\win\Qt\qt_stat.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_stat.h ..\win\Qt\qt_win.h \ - ..\win\Qt\qt_icon.h qt_stat.moc ..\win\Qt\qt_set.h \ - ..\win\Qt\qt_bind.h ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h \ - ..\win\Qt\qt_str.h ..\win\Qt\qt_xpms.h $(QTn_H) +$(OGUI)qt_stat.o: ..\win\Qt\qt_stat.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_icon.h ..\win\Qt\qt_kde0.h \ + ..\win\Qt\qt_main.h ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h \ + ..\win\Qt\qt_set.h ..\win\Qt\qt_stat.h ..\win\Qt\qt_str.h \ + ..\win\Qt\qt_win.h ..\win\Qt\qt_xpms.h qt_stat.moc $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_stat.cpp -$(OGUI)qt_str.o: ..\win\Qt\qt_str.cpp ..\win\Qt\qt_str.h $(QTn_H) +$(OGUI)qt_str.o: ..\win\Qt\qt_str.cpp $(QTn_H) ..\win\Qt\qt_str.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_str.cpp -$(OGUI)qt_streq.o: ..\win\Qt\qt_streq.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_streq.h ..\win\Qt\qt_line.h \ - ..\win\Qt\qt_str.h ..\win\Qt\qt_set.h ..\win\Qt\qt_bind.h \ - ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h $(QTn_H) +$(OGUI)qt_streq.o: ..\win\Qt\qt_streq.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_kde0.h ..\win\Qt\qt_line.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_streq.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_streq.cpp -$(OGUI)qt_svsel.o: ..\win\Qt\qt_svsel.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_svsel.h ..\win\Qt\qt_bind.h \ - ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h ..\win\Qt\qt_str.h \ - $(QTn_H) +$(OGUI)qt_svsel.o: ..\win\Qt\qt_svsel.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_kde0.h ..\win\Qt\qt_main.h \ + ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h ..\win\Qt\qt_str.h \ + ..\win\Qt\qt_svsel.h $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_svsel.cpp -$(OGUI)qt_win.o: ..\win\Qt\qt_win.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_win.h ..\win\Qt\qt_bind.h \ - ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h ..\win\Qt\qt_click.h \ - ..\win\Qt\qt_glyph.h ..\win\Qt\qt_inv.h ..\win\Qt\qt_key.h \ - ..\win\Qt\qt_icon.h ..\win\Qt\qt_map.h ..\win\Qt\qt_clust.h \ - ..\win\Qt\qt_menu.h ..\win\Qt\qt_rip.h ..\win\Qt\qt_msg.h \ - ..\win\Qt\qt_set.h $(QTn_H) +$(OGUI)qt_win.o: ..\win\Qt\qt_win.cpp $(HACK_H) $(QTn_H) \ + ..\win\Qt\qt_bind.h ..\win\Qt\qt_click.h ..\win\Qt\qt_clust.h \ + ..\win\Qt\qt_glyph.h ..\win\Qt\qt_icon.h ..\win\Qt\qt_inv.h \ + ..\win\Qt\qt_kde0.h ..\win\Qt\qt_key.h ..\win\Qt\qt_main.h \ + ..\win\Qt\qt_map.h ..\win\Qt\qt_menu.h ..\win\Qt\qt_msg.h \ + ..\win\Qt\qt_post.h ..\win\Qt\qt_pre.h ..\win\Qt\qt_rip.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) $(INCL)\func_tab.h \ - ..\win\Qt\qt_pre.h ..\win\Qt\qt_post.h ..\win\Qt\qt_xcmd.h \ - qt_xcmd.moc ..\win\Qt\qt_key.h ..\win\Qt\qt_bind.h \ - ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h ..\win\Qt\qt_set.h \ - ..\win\Qt\qt_str.h $(QTn_H) +$(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 \ + ..\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 $(TARGET_CXX) $(TARGET_CXXFLAGS) -Fo$@ ..\win\Qt\qt_xcmd.cpp -$(OGUI)qt_yndlg.o: ..\win\Qt\qt_yndlg.cpp $(HACK_H) ..\win\Qt\qt_pre.h \ - ..\win\Qt\qt_post.h ..\win\Qt\qt_yndlg.h qt_yndlg.moc \ - ..\win\Qt\qt_key.h ..\win\Qt\qt_str.h $(QTn_H) +$(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 -qt_kde0.moc: ..\win\Qt\qt_kde0.h $(QTn_H) - $(MOCPATH) ..\win\Qt\qt_kde0.h -qt_main.moc: ..\win\Qt\qt_main.h ..\win\Qt\qt_kde0.h $(QTn_H) - $(MOCPATH) ..\win\Qt\qt_main.h -qt_map.moc: ..\win\Qt\qt_map.h ..\win\Qt\qt_win.h ..\win\Qt\qt_clust.h $(QTn_H) - $(MOCPATH) ..\win\Qt\qt_map.h -qt_menu.moc: ..\win\Qt\qt_menu.h ..\win\Qt\qt_win.h ..\win\Qt\qt_rip.h $(QTn_H) - $(MOCPATH) ..\win\Qt\qt_menu.h -qt_msg.moc: ..\win\Qt\qt_msg.h ..\win\Qt\qt_win.h $(QTn_H) - $(MOCPATH) ..\win\Qt\qt_msg.h -qt_plsel.moc: ..\win\Qt\qt_plsel.h $(QTn_H) - $(MOCPATH) ..\win\Qt\qt_plsel.h -qt_set.moc: ..\win\Qt\qt_set.h ..\win\Qt\qt_bind.h ..\win\Qt\qt_main.h \ - ..\win\Qt\qt_kde0.h $(QTn_H) - $(MOCPATH) ..\win\Qt\qt_set.h -qt_stat.moc: ..\win\Qt\qt_stat.h ..\win\Qt\qt_win.h ..\win\Qt\qt_icon.h \ - $(QTn_H) - $(MOCPATH) ..\win\Qt\qt_stat.h -qt_xcmd.moc: ..\win\Qt\qt_xcmd.h $(QTn_H) - $(MOCPATH) ..\win\Qt\qt_xcmd.h -qt_yndlg.moc: ..\win\Qt\qt_yndlg.h $(QTn_H) - $(MOCPATH) ..\win\Qt\qt_yndlg.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 +# $(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 \ + ..\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 +# $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\winmap.c +$(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 +# $(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 +# $(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 +# $(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 +# $(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -Fo$@ ..\win\X11\wintext.c +$(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)\wintty.h \ - $(INCL)\func_tab.h +$(OGUI)wc_trace.o: ..\win\chain\wc_trace.c $(HACK_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 \ + ..\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 +# $(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 +# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\curses\cursinvt.c +$(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 +# $(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 \ + ..\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 +# $(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 +# $(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 +# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\tty\getline.c +$(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 +# $(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 +# $(TARGET_CC) $(TARGET_CFLAGS) -Fo$@ ..\win\tty\wintty.c $(OGUI)allmain.o: allmain.c $(HACK_H) -$(OGUI)alloc.o: alloc.c $(CONFIG_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)cmd.o: cmd.c $(HACK_H) $(INCL)\func_tab.h +$(OGUI)calendar.o: calendar.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)\color.h \ - $(INCL)\rm.h $(INCL)\objclass.h $(INCL)\defsym.h \ - $(INCL)\objects.h $(INCL)\wintype.h $(INCL)\sym.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 \ - #zlib.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) $(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)\permonst.h \ - $(INCL)\align.h $(INCL)\monattk.h $(INCL)\monflag.h \ - $(INCL)\monsters.h $(INCL)\objclass.h \ - $(INCL)\defsym.h $(INCL)\objects.h $(INCL)\wintype.h \ - $(INCL)\sym.h $(INCL)\artilist.h $(INCL)\dungeon.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 -$(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)\mfndpos.h \ - $(INCL)\artifact.h -$(OGUI)monst.o: 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 +$(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)nhlua.o: nhlua.c $(HACK_H) $(INCL)\dlb.h - $(Q)$(CC) $(CFLAGS) -wd4324 $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ $(@B).c -$(OGUI)nhlsel.o: nhlsel.c $(HACK_H) $(INCL)\sp_lev.h -$(OGUI)nhlobj.o: nhlobj.c $(HACK_H) $(INCL)\sp_lev.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)\obj.h \ - $(INCL)\prop.h $(INCL)\skills.h $(INCL)\color.h \ - $(INCL)\objclass.h $(INCL)\defsym.h $(INCL)\objects.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) $(INCL)\objclass.h \ - $(INCL)\defsym.h $(INCL)\objects.h $(INCL)\flag.h \ - $(HACK_H) $(INCL)\tcap.h $(INCL)\optlist.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)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)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)symbols.o: symbols.c $(HACK_H) $(INCL)\tcap.h +$(OGUI)strutil.o: strutil.c $(HACK_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)utf8map.o: utf8map.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)\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)worm.o: worm.c $(HACK_H) $(OGUI)worn.o: worn.c $(HACK_H) $(OGUI)write.o: write.c $(HACK_H) $(OGUI)zap.o: zap.c $(HACK_H) + +qt_kde0.moc: $(QTn_H) ..\win\Qt\qt_kde0.h + $(MOCPATH) ..\win\Qt\qt_kde0.h +qt_main.moc: $(QTn_H) ..\win\Qt\qt_kde0.h ..\win\Qt\qt_main.h + $(MOCPATH) ..\win\Qt\qt_main.h +qt_map.moc: $(QTn_H) ..\win\Qt\qt_clust.h ..\win\Qt\qt_map.h ..\win\Qt\qt_win.h + $(MOCPATH) ..\win\Qt\qt_map.h +qt_menu.moc: $(QTn_H) ..\win\Qt\qt_menu.h ..\win\Qt\qt_rip.h ..\win\Qt\qt_win.h + $(MOCPATH) ..\win\Qt\qt_menu.h +qt_msg.moc: $(QTn_H) ..\win\Qt\qt_msg.h ..\win\Qt\qt_win.h + $(MOCPATH) ..\win\Qt\qt_msg.h +qt_plsel.moc: $(QTn_H) ..\win\Qt\qt_plsel.h + $(MOCPATH) ..\win\Qt\qt_plsel.h +qt_set.moc: $(QTn_H) ..\win\Qt\qt_bind.h ..\win\Qt\qt_kde0.h \ + ..\win\Qt\qt_main.h ..\win\Qt\qt_set.h + $(MOCPATH) ..\win\Qt\qt_set.h +qt_stat.moc: $(QTn_H) ..\win\Qt\qt_icon.h ..\win\Qt\qt_stat.h \ + ..\win\Qt\qt_win.h + $(MOCPATH) ..\win\Qt\qt_stat.h +qt_xcmd.moc: $(QTn_H) ..\win\Qt\qt_xcmd.h + $(MOCPATH) ..\win\Qt\qt_xcmd.h +qt_yndlg.moc: $(QTn_H) ..\win\Qt\qt_yndlg.h + $(MOCPATH) ..\win\Qt\qt_yndlg.h # DEPENDENCIES MUST END AT END OF FILE +# IF YOU PUT STUFF HERE IT WILL GO AWAY diff --git a/sys/windows/build-msys2.txt b/sys/windows/build-msys2.txt new file mode 100644 index 0000000000..1258f7be90 --- /dev/null +++ b/sys/windows/build-msys2.txt @@ -0,0 +1,152 @@ +Building NetHack using MSYS2 + +Prerequisite Requirements: + + o MSYS2 + - https://www.msys2.org/ + - Download and run the installer, and start the UCRT64 shell. + then + pacman -S mingw-w64-ucrt-x86_64-gcc + pacman -S git + 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. + +/---------------------------------------------\ +| Directories for a Windows NetHack build | +\---------------------------------------------/ + + (NetHack-top) + | + +-----+------+-----+-------+-----------+-----------+-----~-----+------+ + | | | | | | | | | + dat doc include lib src sys win submodules util + | | | | + +----------+ +------+ +----+ +----+ + | | | | | | | | + | | | | | | | | + lua-5.4.6 pdcursesmod share windows tty win32 lua pdcursesmod + | + vs + | + +----------+-------+--------+--------+-----------+-------+-----+------+ + | | | | | | | | | + makedefs NetHack NetHackW PDCurses PDCursesGui tile2bmp tilemap tiles uudecode + +/-----------------------------------------------------------\ +| Building from MSYS2 bash | +\-----------------------------------------------------------/ + +Required components that are not bundled in the NetHack repository, but +are required to build NetHack yourself. + +Lua + NetHack 3.7 for Windows requires 3rd party Lua source that is not part + of the NetHack distribution or repository. + + A bash shell script for fetching prerequisite + sources is available, and can be run as follows from the top of + the NetHack source tree to obtain lua: + sh sys/windows/fetch.sh lua + +Curses + If you want to include curses interface support in NetHack 3.7 for + 3rd part pdcursesmod source code is required and is not part of the + NetHack distribution or repository. + + A bash shell script for fetching prerequisite + sources is available, and can be run as follows from the top of + the NetHack source tree to obtain pdcursesmod: + sh sys/windows/fetch.sh pdcursesmod + +Building + +Two different versions of NetHack will be built for Windows from the +command line using the Makefile approach: + A tty port utilizing the Win32 Console I/O subsystem Console, + and a curses interface in an executable called NetHack.exe. + + A Win32 native port built on the Windows API Graphical NetHack, + and graphical curses in an executable called NetHackW.exe. + +The Makefile configurations will build both NetHack.exe and NetHackW.exe +and will be able to use the same datafiles, save files and bones files. + +Since the last official release of NetHack, compilers and computer +architectures have evolved and you can now choose whether to build +a 32-bit x86 version, or a 64-bit x64 version. The default Makefile +is set up for a 32-bit x86 version, but that's only because it will +run on the largest number of existing Windows environments. Change it if you +want. Be aware that NetHack's save files and bones files in the 3.7.0 +release are not interchangeable between the 32-bit version and the +64-bit version (or between different platforms). + +I. Dispelling the Myths: + + Compiling NetHack for Windows is not as easy as it sounds, nor as hard + as it looks, however it will behoove you to read this entire section + through before beginning the task. + + 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: + +Setting Up + +1. Change your current directory the top of the nethack + source tree if it isn't there already. + +2. Execute the following command to place copies of the Makefiles in + the src subfolder. + 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 + sys/windows folder from steps #1 and #2 above: + cd src + +Compiling + +4. Now that everything is set up, you should be ready to start the + process. + + Your current directory should be the NetHack src directory. + + 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 + for windows will be in the package subfolder. + +Notes: + +1. To rebuild NetHack after changing something, change your current directory + 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. + MinGW-w64 - winlibs standalone build + - https://github.com/brechtsanders/winlibs_mingw + - Download one of the releases from + https://github.com/brechtsanders/winlibs_mingw/releases + and extract the contents into a folder (ideally in a folder + without spaces in the path), and add the location of the + subfolder containing gcc.exe to your PATH. + + diff --git a/sys/windows/build-nmake.txt b/sys/windows/build-nmake.txt new file mode 100644 index 0000000000..4735b480c7 --- /dev/null +++ b/sys/windows/build-nmake.txt @@ -0,0 +1,145 @@ +Building NetHack using the Visual Studio nmake from the command line + +Prerequisite Requirements: + + o Visual Studio Community Edition + A copy of Microsoft Visual Studio Community Edition needs to + be installed on your machine. See: + https://visualstudio.microsoft.com/vs/community/ + o Lua + NetHack 3.7 for Windows requires 3rd party Lua source that is not part + of the NetHack distribution or repository. + + A windows cmd command procedure for fetching prerequisite sources + is available, and can be run as follows from the top of the + NetHack source tree to obtain lua: + sys\windows\fetch.cmd lua + o pdcursesmod (Only required if curses interface support is desired) + If you want to include curses interface support in NetHack 3.7 for + 3rd part pdcursesmod source code is required and is not part of the + NetHack distribution or repository. + + A windows cmd command procedure for fetching prerequisite + sources is available, and can be run as follows from the top of + the NetHack source tree to obtain pdcursesmod: + sys\windows\fetch.cmd pdcursesmod + +The build Makefiles and procedures produce two executables: + a. A TTY and curses version of NetHack in nethack.exe + b. A Windows and curses graphical version in nethackw.exe. + +You can use one of the following build environments: + +/---------------------------------------------\ +| Directories for a Windows NetHack build | +\---------------------------------------------/ + + (NetHack-top) + | + +-----+------+-----+-------+-----------+-----------+-----~-----+------+ + | | | | | | | | | + dat doc include lib src sys win submodules util + | | | | + +----------+ +------+ +----+ +----+ + | | | | | | | | + | | | | | | | | + lua-5.4.6 pdcursesmod share windows tty win32 lua pdcursesmod + | + vs + | + +----------+-------+--------+--------+-----------+-------+-----+------+ + | | | | | | | | | + makedefs NetHack NetHackW PDCurses PDCursesGui tile2bmp tilemap tiles uudecode + + +/-----------------------------------------------------------\ +| Building From the Command Line Using nmake from one of the | +| Visual Studio Community Editions | +\-----------------------------------------------------------/ + +Building + +Two different versions of NetHack will be built for Windows from the +command line using the Makefile approach: + A tty port utilizing the Win32 Console I/O subsystem Console, + and a curses interface in an executabled called NetHack.exe. + NetHack + A Win32 native port built on the Windows API Graphical NetHack, + and graphical curses in an executable called NetHackW.exe. + +The Makefile configurations will build both; NetHack.exe and NetHackW.exe +and will be able to use the same datafiles, save files and bones files. + +Since the last official release of NetHack, compilers and computer +architectures have evolved and you can now choose whether to build +a 32-bit x86 version, or a 64-bit x64 version. The default Makefile +is set up for a 32-bit x86 version, but that's only because it will +run on the most number of existing Windows environments. Change it if you +want. Be aware that NetHack's save files and bones files in the 3.7.0 +release have not yet evolved enough to allow them to interchange between +the 32-bit version and the 64-bit version (or between different platforms). +That may change in future. + +I. Dispelling the Myths: + + Compiling NetHack for Windows is not as easy as it sounds, nor as hard + as it looks, however it will behoove you to read this entire section + through before beginning the task. + + We have provided a Visual Studio nmake Makefile.make in + sys/windows/Makefile.nmake, which you use from the Windows command + line. + + +II. To compile your copy of NetHack on a Windows machine: + +Setting Up + +1. Change your current directory to the sys\windows subfolder of the nethack + source tree, if you are at the top of the NetHack source tree. + cd sys\windows. + +2. Execute .\nhsetup.bat to place copies of the Makefiles in + the src subfolder. The file sys\windows\Makefile.nmake will copied + to a file src\Makefile. + .\nhsetup.bat + +3. Change your current directory to the src subfolder of the nethack + source tree. The following command assumes you are still in the + sys\windows folder from steps #1 and #2 above: + cd ..\..\src + +Compiling + +4. Now that everything is set up, you should be ready to start the + process. + + Your current directory should be the NetHack src directory. + + Issue these following command: + nmake package + + If all goes well, intermediate NetHack files will be placed in the + binary subfolder of the NetHack tree, and the final NetHack package + for windows will be in the package subfolder. + +Notes: + +1. To rebuild NetHack after changing something, change your current directory + to src and issue the appropriate command for your compiler: + + For Microsoft compiler: + nmake package + +2. Depending on the build and compiler and tools used above, the + package produced by the build will be either contain: + - a 32-bit (x86) .exe file, + which should run on any recent Win32 environment. + or + - a 64-bit (x64) .exe file, + which should run on any 64-bit Windows O/S. + + **Note**: saved games and bones files are NOT compatible between the + 32-bit and the 64-bit versions at this time. + + diff --git a/sys/windows/build-vs.txt b/sys/windows/build-vs.txt new file mode 100644 index 0000000000..a144470df6 --- /dev/null +++ b/sys/windows/build-vs.txt @@ -0,0 +1,72 @@ +Building NetHack using the Visual Studio IDE + +Prerequisite Requirements: + + o Visual Studio Community Edition + A copy of Microsoft Visual Studio Community Edition needs to + be installed on your machine. See: + https://visualstudio.microsoft.com/vs/community/ + o Lua + NetHack 3.7 for Windows requires 3rd party Lua source that is not part + of the NetHack distribution or repository. + + A windows cmd command procedure for fetching prerequisite sources + is available, and can be run as follows from the top of the + NetHack source tree to obtain lua: + sys\windows\fetch.cmd lua + o pdcursesmod (Only required if curses interface support is desired) + If you want to include curses interface support in NetHack 3.7 for + 3rd part pdcursesmod source code is required and is not part of the + NetHack distribution or repository. + + A windows cmd command procedure for fetching prerequisite + sources is available, and can be run as follows from the top of + the NetHack source tree to obtain pdcursesmod: + sys\windows\fetch.cmd pdcursesmod + +This processes produces a zip file containing two executable +versions: + + a. A TTY and curses version of NetHack in nethack.exe + b. A Windows and curses graphical version in nethackw.exe. + + +/---------------------------------------------\ +| Directories for a Windows NetHack build | +\---------------------------------------------/ + + (NetHack-top) + | + +-----+------+-----+-------+-----------+-----------+-----~-----+------+ + | | | | | | | | | + dat doc include lib src sys win submodules util + | | | | + +----------+ +------+ +----+ +----+ + | | | | | | | | + | | | | | | | | + lua-5.4.6 pdcursesmod share windows tty win32 lua pdcursesmod + | + vs + | + +----------+-------+--------+--------+-----------+-------+-----+------+ + | | | | | | | | | + makedefs NetHack NetHackW PDCurses PDCursesGui tile2bmp tilemap tiles uudecode + + +/-----------------------------------------------------------\ +| Building And Running Using Visual Studio 2019, 2022 | +\-----------------------------------------------------------/ + +When using Visual Studio Community Edition, load the provided solution +file within the IDE, build the solution. + +The Visual Studio NetHack solution file can be found here: + sys\windows\vs\NetHack.sln + +The steps are: + 1. Launch the IDE. + 2. Open the appropriate solution file in sys\windows\vs\NetHack.sln. + 3. Select the build configuration you wish to use (Release, Debug, etc.). + 4. From the build menu, select build solution. + 5. Type F5 to start debugging. + diff --git a/sys/windows/consoletty.c b/sys/windows/consoletty.c index b34293bec0..1da59d5f26 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 @@ -39,7 +40,7 @@ #endif #endif -extern boolean getreturn_enabled; /* from sys/share/pcsys.c */ +extern boolean getreturn_enabled; /* from windmain.c */ extern int redirect_stdout; #ifdef TTY_GRAPHICS @@ -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 */ @@ -170,8 +192,9 @@ struct console_t { WORD background; WORD foreground; WORD attr; - int current_nhcolor; - int current_nhbkcolor; + int32 current_nhcolor; + int32 current_nhbkcolor; + int32 current_colorflags; int current_nhattr[ATR_INVERSE+1]; COORD cursor; HANDLE hConOut; @@ -186,15 +209,8 @@ struct console_t { WCHAR cpMap[256]; boolean font_changed; CONSOLE_FONT_INFOEX orig_font_info; -#ifndef VIRTUAL_TERMINAL_SEQUENCES - UINT original_code_page; -} console = { - 0, - (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED), - (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED), - NO_COLOR, -#else /* VIRTUAL_TERMINAL_SEQUENCES */ UINT orig_code_page; +#ifdef VIRTUAL_TERMINAL_SEQUENCES char *orig_localestr; DWORD orig_in_cmode; DWORD orig_out_cmode; @@ -205,36 +221,21 @@ struct console_t { DWORD out_cmode; long color24; int color256idx; +#endif /* VIRTUAL_TERMINAL_SEQUENCES */ } console = { - FALSE, - 0, + FALSE, /* is_ready */ + 0, /* hWnd */ (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED), /* background */ (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED), /* foreground */ 0, /* attr */ 0, /* current_nhcolor */ 0, /* current_nhbkcolor */ -#endif /* VIRTUAL_TERMINAL_SEQUENCES */ - {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE}, -#ifndef VIRTUAL_TERMINAL_SEQUENCES - {0, 0}, - NULL, - NULL, - { 0 }, - 0, - 0, - FALSE, - 0, - NULL, - NULL, - { 0 }, - FALSE, - { 0 }, - 0 -#else /* VIRTUAL_TERMINAL_SEQUENCES */ - { 0, 0 }, /* cursor */ + 0, /* current_colorflags */ + { 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 */ @@ -245,6 +246,7 @@ struct console_t { FALSE, /* font_changed */ { 0 }, /* orig_font_info */ 0U, /* orig_code_page */ +#ifdef VIRTUAL_TERMINAL_SEQUENCES NULL, /* orig_localestr */ 0, /* orig_in_cmode */ 0, /* orig_out_cmode */ @@ -258,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", @@ -269,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, @@ -298,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 @@ -308,15 +315,38 @@ 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; +/* +Windows console palette: +Color Name Console Legacy RGB Values New Default RGB Values +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 +DARK_RED 128,0,0 197,15,31 +DARK_MAGENTA 128,0,128 136,23,152 +DARK_YELLOW 128,128,0 193,156,0 +DARK_WHITE 192,192,192 204,204,204 +BRIGHT_BLACK 128,128,128 118,118,118 +BRIGHT_BLUE 0,0,255 59,120,255 +BRIGHT_GREEN 0,255,0 22,198,12 +BRIGHT_CYAN 0,255,255 97,214,214 +BRIGHT_RED 255,0,0 231,72,86 +BRIGHT_MAGENTA 255,0,255 180,0,158 +BRIGHT_YELLOW 255,255,0 249,241,165 +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; @@ -465,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); @@ -549,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); @@ -564,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; - static const char escseq[] = "\x1b[4m"; + 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 @@ -699,56 +765,57 @@ 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]; - long mcolor = - (color24bit & 0xFFFFFF); /* color 0 has bit 0x1000000 set */ + uint32 mcolor = COLORVAL(color24bit); Snprintf(tcolorbuf, sizeof tcolorbuf, tcfmtstr24bit, ((mcolor >> 16) & 0xFF), /* red */ ((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 } @@ -936,7 +1002,7 @@ void buffer_fill_to_end(cell_t * buffer, cell_t * fill, int x, int y) while (dst != sentinel) *dst++ = *fill; - if ((iflags.debug.immediateflips || !gp.program_state.in_moveloop) + if ((iflags.debug.immediateflips || !program_state.in_moveloop) && buffer == console.back_buffer) back_buffer_flip(); } @@ -952,7 +1018,7 @@ static void buffer_clear_to_end_of_line(cell_t * buffer, int x, int y) while (dst != sentinel) *dst++ = clear_cell; - if (iflags.debug.immediateflips || !gp.program_state.in_moveloop) + if (iflags.debug.immediateflips || !program_state.in_moveloop) back_buffer_flip(); } @@ -964,7 +1030,7 @@ void buffer_write(cell_t * buffer, cell_t * cell, COORD pos) cell_t * dst = buffer + (console.width * pos.Y) + pos.X; *dst = *cell; - if ((iflags.debug.immediateflips || !gp.program_state.in_moveloop) + if ((iflags.debug.immediateflips || !program_state.in_moveloop) && buffer == console.back_buffer) back_buffer_flip(); } @@ -975,26 +1041,18 @@ void buffer_write(cell_t * buffer, cell_t * cell, COORD pos) void gettty(void) { -#ifndef TEXTCOLOR - int k; -#endif erase_char = '\b'; kill_char = 21; /* cntl-U */ iflags.cbreak = TRUE; -#ifdef TEXTCOLOR init_ttycolor(); -#else - for (k = 0; k < CLR_MAX; ++k) - ttycolors[k] = NO_COLOR; -#endif } /* reset terminal to original state */ void -settty(const char* s) +settty(const char *s) { cmov(ttyDisplay->curx, ttyDisplay->cury); - end_screen(); + term_end_screen(); if (s) raw_print(s); restore_original_console_font(); @@ -1017,27 +1075,55 @@ 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; set_option_mod_status("mouse_support", set_in_game); - iflags.colorcount = 16777216; -// iflags.colorcount = 256; } void -tty_number_pad(int state) +tty_number_pad(int state UNUSED) { // do nothing } +/* stub tcap replacements for linkage from wintty.c */ + +void +term_shutdown(void) +{ + consoletty_exit(); +} + +#ifdef ASCIIGRAPH +void +graph_on(void) +{ +} + +void +graph_off(void) +{ +} +#endif + void -tty_start_screen(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 */ @@ -1051,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) { @@ -1068,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: @@ -1086,7 +1164,7 @@ CtrlHandler(DWORD ctrltype) /* called by pcmain() and process_options() */ void -consoletty_open(int mode) +consoletty_open(int mode UNUSED) { int debugvar; @@ -1104,6 +1182,7 @@ consoletty_open(int mode) CO = console.width; really_move_cursor(); + nhUse(debugvar); } void @@ -1111,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; @@ -1140,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 @@ -1149,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 (gp.program_state.done_hup) + 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 @@ -1171,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) { @@ -1185,19 +1269,21 @@ console_poskey(coordxy *x, coordxy *y, int *mod) } #ifdef QWERTZ_SUPPORT if (gc.Cmd.swap_yz) - numpad |= 0x10; + numberpad |= 0x10; #endif - ch = (gp.program_state.done_hup) + 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; } @@ -1220,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 @@ -1257,7 +1345,7 @@ nocmov(int x, int y) } void -xputs(const char* s) +xputs(const char *s) { int k; int slen = (int) strlen(s); @@ -1290,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) @@ -1311,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; @@ -1395,29 +1484,27 @@ xputc_core(int ch) */ #endif void -g_putch(int in_ch) +console_g_putch(int in_ch) { + unsigned char 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 = (in_ch >= 0 && in_ch < SIZE(console.cpMap)); #endif /* VIRTUAL_TERMINAL_SEQUENCES */ - unsigned char ch = (unsigned char) in_ch; + 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 @@ -1428,7 +1515,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 */ @@ -1450,12 +1537,10 @@ g_putch(int in_ch) cell.utf8str[1] = 0; ccount = 2; } -#endif +#endif /* VIRTUAL_TERMINAL_SEQUENCES */ buffer_write(console.back_buffer, &cell, console.cursor); } -#ifdef VIRTUAL_TERMINAL_SEQUENCES -#ifdef UTF8_FROM_CORE /* * Overrides wintty.c function of the same name * for win32. It is used for glyphs only, not text and @@ -1466,6 +1551,8 @@ g_putch(int in_ch) void g_pututf8(uint8 *sequence) { +#ifdef VIRTUAL_TERMINAL_SEQUENCES +#ifdef UTF8_FROM_CORE set_console_cursor(ttyDisplay->curx, ttyDisplay->cury); cell_t cell; cell.attr = console.attr; @@ -1476,28 +1563,47 @@ g_pututf8(uint8 *sequence) Snprintf((char *) cell.utf8str, sizeof cell.utf8str, "%s", (char *) sequence); buffer_write(console.back_buffer, &cell, console.cursor); -} #endif /* UTF8_FROM_CORE */ +#endif +} void -term_start_24bitcolor(struct unicode_representation *uval) +term_start_extracolor(uint32 nhcolor, uint16 color256idx) +{ +#ifdef VIRTUAL_TERMINAL_SEQUENCES + if ((nhcolor & NH_BASIC_COLOR) == 0) { + console.color24 = COLORVAL(nhcolor); /* color 0 has bit 0x1000000 set */ + console.current_colorflags = 0; + console.color256idx = color256idx; + } else { +#endif + /* NH_BASIC_COLOR */ + console.current_nhcolor = COLORVAL(nhcolor); + console.current_colorflags = NH_BASIC_COLOR; + term_start_color(console.current_nhcolor); +#ifdef VIRTUAL_TERMINAL_SEQUENCES + } +#endif +} + +void term_start_256color(int idx UNUSED) { - console.color24 = uval->ucolor; /* color 0 has bit 0x1000000 set */ - console.color256idx = uval->u256coloridx; } void -term_end_24bitcolor(void) +term_end_extracolor(void) { +#ifdef VIRTUAL_TERMINAL_SEQUENCES console.color24 = 0L; console.color256idx = 0; +#endif + console.current_nhcolor = NO_COLOR; } -#endif /* VIRTUAL_TERMINAL_SEQUENCES */ 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, @@ -1515,9 +1621,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; @@ -1543,10 +1646,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++; @@ -1563,6 +1666,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) { @@ -1610,6 +1732,7 @@ tty_delay_output(void) while (goal > clock()) { k = junk; /* Do nothing */ } + nhUse(k); } /* @@ -1636,7 +1759,6 @@ tty_delay_output(void) static void init_ttycolor(void) { -#ifdef TEXTCOLOR ttycolors[CLR_BLACK] = FOREGROUND_INTENSITY; /* fix by Quietust */ ttycolors[CLR_RED] = FOREGROUND_RED; ttycolors[CLR_GREEN] = FOREGROUND_GREEN; @@ -1673,15 +1795,6 @@ init_ttycolor(void) ttycolors_inv[CLR_BRIGHT_CYAN] = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY; ttycolors_inv[CLR_WHITE] = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED | BACKGROUND_INTENSITY; -#else - int k; - ttycolors[0] = FOREGROUND_INTENSITY; - ttycolors_inv[0] = BACKGROUND_INTENSITY; - for (k = 1; k < SIZE(ttycolors); ++k) { - ttycolors[k] = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED; - ttycolors_inv[k] = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED; - } -#endif init_ttycolor_completed = TRUE; } @@ -1765,31 +1878,29 @@ term_start_raw_bold(void) void term_start_color(int color) { -#ifdef TEXTCOLOR - if (color >= 0 && color < CLR_MAX) { + if (color == NO_COLOR) { + term_end_color(); + } else if (color >= 0 && color < CLR_MAX) { console.current_nhcolor = color; - } else -#endif - console.current_nhcolor = NO_COLOR; + } else { + console.current_nhcolor = NO_COLOR; + } } void term_start_bgcolor(int color) { -#ifdef TEXTCOLOR - if (color >= 0 && color < CLR_MAX) { + if (color != NO_COLOR && (color >= 0 && color < CLR_MAX)) { console.current_nhbkcolor = color; - } else -#endif - console.current_nhbkcolor = NO_COLOR; + } else { + console.current_nhbkcolor = NO_COLOR; + } } void term_end_color(void) { -#ifdef TEXTCOLOR console.foreground = DEFTEXTCOLOR; -#endif #ifndef VIRTUAL_TERMINAL_SEQUENCES console.attr = (console.foreground | console.background); #endif /* ! VIRTUAL_TERMINAL_SEQUENCES */ @@ -1851,6 +1962,7 @@ toggle_mouse_support(void) #endif /* VIRTUAL_TERMINAL_SEQUENCES */ break; case 0: + FALLTHROUGH; /*FALLTHRU*/ default: #ifndef VIRTUAL_TERMINAL_SEQUENCES @@ -1875,7 +1987,7 @@ toggle_mouse_support(void) /* handle tty options updates here */ void -consoletty_preference_update(const char* pref) +consoletty_preference_update(const char *pref) { if (stricmp(pref, "mouse_support") == 0) { #ifndef NO_MOUSE_ALLOWED @@ -1911,12 +2023,18 @@ tty_utf8graphics_fixup(void) { CONSOLE_FONT_INFOEX console_font_info; if ((tty_procs.wincap2 & WC2_U_UTF8STR) && SYMHANDLING(H_UTF8)) { + char *localestr = 0; + if (!console.hConOut) console.hConOut = GetStdHandle(STD_OUTPUT_HANDLE); /* the locale */ - if (console.localestr) + if (console.localestr) { free(console.localestr); - console.localestr = dupstr(setlocale(LC_ALL, ".UTF8")); + console.localestr = NULL; + } + localestr = setlocale(LC_ALL, ".UTF8"); + if (localestr) + console.localestr = dupstr(localestr); /* the code page */ SetConsoleOutputCP(65001); console.code_page = GetConsoleOutputCP(); @@ -2007,9 +2125,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(); } @@ -2021,7 +2139,7 @@ win32con_toggle_cursor_info(void) #endif void -map_subkeyvalue(char* op) +map_subkeyvalue(char *op) { char digits[] = "0123456789"; int length, i, idx, val; @@ -2053,7 +2171,7 @@ map_subkeyvalue(char* op) key_overrides[idx] = val; } - +#if 0 /* fatal error */ /*VARARGS1*/ void consoletty_error @@ -2072,6 +2190,7 @@ VA_DECL(const char *, s) VA_END(); exit(EXIT_FAILURE); } +#endif void synch_cursor(void) @@ -2079,13 +2198,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 @@ -2094,22 +2216,28 @@ static int CALLBACK EnumFontCallback( void check_and_set_font(void) { +#ifndef VIRTUAL_TERMINAL_SEQUENCES if (!check_font_widths()) { - raw_print("WARNING: glyphs too wide in console font." - " Changing code page to 437 and font to Consolas\n"); + if (wizard) { + const char *msg = "WARNING: glyphs too wide in console font." + " Changing code page to 437 and font to Consolas"; + + if (iflags.window_inited) + pline ("%s", msg); + else + raw_printf("%s\n", msg); + } set_known_good_console_font(); } +#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); @@ -2176,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]; @@ -2205,6 +2333,7 @@ check_font_widths(void) 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 @@ -2223,7 +2352,7 @@ set_known_good_console_font(void) console.font_changed = TRUE; #ifndef VIRTUAL_TERMINAL_SEQUENCES console.orig_font_info = console_font_info; - console.original_code_page = GetConsoleOutputCP(); + console.orig_code_page = GetConsoleOutputCP(); #else /* VIRTUAL_TERMINAL_SEQUENCES */ console.code_page = GetConsoleOutputCP(); @@ -2258,7 +2387,7 @@ restore_original_console_font(void) BOOL success; #ifndef VIRTUAL_TERMINAL_SEQUENCES raw_print("Restoring original font and code page\n"); - success = SetConsoleOutputCP(console.original_code_page); + success = SetConsoleOutputCP(console.orig_code_page); #else /* VIRTUAL_TERMINAL_SEQUENCES */ if (wizard) raw_print("Restoring original font, code page and locale\n"); @@ -2328,7 +2457,7 @@ void set_cp_map(void) } #if 0 -/* early_raw_print() is used during early game intialization prior to the +/* early_raw_print() is used during early game initialization prior to the * setting up of the windowing system. This allows early errors and panics * to have there messages displayed. * @@ -2418,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() */ @@ -2498,18 +2627,21 @@ 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*/ console.orig_code_page = GetConsoleOutputCP(); /* store the original locale */ - console.orig_localestr = dupstr(setlocale(LC_ALL, "")); + console.orig_localestr = NULL; + localestr = setlocale(LC_ALL, ""); + if (localestr) + console.orig_localestr = dupstr(localestr); /* 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; @@ -2526,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 */ @@ -2610,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 */ @@ -2635,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); } @@ -2668,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 @@ -2889,10 +3022,10 @@ set_keyhandling_via_option(void) int default_processkeystroke( - HANDLE hConIn, - INPUT_RECORD* ir, - boolean* valid, - boolean numberpad, + HANDLE hConIn UNUSED, + INPUT_RECORD *ir, + boolean *valid, + uchar numberpad, int portdebug) { int k = 0; @@ -3054,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, + DWORD *count, + uchar numberpad, int mode, int *mod, coord *cc) @@ -3071,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; @@ -3089,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) { @@ -3317,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; @@ -3328,7 +3461,7 @@ int ray_processkeystroke( HANDLE hConIn, INPUT_RECORD *ir, boolean *valid, - boolean numberpad, + uchar numberpad, int portdebug) { int keycode, vk; @@ -3515,7 +3648,7 @@ ray_checkinput( HANDLE hConIn, INPUT_RECORD *ir, DWORD *count, - boolean numpad, + uchar numberpad, int mode, int *mod, coord *cc) @@ -3527,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; @@ -3550,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 { @@ -3585,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 @@ -3683,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; @@ -3834,7 +3967,7 @@ nh340_checkinput( HANDLE hConIn, INPUT_RECORD *ir, DWORD *count, - boolean numpad, + uchar numberpad, int mode, int *mod, coord *cc) @@ -3846,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; @@ -3866,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) { @@ -3905,5 +4038,4 @@ nh340_checkinput( } return mode ? 0 : ch; } - #endif /* WIN32 */ diff --git a/sys/windows/fetch.cmd b/sys/windows/fetch.cmd new file mode 100644 index 0000000000..7399ed31c5 --- /dev/null +++ b/sys/windows/fetch.cmd @@ -0,0 +1,30 @@ +@echo off +if not exist lib\* mkdir lib + +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 + if NOT exist lib/lua.h ( + cd lib + curl -L %CURLLUASRC% -o %CURLLUADST% + tar -xvf %CURLLUADST% + cd .. + ) + echo Lua placed in lib/lua-%LUA_VERSION%.tar.gz +) + +if [%1] == [pdcursesmod] ( + set PDCVERSION=4.4.0 + set CURLPDCSRC=https://github.com/Bill-Gray/PDCursesMod/archive/refs/tags/v%PDCVERSION%.zip + set CURLPDCDST=pdcursesmod.zip + if NOT exist lib/pdcursesmod/curses.h ( + cd lib + curl -L %CURLPDCSRC% -o %CURLPDCDST% + tar -xvf %CURLPDCDST% + mkdir pdcursesmod + tar -C pdcursesmod --strip-components=1 -xvf %CURLPDCDST% + cd .. + ) +) diff --git a/sys/windows/guitty.c b/sys/windows/guitty.c new file mode 100644 index 0000000000..7dd14ec3ad --- /dev/null +++ b/sys/windows/guitty.c @@ -0,0 +1,4 @@ +/* Provide erase_char and kill_char for Curses on Windows GUI */ + +char erase_char; +char kill_char; diff --git a/sys/windows/nhsetup.bat b/sys/windows/nhsetup.bat index 2565c2bbe5..ed41d23615 100755 --- a/sys/windows/nhsetup.bat +++ b/sys/windows/nhsetup.bat @@ -1,6 +1,6 @@ @REM NetHack 3.7 nhsetup.bat $NHDT-Date: 1596498315 2020/08/03 23:45:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.40 $ */ -@REM Copyright (c) NetHack Development Team 1993-2022 -@REM NetHack may be freely redistributed. See license for details. +@REM Copyright (c) NetHack Development Team 1993-2024 +@REM NetHack may be freely redistributed. See license for details. @REM Win32 setup batch file, see Install.windows for details @REM @echo off @@ -31,20 +31,27 @@ echo Directories look ok. :movemakes echo Moving Makefiles into ..\..\src for those not using Visual Studio -REM Some file movemet for those that still want to use MAKE or NMAKE and a Makefile +REM Some file movement for those that still want to use MAKE or NMAKE and a Makefile :do_tty if NOT exist %BINPATH%\*.* mkdir %BINPATH% if NOT exist %BINPATH%\license copy ..\..\dat\license %BINPATH%\license >nul -echo Copying Microsoft Makefile - Makefile.nmake to ..\..\src\Makefile... -if NOT exist ..\..\src\Makefile goto :donmake + +echo Copying Microsoft Makefile - Makefile.nmake to ..\..\src\Makefile +if NOT exist ..\..\src\Makefile goto donenmake copy ..\..\src\Makefile ..\..\src\Makefile-orig >nul echo Your existing echo ..\..\src\Makefile echo has been renamed to echo ..\..\src\Makefile-orig -:donmake -copy Makefile.nmake ..\..\src\Makefile >nul -echo Microsoft Makefile copied ok. +:donenmake +copy /Y Makefile.nmake ..\..\src\Makefile >nul +echo Microsoft nmake Makefile.nmake copy to ..\..\src\Makefile completed. + +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. goto :done @@ -57,7 +64,7 @@ goto :fini :done echo done! echo. -echo Proceed with the next step documented in Install.windows +echo Proceed with the next step documented in Install.windows echo. :fini diff --git a/sys/windows/sysconf.template b/sys/windows/sysconf.template index 37bec7f6cc..002ca7043e 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. @@ -150,3 +154,6 @@ HIDEUSAGE=1 # # The location that file synchronization locks are stored (writeable) #LOCKDIR=c:\ProgramData\xNetHack\6.0 + +# URL loaded for creating reports to the NetHack DevTeam +#CRASHREPORTURL=https://github.com/copperwater/xNetHack/issues diff --git a/sys/windows/vs/.gitattributes b/sys/windows/vs/.gitattributes index aaae270114..852f614772 100644 --- a/sys/windows/vs/.gitattributes +++ b/sys/windows/vs/.gitattributes @@ -1,2 +1,2 @@ -* NH_filestag=(file%s_for_Visual_Studio_2017_or_2019_or_2022_Community_Edition_builds) +* NH_filestag=(file%s_for_Visual_Studio_2019_or_2022_Community_Edition_builds) ..files !NH_filegenerated diff --git a/sys/windows/vs/FetchPrereq/fetchprereq.nmake b/sys/windows/vs/FetchPrereq/fetchprereq.nmake new file mode 100644 index 0000000000..3b0ea1b8c2 --- /dev/null +++ b/sys/windows/vs/FetchPrereq/fetchprereq.nmake @@ -0,0 +1,78 @@ +# NetHack 3.7 fetchprereq.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 version of Lua we want +LUA_VERSION=5.4.6 +CURLLUASRC=https://www.lua.org/ftp/lua-$(LUA_VERSION).tar.gz +CURLLUADST=lua-$(LUA_VERSION).tar.gz + +# The version of pdcursesmod that we want +PDCDIST=pdcursesmod +PDCURSES_VERSION = 4.4.0 +PDCURL=https://github.com/Bill-Gray/PDCursesMod/archive/refs/tags/v +CURLPDCSRC=$(PDCURL)$(PDCURSES_VERSION).zip +CURLPDCDST=$(PDCDIST) + +# +# relative directories from root of NetHack tree. +# + +LIBSDIR=lib # libraries and external bits +SUBMSDIR=submodules # NetHack git submodules +ROOTDIR=..\..\..\.. # root of NetHack tree relative to project file +LIBDIR=$(ROOTDIR)\$(LIBSDIR) +SUBMDIR=$(ROOTDIR)\$(SUBMSDIR) + + +default: fetchall + +fetchall: libdir fetch-Lua fetch-pdcurses ..\..\..\..\include\nhlua.h + +fetch-lua: fetch-actual-Lua + +fetch-Lua: fetch-actual-Lua + +fetch-actual-Lua: + cd $(LIBDIR) + curl --insecure -R -O $(CURLLUASRC) + tar zxf lua-$(LUA_VERSION).tar.gz + if exist lua-$(LUA_VERSION).tar.gz del lua-$(LUA_VERSION).tar.gz + cd ..\sys\windows\vs\fetchprereq + @echo Lua has been fetched into $(LIBDIR)\lua-$(LUA_VERSION) + +fetch-pdcurses: + cd $(LIBDIR) + curl --insecure -L -R $(PDCURL)$(PDCURSES_VERSION).zip -o $(PDCDIST).zip + if not exist $(PDCDIST)\*.* mkdir $(PDCDIST) + tar -zxC $(PDCDIST) --strip-components=1 -f $(CURLPDCDST).zip + if exist $(CURLPDCDST).zip del $(CURLPDCDST).zip + cd ..\sys\windows\vs\fetchprereq + @echo $(PDCDIST) has been fetched into $(LIBDIR)\$(PDCDIST) + +..\..\..\..\include\nhlua.h: + @echo /* nhlua.h - generated by Makefile from fetchprereq.nmake */ > $@ + @echo #include "lua.h" >> $@ + @echo LUA_API int (lua_error) (lua_State *L) NORETURN; >> $@ + @echo #include "lualib.h" >> $@ + @echo #include "lauxlib.h" >> $@ + @echo /*nhlua.h*/ >> $@ + +libdir: + @if not exist $(LIBDIR)\*.* echo creating directory $(LIB:\=/) + @if not exist $(LIBDIR)\*.* mkdir $(LIBDIR) + +clean: + @if exist $(LIBDIR)\$(PDCDIST) rmdir /Q $(LIBDIR)\$(PDCDIST) /s + @if exist $(LIBDIR)\lua-$(LUA_VERSION) rmdir /Q $(LIBDIR)\lua-$(LUA_VERSION) /s + @if exist ..\..\..\..\include\nhlua.h del /Q ..\..\..\..\include\nhlua.h + +rebuild: + @if exist $(LIBDIR)\$(PDCDIST) echo nothing to do for lib\$(PDCDIST) + @if exist $(LIBDIR)\lua-$(LUA_VERSION) echo nothing to do for lib\lua-$(LUA_VERSION) diff --git a/sys/windows/vs/FetchPrereq/fetchprereq.vcxproj b/sys/windows/vs/FetchPrereq/fetchprereq.vcxproj new file mode 100644 index 0000000000..650e8ed879 --- /dev/null +++ b/sys/windows/vs/FetchPrereq/fetchprereq.vcxproj @@ -0,0 +1,70 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + 17.0 + {503AE687-C33A-45ED-93AA-83967E176D67} + Win32Proj + 10.0 + + + + Makefile + true + + + Makefile + false + + + Makefile + true + + + Makefile + false + + + + + + + + + + + + echo lib\pdcursesmod and lib\lua-$(LUA_VERSION) are already present + pushd $(vsDir)fetchprereq %26%26 nmake -F fetchprereq.nmake clean %26%26 popd + echo rebuilding lib\pdcursesmod and lib\lua-$(LUA_VERSION) + _DEBUG;$(NMakePreprocessorDefinitions) + + + pushd $(vsDir)fetchprereq %26%26 nmake -F fetchprereq.nmake %26%26 popd + pushd $(vsDir)fetchprereq %26%26 nmake -F fetchprereq.nmake clean %26%26 popd + pushd $(vsDir)fetchprereq %26%26 nmake -F fetchprereq.nmake rebuild %26%26 popd + _DEBUG;$(NMakePreprocessorDefinitions) + + + + + \ No newline at end of file diff --git a/sys/windows/vs/NetHack.sln b/sys/windows/vs/NetHack.sln index c23d675975..3bb69f828b 100644 --- a/sys/windows/vs/NetHack.sln +++ b/sys/windows/vs/NetHack.sln @@ -5,52 +5,109 @@ VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NetHackW", "NetHackW\NetHackW.vcxproj", "{CEC5D360-8804-454F-8591-002184C23499}" ProjectSection(ProjectDependencies) = postProject - {93F10526-209E-41D7-BBEA-775787876895} = {93F10526-209E-41D7-BBEA-775787876895} + {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC} = {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC} + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} = {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} + {1693F852-A207-4348-8223-222C2A7FEEEB} = {1693F852-A207-4348-8223-222C2A7FEEEB} + {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} {63F9B82B-F589-4082-ABE5-D4F0682050AB} = {63F9B82B-F589-4082-ABE5-D4F0682050AB} - {BA3DD34C-04B7-40D0-B373-9329AA9E8945} = {BA3DD34C-04B7-40D0-B373-9329AA9E8945} {642BC75D-ABAF-403E-8224-7C725FD4CB42} = {642BC75D-ABAF-403E-8224-7C725FD4CB42} - {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC} = {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC} + {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}" ProjectSection(ProjectDependencies) = postProject + {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} {BA3DD34C-04B7-40D0-B373-9329AA9E8945} = {BA3DD34C-04B7-40D0-B373-9329AA9E8945} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "makedefs", "makedefs\makedefs.vcxproj", "{BA3DD34C-04B7-40D0-B373-9329AA9E8945}" + ProjectSection(ProjectDependencies) = postProject + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} = {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} + {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "recover", "recover\recover.vcxproj", "{2F35F228-6733-4FE5-9B46-B3AA10D4BC2E}" + ProjectSection(ProjectDependencies) = postProject + {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC} = {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC} + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} = {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} + {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} + {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751} = {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tile2bmp", "tile2bmp\tile2bmp.vcxproj", "{642BC75D-ABAF-403E-8224-7C725FD4CB42}" ProjectSection(ProjectDependencies) = postProject - {BA3DD34C-04B7-40D0-B373-9329AA9E8945} = {BA3DD34C-04B7-40D0-B373-9329AA9E8945} + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} = {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} + {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tilemap", "tilemap\tilemap.vcxproj", "{93F10526-209E-41D7-BBEA-775787876895}" ProjectSection(ProjectDependencies) = postProject - {BA3DD34C-04B7-40D0-B373-9329AA9E8945} = {BA3DD34C-04B7-40D0-B373-9329AA9E8945} + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} = {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} + {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "uudecode", "uudecode\uudecode.vcxproj", "{63F9B82B-F589-4082-ABE5-D4F0682050AB}" ProjectSection(ProjectDependencies) = postProject - {BA3DD34C-04B7-40D0-B373-9329AA9E8945} = {BA3DD34C-04B7-40D0-B373-9329AA9E8945} + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} = {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} + {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NetHack", "NetHack\NetHack.vcxproj", "{609BC774-C6F8-4B2B-AA7D-5B3D0EA95751}" ProjectSection(ProjectDependencies) = postProject - {63F9B82B-F589-4082-ABE5-D4F0682050AB} = {63F9B82B-F589-4082-ABE5-D4F0682050AB} - {BA3DD34C-04B7-40D0-B373-9329AA9E8945} = {BA3DD34C-04B7-40D0-B373-9329AA9E8945} {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC} = {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC} + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} = {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} + {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} + {63F9B82B-F589-4082-ABE5-D4F0682050AB} = {63F9B82B-F589-4082-ABE5-D4F0682050AB} + {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} = {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037} + {BAA70D0F-3EC7-4D10-91F0-974F1F49308B} = {BAA70D0F-3EC7-4D10-91F0-974F1F49308B} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PDCurses", "PDCurses\PDCurses.vcxproj", "{BAA70D0F-3EC7-4D10-91F0-974F1F49308B}" + ProjectSection(ProjectDependencies) = postProject + {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} + EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{477BF231-48E0-4312-AA12-9D8576215489}" ProjectSection(SolutionItems) = preProject NetHackProperties.props = NetHackProperties.props EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Pdcursesgui", "PDCursesGui\Pdcursesgui.vcxproj", "{1693F852-A207-4348-8223-222C2A7FEEEB}" + ProjectSection(ProjectDependencies) = postProject + {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hacklib", "hacklib\hacklib.vcxproj", "{096FD6BB-256A-4E68-9B09-2ACA7C606FF3}" + ProjectSection(ProjectDependencies) = postProject + {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fetchprereq", "FetchPrereq\fetchprereq.vcxproj", "{503AE687-C33A-45ED-93AA-83967E176D67}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "package\package.vcxproj", "{0B53AF9B-E1A4-478B-9246-43A39E8B4027}" + ProjectSection(ProjectDependencies) = postProject + {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC} = {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC} + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} = {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} + {1693F852-A207-4348-8223-222C2A7FEEEB} = {1693F852-A207-4348-8223-222C2A7FEEEB} + {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E} = {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E} + {503AE687-C33A-45ED-93AA-83967E176D67} = {503AE687-C33A-45ED-93AA-83967E176D67} + {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751} = {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751} + {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} + {BA3DD34C-04B7-40D0-B373-9329AA9E8945} = {BA3DD34C-04B7-40D0-B373-9329AA9E8945} + {BAA70D0F-3EC7-4D10-91F0-974F1F49308B} = {BAA70D0F-3EC7-4D10-91F0-974F1F49308B} + {CEC5D360-8804-454F-8591-002184C23499} = {CEC5D360-8804-454F-8591-002184C23499} + 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 @@ -131,6 +188,46 @@ Global {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Release|Win32.Build.0 = Release|Win32 {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Release|x64.ActiveCfg = Release|x64 {BAA70D0F-3EC7-4D10-91F0-974F1F49308B}.Release|x64.Build.0 = Release|x64 + {1693F852-A207-4348-8223-222C2A7FEEEB}.Debug|Win32.ActiveCfg = Debug|Win32 + {1693F852-A207-4348-8223-222C2A7FEEEB}.Debug|Win32.Build.0 = Debug|Win32 + {1693F852-A207-4348-8223-222C2A7FEEEB}.Debug|x64.ActiveCfg = Debug|x64 + {1693F852-A207-4348-8223-222C2A7FEEEB}.Debug|x64.Build.0 = Debug|x64 + {1693F852-A207-4348-8223-222C2A7FEEEB}.Release|Win32.ActiveCfg = Release|Win32 + {1693F852-A207-4348-8223-222C2A7FEEEB}.Release|Win32.Build.0 = Release|Win32 + {1693F852-A207-4348-8223-222C2A7FEEEB}.Release|x64.ActiveCfg = Release|x64 + {1693F852-A207-4348-8223-222C2A7FEEEB}.Release|x64.Build.0 = Release|x64 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|Win32.ActiveCfg = Debug|Win32 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|Win32.Build.0 = Debug|Win32 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|x64.ActiveCfg = Debug|x64 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Debug|x64.Build.0 = Debug|x64 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|Win32.ActiveCfg = Release|Win32 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|Win32.Build.0 = Release|Win32 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|x64.ActiveCfg = Release|x64 + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3}.Release|x64.Build.0 = Release|x64 + {503AE687-C33A-45ED-93AA-83967E176D67}.Debug|Win32.ActiveCfg = Debug|Win32 + {503AE687-C33A-45ED-93AA-83967E176D67}.Debug|Win32.Build.0 = Debug|Win32 + {503AE687-C33A-45ED-93AA-83967E176D67}.Debug|x64.ActiveCfg = Debug|x64 + {503AE687-C33A-45ED-93AA-83967E176D67}.Debug|x64.Build.0 = Debug|x64 + {503AE687-C33A-45ED-93AA-83967E176D67}.Release|Win32.ActiveCfg = Release|Win32 + {503AE687-C33A-45ED-93AA-83967E176D67}.Release|Win32.Build.0 = Release|Win32 + {503AE687-C33A-45ED-93AA-83967E176D67}.Release|x64.ActiveCfg = Release|x64 + {503AE687-C33A-45ED-93AA-83967E176D67}.Release|x64.Build.0 = Release|x64 + {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Debug|Win32.ActiveCfg = Debug|Win32 + {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Debug|Win32.Build.0 = Debug|Win32 + {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Debug|x64.ActiveCfg = Debug|x64 + {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Debug|x64.Build.0 = Debug|x64 + {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Release|Win32.ActiveCfg = Release|Win32 + {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Release|Win32.Build.0 = Release|Win32 + {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Release|x64.ActiveCfg = Release|x64 + {0B53AF9B-E1A4-478B-9246-43A39E8B4027}.Release|x64.Build.0 = Release|x64 + {B6B3CC8A-75FD-479C-AB1C-D80FFF0F5037}.Debug|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 b9c2dad7e4..9ba261c6d0 100644 --- a/sys/windows/vs/NetHack/NetHack.vcxproj +++ b/sys/windows/vs/NetHack/NetHack.vcxproj @@ -6,40 +6,58 @@ {609BC774-C6F8-4B2B-AA7D-5B3D0EA95751} Win32Proj NetHack - 10.0.20348.0 + 10.0 - + - - - - + + + + $(BinDir) $(PDCURSES);%(AdditionalIncludeDirectories) - CURSES_GRAPHICS;CHTYPE_32;PDC_NCMOUSE;%(PreprocessorDefinitions) + CURSES_GRAPHICS;PDC_NCMOUSE;%(PreprocessorDefinitions) - $(ToolsDir) + $(ToolsDir);%(AdditionalLibraryDirectories) PDCurses.lib;%(AdditionalDependencies) + + + $(FMODINCDIR);%(AdditionalIncludeDirectories) + SND_LIB_FMOD;USER_SOUNDS;SND_SOUNDEFFECTS_AUTOMAP;RCWAV;%(PreprocessorDefinitions) + + + $(FMODLIBDIR);%(AdditionalLibraryDirectories) + $(FMODLIBLIB);%(AdditionalDependencies) + + - /Gs /Oi- /w44774 %(AdditionalOptions) + /Gs /Oi- /w44774 /w45262 %(AdditionalOptions) 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 + stdclatest + stdclatest + stdclatest + /Gs /Oi- /w44774 /w45262 %(AdditionalOptions) + /Gs /Oi- /w44774 /w45262 %(AdditionalOptions) + /Gs /Oi- /w44774 /w45262 %(AdditionalOptions) + /Gs /Oi- /w44774 /w45262 %(AdditionalOptions) - kernel32.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;bcrypt.lib;%(AdditionalDependencies) $(SndWavDir);%(AdditionalIncludeDirectories) @@ -55,12 +73,6 @@ - - 4701;4702;4244;4310;4774 - %(AdditionalOptions) /wd4774 - 4701;4702;4244;4310;4774;4324 - 4701;4702;4244;4310;4774;4324 - @@ -69,7 +81,9 @@ + + @@ -93,8 +107,9 @@ + + - @@ -142,12 +157,14 @@ + + @@ -155,8 +172,10 @@ + + @@ -175,6 +194,7 @@ + @@ -197,6 +217,7 @@ + @@ -209,7 +230,7 @@ - + @@ -220,8 +241,11 @@ + + + @@ -245,8 +269,10 @@ + + @@ -285,4 +311,4 @@ - + diff --git a/sys/windows/vs/NetHack/afternethack.proj b/sys/windows/vs/NetHack/afternethack.proj index 16420a3fb0..0fb6c9131a 100644 --- a/sys/windows/vs/NetHack/afternethack.proj +++ b/sys/windows/vs/NetHack/afternethack.proj @@ -2,12 +2,12 @@ - + Inputs="$(SysWindDir)xnethackrc.template;$(DocDir)Guidebook.txt;$(DatDir)license;$(SysWindDir)sysconf.template;$(DocDir)nethack.txt;$(DatDir)symbols;$(WinWin32Dir)record;$(DatDir)opthelp" + Outputs="$(BinDir)xnethackrc.template;$(BinDir)Guidebook.txt;$(BinDir)license;$(BinDir)sysconf.template;$(BinDir)symbols.template;$(BinDir)record;$(BinDir)opthelp"> + - + diff --git a/sys/windows/vs/NetHackPackage.wapproj b/sys/windows/vs/NetHackPackage.wapproj index 861490a2e5..fa91dd8348 100644 --- a/sys/windows/vs/NetHackPackage.wapproj +++ b/sys/windows/vs/NetHackPackage.wapproj @@ -112,25 +112,25 @@ - - NetHackW\.nethackrc.template + + NetHackW\xnethackrc.template - + NetHackW\Guidebook.txt - + NetHackW\license - + NetHackW\nhdat$(VERSION_MAJOR)$(VERSION_MINOR)$(PATCHLEVEL) - + NetHackW\opthelp - + NetHackW\symbols.template - + NetHackW\sysconf.template @@ -141,4 +141,4 @@ - \ No newline at end of file + diff --git a/sys/windows/vs/NetHackProperties.props b/sys/windows/vs/NetHackProperties.props index 451aabf4b2..0e5f108895 100644 --- a/sys/windows/vs/NetHackProperties.props +++ b/sys/windows/vs/NetHackProperties.props @@ -5,10 +5,20 @@ 3 7 0 - 5 - 4 - 4> + $(VERSION_MAJOR).$(VERSION_MINOR).$(PATCHLEVEL) + 5 + 4 + 6> $(LUA_MAJOR_VERSION).$(LUA_MINOR_VERSION).$(LUA_PATCH_LEVEL) + $(ROOTDIR)lib\fmod\api\core\ + fmod_vc.lib + fmod.dll + $(FMODROOT)inc + $(FMODROOT)lib\$(PlatformShortName)\ + $(FMODLIBDIR)$(FMODLIBBASENAME) + $(FMODLIBDIR)$(FMODDLLBASENAME) + $(ROOTDIR)sound\fmod\ + $(FMODDIR)fmod.c true diff --git a/sys/windows/vs/NetHackW/NetHackW.vcxproj b/sys/windows/vs/NetHackW/NetHackW.vcxproj index 0de37b4c73..2e90b82d8e 100644 --- a/sys/windows/vs/NetHackW/NetHackW.vcxproj +++ b/sys/windows/vs/NetHackW/NetHackW.vcxproj @@ -4,32 +4,64 @@ {CEC5D360-8804-454F-8591-002184C23499} NetHackW - 10.0.20348.0 + 10.0 - + - - - + + + $(BinDir) + + + $(PDCURSES);%(AdditionalIncludeDirectories) + CURSES_GRAPHICS;PDC_NCMOUSE;%(PreprocessorDefinitions) + + + $(ToolsDir);%(AdditionalLibraryDirectories) + PDCursesGui.lib;%(AdditionalDependencies) + + + + + $(FMODINCDIR);%(AdditionalIncludeDirectories) + SND_LIB_FMOD;USER_SOUNDS;SND_SOUNDEFFECTS_AUTOMAP;RCWAV;%(PreprocessorDefinitions) + + + $(FMODLIBDIR);%(AdditionalLibraryDirectories) + $(FMODLIBLIB);%(AdditionalDependencies) + + false false + + false + false + /Gs /Oi- /w44774 %(AdditionalOptions) Disabled true $(WinWin32Dir);$(IncDir);$(SysWindDir);$(SysShareDir);$(WinShareDir);$(LuaDir);%(AdditionalIncludeDirectories) - TILES;_WINDOWS;DLB;MSWIN_GRAPHICS;SAFEPROCS;NOTTYGRAPHICS;SND_LIB_WINDSOUND;USER_SOUNDS;HAS_STDINT_H;%(PreprocessorDefinitions) - 4820;4706;4244;4245;4100;4310 + TILES;_WINDOWS;DLB;MSWIN_GRAPHICS;SAFEPROCS;NOTTYGRAPHICS;SND_LIB_WINDSOUND;USER_SOUNDS;HAS_STDINT_H;PDC_WIDE;%(PreprocessorDefinitions) + stdclatest + stdclatest + stdclatest + stdclatest + /Gs /Oi- /w44774 /w45262 %(AdditionalOptions) + /Gs /Oi- /w44774 /w45262 %(AdditionalOptions) + /Gs /Oi- /w44774 /w45262 %(AdditionalOptions) + /Gs /Oi- /w44774 /w45262 %(AdditionalOptions) + 4820;4706;4244;4245;4100;4310;6001 4820;4706;4244;4245;4100;4310 @@ -42,19 +74,13 @@ Windows - comctl32.lib;winmm.lib;bcrypt.lib;%(AdditionalDependencies) + hacklib.lib;lualib.lib;dbghelp.lib;comctl32.lib;winmm.lib;bcrypt.lib;%(AdditionalDependencies) $(WinWin32Dir)xNethackW.exe.manifest;%(AdditionalManifestFiles) - - 4701;4702;4244;4310;4774 - %(AdditionalOptions) /wd4774 - 4701;4702;4244;4310;4774;4324 - 4701;4702;4244;4310;4774;4324 - @@ -63,7 +89,9 @@ + + @@ -87,8 +115,9 @@ + + - @@ -125,7 +154,9 @@ - + + 4820;4706;4244;4245;4100;4310;6001 + @@ -136,12 +167,14 @@ + + @@ -149,13 +182,17 @@ + + - + + 4820;4706;4244;4245;4100;4310;6001 + @@ -170,12 +207,30 @@ + + + + + + + 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 + + @@ -224,7 +279,7 @@ - + @@ -237,6 +292,7 @@ + @@ -269,8 +325,10 @@ + + @@ -292,7 +350,7 @@ - + @@ -315,4 +373,4 @@ - + diff --git a/sys/windows/vs/PDCurses/PDCurses.vcxproj b/sys/windows/vs/PDCurses/PDCurses.vcxproj index 68e21a5733..ab75f18db9 100644 --- a/sys/windows/vs/PDCurses/PDCurses.vcxproj +++ b/sys/windows/vs/PDCurses/PDCurses.vcxproj @@ -1,15 +1,15 @@ - + {BAA70D0F-3EC7-4D10-91F0-974F1F49308B} Win32Proj PDCurses - 10.0.20348.0 + 10.0 - + @@ -90,14 +90,14 @@ Disabled - WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + WIN32;_DEBUG;_LIB;PDC_RGB;%(PreprocessorDefinitions) MultiThreadedDebug Disabled - _DEBUG;_LIB;%(PreprocessorDefinitions) + _DEBUG;_LIB;PDC_RGB;%(PreprocessorDefinitions) @@ -105,7 +105,7 @@ MaxSpeed true true - WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + WIN32;NDEBUG;_LIB;PDC_RGB;%(PreprocessorDefinitions) true @@ -117,7 +117,7 @@ MaxSpeed true true - NDEBUG;_LIB;%(PreprocessorDefinitions) + NDEBUG;_LIB;PDC_RGB;%(PreprocessorDefinitions) true diff --git a/sys/windows/vs/PDCursesGui/pdcursesgui.vcxproj b/sys/windows/vs/PDCursesGui/pdcursesgui.vcxproj new file mode 100644 index 0000000000..1c62705f8e --- /dev/null +++ b/sys/windows/vs/PDCursesGui/pdcursesgui.vcxproj @@ -0,0 +1,122 @@ + + + + + + {1693F852-A207-4348-8223-222C2A7FEEEB} + Win32Proj + PDCursesGui + 10.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + 4996;4244;4267;%(DisableSpecificWarnings) + 4996;4244;4267;%(DisableSpecificWarnings) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(PDCURSES);$(IncludePath) + + + + NotUsing + Level3 + true + true + 4996;4244;%(DisableSpecificWarnings) + MultiThreadedDebug + true + + + Windows + true + + + + + Disabled + WIN32;_DEBUG;_LIB;PDC_WIDE;PDC_RGB;%(PreprocessorDefinitions) + MultiThreadedDebug + + + + + Disabled + _DEBUG;_LIB;PDC_WIDE;PDC_RGB;%(PreprocessorDefinitions) + + + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;PDC_WIDE;PDC_RGB;%(PreprocessorDefinitions) + + + true + true + + + + + MaxSpeed + true + true + NDEBUG;_LIB;PDC_WIDE;PDC_RGB;%(PreprocessorDefinitions) + + + true + true + + + + \ No newline at end of file diff --git a/sys/windows/vs/dirs.props b/sys/windows/vs/dirs.props index b858101f69..dc3f7ca7ba 100644 --- a/sys/windows/vs/dirs.props +++ b/sys/windows/vs/dirs.props @@ -1,8 +1,8 @@ - + + 5.4.6 $(MSBuildProjectDirectory)\..\..\..\..\ - $(RootDir)sys\windows\vs\ $(RootDir)binary\$(Configuration)\$(Platform)\ $(ProjectDir)obj\$(Configuration)\$(Platform)\$(TargetName)\ $(ProjectDir)symbols\$(Configuration)\$(Platform)\$(TargetName)\ @@ -12,7 +12,7 @@ $(RootDir)dat\ $(RootDir)doc\ $(RootDir)include\ - $(RootDir)submodules\lua\ + $(RootDir)lib\ $(RootDir)sound\wav\ $(RootDir)sound\windsound\ $(RootDir)src\ @@ -20,6 +20,7 @@ $(RootDir)util\ $(RootDir)sys\share\ $(RootDir)sys\windows\ + $(SysWindDir)vs\ $(RootDir)win\share\ $(RootDir)win\tty\ $(RootDir)win\win32\ @@ -27,8 +28,14 @@ $(ObjDir) $(RootDir)win\curses\ $(RootDir)submodules\ + $(LibDir)lua-$(LUA_VERSION)\src\ - - $(RootDir)lib\PDCurses\ + + $(LibDir)pdcursesmod\ + $(PDCURSESMOD) + + + $(SubmodulesDir)pdcursesmod\ + $(PDCURSESMOD) diff --git a/sys/windows/vs/dlb/afterdlb.proj b/sys/windows/vs/dlb/afterdlb.proj index 967c181fb4..515473a3a7 100644 --- a/sys/windows/vs/dlb/afterdlb.proj +++ b/sys/windows/vs/dlb/afterdlb.proj @@ -7,10 +7,9 @@ - + - - + diff --git a/sys/windows/vs/dlb/dlb.vcxproj b/sys/windows/vs/dlb/dlb.vcxproj index 215226dd19..71b96d8236 100644 --- a/sys/windows/vs/dlb/dlb.vcxproj +++ b/sys/windows/vs/dlb/dlb.vcxproj @@ -1,40 +1,59 @@  - + {0303A585-3F83-4BB7-AF6B-1E12C8FB54AC} - 10.0.20348.0 + 10.0 - + - + - + - + - + - - - + + + $(IncDir);$(SysWindDir);$(SysShareDir);$(LuaDir);%(AdditionalIncludeDirectories) WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;%(PreprocessorDefinitions) + stdclatest + stdclatest + stdclatest + stdclatest + + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) + + + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) + + + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) + + + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) + - @@ -49,4 +68,4 @@ - \ No newline at end of file + diff --git a/sys/windows/vs/files.props b/sys/windows/vs/files.props index af5bf0c870..97d4e7ff0c 100644 --- a/sys/windows/vs/files.props +++ b/sys/windows/vs/files.props @@ -33,6 +33,7 @@ + diff --git a/sys/windows/vs/hacklib/hacklib.vcxproj b/sys/windows/vs/hacklib/hacklib.vcxproj new file mode 100644 index 0000000000..3be14bb62c --- /dev/null +++ b/sys/windows/vs/hacklib/hacklib.vcxproj @@ -0,0 +1,182 @@ + + + + + + + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + 17.0 + Win32Proj + {096FD6BB-256A-4E68-9B09-2ACA7C606FF3} + hacklib + 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) + stdclatest + /w45262 %(AdditionalOptions) + + + + + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + true + $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) + stdclatest + /w45262 %(AdditionalOptions) + + + + + true + true + true + + + + + Level3 + true + _DEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + true + $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) + stdclatest + /w45262 %(AdditionalOptions) + + + + + true + + + + + Level3 + true + true + true + NDEBUG;_LIB;WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + true + $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) + stdclatest + /w45262 %(AdditionalOptions) + + + + + true + true + true + + + + + + + + + + + + + + + diff --git a/sys/windows/vs/lualib/lualib.vcxproj b/sys/windows/vs/lualib/lualib.vcxproj new file mode 100644 index 0000000000..537be7191f --- /dev/null +++ b/sys/windows/vs/lualib/lualib.vcxproj @@ -0,0 +1,481 @@ + + + + + + + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + 4701;4702;4244;4310;4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + %(AdditionalOptions) /wd4774 + + + + 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 a2c7f2df08..5b3c90584d 100644 --- a/sys/windows/vs/makedefs/makedefs.vcxproj +++ b/sys/windows/vs/makedefs/makedefs.vcxproj @@ -4,39 +4,44 @@ {BA3DD34C-04B7-40D0-B373-9329AA9E8945} - 10.0.20348.0 + 10.0 - + - + - + - + - + - - - + + + $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) WIN32CON;DLB;MSWIN_GRAPHICS;ENUM_PM;HAS_STDINT_H;%(PreprocessorDefinitions) + stdclatest + /w45262 %(AdditionalOptions) + + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) + - - - - - - + + + + + @@ -54,15 +59,6 @@ - - - - - - - - - @@ -72,4 +68,4 @@ - \ No newline at end of file + diff --git a/sys/windows/vs/package/package.nmake b/sys/windows/vs/package/package.nmake new file mode 100644 index 0000000000..fb7f127f65 --- /dev/null +++ b/sys/windows/vs/package/package.nmake @@ -0,0 +1,135 @@ +# NetHack 3.7 package.nmake +#============================================================================== +# +# The version of the game this Makefile was designed for + +!IFNDEF NETHACK_VERSION +NETHACK_VERSION="3.7.0" +!MESSAGE NETHACK_VERSION set to $(NETHACK_VERSION). +!ELSE +!MESSAGE NETHACK_VERSION set to $(NETHACK_VERSION) by caller. +!ENDIF + +# A brief variation for use in macros +NHV=$(NETHACK_VERSION:.=) +NHV=$(NHV:"=) + +# +# relative directories from root of NetHack tree. +# + +#LIBSDIR=lib # libraries and external bits +#SUBMSDIR=submodules # NetHack git submodules +PACKAGESDIR=vspackage # put in vspackage to distinguish +ROOTDIR=..\..\..\.. # root of NetHack tree relative to project file + +# Directories we might have to collect things from +# +INCL = $(ROOTDIR)\include # NetHack include files +DAT = $(ROOTDIR)\dat # NetHack data files +DOC = $(ROOTDIR)\doc # NetHack documentation files +UTIL = $(ROOTDIR)\util # Utility source +SRC = $(ROOTDIR)\src # Main source +SSYS = $(ROOTDIR)\sys\share # Shared system files +MSWSYS = $(ROOTDIR)\sys\windows # MS windows specific files +TTY = $(ROOTDIR)\win\tty # window port files (tty) +MSWIN = $(ROOTDIR)\win\win32 # window port files (win32) +WCURSES = $(ROOTDIR)\win\curses # window port files (curses) +WSHR = $(ROOTDIR)\win\share # Tile support files +QT = $(ROOTDIR)\win\Qt # QT support files +X11 = $(ROOTDIR)\win\X11 # X11 support files +LIBDIR = $(ROOTDIR)\lib # libraries and external bits +SUBMDIR = $(ROOTDIR)\submodules # NetHack git submodules +SndWavDir = $(ROOTDIR)\sound\wav # sound files that get integrated + +# Directories we might place collected things +# +VSBINDIR=$(ROOTDIR)\vsbinary +VSPACKAGEDIR = $(ROOTDIR)\vspackage + + + +default: packageall + +#=============================================================================== +# makefile rules +#=============================================================================== + +# Rules for files in dat +{$(DAT)}.dat{$(VSBINDIR)}.dat: + copy /Y $< $@ + +#=============================================================================== +# packaging +#=============================================================================== + +PKGFILES=Guidebook.txt license NetHack.exe NetHack.txt \ + NetHackW.exe opthelp nhdat370 record symbols.template sysconf.template \ + nethackrc.template +FILESTOZIP=$(VSBINDIR)\Guidebook.txt $(VSBINDIR)\license \ + $(VSBINDIR)\NetHack.exe $(VSBINDIR)\NetHack.txt $(VSBINDIR)\NetHackW.exe \ + $(VSBINDIR)\opthelp $(VSBINDIR)\nhdat370 $(VSBINDIR)\record \ + $(VSBINDIR)\symbols.template $(VSBINDIR)\sysconf.template $(VSBINDIR)\nethackrc.template +DBGSYMS = NetHack.PDB NetHackW.PDB +PDBTOZIP = ..\NetHack\symbols\$(Configuration)\$(Platform)\NetHack.PDB \ + ..\NetHackW\symbols\$(Configuration)\$(Platform)\NetHackW.PDB +MAINZIP = $(VSPACKAGEDIR)\nethack-$(NHV)-win-$(PlatformShortName).zip +DBGSYMZIP = $(VSPACKAGEDIR)\nethack-$(NHV)-win-$(PlatformShortName)-debugsymbols.zip + +packageall: packagezip + +packagezip: showvar vsbindir vspackagedir $(FILESTOZIP) $(MAINZIP) $(DBGSYMZIP) + @echo NetHack Windows package created: $(MAINZIP) + +$(MAINZIP): $(FILESTOZIP) +# if not exist $(VSPACKAGEDIR)\*.* mkdir $(VSPACKAGEDIR) + tar -a -cf $(MAINZIP) -C $(VSBINDIR) $(PKGFILES) + +$(DBGSYMZIP): $(PDBTOZIP) + tar -a -cf $(DBGSYMZIP) $(PDBTOZIP) + +$(VSBINDIR)\license: $(BinDir)\license + copy /Y $(BinDir)\license $@ +$(VSBINDIR)\Guidebook.txt: $(BinDir)\Guidebook.txt + copy /Y $(BinDir)\Guidebook.txt $@ +$(VSBINDIR)\NetHack.exe: $(BinDir)\NetHack.exe + copy /Y $(BinDir)\NetHack.exe $@ +$(VSBINDIR)\NetHack.txt: $(BinDir)\NetHack.txt + copy /Y $(BinDir)\NetHack.txt $@ +$(VSBINDIR)\NetHackW.exe: $(BinDir)\NetHackW.exe + copy /Y $(BinDir)\NetHackW.exe $@ +$(VSBINDIR)\opthelp: $(BinDir)\opthelp + copy /Y $(BinDir)\opthelp $@ +$(VSBINDIR)\nhdat$(NHV): $(BinDir)\nhdat$(NHV) + copy /Y $(BinDir)\nhdat$(NHV) $@ +$(VSBINDIR)\symbols.template: $(BinDir)\symbols.template + copy /Y $(BinDir)\symbols.template $@ +$(VSBINDIR)\nethackrc.template: $(BinDir)\nethackrc.template + copy /Y $(BinDir)\nethackrc.template $@ +$(VSBINDIR)\sysconf.template: $(BinDir)\sysconf.template + copy /Y $(BinDir)\sysconf.template $@ +$(VSBINDIR)\record: + -if not exist $(VSBINDIR)\record. goto>$(VSBINDIR)\record. + +showvar: + @echo BinDir=[$(BinDir)] + @echo Platform=[$(Platform)] + @echo PlatformShortName=[$(PlatformShortName)] + @echo Configuration=[$(Configuration)] + +vspackagedir: + @if not exist $(VSPACKAGEDIR)\*.* echo creating directory $(VSPACKAGEDIR:\=/) + @if not exist $(VSPACKAGEDIR)\*.* mkdir $(VSPACKAGEDIR) + +vsbindir: + @if not exist $(VSBINDIR)\*.* echo creating directory $(VSBINDIR:\=/) + @if not exist $(VSBINDIR)\*.* mkdir $(VSBINDIR) + +clean: +# @if exist $(LIBDIR)\$(PDCDIST) rmdir /Q $(LIBDIR)\$(PDCDIST) /s +# @if exist $(LIBDIR)\lua-$(LUA_VERSION) rmdir /Q $(LIBDIR)\lua-$(LUA_VERSION) /s +# @if exist ..\..\..\..\include\nhlua.h del /Q ..\..\..\..\include\nhlua.h + +rebuild: +# @if exist $(LIBDIR)\$(PDCDIST) echo nothing to do for lib\$(PDCDIST) +# @if exist $(LIBDIR)\lua-$(LUA_VERSION) echo nothing to do for lib\lua-$(LUA_VERSION) diff --git a/sys/windows/vs/package/package.vcxproj b/sys/windows/vs/package/package.vcxproj new file mode 100644 index 0000000000..7b09e9277f --- /dev/null +++ b/sys/windows/vs/package/package.vcxproj @@ -0,0 +1,64 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + 17.0 + {0B53AF9B-E1A4-478B-9246-43A39E8B4027} + Win32Proj + 10.0 + + + + Makefile + true + + + Makefile + false + + + Makefile + true + + + Makefile + false + + + + + + + + + + + + pushd $(vsDir)package %26%26 nmake -F package.nmake BinDir="$(BinDir)\" Platform="$(Platform)" PlatformShortName="$(PlatformShortName)" Configuration="$(Configuration)" NETHACK_VERSION="$(NETHACK_VERSION)" %26%26 popd + pushd $(vsDir)package %26%26 nmake -F package.nmake clean %26%26 popd + pushd $(vsDir)package %26%26 nmake -F package.nmake rebuild %26%26 popd + + + + + diff --git a/sys/windows/vs/recover/recover.vcxproj b/sys/windows/vs/recover/recover.vcxproj index dfd5270441..66fd134d0c 100644 --- a/sys/windows/vs/recover/recover.vcxproj +++ b/sys/windows/vs/recover/recover.vcxproj @@ -1,18 +1,18 @@  - + {2F35F228-6733-4FE5-9B46-B3AA10D4BC2E} - 10.0.20348.0 + 10.0 - + - - - - + + + + $(BinDir) @@ -20,7 +20,13 @@ $(IncDir);$(SysWindDir);$(LuaDir);%(AdditionalIncludeDirectories) WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;%(PreprocessorDefinitions) + stdclatest + /w45262 %(AdditionalOptions) + + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) + @@ -44,4 +50,4 @@ - \ No newline at end of file + diff --git a/sys/windows/vs/tile2bmp/tile2bmp.vcxproj b/sys/windows/vs/tile2bmp/tile2bmp.vcxproj index 745a3c7c5f..8207ebceb4 100644 --- a/sys/windows/vs/tile2bmp/tile2bmp.vcxproj +++ b/sys/windows/vs/tile2bmp/tile2bmp.vcxproj @@ -1,34 +1,40 @@  - + {642BC75D-ABAF-403E-8224-7C725FD4CB42} - 10.0.20348.0 + 10.0 - + - + - + - + - + - - - + + + $(IncDir);$(SysWindDir);$(SysShareDir);$(LuaDir);%(AdditionalIncludeDirectories) WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;%(PreprocessorDefinitions) + stdclatest + /w45262 %(AdditionalOptions) + + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) + @@ -50,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 7d8408ca63..aa34626e5a 100644 --- a/sys/windows/vs/tilemap/tilemap.vcxproj +++ b/sys/windows/vs/tilemap/tilemap.vcxproj @@ -1,34 +1,42 @@  - + {93F10526-209E-41D7-BBEA-775787876895} - 10.0.20348.0 + 10.0 - + - + - + - + - + - - - + + + + + $(IncDir);$(SysWindDir);$(SysShareDir);$(LuaDir);%(AdditionalIncludeDirectories) WIN32CON;DLB;MSWIN_GRAPHICS;HAS_STDINT_H;%(PreprocessorDefinitions) + stdclatest + /w45262 %(AdditionalOptions) + + $(ToolsDir);%(AdditionalLibraryDirectories) + hacklib.lib;%(AdditionalDependencies) + @@ -87,4 +95,4 @@ - \ No newline at end of file + diff --git a/sys/windows/vs/uudecode/uudecode.vcxproj b/sys/windows/vs/uudecode/uudecode.vcxproj index 5f82ba7dbc..5b85f9979e 100644 --- a/sys/windows/vs/uudecode/uudecode.vcxproj +++ b/sys/windows/vs/uudecode/uudecode.vcxproj @@ -1,29 +1,29 @@  - + {63F9B82B-F589-4082-ABE5-D4F0682050AB} - 10.0.20348.0 + 10.0 - + - + - + - + - + - - - + + + $(IncDir);$(SysShareDir); @@ -31,10 +31,7 @@ - 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 @@ -47,4 +44,4 @@ - \ No newline at end of file + diff --git a/sys/windows/win10.h b/sys/windows/win10.h index 22a4c0f12e..cc8a0bf48d 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 ba19ec9125..1089f8b45c 100644 --- a/sys/windows/win32api.h +++ b/sys/windows/win32api.h @@ -3,7 +3,7 @@ /* NetHack may be freely redistributed. See license for details. */ /* - * This header file is used to clear up some discrepencies with Visual C + * This header file is used to clear up some discrepancies with Visual C * header files & NetHack before including windows.h, so all NetHack * files should include "win32api.h" rather than . */ @@ -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 740daa0703..49d357af97 100644 --- a/sys/windows/windmain.c +++ b/sys/windows/windmain.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 windmain.c $NHDT-Date: 1596498320 2020/08/03 23:45:20 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.157 $ */ +/* NetHack 3.7 windmain.c $NHDT-Date: 1693359653 2023/08/30 01:40:53 $ $NHDT-Branch: keni-crashweb2 $:$NHDT-Revision: 1.189 $ */ /* Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -10,8 +10,6 @@ #ifdef DLB #include "dlb.h" #endif -#include -#include #include #include #include @@ -20,14 +18,13 @@ #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); +static void early_options(int argc, char **argv); char *translate_path_variables(const char *, char *); 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 +60,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); @@ -86,7 +84,7 @@ int windows_startup_state = 0; /* we flag whether to continue with this */ extern int redirect_stdout; /* from sys/share/pcsys.c */ extern int GUILaunched; HANDLE hStdOut; -char default_window_sys[] = +char default_window_sys[7] = #if defined(MSWIN_GRAPHICS) "mswin"; #elif defined(TTY_GRAPHICS) @@ -114,350 +112,47 @@ 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); - +void windows_raw_print(const char *str); +extern const char *known_handling[]; /* symbols.c */ +extern const char *known_restrictions[]; /* symbols.c */ +/* --------------------------------------------------------------------------- */ 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) -{ - HRESULT hr = CreateDirectoryA(path, NULL); - - if (FAILED(hr) && hr != 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, "\\xNetHack\\"); - 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) -{ - 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)) - return; - - BOOL success = CopyFileA(src_path, dst_path, TRUE); - if(!success) error("Failed to copy '%s' to '%s'", src_path, dst_path); -} - -/* 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_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); - -// update_file(gf.fqn_prefix[SYSCONFPREFIX], SYMBOLS_TEMPLATE, -// gf.fqn_prefix[DATAPREFIX], SYMBOLS_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); - - update_file(gf.fqn_prefix[SYSCONFPREFIX], SYMBOLS, - gf.fqn_prefix[DATAPREFIX], SYMBOLS, TRUE); - -} - -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 .xnethackrc or sysconf */ - copy_file(gf.fqn_prefix[CONFIGPREFIX], CONFIG_FILE, - gf.fqn_prefix[DATAPREFIX], CONFIG_TEMPLATE); -} - -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. + * 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. + * */ -DISABLE_WARNING_UNREACHABLE_CODE - -#if defined(__MINGW32__) && defined(MSWIN_GRAPHICS) -#define MAIN mingw_main +#if defined(MSWIN_GRAPHICS) +#define MAIN nethackw_main +int nethackw_main(int, char **); #else #define MAIN main #endif @@ -471,6 +166,9 @@ MAIN(int argc, char *argv[]) char fnamebuf[BUFSZ], encodedfnamebuf[BUFSZ]; char failbuf[BUFSZ]; int getlock_result = 0; + HWND hwnd; + HDC hdc; + int bpp; #ifdef _MSC_VER _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); @@ -484,7 +182,19 @@ MAIN(int argc, char *argv[]) safe_routines(); #endif /* WIN32CON */ - early_init(); +#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(); + hdc = GetDC(hwnd); + if (hdc) { + bpp = GetDeviceCaps(hdc, BITSPIXEL); + iflags.colorcount = (bpp >= 16) ? 16777216 : (bpp >= 8) ? 256 : 16; + ReleaseDC(hwnd, hdc); + } + #ifdef _MSC_VER #ifdef DEBUG /* set these appropriately for VS debugging */ @@ -517,19 +227,21 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ if (getcwd(orgdir, sizeof orgdir) == (char *) 0) error("xNetHack: current directory path too long"); #endif - + initoptions_init(); // This allows OPTIONS in syscf on Windows. set_default_prefix_locations(argv[0]); #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) chdir(gf.fqn_prefix[HACKPREFIX]); #endif - if (GUILaunched || IsDebuggerPresent()) - getreturn_enabled = TRUE; + /* if (GUILaunched || IsDebuggerPresent()) */ + getreturn_enabled = TRUE; check_recordfile((char *) 0); iflags.windowtype_deferred = TRUE; copy_sysconf_content(); + copy_symbols_content(); + early_options(argc, argv); initoptions(); /* Now that sysconf has had a chance to set the TROUBLEPREFIX, don't @@ -537,7 +249,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) { @@ -560,9 +271,11 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ /* * It seems you really want to play. */ +#ifndef CURSES_GRAPHICS if (argc >= 1 && !strcmpi(default_window_sys, "mswin") && (strstri(argv[0], "xnethackw.exe") || GUILaunched)) iflags.windowtype_locked = TRUE; +#endif windowtype = default_window_sys; #ifdef DLB @@ -582,7 +295,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ #if defined(TTY_GRAPHICS) Strcpy(default_window_sys, "tty"); #else -#if defined(CURSES_GRAPHICS) +#if defined(CURSES_GRAPHICS) && !defined(MSWIN_GRAPHICS) Strcpy(default_window_sys, "curses"); #endif /* CURSES */ #endif /* TTY */ @@ -590,8 +303,9 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ windowtype = gc.chosen_windowtype; } choose_windows(windowtype); - -#if defined(SND_LIB_WINDSOUND) +#if defined(SND_LIB_FMOD) + assign_soundlib(soundlib_fmod); +#elif defined(SND_LIB_WINDSOUND) assign_soundlib(soundlib_windsound); #endif @@ -606,6 +320,9 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ if (WINDOWPORT(tty)) consoletty_open(1); #endif +#ifdef WINCHAIN + commit_windowchain(); +#endif init_nhwindows(&argc, argv); @@ -641,21 +358,21 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ (*utf8graphics_mode_callback)(); #endif - /* strip role,race,&c suffix; calls askname() if gp.plname[] is empty + /* strip role,race,&c suffix; calls askname() if svp.plname[] is empty or holds a generic user name like "player" or "games" */ plnamesuffix(); - set_playmode(); /* sets gp.plname to "wizard" for wizard mode */ + set_playmode(); /* sets svp.plname to "wizard" for wizard mode */ /* until the getlock code is resolved, override askname()'s setting of renameallowed; when False, player_selection() won't resent renaming as an option */ iflags.renameallowed = FALSE; /* Obtain the name of the logged on user and incorporate * it into the name. */ - Sprintf(fnamebuf, "%s", gp.plname); + Sprintf(fnamebuf, "%s", svp.plname); (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); @@ -669,8 +386,8 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ if (!nhfp) { raw_print("Cannot create lock file"); } else { - gh.hackpid = GetCurrentProcessId(); - write(nhfp->fd, (genericptr_t) &gh.hackpid, sizeof(gh.hackpid)); + svh.hackpid = GetCurrentProcessId(); + (void) write(nhfp->fd, (genericptr_t) &svh.hackpid, sizeof(svh.hackpid)); close_nhfile(nhfp); } /* @@ -708,8 +425,8 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ } } } - if (gp.program_state.in_self_recover) { - gp.program_state.in_self_recover = FALSE; + if (program_state.in_self_recover) { + program_state.in_self_recover = FALSE; set_savefile_name(TRUE); } } @@ -744,23 +461,18 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ 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) { @@ -780,13 +492,18 @@ process_options(int argc, char * argv[]) argc--; argv++; } +#if defined(CRASHREPORT) + 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 * says -d directory, hope nobody's using -desomething_else */ argc--; argv++; - const char * dir = argv[0] + 2; + const char *dir = argv[0] + 2; if (*dir == '=' || *dir == ':') dir++; if (!*dir && argc > 1) { @@ -855,11 +572,12 @@ process_options(int argc, char * argv[]) #endif case 'u': if (argv[0][2]) - (void) strncpy(gp.plname, argv[0] + 2, sizeof(gp.plname) - 1); + (void) strncpy(svp.plname, argv[0] + 2, + sizeof(svp.plname) - 1); else if (argc > 1) { argc--; argv++; - (void) strncpy(gp.plname, argv[0], sizeof(gp.plname) - 1); + (void) strncpy(svp.plname, argv[0], sizeof(svp.plname) - 1); } else raw_print("Player name expected after -u"); break; @@ -911,7 +629,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); @@ -956,6 +675,365 @@ nhusage(void) #undef ADD_USAGE } +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, "\\xNetHack\\"); + 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); + } + 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 .xnethackrc 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) @@ -984,11 +1062,18 @@ port_help(void) boolean authorize_wizard_mode(void) { - if (!strcmp(gp.plname, WIZARD_NAME)) + if (!strcmp(svp.plname, WIZARD_NAME)) return TRUE; return FALSE; } +/* similar to above, validate explore mode access */ +boolean +authorize_explore_mode(void) +{ + return TRUE; /* no restrictions on explore mode */ +} + #define PATH_SEPARATOR '\\' #if defined(WIN32) && !defined(WIN32CON) @@ -1023,16 +1108,16 @@ 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()); /* rval = SetStdHandle(STD_OUTPUT_HANDLE, hWrite); */ - freopen("CON", "w", stdout); - freopen("CON", "r", stdin); + (void) freopen("CON", "w", stdout); + (void) freopen("CON", "r", stdin); } has_fakeconsole = TRUE; } @@ -1094,7 +1179,7 @@ windows_exepath(void) } char * -translate_path_variables(const char* str, char* buf) +translate_path_variables(const char *str, char *buf) { const char *src; char evar[BUFSZ], *dest, *envp, *eptr = (char *) 0; @@ -1148,7 +1233,7 @@ translate_path_variables(const char* str, char* buf) /*ARGSUSED*/ void -windows_raw_print(const char* str) +windows_raw_print(const char *str) { if (str) fprintf(stdout, "%s\n", str); @@ -1158,7 +1243,7 @@ windows_raw_print(const char* str) /*ARGSUSED*/ void -windows_raw_print_bold(const char* str) +windows_raw_print_bold(const char *str) { windows_raw_print(str); return; @@ -1186,24 +1271,26 @@ windows_nh_poskey(int *x UNUSED, int *y UNUSED, int *mod UNUSED) /*ARGSUSED*/ char -windows_yn_function(const char* query UNUSED, const char* resp UNUSED, +windows_yn_function(const char *query UNUSED, const char *resp UNUSED, char def UNUSED) { return '\033'; } /*ARGSUSED*/ +#if 0 static void -windows_getlin(const char* prompt UNUSED, char* outbuf) +windows_getlin(const char *prompt UNUSED, char *outbuf) { Strcpy(outbuf, "\033"); } +#endif #ifdef PC_LOCKING static int eraseoldlocks(void) { - register int i; + int i; /* cannot use maxledgerno() here, because we need to find a lock name * before starting everything (including the dungeon initialization @@ -1342,8 +1429,8 @@ getlock(void) gf.fqn_prefix[LEVELPREFIX]); raw_print(oops); } else { - if (write(fd, (char *) &gh.hackpid, sizeof(gh.hackpid)) - != sizeof(gh.hackpid)) { + if (write(fd, (char *) &svh.hackpid, sizeof(svh.hackpid)) + != sizeof(svh.hackpid)) { #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) chdirx(orgdir, 0); #endif @@ -1361,7 +1448,7 @@ getlock(void) #endif /* PC_LOCKING */ boolean -file_exists(const char* path) +file_exists(const char *path) { struct stat sb; @@ -1380,10 +1467,10 @@ RESTORE_WARNING_UNREACHABLE_CODE does not exist, it returns TRUE. */ boolean -file_newer(const char* a_path, const char* b_path) +file_newer(const char *a_path, const char *b_path) { - struct stat a_sb; - struct stat b_sb; + struct stat a_sb = { 0 }; + struct stat b_sb = { 0 }; double timediff; if (stat(a_path, &a_sb)) @@ -1407,7 +1494,7 @@ file_newer(const char* a_path, const char* b_path) int tty_self_recover_prompt(void) { - register int c, ci, ct, pl, retval = 0; + int c, ci, ct, pl, retval = 0; /* for saving/replacing functions, if needed */ struct window_procs saved_procs = {0}; @@ -1475,7 +1562,7 @@ tty_self_recover_prompt(void) int other_self_recover_prompt(void) { - register int c, ci, ct, pl, retval = 0; + int c, ci, ct, pl, retval = 0; boolean ismswin = WINDOWPORT(mswin), iscurses = WINDOWPORT(curses); diff --git a/sys/windows/windsys.c b/sys/windows/windsys.c index 2c92217e0e..d1e32e5187 100644 --- a/sys/windows/windsys.c +++ b/sys/windows/windsys.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 windsys.c $NHDT-Date: 1596498321 2020/08/03 23:45:21 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.64 $ */ +/* NetHack 3.7 windsys.c $NHDT-Date: 1710949760 2024/03/20 15:49:20 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.95 $ */ /* Copyright (c) NetHack PC Development Team 1993, 1994 */ /* NetHack may be freely redistributed. See license for details. */ @@ -20,11 +20,13 @@ #ifndef __BORLANDC__ #include #endif -#include #ifdef TTY_GRAPHICS #include "wintty.h" #endif +#include + #ifdef WIN32 +#include /* * The following WIN32 API routines are used in this file. @@ -50,10 +52,11 @@ int redirect_stdout; #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 @@ -75,7 +78,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 @@ -89,7 +91,7 @@ VA_DECL(const char *, fmt) VA_END(); return; } -#endif +#endif /* WIN32CON */ char switchar(void) @@ -99,7 +101,7 @@ switchar(void) } long -freediskspace(char* path) +freediskspace(char *path) { char tmppath[4]; DWORD SectorsPerCluster = 0; @@ -120,7 +122,7 @@ freediskspace(char* path) * Functions to get filenames using wildcards */ int -findfirst(char* path) +findfirst(char *path) { if (ffhandle) { FindClose(ffhandle); @@ -143,7 +145,7 @@ foundfile_buffer(void) } long -filesize(char* file) +filesize(char *file) { if (findfirst(file)) { return ((long) ffd.nFileSizeLow); @@ -155,13 +157,13 @@ filesize(char* file) * Chdrive() changes the default drive. */ void -chdrive(char* str) +chdrive(char *str) { char *ptr; char drive; if ((ptr = strchr(str, ':')) != (char *) 0) { drive = toupper((uchar) *(ptr - 1)); - _chdrive((drive - 'A') + 1); + (void) _chdrive((drive - 'A') + 1); } } @@ -229,8 +231,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); @@ -325,6 +329,7 @@ interject_assistance(int num, int interjection_type, genericptr_t ptr1, genericp } } break; } + nhUse(interjection_type); } void @@ -388,34 +393,40 @@ void port_insert_pastebuf(char *buf) /* Housekeeping need: +GlobalUnlock(hglbCopy), GlobalFree(hglbCopy), CloseClipboard(), free(w) */ - memcpy(lpwstrCopy, w, abytes); - GlobalUnlock(hglbCopy); - /* Housekeeping need: GlobalFree(hglbCopy), CloseClipboard(), free(w) */ + if (lpwstrCopy) { + memcpy(lpwstrCopy, w, abytes); + GlobalUnlock(hglbCopy); + /* Housekeeping need: GlobalFree(hglbCopy), CloseClipboard(), free(w) + */ - /* put it on the clipboard */ - hresult = SetClipboardData(CF_UNICODETEXT, hglbCopy); - if (!hresult) { - raw_printf("Error copying to clipboard.\n"); - GlobalFree(hglbCopy); /* only needed if clipboard didn't accept data */ + /* put it on the clipboard */ + hresult = SetClipboardData(CF_UNICODETEXT, hglbCopy); + if (!hresult) { + raw_printf("Error copying to clipboard.\n"); + GlobalFree( + hglbCopy); /* only needed if clipboard didn't accept data */ + } + /* Housekeeping need: CloseClipboard(), free(w) */ } - /* Housekeeping need: CloseClipboard(), free(w) */ - CloseClipboard(); free(w); return; } #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 @@ -428,7 +439,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); @@ -443,8 +454,9 @@ GetConsoleHwnd(void) /* printf("%d iterations\n", iterations); */ return hwndFound; } +#endif /* 0 */ #endif /* WIN32CON */ -#endif +#endif /* RUNTIME_PASTEBUF_SUPPORT */ #ifdef RUNTIME_PORT_ID /* @@ -475,6 +487,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) { @@ -491,12 +507,18 @@ nethack_exit(int code) /* use our custom version which works a little cleaner than the stdio one */ windowprocs.win_nhgetch = windows_console_custom_nhgetch; - } + } else #endif if (getreturn_enabled) { raw_print("\n"); - wait_synch(); + if (iflags.window_inited) + wait_synch(); } + /* frees some status tracking data */ + genl_status_finish(); +#ifdef MSWIN_GRAPHICS + free_winmain_stuff(); +#endif exit(code); } @@ -525,7 +547,8 @@ getreturn(const char *str) raw_print(buf); if (WINDOWPORT(tty)) windows_console_custom_nhgetch(); - wait_synch(); + else + wait_synch(); in_getreturn = FALSE; return; } @@ -617,30 +640,37 @@ BOOL winos_font_support_cp437(HFONT hFont) HDC hdc = GetDC(NULL); HFONT oldFont = SelectObject(hdc, hFont); - DWORD size = GetFontUnicodeRanges(hdc, NULL); - GLYPHSET *glyphSet = (GLYPHSET *) malloc(size); - - if (glyphSet != NULL) { - GetFontUnicodeRanges(hdc, glyphSet); - - allFound = TRUE; - for (int i = 0; i < 256 && allFound; i++) { - WCHAR wc = cp437[i]; - BOOL found = FALSE; - for (DWORD j = 0; j < glyphSet->cRanges && !found; j++) { - WCHAR first = glyphSet->ranges[j].wcLow; - WCHAR last = first + glyphSet->ranges[j].cGlyphs - 1; - if (wc >= first && wc <= last) - found = TRUE; + DWORD size = (size_t) GetFontUnicodeRanges(hdc, NULL); + if (size) { + GLYPHSET *glyphSet = (GLYPHSET *) malloc((size_t) size); + if (glyphSet != NULL) { +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 6386 ) +#endif + size = GetFontUnicodeRanges(hdc, glyphSet); +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + allFound = TRUE; + for (int i = 0; i < 256 && allFound; i++) { + WCHAR wc = cp437[i]; + BOOL found = FALSE; + for (DWORD j = 0; j < glyphSet->cRanges && !found; j++) { + WCHAR first = glyphSet->ranges[j].wcLow; + WCHAR last = first + glyphSet->ranges[j].cGlyphs - 1; + + if (wc >= first && wc <= last) + found = TRUE; + } + if (!found) + allFound = FALSE; } - if (!found) - allFound = FALSE; - } - free(glyphSet); + free(glyphSet); + } } - SelectObject(hdc, oldFont); ReleaseDC(NULL, hdc); @@ -722,7 +752,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"; @@ -756,6 +786,298 @@ nt_assert_failed(const char *expression, const char *filepath, int line) expression, filename, line); } +/* Windows helpers for CRASHREPORT etc */ +#ifdef CRASHREPORT +struct CRctxt { + BCRYPT_ALG_HANDLE bah; + BCRYPT_HASH_HANDLE bhh; + PBYTE pbhashobj; + DWORD cbhashobj; /* temp */ + DWORD cbhash; /* hash length */ + DWORD cbdata; /* temp */ + PBYTE pbhash; /* binary hash */ + NTSTATUS st; +} ctxp_ = { NULL, NULL, NULL, 0, 0, 0, NULL, 0 }; +struct CRctxt *ctxp = &ctxp_; // XXX should this now be in gc.* ? + +#define win32err(fn) errname = (char *) fn; goto error + +int +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 + * panic due to a bug in the CRASHREPORT code. */ + impossible("win_cr_helper bad cmd %d", cmd); + return 1; + case 'D': { + char *bidstr = (char *) p; + wchar_t lbidstr[40]; // sizeof(bid), but const + swprintf_s(lbidstr, 40, L"%S", bidstr); + // XXX TODO: need something that will allow copy of just the bid + return MessageBoxW(NULL, lbidstr, L"bidshow", MB_SETFOREGROUND); + } + break; + case 'i': /* HASH_INIT(contextp) */ + if (!IsWindowsVistaOrGreater()) + return 1; // CNG not available. + 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 > (contextp->st = BCryptOpenAlgorithmProvider( + &contextp->bah, BCRYPT_MD4_ALGORITHM, NULL, 0))) { + win32err("BCryptOpenAlgorithmProvider"); + }; + if (0 > (contextp->st = + BCryptGetProperty(contextp->bah, BCRYPT_OBJECT_LENGTH, + (unsigned char *) &contextp->cbhashobj, + sizeof(DWORD), &contextp->cbdata, 0))) { + win32err("BCryptGetProperty1"); + }; + if (0 + == (contextp->pbhashobj = + HeapAlloc(GetProcessHeap(), 0, contextp->cbhashobj))) { + win32err("HeapAlloc1"); + }; + if (0 > (contextp->st = BCryptGetProperty( + contextp->bah, BCRYPT_HASH_LENGTH, (PBYTE) &contextp->cbhash, + sizeof(DWORD), &contextp->cbdata, 0))) { + win32err("BCryptGetProperty2"); + } + if (0 + == (contextp->pbhash = + HeapAlloc(GetProcessHeap(), 0, contextp->cbhash))) { + + win32err("HeapAlloc2\n"); + } + if (0 > BCryptCreateHash(contextp->bah, &contextp->bhh, contextp->pbhashobj, + contextp->cbhashobj, NULL, 0, 0)) { + win32err("BCryptCreateHash"); + } + break; + case 'u': /* HASH_UPDATE(contextp, ptr, len) */ + if (0 > (contextp->st = BCryptHashData(contextp->bhh, p, d, 0))) { + win32err("BCryptHashData"); + } + break; + case 'f': /* HASH_FINISH(contextp) */ + if (0 > BCryptFinishHash(contextp->bhh, contextp->pbhash, contextp->cbhash, 0)) { + win32err("BCryptFinishHash"); + } + break; + case 'c': /* HASH_CLEANUP(contextp) */ + if (contextp->bah) { + BCryptCloseAlgorithmProvider(contextp->bah, 0); + } + if (contextp->bhh) { + BCryptDestroyHash(contextp->bhh); + } + if (contextp->pbhashobj) { + HeapFree(GetProcessHeap(), 0, contextp->pbhashobj); + } + if (contextp->pbhash) { + HeapFree(GetProcessHeap(), 0, contextp->pbhash); + } + break; + 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 + // HASH_CLEANUP). + // NB: assumes !longPathAware in manifest (Win10+) + { + static char binfile[MAX_PATH]; + DWORD rv = GetModuleFileNameA(NULL, binfile, sizeof(binfile)); + if (rv == 0 || rv == sizeof(binfile)) + return 1; +#if (NH_DEVEL_STATUS == NH_STATUS_BETA) + printf("FILE '%s'\n", binfile); +#endif + *(unsigned char **) p = (unsigned char *) binfile; + return 0; + } + } + return 0; /* ok */ +error: + raw_printf("WIN32 function %s failed: status=%" PRIx64 "\n", + errname, (uint64)contextp->st); + return 1; /* fail */ +} +#undef win32err + + +#include +#define MAX_SYM_SIZE 100 +#ifdef __GNUC__ + // gcc can't generate .pdb files. llvm can almost do it. + // For these platforms, use github/ianlancetaylor/libbacktrace. +// XXX this doesn't work yet - we get correct addresses but no symbol info +// XXX so still needs cleanup +// XXX no mark (overflow held to last valid segment) handling yet +// XXX libbacktrace isn't available by default, so don't try until it works +//#define USE_BACKTRACE +#ifdef USE_BACKTRACE +#include + +struct userstate { + int error_count; + int good_count; + char *out; + int outsize; + int maxframes; +} userstate; + +//backtrace_full_callback +static int +btfcb_fn(void *us0, uintptr_t pc, const char *filename, + int lineno, const char *fnname){ + struct userstate *us = us0; + //XXX generate a stack frame line +printf("C: pc=%llx f=%s line=%d fn=%s\n",pc,filename,lineno,fnname); + us->good_count++; + return 0; +} + +//backtrace_error_callback +static void +btecb_fn(void *us0, const char *msg, int errnum){ + struct userstate *us = us0; + us->error_count++; + if(errnum < 0){ +printf("E1: M=%s e=%d\n",msg,errnum); + // XXX save error message + } else { +printf("E2: M=%s e=%d\n",msg,errnum); + // errnum is an errno + //XXX save error message with strerror + } +} +#endif + +#ifdef USE_BACKTRACE +#define USED_IF_BACKTRACE +#else +#define USED_IF_BACKTRACE UNUSED +#endif + +int +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; + userstate.out = out; + userstate.outsize = outsize; + userstate.maxframes = maxframes; + static char binfile[MAX_PATH];// assumes !longPathAware in manifest (Win10+) + DWORD rv = GetModuleFileNameA(NULL, binfile, sizeof(binfile)); + struct backtrace_state *state + = backtrace_create_state(binfile, 0, btecb_fn, &userstate); + if(!state) return userstate.error_count + userstate.good_count; + rv=backtrace_full(state, 0, btfcb_fn, btecb_fn, &userstate); +printf("rv=%d\n",rv); + // XXX rv not checked + // XXX this API leaks its memory; there is no free function + return userstate.error_count + userstate.good_count; +#else + return 0; +#endif +} +#else +// Use win32 API with Visual Studio (and probably MSVC). +#include + +int +win32_cr_gettrace(int maxframes, char *out, int outsize){ + char *mark = out; + void *frames[200]; // XXX why does VS fail var array? wrong C std? + int x; + int tmp; +#define BSIZE (MAX_SYM_SIZE+50) + char buf[BSIZE]; + HANDLE me = GetCurrentProcess(); + SetLastError(0); + // XXX may need to pass the binary's dir + //XXX check for different flags + if(!SymInitialize(me, NULL, TRUE)){ + snprintf(buf, BSIZE, "no stack trace: SymInitialize: %d\n", + (int) GetLastError()); + return 1; + } + int fcount = CaptureStackBackTrace(0, maxframes,frames,NULL); + if(!fcount)goto finish; + char symbol_info_space[sizeof(SYMBOL_INFO)+MAX_SYM_SIZE]; + SYMBOL_INFO *si = (SYMBOL_INFO *)symbol_info_space; + si->MaxNameLen = MAX_SYM_SIZE; + si->SizeOfStruct = sizeof(SYMBOL_INFO); + + for(x=0;xName[0], (long long int)disp64); + if(swr_add_uricoded(buf, &out, &outsize, mark)) + goto finish; + +#if 1 +// XXX does this block do anything useful? + DWORD disp = (DWORD) disp64; + IMAGEHLP_LINE ihl; + ihl.SizeOfStruct = sizeof(IMAGEHLP_LINE); + if (SymGetLineFromAddr(me, adr, &disp, &ihl)) { + printf("L=%d\n", (int) ihl.LineNumber); + } else { +// 7e/1e7 - no info. May need to call SymLoadModule if we need those addrs +// BUT probably system code, so we don't care - experiment +// printf("SGLFA failed: $%08x\n", GetLastError()); + } +//XXXnow format the line +#endif + } else { + // Error 487 (invalid address) seems to mean + // "I can't find any info for this address". + tmp = snprintf(buf, BSIZE, "%d %p (error %d)\n", + x, frames[x], (int) GetLastError()); + if(swr_add_uricoded(buf, &out, &outsize, mark)) + goto finish; + } + if(tmp < 0 || tmp >= outsize){ // XXX is test now wrong? +//printf("FAIL tmp=%d\n",tmp); + fcount = x+1; + goto finish; + } + mark = out; + } +finish: + SymCleanup(me); + return fcount; // XXX if output truncated, fcount could be wrong +} +#endif + +int * +win32_cr_shellexecute(const char *url){ +//XXX Docs say to do this, but has so many caveats I'm going to try skipping it. +//CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + int *rv = (int*)ShellExecuteA(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL); + return rv; +} +#endif /* CRASHREPORT */ + #endif /* WIN32 */ /*windsys.c*/ diff --git a/sys/windows/.xnethackrc.template b/sys/windows/xnethackrc.template similarity index 97% rename from sys/windows/.xnethackrc.template rename to sys/windows/xnethackrc.template index 09ba3a4eba..23d043cbef 100644 --- a/sys/windows/.xnethackrc.template +++ b/sys/windows/xnethackrc.template @@ -74,7 +74,7 @@ MENUCOLOR=" cursed " = red # 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 @@ -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. @@ -233,3 +229,7 @@ OPTIONS=hilite_status:gold/up/yellow/down/brown # St, Dx, Co, In, Wi, Ch OPTIONS=hilite_status:characteristics/up/green/down/red +# CRASHREPORTURL must be set in syscf to enable these options. +# These identify you in crash reports +#OPTIONS=crash_name:Your Name +#OPTIONS=crash_email:user@example.com diff --git a/test/testwish.lua b/test/testwish.lua index 33a524ade4..9bb091802b 100644 --- a/test/testwish.lua +++ b/test/testwish.lua @@ -60,6 +60,9 @@ local wishtest_objects = { ["blessed fireproof +2 pair of speed boots"] = { otyp_name = "speed boots", oclass = "[", oerodeproof = 1, blessed = 1, spe = 2 }, ["tooled horn"] = { otyp_name = "tooled horn", oclass = "(" }, ["meat ring"] = { otyp_name = "meat ring", oclass = "%" }, + ["cursed +3 ring of increase accuracy"] = { otyp_name = "increase accuracy", oclass = "=", spe = 3, cursed = 1 }, + ["ring of accuracy"] = { otyp_name = "increase accuracy", oclass = "=" }, + ["accuracy"] = { otyp_name = "increase accuracy", oclass = "=" }, ["beartrap"] = { otyp_name = "beartrap", oclass = "(" }, ["bear trap"] = { otyp_name = "beartrap", oclass = "(" }, ["landmine"] = { otyp_name = "land mine", oclass = "(" }, @@ -103,3 +106,4 @@ for str, tbl in pairs(wishtest_objects) do end end end +pline("testwish: OK"); diff --git a/util/.gitignore b/util/.gitignore index 40e8248dde..6852dfc877 100644 --- a/util/.gitignore +++ b/util/.gitignore @@ -23,8 +23,10 @@ tile2img.ttp xpm2ppm.ttp nethack.tags readtags +lua-*.dll # dev tool for analyzing data collected by nethack's #define MONITOR_HEAP heaputil heaputil.c tilemappings.lst +clang_rt.asan*.dll diff --git a/util/dlb_main.c b/util/dlb_main.c index e24f4e0d92..7b5bb05634 100644 --- a/util/dlb_main.c +++ b/util/dlb_main.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 dlb_main.c $NHDT-Date: 1629969943 2021/08/26 09:25:43 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.18 $ */ +/* NetHack 3.7 dlb_main.c $NHDT-Date: 1706213798 2024/01/25 20:16:38 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.27 $ */ /* Copyright (c) Kenneth Lorber, Bethesda, Maryland, 1993. */ /* NetHack may be freely redistributed. See license for details. */ @@ -7,6 +7,8 @@ #include "config.h" #include "dlb.h" +#include "hacklib.h" + #if !defined(O_WRONLY) && !defined(MAC) && !defined(AZTEC_C) #include #endif @@ -16,10 +18,7 @@ ATTRNORETURN static void xexit(int) NORETURN; ATTRNORETURN extern void panic(const char *, ...) NORETURN; -char *eos(char *); /* also used by dlb.c */ -FILE *fopen_datafile(const char *, const char *); -unsigned FITSuint_(unsigned long long, const char *, int); -unsigned Strlen_(const char *, const char *, int); +FILE *fopen_datafile(const char *, const char *, int); #ifdef DLB #ifdef DLBLIB @@ -135,19 +134,11 @@ 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); } -char * -eos(char *s) -{ - while (*s) - s++; - return s; -} - #ifdef DLB #define UNUSED_if_no_DLB /*empty*/ #else @@ -168,10 +159,12 @@ main(int argc UNUSED_if_no_DLB, char **argv UNUSED_if_no_DLB) char action = ' '; library lib; - if (argc > 0 && argv[0] && *argv[0]) + if (argc > 0) progname = argv[0]; + if (!progname || !*progname) + progname = default_progname; #ifdef VMS - progname = vms_basename(progname); + progname = vms_basename(progname, FALSE); #endif if (argc < 2) { @@ -365,8 +358,7 @@ main(int argc UNUSED_if_no_DLB, char **argv UNUSED_if_no_DLB) for (; ap < argc; ap++, nfiles++) { if (nfiles == ldlimit) grow_ld(&ld, &ldlimit, DLB_FILES_ALLOC / 5); - ld[nfiles].fname = (char *) alloc(Strlen(argv[ap]) + 1); - Strcpy(ld[nfiles].fname, argv[ap]); + ld[nfiles].fname = dupstr(argv[ap]); } } @@ -383,8 +375,7 @@ main(int argc UNUSED_if_no_DLB, char **argv UNUSED_if_no_DLB) if (nfiles == ldlimit) grow_ld(&ld, &ldlimit, DLB_FILES_ALLOC / 5); *(eos(buf) - 1) = '\0'; /* strip newline */ - ld[nfiles].fname = (char *) alloc((int)strlen(buf) + 1); - Strcpy(ld[nfiles].fname, buf); + ld[nfiles].fname = dupstr(buf); } fclose(list); } @@ -407,7 +398,7 @@ main(int argc UNUSED_if_no_DLB, char **argv UNUSED_if_no_DLB) ld[i].fsize = lseek(fd, 0, SEEK_END); ld[i].foffset = flen; - slen += strlen(ld[i].fname); /* don't add null (yet) */ + slen += (long) strlen(ld[i].fname); /* don't add null (yet) */ flen += ld[i].fsize; close(fd); } @@ -420,10 +411,10 @@ main(int argc UNUSED_if_no_DLB, char **argv UNUSED_if_no_DLB) xexit(EXIT_FAILURE); } - /* caculate directory size */ + /* calculate directory size */ dir_size = 40 /* header line (see below) */ + ((nfiles + 1) * 11) /* handling+file offset+SP+newline */ - + slen + strlen(DLB_DIRECTORY); /* file names */ + + slen + (long) strlen(DLB_DIRECTORY); /* file names */ /* write directory */ write_dlb_directory(out, nfiles, ld, slen, dir_size, flen); @@ -550,16 +541,4 @@ xexit(int retcd) /*NOTREACHED*/ } -/* from hacklib.c */ -unsigned -Strlen_(const char *str, const char *file, int line) -{ - size_t len = strnlen(str, LARGEST_INT); - - if (len == LARGEST_INT) { - panic("%s:%d string too long", file, line); - /*NOTREACHED*/ - } - return (unsigned) len; -} /*dlb_main.c*/ diff --git a/util/makedefs.c b/util/makedefs.c index f2a65e0952..66ea742123 100644 --- a/util/makedefs.c +++ b/util/makedefs.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 makedefs.c $NHDT-Date: 1655402416 2022/06/16 18:00:16 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.215 $ */ +/* NetHack 3.7 makedefs.c $NHDT-Date: 1702948590 2023/12/19 01:16:30 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.233 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Kenneth Lorber, Kensington, Maryland, 2015. */ /* Copyright (c) M. Stephenson, 1990, 1991. */ @@ -7,6 +7,8 @@ #define MAKEDEFS_C /* use to conditionally include file sections */ +#define OLD_MAKEDEFS_OPTIONS + #include "config.h" #include "permonst.h" #include "objclass.h" @@ -19,8 +21,8 @@ #include "context.h" #include "flag.h" #include "dlb.h" +#include "hacklib.h" -#include #ifdef MAC #if defined(__SC__) || defined(__MRC__) /* MPW compilers */ #define MPWTOOL @@ -47,65 +49,79 @@ #endif /* names of files to be generated */ +#define ORACLE_FILE "oracles" +#define DATA_FILE "data" +#define RUMOR_FILE "rumors" + +/* These are affiliated with outdated options + but we define them for us in messages */ #define DATE_FILE "date.h" #define MONST_FILE "pm.h" #define ONAME_FILE "onames.h" #ifndef OPTIONS_FILE #define OPTIONS_FILE "options" #endif -#define ORACLE_FILE "oracles" -#define DATA_FILE "data" -#define RUMOR_FILE "rumors" #define DGN_I_FILE "dungeon.def" #define DGN_O_FILE "dungeon.pdf" -#if 0 #define QTXT_I_FILE "quest.txt" #define QTXT_O_FILE "quest.dat" -#endif #define GITINFO_FILE "gitinfo.txt" + /* locations for those files */ #ifdef AMIGA #define FILE_PREFIX -#define INCLUDE_TEMPLATE "NH:include/t.%s" #define SOURCE_TEMPLATE "NH:src/%s" -#define DGN_TEMPLATE "NH:dat/%s" /* where dungeon.pdf file goes */ #define DATA_TEMPLATE "NH:slib/%s" #define DATA_IN_TEMPLATE "NH:dat/%s" +#if defined(OLD_MAKEDEFS_OPTIONS) +#define INCLUDE_TEMPLATE "NH:include/t.%s" +#define DGN_TEMPLATE "NH:dat/%s" /* where dungeon.pdf file goes */ +#endif /* OLD_MAKEDEFS_OPTIONS */ #else /* not AMIGA */ #if defined(MAC) && !defined(__MACH__) /* MacOS 9 or earlier */ -#define INCLUDE_TEMPLATE ":include:%s" #define SOURCE_TEMPLATE ":src:%s" -#define DGN_TEMPLATE ":dat:%s" /* where dungeon.pdf file goes */ #if __SC__ || __MRC__ #define DATA_TEMPLATE ":Dungeon:%s" #else #define DATA_TEMPLATE ":lib:%s" #endif /* __SC__ || __MRC__ */ #define DATA_IN_TEMPLATE ":dat:%s" +#if defined(OLD_MAKEDEFS_OPTIONS) +#define INCLUDE_TEMPLATE ":include:%s" +#define DGN_TEMPLATE ":dat:%s" /* where dungeon.pdf file goes */ +#endif /* OLD_MAKEDEFS_OPTIONS */ #else /* neither AMIGA nor MAC */ #ifdef OS2 -#define INCLUDE_TEMPLATE "..\\include\\%s" #define SOURCE_TEMPLATE "..\\src\\%s" -#define DGN_TEMPLATE "..\\dat\\%s" /* where dungeon.pdf file goes */ #define DATA_TEMPLATE "..\\dat\\%s" #define DATA_IN_TEMPLATE "..\\dat\\%s" +#if defined(OLD_MAKEDEFS_OPTIONS) +#define INCLUDE_TEMPLATE "..\\include\\%s" +#define DGN_TEMPLATE "..\\dat\\%s" /* where dungeon.pdf file goes */ +#endif /* OLD_MAKEDEFS_OPTIONS */ #else /* not AMIGA, MAC, or OS2 */ -#define INCLUDE_TEMPLATE "../include/%s" #define SOURCE_TEMPLATE "../src/%s" -#define DGN_TEMPLATE "../dat/%s" /* where dungeon.pdf file goes */ #define DATA_TEMPLATE "../dat/%s" #define DATA_IN_TEMPLATE "../dat/%s" +#if defined(OLD_MAKEDEFS_OPTIONS) +#define INCLUDE_TEMPLATE "../include/%s" +#define DGN_TEMPLATE "../dat/%s" /* where dungeon.pdf file goes */ +#endif /* OLD_MAKEDEFS_OPTIONS */ #endif /* else !OS2 */ #endif /* else !MAC */ #endif /* else !AMIGA */ static const char *Dont_Edit_Data = - "#\tThis data file is generated by 'makedefs'. Do not edit. \n", + "#\tThis data file is generated by 'makedefs'. Do not edit. \n"; + +#if defined(OLD_MAKEDEFS_OPTIONS) +static const char *Reference_file = "/* This file is generated by 'makedefs' for reference purposes only. */\n" "/* It is no longer used in the NetHack build. */\n"; +#endif /* OLD_MAKEDEFS_OPTIONS */ static struct version_info version; @@ -126,17 +142,23 @@ int main(void); #else int main(int, char **); #endif + void do_makedefs(char *); -void do_objs(void); void do_data(void); +void do_rumors(void); +void do_oracles(void); + +#if defined(OLD_MAKEDEFS_OPTIONS) +void do_date(void); void do_dungeon(void); void do_options(void); void do_monstr(void); +void do_objs(void); void do_permonst(void); void do_questtxt(void); -void do_rumors(void); -void do_oracles(void); -void do_date(void); +#else +static const char *oldfunctionality(char); +#endif /* OLD_MAKEDEFS_OPTIONS */ extern void monst_globals_init(void); /* monst.c */ extern void objects_globals_init(void); /* objects.c */ @@ -144,26 +166,40 @@ extern void objects_globals_init(void); /* objects.c */ static char *name_file(const char *, const char *); static FILE *getfp(const char *, const char *, const char *, int); static void do_ext_makedefs(int, char **); -static char *xcrypt(const char *); static char *padline(char *, unsigned); static unsigned long read_rumors_file(const char *, int *, long *, unsigned long, unsigned); -static void do_rnd_access_file(const char *, const char *, unsigned); +static void rafile(int); +static void do_rnd_access_file(const char *, const char *, + const char *, unsigned); static boolean d_filter(char *); static boolean h_filter(char *); static void opt_out_words(char *, int *); +static char *fgetline(FILE *); +/* doesn't do much (counts lines) if MAKEDEFS_FILTER_NONASCII isn't enabled */ +static void filter_nonascii(char *); +static void set_fgetline_context(const char *, boolean, boolean); -static char *fgetline(FILE*); +#if defined(OLD_MAKEDEFS_OPTIONS) static char *tmpdup(const char *); static char *macronamelimit(char *, int); static void windowing_sanity(void); static boolean get_gitinfo(char *, char *); +static boolean use_enum = TRUE; +#endif + +/* for MAKEDEFS_FILTER_NONASCII, but not conditionalized; + extra input for fgetline(); not-needed for files that don't use that */ +struct ascii_filter { + const char *filename; + int linenum, warncnt; + boolean dofilter, tabok; +}; +static struct ascii_filter ascii_ctx; /* input, output, tmp */ static FILE *ifp, *ofp, *tfp; -static boolean use_enum = TRUE; - #if defined(__BORLANDC__) && !defined(_WIN32) extern unsigned _stklen = STKSIZ; #endif @@ -184,9 +220,6 @@ ATTRNORETURN static void makedefs_exit(int) NORETURN; ATTRNORETURN static void makedefs_exit(int how) { -#if 0 /* makedefs doesn't need to do this */ - release_runtime_info(); /* dynamic data from mdlib.c and date.c */ -#endif exit(how); /*NOTREACHED*/ } @@ -282,14 +315,35 @@ do_makedefs(char *options) Fprintf(stderr, "makedefs -%c\n", *options); switch (*options) { - case 'o': - case 'O': - do_objs(); - break; case 'd': case 'D': do_data(); break; + case 'h': + case 'H': + do_oracles(); + break; + case 'r': + case 'R': + do_rumors(); + break; + case 's': + case 'S': + rafile('1'); + rafile('2'); + rafile('3'); + rafile('4'); + break; + case '1': + case '2': + case '3': + rafile(*options); + break; +#if defined(OLD_MAKEDEFS_OPTIONS) + case 'o': + case 'O': + do_objs(); + break; case 'e': case 'E': do_dungeon(); @@ -307,51 +361,88 @@ do_makedefs(char *options) case 'Q': do_questtxt(); break; - case 'r': - case 'R': - do_rumors(); +#else + 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'" + " for '%c' (%s) support.\n", + *options, oldfunctionality(*options)); + (void) fflush(stderr); + /*NOTREACHED*/ break; - case 's': - case 'S': +#endif /* OLD_MAKEDEFS_OPTIONS */ + default: + Fprintf(stderr, "Unknown option '%c'.\n", *options); + (void) fflush(stderr); + makedefs_exit(EXIT_FAILURE); + /*NOTREACHED*/ + } + options++; + } + if (more_than_one) + Fprintf(stderr, "Completed.\n"); /* feedback */ + return; +} + +#if !defined(OLD_MAKEDEFS_OPTIONS) +static const char * +oldfunctionality(char sought) +{ + static struct ofn_s { + char lcoflet; + char ucoflet; + const char *ofnam; + } ofn[] = { + { 'e', 'E', DGN_O_FILE }, + { 'o', 'O', ONAME_FILE }, + { 'p', 'P', MONST_FILE }, + { 'q', 'Q', QTXT_O_FILE }, + { 'v', 'V', DATE_FILE " & " OPTIONS_FILE }, + }; + int i; + for (i = 0; i < SIZE(ofn); ++i) { + if (sought == ofn[i].lcoflet || sought == ofn[i].ucoflet) + return ofn[i].ofnam; + } + return "unknown"; +} +#endif /* !OLD_MAKEDEFS_OPTIONS */ + +static void +rafile(int whichone) +{ + switch(whichone) { /* * post-3.6.5: * File must not be empty to avoid divide by 0 * in core's rn2(), so provide a default entry. */ - do_rnd_access_file(EPITAPHFILE, + case '1': + do_rnd_access_file(EPITAPHFILE, "epitaph", /* default epitaph: parody of the default engraving */ "No matter where I went, here I am.", MD_PAD_RUMORS); /* '_RUMORS' is correct here */ - do_rnd_access_file(ENGRAVEFILE, + break; + case '2': + do_rnd_access_file(ENGRAVEFILE, "engrave", /* default engraving: popularized by "The Adventures of Buckaroo Bonzai Across the 8th Dimension" but predates that 1984 movie; some attribute it to Confucius */ "No matter where you go, there you are.", MD_PAD_RUMORS); /* '_RUMORS' used here too */ - do_rnd_access_file(BOGUSMONFILE, + break; + case '3': + do_rnd_access_file(BOGUSMONFILE, "bogusmon", /* default bogusmon: iconic monster that isn't in nethack */ "grue", MD_PAD_BOGONS); - do_rnd_access_file(SHIRTFILE, + break; + case '4': + do_rnd_access_file(SHIRTFILE, "shirts", /* default shirt: announcing you're a character in nethack */ "@", MD_PAD_RUMORS); - break; - case 'h': - case 'H': - do_oracles(); - break; - - default: - Fprintf(stderr, "Unknown option '%c'.\n", *options); - (void) fflush(stderr); - makedefs_exit(EXIT_FAILURE); - /*NOTREACHED*/ - } - options++; } - if (more_than_one) - Fprintf(stderr, "Completed.\n"); /* feedback */ - return; } static char namebuf[1000]; @@ -359,7 +450,7 @@ static char namebuf[1000]; DISABLE_WARNING_FORMAT_NONLITERAL static char * -name_file(const char* template, const char* tag) +name_file(const char *template, const char *tag) { Sprintf(namebuf, template, tag); return namebuf; @@ -378,7 +469,7 @@ delete_file(const char *template, const char *tag) #endif static FILE * -getfp(const char* template, const char* tag, const char* mode, int flg) +getfp(const char *template, const char *tag, const char *mode, int flg) { char *name = name_file(template, tag); FILE *rv = (FILE *) 0; @@ -396,7 +487,7 @@ getfp(const char* template, const char* tag, const char* mode, int flg) err = tmpfile_s(&rv); #if defined(MSDOS) || defined(WIN32) if (!err && (!strcmp(mode, WRTMODE) || !strcmp(mode, RDTMODE))) { - _setmode(fileno(rv), O_TEXT); + (void) _setmode(fileno(rv), O_TEXT); } #endif } else @@ -575,6 +666,15 @@ do_ext_makedefs(int argc, char **argv) } CONTINUE; } + 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)); + } #ifdef notyet IS_OPTION("help") { } @@ -667,7 +767,7 @@ do_grep_showvars(void) } static struct grep_var * -grepsearch(const char* name) +grepsearch(const char *name) { /* XXX make into binary search */ int x = 0; @@ -681,7 +781,7 @@ grepsearch(const char* name) } static int -grep_check_id(const char* id) +grep_check_id(const char *id) { struct grep_var *rv; @@ -710,7 +810,7 @@ grep_check_id(const char* id) } static void -grep_show_wstack(const char* tag) +grep_show_wstack(const char *tag) { int x; @@ -751,7 +851,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); @@ -901,26 +1002,6 @@ grep0(FILE *inputfp0, FILE* outputfp0, int flg) return; } -/* trivial text encryption routine which can't be broken with `tr' */ -static char * -xcrypt(const char* str) -{ /* duplicated in src/hacklib.c */ - static char buf[BUFSZ]; - register const char *p; - register char *q; - register int bitmask; - - for (bitmask = 1, p = str, q = buf; *p; q++) { - *q = *p++; - if (*q & (32 | 64)) - *q ^= bitmask; - if ((bitmask <<= 1) >= 32) - bitmask = 1; - } - *q = '\0'; - return buf; -} - static char * padline(char *line, unsigned padlength) { @@ -965,7 +1046,7 @@ read_rumors_file( unsigned long old_rumor_offset, unsigned padlength) { - char infile[MAXFNAMELEN]; + char infile[MAXFNAMELEN], xbuf[BUFSZ]; char *line; unsigned long rumor_offset; @@ -975,6 +1056,7 @@ read_rumors_file( perror(infile); return 0L; } + set_fgetline_context(infile, TRUE, FALSE); /* copy the rumors */ while ((line = fgetline(ifp)) != 0) { @@ -990,7 +1072,7 @@ read_rumors_file( /*[if we forced binary output, this would be sufficient]*/ *rumor_size += strlen(line); /* includes newline */ #endif - (void) fputs(xcrypt(line), tfp); + (void) fputs(xcrypt(line, xbuf), tfp); free((genericptr_t) line); } /* record the current position; next rumors section will start here */ @@ -1009,11 +1091,14 @@ read_rumors_file( static void do_rnd_access_file( const char *fname, + const char *basefname, const char *deflt_content, unsigned padlength) { - char *line, buf[BUFSZ]; + char *line, buf[BUFSZ], xbuf[BUFSZ], + greptmp[8 + 1 + 3 + 1]; + Sprintf(greptmp, "grep-%.3s.tmp", basefname); Sprintf(filename, DATA_IN_TEMPLATE, fname); Strcat(filename, ".txt"); if (!(ifp = fopen(filename, RDTMODE))) { @@ -1039,19 +1124,21 @@ do_rnd_access_file( Strcpy(buf, deflt_content); if (!strchr(buf, '\n')) /* lines from the file include trailing newline +*/ Strcat(buf, "\n"); /* so make sure that the default one does too */ - (void) fputs(xcrypt(padline(buf, padlength)), ofp); + (void) fputs(xcrypt(padline(buf, padlength), xbuf), ofp); - tfp = getfp(DATA_TEMPLATE, "grep.tmp", WRTMODE, FLG_TEMPFILE); + tfp = getfp(DATA_TEMPLATE, greptmp, WRTMODE, FLG_TEMPFILE); grep0(ifp, tfp, FLG_TEMPFILE); #ifndef HAS_NO_MKSTEMP ifp = tfp; #else - ifp = getfp(DATA_TEMPLATE, "grep.tmp", RDTMODE, 0); + ifp = getfp(DATA_TEMPLATE, greptmp, RDTMODE, 0); #endif + set_fgetline_context(NULL, FALSE, FALSE); + while ((line = fgetline(ifp)) != 0) { if (line[0] != '#' && line[0] != '\n') { (void) padline(line, padlength); - (void) fputs(xcrypt(line), ofp); + (void) fputs(xcrypt(line, xbuf), ofp); } free((genericptr_t) line); } @@ -1059,7 +1146,7 @@ do_rnd_access_file( Fclose(ofp); #ifdef HAS_NO_MKSTEMP - delete_file(DATA_TEMPLATE, "grep.tmp"); + delete_file(DATA_TEMPLATE, greptmp); #endif return; } @@ -1106,9 +1193,10 @@ do_rumors(void) /* record the current position; true rumors will start here */ true_rumor_offset = (unsigned long) ftell(tfp); - false_rumor_offset = (unsigned long) read_rumors_file(".tru", &true_rumor_count, - &true_rumor_size, true_rumor_offset, - MD_PAD_RUMORS); + false_rumor_offset + = (unsigned long) read_rumors_file(".tru", &true_rumor_count, + &true_rumor_size, true_rumor_offset, + MD_PAD_RUMORS); if (!false_rumor_offset) goto rumors_failure; @@ -1132,6 +1220,8 @@ do_rumors(void) Fprintf(ofp, rumors_header, Dont_Edit_Data, true_rumor_count, true_rumor_size, true_rumor_offset, false_rumor_count, false_rumor_size, false_rumor_offset, eof_offset); + + set_fgetline_context(NULL, FALSE, FALSE); /* skip the temp file's dummy header */ if (!(line = fgetline(tfp))) { /* "Don't Edit" */ perror(tempfile); @@ -1164,399 +1254,213 @@ do_rumors(void) } RESTORE_WARNING_FORMAT_NONLITERAL - void -do_date(void) +do_data(void) { -#ifdef KR1ED - long clocktim = 0; -#else - time_t clocktim = 0; -#endif - char githash[BUFSZ], gitbranch[BUFSZ]; - char *c, cbuf[60], buf[BUFSZ]; - const char *ul_sfx; -#if defined(CROSSCOMPILE) && !defined(CROSSCOMPILE_TARGET) - const char *xpref = "HOST_"; -#else - const char *xpref = (const char *) 0; -#endif /* CROSSCOMPILE && !CROSSCOMPILE_TARGET */ - - /* before creating date.h, make sure that xxx_GRAPHICS and - DEFAULT_WINDOW_SYS have been set up in a viable fashion */ - windowing_sanity(); + char infile[60], tempfile[60]; + boolean ok; + long txt_offset; + int entry_cnt, line_cnt; + char *line; + Sprintf(tempfile, DATA_TEMPLATE, "database.tmp"); filename[0] = '\0'; #ifdef FILE_PREFIX Strcat(filename, file_prefix); #endif - Sprintf(eos(filename), INCLUDE_TEMPLATE, DATE_FILE); - if (!(ofp = fopen(filename, WRTMODE))) { + Sprintf(eos(filename), DATA_TEMPLATE, DATA_FILE); + Sprintf(infile, DATA_IN_TEMPLATE, DATA_FILE); +#ifdef SHORT_FILENAMES + Strcat(infile, ".bas"); +#else + Strcat(infile, ".base"); +#endif + if (!(ifp = fopen(infile, RDTMODE))) { /* data.base */ + perror(infile); + makedefs_exit(EXIT_FAILURE); + /*NOTREACHED*/ + } + if (!(ofp = fopen(filename, WRTMODE))) { /* data */ perror(filename); + Fclose(ifp); + makedefs_exit(EXIT_FAILURE); + /*NOTREACHED*/ + } + if (!(tfp = fopen(tempfile, WRTMODE))) { /* database.tmp */ + perror(tempfile); + Fclose(ifp); + Fclose(ofp); + Unlink(filename); makedefs_exit(EXIT_FAILURE); /*NOTREACHED*/ } - /* NB: We've moved on from SCCS, but this way this line - * won't get clobbered when downstream projects import - * this file into something more modern. */ - Fprintf(ofp, "%s", Reference_file); - - (void) time(&clocktim); -#ifdef REPRODUCIBLE_BUILD - { - /* - * Use date+time of latest source file revision (set up in - * our environment rather than derived by scanning sources) - * instead of current date+time, so that later rebuilds of - * the same sources specifying the same configuration will - * produce the same result. - * - * Changing the configuration should be done by modifying - * config.h or conf.h and setting SOURCE_DATE_EPOCH - * based on whichever changed most recently, not by using - * make CFLAGS='-Dthis -Dthat' - * to make alterations on the fly. - * - * Limited validation is performed to prevent dates in the - * future (beyond a leeway of 24 hours) or distant past. - * - * Assumes the value of time_t is in seconds, which is - * fundamental for Unix and mandated by POSIX. For any ports - * where that isn't true, leaving REPRODUCIBLE_BUILD disabled - * is probably preferrable to hacking this code.... - */ - static struct tm nh360; /* static init should yield UTC timezone */ - unsigned long sd_num, sd_earliest, sd_latest; - const char *sd_str = getenv("SOURCE_DATE_EPOCH"); - if (sd_str) { - sd_num = strtoul(sd_str, (char **) 0, 10); - /* - * Note: this does not need to be updated for future - * releases. It serves as a sanity check for potentially - * mis-set environment, not a hard baseline for when the - * current version could have first been built. - */ - /* oldest date we'll accept: 7-Dec-2015 (release of 3.6.0) */ - nh360.tm_mday = 7; - nh360.tm_mon = 12 - 1; - nh360.tm_year = 2015 - 1900; - sd_earliest = (unsigned long) mktime(&nh360); - /* 'youngest' date we'll accept: 24 hours in the future */ - sd_latest = (unsigned long) clocktim + 24L * 60L * 60L; + /* output a dummy header record; we'll rewind and overwrite it later */ + Fprintf(ofp, "%s%08lx\n", Dont_Edit_Data, 0L); - if (sd_num >= sd_earliest && sd_num <= sd_latest) { - /* use SOURCE_DATE_EPOCH value */ - clocktim = (time_t) sd_num; - date_via_env = TRUE; - } else { - Fprintf(stderr, "? Invalid value for SOURCE_DATE_EPOCH (%lu)", - sd_num); - if (sd_num > 0L && sd_num < sd_earliest) - Fprintf(stderr, ", older than %lu", sd_earliest); - else if (sd_num > sd_latest) - Fprintf(stderr, ", newer than %lu", sd_latest); - Fprintf(stderr, ".\n"); - Fprintf(stderr, ": Reverting to current date+time (%lu).\n", - (unsigned long) clocktim); - (void) fflush(stderr); - } - } else { - /* REPRODUCIBLE_BUILD enabled but SOURCE_DATE_EPOCH is missing */ - Fprintf(stderr, "? No value for SOURCE_DATE_EPOCH.\n"); - Fprintf(stderr, ": Using current date+time (%lu).\n", - (unsigned long) clocktim); - (void) fflush(stderr); + entry_cnt = line_cnt = 0; + set_fgetline_context(infile, TRUE, TRUE); + /* read through the input file and split it into two sections */ + while ((line = fgetline(ifp)) != 0) { + if (d_filter(line)) { + free((genericptr_t) line); + continue; } -#if !defined(NOSTRFTIME) - if (!strftime(cbuf, sizeof cbuf, "%c", gmtime(&clocktim))) - cbuf[0] = '\0'; -#else - Strcpy(cbuf, asctime(gmtime(&clocktim))); -#endif /* NOSTRFTIME */ - } -#else - /* ordinary build: use current date+time */ -#if !defined(NOSTRFTIME) - if (!strftime(cbuf, sizeof cbuf, "%a %b %e %H:%M:%S %Y", - localtime(&clocktim))) - cbuf[0] = '\0'; -#else - Strcpy(cbuf, ctime(&clocktim)); -#endif /* NOSTRFTIME */ -#endif /* REPRODUCIBLE_BUILD */ - - if ((c = strchr(cbuf, '\n')) != 0) - *c = '\0'; /* strip off the '\n' */ -#ifdef NHSTDC - ul_sfx = "UL"; -#else - ul_sfx = "L"; -#endif - -#if !defined(CROSSCOMPILE) || !defined(CROSSCOMPILE_TARGET) - Fprintf(ofp, - "\n#if !defined(CROSSCOMPILE) || !defined(CROSSCOMPILE_TARGET)\n"); -#endif /* CROSSCOMPILE || !CROSSCOMPILE_TARGET */ - if (date_via_env) - Fprintf(ofp, "#define SOURCE_DATE_EPOCH (%lu%s) /* via getenv() */\n", - (unsigned long) clocktim, ul_sfx); - Fprintf(ofp, "#define BUILD_DATE \"%s\"\n", cbuf); - if (date_via_env) - Fprintf(ofp, "#define BUILD_TIME SOURCE_DATE_EPOCH\n"); - else - Fprintf(ofp, "#define BUILD_TIME (%lu%s)\n", - (unsigned long) clocktim, ul_sfx); - Fprintf(ofp, "\n"); - Fprintf(ofp, "#define VERSION_NUMBER 0x%08lx%s\n", version.incarnation, - ul_sfx); - Fprintf(ofp, "#define VERSION_FEATURES 0x%08lx%s\n", version.feature_set, - ul_sfx); - { - unsigned long ignored_features = md_ignored_features(); - - Fprintf(ofp, "#define IGNORED_FEATURES 0x%08lx%s\n", - ignored_features, ul_sfx); + if (*line > ' ') { /* got an entry name */ + /* first finish previous entry */ + if (line_cnt) + Fprintf(ofp, "%d\n", line_cnt), line_cnt = 0; + /* output the entry name */ + (void) fputs(line, ofp); + entry_cnt++; /* update number of entries */ + } else if (entry_cnt) { /* got some descriptive text */ + /* update previous entry with current text offset */ + if (!line_cnt) + Fprintf(ofp, "%ld,", ftell(tfp)); + /* save the text line in the scratch file */ + (void) fputs(line, tfp); + line_cnt++; /* update line counter */ + } + free((genericptr_t) line); } - 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__ */ + /* output an end marker and then record the current position */ + if (line_cnt) + Fprintf(ofp, "%d\n", line_cnt); + Fprintf(ofp, ".\n%ld,%d\n", ftell(tfp), 0); + txt_offset = ftell(ofp); + Fclose(ifp); /* all done with original input file */ - Fprintf(ofp, "\n"); - Fprintf(ofp, "#define VERSION_STRING \"%s\"\n", - mdlib_version_string(buf, ".")); - Fprintf(ofp, "#define VERSION_ID \\\n \"%s\"\n", - version_id_string(buf, sizeof buf, cbuf)); - Fprintf(ofp, "#define COPYRIGHT_BANNER_C \\\n \"%s\"\n", - bannerc_string(buf, sizeof buf, cbuf)); - if (get_gitinfo(githash, gitbranch)) { - Fprintf(ofp, "#define NETHACK_GIT_SHA \"%s\"\n", githash); - Fprintf(ofp, "#define NETHACK_GIT_BRANCH \"%s\"\n", gitbranch); + /* reprocess the scratch file; 1st format an error msg, just in case */ + line = malloc(BUFSZ + MAXFNAMELEN); + if (!line) { + fprintf(stderr, "makedefs malloc() failure\n"); + exit(EXIT_FAILURE); } - if (xpref && get_gitinfo(githash, gitbranch)) { - Fprintf(ofp, "#else /* !CROSSCOMPILE || !CROSSCOMPILE_TARGET */\n"); - Fprintf(ofp, "#define NETHACK_%sGIT_SHA \"%s\"\n", - xpref, githash); - Fprintf(ofp, "#define NETHACK_%sGIT_BRANCH \"%s\"\n", - xpref, gitbranch); + Sprintf(line, "rewind of \"%s\"", tempfile); + if (rewind(tfp) != 0) + goto dead_data; + free((genericptr_t) line); + set_fgetline_context(NULL, FALSE, TRUE); + /* copy all lines of text from the scratch file into the output file */ + while ((line = fgetline(tfp)) != 0) { + (void) fputs(line, ofp); + free((genericptr_t) line); } -#if !defined(CROSSCOMPILE) || !defined(CROSSCOMPILE_TARGET) - Fprintf(ofp, "#endif /* !CROSSCOMPILE || !CROSSCOMPILE_TARGET */\n"); -#endif - Fprintf(ofp, "\n"); -#ifdef AMIGA - { - struct tm *tm = localtime((time_t *) &clocktim); - Fprintf(ofp, "#define AMIGA_VERSION_STRING "); - Fprintf(ofp, "\"\\0$VER: NetHack %d.%d.%d (%d.%d.%d)\"\n", - VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL, - tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900); + /* finished with scratch file */ + Fclose(tfp); + Unlink(tempfile); /* remove it */ + + /* update the first record of the output file; prepare error msg 1st */ + line = (char *) alloc(BUFSZ + MAXFNAMELEN); + Sprintf(line, "rewind of \"%s\"", filename); + ok = (rewind(ofp) == 0); + if (ok) { + Sprintf(line, "header rewrite of \"%s\"", filename); + ok = (fprintf(ofp, "%s%08lx\n", Dont_Edit_Data, + (unsigned long) txt_offset) >= 0); } -#endif + if (!ok) { + dead_data: + perror(line); /* report the problem */ + free((genericptr_t) line); + /* close and kill the aborted output file, then give up */ + Fclose(ofp); + Unlink(filename); + makedefs_exit(EXIT_FAILURE); + /*NOTREACHED*/ + } + free((genericptr_t) line); + + /* all done */ Fclose(ofp); + return; } +/* routine to decide whether to discard something from oracles.txt */ static boolean -get_gitinfo(char *githash, char *gitbranch) +h_filter(char *line) { - FILE *gifp; - size_t len; - char infile[MAXFNAMELEN]; - char *line, *strval, *opt, *c, *end; - boolean havebranch = FALSE, havehash = FALSE; + static boolean skip = FALSE; + char *tag; - if (!githash || !gitbranch) return FALSE; + SpinCursor(3); - Sprintf(infile, DATA_IN_TEMPLATE, GITINFO_FILE); - if (!(gifp = fopen(infile, RDTMODE))) { - /* perror(infile); */ - return FALSE; - } + if (*line == '#') + return TRUE; /* ignore comment lines */ - /* read the gitinfo file */ - while ((line = fgetline(gifp)) != 0) { - strval = strchr(line, '='); - if (strval && strlen(strval) < (BUFSZ-1)) { - opt = line; - *strval++ = '\0'; - /* strip off the '\n' */ - if ((c = strchr(strval, '\n')) != 0) - *c = '\0'; - if ((c = strchr(opt, '\n')) != 0) - *c = '\0'; - /* strip leading and trailing white space */ - while (*strval == ' ' || *strval == '\t') - strval++; - end = eos(strval); - while (--end >= strval && (*end == ' ' || *end == '\t')) - *end = '\0'; - while (*opt == ' ' || *opt == '\t') - opt++; - end = eos(opt); - while (--end >= opt && (*end == ' ' || *end == '\t')) - *end = '\0'; + tag = (char *) alloc((unsigned) strlen(line)); /* don't need +1 here */ + if (sscanf(line, "----- %s", tag) == 1) { + skip = FALSE; + } else if (skip && !strncmp(line, "-----", 5)) + skip = FALSE; + free((genericptr_t) tag); + return skip; +} - len = strlen(opt); - if ((len >= strlen("gitbranch")) - && !case_insensitive_comp(opt, "gitbranch")) { - Strcpy(gitbranch, strval); - havebranch = TRUE; - } - if ((len >= strlen("githash")) - && !case_insensitive_comp(opt, "githash")) { - Strcpy(githash, strval); - havehash = TRUE; - } - } - free((genericptr_t) line); - } - Fclose(gifp); - if (havebranch && havehash) - return TRUE; +/* routine to decide whether to discard something from data.base */ +static boolean +d_filter(char *line) +{ + if (*line == '#') + return TRUE; /* ignore comment lines */ return FALSE; } +static const char *special_oracle[] = { + "\"...it is rather disconcerting to be confronted with the", + "following theorem from [Baker, Gill, and Solovay, 1975].", + "", + "Theorem 7.18 There exist recursive languages A and B such that", + " (1) P(A) == NP(A), and", + " (2) P(B) != NP(B)", + "", + "This provides impressive evidence that the techniques that are", + ("currently available will not suffice for proving that P != NP or" + " "), + "that P == NP.\" [Garey and Johnson, p. 185.]" +}; + +/* + The oracle file consists of a "do not edit" comment, a decimal count N + and set of N+1 hexadecimal fseek offsets, followed by N multiple-line + records, separated by "---" lines. The first oracle is a special case. + The input data contains just those multi-line records, separated by + "-----" lines. + */ + void -do_options(void) +do_oracles(void) { - const char *optline; - int infocontext = 0; + char infile[60], tempfile[60], xbuf[BUFSZ]; + boolean in_oracle, ok; + long fpos; + unsigned long txt_offset, offset; + int oracle_cnt; + int i; + char *line; - windowing_sanity(); + Sprintf(tempfile, DATA_TEMPLATE, "oracles.tmp"); filename[0] = '\0'; #ifdef FILE_PREFIX Strcat(filename, file_prefix); #endif - Sprintf(eos(filename), DATA_TEMPLATE, OPTIONS_FILE); + Sprintf(eos(filename), DATA_TEMPLATE, ORACLE_FILE); + Sprintf(infile, DATA_IN_TEMPLATE, ORACLE_FILE); + Strcat(infile, ".txt"); + if (!(ifp = fopen(infile, RDTMODE))) { + perror(infile); + makedefs_exit(EXIT_FAILURE); + /*NOTREACHED*/ + } if (!(ofp = fopen(filename, WRTMODE))) { perror(filename); + Fclose(ifp); makedefs_exit(EXIT_FAILURE); /*NOTREACHED*/ } - while ((optline = do_runtime_info(&infocontext)) != 0) - Fprintf(ofp, "%s\n", optline); - Fclose(ofp); - return; -} - -static void -windowing_sanity(void) -{ -#ifndef DEFAULT_WINDOW_SYS - /* pre-standard compilers didn't support #error; wait til run-time */ - Fprintf(stderr, - "Configuration error: DEFAULT_WINDOW_SYS is not defined.\n"); - makedefs_exit(EXIT_FAILURE); - /*NOTREACHED*/ - -/* put in a dummy value so that do_options() will compile and makedefs - will build, otherwise the message above won't ever get delivered */ -#define DEFAULT_WINDOW_SYS "" -#else /*DEFAULT_WINDOW_SYS*/ - - if (!window_opts[0].id) { - Fprintf(stderr, "Configuration error: no windowing systems " - "(TTY_GRAPHICS, &c) enabled.\n"); - makedefs_exit(EXIT_FAILURE); - /*NOTREACHED*/ - } - - { - int i; - - for (i = 0; window_opts[i].id; ++i) - if (!strcmp(window_opts[i].id, DEFAULT_WINDOW_SYS)) - break; - if (!window_opts[i].id) { /* went through whole list without a match */ - Fprintf(stderr, "Configuration error: DEFAULT_WINDOW_SYS (%s)\n", - DEFAULT_WINDOW_SYS); - Fprintf(stderr, - " does not match any enabled windowing system (%s%s).\n", - window_opts[0].id, window_opts[1].id ? ", &c" : ""); - makedefs_exit(EXIT_FAILURE); - /*NOTREACHED*/ - } - } -#endif /*DEFAULT_WINDOW_SYS*/ - return; -} - -/* routine to decide whether to discard something from data.base */ -static boolean -d_filter(char *line) -{ - if (*line == '#') - return TRUE; /* ignore comment lines */ - return FALSE; -} - -/* - * - New format (v3.1) of 'data' file which allows much faster lookups [pr] -"do not edit" first record is a comment line -01234567 hexadecimal formatted offset to text area -name-a first name of interest -123,4 offset to name's text, and number of lines for it -name-b next name of interest -name-c multiple names which share same description also -456,7 share a single offset,count line -. sentinel to mark end of names -789,0 dummy record containing offset, count of EOF -text-a 4 lines of descriptive text for name-a -text-a at file position 0x01234567L + 123L -text-a -text-a -text-b/text-c 7 lines of text for names-b and -c -text-b/text-c at fseek(0x01234567L + 456L) -... - * - */ - -void -do_data(void) -{ - char infile[60], tempfile[60]; - boolean ok; - long txt_offset; - int entry_cnt, line_cnt; - char *line; - - Sprintf(tempfile, DATA_TEMPLATE, "database.tmp"); - filename[0] = '\0'; -#ifdef FILE_PREFIX - Strcat(filename, file_prefix); -#endif - Sprintf(eos(filename), DATA_TEMPLATE, DATA_FILE); - Sprintf(infile, DATA_IN_TEMPLATE, DATA_FILE); -#ifdef SHORT_FILENAMES - Strcat(infile, ".bas"); -#else - Strcat(infile, ".base"); -#endif - if (!(ifp = fopen(infile, RDTMODE))) { /* data.base */ - perror(infile); - makedefs_exit(EXIT_FAILURE); - /*NOTREACHED*/ - } - if (!(ofp = fopen(filename, WRTMODE))) { /* data */ - perror(filename); - Fclose(ifp); - makedefs_exit(EXIT_FAILURE); - /*NOTREACHED*/ - } - if (!(tfp = fopen(tempfile, WRTMODE))) { /* database.tmp */ + if (!(tfp = fopen(tempfile, WRTMODE))) { /* oracles.tmp */ perror(tempfile); Fclose(ifp); Fclose(ofp); @@ -1566,46 +1470,68 @@ do_data(void) } /* output a dummy header record; we'll rewind and overwrite it later */ - Fprintf(ofp, "%s%08lx\n", Dont_Edit_Data, 0L); + Fprintf(ofp, "%s%5d\n", Dont_Edit_Data, 0); - entry_cnt = line_cnt = 0; - /* read through the input file and split it into two sections */ + /* handle special oracle; it must come first */ + (void) fputs("---\n", tfp); + offset = (unsigned long) ftell(tfp); + Fprintf(ofp, "%05lx\n", offset); /* start pos of special oracle */ + for (i = 0; i < SIZE(special_oracle); i++) { + (void) fputs(xcrypt(special_oracle[i], xbuf), tfp); + (void) fputc('\n', tfp); + } + SpinCursor(3); + + oracle_cnt = 1; + (void) fputs("---\n", tfp); + offset = (unsigned long) ftell(tfp); + Fprintf(ofp, "%05lx\n", offset); /* start pos of first oracle */ + in_oracle = FALSE; + + set_fgetline_context(infile, TRUE, FALSE); while ((line = fgetline(ifp)) != 0) { - if (d_filter(line)) { + SpinCursor(3); + + if (h_filter(line)) { free((genericptr_t) line); continue; } - if (*line > ' ') { /* got an entry name */ - /* first finish previous entry */ - if (line_cnt) - Fprintf(ofp, "%d\n", line_cnt), line_cnt = 0; - /* output the entry name */ - (void) fputs(line, ofp); - entry_cnt++; /* update number of entries */ - } else if (entry_cnt) { /* got some descriptive text */ - /* update previous entry with current text offset */ - if (!line_cnt) - Fprintf(ofp, "%ld,", ftell(tfp)); - /* save the text line in the scratch file */ - (void) fputs(line, tfp); - line_cnt++; /* update line counter */ + if (!strncmp(line, "-----", 5)) { + if (!in_oracle) { + free((genericptr_t) line); + continue; + } + in_oracle = FALSE; + oracle_cnt++; + (void) fputs("---\n", tfp); + offset = (unsigned long) ftell(tfp); + Fprintf(ofp, "%05lx\n", offset); /* start pos of this oracle */ + } else { + in_oracle = TRUE; + (void) fputs(xcrypt(line, xbuf), tfp); } free((genericptr_t) line); } - /* output an end marker and then record the current position */ - if (line_cnt) - Fprintf(ofp, "%d\n", line_cnt); - Fprintf(ofp, ".\n%ld,%d\n", ftell(tfp), 0); - txt_offset = ftell(ofp); + + if (in_oracle) { /* need to terminate last oracle */ + oracle_cnt++; + (void) fputs("---\n", tfp); + offset = (unsigned long) ftell(tfp); + Fprintf(ofp, "%05lx\n", offset); /* eof position */ + } + + /* record the current position */ + txt_offset = (unsigned long) ftell(ofp); Fclose(ifp); /* all done with original input file */ /* reprocess the scratch file; 1st format an error msg, just in case */ - line = malloc(BUFSZ + MAXFNAMELEN); + line = (char *) alloc(BUFSZ + MAXFNAMELEN); Sprintf(line, "rewind of \"%s\"", tempfile); if (rewind(tfp) != 0) goto dead_data; free((genericptr_t) line); /* copy all lines of text from the scratch file into the output file */ + set_fgetline_context(tempfile, FALSE, FALSE); while ((line = fgetline(tfp)) != 0) { (void) fputs(line, ofp); free((genericptr_t) line); @@ -1621,8 +1547,38 @@ do_data(void) ok = (rewind(ofp) == 0); if (ok) { Sprintf(line, "header rewrite of \"%s\"", filename); - ok = (fprintf(ofp, "%s%08lx\n", Dont_Edit_Data, - (unsigned long) txt_offset) >= 0); + ok = (fprintf(ofp, "%s%5d\n", Dont_Edit_Data, oracle_cnt) >= 0); + } + if (ok) { + Sprintf(line, "data rewrite of \"%s\"", filename); + for (i = 0; i <= oracle_cnt; i++) { +#ifndef VMS /* alpha/vms v1.0; this fflush seems to confuse ftell */ + if (!(ok = (fflush(ofp) == 0))) + break; +#endif + if (!(ok = (fpos = ftell(ofp)) >= 0)) + break; + if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0))) + break; + if (!(ok = (fscanf(ofp, "%5lx", &offset) == 1))) + break; +#ifdef MAC +#ifdef __MWERKS__ + /* + MetroWerks CodeWarrior Pro 1's (AKA CW12) version of MSL + (ANSI C Libraries) needs this rewind or else the fprintf + stops working. This may also be true for CW11, but has + never been checked. + */ + rewind(ofp); +#endif +#endif + if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0))) + break; + offset += txt_offset; + if (!(ok = (fprintf(ofp, "%05lx\n", offset) >= 0))) + break; + } } if (!ok) { dead_data: @@ -1642,220 +1598,490 @@ do_data(void) return; } -/* routine to decide whether to discard something from oracles.txt */ -static boolean -h_filter(char *line) +/* 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. + * 3.7: redone to use nethack's alloc() rather than libc's malloc() + * and realloc(). + */ +static char * +fgetline(FILE *fd) { - static boolean skip = FALSE; - char *tag; - - SpinCursor(3); - - if (*line == '#') - return TRUE; /* ignore comment lines */ - - tag = (char *) alloc((unsigned) strlen(line)); /* don't need +1 here */ - if (sscanf(line, "----- %s", tag) == 1) { - skip = FALSE; - } else if (skip && !strncmp(line, "-----", 5)) - skip = FALSE; - free((genericptr_t) tag); - return skip; -} - -static const char *special_oracle[] = { - "\"...it is rather disconcerting to be confronted with the", - "following theorem from [Baker, Gill, and Solovay, 1975].", - "", - "Theorem 7.18 There exist recursive languages A and B such that", - " (1) P(A) == NP(A), and", - " (2) P(B) != NP(B)", - "", - "This provides impressive evidence that the techniques that are", - ("currently available will not suffice for proving that P != NP or" - " "), - "that P == NP.\" [Garey and Johnson, p. 185.]" -}; + static const int inc = (BUFSZ / 2) + 16; /* fgets() wants signed int */ + unsigned len = (unsigned) inc, newlen; /* alloc() wants unsigned int */ + char *c = (char *) alloc(len), *cprime, *ret; -/* - The oracle file consists of a "do not edit" comment, a decimal count N - and set of N+1 hexadecimal fseek offsets, followed by N multiple-line - records, separated by "---" lines. The first oracle is a special case. - The input data contains just those multi-line records, separated by - "-----" lines. - */ + *c = '\0'; + for (;;) { + ret = fgets(c + len - inc, inc, fd); + if (!ret) { + /* don't just assume ret==Null indicates an error; last line + might lack terminating newline; if previous fgets() read it, + instead of returning we would have expanded the buffer and + tried for more, then got Null due to end of file having + already been reached */ + if (feof(fd) && *c && strlen(c) < len) { + Strcat(c, "\n"); /* append missing newline */ + } else { + free((genericptr_t) c), c = NULL; + } + break; /* either with or without added newline, we're done */ + } else if (strchr(c, '\n')) { + /* normal case: we have a full line */ + break; + } + /* didn't fit in c[0..len-1]; expand buffer and read some more + [this was much simpler (and possibly slightly more efficient) + with realloc() but less safe because return values from malloc() + and realloc() were not being checked for Null, and efficiency is + a red herring because growing the buffer will be extremely rare] */ + newlen = len + (unsigned) inc; + cprime = (char *) alloc(newlen); + (void) memcpy(cprime, c, len); + free((genericptr_t) c); + c = cprime; + *(c + len) = '\0'; + len = newlen; + } + + filter_nonascii(c); + return c; +} + +static void +filter_nonascii(char *line) +{ +#ifdef MAKEDEFS_FILTER_NONASCII + char warnbuf[BUFSZ]; + unsigned char *p; + int warned = 0, prevreason = -1, reason; +#endif + + if (!line) /* end of file; uses 'line' for !MAKEDEFS_FILTER_NONASCII */ + return; + ascii_ctx.linenum += 1; + if (!ascii_ctx.dofilter) + return; + +#ifdef MAKEDEFS_FILTER_NONASCII + for (p = (unsigned char *) line; *p; ++p) { + if (*p == '\n') + break; + if (*p == '\t' && ascii_ctx.tabok) + continue; + reason = (*p > 126) ? 3 : (*p == '\t') ? 2 : (*p < ' '); + if (reason != 0) { + if (!warned) + ascii_ctx.warncnt += 1; /* number of lines warned about */ + if (++warned <= 3) { /* show up to 3 warnings for this line */ + if (warned == 1) { + /*assert(ascii_ctx.filename != NULL);*/ + Sprintf(warnbuf, "? %s:", ascii_ctx.filename); + } else { + Strcpy(warnbuf, ","); + } + Sprintf(eos(warnbuf), " %d.%ld", ascii_ctx.linenum, + (long) ((char *) p - line)); /* column */ + if (reason != prevreason) { + Strcat(warnbuf, (reason == 1) ? " non-printable" + : (reason == 3) ? " non-ascii" + : " "); /* (reason == 2) */ + prevreason = reason; + } + Fprintf(stderr, "%s '%03o'", warnbuf, *p); + } else if (warned == 3 + 1) { /* when more than 3 */ + Fprintf(stderr, ", ..."); /* show an indicator */ + } + *p = '#'; + } + } + if (warned > 0) + Fprintf(stderr, "\n"); +#endif + return; +} + +static void +set_fgetline_context( + const char *current_filename, + boolean do_filtering, + boolean tabs_are_ok) /* moot for !do_filtering */ +{ + static const char dummyname[] = "[makedefs input]"; + +#ifndef MAKEDEFS_FILTER_NONASCII + do_filtering = FALSE; +#endif + if (!current_filename) + current_filename = dummyname; + /* change from relative-to-dat to be relative-to-top, iff that's easy */ + if (!strncmp(current_filename, "../", 3)) + current_filename += 3; + + ascii_ctx.filename = current_filename; + ascii_ctx.linenum = ascii_ctx.warncnt = 0; + ascii_ctx.tabok = tabs_are_ok; + ascii_ctx.dofilter = do_filtering; +} + +#if defined(OLD_MAKEDEFS_OPTIONS) +void +do_date(void) +{ +#ifdef KR1ED + long clocktim = 0; +#else + time_t clocktim = 0; +#endif + char githash[BUFSZ], gitbranch[BUFSZ]; + char *c, cbuf[60], buf[BUFSZ]; + const char *ul_sfx; +#if defined(CROSSCOMPILE) && !defined(CROSSCOMPILE_TARGET) + const char *xpref = "HOST_"; +#else + const char *xpref = (const char *) 0; +#endif /* CROSSCOMPILE && !CROSSCOMPILE_TARGET */ + + /* before creating date.h, make sure that xxx_GRAPHICS and + DEFAULT_WINDOW_SYS have been set up in a viable fashion */ + windowing_sanity(); + + filename[0] = '\0'; +#ifdef FILE_PREFIX + Strcat(filename, file_prefix); +#endif + Sprintf(eos(filename), INCLUDE_TEMPLATE, DATE_FILE); + if (!(ofp = fopen(filename, WRTMODE))) { + perror(filename); + makedefs_exit(EXIT_FAILURE); + /*NOTREACHED*/ + } + /* NB: We've moved on from SCCS, but this way this line + * won't get clobbered when downstream projects import + * this file into something more modern. */ + Fprintf(ofp, "%s", Reference_file); + + (void) time(&clocktim); +#ifdef REPRODUCIBLE_BUILD + { + /* + * Use date+time of latest source file revision (set up in + * our environment rather than derived by scanning sources) + * instead of current date+time, so that later rebuilds of + * the same sources specifying the same configuration will + * produce the same result. + * + * Changing the configuration should be done by modifying + * config.h or conf.h and setting SOURCE_DATE_EPOCH + * based on whichever changed most recently, not by using + * make CFLAGS='-Dthis -Dthat' + * to make alterations on the fly. + * + * Limited validation is performed to prevent dates in the + * future (beyond a leeway of 24 hours) or distant past. + * + * Assumes the value of time_t is in seconds, which is + * fundamental for Unix and mandated by POSIX. For any ports + * where that isn't true, leaving REPRODUCIBLE_BUILD disabled + * is probably preferrable to hacking this code.... + */ + static struct tm nh360; /* static init should yield UTC timezone */ + unsigned long sd_num, sd_earliest, sd_latest; + const char *sd_str = getenv("SOURCE_DATE_EPOCH"); + + if (sd_str) { + sd_num = strtoul(sd_str, (char **) 0, 10); + /* + * Note: this does not need to be updated for future + * releases. It serves as a sanity check for potentially + * mis-set environment, not a hard baseline for when the + * current version could have first been built. + */ + /* oldest date we'll accept: 7-Dec-2015 (release of 3.6.0) */ + nh360.tm_mday = 7; + nh360.tm_mon = 12 - 1; + nh360.tm_year = 2015 - 1900; + sd_earliest = (unsigned long) mktime(&nh360); + /* 'youngest' date we'll accept: 24 hours in the future */ + sd_latest = (unsigned long) clocktim + 24L * 60L * 60L; + + if (sd_num >= sd_earliest && sd_num <= sd_latest) { + /* use SOURCE_DATE_EPOCH value */ + clocktim = (time_t) sd_num; + date_via_env = TRUE; + } else { + Fprintf(stderr, "? Invalid value for SOURCE_DATE_EPOCH (%lu)", + sd_num); + if (sd_num > 0L && sd_num < sd_earliest) + Fprintf(stderr, ", older than %lu", sd_earliest); + else if (sd_num > sd_latest) + Fprintf(stderr, ", newer than %lu", sd_latest); + Fprintf(stderr, ".\n"); + Fprintf(stderr, ": Reverting to current date+time (%lu).\n", + (unsigned long) clocktim); + (void) fflush(stderr); + } + } else { + /* REPRODUCIBLE_BUILD enabled but SOURCE_DATE_EPOCH is missing */ + Fprintf(stderr, "? No value for SOURCE_DATE_EPOCH.\n"); + Fprintf(stderr, ": Using current date+time (%lu).\n", + (unsigned long) clocktim); + (void) fflush(stderr); + } +#if !defined(NOSTRFTIME) + if (!strftime(cbuf, sizeof cbuf, "%c", gmtime(&clocktim))) + cbuf[0] = '\0'; +#else + Strcpy(cbuf, asctime(gmtime(&clocktim))); +#endif /* NOSTRFTIME */ + } +#else + /* ordinary build: use current date+time */ +#if !defined(NOSTRFTIME) + if (!strftime(cbuf, sizeof cbuf, "%a %b %e %H:%M:%S %Y", + localtime(&clocktim))) + cbuf[0] = '\0'; +#else + Strcpy(cbuf, ctime(&clocktim)); +#endif /* NOSTRFTIME */ +#endif /* REPRODUCIBLE_BUILD */ + + if ((c = strchr(cbuf, '\n')) != 0) + *c = '\0'; /* strip off the '\n' */ +#ifdef NHSTDC + ul_sfx = "UL"; +#else + ul_sfx = "L"; +#endif + +#if !defined(CROSSCOMPILE) || !defined(CROSSCOMPILE_TARGET) + Fprintf(ofp, + "\n#if !defined(CROSSCOMPILE) || !defined(CROSSCOMPILE_TARGET)\n"); +#endif /* CROSSCOMPILE || !CROSSCOMPILE_TARGET */ + if (date_via_env) + Fprintf(ofp, "#define SOURCE_DATE_EPOCH (%lu%s) /* via getenv() */\n", + (unsigned long) clocktim, ul_sfx); + Fprintf(ofp, "#define BUILD_DATE \"%s\"\n", cbuf); + if (date_via_env) + Fprintf(ofp, "#define BUILD_TIME SOURCE_DATE_EPOCH\n"); + else + Fprintf(ofp, "#define BUILD_TIME (%lu%s)\n", + (unsigned long) clocktim, ul_sfx); + Fprintf(ofp, "\n"); + Fprintf(ofp, "#define VERSION_NUMBER 0x%08lx%s\n", version.incarnation, + ul_sfx); + Fprintf(ofp, "#define VERSION_FEATURES 0x%08lx%s\n", version.feature_set, + ul_sfx); + { + unsigned long ignored_features = md_ignored_features(); + + Fprintf(ofp, "#define IGNORED_FEATURES 0x%08lx%s\n", + ignored_features, ul_sfx); + } + 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, ".")); + Fprintf(ofp, "#define VERSION_ID \\\n \"%s\"\n", + version_id_string(buf, sizeof buf, cbuf)); + Fprintf(ofp, "#define COPYRIGHT_BANNER_C \\\n \"%s\"\n", + bannerc_string(buf, sizeof buf, cbuf)); + if (get_gitinfo(githash, gitbranch)) { + Fprintf(ofp, "#define NETHACK_GIT_SHA \"%s\"\n", githash); + Fprintf(ofp, "#define NETHACK_GIT_BRANCH \"%s\"\n", gitbranch); + } + if (xpref && get_gitinfo(githash, gitbranch)) { + Fprintf(ofp, "#else /* !CROSSCOMPILE || !CROSSCOMPILE_TARGET */\n"); + Fprintf(ofp, "#define NETHACK_%sGIT_SHA \"%s\"\n", + xpref, githash); + Fprintf(ofp, "#define NETHACK_%sGIT_BRANCH \"%s\"\n", + xpref, gitbranch); + } +#if !defined(CROSSCOMPILE) || !defined(CROSSCOMPILE_TARGET) + Fprintf(ofp, "#endif /* !CROSSCOMPILE || !CROSSCOMPILE_TARGET */\n"); +#endif + Fprintf(ofp, "\n"); +#ifdef AMIGA + { + struct tm *tm = localtime((time_t *) &clocktim); + + Fprintf(ofp, "#define AMIGA_VERSION_STRING "); + Fprintf(ofp, "\"\\0$VER: NetHack %d.%d.%d (%d.%d.%d)\"\n", + VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL, + tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900); + } +#endif + Fclose(ofp); + return; +} + +static boolean +get_gitinfo(char *githash, char *gitbranch) +{ + FILE *gifp; + size_t len; + char infile[MAXFNAMELEN]; + char *line, *strval, *opt, *c, *end; + boolean havebranch = FALSE, havehash = FALSE; + + if (!githash || !gitbranch) return FALSE; + + Sprintf(infile, DATA_IN_TEMPLATE, GITINFO_FILE); + if (!(gifp = fopen(infile, RDTMODE))) { + /* perror(infile); */ + return FALSE; + } + set_fgetline_context(infile, TRUE, TRUE); + + /* read the gitinfo file */ + while ((line = fgetline(gifp)) != 0) { + strval = strchr(line, '='); + if (strval && strlen(strval) < (BUFSZ-1)) { + opt = line; + *strval++ = '\0'; + /* strip off the '\n' */ + if ((c = strchr(strval, '\n')) != 0) + *c = '\0'; + if ((c = strchr(opt, '\n')) != 0) + *c = '\0'; + /* strip leading and trailing white space */ + while (*strval == ' ' || *strval == '\t') + strval++; + end = eos(strval); + while (--end >= strval && (*end == ' ' || *end == '\t')) + *end = '\0'; + while (*opt == ' ' || *opt == '\t') + opt++; + end = eos(opt); + while (--end >= opt && (*end == ' ' || *end == '\t')) + *end = '\0'; + + len = strlen(opt); + if ((len >= strlen("gitbranch")) + && !case_insensitive_comp(opt, "gitbranch")) { + Strcpy(gitbranch, strval); + havebranch = TRUE; + } + if ((len >= strlen("githash")) + && !case_insensitive_comp(opt, "githash")) { + Strcpy(githash, strval); + havehash = TRUE; + } + } + free((genericptr_t) line); + } + Fclose(gifp); + if (havebranch && havehash) + return TRUE; + return FALSE; +} void -do_oracles(void) +do_options(void) { - char infile[60], tempfile[60]; - boolean in_oracle, ok; - long fpos; - unsigned long txt_offset, offset; - int oracle_cnt; - register int i; - char *line; + const char *optline; + int infocontext = 0; - Sprintf(tempfile, DATA_TEMPLATE, "oracles.tmp"); + windowing_sanity(); filename[0] = '\0'; #ifdef FILE_PREFIX Strcat(filename, file_prefix); #endif - Sprintf(eos(filename), DATA_TEMPLATE, ORACLE_FILE); - Sprintf(infile, DATA_IN_TEMPLATE, ORACLE_FILE); - Strcat(infile, ".txt"); - if (!(ifp = fopen(infile, RDTMODE))) { - perror(infile); - makedefs_exit(EXIT_FAILURE); - /*NOTREACHED*/ - } + Sprintf(eos(filename), DATA_TEMPLATE, OPTIONS_FILE); if (!(ofp = fopen(filename, WRTMODE))) { perror(filename); - Fclose(ifp); - makedefs_exit(EXIT_FAILURE); - /*NOTREACHED*/ - } - if (!(tfp = fopen(tempfile, WRTMODE))) { /* oracles.tmp */ - perror(tempfile); - Fclose(ifp); - Fclose(ofp); - Unlink(filename); makedefs_exit(EXIT_FAILURE); /*NOTREACHED*/ } + while ((optline = do_runtime_info(&infocontext)) != 0) + Fprintf(ofp, "%s\n", optline); + Fclose(ofp); + return; +} - /* output a dummy header record; we'll rewind and overwrite it later */ - Fprintf(ofp, "%s%5d\n", Dont_Edit_Data, 0); - - /* handle special oracle; it must come first */ - (void) fputs("---\n", tfp); - offset = (unsigned long) ftell(tfp); - Fprintf(ofp, "%05lx\n", offset); /* start pos of special oracle */ - for (i = 0; i < SIZE(special_oracle); i++) { - (void) fputs(xcrypt(special_oracle[i]), tfp); - (void) fputc('\n', tfp); - } - SpinCursor(3); - - oracle_cnt = 1; - (void) fputs("---\n", tfp); - offset = (unsigned long) ftell(tfp); - Fprintf(ofp, "%05lx\n", offset); /* start pos of first oracle */ - in_oracle = FALSE; - - while ((line = fgetline(ifp)) != 0) { - SpinCursor(3); - - if (h_filter(line)) { - free((genericptr_t) line); - continue; - } - if (!strncmp(line, "-----", 5)) { - if (!in_oracle) { - free((genericptr_t) line); - continue; - } - in_oracle = FALSE; - oracle_cnt++; - (void) fputs("---\n", tfp); - offset = (unsigned long) ftell(tfp); - Fprintf(ofp, "%05lx\n", offset); /* start pos of this oracle */ - } else { - in_oracle = TRUE; - (void) fputs(xcrypt(line), tfp); - } - free((genericptr_t) line); - } - - if (in_oracle) { /* need to terminate last oracle */ - oracle_cnt++; - (void) fputs("---\n", tfp); - offset = (unsigned long) ftell(tfp); - Fprintf(ofp, "%05lx\n", offset); /* eof position */ - } +static void +windowing_sanity(void) +{ +#ifndef DEFAULT_WINDOW_SYS + /* pre-standard compilers didn't support #error; wait til run-time */ + Fprintf(stderr, + "Configuration error: DEFAULT_WINDOW_SYS is not defined.\n"); + makedefs_exit(EXIT_FAILURE); + /*NOTREACHED*/ - /* record the current position */ - txt_offset = (unsigned long) ftell(ofp); - Fclose(ifp); /* all done with original input file */ +/* put in a dummy value so that do_options() will compile and makedefs + will build, otherwise the message above won't ever get delivered */ +#define DEFAULT_WINDOW_SYS "" +#else /*DEFAULT_WINDOW_SYS*/ - /* reprocess the scratch file; 1st format an error msg, just in case */ - line = (char *) alloc(BUFSZ + MAXFNAMELEN); - Sprintf(line, "rewind of \"%s\"", tempfile); - if (rewind(tfp) != 0) - goto dead_data; - free((genericptr_t) line); - /* copy all lines of text from the scratch file into the output file */ - while ((line = fgetline(tfp)) != 0) { - (void) fputs(line, ofp); - free((genericptr_t) line); + if (!window_opts[0].id) { + Fprintf(stderr, "Configuration error: no windowing systems " + "(TTY_GRAPHICS, &c) enabled.\n"); + makedefs_exit(EXIT_FAILURE); + /*NOTREACHED*/ } - /* finished with scratch file */ - Fclose(tfp); - Unlink(tempfile); /* remove it */ + { + int i; - /* update the first record of the output file; prepare error msg 1st */ - line = (char *) alloc(BUFSZ + MAXFNAMELEN); - Sprintf(line, "rewind of \"%s\"", filename); - ok = (rewind(ofp) == 0); - if (ok) { - Sprintf(line, "header rewrite of \"%s\"", filename); - ok = (fprintf(ofp, "%s%5d\n", Dont_Edit_Data, oracle_cnt) >= 0); - } - if (ok) { - Sprintf(line, "data rewrite of \"%s\"", filename); - for (i = 0; i <= oracle_cnt; i++) { -#ifndef VMS /* alpha/vms v1.0; this fflush seems to confuse ftell */ - if (!(ok = (fflush(ofp) == 0))) - break; -#endif - if (!(ok = (fpos = ftell(ofp)) >= 0)) - break; - if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0))) - break; - if (!(ok = (fscanf(ofp, "%5lx", &offset) == 1))) - break; -#ifdef MAC -#ifdef __MWERKS__ - /* - MetroWerks CodeWarrior Pro 1's (AKA CW12) version of MSL - (ANSI C Libraries) needs this rewind or else the fprintf - stops working. This may also be true for CW11, but has - never been checked. - */ - rewind(ofp); -#endif -#endif - if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0))) - break; - offset += txt_offset; - if (!(ok = (fprintf(ofp, "%05lx\n", offset) >= 0))) + for (i = 0; window_opts[i].id; ++i) + if (!strcmp(window_opts[i].id, DEFAULT_WINDOW_SYS)) break; + if (!window_opts[i].id) { /* went through whole list without a match */ + Fprintf(stderr, "Configuration error: DEFAULT_WINDOW_SYS (%s)\n", + DEFAULT_WINDOW_SYS); + Fprintf(stderr, + " does not match any enabled windowing system (%s%s).\n", + window_opts[0].id, window_opts[1].id ? ", &c" : ""); + makedefs_exit(EXIT_FAILURE); + /*NOTREACHED*/ } } - if (!ok) { - dead_data: - perror(line); /* report the problem */ - free((genericptr_t) line); - /* close and kill the aborted output file, then give up */ - Fclose(ofp); - Unlink(filename); - makedefs_exit(EXIT_FAILURE); - /*NOTREACHED*/ - } - free((genericptr_t) line); - - /* all done */ - Fclose(ofp); - +#endif /*DEFAULT_WINDOW_SYS*/ return; } +/* + * + New format (v3.1) of 'data' file which allows much faster lookups [pr] +"do not edit" first record is a comment line +01234567 hexadecimal formatted offset to text area +name-a first name of interest +123,4 offset to name's text, and number of lines for it +name-b next name of interest +name-c multiple names which share same description also +456,7 share a single offset,count line +. sentinel to mark end of names +789,0 dummy record containing offset, count of EOF +text-a 4 lines of descriptive text for name-a +text-a at file position 0x01234567L + 123L +text-a +text-a +text-b/text-c 7 lines of text for names-b and -c +text-b/text-c at fseek(0x01234567L + 456L) +... + * + */ + void do_dungeon(void) { - char *line; + char *line, greptmp[8 + 1 + 3 + 1]; + Sprintf(greptmp, "grep-%.3s.tmp", "dun"); Sprintf(filename, DATA_IN_TEMPLATE, DGN_I_FILE); if (!(ifp = fopen(filename, RDTMODE))) { perror(filename); @@ -1874,13 +2100,14 @@ do_dungeon(void) } Fprintf(ofp, "%s", Dont_Edit_Data); - tfp = getfp(DATA_TEMPLATE, "grep.tmp", WRTMODE, FLG_TEMPFILE); + tfp = getfp(DATA_TEMPLATE, greptmp, WRTMODE, FLG_TEMPFILE); grep0(ifp, tfp, FLG_TEMPFILE); #ifndef HAS_NO_MKSTEMP ifp = tfp; #else - ifp = getfp(DATA_TEMPLATE, "grep.tmp", RDTMODE, 0); + ifp = getfp(DATA_TEMPLATE, greptmp, RDTMODE, 0); #endif + set_fgetline_context(NULL, FALSE, TRUE); while ((line = fgetline(ifp)) != 0) { SpinCursor(3); @@ -1895,7 +2122,7 @@ do_dungeon(void) Fclose(ofp); #ifdef HAS_NO_MKSTEMP - delete_file(DATA_TEMPLATE, "grep.tmp"); + delete_file(DATA_TEMPLATE, greptmp); #endif return; } @@ -1992,7 +2219,7 @@ macronamelimit(char *name, int pref) void do_objs(void) { - int i; + int i /*, sum = 0 */; char *c, *objnam; int nspell = 0; int prefix = 0; @@ -2024,6 +2251,7 @@ do_objs(void) /* done with current class? */ if (objects[i].oc_class != class) { class = objects[i].oc_class; + /* sum = 0; */ } for (c = objnam; *c; c++) @@ -2078,6 +2306,7 @@ do_objs(void) n_glass_gems++; break; } + FALLTHROUGH; /*FALLTHRU*/ case VENOM_CLASS: /* fall-through from gem class is ok; objects[] used to have @@ -2087,6 +2316,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"); @@ -2096,6 +2326,8 @@ do_objs(void) Fprintf(ofp, "%s\t%d\n", macronamelimit(objnam, prefix), i); prefix = 0; + /* sum += objects[i].oc_prob; */ + if (sumerr) break; } @@ -2131,55 +2363,8 @@ do_objs(void) return; } -/* 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. - * 3.7: redone to use nethack's alloc() rather than libc's malloc() - * and realloc(). - */ -static char * -fgetline(FILE *fd) -{ - static const int inc = (BUFSZ / 2) + 16; /* fgets() wants signed int */ - unsigned len = (unsigned) inc, newlen; /* alloc() wants unsigned int */ - char *c = (char *) alloc(len), *cprime, *ret; - - *c = '\0'; - for (;;) { - ret = fgets(c + len - inc, inc, fd); - if (!ret) { - /* don't just assume ret==Null indicates an error; last line - might lack terminating newline; if previous fgets() read it, - instead of returning we would have expanded the buffer and - tried for more, then got Null due to end of file having - already been reached */ - if (feof(fd) && *c && strlen(c) < len) { - Strcat(c, "\n"); /* append missing newline */ - } else { - free((genericptr_t) c), c = NULL; - } - break; /* either with or without added newline, we're done */ - } else if (strchr(c, '\n')) { - /* normal case: we have a full line */ - break; - } - /* didn't fit in c[0..len-1]; expand buffer and read some more - [this was much simpler (and possibly slightly more efficient) - with realloc() but less safe because return values from malloc() - and realloc() were not being checked for Null, and efficiency is - a red herring because growing the buffer will be extremely rare] */ - newlen = len + (unsigned) inc; - cprime = (char *) alloc(newlen); - (void) memcpy(cprime, c, len); - free((genericptr_t) c); - c = cprime; - len = newlen; - } - return c; -} - static char * -tmpdup(const char* str) +tmpdup(const char *str) { static char buf[128]; @@ -2188,6 +2373,7 @@ tmpdup(const char* str) (void) strncpy(buf, str, 127); return buf; } +#endif /* OLD_MAKEDEFS_OPTIONS */ /* the STRICT_REF_DEF checking is way out of date... */ #ifdef STRICT_REF_DEF diff --git a/util/mdgrep.h b/util/mdgrep.h index 16022e83d3..335f018b16 100644 --- a/util/mdgrep.h +++ b/util/mdgrep.h @@ -1,277 +1,295 @@ /* - * NetHack 3.7 mdgrep.h $NHDT-Date: 1596498259 2020/08/03 23:44:19 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.14 $ + * NetHack 3.7 mdgrep.h $NHDT-Date: 1710949914 2024/03/20 15:51:54 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.20 $ * Copyright (c) Kenneth Lorber, Kensington, Maryland, 2008 * NetHack may be freely redistributed. See license for details. * - * This file generated by mdgrep.pl version 1.7. + * This file generated by mdgrep.pl version 1.24. * DO NOT EDIT! Your changes will be lost. */ -static struct grep_var grep_vars[] = { { "0", 0 }, - { "1", 1 }, + +static struct grep_var grep_vars[]={ + {"0", 0}, + {"1", 1}, #if defined(ALLDOCS) - { "ALLDOCS", 1 }, + {"ALLDOCS", 1}, #else - { "ALLDOCS", 0 }, + {"ALLDOCS", 0}, #endif #if defined(AMIGA) - { "AMIGA", 1 }, + {"AMIGA", 1}, #else - { "AMIGA", 0 }, + {"AMIGA", 0}, #endif #if defined(AMII_GRAPHICS) - { "AMII_GRAPHICS", 1 }, + {"AMII_GRAPHICS", 1}, #else - { "AMII_GRAPHICS", 0 }, + {"AMII_GRAPHICS", 0}, #endif #if defined(ASCIIGRAPH) - { "ASCIIGRAPH", 1 }, -#else - { "ASCIIGRAPH", 0 }, -#endif -#if defined(BETA) - { "BETA", 1 }, + {"ASCIIGRAPH", 1}, #else - { "BETA", 0 }, + {"ASCIIGRAPH", 0}, #endif #if defined(BSD_JOB_CONTROL) - { "BSD_JOB_CONTROL", 1 }, + {"BSD_JOB_CONTROL", 1}, #else - { "BSD_JOB_CONTROL", 0 }, + {"BSD_JOB_CONTROL", 0}, #endif #if defined(CLIPPING) - { "CLIPPING", 1 }, + {"CLIPPING", 1}, #else - { "CLIPPING", 0 }, + {"CLIPPING", 0}, #endif #if defined(COMPRESS) - { "COMPRESS", 1 }, + {"COMPRESS", 1}, #else - { "COMPRESS", 0 }, + {"COMPRESS", 0}, +#endif +#if defined(CRASHREPORT) + {"CRASHREPORT", 1}, +#else + {"CRASHREPORT", 0}, +#endif +#if defined(CROSSCOMPILE) + {"CROSSCOMPILE", 1}, +#else + {"CROSSCOMPILE", 0}, #endif #if defined(DLB) - { "DLB", 1 }, + {"DLB", 1}, #else - { "DLB", 0 }, + {"DLB", 0}, #endif - { "FALSE", 0 }, + {"FALSE", 0}, #if defined(GEM_GRAPHICS) - { "GEM_GRAPHICS", 1 }, + {"GEM_GRAPHICS", 1}, #else - { "GEM_GRAPHICS", 0 }, + {"GEM_GRAPHICS", 0}, #endif #if defined(GNOME_GRAPHICS) - { "GNOME_GRAPHICS", 1 }, + {"GNOME_GRAPHICS", 1}, #else - { "GNOME_GRAPHICS", 0 }, + {"GNOME_GRAPHICS", 0}, #endif #if defined(HANGUPHANDLING) - { "HANGUPHANDLING", 1 }, + {"HANGUPHANDLING", 1}, #else - { "HANGUPHANDLING", 0 }, + {"HANGUPHANDLING", 0}, #endif #if defined(INSURANCE) - { "INSURANCE", 1 }, -#else - { "INSURANCE", 0 }, -#endif -#if defined(LIFE) - { "LIFE", 1 }, + {"INSURANCE", 1}, #else - { "LIFE", 0 }, + {"INSURANCE", 0}, #endif #if defined(MAC) - { "MAC", 1 }, + {"MAC", 1}, #else - { "MAC", 0 }, + {"MAC", 0}, #endif #if defined(MAC_GRAPHICS) - { "MAC_GRAPHICS", 1 }, + {"MAC_GRAPHICS", 1}, #else - { "MAC_GRAPHICS", 0 }, + {"MAC_GRAPHICS", 0}, #endif #if defined(MAIL) - { "MAIL", 1 }, + {"MAIL", 1}, #else - { "MAIL", 0 }, + {"MAIL", 0}, #endif #if defined(MFLOPPY) - { "MFLOPPY", 1 }, + {"MFLOPPY", 1}, #else - { "MFLOPPY", 0 }, + {"MFLOPPY", 0}, #endif #if defined(MSDOS) - { "MSDOS", 1 }, + {"MSDOS", 1}, #else - { "MSDOS", 0 }, + {"MSDOS", 0}, #endif #if defined(MSWIN_GRAPHICS) - { "MSWIN_GRAPHICS", 1 }, + {"MSWIN_GRAPHICS", 1}, #else - { "MSWIN_GRAPHICS", 0 }, + {"MSWIN_GRAPHICS", 0}, #endif #if defined(NOCWD_ASSUMPTIONS) - { "NOCWD_ASSUMPTIONS", 1 }, + {"NOCWD_ASSUMPTIONS", 1}, #else - { "NOCWD_ASSUMPTIONS", 0 }, + {"NOCWD_ASSUMPTIONS", 0}, #endif #if defined(NOSAVEONHANGUP) - { "NOSAVEONHANGUP", 1 }, + {"NOSAVEONHANGUP", 1}, #else - { "NOSAVEONHANGUP", 0 }, + {"NOSAVEONHANGUP", 0}, #endif #if defined(OS2) - { "OS2", 1 }, + {"OS2", 1}, #else - { "OS2", 0 }, + {"OS2", 0}, #endif #if defined(POSIX_JOB_CONTROL) - { "POSIX_JOB_CONTROL", 1 }, + {"POSIX_JOB_CONTROL", 1}, #else - { "POSIX_JOB_CONTROL", 0 }, + {"POSIX_JOB_CONTROL", 0}, #endif #if defined(QT_GRAPHICS) - { "QT_GRAPHICS", 1 }, + {"QT_GRAPHICS", 1}, #else - { "QT_GRAPHICS", 0 }, + {"QT_GRAPHICS", 0}, #endif #if defined(RANDOM) - { "RANDOM", 1 }, + {"RANDOM", 1}, #else - { "RANDOM", 0 }, + {"RANDOM", 0}, #endif #if defined(SAFERHANGUP) - { "SAFERHANGUP", 1 }, + {"SAFERHANGUP", 1}, #else - { "SAFERHANGUP", 0 }, + {"SAFERHANGUP", 0}, #endif #if defined(SECURE) - { "SECURE", 1 }, + {"SECURE", 1}, #else - { "SECURE", 0 }, + {"SECURE", 0}, #endif #if defined(SHELL) - { "SHELL", 1 }, + {"SHELL", 1}, #else - { "SHELL", 0 }, + {"SHELL", 0}, #endif #if defined(SUSPEND) - { "SUSPEND", 1 }, + {"SUSPEND", 1}, #else - { "SUSPEND", 0 }, + {"SUSPEND", 0}, #endif -#if defined(TEXTCOLOR) - { "TEXTCOLOR", 1 }, +#if defined(TILES_IN_GLYPHMAP) + {"TILES_IN_GLYPHMAP", 1}, #else - { "TEXTCOLOR", 0 }, + {"TILES_IN_GLYPHMAP", 0}, #endif #if defined(TOS) - { "TOS", 1 }, + {"TOS", 1}, #else - { "TOS", 0 }, + {"TOS", 0}, #endif - { "TRUE", 1 }, + {"TRUE", 1}, #if defined(TTY_GRAPHICS) - { "TTY_GRAPHICS", 1 }, + {"TTY_GRAPHICS", 1}, #else - { "TTY_GRAPHICS", 0 }, + {"TTY_GRAPHICS", 0}, #endif #if defined(UNICODE_DRAWING) - { "UNICODE_DRAWING", 1 }, + {"UNICODE_DRAWING", 1}, #else - { "UNICODE_DRAWING", 0 }, + {"UNICODE_DRAWING", 0}, #endif #if defined(UNICODE_PLAYERTEXT) - { "UNICODE_PLAYERTEXT", 1 }, + {"UNICODE_PLAYERTEXT", 1}, #else - { "UNICODE_PLAYERTEXT", 0 }, + {"UNICODE_PLAYERTEXT", 0}, #endif #if defined(UNICODE_WIDEWINPORT) - { "UNICODE_WIDEWINPORT", 1 }, + {"UNICODE_WIDEWINPORT", 1}, #else - { "UNICODE_WIDEWINPORT", 0 }, + {"UNICODE_WIDEWINPORT", 0}, #endif #if defined(UNIX) - { "UNIX", 1 }, + {"UNIX", 1}, #else - { "UNIX", 0 }, + {"UNIX", 0}, #endif #if defined(USER_SOUNDS) - { "USER_SOUNDS", 1 }, + {"USER_SOUNDS", 1}, #else - { "USER_SOUNDS", 0 }, -#endif -#if defined(TILES_IN_GLYPHMAP) - { "TILES_IN_GLYPHMAP", 1 }, -#else - { "TILES_IN_GLYPHMAP", 0 }, + {"USER_SOUNDS", 0}, #endif #if defined(VAR_PLAYGROUND) - { "VAR_PLAYGROUND", 1 }, + {"VAR_PLAYGROUND", 1}, #else - { "VAR_PLAYGROUND", 0 }, + {"VAR_PLAYGROUND", 0}, #endif #if defined(VMS) - { "VMS", 1 }, + {"VMS", 1}, #else - { "VMS", 0 }, + {"VMS", 0}, #endif #if defined(WIN32) - { "WIN32", 1 }, + {"WIN32", 1}, #else - { "WIN32", 0 }, + {"WIN32", 0}, #endif #if defined(WIN32_PLATFORM_HPCPRO) - { "WIN32_PLATFORM_HPCPRO", 1 }, + {"WIN32_PLATFORM_HPCPRO", 1}, #else - { "WIN32_PLATFORM_HPCPRO", 0 }, + {"WIN32_PLATFORM_HPCPRO", 0}, #endif #if defined(WIN32_PLATFORM_WFSP) - { "WIN32_PLATFORM_WFSP", 1 }, + {"WIN32_PLATFORM_WFSP", 1}, #else - { "WIN32_PLATFORM_WFSP", 0 }, + {"WIN32_PLATFORM_WFSP", 0}, +#endif +#if defined(WINNT) + {"WINNT", 1}, +#else + {"WINNT", 0}, #endif #if defined(WIN_CE) - { "WIN_CE", 1 }, + {"WIN_CE", 1}, #else - { "WIN_CE", 0 }, + {"WIN_CE", 0}, #endif #if defined(WIN_CE_POCKETPC) - { "WIN_CE_POCKETPC", 1 }, + {"WIN_CE_POCKETPC", 1}, #else - { "WIN_CE_POCKETPC", 0 }, + {"WIN_CE_POCKETPC", 0}, #endif #if defined(WIN_CE_PS2xx) - { "WIN_CE_PS2xx", 1 }, + {"WIN_CE_PS2xx", 1}, #else - { "WIN_CE_PS2xx", 0 }, + {"WIN_CE_PS2xx", 0}, #endif #if defined(WIN_CE_SMARTPHONE) - { "WIN_CE_SMARTPHONE", 1 }, + {"WIN_CE_SMARTPHONE", 1}, #else - { "WIN_CE_SMARTPHONE", 0 }, + {"WIN_CE_SMARTPHONE", 0}, #endif - { "WIZARD", 1 }, #if defined(X11_GRAPHICS) - { "X11_GRAPHICS", 1 }, + {"X11_GRAPHICS", 1}, #else - { "X11_GRAPHICS", 0 }, + {"X11_GRAPHICS", 0}, #endif #if defined(ZEROCOMP) - { "ZEROCOMP", 1 }, + {"ZEROCOMP", 1}, #else - { "ZEROCOMP", 0 }, + {"ZEROCOMP", 0}, #endif #if defined(ZLIB_COMP) - { "ZLIB_COMP", 1 }, + {"ZLIB_COMP", 1}, #else - { "ZLIB_COMP", 0 }, + {"ZLIB_COMP", 0}, #endif #if defined(__BEOS__) - { "__BEOS__", 1 }, + {"__BEOS__", 1}, +#else + {"__BEOS__", 0}, +#endif + {"NH_DEVEL_STATUS", NH_DEVEL_STATUS}, +#if (NH_DEVEL_STATUS == NH_STATUS_RELEASED) + {"DSNH_STATUS_RELEASED", 1}, +#else + {"DSNH_STATUS_RELEASED", 0}, +#endif +#if (NH_DEVEL_STATUS == NH_STATUS_WIP) + {"DSNH_STATUS_WIP", 1}, +#else + {"DSNH_STATUS_WIP", 0}, +#endif +#if (NH_DEVEL_STATUS == NH_STATUS_BETA) + {"DSNH_STATUS_BETA", 1}, #else - { "__BEOS__", 0 }, + {"DSNH_STATUS_BETA", 0}, #endif - { 0, 0 } }; + {0,0} +}; /* Command ids */ #define TODO_GREP 1 diff --git a/util/mdgrep.pl b/util/mdgrep.pl index 244730108c..785a2c185a 100644 --- a/util/mdgrep.pl +++ b/util/mdgrep.pl @@ -1,5 +1,5 @@ #!perl -# NetHack 3.7 mdgrep.pl $NHDT-Date: 1596498260 2020/08/03 23:44:20 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.20 $ +# NetHack 3.7 mdgrep.pl $NHDT-Date: 1710949914 2024/03/20 15:51:54 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.26 $ # Copyright (c) Kenneth Lorber, Kensington, Maryland # NetHack may be freely redistributed. See license for details. @@ -17,21 +17,27 @@ QT_GRAPHICS GNOME_GRAPHICS GEM_GRAPHICS/; # Game Features: -@feature = qw/ZEROCOMP TILES_IN_GLYPHMAP ASCIIGRAPH CLIPPING TEXTCOLOR +@feature = qw/ZEROCOMP TILES_IN_GLYPHMAP ASCIIGRAPH CLIPPING COMPRESS ZLIB_COMP RANDOM SECURE USER_SOUNDS SAFERHANGUP MFLOPPY NOCWD_ASSUMPTIONS VAR_PLAYGROUND DLB SHELL SUSPEND NOSAVEONHANGUP HANGUPHANDLING BSD_JOB_CONTROL MAIL POSIX_JOB_CONTROL INSURANCE UNICODE_DRAWING UNICODE_WIDEWINPORT UNICODE_PLAYERTEXT + CRASHREPORT /; # Miscellaneous -@misc = qw/BETA/; +@misc = qw/CROSSCOMPILE/; # Meta @meta = qw/ALLDOCS/; # convention: use --grep-define ALLDOCS to notate # items that are conditionally available +# Special mappings (if key == value, define prefix_value, prefix is [0]) +$specials = { + NH_DEVEL_STATUS => [qw/DS NH_STATUS_RELEASED NH_STATUS_WIP NH_STATUS_BETA/] +}; + # JUNK: # MICRO BSD __GNUC__ NHSTDC TERMLIB __linux__ LINUX WIN32CON NO_TERMS # ULTRIX_PROTO TERMINFO _DCC DISPMAP OPT_DISPMAP TARGET_API_MAC_CARBON @@ -53,10 +59,10 @@ $outfile = "mdgrep.h"; sub start_file { - ($rev) = ('$NHDT-Revision: 1.20 $') =~ m/: (.*) .$/; - my $date = '$NHDT-Date: 1596498261 2020/08/03 23:44:21 $'; + ($rev) = ('$NHDT-Revision: 1.26 $') =~ m/: (.*) .$/; + my $date = '$NHDT-Date: 1710949915 2024/03/20 15:51:55 $'; my $branch = '$NHDT-Branch: NetHack-3.7 $'; - my $revision = '$NHDT-Revision: 1.20 $'; + my $revision = '$NHDT-Revision: 1.26 $'; open(OUT, ">$outfile") || die "open $outfile: $!"; # NB: Date and Revision below will be modified when mdgrep.h is written to # git - this is correct (but it means you must commit changes to mdgrep.pl @@ -70,6 +76,8 @@ sub start_file { * This file generated by mdgrep.pl version $rev. * DO NOT EDIT! Your changes will be lost. */ + +static struct grep_var grep_vars[]={ E_O_M } @@ -88,7 +96,6 @@ sub gen_magic { # NB: Do NOT make grep_vars const - it needs to be writable for some debugging # options. sub gen_file { - print OUT "static struct grep_var grep_vars[]={\n"; foreach(@_){ if(defined $magic{$_}){ print OUT < #endif @@ -26,11 +28,12 @@ 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); #ifndef WIN_CE @@ -115,7 +118,7 @@ int main(int argc, char *argv[]) { int argno; - const char *dir = (char *) 0; + const char *dir = (char *) 0, *progname = (char *) 0; #ifdef AMIGA char *startdir = (char *) 0; #endif @@ -128,14 +131,21 @@ main(int argc, char *argv[]) if (!dir) dir = exepath(argv[0]); #endif - if (argc == 1 || (argc == 2 && !strcmp(argv[1], "-"))) { + if (argc > 0) + progname = argv[0]; + if (!progname || !*progname) + progname = "recover"; +#ifdef VMS + /*progname = vms_basebame(progname, FALSE);*/ /* needs vmsfiles.obj */ +#endif + + if (argc < 2 || (argc == 2 && !strcmp(argv[1], "-"))) { Fprintf(stderr, "Usage: %s [ -d directory ] base1 [ base2 ... ]\n", - argv[0]); + progname); #if defined(WIN32) || defined(MSDOS) if (dir) { - Fprintf( - stderr, - "\t(Unless you override it with -d, recover will look \n"); + Fprintf(stderr, + "\t(Unless you override it with -d, recover will look \n"); Fprintf(stderr, "\t in the %s directory on your system)\n", dir); } #endif @@ -243,22 +253,6 @@ 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) { @@ -382,6 +376,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); @@ -391,11 +386,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); @@ -410,10 +411,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); } @@ -435,7 +440,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); @@ -453,8 +461,8 @@ 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); + (void) write(fd, (genericptr_t) &indicate, sizeof indicate); + (void) write(fd, (genericptr_t) &cmc, sizeof cmc); } diff --git a/win/Qt/Qt-issues.txt b/win/Qt/Qt-issues.txt index a7418f984e..1bac3106c5 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 4e87b78001..75a3620c10 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) @@ -158,7 +160,7 @@ NetHackQtBind::qt_Splash() capt->repaint(); qApp->processEvents(); } else { - splash = 0; // caller has alrady done this... + splash = 0; // caller has already done this... } } @@ -237,7 +239,7 @@ void NetHackQtBind::qt_askname() int ch = -1; // -1 => new game have_asked = true; - str_copy(default_plname, gp.plname, PL_NSIZ); + str_copy(default_plname, svp.plname, PL_NSIZ); // We do it all here (plus qt_plsel.cpp and qt_svsel.cpp), // nothing in player_selection(). @@ -252,7 +254,7 @@ void NetHackQtBind::qt_askname() NetHackQtSavedGameSelector sgsel((const char **) saved); ch = sgsel.choose(); if (ch >= 0) - str_copy(gp.plname, saved[ch], SIZE(gp.plname)); + str_copy(svp.plname, saved[ch], SIZE(svp.plname)); // caller needs new lock name even if plname[] hasn't changed // because successful get_saved_games() clobbers gs.SAVEF[] ::iflags.renameinprogress = TRUE; @@ -269,6 +271,7 @@ void NetHackQtBind::qt_askname() // success; handle plname[] verification below prior to returning break; } + FALLTHROUGH; /*FALLTHRU*/ case -2: // Quit @@ -282,10 +285,10 @@ void NetHackQtBind::qt_askname() break; } - if (!*gp.plname) + if (!*svp.plname) // in case Choose() returns with plname[] empty - Strcpy(gp.plname, default_plname); - else if (strcmp(gp.plname, default_plname) != 0) + Strcpy(svp.plname, default_plname); + else if (strcmp(svp.plname, default_plname) != 0) // caller needs to set new lock file name ::iflags.renameinprogress = TRUE; return; @@ -471,11 +474,11 @@ void NetHackQtBind::qt_start_menu(winid wid, unsigned long mbehavior UNUSED) } void NetHackQtBind::qt_add_menu(winid wid, const glyph_info *glyphinfo, - const ANY_P * identifier, char ch, char gch, int attr, int clr UNUSED, + const ANY_P * identifier, char ch, char gch, int attr, int clr, const char *str, unsigned itemflags) { NetHackQtWindow* window=id_to_window[(int)wid]; - window->AddMenu(glyphinfo->glyph, identifier, ch, gch, attr, + window->AddMenu(glyphinfo->glyph, identifier, ch, gch, attr, clr, QString::fromLatin1(str), itemflags); } @@ -498,18 +501,32 @@ void NetHackQtBind::qt_update_inventory(int arg UNUSED) main->updateInventory(); // update the paper doll inventory subset /* doesn't work yet - if (gp.program_state.something_worth_saving && iflags.perm_invent) + if (program_state.something_worth_saving && iflags.perm_invent) display_inventory(NULL, false); */ } win_request_info *NetHackQtBind::qt_ctrl_nhwindow( - winid wid UNUSED, - int request UNUSED, - win_request_info *wri UNUSED) + winid wid, + int request, + win_request_info *wri) { NetHackQtWindow* window UNUSED =id_to_window[(int)wid]; - return (win_request_info *) 0; + + if (!wri) + return (win_request_info *) 0; + + switch(request) { + case set_mode: + case request_settings: + break; + case set_menu_promptstyle: + /* = wri->fromcore.menu_promptstyle; */ + break; + default: + break; + } + return wri; } void NetHackQtBind::qt_mark_synch() @@ -564,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) @@ -577,7 +633,7 @@ int NetHackQtBind::qt_nhgetch() * On OSX (possibly elsewhere), this prevents an infinite * loop repeatedly issuing the complaint: QCoreApplication::exec: The event loop is already running - * to stderr if you syncronously start nethack from a terminal + * to stderr if you synchronously start nethack from a terminal * then switch focus back to that terminal and type ^C. * SIGINT -> done1() -> done2() -> yn_function("Really quit?") * in the core asks for another keystroke. @@ -653,7 +709,7 @@ char NetHackQtBind::qt_more() // ^C comment in that routine] when the core triggers --More-- via // done2() -> really_done() -> display_nhwindow(WIN_MESSAGE, TRUE) // (get rid of this if the exec() loop issue gets properly fixed) - if (::gp.program_state.gameover) + if (::program_state.gameover) return ch; // bypass --More-- and just continue with program exit NetHackQtMessageWindow *mesgwin = main ? main->GetMessageWindow() : NULL; @@ -671,6 +727,7 @@ char NetHackQtBind::qt_more() switch (ch) { case '\0': // hypothetical ch = '\033'; + FALLTHROUGH; /*FALLTHRU*/ case ' ': case '\n': @@ -752,7 +809,7 @@ char NetHackQtBind::qt_yn_function(const char *question_, char cbuf[20]; cbuf[0] = '\0'; - // add the prompt to the messsage window + // add the prompt to the message window NetHackQtBind::qt_putstr(WIN_MESSAGE, ATR_BOLD, message); while (result < 0) { @@ -834,7 +891,7 @@ void NetHackQtBind::qt_getlin(const char *prompt, char *line) keybuffer.Drain(); } - // add the prompt with appended response to the messsage window + // add the prompt with appended response to the message window char buf[BUFSZ + 20], *q; /* +20: plenty of extra room for visctrl() */ copynchars(buf, prompt, BUFSZ - 1); q = eos(buf); @@ -887,16 +944,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]; @@ -948,7 +1017,7 @@ void NetHackQtBind::qt_putmsghistory(const char *msg, boolean is_restoring) if (msg) { //raw_printf("msg='%s'", msg); window->PutStr(ATR_NONE, QString::fromLatin1(msg)); -#ifdef DUMPLOG +#ifdef DUMPLOG_CORE dumplogmsg(msg); #endif } else if (msgs_saved) { @@ -956,7 +1025,7 @@ void NetHackQtBind::qt_putmsghistory(const char *msg, boolean is_restoring) for (int i = 0; i < msgs_strings->size(); ++i) { const QString &nxtmsg = msgs_strings->at(i); window->PutStr(ATR_NONE, nxtmsg); -#ifdef DUMPLOG +#ifdef DUMPLOG_CORE dumplogmsg(nxtmsg.toLatin1().constData()); #endif } @@ -1043,7 +1112,7 @@ boolean NetHackQtBind::msgs_initd = false; static void Qt_positionbar(char *) {} #endif -#if defined(SND_LIB_QTSOUND) && !defined(NO_QT_SOUND) +#if defined(SND_LIB_QTSOUND) && !defined(QT_NO_SOUND) void NetHackQtBind::qtsound_init_nhsound(void) { } @@ -1069,15 +1138,11 @@ void NetHackQtBind::qtsound_ambience(int32_t ambienceid UNUSED, int32_t ambience void NetHackQtBind::qtsound_verbal(char *text UNUSED, int32_t gender UNUSED, int32_t tone UNUSED, int32_t vol UNUSED, int32_t moreinfo UNUSED) { } -#endif -#if defined(USER_SOUNDS) && !defined(QT_NO_SOUND) QSoundEffect *effect = NULL; -#endif void NetHackQtBind::qtsound_play_usersound(char *filename, int32_t volume, int32_t idx UNUSED) { -#if defined(USER_SOUNDS) && !defined(QT_NO_SOUND) if (!effect) effect = new QSoundEffect(nethack_qt_::NetHackQtBind::mainWidget()); if (effect) { @@ -1086,11 +1151,8 @@ void NetHackQtBind::qtsound_play_usersound(char *filename, int32_t volume, int32 effect->setSource(QUrl::fromLocalFile(filename)); effect->play(); } -#else - nhUse(filename); - nhUse(volume); -#endif } +#endif } // namespace nethack_qt_ @@ -1105,8 +1167,9 @@ struct window_procs Qt_procs = { | WC2_SELECTSAVED #endif #ifdef ENHANCED_SYMBOLS - | WC2_U_UTF8STR | WC2_U_24BITCOLOR + | WC2_U_UTF8STR #endif + | WC2_EXTRACOLORS | WC2_STATUSLINES), {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ nethack_qt_::NetHackQtBind::qt_init_nhwindows, @@ -1150,15 +1213,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 b0b9f4f3a4..d0c71759c5 100644 --- a/win/Qt/qt_bind.h +++ b/win/Qt/qt_bind.h @@ -71,6 +71,9 @@ class NetHackQtBind : NetHackQtBindBase { 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 @@ 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_preference_update(const char *optname); static char *qt_getmsghistory(boolean init); diff --git a/win/Qt/qt_glyph.cpp b/win/Qt/qt_glyph.cpp index 98691bea72..9aa739fcec 100644 --- a/win/Qt/qt_glyph.cpp +++ b/win/Qt/qt_glyph.cpp @@ -156,7 +156,6 @@ NetHackQtGlyphs::drawBorderedCell( drawGlyph(painter, glyph, tileidx, lox + 1, loy + 1, reversed); -#ifdef TEXTCOLOR if (border != NO_BORDER) { // gray would be a better mid-point between red and cyan but it // doesn't show up well enough against the wall tile background @@ -199,9 +198,6 @@ NetHackQtGlyphs::drawBorderedCell( } } } -#else - nhUse(border); -#endif } // mis-named routine to get the pixmap for a particular glyph diff --git a/win/Qt/qt_main.cpp b/win/Qt/qt_main.cpp index c1b83d3c10..e364cb0fa3 100644 --- a/win/Qt/qt_main.cpp +++ b/win/Qt/qt_main.cpp @@ -764,7 +764,11 @@ NetHackQtMainWindow::NetHackQtMainWindow(NetHackQtKeyBuffer& ks) : if (actchar[0]) { QString name = menuitem; QAction *action = item[i].menu->addAction(name); - action->setData(actchar); +#if QT_VERSION < 0x060000 + action->setData(actchar); +#else + action->setData(QString(actchar)); +#endif } } else { item[i].menu->addSeparator(); @@ -877,17 +881,14 @@ NetHackQtMainWindow::NetHackQtMainWindow(NetHackQtKeyBuffer& ks) : int w=screensize.width()-10; // XXX arbitrary extra space for frame int h=screensize.height()-50; - int maxwn; - int maxhn; - if (qt_tilewidth != NULL) { - maxwn = atoi(qt_tilewidth) * COLNO + 10; - } else { - maxwn = 1400; - } - if (qt_tileheight != NULL) { - maxhn = atoi(qt_tileheight) * ROWNO * 6/4; - } else { - maxhn = 1024; + int maxwn = 1400; + int maxhn = 1024; + if (qt_settings != NULL) { + auto glyphs = &qt_settings->glyphs(); + if (glyphs != NULL) { + maxwn = glyphs->width() * (COLNO + 1); + maxhn = glyphs->height() * ROWNO * 6/4 + glyphs->height() * 10; + } } // Be exactly the size we want to be - full map... @@ -1014,7 +1015,7 @@ bool NetHackQtMainWindow::ok_for_command() * FIXME: it would be much better to gray-out inapplicable entries * when popping up a command menu instead of needing this. */ - if (::gp.program_state.input_state != commandInp) { + if (::program_state.input_state != commandInp) { NetHackQtBind::qt_nhbell(); // possibly call doKeys("\033"); here? return false; @@ -1058,7 +1059,7 @@ void NetHackQtMainWindow::doQuit(bool) // in case someone wants to change that #ifdef MACOS QString info = nh_qsprintf("This will end your xNetHack session.%s", - !gp.program_state.something_worth_saving ? "" + !program_state.something_worth_saving ? "" : "\n(Cancel quitting and use the Save command" "\nto save your current game.)"); /* this is similar to closeEvent but the details are different; @@ -1076,7 +1077,7 @@ void NetHackQtMainWindow::doQuit(bool) break; // return to game case 1: // quit -- bypass the prompting preformed by done2() - gp.program_state.stopprint++; + program_state.stopprint++; ::done(QUIT); /*NOTREACHED*/ break; @@ -1405,7 +1406,7 @@ void NetHackQtMainWindow::keyPressEvent(QKeyEvent* event) void NetHackQtMainWindow::closeEvent(QCloseEvent *e UNUSED) { int ok = 0; - if ( gp.program_state.something_worth_saving ) { + if ( program_state.something_worth_saving ) { /* this used to offer "Save" and "Cancel" but cancel (ignoring the close attempt) won't work if user has clicked on the window's Close button */ @@ -1420,7 +1421,7 @@ void NetHackQtMainWindow::closeEvent(QCloseEvent *e UNUSED) case 1: // quit -- bypass the prompting preformed by done2() ok = 1; - gp.program_state.stopprint++; + program_state.stopprint++; ::done(QUIT); /*NOTREACHED*/ break; @@ -1455,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 946d2985c2..8e620073d9 100644 --- a/win/Qt/qt_map.cpp +++ b/win/Qt/qt_map.cpp @@ -77,49 +77,6 @@ extern int qt_compact_mode; namespace nethack_qt_ { -#ifdef TEXTCOLOR -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]; - } -} -#endif NetHackQtMapViewport::NetHackQtMapViewport(NetHackQtClickBuffer& click_sink) : QWidget(NULL), @@ -182,10 +139,8 @@ void NetHackQtMapViewport::paintEvent(QPaintEvent* event) painter.begin(this); unsigned special, tileidx; -#ifdef TEXTCOLOR uint32 color; uint32 framecolor; -#endif if (iflags.wc_ascii_map) { // You enter a VERY primitive world! @@ -204,15 +159,20 @@ void NetHackQtMapViewport::paintEvent(QPaintEvent* event) if (SYMHANDLING(H_IBM)) { ch = cp437(ch); } -#ifdef TEXTCOLOR color = Glyphcolor(i, j); - painter.setPen(nhcolor_to_pen(color)); -#else - painter.setPen(Qt::green); -#endif + painter.setPen(NetHackQtBind::nhcolor_to_pen(color)); if (!DrawWalls(painter, i * gW, j * gH, gW, gH, ch)) { + ushort utf16[3]; + if (ch < 0x10000) { + utf16[0] = static_cast(ch); + utf16[1] = 0; + } else { + utf16[0] = static_cast((ch >> 10) + 0xD7C0); + utf16[1] = static_cast((ch & 0x3FF) + 0xDC00); + utf16[2] = 0; + } painter.drawText(i * gW, j * gH, gW, gH, Qt::AlignCenter, - QString(QChar(ch)).left(1)); + QString::fromUtf16(utf16)); } if ((special & MG_PET) != 0 && ::iflags.hilite_pet) { painter.drawPixmap(QPoint(i * gW, j * gH), @@ -222,16 +182,14 @@ void NetHackQtMapViewport::paintEvent(QPaintEvent* event) painter.drawPixmap(QPoint(i * gW, j * gH), pile_annotation); } -#ifdef TEXTCOLOR 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, qt_settings->glyphs().height() - 1); } -#endif } } @@ -253,25 +211,20 @@ void NetHackQtMapViewport::paintEvent(QPaintEvent* event) painter.drawPixmap(QPoint(i * gW, j * gH), pile_annotation); } -#ifdef TEXTCOLOR 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, qt_settings->glyphs().height() - 1); } -#endif } } } if (garea.contains(cursor)) { - int hp100; - if (Upolyd) { - hp100=u.mhmax ? u.mh*100/u.mhmax : 100; - } else { + { int hp = !Upolyd ? u.uhp : u.mh, hpmax = !Upolyd ? u.uhpmax : u.mhmax, hp100 = hpmax ? (hp * 100 / hpmax) : 100; @@ -528,7 +481,7 @@ void NetHackQtMapViewport::Clear() { for (int j = 0; j < ROWNO; ++j) { // - // FIXME: map column 0 should be surpressed from being displayed + // FIXME: map column 0 should be suppressed from being displayed // Glyph(0, j) = GLYPH_NOTHING; Glyphttychar(0, j) = ' '; @@ -585,15 +538,20 @@ void NetHackQtMapViewport::PrintGlyph(int x, int y, Glyphcolor(x, y) = (uint32) glyphinfo->gm.sym.color; GlyphFramecolor(x, y) = (uint32) bkglyphinfo->framecolor; #ifdef ENHANCED_SYMBOLS - if (SYMHANDLING(H_UTF8) - && glyphinfo->gm.u - && glyphinfo->gm.u->utf8str) { + if (SYMHANDLING(H_UTF8) && glyphinfo->gm.u && glyphinfo->gm.u->utf8str) { Glyphttychar(x, y) = glyphinfo->gm.u->utf32ch; - if (glyphinfo->gm.u->ucolor != 0) { - Glyphcolor(x, y) = glyphinfo->gm.u->ucolor | 0x80000000; - } } #endif + if (glyphinfo->gm.customcolor != 0) { + uint32 nhcolor = COLORVAL(glyphinfo->gm.customcolor); + if (glyphinfo->gm.customcolor == nhcolor) { + /* 24-bit color */ + Glyphcolor(x, y) = COLORVAL(glyphinfo->gm.customcolor) | 0x80000000; + } else { + /* NH_BASIC_COLOR */ + Glyphcolor(x, y) = COLORVAL(glyphinfo->gm.customcolor); + } + } Glyphflags(x, y) = glyphinfo->gm.glyphflags; Glyphtileidx(x, y) = (unsigned short) glyphinfo->gm.tileidx; Changed(x, y); @@ -921,11 +879,9 @@ void NetHackQtMapWindow::paintEvent(QPaintEvent* event) qt_settings->glyphs().width()-1, qt_settings->glyphs().height()-1, nhcolor_to_pen(framecolor).color()); - } + } painter.setPen( Qt::green ); -#ifdef TEXTCOLOR painter.setPen( nhcolor_to_pen(color) ); -#endif painter.drawText( i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height(), @@ -934,7 +890,6 @@ void NetHackQtMapWindow::paintEvent(QPaintEvent* event) Qt::AlignCenter, QString(QChar(ch)).left(1) ); -#ifdef TEXTCOLOR if ((special & MG_PET) != 0 && ::iflags.hilite_pet) { painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), @@ -945,7 +900,6 @@ void NetHackQtMapWindow::paintEvent(QPaintEvent* event) j*qt_settings->glyphs().height()), pile_annotation); } -#endif if (framecolor != NO_COLOR) { painter.setPen( nhcolor_to_pen(framecolor) ); painter.drawRect(i*qt_settings->glyphs().width(), @@ -966,7 +920,6 @@ void NetHackQtMapWindow::paintEvent(QPaintEvent* event) uint32 framecolor = GlyphFramecolor(i,j); qt_settings->glyphs().drawCell(painter, g, tileidx, i, j); -#ifdef TEXTCOLOR if ((special & MG_PET) != 0 && ::iflags.hilite_pet) { painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), @@ -977,7 +930,6 @@ void NetHackQtMapWindow::paintEvent(QPaintEvent* event) j*qt_settings->glyphs().height()), pile_annotation); } -#endif if (framecolor != NO_COLOR) { painter.setPen( nhcolor_to_pen(framecolor) ); painter.drawRect(i*qt_settings->glyphs().width(), @@ -990,11 +942,24 @@ void NetHackQtMapWindow::paintEvent(QPaintEvent* event) } if (garea.contains(cursor)) { - int hp100; - if (Upolyd) { - hp100=u.mhmax ? u.mh*100/u.mhmax : 100; - } else { - hp100=u.uhpmax ? u.uhp*100/u.uhpmax : 100; + { + int hp100; + if (Upolyd) { + hp100=u.mhmax ? u.mh*100/u.mhmax : 100; + } else { + hp100=u.uhpmax ? u.uhp*100/u.uhpmax : 100; + } + + if (hp100 > 75) + painter.setPen(Qt::white); + else if (hp100 > 50) + painter.setPen(Qt::yellow); + else if (hp100 > 25) + painter.setPen(QColor(0xff, 0xbf, 0x00)); // orange + else if (hp100 > 10) + painter.setPen(Qt::red); + else + painter.setPen(Qt::magenta); } if (hp100 > 75) diff --git a/win/Qt/qt_menu.cpp b/win/Qt/qt_menu.cpp index a5473155f9..6271e7fd70 100644 --- a/win/Qt/qt_menu.cpp +++ b/win/Qt/qt_menu.cpp @@ -220,6 +220,7 @@ NetHackQtMenuWindow::NetHackQtMenuWindow(QWidget *parent) : this, SLOT(TableCellClicked(int,int))); setLayout(grid); + setModal(true); } NetHackQtMenuWindow::~NetHackQtMenuWindow() @@ -258,7 +259,7 @@ NetHackQtMenuWindow::MenuItem::~MenuItem() } void NetHackQtMenuWindow::AddMenu(int glyph, const ANY_P *identifier, - char ch, char gch, int attr, + char ch, char gch, int attr, int clr, const QString& str, unsigned itemflags) { bool presel = (itemflags & MENU_ITEMFLAGS_SELECTED) != 0; @@ -279,11 +280,11 @@ void NetHackQtMenuWindow::AddMenu(int glyph, const ANY_P *identifier, itemlist[itemcount].ch = ch; itemlist[itemcount].gch = gch; itemlist[itemcount].attr = attr; + itemlist[itemcount].color = clr; itemlist[itemcount].str = str; itemlist[itemcount].selected = itemlist[itemcount].preselected = presel; itemlist[itemcount].itemflags = itemflags; itemlist[itemcount].count = -1L; - itemlist[itemcount].color = -1; // Display the boulder symbol correctly if (str.left(8) == "boulder\t") { int bracket = str.indexOf('['); @@ -293,12 +294,6 @@ void NetHackQtMenuWindow::AddMenu(int glyph, const ANY_P *identifier, + str.mid(bracket+2); } } - int mcolor, mattr; - if (attr == 0 && ::iflags.use_menu_color - && get_menu_coloring(str.toLatin1().constData(), &mcolor, &mattr)) { - itemlist[itemcount].attr = mattr; - itemlist[itemcount].color = mcolor; - } ++itemcount; if (glyph != NO_GLYPH) @@ -488,64 +483,56 @@ 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) +void NetHackQtMenuWindow::SetTwiAttr(QTableWidgetItem *twi, int color, int attr) { - 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; + if (color != NO_COLOR) { + const QPen qp = NetHackQtBind::nhcolor_to_pen(color); + twi->setForeground(qp.color()); + } + + if (attr != ATR_NONE) { + QFont itemfont(table->font()); + switch (attr) { + case ATR_BOLD: + itemfont.setWeight(QFont::Bold); + twi->setFont(itemfont); + break; + case ATR_ITALIC: + itemfont.setItalic(true); + twi->setFont(itemfont); + break; + case ATR_DIM: + twi->setFlags(Qt::NoItemFlags); + break; + case ATR_ULINE: + itemfont.setUnderline(true); + twi->setFont(itemfont); + break; + case ATR_INVERSE: { + QBrush fg = twi->foreground(); + QBrush bg = twi->background(); + if (fg.color() == bg.color()) { + // default foreground and background come up the same for + // some unknown reason + //[pr: both are set to 'Qt::color1' which has same RGB + // value as 'Qt::black'; X11 on OSX behaves similarly] + if (fg.color() == Qt::color1) { + fg = Qt::black; + bg = Qt::white; + } else { + fg = (bg.color() == Qt::white) ? Qt::black : Qt::white; + } + } + twi->setForeground(bg); + twi->setBackground(fg); + break; + } + case ATR_BLINK: + // not supported + break; + } /* switch */ + } /* if attr != ATR_NONE */ } -#endif void NetHackQtMenuWindow::AddRow(int row, const MenuItem& mi) { @@ -618,48 +605,7 @@ void NetHackQtMenuWindow::AddRow(int row, const MenuItem& mi) table->item(row, 4)->setFlags(Qt::ItemIsEnabled); WidenColumn(4, fm.QFM_WIDTH(text)); - if ((int) mi.color != -1) { - twi->setForeground(colors[mi.color].q); - } - - if (mi.attr != ATR_NONE) { - QFont itemfont(table->font()); - switch (mi.attr) { - case ATR_BOLD: - itemfont.setWeight(QFont::Bold); - twi->setFont(itemfont); - break; - case ATR_DIM: - twi->setFlags(Qt::NoItemFlags); - break; - case ATR_ULINE: - itemfont.setUnderline(true); - twi->setFont(itemfont); - break; - case ATR_INVERSE: { - QBrush fg = twi->foreground(); - QBrush bg = twi->background(); - if (fg.color() == bg.color()) { - // default foreground and background come up the same for - // some unknown reason - //[pr: both are set to 'Qt::color1' which has same RGB - // value as 'Qt::black'; X11 on OSX behaves similarly] - if (fg.color() == Qt::color1) { - fg = Qt::black; - bg = Qt::white; - } else { - fg = (bg.color() == Qt::white) ? Qt::black : Qt::white; - } - } - twi->setForeground(bg); - twi->setBackground(fg); - break; - } - case ATR_BLINK: - // not supported - break; - } /* switch */ - } /* if mi.attr != ATR_NONE */ + SetTwiAttr(twi, mi.color, mi.attr); } void NetHackQtMenuWindow::WidenColumn(int column, int width) @@ -1010,6 +956,7 @@ NetHackQtTextWindow::NetHackQtTextWindow(QWidget *parent) : setFocusPolicy(Qt::StrongFocus); // needed so that keystrokes get sent to our keyPressEvent() lines->setFocusPolicy(Qt::NoFocus); + setModal(true); } void NetHackQtTextWindow::doUpdate() @@ -1066,7 +1013,7 @@ void NetHackQtTextWindow::UseRIP(int how, time_t when) /* Put name on stone */ (void) snprintf(rip_line[NAME_LINE], STONE_LINE_LEN + 1, - "%.*s", STONE_LINE_LEN, gp.plname); + "%.*s", STONE_LINE_LEN, svp.plname); /* Put $ on stone; to keep things safe and relatively simple, impose an arbitrary @@ -1168,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(); @@ -1344,13 +1290,13 @@ void NetHackQtMenuOrTextWindow::StartMenu(bool using_WIN_INVEN) } void NetHackQtMenuOrTextWindow::AddMenu( int glyph, const ANY_P* identifier, - char ch, char gch, int attr, + char ch, char gch, int attr, int clr, const QString& str, unsigned itemflags) { if (!actual) MenuOrText_too_soon_warning("AddMenu"); else - actual->AddMenu(glyph, identifier, ch, gch, attr, str, itemflags); + actual->AddMenu(glyph, identifier, ch, gch, attr, clr, str, itemflags); } void NetHackQtMenuOrTextWindow::EndMenu(const QString& prompt) { diff --git a/win/Qt/qt_menu.h b/win/Qt/qt_menu.h index bdb3f83c51..844a470f45 100644 --- a/win/Qt/qt_menu.h +++ b/win/Qt/qt_menu.h @@ -58,7 +58,7 @@ class NetHackQtMenuWindow : public QDialog, public NetHackQtWindow { virtual void StartMenu(bool using_WIN_INVEN = false); virtual void AddMenu(int glyph, const ANY_P *identifier, - char ch, char gch, int attr, + char ch, char gch, int attr, int clr, const QString& str, unsigned itemflags); virtual void EndMenu(const QString& prompt); virtual int SelectMenu(int how, MENU_ITEM_P **menu_list); @@ -128,6 +128,7 @@ public slots: bool isSelected(int row); long count(int row); + void SetTwiAttr(QTableWidgetItem *twi, int color, int attr); void AddRow(int row, const MenuItem& mi); void WidenColumn(int column, int width); void PadMenuColumns(bool split_descr); @@ -195,7 +196,7 @@ class NetHackQtMenuOrTextWindow : public NetHackQtWindow { // Menu virtual void StartMenu(bool using_WIN_INVENT = false); virtual void AddMenu(int glyph, const ANY_P *identifier, - char ch, char gch, int attr, + char ch, char gch, int attr, int clr, const QString& str, unsigned itemflags); virtual void EndMenu(const QString& prompt); virtual int SelectMenu(int how, MENU_ITEM_P **menu_list); diff --git a/win/Qt/qt_plsel.cpp b/win/Qt/qt_plsel.cpp index ae39c96266..2dd7900194 100644 --- a/win/Qt/qt_plsel.cpp +++ b/win/Qt/qt_plsel.cpp @@ -40,17 +40,17 @@ extern "C" { /* check whether plname[] is among the list of generic user names */ static bool generic_plname() { - if (*gp.plname) { + if (*svp.plname) { const char *sptr, *p; const char *genericusers = sysopt.genericusers; - int ln = (int) strlen(gp.plname); + int ln = (int) strlen(svp.plname); if (!genericusers || !*genericusers) genericusers = "player games"; else if (!strcmp(genericusers, "*")) /* "*" => always ask for name */ return true; - while ((sptr = strstri(genericusers, gp.plname)) != NULL) { + while ((sptr = strstri(genericusers, svp.plname)) != NULL) { /* check for full word: start of list or following a space */ if ((sptr == genericusers || sptr[-1] == ' ') /* and also preceding a space or at end of list */ @@ -262,8 +262,8 @@ NetHackQtPlayerSelector::NetHackQtPlayerSelector( // if plname[] contains a generic user name, clear it if (generic_plname()) - *gp.plname = '\0'; - name->setText(gp.plname); + *svp.plname = '\0'; + name->setText(svp.plname); connect(name, SIGNAL(textChanged(const QString&)), this, SLOT(selectName(const QString&))); name->setFocus(); @@ -544,7 +544,7 @@ void NetHackQtPlayerSelector::Randomize() // if plname[] is empty, disable [Play], otherwise [Play] is the default void NetHackQtPlayerSelector::plnamePlayVsQuit() { - if (*gp.plname) { + if (*svp.plname) { play_btn->setEnabled(true); play_btn->setDefault(true); //quit_btn->setDefault(false); @@ -563,7 +563,7 @@ void NetHackQtPlayerSelector::selectName(const QString& n) // (it would be better to set up a validator that rejects leading spaces) while (*name_str == ' ') ++name_str; - str_copy(gp.plname, name_str, PL_NSIZ); + str_copy(svp.plname, name_str, PL_NSIZ); // possibly enable or disable the [Play] button plnamePlayVsQuit(); } diff --git a/win/Qt/qt_set.cpp b/win/Qt/qt_set.cpp index e0eebb282f..48de4387ab 100644 --- a/win/Qt/qt_set.cpp +++ b/win/Qt/qt_set.cpp @@ -86,6 +86,7 @@ NetHackQtSettings::NetHackQtSettings() : normalfixed("courier"), #endif large("times"), + small("times"), theglyphs(0) { int default_fontsize; @@ -118,11 +119,13 @@ NetHackQtSettings::NetHackQtSettings() : // Tile/font sizes read from .xnethackrc if (qt_tilewidth != NULL) { tilewidth.setValue(atoi(qt_tilewidth)); - delete[] qt_tilewidth; + free(qt_tilewidth); + qt_tilewidth = NULL; } if (qt_tileheight != NULL) { tileheight.setValue(atoi(qt_tileheight)); - delete[] qt_tileheight; + free(qt_tileheight); + qt_tileheight = NULL; } if (qt_fontsize != NULL) { switch (tolower(qt_fontsize[0])) { @@ -132,7 +135,8 @@ NetHackQtSettings::NetHackQtSettings() : case 's': default_fontsize = 3; break; case 't': default_fontsize = 4; break; } - delete[] qt_fontsize; + free(qt_fontsize); + qt_fontsize = NULL; } theglyphs=new NetHackQtGlyphs(); @@ -217,7 +221,7 @@ NetHackQtGlyphs& NetHackQtSettings::glyphs() { // Caveat: // 'theglyphs' will be Null if the tiles file couldn't be loaded; - // the game can still procede with an ascii map in that situation. + // the game can still proceed with an ascii map in that situation. return *theglyphs; } @@ -323,6 +327,13 @@ const QFont& NetHackQtSettings::largeFont() return large; } +const QFont& NetHackQtSettings::smallFont() +{ + static int size[]={ 14, 12, 10, 8, 8 }; + small.setPointSize(size[fontsize.currentIndex()]); + return small; +} + bool NetHackQtSettings::ynInMessages() { return !qt_compact_mode && !iflags.wc_popup_dialog; diff --git a/win/Qt/qt_set.h b/win/Qt/qt_set.h index 14ec92c025..99a2cf3cd1 100644 --- a/win/Qt/qt_set.h +++ b/win/Qt/qt_set.h @@ -36,6 +36,7 @@ class NetHackQtSettings : public QDialog { const QFont& normalFont(); const QFont& normalFixedFont(); const QFont& largeFont(); + const QFont& smallFont(); bool ynInMessages(); @@ -71,7 +72,7 @@ public slots: QComboBox fontsize; - QFont normal, normalfixed, large; + QFont normal, normalfixed, large, small; NetHackQtGlyphs* theglyphs; #if 0 diff --git a/win/Qt/qt_stat.cpp b/win/Qt/qt_stat.cpp index 81e825b9ff..13aae4108e 100644 --- a/win/Qt/qt_stat.cpp +++ b/win/Qt/qt_stat.cpp @@ -20,6 +20,7 @@ // varying number of icons (one or more, each paired with...) // corresponding text (Alignment plus zero or more status conditions // including Hunger if not "normal" and encumbrance if not "normal") +// and version description (text only) right justified (when enabled) // // The hitpoint bar spans the width of the status window when enabled. // Title and location are centered. @@ -47,7 +48,7 @@ // with a separator between Cha:NN and it. Time, when active, is // placed after Gold. Score, if enabled and active, is shown in the // filler slot before Gold. When there are no Conditions to display, -// there is an an invisible fake one (blank icon over blank text) +// there is an invisible fake one (blank icon over blank text) // rendered in order to preserve the vertical space they need. // // FIXME: @@ -59,11 +60,15 @@ // There are separate icons for Satiated and Hungry, but Weak, Fainting, // and Fainted all share the Hungry one. Weak should have its own, // Fainting+Fainted should have another. The current two depict -// plates with cutlery which is a bit of an anachronism. Statiated +// plates with cutlery which is a bit of an anachronism. Satiated // could be replaced by a figure in profile with a bulging belly, // Hungry similar but with a slightly concave belly, Weak either a // collapsing figure or a much larger concavity or both, Fainting/ // Fainted a fully collapsed figure. +// If 'version' is being shown but gets squeezed by the cumulative width +// of conditions, the default clipping shows the center portion of the +// text string with beginning and end omitted. We want to force the +// end of the string to be shown with only the beginning omitted. // // TODO: // If/when status conditions become too wide for the status window, scale @@ -138,6 +143,7 @@ NetHackQtStatusWindow::NetHackQtStatusWindow() : lev(this,"Lev"), // 'other' conditions fly(this,"Fly"), ride(this,"Ride"), + vers(this,""), // optional, right justified after 'conditions' hline1(this), // separators hline2(this), hline3(this), @@ -149,7 +155,8 @@ NetHackQtStatusWindow::NetHackQtStatusWindow() : alreadyfullhp(false), was_polyd(false), had_exp(false), - had_score(false) + had_score(false), + prev_versinfo(0U) { if (!qt_compact_mode) { int w = NetHackQtBind::mainWidget()->width(); @@ -195,6 +202,8 @@ NetHackQtStatusWindow::NetHackQtStatusWindow() : p_fly = QPixmap(fly_xpm); p_ride = QPixmap(ride_xpm); + p_vers = QPixmap(blank_xpm); // same all-background pixmap as blank2 + str.setIcon(p_str, "strength"); dex.setIcon(p_dex, "dexterity"); con.setIcon(p_con, "constitution"); @@ -221,6 +230,8 @@ NetHackQtStatusWindow::NetHackQtStatusWindow() : fly.setIcon(p_fly, "flying"); ride.setIcon(p_ride, "riding"); + vers.setIcon(p_vers); // used to align text-only version with conditions + // separator lines #if __cplusplus >= 202002L hline1.setFrameStyle(static_cast(QFrame::HLine) @@ -251,7 +262,7 @@ NetHackQtStatusWindow::NetHackQtStatusWindow() : vline2.setLineWidth(1); vline2.hide(); // padding to keep row 2 aligned with row 1, never shown - // set up last but shown first (above name) via layout below */ + // when 'hitpointbar' is On, the bar gets drawn above name/title QHBoxLayout *hpbar = InitHitpointBar(); // 'statuslines' takes a value of 2 or 3; we use 3 as a request to put @@ -304,7 +315,7 @@ NetHackQtStatusWindow::NetHackQtStatusWindow() : statbox->addWidget(&time); } vbox->addLayout(statbox); - vbox->addWidget(&hline3); // separtor before Time+Score or Conditions + vbox->addWidget(&hline3); // separator before Time+Score or Conditions if (spreadout) { // when not condensed, put Time and Score on an extra row; since // they're both optionally displayed, their row might be empty @@ -342,7 +353,15 @@ NetHackQtStatusWindow::NetHackQtStatusWindow() : condbox->addWidget(&lev); condbox->addWidget(&fly); condbox->addWidget(&ride); - condbox->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + condbox->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + // left justify conditions, whether 'vers' is present or absent + int stretch = 0; + condbox->addStretch(++stretch); // stretch==1 + // to right justify 'vers', use bigger stretch than condbox separator + condbox->addWidget(&vers, ++stretch, // stretch==2 + // text is below a blank icon, so bottom-aligned with the rest + // of the line with its text centered within that bottom area + Qt::AlignRight | Qt::AlignVCenter); vbox->addLayout(condbox); setLayout(vbox); #endif @@ -396,6 +415,7 @@ void NetHackQtStatusWindow::doUpdate() lev.setFont(normal); fly.setFont(normal); ride.setFont(normal); + vers.setFont(normal); // shouldn't need small font updateStats(); } @@ -496,6 +516,11 @@ void NetHackQtStatusWindow::resizeEvent(QResizeEvent*) lev.setGeometry(x,y,iw,lh); x+=iw; fly.setGeometry(x,y,iw,lh); x+=iw; ride.setGeometry(x,y,iw,lh); x+=iw; +#if 0 + // [not fully implemented; this big chunk of code is no longer used] + //X = [get version, set font, measure width of version string] + vers.setGeometry(width() - X, y, width(), lh); x=width(); +#endif x=0; y+=lh; #else // This is clumsy. But QLayout objects are proving balky. @@ -540,6 +565,7 @@ void NetHackQtStatusWindow::fadeHighlighting() //time.dissipateHighlight(); score.dissipateHighlight(); + //vers.dissipateHighlight(); // never gets highlighted hunger.dissipateHighlight(); encumber.dissipateHighlight(); @@ -813,21 +839,36 @@ void NetHackQtStatusWindow::updateStats() if (Flying) ++k, fly.show(); else fly.hide(); if (u.usteed) ++k, ride.show(); else ride.hide(); + // version is optional; displayed to the right of conditions when present + if (::flags.showvers) { + // the value only changes if user modifies the 'versinfo' option + if (::flags.versinfo != prev_versinfo) { + char vbuf[80], *cvers = status_version(vbuf, sizeof vbuf, FALSE); + vers.setLabel(QString(cvers)); + // FIXME: this shouldn't be necessary but without hide() before + // forthcoming show(), the new value isn't appearing + if (!vers.isHidden()) vers.hide(); + prev_versinfo = ::flags.versinfo; + } + ++k, vers.show(); + } else + vers.hide(); + if (Upolyd) { buf = nh_capitalize_words(pmname(&mons[u.umonnum], ::flags.female ? FEMALE : MALE)); } else { - buf = rank_of(u.ulevel, gp.pl_character[0], ::flags.female); + buf = rank_of(u.ulevel, svp.pl_character[0], ::flags.female); } QString buf2; char buf3[BUFSZ]; - buf2 = nh_qsprintf("%s the %s", upstart(strcpy(buf3, gp.plname)), + buf2 = nh_qsprintf("%s the %s", upstart(strcpy(buf3, svp.plname)), buf.toLatin1().constData()); name.setLabel(buf2, NetHackQtLabelledIcon::NoNum, u.ulevel); if (!describe_level(buf3, 0)) { Sprintf(buf3, "%s, level %d", - gd.dungeons[u.uz.dnum].dname, ::depth(&u.uz)); + svd.dungeons[u.uz.dnum].dname, ::depth(&u.uz)); } dlevel.setLabel(buf3); @@ -925,10 +966,11 @@ void NetHackQtStatusWindow::updateStats() if (::flags.time) { // hypothetically Time could grow to enough digits to have trouble // fitting, but it's not worth worrying about - time.setLabel("Time:", (long) gm.moves); + time.setLabel("Time:", (long) svm.moves); } else { time.setLabel(""); } + #ifdef SCORE_ON_BOTL int score_toggled = !had_score ^ !::flags.showscore; if (::flags.showscore) { @@ -1023,6 +1065,9 @@ void NetHackQtStatusWindow::updateStats() fly.setCompareMode(NeitherIsBetter); ride.highlightWhenChanging(); ride.setCompareMode(NeitherIsBetter); + // not a true status condition; doesn't change (unless 'showvers' + // gets toggled or 'versinfo' is modified) so doesn't get highlighted + vers.setCompareMode(NoCompare); } } diff --git a/win/Qt/qt_stat.h b/win/Qt/qt_stat.h index c09fae6c4d..43cf2f154f 100644 --- a/win/Qt/qt_stat.h +++ b/win/Qt/qt_stat.h @@ -65,6 +65,7 @@ private slots: QPixmap p_lev; QPixmap p_fly; QPixmap p_ride; + QPixmap p_vers; // version, when shown, is like a pseudo-condition /* * Status fields, in display order (the three separator lines @@ -127,6 +128,8 @@ private slots: NetHackQtLabelledIcon lev; NetHackQtLabelledIcon fly; NetHackQtLabelledIcon ride; + /* to right of conditions, right justified */ + NetHackQtLabelledIcon vers; // version QFrame hline1; // between dlevel and characteristics QFrame hline2; // between characteristics and regular status fields @@ -143,6 +146,7 @@ private slots: bool was_polyd; bool had_exp; bool had_score; + unsigned prev_versinfo; QHBoxLayout *InitHitpointBar(); void HitpointBar(); diff --git a/win/Qt/qt_svsel.cpp b/win/Qt/qt_svsel.cpp index 4cdffd909f..f3ce84d846 100644 --- a/win/Qt/qt_svsel.cpp +++ b/win/Qt/qt_svsel.cpp @@ -23,19 +23,15 @@ // ... 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 -// enchancement: 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 // having SELECTSAVED defined or if the run-time option 'selectsaved' // is False. SELECTSAVED used to be forced for Qt but isn't any more. -// Howver, we include this code unconditionally. +// However, we include this code unconditionally. // extern "C" { diff --git a/win/Qt/qt_win.cpp b/win/Qt/qt_win.cpp index 907d260d02..1f356399c4 100644 --- a/win/Qt/qt_win.cpp +++ b/win/Qt/qt_win.cpp @@ -22,7 +22,7 @@ // and also because this is my first major application of Qt. // // The problem of NetHack's getkey requirement is solved by intercepting -// key events by overiding QApplication::notify(...), and putting them in +// key events by overriding QApplication::notify(...), and putting them in // a buffer. Mouse clicks on the map window are treated with a similar // buffer. When the NetHack engine calls for a key, one is taken from // the buffer, or if that is empty, QApplication::exec() is called. @@ -103,7 +103,7 @@ void NetHackQtWindow::PutStr(int attr UNUSED, const QString& text UNUSED) { puts void NetHackQtWindow::StartMenu(bool using_WIN_INVEN UNUSED) { puts("unexpected StartMenu"); } void NetHackQtWindow::AddMenu(int glyph UNUSED, const ANY_P* identifier UNUSED, - char ch UNUSED, char gch UNUSED, int attr UNUSED, + char ch UNUSED, char gch UNUSED, int attr UNUSED, int clr UNUSED, const QString& str UNUSED, unsigned itemflags UNUSED) { puts("unexpected AddMenu"); } void NetHackQtWindow::EndMenu(const QString& prompt UNUSED) { puts("unexpected EndMenu"); } diff --git a/win/Qt/qt_win.h b/win/Qt/qt_win.h index ffc10efeab..9dce3baec8 100644 --- a/win/Qt/qt_win.h +++ b/win/Qt/qt_win.h @@ -37,7 +37,7 @@ class NetHackQtWindow { } virtual void StartMenu(bool using_WIN_INVEN = false); virtual void AddMenu(int glyph, const ANY_P* identifier, - char ch, char gch, int attr, + char ch, char gch, int attr, int clr, const QString& str, unsigned itemflags); virtual void EndMenu(const QString& prompt); virtual int SelectMenu(int how, MENU_ITEM_P **menu_list); diff --git a/win/Qt/qt_xcmd.cpp b/win/Qt/qt_xcmd.cpp index 4310ad249c..505949398a 100644 --- a/win/Qt/qt_xcmd.cpp +++ b/win/Qt/qt_xcmd.cpp @@ -36,7 +36,7 @@ // used to select the shorter while still providing opportunity to type // more of the longer command; (there are several such cases: // "#drop[type]", "#known[class]", "#takeoff[all]", "#version[short]"); -// button is left justitied (prior to addition of the filter/layout/reset +// button is left justified (prior to addition of the filter/layout/reset // buttons, [Cancel] stretched all the way across the top of the widget); // [Filter] toggles between normal and autocomplete when playing in normal // or explore mode, cycles through "all", "normal", "autocomplete", and diff --git a/win/Qt/qt_xpms.h b/win/Qt/qt_xpms.h index 43b055e348..7ea343590e 100644 --- a/win/Qt/qt_xpms.h +++ b/win/Qt/qt_xpms.h @@ -1,6 +1,6 @@ // qt_xpms.h - static xpm arrays for use in status display // -// In alhpabetical order by array name. Probably not the best ordering... +// In alphabetical order by array name. Probably not the best ordering... /* clang-format off */ diff --git a/win/Qt/qt_yndlg.cpp b/win/Qt/qt_yndlg.cpp index 40696ac418..57e2f212f7 100644 --- a/win/Qt/qt_yndlg.cpp +++ b/win/Qt/qt_yndlg.cpp @@ -196,7 +196,7 @@ char NetHackQtYnDialog::Exec() QString button_name = QString(visctrl((char) ch[i].cell())); if (is_yn || is_ynq || is_ynaq || is_lr) { // FIXME: a better way to recognize which labels should - // use alterate text is needed + // use alternate text is needed switch (ch[i].cell()) { case 'y': button_name = "Yes"; @@ -220,7 +220,7 @@ char NetHackQtYnDialog::Exec() // for "ynaq" (where "all" is a choice) it's "stop" // and for end of game disclosure it really is "quit" if (question.left(10) == QString("Dump core?") - || (::gp.program_state.gameover + || (::program_state.gameover && question.left(11) == QString("Do you want"))) button_name = "Quit"; else if (is_ynaq) diff --git a/win/Qt/qt_yndlg.h b/win/Qt/qt_yndlg.h index e6c1bcdef4..0dd5d39e12 100644 --- a/win/Qt/qt_yndlg.h +++ b/win/Qt/qt_yndlg.h @@ -20,7 +20,7 @@ class NetHackQtYnDialog : QDialog { QLineEdit *le; QPushButton *y_btn; - // abritrary size; might need to be more sophisicated someday + // arbitrary size; might need to be more sophisticated someday char alt_answer[26 + 1], alt_result[26 + 1]; protected: diff --git a/win/X11/Install.X11 b/win/X11/Install.X11 index 3738ba4f4e..0f822aeb7d 100644 --- a/win/X11/Install.X11 +++ b/win/X11/Install.X11 @@ -96,7 +96,7 @@ aren't familiar, talk to your local X11 guru and read the man pages. nethack.sh automatically adds HACKDIR to your font search path. If you (assuming you are a system administrator) can install the fonts in your -standard X11 font directory the relevent lines in nethack.sh can be removed. +standard X11 font directory the relevant lines in nethack.sh can be removed. Alternatively, all persons playing nethack must add that "xset fp+" command to their .xinitrc file, or whatever file they execute when starting X11. See the note below for the alternative installation procedure diff --git a/win/X11/NetHack.ad b/win/X11/NetHack.ad index 519c8e2c83..22e2c6baec 100644 --- a/win/X11/NetHack.ad +++ b/win/X11/NetHack.ad @@ -30,7 +30,9 @@ NetHack*text*borderWidth: 0 ! tile_file names a file containing full-color tiles for the map. ! If you use a 100dpi (or greater) monitor you may wish to double the ! tile size so you can see the figures. If NetHack was compiled to -! use XPM (USE_XPM in config.h), the tile_file is a standard XPM file. +! use XPM (via #define USE_XPM in config.h or via -DUSE_XPM in src/Makefile, +! which happens when using WANT_WIN_X11=1 or WANT_WIN_ALL=1 with setup.sh +! 'hints' that support such), the tile_file is a standard XPM file. ! Otherwise, it is a custom format. double_tile_size only applies to ! the custom format - to enlarge an XPM file, use processing tools ! such as XV or preferably PBMplus. @@ -66,7 +68,7 @@ NetHack*rip*foreground: black ! questions is _not_ used. Single-character prompts appear in a fixed ! position between the top of the map and the bottom of the messages. ! If False, popups appear near where the pointer is positioned so tend to -! meander around the screen depending upon where the last click ocurred. +! meander around the screen depending upon where the last click occurred. ! (The name 'slow' is misleading; this feature was originally necessitated ! by window managers which were slow putting up popup windows, but the ! fixed-position prompting can be just as useful for quick popups.) @@ -87,7 +89,7 @@ NetHack*highlight_prompt: False ! The number of lines the message window will show without scrolling. !NetHack*message_lines: 12 -! If True, the message window has a line that seperates old and new messages. +! If True, the message window has a line that separates old and new messages. !NetHack*message_line: True ! If True, the default, use a "fancy" style status area below the map. diff --git a/win/X11/Window.c b/win/X11/Window.c index 64f690a6e1..82cb5a1c62 100644 --- a/win/X11/Window.c +++ b/win/X11/Window.c @@ -33,7 +33,7 @@ #include "config.h" /* #define for const for non __STDC__ compilers */ #include "lint.h" /* for nethack's nhStr() macro */ -#include "winX.h" /* to make sure protoypes match corresponding functions */ +#include "winX.h" /* to make sure prototypes match corresponding functions */ static XtResource resources[] = { #define offset(field) XtOffset(WindowWidget, window.field) diff --git a/win/X11/dialogs.c b/win/X11/dialogs.c index 37d81313b0..11664c198a 100644 --- a/win/X11/dialogs.c +++ b/win/X11/dialogs.c @@ -44,7 +44,7 @@ * + Include nethack's lint.h to get nhStr() macro. * + Use nhStr() on string literals (or macros from * that hide string literals) to cast away implicit 'const' in order - * to suppress "warning: assignment discards qualifers from pointer + * to suppress "warning: assignment discards qualifiers from pointer * target type" issued by 'gcc -Wwrite-strings' as used by nethack. * (For this file, always the second parameter to XtSetArg().) * @@ -82,7 +82,7 @@ #undef X11_BUILD #include "lint.h" /* for nethack's nhStr() macro */ -#include "winX.h" /* to make sure protoypes match corresponding functions */ +#include "winX.h" /* to make sure prototypes match corresponding functions */ /* ":" added to both translations below to allow limited redefining of * keysyms before testing for keysym values -- dlc */ @@ -257,7 +257,7 @@ GetDialogResponse(Widget w) return XtNewString(s); } -/* set the default reponse */ +/* set the default response */ void SetDialogResponse(Widget w, String s, unsigned ln) { diff --git a/win/X11/ibm.bdf b/win/X11/ibm.bdf index f2b77f31bd..399d6dff53 100644 --- a/win/X11/ibm.bdf +++ b/win/X11/ibm.bdf @@ -3616,7 +3616,7 @@ fd00 3800 3600 ENDCHAR -STARTCHAR magic sheild 3 +STARTCHAR magic shield 3 ENCODING 171 SWIDTH 666 0 DWIDTH 8 0 diff --git a/win/X11/nethack.rc b/win/X11/nethack.rc index 520060b243..2422984178 100644 --- a/win/X11/nethack.rc +++ b/win/X11/nethack.rc @@ -84,3 +84,8 @@ OPTIONS=catname:Ghisteslwchlohm # 048 035 064 042 \ # 047 045 092 124 124 092 045 047 \ # 047 064 092 064 064 064 092 064 047 + +# CRASHREPORTURL must be set in syscf to enable these options. +# These identify you in crash reports +#OPTIONS=crash_name:Your Name +#OPTIONS=crash_email:user@example.com diff --git a/win/X11/tile2x11.c b/win/X11/tile2x11.c index 82826c2df0..ce9726049d 100644 --- a/win/X11/tile2x11.c +++ b/win/X11/tile2x11.c @@ -139,7 +139,7 @@ xpm_write(FILE *fp) } Fprintf(fp, "/* XPM */\n"); - Fprintf(fp, "static char* nhtiles[] = {\n"); + Fprintf(fp, "static char *nhtiles[] = {\n"); Fprintf(fp, "\"%lu %lu %lu %d\",\n", header.tile_width * header.per_row, (header.tile_height * header.ntiles) / header.per_row, header.ncolors, 1 /* char per color */); diff --git a/win/X11/winX.c b/win/X11/winX.c index 059ca06ea9..db95dea15a 100644 --- a/win/X11/winX.c +++ b/win/X11/winX.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 winX.c $NHDT-Date: 1643491577 2022/01/29 21:26:17 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.110 $ */ +/* NetHack 3.7 winX.c $NHDT-Date: 1717967337 2024/06/09 21:08:57 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.136 $ */ /* Copyright (c) Dean Luick, 1992 */ /* NetHack may be freely redistributed. See license for details. */ @@ -98,6 +98,7 @@ void (*input_func)(Widget, XEvent *, String *, Cardinal *); int click_x, click_y, click_button; /* Click position on a map window * (filled by set_button_values()). */ 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; static int X11_io_error_handler(Display *); @@ -145,8 +146,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 @@ -164,10 +163,8 @@ struct window_procs X11_procs = { * Local functions. */ static winid find_free_window(void); -#ifdef TEXTCOLOR static void nhFreePixel(XtAppContext, XrmValuePtr, XtPointer, XrmValuePtr, Cardinal *); -#endif static boolean new_resource_macro(String, unsigned); static void load_default_resources(void); static void release_default_resources(void); @@ -423,8 +420,8 @@ nhApproxColor( long cdiff = 16777216; /* 2^24; hopefully our map is smaller */ XColor tmp; static XColor *table = 0; - register int i, j; - register long tdiff; + int i, j; + long tdiff; /* if the screen doesn't have a big colormap, don't waste our time or if it's huge, and _some_ match should have been possible */ @@ -707,7 +704,6 @@ load_boldfont(struct xwindow *wp, Widget w) wp->boldfs = XLoadQueryFont(dpy, fontname); } -#ifdef TEXTCOLOR /* ARGSUSED */ static void nhFreePixel( @@ -735,7 +731,6 @@ nhFreePixel( (unsigned long *) toVal->addr, 1, (unsigned long) 0); } } -#endif /*TEXTCOLOR*/ /* [ALI] Utility function to ask Xaw for font height, since the previous * assumption of ascent + descent is not always valid. @@ -799,7 +794,7 @@ new_resource_macro( ++q; /* skip whitespace between name and value */ for (p = eos(q); --p > q && (*p == ' ' || *p == '\t'); ) continue; /* discard trailing whitespace */ - *++p = '\0'; /* q..p containes macro value */ + *++p = '\0'; /* q..p contains macro value */ def_rsrc_valu[numdefs] = dupstr(q); return TRUE; } @@ -996,7 +991,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; @@ -1285,7 +1281,7 @@ X11_update_inventory(int arg) if (iflags.perm_invent) { /* skip any calls to update_inventory() before in_moveloop starts */ - if (gp.program_state.in_moveloop || gp.program_state.gameover) { + if (program_state.in_moveloop || program_state.gameover) { updated_inventory = 1; /* hack to avoid mapping&raising window */ if (!arg) { (void) display_inventory((char *) 0, FALSE); @@ -1308,7 +1304,20 @@ X11_ctrl_nhwindow( int request UNUSED, win_request_info *wri UNUSED) { - return (win_request_info *) 0; + if (!wri) + return (win_request_info *) 0; + + switch(request) { + case set_mode: + case request_settings: + break; + case set_menu_promptstyle: + X11_menu_promptstyle = wri->fromcore.menu_promptstyle; + break; + default: + break; + } + return wri; } /* The current implementation has all of the saved lines on the screen. */ @@ -1386,20 +1395,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) @@ -1608,13 +1603,11 @@ X11_init_nhwindows(int *argcp, char **argv) old_error_handler = XSetErrorHandler(panic_on_error); -#ifdef TEXTCOLOR /* add new color converter to deal with overused colormaps */ XtSetTypeConverter(XtRString, XtRPixel, nhCvtStringToPixel, (XtConvertArgList) nhcolorConvertArgs, XtNumber(nhcolorConvertArgs), XtCacheByDisplay, nhFreePixel); -#endif /* TEXTCOLOR */ /* Register the actions mentioned in "actions". */ XtAppAddActions(app_context, actions, XtNumber(actions)); @@ -1790,7 +1783,7 @@ X11_hangup(Widget w, XEvent *event, String *params, Cardinal *num_params) static void X11_bail(const char *mesg) { - gp.program_state.something_worth_saving = 0; + program_state.something_worth_saving = 0; clearlocks(); X11_exit_nhwindows(mesg); nh_terminate(EXIT_SUCCESS); @@ -1807,7 +1800,7 @@ askname_delete(Widget w, XEvent *event, String *params, Cardinal *num_params) nhUse(num_params); nh_XtPopdown(w); - (void) strcpy(gp.plname, "Mumbles"); /* give them a name... ;-) */ + (void) strcpy(svp.plname, "Mumbles"); /* give them a name... ;-) */ exit_x_event = TRUE; } @@ -1832,11 +1825,11 @@ askname_done(Widget w, XtPointer client_data, XtPointer call_data) } /* Truncate name if necessary */ - if (len >= sizeof gp.plname - 1) - len = sizeof gp.plname - 1; + if (len >= sizeof svp.plname - 1) + len = sizeof svp.plname - 1; - (void) strncpy(gp.plname, s, len); - gp.plname[len] = '\0'; + (void) strncpy(svp.plname, s, len); + svp.plname[len] = '\0'; XtFree(s); nh_XtPopdown(XtParent(dialog)); @@ -1884,7 +1877,7 @@ X11_askname(void) (XtCallbackProc) 0); SetDialogPrompt(dialog, nhStr("What is your name?")); /* set prompt */ - SetDialogResponse(dialog, gp.plname, PL_NSIZ); /* set default answer */ + SetDialogResponse(dialog, svp.plname, PL_NSIZ); /* set default answer */ XtRealizeWidget(popup); positionpopup(popup, TRUE); /* center,bottom */ @@ -2033,7 +2026,7 @@ X11_getlin( /* we get here after the popup has exited; put prompt and response into the message window (and into core's dumplog history) unless play hasn't started yet */ - if (gp.program_state.in_moveloop || gp.program_state.gameover) { + if (program_state.in_moveloop || program_state.gameover) { /* single space has meaning (to remove a previously applied name) so show it clearly; don't care about legibility of multiple spaces */ const char *visanswer = !input[0] ? "" @@ -2112,7 +2105,7 @@ static const char *yn_choices; /* string of acceptable input */ static char yn_def; static char yn_return; /* return value */ static char yn_esc_map; /* ESC maps to this char. */ -static Widget yn_popup; /* popup for the yn fuction (created once) */ +static Widget yn_popup; /* popup for the yn function (created once) */ static Widget yn_label; /* label for yn function (created once) */ static boolean yn_getting_num; /* TRUE if accepting digits */ static boolean yn_preserve_case; /* default is to force yn to lower case */ @@ -2138,7 +2131,7 @@ key_event_to_char(XKeyEvent *key) nbytes = XLookupString(key, keystring, MAX_KEY_STRING, (KeySym *) 0, (XComposeStatus *) 0); - /* Modifier keys return a zero lengh string when pressed. */ + /* Modifier keys return a zero length string when pressed. */ if (nbytes == 0) return '\0'; @@ -2211,8 +2204,15 @@ yn_key(Widget w, XEvent *event, String *params, Cardinal *num_params) } else { if (yn_getting_num) { if (digit(ch)) { + long dgt = (long) (ch - '0'); + yn_ndigits++; - yn_val = (yn_val * 10) + (long) (ch - '0'); + /* yn_val = (10 * yn_val) + (ch - '0'); */ + yn_val = AppendLongDigit(yn_val, dgt); + if (yn_val < 0L) { + yn_ndigits = 0; + yn_val = 0; + } return; /* wait for more input */ } if (yn_ndigits && (ch == '\b' || ch == 127 /*DEL*/)) { @@ -2539,7 +2539,7 @@ highlight_yn(boolean init) if (!appResources.slow || !appResources.highlight_prompt) return; - /* first time through, WIN_MAP isn't fully initiialized yet */ + /* first time through, WIN_MAP isn't fully initialized yet */ xmap = ((map_win != WIN_ERR) ? &window_list[map_win] : (WIN_MAP != WIN_ERR) ? &window_list[WIN_MAP] : 0); @@ -2647,7 +2647,7 @@ init_standard_windows(void) XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom); num_args++; XtSetValues(map_viewport, args, num_args); - /* Create the status window, with the form as it's parent. */ + /* Create the status window, with the form as its parent. */ status_win = find_free_window(); wp = &window_list[status_win]; wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0; @@ -2671,7 +2671,7 @@ init_standard_windows(void) XtSetValues(status, args, num_args); /* - * Realize the popup so that the status widget knows it's size. + * Realize the popup so that the status widget knows its size. * * If we unset MappedWhenManaged then the DECwindow driver doesn't * attach the nethack toplevel to the highest virtual root window. @@ -2802,7 +2802,7 @@ void find_scrollbars( Widget w, /* widget of interest; scroll bars are probably attached to its parent or grandparent */ - Widget last_w, /* if non-zero, don't search ancestory beyond this point */ + Widget last_w, /* if non-zero, don't search ancestry beyond this point */ Widget *horiz, /* output: horizontal scrollbar */ Widget *vert) /* output: vertical scrollbar */ { diff --git a/win/X11/winmap.c b/win/X11/winmap.c index ec9e6e529b..7a4ab8f75c 100644 --- a/win/X11/winmap.c +++ b/win/X11/winmap.c @@ -57,13 +57,11 @@ extern int total_tiles_used, Tile_corr; #define COL0_OFFSET 1 /* change to 0 to revert to displaying unused column 0 */ static X11_map_symbol glyph_char(const glyph_info *glyphinfo); -#ifdef TEXTCOLOR static GC X11_make_gc(struct xwindow *wp, struct text_map_info_t *text_map, X11_color color, boolean inverted); #ifdef ENHANCED_SYMBOLS static void X11_free_gc(struct xwindow *wp, GC gc, X11_color color); #endif -#endif /* TEXTCOLOR */ #ifdef ENHANCED_SYMBOLS static void X11_set_map_font(struct xwindow *wp); #endif @@ -126,18 +124,31 @@ X11_print_glyph( } { X11_map_symbol ch; - register X11_map_symbol *ch_ptr; + X11_map_symbol *ch_ptr; X11_color color; unsigned special; -#ifdef TEXTCOLOR + uint32 nhcolor = 0; int colordif; - register X11_color *co_ptr; -#endif + X11_color *co_ptr; color = glyphinfo->gm.sym.color; special = glyphinfo->gm.glyphflags; ch = glyph_char(glyphinfo); + if (glyphinfo->gm.customcolor != 0) { + if ((glyphinfo->gm.customcolor & NH_BASIC_COLOR) != 0) { + /* NH_BASIC_COLOR */ + color = COLORVAL(glyphinfo->gm.customcolor); + } else if (iflags.colorcount == 256 + && (X11_procs.wincap2 & WC2_EXTRACOLORS) != 0 + && (glyphinfo->gm.customcolor & NH_BASIC_COLOR) == 0) { + uint32 closecolor = get_nhcolor_from_256_index(glyphinfo->gm.color256idx); + nhcolor = COLORVAL(closecolor); + } else { + /* 24-bit color, NH_BASIC_COLOR == 0 */ + nhcolor = COLORVAL(glyphinfo->gm.customcolor); + } + } if (special != map_info->tile_map.glyphs[y][x].glyphflags) { map_info->tile_map.glyphs[y][x].glyphflags = special; update_bbox = TRUE; @@ -150,22 +161,19 @@ X11_print_glyph( if (!map_info->is_tile) update_bbox = TRUE; } -#ifdef TEXTCOLOR co_ptr = &map_info->text_map.colors[y][x]; colordif = (((special & MG_PET) != 0 && iflags.hilite_pet) || ((special & MG_OBJPILE) != 0 && iflags.hilite_pile) - || ((special & (MG_DETECT | MG_BW_LAVA | MG_BW_ICE)) != 0 + || ((special & (MG_DETECT | MG_BW_LAVA | MG_BW_ICE + | MG_BW_SINK | MG_BW_ENGR)) != 0 && iflags.use_inverse)) ? CLR_MAX : 0; color += colordif; -#ifdef ENHANCED_SYMBOLS - if (SYMHANDLING(H_UTF8) && glyphinfo->gm.u != NULL && glyphinfo->gm.u->ucolor != 0) { - color = glyphinfo->gm.u->ucolor | 0x80000000; - if (colordif != 0) { - color |= 0x40000000; - } - } -#endif + if (nhcolor != 0) + color = nhcolor | 0x80000000; + if (colordif != 0) + color |= 0x40000000; + if (*co_ptr != color) { *co_ptr = color; if (!map_info->is_tile) @@ -175,7 +183,6 @@ X11_print_glyph( map_info->text_map.framecolors[y][x] = bkglyphinfo->framecolor; update_bbox = TRUE; } -#endif } if (update_bbox) { /* update row bbox */ @@ -710,7 +717,7 @@ check_cursor_visibility(struct xwindow *wp) /* All values are relative to currently visible area */ #define V_BORDER 0.25 /* if this far from vert edge, shift */ -#define H_BORDER 0.25 /* if this from from horiz edge, shift */ +#define H_BORDER 0.25 /* if this far from horiz edge, shift */ #define H_DELTA 0.25 /* distance of horiz shift */ #define V_DELTA 0.25 /* distance of vert shift */ @@ -861,7 +868,7 @@ map_check_size_change(struct xwindow *wp) if (new_width < map_info->viewport_width || new_height < map_info->viewport_height) { /* [ALI] If the viewport was larger than the map (and so the map - * widget was contrained to be larger than the actual map) then we + * widget was constrained to be larger than the actual map) then we * may be able to shrink the map widget as the viewport shrinks. */ if (map_info->is_tile) { @@ -894,7 +901,7 @@ map_check_size_change(struct xwindow *wp) /* * Fill in parameters "regular" and "inverse" with newly created GCs. - * Using the given background pixel and the foreground pixel optained + * Using the given background pixel and the foreground pixel obtained * by querying the widget with the resource name. */ static void @@ -943,7 +950,6 @@ get_text_gc(struct xwindow *wp, Font font) XtSetArg(arg[0], XtNbackground, &bgpixel); XtGetValues(wp->w, arg, ONE); -#ifdef TEXTCOLOR #define set_color_gc(nh_color, resource_name) \ set_gc(wp->w, font, resource_name, bgpixel, \ &map_info->text_map.color_gcs[nh_color], \ @@ -965,11 +971,6 @@ get_text_gc(struct xwindow *wp, Font font) set_color_gc(CLR_BRIGHT_MAGENTA, XtNbright_magenta); set_color_gc(CLR_BRIGHT_CYAN, XtNbright_cyan); set_color_gc(CLR_WHITE, XtNwhite); -#else - set_gc(wp->w, font, XtNforeground, bgpixel, - &map_info->text_map.copy_gc, - &map_info->text_map.inv_copy_gc); -#endif } /* @@ -989,7 +990,7 @@ display_cursor(struct xwindow *wp) void display_map_window(struct xwindow *wp) { - register int row; + int row; struct map_info_t *map_info = wp->map_information; if ((map_info->is_tile != iflags.wc_tiled_map) @@ -1007,7 +1008,7 @@ display_map_window(struct xwindow *wp) check_cursor_visibility(wp); highlight_yn(TRUE); /* change fg/bg to match map */ } else if (wp->prevx != wp->cursx || wp->prevy != wp->cursy) { - register coordxy x = wp->prevx, y = wp->prevy; + coordxy x = wp->prevx, y = wp->prevy; /* * Previous cursor position is not the same as the current @@ -1068,10 +1069,8 @@ map_all_unexplored(struct map_info_t *map_info) /* [was map_all_stone()] */ tile_map->glyphs[y][x].framecolor = NO_COLOR; text_map->text[y][x] = (uchar) (!x ? mgnothg : mgunexp); -#ifdef TEXTCOLOR text_map->colors[y][x] = NO_COLOR; text_map->framecolors[y][x] = NO_COLOR; -#endif } } @@ -1100,7 +1099,7 @@ clear_map_window(struct xwindow *wp) } /* - * Retreive the font associated with the map window and save attributes + * Retrieve the font associated with the map window and save attributes * that are used when updating it. */ static void @@ -1366,7 +1365,7 @@ map_update(struct xwindow *wp, int start_row, int stop_row, int start_col, int s { struct map_info_t *map_info = wp->map_information; int row; - register int count; + int count; if (start_row < 0 || stop_row >= ROWNO) { impossible("map_update: bad row range %d-%d\n", start_row, stop_row); @@ -1443,7 +1442,6 @@ map_update(struct xwindow *wp, int start_row, int stop_row, int start_col, int s XSetForeground(dpy, tile_map->black_gc, BlackPixelOfScreen(screen)); } -#ifdef TEXTCOLOR { uint32_t fc = tile_map->glyphs[row][cur_col].framecolor; @@ -1454,7 +1452,6 @@ map_update(struct xwindow *wp, int start_row, int stop_row, int start_col, int s tile_map->square_width - 1 , tile_map->square_height - 1); } -#endif } } @@ -1477,9 +1474,8 @@ map_update(struct xwindow *wp, int start_row, int stop_row, int start_col, int s } else { struct text_map_info_t *text_map = &map_info->text_map; -#ifdef TEXTCOLOR { - register X11_color *c_ptr; + X11_color *c_ptr; X11_map_symbol *t_ptr; int cur_col, win_ystart; X11_color color; @@ -1517,38 +1513,9 @@ map_update(struct xwindow *wp, int start_row, int stop_row, int start_col, int s } /* col loop */ } /* row loop */ } -#else /* !TEXTCOLOR */ - { - int win_row, win_xstart; - int win_start_row, win_start_col; - - win_start_row = start_row; - win_start_col = start_col; - - /* We always start at the same x window position and have - the same character count. */ - win_xstart = text_map->square_lbearing - + ((win_start_col - COL0_OFFSET) - * text_map->square_width); - count = stop_col - start_col + 1; - - for (row = start_row, win_row = win_start_row; row <= stop_row; - row++, win_row++) { - X11_draw_image_string(XtDisplay(wp->w), XtWindow(wp->w), - inverted ? text_map->inv_copy_gc - : text_map->copy_gc, - win_xstart, - text_map->square_ascent - + (win_row * text_map->square_height), - &(text_map->text[row][start_col]), - count); - } - } -#endif /* ?TEXTCOLOR */ } } -#ifdef TEXTCOLOR static GC X11_make_gc( struct xwindow *wp UNUSED, @@ -1618,7 +1585,6 @@ X11_free_gc(struct xwindow *wp, GC ggc, X11_color color) } } #endif -#endif /* TEXTCOLOR */ static void X11_draw_image_string( @@ -1921,17 +1887,12 @@ destroy_map_window(struct xwindow *wp) struct text_map_info_t *text_map = &map_info->text_map; /* Free allocated GCs. */ -#ifdef TEXTCOLOR int i; for (i = 0; i < CLR_MAX; i++) { XtReleaseGC(wp->w, text_map->color_gcs[i]); XtReleaseGC(wp->w, text_map->inv_color_gcs[i]); } -#else - XtReleaseGC(wp->w, text_map->copy_gc); - XtReleaseGC(wp->w, text_map->inv_copy_gc); -#endif /* Free the font structure if we allocated one */ #ifdef ENHANCED_SYMBOLS @@ -2026,7 +1987,7 @@ x_event(int exit_condition) /* pkey(retval); */ keep_going = FALSE; #if defined(HANGUPHANDLING) - } else if (gp.program_state.done_hup) { + } else if (program_state.done_hup) { retval = '\033'; inptr = (inptr + 1) % INBUF_SIZE; keep_going = FALSE; @@ -2047,12 +2008,12 @@ x_event(int exit_condition) } keep_going = FALSE; #if defined(HANGUPHANDLING) - } else if (gp.program_state.done_hup) { + } else if (program_state.done_hup) { retval = '\033'; inptr = (inptr + 1) % INBUF_SIZE; keep_going = FALSE; #endif - } + } break; default: panic("x_event: unknown exit condition %d", exit_condition); diff --git a/win/X11/winmenu.c b/win/X11/winmenu.c index 550b81fe60..d4188ac22f 100644 --- a/win/X11/winmenu.c +++ b/win/X11/winmenu.c @@ -254,9 +254,11 @@ menu_key(Widget w, XEvent *event, String *params, Cardinal *num_params) if (menu_info->is_active || perminv_scrolling) { /* handle the input */ /* first check for an explicit selector match, so that it won't be overridden if it happens to duplicate a mapped menu command (':' - to look inside a container vs ':' to select via search string) */ + to look inside a container vs ':' to select via search string); + check for group accelerator match too */ for (curr = menu_info->curr_menu.base; curr; curr = curr->next) - if (curr->identifier.a_void != 0 && curr->selector == ch) + if (curr->identifier.a_void != 0 + && (curr->selector == ch || curr->gselector == ch)) goto make_selection; ch = map_menu_cmd(ch); @@ -366,7 +368,8 @@ menu_key(Widget w, XEvent *event, String *params, Cardinal *num_params) selected_something = FALSE; for (count = 0, curr = menu_info->curr_menu.base; curr; curr = curr->next, count++) - if (curr->identifier.a_void != 0 && curr->selector == ch) + if (curr->identifier.a_void != 0 + && (curr->selector == ch || curr->gselector == ch)) break; if (curr) { @@ -645,7 +648,7 @@ menu_scrollmask(struct xwindow *wp) return scrlmask; } -/* if a menu is scrolled vertically and/horizontallty, return it to the top +/* if a menu is scrolled vertically and/horizontally, return it to the top and far left */ static void menu_unscroll(struct xwindow *wp) @@ -712,7 +715,7 @@ x11_scroll_perminv(int arg UNUSED) /* arg is always 1 */ * We accept some extra characters that menus usually ignore: * ^C will be treated like , leaving menu positioned as-is * and returning to play; or will be treated - * like and , reseting the menu to its top and + * like and , resetting the menu to its top and * returning to play; other characters will either be rejected by * yn_function or stay here for scrolling. */ @@ -776,7 +779,7 @@ X11_start_menu(winid window, unsigned long mbehavior UNUSED) wp = &window_list[window]; if (wp->menu_information->is_menu) { - /* make sure we'ere starting with a clean slate */ + /* make sure we're starting with a clean slate */ free_menu(&wp->menu_information->new_menu); } else { wp->menu_information->is_menu = TRUE; @@ -791,7 +794,7 @@ X11_add_menu(winid window, char ch, /* selector letter; 0 if not selectable */ char gch, /* group accelerator (0 = no group) */ int attr, - int clr UNUSED, + int clr, const char *str, unsigned itemflags) { @@ -810,6 +813,7 @@ X11_add_menu(winid window, item->next = (x11_menu_item *) 0; item->identifier = *identifier; item->attr = attr; + item->color = clr; item->itemflags = itemflags; item->selected = item->preselected = preselected; item->pick_count = -1L; @@ -1310,9 +1314,9 @@ menu_create_entries(struct xwindow *wp, struct menu *curr_menu) XtSetArg(args[num_args], nhStr(XtNborderWidth), 0); num_args++; XtSetArg(args[num_args], nhStr(XtNvertDistance), 0); num_args++; - if (!iflags.use_menu_color || wp->menu_information->disable_mcolors - || !get_menu_coloring(curr->str, &color, &attr)) - attr = curr->attr; + attr = curr->attr; + if (!wp->menu_information->disable_mcolors) + color = curr->color; if (color != NO_COLOR) { if (attr != ATR_INVERSE) diff --git a/win/X11/winmesg.c b/win/X11/winmesg.c index a4e9abe8c3..6c5c01f354 100644 --- a/win/X11/winmesg.c +++ b/win/X11/winmesg.c @@ -92,8 +92,8 @@ create_message_window(struct xwindow *wp, /* window pointer */ if (iflags.msg_history < (unsigned) appResources.message_lines) iflags.msg_history = (unsigned) appResources.message_lines; - if (iflags.msg_history > MAX_HISTORY) /* a sanity check */ - iflags.msg_history = MAX_HISTORY; + if (iflags.msg_history > MAX_MSG_HISTORY) /* a sanity check */ + iflags.msg_history = MAX_MSG_HISTORY; set_circle_buf(mesg_info, (int) iflags.msg_history); @@ -383,7 +383,7 @@ split(char *s, } /* - * Add a line of text to the window. The first line in the curcular list + * Add a line of text to the window. The first line in the circular list * becomes the last. So all we have to do is copy the new line over the * old one. If the line buffer is too small, then allocate a new, larger * one. @@ -391,8 +391,8 @@ split(char *s, static void add_line(struct mesg_info_t *mesg_info, const char *s) { - register struct line_element *curr = mesg_info->head; - register int new_line_length = strlen(s); + struct line_element *curr = mesg_info->head; + int new_line_length = strlen(s); if (new_line_length + 1 > curr->buf_length) { if (curr->line) @@ -410,7 +410,7 @@ add_line(struct mesg_info_t *mesg_info, const char *s) } /* - * Save a position in the text buffer so we can draw a line to seperate + * Save a position in the text buffer so we can draw a line to separate * text from the last time this function was called. * * Save the head position, since it is the line "after" the last displayed @@ -420,7 +420,7 @@ add_line(struct mesg_info_t *mesg_info, const char *s) void set_last_pause(struct xwindow *wp) { - register struct mesg_info_t *mesg_info = wp->mesg_information; + struct mesg_info_t *mesg_info = wp->mesg_information; #ifdef ERASE_LINE /* @@ -448,14 +448,14 @@ static void redraw_message_window(struct xwindow *wp) { struct mesg_info_t *mesg_info = wp->mesg_information; - register struct line_element *curr; - register int row, y_base; + struct line_element *curr; + int row, y_base; /* * Do this the cheap and easy way. Clear the window and just redraw * the whole thing. * - * This could be done more effecently with one call to XDrawText() instead + * This could be done more efficiently with one call to XDrawText() instead * of many calls to XDrawString(). Maybe later. * * Only need to clear if window has new text. @@ -567,7 +567,7 @@ get_gc(Widget w, struct mesg_info_t *mesg_info) /* * Handle resizes on a message window. Correct saved pixel height and width. - * Adjust circle buffer to accomidate the new size. + * Adjust circle buffer to accommodate the new size. * * Problem: If the resize decreases the width of the window such that * some lines are now longer than the window, they will be cut off by diff --git a/win/X11/winmisc.c b/win/X11/winmisc.c index 5bc622e4a6..1fcdf471c7 100644 --- a/win/X11/winmisc.c +++ b/win/X11/winmisc.c @@ -352,11 +352,11 @@ plsel_dialog_acceptvalues(void) XtSetArg(args[0], nhStr(XtNstring), &s); XtGetValues(plsel_name_input, args, ONE); - (void) strncpy(gp.plname, (char *) s, sizeof gp.plname - 1); - gp.plname[sizeof gp.plname - 1] = '\0'; - (void) mungspaces(gp.plname); - if (strlen(gp.plname) < 1) - (void) strcpy(gp.plname, "Mumbles"); + (void) strncpy(svp.plname, (char *) s, sizeof svp.plname - 1); + svp.plname[sizeof svp.plname - 1] = '\0'; + (void) mungspaces(svp.plname); + if (strlen(svp.plname) < 1) + (void) strcpy(svp.plname, "Mumbles"); iflags.renameinprogress = FALSE; } @@ -779,8 +779,8 @@ X11_create_player_selection_name(Widget form) XtSetArg(args[num_args], nhStr(XtNeditType), !plsel_ask_name ? XawtextRead : XawtextEdit); num_args++; XtSetArg(args[num_args], nhStr(XtNresize), XawtextResizeWidth); num_args++; - XtSetArg(args[num_args], nhStr(XtNstring), gp.plname); num_args++; - XtSetArg(args[num_args], XtNinsertPosition, strlen(gp.plname)); num_args++; + XtSetArg(args[num_args], nhStr(XtNstring), svp.plname); num_args++; + XtSetArg(args[num_args], XtNinsertPosition, strlen(svp.plname)); num_args++; XtSetArg(args[num_args], nhStr(XtNaccelerators), XtParseAcceleratorTable(plsel_input_accelerators)); num_args++; plsel_name_input = XtCreateManagedWidget("name_input", @@ -1222,7 +1222,7 @@ X11_player_selection_dialog(void) if (ps_selected == PS_QUIT #if defined(HANGUPHANDLING) - || gp.program_state.done_hup + || program_state.done_hup #endif ) { clearlocks(); @@ -1300,7 +1300,7 @@ X11_player_selection_prompts(void) if (ps_selected == PS_QUIT #if defined(HANGUPHANDLING) - || gp.program_state.done_hup + || program_state.done_hup #endif ) { clearlocks(); @@ -1373,7 +1373,7 @@ X11_player_selection_prompts(void) if (ps_selected == PS_QUIT #if defined(HANGUPHANDLING) - || gp.program_state.done_hup + || program_state.done_hup #endif ) { clearlocks(); @@ -1445,7 +1445,7 @@ X11_player_selection_prompts(void) if (ps_selected == PS_QUIT #if defined(HANGUPHANDLING) - || gp.program_state.done_hup + || program_state.done_hup #endif ) { clearlocks(); @@ -1515,7 +1515,7 @@ X11_player_selection_prompts(void) if (ps_selected == PS_QUIT #if defined(HANGUPHANDLING) - || gp.program_state.done_hup + || program_state.done_hup #endif ) { clearlocks(); @@ -1539,15 +1539,15 @@ void X11_player_selection(void) { if (iflags.wc_player_selection == VIA_DIALOG) { - if (!*gp.plname) { + if (!*svp.plname) { #ifdef UNIX char *defplname = get_login_name(); #else char *defplname = (char *)0; #endif - (void) strncpy(gp.plname, defplname ? defplname : "Mumbles", - sizeof gp.plname - 1); - gp.plname[sizeof gp.plname - 1] = '\0'; + (void) strncpy(svp.plname, defplname ? defplname : "Mumbles", + sizeof svp.plname - 1); + svp.plname[sizeof svp.plname - 1] = '\0'; iflags.renameinprogress = TRUE; } X11_player_selection_dialog(); @@ -1704,7 +1704,7 @@ ec_scroll_to_view(int ec_indx) /* might be greater than extended_cmd_selected */ * If the extended command menu needs to be scrolled in order to move * either the highlighted entry (extended_cmd_selected) or the target * entry (ec_indx) into view, we want to make both end up visible. - * [Highligthed one is the first matching entry when the user types + * [Highlighted one is the first matching entry when the user types * something, such as "adjust" after typing 'a', and will be chosen * by pressing . Target entry is one past the last matching * entry (or last matching entry itself if at end of command list), @@ -1886,7 +1886,7 @@ ec_key(Widget w, XEvent *event, String *params, Cardinal *num_params) swap_fg_bg(extended_commands[extended_cmd_selected]); } /* advance to one past last matching entry, so that all - ambiguous choices, plus one to show thare aren't any + ambiguous choices, plus one to show there aren't any more such, will scroll into view */ do { if (!command_list[i + 1]) diff --git a/win/X11/winstat.c b/win/X11/winstat.c index 1415c825b9..1be79d56dc 100644 --- a/win/X11/winstat.c +++ b/win/X11/winstat.c @@ -94,7 +94,8 @@ #define F_HALLU 40 #define F_WITHER 41 -#define NUM_STATS 42 +#define F_VERS 42 /* version info */ +#define NUM_STATS 43 static int condcolor(long, unsigned long *); static int condattr(long, unsigned long *); @@ -108,6 +109,7 @@ static void tt_reset_color(int, int, unsigned long *); static void tt_status_fixup(void); static Widget create_tty_status_field(int, int, Widget, Widget); static Widget create_tty_status(Widget, Widget); +static void stat_resized(Widget, XtPointer, XtPointer); static void update_fancy_status_field(int, int, int); static void update_fancy_status(boolean); static Widget create_fancy_status(Widget, Widget); @@ -134,15 +136,15 @@ static int next_cond_indx = 0, prev_cond_indx = 0; /* TODO: support statuslines:3 in addition to 2 for the tty-style status */ #define X11_NUM_STATUS_LINES 2 -#define X11_NUM_STATUS_FIELD 15 +#define X11_NUM_STATUS_FIELD 16 static enum statusfields X11_fieldorder[][X11_NUM_STATUS_FIELD] = { { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN, BL_SCORE, BL_FLUSH, BL_FLUSH, BL_FLUSH, BL_FLUSH, BL_FLUSH, - BL_FLUSH }, + BL_FLUSH, BL_FLUSH }, { 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, BL_FLUSH } + BL_CAP, BL_CONDITION, BL_VERS, BL_FLUSH } }; /* condition list for tty-style display, roughly in order of importance */ @@ -159,7 +161,7 @@ static struct tt_condinfo { { BL_MASK_INLAVA, "InLava" }, { BL_MASK_WITHER, "Wither" }, { BL_MASK_HELD, "Held" }, - { BL_MASK_HELD, "Holding" }, + { BL_MASK_HOLDING, "Holding" }, { BL_MASK_BLIND, "Blind" }, { BL_MASK_DEAF, "Deaf" }, { BL_MASK_STUN, "Stun" }, @@ -172,7 +174,7 @@ static struct tt_condinfo { { BL_MASK_RIDE, "Ride" }, }; -static const char *fancy_status_hilite_colors[] = { +static const char *const fancy_status_hilite_colors[] = { "grey15", "red3", "dark green", @@ -219,24 +221,27 @@ condattr(long bm, unsigned long *bmarray) int i; if (bm && bmarray) { - for (i = HL_ATTCLR_DIM; i < BL_ATTCLR_MAX; ++i) { + for (i = HL_ATTCLR_BOLD; i < BL_ATTCLR_MAX; ++i) { if (bmarray[i] && (bm & bmarray[i])) { switch(i) { + case HL_ATTCLR_BOLD: + attr |= HL_BOLD; + break; case HL_ATTCLR_DIM: attr |= HL_DIM; break; - case HL_ATTCLR_BLINK: - attr |= HL_BLINK; + case HL_ATTCLR_ITALIC: + attr |= HL_ITALIC; break; case HL_ATTCLR_ULINE: attr |= HL_ULINE; break; + case HL_ATTCLR_BLINK: + attr |= HL_BLINK; + break; case HL_ATTCLR_INVERSE: attr |= HL_INVERSE; break; - case HL_ATTCLR_BOLD: - attr |= HL_BOLD; - break; } } } @@ -302,15 +307,11 @@ HiliteField(Widget label, struct xwindow *xw = xw_status_win; int colr, attr; -#ifdef TEXTCOLOR if ((colrattr & 0x00ff) >= CLR_MAX) - /* for !TEXTCOLOR, the following line is unconditional */ -#endif colrattr = (colrattr & ~0x00ff) | NO_COLOR; colr = colrattr & 0x00ff; /* guaranteed to be >= 0 and < CLR_MAX */ attr = (colrattr >> 8) & 0x00ff; - /* potentially used even for !TEXTCOLOR configuration */ if (!grayPxl) {/* one-time init */ grayPxl = get_nhcolor(xw, CLR_GRAY).pixel; blackPxl = get_nhcolor(xw, CLR_BLACK).pixel; @@ -399,8 +400,9 @@ PrepStatusField(int fld, Widget label, const char *text) /* set up one status condition for tty-style status display */ static void -DisplayCond(int c_idx, /* index into tt_condorder[] */ - unsigned long *colormasks) +DisplayCond( + int c_idx, /* index into tt_condorder[] */ + unsigned long *colormasks) { Widget label; Arg args[6]; @@ -510,7 +512,10 @@ render_conditions(int row, int dx) /* reset status_hilite for BL_RESET; if highlighting has been disabled or this field is disabled, clear highlighting for this field or condition */ static void -tt_reset_color(int fld, int cond, unsigned long *colormasks) +tt_reset_color( + int fld, + int cond, + unsigned long *colormasks) { Widget label; int colrattr = NO_COLOR; @@ -526,9 +531,7 @@ tt_reset_color(int fld, int cond, unsigned long *colormasks) if (iflags.hilite_delta != 0L && (X11_condition_bits & bm) != 0) { /* BL_RESET before first BL_CONDITION will have colormasks==Null but condcolor() and condattr() can cope with that */ -#ifdef TEXTCOLOR colrattr = condcolor(bm, colormasks); -#endif colrattr |= (condattr(bm, colormasks) << 8); } label = X11_cond_labels[cond]; @@ -642,6 +645,12 @@ tt_status_fixup(void) XtSetArg(args[num_args], nhStr(XtNborderWidth), &si->brd); num_args++; XtSetArg(args[num_args], nhStr(XtNinternalWidth), &si->in_wd); num_args++; XtGetValues(X11_status_labels[0], args, num_args); + + /* X11_status_update_tty() wants this in order to right justify 'vers' */ + if (!xw_status_win->pixel_width) { + XtSetArg(args[0], nhStr(XtNwidth), &xw_status_win->pixel_width); + XtGetValues(xw_status_win->w, args, ONE); + } } DISABLE_WARNING_FORMAT_NONLITERAL @@ -649,10 +658,13 @@ DISABLE_WARNING_FORMAT_NONLITERAL /* core requests updating one status field (or is indicating that it's time to flush all updated fields); tty-style handling */ static void -X11_status_update_tty(int fld, genericptr_t ptr, int chg UNUSED, int percent, - int color, - unsigned long *colormasks) /* bitmask of highlights - for conditions */ +X11_status_update_tty( + int fld, + genericptr_t ptr, + int chg UNUSED, + int percent, + int color, + unsigned long *colormasks) /* bitmask of highlights for conditions */ { static int xtra_space[MAXBLSTATS]; static unsigned long *cond_colormasks = (unsigned long *) 0; @@ -726,10 +738,6 @@ X11_status_update_tty(int fld, genericptr_t ptr, int chg UNUSED, int percent, for a field which isn't active */ *status_vals[fld] = '\0'; } -#ifndef TEXTCOLOR - /* even without color, attribute(s) bits still apply */ - color = (color & ~0x00ff) | NO_COLOR; -#endif #ifdef STATUS_HILITES if (!iflags.hilite_delta) color = NO_COLOR; @@ -794,6 +802,20 @@ X11_status_update_tty(int fld, genericptr_t ptr, int chg UNUSED, int percent, /* where to display the current widget */ lbl_x = (Position) (dx + 1); + if (f == BL_VERS) { + Dimension win_wid = xw_status_win->pixel_width, + fld_wid = lbl_wid + 2 * brd_wid; + + /* in case the doodad for resizing the window via click and + drag is in the lower right corner; justifying all the way + to the edge results in the last character being obscured; + avoid that by treating the 'vers' widget as one char + bigger than it actually is (an implicit trailing space) */ + fld_wid += si->spacew; + /* right justify if there's room */ + if (dx < win_wid - fld_wid) + lbl_x = win_wid - fld_wid; + } (void) memset((genericptr_t) args, 0, sizeof args); num_args = 0; @@ -818,9 +840,12 @@ RESTORE_WARNING_FORMAT_NONLITERAL /*ARGSUSED*/ static void -X11_status_update_fancy(int fld, genericptr_t ptr, int chg UNUSED, - int percent UNUSED, int colrattr, - unsigned long *colormasks UNUSED) +X11_status_update_fancy( + int fld, + genericptr_t ptr, + int chg UNUSED, int percent UNUSED, + int colrattr, + unsigned long *colormasks UNUSED) { static const struct bl_to_ff { int bl, ff; @@ -845,6 +870,7 @@ X11_status_update_fancy(int fld, genericptr_t ptr, int chg UNUSED, { BL_HP, F_HP }, { BL_HPMAX, F_MAXHP }, { BL_LEVELDESC, F_DLEVEL }, + { BL_VERS, F_VERS }, { BL_EXP, F_EXP_PTS } }; static const struct mask_to_ff { @@ -898,10 +924,7 @@ X11_status_update_fancy(int fld, genericptr_t ptr, int chg UNUSED, } else { int colr, attr; -#ifdef TEXTCOLOR if ((colrattr & 0x00ff) >= CLR_MAX) - /* for !TEXTCOLOR, the following line is unconditional */ -#endif colrattr = (colrattr & ~0x00ff) | NO_COLOR; colr = colrattr & 0x00ff; /* guaranteed to be >= 0 and < CLR_MAX */ attr = (colrattr >> 8) & 0x00ff; @@ -915,9 +938,12 @@ X11_status_update_fancy(int fld, genericptr_t ptr, int chg UNUSED, } void -X11_status_update(int fld, genericptr_t ptr, int chg, - int percent, int color, - unsigned long *colormasks) +X11_status_update( + int fld, + genericptr_t ptr, + int chg, int percent, + int color, + unsigned long *colormasks) { if (fld < BL_RESET || fld >= MAXBLSTATS) panic("X11_status_update(%d) -- invalid field", fld); @@ -1037,7 +1063,7 @@ create_status_window_tty(struct xwindow *wp, /* window pointer */ void destroy_status_window_tty(struct xwindow *wp) { - /* If status_information is defined, then it a "text" status window. */ + /* if status_information is defined, then it is a "text" status window */ if (wp->status_information) { if (wp->popup) { nh_XtPopdown(wp->popup); @@ -1062,8 +1088,10 @@ adjust_status_tty(struct xwindow *wp UNUSED, const char *str UNUSED) } void -create_status_window(struct xwindow *wp, /* window pointer */ - boolean create_popup, Widget parent) +create_status_window( + struct xwindow *wp, /* window pointer */ + boolean create_popup, + Widget parent) { struct status_info_t *si = (struct status_info_t *) alloc(sizeof *si); @@ -1077,6 +1105,39 @@ create_status_window(struct xwindow *wp, /* window pointer */ create_status_window_tty(wp, create_popup, parent); else create_status_window_fancy(wp, create_popup, parent); + +#if 0 /* + * this does not work as intended; it triggers + * "Warning: Cannot find callback list in XtAddCallback" + */ + XtAddCallback(wp->w, XtNresizeCallback, stat_resized, (XtPointer) 0); +#else + nhUse(stat_resized); +#endif +} + +/* callback to deal with the game window being resized */ +static void +stat_resized(Widget w, XtPointer call_data, XtPointer client_data) +{ + Arg args[4]; + Cardinal num_args; + struct xwindow *wp = xw_status_win; + + nhUse(call_data); + nhUse(client_data); + + if (w == wp->w) { + num_args = 0; + XtSetArg(args[num_args], XtNwidth, &wp->pixel_width); num_args++; + XtSetArg(args[num_args], XtNwidth, &wp->pixel_height); num_args++; + XtGetValues(w, args, num_args); + } else { + impossible("Status Window resized, but of what widget?"); + } + + /* tell core to call us back for a full status update */ + disp.botlx = TRUE; } void @@ -1247,7 +1308,7 @@ struct X_status_value { /* valid type values */ #define SV_VALUE 0 /* displays a label:value pair */ -#define SV_LABEL 1 /* displays a changable label */ +#define SV_LABEL 1 /* displays a changeable label */ #define SV_NAME 2 /* displays an unchangeable name */ /* for overloaded conditions */ @@ -1277,6 +1338,9 @@ static Widget init_column(const char *, Widget, Widget, Widget, int *, int); static void fixup_cond_widths(void); static Widget init_info_form(Widget, Widget, Widget); +/* narrower values for the array initializer */ +#define W0 (Widget) 0 +#define P0 (Pixel) 0 /* * Notes: * + Alignment needs a different init value, because -1 is an alignment. @@ -1287,68 +1351,72 @@ static Widget init_info_form(Widget, Widget, Widget); */ static struct X_status_value shown_stats[NUM_STATS] = { /* 0 */ - { "", SV_NAME, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 }, + { "", SV_NAME, W0, -1L, 0, FALSE, FALSE, FALSE, P0, 0, 0 }, /* 1 */ - { "Strength", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 }, - { "Dexterity", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 }, - { "Constitution", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 }, - { "Intelligence", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 }, + { "Strength", SV_VALUE, W0, -1L, 0, FALSE, FALSE, FALSE, P0, 0, 0 }, + { "Dexterity", SV_VALUE, W0, -1L, 0, FALSE, FALSE, FALSE, P0, 0, 0 }, + { "Constitution", SV_VALUE, W0, -1L, 0, FALSE, FALSE, FALSE, P0, 0, 0 }, + { "Intelligence", SV_VALUE, W0, -1L, 0, FALSE, FALSE, FALSE, P0, 0, 0 }, /* 5 */ - { "Wisdom", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 }, - { "Charisma", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 }, + { "Wisdom", SV_VALUE, W0, -1L, 0, FALSE, FALSE, FALSE, P0, 0, 0 }, + { "Charisma", SV_VALUE, W0, -1L, 0, FALSE, FALSE, FALSE, P0, 0, 0 }, /* F_NAME: 7 */ - { "", SV_LABEL, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 }, + { "", SV_LABEL, W0, -1L, 0, FALSE, FALSE, FALSE, P0, 0, 0 }, /* F_DLEVEL: 8 */ - { "", SV_LABEL, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 }, - { "Gold", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 }, + { "", SV_LABEL, W0, -1L, 0, FALSE, FALSE, FALSE, P0, 0, 0 }, + { "Gold", SV_VALUE, W0, -1L, 0, FALSE, FALSE, FALSE, P0, 0, 0 }, /* F_HP: 10 */ - { "Hit Points", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 }, - { "Max HP", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 }, - { "Power", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 }, - { "Max Power", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 }, - { "Armor Class", SV_VALUE, (Widget) 0, 256L, 0, FALSE, FALSE, FALSE, 0, 0, 0 }, + { "Hit Points", SV_VALUE, W0, -1L, 0, FALSE, FALSE, FALSE, P0, 0, 0 }, + { "Max HP", SV_VALUE, W0, -1L, 0, FALSE, FALSE, FALSE, P0, 0, 0 }, + { "Power", SV_VALUE, W0, -1L, 0, FALSE, FALSE, FALSE, P0, 0, 0 }, + { "Max Power", SV_VALUE, W0, -1L, 0, FALSE, FALSE, FALSE, P0, 0, 0 }, + { "Armor Class", SV_VALUE, W0, 256L, 0, FALSE, FALSE, FALSE, P0, 0, 0 }, /* F_XP_LEVL: 15 */ - { "Xp Level", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 }, + { "Xp Level", SV_VALUE, W0, -1L, 0, FALSE, FALSE, FALSE, P0, 0, 0 }, /* also 15 (overloaded field) */ - /*{ "Hit Dice", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 },*/ + /*{ "Hit Dice", SV_VALUE, W0, -1L, 0, FALSE, FALSE, FALSE, P0, 0, 0 },*/ /* F_EXP_PTS: 16 (optionally displayed) */ - { "Exp Points", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 }, - { "Alignment", SV_VALUE, (Widget) 0, -2L, 0, FALSE, FALSE, FALSE, 0, 0, 0 }, + { "Exp Points", SV_VALUE, W0, -1L, 0, FALSE, FALSE, FALSE, P0, 0, 0 }, + { "Alignment", SV_VALUE, W0, -2L, 0, FALSE, FALSE, FALSE, P0, 0, 0 }, /* 18, optionally displayed */ - { "Time", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 }, - /* 19, condtionally present, optionally displayed when present */ - { "Score", SV_VALUE, (Widget) 0, -1L, 0, FALSE, FALSE, FALSE, 0, 0, 0 }, + { "Time", SV_VALUE, W0, -1L, 0, FALSE, FALSE, FALSE, P0, 0, 0 }, + /* 19, conditionally present, optionally displayed when present */ + { "Score", SV_VALUE, W0, -1L, 0, FALSE, FALSE, FALSE, P0, 0, 0 }, /* F_HUNGER: 20 (blank if 'normal') */ - { "", SV_NAME, (Widget) 0, -1L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, + { "", SV_NAME, W0, -1L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, /* F_ENCUMBER: 21 (blank if unencumbered) */ - { "", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, - { "Trapped", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, - { "Tethered", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, - { "Levitating", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, + { "", SV_NAME, W0, 0L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, + { "Trapped", SV_NAME, W0, 0L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, + { "Tethered", SV_NAME, W0, 0L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, + { "Levitating", SV_NAME, W0, 0L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, /* 25 */ - { "Flying", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, - { "Riding", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, - { "Grabbed!", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, + { "Flying", SV_NAME, W0, 0L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, + { "Riding", SV_NAME, W0, 0L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, + { "Grabbed!", SV_NAME, W0, 0L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, /* F_STONE: 28 */ - { "Petrifying", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, - { "Slimed", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, + { "Petrifying", SV_NAME, W0, 0L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, + { "Slimed", SV_NAME, W0, 0L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, /* 30 */ - { "Strangled", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, - { "Food Pois", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, - { "Term Ill", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, + { "Strangled", SV_NAME, W0, 0L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, + { "Food Pois", SV_NAME, W0, 0L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, + { "Term Ill", SV_NAME, W0, 0L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, /* F_IN_LAVA: 33 */ - { "Sinking", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, - { "Held", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, + { "Sinking", SV_NAME, W0, 0L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, + { "Held", SV_NAME, W0, 0L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, /* 35 */ - { "Holding", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, - { "Blind", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, - { "Deaf", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, - { "Stunned", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, - { "Confused", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, + { "Holding", SV_NAME, W0, 0L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, + { "Blind", SV_NAME, W0, 0L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, + { "Deaf", SV_NAME, W0, 0L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, + { "Stunned", SV_NAME, W0, 0L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, + { "Confused", SV_NAME, W0, 0L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, /* F_HALLU: 40 (full spelling truncated due to space limitations) */ - { "Hallucinat", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, - { "Wither", SV_NAME, (Widget) 0, 0L, 0, FALSE, TRUE, FALSE, 0, 0, 0 }, + { "Hallucinat", SV_NAME, W0, 0L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, + { "Wither", SV_NAME, W0, 0L, 0, FALSE, TRUE, FALSE, P0, 0, 0 }, + /* F_VERS; optionally shown, generally treated as a pseudo-condition */ + { "Version 1.2.3", SV_LABEL, W0, 0L, 0, FALSE, FALSE, FALSE, P0, 0, 0 }, }; +#undef W0 +#undef P0 /* * The following are supported by the core but not yet handled here: * bareh 'bare handed' (no weapon and no gloves) @@ -1364,13 +1432,17 @@ static struct X_status_value shown_stats[NUM_STATS] = { * woundedl 'wounded legs' (can't kick; temporary dex loss) */ -/* overloaded condition fields */ +/* some conditions are mutually exclusive so we overload their fields in + order to share same display slot */ static const struct f_overload cond_ovl[] = { { (BL_MASK_TRAPPED | BL_MASK_TETHERED), { { BL_MASK_TRAPPED, F_TRAPPED }, { BL_MASK_TETHERED, F_TETHERED } }, }, - { (BL_MASK_HELD | BL_MASK_HOLDING), + { + /* BL_GRABBED is mutually exclusive with these but is more severe so + is shown separately rather than being overloaded with them */ + (BL_MASK_HELD | BL_MASK_HOLDING), { { BL_MASK_HELD, F_HELD }, { BL_MASK_HOLDING, F_HOLDING } }, }, @@ -1467,7 +1539,7 @@ update_val(struct X_status_value *attr_rec, long new_value) if (attr_rec->type == SV_LABEL) { if (attr_rec == &shown_stats[F_NAME]) { - Strcpy(buf, gp.plname); + Strcpy(buf, svp.plname); buf[0] = highc(buf[0]); Strcat(buf, " the "); if (Upolyd) { @@ -1480,22 +1552,28 @@ update_val(struct X_status_value *attr_rec, long new_value) mnam[k] = highc(mnam[k]); } Strcat(buf, mnam); - } else + } else { Strcat(buf, - rank_of(u.ulevel, gp.pl_character[0], flags.female)); + rank_of(u.ulevel, svp.pl_character[0], flags.female)); + } } else if (attr_rec == &shown_stats[F_DLEVEL]) { if (!describe_level(buf, 0)) { - Strcpy(buf, gd.dungeons[u.uz.dnum].dname); + Strcpy(buf, svd.dungeons[u.uz.dnum].dname); Sprintf(eos(buf), ", level %d", depth(&u.uz)); } + } else if (attr_rec == &shown_stats[F_VERS]) { + if (flags.showvers) + (void) status_version(buf, sizeof buf, FALSE); + else + buf[0] = '\0'; } else { impossible("update_val: unknown label type \"%s\"", attr_rec->name); return; } - if (strcmp(buf, attr_rec->name) == 0) + if (!strcmp(buf, attr_rec->name)) return; /* same */ /* Set the label. 'name' field is const for most entries; @@ -1633,8 +1711,10 @@ update_val(struct X_status_value *attr_rec, long new_value) /* * Now highlight the changed information. Don't highlight Time because - * it's continually changing. For others, don't highlight if this is - * the first update. If already highlighted, don't change it unless + * it's continually changing. Don't highlight version because once set + * it only changes if player modifies 'versinfo' option. For others, + * don't highlight if this is the first update. + * If already highlighted, don't change it unless * it's being set to blank (where that item should be reset now instead * of showing highlighted blank until the next expiration check). * @@ -1644,9 +1724,11 @@ update_val(struct X_status_value *attr_rec, long new_value) * highlight because the field becomes blank. */ if (attr_rec->after_init) { - /* toggle if not highlighted and just set to nonblank or if - already highlighted and just set to blank */ - if (attr_rec != &shown_stats[F_TIME] && !attr_rec->set ^ !*buf) { + /* toggle if not highlighted and being set to nonblank or if + already highlighted and being set to blank */ + if (attr_rec != &shown_stats[F_TIME] + && attr_rec != &shown_stats[F_VERS] + && !attr_rec->set ^ !*buf) { /* But don't hilite if inverted from status_hilite since it will already be hilited by apply_hilite_attributes(). */ if (!attr_rec->inverted_hilite) { @@ -1676,7 +1758,7 @@ skip_cond_val(struct X_status_value *sv) if (sv->set) { /* if condition was highlighted and the alternate value has also requested to be highlighted, it used its own copy of - 'set' but the same widget so the highlighing got toggled + 'set' but the same widget so the highlighting got toggled off; this will turn in back on in that exceptional case */ hilight_label(sv->w); sv->set = FALSE; @@ -1722,7 +1804,7 @@ name_widget_has_label(struct X_status_value *sv) XtSetArg(args[0], XtNlabel, &label); XtGetValues(sv->w, args, ONE); - return strlen(label) > 0; + return (*label != '\0'); } static void @@ -1760,10 +1842,11 @@ apply_hilite_attributes(struct X_status_value *sv, int attributes) * dlvl, gold, hp, power, ac, {level & exp or HD **}, time, * status * (stone, slime, strngl, foodpois, termill, * hunger, encumbrance, lev, fly, ride, - * blind, deaf, stun, conf, hallu) + * blind, deaf, stun, conf, hallu, version ***) * - * [*] order of status fields is different on tty. - * [**] HD is shown instead of level and exp if Upolyd. + * [*] order of status fields is different on tty. + * [**] HD is shown instead of level and exp if Upolyd. + * [***] version is optional, right-justified after conditions */ static void update_fancy_status_field(int i, int color, int attributes) @@ -1796,7 +1879,7 @@ update_fancy_status_field(int i, int color, int attributes) break; /* * Label stats. With the exceptions of hunger and encumbrance - * these are either on or off. Pleae leave the ternary operators + * these are either on or off. Please leave the ternary operators * the way they are. I want to specify 0 or 1, not a boolean. */ case F_HUNGER: @@ -1870,6 +1953,11 @@ update_fancy_status_field(int i, int color, int attributes) condmask = BL_MASK_HALLU; break; + /* pseudo-condition */ + case F_VERS: + val = (long) flags.versinfo; /* 1..7 */ + break; + case F_NAME: case F_DLEVEL: val = (long) 0L; @@ -1906,7 +1994,7 @@ update_fancy_status_field(int i, int color, int attributes) val = (long) u.ualign.type; break; case F_TIME: - val = flags.time ? (long) gm.moves : 0L; + val = flags.time ? (long) svm.moves : 0L; break; case F_SCORE: #ifdef SCORE_ON_BOTL @@ -1959,7 +2047,7 @@ update_fancy_status_field(int i, int color, int attributes) static void update_fancy_status(boolean force_update) { - static boolean old_showtime, old_showexp, old_showscore; + static boolean old_showtime, old_showexp, old_showscore, old_showvers; static int old_upolyd = -1; /* -1: force first time update */ int i; @@ -1967,7 +2055,8 @@ update_fancy_status(boolean force_update) || Upolyd != old_upolyd /* Xp vs HD */ || flags.time != old_showtime || flags.showexp != old_showexp - || flags.showscore != old_showscore) { + || flags.showscore != old_showscore + || flags.showvers != old_showvers) { /* update everything; usually only need this on the very first time, then later if the window gets resized or if poly/unpoly triggers Xp <-> HD switch or if an optional field gets @@ -1982,6 +2071,7 @@ update_fancy_status(boolean force_update) old_showtime = flags.time; old_showexp = flags.showexp; old_showscore = flags.showscore; + old_showvers = flags.showvers; } } @@ -2206,8 +2296,10 @@ set_widths(struct X_status_value *sv, int width1, int width2) } static Widget -init_column(const char *name, Widget parent, Widget top, Widget left, - int *col_indices, int xtrawidth) +init_column( + const char *name, + Widget parent, Widget top, Widget left, + int *col_indices, int xtrawidth) { Widget form; Arg args[4]; @@ -2269,6 +2361,7 @@ init_column(const char *name, Widget parent, Widget top, Widget left, * on run-time settings; Xp-level is replaced by Hit-Dice (and Exp-points * suppressed) when the hero is polymorphed. Title and Dungeon-Level span * two columns and might expand to more if 'hitpointbar' is implemented. + * Version is optional, right justified, and much wider than the others. * Title ("Plname the Rank") <> <> <> <> Dungeon-Branch-and-Level <> Hunger Grabbed Held @@ -2276,13 +2369,10 @@ init_column(const char *name, Widget parent, Widget top, Widget left, Power-points Max-Power Dexterity Trapped Slimed Deaf Armor-class Alignment Constitution Levitation Strangled Stunned Xp-level [Exp-points] Intelligence Flying Food-Pois Confused - Gold [Score] Wisdom Riding Term-Ill Hallucinatg - <> [Time] Charisma <> Sinking <> + Gold [Score] Wisdom Riding Term-Ill Hallucinat + <> [Time] Charisma <> Sinking Version * * A seventh column is going to be needed to fit in more conditions. - * - * Possible enhancement: if Exp-points and Score are both disabled, move - * Gold to the Exp-points slot. */ /* including F_DUMMY makes the three status condition columns evenly @@ -2296,7 +2386,7 @@ static int status_indices[3][11] = { { F_DUMMY, F_GRABBED, F_STONE, F_SLIME, F_STRNGL, F_FOODPOIS, F_TERMILL, F_IN_LAVA, -1, 0, 0 }, { F_DUMMY, F_HELD, F_BLIND, F_DEAF, F_STUN, - F_CONF, F_HALLU, F_WITHER, -1, 0, 0 }, + F_CONF, F_HALLU, F_WITHER, F_VERS, -1, 0 }, }; /* used to fill up the empty space to right of 3rd status condition column */ static int leftover_indices[] = { F_DUMMY, -1, 0, 0 }; @@ -2416,6 +2506,23 @@ fixup_cond_widths(void) w2 += 15; } } + + { + Arg args[3]; + Dimension vers_width = 0; + struct X_status_value *sv = &shown_stats[F_VERS]; + + if (sv) { + XtSetArg(args[0], XtNwidth, &vers_width); + XtGetValues(sv->w, args, ONE); + if (vers_width) { + vers_width *= 3; + XtSetArg(args[0], XtNwidth, vers_width); + XtSetArg(args[1], nhStr(XtNjustify), XtJustifyRight); + XtSetValues(sv->w, args, TWO); + } + } + } } /* @@ -2452,7 +2559,14 @@ create_fancy_status(Widget parent, Widget top) Sprintf(buf, "status_condition%d", i + 1); w = init_column(buf, form, (Widget) 0, w, status_indices[i], 0); } - fixup_cond_widths(); /* make all 3 status_conditionN columns same width */ + fixup_cond_widths(); /* make all 3 status_conditionN columns same width + * (actually, the slot for F_VERS is much wider) */ + /* TODO: + * Calculate and set the width of the F_VERS widjet to be from the + * start of the third condition column through the right edge and + * get rid of the dummy column. + */ + /* extra dummy 'column' to allocate any remaining space below the map */ (void) init_column("status_leftover", form, (Widget) 0, w, leftover_indices, 0); diff --git a/win/X11/wintext.c b/win/X11/wintext.c index 291ed81f32..e401b7018d 100644 --- a/win/X11/wintext.c +++ b/win/X11/wintext.c @@ -78,7 +78,7 @@ delete_text(Widget w, XEvent *event, String *params, Cardinal *num_params) } /* - * Callback used for all text windows. The window is poped down on any key + * Callback used for all text windows. The window is popped down on any key * or button down event. It is destroyed if the main nethack code is done * with it. */ @@ -296,7 +296,7 @@ create_text_window(struct xwindow *wp) XtParseTranslationTable(text_translations)); num_args++; - wp->w = XtCreateManagedWidget(gk.killer.name[0] && WIN_MAP == WIN_ERR + wp->w = XtCreateManagedWidget(svk.killer.name[0] && WIN_MAP == WIN_ERR ? "tombstone" : "text_text", /* name */ asciiTextWidgetClass, @@ -467,7 +467,7 @@ calculate_rip_text(int how, time_t when) long cash; /* Put name on stone */ - Sprintf(rip_line[NAME_LINE], "%.16s", gp.plname); /* STONE_LINE_LEN */ + Sprintf(rip_line[NAME_LINE], "%.16s", svp.plname); /* STONE_LINE_LEN */ /* Put $ on stone */ cash = max(gd.done_money, 0L); @@ -482,7 +482,7 @@ calculate_rip_text(int how, time_t when) /* Put death type on stone */ for (line = DEATH_LINE, dpx = buf; line < YEAR_LINE; line++) { - register int i, i0; + int i, i0; char tmpchar; if ((i0 = strlen(dpx)) > STONE_LINE_LEN) { diff --git a/win/X11/winval.c b/win/X11/winval.c index c333a5483c..651ba2cbb3 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 d6df8de4bc..ab63cf3c74 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); @@ -108,7 +105,7 @@ chainin_procs_chain( tdp->nprocs = 0; tdp->ndata = 0; tdp->linknum = n; - cibase = 0; + cibase = tdp; break; case WINCHAIN_INIT: tdp = me; @@ -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, @@ -584,7 +568,7 @@ chainin_ctrl_nhwindow( int request, win_request_info *wri) { - boolean rv; + win_request_info *rv; rv = (*cibase->nprocs->win_ctrl_nhwindow)(cibase->ndata, window, request, wri); @@ -609,7 +593,7 @@ struct window_procs chainin_procs = { chainin_display_nhwindow, chainin_destroy_nhwindow, chainin_curs, chainin_putstr, chainin_putmixed, chainin_display_file, chainin_start_menu, chainin_add_menu, chainin_end_menu, - chainin_select_menu, chainin_message_menu, chainin_update_inventory, + chainin_select_menu, chainin_message_menu, chainin_mark_synch, chainin_wait_synch, #ifdef CLIPPING chainin_cliparound, @@ -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 0f4ebcb57a..72459b8837 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, @@ -702,12 +682,13 @@ chainout_can_suspend(void *vp) win_request_info * chainout_ctrl_nhwindow( + void *vp, winid window, int request, win_request_info *wri) { struct chainout_data *tdp = vp; - boolean rv; + win_request_info *rv; rv = (*tdp->nprocs->win_ctrl_nhwindow)(window, request, wri); @@ -733,7 +714,8 @@ struct chain_procs chainout_procs = { chainout_destroy_nhwindow, chainout_curs, chainout_putstr, chainout_putmixed, chainout_display_file, chainout_start_menu, chainout_add_menu, chainout_end_menu, chainout_select_menu, - chainout_message_menu, chainout_update_inventory, chainout_mark_synch, + chainout_message_menu, + chainout_mark_synch, chainout_wait_synch, #ifdef CLIPPING chainout_cliparound, @@ -753,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 d710f4479a..7287e82a41 100644 --- a/win/chain/wc_trace.c +++ b/win/chain/wc_trace.c @@ -6,11 +6,17 @@ #include "wintty.h" #include "func_tab.h" -#include #include -FILE *wc_tracelogf; /* Should be static, but it's just too useful to have - * access to this logfile from arbitrary other files. */ +#ifdef WIN32 +long getpid(void); +long +getpid(){ + return 0; +} +#endif + +FILE *wc_tracelogf; static unsigned int indent_level; /* Some winfuncs call other winfuncs, so * we need to support nesting. */ @@ -55,7 +61,6 @@ void trace_add_menu(void *,winid, const glyph_info *, const ANY_P *, void trace_end_menu(void *,winid, const char *); int trace_select_menu(void *,winid, int, MENU_ITEM_P **); char trace_message_menu(void *,char, int, const char *); -void trace_update_inventory(void *,int); void trace_mark_synch(void *); void trace_wait_synch(void *); #ifdef CLIPPING @@ -86,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); @@ -101,6 +103,8 @@ void trace_status_update(void *,int, genericptr_t, int, int, int, unsigned long *); boolean trace_can_suspend(void *); +void trace_update_inventory(void *,int); +win_request_info *trace_ctrl_nhwindow(void *, winid, int, win_request_info *); void trace_procs_init(int dir); void *trace_procs_chain(int cmd, int n, void *me, void *nextprocs, void *nextdata); @@ -144,7 +148,7 @@ trace_procs_chain( void trace_procs_init(int dir) { - char fname[200]; + char tfile[20]; long pid; /* processors shouldn't need this test, but just in case */ @@ -152,8 +156,14 @@ trace_procs_init(int dir) return; pid = (long) getpid(); - Sprintf(fname, "%s/tlog.%ld", HACKDIR, pid); + + Sprintf(tfile, "tlog.%ld", pid); +// XXX FQN_NUMBUF is private to files.c + const char *fname = fqname(tfile, TROUBLEPREFIX,7); + printf("TRACEFILE: %s\n",fname); + fflush(stdout); wc_tracelogf = fopen(fname, "w"); + (void)setvbuf(wc_tracelogf, NULL, _IONBF, 0); if (!wc_tracelogf) { fprintf(stderr, "Can't open trace log file %s: %s\n", fname, strerror(errno)); @@ -578,6 +588,16 @@ trace_update_inventory(void *vp, int arg) POST; } +win_request_info * +trace_ctrl_nhwindow(void *vp, winid w, int request, win_request_info *wri){ + struct trace_data *tdp = vp; + + fprintf(wc_tracelogf, "%sctrl_nhwindow(%d, %d, %p)\n", INDENT, w, request, wri); + PRE; + (*tdp->nprocs->win_ctrl_nhwindow)(tdp->ndata, w, request, wri); + POST; +} + void trace_mark_synch(void *vp) { @@ -988,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, @@ -1186,7 +1181,8 @@ trace_can_suspend(void *vp) } struct chain_procs trace_procs = { - "+trace", 0, /* wincap */ + "+trace", wp_trace, + 0, /* wincap */ 0, /* wincap2 */ {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ /* @@ -1202,7 +1198,7 @@ struct chain_procs trace_procs = { trace_create_nhwindow, trace_clear_nhwindow, trace_display_nhwindow, trace_destroy_nhwindow, trace_curs, trace_putstr, trace_putmixed, trace_display_file, trace_start_menu, trace_add_menu, trace_end_menu, - trace_select_menu, trace_message_menu, trace_update_inventory, + trace_select_menu, trace_message_menu, trace_mark_synch, trace_wait_synch, #ifdef CLIPPING trace_cliparound, @@ -1221,11 +1217,11 @@ 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, trace_status_update, trace_can_suspend, + trace_update_inventory, + trace_ctrl_nhwindow }; diff --git a/win/curses/Readme.txt b/win/curses/Readme.txt index fd29d7b4f4..4a5c5bc55f 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, @@ -50,11 +50,11 @@ differences are primarily visual. This windowport supports dymanic resizing of the terminal window, so you can play with it to see how it looks best to you during a game. Also, the align_status and align_message options may be set during the game, so you can experiment -to see what arraingement looks best to you. +to see what arrangement looks best to you. For menus, in addition to the normal configurable keybindings for menu navigation descrived in the Guidebook, you can use the right and left -arrows to to forward or backward one page, respectively, and the home +arrows to forward or backward one page, respectively, and the home and end keys to go to the first and last pages, respectively. Some configuration options that are specific to or relevant to the @@ -105,7 +105,7 @@ CONTACT Please send any bug reports, suggestions, patches, or miscellaneous feedback to me (Karl Garrison) at: kgarrison@pobox.com. Note that as -of this writing, I only have sporatic Internet access, so I may not get +of this writing, I only have sporadic Internet access, so I may not get back to you right away. Happy Hacking! diff --git a/win/curses/Todo.txt b/win/curses/Todo.txt index e942a6fa03..552ddbf579 100644 --- a/win/curses/Todo.txt +++ b/win/curses/Todo.txt @@ -36,7 +36,7 @@ DISPLAY * Animation effects do not display properly - this could probably be fixed with a correct implementation of the curses_delay_output function. - * Support option to set forground and background colors for individual + * Support option to set foreground and background colors for individual windows @@ -45,7 +45,7 @@ MENUS (cursdial.c) - * Menus need to be able to accept a count as input, e.g. to specifiy + * Menus need to be able to accept a count as input, e.g. to specify how many items to drop. * Currently the "preselected" flag for an individual menu item is @@ -79,7 +79,7 @@ MAP WINDOW * The map window would probably benefit from a total redesign. Right now, it uses a pad instead of a regular curses window, which causes a - number of special cases in the code to account for it, and a seperate + number of special cases in the code to account for it, and a separate window behind it just to draw the border. It feels kludgy and annoying! @@ -95,7 +95,7 @@ STATUS WINDOW Gnomish Mines) and perhaps show thermometer bars for hit points and magical power. - * Conversely, if we have a narrower dislay, compress some of the + * Conversely, if we have a narrower display, compress some of the labels to save space, and do not display some items that never or rarely change (e.g. name, level and title, and alignment). Perhaps display changes to these fields in the message window if they do @@ -132,7 +132,7 @@ MISC * Update documentation and in-game help to describe the newly-added options: cursesgraphics, term_rows, term_cols, and windowborders. - * Recognize "Alt" key in a platform-independant way to allow its use + * Recognize "Alt" key in a platform-independent way to allow its use to select extended commands. Currently this works for PDCurses. For Ncurses, the Alt key works in an xterm or rxvt if the -meta8 flag is passed, but I'd like to see a general way of detecting it. diff --git a/win/curses/cursdial.c b/win/curses/cursdial.c index 0235e3839f..dddfd400ec 100644 --- a/win/curses/cursdial.c +++ b/win/curses/cursdial.c @@ -6,9 +6,11 @@ #include "curses.h" #include "hack.h" #include "wincurs.h" +#include "cursinit.h" +#include "curswins.h" +#include "cursmisc.h" #include "cursdial.h" #include "func_tab.h" -#include #if defined(FILENAME_CMP) && !defined(strcasecmp) #define strcasecmp FILENAME_CMP @@ -34,6 +36,7 @@ typedef struct nhmi { char accelerator; /* Character used to select item from menu */ char group_accel; /* Group accelerator for menu item, if any */ int attr; /* Text attributes for item */ + int color; /* Color for item */ const char *str; /* Text of menu item */ boolean presel; /* Whether menu item should be preselected */ boolean selected; /* Whether item is currently selected */ @@ -67,25 +70,27 @@ typedef enum menu_op_type { INVERT } menu_op; -static nhmenu_item *curs_new_menu_item(winid, const char *); -static void curs_pad_menu(nhmenu *, boolean); -static nhmenu *get_menu(winid wid); +static nhmenu_item *curs_new_menu_item(winid, const char *) NONNULL; +static void curs_pad_menu(nhmenu *, boolean) NONNULLARG1; +static nhmenu *get_menu(winid wid); /* might return Null */ static char menu_get_accel(boolean first); -static void menu_determine_pages(nhmenu *menu); -static boolean menu_is_multipage(nhmenu *menu, int width, int height); -static void menu_win_size(nhmenu *menu); +static void menu_determine_pages(nhmenu *menu) NONNULLARG1; +static boolean menu_is_multipage(nhmenu *menu, int width, + int height) NONNULLARG1; +static void menu_win_size(nhmenu *menu) NONNULLARG1; #ifdef NCURSES_MOUSE_VERSION -static nhmenu_item *get_menuitem_y(nhmenu *menu, WINDOW *win UNUSED, - int page_num, int liney); +static nhmenu_item *get_menuitem_y(WINDOW *win UNUSED, nhmenu *menu, + int page_num, int liney) NONNULLARG12; #endif /*NCURSES_MOUSE_VERSION*/ -static void menu_display_page(nhmenu *menu, WINDOW *win, int page_num, - char *, char *); -static int menu_get_selections(WINDOW *win, nhmenu *menu, int how); +static void menu_display_page(WINDOW *win, nhmenu *menu, int page_num, + char *, char *) NONNULLARG12; +static int menu_get_selections(WINDOW *win, nhmenu *menu, + int how) NONNULLARG12; static void menu_select_deselect(WINDOW *win, nhmenu_item *item, - menu_op operation, int); + menu_op operation, int) NONNULLARG12; static int menu_operation(WINDOW *win, nhmenu *menu, menu_op operation, - int page_num); -static void menu_clear_selections(nhmenu *menu); + int page_num) NONNULLARG12; +static void menu_clear_selections(nhmenu *menu) NONNULLARG1; static int menu_max_height(void); static nhmenu *nhmenus = NULL; /* NetHack menu array */ @@ -148,8 +153,9 @@ curses_line_input_dialog( free(tmpstr); } - bwin = curses_create_window(prompt_width, height, + bwin = curses_create_window(TEXT_WIN, prompt_width, height, iflags.window_inited ? UP : CENTER); + curses_set_wid_colors(TEXT_WIN, bwin); wrefresh(bwin); getbegyx(bwin, winy, winx); askwin = newwin(height, prompt_width, winy + 1, winx + 1); @@ -220,7 +226,7 @@ curses_character_input_dialog( re-activate them now that input is being requested */ curses_got_input(); - if (gi.invent || (gm.moves > 1)) { + if (svm.moves > 0) { curses_get_window_size(MAP_WIN, &map_height, &map_width); } else { map_height = term_rows; @@ -247,7 +253,7 @@ curses_character_input_dialog( choicestr[count + 2] = choices[count]; } choicestr[count + 2] = ']'; - if (((def >= 'A') && (def <= 'Z')) || ((def >= 'a') && (def <= 'z'))) { + if ((def >= 'A' && def <= 'Z') || (def >= 'a' && def <= 'z')) { choicestr[count + 3] = ' '; choicestr[count + 4] = '('; choicestr[count + 5] = def; @@ -274,7 +280,7 @@ curses_character_input_dialog( } if (iflags.wc_popup_dialog /*|| curses_stupid_hack*/) { - askwin = curses_create_window(prompt_width, prompt_height, UP); + askwin = curses_create_window(TEXT_WIN, prompt_width, prompt_height, UP); activemenu = askwin; for (count = 0; count < prompt_height; count++) { @@ -283,6 +289,7 @@ curses_character_input_dialog( free(linestr); } + curses_set_wid_colors(TEXT_WIN, askwin); wrefresh(askwin); } else { /* TODO: add SUPPRESS_HISTORY flag, then after getting a response, @@ -320,7 +327,8 @@ curses_character_input_dialog( } break; } else if ((answer == '\n') || (answer == '\r') || (answer == ' ')) { - if ((choices != NULL) && (def != '\0')) { + if ((choices != NULL) + && ((def != '\0') || !strchr(choices, answer))) { answer = def; } break; @@ -388,7 +396,8 @@ curses_ext_cmd(void) if (iflags.wc_popup_dialog) { /* Prompt in popup window */ int x0, y0, w, h; /* bounding coords of popup */ - extwin2 = curses_create_window(25, 1, UP); + extwin2 = curses_create_window(TEXT_WIN, 25, 1, UP); + curses_set_wid_colors(TEXT_WIN, extwin2); wrefresh(extwin2); /* create window inside window to prevent overwriting of border */ getbegyx(extwin2, y0, x0); @@ -417,11 +426,11 @@ curses_ext_cmd(void) while (1) { wmove(extwin, starty, startx); + wclrtoeol(extwin); waddstr(extwin, "# "); wmove(extwin, starty, startx + 2); waddstr(extwin, cur_choice); wmove(extwin, starty, (int) strlen(cur_choice) + startx + 2); - wclrtoeol(extwin); /* if we have an autocomplete command, AND it matches uniquely */ if (matches == 1 && ecmatches) { @@ -585,7 +594,8 @@ curs_new_menu_item(winid wid, const char *str) new_item->identifier = cg.zeroany; new_item->accelerator = '\0'; new_item->group_accel = '\0'; - new_item->attr = 0; + new_item->attr = ATR_NONE; + new_item->color = NO_COLOR; new_item->str = new_str; new_item->presel = FALSE; new_item->selected = FALSE; @@ -607,6 +617,7 @@ curses_add_nhmenu_item( char accelerator, char group_accel, int attr, + int clr, const char *str, unsigned itemflags) { @@ -630,6 +641,7 @@ curses_add_nhmenu_item( new_item->accelerator = accelerator; new_item->group_accel = group_accel; new_item->attr = attr; + new_item->color = clr; new_item->presel = presel; new_item->itemflags = itemflags; current_items = current_menu->entries; @@ -696,6 +708,7 @@ curs_menu_set_bottom_heavy(winid wid) { nhmenu *menu = get_menu(wid); + assert(menu != NULL); /* only called when 'wid' is a menu */ /* * Called after end_menu + finalize_nhmenu, * before select_menu + display_nhmenu. @@ -775,14 +788,15 @@ curses_display_nhmenu( menu_determine_pages(current_menu); /* Display pre and post-game menus centered */ - if ((gm.moves <= 1 && !gi.invent) || gp.program_state.gameover) { - win = curses_create_window(current_menu->width, + if (svm.moves == 0 || program_state.gameover) { + win = curses_create_window(wid, current_menu->width, current_menu->height, CENTER); } else { /* Display during-game menus on the right out of the way */ - win = curses_create_window(current_menu->width, + win = curses_create_window(wid, current_menu->width, current_menu->height, RIGHT); } + curses_set_wid_colors(wid, win); num_chosen = menu_get_selections(win, current_menu, how); curses_destroy_win(win); @@ -1016,9 +1030,9 @@ menu_win_size(nhmenu *menu) int maxwidth, maxheight, curentrywidth, lastline; int maxentrywidth = 0; int maxheaderwidth = menu->prompt ? (int) strlen(menu->prompt) : 0; - nhmenu_item *menu_item_ptr; + nhmenu_item *menu_item_ptr, *last_item_ptr = NULL; - if (gp.program_state.gameover) { + if (program_state.gameover) { /* for final inventory disclosure, use full width */ maxwidth = term_cols - 2; /* +2: borders assumed */ } else { @@ -1035,6 +1049,7 @@ menu_win_size(nhmenu *menu) maxheight = menu_max_height(); /* First, determine the width of the longest menu entry */ + assert(menu->entries != NULL); /* at least one iteration will occur */ for (menu_item_ptr = menu->entries; menu_item_ptr != NULL; menu_item_ptr = menu_item_ptr->next_item) { curentrywidth = (int) strlen(menu_item_ptr->str); @@ -1054,6 +1069,9 @@ menu_win_size(nhmenu *menu) if (curentrywidth > maxentrywidth) { maxentrywidth = curentrywidth; } + + /* might need this later */ + last_item_ptr = menu_item_ptr; } /* @@ -1072,15 +1090,12 @@ menu_win_size(nhmenu *menu) maxwidth = term_cols - 2; } - /* Possibly reduce height if only 1 page */ + /* Possibly reduce height if only 1 page. + Note: menu_is_multipage() populates entry->line_num and + entry->num_lines for all the menu's entries. */ if (!menu_is_multipage(menu, maxwidth, maxheight)) { - menu_item_ptr = menu->entries; - - while (menu_item_ptr->next_item != NULL) { - menu_item_ptr = menu_item_ptr->next_item; - } - - lastline = (menu_item_ptr->line_num) + menu_item_ptr->num_lines; + assert(last_item_ptr != NULL); + lastline = last_item_ptr->line_num + last_item_ptr->num_lines; if (lastline < maxheight) { maxheight = lastline; @@ -1088,7 +1103,7 @@ menu_win_size(nhmenu *menu) } /* avoid a tiny popup window; when it's shown over the endings of - old messsages rather than over the map, it is fairly easy for + old messages rather than over the map, it is fairly easy for the player to overlook it, particularly when walking around and stepping on a pile of 2 items; also, multi-page menus need enough room for "(Page M of N) => " even if all entries are narrower @@ -1100,8 +1115,8 @@ menu_win_size(nhmenu *menu) #ifdef NCURSES_MOUSE_VERSION static nhmenu_item * get_menuitem_y( - nhmenu *menu, WINDOW *win UNUSED, + nhmenu *menu, int page_num, int liney) { @@ -1160,9 +1175,12 @@ get_menuitem_y( /* Displays menu selections in the given window */ +extern color_attr curses_menu_promptstyle; /*cursmain.c */ + static void menu_display_page( - nhmenu *menu, WINDOW *win, + WINDOW *win, + nhmenu *menu, int page_num, /* page number, 1..n rather than 0..n-1 */ char *selectors, /* selection letters on current page */ char *groupaccels) /* group accelerator characters on any page */ @@ -1171,8 +1189,7 @@ menu_display_page( int count, curletter, entry_cols, start_col, num_lines; char *tmpstr; boolean first_accel = TRUE; - int color = NO_COLOR, attr = A_NORMAL; - boolean menu_color = FALSE; + int color = NO_COLOR, attr; /* letters assigned to entries on current page */ if (selectors) @@ -1204,7 +1221,11 @@ menu_display_page( for (count = 0; count < num_lines; count++) { tmpstr = curses_break_str(menu->prompt, menu->width, count + 1); + curses_menu_color_attr(win, curses_menu_promptstyle.color, + curses_menu_promptstyle.attr, ON); mvwprintw(win, count + 1, 1, "%s", tmpstr); + curses_menu_color_attr(win, curses_menu_promptstyle.color, + curses_menu_promptstyle.attr, OFF); free(tmpstr); } } @@ -1275,18 +1296,13 @@ menu_display_page( start_col += 2; } #endif - color = NONE; - menu_color = iflags.use_menu_color - && get_menu_coloring(menu_item_ptr->str, &color, &attr); - if (menu_color) { - attr = curses_convert_attr(attr); - if (color != NONE || attr != A_NORMAL) - curses_menu_color_attr(win, color, attr, ON); - } else { - attr = menu_item_ptr->attr; - if (color != NONE || attr != A_NORMAL) - curses_toggle_color_attr(win, color, attr, ON); - } + color = menu_item_ptr->color; + if (color == NO_COLOR) + color = NONE; + attr = menu_item_ptr->attr; + /* attr is already a curses attr (A_ not ATR_) */ + if (color != NONE || attr != A_NORMAL) + curses_menu_color_attr(win, color, attr, ON); num_lines = curses_num_lines(menu_item_ptr->str, entry_cols); for (count = 0; count < num_lines; count++) { @@ -1299,12 +1315,8 @@ menu_display_page( } } if (color != NONE || attr != A_NORMAL) { - if (menu_color) - curses_menu_color_attr(win, color, attr, OFF); - else - curses_toggle_color_attr(win, color, attr, OFF); + curses_menu_color_attr(win, color, attr, OFF); } - menu_item_ptr = menu_item_ptr->next_item; } @@ -1331,17 +1343,21 @@ menu_display_page( curses_toggle_color_attr(win, HIGHLIGHT_COLOR, NONE, OFF); } } - curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, ON); + if (curses_win_clr_inited(MENU_WIN) < 1) + curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, ON); box(win, 0, 0); - curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, OFF); + if (curses_win_clr_inited(MENU_WIN) < 1) + curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, OFF); wrefresh(win); } /* split out from menu_get_selections() so that perm_invent scrolling - can be controlled from outside the normal menu activity */ + can be controlled from outside the normal menu activity [groundwork; + ultimately not used that way, only from menu_get_seletions() itself] */ boolean curs_nonselect_menu_action( - WINDOW *win, void *menu_v, + WINDOW *win, + void *menu_v, /* generic pointer; caller might not know type 'nhmenu' */ int how, int curletter, int *curpage_p, @@ -1371,7 +1387,7 @@ curs_nonselect_menu_action( if (wmouse_trafo(win, &mev.y, &mev.x, FALSE)) { int y = mev.y; - menu_item_ptr = get_menuitem_y(menu, win, *curpage_p, y); + menu_item_ptr = get_menuitem_y(win, menu, *curpage_p, y); if (menu_item_ptr) { if (how == PICK_ONE) { @@ -1396,7 +1412,7 @@ curs_nonselect_menu_action( case ' ': if (*curpage_p < menu->num_pages) { ++(*curpage_p); - menu_display_page(menu, win, *curpage_p, selectors, (char *) 0); + menu_display_page(win, menu, *curpage_p, selectors, (char *) 0); } else if (menucmd == ' ') { dismiss = TRUE; break; @@ -1407,26 +1423,29 @@ curs_nonselect_menu_action( case MENU_PREVIOUS_PAGE: if (*curpage_p > 1) { --(*curpage_p); - menu_display_page(menu, win, *curpage_p, selectors, (char *) 0); + menu_display_page(win, menu, *curpage_p, selectors, (char *) 0); } break; case KEY_END: case MENU_LAST_PAGE: if (*curpage_p != menu->num_pages) { *curpage_p = menu->num_pages; - menu_display_page(menu, win, *curpage_p, selectors, (char *) 0); + menu_display_page(win, menu, *curpage_p, selectors, (char *) 0); } break; case KEY_HOME: case MENU_FIRST_PAGE: if (*curpage_p != 1) { *curpage_p = 1; - menu_display_page(menu, win, *curpage_p, selectors, (char *) 0); + menu_display_page(win, menu, *curpage_p, selectors, (char *) 0); } break; case MENU_SEARCH: { char search_key[BUFSZ]; + if (how == PICK_NONE) + break; + search_key[0] = '\0'; curses_line_input_dialog("Search for:", search_key, BUFSZ); @@ -1484,7 +1503,7 @@ menu_get_selections(WINDOW *win, nhmenu *menu, int how) nhmenu_item *menu_item_ptr = menu->entries; activemenu = win; - menu_display_page(menu, win, curpage, selectors, groupaccels); + menu_display_page(win, menu, curpage, selectors, groupaccels); while (!dismiss) { curletter = curses_getch(); @@ -1536,9 +1555,12 @@ menu_get_selections(WINDOW *win, nhmenu *menu, int how) break; } } + FALLTHROUGH; /*FALLTHRU*/ default: - if (isdigit(curletter) && !groupaccels[curletter]) { + if (curletter > 0 && curletter < 256 + && isdigit(curletter) && !selectors[curletter] + && !groupaccels[curletter]) { count = curses_get_count(curletter); /* after count, we know some non-digit is already pending */ curletter = curses_getch(); @@ -1573,7 +1595,7 @@ menu_get_selections(WINDOW *win, nhmenu *menu, int how) && curletter == menu_item_ptr->group_accel)) { if (curpage != menu_item_ptr->page_num) { curpage = menu_item_ptr->page_num; - menu_display_page(menu, win, curpage, selectors, + menu_display_page(win, menu, curpage, selectors, (char *) 0); } @@ -1660,7 +1682,7 @@ menu_select_deselect( /* Perform the selected operation (select, unselect, invert selection) -on the given menu page. If menu_page is 0, then perform opetation on +on the given menu page. If menu_page is 0, then perform operation on all pages in menu. Returns last page displayed. */ static int @@ -1692,7 +1714,7 @@ menu_operation( current_page = first_page; if (page_num == 0) { - menu_display_page(menu, win, current_page, (char *) 0, (char *) 0); + menu_display_page(win, menu, current_page, (char *) 0, (char *) 0); } if (menu_item_ptr == NULL) { /* Page not found */ @@ -1707,7 +1729,7 @@ menu_operation( } current_page = menu_item_ptr->page_num; - menu_display_page(menu, win, current_page, (char *) 0, (char *) 0); + menu_display_page(win, menu, current_page, (char *) 0, (char *) 0); } if (menu_item_ptr->identifier.a_void != NULL) { diff --git a/win/curses/cursdial.h b/win/curses/cursdial.h index bda9508d9a..167ec793e4 100644 --- a/win/curses/cursdial.h +++ b/win/curses/cursdial.h @@ -8,14 +8,15 @@ /* Global declarations */ -void curses_line_input_dialog(const char *prompt, char *answer, int buffer); +void curses_line_input_dialog(const char *prompt, char *answer, + int buffer) NONNULLPTRS; int curses_character_input_dialog(const char *prompt, const char *choices, - char def); + char def) NONNULLARG1; int curses_ext_cmd(void); void curses_create_nhmenu(winid wid, unsigned long); void curses_add_nhmenu_item(winid wid, const glyph_info *glyphinfo, const ANY_P *identifier, char accelerator, - char group_accel, int attr, + char group_accel, int attr, int clr, const char *str, unsigned itemflags); void curs_menu_set_bottom_heavy(winid); void curses_finalize_nhmenu(winid wid, const char *prompt); @@ -24,6 +25,7 @@ boolean curses_menu_exists(winid wid); void curses_del_menu(winid, boolean); boolean curs_nonselect_menu_action(WINDOW *win, void *menu, int how, int curletter, int *curpage_p, - char selectors[256], int *num_selected_p); + char selectors[256], int *num_selected_p) + NONNULLPTRS; #endif /* CURSDIAL_H */ diff --git a/win/curses/cursinit.c b/win/curses/cursinit.c index 6f38825843..6b995a310e 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 */ @@ -97,7 +95,7 @@ set_window_position(int *winx, int *winy, int *winw, int *winh, void curses_create_main_windows(void) { - int min_message_height UNUSED = 1; + /* int min_message_height = 1; */ int message_orientation = 0; int status_orientation = 0; int border_space = 0; @@ -112,6 +110,7 @@ curses_create_main_windows(void) case 3: noperminv_borders = TRUE; + FALLTHROUGH; /*FALLTHRU*/ case 1: /* On */ borders = TRUE; @@ -119,6 +118,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); @@ -131,7 +131,7 @@ curses_create_main_windows(void) } if ((term_cols - border_space) < COLNO) { - min_message_height++; + /* min_message_height++; */ } /* Determine status window orientation */ @@ -205,7 +205,7 @@ curses_create_main_windows(void) boolean msg_vertical = (message_orientation == ALIGN_LEFT || message_orientation == ALIGN_RIGHT); - /* Vertical windows have priority. Otherwise, priotity is: + /* Vertical windows have priority. Otherwise, priority is: status > inv > msg */ if (status_vertical) set_window_position(&status_x, &status_y, @@ -295,72 +295,76 @@ curses_create_main_windows(void) } } +static int pairs_used = 0; +static int colors_used = 0; + +/* create a new color */ +int +curses_init_rgb(int r, int g, int b) +{ + if (!can_change_color()) + return 0; + + if (colors_used < COLORS - 1) { + colors_used++; + init_color(colors_used, r*4, g*4, b*4); + return colors_used; + } + return 0; +} + +/* create a new foreground/background combination */ +int +curses_init_pair(int fg, int bg) +{ + if (pairs_used < COLOR_PAIRS - 1) { + pairs_used++; + init_pair(pairs_used, fg, bg); + return pairs_used; + } + return 0; +} + /* Initialize curses colors to colors used by NetHack */ void curses_init_nhcolors(void) { -#ifdef TEXTCOLOR - if (has_colors()) { - use_default_colors(); - init_pair(1, COLOR_BLACK, -1); - init_pair(2, COLOR_RED, -1); - init_pair(3, COLOR_GREEN, -1); - init_pair(4, COLOR_YELLOW, -1); - init_pair(5, COLOR_BLUE, -1); - init_pair(6, COLOR_MAGENTA, -1); - init_pair(7, COLOR_CYAN, -1); - init_pair(8, -1, -1); - - { - int i; - boolean hicolor = FALSE; - - static const int clr_remap[16] = { - COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, - COLOR_BLUE, - COLOR_MAGENTA, COLOR_CYAN, -1, COLOR_WHITE, - COLOR_RED + 8, COLOR_GREEN + 8, COLOR_YELLOW + 8, - COLOR_BLUE + 8, - COLOR_MAGENTA + 8, COLOR_CYAN + 8, COLOR_WHITE + 8 - }; - - for (i = 0; i < (COLORS >= 16 ? 16 : 8); i++) { - init_pair(17 + (i * 2) + 0, clr_remap[i], COLOR_RED); - init_pair(17 + (i * 2) + 1, clr_remap[i], COLOR_BLUE); - } - - if (COLORS >= 16) - hicolor = TRUE; - - /* Work around the crazy definitions above for more background - colors... */ - for (i = 0; i < (COLORS >= 16 ? 16 : 8); i++) { - init_pair((hicolor ? 49 : 9) + i, clr_remap[i], COLOR_GREEN); - init_pair((hicolor ? 65 : 33) + i, clr_remap[i], COLOR_YELLOW); - init_pair((hicolor ? 81 : 41) + i, clr_remap[i], COLOR_MAGENTA); - init_pair((hicolor ? 97 : 49) + i, clr_remap[i], COLOR_CYAN); - init_pair((hicolor ? 113 : 57) + i, clr_remap[i], COLOR_WHITE); - } + /* COLOR_foo + 8 means COLOR | A_BOLD when COLORS < 16 */ + /* otherwise assume the terminal has least 16 different colors */ + /* these map to the NetHack CLR_ defines */ + static const int fg_clr[16] = { + COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, + COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE, + -1, COLOR_RED + 8, COLOR_GREEN + 8, COLOR_YELLOW + 8, + COLOR_BLUE + 8, COLOR_MAGENTA + 8, COLOR_CYAN + 8, COLOR_WHITE + 8 + }; + static const int bg_clr[8] = { + -1, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, + COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE + }; + int bg, nhclr; + int maxc = (COLORS >= 16) ? 16 : 8; + + if (!has_colors()) + return; + + use_default_colors(); + + for (nhclr = CLR_BLACK; nhclr < maxc; nhclr++) { + for (bg = 0; bg < 8; bg++) { + init_pair((maxc * bg) + nhclr + 1, fg_clr[nhclr], bg_clr[bg]); } + } - - if (COLORS >= 16) { -# ifdef USE_DARKGRAY - if (iflags.wc2_darkgray) { - init_pair(1, COLOR_BLACK + 8, -1); - } -# endif - init_pair(9, COLOR_WHITE, -1); - init_pair(10, COLOR_RED + 8, -1); - init_pair(11, COLOR_GREEN + 8, -1); - init_pair(12, COLOR_YELLOW + 8, -1); - init_pair(13, COLOR_BLUE + 8, -1); - init_pair(14, COLOR_MAGENTA + 8, -1); - init_pair(15, COLOR_CYAN + 8, -1); - init_pair(16, COLOR_WHITE + 8, -1); +#ifdef USE_DARKGRAY + if (COLORS >= 16) { + if (iflags.wc2_darkgray) { + init_pair(CLR_BLACK + 1, COLOR_BLACK + 8, -1); } } #endif + colors_used = maxc; + pairs_used = (maxc * 8) + 16 + 1; } #if 0 /* curses_choose_character + curses_character_dialog no longer used */ @@ -683,11 +687,11 @@ int curses_character_dialog(const char **choices, const char *prompt) { int count, count2, ret, curletter; - char used_letters[52]; + char used_letters[invlet_basic]; /* a..zA..Z */ anything identifier; menu_item *selected = NULL; winid wid = curses_get_wid(NHW_MENU); - int clr = 0; + int clr = NO_COLOR; identifier.a_void = 0; curses_start_menu(wid, MENU_BEHAVE_STANDARD); @@ -750,7 +754,7 @@ curses_init_options(void) #ifdef PDCURSES /* PDCurses for SDL, win32 and OS/2 has the ability to set the - terminal size programatically. If the user does not specify a + terminal size programmatically. If the user does not specify a size in the config file, we will set it to a nice big 32x110 to take advantage of some of the nice features of this windowport. */ if (iflags.wc2_term_cols == 0) @@ -777,17 +781,6 @@ curses_init_options(void) /* FIXME: this overrides explicit OPTIONS=!use_inverse */ iflags.wc_inverse = TRUE; /* aka iflags.use_inverse; default is False */ - /* fix up pet highlighting */ - if (iflags.wc2_petattr == -1) /* shouldn't happen */ - iflags.wc2_petattr = A_NORMAL; - if (iflags.wc2_petattr != A_NORMAL) { - /* Pet attribute specified, so hilite_pet should be true */ - iflags.hilite_pet = TRUE; - } else if (iflags.hilite_pet) { - /* pet highlighting specified, so don't leave petattr at A_NORMAL */ - iflags.wc2_petattr = A_REVERSE; - } - /* curses doesn't support 's' (single message at a time; successive ^P's go back to earlier messages) and 'c' (combination; single on first and second of consecutive ^P's, full on third) */ diff --git a/win/curses/cursinit.h b/win/curses/cursinit.h index ba3b5a7eea..fbc9abe08c 100644 --- a/win/curses/cursinit.h +++ b/win/curses/cursinit.h @@ -9,6 +9,8 @@ /* Global declarations */ void curses_create_main_windows(void); +int curses_init_rgb(int r, int g, int b); +int curses_init_pair(int fg, int bg); void curses_init_nhcolors(void); void curses_choose_character(void); int curses_character_dialog(const char **choices, const char *prompt); diff --git a/win/curses/cursinvt.c b/win/curses/cursinvt.c index 00ca687581..4095ceb5a2 100644 --- a/win/curses/cursinvt.c +++ b/win/curses/cursinvt.c @@ -22,6 +22,7 @@ static void curs_show_invt(WINDOW *); struct pi_line { char *invtxt; /* class header or inventory item without letter prefix */ attr_t c_attr; /* attribute for class headers */ + int color; char letter; /* inventory letter; accelerator if this was really a menu; * used to distinguish item lines from header lines and for * display (no selection possible) */ @@ -85,7 +86,10 @@ curs_update_invt(int arg) werase(win); if (!arg) { - + /* + * 'arg'==0 means basic inventory_update(): [re-]populate and + * [re-]display the persistent inventory window. + */ if (pi.array) /* previous data is obsolete */ curs_purge_perminv_data(FALSE); @@ -95,7 +99,13 @@ curs_update_invt(int arg) display_inventory(NULL, FALSE); curs_invt_updated(win); - } else { /* 'arg' is non-zero but otherwise unused */ + } else { + /* + * 'arg'!=0 means #perminv command is providing the player + * with opportunity to scroll the already displayed persistent + * inventory window. Aside from being non-zero, the value of + * 'arg' is ignored. + */ int scrollingdone; /* previous data is still valid; let player interactively scroll it */ @@ -177,6 +187,7 @@ curs_scroll_invt(WINDOW *win UNUSED) res = 1; break; } + FALLTHROUGH; /*FALLTHRU*/ case KEY_RIGHT: case KEY_NPAGE: @@ -281,6 +292,7 @@ curs_add_invt( int linenum, /* line index; 1..n rather than 0..n-1 */ char accelerator, /* selector letter for items, 0 for class headers */ attr_t attr, /* curses attribute for headers, 0 for items */ + int clr, /* NetHack color for headers, NO_COLOR for items */ const char *str) /* formatted inventory item, without invlet prefix, * or class header text */ { @@ -300,6 +312,7 @@ curs_add_invt( newelement.invtxt = dupstr(str); newelement.c_attr = attr; /* note: caller has already converted 'attr' * from tty-style attribute to curses one */ + newelement.color = clr; newelement.letter = accelerator; aptr[pi.inuseindx++] = newelement; @@ -323,9 +336,12 @@ curs_show_invt(WINDOW *win) char accelerator, tmpbuf[BUFSZ]; int attr, color; unsigned lineno, stroffset, widest, left_col, right_col, - first_shown = 0, last_shown = 0, item_count = 0; + first_shown = 0, last_shown = 0, item_count = 0, xtra_line = 0; int x, y, width, height, available_width, - border = curses_window_has_border(INV_WIN) ? 1 : 0; + border = curses_window_has_border(INV_WIN) ? 1 : 0, + /* we actually care about the topmost of message, map, or status + but either they all have borders or none do so just check map */ + otherborder = curses_window_has_border(MAP_WIN) ? 1 : 0; x = border; /* same for every line; 1 if border, 0 otherwise */ @@ -334,6 +350,25 @@ curs_show_invt(WINDOW *win) left_col = pi.coloffset + 1; right_col = left_col + (unsigned) width - 1; + /* + * If there will be any blank lines left at the bottom, sometimes + * insert one blank line at the top. If the first line is an item + * (via !sortpack) which will be obscured by the column indicator, + * or there is only one line (such as "not carrying anything") if + * that line would be at the very top aligned with the top border + * of another window (windowborders=3,4) because that looks odd. + * This only handles the single page case [because scrolling always + * fills pages beyond the first rather than just continuing from + * last line of preceding page, hence no blank line(s) at bottom]. + */ + if ((pi.array[0].letter && pi.inuseindx < (unsigned) height + && widest > (unsigned) width) + || (pi.inuseindx == 1 && otherborder && !border)) { + wmove(win, 1, x); + wprintw(win, "%.*s", width - 2 * border, ""); + xtra_line = 1; + } + for (lineno = 0; lineno < pi.rowoffset; ++lineno) if (pi.array[lineno].letter) ++item_count; @@ -341,14 +376,16 @@ curs_show_invt(WINDOW *win) for (lineno = pi.rowoffset; lineno < pi.inuseindx; ++lineno) { str = pi.array[lineno].invtxt; accelerator = pi.array[lineno].letter; - attr = pi.array[lineno].c_attr; - color = NO_COLOR; + attr = pi.array[lineno].c_attr; /* already converted when stored */ + color = pi.array[lineno].color; + if (color == NO_COLOR) + color = NONE; if (accelerator) ++item_count; /* Figure out where to draw the line */ - y = (int) (lineno - pi.rowoffset) + border; + y = (int) (lineno + xtra_line - pi.rowoffset) + border; if (y - border >= height) { /* height already -2 for Top+Btm border */ /* 'y' has grown too big; there are too many lines to fit */ continue; /* skip, but still loop to update 'item_count' */ @@ -379,18 +416,9 @@ curs_show_invt(WINDOW *win) stroffset = pi_article_skip(str); /* to skip "a "/"an "/"the " */ /* if/when scrolled right, invtxt for item lines gets shifted */ stroffset += pi.coloffset; - - /* only perform menu coloring on item entries, not subtitles */ - if (iflags.use_menu_color) { - attr = 0; - get_menu_coloring(str, &color, (int *) &attr); - attr = curses_convert_attr(attr); - } } if (stroffset < strlen(str)) { - if (color == NO_COLOR) - color = NONE; curses_menu_color_attr(win, color, attr, ON); wprintw(win, "%.*s", available_width, str + stroffset); curses_menu_color_attr(win, color, attr, OFF); @@ -400,10 +428,14 @@ curs_show_invt(WINDOW *win) } /* lineno loop */ if (pi.inuseindx > (unsigned) height) { - /* some lines aren't shown; overwrite rightmost portion of - last line with something like "[1-24 of 30>"; right justified - so that the line might still show something useful; could be on - line of its own, in which case we need to erase that first */ + /* + * More lines than will fit at one time so some lines aren't shown. + * Overwrite the rightmost portion of last line with something + * like "[1-24 of 30>" or "<7-30 of 30]" if we've already scrolled + * forward. Right justified so that the line might still show + * something useful. It could be on a line of its own, in which + * case we need to erase that first. + */ y = height - (1 - border); if ((unsigned) y == pi.inuseindx - pi.rowoffset) { wmove(win, y, x); @@ -416,8 +448,14 @@ curs_show_invt(WINDOW *win) mvwaddstr(win, y, x + (width - (int) strlen(tmpbuf)), tmpbuf); } if (widest > (unsigned) width) { - /* some columns aren't shown; overwrite rightmost portion of - first line with something like "[1-25 of 40}" */ + /* + * More columns than the persistent inventory window can fit so + * some columns aren't shown. Overwrite the rightmost portion of + * first line with something like "[1-25 of 40}" or "{16-40 of 40]". + * For the usual case, that line will be an object class header + * so we won't be obscuring an item, but that might not be the + * situation on page 2 and definitely won't be if 'sortpack' is Off. + */ Sprintf(tmpbuf, "%c%u-%u of %u%c", (left_col > 1) ? '{' : '[', left_col, right_col, widest, diff --git a/win/curses/cursmain.c b/win/curses/cursmain.c index 522fe053d2..586c28325d 100644 --- a/win/curses/cursmain.c +++ b/win/curses/cursmain.c @@ -22,14 +22,30 @@ extern char erase_char, kill_char; extern long curs_mesg_suppress_seq; /* from cursmesg.c */ extern boolean curs_mesg_no_suppress; /* ditto */ +extern int mesg_mixed; +extern glyph_info mesg_gi; + +#ifndef CURSES_GENL_PUTMIXED +#if defined(PDC_WIDE) || defined(NCURSES_WIDECHAR) +#define USE_CURSES_PUTMIXED +#else /* WIDE */ +#ifdef NH_PRAGMA_MESSAGE +#ifdef _MSC_VER +#pragma message ("Curses wide support not defined so NetHack curses message window functionality reduced") +#else +#pragma message "Curses wide support not defined so NetHack curses message window functionality reduced" +#endif /* _MSC_VER */ +#endif /* NH_PRAGMA_MESSAGE */ +#endif /* WIDE */ +#endif /* CURSES_GENL_PUTMIXED */ /* stubs for curses_procs{} */ #ifdef POSITIONBAR static void dummy_update_position_bar(char *); #endif #ifdef CHANGE_COLOR -static void dummy_change_color(int, long, int); -static char *dummy_get_color_string(void); +static void curses_change_color(int, long, int); +static char *curses_get_color_string(void); #endif /* Public functions for curses NetHack interface */ @@ -38,7 +54,7 @@ static char *dummy_get_color_string(void); struct window_procs curses_procs = { WPID(curses), (WC_ALIGN_MESSAGE | WC_ALIGN_STATUS | WC_COLOR | WC_INVERSE - | WC_HILITE_PET + | WC_HILITE_PET | WC_WINDOWCOLORS #ifdef NCURSES_MOUSE_VERSION /* (this macro name works for PDCURSES too) */ | WC_MOUSE_SUPPORT #endif @@ -47,6 +63,7 @@ struct window_procs curses_procs = { #ifdef CURSES_UNICODE | WC2_U_UTF8STR #endif + | WC2_EXTRACOLORS #ifdef SELECTSAVED | WC2_SELECTSAVED #endif @@ -70,7 +87,11 @@ struct window_procs curses_procs = { curses_destroy_nhwindow, curses_curs, curses_putstr, +#ifdef USE_CURSES_PUTMIXED + curses_putmixed, +#else genl_putmixed, +#endif curses_display_file, curses_start_menu, curses_add_menu, @@ -98,15 +119,13 @@ struct window_procs curses_procs = { curses_number_pad, curses_delay_output, #ifdef CHANGE_COLOR - dummy_change_color, + curses_change_color, #ifdef MAC /* old OS 9, not OSX */ (void (*)(int)) 0, (short (*)(winid, char *)) 0, #endif - dummy_get_color_string, + curses_get_color_string, #endif - curses_start_screen, - curses_end_screen, genl_outrip, curses_preference_update, curses_getmsghistory, @@ -129,6 +148,7 @@ int orig_cursor; /* Preserve initial cursor state */ WINDOW *base_term; /* underlying terminal window */ boolean counting; /* Count window is active */ WINDOW *mapwin, *statuswin, *messagewin; /* Main windows */ +color_attr curses_menu_promptstyle = { NO_COLOR, ATR_NONE }; /* Track if we're performing an update to the permanent window. Needed since we aren't using the normal menu functions to handle @@ -136,7 +156,7 @@ WINDOW *mapwin, *statuswin, *messagewin; /* Main windows */ static int inv_update = 0; /* -init_nhwindows(int* argcp, char** argv) +init_nhwindows(int *argcp, char **argv) -- Initialize the windows used by NetHack. This can also create the standard windows listed at the top, but does not display them. @@ -185,12 +205,17 @@ curses_init_nhwindows( #endif #endif + /* if anything has already been output by nethack (for instance, warnings + about RC file issues), let the player acknowlege it before initscr() + erases the screen */ + if (iflags.raw_printed) + curses_wait_synch(); + #ifdef XCURSES base_term = Xinitscr(*argcp, argv); #else base_term = initscr(); #endif -#ifdef TEXTCOLOR if (has_colors()) { start_color(); curses_init_nhcolors(); @@ -200,12 +225,6 @@ curses_init_nhwindows( iflags.wc2_guicolor = FALSE; set_wc2_option_mod_status(WC2_GUICOLOR, set_in_config); } -#else - iflags.use_color = FALSE; - set_option_mod_status("color", set_in_config); - iflags.wc2_guicolor = FALSE; - set_wc2_option_mod_status(WC2_GUICOLOR, set_in_config); -#endif noecho(); raw(); nonl(); /* don't force ^M into newline (^J); input accepts them both @@ -305,9 +324,9 @@ curses_askname(void) } #endif /* SELECTSAVED */ - curses_line_input_dialog("Who are you?", gp.plname, PL_NSIZ); - (void) mungspaces(gp.plname); - if (!gp.plname[0] || gp.plname[0] == '\033') + curses_line_input_dialog("Who are you?", svp.plname, PL_NSIZ); + (void) mungspaces(svp.plname); + if (!svp.plname[0] || svp.plname[0] == '\033') goto bail; iflags.renameallowed = TRUE; /* tty uses this, we don't [yet?] */ @@ -358,7 +377,7 @@ curses_uncurse_terminal(void) { /* also called by panictrace_handler(), a signal handler, so somewhat iffy in that situation; but without this, newlines behave as raw - line feeds so subseqent backtrace gets scrawled all over the screen + line feeds so subsequent backtrace gets scrawled all over the screen and is nearly useless */ curses_cleanup(); curs_set(orig_cursor); @@ -412,6 +431,12 @@ curses_create_nhwindow(int type) { winid wid = curses_get_wid(type); + if (curses_is_menu(wid)) + curses_parse_wid_colors(MENU_WIN, iflags.wcolors[wcolor_menu].fg, + iflags.wcolors[wcolor_menu].bg); + else if (curses_is_text(wid)) + curses_parse_wid_colors(TEXT_WIN, iflags.wcolors[wcolor_text].fg, + iflags.wcolors[wcolor_text].bg); if (curses_is_menu(wid) || curses_is_text(wid)) { curses_start_menu(wid, MENU_BEHAVE_STANDARD); curses_add_wid(wid); @@ -448,7 +473,10 @@ void curses_display_nhwindow(winid wid, boolean block) { menu_item *selected = NULL; + int border = curses_window_has_border(wid) ? 1 : 0; + if (wid == WIN_ERR) + return; if (curses_is_menu(wid) || curses_is_text(wid)) { curses_end_menu(wid, ""); (void) curses_select_menu(wid, PICK_NONE, &selected); @@ -459,8 +487,12 @@ curses_display_nhwindow(winid wid, boolean block) if (!iflags.window_inited && wid == MAP_WIN) { iflags.window_inited = TRUE; } else { + WINDOW *win = curses_get_nhwin(wid); + /* actually display the window */ - wnoutrefresh(curses_get_nhwin(wid)); + wnoutrefresh(win); + if (border) + box(win, 0, 0); /* flush pending writes from other windows too */ doupdate(); } @@ -564,6 +596,44 @@ curses_putstr(winid wid, int attr, const char *text) curs_mesg_no_suppress = FALSE; } +void +curses_putmixed(winid window, int attr, const char *str) +{ + const char *substr = 0; + char buf[BUFSZ]; + boolean done_output = FALSE; +#ifdef ENHANCED_SYMBOLS + int utf8flag = 0; +#endif + + if (window == WIN_MESSAGE) { + str = mixed_to_glyphinfo(str, &mesg_gi); + mesg_mixed = 1; + } else { + if ((substr = strstri(str, "\\G")) != 0) { +#ifdef ENHANCED_SYMBOLS + if ((windowprocs.wincap2 & WC2_U_UTF8STR) && SYMHANDLING(H_UTF8)) { + mixed_to_utf8(buf, sizeof buf, str, &utf8flag); + } else { +#endif + decode_mixed(buf, str); +#ifdef ENHANCED_SYMBOLS + } +#endif + /* now send buf to the normal putstr */ + curses_putstr(window, attr, buf); + done_output = TRUE; + } + } + + if (!done_output) { + /* just send str to the normal putstr */ + curses_putstr(window, attr, str); + } + if (window == WIN_MESSAGE) + mesg_mixed = 0; +} + /* Display the file named str. Complain about missing files iff complain is TRUE. */ @@ -625,25 +695,26 @@ void curses_add_menu(winid wid, const glyph_info *glyphinfo, const ANY_P *identifier, char accelerator, char group_accel, int attr, - int clr UNUSED, const char *str, unsigned itemflags) + int clr, const char *str, unsigned itemflags) { int curses_attr; attr &= ~(ATR_URGENT | ATR_NOHISTORY); curses_attr = curses_convert_attr(attr); + /* 'inv_update': 0 for normal menus, 1 and up for perminv window */ if (inv_update) { /* persistent inventory window; nothing is selectable; omit glyphinfo because perm_invent is to the side of - the map so usually cramped for space */ - curs_add_invt(inv_update, accelerator, curses_attr, str); + the map so usually cramped for horizontal space */ + curs_add_invt(inv_update, accelerator, curses_attr, clr, str); inv_update++; return; } curses_add_nhmenu_item(wid, glyphinfo, identifier, accelerator, group_accel, - curses_attr, str, itemflags); + curses_attr, clr, str, itemflags); } /* @@ -712,7 +783,7 @@ curses_update_inventory(int arg) } /* skip inventory updating during character initialization */ - if (!gp.program_state.in_moveloop && !gp.program_state.gameover) + if (!program_state.in_moveloop && !program_state.gameover) return; if (!arg) { @@ -741,10 +812,29 @@ curses_update_inventory(int arg) win_request_info * curses_ctrl_nhwindow( winid window UNUSED, - int request UNUSED, - win_request_info *wri UNUSED) + int request, + win_request_info *wri) { - return (win_request_info *) 0; + int attr; + + if (!wri) + return (win_request_info *) 0; + + switch (request) { + case set_mode: + case request_settings: + break; + case set_menu_promptstyle: + 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);; + break; + default: + break; + } + return wri; } /* @@ -754,7 +844,11 @@ mark_synch() -- Don't go beyond this point in I/O on any channel until void curses_mark_synch(void) { - curses_refresh_nethack_windows(); + /* full refresh has unintended side-effect of making a menu window + that has called core's get_count() to vanish; do a basic screen + refresh instead */ + /*curses_refresh_nethack_windows();*/ + refresh(); } /* @@ -766,9 +860,30 @@ wait_synch() -- Wait until all pending output is complete (*flush*() for void curses_wait_synch(void) { - if (curses_got_output()) - (void) curses_more(); - curses_mark_synch(); + if (iflags.raw_printed) { +#ifndef PDCURSES + int chr; + /* + * If any message has been issued via raw_print(), make the user + * acknowledge it. This might take place before initscr() so + * access to curses is limited. [Despite that, there's probably + * a more curses-specific way to handle this. FIXME?] + */ + + (void) fprintf(stdout, "\nPress to continue: "); + (void) fflush(stdout); + do { + chr = fgetc(stdin); + } while (chr > 0 && chr != C('j') && chr != C('m') && chr != '\033'); +#endif + iflags.raw_printed = 0; + } + + if (iflags.window_inited) { + if (curses_got_output()) + (void) curses_more(); + curses_mark_synch(); + } /* [do we need 'if (counting) curses_count_window((char *)0);' here?] */ } @@ -824,6 +939,7 @@ curses_print_glyph( int glyph; int ch; int color; + int nhcolor = 0; unsigned int special; int attr = -1; @@ -831,8 +947,24 @@ curses_print_glyph( special = glyphinfo->gm.glyphflags; ch = glyphinfo->ttychar; color = glyphinfo->gm.sym.color; + /* Extra color handling + * FIQ: The curses library does not support truecolor, only the more limited 256 + * color mode. On top of this, the windowport only supports 16 color mode. + * Thus, we only allow users to customize glyph colors to the basic NetHack + * colors. */ + if (glyphinfo->gm.customcolor != 0 + && (curses_procs.wincap2 & WC2_EXTRACOLORS) != 0) { + if ((glyphinfo->gm.customcolor & NH_BASIC_COLOR) != 0) { + color = COLORVAL(glyphinfo->gm.customcolor); +#if 0 + } else { + /* 24-bit color, NH_BASIC_COLOR == 0 */ + nhcolor = COLORVAL(glyphinfo->gm.customcolor); +#endif + } + } if ((special & MG_PET) && iflags.hilite_pet) { - attr = iflags.wc2_petattr; + attr = curses_convert_attr(iflags.wc2_petattr); } else if ((special & MG_PEACEFUL) && iflags.underline_peacefuls) { attr = A_UNDERLINE; @@ -851,13 +983,16 @@ curses_print_glyph( */ if ((special & MG_OBJPILE) && iflags.hilite_pile) { if (iflags.wc_color) - color = 16 + (color * 2) + 1; + color = get_framecolor(color, CLR_BLUE); else /* if (iflags.use_inverse) */ attr = A_REVERSE; } - /* water and lava look the same except for color; when color is off, - render lava in inverse video so that they look different */ - if ((special & (MG_BW_LAVA | MG_BW_ICE)) != 0 && iflags.use_inverse) { + /* water and lava look the same except for color; when color is off + (checked by core), render lava in inverse video so that it looks + different from water; similar for floor vs ice, fountain vs sink, + and corridor vs engranving-in-corridor */ + if ((special & (MG_BW_LAVA | MG_BW_ICE | MG_BW_SINK | MG_BW_ENGR)) + != 0 && iflags.use_inverse) { /* reset_glyphmap() only sets MG_BW_foo if color is off */ attr = A_REVERSE; } @@ -867,20 +1002,15 @@ curses_print_glyph( } } + curses_putch(wid, x, y, ch, #ifdef ENHANCED_SYMBOLS - if (SYMHANDLING(H_UTF8) - && glyphinfo->gm.u - && glyphinfo->gm.u->utf8str) { - curses_putch(wid, x, y, ch, glyphinfo->gm.u, color, - bkglyphinfo->framecolor, attr); - } else { - curses_putch(wid, x, y, ch, NULL, color, - bkglyphinfo->framecolor, attr); - } -#else - curses_putch(wid, x, y, ch, color, - bkglyphinfo->framecolor, attr); + (SYMHANDLING(H_UTF8) + && glyphinfo->gm.u && glyphinfo->gm.u->utf8str) + ? glyphinfo->gm.u : NULL, #endif + (nhcolor != 0) ? nhcolor : color, + bkglyphinfo->framecolor, attr); + } /* @@ -900,12 +1030,11 @@ curses_raw_print(const char *str) if (iflags.window_inited) { curses_message_win_puts(str, FALSE); - } else { - puts(str); + return; } -#else - puts(str); #endif + puts(str); + iflags.raw_printed++; } /* @@ -1082,27 +1211,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 @@ -1177,13 +1285,21 @@ dummy_update_position_bar(char *arg UNUSED) #ifdef CHANGE_COLOR static void -dummy_change_color(int a1 UNUSED, long a2 UNUSED, int a3 UNUSED) +curses_change_color(int color, long rgb, int reverse UNUSED) { - return; + short r, g, b; + + if (!can_change_color()) + return; + + r = (rgb >> 16) & 0xFF; + g = (rgb >> 8) & 0xFF; + b = rgb & 0xFF; + init_color(color % 16, r * 4, g * 4, b * 4); } static char * -dummy_get_color_string(void) +curses_get_color_string(void) { return (char *) 0; } diff --git a/win/curses/cursmesg.c b/win/curses/cursmesg.c index 1d944067ee..bce3f22ec8 100644 --- a/win/curses/cursmesg.c +++ b/win/curses/cursmesg.c @@ -7,7 +7,7 @@ #include "hack.h" #include "wincurs.h" #include "cursmesg.h" -#include +#include "curswins.h" /* defined in sys//tty.c or cursmain.c as last resort; set up by curses_init_nhwindows() */ @@ -26,10 +26,27 @@ long curs_mesg_suppress_seq = -1L; message triggers More>> for the previous message and the player responds with ESC; we need to avoid initiating suppression in that situation */ boolean curs_mesg_no_suppress = FALSE; +/* curses_putmixed() will place information in these next two */ +int mesg_mixed = 0; +glyph_info mesg_gi; + +#ifndef CURSES_GENL_PUTMIXED +#if defined(PDC_WIDE) || defined(NCURSES_WIDECHAR) +#define USE_CURSES_PUTMIXED +#else /* WIDE */ +#ifdef NH_PRAGMA_MESSAGE +#ifdef _MSC_VER +#pragma message("Curses wide support not defined so NetHack curses message window functionality reduced") +#else +#pragma message "Curses wide support not defined so NetHack curses message window functionality reduced" +#endif /* _MSC_VER */ +#endif /* NH_PRAGMA_MESSAGE */ +#endif /* WIDE */ +#endif /* CURSES_GENL_PUTMIXED */ /* Message window routines for curses interface */ -/* Private declatations */ +/* Private declarations */ typedef struct nhpm { char *str; /* Message text */ @@ -43,6 +60,9 @@ static void unscroll_window(winid wid); static void directional_scroll(winid wid, int nlines); static void mesg_add_line(const char *mline); static nhprev_mesg *get_msg_line(boolean reverse, int mindex); +#ifdef USE_CURSES_PUTMIXED +static int curscolor(int nhcolor, boolean *boldon); +#endif static int turn_lines = 0; static int mx = 0; @@ -61,8 +81,13 @@ curses_message_win_puts(const char *message, boolean recursed) int height, width, border_space, linespace; char *tmpstr; WINDOW *win = curses_get_nhwin(MESSAGE_WIN); - boolean bold, border = curses_window_has_border(MESSAGE_WIN); + boolean bold, border = curses_window_has_border(MESSAGE_WIN), + adjustbold = FALSE; int message_length = (int) strlen(message); +#ifdef USE_CURSES_PUTMIXED + boolean have_mixed_leadin = FALSE; + cchar_t mixed_leadin_cchar[2]; +#endif #if 0 /* @@ -81,6 +106,7 @@ curses_message_win_puts(const char *message, boolean recursed) return; /* user has typed ESC to avoid seeing remaining messages. */ } + curses_set_wid_colors(MESSAGE_WIN, NULL); curses_get_window_size(MESSAGE_WIN, &height, &width); border_space = (border ? 1 : 0); if (mx < border_space) @@ -110,6 +136,38 @@ curses_message_win_puts(const char *message, boolean recursed) /* -2: for leading " " (if combining this message with preceding one) */ if (mx > border_space) linespace -= 2; + bold = (height > 1 && !last_messages); + +#ifdef USE_CURSES_PUTMIXED + if (mesg_mixed) { + wchar_t w[2]; + int leadin_color; + + leadin_color = curscolor(mesg_gi.gm.sym.color, &adjustbold); + /* + * curses_putmixed() skipped past the \GNNNNNNNN encoding + * in the string, and filled in the mesg_gi glyphinfo. It + * flagged that to us by setting mesg_mixed. + */ + + w[0] = (wchar_t) mesg_gi.ttychar; +#ifdef ENHANCED_SYMBOLS + if ((windowprocs.wincap2 & WC2_U_UTF8STR) && SYMHANDLING(H_UTF8) + && mesg_gi.gm.u) { + /* FIXME: this won't work with all unicode values (32 bits -> 16 + * bits on Windows) */ + w[0] = (wchar_t) mesg_gi.gm.u->utf32ch; + } +#endif + w[1] = L'\0'; + if (setcchar(mixed_leadin_cchar, w, + (bold || adjustbold) ? A_BOLD : A_NORMAL, + leadin_color, 0) == OK) { + have_mixed_leadin = TRUE; + message_length++; /* account for that additional column */ + } + } +#endif /* USE_CURSES_PUTMIXED */ if (linespace < message_length) { if (my - border_space >= height - 1) { @@ -143,33 +201,80 @@ curses_message_win_puts(const char *message, boolean recursed) } } - bold = (height > 1 && !last_messages); - if (bold) + if (bold || adjustbold) curses_toggle_color_attr(win, NONE, A_BOLD, ON); /* will this message fit as-is or do we need to split it? */ if (mx == border_space && message_length > width - 3) { /* split needed */ tmpstr = curses_break_str(message, (width - 3), 1); +#ifdef USE_CURSES_PUTMIXED + if (have_mixed_leadin) { + mvwadd_wch(win, my, mx, mixed_leadin_cchar); + ++mx; + message_length--; + have_mixed_leadin = FALSE; + mesg_mixed = 0; + } +#endif mvwprintw(win, my, mx, "%s", tmpstr), mx += (int) strlen(tmpstr); /* one space to separate first part of message from rest [is this actually useful?] */ if (mx < width) ++mx; free(tmpstr); - if (bold) + if (bold || adjustbold) curses_toggle_color_attr(win, NONE, A_BOLD, OFF); tmpstr = curses_str_remainder(message, (width - 3), 1); curses_message_win_puts(tmpstr, TRUE); free(tmpstr); } else { +#ifdef USE_CURSES_PUTMIXED + if (have_mixed_leadin) { + mvwadd_wch(win, my, mx, mixed_leadin_cchar); + ++mx; + message_length--; + have_mixed_leadin = FALSE; + mesg_mixed = 0; + } +#endif mvwprintw(win, my, mx, "%s", message), mx += message_length; - if (bold) + if (bold || adjustbold) curses_toggle_color_attr(win, NONE, A_BOLD, OFF); } wrefresh(win); } +#ifdef USE_CURSES_PUTMIXED +static int +curscolor(int nhcolor, boolean *boldon) +{ + int curses_color; + + *boldon = FALSE; + if (nhcolor == 0) { /* make black fg visible */ +#ifdef USE_DARKGRAY + if (iflags.wc2_darkgray) { + if (COLORS > 16) { + /* colorpair for black is already darkgray */ + } else { /* Use bold for a bright black */ + *boldon = TRUE; + } + } else +#endif /* USE_DARKGRAY */ + nhcolor = CLR_BLUE; + } + curses_color = nhcolor + 1; + if (COLORS < 16) { + if (curses_color > 8 && curses_color < 17) + curses_color -= 8; + else if (curses_color > (17 + 16)) + curses_color -= 16; + } + return curses_color; +} +#endif + void curses_got_input(void) { @@ -220,6 +325,7 @@ curses_block( } moreattr = !iflags.wc2_guicolor ? (int) A_REVERSE : NONE; curses_toggle_color_attr(win, MORECOLOR, moreattr, ON); + curses_set_wid_colors(MESSAGE_WIN, NULL); if (blink) { wattron(win, A_BLINK); mvwprintw(win, my, mx, (iflags.msg_is_alert ? "")), mx += morewidth - 1; @@ -235,6 +341,7 @@ curses_block( } while (iflags.msg_is_alert && (ret = wgetch(win) != '\t')); curses_alert_main_borders(FALSE); + curses_set_wid_colors(MESSAGE_WIN, NULL); wrefresh(win); /* cancel mesg suppression; all messages will have had chance to be read */ @@ -280,11 +387,12 @@ curses_more(void) void curses_clear_unhighlight_message_window(void) { - int mh, mw, count, + int mh, mw, rx, ry, brdroffset = curses_window_has_border(MESSAGE_WIN) ? 1 : 0; WINDOW *win = curses_get_nhwin(MESSAGE_WIN); turn_lines = 0; + curses_set_wid_colors(MESSAGE_WIN, NULL); curses_get_window_size(MESSAGE_WIN, &mh, &mw); if (mh == 1) { @@ -293,9 +401,14 @@ curses_clear_unhighlight_message_window(void) } else { mx = mw + brdroffset; /* Force new line on new turn */ - for (count = 0; count < mh; count++) - mvwchgat(win, count + brdroffset, brdroffset, - mw, COLOR_PAIR(8), A_NORMAL, NULL); + for (ry = brdroffset; ry < mh; ry++) { + for (rx = brdroffset; rx < mw; rx++) { + chtype cht = mvwinch(win, ry, rx); + + mvwchgat(win, ry, rx, 1, A_NORMAL, PAIR_NUMBER(cht), NULL); + } + } + wnoutrefresh(win); } wmove(win, my, mx); @@ -313,6 +426,7 @@ curses_last_messages(void) int border = curses_window_has_border(MESSAGE_WIN) ? 1 : 0; WINDOW *win = curses_get_nhwin(MESSAGE_WIN); + curses_set_wid_colors(MESSAGE_WIN, NULL); curses_get_window_size(MESSAGE_WIN, &height, &width); werase(win); mx = my = border; @@ -359,8 +473,8 @@ curses_init_mesg_history(void) max_messages = 1; } - if (max_messages > MESG_HISTORY_MAX) { - max_messages = MESG_HISTORY_MAX; + if (max_messages > MAX_MSG_HISTORY) { + max_messages = MAX_MSG_HISTORY; } } @@ -394,7 +508,7 @@ curses_prev_mesg(void) boolean do_lifo = (iflags.prevmsg_window != 'f'); #ifdef DEBUG static int showturn = 0; /* 1: show hero_seq value in separators */ - int clr = 0; + int clr = NO_COLOR; /* * Set DEBUGFILES=MesgTurn in environment or sysconf to decorate @@ -505,6 +619,7 @@ curses_count_window(const char *count_text) but not for dolook's autodescribe when it refers to a named monster */ if (!countwin) countwin = newwin(1, messagew, winy, winx); + curses_set_wid_colors(MESSAGE_WIN, NULL); werase(countwin); mvwprintw(countwin, 0, 0, "%s", count_text); @@ -780,6 +895,7 @@ directional_scroll(winid wid, int nlines) boolean border = curses_window_has_border(wid); WINDOW *win = curses_get_nhwin(wid); + curses_set_wid_colors(wid, NULL); curses_get_window_size(wid, &wh, &ww); if (wh == 1) { curses_clear_nhwin(wid); @@ -853,8 +969,8 @@ mesg_add_line(const char *mline) ++num_messages; } else { /* at capacity; old head is being removed */ - first_mesg = first_mesg->next_mesg; /* new head */ - first_mesg->prev_mesg = NULL; /* head has no prev_mesg */ + if ((first_mesg = first_mesg->next_mesg) != 0) /* new head */ + first_mesg->prev_mesg = NULL; /* head has no prev_mesg */ } /* since 'current_mesg' might be reusing 'first_mesg' and has now become 'last_mesg', this update must be after head replacement */ @@ -950,7 +1066,7 @@ curses_putmsghistory(const char *msg, boolean restoring_msghist) stash_head = first_mesg, first_mesg = (nhprev_mesg *) 0; last_mesg = (nhprev_mesg *) 0; /* no need to remember the tail */ initd = TRUE; -#ifdef DUMPLOG +#ifdef DUMPLOG_CORE /* this suffices; there's no need to scrub g.saved_pline[] pointers */ gs.saved_pline_index = 0; #endif @@ -962,10 +1078,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 */ - last_mesg->turn = restoring_msghist ? (1L << 3) : gh.hero_seq; -#ifdef DUMPLOG - dumplogmsg(last_mesg->str); + if (last_mesg) { /* appease static analyzer */ + last_mesg->turn = restoring_msghist ? (1L << 3) : gh.hero_seq; +#ifdef DUMPLOG_CORE + dumplogmsg(last_mesg->str); #endif + } } else if (stash_count) { nhprev_mesg *mesg; long mesg_turn; @@ -978,18 +1096,20 @@ curses_putmsghistory(const char *msg, boolean restoring_msghist) stashed messages as newly occurring ones is much simpler; we ignore the backlinks because the list is destroyed as it gets processed hence there can't be any other traversals */ - mesg = stash_head; - stash_head = mesg->next_mesg; - --stash_count; - mesg_turn = mesg->turn; - mesg_add_line(mesg->str); - /* added line became new tail */ - last_mesg->turn = mesg_turn; -#ifdef DUMPLOG - dumplogmsg(mesg->str); + if ((mesg = stash_head) != 0) { + stash_head = mesg->next_mesg; + --stash_count; + mesg_turn = mesg->turn; + mesg_add_line(mesg->str); + /* added line became new tail */ + if (last_mesg) /* appease static analyzer */ + last_mesg->turn = mesg_turn; +#ifdef DUMPLOG_CORE + dumplogmsg(mesg->str); #endif - free((genericptr_t) mesg->str); - free((genericptr_t) mesg); + free((genericptr_t) mesg->str); + free((genericptr_t) mesg); + } } initd = FALSE; /* reset */ } @@ -1000,7 +1120,7 @@ curses_putmsghistory(const char *msg, boolean restoring_msghist) * right) brings up an initial display where the border around * the message window is missing. This draws it. */ - if (restoring_msghist) + if (restoring_msghist && !msg) curses_last_messages(); } diff --git a/win/curses/cursmisc.c b/win/curses/cursmisc.c index 42aa08eb32..96b541bdd4 100644 --- a/win/curses/cursmisc.c +++ b/win/curses/cursmisc.c @@ -10,12 +10,6 @@ #include "func_tab.h" #include "dlb.h" -#include - -#ifndef A_ITALIC -#define A_ITALIC A_UNDERLINE -#endif - /* Misc. curses interface functions */ /* Private declarations */ @@ -32,7 +26,9 @@ static boolean modifiers_available = FALSE; static int modified(int ch); static void update_modifiers(void); -static int parse_escape_sequence(boolean *); +static int parse_escape_sequence(int, boolean *); + +#define SS3 M(C('O')) /* 8-bit escape sequence initiator for VT number pad */ int curses_getch(void) @@ -76,15 +72,17 @@ curses_read_char(void) void curses_toggle_color_attr(WINDOW *win, int color, int attr, int onoff) { -#ifdef TEXTCOLOR + if (color == NO_COLOR) + color = NONE; + int curses_color; + boolean use_bold = FALSE; /* if color is disabled, just show attribute */ if ((win == mapwin) ? !iflags.wc_color /* statuswin is for #if STATUS_HILITES but doesn't need to be conditional */ : !(iflags.wc2_guicolor || win == statuswin)) { -#endif if (attr != NONE) { if (onoff == ON) wattron(win, attr); @@ -92,10 +90,9 @@ curses_toggle_color_attr(WINDOW *win, int color, int attr, int onoff) wattroff(win, attr); } return; -#ifdef TEXTCOLOR } - if (color == 0) { /* make black fg visible */ + if (color == CLR_BLACK) { /* make black fg visible */ # ifdef USE_DARKGRAY if (iflags.wc2_darkgray) { if (COLORS > 16) { @@ -107,17 +104,23 @@ curses_toggle_color_attr(WINDOW *win, int color, int attr, int onoff) # endif/* USE_DARKGRAY */ color = CLR_BLUE; } - curses_color = color + 1; + if (COLORS < 16) { - if (curses_color > 8 && curses_color < 17) - curses_color -= 8; - else if (curses_color > (17 + 16)) - curses_color -= 16; + /* convert NetHack's 16 colors to 8 colors + BOLD */ + int fg = color % 16; + int bg = color / 16; + + if (fg > 7) + use_bold = TRUE; + + curses_color = (8 * (bg % 8)) + (fg % 8) + 1; + } else { + curses_color = color + 1; } + if (onoff == ON) { /* Turn on color/attributes */ if (color != NONE) { - if ((((color > 7) && (color < 17)) || - (color > 17 + 17)) && (COLORS < 16)) { + if (use_bold) { wattron(win, A_BOLD); } wattron(win, COLOR_PAIR(curses_color)); @@ -129,7 +132,7 @@ curses_toggle_color_attr(WINDOW *win, int color, int attr, int onoff) } else { /* Turn off color/attributes */ if (color != NONE) { - if ((color > 7) && (COLORS < 16)) { + if (use_bold) { wattroff(win, A_BOLD); } # ifdef USE_DARKGRAY @@ -148,9 +151,6 @@ curses_toggle_color_attr(WINDOW *win, int color, int attr, int onoff) wattroff(win, attr); } } -#else - nhUse(color); -#endif /* TEXTCOLOR */ } /* call curses_toggle_color_attr() with 'menucolors' instead of 'guicolor' @@ -164,7 +164,8 @@ curses_menu_color_attr(WINDOW *win, int color, int attr, int onoff) /* curses_toggle_color_attr() uses 'guicolor' to decide whether to honor specified color, but menu windows have their own more-specific control, 'menucolors', so override with that here */ - iflags.wc2_guicolor = iflags.use_menu_color; +/* iflags.wc2_guicolor = iflags.use_menu_color; */ + iflags.wc2_guicolor = (color != NONE); curses_toggle_color_attr(win, color, attr, onoff); iflags.wc2_guicolor = save_guicolor; } @@ -288,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]; @@ -360,7 +361,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]; @@ -397,6 +398,7 @@ curses_str_remainder(const char *str, int width, int line_num) if (last_space == 0) { /* No spaces found */ last_space = count - 1; } + assert(IndexOk(last_space, substr)); if (substr[last_space] == '\0') { break; } @@ -650,7 +652,7 @@ curses_view_file(const char *filename, boolean must_exist) char buf[BUFSZ]; menu_item *selected = NULL; dlb *fp = dlb_fopen(filename, "r"); - int clr = 0; + int clr = NO_COLOR; if (fp == NULL) { if (must_exist) @@ -703,11 +705,11 @@ curses_get_count(int first_digit) curses's message window will display that in count window instead */ current_char = get_count(NULL, (char) first_digit, /* 0L => no limit on value unless it wraps - to negative */ + * to negative */ 0L, ¤t_count, - /* default: don't put into message history, - don't echo until second digit entered */ - GC_NOFLAGS); + /* don't put into message history, echo full + * number rather than waiting until 2nd digit */ + GC_ECHOFIRST); ungetch(current_char); if (current_char == '\033') { /* Cancelled with escape */ @@ -720,10 +722,10 @@ curses_get_count(int first_digit) /* Convert the given NetHack text attributes into the format curses understands, and return that format mask. */ -int +attr_t curses_convert_attr(int attr) { - int curses_attr; + attr_t curses_attr; /* first, strip off control flags masked onto the display attributes (caller should have already done this...) */ @@ -758,97 +760,13 @@ curses_convert_attr(int attr) return curses_attr; } - -/* Map letter attributes from a string to bitmask. Return mask on - success (might be 0), or -1 if not found. */ - -int -curses_read_attrs(const char *attrs) -{ - int retattr = 0; - - if (!attrs || !*attrs) - return A_NORMAL; - - if (strchr(attrs, 'b') || strchr(attrs, 'B')) - retattr |= A_BOLD; - if (strchr(attrs, 'i') || strchr(attrs, 'I')) /* inverse */ - retattr |= A_REVERSE; - if (strchr(attrs, 'u') || strchr(attrs, 'U')) - retattr |= A_UNDERLINE; - if (strchr(attrs, 'k') || strchr(attrs, 'K')) - retattr |= A_BLINK; - if (strchr(attrs, 'd') || strchr(attrs, 'D')) - retattr |= A_DIM; -#ifdef A_ITALIC - if (strchr(attrs, 't') || strchr(attrs, 'T')) - retattr |= A_ITALIC; -#endif -#ifdef A_LEFTLINE - if (strchr(attrs, 'l') || strchr(attrs, 'L')) - retattr |= A_LEFTLINE; -#endif -#ifdef A_RIGHTLINE - if (strchr(attrs, 'r') || strchr(attrs, 'R')) - retattr |= A_RIGHTLINE; -#endif - if (retattr == 0) { - /* still default; check for none/normal */ - if (strchr(attrs, 'n') || strchr(attrs, 'N')) - retattr = A_NORMAL; - else - retattr = -1; /* error */ - } - return retattr; -} - -/* format iflags.wc2_petattr into "+a+b..." for set bits a, b, ... - (used by core's 'O' command; return value points past leading '+') */ -char * -curses_fmt_attrs(char *outbuf) -{ - int attr = iflags.wc2_petattr; - - outbuf[0] = '\0'; - if (attr == A_NORMAL) { - Strcpy(outbuf, "+N(None)"); - } else { - if (attr & A_BOLD) - Strcat(outbuf, "+B(Bold)"); - if (attr & A_REVERSE) - Strcat(outbuf, "+I(Inverse)"); - if (attr & A_UNDERLINE) - Strcat(outbuf, "+U(Underline)"); - if (attr & A_BLINK) - Strcat(outbuf, "+K(blinK)"); - if (attr & A_DIM) - Strcat(outbuf, "+D(Dim)"); -#ifdef A_ITALIC - if (attr & A_ITALIC) - Strcat(outbuf, "+T(iTalic)"); -#endif -#ifdef A_LEFTLINE - if (attr & A_LEFTLINE) - Strcat(outbuf, "+L(Left line)"); -#endif -#ifdef A_RIGHTLINE - if (attr & A_RIGHTLINE) - Strcat(outbuf, "+R(Right line)"); -#endif - } - if (!*outbuf) - Sprintf(outbuf, "+unknown [%d]", attr); - return &outbuf[1]; -} - /* Convert special keys into values that NetHack can understand. Currently this is limited to arrow keys, but this may be expanded. */ - int curses_convert_keys(int key) { - boolean reject = (gp.program_state.input_state == otherInp), - as_is = FALSE, numpad_esc; + boolean reject = (program_state.input_state == otherInp), + as_is = FALSE, numpad_esc = FALSE; int ret = key; if (modifiers_available) @@ -857,11 +775,12 @@ curses_convert_keys(int key) /* Handle arrow and keypad keys, but only when getting a command (or a command-like keystroke for getpos() or getdir()). */ switch (key) { + case SS3: /* M-^O, 8-bit version of ESC 'O' c for keypad key */ case '\033': /* ESC or ^[ */ /* changes ESC c to M-c or number pad key to corresponding digit (but we only get here via key==ESC if curses' getch() didn't change the latter to KEY_xyz) */ - ret = parse_escape_sequence(&numpad_esc); + ret = parse_escape_sequence(key, &numpad_esc); reject = ((uchar) ret < 1 || ret > 255); as_is = !numpad_esc; /* don't perform phonepad inversion */ break; @@ -871,6 +790,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) @@ -1059,35 +979,62 @@ curses_mouse_support(int mode) /* 0: off, 1: on, 2: alternate on */ #endif } -/* caller just got an input character of ESC; +/* caller just got an input character of ESC or M-^O; note: curses converts a lot of escape sequences to single values greater than 255 and those won't look like ESC to caller so won't get here */ static int -parse_escape_sequence(boolean *keypadnum) +parse_escape_sequence(int key, boolean *keypadnum) { #ifndef PDCURSES - int ret; + int ret, keypadother = 0; *keypadnum = FALSE; timeout(10); ret = getch(); - if (ret == 'O') { /* Numeric keypad */ - /* ESC O */ - ret = getch(); + if (ret == 'O' || key == SS3) { /* handle numeric keypad */ + /* + * ESC O or M-^O . + * + * For the former, we don't have the next char yet so get it now. + * If there isn't one, treat ESC O as if user typed M-O (which + * is probably the case, via alt+shift+O combo sending two char + * "ESC O"). + * + * For the latter, it there wasn't another char then 'ret' will + * be ERR and we'll treat the result as M-^O. However, if there + * is another char and it is O meant as two characters "M-^O O" + * we'll be fooled, but that's not a valid escape sequence so + * don't worry about those two characters arriving together. + */ + if (key == '\033') + ret = getch(); if (ret == ERR) { - ret = 'O'; /* there was no third char; treat as M-O below */ + /* 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' */ *keypadnum = TRUE; /* convert 'p'..'y' to '0'..'9' below */ + } else if (ret >= 108 && ret <= 110) { /* 'l'..'n' */ + keypadother = 1; /* convert 'l','m','n' to ',','.','-' below */ + } else if (ret == 'M') { + keypadother = 2; /* convert "ESC O M" or "SS3 M" to ^M */ } } timeout(-1); /* reset to 'wait unlimited time for next input' */ if (*keypadnum) { - ret -= (112 - '0'); /* Convert c from 'ESC O c' to digit */ + /* 'p' -> '0', ..., 'y' -> '9' */ + ret -= ('p' - '0'); /* Convert c from 'ESC O c' to digit */ + } else if (keypadother > 0) { + /* conversion for VT keypad keys (no plus; ignore PF1 through PF4) + [typical PC keyboard has period and plus, no comma or minus] */ + if (keypadother == 1) + ret -= ('l' - ','); /* keypad comma, period, or minus */ + else + ret = C('M'); /* keypad */ } else if (ret != ERR && ret <= 255) { /* ESC ; effectively 'altmeta' behind player's back */ ret = M(ret); /* Meta key support for most terminals */ @@ -1097,10 +1044,14 @@ parse_escape_sequence(boolean *keypadnum) return ret; #else + nhUse(key); + nhUse(keypadnum); return '\033'; #endif /* !PDCURSES */ } +#undef SS3 + /* update_modifiers() and modified() will never be called if modifiers_available is FALSE */ diff --git a/win/curses/cursmisc.h b/win/curses/cursmisc.h index 459decb4ac..8d5becb28a 100644 --- a/win/curses/cursmisc.h +++ b/win/curses/cursmisc.h @@ -28,9 +28,7 @@ void curses_posthousekeeping(void); void curses_view_file(const char *filename, boolean must_exist); void curses_rtrim(char *str); long curses_get_count(int first_digit); -int curses_convert_attr(int attr); -int curses_read_attrs(const char *attrs); -char *curses_fmt_attrs(char *); +attr_t curses_convert_attr(int attr); int curses_convert_keys(int key); int curses_get_mouse(coordxy *mousex, coordxy *mousey, int *mod); void curses_mouse_support(int); diff --git a/win/curses/cursstat.c b/win/curses/cursstat.c index fffcffc95b..90a9d93c90 100644 --- a/win/curses/cursstat.c +++ b/win/curses/cursstat.c @@ -27,7 +27,7 @@ static char *status_vals_long[MAXBLSTATS]; static unsigned long *curses_colormasks; static long curses_condition_bits; static int curses_status_colors[MAXBLSTATS]; -static int hpbar_percent, hpbar_color; +static int hpbar_percent, hpbar_crit_hp, hpbar_color; static int vert_status_dirty; static void draw_status(void); @@ -37,9 +37,7 @@ static void curs_HPbar(char *, int); static void curs_stat_conds(int, int, int *, int *, char *, boolean *); static void curs_vert_status_vals(int); #ifdef STATUS_HILITES -#ifdef TEXTCOLOR static int condcolor(long, unsigned long *); -#endif static int condattr(long, unsigned long *); static int nhattr2curses(int); #endif /* STATUS_HILITES */ @@ -59,7 +57,7 @@ curses_status_init(void) *status_vals_long[i] = '\0'; } curses_condition_bits = 0L; - hpbar_percent = 0, hpbar_color = NO_COLOR; + hpbar_percent = hpbar_crit_hp = 0, hpbar_color = NO_COLOR; vert_status_dirty = 1; /* let genl_status_init do most of the initialization */ @@ -152,7 +150,7 @@ curses_status_update( if (fldidx != BL_FLUSH) { if (fldidx < 0 || fldidx >= MAXBLSTATS) { - gc.context.botlx = gc.context.botl = FALSE; /* avoid bot() */ + /* panic immediately sets gb.bot_disabled to avoid bot() */ panic("curses_status_update(%d)", fldidx); } changed_fields |= (1 << fldidx); @@ -163,9 +161,6 @@ curses_status_update( curses_condition_bits = *condptr; curses_colormasks = colormasks; } else { -#ifndef TEXTCOLOR - color_and_attr = (color_and_attr & ~0x00FF) | NO_COLOR; -#endif /* * status_vals[] are used for horizontal orientation * (wide lines of multiple short values). @@ -185,8 +180,8 @@ curses_status_update( } else { Sprintf(status_vals[fldidx], (fldidx == BL_TITLE && iflags.wc2_hitpointbar) - ? "%-30s" : status_fieldfmt[fldidx] - ? status_fieldfmt[fldidx] : "%s", + ? "%-30.30s" : status_fieldfmt[fldidx] + ? status_fieldfmt[fldidx] : "%s", text); /* strip trailing spaces; core ought to do this for us */ if (fldidx == BL_HUNGER || fldidx == BL_LEVELDESC) @@ -200,7 +195,9 @@ curses_status_update( curses_status_colors[fldidx] = color_and_attr; if (iflags.wc2_hitpointbar && fldidx == BL_HP) { hpbar_percent = percent; - hpbar_color = color_and_attr; + hpbar_crit_hp = critically_low_hp(TRUE) ? 1 : 0; + hpbar_color = ((color_and_attr & 0x00ff) | (HL_INVERSE << 8) + | (hpbar_crit_hp ? (HL_BLINK << 8) : 0)); } } } else { /* BL_FLUSH */ @@ -256,12 +253,12 @@ draw_horizontal(boolean border) /* almost all fields already come with a leading space; "xspace" indicates places where we'll generate an extra one */ static const enum statusfields - twolineorder[3][15] = { + twolineorder[3][16] = { { BL_TITLE, /*xspace*/ BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, /*xspace*/ BL_ALIGN, /*xspace*/ BL_SCORE, - BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD }, + BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }, { BL_LEVELDESC, /*xspace*/ BL_GOLD, /*xspace*/ BL_HP, BL_HPMAX, @@ -269,16 +266,16 @@ draw_horizontal(boolean border) /*xspace*/ BL_AC, /*xspace*/ BL_XP, BL_EXP, BL_HD, /*xspace*/ BL_TIME, - /*xspace*/ BL_HUNGER, BL_CAP, BL_CONDITION, + /*xspace*/ BL_HUNGER, BL_CAP, BL_CONDITION, BL_VERS, BL_FLUSH }, { BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, - blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD } + blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD } }, - threelineorder[3][15] = { /* moves align to line 2, leveldesc+ to 3 */ + threelineorder[3][16] = { /* moves align to line 2, leveldesc+ to 3 */ { BL_TITLE, /*xspace*/ BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, /*xspace*/ BL_SCORE, - BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }, + BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }, { BL_ALIGN, /*xspace*/ BL_GOLD, /*xspace*/ BL_HP, BL_HPMAX, @@ -286,14 +283,15 @@ draw_horizontal(boolean border) /*xspace*/ BL_AC, /*xspace*/ BL_XP, BL_EXP, BL_HD, /*xspace*/ BL_HUNGER, BL_CAP, - BL_FLUSH, blPAD, blPAD }, + BL_FLUSH, blPAD, blPAD, blPAD }, { BL_LEVELDESC, /*xspace*/ BL_TIME, /*xspecial*/ BL_CONDITION, + /*xspecial*/ BL_VERS, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD } }; - const enum statusfields (*fieldorder)[15]; + const enum statusfields (*fieldorder)[16]; coordxy spacing[MAXBLSTATS], valline[MAXBLSTATS]; enum statusfields fld, prev_fld; char *text, *p, cbuf[BUFSZ], ebuf[STATVAL_WIDTH]; @@ -302,11 +300,11 @@ draw_horizontal(boolean border) char sbuf[STATVAL_WIDTH]; #endif int i, j, number_of_lines, - cap_and_hunger, exp_points, sho_score, + cap_and_hunger, exp_points, sho_score, sho_vers, /* both height and width get their values set, * but only width gets used in this function */ - height UNUSED, width, w, xtra, clen, x, y, t, ex, ey, - condstart = 0, conddummy = 0; + height, width, w, xtra, clen, x, y, t, ex, ey, + condstart = 0, conddummy = 0, versstart = 0; #ifdef STATUS_HILITES int coloridx = NO_COLOR, attrmask = 0; #endif /* STATUS_HILITES */ @@ -362,6 +360,10 @@ draw_horizontal(boolean border) exp_points = (flags.showexp ? 1 : 0); /* don't bother conditionalizing this; always 0 for !SCORE_ON_BOTL */ sho_score = (status_activefields[BL_SCORE] != 0); + sho_vers = (status_activefields[BL_VERS] != 0); + versstart = sho_vers ? (width - (int) strlen(status_vals[BL_VERS]) + + (border ? 1 : 0)) + : 0; /* simplify testing which fields reside on which lines; assume line #0 */ (void) memset((genericptr_t) valline, 0, sizeof valline); @@ -413,6 +415,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: @@ -428,6 +431,7 @@ draw_horizontal(boolean border) text = cbuf; /* for 'w += strlen(text)' below */ spacing[fld] = (cap_and_hunger == 0); break; + case BL_VERS: case BL_STR: case BL_HP: case BL_ENE: @@ -578,6 +582,11 @@ draw_horizontal(boolean border) } else if (fld != BL_CONDITION) { /* regular field, including title if no hitpointbar */ + if (fld == BL_VERS) { + getyx(win, y, x); + if (x < versstart) + wmove(win, y, versstart); /* right justify */ + } #ifdef STATUS_HILITES coloridx = curses_status_colors[fld]; /* includes attribute */ if (iflags.hilite_delta && coloridx != NO_COLOR) { @@ -591,11 +600,9 @@ draw_horizontal(boolean border) attrmask = nhattr2curses(attrmask); wattron(win, attrmask); } -#ifdef TEXTCOLOR coloridx &= 0x00FF; if (coloridx != NO_COLOR && coloridx != CLR_MAX) curses_toggle_color_attr(win, coloridx, NONE, ON); -#endif } #endif /* STATUS_HILITES */ @@ -603,10 +610,8 @@ draw_horizontal(boolean border) #ifdef STATUS_HILITES if (iflags.hilite_delta) { -#ifdef TEXTCOLOR if (coloridx != NO_COLOR) curses_toggle_color_attr(win, coloridx, NONE, OFF); -#endif if (attrmask) wattroff(win, attrmask); } @@ -624,6 +629,14 @@ draw_horizontal(boolean border) y = j + (border ? 1 : 0); /* cbuf[] was populated above; clen is its length */ if (number_of_lines == 3) { + int vlen = (sho_vers + && fieldorder[j][i + 1] == BL_VERS) + ? ((int) strlen(status_vals[BL_VERS]) + + spacing[BL_VERS]) + : 0; + + clen += vlen; /* when aligning conditions, treat + * version as if an added condition */ /* * For 3-line status, align conditions with hunger * (or where it would have been, when not shown), @@ -641,6 +654,7 @@ draw_horizontal(boolean border) else wmove(win, y, width + (border ? 1 : 0) - clen); } + clen -= vlen; } /* 'asis' was set up by first curs_stat_conds() call above; True means that none of the conditions @@ -661,6 +675,7 @@ draw_horizontal(boolean border) } /* i (fld) */ wclrtoeol(win); /* [superfluous? draw_status() calls werase()] */ } /* j (line) */ + nhUse(height); return; } @@ -672,31 +687,34 @@ draw_vertical(boolean border) removed if we need to shrink to fit within height limit (very rare) */ static const enum statusfields fieldorder[] = { BL_TITLE, /* might be overlaid by hitpoint bar */ - /* 4:blank */ + /* 5:blank */ BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX, BL_AC, - /* 3:blank */ + /* 4:blank */ BL_LEVELDESC, BL_ALIGN, BL_XP, BL_EXP, BL_HD, BL_GOLD, - /* 2:blank (but only if time or score or both enabled) */ + /* 3:blank (but only if time or score or both enabled) */ BL_TIME, BL_SCORE, - /* 1:blank */ + /* 2:blank */ BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, - /* 5:blank (if any of hunger, encumbrance, or conditions appear) */ + /* 6:blank (if any of hunger, encumbrance, or conditions appear) */ BL_HUNGER, BL_CAP, /* these two are shown on same line */ BL_CONDITION, /* shown three per line so may take up to four lines */ + /* 1:blank (bottom justified) */ + BL_VERS, BL_FLUSH }; static const enum statusfields shrinkorder[] = { - BL_STR, BL_SCORE, BL_TIME, BL_LEVELDESC, BL_HP, + BL_VERS, BL_STR, BL_SCORE, BL_TIME, BL_LEVELDESC, BL_HP, BL_CONDITION, BL_CAP, BL_HUNGER }; coordxy spacing[MAXBLSTATS]; - int i, fld, cap_and_hunger, time_and_score, cond_count, per_line; + int i, fld, cap_and_hunger, time_and_score, cond_count, + sho_vers, per_line; char *text; #ifdef STATUS_HILITES char *p = 0, savedch = '\0'; @@ -738,6 +756,7 @@ draw_vertical(boolean border) ++cond_count; } per_line = 2; /* will be changed to 3 if status becomes too tall */ + sho_vers = (status_activefields[BL_VERS] ? 1 : 0); /* count how many lines we'll need; we normally space several groups of fields with blank lines but might need to compress some of those out */ @@ -762,7 +781,7 @@ draw_vertical(boolean border) /* unlike hunger+cap, score is shown on separate line from time; needs time+score separator if time is inactive */ spacing[fld] = (time_and_score == 2) ? 2 - : (time_and_score & 1) ? 1 : 0; + : (time_and_score == 3) ? 1 : 0; break; case BL_HUNGER: /* separated from characteristics unless blank */ @@ -782,6 +801,9 @@ draw_vertical(boolean border) if (cond_count > per_line) height_needed += (cond_count - 1) / per_line; break; + case BL_VERS: + spacing[fld] = sho_vers ? 2 : 0; + break; case BL_XP: case BL_HD: default: @@ -794,9 +816,9 @@ draw_vertical(boolean border) if (height_needed > height) { /* if there are a lot of status conditions, compress them first */ if (per_line == 2 && cond_count > per_line) { - height_needed -= (cond_count - 1) / per_line; - per_line = 3; - height_needed += (cond_count - 1) / per_line; + height_needed -= (cond_count - 1) / per_line; + per_line = 3; + height_needed += (cond_count - 1) / per_line; } if (height_needed > height) { for (i = 0; i < SIZE(shrinkorder); ++i) { @@ -811,7 +833,7 @@ draw_vertical(boolean border) #ifdef SCORE_ON_BOTL /* with all optional fields and every status condition (12 out of the 13 since two are mutually exclusive) active, we need - 21 non-blank lines; curses_create_main_windows() used to + 22 non-blank lines; curses_create_main_windows() used to require 24 lines or more in order to enable vertical status, but that has been relaxed to 20 so height_needed might still be too high after suppressing all the blank lines */ @@ -819,11 +841,18 @@ draw_vertical(boolean border) height_needed -= spacing[BL_SCORE]; spacing[BL_SCORE] = 0; time_and_score &= ~2; - /* height_needed isn't used beyond here but we keep it accurate */ - nhUse(height_needed); } #endif + } else if (height_needed < height) { + if (sho_vers) { + /* bottom justify 'version' */ + spacing[BL_VERS] += height - height_needed; /* 2 + (h - h') */ + height_needed = height; + } } + /* height_needed isn't used beyond here but was updated (for BL_SCORE + or BL_VERS) to keep it accurate in case that changes someday */ + nhUse(height_needed); if (border) x++, y++; @@ -837,9 +866,8 @@ draw_vertical(boolean border) continue; if (spacing[fld]) { - wmove(win, y++, x); /* move to next line */ - if (spacing[fld] == 2) - wmove(win, y++, x); /* skip a line */ + y += spacing[fld]; + wmove(win, y - 1, x); /* move to next (or further) line */ } if (fld == BL_TITLE && iflags.wc2_hitpointbar) { @@ -888,11 +916,9 @@ draw_vertical(boolean border) attrmask = nhattr2curses(attrmask); wattron(win, attrmask); } -#ifdef TEXTCOLOR coloridx &= 0x00FF; if (coloridx != NO_COLOR && coloridx != CLR_MAX) curses_toggle_color_attr(win, coloridx, NONE, ON); -#endif } /* highlighting active */ #endif /* STATUS_HILITES */ @@ -900,10 +926,8 @@ draw_vertical(boolean border) #ifdef STATUS_HILITES if (iflags.hilite_delta) { -#ifdef TEXTCOLOR if (coloridx != NO_COLOR) curses_toggle_color_attr(win, coloridx, NONE, OFF); -#endif if (attrmask) wattroff(win, attrmask); } /* resume normal rendition */ @@ -916,7 +940,7 @@ draw_vertical(boolean border) } else { /* status conditions */ if (cond_count) { - /* output active conditions; usually two per line, but + /* output active conditions; usually two per line, but if window isn't tall enough, it's increased to three per line; cursor is already positioned where they should start */ curs_stat_conds(1, per_line, &x, &y, @@ -929,13 +953,12 @@ draw_vertical(boolean border) /* hitpointbar using hp percent calculation */ static void -curs_HPbar(char *text, /* pre-padded with trailing spaces if short */ - int bar_len) /* width of space within the brackets */ +curs_HPbar( + char *text, /* pre-padded with trailing spaces if short */ + int bar_len) /* width of space within the brackets */ { #ifdef STATUS_HILITES -#ifdef TEXTCOLOR int coloridx = 0; -#endif #endif /* STATUS_HILITES */ int k, bar_pos; char bar[STATVAL_WIDTH], *bar2 = (char *) 0, savedch = '\0'; @@ -948,6 +971,8 @@ curs_HPbar(char *text, /* pre-padded with trailing spaces if short */ bar_len = k; (void) strncpy(bar, text, bar_len); bar[bar_len] = '\0'; + if (hpbar_crit_hp) + repad_with_dashes(bar); bar_pos = (bar_len * hpbar_percent) / 100; if (bar_pos < 1 && hpbar_percent > 0) @@ -961,29 +986,27 @@ curs_HPbar(char *text, /* pre-padded with trailing spaces if short */ } waddch(win, '['); + if (hpbar_crit_hp) + wattron(win, A_BLINK); if (*bar) { /* True unless dead (0 HP => bar_pos == 0) */ /* fixed attribute, not nhattr2curses((hpbar_color >> 8) & 0x00FF) */ wattron(win, A_REVERSE); /* do this even if hilite_delta is 0 */ #ifdef STATUS_HILITES -#ifdef TEXTCOLOR if (iflags.hilite_delta) { coloridx = hpbar_color & 0x00FF; if (coloridx != NO_COLOR) curses_toggle_color_attr(win, coloridx, NONE, ON); } -#endif #endif /* STATUS_HILITES */ /* portion of title corresponding to current hit points */ waddstr(win, bar); #ifdef STATUS_HILITES -#ifdef TEXTCOLOR if (iflags.hilite_delta) { if (coloridx != NO_COLOR) curses_toggle_color_attr(win, coloridx, NONE, OFF); } -#endif #endif /* STATUS_HILITES */ wattroff(win, A_REVERSE); /* do this even if hilite_delta is 0 */ } /* *bar (current HP > 0) */ @@ -993,6 +1016,8 @@ curs_HPbar(char *text, /* pre-padded with trailing spaces if short */ *bar2 = savedch; waddstr(win, bar2); } + if (hpbar_crit_hp) + wattroff(win, A_BLINK); waddch(win, ']'); } @@ -1006,7 +1031,7 @@ DISABLE_WARNING_FORMAT_NONLITERAL static void curs_stat_conds( int vert_cond, /* 0 => horizontal, 1 => vertical */ - int per_line, /* for vertical number of conditions per line */ + int per_line, /* for vertical, number of conditions per line */ int *x, int *y, /* real for vertical, ignored otherwise */ char *condbuf, /* optional output; collect string of conds */ boolean *nohilite) /* optional output; indicates whether -*/ @@ -1088,11 +1113,9 @@ curs_stat_conds( attrmask = nhattr2curses(attrmask); wattron(win, attrmask); } -#ifdef TEXTCOLOR if ((color = condcolor(bitmsk, curses_colormasks)) != NO_COLOR) curses_toggle_color_attr(win, color, NONE, ON); -#endif } #endif /* STATUS_HILITES */ @@ -1101,16 +1124,14 @@ curs_stat_conds( #ifdef STATUS_HILITES if (iflags.hilite_delta) { -#ifdef TEXTCOLOR if (color != NO_COLOR) curses_toggle_color_attr(win, color, NONE, OFF); -#endif if (attrmask) wattroff(win, attrmask); } #endif /* STATUS_HILITES */ /* if that was #3 of 3 advance to next line */ - if (do_vert && (++vert_cond % per_line) == 1) + if (do_vert && cond_bits && (++vert_cond % per_line) == 1) wmove(win, (*y)++, *x); } /* if cond_bits & bitmask */ } /* for i */ @@ -1211,7 +1232,9 @@ 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: case BL_HUNGER: case BL_CAP: @@ -1225,8 +1248,19 @@ curs_vert_status_vals(int win_width) Sprintf(status_vals_long[fldidx], "%*.*s: %s%s", -lbl_width, lbl_width, lbl, leadingspace, text); *status_vals_long[fldidx] = highc(*status_vals_long[fldidx]); + } else if (fldidx == BL_VERS && *text) { + int txtlen = (int) strlen(text); + + /* right justify without "Version :" prefix; if longer than + width, keep only the *end* of the value */ + if (txtlen >= win_width) + Strcpy(status_vals_long[BL_VERS], + eos((char *) text) - win_width); + else + Sprintf(status_vals_long[BL_VERS], + "%*s%s", win_width - txtlen, " ", text); } else if ((fldidx == BL_HUNGER || fldidx == BL_CAP) && *text) { - /* hunger and enbumbrance are shown side-by-side in + /* hunger and encumbrance are shown side-by-side in a 26 character or wider window; if leading space is present, get rid of it, then add one we're sure about */ if (*text == ' ') @@ -1271,7 +1305,6 @@ curs_vert_status_vals(int win_width) } #ifdef STATUS_HILITES -#ifdef TEXTCOLOR /* * Return what color this condition should * be displayed in based on user settings. @@ -1288,7 +1321,6 @@ condcolor(long bm, unsigned long *bmarray) } return NO_COLOR; } -#endif /* TEXTCOLOR */ static int condattr(long bm, unsigned long *bmarray) @@ -1296,26 +1328,27 @@ condattr(long bm, unsigned long *bmarray) int i, attr = 0; if (bm && bmarray) { - for (i = HL_ATTCLR_DIM; i < BL_ATTCLR_MAX; ++i) { + for (i = HL_ATTCLR_BOLD; i < BL_ATTCLR_MAX; ++i) { if ((bmarray[i] & bm) != 0) { - switch (i) { + switch(i) { + case HL_ATTCLR_BOLD: + attr |= HL_BOLD; + break; case HL_ATTCLR_DIM: attr |= HL_DIM; break; - case HL_ATTCLR_BLINK: - attr |= HL_BLINK; + case HL_ATTCLR_ITALIC: + attr |= HL_ITALIC; break; case HL_ATTCLR_ULINE: attr |= HL_ULINE; break; + case HL_ATTCLR_BLINK: + attr |= HL_BLINK; + break; case HL_ATTCLR_INVERSE: attr |= HL_INVERSE; break; - case HL_ATTCLR_BOLD: - attr |= HL_BOLD; - break; - default: - break; } } } @@ -1331,963 +1364,19 @@ nhattr2curses(int attrmask) if (attrmask & HL_BOLD) result |= A_BOLD; - if (attrmask & HL_INVERSE) - result |= A_REVERSE; + if (attrmask & HL_DIM) + result |= A_DIM; + if (attrmask & HL_ITALIC) + result |= A_ITALIC; if (attrmask & HL_ULINE) result |= A_UNDERLINE; if (attrmask & HL_BLINK) result |= A_BLINK; - if (attrmask & HL_DIM) - result |= A_DIM; + if (attrmask & HL_INVERSE) + result |= A_REVERSE; return result; } #endif /* STATUS_HILITES */ -/* ======================================================================== */ - - -#if 0 /* old stuff; some may be re-incorporated, most should be discarded */ -/* Private declarations */ - -/* Used to track previous value of things, to highlight changes. */ -typedef struct nhs { - long value; - int highlight_turns; - int highlight_color; -} nhstat; - -static attr_t get_trouble_color(const char *); -static void draw_trouble_str(const char *); -static void print_statdiff(const char *append, nhstat *, long, int); -static void get_playerrank(char *); -static int hpen_color(boolean, int, int); -static void draw_bar(boolean, int, int, const char *); -static void draw_horizontal(int, int, int, int); -static void draw_horizontal_new(int, int, int, int); -static void draw_vertical(int, int, int, int); -static void curses_add_statuses(WINDOW *, boolean, boolean, int *, int *); -static void curses_add_status(WINDOW *, boolean, boolean, int *, int *, - const char *, int); -static int decrement_highlight(nhstat *, boolean); - -#ifdef STATUS_COLORS -static attr_t hpen_color_attr(boolean, int, int); -extern struct color_option text_color_of(const char *text, - const struct text_color_option *color_options); -struct color_option percentage_color_of(int value, int max, - const struct percent_color_option *color_options); - -extern const struct text_color_option *text_colors; -extern const struct percent_color_option *hp_colors; -extern const struct percent_color_option *pw_colors; -#endif - -/* Whether or not we have printed status window content at least once. - Used to ensure that prev* doesn't end up highlighted on game start. */ -static boolean first = TRUE; -static nhstat prevdepth; -static nhstat prevstr; -static nhstat prevint; -static nhstat prevwis; -static nhstat prevdex; -static nhstat prevcon; -static nhstat prevcha; -static nhstat prevau; -static nhstat prevlevel; -static nhstat prevac; -static nhstat prevexp; -static nhstat prevtime; - -#ifdef SCORE_ON_BOTL -static nhstat prevscore; -#endif - -extern const char *const hu_stat[]; /* from eat.c */ -extern const char *const enc_stat[]; /* from botl.c */ - -/* If the statuscolors patch isn't enabled, have some default colors for status problems - anyway */ - -struct statcolor { - const char *txt; /* For status problems */ - int color; /* Default color assuming STATUS_COLORS isn't enabled */ -}; - -static const struct statcolor default_colors[] = { - {"Satiated", CLR_YELLOW}, - {"Hungry", CLR_YELLOW}, - {"Weak", CLR_ORANGE}, - {"Fainted", CLR_BRIGHT_MAGENTA}, - {"Fainting", CLR_BRIGHT_MAGENTA}, - {"Burdened", CLR_RED}, - {"Stressed", CLR_RED}, - {"Strained", CLR_ORANGE}, - {"Overtaxed", CLR_ORANGE}, - {"Overloaded", CLR_BRIGHT_MAGENTA}, - {"Conf", CLR_BRIGHT_BLUE}, - {"Blind", CLR_BRIGHT_BLUE}, - {"Stun", CLR_BRIGHT_BLUE}, - {"Hallu", CLR_BRIGHT_BLUE}, - {"Ill", CLR_BRIGHT_MAGENTA}, - {"FoodPois", CLR_BRIGHT_MAGENTA}, - {"Slime", CLR_BRIGHT_MAGENTA}, - {NULL, NULL, NO_COLOR}, -}; - -static attr_t -get_trouble_color(const char *stat) -{ - attr_t res = curses_color_attr(CLR_GRAY, 0); - const struct statcolor *clr; - for (clr = default_colors; clr->txt; clr++) { - if (stat && !strcmp(clr->txt, stat)) { -#ifdef STATUS_COLORS - /* Check if we have a color enabled with statuscolors */ - if (!iflags.use_status_colors) - return curses_color_attr(CLR_GRAY, 0); /* no color configured */ - - struct color_option stat_color; - - stat_color = text_color_of(clr->txt, text_colors); - if (stat_color.color == NO_COLOR && !stat_color.attr_bits) - return curses_color_attr(CLR_GRAY, 0); - - if (stat_color.color != NO_COLOR) - res = curses_color_attr(stat_color.color, 0); - - res = curses_color_attr(stat_color.color, 0); - int count; - for (count = 0; (1 << count) <= stat_color.attr_bits; count++) { - if (count != ATR_NONE && - (stat_color.attr_bits & (1 << count))) - res |= curses_convert_attr(count); - } - - return res; -#else - return curses_color_attr(clr->color, 0); -#endif - } - } - - return res; -} - -/* TODO: This is in the wrong place. */ -void -get_playerrank(char *rank) -{ - char buf[BUFSZ]; - if (Upolyd) { - int k = 0; - - Strcpy(buf, pmname(&mons[u.umonnum], flags.female ? FEMALE : MALE)); - while(buf[k] != 0) { - if ((k == 0 || (k > 0 && buf[k-1] == ' ')) && - 'a' <= buf[k] && buf[k] <= 'z') - buf[k] += 'A' - 'a'; - k++; - } - Strcpy(rank, buf); - } else - Strcpy(rank, rank_of(u.ulevel, Role_switch, flags.female)); -} - -/* Handles numerical stat changes of various kinds. - type is generally STAT_OTHER (generic "do nothing special"), - but is used if the stat needs to be handled in a special way. */ -static void -print_statdiff(const char *append, nhstat *stat, long new, int type) -{ - char buf[BUFSZ]; - WINDOW *win = curses_get_nhwin(STATUS_WIN); - - int color = CLR_GRAY; - - /* Turncount isn't highlighted, or it would be highlighted constantly. */ - if (type != STAT_TIME && new != stat->value) { - /* Less AC is better */ - if ((type == STAT_AC && new < stat->value) || - (type != STAT_AC && new > stat->value)) { - color = STAT_UP_COLOR; - if (type == STAT_GOLD) - color = HI_GOLD; - } else - color = STAT_DOWN_COLOR; - - stat->value = new; - stat->highlight_color = color; - stat->highlight_turns = 5; - } else if (stat->highlight_turns) - color = stat->highlight_color; - - attr_t attr = curses_color_attr(color, 0); - wattron(win, attr); - wprintw(win, "%s", append); - if (type == STAT_STR && new > 18) { - if (new > 118) - wprintw(win, "%d", new - 100); - else if (new == 118) - wprintw(win, "18/**"); - else - wprintw(win, "18/%02d", new - 18); - } else - wprintw(win, "%d", new); - - wattroff(win, attr); -} - -static void -draw_trouble_str(const char *str) -{ - WINDOW *win = curses_get_nhwin(STATUS_WIN); - - attr_t attr = get_trouble_color(str); - wattron(win, attr); - wprintw(win, "%s", str); - wattroff(win, attr); -} - -/* Returns a ncurses attribute for foreground and background. - This should probably be in cursinit.c or something. */ -attr_t -curses_color_attr(int nh_color, int bg_color) -{ - int color = nh_color + 1; - attr_t cattr = A_NORMAL; - - if (!nh_color) { -#ifdef USE_DARKGRAY - if (iflags.wc2_darkgray) { - if (COLORS <= 16) - cattr |= A_BOLD; - } else -#endif - color = COLOR_BLUE; - } - - if (COLORS < 16 && color > 8) { - color -= 8; - cattr = A_BOLD; - } - - /* Can we do background colors? We can if we have more than - 16*7 colors (more than 8*7 for terminals with bold) */ - if (COLOR_PAIRS > (COLORS >= 16 ? 16 : 8) * 7) { - /* NH3 has a rather overcomplicated way of defining - its colors past the first 16: - Pair Foreground Background - 17 Black Red - 18 Black Blue - 19 Red Red - 20 Red Blue - 21 Green Red - ... - (Foreground order: Black, Red, Green, Yellow, Blue, - Magenta, Cyan, Gray/White) - - To work around these oddities, we define backgrounds - by the following pairs: - - 16 COLORS - 49-64: Green - 65-80: Yellow - 81-96: Magenta - 97-112: Cyan - 113-128: Gray/White - - 8 COLORS - 9-16: Green - 33-40: Yellow - 41-48: Magenta - 49-56: Cyan - 57-64: Gray/White */ - - if (bg_color == nh_color) - color = 1; /* Make foreground black if fg==bg */ - - if (bg_color == CLR_RED || bg_color == CLR_BLUE) { - /* already defined before extension */ - color *= 2; - color += 16; - if (bg_color == CLR_RED) - color--; - } else { - boolean hicolor = FALSE; - if (COLORS >= 16) - hicolor = TRUE; - - switch (bg_color) { - case CLR_GREEN: - color = (hicolor ? 48 : 8) + color; - break; - case CLR_BROWN: - color = (hicolor ? 64 : 32) + color; - break; - case CLR_MAGENTA: - color = (hicolor ? 80 : 40) + color; - break; - case CLR_CYAN: - color = (hicolor ? 96 : 48) + color; - break; - case CLR_GRAY: - color = (hicolor ? 112 : 56) + color; - break; - default: - break; - } - } - } - cattr |= COLOR_PAIR(color); - - return cattr; -} - -/* Returns a complete curses attribute. - Used to possibly bold/underline/etc HP/Pw. */ -#ifdef STATUS_COLORS -static attr_t -hpen_color_attr(boolean is_hp, int cur, int max) -{ - struct color_option stat_color; - int count; - attr_t attr = 0; - if (!iflags.use_status_colors) - return curses_color_attr(CLR_GRAY, 0); - - stat_color = percentage_color_of(cur, max, is_hp ? hp_colors : pw_colors); - - if (stat_color.color != NO_COLOR) - attr |= curses_color_attr(stat_color.color, 0); - - for (count = 0; (1 << count) <= stat_color.attr_bits; count++) { - if (count != ATR_NONE && (stat_color.attr_bits & (1 << count))) - attr |= curses_convert_attr(count); - } - - return attr; -} -#endif - -/* Return color for the HP bar. - With status colors ON, this respect its configuration (defaulting to gray), - but only obeys the color (no weird attributes for the HP bar). - With status colors OFF, this returns reasonable defaults which are also - used for the HP/Pw text itself. */ -static int -hpen_color(boolean is_hp, int cur, int max) -{ -#ifdef STATUS_COLORS - if (iflags.use_status_colors) { - struct color_option stat_color; - stat_color = percentage_color_of(cur, max, - is_hp ? hp_colors : pw_colors); - - if (stat_color.color == NO_COLOR) - return CLR_GRAY; - else - return stat_color.color; - } else - return CLR_GRAY; -#endif - - int color = CLR_GRAY; - if (cur == max) - color = CLR_GRAY; - else if (cur * 3 > max * 2) /* >2/3 */ - color = is_hp ? CLR_GREEN : CLR_CYAN; - else if (cur * 3 > max) /* >1/3 */ - color = is_hp ? CLR_YELLOW : CLR_BLUE; - else if (cur * 7 > max) /* >1/7 */ - color = is_hp ? CLR_RED : CLR_MAGENTA; - else - color = is_hp ? CLR_ORANGE : CLR_BRIGHT_MAGENTA; - - return color; -} - -/* Draws a bar - is_hp: TRUE if we're drawing HP, Pw otherwise (determines colors) - cur/max: Current/max HP/Pw - title: Not NULL if we are drawing as part of an existing title. - Otherwise, the format is as follows: [ 11 / 11 ] */ -static void -draw_bar(boolean is_hp, int cur, int max, const char *title) -{ - WINDOW *win = curses_get_nhwin(STATUS_WIN); - -#ifdef STATUS_COLORS - if (!iflags.hitpointbar) { - wprintw(win, "%s", !title ? "---" : title); - return; - } -#endif - - char buf[BUFSZ]; - if (title) - Strcpy(buf, title); - else { - int len = 5; - sprintf(buf, "%*d / %-*d", len, cur, len, max); - } - - /* Colors */ - attr_t fillattr, attr; - int color = hpen_color(is_hp, cur, max); - int invcolor = color & 7; - - fillattr = curses_color_attr(color, invcolor); - attr = curses_color_attr(color, 0); - - /* Figure out how much of the bar to fill */ - int fill = 0; - int len = strlen(buf); - if (cur > 0 && max > 0) - fill = len * cur / max; - if (fill > len) - fill = len; - - waddch(win, '['); - wattron(win, fillattr); - wprintw(win, "%.*s", fill, buf); - wattroff(win, fillattr); - wattron(win, attr); - wprintw(win, "%.*s", len - fill, &buf[fill]); - wattroff(win, attr); - waddch(win, ']'); -} - -/* Update the status win - this is called when NetHack would normally - write to the status window, so we know somwthing has changed. We - override the write and update what needs to be updated ourselves. */ -void -curses_update_stats(void) -{ - WINDOW *win = curses_get_nhwin(STATUS_WIN); - orient statorient = (orient) curses_get_window_orientation(STATUS_WIN); - boolean horiz = (statorient != ALIGN_RIGHT && statorient != ALIGN_LEFT); - boolean border = curses_window_has_border(STATUS_WIN); - - /* Clear the window */ - werase(win); - - /* Figure out if we have proper window dimensions for horizontal status */ - if (horiz) { - /* correct y */ - int cy = 3; - if (iflags.statuslines < 3) - cy = 2; - - /* actual y (and x) */ - int ax = 0; - int ay = 0; - getmaxyx(win, ay, ax); - if (border) - ay -= 2; - - if (cy != ay) { - curses_create_main_windows(); - curses_last_messages(); - docrt(); - - /* Reset XP highlight (since classic_status and new show - different numbers) */ - prevexp.highlight_turns = 0; - curses_update_stats(); - return; - } - } - - /* Starting x/y. Passed to draw_horizontal/draw_vertical to keep track of - window positioning. */ - int x = 0; - int y = 0; - - /* Don't start at border position if applicable */ - if (border) { - x++; - y++; - } - - /* Get HP values. */ - int hp = u.uhp; - int hpmax = u.uhpmax; - if (Upolyd) { - hp = u.mh; - hpmax = u.mhmax; - } - - if (horiz) - draw_horizontal(x, y, hp, hpmax); - else - draw_vertical(x, y, hp, hpmax); - - if (border) - box(win, 0, 0); - - wnoutrefresh(win); - - if (first) { - first = FALSE; - - /* Zero highlight timers. This will call curses_update_status again - if needed */ - curses_decrement_highlights(TRUE); - } -} - -static void -draw_horizontal(int x, int y, int hp, int hpmax) -{ - if (iflags.statuslines >= 3) { - /* Draw new-style statusbar */ - draw_horizontal_new(x, y, hp, hpmax); - return; - } - char buf[BUFSZ]; - char rank[BUFSZ]; - WINDOW *win = curses_get_nhwin(STATUS_WIN); - - /* Line 1 */ - wmove(win, y, x); - - get_playerrank(rank); - sprintf(buf, "%s the %s", gp.plname, rank); - - /* Use the title as HP bar (similar to hitpointbar) */ - draw_bar(TRUE, hp, hpmax, buf); - - /* Attributes */ - print_statdiff(" St:", &prevstr, ACURR(A_STR), STAT_STR); - print_statdiff(" Dx:", &prevdex, ACURR(A_DEX), STAT_OTHER); - print_statdiff(" Co:", &prevcon, ACURR(A_CON), STAT_OTHER); - print_statdiff(" In:", &prevint, ACURR(A_INT), STAT_OTHER); - print_statdiff(" Wi:", &prevwis, ACURR(A_WIS), STAT_OTHER); - print_statdiff(" Ch:", &prevcha, ACURR(A_CHA), STAT_OTHER); - - wprintw(win, (u.ualign.type == A_CHAOTIC ? " Chaotic" : - u.ualign.type == A_NEUTRAL ? " Neutral" : " Lawful")); - -#ifdef SCORE_ON_BOTL - if (flags.showscore) - print_statdiff(" S:", &prevscore, botl_score(), STAT_OTHER); -#endif /* SCORE_ON_BOTL */ - - - /* Line 2 */ - y++; - wmove(win, y, x); - - describe_level(buf, 0); - - wprintw(win, "%s", buf); - - print_statdiff("$", &prevau, money_cnt(gi.invent), STAT_GOLD); - - /* HP/Pw use special coloring rules */ - attr_t hpattr, pwattr; -#ifdef STATUS_COLORS - hpattr = hpen_color_attr(TRUE, hp, hpmax); - pwattr = hpen_color_attr(FALSE, u.uen, u.uenmax); -#else - int hpcolor, pwcolor; - hpcolor = hpen_color(TRUE, hp, hpmax); - pwcolor = hpen_color(FALSE, u.uen, u.uenmax); - hpattr = curses_color_attr(hpcolor, 0); - pwattr = curses_color_attr(pwcolor, 0); -#endif - wprintw(win, " HP:"); - wattron(win, hpattr); - wprintw(win, "%d(%d)", (hp < 0) ? 0 : hp, hpmax); - wattroff(win, hpattr); - - wprintw(win, " Pw:"); - wattron(win, pwattr); - wprintw(win, "%d(%d)", u.uen, u.uenmax); - wattroff(win, pwattr); - - print_statdiff(" AC:", &prevac, u.uac, STAT_AC); - - if (Upolyd) - print_statdiff(" HD:", &prevlevel, mons[u.umonnum].mlevel, STAT_OTHER); - else if (flags.showexp) { - print_statdiff(" Xp:", &prevlevel, u.ulevel, STAT_OTHER); - /* use waddch, we don't want to highlight the '/' */ - waddch(win, '/'); - print_statdiff("", &prevexp, u.uexp, STAT_OTHER); - } - else - print_statdiff(" Exp:", &prevlevel, u.ulevel, STAT_OTHER); - - if (flags.time) - print_statdiff(" T:", &prevtime, gm.moves, STAT_TIME); - - curses_add_statuses(win, FALSE, FALSE, NULL, NULL); -} - -static void -draw_horizontal_new(int x, int y, int hp, int hpmax) -{ - char buf[BUFSZ]; - char rank[BUFSZ]; - WINDOW *win = curses_get_nhwin(STATUS_WIN); - - /* Line 1 */ - wmove(win, y, x); - - get_playerrank(rank); - char race[BUFSZ]; - Strcpy(race, gu.urace.adj); - race[0] = highc(race[0]); - wprintw(win, "%s the %s %s%s%s", gp.plname, - (u.ualign.type == A_CHAOTIC ? "Chaotic" : - u.ualign.type == A_NEUTRAL ? "Neutral" : "Lawful"), - Upolyd ? "" : race, Upolyd ? "" : " ", - rank); - - /* Line 2 */ - y++; - wmove(win, y, x); - wprintw(win, "HP:"); - draw_bar(TRUE, hp, hpmax, NULL); - print_statdiff(" AC:", &prevac, u.uac, STAT_AC); - if (Upolyd) - print_statdiff(" HD:", &prevlevel, mons[u.umonnum].mlevel, STAT_OTHER); - else if (flags.showexp) { - /* Ensure that Xp have proper highlight on level change. */ - int levelchange = 0; - if (prevlevel.value != u.ulevel) { - if (prevlevel.value < u.ulevel) - levelchange = 1; - else - levelchange = 2; - } - print_statdiff(" Xp:", &prevlevel, u.ulevel, STAT_OTHER); - /* use waddch, we don't want to highlight the '/' */ - waddch(win, '('); - - /* Figure out amount of Xp needed to next level */ - int xp_left = 0; - if (u.ulevel < 30) - xp_left = (newuexp(u.ulevel) - u.uexp); - - if (levelchange) { - prevexp.value = (xp_left + 1); - if (levelchange == 2) - prevexp.value = (xp_left - 1); - } - print_statdiff("", &prevexp, xp_left, STAT_AC); - waddch(win, ')'); - } - else - print_statdiff(" Exp:", &prevlevel, u.ulevel, STAT_OTHER); - - waddch(win, ' '); - describe_level(buf); - - wprintw(win, "%s", buf); - - /* Line 3 */ - y++; - wmove(win, y, x); - wprintw(win, "Pw:"); - draw_bar(FALSE, u.uen, u.uenmax, NULL); - - print_statdiff(" $", &prevau, money_cnt(gi.invent), STAT_GOLD); - -#ifdef SCORE_ON_BOTL - if (flags.showscore) - print_statdiff(" S:", &prevscore, botl_score(), STAT_OTHER); -#endif /* SCORE_ON_BOTL */ - - if (flags.time) - print_statdiff(" T:", &prevtime, gm.moves, STAT_TIME); - - curses_add_statuses(win, TRUE, FALSE, &x, &y); - - /* Right-aligned attributes */ - int stat_length = 6; /* " Dx:xx" */ - int str_length = 6; - if (ACURR(A_STR) > 18 && ACURR(A_STR) < 119) - str_length = 9; - - getmaxyx(win, y, x); - - /* We want to deal with top line of y. getmaxx would do what we want, but it only - exist for compatibility reasons and might not exist at all in some versions. */ - y = 0; - if (curses_window_has_border(STATUS_WIN)) { - x--; - y++; - } - - x -= stat_length; - int orig_x = x; - wmove(win, y, x); - print_statdiff(" Co:", &prevcon, ACURR(A_CON), STAT_OTHER); - x -= stat_length; - wmove(win, y, x); - print_statdiff(" Dx:", &prevdex, ACURR(A_DEX), STAT_OTHER); - x -= str_length; - wmove(win, y, x); - print_statdiff(" St:", &prevstr, ACURR(A_STR), STAT_STR); - - x = orig_x; - y++; - wmove(win, y, x); - print_statdiff(" Ch:", &prevcha, ACURR(A_CHA), STAT_OTHER); - x -= stat_length; - wmove(win, y, x); - print_statdiff(" Wi:", &prevwis, ACURR(A_WIS), STAT_OTHER); - x -= str_length; - wmove(win, y, x); - print_statdiff(" In:", &prevint, ACURR(A_INT), STAT_OTHER); -} - -/* Personally I never understood the point of a vertical status bar. But removing the - option would be silly, so keep the functionality. */ -static void -draw_vertical(int x, int y, int hp, int hpmax) -{ - char buf[BUFSZ]; - char rank[BUFSZ]; - WINDOW *win = curses_get_nhwin(STATUS_WIN); - - /* Print title and dungeon branch */ - wmove(win, y++, x); - - get_playerrank(rank); - int ranklen = strlen(rank); - int namelen = strlen(gp.plname); - int maxlen = 19; -#ifdef STATUS_COLORS - if (!iflags.hitpointbar) - maxlen += 2; /* With no hitpointbar, we can fit more since there's no "[]" */ -#endif - - if ((ranklen + namelen) > maxlen) { - /* The result doesn't fit. Strip name if >10 characters, then strip title */ - if (namelen > 10) { - while (namelen > 10 && (ranklen + namelen) > maxlen) - namelen--; - } - - while ((ranklen + namelen) > maxlen) - ranklen--; /* Still doesn't fit, strip rank */ - } - sprintf(buf, "%-*s the %-*s", namelen, gp.plname, ranklen, rank); - draw_bar(TRUE, hp, hpmax, buf); - wmove(win, y++, x); - wprintw(win, "%s", dungeons[u.uz.dnum].dname); - - y++; /* Blank line inbetween */ - wmove(win, y++, x); - - /* Attributes. Old vertical order is preserved */ - print_statdiff("Strength: ", &prevstr, ACURR(A_STR), STAT_STR); - wmove(win, y++, x); - print_statdiff("Intelligence: ", &prevint, ACURR(A_INT), STAT_OTHER); - wmove(win, y++, x); - print_statdiff("Wisdom: ", &prevwis, ACURR(A_WIS), STAT_OTHER); - wmove(win, y++, x); - print_statdiff("Dexterity: ", &prevdex, ACURR(A_DEX), STAT_OTHER); - wmove(win, y++, x); - print_statdiff("Constitution: ", &prevcon, ACURR(A_CON), STAT_OTHER); - wmove(win, y++, x); - print_statdiff("Charisma: ", &prevcha, ACURR(A_CHA), STAT_OTHER); - wmove(win, y++, x); - wprintw(win, "Alignment: "); - wprintw(win, (u.ualign.type == A_CHAOTIC ? "Chaotic" : - u.ualign.type == A_NEUTRAL ? "Neutral" : "Lawful")); - wmove(win, y++, x); - wprintw(win, "Dungeon Level: "); - - /* Astral Plane doesn't fit */ - if (In_endgame(&u.uz)) - wprintw(win, "%s", Is_astralevel(&u.uz) ? "Astral" : "End Game"); - else - wprintw(win, "%d", depth(&u.uz)); - wmove(win, y++, x); - - print_statdiff("Gold: ", &prevau, money_cnt(gi.invent), STAT_GOLD); - wmove(win, y++, x); - - /* HP/Pw use special coloring rules */ - attr_t hpattr, pwattr; -#ifdef STATUS_COLORS - hpattr = hpen_color_attr(TRUE, hp, hpmax); - pwattr = hpen_color_attr(FALSE, u.uen, u.uenmax); -#else - int hpcolor, pwcolor; - hpcolor = hpen_color(TRUE, hp, hpmax); - pwcolor = hpen_color(FALSE, u.uen, u.uenmax); - hpattr = curses_color_attr(hpcolor, 0); - pwattr = curses_color_attr(pwcolor, 0); -#endif - - wprintw(win, "Hit Points: "); - wattron(win, hpattr); - wprintw(win, "%d/%d", (hp < 0) ? 0 : hp, hpmax); - wattroff(win, hpattr); - wmove(win, y++, x); - - wprintw(win, "Magic Power: "); - wattron(win, pwattr); - wprintw(win, "%d/%d", u.uen, u.uenmax); - wattroff(win, pwattr); - wmove(win, y++, x); - - print_statdiff("Armor Class: ", &prevac, u.uac, STAT_AC); - wmove(win, y++, x); - - if (Upolyd) - print_statdiff("Hit Dice: ", &prevlevel, mons[u.umonnum].mlevel, - STAT_OTHER); - else if (flags.showexp) { - print_statdiff("Experience: ", &prevlevel, u.ulevel, STAT_OTHER); - /* use waddch, we don't want to highlight the '/' */ - waddch(win, '/'); - print_statdiff("", &prevexp, u.uexp, STAT_OTHER); - } - else - print_statdiff("Level: ", &prevlevel, u.ulevel, STAT_OTHER); - wmove(win, y++, x); - - if (flags.time) { - print_statdiff("Time: ", &prevtime, gm.moves, STAT_TIME); - wmove(win, y++, x); - } - -#ifdef SCORE_ON_BOTL - if (flags.showscore) { - print_statdiff("Score: ", &prevscore, botl_score(), - STAT_OTHER); - wmove(win, y++, x); - } -#endif /* SCORE_ON_BOTL */ - - curses_add_statuses(win, FALSE, TRUE, &x, &y); -} - -static void -curses_add_statuses(WINDOW *win, boolean align_right, - boolean vertical, int *x, int *y) -{ - if (align_right) { - /* Right-aligned statuses. Since add_status decrease one x more - (to separate them with spaces), add 1 to x unless we have borders - (which would offset what add_status does) */ - int mx = *x; - int my = *y; - getmaxyx(win, my, mx); - if (!curses_window_has_border(STATUS_WIN)) - mx++; - - *x = mx; - } - -#define statprob(str, trouble) \ - curses_add_status(win, align_right, vertical, x, y, str, trouble) - - /* Hunger */ - statprob(hu_stat[u.uhs], u.uhs != 1); /* 1 is NOT_HUNGRY (not defined here) */ - - /* General troubles */ - statprob("Conf", Confusion); - statprob("Blind", Blind); - statprob("Stun", Stunned); - statprob("Hallu", Hallucination); - statprob("Ill", (u.usick_type & SICK_NONVOMITABLE)); - statprob("FoodPois", (u.usick_type & SICK_VOMITABLE)); - statprob("Slime", Slimed); - - /* Encumbrance */ - int enc = near_capacity(); - statprob(enc_stat[enc], enc > UNENCUMBERED); -#undef statprob -} - -static void -curses_add_status(WINDOW *win, boolean align_right, boolean vertical, - int *x, int *y, const char *str, int trouble) -{ - /* If vertical is TRUE here with no x/y, that's an error. But handle - it gracefully since NH3 doesn't recover well in crashes. */ - if (!x || !y) - vertical = FALSE; - - if (!trouble) - return; - - if (!vertical && !align_right) - waddch(win, ' '); - - /* For whatever reason, hunger states have trailing spaces. Get rid of - them. */ - char buf[BUFSZ]; - Strcpy(buf, str); - int i; - for (i = 0; (buf[i] != ' ' && buf[i] != '\0'); i++) ; - - buf[i] = '\0'; - if (align_right) { - *x -= (strlen(buf) + 1); /* add spacing */ - wmove(win, *y, *x); - } - - draw_trouble_str(buf); - - if (vertical) { - wmove(win, *y, *x); - *y += 1; /* ++ advances the pointer addr */ - } -} - -/* Decrement a single highlight, return 1 if decremented to zero. zero is TRUE if we're - zeroing the highlight. */ -static int -decrement_highlight(nhstat *stat, boolean zero) -{ - if (stat->highlight_turns > 0) { - if (zero) { - stat->highlight_turns = 0; - return 1; - } - - stat->highlight_turns--; - if (stat->highlight_turns == 0) - return 1; - } - return 0; -} - -/* Decrement the highlight_turns for all stats. Call curses_update_stats - if needed to unhighlight a stat */ -void -curses_decrement_highlights(boolean zero) -{ - int unhighlight = 0; - - unhighlight |= decrement_highlight(&prevdepth, zero); - unhighlight |= decrement_highlight(&prevstr, zero); - unhighlight |= decrement_highlight(&prevdex, zero); - unhighlight |= decrement_highlight(&prevcon, zero); - unhighlight |= decrement_highlight(&prevint, zero); - unhighlight |= decrement_highlight(&prevwis, zero); - unhighlight |= decrement_highlight(&prevcha, zero); - unhighlight |= decrement_highlight(&prevau, zero); - unhighlight |= decrement_highlight(&prevlevel, zero); - unhighlight |= decrement_highlight(&prevac, zero); - unhighlight |= decrement_highlight(&prevexp, zero); - unhighlight |= decrement_highlight(&prevtime, zero); -#ifdef SCORE_ON_BOTL - unhighlight |= decrement_highlight(&prevscore, zero); -#endif - - if (unhighlight) - curses_update_stats(); -} -#endif /*0*/ - /*cursstat.c*/ diff --git a/win/curses/curswins.c b/win/curses/curswins.c index 95dcda4d60..19c6c380af 100644 --- a/win/curses/curswins.c +++ b/win/curses/curswins.c @@ -9,7 +9,10 @@ #include "curses.h" #include "hack.h" #include "wincurs.h" +#include "cursinit.h" +#include "cursmisc.h" #include "curswins.h" +#include "cursstat.h" /* Window handling for curses interface */ @@ -21,8 +24,11 @@ typedef struct nhw { int width; /* Usable width not counting border */ int height; /* Usable height not counting border */ int x; /* start of window on terminal (left) */ - int y; /* start of window on termial (top) */ + int y; /* start of window on terminal (top) */ int orientation; /* Placement of window relative to map */ + boolean clr_inited; /* fg/bg/colorpair inited? */ + int fg, bg; /* foreground, background color index */ + int colorpair; /* color pair of fg, bg */ boolean border; /* Whether window has a visible border */ } nethack_window; @@ -52,7 +58,7 @@ static void clear_map(void); /* Create a window with the specified size and orientation */ WINDOW * -curses_create_window(int width, int height, orient orientation) +curses_create_window(int wid, int width, int height, orient orientation) { int mapx = 0, mapy = 0, maph = 0, mapw = 0; int startx = 0; @@ -63,7 +69,7 @@ curses_create_window(int width, int height, orient orientation) if ((orientation == UP) || (orientation == DOWN) || (orientation == LEFT) || (orientation == RIGHT)) { - if (gi.invent || (gm.moves > 1)) { + if (svm.moves > 0) { map_border = curses_window_has_border(MAP_WIN); curses_get_window_xy(MAP_WIN, &mapx, &mapy); curses_get_window_size(MAP_WIN, &maph, &mapw); @@ -92,13 +98,14 @@ curses_create_window(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); starty = (term_rows / 2) - (height / 2); break; case UP: - if (gi.invent || (gm.moves > 1)) { + if (svm.moves > 0) { startx = (mapw / 2) - (width / 2) + mapx + mapb_offset; } else { startx = 0; @@ -107,7 +114,7 @@ curses_create_window(int width, int height, orient orientation) starty = mapy + mapb_offset; break; case DOWN: - if (gi.invent || (gm.moves > 1)) { + if (svm.moves > 0) { startx = (mapw / 2) - (width / 2) + mapx + mapb_offset; } else { startx = 0; @@ -123,7 +130,7 @@ curses_create_window(int width, int height, orient orientation) starty = term_rows - height; break; case RIGHT: - if (gi.invent || (gm.moves > 1)) { + if (svm.moves > 0) { startx = (mapw + mapx + (mapb_offset * 2)) - width; } else { startx = term_cols - width; @@ -142,24 +149,83 @@ curses_create_window(int width, int height, orient orientation) } win = newwin(height, width, starty, startx); - curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, ON); + + if (curses_is_text(wid)) + wid = TEXT_WIN; + else if (curses_is_menu(wid)) + wid = MENU_WIN; + + if (nhwins[wid].clr_inited < 1) + curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, ON); box(win, 0, 0); - curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, OFF); + if (nhwins[wid].clr_inited < 1) + curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, OFF); return win; } +int +curses_win_clr_inited(int wid) +{ + if (curses_is_text(wid)) { + wid = TEXT_WIN; + } else if (curses_is_menu(wid)) { + wid = MENU_WIN; + } + return nhwins[wid].clr_inited; +} + +void +curses_set_wid_colors(int wid, WINDOW *win) +{ + if (wid == TEXT_WIN || curses_is_text(wid)) { + wid = TEXT_WIN; + if (!nhwins[wid].clr_inited) + curses_parse_wid_colors(wid, iflags.wcolors[wcolor_text].fg, + iflags.wcolors[wcolor_text].bg); + } else if (wid == MENU_WIN || curses_is_menu(wid)) { + wid = MENU_WIN; + if (!nhwins[wid].clr_inited) + curses_parse_wid_colors(wid, iflags.wcolors[wcolor_menu].fg, + iflags.wcolors[wcolor_menu].bg); + } + /* FIXME: colors and nhwins[] entry for perm invent window */ + if (nhwins[wid].clr_inited > 0) { + wbkgd(win ? win : nhwins[wid].curwin, + COLOR_PAIR(nhwins[wid].colorpair)); + } +} /* Erase and delete curses window, and refresh standard windows */ void curses_destroy_win(WINDOW *win) { - werase(win); - wrefresh(win); + int mapwidth = 0, winwidth, dummyht; + + /* + * In case map is narrower than the space alloted for it, if we + * are destroying a popup window and it is wider than the map, + * erase the popup first. It probably has overwritten some of + * the next-to-map empty space. If we don't clear that now, the + * base window will remember it and redisplay it during refreshes. + * + * Note: since we almost never destroy non-popups, we don't really + * need to determine whether 'win' is one. Overhead for unnecessary + * erasure is negligible. + */ + getmaxyx(win, dummyht, winwidth); /* macro, assigns to its args */ + if (mapwin) + getmaxyx(mapwin, dummyht, mapwidth); + if (winwidth > mapwidth) { + werase(win); + wnoutrefresh(win); + } + delwin(win); if (win == activemenu) activemenu = NULL; curses_refresh_nethack_windows(); + nhUse(dummyht); } @@ -175,12 +241,17 @@ curses_refresh_nethack_windows(void) map_window = curses_get_nhwin(MAP_WIN); inv_window = curses_get_nhwin(INV_WIN); - if ((gm.moves <= 1) && !gi.invent) { + if (!iflags.window_inited) { + return; + } + + if (svm.moves == 0) { /* Main windows not yet displayed; refresh base window instead */ touchwin(stdscr); refresh(); } else { if (status_window != NULL) { + curses_set_wid_colors(STATUS_WIN, NULL); touchwin(status_window); wnoutrefresh(status_window); } @@ -189,6 +260,7 @@ curses_refresh_nethack_windows(void) wnoutrefresh(map_window); } if (message_window != NULL) { + curses_set_wid_colors(MESSAGE_WIN, NULL); touchwin(message_window); wnoutrefresh(message_window); } @@ -215,12 +287,74 @@ curses_get_nhwin(winid wid) return nhwins[wid].curwin; } +boolean +parse_hexstr(char *colorbuf, int *red, int *green, int *blue) +{ + int len = colorbuf ? strlen(colorbuf) : 0; + + if (len == 7 && colorbuf[0] == '#') { + char tmpbuf[16]; + + Sprintf(tmpbuf, "0x%c%c", colorbuf[1], colorbuf[2]); + *red = strtol(tmpbuf, NULL, 0); + Sprintf(tmpbuf, "0x%c%c", colorbuf[3], colorbuf[4]); + *green = strtol(tmpbuf, NULL, 0); + Sprintf(tmpbuf, "0x%c%c", colorbuf[5], colorbuf[6]); + *blue = strtol(tmpbuf, NULL, 0); + return TRUE; + } + return FALSE; +} + +void +curses_parse_wid_colors(int wid, char *fg, char *bg) +{ + + if (curses_is_text(wid)) { + wid = TEXT_WIN; + } else if (curses_is_menu(wid)) { + wid = MENU_WIN; + } + + if (nhwins[wid].clr_inited) + return; + + int nh_fg = fg ? match_str2clr(fg, TRUE) : CLR_MAX; + int nh_bg = bg ? match_str2clr(bg, TRUE) : CLR_MAX; + int r, g, b; + + if (nh_fg == CLR_MAX) { + if (fg && parse_hexstr(fg, &r, &g, &b)) { + nh_fg = curses_init_rgb(r, g, b); + } else { + nh_fg = -1; + } + } + if (nh_bg == CLR_MAX) { + if (bg && parse_hexstr(bg, &r, &g, &b)) { + nh_bg = curses_init_rgb(r, g, b); + } else { + nh_bg = -1; + } + } + + nhwins[wid].fg = nh_fg; + nhwins[wid].bg = nh_bg; + if (nh_fg == -1 || nh_bg == -1) { + nhwins[wid].clr_inited = -1; + } else { + nhwins[wid].colorpair = curses_init_pair(nh_fg, nh_bg); + nhwins[wid].clr_inited = 1; + } +} + /* Add curses window pointer and window info to list for given NetHack winid */ void -curses_add_nhwin(winid wid, int height, int width, int y, int x, - orient orientation, boolean border) +curses_add_nhwin( + winid wid, int height, int width, int y, int x, + orient orientation, boolean border) { WINDOW *win; int real_width = width; @@ -239,6 +373,9 @@ curses_add_nhwin(winid wid, int height, int width, int y, int x, nhwins[wid].x = x; nhwins[wid].y = y; nhwins[wid].orientation = orientation; + nhwins[wid].fg = nhwins[wid].bg = 0; + nhwins[wid].colorpair = -1; + nhwins[wid].clr_inited = 0; if (border) { real_width += 2; /* leave room for bounding box */ @@ -246,13 +383,20 @@ curses_add_nhwin(winid wid, int height, int width, int y, int x, } win = newwin(real_height, real_width, y, x); + nhwins[wid].curwin = win; switch (wid) { case MESSAGE_WIN: messagewin = win; + curses_parse_wid_colors(wid, iflags.wcolors[wcolor_message].fg, + iflags.wcolors[wcolor_message].bg); + curses_set_wid_colors(wid, NULL); break; case STATUS_WIN: statuswin = win; + curses_parse_wid_colors(wid, iflags.wcolors[wcolor_status].fg, + iflags.wcolors[wcolor_status].bg); + curses_set_wid_colors(wid, NULL); break; case MAP_WIN: mapwin = win; @@ -264,7 +408,6 @@ curses_add_nhwin(winid wid, int height, int width, int y, int x, box(win, 0, 0); } - nhwins[wid].curwin = win; } @@ -299,6 +442,7 @@ curses_add_wid(winid wid) void curses_refresh_nhwin(winid wid) { + curses_set_wid_colors(wid, NULL); wnoutrefresh(curses_get_nhwin(wid)); doupdate(); } @@ -322,8 +466,10 @@ curses_del_nhwin(winid wid) wid); return; } - delwin(nhwins[wid].curwin); - nhwins[wid].curwin = NULL; + if (nhwins[wid].curwin != NULL) { + delwin(nhwins[wid].curwin); + nhwins[wid].curwin = NULL; + } nhwins[wid].nhwin = -1; } @@ -455,6 +601,10 @@ curses_get_window_size(winid wid, int *height, int *width) boolean curses_window_has_border(winid wid) { + if (curses_is_menu(wid)) + wid = MENU_WIN; + else if (curses_is_text(wid)) + wid = TEXT_WIN; return nhwins[wid].border; } @@ -528,8 +678,9 @@ curses_puts(winid wid, int attr, const char *text) } Id = cg.zeroany; curses_add_nhmenu_item(wid, &nul_glyphinfo, &Id, 0, 0, - attr, text, MENU_ITEMFLAGS_NONE); + attr, NO_COLOR, text, MENU_ITEMFLAGS_NONE); } else { + curses_set_wid_colors(wid, NULL); waddstr(win, text); wnoutrefresh(win); } @@ -549,6 +700,7 @@ curses_clear_nhwin(winid wid) clear_map(); } + curses_set_wid_colors(wid, NULL); werase(win); if (border) { @@ -564,6 +716,7 @@ curses_alert_win_border(winid wid, boolean onoff) if (!win || !curses_window_has_border(wid)) return; + curses_set_wid_colors(wid, NULL); if (onoff) curses_toggle_color_attr(win, ALERT_BORDER_COLOR, NONE, ON); box(win, 0, 0); @@ -598,26 +751,12 @@ is_main_window(winid wid) /* Unconditionally write a single character to a window at the given coordinates without a refresh. Currently only used for the map. */ -static int +/* convert nhcolor (fg) and framecolor (bg) to curses colorpair */ +int get_framecolor(int nhcolor, int framecolor) { - boolean hicolor = (COLORS >= 16), adj_framecolor = framecolor; - static int framecolors[16][8] = { - { 0, 16, 8, 32, 17, 40, 48, 0 }, { 1, 18, 9, 33, 19, 41, 49, 1 }, - { 2, 20, 10, 34, 21, 42, 50, 2 }, { 3, 22, 11, 35, 23, 43, 51, 3 }, - { 4, 24, 12, 36, 25, 44, 52, 4 }, { 5, 26, 13, 37, 27, 45, 53, 5 }, - { 6, 28, 14, 38, 29, 46, 54, 6 }, { 0, 30, 15, 72, 31, 88, 56, 0 }, - { 0, 30, 15, 39, 31, 47, 55, 0 }, { 1, 18, 9, 73, 19, 89, 57, 121 }, - { 2, 20, 10, 74, 21, 90, 58, 122 }, { 2, 22, 11, 75, 23, 91, 59, 123 }, - { 4, 24, 12, 76, 25, 44, 60, 124 }, { 5, 26, 13, 77, 27, 93, 61, 125 }, - { 6, 28, 14, 78, 29, 94, 62, 126 }, { 0, 30, 15, 79, 31, 95, 63, 127 }, - }; - - if (framecolor < 16 && framecolor >= 8) - adj_framecolor = framecolor - 8; - return ((nhcolor < (hicolor ? 16 : 8) && adj_framecolor < 8) - ? framecolors[nhcolor][adj_framecolor] - : nhcolor); + /* curses_toggle_color_attr() adds the +1 and takes care of COLORS < 16 */ + return (16 * (framecolor % 8)) + (nhcolor % 16); } static void diff --git a/win/curses/curswins.h b/win/curses/curswins.h index 589e81bb03..5842f52edc 100644 --- a/win/curses/curswins.h +++ b/win/curses/curswins.h @@ -9,11 +9,15 @@ /* Global declarations */ -WINDOW *curses_create_window(int width, int height, orient orientation); +WINDOW *curses_create_window(int wid, int width, int height, orient orientation); +int curses_win_clr_inited(int wid); +void curses_set_wid_colors(int wid, WINDOW *win); void curses_destroy_win(WINDOW * win); void curses_refresh_nethack_windows(void); WINDOW *curses_get_nhwin(winid wid); +void curses_parse_wid_colors(int wid, char *fg, char *bg); +boolean parse_hexstr(char *colorbuf, int *red, int *green, int *blue); void curses_add_nhwin(winid wid, int height, int width, int y, int x, orient orientation, boolean border); void curses_add_wid(winid wid); diff --git a/win/macosx/.gitattributes b/win/macosx/.gitattributes index ce60e3048c..fc696711d1 100644 --- a/win/macosx/.gitattributes +++ b/win/macosx/.gitattributes @@ -1,2 +1,3 @@ *.applescript NHSUBST +*.JavaScript NHSUBST * NH_filestag=(file%s_for_macosx_versions) diff --git a/win/share/.gitattributes b/win/share/.gitattributes index cb0ac54485..3e47933f49 100644 --- a/win/share/.gitattributes +++ b/win/share/.gitattributes @@ -1,2 +1,3 @@ tile.doc NHSUBST +*.lua NHSUBST * NH_filestag=(file%s_for_versions_using_optional_tiles) diff --git a/win/share/gifread.c b/win/share/gifread.c index 326677e74b..18c0dd5de0 100644 --- a/win/share/gifread.c +++ b/win/share/gifread.c @@ -338,7 +338,7 @@ LWZReadByte(FILE *fd, int flag) /*, int input_code_size)*/ static int clear_code, end_code; static int table[2][(1 << MAX_LWZ_BITS)]; static int stack[(1 << (MAX_LWZ_BITS)) * 2], *sp; - register int i; + int i; if (flag) { set_code_size = input_code_size; diff --git a/win/share/monsters.txt b/win/share/monsters.txt index a3c64d62d4..353455c79e 100644 --- a/win/share/monsters.txt +++ b/win/share/monsters.txt @@ -1,3 +1,6 @@ +# monsters.txt - tile definitions for monsters +# Note: lines beginning with '# tile ' are not comments; other +# lines beginning with '#' are. . = (71, 108, 108) A = (0, 0, 0) B = (0, 182, 255) @@ -46,6 +49,12 @@ Z = (195, 195, 195) ......JA.JA..... ................ } +# +# 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. +# # tile 1 (giant ant,female) { ................ @@ -845,7 +854,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 @@ -889,7 +898,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 45 (werewolf,female) @@ -952,39 +961,39 @@ Z = (195, 195, 195) # tile 48 (warg,male) { ................ - ...P..P....PP... - ...P.PP......P.. - ..PPPP.......P.A - ..N.NPP......P.A - .P..PPPPP....P.A - PPPPDPPPPPPPPPAA - DPPNDPPPPPPPPPAA - ..DDDPPPPPPPPPAA - PNDNP.PPPPPPPPAA - .PPPPAPPPPAPP.A. - ...PAAPPAAAAPPAA - ...PAAP.AAAAP.A. - .PPPAAPAAAAAPAA. - ....PPPAA.PPPA.. + ...P..0....P0... + ...0.0P......0.. + ..0P0P.......P.A + ..N.N0P......0.A + .P..0P0P0....P.A + P0P0D0P0P0P0P0AA + DP0NDP0P0P0P0PAA + ..DDD0P0P0P0P0AA + 0NDN0.0P0P0P0PAA + .0P0PAP0P0A0P.A. + ...PAA0PAAAA0PAA + ...0AAP.AAAAP.A. + .P0PAA0AAAAA0AA. + ....P0PAA.P0PA.. ................ } # tile 49 (warg,female) { ................ - ...P..P.....P... - ...P.PP......P.. - ..PPPP.......P.A - ..N.NPP......P.A - .P..PPPPP....P.A - PPPPDPPPPPPPPPAA - DPPNDPPPPPPPPPAA - ..DDDPPPPPPPPPAA - PNDNP.PPPPPPPPAA - .PPPPAPPPPAPP.A. - ...PAAPPAAAAPPAA - ...PAAP.AAAAP.A. - .PPPAAPAAAAAPAA. - ....PPPAA.PPPA.. + ...P..0.....P... + ...0.0P......0.. + ..0P0P.......P.A + ..N.N0P......0.A + .P..0P0P0....P.A + P0P0D0P0P0P0P0AA + DP0NDP0P0P0P0PAA + ..DDD0P0P0P0P0AA + 0NDN0.0P0P0P0PAA + .0P0PAP0P0A0P.A. + ...PAA0PAAAA0PAA + ...0AAP.AAAAP.A. + .P0PAA0AAAAA0AA. + ....P0PAA.P0PA.. ................ } # tile 50 (winter wolf,male) @@ -1424,7 +1433,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 @@ -1810,7 +1819,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 93 (hobbit,female) @@ -1832,7 +1841,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, @@ -2466,7 +2475,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. # @@ -2793,7 +2802,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 146 (orc,male) +# tile 144 (orc,male) { ................ ................ @@ -2812,7 +2821,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 147 (orc,female) +# tile 145 (orc,female) { ................ ................ @@ -2831,7 +2840,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 148 (hill orc,male) +# tile 146 (hill orc,male) { ................ ................ @@ -2850,7 +2859,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 149 (hill orc,female) +# tile 147 (hill orc,female) { ................ ................ @@ -2869,7 +2878,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 144 (bugbear,male) +# tile 148 (bugbear,male) { ................ ................ @@ -2888,7 +2897,7 @@ Z = (195, 195, 195) ........CCA..... ................ } -# tile 145 (bugbear,female) +# tile 149 (bugbear,female) { ................ ................ @@ -4750,7 +4759,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 247 (Aleax,female) @@ -4867,7 +4876,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. # @@ -5099,6 +5108,10 @@ Z = (195, 195, 195) ....CA...C...... ................ } +# 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. +# # tile 265 (plains centaur,female) { ................ @@ -6543,7 +6556,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. # @@ -6585,7 +6598,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. # @@ -6665,7 +6678,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. # @@ -6859,7 +6872,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. # @@ -7015,7 +7028,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 365 (minotaur,female) @@ -7474,7 +7487,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. # @@ -8124,7 +8137,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 423 (ogre tyrant,female) @@ -9080,7 +9093,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 473 (quantum mechanic,female) @@ -9140,7 +9153,7 @@ Z = (195, 195, 195) ......CFFCAA.... ....IIC.IIA..... } -# 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. # @@ -9391,7 +9404,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 @@ -10651,7 +10664,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 555 (human,female) @@ -10730,7 +10743,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 559 (werejackal,female) @@ -10771,7 +10784,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 561 (werewolf,female) @@ -10888,7 +10901,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. # @@ -11714,18 +11727,18 @@ Z = (195, 195, 195) ................ ......OPP.O..... ......PPPP...... - .....CPPDP...... - ....CCCPPP...... - ....CCPPPP...... - ....C..PPA...... - .....DDAADD.AAA. - ....DDDDDDDDAAA. - ....DADDDDADAAA. - ....DADDDDADAAA. - ....DADDDDADAAA. - ......DAADAAAA.. - ......DAADAA.A.. - .....DDA.DDA.... + .....KPPDP...... + ....KJJPPP...... + ....KJPPPP...... + ....K..PPA...... + .....GGAAGG.AAA. + ....FDFDFDFDAAA. + ..G.GADGDFAGAGA. + ....GAFGFGAGAAA. + ..G.DADFDGAFAAA. + ......GAADAAAG.. + .F.G..GAAFAA.A.. + .....DFA.GGA..F. ................ } # tile 611 (vrock,female) @@ -11733,18 +11746,18 @@ Z = (195, 195, 195) ................ ......OPP.O..... ......PPPP...... - .....CPPDP...... - ....CCCPPP...... - ....CCPPPP...... - ....C..PPA...... - .....DDAADD.AAA. - ....DDDDDDDDAAA. - ....DADDDDADAAA. - ....DADDDDADAAA. - ....DADDDDADAAA. - ......DAADAAAA.. - ......DAADAA.A.. - .....DDA.DDA.... + .....LPPGP...... + ....LCCPPP...... + ....LCPPPP...... + ....L..PPA...... + .....GGAAGG.AAA. + ....FDFDFDFDAAA. + ..G.GADGDFAGAGA. + ....GAFGFGAGAAA. + ..G.DADFDGAFAAA. + ......GAADAAAG.. + .F.G..GAAFAA.A.. + .....DFA.GGA..F. ................ } # tile 612 (hezrou,male) @@ -13096,7 +13109,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. # @@ -14335,43 +14348,45 @@ Z = (195, 195, 195) ....CJJJCLAAAAAA ..LLLLL.LLLLLAA. } +#: Ixoth is a male red dragon +#: [for many years his tile erroneously depicted a demon] # tile 748 (Ixoth,male) { - ....O......O.... - ....O......O.... - ...LOOCDDCOOL... - ...DDDDDDDDDDD.. - .CCDDDGDDGDDDCC. - CCDKDDDDDDDDJCCC - CDDKKKDIIDJJDCCD - CDDKDDKJJJDDDCDD - CCDKDDDDDDDDKCDD - .CCDKKDDDDKKCDDD - .CCDADKDDKDACDD. - .DDDADDDDDDADDD. - ....CCDDDDKKAA.. - ...CDDDAADDDKAA. - ..CDDDAAA.DDDK.. - ................ + ......O......... + ...KJ.DDD.O..... + ..KJODDDKKDD..O. + .KJCDIDKJJJDDDDD + .KJDDDKJJJJJAAA. + .KJDDKJJKJDAAA.. + .CJODCJKJDDDA... + .CJIDCKJDADDA... + .ACDOCKDAAAD.... + .AADDDCDAAAA.... + ..DDDOICOAAA.... + .DDDOODIODA..... + .DDADDDDCDA..... + DDAADGDDGDD..... + DDDAADDDDADD.... + ADD.AADD..AD.... } # tile 749 (Ixoth,female) { - ....O......O.... - ....O......O.... - ...LOOCDDCOOL... - ...DDDDDDDDDDD.. - .CCDDDGDDGDDDCC. - CCDKDDDDDDDDJCCC - CDDKKKDIIDJJDCCD - CDDKDDKJJJDDDCDD - CCDKDDDDDDDDKCDD - .CCDKKDDDDKKCDDD - .CCDADKDDKDACDD. - .DDDADDDDDDADDD. - ....CCDDDDKKAA.. - ...CDDDAADDDKAA. - ..CDDDAAA.DDDK.. - ................ + ......O......... + ...KJ.DDD.O..... + ..KJODDDKKDD..O. + .KJCDIDKJJJDDDDD + .KJDDDKJJJJJAAA. + .KJDDKJJKJDAAA.. + .CJODCJKJDDDA... + .CJIDCKJDADDA... + .ACDOCKDAAAD.... + .AADDDCDAAAA.... + ..DDDOICOAAA.... + .DDDOODIODA..... + .DDADDDDCDA..... + DDAADGDDGDD..... + DDDAADDDDADD.... + ADD.AADD..AD.... } # tile 750 (Master Kaen,male) { diff --git a/win/share/objects.txt b/win/share/objects.txt index 0814f7b5e0..1ed472dba2 100644 --- a/win/share/objects.txt +++ b/win/share/objects.txt @@ -1,4 +1,4 @@ -# objects.txt - tile defintions for objects +# objects.txt - tile definitions for objects # Note: lines beginning with '# tile ' are not comments; other # lines beginning with '#' are. . = (71, 108, 108) @@ -7289,7 +7289,26 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 381 (plain / blank paper) +# tile 381 (checkered / chain lightning) +{ + ................ + ................ + ................ + ....AAAA........ + ....AAAAAA...... + ...AAADDDANNP... + ...AADDAADNNN... + ..PNAADDNNNNOA.. + ..NNDNADDNNNOAA. + .PNNNDDDAAAONA.. + .NNNNNNAAAAAAA.. + .NOONNAAAAAOA... + ..PNOOOAAAOA.... + ....PAAOOOA..... + .......AAA...... + ................ +} +# tile 382 (plain / blank paper) { ................ ................ @@ -7308,7 +7327,7 @@ Z = (195, 195, 195) .......JJJAA.... ................ } -# tile 382 (paperback / novel) +# tile 383 (paperback / novel) { ................ ................ @@ -7327,7 +7346,7 @@ Z = (195, 195, 195) .......EEEAA.... ................ } -# tile 383 (papyrus / Book of the Dead) +# tile 384 (papyrus / Book of the Dead) { ................ ................ @@ -7346,7 +7365,7 @@ Z = (195, 195, 195) .......AAA...... ................ } -# tile 384 (glass / light) +# tile 385 (glass / light) { ................ ................ @@ -7365,7 +7384,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 385 (balsa / secret door detection) +# tile 386 (balsa / secret door detection) { ................ ................ @@ -7384,7 +7403,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 386 (crystal / enlightenment) +# tile 387 (crystal / enlightenment) { ................ ................ @@ -7403,7 +7422,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 387 (maple / create monster) +# tile 388 (maple / create monster) { ................ ................ @@ -7422,7 +7441,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 388 (pine / wishing) +# tile 389 (pine / wishing) { ................ ................ @@ -7441,7 +7460,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 389 (oak / nothing) +# tile 390 (oak / nothing) { ................ ................ @@ -7460,7 +7479,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 390 (ebony / striking) +# tile 391 (ebony / striking) { ................ ................ @@ -7479,7 +7498,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 391 (marble / make invisible) +# tile 392 (marble / make invisible) { ................ ................ @@ -7498,7 +7517,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 392 (tin / slow monster) +# tile 393 (tin / slow monster) { ................ ................ @@ -7517,7 +7536,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 393 (brass / speed monster) +# tile 394 (brass / speed monster) { ................ ................ @@ -7536,7 +7555,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 394 (copper / undead turning) +# tile 395 (copper / undead turning) { ................ ................ @@ -7555,7 +7574,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 395 (silver / polymorph) +# tile 396 (silver / polymorph) { ................ ................ @@ -7574,7 +7593,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 396 (platinum / cancellation) +# tile 397 (platinum / cancellation) { ................ ................ @@ -7593,7 +7612,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 397 (iridium / teleportation) +# tile 398 (iridium / teleportation) { ................ ................ @@ -7612,7 +7631,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 398 (zinc / opening) +# tile 399 (zinc / opening) { ................ ................ @@ -7631,7 +7650,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 399 (aluminum / locking) +# tile 400 (aluminum / locking) { ................ ................ @@ -7650,7 +7669,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 400 (uranium / probing) +# tile 401 (uranium / probing) { ................ ................ @@ -7669,7 +7688,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 401 (iron / digging) +# tile 402 (iron / digging) { ................ ................ @@ -7688,7 +7707,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 402 (steel / magic missile) +# tile 403 (steel / magic missile) { ................ ................ @@ -7707,7 +7726,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 403 (hexagonal / fire) +# tile 404 (hexagonal / fire) { ................ ................ @@ -7726,7 +7745,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 404 (short / cold) +# tile 405 (short / cold) { ................ ................ @@ -7745,7 +7764,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 405 (runed / sleep) +# tile 406 (runed / sleep) { ................ ................ @@ -7764,7 +7783,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 406 (long / death) +# tile 407 (long / death) { ................ .............NO. @@ -7783,7 +7802,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 407 (curved / lightning) +# tile 408 (curved / lightning) { ................ .......NO....... @@ -7802,7 +7821,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 408 (forked) +# tile 409 (forked) { ................ ................ @@ -7821,7 +7840,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 409 (spiked) +# tile 410 (spiked) { ................ ................ @@ -7840,7 +7859,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 410 (jeweled) +# tile 411 (jeweled) { ................ ................ @@ -7859,7 +7878,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 411 (gold) +# tile 412 (gold) { ................ ................ @@ -7878,7 +7897,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 412 (gilded) +# tile 413 (gilded) { ................ ................ @@ -7897,7 +7916,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 413 (gold piece) +# tile 414 (gold piece) { ................ ................ @@ -7916,7 +7935,7 @@ Z = (195, 195, 195) .........HA..... ...........HA... } -# tile 414 (white / dilithium crystal) +# tile 415 (white / dilithium crystal) { ................ ................ @@ -7935,7 +7954,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 415 (white / diamond) +# tile 416 (white / diamond) { ................ ................ @@ -7954,7 +7973,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 416 (red / ruby) +# tile 417 (red / ruby) { ................ ................ @@ -7973,7 +7992,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 417 (orange / jacinth) +# tile 418 (orange / jacinth) { ................ ................ @@ -7992,7 +8011,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 418 (blue / sapphire) +# tile 419 (blue / sapphire) { ................ ................ @@ -8011,7 +8030,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 419 (black / black opal) +# tile 420 (black / black opal) { ................ ................ @@ -8030,7 +8049,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 420 (green / emerald) +# tile 421 (green / emerald) { ................ ................ @@ -8049,7 +8068,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 421 (green / turquoise) +# tile 422 (green / turquoise) { ................ ................ @@ -8068,7 +8087,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 422 (yellow / citrine) +# tile 423 (yellow / citrine) { ................ ................ @@ -8087,7 +8106,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 423 (green / aquamarine) +# tile 424 (green / aquamarine) { ................ ................ @@ -8106,7 +8125,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 424 (yellowish brown / amber) +# tile 425 (yellowish brown / amber) { ................ ................ @@ -8125,7 +8144,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 425 (yellowish brown / topaz) +# tile 426 (yellowish brown / topaz) { ................ ................ @@ -8144,7 +8163,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 426 (black / jet) +# tile 427 (black / jet) { ................ ................ @@ -8163,7 +8182,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 427 (white / opal) +# tile 428 (white / opal) { ................ ................ @@ -8182,7 +8201,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 428 (yellow / chrysoberyl) +# tile 429 (yellow / chrysoberyl) { ................ ................ @@ -8201,7 +8220,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 429 (red / garnet) +# tile 430 (red / garnet) { ................ ................ @@ -8220,7 +8239,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 430 (violet / amethyst) +# tile 431 (violet / amethyst) { ................ ................ @@ -8239,7 +8258,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 431 (red / jasper) +# tile 432 (red / jasper) { ................ ................ @@ -8258,7 +8277,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 432 (violet / fluorite) +# tile 433 (violet / fluorite) { ................ ................ @@ -8277,7 +8296,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 433 (black / obsidian) +# tile 434 (black / obsidian) { ................ ................ @@ -8296,7 +8315,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 434 (orange / agate) +# tile 435 (orange / agate) { ................ ................ @@ -8315,7 +8334,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 435 (green / jade) +# tile 436 (green / jade) { ................ ................ @@ -8334,7 +8353,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 436 (white / worthless piece of white glass) +# tile 437 (white / worthless piece of white glass) { ................ ................ @@ -8353,7 +8372,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 437 (blue / worthless piece of blue glass) +# tile 438 (blue / worthless piece of blue glass) { ................ ................ @@ -8372,7 +8391,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 438 (red / worthless piece of red glass) +# tile 439 (red / worthless piece of red glass) { ................ ................ @@ -8391,7 +8410,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 439 (yellowish brown / worthless piece of yellowish brown glass) +# tile 440 (yellowish brown / worthless piece of yellowish brown glass) { ................ ................ @@ -8410,7 +8429,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 440 (orange / worthless piece of orange glass) +# tile 441 (orange / worthless piece of orange glass) { ................ ................ @@ -8429,7 +8448,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 441 (yellow / worthless piece of yellow glass) +# tile 442 (yellow / worthless piece of yellow glass) { ................ ................ @@ -8448,7 +8467,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 442 (black / worthless piece of black glass) +# tile 443 (black / worthless piece of black glass) { ................ ................ @@ -8467,7 +8486,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 443 (green / worthless piece of green glass) +# tile 444 (green / worthless piece of green glass) { ................ ................ @@ -8486,7 +8505,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 444 (violet / worthless piece of violet glass) +# tile 445 (violet / worthless piece of violet glass) { ................ ................ @@ -8505,7 +8524,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 445 (gray / luckstone) +# tile 446 (gray / luckstone) { ................ ................ @@ -8524,7 +8543,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 446 (gray / touchstone) +# tile 447 (gray / touchstone) { ................ ................ @@ -8543,7 +8562,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 447 (gray / thiefstone) +# tile 448 (gray / thiefstone) { ................ ................ @@ -8562,7 +8581,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 448 (gray / flint) +# tile 449 (gray / flint) { ................ ................ @@ -8581,7 +8600,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 449 (rock) +# tile 450 (rock) { ................ ................ @@ -8600,7 +8619,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 450 (boulder) +# tile 451 (boulder) { ................ ................ @@ -8619,7 +8638,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 451 (statue) +# tile 452 (statue) { ................ ........JJ...... @@ -8638,7 +8657,7 @@ Z = (195, 195, 195) .....JJJJJJAA... ................ } -# tile 452 (heavy iron ball) +# tile 453 (heavy iron ball) { ................ ................ @@ -8657,7 +8676,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 453 (iron chain) +# tile 454 (iron chain) { ................ ................ @@ -8676,7 +8695,7 @@ Z = (195, 195, 195) ...........PP.PA ............AA.. } -# tile 454 (splash of venom / splash of blinding venom) +# tile 455 (splash of venom / splash of blinding venom) { ................ ................ @@ -8695,7 +8714,7 @@ Z = (195, 195, 195) ................ ................ } -# tile 455 (splash of venom / splash of acid venom) +# tile 456 (splash of venom / splash of acid venom) { ................ ................ diff --git a/win/share/other.txt b/win/share/other.txt index 4ce336d993..0bc6af0701 100644 --- a/win/share/other.txt +++ b/win/share/other.txt @@ -1,3 +1,6 @@ +# other.txt - tile definitions for 'other' (walls, furniture, explosions) +# Note: lines beginning with '# tile ' are not comments; other +# lines beginning with '#' are. . = (71, 108, 108) A = (0, 0, 0) B = (0, 182, 255) @@ -426,14 +429,14 @@ Z = (195, 195, 195) .A.A.A.A.A.A.A.A A.A.A.A.A.A.A.A. } -# tile 21 (engraved part of a room) +# tile 21 (engraving in a room) { ................ ................ - ...E...E........ - ....E...E....... - .........E..E... - ..........E..... + ...H...H........ + ....H...H....... + .........H..H... + ..........H..... ................ .......PP....... .......PP....... @@ -483,17 +486,17 @@ Z = (195, 195, 195) ................ ................ } -# tile 24 (engraved part of a corridor) +# tile 24 (engraving in a corridor) { ................ - ...E...E........ - ....E...E....... - .........E..E... - ..........E..... + ...H...H........ + ....H...H....... + .........H..H... + ..........H..... ................ .......PP....... - ......PPPP...... - ......PPPP...... + ......P.PP...... + ......PP.P...... .......PP....... ................ ................ @@ -2592,23 +2595,24 @@ Z = (195, 195, 195) BBBBBBGGGGBBBBBB BBBBBBBBBBBBBBBB } +# S_goodpos (dollar sign from generic coin tile) # tile 135 (valid position) { ................ ................ - .....GGGG....... - ....GGGGGG...... - ...GGFFFFGG..... - ...GGF...GGF.... - ....FF...GGF.... - ........GGFF.... - .......GGFF..... - ......GGFF...... - ......GGF....... - .......FF....... - ......GG........ - ......GGF....... - .......FF....... + ................ + ........B....... + .......OOO...... + ......O.BSS..... + ......OSB....... + .......OOO...... + ........BSO..... + ........B.OS.... + .......OOO.S.... + ........BSS..... + ................ + ................ + ................ ................ } # tile 136 (swallow top left) diff --git a/win/share/safeproc.c b/win/share/safeproc.c index f0497ca783..19e9aa9b63 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; } @@ -326,7 +356,7 @@ safe_nhgetch(void) /* * return a key, or 0, in which case a mouse button was pressed - * mouse events should be returned as character postitions in the map window. + * mouse events should be returned as character positions in the map window. * Since normal tty's don't have mice, just return a key. */ /*ARGSUSED*/ @@ -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) { @@ -430,7 +448,7 @@ safe_outrip(winid tmpwin UNUSED, int how UNUSED, time_t when UNUSED) /*ARGSUSED*/ void -safe_preference_update(const char* pref UNUSED) +safe_preference_update(const char *pref UNUSED) { return; } @@ -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 } /************************************************************** @@ -518,7 +546,7 @@ stdio_wait_synch(void) /* Add to your code: windowprocs.win_raw_print = stdio_raw_print; */ void -stdio_raw_print(const char* str) +stdio_raw_print(const char *str) { if (str) fprintf(stdout, "%s\n", str); @@ -528,7 +556,7 @@ stdio_raw_print(const char* str) /* no newline variation, add to your code: windowprocs.win_raw_print = stdio_nonl_raw_print; */ void -stdio_nonl_raw_print(const char* str) +stdio_nonl_raw_print(const char *str) { if (str) fprintf(stdout, "%s", str); @@ -537,7 +565,7 @@ stdio_nonl_raw_print(const char* str) /* Add to your code: windowprocs.win_raw_print_bold = stdio_raw_print_bold; */ void -stdio_raw_print_bold(const char* str) +stdio_raw_print_bold(const char *str) { stdio_raw_print(str); return; @@ -550,5 +578,19 @@ stdio_nhgetch(void) return getchar(); } +#ifdef CHANGE_COLOR +void +safe_change_color(int color UNUSED, long rgb UNUSED, int reverse UNUSED) +{ +} + +char * +safe_get_color_string(void) +{ + return (""); +} + + +#endif /* safeprocs.c */ diff --git a/win/share/tile2bmp.c b/win/share/tile2bmp.c index 4edc3ba225..4b7312affb 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. */ @@ -15,6 +15,8 @@ #endif #include "config.h" +#include "hacklib.h" + #include "tile.h" extern void monst_globals_init(void); extern void objects_globals_init(void); @@ -58,8 +60,6 @@ lelong(int32_t x) #endif } -unsigned FITSuint_(unsigned long long, const char *, int); - #ifdef __GNUC__ typedef struct tagBMIH { uint32_t biSize; @@ -142,7 +142,7 @@ DISABLE_WARNING_UNREACHABLE_CODE int main(int argc, char *argv[]) { - int i, j; + int i, j, number_of_rows, number_in_final_row; uchar *c; char tilefile_full_path[256] = { 0 }; @@ -251,8 +251,19 @@ main(int argc, char *argv[]) } fwrite(newbmp, bmpsize, 1, fp); fclose(fp); - Fprintf(stderr, "Total of %d tiles written to %s.\n", - tiles_counted, bmpname); + number_of_rows = tiles_counted / max_tiles_in_row; + number_in_final_row = tiles_counted % max_tiles_in_row; + + Fprintf(stderr, "Total of %d tiles written to %s, " + "%d full rows of %d tiles", + tiles_counted, bmpname, + number_of_rows, max_tiles_in_row); + if (number_in_final_row != 0) { + Fprintf(stderr, ", final row %d has %d tile%s", + number_of_rows + 1, number_in_final_row, + number_in_final_row > 1 ? "s" : ""); + } + (void) fputs(".\n\n", stderr); free((genericptr_t) newbmp); exit(EXIT_SUCCESS); diff --git a/win/share/tilemap.c b/win/share/tilemap.c index 33269d2429..7f138cd00b 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: 1736530790 2025/01/10 09:39:50 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.86 $ */ /* Copyright (c) 2016 by Michael Allison */ /* NetHack may be freely redistributed. See license for details. */ @@ -18,10 +18,14 @@ #include "rm.h" #else #include "hack.h" -#include "display.h" -#include #endif +#ifdef Snprintf +#undef Snprintf +#endif +#define Snprintf(str, size, ...) \ + nh_snprintf(__func__, __LINE__, str, size, __VA_ARGS__) + #ifdef MONITOR_HEAP /* with heap monitoring enabled, free(ptr) is a macro which expands to nhfree(ptr,__FILE__,__LINE__); since tilemap doesn't link with @@ -30,10 +34,6 @@ #endif #define Fprintf (void) fprintf -#define Snprintf(str, size, ...) \ - nh_snprintf(__func__, __LINE__, str, size, __VA_ARGS__) -void nh_snprintf(const char *func, int line, char *str, size_t size, - const char *fmt, ...); /* * Defining OBTAIN_TILEMAP to get a listing of the tile-mappings @@ -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; @@ -125,14 +121,14 @@ struct tilemap_t { #endif } tilemap[MAX_GLYPH]; -#define MAX_TILENAM 30 +#define MAX_TILENAM 256 /* List of tiles encountered and their usage */ struct tiles_used { int tilenum; enum tilesrc src; int file_entry; char tilenam[MAX_TILENAM]; - char references[120]; + char references[1024]; }; struct tiles_used *tilelist[2500] = { 0 }; @@ -1486,16 +1482,17 @@ main(int argc UNUSED, char *argv[] UNUSED) Fprintf(ofp, "%smaxmontile = %d,\n", indent, lastmontile); Fprintf(ofp, "%smaxobjtile = %d,\n", indent, lastobjtile); Fprintf(ofp, "%smaxothtile = %d;\n\n", indent, lastothtile); + Fprintf(ofp, "#define NO_CUSTOMCOLOR (0U)\n\n"); Fprintf(ofp, "/* glyph, ttychar, { %s%s } */\n", - "glyphflags, {color, symidx}, ovidx, tileidx", enhanced); + "glyphflags, { NO_COLOR, symidx }, NO_CUSTOMCOLOR, NO_CUSTOMCOLOR, ovidx, tileidx", enhanced); #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, - "MG_UNEXPL, { NO_COLOR, SYM_UNEXPLORED + SYM_OFF_X }", + "MG_UNEXPL, { NO_COLOR, SYM_UNEXPLORED + SYM_OFF_X }, NO_CUSTOMCOLOR, NO_CUSTOMCOLOR", enhanced); Fprintf(ofp, "};\n"); Fprintf(ofp, "\nglyph_map glyphmap[MAX_GLYPH] = {\n"); @@ -1510,7 +1507,7 @@ main(int argc UNUSED, char *argv[] UNUSED) /*NOTREACHED*/ } Fprintf(ofp, - " { 0U, { 0, 0 }, %4d%s }, /* [%04d] %s:%03d %s */\n", + " { 0U, { NO_COLOR, 0 }, NO_CUSTOMCOLOR, NO_CUSTOMCOLOR, %4d%s }, /* [%04d] %s:%03d %s */\n", tilenum, enhanced, i, tilesrc_texts[tilelist[tilenum]->src], tilelist[tilenum]->file_entry, @@ -1610,7 +1607,8 @@ precheck(int offset, const char *glyphtype) glyphtype); } -void add_tileref( +void +add_tileref( int n, int glyphref, enum tilesrc src, @@ -1623,14 +1621,19 @@ void add_tileref( char buf[BUFSZ]; if (!tilelist[n]) { - tilelist[n] = malloc(sizeof temp); - tilelist[n]->tilenum = n; - tilelist[n]->src = src; - tilelist[n]->file_entry = entrynum; - /* leave room for trailing "...nnnn" */ - Snprintf(tilelist[n]->tilenam, sizeof tilelist[n]->tilenam - 7, - "%s%s", prefix, nam); - tilelist[n]->references[0] = '\0'; + if ((tilelist[n] = malloc(sizeof temp)) != 0) { + tilelist[n]->tilenum = n; + tilelist[n]->src = src; + tilelist[n]->file_entry = entrynum; + /* leave room for trailing "...nnnn" */ + Snprintf(tilelist[n]->tilenam, sizeof tilelist[n]->tilenam - 7, + "%s%s", prefix, nam); + tilelist[n]->references[0] = '\0'; + } else { + Fprintf(stderr, "tilemap malloc failure %zu bytes\n", + sizeof temp); + exit(EXIT_FAILURE); + } } Snprintf(temp.references, sizeof temp.references - 7, /* room for "...nnnn" */ @@ -1674,28 +1677,4 @@ free_tilerefs(void) #endif -DISABLE_WARNING_FORMAT_NONLITERAL - -void -nh_snprintf( - const char *func UNUSED, - int line UNUSED, - char *str, size_t size, - const char *fmt, ...) -{ - va_list ap; - int n; - - va_start(ap, fmt); - n = vsnprintf(str, size, fmt, ap); - va_end(ap); - - if (n < 0 || (size_t) n >= size) { /* is there a problem? */ - str[size - 1] = 0; /* make sure it is nul terminated */ - } -} - -RESTORE_WARNING_FORMAT_NONLITERAL - - /*tilemap.c*/ diff --git a/win/share/tiletext.c b/win/share/tiletext.c index 6ad8a098dc..8b3f261eb2 100644 --- a/win/share/tiletext.c +++ b/win/share/tiletext.c @@ -71,10 +71,10 @@ enum { MONSTER_SET, OBJECT_SET, OTHER_SET}; [21] U = (205,205,205) grayshade maps to [20] or T for shade of gray [22] V = (104, 104, 104) grayshade maps to [27] or 0 for shade of gray [23] W = (131, 131, 131) grayshade maps to [22] or V for shade of gray - [24] X = (140, 140, 140) grayshade not mapped in graymappings - [25] Y = (149, 149, 149) grayshade not mapped in graymappings - [26] Z = (195, 195, 195) grayshade not mapped in graymappings - [27] 0 = (100, 100, 100) grayshade not mapped in graymappings + [24] X = (140, 140, 140) grayshade maps to [23] or W for shade of gray + [25] Y = (149, 149, 149) grayshade maps to [24] or X for shade of gray + [26] Z = (195, 195, 195) grayshade maps to [25] or Y for shade of gray + [27] 0 = (100, 100, 100) grayshade maps to [20] or T for shade of gray [28] 1 = (72, 108, 108) not mapped in graymappings ----------------------------------------------------------------------------- */ @@ -84,8 +84,8 @@ static int grayscale = 0; static const int graymappings[] = { /* . A B C D E F G H I J K L M N O P */ 0, 1, 17, 18, 19, 20, 27, 22, 23, 24, 25, 26, 21, 15, 13, 14, 14, - /* Q R S T U V W */ - 1, 17, 18, 19, 20, 27, 22 + /* Q R S T U V W X Y Z 0 */ + 1, 17, 18, 19, 20, 27, 22, 23, 24, 25, 20 }; void @@ -199,6 +199,9 @@ read_txttile(FILE *txtfile, pixel (*pixels)[TILE_X]) if (reslt <= 0) goto done; + ttype[sizeof ttype - 1] = '\0'; + buf[sizeof buf - 1] = '\0'; + if (tile_set == MONSTER_SET && gend[0] == 'f') gidx = 1; @@ -262,10 +265,14 @@ read_txttile(FILE *txtfile, pixel (*pixels)[TILE_X]) } if (k == -1) { Fprintf(stderr, "color %c not in colormap!\n", inbuf[i]); - } else { + } else if (k >= 0 && k < MAXCOLORMAPSIZE) { pixels[j][i].r = ColorMap[CM_RED][k]; pixels[j][i].g = ColorMap[CM_GREEN][k]; pixels[j][i].b = ColorMap[CM_BLUE][k]; + } else { + Fprintf(stderr, + "%d exceeds array boundary for ColorMap[][%d]!\n", + k, MAXCOLORMAPSIZE); } } } @@ -382,7 +389,7 @@ fopen_text_file(const char *filename, const char *type) int i; if (tile_file != (FILE *) 0) { - Fprintf(stderr, "can only open one text file at at time\n"); + Fprintf(stderr, "can only open one text file at a time\n"); return FALSE; } diff --git a/win/shim/winshim.c b/win/shim/winshim.c index 59046fd5b8..81bc7411d1 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), - "vippiiisi", + "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,21 +158,18 @@ 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__ @@ -183,10 +180,18 @@ 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, + int request UNUSED, win_request_info *wri UNUSED) { return (win_request_info *) 0; } @@ -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) @@ -213,11 +219,7 @@ struct window_procs shim_procs = { | WC2_RESET_STATUS #endif | WC2_DARKGRAY | WC2_SUPPRESS_HIST | WC2_STATUSLINES), -#ifdef TEXTCOLOR {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ -#else - {1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1}, -#endif shim_init_nhwindows, shim_player_selection, shim_askname, shim_get_nh_event, shim_exit_nhwindows, shim_suspend_nhwindows, shim_resume_nhwindows, shim_create_nhwindow, shim_clear_nhwindow, shim_display_nhwindow, @@ -242,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, @@ -266,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); @@ -291,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 ff797e30a1..1808672d27 100644 --- a/win/tty/getline.c +++ b/win/tty/getline.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 getline.c $NHDT-Date: 1596498343 2020/08/03 23:45:43 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.44 $ */ +/* NetHack 3.7 getline.c $NHDT-Date: 1701285885 2023/11/29 19:24:45 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.59 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -28,23 +28,27 @@ extern char erase_char, kill_char; /* from appropriate tty.c file */ /* * Read a line closed with '\n' into the array char bufp[BUFSZ]. * (The '\n' is not stored. The string is closed with a '\0'.) - * Reading can be interrupted by an escape ('\033') - now the - * resulting string is "\033". + * Reading can be interrupted by an escape ('\033'). If there is already + * some text, it is removed and prompting continues as if from the start. + * However, if there is no text yet (or anymore) then "\033" is returned. */ void -tty_getlin(const char *query, register char *bufp) +tty_getlin(const char *query, char *bufp) { suppress_history = FALSE; hooked_tty_getlin(query, bufp, (getlin_hook_proc) 0); } static void -hooked_tty_getlin(const char *query, register char *bufp, getlin_hook_proc hook) +hooked_tty_getlin( + const char *query, + char *bufp, + getlin_hook_proc hook) { - register char *obufp = bufp; - register int c; + char *obufp = bufp; + int c; struct WinDesc *cw = wins[WIN_MESSAGE]; - boolean doprev = 0; + boolean doprev = FALSE; if (ttyDisplay->toplin == TOPLINE_NEED_MORE && !(cw->flags & WIN_STOP)) more(); @@ -52,8 +56,16 @@ hooked_tty_getlin(const char *query, register char *bufp, getlin_hook_proc hook) ttyDisplay->toplin = TOPLINE_SPECIAL_PROMPT; ttyDisplay->inread++; - /* issue the prompt */ + /* + * Issue the prompt. + * + * custompline() will call vpline() which calls flush_screen() which + * calls bot(). The core now disables bot() processing while inside + * getlin, so the screen won't be modified during whatever this prompt + * is for. + */ custompline(OVERRIDE_MSGTYPE | SUPPRESS_HISTORY, "%s ", query); + #ifdef EDIT_GETLIN /* bufp is input/output; treat current contents (presumed to be from previous getlin()) as default input */ @@ -67,7 +79,9 @@ hooked_tty_getlin(const char *query, register char *bufp, getlin_hook_proc hook) for (;;) { (void) fflush(stdout); Strcat(strcat(strcpy(gt.toplines, query), " "), obufp); + term_curs_set(1); c = pgetchar(); + term_curs_set(0); if (c == '\033' || c == EOF) { if (c == '\033' && obufp[0] != '\0') { obufp[0] = '\0'; @@ -87,30 +101,37 @@ hooked_tty_getlin(const char *query, register char *bufp, getlin_hook_proc hook) ttyDisplay->intr--; *bufp = 0; } - if (c == '\020') { /* ctrl-P */ - if (iflags.prevmsg_window != 's') { - int sav = ttyDisplay->inread; - - ttyDisplay->inread = 0; + if (c == C('p')) { /* ctrl-P, doesn't honor rebinding #prevmsg cmd */ + int sav = ttyDisplay->inread; + + ttyDisplay->inread = 0; + if (iflags.prevmsg_window == 's' + || (iflags.prevmsg_window == 'c' && !doprev)) { + /* msg_window:single, or msg_window:combination while it's + behaving like msg_window:single */ + if (!doprev) + (void) tty_doprev_message(); /* need two initially */ + (void) tty_doprev_message(); + ttyDisplay->inread = sav; + doprev = TRUE; + continue; + } else { + /* msg_window:full or reverse, or msg_window:combination while + it's behaving like msg_window:full */ (void) tty_doprev_message(); ttyDisplay->inread = sav; + doprev = FALSE; tty_clear_nhwindow(WIN_MESSAGE); cw->maxcol = cw->maxrow; addtopl(query); addtopl(" "); *bufp = 0; addtopl(obufp); - } else { - if (!doprev) - (void) tty_doprev_message(); /* need two initially */ - (void) tty_doprev_message(); - doprev = 1; - continue; } - } else if (doprev && iflags.prevmsg_window == 's') { + } else if (doprev) { tty_clear_nhwindow(WIN_MESSAGE); cw->maxcol = cw->maxrow; - doprev = 0; + doprev = FALSE; addtopl(query); addtopl(" "); *bufp = 0; @@ -195,7 +216,7 @@ hooked_tty_getlin(const char *query, register char *bufp, getlin_hook_proc hook) /* prevent next message from pushing current query+answer into tty message history */ *gt.toplines = '\0'; -#ifdef DUMPLOG +#ifdef DUMPLOG_CORE } else { /* needed because we've bypassed pline() */ dumplogmsg(gt.toplines); @@ -204,14 +225,14 @@ hooked_tty_getlin(const char *query, register char *bufp, getlin_hook_proc hook) } void -xwaitforspace(register const char *s) /* chars allowed besides return */ +xwaitforspace(const char *s) /* chars allowed besides return */ { - register int c, x = ttyDisplay ? (int) ttyDisplay->dismiss_more : '\n'; + int c, x = ttyDisplay ? (int) ttyDisplay->dismiss_more : '\n'; morc = 0; while ( #ifdef HANGUPHANDLING - !gp.program_state.done_hup && + !program_state.done_hup && #endif (c = tty_nhgetch()) != EOF) { if ((c == '\n' || c == '\r') && !iflags.msg_is_alert) diff --git a/win/tty/termcap.c b/win/tty/termcap.c index e26b6b5d27..3af6333f9b 100644 --- a/win/tty/termcap.c +++ b/win/tty/termcap.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 termcap.c $NHDT-Date: 1609459769 2021/01/01 00:09:29 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.41 $ */ +/* NetHack 3.7 termcap.c $NHDT-Date: 1701946349 2023/12/07 10:52:29 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.60 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Pasi Kallinen, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -7,6 +7,9 @@ #if defined(TTY_GRAPHICS) && !defined(NO_TERMS) +/* leave this undefined; it produces bad screen output with rxvt-unicode */ +/*#define DECgraphicsOptimization*/ + #include "wintty.h" #include "tcap.h" @@ -17,15 +20,15 @@ static char *e_atr2str(int); void cmov(int, int); void nocmov(int, int); -void term_start_24bitcolor(struct unicode_representation *); -void term_end_24bitcolor(void); +void term_start_extracolor(uint32, uint16); +void term_end_extracolor(void); -#if defined(TEXTCOLOR) && defined(TERMLIB) +#if defined(TERMLIB) #if (!defined(UNIX) || !defined(TERMINFO)) && !defined(TOS) static void analyze_seq(char *, int *, int *); #endif #endif -#if defined(TEXTCOLOR) && (defined(TERMLIB) || defined(ANSI_DEFAULT)) +#if (defined(TERMLIB) || defined(ANSI_DEFAULT)) static void init_hilite(void); static void kill_hilite(void); #endif @@ -33,6 +36,10 @@ static void kill_hilite(void); /* (see tcap.h) -- nh_CM, nh_ND, nh_CD, nh_HI,nh_HE, nh_US,nh_UE, ul_hack */ struct tc_lcl_data tc_lcl_data = { 0, 0, 0, 0, 0, 0, 0, FALSE }; +static char *nh_VI = (char *) 0; /* cursor_invisible */ +static char *nh_VE = (char *) 0; /* cursor_normal */ +/*static char *nh_VS = (char *) 0;*/ /* cursor_visible (highlighted cursor) */ + static char *HO, *CL, *CE, *UP, *XD, *BC, *SO, *SE, *TI, *TE; static char *VS, *VE; static char *ME, *MR, *MB, *MH, *MD; @@ -45,13 +52,11 @@ static char PC = '\0'; static char tbuf[512]; #endif /*TERMLIB*/ -#ifdef TEXTCOLOR #ifdef TOS const char *hilites[CLR_MAX]; /* terminal escapes for the various colors */ #else char NEARDATA *hilites[CLR_MAX]; /* terminal escapes for the various colors */ #endif -#endif static char *KS = (char *) 0, *KE = (char *) 0; /* keypad sequences */ static char nullstr[] = ""; @@ -73,11 +78,11 @@ 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 - register const char *term; - register char *tptr; + const char *term; + char *tptr; char *tbufptr, *pc; int i; @@ -160,9 +165,7 @@ tty_startup(int *wid, int *hgt) AE = nhStr("\017"); #endif TE = VS = VE = nullstr; -#ifdef TEXTCOLOR init_hilite(); -#endif /* TEXTCOLOR */ *wid = CO; *hgt = LI; CL = nhStr("\033[2J"); /* last thing set */ @@ -281,6 +284,12 @@ tty_startup(int *wid, int *hgt) if (!ME) ME = SE ? SE : nullstr; /* default to SE value */ + nh_VI = Tgetstr(nhStr("vi")); + nh_VE = Tgetstr(nhStr("ve")); + /*nh_VS = Tgetstr(nhStr("vs"));*/ + if (!nh_VI || !nh_VE /*|| !nh_VS*/ ) + nh_VI = nh_VE = /*nh_VS =*/ (char *) 0; + /* Get rid of padding numbers for nh_HI and nh_HE. Hope they * aren't really needed!!! nh_HI and nh_HE are outputted to the * pager as a string - so how can you send it NULs??? @@ -297,7 +306,6 @@ tty_startup(int *wid, int *hgt) AS = Tgetstr(nhStr("as")); /* alt charset start */ AE = Tgetstr(nhStr("ae")); /* alt charset end */ nh_CD = Tgetstr(nhStr("cd")); /* clear lines from cursor and down */ -#ifdef TEXTCOLOR #if defined(TOS) && defined(__GNUC__) if (!strcmp(term, "builtin") || !strcmp(term, "tw52") || !strcmp(term, "st52")) { @@ -305,7 +313,6 @@ tty_startup(int *wid, int *hgt) } #else init_hilite(); -#endif #endif *wid = CO; *hgt = LI; @@ -329,10 +336,10 @@ 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(TEXTCOLOR) && (defined(TERMLIB) || defined(ANSI_DEFAULT)) +#if defined(TERMLIB) || defined(ANSI_DEFAULT) kill_hilite(); #endif #ifdef TERMLIB @@ -396,43 +403,78 @@ 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)) - xputs("\033)0"); + 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 #if defined(ASCIIGRAPH) && !defined(NO_TERMS) +#if DECgraphicsOptimization /* some termcaps suffer from the bizarre notion that resetting video attributes should also reset the chosen character set */ - { - const char *nh_he = nh_HE, *ae = AE; - int he_limit, ae_length; + if (dynamic_HIHE) { + assert(nh_HE != NULL); + xputs(nh_HE); /* turn off any active highlighting (before maybe + * changing HE or AE) */ + (void) strsubst(nh_HE, AE, ""); + (void) strsubst(nh_HE, ctrlO, ""); + /* if AE has prefixing, substituting an empty string for it in HE + would only work if it is a leading prefix of HE; remove the + magic sequences that loads US set ("\e(B") or UK set ("\e(A") + into the primary character set since we don't want HE to do that */ + (void) strsubst(nh_HE, "\033(B", ""); + (void) strsubst(nh_HE, "\033(A", ""); + } +#endif - if (digit(*ae)) { /* skip over delay prefix, if any */ - do - ++ae; - while (digit(*ae)); - if (*ae == '.') { + /* if AE is still present in HE, set a flag so that glyph writing + code will know that AS needs to be refreshed for consecutive + line drawing characters */ + const char *ae = AE; + + if (digit(*ae)) { /* skip over delay prefix, if any */ + do + ++ae; + while (digit(*ae)); + if (*ae == '.') { + ++ae; + if (digit(*ae)) ++ae; - if (digit(*ae)) - ++ae; - } - if (*ae == '*') - ++ae; - } - /* can't use nethack's case-insensitive strstri() here, and some old - systems don't have strstr(), so use brute force substring search */ - ae_length = strlen(ae), he_limit = strlen(nh_he); - while (he_limit >= ae_length) { - if (strncmp(nh_he, ae, ae_length) == 0) { - HE_resets_AS = TRUE; - break; - } - ++nh_he, --he_limit; } + if (*ae == '*') + ++ae; } -#endif + /* stdc strstr(), not nethack's strstri(); HE ends color, ME ends + inverse video; they might have the same value; sequences to end + other attributes aren't known to sometimes contain AE */ + if ((nh_HE && strstr(nh_HE, ae)) || (ME && strstr(ME, ae))) + HE_resets_AS = TRUE; + +#ifdef DECgraphicsOptimization + /* some termcaps have AS load the line-drawing character set as + primary instead of having initialization load it as secondary + (we've already done that init) and then having AS simply switch + to secondary (change to do that now); they also have AE load + the US character set, which we avoid by not touching primary; + if HE_resets_AS, we can't simplify AS/AE due to the risk that + HE is changing the primary set rather than just toggling to it */ + if (!HE_resets_AS && !strcmp(AS, "\033(0") && !strcmp(AE, "\033(B")) { + /* first output old AE to make sure we aren't about to leave + primary set with line drawing chars */ + xputs(AE); + AS = ctrlN; + AE = ctrlO; + } +#endif /* DECgraphicsOptimization */ + xputs(AE); +#endif /* ASCIIGRAPH && !NO_TERMS */ } #endif /* TERMLIB */ @@ -448,7 +490,7 @@ static void tty_ascgraphics_hilite_fixup(void); static void tty_ascgraphics_hilite_fixup(void) { - register int c; + int c; for (c = 0; c < CLR_MAX / 2; c++) if (c != CLR_BLACK) { @@ -463,7 +505,7 @@ tty_ascgraphics_hilite_fixup(void) #endif /* PC9800 */ void -tty_start_screen(void) +term_start_screen(void) { xputs(TI); xputs(VS); @@ -495,7 +537,7 @@ tty_start_screen(void) } void -tty_end_screen(void) +term_end_screen(void) { term_clear_screen(); xputs(VE); @@ -560,7 +602,7 @@ nocmov(int x, int y) } void -cmov(register int x, register int y) +cmov(int x, int y) { xputs(tgoto(nh_CM, x, y)); ttyDisplay->cury = y; @@ -581,7 +623,7 @@ xputc(int c) /* actually char, but explicitly specify its widened type */ * * xputc() used to be declared as 'void xputc(c) char c; {}' but * avoiding the proper type 'int' just to avoid (void) casts when - * ignoring the result can't have been sufficent reason to add it. + * ignoring the result can't have been sufficient reason to add it. * It also had '#if apollo' conditional to have the arg be int. * Matching putchar()'s declaration and using explicit casts where * warranted is more robust, so we're just a jacket around that. @@ -605,7 +647,7 @@ cl_end(void) if (CE) { xputs(CE); } else { /* no-CE fix - free after Harold Rynes */ - register int cx = ttyDisplay->curx + 1; + int cx = ttyDisplay->curx + 1; /* this looks terrible, especially on a slow terminal but is better than nothing */ @@ -622,11 +664,13 @@ 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); home(); + /* set remembered data to all spaces */ + erase_tty_screen(); } } @@ -744,7 +788,7 @@ void tty_delay_output(void) { #if defined(MICRO) - register int i; + int i; #endif if (iflags.debug_fuzzer) return; @@ -775,9 +819,9 @@ tty_delay_output(void) } else if (ospeed > 0 && ospeed < SIZE(tmspc10) && nh_CM) { /* delay by sending cm(here) an appropriate number of times */ - register int cmlen = + int cmlen = (int) strlen(tgoto(nh_CM, ttyDisplay->curx, ttyDisplay->cury)); - register int i = 500 + tmspc10[ospeed] / 2; + int i = 500 + tmspc10[ospeed] / 2; while (i > 0) { cmov((int) ttyDisplay->curx, (int) ttyDisplay->cury); @@ -794,7 +838,7 @@ cl_eos(void) /* free after Robert Viduya */ if (nh_CD) { xputs(nh_CD); } else { - register int cy = ttyDisplay->cury + 1; + int cy = ttyDisplay->cury + 1; while (cy <= LI - 2) { cl_end(); @@ -807,7 +851,7 @@ cl_eos(void) /* free after Robert Viduya */ } } -#if defined(TEXTCOLOR) && defined(TERMLIB) +#if defined(TERMLIB) #if defined(UNIX) && defined(TERMINFO) /* * Sets up color highlighting, using terminfo(4) escape sequences. @@ -839,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 @@ -897,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 @@ -925,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--) { @@ -958,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); @@ -974,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 @@ -988,7 +1023,7 @@ init_hilite(void) Strcat(hilites[CLR_BLACK], scratch); } } else { - /* But it's concievable that hilighted black-on-black could + /* But it's conceivable that hilighted black-on-black could * still be invisible on many others. We substitute blue for * black. */ @@ -1005,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]) @@ -1048,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 */ @@ -1058,7 +1095,7 @@ kill_hilite(void) static void analyze_seq(char *str, int *fg, int *bg) { - register int c, code; + int c, code; int len; #ifdef MICRO @@ -1112,14 +1149,14 @@ 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. */ static void init_hilite(void) { - register int c; + int c; #ifdef TOS extern unsigned long tos_numcolors; /* in tos.c */ static char NOCOL[] = "\033b0", COLHE[] = "\033q\033b0"; @@ -1135,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'); @@ -1178,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)) @@ -1202,7 +1240,7 @@ static void kill_hilite(void) { #ifndef TOS - register int c; + int c; for (c = 0; c < CLR_MAX / 2; c++) { if (hilites[c | BRIGHT] == hilites[c]) @@ -1216,15 +1254,15 @@ kill_hilite(void) return; } #endif /* UNIX && TERMINFO */ -#endif /* TEXTCOLOR && TERMLIB */ +#endif /* TERMLIB */ -#if defined(TEXTCOLOR) && !defined(TERMLIB) && defined(ANSI_DEFAULT) +#if !defined(TERMLIB) && defined(ANSI_DEFAULT) static char adef_nilstring[] = ""; static void init_hilite(void) { - register int c; + int c; if (!hilites[CLR_BLACK]) hilites[CLR_BLACK] = adef_nilstring; @@ -1254,7 +1292,7 @@ init_hilite(void) } } - /* See TEXTCOLOR && TERMLIB && UNIX && TERMINFO code above. */ + /* See TERMLIB && UNIX && TERMINFO code above. */ if (iflags.wc2_darkgray) { /* Bright black is dark gray. */ hilites[CLR_BLACK] = (char *) alloc(sizeof "\033[1;30m"); @@ -1268,7 +1306,7 @@ init_hilite(void) static void kill_hilite(void) { - register int c; + int c; for (c = 0; c < CLR_MAX / 2; c++) { if (c == CLR_GRAY || hilites[c] == adef_nilstring) @@ -1291,7 +1329,7 @@ kill_hilite(void) hilites[CLR_BLACK] = 0; } } -#endif /* TEXTCOLOR && !TERMLIB && ANSI_DEFAULT */ +#endif /* !TERMLIB && ANSI_DEFAULT */ static char nulstr[] = ""; @@ -1303,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: @@ -1313,6 +1352,7 @@ s_atr2str(int n) if (nh_US && *nh_US) return nh_US; } + FALLTHROUGH; /*FALLTHRU*/ case ATR_BOLD: if (MD && *MD) @@ -1340,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: @@ -1424,7 +1467,6 @@ term_end_raw_bold(void) xputs(soOff); } -#ifdef TEXTCOLOR void term_end_color(void) { @@ -1434,7 +1476,9 @@ term_end_color(void) void term_start_color(int color) { - if (color < CLR_MAX && hilites[color] && *hilites[color]) + if (color == NO_COLOR) + xputs(nh_HE); /* inline term_end_color() */ + else if (color < CLR_MAX && hilites[color] && *hilites[color]) xputs(hilites[color]); } @@ -1445,9 +1489,31 @@ term_start_bgcolor(int color) Sprintf(tmp, "\033[%dm", ((color % 8) + 40)); xputs(tmp); } -#endif /* TEXTCOLOR */ -#ifdef ENHANCED_SYMBOLS +/* hide or show cursor */ +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 +void +tty_change_color(int color UNUSED, long rgb UNUSED, int reverse UNUSED) +{ + return; +} +#endif /* CHANGE_COLOR */ + #ifndef SEP2 #define tcfmtstr "\033[38;2;%ld;%ld;%ldm" @@ -1456,7 +1522,7 @@ term_start_bgcolor(int color) #define tcfmtstr256 "\033[38;5;%dm" #else #define tcfmtstr24bit "\033[38;2;%lu;%lu;%lum" -#define tcfmtstr256 "\033[38:5:%ldm" +#define tcfmtstr256 "\033[38:5:%lum" #endif #endif @@ -1474,36 +1540,31 @@ static void emit24bit(long mcolor) xputs(tcolorbuf); } -static void emit256(int u256coloridx) +static void emit256(int color256idx) { static char tcolorbuf[QBUFSZ]; Snprintf(tcolorbuf, sizeof tcolorbuf, tcfmtstr256, - u256coloridx); + color256idx); xputs(tcolorbuf); } void -term_start_24bitcolor(struct unicode_representation *urep) +term_start_extracolor(uint32 customcolor, uint16 color256idx) { - if (urep && SYMHANDLING(H_UTF8)) { - /* color 0 has bit 0x1000000 set */ - long mcolor = (urep->ucolor & 0xFFFFFF); - if (iflags.colorcount == 256) - emit256(urep->u256coloridx); - else - emit24bit(mcolor); - } + /* color 0 has bit 0x1000000 set */ + long mcolor = (customcolor & 0xFFFFFF); + if (iflags.colorcount == 256) + emit256(color256idx); + else + emit24bit(mcolor); } void -term_end_24bitcolor(void) +term_end_extracolor(void) { - if (SYMHANDLING(H_UTF8)) { xputs("\033[0m"); - } } -#endif /* ENHANCED_SYMBOLS */ #endif /* TTY_GRAPHICS && !NO_TERMS */ /*termcap.c*/ diff --git a/win/tty/topl.c b/win/tty/topl.c index d8ac0287e0..e61c07562e 100644 --- a/win/tty/topl.c +++ b/win/tty/topl.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 topl.c $NHDT-Date: 1596498344 2020/08/03 23:45:44 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.72 $ */ +/* NetHack 3.7 topl.c $NHDT-Date: 1717967339 2024/06/09 21:08:59 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.89 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2009. */ /* NetHack may be freely redistributed. See license for details. */ @@ -19,7 +19,7 @@ static void free_msghistory_snapshot(boolean); int tty_doprev_message(void) { - register struct WinDesc *cw = wins[WIN_MESSAGE]; + struct WinDesc *cw = wins[WIN_MESSAGE]; winid prevmsg_win; int i; @@ -125,7 +125,7 @@ redotoplin(const char *str) home(); if (!ttyDisplay->topl_utf8) { - if (*str & 0x80) { + if (ttyDisplay->mixed && (*str & 0x80)) { /* kludge for the / command, the only time we ever want a */ /* graphics character on the top line */ g_putch((int) *str++); @@ -169,7 +169,7 @@ show_topl(const char *str) void remember_topl(void) { - register struct WinDesc *cw = wins[WIN_MESSAGE]; + struct WinDesc *cw = wins[WIN_MESSAGE]; int idx = cw->maxrow; unsigned len = strlen(gt.toplines) + 1; @@ -184,7 +184,7 @@ remember_topl(void) cw->datlen[idx] = (short) len; } Strcpy(cw->data[idx], gt.toplines); - if (!gp.program_state.in_checkpoint) { + if (!program_state.in_checkpoint) { *gt.toplines = '\0'; cw->maxcol = cw->maxrow = (idx + 1) % cw->rows; } @@ -254,10 +254,10 @@ more(void) } void -update_topl(register const char *bp) +update_topl(const char *bp) { - register char *tl, *otl; - register int n0; + char *tl, *otl; + int n0; int notdied = 1; struct WinDesc *cw = wins[WIN_MESSAGE]; boolean skip = (cw->flags & (WIN_STOP | WIN_NOSTOP)) == WIN_STOP; @@ -310,7 +310,7 @@ update_topl(register const char *bp) static void topl_putsym(char c) { - register struct WinDesc *cw = wins[WIN_MESSAGE]; + struct WinDesc *cw = wins[WIN_MESSAGE]; if (cw == (struct WinDesc *) 0) panic("Putsym window MESSAGE nonexistent"); @@ -357,7 +357,7 @@ putsyms(const char *str) } static void -removetopl(register int n) +removetopl(int n) { /* assume addtopl() has been done, so ttyDisplay->toplin is already set */ while (n-- > 0) @@ -385,7 +385,7 @@ tty_yn_function( * are allowed). If it includes an , anything beyond that won't * be shown in the prompt to the user but will be acceptable as input. */ - register char q; + char q; char rtmp[40]; boolean digit_ok, allow_num, preserve_case = FALSE; struct WinDesc *cw = wins[WIN_MESSAGE]; @@ -499,7 +499,10 @@ tty_yn_function( if (!preserve_case) z = lowc(z); if (digit(z)) { - value = (10 * value) + (z - '0'); + long dgt = (long) (z - '0'); + + /* value = (10 * value) + (z - '0'); */ + value = AppendLongDigit(value, dgt); if (value < 0) break; /* overflow: try again */ digit_string[0] = z; @@ -540,7 +543,7 @@ tty_yn_function( (void) key2txt(q, rtmp); /* addtopl(rtmp); -- rewrite gt.toplines instead */ Sprintf(gt.toplines, "%s%s", prompt, rtmp); -#ifdef DUMPLOG +#ifdef DUMPLOG_CORE dumplogmsg(gt.toplines); #endif ttyDisplay->inread--; @@ -588,7 +591,7 @@ msghistory_snapshot( if (mesg && *mesg) { snapshot_mesgs[outidx++] = mesg; if (purge) { - /* we're taking this pointer away; subsequest history + /* we're taking this pointer away; subsequent history updates will eventually allocate a new one to replace it */ cw->data[inidx] = (char *) 0; cw->datlen[inidx] = 0; @@ -690,7 +693,7 @@ tty_putmsghistory(const char *msg, boolean restoring_msghist) restored ones are being put into place */ msghistory_snapshot(TRUE); initd = TRUE; -#ifdef DUMPLOG +#ifdef DUMPLOG_CORE /* this suffices; there's no need to scrub saved_pline[] pointers */ gs.saved_pline_index = 0; #endif @@ -707,7 +710,7 @@ tty_putmsghistory(const char *msg, boolean restoring_msghist) /* move most recent message to history, make this become most recent */ remember_topl(); Strcpy(gt.toplines, msg); -#ifdef DUMPLOG +#ifdef DUMPLOG_CORE dumplogmsg(gt.toplines); #endif } else if (snapshot_mesgs) { @@ -718,7 +721,7 @@ tty_putmsghistory(const char *msg, boolean restoring_msghist) for (idx = 0; snapshot_mesgs[idx]; ++idx) { remember_topl(); Strcpy(gt.toplines, snapshot_mesgs[idx]); -#ifdef DUMPLOG +#ifdef DUMPLOG_CORE dumplogmsg(gt.toplines); #endif } diff --git a/win/tty/wintty.c b/win/tty/wintty.c index d00d7684dc..f8d73fe886 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 wintty.c $NHDT-Date: 1661295670 2022/08/23 23:01:10 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.326 $ */ +/* NetHack 3.7 wintty.c $NHDT-Date: 1717967340 2024/06/09 21:09:00 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.410 $ */ /* Copyright (c) David Cohrs, 1991 */ /* NetHack may be freely redistributed. See license for details. */ @@ -17,6 +17,9 @@ #ifdef TTY_GRAPHICS #include "dlb.h" +/* leave this undefined; it produces bad screen output with rxvt-unicode */ +/*#define DECgraphicsOptimization*/ + #ifdef MAC #define MICRO /* The Mac is a MICRO only for this file, not in general! */ #ifdef THINK_C @@ -24,21 +27,16 @@ 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 #include "wintty.h" -#ifdef CLIPPING /* might want SIGWINCH */ -#if defined(BSD) || defined(ULTRIX) || defined(AIX_31) || defined(_BULL_SOURCE) +#if defined(CLIPPING) && !defined(NO_SIGNAL) #include +#ifdef SIGWINCH +#define RESIZABLE #endif #endif @@ -80,7 +78,7 @@ extern void msmsg(const char *, ...); */ #define HUPSKIP() \ do { \ - if (gp.program_state.done_hup) { \ + if (program_state.done_hup) { \ morc = '\033'; \ return; \ } \ @@ -88,7 +86,7 @@ extern void msmsg(const char *, ...); /* morc=ESC - in case we bypass xwaitforspace() which sets that */ #define HUPSKIP_RESULT(RES) \ do { \ - if (gp.program_state.done_hup) \ + if (program_state.done_hup) \ return (RES); \ } while (0) #else /* !HANGUPHANDLING */ @@ -119,16 +117,12 @@ struct window_procs tty_procs = { | WC2_RESET_STATUS #endif | WC2_DARKGRAY | WC2_SUPPRESS_HIST | WC2_URGENT_MESG | WC2_STATUSLINES - | WC2_U_UTF8STR + | WC2_U_UTF8STR | WC2_PETATTR #if !defined(NO_TERMS) || defined(WIN32CON) - | WC2_U_24BITCOLOR + | WC2_EXTRACOLORS #endif ), -#ifdef TEXTCOLOR {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ -#else - {1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1}, -#endif tty_init_nhwindows, tty_player_selection, tty_askname, tty_get_nh_event, tty_exit_nhwindows, tty_suspend_nhwindows, tty_resume_nhwindows, tty_create_nhwindow, tty_clear_nhwindow, tty_display_nhwindow, @@ -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, @@ -177,11 +170,14 @@ struct DisplayDesc *ttyDisplay; /* the tty display descriptor */ extern void cmov(int, int); /* from termcap.c */ extern void nocmov(int, int); /* from termcap.c */ +static volatile int erasing_tty_screen; /* volatile: SIGWINCH */ + #if defined(UNIX) || defined(VMS) static char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */ #endif -static char winpanicstr[] = "Bad window id %d"; +static const char winpanicstr[] = "Bad window Id %d (%s)"; +#define ttywindowpanic() panic(winpanicstr, window, __func__) char defmorestr[] = "--More--"; #ifdef CLIPPING @@ -200,19 +196,15 @@ 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 -#if defined(MICRO) || defined(WIN32CON) -static const char to_continue[] = "to continue"; -#define getret() getreturn(to_continue) -#else -static void getret(void); -#endif static void bail(const char *); /* __attribute__((noreturn)) */ +static void newclipping(coordxy, coordxy); static void new_status_window(void); +static void getret(void); static void erase_menu_or_text(winid, struct WinDesc *, boolean); static void free_window_info(struct WinDesc *, boolean); static boolean toggle_menu_curr(winid, tty_menu_item *, int, boolean, @@ -232,8 +224,9 @@ static const char *compress_str(const char *); #ifndef STATUS_HILITES static void tty_putsym(winid, int, int, char); #endif -#ifdef STATUS_HILITES #define MAX_STATUS_ROWS 3 +#define StatusRows() ((iflags.wc2_statuslines <= 2) ? 2 : MAX_STATUS_ROWS) +#ifdef STATUS_HILITES static boolean check_fields(boolean forcefields, int sz[MAX_STATUS_ROWS]); static void render_status(void); static void tty_putstatusfield(const char *, int, int); @@ -250,34 +243,41 @@ static void status_sanity_check(void); void g_pututf8(uint8 *utf8str); #endif +static boolean calling_from_update_inventory = FALSE; #ifdef TTY_PERM_INVENT -static char Empty[1] = { '\0' }; -static struct tty_perminvent_cell zerottycell = { 0, 0, 0, { 0 }, 0 }; +static struct tty_perminvent_cell emptyttycell = { + 0, 0, 0, { 0 }, NO_COLOR + 1 +}; static glyph_info zerogi = { 0 }; static struct to_core zero_tocore = { 0 }; enum { border_left, border_middle, border_right, border_elements }; static int bordercol[border_elements] = { 0, 0, 0 }; /* left, middle, right */ static int ttyinvmode = InvNormal; /* enum is in wintype.h */ -static int inuse_only_start = 0; +static int inuse_only_start = 0; /* next slot to use for in-use-only mode */ static boolean done_tty_perm_invent_init = FALSE; -enum { tty_slots = 52 + 1 + 1 }; +enum { tty_slots = invlet_basic + invlet_gold + invlet_overflow }; /* 54 */ static boolean slot_tracker[tty_slots]; +static int ttyinv_slots_used = 0; /* 1-based, slot_trackter[0..slots-1] */ static long last_glyph_reset_when; #ifndef NOINVSYM /* invent.c */ #define NOINVSYM '#' #endif -static boolean calling_from_update_inventory = FALSE; static int ttyinv_create_window(int, struct WinDesc *); +static void ttyinv_remove_data(struct WinDesc *, boolean); static void ttyinv_add_menu(winid, struct WinDesc *, char ch, int attr, int clr, const char *str); +static int selector_to_slot(char ch, const int invflags, boolean *ignore); +static char slot_to_invlet(int, boolean); +static void ttyinv_inuse_fulllines(struct WinDesc *, int); +static void ttyinv_inuse_twosides(struct WinDesc *, int); +static void ttyinv_end_menu(int, struct WinDesc *); static void ttyinv_render(winid window, struct WinDesc *cw); static void tty_invent_box_glyph_init(struct WinDesc *cw); static boolean assesstty(enum inv_modes, short *, short *, long *, long *, long *, long *, long *); static void ttyinv_populate_slot(struct WinDesc *, int, int, - const char *, int32_t); -static int selector_to_slot(char ch, const int invflags, boolean *ignore); -#endif + const char *, uint32, int); +#endif /* TTY_PERM_INVENT */ /* * A string containing all the default commands -- to add to a list @@ -314,7 +314,7 @@ print_vt_code(int i, int c, int d) } } #else -# define print_vt_code(i, c, d) ; +#define print_vt_code(i, c, d) /*empty*/ #endif /* !TTY_TILES_ESCCODES */ #define print_vt_code1(i) print_vt_code((i), -1, -1) #define print_vt_code2(i,c) print_vt_code((i), (c), -1) @@ -334,9 +334,9 @@ print_vt_soundcode_idx(int idx, int v) idx, VT_ANSI_COMMAND); } } -#else -# define print_vt_soundcode_idx(idx, v) ; -#endif /* !TTY_SOUND_ESCCODES */ +#else /* !(USER_SOUNDS && TTY_SOUND_ESCCODES) */ +#define print_vt_soundcode_idx(idx, v) ; +#endif /* ?(USER_SOUNDS && TTY_SOUND_ESCCODES) */ /* clean up and quit */ static void @@ -348,84 +348,141 @@ bail(const char *mesg) /*NOTREACHED*/ } -#if defined(SIGWINCH) && defined(CLIPPING) && !defined(NO_SIGNAL) +#ifdef RESIZABLE static void winch_handler(int); +static void resize_tty(void); + +static volatile int resize_mesg = 0; - /* - * This really ought to just set a flag like the hangup handler does, - * then check the flag at "safe" times, in case the signal arrives - * while something fragile is executing. Better to have a brief period - * where display updates don't fit the new size than for tty internals - * to become corrupted. - * - * 'winch_seen' has been "notyet" for a long time.... - */ /* signal handler is called with at least 1 arg */ /*ARGUSED*/ static void winch_handler(int sig_unused UNUSED) { - int oldLI = LI, oldCO = CO, i; - register struct WinDesc *cw; - #ifdef WINCHAIN { #define WINCH_MESSAGE "(SIGWINCH)" if (wc_tracelogf) (void) write(fileno(wc_tracelogf), WINCH_MESSAGE, - strlen(WINCH_MESSAGE)); + sizeof WINCH_MESSAGE - sizeof ""); #undef WINCH_MESSAGE } #endif -#ifndef VMS - getwindowsz(); -#endif - /* For long running events such as multi-page menus and - * display_file(), we note the signal's occurance and - * hope the code there decides to handle the situation - * and reset the flag. There will be race conditions - * when handling this - code handlers so it doesn't matter. - */ -#ifdef notyet - winch_seen = TRUE; + + program_state.resize_pending++; /* resize_tty() will reset it */ + /* if nethack is waiting for input, which is the most likely scenario, + we will go ahead and respond to the resize immediately; otherwise, + tty_nhgetch() will do so the next time it's called */ + if (program_state.getting_char) { + resize_tty(); +#if 0 /* [this doesn't work as intended and seems to be unnecessary] */ + if (resize_mesg) { + /* resize_tty() put "Press a key to continue: " on top line */ + (void) tty_nhgetch(); /* recursion... */ + } #endif - if ((oldLI != LI || oldCO != CO) && ttyDisplay) { - ttyDisplay->rows = LI; - ttyDisplay->cols = CO; + } + return; +} - cw = wins[BASE_WINDOW]; - cw->rows = ttyDisplay->rows; - cw->cols = ttyDisplay->cols; +/* query the system for tty size (vis getwindowsz()), and adapt the game's + windows to match */ +static void +resize_tty(void) +{ + int oldLI = LI, oldCO = CO, mapx, mapy, oldtoplin, i; + boolean map_active; + struct WinDesc *cw; - if (iflags.window_inited) { - cw = wins[WIN_MESSAGE]; - cw->curx = cw->cury = 0; + /* reset to 0 rather than just decrement */ + program_state.resize_pending = 0; + resize_mesg = 0; - new_status_window(); - if (u.ux) { - i = ttyDisplay->toplin; - ttyDisplay->toplin = TOPLINE_EMPTY; - docrt(); - bot(); - ttyDisplay->toplin = i; - flush_screen(1); - if (i) { - addtopl(gt.toplines); - } else - for (i = WIN_INVEN; i < MAXWIN; i++) - if (wins[i] && wins[i]->active) { - /* cop-out */ - addtopl("Press Return to continue: "); - break; - } - (void) fflush(stdout); - if (i < 2) - flush_screen(1); + getwindowsz(); /* update LI and CO */ + if (!ttyDisplay || (LI == oldLI && CO == oldCO)) + return; + + ttyDisplay->rows = LI; + ttyDisplay->cols = CO; + + cw = wins[BASE_WINDOW]; + cw->rows = ttyDisplay->rows; + cw->cols = ttyDisplay->cols; + + if (!iflags.window_inited) + return; + + /* try to figure out where we'll want to leave the cursor */ + cw = (WIN_MAP != WIN_ERR) ? wins[WIN_MAP] : NULL; + map_active = (cw && cw->active); + if (!map_active) + mapx = 1, mapy = 0; + else if (gg.getposx < 1) + mapx = u.ux, mapy = u.uy; + else + mapx = gg.getposx, mapy = gg.getposy; + + oldtoplin = ttyDisplay->toplin; + ttyDisplay->toplin = TOPLINE_EMPTY; /* (before term_clear_screen()) */ + /* whatever used to be on the screen has become suspect; + blank it all out, then redo the display */ + term_clear_screen(); + new_status_window(); + + /* if the map window is shown, redisplay it (also status, perminv) */ + if (map_active) { + docrt_flags(docrtRefresh); + bot(); /* context.botlx=1 gets set by docrt() */ + for (i = 0; i < MAXWIN; ++i) { + if (i == BASE_WINDOW + || i == WIN_MAP /* docrt() updates it */ + || i == WIN_STATUS /* docrt()+bot() updates it */ +#ifdef TTY_PERM_INVENT + || i == WIN_INVEN /* docrt() again */ +#endif + || i == WIN_MESSAGE) /* we fake it here */ + continue; + if (wins[i] && wins[i]->active) { + /* cop-out */ + oldtoplin = TOPLINE_EMPTY; /* don't restore it below */ + ttyDisplay->toplin = TOPLINE_NON_EMPTY; + addtopl("Press a key to continue: "); + resize_mesg++; + break; } } + } /* wins[WIN_MAP].active */ + + if (oldtoplin != TOPLINE_EMPTY) { + ttyDisplay->toplin = oldtoplin; + addtopl(gt.toplines); } + if (map_active) { + newclipping(mapx, mapy); + tty_curs(WIN_MAP, mapx, mapy); + tty_display_nhwindow(WIN_MAP, FALSE); /*fflush(stdout)*/ + } + return; +} +#endif /* RESIZABLE */ + +static void +newclipping(coordxy x, coordxy y) +{ +#ifdef CLIPPING + if (CO < COLNO || LI < 1 + ROWNO + iflags.wc2_statuslines) { + setclipped(); /* sets clipping=TRUE */ + if (x) + tty_cliparound(x, y); + } else { + clipping = FALSE; + clipx = clipy = 0; + } +#else + nhUse(x + y); +#endif + return; } -#endif /* SIGWINCH && CLIPPING && !NO_SIGNAL */ /* destroy and recreate status window; extracted from winch_handler() and augmented for use by tty_preference_update() */ @@ -433,11 +490,9 @@ static void new_status_window(void) { if (WIN_STATUS != WIN_ERR) { - /* if it's shrinking, clear it before destroying so that + /* in case it's shrinking, clear it before destroying so that dropped portion won't show anything that's now becoming stale */ - if (wins[WIN_STATUS]->maxrow > iflags.wc2_statuslines) - tty_clear_nhwindow(WIN_STATUS); - + tty_clear_nhwindow(WIN_STATUS); tty_destroy_nhwindow(WIN_STATUS), WIN_STATUS = WIN_ERR; } /* frees some status tracking data */ @@ -448,18 +503,6 @@ new_status_window(void) #ifdef STATUS_HILITES status_initialize(REASSESS_ONLY); #endif - -#ifdef CLIPPING - if (u.ux) { - if (LI < 1 + ROWNO + iflags.wc2_statuslines) { - setclipped(); - tty_cliparound(u.ux, u.uy); - } else { - clipping = FALSE; - clipx = clipy = 0; - } - } -#endif } /*ARGSUSED*/ @@ -470,13 +513,13 @@ tty_init_nhwindows(int *argcp UNUSED, char **argv UNUSED) /* options aren't processed yet so wc2_statuslines might be 0; make sure that it has a reasonable value during tty setup */ - iflags.wc2_statuslines = (iflags.wc2_statuslines < 3) ? 2 : 3; + iflags.wc2_statuslines = StatusRows(); /* 2 or 3; 0 => 2 */ /* * 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) @@ -485,8 +528,9 @@ 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 */ ttyDisplay = (struct DisplayDesc *) alloc(sizeof (struct DisplayDesc)); @@ -497,10 +541,10 @@ tty_init_nhwindows(int *argcp UNUSED, char **argv UNUSED) ttyDisplay->curx = ttyDisplay->cury = 0; ttyDisplay->inmore = ttyDisplay->inread = ttyDisplay->intr = 0; ttyDisplay->dismiss_more = 0; -#ifdef TEXTCOLOR ttyDisplay->color = NO_COLOR; -#endif ttyDisplay->attrs = 0; + ttyDisplay->topl_utf8 = 0; + ttyDisplay->mixed = 0; /* set up the default windows */ BASE_WINDOW = tty_create_nhwindow(NHW_BASE); @@ -508,7 +552,7 @@ tty_init_nhwindows(int *argcp UNUSED, char **argv UNUSED) ttyDisplay->lastwin = WIN_ERR; -#if defined(SIGWINCH) && defined(CLIPPING) && !defined(NO_SIGNAL) +#ifdef RESIZABLE (void) signal(SIGWINCH, (SIG_RET_TYPE) winch_handler); #endif @@ -539,19 +583,23 @@ tty_init_nhwindows(int *argcp UNUSED, char **argv UNUSED) /* 'statuslines' defaults to set_in_config, allowed but invisible; make it dynamically settable if feasible, otherwise visible */ - if (tty_procs.wincap2 & WC2_STATUSLINES) + if ((tty_procs.wincap2 & WC2_STATUSLINES) != 0) set_wc2_option_mod_status(WC2_STATUSLINES, #ifndef CLIPPING - (LI < 1 + ROWNO + 2) ? set_gameview : + (LI <= 1 + ROWNO + 2) ? set_gameview : #endif - set_in_game); + set_in_game); } void tty_preference_update(const char *pref) { - if (!strcmp(pref, "statuslines") && iflags.window_inited) { + boolean newstatuslines = (!strcmp(pref, "statuslines") + && iflags.window_inited); + + if (newstatuslines) { new_status_window(); + newclipping(u.ux, u.uy); } #if defined(WIN32CON) @@ -570,6 +618,13 @@ tty_preference_update(const char *pref) if (WIN_INVEN != WIN_ERR) tty_invent_box_glyph_init(wins[WIN_INVEN]); } + /* if newstatuslines has been toggled between 2 and 3 or vice versa + then we want to reposition the perm_invent window to match */ + if (newstatuslines && WIN_INVEN != WIN_ERR) { + perm_invent_toggled(TRUE); /*TEMP?*/ + tty_destroy_nhwindow(WIN_INVEN), WIN_INVEN = WIN_ERR; + perm_invent_toggled(FALSE); /*TEMP?*/ + } #endif return; } @@ -585,7 +640,7 @@ tty_player_selection(void) } /* - * gp.plname is filled either by an option (-u Player or -uPlayer) or + * svp.plname is filled either by an option (-u Player or -uPlayer) or * explicitly (by being the wizard) or by askname. * It may still contain a suffix denoting the role, etc. * Always called after init_nhwindows() and before @@ -595,7 +650,7 @@ void tty_askname(void) { static const char who_are_you[] = "Who are you? "; - register int c, ct, tryct = 0; + int c, ct, tryct = 0; #ifdef SELECTSAVED if (iflags.wc2_selectsaved && !iflags.renameinprogress) @@ -603,10 +658,11 @@ tty_askname(void) case -1: bail("Until next time then..."); /* quit */ /*NOTREACHED*/ + break; case 0: break; /* no game chosen; start new game */ case 1: - return; /* gp.plname[] has been set */ + return; /* svp.plname[] has been set */ } #endif /* SELECTSAVED */ @@ -669,7 +725,7 @@ tty_askname(void) && !(c >= '0' && c <= '9' && ct > 0)) c = '_'; #endif - if (ct < (int) (sizeof gp.plname) - 1) { + if (ct < (int) (sizeof svp.plname) - 1) { #if defined(MICRO) #if defined(MSDOS) if (iflags.grmode) { @@ -680,13 +736,13 @@ tty_askname(void) #else (void) putchar(c); #endif - gp.plname[ct++] = c; + svp.plname[ct++] = c; #ifdef WIN32CON ttyDisplay->curx++; #endif } } - gp.plname[ct] = 0; + svp.plname[ct] = 0; } while (ct == 0); /* move to next line to simulate echo of user's */ @@ -703,10 +759,12 @@ tty_get_nh_event(void) return; } -#if !defined(MICRO) && !defined(WIN32CON) static void getret(void) { +#if defined(MICRO) || defined(WIN32CON) + getreturn("to continue"); +#else HUPSKIP(); xputs("\n"); if (flags.standout) @@ -717,12 +775,14 @@ getret(void) if (flags.standout) standoutend(); xwaitforspace(" "); -} #endif + iflags.raw_printed = 0; +} void tty_suspend_nhwindows(const char *str) { + term_curs_set(1); settty(str); /* calls end_screen, perhaps raw_print */ if (!str) tty_raw_print(""); /* calls fflush(stdout) */ @@ -732,10 +792,19 @@ void tty_resume_nhwindows(void) { gettty(); - setftty(); /* calls start_screen */ + setftty(); /* calls term_start_screen */ + term_curs_set(0); docrt(); } +#ifdef CHANGE_COLOR +char * +tty_get_color_string(void) +{ + return (char *) 0; +} +#endif /* CHANGE_COLOR */ + void tty_exit_nhwindows(const char *str) { @@ -770,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; } @@ -823,8 +887,8 @@ tty_create_nhwindow(int type) /* sanity check */ if (iflags.msg_history < 20) iflags.msg_history = 20; - else if (iflags.msg_history > 60) - iflags.msg_history = 60; + else if (iflags.msg_history > MAX_MSG_HISTORY) + iflags.msg_history = MAX_MSG_HISTORY; newwin->maxrow = newwin->rows = iflags.msg_history; newwin->maxcol = newwin->cols = 0; break; @@ -898,7 +962,10 @@ tty_create_nhwindow(int type) RESTORE_WARNING_UNREACHABLE_CODE static void -erase_menu_or_text(winid window, struct WinDesc *cw, boolean clear) +erase_menu_or_text( + winid window, + struct WinDesc *cw, + boolean clear) { if (cw->offx == 0) { if (cw->offy) { @@ -962,17 +1029,15 @@ free_window_info(struct WinDesc *cw, boolean free_data) } } -DISABLE_WARNING_FORMAT_NONLITERAL - void tty_clear_nhwindow(winid window) { int i, j, m, n; - register struct WinDesc *cw = 0; + struct WinDesc *cw = 0; HUPSKIP(); if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) - panic(winpanicstr, window); + ttywindowpanic(); ttyDisplay->lastwin = window; print_vt_code2(AVTC_SELECT_WINDOW, window); @@ -980,10 +1045,13 @@ tty_clear_nhwindow(winid window) switch (cw->type) { case NHW_MESSAGE: if (ttyDisplay->toplin != TOPLINE_EMPTY) { - home(); - cl_end(); - if (cw->cury) - docorner(1, cw->cury + 1, 0); + if (!erasing_tty_screen) { + home(); + cl_end(); + if (cw->cury) + docorner(1, cw->cury + 1, 0); + } + cw->curx = cw->cury = 0; ttyDisplay->toplin = TOPLINE_EMPTY; } break; @@ -991,36 +1059,55 @@ tty_clear_nhwindow(winid window) m = cw->maxrow; n = cw->cols; for (i = 0; i < m; ++i) { - tty_curs(window, 1, i); - cl_end(); - + if (!erasing_tty_screen) { + tty_curs(window, 1, i); + cl_end(); + } for (j = 0; j < n - 1; ++j) cw->data[i][j] = ' '; cw->data[i][n - 1] = '\0'; /*finalx[i][NOW] = finalx[i][BEFORE] = 0;*/ } - gc.context.botlx = 1; + disp.botlx = TRUE; break; case NHW_MAP: /* cheap -- clear the whole thing and tell nethack to redraw botl */ - gc.context.botlx = 1; + disp.botlx = TRUE; + FALLTHROUGH; /*FALLTHRU*/ case NHW_BASE: - term_clear_screen(); - /* [this should reset state for MESSAGE, MAP, and STATUS] */ + /* if erasing_tty_screen is True, calling sequence is + term_clear_screen -> erase_tty_screen -> tty_clear_nhwindow + so we don't call term_clear_screen again */ + if (!erasing_tty_screen) { + term_clear_screen(); + } + /* + * Neither map nor base window tracks what is currently shown so + * there's no stale data that would need to be changed to spaces. + */ break; case NHW_MENU: case NHW_TEXT: - if (cw->active) - erase_menu_or_text(window, cw, TRUE); - free_window_info(cw, FALSE); +#ifdef TTY_PERM_INVENT + case NHW_PERMINVENT: +#endif + if (!erasing_tty_screen) { + if (cw->active) + erase_menu_or_text(window, cw, TRUE); +#ifdef TTY_PERM_INVENT + if (cw->type == NHW_MENU && cw->mbehavior == MENU_BEHAVE_PERMINV) + ttyinv_remove_data(cw, FALSE); + else +#endif + free_window_info(cw, FALSE); + } break; } cw->curx = cw->cury = 0; + /* cw->blanked = TRUE; -- this isn't used */ } -RESTORE_WARNING_FORMAT_NONLITERAL - /* toggle a specific entry */ static boolean toggle_menu_curr( @@ -1110,8 +1197,12 @@ set_item_state( HUPSKIP(); tty_curs(window, 4, lineno); term_start_attr(item->attr); + if (item->color != NO_COLOR) + term_start_color(item->color); (void) putchar(ch); ttyDisplay->curx++; + if (item->color != NO_COLOR) + term_end_color(); term_end_attr(item->attr); } @@ -1238,21 +1329,13 @@ toggle_menu_attr(boolean on, int color, int attr) { if (on) { term_start_attr(attr); -#ifdef TEXTCOLOR if (color != NO_COLOR) term_start_color(color); -#endif } else { -#ifdef TEXTCOLOR if (color != NO_COLOR) term_end_color(); -#endif term_end_attr(attr); } - -#ifndef TEXTCOLOR - nhUse(color); -#endif } static void @@ -1281,7 +1364,7 @@ process_menu_window(winid window, struct WinDesc *cw) gacc[0] = '\0'; if (cw->how != PICK_NONE) { int i, gcnt[128]; -#define GSELIDX(c) (c & 127) /* guard against `signed char' */ +#define GSELIDX(c) ((c) & 127) /* guard against `signed char' */ for (i = 0; i < SIZE(gcnt); i++) gcnt[i] = 0; @@ -1305,6 +1388,7 @@ process_menu_window(winid window, struct WinDesc *cw) *rp++ = curr->gselector; *rp = '\0'; /* re-terminate for strchr() */ } +#undef GSELIDX } resp_len = 0; /* lint suppression */ @@ -1350,9 +1434,8 @@ process_menu_window(winid window, struct WinDesc *cw) (void) putchar(' '); ++ttyDisplay->curx; - if (!iflags.use_menu_color - || !get_menu_coloring(curr->str, &color, &attr)) - attr = curr->attr; + attr = curr->attr; + color = curr->color; /* which character to start attribute highlighting; whole line for headers and such, after the selector @@ -1434,11 +1517,15 @@ process_menu_window(winid window, struct WinDesc *cw) } } /* set extra chars.. */ - Strcat(resp, default_menu_cmds); + if (*gacc) { + Strcat(resp, gacc); /* group accelerators */ + if (cw->how == PICK_ONE) + resp_len = (int) strlen(resp); + } Strcat(resp, " "); /* next page or end */ Strcat(resp, "0123456789\033\n\r"); /* counts, quit */ - Strcat(resp, gacc); /* group accelerators */ Strcat(resp, gm.mapped_menu_cmds); + Strcat(resp, default_menu_cmds); if (cw->npages > 1) Sprintf(cw->morestr, "(%d of %d)", curr_page + 1, @@ -1483,7 +1570,14 @@ process_menu_window(winid window, struct WinDesc *cw) if (!counting && strchr(gacc, morc)) goto group_accel; - count = (count * 10L) + (long) (morc - '0'); + { + long dgt = (long) (morc - '0'); + + /* count = (10 * count) + (morc - '0'); */ + count = AppendLongDigit(count, dgt); + if (count < 0L) /* overflow */ + continue; /* reset_count is True */ + } /* * It is debatable whether we should allow 0 to * start a count. There is no difference if the @@ -1631,6 +1725,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)) { @@ -1662,6 +1757,7 @@ process_menu_window(winid window, struct WinDesc *cw) } /* while */ cw->morestr = msave; free((genericptr_t) morestr); +#undef MENU_EXPLICIT_CHOICE } static void @@ -1669,7 +1765,7 @@ process_text_window(winid window, struct WinDesc *cw) { int i, n, attr; boolean linestart; - register char *cp; + char *cp; for (n = 0, i = 0; i < cw->maxrow; i++) { HUPSKIP(); @@ -1747,20 +1843,18 @@ process_text_window(winid window, struct WinDesc *cw) } } -DISABLE_WARNING_FORMAT_NONLITERAL /* RESTORE after tty_select_menu */ - /*ARGSUSED*/ void tty_display_nhwindow( winid window, boolean blocking) /* with ttys, all windows are blocking */ { - register struct WinDesc *cw = 0; + struct WinDesc *cw = 0; short s_maxcol; HUPSKIP(); if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) - panic(winpanicstr, window); + ttywindowpanic(); if (cw->flags & WIN_CANCELLED) return; ttyDisplay->lastwin = window; @@ -1789,12 +1883,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; @@ -1849,11 +1945,11 @@ tty_display_nhwindow( void tty_dismiss_nhwindow(winid window) { - register struct WinDesc *cw = 0; + struct WinDesc *cw = 0; HUPSKIP(); if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) - panic(winpanicstr, window); + ttywindowpanic(); print_vt_code2(AVTC_SELECT_WINDOW, window); @@ -1862,6 +1958,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: @@ -1875,12 +1972,13 @@ tty_dismiss_nhwindow(winid window) break; case NHW_MENU: case NHW_TEXT: + case NHW_PERMINVENT: if (cw->active) { /* skip erasure if window_inited has been reset to 0 during final run-down in case this is the end-of-game window; the contents of that window should remain shown even when the window itself has gone away */ - if (iflags.window_inited) { + if (iflags.window_inited && !erasing_tty_screen) { boolean clearscreen = FALSE; /* just erase the menu */ /* during role/race/&c selection, menus are put up on top @@ -1888,7 +1986,7 @@ tty_dismiss_nhwindow(winid window) can't refresh--force the screen to be cleared instead (affects dismissal of 'reset role filtering' menu if screen height forces that to need a second page) */ - if (gp.program_state.in_role_selection) + if (program_state.in_role_selection) clearscreen = TRUE; erase_menu_or_text(window, cw, clearscreen); @@ -1903,10 +2001,13 @@ tty_dismiss_nhwindow(winid window) void tty_destroy_nhwindow(winid window) { - register struct WinDesc *cw = 0; + struct WinDesc *cw = 0; - if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) - panic(winpanicstr, window); + if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) { + if (window == WIN_INVEN) /* Null wins[WIN_INVEN] is tolerated */ + return; + ttywindowpanic(); + } if (cw->active) tty_dismiss_nhwindow(window); @@ -1915,42 +2016,41 @@ tty_destroy_nhwindow(winid window) if (cw->type == NHW_MAP) term_clear_screen(); #ifdef TTY_PERM_INVENT - if (cw->type == NHW_PERMINVENT) { - int r, c; - - if (cw->cells) { - for (r = 0; r < cw->maxrow; r++) { - if (cw->cells[r]) { - for (c = 0; c < cw->maxcol; c++) { - /* glyph is a flag indicating whether content union - contains a glyph_info structure or just a char */ - if (cw->cells[r][c].glyph) - free((genericptr_t) cw->cells[r][c].content.gi); - cw->cells[r][c] = zerottycell; - cw->cells[r][c].glyph = 0; - } - free((genericptr_t) cw->cells[r]); - cw->cells[r] = (struct tty_perminvent_cell *) 0; - } - } - free((genericptr_t) cw->cells); - cw->cells = (struct tty_perminvent_cell **) 0; - cw->rows = cw->cols = 0; - } - cw->maxrow = cw->maxcol = 0; - WIN_INVEN = WIN_ERR; - done_tty_perm_invent_init = FALSE; - } + if (cw->type == NHW_MENU && cw->mbehavior == MENU_BEHAVE_PERMINV) + ttyinv_remove_data(cw, TRUE); #endif free_window_info(cw, TRUE); free((genericptr_t) cw); wins[window] = 0; /* available for re-use */ } +/* when screen is cleared, reset stored data to spaces */ +void +erase_tty_screen(void) +{ + struct WinDesc *cw; + int i; + + HUPSKIP(); + if (erasing_tty_screen++) + return; +#if 0 /* originally we called term_clear_screen() but now it calls us */ + term_clear_screen(); /* erase the screen */ +#endif + /* update window data to reflect that each active window is all blanks */ + for (i = 0; i < MAXWIN; ++i) { + cw = wins[i]; + if (cw && cw->active) + tty_clear_nhwindow(i); + } + tty_curs(BASE_WINDOW, 1, 0); + erasing_tty_screen = 0; +} + void -tty_curs(winid window, - register int x, register int y) /* not xchar: perhaps xchar is unsigned - * then curx-x would be unsigned too */ +tty_curs( + winid window, + int x, int y) /* coordinates (and curx-x) must be signed */ { struct WinDesc *cw = 0; int cx = ttyDisplay->curx; @@ -1958,7 +2058,7 @@ tty_curs(winid window, HUPSKIP(); if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) - panic(winpanicstr, window); + ttywindowpanic(); ttyDisplay->lastwin = window; print_vt_code2(AVTC_SELECT_WINDOW, window); @@ -1966,10 +2066,9 @@ tty_curs(winid window, #if defined(TILES_IN_GLYPHMAP) && defined(MSDOS) adjust_cursor_flags(cw); #endif - cw->curx = --x; /* column 0 is never used */ - cw->cury = y; + #ifdef DEBUG - if (x < 0 || y < 0 || y >= cw->rows || x > cw->cols) { + if (x < 1 || y < 0 || y >= cw->rows || x > cw->cols) { const char *s = "[unknown type]"; switch (cw->type) { @@ -1992,17 +2091,21 @@ tty_curs(winid window, s = "[base window]"; break; } - debugpline4("bad curs positioning win %d %s (%d,%d)", window, s, x, y); - /* This return statement caused a functional difference between - DEBUG and non-DEBUG operation, so it is being commented - out. It caused tty_curs() to fail to move the cursor to the - location it needed to be if the x,y range checks failed, - leaving the next piece of output to be displayed at whatever - random location the cursor happened to be at prior. */ - - /* return; */ + /* avoid sending a line to the message window if we're complaining + about cursor positioning in message window; perhaps raw_printf? + this ought to be using impossible() so that someone might + actually see it */ + if (cw->type != NHW_MESSAGE) + debugpline4("tty_curs: bad positioning win %d %s <%d,%d>", + window, s, x, y); + /* don't try to fix up x,y here because then tty_curs() would + behave differently depending on whether DEBUG is defined; + garbage in, garbage out is the order of the day... */ } -#endif +#endif /* DEBUG */ + cw->curx = --x; /* column 0 is not used */ + cw->cury = y; + x += cw->offx; y += cw->offy; @@ -2051,11 +2154,11 @@ tty_curs(winid window, static void tty_putsym(winid window, int x, int y, char ch) { - register struct WinDesc *cw = 0; + struct WinDesc *cw = 0; HUPSKIP(); if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) - panic(winpanicstr, window); + ttywindowpanic(); print_vt_code2(AVTC_SELECT_WINDOW, window); @@ -2115,12 +2218,12 @@ compress_str(const char *str) void tty_putstr(winid window, int attr, const char *str) { - register struct WinDesc *cw = 0; - register char *ob; - register long i, n0; + struct WinDesc *cw = 0; + char *ob; + long i, n0; #ifndef STATUS_HILITES - register const char *nb; - register long j; + const char *nb; + long j; #endif HUPSKIP(); @@ -2151,6 +2254,14 @@ tty_putstr(winid window, int attr, const char *str) int suppress_history = (attr & ATR_NOHISTORY), urgent_message = (attr & ATR_URGENT); +#if defined(ASCIIGRAPH) && !defined(NO_TERMS) + /* if ^C occurs, player is prompted with "Really quit?" and that + prompt is issued via tty_putstr(WIN_MESSAGE); if ^C happens + while writing DECgraphics chars, the prompt text would be + rendered as VT line-drawing characters unless we do this; + also, if color was in progress it wouldn't be switched off */ + end_glyphout(); +#endif /* if message is designated 'urgent' don't suppress it if user has typed ESC at --More-- prompt when dismissing an earlier message; besides turning off WIN_STOP, we need to prevent current message @@ -2185,7 +2296,7 @@ tty_putstr(winid window, int attr, const char *str) #ifndef STATUS_HILITES case NHW_STATUS: ob = &cw->data[cw->cury][j = cw->curx]; - if (gc.context.botlx) + if (disp.botlx) *ob = '\0'; if (!cw->cury && (int) strlen(str) >= CO) { /* the characters before "St:" are unnecessary */ @@ -2196,7 +2307,7 @@ tty_putstr(winid window, int attr, const char *str) nb = str; for (i = cw->curx + 1, n0 = cw->cols; i < n0; i++, nb++) { if (!*nb) { - if (*ob || gc.context.botlx) { + if (*ob || disp.botlx) { /* last char printed may be in middle of line */ tty_curs(WIN_STATUS, i, cw->cury); cl_end(); @@ -2339,9 +2450,6 @@ tty_display_file( nh_terminate(EXIT_FAILURE); } (void) close(fd); -#ifdef notyet - winch_seen = 0; -#endif } #else /* DEF_PAGER */ { @@ -2372,7 +2480,7 @@ tty_display_file( #endif ) { /* attempt to scroll text below map window if there's room */ - wins[datawin]->offy = wins[WIN_STATUS]->offy + 3; + wins[datawin]->offy = wins[WIN_STATUS]->offy + StatusRows(); if ((int) wins[datawin]->offy + 12 > (int) ttyDisplay->rows) wins[datawin]->offy = 0; } @@ -2406,22 +2514,24 @@ tty_start_menu(winid window, unsigned long mbehavior) struct WinDesc *cw = 0; if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) - panic(winpanicstr, window); + ttywindowpanic(); #ifdef TTY_PERM_INVENT - if (window != WIN_ERR && cw->mbehavior == MENU_BEHAVE_PERMINV) { + if (cw->mbehavior == MENU_BEHAVE_PERMINV) { /* PERMINV is ready to go already; not much to do here */ inuse_only_start = 0; return; } if (mbehavior == MENU_BEHAVE_PERMINV - && (iflags.perm_invent - || gp.perm_invent_toggling_direction == toggling_on)) { + && (iflags.perm_invent + || gp.perm_invent_toggling_direction == toggling_on)) { winid w = ttyinv_create_window(window, wins[window]); + if (w == WIN_ERR) { - /* something went wrong, so add clean up code here */ + ; /* something went wrong, so add clean up code here */ } else { cw->mbehavior = mbehavior; + WIN_INVEN = w; } return; } @@ -2447,12 +2557,12 @@ tty_add_menu( char ch, /* selector letter (0 = pick our own) */ char gch, /* group accelerator (0 = no group) */ int attr, /* attribute for string (like tty_putstr()) */ - int clr UNUSED, /* color for string */ + int clr, /* color for string */ const char *str, /* menu string */ unsigned int itemflags) /* itemflags such as MENU_ITEMFLAGS_SELECTED */ { boolean preselected = ((itemflags & MENU_ITEMFLAGS_SELECTED) != 0); - register struct WinDesc *cw = 0; + struct WinDesc *cw = 0; tty_menu_item *item; const char *newstr; char buf[4 + BUFSZ]; @@ -2464,7 +2574,7 @@ tty_add_menu( if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0 || cw->type != NHW_MENU) - panic(winpanicstr, window); + ttywindowpanic(); #ifdef TTY_PERM_INVENT if (cw->mbehavior == MENU_BEHAVE_PERMINV) { @@ -2497,6 +2607,7 @@ tty_add_menu( item->selector = ch; item->gselector = gch; item->attr = attr; + item->color = clr; item->str = dupstr(newstr); item->next = cw->mlist; @@ -2518,6 +2629,8 @@ reverse(tty_menu_item *curr) return head; } +static color_attr tty_menu_promptstyle = { NO_COLOR, ATR_NONE }; + /* * End a menu in this window, window must a type NHW_MENU. This routine * processes the string list. We calculate the # of pages, then assign @@ -2525,15 +2638,16 @@ reverse(tty_menu_item *curr) * height of the window. */ void -tty_end_menu(winid window, /* menu to use */ - const char *prompt) /* prompt to for menu */ +tty_end_menu( + winid window, /* menu to use */ + const char *prompt) /* prompt to for menu */ { struct WinDesc *cw = 0; tty_menu_item *curr; short len; int lmax, n; char menu_ch; - int clr = 0; + int clr = NO_COLOR; if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0 || cw->type != NHW_MENU) { @@ -2542,14 +2656,12 @@ tty_end_menu(winid window, /* menu to use */ requires a panic, just an early return. */ if (window == WIN_INVEN && !cw) return; - panic(winpanicstr, window); + ttywindowpanic(); } #ifdef TTY_PERM_INVENT - if (cw->mbehavior == MENU_BEHAVE_PERMINV - && (iflags.perm_invent || gp.perm_invent_toggling_direction == toggling_on) - && window == WIN_INVEN) { - if (gp.program_state.in_moveloop) - ttyinv_render(window, cw); + /* (probably don't need to check both of these conditions) */ + if (cw->mbehavior == MENU_BEHAVE_PERMINV && window == WIN_INVEN) { + ttyinv_end_menu(window, cw); return; } #endif @@ -2565,7 +2677,8 @@ tty_end_menu(winid window, /* menu to use */ tty_add_menu(window, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); tty_add_menu(window, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, prompt, MENU_ITEMFLAGS_NONE); + tty_menu_promptstyle.attr, tty_menu_promptstyle.color, + prompt, MENU_ITEMFLAGS_NONE); } /* 52: 'a'..'z' and 'A'..'Z'; avoids selector duplication within a page */ @@ -2653,14 +2766,14 @@ tty_end_menu(winid window, /* menu to use */ int tty_select_menu(winid window, int how, menu_item **menu_list) { - register struct WinDesc *cw = 0; + struct WinDesc *cw = 0; tty_menu_item *curr; menu_item *mi; int n, cancelled; if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0 || cw->type != NHW_MENU) - panic(winpanicstr, window); + ttywindowpanic(); if (cw->mbehavior == MENU_BEHAVE_PERMINV) { return 0; @@ -2725,23 +2838,20 @@ tty_message_menu(char let, int how, const char *mesg) return ((how == PICK_ONE && morc == let) || morc == '\033') ? morc : '\0'; } -RESTORE_WARNING_FORMAT_NONLITERAL - win_request_info * -tty_ctrl_nhwindow(winid window UNUSED, int request, win_request_info *wri) +tty_ctrl_nhwindow( + winid window UNUSED, + int request, + win_request_info *wri) { -#if !defined(TTY_PERM_INVENT) - return (win_request_info *) 0; - nhUse(window); - nhUse(request); - nhUse(wri); -#else +#if defined(TTY_PERM_INVENT) boolean tty_ok /*, show_gold */, inuse_only; int maxslot; /* these types are set match the wintty.h field declarations */ long minrow; /* long to match maxrow declaration in wintty.h */ short offx, offy; long rows, cols, maxrow, maxcol; +#endif if (!wri) return (win_request_info *) 0; @@ -2749,31 +2859,47 @@ tty_ctrl_nhwindow(winid window UNUSED, int request, win_request_info *wri) switch (request) { case set_mode: case request_settings: +#if defined(TTY_PERM_INVENT) ttyinvmode = wri->fromcore.invmode; - /* show_gold = (ttyinvmode & InvShowGold) != 0; */ - inuse_only = ((ttyinvmode & InvInUse) != 0); if (request == set_mode) break; + /* show_gold = (ttyinvmode & InvShowGold) != 0; */ + inuse_only = ((ttyinvmode & InvInUse) != 0); wri->tocore = zero_tocore; tty_ok = assesstty(ttyinvmode, &offx, &offy, &rows, &cols, &maxcol, &minrow, &maxrow); - wri->tocore.needrows = (int) (minrow + 1 + ROWNO + 3); - wri->tocore.needcols = (int) tty_perminv_mincol; - wri->tocore.haverows = (int) ttyDisplay->rows; - wri->tocore.havecols = (int) ttyDisplay->cols; - if (!tty_ok) { - wri->tocore.tocore_flags |= prohibited; /* prohibited */ - return wri; + if (!tty_ok && rows == 0 && cols == 0) { + /* something is terribly wrong, possibly too early in startup */ + wri->tocore.tocore_flags |= too_early; + } else { + 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; + } } - maxslot = (maxrow - 2) * (!inuse_only ? 2 : 1); - wri->tocore.maxslot = maxslot; - return wri; +#endif /* TTY_PERM_INVENT */ + break; + case set_menu_promptstyle: + tty_menu_promptstyle = wri->fromcore.menu_promptstyle; break; default: - impossible("invalid request to tty_update_invent_slot %u", request); + break; } return wri; -#endif } #ifdef TTY_PERM_INVENT @@ -2781,7 +2907,7 @@ tty_ctrl_nhwindow(winid window UNUSED, int request, win_request_info *wri) static int ttyinv_create_window(int newid, struct WinDesc *newwin) { - int i, r, c; + int r, c; long minrow; /* long to match maxrow declaration */ unsigned n; @@ -2811,23 +2937,25 @@ ttyinv_create_window(int newid, struct WinDesc *newwin) * */ - /* preliminary init in case tty_desctroy_nhwindow() gets called */ + /* preliminary init in case tty_destroy_nhwindow() gets called */ newwin->data = (char **) 0; newwin->datlen = (short *) 0; newwin->cells = (struct tty_perminvent_cell **) 0; - if (!assesstty(ttyinvmode, &newwin->offx, &newwin->offy, &newwin->rows, &newwin->cols, &newwin->maxcol, &minrow, &newwin->maxrow)) { - tty_destroy_nhwindow(newid); /* sets WIN_INVEN to WIN_ERR */ + tty_destroy_nhwindow(newid); + WIN_INVEN = WIN_ERR; pline("%s.", "tty perm_invent could not be enabled"); pline("tty perm_invent needs a terminal that is at least %dx%d, " "yours is %dx%d.", - (int) (minrow + 1 + ROWNO + 3), tty_perminv_mincol, + (int) (minrow + 1 + ROWNO + StatusRows()), tty_perminv_mincol, ttyDisplay->rows, ttyDisplay->cols); tty_wait_synch(); +#ifndef RESIZABLE set_option_mod_status("perm_invent", set_gameview); +#endif iflags.perm_invent = FALSE; return WIN_ERR; } @@ -2835,64 +2963,117 @@ ttyinv_create_window(int newid, struct WinDesc *newwin) /* * Terminal/window/screen is big enough. */ - newwin->maxrow = minrow; + /*newwin->maxrow = minrow;*/ newwin->maxcol = newwin->cols; /* establish the borders */ bordercol[border_left] = 0; bordercol[border_middle] = (newwin->maxcol + 1) / 2; bordercol[border_right] = newwin->maxcol - 1; - /* for in-use mode, use full lines */ + /* for in-use mode, use full lines; it will switch to two panels if + there are more items than the number of full lines */ if ((ttyinvmode & InvInUse) != 0) bordercol[border_middle] = bordercol[border_right]; + ttyinv_slots_used = 0; - n = (unsigned) (newwin->maxrow * sizeof(struct tty_perminvent_cell *)); + n = (unsigned) (newwin->maxrow * sizeof (struct tty_perminvent_cell *)); newwin->cells = (struct tty_perminvent_cell **) alloc(n); - n = (unsigned) (newwin->maxcol * sizeof(struct tty_perminvent_cell)); - for (i = 0; i < newwin->maxrow; i++) - newwin->cells[i] = (struct tty_perminvent_cell *) alloc(n); - - n = (unsigned) sizeof(glyph_info); - for (r = 0; r < newwin->maxrow; r++) - for (c = 0; c < newwin->maxcol; c++) { - newwin->cells[r][c] = zerottycell; - if (r == 0 || r == newwin->maxrow - 1 - || c == bordercol[border_left] - || c == bordercol[border_middle] - || c == bordercol[border_right]) { - newwin->cells[r][c].content.gi = (glyph_info *) alloc(n); - *newwin->cells[r][c].content.gi = zerogi; - newwin->cells[r][c].glyph = 1; - } - } + n = (unsigned) (newwin->maxcol * sizeof (struct tty_perminvent_cell)); + for (r = 0; r < newwin->maxrow; r++) { + newwin->cells[r] = (struct tty_perminvent_cell *) alloc(n); + for (c = 0; c < newwin->maxcol; c++) + newwin->cells[r][c] = emptyttycell; + } newwin->active = 1; tty_invent_box_glyph_init(newwin); + return newid; } +/* discard perminvent window or erase it and set remembered data to spaces */ static void -ttyinv_add_menu(winid window UNUSED, struct WinDesc *cw, char ch, - int attr UNUSED, int clr UNUSED, const char *str) +ttyinv_remove_data(struct WinDesc *cw, boolean destroy) +{ + if (!cw) { + impossible("Removing ttyinv data for nonexistent perm invent window?"); + return; + } + + if (cw->cells) { + int r, c; + + for (r = 0; r < cw->maxrow; r++) { + if (cw->cells[r]) { + for (c = 0; c < cw->maxcol; c++) { + struct tty_perminvent_cell *invcell = &cw->cells[r][c]; + + /* glyph is a flag indicating whether content union + contains a glyph_info structure or just a char */ + if (invcell->glyph) + free((genericptr_t) invcell->content.gi); + *invcell = emptyttycell; /* sets glyph flag to 0 */ + if (!destroy) { /* erasing */ + invcell->content.ttychar = ' '; + invcell->text = 1; + invcell->refresh = 1; /* full redraw wanted */ + } + } + if (destroy) + free((genericptr_t) cw->cells[r]), + cw->cells[r] = (struct tty_perminvent_cell *) 0; + } + } + if (destroy) { + free((genericptr_t) cw->cells), + cw->cells = (struct tty_perminvent_cell **) 0; + cw->rows = cw->cols = 0; + } + } + if (destroy) { + cw->maxrow = cw->maxcol = 0; + /*WIN_INVEN = WIN_ERR;*/ /* caller's responsibility */ + done_tty_perm_invent_init = FALSE; + } + ttyinv_slots_used = 0; /* reset: inuse_only isn't using any slots */ +} + +static void +ttyinv_add_menu( + winid window UNUSED, + struct WinDesc *cw, + char ch, + int attr UNUSED, int clr, + const char *str) { char invbuf[BUFSZ]; const char *text; - boolean inuse_only = (ttyinvmode & InvInUse) != 0, - show_gold = (ttyinvmode & InvShowGold) != 0, - /* sparse = (ttyinvmode & InvSparse) != 0, */ + boolean show_gold = (ttyinvmode & InvShowGold) != 0, + inuse_only = (ttyinvmode & InvInUse) != 0, ignore = FALSE; - int row, side, slot = 0, rows_per_side = (!show_gold ? 26 : 27); + int row, side, slot, startcolor_at = 0, + rows_per_side = (inuse_only ? (cw->maxrow - 2) + : !show_gold ? 26 + : 27); - if (!gp.program_state.in_moveloop) + if (!program_state.in_moveloop) return; slot = selector_to_slot(ch, ttyinvmode, &ignore); + if (inuse_only && slot > 2 * rows_per_side) + ignore = TRUE; /* left & right sides full; no 3rd 'side' available */ if (!ignore) { - /* inuse_only = ((ttyinvmode & InvInUse) != 0); */ slot_tracker[slot] = TRUE; - text = Empty; /* lint suppression */ - /* maxslot = ((int) cw->maxrow - 2) * (!inuse_only ? 2 : - * 1); */ + /* if we need to expand inuse_only from one side to two, do so now; + entries are shown in row 1 thru rows_per_side (with rows 0 and + rows_per_side+1 containing boundary lines) but stored in + slots 0 thru rows_per_side-1, so zero-based slot==rows_per_side + is the entry that will be shown on first row of the second side + (and one-base ttyinv_slots_used==rows_per_side means that on the + previous inventory_update(), full lines filled all the rows; + ttyinv_slots_used==0 means that we've just enabled perm_invent) */ + if (inuse_only && slot == rows_per_side + && ttyinv_slots_used % rows_per_side == 0) + ttyinv_inuse_twosides(cw, rows_per_side); - /* TODO: check for MENUCOLORS match */ text = str; /* 'text' will switch to invbuf[] below */ /* strip away "a"/"an"/"the" prefix to show a bit more of the interesting part of the object's description; this @@ -2907,102 +3088,256 @@ ttyinv_add_menu(winid window UNUSED, struct WinDesc *cw, char ch, if (text[1] == 'h' && text[2] == 'e' && text[3] == ' ') text += 4; } + /* + * TODO? + * Replace "c - " prefix with "c: " or just "c " to have a bit more + * room for 'text' (the item description). If this prefix gets + * changed, the indentation for empty inventory in ttyinv_render() + * should be changed to match. + */ Snprintf(invbuf, sizeof invbuf, "%c - %s", ch, text); text = invbuf; + startcolor_at = (int) (sizeof "a - " - sizeof ""); /* 4 */ row = (slot % rows_per_side) + 1; /* +1: top border */ /* side: left side panel or right side panel, not a window column */ - side = slot < rows_per_side ? 0 : 1; - if (!(inuse_only && side == 1)) - ttyinv_populate_slot(cw, row, side, text, 0); + side = slot / rows_per_side; + ttyinv_populate_slot(cw, row, side, text, + (uint32) clr, startcolor_at); } return; } + +/* convert a..zA..Z to 0..51 or for 'show_gold' $a..zA..Z# to 0..53 */ static int -selector_to_slot(char ch, const int invflags, boolean *ignore) +selector_to_slot( + char ch, + const int invflags, + boolean *ignore) { int slot = 0; boolean show_gold = (invflags & InvShowGold) != 0, inuse_only = (invflags & InvInUse) != 0; -#if 0 - sparse = (invflags & InvSparse) != 0, -#endif *ignore = FALSE; - switch (ch) { - case '$': - if (!show_gold) + if (inuse_only) { + /* + * Inuse_only uses slots 0 to N-1 instead of fixed positions. + * Caller--or caller's caller's ... caller--only passes us + * relevant items so no filtering is necessary here. + * '!show_gold' gets ignored here since gold might be quivered + * and become an in-use item. + * Inuse_only items might include one (or more than one) with + * invlet '#' but it isn't special for Inuse_only. + * When nothing is in use, the core sends a menu header line to + * to indicate such in order to avoid having an empty menu; we + * ignore that and reconstruct one in ttyinv_render(). + */ + if (!ch) *ignore = TRUE; - slot = 0; - break; - case '#': - slot = 52 + (show_gold ? 1 : 0); - break; - case 0: - *ignore = TRUE; - break; - default: - if (!inuse_only) { + else + slot = inuse_only_start++; + } else { + /* normal perminv */ + switch (ch) { + case '$': + if (!show_gold) + *ignore = TRUE; + else + slot = 0; + break; + case '#': + /* overflow item won't be gold but there is only room allocated + for it when gold is also eligible to be shown (hero doesn't + have to be carrying any, we just need the extra row it gets) */ + if (!show_gold) + *ignore = TRUE; + else + /* fixed location, bottom of right-hand panel; if there is + more than 1 overflow item, we are only able to show 1 */ + slot = 0 + 52 + 1; /* 0 for gold, 2 * 26 letters, then '#' */ + break; + case '\0': + *ignore = TRUE; + break; + default: if (ch >= 'a' && ch <= 'z') slot = (ch - 'a') + (show_gold ? 1 : 0); - if (ch >= 'A' && ch <= 'Z') + else if (ch >= 'A' && ch <= 'Z') slot = (ch - 'A') + (show_gold ? 1 : 0) + 26; - } else { - if ((ch >= 'a' && ch <= 'z') - || (ch >= 'A' && ch <= 'Z')) - slot = (show_gold ? 1 : 0) + inuse_only_start++; + break; } - } + } /* normal vs inuse_only */ return slot; } +static char +slot_to_invlet(int slot, boolean incl_gold) +{ + char res = '\0'; + + switch (slot) { + case 0: + res = incl_gold ? '$' : 'a'; + break; + case 53: + /* if (!incl_gold) impossible("ttyinv_render: slot %d?", slot); */ + res = '#'; + break; + default: + if (incl_gold) + --slot; /* simplify 1..52 (0 and 53 won't get here) to 0..51 */ + res = (slot < 26) ? ('a' + slot) + : (slot < 52) ? ('A' + slot - 26) + : '?'; /* won't happen */ + break; + } + return res; +} + +/* called if inuse_only contracts from two sides to one; there won't be + full lines shown until the next inventory update because the data past + the middle boundary was clipped rather than saved; caller or caller's + caller or somewhere up the call chain will call update_inventory() to + redraw it all and rectify that */ +static void +ttyinv_inuse_fulllines( + struct WinDesc *cw, + int rows_per_side UNUSED) +{ + bordercol[border_middle] = bordercol[border_right]; + tty_invent_box_glyph_init(cw); +} + +/* called when inuse_only expands from full lines (one side) to two sides */ +static void +ttyinv_inuse_twosides( + struct WinDesc *cw, + int rows_per_side) +{ + int row, col; + + bordercol[border_middle] = (cw->maxcol + 1) / 2; + tty_invent_box_glyph_init(cw); + /* draw the middle boundary */ + col = bordercol[border_middle]; + for (row = 0; row <= rows_per_side; ++row) + tty_refresh_inventory(col, col, row); +} + +/* split out of tty_end_menu(); persistent inventory is ready to display */ +static void +ttyinv_end_menu(int window, struct WinDesc *cw) +{ + if (iflags.perm_invent + || gp.perm_invent_toggling_direction == toggling_on) { + if (program_state.in_moveloop) { + boolean inuse_only = ((ttyinvmode & InvInUse) != 0); + int rows_per_side = inuse_only ? cw->maxrow - 2 : 0; + int old_slots_used = ttyinv_slots_used; /* value before render */ + + ttyinv_render(window, cw); + + /* if inuse_only was using two sides and has just shrunk to one, + it will switch to full rows instead of side-by-side panels + but current data only holds the left-hand panel's portion; + rerun the whole thing to regenerate previously clipped data; + fortunately this should be a fairly rare occurrence */ + if (inuse_only && old_slots_used > rows_per_side + /* 'ttyinv_slots_used' was just updated by ttyinv_render() + to the number of entries currently shown */ + && ttyinv_slots_used <= rows_per_side) + tty_update_inventory(0); /* will call back to core for data */ + } + } +} + +/* display persistent inventory from data collected by add_menu() */ static void ttyinv_render(winid window, struct WinDesc *cw) { int row, col, slot, side, filled_count = 0, slot_limit; + uint32 current_row_color = NO_COLOR; struct tty_perminvent_cell *cell; - char invbuf[BUFSZ], *text; - boolean force_redraw = gp.program_state.in_docrt ? TRUE : FALSE, - show_gold = (ttyinvmode & InvShowGold) != 0, - inuse_only = (ttyinvmode & InvInUse) != 0; - int rows_per_side = (!show_gold ? 26 : 27); + char invbuf[BUFSZ]; + boolean force_redraw = program_state.in_docrt ? TRUE : FALSE, + inuse_only = (ttyinvmode & InvInUse) != 0, + show_gold = (ttyinvmode & InvShowGold) != 0 && !inuse_only, + sparse = (ttyinvmode & InvSparse) != 0 && !inuse_only; + int rows_per_side = (inuse_only ? (cw->maxrow - 2) + : !show_gold ? 26 + : 27); slot_limit = SIZE(slot_tracker); if (inuse_only) { - rows_per_side = cw->maxrow - 2; /* -2 top and bottom borders */ + slot_limit = rows_per_side; /* assume one side */ + if (ttyinv_slots_used == 0 || ttyinv_slots_used >= rows_per_side) + slot_limit *= 2; /* second side is populated */ + } else if (!show_gold) { + slot_limit -= 2; /* there are two extra slots for gold and overflow; + * blanking them for !show_gold would wrap back to + * rows 1 and 2 and need non-existent third side */ } + for (slot = 0; slot < slot_limit; ++slot) if (slot_tracker[slot]) filled_count++; + /* clear unused slots */ for (slot = 0; slot < slot_limit; ++slot) { if (slot_tracker[slot]) continue; if (slot == 0 && !filled_count) { Sprintf(invbuf, "%-4s[%s]", "", - !filled_count ? "empty" - : inuse_only ? "no items are in use" - : "only gold"); - text = invbuf; + inuse_only ? "no items are in use" + : (!show_gold && money_cnt(gi.invent)) ? "only gold" + : "empty"); + } else if (sparse && filled_count) { + Sprintf(invbuf, "%c", slot_to_invlet(slot, show_gold)); } else { - text = Empty; /* "" => fill slot with spaces */ + invbuf[0] = '\0'; /* "" => fill slot with spaces */ } row = (slot % rows_per_side) + 1; /* +1: top border */ /* side: left side panel or right side panel, not a window column */ - side = slot < rows_per_side ? 0 : 1; - if (!(inuse_only && side == 1)) - ttyinv_populate_slot(cw, row, side, text, 0); + side = slot / rows_per_side; + ttyinv_populate_slot(cw, row, side, invbuf, NO_COLOR, 0); + } + + /* inuse_only might switch from one panel to two or vice versa */ + if (inuse_only && filled_count != ttyinv_slots_used) { + if (filled_count > rows_per_side + && ttyinv_slots_used <= rows_per_side) { + /* need second side; set up the middle border */ + /* done earlier, in add_menu(): + ttyinv_inuse_twosides(cw, rows_per_side); + */ + } else if (filled_count <= rows_per_side + && ttyinv_slots_used > rows_per_side) { + /* have second side but don't need/want it anymore */ + ttyinv_inuse_fulllines(cw, rows_per_side); + } + ttyinv_slots_used = filled_count; } + /* has there been a glyph reset since we last got here? */ if (gg.glyph_reset_timestamp > last_glyph_reset_when) { - // tty_invent_box_glyph_init(wins[WIN_INVEN]); + /* // tty_invent_box_glyph_init(wins[WIN_INVEN]); */ last_glyph_reset_when = gg.glyph_reset_timestamp; force_redraw = TRUE; } /* render to the display */ calling_from_update_inventory = TRUE; - for (row = 0; row < cw->maxrow; ++row) + for (row = 0; row < cw->maxrow; ++row) { for (col = 0; col < cw->maxcol; ++col) { cell = &cw->cells[row][col]; if (cell->refresh || force_redraw) { + if (cell->color && (current_row_color != cell->color - 1)) { + current_row_color = cell->color - 1; +#if 0 + if (current_row_color == NO_COLOR) + term_end_color(); + else +#endif + term_start_color(current_row_color); + } if (cell->glyph) { tty_print_glyph(window, col + 1, row, cell->content.gi, &nul_glyphinfo); @@ -3017,49 +3352,16 @@ ttyinv_render(winid window, struct WinDesc *cw) cell->refresh = 0; } } + if (current_row_color != NO_COLOR) + term_end_color(); + } tty_curs(window, 1, 0); for (slot = 0; slot < SIZE(slot_tracker); ++slot) - slot_tracker[slot] = 0; + slot_tracker[slot] = FALSE; calling_from_update_inventory = FALSE; return; } -/* - * returns TRUE if things are ok - */ -static boolean -assesstty( - enum inv_modes invmode, - short *offx, short *offy, long *rows, long *cols, - long *maxcol, long *minrow, long *maxrow) -{ - boolean show_gold, inuse_only; - - show_gold = (invmode & InvShowGold) != 0; - inuse_only = (invmode & InvInUse) != 0; - - *offx = 0; - /* topline + map rows + status lines */ - *offy = 1 + ROWNO + 3; /* 3: + 2 + (iflags.wc2_statuslines > 2) */ - *rows = (ttyDisplay->rows - (*offy)); - *cols = ttyDisplay->cols; - *minrow = tty_perminv_minrow; - if (show_gold) - *minrow += 1; - /* "normal" max for items in use would be 3 weapon + 7 armor + 4 - accessories == 14, but being punished and picking up the ball will - add 1, and some quest artifacts have an an #invoke property that's - tracked via obj->owornmask so could add more; if hero ends up with - more than 15 in-use items, some will be left out; - Qt's "paper doll" adds first lit lamp/candle and first active - leash; those aren't tracked via owornmask so we don't notice them */ - if (inuse_only) - *minrow = 1 + 15 + 1; /* top border + 15 lines + bottom border */ - *maxrow = *minrow; - *maxcol = *cols; - return !(*rows < *minrow || *cols < tty_perminv_mincol); -} - /* put the formatted object description for one item into a particular row and left/right panel, truncating if long or padding with spaces if short */ static void @@ -3067,48 +3369,54 @@ ttyinv_populate_slot( struct WinDesc *cw, int row, /* 'row' within the window, not within screen */ int side, /* 'side'==0 is left panel or ==1 is right panel */ - const char *text, int32_t color) + const char *text, + uint32 color, + int clroffset) { struct tty_perminvent_cell *cell; char c; int ccnt, col, endcol; + boolean oops, inuse_only = (ttyinvmode & InvInUse) != 0; - /* FIXME: this needs a review. Crashed under InvInUse without */ - if ((ttyinvmode & InvInUse) != 0) - col = bordercol[0] + 1; - else - col = bordercol[side] + 1; + oops = (row < 0 || (long) row >= cw->maxrow || side < 0); + if (inuse_only && side > 1 && !oops) + return; /* there might be more in-use than fits; ignore excess */ + if (oops || side > 1) + panic("ttyinv_populate_slot row=%d, side=%d", row, side); + col = bordercol[side] + 1; endcol = bordercol[side + 1] - 1; cell = &cw->cells[row][col]; - if (cell->color != color) - cell->refresh = 1; - cell->color = color; for (ccnt = col; ccnt <= endcol; ++ccnt, ++cell) { /* [don't expect this to happen] if there was a glyph here, release memory allocated for it; gi pointer and ttychar character overlay each other in a union, so clear gi before assigning ttychar */ if (cell->glyph) { - free((genericptr_t) cell->content.gi), cell->content.gi = 0; - cell->glyph = 0; /* cell->content.gi is gone */ + free((genericptr_t) cell->content.gi); + *cell = emptyttycell; /* clears cell->glyph and cell->content */ } - if ((c = *text) != '\0') { - if (cell->content.ttychar != c) - cell->refresh = 1; - cell->content.ttychar = c; + if ((c = *text) != '\0') ++text; - } else { - if (cell->content.ttychar != ' ') - cell->refresh = 1; - cell->content.ttychar = ' '; + else + c = ' '; + + if (cell->content.ttychar != c) { + cell->content.ttychar = c; + cell->refresh = 1; + } + if (cell->color != color + 1) { + /* offset color by 1 so 0 is not valid */ + if (ccnt >= (col + clroffset)) + cell->color = color + 1; + else + cell->color = NO_COLOR + 1; + cell->refresh = 1; } cell->text = 1; /* cell->content.ttychar is current */ } } -DISABLE_WARNING_FORMAT_NONLITERAL - void tty_refresh_inventory(int start, int stop, int y) { @@ -3116,27 +3424,33 @@ tty_refresh_inventory(int start, int stop, int y) struct WinDesc *cw = 0; winid window = WIN_INVEN; struct tty_perminvent_cell *cell; + boolean printing_glyphs; - if (window == WIN_ERR || !iflags.perm_invent || y < 0) + if (window == WIN_ERR || !iflags.perm_invent + || gp.perm_invent_toggling_direction == toggling_off) return; if ((cw = wins[window]) == (struct WinDesc *) 0) - panic(winpanicstr, window); + ttywindowpanic(); if (col_limit > cw->maxcol) col_limit = cw->maxcol; - if (row >= cw->maxrow) - return; /* out of our range. Huge menus can do this */ - /* we've been asked to redisplay a portion of the screen, one row */ + printing_glyphs = FALSE; for (col = start - 1; col < col_limit; ++col) { cell = &cw->cells[row][col]; + if (cell->color != ttyDisplay->color + 1) { + term_start_color(cell->color - 1); + ttyDisplay->color = cell->color - 1; + } if (cell->glyph) { tty_print_glyph(window, col + 1, row, cell->content.gi, &nul_glyphinfo); - end_glyphout(); + printing_glyphs = TRUE; } else { + if (printing_glyphs) + end_glyphout(); if (col != cw->curx || row != cw->cury) tty_curs(window, col + 1, row); (void) putchar(cell->content.ttychar); @@ -3145,16 +3459,26 @@ tty_refresh_inventory(int start, int stop, int y) } cell->refresh = 0; } + if (printing_glyphs) + end_glyphout(); + if (ttyDisplay->color != NO_COLOR) { + term_end_color(); + ttyDisplay->color = NO_COLOR; + } } -RESTORE_WARNING_FORMAT_NONLITERAL - static void tty_invent_box_glyph_init(struct WinDesc *cw) - { - int row, col; +{ + int row, col, glyph, bordercolor = NO_COLOR; uchar sym; struct tty_perminvent_cell *cell; + boolean inuse_only = (ttyinvmode & InvInUse) != 0, + show_gold = (ttyinvmode & InvShowGold) != 0 && !inuse_only; + int rows_per_side = (inuse_only ? (cw->maxrow - 2) + : !show_gold ? 26 + : 27), + bottomrow = rows_per_side + 1; if (cw == 0 || !cw->active) return; @@ -3162,76 +3486,121 @@ tty_invent_box_glyph_init(struct WinDesc *cw) for (row = 0; row < cw->maxrow; ++row) for (col = 0; col < cw->maxcol; ++col) { cell = &cw->cells[row][col]; - /* cell->glyph is a flag for whether the content union contains - a glyph_info structure rather than just a char */ - if (!cell->glyph) - continue; - /* sym will always get another value; if for some reason it - doesn't, this default is valid for cmap_walls_to_glyph() */ - sym = S_crwall; + sym = S_crwall; /* placeholder */ /* note: for top and bottom, check [border_right] before - [border_middle] because they could be the same and if so - we want corner rather than tee */ + [border_middle] because they could be the same column (for + InvInUse) and if so we want corner rather than tee there */ if (row == 0) { - if (col == bordercol[border_left]) - sym = S_tlcorn; - else if (col == bordercol[border_right]) - sym = S_trcorn; - else if (col == bordercol[border_middle]) - sym = S_tdwall; - else /*if ((col > bordercol[border_left] - && col < bordercol[border_middle]) - || (col > bordercol[border_middle] - && col < bordercol[border_right]))*/ - sym = S_hwall; - } else if (row == (cw->maxrow - 1)) { - if (col == bordercol[border_left]) - sym = S_blcorn; - else if (col == bordercol[border_right]) - sym = S_brcorn; - else if (col == bordercol[border_middle]) - sym = S_tuwall; - else /*if ((col > bordercol[border_left] - && col < bordercol[border_middle]) - || (col > bordercol[border_middle] - && col < bordercol[border_right]))*/ - sym = S_hwall; - } else { + sym = (col == bordercol[border_left]) ? S_tlcorn + : (col == bordercol[border_right]) ? S_trcorn + : (col == bordercol[border_middle]) ? S_tdwall + : S_hwall; + } else if (row < bottomrow) { if (col == bordercol[border_left] || col == bordercol[border_middle] || col == bordercol[border_right]) sym = S_vwall; + /* else sym keeps placeholder value */ + } else if (row == bottomrow) { + sym = (col == bordercol[border_left]) ? S_blcorn + : (col == bordercol[border_right]) ? S_brcorn + : (col == bordercol[border_middle]) ? S_tuwall + : S_hwall; } - /* to get here, cell->glyph is 1 and cell->content union has gi */ - { - int oldsymidx = cell->content.gi->gm.sym.symidx; -#ifdef ENHANCED_SYMBOLS - struct unicode_representation * - oldgmu = cell->content.gi->gm.u; -#endif - int glyph = cmap_D0walls_to_glyph(sym); - - map_glyphinfo(0, 0, glyph, 0, cell->content.gi); - if ( -#ifdef ENHANCED_SYMBOLS - cell->content.gi->gm.u != oldgmu || -#endif - cell->content.gi->gm.sym.symidx != oldsymidx) + if (sym == S_crwall) { /* not a boundary (but might have been) */ + if (cell->glyph) { + /* presumably is no longer wanted middle vertical line */ + free((genericptr_t) cell->content.gi); + *cell = emptyttycell; /* clears cell->glyph */ + cell->content.ttychar = ' '; + cell->text = 1; cell->refresh = 1; - cell->glyph = 1; /* (redundant) */ - cell->text = 0; + } + continue; } + /* a boundary; if it doesn't hold a glyph yet, allocate one */ + if (!cell->glyph) { + cell->content.gi = (glyph_info *) alloc(sizeof (glyph_info)); + *(cell->content.gi) = zerogi; + cell->glyph = 1, cell->text = 0; + } + + /* to get here, cell->glyph is 1 and cell->content union has gi */ + glyph = cmap_D0walls_to_glyph(sym); + map_glyphinfo(0, 0, glyph, 0, cell->content.gi); + cell->glyph = 1; /* (redundant) */ + cell->text = 0; + /* originally this conditionally set refresh depending upon + whether the glyph was already shown, but that optimization + is for something that rarely happens (boundary lines aren't + redrawn very often, and most of the time when they are it's + because they were erased or overwritten by something so + won't match the prior value anymore) so skip it */ + cell->refresh = 1; + cell->color = bordercolor + 1; } done_tty_perm_invent_init = TRUE; } + +/* + * returns TRUE if things are ok + */ +static boolean +assesstty( + enum inv_modes invmode, + short *offx, short *offy, long *rows, long *cols, + long *maxcol, long *minrow, long *maxrow) +{ + boolean inuse_only = (invmode & InvInUse) != 0, + show_gold = (invmode & InvShowGold) != 0 && !inuse_only; + 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) */ + *rows = (ttyDisplay->rows - *offy); + *cols = ttyDisplay->cols; + *minrow = perminv_minrow; +#define SMALL_INUSE_WINDOW +#ifdef SMALL_INUSE_WINDOW +#undef SMALL_INUSE_WINDOW + /* simplify testing by not requiring a small font to have enough room */ + if (inuse_only) + *minrow = 1 + 8 + 1; +#else + /* "normal" max for items in use would be 3 weapon + 7 armor + 4 + accessories == 14, but lit lamps/candles and attached leashes are + also included; if hero ends up with more than 15 in-use items, + some will be left out */ + if (inuse_only) + *minrow = 1 + 15 + 1; /* top border + 15 lines + bottom border */ +#endif + *maxrow = min(*rows, perminv_minrow); + *maxcol = *cols; + return !(*rows < *minrow || *cols < tty_perminv_mincol); +} + + #endif /* TTY_PERM_INVENT */ /* update persistent inventory window */ void tty_update_inventory(int arg UNUSED) { - /* currently not used */ +#ifdef TTY_PERM_INVENT + sync_perminvent(); /* call back to core */ +#else + /* do nothing */ +#endif return; } @@ -3256,7 +3625,7 @@ tty_wait_synch(void) if (ttyDisplay->inmore) { addtopl("--More--"); (void) fflush(stdout); - } else if (ttyDisplay->inread > gp.program_state.gameover) { + } else if (ttyDisplay->inread > program_state.gameover) { /* this can only happen if we were reading and got interrupted */ ttyDisplay->toplin = TOPLINE_SPECIAL_PROMPT; /* do this twice; 1st time gets the Quit? message again */ @@ -3269,21 +3638,23 @@ tty_wait_synch(void) } void -docorner(register int xmin, register int ymax, int ystart_between_menu_pages) +docorner( + int xmin, int ymax, + int ystart_between_menu_pages) { - register int y; - register struct WinDesc *cw = wins[WIN_MAP]; + int y; + struct WinDesc *cw = wins[WIN_MAP]; int ystart = 0; #ifdef TTY_PERM_INVENT struct WinDesc *icw = 0; - if (WIN_INVEN != WIN_ERR) + if (WIN_INVEN != WIN_ERR && iflags.perm_invent) icw = wins[WIN_INVEN]; #endif HUPSKIP(); #if 0 /* this optimization is not valuable enough to justify - abusing core internals... */ + * abusing core internals... */ if (u.uswallow) { /* Can be done more efficiently */ swallowed(1); /* without this flush, if we happen to follow --More-- displayed in @@ -3294,7 +3665,7 @@ docorner(register int xmin, register int ymax, int ystart_between_menu_pages) } #endif /*0*/ -#if defined(SIGWINCH) && defined(CLIPPING) +#ifdef RESIZABLE if (ymax > LI) ymax = LI; /* can happen if window gets smaller */ #endif @@ -3306,8 +3677,9 @@ docorner(register int xmin, register int ymax, int ystart_between_menu_pages) if (!ystart_between_menu_pages) cl_end(); /* clear to end of line */ #ifdef TTY_PERM_INVENT - /* the whole thing is beyond the board */ - if (icw) + /* the whole thing is beyond the board but not necessarily all the + way to the bottom of the screen */ + if (icw && y >= (int) icw->offy && y < icw->offy + icw->maxrow) tty_refresh_inventory(xmin - (int) icw->offx, icw->maxcol, y - (int) icw->offy); #endif @@ -3334,7 +3706,7 @@ docorner(register int xmin, register int ymax, int ystart_between_menu_pages) if (ymax >= (int) wins[WIN_STATUS]->offy && !ystart_between_menu_pages) { /* we have wrecked the bottom line */ - gc.context.botlx = 1; + disp.botlx = TRUE; bot(); } } @@ -3349,23 +3721,21 @@ end_glyphout(void) graph_off(); } #endif -#ifdef TEXTCOLOR if (ttyDisplay->color != NO_COLOR) { term_end_color(); ttyDisplay->color = NO_COLOR; } -#endif } -#ifndef WIN32CON void g_putch(int in_ch) { - register char ch = (char) 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) @@ -3383,21 +3753,32 @@ g_putch(int in_ch) } (void) putchar((ch ^ 0x80)); /* Strip 8th bit */ } else { - if (GFlag) { + if (GFlag +#ifdef DECgraphicsOptimization + /* for DECgraphics, we only need to switch back from the line + drawing character set to the normal one if 'ch' is a lowercase + letter or one of a handful of punctuation characters (the + range is contiguous but somewhat odd); deferring graph_off() + now might allow skipping both it and next potential graph_on() */ + && ch >= 0x5f && ch <= 0x7e +#endif + ) { graph_off(); GFlag = FALSE; } (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) @@ -3447,7 +3828,7 @@ tty_cliparound(int x, int y) clipy = clipymax - (LI - 1 - iflags.wc2_statuslines); } if (clipx != oldx || clipy != oldy) { - redraw_map(); /* ask the core to resend the map window's data */ + redraw_map(TRUE); /* ask the core to resend the map window's data */ } } #endif /* CLIPPING */ @@ -3470,11 +3851,10 @@ tty_print_glyph( { boolean inverse_on = FALSE, underline_on = FALSE, colordone = FALSE, glyphdone = FALSE; - int ch, color; + boolean petattr = FALSE; + int ch; + uint32 color; unsigned special; -#ifdef ENHANCED_SYMBOLS - boolean color24bit_on = FALSE; -#endif HUPSKIP(); #ifdef CLIPPING @@ -3502,49 +3882,48 @@ tty_print_glyph( } #endif if (iflags.use_color) { -#ifdef TEXTCOLOR + ttyDisplay->colorflags = NH_BASIC_COLOR; if (color != ttyDisplay->color) { if (ttyDisplay->color != NO_COLOR) term_end_color(); } -#endif -#ifdef ENHANCED_SYMBOLS /* we don't link with termcap.o if NO_TERMS is defined */ - if ((tty_procs.wincap2 & WC2_U_24BITCOLOR) && SYMHANDLING(H_UTF8) + if ((tty_procs.wincap2 & WC2_EXTRACOLORS) + && glyphinfo->gm.customcolor != 0 && iflags.colorcount >= 256 -#ifdef TTY_PERM_INVENT - && !calling_from_update_inventory -#endif - && glyphinfo->gm.u && glyphinfo->gm.u->ucolor) { - term_start_24bitcolor(glyphinfo->gm.u); - color24bit_on = TRUE; - colordone = TRUE; + && !calling_from_update_inventory) { + if ((glyphinfo->gm.customcolor & NH_BASIC_COLOR) == 0) { + term_start_extracolor(glyphinfo->gm.customcolor, + glyphinfo->gm.color256idx); + ttyDisplay->colorflags = 0; + colordone = TRUE; + } else { + color = COLORVAL(glyphinfo->gm.customcolor); + } } -#endif -#ifdef TEXTCOLOR if (!colordone) { ttyDisplay->color = color; if (color != NO_COLOR) term_start_color(color); } -#endif /* TEXTCOLOR */ } /* iflags.use_color aka iflags.wc_color */ /* must be after color check; term_end_color may turn off inverse too; - BW_LAVA and BW_ICE won't ever be set when color is on; + BW_LAVA, BW_ICE, BW_SINK, BW_ENGR won't ever be set when color is on; (tried bold for ice but it didn't look very good; inverse is easier to see although the Valkyrie quest ends up being hard on the eyes) */ if (iflags.use_color && bkglyphinfo && bkglyphinfo->framecolor != NO_COLOR) { -#ifdef TEXTCOLOR ttyDisplay->framecolor = bkglyphinfo->framecolor; term_start_bgcolor(bkglyphinfo->framecolor); -#endif - } else if (((special & MG_PET) != 0 && iflags.hilite_pet) - || ((special & MG_OBJPILE) != 0 && iflags.hilite_pile) - || ((special & MG_FEMALE) != 0 && wizard && iflags.wizmgender) - || ((special & (MG_DETECT | MG_BW_LAVA | MG_BW_ICE | MG_BW_SINK)) != 0 - && iflags.use_inverse)) { + } else if ((special & MG_PET) != 0 && iflags.hilite_pet) { + term_start_attr(iflags.wc2_petattr); + petattr = TRUE; + } else if ((((special & MG_OBJPILE) != 0 && iflags.hilite_pile) + || ((special & MG_FEMALE) != 0 && wizard && iflags.wizmgender) + || ((special & (MG_DETECT | MG_BW_LAVA | MG_BW_ICE + | MG_BW_SINK | MG_BW_ENGR)) != 0)) + && iflags.use_inverse) { term_start_attr(ATR_INVERSE); inverse_on = TRUE; } @@ -3577,20 +3956,19 @@ tty_print_glyph( } if (inverse_on) term_end_attr(ATR_INVERSE); + else if (petattr) + term_end_attr(iflags.wc2_petattr); if (iflags.use_color) { -#ifdef TEXTCOLOR /* turn off color as well, turning off ATR_INVERSE may have done this already and if so, we won't know the current state unless we do it explicitly */ - if (ttyDisplay->color != NO_COLOR || ttyDisplay->framecolor != NO_COLOR) { + if (ttyDisplay->color != NO_COLOR + || ttyDisplay->framecolor != NO_COLOR) { term_end_color(); ttyDisplay->color = ttyDisplay->framecolor = NO_COLOR; } -#endif -#ifdef ENHANCED_SYMBOLS - if (color24bit_on) - term_end_24bitcolor(); -#endif + if (ttyDisplay->colorflags != NH_BASIC_COLOR) + term_end_extracolor(); } print_vt_code1(AVTC_GLYPH_END); @@ -3605,7 +3983,21 @@ term_start_bgcolor(int color) { /* placeholder for now */ } -#endif /* !MSDOS && !WIN32 */ +void +term_curs_set(int visibility UNUSED) +{ + /* nothing */ +} +#endif + +#ifdef CHANGE_COLOR +void +tty_change_color(int color UNUSED, long rgb UNUSED, int reverse UNUSED) +{ + /* nothing */ +} +#endif /* CHANGE_COLOR */ + #endif /* NO_TERMS */ void @@ -3614,6 +4006,8 @@ tty_raw_print(const char *str) HUPSKIP(); if (ttyDisplay) ttyDisplay->rawprint++; + else if (*str) + iflags.raw_printed++; print_vt_code2(AVTC_SELECT_WINDOW, NHW_BASE); #if defined(MICRO) || defined(WIN32CON) msmsg("%s\n", str); @@ -3629,6 +4023,8 @@ tty_raw_print_bold(const char *str) HUPSKIP(); if (ttyDisplay) ttyDisplay->rawprint++; + else if (*str) + iflags.raw_printed++; print_vt_code2(AVTC_SELECT_WINDOW, NHW_BASE); term_start_raw_bold(); #if defined(MICRO) || defined(WIN32CON) @@ -3650,17 +4046,16 @@ tty_nhgetch(void) { int i; #ifdef UNIX - /* kludge alert: Some Unix variants return funny values if getc() - * is called, interrupted, and then called again. There - * is non-reentrant code in the internal _filbuf() routine, called by - * getc(). + /* kludge alert: Some Unix variants return funny values if getc() is + * called, interrupted, and then called again. There is non-reentrant + * code in the internal _filbuf() routine, called by getc(). */ - static volatile int nesting = 0; char nestbuf; #endif HUPSKIP_RESULT('\033'); print_vt_code1(AVTC_INLINE_SYNC); + term_curs_set(1); (void) fflush(stdout); /* Note: if raw_print() and wait_synch() get called to report terminal * initialization problems, then wins[] and ttyDisplay might not be @@ -3672,21 +4067,34 @@ tty_nhgetch(void) if (iflags.debug_fuzzer) { i = randomkey(); } else { +#ifdef RESIZABLE + if (program_state.resize_pending) + resize_tty(); +#endif + program_state.getting_char++; #ifdef UNIX - i = (++nesting == 1) + i = (program_state.getting_char == 1) ? tgetch() - : (read(fileno(stdin), (genericptr_t) &nestbuf, 1) == 1) - ? (int) nestbuf : EOF; - --nesting; + : ((read(fileno(stdin), (genericptr_t) &nestbuf, 1) == 1) + ? (int) nestbuf : EOF); #else i = tgetch(); +#endif + program_state.getting_char--; +#ifdef RESIZABLE + if (resize_mesg) { + resize_mesg = 0; + tty_clear_nhwindow(WIN_MESSAGE); + i = '\033'; + } #endif } + term_curs_set(0); if (!i) i = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */ else if (i == EOF) i = '\033'; /* same for EOF */ - /* topline has been seen - we can clear need for more */ + /* topline has been seen - we can clear the need for --More-- */ if (ttyDisplay && ttyDisplay->toplin == TOPLINE_NEED_MORE) ttyDisplay->toplin = TOPLINE_NON_EMPTY; #ifdef TTY_TILES_ESCCODES @@ -3729,7 +4137,7 @@ tty_nh_poskey(coordxy *x UNUSED, coordxy *y UNUSED, int *mod UNUSED) i = console_poskey(x, y, mod); if (!i && mod && (*mod == 0 || *mod == EOF)) i = '\033'; /* map NUL or EOF to ESC, nethack doesn't expect either */ - /* topline has been seen - we can clear need for more */ + /* topline has been seen - we can clear the need for --More-- */ if (ttyDisplay && ttyDisplay->toplin == TOPLINE_NEED_MORE) ttyDisplay->toplin = TOPLINE_NON_EMPTY; #else /* !WIN32CON */ @@ -3769,6 +4177,7 @@ tty_putmixed(winid window, int attr, const char *str) tty_raw_print(str); return; } + ttyDisplay->mixed = 1; #ifdef ENHANCED_SYMBOLS if ((windowprocs.wincap2 & WC2_U_UTF8STR) && SYMHANDLING(H_UTF8)) { mixed_to_utf8(buf, sizeof buf, str, &utf8flag); @@ -3780,6 +4189,7 @@ tty_putmixed(winid window, int attr, const char *str) /* now send it to the normal tty_putstr */ tty_putstr(window, attr, buf); ttyDisplay->topl_utf8 = 0; + ttyDisplay->mixed = 0; } /* @@ -3846,46 +4256,46 @@ extern boolean status_activefields[MAXBLSTATS]; extern winid WIN_STATUS; #ifdef STATUS_HILITES -#ifdef TEXTCOLOR static int condcolor(long, unsigned long *); -#endif static int condattr(long, unsigned long *); static unsigned long *tty_colormasks; static long tty_condition_bits; static struct tty_status_fields tty_status[2][MAXBLSTATS]; /* 2: NOW,BEFORE */ -static int hpbar_percent, hpbar_color; +static int hpbar_percent, hpbar_crit_hp; extern const struct conditions_t conditions[CONDITION_COUNT]; -static const char *encvals[3][6] = { +static const char *const encvals[3][6] = { { "", "Burdened", "Stressed", "Strained", "Overtaxed", "Overloaded" }, { "", "Burden", "Stress", "Strain", "Overtax", "Overload" }, { "", "Brd", "Strs", "Strn", "Ovtx", "Ovld" } }; #define blPAD BL_FLUSH -#define MAX_PER_ROW 15 +#define MAX_PER_ROW 16 /* 2 or 3 status lines */ static const enum statusfields twolineorder[3][MAX_PER_ROW] = { { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN, - BL_SCORE, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD }, + BL_SCORE, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }, { 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, BL_FLUSH }, + BL_CAP, BL_CONDITION, BL_VERS, BL_FLUSH }, /* third row of array isn't used for twolineorder */ { BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, - blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD } + blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD } }, - /* Align moved from 1 to 2, Leveldesc+Time+Condition moved from 2 to 3 */ + /* Align moved from 1 to 2, Leveldesc+Time+Cond+Vers moved from 2 to 3 */ threelineorder[3][MAX_PER_ROW] = { { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, - BL_SCORE, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }, + BL_SCORE, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }, { BL_ALIGN, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX, BL_AC, BL_XP, BL_EXP, BL_HD, BL_HUNGER, - BL_CAP, BL_FLUSH, blPAD, blPAD }, - { BL_LEVELDESC, BL_TIME, BL_CONDITION, BL_FLUSH, blPAD, blPAD, - blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD } + BL_CAP, BL_FLUSH, blPAD, blPAD, blPAD }, + { BL_LEVELDESC, BL_TIME, BL_CONDITION, BL_VERS, BL_FLUSH, blPAD, + blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD } }; static const enum statusfields (*fieldorder)[MAX_PER_ROW]; +#undef MAX_PER_ROW +#undef blPAD static int finalx[3][2]; /* [rows][NOW or BEFORE] */ static boolean windowdata_init = FALSE; @@ -3924,7 +4334,7 @@ tty_status_init(void) #ifdef STATUS_HILITES int i, num_rows; - num_rows = (iflags.wc2_statuslines < 3) ? 2 : 3; + num_rows = StatusRows(); /* 2 or 3 */ fieldorder = (num_rows != 3) ? twolineorder : threelineorder; for (i = 0; i < MAXBLSTATS; ++i) { @@ -3939,7 +4349,7 @@ tty_status_init(void) tty_status[BEFORE][i] = tty_status[NOW][i]; } tty_condition_bits = 0L; - hpbar_percent = 0, hpbar_color = NO_COLOR; + hpbar_percent = hpbar_crit_hp = 0; #endif /* STATUS_HILITES */ /* let genl_status_init do most of the initialization */ @@ -3947,8 +4357,11 @@ tty_status_init(void) } void -tty_status_enablefield(int fieldidx, const char *nm, const char *fmt, - boolean enable) +tty_status_enablefield( + int fieldidx, + const char *nm, + const char *fmt, + boolean enable) { genl_status_enablefield(fieldidx, nm, fmt, enable); } @@ -3964,13 +4377,13 @@ tty_status_enablefield(int fieldidx, const char *nm, const char *fmt, * BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, * BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX, * BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX, - * BL_LEVELDESC, BL_EXP, BL_CONDITION + * BL_LEVELDESC, BL_EXP, BL_CONDITION, BL_VERS * -- fldindex could also be BL_FLUSH (-1), which is not really * a field index, but is a special trigger to tell the * windowport that it should output all changes received * to this point. It marks the end of a bot() cycle. * -- fldindex could also be BL_RESET (-3), which is not really - * a field index, but is a special advisory to to tell the + * a field index, but is a special advisory to tell the * windowport that it should redisplay all its status fields, * even if no changes have been presented to it. * -- ptr is usually a "char *", unless fldindex is BL_CONDITION. @@ -4058,6 +4471,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) { @@ -4078,12 +4492,10 @@ tty_status_update( break; case BL_GOLD: text = decode_mixed(goldbuf, text); + FALLTHROUGH; /*FALLTHRU*/ default: attrmask = (color >> 8) & 0x00FF; -#ifndef TEXTCOLOR - color = NO_COLOR; -#endif fmt = status_fieldfmt[fldidx]; if (!fmt) fmt = "%s"; @@ -4106,7 +4518,7 @@ tty_status_update( } /* The core botl engine sends a single blank to the window port - for carrying-capacity when its unused. Let's suppress that */ + for carrying-capacity when it's unused. Let's suppress that */ if (fldidx >= 0 && fldidx < MAXBLSTATS && tty_status[NOW][fldidx].lth == 1 && status_vals[fldidx][0] == ' ') { @@ -4120,13 +4532,16 @@ tty_status_update( if (iflags.wc2_hitpointbar) { /* Special additional processing for hitpointbar */ hpbar_percent = percent; - hpbar_color = (color & 0x00FF); - tty_status[NOW][BL_TITLE].color = hpbar_color; + hpbar_crit_hp = critically_low_hp(TRUE) ? 1 : 0; + tty_status[NOW][BL_TITLE].color = (color & 0x00FF); + attrmask = HL_INVERSE | (hpbar_crit_hp ? HL_BLINK : 0); + tty_status[NOW][BL_TITLE].attr = term_attr_fixup(attrmask); tty_status[NOW][BL_TITLE].dirty = TRUE; } 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. @@ -4167,8 +4582,7 @@ make_things_fit(boolean force_update) int trycnt, fitting = 0, requirement; int rowsz[MAX_STATUS_ROWS], num_rows, condrow, otheroptions = 0; - num_rows = (iflags.wc2_statuslines < MAX_STATUS_ROWS) - ? 2 : MAX_STATUS_ROWS; + num_rows = StatusRows(); condrow = num_rows - 1; /* always last row, 1 for 0..1 or 2 for 0..2 */ cond_shrinklvl = 0; if (enc_shrinklvl > 0 && num_rows == 2) @@ -4234,9 +4648,7 @@ check_fields(boolean forcefields, int sz[MAX_STATUS_ROWS]) if (!windowdata_init && !check_windowdata()) return FALSE; - num_rows = (iflags.wc2_statuslines < MAX_STATUS_ROWS) - ? 2 : MAX_STATUS_ROWS; - + num_rows = StatusRows(); /* 2 or 3 */ for (row = 0; row < num_rows; ++row) { sz[row] = 0; col = 1; @@ -4330,15 +4742,16 @@ check_fields(boolean forcefields, int sz[MAX_STATUS_ROWS]) static void status_sanity_check(void) { - int i; - static boolean in_sanity_check = FALSE; static const char *const idxtext[] = { - "BL_TITLE", "BL_STR", "BL_DX", "BL_CO", "BL_IN", "BL_WI", /* 0.. 5 */ - "BL_CH","BL_ALIGN", "BL_SCORE", "BL_CAP", "BL_GOLD", /* 6.. 10 */ - "BL_ENE", "BL_ENEMAX", "BL_XP", "BL_AC", "BL_HD", /* 11.. 15 */ - "BL_TIME", "BL_HUNGER", "BL_HP", "BL_HPMAX", /* 16.. 19 */ - "BL_LEVELDESC", "BL_EXP", "BL_CONDITION" /* 20.. 22 */ + "BL_TITLE", "BL_STR", "BL_DX", "BL_CO", "BL_IN", "BL_WI", /* 0.. 5 */ + "BL_CH","BL_ALIGN", "BL_SCORE", "BL_CAP", "BL_GOLD", /* 6..10 */ + "BL_ENE", "BL_ENEMAX", "BL_XP", "BL_AC", "BL_HD", /* 11..15 */ + "BL_TIME", "BL_HUNGER", "BL_HP", "BL_HPMAX", /* 16..19 */ + "BL_LEVELDESC", "BL_EXP", "BL_CONDITION", /* 20..22 */ + "BL_VERS", /* 23 */ }; + static boolean in_sanity_check = FALSE; + int i; if (in_sanity_check) return; @@ -4349,9 +4762,15 @@ status_sanity_check(void) */ for (i = 0; i < MAXBLSTATS; ++i) { if (tty_status[NOW][i].sanitycheck) { - char panicmsg[BUFSZ]; + char panicmsg[BUFSZ], indxtxt[40]; - Sprintf(panicmsg, "failed on tty_status[NOW][%s].", idxtext[i]); + /* guard against an increase in MAXBLSTATS in botl.h without + a corresponding expansion of idxtext[] here */ + if (i < SIZE(idxtext)) + Strcpy(indxtxt, idxtext[i]); + else + Sprintf(indxtxt, "%d", i); + Sprintf(panicmsg, "failed on tty_status[NOW][%s].", indxtxt); paniclog("status_sanity_check", panicmsg); tty_status[NOW][i].sanitycheck = FALSE; /* @@ -4476,7 +4895,6 @@ check_windowdata(void) return TRUE; } -#ifdef TEXTCOLOR /* * Return what color this condition should * be displayed in based on user settings. @@ -4493,13 +4911,6 @@ condcolor(long bm, unsigned long *bmarray) } return NO_COLOR; } -#else -/* might need something more elaborate if some compiler complains that - the condition where this gets used always has the same value */ -#define condcolor(bm,bmarray) NO_COLOR -#define term_start_color(color) /*empty*/ -#define term_end_color() /*empty*/ -#endif /* TEXTCOLOR */ static int condattr(long bm, unsigned long *bmarray) @@ -4508,24 +4919,27 @@ condattr(long bm, unsigned long *bmarray) int i; if (bm && bmarray) { - for (i = HL_ATTCLR_DIM; i < BL_ATTCLR_MAX; ++i) { + for (i = HL_ATTCLR_BOLD; i < BL_ATTCLR_MAX; ++i) { if ((bm & bmarray[i]) != 0) { switch (i) { + case HL_ATTCLR_BOLD: + attr |= HL_BOLD; + break; case HL_ATTCLR_DIM: attr |= HL_DIM; break; - case HL_ATTCLR_BLINK: - attr |= HL_BLINK; + case HL_ATTCLR_ITALIC: + attr |= HL_ITALIC; break; case HL_ATTCLR_ULINE: attr |= HL_ULINE; break; + case HL_ATTCLR_BLINK: + attr |= HL_BLINK; + break; case HL_ATTCLR_INVERSE: attr |= HL_INVERSE; break; - case HL_ATTCLR_BOLD: - attr |= HL_BOLD; - break; } } } @@ -4538,28 +4952,32 @@ condattr(long bm, unsigned long *bmarray) if (m) { \ if ((m) & HL_BOLD) \ term_start_attr(ATR_BOLD); \ - if ((m) & HL_INVERSE) \ - term_start_attr(ATR_INVERSE); \ + if ((m) & HL_DIM) \ + term_start_attr(ATR_DIM); \ + if ((m) & HL_ITALIC) \ + term_start_attr(ATR_ITALIC); \ if ((m) & HL_ULINE) \ term_start_attr(ATR_ULINE); \ if ((m) & HL_BLINK) \ term_start_attr(ATR_BLINK); \ - if ((m) & HL_DIM) \ - term_start_attr(ATR_DIM); \ + if ((m) & HL_INVERSE) \ + term_start_attr(ATR_INVERSE); \ } \ } while (0) #define End_Attr(m) \ do { \ if (m) { \ - if ((m) & HL_DIM) \ - term_end_attr(ATR_DIM); \ + if ((m) & HL_INVERSE) \ + term_end_attr(ATR_INVERSE); \ if ((m) & HL_BLINK) \ term_end_attr(ATR_BLINK); \ if ((m) & HL_ULINE) \ term_end_attr(ATR_ULINE); \ - if ((m) & HL_INVERSE) \ - term_end_attr(ATR_INVERSE); \ + if ((m) & HL_ITALIC) \ + term_end_attr(ATR_ITALIC); \ + if ((m) & HL_DIM) \ + term_end_attr(ATR_DIM); \ if ((m) & HL_BOLD) \ term_end_attr(ATR_BOLD); \ } \ @@ -4569,7 +4987,8 @@ static void render_status(void) { long mask, bits; - int i, x, y, idx, c, ci, row, tlth, num_rows, coloridx = 0, attrmask = 0; + int i, x, y, idx, c, ci, row, tlth, num_rows, + coloridx = 0, attrmask = 0; char *text; struct WinDesc *cw = 0; @@ -4579,7 +4998,7 @@ render_status(void) return; } - num_rows = (iflags.wc2_statuslines < MAX_STATUS_ROWS) ? 2 : MAX_STATUS_ROWS; + num_rows = StatusRows(); /* 2 or 3 */ for (row = 0; row < num_rows; ++row) { HUPSKIP(); y = row; @@ -4602,13 +5021,24 @@ render_status(void) * +-----------------+ */ bits = tty_condition_bits; - /* if no bits are set, we can fall through condition - rendering code to finalx[] handling (and subsequent - rest-of-line erasure if line is shorter than before) */ - if (num_rows == MAX_STATUS_ROWS && bits != 0L) { - int k; + /* + * If no bits are set, we can fall through condition + * rendering code to finalx[] handling (and subsequent + * rest-of-line erasure if line is shorter than before). + * + * First, when conditions are on 3rd row, they might + * be indented to line up with a position on 2nd row. + */ + if (row == MAX_STATUS_ROWS - 1 && bits != 0L) { + int cstart, last_col = cw->cols; char *dat = &cw->data[y][0]; + /* 'version' might follow conditions; if so, adjust + expectations for where conditions should end; + only matters when conditions are being indented */ + if (status_activefields[BL_VERS] + && fieldorder[row][i + 1] == BL_VERS) + last_col -= (int) tty_status[NOW][BL_VERS].lth; /* line up with hunger (or where it would have been when currently omitted); if there isn't enough room for that, right justify; or place @@ -4618,20 +5048,23 @@ render_status(void) if (tty_status[BEFORE][BL_HUNGER].y < row && x < tty_status[BEFORE][BL_HUNGER].x && (tty_status[BEFORE][BL_HUNGER].x + tlth - < cw->cols - 1)) - k = tty_status[BEFORE][BL_HUNGER].x; + < last_col - 1)) + cstart = tty_status[BEFORE][BL_HUNGER].x; else if (x + tlth < cw->cols - 1) - k = cw->cols - tlth; + cstart = last_col - tlth; else - k = x; - while (x < k) { - if (dat[x - 1] != ' ') - tty_putstatusfield(" ", x, y); - ++x; + cstart = x; + /* indent conditions to line them up with 2nd row */ + if (x < cstart) { + do { + if (dat[x - 1] != ' ') + tty_putstatusfield(" ", x, y); + } while (++x < cstart); + tty_status[NOW][BL_CONDITION].x = x; + tty_curs(WIN_STATUS, x, y); } - tty_status[NOW][BL_CONDITION].x = x; - tty_curs(WIN_STATUS, x, y); } + /* actually draw condition words */ for (c = 0; c < SIZE(conditions) && bits != 0L; ++c) { ci = cond_idx[c]; mask = conditions[ci].mask; @@ -4684,7 +5117,7 @@ render_status(void) */ /* hitpointbar using hp percent calculation */ int bar_len, bar_pos = 0; - char bar[MAXCO], *bar2 = (char *) 0, savedch = '\0'; + char bar[30 + 1], *bar2 = (char *) 0, savedch = '\0'; boolean twoparts = (hpbar_percent < 100); /* force exactly 30 characters, padded with spaces @@ -4692,10 +5125,14 @@ render_status(void) if (strlen(text) != 30) { Sprintf(bar, "%-30.30s", text); Strcpy(status_vals[BL_TITLE], bar); - } else + } else { Strcpy(bar, text); + } + if (hpbar_crit_hp) + repad_with_dashes(bar); bar_len = (int) strlen(bar); /* always 30 */ tlth = bar_len + 2; + 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 which is not highlighted */ @@ -4712,19 +5149,25 @@ render_status(void) } tty_putstatusfield("[", x++, y); if (*bar) { /* always True, unless twoparts+dead (0 HP) */ - term_start_attr(ATR_INVERSE); - if (iflags.hilite_delta && hpbar_color != NO_COLOR) - term_start_color(hpbar_color); + coloridx = tty_status[NOW][BL_TITLE].color; + attrmask = tty_status[NOW][BL_TITLE].attr; + Begin_Attr(attrmask); + if (iflags.hilite_delta && coloridx != NO_COLOR) + term_start_color(coloridx); tty_putstatusfield(bar, x, y); x += (int) strlen(bar); - if (iflags.hilite_delta && hpbar_color != NO_COLOR) + if (iflags.hilite_delta && coloridx != NO_COLOR) term_end_color(); - term_end_attr(ATR_INVERSE); + End_Attr(attrmask); } - if (twoparts) { /* no highlighting for second part */ + if (twoparts) { + if ((attrmask & HL_BLINK) != 0) + term_start_attr(ATR_BLINK); *bar2 = savedch; tty_putstatusfield(bar2, x, y); x += (int) strlen(bar2); + if ((attrmask & HL_BLINK) != 0) + term_end_attr(ATR_BLINK); } tty_putstatusfield("]", x++, y); } else { @@ -4734,6 +5177,34 @@ render_status(void) * | in a special case above | * +-----------------------------+ */ + if (idx == BL_VERS + /* if 'version' is the last field in its row, right + justify it (otherwise just treat it as ordinary) */ + && fieldorder[row][i + 1] == BL_FLUSH) { + int vstart; + char *dat = &cw->data[y][0]; + /* FIXME: there's something fishy going on here; + 'x' ends up out of synch when conditions have + 3rd row indentation and the indenting of version + overwrites them with spaces; this hides that */ + int vx = tty_status[BEFORE][BL_CONDITION].x + + tty_status[BEFORE][BL_CONDITION].lth; + + if (i > 0 && fieldorder[row][i - 1] == BL_CONDITION + && x != vx) { + x = vx; + tty_curs(WIN_STATUS, x, y); + } + /* indent version to right justify it */ + vstart = cw->cols - (int) tty_status[NOW][idx].lth; + if (x < vstart) { + do { + if (dat[x - 1] != ' ') + tty_putstatusfield(" ", x, y); + } while (++x < vstart); + tty_status[NOW][BL_VERS].x = x; + } + } if (iflags.hilite_delta) { while (*text == ' ') { tty_putstatusfield(" ", x++, y); @@ -4771,7 +5242,7 @@ render_status(void) * - Copy the entire tty_status struct. */ tty_status[BEFORE][idx] = tty_status[NOW][idx]; - } + } /* for i=..., idx=fieldorder[][i] */ x = finalx[row][NOW]; if ((x < finalx[row][BEFORE] || !finalx[row][BEFORE]) && x + 1 < cw->cols) { @@ -4783,20 +5254,64 @@ render_status(void) * - Copy the last written column number on the row. */ finalx[row][BEFORE] = finalx[row][NOW]; - } + } /* for row=... */ return; } +#undef Begin_Attr +#undef End_Attr +#ifdef condcolor +#undef condcolor +#endif +#ifdef term_start_color +#undef term_start_color +#undef term_end_color +#endif +#undef FORCE_RESET +#undef NO_RESET +#undef MAX_STATUS_ROWS +#undef StatusRows + #endif /* STATUS_HILITES */ #if defined(USER_SOUNDS) && defined(TTY_SOUND_ESCCODES) void play_usersound_via_idx(int idx, int volume) { - print_vt_soundcode_idx(idx, volume); + print_vt_soundcode_idx(idx, volume); } #endif /* USER_SOUNDS && TTY_SOUND_ESCCODES */ +#ifdef VT_ANSI_COMMAND +#undef VT_ANSI_COMMAND +#endif +#ifdef AVTC_GLYPH_START /* TTY_TILES_ESCCODES */ +#undef AVTC_GLYPH_START +#undef AVTC_GLYPH_END +#undef AVTC_SELECT_WINDOW +#undef AVTC_INLINE_SYNC +#endif +#ifdef AVTC_SOUND_PLAY /* TTY_SOUND_ESCCODES */ +#undef AVTC_SOUND_PLAY +#endif + +#ifdef print_vt_code +#undef print_vt_code +#endif +#undef print_vt_code1 +#undef print_vt_code2 +#undef print_vt_code3 +#ifdef print_vt_soundcode_idx +#undef print_vt_soundcode_idx +#endif + +#undef RESIZABLE +#undef HUPSKIP +#undef HUPSKIP_RESULT +#undef ttypanic + #endif /* TTY_GRAPHICS */ +#undef H2344_BROKEN + /*wintty.c*/ diff --git a/win/win32/NetHackW.c b/win/win32/NetHackW.c index 632c0595e6..45f8bf7099 100644 --- a/win/win32/NetHackW.c +++ b/win/win32/NetHackW.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 NetHackW.c $NHDT-Date: 1596498365 2020/08/03 23:46:05 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.72 $ */ +/* NetHack 3.7 NetHackW.c $NHDT-Date: 1693359674 2023/08/30 01:41:14 $ $NHDT-Branch: keni-crashweb2 $:$NHDT-Revision: 1.79 $ */ /* Copyright (C) 2001 by Alex Kompel */ /* NetHack may be freely redistributed. See license for details. */ @@ -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 @@ -71,19 +70,27 @@ int GUILaunched = TRUE; /* We tell shared startup code in windmain.c #define _strdup(s1) strdup(s1) #endif -// Foward declarations of functions included in this code module: +// 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 +#ifdef _MSC_VER +#pragma warning( push ) +#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]; @@ -123,7 +130,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, windowprocs.win_wait_synch = mswin_wait_synch; win10_init(); - early_init(); + early_init(0, NULL); /* Change as needed to support CRASHREPORT */ /* init application structure */ _nethack_app.hApp = hInstance; @@ -158,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; @@ -165,7 +174,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, _nethack_app.bNoSounds = FALSE; -#if 0 /* GdiTransparentBlt does not render spash bitmap for whatever reason */ +#if 0 /* GdiTransparentBlt does not render splash bitmap for whatever reason */ /* use system-provided TransparentBlt for Win2k+ */ ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); @@ -201,18 +210,18 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, /* get command line parameters */ p = _get_cmd_arg(GetCommandLine()); - p = _get_cmd_arg(NULL); /* skip first paramter - command name */ + p = _get_cmd_arg(NULL); /* skip first parameter - command name */ 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]); @@ -227,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; } } @@ -236,16 +245,40 @@ 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 + PNHWinApp GetNHApp(void) { @@ -293,7 +326,7 @@ _get_cmd_arg(TCHAR *pCmdLine) } else { pArgs = NULL; } - + nhUse(bQuoted); return pRetArg; } @@ -411,7 +444,7 @@ _nhapply_image_transparent(HDC hDC, int x, int y, int width, int height, /* Mask out the transparent colored pixels on the source image. */ BitBlt(hdcSave, 0, 0, width, height, hdcBack, 0, 0, SRCAND); - /* XOR the source image with the beckground. */ + /* XOR the source image with the background. */ BitBlt(hdcMem, 0, 0, width, height, hdcSave, 0, 0, SRCPAINT); /* blt resulting image to the screen */ diff --git a/win/win32/mhdlg.c b/win/win32/mhdlg.c index 76874634b4..94a7ecf3e5 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 */ @@ -38,7 +41,7 @@ mswin_getlin_window(const char *question, char *result, size_t result_size) INT_PTR ret; struct getlin_data data; - /* initilize dialog data */ + /* initialize dialog data */ ZeroMemory(&data, sizeof(data)); data.question = question; data.result = result; @@ -136,6 +139,7 @@ GetlinDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_COMMAND: { TCHAR wbuf2[BUFSZ]; + wbuf2[BUFSZ - 1] = '\0'; switch (LOWORD(wParam)) { /* OK button was pressed */ case IDOK: @@ -145,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: @@ -245,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: @@ -343,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) @@ -401,7 +412,7 @@ calculate_player_selector_layout(plsel_data_t * data) name_box->size.cy = (int) (24 * scale); control_t * role_list = &data->controls[psc_role_list]; - /* NOTE: we dont' scale the list view reported height as it appears these + /* NOTE: we don't scale the list view reported height as it appears these values are the actual size the control will be drawn at using the existing DPI value */ role_list->size.cy = list_view_height(role_list->hWnd, data->role_count); @@ -647,7 +658,7 @@ plselInitDialog(struct plsel_data * data) /* set player name */ control_t * name_box = &data->controls[psc_name_box]; - SetDlgItemText(data->dialog, name_box->id, NH_A2W(gp.plname, wbuf, sizeof(wbuf))); + SetDlgItemText(data->dialog, name_box->id, NH_A2W(svp.plname, wbuf, sizeof(wbuf))); plselRandomize(data); @@ -871,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; @@ -981,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 0b08e8fcb9..7eb0b8eb9e 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/mhfont.c b/win/win32/mhfont.c index 638fac5aa9..291acacbc5 100644 --- a/win/win32/mhfont.c +++ b/win/win32/mhfont.c @@ -55,7 +55,7 @@ mswin_font_supports_unicode(HFONT hFont) return FALSE; } -/* create font based on window type, charater attributes and +/* create font based on window type, character attributes and window device context */ cached_font * mswin_get_font(int win_type, int attr, HDC hdc, BOOL replace) diff --git a/win/win32/mhinput.c b/win/win32/mhinput.c index e2317870f7..9ad6c32f03 100644 --- a/win/win32/mhinput.c +++ b/win/win32/mhinput.c @@ -39,7 +39,7 @@ mswin_have_input(void) return #ifdef SAFERHANGUP /* we always have input (ESC) if hangup was requested */ - gp.program_state.done_hup || + program_state.done_hup || #endif (nhi_read_pos != nhi_write_pos); } @@ -69,7 +69,7 @@ mswin_input_pop(void) #ifdef SAFERHANGUP /* always return ESC when hangup was requested */ - if (gp.program_state.done_hup) { + if (program_state.done_hup) { static MSNHEvent hangup_event; hangup_event.type = NHEVENT_CHAR; hangup_event.ei.kbd.ch = '\033'; @@ -98,7 +98,7 @@ mswin_input_peek(void) #ifdef SAFERHANGUP /* always return ESC when hangup was requested */ - if (gp.program_state.done_hup) { + if (program_state.done_hup) { static MSNHEvent hangup_event; hangup_event.type = NHEVENT_CHAR; hangup_event.ei.kbd.ch = '\033'; diff --git a/win/win32/mhmain.c b/win/win32/mhmain.c index 1072ca8029..00e47e9181 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( @@ -383,7 +386,7 @@ MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) c = 0; ZeroMemory(kbd_state, sizeof(kbd_state)); - GetKeyboardState(kbd_state); + (void) GetKeyboardState(kbd_state); if (ToAscii((UINT) wParam, (lParam >> 16) & 0xFF, kbd_state, &c, 0)) { NHEVENT_KBD(c & 0xFF); @@ -457,16 +460,16 @@ MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_CLOSE: { /* exit gracefully */ - if (gp.program_state.gameover) { + if (program_state.gameover) { /* assume the user really meant this, as the game is already * over... */ /* to make sure we still save bones, just set stop printing flag */ - gp.program_state.stopprint++; + program_state.stopprint++; NHEVENT_KBD( '\033'); /* and send keyboard input as if user pressed ESC */ /* additional code for this is done in menu and rip windows */ - } else if (!gp.program_state.something_worth_saving) { + } else if (!program_state.something_worth_saving) { /* User exited before the game started, e.g. during splash display */ /* Just get out. */ @@ -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; @@ -842,7 +850,7 @@ onWMCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) case IDM_SAVE: if (iflags.debug_fuzzer) break; - if (!gp.program_state.gameover && !gp.program_state.done_hup) + if (!program_state.gameover && !program_state.done_hup) dosave(); else MessageBeep(0); @@ -922,16 +930,23 @@ onWMCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) #ifdef ENHANCED_SYMBOLS if (SYMHANDLING(H_UTF8)) { - WCHAR *p_copy = (WCHAR *) GlobalLock(hglbCopy); - wcsncpy(p_copy, wp, len); - p_copy[len] = 0; // null character - } else + WCHAR *p_copy; + + if ((p_copy = (WCHAR *) GlobalLock(hglbCopy)) != 0) { + wcsncpy(p_copy, wp, len); + p_copy[len] = 0; // null character + } + } else { #endif - { - char *p_copy = (char *) GlobalLock(hglbCopy); - strncpy(p_copy, p, len); - p_copy[len] = 0; // null character + char *p_copy; + + if ((p_copy = (char *) GlobalLock(hglbCopy)) != 0) { + strncpy(p_copy, p, len); + p_copy[len] = 0; // null character + } +#ifdef ENHANCED_SYMBOLS } +#endif GlobalUnlock(hglbCopy); #ifdef ENHANCED_SYMBOLS @@ -1001,28 +1016,34 @@ onWMCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) text = nh_compose_ascii_screenshot(); if (!text) return FALSE; - tlen = strlen(text); wtext = (wchar_t *) malloc(tlen * sizeof(wchar_t)); - if (!wtext) - panic("out of memory"); - MultiByteToWideChar(NH_CODEPAGE, 0, text, -1, wtext, tlen); + if (wtext) { + MultiByteToWideChar(NH_CODEPAGE, 0, text, -1, wtext, tlen); + } } + filename[SIZE(filename) - 1] = '\0'; 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); - free(text); - free(wtext); + if (text) + free(text); + if (wtext) + free(wtext); return FALSE; } - fwrite(wtext, tlen * sizeof(wchar_t), 1, pFile); - fclose(pFile); - free(text); - free(wtext); + if (wtext) { + fwrite(wtext, tlen * sizeof(wchar_t), 1, pFile); + fclose(pFile); + free(wtext); + } + if (text) + free(text); } break; case IDM_NHMODE: { @@ -1098,10 +1119,11 @@ onWMCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) default: return 1; } + nhUse(wmEvent); return 0; } -// Mesage handler for about box. +// Message handler for about box. LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { @@ -1197,7 +1219,7 @@ mswin_select_map_mode(int mode) iflags.wc_map_mode = mode; /* - ** first, check if WIN_MAP has been inialized. + ** first, check if WIN_MAP has been initialized. ** If not - attempt to retrieve it by type, then check it again */ if (map_id == WIN_ERR) @@ -1303,26 +1325,28 @@ nh_compose_ascii_screenshot(void) text = (PMSNHMsgGetText) malloc(sizeof(MSNHMsgGetText) + TEXT_BUFFER_SIZE); - 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); - SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND, - (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) text); - strcpy(retval, text->buffer); - - ZeroMemory(text->buffer, TEXT_BUFFER_SIZE); - SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_MSNH_COMMAND, - (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) text); - strcat(retval, text->buffer); - - ZeroMemory(text->buffer, TEXT_BUFFER_SIZE); - SendMessage(mswin_hwnd_from_winid(WIN_STATUS), WM_MSNH_COMMAND, - (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) text); - strcat(retval, text->buffer); - - free(text); + if (text && retval) { + 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); + SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND, + (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) text); + strcpy(retval, text->buffer); + + ZeroMemory(text->buffer, TEXT_BUFFER_SIZE); + SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_MSNH_COMMAND, + (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) text); + strcat(retval, text->buffer); + + ZeroMemory(text->buffer, TEXT_BUFFER_SIZE); + SendMessage(mswin_hwnd_from_winid(WIN_STATUS), WM_MSNH_COMMAND, + (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) text); + strcat(retval, text->buffer); + + free(text); + } return retval; } @@ -1342,40 +1366,42 @@ nh_compose_unicode_screenshot(void) text = (PMSNHMsgGetText) malloc(sizeof(MSNHMsgGetText) + TEXT_BUFFER_SIZE); - text->max_size = - TEXT_BUFFER_SIZE - - 1; /* make sure we always have 0 at the end of the buffer */ - - wtext = - (PMSNHMsgGetWideText) malloc(sizeof(MSNHMsgGetWideText) - + TEXT_BUFFER_SIZE * sizeof(WCHAR)); - wtext->max_size = - TEXT_BUFFER_SIZE - - 1; /* make sure we always have 0 at the end of the buffer */ - - ZeroMemory(text->buffer, TEXT_BUFFER_SIZE); - SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND, - (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) text); - retsize += MultiByteToWideChar(CP_ACP, 0, - text->buffer, strlen(text->buffer), - retval + retsize, max_size - retsize); - - ZeroMemory(wtext->buffer, TEXT_BUFFER_SIZE * sizeof(WCHAR)); - SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_MSNH_COMMAND, - (WPARAM) MSNH_MSG_GETWIDETEXT, (LPARAM) wtext); - wcsncpy(retval + retsize, wtext->buffer, max_size - retsize - 1); - retsize += wcslen(retval + retsize); - - ZeroMemory(text->buffer, TEXT_BUFFER_SIZE); - SendMessage(mswin_hwnd_from_winid(WIN_STATUS), WM_MSNH_COMMAND, - (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) text); - retsize += MultiByteToWideChar(CP_ACP, 0, - text->buffer, strlen(text->buffer), - retval + retsize, max_size - retsize); - retval[retsize] = L'\0'; - - free(text); - free(wtext); + if (text && retval) { + text->max_size = + TEXT_BUFFER_SIZE + - 1; /* make sure we always have 0 at the end of the buffer */ + + wtext = (PMSNHMsgGetWideText) malloc( + sizeof(MSNHMsgGetWideText) + TEXT_BUFFER_SIZE * sizeof(WCHAR)); + if (wtext) { + wtext->max_size = + TEXT_BUFFER_SIZE + - 1; /* make sure we always have 0 at the end of the buffer */ + + ZeroMemory(text->buffer, TEXT_BUFFER_SIZE); + SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND, + (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) text); + retsize += MultiByteToWideChar( + CP_ACP, 0, text->buffer, strlen(text->buffer), + retval + retsize, max_size - retsize); + + ZeroMemory(wtext->buffer, TEXT_BUFFER_SIZE * sizeof(WCHAR)); + SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_MSNH_COMMAND, + (WPARAM) MSNH_MSG_GETWIDETEXT, (LPARAM) wtext); + wcsncpy(retval + retsize, wtext->buffer, max_size - retsize - 1); + retsize += wcslen(retval + retsize); + + ZeroMemory(text->buffer, TEXT_BUFFER_SIZE); + SendMessage(mswin_hwnd_from_winid(WIN_STATUS), WM_MSNH_COMMAND, + (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) text); + retsize += MultiByteToWideChar( + CP_ACP, 0, text->buffer, strlen(text->buffer), + retval + retsize, max_size - retsize); + retval[retsize] = L'\0'; + free(wtext); + } + free(text); + } return retval; } #endif diff --git a/win/win32/mhmain.h b/win/win32/mhmain.h index e25bd51451..c4aea5ce20 100644 --- a/win/win32/mhmain.h +++ b/win/win32/mhmain.h @@ -5,7 +5,7 @@ #ifndef MSWINMainWindow_h #define MSWINMainWindow_h -/* this is a main appliation window */ +/* this is a main application window */ #include "winMS.h" diff --git a/win/win32/mhmap.c b/win/win32/mhmap.c index dc97ba5db6..36904f7d55 100644 --- a/win/win32/mhmap.c +++ b/win/win32/mhmap.c @@ -324,7 +324,7 @@ mswin_map_layout(HWND hWnd, LPSIZE map_size) data->xFrontTile = max(data->xFrontTile, 1); data->yFrontTile = max(data->yFrontTile, 1); - /* calcuate ASCII cursor height */ + /* calculate ASCII cursor height */ data->yBlinkCursor = (int) ((double) CURSOR_HEIGHT * data->backScale); data->yNoBlinkCursor = data->yBackTile; @@ -585,7 +585,7 @@ MapWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) size.cx = LOWORD(lParam); size.cy = HIWORD(lParam); } else { - /* mapping factor is unchaged we just need to adjust scroll bars + /* mapping factor is unchanged we just need to adjust scroll bars */ size.cx = data->xFrontTile * COLNO; size.cy = data->yFrontTile * ROWNO; @@ -640,6 +640,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: @@ -762,6 +763,7 @@ onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) msg_data->buffer[index++] = '\r'; msg_data->buffer[index++] = '\n'; } + nhUse(mgch); } break; #ifdef ENHANCED_SYMBOLS @@ -833,6 +835,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); @@ -976,17 +979,20 @@ paintGlyph(PNHMapWindow data, int i, int j, RECT * rect) && glyphinfo->gm.u && glyphinfo->gm.u->utf8str) { ch = glyphinfo->gm.u->utf32ch; - if (glyphinfo->gm.u->ucolor != 0) { - rgbcolor = RGB( - (glyphinfo->gm.u->ucolor >> 16) & 0xFF, - (glyphinfo->gm.u->ucolor >> 8) & 0xFF, - (glyphinfo->gm.u->ucolor >> 0) & 0xFF); - } } #endif + if ((glyphinfo->gm.customcolor & NH_BASIC_COLOR) == 0) { + rgbcolor = RGB((glyphinfo->gm.customcolor >> 16) & 0xFF, + (glyphinfo->gm.customcolor >> 8) & 0xFF, + (glyphinfo->gm.customcolor >> 0) & 0xFF); + } else { + color = (int) COLORVAL(glyphinfo->gm.customcolor); + rgbcolor = nhcolor_to_RGB(color); + } if (((data->map[i][j].gm.glyphflags & MG_PET) && iflags.hilite_pet) - || ((data->map[i][j].gm.glyphflags & (MG_DETECT | MG_BW_LAVA)) - && iflags.use_inverse)) { + || ((data->map[i][j].gm.glyphflags & (MG_DETECT | MG_BW_LAVA + | MG_BW_ICE | MG_BW_SINK + | MG_BW_ENGR)) != 0)) { back_brush = CreateSolidBrush(nhcolor_to_RGB(CLR_GRAY)); FillRect(data->backBufferDC, rect, back_brush); @@ -1059,6 +1065,7 @@ static void setGlyph(PNHMapWindow data, int i, int j, || (data->bkmap[i][j].glyph != bg->glyph) || data->map[i][j].ttychar != fg->ttychar || data->map[i][j].gm.sym.color != fg->gm.sym.color + || data->map[i][j].gm.customcolor != fg->gm.customcolor || data->map[i][j].gm.glyphflags != fg->gm.glyphflags || data->map[i][j].gm.tileidx != fg->gm.tileidx) { data->map[i][j] = *fg; @@ -1272,7 +1279,6 @@ void nhglyph2charcolor(short g, uchar *ch, int *color) { int offset; -#ifdef TEXTCOLOR #define zap_color(n) *color = iflags.use_color ? zapcolors[n] : NO_COLOR #define cmap_color(n) *color = iflags.use_color ? defsyms[n].color : NO_COLOR @@ -1283,17 +1289,6 @@ nhglyph2charcolor(short g, uchar *ch, int *color) #define warn_color(n) \ *color = iflags.use_color ? def_warnsyms[n].color : NO_COLOR -#else /* no text color */ - -#define zap_color(n) -#define cmap_color(n) -#define obj_color(n) -#define mon_color(n) -#define pet_color(c) -#define warn_color(c) - *color = CLR_WHITE; -#endif - if ((offset = (g - GLYPH_WARNING_OFF)) >= 0) { /* a warning flash */ *ch = showsyms[offset + SYM_OFF_W]; warn_color(offset); diff --git a/win/win32/mhmenu.c b/win/win32/mhmenu.c index 1cabfe025e..d39bdc1b43 100644 --- a/win/win32/mhmenu.c +++ b/win/win32/mhmenu.c @@ -32,6 +32,7 @@ typedef struct mswin_menu_item { char accelerator; char group_accel; int attr; + int color; char str[NHMENU_STR_SIZE]; boolean presel; unsigned int itemflags; @@ -94,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 @@ -282,22 +285,24 @@ MenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) HDC hdc = GetDC(control); data = (PNHMenuWindow) malloc(sizeof(NHMenuWindow)); - ZeroMemory(data, sizeof(NHMenuWindow)); - data->type = MENU_TYPE_TEXT; - data->how = PICK_NONE; - data->result = 0; - data->done = 0; - data->bmpChecked = - LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL)); - data->bmpCheckedCount = - LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL_COUNT)); - data->bmpNotChecked = - LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_UNSEL)); - data->bmpDC = CreateCompatibleDC(hdc); - data->is_active = FALSE; - SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) data); - - /* set font for the text cotrol */ + if (data) { + ZeroMemory(data, sizeof(NHMenuWindow)); + data->type = MENU_TYPE_TEXT; + data->how = PICK_NONE; + data->result = 0; + data->done = 0; + data->bmpChecked = + LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL)); + data->bmpCheckedCount = LoadBitmap( + GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL_COUNT)); + data->bmpNotChecked = + LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_UNSEL)); + 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); SendMessage(control, WM_SETFONT, (WPARAM) font->hFont, @@ -349,10 +354,10 @@ MenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) return FALSE; case WM_CLOSE: - if (gp.program_state.gameover) { + if (program_state.gameover) { data->result = -1; data->done = 1; - gp.program_state.stopprint++; + program_state.stopprint++; return TRUE; } else return FALSE; @@ -517,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; } @@ -538,6 +544,7 @@ onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) RECT text_rt; HGDIOBJ saveFont; HDC hdc; + TCHAR *was; if (data->type != MENU_TYPE_TEXT) SetMenuType(hWnd, MENU_TYPE_TEXT); @@ -546,12 +553,17 @@ onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) text_size = strlen(msg_data->text) + 4; data->menui.text.text = (TCHAR *) malloc(text_size * sizeof(data->menui.text.text[0])); - ZeroMemory(data->menui.text.text, - text_size * sizeof(data->menui.text.text[0])); + if (data->menui.text.text) + ZeroMemory(data->menui.text.text, + text_size * sizeof(data->menui.text.text[0])); } else { + was = data->menui.text.text; + text_size = _tcslen(data->menui.text.text) + strlen(msg_data->text) + 4; data->menui.text.text = (TCHAR *) realloc( data->menui.text.text, text_size * sizeof(data->menui.text.text[0])); + if (!data->menui.text.text) + free(was); /* this is not a good situation, but not a leak either */ } if (!data->menui.text.text) break; @@ -610,74 +622,88 @@ onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) break; if (data->menui.menu.size == data->menui.menu.allocated) { + PNHMenuItem was = data->menui.menu.items; + data->menui.menu.allocated += 10; data->menui.menu.items = (PNHMenuItem) realloc( 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; } - new_item = data->menui.menu.size; - ZeroMemory(&data->menui.menu.items[new_item], - sizeof(data->menui.menu.items[new_item])); - data->menui.menu.items[new_item].glyphinfo = msg_data->glyphinfo; - data->menui.menu.items[new_item].identifier = *msg_data->identifier; - data->menui.menu.items[new_item].accelerator = msg_data->accelerator; - data->menui.menu.items[new_item].group_accel = msg_data->group_accel; - data->menui.menu.items[new_item].attr = msg_data->attr; - strncpy(data->menui.menu.items[new_item].str, msg_data->str, - NHMENU_STR_SIZE); - /* prevent & being interpreted as a mnemonic start */ - strNsubst(data->menui.menu.items[new_item].str, "&", "&&", 0); - data->menui.menu.items[new_item].presel = msg_data->presel; - data->menui.menu.items[new_item].itemflags = msg_data->itemflags; - - /* calculate tabstop size */ - hDC = GetDC(hWnd); - cached_font * font = mswin_get_font(NHW_MENU, msg_data->attr, hDC, FALSE); - saveFont = SelectObject(hDC, font->hFont); - GetTextMetrics(hDC, &tm); - p1 = data->menui.menu.items[new_item].str; - p = strchr(data->menui.menu.items[new_item].str, '\t'); - column = 0; - for (;;) { - TCHAR wbuf[BUFSZ]; - RECT drawRect; - SetRect(&drawRect, 0, 0, 1, 1); - if (p != NULL) - *p = '\0'; /* for time being, view tab field as zstring */ - DrawText(hDC, NH_A2W(p1, wbuf, BUFSZ), strlen(p1), &drawRect, - DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_EXPANDTABS - | DT_SINGLELINE); - data->menui.menu.tab_stop_size[column] = - max(data->menui.menu.tab_stop_size[column], - drawRect.right - drawRect.left); - - menuitemwidth += data->menui.menu.tab_stop_size[column]; - - if (p != NULL) - *p = '\t'; - else /* last string so, */ - break; - - /* add the separation only when not the last item */ - /* in the last item, we break out of the loop, in the statement - * just above */ - menuitemwidth += TAB_SEPARATION; + if (data->menui.menu.items) { + new_item = data->menui.menu.size; + ZeroMemory(&data->menui.menu.items[new_item], + sizeof(data->menui.menu.items[new_item])); + data->menui.menu.items[new_item].glyphinfo = msg_data->glyphinfo; + data->menui.menu.items[new_item].identifier = + *msg_data->identifier; + data->menui.menu.items[new_item].accelerator = + msg_data->accelerator; + data->menui.menu.items[new_item].group_accel = + msg_data->group_accel; + data->menui.menu.items[new_item].attr = msg_data->attr; + data->menui.menu.items[new_item].color = msg_data->color; + strncpy(data->menui.menu.items[new_item].str, msg_data->str, + NHMENU_STR_SIZE); + /* prevent & being interpreted as a mnemonic start */ + strNsubst(data->menui.menu.items[new_item].str, "&", "&&", 0); + data->menui.menu.items[new_item].presel = msg_data->presel; + data->menui.menu.items[new_item].itemflags = msg_data->itemflags; + + /* calculate tabstop size */ + hDC = GetDC(hWnd); + cached_font *font = + mswin_get_font(NHW_MENU, msg_data->attr, hDC, FALSE); + saveFont = SelectObject(hDC, font->hFont); + GetTextMetrics(hDC, &tm); + p1 = data->menui.menu.items[new_item].str; + p = strchr(data->menui.menu.items[new_item].str, '\t'); + column = 0; + for (;;) { + TCHAR wbuf[BUFSZ]; + RECT drawRect; + SetRect(&drawRect, 0, 0, 1, 1); + if (p != NULL) + *p = '\0'; /* for time being, view tab field as zstring */ + DrawText(hDC, NH_A2W(p1, wbuf, BUFSZ), strlen(p1), &drawRect, + DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_EXPANDTABS + | DT_SINGLELINE); + data->menui.menu.tab_stop_size[column] = + max(data->menui.menu.tab_stop_size[column], + drawRect.right - drawRect.left); + + menuitemwidth += data->menui.menu.tab_stop_size[column]; + + if (p != NULL) + *p = '\t'; + else /* last string so, */ + break; + + /* add the separation only when not the last item */ + /* in the last item, we break out of the loop, in the + * statement just above */ + menuitemwidth += TAB_SEPARATION; + + ++column; + p1 = p + 1; + p = strchr(p1, '\t'); + } - ++column; - p1 = p + 1; - p = strchr(p1, '\t'); - } - SelectObject(hDC, saveFont); - ReleaseDC(hWnd, hDC); + SelectObject(hDC, saveFont); + ReleaseDC(hWnd, hDC); - /* calculate new menu width */ - data->menui.menu.menu_cx = - max(data->menui.menu.menu_cx, - 2 * TILE_X + menuitemwidth - + (tm.tmAveCharWidth + tm.tmOverhang) * 12); + /* calculate new menu width */ + data->menui.menu.menu_cx = + max(data->menui.menu.menu_cx, + 2 * TILE_X + menuitemwidth + + (tm.tmAveCharWidth + tm.tmOverhang) * 12); - /* increment size */ - data->menui.menu.size++; + /* increment size */ + data->menui.menu.size++; + } } break; case MSNH_MSG_ENDMENU: { @@ -697,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) @@ -889,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; @@ -906,6 +940,7 @@ SetMenuListType(HWND hWnd, int how) } if (data->is_active) SetFocus(control); + nhUse(nItem); } /*-----------------------------------------------------------------------------*/ HWND @@ -952,14 +987,18 @@ onMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam) /* Set the height of the list box items to max height of the individual * items */ for (i = 0; i < data->menui.menu.size; i++) { +/* int map_y = GetNHApp()->mapTile_Y; */ + int map_y = 16; /* leave it at the default size, unless + * we are actually showing the larger + * icons in the menu */ if (NHMENU_HAS_GLYPH(data->menui.menu.items[i]) && !IS_MAP_ASCII(iflags.wc_map_mode)) { lpmis->itemHeight = max(lpmis->itemHeight, - (UINT) max(tm.tmHeight, GetNHApp()->mapTile_Y) + 2); + (UINT) max(tm.tmHeight, map_y) + 2); } else { lpmis->itemHeight = - max(lpmis->itemHeight, (UINT) max(tm.tmHeight, TILE_Y) + 2); + max(lpmis->itemHeight, (UINT) max(tm.tmHeight, map_y) + 2); } } @@ -989,9 +1028,6 @@ onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam) char *p, *p1; int column; int spacing = 0; - - int color = NO_COLOR, attr; - boolean menucolr = FALSE; double monitorScale = win10_monitor_scale(hWnd); int tileXScaled = (int) (TILE_X * monitorScale); int tileYScaled = (int) (TILE_Y * monitorScale); @@ -1001,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); @@ -1019,6 +1055,9 @@ onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam) ? menu_fg_color : (COLORREF) GetSysColor(DEFAULT_COLOR_FG_MENU)); + if (item->color != NO_COLOR) + (void) SetTextColor(lpdis->hDC, nhcolor_to_RGB(item->color)); + GetTextMetrics(lpdis->hDC, &tm); spacing = tm.tmAveCharWidth; @@ -1056,15 +1095,6 @@ onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam) if (item->accelerator != 0) { buf[0] = item->accelerator; buf[1] = '\x0'; - - if (iflags.use_menu_color - && (menucolr = get_menu_coloring(item->str, &color, &attr))) { - cached_font * menu_font = mswin_get_font(NHW_MENU, attr, lpdis->hDC, FALSE); - SelectObject(lpdis->hDC, menu_font->hFont); - if (color != NO_COLOR) - SetTextColor(lpdis->hDC, nhcolor_to_RGB(color)); - } - SetRect(&drawRect, x, lpdis->rcItem.top, lpdis->rcItem.right, lpdis->rcItem.bottom); DrawText(lpdis->hDC, NH_A2W(buf, wbuf, 2), 1, &drawRect, @@ -1171,13 +1201,15 @@ 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); } - /* TOOD: add blinking for blink text */ + /* TODO: add blinking for blink text */ cached_font * blink_font = mswin_get_font(NHW_MENU, ATR_BLINK, lpdis->hDC, FALSE); SelectObject(lpdis->hDC, blink_font->hFont); @@ -1517,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/mhmsg.h b/win/win32/mhmsg.h index 90413e0ccb..54a2e4c3d4 100644 --- a/win/win32/mhmsg.h +++ b/win/win32/mhmsg.h @@ -54,7 +54,7 @@ typedef struct mswin_nhmsg_add_menu { char accelerator; char group_accel; int attr; - int clr; + int color; const char *str; boolean presel; unsigned int itemflags; diff --git a/win/win32/mhmsgwnd.c b/win/win32/mhmsgwnd.c index d910ed209b..e4d62b5aba 100644 --- a/win/win32/mhmsgwnd.c +++ b/win/win32/mhmsgwnd.c @@ -10,8 +10,7 @@ #define MSG_WRAP_TEXT #define MSG_VISIBLE_LINES max(iflags.wc_vary_msgcount, 1) -#define MAX_MSG_LINES 128 -#define MSG_LINES (int) min(iflags.msg_history, MAX_MSG_LINES) +#define MSG_LINES (int) min(iflags.msg_history, MAX_MSG_HISTORY) #define MAXWINDOWTEXT TBUFSZ #define DEFAULT_COLOR_BG_MSG COLOR_WINDOW @@ -26,7 +25,7 @@ struct window_line { typedef struct mswin_nethack_message_window { size_t max_text; - struct window_line window_text[MAX_MSG_LINES]; + struct window_line window_text[MAX_MSG_HISTORY]; int lines_last_turn; /* lines added during the last turn */ int lines_not_seen; /* lines not yet seen by user after last turn or --More-- */ @@ -172,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: { @@ -294,7 +294,7 @@ onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) int okkey = 0; char tmptext[MAXWINDOWTEXT + 1]; - // @@@ Ok respnses + // @@@ Ok responses /* save original text */ strcpy(tmptext, data->window_text[MSG_LINES - 1].text); @@ -481,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, @@ -647,6 +647,7 @@ onPaint(HWND hWnd) strcpy(tmptext, data->window_text[i].text); strip_newline(tmptext); NH_A2W(tmptext, wbuf, sizeof(wbuf)); + wbuf[SIZE(wbuf) - 1] = '\0'; wlen = _tcslen(wbuf); setMsgTextColor(hdc, i < (MSG_LINES - data->lines_last_turn)); #ifdef MSG_WRAP_TEXT @@ -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); @@ -794,7 +796,7 @@ can_append_text(HWND hWnd, int attr, const char *text) if (data->lines_not_seen == 0) return FALSE; - /* cannot append text with different attrbutes */ + /* cannot append text with different attributes */ if (data->window_text[MSG_LINES - 1].attr != attr) return FALSE; @@ -802,7 +804,7 @@ can_append_text(HWND hWnd, int attr, const char *text) if (str_end_is(data->window_text[MSG_LINES - 1].text, "\n")) return FALSE; - /* check if the maximum string langth will be exceeded */ + /* check if the maximum string length will be exceeded */ if (strlen(data->window_text[MSG_LINES - 1].text) + 2 + /* space characters */ strlen(text) + strlen(MORE) diff --git a/win/win32/mhrip.c b/win/win32/mhrip.c index fdeefe716e..28036307b5 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; } @@ -227,7 +228,7 @@ NHRIPWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) GetNHApp()->hMainWnd = NULL; DestroyWindow(hWnd); SetFocus(GetNHApp()->hMainWnd); - gp.program_state.stopprint++; + program_state.stopprint++; return TRUE; case WM_DESTROY: @@ -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; } @@ -262,13 +264,20 @@ onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) text_size = strlen(msg_data->text) + 4; data->window_text = (TCHAR *) malloc(text_size * sizeof(data->window_text[0])); - ZeroMemory(data->window_text, - text_size * sizeof(data->window_text[0])); + if (data->window_text) { + ZeroMemory(data->window_text, + text_size * sizeof(data->window_text[0])); + } } else { + TCHAR *was = data->window_text; + text_size = _tcslen(data->window_text) + strlen(msg_data->text) + 4; data->window_text = (TCHAR *) realloc( data->window_text, text_size * sizeof(data->window_text[0])); + if (!data->window_text) { + free(was); + } } if (!data->window_text) break; @@ -288,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 6d81bd17a6..5c5ef6328a 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 ab62b6c7ab..eee9047f85 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) { @@ -73,7 +77,7 @@ typedef struct mswin_nethack_status_window { mswin_status_lines * status_lines; back_buffer_t back_buffer; boolean blink_state; /* true = invert blink text */ - boolean has_blink_fields; /* true if one or more has blink attriubte */ + boolean has_blink_fields; /* true if one or more has blink attribute */ } NHStatusWindow, *PNHStatusWindow; @@ -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 a70a90f6e8..ec34733340 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 5244620272..8463a187e7 100644 --- a/win/win32/mhtext.c +++ b/win/win32/mhtext.c @@ -36,7 +36,7 @@ mswin_init_text_window(void) mswin_get_window_placement(NHW_TEXT, &rt); } - /* create text widnow object */ + /* create text window object */ ret = CreateDialog(GetNHApp()->hApp, MAKEINTRESOURCE(IDD_NHTEXT), GetNHApp()->hMainWnd, NHTextWndProc); if (!ret) @@ -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; @@ -198,13 +201,20 @@ onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) text_size = strlen(msg_data->text) + 4; data->window_text = (TCHAR *) malloc(text_size * sizeof(data->window_text[0])); - ZeroMemory(data->window_text, - text_size * sizeof(data->window_text[0])); + if (data->window_text) { + ZeroMemory(data->window_text, + text_size * sizeof(data->window_text[0])); + } } else { + TCHAR *was = data->window_text; + text_size = _tcslen(data->window_text) + strlen(msg_data->text) + 4; data->window_text = (TCHAR *) realloc( data->window_text, text_size * sizeof(data->window_text[0])); + if (!data->window_text) { + free(was); + } } if (!data->window_text) break; diff --git a/win/win32/mswproc.c b/win/win32/mswproc.c index 98994a29df..906917f219 100644 --- a/win/win32/mswproc.c +++ b/win/win32/mswproc.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mswproc.c $NHDT-Date: 1613292828 2021/02/14 08:53:48 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.165 $ */ +/* NetHack 3.7 mswproc.c $NHDT-Date: 1717967341 2024/06/09 21:09:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.193 $ */ /* Copyright (C) 2001 by Alex Kompel */ /* NetHack may be freely redistributed. See license for details. */ @@ -7,12 +7,13 @@ * 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" #include "func_tab.h" /* for extended commands */ #include "winMS.h" -#include + #include #include "mhmap.h" #include "mhstatus.h" @@ -28,6 +29,7 @@ #include "mhmain.h" #include "mhfont.h" #include "resource.h" +#undef MSWPROC_C #define LLEN 128 @@ -35,7 +37,7 @@ #ifdef DEBUG # ifdef _DEBUG -static FILE* _s_debugfp = NULL; +static FILE *_s_debugfp = NULL; extern void logDebug(const char *fmt, ...); # endif #endif @@ -76,6 +78,7 @@ COLORREF message_bg_color = RGB(0, 0, 0); COLORREF message_fg_color = RGB(0xFF, 0xFF, 0xFF); strbuf_t raw_print_strbuf = { 0 }; +color_attr mswin_menu_promptstyle = { NO_COLOR, ATR_NONE }; /* Interface definition, for windows.c */ struct window_procs mswin_procs = { @@ -92,7 +95,7 @@ struct window_procs mswin_procs = { WC2_HITPOINTBAR | WC2_FLUSH_STATUS | WC2_RESET_STATUS | WC2_HILITE_STATUS | #endif #ifdef ENHANCED_SYMBOLS - WC2_U_UTF8STR | WC2_U_24BITCOLOR | + WC2_U_UTF8STR | WC2_EXTRACOLORS | #endif 0L, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ @@ -114,10 +117,9 @@ struct window_procs mswin_procs = { mswin_nh_poskey, mswin_nhbell, mswin_doprev_message, mswin_yn_function, mswin_getlin, mswin_get_ext_cmd, mswin_number_pad, mswin_delay_output, #ifdef CHANGE_COLOR /* only a Mac option currently */ - mswin, mswin_change_background, + 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, @@ -127,7 +129,7 @@ struct window_procs mswin_procs = { }; /* -init_nhwindows(int* argcp, char** argv) +init_nhwindows(int *argcp, char **argv) -- Initialize the windows used by NetHack. This can also create the standard windows listed at the top, but does not display them. @@ -160,10 +162,10 @@ mswin_init_nhwindows(int *argc, char **argv) mswin_nh_input_init(); /* set it to WIN_ERR so we can detect attempts to - use this ID before it is inialized */ + use this ID before it is initialized */ WIN_MAP = WIN_ERR; - /* Read Windows settings from the reqistry */ + /* Read Windows settings from the registry */ /* First set safe defaults */ GetNHApp()->regMainMinX = CW_USEDEFAULT; mswin_read_reg(); @@ -239,22 +241,22 @@ mswin_init_nhwindows(int *argc, char **argv) | WC_FONTSIZ_TEXT | WC_VARY_MSGCOUNT, set_in_game); - mswin_color_from_string(iflags.wc_foregrnd_menu, &menu_fg_brush, - &menu_fg_color); - mswin_color_from_string(iflags.wc_foregrnd_message, &message_fg_brush, - &message_fg_color); - mswin_color_from_string(iflags.wc_foregrnd_status, &status_fg_brush, - &status_fg_color); - mswin_color_from_string(iflags.wc_foregrnd_text, &text_fg_brush, - &text_fg_color); - mswin_color_from_string(iflags.wc_backgrnd_menu, &menu_bg_brush, - &menu_bg_color); - mswin_color_from_string(iflags.wc_backgrnd_message, &message_bg_brush, - &message_bg_color); - mswin_color_from_string(iflags.wc_backgrnd_status, &status_bg_brush, - &status_bg_color); - mswin_color_from_string(iflags.wc_backgrnd_text, &text_bg_brush, - &text_bg_color); + mswin_color_from_string(iflags.wcolors[wcolor_menu].fg, + &menu_fg_brush, &menu_fg_color); + mswin_color_from_string(iflags.wcolors[wcolor_message].fg, + &message_fg_brush, &message_fg_color); + mswin_color_from_string(iflags.wcolors[wcolor_status].fg, + &status_fg_brush, &status_fg_color); + mswin_color_from_string(iflags.wcolors[wcolor_text].fg, + &text_fg_brush, &text_fg_color); + mswin_color_from_string(iflags.wcolors[wcolor_menu].bg, + &menu_bg_brush, &menu_bg_color); + mswin_color_from_string(iflags.wcolors[wcolor_message].bg, + &message_bg_brush, &message_bg_color); + mswin_color_from_string(iflags.wcolors[wcolor_status].bg, + &status_bg_brush, &status_bg_color); + mswin_color_from_string(iflags.wcolors[wcolor_text].bg, + &text_bg_brush, &text_bg_color); if (iflags.wc_splash_screen) mswin_display_splash_window(FALSE); @@ -337,7 +339,7 @@ prompt_for_player_selection(void) anything any; menu_item *selected = 0; DWORD box_result; - int clr = 0; + int clr = NO_COLOR; logDebug("prompt_for_player_selection()\n"); @@ -370,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) @@ -449,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); @@ -523,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); @@ -598,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); @@ -672,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); @@ -695,7 +698,7 @@ mswin_askname(void) { logDebug("mswin_askname()\n"); - if (mswin_getlin_window("Who are you?", gp.plname, PL_NSIZ) == IDCANCEL) { + if (mswin_getlin_window("Who are you?", svp.plname, PL_NSIZ) == IDCANCEL) { bail("bye-bye"); /* not reached */ } @@ -1010,6 +1013,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; @@ -1020,11 +1024,17 @@ mswin_putstr_ex(winid wid, int attr, const char *text, int app) /* yield a bit so it gets done immediately */ mswin_get_nh_event(); } else { + char *was = GetNHApp()->saved_text; + // build text to display later in message box GetNHApp()->saved_text = realloc(GetNHApp()->saved_text, strlen(text) + strlen(GetNHApp()->saved_text) + 1); - strcat(GetNHApp()->saved_text, text); + if (!GetNHApp()->saved_text) { + free(was); + } else { + strcat(GetNHApp()->saved_text, text); + } } } @@ -1043,8 +1053,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); } @@ -1146,6 +1157,7 @@ mswin_add_menu(winid wid, const glyph_info *glyphinfo, data.accelerator = accelerator; data.group_accel = group_accel; data.attr = attr; + data.color = clr; data.str = str; data.presel = presel; data.itemflags = itemflags; @@ -1233,18 +1245,31 @@ void mswin_update_inventory(int arg) { logDebug("mswin_update_inventory(%d)\n", arg); - if (iflags.perm_invent && gp.program_state.something_worth_saving + if (iflags.perm_invent && program_state.something_worth_saving && iflags.window_inited && WIN_INVEN != WIN_ERR) display_inventory(NULL, FALSE); } win_request_info * mswin_ctrl_nhwindow( - winid window, + winid window UNUSED, int request, win_request_info *wri) { - return (win_request_info *) 0; + if (!wri) + return (win_request_info *) 0; + + switch(request) { + case set_mode: + case request_settings: + break; + case set_menu_promptstyle: + mswin_menu_promptstyle = wri->fromcore.menu_promptstyle; + break; + default: + break; + } + return wri; } /* @@ -1332,10 +1357,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); @@ -1606,6 +1633,7 @@ mswin_yn_function(const char *question, const char *choices, char def) char z, digit_string[2]; int n_len = 0; long value = 0; + mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, ("#"), 1); n_len++; digit_string[1] = '\0'; @@ -1619,7 +1647,10 @@ mswin_yn_function(const char *question, const char *choices, char def) do { /* loop until we get a non-digit */ z = lowc(readchar()); if (digit(z)) { - value = (10 * value) + (z - '0'); + long dgt = (long) (z - '0'); + + /* value = (10 * value) + (z - '0'); */ + value = AppendLongDigit(value, dgt); if (value < 0) break; /* overflow: try again */ digit_string[0] = z; @@ -1667,7 +1698,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; } @@ -1870,41 +1901,16 @@ mswin_delay_output(void) } void -mswin_change_color(void) +mswin_change_color(int color, long rgb, int reverse) { - logDebug("mswin_change_color()\n"); + logDebug("mswin_change_color(%d, %ld, %d)\n", color, rgb, reverse); } 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 *) ""; } /* @@ -1928,7 +1934,7 @@ mswin_outrip(winid wid, int how, time_t when) } /* Put name on stone */ - Sprintf(buf, "%s", gp.plname); + Sprintf(buf, "%s", svp.plname); buf[STONE_LINE_LEN] = 0; putstr(wid, 0, buf); @@ -2075,28 +2081,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); - text->max_size = - TEXT_BUFFER_SIZE - - 1; /* make sure we always have 0 at the end of the buffer */ + 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); - SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND, - (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) text); + ZeroMemory(history_text->buffer, TEXT_BUFFER_SIZE); + SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND, + (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 { @@ -2516,7 +2526,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); } @@ -2653,7 +2663,7 @@ static color_table_value color_table[] = { }; typedef struct ctbv { - char *colorstring; + const char *colorstring; int syscolorvalue; } color_table_brush_value; @@ -2688,7 +2698,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; @@ -2700,27 +2710,29 @@ mswin_color_from_string(char *colorstring, HBRUSH *brushptr, - hexadecimals); ++colorstring; red_value *= 16; - red_value += (int) (strchr(hexadecimals, tolower((uchar) *colorstring)) - - hexadecimals); + red_value += + (int) (strchr(hexadecimals, tolower((uchar) *colorstring)) + - hexadecimals); ++colorstring; - green_value = (int) (strchr(hexadecimals, - tolower((uchar) *colorstring)) - - hexadecimals); + green_value = + (int) (strchr(hexadecimals, tolower((uchar) *colorstring)) + - hexadecimals); ++colorstring; green_value *= 16; - green_value += (int) (strchr(hexadecimals, - tolower((uchar) *colorstring)) - - hexadecimals); + green_value += + (int) (strchr(hexadecimals, tolower((uchar) *colorstring)) + - hexadecimals); ++colorstring; - blue_value = (int) (strchr(hexadecimals, tolower((uchar) *colorstring)) - - hexadecimals); + blue_value = + (int) (strchr(hexadecimals, tolower((uchar) *colorstring)) + - hexadecimals); ++colorstring; blue_value *= 16; - blue_value += (int) (strchr(hexadecimals, - tolower((uchar) *colorstring)) - - hexadecimals); + blue_value += + (int) (strchr(hexadecimals, tolower((uchar) *colorstring)) + - hexadecimals); ++colorstring; *colorptr = RGB(red_value, green_value, blue_value); @@ -2743,7 +2755,10 @@ mswin_color_from_string(char *colorstring, HBRUSH *brushptr, if (max_brush > TOTAL_BRUSHES) panic("Too many colors!"); *brushptr = CreateSolidBrush(*colorptr); - brush_table[max_brush++] = *brushptr; + if (IndexOk(max_brush, brush_table)) { + brush_table[max_brush] = *brushptr; + max_brush++; + } } void @@ -2820,7 +2835,7 @@ int NHMessageBox(HWND hWnd, LPCTSTR text, UINT type) { TCHAR title[MAX_LOADSTRING]; - if (gp.program_state.exiting && !strcmp(text, "\n")) + if (program_state.exiting && !strcmp(text, "\n")) text = "Press Enter to exit"; LoadString(GetNHApp()->hApp, IDS_APP_TITLE_SHORT, title, MAX_LOADSTRING); @@ -2834,37 +2849,37 @@ 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_WITHER, "Wither" }, + { 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 }, + { BL_MASK_WITHER, "Wither", 0 }, }; extern winid WIN_STATUS; @@ -3013,7 +3028,7 @@ mswin_status_enablefield(int fieldidx, const char *nm, const char *fmt, } } -/* TODO: turn this into a commmon helper; multiple identical implementations */ +/* TODO: turn this into a common helper; multiple identical implementations */ static int mswin_condcolor(long bm, unsigned long *bmarray) { @@ -3031,11 +3046,12 @@ static int mswin_condattr(long bm, unsigned long *bmarray) { if (bm && bmarray) { - if (bm & bmarray[HL_ATTCLR_DIM]) return HL_DIM; - if (bm & bmarray[HL_ATTCLR_BLINK]) return HL_BLINK; - if (bm & bmarray[HL_ATTCLR_ULINE]) return HL_ULINE; + if (bm & bmarray[HL_ATTCLR_BOLD]) return HL_BOLD; + if (bm & bmarray[HL_ATTCLR_DIM]) return HL_DIM; + if (bm & bmarray[HL_ATTCLR_ITALIC]) return HL_ITALIC; + if (bm & bmarray[HL_ATTCLR_ULINE]) return HL_ULINE; + if (bm & bmarray[HL_ATTCLR_BLINK]) return HL_BLINK; if (bm & bmarray[HL_ATTCLR_INVERSE]) return HL_INVERSE; - if (bm & bmarray[HL_ATTCLR_BOLD]) return HL_BOLD; } return HL_NONE; @@ -3057,7 +3073,7 @@ status_update(int fldindex, genericptr_t ptr, int chg, int percent, int color, u windowport that it should output all changes received to this point. It marks the end of a bot() cycle. -- fldindex could also be BL_RESET, which is not really - a field index, but is a special advisory to to tell the + a field index, but is a special advisory to tell the windowport that it should redisplay all its status fields, even if no changes have been presented to it. -- ptr is usually a "char *", unless fldindex is BL_CONDITION. @@ -3085,7 +3101,7 @@ status_update(int fldindex, genericptr_t ptr, int chg, int percent, int color, u use to display the text. -- condmasks is a pointer to a set of BL_ATTCLR_MAX unsigned longs telling which conditions should be displayed in each - color and attriubte. + color and attribute. */ DISABLE_WARNING_FORMAT_NONLITERAL diff --git a/win/win32/winMS.h b/win/win32/winMS.h index 23d98b7369..1de3db0426 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); @@ -178,10 +186,8 @@ void mswin_getlin(const char *question, char *input); int mswin_get_ext_cmd(void); void mswin_number_pad(int state); void mswin_delay_output(void); -void mswin_change_color(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))) diff --git a/win/win32/xNetHackW.rc b/win/win32/xNetHackW.rc index 8ae200d48f..8308419c95 100644 --- a/win/win32/xNetHackW.rc +++ b/win/win32/xNetHackW.rc @@ -34,7 +34,6 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // remains consistent on all systems. IDI_NETHACKW ICON "NETHACK.ICO" - ///////////////////////////////////////////////////////////////////////////// // // Menu @@ -455,6 +454,17 @@ sound_Wooden_Harp_G WAVE "sound_Wooden_Harp_G.wav" sa2_xpleveldown WAVE "sa2_xpleveldown.wav" sa2_xplevelup WAVE "sa2_xplevelup.wav" #endif + +#ifdef VIA_MAKE +#ifndef ID_MANIFEST +#define ID_MANIFEST 1 +#endif +#ifndef RT_MANIFEST +#define RT_MANIFEST MAKEINTRESOURCE(24) +#endif +ID_MANIFEST RT_MANIFEST "NetHackW.exe.manifest" +#endif + #endif // English (United States) resources /////////////////////////////////////////////////////////////////////////////